summaryrefslogtreecommitdiff
path: root/chromium/components
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components')
-rw-r--r--chromium/components/BUILD.gn21
-rw-r--r--chromium/components/OWNERS3
-rw-r--r--chromium/components/README20
-rw-r--r--chromium/components/about_handler/url_request_about_job.cc4
-rw-r--r--chromium/components/app_modal/javascript_dialog_manager.cc52
-rw-r--r--chromium/components/app_modal/javascript_dialog_manager.h9
-rw-r--r--chromium/components/app_modal_strings.grdp6
-rw-r--r--chromium/components/arc/BUILD.gn22
-rw-r--r--chromium/components/arc/appfuse/OWNERS1
-rw-r--r--chromium/components/arc/appfuse/arc_appfuse_bridge.cc102
-rw-r--r--chromium/components/arc/appfuse/arc_appfuse_bridge.h53
-rw-r--r--chromium/components/arc/arc_bridge_host_impl.cc10
-rw-r--r--chromium/components/arc/arc_bridge_host_impl.h3
-rw-r--r--chromium/components/arc/arc_bridge_service.h15
-rw-r--r--chromium/components/arc/arc_features.cc14
-rw-r--r--chromium/components/arc/arc_features.h2
-rw-r--r--chromium/components/arc/arc_features_parser.cc139
-rw-r--r--chromium/components/arc/arc_features_parser.h93
-rw-r--r--chromium/components/arc/arc_features_parser_unittest.cc124
-rw-r--r--chromium/components/arc/arc_prefs.cc19
-rw-r--r--chromium/components/arc/arc_prefs.h6
-rw-r--r--chromium/components/arc/arc_session.cc7
-rw-r--r--chromium/components/arc/arc_session.h53
-rw-r--r--chromium/components/arc/arc_session_impl.cc110
-rw-r--r--chromium/components/arc/arc_session_impl.h7
-rw-r--r--chromium/components/arc/arc_session_impl_unittest.cc99
-rw-r--r--chromium/components/arc/arc_session_runner.cc14
-rw-r--r--chromium/components/arc/arc_session_runner.h17
-rw-r--r--chromium/components/arc/arc_session_runner_unittest.cc53
-rw-r--r--chromium/components/arc/arc_util.cc11
-rw-r--r--chromium/components/arc/arc_util.h16
-rw-r--r--chromium/components/arc/audio/DEPS4
-rw-r--r--chromium/components/arc/audio/arc_audio_bridge.cc11
-rw-r--r--chromium/components/arc/common/BUILD.gn2
-rw-r--r--chromium/components/arc/common/accessibility_helper.mojom8
-rw-r--r--chromium/components/arc/common/app.mojom35
-rw-r--r--chromium/components/arc/common/appfuse.mojom27
-rw-r--r--chromium/components/arc/common/arc_bridge.mojom12
-rw-r--r--chromium/components/arc/common/auth.mojom9
-rw-r--r--chromium/components/arc/common/bluetooth.mojom19
-rw-r--r--chromium/components/arc/common/disk_quota.mojom22
-rw-r--r--chromium/components/arc/common/ime.mojom8
-rw-r--r--chromium/components/arc/common/intent_helper.mojom26
-rw-r--r--chromium/components/arc/common/notifications.mojom46
-rw-r--r--chromium/components/arc/common/policy.mojom5
-rw-r--r--chromium/components/arc/common/process.mojom83
-rw-r--r--chromium/components/arc/common/timer.mojom38
-rw-r--r--chromium/components/arc/common/timer.typemap8
-rw-r--r--chromium/components/arc/common/video_accelerator_struct_traits.cc8
-rw-r--r--chromium/components/arc/common/video_common.mojom10
-rw-r--r--chromium/components/arc/common/volume_mounter.typemap5
-rw-r--r--chromium/components/arc/crash_collector/arc_crash_collector_bridge.cc15
-rw-r--r--chromium/components/arc/disk_quota/arc_disk_quota_bridge.cc103
-rw-r--r--chromium/components/arc/disk_quota/arc_disk_quota_bridge.h49
-rw-r--r--chromium/components/arc/ime/DEPS1
-rw-r--r--chromium/components/arc/ime/arc_ime_bridge.h6
-rw-r--r--chromium/components/arc/ime/arc_ime_bridge_impl.cc11
-rw-r--r--chromium/components/arc/ime/arc_ime_bridge_impl.h5
-rw-r--r--chromium/components/arc/ime/arc_ime_service.cc103
-rw-r--r--chromium/components/arc/ime/arc_ime_service.h11
-rw-r--r--chromium/components/arc/ime/arc_ime_service_unittest.cc56
-rw-r--r--chromium/components/arc/intent_helper/arc_intent_helper_bridge.cc40
-rw-r--r--chromium/components/arc/intent_helper/arc_intent_helper_bridge.h15
-rw-r--r--chromium/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc20
-rw-r--r--chromium/components/arc/intent_helper/intent_filter.cc3
-rw-r--r--chromium/components/arc/intent_helper/intent_filter.h5
-rw-r--r--chromium/components/arc/intent_helper/intent_filter_struct_traits.cc10
-rw-r--r--chromium/components/arc/intent_helper/intent_filter_struct_traits.h4
-rw-r--r--chromium/components/arc/intent_helper/intent_filter_unittest.cc5
-rw-r--r--chromium/components/arc/intent_helper/open_url_delegate.h22
-rw-r--r--chromium/components/arc/intent_helper/page_transition_util.cc45
-rw-r--r--chromium/components/arc/intent_helper/page_transition_util.h24
-rw-r--r--chromium/components/arc/intent_helper/page_transition_util_unittest.cc218
-rw-r--r--chromium/components/arc/metrics/arc_metrics_service_unittest.cc2
-rw-r--r--chromium/components/arc/midis/arc_midis_bridge.cc22
-rw-r--r--chromium/components/arc/power/arc_power_bridge.cc2
-rw-r--r--chromium/components/arc/timer/OWNERS (renamed from chromium/components/arc/ime/OWNERS)0
-rw-r--r--chromium/components/arc/timer/arc_timer.cc76
-rw-r--r--chromium/components/arc/timer/arc_timer.h79
-rw-r--r--chromium/components/arc/timer/arc_timer_bridge.cc305
-rw-r--r--chromium/components/arc/timer/arc_timer_bridge.h75
-rw-r--r--chromium/components/arc/timer/arc_timer_bridge_unittest.cc345
-rw-r--r--chromium/components/arc/timer/arc_timer_struct_traits.cc40
-rw-r--r--chromium/components/arc/timer/arc_timer_struct_traits.h22
-rw-r--r--chromium/components/arc/timer/arc_timer_traits.cc97
-rw-r--r--chromium/components/arc/timer/arc_timer_traits.h36
-rw-r--r--chromium/components/arc/timer/create_timer_request.cc13
-rw-r--r--chromium/components/arc/timer/create_timer_request.h28
-rw-r--r--chromium/components/arc/usb/usb_host_bridge.cc36
-rw-r--r--chromium/components/arc/usb/usb_host_bridge.h3
-rw-r--r--chromium/components/arc/video_accelerator/DEPS3
-rw-r--r--chromium/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h2
-rw-r--r--chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc7
-rw-r--r--chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h9
-rw-r--r--chromium/components/arc/volume_mounter/OWNERS3
-rw-r--r--chromium/components/arc/volume_mounter/volume_mounter_struct_traits.cc (renamed from chromium/components/arc/volume_mounter/volume_mounter_traits.cc)4
-rw-r--r--chromium/components/arc/volume_mounter/volume_mounter_struct_traits.h (renamed from chromium/components/arc/volume_mounter/volume_mounter_traits.h)6
-rw-r--r--chromium/components/assist_ranker/BUILD.gn5
-rw-r--r--chromium/components/assist_ranker/DEPS3
-rw-r--r--chromium/components/assist_ranker/assist_ranker_service_impl.cc8
-rw-r--r--chromium/components/assist_ranker/assist_ranker_service_impl.h10
-rw-r--r--chromium/components/assist_ranker/base_predictor_unittest.cc16
-rw-r--r--chromium/components/assist_ranker/binary_classifier_predictor.cc6
-rw-r--r--chromium/components/assist_ranker/binary_classifier_predictor.h7
-rw-r--r--chromium/components/assist_ranker/binary_classifier_predictor_unittest.cc9
-rw-r--r--chromium/components/assist_ranker/predictor_config_definitions.cc8
-rw-r--r--chromium/components/assist_ranker/ranker_model_loader_impl.cc17
-rw-r--r--chromium/components/assist_ranker/ranker_model_loader_impl.h22
-rw-r--r--chromium/components/assist_ranker/ranker_model_loader_impl_unittest.cc41
-rw-r--r--chromium/components/assist_ranker/ranker_url_fetcher.cc63
-rw-r--r--chromium/components/assist_ranker/ranker_url_fetcher.h28
-rw-r--r--chromium/components/autofill/OWNERS1
-rw-r--r--chromium/components/autofill/android/BUILD.gn11
-rw-r--r--chromium/components/autofill/android/autofill_provider_android.cc3
-rw-r--r--chromium/components/autofill/android/autofill_provider_android.h12
-rw-r--r--chromium/components/autofill/android/java/res/values/colors.xml3
-rw-r--r--chromium/components/autofill/android/java/res/values/dimens.xml8
-rw-r--r--chromium/components/autofill/content/browser/BUILD.gn2
-rw-r--r--chromium/components/autofill/content/browser/DEPS1
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver.cc13
-rw-r--r--chromium/components/autofill/content/browser/content_autofill_driver.h4
-rw-r--r--chromium/components/autofill/content/browser/risk/fingerprint.cc11
-rw-r--r--chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc2
-rw-r--r--chromium/components/autofill/content/common/BUILD.gn1
-rw-r--r--chromium/components/autofill/content/common/autofill_agent.mojom12
-rw-r--r--chromium/components/autofill/content/common/autofill_driver.mojom57
-rw-r--r--chromium/components/autofill/content/common/autofill_types.mojom21
-rw-r--r--chromium/components/autofill/content/common/autofill_types.typemap9
-rw-r--r--chromium/components/autofill/content/common/autofill_types_struct_traits.cc62
-rw-r--r--chromium/components/autofill/content/common/autofill_types_struct_traits.h60
-rw-r--r--chromium/components/autofill/content/common/autofill_types_struct_traits_unittest.cc49
-rw-r--r--chromium/components/autofill/content/common/test_autofill_types.mojom1
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent.cc56
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent.h18
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util.cc188
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util.h57
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc120
-rw-r--r--chromium/components/autofill/content/renderer/form_cache.cc23
-rw-r--r--chromium/components/autofill/content/renderer/password_autofill_agent.cc434
-rw-r--r--chromium/components/autofill/content/renderer/password_autofill_agent.h94
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils.cc647
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils.h14
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc118
-rw-r--r--chromium/components/autofill/content/renderer/password_generation_agent.cc241
-rw-r--r--chromium/components/autofill/content/renderer/password_generation_agent.h43
-rw-r--r--chromium/components/autofill/content/renderer/renderer_save_password_progress_logger.cc9
-rw-r--r--chromium/components/autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc11
-rw-r--r--chromium/components/autofill/core/browser/BUILD.gn65
-rw-r--r--chromium/components/autofill/core/browser/DEPS9
-rw-r--r--chromium/components/autofill/core/browser/address_combobox_model_unittest.cc4
-rw-r--r--chromium/components/autofill/core/browser/address_field.cc35
-rw-r--r--chromium/components/autofill/core/browser/address_field.h6
-rw-r--r--chromium/components/autofill/core/browser/address_field_unittest.cc104
-rw-r--r--chromium/components/autofill/core/browser/autocomplete_history_manager.cc14
-rw-r--r--chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc53
-rw-r--r--chromium/components/autofill/core/browser/autofill_address_policy_handler.cc36
-rw-r--r--chromium/components/autofill/core/browser/autofill_address_policy_handler.h30
-rw-r--r--chromium/components/autofill/core/browser/autofill_address_policy_handler_unittest.cc78
-rw-r--r--chromium/components/autofill/core/browser/autofill_assistant_unittest.cc18
-rw-r--r--chromium/components/autofill/core/browser/autofill_client.h24
-rw-r--r--chromium/components/autofill/core/browser/autofill_credit_card_policy_handler.cc4
-rw-r--r--chromium/components/autofill/core/browser/autofill_credit_card_policy_handler_unittest.cc30
-rw-r--r--chromium/components/autofill/core/browser/autofill_data_util.cc8
-rw-r--r--chromium/components/autofill/core/browser/autofill_download_manager.cc138
-rw-r--r--chromium/components/autofill/core/browser/autofill_download_manager.h29
-rw-r--r--chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc271
-rw-r--r--chromium/components/autofill/core/browser/autofill_driver.h9
-rw-r--r--chromium/components/autofill/core/browser/autofill_experiments.cc152
-rw-r--r--chromium/components/autofill/core/browser/autofill_experiments.h92
-rw-r--r--chromium/components/autofill/core/browser/autofill_experiments_unittest.cc5
-rw-r--r--chromium/components/autofill/core/browser/autofill_external_delegate.cc78
-rw-r--r--chromium/components/autofill/core/browser/autofill_external_delegate.h12
-rw-r--r--chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc198
-rw-r--r--chromium/components/autofill/core/browser/autofill_field.cc6
-rw-r--r--chromium/components/autofill/core/browser/autofill_field.h12
-rw-r--r--chromium/components/autofill/core/browser/autofill_handler.cc117
-rw-r--r--chromium/components/autofill/core/browser/autofill_handler.h61
-rw-r--r--chromium/components/autofill/core/browser/autofill_handler_proxy.cc23
-rw-r--r--chromium/components/autofill/core/browser/autofill_handler_proxy.h13
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager.cc491
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager.h97
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager_unittest.cc257
-rw-r--r--chromium/components/autofill/core/browser/autofill_merge_unittest.cc5
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics.cc403
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics.h122
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics_unittest.cc1201
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile.cc42
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile.h10
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile_sync_util.cc235
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile_sync_util.h45
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc289
-rw-r--r--chromium/components/autofill/core/browser/autofill_provider.h3
-rw-r--r--chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.cc21
-rw-r--r--chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.h12
-rw-r--r--chromium/components/autofill/core/browser/autofill_test_utils.cc63
-rw-r--r--chromium/components/autofill/core/browser/autofill_test_utils.h11
-rw-r--r--chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.cc7
-rw-r--r--chromium/components/autofill/core/browser/autofill_wallet_data_type_controller_unittest.cc4
-rw-r--r--chromium/components/autofill/core/browser/credit_card.cc220
-rw-r--r--chromium/components/autofill/core/browser/credit_card.h15
-rw-r--r--chromium/components/autofill/core/browser/credit_card_save_manager.cc209
-rw-r--r--chromium/components/autofill/core/browser/credit_card_save_manager.h33
-rw-r--r--chromium/components/autofill/core/browser/credit_card_save_manager_unittest.cc2009
-rw-r--r--chromium/components/autofill/core/browser/credit_card_unittest.cc215
-rw-r--r--chromium/components/autofill/core/browser/form_data_importer.cc118
-rw-r--r--chromium/components/autofill/core/browser/form_data_importer.h60
-rw-r--r--chromium/components/autofill/core/browser/form_data_importer_unittest.cc628
-rw-r--r--chromium/components/autofill/core/browser/form_field.cc9
-rw-r--r--chromium/components/autofill/core/browser/form_field.h28
-rw-r--r--chromium/components/autofill/core/browser/form_field_unittest.cc31
-rw-r--r--chromium/components/autofill/core/browser/form_group.cc4
-rw-r--r--chromium/components/autofill/core/browser/form_group.h4
-rw-r--r--chromium/components/autofill/core/browser/form_structure.cc324
-rw-r--r--chromium/components/autofill/core/browser/form_structure.h127
-rw-r--r--chromium/components/autofill/core/browser/form_structure_unittest.cc1357
-rw-r--r--chromium/components/autofill/core/browser/local_card_migration_manager.cc170
-rw-r--r--chromium/components/autofill/core/browser/local_card_migration_manager.h96
-rw-r--r--chromium/components/autofill/core/browser/local_card_migration_manager_unittest.cc599
-rw-r--r--chromium/components/autofill/core/browser/password_generator.cc255
-rw-r--r--chromium/components/autofill/core/browser/password_generator.h57
-rw-r--r--chromium/components/autofill/core/browser/password_generator_fips181.cc124
-rw-r--r--chromium/components/autofill/core/browser/password_generator_fips181.h62
-rw-r--r--chromium/components/autofill/core/browser/password_generator_fips181_fuzzer.cc47
-rw-r--r--chromium/components/autofill/core/browser/password_generator_fips181_unittest.cc117
-rw-r--r--chromium/components/autofill/core/browser/password_generator_unittest.cc297
-rw-r--r--chromium/components/autofill/core/browser/password_requirements_spec_fetcher.h41
-rw-r--r--chromium/components/autofill/core/browser/password_requirements_spec_fetcher_impl.cc314
-rw-r--r--chromium/components/autofill/core/browser/password_requirements_spec_fetcher_impl.h143
-rw-r--r--chromium/components/autofill/core/browser/password_requirements_spec_fetcher_unittest.cc343
-rw-r--r--chromium/components/autofill/core/browser/password_requirements_spec_printer.cc44
-rw-r--r--chromium/components/autofill/core/browser/password_requirements_spec_printer.h19
-rw-r--r--chromium/components/autofill/core/browser/payments/full_card_request_unittest.cc12
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_client.cc246
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_client.h81
-rw-r--r--chromium/components/autofill/core/browser/payments/payments_client_unittest.cc127
-rw-r--r--chromium/components/autofill/core/browser/payments/test_payments_client.cc19
-rw-r--r--chromium/components/autofill/core/browser/payments/test_payments_client.h30
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager.cc496
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager.h78
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager_unittest.cc566
-rw-r--r--chromium/components/autofill/core/browser/popup_item_ids.h5
-rw-r--r--chromium/components/autofill/core/browser/proto/BUILD.gn2
-rw-r--r--chromium/components/autofill/core/browser/proto/password_requirements.proto89
-rw-r--r--chromium/components/autofill/core/browser/proto/password_requirements_shard.proto22
-rw-r--r--chromium/components/autofill/core/browser/proto/server.proto28
-rw-r--r--chromium/components/autofill/core/browser/search_field.cc32
-rw-r--r--chromium/components/autofill/core/browser/search_field.h37
-rw-r--r--chromium/components/autofill/core/browser/search_field_unittest.cc77
-rw-r--r--chromium/components/autofill/core/browser/suggestion.cc18
-rw-r--r--chromium/components/autofill/core/browser/suggestion.h6
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_client.cc61
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_client.h31
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_driver.cc19
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_driver.h7
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_external_delegate.cc25
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_external_delegate.h16
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_manager.cc34
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_manager.h19
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_provider.cc3
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_provider.h3
-rw-r--r--chromium/components/autofill/core/browser/test_event_waiter.h90
-rw-r--r--chromium/components/autofill/core/browser/test_form_data_importer.cc4
-rw-r--r--chromium/components/autofill/core/browser/test_form_data_importer.h4
-rw-r--r--chromium/components/autofill/core/browser/test_local_card_migration_manager.cc54
-rw-r--r--chromium/components/autofill/core/browser/test_local_card_migration_manager.h52
-rw-r--r--chromium/components/autofill/core/browser/test_personal_data_manager.cc18
-rw-r--r--chromium/components/autofill/core/browser/test_personal_data_manager.h14
-rw-r--r--chromium/components/autofill/core/browser/test_sync_service.cc25
-rw-r--r--chromium/components/autofill/core/browser/test_sync_service.h29
-rw-r--r--chromium/components/autofill/core/browser/ui/DEPS5
-rw-r--r--chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller.h2
-rw-r--r--chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc154
-rw-r--r--chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h15
-rw-r--r--chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl_unittest.cc14
-rw-r--r--chromium/components/autofill/core/browser/ui/local_card_migration_bubble_controller.h37
-rw-r--r--chromium/components/autofill/core/browser/ui/save_card_bubble_controller.h16
-rw-r--r--chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc7
-rw-r--r--chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h5
-rw-r--r--chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc324
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc302
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h117
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc1256
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc296
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h139
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc438
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table.cc1292
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table.h22
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc76
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc29
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc10
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h1
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc6
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h1
-rw-r--r--chromium/components/autofill/core/common/BUILD.gn3
-rw-r--r--chromium/components/autofill/core/common/autofill_features.cc56
-rw-r--r--chromium/components/autofill/core/common/autofill_features.h13
-rw-r--r--chromium/components/autofill/core/common/autofill_l10n_util_unittest.cc2
-rw-r--r--chromium/components/autofill/core/common/autofill_pref_names.cc3
-rw-r--r--chromium/components/autofill/core/common/autofill_pref_names.h1
-rw-r--r--chromium/components/autofill/core/common/autofill_regex_constants.cc16
-rw-r--r--chromium/components/autofill/core/common/autofill_switches.cc8
-rw-r--r--chromium/components/autofill/core/common/autofill_switches.h2
-rw-r--r--chromium/components/autofill/core/common/autofill_util.cc8
-rw-r--r--chromium/components/autofill/core/common/autofill_util.h4
-rw-r--r--chromium/components/autofill/core/common/filling_status.h19
-rw-r--r--chromium/components/autofill/core/common/form_data.cc16
-rw-r--r--chromium/components/autofill/core/common/form_data.h9
-rw-r--r--chromium/components/autofill/core/common/form_field_data.cc6
-rw-r--r--chromium/components/autofill/core/common/form_field_data.h18
-rw-r--r--chromium/components/autofill/core/common/password_form.cc10
-rw-r--r--chromium/components/autofill/core/common/password_form.h21
-rw-r--r--chromium/components/autofill/core/common/password_form_fill_data.cc9
-rw-r--r--chromium/components/autofill/core/common/password_form_fill_data.h20
-rw-r--r--chromium/components/autofill/core/common/password_form_fill_data_unittest.cc40
-rw-r--r--chromium/components/autofill/core/common/password_generation_util.cc35
-rw-r--r--chromium/components/autofill/core/common/password_generation_util.h34
-rw-r--r--chromium/components/autofill/core/common/save_password_progress_logger.cc32
-rw-r--r--chromium/components/autofill/core/common/save_password_progress_logger.h6
-rw-r--r--chromium/components/autofill/ios/browser/BUILD.gn3
-rw-r--r--chromium/components/autofill/ios/browser/DEPS1
-rw-r--r--chromium/components/autofill/ios/browser/autofill_agent.h2
-rw-r--r--chromium/components/autofill/ios/browser/autofill_agent.mm171
-rw-r--r--chromium/components/autofill/ios/browser/autofill_agent_unittests.mm191
-rw-r--r--chromium/components/autofill/ios/browser/autofill_driver_ios.h1
-rw-r--r--chromium/components/autofill/ios/browser/autofill_driver_ios.mm8
-rw-r--r--chromium/components/autofill/ios/browser/autofill_util.mm2
-rw-r--r--chromium/components/autofill/ios/browser/fake_autofill_agent.mm9
-rw-r--r--chromium/components/autofill/ios/browser/fake_js_autofill_manager.h6
-rw-r--r--chromium/components/autofill/ios/browser/fake_js_autofill_manager.mm12
-rw-r--r--chromium/components/autofill/ios/browser/form_suggestion_provider.h6
-rw-r--r--chromium/components/autofill/ios/browser/js_autofill_manager.h9
-rw-r--r--chromium/components/autofill/ios/browser/js_autofill_manager.mm24
-rw-r--r--chromium/components/autofill/ios/browser/resources/autofill_controller.js39
-rw-r--r--chromium/components/autofill/ios/fill/BUILD.gn35
-rw-r--r--chromium/components/autofill/ios/form_util/BUILD.gn73
-rw-r--r--chromium/components/autofill/ios/form_util/DEPS (renamed from chromium/components/autofill/ios/fill/DEPS)1
-rw-r--r--chromium/components/autofill/ios/form_util/fill_js_unittest.mm (renamed from chromium/components/autofill/ios/fill/fill_js_unittest.mm)3
-rw-r--r--chromium/components/autofill/ios/form_util/form_activity_observer.h58
-rw-r--r--chromium/components/autofill/ios/form_util/form_activity_observer_bridge.h71
-rw-r--r--chromium/components/autofill/ios/form_util/form_activity_observer_bridge.mm50
-rw-r--r--chromium/components/autofill/ios/form_util/form_activity_observer_bridge_unittest.mm113
-rw-r--r--chromium/components/autofill/ios/form_util/form_activity_tab_helper.h74
-rw-r--r--chromium/components/autofill/ios/form_util/form_activity_tab_helper.mm133
-rw-r--r--chromium/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm92
-rw-r--r--chromium/components/autofill/ios/form_util/form_unittest.mm (renamed from chromium/components/autofill/ios/fill/form_unittest.mm)71
-rw-r--r--chromium/components/autofill/ios/form_util/resources/fill.js (renamed from chromium/components/autofill/ios/fill/resources/fill.js)5
-rw-r--r--chromium/components/autofill/ios/form_util/resources/form.js (renamed from chromium/components/autofill/ios/fill/resources/form.js)97
-rw-r--r--chromium/components/autofill/ios/form_util/test_form_activity_observer.h57
-rw-r--r--chromium/components/autofill/ios/form_util/test_form_activity_observer.mm48
-rw-r--r--chromium/components/autofill/ios/form_util/test_form_activity_tab_helper.h37
-rw-r--r--chromium/components/autofill/ios/form_util/test_form_activity_tab_helper.mm43
-rw-r--r--chromium/components/autofill_strings.grdp90
-rw-r--r--chromium/components/autofill_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_NEW_CARD_LINK.png.sha11
-rw-r--r--chromium/components/autofill_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_PERMANENT.png.sha11
-rw-r--r--chromium/components/autofill_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN_CVC.png.sha11
-rw-r--r--chromium/components/blacklist/OWNERS3
-rw-r--r--chromium/components/blacklist/README.md58
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/BUILD.gn35
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist.cc222
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist.h183
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_data.cc176
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h176
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_delegate.h (renamed from chromium/components/previews/core/previews_black_list_delegate.h)22
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_item.cc (renamed from chromium/components/previews/core/previews_black_list_item.cc)38
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_item.h (renamed from chromium/components/previews/core/previews_black_list_item.h)47
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_item_unittest.cc (renamed from chromium/components/previews/core/previews_black_list_item_unittest.cc)49
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_unittest.cc1189
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/opt_out_store.h49
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/sql/BUILD.gn34
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/sql/DEPS3
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/sql/opt_out_store_sql.cc423
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/sql/opt_out_store_sql.h (renamed from chromium/components/previews/core/previews_opt_out_store_sql.h)46
-rw-r--r--chromium/components/blacklist/opt_out_blacklist/sql/opt_out_store_sql_unittest.cc352
-rw-r--r--chromium/components/bookmarks/browser/BUILD.gn3
-rw-r--r--chromium/components/bookmarks/browser/bookmark_client.h13
-rw-r--r--chromium/components/bookmarks/browser/bookmark_codec.cc39
-rw-r--r--chromium/components/bookmarks/browser/bookmark_codec.h17
-rw-r--r--chromium/components/bookmarks/browser/bookmark_codec_unittest.cc122
-rw-r--r--chromium/components/bookmarks/browser/bookmark_model.cc53
-rw-r--r--chromium/components/bookmarks/browser/bookmark_model.h24
-rw-r--r--chromium/components/bookmarks/browser/bookmark_model_unittest.cc4
-rw-r--r--chromium/components/bookmarks/browser/bookmark_node_data.cc8
-rw-r--r--chromium/components/bookmarks/browser/bookmark_node_data_unittest.cc9
-rw-r--r--chromium/components/bookmarks/browser/bookmark_storage.cc12
-rw-r--r--chromium/components/bookmarks/browser/bookmark_storage.h14
-rw-r--r--chromium/components/bookmarks/browser/bookmark_utils.cc3
-rw-r--r--chromium/components/bookmarks/browser/history_bookmark_model.h41
-rw-r--r--chromium/components/bookmarks/browser/model_loader.cc52
-rw-r--r--chromium/components/bookmarks/browser/model_loader.h75
-rw-r--r--chromium/components/bookmarks/browser/url_index.cc4
-rw-r--r--chromium/components/bookmarks/browser/url_index.h20
-rw-r--r--chromium/components/browser_sync/BUILD.gn6
-rw-r--r--chromium/components/browser_sync/DEPS2
-rw-r--r--chromium/components/browser_sync/abstract_profile_sync_service_test.cc4
-rw-r--r--chromium/components/browser_sync/profile_sync_components_factory_impl.cc220
-rw-r--r--chromium/components/browser_sync/profile_sync_components_factory_impl.h20
-rw-r--r--chromium/components/browser_sync/profile_sync_service.cc738
-rw-r--r--chromium/components/browser_sync/profile_sync_service.h190
-rw-r--r--chromium/components/browser_sync/profile_sync_service_autofill_unittest.cc36
-rw-r--r--chromium/components/browser_sync/profile_sync_service_mock.cc4
-rw-r--r--chromium/components/browser_sync/profile_sync_service_mock.h13
-rw-r--r--chromium/components/browser_sync/profile_sync_service_startup_unittest.cc699
-rw-r--r--chromium/components/browser_sync/profile_sync_service_unittest.cc681
-rw-r--r--chromium/components/browser_sync/profile_sync_test_util.cc70
-rw-r--r--chromium/components/browser_sync/profile_sync_test_util.h30
-rw-r--r--chromium/components/browser_sync/signin_confirmation_helper.cc40
-rw-r--r--chromium/components/browser_sync/signin_confirmation_helper.h12
-rw-r--r--chromium/components/browser_sync/sync_auth_manager.cc190
-rw-r--r--chromium/components/browser_sync/sync_auth_manager.h76
-rw-r--r--chromium/components/browser_sync/sync_auth_manager_unittest.cc479
-rw-r--r--chromium/components/browser_sync_strings.grdp12
-rw-r--r--chromium/components/browser_watcher/postmortem_report_collector_unittest.cc2
-rw-r--r--chromium/components/browser_watcher/watcher_metrics_provider_win_unittest.cc2
-rw-r--r--chromium/components/browser_watcher/window_hang_monitor_win.cc1
-rw-r--r--chromium/components/browser_watcher/window_hang_monitor_win.h2
-rw-r--r--chromium/components/browsing_data/content/DEPS5
-rw-r--r--chromium/components/browsing_data/content/conditional_cache_counting_helper.cc73
-rw-r--r--chromium/components/browsing_data/content/conditional_cache_counting_helper.h25
-rw-r--r--chromium/components/browsing_data/core/BUILD.gn6
-rw-r--r--chromium/components/browsing_data/core/DEPS1
-rw-r--r--chromium/components/browsing_data/core/counters/passwords_counter.cc2
-rw-r--r--chromium/components/browsing_data/core/history_notice_utils_unittest.cc24
-rw-r--r--chromium/components/bubble/bubble_manager.cc4
-rw-r--r--chromium/components/bubble/bubble_manager.h3
-rw-r--r--chromium/components/captive_portal/captive_portal_detector.cc8
-rw-r--r--chromium/components/captive_portal/captive_portal_detector.h4
-rw-r--r--chromium/components/captive_portal/captive_portal_detector_unittest.cc8
-rw-r--r--chromium/components/cast_certificate/cast_cert_validator.cc5
-rw-r--r--chromium/components/cast_certificate/cast_crl.cc3
-rw-r--r--chromium/components/cast_channel/OWNERS1
-rw-r--r--chromium/components/cast_channel/cast_socket.cc4
-rw-r--r--chromium/components/cast_channel/cast_socket.h4
-rw-r--r--chromium/components/cast_channel/cast_socket_unittest.cc6
-rw-r--r--chromium/components/cast_channel/keep_alive_delegate.cc8
-rw-r--r--chromium/components/cast_channel/keep_alive_delegate.h9
-rw-r--r--chromium/components/cast_channel/keep_alive_delegate_unittest.cc21
-rw-r--r--chromium/components/cbor/cbor_values.cc5
-rw-r--r--chromium/components/cbor/cbor_values.h3
-rw-r--r--chromium/components/cdm/browser/media_drm_storage_impl.cc200
-rw-r--r--chromium/components/cdm/browser/media_drm_storage_impl_unittest.cc105
-rw-r--r--chromium/components/cdm/renderer/android_key_systems.cc37
-rw-r--r--chromium/components/cdm/renderer/external_clear_key_key_system_properties.cc8
-rw-r--r--chromium/components/cdm/renderer/external_clear_key_key_system_properties.h4
-rw-r--r--chromium/components/cdm/renderer/widevine_key_system_properties.cc60
-rw-r--r--chromium/components/cdm/renderer/widevine_key_system_properties.h29
-rw-r--r--chromium/components/certificate_transparency/chrome_require_ct_delegate.cc9
-rw-r--r--chromium/components/certificate_transparency/chrome_require_ct_delegate_unittest.cc6
-rw-r--r--chromium/components/certificate_transparency/single_tree_tracker.h20
-rw-r--r--chromium/components/certificate_transparency/single_tree_tracker_unittest.cc2
-rw-r--r--chromium/components/certificate_transparency/sth_distributor_unittest.cc2
-rw-r--r--chromium/components/certificate_transparency/tree_state_tracker.cc1
-rw-r--r--chromium/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom12
-rw-r--r--chromium/components/chrome_cleaner/public/typemaps/chrome_prompt.typemap5
-rw-r--r--chromium/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc30
-rw-r--r--chromium/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h8
-rw-r--r--chromium/components/component_updater/BUILD.gn5
-rw-r--r--chromium/components/component_updater/component_installer_unittest.cc36
-rw-r--r--chromium/components/component_updater/component_updater_command_line_config_policy.cc15
-rw-r--r--chromium/components/component_updater/component_updater_command_line_config_policy.h6
-rw-r--r--chromium/components/component_updater/component_updater_service.cc99
-rw-r--r--chromium/components/component_updater/component_updater_service.h13
-rw-r--r--chromium/components/component_updater/component_updater_service_internal.h16
-rw-r--r--chromium/components/component_updater/component_updater_service_unittest.cc184
-rw-r--r--chromium/components/component_updater/configurator_impl.cc12
-rw-r--r--chromium/components/component_updater/configurator_impl.h17
-rw-r--r--chromium/components/component_updater/configurator_impl_unittest.cc51
-rw-r--r--chromium/components/component_updater/timer.cc3
-rw-r--r--chromium/components/component_updater/timer.h2
-rw-r--r--chromium/components/component_updater/timer_update_scheduler.cc27
-rw-r--r--chromium/components/component_updater/timer_update_scheduler.h37
-rw-r--r--chromium/components/component_updater/update_scheduler.h39
-rw-r--r--chromium/components/components_strings.grd2
-rw-r--r--chromium/components/consent_auditor/BUILD.gn12
-rw-r--r--chromium/components/consent_auditor/DEPS1
-rw-r--r--chromium/components/consent_auditor/consent_auditor.cc144
-rw-r--r--chromium/components/consent_auditor/consent_auditor.h50
-rw-r--r--chromium/components/consent_auditor/consent_auditor_impl.cc230
-rw-r--r--chromium/components/consent_auditor/consent_auditor_impl.h102
-rw-r--r--chromium/components/consent_auditor/consent_auditor_impl_unittest.cc (renamed from chromium/components/consent_auditor/consent_auditor_unittest.cc)211
-rw-r--r--chromium/components/consent_auditor/consent_sync_bridge.h30
-rw-r--r--chromium/components/consent_auditor/consent_sync_bridge_impl.cc313
-rw-r--r--chromium/components/consent_auditor/consent_sync_bridge_impl.h101
-rw-r--r--chromium/components/consent_auditor/consent_sync_bridge_impl_unittest.cc491
-rw-r--r--chromium/components/consent_auditor/fake_consent_auditor.cc26
-rw-r--r--chromium/components/consent_auditor/fake_consent_auditor.h21
-rw-r--r--chromium/components/constrained_window/native_web_contents_modal_dialog_manager_views.cc7
-rw-r--r--chromium/components/content_settings/core/browser/BUILD.gn3
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_ephemeral_provider.cc133
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_ephemeral_provider.h65
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_ephemeral_provider_unittest.cc179
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_info.cc6
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_info.h12
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_observer.h2
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_pref_provider.cc52
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_pref_provider.h10
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_registry.cc107
-rw-r--r--chromium/components/content_settings/core/browser/content_settings_registry.h3
-rw-r--r--chromium/components/content_settings/core/browser/cookie_settings.cc61
-rw-r--r--chromium/components/content_settings/core/browser/cookie_settings.h36
-rw-r--r--chromium/components/content_settings/core/browser/host_content_settings_map.cc36
-rw-r--r--chromium/components/content_settings/core/browser/host_content_settings_map.h3
-rw-r--r--chromium/components/content_settings/core/browser/website_settings_registry.cc26
-rw-r--r--chromium/components/content_settings/core/common/BUILD.gn6
-rw-r--r--chromium/components/content_settings/core/common/DEPS1
-rw-r--r--chromium/components/content_settings/core/common/cookie_settings_base.cc70
-rw-r--r--chromium/components/content_settings/core/common/cookie_settings_base.h64
-rw-r--r--chromium/components/content_settings/core/common/cookie_settings_base_unittest.cc153
-rw-r--r--chromium/components/content_settings/core/common/features.cc18
-rw-r--r--chromium/components/content_settings/core/common/features.h21
-rw-r--r--chromium/components/content_view/BUILD.gn15
-rw-r--r--chromium/components/content_view/README2
-rw-r--r--chromium/components/crash/android/OWNERS4
-rw-r--r--chromium/components/crash/content/app/BUILD.gn1
-rw-r--r--chromium/components/crash/content/app/breakpad_linux.cc25
-rw-r--r--chromium/components/crash/content/app/crash_reporter_client.cc11
-rw-r--r--chromium/components/crash/content/app/crash_reporter_client.h16
-rw-r--r--chromium/components/crash/content/app/crashpad_linux.cc81
-rw-r--r--chromium/components/crash/content/browser/BUILD.gn7
-rw-r--r--chromium/components/crash/content/browser/child_exit_observer_android.cc (renamed from chromium/components/crash/content/browser/crash_dump_observer_android.cc)46
-rw-r--r--chromium/components/crash/content/browser/child_exit_observer_android.h (renamed from chromium/components/crash/content/browser/crash_dump_observer_android.h)39
-rw-r--r--chromium/components/crash/content/browser/child_process_crash_observer_android.cc12
-rw-r--r--chromium/components/crash/content/browser/child_process_crash_observer_android.h13
-rw-r--r--chromium/components/crash/content/browser/crash_dump_manager_android.cc215
-rw-r--r--chromium/components/crash/content/browser/crash_dump_manager_android.h76
-rw-r--r--chromium/components/crash/content/browser/crash_dump_manager_android_unittest.cc175
-rw-r--r--chromium/components/crash/content/browser/crash_handler_host_linux.cc2
-rw-r--r--chromium/components/crash/content/browser/crash_metrics_reporter_android.cc271
-rw-r--r--chromium/components/crash/content/browser/crash_metrics_reporter_android.h91
-rw-r--r--chromium/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc255
-rwxr-xr-xchromium/components/crash/content/tools/generate_breakpad_symbols.py70
-rw-r--r--chromium/components/cronet/BUILD.gn6
-rw-r--r--chromium/components/cronet/android/BUILD.gn2162
-rw-r--r--chromium/components/cryptauth/BUILD.gn18
-rw-r--r--chromium/components/cryptauth/DEPS1
-rw-r--r--chromium/components/cryptauth/background_eid_generator.cc27
-rw-r--r--chromium/components/cryptauth/background_eid_generator.h15
-rw-r--r--chromium/components/cryptauth/background_eid_generator_unittest.cc29
-rw-r--r--chromium/components/cryptauth/ble/ble_advertisement_generator.cc44
-rw-r--r--chromium/components/cryptauth/ble/ble_advertisement_generator.h26
-rw-r--r--chromium/components/cryptauth/ble/ble_advertisement_generator_unittest.cc66
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.cc43
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.h11
-rw-r--r--chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc76
-rw-r--r--chromium/components/cryptauth/ble/fake_ble_advertisement_generator.cc7
-rw-r--r--chromium/components/cryptauth/ble/fake_ble_advertisement_generator.h5
-rw-r--r--chromium/components/cryptauth/connection.cc11
-rw-r--r--chromium/components/cryptauth/connection.h9
-rw-r--r--chromium/components/cryptauth/connection_observer.h7
-rw-r--r--chromium/components/cryptauth/connection_unittest.cc73
-rw-r--r--chromium/components/cryptauth/cryptauth_access_token_fetcher.h30
-rw-r--r--chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.cc60
-rw-r--r--chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.h59
-rw-r--r--chromium/components/cryptauth/cryptauth_access_token_fetcher_impl_unittest.cc76
-rw-r--r--chromium/components/cryptauth/cryptauth_api_call_flow.cc30
-rw-r--r--chromium/components/cryptauth/cryptauth_api_call_flow.h3
-rw-r--r--chromium/components/cryptauth/cryptauth_api_call_flow_unittest.cc19
-rw-r--r--chromium/components/cryptauth/cryptauth_client.h3
-rw-r--r--chromium/components/cryptauth/cryptauth_client_impl.cc67
-rw-r--r--chromium/components/cryptauth/cryptauth_client_impl.h39
-rw-r--r--chromium/components/cryptauth/cryptauth_client_impl_unittest.cc248
-rw-r--r--chromium/components/cryptauth/cryptauth_device_manager_impl.cc112
-rw-r--r--chromium/components/cryptauth/cryptauth_device_manager_impl.h3
-rw-r--r--chromium/components/cryptauth/cryptauth_device_manager_impl_unittest.cc204
-rw-r--r--chromium/components/cryptauth/cryptauth_enroller_impl.cc5
-rw-r--r--chromium/components/cryptauth/cryptauth_enroller_impl.h5
-rw-r--r--chromium/components/cryptauth/cryptauth_enroller_impl_unittest.cc5
-rw-r--r--chromium/components/cryptauth/cryptauth_enrollment_manager_impl.cc24
-rw-r--r--chromium/components/cryptauth/data_with_timestamp.h6
-rw-r--r--chromium/components/cryptauth/device_to_device_authenticator.cc2
-rw-r--r--chromium/components/cryptauth/device_to_device_authenticator.h8
-rw-r--r--chromium/components/cryptauth/device_to_device_authenticator_unittest.cc13
-rw-r--r--chromium/components/cryptauth/expiring_remote_device_cache.cc50
-rw-r--r--chromium/components/cryptauth/expiring_remote_device_cache.h54
-rw-r--r--chromium/components/cryptauth/expiring_remote_device_cache_unittest.cc74
-rw-r--r--chromium/components/cryptauth/fake_background_eid_generator.cc3
-rw-r--r--chromium/components/cryptauth/fake_background_eid_generator.h3
-rw-r--r--chromium/components/cryptauth/fake_connection.cc10
-rw-r--r--chromium/components/cryptauth/fake_connection.h11
-rw-r--r--chromium/components/cryptauth/fake_secure_channel.cc29
-rw-r--r--chromium/components/cryptauth/fake_secure_channel.h32
-rw-r--r--chromium/components/cryptauth/fake_secure_context.cc2
-rw-r--r--chromium/components/cryptauth/fake_secure_context.h6
-rw-r--r--chromium/components/cryptauth/fake_software_feature_manager.cc8
-rw-r--r--chromium/components/cryptauth/fake_software_feature_manager.h13
-rw-r--r--chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.cc43
-rw-r--r--chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.h39
-rw-r--r--chromium/components/cryptauth/network_request_error.cc37
-rw-r--r--chromium/components/cryptauth/network_request_error.h41
-rw-r--r--chromium/components/cryptauth/proto/BUILD.gn15
-rw-r--r--chromium/components/cryptauth/proto/enum_string_util.cc43
-rw-r--r--chromium/components/cryptauth/proto/enum_string_util.h19
-rw-r--r--chromium/components/cryptauth/remote_beacon_seed_fetcher.cc39
-rw-r--r--chromium/components/cryptauth/remote_beacon_seed_fetcher.h39
-rw-r--r--chromium/components/cryptauth/remote_beacon_seed_fetcher_unittest.cc148
-rw-r--r--chromium/components/cryptauth/remote_device.cc36
-rw-r--r--chromium/components/cryptauth/remote_device.h14
-rw-r--r--chromium/components/cryptauth/remote_device_cache.cc48
-rw-r--r--chromium/components/cryptauth/remote_device_cache.h14
-rw-r--r--chromium/components/cryptauth/remote_device_cache_unittest.cc38
-rw-r--r--chromium/components/cryptauth/remote_device_loader.cc23
-rw-r--r--chromium/components/cryptauth/remote_device_loader.h8
-rw-r--r--chromium/components/cryptauth/remote_device_loader_unittest.cc78
-rw-r--r--chromium/components/cryptauth/remote_device_provider_impl.cc2
-rw-r--r--chromium/components/cryptauth/remote_device_provider_impl_unittest.cc3
-rw-r--r--chromium/components/cryptauth/remote_device_ref.cc10
-rw-r--r--chromium/components/cryptauth/remote_device_ref.h32
-rw-r--r--chromium/components/cryptauth/remote_device_ref_unittest.cc10
-rw-r--r--chromium/components/cryptauth/remote_device_test_util.cc49
-rw-r--r--chromium/components/cryptauth/remote_device_test_util.h12
-rw-r--r--chromium/components/cryptauth/secure_channel.cc43
-rw-r--r--chromium/components/cryptauth/secure_channel.h46
-rw-r--r--chromium/components/cryptauth/secure_channel_unittest.cc70
-rw-r--r--chromium/components/cryptauth/session_keys.cc13
-rw-r--r--chromium/components/cryptauth/software_feature_manager.h9
-rw-r--r--chromium/components/cryptauth/software_feature_manager_impl.cc22
-rw-r--r--chromium/components/cryptauth/software_feature_manager_impl.h12
-rw-r--r--chromium/components/cryptauth/software_feature_manager_impl_unittest.cc69
-rw-r--r--chromium/components/cryptauth/software_feature_state.cc25
-rw-r--r--chromium/components/cryptauth/software_feature_state.h7
-rw-r--r--chromium/components/cryptauth/sync_scheduler_impl.cc6
-rw-r--r--chromium/components/cryptauth/sync_scheduler_impl.h6
-rw-r--r--chromium/components/cryptauth/sync_scheduler_impl_unittest.cc14
-rw-r--r--chromium/components/data_reduction_proxy/DEPS1
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/BUILD.gn3
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/DEPS4
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/content_lofi_decider.cc19
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/content_lofi_decider.h11
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.cc9
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.h2
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider_unittest.cc2
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.cc140
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.h60
-rw-r--r--chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl_unittest.cc461
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc74
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h17
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc176
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc2
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc177
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h29
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc164
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc50
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h30
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc51
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h16
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc119
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc19
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc2
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data.cc62
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h64
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_unittest.cc42
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer.cc15
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer_unittest.cc8
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc11
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h7
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc2
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc9
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc2
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc31
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h9
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc2
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc66
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h23
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc91
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.cc32
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc6
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h14
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc35
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc11
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h31
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc2
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h10
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc24
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h9
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_store_impl.cc8
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/data_usage_store_unittest.cc2
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/network_properties_manager_unittest.cc2
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/warmup_url_fetcher.cc14
-rw-r--r--chromium/components/data_reduction_proxy/core/browser/warmup_url_fetcher_unittest.cc12
-rw-r--r--chromium/components/data_reduction_proxy/core/common/BUILD.gn1
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc6
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h1
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc17
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h18
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc6
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h4
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.cc22
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h8
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc6
-rw-r--r--chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h1
-rw-r--r--chromium/components/data_reduction_proxy/core/common/lofi_decider.h12
-rw-r--r--chromium/components/data_reduction_proxy/core/common/resource_type_provider.h6
-rw-r--r--chromium/components/data_reduction_proxy/proto/client_config.proto5
-rw-r--r--chromium/components/data_reduction_proxy/proto/pageload_metrics.proto41
-rw-r--r--chromium/components/data_usage/OWNERS9
-rw-r--r--chromium/components/data_usage/android/BUILD.gn35
-rw-r--r--chromium/components/data_usage/android/DEPS6
-rw-r--r--chromium/components/data_usage/android/traffic_stats_amortizer.cc389
-rw-r--r--chromium/components/data_usage/android/traffic_stats_amortizer.h166
-rw-r--r--chromium/components/data_usage/android/traffic_stats_amortizer_unittest.cc848
-rw-r--r--chromium/components/data_usage/core/BUILD.gn37
-rw-r--r--chromium/components/data_usage/core/DEPS5
-rw-r--r--chromium/components/data_usage/core/data_use.cc59
-rw-r--r--chromium/components/data_usage/core/data_use.h75
-rw-r--r--chromium/components/data_usage/core/data_use_aggregator.cc135
-rw-r--r--chromium/components/data_usage/core/data_use_aggregator.h108
-rw-r--r--chromium/components/data_usage/core/data_use_aggregator_unittest.cc434
-rw-r--r--chromium/components/data_usage/core/data_use_amortizer.h43
-rw-r--r--chromium/components/data_usage/core/data_use_annotator.h40
-rw-r--r--chromium/components/data_use_measurement/core/data_use_measurement.cc30
-rw-r--r--chromium/components/data_use_measurement/core/data_use_measurement.h11
-rw-r--r--chromium/components/data_use_measurement/core/data_use_measurement_unittest.cc2
-rw-r--r--chromium/components/data_use_measurement/core/data_use_network_delegate.cc5
-rw-r--r--chromium/components/data_use_measurement/core/data_use_network_delegate.h6
-rw-r--r--chromium/components/data_use_measurement/core/data_use_network_delegate_unittest.cc5
-rw-r--r--chromium/components/data_use_measurement/core/data_use_user_data.h2
-rw-r--r--chromium/components/discardable_memory/OWNERS1
-rw-r--r--chromium/components/discardable_memory/common/discardable_shared_memory_heap_unittest.cc2
-rw-r--r--chromium/components/dom_distiller/DEPS2
-rw-r--r--chromium/components/dom_distiller/core/BUILD.gn3
-rw-r--r--chromium/components/dom_distiller/core/distiller_unittest.cc1
-rw-r--r--chromium/components/dom_distiller/core/distiller_url_fetcher.cc67
-rw-r--r--chromium/components/dom_distiller/core/distiller_url_fetcher.h36
-rw-r--r--chromium/components/dom_distiller/core/distiller_url_fetcher_unittest.cc37
-rw-r--r--chromium/components/dom_distiller/standalone/content_extractor_browsertest.cc2
-rw-r--r--chromium/components/dom_distiller/webui/resources/about_dom_distiller.html1
-rw-r--r--chromium/components/domain_reliability/OWNERS3
-rw-r--r--chromium/components/domain_reliability/quic_error_mapping.cc236
-rw-r--r--chromium/components/domain_reliability/quic_error_mapping.h2
-rw-r--r--chromium/components/domain_reliability/service.cc19
-rw-r--r--chromium/components/domain_reliability/service_unittest.cc11
-rw-r--r--chromium/components/domain_reliability/util.cc7
-rw-r--r--chromium/components/download/BUILD.gn2
-rw-r--r--chromium/components/download/database/BUILD.gn64
-rw-r--r--chromium/components/download/database/DEPS (renamed from chromium/components/download/downloader/in_progress/DEPS)1
-rw-r--r--chromium/components/download/database/download_db.h51
-rw-r--r--chromium/components/download/database/download_db_conversions.cc (renamed from chromium/components/download/downloader/in_progress/in_progress_conversions.cc)157
-rw-r--r--chromium/components/download/database/download_db_conversions.h71
-rw-r--r--chromium/components/download/database/download_db_conversions_unittest.cc (renamed from chromium/components/download/downloader/in_progress/in_progress_conversions_unittest.cc)37
-rw-r--r--chromium/components/download/database/download_db_entry.cc (renamed from chromium/components/download/downloader/in_progress/download_db_entry.cc)14
-rw-r--r--chromium/components/download/database/download_db_entry.h (renamed from chromium/components/download/downloader/in_progress/download_db_entry.h)14
-rw-r--r--chromium/components/download/database/download_db_impl.cc173
-rw-r--r--chromium/components/download/database/download_db_impl.h90
-rw-r--r--chromium/components/download/database/download_db_impl_unittest.cc250
-rw-r--r--chromium/components/download/database/download_info.cc (renamed from chromium/components/download/downloader/in_progress/download_info.cc)4
-rw-r--r--chromium/components/download/database/download_info.h (renamed from chromium/components/download/downloader/in_progress/download_info.h)14
-rw-r--r--chromium/components/download/database/download_namespace.cc30
-rw-r--r--chromium/components/download/database/download_namespace.h30
-rw-r--r--chromium/components/download/database/in_progress/download_entry.cc (renamed from chromium/components/download/downloader/in_progress/download_entry.cc)2
-rw-r--r--chromium/components/download/database/in_progress/download_entry.h (renamed from chromium/components/download/downloader/in_progress/download_entry.h)6
-rw-r--r--chromium/components/download/database/in_progress/in_progress_cache.h (renamed from chromium/components/download/downloader/in_progress/in_progress_cache.h)8
-rw-r--r--chromium/components/download/database/in_progress/in_progress_cache_impl.cc (renamed from chromium/components/download/downloader/in_progress/in_progress_cache_impl.cc)23
-rw-r--r--chromium/components/download/database/in_progress/in_progress_cache_impl.h (renamed from chromium/components/download/downloader/in_progress/in_progress_cache_impl.h)14
-rw-r--r--chromium/components/download/database/in_progress/in_progress_cache_impl_unittest.cc (renamed from chromium/components/download/downloader/in_progress/in_progress_cache_impl_unittest.cc)2
-rw-r--r--chromium/components/download/database/in_progress/in_progress_info.cc (renamed from chromium/components/download/downloader/in_progress/in_progress_info.cc)15
-rw-r--r--chromium/components/download/database/in_progress/in_progress_info.h (renamed from chromium/components/download/downloader/in_progress/in_progress_info.h)36
-rw-r--r--chromium/components/download/database/in_progress/ukm_info.cc (renamed from chromium/components/download/downloader/in_progress/ukm_info.cc)2
-rw-r--r--chromium/components/download/database/in_progress/ukm_info.h (renamed from chromium/components/download/downloader/in_progress/ukm_info.h)6
-rw-r--r--chromium/components/download/database/proto/BUILD.gn (renamed from chromium/components/download/downloader/in_progress/proto/BUILD.gn)2
-rw-r--r--chromium/components/download/database/proto/download_entry.proto (renamed from chromium/components/download/downloader/in_progress/proto/download_entry.proto)58
-rw-r--r--chromium/components/download/database/proto/download_source.proto (renamed from chromium/components/download/downloader/in_progress/proto/download_source.proto)2
-rw-r--r--chromium/components/download/database/switches.cc17
-rw-r--r--chromium/components/download/database/switches.h19
-rw-r--r--chromium/components/download/downloader/in_progress/BUILD.gn54
-rw-r--r--chromium/components/download/downloader/in_progress/in_progress_conversions.h70
-rw-r--r--chromium/components/download/internal/background_service/controller.h2
-rw-r--r--chromium/components/download/internal/background_service/controller_impl.cc12
-rw-r--r--chromium/components/download/internal/background_service/controller_impl.h4
-rw-r--r--chromium/components/download/internal/background_service/controller_impl_unittest.cc14
-rw-r--r--chromium/components/download/internal/background_service/download_service_impl.cc49
-rw-r--r--chromium/components/download/internal/background_service/download_service_impl.h6
-rw-r--r--chromium/components/download/internal/background_service/download_service_impl_unittest.cc2
-rw-r--r--chromium/components/download/internal/background_service/empty_task_scheduler.cc4
-rw-r--r--chromium/components/download/internal/background_service/empty_task_scheduler.h7
-rw-r--r--chromium/components/download/internal/background_service/in_memory_download.cc22
-rw-r--r--chromium/components/download/internal/background_service/in_memory_download.h16
-rw-r--r--chromium/components/download/internal/background_service/in_memory_download_driver.cc1
-rw-r--r--chromium/components/download/internal/background_service/in_memory_download_unittest.cc44
-rw-r--r--chromium/components/download/internal/background_service/model_impl_unittest.cc2
-rw-r--r--chromium/components/download/internal/background_service/scheduler/scheduler_impl_unittest.cc3
-rw-r--r--chromium/components/download/internal/common/BUILD.gn10
-rw-r--r--chromium/components/download/internal/common/DEPS1
-rw-r--r--chromium/components/download/internal/common/OWNERS2
-rw-r--r--chromium/components/download/internal/common/download_create_info.cc4
-rw-r--r--chromium/components/download/internal/common/download_db_cache.cc246
-rw-r--r--chromium/components/download/internal/common/download_db_cache.h88
-rw-r--r--chromium/components/download/internal/common/download_db_cache_unittest.cc236
-rw-r--r--chromium/components/download/internal/common/download_item_impl.cc31
-rw-r--r--chromium/components/download/internal/common/download_item_impl_delegate.cc6
-rw-r--r--chromium/components/download/internal/common/download_item_impl_unittest.cc128
-rw-r--r--chromium/components/download/internal/common/download_response_handler.cc28
-rw-r--r--chromium/components/download/internal/common/download_stats.cc5
-rw-r--r--chromium/components/download/internal/common/download_stats_unittest.cc2
-rw-r--r--chromium/components/download/internal/common/download_task_runner.cc15
-rw-r--r--chromium/components/download/internal/common/download_ukm_helper.cc7
-rw-r--r--chromium/components/download/internal/common/download_utils.cc61
-rw-r--r--chromium/components/download/internal/common/in_progress_download_manager.cc251
-rw-r--r--chromium/components/download/internal/common/resource_downloader.cc44
-rw-r--r--chromium/components/download/internal/common/resource_downloader.h16
-rw-r--r--chromium/components/download/internal/common/stream_handle_input_stream.cc5
-rw-r--r--chromium/components/download/internal/common/url_download_handler_factory.cc4
-rw-r--r--chromium/components/download/internal/common/url_download_request_handle.cc47
-rw-r--r--chromium/components/download/public/background_service/download_service.h4
-rw-r--r--chromium/components/download/public/background_service/task_scheduler.h6
-rw-r--r--chromium/components/download/public/common/BUILD.gn6
-rw-r--r--chromium/components/download/public/common/download_create_info.h9
-rw-r--r--chromium/components/download/public/common/download_interrupt_reason_values.h5
-rw-r--r--chromium/components/download/public/common/download_item_impl.h2
-rw-r--r--chromium/components/download/public/common/download_item_impl_delegate.h4
-rw-r--r--chromium/components/download/public/common/download_response_handler.h12
-rw-r--r--chromium/components/download/public/common/download_save_info.cc15
-rw-r--r--chromium/components/download/public/common/download_save_info.h12
-rw-r--r--chromium/components/download/public/common/download_start_observer.h24
-rw-r--r--chromium/components/download/public/common/download_ukm_helper.h6
-rw-r--r--chromium/components/download/public/common/download_url_parameters.cc1
-rw-r--r--chromium/components/download/public/common/download_url_parameters.h11
-rw-r--r--chromium/components/download/public/common/download_utils.h15
-rw-r--r--chromium/components/download/public/common/in_progress_download_manager.h96
-rw-r--r--chromium/components/download/public/common/url_download_handler.h9
-rw-r--r--chromium/components/download/public/common/url_download_request_handle.h40
-rw-r--r--chromium/components/download/quarantine/quarantine_win_unittest.cc2
-rw-r--r--chromium/components/drive/BUILD.gn5
-rw-r--r--chromium/components/embedder_support/android/BUILD.gn (renamed from chromium/components/web_contents_delegate_android/BUILD.gn)96
-rw-r--r--chromium/components/embedder_support/android/DEPS (renamed from chromium/components/web_contents_delegate_android/java/DEPS)7
-rw-r--r--chromium/components/embedder_support/android/OWNERS (renamed from chromium/components/content_view/OWNERS)0
-rw-r--r--chromium/components/embedder_support/android/README2
-rw-r--r--chromium/components/embedder_support/android/delegate/DEPS (renamed from chromium/components/web_contents_delegate_android/DEPS)0
-rw-r--r--chromium/components/embedder_support/android/delegate/OWNERS2
-rw-r--r--chromium/components/embedder_support/android/delegate/color_chooser_android.cc (renamed from chromium/components/web_contents_delegate_android/color_chooser_android.cc)5
-rw-r--r--chromium/components/embedder_support/android/delegate/color_chooser_android.h (renamed from chromium/components/web_contents_delegate_android/color_chooser_android.h)6
-rw-r--r--chromium/components/embedder_support/android/delegate/web_contents_delegate_android.cc (renamed from chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc)45
-rw-r--r--chromium/components/embedder_support/android/delegate/web_contents_delegate_android.h (renamed from chromium/components/web_contents_delegate_android/web_contents_delegate_android.h)18
-rw-r--r--chromium/components/embedder_support/android/java/res/drawable-hdpi/color_picker_advanced_select_handle.png (renamed from chromium/components/web_contents_delegate_android/java/res/drawable-hdpi/color_picker_advanced_select_handle.png)bin1311 -> 1311 bytes
-rw-r--r--chromium/components/embedder_support/android/java/res/drawable-mdpi/color_picker_advanced_select_handle.png (renamed from chromium/components/web_contents_delegate_android/java/res/drawable-mdpi/color_picker_advanced_select_handle.png)bin761 -> 761 bytes
-rw-r--r--chromium/components/embedder_support/android/java/res/drawable-xhdpi/color_picker_advanced_select_handle.png (renamed from chromium/components/web_contents_delegate_android/java/res/drawable-xhdpi/color_picker_advanced_select_handle.png)bin1827 -> 1827 bytes
-rw-r--r--chromium/components/embedder_support/android/java/res/drawable/color_button_background.xml (renamed from chromium/components/web_contents_delegate_android/java/res/drawable/color_button_background.xml)0
-rw-r--r--chromium/components/embedder_support/android/java/res/drawable/color_picker_border.xml (renamed from chromium/components/web_contents_delegate_android/java/res/drawable/color_picker_border.xml)0
-rw-r--r--chromium/components/embedder_support/android/java/res/layout/color_picker_advanced_component.xml (renamed from chromium/components/web_contents_delegate_android/java/res/layout/color_picker_advanced_component.xml)0
-rw-r--r--chromium/components/embedder_support/android/java/res/layout/color_picker_dialog_content.xml (renamed from chromium/components/web_contents_delegate_android/java/res/layout/color_picker_dialog_content.xml)6
-rw-r--r--chromium/components/embedder_support/android/java/res/layout/color_picker_dialog_title.xml (renamed from chromium/components/web_contents_delegate_android/java/res/layout/color_picker_dialog_title.xml)0
-rw-r--r--chromium/components/embedder_support/android/java/res/values/colors.xml (renamed from chromium/components/web_contents_delegate_android/java/res/values/colors.xml)0
-rw-r--r--chromium/components/embedder_support/android/java/res/values/dimens.xml (renamed from chromium/components/web_contents_delegate_android/java/res/values/dimens.xml)0
-rw-r--r--chromium/components/embedder_support/android/java/src/org/chromium/components/embedder_support/delegate/DEPS (renamed from chromium/components/content_view/DEPS)0
-rw-r--r--chromium/components/embedder_support/android/java/src/org/chromium/components/embedder_support/delegate/OWNERS (renamed from chromium/components/web_contents_delegate_android/OWNERS)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_am.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_am.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ar.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ar.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_bg.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_bg.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ca.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ca.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_cs.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_cs.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_da.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_da.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_de.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_de.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_el.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_el.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_en-GB.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_en-GB.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_es-419.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_es-419.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_es.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_es.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_fa.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_fa.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_fi.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_fi.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_fil.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_fil.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_fr.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_fr.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_hi.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_hi.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_hr.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_hr.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_hu.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_hu.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_id.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_id.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_it.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_it.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_iw.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_iw.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ja.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ja.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ko.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ko.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_lt.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_lt.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_lv.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_lv.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_nl.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_nl.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_no.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_no.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_pl.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_pl.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_pt-BR.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_pt-BR.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_pt-PT.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_pt-PT.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ro.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ro.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ru.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ru.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sk.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sk.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sl.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sl.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sr.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sr.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sv.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sv.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sw.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sw.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_th.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_th.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_tr.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_tr.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_uk.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_uk.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_vi.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_vi.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_zh-CN.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_zh-CN.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_zh-TW.xtb (renamed from chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_zh-TW.xtb)0
-rw-r--r--chromium/components/embedder_support/android/java/strings/web_contents_delegate_android_strings.grd (renamed from chromium/components/web_contents_delegate_android/java/strings/web_contents_delegate_android_strings.grd)0
-rw-r--r--chromium/components/embedder_support/android/view/content_view_render_view.cc137
-rw-r--r--chromium/components/embedder_support/android/view/content_view_render_view.h78
-rw-r--r--chromium/components/encrypted_messages/message_encrypter.cc9
-rw-r--r--chromium/components/error_page/OWNERS4
-rw-r--r--chromium/components/error_page/common/localized_error.cc4
-rw-r--r--chromium/components/exo/BUILD.gn5
-rw-r--r--chromium/components/exo/DEPS2
-rw-r--r--chromium/components/exo/OWNERS2
-rw-r--r--chromium/components/exo/buffer.cc22
-rw-r--r--chromium/components/exo/buffer_unittest.cc2
-rw-r--r--chromium/components/exo/client_controlled_shell_surface.cc148
-rw-r--r--chromium/components/exo/client_controlled_shell_surface.h13
-rw-r--r--chromium/components/exo/client_controlled_shell_surface_unittest.cc434
-rw-r--r--chromium/components/exo/display.cc26
-rw-r--r--chromium/components/exo/display.h9
-rw-r--r--chromium/components/exo/display_unittest.cc2
-rw-r--r--chromium/components/exo/input_method_surface.cc39
-rw-r--r--chromium/components/exo/input_method_surface.h37
-rw-r--r--chromium/components/exo/input_method_surface_manager.h28
-rw-r--r--chromium/components/exo/keyboard.cc63
-rw-r--r--chromium/components/exo/keyboard.h11
-rw-r--r--chromium/components/exo/keyboard_delegate.h2
-rw-r--r--chromium/components/exo/keyboard_unittest.cc75
-rw-r--r--chromium/components/exo/layer_tree_frame_sink_holder.cc23
-rw-r--r--chromium/components/exo/layer_tree_frame_sink_holder.h13
-rw-r--r--chromium/components/exo/notification_surface.cc4
-rw-r--r--chromium/components/exo/pointer.cc47
-rw-r--r--chromium/components/exo/pointer.h4
-rw-r--r--chromium/components/exo/pointer_unittest.cc80
-rw-r--r--chromium/components/exo/seat.cc51
-rw-r--r--chromium/components/exo/seat.h28
-rw-r--r--chromium/components/exo/shared_memory.cc2
-rw-r--r--chromium/components/exo/shell_surface.cc86
-rw-r--r--chromium/components/exo/shell_surface.h16
-rw-r--r--chromium/components/exo/shell_surface_base.cc420
-rw-r--r--chromium/components/exo/shell_surface_base.h67
-rw-r--r--chromium/components/exo/shell_surface_unittest.cc120
-rw-r--r--chromium/components/exo/surface.cc3
-rw-r--r--chromium/components/exo/surface.h4
-rw-r--r--chromium/components/exo/surface_tree_host.cc111
-rw-r--r--chromium/components/exo/surface_tree_host.h41
-rw-r--r--chromium/components/exo/surface_unittest.cc67
-rw-r--r--chromium/components/exo/touch.cc9
-rw-r--r--chromium/components/exo/touch.h3
-rw-r--r--chromium/components/exo/wayland/BUILD.gn8
-rw-r--r--chromium/components/exo/wayland/clients/client_base.cc4
-rw-r--r--chromium/components/exo/wayland/clients/client_base.h1
-rw-r--r--chromium/components/exo/wayland/clients/client_helper.cc1
-rw-r--r--chromium/components/exo/wayland/clients/client_helper.h1
-rw-r--r--chromium/components/exo/wayland/server.cc312
-rw-r--r--chromium/components/exo/xdg_shell_surface.h11
-rw-r--r--chromium/components/favicon/content/content_favicon_driver.cc2
-rw-r--r--chromium/components/favicon/content/content_favicon_driver_unittest.cc2
-rw-r--r--chromium/components/favicon/core/favicon_handler_unittest.cc2
-rw-r--r--chromium/components/favicon/core/large_icon_service_unittest.cc2
-rw-r--r--chromium/components/favicon/ios/DEPS1
-rw-r--r--chromium/components/favicon/ios/web_favicon_driver.mm5
-rw-r--r--chromium/components/feature_engagement/README.md21
-rw-r--r--chromium/components/feature_engagement/internal/android/tracker_impl_android.cc9
-rw-r--r--chromium/components/feature_engagement/internal/chrome_variations_configuration_unittest.cc2
-rw-r--r--chromium/components/feature_engagement/internal/feature_config_condition_validator.cc4
-rw-r--r--chromium/components/feature_engagement/internal/persistent_event_store_unittest.cc2
-rw-r--r--chromium/components/feature_engagement/internal/tracker_impl.cc6
-rw-r--r--chromium/components/feature_engagement/internal/tracker_impl_unittest.cc47
-rw-r--r--chromium/components/feature_engagement/public/event_constants.cc1
-rw-r--r--chromium/components/feature_engagement/public/event_constants.h3
-rw-r--r--chromium/components/feature_engagement/public/feature_constants.cc6
-rw-r--r--chromium/components/feature_engagement/public/feature_constants.h2
-rw-r--r--chromium/components/feature_engagement/public/feature_list.cc2
-rw-r--r--chromium/components/feature_engagement/public/feature_list.h5
-rw-r--r--chromium/components/feature_engagement/public/tracker.h2
-rw-r--r--chromium/components/feed/DEPS2
-rw-r--r--chromium/components/feed/OWNERS1
-rw-r--r--chromium/components/feed/core/BUILD.gn29
-rw-r--r--chromium/components/feed/core/feed_host_service.cc16
-rw-r--r--chromium/components/feed/core/feed_host_service.h10
-rw-r--r--chromium/components/feed/core/feed_image_database.cc10
-rw-r--r--chromium/components/feed/core/feed_image_database.h11
-rw-r--r--chromium/components/feed/core/feed_image_database_unittest.cc8
-rw-r--r--chromium/components/feed/core/feed_image_manager.cc12
-rw-r--r--chromium/components/feed/core/feed_image_manager.h6
-rw-r--r--chromium/components/feed/core/feed_image_manager_unittest.cc46
-rw-r--r--chromium/components/feed/core/feed_networking_host.cc38
-rw-r--r--chromium/components/feed/core/feed_networking_host_unittest.cc52
-rw-r--r--chromium/components/feed/core/feed_scheduler_host.cc403
-rw-r--r--chromium/components/feed/core/feed_scheduler_host.h189
-rw-r--r--chromium/components/feed/core/feed_scheduler_host_unittest.cc877
-rw-r--r--chromium/components/feed/core/feed_storage_database.cc434
-rw-r--r--chromium/components/feed/core/feed_storage_database.h171
-rw-r--r--chromium/components/feed/core/feed_storage_database_unittest.cc607
-rw-r--r--chromium/components/feed/core/pref_names.cc27
-rw-r--r--chromium/components/feed/core/pref_names.h35
-rw-r--r--chromium/components/feed/core/proto/BUILD.gn1
-rw-r--r--chromium/components/feed/core/proto/feed_storage.proto20
-rw-r--r--chromium/components/feed/core/user_classifier.cc364
-rw-r--r--chromium/components/feed/core/user_classifier.h111
-rw-r--r--chromium/components/feed/core/user_classifier_unittest.cc310
-rw-r--r--chromium/components/feed/features.gni3
-rw-r--r--chromium/components/feedback/BUILD.gn5
-rw-r--r--chromium/components/feedback/DEPS3
-rw-r--r--chromium/components/feedback/feedback_data.cc10
-rw-r--r--chromium/components/feedback/feedback_data_unittest.cc30
-rw-r--r--chromium/components/feedback/feedback_report.cc31
-rw-r--r--chromium/components/feedback/feedback_report.h17
-rw-r--r--chromium/components/feedback/feedback_uploader.cc127
-rw-r--r--chromium/components/feedback/feedback_uploader.h34
-rw-r--r--chromium/components/feedback/feedback_uploader_delegate.cc71
-rw-r--r--chromium/components/feedback/feedback_uploader_delegate.h43
-rw-r--r--chromium/components/feedback/feedback_uploader_dispatch_unittest.cc116
-rw-r--r--chromium/components/feedback/feedback_uploader_factory.cc7
-rw-r--r--chromium/components/feedback/feedback_uploader_unittest.cc28
-rw-r--r--chromium/components/flags_ui/resources/flags.html4
-rw-r--r--chromium/components/gcm_driver/BUILD.gn9
-rw-r--r--chromium/components/google/core/browser/OWNERS1
-rw-r--r--chromium/components/guest_view/browser/guest_view_base.cc17
-rw-r--r--chromium/components/guest_view/browser/guest_view_base.h11
-rw-r--r--chromium/components/guest_view/browser/guest_view_manager.cc6
-rw-r--r--chromium/components/guest_view/browser/guest_view_manager.h4
-rw-r--r--chromium/components/heap_profiling/supervisor.cc21
-rw-r--r--chromium/components/heap_profiling/test_driver.cc41
-rw-r--r--chromium/components/history/DEPS3
-rw-r--r--chromium/components/history/content/browser/content_history_backend_db_unittest.cc1
-rw-r--r--chromium/components/history/core/browser/BUILD.gn4
-rw-r--r--chromium/components/history/core/browser/browsing_history_service.cc2
-rw-r--r--chromium/components/history/core/browser/browsing_history_service.h4
-rw-r--r--chromium/components/history/core/browser/browsing_history_service_unittest.cc13
-rw-r--r--chromium/components/history/core/browser/domain_mixing_metrics_unittest.cc4
-rw-r--r--chromium/components/history/core/browser/history_backend.cc60
-rw-r--r--chromium/components/history/core/browser/history_backend.h20
-rw-r--r--chromium/components/history/core/browser/history_backend_db_unittest.cc112
-rw-r--r--chromium/components/history/core/browser/history_backend_unittest.cc217
-rw-r--r--chromium/components/history/core/browser/history_database.cc14
-rw-r--r--chromium/components/history/core/browser/history_querying_unittest.cc6
-rw-r--r--chromium/components/history/core/browser/history_service.h4
-rw-r--r--chromium/components/history/core/browser/history_service_unittest.cc34
-rw-r--r--chromium/components/history/core/browser/history_types.cc9
-rw-r--r--chromium/components/history/core/browser/history_types.h10
-rw-r--r--chromium/components/history/core/browser/thumbnail_database.cc48
-rw-r--r--chromium/components/history/core/browser/thumbnail_database_unittest.cc8
-rw-r--r--chromium/components/history/core/browser/top_sites_database.cc10
-rw-r--r--chromium/components/history/core/browser/top_sites_database_unittest.cc4
-rw-r--r--chromium/components/history/core/browser/typed_url_model_type_controller.cc1
-rw-r--r--chromium/components/history/core/browser/typed_url_sync_bridge.cc15
-rw-r--r--chromium/components/history/core/browser/typed_url_sync_bridge.h2
-rw-r--r--chromium/components/history/core/browser/typed_url_sync_bridge_unittest.cc8
-rw-r--r--chromium/components/history/core/browser/typed_url_sync_metadata_database.cc43
-rw-r--r--chromium/components/history/core/browser/typed_url_sync_metadata_database.h9
-rw-r--r--chromium/components/history/core/browser/url_database.cc11
-rw-r--r--chromium/components/history/core/browser/visit_database.cc12
-rw-r--r--chromium/components/history/core/browser/visit_database.h4
-rw-r--r--chromium/components/history/core/browser/web_history_service.cc152
-rw-r--r--chromium/components/history/core/browser/web_history_service.h8
-rw-r--r--chromium/components/history/core/browser/web_history_service_unittest.cc18
-rw-r--r--chromium/components/history/core/test/BUILD.gn1
-rw-r--r--chromium/components/history_strings.grdp2
-rw-r--r--chromium/components/image_fetcher/DEPS2
-rw-r--r--chromium/components/image_fetcher/core/BUILD.gn3
-rw-r--r--chromium/components/image_fetcher/core/image_data_fetcher.cc143
-rw-r--r--chromium/components/image_fetcher/core/image_data_fetcher.h72
-rw-r--r--chromium/components/image_fetcher/core/image_data_fetcher_unittest.cc235
-rw-r--r--chromium/components/image_fetcher/core/image_fetcher.h21
-rw-r--r--chromium/components/image_fetcher/core/image_fetcher_impl.cc8
-rw-r--r--chromium/components/image_fetcher/core/image_fetcher_impl.h11
-rw-r--r--chromium/components/image_fetcher/core/image_fetcher_impl_unittest.cc105
-rw-r--r--chromium/components/image_fetcher/core/image_fetcher_types.h51
-rw-r--r--chromium/components/image_fetcher/ios/BUILD.gn4
-rw-r--r--chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper.h28
-rw-r--r--chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper.mm24
-rw-r--r--chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper_unittest.mm111
-rw-r--r--chromium/components/image_fetcher/ios/ios_image_decoder_impl.mm8
-rw-r--r--chromium/components/infobars/core/infobar_container.cc4
-rw-r--r--chromium/components/infobars/core/infobar_delegate.h3
-rw-r--r--chromium/components/invalidation/impl/BUILD.gn32
-rw-r--r--chromium/components/invalidation/public/BUILD.gn5
-rw-r--r--chromium/components/json_schema/OWNERS4
-rw-r--r--chromium/components/json_schema/json_schema_constants.cc1
-rw-r--r--chromium/components/json_schema/json_schema_constants.h1
-rw-r--r--chromium/components/json_schema/json_schema_validator.cc14
-rw-r--r--chromium/components/json_schema/json_schema_validator_unittest.cc18
-rw-r--r--chromium/components/keep_alive_registry/OWNERS2
-rw-r--r--chromium/components/keyed_service/OWNERS2
-rw-r--r--chromium/components/keyed_service/content/browser_context_dependency_manager.cc5
-rw-r--r--chromium/components/keyed_service/content/browser_context_dependency_manager.h4
-rw-r--r--chromium/components/language/core/browser/BUILD.gn2
-rw-r--r--chromium/components/language/core/browser/language_model.h4
-rw-r--r--chromium/components/language/core/browser/language_model_manager.cc25
-rw-r--r--chromium/components/language/core/browser/language_model_manager.h35
-rw-r--r--chromium/components/language/core/browser/pref_names.cc3
-rw-r--r--chromium/components/language/core/browser/pref_names.h6
-rw-r--r--chromium/components/leveldb_proto/leveldb_database.cc34
-rw-r--r--chromium/components/leveldb_proto/leveldb_database.h2
-rw-r--r--chromium/components/leveldb_proto/proto_database.h9
-rw-r--r--chromium/components/leveldb_proto/proto_database_impl.h40
-rw-r--r--chromium/components/leveldb_proto/proto_database_impl_unittest.cc38
-rw-r--r--chromium/components/leveldb_proto/testing/fake_db.h24
-rw-r--r--chromium/components/metrics/BUILD.gn10
-rw-r--r--chromium/components/metrics/call_stack_profile_builder.cc106
-rw-r--r--chromium/components/metrics/call_stack_profile_builder.h77
-rw-r--r--chromium/components/metrics/call_stack_profile_builder_unittest.cc159
-rw-r--r--chromium/components/metrics/call_stack_profile_collector.cc12
-rw-r--r--chromium/components/metrics/call_stack_profile_collector.h2
-rw-r--r--chromium/components/metrics/call_stack_profile_metrics_provider.cc138
-rw-r--r--chromium/components/metrics/call_stack_profile_metrics_provider.h23
-rw-r--r--chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc521
-rw-r--r--chromium/components/metrics/child_call_stack_profile_collector.cc34
-rw-r--r--chromium/components/metrics/child_call_stack_profile_collector.h48
-rw-r--r--chromium/components/metrics/child_call_stack_profile_collector_unittest.cc85
-rw-r--r--chromium/components/metrics/generate_expired_histograms_array.gni2
-rw-r--r--chromium/components/metrics/gpu/gpu_metrics_provider.cc9
-rw-r--r--chromium/components/metrics/metrics_log.cc5
-rw-r--r--chromium/components/metrics/metrics_log_unittest.cc6
-rw-r--r--chromium/components/metrics/metrics_state_manager_unittest.cc2
-rw-r--r--chromium/components/metrics/net/net_metrics_log_uploader.cc7
-rw-r--r--chromium/components/metrics/net/net_metrics_log_uploader_unittest.cc16
-rw-r--r--chromium/components/metrics/net/network_metrics_provider.cc4
-rw-r--r--chromium/components/metrics/net/network_metrics_provider.h2
-rw-r--r--chromium/components/metrics/public/cpp/OWNERS2
-rw-r--r--chromium/components/metrics/public/cpp/call_stack_profile.typemap2
-rw-r--r--chromium/components/metrics/public/cpp/call_stack_profile_struct_traits.h16
-rw-r--r--chromium/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc134
-rw-r--r--chromium/components/metrics/public/interfaces/call_stack_profile_collector.mojom2
-rw-r--r--chromium/components/metrics/single_sample_metrics_factory_impl_unittest.cc2
-rw-r--r--chromium/components/metrics/stability_metrics_helper.cc18
-rw-r--r--chromium/components/metrics/stability_metrics_helper.h7
-rw-r--r--chromium/components/metrics/stability_metrics_helper_unittest.cc2
-rw-r--r--chromium/components/metrics/stability_metrics_provider_unittest.cc2
-rw-r--r--chromium/components/minidump_uploader/OWNERS4
-rw-r--r--chromium/components/mirroring/service/BUILD.gn14
-rw-r--r--chromium/components/mirroring/service/captured_audio_input.cc96
-rw-r--r--chromium/components/mirroring/service/captured_audio_input.h64
-rw-r--r--chromium/components/mirroring/service/captured_audio_input_unittest.cc177
-rw-r--r--chromium/components/mirroring/service/interface.h28
-rw-r--r--chromium/components/mirroring/service/media_remoter.cc195
-rw-r--r--chromium/components/mirroring/service/media_remoter.h147
-rw-r--r--chromium/components/mirroring/service/media_remoter_unittest.cc220
-rw-r--r--chromium/components/mirroring/service/message_dispatcher.cc7
-rw-r--r--chromium/components/mirroring/service/message_dispatcher.h2
-rw-r--r--chromium/components/mirroring/service/message_dispatcher_unittest.cc7
-rw-r--r--chromium/components/mirroring/service/mirror_settings.cc10
-rw-r--r--chromium/components/mirroring/service/mirror_settings.h7
-rw-r--r--chromium/components/mirroring/service/remoting_sender.cc216
-rw-r--r--chromium/components/mirroring/service/remoting_sender.h120
-rw-r--r--chromium/components/mirroring/service/remoting_sender_unittest.cc615
-rw-r--r--chromium/components/mirroring/service/rtp_stream.cc7
-rw-r--r--chromium/components/mirroring/service/rtp_stream.h14
-rw-r--r--chromium/components/mirroring/service/session.cc513
-rw-r--r--chromium/components/mirroring/service/session.h65
-rw-r--r--chromium/components/mirroring/service/session_monitor.cc17
-rw-r--r--chromium/components/mirroring/service/session_monitor.h8
-rw-r--r--chromium/components/mirroring/service/session_monitor_unittest.cc22
-rw-r--r--chromium/components/mirroring/service/session_unittest.cc352
-rw-r--r--chromium/components/nacl/broker/BUILD.gn3
-rw-r--r--chromium/components/nacl/browser/BUILD.gn1
-rw-r--r--chromium/components/nacl/common/BUILD.gn2
-rw-r--r--chromium/components/nacl/loader/BUILD.gn16
-rw-r--r--chromium/components/navigation_interception/BUILD.gn1
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_throttle.cc112
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_throttle.h46
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_throttle_unittest.cc86
-rw-r--r--chromium/components/navigation_metrics/OWNERS3
-rw-r--r--chromium/components/navigation_metrics/navigation_metrics_unittest.cc2
-rw-r--r--chromium/components/net_log/net_export_file_writer_unittest.cc5
-rw-r--r--chromium/components/neterror/OWNERS1
-rw-r--r--chromium/components/network_hints/OWNERS2
-rw-r--r--chromium/components/network_session_configurator/browser/network_session_configurator.cc39
-rw-r--r--chromium/components/network_session_configurator/browser/network_session_configurator.h2
-rw-r--r--chromium/components/network_session_configurator/browser/network_session_configurator_unittest.cc99
-rw-r--r--chromium/components/network_time/BUILD.gn2
-rw-r--r--chromium/components/network_time/DEPS2
-rw-r--r--chromium/components/network_time/network_time_tracker.cc108
-rw-r--r--chromium/components/network_time/network_time_tracker.h37
-rw-r--r--chromium/components/network_time/network_time_tracker_unittest.cc25
-rw-r--r--chromium/components/ntp_snippets/BUILD.gn2
-rw-r--r--chromium/components/ntp_snippets/breaking_news/breaking_news_gcm_app_handler_unittest.cc2
-rw-r--r--chromium/components/ntp_snippets/breaking_news/subscription_json_request.cc119
-rw-r--r--chromium/components/ntp_snippets/breaking_news/subscription_json_request.h37
-rw-r--r--chromium/components/ntp_snippets/breaking_news/subscription_json_request_unittest.cc104
-rw-r--r--chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.cc37
-rw-r--r--chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.h14
-rw-r--r--chromium/components/ntp_snippets/breaking_news/subscription_manager_impl_unittest.cc79
-rw-r--r--chromium/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc2
-rw-r--r--chromium/components/ntp_snippets/content_suggestions_metrics_unittest.cc2
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service.cc46
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service.h23
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy_unittest.cc13
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service_unittest.cc117
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestion.cc6
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestion.h6
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_cache.cc41
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_cache.h44
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.cc19
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.h6
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_features.cc8
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_features.h3
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_fetch.cc78
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_fetch.h4
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_fetcher_impl_unittest.cc119
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.cc7
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h2
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter_unittest.cc51
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_reporter.cc5
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_reporter.h6
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_result.cc38
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_result.h34
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_test_utils.cc16
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_test_utils.h1
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry.cc4
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry.h1
-rw-r--r--chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry_unittest.cc21
-rw-r--r--chromium/components/ntp_snippets/contextual/proto/get_pivots_response.proto19
-rw-r--r--chromium/components/ntp_snippets/features.cc2
-rw-r--r--chromium/components/ntp_snippets/ntp_snippets_constants.cc3
-rw-r--r--chromium/components/ntp_snippets/remote/cached_image_fetcher.cc2
-rw-r--r--chromium/components/ntp_snippets/remote/cached_image_fetcher_unittest.cc37
-rw-r--r--chromium/components/ntp_snippets/remote/json_request.cc183
-rw-r--r--chromium/components/ntp_snippets/remote/json_request.h47
-rw-r--r--chromium/components/ntp_snippets/remote/json_request_unittest.cc16
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.cc75
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h31
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl_unittest.cc356
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc6
-rw-r--r--chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc98
-rw-r--r--chromium/components/ntp_snippets/remote/test_utils.cc17
-rw-r--r--chromium/components/ntp_snippets/remote/test_utils.h7
-rw-r--r--chromium/components/ntp_snippets/time_serialization.cc12
-rw-r--r--chromium/components/ntp_snippets/time_serialization.h4
-rw-r--r--chromium/components/ntp_snippets/time_serialization_unittest.cc13
-rw-r--r--chromium/components/ntp_snippets/user_classifier_unittest.cc2
-rw-r--r--chromium/components/ntp_snippets_strings.grdp2
-rw-r--r--chromium/components/ntp_tiles/BUILD.gn10
-rw-r--r--chromium/components/ntp_tiles/DEPS2
-rw-r--r--chromium/components/ntp_tiles/constants.cc27
-rw-r--r--chromium/components/ntp_tiles/constants.h13
-rw-r--r--chromium/components/ntp_tiles/custom_links_manager.h72
-rw-r--r--chromium/components/ntp_tiles/custom_links_manager_impl.cc149
-rw-r--r--chromium/components/ntp_tiles/custom_links_manager_impl.h71
-rw-r--r--chromium/components/ntp_tiles/custom_links_manager_impl_unittest.cc400
-rw-r--r--chromium/components/ntp_tiles/custom_links_store.cc77
-rw-r--r--chromium/components/ntp_tiles/custom_links_store.h55
-rw-r--r--chromium/components/ntp_tiles/custom_links_store_unittest.cc83
-rw-r--r--chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc2
-rw-r--r--chromium/components/ntp_tiles/metrics.cc3
-rw-r--r--chromium/components/ntp_tiles/metrics_unittest.cc2
-rw-r--r--chromium/components/ntp_tiles/most_visited_sites.cc247
-rw-r--r--chromium/components/ntp_tiles/most_visited_sites.h88
-rw-r--r--chromium/components/ntp_tiles/most_visited_sites_unittest.cc495
-rw-r--r--chromium/components/ntp_tiles/popular_sites_impl.cc47
-rw-r--r--chromium/components/ntp_tiles/popular_sites_impl.h18
-rw-r--r--chromium/components/ntp_tiles/popular_sites_impl_unittest.cc60
-rw-r--r--chromium/components/ntp_tiles/pref_names.cc4
-rw-r--r--chromium/components/ntp_tiles/pref_names.h3
-rw-r--r--chromium/components/ntp_tiles/tile_source.h2
-rw-r--r--chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc6
-rw-r--r--chromium/components/nux/OWNERS1
-rw-r--r--chromium/components/nux/show_promo_delegate.h26
-rw-r--r--chromium/components/nux_google_apps/BUILD.gn35
-rw-r--r--chromium/components/nux_google_apps/DEPS10
-rw-r--r--chromium/components/nux_google_apps/OWNERS1
-rw-r--r--chromium/components/nux_google_apps/README5
-rw-r--r--chromium/components/nux_google_apps/constants.cc16
-rw-r--r--chromium/components/nux_google_apps/constants.h19
-rw-r--r--chromium/components/nux_google_apps/google_apps_handler.cc185
-rw-r--r--chromium/components/nux_google_apps/google_apps_handler.h71
-rw-r--r--chromium/components/nux_google_apps/resources/BUILD.gn27
-rw-r--r--chromium/components/nux_google_apps/resources/apps_chooser.html103
-rw-r--r--chromium/components/nux_google_apps/resources/apps_chooser.js93
-rw-r--r--chromium/components/nux_google_apps/resources/chrome_store_24dp_1x.pngbin0 -> 705 bytes
-rw-r--r--chromium/components/nux_google_apps/resources/chrome_store_24dp_2x.pngbin0 -> 1258 bytes
-rw-r--r--chromium/components/nux_google_apps/resources/gmail_24dp_1x.pngbin0 -> 818 bytes
-rw-r--r--chromium/components/nux_google_apps/resources/gmail_24dp_2x.pngbin0 -> 1618 bytes
-rw-r--r--chromium/components/nux_google_apps/resources/maps_24dp_1x.pngbin0 -> 1081 bytes
-rw-r--r--chromium/components/nux_google_apps/resources/maps_24dp_2x.pngbin0 -> 2061 bytes
-rw-r--r--chromium/components/nux_google_apps/resources/news_24dp_1x.pngbin0 -> 815 bytes
-rw-r--r--chromium/components/nux_google_apps/resources/news_24dp_2x.pngbin0 -> 1676 bytes
-rw-r--r--chromium/components/nux_google_apps/resources/nux_google_apps.html88
-rw-r--r--chromium/components/nux_google_apps/resources/nux_google_apps.js25
-rw-r--r--chromium/components/nux_google_apps/resources/nux_google_apps_proxy.html2
-rw-r--r--chromium/components/nux_google_apps/resources/nux_google_apps_proxy.js29
-rw-r--r--chromium/components/nux_google_apps/resources/translate_24dp_1x.pngbin0 -> 825 bytes
-rw-r--r--chromium/components/nux_google_apps/resources/translate_24dp_2x.pngbin0 -> 1847 bytes
-rw-r--r--chromium/components/nux_google_apps/resources/youtube_24dp_1x.pngbin0 -> 290 bytes
-rw-r--r--chromium/components/nux_google_apps/resources/youtube_24dp_2x.pngbin0 -> 512 bytes
-rw-r--r--chromium/components/nux_google_apps_strings.grdp12
-rw-r--r--chromium/components/offline_items_collection/core/android/offline_content_aggregator_bridge.cc8
-rw-r--r--chromium/components/offline_items_collection/core/fail_state.h3
-rw-r--r--chromium/components/offline_items_collection/core/offline_content_aggregator_unittest.cc2
-rw-r--r--chromium/components/offline_items_collection/core/offline_item.h6
-rw-r--r--chromium/components/offline_items_collection/core/offline_item_filter.h8
-rw-r--r--chromium/components/offline_items_collection/core/offline_item_state.h5
-rw-r--r--chromium/components/offline_items_collection/core/pending_state.h5
-rw-r--r--chromium/components/offline_items_collection/core/test_support/BUILD.gn5
-rw-r--r--chromium/components/offline_items_collection/core/test_support/offline_item_test_support.cc120
-rw-r--r--chromium/components/offline_pages/content/background_loader/background_loader_contents.cc8
-rw-r--r--chromium/components/offline_pages/content/background_loader/background_loader_contents.h2
-rw-r--r--chromium/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc12
-rw-r--r--chromium/components/offline_pages/content/renovations/render_frame_script_injector.cc18
-rw-r--r--chromium/components/offline_pages/core/BUILD.gn4
-rw-r--r--chromium/components/offline_pages/core/archive_manager.cc8
-rw-r--r--chromium/components/offline_pages/core/archive_manager_unittest.cc2
-rw-r--r--chromium/components/offline_pages/core/background/BUILD.gn1
-rw-r--r--chromium/components/offline_pages/core/background/request_coordinator.cc14
-rw-r--r--chromium/components/offline_pages/core/background/request_coordinator_unittest.cc2
-rw-r--r--chromium/components/offline_pages/core/background/request_queue_store_sql.cc47
-rw-r--r--chromium/components/offline_pages/core/background/request_queue_store_sql.h2
-rw-r--r--chromium/components/offline_pages/core/background/scheduler.h4
-rw-r--r--chromium/components/offline_pages/core/background/scheduler_stub.cc2
-rw-r--r--chromium/components/offline_pages/core/background/scheduler_stub.h6
-rw-r--r--chromium/components/offline_pages/core/downloads/BUILD.gn5
-rw-r--r--chromium/components/offline_pages/core/downloads/download_notifying_observer.cc123
-rw-r--r--chromium/components/offline_pages/core/downloads/download_notifying_observer.h65
-rw-r--r--chromium/components/offline_pages/core/downloads/download_notifying_observer_unittest.cc312
-rw-r--r--chromium/components/offline_pages/core/downloads/download_ui_adapter.cc482
-rw-r--r--chromium/components/offline_pages/core/downloads/download_ui_adapter.h104
-rw-r--r--chromium/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc200
-rw-r--r--chromium/components/offline_pages/core/downloads/offline_item_conversions.cc2
-rw-r--r--chromium/components/offline_pages/core/downloads/offline_item_conversions_unittest.cc20
-rw-r--r--chromium/components/offline_pages/core/downloads/offline_page_download_notifier.h45
-rw-r--r--chromium/components/offline_pages/core/model/add_page_task.cc4
-rw-r--r--chromium/components/offline_pages/core/model/add_page_task.h6
-rw-r--r--chromium/components/offline_pages/core/model/add_page_to_download_manager_task.cc10
-rw-r--r--chromium/components/offline_pages/core/model/add_page_to_download_manager_task.h6
-rw-r--r--chromium/components/offline_pages/core/model/cleanup_thumbnails_task.cc4
-rw-r--r--chromium/components/offline_pages/core/model/cleanup_thumbnails_task.h6
-rw-r--r--chromium/components/offline_pages/core/model/cleanup_thumbnails_task_unittest.cc2
-rw-r--r--chromium/components/offline_pages/core/model/clear_digest_task.cc6
-rw-r--r--chromium/components/offline_pages/core/model/clear_digest_task.h6
-rw-r--r--chromium/components/offline_pages/core/model/clear_storage_task.cc14
-rw-r--r--chromium/components/offline_pages/core/model/clear_storage_task.h6
-rw-r--r--chromium/components/offline_pages/core/model/clear_storage_task_unittest.cc2
-rw-r--r--chromium/components/offline_pages/core/model/complete_offline_page_upgrade_task.cc8
-rw-r--r--chromium/components/offline_pages/core/model/complete_offline_page_upgrade_task.h6
-rw-r--r--chromium/components/offline_pages/core/model/delete_page_task.cc14
-rw-r--r--chromium/components/offline_pages/core/model/delete_page_task.h18
-rw-r--r--chromium/components/offline_pages/core/model/delete_page_task_unittest.cc6
-rw-r--r--chromium/components/offline_pages/core/model/get_pages_task.cc24
-rw-r--r--chromium/components/offline_pages/core/model/get_pages_task.h30
-rw-r--r--chromium/components/offline_pages/core/model/get_thumbnail_task.cc4
-rw-r--r--chromium/components/offline_pages/core/model/get_thumbnail_task.h6
-rw-r--r--chromium/components/offline_pages/core/model/has_thumbnail_task.cc4
-rw-r--r--chromium/components/offline_pages/core/model/has_thumbnail_task.h6
-rw-r--r--chromium/components/offline_pages/core/model/mark_page_accessed_task.cc8
-rw-r--r--chromium/components/offline_pages/core/model/mark_page_accessed_task.h6
-rw-r--r--chromium/components/offline_pages/core/model/mark_page_accessed_task_unittest.cc2
-rw-r--r--chromium/components/offline_pages/core/model/model_task_test_base.h2
-rw-r--r--chromium/components/offline_pages/core/model/offline_page_model_taskified.cc186
-rw-r--r--chromium/components/offline_pages/core/model/offline_page_model_taskified.h79
-rw-r--r--chromium/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc66
-rw-r--r--chromium/components/offline_pages/core/model/persistent_page_consistency_check_task.cc6
-rw-r--r--chromium/components/offline_pages/core/model/persistent_page_consistency_check_task.h6
-rw-r--r--chromium/components/offline_pages/core/model/persistent_page_consistency_check_task_unittest.cc2
-rw-r--r--chromium/components/offline_pages/core/model/start_offline_page_upgrade_task.cc8
-rw-r--r--chromium/components/offline_pages/core/model/start_offline_page_upgrade_task.h6
-rw-r--r--chromium/components/offline_pages/core/model/startup_maintenance_task.cc4
-rw-r--r--chromium/components/offline_pages/core/model/startup_maintenance_task.h6
-rw-r--r--chromium/components/offline_pages/core/model/startup_maintenance_task_unittest.cc2
-rw-r--r--chromium/components/offline_pages/core/model/store_thumbnail_task.cc4
-rw-r--r--chromium/components/offline_pages/core/model/store_thumbnail_task.h6
-rw-r--r--chromium/components/offline_pages/core/model/store_thumbnail_task_unittest.cc2
-rw-r--r--chromium/components/offline_pages/core/model/update_file_path_task.cc6
-rw-r--r--chromium/components/offline_pages/core/model/update_file_path_task.h6
-rw-r--r--chromium/components/offline_pages/core/offline_page_archiver.cc90
-rw-r--r--chromium/components/offline_pages/core/offline_page_archiver.h24
-rw-r--r--chromium/components/offline_pages/core/offline_page_archiver_unittest.cc14
-rw-r--r--chromium/components/offline_pages/core/offline_page_feature.cc18
-rw-r--r--chromium/components/offline_pages/core/offline_page_feature.h7
-rw-r--r--chromium/components/offline_pages/core/offline_page_feature_unittest.cc14
-rw-r--r--chromium/components/offline_pages/core/offline_page_metadata_store.cc (renamed from chromium/components/offline_pages/core/offline_page_metadata_store_sql.cc)101
-rw-r--r--chromium/components/offline_pages/core/offline_page_metadata_store.h (renamed from chromium/components/offline_pages/core/offline_page_metadata_store_sql.h)40
-rw-r--r--chromium/components/offline_pages/core/offline_page_metadata_store_test_util.cc16
-rw-r--r--chromium/components/offline_pages/core/offline_page_metadata_store_test_util.h12
-rw-r--r--chromium/components/offline_pages/core/offline_page_metadata_store_unittest.cc66
-rw-r--r--chromium/components/offline_pages/core/offline_page_model.h50
-rw-r--r--chromium/components/offline_pages/core/offline_page_test_archiver.cc35
-rw-r--r--chromium/components/offline_pages/core/offline_page_test_archiver.h9
-rw-r--r--chromium/components/offline_pages/core/offline_page_types.h13
-rw-r--r--chromium/components/offline_pages/core/prefetch/BUILD.gn12
-rw-r--r--chromium/components/offline_pages/core/prefetch/add_unique_urls_task.cc8
-rw-r--r--chromium/components/offline_pages/core/prefetch/download_archives_task.cc25
-rw-r--r--chromium/components/offline_pages/core/prefetch/download_archives_task.h4
-rw-r--r--chromium/components/offline_pages/core/prefetch/download_archives_task_unittest.cc29
-rw-r--r--chromium/components/offline_pages/core/prefetch/download_cleanup_task.cc6
-rw-r--r--chromium/components/offline_pages/core/prefetch/download_completed_task.cc11
-rw-r--r--chromium/components/offline_pages/core/prefetch/download_completed_task_unittest.cc2
-rw-r--r--chromium/components/offline_pages/core/prefetch/finalize_dismissed_url_suggestion_task.cc8
-rw-r--r--chromium/components/offline_pages/core/prefetch/generate_page_bundle_reconcile_task.cc8
-rw-r--r--chromium/components/offline_pages/core/prefetch/generate_page_bundle_reconcile_task_unittest.cc4
-rw-r--r--chromium/components/offline_pages/core/prefetch/generate_page_bundle_request.cc20
-rw-r--r--chromium/components/offline_pages/core/prefetch/generate_page_bundle_request.h2
-rw-r--r--chromium/components/offline_pages/core/prefetch/generate_page_bundle_request_unittest.cc4
-rw-r--r--chromium/components/offline_pages/core/prefetch/generate_page_bundle_task.cc12
-rw-r--r--chromium/components/offline_pages/core/prefetch/generate_page_bundle_task.h2
-rw-r--r--chromium/components/offline_pages/core/prefetch/get_operation_request.cc24
-rw-r--r--chromium/components/offline_pages/core/prefetch/get_operation_request.h2
-rw-r--r--chromium/components/offline_pages/core/prefetch/get_operation_request_unittest.cc7
-rw-r--r--chromium/components/offline_pages/core/prefetch/get_operation_task.cc13
-rw-r--r--chromium/components/offline_pages/core/prefetch/get_operation_task.h2
-rw-r--r--chromium/components/offline_pages/core/prefetch/import_archives_task.cc6
-rw-r--r--chromium/components/offline_pages/core/prefetch/import_cleanup_task.cc6
-rw-r--r--chromium/components/offline_pages/core/prefetch/import_completed_task.cc6
-rw-r--r--chromium/components/offline_pages/core/prefetch/mark_operation_done_task.cc6
-rw-r--r--chromium/components/offline_pages/core/prefetch/metrics_finalization_task.cc6
-rw-r--r--chromium/components/offline_pages/core/prefetch/metrics_finalization_task_unittest.cc2
-rw-r--r--chromium/components/offline_pages/core/prefetch/mock_thumbnail_fetcher.h9
-rw-r--r--chromium/components/offline_pages/core/prefetch/page_bundle_update_task.cc49
-rw-r--r--chromium/components/offline_pages/core/prefetch/page_bundle_update_task_unittest.cc33
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc4
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc79
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_downloader.h3
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl.cc23
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl.h3
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl_unittest.cc22
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc4
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_importer_impl_unittest.cc5
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory.h4
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.cc20
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.h17
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl_unittest.cc2
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc15
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.h10
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher_unittest.cc18
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc16
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_server_urls.cc5
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_server_urls.h1
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_server_urls_unittest.cc8
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_task_test_base.cc4
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_task_test_base.h9
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_task_test_base_unittest.cc7
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_types.cc12
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_types.h13
-rw-r--r--chromium/components/offline_pages/core/prefetch/proto/any.proto1
-rw-r--r--chromium/components/offline_pages/core/prefetch/proto/offline_pages.proto1
-rw-r--r--chromium/components/offline_pages/core/prefetch/proto/operation.proto1
-rw-r--r--chromium/components/offline_pages/core/prefetch/proto/status.proto1
-rw-r--r--chromium/components/offline_pages/core/prefetch/sent_get_operation_cleanup_task.cc6
-rw-r--r--chromium/components/offline_pages/core/prefetch/sent_get_operation_cleanup_task_unittest.cc4
-rw-r--r--chromium/components/offline_pages/core/prefetch/stale_entry_finalizer_task.cc51
-rw-r--r--chromium/components/offline_pages/core/prefetch/stale_entry_finalizer_task_unittest.cc54
-rw-r--r--chromium/components/offline_pages/core/prefetch/store/prefetch_store.cc4
-rw-r--r--chromium/components/offline_pages/core/prefetch/store/prefetch_store.h30
-rw-r--r--chromium/components/offline_pages/core/prefetch/store/prefetch_store_schema_unittest.cc12
-rw-r--r--chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.cc50
-rw-r--r--chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.h1
-rw-r--r--chromium/components/offline_pages/core/prefetch/test_download_service.cc2
-rw-r--r--chromium/components/offline_pages/core/prefetch/test_download_service.h8
-rw-r--r--chromium/components/offline_pages/core/prefetch/test_prefetch_downloader.cc11
-rw-r--r--chromium/components/offline_pages/core/prefetch/test_prefetch_downloader.h14
-rw-r--r--chromium/components/offline_pages/core/renovations/page_renovator.cc10
-rw-r--r--chromium/components/offline_pages/core/renovations/page_renovator.h2
-rw-r--r--chromium/components/offline_pages/core/renovations/page_renovator_unittest.cc2
-rw-r--r--chromium/components/offline_pages/core/renovations/script_injector.h2
-rw-r--r--chromium/components/offline_pages/core/snapshot_controller.cc14
-rw-r--r--chromium/components/offline_pages/core/stub_offline_page_model.cc14
-rw-r--r--chromium/components/offline_pages/core/stub_offline_page_model.h26
-rw-r--r--chromium/components/offline_pages/core/task.cc11
-rw-r--r--chromium/components/offline_pages/core/task.h6
-rw-r--r--chromium/components/offline_pages/core/task_queue.cc7
-rw-r--r--chromium/components/offline_pages/core/task_unittest.cc2
-rw-r--r--chromium/components/offline_pages/core/test_task.cc12
-rw-r--r--chromium/components/offline_pages/core/test_task.h4
-rw-r--r--chromium/components/offline_pages/core/test_task_runner.cc6
-rw-r--r--chromium/components/offline_pages/resources/PRESUBMIT.py2
-rw-r--r--chromium/components/omnibox/browser/BUILD.gn20
-rw-r--r--chromium/components/omnibox_strings.grdp12
-rw-r--r--chromium/components/omnibox_strings_grdp/OWNERS2
-rw-r--r--chromium/components/onc/docs/onc_spec.md4
-rw-r--r--chromium/components/onc/onc_constants.cc4
-rw-r--r--chromium/components/onc/onc_constants.h2
-rw-r--r--chromium/components/optimization_guide/optimization_guide_service_observer.h1
-rw-r--r--chromium/components/optimization_guide/optimization_guide_service_unittest.cc2
-rw-r--r--chromium/components/optimization_guide/proto/hints.proto117
-rw-r--r--chromium/components/optimization_guide/test_component_creator.cc14
-rw-r--r--chromium/components/optimization_guide/test_component_creator.h15
-rw-r--r--chromium/components/os_crypt/BUILD.gn4
-rw-r--r--chromium/components/os_crypt/keychain_password_mac.mm17
-rw-r--r--chromium/components/os_crypt/os_crypt_win.cc8
-rw-r--r--chromium/components/password_manager/OWNERS2
-rw-r--r--chromium/components/password_manager/content/browser/BUILD.gn5
-rw-r--r--chromium/components/password_manager/content/browser/DEPS1
-rw-r--r--chromium/components/password_manager/content/browser/bad_message.cc56
-rw-r--r--chromium/components/password_manager/content/browser/bad_message.h30
-rw-r--r--chromium/components/password_manager/content/browser/content_credential_manager.cc2
-rw-r--r--chromium/components/password_manager/content/browser/content_credential_manager.h15
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver.cc223
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver.h67
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver_factory.cc24
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver_factory.h4
-rw-r--r--chromium/components/password_manager/content/browser/content_password_manager_driver_unittest.cc12
-rw-r--r--chromium/components/password_manager/content/browser/form_submission_tracker_util.cc31
-rw-r--r--chromium/components/password_manager/content/browser/form_submission_tracker_util.h28
-rw-r--r--chromium/components/password_manager/content/browser/form_submission_tracker_util_unittest.cc47
-rw-r--r--chromium/components/password_manager/content/browser/password_requirements_service_factory.cc120
-rw-r--r--chromium/components/password_manager/content/browser/password_requirements_service_factory.h50
-rw-r--r--chromium/components/password_manager/content/common/credential_manager.typemap8
-rw-r--r--chromium/components/password_manager/content/common/credential_manager_mojom_traits.cc104
-rw-r--r--chromium/components/password_manager/content/common/credential_manager_mojom_traits.h22
-rw-r--r--chromium/components/password_manager/core/browser/BUILD.gn44
-rw-r--r--chromium/components/password_manager/core/browser/DEPS3
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend.cc16
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend.h15
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend_unittest.cc13
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.cc8
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.h6
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc114
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h41
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher_unittest.cc152
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_service.cc15
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_service.h11
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/affiliation_service_unittest.cc12
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.cc16
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.h13
-rw-r--r--chromium/components/password_manager/core/browser/android_affiliation/test_affiliation_fetcher_factory.h10
-rw-r--r--chromium/components/password_manager/core/browser/browser_save_password_progress_logger.cc61
-rw-r--r--chromium/components/password_manager/core/browser/browser_save_password_progress_logger_unittest.cc17
-rw-r--r--chromium/components/password_manager/core/browser/credentials_filter.h12
-rw-r--r--chromium/components/password_manager/core/browser/export/password_manager_exporter_unittest.cc2
-rw-r--r--chromium/components/password_manager/core/browser/form_parsing/BUILD.gn35
-rw-r--r--chromium/components/password_manager/core/browser/form_parsing/form_parser.cc730
-rw-r--r--chromium/components/password_manager/core/browser/form_parsing/form_parser.h35
-rw-r--r--chromium/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc1305
-rw-r--r--chromium/components/password_manager/core/browser/form_parsing/fuzzer/BUILD.gn80
-rw-r--r--chromium/components/password_manager/core/browser/form_parsing/fuzzer/form_parser_generic_fuzzer.cc44
-rw-r--r--chromium/components/password_manager/core/browser/form_parsing/fuzzer/form_parser_proto_generic_fuzzer.cc43
-rw-r--r--chromium/components/password_manager/core/browser/form_parsing/password_field_prediction.cc63
-rw-r--r--chromium/components/password_manager/core/browser/form_parsing/password_field_prediction.h47
-rw-r--r--chromium/components/password_manager/core/browser/form_parsing/password_field_prediction_unittest.cc127
-rw-r--r--chromium/components/password_manager/core/browser/form_submission_observer.h25
-rw-r--r--chromium/components/password_manager/core/browser/hash_password_manager.cc202
-rw-r--r--chromium/components/password_manager/core/browser/hash_password_manager.h45
-rw-r--r--chromium/components/password_manager/core/browser/hash_password_manager_unittest.cc195
-rw-r--r--chromium/components/password_manager/core/browser/login_database.cc14
-rw-r--r--chromium/components/password_manager/core/browser/login_database_unittest.cc24
-rw-r--r--chromium/components/password_manager/core/browser/mock_password_store.h4
-rw-r--r--chromium/components/password_manager/core/browser/new_password_form_manager.cc223
-rw-r--r--chromium/components/password_manager/core/browser/new_password_form_manager.h71
-rw-r--r--chromium/components/password_manager/core/browser/new_password_form_manager_unittest.cc160
-rw-r--r--chromium/components/password_manager/core/browser/password_autofill_manager.cc255
-rw-r--r--chromium/components/password_manager/core/browser/password_autofill_manager.h55
-rw-r--r--chromium/components/password_manager/core/browser/password_autofill_manager_unittest.cc527
-rw-r--r--chromium/components/password_manager/core/browser/password_bubble_experiment.cc4
-rw-r--r--chromium/components/password_manager/core/browser/password_bubble_experiment.h3
-rw-r--r--chromium/components/password_manager/core/browser/password_bubble_experiment_unittest.cc31
-rw-r--r--chromium/components/password_manager/core/browser/password_form_filling.cc6
-rw-r--r--chromium/components/password_manager/core/browser/password_form_filling_unittest.cc6
-rw-r--r--chromium/components/password_manager/core/browser/password_form_manager.cc612
-rw-r--r--chromium/components/password_manager/core/browser/password_form_manager.h133
-rw-r--r--chromium/components/password_manager/core/browser/password_form_manager_unittest.cc498
-rw-r--r--chromium/components/password_manager/core/browser/password_form_metrics_recorder.cc44
-rw-r--r--chromium/components/password_manager/core/browser/password_form_metrics_recorder.h47
-rw-r--r--chromium/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc4
-rw-r--r--chromium/components/password_manager/core/browser/password_generation_manager.cc79
-rw-r--r--chromium/components/password_manager/core/browser/password_generation_manager.h35
-rw-r--r--chromium/components/password_manager/core/browser/password_generation_manager_unittest.cc198
-rw-r--r--chromium/components/password_manager/core/browser/password_hash_data.cc128
-rw-r--r--chromium/components/password_manager/core/browser/password_hash_data.h72
-rw-r--r--chromium/components/password_manager/core/browser/password_hash_data_unittest.cc36
-rw-r--r--chromium/components/password_manager/core/browser/password_manager.cc217
-rw-r--r--chromium/components/password_manager/core/browser/password_manager.h41
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_client.cc22
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_client.h45
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_constants.h2
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_driver.h23
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_metrics_recorder.h4
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_metrics_util.cc16
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_metrics_util.h29
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_unittest.cc219
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_util.cc27
-rw-r--r--chromium/components/password_manager/core/browser/password_manager_util.h9
-rw-r--r--chromium/components/password_manager/core/browser/password_requirements_service.cc99
-rw-r--r--chromium/components/password_manager/core/browser/password_requirements_service.h72
-rw-r--r--chromium/components/password_manager/core/browser/password_requirements_service_unittest.cc159
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detection_manager.cc40
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detection_manager.h5
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detection_manager_unittest.cc6
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detector.cc62
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detector.h10
-rw-r--r--chromium/components/password_manager/core/browser/password_reuse_detector_unittest.cc60
-rw-r--r--chromium/components/password_manager/core/browser/password_store.cc119
-rw-r--r--chromium/components/password_manager/core/browser/password_store.h59
-rw-r--r--chromium/components/password_manager/core/browser/password_store_factory_util.cc10
-rw-r--r--chromium/components/password_manager/core/browser/password_store_factory_util.h6
-rw-r--r--chromium/components/password_manager/core/browser/password_store_signin_notifier.cc6
-rw-r--r--chromium/components/password_manager/core/browser/password_store_unittest.cc51
-rw-r--r--chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever.cc99
-rw-r--r--chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever.h77
-rw-r--r--chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever_unittest.cc165
-rw-r--r--chromium/components/password_manager/core/browser/stub_credentials_filter.cc12
-rw-r--r--chromium/components/password_manager/core/browser/stub_credentials_filter.h5
-rw-r--r--chromium/components/password_manager/core/browser/stub_password_manager_client.cc2
-rw-r--r--chromium/components/password_manager/core/browser/stub_password_manager_client.h3
-rw-r--r--chromium/components/password_manager/core/browser/stub_password_manager_driver.cc4
-rw-r--r--chromium/components/password_manager/core/browser/stub_password_manager_driver.h2
-rw-r--r--chromium/components/password_manager/core/browser/test_password_store.cc1
-rw-r--r--chromium/components/password_manager/core/browser/test_password_store.h5
-rw-r--r--chromium/components/password_manager/core/browser/vote_uploads_test_matchers.h166
-rw-r--r--chromium/components/password_manager/core/browser/votes_uploader.cc541
-rw-r--r--chromium/components/password_manager/core/browser/votes_uploader.h218
-rw-r--r--chromium/components/password_manager/core/browser/votes_uploader_unittest.cc263
-rw-r--r--chromium/components/password_manager/core/common/password_manager_features.cc58
-rw-r--r--chromium/components/password_manager/core/common/password_manager_features.h21
-rw-r--r--chromium/components/password_manager/sync/browser/password_sync_util.cc39
-rw-r--r--chromium/components/password_manager/sync/browser/password_sync_util.h15
-rw-r--r--chromium/components/password_manager/sync/browser/password_sync_util_unittest.cc28
-rw-r--r--chromium/components/password_manager/sync/browser/sync_credentials_filter.cc21
-rw-r--r--chromium/components/password_manager/sync/browser/sync_credentials_filter.h5
-rw-r--r--chromium/components/password_manager/sync/browser/sync_credentials_filter_unittest.cc66
-rw-r--r--chromium/components/password_manager_strings.grdp18
-rw-r--r--chromium/components/payments/content/BUILD.gn2
-rw-r--r--chromium/components/payments/content/DEPS2
-rw-r--r--chromium/components/payments/content/android/DEPS1
-rw-r--r--chromium/components/payments/content/android/payment_manifest_downloader_android.cc8
-rw-r--r--chromium/components/payments/content/android/payment_manifest_downloader_android.h6
-rw-r--r--chromium/components/payments/content/installable_payment_app_crawler.cc63
-rw-r--r--chromium/components/payments/content/manifest_verifier.cc7
-rw-r--r--chromium/components/payments/content/payment_method_manifest_table_unittest.cc23
-rw-r--r--chromium/components/payments/content/payment_request.cc4
-rw-r--r--chromium/components/payments/content/payment_request_converter.cc2
-rw-r--r--chromium/components/payments/content/payment_request_spec.cc8
-rw-r--r--chromium/components/payments/content/payment_request_spec_unittest.cc113
-rw-r--r--chromium/components/payments/content/payment_request_state.cc6
-rw-r--r--chromium/components/payments/content/payment_request_state_unittest.cc17
-rw-r--r--chromium/components/payments/content/payment_response_helper_unittest.cc4
-rw-r--r--chromium/components/payments/content/service_worker_payment_app_factory.cc7
-rw-r--r--chromium/components/payments/content/service_worker_payment_app_factory_unittest.cc24
-rw-r--r--chromium/components/payments/content/service_worker_payment_instrument.cc66
-rw-r--r--chromium/components/payments/content/service_worker_payment_instrument.h2
-rw-r--r--chromium/components/payments/content/service_worker_payment_instrument_unittest.cc47
-rw-r--r--chromium/components/payments/core/BUILD.gn6
-rw-r--r--chromium/components/payments/core/DEPS2
-rw-r--r--chromium/components/payments/core/autofill_payment_instrument.cc5
-rw-r--r--chromium/components/payments/core/autofill_payment_instrument.h2
-rw-r--r--chromium/components/payments/core/autofill_payment_instrument_unittest.cc13
-rw-r--r--chromium/components/payments/core/currency_formatter.cc10
-rw-r--r--chromium/components/payments/core/currency_formatter_unittest.cc8
-rw-r--r--chromium/components/payments/core/features.cc3
-rw-r--r--chromium/components/payments/core/features.h3
-rw-r--r--chromium/components/payments/core/journey_logger.cc15
-rw-r--r--chromium/components/payments/core/journey_logger.h15
-rw-r--r--chromium/components/payments/core/journey_logger_unittest.cc90
-rw-r--r--chromium/components/payments/core/payment_details_modifier.cc9
-rw-r--r--chromium/components/payments/core/payment_details_modifier_unittest.cc27
-rw-r--r--chromium/components/payments/core/payment_details_validation.cc4
-rw-r--r--chromium/components/payments/core/payment_details_validation_unittest.cc14
-rw-r--r--chromium/components/payments/core/payment_instrument.h4
-rw-r--r--chromium/components/payments/core/payment_manifest_downloader.cc186
-rw-r--r--chromium/components/payments/core/payment_manifest_downloader.h72
-rw-r--r--chromium/components/payments/core/payment_manifest_downloader_unittest.cc329
-rw-r--r--chromium/components/payments/core/payment_method_data.cc30
-rw-r--r--chromium/components/payments/core/payment_method_data.h4
-rw-r--r--chromium/components/payments/core/payment_method_data_unittest.cc50
-rw-r--r--chromium/components/payments/core/payment_request_data_util.cc104
-rw-r--r--chromium/components/payments/core/test_payment_manifest_downloader.cc5
-rw-r--r--chromium/components/payments/core/test_payment_manifest_downloader.h8
-rw-r--r--chromium/components/payments/core/test_payment_request_delegate.cc21
-rw-r--r--chromium/components/payments/core/test_payment_request_delegate.h20
-rw-r--r--chromium/components/payments/core/web_payment_request_unittest.cc8
-rw-r--r--chromium/components/payments_strings.grdp3
-rw-r--r--chromium/components/payments_strings_grdp/IDS_SETTINGS_CAN_MAKE_PAYMENT_TOGGLE_LABEL.png.sha12
-rw-r--r--chromium/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc8
-rw-r--r--chromium/components/plugins/renderer/webview_plugin.cc17
-rw-r--r--chromium/components/plugins/renderer/webview_plugin.h13
-rw-r--r--chromium/components/policy/BUILD.gn5
-rw-r--r--chromium/components/policy/core/common/BUILD.gn8
-rw-r--r--chromium/components/policy_strings.grdp3
-rw-r--r--chromium/components/pref_registry/pref_registry_syncable.cc10
-rw-r--r--chromium/components/pref_registry/pref_registry_syncable.h12
-rw-r--r--chromium/components/prefs/json_pref_store_unittest.cc2
-rw-r--r--chromium/components/prefs/overlay_user_pref_store.cc152
-rw-r--r--chromium/components/prefs/overlay_user_pref_store.h37
-rw-r--r--chromium/components/prefs/overlay_user_pref_store_unittest.cc126
-rw-r--r--chromium/components/prefs/pref_registry_simple.cc6
-rw-r--r--chromium/components/prefs/pref_registry_simple.h4
-rw-r--r--chromium/components/prefs/pref_service.cc12
-rw-r--r--chromium/components/prefs/pref_service.h19
-rw-r--r--chromium/components/prefs/pref_service_unittest.cc29
-rw-r--r--chromium/components/previews/content/BUILD.gn13
-rw-r--r--chromium/components/previews/content/DEPS1
-rw-r--r--chromium/components/previews/content/activation_list.cc59
-rw-r--r--chromium/components/previews/content/activation_list.h50
-rw-r--r--chromium/components/previews/content/activation_list_unittest.cc77
-rw-r--r--chromium/components/previews/content/previews_content_util.cc64
-rw-r--r--chromium/components/previews/content/previews_content_util_unittest.cc135
-rw-r--r--chromium/components/previews/content/previews_decider_impl.cc (renamed from chromium/components/previews/content/previews_io_data.cc)209
-rw-r--r--chromium/components/previews/content/previews_decider_impl.h (renamed from chromium/components/previews/content/previews_io_data.h)57
-rw-r--r--chromium/components/previews/content/previews_decider_impl_unittest.cc (renamed from chromium/components/previews/content/previews_io_data_unittest.cc)710
-rw-r--r--chromium/components/previews/content/previews_hints.cc264
-rw-r--r--chromium/components/previews/content/previews_hints.h75
-rw-r--r--chromium/components/previews/content/previews_optimization_guide.cc239
-rw-r--r--chromium/components/previews/content/previews_optimization_guide.h22
-rw-r--r--chromium/components/previews/content/previews_optimization_guide_unittest.cc260
-rw-r--r--chromium/components/previews/content/previews_ui_service.cc51
-rw-r--r--chromium/components/previews/content/previews_ui_service.h64
-rw-r--r--chromium/components/previews/content/previews_ui_service_unittest.cc59
-rw-r--r--chromium/components/previews/core/BUILD.gn15
-rw-r--r--chromium/components/previews/core/DEPS2
-rw-r--r--chromium/components/previews/core/previews_amp_converter.cc183
-rw-r--r--chromium/components/previews/core/previews_amp_converter.h44
-rw-r--r--chromium/components/previews/core/previews_amp_converter_unittest.cc278
-rw-r--r--chromium/components/previews/core/previews_black_list.cc308
-rw-r--r--chromium/components/previews/core/previews_black_list.h115
-rw-r--r--chromium/components/previews/core/previews_black_list_unittest.cc1025
-rw-r--r--chromium/components/previews/core/previews_decider.h3
-rw-r--r--chromium/components/previews/core/previews_experiments.cc31
-rw-r--r--chromium/components/previews/core/previews_experiments.h14
-rw-r--r--chromium/components/previews/core/previews_features.cc19
-rw-r--r--chromium/components/previews/core/previews_features.h4
-rw-r--r--chromium/components/previews/core/previews_logger.h8
-rw-r--r--chromium/components/previews/core/previews_opt_out_store.h55
-rw-r--r--chromium/components/previews/core/previews_opt_out_store_sql.cc461
-rw-r--r--chromium/components/previews/core/previews_opt_out_store_sql_unittest.cc356
-rw-r--r--chromium/components/previews/core/previews_user_data.h22
-rw-r--r--chromium/components/previews/core/test_previews_decider.cc3
-rw-r--r--chromium/components/previews/core/test_previews_decider.h4
-rw-r--r--chromium/components/printing/browser/features.cc2
-rw-r--r--chromium/components/printing/browser/print_manager_utils.cc2
-rw-r--r--chromium/components/printing/common/print_messages.cc12
-rw-r--r--chromium/components/printing/common/print_messages.h71
-rw-r--r--chromium/components/printing/renderer/DEPS1
-rw-r--r--chromium/components/printing/renderer/print_render_frame_helper.cc166
-rw-r--r--chromium/components/printing/renderer/print_render_frame_helper.h9
-rw-r--r--chromium/components/printing/renderer/print_render_frame_helper_mac.mm2
-rw-r--r--chromium/components/profile_metrics/OWNERS1
-rw-r--r--chromium/components/quirks/BUILD.gn2
-rw-r--r--chromium/components/quirks/DEPS5
-rw-r--r--chromium/components/quirks/quirks_client.cc76
-rw-r--r--chromium/components/quirks/quirks_client.h16
-rw-r--r--chromium/components/quirks/quirks_manager.cc43
-rw-r--r--chromium/components/quirks/quirks_manager.h41
-rw-r--r--chromium/components/rappor/rappor_prefs_unittest.cc2
-rw-r--r--chromium/components/reading_list/core/reading_list_model_unittest.cc2
-rw-r--r--chromium/components/reading_list/core/reading_list_store.cc25
-rw-r--r--chromium/components/reading_list/core/reading_list_store.h5
-rw-r--r--chromium/components/reading_list/core/reading_list_store_unittest.cc136
-rw-r--r--chromium/components/renderer_context_menu/context_menu_content_type.cc10
-rw-r--r--chromium/components/renderer_context_menu/context_menu_content_type.h1
-rw-r--r--chromium/components/renderer_context_menu/render_view_context_menu_base.cc8
-rw-r--r--chromium/components/renderer_context_menu/render_view_context_menu_base.h3
-rw-r--r--chromium/components/renderer_context_menu/render_view_context_menu_proxy.h3
-rw-r--r--chromium/components/renderer_context_menu/views/toolkit_delegate_views.cc5
-rw-r--r--chromium/components/renderer_context_menu/views/toolkit_delegate_views.h1
-rw-r--r--chromium/components/reset_password_strings.grdp6
-rw-r--r--chromium/components/resources/OWNERS4
-rw-r--r--chromium/components/resources/autofill_scaled_resources.grdp2
-rw-r--r--chromium/components/resources/components_resources.grd3
-rw-r--r--chromium/components/resources/default_100_percent/autofill/autofill_tooltip_icon.pngbin311 -> 0 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/autofill_tooltip_icon_hover.pngbin282 -> 0 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/credit_card_cvc_hint.pngbin260 -> 426 bytes
-rw-r--r--chromium/components/resources/default_100_percent/autofill/credit_card_cvc_hint_amex.pngbin262 -> 328 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/autofill_tooltip_icon.pngbin597 -> 0 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/autofill_tooltip_icon_hover.pngbin578 -> 0 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/credit_card_cvc_hint.pngbin549 -> 625 bytes
-rw-r--r--chromium/components/resources/default_200_percent/autofill/credit_card_cvc_hint_amex.pngbin529 -> 473 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/autofill_tooltip_icon.pngbin2228 -> 0 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/autofill_tooltip_icon_hover.pngbin2216 -> 0 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/credit_card_cvc_hint.pngbin0 -> 880 bytes
-rw-r--r--chromium/components/resources/default_300_percent/autofill/credit_card_cvc_hint_amex.pngbin0 -> 725 bytes
-rw-r--r--chromium/components/resources/nux_google_apps.grdp21
-rw-r--r--chromium/components/safe_browsing/BUILD.gn14
-rw-r--r--chromium/components/safe_browsing/DEPS1
-rw-r--r--chromium/components/safe_browsing/android/remote_database_manager.cc5
-rw-r--r--chromium/components/safe_browsing/android/remote_database_manager.h1
-rw-r--r--chromium/components/safe_browsing/browser/base_parallel_resource_throttle.cc3
-rw-r--r--chromium/components/safe_browsing/browser/browser_url_loader_throttle.cc3
-rw-r--r--chromium/components/safe_browsing/browser/browser_url_loader_throttle.h8
-rw-r--r--chromium/components/safe_browsing/browser/safe_browsing_network_context.cc1
-rw-r--r--chromium/components/safe_browsing/browser/safe_browsing_url_request_context_getter.cc8
-rw-r--r--chromium/components/safe_browsing/browser/threat_details.cc18
-rw-r--r--chromium/components/safe_browsing/common/safe_browsing_prefs.cc106
-rw-r--r--chromium/components/safe_browsing/common/safe_browsing_prefs.h11
-rw-r--r--chromium/components/safe_browsing/common/safe_browsing_prefs_unittest.cc273
-rw-r--r--chromium/components/safe_browsing/db/database_manager.h2
-rw-r--r--chromium/components/safe_browsing/db/test_database_manager.cc4
-rw-r--r--chromium/components/safe_browsing/db/test_database_manager.h1
-rw-r--r--chromium/components/safe_browsing/db/v4_get_hash_protocol_manager.cc4
-rw-r--r--chromium/components/safe_browsing/db/v4_get_hash_protocol_manager.h2
-rw-r--r--chromium/components/safe_browsing/db/v4_get_hash_protocol_manager_unittest.cc87
-rw-r--r--chromium/components/safe_browsing/db/v4_local_database_manager.cc5
-rw-r--r--chromium/components/safe_browsing/db/v4_local_database_manager.h1
-rw-r--r--chromium/components/safe_browsing/db/v4_protocol_manager_util.h7
-rw-r--r--chromium/components/safe_browsing/db/v4_protocol_manager_util_unittest.cc28
-rw-r--r--chromium/components/safe_browsing/features.cc16
-rw-r--r--chromium/components/safe_browsing/features.h1
-rw-r--r--chromium/components/safe_browsing/password_protection/BUILD.gn1
-rw-r--r--chromium/components/safe_browsing/password_protection/DEPS1
-rw-r--r--chromium/components/safe_browsing/password_protection/mock_password_protection_service.cc3
-rw-r--r--chromium/components/safe_browsing/password_protection/mock_password_protection_service.h29
-rw-r--r--chromium/components/safe_browsing/password_protection/password_protection_request.cc93
-rw-r--r--chromium/components/safe_browsing/password_protection/password_protection_request.h28
-rw-r--r--chromium/components/safe_browsing/password_protection/password_protection_service.cc474
-rw-r--r--chromium/components/safe_browsing/password_protection/password_protection_service.h98
-rw-r--r--chromium/components/safe_browsing/password_protection/password_protection_service_unittest.cc531
-rw-r--r--chromium/components/safe_browsing/ping_manager.cc (renamed from chromium/components/safe_browsing/base_ping_manager.cc)29
-rw-r--r--chromium/components/safe_browsing/ping_manager.h (renamed from chromium/components/safe_browsing/base_ping_manager.h)31
-rw-r--r--chromium/components/safe_browsing/ping_manager_unittest.cc (renamed from chromium/components/safe_browsing/base_ping_manager_unittest.cc)16
-rw-r--r--chromium/components/safe_browsing/proto/PRESUBMIT.py20
-rw-r--r--chromium/components/safe_browsing/proto/csd.proto37
-rw-r--r--chromium/components/safe_browsing/renderer/renderer_url_loader_throttle.cc3
-rw-r--r--chromium/components/safe_browsing/renderer/renderer_url_loader_throttle.h8
-rw-r--r--chromium/components/safe_browsing/triggers/ad_sampler_trigger_unittest.cc2
-rw-r--r--chromium/components/safe_browsing/triggers/suspicious_site_trigger_unittest.cc2
-rw-r--r--chromium/components/safe_browsing/triggers/trigger_manager.cc17
-rw-r--r--chromium/components/safe_browsing/triggers/trigger_manager_unittest.cc44
-rw-r--r--chromium/components/safe_browsing/triggers/trigger_throttler.cc4
-rw-r--r--chromium/components/safe_browsing/triggers/trigger_throttler.h3
-rw-r--r--chromium/components/safe_browsing/triggers/trigger_throttler_unittest.cc13
-rw-r--r--chromium/components/safe_browsing/web_ui/BUILD.gn2
-rw-r--r--chromium/components/safe_browsing/web_ui/DEPS3
-rw-r--r--chromium/components/safe_browsing/web_ui/resources/safe_browsing.css9
-rw-r--r--chromium/components/safe_browsing/web_ui/resources/safe_browsing.html89
-rw-r--r--chromium/components/safe_browsing/web_ui/resources/safe_browsing.js195
-rw-r--r--chromium/components/safe_browsing/web_ui/safe_browsing_ui.cc809
-rw-r--r--chromium/components/safe_browsing/web_ui/safe_browsing_ui.h191
-rw-r--r--chromium/components/safe_search_api/BUILD.gn38
-rw-r--r--chromium/components/safe_search_api/DEPS6
-rw-r--r--chromium/components/safe_search_api/OWNERS2
-rw-r--r--chromium/components/safe_search_api/url_checker.cc239
-rw-r--r--chromium/components/safe_search_api/url_checker.h91
-rw-r--r--chromium/components/safe_search_api/url_checker_unittest.cc244
-rw-r--r--chromium/components/search/OWNERS1
-rw-r--r--chromium/components/search_engines/BUILD.gn3
-rw-r--r--chromium/components/search_engines/DEPS2
-rw-r--r--chromium/components/search_engines/prepopulated_engines.json89
-rw-r--r--chromium/components/search_engines/search_engine_data_type_controller_unittest.cc8
-rw-r--r--chromium/components/search_engines/template_url.cc3
-rw-r--r--chromium/components/search_engines/template_url_fetcher.cc98
-rw-r--r--chromium/components/search_engines/template_url_fetcher.h33
-rw-r--r--chromium/components/search_engines/template_url_service.cc21
-rw-r--r--chromium/components/search_provider_logos/BUILD.gn3
-rw-r--r--chromium/components/search_provider_logos/DEPS3
-rw-r--r--chromium/components/search_provider_logos/logo_service_impl.cc8
-rw-r--r--chromium/components/search_provider_logos/logo_service_impl.h10
-rw-r--r--chromium/components/search_provider_logos/logo_service_impl_unittest.cc111
-rw-r--r--chromium/components/search_provider_logos/logo_tracker.cc64
-rw-r--r--chromium/components/search_provider_logos/logo_tracker.h32
-rw-r--r--chromium/components/security_interstitials/content/unsafe_resource.cc8
-rw-r--r--chromium/components/security_interstitials/content/unsafe_resource.h2
-rw-r--r--chromium/components/security_interstitials_strings.grdp2
-rw-r--r--chromium/components/security_state/OWNERS1
-rw-r--r--chromium/components/security_state/content/content_utils_unittest.cc2
-rw-r--r--chromium/components/security_state/core/security_state.h3
-rw-r--r--chromium/components/security_state/core/security_state_unittest.cc2
-rw-r--r--chromium/components/services/filesystem/OWNERS2
-rw-r--r--chromium/components/services/font/BUILD.gn74
-rw-r--r--chromium/components/services/font/DEPS3
-rw-r--r--chromium/components/services/font/OWNERS4
-rw-r--r--chromium/components/services/font/font_loader_test.cc235
-rw-r--r--chromium/components/services/font/font_loader_test.h38
-rw-r--r--chromium/components/services/font/font_service_app.cc121
-rw-r--r--chromium/components/services/font/font_service_app.h24
-rw-r--r--chromium/components/services/font/manifest.json4
-rw-r--r--chromium/components/services/font/ppapi_fontconfig_matching.cc262
-rw-r--r--chromium/components/services/font/ppapi_fontconfig_matching.h29
-rw-r--r--chromium/components/services/font/public/cpp/font_loader.cc38
-rw-r--r--chromium/components/services/font/public/cpp/font_loader.h29
-rw-r--r--chromium/components/services/font/public/cpp/font_service_thread.cc228
-rw-r--r--chromium/components/services/font/public/cpp/font_service_thread.h84
-rw-r--r--chromium/components/services/font/public/interfaces/font_service.mojom44
-rw-r--r--chromium/components/services/font/test_manifest.json11
-rw-r--r--chromium/components/services/heap_profiling/BUILD.gn1
-rw-r--r--chromium/components/services/heap_profiling/DEPS1
-rw-r--r--chromium/components/services/heap_profiling/connection_manager.cc8
-rw-r--r--chromium/components/services/heap_profiling/connection_manager.h1
-rw-r--r--chromium/components/services/heap_profiling/json_exporter_unittest.cc7
-rw-r--r--chromium/components/services/heap_profiling/public/cpp/BUILD.gn2
-rw-r--r--chromium/components/services/heap_profiling/public/cpp/DEPS1
-rw-r--r--chromium/components/services/heap_profiling/public/cpp/allocator_shim.cc126
-rw-r--r--chromium/components/services/heap_profiling/public/cpp/allocator_shim.h1
-rw-r--r--chromium/components/services/heap_profiling/public/cpp/client.cc8
-rw-r--r--chromium/components/services/heap_profiling/public/cpp/controller.cc6
-rw-r--r--chromium/components/services/heap_profiling/public/cpp/sender_pipe.h16
-rw-r--r--chromium/components/services/heap_profiling/public/cpp/sender_pipe_posix.cc7
-rw-r--r--chromium/components/services/heap_profiling/public/cpp/sender_pipe_unittest.cc19
-rw-r--r--chromium/components/services/heap_profiling/public/cpp/sender_pipe_win.cc12
-rw-r--r--chromium/components/services/heap_profiling/receiver_pipe.cc3
-rw-r--r--chromium/components/services/heap_profiling/receiver_pipe.h6
-rw-r--r--chromium/components/services/heap_profiling/receiver_pipe_posix.cc12
-rw-r--r--chromium/components/services/heap_profiling/receiver_pipe_posix.h2
-rw-r--r--chromium/components/services/heap_profiling/receiver_pipe_win.cc8
-rw-r--r--chromium/components/services/heap_profiling/receiver_pipe_win.h2
-rw-r--r--chromium/components/services/leveldb/OWNERS1
-rw-r--r--chromium/components/services/pdf_compositor/pdf_compositor_service.cc4
-rw-r--r--chromium/components/services/pdf_compositor/public/cpp/pdf_compositor_service_factory.cc2
-rw-r--r--chromium/components/sessions/BUILD.gn25
-rw-r--r--chromium/components/sessions/DEPS1
-rw-r--r--chromium/components/sessions/core/DEPS1
-rw-r--r--chromium/components/sessions/core/in_memory_tab_restore_service.cc96
-rw-r--r--chromium/components/sessions/core/in_memory_tab_restore_service.h66
-rw-r--r--chromium/components/sessions/core/serialized_navigation_entry.cc266
-rw-r--r--chromium/components/sessions/core/serialized_navigation_entry.h39
-rw-r--r--chromium/components/sessions/core/serialized_navigation_entry_test_helper.cc4
-rw-r--r--chromium/components/sessions/core/serialized_navigation_entry_unittest.cc162
-rw-r--r--chromium/components/sessions/core/session_id.cc4
-rw-r--r--chromium/components/sessions/core/session_id.h4
-rw-r--r--chromium/components/sessions/core/session_id_generator.cc103
-rw-r--r--chromium/components/sessions/core/session_id_generator.h64
-rw-r--r--chromium/components/sessions/core/session_id_generator_unittest.cc165
-rw-r--r--chromium/components/sessions/core/session_types.cc34
-rw-r--r--chromium/components/sessions/core/session_types.h15
-rw-r--r--chromium/components/sessions/core/session_types_unittest.cc111
-rw-r--r--chromium/components/sessions/core/tab_restore_service_helper.h6
-rw-r--r--chromium/components/signin/DEPS6
-rw-r--r--chromium/components/signin/core/browser/BUILD.gn11
-rw-r--r--chromium/components/signin/core/browser/DEPS2
-rw-r--r--chromium/components/signin/core/browser/account_fetcher_service.cc9
-rw-r--r--chromium/components/signin/core/browser/account_fetcher_service.h3
-rw-r--r--chromium/components/signin/core/browser/account_info.cc11
-rw-r--r--chromium/components/signin/core/browser/account_info.h6
-rw-r--r--chromium/components/signin/core/browser/account_investigator_unittest.cc10
-rw-r--r--chromium/components/signin/core/browser/account_reconcilor.cc21
-rw-r--r--chromium/components/signin/core/browser/account_reconcilor.h13
-rw-r--r--chromium/components/signin/core/browser/account_reconcilor_unittest.cc36
-rw-r--r--chromium/components/signin/core/browser/account_tracker_service_unittest.cc29
-rw-r--r--chromium/components/signin/core/browser/child_account_info_fetcher.cc5
-rw-r--r--chromium/components/signin/core/browser/child_account_info_fetcher.h7
-rw-r--r--chromium/components/signin/core/browser/child_account_info_fetcher_impl.cc14
-rw-r--r--chromium/components/signin/core/browser/child_account_info_fetcher_impl.h9
-rw-r--r--chromium/components/signin/core/browser/chrome_connected_header_helper.cc15
-rw-r--r--chromium/components/signin/core/browser/chrome_connected_header_helper.h5
-rw-r--r--chromium/components/signin/core/browser/dice_header_helper.cc11
-rw-r--r--chromium/components/signin/core/browser/dice_header_helper.h8
-rw-r--r--chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.cc62
-rw-r--r--chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.h18
-rw-r--r--chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc4
-rw-r--r--chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h13
-rw-r--r--chromium/components/signin/core/browser/fake_signin_manager.cc2
-rw-r--r--chromium/components/signin/core/browser/gaia_cookie_manager_service.cc165
-rw-r--r--chromium/components/signin/core/browser/gaia_cookie_manager_service.h49
-rw-r--r--chromium/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc206
-rw-r--r--chromium/components/signin/core/browser/profile_identity_provider.cc41
-rw-r--r--chromium/components/signin/core/browser/profile_identity_provider.h41
-rw-r--r--chromium/components/signin/core/browser/profile_management_switches.cc184
-rw-r--r--chromium/components/signin/core/browser/profile_management_switches.h106
-rw-r--r--chromium/components/signin/core/browser/profile_management_switches_unittest.cc151
-rw-r--r--chromium/components/signin/core/browser/profile_oauth2_token_service.cc30
-rw-r--r--chromium/components/signin/core/browser/profile_oauth2_token_service.h36
-rw-r--r--chromium/components/signin/core/browser/scoped_account_consistency.cc67
-rw-r--r--chromium/components/signin/core/browser/scoped_account_consistency.h58
-rw-r--r--chromium/components/signin/core/browser/signin_client.cc8
-rw-r--r--chromium/components/signin/core/browser/signin_client.h24
-rw-r--r--chromium/components/signin/core/browser/signin_header_helper.cc58
-rw-r--r--chromium/components/signin/core/browser/signin_header_helper.h31
-rw-r--r--chromium/components/signin/core/browser/signin_header_helper_unittest.cc89
-rw-r--r--chromium/components/signin/core/browser/signin_investigator_unittest.cc2
-rw-r--r--chromium/components/signin/core/browser/signin_manager.cc7
-rw-r--r--chromium/components/signin/core/browser/signin_manager.h4
-rw-r--r--chromium/components/signin/core/browser/signin_manager_unittest.cc4
-rw-r--r--chromium/components/signin/core/browser/signin_metrics.cc232
-rw-r--r--chromium/components/signin/core/browser/signin_metrics.h14
-rw-r--r--chromium/components/signin/core/browser/signin_metrics_unittest.cc55
-rw-r--r--chromium/components/signin/core/browser/test_signin_client.cc22
-rw-r--r--chromium/components/signin/core/browser/test_signin_client.h28
-rw-r--r--chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h2
-rw-r--r--chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm2
-rw-r--r--chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate_unittest.mm6
-rw-r--r--chromium/components/spellcheck/browser/BUILD.gn3
-rw-r--r--chromium/components/spellcheck/browser/DEPS2
-rw-r--r--chromium/components/spellcheck/browser/spellcheck_host_metrics_unittest.cc2
-rw-r--r--chromium/components/spellcheck/browser/spellchecker_session_bridge_android.cc10
-rw-r--r--chromium/components/spellcheck/browser/spelling_service_client.cc87
-rw-r--r--chromium/components/spellcheck/browser/spelling_service_client.h52
-rw-r--r--chromium/components/ssl_errors/BUILD.gn2
-rw-r--r--chromium/components/ssl_errors/DEPS2
-rw-r--r--chromium/components/ssl_errors/error_classification_unittest.cc31
-rw-r--r--chromium/components/startup_metric_utils/browser/startup_metric_utils.cc46
-rw-r--r--chromium/components/storage_monitor/image_capture_device.mm34
-rw-r--r--chromium/components/storage_monitor/test_media_transfer_protocol_manager_chromeos.cc2
-rw-r--r--chromium/components/storage_monitor/test_media_transfer_protocol_manager_chromeos.h2
-rw-r--r--chromium/components/strings/components_strings_am.xtb48
-rw-r--r--chromium/components/strings/components_strings_ar.xtb50
-rw-r--r--chromium/components/strings/components_strings_bg.xtb48
-rw-r--r--chromium/components/strings/components_strings_bn.xtb50
-rw-r--r--chromium/components/strings/components_strings_ca.xtb56
-rw-r--r--chromium/components/strings/components_strings_cs.xtb48
-rw-r--r--chromium/components/strings/components_strings_da.xtb48
-rw-r--r--chromium/components/strings/components_strings_de.xtb62
-rw-r--r--chromium/components/strings/components_strings_el.xtb48
-rw-r--r--chromium/components/strings/components_strings_en-GB.xtb48
-rw-r--r--chromium/components/strings/components_strings_es-419.xtb48
-rw-r--r--chromium/components/strings/components_strings_es.xtb48
-rw-r--r--chromium/components/strings/components_strings_et.xtb48
-rw-r--r--chromium/components/strings/components_strings_fa.xtb48
-rw-r--r--chromium/components/strings/components_strings_fi.xtb48
-rw-r--r--chromium/components/strings/components_strings_fil.xtb48
-rw-r--r--chromium/components/strings/components_strings_fr.xtb48
-rw-r--r--chromium/components/strings/components_strings_gu.xtb48
-rw-r--r--chromium/components/strings/components_strings_hi.xtb60
-rw-r--r--chromium/components/strings/components_strings_hr.xtb48
-rw-r--r--chromium/components/strings/components_strings_hu.xtb48
-rw-r--r--chromium/components/strings/components_strings_id.xtb48
-rw-r--r--chromium/components/strings/components_strings_it.xtb48
-rw-r--r--chromium/components/strings/components_strings_iw.xtb50
-rw-r--r--chromium/components/strings/components_strings_ja.xtb48
-rw-r--r--chromium/components/strings/components_strings_kn.xtb48
-rw-r--r--chromium/components/strings/components_strings_ko.xtb50
-rw-r--r--chromium/components/strings/components_strings_lt.xtb48
-rw-r--r--chromium/components/strings/components_strings_lv.xtb48
-rw-r--r--chromium/components/strings/components_strings_ml.xtb48
-rw-r--r--chromium/components/strings/components_strings_mr.xtb48
-rw-r--r--chromium/components/strings/components_strings_ms.xtb48
-rw-r--r--chromium/components/strings/components_strings_nl.xtb48
-rw-r--r--chromium/components/strings/components_strings_no.xtb48
-rw-r--r--chromium/components/strings/components_strings_pl.xtb48
-rw-r--r--chromium/components/strings/components_strings_pt-BR.xtb48
-rw-r--r--chromium/components/strings/components_strings_pt-PT.xtb48
-rw-r--r--chromium/components/strings/components_strings_ro.xtb48
-rw-r--r--chromium/components/strings/components_strings_ru.xtb48
-rw-r--r--chromium/components/strings/components_strings_sk.xtb48
-rw-r--r--chromium/components/strings/components_strings_sl.xtb48
-rw-r--r--chromium/components/strings/components_strings_sr.xtb48
-rw-r--r--chromium/components/strings/components_strings_sv.xtb48
-rw-r--r--chromium/components/strings/components_strings_sw.xtb48
-rw-r--r--chromium/components/strings/components_strings_ta.xtb50
-rw-r--r--chromium/components/strings/components_strings_te.xtb48
-rw-r--r--chromium/components/strings/components_strings_th.xtb48
-rw-r--r--chromium/components/strings/components_strings_tr.xtb48
-rw-r--r--chromium/components/strings/components_strings_uk.xtb48
-rw-r--r--chromium/components/strings/components_strings_vi.xtb48
-rw-r--r--chromium/components/strings/components_strings_zh-CN.xtb48
-rw-r--r--chromium/components/strings/components_strings_zh-TW.xtb48
-rw-r--r--chromium/components/subresource_filter/README.md2
-rw-r--r--chromium/components/subresource_filter/content/browser/BUILD.gn2
-rw-r--r--chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc2
-rw-r--r--chromium/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc2
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc145
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h109
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc28
-rw-r--r--chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h17
-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.cc6
-rw-r--r--chromium/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h1
-rw-r--r--chromium/components/subresource_filter/content/browser/navigation_console_logger_unittest.cc19
-rw-r--r--chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc67
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_client.h23
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_observer.h21
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_observer_manager.cc12
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_observer_manager.h12
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.cc24
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h22
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc200
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h32
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc293
-rw-r--r--chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.cc3
-rw-r--r--chromium/components/subresource_filter/content/browser/verified_ruleset_dealer.cc6
-rw-r--r--chromium/components/subresource_filter/content/browser/verified_ruleset_dealer_unittest.cc24
-rw-r--r--chromium/components/subresource_filter/content/common/ad_delay_throttle.cc31
-rw-r--r--chromium/components/subresource_filter/content/common/ad_delay_throttle.h10
-rw-r--r--chromium/components/subresource_filter/content/common/ad_delay_throttle_unittest.cc12
-rw-r--r--chromium/components/subresource_filter/content/common/ruleset_dealer.cc8
-rw-r--r--chromium/components/subresource_filter/content/common/ruleset_dealer_unittest.cc22
-rw-r--r--chromium/components/subresource_filter/content/common/subresource_filter_utils.cc13
-rw-r--r--chromium/components/subresource_filter/content/renderer/subresource_filter_agent.cc5
-rw-r--r--chromium/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc18
-rw-r--r--chromium/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc7
-rw-r--r--chromium/components/subresource_filter/core/browser/copying_file_stream.cc4
-rw-r--r--chromium/components/subresource_filter/core/browser/copying_file_stream.h4
-rw-r--r--chromium/components/subresource_filter/core/browser/ruleset_service.cc18
-rw-r--r--chromium/components/subresource_filter/core/browser/ruleset_service.h6
-rw-r--r--chromium/components/subresource_filter/core/browser/ruleset_service_unittest.cc2
-rw-r--r--chromium/components/subresource_filter/core/browser/subresource_filter_features.cc24
-rw-r--r--chromium/components/subresource_filter/core/browser/subresource_filter_features.h13
-rw-r--r--chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.cc47
-rw-r--r--chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.h41
-rw-r--r--chromium/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc19
-rw-r--r--chromium/components/subresource_filter/core/common/BUILD.gn14
-rw-r--r--chromium/components/subresource_filter/core/common/activation_decision.h5
-rw-r--r--chromium/components/subresource_filter/core/common/document_subresource_filter.cc3
-rw-r--r--chromium/components/subresource_filter/core/common/document_subresource_filter_unittest.cc2
-rw-r--r--chromium/components/subresource_filter/core/common/indexed_ruleset.cc9
-rw-r--r--chromium/components/subresource_filter/core/common/indexed_ruleset_fuzzer.cc77
-rw-r--r--chromium/components/subresource_filter/core/common/memory_mapped_ruleset.cc27
-rw-r--r--chromium/components/subresource_filter/core/common/memory_mapped_ruleset.h7
-rw-r--r--chromium/components/subresource_filter/core/common/perftests/indexed_ruleset_perftest.cc5
-rw-r--r--chromium/components/subresource_filter/core/common/scoped_timers_unittest.cc2
-rw-r--r--chromium/components/subresource_filter/core/common/test_ruleset_creator.cc2
-rw-r--r--chromium/components/subresource_filter/core/common/unindexed_ruleset.cc6
-rw-r--r--chromium/components/subresource_filter/core/common/unindexed_ruleset.h28
-rw-r--r--chromium/components/subresource_filter/core/common/unindexed_ruleset_unittest.cc7
-rw-r--r--chromium/components/subresource_filter/tools/BUILD.gn11
-rw-r--r--chromium/components/subresource_filter/tools/filter_tool_main.cc3
-rw-r--r--chromium/components/subresource_filter/tools/filter_tool_unittest.cc2
-rw-r--r--chromium/components/subresource_filter/tools/indexing_tool.cc7
-rw-r--r--chromium/components/subresource_filter/tools/ruleset_converter/rule_stream.cc7
-rw-r--r--chromium/components/suggestions/blacklist_store_unittest.cc2
-rw-r--r--chromium/components/suggestions/suggestions_service_impl.cc25
-rw-r--r--chromium/components/suggestions/suggestions_service_impl.h7
-rw-r--r--chromium/components/suggestions/suggestions_service_impl_unittest.cc25
-rw-r--r--chromium/components/sync/BUILD.gn30
-rw-r--r--chromium/components/sync/protocol/protocol_sources.gni1
-rw-r--r--chromium/components/sync_bookmarks/BUILD.gn15
-rw-r--r--chromium/components/sync_bookmarks/DEPS1
-rw-r--r--chromium/components/sync_bookmarks/bookmark_change_processor.cc4
-rw-r--r--chromium/components/sync_bookmarks/bookmark_change_processor.h4
-rw-r--r--chromium/components/sync_bookmarks/bookmark_data_type_controller.cc10
-rw-r--r--chromium/components/sync_bookmarks/bookmark_data_type_controller_unittest.cc43
-rw-r--r--chromium/components/sync_bookmarks/bookmark_local_changes_builder.cc107
-rw-r--r--chromium/components/sync_bookmarks/bookmark_local_changes_builder.h33
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_observer_impl.cc324
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_observer_impl.h93
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc448
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_type_controller.cc177
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_type_controller.h75
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_type_controller_unittest.cc244
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_type_processor.cc512
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_type_processor.h147
-rw-r--r--chromium/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc284
-rw-r--r--chromium/components/sync_bookmarks/bookmark_remote_updates_handler.cc359
-rw-r--r--chromium/components/sync_bookmarks/bookmark_remote_updates_handler.h89
-rw-r--r--chromium/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc95
-rw-r--r--chromium/components/sync_bookmarks/bookmark_sync_service.cc54
-rw-r--r--chromium/components/sync_bookmarks/bookmark_sync_service.h60
-rw-r--r--chromium/components/sync_bookmarks/synced_bookmark_tracker.cc243
-rw-r--r--chromium/components/sync_bookmarks/synced_bookmark_tracker.h121
-rw-r--r--chromium/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc235
-rw-r--r--chromium/components/sync_preferences/BUILD.gn3
-rw-r--r--chromium/components/sync_preferences/pref_model_associator.cc156
-rw-r--r--chromium/components/sync_preferences/pref_model_associator.h39
-rw-r--r--chromium/components/sync_preferences/pref_model_associator_unittest.cc6
-rw-r--r--chromium/components/sync_preferences/pref_service_syncable.cc38
-rw-r--r--chromium/components/sync_preferences/pref_service_syncable.h9
-rw-r--r--chromium/components/sync_preferences/pref_service_syncable_unittest.cc186
-rw-r--r--chromium/components/sync_preferences/unknown_user_pref_accessor.cc197
-rw-r--r--chromium/components/sync_preferences/unknown_user_pref_accessor.h104
-rw-r--r--chromium/components/sync_sessions/BUILD.gn3
-rw-r--r--chromium/components/sync_sessions/abstract_sessions_sync_manager.h1
-rw-r--r--chromium/components/sync_sessions/local_session_event_handler_impl.cc293
-rw-r--r--chromium/components/sync_sessions/local_session_event_handler_impl.h45
-rw-r--r--chromium/components/sync_sessions/local_session_event_handler_impl_unittest.cc205
-rw-r--r--chromium/components/sync_sessions/local_session_event_router.h5
-rw-r--r--chromium/components/sync_sessions/lost_navigations_recorder_unittest.cc2
-rw-r--r--chromium/components/sync_sessions/open_tabs_ui_delegate_impl.cc2
-rw-r--r--chromium/components/sync_sessions/session_data_type_controller.cc26
-rw-r--r--chromium/components/sync_sessions/session_data_type_controller.h8
-rw-r--r--chromium/components/sync_sessions/session_data_type_controller_unittest.cc123
-rw-r--r--chromium/components/sync_sessions/session_model_type_controller.cc51
-rw-r--r--chromium/components/sync_sessions/session_model_type_controller.h47
-rw-r--r--chromium/components/sync_sessions/session_store.cc47
-rw-r--r--chromium/components/sync_sessions/session_sync_bridge.cc77
-rw-r--r--chromium/components/sync_sessions/session_sync_bridge.h11
-rw-r--r--chromium/components/sync_sessions/session_sync_bridge_unittest.cc384
-rw-r--r--chromium/components/sync_sessions/sessions_sync_manager.cc93
-rw-r--r--chromium/components/sync_sessions/sessions_sync_manager.h1
-rw-r--r--chromium/components/sync_sessions/sessions_sync_manager_unittest.cc222
-rw-r--r--chromium/components/sync_sessions/synced_session.cc314
-rw-r--r--chromium/components/sync_sessions/synced_session.h29
-rw-r--r--chromium/components/sync_sessions/synced_session_tracker.cc4
-rw-r--r--chromium/components/sync_sessions/synced_session_tracker.h8
-rw-r--r--chromium/components/sync_sessions/synced_session_unittest.cc282
-rw-r--r--chromium/components/sync_sessions/synced_tab_delegate.h7
-rw-r--r--chromium/components/sync_sessions/test_synced_window_delegates_getter.cc36
-rw-r--r--chromium/components/sync_sessions/test_synced_window_delegates_getter.h10
-rw-r--r--chromium/components/sync_wifi/wifi_config_delegate_chromeos_unittest.cc7
-rw-r--r--chromium/components/task_scheduler_util/variations_util.cc6
-rw-r--r--chromium/components/task_scheduler_util/variations_util_unittest.cc10
-rw-r--r--chromium/components/test/BUILD.gn2
-rw-r--r--chromium/components/timers/alarm_timer_chromeos.cc98
-rw-r--r--chromium/components/timers/alarm_timer_chromeos.h54
-rw-r--r--chromium/components/timers/alarm_timer_unittest.cc258
-rw-r--r--chromium/components/toolbar/test_toolbar_model.cc4
-rw-r--r--chromium/components/toolbar/test_toolbar_model.h1
-rw-r--r--chromium/components/toolbar/toolbar_field_trial.cc2
-rw-r--r--chromium/components/toolbar/toolbar_field_trial.h3
-rw-r--r--chromium/components/toolbar/toolbar_model.h8
-rw-r--r--chromium/components/toolbar/toolbar_model_impl.cc67
-rw-r--r--chromium/components/toolbar/toolbar_model_impl.h3
-rw-r--r--chromium/components/tracing/BUILD.gn1
-rw-r--r--chromium/components/tracing/child/child_trace_message_filter.cc1
-rw-r--r--chromium/components/tracing/common/graphics_memory_dump_provider_android_unittest.cc2
-rw-r--r--chromium/components/tracing/common/trace_startup_config.cc5
-rw-r--r--chromium/components/tracing/common/trace_startup_config.h9
-rw-r--r--chromium/components/translate/core/browser/BUILD.gn3
-rw-r--r--chromium/components/ui_devtools/views/ui_devtools_unittest.cc2
-rw-r--r--chromium/components/ui_devtools/viz_views/frame_sink_element.cc4
-rw-r--r--chromium/components/ukm/BUILD.gn5
-rw-r--r--chromium/components/ukm/DEPS7
-rw-r--r--chromium/components/ukm/content/BUILD.gn3
-rw-r--r--chromium/components/ukm/content/app_source_url_recorder.cc50
-rw-r--r--chromium/components/ukm/content/app_source_url_recorder.h42
-rw-r--r--chromium/components/ukm/content/app_source_url_recorder_test.cc66
-rw-r--r--chromium/components/ukm/content/source_url_recorder.cc30
-rw-r--r--chromium/components/ukm/debug/ukm_debug_data_extractor.cc6
-rw-r--r--chromium/components/ukm/observers/sync_disable_observer.cc144
-rw-r--r--chromium/components/ukm/observers/sync_disable_observer.h97
-rw-r--r--chromium/components/ukm/observers/sync_disable_observer_unittest.cc142
-rw-r--r--chromium/components/ukm/test_ukm_recorder.cc1
-rw-r--r--chromium/components/ukm/ukm_recorder_impl.cc146
-rw-r--r--chromium/components/ukm/ukm_recorder_impl.h82
-rw-r--r--chromium/components/ukm/ukm_service.h5
-rw-r--r--chromium/components/ukm/ukm_service_unittest.cc28
-rw-r--r--chromium/components/undo/bookmark_undo_service.cc2
-rw-r--r--chromium/components/unified_consent/BUILD.gn65
-rw-r--r--chromium/components/unified_consent/DEPS9
-rw-r--r--chromium/components/unified_consent/OWNERS5
-rw-r--r--chromium/components/unified_consent/README.md8
-rw-r--r--chromium/components/unified_consent/feature.cc38
-rw-r--r--chromium/components/unified_consent/feature.h38
-rw-r--r--chromium/components/unified_consent/feature_unittest.cc53
-rw-r--r--chromium/components/unified_consent/pref_names.cc26
-rw-r--r--chromium/components/unified_consent/pref_names.h19
-rw-r--r--chromium/components/unified_consent/scoped_unified_consent.cc (renamed from chromium/components/signin/core/browser/scoped_unified_consent.cc)19
-rw-r--r--chromium/components/unified_consent/scoped_unified_consent.h (renamed from chromium/components/signin/core/browser/scoped_unified_consent.h)17
-rw-r--r--chromium/components/unified_consent/unified_consent_service.cc486
-rw-r--r--chromium/components/unified_consent/unified_consent_service.h168
-rw-r--r--chromium/components/unified_consent/unified_consent_service_client.cc50
-rw-r--r--chromium/components/unified_consent/unified_consent_service_client.h94
-rw-r--r--chromium/components/unified_consent/unified_consent_service_unittest.cc645
-rw-r--r--chromium/components/unified_consent/url_keyed_data_collection_consent_helper.cc191
-rw-r--r--chromium/components/unified_consent/url_keyed_data_collection_consent_helper.h88
-rw-r--r--chromium/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc220
-rw-r--r--chromium/components/update_client/BUILD.gn9
-rw-r--r--chromium/components/update_client/DEPS2
-rw-r--r--chromium/components/update_client/action_runner_win.cc4
-rw-r--r--chromium/components/update_client/background_downloader_win.cc10
-rw-r--r--chromium/components/update_client/command_line_config_policy.cc4
-rw-r--r--chromium/components/update_client/command_line_config_policy.h4
-rw-r--r--chromium/components/update_client/component.cc14
-rw-r--r--chromium/components/update_client/component.h17
-rw-r--r--chromium/components/update_client/component_patcher.cc4
-rw-r--r--chromium/components/update_client/configurator.h8
-rw-r--r--chromium/components/update_client/ping_manager_unittest.cc19
-rw-r--r--chromium/components/update_client/protocol_builder_unittest.cc3
-rw-r--r--chromium/components/update_client/protocol_parser.cc20
-rw-r--r--chromium/components/update_client/protocol_parser_unittest.cc153
-rw-r--r--chromium/components/update_client/request_sender.cc100
-rw-r--r--chromium/components/update_client/request_sender.h28
-rw-r--r--chromium/components/update_client/request_sender_unittest.cc129
-rw-r--r--chromium/components/update_client/test_configurator.cc19
-rw-r--r--chromium/components/update_client/test_configurator.h16
-rw-r--r--chromium/components/update_client/test_installer.cc2
-rw-r--r--chromium/components/update_client/update_checker.cc44
-rw-r--r--chromium/components/update_client/update_checker.h9
-rw-r--r--chromium/components/update_client/update_checker_unittest.cc151
-rw-r--r--chromium/components/update_client/update_client.cc5
-rw-r--r--chromium/components/update_client/update_client_errors.h24
-rw-r--r--chromium/components/update_client/update_client_unittest.cc568
-rw-r--r--chromium/components/update_client/update_engine.cc68
-rw-r--r--chromium/components/update_client/update_engine.h10
-rw-r--r--chromium/components/update_client/url_loader_post_interceptor.cc263
-rw-r--r--chromium/components/update_client/url_loader_post_interceptor.h (renamed from chromium/components/update_client/url_request_post_interceptor.h)145
-rw-r--r--chromium/components/update_client/url_request_post_interceptor.cc340
-rw-r--r--chromium/components/update_client/utils.cc50
-rw-r--r--chromium/components/update_client/utils.h16
-rw-r--r--chromium/components/upload_list/text_log_upload_list.cc2
-rw-r--r--chromium/components/url_formatter/BUILD.gn6
-rw-r--r--chromium/components/url_formatter/idn_spoof_checker.cc83
-rw-r--r--chromium/components/url_formatter/idn_spoof_checker.h13
-rw-r--r--chromium/components/url_formatter/top_domains/BUILD.gn73
-rw-r--r--chromium/components/url_formatter/top_domains/alexa_domains.skeletons9184
-rw-r--r--chromium/components/url_formatter/top_domains/alexa_skeletons.gperf9185
-rw-r--r--chromium/components/url_formatter/top_domains/make_top_domain_skeletons.cc (renamed from chromium/components/url_formatter/top_domains/make_top_domain_gperf.cc)40
-rw-r--r--chromium/components/url_formatter/top_domains/test_domains.skeletons34
-rw-r--r--chromium/components/url_formatter/top_domains/top_domain_generator.cc149
-rw-r--r--chromium/components/url_formatter/top_domains/top_domain_state_generator.cc164
-rw-r--r--chromium/components/url_formatter/top_domains/top_domain_state_generator.h37
-rw-r--r--chromium/components/url_formatter/top_domains/top_domains_trie.template11
-rw-r--r--chromium/components/url_formatter/top_domains/trie_entry.cc57
-rw-r--r--chromium/components/url_formatter/top_domains/trie_entry.h47
-rw-r--r--chromium/components/url_formatter/url_fixer.cc12
-rw-r--r--chromium/components/url_formatter/url_fixer_unittest.cc1
-rw-r--r--chromium/components/url_formatter/url_formatter_unittest.cc17
-rw-r--r--chromium/components/url_matcher/regex_set_matcher.cc2
-rw-r--r--chromium/components/url_matcher/url_matcher.cc3
-rw-r--r--chromium/components/url_matcher/url_matcher_factory_unittest.cc12
-rw-r--r--chromium/components/variations/field_trial_config/BUILD.gn18
-rw-r--r--chromium/components/variations/field_trial_config/field_trial_testing_config_schema.json6
-rw-r--r--chromium/components/variations/field_trial_config/field_trial_util.cc66
-rw-r--r--chromium/components/variations/field_trial_config/field_trial_util.h8
-rw-r--r--chromium/components/variations/field_trial_config/field_trial_util_unittest.cc158
-rw-r--r--chromium/components/variations/net/BUILD.gn2
-rw-r--r--chromium/components/variations/net/DEPS1
-rw-r--r--chromium/components/variations/net/variations_http_headers.cc62
-rw-r--r--chromium/components/variations/net/variations_http_headers.h35
-rw-r--r--chromium/components/variations/proto/study.proto1
-rw-r--r--chromium/components/variations/service/BUILD.gn3
-rw-r--r--chromium/components/variations/service/DEPS2
-rw-r--r--chromium/components/variations/service/safe_seed_manager_unittest.cc2
-rw-r--r--chromium/components/variations/service/variations_field_trial_creator.cc12
-rw-r--r--chromium/components/variations/service/variations_field_trial_creator.h4
-rw-r--r--chromium/components/variations/service/variations_field_trial_creator_unittest.cc6
-rw-r--r--chromium/components/variations/service/variations_service.cc112
-rw-r--r--chromium/components/variations/service/variations_service.h31
-rw-r--r--chromium/components/variations/service/variations_service_client.h8
-rw-r--r--chromium/components/variations/service/variations_service_unittest.cc218
-rw-r--r--chromium/components/variations/variations_http_header_provider.cc21
-rw-r--r--chromium/components/variations/variations_request_scheduler_mobile_unittest.cc6
-rw-r--r--chromium/components/variations/variations_seed_store_unittest.cc2
-rw-r--r--chromium/components/vector_icons/location_on.icon14
-rw-r--r--chromium/components/visitedlink/browser/visitedlink_event_listener.cc2
-rw-r--r--chromium/components/visitedlink/browser/visitedlink_event_listener.h4
-rw-r--r--chromium/components/visitedlink/browser/visitedlink_master.cc22
-rw-r--r--chromium/components/visitedlink/test/BUILD.gn1
-rw-r--r--chromium/components/viz/BUILD.gn14
-rw-r--r--chromium/components/viz/DEPS6
-rw-r--r--chromium/components/viz/OWNERS2
-rw-r--r--chromium/components/viz/PRESUBMIT.py10
-rw-r--r--chromium/components/viz/README.md5
-rw-r--r--chromium/components/viz/client/BUILD.gn20
-rw-r--r--chromium/components/viz/client/DEPS18
-rw-r--r--chromium/components/viz/client/client_layer_tree_frame_sink.cc240
-rw-r--r--chromium/components/viz/client/client_layer_tree_frame_sink.h140
-rw-r--r--chromium/components/viz/client/client_layer_tree_frame_sink_unittest.cc116
-rw-r--r--chromium/components/viz/client/client_resource_provider.cc287
-rw-r--r--chromium/components/viz/client/client_resource_provider.h141
-rw-r--r--chromium/components/viz/client/client_resource_provider_unittest.cc571
-rw-r--r--chromium/components/viz/client/frame_evictor.h1
-rw-r--r--chromium/components/viz/client/hit_test_data_provider_draw_quad_unittest.cc3
-rw-r--r--chromium/components/viz/client/shared_bitmap_reporter.cc (renamed from chromium/components/viz/common/resources/shared_bitmap_reporter.cc)2
-rw-r--r--chromium/components/viz/client/shared_bitmap_reporter.h35
-rw-r--r--chromium/components/viz/common/BUILD.gn24
-rw-r--r--chromium/components/viz/common/DEPS11
-rw-r--r--chromium/components/viz/common/frame_sinks/begin_frame_args.cc13
-rw-r--r--chromium/components/viz/common/frame_sinks/begin_frame_args.h20
-rw-r--r--chromium/components/viz/common/frame_sinks/begin_frame_source.cc4
-rw-r--r--chromium/components/viz/common/frame_sinks/begin_frame_source.h6
-rw-r--r--chromium/components/viz/common/frame_sinks/begin_frame_source_unittest.cc247
-rw-r--r--chromium/components/viz/common/frame_sinks/copy_output_request.cc18
-rw-r--r--chromium/components/viz/common/gl_helper.cc3
-rw-r--r--chromium/components/viz/common/gl_helper_benchmark.cc2
-rw-r--r--chromium/components/viz/common/gl_helper_scaling.cc2
-rw-r--r--chromium/components/viz/common/gl_helper_unittest.cc2
-rw-r--r--chromium/components/viz/common/gpu/context_cache_controller_unittest.cc50
-rw-r--r--chromium/components/viz/common/gpu/context_lost_reason.cc53
-rw-r--r--chromium/components/viz/common/gpu/context_lost_reason.h40
-rw-r--r--chromium/components/viz/common/gpu/texture_allocation.cc2
-rw-r--r--chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc41
-rw-r--r--chromium/components/viz/common/gpu/vulkan_in_process_context_provider.h1
-rw-r--r--chromium/components/viz/common/quads/compositor_frame_metadata.h35
-rw-r--r--chromium/components/viz/common/quads/draw_quad.cc5
-rw-r--r--chromium/components/viz/common/quads/draw_quad_unittest.cc4
-rw-r--r--chromium/components/viz/common/quads/picture_draw_quad.cc4
-rw-r--r--chromium/components/viz/common/quads/picture_draw_quad.h6
-rw-r--r--chromium/components/viz/common/quads/render_pass.cc7
-rw-r--r--chromium/components/viz/common/quads/render_pass.h4
-rw-r--r--chromium/components/viz/common/quads/shared_quad_state.cc9
-rw-r--r--chromium/components/viz/common/resources/platform_color.h9
-rw-r--r--chromium/components/viz/common/resources/platform_color_unittest.cc9
-rw-r--r--chromium/components/viz/common/resources/resource.cc71
-rw-r--r--chromium/components/viz/common/resources/resource.h206
-rw-r--r--chromium/components/viz/common/resources/resource_format.h11
-rw-r--r--chromium/components/viz/common/resources/resource_format_utils.cc156
-rw-r--r--chromium/components/viz/common/resources/resource_format_utils.h5
-rw-r--r--chromium/components/viz/common/resources/resource_type.h18
-rw-r--r--chromium/components/viz/common/resources/shared_bitmap.cc (renamed from chromium/components/viz/common/quads/shared_bitmap.cc)14
-rw-r--r--chromium/components/viz/common/resources/shared_bitmap.h (renamed from chromium/components/viz/common/quads/shared_bitmap.h)36
-rw-r--r--chromium/components/viz/common/resources/shared_bitmap_reporter.h34
-rw-r--r--chromium/components/viz/common/resources/transferable_resource.h9
-rw-r--r--chromium/components/viz/common/skia_helper.cc2
-rw-r--r--chromium/components/viz/common/surfaces/child_local_surface_id_allocator.cc23
-rw-r--r--chromium/components/viz/common/surfaces/child_local_surface_id_allocator_unittest.cc21
-rw-r--r--chromium/components/viz/common/surfaces/frame_sink_id.h16
-rw-r--r--chromium/components/viz/common/surfaces/frame_sink_id_allocator.cc16
-rw-r--r--chromium/components/viz/common/surfaces/frame_sink_id_allocator.h6
-rw-r--r--chromium/components/viz/common/surfaces/local_surface_id.cc15
-rw-r--r--chromium/components/viz/common/surfaces/local_surface_id.h24
-rw-r--r--chromium/components/viz/common/surfaces/parent_local_surface_id_allocator.cc44
-rw-r--r--chromium/components/viz/common/surfaces/parent_local_surface_id_allocator.h2
-rw-r--r--chromium/components/viz/common/surfaces/surface_id.h4
-rw-r--r--chromium/components/viz/common/surfaces/surface_range.cc60
-rw-r--r--chromium/components/viz/common/surfaces/surface_range.h58
-rw-r--r--chromium/components/viz/common/yuv_readback_unittest.cc5
-rw-r--r--chromium/components/viz/host/BUILD.gn14
-rw-r--r--chromium/components/viz/host/DEPS4
-rw-r--r--chromium/components/viz/host/client_frame_sink_video_capturer.cc11
-rw-r--r--chromium/components/viz/host/client_frame_sink_video_capturer.h11
-rw-r--r--chromium/components/viz/host/hit_test/DEPS2
-rw-r--r--chromium/components/viz/host/hit_test/hit_test_query.cc21
-rw-r--r--chromium/components/viz/host/hit_test/hit_test_query.h3
-rw-r--r--chromium/components/viz/host/hit_test/hit_test_region_observer.h34
-rw-r--r--chromium/components/viz/host/host_display_client.cc65
-rw-r--r--chromium/components/viz/host/host_display_client.h62
-rw-r--r--chromium/components/viz/host/host_frame_sink_manager.cc35
-rw-r--r--chromium/components/viz/host/host_frame_sink_manager.h25
-rw-r--r--chromium/components/viz/host/host_frame_sink_manager_unittest.cc12
-rw-r--r--chromium/components/viz/host/host_gpu_memory_buffer_manager.cc337
-rw-r--r--chromium/components/viz/host/host_gpu_memory_buffer_manager.h145
-rw-r--r--chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc (renamed from chromium/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc)81
-rw-r--r--chromium/components/viz/host/server_gpu_memory_buffer_manager.cc251
-rw-r--r--chromium/components/viz/host/server_gpu_memory_buffer_manager.h110
-rw-r--r--chromium/components/viz/service/BUILD.gn53
-rw-r--r--chromium/components/viz/service/DEPS4
-rw-r--r--chromium/components/viz/service/display/DEPS3
-rw-r--r--chromium/components/viz/service/display/bsp_tree_perftest.cc6
-rw-r--r--chromium/components/viz/service/display/copy_output_scaling_pixeltest.cc2
-rw-r--r--chromium/components/viz/service/display/dc_layer_overlay.cc271
-rw-r--r--chromium/components/viz/service/display/dc_layer_overlay.h20
-rw-r--r--chromium/components/viz/service/display/direct_renderer.cc32
-rw-r--r--chromium/components/viz/service/display/display.cc100
-rw-r--r--chromium/components/viz/service/display/display.h3
-rw-r--r--chromium/components/viz/service/display/display_client.h1
-rw-r--r--chromium/components/viz/service/display/display_perftest.cc2
-rw-r--r--chromium/components/viz/service/display/display_resource_provider.cc408
-rw-r--r--chromium/components/viz/service/display/display_resource_provider.h179
-rw-r--r--chromium/components/viz/service/display/display_resource_provider_unittest.cc324
-rw-r--r--chromium/components/viz/service/display/display_scheduler.cc7
-rw-r--r--chromium/components/viz/service/display/display_scheduler_unittest.cc15
-rw-r--r--chromium/components/viz/service/display/display_unittest.cc28
-rw-r--r--chromium/components/viz/service/display/gl_renderer.cc52
-rw-r--r--chromium/components/viz/service/display/gl_renderer.h3
-rw-r--r--chromium/components/viz/service/display/gl_renderer_copier.cc1
-rw-r--r--chromium/components/viz/service/display/gl_renderer_unittest.cc475
-rw-r--r--chromium/components/viz/service/display/output_surface.cc17
-rw-r--r--chromium/components/viz/service/display/output_surface.h12
-rw-r--r--chromium/components/viz/service/display/output_surface_client.h4
-rw-r--r--chromium/components/viz/service/display/overlay_strategy_underlay_cast.cc5
-rw-r--r--chromium/components/viz/service/display/overlay_strategy_underlay_cast.h3
-rw-r--r--chromium/components/viz/service/display/overlay_unittest.cc358
-rw-r--r--chromium/components/viz/service/display/program_binding.h1
-rw-r--r--chromium/components/viz/service/display/renderer_pixeltest.cc230
-rw-r--r--chromium/components/viz/service/display/resource_fence.h (renamed from chromium/components/viz/common/resources/resource_fence.h)8
-rw-r--r--chromium/components/viz/service/display/resource_metadata.cc (renamed from chromium/components/viz/common/resources/resource_metadata.cc)2
-rw-r--r--chromium/components/viz/service/display/resource_metadata.h (renamed from chromium/components/viz/common/resources/resource_metadata.h)14
-rw-r--r--chromium/components/viz/service/display/scoped_render_pass_texture.cc3
-rw-r--r--chromium/components/viz/service/display/shared_bitmap_manager.h (renamed from chromium/components/viz/common/resources/shared_bitmap_manager.h)21
-rw-r--r--chromium/components/viz/service/display/skia_output_surface.cc4
-rw-r--r--chromium/components/viz/service/display/skia_output_surface.h38
-rw-r--r--chromium/components/viz/service/display/skia_renderer.cc492
-rw-r--r--chromium/components/viz/service/display/skia_renderer.h21
-rw-r--r--chromium/components/viz/service/display/software_renderer.cc52
-rw-r--r--chromium/components/viz/service/display/software_renderer_unittest.cc35
-rw-r--r--chromium/components/viz/service/display/surface_aggregator.cc97
-rw-r--r--chromium/components/viz/service/display/surface_aggregator.h7
-rw-r--r--chromium/components/viz/service/display/surface_aggregator_perftest.cc14
-rw-r--r--chromium/components/viz/service/display/surface_aggregator_pixeltest.cc5
-rw-r--r--chromium/components/viz/service/display/surface_aggregator_unittest.cc777
-rw-r--r--chromium/components/viz/service/display/sync_query_collection.cc2
-rw-r--r--chromium/components/viz/service/display/texture_deleter_unittest.cc14
-rw-r--r--chromium/components/viz/service/display_embedder/DEPS8
-rw-r--r--chromium/components/viz/service/display_embedder/buffer_queue.cc2
-rw-r--r--chromium/components/viz/service/display_embedder/buffer_queue_unittest.cc62
-rw-r--r--chromium/components/viz/service/display_embedder/display_provider.h17
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface.cc34
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface.h15
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface_android.cc35
-rw-r--r--chromium/components/viz/service/display_embedder/gl_output_surface_android.h38
-rw-r--r--chromium/components/viz/service/display_embedder/gpu_display_provider.cc154
-rw-r--r--chromium/components/viz/service/display_embedder/gpu_display_provider.h28
-rw-r--r--chromium/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc5
-rw-r--r--chromium/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h4
-rw-r--r--chromium/components/viz/service/display_embedder/server_shared_bitmap_manager.cc97
-rw-r--r--chromium/components/viz/service/display_embedder/server_shared_bitmap_manager.h25
-rw-r--r--chromium/components/viz/service/display_embedder/server_shared_bitmap_manager_unittest.cc21
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_surface_impl.cc214
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_surface_impl.h26
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc331
-rw-r--r--chromium/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h46
-rw-r--r--chromium/components/viz/service/display_embedder/software_output_device_win.cc110
-rw-r--r--chromium/components/viz/service/display_embedder/software_output_device_win.h3
-rw-r--r--chromium/components/viz/service/display_embedder/software_output_surface.cc5
-rw-r--r--chromium/components/viz/service/display_embedder/viz_process_context_provider.cc71
-rw-r--r--chromium/components/viz/service/display_embedder/viz_process_context_provider.h10
-rw-r--r--chromium/components/viz/service/frame_sinks/DEPS5
-rw-r--r--chromium/components/viz/service/frame_sinks/compositor_frame_sink_impl.cc4
-rw-r--r--chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.cc271
-rw-r--r--chromium/components/viz/service/frame_sinks/compositor_frame_sink_support.h62
-rw-r--r--chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc128
-rw-r--r--chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc32
-rw-r--r--chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h9
-rw-r--r--chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc13
-rw-r--r--chromium/components/viz/service/frame_sinks/external_begin_frame_source_android.cc60
-rw-r--r--chromium/components/viz/service/frame_sinks/external_begin_frame_source_android.h45
-rw-r--r--chromium/components/viz/service/frame_sinks/external_begin_frame_source_android_unittest.cc76
-rw-r--r--chromium/components/viz/service/frame_sinks/external_begin_frame_source_mojo.cc (renamed from chromium/components/viz/service/display_embedder/external_begin_frame_controller_impl.cc)31
-rw-r--r--chromium/components/viz/service/frame_sinks/external_begin_frame_source_mojo.h (renamed from chromium/components/viz/service/display_embedder/external_begin_frame_controller_impl.h)25
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.cc126
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_sink_manager_impl.h68
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc22
-rw-r--r--chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc199
-rw-r--r--chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h39
-rw-r--r--chromium/components/viz/service/frame_sinks/surface_references_unittest.cc32
-rw-r--r--chromium/components/viz/service/frame_sinks/surface_synchronization_unittest.cc253
-rw-r--r--chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc78
-rw-r--r--chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h22
-rw-r--r--chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc39
-rw-r--r--chromium/components/viz/service/frame_sinks/video_capture/video_capture_overlay.cc574
-rw-r--r--chromium/components/viz/service/frame_sinks/video_capture/video_capture_overlay.h186
-rw-r--r--chromium/components/viz/service/frame_sinks/video_capture/video_capture_overlay_unittest.cc554
-rw-r--r--chromium/components/viz/service/frame_sinks/video_detector.cc5
-rw-r--r--chromium/components/viz/service/frame_sinks/video_detector.h1
-rw-r--r--chromium/components/viz/service/frame_sinks/video_detector_unittest.cc5
-rw-r--r--chromium/components/viz/service/gl/DEPS1
-rw-r--r--chromium/components/viz/service/gl/gpu_service_impl.cc191
-rw-r--r--chromium/components/viz/service/gl/gpu_service_impl.h66
-rw-r--r--chromium/components/viz/service/gl/gpu_service_impl_unittest.cc4
-rw-r--r--chromium/components/viz/service/hit_test/DEPS1
-rw-r--r--chromium/components/viz/service/hit_test/hit_test_aggregator.cc8
-rw-r--r--chromium/components/viz/service/hit_test/hit_test_aggregator_unittest.cc8
-rw-r--r--chromium/components/viz/service/hit_test/hit_test_manager_fuzzer.cc4
-rw-r--r--chromium/components/viz/service/main/BUILD.gn1
-rw-r--r--chromium/components/viz/service/main/viz_compositor_thread_runner.cc108
-rw-r--r--chromium/components/viz/service/main/viz_compositor_thread_runner.h59
-rw-r--r--chromium/components/viz/service/main/viz_main_impl.cc118
-rw-r--r--chromium/components/viz/service/main/viz_main_impl.h44
-rw-r--r--chromium/components/viz/service/surfaces/referenced_surface_tracker.cc (renamed from chromium/components/viz/service/frame_sinks/referenced_surface_tracker.cc)2
-rw-r--r--chromium/components/viz/service/surfaces/referenced_surface_tracker.h (renamed from chromium/components/viz/service/frame_sinks/referenced_surface_tracker.h)6
-rw-r--r--chromium/components/viz/service/surfaces/referenced_surface_tracker_unittest.cc (renamed from chromium/components/viz/service/frame_sinks/referenced_surface_tracker_unittest.cc)2
-rw-r--r--chromium/components/viz/service/surfaces/surface.cc144
-rw-r--r--chromium/components/viz/service/surfaces/surface.h49
-rw-r--r--chromium/components/viz/service/surfaces/surface_client.h16
-rw-r--r--chromium/components/viz/service/surfaces/surface_dependency_tracker.cc62
-rw-r--r--chromium/components/viz/service/surfaces/surface_dependency_tracker.h1
-rw-r--r--chromium/components/viz/service/surfaces/surface_hittest_unittest.cc4
-rw-r--r--chromium/components/viz/service/surfaces/surface_manager.cc51
-rw-r--r--chromium/components/viz/service/surfaces/surface_manager.h27
-rw-r--r--chromium/components/viz/service/surfaces/surface_reference.cc2
-rw-r--r--chromium/components/viz/service/surfaces/surface_unittest.cc19
-rw-r--r--chromium/components/viz/test/BUILD.gn7
-rw-r--r--chromium/components/web_resource/BUILD.gn3
-rw-r--r--chromium/components/web_resource/DEPS2
-rw-r--r--chromium/components/web_resource/eula_accepted_notifier.h1
-rw-r--r--chromium/components/web_resource/web_resource_service.cc56
-rw-r--r--chromium/components/web_resource/web_resource_service.h25
-rw-r--r--chromium/components/web_resource/web_resource_service_unittest.cc40
-rw-r--r--chromium/components/webcrypto/BUILD.gn2
-rw-r--r--chromium/components/webcrypto/DEPS2
-rw-r--r--chromium/components/webcrypto/algorithms/ec.cc3
-rw-r--r--chromium/components/webcrypto/algorithms/ecdsa_unittest.cc8
-rw-r--r--chromium/components/webcrypto/algorithms/rsa_oaep_unittest.cc14
-rw-r--r--chromium/components/webcrypto/algorithms/test_helpers.cc6
-rw-r--r--chromium/components/webcrypto/fuzzer_support.cc4
-rw-r--r--chromium/components/webcrypto/jwk.cc2
-rw-r--r--chromium/components/webdata/common/BUILD.gn1
-rw-r--r--chromium/components/webdata/common/web_data_service_base.cc12
-rw-r--r--chromium/components/webdata/common/web_database.cc11
-rw-r--r--chromium/components/webdata/common/web_database.h7
-rw-r--r--chromium/components/webdata/common/web_database_backend.cc2
-rw-r--r--chromium/components/webdata/common/web_database_migration_unittest.cc67
-rw-r--r--chromium/components/webdata/common/web_database_service.cc16
-rw-r--r--chromium/components/webdata_services/web_data_service_test_util.cc8
-rw-r--r--chromium/components/webdata_services/web_data_service_test_util.h6
-rw-r--r--chromium/components/webdata_services/web_data_service_wrapper.cc82
-rw-r--r--chromium/components/webdata_services/web_data_service_wrapper.h13
-rw-r--r--chromium/components/wifi/BUILD.gn1
-rw-r--r--chromium/components/wifi/network_properties.cc2
-rw-r--r--chromium/components/wifi/wifi_service_mac.mm13
-rw-r--r--chromium/components/wifi/wifi_service_win.cc349
-rw-r--r--chromium/components/wifi/wifi_test.cc2
-rw-r--r--chromium/components/zoom/zoom_event_manager.cc6
-rw-r--r--chromium/components/zoom/zoom_event_manager.h4
-rw-r--r--chromium/components/zucchini/BUILD.gn6
-rw-r--r--chromium/components/zucchini/README.md44
-rw-r--r--chromium/components/zucchini/algorithm.h50
-rw-r--r--chromium/components/zucchini/algorithm_unittest.cc139
-rw-r--r--chromium/components/zucchini/buffer_source.cc2
-rw-r--r--chromium/components/zucchini/buffer_view.h8
-rw-r--r--chromium/components/zucchini/buffer_view_unittest.cc12
-rw-r--r--chromium/components/zucchini/disassembler.h112
-rw-r--r--chromium/components/zucchini/disassembler_dex.cc841
-rw-r--r--chromium/components/zucchini/disassembler_dex.h153
-rw-r--r--chromium/components/zucchini/disassembler_no_op.cc1
-rw-r--r--chromium/components/zucchini/disassembler_win32.h4
-rw-r--r--chromium/components/zucchini/element_detection_unittest.cc1
-rw-r--r--chromium/components/zucchini/equivalence_map.cc99
-rw-r--r--chromium/components/zucchini/equivalence_map.h42
-rw-r--r--chromium/components/zucchini/equivalence_map_unittest.cc235
-rw-r--r--chromium/components/zucchini/fuzzers/BUILD.gn123
-rw-r--r--chromium/components/zucchini/fuzzers/apply_fuzzer.cc (renamed from chromium/components/zucchini/fuzzers/raw_apply_fuzzer.cc)4
-rwxr-xr-xchromium/components/zucchini/fuzzers/create_seed_file_pair.py19
-rw-r--r--chromium/components/zucchini/fuzzers/disassembler_dex_fuzzer.cc54
-rw-r--r--chromium/components/zucchini/fuzzers/disassembler_win32_fuzzer.cc88
-rw-r--r--chromium/components/zucchini/fuzzers/file_pair.proto8
-rwxr-xr-xchromium/components/zucchini/fuzzers/generate_fuzzer_data.py12
-rw-r--r--chromium/components/zucchini/fuzzers/imposed_ensemble_matcher_fuzzer.cc67
-rw-r--r--chromium/components/zucchini/fuzzers/raw_gen_fuzzer.cc21
-rw-r--r--chromium/components/zucchini/fuzzers/ztf_gen_fuzzer.cc67
-rw-r--r--chromium/components/zucchini/integration_test.cc13
-rw-r--r--chromium/components/zucchini/main_utils.cc2
-rw-r--r--chromium/components/zucchini/mapped_file.cc4
-rw-r--r--chromium/components/zucchini/mapped_file.h4
-rw-r--r--chromium/components/zucchini/reloc_utils.cc12
-rw-r--r--chromium/components/zucchini/reloc_utils_unittest.cc2
-rw-r--r--chromium/components/zucchini/type_dex.h30
-rw-r--r--chromium/components/zucchini/zucchini.h33
-rw-r--r--chromium/components/zucchini/zucchini_apply.cc17
-rw-r--r--chromium/components/zucchini/zucchini_commands.cc66
-rw-r--r--chromium/components/zucchini/zucchini_gen.cc44
-rw-r--r--chromium/components/zucchini/zucchini_gen_unittest.cc7
-rw-r--r--chromium/components/zucchini/zucchini_integration.cc154
-rw-r--r--chromium/components/zucchini/zucchini_integration.h53
2563 files changed, 106167 insertions, 54272 deletions
diff --git a/chromium/components/BUILD.gn b/chromium/components/BUILD.gn
index 867b35a7267..45e7fa41170 100644
--- a/chromium/components/BUILD.gn
+++ b/chromium/components/BUILD.gn
@@ -7,6 +7,7 @@ import("//build/config/features.gni")
import("//build/config/ui.gni")
import("//components/nacl/features.gni")
import("//media/media_options.gni")
+import("//ppapi/buildflags/buildflags.gni")
import("//printing/buildflags/buildflags.gni")
import("//rlz/buildflags/buildflags.gni")
import("//testing/test.gni")
@@ -67,6 +68,8 @@ test("components_unittests") {
"//components/autofill/core/browser:unit_tests",
"//components/autofill/core/common:unit_tests",
"//components/base32:unit_tests",
+ "//components/blacklist/opt_out_blacklist:unit_tests",
+ "//components/blacklist/opt_out_blacklist/sql:unit_tests",
"//components/bookmarks/browser:unit_tests",
"//components/bookmarks/managed:unit_tests",
"//components/browser_sync:unit_tests",
@@ -81,7 +84,6 @@ test("components_unittests") {
"//components/content_settings/core/browser:unit_tests",
"//components/content_settings/core/common:unit_tests",
"//components/crx_file:unit_tests",
- "//components/data_usage/core:unit_tests",
"//components/device_event_log:unit_tests",
"//components/dom_distiller/core:unit_tests",
"//components/download:unit_tests",
@@ -127,6 +129,7 @@ test("components_unittests") {
"//components/query_parser:unit_tests",
"//components/rappor:unit_tests",
"//components/reading_list/core:unit_tests",
+ "//components/safe_search_api:unit_tests",
"//components/search:unit_tests",
"//components/search_engines:unit_tests",
"//components/search_provider_logos:unit_tests",
@@ -153,6 +156,7 @@ test("components_unittests") {
"//components/translate/core/language_detection:unit_tests",
"//components/ukm:unit_tests",
"//components/undo:unit_tests",
+ "//components/unified_consent:unit_tests",
"//components/update_client:unit_tests",
"//components/upload_list:unit_tests",
"//components/url_formatter:unit_tests",
@@ -176,7 +180,7 @@ test("components_unittests") {
if (is_ios) {
deps += [
"//components/autofill/ios/browser:unit_tests",
- "//components/autofill/ios/fill:unit_tests",
+ "//components/autofill/ios/form_util:unit_tests",
"//components/image_fetcher/ios:unit_tests",
"//components/signin/ios/browser:unit_tests",
"//components/translate/ios/browser:unit_tests",
@@ -472,11 +476,11 @@ if (!is_ios) {
"//content/test:browsertest_support",
"//content/test:test_support",
"//device/bluetooth",
- "//device/geolocation/public/cpp:test_support",
"//google_apis",
"//ipc:test_support",
"//net:test_support",
"//printing/buildflags",
+ "//services/device/public/cpp/test:test_support",
"//services/device/public/mojom",
"//services/service_manager/public/cpp",
"//services/service_manager/public/mojom",
@@ -496,7 +500,7 @@ if (!is_ios) {
data += [ "$root_out_dir/Content Shell.app/" ]
}
- if (!is_android) {
+ if (enable_plugins) {
sources += [ "pdf/renderer/pdf_accessibility_tree_browsertest.cc" ]
deps += [ "//components/pdf/renderer" ]
}
@@ -566,18 +570,15 @@ if (!is_ios) {
data_deps = [
":components_tests_pak",
"//ui/resources:ui_test_pak",
+
+ # Needed for isolate script to execute.
+ "//testing:run_perf_test",
]
data = [
"$root_out_dir/components_tests_resources.pak",
"$root_out_dir/ui_test.pak",
"//components/subresource_filter/core/common/perftests/data",
-
- # Needed for isolate script to execute.
- "//testing/scripts/common.py",
- "//testing/xvfb.py",
- "//testing/scripts/run_gtest_perf_test.py",
- "//tools/perf/generate_legacy_perf_dashboard_json.py",
]
if (is_android) {
diff --git a/chromium/components/OWNERS b/chromium/components/OWNERS
index 5bbb56254ee..3b022eb87f9 100644
--- a/chromium/components/OWNERS
+++ b/chromium/components/OWNERS
@@ -13,6 +13,7 @@ per-file crash_strings.grdp=file://components/crash/OWNERS
per-file dom_distiller_strings.grdp=file://components/dom_distiller/OWNERS
per-file error_page_strings.grdp=file://components/error_page/OWNERS
per-file ntp_snippets_strings.grdp=file://components/ntp_snippets/OWNERS
+per-file nux_google_apps_strings.grdp=file://components/nux_google_apps/OWNERS
per-file omnibox_strings.grdp=file://components/omnibox/OWNERS
per-file page_info_strings.grdp=file://chrome/browser/ui/page_info/OWNERS
per-file page_info_strings_grdp=file://chrome/browser/ui/page_info/OWNERS
@@ -30,7 +31,7 @@ per-file sync_ui_strings.grdp=file://components/sync/OWNERS
per-file translate_strings.grdp=file://components/translate/OWNERS
per-file undo_strings.grdp=file://components/undo/OWNERS
per-file version_ui_strings.grdp=file://components/version_ui/OWNERS
-per-file web_contents_delegate_android_strings.grdp=file://components/web_contents_delegate_android/OWNERS
+per-file web_contents_delegate_android_strings.grdp=file://components/embedder_support/android/delegate/OWNERS
# These are for the common case of adding or removing tests. If you're making
# structural changes, please get a review from one of the overall components
diff --git a/chromium/components/README b/chromium/components/README
index b3bb2ffb652..a5053b2c8bf 100644
--- a/chromium/components/README
+++ b/chromium/components/README
@@ -1,11 +1,19 @@
This directory is for features that are intended for reuse. Example use cases:
--code that is shared by Chrome on iOS and other Chrome platforms (since the iOS
- port doesn't use src/chrome)
--code that is shared between multiple embedders of content (e.g., Android
+- features that are shared by Chrome on iOS and other Chrome platforms (since
+ the iOS port doesn't use src/chrome)
+- features that are shared between multiple embedders of content (e.g., Android
WebView and Chrome)
--code that is shared between Blink and the browser process (since code in the
- browser doesn't use Blink, while Blink doesn't include content or chrome to
- avoid circular dependencies)
+- features that are shared between Blink and the browser process
+ * Note: It is also possible to place code shared between Blink and the
+ browser process into //third_party/blink/common. The distinction comes
+ down to (a) whether Blink is the owner of the code in question or a consumer
+ of it and (b) whether the code in question is shared by Chrome on iOS as
+ well. If the code is conceptually its own cross-process feature with Blink
+ as a consumer, then //components can make sense. If it's conceptually
+ Blink code, then third_party/blink/common likely makes more sense. (In the
+ so-far hypothetical case where it's conceptually Blink code that is shared
+ by iOS, raise the question on chromium-dev@, where the right folks will see
+ it).
In general, if some code is used by a directory "foo" and things above "foo" in
the dependency tree, the code should probably live in "foo".
diff --git a/chromium/components/about_handler/url_request_about_job.cc b/chromium/components/about_handler/url_request_about_job.cc
index 834b325d25a..af4cdfcc9cd 100644
--- a/chromium/components/about_handler/url_request_about_job.cc
+++ b/chromium/components/about_handler/url_request_about_job.cc
@@ -26,8 +26,8 @@ void URLRequestAboutJob::Start() {
// Start reading asynchronously so that all error reporting and data
// callbacks happen as they would for network requests.
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::Bind(&URLRequestAboutJob::StartAsync, weak_factory_.GetWeakPtr()));
+ FROM_HERE, base::BindOnce(&URLRequestAboutJob::StartAsync,
+ weak_factory_.GetWeakPtr()));
}
void URLRequestAboutJob::Kill() {
diff --git a/chromium/components/app_modal/javascript_dialog_manager.cc b/chromium/components/app_modal/javascript_dialog_manager.cc
index 6e7dc9fdfbc..14427e30891 100644
--- a/chromium/components/app_modal/javascript_dialog_manager.cc
+++ b/chromium/components/app_modal/javascript_dialog_manager.cc
@@ -56,17 +56,6 @@ bool ShouldDisplaySuppressCheckbox(
return extra_data->has_already_shown_a_dialog_;
}
-void LogUMAMessageLengthStats(const base::string16& message) {
- UMA_HISTOGRAM_COUNTS("JSDialogs.CountOfJSDialogMessageCharacters",
- static_cast<int32_t>(message.length()));
-
- int32_t newline_count =
- std::count_if(message.begin(), message.end(),
- [](const base::char16& c) { return c == '\n'; });
- UMA_HISTOGRAM_COUNTS("JSDialogs.CountOfJSDialogMessageNewlines",
- newline_count);
-}
-
} // namespace
// static
@@ -217,7 +206,6 @@ void JavaScriptDialogManager::RunJavaScriptDialog(
extensions_client_->OnDialogOpened(web_contents);
- LogUMAMessageLengthStats(message_text);
AppModalDialogQueue::GetInstance()->AddDialog(new JavaScriptAppModalDialog(
web_contents, &javascript_dialog_extra_data_, dialog_title, dialog_type,
message_text, default_prompt_text,
@@ -234,6 +222,16 @@ void JavaScriptDialogManager::RunBeforeUnloadDialog(
content::RenderFrameHost* render_frame_host,
bool is_reload,
DialogClosedCallback callback) {
+ RunBeforeUnloadDialogWithOptions(web_contents, render_frame_host, is_reload,
+ false, std::move(callback));
+}
+
+void JavaScriptDialogManager::RunBeforeUnloadDialogWithOptions(
+ content::WebContents* web_contents,
+ content::RenderFrameHost* render_frame_host,
+ bool is_reload,
+ bool is_app,
+ DialogClosedCallback callback) {
ChromeJavaScriptDialogExtraData* extra_data =
&javascript_dialog_extra_data_[web_contents];
@@ -245,21 +243,27 @@ void JavaScriptDialogManager::RunBeforeUnloadDialog(
}
// Build the dialog message. We explicitly do _not_ allow the webpage to
- // specify the contents of this dialog, because most of the time nowadays it's
- // used for scams.
+ // specify the contents of this dialog, as per the current spec
//
- // This does not violate the spec. Per
- // https://html.spec.whatwg.org/#prompt-to-unload-a-document, step 7:
+ // https://html.spec.whatwg.org/#unloading-documents, step 8:
//
- // "The prompt shown by the user agent may include the string of the
- // returnValue attribute, or some leading subset thereof."
+ // "The message shown to the user is not customizable, but instead
+ // determined by the user agent. In particular, the actual value of the
+ // returnValue attribute is ignored."
//
- // The prompt MAY include the string. It doesn't any more. Scam web page
- // authors have abused this, so we're taking away the toys from everyone. This
- // is why we can't have nice things.
-
- const base::string16 title = l10n_util::GetStringUTF16(is_reload ?
- IDS_BEFORERELOAD_MESSAGEBOX_TITLE : IDS_BEFOREUNLOAD_MESSAGEBOX_TITLE);
+ // This message used to be customizable, but it was frequently abused by
+ // scam websites so the specification was changed.
+
+ base::string16 title;
+ if (is_app) {
+ title = l10n_util::GetStringUTF16(
+ is_reload ? IDS_BEFORERELOAD_APP_MESSAGEBOX_TITLE
+ : IDS_BEFOREUNLOAD_APP_MESSAGEBOX_TITLE);
+ } else {
+ title = l10n_util::GetStringUTF16(is_reload
+ ? IDS_BEFORERELOAD_MESSAGEBOX_TITLE
+ : IDS_BEFOREUNLOAD_MESSAGEBOX_TITLE);
+ }
const base::string16 message =
l10n_util::GetStringUTF16(IDS_BEFOREUNLOAD_MESSAGEBOX_MESSAGE);
diff --git a/chromium/components/app_modal/javascript_dialog_manager.h b/chromium/components/app_modal/javascript_dialog_manager.h
index 57c1cac9d4d..490881465ee 100644
--- a/chromium/components/app_modal/javascript_dialog_manager.h
+++ b/chromium/components/app_modal/javascript_dialog_manager.h
@@ -42,6 +42,15 @@ class JavaScriptDialogManager : public content::JavaScriptDialogManager {
base::string16 GetTitle(content::WebContents* web_contents,
const GURL& alerting_frame_url);
+ // Displays a dialog asking the user if they want to leave a page. Displays
+ // a different message if the site is in an app window.
+ void RunBeforeUnloadDialogWithOptions(
+ content::WebContents* web_contents,
+ content::RenderFrameHost* render_frame_host,
+ bool is_reload,
+ bool is_app,
+ DialogClosedCallback callback);
+
// JavaScriptDialogManager:
void RunJavaScriptDialog(content::WebContents* web_contents,
content::RenderFrameHost* render_frame_host,
diff --git a/chromium/components/app_modal_strings.grdp b/chromium/components/app_modal_strings.grdp
index 7ffaaeb2141..fcb4999358c 100644
--- a/chromium/components/app_modal_strings.grdp
+++ b/chromium/components/app_modal_strings.grdp
@@ -23,6 +23,9 @@
<message name="IDS_BEFOREUNLOAD_MESSAGEBOX_TITLE" desc="Title for the 'before unload' dialog.">
Leave site?
</message>
+ <message name="IDS_BEFOREUNLOAD_APP_MESSAGEBOX_TITLE" desc="Title for the 'before unload' dialog for apps.">
+ Leave app?
+ </message>
<message name="IDS_BEFOREUNLOAD_MESSAGEBOX_OK_BUTTON_LABEL" desc="The text on the button which navigates the user away from the page.">
Leave
</message>
@@ -34,6 +37,9 @@
<message name="IDS_BEFORERELOAD_MESSAGEBOX_TITLE" desc="Title for the 'before unload' dialog for reloads.">
Reload site?
</message>
+ <message name="IDS_BEFORERELOAD_APP_MESSAGEBOX_TITLE" desc="Title for the 'before unload' dialog for reloads of apps.">
+ Reload app?
+ </message>
<message name="IDS_BEFORERELOAD_MESSAGEBOX_OK_BUTTON_LABEL" desc="The text on the button which reloads the page.">
Reload
</message>
diff --git a/chromium/components/arc/BUILD.gn b/chromium/components/arc/BUILD.gn
index ee4af0d1166..caf03829d3d 100644
--- a/chromium/components/arc/BUILD.gn
+++ b/chromium/components/arc/BUILD.gn
@@ -6,6 +6,8 @@ import("//testing/test.gni")
static_library("arc") {
sources = [
+ "appfuse/arc_appfuse_bridge.cc",
+ "appfuse/arc_appfuse_bridge.h",
"audio/arc_audio_bridge.cc",
"audio/arc_audio_bridge.h",
"bluetooth/bluetooth_type_converters.cc",
@@ -14,6 +16,8 @@ static_library("arc") {
"clipboard/arc_clipboard_bridge.h",
"crash_collector/arc_crash_collector_bridge.cc",
"crash_collector/arc_crash_collector_bridge.h",
+ "disk_quota/arc_disk_quota_bridge.cc",
+ "disk_quota/arc_disk_quota_bridge.h",
"ime/arc_ime_bridge.h",
"ime/arc_ime_bridge_impl.cc",
"ime/arc_ime_bridge_impl.h",
@@ -32,8 +36,7 @@ static_library("arc") {
"intent_helper/intent_filter.h",
"intent_helper/link_handler_model.cc",
"intent_helper/link_handler_model.h",
- "intent_helper/page_transition_util.cc",
- "intent_helper/page_transition_util.h",
+ "intent_helper/open_url_delegate.h",
"lock_screen/arc_lock_screen_bridge.cc",
"lock_screen/arc_lock_screen_bridge.h",
"metrics/arc_metrics_service.cc",
@@ -50,12 +53,8 @@ static_library("arc") {
"rotation_lock/arc_rotation_lock_bridge.h",
"storage_manager/arc_storage_manager.cc",
"storage_manager/arc_storage_manager.h",
- "timer/arc_timer.cc",
- "timer/arc_timer.h",
"timer/arc_timer_bridge.cc",
"timer/arc_timer_bridge.h",
- "timer/create_timer_request.cc",
- "timer/create_timer_request.h",
"usb/usb_host_bridge.cc",
"usb/usb_host_bridge.h",
"usb/usb_host_ui_delegate.h",
@@ -93,7 +92,6 @@ static_library("arc") {
"//device/usb/mojo",
"//device/usb/public/mojom",
"//google_apis",
- "//mojo/edk",
"//services/device/public/mojom",
"//skia",
"//third_party/re2:re2",
@@ -151,6 +149,8 @@ static_library("arc_base") {
"arc_data_remover.h",
"arc_features.cc",
"arc_features.h",
+ "arc_features_parser.cc",
+ "arc_features_parser.h",
"arc_service_manager.cc",
"arc_service_manager.h",
"arc_session.cc",
@@ -173,7 +173,8 @@ static_library("arc_base") {
"//components/prefs",
"//components/user_manager",
"//content/public/common",
- "//mojo/edk",
+ "//mojo/public/cpp/platform",
+ "//mojo/public/cpp/system",
"//ui/aura",
]
@@ -260,7 +261,7 @@ static_library("arc_test_support") {
"//components/prefs:test_support",
"//components/user_prefs",
"//content/test:test_support",
- "//mojo/edk",
+ "//mojo/public/cpp/platform",
"//mojo/public/cpp/system",
]
}
@@ -269,6 +270,7 @@ source_set("unit_tests") {
testonly = true
sources = [
"arc_data_remover_unittest.cc",
+ "arc_features_parser_unittest.cc",
"arc_session_impl_unittest.cc",
"arc_session_runner_unittest.cc",
"arc_util_unittest.cc",
@@ -280,7 +282,6 @@ source_set("unit_tests") {
"intent_helper/font_size_util_unittest.cc",
"intent_helper/intent_filter_unittest.cc",
"intent_helper/link_handler_model_unittest.cc",
- "intent_helper/page_transition_util_unittest.cc",
"metrics/arc_metrics_service_unittest.cc",
"power/arc_power_bridge_unittest.cc",
"timer/arc_timer_bridge_unittest.cc",
@@ -315,6 +316,7 @@ source_set("unit_tests") {
"//ui/base/ime",
"//ui/events",
"//ui/events:dom_keycode_converter",
+ "//ui/keyboard:keyboard",
"//url:url",
]
}
diff --git a/chromium/components/arc/appfuse/OWNERS b/chromium/components/arc/appfuse/OWNERS
new file mode 100644
index 00000000000..e162772527d
--- /dev/null
+++ b/chromium/components/arc/appfuse/OWNERS
@@ -0,0 +1 @@
+hashimoto@chromium.org
diff --git a/chromium/components/arc/appfuse/arc_appfuse_bridge.cc b/chromium/components/arc/appfuse/arc_appfuse_bridge.cc
new file mode 100644
index 00000000000..a7b4b3208eb
--- /dev/null
+++ b/chromium/components/arc/appfuse/arc_appfuse_bridge.cc
@@ -0,0 +1,102 @@
+// 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/arc/appfuse/arc_appfuse_bridge.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/singleton.h"
+#include "chromeos/dbus/arc_appfuse_provider_client.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+
+#include <sys/epoll.h>
+
+namespace arc {
+
+namespace {
+
+// Singleton factory for ArcAppfuseBridge.
+class ArcAppfuseBridgeFactory
+ : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+ ArcAppfuseBridge,
+ ArcAppfuseBridgeFactory> {
+ public:
+ // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+ static constexpr const char* kName = "ArcAppfuseBridgeFactory";
+
+ static ArcAppfuseBridgeFactory* GetInstance() {
+ return base::Singleton<ArcAppfuseBridgeFactory>::get();
+ }
+
+ private:
+ friend base::DefaultSingletonTraits<ArcAppfuseBridgeFactory>;
+ ArcAppfuseBridgeFactory() = default;
+ ~ArcAppfuseBridgeFactory() override = default;
+};
+
+void RunWithScopedHandle(base::OnceCallback<void(mojo::ScopedHandle)> callback,
+ base::Optional<base::ScopedFD> fd) {
+ if (!fd || !fd.value().is_valid()) {
+ LOG(ERROR) << "Invalid FD: fd.has_value() = " << fd.has_value();
+ std::move(callback).Run(mojo::ScopedHandle());
+ return;
+ }
+ mojo::ScopedHandle wrapped_handle =
+ mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(fd.value())));
+ if (!wrapped_handle.is_valid()) {
+ LOG(ERROR) << "Failed to wrap handle";
+ std::move(callback).Run(mojo::ScopedHandle());
+ return;
+ }
+ std::move(callback).Run(std::move(wrapped_handle));
+}
+
+} // namespace
+
+// static
+ArcAppfuseBridge* ArcAppfuseBridge::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return ArcAppfuseBridgeFactory::GetForBrowserContext(context);
+}
+
+ArcAppfuseBridge::ArcAppfuseBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service)
+ : arc_bridge_service_(bridge_service) {
+ arc_bridge_service_->appfuse()->SetHost(this);
+}
+
+ArcAppfuseBridge::~ArcAppfuseBridge() {
+ arc_bridge_service_->appfuse()->SetHost(nullptr);
+}
+
+void ArcAppfuseBridge::Mount(uint32_t uid,
+ int32_t mount_id,
+ MountCallback callback) {
+ // This is always safe because DBusThreadManager outlives ArcServiceLauncher.
+ chromeos::DBusThreadManager::Get()->GetArcAppfuseProviderClient()->Mount(
+ uid, mount_id, base::BindOnce(&RunWithScopedHandle, std::move(callback)));
+}
+
+void ArcAppfuseBridge::Unmount(uint32_t uid,
+ int32_t mount_id,
+ UnmountCallback callback) {
+ chromeos::DBusThreadManager::Get()->GetArcAppfuseProviderClient()->Unmount(
+ uid, mount_id, std::move(callback));
+}
+
+void ArcAppfuseBridge::OpenFile(uint32_t uid,
+ int32_t mount_id,
+ int32_t file_id,
+ int32_t flags,
+ OpenFileCallback callback) {
+ chromeos::DBusThreadManager::Get()->GetArcAppfuseProviderClient()->OpenFile(
+ uid, mount_id, file_id, flags,
+ base::BindOnce(&RunWithScopedHandle, std::move(callback)));
+}
+
+} // namespace arc
diff --git a/chromium/components/arc/appfuse/arc_appfuse_bridge.h b/chromium/components/arc/appfuse/arc_appfuse_bridge.h
new file mode 100644
index 00000000000..615f01b2ceb
--- /dev/null
+++ b/chromium/components/arc/appfuse/arc_appfuse_bridge.h
@@ -0,0 +1,53 @@
+// 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_ARC_APPFUSE_ARC_APPFUSE_BRIDGE_H_
+#define COMPONENTS_ARC_APPFUSE_ARC_APPFUSE_BRIDGE_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "components/arc/common/appfuse.mojom.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace content {
+class BrowserContext;
+} // namespace content
+
+namespace arc {
+
+class ArcBridgeService;
+
+// This class handles Appfuse mount/unmount requests from the ARC container.
+class ArcAppfuseBridge : public KeyedService, public mojom::AppfuseHost {
+ public:
+ // Returns singleton instance for the given BrowserContext,
+ // or nullptr if the browser |context| is not allowed to use ARC.
+ static ArcAppfuseBridge* GetForBrowserContext(
+ content::BrowserContext* context);
+
+ ArcAppfuseBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service);
+ ~ArcAppfuseBridge() override;
+
+ // mojom::AppfuseHost overrides:
+ void Mount(uint32_t uid, int32_t mount_id, MountCallback callback) override;
+ void Unmount(uint32_t uid,
+ int32_t mount_id,
+ UnmountCallback callback) override;
+ void OpenFile(uint32_t uid,
+ int32_t mount_id,
+ int32_t file_id,
+ int32_t flags,
+ OpenFileCallback callback) override;
+
+ private:
+ ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
+
+ DISALLOW_COPY_AND_ASSIGN(ArcAppfuseBridge);
+};
+
+} // namespace arc
+
+#endif // COMPONENTS_ARC_APPFUSE_ARC_APPFUSE_BRIDGE_H_
diff --git a/chromium/components/arc/arc_bridge_host_impl.cc b/chromium/components/arc/arc_bridge_host_impl.cc
index c59d2db6b9e..93e74a82e02 100644
--- a/chromium/components/arc/arc_bridge_host_impl.cc
+++ b/chromium/components/arc/arc_bridge_host_impl.cc
@@ -46,6 +46,11 @@ void ArcBridgeHostImpl::OnAppInstanceReady(mojom::AppInstancePtr app_ptr) {
OnInstanceReady(arc_bridge_service_->app(), std::move(app_ptr));
}
+void ArcBridgeHostImpl::OnAppfuseInstanceReady(
+ mojom::AppfuseInstancePtr appfuse_ptr) {
+ OnInstanceReady(arc_bridge_service_->appfuse(), std::move(appfuse_ptr));
+}
+
void ArcBridgeHostImpl::OnAudioInstanceReady(
mojom::AudioInstancePtr audio_ptr) {
OnInstanceReady(arc_bridge_service_->audio(), std::move(audio_ptr));
@@ -94,6 +99,11 @@ void ArcBridgeHostImpl::OnCrashCollectorInstanceReady(
std::move(crash_collector_ptr));
}
+void ArcBridgeHostImpl::OnDiskQuotaInstanceReady(
+ mojom::DiskQuotaInstancePtr disk_quota_ptr) {
+ OnInstanceReady(arc_bridge_service_->disk_quota(), std::move(disk_quota_ptr));
+}
+
void ArcBridgeHostImpl::OnEnterpriseReportingInstanceReady(
mojom::EnterpriseReportingInstancePtr enterprise_reporting_ptr) {
OnInstanceReady(arc_bridge_service_->enterprise_reporting(),
diff --git a/chromium/components/arc/arc_bridge_host_impl.h b/chromium/components/arc/arc_bridge_host_impl.h
index 07ca1b8d308..df466053001 100644
--- a/chromium/components/arc/arc_bridge_host_impl.h
+++ b/chromium/components/arc/arc_bridge_host_impl.h
@@ -39,6 +39,7 @@ class ArcBridgeHostImpl : public mojom::ArcBridgeHost {
void OnAccessibilityHelperInstanceReady(
mojom::AccessibilityHelperInstancePtr accessibility_helper_ptr) override;
void OnAppInstanceReady(mojom::AppInstancePtr app_ptr) override;
+ void OnAppfuseInstanceReady(mojom::AppfuseInstancePtr appfuse_ptr) override;
void OnAudioInstanceReady(mojom::AudioInstancePtr audio_ptr) override;
void OnAuthInstanceReady(mojom::AuthInstancePtr auth_ptr) override;
void OnBackupSettingsInstanceReady(
@@ -55,6 +56,8 @@ class ArcBridgeHostImpl : public mojom::ArcBridgeHost {
mojom::ClipboardInstancePtr clipboard_ptr) override;
void OnCrashCollectorInstanceReady(
mojom::CrashCollectorInstancePtr crash_collector_ptr) override;
+ void OnDiskQuotaInstanceReady(
+ mojom::DiskQuotaInstancePtr disk_quota_ptr) override;
void OnEnterpriseReportingInstanceReady(
mojom::EnterpriseReportingInstancePtr enterprise_reporting_ptr) override;
void OnFileSystemInstanceReady(
diff --git a/chromium/components/arc/arc_bridge_service.h b/chromium/components/arc/arc_bridge_service.h
index 43bd651aaa1..75dc0461f45 100644
--- a/chromium/components/arc/arc_bridge_service.h
+++ b/chromium/components/arc/arc_bridge_service.h
@@ -18,6 +18,8 @@ class AccessibilityHelperHost;
class AccessibilityHelperInstance;
class AppHost;
class AppInstance;
+class AppfuseHost;
+class AppfuseInstance;
class AudioHost;
class AudioInstance;
class AuthHost;
@@ -34,6 +36,8 @@ class ClipboardHost;
class ClipboardInstance;
class CrashCollectorHost;
class CrashCollectorInstance;
+class DiskQuotaHost;
+class DiskQuotaInstance;
class EnterpriseReportingHost;
class EnterpriseReportingInstance;
class FileSystemHost;
@@ -68,8 +72,8 @@ class RotationLockInstance;
class ScreenCaptureHost;
class ScreenCaptureInstance;
class StorageManagerInstance;
-class TimerInstance;
class TimerHost;
+class TimerInstance;
class TracingInstance;
class TtsHost;
class TtsInstance;
@@ -103,6 +107,9 @@ class ArcBridgeService {
return &accessibility_helper_;
}
ConnectionHolder<mojom::AppInstance, mojom::AppHost>* app() { return &app_; }
+ ConnectionHolder<mojom::AppfuseInstance, mojom::AppfuseHost>* appfuse() {
+ return &appfuse_;
+ }
ConnectionHolder<mojom::AudioInstance, mojom::AudioHost>* audio() {
return &audio_;
}
@@ -136,6 +143,10 @@ class ArcBridgeService {
crash_collector() {
return &crash_collector_;
}
+ ConnectionHolder<mojom::DiskQuotaInstance, mojom::DiskQuotaHost>*
+ disk_quota() {
+ return &disk_quota_;
+ }
ConnectionHolder<mojom::EnterpriseReportingInstance,
mojom::EnterpriseReportingHost>*
enterprise_reporting() {
@@ -234,6 +245,7 @@ class ArcBridgeService {
mojom::AccessibilityHelperHost>
accessibility_helper_;
ConnectionHolder<mojom::AppInstance, mojom::AppHost> app_;
+ ConnectionHolder<mojom::AppfuseInstance, mojom::AppfuseHost> appfuse_;
ConnectionHolder<mojom::AudioInstance, mojom::AudioHost> audio_;
ConnectionHolder<mojom::AuthInstance, mojom::AuthHost> auth_;
ConnectionHolder<mojom::BackupSettingsInstance> backup_settings_;
@@ -245,6 +257,7 @@ class ArcBridgeService {
ConnectionHolder<mojom::ClipboardInstance, mojom::ClipboardHost> clipboard_;
ConnectionHolder<mojom::CrashCollectorInstance, mojom::CrashCollectorHost>
crash_collector_;
+ ConnectionHolder<mojom::DiskQuotaInstance, mojom::DiskQuotaHost> disk_quota_;
ConnectionHolder<mojom::EnterpriseReportingInstance,
mojom::EnterpriseReportingHost>
enterprise_reporting_;
diff --git a/chromium/components/arc/arc_features.cc b/chromium/components/arc/arc_features.cc
index b87808fd3c1..58639ae4d16 100644
--- a/chromium/components/arc/arc_features.cc
+++ b/chromium/components/arc/arc_features.cc
@@ -19,12 +19,18 @@ const base::Feature kBootCompletedBroadcastFeature {
// Controls whether we should delete all ARC data before transitioning a user
// from regular to child account.
const base::Feature kCleanArcDataOnRegularToChildTransitionFeature{
- "ArcCleanDataOnRegularToChildTransition", base::FEATURE_ENABLED_BY_DEFAULT};
+ "ArcCleanDataOnRegularToChildTransition",
+ base::FEATURE_DISABLED_BY_DEFAULT};
// Controls whether ARC handles child->regular account transition.
const base::Feature kEnableChildToRegularTransitionFeature{
"ArcEnableChildToRegularTransition", base::FEATURE_ENABLED_BY_DEFAULT};
+// Controls whether ARC input methods (usually installed via Play Store) are
+// available.
+const base::Feature kEnableInputMethodFeature{
+ "ArcInputMethod", base::FEATURE_DISABLED_BY_DEFAULT};
+
// Controls whether ARC handles regular->child account transition.
const base::Feature kEnableRegularToChildTransitionFeature{
"ArcEnableRegularToChildTransition", base::FEATURE_ENABLED_BY_DEFAULT};
@@ -34,6 +40,12 @@ const base::Feature kNativeBridgeExperimentFeature {
"ArcNativeBridgeExperiment", base::FEATURE_ENABLED_BY_DEFAULT
};
+// Controls Smart Text Selection for Chrome.
+// When enabled, the context menu will show contextual quick actions based on
+// the current text selection.
+const base::Feature kSmartTextSelectionFeature{
+ "ArcSmartTextSelection", base::FEATURE_DISABLED_BY_DEFAULT};
+
// Controls ARC USB host integration.
// When enabled, Android apps will be able to use usb host features.
const base::Feature kUsbHostFeature{"ArcUsbHost",
diff --git a/chromium/components/arc/arc_features.h b/chromium/components/arc/arc_features.h
index 25fd5a603a7..1935b45deaf 100644
--- a/chromium/components/arc/arc_features.h
+++ b/chromium/components/arc/arc_features.h
@@ -16,8 +16,10 @@ extern const base::Feature kAvailableForChildAccountFeature;
extern const base::Feature kBootCompletedBroadcastFeature;
extern const base::Feature kCleanArcDataOnRegularToChildTransitionFeature;
extern const base::Feature kEnableChildToRegularTransitionFeature;
+extern const base::Feature kEnableInputMethodFeature;
extern const base::Feature kEnableRegularToChildTransitionFeature;
extern const base::Feature kNativeBridgeExperimentFeature;
+extern const base::Feature kSmartTextSelectionFeature;
extern const base::Feature kUsbHostFeature;
extern const base::Feature kVpnFeature;
diff --git a/chromium/components/arc/arc_features_parser.cc b/chromium/components/arc/arc_features_parser.cc
new file mode 100644
index 00000000000..beea0d991f2
--- /dev/null
+++ b/chromium/components/arc/arc_features_parser.cc
@@ -0,0 +1,139 @@
+// 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/arc/arc_features_parser.h"
+
+#include <memory>
+
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/values.h"
+
+namespace arc {
+
+namespace {
+
+constexpr const base::FilePath::CharType kArcFeaturesJsonFile[] =
+ FILE_PATH_LITERAL("/etc/arc/features.json");
+
+base::Optional<ArcFeatures> ParseFeaturesJson(base::StringPiece input_json) {
+ ArcFeatures arc_features;
+
+ int error_code;
+ std::string error_msg;
+ std::unique_ptr<base::Value> json_value =
+ base::JSONReader::ReadAndReturnError(input_json, base::JSON_PARSE_RFC,
+ &error_code, &error_msg);
+ if (!json_value || !json_value->is_dict()) {
+ LOG(ERROR) << "Error parsing feature JSON: " << error_msg;
+ return base::nullopt;
+ }
+
+ // Parse each item under features.
+ const base::Value* feature_list =
+ json_value->FindKeyOfType("features", base::Value::Type::LIST);
+ if (!feature_list) {
+ LOG(ERROR) << "No feature list in JSON.";
+ return base::nullopt;
+ }
+ for (auto& feature_item : feature_list->GetList()) {
+ const base::Value* feature_name =
+ feature_item.FindKeyOfType("name", base::Value::Type::STRING);
+ const base::Value* feature_version =
+ feature_item.FindKeyOfType("version", base::Value::Type::INTEGER);
+ if (!feature_name || feature_name->GetString().empty()) {
+ LOG(ERROR) << "Missing name in the feature.";
+ return base::nullopt;
+ }
+ if (!feature_version) {
+ LOG(ERROR) << "Missing version in the feature.";
+ return base::nullopt;
+ }
+ arc_features.feature_map.emplace(feature_name->GetString(),
+ feature_version->GetInt());
+ }
+
+ // Parse each item under unavailable_features.
+ const base::Value* unavailable_feature_list = json_value->FindKeyOfType(
+ "unavailable_features", base::Value::Type::LIST);
+ if (!unavailable_feature_list) {
+ LOG(ERROR) << "No unavailable feature list in JSON.";
+ return base::nullopt;
+ }
+ for (auto& feature_item : unavailable_feature_list->GetList()) {
+ if (!feature_item.is_string()) {
+ LOG(ERROR) << "Item in the unavailable feature list is not a string.";
+ return base::nullopt;
+ }
+
+ if (feature_item.GetString().empty()) {
+ LOG(ERROR) << "Missing name in the feature.";
+ return base::nullopt;
+ }
+ arc_features.unavailable_features.emplace_back(feature_item.GetString());
+ }
+
+ // Parse each item under properties.
+ const base::Value* properties =
+ json_value->FindKeyOfType("properties", base::Value::Type::DICTIONARY);
+ if (!properties) {
+ LOG(ERROR) << "No properties in JSON.";
+ return base::nullopt;
+ }
+ for (const auto& item : properties->DictItems()) {
+ if (!item.second.is_string()) {
+ LOG(ERROR) << "Item in the properties mapping is not a string.";
+ return base::nullopt;
+ }
+
+ arc_features.build_props.emplace(item.first, item.second.GetString());
+ }
+
+ return arc_features;
+}
+
+base::Optional<ArcFeatures> ReadOnFileThread(const base::FilePath& file_path) {
+ DCHECK(!file_path.empty());
+ base::AssertBlockingAllowed();
+
+ std::string input_json;
+ if (!base::ReadFileToString(file_path, &input_json)) {
+ PLOG(ERROR) << "Cannot read file " << file_path.value() << " into string.";
+ return base::nullopt;
+ }
+
+ if (input_json.empty()) {
+ LOG(ERROR) << "Input JSON is empty in file " << file_path.value();
+ return base::nullopt;
+ }
+
+ return ParseFeaturesJson(input_json);
+}
+
+} // namespace
+
+ArcFeatures::ArcFeatures() = default;
+ArcFeatures::ArcFeatures(ArcFeatures&& other) = default;
+ArcFeatures::~ArcFeatures() = default;
+ArcFeatures& ArcFeatures::operator=(ArcFeatures&& other) = default;
+
+void ArcFeaturesParser::GetArcFeatures(
+ base::OnceCallback<void(base::Optional<ArcFeatures>)> callback) {
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::BindOnce(&ReadOnFileThread, base::FilePath(kArcFeaturesJsonFile)),
+ std::move(callback));
+}
+
+base::Optional<ArcFeatures> ArcFeaturesParser::ParseFeaturesJsonForTesting(
+ base::StringPiece input_json) {
+ return ParseFeaturesJson(input_json);
+}
+
+} // namespace arc
diff --git a/chromium/components/arc/arc_features_parser.h b/chromium/components/arc/arc_features_parser.h
new file mode 100644
index 00000000000..0b0219aa555
--- /dev/null
+++ b/chromium/components/arc/arc_features_parser.h
@@ -0,0 +1,93 @@
+// 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_ARC_ARC_FEATURES_PARSER_H_
+#define COMPONENTS_ARC_ARC_FEATURES_PARSER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/strings/string_piece.h"
+
+namespace arc {
+
+// This struct contains an ARC available feature map, unavailable feature set
+// and ARC build property map.
+struct ArcFeatures {
+ // Key is the feature name. Value is the feature version.
+ using FeatureVersionMapping = std::map<std::string, int>;
+
+ // Each item in the vector is the feature name.
+ using FeatureList = std::vector<std::string>;
+
+ // Key is the property key, such as "ro.build.version.sdk". Value is the
+ // corresponding property value.
+ using BuildPropsMapping = std::map<std::string, std::string>;
+
+ ArcFeatures();
+ ArcFeatures(ArcFeatures&& other);
+ ~ArcFeatures();
+
+ ArcFeatures& operator=(ArcFeatures&& other);
+
+ // This map contains all ARC system available features. For each feature, it
+ // has the name and version. Unavailable features have been filtered out from
+ // this map.
+ FeatureVersionMapping feature_map;
+
+ // This list contains all ARC unavailable feature names.
+ FeatureList unavailable_features;
+
+ // This map contains all ARC build properties.
+ BuildPropsMapping build_props;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ArcFeatures);
+};
+
+// Parses JSON files for Android system available features and build properties.
+//
+// A feature JSON file looks like this:
+// {
+// "features": [
+// {
+// "name": "android.hardware.location",
+// "version: 2
+// },
+// {
+// "name": "android.hardware.location.network",
+// "version": 0
+// }
+// ],
+// "unavailable_features": [
+// "android.hardware.usb.accessory",
+// "android.software.live_tv"
+// ],
+// "properties": {
+// "ro.product.cpu.abilist": "x86_64,x86,armeabi-v7a,armeabi",
+// "ro.build.version.sdk": "25"
+// }
+// }
+class ArcFeaturesParser {
+ public:
+ // Get ARC system available features.
+ static void GetArcFeatures(
+ base::OnceCallback<void(base::Optional<ArcFeatures>)> callback);
+
+ // Given an input feature JSON, return ARC features. This method is for
+ // testing only.
+ static base::Optional<ArcFeatures> ParseFeaturesJsonForTesting(
+ base::StringPiece input_json);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ArcFeaturesParser);
+};
+
+} // namespace arc
+
+#endif // COMPONENTS_ARC_ARC_FEATURES_PARSER_H_
diff --git a/chromium/components/arc/arc_features_parser_unittest.cc b/chromium/components/arc/arc_features_parser_unittest.cc
new file mode 100644
index 00000000000..5df03355a86
--- /dev/null
+++ b/chromium/components/arc/arc_features_parser_unittest.cc
@@ -0,0 +1,124 @@
+// 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/arc/arc_features_parser.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace arc {
+namespace {
+
+class ArcFeaturesParserTest : public testing::Test {
+ public:
+ ArcFeaturesParserTest() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ArcFeaturesParserTest);
+};
+
+constexpr const char kValidJson[] = R"json({"features": [
+ {
+ "name": "com.google.android.feature.GOOGLE_BUILD",
+ "version": 0
+ },
+ {
+ "name": "com.google.android.feature.GOOGLE_EXPERIENCE",
+ "version": 2
+ }
+ ],
+ "unavailable_features": [],
+ "properties": {
+ "ro.product.cpu.abilist": "x86_64,x86,armeabi-v7a,armeabi",
+ "ro.build.version.sdk": "25"
+ }})json";
+
+constexpr const char kValidJsonWithUnavailableFeature[] =
+ R"json({"features": [
+ {
+ "name": "android.software.home_screen",
+ "version": 0
+ },
+ {
+ "name": "com.google.android.feature.GOOGLE_EXPERIENCE",
+ "version": 0
+ }
+ ],
+ "unavailable_features": ["android.software.location"],
+ "properties": {}})json";
+
+constexpr const char kValidJsonFeatureEmptyName[] =
+ R"json({"features": [
+ {
+ "name": "android.hardware.faketouch",
+ "version": 0
+ },
+ {
+ "name": "android.hardware.location",
+ "version": 0
+ },
+ {
+ "name": "",
+ "version": 0
+ }
+ ],
+ "unavailable_features": ["android.software.home_screen", ""],
+ "properties": {}})json";
+
+constexpr const char kValidJsonWithMissingFields[] =
+ R"json({"invalid_root": [
+ {
+ "name": "android.hardware.location"
+ },
+ {
+ "name": "android.hardware.location.network"
+ }
+ ],
+ "invalid_root_second": [],
+ "invalid_root_third": {}})json";
+
+TEST_F(ArcFeaturesParserTest, ParseEmptyJson) {
+ base::Optional<ArcFeatures> arc_features =
+ ArcFeaturesParser::ParseFeaturesJsonForTesting(base::StringPiece());
+ EXPECT_EQ(arc_features, base::nullopt);
+}
+
+TEST_F(ArcFeaturesParserTest, ParseInvalidJson) {
+ base::Optional<ArcFeatures> arc_features =
+ ArcFeaturesParser::ParseFeaturesJsonForTesting(
+ kValidJsonWithMissingFields);
+ EXPECT_EQ(arc_features, base::nullopt);
+}
+
+TEST_F(ArcFeaturesParserTest, ParseValidJson) {
+ base::Optional<ArcFeatures> arc_features =
+ ArcFeaturesParser::ParseFeaturesJsonForTesting(kValidJson);
+ auto feature_map = arc_features->feature_map;
+ auto unavailable_features = arc_features->unavailable_features;
+ auto build_props = arc_features->build_props;
+ EXPECT_EQ(feature_map.size(), 2u);
+ EXPECT_EQ(unavailable_features.size(), 0u);
+ EXPECT_EQ(build_props.size(), 2u);
+}
+
+TEST_F(ArcFeaturesParserTest, ParseValidJsonWithUnavailableFeature) {
+ base::Optional<ArcFeatures> arc_features =
+ ArcFeaturesParser::ParseFeaturesJsonForTesting(
+ kValidJsonWithUnavailableFeature);
+ auto feature_map = arc_features->feature_map;
+ auto unavailable_features = arc_features->unavailable_features;
+ auto build_props = arc_features->build_props;
+ EXPECT_EQ(feature_map.size(), 2u);
+ EXPECT_EQ(unavailable_features.size(), 1u);
+ EXPECT_EQ(build_props.size(), 0u);
+}
+
+TEST_F(ArcFeaturesParserTest, ParseValidJsonWithEmptyFeatureName) {
+ base::Optional<ArcFeatures> arc_features =
+ ArcFeaturesParser::ParseFeaturesJsonForTesting(
+ kValidJsonFeatureEmptyName);
+ EXPECT_EQ(arc_features, base::nullopt);
+}
+
+} // namespace
+} // namespace arc
diff --git a/chromium/components/arc/arc_prefs.cc b/chromium/components/arc/arc_prefs.cc
index 7527b09f3b2..71e83b18b3c 100644
--- a/chromium/components/arc/arc_prefs.cc
+++ b/chromium/components/arc/arc_prefs.cc
@@ -54,6 +54,11 @@ const char kArcLocationServiceEnabled[] = "arc.location_service.enabled";
const char kArcPackages[] = "arc.packages";
// A preference that indicates that Play Auto Install flow was already started.
const char kArcPaiStarted[] = "arc.pai.started";
+// A preference that indicates that Play Fast App Reinstall flow was already
+// started.
+const char kArcFastAppReinstallStarted[] = "arc.fast.app.reinstall.started";
+// A preference to keep list of Play Fast App Reinstall packages.
+const char kArcFastAppReinstallPackages[] = "arc.fast.app.reinstall.packages";
// A preference that holds the list of apps that the admin requested to be
// push-installed.
const char kArcPushInstallAppsRequested[] = "arc.push_install.requested";
@@ -80,6 +85,11 @@ const char kArcVoiceInteractionValuePropAccepted[] =
const char kEcryptfsMigrationStrategy[] = "ecryptfs_migration_strategy";
// A preference that indicates whether the SMS Connect feature is enabled.
const char kSmsConnectEnabled[] = "multidevice.sms_connect_enabled";
+
+// A preference that indicates the user has accepted voice interaction activity
+// control settings.
+const char kVoiceInteractionActivityControlAccepted[] =
+ "settings.voice_interaction.activity_control.accepted";
// A preference that indicates the user has enabled voice interaction services.
const char kVoiceInteractionEnabled[] = "settings.voice_interaction.enabled";
// A preference that indicates the user has allowed voice interaction services
@@ -87,6 +97,10 @@ const char kVoiceInteractionEnabled[] = "settings.voice_interaction.enabled";
// screen).
const char kVoiceInteractionContextEnabled[] =
"settings.voice_interaction.context.enabled";
+// A preference that indicates the user has allowed voice interaction services
+// to use hotword listening.
+const char kVoiceInteractionHotwordEnabled[] =
+ "settings.voice_interaction.hotword.enabled";
void RegisterProfilePrefs(PrefRegistrySimple* registry) {
// TODO(dspaid): Implement a mechanism to allow this to sync on first boot
@@ -111,10 +125,14 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) {
static_cast<int>(ArcSupervisionTransition::NO_TRANSITION));
// Sorted in lexicographical order.
+ registry->RegisterBooleanPref(kVoiceInteractionActivityControlAccepted,
+ false);
registry->RegisterBooleanPref(kArcDataRemoveRequested, false);
registry->RegisterBooleanPref(kArcEnabled, false);
registry->RegisterBooleanPref(kArcInitialSettingsPending, false);
registry->RegisterBooleanPref(kArcPaiStarted, false);
+ registry->RegisterBooleanPref(kArcFastAppReinstallStarted, false);
+ registry->RegisterListPref(kArcFastAppReinstallPackages);
registry->RegisterBooleanPref(kArcPolicyComplianceReported, false);
registry->RegisterBooleanPref(kArcSignedIn, false);
registry->RegisterBooleanPref(kArcSkippedReportingNotice, false);
@@ -124,6 +142,7 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(kSmsConnectEnabled, true);
registry->RegisterBooleanPref(kVoiceInteractionContextEnabled, false);
registry->RegisterBooleanPref(kVoiceInteractionEnabled, false);
+ registry->RegisterBooleanPref(kVoiceInteractionHotwordEnabled, false);
}
} // namespace prefs
diff --git a/chromium/components/arc/arc_prefs.h b/chromium/components/arc/arc_prefs.h
index 20adee6b35f..23c8b321879 100644
--- a/chromium/components/arc/arc_prefs.h
+++ b/chromium/components/arc/arc_prefs.h
@@ -18,6 +18,8 @@ ARC_EXPORT extern const char kArcApps[];
ARC_EXPORT extern const char kArcBackupRestoreEnabled[];
ARC_EXPORT extern const char kArcDataRemoveRequested[];
ARC_EXPORT extern const char kArcEnabled[];
+ARC_EXPORT extern const char kArcFastAppReinstallPackages[];
+ARC_EXPORT extern const char kArcFastAppReinstallStarted[];
ARC_EXPORT extern const char kArcInitialSettingsPending[];
ARC_EXPORT extern const char kArcPolicyComplianceReported[];
ARC_EXPORT extern const char kArcTermsAccepted[];
@@ -35,8 +37,12 @@ ARC_EXPORT extern const char kArcCompatibleFilesystemChosen[];
ARC_EXPORT extern const char kArcVoiceInteractionValuePropAccepted[];
ARC_EXPORT extern const char kEcryptfsMigrationStrategy[];
ARC_EXPORT extern const char kSmsConnectEnabled[];
+
+// TODO(b/110211045): Move Assistant related prefs to ash.
+ARC_EXPORT extern const char kVoiceInteractionActivityControlAccepted[];
ARC_EXPORT extern const char kVoiceInteractionEnabled[];
ARC_EXPORT extern const char kVoiceInteractionContextEnabled[];
+ARC_EXPORT extern const char kVoiceInteractionHotwordEnabled[];
void RegisterProfilePrefs(PrefRegistrySimple* registry);
diff --git a/chromium/components/arc/arc_session.cc b/chromium/components/arc/arc_session.cc
index d5c887a1136..83278f61374 100644
--- a/chromium/components/arc/arc_session.cc
+++ b/chromium/components/arc/arc_session.cc
@@ -8,6 +8,13 @@
namespace arc {
+ArcSession::UpgradeParams::UpgradeParams() = default;
+ArcSession::UpgradeParams::UpgradeParams(ArcSession::UpgradeParams&& other) =
+ default;
+ArcSession::UpgradeParams& ArcSession::UpgradeParams::operator=(
+ ArcSession::UpgradeParams&& other) = default;
+ArcSession::UpgradeParams::~UpgradeParams() = default;
+
ArcSession::ArcSession() = default;
ArcSession::~ArcSession() = default;
diff --git a/chromium/components/arc/arc_session.h b/chromium/components/arc/arc_session.h
index d0d680e0cf0..247250d67b5 100644
--- a/chromium/components/arc/arc_session.h
+++ b/chromium/components/arc/arc_session.h
@@ -6,10 +6,18 @@
#define COMPONENTS_ARC_ARC_SESSION_H_
#include <memory>
+#include <string>
+#include <vector>
+#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "components/arc/arc_stop_reason.h"
+#include "components/arc/arc_supervision_transition.h"
+
+namespace base {
+class FilePath;
+}
namespace arc {
@@ -38,6 +46,43 @@ class ArcSession {
virtual ~Observer() = default;
};
+ // Parameters to upgrade request.
+ struct UpgradeParams {
+ // Explicit ctor/dtor declaration is necessary for complex struct. See
+ // https://cs.chromium.org/chromium/src/tools/clang/plugins/FindBadConstructsConsumer.cpp
+ UpgradeParams();
+ ~UpgradeParams();
+ UpgradeParams(UpgradeParams&& other);
+ UpgradeParams& operator=(UpgradeParams&& other);
+
+ // Whether the account is a child.
+ bool is_child;
+
+ // The supervision transition state for this account. Indicates whether
+ // child account should become regular, regular account should become child
+ // or neither.
+ ArcSupervisionTransition supervision_transition =
+ ArcSupervisionTransition::NO_TRANSITION;
+
+ // Define language configuration set during Android container boot.
+ // |preferred_languages| may be empty.
+ std::string locale;
+ std::vector<std::string> preferred_languages;
+
+ // Whether ARC is being upgraded in a demo session.
+ bool is_demo_session = false;
+
+ // |demo_session_apps_path| is a file path to the image containing set of
+ // demo apps that should be pre-installed into the Android container for
+ // demo sessions. It might be empty, in which case no demo apps will be
+ // pre-installed.
+ // Should be empty if |is_demo_session| is not set.
+ base::FilePath demo_session_apps_path;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(UpgradeParams);
+ };
+
// Creates a default instance of ArcSession.
static std::unique_ptr<ArcSession> Create(
ArcBridgeService* arc_bridge_service);
@@ -46,10 +91,10 @@ class ArcSession {
// Sends D-Bus message to start a mini-container.
virtual void StartMiniInstance() = 0;
- // Sends a D-Bus message to upgrade to a full instance if
- // possible. This might be done asynchronously; the message might only be sent
- // after other operations have completed.
- virtual void RequestUpgrade() = 0;
+ // Sends a D-Bus message to upgrade to a full instance if possible. This might
+ // be done asynchronously; the message might only be sent after other
+ // operations have completed.
+ virtual void RequestUpgrade(UpgradeParams params) = 0;
// Requests to stop the currently-running instance regardless of its mode.
// The completion is notified via OnSessionStopped() of the Observer.
diff --git a/chromium/components/arc/arc_session_impl.cc b/chromium/components/arc/arc_session_impl.cc
index e9b77cfd837..b185df06844 100644
--- a/chromium/components/arc/arc_session_impl.cc
+++ b/chromium/components/arc/arc_session_impl.cc
@@ -14,6 +14,8 @@
#include "base/command_line.h"
#include "base/location.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
#include "base/task_scheduler/post_task.h"
#include "base/task_scheduler/task_traits.h"
#include "chromeos/chromeos_switches.h"
@@ -24,14 +26,12 @@
#include "components/arc/arc_bridge_host_impl.h"
#include "components/arc/arc_features.h"
#include "components/user_manager/user_manager.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/named_platform_handle.h"
-#include "mojo/edk/embedder/named_platform_handle_utils.h"
-#include "mojo/edk/embedder/outgoing_broker_client_invitation.h"
-#include "mojo/edk/embedder/platform_channel_pair.h"
-#include "mojo/edk/embedder/platform_channel_utils_posix.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/platform/named_platform_channel.h"
+#include "mojo/public/cpp/platform/platform_channel.h"
+#include "mojo/public/cpp/platform/platform_handle.h"
+#include "mojo/public/cpp/platform/socket_utils_posix.h"
+#include "mojo/public/cpp/system/invitation.h"
namespace arc {
@@ -48,6 +48,12 @@ chromeos::SessionManagerClient* GetSessionManagerClient() {
return chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
}
+std::string GenerateRandomToken() {
+ char random_bytes[16];
+ base::RandBytes(random_bytes, 16);
+ return base::HexEncode(random_bytes, 16);
+}
+
// Creates a pipe. Returns true on success, otherwise false.
// On success, |read_fd| will be set to the fd of the read side, and
// |write_fd| will be set to the one of write side.
@@ -99,6 +105,27 @@ ArcStopReason GetArcStopReason(bool low_disk_space, bool stop_requested) {
return ArcStopReason::GENERIC_BOOT_FAILURE;
}
+// Converts ArcSupervisionTransition into
+// login_manager::UpgradeArcContainerRequest_SupervisionTransition.
+login_manager::UpgradeArcContainerRequest_SupervisionTransition
+ToLoginManagerSupervisionTransition(ArcSupervisionTransition transition) {
+ switch (transition) {
+ case ArcSupervisionTransition::NO_TRANSITION:
+ return login_manager::
+ UpgradeArcContainerRequest_SupervisionTransition_NONE;
+ case ArcSupervisionTransition::CHILD_TO_REGULAR:
+ return login_manager::
+ UpgradeArcContainerRequest_SupervisionTransition_CHILD_TO_REGULAR;
+ case ArcSupervisionTransition::REGULAR_TO_CHILD:
+ return login_manager::
+ UpgradeArcContainerRequest_SupervisionTransition_REGULAR_TO_CHILD;
+ default:
+ NOTREACHED() << "Invalid transition " << transition;
+ return login_manager::
+ UpgradeArcContainerRequest_SupervisionTransition_NONE;
+ }
+}
+
// Real Delegate implementation to connect Mojo.
class ArcSessionDelegateImpl : public ArcSessionImpl::Delegate {
public:
@@ -110,11 +137,11 @@ class ArcSessionDelegateImpl : public ArcSessionImpl::Delegate {
ConnectMojoCallback callback) override;
private:
- // Synchronously accepts a connection on |socket_fd| and then processes the
- // connected socket's file descriptor. This is designed to run on a
+ // Synchronously accepts a connection on |server_endpoint| and then processes
+ // the connected socket's file descriptor. This is designed to run on a
// blocking thread.
static mojo::ScopedMessagePipeHandle ConnectMojoInternal(
- mojo::edk::ScopedInternalPlatformHandle socket_fd,
+ base::ScopedFD socket_fd,
base::ScopedFD cancel_fd);
// Called when Mojo connection is established or canceled.
@@ -150,14 +177,10 @@ base::ScopedFD ArcSessionDelegateImpl::ConnectMojo(
// For production, |socket_fd| passed from session_manager is either a valid
// socket or a valid file descriptor (/dev/null). For testing, |socket_fd|
// might be invalid.
- mojo::edk::InternalPlatformHandle raw_handle(socket_fd.release());
- raw_handle.needs_connection = true;
-
- mojo::edk::ScopedInternalPlatformHandle mojo_socket_fd(raw_handle);
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&ArcSessionDelegateImpl::ConnectMojoInternal,
- std::move(mojo_socket_fd), std::move(cancel_fd)),
+ std::move(socket_fd), std::move(cancel_fd)),
base::BindOnce(&ArcSessionDelegateImpl::OnMojoConnected,
weak_factory_.GetWeakPtr(), std::move(callback)));
return return_fd;
@@ -165,35 +188,32 @@ base::ScopedFD ArcSessionDelegateImpl::ConnectMojo(
// static
mojo::ScopedMessagePipeHandle ArcSessionDelegateImpl::ConnectMojoInternal(
- mojo::edk::ScopedInternalPlatformHandle socket_fd,
+ base::ScopedFD socket_fd,
base::ScopedFD cancel_fd) {
- if (!WaitForSocketReadable(socket_fd.get().handle, cancel_fd.get())) {
+ if (!WaitForSocketReadable(socket_fd.get(), cancel_fd.get())) {
VLOG(1) << "Mojo connection was cancelled.";
return mojo::ScopedMessagePipeHandle();
}
- mojo::edk::ScopedInternalPlatformHandle scoped_fd;
- if (!mojo::edk::ServerAcceptConnection(socket_fd, &scoped_fd,
- /* check_peer_user = */ false) ||
- !scoped_fd.is_valid()) {
+ base::ScopedFD connection_fd;
+ if (!mojo::AcceptSocketConnection(socket_fd.get(), &connection_fd,
+ /* check_peer_user = */ false) ||
+ !connection_fd.is_valid()) {
return mojo::ScopedMessagePipeHandle();
}
- // Hardcode pid 0 since it is unused in mojo.
- const base::ProcessHandle kUnusedChildProcessHandle = 0;
- mojo::edk::PlatformChannelPair channel_pair;
- mojo::edk::OutgoingBrokerClientInvitation invitation;
-
- std::string token = mojo::edk::GenerateRandomToken();
+ mojo::PlatformChannel channel;
+ mojo::OutgoingInvitation invitation;
+ // Generate an arbitrary 32-byte string. ARC uses this length as a protocol
+ // version identifier.
+ std::string token = GenerateRandomToken();
mojo::ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe(token);
+ mojo::OutgoingInvitation::Send(std::move(invitation),
+ base::kNullProcessHandle,
+ channel.TakeLocalEndpoint());
- invitation.Send(
- kUnusedChildProcessHandle,
- mojo::edk::ConnectionParams(mojo::edk::TransportProtocol::kLegacy,
- channel_pair.PassServerHandle()));
-
- std::vector<mojo::edk::ScopedInternalPlatformHandle> handles;
- handles.emplace_back(channel_pair.PassClientHandle());
+ std::vector<base::ScopedFD> fds;
+ fds.emplace_back(channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD());
// We need to send the length of the message as a single byte, so make sure it
// fits.
@@ -201,8 +221,8 @@ mojo::ScopedMessagePipeHandle ArcSessionDelegateImpl::ConnectMojoInternal(
uint8_t message_length = static_cast<uint8_t>(token.size());
struct iovec iov[] = {{&message_length, sizeof(message_length)},
{const_cast<char*>(token.c_str()), token.size()}};
- ssize_t result = mojo::edk::PlatformChannelSendmsgWithHandles(
- scoped_fd, iov, sizeof(iov) / sizeof(iov[0]), handles);
+ ssize_t result = mojo::SendmsgWithHandles(connection_fd.get(), iov,
+ sizeof(iov) / sizeof(iov[0]), fds);
if (result == -1) {
PLOG(ERROR) << "sendmsg";
return mojo::ScopedMessagePipeHandle();
@@ -272,10 +292,12 @@ void ArcSessionImpl::StartMiniInstance() {
weak_factory_.GetWeakPtr()));
}
-void ArcSessionImpl::RequestUpgrade() {
+void ArcSessionImpl::RequestUpgrade(UpgradeParams params) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(!params.locale.empty());
upgrade_requested_ = true;
+ upgrade_params_ = std::move(params);
switch (state_) {
case State::NOT_STARTED:
@@ -365,6 +387,20 @@ void ArcSessionImpl::DoUpgrade() {
<< packages_cache_mode_string << ".";
}
+ request.set_is_child(upgrade_params_.is_child);
+ request.set_supervision_transition(ToLoginManagerSupervisionTransition(
+ upgrade_params_.supervision_transition));
+ request.set_locale(upgrade_params_.locale);
+ for (const std::string& language : upgrade_params_.preferred_languages)
+ request.add_preferred_languages(language);
+
+ request.set_is_demo_session(upgrade_params_.is_demo_session);
+ if (!upgrade_params_.demo_session_apps_path.empty()) {
+ DCHECK(upgrade_params_.is_demo_session);
+ request.set_demo_session_apps_path(
+ upgrade_params_.demo_session_apps_path.value());
+ }
+
chromeos::SessionManagerClient* client = GetSessionManagerClient();
client->UpgradeArcContainer(
request,
diff --git a/chromium/components/arc/arc_session_impl.h b/chromium/components/arc/arc_session_impl.h
index f1fb9612577..fbaefe930b2 100644
--- a/chromium/components/arc/arc_session_impl.h
+++ b/chromium/components/arc/arc_session_impl.h
@@ -8,8 +8,10 @@
#include <memory>
#include <ostream>
#include <string>
+#include <vector>
#include "base/callback.h"
+#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
@@ -140,7 +142,7 @@ class ArcSessionImpl : public ArcSession,
// ArcSession overrides:
void StartMiniInstance() override;
- void RequestUpgrade() override;
+ void RequestUpgrade(UpgradeParams params) override;
void Stop() override;
bool IsStopRequested() override;
void OnShutdown() override;
@@ -200,6 +202,9 @@ class ArcSessionImpl : public ArcSession,
// to notify cancelling of the procedure.
base::ScopedFD accept_cancel_pipe_;
+ // Parameters to upgrade request.
+ UpgradeParams upgrade_params_;
+
// Mojo endpoint.
std::unique_ptr<mojom::ArcBridgeHost> arc_bridge_host_;
diff --git a/chromium/components/arc/arc_session_impl_unittest.cc b/chromium/components/arc/arc_session_impl_unittest.cc
index 15eb937bb34..9bd44c7e94d 100644
--- a/chromium/components/arc/arc_session_impl_unittest.cc
+++ b/chromium/components/arc/arc_session_impl_unittest.cc
@@ -30,6 +30,13 @@ namespace {
constexpr char kFakeGmail[] = "user@gmail.com";
constexpr char kFakeGmailGaiaId[] = "1234567890";
+constexpr char kDefaultLocale[] = "en-US";
+
+ArcSession::UpgradeParams DefaultUpgradeParams() {
+ ArcSession::UpgradeParams params;
+ params.locale = kDefaultLocale;
+ return params;
+}
class FakeDelegate : public ArcSessionImpl::Delegate {
public:
@@ -236,7 +243,7 @@ TEST_F(ArcSessionImplTest, Upgrade_LowDisk) {
TestArcSessionObserver observer(arc_session.get(), &run_loop);
ASSERT_NO_FATAL_FAILURE(SetupMiniContainer(arc_session.get(), &observer));
- arc_session->RequestUpgrade();
+ arc_session->RequestUpgrade(DefaultUpgradeParams());
run_loop.Run();
EXPECT_EQ(ArcSessionImpl::State::STOPPED, arc_session->GetStateForTesting());
@@ -255,7 +262,7 @@ TEST_F(ArcSessionImplTest, Upgrade_Success) {
ASSERT_NO_FATAL_FAILURE(SetupMiniContainer(arc_session.get(), &observer));
// Then, upgrade to a full instance.
- arc_session->RequestUpgrade();
+ arc_session->RequestUpgrade(DefaultUpgradeParams());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(ArcSessionImpl::State::RUNNING_FULL_INSTANCE,
@@ -275,7 +282,7 @@ TEST_F(ArcSessionImplTest, Upgrade_DBusFail) {
EmulateDBusFailure();
// Then upgrade, which should fail.
- arc_session->RequestUpgrade();
+ arc_session->RequestUpgrade(DefaultUpgradeParams());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(ArcSessionImpl::State::STOPPED, arc_session->GetStateForTesting());
@@ -300,7 +307,7 @@ TEST_F(ArcSessionImplTest, Upgrade_MojoConnectionFail) {
ASSERT_NO_FATAL_FAILURE(SetupMiniContainer(arc_session.get(), &observer));
// Upgrade should fail, due to Mojo connection fail set above.
- arc_session->RequestUpgrade();
+ arc_session->RequestUpgrade(DefaultUpgradeParams());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(ArcSessionImpl::State::STOPPED, arc_session->GetStateForTesting());
@@ -321,7 +328,7 @@ TEST_F(ArcSessionImplTest, Upgrade_StartingMiniInstance) {
arc_session->GetStateForTesting());
// Before moving forward to RUNNING_MINI_INSTANCE, start upgrading it.
- arc_session->RequestUpgrade();
+ arc_session->RequestUpgrade(DefaultUpgradeParams());
// The state should not immediately switch to STARTING_FULL_INSTANCE, yet.
EXPECT_EQ(ArcSessionImpl::State::STARTING_MINI_INSTANCE,
@@ -382,7 +389,7 @@ TEST_F(ArcSessionImplTest, Stop_StartingFullInstanceForUpgrade) {
ASSERT_NO_FATAL_FAILURE(SetupMiniContainer(arc_session.get(), &observer));
// Then upgrade.
- arc_session->RequestUpgrade();
+ arc_session->RequestUpgrade(DefaultUpgradeParams());
ASSERT_EQ(ArcSessionImpl::State::STARTING_FULL_INSTANCE,
arc_session->GetStateForTesting());
@@ -410,7 +417,7 @@ TEST_F(ArcSessionImplTest, Stop_ConnectingMojoForUpgrade) {
ASSERT_NO_FATAL_FAILURE(SetupMiniContainer(arc_session.get(), &observer));
// Then upgrade. This should suspend at Mojo connection.
- arc_session->RequestUpgrade();
+ arc_session->RequestUpgrade(DefaultUpgradeParams());
base::RunLoop().RunUntilIdle();
ASSERT_EQ(ArcSessionImpl::State::CONNECTING_MOJO,
arc_session->GetStateForTesting());
@@ -436,7 +443,7 @@ TEST_F(ArcSessionImplTest, Stop_RunningFullInstanceForUpgrade) {
ASSERT_NO_FATAL_FAILURE(SetupMiniContainer(arc_session.get(), &observer));
// And upgrade successfully.
- arc_session->RequestUpgrade();
+ arc_session->RequestUpgrade(DefaultUpgradeParams());
base::RunLoop().RunUntilIdle();
ASSERT_EQ(ArcSessionImpl::State::RUNNING_FULL_INSTANCE,
arc_session->GetStateForTesting());
@@ -463,7 +470,7 @@ TEST_F(ArcSessionImplTest,
arc_session->GetStateForTesting());
// Request to upgrade during starting mini container.
- arc_session->RequestUpgrade();
+ arc_session->RequestUpgrade(DefaultUpgradeParams());
// Then, the state should stay at STARTING_MINI_INSTANCE.
ASSERT_EQ(ArcSessionImpl::State::STARTING_MINI_INSTANCE,
arc_session->GetStateForTesting());
@@ -512,7 +519,7 @@ TEST_F(ArcSessionImplTest, ArcStopInstance) {
auto arc_session = CreateArcSession();
TestArcSessionObserver observer(arc_session.get());
arc_session->StartMiniInstance();
- arc_session->RequestUpgrade();
+ arc_session->RequestUpgrade(DefaultUpgradeParams());
base::RunLoop().RunUntilIdle();
ASSERT_EQ(ArcSessionImpl::State::RUNNING_FULL_INSTANCE,
arc_session->GetStateForTesting());
@@ -536,7 +543,7 @@ TEST_F(ArcSessionImplTest, ArcStopInstance) {
TEST_F(ArcSessionImplTest, ArcStopInstance_WrongContainerInstanceId) {
auto arc_session = CreateArcSession();
arc_session->StartMiniInstance();
- arc_session->RequestUpgrade();
+ arc_session->RequestUpgrade(DefaultUpgradeParams());
base::RunLoop().RunUntilIdle();
ASSERT_EQ(ArcSessionImpl::State::RUNNING_FULL_INSTANCE,
arc_session->GetStateForTesting());
@@ -592,7 +599,7 @@ TEST_P(ArcSessionImplPackagesCacheModeTest, PackagesCacheModes) {
arc_session->StartMiniInstance();
if (state.full_container)
- arc_session->RequestUpgrade();
+ arc_session->RequestUpgrade(DefaultUpgradeParams());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(state.expected_packages_cache_mode, GetSessionManagerClient()
->last_upgrade_arc_request()
@@ -603,5 +610,73 @@ INSTANTIATE_TEST_CASE_P(,
ArcSessionImplPackagesCacheModeTest,
::testing::ValuesIn(kPackagesCacheModeStates));
+TEST_F(ArcSessionImplTest, IsChild) {
+ auto arc_session = CreateArcSession();
+ arc_session->StartMiniInstance();
+
+ ArcSession::UpgradeParams params;
+ params.is_child = true;
+ params.locale = kDefaultLocale;
+ arc_session->RequestUpgrade(std::move(params));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(GetSessionManagerClient()->last_upgrade_arc_request().is_child());
+}
+
+TEST_F(ArcSessionImplTest, DemoSession) {
+ auto arc_session = CreateArcSession();
+ arc_session->StartMiniInstance();
+
+ const std::string demo_apps_path =
+ "/run/imageloader/demo_mode_resources/android_apps.squash";
+ ArcSession::UpgradeParams params;
+ params.is_demo_session = true;
+ params.demo_session_apps_path = base::FilePath(demo_apps_path);
+ params.locale = kDefaultLocale;
+ arc_session->RequestUpgrade(std::move(params));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(
+ GetSessionManagerClient()->last_upgrade_arc_request().is_demo_session());
+ EXPECT_EQ(demo_apps_path, GetSessionManagerClient()
+ ->last_upgrade_arc_request()
+ .demo_session_apps_path());
+}
+
+TEST_F(ArcSessionImplTest, DemoSessionWithoutOfflineDemoApps) {
+ auto arc_session = CreateArcSession();
+ arc_session->StartMiniInstance();
+
+ ArcSession::UpgradeParams params;
+ params.is_demo_session = true;
+ params.locale = kDefaultLocale;
+ arc_session->RequestUpgrade(std::move(params));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(
+ GetSessionManagerClient()->last_upgrade_arc_request().is_demo_session());
+ EXPECT_EQ(std::string(), GetSessionManagerClient()
+ ->last_upgrade_arc_request()
+ .demo_session_apps_path());
+}
+
+TEST_F(ArcSessionImplTest, SupervisionTransitionShouldGraduate) {
+ auto arc_session = CreateArcSession();
+ arc_session->StartMiniInstance();
+
+ ArcSession::UpgradeParams params;
+ params.supervision_transition = ArcSupervisionTransition::CHILD_TO_REGULAR;
+ params.locale = kDefaultLocale;
+ arc_session->RequestUpgrade(std::move(params));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(
+ login_manager::
+ UpgradeArcContainerRequest_SupervisionTransition_CHILD_TO_REGULAR,
+ GetSessionManagerClient()
+ ->last_upgrade_arc_request()
+ .supervision_transition());
+}
+
} // namespace
} // namespace arc
diff --git a/chromium/components/arc/arc_session_runner.cc b/chromium/components/arc/arc_session_runner.cc
index ca10d68d93d..94c5f3069da 100644
--- a/chromium/components/arc/arc_session_runner.cc
+++ b/chromium/components/arc/arc_session_runner.cc
@@ -137,6 +137,15 @@ void ArcSessionRunner::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
+void ArcSessionRunner::RequestStartMiniInstance() {
+ RequestStart(ArcInstanceMode::MINI_INSTANCE);
+}
+
+void ArcSessionRunner::RequestUpgrade(ArcSession::UpgradeParams params) {
+ upgrade_params_ = std::move(params);
+ RequestStart(ArcInstanceMode::FULL_INSTANCE);
+}
+
void ArcSessionRunner::RequestStart(ArcInstanceMode request_mode) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -232,8 +241,9 @@ void ArcSessionRunner::StartArcSession() {
if (!restart_after_crash_count_)
RecordInstanceCrashUma(ArcContainerLifetimeEvent::CONTAINER_STARTING);
}
- if (target_mode_ == ArcInstanceMode::FULL_INSTANCE)
- arc_session_->RequestUpgrade();
+ if (target_mode_ == ArcInstanceMode::FULL_INSTANCE) {
+ arc_session_->RequestUpgrade(std::move(upgrade_params_));
+ }
}
void ArcSessionRunner::RestartArcSession() {
diff --git a/chromium/components/arc/arc_session_runner.h b/chromium/components/arc/arc_session_runner.h
index ab9bbff8ce5..f7cdb805615 100644
--- a/chromium/components/arc/arc_session_runner.h
+++ b/chromium/components/arc/arc_session_runner.h
@@ -6,8 +6,10 @@
#define COMPONENTS_ARC_ARC_SESSION_RUNNER_H_
#include <memory>
+#include <vector>
#include "base/callback.h"
+#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
@@ -74,9 +76,12 @@ class ArcSessionRunner : public ArcSession::Observer {
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
- // Starts the ARC service, then it will connect the Mojo channel. When the
- // bridge becomes ready, registered Observer's OnSessionReady() is called.
- void RequestStart(ArcInstanceMode request_mode);
+ // Starts the mini ARC instance.
+ void RequestStartMiniInstance();
+
+ // Starts the full ARC instance, then it will connect the Mojo channel. When
+ // the bridge becomes ready, registered Observer's OnSessionReady() is called.
+ void RequestUpgrade(ArcSession::UpgradeParams params);
// Stops the ARC service.
void RequestStop();
@@ -101,6 +106,9 @@ class ArcSessionRunner : public ArcSession::Observer {
// Restarts an ARC instance.
void RestartArcSession();
+ // Starts an ARC instance in |request_mode|.
+ void RequestStart(ArcInstanceMode request_mode);
+
// ArcSession::Observer:
void OnSessionStopped(ArcStopReason reason,
bool was_running,
@@ -128,6 +136,9 @@ class ArcSessionRunner : public ArcSession::Observer {
// nullptr if the state is STOPPED, otherwise non-nullptr.
std::unique_ptr<ArcSession> arc_session_;
+ // Parameters to upgrade request.
+ ArcSession::UpgradeParams upgrade_params_;
+
// WeakPtrFactory to use callbacks.
base::WeakPtrFactory<ArcSessionRunner> weak_ptr_factory_;
diff --git a/chromium/components/arc/arc_session_runner_unittest.cc b/chromium/components/arc/arc_session_runner_unittest.cc
index 0967be81cb3..57ef9318de8 100644
--- a/chromium/components/arc/arc_session_runner_unittest.cc
+++ b/chromium/components/arc/arc_session_runner_unittest.cc
@@ -12,7 +12,7 @@
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_session_manager_client.h"
@@ -35,6 +35,12 @@ constexpr int kContainerCrashedEarly =
constexpr int kContainerCrashed =
static_cast<int>(ArcContainerLifetimeEvent::CONTAINER_CRASHED);
+ArcSession::UpgradeParams DefaultUpgradeParams() {
+ ArcSession::UpgradeParams params;
+ params.locale = "en-US";
+ return params;
+}
+
class DoNothingObserver : public ArcSessionRunner::Observer {
public:
void OnSessionStopped(ArcStopReason reason, bool restarting) override {
@@ -170,7 +176,7 @@ TEST_F(ArcSessionRunnerTest, Basic) {
EXPECT_FALSE(arc_session());
- arc_session_runner()->RequestStart(ArcInstanceMode::FULL_INSTANCE);
+ arc_session_runner()->RequestUpgrade(DefaultUpgradeParams());
ASSERT_TRUE(arc_session());
EXPECT_TRUE(arc_session()->is_running());
@@ -186,7 +192,7 @@ TEST_F(ArcSessionRunnerTest, StopMidStartup) {
base::Bind(&ArcSessionRunnerTest::CreateSuspendedArcSession));
EXPECT_FALSE(arc_session());
- arc_session_runner()->RequestStart(ArcInstanceMode::FULL_INSTANCE);
+ arc_session_runner()->RequestUpgrade(DefaultUpgradeParams());
ASSERT_TRUE(arc_session());
EXPECT_FALSE(arc_session()->is_running());
@@ -201,7 +207,7 @@ TEST_F(ArcSessionRunnerTest, StopMidStartup_MiniInstance) {
base::Bind(&ArcSessionRunnerTest::CreateSuspendedArcSession));
EXPECT_FALSE(arc_session());
- arc_session_runner()->RequestStart(ArcInstanceMode::MINI_INSTANCE);
+ arc_session_runner()->RequestStartMiniInstance();
ASSERT_TRUE(arc_session());
EXPECT_FALSE(arc_session()->is_running());
@@ -217,7 +223,7 @@ TEST_F(ArcSessionRunnerTest, BootFailure) {
ArcStopReason::GENERIC_BOOT_FAILURE));
EXPECT_FALSE(arc_session());
- arc_session_runner()->RequestStart(ArcInstanceMode::FULL_INSTANCE);
+ arc_session_runner()->RequestUpgrade(DefaultUpgradeParams());
EXPECT_EQ(ArcStopReason::GENERIC_BOOT_FAILURE, stop_reason());
EXPECT_FALSE(arc_session());
EXPECT_FALSE(restarting());
@@ -232,15 +238,15 @@ TEST_F(ArcSessionRunnerTest, BootFailure_MiniInstance) {
// If starting the mini instance fails, arc_session_runner()'s state goes back
// to STOPPED, but its observers won't be notified.
- arc_session_runner()->RequestStart(ArcInstanceMode::MINI_INSTANCE);
+ arc_session_runner()->RequestStartMiniInstance();
arc_session()->EmulateMiniContainerStart();
EXPECT_FALSE(arc_session());
EXPECT_FALSE(stopped_called());
- // Also make sure that RequestStart() works just fine after the boot
+ // Also make sure that RequestUpgrade() works just fine after the boot
// failure.
ResetArcSessionFactory(base::Bind(FakeArcSession::Create));
- arc_session_runner()->RequestStart(ArcInstanceMode::FULL_INSTANCE);
+ arc_session_runner()->RequestUpgrade(DefaultUpgradeParams());
ASSERT_TRUE(arc_session());
EXPECT_TRUE(arc_session()->is_running());
}
@@ -255,22 +261,21 @@ TEST_F(ArcSessionRunnerTest, Crash_MiniInstance) {
// If starting the mini instance fails, arc_session_runner()'s state goes back
// to STOPPED, but its observers won't be notified.
- arc_session_runner()->RequestStart(ArcInstanceMode::MINI_INSTANCE);
+ arc_session_runner()->RequestStartMiniInstance();
arc_session()->EmulateMiniContainerStart();
EXPECT_FALSE(arc_session());
EXPECT_FALSE(stopped_called());
}
-// Tests that RequestStart(FULL_INSTANCE) works after calling
-// RequestStart(MINI_INSTANCE).
+// Tests that RequestUpgrade works after calling RequestStart.
TEST_F(ArcSessionRunnerTest, Upgrade) {
EXPECT_FALSE(arc_session());
- arc_session_runner()->RequestStart(ArcInstanceMode::MINI_INSTANCE);
+ arc_session_runner()->RequestStartMiniInstance();
ASSERT_TRUE(arc_session());
EXPECT_FALSE(arc_session()->is_running());
- arc_session_runner()->RequestStart(ArcInstanceMode::FULL_INSTANCE);
+ arc_session_runner()->RequestUpgrade(DefaultUpgradeParams());
ASSERT_TRUE(arc_session());
EXPECT_TRUE(arc_session()->is_running());
}
@@ -280,7 +285,7 @@ TEST_F(ArcSessionRunnerTest, Restart) {
arc_session_runner()->SetRestartDelayForTesting(base::TimeDelta());
EXPECT_FALSE(arc_session());
- arc_session_runner()->RequestStart(ArcInstanceMode::FULL_INSTANCE);
+ arc_session_runner()->RequestUpgrade(DefaultUpgradeParams());
ASSERT_TRUE(arc_session());
EXPECT_TRUE(arc_session()->is_running());
@@ -302,7 +307,7 @@ TEST_F(ArcSessionRunnerTest, GracefulStop) {
arc_session_runner()->SetRestartDelayForTesting(base::TimeDelta());
EXPECT_FALSE(arc_session());
- arc_session_runner()->RequestStart(ArcInstanceMode::FULL_INSTANCE);
+ arc_session_runner()->RequestUpgrade(DefaultUpgradeParams());
ASSERT_TRUE(arc_session());
EXPECT_TRUE(arc_session()->is_running());
@@ -318,7 +323,7 @@ TEST_F(ArcSessionRunnerTest, Shutdown) {
arc_session_runner()->SetRestartDelayForTesting(base::TimeDelta());
EXPECT_FALSE(arc_session());
- arc_session_runner()->RequestStart(ArcInstanceMode::FULL_INSTANCE);
+ arc_session_runner()->RequestUpgrade(DefaultUpgradeParams());
ASSERT_TRUE(arc_session());
EXPECT_TRUE(arc_session()->is_running());
@@ -351,12 +356,12 @@ TEST_F(ArcSessionRunnerTest, RemoveUnknownObserver) {
TEST_F(ArcSessionRunnerTest, UmaRecording_StartUpgradeShutdown) {
base::HistogramTester tester;
- arc_session_runner()->RequestStart(ArcInstanceMode::MINI_INSTANCE);
+ arc_session_runner()->RequestStartMiniInstance();
tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting,
1 /* count of the sample */);
// Boot continue should not increase the count.
- arc_session_runner()->RequestStart(ArcInstanceMode::FULL_INSTANCE);
+ arc_session_runner()->RequestUpgrade(DefaultUpgradeParams());
tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting,
1);
@@ -370,7 +375,7 @@ TEST_F(ArcSessionRunnerTest, UmaRecording_StartUpgradeShutdown) {
TEST_F(ArcSessionRunnerTest, UmaRecording_StartShutdown) {
base::HistogramTester tester;
- arc_session_runner()->RequestStart(ArcInstanceMode::FULL_INSTANCE);
+ arc_session_runner()->RequestUpgrade(DefaultUpgradeParams());
tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting,
1);
// "0" should be recorded as a restart count on shutdown.
@@ -386,10 +391,10 @@ TEST_F(ArcSessionRunnerTest, UmaRecording_CrashTwice) {
arc_session_runner()->SetRestartDelayForTesting(base::TimeDelta());
EXPECT_FALSE(arc_session());
- arc_session_runner()->RequestStart(ArcInstanceMode::MINI_INSTANCE);
+ arc_session_runner()->RequestStartMiniInstance();
tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting,
1);
- arc_session_runner()->RequestStart(ArcInstanceMode::FULL_INSTANCE);
+ arc_session_runner()->RequestUpgrade(DefaultUpgradeParams());
// Stop the instance with CRASH.
arc_session()->StopWithReason(ArcStopReason::CRASH);
@@ -413,7 +418,7 @@ TEST_F(ArcSessionRunnerTest, UmaRecording_CrashTwice) {
TEST_F(ArcSessionRunnerTest, UmaRecording_CrashMini) {
base::HistogramTester tester;
- arc_session_runner()->RequestStart(ArcInstanceMode::MINI_INSTANCE);
+ arc_session_runner()->RequestStartMiniInstance();
tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting,
1);
@@ -432,7 +437,7 @@ TEST_F(ArcSessionRunnerTest, UmaRecording_CrashMini) {
TEST_F(ArcSessionRunnerTest, UmaRecording_BootFail) {
base::HistogramTester tester;
- arc_session_runner()->RequestStart(ArcInstanceMode::MINI_INSTANCE);
+ arc_session_runner()->RequestStartMiniInstance();
tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting,
1);
@@ -450,7 +455,7 @@ TEST_F(ArcSessionRunnerTest, UmaRecording_BootFail) {
TEST_F(ArcSessionRunnerTest, UmaRecording_LowDisk) {
base::HistogramTester tester;
- arc_session_runner()->RequestStart(ArcInstanceMode::FULL_INSTANCE);
+ arc_session_runner()->RequestUpgrade(DefaultUpgradeParams());
tester.ExpectUniqueSample("Arc.ContainerLifetimeEvent", kContainerStarting,
1);
diff --git a/chromium/components/arc/arc_util.cc b/chromium/components/arc/arc_util.cc
index 880db840e9b..6acaeb3e8f1 100644
--- a/chromium/components/arc/arc_util.cc
+++ b/chromium/components/arc/arc_util.cc
@@ -83,7 +83,7 @@ bool IsWebstoreSearchEnabled() {
}
bool IsPlayStoreAvailable() {
- if (IsRobotAccountMode())
+ if (IsRobotOrOfflineDemoAccountMode())
return false;
const auto* command_line = base::CommandLine::ForCurrentProcess();
if (!command_line->HasSwitch(chromeos::switches::kArcStartMode))
@@ -103,6 +103,11 @@ bool ShouldArcAlwaysStart() {
return value == kAlwaysStartWithNoPlayStore || value == kAlwaysStart;
}
+bool ShouldShowOptInForTesting() {
+ return base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kArcForceShowOptInUi);
+}
+
void SetArcAlwaysStartForTesting(bool play_store_available) {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
chromeos::switches::kArcStartMode,
@@ -138,7 +143,7 @@ bool IsArcKioskMode() {
user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp();
}
-bool IsRobotAccountMode() {
+bool IsRobotOrOfflineDemoAccountMode() {
return user_manager::UserManager::IsInitialized() &&
(user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp() ||
user_manager::UserManager::Get()->IsLoggedInAsPublicAccount());
@@ -180,7 +185,7 @@ bool IsArcOptInVerificationDisabled() {
chromeos::switches::kDisableArcOptInVerification);
}
-bool IsArcAppWindow(aura::Window* window) {
+bool IsArcAppWindow(const aura::Window* window) {
if (!window)
return false;
return window->GetProperty(aura::client::kAppType) ==
diff --git a/chromium/components/arc/arc_util.h b/chromium/components/arc/arc_util.h
index c706565aa15..3a6c323ebfd 100644
--- a/chromium/components/arc/arc_util.h
+++ b/chromium/components/arc/arc_util.h
@@ -46,6 +46,9 @@ bool IsPlayStoreAvailable();
// mode.
bool ShouldArcAlwaysStart();
+// Returns true if ARC OptIn ui needs to be shown for testing.
+bool ShouldShowOptInForTesting();
+
// Enables to always start ARC for testing, by appending the command line flag.
// If |bool play_store_available| is not set then flag that disables ARC Play
// Store UI is added.
@@ -75,12 +78,15 @@ void SetArcAvailableCommandLineForTesting(base::CommandLine* command_line);
// should also return true in that case.
bool IsArcKioskMode();
-// Returns true if current user is a robot account user.
-// These are Public Session and ARC Kiosk users.
+// Returns true if current user is a robot account user, or offline demo mode
+// user.
+// These are Public Session and ARC Kiosk users. Note that demo mode, including
+// offline demo mode, is implemented as a Public Session - offline demo mode
+// is setup offline and it isn't associated with a working robot account.
// As it can return true only when user is already initialized, it implies
// that ARC availability was checked before.
-// The check is basically IsArcKioskMode() | IsPublicSessionMode().
-bool IsRobotAccountMode();
+// The check is basically IsArcKioskMode() | IsLoggedInAsPublicSession().
+bool IsRobotOrOfflineDemoAccountMode();
// Returns true if ARC is allowed for the given user. Note this should not be
// used as a signal of whether ARC is allowed alone because it only considers
@@ -95,7 +101,7 @@ bool IsArcOptInVerificationDisabled();
// Returns true if the |window|'s aura::client::kAppType is ARC_APP. When
// |window| is nullptr, returns false.
-bool IsArcAppWindow(aura::Window* window);
+bool IsArcAppWindow(const aura::Window* window);
// Returns true if data clean up is requested for each ARC start.
bool IsArcDataCleanupOnStartRequested();
diff --git a/chromium/components/arc/audio/DEPS b/chromium/components/arc/audio/DEPS
index 5ff5c5b2fa8..ef5d54a3d94 100644
--- a/chromium/components/arc/audio/DEPS
+++ b/chromium/components/arc/audio/DEPS
@@ -1,4 +1,6 @@
include_rules = [
- "+ash/system/audio/tray_audio.h",
+ "+ash/public",
"+chromeos/audio",
+ "+content/public/common",
+ "+services/service_manager/public",
]
diff --git a/chromium/components/arc/audio/arc_audio_bridge.cc b/chromium/components/arc/audio/arc_audio_bridge.cc
index 72a8106d695..258d9db3d6c 100644
--- a/chromium/components/arc/audio/arc_audio_bridge.cc
+++ b/chromium/components/arc/audio/arc_audio_bridge.cc
@@ -6,12 +6,15 @@
#include <utility>
-#include "ash/system/audio/tray_audio.h"
+#include "ash/public/interfaces/constants.mojom.h"
+#include "ash/public/interfaces/system_tray.mojom.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "chromeos/audio/audio_device.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
+#include "content/public/common/service_manager_connection.h"
+#include "services/service_manager/public/cpp/connector.h"
namespace arc {
namespace {
@@ -72,7 +75,11 @@ void ArcAudioBridge::OnConnectionClosed() {
void ArcAudioBridge::ShowVolumeControls() {
DVLOG(2) << "ArcAudioBridge::ShowVolumeControls";
- ash::TrayAudio::ShowPopUpVolumeView();
+ ash::mojom::SystemTrayPtr system_tray_ptr;
+ content::ServiceManagerConnection::GetForProcess()
+ ->GetConnector()
+ ->BindInterface(ash::mojom::kServiceName, &system_tray_ptr);
+ system_tray_ptr->ShowVolumeSliderBubble();
}
void ArcAudioBridge::OnSystemVolumeUpdateRequest(int32_t percent) {
diff --git a/chromium/components/arc/common/BUILD.gn b/chromium/components/arc/common/BUILD.gn
index b8da88bd7a0..fba35b76a0a 100644
--- a/chromium/components/arc/common/BUILD.gn
+++ b/chromium/components/arc/common/BUILD.gn
@@ -13,6 +13,7 @@ if (is_chromeos) {
sources = [
"accessibility_helper.mojom",
"app.mojom",
+ "appfuse.mojom",
"arc_bridge.mojom",
"audio.mojom",
"auth.mojom",
@@ -23,6 +24,7 @@ if (is_chromeos) {
"cert_store.mojom",
"clipboard.mojom",
"crash_collector.mojom",
+ "disk_quota.mojom",
"enterprise_reporting.mojom",
"file_system.mojom",
"ime.mojom",
diff --git a/chromium/components/arc/common/accessibility_helper.mojom b/chromium/components/arc/common/accessibility_helper.mojom
index 8fb1ab75c8f..035dc8e7f55 100644
--- a/chromium/components/arc/common/accessibility_helper.mojom
+++ b/chromium/components/arc/common/accessibility_helper.mojom
@@ -106,7 +106,10 @@ enum AccessibilityBooleanProperty {
MULTI_LINE,
CONTENT_INVALID,
CONTEXT_CLICKABLE,
- IMPORTANCE
+ IMPORTANCE,
+ SCREEN_READER_FOCUSABLE,
+ SHOWING_HINT_TEXT,
+ HEADING
};
// These fields are taken from string instance members of
@@ -119,7 +122,8 @@ enum AccessibilityStringProperty {
CONTENT_DESCRIPTION,
VIEW_ID_RESOURCE_NAME,
CHROME_ROLE, // Chrome only
- ROLE_DESCRIPTION // Chrome only
+ ROLE_DESCRIPTION, // Chrome only
+ TOOLTIP
};
// These fields are taken from int instance members of
diff --git a/chromium/components/arc/common/app.mojom b/chromium/components/arc/common/app.mojom
index 450e6879561..52114b12d79 100644
--- a/chromium/components/arc/common/app.mojom
+++ b/chromium/components/arc/common/app.mojom
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Next MinVersion: 32
+// Next MinVersion: 36
module arc.mojom;
@@ -22,6 +22,7 @@ struct AppInfo {
string activity;
[MinVersion=2] bool sticky; // true if the app cannot be uninstalled
[MinVersion=7] bool notifications_enabled;
+ [MinVersion=35] bool suspended;
};
// Describes ARC package.
@@ -156,6 +157,18 @@ enum AppDataRequestState {
FAILED_TO_CALL_GLOBALQUERY = 8,
};
+// Describes the category type of app shortcut item.
+[Extensible]
+enum AppShortcutItemType {
+ // Static shortcut, which means it was published from AndroidManifest.xml.
+ kStatic = 0,
+
+ // Dynamic shortcut, which means it was published at runtime using the
+ // ShortcutManagerAPI. Dynamic shortcuts are links to specific,
+ // context-sensitive actions within the app.
+ kDynamic = 1,
+};
+
// Describes app shortcut that is published by Android's ShortcutManager.
struct AppShortcutItem {
// The ID of this shortcut. Unique within each publisher app and stable across
@@ -170,6 +183,12 @@ struct AppShortcutItem {
// The package name of the publisher app.
[MinVersion=31] string? package_name;
+
+ // The category type of this shortcut.
+ [MinVersion=32] AppShortcutItemType type;
+
+ // "Rank" of a shortcut, which is a non-negative, sequential value.
+ [MinVersion=32] int32 rank;
};
// Next method ID: 18
@@ -254,7 +273,7 @@ interface AppHost {
};
// TODO(lhchavez): Migrate all request/response messages to Mojo.
-// Next method ID: 25
+// Next method ID: 27
interface AppInstance {
// DEPRECATED: Please use Init@21 instead.
InitDeprecated@0(AppHost host_ptr);
@@ -339,12 +358,20 @@ interface AppInstance {
// Sends a request to ARC to start PAI flow.
[MinVersion=21] StartPaiFlow@17();
+ // Sends a request to ARC to start FastAppReinstall flow.
+ [MinVersion=33] StartFastAppReinstallFlow@25(array<string> arc_package_names);
+
// Sends a request to ARC to uninstall the given package. Error (if ever
// happens) is ignored, and uninstall option should appear in the UI.
[MinVersion=2] UninstallPackage@5(string package_name);
- // Sends a request to ARC to get app shortcuts for app with |package_name|. If
- // |package_name| is empty, it will query for all launchable activities.
+ // Sends a request to ARC to get app shortcuts for apps in global scope that
+ // match a |query|. Limits the results to only return |max_results|.
+ [MinVersion=34] GetAppShortcutGlobalQueryItems@26(
+ string query, int32 max_results) =>
+ (array<AppShortcutItem> shortcut_items);
+
+ // Sends a request to ARC to get app shortcuts for app with |package_name|.
[MinVersion=29] GetAppShortcutItems@23(string package_name) =>
(array<AppShortcutItem> shortcut_items);
diff --git a/chromium/components/arc/common/appfuse.mojom b/chromium/components/arc/common/appfuse.mojom
new file mode 100644
index 00000000000..ee81f3c788b
--- /dev/null
+++ b/chromium/components/arc/common/appfuse.mojom
@@ -0,0 +1,27 @@
+// 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.
+
+// Next MinVersion: 1
+
+module arc.mojom;
+
+// Next Method ID: 3
+interface AppfuseHost {
+ // Mounts a new appfuse file system and returns a filtered /dev/fuse FD
+ // associated with the mounted file system.
+ Mount@0(uint32 uid, int32 mount_id) => (handle? fd);
+
+ // Unmounts the specified appfuse file system.
+ Unmount@1(uint32 uid, int32 mount_id) => (bool success);
+
+ // Opens a file under the specified appfuse file system.
+ OpenFile@2(uint32 uid, int32 mount_id, int32 file_id, int32 flags) =>
+ (handle? fd);
+};
+
+// Next Method ID: 1
+interface AppfuseInstance {
+ // Establishes full-duplex communication with the host.
+ Init@0(AppfuseHost host_ptr) => ();
+};
diff --git a/chromium/components/arc/common/arc_bridge.mojom b/chromium/components/arc/common/arc_bridge.mojom
index 6966f3e53be..0ddd8236cfb 100644
--- a/chromium/components/arc/common/arc_bridge.mojom
+++ b/chromium/components/arc/common/arc_bridge.mojom
@@ -6,6 +6,7 @@ module arc.mojom;
import "components/arc/common/accessibility_helper.mojom";
import "components/arc/common/app.mojom";
+import "components/arc/common/appfuse.mojom";
import "components/arc/common/audio.mojom";
import "components/arc/common/auth.mojom";
import "components/arc/common/backup_settings.mojom";
@@ -15,6 +16,7 @@ import "components/arc/common/cast_receiver.mojom";
import "components/arc/common/cert_store.mojom";
import "components/arc/common/clipboard.mojom";
import "components/arc/common/crash_collector.mojom";
+import "components/arc/common/disk_quota.mojom";
import "components/arc/common/enterprise_reporting.mojom";
import "components/arc/common/file_system.mojom";
import "components/arc/common/ime.mojom";
@@ -46,9 +48,9 @@ import "components/arc/common/volume_mounter.mojom";
import "components/arc/common/wake_lock.mojom";
import "components/arc/common/wallpaper.mojom";
-// Next MinVersion: 39
+// Next MinVersion: 41
// Deprecated method IDs: 101, 105
-// Next method ID: 144
+// Next method ID: 146
interface ArcBridgeHost {
// Keep the entries alphabetical. In order to do so without breaking
// compatibility with the ARC instance, explicitly assign each interface a
@@ -61,6 +63,9 @@ interface ArcBridgeHost {
// Notifies Chrome that the AppInstance interface is ready.
OnAppInstanceReady@100(AppInstance instance_ptr);
+ // Notifies Chrome that the AppfuseInstance interface is ready.
+ [MinVersion=40] OnAppfuseInstanceReady@145(AppfuseInstance instance_ptr);
+
// Notifies Chrome that the AudioInstance interface is ready.
[MinVersion=8] OnAudioInstanceReady@115(AudioInstance instance_ptr);
@@ -92,6 +97,9 @@ interface ArcBridgeHost {
[MinVersion=7] OnCrashCollectorInstanceReady@112(
CrashCollectorInstance instance_ptr);
+ // Notifies Chrome that the DiskQuotaInstance interface is ready.
+ [MinVersion=39] OnDiskQuotaInstanceReady@144(DiskQuotaInstance instance_ptr);
+
// Notifies Chrome that the EnterpriseReportingInstance interface is ready.
[MinVersion=15] OnEnterpriseReportingInstanceReady@122(
EnterpriseReportingInstance instance_ptr);
diff --git a/chromium/components/arc/common/auth.mojom b/chromium/components/arc/common/auth.mojom
index 5cf12bdbfec..44989756caf 100644
--- a/chromium/components/arc/common/auth.mojom
+++ b/chromium/components/arc/common/auth.mojom
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Next MinVersion: 14
+// Next MinVersion: 15
module arc.mojom;
@@ -128,7 +128,7 @@ enum SupervisionChangeStatus {
// These values describe the type of the Chrome account to provision.
[Extensible]
enum ChromeAccountType {
- // Next value: 5
+ // Next value: 6
UNKNOWN = 0,
// Chrome login account type is a user account.
@@ -142,6 +142,11 @@ enum ChromeAccountType {
// Chrome login account type is a child account.
CHILD_ACCOUNT = 4,
+
+ // Chrome login account is a demo session account that was enrolled offline,
+ // using policies bundled with Chrome OS, and thus does not have a working
+ // robot account associated with it.
+ [MinVersion=14] OFFLINE_DEMO_ACCOUNT = 5,
};
// These values describe the type of the metrics to report.
diff --git a/chromium/components/arc/common/bluetooth.mojom b/chromium/components/arc/common/bluetooth.mojom
index 548f35b268d..df85323125d 100644
--- a/chromium/components/arc/common/bluetooth.mojom
+++ b/chromium/components/arc/common/bluetooth.mojom
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Next MinVersion: 11
+// Next MinVersion: 13
module arc.mojom;
@@ -292,7 +292,7 @@ struct BluetoothCreateSdpRecordResult {
uint32 service_handle;
};
-// Next Method ID: 44
+// Next Method ID: 45
interface BluetoothHost {
EnableAdapter@0() => (BluetoothAdapterState state);
DisableAdapter@1() => (BluetoothAdapterState state);
@@ -329,7 +329,8 @@ interface BluetoothHost {
[MinVersion=1] WriteGattCharacteristic@23(BluetoothAddress remote_addr,
BluetoothGattServiceID service_id,
BluetoothGattID char_id,
- BluetoothGattValue value)
+ BluetoothGattValue value,
+ [MinVersion=11] bool prepare)
=> (BluetoothGattStatus status);
[MinVersion=1] ReadGattDescriptor@24(BluetoothAddress remote_addr,
BluetoothGattServiceID service_id,
@@ -342,6 +343,9 @@ interface BluetoothHost {
BluetoothGattID desc_id,
BluetoothGattValue value)
=> (BluetoothGattStatus status);
+ [MinVersion=11] ExecuteWrite@44(BluetoothAddress remote_addr,
+ bool execute)
+ => (BluetoothGattStatus status);
[MinVersion=1] RegisterForGattNotification@26(
BluetoothAddress remote_addr,
BluetoothGattServiceID service_id,
@@ -405,7 +409,7 @@ interface BluetoothHost {
=> (BluetoothGattStatus status);
};
-// Next Method ID: 20
+// Next Method ID: 21
interface BluetoothInstance {
// DEPRECATED: Please use Init@18 instead.
InitDeprecated@0(BluetoothHost host_ptr);
@@ -463,7 +467,12 @@ interface BluetoothInstance {
int32 attribute_handle,
int32 offset,
array<uint8> value,
- [MinVersion=9] BluetoothGattDBAttributeType attribute_type)
+ [MinVersion=9] BluetoothGattDBAttributeType attribute_type,
+ [MinVersion=12] bool is_prepare)
+ => (BluetoothGattStatus status);
+ [MinVersion=12] RequestGattExecuteWrite@20(
+ BluetoothAddress address,
+ bool execute)
=> (BluetoothGattStatus status);
[MinVersion=10] OnMTUReceived@19(BluetoothAddress remote_addr, uint16 mtu);
diff --git a/chromium/components/arc/common/disk_quota.mojom b/chromium/components/arc/common/disk_quota.mojom
new file mode 100644
index 00000000000..006537a6c95
--- /dev/null
+++ b/chromium/components/arc/common/disk_quota.mojom
@@ -0,0 +1,22 @@
+// 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.
+
+module arc.mojom;
+
+// Next Method ID: 3
+interface DiskQuotaHost {
+ // Whether cryptohome supports quota-based stats.
+ IsQuotaSupported@0() => (bool supported);
+
+ // Get the current disk space usage for a uid. Returns -1 for failure.
+ GetCurrentSpaceForUid@1(uint32 uid) => (int64 cur_space);
+
+ // Get the current disk space usage for a gid. Returns -1 for failure.
+ GetCurrentSpaceForGid@2(uint32 gid) => (int64 cur_space);
+};
+
+// Next Method ID: 1
+interface DiskQuotaInstance {
+ Init@0(DiskQuotaHost host_ptr) => ();
+}; \ No newline at end of file
diff --git a/chromium/components/arc/common/ime.mojom b/chromium/components/arc/common/ime.mojom
index 732a37bcbd4..c06d96b92b2 100644
--- a/chromium/components/arc/common/ime.mojom
+++ b/chromium/components/arc/common/ime.mojom
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Next MinVersion: 10
+// Next MinVersion: 11
module arc.mojom;
@@ -37,7 +37,9 @@ struct CompositionSegment {
// Next method ID: 6
interface ImeHost {
// Notifies Chrome that the text input focus is changed.
- OnTextInputTypeChanged@0(TextInputType type);
+ OnTextInputTypeChanged@0(
+ TextInputType type,
+ [MinVersion=10] bool is_personalized_learning_allowed);
// Notifies Chrome that the cursor poisition has changed.
//
@@ -52,7 +54,7 @@ interface ImeHost {
[MinVersion=1] OnCancelComposition@2();
// Show virtual keyboard of Chrome OS if needed.
- [MinVersion=2] ShowImeIfNeeded@3();
+ [MinVersion=2] ShowVirtualKeyboardIfEnabled@3();
// Notifies Chrome that the cursor position has changed and
// also sends surrounding text.
diff --git a/chromium/components/arc/common/intent_helper.mojom b/chromium/components/arc/common/intent_helper.mojom
index e292f6d7869..c189631926a 100644
--- a/chromium/components/arc/common/intent_helper.mojom
+++ b/chromium/components/arc/common/intent_helper.mojom
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Next MinVersion: 21
+// Next MinVersion: 22
module arc.mojom;
@@ -48,6 +48,7 @@ struct IntentFilter {
[MinVersion=10] array<AuthorityEntry>? data_authorities;
[MinVersion=10] array<PatternMatcher>? data_paths;
[MinVersion=10] array<PatternMatcher>? deprecated_data_scheme_specific_parts;
+ [MinVersion=21] string? package_name; // Package which registered the filter.
};
// Describes a package that can handle an intent.
@@ -123,6 +124,22 @@ enum ChromePage {
LAST = ABOUTHISTORY,
};
+// Describes an action given by the android text classifier (e.g. open maps).
+struct TextSelectionAction {
+ // The icon of the component that can handle the action intent.
+ ActivityIcon icon;
+
+ // The activity and package name of the component that handle the intent.
+ ActivityName activity;
+
+ // The title of the action that will be shown in the UI, e.g. "Map", "Call",
+ // "Open".
+ string title;
+
+ // The intent to launch when the action is clicked.
+ IntentInfo action_intent;
+};
+
// Handles intents from ARC in Chrome.
// Deprecated method ID: 4
// Next method ID: 8
@@ -157,7 +174,7 @@ interface IntentHelperHost {
};
// Sends intents to ARC on behalf of Chrome.
-// Next method ID: 14
+// Next method ID: 15
interface IntentHelperInstance {
// Sets the given package as a preferred package. The next time an ACTION_VIEW
// intent is sent with a URL that requires disambiguation, instead of opening
@@ -217,4 +234,9 @@ interface IntentHelperInstance {
string package_name,
string cls,
string extras);
+
+ // Asks TextClassifier to generate an action that can handle the |text|.
+ // |scale_factor| is used to find a suitable icon to show in the context menu.
+ [MinVersion=21] ClassifySelection@14(string text, ScaleFactor scale_factor)
+ => (TextSelectionAction? action);
};
diff --git a/chromium/components/arc/common/notifications.mojom b/chromium/components/arc/common/notifications.mojom
index ffcbcc5b67b..8f5aa7b9cbf 100644
--- a/chromium/components/arc/common/notifications.mojom
+++ b/chromium/components/arc/common/notifications.mojom
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Next MinVersion: 17
+// Next MinVersion: 20
module arc.mojom;
@@ -72,6 +72,19 @@ enum ArcNotificationShownContents {
CONTENTS_SHOWN = 0,
// The notification settings view is shown.
SETTINGS_SHOWN = 1,
+ // The notification snooze settings view is shown.
+ [MinVersion=17]
+ SNOOZE_SHOWN = 2,
+};
+
+// Flag for various feature of ARC notifications.
+[Extensible, MinVersion=17]
+struct ArcNotificationFlags {
+ // Bits for features.
+ const uint32 SUPPORT_SNOOZE = 1; // 1 << 0
+
+ // Bit-masked value.
+ uint32 value;
};
struct ArcNotificationData {
@@ -139,10 +152,21 @@ struct ArcNotificationData {
Rect? swipe_input_rect;
[MinVersion=15]
string? package_name;
+ [MinVersion=17]
+ ArcNotificationFlags? flags;
+};
+
+[Extensible, MinVersion=18]
+struct ArcDoNotDisturbStatus {
+ bool enabled;
};
-// Next Method ID: 7
+// Next Method ID: 8
interface NotificationsHost {
+ // Set the Do-Not-Disturb status on Chrome side from Android side.
+ [MinVersion=18]
+ OnDoNotDisturbStatusUpdated@7(ArcDoNotDisturbStatus status);
+
// Tells the Chrome that a notification is posted (created or updated) on
// Android. If you know that the notification exists, please consider to use
// OnNotificationUpdate instead.
@@ -162,7 +186,7 @@ interface NotificationsHost {
OpenMessageCenter@6();
};
-// Next Method ID: 6
+// Next Method ID: 9
// TODO(lhchavez): Migrate all request/response messages to Mojo.
interface NotificationsInstance {
// DEPRECATED: Please use Init@5 instead.
@@ -192,4 +216,20 @@ interface NotificationsInstance {
// side.
[MinVersion=9]
OpenNotificationSettings@4(string key);
+
+ // Requests to Android side to open snooze settings.
+ // |key| is the identifier of the notification which is generated by Android
+ // side.
+ [MinVersion=17]
+ OpenNotificationSnoozeSettings@6(string key);
+
+ // Sets the Do-Not-Disturb status on Android side from Chrome side.
+ [MinVersion=18]
+ SetDoNotDisturbStatusOnAndroid@7(ArcDoNotDisturbStatus status);
+
+ // Cancels the long-press operation even during touching the view. This should
+ // be called when the on-going gesture is recognized not as a normal touch
+ // event but as a scroll event, and the on-going touch should be cancelled.
+ [MinVersion=19]
+ CancelLongPress@8(string key);
};
diff --git a/chromium/components/arc/common/policy.mojom b/chromium/components/arc/common/policy.mojom
index f2a8804d84a..35396e8c8d8 100644
--- a/chromium/components/arc/common/policy.mojom
+++ b/chromium/components/arc/common/policy.mojom
@@ -97,9 +97,8 @@ interface PolicyInstance {
OnPolicyUpdated@1();
// Forwards a command received from the management server. The payload is
- // opaque to Chrome OS (it contains JSON from the RemoteCommand.payload field
- // for the USER_ARC_COMMAND RemoteCommand - cf.
- // device_management_backend.proto).
+ // opaque to Chrome (it contains JSON from the RemoteCommand.payload field for
+ // the USER_ARC_COMMAND RemoteCommand - cf. device_management_backend.proto).
[MinVersion=4] OnCommandReceived@3(string command)
=> (CommandResultType result);
};
diff --git a/chromium/components/arc/common/process.mojom b/chromium/components/arc/common/process.mojom
index 18b90c30a28..62137d0b117 100644
--- a/chromium/components/arc/common/process.mojom
+++ b/chromium/components/arc/common/process.mojom
@@ -2,15 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// Next MinVersion: 6
+// Next MinVersion: 7
module arc.mojom;
// Describes the current process state, as defined by AOSP in
// android.app.ActivityManager.
+[Extensible]
enum ProcessState {
- // Process does not exist.
- NONEXISTENT = -1,
+ // Not a real process state.
+ UNKNOWN = -1,
// Process is a persistent system process.
PERSISTENT = 0,
@@ -22,55 +23,65 @@ enum ProcessState {
// all activities that are visible to the user.
TOP = 2,
- // Process is hosting a foreground service due to a system binding.
- BOUND_FOREGROUND_SERVICE = 3,
-
// Process is hosting a foreground service.
- FOREGROUND_SERVICE = 4,
+ FOREGROUND_SERVICE = 3,
- // Same as PROCESS_STATE_TOP but while device is sleeping.
- TOP_SLEEPING = 5,
+ // Process is hosting a foreground service due to a system binding.
+ BOUND_FOREGROUND_SERVICE = 4,
// Process is important to the user, and something they are aware of.
- IMPORTANT_FOREGROUND = 6,
+ IMPORTANT_FOREGROUND = 5,
// Process is important to the user, but not something they are aware of.
- IMPORTANT_BACKGROUND = 7,
+ IMPORTANT_BACKGROUND = 6,
+
+ // Process is in the background transient so we will try to keep running.
+ TRANSIENT_BACKGROUND = 7,
// Process is in the background running a backup/restore operation.
BACKUP = 8,
- // Process is in the background, but it can't restore its state so we want
- // to try to avoid killing it.
- HEAVY_WEIGHT = 9,
-
// Process is in the background running a service. Unlike oom_adj, this level
// is used for both the normal running in background state and the executing
// operations state.
- SERVICE = 10,
+ SERVICE = 9,
- // Process is in the background running a receiver. Note that from the
- // perspective of oom_adj receivers run at a higher foreground level, but for
- // our prioritization here that is not necessary and putting them below
+ // Process is in the background running a receiver. Note that from the
+ // perspective of oom_adj, receivers run at a higher foreground level, but
+ // for our prioritization here that is not necessary and putting them below
// services means many fewer changes in some process states as they receive
// broadcasts.
- RECEIVER = 11,
+ RECEIVER = 10,
+
+ // Same as PROCESS_STATE_TOP but while device is sleeping.
+ TOP_SLEEPING = 11,
+
+ // Process is in the background, but it can't restore its state so we want
+ // to try to avoid killing it.
+ HEAVY_WEIGHT = 12,
// Process is in the background but hosts the home activity.
- HOME = 12,
+ HOME = 13,
// Process is in the background but hosts the last shown activity.
- LAST_ACTIVITY = 13,
+ LAST_ACTIVITY = 14,
// Process is being cached for later use and contains activities.
- CACHED_ACTIVITY = 14,
+ CACHED_ACTIVITY = 15,
// Process is being cached for later use and is a client of another cached
// process that contains activities.
- CACHED_ACTIVITY_CLIENT = 15,
+ CACHED_ACTIVITY_CLIENT = 16,
+
+ // Process is being cached for later use and has an activity that corresponds
+ // to an existing recent task.
+ CACHED_RECENT = 17,
// Process is being cached for later use and is empty.
- CACHED_EMPTY = 16,
+ CACHED_EMPTY = 18,
+
+ // Process does not exist.
+ NONEXISTENT = 19,
};
// Describes a running ARC process.
@@ -86,33 +97,21 @@ struct RunningAppProcessInfo {
ProcessState process_state;
// Package names running in the process.
- [MinVersion=4] array<string>? packages;
+ array<string>? packages;
// Whether this app is focused in ARC multi-window environment.
- [MinVersion=5] bool is_focused;
+ bool is_focused;
// Last time the process was active. Milliseconds since boot.
// The clock is monotonic (comes from Android System.uptimeMillis()).
- [MinVersion=5] int64 last_activity_time;
+ int64 last_activity_time;
};
interface ProcessInstance {
- // Requests ARC instance to return the current process list.
- RequestProcessList@0() => (array<RunningAppProcessInfo> processes);
-
// Requests ARC instance to kill a process.
[MinVersion=1]
KillProcess@1(uint32 pid, string reason);
- // Sets oom_score_adj of a process.
- [MinVersion=2]
- DeprecatedSetOomScoreAdj@2(uint32 pid, int32 score);
-
- // Disables Android built-in oom_adj adjustment.
- [MinVersion=2]
- DeprecatedDisableBuiltinOomAdjustment@3();
-
- // Disables Android lowmemorykiller.
- [MinVersion=3]
- DeprecatedDisableLowMemoryKiller@4();
+ [MinVersion=6]
+ RequestProcessList@5() => (array<RunningAppProcessInfo> processes);
};
diff --git a/chromium/components/arc/common/timer.mojom b/chromium/components/arc/common/timer.mojom
index 6efb88e8f8e..4d8760e3096 100644
--- a/chromium/components/arc/common/timer.mojom
+++ b/chromium/components/arc/common/timer.mojom
@@ -30,33 +30,21 @@ struct CreateTimerRequest {
handle expiration_fd;
};
-// Next method ID: 1
-interface Timer {
- // Starts the timer to run at |absolute_expiration_time| in the future. If
- // the timer is already running, it will be replaced. Notification will be
- // performed as an 8-byte write to the associated expiration fd. Returns
- // |ArcTimerResult::SUCCESS| if the timer can be started and
- // |ArcTimerResult::FAILURE| otherwise.
- Start@0(mojo_base.mojom.TimeTicks absolute_expiration_time)
- => (ArcTimerResult result);
-};
-
-struct CreateTimerResponse {
- // Type of the clock associated with the timer.
- ClockId clock_id;
-
- // Timer object that will set timers of type |clock_id|.
- Timer timer;
-};
-
-// Next method ID: 1
+// Next method ID: 2
interface TimerHost {
- // Creates timers with the given arguments. Returns a null array on failure
- // and an array of |Timer| objects of the same size as |arc_timer_requests|
- // on success. Only one |Timer| per clock id is created and
- // |arc_timer_requests| need to have unique clock ids for success.
+ // Creates timers with the given arguments. Returns |ArcTimerResult::SUCCESS|
+ // iff timers corresponding to all clocks in |timer_requests| are created.
CreateTimers@0(array<CreateTimerRequest> timer_requests)
- => (array<CreateTimerResponse>? response);
+ => (ArcTimerResult result);
+
+ // Starts the timer of type |clock_id| to run at |absolute_expiration_time| in
+ // the future. If the timer is already running, it will be replaced.
+ // Notification will be performed as an 8-byte write to the associated
+ // expiration fd. Returns |ArcTimerResult::SUCCESS| if the timer can be
+ // started and |ArcTimerResult::FAILURE| otherwise.
+ StartTimer@1(ClockId clock_id,
+ mojo_base.mojom.TimeTicks absolute_expiration_time)
+ => (ArcTimerResult result);
};
// Next method ID: 1
diff --git a/chromium/components/arc/common/timer.typemap b/chromium/components/arc/common/timer.typemap
index 6f46f6c8cea..df7d6f3401c 100644
--- a/chromium/components/arc/common/timer.typemap
+++ b/chromium/components/arc/common/timer.typemap
@@ -3,10 +3,8 @@
# found in the LICENSE file.
mojom = "//components/arc/common/timer.mojom"
-public_headers = [ "//components/arc/timer/create_timer_request.h" ]
-traits_headers = [ "//components/arc/timer/arc_timer_traits.h" ]
+traits_headers = [ "//components/arc/timer/arc_timer_struct_traits.h" ]
sources = [
- "//components/arc/timer/arc_timer_traits.cc",
+ "//components/arc/timer/arc_timer_struct_traits.cc",
]
-type_mappings =
- [ "arc.mojom.CreateTimerRequest=arc::CreateTimerRequest[move_only]" ]
+type_mappings = [ "arc.mojom.ClockId=clockid_t" ]
diff --git a/chromium/components/arc/common/video_accelerator_struct_traits.cc b/chromium/components/arc/common/video_accelerator_struct_traits.cc
index 63ee67d25a2..dfb8d49f05f 100644
--- a/chromium/components/arc/common/video_accelerator_struct_traits.cc
+++ b/chromium/components/arc/common/video_accelerator_struct_traits.cc
@@ -52,7 +52,9 @@ CHECK_PROFILE_ENUM(THEORAPROFILE_MIN);
CHECK_PROFILE_ENUM(THEORAPROFILE_ANY);
CHECK_PROFILE_ENUM(THEORAPROFILE_MAX);
CHECK_PROFILE_ENUM(AV1PROFILE_MIN);
-CHECK_PROFILE_ENUM(AV1PROFILE_PROFILE0);
+CHECK_PROFILE_ENUM(AV1PROFILE_PROFILE_MAIN);
+CHECK_PROFILE_ENUM(AV1PROFILE_PROFILE_HIGH);
+CHECK_PROFILE_ENUM(AV1PROFILE_PROFILE_PRO);
CHECK_PROFILE_ENUM(AV1PROFILE_MAX);
CHECK_PROFILE_ENUM(VIDEO_CODEC_PROFILE_MAX);
@@ -95,7 +97,9 @@ bool EnumTraits<arc::mojom::VideoCodecProfile, media::VideoCodecProfile>::
case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE5:
case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE7:
case arc::mojom::VideoCodecProfile::THEORAPROFILE_ANY:
- case arc::mojom::VideoCodecProfile::AV1PROFILE_PROFILE0:
+ case arc::mojom::VideoCodecProfile::AV1PROFILE_PROFILE_MAIN:
+ case arc::mojom::VideoCodecProfile::AV1PROFILE_PROFILE_HIGH:
+ case arc::mojom::VideoCodecProfile::AV1PROFILE_PROFILE_PRO:
*output = static_cast<media::VideoCodecProfile>(input);
return true;
}
diff --git a/chromium/components/arc/common/video_common.mojom b/chromium/components/arc/common/video_common.mojom
index 968917c0bfb..188a3509cfd 100644
--- a/chromium/components/arc/common/video_common.mojom
+++ b/chromium/components/arc/common/video_common.mojom
@@ -50,12 +50,12 @@ enum VideoCodecProfile {
THEORAPROFILE_MIN = 23,
THEORAPROFILE_ANY = THEORAPROFILE_MIN,
THEORAPROFILE_MAX = THEORAPROFILE_ANY,
- // TODO(dalecurtis): AV1 profiles are not finalized, this needs updating
- // before enabling for release. http://crbug.com/784993
AV1PROFILE_MIN = 24,
- AV1PROFILE_PROFILE0 = AV1PROFILE_MIN,
- AV1PROFILE_MAX = AV1PROFILE_PROFILE0,
- VIDEO_CODEC_PROFILE_MAX = AV1PROFILE_PROFILE0,
+ AV1PROFILE_PROFILE_MAIN = AV1PROFILE_MIN,
+ AV1PROFILE_PROFILE_HIGH = 25,
+ AV1PROFILE_PROFILE_PRO = 26,
+ AV1PROFILE_MAX = AV1PROFILE_PROFILE_PRO,
+ VIDEO_CODEC_PROFILE_MAX = AV1PROFILE_PROFILE_PRO,
};
[Extensible]
diff --git a/chromium/components/arc/common/volume_mounter.typemap b/chromium/components/arc/common/volume_mounter.typemap
index 0b905728284..4a025cf050b 100644
--- a/chromium/components/arc/common/volume_mounter.typemap
+++ b/chromium/components/arc/common/volume_mounter.typemap
@@ -9,9 +9,10 @@ public_headers = [
"//chromeos/dbus/dbus_client_implementation_type.h",
"//chromeos/disks/disk_mount_manager.h",
]
-traits_headers = [ "//components/arc/volume_mounter/volume_mounter_traits.h" ]
+traits_headers =
+ [ "//components/arc/volume_mounter/volume_mounter_struct_traits.h" ]
sources = [
- "//components/arc/volume_mounter/volume_mounter_traits.cc",
+ "//components/arc/volume_mounter/volume_mounter_struct_traits.cc",
]
type_mappings = [
"arc.mojom.DeviceType=chromeos::DeviceType",
diff --git a/chromium/components/arc/crash_collector/arc_crash_collector_bridge.cc b/chromium/components/arc/crash_collector/arc_crash_collector_bridge.cc
index bb49c4d3c73..3f13c056a15 100644
--- a/chromium/components/arc/crash_collector/arc_crash_collector_bridge.cc
+++ b/chromium/components/arc/crash_collector/arc_crash_collector_bridge.cc
@@ -9,6 +9,7 @@
#include <utility>
+#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/process/launch.h"
@@ -16,7 +17,7 @@
#include "base/task_scheduler/task_traits.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
-#include "mojo/edk/embedder/embedder.h"
+#include "mojo/public/cpp/system/platform_handle.h"
namespace {
@@ -27,11 +28,9 @@ void RunCrashReporter(const std::string& crash_type,
const std::string& device,
const std::string& board,
const std::string& cpu_abi,
- mojo::edk::ScopedInternalPlatformHandle pipe) {
+ base::ScopedFD pipe) {
base::LaunchOptions options;
- options.fds_to_remap.push_back(
- std::make_pair(pipe.get().handle, STDIN_FILENO));
-
+ options.fds_to_remap.emplace_back(pipe.get(), STDIN_FILENO);
auto process =
base::LaunchProcess({kCrashReporterPath, "--arc_java_crash=" + crash_type,
"--arc_device=" + device, "--arc_board=" + board,
@@ -91,14 +90,10 @@ ArcCrashCollectorBridge::~ArcCrashCollectorBridge() {
void ArcCrashCollectorBridge::DumpCrash(const std::string& type,
mojo::ScopedHandle pipe) {
- mojo::edk::ScopedInternalPlatformHandle pipe_handle;
- mojo::edk::PassWrappedInternalPlatformHandle(pipe.release().value(),
- &pipe_handle);
-
base::PostTaskWithTraits(
FROM_HERE, {base::WithBaseSyncPrimitives()},
base::BindOnce(&RunCrashReporter, type, device_, board_, cpu_abi_,
- std::move(pipe_handle)));
+ mojo::UnwrapPlatformHandle(std::move(pipe)).TakeFD()));
}
void ArcCrashCollectorBridge::SetBuildProperties(const std::string& device,
diff --git a/chromium/components/arc/disk_quota/arc_disk_quota_bridge.cc b/chromium/components/arc/disk_quota/arc_disk_quota_bridge.cc
new file mode 100644
index 00000000000..2489e90e962
--- /dev/null
+++ b/chromium/components/arc/disk_quota/arc_disk_quota_bridge.cc
@@ -0,0 +1,103 @@
+// 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/arc/disk_quota/arc_disk_quota_bridge.h"
+
+#include "base/bind.h"
+#include "base/memory/singleton.h"
+#include "base/optional.h"
+#include "chromeos/dbus/cryptohome_client.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
+
+namespace arc {
+
+namespace {
+
+// Singleton factory for ArcDiskQuotaBridge.
+class ArcDiskQuotaBridgeFactory
+ : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+ ArcDiskQuotaBridge,
+ ArcDiskQuotaBridgeFactory> {
+ public:
+ // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+ static constexpr const char* kName = "ArcDiskQuotaBridgeFactory";
+
+ static ArcDiskQuotaBridgeFactory* GetInstance() {
+ return base::Singleton<ArcDiskQuotaBridgeFactory>::get();
+ }
+
+ private:
+ friend base::DefaultSingletonTraits<ArcDiskQuotaBridgeFactory>;
+ ArcDiskQuotaBridgeFactory() = default;
+ ~ArcDiskQuotaBridgeFactory() override = default;
+};
+
+} // namespace
+
+// static
+ArcDiskQuotaBridge* ArcDiskQuotaBridge::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return ArcDiskQuotaBridgeFactory::GetForBrowserContext(context);
+}
+
+ArcDiskQuotaBridge::ArcDiskQuotaBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service)
+ : arc_bridge_service_(bridge_service) {
+ arc_bridge_service_->disk_quota()->SetHost(this);
+}
+
+ArcDiskQuotaBridge::~ArcDiskQuotaBridge() {
+ arc_bridge_service_->disk_quota()->SetHost(nullptr);
+}
+
+void ArcDiskQuotaBridge::IsQuotaSupported(IsQuotaSupportedCallback callback) {
+ chromeos::DBusThreadManager::Get()->GetCryptohomeClient()->IsQuotaSupported(
+ base::BindOnce(
+ [](IsQuotaSupportedCallback callback, base::Optional<bool> result) {
+ LOG_IF(ERROR, !result.has_value())
+ << "Failed to retrieve result from IsQuotaSupported call.";
+ std::move(callback).Run(result.value_or(false));
+ },
+ std::move(callback)));
+}
+
+void ArcDiskQuotaBridge::GetCurrentSpaceForUid(
+ uint32_t uid,
+ GetCurrentSpaceForUidCallback callback) {
+ chromeos::DBusThreadManager::Get()
+ ->GetCryptohomeClient()
+ ->GetCurrentSpaceForUid(
+ uid, base::BindOnce(
+ [](GetCurrentSpaceForUidCallback callback, int uid,
+ base::Optional<int64_t> result) {
+ LOG_IF(ERROR, !result.has_value())
+ << "Failed to retrieve result from "
+ "GetCurrentSpaceForUid for android uid="
+ << uid;
+ std::move(callback).Run(result.value_or(-1LL));
+ },
+ std::move(callback), uid));
+}
+
+void ArcDiskQuotaBridge::GetCurrentSpaceForGid(
+ uint32_t gid,
+ GetCurrentSpaceForGidCallback callback) {
+ chromeos::DBusThreadManager::Get()
+ ->GetCryptohomeClient()
+ ->GetCurrentSpaceForGid(
+ gid, base::BindOnce(
+ [](GetCurrentSpaceForGidCallback callback, int gid,
+ base::Optional<int64_t> result) {
+ LOG_IF(ERROR, !result.has_value())
+ << "Failed to retrieve result from "
+ "GetCurrentSpaceForGid for android gid="
+ << gid;
+ std::move(callback).Run(result.value_or(-1LL));
+ },
+ std::move(callback), gid));
+}
+
+} // namespace arc
diff --git a/chromium/components/arc/disk_quota/arc_disk_quota_bridge.h b/chromium/components/arc/disk_quota/arc_disk_quota_bridge.h
new file mode 100644
index 00000000000..3b8c73f0055
--- /dev/null
+++ b/chromium/components/arc/disk_quota/arc_disk_quota_bridge.h
@@ -0,0 +1,49 @@
+// 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_ARC_DISK_QUOTA_ARC_DISK_QUOTA_BRIDGE_H_
+#define COMPONENTS_ARC_DISK_QUOTA_ARC_DISK_QUOTA_BRIDGE_H_
+
+#include "base/macros.h"
+#include "components/arc/common/disk_quota.mojom.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace content {
+class BrowserContext;
+} // namespace content
+
+namespace arc {
+
+class ArcBridgeService;
+
+// This class proxies quota requests from Android to cryptohome.
+class ArcDiskQuotaBridge : public KeyedService, public mojom::DiskQuotaHost {
+ public:
+ // Returns singleton instance for the given BrowserContext,
+ // or nullptr if the browser |context| is not allowed to use ARC.
+ static ArcDiskQuotaBridge* GetForBrowserContext(
+ content::BrowserContext* context);
+
+ ArcDiskQuotaBridge(content::BrowserContext* context,
+ ArcBridgeService* bridge_service);
+ ~ArcDiskQuotaBridge() override;
+
+ // mojom::DiskQuotaHost overrides:
+ void IsQuotaSupported(IsQuotaSupportedCallback callback) override;
+
+ void GetCurrentSpaceForUid(uint32_t uid,
+ GetCurrentSpaceForUidCallback callback) override;
+
+ void GetCurrentSpaceForGid(uint32_t gid,
+ GetCurrentSpaceForGidCallback callback) override;
+
+ private:
+ ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
+
+ DISALLOW_COPY_AND_ASSIGN(ArcDiskQuotaBridge);
+};
+
+} // namespace arc
+
+#endif // COMPONENTS_ARC_DISK_QUOTA_ARC_DISK_QUOTA_BRIDGE_H_
diff --git a/chromium/components/arc/ime/DEPS b/chromium/components/arc/ime/DEPS
index 9610b76fc63..b4af81c78ca 100644
--- a/chromium/components/arc/ime/DEPS
+++ b/chromium/components/arc/ime/DEPS
@@ -1,6 +1,7 @@
include_rules = [
"+ui/aura",
"+ui/base/ime",
+ "+ui/base/ui_base_features.h",
"+ui/events",
"+ui/gfx/geometry",
"+ui/keyboard",
diff --git a/chromium/components/arc/ime/arc_ime_bridge.h b/chromium/components/arc/ime/arc_ime_bridge.h
index b982b8b0c04..4feceaea6f5 100644
--- a/chromium/components/arc/ime/arc_ime_bridge.h
+++ b/chromium/components/arc/ime/arc_ime_bridge.h
@@ -29,11 +29,13 @@ class ArcImeBridge {
// Received IPCs are deserialized and passed to this delegate.
class Delegate {
public:
- virtual void OnTextInputTypeChanged(ui::TextInputType type) = 0;
+ virtual void OnTextInputTypeChanged(
+ ui::TextInputType type,
+ bool is_personalized_learning_allowed) = 0;
virtual void OnCursorRectChanged(const gfx::Rect& rect,
bool is_screen_cooridnates) = 0;
virtual void OnCancelComposition() = 0;
- virtual void ShowImeIfNeeded() = 0;
+ virtual void ShowVirtualKeyboardIfEnabled() = 0;
virtual void OnCursorRectChangedWithSurroundingText(
const gfx::Rect& rect,
const gfx::Range& text_range,
diff --git a/chromium/components/arc/ime/arc_ime_bridge_impl.cc b/chromium/components/arc/ime/arc_ime_bridge_impl.cc
index 47b41789f54..626d17332a1 100644
--- a/chromium/components/arc/ime/arc_ime_bridge_impl.cc
+++ b/chromium/components/arc/ime/arc_ime_bridge_impl.cc
@@ -134,8 +134,11 @@ void ArcImeBridgeImpl::SendOnKeyboardAppearanceChanging(
ime_instance->OnKeyboardAppearanceChanging(new_bounds, is_available);
}
-void ArcImeBridgeImpl::OnTextInputTypeChanged(mojom::TextInputType type) {
- delegate_->OnTextInputTypeChanged(ConvertTextInputType(type));
+void ArcImeBridgeImpl::OnTextInputTypeChanged(
+ mojom::TextInputType type,
+ bool is_personalized_learning_allowed) {
+ delegate_->OnTextInputTypeChanged(ConvertTextInputType(type),
+ is_personalized_learning_allowed);
}
void ArcImeBridgeImpl::OnCursorRectChanged(const gfx::Rect& rect,
@@ -147,8 +150,8 @@ void ArcImeBridgeImpl::OnCancelComposition() {
delegate_->OnCancelComposition();
}
-void ArcImeBridgeImpl::ShowImeIfNeeded() {
- delegate_->ShowImeIfNeeded();
+void ArcImeBridgeImpl::ShowVirtualKeyboardIfEnabled() {
+ delegate_->ShowVirtualKeyboardIfEnabled();
}
void ArcImeBridgeImpl::OnCursorRectChangedWithSurroundingText(
diff --git a/chromium/components/arc/ime/arc_ime_bridge_impl.h b/chromium/components/arc/ime/arc_ime_bridge_impl.h
index fdbffcab3d7..48904d312ca 100644
--- a/chromium/components/arc/ime/arc_ime_bridge_impl.h
+++ b/chromium/components/arc/ime/arc_ime_bridge_impl.h
@@ -38,11 +38,12 @@ class ArcImeBridgeImpl : public ArcImeBridge, public mojom::ImeHost {
bool is_available) override;
// mojom::ImeHost overrides:
- void OnTextInputTypeChanged(mojom::TextInputType type) override;
+ void OnTextInputTypeChanged(mojom::TextInputType type,
+ bool is_personalized_learning_allowed) override;
void OnCursorRectChanged(const gfx::Rect& rect,
bool screen_coordinates) override;
void OnCancelComposition() override;
- void ShowImeIfNeeded() override;
+ void ShowVirtualKeyboardIfEnabled() override;
void OnCursorRectChangedWithSurroundingText(const gfx::Rect& rect,
const gfx::Range& text_range,
const std::string& text_in_range,
diff --git a/chromium/components/arc/ime/arc_ime_service.cc b/chromium/components/arc/ime/arc_ime_service.cc
index d6155b3297e..61372bb9a20 100644
--- a/chromium/components/arc/ime/arc_ime_service.cc
+++ b/chromium/components/arc/ime/arc_ime_service.cc
@@ -11,6 +11,7 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
+#include "components/arc/arc_util.h"
#include "components/arc/ime/arc_ime_bridge_impl.h"
#include "components/exo/shell_surface.h"
#include "components/exo/surface.h"
@@ -19,16 +20,19 @@
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/ime/input_method.h"
+#include "ui/base/ui_base_features.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/range/range.h"
+#include "ui/keyboard/keyboard_controller.h"
namespace arc {
namespace {
-constexpr char kArcAppIdPrefix[] = "org.chromium.arc";
+constexpr char kArcNotificationAppId[] =
+ "org.chromium.arc.ArcNotificationService";
base::Optional<double> g_override_default_device_scale_factor;
@@ -59,15 +63,19 @@ class ArcWindowDelegateImpl : public ArcImeService::ArcWindowDelegate {
aura::Window* active = exo::WMHelper::GetInstance()->GetActiveWindow();
if (!active || !active->Contains(window))
return false;
-
- if (IsArcNotificationWindow(window, active))
+ // If the ARC app is focused, the active window should be ARC app window.
+ if (IsArcAppWindow(active))
return true;
- // Need to get an application id from the active window because only
- // ShellSurface window has the application id.
- const std::string* app_id = exo::ShellSurface::GetApplicationId(active);
- return app_id && base::StartsWith(*app_id, kArcAppIdPrefix,
- base::CompareCase::SENSITIVE);
+ // If the ARC notification is focused, the active window is not ARC app
+ // window. Find an app id set to the window and check if it is the ARC
+ // notification app id.
+ for (; window && window != active; window = window->parent()) {
+ const std::string* app_id = exo::ShellSurface::GetApplicationId(window);
+ if (app_id)
+ return *app_id == kArcNotificationAppId;
+ }
+ return false;
}
void RegisterFocusObserver() override {
@@ -91,20 +99,6 @@ class ArcWindowDelegateImpl : public ArcImeService::ArcWindowDelegate {
}
private:
- bool IsArcNotificationWindow(const aura::Window* window,
- const aura::Window* active) const {
- DCHECK(IsExoWindow(window));
- // TODO(yhanada): Should set an application id for NotificationSurface
- // to kArcAppIdPrefix, then we can eliminate this method.
- // https://crbug.com/834027
- for (const aura::Window* parent = window; parent != active;
- parent = parent->parent()) {
- if (parent->GetName() == "ExoNotificationSurface")
- return true;
- }
- return false;
- }
-
ArcImeService* const ime_service_;
DISALLOW_COPY_AND_ASSIGN(ArcWindowDelegateImpl);
@@ -145,8 +139,8 @@ ArcImeService::ArcImeService(content::BrowserContext* context,
: ime_bridge_(new ArcImeBridgeImpl(this, bridge_service)),
arc_window_delegate_(new ArcWindowDelegateImpl(this)),
ime_type_(ui::TEXT_INPUT_TYPE_NONE),
+ is_personalized_learning_allowed_(false),
has_composition_text_(false),
- keyboard_controller_(nullptr),
is_focus_observer_installed_(false) {
aura::Env* env = aura::Env::GetInstanceDontCreate();
if (env)
@@ -165,11 +159,13 @@ ArcImeService::~ArcImeService() {
aura::Env* env = aura::Env::GetInstanceDontCreate();
if (env)
env->RemoveObserver(this);
- // Removing |this| from KeyboardController.
- keyboard::KeyboardController* keyboard_controller =
- keyboard::KeyboardController::GetInstance();
- if (keyboard_controller && keyboard_controller_ == keyboard_controller) {
- keyboard_controller_->RemoveObserver(this);
+
+ // KeyboardController is destroyed before ArcImeService (except in tests),
+ // so check whether there is a KeyboardController first before removing |this|
+ // from KeyboardController observers.
+ if (keyboard::KeyboardController::HasInstance()) {
+ auto* keyboard_controller = keyboard::KeyboardController::Get();
+ keyboard_controller->RemoveObserver(this);
}
}
@@ -214,12 +210,16 @@ void ArcImeService::OnWindowInitialized(aura::Window* new_window) {
is_focus_observer_installed_ = true;
}
}
- keyboard::KeyboardController* keyboard_controller =
- keyboard::KeyboardController::GetInstance();
- if (keyboard_controller && keyboard_controller_ != keyboard_controller) {
- // Registering |this| as an observer only once per KeyboardController.
- keyboard_controller_ = keyboard_controller;
- keyboard_controller_->AddObserver(this);
+
+ // TODO(mash): Support virtual keyboard under MASH. There is no
+ // KeyboardController in the browser process under MASH.
+ if (features::IsAshInBrowserProcess() &&
+ keyboard::KeyboardController::HasInstance()) {
+ auto* keyboard_controller = keyboard::KeyboardController::Get();
+ if (keyboard_controller->enabled() &&
+ !keyboard_controller->HasObserver(this)) {
+ keyboard_controller->AddObserver(this);
+ }
}
}
@@ -269,10 +269,15 @@ void ArcImeService::OnWindowFocused(aura::Window* gained_focus,
////////////////////////////////////////////////////////////////////////////////
// Overridden from arc::ArcImeBridge::Delegate
-void ArcImeService::OnTextInputTypeChanged(ui::TextInputType type) {
- if (ime_type_ == type)
+void ArcImeService::OnTextInputTypeChanged(
+ ui::TextInputType type,
+ bool is_personalized_learning_allowed) {
+ if (ime_type_ == type &&
+ is_personalized_learning_allowed_ == is_personalized_learning_allowed) {
return;
+ }
ime_type_ = type;
+ is_personalized_learning_allowed_ = is_personalized_learning_allowed;
ui::InputMethod* const input_method = GetInputMethod();
if (input_method)
@@ -297,10 +302,10 @@ void ArcImeService::OnCancelComposition() {
input_method->CancelComposition(this);
}
-void ArcImeService::ShowImeIfNeeded() {
+void ArcImeService::ShowVirtualKeyboardIfEnabled() {
ui::InputMethod* const input_method = GetInputMethod();
if (input_method && input_method->GetTextInputClient() == this) {
- input_method->ShowImeIfNeeded();
+ input_method->ShowVirtualKeyboardIfEnabled();
}
}
@@ -323,9 +328,14 @@ void ArcImeService::OnCursorRectChangedWithSurroundingText(
}
void ArcImeService::RequestHideIme() {
- auto* keyboard_controller = keyboard::KeyboardController::GetInstance();
- if (keyboard_controller)
- keyboard_controller->MaybeHideKeyboard();
+ // TODO(mash): Support virtual keyboard under MASH. There is no
+ // KeyboardController in the browser process under MASH.
+ if (features::IsAshInBrowserProcess() &&
+ keyboard::KeyboardController::HasInstance()) {
+ auto* keyboard_controller = keyboard::KeyboardController::Get();
+ if (keyboard_controller->enabled())
+ keyboard_controller->HideKeyboardImplicitlyBySystem();
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -345,8 +355,7 @@ void ArcImeService::OnKeyboardAppearanceChanged(
gfx::Rect bounds_in_px =
gfx::ScaleToEnclosingRect(new_bounds, GetDefaultDeviceScaleFactor());
- ime_bridge_->SendOnKeyboardAppearanceChanging(bounds_in_px,
- state.is_available);
+ ime_bridge_->SendOnKeyboardAppearanceChanging(bounds_in_px, state.is_visible);
}
////////////////////////////////////////////////////////////////////////////////
@@ -513,16 +522,14 @@ bool ArcImeService::IsTextEditCommandEnabled(
return false;
}
-const std::string& ArcImeService::GetClientSourceInfo() const {
+ukm::SourceId ArcImeService::GetClientSourceForMetrics() const {
// TODO(yhanada): Implement this method. crbug.com/752657
NOTIMPLEMENTED_LOG_ONCE();
- return base::EmptyString();
+ return ukm::SourceId();
}
bool ArcImeService::ShouldDoLearning() {
- // TODO(https://crbug.com/311180): Implement this method.
- NOTIMPLEMENTED_LOG_ONCE();
- return true;
+ return is_personalized_learning_allowed_;
}
// static
diff --git a/chromium/components/arc/ime/arc_ime_service.h b/chromium/components/arc/ime/arc_ime_service.h
index a1beae6cdb7..23ced4fae91 100644
--- a/chromium/components/arc/ime/arc_ime_service.h
+++ b/chromium/components/arc/ime/arc_ime_service.h
@@ -17,7 +17,6 @@
#include "ui/base/ime/text_input_flags.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/gfx/geometry/rect.h"
-#include "ui/keyboard/keyboard_controller.h"
#include "ui/keyboard/keyboard_controller_observer.h"
namespace aura {
@@ -85,11 +84,12 @@ class ArcImeService : public KeyedService,
aura::Window* lost_focus) override;
// Overridden from ArcImeBridge::Delegate:
- void OnTextInputTypeChanged(ui::TextInputType type) override;
+ void OnTextInputTypeChanged(ui::TextInputType type,
+ bool is_personalized_learning_allowed) override;
void OnCursorRectChanged(const gfx::Rect& rect,
bool is_screen_coordinates) override;
void OnCancelComposition() override;
- void ShowImeIfNeeded() override;
+ void ShowVirtualKeyboardIfEnabled() override;
void OnCursorRectChangedWithSurroundingText(
const gfx::Rect& rect,
const gfx::Range& text_range,
@@ -137,7 +137,7 @@ class ArcImeService : public KeyedService,
bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override;
void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override {
}
- const std::string& GetClientSourceInfo() const override;
+ ukm::SourceId GetClientSourceForMetrics() const override;
bool ShouldDoLearning() override;
// Normally, the default device scale factor is used to convert from DPI to
@@ -163,6 +163,7 @@ class ArcImeService : public KeyedService,
std::unique_ptr<ArcImeBridge> ime_bridge_;
std::unique_ptr<ArcWindowDelegate> arc_window_delegate_;
ui::TextInputType ime_type_;
+ bool is_personalized_learning_allowed_;
gfx::Rect cursor_rect_;
bool has_composition_text_;
gfx::Range text_range_;
@@ -171,8 +172,6 @@ class ArcImeService : public KeyedService,
aura::Window* focused_arc_window_ = nullptr;
- keyboard::KeyboardController* keyboard_controller_;
-
bool is_focus_observer_installed_;
DISALLOW_COPY_AND_ASSIGN(ArcImeService);
diff --git a/chromium/components/arc/ime/arc_ime_service_unittest.cc b/chromium/components/arc/ime/arc_ime_service_unittest.cc
index 46eebb7b13e..2555ba59435 100644
--- a/chromium/components/arc/ime/arc_ime_service_unittest.cc
+++ b/chromium/components/arc/ime/arc_ime_service_unittest.cc
@@ -19,6 +19,7 @@
#include "ui/base/ime/dummy_input_method.h"
#include "ui/events/event.h"
#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/keyboard/keyboard_controller.h"
namespace arc {
@@ -60,10 +61,12 @@ class FakeArcImeBridge : public ArcImeBridge {
class FakeInputMethod : public ui::DummyInputMethod {
public:
- FakeInputMethod() : client_(nullptr),
- count_show_ime_if_needed_(0),
- count_cancel_composition_(0),
- count_set_focused_text_input_client_(0) {}
+ FakeInputMethod()
+ : client_(nullptr),
+ count_show_ime_if_needed_(0),
+ count_cancel_composition_(0),
+ count_set_focused_text_input_client_(0),
+ count_on_text_input_type_changed_(0) {}
void SetFocusedTextInputClient(ui::TextInputClient* client) override {
count_set_focused_text_input_client_++;
@@ -74,9 +77,7 @@ class FakeInputMethod : public ui::DummyInputMethod {
return client_;
}
- void ShowImeIfNeeded() override {
- count_show_ime_if_needed_++;
- }
+ void ShowVirtualKeyboardIfEnabled() override { count_show_ime_if_needed_++; }
void CancelComposition(const ui::TextInputClient* client) override {
if (client == client_)
@@ -88,6 +89,10 @@ class FakeInputMethod : public ui::DummyInputMethod {
client_ = nullptr;
}
+ void OnTextInputTypeChanged(const ui::TextInputClient* client) override {
+ count_on_text_input_type_changed_++;
+ }
+
int count_show_ime_if_needed() const {
return count_show_ime_if_needed_;
}
@@ -100,11 +105,16 @@ class FakeInputMethod : public ui::DummyInputMethod {
return count_set_focused_text_input_client_;
}
+ int count_on_text_input_type_changed() const {
+ return count_on_text_input_type_changed_;
+ }
+
private:
ui::TextInputClient* client_;
int count_show_ime_if_needed_;
int count_cancel_composition_;
int count_set_focused_text_input_client_;
+ int count_on_text_input_type_changed_;
};
// Helper class for testing the window focus tracking feature of ArcImeService,
@@ -165,6 +175,9 @@ class ArcImeServiceTest : public testing::Test {
FakeArcWindowDelegate* fake_window_delegate_; // Owned by |instance_|
std::unique_ptr<aura::Window> arc_win_;
+ // Needed by ArcImeService.
+ keyboard::KeyboardController keyboard_controller_;
+
private:
void SetUp() override {
arc_bridge_service_ = std::make_unique<ArcBridgeService>();
@@ -220,17 +233,17 @@ TEST_F(ArcImeServiceTest, HasCompositionText) {
EXPECT_FALSE(instance_->HasCompositionText());
}
-TEST_F(ArcImeServiceTest, ShowImeIfNeeded) {
+TEST_F(ArcImeServiceTest, ShowVirtualKeyboardIfEnabled) {
instance_->OnWindowFocused(arc_win_.get(), nullptr);
- instance_->OnTextInputTypeChanged(ui::TEXT_INPUT_TYPE_NONE);
+ instance_->OnTextInputTypeChanged(ui::TEXT_INPUT_TYPE_NONE, false);
ASSERT_EQ(0, fake_input_method_->count_show_ime_if_needed());
// Text input type change does not imply the show ime request.
- instance_->OnTextInputTypeChanged(ui::TEXT_INPUT_TYPE_TEXT);
+ instance_->OnTextInputTypeChanged(ui::TEXT_INPUT_TYPE_TEXT, true);
EXPECT_EQ(0, fake_input_method_->count_show_ime_if_needed());
- instance_->ShowImeIfNeeded();
+ instance_->ShowVirtualKeyboardIfEnabled();
EXPECT_EQ(1, fake_input_method_->count_show_ime_if_needed());
}
@@ -246,12 +259,12 @@ TEST_F(ArcImeServiceTest, InsertChar) {
instance_->OnWindowFocused(arc_win_.get(), nullptr);
// When text input type is NONE, the event is not forwarded.
- instance_->OnTextInputTypeChanged(ui::TEXT_INPUT_TYPE_NONE);
+ instance_->OnTextInputTypeChanged(ui::TEXT_INPUT_TYPE_NONE, false);
instance_->InsertChar(ui::KeyEvent('a', ui::VKEY_A, 0));
EXPECT_EQ(0, fake_arc_ime_bridge_->count_send_insert_text());
// When the bridge is accepting text inputs, forward the event.
- instance_->OnTextInputTypeChanged(ui::TEXT_INPUT_TYPE_TEXT);
+ instance_->OnTextInputTypeChanged(ui::TEXT_INPUT_TYPE_TEXT, true);
instance_->InsertChar(ui::KeyEvent('a', ui::VKEY_A, 0));
EXPECT_EQ(1, fake_arc_ime_bridge_->count_send_insert_text());
}
@@ -386,4 +399,21 @@ TEST_F(ArcImeServiceTest, GetCaretBounds) {
instance_->GetCaretBounds());
}
+TEST_F(ArcImeServiceTest, ShouldDoLearning) {
+ instance_->OnWindowFocused(arc_win_.get(), nullptr);
+
+ ASSERT_NE(ui::TEXT_INPUT_TYPE_TEXT, instance_->GetTextInputType());
+ instance_->OnTextInputTypeChanged(ui::TEXT_INPUT_TYPE_TEXT, true);
+ EXPECT_TRUE(instance_->ShouldDoLearning());
+ EXPECT_EQ(1, fake_input_method_->count_on_text_input_type_changed());
+
+ instance_->OnTextInputTypeChanged(ui::TEXT_INPUT_TYPE_TEXT, false);
+ EXPECT_FALSE(instance_->ShouldDoLearning());
+ EXPECT_EQ(2, fake_input_method_->count_on_text_input_type_changed());
+
+ instance_->OnTextInputTypeChanged(ui::TEXT_INPUT_TYPE_URL, false);
+ EXPECT_FALSE(instance_->ShouldDoLearning());
+ EXPECT_EQ(3, fake_input_method_->count_on_text_input_type_changed());
+}
+
} // namespace arc
diff --git a/chromium/components/arc/intent_helper/arc_intent_helper_bridge.cc b/chromium/components/arc/intent_helper/arc_intent_helper_bridge.cc
index 653614ba259..a5b91e4fc47 100644
--- a/chromium/components/arc/intent_helper/arc_intent_helper_bridge.cc
+++ b/chromium/components/arc/intent_helper/arc_intent_helper_bridge.cc
@@ -11,7 +11,6 @@
#include "ash/public/interfaces/constants.mojom.h"
#include "ash/public/interfaces/wallpaper.mojom.h"
#include "ash/shell.h"
-#include "ash/shell_delegate.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
@@ -20,6 +19,7 @@
#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/audio/arc_audio_bridge.h"
+#include "components/arc/intent_helper/open_url_delegate.h"
#include "components/url_formatter/url_fixer.h"
#include "content/public/common/service_manager_connection.h"
#include "services/service_manager/public/cpp/connector.h"
@@ -73,18 +73,9 @@ static_assert(arraysize(kMapping) ==
static_cast<size_t>(mojom::ChromePage::LAST) + 1,
"kMapping is out of sync");
-class OpenUrlDelegateImpl : public ArcIntentHelperBridge::OpenUrlDelegate {
- public:
- ~OpenUrlDelegateImpl() override = default;
-
- // ArcIntentHelperBridge::OpenUrlDelegate:
- void OpenUrl(const GURL& url) override {
- // TODO(mash): Support this functionality without ash::Shell access in
- // Chrome.
- if (ash::Shell::HasInstance())
- ash::Shell::Get()->shell_delegate()->OpenUrlFromArc(url);
- }
-};
+// Not owned. Must outlive all ArcIntentHelperBridge instances. Typically this
+// is ChromeNewWindowClient in the browser.
+OpenUrlDelegate* g_open_url_delegate = nullptr;
// Singleton factory for ArcIntentHelperBridge.
class ArcIntentHelperBridgeFactory
@@ -132,11 +123,15 @@ std::string ArcIntentHelperBridge::AppendStringToIntentHelperPackageName(
return base::JoinString({kArcIntentHelperPackageName, to_append}, ".");
}
+// static
+void ArcIntentHelperBridge::SetOpenUrlDelegate(OpenUrlDelegate* delegate) {
+ g_open_url_delegate = delegate;
+}
+
ArcIntentHelperBridge::ArcIntentHelperBridge(content::BrowserContext* context,
ArcBridgeService* bridge_service)
: context_(context),
arc_bridge_service_(bridge_service),
- open_url_delegate_(std::make_unique<OpenUrlDelegateImpl>()),
allowed_chrome_pages_map_(std::cbegin(kMapping), std::cend(kMapping)),
allowed_arc_schemes_(std::cbegin(kArcSchemes), std::cend(kArcSchemes)) {
arc_bridge_service_->intent_helper()->SetHost(this);
@@ -171,7 +166,7 @@ void ArcIntentHelperBridge::OnOpenUrl(const std::string& url) {
return;
if (allowed_arc_schemes_.find(gurl.scheme()) != allowed_arc_schemes_.end())
- open_url_delegate_->OpenUrl(gurl);
+ g_open_url_delegate->OpenUrlFromArc(gurl);
}
void ArcIntentHelperBridge::OnOpenChromePage(mojom::ChromePage page) {
@@ -184,10 +179,12 @@ void ArcIntentHelperBridge::OnOpenChromePage(mojom::ChromePage page) {
}
GURL page_gurl(it->second);
- if (page_gurl.SchemeIs(url::kAboutScheme))
- open_url_delegate_->OpenUrl(page_gurl);
- else
- open_url_delegate_->OpenUrl(GURL(kSettingsPageBaseUrl).Resolve(it->second));
+ if (page_gurl.SchemeIs(url::kAboutScheme)) {
+ g_open_url_delegate->OpenUrlFromArc(page_gurl);
+ } else {
+ g_open_url_delegate->OpenUrlFromArc(
+ GURL(kSettingsPageBaseUrl).Resolve(it->second));
+ }
}
void ArcIntentHelperBridge::OpenWallpaperPicker() {
@@ -274,9 +271,4 @@ void ArcIntentHelperBridge::OnIntentFiltersUpdated(
observer.OnIntentFiltersUpdated();
}
-void ArcIntentHelperBridge::SetOpenUrlDelegateForTesting(
- std::unique_ptr<OpenUrlDelegate> open_url_delegate) {
- open_url_delegate_ = std::move(open_url_delegate);
-}
-
} // namespace arc
diff --git a/chromium/components/arc/intent_helper/arc_intent_helper_bridge.h b/chromium/components/arc/intent_helper/arc_intent_helper_bridge.h
index 9922c11922b..a63b85ae6a8 100644
--- a/chromium/components/arc/intent_helper/arc_intent_helper_bridge.h
+++ b/chromium/components/arc/intent_helper/arc_intent_helper_bridge.h
@@ -30,6 +30,7 @@ namespace arc {
class ArcBridgeService;
class IntentFilter;
+class OpenUrlDelegate;
// Receives intents from ARC.
class ArcIntentHelperBridge
@@ -48,6 +49,8 @@ class ArcIntentHelperBridge
static std::string AppendStringToIntentHelperPackageName(
const std::string& to_append);
+ static void SetOpenUrlDelegate(OpenUrlDelegate* delegate);
+
ArcIntentHelperBridge(content::BrowserContext* context,
ArcBridgeService* bridge_service);
~ArcIntentHelperBridge() override;
@@ -67,14 +70,6 @@ class ArcIntentHelperBridge
void SetWallpaperDeprecated(const std::vector<uint8_t>& jpeg_data) override;
void OpenVolumeControl() override;
- class OpenUrlDelegate {
- public:
- virtual ~OpenUrlDelegate() = default;
-
- // Opens the given URL in the Chrome browser.
- virtual void OpenUrl(const GURL& url) = 0;
- };
-
// Retrieves icons for the |activities| and calls |callback|.
// See ActivityIconLoader::GetActivityIcons() for more details.
using ActivityName = internal::ActivityIconLoader::ActivityName;
@@ -102,9 +97,6 @@ class ArcIntentHelperBridge
static std::vector<mojom::IntentHandlerInfoPtr> FilterOutIntentHelper(
std::vector<mojom::IntentHandlerInfoPtr> handlers);
- void SetOpenUrlDelegateForTesting(
- std::unique_ptr<OpenUrlDelegate> open_url_delegate);
-
static const char kArcIntentHelperPackageName[];
private:
@@ -113,7 +105,6 @@ class ArcIntentHelperBridge
content::BrowserContext* const context_;
ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
- std::unique_ptr<OpenUrlDelegate> open_url_delegate_;
internal::ActivityIconLoader icon_loader_;
// List of intent filters from Android. Used to determine if Chrome should
diff --git a/chromium/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc b/chromium/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc
index dde660f0f8d..e747a669cc3 100644
--- a/chromium/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc
+++ b/chromium/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc
@@ -10,16 +10,19 @@
#include "base/memory/ptr_util.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/common/intent_helper.mojom.h"
+#include "components/arc/intent_helper/open_url_delegate.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace arc {
namespace {
+constexpr char kPackageName[] = "default.package.name";
+
IntentFilter GetIntentFilter(const std::string& host) {
std::vector<IntentFilter::AuthorityEntry> authorities;
authorities.emplace_back(host, -1);
- return IntentFilter(std::move(authorities),
+ return IntentFilter(kPackageName, std::move(authorities),
std::vector<IntentFilter::PatternMatcher>());
}
@@ -29,12 +32,12 @@ class ArcIntentHelperTest : public testing::Test {
protected:
ArcIntentHelperTest() = default;
- class TestOpenUrlDelegate : public ArcIntentHelperBridge::OpenUrlDelegate {
+ class TestOpenUrlDelegate : public OpenUrlDelegate {
public:
~TestOpenUrlDelegate() override = default;
- // ArcIntentHelperBridge::OpenUrlDelegate:
- void OpenUrl(const GURL& url) override { last_opened_url_ = url; }
+ // OpenUrlDelegate:
+ void OpenUrlFromArc(const GURL& url) override { last_opened_url_ = url; }
GURL TakeLastOpenedUrl() {
GURL result = std::move(last_opened_url_);
@@ -47,21 +50,22 @@ class ArcIntentHelperTest : public testing::Test {
};
std::unique_ptr<ArcBridgeService> arc_bridge_service_;
- TestOpenUrlDelegate* test_open_url_delegate_; // owned by |instance_|
+ std::unique_ptr<TestOpenUrlDelegate> test_open_url_delegate_;
std::unique_ptr<ArcIntentHelperBridge> instance_;
private:
void SetUp() override {
arc_bridge_service_ = std::make_unique<ArcBridgeService>();
+ test_open_url_delegate_ = std::make_unique<TestOpenUrlDelegate>();
instance_ = std::make_unique<ArcIntentHelperBridge>(
nullptr /* context */, arc_bridge_service_.get());
- test_open_url_delegate_ = new TestOpenUrlDelegate();
- instance_->SetOpenUrlDelegateForTesting(
- base::WrapUnique(test_open_url_delegate_));
+ ArcIntentHelperBridge::SetOpenUrlDelegate(test_open_url_delegate_.get());
}
void TearDown() override {
+ ArcIntentHelperBridge::SetOpenUrlDelegate(nullptr);
instance_.reset();
+ test_open_url_delegate_.reset();
arc_bridge_service_.reset();
}
diff --git a/chromium/components/arc/intent_helper/intent_filter.cc b/chromium/components/arc/intent_helper/intent_filter.cc
index 32fb9a143f0..d82b8ba260f 100644
--- a/chromium/components/arc/intent_helper/intent_filter.cc
+++ b/chromium/components/arc/intent_helper/intent_filter.cc
@@ -17,9 +17,10 @@ IntentFilter::IntentFilter() = default;
IntentFilter::IntentFilter(IntentFilter&& other) = default;
IntentFilter::IntentFilter(
+ const std::string& package_name,
std::vector<IntentFilter::AuthorityEntry> authorities,
std::vector<IntentFilter::PatternMatcher> paths)
- : authorities_(std::move(authorities)) {
+ : package_name_(package_name), authorities_(std::move(authorities)) {
// In order to register a path we need to have at least one authority.
if (!authorities_.empty())
paths_ = std::move(paths);
diff --git a/chromium/components/arc/intent_helper/intent_filter.h b/chromium/components/arc/intent_helper/intent_filter.h
index e6c447f5873..0640775525d 100644
--- a/chromium/components/arc/intent_helper/intent_filter.h
+++ b/chromium/components/arc/intent_helper/intent_filter.h
@@ -71,7 +71,8 @@ class IntentFilter {
IntentFilter();
IntentFilter(IntentFilter&& other);
- IntentFilter(std::vector<AuthorityEntry> authorities,
+ IntentFilter(const std::string& package_name,
+ std::vector<AuthorityEntry> authorities,
std::vector<PatternMatcher> paths);
~IntentFilter();
@@ -79,6 +80,7 @@ class IntentFilter {
bool Match(const GURL& url) const;
+ const std::string& package_name() const { return package_name_; }
const std::vector<AuthorityEntry>& authorities() const {
return authorities_;
}
@@ -88,6 +90,7 @@ class IntentFilter {
bool MatchDataAuthority(const GURL& url) const;
bool HasDataPath(const GURL& url) const;
+ std::string package_name_;
std::vector<AuthorityEntry> authorities_;
std::vector<PatternMatcher> paths_;
diff --git a/chromium/components/arc/intent_helper/intent_filter_struct_traits.cc b/chromium/components/arc/intent_helper/intent_filter_struct_traits.cc
index e4a59fceafe..1a33f43a5eb 100644
--- a/chromium/components/arc/intent_helper/intent_filter_struct_traits.cc
+++ b/chromium/components/arc/intent_helper/intent_filter_struct_traits.cc
@@ -23,7 +23,12 @@ bool StructTraits<arc::mojom::IntentFilterDataView, arc::IntentFilter>::
if (!data.ReadDataPaths(&paths))
return false;
- *out = arc::IntentFilter(std::move(authorities), std::move(paths));
+ std::string package_name;
+ if (!data.ReadPackageName(&package_name))
+ return false;
+
+ *out =
+ arc::IntentFilter(package_name, std::move(authorities), std::move(paths));
return true;
}
@@ -32,8 +37,7 @@ bool StructTraits<arc::mojom::AuthorityEntryDataView,
Read(arc::mojom::AuthorityEntryDataView data,
arc::IntentFilter::AuthorityEntry* out) {
std::string host;
- bool result = data.ReadHost(&host);
- if (!result)
+ if (!data.ReadHost(&host))
return false;
*out = arc::IntentFilter::AuthorityEntry(std::move(host), data.port());
diff --git a/chromium/components/arc/intent_helper/intent_filter_struct_traits.h b/chromium/components/arc/intent_helper/intent_filter_struct_traits.h
index a76ba40790c..421001dad36 100644
--- a/chromium/components/arc/intent_helper/intent_filter_struct_traits.h
+++ b/chromium/components/arc/intent_helper/intent_filter_struct_traits.h
@@ -43,6 +43,10 @@ struct StructTraits<arc::mojom::IntentFilterDataView, arc::IntentFilter> {
return base::span<arc::IntentFilter::PatternMatcher>();
}
+ static const std::string& package_name(const arc::IntentFilter& r) {
+ return r.package_name();
+ }
+
static bool Read(arc::mojom::IntentFilterDataView data,
arc::IntentFilter* out);
};
diff --git a/chromium/components/arc/intent_helper/intent_filter_unittest.cc b/chromium/components/arc/intent_helper/intent_filter_unittest.cc
index 1205c2e529c..73a9d3fe934 100644
--- a/chromium/components/arc/intent_helper/intent_filter_unittest.cc
+++ b/chromium/components/arc/intent_helper/intent_filter_unittest.cc
@@ -18,6 +18,8 @@ namespace arc {
namespace {
+constexpr char kPackageName[] = "default.package.name";
+
class IntentFilterBuilder {
public:
IntentFilterBuilder() = default;
@@ -38,7 +40,8 @@ class IntentFilterBuilder {
}
operator IntentFilter() {
- return IntentFilter(std::move(authorities_), std::move(paths_));
+ return IntentFilter(kPackageName, std::move(authorities_),
+ std::move(paths_));
}
private:
diff --git a/chromium/components/arc/intent_helper/open_url_delegate.h b/chromium/components/arc/intent_helper/open_url_delegate.h
new file mode 100644
index 00000000000..63744e9188b
--- /dev/null
+++ b/chromium/components/arc/intent_helper/open_url_delegate.h
@@ -0,0 +1,22 @@
+// 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_ARC_INTENT_HELPER_OPEN_URL_DELEGATE_H_
+#define COMPONENTS_ARC_INTENT_HELPER_OPEN_URL_DELEGATE_H_
+
+class GURL;
+
+namespace arc {
+
+class OpenUrlDelegate {
+ public:
+ virtual ~OpenUrlDelegate() = default;
+
+ // Opens the given URL in the Chrome browser.
+ virtual void OpenUrlFromArc(const GURL& url) = 0;
+};
+
+} // namespace arc
+
+#endif // COMPONENTS_ARC_INTENT_HELPER_OPEN_URL_DELEGATE_H_
diff --git a/chromium/components/arc/intent_helper/page_transition_util.cc b/chromium/components/arc/intent_helper/page_transition_util.cc
deleted file mode 100644
index e4cc29db66c..00000000000
--- a/chromium/components/arc/intent_helper/page_transition_util.cc
+++ /dev/null
@@ -1,45 +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/arc/intent_helper/page_transition_util.h"
-
-namespace arc {
-
-bool ShouldIgnoreNavigation(ui::PageTransition page_transition,
- bool allow_form_submit,
- bool allow_client_redirect) {
- // |allow_client_redirect| is true only for non-http(s) cases, and for those
- // we can ignore the CLIENT/SERVER REDIRECT flags, otherwise mask out the
- // SERVER_REDIRECT flag only.
- page_transition = MaskOutPageTransition(
- page_transition, allow_client_redirect
- ? ui::PAGE_TRANSITION_IS_REDIRECT_MASK
- : ui::PAGE_TRANSITION_SERVER_REDIRECT);
-
- if (!ui::PageTransitionCoreTypeIs(page_transition,
- ui::PAGE_TRANSITION_LINK) &&
- !(allow_form_submit &&
- ui::PageTransitionCoreTypeIs(page_transition,
- ui::PAGE_TRANSITION_FORM_SUBMIT))) {
- // Do not handle the |url| if this event wasn't spawned by the user clicking
- // on a link.
- return true;
- }
-
- if (ui::PageTransitionGetQualifier(page_transition) != 0) {
- // Qualifiers indicate that this navigation was the result of a click on a
- // forward/back button, or typing in the URL bar. Don't handle any of those
- // types of navigations.
- return true;
- }
-
- return false;
-}
-
-ui::PageTransition MaskOutPageTransition(ui::PageTransition page_transition,
- ui::PageTransition mask) {
- return ui::PageTransitionFromInt(page_transition & ~mask);
-}
-
-} // namespace arc
diff --git a/chromium/components/arc/intent_helper/page_transition_util.h b/chromium/components/arc/intent_helper/page_transition_util.h
deleted file mode 100644
index d7e726af2e2..00000000000
--- a/chromium/components/arc/intent_helper/page_transition_util.h
+++ /dev/null
@@ -1,24 +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_ARC_INTENT_HELPER_PAGE_TRANSITION_UTIL_H_
-#define COMPONENTS_ARC_INTENT_HELPER_PAGE_TRANSITION_UTIL_H_
-
-#include "base/macros.h"
-#include "ui/base/page_transition_types.h"
-
-namespace arc {
-
-// Returns true if ARC should ignore the navigation with the |page_transition|.
-bool ShouldIgnoreNavigation(ui::PageTransition page_transition,
- bool allow_form_submit,
- bool allow_client_redirect);
-
-// Removes |mask| bits from |page_transition|.
-ui::PageTransition MaskOutPageTransition(ui::PageTransition page_transition,
- ui::PageTransition mask);
-
-} // namespace arc
-
-#endif // COMPONENTS_ARC_INTENT_HELPER_PAGE_TRANSITION_UTIL_H_
diff --git a/chromium/components/arc/intent_helper/page_transition_util_unittest.cc b/chromium/components/arc/intent_helper/page_transition_util_unittest.cc
deleted file mode 100644
index 8c1aeb10c35..00000000000
--- a/chromium/components/arc/intent_helper/page_transition_util_unittest.cc
+++ /dev/null
@@ -1,218 +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 <utility>
-
-#include "components/arc/intent_helper/page_transition_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/page_transition_types.h"
-
-namespace arc {
-
-// Tests that ShouldIgnoreNavigation returns false only for
-// PAGE_TRANSITION_LINK.
-TEST(PageTransitionUtilTest, TestShouldIgnoreNavigationWithCoreTypes) {
- EXPECT_FALSE(ShouldIgnoreNavigation(ui::PAGE_TRANSITION_LINK, false, false));
- EXPECT_FALSE(ShouldIgnoreNavigation(ui::PAGE_TRANSITION_LINK, true, true));
- EXPECT_TRUE(ShouldIgnoreNavigation(ui::PAGE_TRANSITION_TYPED, false, false));
- EXPECT_TRUE(ShouldIgnoreNavigation(ui::PAGE_TRANSITION_TYPED, true, true));
- EXPECT_TRUE(
- ShouldIgnoreNavigation(ui::PAGE_TRANSITION_AUTO_BOOKMARK, false, false));
- EXPECT_TRUE(
- ShouldIgnoreNavigation(ui::PAGE_TRANSITION_AUTO_BOOKMARK, true, true));
- EXPECT_TRUE(
- ShouldIgnoreNavigation(ui::PAGE_TRANSITION_AUTO_SUBFRAME, false, false));
- EXPECT_TRUE(
- ShouldIgnoreNavigation(ui::PAGE_TRANSITION_AUTO_SUBFRAME, true, true));
- EXPECT_TRUE(ShouldIgnoreNavigation(ui::PAGE_TRANSITION_MANUAL_SUBFRAME, false,
- false));
- EXPECT_TRUE(
- ShouldIgnoreNavigation(ui::PAGE_TRANSITION_MANUAL_SUBFRAME, true, true));
- EXPECT_TRUE(
- ShouldIgnoreNavigation(ui::PAGE_TRANSITION_GENERATED, false, false));
- EXPECT_TRUE(
- ShouldIgnoreNavigation(ui::PAGE_TRANSITION_GENERATED, true, true));
- EXPECT_TRUE(
- ShouldIgnoreNavigation(ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false, false));
- EXPECT_TRUE(
- ShouldIgnoreNavigation(ui::PAGE_TRANSITION_AUTO_TOPLEVEL, true, true));
- EXPECT_TRUE(
- ShouldIgnoreNavigation(ui::PAGE_TRANSITION_FORM_SUBMIT, false, false));
- EXPECT_FALSE(
- ShouldIgnoreNavigation(ui::PAGE_TRANSITION_FORM_SUBMIT, true, true));
- EXPECT_TRUE(ShouldIgnoreNavigation(ui::PAGE_TRANSITION_RELOAD, false, false));
- EXPECT_TRUE(ShouldIgnoreNavigation(ui::PAGE_TRANSITION_RELOAD, true, true));
- EXPECT_TRUE(
- ShouldIgnoreNavigation(ui::PAGE_TRANSITION_KEYWORD, false, false));
- EXPECT_TRUE(ShouldIgnoreNavigation(ui::PAGE_TRANSITION_KEYWORD, true, true));
- EXPECT_TRUE(ShouldIgnoreNavigation(ui::PAGE_TRANSITION_KEYWORD_GENERATED,
- false, false));
- EXPECT_TRUE(ShouldIgnoreNavigation(ui::PAGE_TRANSITION_KEYWORD_GENERATED,
- true, true));
-
- static_assert(static_cast<int32_t>(ui::PAGE_TRANSITION_KEYWORD_GENERATED) ==
- static_cast<int32_t>(ui::PAGE_TRANSITION_LAST_CORE),
- "Not all core transition types are covered here");
-}
-
-// Tests that ShouldIgnoreNavigation returns true when no qualifiers except
-// server redirect are provided.
-TEST(PageTransitionUtilTest, TestShouldIgnoreNavigationWithLinkWithQualifiers) {
- // The navigation is triggered by Forward or Back button.
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
- ui::PAGE_TRANSITION_FORWARD_BACK),
- false, false));
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORM_SUBMIT |
- ui::PAGE_TRANSITION_FORWARD_BACK),
- true, true));
- // The user used the address bar to triger the navigation.
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
- ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
- false, false));
- // The user pressed the Home button.
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
- ui::PAGE_TRANSITION_HOME_PAGE),
- false, false));
- // ARC (for example) opened the link in Chrome.
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
- ui::PAGE_TRANSITION_FROM_API),
- false, false));
- // The navigation is triggered by a client side redirect.
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
- ui::PAGE_TRANSITION_CLIENT_REDIRECT),
- false, false));
- // Also tests the case with 2+ qualifiers.
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
- ui::PAGE_TRANSITION_FROM_ADDRESS_BAR |
- ui::PAGE_TRANSITION_CLIENT_REDIRECT),
- false, false));
-}
-
-// Just in case, does the same with ui::PAGE_TRANSITION_TYPED.
-TEST(PageTransitionUtilTest,
- TestShouldIgnoreNavigationWithTypedWithQualifiers) {
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
- ui::PAGE_TRANSITION_FORWARD_BACK),
- false, false));
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
- ui::PAGE_TRANSITION_FORWARD_BACK),
- true, true));
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
- ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
- false, false));
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
- ui::PAGE_TRANSITION_HOME_PAGE),
- false, false));
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
- ui::PAGE_TRANSITION_FROM_API),
- false, false));
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
- ui::PAGE_TRANSITION_FROM_ADDRESS_BAR |
- ui::PAGE_TRANSITION_CLIENT_REDIRECT),
- false, false));
-}
-
-// Tests that ShouldIgnoreNavigation returns false if SERVER_REDIRECT is the
-// only qualifier given.
-TEST(PageTransitionUtilTest, TestShouldIgnoreNavigationWithServerRedirect) {
- EXPECT_FALSE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
- ui::PAGE_TRANSITION_SERVER_REDIRECT),
- false, false));
- EXPECT_FALSE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORM_SUBMIT |
- ui::PAGE_TRANSITION_SERVER_REDIRECT),
- true, true));
-
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
- ui::PAGE_TRANSITION_SERVER_REDIRECT |
- ui::PAGE_TRANSITION_FROM_API),
- false, false));
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORM_SUBMIT |
- ui::PAGE_TRANSITION_SERVER_REDIRECT |
- ui::PAGE_TRANSITION_FROM_API),
- true, true));
-}
-
-// Test that ShouldIgnoreNavigation accepts CLIENT_REDIRECT qualifier when
-// |allow_form_submit| equals true.
-TEST(PageTransitionUtilTest, TestShouldIgnoreNavigationWithClientRedirect) {
- EXPECT_FALSE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK), true, true));
- EXPECT_FALSE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
- ui::PAGE_TRANSITION_CLIENT_REDIRECT),
- true, true));
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
- ui::PAGE_TRANSITION_CLIENT_REDIRECT |
- ui::PAGE_TRANSITION_HOME_PAGE),
- true, true));
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
- ui::PAGE_TRANSITION_HOME_PAGE),
- true, true));
-}
-
-// Test that MaskOutPageTransition correctly remove a qualifier from a given
-// |page_transition|.
-TEST(PageTransitionUtilTest, TestMaskOutPageTransition) {
- ui::PageTransition page_transition = ui::PageTransitionFromInt(
- ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_CLIENT_REDIRECT);
- EXPECT_EQ(static_cast<int>(ui::PAGE_TRANSITION_LINK),
- static_cast<int>(MaskOutPageTransition(
- page_transition, ui::PAGE_TRANSITION_CLIENT_REDIRECT)));
-
- page_transition = ui::PageTransitionFromInt(
- ui::PAGE_TRANSITION_LINK | ui::PAGE_TRANSITION_SERVER_REDIRECT);
- EXPECT_EQ(static_cast<int>(ui::PAGE_TRANSITION_LINK),
- static_cast<int>(MaskOutPageTransition(
- page_transition, ui::PAGE_TRANSITION_SERVER_REDIRECT)));
-}
-
-// Test mixed variants between |allow_form_submit| and |allow_client_redirect|.
-TEST(PageTransitionUtilTest, TestShouldIgnoreNavigationWithNonProdScenarios) {
- EXPECT_FALSE(ShouldIgnoreNavigation(ui::PAGE_TRANSITION_LINK, true, false));
- EXPECT_FALSE(ShouldIgnoreNavigation(ui::PAGE_TRANSITION_LINK, false, true));
-
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
- ui::PAGE_TRANSITION_CLIENT_REDIRECT),
- true, false));
- EXPECT_FALSE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
- ui::PAGE_TRANSITION_CLIENT_REDIRECT),
- false, true));
-
- EXPECT_FALSE(
- ShouldIgnoreNavigation(ui::PAGE_TRANSITION_FORM_SUBMIT, true, false));
- EXPECT_TRUE(
- ShouldIgnoreNavigation(ui::PAGE_TRANSITION_FORM_SUBMIT, false, true));
-
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORM_SUBMIT |
- ui::PAGE_TRANSITION_CLIENT_REDIRECT),
- true, false));
- EXPECT_TRUE(ShouldIgnoreNavigation(
- ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORM_SUBMIT |
- ui::PAGE_TRANSITION_CLIENT_REDIRECT),
- false, true));
-}
-
-} // namespace arc
diff --git a/chromium/components/arc/metrics/arc_metrics_service_unittest.cc b/chromium/components/arc/metrics/arc_metrics_service_unittest.cc
index 49a291bdb3b..bee6e7e014f 100644
--- a/chromium/components/arc/metrics/arc_metrics_service_unittest.cc
+++ b/chromium/components/arc/metrics/arc_metrics_service_unittest.cc
@@ -12,7 +12,7 @@
#include "base/metrics/histogram_samples.h"
#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_session_manager_client.h"
#include "components/arc/arc_service_manager.h"
diff --git a/chromium/components/arc/midis/arc_midis_bridge.cc b/chromium/components/arc/midis/arc_midis_bridge.cc
index 5cc0ecfe9d6..86f797498e9 100644
--- a/chromium/components/arc/midis/arc_midis_bridge.cc
+++ b/chromium/components/arc/midis/arc_midis_bridge.cc
@@ -12,10 +12,8 @@
#include "chromeos/dbus/dbus_thread_manager.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/outgoing_broker_client_invitation.h"
-#include "mojo/edk/embedder/platform_channel_pair.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/public/cpp/platform/platform_channel.h"
+#include "mojo/public/cpp/system/invitation.h"
namespace arc {
namespace {
@@ -78,17 +76,13 @@ void ArcMidisBridge::Connect(mojom::MidisServerRequest request,
return;
}
DVLOG(1) << "Bootstrapping the Midis connection via D-Bus.";
- mojo::edk::OutgoingBrokerClientInvitation invitation;
- mojo::edk::PlatformChannelPair channel_pair;
+ mojo::OutgoingInvitation invitation;
+ mojo::PlatformChannel channel;
mojo::ScopedMessagePipeHandle server_pipe =
invitation.AttachMessagePipe("arc-midis-pipe");
- invitation.Send(
- base::kNullProcessHandle,
- mojo::edk::ConnectionParams(mojo::edk::TransportProtocol::kLegacy,
- channel_pair.PassServerHandle()));
- mojo::edk::ScopedInternalPlatformHandle child_handle =
- channel_pair.PassClientHandle();
- base::ScopedFD fd(child_handle.release().handle);
+ mojo::OutgoingInvitation::Send(std::move(invitation),
+ base::kNullProcessHandle,
+ channel.TakeLocalEndpoint());
midis_host_ptr_.Bind(
mojo::InterfacePtrInfo<mojom::MidisHost>(std::move(server_pipe), 0u));
@@ -99,7 +93,7 @@ void ArcMidisBridge::Connect(mojom::MidisServerRequest request,
chromeos::DBusThreadManager::Get()
->GetArcMidisClient()
->BootstrapMojoConnection(
- std::move(fd),
+ channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD(),
base::BindOnce(&ArcMidisBridge::OnBootstrapMojoConnection,
weak_factory_.GetWeakPtr(), std::move(request),
std::move(client_ptr)));
diff --git a/chromium/components/arc/power/arc_power_bridge.cc b/chromium/components/arc/power/arc_power_bridge.cc
index be74d85e165..7e7a2db3d64 100644
--- a/chromium/components/arc/power/arc_power_bridge.cc
+++ b/chromium/components/arc/power/arc_power_bridge.cc
@@ -139,7 +139,7 @@ ArcPowerBridge::~ArcPowerBridge() {
bool ArcPowerBridge::TriggerNotifyBrightnessTimerForTesting() {
if (!notify_brightness_timer_.IsRunning())
return false;
- notify_brightness_timer_.user_task().Run();
+ notify_brightness_timer_.FireNow();
return true;
}
diff --git a/chromium/components/arc/ime/OWNERS b/chromium/components/arc/timer/OWNERS
index ef5cf841917..ef5cf841917 100644
--- a/chromium/components/arc/ime/OWNERS
+++ b/chromium/components/arc/timer/OWNERS
diff --git a/chromium/components/arc/timer/arc_timer.cc b/chromium/components/arc/timer/arc_timer.cc
deleted file mode 100644
index 8f51afb6e25..00000000000
--- a/chromium/components/arc/timer/arc_timer.cc
+++ /dev/null
@@ -1,76 +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/arc/timer/arc_timer.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/posix/unix_domain_socket.h"
-#include "base/threading/thread_restrictions.h"
-
-namespace arc {
-
-ArcTimer::ArcTimer(base::ScopedFD expiration_fd,
- mojo::InterfaceRequest<mojom::Timer> request,
- ConnectionErrorCallback connection_error_callback)
- : binding_(this, std::move(request)),
- expiration_fd_(std::move(expiration_fd)),
- connection_error_callback_(std::move(connection_error_callback)) {
- // It's safe to pass |this| because the error handler will be reset when
- // the |binding_| is unbound or closed.
- binding_.set_connection_error_handler(
- base::BindOnce(&ArcTimer::HandleConnectionError, base::Unretained(this)));
-}
-
-ArcTimer::~ArcTimer() = default;
-
-void ArcTimer::Start(base::TimeTicks absolute_expiration_time,
- StartCallback callback) {
- // Start the timer to expire at |absolute_expiration_time|. This call
- // automatically overrides the previous timer set.
- //
- // If the firing time has expired then set the timer to expire
- // immediately.
- base::TimeTicks current_time_ticks = base::TimeTicks::Now();
- base::TimeDelta delay;
- if (absolute_expiration_time > current_time_ticks)
- delay = absolute_expiration_time - current_time_ticks;
- base::Time current_time = base::Time::Now();
- DVLOG(1) << "CurrentTime: " << current_time
- << " NextAlarmAt: " << current_time + delay;
- // It's safe to pass |this| because on destruction of this object the
- // timer is stopped and the callback can't be invoked.
- timer_.Start(
- FROM_HERE, delay,
- base::BindRepeating(&ArcTimer::OnExpiration, base::Unretained(this)));
- std::move(callback).Run(arc::mojom::ArcTimerResult::SUCCESS);
-}
-
-void ArcTimer::HandleConnectionError() {
- // Stop any pending timers when connection with the instance is dropped.
- timer_.Stop();
- std::move(connection_error_callback_).Run(this);
-}
-
-void ArcTimer::OnExpiration() {
- DVLOG(1) << "Expiration callback";
- base::AssertBlockingAllowed();
- // The instance expects 8 bytes on the read end similar to what happens on
- // a timerfd expiration. The timerfd API expects this to be the number of
- // expirations, however, more than one expiration isn't tracked currently.
- const uint64_t timer_data = 1;
- if (!base::UnixDomainSocket::SendMsg(expiration_fd_.get(), &timer_data,
- sizeof(timer_data),
- std::vector<int>())) {
- LOG(ERROR) << "Failed to indicate timer expiration to the instance";
- }
-}
-
-} // namespace arc
diff --git a/chromium/components/arc/timer/arc_timer.h b/chromium/components/arc/timer/arc_timer.h
deleted file mode 100644
index ad16408c64b..00000000000
--- a/chromium/components/arc/timer/arc_timer.h
+++ /dev/null
@@ -1,79 +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_ARC_TIMER_ARC_TIMER_H_
-#define COMPONENTS_ARC_TIMER_ARC_TIMER_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <set>
-
-#include "base/callback.h"
-#include "base/files/file.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/task_scheduler/post_task.h"
-#include "base/time/time.h"
-#include "components/arc/common/timer.mojom.h"
-#include "components/arc/timer/create_timer_request.h"
-#include "components/timers/alarm_timer_chromeos.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace arc {
-
-// Implements the timer interface exported to the instance. All accesses to an
-// instance of this class must happen on a single sequence that supports
-// blocking operations and the |base::FileDescriptorWatcher| API.
-//
-// In the current implementation, |ArcTimer| objects are bound to a mojo proxy
-// on a separate sequence in |arc_timer_bridge.cc|. Mojo guarantees to call
-// all callbacks on the sequence that the mojo::Binding was created on.
-// Consequently, even the timer expiration i.e. |OnExpiration| happens on the
-// sequence and is allowed to do I/O.
-class ArcTimer : public mojom::Timer {
- // Called when there is connection error on the mojo |binding_|. Called on the
- // the sequence this object was created on.
- using ConnectionErrorCallback = base::OnceCallback<void(ArcTimer* timer)>;
-
- public:
- ArcTimer(base::ScopedFD expiration_fd,
- mojo::InterfaceRequest<mojom::Timer> request,
- ConnectionErrorCallback connection_error_callback);
- ~ArcTimer() override;
-
- // mojom::Timer overrides. Runs on the sequence this object was created
- // on since this class is bound to a mojo proxy on a separate sequence.
- void Start(base::TimeTicks absolute_expiration_time,
- StartCallback callback) override;
-
- private:
- // Handles connection errors with the instance. Runs on the sequence this
- // object was created on.
- void HandleConnectionError();
-
- // Callback fired when the timer in |ArcTimer| expires. Runs on the sequence
- // this object was created on.
- void OnExpiration();
-
- mojo::Binding<mojom::Timer> binding_;
-
- // The timer that will be scheduled. Only accessed from the sequence on which
- // this object is created.
- timers::SimpleAlarmTimer timer_;
-
- // The file descriptor which will be written to when |timer| expires. Only
- // accessed from the sequence on which this object is created.
- base::ScopedFD expiration_fd_;
-
- ConnectionErrorCallback connection_error_callback_;
-
- DISALLOW_COPY_AND_ASSIGN(ArcTimer);
-};
-
-} // namespace arc
-
-#endif // COMPONENTS_ARC_TIMER_ARC_TIMER_H_
diff --git a/chromium/components/arc/timer/arc_timer_bridge.cc b/chromium/components/arc/timer/arc_timer_bridge.cc
index 9206c22e0cf..a2ce052fda9 100644
--- a/chromium/components/arc/timer/arc_timer_bridge.cc
+++ b/chromium/components/arc/timer/arc_timer_bridge.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 <utility>
+#include <set>
#include "base/containers/flat_set.h"
#include "base/logging.h"
@@ -10,17 +10,55 @@
#include "base/stl_util.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/power_manager_client.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "components/arc/arc_service_manager.h"
-#include "components/arc/timer/arc_timer.h"
#include "components/arc/timer/arc_timer_bridge.h"
-#include "components/arc/timer/arc_timer_traits.h"
+#include "components/arc/timer/arc_timer_struct_traits.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/platform_handle.h"
namespace arc {
namespace {
+// Tag to be used with the powerd timer API.
+constexpr char kTag[] = "ARC";
+
+mojom::ArcTimerResult ConvertBoolResultToMojo(bool result) {
+ return result ? mojom::ArcTimerResult::SUCCESS
+ : mojom::ArcTimerResult::FAILURE;
+}
+
+// Callback for powerd API called in |StartTimer|.
+void OnStartTimer(mojom::TimerHost::StartTimerCallback callback, bool result) {
+ std::move(callback).Run(ConvertBoolResultToMojo(result));
+}
+
+// Unwraps a mojo handle to a file descriptor on the system.
+base::ScopedFD UnwrapScopedHandle(mojo::ScopedHandle handle) {
+ base::PlatformFile platform_file;
+ if (mojo::UnwrapPlatformFile(std::move(handle), &platform_file) !=
+ MOJO_RESULT_OK) {
+ LOG(ERROR) << "Failed to unwrap mojo handle";
+ return base::ScopedFD();
+ }
+ return base::ScopedFD(platform_file);
+}
+
+// Returns true iff |arc_timer_requests| contains duplicate clock id values.
+bool ContainsDuplicateClocks(
+ const std::vector<arc::mojom::CreateTimerRequestPtr>& arc_timer_requests) {
+ std::set<clockid_t> seen_clock_ids;
+ for (const auto& request : arc_timer_requests) {
+ if (!seen_clock_ids.emplace(request->clock_id).second)
+ return true;
+ }
+ return false;
+}
+
// Singleton factory for ArcTimerBridge.
class ArcTimerBridgeFactory
: public internal::ArcBrowserContextKeyedServiceFactoryBase<
@@ -40,10 +78,6 @@ class ArcTimerBridgeFactory
~ArcTimerBridgeFactory() override = default;
};
-bool IsSupportedClock(int32_t clock_id) {
- return clock_id == CLOCK_BOOTTIME_ALARM || clock_id == CLOCK_REALTIME_ALARM;
-}
-
} // namespace
// static
@@ -65,9 +99,7 @@ ArcTimerBridge* ArcTimerBridge::GetForBrowserContextForTesting(
ArcTimerBridge::ArcTimerBridge(content::BrowserContext* context,
ArcBridgeService* bridge_service)
- : timer_task_runner_(
- base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})),
- arc_bridge_service_(bridge_service),
+ : arc_bridge_service_(bridge_service),
binding_(this),
weak_ptr_factory_(this) {
arc_bridge_service_->timer()->SetHost(this);
@@ -79,170 +111,137 @@ ArcTimerBridge::~ArcTimerBridge() {
arc_bridge_service_->timer()->SetHost(nullptr);
}
-void ArcTimerBridge::CreateTimers(
- std::vector<CreateTimerRequest> arc_timer_requests,
- CreateTimersCallback callback) {
- DVLOG(1) << "Received CreateTimers";
- // Alarm timers can't be created on the UI thread because they make syscalls
- // in the constructor. Post a task to create |ArcTimer| objects containing
- // alarm timer objects.
- base::PostTaskAndReplyWithResult(
- timer_task_runner_.get(), FROM_HERE,
- base::BindOnce(&CreateArcTimers, !arc_timers_.empty(),
- base::SequencedTaskRunnerHandle::Get(), timer_task_runner_,
- std::move(arc_timer_requests),
- weak_ptr_factory_.GetWeakPtr()),
- base::BindOnce(&ArcTimerBridge::OnArcTimersCreated,
- weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
void ArcTimerBridge::OnConnectionClosed() {
- DVLOG(1) << "OnConnectionClosed";
DeleteArcTimers();
}
-// Deleter for |ArcTimer|s. Deletes the timer object on |task_runner|.
-class ArcTimerBridge::DeleteOnSequence {
- public:
- explicit DeleteOnSequence(
- scoped_refptr<base::SequencedTaskRunner> task_runner)
- : task_runner_(std::move(task_runner)) {}
- ~DeleteOnSequence() = default;
- DeleteOnSequence(DeleteOnSequence&&) = default;
- DeleteOnSequence& operator=(DeleteOnSequence&&) = default;
-
- void operator()(ArcTimer* timer) const {
- task_runner_->DeleteSoon(FROM_HERE, timer);
+void ArcTimerBridge::CreateTimers(
+ std::vector<arc::mojom::CreateTimerRequestPtr> arc_timer_requests,
+ CreateTimersCallback callback) {
+ // Duplicate clocks are not allowed.
+ if (ContainsDuplicateClocks(arc_timer_requests)) {
+ std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
+ return;
}
- private:
- scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
- DISALLOW_COPY_AND_ASSIGN(DeleteOnSequence);
-};
-
-void ArcTimerBridge::OnConnectionError(ArcTimer* timer) {
- DVLOG(1) << "OnConnectionError";
- // At this point the mojo binding for |timer| has an error. Find and delete
- // the timer. Since the lifetime of |ArcTimer| objects is managed by this
- // class it is safe to compare |timer|.
- base::EraseIf(
- arc_timers_,
- [timer](const std::unique_ptr<ArcTimer, DeleteOnSequence>& element) {
- return element.get() == timer;
- });
-}
-
-// static.
-void ArcTimerBridge::OnConnectionErrorOnTimerThread(
- scoped_refptr<base::SequencedTaskRunner> task_runner,
- base::WeakPtr<ArcTimerBridge> weak_self,
- ArcTimer* timer) {
- // Post task on the main thread since the task will access class members.
- task_runner->PostTask(
- FROM_HERE,
- base::BindOnce(&ArcTimerBridge::OnConnectionError, weak_self, timer));
-}
-
-struct ArcTimerBridge::TimersAndProxies {
- TimersAndProxies() = default;
- ~TimersAndProxies() = default;
- TimersAndProxies(TimersAndProxies&&) = default;
- TimersAndProxies& operator=(TimersAndProxies&&) = default;
- std::vector<int32_t> clocks;
- std::vector<std::unique_ptr<ArcTimer, DeleteOnSequence>> timers;
- std::vector<mojom::TimerPtrInfo> proxies;
-};
-
-// static.
-base::Optional<ArcTimerBridge::TimersAndProxies>
-ArcTimerBridge::CreateArcTimers(
- bool timers_already_created,
- scoped_refptr<base::SequencedTaskRunner> original_task_runner,
- scoped_refptr<base::SequencedTaskRunner> timer_task_runner,
- std::vector<arc::CreateTimerRequest> arc_timer_requests,
- base::WeakPtr<ArcTimerBridge> weak_self) {
- if (timers_already_created) {
- LOG(ERROR) << "Double creation not supported";
- return base::nullopt;
+ // Convert mojo arguments to D-Bus arguments required by powerd to create
+ // timers.
+ std::vector<std::pair<clockid_t, base::ScopedFD>> requests;
+ for (auto& request : arc_timer_requests) {
+ clockid_t clock_id = request->clock_id;
+ base::ScopedFD expiration_fd =
+ UnwrapScopedHandle(std::move(request->expiration_fd));
+ if (!expiration_fd.is_valid()) {
+ LOG(ERROR) << "Unwrapped expiration fd is invalid for clock=" << clock_id;
+ std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
+ return;
+ }
+ requests.emplace_back(clock_id, std::move(expiration_fd));
}
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->DeleteArcTimers(
+ kTag, base::BindOnce(&ArcTimerBridge::OnDeleteBeforeCreateArcTimers,
+ weak_ptr_factory_.GetWeakPtr(), std::move(requests),
+ std::move(callback)));
+}
- // Iterate over the list of {clock_id, expiration_fd} and create an |ArcTimer|
- // and |mojom::TimerPtrInfo| entry for each clock.
- base::flat_set<int32_t> seen_clocks;
- ArcTimerBridge::TimersAndProxies result;
- for (auto& request : arc_timer_requests) {
- // Read each entry one by one. Each entry will have an |ArcTimer| entry
- // associated with it.
- int32_t clock_id = request.clock_id;
+void ArcTimerBridge::StartTimer(clockid_t clock_id,
+ base::TimeTicks absolute_expiration_time,
+ StartTimerCallback callback) {
+ auto timer_id = GetTimerId(clock_id);
+ if (!timer_id.has_value()) {
+ LOG(ERROR) << "Timer for clock=" << clock_id << " not created";
+ std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
+ return;
+ }
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->StartArcTimer(
+ timer_id.value(), absolute_expiration_time,
+ base::BindOnce(&OnStartTimer, std::move(callback)));
+}
- if (!IsSupportedClock(clock_id)) {
- LOG(ERROR) << "Unsupported clock=" << clock_id;
- return base::nullopt;
- }
+void ArcTimerBridge::DeleteArcTimers() {
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->DeleteArcTimers(
+ kTag, base::BindOnce(&ArcTimerBridge::OnDeleteArcTimers,
+ weak_ptr_factory_.GetWeakPtr()));
+}
- if (!seen_clocks.insert(clock_id).second) {
- LOG(ERROR) << "Duplicate clocks not supported";
- return base::nullopt;
- }
+void ArcTimerBridge::OnDeleteArcTimers(bool result) {
+ if (!result) {
+ LOG(ERROR) << "Delete timers failed";
+ return;
+ }
- base::ScopedFD expiration_fd = std::move(request.expiration_fd);
- if (!expiration_fd.is_valid()) {
- LOG(ERROR) << "Bad file descriptor for clock=" << clock_id;
- return base::nullopt;
- }
+ // If the delete call succeeded then delete any timer ids stored and make a
+ // create timers call.
+ DVLOG(1) << "Delete timers succeeded";
+ timer_ids_.clear();
+}
- mojom::TimerPtrInfo timer_proxy_info;
- // TODO(b/69759087): Make |ArcTimer| take |clock_id| to create timers of
- // different clock types.
- // The instance opens clocks of type CLOCK_BOOTTIME_ALARM and
- // CLOCK_REALTIME_ALARM. However, it uses only CLOCK_BOOTTIME_ALARM to set
- // wake up alarms. At this point, it's okay to pretend the host supports
- // CLOCK_REALTIME_ALARM instead of returning an error.
- //
- // Mojo guarantees to call all callbacks on the task runner that the
- // mojo::Binding i.e. |ArcTimer| was created on.
- result.clocks.push_back(clock_id);
- result.timers.push_back(std::unique_ptr<ArcTimer, DeleteOnSequence>(
- new ArcTimer(
- std::move(expiration_fd), mojo::MakeRequest(&timer_proxy_info),
- base::BindOnce(&ArcTimerBridge::OnConnectionErrorOnTimerThread,
- original_task_runner, weak_self)),
- DeleteOnSequence(timer_task_runner)));
- result.proxies.push_back(std::move(timer_proxy_info));
+void ArcTimerBridge::OnDeleteBeforeCreateArcTimers(
+ std::vector<std::pair<clockid_t, base::ScopedFD>>
+ create_arc_timers_requests,
+ CreateTimersCallback callback,
+ bool result) {
+ if (!result) {
+ LOG(ERROR) << "Delete timers before create failed";
+ std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
+ return;
}
- return result;
+
+ DVLOG(1) << "Delete before create timers succeeded";
+ // If the delete call succeeded then delete any timer ids stored and make a
+ // create timers call.
+ timer_ids_.clear();
+ std::vector<clockid_t> clock_ids;
+ for (const auto& request : create_arc_timers_requests)
+ clock_ids.emplace_back(request.first);
+
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->CreateArcTimers(
+ kTag, std::move(create_arc_timers_requests),
+ base::BindOnce(&ArcTimerBridge::OnCreateArcTimers,
+ weak_ptr_factory_.GetWeakPtr(), std::move(clock_ids),
+ std::move(callback)));
}
-void ArcTimerBridge::OnArcTimersCreated(
+void ArcTimerBridge::OnCreateArcTimers(
+ std::vector<clockid_t> clock_ids,
CreateTimersCallback callback,
- base::Optional<TimersAndProxies> timers_and_proxies) {
- if (timers_and_proxies == base::nullopt) {
- std::move(callback).Run(base::nullopt);
+ base::Optional<std::vector<TimerId>> timer_ids) {
+ // The API returns a list of timer ids corresponding to each clock in
+ // |clock_ids|.
+ if (!timer_ids.has_value()) {
+ LOG(ERROR) << "Create timers failed";
+ std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
return;
}
- DCHECK_EQ(timers_and_proxies->clocks.size(),
- timers_and_proxies->timers.size());
- DCHECK_EQ(timers_and_proxies->clocks.size(),
- timers_and_proxies->proxies.size());
- arc_timers_ = std::move(timers_and_proxies->timers);
-
- // Respond to instance with timer proxies.
- std::vector<mojom::CreateTimerResponsePtr> result;
- for (size_t i = 0; i < timers_and_proxies->clocks.size(); i++) {
- mojom::CreateTimerResponsePtr response = mojom::CreateTimerResponse::New();
- response->clock_id =
- mojo::EnumTraits<arc::mojom::ClockId, int32_t>::ToMojom(
- timers_and_proxies->clocks[i]);
- response->timer = std::move(timers_and_proxies->proxies[i]);
- result.push_back(std::move(response));
+
+ std::vector<TimerId> result = timer_ids.value();
+ if (result.size() != clock_ids.size()) {
+ std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
+ return;
+ }
+
+ // Map clock id values to timer ids.
+ auto timer_id_iter = result.begin();
+ for (clockid_t clock_id : clock_ids) {
+ DVLOG(1) << "Storing clock=" << clock_id << " timer id=" << *timer_id_iter;
+ if (!timer_ids_.emplace(clock_id, *timer_id_iter).second) {
+ // This should never happen as any collision should have been detected on
+ // the powerd side and it should have returned an error.
+ LOG(ERROR) << "Can't store clock=" << clock_id;
+ timer_ids_.clear();
+ std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
+ return;
+ }
+ timer_id_iter++;
}
- std::move(callback).Run(std::move(result));
+ std::move(callback).Run(mojom::ArcTimerResult::SUCCESS);
}
-void ArcTimerBridge::DeleteArcTimers() {
- // The timer objects are deleted on |timer_task_runner_|.
- arc_timers_.clear();
+base::Optional<ArcTimerBridge::TimerId> ArcTimerBridge::GetTimerId(
+ clockid_t clock_id) const {
+ auto it = timer_ids_.find(clock_id);
+ return (it == timer_ids_.end()) ? base::nullopt
+ : base::make_optional<TimerId>(it->second);
}
} // namespace arc
diff --git a/chromium/components/arc/timer/arc_timer_bridge.h b/chromium/components/arc/timer/arc_timer_bridge.h
index 57603a09f56..162941a4aab 100644
--- a/chromium/components/arc/timer/arc_timer_bridge.h
+++ b/chromium/components/arc/timer/arc_timer_bridge.h
@@ -5,17 +5,18 @@
#ifndef COMPONENTS_ARC_TIMER_ARC_TIMER_BRIDGE_H_
#define COMPONENTS_ARC_TIMER_ARC_TIMER_BRIDGE_H_
+#include <map>
#include <memory>
+#include <utility>
#include <vector>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/sequenced_task_runner.h"
+#include "base/time/time.h"
#include "components/arc/common/timer.mojom.h"
#include "components/arc/connection_observer.h"
-#include "components/arc/timer/arc_timer.h"
-#include "components/arc/timer/create_timer_request.h"
#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/binding.h"
@@ -34,6 +35,8 @@ class ArcTimerBridge : public KeyedService,
public ConnectionObserver<mojom::TimerInstance>,
public mojom::TimerHost {
public:
+ using TimerId = int32_t;
+
// Returns the factory instance for this class.
static BrowserContextKeyedServiceFactory* GetFactory();
@@ -52,52 +55,42 @@ class ArcTimerBridge : public KeyedService,
void OnConnectionClosed() override;
// mojom::TimerHost overrides.
- void CreateTimers(std::vector<CreateTimerRequest> arc_timer_requests,
- CreateTimersCallback callback) override;
+ void CreateTimers(
+ std::vector<arc::mojom::CreateTimerRequestPtr> arc_timer_requests,
+ CreateTimersCallback callback) override;
+ void StartTimer(clockid_t clock_id,
+ base::TimeTicks absolute_expiration_time,
+ StartTimerCallback callback) override;
private:
- class DeleteOnSequence;
- struct TimersAndProxies;
-
- // Deletes |timer| from |arc_timers_|.
- void OnConnectionError(ArcTimer* timer);
-
- // Callback invoked when an |ArcTimer| object has a connection error on it's
- // mojo binding. It schedules |OnConnectionErrror| on |task_runner| which
- // finally deletes |timer|. Since, |ArcTimerBridge| lives on the main thread
- // and this function runs on |timer_task_runner_| this function needs to be
- // static to be thread safe.
- static void OnConnectionErrorOnTimerThread(
- scoped_refptr<base::SequencedTaskRunner> task_runner,
- base::WeakPtr<ArcTimerBridge> weak_self,
- ArcTimer* timer);
-
- // Creates timers with the given arguments. Returns base::nullopt on failure.
- // On success, returns a non-empty vector of |TimersAndProxies| objects. Runs
- // on |timer_task_runner| and should only access thread-safe members of the
- // parent class. For this reason it is also a static member.
- static base::Optional<TimersAndProxies> CreateArcTimers(
- bool timers_already_created,
- scoped_refptr<base::SequencedTaskRunner> original_task_runner,
- scoped_refptr<base::SequencedTaskRunner> timer_task_runner,
- std::vector<arc::CreateTimerRequest> arc_timer_requests,
- base::WeakPtr<ArcTimerBridge> weak_self);
-
- // Callback for |CreateArcTimers|. Runs |callback| before exiting. Runs on the
- // main thread.
- void OnArcTimersCreated(CreateTimersCallback callback,
- base::Optional<TimersAndProxies> timers_and_proxies);
-
// Deletes all timers.
void DeleteArcTimers();
- // Task runner on which all |ArcTimer| related operations are scheduled.
- scoped_refptr<base::SequencedTaskRunner> timer_task_runner_;
+ // Callback for (powerd API) call made in |DeleteArcTimers|.
+ void OnDeleteArcTimers(bool result);
+
+ // Callback for delete timers (powerd API) call made in |CreateTimers|.
+ void OnDeleteBeforeCreateArcTimers(
+ std::vector<std::pair<clockid_t, base::ScopedFD>>
+ create_arc_timers_requests,
+ CreateTimersCallback callback,
+ bool result);
+
+ // Callback for powerd's D-Bus API called in |CreateTimers|.
+ void OnCreateArcTimers(std::vector<clockid_t> clock_ids,
+ CreateTimersCallback callback,
+ base::Optional<std::vector<TimerId>> timer_ids);
+
+ // Retrieves the timer id corresponding to |clock_id|. If a mapping exists in
+ // |timer_ids_| then returns an int32_t >= 0. Else returns base::nullopt.
+ base::Optional<TimerId> GetTimerId(clockid_t clock_id) const;
- ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
+ // Owned by ArcServiceManager.
+ ArcBridgeService* const arc_bridge_service_;
- // Store of |ArcTimer| objects.
- std::vector<std::unique_ptr<ArcTimer, DeleteOnSequence>> arc_timers_;
+ // Mapping of clock ids (coresponding to <sys/timerfd.h>) sent by the instance
+ // in |CreateTimers| to timer ids returned in |OnCreateArcTimersDBusMethod|.
+ std::map<clockid_t, TimerId> timer_ids_;
mojo::Binding<mojom::TimerHost> binding_;
diff --git a/chromium/components/arc/timer/arc_timer_bridge_unittest.cc b/chromium/components/arc/timer/arc_timer_bridge_unittest.cc
index f3f4fc89b6f..d91abf12cc5 100644
--- a/chromium/components/arc/timer/arc_timer_bridge_unittest.cc
+++ b/chromium/components/arc/timer/arc_timer_bridge_unittest.cc
@@ -15,6 +15,8 @@
#include "base/posix/unix_domain_socket.h"
#include "base/run_loop.h"
#include "base/time/time.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/power_manager_client.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/common/timer.mojom.h"
@@ -23,22 +25,74 @@
#include "components/arc/test/fake_timer_instance.h"
#include "components/arc/test/test_browser_context.h"
#include "components/arc/timer/arc_timer_bridge.h"
-#include "components/arc/timer/arc_timer_traits.h"
-#include "components/arc/timer/create_timer_request.h"
+#include "components/arc/timer/arc_timer_struct_traits.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
#include "content/public/test/test_browser_thread_bundle.h"
+#include "mojo/public/cpp/system/handle.h"
+#include "mojo/public/cpp/system/platform_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace arc {
namespace {
+// Converts a system file descriptor to a mojo handle that can be sent to the
+// host.
+mojo::ScopedHandle WrapPlatformFd(base::ScopedFD scoped_fd) {
+ mojo::ScopedHandle handle = mojo::WrapPlatformFile(scoped_fd.release());
+ if (!handle.is_valid()) {
+ LOG(ERROR) << "Failed to wrap platform handle";
+ return mojo::ScopedHandle();
+ }
+ return handle;
+}
+
+// Callback for D-Bus operations.
+void TimerOperationCallback(base::OnceClosure quit_callback,
+ bool* op_result,
+ mojom::ArcTimerResult result) {
+ *op_result = (result == mojom::ArcTimerResult::SUCCESS);
+ std::move(quit_callback).Run();
+}
+
+// Stores clock ids and their corresponding file descriptors. These file
+// descriptors indicate when a timer corresponding to the clock has expired on
+// a read.
+class ArcTimerStore {
+ public:
+ ArcTimerStore() = default;
+
+ bool AddTimer(clockid_t clock_id, base::ScopedFD read_fd) {
+ return arc_timers_.emplace(clock_id, std::move(read_fd)).second;
+ }
+
+ void ClearTimers() { return arc_timers_.clear(); }
+
+ base::Optional<int> GetTimerReadFd(clockid_t clock_id) {
+ if (!HasTimer(clock_id))
+ return base::nullopt;
+ return base::Optional<int>(arc_timers_[clock_id].get());
+ }
+
+ bool HasTimer(clockid_t clock_id) const {
+ auto it = arc_timers_.find(clock_id);
+ return it != arc_timers_.end() && it->second.is_valid();
+ }
+
+ private:
+ // Map of a clock id to read fd that is signalled when the timer corresponding
+ // the clock expires.
+ std::map<clockid_t, base::ScopedFD> arc_timers_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArcTimerStore);
+};
+
class ArcTimerTest : public testing::Test {
public:
ArcTimerTest()
- : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
- timer_bridge_(
- ArcTimerBridge::GetForBrowserContextForTesting(&context_)) {
+ : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {
+ chromeos::DBusThreadManager::Initialize();
+ timer_bridge_ = ArcTimerBridge::GetForBrowserContextForTesting(&context_);
// This results in ArcTimerBridge::OnInstanceReady being called.
ArcServiceManager::Get()->arc_bridge_service()->timer()->SetInstance(
&timer_instance_);
@@ -52,178 +106,130 @@ class ArcTimerTest : public testing::Test {
ArcServiceManager::Get()->arc_bridge_service()->timer()->CloseInstance(
&timer_instance_);
timer_bridge_->Shutdown();
+ chromeos::DBusThreadManager::Shutdown();
}
protected:
- FakeTimerInstance* GetFakeTimerInstance() { return &timer_instance_; }
+ // Returns true iff timer creation of each clock type succeeded.
+ bool CreateTimers(const std::vector<clockid_t>& clocks);
+
+ // Returns true iff a timer for |clock_id| is successfully scheduled.
+ bool StartTimer(clockid_t clock_id, base::TimeTicks absolute_expiration_time);
+
+ // Returns true iff the read descriptor of a timer is signalled. If the
+ // signalling is incorrect returns false. Blocks otherwise.
+ bool WaitForExpiration(clockid_t clock_id);
private:
+ // Stores |read_fds| corresponding to clock ids in |clocks| in
+ // |arc_timer_store_|.
+ bool StoreReadFds(const std::vector<clockid_t> clocks,
+ std::vector<base::ScopedFD> read_fds);
+
content::TestBrowserThreadBundle thread_bundle_;
ArcServiceManager arc_service_manager_;
TestBrowserContext context_;
FakeTimerInstance timer_instance_;
- ArcTimerBridge* const timer_bridge_;
+ ArcTimerStore arc_timer_store_;
+
+ ArcTimerBridge* timer_bridge_;
DISALLOW_COPY_AND_ASSIGN(ArcTimerTest);
};
-// Stores timer proxies associated with each clock's timer.
-class ArcTimerStore {
- public:
- ArcTimerStore() = default;
-
- bool AddTimer(clockid_t clock_id, base::ScopedFD read_fd) {
- ArcTimerStore::ArcTimerInfo arc_timer_info;
- arc_timer_info.read_fd = std::move(read_fd);
- auto emplace_result =
- arc_timers_.emplace(clock_id, std::move(arc_timer_info));
- if (!emplace_result.second)
- ADD_FAILURE() << "Failed to create timer entry";
- return emplace_result.second;
- }
-
- void ClearTimers() { return arc_timers_.clear(); }
-
- base::Optional<int> GetTimerReadFd(clockid_t clock_id) {
- if (!HasTimer(clock_id))
- return base::nullopt;
- return base::Optional<int>(arc_timers_[clock_id].read_fd.get());
- }
-
- mojom::TimerPtr* GetTimerProxy(clockid_t clock_id) {
- if (!HasTimer(clock_id))
- return nullptr;
- return &arc_timers_[clock_id].timer;
- }
-
- bool HasTimer(clockid_t clock_id) {
- auto it = arc_timers_.find(clock_id);
- return (it != arc_timers_.end() && it->second.timer);
- }
-
- bool SetTimerProxy(clockid_t clock_id, mojom::TimerPtrInfo timer) {
- // An entry for storing the timer proxy should already be present.
- auto it = arc_timers_.find(clock_id);
- if (it == arc_timers_.end())
+bool ArcTimerTest::StoreReadFds(const std::vector<clockid_t> clocks,
+ std::vector<base::ScopedFD> read_fds) {
+ auto read_fd_iter = read_fds.begin();
+ for (auto clock_id : clocks) {
+ // This should never fail because at this point timers have been created by
+ // powerd and |clocks| doesn't have any duplicate clock ids.
+ if (!arc_timer_store_.AddTimer(clock_id, std::move(*read_fd_iter))) {
+ LOG(ERROR) << "Error while adding clock=" << clock_id << " to store";
+ arc_timer_store_.ClearTimers();
return false;
- it->second.timer = mojom::TimerPtr(std::move(timer));
- return true;
- }
-
- size_t size() const { return arc_timers_.size(); }
-
- private:
- struct ArcTimerInfo {
- // The mojom::Timer associated with an arc timer.
- mojom::TimerPtr timer;
-
- // The fd that will be signalled by the host when the timer expires.
- base::ScopedFD read_fd;
- };
-
- std::map<clockid_t, ArcTimerInfo> arc_timers_;
-
- DISALLOW_COPY_AND_ASSIGN(ArcTimerStore);
-};
-
-// Stores timer proxies returned by |mojom::TimerHost::CreateTimers|. Iff timers
-// for all clocks are created successfully, then |arc_timer_store| will have
-// proxies corresponding to each clock.
-void CreateTimersCallback(
- ArcTimerStore* arc_timer_store,
- base::OnceClosure quit_callback,
- base::Optional<std::vector<mojom::CreateTimerResponsePtr>> result) {
- base::ScopedClosureRunner quit_runner(std::move(quit_callback));
-
- if (result == base::nullopt) {
- ADD_FAILURE() << "Null timer objects array returned";
- arc_timer_store->ClearTimers();
- return;
- }
-
- const size_t responses_size = result.value().size();
- if (responses_size != arc_timer_store->size()) {
- ADD_FAILURE() << "Incorrect number of timer objects returned: "
- << responses_size;
- arc_timer_store->ClearTimers();
- return;
- }
-
- // Store the timer objects. These will be retrieved to set timers.
- for (auto& response : result.value()) {
- int32_t clock_id;
- if (!mojo::EnumTraits<arc::mojom::ClockId, int32_t>::FromMojom(
- response->clock_id, &clock_id)) {
- ADD_FAILURE() << "Failed to convert mojo clock id: "
- << response->clock_id;
- arc_timer_store->ClearTimers();
- return;
}
- EXPECT_TRUE(
- arc_timer_store->SetTimerProxy(clock_id, std::move(response->timer)));
+ read_fd_iter++;
}
+ return true;
}
-// Returns true iff timer creation of each clock type succeeded.
-bool CreateTimers(const std::vector<clockid_t>& clocks,
- ArcTimerStore* arc_timer_store,
- FakeTimerInstance* timer_instance) {
+bool ArcTimerTest::CreateTimers(const std::vector<clockid_t>& clocks) {
// Create requests to create a timer for each clock.
- std::vector<CreateTimerRequest> arc_timer_requests;
- for (const clockid_t& clock : clocks) {
- CreateTimerRequest request;
- request.clock_id = clock;
- // Create a socket pair for each clock. One socket will be part of the mojo
- // argument and will be used by the host to indicate when the timer expires.
- // The other socket will be used to detect the expiration of the timer by
- // epolling and reading.
+ std::vector<mojom::CreateTimerRequestPtr> arc_timer_requests;
+ std::vector<base::ScopedFD> read_fds;
+ for (auto clock_id : clocks) {
+ mojom::CreateTimerRequestPtr request = mojom::CreateTimerRequest::New();
+ // Create a socket pair for each clock. One socket will be part of the
+ // mojo argument and will be used by the host to indicate when the timer
+ // expires. The other socket will be used to detect the expiration of the
+ // timer by epolling and reading.
base::ScopedFD read_fd;
base::ScopedFD write_fd;
if (!base::CreateSocketPair(&read_fd, &write_fd)) {
- ADD_FAILURE() << "Failed to create socket pair for ARC timers";
- return false;
- }
- request.expiration_fd = std::move(write_fd);
- // Create an entry for each clock in the store. The timer object will be
- // populated in the callback to the call to create timers.
- if (!arc_timer_store->AddTimer(clock, std::move(read_fd))) {
- ADD_FAILURE() << "Failed to create timer entry";
- arc_timer_store->ClearTimers();
+ LOG(ERROR) << "Failed to create socket pair for ARC timers";
return false;
}
+ request->clock_id = clock_id;
+ request->expiration_fd = WrapPlatformFd(std::move(write_fd));
arc_timer_requests.emplace_back(std::move(request));
+
+ read_fds.emplace_back(std::move(read_fd));
}
- // Call the host to create timers.
+
+ // Clear local test state before creating timers.
+ arc_timer_store_.ClearTimers();
+
+ // Call the host to create timers. Safe to use base::Unretained(this) as the
+ // class is guaranteed to exist for the duration of the test.
+ bool result;
base::RunLoop loop;
- timer_instance->CallCreateTimers(
+
+ timer_instance_.GetTimerHost()->CreateTimers(
std::move(arc_timer_requests),
- base::BindOnce(&CreateTimersCallback, arc_timer_store,
- loop.QuitClosure()));
+ base::BindOnce(&TimerOperationCallback, loop.QuitClosure(), &result));
loop.Run();
- // Check if each clock's timer is created successfully.
- for (const clockid_t clock : clocks) {
- if (!arc_timer_store->HasTimer(clock)) {
- ADD_FAILURE() << "Failed to create timer for clock:" << clock;
- return false;
- }
+ if (!result)
+ return false;
+
+ // If timer creation succeeded, store the read fds associated with each clock
+ // in the store. The read fd will be used to wait on for a timer expiration.
+ if (!StoreReadFds(clocks, std::move(read_fds))) {
+ return false;
}
+
return true;
}
-// Returns true iff the read descriptor of a timer is signalled. If the
-// signalling is incorrect returns false. Blocks otherwise.
-bool WaitForExpiration(clockid_t clock_id, ArcTimerStore* arc_timer_store) {
- if (!arc_timer_store->HasTimer(clock_id)) {
- ADD_FAILURE() << "Timer of type: " << clock_id << " not present";
+bool ArcTimerTest::StartTimer(clockid_t clock_id,
+ base::TimeTicks absolute_expiration_time) {
+ // Call the host to start a timer corresponding to |clock_id|. Safe to use
+ // base::Unretained(this) as the class is guaranteed to exist for the
+ // duration of the test.
+ base::RunLoop loop;
+ bool result;
+ timer_instance_.GetTimerHost()->StartTimer(
+ clock_id, absolute_expiration_time,
+ base::BindOnce(&TimerOperationCallback, loop.QuitClosure(), &result));
+ loop.Run();
+ return result;
+}
+
+bool ArcTimerTest::WaitForExpiration(clockid_t clock_id) {
+ if (!arc_timer_store_.HasTimer(clock_id)) {
+ LOG(ERROR) << "Timer of clock=" << clock_id << " not present";
return false;
}
// Wait for the host to indicate expiration by watching the read end of the
// socket pair.
base::Optional<int> timer_read_fd_opt =
- arc_timer_store->GetTimerReadFd(clock_id);
- EXPECT_NE(timer_read_fd_opt, base::nullopt);
+ arc_timer_store_.GetTimerReadFd(clock_id);
+ // This should never happen if the timer was present in the store.
+ if (!timer_read_fd_opt.has_value()) {
+ ADD_FAILURE() << "Clock=" << clock_id << " read fd not found";
+ return false;
+ }
int timer_read_fd = timer_read_fd_opt.value();
base::RunLoop loop;
std::unique_ptr<base::FileDescriptorWatcher::Controller>
@@ -231,42 +237,57 @@ bool WaitForExpiration(clockid_t clock_id, ArcTimerStore* arc_timer_store) {
timer_read_fd, loop.QuitClosure());
loop.Run();
- // The timer expects 8 bytes to be written from the host upon expiration.
- uint64_t timer_data;
+ // The timer expects 8 bytes to be written from the host upon expiration and
+ // the number of expirations to be 1.
+ uint64_t num_expirations;
std::vector<base::ScopedFD> fds;
ssize_t bytes_read = base::UnixDomainSocket::RecvMsg(
- timer_read_fd, &timer_data, sizeof(timer_data), &fds);
- if (bytes_read < static_cast<ssize_t>(sizeof(timer_data))) {
- ADD_FAILURE() << "Incorrect timer wake up bytes_read: " << bytes_read;
+ timer_read_fd, &num_expirations, sizeof(num_expirations), &fds);
+ if (bytes_read < static_cast<ssize_t>(sizeof(num_expirations))) {
+ LOG(ERROR) << "Incorrect timer wake up bytes_read=" << bytes_read;
return false;
}
- LOG(INFO) << "Actual expiration time: " << base::Time::Now();
+ EXPECT_EQ(num_expirations, 1ULL);
+
+ // TODO(fdoray): Remove this hack once WatchReadable fixes crbug.com/74118.
+ // This is required for |watch_readable_controller| to clean up properly.
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
return true;
}
-TEST_F(ArcTimerTest, StartTimer) {
+TEST_F(ArcTimerTest, StartTimerTest) {
std::vector<clockid_t> clocks = {CLOCK_REALTIME_ALARM, CLOCK_BOOTTIME_ALARM};
- ArcTimerStore arc_timer_store;
// Create timers before starting it.
- EXPECT_TRUE(CreateTimers(clocks, &arc_timer_store, GetFakeTimerInstance()));
- mojom::TimerPtr* timer = arc_timer_store.GetTimerProxy(CLOCK_BOOTTIME_ALARM);
- ASSERT_TRUE(timer);
- ASSERT_TRUE(*timer);
+ EXPECT_TRUE(CreateTimers(clocks));
// Start timer and check if timer expired.
base::TimeDelta delay = base::TimeDelta::FromMilliseconds(20);
- base::RunLoop loop;
- LOG(INFO) << "Start time: " << base::Time::Now()
- << " Expiration time: " << base::Time::Now() + delay;
- (*timer)->Start(base::TimeTicks::Now() + delay,
- base::BindOnce(
- [](base::RunLoop* loop, mojom::ArcTimerResult result) {
- if (result != mojom::ArcTimerResult::SUCCESS)
- ADD_FAILURE() << "Start timer failed";
- loop->Quit();
- },
- &loop));
- loop.Run();
- EXPECT_TRUE(WaitForExpiration(CLOCK_BOOTTIME_ALARM, &arc_timer_store));
+ EXPECT_TRUE(StartTimer(CLOCK_BOOTTIME_ALARM, base::TimeTicks::Now() + delay));
+ EXPECT_TRUE(WaitForExpiration(CLOCK_BOOTTIME_ALARM));
+}
+
+TEST_F(ArcTimerTest, InvalidCreateTimersArgsTest) {
+ std::vector<clockid_t> clocks = {CLOCK_REALTIME_ALARM, CLOCK_BOOTTIME_ALARM,
+ CLOCK_BOOTTIME_ALARM};
+ // Timers with duplicate clock ids shouldn't succeed.
+ EXPECT_FALSE(CreateTimers(clocks));
+}
+
+TEST_F(ArcTimerTest, InvalidStartTimerArgsTest) {
+ std::vector<clockid_t> clocks = {CLOCK_REALTIME_ALARM};
+ EXPECT_TRUE(CreateTimers(clocks));
+ // Start timer should fail due to un-registered clock id.
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(20);
+ EXPECT_FALSE(
+ StartTimer(CLOCK_BOOTTIME_ALARM, base::TimeTicks::Now() + delay));
+}
+
+TEST_F(ArcTimerTest, CheckMultipleCreateTimersTest) {
+ std::vector<clockid_t> clocks = {CLOCK_REALTIME_ALARM};
+ EXPECT_TRUE(CreateTimers(clocks));
+ // The create implementation calls powerd's delete API before calling create.
+ // The second create should thus succeed.
+ EXPECT_TRUE(CreateTimers(clocks));
}
} // namespace
diff --git a/chromium/components/arc/timer/arc_timer_struct_traits.cc b/chromium/components/arc/timer/arc_timer_struct_traits.cc
new file mode 100644
index 00000000000..77c37ae8eff
--- /dev/null
+++ b/chromium/components/arc/timer/arc_timer_struct_traits.cc
@@ -0,0 +1,40 @@
+// 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/arc/timer/arc_timer_struct_traits.h"
+
+#include <utility>
+
+namespace mojo {
+
+// static
+arc::mojom::ClockId EnumTraits<arc::mojom::ClockId, clockid_t>::ToMojom(
+ clockid_t clock_id) {
+ switch (clock_id) {
+ case CLOCK_REALTIME_ALARM:
+ return arc::mojom::ClockId::REALTIME_ALARM;
+ case CLOCK_BOOTTIME_ALARM:
+ return arc::mojom::ClockId::BOOTTIME_ALARM;
+ }
+ NOTREACHED();
+ return arc::mojom::ClockId::BOOTTIME_ALARM;
+}
+
+// static
+bool EnumTraits<arc::mojom::ClockId, clockid_t>::FromMojom(
+ arc::mojom::ClockId input,
+ clockid_t* output) {
+ switch (input) {
+ case arc::mojom::ClockId::REALTIME_ALARM:
+ *output = CLOCK_REALTIME_ALARM;
+ return true;
+ case arc::mojom::ClockId::BOOTTIME_ALARM:
+ *output = CLOCK_BOOTTIME_ALARM;
+ return true;
+ }
+ NOTREACHED();
+ return false;
+}
+
+} // namespace mojo
diff --git a/chromium/components/arc/timer/arc_timer_struct_traits.h b/chromium/components/arc/timer/arc_timer_struct_traits.h
new file mode 100644
index 00000000000..40adac5a39c
--- /dev/null
+++ b/chromium/components/arc/timer/arc_timer_struct_traits.h
@@ -0,0 +1,22 @@
+// 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_ARC_TIMER_ARC_TIMER_STRUCT_TRAITS_H_
+#define COMPONENTS_ARC_TIMER_ARC_TIMER_STRUCT_TRAITS_H_
+
+#include <time.h>
+
+#include "components/arc/common/timer.mojom.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<arc::mojom::ClockId, clockid_t> {
+ static arc::mojom::ClockId ToMojom(clockid_t clock_id);
+ static bool FromMojom(arc::mojom::ClockId input, clockid_t* output);
+};
+
+} // namespace mojo
+
+#endif // COMPONENTS_ARC_TIMER_ARC_TIMER_STRUCT_TRAITS_H_
diff --git a/chromium/components/arc/timer/arc_timer_traits.cc b/chromium/components/arc/timer/arc_timer_traits.cc
deleted file mode 100644
index d2feb07cd89..00000000000
--- a/chromium/components/arc/timer/arc_timer_traits.cc
+++ /dev/null
@@ -1,97 +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 <utility>
-
-#include "base/posix/unix_domain_socket.h"
-#include "components/arc/timer/arc_timer_traits.h"
-#include "mojo/public/cpp/system/handle.h"
-#include "mojo/public/cpp/system/platform_handle.h"
-
-namespace {
-
-// Unwraps a mojo handle to a file descriptor on the system.
-base::ScopedFD UnwrapPlatformHandle(mojo::ScopedHandle handle) {
- base::PlatformFile platform_file;
- if (mojo::UnwrapPlatformFile(std::move(handle), &platform_file) !=
- MOJO_RESULT_OK) {
- LOG(ERROR) << "Failed to unwrap mojo handle";
- return base::ScopedFD();
- }
- return base::ScopedFD(platform_file);
-}
-
-// Converts a system file descriptor to a mojo handle that can be sent to the
-// host.
-mojo::ScopedHandle WrapPlatformFd(base::ScopedFD scoped_fd) {
- mojo::ScopedHandle handle = mojo::WrapPlatformFile(scoped_fd.release());
- if (!handle.is_valid()) {
- LOG(ERROR) << "Failed to wrap platform handle";
- return mojo::ScopedHandle();
- }
- return handle;
-}
-
-} // namespace
-
-namespace mojo {
-
-// static
-arc::mojom::ClockId EnumTraits<arc::mojom::ClockId, int32_t>::ToMojom(
- int32_t clock_id) {
- switch (clock_id) {
- case CLOCK_REALTIME_ALARM:
- return arc::mojom::ClockId::REALTIME_ALARM;
- case CLOCK_BOOTTIME_ALARM:
- return arc::mojom::ClockId::BOOTTIME_ALARM;
- }
- NOTREACHED();
- return arc::mojom::ClockId::BOOTTIME_ALARM;
-}
-
-// static
-bool EnumTraits<arc::mojom::ClockId, int32_t>::FromMojom(
- arc::mojom::ClockId input,
- int32_t* output) {
- switch (input) {
- case arc::mojom::ClockId::REALTIME_ALARM:
- *output = CLOCK_REALTIME_ALARM;
- return true;
- case arc::mojom::ClockId::BOOTTIME_ALARM:
- *output = CLOCK_BOOTTIME_ALARM;
- return true;
- }
- NOTREACHED();
- return false;
-}
-
-// static
-arc::mojom::ClockId
-StructTraits<arc::mojom::CreateTimerRequestDataView, arc::CreateTimerRequest>::
- clock_id(const arc::CreateTimerRequest& arc_timer_request) {
- return EnumTraits<arc::mojom::ClockId, int32_t>::ToMojom(
- arc_timer_request.clock_id);
-}
-
-// static
-mojo::ScopedHandle
-StructTraits<arc::mojom::CreateTimerRequestDataView, arc::CreateTimerRequest>::
- expiration_fd(arc::CreateTimerRequest& arc_timer_request) {
- return WrapPlatformFd(std::move(arc_timer_request.expiration_fd));
-}
-
-// static
-bool StructTraits<
- arc::mojom::CreateTimerRequestDataView,
- arc::CreateTimerRequest>::Read(arc::mojom::CreateTimerRequestDataView input,
- arc::CreateTimerRequest* output) {
- if (!EnumTraits<arc::mojom::ClockId, int32_t>::FromMojom(input.clock_id(),
- &output->clock_id)) {
- return false;
- }
- output->expiration_fd = UnwrapPlatformHandle(input.TakeExpirationFd());
- return true;
-}
-
-} // namespace mojo
diff --git a/chromium/components/arc/timer/arc_timer_traits.h b/chromium/components/arc/timer/arc_timer_traits.h
deleted file mode 100644
index 252791ecdaa..00000000000
--- a/chromium/components/arc/timer/arc_timer_traits.h
+++ /dev/null
@@ -1,36 +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_ARC_TIMER_ARC_TIMER_TRAITS_H_
-#define COMPONENTS_ARC_TIMER_ARC_TIMER_TRAITS_H_
-
-#include "components/arc/common/timer.mojom.h"
-#include "components/arc/timer/create_timer_request.h"
-
-namespace mojo {
-
-template <>
-struct EnumTraits<arc::mojom::ClockId, int32_t> {
- static arc::mojom::ClockId ToMojom(int32_t clock_id);
- static bool FromMojom(arc::mojom::ClockId input, int32_t* output);
-};
-
-template <>
-struct StructTraits<arc::mojom::CreateTimerRequestDataView,
- arc::CreateTimerRequest> {
- // Due to already defined EnumTraits for |ClockId| the return type is int32_t
- // and not |arc::mojom::ClockId|.
- static arc::mojom::ClockId clock_id(
- const arc::CreateTimerRequest& arc_timer_request);
-
- static mojo::ScopedHandle expiration_fd(
- arc::CreateTimerRequest& arc_timer_request);
-
- static bool Read(arc::mojom::CreateTimerRequestDataView input,
- arc::CreateTimerRequest* output);
-};
-
-} // namespace mojo
-
-#endif // COMPONENTS_ARC_TIMER_ARC_TIMER_TRAITS_H_
diff --git a/chromium/components/arc/timer/create_timer_request.cc b/chromium/components/arc/timer/create_timer_request.cc
deleted file mode 100644
index 95ca1fd8449..00000000000
--- a/chromium/components/arc/timer/create_timer_request.cc
+++ /dev/null
@@ -1,13 +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/arc/timer/create_timer_request.h"
-
-namespace arc {
-
-CreateTimerRequest::CreateTimerRequest() = default;
-CreateTimerRequest::CreateTimerRequest(CreateTimerRequest&&) = default;
-CreateTimerRequest::~CreateTimerRequest() = default;
-
-} // namespace arc
diff --git a/chromium/components/arc/timer/create_timer_request.h b/chromium/components/arc/timer/create_timer_request.h
deleted file mode 100644
index 252e5cca506..00000000000
--- a/chromium/components/arc/timer/create_timer_request.h
+++ /dev/null
@@ -1,28 +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_ARC_TIMER_CREATE_TIMER_REQUEST_H_
-#define COMPONENTS_ARC_TIMER_CREATE_TIMER_REQUEST_H_
-
-#include <stdint.h>
-
-#include "base/files/scoped_file.h"
-
-// Typemapping for |CreateTimerRequest| in timer.mojom
-namespace arc {
-
-struct CreateTimerRequest {
- CreateTimerRequest();
- CreateTimerRequest(CreateTimerRequest&&);
- ~CreateTimerRequest();
-
- int32_t clock_id;
- base::ScopedFD expiration_fd;
-
- DISALLOW_COPY_AND_ASSIGN(CreateTimerRequest);
-};
-
-} // namespace arc
-
-#endif // COMPONENTS_ARC_TIMER_CREATE_TIMER_REQUEST_H_
diff --git a/chromium/components/arc/usb/usb_host_bridge.cc b/chromium/components/arc/usb/usb_host_bridge.cc
index b88ab1a9e30..860c29c5b77 100644
--- a/chromium/components/arc/usb/usb_host_bridge.cc
+++ b/chromium/components/arc/usb/usb_host_bridge.cc
@@ -19,8 +19,7 @@
#include "device/usb/mojo/type_converters.h"
#include "device/usb/usb_device_handle.h"
#include "device/usb/usb_device_linux.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/public/cpp/system/platform_handle.h"
namespace arc {
namespace {
@@ -51,18 +50,14 @@ void OnDeviceOpened(mojom::UsbHostHost::OpenDeviceCallback callback,
std::move(callback).Run(mojo::ScopedHandle());
return;
}
- mojo::edk::ScopedInternalPlatformHandle platform_handle{
- mojo::edk::InternalPlatformHandle(fd.release())};
- MojoHandle wrapped_handle;
- MojoResult wrap_result = mojo::edk::CreateInternalPlatformHandleWrapper(
- std::move(platform_handle), &wrapped_handle);
- if (wrap_result != MOJO_RESULT_OK) {
- LOG(ERROR) << "Failed to wrap device FD. Closing: " << wrap_result;
+ mojo::ScopedHandle wrapped_handle =
+ mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(fd)));
+ if (!wrapped_handle.is_valid()) {
+ LOG(ERROR) << "Failed to wrap device FD. Closing.";
std::move(callback).Run(mojo::ScopedHandle());
return;
}
- mojo::ScopedHandle scoped_handle{mojo::Handle(wrapped_handle)};
- std::move(callback).Run(std::move(scoped_handle));
+ std::move(callback).Run(std::move(wrapped_handle));
}
void OnDeviceOpenError(mojom::UsbHostHost::OpenDeviceCallback callback,
@@ -223,8 +218,8 @@ void ArcUsbHostBridge::OnDeviceRemoved(
return;
}
- usb_host_instance->OnDeviceRemoved(
- device.get()->guid(), GetEventReceiverPackages(device.get()->guid()));
+ usb_host_instance->OnDeviceRemoved(device.get()->guid(),
+ GetEventReceiverPackages(device));
if (ui_delegate_)
ui_delegate_->DeviceRemoved(device.get()->guid());
@@ -260,10 +255,9 @@ void ArcUsbHostBridge::SetUiDelegate(ArcUsbHostUiDelegate* ui_delegate) {
}
std::vector<std::string> ArcUsbHostBridge::GetEventReceiverPackages(
- const std::string& guid) {
- scoped_refptr<device::UsbDevice> device = usb_service_->GetDevice(guid);
- if (!device.get()) {
- LOG(WARNING) << "Unknown USB device " << guid;
+ scoped_refptr<device::UsbDevice> device) {
+ if (!device) {
+ LOG(WARNING) << "Unknown USB device.";
return std::vector<std::string>();
}
@@ -271,7 +265,8 @@ std::vector<std::string> ArcUsbHostBridge::GetEventReceiverPackages(
return std::vector<std::string>();
std::unordered_set<std::string> receivers = ui_delegate_->GetEventPackageList(
- guid, device->serial_number(), device->vendor_id(), device->product_id());
+ device->guid(), device->serial_number(), device->vendor_id(),
+ device->product_id());
return std::vector<std::string>(receivers.begin(), receivers.end());
}
@@ -288,10 +283,11 @@ void ArcUsbHostBridge::OnDeviceChecked(const std::string& guid, bool allowed) {
mojom::UsbHostInstance* usb_host_instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service_->usb_host(), OnDeviceAdded);
- if (!usb_host_instance)
+ if (!usb_host_instance || !usb_service_)
return;
- usb_host_instance->OnDeviceAdded(guid, GetEventReceiverPackages(guid));
+ usb_host_instance->OnDeviceAdded(
+ guid, GetEventReceiverPackages(usb_service_->GetDevice(guid)));
}
void ArcUsbHostBridge::DoRequestUserAuthorization(
diff --git a/chromium/components/arc/usb/usb_host_bridge.h b/chromium/components/arc/usb/usb_host_bridge.h
index b74b6f8eb96..35e87b8f3e2 100644
--- a/chromium/components/arc/usb/usb_host_bridge.h
+++ b/chromium/components/arc/usb/usb_host_bridge.h
@@ -74,7 +74,8 @@ class ArcUsbHostBridge : public KeyedService,
void SetUiDelegate(ArcUsbHostUiDelegate* ui_delegate);
private:
- std::vector<std::string> GetEventReceiverPackages(const std::string& guid);
+ std::vector<std::string> GetEventReceiverPackages(
+ scoped_refptr<device::UsbDevice> device);
void OnDeviceChecked(const std::string& guid, bool allowed);
void DoRequestUserAuthorization(const std::string& guid,
const std::string& package,
diff --git a/chromium/components/arc/video_accelerator/DEPS b/chromium/components/arc/video_accelerator/DEPS
index be1c9c99ce2..a3bfb623815 100644
--- a/chromium/components/arc/video_accelerator/DEPS
+++ b/chromium/components/arc/video_accelerator/DEPS
@@ -1,11 +1,10 @@
include_rules = [
"+components/arc/common",
- "+gpu/command_buffer/service/gpu_preferences.h",
+ "+gpu/config/gpu_preferences.h",
"+media/video",
"+media/base/video_frame.h",
"+media/base/video_types.h",
"+media/gpu",
- "+mojo/edk/embedder",
"+services/service_manager/public/cpp",
"+ui/gfx",
"+ui/ozone/public",
diff --git a/chromium/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h b/chromium/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h
index 2b881c7af89..889176ee61f 100644
--- a/chromium/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h
+++ b/chromium/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h
@@ -14,7 +14,7 @@
#include "base/files/scoped_file.h"
#include "base/threading/thread_checker.h"
#include "components/arc/common/video_decode_accelerator.mojom.h"
-#include "gpu/command_buffer/service/gpu_preferences.h"
+#include "gpu/config/gpu_preferences.h"
#include "media/video/video_decode_accelerator.h"
namespace arc {
diff --git a/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc b/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
index 5e8eaf867ae..1f9c88f0e7f 100644
--- a/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
+++ b/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
@@ -55,15 +55,14 @@ void GpuArcVideoEncodeAccelerator::RequireBitstreamBuffers(
void GpuArcVideoEncodeAccelerator::BitstreamBufferReady(
int32_t bitstream_buffer_id,
- size_t payload_size,
- bool key_frame,
- base::TimeDelta timestamp) {
+ const media::BitstreamBufferMetadata& metadata) {
DVLOGF(2) << "id=" << bitstream_buffer_id;
DCHECK(client_);
auto iter = use_bitstream_cbs_.find(bitstream_buffer_id);
DCHECK(iter != use_bitstream_cbs_.end());
std::move(iter->second)
- .Run(payload_size, key_frame, timestamp.InMicroseconds());
+ .Run(metadata.payload_size_bytes, metadata.key_frame,
+ metadata.timestamp.InMicroseconds());
use_bitstream_cbs_.erase(iter);
}
diff --git a/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h b/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h
index 8e344e9e9ef..ac9e2ab0794 100644
--- a/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h
+++ b/chromium/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.h
@@ -13,7 +13,7 @@
#include "base/macros.h"
#include "components/arc/common/video_encode_accelerator.mojom.h"
#include "components/arc/video_accelerator/video_frame_plane.h"
-#include "gpu/command_buffer/service/gpu_preferences.h"
+#include "gpu/config/gpu_preferences.h"
#include "media/video/video_encode_accelerator.h"
namespace arc {
@@ -38,10 +38,9 @@ class GpuArcVideoEncodeAccelerator
void RequireBitstreamBuffers(unsigned int input_count,
const gfx::Size& input_coded_size,
size_t output_buffer_size) override;
- void BitstreamBufferReady(int32_t bitstream_buffer_id,
- size_t payload_size,
- bool key_frame,
- base::TimeDelta timestamp) override;
+ void BitstreamBufferReady(
+ int32_t bitstream_buffer_id,
+ const media::BitstreamBufferMetadata& metadata) override;
void NotifyError(Error error) override;
// ::arc::mojom::VideoEncodeAccelerator implementation.
diff --git a/chromium/components/arc/volume_mounter/OWNERS b/chromium/components/arc/volume_mounter/OWNERS
new file mode 100644
index 00000000000..ef5cf841917
--- /dev/null
+++ b/chromium/components/arc/volume_mounter/OWNERS
@@ -0,0 +1,3 @@
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *_struct_traits*.*=file://components/arc/common/ARC_SECURITY_OWNERS
diff --git a/chromium/components/arc/volume_mounter/volume_mounter_traits.cc b/chromium/components/arc/volume_mounter/volume_mounter_struct_traits.cc
index c98edf382fb..b516fd79d4d 100644
--- a/chromium/components/arc/volume_mounter/volume_mounter_traits.cc
+++ b/chromium/components/arc/volume_mounter/volume_mounter_struct_traits.cc
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/arc/volume_mounter/volume_mounter_traits.h"
+#include "components/arc/volume_mounter/volume_mounter_struct_traits.h"
+
+#include "base/logging.h"
namespace mojo {
diff --git a/chromium/components/arc/volume_mounter/volume_mounter_traits.h b/chromium/components/arc/volume_mounter/volume_mounter_struct_traits.h
index 974d66bcdbc..e235db29637 100644
--- a/chromium/components/arc/volume_mounter/volume_mounter_traits.h
+++ b/chromium/components/arc/volume_mounter/volume_mounter_struct_traits.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_ARC_VOLUME_MOUNTER_VOLUME_MOUNTER_TRAITS_H_
-#define COMPONENTS_ARC_VOLUME_MOUNTER_VOLUME_MOUNTER_TRAITS_H_
+#ifndef COMPONENTS_ARC_VOLUME_MOUNTER_VOLUME_MOUNTER_STRUCT_TRAITS_H_
+#define COMPONENTS_ARC_VOLUME_MOUNTER_VOLUME_MOUNTER_STRUCT_TRAITS_H_
#include "chromeos/disks/disk_mount_manager.h"
#include "components/arc/common/volume_mounter.mojom.h"
@@ -28,4 +28,4 @@ struct EnumTraits<arc::mojom::MountEvent,
} // namespace mojo
-#endif // COMPONENTS_ARC_VOLUME_MOUNTER_VOLUME_MOUNTER_TRAITS_H_
+#endif // COMPONENTS_ARC_VOLUME_MOUNTER_VOLUME_MOUNTER_STRUCT_TRAITS_H_
diff --git a/chromium/components/assist_ranker/BUILD.gn b/chromium/components/assist_ranker/BUILD.gn
index 130553d6959..f41a45fc2f3 100644
--- a/chromium/components/assist_ranker/BUILD.gn
+++ b/chromium/components/assist_ranker/BUILD.gn
@@ -39,6 +39,9 @@ static_library("assist_ranker") {
"//components/keyed_service/core",
"//net",
"//services/metrics/public/cpp:metrics_cpp",
+ "//services/network:network_service",
+ "//services/network/public/cpp",
+ "//services/network/public/mojom",
"//url",
]
}
@@ -62,6 +65,8 @@ source_set("unit_tests") {
"//components/assist_ranker/proto",
"//components/ukm:test_support",
"//net:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//testing/gtest",
]
}
diff --git a/chromium/components/assist_ranker/DEPS b/chromium/components/assist_ranker/DEPS
index 22c088fe59f..f1b4b559e6e 100644
--- a/chromium/components/assist_ranker/DEPS
+++ b/chromium/components/assist_ranker/DEPS
@@ -5,5 +5,8 @@ include_rules = [
"+components/ukm",
"+net",
"+services/metrics/public",
+ "+services/network/public/cpp",
+ "+services/network/public/mojom",
+ "+services/network/test",
"+third_party/protobuf",
]
diff --git a/chromium/components/assist_ranker/assist_ranker_service_impl.cc b/chromium/components/assist_ranker/assist_ranker_service_impl.cc
index 5b870b8f578..04ea3cea1c5 100644
--- a/chromium/components/assist_ranker/assist_ranker_service_impl.cc
+++ b/chromium/components/assist_ranker/assist_ranker_service_impl.cc
@@ -6,15 +6,15 @@
#include "base/memory/weak_ptr.h"
#include "components/assist_ranker/binary_classifier_predictor.h"
#include "components/assist_ranker/ranker_model_loader_impl.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "url/gurl.h"
namespace assist_ranker {
AssistRankerServiceImpl::AssistRankerServiceImpl(
base::FilePath base_path,
- net::URLRequestContextGetter* url_request_context_getter)
- : url_request_context_getter_(url_request_context_getter),
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+ : url_loader_factory_(std::move(url_loader_factory)),
base_path_(std::move(base_path)) {}
AssistRankerServiceImpl::~AssistRankerServiceImpl() {}
@@ -35,7 +35,7 @@ AssistRankerServiceImpl::FetchBinaryClassifierPredictor(
DVLOG(1) << "Initializing predictor: " << model_name;
std::unique_ptr<BinaryClassifierPredictor> predictor =
BinaryClassifierPredictor::Create(config, GetModelPath(model_name),
- url_request_context_getter_.get());
+ url_loader_factory_);
base::WeakPtr<BinaryClassifierPredictor> weak_ptr =
base::AsWeakPtr(predictor.get());
predictor_map_[model_name] = std::move(predictor);
diff --git a/chromium/components/assist_ranker/assist_ranker_service_impl.h b/chromium/components/assist_ranker/assist_ranker_service_impl.h
index 1b75ff4017f..e1b00c284a9 100644
--- a/chromium/components/assist_ranker/assist_ranker_service_impl.h
+++ b/chromium/components/assist_ranker/assist_ranker_service_impl.h
@@ -15,8 +15,8 @@
#include "components/assist_ranker/assist_ranker_service.h"
#include "components/assist_ranker/predictor_config.h"
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
}
namespace assist_ranker {
@@ -28,7 +28,7 @@ class AssistRankerServiceImpl : public AssistRankerService {
public:
AssistRankerServiceImpl(
base::FilePath base_path,
- net::URLRequestContextGetter* url_request_context_getter);
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
~AssistRankerServiceImpl() override;
// AssistRankerService...
@@ -39,8 +39,8 @@ class AssistRankerServiceImpl : public AssistRankerService {
// Returns the full path to the model cache.
base::FilePath GetModelPath(const std::string& model_filename);
- // Request Context Getter used for RankerURLFetcher.
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+ // URL loader factory used for RankerURLFetcher.
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// Base path where models are stored.
const base::FilePath base_path_;
diff --git a/chromium/components/assist_ranker/base_predictor_unittest.cc b/chromium/components/assist_ranker/base_predictor_unittest.cc
index aae82621b11..5b770768e98 100644
--- a/chromium/components/assist_ranker/base_predictor_unittest.cc
+++ b/chromium/components/assist_ranker/base_predictor_unittest.cc
@@ -100,8 +100,8 @@ std::unique_ptr<FakePredictor> FakePredictor::Create() {
class BasePredictorTest : public ::testing::Test {
protected:
BasePredictorTest() = default;
- // Disables Query for the test predictor.
- void DisableQuery();
+
+ void SetUp() override;
ukm::SourceId GetSourceId();
@@ -120,16 +120,17 @@ class BasePredictorTest : public ::testing::Test {
DISALLOW_COPY_AND_ASSIGN(BasePredictorTest);
};
+void BasePredictorTest::SetUp() {
+ ::testing::Test::SetUp();
+ scoped_feature_list_.Init();
+}
+
ukm::SourceId BasePredictorTest::GetSourceId() {
ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID();
test_ukm_recorder_.UpdateSourceURL(source_id, GURL(kTestNavigationUrl));
return source_id;
}
-void BasePredictorTest::DisableQuery() {
- scoped_feature_list_.InitWithFeatures({}, {kTestRankerQuery});
-}
-
TEST_F(BasePredictorTest, BaseTest) {
auto predictor = FakePredictor::Create();
EXPECT_EQ(kTestModelName, predictor->GetModelName());
@@ -139,7 +140,8 @@ TEST_F(BasePredictorTest, BaseTest) {
}
TEST_F(BasePredictorTest, QueryDisabled) {
- DisableQuery();
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(kTestRankerQuery);
auto predictor = FakePredictor::Create();
EXPECT_EQ(kTestModelName, predictor->GetModelName());
EXPECT_EQ(kTestDefaultModelUrl, predictor->GetModelUrl());
diff --git a/chromium/components/assist_ranker/binary_classifier_predictor.cc b/chromium/components/assist_ranker/binary_classifier_predictor.cc
index 1200cfec46d..651eaab8b40 100644
--- a/chromium/components/assist_ranker/binary_classifier_predictor.cc
+++ b/chromium/components/assist_ranker/binary_classifier_predictor.cc
@@ -13,7 +13,7 @@
#include "components/assist_ranker/proto/ranker_model.pb.h"
#include "components/assist_ranker/ranker_model.h"
#include "components/assist_ranker/ranker_model_loader_impl.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace assist_ranker {
@@ -26,7 +26,7 @@ BinaryClassifierPredictor::~BinaryClassifierPredictor(){};
std::unique_ptr<BinaryClassifierPredictor> BinaryClassifierPredictor::Create(
const PredictorConfig& config,
const base::FilePath& model_path,
- net::URLRequestContextGetter* request_context_getter) {
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
std::unique_ptr<BinaryClassifierPredictor> predictor(
new BinaryClassifierPredictor(config));
if (!predictor->is_query_enabled()) {
@@ -40,7 +40,7 @@ std::unique_ptr<BinaryClassifierPredictor> BinaryClassifierPredictor::Create(
base::BindRepeating(&BinaryClassifierPredictor::ValidateModel),
base::BindRepeating(&BinaryClassifierPredictor::OnModelAvailable,
base::Unretained(predictor.get())),
- request_context_getter, model_path, model_url, config.uma_prefix);
+ url_loader_factory, model_path, model_url, config.uma_prefix);
predictor->LoadModel(std::move(model_loader));
return predictor;
}
diff --git a/chromium/components/assist_ranker/binary_classifier_predictor.h b/chromium/components/assist_ranker/binary_classifier_predictor.h
index e932c91388f..6aa1a9f1031 100644
--- a/chromium/components/assist_ranker/binary_classifier_predictor.h
+++ b/chromium/components/assist_ranker/binary_classifier_predictor.h
@@ -13,8 +13,8 @@ namespace base {
class FilePath;
}
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
}
namespace assist_ranker {
@@ -32,7 +32,8 @@ class BinaryClassifierPredictor : public BasePredictor {
static std::unique_ptr<BinaryClassifierPredictor> Create(
const PredictorConfig& config,
const base::FilePath& model_path,
- net::URLRequestContextGetter* request_context_getter) WARN_UNUSED_RESULT;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+ WARN_UNUSED_RESULT;
// Fills in a boolean decision given a RankerExample. Returns false if a
// prediction could not be made (e.g. the model is not loaded yet).
diff --git a/chromium/components/assist_ranker/binary_classifier_predictor_unittest.cc b/chromium/components/assist_ranker/binary_classifier_predictor_unittest.cc
index dcde981bf87..672c80fadf0 100644
--- a/chromium/components/assist_ranker/binary_classifier_predictor_unittest.cc
+++ b/chromium/components/assist_ranker/binary_classifier_predictor_unittest.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/feature_list.h"
+#include "base/test/scoped_feature_list.h"
#include "components/assist_ranker/fake_ranker_model_loader.h"
#include "components/assist_ranker/proto/ranker_model.pb.h"
#include "components/assist_ranker/ranker_model.h"
@@ -20,6 +21,8 @@ using ::assist_ranker::testing::FakeRankerModelLoader;
class BinaryClassifierPredictorTest : public ::testing::Test {
public:
+ void SetUp() override;
+
std::unique_ptr<BinaryClassifierPredictor> InitPredictor(
std::unique_ptr<RankerModel> ranker_model,
const PredictorConfig& config);
@@ -33,8 +36,14 @@ class BinaryClassifierPredictorTest : public ::testing::Test {
const std::string feature_ = "feature";
const float weight_ = 1.0;
const float threshold_ = 0.5;
+ base::test::ScopedFeatureList scoped_feature_list_;
};
+void BinaryClassifierPredictorTest::SetUp() {
+ ::testing::Test::SetUp();
+ scoped_feature_list_.Init();
+}
+
std::unique_ptr<BinaryClassifierPredictor>
BinaryClassifierPredictorTest::InitPredictor(
std::unique_ptr<RankerModel> ranker_model,
diff --git a/chromium/components/assist_ranker/predictor_config_definitions.cc b/chromium/components/assist_ranker/predictor_config_definitions.cc
index 879306d539d..e967f682f36 100644
--- a/chromium/components/assist_ranker/predictor_config_definitions.cc
+++ b/chromium/components/assist_ranker/predictor_config_definitions.cc
@@ -36,6 +36,8 @@ const base::flat_set<std::string>* GetContextualSearchFeatureWhitelist() {
static auto* kContextualSearchFeatureWhitelist =
new base::flat_set<std::string>({"DidOptIn",
"DurationAfterScrollMs",
+ "EntityImpressionsCount",
+ "EntityOpensCount",
"FontSize",
"IsEntity",
"IsEntityEligible",
@@ -45,6 +47,7 @@ const base::flat_set<std::string>* GetContextualSearchFeatureWhitelist() {
"IsSecondTapOverride",
"IsShortWord",
"IsWordEdge",
+ "OpenCount",
"OutcomeRankerDidPredict",
"OutcomeRankerPrediction",
"OutcomeWasCardsDataShown",
@@ -56,7 +59,12 @@ const base::flat_set<std::string>* GetContextualSearchFeatureWhitelist() {
"Previous28DayImpressionsCount",
"PreviousWeekCtrPercent",
"PreviousWeekImpressionsCount",
+ "QuickActionImpressionsCount",
+ "QuickActionsIgnored",
+ "QuickActionsTaken",
+ "QuickAnswerCount",
"ScreenTopDps",
+ "TapCount",
"TapDurationMs",
"WasScreenBottom"});
return kContextualSearchFeatureWhitelist;
diff --git a/chromium/components/assist_ranker/ranker_model_loader_impl.cc b/chromium/components/assist_ranker/ranker_model_loader_impl.cc
index e03529f3038..4159f7fd85f 100644
--- a/chromium/components/assist_ranker/ranker_model_loader_impl.cc
+++ b/chromium/components/assist_ranker/ranker_model_loader_impl.cc
@@ -22,6 +22,7 @@
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/assist_ranker/proto/ranker_model.pb.h"
#include "components/assist_ranker/ranker_url_fetcher.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace assist_ranker {
namespace {
@@ -90,7 +91,7 @@ void SaveToFile(const GURL& model_url,
RankerModelLoaderImpl::RankerModelLoaderImpl(
ValidateModelCallback validate_model_cb,
OnModelAvailableCallback on_model_available_cb,
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
base::FilePath model_path,
GURL model_url,
std::string uma_prefix)
@@ -99,7 +100,7 @@ RankerModelLoaderImpl::RankerModelLoaderImpl(
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
validate_model_cb_(std::move(validate_model_cb)),
on_model_available_cb_(std::move(on_model_available_cb)),
- request_context_getter_(request_context_getter),
+ url_loader_factory_(std::move(url_loader_factory)),
model_path_(std::move(model_path)),
model_url_(std::move(model_url)),
uma_prefix_(std::move(uma_prefix)),
@@ -147,9 +148,9 @@ void RankerModelLoaderImpl::StartLoadFromFile() {
load_start_time_ = base::TimeTicks::Now();
base::PostTaskAndReplyWithResult(
background_task_runner_.get(), FROM_HERE,
- base::Bind(&LoadFromFile, model_path_),
- base::Bind(&RankerModelLoaderImpl::OnFileLoaded,
- weak_ptr_factory_.GetWeakPtr()));
+ base::BindOnce(&LoadFromFile, model_path_),
+ base::BindOnce(&RankerModelLoaderImpl::OnFileLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
}
void RankerModelLoaderImpl::OnFileLoaded(const std::string& data) {
@@ -220,9 +221,9 @@ void RankerModelLoaderImpl::StartLoadFromURL() {
load_start_time_ + base::TimeDelta::FromMinutes(kMinRetryDelayMins);
bool request_started =
url_fetcher_->Request(model_url_,
- base::Bind(&RankerModelLoaderImpl::OnURLFetched,
- weak_ptr_factory_.GetWeakPtr()),
- request_context_getter_.get());
+ base::BindOnce(&RankerModelLoaderImpl::OnURLFetched,
+ weak_ptr_factory_.GetWeakPtr()),
+ url_loader_factory_.get());
// |url_fetcher_| maintains a request retry counter. If all allowed attempts
// have already been exhausted, then the loader is finished and has abandoned
diff --git a/chromium/components/assist_ranker/ranker_model_loader_impl.h b/chromium/components/assist_ranker/ranker_model_loader_impl.h
index 4ef151c3374..1a94ac3f4ea 100644
--- a/chromium/components/assist_ranker/ranker_model_loader_impl.h
+++ b/chromium/components/assist_ranker/ranker_model_loader_impl.h
@@ -16,13 +16,16 @@
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
-#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"
namespace base {
class SequencedTaskRunner;
} // namespace base
+namespace network {
+class SharedURLLoaderFactory;
+}
+
namespace assist_ranker {
class RankerURLFetcher;
@@ -48,12 +51,13 @@ class RankerModelLoaderImpl : public RankerModelLoader {
//
// |uma_prefix| will be used as a prefix for the names of all UMA metrics
// generated by this loader.
- RankerModelLoaderImpl(ValidateModelCallback validate_model_callback,
- OnModelAvailableCallback on_model_available_callback,
- net::URLRequestContextGetter* request_context_getter,
- base::FilePath model_path,
- GURL model_url,
- std::string uma_prefix);
+ RankerModelLoaderImpl(
+ ValidateModelCallback validate_model_callback,
+ OnModelAvailableCallback on_model_available_callback,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ base::FilePath model_path,
+ GURL model_url,
+ std::string uma_prefix);
~RankerModelLoaderImpl() override;
@@ -130,8 +134,8 @@ class RankerModelLoaderImpl : public RankerModelLoader {
// constructed.
const OnModelAvailableCallback on_model_available_cb_;
- // Request Context Getter used for RankerURLFetcher.
- scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+ // URL loader factory used for RankerURLFetcher.
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// The path at which the model is (or should be) cached.
const base::FilePath model_path_;
diff --git a/chromium/components/assist_ranker/ranker_model_loader_impl_unittest.cc b/chromium/components/assist_ranker/ranker_model_loader_impl_unittest.cc
index 9799c9ceb5c..32b2760400a 100644
--- a/chromium/components/assist_ranker/ranker_model_loader_impl_unittest.cc
+++ b/chromium/components/assist_ranker/ranker_model_loader_impl_unittest.cc
@@ -20,8 +20,9 @@
#include "components/assist_ranker/proto/ranker_model.pb.h"
#include "components/assist_ranker/proto/translate_ranker_model.pb.h"
#include "components/assist_ranker/ranker_model.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.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_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -76,15 +77,13 @@ class RankerModelLoaderImplTest : public ::testing::Test {
// Sets up the task scheduling/task-runner environment for each test.
base::test::ScopedTaskEnvironment scoped_task_environment_;
- // Override the default URL fetcher to return custom responses for tests.
- net::FakeURLFetcherFactory url_fetcher_factory_;
+ // Override the default URL loader to return custom responses for tests.
+ network::TestURLLoaderFactory test_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
// Temporary directory for model files.
base::ScopedTempDir scoped_temp_dir_;
- // Used for URLFetcher.
- scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
-
// A queue of responses to return from Validate(). If empty, validate will
// return 'OK'.
base::circular_deque<RankerModelStatus> validate_model_response_;
@@ -114,13 +113,13 @@ class RankerModelLoaderImplTest : public ::testing::Test {
DISALLOW_COPY_AND_ASSIGN(RankerModelLoaderImplTest);
};
-RankerModelLoaderImplTest::RankerModelLoaderImplTest()
- : url_fetcher_factory_(nullptr) {}
+RankerModelLoaderImplTest::RankerModelLoaderImplTest() {
+ test_shared_loader_factory_ =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_loader_factory_);
+}
void RankerModelLoaderImplTest::SetUp() {
- request_context_getter_ =
- new net::TestURLRequestContextGetter(base::ThreadTaskRunnerHandle::Get());
-
ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
const auto& temp_dir_path = scoped_temp_dir_.GetPath();
@@ -172,7 +171,7 @@ bool RankerModelLoaderImplTest::DoLoaderTest(const base::FilePath& model_path,
base::Unretained(this)),
base::Bind(&RankerModelLoaderImplTest::OnModelAvailable,
base::Unretained(this)),
- request_context_getter_.get(), model_path, model_url,
+ test_shared_loader_factory_, model_path, model_url,
"RankerModelLoaderImplTest");
loader->NotifyOfRankerActivity();
scoped_task_environment_.RunUntilIdle();
@@ -182,15 +181,13 @@ bool RankerModelLoaderImplTest::DoLoaderTest(const base::FilePath& model_path,
void RankerModelLoaderImplTest::InitRemoteModels() {
InitModel(remote_model_url_, base::Time(), base::TimeDelta(), &remote_model_);
- url_fetcher_factory_.SetFakeResponse(
- remote_model_url_, remote_model_.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- url_fetcher_factory_.SetFakeResponse(invalid_model_url_, kInvalidModelData,
- net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- url_fetcher_factory_.SetFakeResponse(failed_model_url_, "",
- net::HTTP_INTERNAL_SERVER_ERROR,
- net::URLRequestStatus::FAILED);
+ test_loader_factory_.AddResponse(remote_model_url_.spec(),
+ remote_model_.SerializeAsString());
+ test_loader_factory_.AddResponse(invalid_model_url_.spec(),
+ kInvalidModelData);
+ test_loader_factory_.AddResponse(
+ failed_model_url_, network::ResourceResponseHead(), "",
+ network::URLLoaderCompletionStatus(net::HTTP_INTERNAL_SERVER_ERROR));
}
void RankerModelLoaderImplTest::InitLocalModels() {
diff --git a/chromium/components/assist_ranker/ranker_url_fetcher.cc b/chromium/components/assist_ranker/ranker_url_fetcher.cc
index 0f8989e0973..8d0f709499b 100644
--- a/chromium/components/assist_ranker/ranker_url_fetcher.cc
+++ b/chromium/components/assist_ranker/ranker_url_fetcher.cc
@@ -9,8 +9,9 @@
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_status.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
namespace assist_ranker {
@@ -21,14 +22,15 @@ const int kMaxRetry = 16;
} // namespace
-RankerURLFetcher::RankerURLFetcher() : state_(IDLE), retry_count_(0) {}
+RankerURLFetcher::RankerURLFetcher()
+ : state_(IDLE), retry_count_(0), max_retry_on_5xx_(0) {}
RankerURLFetcher::~RankerURLFetcher() {}
bool RankerURLFetcher::Request(
const GURL& url,
- const RankerURLFetcher::Callback& callback,
- net::URLRequestContextGetter* request_context_getter) {
+ RankerURLFetcher::Callback callback,
+ network::mojom::URLLoaderFactory* url_loader_factory) {
// This function is not supposed to be called if the previous operation is not
// finished.
if (state_ == REQUESTING) {
@@ -42,9 +44,9 @@ bool RankerURLFetcher::Request(
state_ = REQUESTING;
url_ = url;
- callback_ = callback;
+ callback_ = std::move(callback);
- if (request_context_getter == nullptr)
+ if (url_loader_factory == nullptr)
return false;
net::NetworkTrafficAnnotationTag traffic_annotation =
@@ -73,39 +75,38 @@ bool RankerURLFetcher::Request(
policy_exception_justification:
"Not implemented, considered not necessary as no user data is sent."
})");
- // Create and initialize the URL fetcher.
- fetcher_ = net::URLFetcher::Create(url_, net::URLFetcher::GET, this,
- traffic_annotation);
- data_use_measurement::DataUseUserData::AttachToFetcher(
- fetcher_.get(),
- data_use_measurement::DataUseUserData::MACHINE_INTELLIGENCE);
- fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES);
- fetcher_->SetRequestContext(request_context_getter);
-
- // Set retry parameter for HTTP status code 5xx. This doesn't work against
- // 106 (net::ERR_INTERNET_DISCONNECTED) and so on.
- fetcher_->SetMaxRetriesOn5xx(max_retry_on_5xx_);
- fetcher_->Start();
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = url_;
+ resource_request->load_flags =
+ net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
+ // TODO(https://crbug.com/808498): Re-add data use measurement once
+ // SimpleURLLoader supports it.
+ // ID=data_use_measurement::DataUseUserData::MACHINE_INTELLIGENCE
+ simple_url_loader_ = network::SimpleURLLoader::Create(
+ std::move(resource_request), traffic_annotation);
+ if (max_retry_on_5xx_ > 0) {
+ simple_url_loader_->SetRetryOptions(max_retry_on_5xx_,
+ network::SimpleURLLoader::RETRY_ON_5XX);
+ }
+ simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory,
+ base::BindOnce(&RankerURLFetcher::OnSimpleLoaderComplete,
+ base::Unretained(this)));
return true;
}
-void RankerURLFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK(fetcher_.get() == source);
-
+void RankerURLFetcher::OnSimpleLoaderComplete(
+ std::unique_ptr<std::string> response_body) {
std::string data;
- if (source->GetStatus().status() == net::URLRequestStatus::SUCCESS &&
- source->GetResponseCode() == net::HTTP_OK) {
+ if (response_body) {
state_ = COMPLETED;
- source->GetResponseAsString(&data);
+ data = std::move(*response_body);
} else {
state_ = FAILED;
}
-
- // Transfer URLFetcher's ownership before invoking a callback.
- std::unique_ptr<const net::URLFetcher> delete_ptr(fetcher_.release());
- callback_.Run(state_ == COMPLETED, data);
+ simple_url_loader_.reset();
+ std::move(callback_).Run(state_ == COMPLETED, data);
}
} // namespace assist_ranker
diff --git a/chromium/components/assist_ranker/ranker_url_fetcher.h b/chromium/components/assist_ranker/ranker_url_fetcher.h
index 7c2305a9800..4f22b221174 100644
--- a/chromium/components/assist_ranker/ranker_url_fetcher.h
+++ b/chromium/components/assist_ranker/ranker_url_fetcher.h
@@ -9,17 +9,22 @@
#include "base/callback.h"
#include "base/macros.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"
+namespace network {
+class SimpleURLLoader;
+namespace mojom {
+class URLLoaderFactory;
+} // namespace mojom
+} // namespace network
+
namespace assist_ranker {
// Downloads Ranker models.
-class RankerURLFetcher : public net::URLFetcherDelegate {
+class RankerURLFetcher {
public:
// Callback type for Request().
- typedef base::Callback<void(bool, const std::string&)> Callback;
+ typedef base::OnceCallback<void(bool, const std::string&)> Callback;
// Represents internal state if the fetch is completed successfully.
enum State {
@@ -30,7 +35,7 @@ class RankerURLFetcher : public net::URLFetcherDelegate {
};
RankerURLFetcher();
- ~RankerURLFetcher() override;
+ ~RankerURLFetcher();
int max_retry_on_5xx() { return max_retry_on_5xx_; }
void set_max_retry_on_5xx(int count) { max_retry_on_5xx_ = count; }
@@ -40,24 +45,23 @@ class RankerURLFetcher : public net::URLFetcherDelegate {
// Returns false if the previous request is not finished, or the request
// is omitted due to retry limitation.
bool Request(const GURL& url,
- const Callback& callback,
- net::URLRequestContextGetter* request_context);
+ Callback callback,
+ network::mojom::URLLoaderFactory* url_loader_factory);
// Gets internal state.
State state() { return state_; }
- // net::URLFetcherDelegate implementation:
- void OnURLFetchComplete(const net::URLFetcher* source) override;
-
private:
+ void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
+
// URL to send the request.
GURL url_;
// Internal state.
enum State state_;
- // URLFetcher instance.
- std::unique_ptr<net::URLFetcher> fetcher_;
+ // SimpleURLLoader instance.
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
// Callback passed at Request(). It will be invoked when an asynchronous
// fetch operation is finished.
diff --git a/chromium/components/autofill/OWNERS b/chromium/components/autofill/OWNERS
index 4afcbe94aa4..d07c416b44f 100644
--- a/chromium/components/autofill/OWNERS
+++ b/chromium/components/autofill/OWNERS
@@ -1,3 +1,4 @@
+dvadym@chromium.org
estade@chromium.org
mathp@chromium.org
rogerm@chromium.org
diff --git a/chromium/components/autofill/android/BUILD.gn b/chromium/components/autofill/android/BUILD.gn
index 27eaab47d7d..fea72723c6e 100644
--- a/chromium/components/autofill/android/BUILD.gn
+++ b/chromium/components/autofill/android/BUILD.gn
@@ -62,6 +62,12 @@ android_resources("autofill_java_resources") {
]
}
+java_cpp_enum("autofill_core_browser_java_enums") {
+ sources = [
+ "../core/browser/popup_item_ids.h",
+ ]
+}
+
android_library("autofill_java") {
deps = [
":autofill_java_resources",
@@ -75,9 +81,10 @@ android_library("autofill_java") {
"java/src/org/chromium/components/autofill/AutofillPopup.java",
"java/src/org/chromium/components/autofill/AutofillSuggestion.java",
]
+ srcjar_deps = [ ":autofill_core_browser_java_enums" ]
}
-java_cpp_enum("generated_enum") {
+java_cpp_enum("autofill_core_common_java_enums") {
sources = [
"../core/common/submission_source.h",
]
@@ -94,7 +101,7 @@ android_library("provider_java") {
"java/src/org/chromium/components/autofill/FormData.java",
"java/src/org/chromium/components/autofill/FormFieldData.java",
]
- srcjar_deps = [ ":generated_enum" ]
+ srcjar_deps = [ ":autofill_core_common_java_enums" ]
}
generate_jni("jni_headers") {
diff --git a/chromium/components/autofill/android/autofill_provider_android.cc b/chromium/components/autofill/android/autofill_provider_android.cc
index bcc02fad5a1..644270cb779 100644
--- a/chromium/components/autofill/android/autofill_provider_android.cc
+++ b/chromium/components/autofill/android/autofill_provider_android.cc
@@ -53,7 +53,8 @@ void AutofillProviderAndroid::OnQueryFormFieldAutofill(
int32_t id,
const FormData& form,
const FormFieldData& field,
- const gfx::RectF& bounding_box) {
+ const gfx::RectF& bounding_box,
+ bool /*unused_autoselect_first_suggestion*/) {
// 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.
diff --git a/chromium/components/autofill/android/autofill_provider_android.h b/chromium/components/autofill/android/autofill_provider_android.h
index 9a707c541c4..0cc29ac8c5a 100644
--- a/chromium/components/autofill/android/autofill_provider_android.h
+++ b/chromium/components/autofill/android/autofill_provider_android.h
@@ -26,11 +26,13 @@ class AutofillProviderAndroid : public AutofillProvider {
~AutofillProviderAndroid() override;
// AutofillProvider:
- void OnQueryFormFieldAutofill(AutofillHandlerProxy* handler,
- int32_t id,
- const FormData& form,
- const FormFieldData& field,
- const gfx::RectF& bounding_box) override;
+ void OnQueryFormFieldAutofill(
+ AutofillHandlerProxy* handler,
+ int32_t id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ bool /*unused_autoselect_first_suggestion*/) override;
void OnTextFieldDidChange(AutofillHandlerProxy* handler,
const FormData& form,
const FormFieldData& field,
diff --git a/chromium/components/autofill/android/java/res/values/colors.xml b/chromium/components/autofill/android/java/res/values/colors.xml
index 416737f2fc7..3d03d8b3f32 100644
--- a/chromium/components/autofill/android/java/res/values/colors.xml
+++ b/chromium/components/autofill/android/java/res/values/colors.xml
@@ -5,7 +5,6 @@
found in the LICENSE file.
-->
<resources>
- <!-- Text colors of warning messages for credit card and password forms. -->
- <color name="http_bad_warning_message_text">#C53929</color>
+ <!-- Text colors of warning messages for credit card forms. -->
<color name="insecure_context_payment_disabled_message_text">#646464</color>
</resources>
diff --git a/chromium/components/autofill/android/java/res/values/dimens.xml b/chromium/components/autofill/android/java/res/values/dimens.xml
index a4700f7a222..1327d16ea44 100644
--- a/chromium/components/autofill/android/java/res/values/dimens.xml
+++ b/chromium/components/autofill/android/java/res/values/dimens.xml
@@ -5,12 +5,4 @@
found in the LICENSE file.
-->
<resources>
- <!--
- Larger label and icon sizes for Form-Not-Secure experiment
- (warning messages on credit card and password forms when users
- are on HTTP sites).
- -->
- <dimen name="dropdown_item_larger_sublabel_font_size">18sp</dimen>
- <dimen name="dropdown_large_icon_size">24dp</dimen>
- <dimen name="dropdown_large_icon_margin">10dp</dimen>
</resources>
diff --git a/chromium/components/autofill/content/browser/BUILD.gn b/chromium/components/autofill/content/browser/BUILD.gn
index 5f7fc9d3a77..877af89df15 100644
--- a/chromium/components/autofill/content/browser/BUILD.gn
+++ b/chromium/components/autofill/content/browser/BUILD.gn
@@ -33,12 +33,12 @@ static_library("browser") {
"//components/user_prefs",
"//content/public/browser",
"//content/public/common",
- "//device/geolocation/public/cpp",
"//gpu/config",
"//mojo/public/cpp/bindings",
"//mojo/public/cpp/system",
"//net",
"//ppapi/buildflags",
+ "//services/device/public/cpp/geolocation",
"//services/device/public/mojom",
"//services/service_manager/public/cpp",
"//sql",
diff --git a/chromium/components/autofill/content/browser/DEPS b/chromium/components/autofill/content/browser/DEPS
index 4e97f5365f3..c9b21e464f1 100644
--- a/chromium/components/autofill/content/browser/DEPS
+++ b/chromium/components/autofill/content/browser/DEPS
@@ -1,7 +1,6 @@
include_rules = [
"+content/public/browser",
"+crypto/random.h",
- "+device/geolocation/public",
"+gpu/config/gpu_info.h",
"+services/device/public",
"+services/service_manager/public/mojom",
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.cc b/chromium/components/autofill/content/browser/content_autofill_driver.cc
index 10784fd0147..fb8391c870f 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver.cc
+++ b/chromium/components/autofill/content/browser/content_autofill_driver.cc
@@ -80,6 +80,13 @@ net::URLRequestContextGetter* ContentAutofillDriver::GetURLRequestContext() {
GetURLRequestContext();
}
+scoped_refptr<network::SharedURLLoaderFactory>
+ContentAutofillDriver::GetURLLoaderFactory() {
+ return content::BrowserContext::GetDefaultStoragePartition(
+ render_frame_host_->GetSiteInstance()->GetBrowserContext())
+ ->GetURLLoaderFactoryForBrowserProcess();
+}
+
bool ContentAutofillDriver::RendererIsAvailable() {
return render_frame_host_->GetRenderViewHost() != nullptr;
}
@@ -219,8 +226,10 @@ void ContentAutofillDriver::QueryFormFieldAutofill(
int32_t id,
const FormData& form,
const FormFieldData& field,
- const gfx::RectF& bounding_box) {
- autofill_handler_->OnQueryFormFieldAutofill(id, form, field, bounding_box);
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion) {
+ autofill_handler_->OnQueryFormFieldAutofill(id, form, field, bounding_box,
+ autoselect_first_suggestion);
}
void ContentAutofillDriver::HidePopup() {
diff --git a/chromium/components/autofill/content/browser/content_autofill_driver.h b/chromium/components/autofill/content/browser/content_autofill_driver.h
index 96d93f94e24..83c9d743c57 100644
--- a/chromium/components/autofill/content/browser/content_autofill_driver.h
+++ b/chromium/components/autofill/content/browser/content_autofill_driver.h
@@ -51,6 +51,7 @@ class ContentAutofillDriver : public AutofillDriver,
// AutofillDriver:
bool IsIncognito() const override;
net::URLRequestContextGetter* GetURLRequestContext() override;
+ scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
bool RendererIsAvailable() override;
void SendFormDataToRenderer(int query_id,
RendererFormDataAction action,
@@ -91,7 +92,8 @@ class ContentAutofillDriver : public AutofillDriver,
void QueryFormFieldAutofill(int32_t id,
const FormData& form,
const FormFieldData& field,
- const gfx::RectF& bounding_box) override;
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion) override;
void HidePopup() override;
void FocusNoLongerOnForm() override;
void FocusOnFormField(const FormData& form,
diff --git a/chromium/components/autofill/content/browser/risk/fingerprint.cc b/chromium/components/autofill/content/browser/risk/fingerprint.cc
index 328d3c3586d..5b8edff8583 100644
--- a/chromium/components/autofill/content/browser/risk/fingerprint.cc
+++ b/chromium/components/autofill/content/browser/risk/fingerprint.cc
@@ -38,10 +38,10 @@
#include "content/public/browser/web_contents.h"
#include "content/public/common/screen_info.h"
#include "content/public/common/webplugininfo.h"
-#include "device/geolocation/public/cpp/geoposition.h"
#include "gpu/config/gpu_info.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "ppapi/buildflags/buildflags.h"
+#include "services/device/public/cpp/geolocation/geoposition.h"
#include "services/device/public/mojom/constants.mojom.h"
#include "services/device/public/mojom/geolocation.mojom.h"
#include "services/device/public/mojom/geolocation_context.mojom.h"
@@ -168,13 +168,14 @@ void AddGpuInfoToFingerprint(Fingerprint::MachineCharacteristics* machine,
return;
const gpu::GPUInfo gpu_info = gpu_data_manager.GetGPUInfo();
+ const gpu::GPUInfo::GPUDevice& active_gpu = gpu_info.active_gpu();
Fingerprint::MachineCharacteristics::Graphics* graphics =
machine->mutable_graphics_card();
- graphics->set_vendor_id(gpu_info.gpu.vendor_id);
- graphics->set_device_id(gpu_info.gpu.device_id);
- graphics->set_driver_version(gpu_info.driver_version);
- graphics->set_driver_date(gpu_info.driver_date);
+ graphics->set_vendor_id(active_gpu.vendor_id);
+ graphics->set_device_id(active_gpu.device_id);
+ graphics->set_driver_version(active_gpu.driver_version);
+ graphics->set_driver_date(active_gpu.driver_date);
}
// Waits for all asynchronous data required for the fingerprint to be loaded,
diff --git a/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc b/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc
index 25d5f8f6c6e..d0a06adbd7a 100644
--- a/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc
+++ b/chromium/components/autofill/content/browser/risk/fingerprint_browsertest.cc
@@ -18,7 +18,7 @@
#include "content/public/common/service_manager_connection.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/test_utils.h"
-#include "device/geolocation/public/cpp/scoped_geolocation_overrider.h"
+#include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
#include "services/device/public/mojom/geoposition.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chromium/components/autofill/content/common/BUILD.gn b/chromium/components/autofill/content/common/BUILD.gn
index 1b2628c71b0..04379f467a7 100644
--- a/chromium/components/autofill/content/common/BUILD.gn
+++ b/chromium/components/autofill/content/common/BUILD.gn
@@ -25,6 +25,7 @@ mojom("mojo_types") {
public_deps = [
"//mojo/public/mojom/base",
+ "//ui/gfx/geometry/mojo",
"//url/mojom:url_mojom_gurl",
"//url/mojom:url_mojom_origin",
]
diff --git a/chromium/components/autofill/content/common/autofill_agent.mojom b/chromium/components/autofill/content/common/autofill_agent.mojom
index 66a2f598b61..891f2b86ea0 100644
--- a/chromium/components/autofill/content/common/autofill_agent.mojom
+++ b/chromium/components/autofill/content/common/autofill_agent.mojom
@@ -84,6 +84,10 @@ interface PasswordAutofillAgent {
// ShowPasswordSuggestions messages to the browser.
FillPasswordForm(int32 key, PasswordFormFillData form_data);
+ // Fills the given |credential| into the last focused text input.
+ FillIntoFocusedField(bool is_password, mojo_base.mojom.String16 credential)
+ => (FillingStatus status);
+
// Notification to start (|active| == true) or stop (|active| == false)
// logging the decisions made about saving the password.
SetLoggingState(bool active);
@@ -97,10 +101,6 @@ interface PasswordAutofillAgent {
// Renderer is expected to return the found password form. If no password form
// is focused, the response will contain an empty |autofill::PasswordForm|.
FindFocusedPasswordForm() => (PasswordForm form);
-
- // Notifies PasswordAutofillAgent that the matching blacklisted form was
- // found.
- BlacklistedFormFound();
};
// There is one instance of this interface per render frame in the render
@@ -115,10 +115,6 @@ interface PasswordGenerationAgent {
// browser with the message |ShowPasswordGenerationPopup|.
UserTriggeredGeneratePassword();
- // Tells the renderer to populate the correct password fields with this
- // generated password.
- UserSelectedManualGenerationOption();
-
// Tells the renderer that this password form is not blacklisted. A form can
// be blacklisted if a user chooses "never save passwords for this site".
FormNotBlacklisted(PasswordForm form);
diff --git a/chromium/components/autofill/content/common/autofill_driver.mojom b/chromium/components/autofill/content/common/autofill_driver.mojom
index c15cb9fa67a..34be2a1e28e 100644
--- a/chromium/components/autofill/content/common/autofill_driver.mojom
+++ b/chromium/components/autofill/content/common/autofill_driver.mojom
@@ -50,7 +50,8 @@ interface AutofillDriver {
QueryFormFieldAutofill(int32 id,
FormData form,
FormFieldData field,
- gfx.mojom.RectF bounding_box);
+ gfx.mojom.RectF bounding_box,
+ bool autoselect_first_suggestion);
// Instructs the browser to hide the Autofill popup if it is open.
HidePopup();
@@ -123,19 +124,6 @@ interface PasswordManagerDriver {
mojo_base.mojom.String16 typed_username,
int32 options, gfx.mojom.RectF bounds);
- // Instructs the browser to show a suggestion, which will redirect the user to
- // the list of all saved passwords. The popup will use |text_direction| for
- // displaying text.
- ShowManualFallbackSuggestion(mojo_base.mojom.TextDirection text_direction,
- gfx.mojom.RectF bounds);
-
- // Instructs the browser to presave the form with generated password.
- PresaveGeneratedPassword(PasswordForm password_form);
-
- // Instructs the browser that form no longer contains a generated password and
- // the presaved form should be removed.
- PasswordNoLongerGenerated(PasswordForm password_form);
-
// Sends the outcome of HTML parsing based form classifier that detects the
// forms where password generation should be available.
SaveGenerationFieldDetectedByClassifier(
@@ -145,6 +133,10 @@ interface PasswordManagerDriver {
// username/password field is on.
CheckSafeBrowsingReputation(
url.mojom.Url form_action, url.mojom.Url frame_url);
+
+ // The focus changed to a different input in the same frame (e.g. tabbed from
+ // email to password field).
+ FocusedInputChanged(bool is_fillable, bool is_password_field);
};
// There is one instance of this interface per web contents in the browser
@@ -154,24 +146,33 @@ interface PasswordManagerClient {
// form. This is used for UMA stats.
GenerationAvailableForForm(PasswordForm password_form);
- // Instructs the browser to show the password generation popup at the
- // specified location. This location should be specified in the renderers
- // coordinate system. Form is the form associated with the password field.
- // The popup will be anchored at |bounds|. The generated password
- // will be no longer than |max_length|. |generation_element| should contain a
- // name of a password field at which generation popup is attached.
- // |is_manually_triggered| informs whether it is automatically or manually
- // triggered generation.
- ShowPasswordGenerationPopup(
- gfx.mojom.RectF bounds, int32 max_length,
- mojo_base.mojom.String16 generation_element, bool is_manually_triggered,
- PasswordForm password_form);
+ // Notifies the browser when automatic generation becomes available or
+ // unavailable and provides data needed by the UI.
+ AutomaticGenerationStatusChanged(
+ bool available, PasswordGenerationUIData? password_generation_ui_data);
+
+ // Instructs the browser to show the password generation popup for manual
+ // generation and provides the data necessary to display it.
+ // TODO(crbug.com/845458): Replace this with a method called from the browser
+ // when user triggers generation manually which returns a boolean signaling
+ // whether the state for generation could be saved or not.
+ ShowManualPasswordGenerationPopup(
+ PasswordGenerationUIData password_generation_ui_data);
// Instructs the browser to show the popup for editing a generated password.
// The location should be specified in the renderers coordinate system. Form
// is the form associated with the password field.
ShowPasswordEditingPopup(gfx.mojom.RectF bounds, PasswordForm password_form);
- // Instructs the browser to hide any password generation popups.
- HidePasswordGenerationPopup();
+ // Informs the browser that the password generation option was rejected
+ // by the user typing more characters than the maximum offer size into the
+ // password field.
+ PasswordGenerationRejectedByTyping();
+
+ // Instructs the browser to presave the form with generated password.
+ PresaveGeneratedPassword(PasswordForm password_form);
+
+ // Instructs the browser that form no longer contains a generated password and
+ // the presaved form should be removed.
+ PasswordNoLongerGenerated(PasswordForm password_form);
};
diff --git a/chromium/components/autofill/content/common/autofill_types.mojom b/chromium/components/autofill/content/common/autofill_types.mojom
index bbda37d00a4..c9ecc5760f8 100644
--- a/chromium/components/autofill/content/common/autofill_types.mojom
+++ b/chromium/components/autofill/content/common/autofill_types.mojom
@@ -7,6 +7,7 @@ module autofill.mojom;
import "mojo/public/mojom/base/text_direction.mojom";
import "mojo/public/mojom/base/time.mojom";
import "mojo/public/mojom/base/string16.mojom";
+import "ui/gfx/geometry/mojo/geometry.mojom";
import "url/mojom/origin.mojom";
import "url/mojom/url.mojom";
@@ -99,6 +100,13 @@ enum LabelSource {
VALUE,
};
+// autofill::FillingStatus
+enum FillingStatus {
+ SUCCESS,
+ ERROR_NO_VALID_FIELD,
+ ERROR_NOT_ALLOWED,
+};
+
// autofill::FormFieldData
struct FormFieldData {
mojo_base.mojom.String16 label;
@@ -142,6 +150,7 @@ struct FormData {
bool is_formless_checkout;
uint32 unique_renderer_id;
array<FormFieldData> fields;
+ array<uint32> username_predictions;
};
// autofill::FormFieldDataPredictions
@@ -170,15 +179,18 @@ struct PasswordAndRealm {
// autofill::PasswordFormFillData
struct PasswordFormFillData {
+ uint32 form_renderer_id;
mojo_base.mojom.String16 name;
url.mojom.Url origin;
url.mojom.Url action;
FormFieldData username_field;
FormFieldData password_field;
+ bool username_may_use_prefilled_placeholder;
string preferred_realm;
map<mojo_base.mojom.String16, PasswordAndRealm> additional_logins;
bool wait_for_username;
bool is_possible_change_password_form;
+ bool has_renderer_ids;
};
// autofill::PasswordFormGenerationData
@@ -189,6 +201,15 @@ struct PasswordFormGenerationData {
uint32 confirmation_field_signature;
};
+// autofill::password_generation::PasswordGenerationUIData
+struct PasswordGenerationUIData {
+ gfx.mojom.RectF bounds;
+ int32 max_length;
+ mojo_base.mojom.String16 generation_element;
+ mojo_base.mojom.TextDirection text_direction;
+ PasswordForm password_form;
+};
+
// autofill::ValueElementPair
struct ValueElementPair {
mojo_base.mojom.String16 value;
diff --git a/chromium/components/autofill/content/common/autofill_types.typemap b/chromium/components/autofill/content/common/autofill_types.typemap
index 44e88ba309a..1f9b633589c 100644
--- a/chromium/components/autofill/content/common/autofill_types.typemap
+++ b/chromium/components/autofill/content/common/autofill_types.typemap
@@ -4,6 +4,7 @@
mojom = "//components/autofill/content/common/autofill_types.mojom"
public_headers = [
+ "//components/autofill/core/common/filling_status.h",
"//components/autofill/core/common/form_data.h",
"//components/autofill/core/common/form_data_predictions.h",
"//components/autofill/core/common/form_field_data.h",
@@ -12,6 +13,7 @@ public_headers = [
"//components/autofill/core/common/password_form_field_prediction_map.h",
"//components/autofill/core/common/password_form_fill_data.h",
"//components/autofill/core/common/password_form_generation_data.h",
+ "//components/autofill/core/common/password_generation_util.h",
"//components/autofill/core/common/submission_source.h",
]
traits_headers =
@@ -23,10 +25,12 @@ deps = [
"//base",
"//base:i18n",
"//components/autofill/core/common",
+ "//ui/gfx/geometry/mojo:struct_traits",
]
type_mappings = [
"autofill.mojom.CheckStatus=autofill::FormFieldData::CheckStatus",
+ "autofill.mojom.FillingStatus=autofill::FillingStatus",
"autofill.mojom.FormData=autofill::FormData",
"autofill.mojom.FormDataPredictions=autofill::FormDataPredictions",
"autofill.mojom.FormFieldData=autofill::FormFieldData",
@@ -41,9 +45,10 @@ type_mappings = [
"autofill.mojom.PasswordFormGenerationData=autofill::PasswordFormGenerationData",
"autofill.mojom.PasswordFormLayout=autofill::PasswordForm::Layout",
"autofill.mojom.PasswordFormScheme=autofill::PasswordForm::Scheme",
+ "autofill.mojom.PasswordFormSubmissionIndicatorEvent=autofill::PasswordForm::SubmissionIndicatorEvent",
"autofill.mojom.PasswordFormType=autofill::PasswordForm::Type",
+ "autofill.mojom.PasswordGenerationUIData=autofill::password_generation::PasswordGenerationUIData",
"autofill.mojom.RoleAttribute=autofill::FormFieldData::RoleAttribute",
- "autofill.mojom.ValueElementPair=autofill::ValueElementPair",
- "autofill.mojom.PasswordFormSubmissionIndicatorEvent=autofill::PasswordForm::SubmissionIndicatorEvent",
"autofill.mojom.SubmissionSource=autofill::SubmissionSource",
+ "autofill.mojom.ValueElementPair=autofill::ValueElementPair",
]
diff --git a/chromium/components/autofill/content/common/autofill_types_struct_traits.cc b/chromium/components/autofill/content/common/autofill_types_struct_traits.cc
index d4552fd7520..fad95dddd5c 100644
--- a/chromium/components/autofill/content/common/autofill_types_struct_traits.cc
+++ b/chromium/components/autofill/content/common/autofill_types_struct_traits.cc
@@ -6,8 +6,8 @@
#include "base/i18n/rtl.h"
#include "mojo/public/cpp/base/string16_mojom_traits.h"
-#include "mojo/public/cpp/base/text_direction_mojom_traits.h"
#include "mojo/public/cpp/base/time_mojom_traits.h"
+#include "ui/gfx/geometry/mojo/geometry_struct_traits.h"
#include "url/mojom/origin_mojom_traits.h"
#include "url/mojom/url_gurl_mojom_traits.h"
@@ -533,6 +533,41 @@ bool EnumTraits<autofill::mojom::LabelSource,
}
// static
+autofill::mojom::FillingStatus
+EnumTraits<autofill::mojom::FillingStatus, autofill::FillingStatus>::ToMojom(
+ autofill::FillingStatus input) {
+ switch (input) {
+ case autofill::FillingStatus::SUCCESS:
+ return autofill::mojom::FillingStatus::SUCCESS;
+ case autofill::FillingStatus::ERROR_NO_VALID_FIELD:
+ return autofill::mojom::FillingStatus::ERROR_NO_VALID_FIELD;
+ case autofill::FillingStatus::ERROR_NOT_ALLOWED:
+ return autofill::mojom::FillingStatus::ERROR_NOT_ALLOWED;
+ }
+ NOTREACHED();
+ return autofill::mojom::FillingStatus::SUCCESS;
+}
+
+// static
+bool EnumTraits<autofill::mojom::FillingStatus, autofill::FillingStatus>::
+ FromMojom(autofill::mojom::FillingStatus input,
+ autofill::FillingStatus* output) {
+ switch (input) {
+ case autofill::mojom::FillingStatus::SUCCESS:
+ *output = autofill::FillingStatus::SUCCESS;
+ return true;
+ case autofill::mojom::FillingStatus::ERROR_NO_VALID_FIELD:
+ *output = autofill::FillingStatus::ERROR_NO_VALID_FIELD;
+ return true;
+ case autofill::mojom::FillingStatus::ERROR_NOT_ALLOWED:
+ *output = autofill::FillingStatus::ERROR_NOT_ALLOWED;
+ return true;
+ }
+ NOTREACHED();
+ return false;
+}
+
+// static
bool StructTraits<
autofill::mojom::FormFieldDataDataView,
autofill::FormFieldData>::Read(autofill::mojom::FormFieldDataDataView data,
@@ -614,6 +649,9 @@ bool StructTraits<autofill::mojom::FormDataDataView, autofill::FormData>::Read(
if (!data.ReadFields(&out->fields))
return false;
+ if (!data.ReadUsernamePredictions(&out->username_predictions))
+ return false;
+
return true;
}
@@ -681,9 +719,13 @@ bool StructTraits<autofill::mojom::PasswordFormFillDataDataView,
!data.ReadAdditionalLogins(&out->additional_logins))
return false;
+ out->form_renderer_id = data.form_renderer_id();
out->wait_for_username = data.wait_for_username();
out->is_possible_change_password_form =
data.is_possible_change_password_form();
+ out->has_renderer_ids = data.has_renderer_ids();
+ out->username_may_use_prefilled_placeholder =
+ data.username_may_use_prefilled_placeholder();
return true;
}
@@ -705,6 +747,24 @@ bool StructTraits<autofill::mojom::PasswordFormGenerationDataDataView,
}
// static
+bool StructTraits<autofill::mojom::PasswordGenerationUIDataDataView,
+ autofill::password_generation::PasswordGenerationUIData>::
+ Read(autofill::mojom::PasswordGenerationUIDataDataView data,
+ autofill::password_generation::PasswordGenerationUIData* out) {
+ if (!data.ReadBounds(&out->bounds))
+ return false;
+
+ out->max_length = data.max_length();
+
+ if (!data.ReadGenerationElement(&out->generation_element) ||
+ !data.ReadTextDirection(&out->text_direction) ||
+ !data.ReadPasswordForm(&out->password_form))
+ return false;
+
+ return true;
+}
+
+// static
bool StructTraits<
autofill::mojom::PasswordFormDataView,
autofill::PasswordForm>::Read(autofill::mojom::PasswordFormDataView data,
diff --git a/chromium/components/autofill/content/common/autofill_types_struct_traits.h b/chromium/components/autofill/content/common/autofill_types_struct_traits.h
index 4c42d624ac8..68d03e0ec10 100644
--- a/chromium/components/autofill/content/common/autofill_types_struct_traits.h
+++ b/chromium/components/autofill/content/common/autofill_types_struct_traits.h
@@ -21,8 +21,11 @@
#include "components/autofill/core/common/password_form_field_prediction_map.h"
#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/autofill/core/common/password_form_generation_data.h"
+#include "components/autofill/core/common/password_generation_util.h"
#include "components/autofill/core/common/submission_source.h"
+#include "mojo/public/cpp/base/text_direction_mojom_traits.h"
#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "ui/gfx/geometry/rect_f.h"
#include "url/origin.h"
namespace mojo {
@@ -119,6 +122,13 @@ struct EnumTraits<autofill::mojom::LabelSource,
};
template <>
+struct EnumTraits<autofill::mojom::FillingStatus, autofill::FillingStatus> {
+ static autofill::mojom::FillingStatus ToMojom(autofill::FillingStatus input);
+ static bool FromMojom(autofill::mojom::FillingStatus input,
+ autofill::FillingStatus* output);
+};
+
+template <>
struct StructTraits<autofill::mojom::FormFieldDataDataView,
autofill::FormFieldData> {
static const base::string16& label(const autofill::FormFieldData& r) {
@@ -262,6 +272,11 @@ struct StructTraits<autofill::mojom::FormDataDataView, autofill::FormData> {
return r.fields;
}
+ static const std::vector<uint32_t>& username_predictions(
+ const autofill::FormData& r) {
+ return r.username_predictions;
+ }
+
static bool Read(autofill::mojom::FormDataDataView data,
autofill::FormData* out);
};
@@ -347,6 +362,9 @@ struct StructTraits<autofill::mojom::PasswordAndRealmDataView,
template <>
struct StructTraits<autofill::mojom::PasswordFormFillDataDataView,
autofill::PasswordFormFillData> {
+ static uint32_t form_renderer_id(const autofill::PasswordFormFillData& r) {
+ return r.form_renderer_id;
+ }
static const base::string16& name(const autofill::PasswordFormFillData& r) {
return r.name;
@@ -370,6 +388,11 @@ struct StructTraits<autofill::mojom::PasswordFormFillDataDataView,
return r.password_field;
}
+ static bool username_may_use_prefilled_placeholder(
+ const autofill::PasswordFormFillData& r) {
+ return r.username_may_use_prefilled_placeholder;
+ }
+
static const std::string& preferred_realm(
const autofill::PasswordFormFillData& r) {
return r.preferred_realm;
@@ -389,6 +412,10 @@ struct StructTraits<autofill::mojom::PasswordFormFillDataDataView,
return r.is_possible_change_password_form;
}
+ static bool has_renderer_ids(const autofill::PasswordFormFillData& r) {
+ return r.has_renderer_ids;
+ }
+
static bool Read(autofill::mojom::PasswordFormFillDataDataView data,
autofill::PasswordFormFillData* out);
};
@@ -421,6 +448,39 @@ struct StructTraits<autofill::mojom::PasswordFormGenerationDataDataView,
};
template <>
+struct StructTraits<autofill::mojom::PasswordGenerationUIDataDataView,
+ autofill::password_generation::PasswordGenerationUIData> {
+ static const gfx::RectF& bounds(
+ const autofill::password_generation::PasswordGenerationUIData& r) {
+ return r.bounds;
+ }
+
+ static int max_length(
+ const autofill::password_generation::PasswordGenerationUIData& r) {
+ return r.max_length;
+ }
+
+ static const base::string16& generation_element(
+ const autofill::password_generation::PasswordGenerationUIData& r) {
+ return r.generation_element;
+ }
+
+ static base::i18n::TextDirection text_direction(
+ const autofill::password_generation::PasswordGenerationUIData& r) {
+ return r.text_direction;
+ }
+
+ static const autofill::PasswordForm& password_form(
+ const autofill::password_generation::PasswordGenerationUIData& r) {
+ return r.password_form;
+ }
+
+ static bool Read(
+ autofill::mojom::PasswordGenerationUIDataDataView data,
+ autofill::password_generation::PasswordGenerationUIData* out);
+};
+
+template <>
struct StructTraits<autofill::mojom::PasswordFormDataView,
autofill::PasswordForm> {
static autofill::PasswordForm::Scheme scheme(
diff --git a/chromium/components/autofill/content/common/autofill_types_struct_traits_unittest.cc b/chromium/components/autofill/content/common/autofill_types_struct_traits_unittest.cc
index 6c0b146e472..f0f8abfdea2 100644
--- a/chromium/components/autofill/content/common/autofill_types_struct_traits_unittest.cc
+++ b/chromium/components/autofill/content/common/autofill_types_struct_traits_unittest.cc
@@ -11,6 +11,7 @@
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/password_generation_util.h"
#include "components/autofill/core/common/signatures_util.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/bindings/interface_request.h"
@@ -35,6 +36,7 @@ void CreateTestFieldDataPredictions(const std::string& signature,
}
void CreateTestPasswordFormFillData(PasswordFormFillData* fill_data) {
+ fill_data->form_renderer_id = 1234;
fill_data->name = base::ASCIIToUTF16("TestName");
fill_data->origin = GURL("https://foo.com/");
fill_data->action = GURL("https://foo.com/login");
@@ -143,8 +145,18 @@ void CreateTestFormsPredictionsMap(FormsPredictionsMap* predictions) {
PasswordFormFieldPredictionType::PREDICTION_CURRENT_PASSWORD;
}
+void CreatePasswordGenerationUIData(
+ password_generation::PasswordGenerationUIData* data) {
+ data->bounds = gfx::RectF(1, 1, 200, 100);
+ data->max_length = 20;
+ data->generation_element = base::ASCIIToUTF16("generation_element");
+ data->text_direction = base::i18n::RIGHT_TO_LEFT;
+ CreateTestPasswordForm(&data->password_form);
+}
+
void CheckEqualPasswordFormFillData(const PasswordFormFillData& expected,
const PasswordFormFillData& actual) {
+ EXPECT_EQ(expected.form_renderer_id, actual.form_renderer_id);
EXPECT_EQ(expected.name, actual.name);
EXPECT_EQ(expected.origin, actual.origin);
EXPECT_EQ(expected.action, actual.action);
@@ -184,6 +196,16 @@ void CheckEqualPasswordFormGenerationData(
actual.confirmation_field_signature.value());
}
+void CheckEqualPassPasswordGenerationUIData(
+ const password_generation::PasswordGenerationUIData& expected,
+ const password_generation::PasswordGenerationUIData& actual) {
+ EXPECT_EQ(expected.bounds, actual.bounds);
+ EXPECT_EQ(expected.max_length, actual.max_length);
+ EXPECT_EQ(expected.generation_element, actual.generation_element);
+ EXPECT_EQ(expected.text_direction, actual.text_direction);
+ EXPECT_EQ(expected.password_form, actual.password_form);
+}
+
} // namespace
class AutofillTypeTraitsTestImpl : public testing::Test,
@@ -231,6 +253,12 @@ class AutofillTypeTraitsTestImpl : public testing::Test,
std::move(callback).Run(s);
}
+ void PassPasswordGenerationUIData(
+ const password_generation::PasswordGenerationUIData& s,
+ PassPasswordGenerationUIDataCallback callback) override {
+ std::move(callback).Run(s);
+ }
+
void PassPasswordForm(const PasswordForm& s,
PassPasswordFormCallback callback) override {
std::move(callback).Run(s);
@@ -291,6 +319,14 @@ void ExpectPasswordFormGenerationData(
closure.Run();
}
+void ExpectPasswordGenerationUIData(
+ const password_generation::PasswordGenerationUIData& expected,
+ base::OnceClosure closure,
+ const password_generation::PasswordGenerationUIData& passed) {
+ CheckEqualPassPasswordGenerationUIData(expected, passed);
+ std::move(closure).Run();
+}
+
void ExpectPasswordForm(const PasswordForm& expected,
const base::Closure& closure,
const PasswordForm& passed) {
@@ -332,6 +368,7 @@ TEST_F(AutofillTypeTraitsTestImpl, PassFormFieldData) {
TEST_F(AutofillTypeTraitsTestImpl, PassFormData) {
FormData input;
test::CreateTestAddressFormData(&input);
+ input.username_predictions = {1, 13, 2};
base::RunLoop loop;
mojom::TypeTraitsTestPtr proxy = GetTypeTraitsTestProxy();
@@ -402,6 +439,18 @@ TEST_F(AutofillTypeTraitsTestImpl, PassPasswordFormGenerationData) {
loop.Run();
}
+TEST_F(AutofillTypeTraitsTestImpl, PassPasswordGenerationUIData) {
+ password_generation::PasswordGenerationUIData input;
+ CreatePasswordGenerationUIData(&input);
+
+ base::RunLoop loop;
+ mojom::TypeTraitsTestPtr proxy = GetTypeTraitsTestProxy();
+ proxy->PassPasswordGenerationUIData(
+ input, base::BindOnce(&ExpectPasswordGenerationUIData, input,
+ loop.QuitClosure()));
+ loop.Run();
+}
+
TEST_F(AutofillTypeTraitsTestImpl, PassPasswordForm) {
PasswordForm input;
CreateTestPasswordForm(&input);
diff --git a/chromium/components/autofill/content/common/test_autofill_types.mojom b/chromium/components/autofill/content/common/test_autofill_types.mojom
index b578eb47ada..a0a0234d217 100644
--- a/chromium/components/autofill/content/common/test_autofill_types.mojom
+++ b/chromium/components/autofill/content/common/test_autofill_types.mojom
@@ -15,4 +15,5 @@ interface TypeTraitsTest {
PassPasswordFormFillData(PasswordFormFillData s) => (PasswordFormFillData passed);
PassFormsPredictionsMap(FormsPredictionsMap s) => (FormsPredictionsMap passed);
PassPasswordFormGenerationData(PasswordFormGenerationData s) => (PasswordFormGenerationData passed);
+ PassPasswordGenerationUIData(PasswordGenerationUIData s) => (PasswordGenerationUIData passed);
};
diff --git a/chromium/components/autofill/content/renderer/autofill_agent.cc b/chromium/components/autofill/content/renderer/autofill_agent.cc
index 2af83153a60..2476e264b9f 100644
--- a/chromium/components/autofill/content/renderer/autofill_agent.cc
+++ b/chromium/components/autofill/content/renderer/autofill_agent.cc
@@ -60,6 +60,7 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/events/keycodes/keyboard_codes.h"
+using blink::WebAutofillState;
using blink::WebAutofillClient;
using blink::WebConsoleMessage;
using blink::WebDocument;
@@ -125,8 +126,8 @@ AutofillAgent::ShowSuggestionsOptions::ShowSuggestionsOptions()
: autofill_on_empty_values(false),
requires_caret_at_end(false),
show_full_suggestion_list(false),
- show_password_suggestions_only(false) {
-}
+ show_password_suggestions_only(false),
+ autoselect_first_suggestion(false) {}
AutofillAgent::AutofillAgent(content::RenderFrame* render_frame,
PasswordAutofillAgent* password_autofill_agent,
@@ -137,7 +138,7 @@ AutofillAgent::AutofillAgent(content::RenderFrame* render_frame,
password_autofill_agent_(password_autofill_agent),
password_generation_agent_(password_generation_agent),
autofill_query_id_(0),
- was_query_node_autofilled_(false),
+ query_node_autofill_state_(WebAutofillState::kNotFilled),
ignore_text_changes_(false),
is_popup_possibly_visible_(false),
is_generation_popup_possibly_visible_(false),
@@ -192,6 +193,9 @@ void AutofillAgent::DidFinishDocumentLoad() {
}
void AutofillAgent::DidChangeScrollOffset() {
+ if (element_.IsNull())
+ return;
+
if (!focus_requires_scroll_) {
// Post a task here since scroll offset may change during layout.
// (https://crbug.com/804886)
@@ -208,7 +212,7 @@ void AutofillAgent::DidChangeScrollOffset() {
void AutofillAgent::DidChangeScrollOffsetImpl(
const WebFormControlElement& element) {
- if (element != element_ || focus_requires_scroll_ ||
+ if (element != element_ || element_.IsNull() || focus_requires_scroll_ ||
!is_popup_possibly_visible_ || !element_.Focused())
return;
@@ -352,6 +356,8 @@ void AutofillAgent::TextFieldDidReceiveKeyDown(const WebInputElement& element,
ShowSuggestionsOptions options;
options.autofill_on_empty_values = true;
options.requires_caret_at_end = true;
+ options.autoselect_first_suggestion =
+ ShouldAutoselectFirstSuggestionOnArrowDown();
ShowSuggestions(element, options);
}
}
@@ -408,6 +414,24 @@ void AutofillAgent::DoAcceptDataListSuggestion(
DoFillFieldWithValue(new_value, input_element);
}
+void AutofillAgent::TriggerRefillIfNeeded(const FormData& form) {
+ if (!base::FeatureList::IsEnabled(features::kAutofillDynamicForms))
+ return;
+ FormFieldData field;
+ FormData updated_form;
+ if (form_util::FindFormAndFieldForFormControlElement(element_, &updated_form,
+ &field) &&
+ !form.DynamicallySameFormAs(updated_form)) {
+ base::TimeTicks forms_seen_timestamp = base::TimeTicks::Now();
+ WebLocalFrame* frame = render_frame()->GetWebFrame();
+ std::vector<FormData> forms;
+ forms.push_back(updated_form);
+ // Always communicate to browser process for topmost frame.
+ if (!forms.empty() || !frame->Parent())
+ GetAutofillDriver()->FormsSeen(forms, forms_seen_timestamp);
+ }
+}
+
// mojom::AutofillAgent:
void AutofillAgent::FillForm(int32_t id, const FormData& form) {
if (element_.IsNull())
@@ -421,12 +445,14 @@ void AutofillAgent::FillForm(int32_t id, const FormData& form) {
if (base::FeatureList::IsEnabled(features::kAutofillDynamicForms))
ReplaceElementIfNowInvalid(form);
- was_query_node_autofilled_ = element_.IsAutofilled();
+ query_node_autofill_state_ = element_.GetAutofillState();
form_util::FillForm(form, element_);
if (!element_.Form().IsNull())
UpdateLastInteractedForm(element_.Form());
GetAutofillDriver()->DidFillAutofillFormData(form, base::TimeTicks::Now());
+
+ TriggerRefillIfNeeded(form);
}
void AutofillAgent::PreviewForm(int32_t id, const FormData& form) {
@@ -436,7 +462,7 @@ void AutofillAgent::PreviewForm(int32_t id, const FormData& form) {
if (id != autofill_query_id_)
return;
- was_query_node_autofilled_ = element_.IsAutofilled();
+ query_node_autofill_state_ = element_.GetAutofillState();
form_util::PreviewForm(form, element_);
GetAutofillDriver()->DidPreviewAutofillFormData();
@@ -469,7 +495,7 @@ void AutofillAgent::ClearPreviewedForm() {
return;
form_util::ClearPreviewedFormWithElement(element_,
- was_query_node_autofilled_);
+ query_node_autofill_state_);
}
void AutofillAgent::FillFieldWithValue(const base::string16& value) {
@@ -479,7 +505,7 @@ void AutofillAgent::FillFieldWithValue(const base::string16& value) {
WebInputElement* input_element = ToWebInputElement(&element_);
if (input_element) {
DoFillFieldWithValue(value, input_element);
- input_element->SetAutofilled(true);
+ input_element->SetAutofillState(WebAutofillState::kAutofilled);
}
}
@@ -624,7 +650,7 @@ void AutofillAgent::ShowSuggestions(const WebFormControlElement& element,
return;
}
- QueryAutofillSuggestions(element);
+ QueryAutofillSuggestions(element, options.autoselect_first_suggestion);
}
void AutofillAgent::SetQueryPasswordSuggestion(bool query) {
@@ -640,7 +666,8 @@ void AutofillAgent::SetFocusRequiresScroll(bool require) {
}
void AutofillAgent::QueryAutofillSuggestions(
- const WebFormControlElement& element) {
+ const WebFormControlElement& element,
+ bool autoselect_first_suggestion) {
if (!element.GetDocument().GetFrame())
return;
@@ -683,7 +710,8 @@ void AutofillAgent::QueryAutofillSuggestions(
GetAutofillDriver()->SetDataList(data_list_values, data_list_labels);
GetAutofillDriver()->QueryFormFieldAutofill(
autofill_query_id_, form, field,
- render_frame()->GetRenderView()->ElementBoundsInWindow(element_));
+ render_frame()->GetRenderView()->ElementBoundsInWindow(element_),
+ autoselect_first_suggestion);
}
void AutofillAgent::DoFillFieldWithValue(const base::string16& value,
@@ -696,9 +724,9 @@ void AutofillAgent::DoFillFieldWithValue(const base::string16& value,
void AutofillAgent::DoPreviewFieldWithValue(const base::string16& value,
WebInputElement* node) {
- was_query_node_autofilled_ = element_.IsAutofilled();
+ query_node_autofill_state_ = element_.GetAutofillState();
node->SetSuggestedValue(blink::WebString::FromUTF16(value));
- node->SetAutofilled(true);
+ node->SetAutofillState(WebAutofillState::kPreviewed);
form_util::PreviewSuggestion(node->SuggestedValue().Utf16(),
node->Value().Utf16(), node);
}
@@ -1056,7 +1084,7 @@ const mojom::AutofillDriverPtr& AutofillAgent::GetAutofillDriver() {
return autofill_driver_;
}
-const mojom::PasswordManagerDriverPtr&
+const mojom::PasswordManagerDriverAssociatedPtr&
AutofillAgent::GetPasswordManagerDriver() {
DCHECK(password_autofill_agent_);
return password_autofill_agent_->GetPasswordManagerDriver();
diff --git a/chromium/components/autofill/content/renderer/autofill_agent.h b/chromium/components/autofill/content/renderer/autofill_agent.h
index 36bf731bb83..ba36f73adfb 100644
--- a/chromium/components/autofill/content/renderer/autofill_agent.h
+++ b/chromium/components/autofill/content/renderer/autofill_agent.h
@@ -64,7 +64,7 @@ class AutofillAgent : public content::RenderFrameObserver,
const mojom::AutofillDriverPtr& GetAutofillDriver();
- const mojom::PasswordManagerDriverPtr& GetPasswordManagerDriver();
+ const mojom::PasswordManagerDriverAssociatedPtr& GetPasswordManagerDriver();
// mojom::AutofillAgent:
void FillForm(int32_t id, const FormData& form) override;
@@ -149,6 +149,10 @@ class AutofillAgent : public content::RenderFrameObserver,
// Specifies that only show a suggestions box if |element| is part of a
// password form, otherwise show no suggestions.
bool show_password_suggestions_only;
+
+ // 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;
};
// content::RenderFrameObserver:
@@ -208,7 +212,8 @@ class AutofillAgent : public content::RenderFrameObserver,
// Queries the browser for Autocomplete and Autofill suggestions for the given
// |element|.
- void QueryAutofillSuggestions(const blink::WebFormControlElement& element);
+ void QueryAutofillSuggestions(const blink::WebFormControlElement& element,
+ bool autoselect_first_suggestion);
// Sets the element value to reflect the selected |suggested_value|.
void DoAcceptDataListSuggestion(const base::string16& suggested_value);
@@ -266,6 +271,11 @@ class AutofillAgent : public content::RenderFrameObserver,
// |element_| with it if it's found.
void ReplaceElementIfNowInvalid(const FormData& form);
+ // Trigger a refill if needed for dynamic forms. A refill is needed if some
+ // properties of the form (name, number of fields), or fields (name, id,
+ // label, visibility, control type) have changed after an autofill.
+ void TriggerRefillIfNeeded(const FormData& form);
+
// Formerly cached forms for all frames, now only caches forms for the current
// frame.
FormCache form_cache_;
@@ -302,8 +312,8 @@ class AutofillAgent : public content::RenderFrameObserver,
// We use a simplified comparison function.
std::set<FormData, FormDataCompare> submitted_forms_;
- // Was the query node autofilled prior to previewing the form?
- bool was_query_node_autofilled_;
+ // The query node autofill state prior to previewing the form.
+ blink::WebAutofillState query_node_autofill_state_;
// Whether or not to ignore text changes. Useful for when we're committing
// a composition when we are defocusing the WebView and we don't want to
diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.cc b/chromium/components/autofill/content/renderer/form_autofill_util.cc
index 4c4ed513388..8f11a5adb45 100644
--- a/chromium/components/autofill/content/renderer/form_autofill_util.cc
+++ b/chromium/components/autofill/content/renderer/form_autofill_util.cc
@@ -45,6 +45,7 @@
#include "third_party/blink/public/web/web_select_element.h"
using autofill::FormFieldData;
+using blink::WebAutofillState;
using blink::WebDocument;
using blink::WebElement;
using blink::WebElementCollection;
@@ -81,10 +82,6 @@ enum FieldFilterMask {
FILTER_NON_FOCUSABLE_ELEMENTS,
};
-// If true, operations causing layout computation should be avoided. Set by
-// ScopedLayoutPreventer.
-bool g_prevent_layout = false;
-
void TruncateString(base::string16* str, size_t max_length) {
if (str->length() > max_length)
str->resize(max_length);
@@ -855,16 +852,22 @@ void ForEachMatchingFormFieldCommon(
CR_DEFINE_STATIC_LOCAL(WebString, kValue, ("value"));
CR_DEFINE_STATIC_LOCAL(WebString, kPlaceholder, ("placeholder"));
+ if (!is_initiating_element &&
+ element->GetAutofillState() == WebAutofillState::kAutofilled)
+ continue;
+
if (!force_override && !is_initiating_element &&
- // A text field, with a non-empty value that 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 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.
+ // 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
+ // 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.
(IsAutofillableInputElement(input_element) ||
IsTextAreaElement(*element)) &&
+ (element->UserHasEditedTheField() ||
+ !base::FeatureList::IsEnabled(features::kAutofillPrefilledFields)) &&
!SanitizedFieldIsEmpty(element->Value().Utf16()) &&
(!element->HasAttribute(kValue) ||
element->GetAttribute(kValue) != element->Value()) &&
@@ -873,9 +876,6 @@ void ForEachMatchingFormFieldCommon(
base::i18n::ToLower(element->Value().Utf16())))
continue;
- DCHECK(!g_prevent_layout || !(filters & FILTER_NON_FOCUSABLE_ELEMENTS))
- << "The callsite of this code wanted to both prevent layout and check "
- "isFocusable. Pick one.";
if (((filters & FILTER_DISABLED_ELEMENTS) && !element->IsEnabled()) ||
((filters & FILTER_READONLY_ELEMENTS) && element->IsReadOnly()) ||
// See description for FILTER_NON_FOCUSABLE_ELEMENTS.
@@ -953,7 +953,7 @@ void FillFormField(const FormFieldData& data,
if (!field->GetDocument().GetFrame())
return;
- field->SetAutofilled(true);
+ field->SetAutofillState(WebAutofillState::kAutofilled);
field->SetAutofillSection(WebString::FromUTF8(data.section));
if (is_initiating_node &&
@@ -987,10 +987,10 @@ 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->SetAutofilled(true);
+ input_element->SetAutofillState(WebAutofillState::kPreviewed);
} else if (IsTextAreaElement(*field) || IsSelectElement(*field)) {
field->SetSuggestedValue(blink::WebString::FromUTF16(data.value));
- field->SetAutofilled(true);
+ field->SetAutofillState(WebAutofillState::kPreviewed);
}
if (is_initiating_node &&
@@ -1109,7 +1109,7 @@ void MatchLabelsAndFields(
// or
// 2) a NULL |form_element|.
//
-// If |field| is not NULL, then |form_control_element| should be not NULL.
+// If |field| is not NULL, then |form_control_element| should not be NULL.
bool FormOrFieldsetsToFormData(
const blink::WebFormElement* form_element,
const blink::WebFormControlElement* form_control_element,
@@ -1179,6 +1179,7 @@ bool FormOrFieldsetsToFormData(
// the DOM. We use the |fields_extracted| vector to make sure we assign the
// extracted label to the correct field, as it's possible |form_fields| will
// not contain all of the elements in |control_elements|.
+ bool found_field = false;
for (size_t i = 0, field_idx = 0;
i < control_elements.size() && field_idx < form_fields.size(); ++i) {
// This field didn't meet the requirements, so don't try to find a label
@@ -1194,12 +1195,20 @@ bool FormOrFieldsetsToFormData(
}
TruncateString(&form_fields[field_idx]->label, kMaxDataLength);
- if (field && *form_control_element == control_element)
+ if (field && *form_control_element == control_element) {
*field = *form_fields[field_idx];
+ found_field = true;
+ }
++field_idx;
}
+ // The form_control_element was not found in control_elements. This can
+ // happen if elements are dynamically removed from the form while it is
+ // being processed. See http://crbug.com/849870
+ if (field && !found_field)
+ return false;
+
// Copy the created FormFields into the resulting FormData object.
for (const auto& field : form_fields)
form->fields.push_back(*field);
@@ -1257,10 +1266,7 @@ bool ScriptModifiedUsernameAcceptable(
const base::string16* typed_from_key = map_key.second.first.get();
if (!typed_from_key)
continue;
- const WebInputElement* input_element = ToWebInputElement(&map_key.first);
- if (input_element && input_element->IsTextField() &&
- !input_element->IsPasswordFieldForAutofill() &&
- typed_from_key->size() >= kMinMatchSize &&
+ if (typed_from_key->size() >= kMinMatchSize &&
lowercase.find(base::i18n::ToLower(*typed_from_key)) !=
base::string16::npos) {
return true;
@@ -1272,18 +1278,6 @@ bool ScriptModifiedUsernameAcceptable(
} // namespace
-ScopedLayoutPreventer::ScopedLayoutPreventer() {
- DCHECK(!g_prevent_layout) << "Is any other instance of ScopedLayoutPreventer "
- "alive in the same process?";
- g_prevent_layout = true;
-}
-
-ScopedLayoutPreventer::~ScopedLayoutPreventer() {
- DCHECK(g_prevent_layout) << "Is any other instance of ScopedLayoutPreventer "
- "alive in the same process?";
- g_prevent_layout = false;
-}
-
GURL StripAuthAndParams(const GURL& gurl) {
GURL::Replacements rep;
rep.ClearUsername();
@@ -1426,6 +1420,10 @@ bool IsAutofillableElement(const WebFormControlElement& element) {
IsSelectElement(element) || IsTextAreaElement(element);
}
+bool IsWebElementVisible(const blink::WebElement& element) {
+ return element.IsFocusable();
+}
+
const base::string16 GetFormIdentifier(const WebFormElement& form) {
base::string16 identifier = form.GetName().Utf16();
CR_DEFINE_STATIC_LOCAL(WebString, kId, ("id"));
@@ -1435,13 +1433,18 @@ const base::string16 GetFormIdentifier(const WebFormElement& form) {
return identifier;
}
-bool IsWebElementVisible(const blink::WebElement& element) {
- // Testing anything related to visibility is likely to trigger layout. If that
- // should not happen, all elements are suspected of being visible. This is
- // consistent with the default value of FormFieldData::is_focusable.
- if (g_prevent_layout)
- return true;
- return element.IsFocusable();
+base::i18n::TextDirection GetTextDirectionForElement(
+ const blink::WebFormControlElement& element) {
+ // Use 'text-align: left|right' if set or 'direction' otherwise.
+ // See https://crbug.com/482339
+ base::i18n::TextDirection direction = element.DirectionForFormData() == "rtl"
+ ? base::i18n::RIGHT_TO_LEFT
+ : base::i18n::LEFT_TO_RIGHT;
+ if (element.AlignmentForFormData() == "left")
+ direction = base::i18n::LEFT_TO_RIGHT;
+ else if (element.AlignmentForFormData() == "right")
+ direction = base::i18n::RIGHT_TO_LEFT;
+ return direction;
}
std::vector<blink::WebFormControlElement> ExtractAutofillableElementsFromSet(
@@ -1504,7 +1507,8 @@ void WebFormControlElementToFormField(
if (field_value_and_properties_map) {
FieldValueAndPropertiesMaskMap::const_iterator it =
- field_value_and_properties_map->find(element);
+ field_value_and_properties_map->find(
+ element.UniqueRendererFormControlId());
if (it != field_value_and_properties_map->end())
field->properties_mask = it->second.second;
}
@@ -1516,19 +1520,12 @@ void WebFormControlElementToFormField(
if (IsAutofillableInputElement(input_element) ||
IsTextAreaElement(element) ||
IsSelectElement(element)) {
+ // The browser doesn't need to differentiate between preview and autofill.
field->is_autofilled = element.IsAutofilled();
field->is_focusable = IsWebElementVisible(element);
field->should_autocomplete = element.AutoComplete();
- // Use 'text-align: left|right' if set or 'direction' otherwise.
- // See crbug.com/482339
- field->text_direction = element.DirectionForFormData() == "rtl"
- ? base::i18n::RIGHT_TO_LEFT
- : base::i18n::LEFT_TO_RIGHT;
- if (element.AlignmentForFormData() == "left")
- field->text_direction = base::i18n::LEFT_TO_RIGHT;
- else if (element.AlignmentForFormData() == "right")
- field->text_direction = base::i18n::RIGHT_TO_LEFT;
+ field->text_direction = GetTextDirectionForElement(element);
field->is_enabled = element.IsEnabled();
field->is_readonly = element.IsReadOnly();
field->is_default = element.GetAttribute("value") == element.Value();
@@ -1588,9 +1585,14 @@ void WebFormControlElementToFormField(
field->properties_mask & (FieldPropertiesFlags::USER_TYPED |
FieldPropertiesFlags::AUTOFILLED)) {
const base::string16 typed_value =
- *field_value_and_properties_map->at(element).first;
-
- if (!ScriptModifiedUsernameAcceptable(value, typed_value,
+ *field_value_and_properties_map
+ ->at(element.UniqueRendererFormControlId())
+ .first;
+
+ // The typed value is preserved for all passwords. It is also preserved for
+ // potential usernames, as long as the |value| is not deemed acceptable.
+ if (field->form_control_type == "password" ||
+ !ScriptModifiedUsernameAcceptable(value, typed_value,
*field_value_and_properties_map)) {
field->typed_value = typed_value;
}
@@ -1855,7 +1857,7 @@ void PreviewForm(const FormData& form, const WebFormControlElement& element) {
}
bool ClearPreviewedFormWithElement(const WebFormControlElement& element,
- bool was_autofilled) {
+ blink::WebAutofillState old_autofill_state) {
WebFormElement form_element = element.Form();
std::vector<WebFormControlElement> control_elements;
if (form_element.IsNull()) {
@@ -1882,9 +1884,8 @@ bool ClearPreviewedFormWithElement(const WebFormControlElement& element,
!IsSelectElement(control_element))
continue;
- // If the element is not auto-filled, we did not preview it,
- // so there is nothing to reset.
- if (!control_element.IsAutofilled())
+ // Only clear previewed fields.
+ if (control_element.GetAutofillState() != WebAutofillState::kPreviewed)
continue;
if ((IsTextInput(input_element) || IsMonthInput(input_element) ||
@@ -1900,18 +1901,19 @@ bool ClearPreviewedFormWithElement(const WebFormControlElement& element,
control_element.SetSuggestedValue(WebString());
bool is_initiating_node = (element == control_element);
if (is_initiating_node) {
- control_element.SetAutofilled(was_autofilled);
// Clearing the suggested value in the focused node (above) can cause
// selection to be lost. We force selection range to restore the text
// cursor.
int length = control_element.Value().length();
control_element.SetSelectionRange(length, length);
+ control_element.SetAutofillState(old_autofill_state);
+
} else {
- control_element.SetAutofilled(false);
+ control_element.SetAutofillState(WebAutofillState::kNotFilled);
}
} else if (IsSelectElement(control_element)) {
control_element.SetSuggestedValue(WebString());
- control_element.SetAutofilled(false);
+ control_element.SetAutofillState(WebAutofillState::kNotFilled);
}
}
@@ -1981,5 +1983,67 @@ bool InferLabelForElementForTesting(const WebFormControlElement& element,
return InferLabelForElement(element, stop_words, label, label_source);
}
+WebFormElement FindFormByUniqueRendererId(WebDocument doc,
+ uint32_t form_renderer_id) {
+ blink::WebVector<WebFormElement> forms;
+ doc.Forms(forms);
+
+ for (const auto& form : forms) {
+ if (form.UniqueRendererFormId() == form_renderer_id)
+ return form;
+ }
+ return WebFormElement();
+}
+
+std::vector<WebFormControlElement> FindFormControlElementsByUniqueRendererId(
+ WebDocument doc,
+ const std::vector<uint32_t>& form_control_renderer_ids) {
+ DCHECK_LE(form_control_renderer_ids.size(), 10u)
+ << "More effective look-up should be implemented";
+ WebElementCollection elements = doc.All();
+ std::vector<WebFormControlElement> result(form_control_renderer_ids.size());
+
+ for (WebElement element = elements.FirstItem(); !element.IsNull();
+ element = elements.NextItem()) {
+ if (!element.IsFormControlElement())
+ continue;
+ WebFormControlElement control = element.To<WebFormControlElement>();
+ auto it = std::find(form_control_renderer_ids.begin(),
+ form_control_renderer_ids.end(),
+ control.UniqueRendererFormControlId());
+ if (it == form_control_renderer_ids.end())
+ continue;
+ size_t index = std::distance(form_control_renderer_ids.begin(), it);
+ result[index] = control;
+ }
+
+ return result;
+}
+
+std::vector<WebFormControlElement> FindFormControlElementsByUniqueRendererId(
+ WebDocument doc,
+ uint32_t form_renderer_id,
+ const std::vector<uint32_t>& form_control_renderer_ids) {
+ DCHECK_LE(form_control_renderer_ids.size(), 10u)
+ << "More effective look-up should be implemented";
+ std::vector<WebFormControlElement> result(form_control_renderer_ids.size());
+ WebFormElement form = FindFormByUniqueRendererId(doc, form_renderer_id);
+ if (form.IsNull())
+ return result;
+
+ WebVector<WebFormControlElement> fields;
+ form.GetFormControlElements(fields);
+ for (const auto& field : fields) {
+ auto it = std::find(form_control_renderer_ids.begin(),
+ form_control_renderer_ids.end(),
+ field.UniqueRendererFormControlId());
+ if (it == form_control_renderer_ids.end())
+ continue;
+ size_t index = std::distance(form_control_renderer_ids.begin(), it);
+ result[index] = field;
+ }
+ return result;
+}
+
} // namespace form_util
} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.h b/chromium/components/autofill/content/renderer/form_autofill_util.h
index a239c27bc82..ab69b3fbfca 100644
--- a/chromium/components/autofill/content/renderer/form_autofill_util.h
+++ b/chromium/components/autofill/content/renderer/form_autofill_util.h
@@ -60,22 +60,6 @@ enum ExtractMask {
// Copied to components/autofill/ios/browser/resources/autofill_controller.js.
extern const size_t kMaxParseableFields;
-// Create an instance of ScopedLayoutPreventer to stop form_util code from
-// triggering layout as a side-effect. For example, when creating a
-// FormFieldData, a call to WebNode::isFocusable is normally made, which may
-// trigger a layout computation. When an instance of ScopedLayoutPreventer is
-// alive, that call will not be made. On destruction, this class allows
-// layout-triggering calls again. It is not thread safe and multiple instances
-// should not be created at the same time in the same process.
-class ScopedLayoutPreventer {
- public:
- ScopedLayoutPreventer();
- ~ScopedLayoutPreventer();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ScopedLayoutPreventer);
-};
-
// Helper function that strips any authentication data, as well as query and
// ref portions of URL
GURL StripAuthAndParams(const GURL& gurl);
@@ -139,6 +123,10 @@ bool IsWebElementVisible(const blink::WebElement& element);
// attribute.
const base::string16 GetFormIdentifier(const blink::WebFormElement& form);
+// Returns text alignment for |element|.
+base::i18n::TextDirection GetTextDirectionForElement(
+ const blink::WebFormControlElement& element);
+
// Returns all the auto-fillable form control elements in |control_elements|.
std::vector<blink::WebFormControlElement> ExtractAutofillableElementsFromSet(
const blink::WebVector<blink::WebFormControlElement>& control_elements);
@@ -245,7 +233,7 @@ void PreviewForm(const FormData& form,
// autofilled state of |node| to |was_autofilled|. Returns false if the form is
// not found.
bool ClearPreviewedFormWithElement(const blink::WebFormControlElement& element,
- bool was_autofilled);
+ blink::WebAutofillState old_autofill_state);
// Checks if the webpage is empty.
// This kind of webpage is considered as empty:
@@ -288,6 +276,41 @@ bool InferLabelForElementForTesting(const blink::WebFormControlElement& element,
base::string16* label,
FormFieldData::LabelSource* label_source);
+// Returns form by unique renderer id. Return null element if there is no form
+// with given form renderer id.
+blink::WebFormElement FindFormByUniqueRendererId(blink::WebDocument doc,
+ uint32_t form_renderer_id);
+
+// Note: The vector-based API of the following two functions is a tax for limiting
+// the frequency and duration of retrieving a lot of DOM elements. Alternative
+// solutions have been discussed on https://crrev.com/c/1108201.
+
+// Returns form control elements by unique renderer id. The result has the same
+// number elements as |form_control_renderer_ids| and i-th element of the result
+// corresponds to the i-th element of |form_control_renderer_ids|.
+// |form_control_renderer_ids| is supposed to be small (<=10 elements), because
+// it is being frequently searched by linear pass over its elements.
+// The call of this function might be time expensive, because it retrieves all
+// DOM elements.
+std::vector<blink::WebFormControlElement>
+FindFormControlElementsByUniqueRendererId(
+ blink::WebDocument doc,
+ const std::vector<uint32_t>& form_control_renderer_ids);
+
+// Returns form control elements by unique renderer id from the form with unique
+// id |form_renderer_id|. The result has the same number elements as
+// |form_control_renderer_ids| and i-th element of the result corresponds to the
+// i-th element of |form_control_renderer_ids|.
+// |form_control_renderer_ids| is supposed to be small (<=10 elements), because
+// it is being frequently searched by linear pass over its elements.
+// This function is faster than the previous one, because it only retrieves form
+// control elements from a single form.
+std::vector<blink::WebFormControlElement>
+FindFormControlElementsByUniqueRendererId(
+ blink::WebDocument doc,
+ uint32_t form_renderer_id,
+ const std::vector<uint32_t>& form_control_renderer_ids);
+
} // namespace form_util
} // namespace autofill
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 f5dabe4f781..ac1f4085c65 100644
--- a/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc
+++ b/chromium/components/autofill/content/renderer/form_autofill_util_browsertest.cc
@@ -411,40 +411,90 @@ TEST_F(FormAutofillUtilsTest, IsFocusable) {
.GetElementById("name2")
.To<blink::WebFormControlElement>());
- // Computing visibility only happens if causing a layout in Blink is
- // acceptable. The first block below checks the "layout acceptable"
- // situation, the one after it the "layout to be avoided" situation.
- {
- EXPECT_TRUE(autofill::form_util::IsWebElementVisible(control_elements[0]));
- EXPECT_FALSE(autofill::form_util::IsWebElementVisible(control_elements[1]));
-
- autofill::FormData target;
- EXPECT_TRUE(
- autofill::form_util::UnownedPasswordFormElementsAndFieldSetsToFormData(
- dummy_fieldsets, control_elements, nullptr,
- web_frame->GetDocument(), nullptr,
- autofill::form_util::EXTRACT_NONE, &target, nullptr));
- ASSERT_EQ(2u, target.fields.size());
- EXPECT_EQ(base::UTF8ToUTF16("name1"), target.fields[0].name);
- EXPECT_TRUE(target.fields[0].is_focusable);
- EXPECT_EQ(base::UTF8ToUTF16("name2"), target.fields[1].name);
- EXPECT_FALSE(target.fields[1].is_focusable);
- }
- {
- autofill::form_util::ScopedLayoutPreventer preventer;
- EXPECT_TRUE(autofill::form_util::IsWebElementVisible(control_elements[0]));
- EXPECT_TRUE(autofill::form_util::IsWebElementVisible(control_elements[1]));
-
- autofill::FormData target;
- EXPECT_TRUE(
- autofill::form_util::UnownedPasswordFormElementsAndFieldSetsToFormData(
- dummy_fieldsets, control_elements, nullptr,
- web_frame->GetDocument(), nullptr,
- autofill::form_util::EXTRACT_NONE, &target, nullptr));
- ASSERT_EQ(2u, target.fields.size());
- EXPECT_EQ(base::UTF8ToUTF16("name1"), target.fields[0].name);
- EXPECT_TRUE(target.fields[0].is_focusable);
- EXPECT_EQ(base::UTF8ToUTF16("name2"), target.fields[1].name);
- EXPECT_TRUE(target.fields[1].is_focusable);
+ EXPECT_TRUE(autofill::form_util::IsWebElementVisible(control_elements[0]));
+ EXPECT_FALSE(autofill::form_util::IsWebElementVisible(control_elements[1]));
+
+ autofill::FormData target;
+ EXPECT_TRUE(
+ autofill::form_util::UnownedPasswordFormElementsAndFieldSetsToFormData(
+ dummy_fieldsets, control_elements, nullptr, web_frame->GetDocument(),
+ nullptr, autofill::form_util::EXTRACT_NONE, &target, nullptr));
+ ASSERT_EQ(2u, target.fields.size());
+ EXPECT_EQ(base::UTF8ToUTF16("name1"), target.fields[0].name);
+ EXPECT_TRUE(target.fields[0].is_focusable);
+ EXPECT_EQ(base::UTF8ToUTF16("name2"), target.fields[1].name);
+ EXPECT_FALSE(target.fields[1].is_focusable);
+}
+
+TEST_F(FormAutofillUtilsTest, FindFormByUniqueId) {
+ LoadHTML("<body><form id='form1'></form><form id='form2'></form></body>");
+ WebDocument doc = GetMainFrame()->GetDocument();
+ blink::WebVector<WebFormElement> forms;
+ doc.Forms(forms);
+
+ for (const auto& form : forms) {
+ EXPECT_EQ(form, autofill::form_util::FindFormByUniqueRendererId(
+ doc, form.UniqueRendererFormId()));
}
+
+ // Expect null form element for non-existing form id.
+ uint32_t non_existing_id = forms[0].UniqueRendererFormId() + 1000;
+ EXPECT_TRUE(
+ autofill::form_util::FindFormByUniqueRendererId(doc, non_existing_id)
+ .IsNull());
+}
+
+TEST_F(FormAutofillUtilsTest, FindFormControlElementsByUniqueIdNoForm) {
+ LoadHTML("<body><input id='i1'><input id='i2'><input id='i3'></body>");
+ WebDocument doc = GetMainFrame()->GetDocument();
+ auto input1 = doc.GetElementById("i1").To<WebInputElement>();
+ auto input3 = doc.GetElementById("i3").To<WebInputElement>();
+ uint32_t non_existing_id = input3.UniqueRendererFormControlId() + 1000;
+
+ std::vector<uint32_t> renderer_ids = {input3.UniqueRendererFormControlId(),
+ non_existing_id,
+ input1.UniqueRendererFormControlId()};
+
+ auto elements =
+ autofill::form_util::FindFormControlElementsByUniqueRendererId(
+ doc, renderer_ids);
+
+ ASSERT_EQ(3u, elements.size());
+ EXPECT_EQ(input3, elements[0]);
+ EXPECT_TRUE(elements[1].IsNull());
+ EXPECT_EQ(input1, elements[2]);
+}
+
+TEST_F(FormAutofillUtilsTest, FindFormControlElementsByUniqueIdWithForm) {
+ LoadHTML(
+ "<body><form id='f1'><input id='i1'><input id='i2'></form><input "
+ "id='i3'></body>");
+ WebDocument doc = GetMainFrame()->GetDocument();
+ auto form = doc.GetElementById("f1").To<WebFormElement>();
+ auto input1 = doc.GetElementById("i1").To<WebInputElement>();
+ auto input3 = doc.GetElementById("i3").To<WebInputElement>();
+ uint32_t non_existing_id = input3.UniqueRendererFormControlId() + 1000;
+
+ std::vector<uint32_t> renderer_ids = {input3.UniqueRendererFormControlId(),
+ non_existing_id,
+ input1.UniqueRendererFormControlId()};
+
+ auto elements =
+ autofill::form_util::FindFormControlElementsByUniqueRendererId(
+ doc, form.UniqueRendererFormId(), renderer_ids);
+
+ // |input3| is not in the form, so it shouldn't be returned.
+ ASSERT_EQ(3u, elements.size());
+ EXPECT_TRUE(elements[0].IsNull());
+ EXPECT_TRUE(elements[1].IsNull());
+ EXPECT_EQ(input1, elements[2]);
+
+ // Expect that no elements are retured for non existing form id.
+ uint32_t non_existing_form_id = form.UniqueRendererFormId() + 1000;
+ elements = autofill::form_util::FindFormControlElementsByUniqueRendererId(
+ doc, non_existing_form_id, renderer_ids);
+ ASSERT_EQ(3u, elements.size());
+ EXPECT_TRUE(elements[0].IsNull());
+ EXPECT_TRUE(elements[1].IsNull());
+ EXPECT_TRUE(elements[2].IsNull());
}
diff --git a/chromium/components/autofill/content/renderer/form_cache.cc b/chromium/components/autofill/content/renderer/form_cache.cc
index 665ebda4b05..b40e25de118 100644
--- a/chromium/components/autofill/content/renderer/form_cache.cc
+++ b/chromium/components/autofill/content/renderer/form_cache.cc
@@ -33,6 +33,7 @@
#include "third_party/blink/public/web/web_select_element.h"
#include "ui/base/l10n/l10n_util.h"
+using blink::WebAutofillState;
using blink::WebConsoleMessage;
using blink::WebDocument;
using blink::WebElement;
@@ -322,7 +323,7 @@ bool FormCache::ClearSectionWithElement(const WebFormControlElement& element) {
if (control_element.AutofillSection() != element.AutofillSection())
continue;
- control_element.SetAutofilled(false);
+ control_element.SetAutofillState(WebAutofillState::kNotFilled);
WebInputElement* input_element = ToWebInputElement(&control_element);
if (form_util::IsTextInput(input_element) ||
@@ -386,22 +387,18 @@ bool FormCache::ShowPredictions(const FormDataPredictions& form,
for (size_t i = 0; i < web_forms.size(); ++i) {
form_element = web_forms[i];
- // Note: matching on the form name here which is not guaranteed to be
- // unique for the page, nor is it guaranteed to be non-empty. Ideally,
- // we would have a way to uniquely identify the form cross-process. For
- // now, we'll check form name and form action for identity.
- // Also note that WebString() == WebString(string16()) does not evaluate
- // to |true| -- WebKit distinguishes between a "null" string (lhs) and
- // an "empty" string (rhs). We don't want that distinction, so forcing
- // to string16.
+ // To match two forms, we look for the form's name and the number of
+ // fields on that form. (Form names may not be unique.)
+ // Note: WebString() == WebString(string16()) does not evaluate to |true|
+ // -- WebKit distinguishes between a "null" string (lhs) and an "empty"
+ // string (rhs). We don't want that distinction, so forcing to string16.
base::string16 element_name = form_util::GetFormIdentifier(form_element);
- GURL action(
- form_element.GetDocument().CompleteURL(form_element.Action()));
- if (element_name == form.data.name && action == form.data.action) {
+ if (element_name == form.data.name) {
found_form = true;
control_elements =
form_util::ExtractAutofillableElementsInForm(form_element);
- break;
+ if (control_elements.size() == form.fields.size())
+ break;
}
}
diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.cc b/chromium/components/autofill/content/renderer/password_autofill_agent.cc
index ea087e2d270..900a507d742 100644
--- a/chromium/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/chromium/components/autofill/content/renderer/password_autofill_agent.cc
@@ -40,6 +40,7 @@
#include "content/public/renderer/render_view.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.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/platform/web_input_event.h"
#include "third_party/blink/public/platform/web_security_origin.h"
#include "third_party/blink/public/platform/web_vector.h"
@@ -55,6 +56,11 @@
#include "ui/events/keycodes/keyboard_codes.h"
#include "url/gurl.h"
+using blink::WebAutofillState;
+using blink::WebDocument;
+using blink::WebInputElement;
+using blink::WebFormControlElement;
+
namespace autofill {
namespace {
@@ -406,19 +412,22 @@ void UpdateFieldValueAndPropertiesMaskMap(
FieldPropertiesMask added_flags,
FieldValueAndPropertiesMaskMap* field_value_and_properties_map) {
FieldValueAndPropertiesMaskMap::iterator it =
- field_value_and_properties_map->find(element);
+ field_value_and_properties_map->find(
+ element.UniqueRendererFormControlId());
if (it != field_value_and_properties_map->end()) {
if (value)
it->second.first.reset(new base::string16(*value));
it->second.second |= added_flags;
} else {
- (*field_value_and_properties_map)[element] = std::make_pair(
- value ? std::make_unique<base::string16>(*value) : nullptr,
- added_flags);
+ (*field_value_and_properties_map)[element.UniqueRendererFormControlId()] =
+ std::make_pair(
+ value ? std::make_unique<base::string16>(*value) : nullptr,
+ added_flags);
}
// Reset USER_TYPED and AUTOFILLED flags if the value is empty.
if (value && value->empty()) {
- (*field_value_and_properties_map)[element].second &=
+ (*field_value_and_properties_map)[element.UniqueRendererFormControlId()]
+ .second &=
~(FieldPropertiesFlags::USER_TYPED | FieldPropertiesFlags::AUTOFILLED);
}
}
@@ -499,9 +508,19 @@ bool IsPublicSuffixDomainMatch(const std::string& url1,
gurl1.port() == gurl2.port();
}
-// Annotate |fields| with field signatures as HTML attributes.
+// Helper function that calculates form signature for |password_form| and
+// returns it as blink::WebString.
+blink::WebString GetFormSignatureAsWebString(
+ const PasswordForm& password_form) {
+ return blink::WebString::FromUTF8(
+ base::NumberToString(CalculateFormSignature(password_form.form_data)));
+}
+
+// Annotate |fields| with field signatures and form signature as HTML
+// attributes.
void AnnotateFieldsWithSignatures(
- std::vector<blink::WebFormControlElement>* fields) {
+ std::vector<blink::WebFormControlElement>* fields,
+ const blink::WebString& form_signature) {
for (blink::WebFormControlElement& control_element : *fields) {
FieldSignature field_signature = CalculateFieldSignatureByNameAndType(
control_element.NameForAutofill().Utf16(),
@@ -509,6 +528,9 @@ void AnnotateFieldsWithSignatures(
control_element.SetAttribute(
blink::WebString::FromASCII(kDebugAttributeForFieldSignature),
blink::WebString::FromUTF8(base::NumberToString(field_signature)));
+ control_element.SetAttribute(
+ blink::WebString::FromASCII(kDebugAttributeForFormSignature),
+ form_signature);
}
}
@@ -520,21 +542,28 @@ void AnnotateFormsAndFieldsWithSignatures(
for (blink::WebFormElement& form : *forms) {
std::unique_ptr<PasswordForm> password_form(
CreatePasswordFormFromWebForm(form, nullptr, nullptr, nullptr));
+ blink::WebString form_signature;
if (password_form) {
+ form_signature = GetFormSignatureAsWebString(*password_form);
form.SetAttribute(
blink::WebString::FromASCII(kDebugAttributeForFormSignature),
- blink::WebString::FromUTF8(base::NumberToString(
- CalculateFormSignature(password_form->form_data))));
+ form_signature);
}
std::vector<blink::WebFormControlElement> form_fields =
form_util::ExtractAutofillableElementsInForm(form);
- AnnotateFieldsWithSignatures(&form_fields);
+ AnnotateFieldsWithSignatures(&form_fields, form_signature);
}
std::vector<blink::WebFormControlElement> unowned_elements =
form_util::GetUnownedAutofillableFormFieldElements(
frame->GetDocument().All(), nullptr);
- AnnotateFieldsWithSignatures(&unowned_elements);
+ std::unique_ptr<PasswordForm> password_form(
+ CreatePasswordFormFromUnownedInputElements(*frame, nullptr, nullptr,
+ nullptr));
+ blink::WebString form_signature;
+ if (password_form)
+ form_signature = GetFormSignatureAsWebString(*password_form);
+ AnnotateFieldsWithSignatures(&unowned_elements, form_signature);
}
// Returns true iff there is a password field in |frame|.
@@ -589,18 +618,6 @@ blink::WebInputElement FindUsernameElementPrecedingPasswordElement(
return blink::WebInputElement();
}
-bool ShouldShowStandaloneManuallFallback(const blink::WebInputElement& element,
- const GURL& url) {
- return (
- element.IsPasswordFieldForAutofill() &&
- !IsCreditCardVerificationPasswordField(element) &&
- AutocompleteFlagForElement(element) != AutocompleteFlag::CREDIT_CARD &&
- !base::StartsWith(url.scheme(), "chrome", base::CompareCase::SENSITIVE) &&
- !url.SchemeIs(url::kAboutScheme) &&
- base::FeatureList::IsEnabled(
- password_manager::features::kEnableManualFallbacksFillingStandalone));
-}
-
PasswordForm::SubmissionIndicatorEvent ToSubmissionIndicatorEvent(
SubmissionSource source) {
switch (source) {
@@ -617,6 +634,13 @@ PasswordForm::SubmissionIndicatorEvent ToSubmissionIndicatorEvent(
}
}
+WebInputElement ConvertToWebInput(const WebFormControlElement& element) {
+ if (element.IsNull())
+ return WebInputElement();
+ const WebInputElement* input = blink::ToWebInputElement(&element);
+ return input ? *input : WebInputElement();
+}
+
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -631,8 +655,8 @@ PasswordAutofillAgent::PasswordAutofillAgent(
: content::RenderFrameObserver(render_frame),
last_supplied_password_info_iter_(web_input_to_password_info_.end()),
logging_state_active_(false),
- was_username_autofilled_(false),
- was_password_autofilled_(false),
+ username_autofill_state_(WebAutofillState::kNotFilled),
+ password_autofill_state_(WebAutofillState::kNotFilled),
sent_request_to_store_(false),
checked_safe_browsing_reputation_(false),
binding_(this) {
@@ -700,7 +724,7 @@ void PasswordAutofillAgent::PasswordValueGatekeeper::ShowValue(
blink::WebInputElement* element) {
if (!element->IsNull() && !element->SuggestedValue().IsEmpty()) {
element->SetAutofillValue(element->SuggestedValue());
- element->SetAutofilled(true);
+ element->SetAutofillState(WebAutofillState::kAutofilled);
}
}
@@ -708,7 +732,7 @@ bool PasswordAutofillAgent::TextDidChangeInTextField(
const blink::WebInputElement& element) {
// TODO(vabr): Get a mutable argument instead. http://crbug.com/397083
blink::WebInputElement mutable_element = element; // We need a non-const.
- mutable_element.SetAutofilled(false);
+ mutable_element.SetAutofillState(WebAutofillState::kNotFilled);
WebInputToPasswordInfoMap::iterator iter =
web_input_to_password_info_.find(element);
@@ -740,7 +764,7 @@ void PasswordAutofillAgent::UpdateStateForTextChange(
web_input_to_password_info_[iter->second].password_was_edited_last = true;
// Note that the suggested value of |mutable_element| was reset when its
// value changed.
- mutable_element.SetAutofilled(false);
+ mutable_element.SetAutofillState(WebAutofillState::kNotFilled);
}
}
@@ -773,7 +797,7 @@ bool PasswordAutofillAgent::FillSuggestion(
password_info->password_field = password_element;
}
- // Call OnFieldAutofilled before WebInputElement::SetAutofilled which may
+ // Call OnFieldAutofilled before WebInputElement::SetAutofillState which may
// cause frame closing.
if (password_generation_agent_)
password_generation_agent_->OnFieldAutofilled(password_element);
@@ -781,20 +805,10 @@ bool PasswordAutofillAgent::FillSuggestion(
if (IsUsernameAmendable(username_element,
element->IsPasswordFieldForAutofill()) &&
username_element.Value().Utf16() != username) {
- username_element.SetAutofillValue(blink::WebString::FromUTF16(username));
- username_element.SetAutofilled(true);
- UpdateFieldValueAndPropertiesMaskMap(username_element, &username,
- FieldPropertiesFlags::AUTOFILLED,
- &field_value_and_properties_map_);
+ FillField(&username_element, username);
}
- password_element.SetAutofillValue(blink::WebString::FromUTF16(password));
- password_element.SetAutofilled(true);
- UpdateFieldValueAndPropertiesMaskMap(password_element, &password,
- FieldPropertiesFlags::AUTOFILLED,
- &field_value_and_properties_map_);
- ProvisionallySavePassword(password_element.Form(), password_element,
- RESTRICTION_NONE);
+ FillPasswordFieldAndSave(&password_element, password);
blink::WebInputElement mutable_filled_element = *element;
mutable_filled_element.SetSelectionRange(element->Value().length(),
@@ -803,6 +817,47 @@ bool PasswordAutofillAgent::FillSuggestion(
return true;
}
+void PasswordAutofillAgent::FillIntoFocusedField(
+ bool is_password,
+ const base::string16& credential,
+ FillIntoFocusedFieldCallback callback) {
+ if (focused_input_element_.IsNull()) {
+ std::move(callback).Run(autofill::FillingStatus::ERROR_NO_VALID_FIELD);
+ return;
+ }
+ if (is_password) {
+ if (!focused_input_element_.IsPasswordFieldForAutofill()) {
+ std::move(callback).Run(autofill::FillingStatus::ERROR_NOT_ALLOWED);
+ return;
+ }
+ FillPasswordFieldAndSave(&focused_input_element_, credential);
+ } else {
+ FillField(&focused_input_element_, credential);
+ }
+ std::move(callback).Run(autofill::FillingStatus::SUCCESS);
+}
+
+void PasswordAutofillAgent::FillField(blink::WebInputElement* input,
+ const base::string16& credential) {
+ DCHECK(input);
+ DCHECK(!input->IsNull());
+ input->SetAutofillValue(blink::WebString::FromUTF16(credential));
+ input->SetAutofillState(WebAutofillState::kAutofilled);
+ UpdateFieldValueAndPropertiesMaskMap(
+ *input, &credential, FieldPropertiesFlags::AUTOFILLED_ON_USER_TRIGGER,
+ &field_value_and_properties_map_);
+}
+
+void PasswordAutofillAgent::FillPasswordFieldAndSave(
+ blink::WebInputElement* password_input,
+ const base::string16& credential) {
+ DCHECK(password_input);
+ DCHECK(password_input->IsPasswordFieldForAutofill());
+ FillField(password_input, credential);
+ ProvisionallySavePassword(password_input->Form(), *password_input,
+ RESTRICTION_NONE);
+}
+
bool PasswordAutofillAgent::PreviewSuggestion(
const blink::WebFormControlElement& control_element,
const blink::WebString& username,
@@ -827,15 +882,15 @@ bool PasswordAutofillAgent::PreviewSuggestion(
if (username_query_prefix_.empty())
username_query_prefix_ = username_element.Value().Utf16();
- was_username_autofilled_ = username_element.IsAutofilled();
+ username_autofill_state_ = username_element.GetAutofillState();
username_element.SetSuggestedValue(username);
- username_element.SetAutofilled(true);
+ username_element.SetAutofillState(WebAutofillState::kPreviewed);
form_util::PreviewSuggestion(username_element.SuggestedValue().Utf16(),
username_query_prefix_, &username_element);
}
- was_password_autofilled_ = password_element.IsAutofilled();
+ password_autofill_state_ = password_element.GetAutofillState();
password_element.SetSuggestedValue(password);
- password_element.SetAutofilled(true);
+ password_element.SetAutofillState(WebAutofillState::kPreviewed);
return true;
}
@@ -969,11 +1024,6 @@ bool PasswordAutofillAgent::ShowSuggestions(
frame_url);
}
#endif
- if (!generation_popup_showing && !blacklisted_form_found_ &&
- ShouldShowStandaloneManuallFallback(element, frame_url) &&
- ShowManualFallbackSuggestion(element)) {
- return true;
- }
}
return false;
}
@@ -1194,10 +1244,6 @@ void PasswordAutofillAgent::SendPasswordForms(bool only_visible) {
}
void PasswordAutofillAgent::DidFinishDocumentLoad() {
- // The |frame| contents have been parsed, but not yet rendered. Let the
- // PasswordManager know that forms are loaded, even though we can't yet tell
- // whether they're visible.
- form_util::ScopedLayoutPreventer layout_preventer;
SendPasswordForms(false);
}
@@ -1296,6 +1342,32 @@ void PasswordAutofillAgent::OnWillSubmitForm(
}
}
+void PasswordAutofillAgent::FocusedNodeChanged(const blink::WebNode& node) {
+ focused_input_element_.Reset();
+
+ if (node.IsNull() || // |node| is null <==> focus outside of frame.
+ !node.IsElementNode()) { // Not a valid blink::WebElement.
+ GetPasswordManagerDriver()->FocusedInputChanged(
+ /*is_fillable=*/false, /*is_password_field=*/false);
+ return;
+ }
+
+ blink::WebElement web_element = node.ToConst<blink::WebElement>();
+ const WebInputElement* input = ToWebInputElement(&web_element);
+ if (!input) {
+ GetPasswordManagerDriver()->FocusedInputChanged(
+ /*is_fillable=*/false, /*is_password_field=*/false);
+ return; // If the node isn't an element, don't even try to convert.
+ }
+ bool is_password = false;
+ bool is_fillable = input->IsTextField() && IsElementEditable(*input);
+ if (is_fillable) {
+ focused_input_element_ = *input;
+ is_password = focused_input_element_.IsPasswordFieldForAutofill();
+ }
+ GetPasswordManagerDriver()->FocusedInputChanged(is_fillable, is_password);
+}
+
void PasswordAutofillAgent::OnDestruct() {
binding_.Close();
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
@@ -1351,10 +1423,38 @@ void PasswordAutofillAgent::OnProbablyFormSubmitted() {
}
}
+void PasswordAutofillAgent::FillUsingRendererIDs(
+ int key,
+ const PasswordFormFillData& form_data) {
+ std::unique_ptr<RendererSavePasswordProgressLogger> logger;
+ if (logging_state_active_) {
+ logger.reset(new RendererSavePasswordProgressLogger(
+ GetPasswordManagerDriver().get()));
+ logger->LogMessage(Logger::STRING_ON_FILL_PASSWORD_FORM_METHOD);
+ }
+ WebInputElement username_element, password_element;
+ std::tie(username_element, password_element) =
+ FindUsernamePasswordElements(form_data);
+ if (password_element.IsNull()) {
+ MaybeStoreFallbackData(key, form_data);
+ return;
+ }
+
+ StoreDataForFillOnAccountSelect(key, form_data, username_element,
+ password_element);
+ FillFormOnPasswordReceived(form_data, username_element, password_element,
+ &field_value_and_properties_map_, logger.get());
+}
+
// mojom::PasswordAutofillAgent:
void PasswordAutofillAgent::FillPasswordForm(
int key,
const PasswordFormFillData& form_data) {
+ if (form_data.has_renderer_ids) {
+ FillUsingRendererIDs(key, form_data);
+ return;
+ }
+
std::vector<blink::WebInputElement> elements;
std::unique_ptr<RendererSavePasswordProgressLogger> logger;
if (logging_state_active_) {
@@ -1380,8 +1480,6 @@ void PasswordAutofillAgent::FillPasswordForm(
FillFormOnPasswordReceived(
form_data, username_element, password_element,
&field_value_and_properties_map_,
- base::Bind(&PasswordValueGatekeeper::RegisterElement,
- base::Unretained(&gatekeeper_)),
logger.get());
}
}
@@ -1448,33 +1546,13 @@ void PasswordAutofillAgent::GetFillableElementFromFormData(
blink::WebInputElement main_element =
username_element.IsNull() ? password_element : username_element;
-
- PasswordInfo password_info;
- password_info.fill_data = form_data;
- password_info.key = key;
- password_info.password_field = password_element;
- web_input_to_password_info_[main_element] = password_info;
- last_supplied_password_info_iter_ =
- web_input_to_password_info_.find(main_element);
- if (!main_element.IsPasswordFieldForAutofill())
- password_to_username_[password_element] = username_element;
if (elements)
elements->push_back(main_element);
+ StoreDataForFillOnAccountSelect(key, form_data, username_element,
+ password_element);
}
- // This is a fallback, if for some reasons elements for filling were not found
- // (for example because they were renamed by JavaScript) then add fill data
- // for |web_input_to_password_info_|. When the user clicks on a password
- // field which is not a key in |web_input_to_password_info_|, the first
- // element from |web_input_to_password_info_| will be used in
- // PasswordAutofillAgent::FindPasswordInfoForElement to propose to fill.
- if (web_input_to_password_info_.empty()) {
- PasswordInfo password_info;
- password_info.fill_data = form_data;
- password_info.key = key;
- web_input_to_password_info_[blink::WebInputElement()] = password_info;
- last_supplied_password_info_iter_ = web_input_to_password_info_.begin();
- }
+ MaybeStoreFallbackData(key, form_data);
}
void PasswordAutofillAgent::FocusedNodeHasChanged(const blink::WebNode& node) {
@@ -1556,10 +1634,6 @@ void PasswordAutofillAgent::FindFocusedPasswordForm(
std::move(callback).Run(*password_form);
}
-void PasswordAutofillAgent::BlacklistedFormFound() {
- blacklisted_form_found_ = true;
-}
-
////////////////////////////////////////////////////////////////////////////////
// PasswordAutofillAgent, private:
@@ -1604,33 +1678,19 @@ bool PasswordAutofillAgent::ShowSuggestionPopup(
return CanShowSuggestion(password_info.fill_data, username_string, show_all);
}
-bool PasswordAutofillAgent::ShowManualFallbackSuggestion(
- const blink::WebInputElement& element) {
- if (!element.Value().IsEmpty()) {
- HidePopup();
- return false;
- }
-
- FormData form;
- FormFieldData field;
- form_util::FindFormAndFieldForFormControlElement(element, &form, &field);
- GetPasswordManagerDriver()->ShowManualFallbackSuggestion(
- field.text_direction,
- render_frame()->GetRenderView()->ElementBoundsInWindow(element));
- return true;
-}
-
void PasswordAutofillAgent::FrameClosing() {
- for (auto const& iter : web_input_to_password_info_) {
- password_to_username_.erase(iter.second.password_field);
- }
web_input_to_password_info_.clear();
+ password_to_username_.clear();
+ last_supplied_password_info_iter_ = web_input_to_password_info_.end();
provisionally_saved_form_.Reset();
field_value_and_properties_map_.clear();
- username_detector_cache_.clear();
+ username_autofill_state_ = WebAutofillState::kNotFilled;
+ password_autofill_state_ = WebAutofillState::kNotFilled;
sent_request_to_store_ = false;
checked_safe_browsing_reputation_ = false;
- blacklisted_form_found_ = false;
+ username_query_prefix_.clear();
+ form_predictions_.clear();
+ username_detector_cache_.clear();
#if !defined(OS_ANDROID) && !defined(OS_IOS)
page_passwords_analyser_.Reset();
#endif
@@ -1641,13 +1701,13 @@ void PasswordAutofillAgent::ClearPreview(
blink::WebInputElement* password) {
if (!username->IsNull() && !username->SuggestedValue().IsEmpty()) {
username->SetSuggestedValue(blink::WebString());
- username->SetAutofilled(was_username_autofilled_);
+ username->SetAutofillState(username_autofill_state_);
username->SetSelectionRange(username_query_prefix_.length(),
username->Value().length());
}
if (!password->SuggestedValue().IsEmpty()) {
password->SetSuggestedValue(blink::WebString());
- password->SetAutofilled(was_password_autofilled_);
+ password->SetAutofillState(password_autofill_state_);
}
}
void PasswordAutofillAgent::ProvisionallySavePassword(
@@ -1688,9 +1748,8 @@ bool PasswordAutofillAgent::FillUserNameAndPassword(
blink::WebInputElement* password_element,
const PasswordFormFillData& fill_data,
bool exact_username_match,
- bool set_selection,
+ bool username_may_use_prefilled_placeholder,
FieldValueAndPropertiesMaskMap* field_value_and_properties_map,
- base::Callback<void(blink::WebInputElement*)> registration_callback,
RendererSavePasswordProgressLogger* logger) {
if (logger)
logger->LogMessage(Logger::STRING_FILL_USERNAME_AND_PASSWORD_METHOD);
@@ -1710,23 +1769,28 @@ bool PasswordAutofillAgent::FillUserNameAndPassword(
// not autocompletable (no username case).
base::string16 current_username;
- // Whether the username element was prefilled with content that was not on a
- // list of known placeholder texts (e.g. "username or email").
- bool prefilled_not_placeholder_username = false;
+ // Whether the username element was prefilled with content that was on a
+ // list of known placeholder texts that should be overridden (e.g. "username
+ // or email" or there is a server hint that it is just a placeholder).
+ bool prefilled_placeholder_username = false;
if (!username_element->IsNull()) {
+ prefilled_placeholder_username =
+ !username_element->Value().IsEmpty() &&
+ (PossiblePrefilledUsernameValue(username_element->Value().Utf8()) ||
+ username_may_use_prefilled_placeholder);
if (!username_element->Value().IsEmpty() &&
- !PossiblePrefilledUsernameValue(username_element->Value().Utf8())) {
+ !prefilled_placeholder_username) {
// Username is filled with content that was not on a list of known
- // placeholder texts (e.g. "username or email").
+ // placeholder texts (e.g. "username or email") nor there is server-side
+ // data that this value is placeholder.
current_username = username_element->Value().Utf16();
- prefilled_not_placeholder_username = true;
} else if (IsElementAutocompletable(*username_element)) {
current_username = fill_data.username_field.value;
}
}
- // username and password will contain the match found if any.
+ // |username| and |password| will contain the match found if any.
base::string16 username;
base::string16 password;
@@ -1734,14 +1798,15 @@ bool PasswordAutofillAgent::FillUserNameAndPassword(
logger, &username, &password);
if (password.empty()) {
- if (prefilled_not_placeholder_username) {
+ if (!username_element->IsNull() && !username_element->Value().IsEmpty() &&
+ !prefilled_placeholder_username) {
LogPrefilledUsernameFillOutcome(
PrefilledUsernameFillOutcome::kPrefilledUsernameNotOverridden);
}
return false;
}
- // Call OnFieldAutofilled before WebInputElement::SetAutofilled which may
+ // Call OnFieldAutofilled before WebInputElement::SetAutofillState which may
// cause frame closing.
if (password_generation_agent_)
password_generation_agent_->OnFieldAutofilled(*password_element);
@@ -1749,34 +1814,25 @@ bool PasswordAutofillAgent::FillUserNameAndPassword(
// Input matches the username, fill in required values.
if (!username_element->IsNull() &&
IsElementAutocompletable(*username_element)) {
- // Fill a non-empty username if it is safe to override the value of the
- // username element. It is safe to override if the value is empty or a known
- // placeholder value.
- if (!username.empty()) {
- if (username_element->Value().IsEmpty()) {
- username_element->SetSuggestedValue(
- blink::WebString::FromUTF16(username));
- registration_callback.Run(username_element);
- } else if (PossiblePrefilledUsernameValue(
- username_element->Value().Utf8())) {
- username_element->SetSuggestedValue(
- blink::WebString::FromUTF16(username));
- registration_callback.Run(username_element);
+ if (!username.empty() && (username_element->Value().IsEmpty() ||
+ prefilled_placeholder_username)) {
+ username_element->SetSuggestedValue(
+ blink::WebString::FromUTF16(username));
+ gatekeeper_.RegisterElement(username_element);
+ if (prefilled_placeholder_username) {
LogPrefilledUsernameFillOutcome(
PrefilledUsernameFillOutcome::
kPrefilledPlaceholderUsernameOverridden);
}
}
- UpdateFieldValueAndPropertiesMaskMap(*username_element, &username,
- FieldPropertiesFlags::AUTOFILLED,
- field_value_and_properties_map);
- username_element->SetAutofilled(true);
+
+ UpdateFieldValueAndPropertiesMaskMap(
+ *username_element, &username,
+ FieldPropertiesFlags::AUTOFILLED_ON_PAGELOAD,
+ field_value_and_properties_map);
+ username_element->SetAutofillState(WebAutofillState::kAutofilled);
if (logger)
logger->LogElementName(Logger::STRING_USERNAME_FILLED, *username_element);
- if (set_selection) {
- form_util::PreviewSuggestion(username, current_username,
- username_element);
- }
}
// Wait to fill in the password until a user gesture occurs. This is to make
@@ -1784,13 +1840,14 @@ bool PasswordAutofillAgent::FillUserNameAndPassword(
// user is intentionally interacting with the page.
if (password_element->Value().Utf16() != password)
password_element->SetSuggestedValue(blink::WebString::FromUTF16(password));
- UpdateFieldValueAndPropertiesMaskMap(*password_element, &password,
- FieldPropertiesFlags::AUTOFILLED,
- field_value_and_properties_map);
+ UpdateFieldValueAndPropertiesMaskMap(
+ *password_element, &password,
+ FieldPropertiesFlags::AUTOFILLED_ON_PAGELOAD,
+ field_value_and_properties_map);
ProvisionallySavePassword(password_element->Form(), *password_element,
RESTRICTION_NONE);
- registration_callback.Run(password_element);
- password_element->SetAutofilled(true);
+ gatekeeper_.RegisterElement(password_element);
+ password_element->SetAutofillState(WebAutofillState::kAutofilled);
if (logger)
logger->LogElementName(Logger::STRING_PASSWORD_FILLED, *password_element);
@@ -1811,7 +1868,6 @@ bool PasswordAutofillAgent::FillFormOnPasswordReceived(
blink::WebInputElement username_element,
blink::WebInputElement password_element,
FieldValueAndPropertiesMaskMap* field_value_and_properties_map,
- base::Callback<void(blink::WebInputElement*)> registration_callback,
RendererSavePasswordProgressLogger* logger) {
// Do not fill if the password field is in a chain of iframes not having
// identical origin.
@@ -1839,8 +1895,8 @@ bool PasswordAutofillAgent::FillFormOnPasswordReceived(
// match for read-only username fields.
return FillUserNameAndPassword(
&username_element, &password_element, fill_data, exact_username_match,
- false /* set_selection */, field_value_and_properties_map,
- registration_callback, logger);
+ fill_data.username_may_use_prefilled_placeholder,
+ field_value_and_properties_map, logger);
}
void PasswordAutofillAgent::OnProvisionallySaveForm(
@@ -1903,14 +1959,92 @@ void PasswordAutofillAgent::HidePopup() {
}
}
-const mojom::PasswordManagerDriverPtr&
+const mojom::PasswordManagerDriverAssociatedPtr&
PasswordAutofillAgent::GetPasswordManagerDriver() {
if (!password_manager_driver_) {
- render_frame()->GetRemoteInterfaces()->GetInterface(
- mojo::MakeRequest(&password_manager_driver_));
+ render_frame()->GetRemoteAssociatedInterfaces()->GetInterface(
+ &password_manager_driver_);
}
-
return password_manager_driver_;
}
+std::pair<WebInputElement, WebInputElement>
+PasswordAutofillAgent::FindUsernamePasswordElements(
+ const PasswordFormFillData& form_data) {
+ const uint32_t username_renderer_id =
+ form_data.username_field.unique_renderer_id;
+ const uint32_t password_renderer_id =
+ form_data.password_field.unique_renderer_id;
+ const bool is_username_present =
+ username_renderer_id != FormFieldData::kNotSetFormControlRendererId;
+ const bool is_password_present =
+ password_renderer_id != FormFieldData::kNotSetFormControlRendererId;
+
+ std::vector<uint32_t> element_ids;
+ if (is_password_present)
+ element_ids.push_back(password_renderer_id);
+ if (is_username_present)
+ element_ids.push_back(username_renderer_id);
+
+ WebDocument doc = render_frame()->GetWebFrame()->GetDocument();
+ bool wrapped_in_form_tag =
+ form_data.form_renderer_id != FormData::kNotSetFormRendererId;
+ std::vector<WebFormControlElement> elements =
+ wrapped_in_form_tag
+ ? form_util::FindFormControlElementsByUniqueRendererId(
+ doc, form_data.form_renderer_id, element_ids)
+ : form_util::FindFormControlElementsByUniqueRendererId(doc,
+ element_ids);
+
+ // Set password element.
+ WebInputElement password_field;
+ size_t current_index = 0;
+ if (is_password_present)
+ password_field = ConvertToWebInput(elements[current_index++]);
+
+ // Set username element.
+ WebInputElement username_field;
+ if (is_username_present)
+ username_field = ConvertToWebInput(elements[current_index++]);
+
+ return std::make_pair(username_field, password_field);
+}
+
+void PasswordAutofillAgent::StoreDataForFillOnAccountSelect(
+ int key,
+ const PasswordFormFillData& form_data,
+ WebInputElement username_element,
+ WebInputElement password_element) {
+ WebInputElement main_element =
+ username_element.IsNull() ? password_element : username_element;
+
+ PasswordInfo password_info;
+ password_info.fill_data = form_data;
+ password_info.key = key;
+ password_info.password_field = password_element;
+ web_input_to_password_info_[main_element] = password_info;
+ last_supplied_password_info_iter_ =
+ web_input_to_password_info_.find(main_element);
+ if (!main_element.IsPasswordFieldForAutofill())
+ password_to_username_[password_element] = username_element;
+}
+
+void PasswordAutofillAgent::MaybeStoreFallbackData(
+ int key,
+ const PasswordFormFillData& form_data) {
+ if (!web_input_to_password_info_.empty())
+ return;
+ // If for some reasons elements for filling were not found (for example
+ // because they were renamed by JavaScript) then add fill data for
+ // |web_input_to_password_info_|. When the user clicks on a password field
+ // which is not a key in |web_input_to_password_info_|, the first element from
+ // |web_input_to_password_info_| will be used in
+ // PasswordAutofillAgent::FindPasswordInfoForElement to propose to fill.
+ PasswordInfo password_info;
+ password_info.fill_data = form_data;
+ password_info.key = key;
+ web_input_to_password_info_[WebInputElement()] = password_info;
+ last_supplied_password_info_iter_ = web_input_to_password_info_.begin();
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.h b/chromium/components/autofill/content/renderer/password_autofill_agent.h
index 228907d12ce..a284a03bb42 100644
--- a/chromium/components/autofill/content/renderer/password_autofill_agent.h
+++ b/chromium/components/autofill/content/renderer/password_autofill_agent.h
@@ -81,17 +81,19 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
void SetPasswordGenerationAgent(PasswordGenerationAgent* generation_agent);
- const mojom::PasswordManagerDriverPtr& GetPasswordManagerDriver();
+ const mojom::PasswordManagerDriverAssociatedPtr& GetPasswordManagerDriver();
// mojom::PasswordAutofillAgent:
void FillPasswordForm(int key,
const PasswordFormFillData& form_data) override;
+ void FillIntoFocusedField(bool is_password,
+ const base::string16& credential,
+ FillIntoFocusedFieldCallback callback) override;
void SetLoggingState(bool active) override;
void AutofillUsernameAndPasswordDataReceived(
const FormsPredictionsMap& predictions) override;
void FindFocusedPasswordForm(
FindFocusedPasswordFormCallback callback) override;
- void BlacklistedFormFound() override;
// FormTracker::Observer
void OnProvisionallySaveForm(const blink::WebFormElement& form,
@@ -101,7 +103,7 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
void OnFormSubmitted(const blink::WebFormElement& form) override;
void OnInferredFormSubmission(SubmissionSource source) override;
- // WebFrameClient editor related calls forwarded by AutofillAgent.
+ // WebLocalFrameClient editor related calls forwarded by AutofillAgent.
// If they return true, it indicates the event was consumed and should not
// be used for any other autofill activity.
bool TextDidChangeInTextField(const blink::WebInputElement& element);
@@ -149,11 +151,6 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
// on a non-secure page.
void ShowNotSecureWarning(const blink::WebInputElement& element);
- // Shows an Autofill-style popup with an option to go to settings and check
- // all saved passwords. Returns true if the suggestion was shown, false
- // otherwise.
- bool ShowManualFallbackSuggestion(const blink::WebInputElement& element);
-
// Called when new form controls are inserted.
void OnDynamicFormsSeen();
@@ -208,11 +205,10 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
// The key under which PasswordAutofillManager can find info for filling.
int key = -1;
};
- typedef std::map<blink::WebInputElement, PasswordInfo>
- WebInputToPasswordInfoMap;
- typedef std::map<blink::WebElement, int> WebElementToPasswordInfoKeyMap;
- typedef std::map<blink::WebInputElement, blink::WebInputElement>
- PasswordToLoginMap;
+ using WebInputToPasswordInfoMap =
+ std::map<blink::WebInputElement, PasswordInfo>;
+ using PasswordToLoginMap =
+ std::map<blink::WebInputElement, blink::WebInputElement>;
// This class keeps track of autofilled password input elements and makes sure
// the autofilled password value is not accessible to JavaScript code until
@@ -251,6 +247,7 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
void WillCommitProvisionalLoad() override;
void DidCommitProvisionalLoad(bool is_new_navigation,
bool is_same_document_navigation) override;
+ void FocusedNodeChanged(const blink::WebNode& node) override;
void OnDestruct() override;
// Scans the given frame for password forms and sends them up to the browser.
@@ -288,6 +285,16 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
void ClearPreview(blink::WebInputElement* username,
blink::WebInputElement* password);
+ // Checks that a given input field is valid before filling the given |input|
+ // with the given |credential| and marking the field as auto-filled.
+ void FillField(blink::WebInputElement* input,
+ const base::string16& credential);
+
+ // Uses |FillField| to fill the given |credential| into the |password_input|.
+ // Saves the password for its associated form.
+ void FillPasswordFieldAndSave(blink::WebInputElement* password_input,
+ const base::string16& credential);
+
// Saves |form| and |input| in |provisionally_saved_form_|, as long as it
// satisfies |restriction|. |form| and |input| are the elements user has just
// been interacting with before the form save. |form| or |input| can be null
@@ -299,19 +306,18 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
// This function attempts to fill |username_element| and |password_element|
// with values from |fill_data|. The |username_element| and |password_element|
- // will only have the suggestedValue set, and will be registered for copying
- // that to the real value through |registration_callback|. If a match is
- // found, return true and |field_value_and_properties_map| will be modified
- // with the autofilled credentials and |FieldPropertiesFlags::AUTOFILLED|
- // flag.
+ // will only have the suggestedValue set. If a match is found, return true and
+ // |field_value_and_properties_map| will be modified with the autofilled
+ // credentials and |FieldPropertiesFlags::AUTOFILLED| flag.
+ // If |username_may_use_prefilled_placeholder| then this function may
+ // overwrite the value of username field.
bool FillUserNameAndPassword(
blink::WebInputElement* username_element,
blink::WebInputElement* password_element,
const PasswordFormFillData& fill_data,
bool exact_username_match,
- bool set_selection,
+ bool username_may_use_prefilled_placeholder,
FieldValueAndPropertiesMaskMap* field_value_and_properties_map,
- base::Callback<void(blink::WebInputElement*)> registration_callback,
RendererSavePasswordProgressLogger* logger);
// Logs whether a username value that was prefilled by the website was
@@ -324,15 +330,12 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
// unless the |username_element| already has a value set. In that case,
// attempts to fill the password matching the already filled username, if
// such a password exists. The |password_element| will have the
- // |suggestedValue| set, and |suggestedValue| will be registered for copying
- // to the real value through |registration_callback|. Returns true if the
- // password is filled.
+ // |suggestedValue| set. Returns true if the password is filled.
bool FillFormOnPasswordReceived(
const PasswordFormFillData& fill_data,
blink::WebInputElement username_element,
blink::WebInputElement password_element,
FieldValueAndPropertiesMaskMap* field_value_and_properties_map,
- base::Callback<void(blink::WebInputElement*)> registration_callback,
RendererSavePasswordProgressLogger* logger);
// Helper function called when form submission is successful.
@@ -344,6 +347,30 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
void HidePopup();
+ // TODO(https://crbug.com/831123): Rename to FillPasswordForm when browser
+ // form parsing is launched.
+ void FillUsingRendererIDs(int key, const PasswordFormFillData& form_data);
+
+ // Returns pair(username_element, password_element) based on renderer ids from
+ // |username_field| and |password_field| from |form_data|.
+ std::pair<blink::WebInputElement, blink::WebInputElement>
+ FindUsernamePasswordElements(const PasswordFormFillData& form_data);
+
+ // Populates |web_input_to_password_info_| and |password_to_username_| in
+ // order to provide fill on account select on |username_element| and
+ // |password_element| with credentials from |form_data|.
+ void StoreDataForFillOnAccountSelect(int key,
+ const PasswordFormFillData& form_data,
+ blink::WebInputElement username_element,
+ blink::WebInputElement password_element);
+
+ // In case when |web_input_to_password_info_| is empty (i.e. no fill on
+ // account select data yet) this function populates
+ // |web_input_to_password_info_| in order to provide fill on account select on
+ // any password field (aka filling fallback) with credentials from
+ // |form_data|.
+ void MaybeStoreFallbackData(int key, const PasswordFormFillData& form_data);
+
// The logins we have filled so far with their associated info.
WebInputToPasswordInfoMap web_input_to_password_info_;
// A (sort-of) reverse map to |web_input_to_password_info_|.
@@ -365,13 +392,19 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
PasswordValueGatekeeper gatekeeper_;
+ // The currently focused input field. Not null if its a valid input that can
+ // be filled with a suggestions.
+ blink::WebInputElement focused_input_element_;
+
// True indicates that user debug information should be logged.
bool logging_state_active_;
- // True indicates that the username field was autofilled, false otherwise.
- bool was_username_autofilled_;
- // True indicates that the password field was autofilled, false otherwise.
- bool was_password_autofilled_;
+ // Indicates whether the field is filled, previewed, or not filled by
+ // autofill.
+ blink::WebAutofillState username_autofill_state_;
+ // Indicates whether the field is filled, previewed, or not filled by
+ // autofill.
+ blink::WebAutofillState password_autofill_state_;
// True indicates that a request for credentials has been sent to the store.
bool sent_request_to_store_;
@@ -398,14 +431,11 @@ class PasswordAutofillAgent : public content::RenderFrameObserver,
PagePasswordsAnalyser page_passwords_analyser_;
#endif
- mojom::PasswordManagerDriverPtr password_manager_driver_;
+ mojom::PasswordManagerDriverAssociatedPtr password_manager_driver_;
mojo::Binding<mojom::PasswordAutofillAgent> binding_;
- bool blacklisted_form_found_ = false;
-
bool prefilled_username_metrics_logged_ = false;
-
DISALLOW_COPY_AND_ASSIGN(PasswordAutofillAgent);
};
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 1cd2c4e812e..68e02e38c21 100644
--- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc
+++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -10,7 +10,6 @@
#include <set>
#include <string>
-#include "base/containers/flat_set.h"
#include "base/i18n/case_conversion.h"
#include "base/lazy_instance.h"
#include "base/macros.h"
@@ -82,7 +81,7 @@ AutocompleteFlag ExtractAutocompleteFlag(const std::string& attribute) {
return cc_seen ? AutocompleteFlag::CREDIT_CARD : AutocompleteFlag::NONE;
}
-// Helper to spare map::find boilerplate when caching element's autocomplete
+// Helper to spare map::find boilerplate when caching field's autocomplete
// attributes.
class AutocompleteCache {
public:
@@ -90,18 +89,18 @@ class AutocompleteCache {
~AutocompleteCache();
- // Computes and stores the AutocompleteFlag for |element| based on its
+ // Computes and stores the AutocompleteFlag for |field| based on its
// autocomplete attribute. Note that this cannot be done on-demand during
// RetrieveFor, because the cache spares space and look-up time by not storing
// AutocompleteFlag::NONE values, hence for all elements without an
// autocomplete attribute, every retrieval would result in a new computation.
- void Store(const WebInputElement& element);
+ void Store(const FormFieldData* field);
- // Retrieves the value previously stored for |element|.
- AutocompleteFlag RetrieveFor(const WebInputElement& element) const;
+ // Retrieves the value previously stored for |field|.
+ AutocompleteFlag RetrieveFor(const FormFieldData* field) const;
private:
- std::map<WebInputElement, AutocompleteFlag> cache_;
+ std::map<const FormFieldData*, AutocompleteFlag> cache_;
DISALLOW_COPY_AND_ASSIGN(AutocompleteCache);
};
@@ -110,18 +109,19 @@ AutocompleteCache::AutocompleteCache() = default;
AutocompleteCache::~AutocompleteCache() = default;
-void AutocompleteCache::Store(const WebInputElement& element) {
- const AutocompleteFlag flag = AutocompleteFlagForElement(element);
+void AutocompleteCache::Store(const FormFieldData* field) {
+ const AutocompleteFlag flag =
+ ExtractAutocompleteFlag(field->autocomplete_attribute);
// Only store non-trivial flags. Most of the elements will have the NONE
// value, so spare storage and lookup time by assuming anything not stored in
// |cache_| has the NONE flag.
if (flag != AutocompleteFlag::NONE)
- cache_[element] = flag;
+ cache_[field] = flag;
}
AutocompleteFlag AutocompleteCache::RetrieveFor(
- const WebInputElement& element) const {
- auto it = cache_.find(element);
+ const FormFieldData* field) const {
+ auto it = cache_.find(field);
if (it == cache_.end())
return AutocompleteFlag::NONE;
return it->second;
@@ -139,29 +139,6 @@ enum class FieldFilteringLevel {
USER_INPUT = 2
};
-// PasswordForms can be constructed for both WebFormElements and for collections
-// of WebInputElements that are not in a WebFormElement. This intermediate
-// aggregating structure is provided so GetPasswordForm() only has one
-// view of the underlying data, regardless of its origin.
-struct SyntheticForm {
- SyntheticForm();
- SyntheticForm(SyntheticForm&& other);
- ~SyntheticForm();
-
- // Contains control elements of the represented form, including not fillable
- // ones.
- std::vector<blink::WebFormControlElement> control_elements;
- // The origin of the containing document.
- GURL origin;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(SyntheticForm);
-};
-
-SyntheticForm::SyntheticForm() = default;
-SyntheticForm::SyntheticForm(SyntheticForm&& other) = default;
-SyntheticForm::~SyntheticForm() = default;
-
// Layout classification of password forms
// A layout sequence of a form is the sequence of it's non-password and password
// input fields, represented by "N" and "P", respectively. A form like this
@@ -200,18 +177,6 @@ struct LoginAndSignupLazyInstanceTraits
base::LazyInstance<re2::RE2, LoginAndSignupLazyInstanceTraits>
g_login_and_signup_matcher = LAZY_INSTANCE_INITIALIZER;
-// Return a pointer to WebInputElement iff |control_element| is an enabled text
-// input element. Otherwise, returns nullptr.
-const WebInputElement* GetEnabledTextInputFieldOrNull(
- const WebFormControlElement& control_element) {
- const WebInputElement* input_element = ToWebInputElement(&control_element);
- if (input_element && input_element->IsEnabled() &&
- input_element->IsTextField()) {
- return input_element;
- }
- return nullptr;
-}
-
// Given the sequence of non-password and password text input fields of a form,
// represented as a string of Ns (non-password) and Ps (password), computes the
// layout type of that form.
@@ -225,48 +190,30 @@ PasswordForm::Layout SequenceToLayout(base::StringPiece layout_sequence) {
return PasswordForm::Layout::LAYOUT_OTHER;
}
-void PopulateSyntheticFormFromWebForm(const WebFormElement& web_form,
- SyntheticForm* synthetic_form) {
- // TODO(vabr): The fact that we are actually passing all form fields, not just
- // autofillable ones (cause of http://crbug.com/537396, see also
- // http://crbug.com/543006) is not tested yet, due to difficulties to fake
- // test frame origin to match GAIA login page. Once this code gets moved to
- // browser, we need to add tests for this as well.
- blink::WebVector<blink::WebFormControlElement> web_control_elements;
- web_form.GetFormControlElements(web_control_elements);
- synthetic_form->control_elements.assign(web_control_elements.begin(),
- web_control_elements.end());
- synthetic_form->origin =
- form_util::GetCanonicalOriginForDocument(web_form.GetDocument());
-}
-
// Helper to determine which password is the main (current) one, and which is
// the new password (e.g., on a sign-up or change password form), if any. If the
// new password is found and there is another password field with the same user
// input, the function also sets |confirmation_password| to this field.
-void LocateSpecificPasswords(std::vector<WebInputElement> passwords,
- WebInputElement* current_password,
- WebInputElement* new_password,
- WebInputElement* confirmation_password,
+void LocateSpecificPasswords(std::vector<const FormFieldData*> passwords,
+ const FormFieldData** current_password,
+ const FormFieldData** new_password,
+ const FormFieldData** confirmation_password,
const AutocompleteCache& autocomplete_cache) {
DCHECK(!passwords.empty());
- DCHECK(current_password && current_password->IsNull());
- DCHECK(new_password && new_password->IsNull());
- DCHECK(confirmation_password && confirmation_password->IsNull());
+ DCHECK(current_password && !*current_password);
+ DCHECK(new_password && !*new_password);
+ DCHECK(confirmation_password && !*confirmation_password);
// First, look for elements marked with either autocomplete='current-password'
// or 'new-password' -- if we find any, take the hint, and treat the first of
// each kind as the element we are looking for.
- for (const WebInputElement& password : passwords) {
+ for (const FormFieldData* password : passwords) {
const AutocompleteFlag flag = autocomplete_cache.RetrieveFor(password);
- if (flag == AutocompleteFlag::CURRENT_PASSWORD &&
- current_password->IsNull()) {
+ if (flag == AutocompleteFlag::CURRENT_PASSWORD && !*current_password) {
*current_password = password;
- } else if (flag == AutocompleteFlag::NEW_PASSWORD &&
- new_password->IsNull()) {
+ } else if (flag == AutocompleteFlag::NEW_PASSWORD && !*new_password) {
*new_password = password;
- } else if (!new_password->IsNull() &&
- (new_password->Value() == password.Value())) {
+ } else if (*new_password && ((*new_password)->value == password->value)) {
*confirmation_password = password;
}
}
@@ -276,7 +223,7 @@ void LocateSpecificPasswords(std::vector<WebInputElement> passwords,
// rest of the password fields unmarked. Perhaps they are used for other
// purposes, e.g., PINs, OTPs, and the like. So we skip all the heuristics we
// normally do, and ignore the rest of the password fields.
- if (!current_password->IsNull() || !new_password->IsNull())
+ if (*current_password || *new_password)
return;
switch (passwords.size()) {
@@ -285,8 +232,8 @@ void LocateSpecificPasswords(std::vector<WebInputElement> passwords,
*current_password = passwords[0];
break;
case 2:
- if (!passwords[0].Value().IsEmpty() &&
- passwords[0].Value() == passwords[1].Value()) {
+ if (!passwords[0]->value.empty() &&
+ passwords[0]->value == passwords[1]->value) {
// Two identical non-empty passwords: assume we are seeing a new
// password with a confirmation. This can be either a sign-up form or a
// password change form that does not ask for the old password.
@@ -302,20 +249,20 @@ void LocateSpecificPasswords(std::vector<WebInputElement> passwords,
}
break;
default:
- if (!passwords[0].Value().IsEmpty() &&
- passwords[0].Value() == passwords[1].Value() &&
- passwords[0].Value() == passwords[2].Value()) {
+ if (!passwords[0]->value.empty() &&
+ passwords[0]->value == passwords[1]->value &&
+ passwords[0]->value == passwords[2]->value) {
// All three passwords are the same and non-empty? It may be a change
// password form where old and new passwords are the same. It doesn't
// matter what field is correct, let's save the value.
*current_password = passwords[0];
- } else if (passwords[1].Value() == passwords[2].Value()) {
+ } else if (passwords[1]->value == passwords[2]->value) {
// New password is the duplicated one, and comes second; or empty form
// with 3 password fields, in which case we will assume this layout.
*current_password = passwords[0];
*new_password = passwords[1];
*confirmation_password = passwords[2];
- } else if (passwords[0].Value() == passwords[1].Value()) {
+ } else if (passwords[0]->value == passwords[1]->value) {
// It is strange that the new password comes first, but trust more which
// fields are duplicated than the ordering of fields. Assume that
// any password fields after the new password contain sensitive
@@ -332,11 +279,10 @@ void LocateSpecificPasswords(std::vector<WebInputElement> passwords,
}
void FindPredictedElements(
- const std::vector<blink::WebFormControlElement>& control_elements,
const FormData& form_data,
const FormsPredictionsMap& form_predictions,
- std::map<WebInputElement, PasswordFormFieldPredictionType>*
- predicted_elements) {
+ std::map<const FormFieldData*, PasswordFormFieldPredictionType>*
+ predicted_fields) {
// Matching only requires that action and name of the form match to allow
// the username to be updated even if the form is changed after page load.
// See https://crbug.com/476092 for more details.
@@ -352,21 +298,12 @@ void FindPredictedElements(
if (!field_predictions)
return;
- std::vector<blink::WebFormControlElement> autofillable_elements =
- form_util::ExtractAutofillableElementsFromSet(control_elements);
for (const auto& prediction : *field_predictions) {
const FormFieldData& target_field = prediction.first;
const PasswordFormFieldPredictionType& type = prediction.second;
- for (const auto& control_element : autofillable_elements) {
- if (control_element.NameForAutofill().Utf16() == target_field.name) {
- const WebInputElement* input_element =
- ToWebInputElement(&control_element);
-
- // TODO(sebsg): Investigate why this guard is necessary, see
- // https://crbug.com/517490 for more details.
- if (input_element) {
- (*predicted_elements)[*input_element] = type;
- }
+ for (const FormFieldData& field : form_data.fields) {
+ if (field.name == target_field.name) {
+ (*predicted_fields)[&field] = type;
break;
}
}
@@ -387,50 +324,33 @@ base::LazyInstance<re2::RE2, PasswordSiteUrlLazyInstanceTraits>
g_password_site_matcher = LAZY_INSTANCE_INITIALIZER;
// Returns the |input_field| name if its non-empty; otherwise a |dummy_name|.
-base::string16 FieldName(const WebInputElement& input_field,
- const char dummy_name[]) {
- base::string16 field_name = input_field.NameForAutofill().Utf16();
- return field_name.empty() ? base::ASCIIToUTF16(dummy_name) : field_name;
-}
-
-// Returns true iff the properties mask of |element| intersects with |mask|.
-bool FieldHasPropertiesMask(const FieldValueAndPropertiesMaskMap* field_map,
- const blink::WebFormControlElement& element,
- FieldPropertiesMask mask) {
- if (!field_map)
- return false;
- FieldValueAndPropertiesMaskMap::const_iterator it = field_map->find(element);
- return it != field_map->end() && (it->second.second & mask);
+base::string16 FieldName(const FormFieldData* input_field,
+ const char* dummy_name) {
+ return input_field->name.empty() ? base::ASCIIToUTF16(dummy_name)
+ : input_field->name;
}
-// Return the maximal filtering criteria that |element| passes.
+// Return the maximal filtering criteria that |field| passes.
// If |ignore_autofilled_values|, autofilled value isn't considered user input.
-FieldFilteringLevel GetFiltertingLevelForField(
- const blink::WebFormControlElement& element,
- const FieldValueAndPropertiesMaskMap* field_value_and_properties_map,
- bool ignore_autofilled_values) {
+FieldFilteringLevel GetFiltertingLevelForField(const FormFieldData& field,
+ bool ignore_autofilled_values) {
FieldPropertiesMask user_input_mask =
ignore_autofilled_values
? FieldPropertiesFlags::USER_TYPED
: FieldPropertiesFlags::USER_TYPED | FieldPropertiesFlags::AUTOFILLED;
- if (FieldHasPropertiesMask(field_value_and_properties_map, element,
- user_input_mask)) {
+ if (field.properties_mask & user_input_mask)
return FieldFilteringLevel::USER_INPUT;
- }
- return form_util::IsWebElementVisible(element)
- ? FieldFilteringLevel::VISIBILITY
- : FieldFilteringLevel::NO_FILTER;
+ return field.is_focusable ? FieldFilteringLevel::VISIBILITY
+ : FieldFilteringLevel::NO_FILTER;
}
// Calculates the maximal filtering levels for password and username fields and
// saves them to |username_fields_level| and |password_fields_level|. The
// criteria for username fields considers only the fields before the first
// password field that has the maximal filtering level.
-void GetFieldFilteringLevels(
- const std::vector<blink::WebFormControlElement>& control_elements,
- const FieldValueAndPropertiesMaskMap* field_value_and_properties_map,
- FieldFilteringLevel* username_fields_level,
- FieldFilteringLevel* password_fields_level) {
+void GetFieldFilteringLevels(const std::vector<FormFieldData>& fields,
+ FieldFilteringLevel* username_fields_level,
+ FieldFilteringLevel* password_fields_level) {
DCHECK(password_fields_level);
DCHECK(username_fields_level);
*username_fields_level = FieldFilteringLevel::NO_FILTER;
@@ -438,12 +358,9 @@ void GetFieldFilteringLevels(
FieldFilteringLevel max_level_found_for_username_fields =
FieldFilteringLevel::NO_FILTER;
- for (auto& control_element : control_elements) {
- const WebInputElement* input_element = ToWebInputElement(&control_element);
- if (!input_element || !input_element->IsEnabled() ||
- !input_element->IsTextField()) {
+ for (const FormFieldData& field : fields) {
+ if (!field.is_enabled || !field.IsTextInputElement())
continue;
- }
// TODO(crbug.com/789917): Ignore autofilled values here because if there
// are only autofilled values then a form may not be filled completely (i.e.
@@ -451,11 +368,9 @@ void GetFieldFilteringLevels(
// fields filtering. Once the bug is resolved, autofilled values will not be
// ignored.
FieldFilteringLevel current_field_filtering_level =
- GetFiltertingLevelForField(control_element,
- field_value_and_properties_map,
- true /* ignore_autofilled_values */);
+ GetFiltertingLevelForField(field, true /* ignore_autofilled_values */);
- if (input_element->IsPasswordFieldForAutofill()) {
+ if (field.form_control_type == "password") {
if (*password_fields_level < current_field_filtering_level) {
*password_fields_level = current_field_filtering_level;
*username_fields_level = max_level_found_for_username_fields;
@@ -467,51 +382,10 @@ void GetFieldFilteringLevels(
}
}
-ValueElementPair MakePossibleUsernamePair(const blink::WebInputElement& input) {
- base::string16 trimmed_input_value, trimmed_input_autofill;
- base::TrimString(input.Value().Utf16(), base::ASCIIToUTF16(" "),
- &trimmed_input_value);
- return {trimmed_input_value, input.NameForAutofill().Utf16()};
-}
-
-// Check if a script modified username is suitable for Password Manager to
-// remember.
-bool ScriptModifiedUsernameAcceptable(
- const base::string16& username_value,
- const base::string16& typed_username_value,
- const PasswordForm* password_form,
- const FieldValueAndPropertiesMaskMap* field_value_and_properties_map) {
- DCHECK(password_form);
- DCHECK(field_value_and_properties_map);
- // The minimal size of a field value that will be matched.
- const size_t kMinMatchSize = 3u;
- const auto username = base::i18n::ToLower(username_value);
- const auto typed_username = base::i18n::ToLower(typed_username_value);
- if (base::StartsWith(username, typed_username, base::CompareCase::SENSITIVE))
- return true;
-
- // Check if the username was generated by javascript based on user typed name.
- if (typed_username.size() >= kMinMatchSize &&
- username_value.find(typed_username) != base::string16::npos)
- return true;
-
- // Check if the username was generated by javascript based on user typed or
- // autofilled field values.
- for (const auto& field_prop : *field_value_and_properties_map) {
- if (!field_prop.second.first)
- continue;
- const auto& field_value = *field_prop.second.first;
- const WebInputElement* input_element = ToWebInputElement(&field_prop.first);
- if (input_element && input_element->IsTextField() &&
- !input_element->IsPasswordFieldForAutofill() &&
- field_value.size() >= kMinMatchSize &&
- username_value.find(base::i18n::ToLower(field_value)) !=
- base::string16::npos) {
- return true;
- }
- }
-
- return false;
+ValueElementPair MakePossibleUsernamePair(const FormFieldData* input) {
+ base::string16 trimmed_input_value;
+ base::TrimString(input->value, base::ASCIIToUTF16(" "), &trimmed_input_value);
+ return {trimmed_input_value, input->name};
}
bool StringMatchesCVC(const base::string16& str) {
@@ -529,29 +403,46 @@ bool IsEnabledPasswordFieldPresent(const std::vector<FormFieldData>& fields) {
}
// Find the first element in |username_predictions| (i.e. the most reliable
-// prediction) that occurs in |possible_usernames|. If the element is found, the
-// method saves it to |username_element| and returns true.
-bool FindUsernameInPredictions(
- const std::vector<blink::WebInputElement>& username_predictions,
- const std::vector<blink::WebInputElement>& possible_usernames,
- WebInputElement* username_element) {
- // To speed-up the matching for-loop below, convert |possible_usernames| to a
- // set. Creating is O(N log N) for N=possible_usernames.size(). Retrieval is
- // O(log N), so the whole for-loop is O(M log N) for
- // M=username_predictions.size(). Use flat_set, because of cache locality (the
- // M and N are likely small, so this can make a difference) and less heap
- // allocations.
- const base::flat_set<blink::WebInputElement> usernames(
- possible_usernames.begin(), possible_usernames.end());
-
- for (const blink::WebInputElement& prediction : username_predictions) {
- auto iter = usernames.find(prediction);
- if (iter != usernames.end()) {
- *username_element = *iter;
- return true;
+// prediction) that occurs in |possible_usernames|.
+const FormFieldData* FindUsernameInPredictions(
+ const std::vector<uint32_t>& username_predictions,
+ const std::vector<const FormFieldData*>& possible_usernames) {
+ for (uint32_t predicted_id : username_predictions) {
+ auto iter =
+ std::find_if(possible_usernames.begin(), possible_usernames.end(),
+ [predicted_id](const FormFieldData* field) {
+ return field->unique_renderer_id == predicted_id;
+ });
+ if (iter != possible_usernames.end()) {
+ return *iter;
}
}
- return false;
+ return nullptr;
+}
+
+// Extracts the username predictions. |control_elements| should be all the DOM
+// elements of the form, |form_data| should be the already extracted FormData
+// representation of that form. |username_detector_cache| is optional, and can
+// be used to spare recomputation if called multiple times for the same form.
+std::vector<uint32_t> GetUsernamePredictions(
+ const std::vector<blink::WebFormControlElement>& control_elements,
+ const FormData& form_data,
+ UsernameDetectorCache* username_detector_cache) {
+ std::vector<uint32_t> username_predictions;
+ // Dummy cache stores the predictions in case no real cache was passed to
+ // here.
+ UsernameDetectorCache dummy_cache;
+ if (!username_detector_cache)
+ username_detector_cache = &dummy_cache;
+
+ const std::vector<WebInputElement>& username_predictions_dom =
+ GetPredictionsFieldBasedOnHtmlAttributes(control_elements, form_data,
+ username_detector_cache);
+ username_predictions.reserve(username_predictions_dom.size());
+ for (const WebInputElement& element : username_predictions_dom) {
+ username_predictions.push_back(element.UniqueRendererFormControlId());
+ }
+ return username_predictions;
}
// Get information about a login form encapsulated in a PasswordForm struct.
@@ -559,37 +450,44 @@ bool FindUsernameInPredictions(
// associated string is used instead of the element's value to create
// the PasswordForm.
bool GetPasswordForm(
- SyntheticForm form,
+ const GURL& form_origin,
+ const std::vector<blink::WebFormControlElement>& control_elements,
PasswordForm* password_form,
- const FieldValueAndPropertiesMaskMap* field_value_and_properties_map,
const FormsPredictionsMap* form_predictions,
UsernameDetectorCache* username_detector_cache) {
- DCHECK(!form.control_elements.empty());
+ DCHECK(!control_elements.empty());
+
+ const FormData& form_data = password_form->form_data;
// Early exit if no passwords to be typed into.
- if (!IsEnabledPasswordFieldPresent(password_form->form_data.fields))
+ if (!IsEnabledPasswordFieldPresent(form_data.fields))
return false;
- // Narrow the scope to enabled inputs.
- std::vector<WebInputElement> enabled_inputs;
- enabled_inputs.reserve(form.control_elements.size());
- for (const WebFormControlElement& control_element : form.control_elements) {
- const WebInputElement* input_element =
- GetEnabledTextInputFieldOrNull(control_element);
- if (input_element)
- enabled_inputs.push_back(*input_element);
+ // Evaluate the context of the fields.
+ if (base::FeatureList::IsEnabled(
+ password_manager::features::kHtmlBasedUsernameDetector)) {
+ password_form->form_data.username_predictions = GetUsernamePredictions(
+ control_elements, form_data, username_detector_cache);
+ }
+
+ // Narrow the scope to enabled text inputs.
+ std::vector<const FormFieldData*> enabled_fields;
+ enabled_fields.reserve(form_data.fields.size());
+ for (const FormFieldData& field : form_data.fields) {
+ if (field.is_enabled && field.IsTextInputElement())
+ enabled_fields.push_back(&field);
}
// Remember the list of password fields without any heuristics applied in case
// the heuristics fail and a fall-back is needed:
// All password fields.
- std::vector<WebInputElement> passwords_without_heuristics;
+ std::vector<const FormFieldData*> passwords_without_heuristics;
// Map from all password fields to the most recent non-password text input.
- std::map<WebInputElement, WebInputElement>
+ std::map<const FormFieldData*, const FormFieldData*>
preceding_text_input_for_password_without_heuristics;
- WebInputElement most_recent_text_input; // Just a temporary.
- for (const WebInputElement& input : enabled_inputs) {
- if (input.IsPasswordFieldForAutofill()) {
+ const FormFieldData* most_recent_text_input = nullptr; // Just a temporary.
+ for (const FormFieldData* input : enabled_fields) {
+ if (input->form_control_type == "password") {
passwords_without_heuristics.push_back(input);
preceding_text_input_for_password_without_heuristics[input] =
most_recent_text_input;
@@ -600,25 +498,29 @@ bool GetPasswordForm(
// Fill the cache with autocomplete flags.
AutocompleteCache autocomplete_cache;
- for (const WebInputElement& input : enabled_inputs) {
+ for (const FormFieldData* input : enabled_fields) {
autocomplete_cache.Store(input);
}
// Narrow the scope further: drop credit-card fields.
- std::vector<WebInputElement> plausible_inputs;
- plausible_inputs.reserve(enabled_inputs.size());
- for (const WebInputElement& input : enabled_inputs) {
+ std::vector<const FormFieldData*> plausible_inputs;
+ plausible_inputs.reserve(enabled_fields.size());
+ for (const FormFieldData* input : enabled_fields) {
const AutocompleteFlag flag = autocomplete_cache.RetrieveFor(input);
if (flag == AutocompleteFlag::CURRENT_PASSWORD ||
flag == AutocompleteFlag::NEW_PASSWORD) {
// A field marked as a password is considered not a credit-card field, no
// matter what.
plausible_inputs.push_back(input);
- } else if (flag != AutocompleteFlag::CREDIT_CARD &&
- !IsCreditCardVerificationPasswordField(input)) {
- // Otherwise ensure that nothing hints that |input| is a credit-card
- // field.
- plausible_inputs.push_back(input);
+ } else if (flag != AutocompleteFlag::CREDIT_CARD) {
+ const bool is_credit_card_verification =
+ input->form_control_type == "password" &&
+ (StringMatchesCVC(input->name) || StringMatchesCVC(input->id));
+ if (!is_credit_card_verification) {
+ // Otherwise ensure that nothing hints that |input| is a credit-card
+ // field.
+ plausible_inputs.push_back(input);
+ }
}
}
@@ -627,19 +529,17 @@ bool GetPasswordForm(
// Compute the best filtering levels for usernames and for passwords.
FieldFilteringLevel username_fields_level = FieldFilteringLevel::NO_FILTER;
FieldFilteringLevel password_fields_level = FieldFilteringLevel::NO_FILTER;
- GetFieldFilteringLevels(form.control_elements, field_value_and_properties_map,
- &username_fields_level, &password_fields_level);
+ GetFieldFilteringLevels(form_data.fields, &username_fields_level,
+ &password_fields_level);
// Remove all fields with filtering level below the best.
base::EraseIf(
- plausible_inputs, [&field_value_and_properties_map, password_fields_level,
- username_fields_level](const WebInputElement& input) {
- FieldFilteringLevel current_field_level =
- GetFiltertingLevelForField(input, field_value_and_properties_map,
- false /* ignore_autofilled_values */);
- if (input.IsPasswordFieldForAutofill())
- return (current_field_level < password_fields_level);
- else
- return (current_field_level < username_fields_level);
+ plausible_inputs, [password_fields_level,
+ username_fields_level](const FormFieldData* input) {
+ FieldFilteringLevel current_field_level = GetFiltertingLevelForField(
+ *input, false /* ignore_autofilled_values */);
+ if (input->form_control_type == "password")
+ return current_field_level < password_fields_level;
+ return current_field_level < username_fields_level;
});
// Further, remove all readonly passwords. If the password field is readonly,
@@ -649,17 +549,15 @@ bool GetPasswordForm(
// made readonly by JavaScript before submission, it remains interesting. If
// the password was marked via the autocomplete attribute, it also remains
// interesting.
- base::EraseIf(plausible_inputs, [&field_value_and_properties_map,
- &autocomplete_cache](
- const WebInputElement& input) {
- if (!input.IsPasswordFieldForAutofill())
+ base::EraseIf(plausible_inputs, [&autocomplete_cache](
+ const FormFieldData* input) {
+ if (!input->is_readonly)
return false;
- if (!input.IsReadOnly())
+ if (input->form_control_type != "password")
return false;
// Check if the field was only made readonly before submission.
- if (FieldHasPropertiesMask(field_value_and_properties_map, input,
- FieldPropertiesFlags::USER_TYPED |
- FieldPropertiesFlags::AUTOFILLED)) {
+ if (input->properties_mask &
+ (FieldPropertiesFlags::USER_TYPED | FieldPropertiesFlags::AUTOFILLED)) {
return false;
}
// Check whether the field was explicitly marked as password.
@@ -672,15 +570,16 @@ bool GetPasswordForm(
});
// Evaluate available server-side predictions.
- std::map<WebInputElement, PasswordFormFieldPredictionType> predicted_elements;
- WebInputElement predicted_username_element;
+ std::map<const FormFieldData*, PasswordFormFieldPredictionType>
+ predicted_fields;
+ const FormFieldData* predicted_username_field = nullptr;
if (form_predictions) {
- FindPredictedElements(form.control_elements, password_form->form_data,
- *form_predictions, &predicted_elements);
+ FindPredictedElements(password_form->form_data, *form_predictions,
+ &predicted_fields);
- for (const auto& predicted_pair : predicted_elements) {
+ for (const auto& predicted_pair : predicted_fields) {
if (predicted_pair.second == PREDICTION_USERNAME) {
- predicted_username_element = predicted_pair.first;
+ predicted_username_field = predicted_pair.first;
break;
}
}
@@ -689,31 +588,30 @@ bool GetPasswordForm(
// Finally, remove all password fields for which we have a negative
// prediction, unless they are explicitly marked by the autocomplete attribute
// as a password.
- base::EraseIf(plausible_inputs, [&predicted_elements, &autocomplete_cache](
- const WebInputElement& input) {
- if (!input.IsPasswordFieldForAutofill())
+ base::EraseIf(plausible_inputs, [&predicted_fields, &autocomplete_cache](
+ const FormFieldData* input) {
+ if (input->form_control_type != "password")
return false;
const AutocompleteFlag flag = autocomplete_cache.RetrieveFor(input);
if (flag == AutocompleteFlag::CURRENT_PASSWORD ||
flag == AutocompleteFlag::NEW_PASSWORD) {
return false;
}
- auto possible_password_element_iterator = predicted_elements.find(input);
- return possible_password_element_iterator != predicted_elements.end() &&
- possible_password_element_iterator->second ==
- PREDICTION_NOT_PASSWORD;
+ auto possible_password_field_iterator = predicted_fields.find(input);
+ return possible_password_field_iterator != predicted_fields.end() &&
+ possible_password_field_iterator->second == PREDICTION_NOT_PASSWORD;
});
// Derive the list of all plausible passwords, usernames and the non-password
// inputs preceding the plausible passwords.
- std::vector<WebInputElement> plausible_passwords;
- std::vector<WebInputElement> plausible_usernames;
- std::map<WebInputElement, WebInputElement>
+ std::vector<const FormFieldData*> plausible_passwords;
+ std::vector<const FormFieldData*> plausible_usernames;
+ std::map<const FormFieldData*, const FormFieldData*>
preceding_text_input_for_plausible_password;
- most_recent_text_input.Reset();
+ most_recent_text_input = nullptr;
plausible_usernames.reserve(plausible_inputs.size());
- for (const WebInputElement& input : plausible_inputs) {
- if (input.IsPasswordFieldForAutofill()) {
+ for (const FormFieldData* input : plausible_inputs) {
+ if (input->form_control_type == "password") {
plausible_passwords.push_back(input);
preceding_text_input_for_plausible_password[input] =
most_recent_text_input;
@@ -724,42 +622,30 @@ bool GetPasswordForm(
}
// Evaluate autocomplete attributes for username.
- WebInputElement username_by_attribute;
- for (const WebInputElement& input : plausible_inputs) {
- if (!input.IsPasswordFieldForAutofill()) {
+ const FormFieldData* username_by_attribute = nullptr;
+ for (const FormFieldData* input : plausible_inputs) {
+ if (input->form_control_type != "password") {
if (autocomplete_cache.RetrieveFor(input) == AutocompleteFlag::USERNAME) {
// Only consider the first occurrence of autocomplete='username'.
// Multiple occurences hint at the attribute being used incorrectly, in
// which case sticking to the first one is just a bet.
- if (username_by_attribute.IsNull()) {
+ if (!username_by_attribute) {
username_by_attribute = input;
+ break;
}
}
}
}
// Evaluate the context of the fields.
- WebInputElement username_element_by_context;
+ const FormFieldData* username_field_by_context = nullptr;
if (base::FeatureList::IsEnabled(
password_manager::features::kHtmlBasedUsernameDetector)) {
- // Call HTML based username detector only if neither server predictions nor
+ // Use HTML based username detector only if neither server predictions nor
// autocomplete attributes were useful to detect the username.
- if (predicted_username_element.IsNull() && username_by_attribute.IsNull()) {
- // Dummy cache stores the predictions in case no real cache was passed to
- // here.
- UsernameDetectorCache dummy_cache;
- if (!username_detector_cache)
- username_detector_cache = &dummy_cache;
-
- const std::vector<blink::WebInputElement>& username_predictions =
- GetPredictionsFieldBasedOnHtmlAttributes(form.control_elements,
- password_form->form_data,
- username_detector_cache);
-
- if (!FindUsernameInPredictions(username_predictions, plausible_usernames,
- &username_element_by_context)) {
- username_element_by_context.Reset();
- }
+ if (!predicted_username_field && !username_by_attribute) {
+ username_field_by_context = FindUsernameInPredictions(
+ form_data.username_predictions, plausible_usernames);
}
}
@@ -767,8 +653,9 @@ bool GetPasswordForm(
// sign-up, sign-in, etc.).
std::string layout_sequence;
layout_sequence.reserve(plausible_inputs.size());
- for (const WebInputElement& input : plausible_inputs) {
- layout_sequence.push_back((input.IsPasswordFieldForAutofill()) ? 'P' : 'N');
+ for (const FormFieldData* input : plausible_inputs) {
+ layout_sequence.push_back((input->form_control_type == "password") ? 'P'
+ : 'N');
}
// Populate all_possible_passwords and form_has_autofilled_value in
@@ -782,22 +669,20 @@ bool GetPasswordForm(
// Pretend that an empty value has been already seen, so that empty-valued
// password elements won't get added to |all_possible_passwords|.
seen_values.insert(base::StringPiece16());
- for (const WebInputElement& password_element : passwords_without_heuristics) {
- const base::string16 value = password_element.Value().Utf16();
- if (seen_values.count(value) > 0)
+ for (const FormFieldData* password_field : passwords_without_heuristics) {
+ if (seen_values.count(password_field->value) > 0)
continue;
all_possible_passwords.push_back(
- {std::move(value), password_element.NameForAutofill().Utf16()});
+ {password_field->value, password_field->name});
seen_values.insert(
base::StringPiece16(all_possible_passwords.back().first));
}
bool form_has_autofilled_value = false;
- for (const WebInputElement& password_element : passwords_without_heuristics) {
- bool element_has_autofilled_value =
- FieldHasPropertiesMask(field_value_and_properties_map, password_element,
- FieldPropertiesFlags::AUTOFILLED);
- form_has_autofilled_value |= element_has_autofilled_value;
+ for (const FormFieldData* password_field : passwords_without_heuristics) {
+ bool field_has_autofilled_value =
+ password_field->properties_mask & FieldPropertiesFlags::AUTOFILLED;
+ form_has_autofilled_value |= field_has_autofilled_value;
}
if (!all_possible_passwords.empty()) {
@@ -818,66 +703,66 @@ bool GetPasswordForm(
}
// Find the password fields.
- WebInputElement password;
- WebInputElement new_password;
- WebInputElement confirmation_password;
+ const FormFieldData* password = nullptr;
+ const FormFieldData* new_password = nullptr;
+ const FormFieldData* confirmation_password = nullptr;
LocateSpecificPasswords(std::move(plausible_passwords), &password,
&new_password, &confirmation_password,
autocomplete_cache);
// Choose the username element.
- WebInputElement username_element;
+ const FormFieldData* username_field = nullptr;
UsernameDetectionMethod username_detection_method =
UsernameDetectionMethod::NO_USERNAME_DETECTED;
password_form->username_marked_by_site = false;
- if (!predicted_username_element.IsNull()) {
+ if (predicted_username_field) {
// Server predictions are most trusted, so try them first. Only if the form
// already has user input and the predicted username field has an empty
// value, then don't trust the prediction (can be caused by, e.g., a <form>
// actually contains several forms).
- if ((password_fields_level < FieldFilteringLevel::USER_INPUT ||
- !predicted_username_element.Value().IsEmpty())) {
- username_element = predicted_username_element;
+ if (password_fields_level < FieldFilteringLevel::USER_INPUT ||
+ !predicted_username_field->value.empty()) {
+ username_field = predicted_username_field;
password_form->was_parsed_using_autofill_predictions = true;
username_detection_method =
UsernameDetectionMethod::SERVER_SIDE_PREDICTION;
}
}
- if (username_element.IsNull() && !username_by_attribute.IsNull()) {
+ if (!username_field && username_by_attribute) {
// Next in the trusted queue: autocomplete attributes.
- username_element = username_by_attribute;
+ username_field = username_by_attribute;
username_detection_method = UsernameDetectionMethod::AUTOCOMPLETE_ATTRIBUTE;
}
- if (username_element.IsNull() && !username_element_by_context.IsNull()) {
+ if (!username_field && username_field_by_context) {
// Last step before base heuristics: HTML-based classifier.
- username_element = username_element_by_context;
+ username_field = username_field_by_context;
username_detection_method = UsernameDetectionMethod::HTML_BASED_CLASSIFIER;
}
// Compute base heuristic for username detection.
- WebInputElement base_heuristic_username;
- if (!password.IsNull()) {
+ const FormFieldData* base_heuristic_username = nullptr;
+ if (password) {
base_heuristic_username =
preceding_text_input_for_plausible_password[password];
}
- if (base_heuristic_username.IsNull() && !new_password.IsNull()) {
+ if (!base_heuristic_username && new_password) {
base_heuristic_username =
preceding_text_input_for_plausible_password[new_password];
}
// Apply base heuristic for username detection.
- if (username_element.IsNull()) {
- username_element = base_heuristic_username;
- if (!username_element.IsNull())
+ if (!username_field) {
+ username_field = base_heuristic_username;
+ if (username_field)
username_detection_method = UsernameDetectionMethod::BASE_HEURISTIC;
- } else if (base_heuristic_username == username_element &&
+ } else if (base_heuristic_username == username_field &&
username_detection_method !=
UsernameDetectionMethod::AUTOCOMPLETE_ATTRIBUTE) {
// TODO(crbug.com/786404): when the bug is fixed, remove this block and
- // calculate |base_heuristic_username| only if |username_element.IsNull()|
+ // calculate |base_heuristic_username| only if |username_field| is null.
// This block was added to measure the impact of server-side predictions and
// HTML based classifier compared to "old classifiers" (the based heuristic
// and 'autocomplete' attribute).
@@ -888,51 +773,38 @@ bool GetPasswordForm(
UsernameDetectionMethod::USERNAME_DETECTION_METHOD_COUNT);
// Populate the username fields in |password_form|.
- if (!username_element.IsNull()) {
+ if (username_field) {
password_form->username_element =
- FieldName(username_element, "anonymous_username");
- base::string16 username_value = username_element.Value().Utf16();
- if (FieldHasPropertiesMask(field_value_and_properties_map, username_element,
- FieldPropertiesFlags::USER_TYPED |
- FieldPropertiesFlags::AUTOFILLED)) {
- base::string16 typed_username_value =
- *field_value_and_properties_map->at(username_element).first;
-
- if (!ScriptModifiedUsernameAcceptable(username_value,
- typed_username_value, password_form,
- field_value_and_properties_map)) {
- // If |username_value| was obtained by autofilling
- // |typed_username_value|, |typed_username_value| might be incomplete,
- // so we should leave autofilled value.
- username_value = typed_username_value;
- }
+ FieldName(username_field, "anonymous_username");
+ password_form->username_value = username_field->value;
+ if ((username_field->properties_mask &
+ (FieldPropertiesFlags::USER_TYPED |
+ FieldPropertiesFlags::AUTOFILLED)) &&
+ !username_field->typed_value.empty()) {
+ password_form->username_value = username_field->typed_value;
}
- password_form->username_value = username_value;
}
// Populate the password fields in |password_form|.
- if (!password.IsNull()) {
+ if (password) {
password_form->password_element = FieldName(password, "anonymous_password");
- blink::WebString password_value = password.Value();
- if (FieldHasPropertiesMask(field_value_and_properties_map, password,
- FieldPropertiesFlags::USER_TYPED |
- FieldPropertiesFlags::AUTOFILLED)) {
- password_value = blink::WebString::FromUTF16(
- *field_value_and_properties_map->at(password).first);
+ password_form->password_value = password->value;
+ if ((password->properties_mask & (FieldPropertiesFlags::USER_TYPED |
+ FieldPropertiesFlags::AUTOFILLED)) &&
+ !password->typed_value.empty()) {
+ password_form->password_value = password->typed_value;
}
- password_form->password_value = password_value.Utf16();
}
- if (!new_password.IsNull()) {
+ if (new_password) {
password_form->new_password_element =
FieldName(new_password, "anonymous_new_password");
- password_form->new_password_value = new_password.Value().Utf16();
- password_form->new_password_value_is_default =
- new_password.GetAttribute("value") == new_password.Value();
+ password_form->new_password_value = new_password->value;
+ password_form->new_password_value_is_default = new_password->is_default;
if (autocomplete_cache.RetrieveFor(new_password) ==
AutocompleteFlag::NEW_PASSWORD) {
password_form->new_password_marked_by_site = true;
}
- if (!confirmation_password.IsNull()) {
+ if (confirmation_password) {
password_form->confirmation_password_element =
FieldName(confirmation_password, "anonymous_confirmation_password");
}
@@ -940,8 +812,8 @@ bool GetPasswordForm(
// Populate |other_possible_usernames| in |password_form|.
ValueElementVector other_possible_usernames;
- for (const WebInputElement& plausible_username : plausible_usernames) {
- if (plausible_username == username_element)
+ for (const FormFieldData* plausible_username : plausible_usernames) {
+ if (plausible_username == username_field)
continue;
auto pair = MakePossibleUsernamePair(plausible_username);
if (!pair.first.empty())
@@ -950,7 +822,7 @@ bool GetPasswordForm(
password_form->other_possible_usernames = std::move(other_possible_usernames);
// Report metrics.
- if (username_element.IsNull()) {
+ if (!username_field) {
// To get a better idea on how password forms without a username field
// look like, report the total number of text and password fields.
UMA_HISTOGRAM_COUNTS_100(
@@ -962,7 +834,7 @@ bool GetPasswordForm(
std::count(layout_sequence.begin(), layout_sequence.end(), 'P'));
}
- password_form->origin = std::move(form.origin);
+ password_form->origin = std::move(form_origin);
password_form->signon_realm = GetSignOnRealm(password_form->origin);
password_form->scheme = PasswordForm::SCHEME_HTML;
password_form->preferred = false;
@@ -973,6 +845,13 @@ bool GetPasswordForm(
return true;
}
+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
AutocompleteFlag AutocompleteFlagForElement(const WebInputElement& element) {
@@ -993,10 +872,8 @@ re2::RE2* CreateMatcher(void* instance, const char* pattern) {
}
bool IsGaiaReauthenticationForm(const blink::WebFormElement& form) {
- if (GURL(form.GetDocument().Url()).GetOrigin() !=
- GaiaUrls::GetInstance()->gaia_url().GetOrigin()) {
+ if (!HasGaiaSchemeAndHost(form))
return false;
- }
bool has_rart_field = false;
bool has_continue_field = false;
@@ -1024,16 +901,14 @@ bool IsGaiaReauthenticationForm(const blink::WebFormElement& form) {
has_continue_field = true;
}
}
-
return has_rart_field && has_continue_field;
}
bool IsGaiaWithSkipSavePasswordForm(const blink::WebFormElement& form) {
- GURL url(form.GetDocument().Url());
- if (url.GetOrigin() != GaiaUrls::GetInstance()->gaia_url().GetOrigin()) {
+ if (!HasGaiaSchemeAndHost(form))
return false;
- }
+ GURL url(form.GetDocument().Url());
std::string should_skip_password;
if (!net::GetValueForKeyInQuery(url, "ssp", &should_skip_password))
return false;
@@ -1056,8 +931,10 @@ std::unique_ptr<PasswordForm> CreatePasswordFormFromWebForm(
IsGaiaWithSkipSavePasswordForm(web_form) ||
IsGaiaReauthenticationForm(web_form);
- SyntheticForm synthetic_form;
- PopulateSyntheticFormFromWebForm(web_form, &synthetic_form);
+ blink::WebVector<blink::WebFormControlElement> control_elements;
+ web_form.GetFormControlElements(control_elements);
+ if (control_elements.empty())
+ return nullptr;
if (!WebFormElementToFormData(
web_form, blink::WebFormControlElement(),
@@ -1066,9 +943,10 @@ std::unique_ptr<PasswordForm> CreatePasswordFormFromWebForm(
return nullptr;
}
- if (!GetPasswordForm(std::move(synthetic_form), password_form.get(),
- field_value_and_properties_map, form_predictions,
- username_detector_cache)) {
+ if (!GetPasswordForm(
+ form_util::GetCanonicalOriginForDocument(web_form.GetDocument()),
+ control_elements.ReleaseVector(), password_form.get(),
+ form_predictions, username_detector_cache)) {
return nullptr;
}
return password_form;
@@ -1079,28 +957,25 @@ std::unique_ptr<PasswordForm> CreatePasswordFormFromUnownedInputElements(
const FieldValueAndPropertiesMaskMap* field_value_and_properties_map,
const FormsPredictionsMap* form_predictions,
UsernameDetectorCache* username_detector_cache) {
- SyntheticForm synthetic_form;
std::vector<blink::WebElement> fieldsets;
- synthetic_form.control_elements = form_util::GetUnownedFormFieldElements(
- frame.GetDocument().All(), &fieldsets);
- synthetic_form.origin =
- form_util::GetCanonicalOriginForDocument(frame.GetDocument());
-
- if (synthetic_form.control_elements.empty())
+ std::vector<blink::WebFormControlElement> control_elements =
+ form_util::GetUnownedFormFieldElements(frame.GetDocument().All(),
+ &fieldsets);
+ if (control_elements.empty())
return nullptr;
auto password_form = std::make_unique<PasswordForm>();
if (!UnownedPasswordFormElementsAndFieldSetsToFormData(
- fieldsets, synthetic_form.control_elements, nullptr,
- frame.GetDocument(), field_value_and_properties_map,
- form_util::EXTRACT_VALUE, &password_form->form_data,
- nullptr /* FormFieldData */)) {
+ fieldsets, control_elements, nullptr, frame.GetDocument(),
+ field_value_and_properties_map, form_util::EXTRACT_VALUE,
+ &password_form->form_data, nullptr /* FormFieldData */)) {
return nullptr;
}
- if (!GetPasswordForm(std::move(synthetic_form), password_form.get(),
- field_value_and_properties_map, form_predictions,
- username_detector_cache)) {
+ if (!GetPasswordForm(
+ form_util::GetCanonicalOriginForDocument(frame.GetDocument()),
+ control_elements, password_form.get(), form_predictions,
+ username_detector_cache)) {
return nullptr;
}
@@ -1109,14 +984,6 @@ std::unique_ptr<PasswordForm> CreatePasswordFormFromUnownedInputElements(
return password_form;
}
-bool IsCreditCardVerificationPasswordField(
- const blink::WebInputElement& field) {
- if (!field.IsPasswordFieldForAutofill())
- return false;
- return StringMatchesCVC(field.GetAttribute("id").Utf16()) ||
- StringMatchesCVC(field.GetAttribute("name").Utf16());
-}
-
std::string GetSignOnRealm(const GURL& origin) {
GURL::Replacements rep;
rep.SetPathStr("");
diff --git a/chromium/components/autofill/content/renderer/password_form_conversion_utils.h b/chromium/components/autofill/content/renderer/password_form_conversion_utils.h
index a571ebd604d..50b49e45a86 100644
--- a/chromium/components/autofill/content/renderer/password_form_conversion_utils.h
+++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.h
@@ -20,7 +20,6 @@
namespace blink {
class WebFormElement;
-class WebFormControlElement;
class WebInputElement;
class WebLocalFrame;
}
@@ -65,10 +64,11 @@ bool IsGaiaReauthenticationForm(const blink::WebFormElement& form);
// Tests whether the given form is a GAIA form with a skip password argument.
bool IsGaiaWithSkipSavePasswordForm(const blink::WebFormElement& form);
-typedef std::map<
- const blink::WebFormControlElement,
- std::pair<std::unique_ptr<base::string16>, FieldPropertiesMask>>
- FieldValueAndPropertiesMaskMap;
+// TODO(https://crbug.com/849291): Create separate class for keeping information
+// from FieldValueAndPropertiesMaskMap.
+using FieldValueAndPropertiesMaskMap =
+ std::map<uint32_t,
+ std::pair<std::unique_ptr<base::string16>, FieldPropertiesMask>>;
// Create a PasswordForm from DOM form. Webkit doesn't allow storing
// custom metadata to DOM nodes, so we have to do this every time an event
@@ -95,10 +95,6 @@ std::unique_ptr<PasswordForm> CreatePasswordFormFromUnownedInputElements(
const FormsPredictionsMap* form_predictions,
UsernameDetectorCache* username_detector_cache);
-// Returns whether the form |field| has a "password" type, but looks like a
-// credit card verification field.
-bool IsCreditCardVerificationPasswordField(const blink::WebInputElement& field);
-
// The "Realm" for the sign-on. This is scheme, host, port.
std::string GetSignOnRealm(const GURL& origin);
diff --git a/chromium/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc b/chromium/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
index 30058360d75..732fb63e59c 100644
--- a/chromium/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
+++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
@@ -10,7 +10,7 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "components/autofill/content/renderer/form_autofill_util.h"
@@ -155,6 +155,12 @@ class PasswordFormBuilder {
name_and_id, name_and_id, value);
}
+ // Add a field with a given type. Useful to add non-text fields.
+ void AddFieldWithType(const char* name_and_id, const char* type) {
+ base::StringAppendF(&html_, "<INPUT type=\"%s\" name=\"%s\" id=\"%s\"/>",
+ type, name_and_id, name_and_id);
+ }
+
// Appends a new submit-type field at the end of the form with the specified
// |name|.
void AddSubmitButton(const char* name) {
@@ -215,7 +221,7 @@ class MAYBE_PasswordFormConversionUtilsTest : public content::RenderViewTest {
input_element->SetActivatedSubmit(true);
if (with_user_input) {
const base::string16 element_value = input_element->Value().Utf16();
- user_input[control_elements[i]] =
+ user_input[control_elements[i].UniqueRendererFormControlId()] =
std::make_pair(std::make_unique<base::string16>(element_value),
FieldPropertiesFlags::USER_TYPED);
}
@@ -736,12 +742,14 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest,
// "email" field should be ignored despite it is more reliable than prediction
// for "id" field.
FieldValueAndPropertiesMaskMap user_input;
- user_input[control_elements[2]] = std::make_pair( // id
- std::make_unique<base::string16>(control_elements[2].Value().Utf16()),
- FieldPropertiesFlags::USER_TYPED);
- user_input[control_elements[3]] = std::make_pair( // password
- std::make_unique<base::string16>(control_elements[3].Value().Utf16()),
- FieldPropertiesFlags::USER_TYPED);
+ user_input[control_elements[2].UniqueRendererFormControlId()] =
+ std::make_pair( // id
+ std::make_unique<base::string16>(control_elements[2].Value().Utf16()),
+ FieldPropertiesFlags::USER_TYPED);
+ user_input[control_elements[3].UniqueRendererFormControlId()] =
+ std::make_pair( // password
+ std::make_unique<base::string16>(control_elements[3].Value().Utf16()),
+ FieldPropertiesFlags::USER_TYPED);
std::unique_ptr<PasswordForm> password_form = CreatePasswordFormFromWebForm(
form, &user_input, nullptr, &username_detector_cache);
@@ -1575,14 +1583,16 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest, UserInput) {
WebVector<WebFormControlElement> control_elements;
form.GetFormControlElements(control_elements);
ASSERT_EQ("nonvisible_text", control_elements[0].NameForAutofill().Utf8());
- user_input[control_elements[0]] = std::make_pair(
- std::make_unique<base::string16>(control_elements[0].Value().Utf16()),
- FieldPropertiesFlags::USER_TYPED);
+ user_input[control_elements[0].UniqueRendererFormControlId()] =
+ std::make_pair(
+ std::make_unique<base::string16>(control_elements[0].Value().Utf16()),
+ FieldPropertiesFlags::USER_TYPED);
ASSERT_EQ("nonvisible_password",
control_elements[2].NameForAutofill().Utf8());
- user_input[control_elements[2]] = std::make_pair(
- std::make_unique<base::string16>(control_elements[2].Value().Utf16()),
- FieldPropertiesFlags::USER_TYPED);
+ user_input[control_elements[2].UniqueRendererFormControlId()] =
+ std::make_pair(
+ std::make_unique<base::string16>(control_elements[2].Value().Utf16()),
+ FieldPropertiesFlags::USER_TYPED);
std::unique_ptr<PasswordForm> password_form =
CreatePasswordFormFromWebForm(form, &user_input, nullptr, nullptr);
@@ -1633,14 +1643,16 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest,
form.GetFormControlElements(control_elements);
ASSERT_EQ("password_with_user_input1",
control_elements[9].NameForAutofill().Utf8());
- user_input[control_elements[9]] = std::make_pair(
- std::make_unique<base::string16>(control_elements[9].Value().Utf16()),
- FieldPropertiesFlags::USER_TYPED);
+ user_input[control_elements[9].UniqueRendererFormControlId()] =
+ std::make_pair(
+ std::make_unique<base::string16>(control_elements[9].Value().Utf16()),
+ FieldPropertiesFlags::USER_TYPED);
ASSERT_EQ("password_with_user_input2",
control_elements[10].NameForAutofill().Utf8());
- user_input[control_elements[10]] = std::make_pair(
- std::make_unique<base::string16>(control_elements[10].Value().Utf16()),
- FieldPropertiesFlags::USER_TYPED);
+ user_input[control_elements[10].UniqueRendererFormControlId()] =
+ std::make_pair(std::make_unique<base::string16>(
+ control_elements[10].Value().Utf16()),
+ FieldPropertiesFlags::USER_TYPED);
std::unique_ptr<PasswordForm> password_form =
CreatePasswordFormFromWebForm(form, &user_input, nullptr, nullptr);
@@ -1691,14 +1703,16 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest,
form.GetFormControlElements(control_elements);
ASSERT_EQ("password_with_user_input1",
control_elements[7].NameForAutofill().Utf8());
- user_input[control_elements[7]] = std::make_pair(
- std::make_unique<base::string16>(control_elements[7].Value().Utf16()),
- FieldPropertiesFlags::USER_TYPED);
+ user_input[control_elements[7].UniqueRendererFormControlId()] =
+ std::make_pair(
+ std::make_unique<base::string16>(control_elements[7].Value().Utf16()),
+ FieldPropertiesFlags::USER_TYPED);
ASSERT_EQ("password_with_user_input2",
control_elements[9].NameForAutofill().Utf8());
- user_input[control_elements[9]] = std::make_pair(
- std::make_unique<base::string16>(control_elements[9].Value().Utf16()),
- FieldPropertiesFlags::USER_TYPED);
+ user_input[control_elements[9].UniqueRendererFormControlId()] =
+ std::make_pair(
+ std::make_unique<base::string16>(control_elements[9].Value().Utf16()),
+ FieldPropertiesFlags::USER_TYPED);
std::unique_ptr<PasswordForm> password_form =
CreatePasswordFormFromWebForm(form, &user_input, nullptr, nullptr);
@@ -1854,13 +1868,14 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest,
FieldPropertiesMask mask = FieldPropertiesFlags::AUTOFILLED;
if (autofilled_value_was_modified_by_user)
mask |= FieldPropertiesFlags::USER_TYPED;
- user_input[control_elements[1]] =
+ user_input[control_elements[1].UniqueRendererFormControlId()] =
std::make_pair(std::make_unique<base::string16>(
base::ASCIIToUTF16("autofilled_value")),
mask);
- user_input[control_elements[2]] = std::make_pair(
- std::make_unique<base::string16>(base::ASCIIToUTF16("user_value")),
- FieldPropertiesFlags::USER_TYPED);
+ user_input[control_elements[2].UniqueRendererFormControlId()] =
+ std::make_pair(
+ std::make_unique<base::string16>(base::ASCIIToUTF16("user_value")),
+ FieldPropertiesFlags::USER_TYPED);
std::unique_ptr<PasswordForm> password_form(
CreatePasswordFormFromWebForm(form, &user_input, nullptr, nullptr));
@@ -2041,7 +2056,7 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest,
FieldValueAndPropertiesMaskMap user_input;
WebInputElement* input_element = ToWebInputElement(&control_elements[3]);
const base::string16 element_value = input_element->Value().Utf16();
- user_input[control_elements[3]] =
+ user_input[control_elements[3].UniqueRendererFormControlId()] =
std::make_pair(std::make_unique<base::string16>(element_value),
FieldPropertiesFlags::USER_TYPED);
@@ -2462,21 +2477,24 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest, TypedValuePreserved) {
ASSERT_EQ(3u, control_elements.size());
ASSERT_EQ("fine", control_elements[0].NameForAutofill().Utf8());
control_elements[0].SetAutofillValue("same_value");
- user_input[control_elements[0]] = std::make_pair(
- std::make_unique<base::string16>(control_elements[0].Value().Utf16()),
- FieldPropertiesFlags::USER_TYPED);
+ user_input[control_elements[0].UniqueRendererFormControlId()] =
+ std::make_pair(
+ std::make_unique<base::string16>(control_elements[0].Value().Utf16()),
+ FieldPropertiesFlags::USER_TYPED);
ASSERT_EQ("mangled", control_elements[1].NameForAutofill().Utf8());
control_elements[1].SetAutofillValue("mangled_value");
- user_input[control_elements[1]] = std::make_pair(
- std::make_unique<base::string16>(base::UTF8ToUTF16("original_value")),
- FieldPropertiesFlags::USER_TYPED);
+ user_input[control_elements[1].UniqueRendererFormControlId()] =
+ std::make_pair(
+ std::make_unique<base::string16>(base::UTF8ToUTF16("original_value")),
+ FieldPropertiesFlags::USER_TYPED);
ASSERT_EQ("completed_for_user", control_elements[2].NameForAutofill().Utf8());
control_elements[2].SetAutofillValue("email@gmail.com");
- user_input[control_elements[2]] = std::make_pair(
- std::make_unique<base::string16>(base::UTF8ToUTF16("email")),
- FieldPropertiesFlags::USER_TYPED);
+ user_input[control_elements[2].UniqueRendererFormControlId()] =
+ std::make_pair(
+ std::make_unique<base::string16>(base::UTF8ToUTF16("email")),
+ FieldPropertiesFlags::USER_TYPED);
std::unique_ptr<PasswordForm> password_form =
CreatePasswordFormFromWebForm(form, &user_input, nullptr, nullptr);
@@ -2502,4 +2520,24 @@ TEST_F(MAYBE_PasswordFormConversionUtilsTest, TypedValuePreserved) {
EXPECT_EQ(base::string16(), password_form->form_data.fields[2].typed_value);
}
+// Check that non-text fields are ignored.
+TEST_F(MAYBE_PasswordFormConversionUtilsTest, NonTextFields) {
+ PasswordFormBuilder builder(kTestFormActionURL);
+ // Avoid calling the text fields anything related to "username" to prevent the
+ // local HTML classifier from influencing the test result.
+ builder.AddTextField("textField", "", "");
+ builder.AddFieldWithType("radioInput", "radio");
+ builder.AddPasswordField("password", "", "");
+ std::string html = builder.ProduceHTML();
+
+ WebFormElement form;
+ LoadWebFormFromHTML(html, &form, nullptr);
+
+ std::unique_ptr<PasswordForm> password_form =
+ CreatePasswordFormFromWebForm(form, nullptr, nullptr, nullptr);
+
+ ASSERT_TRUE(password_form);
+ EXPECT_EQ(base::UTF8ToUTF16("textField"), password_form->username_element);
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.cc b/chromium/components/autofill/content/renderer/password_generation_agent.cc
index 1173ba6a0ab..19467682855 100644
--- a/chromium/components/autofill/content/renderer/password_generation_agent.cc
+++ b/chromium/components/autofill/content/renderer/password_generation_agent.cc
@@ -34,10 +34,13 @@
#include "third_party/blink/public/web/web_view.h"
#include "ui/gfx/geometry/rect.h"
+using blink::WebAutofillState;
+
namespace autofill {
namespace {
+using autofill::form_util::GetTextDirectionForElement;
using Logger = autofill::SavePasswordProgressLogger;
// Returns pairs of |PasswordForm| and corresponding |WebFormElement| for all
@@ -213,6 +216,30 @@ void PasswordGenerationAgent::DidCommitProvisionalLoad(
void PasswordGenerationAgent::DidFinishDocumentLoad() {
// Update stats for main frame navigation.
if (!render_frame()->GetWebFrame()->Parent()) {
+ // Log statistics after navigation so that we only log once per page.
+ if (enabled_ || generation_form_data_) {
+ if (generation_form_data_ &&
+ !generation_form_data_->password_elements.empty()) {
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::SIGN_UP_DETECTED);
+ } else {
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::NO_SIGN_UP_DETECTED);
+ }
+ if (password_edited_) {
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::PASSWORD_EDITED);
+ }
+ if (generation_popup_shown_) {
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::GENERATION_POPUP_SHOWN);
+ }
+ if (editing_popup_shown_) {
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::EDITING_POPUP_SHOWN);
+ }
+ }
+
// In every navigation, the IPC message sent by the password autofill
// manager to query whether the current form is blacklisted or not happens
// when the document load finishes, so we need to clear previous states
@@ -225,35 +252,12 @@ void PasswordGenerationAgent::DidFinishDocumentLoad() {
generation_enabled_forms_.clear();
generation_element_.Reset();
possible_account_creation_forms_.clear();
-
- // Log statistics after navigation so that we only log once per page.
- if (generation_form_data_ &&
- generation_form_data_->password_elements.empty()) {
- password_generation::LogPasswordGenerationEvent(
- password_generation::NO_SIGN_UP_DETECTED);
- } else {
- password_generation::LogPasswordGenerationEvent(
- password_generation::SIGN_UP_DETECTED);
- }
generation_form_data_.reset();
password_is_generated_ = false;
- if (password_edited_) {
- password_generation::LogPasswordGenerationEvent(
- password_generation::PASSWORD_EDITED);
- }
password_edited_ = false;
-
- if (generation_popup_shown_) {
- password_generation::LogPasswordGenerationEvent(
- password_generation::GENERATION_POPUP_SHOWN);
- }
generation_popup_shown_ = false;
-
- if (editing_popup_shown_) {
- password_generation::LogPasswordGenerationEvent(
- password_generation::EDITING_POPUP_SHOWN);
- }
editing_popup_shown_ = false;
+ is_manually_triggered_ = false;
}
FindPossibleGenerationForm();
@@ -369,6 +373,9 @@ void PasswordGenerationAgent::FormNotBlacklisted(const PasswordForm& form) {
void PasswordGenerationAgent::GeneratedPasswordAccepted(
const base::string16& password) {
+ // static cast is workaround for linker error.
+ DCHECK_LE(static_cast<size_t>(kMinimumLengthForEditedPassword),
+ password.size());
password_is_generated_ = true;
password_edited_ = false;
password_generation::LogPasswordGenerationEvent(
@@ -380,14 +387,14 @@ void PasswordGenerationAgent::GeneratedPasswordAccepted(
// frame.
if (!render_frame())
return;
- password_element.SetAutofilled(true);
+ password_element.SetAutofillState(WebAutofillState::kAutofilled);
// Advance focus to the next input field. We assume password fields in
// an account creation form are always adjacent.
render_frame()->GetRenderView()->GetWebView()->AdvanceFocus(false);
}
std::unique_ptr<PasswordForm> presaved_form(CreatePasswordFormToPresave());
if (presaved_form)
- GetPasswordManagerDriver()->PresaveGeneratedPassword(*presaved_form);
+ GetPasswordManagerClient()->PresaveGeneratedPassword(*presaved_form);
// Call UpdateStateForTextChange after the corresponding PasswordFormManager
// is notified that the password was generated.
@@ -415,9 +422,6 @@ PasswordGenerationAgent::CreatePasswordFormToPresave() {
}
if (password_form) {
password_form->type = PasswordForm::TYPE_GENERATED;
- // TODO(kolos): when we are good in username detection, save username
- // as well.
- password_form->username_value = base::string16();
password_form->password_value = generation_element_.Value().Utf16();
}
@@ -431,6 +435,25 @@ void PasswordGenerationAgent::FoundFormsEligibleForGeneration(
DetermineGenerationElement();
}
+void PasswordGenerationAgent::UserTriggeredGeneratePassword() {
+ if (SetUpUserTriggeredGeneration()) {
+ LogMessage(Logger::STRING_GENERATION_RENDERER_SHOW_MANUAL_GENERATION_POPUP);
+ autofill::password_generation::PasswordGenerationUIData
+ password_generation_ui_data(
+ render_frame()->GetRenderView()->ElementBoundsInWindow(
+ generation_element_),
+ generation_element_.MaxLength(),
+ generation_element_.NameForAutofill().Utf16(),
+ GetTextDirectionForElement(generation_element_),
+ *generation_form_data_->form);
+ // TODO(crbug.com/845458): remove it. The renderer should never invoke the
+ // prompt directly.
+ GetPasswordManagerClient()->ShowManualPasswordGenerationPopup(
+ password_generation_ui_data);
+ generation_popup_shown_ = true;
+ }
+}
+
void PasswordGenerationAgent::DetermineGenerationElement() {
if (generation_form_data_) {
LogMessage(Logger::STRING_GENERATION_RENDERER_FORM_ALREADY_FOUND);
@@ -547,21 +570,28 @@ bool PasswordGenerationAgent::FocusedNodeHasChanged(
if (!generation_element_.IsNull())
generation_element_.SetShouldRevealPassword(false);
- if (node.IsNull() || !node.IsElementNode())
+ if (node.IsNull() || !node.IsElementNode()) {
+ AutomaticGenerationStatusChanged(false);
return false;
+ }
const blink::WebElement web_element = node.ToConst<blink::WebElement>();
- if (!web_element.GetDocument().GetFrame())
+ if (!web_element.GetDocument().GetFrame()) {
+ AutomaticGenerationStatusChanged(false);
return false;
+ }
const blink::WebInputElement* element = ToWebInputElement(&web_element);
if (element && element->IsPasswordFieldForAutofill())
last_focused_password_element_ = *element;
- if (!element || *element != generation_element_)
+ if (!element || *element != generation_element_) {
+ AutomaticGenerationStatusChanged(false);
return false;
+ }
if (password_is_generated_) {
- if (generation_element_.Value().IsEmpty()) {
+ if (generation_element_.Value().length() <
+ kMinimumLengthForEditedPassword) {
PasswordNoLongerGenerated();
} else {
generation_element_.SetShouldRevealPassword(true);
@@ -570,64 +600,94 @@ bool PasswordGenerationAgent::FocusedNodeHasChanged(
return true;
}
- // Assume that if the password field has less than kMaximumOfferSize
- // characters then the user is not finished typing their password and display
- // the password suggestion.
+ // Assume that if the password field has less than
+ // |kMaximumCharsForGenerationOffer| characters then the user is not finished
+ // typing their password and display the password suggestion.
if (!element->IsReadOnly() && element->IsEnabled() &&
- element->Value().length() <= kMaximumOfferSize) {
- ShowGenerationPopup();
+ element->Value().length() <= kMaximumCharsForGenerationOffer) {
+ MaybeOfferAutomaticGeneration();
return true;
}
+ AutomaticGenerationStatusChanged(false);
return false;
}
bool PasswordGenerationAgent::TextDidChangeInTextField(
const blink::WebInputElement& element) {
- if (element != generation_element_)
- return false;
-
- if (element.Value().IsEmpty()) {
- if (password_is_generated_) {
- // User generated a password and then deleted it.
- PasswordNoLongerGenerated();
+ if (element != generation_element_) {
+ // Presave the username if it has been changed.
+ if (password_is_generated_ &&
+ (element.Form() == generation_element_.Form())) {
+ std::unique_ptr<PasswordForm> presaved_form(
+ CreatePasswordFormToPresave());
+ if (presaved_form)
+ GetPasswordManagerClient()->PresaveGeneratedPassword(*presaved_form);
}
+ return false;
+ }
- // Offer generation again.
- ShowGenerationPopup();
- } else if (password_is_generated_) {
- password_edited_ = true;
- // Mirror edits to any confirmation password fields.
- CopyElementValueToOtherInputElements(&element,
- &generation_form_data_->password_elements);
- std::unique_ptr<PasswordForm> presaved_form(CreatePasswordFormToPresave());
- if (presaved_form) {
- GetPasswordManagerDriver()->PresaveGeneratedPassword(*presaved_form);
- }
- } else if (element.Value().length() > kMaximumOfferSize) {
+ if (!password_is_generated_ &&
+ element.Value().length() > kMaximumCharsForGenerationOffer) {
// User has rejected the feature and has started typing a password.
- HidePopup();
+ GenerationRejectedByTyping();
} else {
- // Password isn't generated and there are fewer than kMaximumOfferSize
- // characters typed, so keep offering the password. Note this function
- // will just keep the previous popup if one is already showing.
- ShowGenerationPopup();
+ const bool leave_editing_state =
+ password_is_generated_ &&
+ element.Value().length() < kMinimumLengthForEditedPassword;
+ if (!password_is_generated_ || leave_editing_state) {
+ // The call may pop up a generation prompt, replacing the editing prompt
+ // if it was previously shown.
+ MaybeOfferAutomaticGeneration();
+ }
+ if (leave_editing_state) {
+ // Tell the browser that the state isn't "editing" anymore. The browser
+ // should hide the editing prompt if it wasn't replaced above.
+ PasswordNoLongerGenerated();
+ } else if (password_is_generated_) {
+ password_edited_ = true;
+ // Mirror edits to any confirmation password fields.
+ CopyElementValueToOtherInputElements(
+ &element, &generation_form_data_->password_elements);
+ std::unique_ptr<PasswordForm> presaved_form(
+ CreatePasswordFormToPresave());
+ if (presaved_form) {
+ GetPasswordManagerClient()->PresaveGeneratedPassword(*presaved_form);
+ }
+ }
}
-
return true;
}
-void PasswordGenerationAgent::ShowGenerationPopup() {
- if (!render_frame() || generation_element_.IsNull())
- return;
- LogMessage(Logger::STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP);
- GetPasswordManagerClient()->ShowPasswordGenerationPopup(
- render_frame()->GetRenderView()->ElementBoundsInWindow(
- generation_element_),
- generation_element_.MaxLength(),
- generation_element_.NameForAutofill().Utf16(), is_manually_triggered_,
- *generation_form_data_->form);
- generation_popup_shown_ = true;
+void PasswordGenerationAgent::MaybeOfferAutomaticGeneration() {
+ // TODO(crbug.com/852309): Add this check to the generation element class.
+ if (!is_manually_triggered_) {
+ AutomaticGenerationStatusChanged(true /* available */);
+ }
+}
+
+void PasswordGenerationAgent::AutomaticGenerationStatusChanged(bool available) {
+ if (available) {
+ if (!render_frame() || generation_element_.IsNull())
+ return;
+ LogMessage(
+ Logger::STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE);
+ autofill::password_generation::PasswordGenerationUIData
+ password_generation_ui_data(
+ render_frame()->GetRenderView()->ElementBoundsInWindow(
+ generation_element_),
+ generation_element_.MaxLength(),
+ generation_element_.NameForAutofill().Utf16(),
+ GetTextDirectionForElement(generation_element_),
+ *generation_form_data_->form);
+ generation_popup_shown_ = true;
+ GetPasswordManagerClient()->AutomaticGenerationStatusChanged(
+ true, password_generation_ui_data);
+ } else {
+ // Hide the generation popup.
+ GetPasswordManagerClient()->AutomaticGenerationStatusChanged(false,
+ base::nullopt);
+ }
}
void PasswordGenerationAgent::ShowEditingPopup() {
@@ -636,12 +696,12 @@ void PasswordGenerationAgent::ShowEditingPopup() {
GetPasswordManagerClient()->ShowPasswordEditingPopup(
render_frame()->GetRenderView()->ElementBoundsInWindow(
generation_element_),
- *generation_form_data_->form);
+ *CreatePasswordFormToPresave());
editing_popup_shown_ = true;
}
-void PasswordGenerationAgent::HidePopup() {
- GetPasswordManagerClient()->HidePasswordGenerationPopup();
+void PasswordGenerationAgent::GenerationRejectedByTyping() {
+ GetPasswordManagerClient()->PasswordGenerationRejectedByTyping();
}
void PasswordGenerationAgent::PasswordNoLongerGenerated() {
@@ -652,30 +712,21 @@ void PasswordGenerationAgent::PasswordNoLongerGenerated() {
generation_element_.SetShouldRevealPassword(false);
for (blink::WebInputElement& password :
generation_form_data_->password_elements)
- password.SetAutofilled(false);
+ password.SetAutofillState(WebAutofillState::kNotFilled);
password_generation::LogPasswordGenerationEvent(
password_generation::PASSWORD_DELETED);
- CopyElementValueToOtherInputElements(
- &generation_element_, &generation_form_data_->password_elements);
+ // Clear all other password fields.
+ for (blink::WebInputElement& element :
+ generation_form_data_->password_elements) {
+ if (generation_element_ != element)
+ element.SetAutofillValue(blink::WebString());
+ }
std::unique_ptr<PasswordForm> presaved_form(CreatePasswordFormToPresave());
if (presaved_form)
- GetPasswordManagerDriver()->PasswordNoLongerGenerated(*presaved_form);
-}
-
-void PasswordGenerationAgent::UserTriggeredGeneratePassword() {
- if (SetUpUserTriggeredGeneration())
- ShowGenerationPopup();
-}
-
-void PasswordGenerationAgent::UserSelectedManualGenerationOption() {
- if (SetUpUserTriggeredGeneration()) {
- last_focused_password_element_.SetAutofillValue(blink::WebString());
- last_focused_password_element_.SetAutofilled(false);
- ShowGenerationPopup();
- }
+ GetPasswordManagerClient()->PasswordNoLongerGenerated(*presaved_form);
}
-const mojom::PasswordManagerDriverPtr&
+const mojom::PasswordManagerDriverAssociatedPtr&
PasswordGenerationAgent::GetPasswordManagerDriver() {
DCHECK(password_agent_);
return password_agent_->GetPasswordManagerDriver();
diff --git a/chromium/components/autofill/content/renderer/password_generation_agent.h b/chromium/components/autofill/content/renderer/password_generation_agent.h
index a669c07d6e3..f19f17bc18b 100644
--- a/chromium/components/autofill/content/renderer/password_generation_agent.h
+++ b/chromium/components/autofill/content/renderer/password_generation_agent.h
@@ -35,6 +35,15 @@ class PasswordAutofillAgent;
class PasswordGenerationAgent : public content::RenderFrameObserver,
public mojom::PasswordGenerationAgent {
public:
+ // Maximum number of characters typed by user while the generation is still
+ // offered. When the (kMaximumCharsForGenerationOffer + 1)-th character is
+ // typed, the generation becomes unavailable.
+ static const size_t kMaximumCharsForGenerationOffer = 5;
+
+ // User can edit the generated password. If the length falls below this value,
+ // the password is no longer considered generated.
+ static const size_t kMinimumLengthForEditedPassword = 4;
+
PasswordGenerationAgent(content::RenderFrame* render_frame,
PasswordAutofillAgent* password_agent,
service_manager::BinderRegistry* registry);
@@ -50,7 +59,6 @@ class PasswordGenerationAgent : public content::RenderFrameObserver,
// Sets |generation_element_| to the focused password field and shows a
// generation popup at this field.
void UserTriggeredGeneratePassword() override;
- void UserSelectedManualGenerationOption() override;
// Enables the form classifier.
void AllowToRunFormClassifier() override;
@@ -68,8 +76,11 @@ class PasswordGenerationAgent : public content::RenderFrameObserver,
// Called right before PasswordAutofillAgent filled |password_element|.
void OnFieldAutofilled(const blink::WebInputElement& password_element);
- // The length that a password can be before the UI is hidden.
- static const size_t kMaximumOfferSize = 5;
+#if defined(UNIT_TEST)
+ // This method requests the autofill::mojom::PasswordManagerClient which binds
+ // requests the binding if it wasn't bound yet.
+ void RequestPasswordManagerClientForTesting() { GetPasswordManagerClient(); }
+#endif
protected:
// Returns true if the document for |render_frame()| is one that we should
@@ -100,7 +111,7 @@ class PasswordGenerationAgent : public content::RenderFrameObserver,
void DidFinishLoad() override;
void OnDestruct() override;
- const mojom::PasswordManagerDriverPtr& GetPasswordManagerDriver();
+ const mojom::PasswordManagerDriverAssociatedPtr& GetPasswordManagerDriver();
const mojom::PasswordManagerClientAssociatedPtr& GetPasswordManagerClient();
@@ -118,14 +129,24 @@ class PasswordGenerationAgent : public content::RenderFrameObserver,
// all required information is collected.
bool SetUpUserTriggeredGeneration();
- // Show password generation UI anchored at |generation_element_|.
- void ShowGenerationPopup();
+ // This is called whenever automatic generation could be offered.
+ // If manual generation was already requested, automatic generation will
+ // not be offered.
+ void MaybeOfferAutomaticGeneration();
+
+ // Signals the browser that it should offer or rescind automatic password
+ // generation depending whether the user has just focused a form field
+ // suitable for generation or has changed focus from such a field.
+ void AutomaticGenerationStatusChanged(bool available);
// Show UI for editing a generated password at |generation_element_|.
void ShowEditingPopup();
- // Hides a password generation popup if one exists.
- void HidePopup();
+ // Signals the browser that generation was rejected. This happens when the
+ // user types more characters than the maximum offer size into the password
+ // field. Upon receiving this message, the browser can choose to hide the
+ // generation UI or not, depending on the platform.
+ void GenerationRejectedByTyping();
// Stops treating a password as generated.
void PasswordNoLongerGenerated();
@@ -176,7 +197,7 @@ class PasswordGenerationAgent : public content::RenderFrameObserver,
// password.
bool password_is_generated_;
- // True if password generation was manually triggered.
+ // True if the last password generation was manually triggered.
bool is_manually_triggered_;
// True if a password was generated and the user edited it. Used for UMA
@@ -186,6 +207,10 @@ class PasswordGenerationAgent : public content::RenderFrameObserver,
// True if the generation popup was shown during this navigation. Used to
// track UMA stats per page visit rather than per display, since the former
// is more interesting.
+ // TODO(crbug.com/845458): Remove this or change the description of the
+ // logged event as calling AutomaticgenerationStatusChanged will no longer
+ // imply that a popup is shown. This could instead be logged with the
+ // metrics collected on the browser process.
bool generation_popup_shown_;
// True if the editing popup was shown during this navigation. Used to track
diff --git a/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger.cc b/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger.cc
index 95ea490fb6d..46aae75d3e6 100644
--- a/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger.cc
+++ b/chromium/components/autofill/content/renderer/renderer_save_password_progress_logger.cc
@@ -5,9 +5,12 @@
#include "components/autofill/content/renderer/renderer_save_password_progress_logger.h"
#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "third_party/blink/public/web/web_form_control_element.h"
+using base::UintToString;
+
namespace autofill {
RendererSavePasswordProgressLogger::RendererSavePasswordProgressLogger(
@@ -25,8 +28,10 @@ void RendererSavePasswordProgressLogger::SendLog(const std::string& log) {
void RendererSavePasswordProgressLogger::LogElementName(
StringID label,
const blink::WebFormControlElement& element) {
- LogValue(label,
- base::Value(ScrubElementID((element.NameForAutofill().Utf16()))));
+ std::string text =
+ "name = " + ScrubElementID(element.NameForAutofill().Utf8()) +
+ ", renderer_id = " + UintToString(element.UniqueRendererFormControlId());
+ LogValue(label, base::Value(text));
}
} // namespace autofill
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 9fd6c67503f..c9c75857070 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
@@ -58,21 +58,12 @@ class FakeContentPasswordManagerDriver : public mojom::PasswordManagerDriver {
void SameDocumentNavigation(
const autofill::PasswordForm& password_form) override {}
- void PresaveGeneratedPassword(
- const autofill::PasswordForm& password_form) override {}
-
- void PasswordNoLongerGenerated(
- const autofill::PasswordForm& password_form) override {}
-
void ShowPasswordSuggestions(int key,
base::i18n::TextDirection text_direction,
const base::string16& typed_username,
int options,
const gfx::RectF& bounds) override {}
- void ShowManualFallbackSuggestion(base::i18n::TextDirection text_direction,
- const gfx::RectF& bounds) override {}
-
void RecordSavePasswordProgress(const std::string& log) override {
called_record_save_ = true;
log_ = log;
@@ -87,6 +78,8 @@ class FakeContentPasswordManagerDriver : public mojom::PasswordManagerDriver {
void CheckSafeBrowsingReputation(const GURL& form_action,
const GURL& frame_url) override {}
+ void FocusedInputChanged(bool is_fillable, bool is_password_field) override {}
+
// Records whether RecordSavePasswordProgress() gets called.
bool called_record_save_;
// Records data received via RecordSavePasswordProgress() call.
diff --git a/chromium/components/autofill/core/browser/BUILD.gn b/chromium/components/autofill/core/browser/BUILD.gn
index 7a1cb4f07ff..24528a81894 100644
--- a/chromium/components/autofill/core/browser/BUILD.gn
+++ b/chromium/components/autofill/core/browser/BUILD.gn
@@ -62,6 +62,8 @@ static_library("browser") {
"autofill_profile.h",
"autofill_profile_comparator.cc",
"autofill_profile_comparator.h",
+ "autofill_profile_sync_util.cc",
+ "autofill_profile_sync_util.h",
"autofill_profile_validation_util.cc",
"autofill_profile_validation_util.h",
"autofill_profile_validator.cc",
@@ -109,10 +111,17 @@ static_library("browser") {
"form_types.h",
"legal_message_line.cc",
"legal_message_line.h",
+ "local_card_migration_manager.cc",
+ "local_card_migration_manager.h",
"name_field.cc",
"name_field.h",
"password_generator.cc",
"password_generator.h",
+ "password_requirements_spec_fetcher.h",
+ "password_requirements_spec_fetcher_impl.cc",
+ "password_requirements_spec_fetcher_impl.h",
+ "password_requirements_spec_printer.cc",
+ "password_requirements_spec_printer.h",
"payments/full_card_request.cc",
"payments/full_card_request.h",
"payments/payments_client.cc",
@@ -139,6 +148,8 @@ static_library("browser") {
"region_data_loader_impl.cc",
"region_data_loader_impl.h",
"risk_data_loader.h",
+ "search_field.cc",
+ "search_field.h",
"state_names.cc",
"state_names.h",
"subkey_requester.cc",
@@ -159,6 +170,10 @@ static_library("browser") {
"webdata/autofill_entry.h",
"webdata/autofill_profile_data_type_controller.cc",
"webdata/autofill_profile_data_type_controller.h",
+ "webdata/autofill_profile_sync_bridge.cc",
+ "webdata/autofill_profile_sync_bridge.h",
+ "webdata/autofill_profile_sync_difference_tracker.cc",
+ "webdata/autofill_profile_sync_difference_tracker.h",
"webdata/autofill_profile_syncable_service.cc",
"webdata/autofill_profile_syncable_service.h",
"webdata/autofill_table.cc",
@@ -203,11 +218,16 @@ static_library("browser") {
}
if (!is_android) {
- sources += [ "ui/save_card_bubble_controller.h" ]
+ sources += [
+ "ui/local_card_migration_bubble_controller.h",
+ "ui/save_card_bubble_controller.h",
+ ]
}
if (!is_ios) {
sources += [
+ "autofill_address_policy_handler.cc",
+ "autofill_address_policy_handler.h",
"autofill_credit_card_policy_handler.cc",
"autofill_credit_card_policy_handler.h",
"autofill_policy_handler.cc",
@@ -221,10 +241,12 @@ static_library("browser") {
"//components/autofill/core/browser/proto",
"//components/autofill/core/common",
"//components/resources",
+ "//components/security_state/core",
"//skia",
"//third_party/libaddressinput",
]
deps = [
+ ":password_generator_fips181",
"//base",
"//base:i18n",
"//components/data_use_measurement/core",
@@ -233,7 +255,6 @@ static_library("browser") {
"//components/os_crypt",
"//components/pref_registry",
"//components/prefs",
- "//components/security_state/core",
"//components/signin/core/browser:signin_metrics",
"//components/strings",
"//components/sync",
@@ -245,11 +266,13 @@ static_library("browser") {
"//services/identity/public/cpp",
"//services/metrics/public/cpp:metrics_cpp",
"//services/metrics/public/cpp:ukm_builders",
+ "//services/network/public/cpp",
+ "//services/network/public/mojom",
"//sql",
- "//third_party/fips181",
"//third_party/icu",
"//third_party/libphonenumber",
"//third_party/re2",
+ "//ui/accessibility:accessibility",
"//ui/base",
"//ui/gfx",
"//ui/gfx/geometry",
@@ -295,10 +318,13 @@ static_library("test_support") {
"test_autofill_provider.h",
"test_credit_card_save_manager.cc",
"test_credit_card_save_manager.h",
+ "test_event_waiter.h",
"test_form_data_importer.cc",
"test_form_data_importer.h",
"test_form_structure.cc",
"test_form_structure.h",
+ "test_local_card_migration_manager.cc",
+ "test_local_card_migration_manager.h",
"test_personal_data_manager.cc",
"test_personal_data_manager.h",
"test_region_data_loader.cc",
@@ -326,6 +352,8 @@ static_library("test_support") {
"//components/ukm:test_support",
"//google_apis:test_support",
"//services/identity/public/cpp:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//skia",
"//testing/gtest",
"//third_party/libaddressinput:util",
@@ -334,6 +362,17 @@ static_library("test_support") {
]
}
+static_library("password_generator_fips181") {
+ sources = [
+ "password_generator_fips181.cc",
+ "password_generator_fips181.h",
+ ]
+ deps = [
+ "//base",
+ "//third_party/fips181",
+ ]
+}
+
bundle_data("unit_tests_bundle_data") {
sources = [
"//components/test/data/autofill/merge/input/ambiguous.in",
@@ -380,6 +419,7 @@ source_set("unit_tests") {
"autofill_merge_unittest.cc",
"autofill_metrics_unittest.cc",
"autofill_profile_comparator_unittest.cc",
+ "autofill_profile_sync_util_unittest.cc",
"autofill_profile_unittest.cc",
"autofill_profile_validation_util_unittest.cc",
"autofill_profile_validator_unittest.cc",
@@ -397,8 +437,11 @@ source_set("unit_tests") {
"form_field_unittest.cc",
"form_structure_unittest.cc",
"legal_message_line_unittest.cc",
+ "local_card_migration_manager_unittest.cc",
"name_field_unittest.cc",
+ "password_generator_fips181_unittest.cc",
"password_generator_unittest.cc",
+ "password_requirements_spec_fetcher_unittest.cc",
"payments/full_card_request_unittest.cc",
"payments/payments_client_unittest.cc",
"payments/payments_service_url_unittest.cc",
@@ -408,10 +451,13 @@ source_set("unit_tests") {
"phone_number_unittest.cc",
"rationalization_util_unittest.cc",
"region_combobox_model_unittest.cc",
+ "search_field_unittest.cc",
"subkey_requester_unittest.cc",
"ui/card_unmask_prompt_controller_impl_unittest.cc",
"validation_unittest.cc",
"webdata/autocomplete_sync_bridge_unittest.cc",
+ "webdata/autofill_profile_sync_bridge_unittest.cc",
+ "webdata/autofill_profile_sync_difference_tracker_unittest.cc",
"webdata/autofill_profile_syncable_service_unittest.cc",
"webdata/autofill_table_unittest.cc",
"webdata/autofill_wallet_metadata_syncable_service_unittest.cc",
@@ -425,6 +471,7 @@ source_set("unit_tests") {
if (!is_ios) {
sources += [
+ "autofill_address_policy_handler_unittest.cc",
"autofill_credit_card_policy_handler_unittest.cc",
"autofill_policy_handler_unittest.cc",
]
@@ -432,6 +479,7 @@ source_set("unit_tests") {
deps = [
":browser",
+ ":password_generator_fips181",
":test_support",
":unit_tests_bundle_data",
"//base",
@@ -457,6 +505,8 @@ source_set("unit_tests") {
"//net:test_support",
"//services/identity/public/cpp:test_support",
"//services/metrics/public/cpp:ukm_builders",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//sql",
"//testing/gmock",
"//testing/gtest",
@@ -484,3 +534,12 @@ fuzzer_test("form_structure_fuzzer") {
seed_corpus = "form_structure_fuzzer_corpus"
dict = "form_structure_fuzzer.dict"
}
+
+fuzzer_test("password_generator_fips181_fuzzer") {
+ sources = [
+ "password_generator_fips181_fuzzer.cc",
+ ]
+ deps = [
+ ":password_generator_fips181",
+ ]
+}
diff --git a/chromium/components/autofill/core/browser/DEPS b/chromium/components/autofill/core/browser/DEPS
index fa356faa138..5bef9b837fa 100644
--- a/chromium/components/autofill/core/browser/DEPS
+++ b/chromium/components/autofill/core/browser/DEPS
@@ -23,16 +23,22 @@ include_rules = [
"+net",
"+services/identity/public",
"+services/metrics/public",
+ "+services/network/public",
+ "+services/network/test",
"+sql",
"+third_party/fips181",
"+third_party/libaddressinput", # For address i18n.
"+third_party/libphonenumber", # For phone number i18n.
"+third_party/re2",
+ "+ui/accessibility",
"+ui/base",
"+ui/gfx",
]
specific_include_rules = {
+ "autofill_download_manager_unittest\.cc": [
+ "+services/network/test",
+ ],
"autofill_manager_unittest\.cc": [
"+components/ukm",
],
@@ -45,4 +51,7 @@ specific_include_rules = {
"test_autofill_client\.h": [
"+components/ukm",
],
+ "password_requirements_spec_fetcher_unittest\.cc": [
+ "+services/network/test",
+ ]
}
diff --git a/chromium/components/autofill/core/browser/address_combobox_model_unittest.cc b/chromium/components/autofill/core/browser/address_combobox_model_unittest.cc
index 487e735b239..a886a88e19d 100644
--- a/chromium/components/autofill/core/browser/address_combobox_model_unittest.cc
+++ b/chromium/components/autofill/core/browser/address_combobox_model_unittest.cc
@@ -17,6 +17,7 @@ const char kAppLocale[] = "fr-CA";
TEST(AddressComboboxModelTest, Empty) {
TestPersonalDataManager test_personal_data_manager;
+ test_personal_data_manager.SetAutofillProfileEnabled(true);
AddressComboboxModel model(test_personal_data_manager, kAppLocale, "");
EXPECT_EQ(1, model.GetItemCount());
@@ -27,6 +28,7 @@ TEST(AddressComboboxModelTest, Empty) {
TEST(AddressComboboxModelTest, OneAddress) {
TestPersonalDataManager test_personal_data_manager;
+ test_personal_data_manager.SetAutofillProfileEnabled(true);
AutofillProfile profile1(test::GetFullProfile());
test_personal_data_manager.AddProfile(profile1);
@@ -45,6 +47,7 @@ TEST(AddressComboboxModelTest, OneAddress) {
TEST(AddressComboboxModelTest, TwoAddresses) {
TestPersonalDataManager test_personal_data_manager;
+ test_personal_data_manager.SetAutofillProfileEnabled(true);
AutofillProfile profile1(test::GetFullProfile());
AutofillProfile profile2(test::GetFullProfile2());
@@ -70,6 +73,7 @@ TEST(AddressComboboxModelTest, TwoAddresses) {
TEST(AddressComboboxModelTest, AddAnAddress) {
TestPersonalDataManager test_personal_data_manager;
+ test_personal_data_manager.SetAutofillProfileEnabled(true);
AutofillProfile profile1(test::GetFullProfile());
test_personal_data_manager.AddProfile(profile1);
diff --git a/chromium/components/autofill/core/browser/address_field.cc b/chromium/components/autofill/core/browser/address_field.cc
index 5b78bfc3372..4ef78fef4f6 100644
--- a/chromium/components/autofill/core/browser/address_field.cc
+++ b/chromium/components/autofill/core/browser/address_field.cc
@@ -63,6 +63,10 @@ std::unique_ptr<FormField> AddressField::Parse(AutofillScanner* scanner) {
ParseField(scanner, base::UTF8ToUTF16(kAddressNameIgnoredRe),
nullptr)) {
continue;
+ // Ignore email addresses.
+ } else if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kEmailRe),
+ MATCH_DEFAULT | MATCH_TEXT_AREA, nullptr)) {
+ continue;
} else if (address_field->ParseAddressLines(scanner) ||
address_field->ParseCityStateZipCode(scanner) ||
address_field->ParseCountry(scanner) ||
@@ -90,8 +94,6 @@ std::unique_ptr<FormField> AddressField::Parse(AutofillScanner* scanner) {
}
continue;
- } else if (address_field->ParseSearchTerm(scanner)) {
- continue;
} else {
// No field found.
break;
@@ -100,15 +102,10 @@ std::unique_ptr<FormField> AddressField::Parse(AutofillScanner* scanner) {
// If we have identified any address fields in this field then it should be
// added to the list of fields.
- if (address_field->company_ ||
- address_field->address1_ ||
- address_field->address2_ ||
- address_field->address3_ ||
- address_field->street_address_ ||
- address_field->city_ ||
- address_field->state_ ||
- address_field->zip_ ||
- address_field->zip4_ ||
+ if (address_field->company_ || address_field->address1_ ||
+ address_field->address2_ || address_field->address3_ ||
+ address_field->street_address_ || address_field->city_ ||
+ address_field->state_ || address_field->zip_ || address_field->zip4_ ||
address_field->country_) {
// Don't slurp non-labeled fields at the end into the address.
if (has_trailing_non_labeled_fields)
@@ -130,8 +127,7 @@ AddressField::AddressField()
state_(nullptr),
zip_(nullptr),
zip4_(nullptr),
- country_(nullptr),
- search_term_(nullptr) {}
+ country_(nullptr) {}
void AddressField::AddClassifications(
FieldCandidatesMap* field_candidates) const {
@@ -160,19 +156,10 @@ void AddressField::AddClassifications(
field_candidates);
AddClassification(country_, ADDRESS_HOME_COUNTRY, kBaseAddressParserScore,
field_candidates);
- AddClassification(search_term_, SEARCH_TERM, kBaseAddressParserScore,
- field_candidates);
-}
-
-bool AddressField::ParseSearchTerm(AutofillScanner* scanner) {
- if (search_term_ && !search_term_->IsEmpty())
- return false;
-
- return ParseField(scanner, UTF8ToUTF16(kSearchTermRe), &search_term_);
}
bool AddressField::ParseCompany(AutofillScanner* scanner) {
- if (company_ && !company_->IsEmpty())
+ if (company_)
return false;
return ParseField(scanner, UTF8ToUTF16(kCompanyRe), &company_);
@@ -236,7 +223,7 @@ bool AddressField::ParseAddressLines(AutofillScanner* scanner) {
}
bool AddressField::ParseCountry(AutofillScanner* scanner) {
- if (country_ && !country_->IsEmpty())
+ if (country_)
return false;
scanner->SaveCursor();
diff --git a/chromium/components/autofill/core/browser/address_field.h b/chromium/components/autofill/core/browser/address_field.h
index 472b4a2d08e..a6117cb81f8 100644
--- a/chromium/components/autofill/core/browser/address_field.h
+++ b/chromium/components/autofill/core/browser/address_field.h
@@ -47,10 +47,6 @@ class AddressField : public FormField {
FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseCountry);
FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseTwoLineAddressMissingLabel);
FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseCompany);
- FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseSearchTermFirst);
- FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseSearchTermSecond);
- FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseSearchTermIsolated);
- FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseNonSearchTermWithSearch);
static const int kZipCodeMatchType;
static const int kCityMatchType;
@@ -64,7 +60,6 @@ class AddressField : public FormField {
bool ParseZipCode(AutofillScanner* scanner);
bool ParseCity(AutofillScanner* scanner);
bool ParseState(AutofillScanner* scanner);
- bool ParseSearchTerm(AutofillScanner* scanner);
// Parses the current field pointed to by |scanner|, if it exists, and tries
// to figure out whether the field's type: city, state, zip, or none of those.
@@ -98,7 +93,6 @@ class AddressField : public FormField {
AutofillField* zip_;
AutofillField* zip4_; // optional ZIP+4; we don't fill this yet.
AutofillField* country_;
- AutofillField* search_term_;
DISALLOW_COPY_AND_ASSIGN(AddressField);
};
diff --git a/chromium/components/autofill/core/browser/address_field_unittest.cc b/chromium/components/autofill/core/browser/address_field_unittest.cc
index 3ce59ca9fed..4791ab09125 100644
--- a/chromium/components/autofill/core/browser/address_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/address_field_unittest.cc
@@ -277,108 +277,4 @@ TEST_F(AddressFieldTest, ParseCompany) {
field_candidates_map_[ASCIIToUTF16("company1")].BestHeuristicType());
}
-TEST_F(AddressFieldTest, ParseSearchTermFirst) {
- FormFieldData search_field;
- search_field.form_control_type = "text";
-
- search_field.label = ASCIIToUTF16("Search");
- search_field.name = ASCIIToUTF16("search");
-
- FormFieldData address_field;
- address_field.form_control_type = "text";
-
- address_field.label = ASCIIToUTF16("Address");
- address_field.name = ASCIIToUTF16("address");
-
- list_.push_back(
- std::make_unique<AutofillField>(search_field, ASCIIToUTF16("search1")));
- list_.push_back(
- std::make_unique<AutofillField>(address_field, ASCIIToUTF16("address")));
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_NE(nullptr, field_.get());
- field_->AddClassifications(&field_candidates_map_);
- ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("search1")) !=
- field_candidates_map_.end());
- EXPECT_EQ(SEARCH_TERM,
- field_candidates_map_[ASCIIToUTF16("search1")].BestHeuristicType());
-}
-
-TEST_F(AddressFieldTest, ParseSearchTermSecond) {
- FormFieldData search_field;
- search_field.form_control_type = "text";
-
- search_field.label = ASCIIToUTF16("Search");
- search_field.name = ASCIIToUTF16("search");
-
- FormFieldData address_field;
- address_field.form_control_type = "text";
-
- address_field.label = ASCIIToUTF16("Address");
- address_field.name = ASCIIToUTF16("address");
-
- list_.push_back(
- std::make_unique<AutofillField>(address_field, ASCIIToUTF16("address")));
- list_.push_back(
- std::make_unique<AutofillField>(search_field, ASCIIToUTF16("search1")));
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_NE(nullptr, field_.get());
- field_->AddClassifications(&field_candidates_map_);
- ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("search1")) !=
- field_candidates_map_.end());
- EXPECT_EQ(SEARCH_TERM,
- field_candidates_map_[ASCIIToUTF16("search1")].BestHeuristicType());
-}
-
-// For fields that are identified and not detected, (practically expanded
-// version of UNKNOWN fields,) we would not detect them if they are isolated.
-TEST_F(AddressFieldTest, ParseSearchTermIsolated) {
- FormFieldData search_field;
- search_field.form_control_type = "text";
-
- search_field.label = ASCIIToUTF16("Search");
- search_field.name = ASCIIToUTF16("search");
-
- list_.push_back(
- std::make_unique<AutofillField>(search_field, ASCIIToUTF16("search1")));
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_EQ(nullptr, field_.get());
-}
-
-// For a "search xx" phrase, xx has priority to search, if xx is a valid
-// fillable type.
-TEST_F(AddressFieldTest, ParseNonSearchTermWithSearch) {
- FormFieldData addr_field;
- addr_field.form_control_type = "text";
-
- addr_field.label = ASCIIToUTF16("Search Address");
- addr_field.name = ASCIIToUTF16("search_addr");
-
- FormFieldData company_field;
- company_field.form_control_type = "text";
-
- company_field.label = ASCIIToUTF16("Company");
- company_field.name = ASCIIToUTF16("company1");
-
- list_.push_back(
- std::make_unique<AutofillField>(addr_field, ASCIIToUTF16("search_addr")));
- list_.push_back(
- std::make_unique<AutofillField>(company_field, ASCIIToUTF16("company1")));
-
- AutofillScanner scanner(list_);
- field_ = Parse(&scanner);
- ASSERT_NE(nullptr, field_.get());
- field_->AddClassifications(&field_candidates_map_);
- ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("search_addr")) !=
- field_candidates_map_.end());
- EXPECT_EQ(
- ADDRESS_HOME_LINE1,
- field_candidates_map_[ASCIIToUTF16("search_addr")].BestHeuristicType());
-}
-
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager.cc b/chromium/components/autofill/core/browser/autocomplete_history_manager.cc
index f9f06cf23b9..d54741f0e61 100644
--- a/chromium/components/autofill/core/browser/autocomplete_history_manager.cc
+++ b/chromium/components/autofill/core/browser/autocomplete_history_manager.cc
@@ -90,14 +90,14 @@ void AutocompleteHistoryManager::OnWillSubmitForm(const FormData& form) {
// - value is not a SSN
// - field was not identified as a CVC field (this is handled in
// AutofillManager)
+ // - field is focusable
+ // - not a presentation field
std::vector<FormFieldData> values;
for (const FormFieldData& field : form.fields) {
- if (!field.value.empty() &&
- !field.name.empty() &&
- IsTextField(field) &&
- field.should_autocomplete &&
- !IsValidCreditCardNumber(field.value) &&
- !IsSSN(field.value)) {
+ if (!field.value.empty() && !field.name.empty() && IsTextField(field) &&
+ field.should_autocomplete && !IsValidCreditCardNumber(field.value) &&
+ !IsSSN(field.value) && field.is_focusable &&
+ field.role != FormFieldData::ROLE_ATTRIBUTE_PRESENTATION) {
values.push_back(field);
}
}
@@ -134,7 +134,7 @@ void AutocompleteHistoryManager::SendSuggestions(
}
}
- external_delegate_->OnSuggestionsReturned(query_id_, suggestions);
+ external_delegate_->OnSuggestionsReturned(query_id_, suggestions, false);
query_id_ = 0;
}
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 c22cf0c45c4..cdf182a3bc7 100644
--- a/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc
@@ -186,6 +186,48 @@ TEST_F(AutocompleteHistoryManagerTest, FieldWithAutocompleteOff) {
autocomplete_manager_->OnWillSubmitForm(form);
}
+// Tests that text entered into fields that are not focusable is not sent to the
+// WebDatabase to be saved.
+TEST_F(AutocompleteHistoryManagerTest, NonFocusableField) {
+ FormData form;
+ form.name = ASCIIToUTF16("MyForm");
+ form.origin = GURL("http://myform.com/form.html");
+ form.action = GURL("http://myform.com/submit.html");
+
+ // Unfocusable field.
+ FormFieldData field;
+ field.label = ASCIIToUTF16("Something esoteric");
+ field.name = ASCIIToUTF16("esoterica");
+ field.value = ASCIIToUTF16("a truly esoteric value, I assure you");
+ field.form_control_type = "text";
+ field.is_focusable = false;
+ form.fields.push_back(field);
+
+ EXPECT_CALL(*web_data_service_, AddFormFields(_)).Times(0);
+ autocomplete_manager_->OnWillSubmitForm(form);
+}
+
+// Tests that text entered into presentation fields is not sent to the
+// WebDatabase to be saved.
+TEST_F(AutocompleteHistoryManagerTest, PresentationField) {
+ FormData form;
+ form.name = ASCIIToUTF16("MyForm");
+ form.origin = GURL("http://myform.com/form.html");
+ form.action = GURL("http://myform.com/submit.html");
+
+ // Presentation field.
+ FormFieldData field;
+ field.label = ASCIIToUTF16("Something esoteric");
+ field.name = ASCIIToUTF16("esoterica");
+ field.value = ASCIIToUTF16("a truly esoteric value, I assure you");
+ field.form_control_type = "text";
+ field.role = FormFieldData::ROLE_ATTRIBUTE_PRESENTATION;
+ form.fields.push_back(field);
+
+ EXPECT_CALL(*web_data_service_, AddFormFields(_)).Times(0);
+ autocomplete_manager_->OnWillSubmitForm(form);
+}
+
namespace {
class MockAutofillExternalDelegate : public AutofillExternalDelegate {
@@ -195,9 +237,10 @@ class MockAutofillExternalDelegate : public AutofillExternalDelegate {
: AutofillExternalDelegate(autofill_manager, autofill_driver) {}
~MockAutofillExternalDelegate() override {}
- MOCK_METHOD3(OnSuggestionsReturned,
+ MOCK_METHOD4(OnSuggestionsReturned,
void(int query_id,
const std::vector<Suggestion>& suggestions,
+ bool autoselect_first_suggestion,
bool is_all_server_suggestions));
private:
@@ -233,7 +276,7 @@ TEST_F(AutocompleteHistoryManagerTest, ExternalDelegate) {
autocomplete_history_manager.SetExternalDelegate(&external_delegate);
// Should trigger a call to OnSuggestionsReturned, verified by the mock.
- EXPECT_CALL(external_delegate, OnSuggestionsReturned(_, _, _));
+ EXPECT_CALL(external_delegate, OnSuggestionsReturned(_, _, _, _));
autocomplete_history_manager.SendSuggestions(nullptr);
}
@@ -258,9 +301,9 @@ TEST_F(AutocompleteHistoryManagerTest, NoAutocompleteSuggestionsForTextarea) {
FormFieldData field;
test::CreateTestFormField("Address", "address", "", "textarea", &field);
- EXPECT_CALL(
- external_delegate,
- OnSuggestionsReturned(0, testing::Truly(IsEmptySuggestionVector), _));
+ EXPECT_CALL(external_delegate,
+ OnSuggestionsReturned(0, testing::Truly(IsEmptySuggestionVector),
+ false, _));
autocomplete_history_manager.OnGetAutocompleteSuggestions(
0,
field.name,
diff --git a/chromium/components/autofill/core/browser/autofill_address_policy_handler.cc b/chromium/components/autofill/core/browser/autofill_address_policy_handler.cc
new file mode 100644
index 00000000000..5e930c8dee7
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_address_policy_handler.cc
@@ -0,0 +1,36 @@
+// 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/autofill_address_policy_handler.h"
+
+#include "base/values.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_value_map.h"
+
+namespace autofill {
+
+AutofillAddressPolicyHandler::AutofillAddressPolicyHandler()
+ : TypeCheckingPolicyHandler(policy::key::kAutofillAddressEnabled,
+ base::Value::Type::BOOLEAN) {}
+
+AutofillAddressPolicyHandler::~AutofillAddressPolicyHandler() {}
+
+void AutofillAddressPolicyHandler::ApplyPolicySettings(
+ const policy::PolicyMap& policies,
+ PrefValueMap* prefs) {
+ const base::Value* value = policies.GetValue(policy_name());
+ bool autofill_profile_enabled;
+ if (value && value->GetAsBoolean(&autofill_profile_enabled) &&
+ !autofill_profile_enabled) {
+ prefs->SetBoolean(autofill::prefs::kAutofillProfileEnabled, false);
+ } else {
+ // Temporary fix for M69. If there is no policy explicitly disabling this
+ // pref, it should be set to true.
+ prefs->SetBoolean(autofill::prefs::kAutofillProfileEnabled, true);
+ }
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_address_policy_handler.h b/chromium/components/autofill/core/browser/autofill_address_policy_handler.h
new file mode 100644
index 00000000000..20bb884718f
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_address_policy_handler.h
@@ -0,0 +1,30 @@
+// 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_AUTOFILL_ADDRESS_POLICY_HANDLER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_ADDRESS_POLICY_HANDLER_H_
+
+#include "base/macros.h"
+#include "components/policy/core/browser/configuration_policy_handler.h"
+#include "components/policy/policy_export.h"
+
+namespace autofill {
+
+// ConfigurationPolicyHandler for the AutofillAddressEnabled policy.
+class AutofillAddressPolicyHandler : public policy::TypeCheckingPolicyHandler {
+ public:
+ AutofillAddressPolicyHandler();
+ ~AutofillAddressPolicyHandler() override;
+
+ // ConfigurationPolicyHandler methods:
+ void ApplyPolicySettings(const policy::PolicyMap& policies,
+ PrefValueMap* prefs) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutofillAddressPolicyHandler);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_ADDRESS_POLICY_HANDLER_H_
diff --git a/chromium/components/autofill/core/browser/autofill_address_policy_handler_unittest.cc b/chromium/components/autofill/core/browser/autofill_address_policy_handler_unittest.cc
new file mode 100644
index 00000000000..dd299921092
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_address_policy_handler_unittest.cc
@@ -0,0 +1,78 @@
+// 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/autofill_address_policy_handler.h"
+
+#include <memory>
+
+#include "base/values.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_value_map.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+// Test cases for the Autofill address policy setting.
+class AutofillAddressPolicyHandlerTest : public testing::Test {};
+
+TEST_F(AutofillAddressPolicyHandlerTest, Default) {
+ policy::PolicyMap policy;
+ PrefValueMap prefs;
+ AutofillAddressPolicyHandler handler;
+ handler.ApplyPolicySettings(policy, &prefs);
+
+ // Temporary fix for M69. The pref is enabled by default unless it's disabled
+ // by policy.
+ const base::Value* value = nullptr;
+ ASSERT_TRUE(prefs.GetValue(autofill::prefs::kAutofillProfileEnabled, &value));
+ EXPECT_TRUE(value);
+ bool autofill_profile_enabled = false;
+ ASSERT_TRUE(value->GetAsBoolean(&autofill_profile_enabled));
+ EXPECT_TRUE(autofill_profile_enabled);
+}
+
+TEST_F(AutofillAddressPolicyHandlerTest, Enabled) {
+ policy::PolicyMap policy;
+ policy.Set(policy::key::kAutofillAddressEnabled,
+ policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
+ policy::POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(true),
+ nullptr);
+ PrefValueMap prefs;
+ AutofillAddressPolicyHandler handler;
+ handler.ApplyPolicySettings(policy, &prefs);
+
+ // Temporary fix for M69. The pref is enabled by default unless it's disabled
+ // by policy.
+ const base::Value* value = nullptr;
+ ASSERT_TRUE(prefs.GetValue(autofill::prefs::kAutofillProfileEnabled, &value));
+ EXPECT_TRUE(value);
+ bool autofill_profile_enabled = false;
+ ASSERT_TRUE(value->GetAsBoolean(&autofill_profile_enabled));
+ EXPECT_TRUE(autofill_profile_enabled);
+}
+
+TEST_F(AutofillAddressPolicyHandlerTest, Disabled) {
+ policy::PolicyMap policy;
+ policy.Set(policy::key::kAutofillAddressEnabled,
+ policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
+ policy::POLICY_SOURCE_CLOUD, std::make_unique<base::Value>(false),
+ nullptr);
+ PrefValueMap prefs;
+ AutofillAddressPolicyHandler handler;
+ handler.ApplyPolicySettings(policy, &prefs);
+
+ // Disabling Autofill for profiles should switch the prefs to managed.
+ const base::Value* value = nullptr;
+ EXPECT_TRUE(prefs.GetValue(autofill::prefs::kAutofillProfileEnabled, &value));
+ ASSERT_TRUE(value);
+ bool autofill_profile_enabled = true;
+ bool result = value->GetAsBoolean(&autofill_profile_enabled);
+ ASSERT_TRUE(result);
+ EXPECT_FALSE(autofill_profile_enabled);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc b/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc
index d7fb2f020c8..342c81ed767 100644
--- a/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc
@@ -104,7 +104,8 @@ class AutofillAssistantTest : public testing::Test {
std::unique_ptr<FormStructure> CreateValidCreditCardForm() {
std::unique_ptr<FormStructure> form_structure;
form_structure.reset(new FormStructure(CreateValidCreditCardFormData()));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
return form_structure;
}
@@ -153,7 +154,8 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_Secure) {
// Can be shown if the context is secure.
FormData form = CreateValidCreditCardFormData();
std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::vector<std::unique_ptr<FormStructure>> form_structures;
form_structures.push_back(std::move(form_structure));
@@ -170,7 +172,8 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_NotSecure) {
form.origin = GURL("http://myform.com");
form.action = GURL("http://myform.com/submit");
std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::vector<std::unique_ptr<FormStructure>> form_structures;
form_structures.push_back(std::move(form_structure));
@@ -185,7 +188,8 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_Javascript) {
FormData form = CreateValidCreditCardFormData();
form.action = GURL("javascript:alert('hello');");
std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::vector<std::unique_ptr<FormStructure>> form_structures;
form_structures.push_back(std::move(form_structure));
@@ -200,7 +204,8 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_WeirdJs) {
FormData form = CreateValidCreditCardFormData();
form.action = GURL("javascript:myFunc");
std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::vector<std::unique_ptr<FormStructure>> form_structures;
form_structures.push_back(std::move(form_structure));
@@ -214,7 +219,8 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_EmptyAction) {
FormData form = CreateValidCreditCardFormData();
form.action = GURL();
std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::vector<std::unique_ptr<FormStructure>> form_structures;
form_structures.push_back(std::move(form_structure));
diff --git a/chromium/components/autofill/core/browser/autofill_client.h b/chromium/components/autofill/core/browser/autofill_client.h
index 77f5dd8390e..d49be23802a 100644
--- a/chromium/components/autofill/core/browser/autofill_client.h
+++ b/chromium/components/autofill/core/browser/autofill_client.h
@@ -15,6 +15,8 @@
#include "base/strings/string16.h"
#include "base/values.h"
#include "components/autofill/core/browser/risk_data_loader.h"
+#include "components/security_state/core/security_state.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
#include "ui/base/window_open_disposition.h"
#include "url/gurl.h"
@@ -44,6 +46,7 @@ namespace autofill {
class AddressNormalizer;
class AutofillPopupDelegate;
+class AutofillProfile;
class AutofillWebDataService;
class CardUnmaskDelegate;
class CreditCard;
@@ -110,12 +113,23 @@ class AutofillClient : public RiskDataLoader {
// Gets the UKM service associated with this client (for metrics).
virtual ukm::UkmRecorder* GetUkmRecorder() = 0;
+ // Gets the UKM source id associated with this client (for metrics).
+ virtual ukm::SourceId GetUkmSourceId() = 0;
+
// Gets an AddressNormalizer instance (can be null).
virtual AddressNormalizer* GetAddressNormalizer() = 0;
+ // Gets the security level used for recording histograms for the current
+ // context if possible, SECURITY_LEVEL_COUNT otherwise.
+ virtual security_state::SecurityLevel GetSecurityLevelForUmaHistograms() = 0;
+
// Causes the Autofill settings UI to be shown.
virtual void ShowAutofillSettings() = 0;
+ // Runs |callback| if the |profile| should be imported as personal data.
+ virtual void ConfirmSaveAutofillProfile(const AutofillProfile& profile,
+ base::OnceClosure callback) = 0;
+
// A user has attempted to use a masked card. Prompt them for further
// information to proceed.
virtual void ShowUnmaskPrompt(const CreditCard& card,
@@ -123,17 +137,22 @@ class AutofillClient : public RiskDataLoader {
base::WeakPtr<CardUnmaskDelegate> delegate) = 0;
virtual void OnUnmaskVerificationResult(PaymentsRpcResult result) = 0;
+ // Runs |closure| if the user accepts the migration process.
+ virtual void ShowLocalCardMigrationPrompt(base::OnceClosure closure) = 0;
+
// Runs |callback| if the |card| should be imported as personal data.
// |metric_logger| can be used to log user actions.
virtual void ConfirmSaveCreditCardLocally(const CreditCard& card,
const base::Closure& callback) = 0;
// Runs |callback| if the |card| should be uploaded to Payments. Displays the
- // contents of |legal_message| to the user.
+ // contents of |legal_message| to the user. Displays a cardholder name
+ // textfield in the bubble if |should_request_name_from_user| is true.
virtual void ConfirmSaveCreditCardToCloud(
const CreditCard& card,
std::unique_ptr<base::DictionaryValue> legal_message,
- const base::Closure& callback) = 0;
+ bool should_request_name_from_user,
+ base::OnceCallback<void(const base::string16&)> callback) = 0;
// Will show an infobar to get user consent for Credit Card assistive filling.
// Will run |callback| on success.
@@ -156,6 +175,7 @@ class AutofillClient : public RiskDataLoader {
const gfx::RectF& element_bounds,
base::i18n::TextDirection text_direction,
const std::vector<Suggestion>& suggestions,
+ bool autoselect_first_suggestion,
base::WeakPtr<AutofillPopupDelegate> delegate) = 0;
// Update the data list values shown by the Autofill popup, if visible.
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 d16a9ffd4d7..06b9deec8e9 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
@@ -26,6 +26,10 @@ void AutofillCreditCardPolicyHandler::ApplyPolicySettings(
if (value && value->GetAsBoolean(&autofill_credit_card_enabled) &&
!autofill_credit_card_enabled) {
prefs->SetBoolean(autofill::prefs::kAutofillCreditCardEnabled, false);
+ } else {
+ // Temporary fix for M69. If there is no policy explicitly disabling this
+ // pref, it should be set to true.
+ prefs->SetBoolean(autofill::prefs::kAutofillCreditCardEnabled, true);
}
}
diff --git a/chromium/components/autofill/core/browser/autofill_credit_card_policy_handler_unittest.cc b/chromium/components/autofill/core/browser/autofill_credit_card_policy_handler_unittest.cc
index 6a911d0cdc8..4f548ca1b14 100644
--- a/chromium/components/autofill/core/browser/autofill_credit_card_policy_handler_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_credit_card_policy_handler_unittest.cc
@@ -24,8 +24,16 @@ TEST_F(AutofillCreditCardPolicyHandlerTest, Default) {
PrefValueMap prefs;
AutofillCreditCardPolicyHandler handler;
handler.ApplyPolicySettings(policy, &prefs);
- EXPECT_FALSE(
- prefs.GetValue(autofill::prefs::kAutofillCreditCardEnabled, nullptr));
+
+ // Temporary fix for M69. The pref is enabled by default unless it's disabled
+ // by policy.
+ const base::Value* value = nullptr;
+ ASSERT_TRUE(
+ prefs.GetValue(autofill::prefs::kAutofillCreditCardEnabled, &value));
+ EXPECT_TRUE(value);
+ bool autofill_credit_card_enabled = false;
+ ASSERT_TRUE(value->GetAsBoolean(&autofill_credit_card_enabled));
+ EXPECT_TRUE(autofill_credit_card_enabled);
}
TEST_F(AutofillCreditCardPolicyHandlerTest, Enabled) {
@@ -38,9 +46,15 @@ TEST_F(AutofillCreditCardPolicyHandlerTest, Enabled) {
AutofillCreditCardPolicyHandler handler;
handler.ApplyPolicySettings(policy, &prefs);
- // Enabling Autofill for credit cards should not set the prefs.
- EXPECT_FALSE(
- prefs.GetValue(autofill::prefs::kAutofillCreditCardEnabled, nullptr));
+ // Temporary fix for M69. The pref is enabled by default unless it's disabled
+ // by policy.
+ const base::Value* value = nullptr;
+ ASSERT_TRUE(
+ prefs.GetValue(autofill::prefs::kAutofillCreditCardEnabled, &value));
+ EXPECT_TRUE(value);
+ bool autofill_credit_card_enabled = false;
+ ASSERT_TRUE(value->GetAsBoolean(&autofill_credit_card_enabled));
+ EXPECT_TRUE(autofill_credit_card_enabled);
}
TEST_F(AutofillCreditCardPolicyHandlerTest, Disabled) {
@@ -58,10 +72,10 @@ TEST_F(AutofillCreditCardPolicyHandlerTest, Disabled) {
EXPECT_TRUE(
prefs.GetValue(autofill::prefs::kAutofillCreditCardEnabled, &value));
ASSERT_TRUE(value);
- bool autofill_credt_card_enabled = true;
- bool result = value->GetAsBoolean(&autofill_credt_card_enabled);
+ bool autofill_credit_card_enabled = true;
+ bool result = value->GetAsBoolean(&autofill_credit_card_enabled);
ASSERT_TRUE(result);
- EXPECT_FALSE(autofill_credt_card_enabled);
+ EXPECT_FALSE(autofill_credit_card_enabled);
}
} // namespace autofill \ No newline at end of file
diff --git a/chromium/components/autofill/core/browser/autofill_data_util.cc b/chromium/components/autofill/core/browser/autofill_data_util.cc
index 06232ab31a9..7f2b78b89e0 100644
--- a/chromium/components/autofill/core/browser/autofill_data_util.cc
+++ b/chromium/components/autofill/core/browser/autofill_data_util.cc
@@ -44,6 +44,11 @@ const PaymentRequestData kPaymentRequestData[]{
IDS_AUTOFILL_CC_UNION_PAY},
{autofill::kVisaCard, "visa", IDR_AUTOFILL_CC_VISA, IDS_AUTOFILL_CC_VISA},
};
+
+const PaymentRequestData kGooglePayBrandingRequestData = {
+ "googlePay", "googlePay", IDR_AUTOFILL_GOOGLE_PAY,
+ IDS_AUTOFILL_CC_GOOGLE_PAY};
+
const PaymentRequestData kGenericPaymentRequestData = {
autofill::kGenericCard, "generic", IDR_AUTOFILL_CC_GENERIC,
IDS_AUTOFILL_CC_GENERIC};
@@ -420,6 +425,9 @@ const PaymentRequestData& GetPaymentRequestData(
if (issuer_network == data.issuer_network)
return data;
}
+ if (issuer_network == kGooglePayBrandingRequestData.issuer_network) {
+ return kGooglePayBrandingRequestData;
+ }
return kGenericPaymentRequestData;
}
diff --git a/chromium/components/autofill/core/browser/autofill_download_manager.cc b/chromium/components/autofill/core/browser/autofill_download_manager.cc
index 478ecb4cd5b..6c2fa15f626 100644
--- a/chromium/components/autofill/core/browser/autofill_download_manager.cc
+++ b/chromium/components/autofill/core/browser/autofill_download_manager.cc
@@ -34,6 +34,8 @@
#include "net/http/http_status_code.h"
#include "net/http/http_util.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace autofill {
@@ -69,12 +71,6 @@ const net::BackoffEntry::Policy kAutofillBackoffPolicy = {
false,
};
-#if defined(GOOGLE_CHROME_BUILD)
-const char kClientName[] = "Google+Chrome";
-#else
-const char kClientName[] = "Chromium";
-#endif // defined(GOOGLE_CHROME_BUILD)
-
const char kDefaultAutofillServerURL[] =
"https://clients1.google.com/tbproxy/af/";
@@ -291,8 +287,7 @@ AutofillDownloadManager::AutofillDownloadManager(AutofillDriver* driver,
observer_(observer),
autofill_server_url_(GetAutofillServerURL()),
max_form_cache_size_(kMaxFormCacheSize),
- fetcher_backoff_(&kAutofillBackoffPolicy),
- fetcher_id_for_unittest_(0),
+ loader_backoff_(&kAutofillBackoffPolicy),
weak_factory_(this) {
DCHECK(observer_);
}
@@ -368,81 +363,73 @@ bool AutofillDownloadManager::StartUploadRequest(
return StartRequest(std::move(request_data));
}
-std::tuple<GURL, net::URLFetcher::RequestType>
-AutofillDownloadManager::GetRequestURLAndMethod(
+std::tuple<GURL, std::string> AutofillDownloadManager::GetRequestURLAndMethod(
const FormRequestData& request_data) const {
- net::URLFetcher::RequestType method = net::URLFetcher::POST;
- std::string query_str(base::StrCat({"client=", kClientName}));
+ std::string method("POST");
+ std::string query_str;
if (request_data.request_type == AutofillDownloadManager::REQUEST_QUERY) {
if (request_data.payload.length() <= kMaxQueryGetSize &&
base::FeatureList::IsEnabled(features::kAutofillCacheQueryResponses)) {
- method = net::URLFetcher::GET;
+ method = "GET";
std::string base64_payload;
base::Base64UrlEncode(request_data.payload,
base::Base64UrlEncodePolicy::INCLUDE_PADDING,
&base64_payload);
- base::StrAppend(&query_str, {"&q=", base64_payload});
+ base::StrAppend(&query_str, {"q=", base64_payload});
}
- UMA_HISTOGRAM_BOOLEAN("Autofill.Query.Method",
- (method == net::URLFetcher::GET) ? 0 : 1);
+ UMA_HISTOGRAM_BOOLEAN("Autofill.Query.Method", (method == "GET") ? 0 : 1);
}
GURL::Replacements replacements;
- replacements.SetQueryStr(std::move(query_str));
+ replacements.SetQueryStr(query_str);
GURL url = autofill_server_url_
.Resolve(RequestTypeToString(request_data.request_type))
.ReplaceComponents(replacements);
-
- return std::make_tuple(std::move(url), method);
+ return std::make_tuple(std::move(url), std::move(method));
}
bool AutofillDownloadManager::StartRequest(FormRequestData request_data) {
- net::URLRequestContextGetter* request_context =
- driver_->GetURLRequestContext();
- DCHECK(request_context);
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
+ driver_->GetURLLoaderFactory();
+ DCHECK(url_loader_factory);
// Get the URL and method to use for this request.
- net::URLFetcher::RequestType method;
+ std::string method;
GURL request_url;
std::tie(request_url, method) = GetRequestURLAndMethod(request_data);
- // Id is ignored for regular chrome, in unit test id's for fake fetcher
- // factory will be 0, 1, 2, ...
- std::unique_ptr<net::URLFetcher> fetcher = net::URLFetcher::Create(
- fetcher_id_for_unittest_++, request_url, method, this,
- GetNetworkTrafficAnnotation(request_data.request_type));
-
- data_use_measurement::DataUseUserData::AttachToFetcher(
- fetcher.get(), data_use_measurement::DataUseUserData::AUTOFILL);
- fetcher->SetAutomaticallyRetryOn5xx(false);
- fetcher->SetRequestContext(request_context);
- fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DO_NOT_SEND_COOKIES);
- if (method == net::URLFetcher::POST) {
- fetcher->SetUploadData("text/proto", request_data.payload);
- }
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = request_url;
+ resource_request->load_flags =
+ net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
+ resource_request->method = method;
// Add Chrome experiment state to the request headers.
- net::HttpRequestHeaders headers;
- // Note: It's OK to pass SignedIn::kNo if it's unknown, as it does not affect
- // transmission of experiments coming from the variations server.
- variations::AppendVariationHeaders(fetcher->GetOriginalURL(),
- driver_->IsIncognito()
- ? variations::InIncognito::kYes
- : variations::InIncognito::kNo,
- variations::SignedIn::kNo, &headers);
- fetcher->SetExtraRequestHeaders(headers.ToString());
-
- // Transfer ownership of the fetcher into url_fetchers_. Temporarily hang
- // onto the raw pointer to use it as a key and to kick off the request;
- // transferring ownership (std::move) invalidates the |fetcher| variable.
- auto* raw_fetcher = fetcher.get();
- url_fetchers_[raw_fetcher] =
- std::make_pair(std::move(fetcher), std::move(request_data));
- raw_fetcher->Start();
+ variations::AppendVariationHeadersUnknownSignedIn(
+ request_url,
+ driver_->IsIncognito() ? variations::InIncognito::kYes
+ : variations::InIncognito::kNo,
+ &resource_request->headers);
+
+ auto simple_loader = network::SimpleURLLoader::Create(
+ std::move(resource_request),
+ GetNetworkTrafficAnnotation(request_data.request_type));
+ if (method == "POST")
+ simple_loader->AttachStringForUpload(request_data.payload, "text/proto");
+ // Transfer ownership of the loader into url_loaders_. Temporarily hang
+ // onto the raw pointer to use it as a key and to kick off the request;
+ // transferring ownership (std::move) invalidates the |simple_loader|
+ // variable.
+ auto* raw_simple_loader = simple_loader.get();
+ url_loaders_.push_back(std::move(simple_loader));
+ raw_simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory.get(),
+ base::BindOnce(&AutofillDownloadManager::OnSimpleLoaderComplete,
+ base::Unretained(this), std::move(--url_loaders_.end()),
+ std::move(request_data)));
return true;
}
@@ -498,24 +485,22 @@ std::string AutofillDownloadManager::GetCombinedSignature(
return signature;
}
-void AutofillDownloadManager::OnURLFetchComplete(
- const net::URLFetcher* source) {
- auto it = url_fetchers_.find(const_cast<net::URLFetcher*>(source));
- if (it == url_fetchers_.end()) {
- // Looks like crash on Mac is possibly caused with callback entering here
- // with unknown fetcher when network is refreshed.
- return;
- }
-
- // Move the fetcher and request out of the active fetchers list.
- std::unique_ptr<net::URLFetcher> fetcher = std::move(it->second.first);
- FormRequestData request_data = std::move(it->second.second);
- url_fetchers_.erase(it);
+void AutofillDownloadManager::OnSimpleLoaderComplete(
+ std::list<std::unique_ptr<network::SimpleURLLoader>>::iterator it,
+ FormRequestData request_data,
+ std::unique_ptr<std::string> response_body) {
+ // Move the loader out of the active loaders list.
+ std::unique_ptr<network::SimpleURLLoader> simple_loader = std::move(*it);
+ url_loaders_.erase(it);
CHECK(request_data.form_signatures.size());
- const int response_code = fetcher->GetResponseCode();
- const bool success = (response_code == net::HTTP_OK);
- fetcher_backoff_.InformOfRequest(success);
+ int response_code = -1;
+ if (simple_loader->ResponseInfo() && simple_loader->ResponseInfo()->headers) {
+ response_code = simple_loader->ResponseInfo()->headers->response_code();
+ }
+
+ const bool success = !!response_body;
+ loader_backoff_.InformOfRequest(success);
LogHttpResponseCode(request_data.request_type, response_code);
@@ -541,16 +526,15 @@ void AutofillDownloadManager::OnURLFetchComplete(
base::BindOnce(
base::IgnoreResult(&AutofillDownloadManager::StartRequest),
weak_factory_.GetWeakPtr(), std::move(request_data)),
- fetcher_backoff_.GetTimeUntilRelease());
+ loader_backoff_.GetTimeUntilRelease());
return;
}
if (request_data.request_type == AutofillDownloadManager::REQUEST_QUERY) {
- std::string response_body;
- fetcher->GetResponseAsString(&response_body);
- CacheQueryRequest(request_data.form_signatures, response_body);
- UMA_HISTOGRAM_BOOLEAN("Autofill.Query.WasInCache", fetcher->WasCached());
- observer_->OnLoadedServerPredictions(std::move(response_body),
+ CacheQueryRequest(request_data.form_signatures, *response_body);
+ UMA_HISTOGRAM_BOOLEAN("Autofill.Query.WasInCache",
+ simple_loader->LoadedFromCache());
+ observer_->OnLoadedServerPredictions(std::move(*response_body),
request_data.form_signatures);
return;
}
diff --git a/chromium/components/autofill/core/browser/autofill_download_manager.h b/chromium/components/autofill/core/browser/autofill_download_manager.h
index e0a564b14ad..675bb0dde30 100644
--- a/chromium/components/autofill/core/browser/autofill_download_manager.h
+++ b/chromium/components/autofill/core/browser/autofill_download_manager.h
@@ -20,8 +20,7 @@
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "net/base/backoff_entry.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
+#include "services/network/public/cpp/simple_url_loader.h"
#include "url/gurl.h"
namespace autofill {
@@ -30,7 +29,7 @@ class AutofillDriver;
class FormStructure;
// Handles getting and updating Autofill heuristics.
-class AutofillDownloadManager : public net::URLFetcherDelegate {
+class AutofillDownloadManager {
public:
enum RequestType { REQUEST_QUERY, REQUEST_UPLOAD, };
@@ -64,7 +63,7 @@ class AutofillDownloadManager : public net::URLFetcherDelegate {
// |observer| - observer to notify on successful completion or error.
AutofillDownloadManager(AutofillDriver* driver,
Observer* observer);
- ~AutofillDownloadManager() override;
+ virtual ~AutofillDownloadManager();
// Starts a query request to Autofill servers. The observer is called with the
// list of the fields of all requested forms.
@@ -102,7 +101,7 @@ class AutofillDownloadManager : public net::URLFetcherDelegate {
// described by |request_data|. If the returned method is GET, the URL
// fully encompasses the request, do not include request_data.payload when
// transmitting the request.
- std::tuple<GURL, net::URLFetcher::RequestType> GetRequestURLAndMethod(
+ std::tuple<GURL, std::string> GetRequestURLAndMethod(
const FormRequestData& request_data) const;
// Initiates request to Autofill servers to download/upload type predictions.
@@ -130,8 +129,10 @@ class AutofillDownloadManager : public net::URLFetcherDelegate {
std::string GetCombinedSignature(
const std::vector<std::string>& forms_in_query) const;
- // net::URLFetcherDelegate implementation:
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ void OnSimpleLoaderComplete(
+ std::list<std::unique_ptr<network::SimpleURLLoader>>::iterator it,
+ FormRequestData request_data,
+ std::unique_ptr<std::string> response_body);
// The AutofillDriver that this instance will use. Must not be null, and must
// outlive this instance.
@@ -145,23 +146,15 @@ class AutofillDownloadManager : public net::URLFetcherDelegate {
// final path component for the request and the query params.
GURL autofill_server_url_;
- // For each requested form for both query and upload we create a separate
- // request and save its info. As url fetcher is identified by its address
- // we use a map between fetchers and info. The value type is a pair of an
- // owning pointer to the key and the actual FormRequestData.
- std::map<net::URLFetcher*,
- std::pair<std::unique_ptr<net::URLFetcher>, FormRequestData>>
- url_fetchers_;
+ // Loaders used for the processing the requests. Invalidated after completion.
+ std::list<std::unique_ptr<network::SimpleURLLoader>> url_loaders_;
// Cached QUERY requests.
QueryRequestCache cached_forms_;
size_t max_form_cache_size_;
// Used for exponential backoff of requests.
- net::BackoffEntry fetcher_backoff_;
-
- // Needed for unit-test.
- int fetcher_id_for_unittest_;
+ net::BackoffEntry loader_backoff_;
base::WeakPtrFactory<AutofillDownloadManager> weak_factory_;
};
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 cccf4753bfd..af9142ea5ac 100644
--- a/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_download_manager_unittest.cc
@@ -11,13 +11,15 @@
#include <utility>
#include <vector>
+#include "base/format_macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task_scheduler/post_task.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_command_line.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
@@ -35,9 +37,12 @@
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
-#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_status.h"
#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -54,18 +59,6 @@ const int METHOD_GET = 0;
const int METHOD_POST = 1;
const int CACHE_MISS = 0;
const int CACHE_HIT = 1;
-// Call |fetcher->OnURLFetchComplete()| as the URLFetcher would when
-// a response is received. Params allow caller to set fake status.
-void FakeOnURLFetchComplete(net::TestURLFetcher* fetcher,
- int response_code,
- const std::string& response_body) {
- fetcher->set_url(GURL());
- fetcher->set_status(net::URLRequestStatus());
- fetcher->set_response_code(response_code);
- fetcher->SetResponseString(response_body);
-
- fetcher->delegate()->OnURLFetchComplete(fetcher);
-}
std::vector<FormStructure*> ToRawPointerVector(
const std::vector<std::unique_ptr<FormStructure>>& list) {
@@ -81,7 +74,7 @@ std::vector<FormStructure*> ToRawPointerVector(
// AutofillDownloadManager::Observer and creates an instance of
// AutofillDownloadManager. Then it records responses to different initiated
// requests, which are verified later. To mock network requests
-// TestURLFetcherFactory is used, which creates URLFetchers that do not
+// TestURLLoaderFactory is used, which creates SimpleURLLoaders that do not
// go over the wire, but allow calling back HTTP responses directly.
// The responses in test are out of order and verify: successful query request,
// successful upload request, failed upload request.
@@ -89,10 +82,11 @@ class AutofillDownloadManagerTest : public AutofillDownloadManager::Observer,
public testing::Test {
public:
AutofillDownloadManagerTest()
- : request_context_(base::MakeRefCounted<net::TestURLRequestContextGetter>(
- base::ThreadTaskRunnerHandle::Get())),
+ : test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)),
download_manager_(&driver_, this) {
- driver_.SetURLRequestContext(request_context_.get());
+ driver_.SetSharedURLLoaderFactory(test_shared_loader_factory_);
}
void LimitCache(size_t cache_size) {
@@ -143,9 +137,19 @@ class AutofillDownloadManagerTest : public AutofillDownloadManager::Observer,
ResponseData() : type_of_response(REQUEST_QUERY_FAILED), error(0) {}
};
+ network::TestURLLoaderFactory::PendingRequest* GetPendingRequest(
+ size_t index = 0) {
+ if (index >= test_url_loader_factory_.pending_requests()->size())
+ return nullptr;
+ auto* request = &(*test_url_loader_factory_.pending_requests())[index];
+ DCHECK(request);
+ return request;
+ }
+
base::MessageLoop message_loop_;
std::list<ResponseData> responses_;
- scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
TestAutofillDriver driver_;
AutofillDownloadManager download_manager_;
};
@@ -154,9 +158,6 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) {
base::test::ScopedFeatureList fl;
fl.InitAndEnableFeature(features::kAutofillCacheQueryResponses);
- // Create and register factory.
- net::TestURLFetcherFactory factory;
-
FormData form;
FormFieldData field;
@@ -277,23 +278,24 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) {
// Return them out of sequence.
// Request 1: Successful upload.
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(1);
- ASSERT_TRUE(fetcher);
- FakeOnURLFetchComplete(fetcher, net::HTTP_OK, std::string(responses[1]));
+ auto* request = GetPendingRequest(1);
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request, responses[1]);
histogram.ExpectBucketCount("Autofill.Upload.HttpResponseCode", net::HTTP_OK,
1);
// Request 2: Unsuccessful upload.
- fetcher = factory.GetFetcherByID(2);
- ASSERT_TRUE(fetcher);
- FakeOnURLFetchComplete(fetcher, net::HTTP_NOT_FOUND,
- std::string(responses[2]));
+ request = GetPendingRequest(2);
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request, network::CreateResourceResponseHead(net::HTTP_NOT_FOUND),
+ responses[2], network::URLLoaderCompletionStatus(net::OK));
histogram.ExpectBucketCount("Autofill.Upload.HttpResponseCode",
net::HTTP_NOT_FOUND, 1);
+
// Request 0: Successful query.
- fetcher = factory.GetFetcherByID(0);
- ASSERT_TRUE(fetcher);
- FakeOnURLFetchComplete(fetcher, net::HTTP_OK, std::string(responses[0]));
+ request = GetPendingRequest(0);
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request, responses[0]);
EXPECT_EQ(3U, responses_.size());
histogram.ExpectBucketCount("Autofill.Query.WasInCache", CACHE_MISS, 1);
histogram.ExpectBucketCount("Autofill.Query.HttpResponseCode", net::HTTP_OK,
@@ -336,13 +338,14 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) {
// Request with id 4, not successful.
EXPECT_TRUE(
download_manager_.StartQueryRequest(ToRawPointerVector(form_structures)));
- fetcher = factory.GetFetcherByID(4);
- ASSERT_TRUE(fetcher);
+ request = GetPendingRequest(4);
histogram.ExpectUniqueSample("Autofill.ServerQueryResponse",
AutofillMetrics::QUERY_SENT, 2);
histogram.ExpectUniqueSample("Autofill.Query.Method", METHOD_GET, 2);
- FakeOnURLFetchComplete(fetcher, net::HTTP_INTERNAL_SERVER_ERROR,
- std::string(responses[0]));
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request,
+ network::CreateResourceResponseHead(net::HTTP_INTERNAL_SERVER_ERROR),
+ responses[0], network::URLLoaderCompletionStatus(net::OK));
histogram.ExpectBucketCount("Autofill.Query.HttpResponseCode",
net::HTTP_INTERNAL_SERVER_ERROR, 1);
@@ -360,10 +363,13 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) {
histogram.ExpectBucketCount("Autofill.ServerQueryResponse",
AutofillMetrics::QUERY_SENT, 3);
histogram.ExpectBucketCount("Autofill.Query.Method", METHOD_GET, 3);
- fetcher = factory.GetFetcherByID(5);
- ASSERT_TRUE(fetcher);
- fetcher->set_was_cached(true);
- FakeOnURLFetchComplete(fetcher, net::HTTP_OK, std::string(responses[0]));
+ request = GetPendingRequest(5);
+
+ network::URLLoaderCompletionStatus status(net::OK);
+ status.exists_in_cache = true;
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request, network::CreateResourceResponseHead(net::HTTP_OK), responses[0],
+ status);
// Check Request 5.
EXPECT_EQ(responses_.front().type_of_response,
@@ -388,16 +394,13 @@ TEST_F(AutofillDownloadManagerTest, QueryAndUploadTest) {
histogram.ExpectBucketCount("Autofill.ServerQueryResponse",
AutofillMetrics::QUERY_SENT, 4);
histogram.ExpectBucketCount("Autofill.Query.Method", METHOD_POST, 1);
- fetcher = factory.GetFetcherByID(6);
- ASSERT_TRUE(fetcher);
- FakeOnURLFetchComplete(fetcher, net::HTTP_OK, std::string(responses[0]));
+ request = GetPendingRequest(6);
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request, responses[0]);
histogram.ExpectBucketCount("Autofill.Query.WasInCache", CACHE_MISS, 2);
}
TEST_F(AutofillDownloadManagerTest, BackoffLogic_Query) {
- // Create and register factory.
- net::TestURLFetcherFactory factory;
-
FormData form;
FormFieldData field;
field.label = ASCIIToUTF16("address");
@@ -430,33 +433,38 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Query) {
histogram.ExpectUniqueSample("Autofill.ServerQueryResponse",
AutofillMetrics::QUERY_SENT, 1);
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
- ASSERT_TRUE(fetcher);
+ auto* request = GetPendingRequest(0);
// Request error incurs a retry after 1 second.
- FakeOnURLFetchComplete(fetcher, net::HTTP_INTERNAL_SERVER_ERROR, "");
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request,
+ network::CreateResourceResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), "",
+ network::URLLoaderCompletionStatus(net::OK));
+
EXPECT_EQ(1U, responses_.size());
- EXPECT_LT(download_manager_.fetcher_backoff_.GetTimeUntilRelease(),
+ EXPECT_LT(download_manager_.loader_backoff_.GetTimeUntilRelease(),
base::TimeDelta::FromMilliseconds(1100));
+ base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated(),
+ FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(1100));
- base::RunLoop().Run();
+ run_loop.Run();
// Get the retried request.
- fetcher = factory.GetFetcherByID(1);
- ASSERT_TRUE(fetcher);
+ request = GetPendingRequest(1);
// Next error incurs a retry after 2 seconds.
- FakeOnURLFetchComplete(fetcher, net::HTTP_REQUEST_ENTITY_TOO_LARGE,
- "<html></html>");
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request,
+ network::CreateResourceResponseHead(net::HTTP_REQUEST_ENTITY_TOO_LARGE),
+ "<html></html>", network::URLLoaderCompletionStatus(net::OK));
+
EXPECT_EQ(2U, responses_.size());
- EXPECT_LT(download_manager_.fetcher_backoff_.GetTimeUntilRelease(),
+ EXPECT_LT(download_manager_.loader_backoff_.GetTimeUntilRelease(),
base::TimeDelta::FromMilliseconds(2100));
// There should not be an additional retry.
- fetcher = factory.GetFetcherByID(2);
- ASSERT_FALSE(fetcher);
+ ASSERT_EQ(test_url_loader_factory_.NumPending(), 0);
histogram.ExpectBucketCount("Autofill.Query.HttpResponseCode",
net::HTTP_REQUEST_ENTITY_TOO_LARGE, 1);
auto buckets = histogram.GetAllSamples("Autofill.Query.FailingPayloadSize");
@@ -465,9 +473,6 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Query) {
}
TEST_F(AutofillDownloadManagerTest, BackoffLogic_Upload) {
- // Create and register factory.
- net::TestURLFetcherFactory factory;
-
FormData form;
FormFieldData field;
field.label = ASCIIToUTF16("address");
@@ -496,18 +501,21 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Upload) {
EXPECT_TRUE(download_manager_.StartUploadRequest(
*form_structure, true, ServerFieldTypeSet(), std::string(), true));
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
- ASSERT_TRUE(fetcher);
+ auto* request = GetPendingRequest(0);
// Error incurs a retry after 1 second.
- FakeOnURLFetchComplete(fetcher, net::HTTP_INTERNAL_SERVER_ERROR, "");
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request,
+ network::CreateResourceResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), "",
+ network::URLLoaderCompletionStatus(net::OK));
EXPECT_EQ(1U, responses_.size());
- EXPECT_LT(download_manager_.fetcher_backoff_.GetTimeUntilRelease(),
+ EXPECT_LT(download_manager_.loader_backoff_.GetTimeUntilRelease(),
base::TimeDelta::FromMilliseconds(1100));
+ base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated(),
+ FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(1100));
- base::RunLoop().Run();
+ run_loop.Run();
// Check that it was a failure.
EXPECT_EQ(AutofillDownloadManagerTest::REQUEST_UPLOAD_FAILED,
@@ -519,9 +527,9 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Upload) {
responses_.pop_front();
// Get the retried request, and make it successful.
- fetcher = factory.GetFetcherByID(1);
- ASSERT_TRUE(fetcher);
- FakeOnURLFetchComplete(fetcher, net::HTTP_OK, "");
+ request = GetPendingRequest(1);
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request, "");
// Check success of response.
EXPECT_EQ(AutofillDownloadManagerTest::UPLOAD_SUCCESSFULL,
@@ -536,11 +544,12 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Upload) {
base::HistogramTester histogram;
EXPECT_TRUE(download_manager_.StartUploadRequest(
*form_structure, true, ServerFieldTypeSet(), std::string(), true));
- fetcher = factory.GetFetcherByID(2);
- ASSERT_TRUE(fetcher);
- FakeOnURLFetchComplete(fetcher, net::HTTP_REQUEST_ENTITY_TOO_LARGE, "");
- fetcher = factory.GetFetcherByID(3);
- ASSERT_FALSE(fetcher);
+ request = GetPendingRequest(2);
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request,
+ network::CreateResourceResponseHead(net::HTTP_REQUEST_ENTITY_TOO_LARGE),
+ "", network::URLLoaderCompletionStatus(net::OK));
+ ASSERT_EQ(test_url_loader_factory_.NumPending(), 0);
histogram.ExpectBucketCount("Autofill.Upload.HttpResponseCode",
net::HTTP_REQUEST_ENTITY_TOO_LARGE, 1);
auto buckets = histogram.GetAllSamples("Autofill.Upload.FailingPayloadSize");
@@ -549,9 +558,6 @@ TEST_F(AutofillDownloadManagerTest, BackoffLogic_Upload) {
}
TEST_F(AutofillDownloadManagerTest, QueryTooManyFieldsTest) {
- // Create and register factory.
- net::TestURLFetcherFactory factory;
-
// Create a query that contains too many fields for the server.
std::vector<FormData> forms(21);
std::vector<std::unique_ptr<FormStructure>> form_structures;
@@ -572,9 +578,6 @@ TEST_F(AutofillDownloadManagerTest, QueryTooManyFieldsTest) {
}
TEST_F(AutofillDownloadManagerTest, QueryNotTooManyFieldsTest) {
- // Create and register factory.
- net::TestURLFetcherFactory factory;
-
// Create a query that contains a lot of fields, but not too many for the
// server.
std::vector<FormData> forms(25);
@@ -596,9 +599,6 @@ TEST_F(AutofillDownloadManagerTest, QueryNotTooManyFieldsTest) {
}
TEST_F(AutofillDownloadManagerTest, CacheQueryTest) {
- // Create and register factory.
- net::TestURLFetcherFactory factory;
-
FormData form;
FormFieldData field;
@@ -668,9 +668,9 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) {
// No responses yet
EXPECT_EQ(0U, responses_.size());
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
- ASSERT_TRUE(fetcher);
- FakeOnURLFetchComplete(fetcher, 200, std::string(responses[0]));
+ auto* request = GetPendingRequest(0);
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request, responses[0]);
ASSERT_EQ(1U, responses_.size());
EXPECT_EQ(responses[0], responses_.front().response);
@@ -694,9 +694,9 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) {
// No responses yet
EXPECT_EQ(0U, responses_.size());
- fetcher = factory.GetFetcherByID(1);
- ASSERT_TRUE(fetcher);
- FakeOnURLFetchComplete(fetcher, 200, std::string(responses[1]));
+ request = GetPendingRequest(1);
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request, responses[1]);
ASSERT_EQ(1U, responses_.size());
EXPECT_EQ(responses[1], responses_.front().response);
@@ -708,9 +708,9 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) {
histogram.ExpectUniqueSample("Autofill.ServerQueryResponse",
AutofillMetrics::QUERY_SENT, 4);
- fetcher = factory.GetFetcherByID(2);
- ASSERT_TRUE(fetcher);
- FakeOnURLFetchComplete(fetcher, 200, std::string(responses[2]));
+ request = GetPendingRequest(2);
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request, responses[2]);
ASSERT_EQ(1U, responses_.size());
EXPECT_EQ(responses[2], responses_.front().response);
@@ -741,9 +741,9 @@ TEST_F(AutofillDownloadManagerTest, CacheQueryTest) {
// No responses yet
EXPECT_EQ(0U, responses_.size());
- fetcher = factory.GetFetcherByID(3);
- ASSERT_TRUE(fetcher);
- FakeOnURLFetchComplete(fetcher, 200, std::string(responses[0]));
+ request = GetPendingRequest(3);
+ test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+ request, responses[0]);
ASSERT_EQ(1U, responses_.size());
EXPECT_EQ(responses[0], responses_.front().response);
}
@@ -767,10 +767,10 @@ class AutofillQueryTest : public AutofillDownloadManager::Observer,
server_.base_url().Resolve("/tbproxy/af/").spec().c_str());
// Intialize the autofill driver.
- request_context_ = base::MakeRefCounted<net::TestURLRequestContextGetter>(
- scoped_task_environment_.GetMainThreadTaskRunner());
+ shared_url_loader_factory_ =
+ base::MakeRefCounted<network::TestSharedURLLoaderFactory>();
driver_ = std::make_unique<TestAutofillDriver>();
- driver_->SetURLRequestContext(request_context_.get());
+ driver_->SetSharedURLLoaderFactory(shared_url_loader_factory_);
}
void TearDown() override {
@@ -800,7 +800,12 @@ class AutofillQueryTest : public AutofillDownloadManager::Observer,
response->set_code(net::HTTP_OK);
response->set_content(proto.SerializeAsString());
response->set_content_type("text/proto");
- response->AddCustomHeader("Cache-Control", "max-age=86400");
+ response->AddCustomHeader(
+ "Cache-Control",
+ base::StringPrintf(
+ "max-age=%" PRId64,
+ base::TimeDelta::FromMilliseconds(cache_expiration_in_milliseconds_)
+ .InSeconds()));
return response;
}
@@ -821,9 +826,10 @@ class AutofillQueryTest : public AutofillDownloadManager::Observer,
base::test::ScopedCommandLine scoped_command_line_;
base::test::ScopedFeatureList scoped_feature_list_;
EmbeddedTestServer server_;
+ int cache_expiration_in_milliseconds_ = 100000;
std::unique_ptr<base::RunLoop> run_loop_;
size_t call_count_ = 0;
- scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+ scoped_refptr<network::TestSharedURLLoaderFactory> shared_url_loader_factory_;
std::unique_ptr<TestAutofillDriver> driver_;
};
@@ -843,8 +849,9 @@ TEST_F(AutofillQueryTest, CacheableResponse) {
// Query for the form. This should go to the embedded server.
{
- SCOPED_TRACE("Firstl Query");
+ SCOPED_TRACE("First Query");
base::HistogramTester histogram;
+ call_count_ = 0;
ASSERT_NO_FATAL_FAILURE(SendQueryRequest(form_structures));
EXPECT_EQ(1u, call_count_);
histogram.ExpectBucketCount("Autofill.ServerQueryResponse",
@@ -853,15 +860,69 @@ TEST_F(AutofillQueryTest, CacheableResponse) {
histogram.ExpectBucketCount("Autofill.Query.WasInCache", CACHE_MISS, 1);
}
- // Query again for the form. This should go to the local cache.
+ // Query again the next day. This should go to the cache, since the max-age
+ // for the cached response is 2 days.
{
SCOPED_TRACE("Second Query");
base::HistogramTester histogram;
+ call_count_ = 0;
ASSERT_NO_FATAL_FAILURE(SendQueryRequest(form_structures));
- EXPECT_EQ(1u, call_count_);
+ EXPECT_EQ(0u, call_count_);
+ histogram.ExpectBucketCount("Autofill.ServerQueryResponse",
+ AutofillMetrics::QUERY_SENT, 1);
histogram.ExpectBucketCount("Autofill.Query.Method", METHOD_GET, 1);
histogram.ExpectBucketCount("Autofill.Query.WasInCache", CACHE_HIT, 1);
}
}
+TEST_F(AutofillQueryTest, ExpiredCacheInResponse) {
+ FormFieldData field;
+ field.label = ASCIIToUTF16("First Name:");
+ field.name = ASCIIToUTF16("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));
+
+ // Set the cache expiration interval to 0.
+ cache_expiration_in_milliseconds_ = 0;
+
+ // Query for the form. This should go to the embedded server.
+ {
+ SCOPED_TRACE("First Query");
+ base::HistogramTester histogram;
+ call_count_ = 0;
+ ASSERT_NO_FATAL_FAILURE(SendQueryRequest(form_structures));
+ EXPECT_EQ(1u, call_count_);
+ histogram.ExpectBucketCount("Autofill.ServerQueryResponse",
+ AutofillMetrics::QUERY_SENT, 1);
+ histogram.ExpectBucketCount("Autofill.Query.Method", METHOD_GET, 1);
+ histogram.ExpectBucketCount("Autofill.Query.WasInCache", CACHE_MISS, 1);
+ }
+
+ // The cache entry had a max age of 0 ms, so delaying only a few milliseconds
+ // ensures the cache expires and no request are served by cached content
+ // (ie this should go to the embedded server).
+ base::RunLoop run_loop;
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, run_loop.QuitClosure(),
+ base::TimeDelta::FromMilliseconds(100));
+ run_loop.Run();
+
+ {
+ SCOPED_TRACE("Second Query");
+ base::HistogramTester histogram;
+ call_count_ = 0;
+ ASSERT_NO_FATAL_FAILURE(SendQueryRequest(form_structures));
+ EXPECT_EQ(1u, call_count_);
+ histogram.ExpectBucketCount("Autofill.ServerQueryResponse",
+ AutofillMetrics::QUERY_SENT, 1);
+ histogram.ExpectBucketCount("Autofill.Query.Method", METHOD_GET, 1);
+ histogram.ExpectBucketCount("Autofill.Query.WasInCache", CACHE_MISS, 1);
+ }
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_driver.h b/chromium/components/autofill/core/browser/autofill_driver.h
index 2ed290d3321..5e71f95ce25 100644
--- a/chromium/components/autofill/core/browser/autofill_driver.h
+++ b/chromium/components/autofill/core/browser/autofill_driver.h
@@ -7,12 +7,17 @@
#include <vector>
+#include "base/memory/scoped_refptr.h"
#include "components/autofill/core/common/form_data.h"
namespace net {
class URLRequestContextGetter;
}
+namespace network {
+class SharedURLLoaderFactory;
+}
+
namespace gfx {
class RectF;
}
@@ -42,6 +47,10 @@ class AutofillDriver {
// Returns the URL request context information associated with this driver.
virtual net::URLRequestContextGetter* GetURLRequestContext() = 0;
+ // Returns the URL loader factory associated with this driver.
+ virtual scoped_refptr<network::SharedURLLoaderFactory>
+ GetURLLoaderFactory() = 0;
+
// Returns true iff the renderer is available for communication.
virtual bool RendererIsAvailable() = 0;
diff --git a/chromium/components/autofill/core/browser/autofill_experiments.cc b/chromium/components/autofill/core/browser/autofill_experiments.cc
index 04b3b3afa52..35548d3eea4 100644
--- a/chromium/components/autofill/core/browser/autofill_experiments.cc
+++ b/chromium/components/autofill/core/browser/autofill_experiments.cc
@@ -34,14 +34,10 @@ const base::Feature kAutofillCreditCardAssist{
"AutofillCreditCardAssist", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kAutofillScanCardholderName{
"AutofillScanCardholderName", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kAutofillCreditCardBankNameDisplay{
- "AutofillCreditCardBankNameDisplay", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kAutofillCreditCardAblationExperiment{
"AutofillCreditCardAblationExperiment", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kAutofillCreditCardPopupLayout{
- "AutofillCreditCardPopupLayout", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kAutofillCreditCardLastUsedDateDisplay{
- "AutofillCreditCardLastUsedDateDisplay", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kAutofillCreditCardLocalCardMigration{
+ "AutofillCreditCardLocalCardMigration", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kAutofillDeleteDisusedAddresses{
"AutofillDeleteDisusedAddresses", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kAutofillDeleteDisusedCreditCards{
@@ -53,6 +49,9 @@ const base::Feature kAutofillPreferServerNamePredictions{
const base::Feature kAutofillRationalizeFieldTypePredictions{
"AutofillRationalizeFieldTypePredictions",
base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kAutofillSaveCardDialogUnlabeledExpirationDate{
+ "AutofillSaveCardDialogUnlabeledExpirationDate",
+ base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kAutofillSuggestInvalidProfileData{
"AutofillSuggestInvalidProfileData", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kAutofillSuppressDisusedAddresses{
@@ -63,50 +62,30 @@ const base::Feature kAutofillUpstream{"AutofillUpstream",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kAutofillUpstreamAllowAllEmailDomains{
"AutofillUpstreamAllowAllEmailDomains", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kAutofillUpstreamSendDetectedValues{
- "AutofillUpstreamSendDetectedValues", base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kAutofillUpstreamAlwaysRequestCardholderName{
+ "AutofillUpstreamAlwaysRequestCardholderName",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kAutofillUpstreamBlankCardholderNameField{
+ "AutofillUpstreamBlankCardholderNameField",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kAutofillUpstreamEditableCardholderName{
+ "AutofillUpstreamEditableCardholderName",
+ base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kAutofillUpstreamSendPanFirstSix{
"AutofillUpstreamSendPanFirstSix", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kAutofillUpstreamUpdatePromptExplanation{
"AutofillUpstreamUpdatePromptExplanation",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kAutofillVoteUsingInvalidProfileData{
"AutofillVoteUsingInvalidProfileData", base::FEATURE_ENABLED_BY_DEFAULT};
const char kCreditCardSigninPromoImpressionLimitParamKey[] = "impression_limit";
-const char kAutofillCreditCardPopupBackgroundColorKey[] = "background_color";
-const char kAutofillCreditCardPopupDividerColorKey[] = "dropdown_divider_color";
-const char kAutofillCreditCardPopupValueBoldKey[] = "is_value_bold";
-const char kAutofillCreditCardPopupIsValueAndLabelInSingleLineKey[] =
- "is_value_and_label_in_single_line";
-const char kAutofillPopupDropdownItemHeightKey[] = "dropdown_item_height";
-const char kAutofillCreditCardPopupIsIconAtStartKey[] =
- "is_credit_card_icon_at_start";
-const char kAutofillPopupMarginKey[] = "margin";
-const char kAutofillCreditCardLastUsedDateShowExpirationDateKey[] =
- "show_expiration_date";
#if defined(OS_MACOSX)
const base::Feature kMacViewsAutofillPopup{"MacViewsAutofillPopup",
base::FEATURE_ENABLED_BY_DEFAULT};
#endif // defined(OS_MACOSX)
-namespace {
-
-// Returns parameter value in |kAutofillCreditCardPopupLayout| feature, or 0 if
-// parameter is not specified.
-unsigned int GetCreditCardPopupParameterUintValue(
- const std::string& param_name) {
- unsigned int value;
- const std::string param_value = variations::GetVariationParamValueByFeature(
- kAutofillCreditCardPopupLayout, param_name);
- if (!param_value.empty() && base::StringToUint(param_value, &value))
- return value;
- return 0;
-}
-
-} // namespace
-
bool IsAutofillEnabled(const PrefService* pref_service) {
return pref_service->GetBoolean(prefs::kAutofillEnabled);
}
@@ -125,77 +104,8 @@ bool IsAutofillCreditCardAssistEnabled() {
#endif
}
-bool IsAutofillCreditCardPopupLayoutExperimentEnabled() {
- return base::FeatureList::IsEnabled(kAutofillCreditCardPopupLayout);
-}
-
-bool IsAutofillCreditCardLastUsedDateDisplayExperimentEnabled() {
- return base::FeatureList::IsEnabled(kAutofillCreditCardLastUsedDateDisplay);
-}
-
-bool IsAutofillCreditCardBankNameDisplayExperimentEnabled() {
- return base::FeatureList::IsEnabled(kAutofillCreditCardBankNameDisplay);
-}
-
-// |GetCreditCardPopupParameterUintValue| returns 0 if experiment parameter is
-// not specified. 0 == |SK_ColorTRANSPARENT|.
-SkColor GetCreditCardPopupBackgroundColor() {
- return GetCreditCardPopupParameterUintValue(
- kAutofillCreditCardPopupBackgroundColorKey);
-}
-
-SkColor GetCreditCardPopupDividerColor() {
- return GetCreditCardPopupParameterUintValue(
- kAutofillCreditCardPopupDividerColorKey);
-}
-
-bool IsCreditCardPopupValueBold() {
- const std::string param_value = variations::GetVariationParamValueByFeature(
- kAutofillCreditCardPopupLayout, kAutofillCreditCardPopupValueBoldKey);
- return param_value == "true";
-}
-
-unsigned int GetPopupDropdownItemHeight() {
- return GetCreditCardPopupParameterUintValue(
- kAutofillPopupDropdownItemHeightKey);
-}
-
-bool IsIconInCreditCardPopupAtStart() {
- const std::string param_value = variations::GetVariationParamValueByFeature(
- kAutofillCreditCardPopupLayout, kAutofillCreditCardPopupIsIconAtStartKey);
- return param_value == "true";
-}
-
-bool ShowExpirationDateInAutofillCreditCardLastUsedDate() {
- const std::string param_value = variations::GetVariationParamValueByFeature(
- kAutofillCreditCardLastUsedDateDisplay,
- kAutofillCreditCardLastUsedDateShowExpirationDateKey);
- return param_value == "true";
-}
-
-// Modifies |suggestion| as follows if experiment to display value and label in
-// a single line is enabled.
-// Say, |value| is 'Visa ....1111' and |label| is '01/18' (expiration date).
-// Modifies |value| to 'Visa ....1111, exp 01/18' and clears |label|.
-void ModifyAutofillCreditCardSuggestion(Suggestion* suggestion) {
- DCHECK(IsAutofillCreditCardPopupLayoutExperimentEnabled());
- const std::string param_value = variations::GetVariationParamValueByFeature(
- kAutofillCreditCardPopupLayout,
- kAutofillCreditCardPopupIsValueAndLabelInSingleLineKey);
- if (param_value == "true") {
- const base::string16 format_string = l10n_util::GetStringUTF16(
- IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_LABEL_AND_ABBR);
- if (!format_string.empty()) {
- suggestion->value.append(l10n_util::GetStringFUTF16(
- IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_LABEL_AND_ABBR,
- suggestion->label));
- }
- suggestion->label.clear();
- }
-}
-
-unsigned int GetPopupMargin() {
- return GetCreditCardPopupParameterUintValue(kAutofillPopupMarginKey);
+bool IsAutofillCreditCardLocalCardMigrationExperimentEnabled() {
+ return base::FeatureList::IsEnabled(kAutofillCreditCardLocalCardMigration);
}
bool OfferStoreUnmaskedCards() {
@@ -268,8 +178,18 @@ bool IsCreditCardUploadEnabled(const PrefService* pref_service,
return base::FeatureList::IsEnabled(kAutofillUpstream);
}
-bool IsAutofillUpstreamSendDetectedValuesExperimentEnabled() {
- return base::FeatureList::IsEnabled(kAutofillUpstreamSendDetectedValues);
+bool IsAutofillUpstreamAlwaysRequestCardholderNameExperimentEnabled() {
+ return base::FeatureList::IsEnabled(
+ kAutofillUpstreamAlwaysRequestCardholderName);
+}
+
+bool IsAutofillUpstreamBlankCardholderNameFieldExperimentEnabled() {
+ return base::FeatureList::IsEnabled(
+ kAutofillUpstreamBlankCardholderNameField);
+}
+
+bool IsAutofillUpstreamEditableCardholderNameExperimentEnabled() {
+ return base::FeatureList::IsEnabled(kAutofillUpstreamEditableCardholderName);
}
bool IsAutofillUpstreamSendPanFirstSixExperimentEnabled() {
@@ -291,4 +211,18 @@ bool IsMacViewsAutofillPopupExperimentEnabled() {
}
#endif // defined(OS_MACOSX)
+bool ShouldUseNativeViews() {
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+ return base::FeatureList::IsEnabled(kAutofillExpandedPopupViews) ||
+ base::FeatureList::IsEnabled(::features::kExperimentalUi);
+#else
+ return false;
+#endif
+}
+
+bool IsAutofillSaveCardDialogUnlabeledExpirationDateEnabled() {
+ return base::FeatureList::IsEnabled(
+ kAutofillSaveCardDialogUnlabeledExpirationDate);
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_experiments.h b/chromium/components/autofill/core/browser/autofill_experiments.h
index 8a603e29765..7264f45c5fe 100644
--- a/chromium/components/autofill/core/browser/autofill_experiments.h
+++ b/chromium/components/autofill/core/browser/autofill_experiments.h
@@ -9,7 +9,6 @@
#include "base/strings/string16.h"
#include "build/build_config.h"
-#include "third_party/skia/include/core/SkColor.h"
class PrefService;
@@ -23,27 +22,26 @@ class SyncService;
namespace autofill {
-struct Suggestion;
-
extern const base::Feature kAutofillAlwaysFillAddresses;
extern const base::Feature kAutofillCreateDataForTest;
extern const base::Feature kAutofillCreditCardAssist;
extern const base::Feature kAutofillScanCardholderName;
extern const base::Feature kAutofillCreditCardAblationExperiment;
-extern const base::Feature kAutofillCreditCardBankNameDisplay;
-extern const base::Feature kAutofillCreditCardPopupLayout;
-extern const base::Feature kAutofillCreditCardLastUsedDateDisplay;
+extern const base::Feature kAutofillCreditCardLocalCardMigration;
extern const base::Feature kAutofillDeleteDisusedAddresses;
extern const base::Feature kAutofillDeleteDisusedCreditCards;
extern const base::Feature kAutofillExpandedPopupViews;
extern const base::Feature kAutofillPreferServerNamePredictions;
extern const base::Feature kAutofillRationalizeFieldTypePredictions;
+extern const base::Feature kAutofillSaveCardDialogUnlabeledExpirationDate;
extern const base::Feature kAutofillSuggestInvalidProfileData;
extern const base::Feature kAutofillSuppressDisusedAddresses;
extern const base::Feature kAutofillSuppressDisusedCreditCards;
extern const base::Feature kAutofillUpstream;
extern const base::Feature kAutofillUpstreamAllowAllEmailDomains;
-extern const base::Feature kAutofillUpstreamSendDetectedValues;
+extern const base::Feature kAutofillUpstreamAlwaysRequestCardholderName;
+extern const base::Feature kAutofillUpstreamBlankCardholderNameField;
+extern const base::Feature kAutofillUpstreamEditableCardholderName;
extern const base::Feature kAutofillUpstreamSendPanFirstSix;
extern const base::Feature kAutofillUpstreamUpdatePromptExplanation;
extern const base::Feature kAutofillVoteUsingInvalidProfileData;
@@ -80,57 +78,28 @@ bool IsCreditCardUploadEnabled(const PrefService* pref_service,
const syncer::SyncService* sync_service,
const std::string& user_email);
-// Returns whether the new Autofill credit card popup layout experiment is
-// enabled.
-bool IsAutofillCreditCardPopupLayoutExperimentEnabled();
-
-// Returns whether Autofill credit card last used date display experiment is
+// Returns whether Autofill credit card local card migration experiment is
// enabled.
-bool IsAutofillCreditCardLastUsedDateDisplayExperimentEnabled();
-
-// Returns whether Autofill credit card last used date shows expiration date.
-bool ShowExpirationDateInAutofillCreditCardLastUsedDate();
-
-// Returns whether Autofill credit card bank name display experiment is enabled.
-bool IsAutofillCreditCardBankNameDisplayExperimentEnabled();
-
-// Returns the background color for credit card autofill popup, or
-// |SK_ColorTRANSPARENT| if the new credit card autofill popup layout experiment
-// is not enabled.
-SkColor GetCreditCardPopupBackgroundColor();
-
-// Returns the divider color for credit card autofill popup, or
-// |SK_ColorTRANSPARENT| if the new credit card autofill popup layout experiment
-// is not enabled.
-SkColor GetCreditCardPopupDividerColor();
-
-// Returns true if the credit card autofill popup suggestion value is displayed
-// in bold type face.
-bool IsCreditCardPopupValueBold();
-
-// Returns the dropdown item height for autofill popup, returning 0 if the
-// dropdown item height isn't configured in an experiment to tweak autofill
-// popup layout.
-unsigned int GetPopupDropdownItemHeight();
-
-// Returns true if the icon in the credit card autofill popup must be displayed
-// before the credit card value or any other suggestion text.
-bool IsIconInCreditCardPopupAtStart();
-
-// Modifies the suggestion value and label if the new credit card autofill popup
-// experiment is enabled to tweak the display of the value and label.
-void ModifyAutofillCreditCardSuggestion(struct Suggestion* suggestion);
-
-// Returns the margin for the icon, label and between icon and label. Returns 0
-// if the margin isn't configured in an experiment to tweak autofill popup
-// layout.
-unsigned int GetPopupMargin();
-
-// Returns whether the experiment is enabled where Chrome Upstream always checks
-// to see if it can offer to save (even though some data like name, address, and
-// CVC might be missing) by sending metadata on what form values were detected
-// along with whether the user is a Google Payments customer.
-bool IsAutofillUpstreamSendDetectedValuesExperimentEnabled();
+bool IsAutofillCreditCardLocalCardMigrationExperimentEnabled();
+
+// For testing purposes; not to be launched. When enabled, Chrome Upstream
+// always requests that the user enters/confirms cardholder name in the
+// offer-to-save dialog, regardless of if it was present or if the user is a
+// Google Payments customer. Note that this will override the detected
+// cardholder name, if one was found.
+bool IsAutofillUpstreamAlwaysRequestCardholderNameExperimentEnabled();
+
+// For experimental purposes; not to be made available in chrome://flags. When
+// enabled and Chrome Upstream requests the cardholder name in the offer-to-save
+// dialog, the field will be blank instead of being prefilled with the name from
+// the user's Google Account.
+bool IsAutofillUpstreamBlankCardholderNameFieldExperimentEnabled();
+
+// Returns whether the experiment is enabled where Chrome Upstream can request
+// the user to enter/confirm cardholder name in the offer-to-save bubble if it
+// was not detected or was conflicting during the checkout flow and the user is
+// NOT a Google Payments customer.
+bool IsAutofillUpstreamEditableCardholderNameExperimentEnabled();
// Returns whether the experiment is enabled where Chrome Upstream sends the
// first six digits of the card PAN to Google Payments to help determine whether
@@ -148,6 +117,15 @@ bool IsAutofillUpstreamUpdatePromptExplanationExperimentEnabled();
bool IsMacViewsAutofillPopupExperimentEnabled();
#endif // defined(OS_MACOSX)
+// Returns true if the native Views implementation of the Desktop dropdown
+// should be used. This will also be true if the kExperimentalUi flag is true,
+// which forces a bunch of forthcoming UI changes on.
+bool ShouldUseNativeViews();
+
+// Returns true if expiration dates on the save card dialog should be
+// unlabeled, i.e. not preceded by "Exp."
+bool IsAutofillSaveCardDialogUnlabeledExpirationDateEnabled();
+
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_EXPERIMENTS_H_
diff --git a/chromium/components/autofill/core/browser/autofill_experiments_unittest.cc b/chromium/components/autofill/core/browser/autofill_experiments_unittest.cc
index 36bb8b727f6..7c8b01dcfd1 100644
--- a/chromium/components/autofill/core/browser/autofill_experiments_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_experiments_unittest.cc
@@ -49,7 +49,8 @@ TEST_F(AutofillExperimentsTest, DenyUpload_FeatureDisabled) {
TEST_F(AutofillExperimentsTest, DenyUpload_SyncServiceCannotStart) {
scoped_feature_list_.InitAndEnableFeature(kAutofillUpstream);
- sync_service_.SetCanSyncStart(false);
+ sync_service_.SetDisableReasons(
+ syncer::SyncService::DISABLE_REASON_USER_CHOICE);
EXPECT_FALSE(IsCreditCardUploadEnabled());
}
@@ -62,7 +63,7 @@ TEST_F(AutofillExperimentsTest, DenyUpload_AuthError) {
TEST_F(AutofillExperimentsTest,
DenyUpload_SyncServiceDoesNotHaveAutofillProfilePreferredDataType) {
scoped_feature_list_.InitAndEnableFeature(kAutofillUpstream);
- sync_service_.SetPreferredDataTypes(syncer::ModelTypeSet());
+ sync_service_.SetDataTypes(syncer::ModelTypeSet());
EXPECT_FALSE(IsCreditCardUploadEnabled());
}
diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.cc b/chromium/components/autofill/core/browser/autofill_external_delegate.cc
index f49a26785b2..aca02c8f337 100644
--- a/chromium/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/chromium/components/autofill/core/browser/autofill_external_delegate.cc
@@ -22,9 +22,11 @@
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/autofill_metrics.h"
#include "components/autofill/core/browser/popup_item_ids.h"
+#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_util.h"
#include "components/signin/core/browser/signin_metrics.h"
#include "components/strings/grit/components_strings.h"
+#include "ui/accessibility/platform/ax_platform_node.h"
#include "ui/base/l10n/l10n_util.h"
namespace autofill {
@@ -45,7 +47,6 @@ AutofillExternalDelegate::AutofillExternalDelegate(AutofillManager* manager,
driver_(driver),
query_id_(0),
has_autofill_suggestions_(false),
- has_shown_popup_for_current_edit_(false),
should_show_scan_credit_card_(false),
popup_type_(PopupType::kUnspecified),
should_show_cc_signin_promo_(false),
@@ -76,6 +77,7 @@ void AutofillExternalDelegate::OnQuery(int query_id,
void AutofillExternalDelegate::OnSuggestionsReturned(
int query_id,
const std::vector<Suggestion>& input_suggestions,
+ bool autoselect_first_suggestion,
bool is_all_server_suggestions) {
if (query_id != query_id_)
return;
@@ -90,8 +92,7 @@ void AutofillExternalDelegate::OnSuggestionsReturned(
// go between the values and menu items. Skip this when using the Native Views
// implementation, which has its own logic for distinguishing footer rows.
// TODO(crbug.com/831603): Remove this when the relevant feature is on 100%.
- if (!suggestions.empty() &&
- !base::FeatureList::IsEnabled(autofill::kAutofillExpandedPopupViews)) {
+ if (!suggestions.empty() && !autofill::ShouldUseNativeViews()) {
suggestions.push_back(Suggestion());
suggestions.back().frontend_id = POPUP_ITEM_ID_SEPARATOR;
}
@@ -103,11 +104,6 @@ void AutofillExternalDelegate::OnSuggestionsReturned(
scan_credit_card.frontend_id = POPUP_ITEM_ID_SCAN_CREDIT_CARD;
scan_credit_card.icon = base::ASCIIToUTF16("scanCreditCardIcon");
suggestions.push_back(scan_credit_card);
-
- if (!has_shown_popup_for_current_edit_) {
- AutofillMetrics::LogScanCreditCardPromptMetric(
- AutofillMetrics::SCAN_CARD_ITEM_SHOWN);
- }
}
// Only include "Autofill Options" special menu item if we have Autofill
@@ -166,10 +162,23 @@ void AutofillExternalDelegate::OnSuggestionsReturned(
// Send to display.
if (query_field_.is_focusable) {
- manager_->client()->ShowAutofillPopup(element_bounds_,
- query_field_.text_direction,
- suggestions,
- GetWeakPtr());
+ manager_->client()->ShowAutofillPopup(
+ element_bounds_, query_field_.text_direction, suggestions,
+ autoselect_first_suggestion, GetWeakPtr());
+ }
+}
+
+bool AutofillExternalDelegate::HasActiveScreenReader() const {
+ return ui::AXPlatformNode::GetAccessibilityMode().has_mode(
+ ui::AXMode::kScreenReader);
+}
+
+void AutofillExternalDelegate::OnAutofillAvailabilityEvent(
+ bool has_suggestions) {
+ if (has_suggestions) {
+ ui::AXPlatformNode::OnInputSuggestionsAvailable();
+ } else {
+ ui::AXPlatformNode::OnInputSuggestionsUnavailable();
}
}
@@ -184,10 +193,13 @@ void AutofillExternalDelegate::SetCurrentDataListValues(
}
void AutofillExternalDelegate::OnPopupShown() {
- manager_->DidShowSuggestions(
- has_autofill_suggestions_ && !has_shown_popup_for_current_edit_,
- query_form_, query_field_);
- has_shown_popup_for_current_edit_ |= has_autofill_suggestions_;
+ manager_->DidShowSuggestions(has_autofill_suggestions_, query_form_,
+ query_field_);
+
+ if (should_show_scan_credit_card_) {
+ AutofillMetrics::LogScanCreditCardPromptMetric(
+ AutofillMetrics::SCAN_CARD_ITEM_SHOWN);
+ }
}
void AutofillExternalDelegate::OnPopupHidden() {
@@ -269,8 +281,6 @@ bool AutofillExternalDelegate::RemoveSuggestion(const base::string16& value,
void AutofillExternalDelegate::DidEndTextFieldEditing() {
manager_->client()->HideAutofillPopup();
-
- has_shown_popup_for_current_edit_ = false;
}
void AutofillExternalDelegate::ClearPreviewedForm() {
@@ -355,9 +365,23 @@ void AutofillExternalDelegate::ApplyAutofillOptions(
// include a hint for keyboard accessory.
suggestions->push_back(Suggestion(GetSettingsSuggestionValue()));
suggestions->back().frontend_id = POPUP_ITEM_ID_AUTOFILL_OPTIONS;
+ // On Android and Desktop, Google Pay branding is shown along with Settings.
+ // So Google Pay Icon is just attached to an existing menu item.
if (is_all_server_suggestions)
suggestions->back().icon = base::ASCIIToUTF16("googlePay");
+// On iOS, GooglePayIcon comes at the begining and hence prepended to the list.
+#if defined(OS_IOS)
+ if (base::FeatureList::IsEnabled(
+ features::kAutofillDownstreamUseGooglePayBrandingOniOS) &&
+ is_all_server_suggestions) {
+ Suggestion googlepay_icon;
+ googlepay_icon.icon = base::ASCIIToUTF16("googlePay");
+ googlepay_icon.frontend_id = POPUP_ITEM_ID_GOOGLE_PAY_BRANDING;
+ suggestions->insert(suggestions->begin(), googlepay_icon);
+ }
+#endif
+
#if defined(OS_ANDROID)
if (IsKeyboardAccessoryEnabled()) {
suggestions->back().icon = base::ASCIIToUTF16("settings");
@@ -410,20 +434,14 @@ void AutofillExternalDelegate::InsertDataListValues(
base::string16 AutofillExternalDelegate::GetSettingsSuggestionValue()
const {
- if (base::FeatureList::IsEnabled(autofill::kAutofillExpandedPopupViews)) {
- if (GetPopupType() == PopupType::kAddresses)
- return l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE_ADDRESSES);
+ if (GetPopupType() == PopupType::kAddresses)
+ return l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE_ADDRESSES);
- if (GetPopupType() == PopupType::kCreditCards)
- return l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE_PAYMENT_METHODS);
-
- DCHECK_EQ(GetPopupType(), PopupType::kPersonalInformation);
- return l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE);
- }
+ if (GetPopupType() == PopupType::kCreditCards)
+ return l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE_PAYMENT_METHODS);
- return l10n_util::GetStringUTF16(
- IsKeyboardAccessoryEnabled() ? IDS_AUTOFILL_OPTIONS_CONTENT_DESCRIPTION
- : IDS_AUTOFILL_SETTINGS_POPUP);
+ DCHECK_EQ(GetPopupType(), PopupType::kPersonalInformation);
+ return l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE);
}
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.h b/chromium/components/autofill/core/browser/autofill_external_delegate.h
index 3a5bff6edcd..6441e5bb011 100644
--- a/chromium/components/autofill/core/browser/autofill_external_delegate.h
+++ b/chromium/components/autofill/core/browser/autofill_external_delegate.h
@@ -72,8 +72,16 @@ class AutofillExternalDelegate : public AutofillPopupDelegate {
// to be displayed. Called when an Autofill query result is available.
virtual void OnSuggestionsReturned(int query_id,
const std::vector<Suggestion>& suggestions,
+ bool autoselect_first_suggestion,
bool is_all_server_suggestions = false);
+ // Returns true if there is a screen reader installed on the machine.
+ virtual bool HasActiveScreenReader() const;
+
+ // Indicates on focus changed if autofill is available or unavailable, so
+ // state can be announced by screen readers.
+ virtual void OnAutofillAvailabilityEvent(bool has_suggestions);
+
// Set the data list value associated with the current field.
void SetCurrentDataListValues(
const std::vector<base::string16>& data_list_values,
@@ -147,10 +155,6 @@ class AutofillExternalDelegate : public AutofillPopupDelegate {
// Does the popup include any Autofill profile or credit card suggestions?
bool has_autofill_suggestions_;
- // Have we already shown Autofill suggestions for the field the user is
- // currently editing? Used to keep track of state for metrics logging.
- bool has_shown_popup_for_current_edit_;
-
bool should_show_scan_credit_card_;
PopupType popup_type_;
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 226b25921be..e35f340546a 100644
--- a/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -11,9 +11,9 @@
#include "base/message_loop/message_loop.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
-#include "base/test/user_action_tester.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/autofill_external_delegate.h"
@@ -24,6 +24,7 @@
#include "components/autofill/core/browser/suggestion_test_helpers.h"
#include "components/autofill/core/browser/test_autofill_client.h"
#include "components/autofill/core/browser/test_autofill_driver.h"
+#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h"
@@ -34,6 +35,7 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/geometry/rect.h"
+using autofill::features::kAutofillDownstreamUseGooglePayBrandingOniOS;
using base::ASCIIToUTF16;
using testing::_;
@@ -70,10 +72,11 @@ class MockAutofillClient : public TestAutofillClient {
MOCK_METHOD1(ScanCreditCard,
void(const CreditCardScanCallback& callbacK));
- MOCK_METHOD4(ShowAutofillPopup,
+ MOCK_METHOD5(ShowAutofillPopup,
void(const gfx::RectF& element_bounds,
base::i18n::TextDirection text_direction,
const std::vector<Suggestion>& suggestions,
+ bool autoselect_first_suggestion,
base::WeakPtr<AutofillPopupDelegate> delegate));
MOCK_METHOD2(UpdateAutofillPopupDataListValues,
@@ -160,7 +163,8 @@ class AutofillExternalDelegateUnitTest : public testing::Test {
std::vector<Suggestion> suggestions;
suggestions.push_back(Suggestion());
suggestions[0].frontend_id = kAutofillProfileId;
- external_delegate_->OnSuggestionsReturned(kQueryId, suggestions);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, suggestions, /*autoselect_first_suggestion=*/false);
}
testing::NiceMock<MockAutofillClient> autofill_client_;
@@ -184,13 +188,14 @@ TEST_F(AutofillExternalDelegateUnitTest, TestExternalDelegateVirtualCalls) {
static_cast<int>(POPUP_ITEM_ID_AUTOFILL_OPTIONS));
EXPECT_CALL(
autofill_client_,
- ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), _));
+ ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), false, _));
// This should call ShowAutofillPopup.
std::vector<Suggestion> autofill_item;
autofill_item.push_back(Suggestion());
autofill_item[0].frontend_id = kAutofillProfileId;
- external_delegate_->OnSuggestionsReturned(kQueryId, autofill_item);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
EXPECT_CALL(*autofill_manager_,
FillOrPreviewForm(
@@ -221,8 +226,9 @@ TEST_F(AutofillExternalDelegateUnitTest,
#endif
static_cast<int>(POPUP_ITEM_ID_AUTOFILL_OPTIONS));
- EXPECT_CALL(autofill_client_,
- ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), _));
+ EXPECT_CALL(
+ autofill_client_,
+ ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), false, _));
base::UserActionTester user_action_tester;
@@ -230,7 +236,8 @@ TEST_F(AutofillExternalDelegateUnitTest,
std::vector<Suggestion> autofill_item;
autofill_item.push_back(Suggestion());
autofill_item[0].frontend_id = kAutofillProfileId;
- external_delegate_->OnSuggestionsReturned(kQueryId, autofill_item);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
EXPECT_EQ(0, user_action_tester.GetActionCount(
"Signin_Impression_FromAutofillDropdown"));
@@ -258,14 +265,16 @@ TEST_F(AutofillExternalDelegateUnitTest,
auto element_ids = testing::ElementsAre(
static_cast<int>(POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO));
- EXPECT_CALL(autofill_client_,
- ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), _));
+ EXPECT_CALL(
+ autofill_client_,
+ ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), false, _));
base::UserActionTester user_action_tester;
// This should call ShowAutofillPopup.
std::vector<Suggestion> items;
- external_delegate_->OnSuggestionsReturned(kQueryId, items);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, items, /*autoselect_first_suggestion=*/false);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Signin_Impression_FromAutofillDropdown"));
@@ -306,36 +315,35 @@ TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateDataList) {
static_cast<int>(POPUP_ITEM_ID_AUTOFILL_OPTIONS));
EXPECT_CALL(
autofill_client_,
- ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), _));
+ ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), false, _));
// This should call ShowAutofillPopup.
std::vector<Suggestion> autofill_item;
autofill_item.push_back(Suggestion());
autofill_item[0].frontend_id = kAutofillProfileId;
- external_delegate_->OnSuggestionsReturned(kQueryId, autofill_item);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
// Try calling OnSuggestionsReturned with no Autofill values and ensure
// the datalist items are still shown.
// The enum must be cast to an int to prevent compile errors on linux_rel.
EXPECT_CALL(
autofill_client_,
- ShowAutofillPopup(
- _,
- _,
- SuggestionVectorIdsAre(testing::ElementsAre(
- static_cast<int>(POPUP_ITEM_ID_DATALIST_ENTRY))),
- _));
+ ShowAutofillPopup(_, _,
+ SuggestionVectorIdsAre(testing::ElementsAre(
+ static_cast<int>(POPUP_ITEM_ID_DATALIST_ENTRY))),
+ false, _));
autofill_item.clear();
- external_delegate_->OnSuggestionsReturned(kQueryId, autofill_item);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
}
// Test that datalist values can get updated while a popup is showing.
TEST_F(AutofillExternalDelegateUnitTest, UpdateDataListWhileShowingPopup) {
IssueOnQuery(kQueryId);
- EXPECT_CALL(autofill_client_, ShowAutofillPopup(_, _, _, _))
- .Times(0);
+ EXPECT_CALL(autofill_client_, ShowAutofillPopup(_, _, _, _, _)).Times(0);
// Make sure just setting the data list values doesn't cause the popup to
// appear.
@@ -362,13 +370,14 @@ TEST_F(AutofillExternalDelegateUnitTest, UpdateDataListWhileShowingPopup) {
static_cast<int>(POPUP_ITEM_ID_AUTOFILL_OPTIONS));
EXPECT_CALL(
autofill_client_,
- ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), _));
+ ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), false, _));
// Ensure the popup is displayed.
std::vector<Suggestion> autofill_item;
autofill_item.push_back(Suggestion());
autofill_item[0].frontend_id = kAutofillProfileId;
- external_delegate_->OnSuggestionsReturned(kQueryId, autofill_item);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
// This would normally get called from ShowAutofillPopup, but it is mocked so
// we need to call OnPopupShown ourselves.
@@ -413,15 +422,17 @@ TEST_F(AutofillExternalDelegateUnitTest, DuplicateAutofillDatalistValues) {
static_cast<int>(POPUP_ITEM_ID_SEPARATOR),
#endif
static_cast<int>(POPUP_ITEM_ID_AUTOFILL_OPTIONS));
- EXPECT_CALL(autofill_client_,
- ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), _));
+ EXPECT_CALL(
+ autofill_client_,
+ ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), false, _));
// 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 = ASCIIToUTF16("Rick");
autofill_item[0].frontend_id = kAutofillProfileId;
- external_delegate_->OnSuggestionsReturned(kQueryId, autofill_item);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
}
// Test that we de-dupe autocomplete values against datalist values, keeping the
@@ -448,8 +459,9 @@ TEST_F(AutofillExternalDelegateUnitTest, DuplicateAutocompleteDatalistValues) {
static_cast<int>(POPUP_ITEM_ID_SEPARATOR),
#endif
static_cast<int>(POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY));
- EXPECT_CALL(autofill_client_,
- ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), _));
+ EXPECT_CALL(
+ autofill_client_,
+ ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), false, _));
// Have an Autocomplete item that is identical to one of the datalist entries
// and one that is distinct.
@@ -460,7 +472,8 @@ TEST_F(AutofillExternalDelegateUnitTest, DuplicateAutocompleteDatalistValues) {
autocomplete_items.push_back(Suggestion());
autocomplete_items[1].value = ASCIIToUTF16("Cain");
autocomplete_items[1].frontend_id = POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY;
- external_delegate_->OnSuggestionsReturned(kQueryId, autocomplete_items);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autocomplete_items, /*autoselect_first_suggestion=*/false);
}
// Test that the Autofill popup is able to display warnings explaining why
@@ -473,16 +486,18 @@ TEST_F(AutofillExternalDelegateUnitTest, AutofillWarnings) {
EXPECT_CALL(
autofill_client_,
ShowAutofillPopup(
- _, _, SuggestionVectorIdsAre(testing::ElementsAre(static_cast<int>(
- POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE))),
- _));
+ _, _,
+ SuggestionVectorIdsAre(testing::ElementsAre(static_cast<int>(
+ POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE))),
+ false, _));
// This should call ShowAutofillPopup.
std::vector<Suggestion> autofill_item;
autofill_item.push_back(Suggestion());
autofill_item[0].frontend_id =
POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE;
- external_delegate_->OnSuggestionsReturned(kQueryId, autofill_item);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
}
// Test that Autofill warnings are removed if there are also autocomplete
@@ -492,12 +507,12 @@ TEST_F(AutofillExternalDelegateUnitTest,
IssueOnQuery(kQueryId);
// The enums must be cast to ints to prevent compile errors on linux_rel.
- EXPECT_CALL(
- autofill_client_,
- ShowAutofillPopup(
- _, _, SuggestionVectorIdsAre(testing::ElementsAre(
- static_cast<int>(POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY))),
- _));
+ EXPECT_CALL(autofill_client_,
+ ShowAutofillPopup(
+ _, _,
+ SuggestionVectorIdsAre(testing::ElementsAre(
+ static_cast<int>(POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY))),
+ false, _));
// This should call ShowAutofillPopup.
std::vector<Suggestion> suggestions;
@@ -507,7 +522,8 @@ TEST_F(AutofillExternalDelegateUnitTest,
suggestions.push_back(Suggestion());
suggestions[1].value = ASCIIToUTF16("Rick");
suggestions[1].frontend_id = POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY;
- external_delegate_->OnSuggestionsReturned(kQueryId, suggestions);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, suggestions, /*autoselect_first_suggestion=*/false);
}
// Test that the Autofill delegate doesn't try and fill a form with a
@@ -550,7 +566,7 @@ TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateClearPreviewedForm) {
// Test that the popup is hidden once we are done editing the autofill field.
TEST_F(AutofillExternalDelegateUnitTest,
ExternalDelegateHidePopupAfterEditing) {
- EXPECT_CALL(autofill_client_, ShowAutofillPopup(_, _, _, _));
+ EXPECT_CALL(autofill_client_, ShowAutofillPopup(_, _, _, _, _));
test::GenerateTestAutofillPopup(external_delegate_.get());
EXPECT_CALL(autofill_client_, HideAutofillPopup());
@@ -616,7 +632,7 @@ TEST_F(AutofillExternalDelegateUnitTest, ScanCreditCardPromptMetricsTest) {
base::HistogramTester histogram;
IssueOnQuery(kQueryId);
IssueOnSuggestionsReturned();
- external_delegate_->OnPopupHidden();
+ external_delegate_->OnPopupShown();
histogram.ExpectUniqueSample("Autofill.ScanCreditCardPrompt",
AutofillMetrics::SCAN_CARD_ITEM_SHOWN, 1);
}
@@ -627,6 +643,7 @@ TEST_F(AutofillExternalDelegateUnitTest, ScanCreditCardPromptMetricsTest) {
base::HistogramTester histogram;
IssueOnQuery(kQueryId);
IssueOnSuggestionsReturned();
+ external_delegate_->OnPopupShown();
external_delegate_->DidAcceptSuggestion(base::string16(),
POPUP_ITEM_ID_SCAN_CREDIT_CARD,
0);
@@ -645,6 +662,7 @@ TEST_F(AutofillExternalDelegateUnitTest, ScanCreditCardPromptMetricsTest) {
base::HistogramTester histogram;
IssueOnQuery(kQueryId);
IssueOnSuggestionsReturned();
+ external_delegate_->OnPopupShown();
external_delegate_->DidAcceptSuggestion(base::string16(),
POPUP_ITEM_ID_CLEAR_FORM,
0);
@@ -663,9 +681,7 @@ TEST_F(AutofillExternalDelegateUnitTest, ScanCreditCardPromptMetricsTest) {
base::HistogramTester histogram;
IssueOnQuery(kQueryId);
IssueOnSuggestionsReturned();
- external_delegate_->DidAcceptSuggestion(base::string16(),
- POPUP_ITEM_ID_CLEAR_FORM,
- 0);
+ external_delegate_->OnPopupShown();
histogram.ExpectTotalCount("Autofill.ScanCreditCardPrompt", 0);
}
}
@@ -707,10 +723,11 @@ TEST_F(AutofillExternalDelegateUnitTest, IgnoreAutocompleteOffForAutofill) {
autofill_items[0].frontend_id = POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY;
// Ensure the popup tries to show itself, despite autocomplete="off".
- EXPECT_CALL(autofill_client_, ShowAutofillPopup(_, _, _, _));
+ EXPECT_CALL(autofill_client_, ShowAutofillPopup(_, _, _, _, _));
EXPECT_CALL(autofill_client_, HideAutofillPopup()).Times(0);
- external_delegate_->OnSuggestionsReturned(kQueryId, autofill_items);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autofill_items, /*autoselect_first_suggestion=*/false);
}
TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateFillFieldWithValue) {
@@ -734,19 +751,69 @@ TEST_F(AutofillExternalDelegateUnitTest, ShouldShowGooglePayIcon) {
base::string16(),
#endif
base::string16(), base::ASCIIToUTF16("googlePay"));
- EXPECT_CALL(
- autofill_client_,
- ShowAutofillPopup(_, _, SuggestionVectorIconsAre(element_icons), _));
+ EXPECT_CALL(autofill_client_,
+ ShowAutofillPopup(_, _, SuggestionVectorIconsAre(element_icons),
+ false, _));
+
+ std::vector<Suggestion> autofill_item;
+ autofill_item.push_back(Suggestion());
+ autofill_item[0].frontend_id = kAutofillProfileId;
+
+ // This should call ShowAutofillPopup.
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autofill_item, /*autoselect_first_suggestion=*/false, true);
+}
+
+#if defined(OS_IOS)
+TEST_F(AutofillExternalDelegateUnitTest, ShouldShowGooglePayIconOniOS) {
+ // Turn on feature flag.
+ base::test::ScopedFeatureList scoped_feature_list_;
+ scoped_feature_list_.InitAndEnableFeature(
+ kAutofillDownstreamUseGooglePayBrandingOniOS);
+ IssueOnQuery(kQueryId);
+
+ auto element_icons =
+ testing::ElementsAre(base::ASCIIToUTF16("googlePay"), base::string16(),
+ base::string16(), base::ASCIIToUTF16("googlePay"));
+ EXPECT_CALL(autofill_client_,
+ ShowAutofillPopup(_, _, SuggestionVectorIconsAre(element_icons),
+ false, _));
std::vector<Suggestion> autofill_item;
autofill_item.push_back(Suggestion());
autofill_item[0].frontend_id = kAutofillProfileId;
// This should call ShowAutofillPopup.
- external_delegate_->OnSuggestionsReturned(kQueryId, autofill_item, true);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autofill_item, /*autoselect_first_suggestion=*/false, true);
}
TEST_F(AutofillExternalDelegateUnitTest,
+ ShouldNotShowGooglePayIconOniOSIfExperimentOff) {
+ // Turn on feature flag.
+ base::test::ScopedFeatureList scoped_feature_list_;
+ scoped_feature_list_.InitAndDisableFeature(
+ kAutofillDownstreamUseGooglePayBrandingOniOS);
+ IssueOnQuery(kQueryId);
+
+ auto element_icons = testing::ElementsAre(
+ base::string16(), base::string16(),
+ base::string16() /* Autofill setting item does not have icon. */);
+ EXPECT_CALL(autofill_client_,
+ ShowAutofillPopup(_, _, SuggestionVectorIconsAre(element_icons),
+ false, _));
+
+ std::vector<Suggestion> autofill_item;
+ autofill_item.push_back(Suggestion());
+ autofill_item[0].frontend_id = kAutofillProfileId;
+
+ // This should call ShowAutofillPopup.
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autofill_item, /*autoselect_first_suggestion=*/false, false);
+}
+#endif // defined(OS_IOS)
+
+TEST_F(AutofillExternalDelegateUnitTest,
ShouldNotShowGooglePayIconIfSuggestionsContainLocalCards) {
IssueOnQuery(kQueryId);
@@ -756,16 +823,17 @@ TEST_F(AutofillExternalDelegateUnitTest,
#endif
base::string16(),
base::string16() /* Autofill setting item does not have icon. */);
- EXPECT_CALL(
- autofill_client_,
- ShowAutofillPopup(_, _, SuggestionVectorIconsAre(element_icons), _));
+ EXPECT_CALL(autofill_client_,
+ ShowAutofillPopup(_, _, SuggestionVectorIconsAre(element_icons),
+ false, _));
std::vector<Suggestion> autofill_item;
autofill_item.push_back(Suggestion());
autofill_item[0].frontend_id = kAutofillProfileId;
// This should call ShowAutofillPopup.
- external_delegate_->OnSuggestionsReturned(kQueryId, autofill_item, false);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autofill_item, /*autoselect_first_suggestion=*/false, false);
}
TEST_F(AutofillExternalDelegateUnitTest, ShouldUseNewSettingName) {
@@ -775,17 +843,18 @@ TEST_F(AutofillExternalDelegateUnitTest, ShouldUseNewSettingName) {
#if !defined(OS_ANDROID)
base::string16(),
#endif
- base::string16(), l10n_util::GetStringUTF16(IDS_AUTOFILL_SETTINGS_POPUP));
- EXPECT_CALL(
- autofill_client_,
- ShowAutofillPopup(_, _, SuggestionVectorValuesAre(element_values), _));
+ base::string16(), l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE));
+ EXPECT_CALL(autofill_client_,
+ ShowAutofillPopup(_, _, SuggestionVectorValuesAre(element_values),
+ false, _));
std::vector<Suggestion> autofill_item;
autofill_item.push_back(Suggestion());
autofill_item[0].frontend_id = kAutofillProfileId;
// This should call ShowAutofillPopup.
- external_delegate_->OnSuggestionsReturned(kQueryId, autofill_item);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
}
#if !defined(OS_ANDROID)
@@ -810,12 +879,13 @@ TEST_F(AutofillExternalDelegateUnitTest, IncludeFooterSeparatorForOldUIOnly) {
EXPECT_CALL(
autofill_client_,
- ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), _));
+ ShowAutofillPopup(_, _, SuggestionVectorIdsAre(element_ids), false, _));
std::vector<Suggestion> autofill_item;
autofill_item.push_back(Suggestion());
autofill_item[0].frontend_id = kAutofillProfileId;
- external_delegate_->OnSuggestionsReturned(kQueryId, autofill_item);
+ external_delegate_->OnSuggestionsReturned(
+ kQueryId, autofill_item, /*autoselect_first_suggestion=*/false);
};
tester(false,
diff --git a/chromium/components/autofill/core/browser/autofill_field.cc b/chromium/components/autofill/core/browser/autofill_field.cc
index b1c931f73f9..0d427d70b72 100644
--- a/chromium/components/autofill/core/browser/autofill_field.cc
+++ b/chromium/components/autofill/core/browser/autofill_field.cc
@@ -6,6 +6,8 @@
#include <stdint.h>
+#include <utility>
+
#include "base/feature_list.h"
#include "base/strings/string_number_conversions.h"
#include "components/autofill/core/browser/autofill_experiments.h"
@@ -162,6 +164,10 @@ bool AutofillField::IsFieldFillable() const {
return !Type().IsUnknown();
}
+void AutofillField::SetPasswordRequirements(PasswordRequirementsSpec spec) {
+ password_requirements_ = std::move(spec);
+}
+
bool AutofillField::IsCreditCardPrediction() const {
return AutofillType(server_type_).group() == CREDIT_CARD ||
AutofillType(heuristic_type_).group() == CREDIT_CARD;
diff --git a/chromium/components/autofill/core/browser/autofill_field.h b/chromium/components/autofill/core/browser/autofill_field.h
index 5b40d57b1b3..7b616def23c 100644
--- a/chromium/components/autofill/core/browser/autofill_field.h
+++ b/chromium/components/autofill/core/browser/autofill_field.h
@@ -10,9 +10,11 @@
#include <string>
#include "base/macros.h"
+#include "base/optional.h"
#include "base/strings/string16.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/proto/password_requirements.pb.h"
#include "components/autofill/core/browser/proto/server.pb.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/signatures_util.h"
@@ -147,6 +149,12 @@ class AutofillField : public FormFieldData {
return vote_type_;
}
+ void SetPasswordRequirements(PasswordRequirementsSpec spec);
+ const base::Optional<PasswordRequirementsSpec>& password_requirements()
+ const {
+ return password_requirements_;
+ }
+
private:
// Whether the heuristics or server predict a credit card field.
bool IsCreditCardPrediction() const;
@@ -162,6 +170,10 @@ class AutofillField : public FormFieldData {
std::vector<AutofillQueryResponseContents::Field::FieldPrediction>
server_predictions_;
+ // Requirements the site imposes to passwords (for password generation).
+ // Corresponds to the requirements determined by the Autofill server.
+ base::Optional<PasswordRequirementsSpec> password_requirements_;
+
// The type of the field, as determined by the local heuristics.
ServerFieldType heuristic_type_;
diff --git a/chromium/components/autofill/core/browser/autofill_handler.cc b/chromium/components/autofill/core/browser/autofill_handler.cc
index 89a5c7a5970..04c50fa0d53 100644
--- a/chromium/components/autofill/core/browser/autofill_handler.cc
+++ b/chromium/components/autofill/core/browser/autofill_handler.cc
@@ -4,11 +4,22 @@
#include "components/autofill/core/browser/autofill_handler.h"
+#include "base/containers/adapters.h"
+#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/common/autofill_data_validation.h"
+#include "components/autofill/core/common/signatures_util.h"
#include "ui/gfx/geometry/rect_f.h"
namespace autofill {
+namespace {
+
+// Set a conservative upper bound on the number of forms we are willing to
+// cache, simply to prevent unbounded memory consumption.
+const size_t kMaxFormCacheSize = 100;
+
+} // namespace
+
using base::TimeTicks;
AutofillHandler::AutofillHandler(AutofillDriver* driver) : driver_(driver) {}
@@ -23,6 +34,36 @@ void AutofillHandler::OnFormSubmitted(const FormData& form,
OnFormSubmittedImpl(form, known_success, source, timestamp);
}
+void AutofillHandler::OnFormsSeen(const std::vector<FormData>& forms,
+ const base::TimeTicks timestamp) {
+ if (!IsValidFormDataVector(forms) || !driver_->RendererIsAvailable())
+ return;
+
+ // This should be called even forms is empty, AutofillProviderAndroid uses
+ // this event to detect form submission.
+ if (!ShouldParseForms(forms, timestamp))
+ return;
+
+ if (forms.empty())
+ return;
+
+ std::vector<FormStructure*> form_structures;
+ for (const FormData& form : forms) {
+ const auto parse_form_start_time = TimeTicks::Now();
+ FormStructure* form_structure = nullptr;
+ if (!ParseForm(form, /*cached_form=*/nullptr, &form_structure))
+ continue;
+ DCHECK(form_structure);
+ if (form_structure == nullptr)
+ continue;
+ form_structures.push_back(form_structure);
+ AutofillMetrics::LogParseFormTiming(TimeTicks::Now() -
+ parse_form_start_time);
+ }
+ if (!form_structures.empty())
+ OnFormsParsed(form_structures, timestamp);
+}
+
void AutofillHandler::OnTextFieldDidChange(const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
@@ -60,17 +101,20 @@ void AutofillHandler::OnSelectControlDidChange(const FormData& form,
OnSelectControlDidChangeImpl(form, field, transformed_box);
}
-void AutofillHandler::OnQueryFormFieldAutofill(int query_id,
- const FormData& form,
- const FormFieldData& field,
- const gfx::RectF& bounding_box) {
+void AutofillHandler::OnQueryFormFieldAutofill(
+ int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion) {
if (!IsValidFormData(form) || !IsValidFormFieldData(field))
return;
gfx::RectF transformed_box =
driver_->TransformBoundingBoxToViewportCoordinates(bounding_box);
- OnQueryFormFieldAutofillImpl(query_id, form, field, transformed_box);
+ OnQueryFormFieldAutofillImpl(query_id, form, field, transformed_box,
+ autoselect_first_suggestion);
}
void AutofillHandler::OnFocusOnFormField(const FormData& form,
@@ -92,4 +136,67 @@ void AutofillHandler::SendFormDataToRenderer(
driver_->SendFormDataToRenderer(query_id, action, data);
}
+bool AutofillHandler::FindCachedForm(const FormData& form,
+ FormStructure** form_structure) const {
+ // Find the FormStructure that corresponds to |form|.
+ // Scan backward through the cached |form_structures_|, as updated versions of
+ // forms are added to the back of the list, whereas original versions of these
+ // forms might appear toward the beginning of the list. The communication
+ // protocol with the crowdsourcing server does not permit us to discard the
+ // original versions of the forms.
+ *form_structure = nullptr;
+ const auto& form_signature = autofill::CalculateFormSignature(form);
+ for (auto& cur_form : base::Reversed(form_structures_)) {
+ if (cur_form->form_signature() == form_signature || *cur_form == form) {
+ *form_structure = cur_form.get();
+
+ // The same form might be cached with multiple field counts: in some
+ // cases, non-autofillable fields are filtered out, whereas in other cases
+ // they are not. To avoid thrashing the cache, keep scanning until we
+ // find a cached version with the same number of fields, if there is one.
+ if (cur_form->field_count() == form.fields.size())
+ break;
+ }
+ }
+
+ if (!(*form_structure))
+ return false;
+
+ return true;
+}
+
+bool AutofillHandler::ParseForm(const FormData& form,
+ const FormStructure* cached_form,
+ FormStructure** parsed_form_structure) {
+ DCHECK(parsed_form_structure);
+ if (form_structures_.size() >= kMaxFormCacheSize)
+ return false;
+
+ auto form_structure = std::make_unique<FormStructure>(form);
+ form_structure->ParseFieldTypesFromAutocompleteAttributes();
+ if (!form_structure->ShouldBeParsed()) {
+ return false;
+ }
+
+ if (cached_form) {
+ // We need to keep the server data if available. We need to use them while
+ // determining the heuristics.
+ form_structure->RetrieveFromCache(*cached_form,
+ /*apply_is_autofilled=*/true,
+ /*only_server_and_autofill_state=*/true);
+ }
+
+ // Ownership is transferred to |form_structures_| which maintains it until
+ // the manager is Reset() or destroyed. It is safe to use references below
+ // as long as receivers don't take ownership.
+ form_structure->set_form_parsed_timestamp(TimeTicks::Now());
+ form_structures_.push_back(std::move(form_structure));
+ *parsed_form_structure = form_structures_.back().get();
+ return true;
+}
+
+void AutofillHandler::Reset() {
+ form_structures_.clear();
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_handler.h b/chromium/components/autofill/core/browser/autofill_handler.h
index b6592b1cfd7..74e76a5fa77 100644
--- a/chromium/components/autofill/core/browser/autofill_handler.h
+++ b/chromium/components/autofill/core/browser/autofill_handler.h
@@ -23,6 +23,7 @@ namespace autofill {
struct FormData;
struct FormFieldData;
+class FormStructure;
// This class defines the interface should be implemented by autofill
// implementation in browser side to interact with AutofillDriver.
@@ -56,7 +57,8 @@ class AutofillHandler {
void OnQueryFormFieldAutofill(int query_id,
const FormData& form,
const FormFieldData& field,
- const gfx::RectF& bounding_box);
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion);
// Invoked when |form|'s |field| has focus.
void OnFocusOnFormField(const FormData& form,
@@ -71,6 +73,10 @@ class AutofillHandler {
SubmissionSource source,
base::TimeTicks timestamp);
+ // Invoked when |forms| has been detected.
+ void OnFormsSeen(const std::vector<FormData>& forms,
+ const base::TimeTicks timestamp);
+
// Invoked when focus is no longer on form.
virtual void OnFocusNoLongerOnForm() = 0;
@@ -82,10 +88,6 @@ class AutofillHandler {
// Invoked when preview autofill value has been shown.
virtual void OnDidPreviewAutofillFormData() = 0;
- // Invoked when |forms| has been detected.
- virtual void OnFormsSeen(const std::vector<FormData>& forms,
- const base::TimeTicks timestamp) = 0;
-
// Invoked when textfeild editing ended
virtual void OnDidEndTextFieldEditing() = 0;
@@ -100,13 +102,21 @@ class AutofillHandler {
virtual void SelectFieldOptionsDidChange(const FormData& form) = 0;
// Resets cache.
- virtual void Reset() = 0;
+ virtual void Reset();
// Send the form |data| to renderer for the specified |action|.
void SendFormDataToRenderer(int query_id,
AutofillDriver::RendererFormDataAction action,
const FormData& data);
+ // Returns the number of forms this Autofill handler is aware of.
+ size_t NumFormsDetected() const { return form_structures_.size(); }
+
+ // Returns the present form structures seen by Autofill handler.
+ const std::vector<std::unique_ptr<FormStructure>>& form_structures() {
+ return form_structures_;
+ }
+
protected:
AutofillHandler(AutofillDriver* driver);
@@ -124,10 +134,12 @@ class AutofillHandler {
const FormFieldData& field,
const gfx::RectF& bounding_box) = 0;
- virtual void OnQueryFormFieldAutofillImpl(int query_id,
- const FormData& form,
- const FormFieldData& field,
- const gfx::RectF& bounding_box) = 0;
+ virtual void OnQueryFormFieldAutofillImpl(
+ int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion) = 0;
virtual void OnFocusOnFormFieldImpl(const FormData& form,
const FormFieldData& field,
@@ -137,9 +149,38 @@ class AutofillHandler {
const FormFieldData& field,
const gfx::RectF& bounding_box) = 0;
+ // Return whether the |forms| from OnFormSeen() should be parsed to
+ // form_structures.
+ virtual bool ShouldParseForms(const std::vector<FormData>& forms,
+ const base::TimeTicks timestamp) = 0;
+
+ // Invoked when forms from OnFormsSeen() has been parsed to |form_structures|.
+ virtual void OnFormsParsed(const std::vector<FormStructure*>& form_structures,
+ const base::TimeTicks timestamp) = 0;
+
AutofillDriver* driver() { return driver_; }
+ // Fills |form_structure| cached element corresponding to |form|.
+ // Returns false if the cached element was not found.
+ bool FindCachedForm(const FormData& form,
+ FormStructure** form_structure) const WARN_UNUSED_RESULT;
+
+ std::vector<std::unique_ptr<FormStructure>>* mutable_form_structures() {
+ return &form_structures_;
+ }
+
+ // Parses the |form| with the server data retrieved from the |cached_form|
+ // (if any), and writes it to the |parse_form_structure|. Adds the
+ // |parse_form_structure| to the |form_structures_|. Returns true if the form
+ // is parsed.
+ bool ParseForm(const FormData& form,
+ const FormStructure* cached_form,
+ FormStructure** parsed_form_structure);
+
private:
+ // Our copy of the form data.
+ std::vector<std::unique_ptr<FormStructure>> form_structures_;
+
// Provides driver-level context to the shared code of the component. Must
// outlive this object.
AutofillDriver* driver_;
diff --git a/chromium/components/autofill/core/browser/autofill_handler_proxy.cc b/chromium/components/autofill/core/browser/autofill_handler_proxy.cc
index eee6adc468d..1f6cc73f3ac 100644
--- a/chromium/components/autofill/core/browser/autofill_handler_proxy.cc
+++ b/chromium/components/autofill/core/browser/autofill_handler_proxy.cc
@@ -42,9 +42,10 @@ void AutofillHandlerProxy::OnQueryFormFieldAutofillImpl(
int query_id,
const FormData& form,
const FormFieldData& field,
- const gfx::RectF& bounding_box) {
- provider_->OnQueryFormFieldAutofill(this, query_id, form, field,
- bounding_box);
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion) {
+ provider_->OnQueryFormFieldAutofill(this, query_id, form, field, bounding_box,
+ autoselect_first_suggestion);
}
void AutofillHandlerProxy::OnFocusOnFormFieldImpl(
@@ -61,6 +62,17 @@ void AutofillHandlerProxy::OnSelectControlDidChangeImpl(
provider_->OnSelectControlDidChange(this, form, field, bounding_box);
}
+bool AutofillHandlerProxy::ShouldParseForms(const std::vector<FormData>& forms,
+ const base::TimeTicks timestamp) {
+ provider_->OnFormsSeen(this, forms, timestamp);
+ // Don't use form_structure.
+ return false;
+}
+
+void AutofillHandlerProxy::OnFormsParsed(
+ const std::vector<FormStructure*>& form_structures,
+ const base::TimeTicks timestamp) {}
+
void AutofillHandlerProxy::OnFocusNoLongerOnForm() {
provider_->OnFocusNoLongerOnForm(this);
}
@@ -73,11 +85,6 @@ void AutofillHandlerProxy::OnDidFillAutofillFormData(
void AutofillHandlerProxy::OnDidPreviewAutofillFormData() {}
-void AutofillHandlerProxy::OnFormsSeen(const std::vector<FormData>& forms,
- const base::TimeTicks timestamp) {
- provider_->OnFormsSeen(this, forms, timestamp);
-}
-
void AutofillHandlerProxy::OnDidEndTextFieldEditing() {}
void AutofillHandlerProxy::OnHidePopup() {}
diff --git a/chromium/components/autofill/core/browser/autofill_handler_proxy.h b/chromium/components/autofill/core/browser/autofill_handler_proxy.h
index 34af7760e1e..364643a9aae 100644
--- a/chromium/components/autofill/core/browser/autofill_handler_proxy.h
+++ b/chromium/components/autofill/core/browser/autofill_handler_proxy.h
@@ -24,10 +24,6 @@ class AutofillHandlerProxy : public AutofillHandler {
const base::TimeTicks timestamp) override;
void OnDidPreviewAutofillFormData() override;
-
- void OnFormsSeen(const std::vector<FormData>& forms,
- const base::TimeTicks timestamp) override;
-
void OnDidEndTextFieldEditing() override;
void OnHidePopup() override;
void OnSetDataList(const std::vector<base::string16>& values,
@@ -58,7 +54,8 @@ class AutofillHandlerProxy : public AutofillHandler {
void OnQueryFormFieldAutofillImpl(int query_id,
const FormData& form,
const FormFieldData& field,
- const gfx::RectF& bounding_box) override;
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion) override;
void OnFocusOnFormFieldImpl(const FormData& form,
const FormFieldData& field,
@@ -68,6 +65,12 @@ class AutofillHandlerProxy : public AutofillHandler {
const FormFieldData& field,
const gfx::RectF& bounding_box) override;
+ bool ShouldParseForms(const std::vector<FormData>& forms,
+ const base::TimeTicks timestamp) override;
+
+ void OnFormsParsed(const std::vector<FormStructure*>& form_structures,
+ const base::TimeTicks timestamp) override;
+
private:
AutofillProvider* provider_;
base::WeakPtrFactory<AutofillHandlerProxy> weak_ptr_factory_;
diff --git a/chromium/components/autofill/core/browser/autofill_manager.cc b/chromium/components/autofill/core/browser/autofill_manager.cc
index 2a6fc3470e2..34026869008 100644
--- a/chromium/components/autofill/core/browser/autofill_manager.cc
+++ b/chromium/components/autofill/core/browser/autofill_manager.cc
@@ -65,11 +65,11 @@
#include "components/autofill/core/common/form_data_predictions.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/password_form_fill_data.h"
-#include "components/autofill/core/common/signatures_util.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/security_state/core/security_state.h"
#include "components/strings/grit/components_strings.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/geometry/rect.h"
#include "url/gurl.h"
@@ -89,10 +89,6 @@ namespace {
const size_t kMaxRecentFormSignaturesToRemember = 3;
-// Set a conservative upper bound on the number of forms we are willing to
-// cache, simply to prevent unbounded memory consumption.
-const size_t kMaxFormCacheSize = 100;
-
// Time to wait, in ms, after a dynamic form change before triggering a refill.
// This is used for sites that change multiple things consecutively.
const size_t kWaitTimeForDynamicFormsMs = 200;
@@ -100,23 +96,6 @@ const size_t kWaitTimeForDynamicFormsMs = 200;
// The time limit, in ms, between a fill and when a refill can happen.
const int kLimitBeforeRefillMs = 1000;
-// Precondition: |form_structure| and |form| should correspond to the same
-// logical form. Returns true if any field in the given |section| within |form|
-// is auto-filled.
-bool SectionHasAutofilledField(const FormStructure& form_structure,
- const FormData& form,
- const std::string& section) {
- DCHECK_EQ(form_structure.field_count(), form.fields.size());
- for (size_t i = 0; i < form_structure.field_count(); ++i) {
- if (form_structure.field(i)->section == section &&
- form.fields[i].is_autofilled) {
- return true;
- }
- }
-
- return false;
-}
-
// Returns the credit card field |value| trimmed from whitespace and with stop
// characters removed.
base::string16 SanitizeCreditCardFieldValue(const base::string16& value) {
@@ -212,6 +191,9 @@ void AutofillManager::RegisterProfilePrefs(
registry->RegisterBooleanPref(
prefs::kAutofillEnabled, true,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+ registry->RegisterBooleanPref(
+ prefs::kAutofillProfileEnabled, true,
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
registry->RegisterIntegerPref(
prefs::kAutofillLastVersionDeduped, 0,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
@@ -333,28 +315,15 @@ bool AutofillManager::ShouldShowCreditCardSigninPromo(
return false;
}
-void AutofillManager::OnFormsSeen(const std::vector<FormData>& forms,
- const TimeTicks timestamp) {
- if (!IsValidFormDataVector(forms))
- return;
-
- if (!driver()->RendererIsAvailable())
- return;
-
+bool AutofillManager::ShouldParseForms(const std::vector<FormData>& forms,
+ const base::TimeTicks timestamp) {
bool enabled = IsAutofillEnabled();
if (!has_logged_autofill_enabled_) {
AutofillMetrics::LogIsAutofillEnabledAtPageLoad(enabled);
has_logged_autofill_enabled_ = true;
}
- if (!enabled)
- return;
-
- for (const FormData& form : forms) {
- forms_loaded_timestamps_[form] = timestamp;
- }
-
- ParseForms(forms);
+ return enabled;
}
void AutofillManager::OnFormSubmittedImpl(const FormData& form,
@@ -383,7 +352,8 @@ void AutofillManager::OnFormSubmittedImpl(const FormData& form,
}
autocomplete_history_manager_->OnWillSubmitForm(form_for_autocomplete);
- address_form_event_logger_->OnWillSubmitForm();
+ if (IsProfileAutofillEnabled())
+ address_form_event_logger_->OnWillSubmitForm();
if (IsCreditCardAutofillEnabled())
credit_card_form_event_logger_->OnWillSubmitForm();
@@ -402,8 +372,9 @@ void AutofillManager::OnFormSubmittedImpl(const FormData& form,
AutofillMetrics::CardNumberStatus card_number_status =
GetCardNumberStatus(credit_card);
- address_form_event_logger_->OnFormSubmitted(/*force_logging=*/false,
- card_number_status);
+ if (IsProfileAutofillEnabled())
+ address_form_event_logger_->OnFormSubmitted(/*force_logging=*/false,
+ card_number_status);
if (IsCreditCardAutofillEnabled())
credit_card_form_event_logger_->OnFormSubmitted(enable_ablation_logging_,
card_number_status);
@@ -414,6 +385,7 @@ void AutofillManager::OnFormSubmittedImpl(const FormData& form,
// Update Personal Data with the form's submitted data.
// Also triggers offering local/upload credit card save, if applicable.
form_data_importer_->ImportFormData(*submitted_form,
+ IsProfileAutofillEnabled(),
IsCreditCardAutofillEnabled());
}
@@ -506,13 +478,14 @@ void AutofillManager::OnTextFieldDidChangeImpl(const FormData& form,
UpdatePendingForm(form);
if (!user_did_type_ || autofill_field->is_autofilled)
- form_interactions_ukm_logger_->LogTextFieldDidChange(
- *autofill_field, form_structure->form_parsed_timestamp());
+ form_interactions_ukm_logger_->LogTextFieldDidChange(*form_structure,
+ *autofill_field);
if (!user_did_type_) {
user_did_type_ = true;
- AutofillMetrics::LogUserHappinessMetric(AutofillMetrics::USER_DID_TYPE,
- autofill_field->Type().group());
+ AutofillMetrics::LogUserHappinessMetric(
+ AutofillMetrics::USER_DID_TYPE, autofill_field->Type().group(),
+ client_->GetSecurityLevelForUmaHistograms());
}
if (autofill_field->is_autofilled) {
@@ -520,13 +493,15 @@ void AutofillManager::OnTextFieldDidChangeImpl(const FormData& form,
autofill_field->set_previously_autofilled(true);
AutofillMetrics::LogUserHappinessMetric(
AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD,
- autofill_field->Type().group());
+ autofill_field->Type().group(),
+ client_->GetSecurityLevelForUmaHistograms());
if (!user_did_edit_autofilled_field_) {
user_did_edit_autofilled_field_ = true;
AutofillMetrics::LogUserHappinessMetric(
AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD_ONCE,
- autofill_field->Type().group());
+ autofill_field->Type().group(),
+ client_->GetSecurityLevelForUmaHistograms());
}
}
@@ -542,123 +517,43 @@ void AutofillManager::OnQueryFormFieldAutofillImpl(
int query_id,
const FormData& form,
const FormFieldData& field,
- const gfx::RectF& transformed_box) {
+ const gfx::RectF& transformed_box,
+ bool autoselect_first_suggestion) {
external_delegate_->OnQuery(query_id, form, field, transformed_box);
- // Need to refresh models before using the form_event_loggers.
- bool is_autofill_possible = RefreshDataModels();
-
- FormStructure* form_structure = nullptr;
- AutofillField* autofill_field = nullptr;
- bool got_autofillable_form =
- GetCachedFormAndField(form, field, &form_structure, &autofill_field) &&
- // Don't send suggestions or track forms that should not be parsed.
- form_structure->ShouldBeParsed();
-
- bool is_filling_credit_card = false;
-
- // Flag to indicate whether all suggestions come from Google Payments.
- bool is_all_server_suggestions = false;
-
- // Log interactions of forms that are autofillable.
- if (got_autofillable_form) {
- if (autofill_field->Type().group() == CREDIT_CARD) {
- is_filling_credit_card = true;
- driver()->DidInteractWithCreditCardForm();
- credit_card_form_event_logger_->OnDidInteractWithAutofillableForm(
- form_structure->form_signature());
- } else {
- address_form_event_logger_->OnDidInteractWithAutofillableForm(
- form_structure->form_signature());
- }
- }
-
std::vector<Suggestion> suggestions;
- const bool is_context_secure =
- !IsFormNonSecure(form) ||
- !base::FeatureList::IsEnabled(
- features::kAutofillRequireSecureCreditCardContext);
+ SuggestionsContext context;
+ GetAvailableSuggestions(form, field, &suggestions, &context);
- // TODO(rogerm): Early exit here on !driver()->RendererIsAvailable()?
- // We skip populating autofill data, but might generate warnings and or
- // signin promo to show over the unavailable renderer. That seems a mistake.
+ if (context.is_autofill_available) {
+ switch (context.suppress_reason) {
+ case SuppressReason::kNotSuppressed:
+ break;
- if (is_autofill_possible && driver()->RendererIsAvailable() &&
- got_autofillable_form) {
- // On desktop, don't return non credit card related suggestions for forms or
- // fields that have the "autocomplete" attribute set to off, only if the
- // feature to always fill addresses is off.
- if (!base::FeatureList::IsEnabled(kAutofillAlwaysFillAddresses) &&
- IsDesktopPlatform() && !is_filling_credit_card &&
- !field.should_autocomplete) {
- return;
- }
+ case SuppressReason::kCreditCardsAblation:
+ enable_ablation_logging_ = true;
+ autocomplete_history_manager_->CancelPendingQuery();
+ external_delegate_->OnSuggestionsReturned(query_id, suggestions,
+ autoselect_first_suggestion);
+ return;
- if (is_filling_credit_card) {
- suggestions = GetCreditCardSuggestions(field, autofill_field->Type(),
- &is_all_server_suggestions);
- } else {
- suggestions =
- GetProfileSuggestions(*form_structure, field, *autofill_field);
- }
-
- // Logic for disabling/ablating credit card autofill.
- if (base::FeatureList::IsEnabled(kAutofillCreditCardAblationExperiment) &&
- is_filling_credit_card && !suggestions.empty()) {
- suggestions.clear();
- autocomplete_history_manager_->CancelPendingQuery();
- external_delegate_->OnSuggestionsReturned(query_id, suggestions);
- enable_ablation_logging_ = true;
- return;
+ case SuppressReason::kAutocompleteOff:
+ return;
}
if (!suggestions.empty()) {
- if (is_filling_credit_card)
- AutofillMetrics::LogIsQueriedCreditCardFormSecure(is_context_secure);
-
- // Don't provide credit card suggestions for non-secure pages, but do
- // provide them for secure pages with passive mixed content (see impl. of
- // IsContextSecure).
- if (is_filling_credit_card && !is_context_secure) {
- // Replace the suggestion content with a warning message explaining why
- // Autofill is disabled for a website. The string is different if the
- // credit card autofill HTTP warning experiment is enabled.
- Suggestion warning_suggestion(l10n_util::GetStringUTF16(
- IDS_AUTOFILL_WARNING_INSECURE_CONNECTION));
- warning_suggestion.frontend_id =
- POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE;
- suggestions.assign(1, warning_suggestion);
- } else {
- bool section_has_autofilled_field = SectionHasAutofilledField(
- *form_structure, form, autofill_field->section);
- if (section_has_autofilled_field) {
- // If the relevant section has auto-filled fields and the renderer is
- // querying for suggestions, then for some fields, the user is editing
- // the value of a field. In this case, mimic autocomplete: don't
- // display labels or icons, as that information is redundant.
- // Moreover, filter out duplicate suggestions.
- std::set<base::string16> seen_values;
- for (auto iter = suggestions.begin(); iter != suggestions.end();) {
- if (!seen_values.insert(iter->value).second) {
- // If we've seen this suggestion value before, remove it.
- iter = suggestions.erase(iter);
- } else {
- iter->label.clear();
- iter->icon.clear();
- ++iter;
- }
- }
- }
-
- // The first time we show suggestions on this page, log the number of
- // suggestions available.
- // TODO(mathp): Differentiate between number of suggestions available
- // (current metric) and number shown to the user.
- if (!has_logged_address_suggestions_count_ &&
- !section_has_autofilled_field) {
- AutofillMetrics::LogAddressSuggestionsCount(suggestions.size());
- has_logged_address_suggestions_count_ = true;
- }
+ if (context.is_filling_credit_card) {
+ AutofillMetrics::LogIsQueriedCreditCardFormSecure(
+ context.is_context_secure);
+ }
+
+ // The first time we show suggestions on this page, log the number of
+ // suggestions available.
+ // TODO(mathp): Differentiate between number of suggestions available
+ // (current metric) and number shown to the user.
+ if (!has_logged_address_suggestions_count_) {
+ AutofillMetrics::LogAddressSuggestionsCount(suggestions.size());
+ has_logged_address_suggestions_count_ = true;
}
}
}
@@ -670,11 +565,13 @@ void AutofillManager::OnQueryFormFieldAutofillImpl(
// credit card expiration, cvc or number.
if (suggestions.empty() && !ShouldShowCreditCardSigninPromo(form, field) &&
field.should_autocomplete &&
- !(autofill_field &&
- (IsCreditCardExpirationType(autofill_field->Type().GetStorableType()) ||
- autofill_field->Type().html_type() == HTML_TYPE_UNRECOGNIZED ||
- autofill_field->Type().GetStorableType() == CREDIT_CARD_NUMBER ||
- autofill_field->Type().GetStorableType() ==
+ !(context.focused_field &&
+ (IsCreditCardExpirationType(
+ context.focused_field->Type().GetStorableType()) ||
+ context.focused_field->Type().html_type() == HTML_TYPE_UNRECOGNIZED ||
+ context.focused_field->Type().GetStorableType() ==
+ CREDIT_CARD_NUMBER ||
+ context.focused_field->Type().GetStorableType() ==
CREDIT_CARD_VERIFICATION_CODE))) {
// Suggestions come back asynchronously, so the Autocomplete manager will
// handle sending the results back to the renderer.
@@ -686,7 +583,8 @@ void AutofillManager::OnQueryFormFieldAutofillImpl(
// Send Autofill suggestions (could be an empty list).
autocomplete_history_manager_->CancelPendingQuery();
external_delegate_->OnSuggestionsReturned(query_id, suggestions,
- is_all_server_suggestions);
+ autoselect_first_suggestion,
+ context.is_all_server_suggestions);
}
bool AutofillManager::WillFillCreditCardNumber(const FormData& form,
@@ -823,11 +721,27 @@ void AutofillManager::FillCreditCardForm(int query_id,
void AutofillManager::OnFocusNoLongerOnForm() {
ProcessPendingFormForUpload();
+ if (external_delegate_->HasActiveScreenReader())
+ external_delegate_->OnAutofillAvailabilityEvent(false);
}
void AutofillManager::OnFocusOnFormFieldImpl(const FormData& form,
const FormFieldData& field,
- const gfx::RectF& bounding_box) {}
+ const gfx::RectF& bounding_box) {
+ // Notify installed screen readers if the focus is on a field for which there
+ // are suggestions to present. Ignore if a screen reader is not present.
+ if (!external_delegate_->HasActiveScreenReader())
+ return;
+
+ // TODO(https://crbug.com/848427): Add metrics for performance impact.
+ std::vector<Suggestion> suggestions;
+ SuggestionsContext context;
+ GetAvailableSuggestions(form, field, &suggestions, &context);
+
+ external_delegate_->OnAutofillAvailabilityEvent(
+ context.suppress_reason == SuppressReason::kNotSuppressed &&
+ !suggestions.empty());
+}
void AutofillManager::OnSelectControlDidChangeImpl(
const FormData& form,
@@ -855,18 +769,20 @@ void AutofillManager::OnDidFillAutofillFormData(const FormData& form,
if (FindCachedForm(form, &form_structure)) {
form_types = form_structure->GetFormTypes();
}
- AutofillMetrics::LogUserHappinessMetric(AutofillMetrics::USER_DID_AUTOFILL,
- form_types);
+ AutofillMetrics::LogUserHappinessMetric(
+ AutofillMetrics::USER_DID_AUTOFILL, form_types,
+ client_->GetSecurityLevelForUmaHistograms());
if (!user_did_autofill_) {
user_did_autofill_ = true;
AutofillMetrics::LogUserHappinessMetric(
- AutofillMetrics::USER_DID_AUTOFILL_ONCE, form_types);
+ AutofillMetrics::USER_DID_AUTOFILL_ONCE, form_types,
+ client_->GetSecurityLevelForUmaHistograms());
}
UpdateInitialInteractionTimestamp(timestamp);
}
-void AutofillManager::DidShowSuggestions(bool is_new_popup,
+void AutofillManager::DidShowSuggestions(bool has_autofill_suggestions,
const FormData& form,
const FormFieldData& field) {
if (test_delegate_)
@@ -876,15 +792,17 @@ void AutofillManager::DidShowSuggestions(bool is_new_popup,
if (!GetCachedFormAndField(form, field, &form_structure, &autofill_field))
return;
- if (is_new_popup) {
- AutofillMetrics::LogUserHappinessMetric(AutofillMetrics::SUGGESTIONS_SHOWN,
- autofill_field->Type().group());
+ if (has_autofill_suggestions) {
+ AutofillMetrics::LogUserHappinessMetric(
+ AutofillMetrics::SUGGESTIONS_SHOWN, autofill_field->Type().group(),
+ client_->GetSecurityLevelForUmaHistograms());
if (!did_show_suggestions_) {
did_show_suggestions_ = true;
AutofillMetrics::LogUserHappinessMetric(
AutofillMetrics::SUGGESTIONS_SHOWN_ONCE,
- autofill_field->Type().group());
+ autofill_field->Type().group(),
+ client_->GetSecurityLevelForUmaHistograms());
}
if (autofill_field->Type().group() == CREDIT_CARD) {
@@ -995,11 +913,6 @@ bool AutofillManager::IsShowingUnmaskPrompt() {
return full_card_request_ && full_card_request_->IsGettingFullCard();
}
-const std::vector<std::unique_ptr<FormStructure>>&
-AutofillManager::GetFormStructures() {
- return form_structures_;
-}
-
payments::FullCardRequest* AutofillManager::GetOrCreateFullCardRequest() {
if (!full_card_request_) {
full_card_request_.reset(new payments::FullCardRequest(
@@ -1036,7 +949,7 @@ void AutofillManager::SelectFieldOptionsDidChange(const FormData& form) {
FormStructure* cached_form = nullptr;
ignore_result(FindCachedForm(form, &cached_form));
- if (!ParseForm(form, cached_form, &form_structure))
+ if (!ParseFormInternal(form, cached_form, &form_structure))
return;
if (ShouldTriggerRefill(*form_structure))
@@ -1051,7 +964,7 @@ void AutofillManager::OnLoadedServerPredictions(
// the end of the list (and reverse the resulting pointer vector).
std::vector<FormStructure*> queried_forms;
for (const std::string& signature : base::Reversed(form_signatures)) {
- for (auto& cur_form : base::Reversed(form_structures_)) {
+ for (auto& cur_form : base::Reversed(form_structures())) {
if (cur_form->FormSignatureAsStr() == signature) {
queried_forms.push_back(cur_form.get());
break;
@@ -1066,7 +979,8 @@ void AutofillManager::OnLoadedServerPredictions(
return;
// Parse and store the server predictions.
- FormStructure::ParseQueryResponse(std::move(response), queried_forms);
+ FormStructure::ParseQueryResponse(std::move(response), queried_forms,
+ form_interactions_ukm_logger_.get());
// Will log quality metrics for each FormStructure based on the presence of
// autocomplete attributes, if available.
@@ -1131,7 +1045,12 @@ bool AutofillManager::IsAutofillEnabled() const {
client_->IsAutofillSupported();
}
-bool AutofillManager::IsCreditCardAutofillEnabled() {
+bool AutofillManager::IsProfileAutofillEnabled() const {
+ return client_->GetPrefs()->GetBoolean(prefs::kAutofillProfileEnabled) &&
+ client_->IsAutofillSupported();
+}
+
+bool AutofillManager::IsCreditCardAutofillEnabled() const {
return client_->GetPrefs()->GetBoolean(prefs::kAutofillCreditCardEnabled) &&
client_->IsAutofillSupported();
}
@@ -1192,7 +1111,7 @@ void AutofillManager::Reset() {
// save a card is shown after page navigation.
ProcessPendingFormForUpload();
DCHECK(!pending_form_data_);
- form_structures_.clear();
+ AutofillHandler::Reset();
form_interactions_ukm_logger_.reset(
new AutofillMetrics::FormInteractionsUkmLogger(
client_->GetUkmRecorder()));
@@ -1229,7 +1148,7 @@ AutofillManager::AutofillManager(
: AutofillHandler(driver),
client_(client),
payments_client_(std::make_unique<payments::PaymentsClient>(
- driver->GetURLRequestContext(),
+ driver->GetURLLoaderFactory(),
client->GetPrefs(),
client->GetIdentityManager(),
/*unmask_delegate=*/this,
@@ -1399,10 +1318,12 @@ void AutofillManager::FillOrPreviewDataModelForm(
// Don't fill hidden fields, with the exception of <select> fields, for
// the sake of filling the synthetic fields.
- if ((!cached_field->is_focusable ||
- cached_field->role == FormFieldData::ROLE_ATTRIBUTE_PRESENTATION) &&
- result.fields[i].form_control_type != "select-one") {
- continue;
+ if (!cached_field->IsVisible()) {
+ bool skip = result.fields[i].form_control_type != "select-one";
+ form_interactions_ukm_logger_->LogHiddenRepresentationalFieldSkipDecision(
+ *form_structure, *cached_field, skip);
+ if (skip)
+ continue;
}
// Don't fill previously autofilled fields except the initiating field or
@@ -1446,13 +1367,11 @@ void AutofillManager::FillOrPreviewDataModelForm(
// will be sent to the renderer.
FillFieldWithValue(cached_field, data_model, &result.fields[i],
should_notify, cvc);
-
+ // On the renderer, only the section of newly autofilled fields are updated.
if (result.fields[i].is_autofilled)
result.fields[i].section = form_structure->field(i)->section;
- if ((!cached_field->is_focusable ||
- cached_field->role == FormFieldData::ROLE_ATTRIBUTE_PRESENTATION) &&
- result.fields[i].is_autofilled) {
+ if (!cached_field->IsVisible() && result.fields[i].is_autofilled) {
AutofillMetrics::LogHiddenOrPresentationalSelectFieldsFilled();
}
}
@@ -1489,35 +1408,6 @@ std::unique_ptr<FormStructure> AutofillManager::ValidateSubmittedForm(
return submitted_form;
}
-bool AutofillManager::FindCachedForm(const FormData& form,
- FormStructure** form_structure) const {
- // Find the FormStructure that corresponds to |form|.
- // Scan backward through the cached |form_structures_|, as updated versions of
- // forms are added to the back of the list, whereas original versions of these
- // forms might appear toward the beginning of the list. The communication
- // protocol with the crowdsourcing server does not permit us to discard the
- // original versions of the forms.
- *form_structure = nullptr;
- const auto& form_signature = autofill::CalculateFormSignature(form);
- for (auto& cur_form : base::Reversed(form_structures_)) {
- if (cur_form->form_signature() == form_signature || *cur_form == form) {
- *form_structure = cur_form.get();
-
- // The same form might be cached with multiple field counts: in some
- // cases, non-autofillable fields are filtered out, whereas in other cases
- // they are not. To avoid thrashing the cache, keep scanning until we
- // find a cached version with the same number of fields, if there is one.
- if (cur_form->field_count() == form.fields.size())
- break;
- }
- }
-
- if (!(*form_structure))
- return false;
-
- return true;
-}
-
bool AutofillManager::GetCachedFormAndField(const FormData& form,
const FormFieldData& field,
FormStructure** form_structure,
@@ -1596,7 +1486,7 @@ bool AutofillManager::UpdateCachedForm(const FormData& live_form,
// Note: We _must not_ remove the original version of the cached form from
// the list of |form_structures_|. Otherwise, we break parsing of the
// crowdsourcing server's response to our query.
- if (!ParseForm(live_form, cached_form, updated_form))
+ if (!ParseFormInternal(live_form, cached_form, updated_form))
return false;
// Annotate the updated form with its predicted types.
@@ -1666,30 +1556,27 @@ std::vector<Suggestion> AutofillManager::GetCreditCardSuggestions(
for (size_t i = 0; i < suggestions.size(); i++) {
suggestions[i].frontend_id =
MakeFrontendID(suggestions[i].backend_id, std::string());
- suggestions[i].is_value_bold = IsCreditCardPopupValueBold();
}
return suggestions;
}
-void AutofillManager::ParseForms(const std::vector<FormData>& forms) {
- if (forms.empty())
- return;
+void AutofillManager::OnFormsParsed(
+ const std::vector<FormStructure*>& form_structures,
+ const base::TimeTicks timestamp) {
+ DCHECK(!form_structures.empty());
// Setup the url for metrics that we will collect for this form.
form_interactions_ukm_logger_->OnFormsParsed(
- forms[0].main_frame_origin.GetURL());
+ form_structures[0]->ToFormData().main_frame_origin.GetURL(),
+ client_->GetUkmSourceId());
std::vector<FormStructure*> non_queryable_forms;
std::vector<FormStructure*> queryable_forms;
std::set<FormType> form_types;
- for (const FormData& form : forms) {
- const auto parse_form_start_time = TimeTicks::Now();
-
- FormStructure* form_structure = nullptr;
- if (!ParseForm(form, /*cached_form=*/nullptr, &form_structure))
- continue;
- DCHECK(form_structure);
-
+ for (FormStructure* form_structure : form_structures) {
+ form_structure->DetermineHeuristicTypes(client_->GetUkmRecorder(),
+ client_->GetUkmSourceId());
+ forms_loaded_timestamps_[form_structure->ToFormData()] = timestamp;
std::set<FormType> current_form_types = form_structure->GetFormTypes();
form_types.insert(current_form_types.begin(), current_form_types.end());
// Set aside forms with method GET or author-specified types, so that they
@@ -1699,9 +1586,6 @@ void AutofillManager::ParseForms(const std::vector<FormData>& forms) {
else
non_queryable_forms.push_back(form_structure);
- AutofillMetrics::LogParseFormTiming(TimeTicks::Now() -
- parse_form_start_time);
-
// 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.
@@ -1716,18 +1600,20 @@ void AutofillManager::ParseForms(const std::vector<FormData>& forms) {
if (filling_context->on_refill_timer.IsRunning())
filling_context->on_refill_timer.AbandonAndStop();
+ // Can TriggerRefill get FormData from FormStructure?
filling_context->on_refill_timer.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kWaitTimeForDynamicFormsMs),
base::BindRepeating(&AutofillManager::TriggerRefill,
- weak_ptr_factory_.GetWeakPtr(), form,
- form_structure));
+ weak_ptr_factory_.GetWeakPtr(),
+ form_structure->ToFormData(), form_structure));
}
}
if (!queryable_forms.empty() || !non_queryable_forms.empty()) {
- AutofillMetrics::LogUserHappinessMetric(AutofillMetrics::FORMS_LOADED,
- form_types);
+ AutofillMetrics::LogUserHappinessMetric(
+ AutofillMetrics::FORMS_LOADED, form_types,
+ client_->GetSecurityLevelForUmaHistograms());
#if defined(OS_IOS)
// Log this from same location as AutofillMetrics::FORMS_LOADED to ensure
@@ -1742,7 +1628,8 @@ void AutofillManager::ParseForms(const std::vector<FormData>& forms) {
// prompt for credit card assisted filling. Upon accepting the infobar, the
// form will automatically be filled with the user's information through this
// class' FillCreditCardForm().
- if (autofill_assistant_.CanShowCreditCardAssist(form_structures_)) {
+ if (autofill_assistant_.CanShowCreditCardAssist(
+ AutofillHandler::form_structures())) {
const std::vector<CreditCard*> cards =
personal_data_->GetCreditCardsToSuggest(
client_->AreServerCardsSupported());
@@ -1765,35 +1652,16 @@ void AutofillManager::ParseForms(const std::vector<FormData>& forms) {
}
}
-bool AutofillManager::ParseForm(const FormData& form,
- const FormStructure* cached_form,
- FormStructure** parsed_form_structure) {
- DCHECK(parsed_form_structure);
- if (form_structures_.size() >= kMaxFormCacheSize)
- return false;
-
- auto form_structure = std::make_unique<FormStructure>(form);
- form_structure->ParseFieldTypesFromAutocompleteAttributes();
- if (!form_structure->ShouldBeParsed()) {
- return false;
- }
-
- if (cached_form) {
- // We need to keep the server data if available. We need to use them while
- // determining the heuristics.
- form_structure->RetrieveFromCache(*cached_form,
- /*apply_is_autofilled=*/true,
- /*only_server_and_autofill_state=*/true);
+bool AutofillManager::ParseFormInternal(const FormData& form,
+ const FormStructure* cached_form,
+ FormStructure** parsed_form_structure) {
+ if (ParseForm(form, cached_form, parsed_form_structure)) {
+ (*parsed_form_structure)
+ ->DetermineHeuristicTypes(client_->GetUkmRecorder(),
+ client_->GetUkmSourceId());
+ return true;
}
-
- // Ownership is transferred to |form_structures_| which maintains it until
- // the manager is Reset() or destroyed. It is safe to use references below
- // as long as receivers don't take ownership.
- form_structure->set_form_parsed_timestamp(TimeTicks::Now());
- form_structures_.push_back(std::move(form_structure));
- *parsed_form_structure = form_structures_.back().get();
- (*parsed_form_structure)->DetermineHeuristicTypes(client_->GetUkmRecorder());
- return true;
+ return false;
}
int AutofillManager::BackendIDToInt(const std::string& backend_id) const {
@@ -2048,7 +1916,8 @@ void AutofillManager::FillFieldWithValue(AutofillField* autofill_field,
// fields with non-empty values, such as select-one fields.
field_data->is_autofilled = true;
AutofillMetrics::LogUserHappinessMetric(
- AutofillMetrics::FIELD_WAS_AUTOFILLED, autofill_field->Type().group());
+ AutofillMetrics::FIELD_WAS_AUTOFILLED, autofill_field->Type().group(),
+ client_->GetSecurityLevelForUmaHistograms());
if (should_notify) {
client_->DidFillOrPreviewField(
@@ -2120,6 +1989,90 @@ void AutofillManager::TriggerRefill(const FormData& form,
/*is_refill=*/true);
}
+void AutofillManager::GetAvailableSuggestions(
+ const FormData& form,
+ const FormFieldData& field,
+ std::vector<Suggestion>* suggestions,
+ SuggestionsContext* context) {
+ DCHECK(suggestions);
+ DCHECK(context);
+
+ // Need to refresh models before using the form_event_loggers.
+ bool is_autofill_possible = RefreshDataModels();
+
+ bool got_autofillable_form =
+ GetCachedFormAndField(form, field, &context->form_structure,
+ &context->focused_field) &&
+ // Don't send suggestions or track forms that should not be parsed.
+ context->form_structure->ShouldBeParsed();
+
+ // Log interactions of forms that are autofillable.
+ if (got_autofillable_form) {
+ if (context->focused_field->Type().group() == CREDIT_CARD) {
+ context->is_filling_credit_card = true;
+ driver()->DidInteractWithCreditCardForm();
+ credit_card_form_event_logger_->OnDidInteractWithAutofillableForm(
+ context->form_structure->form_signature());
+ } else {
+ address_form_event_logger_->OnDidInteractWithAutofillableForm(
+ context->form_structure->form_signature());
+ }
+ }
+
+ context->is_context_secure = !IsFormNonSecure(form);
+
+ // TODO(rogerm): Early exit here on !driver()->RendererIsAvailable()?
+ // We skip populating autofill data, but might generate warnings and or
+ // signin promo to show over the unavailable renderer. That seems a mistake.
+
+ if (!is_autofill_possible || !driver()->RendererIsAvailable() ||
+ !got_autofillable_form)
+ return;
+
+ context->is_autofill_available = true;
+
+ if (context->is_filling_credit_card) {
+ *suggestions =
+ GetCreditCardSuggestions(field, context->focused_field->Type(),
+ &context->is_all_server_suggestions);
+
+ // Logic for disabling/ablating credit card autofill.
+ if (base::FeatureList::IsEnabled(kAutofillCreditCardAblationExperiment) &&
+ !suggestions->empty()) {
+ context->suppress_reason = SuppressReason::kCreditCardsAblation;
+ suggestions->clear();
+ return;
+ }
+ } else {
+ // On desktop, don't return non credit card related suggestions for forms or
+ // fields that have the "autocomplete" attribute set to off, only if the
+ // feature to always fill addresses is off.
+ if (!base::FeatureList::IsEnabled(kAutofillAlwaysFillAddresses) &&
+ IsDesktopPlatform() && !field.should_autocomplete) {
+ context->suppress_reason = SuppressReason::kAutocompleteOff;
+ return;
+ }
+
+ *suggestions = GetProfileSuggestions(*context->form_structure, field,
+ *context->focused_field);
+ }
+
+ // Don't provide credit card suggestions for non-secure pages, but do provide
+ // them for secure pages with passive mixed content (see implementation of
+ // IsContextSecure).
+ if (!suggestions->empty() && context->is_filling_credit_card &&
+ !context->is_context_secure) {
+ // Replace the suggestion content with a warning message explaining why
+ // Autofill is disabled for a website. The string is different if the
+ // credit card autofill HTTP warning experiment is enabled.
+ Suggestion warning_suggestion(
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_INSECURE_CONNECTION));
+ warning_suggestion.frontend_id =
+ POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE;
+ suggestions->assign(1, warning_suggestion);
+ }
+}
+
AutofillMetrics::CardNumberStatus AutofillManager::GetCardNumberStatus(
CreditCard& credit_card) {
base::string16 number = credit_card.number();
diff --git a/chromium/components/autofill/core/browser/autofill_manager.h b/chromium/components/autofill/core/browser/autofill_manager.h
index 8b4130cc436..eda55e319c2 100644
--- a/chromium/components/autofill/core/browser/autofill_manager.h
+++ b/chromium/components/autofill/core/browser/autofill_manager.h
@@ -29,7 +29,6 @@
#include "components/autofill/core/browser/card_unmask_delegate.h"
#include "components/autofill/core/browser/field_filler.h"
#include "components/autofill/core/browser/form_data_importer.h"
-#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/form_types.h"
#include "components/autofill/core/browser/payments/full_card_request.h"
#include "components/autofill/core/browser/payments/payments_client.h"
@@ -114,7 +113,7 @@ class AutofillManager : public AutofillHandler,
const FormFieldData& field,
const CreditCard& credit_card,
const base::string16& cvc);
- void DidShowSuggestions(bool is_new_popup,
+ void DidShowSuggestions(bool has_autofill_suggestions,
const FormData& form,
const FormFieldData& field);
@@ -136,9 +135,6 @@ class AutofillManager : public AutofillHandler,
// Returns true when the Payments card unmask prompt is being displayed.
bool IsShowingUnmaskPrompt();
- // Returns the present form structures seen by Autofill manager.
- const std::vector<std::unique_ptr<FormStructure>>& GetFormStructures();
-
AutofillClient* client() { return client_; }
AutofillDownloadManager* download_manager() {
@@ -178,11 +174,12 @@ class AutofillManager : public AutofillHandler,
// AutofillHandler:
void OnFocusNoLongerOnForm() override;
+ void OnFocusOnFormFieldImpl(const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box) override;
void OnDidFillAutofillFormData(const FormData& form,
const base::TimeTicks timestamp) override;
void OnDidPreviewAutofillFormData() override;
- void OnFormsSeen(const std::vector<FormData>& forms,
- const base::TimeTicks timestamp) override;
void OnDidEndTextFieldEditing() override;
void OnHidePopup() override;
void OnSetDataList(const std::vector<base::string16>& values,
@@ -194,18 +191,19 @@ class AutofillManager : public AutofillHandler,
// client supports Autofill.
virtual bool IsAutofillEnabled() const;
+ // Returns true if the value of the AutofillProfileEnabled pref is true and
+ // the client supports Autofill.
+ virtual bool IsProfileAutofillEnabled() const;
+
// Returns true if the value of the AutofillCreditCardEnabled pref is true and
// the client supports Autofill.
- virtual bool IsCreditCardAutofillEnabled();
+ virtual bool IsCreditCardAutofillEnabled() const;
// Shared code to determine if |form| should be uploaded to the Autofill
// server. It verifies that uploading is allowed and |form| meets conditions
// to be uploadable. Exposed for testing.
bool ShouldUploadForm(const FormStructure& form);
- // Returns the number of forms this Autofill Manager is aware of.
- size_t NumFormsDetected() const { return form_structures_.size(); }
-
protected:
// Test code should prefer to use this constructor.
AutofillManager(AutofillDriver* driver,
@@ -259,17 +257,15 @@ class AutofillManager : public AutofillHandler,
void OnQueryFormFieldAutofillImpl(int query_id,
const FormData& form,
const FormFieldData& field,
- const gfx::RectF& transformed_box) override;
- void OnFocusOnFormFieldImpl(const FormData& form,
- const FormFieldData& field,
- const gfx::RectF& bounding_box) override;
+ const gfx::RectF& transformed_box,
+ bool autoselect_first_suggestion) override;
void OnSelectControlDidChangeImpl(const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) override;
-
- std::vector<std::unique_ptr<FormStructure>>* form_structures() {
- return &form_structures_;
- }
+ bool ShouldParseForms(const std::vector<FormData>& forms,
+ const base::TimeTicks timestamp) override;
+ void OnFormsParsed(const std::vector<FormStructure*>& form_structures,
+ const base::TimeTicks timestamp) override;
AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger() {
return form_interactions_ukm_logger_.get();
@@ -284,6 +280,9 @@ class AutofillManager : public AutofillHandler,
}
// Exposed for testing.
+ payments::PaymentsClient* payments_client() { return payments_client_.get(); }
+
+ // Exposed for testing.
void set_payments_client(payments::PaymentsClient* payments_client) {
payments_client_.reset(payments_client);
}
@@ -313,6 +312,34 @@ class AutofillManager : public AutofillHandler,
std::set<FieldTypeGroup> type_groups_originally_filled;
};
+ // Indicates the reason why autofill suggestions are suppressed.
+ enum class SuppressReason {
+ kNotSuppressed,
+ // Credit card suggestions are not shown because an ablation experiment is
+ // enabled.
+ kCreditCardsAblation,
+ // Address suggestions are not shown because the field is annotated with
+ // autocomplete=off and the directive is being observed by the browser.
+ kAutocompleteOff,
+ };
+
+ // 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 is_all_server_suggestions = false;
+ SuppressReason suppress_reason = SuppressReason::kNotSuppressed;
+ };
+
+ bool ParseFormInternal(const FormData& form,
+ const FormStructure* cached_form,
+ FormStructure** parsed_form_structure);
+
// AutofillDownloadManager::Observer:
void OnLoadedServerPredictions(
std::string response,
@@ -387,11 +414,6 @@ class AutofillManager : public AutofillHandler,
// or personal data.
std::unique_ptr<FormStructure> ValidateSubmittedForm(const FormData& form);
- // Fills |form_structure| cached element corresponding to |form|.
- // Returns false if the cached element was not found.
- bool FindCachedForm(const FormData& form,
- FormStructure** form_structure) const WARN_UNUSED_RESULT;
-
// Fills |form_structure| and |autofill_field| with the cached elements
// corresponding to |form| and |field|. This might have the side-effect of
// updating the cache. Returns false if the |form| is not autofillable, or if
@@ -439,14 +461,6 @@ class AutofillManager : public AutofillHandler,
// Parses the forms using heuristic matching and querying the Autofill server.
void ParseForms(const std::vector<FormData>& forms);
- // Parses the |form| with the server data retrieved from the |cached_form|
- // (if any), and writes it to the |parse_form_structure|. Adds the
- // |parse_form_structure| to the |form_structures_|. Returns true if the form
- // is parsed.
- bool ParseForm(const FormData& form,
- const FormStructure* cached_form,
- FormStructure** parsed_form_structure);
-
// If |initial_interaction_timestamp_| is unset or is set to a later time than
// |interaction_timestamp|, updates the cached timestamp. The latter check is
// needed because IPC messages can arrive out of order.
@@ -507,6 +521,15 @@ class AutofillManager : public AutofillHandler,
// called if ShouldTriggerRefill returns true.
void TriggerRefill(const FormData& form, FormStructure* form_structure);
+ // 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
+ // if the context is secure.
+ void GetAvailableSuggestions(const FormData& form,
+ const FormFieldData& field,
+ std::vector<Suggestion>* suggestions,
+ SuggestionsContext* context);
+
AutofillClient* const client_;
// Handles Payments service requests.
@@ -565,9 +588,6 @@ class AutofillManager : public AutofillHandler,
// page.
base::TimeTicks initial_interaction_timestamp_;
- // Our copy of the form data.
- std::vector<std::unique_ptr<FormStructure>> form_structures_;
-
// A copy of the currently interacted form data.
std::unique_ptr<FormData> pending_form_data_;
@@ -638,6 +658,13 @@ class AutofillManager : public AutofillHandler,
FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, CreditCardSubmittedFormEvents);
FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest,
CreditCardCheckoutFlowUserActions);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest,
+ LogHiddenRepresentationalFieldSkipDecision);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest,
+ LogRepeatedAddressTypeRationalized);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest,
+ LogRepeatedStateCountryTypeRationalized);
+
FRIEND_TEST_ALL_PREFIXES(
AutofillMetricsTest,
CreditCardSubmittedWithoutSelectingSuggestionsNoCard);
diff --git a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
index 48067a8a6ac..4db3f8b3dfe 100644
--- a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -21,7 +21,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -71,6 +71,7 @@
using autofill::features::kAutofillEnforceMinRequiredFieldsForHeuristics;
using autofill::features::kAutofillEnforceMinRequiredFieldsForQuery;
using autofill::features::kAutofillEnforceMinRequiredFieldsForUpload;
+using autofill::features::kAutofillRestrictUnownedFieldsToFormlessCheckout;
using base::ASCIIToUTF16;
using base::UTF8ToUTF16;
using testing::_;
@@ -289,7 +290,8 @@ class AutofillManagerTest : public testing::Test {
void SetUp() override {
autofill_client_.SetPrefs(test::PrefServiceForTesting());
- personal_data_.set_database(autofill_client_.GetDatabase());
+ personal_data_.Init(autofill_client_.GetDatabase(), nullptr,
+ autofill_client_.GetPrefs(), nullptr, false);
personal_data_.SetPrefService(autofill_client_.GetPrefs());
autofill_driver_ =
std::make_unique<testing::NiceMock<MockAutofillDriver>>();
@@ -368,9 +370,6 @@ class AutofillManagerTest : public testing::Test {
autofill_manager_.reset();
autofill_driver_.reset();
- // Remove the AutofillWebDataService so TestPersonalDataManager does not
- // need to care about removing self as an observer in destruction.
- personal_data_.set_database(scoped_refptr<AutofillWebDataService>(nullptr));
personal_data_.SetPrefService(nullptr);
personal_data_.ClearCreditCards();
@@ -380,8 +379,9 @@ class AutofillManagerTest : public testing::Test {
void GetAutofillSuggestions(int query_id,
const FormData& form,
const FormFieldData& field) {
- autofill_manager_->OnQueryFormFieldAutofill(query_id, form, field,
- gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ query_id, form, field, gfx::RectF(),
+ /*autoselect_first_suggestion=*/false);
}
void GetAutofillSuggestions(const FormData& form,
@@ -995,9 +995,10 @@ TEST_F(AutofillManagerTest,
// Test that we sent the right values to the external delegate. No labels,
// with duplicate values "Grimes" merged.
- CheckSuggestions(kDefaultPageID,
- Suggestion("Googler", "" /* no label */, "", 1),
- Suggestion("Grimes", "" /* no label */, "", 2));
+ CheckSuggestions(
+ kDefaultPageID, Suggestion("Googler", "1600 Amphitheater pkwy", "", 1),
+ Suggestion("Grimes", "1234 Smith Blvd., Carl Grimes", "", 2),
+ Suggestion("Grimes", "1234 Smith Blvd., Robin Grimes", "", 3));
}
// Tests that we return address profile suggestions values when the section
@@ -1019,7 +1020,7 @@ TEST_F(AutofillManagerTest, GetProfileSuggestions_AlreadyAutofilledNoLabels) {
// Test that we sent the right values to the external delegate. No labels.
CheckSuggestions(kDefaultPageID,
- Suggestion("Elvis", "" /* no label */, "", 1));
+ Suggestion("Elvis", "3734 Elvis Presley Blvd.", "", 1));
}
// Test that we return no suggestions when the form has no relevant fields.
@@ -1545,7 +1546,7 @@ TEST_F(AutofillManagerTest,
AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1);
}
-// Test that we return autocomplete-like suggestions when trying to autofill
+// Test that we return normal autofill suggestions when trying to autofill
// already filled forms.
TEST_F(AutofillManagerTest, GetFieldSuggestionsWhenFormIsAutofilled) {
// Set up our form data.
@@ -1560,8 +1561,9 @@ TEST_F(AutofillManagerTest, GetFieldSuggestionsWhenFormIsAutofilled) {
GetAutofillSuggestions(form, field);
// Test that we sent the right values to the external delegate.
- CheckSuggestions(kDefaultPageID, Suggestion("Charles", "", "", 1),
- Suggestion("Elvis", "", "", 2));
+ CheckSuggestions(kDefaultPageID,
+ Suggestion("Charles", "123 Apple St.", "", 1),
+ Suggestion("Elvis", "3734 Elvis Presley Blvd.", "", 2));
}
// Test that nothing breaks when there are autocomplete suggestions but no
@@ -1612,7 +1614,8 @@ TEST_F(AutofillManagerTest, GetFieldSuggestionsWithDuplicateValues) {
GetAutofillSuggestions(form, field);
// Test that we sent the right values to the external delegate.
- CheckSuggestions(kDefaultPageID, Suggestion("Elvis", "", "", 1));
+ CheckSuggestions(kDefaultPageID,
+ Suggestion("Elvis", "3734 Elvis Presley Blvd.", "", 1));
}
TEST_F(AutofillManagerTest, GetProfileSuggestions_FancyPhone) {
@@ -4125,7 +4128,8 @@ TEST_F(AutofillManagerTest, OnLoadedServerPredictions) {
// Simulate having seen this form on page load.
// |form_structure| will be owned by |autofill_manager_|.
TestFormStructure* form_structure = new TestFormStructure(form);
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_recorder */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
autofill_manager_->AddSeenFormStructure(base::WrapUnique(form_structure));
// Similarly, a second form.
@@ -4145,7 +4149,8 @@ TEST_F(AutofillManagerTest, OnLoadedServerPredictions) {
form2.fields.push_back(field);
TestFormStructure* form_structure2 = new TestFormStructure(form2);
- form_structure2->DetermineHeuristicTypes(nullptr /* ukm_recorder */);
+ form_structure2->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
autofill_manager_->AddSeenFormStructure(base::WrapUnique(form_structure2));
AutofillQueryResponseContents response;
@@ -4197,7 +4202,8 @@ TEST_F(AutofillManagerTest, OnLoadedServerPredictions_ResetManager) {
// Simulate having seen this form on page load.
// |form_structure| will be owned by |autofill_manager_|.
TestFormStructure* form_structure = new TestFormStructure(form);
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_recorder */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
autofill_manager_->AddSeenFormStructure(base::WrapUnique(form_structure));
AutofillQueryResponseContents response;
@@ -4250,7 +4256,8 @@ TEST_F(AutofillManagerTest, DetermineHeuristicsWithOverallPrediction) {
// Simulate having seen this form on page load.
// |form_structure| will be owned by |autofill_manager_|.
TestFormStructure* form_structure = new TestFormStructure(form);
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_recorder */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
autofill_manager_->AddSeenFormStructure(base::WrapUnique(form_structure));
AutofillQueryResponseContents response;
@@ -4317,7 +4324,8 @@ TEST_F(AutofillManagerTest, FormSubmittedServerTypes) {
// Simulate having seen this form on page load.
// |form_structure| will be owned by |autofill_manager_|.
TestFormStructure* form_structure = new TestFormStructure(form);
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_recorder */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
// Clear the heuristic types, and instead set the appropriate server types.
std::vector<ServerFieldType> heuristic_types, server_types;
@@ -5268,6 +5276,41 @@ TEST_F(AutofillManagerTest, FillInUpdatedExpirationDate) {
"4012888888881881");
}
+TEST_F(AutofillManagerTest, ProfileDisabledDoesNotFillFormData) {
+ autofill_manager_->SetProfileEnabled(false);
+
+ // Set up our form data.
+ FormData form;
+ test::CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ const char guid[] = "00000000-0000-0000-0000-000000000001";
+
+ // Expect no fields filled, no form data sent to renderer.
+ EXPECT_CALL(*autofill_driver_, SendFormDataToRenderer(_, _, _)).Times(0);
+
+ FillAutofillFormData(kDefaultPageID, form, *form.fields.begin(),
+ MakeFrontendID(std::string(), guid));
+}
+
+TEST_F(AutofillManagerTest, ProfileDisabledDoesNotSuggest) {
+ autofill_manager_->SetProfileEnabled(false);
+
+ // Set up our form data.
+ FormData form;
+ test::CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ FormFieldData field;
+ test::CreateTestFormField("Email", "email", "", "email", &field);
+ GetAutofillSuggestions(form, field);
+ // Expect no suggestions as autofill and autocomplete are disabled for
+ // addresses.
+ EXPECT_FALSE(external_delegate_->on_suggestions_returned_seen());
+}
+
TEST_F(AutofillManagerTest, CreditCardDisabledDoesNotFillFormData) {
autofill_manager_->SetCreditCardEnabled(false);
@@ -5297,8 +5340,7 @@ TEST_F(AutofillManagerTest, CreditCardDisabledDoesNotSuggest) {
FormsSeen(forms);
FormFieldData field;
- test::CreateTestFormField("Name on Card", "nameoncard", "pres", "text",
- &field);
+ test::CreateTestFormField("Name on Card", "nameoncard", "", "text", &field);
GetAutofillSuggestions(form, field);
// Expect no suggestions as autofill and autocomplete are disabled for credit
// cards.
@@ -5922,7 +5964,8 @@ TEST_F(AutofillManagerTest, DisplaySuggestionsForUpdatedServerTypedForm) {
form.fields.push_back(field);
auto form_structure = std::make_unique<TestFormStructure>(form);
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_recorder */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
// Make sure the form can not be autofilled now.
ASSERT_EQ(0u, form_structure->autofill_count());
for (size_t idx = 0; idx < form_structure->field_count(); ++idx) {
@@ -6136,4 +6179,172 @@ TEST_F(AutofillManagerTest,
ASSERT_FALSE(external_delegate_->is_all_server_suggestions());
}
+// Test param indicates if there is an active screen reader.
+class OnFocusOnFormFieldTest : public AutofillManagerTest,
+ public testing::WithParamInterface<bool> {
+ protected:
+ OnFocusOnFormFieldTest() = default;
+ ~OnFocusOnFormFieldTest() override = default;
+
+ void SetUp() override {
+ AutofillManagerTest::SetUp();
+
+ has_active_screen_reader_ = GetParam();
+ external_delegate_->set_has_active_screen_reader(has_active_screen_reader_);
+
+ scoped_feature_list_.InitWithFeatures(
+ // Enabled
+ {},
+ // Disabled
+ {kAutofillEnforceMinRequiredFieldsForHeuristics,
+ kAutofillEnforceMinRequiredFieldsForQuery,
+ kAutofillEnforceMinRequiredFieldsForUpload,
+ kAutofillRestrictUnownedFieldsToFormlessCheckout});
+ }
+
+ void TearDown() override {
+ external_delegate_->set_has_active_screen_reader(false);
+
+ AutofillManagerTest::TearDown();
+ }
+
+ void CheckSuggestionsAvailableIfScreenReaderRunning() {
+ EXPECT_EQ(has_active_screen_reader_,
+ external_delegate_->has_suggestions_available_on_field_focus());
+ }
+
+ void CheckNoSuggestionsAvailableOnFieldFocus() {
+ EXPECT_FALSE(
+ external_delegate_->has_suggestions_available_on_field_focus());
+ }
+
+ bool has_active_screen_reader_;
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_P(OnFocusOnFormFieldTest, AddressSuggestions) {
+ FormData form;
+ form.name = ASCIIToUTF16("MyForm");
+ form.origin = GURL("https://myform.com/form.html");
+ form.action = GURL("https://myform.com/submit.html");
+ FormFieldData field;
+ // Set a valid autocomplete attribute for the first name.
+ test::CreateTestFormField("First name", "firstname", "", "text", &field);
+ field.autocomplete_attribute = "given-name";
+ form.fields.push_back(field);
+ // Set an unrecognized autocomplete attribute for the last name.
+ test::CreateTestFormField("Last Name", "lastname", "", "text", &field);
+ field.autocomplete_attribute = "unrecognized";
+ form.fields.push_back(field);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Suggestions should be returned for the first field.
+ autofill_manager_->OnFocusOnFormFieldImpl(form, form.fields[0], gfx::RectF());
+ CheckSuggestionsAvailableIfScreenReaderRunning();
+
+ // No suggestions should be provided for the second field because of its
+ // unrecognized autocomplete attribute.
+ autofill_manager_->OnFocusOnFormFieldImpl(form, form.fields[1], gfx::RectF());
+ CheckNoSuggestionsAvailableOnFieldFocus();
+}
+
+TEST_P(OnFocusOnFormFieldTest, AddressSuggestions_AutocompleteOffNotRespected) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(kAutofillAlwaysFillAddresses);
+
+ FormData form;
+ form.name = ASCIIToUTF16("MyForm");
+ form.origin = GURL("https://myform.com/form.html");
+ form.action = GURL("https://myform.com/submit.html");
+ FormFieldData field;
+ // Set a valid autocomplete attribute for the first name.
+ test::CreateTestFormField("First name", "firstname", "", "text", &field);
+ field.autocomplete_attribute = "given-name";
+ form.fields.push_back(field);
+ // Set an autocomplete=off attribute for the last name.
+ test::CreateTestFormField("Last Name", "lastname", "", "text", &field);
+ field.should_autocomplete = false;
+ form.fields.push_back(field);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ autofill_manager_->OnFocusOnFormFieldImpl(form, form.fields[1], gfx::RectF());
+ CheckSuggestionsAvailableIfScreenReaderRunning();
+}
+
+TEST_P(OnFocusOnFormFieldTest, AddressSuggestions_AutocompleteOffRespected) {
+ if (!IsDesktopPlatform())
+ return;
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(kAutofillAlwaysFillAddresses);
+
+ FormData form;
+ form.name = ASCIIToUTF16("MyForm");
+ form.origin = GURL("https://myform.com/form.html");
+ form.action = GURL("https://myform.com/submit.html");
+ FormFieldData field;
+ // Set a valid autocomplete attribute for the first name.
+ test::CreateTestFormField("First name", "firstname", "", "text", &field);
+ field.autocomplete_attribute = "given-name";
+ form.fields.push_back(field);
+ // Set an autocomplete=off attribute for the last name.
+ test::CreateTestFormField("Last Name", "lastname", "", "text", &field);
+ field.should_autocomplete = false;
+ form.fields.push_back(field);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ autofill_manager_->OnFocusOnFormFieldImpl(form, form.fields[1], gfx::RectF());
+ CheckNoSuggestionsAvailableOnFieldFocus();
+}
+
+TEST_P(OnFocusOnFormFieldTest, CreditCardSuggestions_SecureContext) {
+ // Set up our form data.
+ FormData form;
+ CreateTestCreditCardFormData(&form, true, false);
+ // Clear the form action.
+ form.action = GURL();
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ autofill_manager_->OnFocusOnFormFieldImpl(form, form.fields[1], gfx::RectF());
+ CheckSuggestionsAvailableIfScreenReaderRunning();
+}
+
+TEST_P(OnFocusOnFormFieldTest, CreditCardSuggestions_NonSecureContext) {
+ // Set up our form data.
+ FormData form;
+ CreateTestCreditCardFormData(&form, false, false);
+ // Clear the form action.
+ form.action = GURL();
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ autofill_manager_->OnFocusOnFormFieldImpl(form, form.fields[1], gfx::RectF());
+ // In a non-HTTPS context, there will be a warning indicating the page is
+ // insecure.
+ CheckSuggestionsAvailableIfScreenReaderRunning();
+}
+
+TEST_P(OnFocusOnFormFieldTest, CreditCardSuggestions_Ablation) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ kAutofillCreditCardAblationExperiment);
+
+ // Set up our form data.
+ FormData form;
+ CreateTestCreditCardFormData(&form, true, false);
+ // Clear the form action.
+ form.action = GURL();
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ autofill_manager_->OnFocusOnFormFieldImpl(form, form.fields[1], gfx::RectF());
+ CheckNoSuggestionsAvailableOnFieldFocus();
+}
+
+INSTANTIATE_TEST_CASE_P(All, OnFocusOnFormFieldTest, testing::Bool());
+
} // 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 2d29dc4284f..0695357a5fb 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/form_data_importer.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/test_autofill_client.h"
#include "components/autofill/core/common/form_data.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -181,6 +182,7 @@ class AutofillMergeTest : public DataDrivenTest,
// Deserializes |str| into a field type.
ServerFieldType StringToFieldType(const std::string& str);
+ TestAutofillClient autofill_client_;
PersonalDataManagerMock personal_data_;
std::unique_ptr<FormDataImporter> form_data_importer_;
@@ -204,7 +206,7 @@ AutofillMergeTest::~AutofillMergeTest() {
void AutofillMergeTest::SetUp() {
test::DisableSystemServices(nullptr);
form_data_importer_ = std::make_unique<FormDataImporter>(
- /*AutofillClient=*/nullptr,
+ &autofill_client_,
/*payments::PaymentsClient=*/nullptr, &personal_data_, "en");
}
@@ -276,6 +278,7 @@ void AutofillMergeTest::MergeProfiles(const std::string& profiles,
// Import the profile.
std::unique_ptr<CreditCard> imported_credit_card;
form_data_importer_->ImportFormData(form_structure,
+ true, // address autofill enabled,
true, // credit card autofill enabled
false, // should return local card
&imported_credit_card);
diff --git a/chromium/components/autofill/core/browser/autofill_metrics.cc b/chromium/components/autofill/core/browser/autofill_metrics.cc
index 9098b92214b..7295ceb2f42 100644
--- a/chromium/components/autofill/core/browser/autofill_metrics.cc
+++ b/chromium/components/autofill/core/browser/autofill_metrics.cc
@@ -79,13 +79,6 @@ std::string PreviousSaveCreditCardPromptUserDecisionToString(
return previous_response;
}
-ukm::SourceId NewUkmSourceWithUrl(ukm::UkmRecorder* ukm_recorder,
- const GURL& url) {
- ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID();
- AutofillMetrics::UpdateSourceURL(ukm_recorder, source_id, url);
- return source_id;
-}
-
// Reduce FormSignature space (in UKM) to a small range for privacy reasons.
int64_t HashFormSignature(autofill::FormSignature form_signature) {
return static_cast<uint64_t>(form_signature) % 1021;
@@ -96,6 +89,31 @@ int64_t HashFieldSignature(autofill::FieldSignature field_signature) {
return static_cast<uint64_t>(field_signature) % 1021;
}
+std::string GetHistogramSuffixForSecurityLevel(
+ security_state::SecurityLevel level) {
+ switch (level) {
+ case security_state::EV_SECURE:
+ return "EV_SECURE";
+ case security_state::SECURE:
+ return "SECURE";
+ case security_state::NONE:
+ return "NONE";
+ case security_state::HTTP_SHOW_WARNING:
+ return "HTTP_SHOW_WARNING";
+ case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
+ return "SECURE_WITH_POLICY_INSTALLED_CERT";
+ case security_state::DANGEROUS:
+ return "DANGEROUS";
+ default:
+ return "OTHER";
+ }
+}
+
+std::string GetSecurityLevelHistogramName(const std::string prefix,
+ security_state::SecurityLevel level) {
+ return prefix + "." + GetHistogramSuffixForSecurityLevel(level);
+}
+
} // namespace
// First, translates |field_type| to the corresponding logical |group| from
@@ -244,6 +262,8 @@ namespace {
// A version of the UMA_HISTOGRAM_ENUMERATION macro that allows the |name|
// to vary over the program's runtime.
+// TODO(crbug.com/850520): Remove this function when the remaining logs use the
+// histogram_functions instead.
void LogUMAHistogramEnumeration(const std::string& name,
int sample,
int boundary_value) {
@@ -256,17 +276,6 @@ void LogUMAHistogramEnumeration(const std::string& name,
histogram->Add(sample);
}
-// A version of the UMA_HISTOGRAM_LONG_TIMES macro that allows the |name|
-// to vary over the program's runtime.
-void LogUMAHistogramLongTimes(const std::string& name,
- const base::TimeDelta& duration) {
- // Note: This leaks memory, which is expected behavior.
- base::HistogramBase* histogram = base::Histogram::FactoryTimeGet(
- name, base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromHours(1),
- 50, base::HistogramBase::kUmaTargetedHistogramFlag);
- histogram->AddTime(duration);
-}
-
const char* GetQualityMetricPredictionSource(
AutofillMetrics::QualityMetricPredictionSource source) {
switch (source) {
@@ -371,14 +380,14 @@ void LogPredictionQualityMetricsForFieldsOnlyFilledWhenFocused(
// Only log aggregate true negative; do not log type specific metrics
// for UNKNOWN/EMPTY.
DVLOG(2) << "TRUE NEGATIVE";
- LogUMAHistogramEnumeration(
+ base::UmaHistogramEnumeration(
aggregate_histogram,
(is_empty ? AutofillMetrics::TRUE_NEGATIVE_EMPTY
: (is_ambiguous ? AutofillMetrics::TRUE_NEGATIVE_AMBIGUOUS
: AutofillMetrics::TRUE_NEGATIVE_UNKNOWN)),
AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
if (log_rationalization_metrics) {
- LogUMAHistogramEnumeration(
+ base::UmaHistogramEnumeration(
rationalization_quality_histogram,
(is_empty ? AutofillMetrics::RATIONALIZATION_GOOD
: AutofillMetrics::RATIONALIZATION_OK),
@@ -393,16 +402,16 @@ void LogPredictionQualityMetricsForFieldsOnlyFilledWhenFocused(
// automatically if there has been no rationalization.
if (predicted_type == actual_type) {
DVLOG(2) << "TRUE POSITIVE";
- LogUMAHistogramEnumeration(aggregate_histogram,
- AutofillMetrics::TRUE_POSITIVE,
- AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
+ base::UmaHistogramEnumeration(
+ aggregate_histogram, AutofillMetrics::TRUE_POSITIVE,
+ AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
LogUMAHistogramEnumeration(
type_specific_histogram,
GetFieldTypeGroupMetric(actual_type, AutofillMetrics::TRUE_POSITIVE),
KMaxFieldTypeGroupMetric);
if (log_rationalization_metrics) {
bool duplicated_filling = DuplicatedFilling(form, field);
- LogUMAHistogramEnumeration(
+ base::UmaHistogramEnumeration(
rationalization_quality_histogram,
(duplicated_filling ? AutofillMetrics::RATIONALIZATION_BAD
: AutofillMetrics::RATIONALIZATION_OK),
@@ -414,9 +423,9 @@ void LogPredictionQualityMetricsForFieldsOnlyFilledWhenFocused(
DVLOG(2) << "MISMATCH";
// Here the prediction is wrong, but user has to provide some value still.
// This should be a false negative.
- LogUMAHistogramEnumeration(aggregate_histogram,
- AutofillMetrics::FALSE_NEGATIVE_MISMATCH,
- AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
+ base::UmaHistogramEnumeration(
+ aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH,
+ AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
// Log FALSE_NEGATIVE_MISMATCH for predicted type if it did predicted
// something but actual type is different.
if (predicted_type != UNKNOWN_TYPE)
@@ -429,7 +438,7 @@ void LogPredictionQualityMetricsForFieldsOnlyFilledWhenFocused(
// Logging RATIONALIZATION_OK despite of type mismatch here because autofill
// would have got it wrong with or without rationalization. Rationalization
// here does not help, neither does it do any harm.
- LogUMAHistogramEnumeration(
+ base::UmaHistogramEnumeration(
rationalization_quality_histogram, AutofillMetrics::RATIONALIZATION_OK,
AutofillMetrics::NUM_RATIONALIZATION_QUALITY_METRICS);
}
@@ -451,7 +460,7 @@ void LogPredictionQualityMetricsForCommonFields(
// Only log aggregate true negative; do not log type specific metrics
// for UNKNOWN/EMPTY.
DVLOG(2) << "TRUE NEGATIVE";
- LogUMAHistogramEnumeration(
+ base::UmaHistogramEnumeration(
aggregate_histogram,
(is_empty ? AutofillMetrics::TRUE_NEGATIVE_EMPTY
: (is_ambiguous ? AutofillMetrics::TRUE_NEGATIVE_AMBIGUOUS
@@ -463,9 +472,9 @@ void LogPredictionQualityMetricsForCommonFields(
DVLOG(2) << "TRUE POSITIVE";
// Log both aggregate and type specific true positive if we correctly
// predict that type with which the field was filled.
- LogUMAHistogramEnumeration(aggregate_histogram,
- AutofillMetrics::TRUE_POSITIVE,
- AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
+ base::UmaHistogramEnumeration(
+ aggregate_histogram, AutofillMetrics::TRUE_POSITIVE,
+ AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
LogUMAHistogramEnumeration(
type_specific_histogram,
GetFieldTypeGroupMetric(actual_type, AutofillMetrics::TRUE_POSITIVE),
@@ -483,8 +492,9 @@ void LogPredictionQualityMetricsForCommonFields(
(is_empty ? AutofillMetrics::FALSE_POSITIVE_EMPTY
: (is_ambiguous ? AutofillMetrics::FALSE_POSITIVE_AMBIGUOUS
: AutofillMetrics::FALSE_POSITIVE_UNKNOWN));
- LogUMAHistogramEnumeration(aggregate_histogram, metric,
- AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
+ base::UmaHistogramEnumeration(
+ aggregate_histogram, metric,
+ AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
LogUMAHistogramEnumeration(type_specific_histogram,
GetFieldTypeGroupMetric(predicted_type, metric),
KMaxFieldTypeGroupMetric);
@@ -496,9 +506,9 @@ void LogPredictionQualityMetricsForCommonFields(
// unknown.
if (predicted_type == UNKNOWN_TYPE) {
DVLOG(2) << "FALSE NEGATIVE";
- LogUMAHistogramEnumeration(aggregate_histogram,
- AutofillMetrics::FALSE_NEGATIVE_UNKNOWN,
- AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
+ base::UmaHistogramEnumeration(
+ aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN,
+ AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
LogUMAHistogramEnumeration(
type_specific_histogram,
GetFieldTypeGroupMetric(actual_type,
@@ -514,9 +524,9 @@ void LogPredictionQualityMetricsForCommonFields(
// This is a mismatch. From the reference of the actual type, this is a false
// negative (it was T, but predicted U). From the reference of the prediction,
// this is a false positive (predicted it was T, but it was U).
- LogUMAHistogramEnumeration(aggregate_histogram,
- AutofillMetrics::FALSE_NEGATIVE_MISMATCH,
- AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
+ base::UmaHistogramEnumeration(
+ aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_MISMATCH,
+ AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
LogUMAHistogramEnumeration(
type_specific_histogram,
GetFieldTypeGroupMetric(actual_type,
@@ -633,14 +643,6 @@ AutofillMetrics::FormEvent GetCardNumberStatusFormEvent(
} // namespace
// static
-void AutofillMetrics::UpdateSourceURL(ukm::UkmRecorder* ukm_recorder,
- ukm::SourceId source_id,
- const GURL& url) {
- if (ukm_recorder)
- ukm_recorder->UpdateSourceURL(source_id, url);
-}
-
-// static
void AutofillMetrics::LogSubmittedCardStateMetric(
SubmittedCardStateMetric metric) {
DCHECK_LT(metric, NUM_SUBMITTED_CARD_STATE_METRICS);
@@ -658,6 +660,22 @@ void AutofillMetrics::LogSubmittedServerCardExpirationStatusMetric(
}
// static
+void AutofillMetrics::LogUploadDisallowedForNetworkMetric(
+ const std::string& network) {
+ UploadDisallowedForNetworkMetric metric;
+ if (network == kEloCard) {
+ metric = DISALLOWED_ELO;
+ } else if (network == kJCBCard) {
+ metric = DISALLOWED_JCB;
+ } else {
+ NOTREACHED();
+ return;
+ }
+ UMA_HISTOGRAM_ENUMERATION("Autofill.CreditCardUploadDisallowedForNetwork",
+ metric);
+}
+
+// static
void AutofillMetrics::LogUploadOfferedCardOriginMetric(
UploadOfferedCardOriginMetric metric) {
DCHECK_LT(metric, NUM_UPLOAD_OFFERED_CARD_ORIGIN_METRICS);
@@ -674,6 +692,16 @@ void AutofillMetrics::LogUploadAcceptedCardOriginMetric(
}
// static
+void AutofillMetrics::LogSaveCardCardholderNamePrefilled(bool prefilled) {
+ UMA_HISTOGRAM_BOOLEAN("Autofill.SaveCardCardholderNamePrefilled", prefilled);
+}
+
+// static
+void AutofillMetrics::LogSaveCardCardholderNameWasEdited(bool edited) {
+ UMA_HISTOGRAM_BOOLEAN("Autofill.SaveCardCardholderNameWasEdited", edited);
+}
+
+// static
void AutofillMetrics::LogCardUploadDecisionMetrics(
int upload_decision_metrics) {
DCHECK(upload_decision_metrics);
@@ -693,9 +721,9 @@ void AutofillMetrics::LogCreditCardInfoBarMetric(
DCHECK_LT(metric, NUM_INFO_BAR_METRICS);
std::string destination = is_uploading ? ".Server" : ".Local";
- LogUMAHistogramEnumeration("Autofill.CreditCardInfoBar" + destination, metric,
- NUM_INFO_BAR_METRICS);
- LogUMAHistogramEnumeration(
+ base::UmaHistogramEnumeration("Autofill.CreditCardInfoBar" + destination,
+ metric, NUM_INFO_BAR_METRICS);
+ base::UmaHistogramEnumeration(
"Autofill.CreditCardInfoBar" + destination +
PreviousSaveCreditCardPromptUserDecisionToString(
previous_save_credit_card_prompt_user_decision),
@@ -714,18 +742,51 @@ void AutofillMetrics::LogSaveCardPromptMetric(
SaveCardPromptMetric metric,
bool is_uploading,
bool is_reshow,
- int previous_save_credit_card_prompt_user_decision) {
+ bool is_requesting_cardholder_name,
+ int previous_save_credit_card_prompt_user_decision,
+ security_state::SecurityLevel security_level) {
DCHECK_LT(metric, NUM_SAVE_CARD_PROMPT_METRICS);
std::string destination = is_uploading ? ".Upload" : ".Local";
std::string show = is_reshow ? ".Reshows" : ".FirstShow";
- LogUMAHistogramEnumeration(
- "Autofill.SaveCreditCardPrompt" + destination + show, metric,
- NUM_SAVE_CARD_PROMPT_METRICS);
- LogUMAHistogramEnumeration(
- "Autofill.SaveCreditCardPrompt" + destination + show +
+ std::string metric_with_destination_and_show =
+ "Autofill.SaveCreditCardPrompt" + destination + show;
+ base::UmaHistogramEnumeration(metric_with_destination_and_show, metric,
+ NUM_SAVE_CARD_PROMPT_METRICS);
+ if (is_requesting_cardholder_name) {
+ base::UmaHistogramEnumeration(
+ metric_with_destination_and_show + ".RequestingCardholderName", metric,
+ NUM_SAVE_CARD_PROMPT_METRICS);
+ }
+ base::UmaHistogramEnumeration(
+ metric_with_destination_and_show +
PreviousSaveCreditCardPromptUserDecisionToString(
previous_save_credit_card_prompt_user_decision),
metric, NUM_SAVE_CARD_PROMPT_METRICS);
+
+ LogSaveCardPromptMetricBySecurityLevel(metric, is_uploading, security_level);
+}
+
+// static
+void AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel(
+ SaveCardPromptMetric metric,
+ bool is_uploading,
+ security_state::SecurityLevel security_level) {
+ // Getting a SECURITY_LEVEL_COUNT security level means that it was not
+ // possible to get the real security level. Don't log.
+ if (security_level == security_state::SecurityLevel::SECURITY_LEVEL_COUNT) {
+ return;
+ }
+
+ std::string histogram_name = "Autofill.SaveCreditCardPrompt.";
+ if (is_uploading) {
+ histogram_name += "Upload";
+ } else {
+ histogram_name += "Local";
+ }
+
+ base::UmaHistogramEnumeration(
+ GetSecurityLevelHistogramName(histogram_name, security_level), metric,
+ NUM_SAVE_CARD_PROMPT_METRICS);
}
// static
@@ -741,12 +802,50 @@ void AutofillMetrics::LogScanCreditCardCompleted(
const base::TimeDelta& duration,
bool completed) {
std::string suffix = completed ? "Completed" : "Cancelled";
- LogUMAHistogramLongTimes("Autofill.ScanCreditCard.Duration_" + suffix,
- duration);
+ base::UmaHistogramLongTimes("Autofill.ScanCreditCard.Duration_" + suffix,
+ duration);
UMA_HISTOGRAM_BOOLEAN("Autofill.ScanCreditCard.Completed", completed);
}
// static
+void AutofillMetrics::LogLocalCardMigrationBubbleOfferMetric(
+ LocalCardMigrationBubbleOfferMetric metric,
+ bool is_reshow) {
+ DCHECK_LT(metric, NUM_LOCAL_CARD_MIGRATION_BUBBLE_OFFER_METRICS);
+ std::string histogram_name = "Autofill.LocalCardMigrationBubbleOffer.";
+ histogram_name += is_reshow ? "Reshows" : "FirstShow";
+ base::UmaHistogramEnumeration(histogram_name, metric,
+ NUM_LOCAL_CARD_MIGRATION_BUBBLE_OFFER_METRICS);
+}
+
+// static
+void AutofillMetrics::LogLocalCardMigrationBubbleUserInteractionMetric(
+ LocalCardMigrationBubbleUserInteractionMetric metric,
+ bool is_reshow) {
+ DCHECK_LT(metric, NUM_LOCAL_CARD_MIGRATION_BUBBLE_USER_INTERACTION_METRICS);
+ std::string histogram_name =
+ "Autofill.LocalCardMigrationBubbleUserInteraction.";
+ histogram_name += is_reshow ? "Reshows" : "FirstShow";
+ base::UmaHistogramEnumeration(
+ histogram_name, metric,
+ NUM_LOCAL_CARD_MIGRATION_BUBBLE_USER_INTERACTION_METRICS);
+}
+
+// static
+void AutofillMetrics::LogSaveCardWithFirstAndLastNameOffered(bool is_local) {
+ std::string histogram_name = "Autofill.SaveCardWithFirstAndLastNameOffered.";
+ histogram_name += is_local ? "Local" : "Server";
+ base::UmaHistogramBoolean(histogram_name, true);
+}
+
+// static
+void AutofillMetrics::LogSaveCardWithFirstAndLastNameComplete(bool is_local) {
+ std::string histogram_name = "Autofill.SaveCardWithFirstAndLastNameComplete.";
+ histogram_name += is_local ? "Local" : "Server";
+ base::UmaHistogramBoolean(histogram_name, true);
+}
+
+// static
void AutofillMetrics::LogUnmaskPromptEvent(UnmaskPromptEvent event) {
UMA_HISTOGRAM_ENUMERATION("Autofill.UnmaskPrompt.Events", event,
NUM_UNMASK_PROMPT_EVENTS);
@@ -776,9 +875,9 @@ void AutofillMetrics::LogUnmaskPromptEventDuration(
NOTREACHED();
return;
}
- LogUMAHistogramLongTimes("Autofill.UnmaskPrompt.Duration", duration);
- LogUMAHistogramLongTimes("Autofill.UnmaskPrompt.Duration." + suffix,
- duration);
+ base::UmaHistogramLongTimes("Autofill.UnmaskPrompt.Duration", duration);
+ base::UmaHistogramLongTimes("Autofill.UnmaskPrompt.Duration." + suffix,
+ duration);
}
// static
@@ -833,10 +932,10 @@ void AutofillMetrics::LogRealPanDuration(
NOTREACHED();
return;
}
- LogUMAHistogramLongTimes("Autofill.UnmaskPrompt.GetRealPanDuration",
- duration);
- LogUMAHistogramLongTimes("Autofill.UnmaskPrompt.GetRealPanDuration." + suffix,
- duration);
+ base::UmaHistogramLongTimes("Autofill.UnmaskPrompt.GetRealPanDuration",
+ duration);
+ base::UmaHistogramLongTimes(
+ "Autofill.UnmaskPrompt.GetRealPanDuration." + suffix, duration);
}
// static
@@ -859,9 +958,10 @@ void AutofillMetrics::LogUnmaskingDuration(
NOTREACHED();
return;
}
- LogUMAHistogramLongTimes("Autofill.UnmaskPrompt.UnmaskingDuration", duration);
- LogUMAHistogramLongTimes("Autofill.UnmaskPrompt.UnmaskingDuration." + suffix,
- duration);
+ base::UmaHistogramLongTimes("Autofill.UnmaskPrompt.UnmaskingDuration",
+ duration);
+ base::UmaHistogramLongTimes(
+ "Autofill.UnmaskPrompt.UnmaskingDuration." + suffix, duration);
}
// static
@@ -915,35 +1015,80 @@ void AutofillMetrics::LogServerQueryMetric(ServerQueryMetric metric) {
}
// static
-void AutofillMetrics::LogUserHappinessMetric(UserHappinessMetric metric,
- FieldTypeGroup field_type_group) {
+void AutofillMetrics::LogUserHappinessMetric(
+ UserHappinessMetric metric,
+ FieldTypeGroup field_type_group,
+ security_state::SecurityLevel security_level) {
LogUserHappinessMetric(
- metric, {FormTypes::FieldTypeGroupToFormType(field_type_group)});
+ metric, {FormTypes::FieldTypeGroupToFormType(field_type_group)},
+ security_level);
}
// static
void AutofillMetrics::LogUserHappinessMetric(
UserHappinessMetric metric,
- const std::set<FormType>& form_types) {
+ const std::set<FormType>& form_types,
+ security_state::SecurityLevel security_level) {
DCHECK_LT(metric, NUM_USER_HAPPINESS_METRICS);
UMA_HISTOGRAM_ENUMERATION("Autofill.UserHappiness", metric,
NUM_USER_HAPPINESS_METRICS);
if (base::ContainsKey(form_types, CREDIT_CARD_FORM)) {
UMA_HISTOGRAM_ENUMERATION("Autofill.UserHappiness.CreditCard", metric,
NUM_USER_HAPPINESS_METRICS);
+ LogUserHappinessBySecurityLevel(metric, CREDIT_CARD_FORM, security_level);
}
if (base::ContainsKey(form_types, ADDRESS_FORM)) {
UMA_HISTOGRAM_ENUMERATION("Autofill.UserHappiness.Address", metric,
NUM_USER_HAPPINESS_METRICS);
+ LogUserHappinessBySecurityLevel(metric, ADDRESS_FORM, security_level);
}
if (base::ContainsKey(form_types, PASSWORD_FORM)) {
UMA_HISTOGRAM_ENUMERATION("Autofill.UserHappiness.Password", metric,
NUM_USER_HAPPINESS_METRICS);
+ LogUserHappinessBySecurityLevel(metric, PASSWORD_FORM, security_level);
}
if (base::ContainsKey(form_types, UNKNOWN_FORM_TYPE)) {
UMA_HISTOGRAM_ENUMERATION("Autofill.UserHappiness.Unknown", metric,
NUM_USER_HAPPINESS_METRICS);
+ LogUserHappinessBySecurityLevel(metric, UNKNOWN_FORM_TYPE, security_level);
+ }
+}
+
+// static
+void AutofillMetrics::LogUserHappinessBySecurityLevel(
+ UserHappinessMetric metric,
+ FormType form_type,
+ security_state::SecurityLevel security_level) {
+ if (security_level == security_state::SecurityLevel::SECURITY_LEVEL_COUNT) {
+ return;
+ }
+
+ std::string histogram_name = "Autofill.UserHappiness.";
+ switch (form_type) {
+ case CREDIT_CARD_FORM:
+ histogram_name += "CreditCard";
+ break;
+
+ case ADDRESS_FORM:
+ histogram_name += "Address";
+ break;
+
+ case PASSWORD_FORM:
+ histogram_name += "Password";
+ break;
+
+ case UNKNOWN_FORM_TYPE:
+ histogram_name += "Unknown";
+ break;
+
+ default:
+ NOTREACHED();
+ return;
}
+
+ base::UmaHistogramEnumeration(
+ GetSecurityLevelHistogramName(histogram_name, security_level), metric,
+ NUM_USER_HAPPINESS_METRICS);
}
// static
@@ -1324,13 +1469,13 @@ void AutofillMetrics::LogShowedHttpNotSecureExplanation() {
// static
void AutofillMetrics::LogCardUploadDecisionsUkm(ukm::UkmRecorder* ukm_recorder,
+ ukm::SourceId source_id,
const GURL& url,
int upload_decision_metrics) {
DCHECK(upload_decision_metrics);
DCHECK_LT(upload_decision_metrics, 1 << kNumCardUploadDecisionMetrics);
if (!url.is_valid())
return;
- ukm::SourceId source_id = NewUkmSourceWithUrl(ukm_recorder, url);
ukm::builders::Autofill_CardUploadDecision(source_id)
.SetUploadDecision(upload_decision_metrics)
.Record(ukm_recorder);
@@ -1339,6 +1484,7 @@ void AutofillMetrics::LogCardUploadDecisionsUkm(ukm::UkmRecorder* ukm_recorder,
// static
void AutofillMetrics::LogDeveloperEngagementUkm(
ukm::UkmRecorder* ukm_recorder,
+ ukm::SourceId source_id,
const GURL& url,
bool is_for_credit_card,
std::set<FormType> form_types,
@@ -1349,7 +1495,7 @@ void AutofillMetrics::LogDeveloperEngagementUkm(
1 << NUM_DEVELOPER_ENGAGEMENT_METRICS);
if (!url.is_valid())
return;
- ukm::SourceId source_id = NewUkmSourceWithUrl(ukm_recorder, url);
+
ukm::builders::Autofill_DeveloperEngagement(source_id)
.SetDeveloperEngagement(developer_engagement_metrics)
.SetIsForCreditCard(is_for_credit_card)
@@ -1452,7 +1598,8 @@ void AutofillMetrics::FormEventLogger::OnDidFillSuggestion(
const AutofillField& field) {
DCHECK(is_for_credit_card_);
form_interactions_ukm_logger_->LogDidFillSuggestion(
- static_cast<int>(credit_card.record_type()), form, field);
+ static_cast<int>(credit_card.record_type()),
+ /*is_for_credit_card=*/true, form, field);
if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD)
Log(AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED);
@@ -1496,7 +1643,8 @@ void AutofillMetrics::FormEventLogger::OnDidFillSuggestion(
const AutofillField& field) {
DCHECK(!is_for_credit_card_);
form_interactions_ukm_logger_->LogDidFillSuggestion(
- static_cast<int>(profile.record_type()), form, field);
+ static_cast<int>(profile.record_type()),
+ /*is_for_for_credit_card=*/false, form, field);
if (profile.record_type() == AutofillProfile::SERVER_PROFILE)
Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED);
@@ -1602,14 +1750,14 @@ void AutofillMetrics::FormEventLogger::Log(FormEvent event) const {
name += "CreditCard";
else
name += "Address";
- LogUMAHistogramEnumeration(name, event, NUM_FORM_EVENTS);
+ base::UmaHistogramEnumeration(name, event, NUM_FORM_EVENTS);
// Log again in a different histogram for credit card forms on nonsecure
// pages, so that form interactions on nonsecure pages can be analyzed on
// their own.
if (is_for_credit_card_ && !is_context_secure_) {
- LogUMAHistogramEnumeration(name + ".OnNonsecurePage", event,
- NUM_FORM_EVENTS);
+ base::UmaHistogramEnumeration(name + ".OnNonsecurePage", event,
+ NUM_FORM_EVENTS);
}
// Logging again in a different histogram for segmentation purposes.
@@ -1621,14 +1769,14 @@ void AutofillMetrics::FormEventLogger::Log(FormEvent event) const {
name += ".WithOnlyLocalData";
else
name += ".WithBothServerAndLocalData";
- LogUMAHistogramEnumeration(name, event, NUM_FORM_EVENTS);
+ base::UmaHistogramEnumeration(name, event, NUM_FORM_EVENTS);
}
void AutofillMetrics::FormEventLogger::Log(
BankNameDisplayedFormEvent event) const {
DCHECK_LT(event, BANK_NAME_NUM_FORM_EVENTS);
std::string name("Autofill.FormEvents.CreditCard.BankNameDisplayed");
- LogUMAHistogramEnumeration(name, event, BANK_NAME_NUM_FORM_EVENTS);
+ base::UmaHistogramEnumeration(name, event, BANK_NAME_NUM_FORM_EVENTS);
}
AutofillMetrics::FormInteractionsUkmLogger::FormInteractionsUkmLogger(
@@ -1636,11 +1784,13 @@ AutofillMetrics::FormInteractionsUkmLogger::FormInteractionsUkmLogger(
: ukm_recorder_(ukm_recorder) {}
void AutofillMetrics::FormInteractionsUkmLogger::OnFormsParsed(
- const GURL& url) {
+ const GURL& url,
+ const ukm::SourceId source_id) {
if (ukm_recorder_ == nullptr)
return;
url_ = url;
+ source_id_ = source_id;
}
void AutofillMetrics::FormInteractionsUkmLogger::LogInteractedWithForm(
@@ -1651,9 +1801,6 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogInteractedWithForm(
if (!CanLog())
return;
- if (source_id_ == -1)
- GetNewSourceID();
-
ukm::builders::Autofill_InteractedWithForm(source_id_)
.SetIsForCreditCard(is_for_credit_card)
.SetLocalRecordTypeCount(local_record_type_count)
@@ -1669,9 +1816,6 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogSuggestionsShown(
if (!CanLog())
return;
- if (source_id_ == -1)
- GetNewSourceID();
-
ukm::builders::Autofill_SuggestionsShown(source_id_)
.SetHeuristicType(static_cast<int>(field.heuristic_type()))
.SetHtmlFieldType(static_cast<int>(field.html_type()))
@@ -1688,9 +1832,6 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogSelectedMaskedServerCard(
if (!CanLog())
return;
- if (source_id_ == -1)
- GetNewSourceID();
-
ukm::builders::Autofill_SelectedMaskedServerCard(source_id_)
.SetMillisecondsSinceFormParsed(
MillisecondsSinceFormParsed(form_parsed_timestamp))
@@ -1699,16 +1840,15 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogSelectedMaskedServerCard(
void AutofillMetrics::FormInteractionsUkmLogger::LogDidFillSuggestion(
int record_type,
+ bool is_for_credit_card,
const FormStructure& form,
const AutofillField& field) {
if (!CanLog())
return;
- if (source_id_ == -1)
- GetNewSourceID();
-
ukm::builders::Autofill_SuggestionFilled(source_id_)
.SetRecordType(record_type)
+ .SetIsForCreditCard(is_for_credit_card)
.SetMillisecondsSinceFormParsed(
MillisecondsSinceFormParsed(form.form_parsed_timestamp()))
.SetFormSignature(HashFormSignature(form.form_signature()))
@@ -1717,15 +1857,14 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogDidFillSuggestion(
}
void AutofillMetrics::FormInteractionsUkmLogger::LogTextFieldDidChange(
- const AutofillField& field,
- const base::TimeTicks& form_parsed_timestamp) {
+ const FormStructure& form,
+ const AutofillField& field) {
if (!CanLog())
return;
- if (source_id_ == -1)
- GetNewSourceID();
-
ukm::builders::Autofill_TextFieldDidChange(source_id_)
+ .SetFormSignature(HashFormSignature(form.form_signature()))
+ .SetFieldSignature(HashFieldSignature(field.GetFieldSignature()))
.SetFieldTypeGroup(static_cast<int>(field.Type().group()))
.SetHeuristicType(static_cast<int>(field.heuristic_type()))
.SetServerType(static_cast<int>(field.server_type()))
@@ -1734,7 +1873,7 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogTextFieldDidChange(
.SetIsAutofilled(field.is_autofilled)
.SetIsEmpty(field.IsEmpty())
.SetMillisecondsSinceFormParsed(
- MillisecondsSinceFormParsed(form_parsed_timestamp))
+ MillisecondsSinceFormParsed(form.form_parsed_timestamp()))
.Record(ukm_recorder_);
}
@@ -1745,9 +1884,6 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogFieldFillStatus(
if (!CanLog())
return;
- if (source_id_ == -1)
- GetNewSourceID();
-
ukm::builders::Autofill_FieldFillStatus(source_id_)
.SetMillisecondsSinceFormParsed(
MillisecondsSinceFormParsed(form.form_parsed_timestamp()))
@@ -1773,9 +1909,6 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogFieldType(
if (!CanLog())
return;
- if (source_id_ == -1)
- GetNewSourceID();
-
ukm::builders::Autofill_FieldTypeValidation(source_id_)
.SetMillisecondsSinceFormParsed(
MillisecondsSinceFormParsed(form_parsed_timestamp))
@@ -1788,6 +1921,47 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogFieldType(
.Record(ukm_recorder_);
}
+void AutofillMetrics::FormInteractionsUkmLogger::
+ LogHiddenRepresentationalFieldSkipDecision(const FormStructure& form,
+ const AutofillField& field,
+ bool is_skipped) {
+ if (!CanLog())
+ return;
+
+ ukm::builders::Autofill_HiddenRepresentationalFieldSkipDecision(source_id_)
+ .SetFormSignature(HashFormSignature(form.form_signature()))
+ .SetFieldSignature(HashFieldSignature(field.GetFieldSignature()))
+ .SetFieldTypeGroup(static_cast<int>(field.Type().group()))
+ .SetFieldOverallType(static_cast<int>(field.Type().GetStorableType()))
+ .SetHeuristicType(static_cast<int>(field.heuristic_type()))
+ .SetServerType(static_cast<int>(field.server_type()))
+ .SetHtmlFieldType(static_cast<int>(field.html_type()))
+ .SetHtmlFieldMode(static_cast<int>(field.html_mode()))
+ .SetIsSkipped(is_skipped)
+ .Record(ukm_recorder_);
+}
+
+void AutofillMetrics::FormInteractionsUkmLogger::
+ LogRepeatedServerTypePredictionRationalized(
+ const FormSignature form_signature,
+ const AutofillField& field,
+ ServerFieldType old_type) {
+ if (!CanLog())
+ return;
+
+ ukm::builders::Autofill_RepeatedServerTypePredictionRationalized(source_id_)
+ .SetFormSignature(HashFormSignature(form_signature))
+ .SetFieldSignature(HashFieldSignature(field.GetFieldSignature()))
+ .SetFieldTypeGroup(static_cast<int>(field.Type().group()))
+ .SetFieldNewOverallType(static_cast<int>(field.Type().GetStorableType()))
+ .SetHeuristicType(static_cast<int>(field.heuristic_type()))
+ .SetHtmlFieldType(static_cast<int>(field.html_type()))
+ .SetHtmlFieldMode(static_cast<int>(field.html_mode()))
+ .SetServerType(static_cast<int>(field.server_type()))
+ .SetFieldOldOverallType(static_cast<int>(old_type))
+ .Record(ukm_recorder_);
+}
+
int64_t AutofillMetrics::FormTypesToBitVector(
const std::set<FormType>& form_types) {
int64_t form_type_bv = 0;
@@ -1807,9 +1981,6 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogFormSubmitted(
if (!CanLog())
return;
- if (source_id_ == -1)
- GetNewSourceID();
-
ukm::builders::Autofill_FormSubmitted builder(source_id_);
builder.SetAutofillFormSubmittedState(static_cast<int>(state))
.SetIsForCreditCard(is_for_credit_card)
@@ -1826,13 +1997,6 @@ void AutofillMetrics::FormInteractionsUkmLogger::LogFormSubmitted(
builder.Record(ukm_recorder_);
}
-void AutofillMetrics::FormInteractionsUkmLogger::UpdateSourceURL(
- const GURL& url) {
- url_ = url;
- if (CanLog())
- AutofillMetrics::UpdateSourceURL(ukm_recorder_, source_id_, url_);
-}
-
bool AutofillMetrics::FormInteractionsUkmLogger::CanLog() const {
return ukm_recorder_ && url_.is_valid();
}
@@ -1846,11 +2010,6 @@ int64_t AutofillMetrics::FormInteractionsUkmLogger::MillisecondsSinceFormParsed(
return (now - form_parsed_timestamp).InMilliseconds();
}
-void AutofillMetrics::FormInteractionsUkmLogger::GetNewSourceID() {
- source_id_ = ukm_recorder_->GetNewSourceID();
- AutofillMetrics::UpdateSourceURL(ukm_recorder_, source_id_, url_);
-}
-
AutofillMetrics::UkmTimestampPin::UkmTimestampPin(
FormInteractionsUkmLogger* logger)
: logger_(logger) {
diff --git a/chromium/components/autofill/core/browser/autofill_metrics.h b/chromium/components/autofill/core/browser/autofill_metrics.h
index 83c4bb39fc6..192a7f1219f 100644
--- a/chromium/components/autofill/core/browser/autofill_metrics.h
+++ b/chromium/components/autofill/core/browser/autofill_metrics.h
@@ -21,6 +21,7 @@
#include "components/autofill/core/common/autofill_pref_names.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/signatures_util.h"
+#include "components/security_state/core/security_state.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
namespace autofill {
@@ -89,6 +90,9 @@ class AutofillMetrics {
// One or more valid addresses, and a name were available but the request to
// Payments for upload details failed.
UPLOAD_NOT_OFFERED_GET_UPLOAD_DETAILS_FAILED = 1 << 11,
+ // A textfield for the user to enter/confirm cardholder name was surfaced
+ // in the offer-to-save dialog.
+ USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME = 1 << 12,
// Update |kNumCardUploadDecisionMetrics| when adding new enum here.
};
@@ -152,6 +156,14 @@ class AutofillMetrics {
NUM_SUBMITTED_SERVER_CARD_EXPIRATION_STATUS_METRICS,
};
+ // Metric to measure volume of cards that are disallowed for upload by their
+ // network, most likely due to their network being blocked by Google Payments.
+ enum UploadDisallowedForNetworkMetric {
+ DISALLOWED_ELO = 0,
+ DISALLOWED_JCB = 1,
+ kMaxValue = DISALLOWED_JCB,
+ };
+
// Metric to measure if a card for which upload was offered is already stored
// as a local card on the device or if it has not yet been seen.
enum UploadOfferedCardOriginMetric {
@@ -199,7 +211,8 @@ class AutofillMetrics {
// the prompt was hidden.
SAVE_CARD_PROMPT_END_NAVIGATION_HIDDEN,
// The prompt was dismissed because the user clicked the "Learn more" link.
- SAVE_CARD_PROMPT_DISMISS_CLICK_LEARN_MORE,
+ // Deprecated.
+ DEPRECATED_SAVE_CARD_PROMPT_DISMISS_CLICK_LEARN_MORE,
// The prompt was dismissed because the user clicked a legal message link.
SAVE_CARD_PROMPT_DISMISS_CLICK_LEGAL_MESSAGE,
@@ -385,6 +398,31 @@ class AutofillMetrics {
NUM_SCAN_CREDIT_CARD_PROMPT_METRICS,
};
+ // Metrics to track events when local credit card migration is offered.
+ enum LocalCardMigrationBubbleOfferMetric {
+ // The bubble is requested due to a credit card being used or
+ // local card migration icon in the omnibox being clicked.
+ LOCAL_CARD_MIGRATION_BUBBLE_REQUESTED = 0,
+ // The bubble is actually shown to the user.
+ LOCAL_CARD_MIGRATION_BUBBLE_SHOWN = 1,
+ NUM_LOCAL_CARD_MIGRATION_BUBBLE_OFFER_METRICS,
+ };
+
+ // Metrics to track user interactions with the bubble.
+ enum LocalCardMigrationBubbleUserInteractionMetric {
+ // The user explicitly accepts the offer.
+ LOCAL_CARD_MIGRATION_BUBBLE_CLOSED_ACCEPTED = 0,
+ // The user explicitly denies the offer (clicks the cancel button).
+ LOCAL_CARD_MIGRATION_BUBBLE_CLOSED_DENIED = 1,
+ // The bubble is closed due to user navigating away from the page
+ // while the bubble was showing.
+ LOCAL_CARD_MIGRATION_BUBBLE_CLOSED_NAVIGATED_WHILE_SHOWING = 2,
+ // The bubble is closed due to user navigating away from the page
+ // while the bubble was hidden.
+ LOCAL_CARD_MIGRATION_BUBBLE_CLOSED_NAVIGATED_WHILE_HIDDEN = 3,
+ NUM_LOCAL_CARD_MIGRATION_BUBBLE_USER_INTERACTION_METRICS,
+ };
+
// Each of these metrics is logged only for potentially autofillable forms,
// i.e. forms with at least three fields, etc.
// These are used to derive certain "user happiness" metrics. For example, we
@@ -672,7 +710,9 @@ class AutofillMetrics {
const GURL& url() const { return url_; }
- void OnFormsParsed(const GURL& url);
+ // Initializes this logger with a valid url and source_id.
+ // Unless forms is parsed no autofill UKM can be recorded.
+ void OnFormsParsed(const GURL& url, const ukm::SourceId source_id);
void LogInteractedWithForm(bool is_for_credit_card,
size_t local_record_type_count,
size_t server_record_type_count,
@@ -683,10 +723,11 @@ class AutofillMetrics {
void LogSelectedMaskedServerCard(
const base::TimeTicks& form_parsed_timestamp);
void LogDidFillSuggestion(int record_type,
+ bool is_for_credit_card,
const FormStructure& form,
const AutofillField& field);
- void LogTextFieldDidChange(const AutofillField& field,
- const base::TimeTicks& form_parsed_timestamp);
+ void LogTextFieldDidChange(const FormStructure& form,
+ const AutofillField& field);
void LogFieldFillStatus(const FormStructure& form,
const AutofillField& field,
QualityMetricType metric_type);
@@ -703,16 +744,23 @@ class AutofillMetrics {
const base::TimeTicks& form_parsed_timestamp,
FormSignature form_signature);
- // We initialize |url_| with the form's URL when we log the first form
- // interaction. Later, we may update |url_| with the |source_url()| for the
- // submitted form.
- void UpdateSourceURL(const GURL& url);
+ // Log whether the autofill decided to skip or to fill each
+ // hidden/representational field.
+ void LogHiddenRepresentationalFieldSkipDecision(const FormStructure& form,
+ const AutofillField& field,
+ bool is_skipped);
+
+ // Log the fields for which the autofill decided to rationalize the server
+ // type predictions due to repetition of the type.
+ void LogRepeatedServerTypePredictionRationalized(
+ const FormSignature form_signature,
+ const AutofillField& field,
+ ServerFieldType old_type);
private:
bool CanLog() const;
int64_t MillisecondsSinceFormParsed(
const base::TimeTicks& form_parsed_timestamp) const;
- void GetNewSourceID();
ukm::UkmRecorder* ukm_recorder_; // Weak reference.
ukm::SourceId source_id_ = -1;
@@ -733,11 +781,6 @@ class AutofillMetrics {
DISALLOW_IMPLICIT_CONSTRUCTORS(UkmTimestampPin);
};
- // Friended Helper for recording main frame URLs to UKM.
- static void UpdateSourceURL(ukm::UkmRecorder* ukm_recorder,
- ukm::SourceId source_id,
- const GURL& url);
-
static void LogSubmittedCardStateMetric(SubmittedCardStateMetric metric);
// If a credit card that matches a server card (unmasked or not) was submitted
@@ -746,6 +789,10 @@ class AutofillMetrics {
static void LogSubmittedServerCardExpirationStatusMetric(
SubmittedServerCardExpirationStatusMetric metric);
+ // When credit card upload is disallowed for a particular network, logs which
+ // network was blocked.
+ static void LogUploadDisallowedForNetworkMetric(const std::string& network);
+
// When credit card upload is offered, logs whether the card being offered is
// already a local card on the device or not.
static void LogUploadOfferedCardOriginMetric(
@@ -756,6 +803,15 @@ class AutofillMetrics {
static void LogUploadAcceptedCardOriginMetric(
UploadAcceptedCardOriginMetric metric);
+ // When a cardholder name fix flow is shown during credit card upload, logs
+ // whether the cardholder name was prefilled or not.
+ static void LogSaveCardCardholderNamePrefilled(bool prefilled);
+
+ // When a cardholder name fix flow is shown during credit card upload and the
+ // user accepts upload, logs whether the final cardholder name was changed
+ // from its prefilled value or not.
+ static void LogSaveCardCardholderNameWasEdited(bool edited);
+
// |upload_decision_metrics| is a bitmask of |CardUploadDecisionMetric|.
static void LogCardUploadDecisionMetrics(int upload_decision_metrics);
static void LogCreditCardInfoBarMetric(
@@ -767,8 +823,20 @@ class AutofillMetrics {
SaveCardPromptMetric metric,
bool is_uploading,
bool is_reshow,
- int previous_save_credit_card_prompt_user_decision);
+ bool is_requesting_cardholder_name,
+ int previous_save_credit_card_prompt_user_decision,
+ security_state::SecurityLevel security_level);
+ static void LogSaveCardPromptMetricBySecurityLevel(
+ SaveCardPromptMetric metric,
+ bool is_uploading,
+ security_state::SecurityLevel security_level);
static void LogScanCreditCardPromptMetric(ScanCreditCardPromptMetric metric);
+ static void LogLocalCardMigrationBubbleOfferMetric(
+ LocalCardMigrationBubbleOfferMetric metric,
+ bool is_reshow);
+ static void LogLocalCardMigrationBubbleUserInteractionMetric(
+ LocalCardMigrationBubbleUserInteractionMetric metric,
+ bool is_reshow);
// Should be called when credit card scan is finished. |duration| should be
// the time elapsed between launching the credit card scanner and getting back
@@ -777,6 +845,9 @@ class AutofillMetrics {
static void LogScanCreditCardCompleted(const base::TimeDelta& duration,
bool completed);
+ static void LogSaveCardWithFirstAndLastNameOffered(bool is_local);
+ static void LogSaveCardWithFirstAndLastNameComplete(bool is_local);
+
static void LogDeveloperEngagementMetric(DeveloperEngagementMetric metric);
static void LogHeuristicPredictionQualityMetrics(
@@ -797,11 +868,20 @@ class AutofillMetrics {
static void LogServerQueryMetric(ServerQueryMetric metric);
- static void LogUserHappinessMetric(UserHappinessMetric metric,
- FieldTypeGroup field_type_group);
+ static void LogUserHappinessMetric(
+ UserHappinessMetric metric,
+ FieldTypeGroup field_type_group,
+ security_state::SecurityLevel security_level);
+
+ static void LogUserHappinessMetric(
+ UserHappinessMetric metric,
+ const std::set<FormType>& form_types,
+ security_state::SecurityLevel security_level);
- static void LogUserHappinessMetric(UserHappinessMetric metric,
- const std::set<FormType>& form_types);
+ static void LogUserHappinessBySecurityLevel(
+ UserHappinessMetric metric,
+ FormType form_type,
+ security_state::SecurityLevel security_level);
// Logs |event| to the unmask prompt events histogram.
static void LogUnmaskPromptEvent(UnmaskPromptEvent event);
@@ -971,6 +1051,7 @@ class AutofillMetrics {
// Logs the card upload decisions ukm for the specified |url|.
// |upload_decision_metrics| is a bitmask of |CardUploadDecisionMetric|.
static void LogCardUploadDecisionsUkm(ukm::UkmRecorder* ukm_recorder,
+ ukm::SourceId source_id,
const GURL& url,
int upload_decision_metrics);
@@ -981,6 +1062,7 @@ class AutofillMetrics {
// FormType recorded for the page. This will be stored as a bit vector
// in UKM.
static void LogDeveloperEngagementUkm(ukm::UkmRecorder* ukm_recorder,
+ ukm::SourceId source_id,
const GURL& url,
bool is_for_credit_card,
std::set<FormType> form_types,
@@ -1082,7 +1164,7 @@ class AutofillMetrics {
};
private:
- static const int kNumCardUploadDecisionMetrics = 12;
+ static const int kNumCardUploadDecisionMetrics = 13;
DISALLOW_IMPLICIT_CONSTRUCTORS(AutofillMetrics);
};
diff --git a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc
index 9417d1f34b2..8052cca1a87 100644
--- a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -15,10 +15,10 @@
#include "base/metrics/metrics_hashes.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
-#include "base/test/user_action_tester.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/autofill_external_delegate.h"
@@ -67,6 +67,10 @@ using UkmSelectedMaskedServerCardType =
ukm::builders::Autofill_SelectedMaskedServerCard;
using UkmSuggestionFilledType = ukm::builders::Autofill_SuggestionFilled;
using UkmTextFieldDidChangeType = ukm::builders::Autofill_TextFieldDidChange;
+using UkmLogHiddenRepresentationalFieldSkipDecisionType =
+ ukm::builders::Autofill_HiddenRepresentationalFieldSkipDecision;
+using UkmLogRepeatedServerTypePredictionRationalized =
+ ukm::builders::Autofill_RepeatedServerTypePredictionRationalized;
using UkmFormSubmittedType = ukm::builders::Autofill_FormSubmitted;
using UkmFieldTypeValidationType = ukm::builders::Autofill_FieldTypeValidation;
using UkmFieldFillStatusType = ukm::builders::Autofill_FieldFillStatus;
@@ -244,6 +248,9 @@ class AutofillMetricsTest : public testing::Test {
// bank name.
void RecreateFullServerCreditCardWithBankName();
+ // Purge recorded UKM metrics for running more tests.
+ void PurgeUKM();
+
base::test::ScopedTaskEnvironment scoped_task_environment_;
ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
MockAutofillClient autofill_client_;
@@ -268,7 +275,6 @@ void AutofillMetricsTest::SetUp() {
autofill_client_.SetPrefs(test::PrefServiceForTesting());
personal_data_ = std::make_unique<TestPersonalDataManager>();
- personal_data_->set_database(autofill_client_.GetDatabase());
personal_data_->SetPrefService(autofill_client_.GetPrefs());
personal_data_->SetSyncServiceForTest(&sync_service_);
autofill_driver_ = std::make_unique<TestAutofillDriver>();
@@ -293,6 +299,12 @@ void AutofillMetricsTest::TearDown() {
test_ukm_recorder_.Purge();
}
+void AutofillMetricsTest::PurgeUKM() {
+ autofill_manager_->Reset();
+ test_ukm_recorder_.Purge();
+ autofill_client_.InitializeUKMSources();
+}
+
void AutofillMetricsTest::CreateAmbiguousProfiles() {
personal_data_->ClearProfiles();
CreateTestAutofillProfiles();
@@ -395,8 +407,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
std::vector<ServerFieldType> heuristic_types, server_types;
FormFieldData field;
@@ -719,6 +730,394 @@ TEST_F(AutofillMetricsTest,
}
}
+// Test that we log the skip decisions for hidden/representational fields
+// correctly.
+TEST_F(AutofillMetricsTest, LogHiddenRepresentationalFieldSkipDecision) {
+ // Create a profile.
+ RecreateProfile();
+
+ // Set up our form data.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.origin = 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());
+
+ FormFieldData field;
+ std::vector<ServerFieldType> field_types;
+ int64_t field_signature[4];
+
+ // no decision
+ test::CreateTestFormField("Name", "name", "", "text", &field);
+ form.fields.push_back(field);
+ field_types.push_back(NAME_FULL);
+
+ // skips
+ test::CreateTestFormField("Street", "street", "", "text", &field);
+ field.is_focusable = false;
+ form.fields.push_back(field);
+ field_types.push_back(ADDRESS_HOME_LINE1);
+ field_signature[0] = Collapse(CalculateFieldSignatureForField(field));
+
+ // skips
+ test::CreateTestFormField("City", "city", "", "text", &field);
+ field.role = FormFieldData::ROLE_ATTRIBUTE_PRESENTATION;
+ form.fields.push_back(field);
+ field_types.push_back(ADDRESS_HOME_CITY);
+ field_signature[1] = Collapse(CalculateFieldSignatureForField(field));
+
+ // doesn't skip
+ test::CreateTestFormField("State", "state", "", "select-one", &field);
+ field.is_focusable = false;
+ form.fields.push_back(field);
+ field_types.push_back(ADDRESS_HOME_STATE);
+ field_signature[2] = Collapse(CalculateFieldSignatureForField(field));
+
+ // doesn't skip
+ test::CreateTestFormField("Country", "country", "", "select-one", &field);
+ field.role = FormFieldData::ROLE_ATTRIBUTE_PRESENTATION;
+ form.fields.push_back(field);
+ field_types.push_back(ADDRESS_HOME_COUNTRY);
+ field_signature[3] = Collapse(CalculateFieldSignatureForField(field));
+
+ int64_t form_signature = Collapse(CalculateFormSignature(form));
+
+ // Simulate having seen this form on page load.
+ // |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("00000000-0000-0000-0000-000000000001"); // local profile.
+ autofill_manager_->FillOrPreviewForm(
+ AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
+ autofill_manager_->MakeFrontendID(std::string(), guid));
+ }
+
+ VerifyFormInteractionUkm(
+ test_ukm_recorder_, form,
+ UkmLogHiddenRepresentationalFieldSkipDecisionType::kEntryName,
+ {{{UkmLogHiddenRepresentationalFieldSkipDecisionType::kFormSignatureName,
+ form_signature},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldSignatureName,
+ field_signature[0]},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldTypeGroupName,
+ ADDRESS_HOME},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::
+ kFieldOverallTypeName,
+ ADDRESS_HOME_LINE1},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHeuristicTypeName,
+ ADDRESS_HOME_LINE1},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kServerTypeName,
+ ADDRESS_HOME_LINE1},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHtmlFieldTypeName,
+ HTML_TYPE_UNSPECIFIED},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHtmlFieldModeName,
+ HTML_MODE_NONE},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kIsSkippedName,
+ true}},
+ {{UkmLogHiddenRepresentationalFieldSkipDecisionType::kFormSignatureName,
+ form_signature},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldSignatureName,
+ field_signature[1]},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldTypeGroupName,
+ ADDRESS_HOME},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::
+ kFieldOverallTypeName,
+ ADDRESS_HOME_CITY},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHeuristicTypeName,
+ ADDRESS_HOME_CITY},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kServerTypeName,
+ ADDRESS_HOME_CITY},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHtmlFieldTypeName,
+ HTML_TYPE_UNSPECIFIED},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHtmlFieldModeName,
+ HTML_MODE_NONE},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kIsSkippedName,
+ true}},
+ {{UkmLogHiddenRepresentationalFieldSkipDecisionType::kFormSignatureName,
+ form_signature},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldSignatureName,
+ field_signature[2]},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldTypeGroupName,
+ ADDRESS_HOME},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::
+ kFieldOverallTypeName,
+ ADDRESS_HOME_STATE},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHeuristicTypeName,
+ ADDRESS_HOME_STATE},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kServerTypeName,
+ ADDRESS_HOME_STATE},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHtmlFieldTypeName,
+ HTML_TYPE_UNSPECIFIED},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHtmlFieldModeName,
+ HTML_MODE_NONE},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kIsSkippedName,
+ false}},
+ {{UkmLogHiddenRepresentationalFieldSkipDecisionType::kFormSignatureName,
+ form_signature},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldSignatureName,
+ field_signature[3]},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldTypeGroupName,
+ ADDRESS_HOME},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::
+ kFieldOverallTypeName,
+ ADDRESS_HOME_COUNTRY},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHeuristicTypeName,
+ ADDRESS_HOME_COUNTRY},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kServerTypeName,
+ ADDRESS_HOME_COUNTRY},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHtmlFieldTypeName,
+ HTML_TYPE_UNSPECIFIED},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHtmlFieldModeName,
+ HTML_MODE_NONE},
+ {UkmLogHiddenRepresentationalFieldSkipDecisionType::kIsSkippedName,
+ false}}});
+}
+
+// Test that we log the address line fields whose server types are rationalized
+TEST_F(AutofillMetricsTest, LogRepeatedAddressTypeRationalized) {
+ // Set up our form data.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.origin = 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());
+
+ int64_t field_signature[2];
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("fullname");
+ field.name = ASCIIToUTF16("fullname");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Street 1");
+ field.name = ASCIIToUTF16("street1");
+ form.fields.push_back(field);
+ field_signature[0] = Collapse(CalculateFieldSignatureForField(field));
+
+ field.label = ASCIIToUTF16("Street 2");
+ field.name = ASCIIToUTF16("street2");
+ form.fields.push_back(field);
+ field_signature[1] = Collapse(CalculateFieldSignatureForField(field));
+
+ int64_t form_signature = Collapse(CalculateFormSignature(form));
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+
+ std::vector<ServerFieldType> field_types;
+ for (size_t i = 0; i < forms[0]->field_count(); ++i)
+ field_types.push_back(UNKNOWN_TYPE);
+
+ // Simulate having seen this form on page load.
+ // |form_structure| will be owned by |autofill_manager_|.
+ autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+ AutofillQueryResponseContents response;
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+
+ FormStructure::ParseQueryResponse(
+ response_string, forms,
+ autofill_manager_->form_interactions_ukm_logger());
+
+ ASSERT_EQ(test_ukm_recorder_
+ .GetEntriesByName(
+ UkmLogRepeatedServerTypePredictionRationalized::kEntryName)
+ .size(),
+ (size_t)2);
+
+ VerifyFormInteractionUkm(
+ test_ukm_recorder_, form,
+ UkmLogRepeatedServerTypePredictionRationalized::kEntryName,
+ {{{UkmLogRepeatedServerTypePredictionRationalized::kFormSignatureName,
+ form_signature},
+ {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName,
+ field_signature[0]},
+ {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName,
+ ADDRESS_HOME},
+ {UkmLogRepeatedServerTypePredictionRationalized::
+ kFieldOldOverallTypeName,
+ ADDRESS_HOME_STREET_ADDRESS},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHeuristicTypeName,
+ UNKNOWN_TYPE},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldTypeName,
+ HTML_TYPE_UNSPECIFIED},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldModeName,
+ HTML_MODE_NONE},
+ {UkmLogRepeatedServerTypePredictionRationalized::
+ kFieldNewOverallTypeName,
+ ADDRESS_HOME_LINE1},
+ {UkmLogRepeatedServerTypePredictionRationalized::kServerTypeName,
+ ADDRESS_HOME_STREET_ADDRESS}},
+ {{UkmLogRepeatedServerTypePredictionRationalized::kFormSignatureName,
+ form_signature},
+ {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName,
+ field_signature[1]},
+ {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName,
+ ADDRESS_HOME},
+ {UkmLogRepeatedServerTypePredictionRationalized::
+ kFieldOldOverallTypeName,
+ ADDRESS_HOME_STREET_ADDRESS},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHeuristicTypeName,
+ UNKNOWN_TYPE},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldTypeName,
+ HTML_TYPE_UNSPECIFIED},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldModeName,
+ HTML_MODE_NONE},
+ {UkmLogRepeatedServerTypePredictionRationalized::
+ kFieldNewOverallTypeName,
+ ADDRESS_HOME_LINE2},
+ {UkmLogRepeatedServerTypePredictionRationalized::kServerTypeName,
+ ADDRESS_HOME_STREET_ADDRESS}}});
+}
+
+// Test that we log the state/country fields whose server types are rationalized
+TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) {
+ // Set up our form data.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.origin = 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());
+
+ int64_t field_signature[3];
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ form.fields.push_back(field);
+ field_signature[0] = Collapse(CalculateFieldSignatureForField(field));
+
+ field.label = ASCIIToUTF16("fullname");
+ field.name = ASCIIToUTF16("fullname");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ form.fields.push_back(field);
+ field_signature[2] = Collapse(CalculateFieldSignatureForField(field));
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ field.is_focusable = false;
+ field.form_control_type = "select-one";
+ form.fields.push_back(field);
+ // Regardless of the order of appearance, hidden fields are rationalized
+ // before their corresponding visible one.
+ field_signature[1] = Collapse(CalculateFieldSignatureForField(field));
+
+ int64_t form_signature = Collapse(CalculateFormSignature(form));
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+
+ std::vector<ServerFieldType> field_types;
+ for (size_t i = 0; i < forms[0]->field_count(); ++i)
+ field_types.push_back(UNKNOWN_TYPE);
+
+ // Simulate having seen this form on page load.
+ // |form_structure| will be owned by |autofill_manager_|.
+ autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+ AutofillQueryResponseContents response;
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+
+ FormStructure::ParseQueryResponse(
+ response_string, forms,
+ autofill_manager_->form_interactions_ukm_logger());
+
+ ASSERT_EQ(test_ukm_recorder_
+ .GetEntriesByName(
+ UkmLogRepeatedServerTypePredictionRationalized::kEntryName)
+ .size(),
+ (size_t)3);
+
+ VerifyFormInteractionUkm(
+ test_ukm_recorder_, form,
+ UkmLogRepeatedServerTypePredictionRationalized::kEntryName,
+ {{{UkmLogRepeatedServerTypePredictionRationalized::kFormSignatureName,
+ form_signature},
+ {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName,
+ field_signature[0]},
+ {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName,
+ ADDRESS_HOME},
+ {UkmLogRepeatedServerTypePredictionRationalized::
+ kFieldOldOverallTypeName,
+ ADDRESS_HOME_COUNTRY},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHeuristicTypeName,
+ UNKNOWN_TYPE},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldTypeName,
+ HTML_TYPE_UNSPECIFIED},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldModeName,
+ HTML_MODE_NONE},
+ {UkmLogRepeatedServerTypePredictionRationalized::kServerTypeName,
+ ADDRESS_HOME_COUNTRY},
+ {UkmLogRepeatedServerTypePredictionRationalized::
+ kFieldNewOverallTypeName,
+ ADDRESS_HOME_COUNTRY}},
+ {{UkmLogRepeatedServerTypePredictionRationalized::kFormSignatureName,
+ form_signature},
+ {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName,
+ field_signature[1]},
+ {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName,
+ ADDRESS_HOME},
+ {UkmLogRepeatedServerTypePredictionRationalized::
+ kFieldOldOverallTypeName,
+ ADDRESS_HOME_COUNTRY},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHeuristicTypeName,
+ UNKNOWN_TYPE},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldTypeName,
+ HTML_TYPE_UNSPECIFIED},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldModeName,
+ HTML_MODE_NONE},
+ {UkmLogRepeatedServerTypePredictionRationalized::
+ kFieldNewOverallTypeName,
+ ADDRESS_HOME_STATE},
+ {UkmLogRepeatedServerTypePredictionRationalized::kServerTypeName,
+ ADDRESS_HOME_COUNTRY}},
+ {{UkmLogRepeatedServerTypePredictionRationalized::kFormSignatureName,
+ form_signature},
+ {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName,
+ field_signature[2]},
+ {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName,
+ ADDRESS_HOME},
+ {UkmLogRepeatedServerTypePredictionRationalized::
+ kFieldOldOverallTypeName,
+ ADDRESS_HOME_COUNTRY},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHeuristicTypeName,
+ UNKNOWN_TYPE},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldTypeName,
+ HTML_TYPE_UNSPECIFIED},
+ {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldModeName,
+ HTML_MODE_NONE},
+ {UkmLogRepeatedServerTypePredictionRationalized::
+ kFieldNewOverallTypeName,
+ ADDRESS_HOME_STATE},
+ {UkmLogRepeatedServerTypePredictionRationalized::kServerTypeName,
+ ADDRESS_HOME_COUNTRY}}});
+}
+
// Test that we log quality metrics appropriately with fields having
// only_fill_when_focused and are supposed to log RATIONALIZATION_BAD.
TEST_F(AutofillMetricsTest,
@@ -1095,8 +1494,7 @@ TEST_P(QualityMetricsTest, Classification) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
std::vector<ServerFieldType> heuristic_types, server_types, actual_types;
AutofillField field;
@@ -1313,8 +1711,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics_NoSubmission) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
std::vector<ServerFieldType> heuristic_types, server_types;
FormFieldData field;
@@ -1527,8 +1924,10 @@ TEST_F(AutofillMetricsTest, QualityMetrics_BasedOnAutocomplete) {
std::unique_ptr<TestFormStructure> form_structure =
std::make_unique<TestFormStructure>(form);
TestFormStructure* form_structure_ptr = form_structure.get();
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_recorder */);
- autofill_manager_->form_structures()->push_back(std::move(form_structure));
+ form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
+ autofill_manager_->mutable_form_structures()->push_back(
+ std::move(form_structure));
AutofillQueryResponseContents response;
// Server response will match with autocomplete.
@@ -1969,8 +2368,7 @@ TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields_NoSubmission) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
std::vector<ServerFieldType> heuristic_types, server_types;
@@ -2019,8 +2417,7 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -2124,8 +2521,7 @@ TEST_F(AutofillMetricsTest,
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -2135,7 +2531,7 @@ TEST_F(AutofillMetricsTest,
std::vector<FormData> forms(1, form);
- // Ensure no metrics are logged when loading a non-fillable form.
+ // Ensure no entries are logged when loading a non-fillable form.
{
base::test::ScopedFeatureList features;
features.InitAndEnableFeature(
@@ -2143,7 +2539,6 @@ TEST_F(AutofillMetricsTest,
autofill_manager_->OnFormsSeen(forms, TimeTicks::Now());
autofill_manager_->Reset();
- EXPECT_EQ(0ul, test_ukm_recorder_.sources_count());
EXPECT_EQ(0ul, test_ukm_recorder_.entries_count());
}
@@ -2172,8 +2567,7 @@ TEST_F(AutofillMetricsTest,
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -2230,8 +2624,7 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -2249,14 +2642,13 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) {
{
SCOPED_TRACE("VPA is the only hint");
autofill_manager_->OnFormsSeen(forms, TimeTicks::Now());
- autofill_manager_->Reset();
VerifyDeveloperEngagementUkm(
test_ukm_recorder_, forms.back(), /*is_for_credit_card=*/false,
/* UPI VPA has Unknown form type.*/
{FormType::ADDRESS_FORM, FormType::UNKNOWN_FORM_TYPE},
{AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT});
- test_ukm_recorder_.Purge();
+ PurgeUKM();
}
// Add another field with an author-specified field type to the form.
@@ -2267,7 +2659,6 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) {
{
SCOPED_TRACE("VPA and other autocomplete hint present");
autofill_manager_->OnFormsSeen(forms, TimeTicks::Now());
- autofill_manager_->Reset();
VerifyDeveloperEngagementUkm(
test_ukm_recorder_, forms.back(), /*is_for_credit_card=*/false,
@@ -2275,6 +2666,7 @@ TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) {
{FormType::ADDRESS_FORM, FormType::UNKNOWN_FORM_TYPE},
{AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS,
AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT});
+ PurgeUKM();
}
}
@@ -2398,96 +2790,12 @@ TEST_F(AutofillMetricsTest, LogStoredCreditCardMetrics) {
"Autofill.DaysSinceLastUse.StoredCreditCard.Server.Unmasked", 200, 3);
}
-// Test that the profile count is logged correctly.
-TEST_F(AutofillMetricsTest, StoredProfileCount) {
- // The metric should be logged when the profiles are first loaded.
- {
- base::HistogramTester histogram_tester;
- personal_data_->LoadProfiles();
- histogram_tester.ExpectUniqueSample("Autofill.StoredProfileCount", 2, 1);
- }
-
- // The metric should only be logged once.
- {
- base::HistogramTester histogram_tester;
- personal_data_->LoadProfiles();
- histogram_tester.ExpectTotalCount("Autofill.StoredProfileCount", 0);
- }
-}
-
-// Test that the local credit card count is logged correctly.
-TEST_F(AutofillMetricsTest, StoredLocalCreditCardCount) {
- // The metric should be logged when the credit cards are first loaded.
- {
- base::HistogramTester histogram_tester;
- RecreateCreditCards(true /* include_local_credit_card */,
- false /* include_masked_server_credit_card */,
- false /* include_full_server_credit_card */);
- histogram_tester.ExpectUniqueSample("Autofill.StoredLocalCreditCardCount",
- 1, 1);
- }
-
- // The metric should only be logged once.
- {
- base::HistogramTester histogram_tester;
- RecreateCreditCards(true /* include_local_credit_card */,
- false /* include_masked_server_credit_card */,
- false /* include_full_server_credit_card */);
- histogram_tester.ExpectTotalCount("Autofill.StoredLocalCreditCardCount", 0);
- }
-}
-
-// Test that the masked server credit card counts are logged correctly.
-TEST_F(AutofillMetricsTest, StoredServerCreditCardCounts_Masked) {
- // The metrics should be logged when the credit cards are first loaded.
- {
- base::HistogramTester histogram_tester;
- RecreateCreditCards(false /* include_local_credit_card */,
- true /* include_masked_server_credit_card */,
- false /* include_full_server_credit_card */);
- histogram_tester.ExpectUniqueSample(
- "Autofill.StoredServerCreditCardCount.Masked", 1, 1);
- }
-
- // The metrics should only be logged once.
- {
- base::HistogramTester histogram_tester;
- RecreateCreditCards(false /* include_local_credit_card */,
- true /* include_masked_server_credit_card */,
- true /* include_full_server_credit_card */);
- histogram_tester.ExpectTotalCount(
- "Autofill.StoredServerCreditCardCount.Masked", 0);
- }
-}
-
-// Test that the unmasked (full) server credit card counts are logged correctly.
-TEST_F(AutofillMetricsTest, StoredServerCreditCardCounts_Unmasked) {
- // The metrics should be logged when the credit cards are first loaded.
- {
- base::HistogramTester histogram_tester;
- RecreateCreditCards(false /* include_local_credit_card */,
- false /* include_masked_server_credit_card */,
- true /* include_full_server_credit_card */);
- histogram_tester.ExpectUniqueSample(
- "Autofill.StoredServerCreditCardCount.Unmasked", 1, 1);
- }
-
- // The metrics should only be logged once.
- {
- base::HistogramTester histogram_tester;
- RecreateCreditCards(false /* include_local_credit_card */,
- false /* include_masked_server_credit_card */,
- true /* include_full_server_credit_card */);
- histogram_tester.ExpectTotalCount(
- "Autofill.StoredServerCreditCardCount.Unmasked", 0);
- }
-}
-
// Test that we correctly log when Autofill is enabled.
TEST_F(AutofillMetricsTest, AutofillIsEnabledAtStartup) {
base::HistogramTester histogram_tester;
personal_data_->SetAutofillEnabled(true);
personal_data_->Init(autofill_client_.GetDatabase(),
+ /*account_database=*/nullptr,
autofill_client_.GetPrefs(),
/*identity_manager=*/nullptr,
/*is_off_the_record=*/false);
@@ -2499,6 +2807,7 @@ TEST_F(AutofillMetricsTest, AutofillIsDisabledAtStartup) {
base::HistogramTester histogram_tester;
personal_data_->SetAutofillEnabled(false);
personal_data_->Init(autofill_client_.GetDatabase(),
+ /*account_database=*/nullptr,
autofill_client_.GetPrefs(),
/*identity_manager=*/nullptr,
/*is_off_the_record=*/false);
@@ -2512,8 +2821,7 @@ TEST_F(AutofillMetricsTest, AddressSuggestionsCount) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -2534,7 +2842,8 @@ TEST_F(AutofillMetricsTest, AddressSuggestionsCount) {
{
// Simulate activating the autofill popup for the phone field.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample("Autofill.AddressSuggestionsCount", 2,
1);
}
@@ -2544,7 +2853,8 @@ 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;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectTotalCount("Autofill.AddressSuggestionsCount", 0);
}
@@ -2555,7 +2865,8 @@ TEST_F(AutofillMetricsTest, AddressSuggestionsCount) {
{
// Simulate activating the autofill popup for the email field after typing.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample("Autofill.AddressSuggestionsCount", 1,
1);
}
@@ -2565,11 +2876,12 @@ TEST_F(AutofillMetricsTest, AddressSuggestionsCount) {
autofill_manager_->AddSeenForm(form, field_types, field_types);
{
- // Simulate activating the autofill popup for the email field after typing.
+ // Simulate activating the autofill popup for the email field after a fill.
form.fields[0].is_autofilled = true;
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
- histogram_tester.ExpectTotalCount("Autofill.AddressSuggestionsCount", 0);
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ histogram_tester.ExpectTotalCount("Autofill.AddressSuggestionsCount", 1);
}
}
@@ -2584,8 +2896,7 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -2606,7 +2917,8 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
// Simulate an Autofill query on a credit card field.
{
base::UserActionTester user_action_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledCreditCardSuggestions"));
}
@@ -2656,7 +2968,8 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
// Simulate submitting the credit card form.
{
base::UserActionTester user_action_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
EXPECT_EQ(1,
@@ -2690,12 +3003,14 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) {
test_ukm_recorder_, form, UkmSuggestionFilledType::kEntryName,
{{{UkmSuggestionFilledType::kRecordTypeName, CreditCard::LOCAL_CARD},
{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0},
+ {UkmSuggestionFilledType::kIsForCreditCardName, true},
{UkmSuggestionFilledType::kFieldSignatureName,
Collapse(CalculateFieldSignatureForField(form.fields.front()))},
{UkmSuggestionFilledType::kFormSignatureName,
Collapse(CalculateFormSignature(form))}},
{{UkmSuggestionFilledType::kRecordTypeName, CreditCard::LOCAL_CARD},
{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0},
+ {UkmSuggestionFilledType::kIsForCreditCardName, true},
{UkmSuggestionFilledType::kFieldSignatureName,
Collapse(CalculateFieldSignatureForField(form.fields.front()))},
{UkmSuggestionFilledType::kFormSignatureName,
@@ -2717,8 +3032,7 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -2739,7 +3053,8 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
// Simulate an Autofill query on a profile field.
{
base::UserActionTester user_action_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledProfileSuggestions"));
}
@@ -2788,7 +3103,8 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
// Simulate submitting the profile form.
{
base::UserActionTester user_action_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
EXPECT_EQ(1,
@@ -2822,6 +3138,7 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
test_ukm_recorder_, form, UkmSuggestionFilledType::kEntryName,
{{{UkmSuggestionFilledType::kRecordTypeName,
AutofillProfile::LOCAL_PROFILE},
+ {UkmSuggestionFilledType::kIsForCreditCardName, false},
{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0},
{UkmSuggestionFilledType::kFieldSignatureName,
Collapse(CalculateFieldSignatureForField(form.fields.front()))},
@@ -2830,6 +3147,7 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) {
{{UkmSuggestionFilledType::kRecordTypeName,
AutofillProfile::LOCAL_PROFILE},
{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0},
+ {UkmSuggestionFilledType::kIsForCreditCardName, false},
{UkmSuggestionsShownType::kFieldSignatureName,
Collapse(CalculateFieldSignatureForField(form.fields.front()))},
{UkmSuggestionsShownType::kFormSignatureName,
@@ -2870,28 +3188,32 @@ TEST_F(AutofillMetricsTest, PolledCreditCardSuggestions_DebounceLogs) {
// Simulate an Autofill query on a credit card field. A poll should be logged.
base::UserActionTester user_action_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, form.fields[0],
- gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, form.fields[0], gfx::RectF(),
+ /*autoselect_first_suggestion=*/false);
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.
- autofill_manager_->OnQueryFormFieldAutofill(0, form, form.fields[0],
- gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, form.fields[0], gfx::RectF(),
+ /*autoselect_first_suggestion=*/false);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledCreditCardSuggestions"));
// Simulate a query to another field. There should be a second poll logged.
- autofill_manager_->OnQueryFormFieldAutofill(0, form, form.fields[1],
- gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, form.fields[1], gfx::RectF(),
+ /*autoselect_first_suggestion=*/false);
EXPECT_EQ(2, user_action_tester.GetActionCount(
"Autofill_PolledCreditCardSuggestions"));
// Simulate a query back to the initial field. There should be a third poll
// logged.
- autofill_manager_->OnQueryFormFieldAutofill(0, form, form.fields[0],
- gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, form.fields[0], gfx::RectF(),
+ /*autoselect_first_suggestion=*/false);
EXPECT_EQ(3, user_action_tester.GetActionCount(
"Autofill_PolledCreditCardSuggestions"));
}
@@ -2908,9 +3230,7 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
- autofill_client_.set_form_origin(form.origin);
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -2929,14 +3249,15 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) {
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ 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;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, form.fields[1],
- gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, form.fields[1], gfx::RectF(),
+ /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample(
"Autofill.QueriedCreditCardFormIsSecure", false, 1);
}
@@ -2947,14 +3268,14 @@ TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) {
form.origin = GURL("https://example.com/form.html");
form.action = GURL("https://example.com/submit.html");
form.main_frame_origin =
- url::Origin::Create(GURL("https://example_root.com/form.html"));
- autofill_client_.set_form_origin(form.origin);
+ 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;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, form.fields[1],
- gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, form.fields[1], gfx::RectF(),
+ /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample(
"Autofill.QueriedCreditCardFormIsSecure", true, 1);
}
@@ -2970,8 +3291,7 @@ TEST_F(AutofillMetricsTest, PolledProfileSuggestions_DebounceLogs) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -2991,28 +3311,32 @@ TEST_F(AutofillMetricsTest, PolledProfileSuggestions_DebounceLogs) {
// Simulate an Autofill query on a profile field. A poll should be logged.
base::UserActionTester user_action_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, form.fields[0],
- gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, form.fields[0], gfx::RectF(),
+ /*autoselect_first_suggestion=*/false);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledProfileSuggestions"));
// Simulate a second query on the same field. There should still only be poll
// logged.
- autofill_manager_->OnQueryFormFieldAutofill(0, form, form.fields[0],
- gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, form.fields[0], gfx::RectF(),
+ /*autoselect_first_suggestion=*/false);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledProfileSuggestions"));
// Simulate a query to another field. There should be a second poll logged.
- autofill_manager_->OnQueryFormFieldAutofill(0, form, form.fields[1],
- gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, form.fields[1], gfx::RectF(),
+ /*autoselect_first_suggestion=*/false);
EXPECT_EQ(2, user_action_tester.GetActionCount(
"Autofill_PolledProfileSuggestions"));
// Simulate a query back to the initial field. There should be a third poll
// logged.
- autofill_manager_->OnQueryFormFieldAutofill(0, form, form.fields[0],
- gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, form.fields[0], gfx::RectF(),
+ /*autoselect_first_suggestion=*/false);
EXPECT_EQ(3, user_action_tester.GetActionCount(
"Autofill_PolledProfileSuggestions"));
}
@@ -3024,8 +3348,7 @@ TEST_F(AutofillMetricsTest, CreditCardInteractedFormEvents) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -3046,7 +3369,8 @@ TEST_F(AutofillMetricsTest, CreditCardInteractedFormEvents) {
{
// Simulate activating the autofill popup for the credit card field.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.CreditCard",
AutofillMetrics::FORM_EVENT_INTERACTED_ONCE, 1);
@@ -3059,8 +3383,10 @@ TEST_F(AutofillMetricsTest, CreditCardInteractedFormEvents) {
{
// Simulate activating the autofill popup for the credit card field twice.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
- autofill_manager_->OnQueryFormFieldAutofill(1, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 1, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.CreditCard",
AutofillMetrics::FORM_EVENT_INTERACTED_ONCE, 1);
@@ -3074,8 +3400,7 @@ TEST_F(AutofillMetricsTest, CreditCardShownFormEvents) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -3164,7 +3489,8 @@ TEST_F(AutofillMetricsTest, CreditCardShownFormEvents) {
{
// Simulating new popup being shown.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
@@ -3186,7 +3512,8 @@ TEST_F(AutofillMetricsTest, CreditCardShownFormEvents) {
{
// Simulating two popups in the same page load.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
histogram_tester.ExpectBucketCount(
@@ -3212,7 +3539,8 @@ TEST_F(AutofillMetricsTest, CreditCardShownFormEvents) {
{
// Simulating new popup being shown.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
histogram_tester.ExpectBucketCount(
"Autofill.FormEvents.CreditCard",
@@ -3234,7 +3562,8 @@ TEST_F(AutofillMetricsTest, CreditCardShownFormEvents) {
{
// Simulating two popups in the same page load.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
histogram_tester.ExpectBucketCount(
@@ -3262,8 +3591,7 @@ TEST_F(AutofillMetricsTest, CreditCardSelectedFormEvents) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -3334,8 +3662,7 @@ TEST_F(AutofillMetricsTest, CreditCardFilledFormEvents) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -3458,7 +3785,8 @@ TEST_F(AutofillMetricsTest, CreditCardFilledFormEvents) {
base::HistogramTester histogram_tester;
std::string guid(
"10000000-0000-0000-0000-000000000002"); // masked server card
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->FillOrPreviewForm(
AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.back(),
autofill_manager_->MakeFrontendID(guid, std::string()));
@@ -3482,7 +3810,8 @@ TEST_F(AutofillMetricsTest, CreditCardFilledFormEvents) {
base::HistogramTester histogram_tester;
std::string guid(
"10000000-0000-0000-0000-000000000002"); // masked server card
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->FillOrPreviewForm(
AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.back(),
autofill_manager_->MakeFrontendID(guid, std::string()));
@@ -3509,7 +3838,8 @@ TEST_F(AutofillMetricsTest, CreditCardFilledFormEvents) {
base::HistogramTester histogram_tester;
std::string guid(
"10000000-0000-0000-0000-000000000003"); // full server card
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->FillOrPreviewForm(
AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, field,
autofill_manager_->MakeFrontendID(guid, std::string()));
@@ -3529,7 +3859,8 @@ TEST_F(AutofillMetricsTest, CreditCardFilledFormEvents) {
base::HistogramTester histogram_tester;
std::string guid(
"10000000-0000-0000-0000-000000000003"); // full server card
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->FillOrPreviewForm(
AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, field,
autofill_manager_->MakeFrontendID(guid, std::string()));
@@ -3555,8 +3886,7 @@ TEST_F(AutofillMetricsTest, CreditCardGetRealPanDuration) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -3627,8 +3957,7 @@ TEST_F(AutofillMetricsTest,
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -3649,7 +3978,8 @@ TEST_F(AutofillMetricsTest,
// Simulating submission with suggestion shown, but not selected.
base::HistogramTester histogram_tester;
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
@@ -3690,7 +4020,8 @@ TEST_F(AutofillMetricsTest,
// Simulating submission with suggestion shown, but not selected.
base::HistogramTester histogram_tester;
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
@@ -3733,7 +4064,8 @@ TEST_F(AutofillMetricsTest,
// Simulating submission with suggestion shown, but not selected.
base::HistogramTester histogram_tester;
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
@@ -3755,8 +4087,7 @@ TEST_F(AutofillMetricsTest,
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -3778,7 +4109,8 @@ TEST_F(AutofillMetricsTest,
// Simulating submission with suggestion shown, but not selected.
base::HistogramTester histogram_tester;
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
@@ -3800,8 +4132,7 @@ TEST_F(AutofillMetricsTest,
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -3823,7 +4154,8 @@ TEST_F(AutofillMetricsTest,
// Simulating submission with suggestion shown, but not selected.
base::HistogramTester histogram_tester;
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
@@ -3845,8 +4177,7 @@ TEST_F(AutofillMetricsTest,
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -3868,7 +4199,8 @@ TEST_F(AutofillMetricsTest,
// Simulating submission with suggestion shown and selected.
base::HistogramTester histogram_tester;
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
std::string guid("10000000-0000-0000-0000-000000000001");
autofill_manager_->FillOrPreviewForm(
AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.back(),
@@ -3900,8 +4232,7 @@ TEST_F(AutofillMetricsTest, ShouldNotLogFormEventNoCardForAddressForm) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -3922,7 +4253,8 @@ TEST_F(AutofillMetricsTest, ShouldNotLogFormEventNoCardForAddressForm) {
// Simulating submission with no filled data.
base::HistogramTester histogram_tester;
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
@@ -3942,8 +4274,7 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -3964,7 +4295,8 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
{
// Simulating submission with no filled data.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
@@ -3981,8 +4313,7 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
}
// Reset the autofill manager state and purge UKM logs.
- autofill_manager_->Reset();
- test_ukm_recorder_.Purge();
+ PurgeUKM();
autofill_manager_->AddSeenForm(form, field_types, field_types);
@@ -3990,7 +4321,8 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
// Simulating submission with suggestion shown.
base::HistogramTester histogram_tester;
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
@@ -4018,8 +4350,7 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
}
// Reset the autofill manager state and purge UKM logs.
- autofill_manager_->Reset();
- test_ukm_recorder_.Purge();
+ PurgeUKM();
autofill_manager_->AddSeenForm(form, field_types, field_types);
@@ -4029,7 +4360,8 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
// triggered.
base::HistogramTester histogram_tester;
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
// Trigger UploadFormDataAsyncCallback.
@@ -4059,15 +4391,15 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
}
// Reset the autofill manager state and purge UKM logs.
- autofill_manager_->Reset();
- test_ukm_recorder_.Purge();
+ PurgeUKM();
autofill_manager_->AddSeenForm(form, field_types, field_types);
{
// Simulating submission with filled local data.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
std::string guid("10000000-0000-0000-0000-000000000001"); // local card
autofill_manager_->FillOrPreviewForm(
AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
@@ -4084,6 +4416,7 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
VerifyFormInteractionUkm(
test_ukm_recorder_, form, UkmSuggestionFilledType::kEntryName,
{{{UkmSuggestionFilledType::kRecordTypeName, CreditCard::LOCAL_CARD},
+ {UkmSuggestionFilledType::kIsForCreditCardName, true},
{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0},
{UkmSuggestionFilledType::kFieldSignatureName,
Collapse(CalculateFieldSignatureForField(form.fields.front()))},
@@ -4096,15 +4429,15 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
}
// Reset the autofill manager state and purge UKM logs.
- autofill_manager_->Reset();
- test_ukm_recorder_.Purge();
+ PurgeUKM();
autofill_manager_->AddSeenForm(form, field_types, field_types);
{
// Simulating submission with filled server data.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
std::string guid(
"10000000-0000-0000-0000-000000000003"); // full server card
autofill_manager_->FillOrPreviewForm(
@@ -4124,6 +4457,7 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
{{{UkmSuggestionFilledType::kRecordTypeName,
CreditCard::FULL_SERVER_CARD},
{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0},
+ {UkmSuggestionFilledType::kIsForCreditCardName, true},
{UkmSuggestionFilledType::kFieldSignatureName,
Collapse(CalculateFieldSignatureForField(form.fields.front()))},
{UkmSuggestionFilledType::kFormSignatureName,
@@ -4135,8 +4469,7 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
}
// Reset the autofill manager state and purge UKM logs.
- autofill_manager_->Reset();
- test_ukm_recorder_.Purge();
+ PurgeUKM();
autofill_manager_->AddSeenForm(form, field_types, field_types);
@@ -4165,6 +4498,7 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
{{{UkmSuggestionFilledType::kRecordTypeName,
CreditCard::MASKED_SERVER_CARD},
{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0},
+ {UkmSuggestionFilledType::kIsForCreditCardName, true},
{UkmSuggestionFilledType::kFieldSignatureName,
Collapse(CalculateFieldSignatureForField(form.fields.back()))},
{UkmSuggestionFilledType::kFormSignatureName,
@@ -4179,8 +4513,7 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
}
// Reset the autofill manager state and purge UKM logs.
- autofill_manager_->Reset();
- test_ukm_recorder_.Purge();
+ PurgeUKM();
// Recreating cards as the previous test should have upgraded the masked
// card to a full card.
@@ -4195,7 +4528,8 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
{
// Simulating multiple submissions.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
@@ -4263,8 +4597,7 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
}
// Reset the autofill manager state and purge UKM logs.
- autofill_manager_->Reset();
- test_ukm_recorder_.Purge();
+ PurgeUKM();
autofill_manager_->AddSeenForm(form, field_types, field_types);
@@ -4340,8 +4673,7 @@ TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -4362,7 +4694,8 @@ TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) {
{
// Simulating submission with no filled data.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
@@ -4381,7 +4714,8 @@ TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) {
// Simulating submission with suggestion shown.
base::HistogramTester histogram_tester;
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
@@ -4399,7 +4733,8 @@ TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) {
{
// Simulating submission with filled local data.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
std::string guid("10000000-0000-0000-0000-000000000001"); // local card
autofill_manager_->FillOrPreviewForm(
AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
@@ -4421,7 +4756,8 @@ TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) {
{
// Simulating submission with filled server data.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
// Full server card.
std::string guid("10000000-0000-0000-0000-000000000003");
autofill_manager_->FillOrPreviewForm(
@@ -4473,7 +4809,8 @@ TEST_F(AutofillMetricsTest, CreditCardWillSubmitFormEvents) {
{
// Simulating multiple submissions.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
autofill_manager_->OnFormSubmitted(
@@ -4569,8 +4906,7 @@ TEST_F(AutofillMetricsTest, AddressInteractedFormEvents) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -4591,7 +4927,8 @@ TEST_F(AutofillMetricsTest, AddressInteractedFormEvents) {
{
// Simulate activating the autofill popup for the street field.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_INTERACTED_ONCE, 1);
@@ -4604,8 +4941,10 @@ TEST_F(AutofillMetricsTest, AddressInteractedFormEvents) {
{
// Simulate activating the autofill popup for the street field twice.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
- autofill_manager_->OnQueryFormFieldAutofill(1, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 1, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.Address",
AutofillMetrics::FORM_EVENT_INTERACTED_ONCE, 1);
@@ -4621,8 +4960,7 @@ TEST_F(AutofillMetricsTest, AddressShownFormEvents) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -4711,8 +5049,7 @@ TEST_F(AutofillMetricsTest, AddressFilledFormEvents) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -4777,8 +5114,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -4799,7 +5135,8 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
{
// Simulating submission with no filled data.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
@@ -4815,8 +5152,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
}
// Reset the autofill manager state and purge UKM logs.
- autofill_manager_->Reset();
- test_ukm_recorder_.Purge();
+ PurgeUKM();
autofill_manager_->AddSeenForm(form, field_types, field_types);
@@ -4825,7 +5161,8 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
// autofill manager is reset before UploadFormDataAsyncCallback is
// triggered.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
// Trigger UploadFormDataAsyncCallback.
@@ -4843,8 +5180,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
}
// Reset the autofill manager state and purge UKM logs.
- autofill_manager_->Reset();
- test_ukm_recorder_.Purge();
+ PurgeUKM();
autofill_manager_->AddSeenForm(form, field_types, field_types);
@@ -4852,7 +5188,8 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
// Simulating submission with suggestion shown.
base::HistogramTester histogram_tester;
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
@@ -4870,7 +5207,8 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
{
// Simulating submission with filled local data.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
std::string guid("00000000-0000-0000-0000-000000000001"); // local profile
autofill_manager_->FillOrPreviewForm(
AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
@@ -4892,7 +5230,8 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
{
// Simulating multiple submissions.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
autofill_manager_->OnFormSubmitted(
@@ -4982,8 +5321,7 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -5004,7 +5342,8 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
{
// Simulating submission with no filled data.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
@@ -5023,7 +5362,8 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
// Simulating submission with suggestion shown.
base::HistogramTester histogram_tester;
autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
histogram_tester.ExpectBucketCount(
@@ -5041,7 +5381,8 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
{
// Simulating submission with filled local data.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
std::string guid("00000000-0000-0000-0000-000000000001"); // local profile
autofill_manager_->FillOrPreviewForm(
AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, form.fields.front(),
@@ -5063,7 +5404,8 @@ TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) {
{
// Simulating multiple submissions.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
autofill_manager_->OnFormSubmitted(
form, false, SubmissionSource::FORM_SUBMISSION, TimeTicks::Now());
autofill_manager_->OnFormSubmitted(
@@ -5159,8 +5501,7 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -5184,7 +5525,8 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
{
// Simulate activating the autofill popup for the credit card field.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.CreditCard.WithNoData",
AutofillMetrics::FORM_EVENT_INTERACTED_ONCE, 1);
@@ -5200,7 +5542,8 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
{
// Simulate activating the autofill popup for the credit card field.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.CreditCard.WithOnlyLocalData",
AutofillMetrics::FORM_EVENT_INTERACTED_ONCE, 1);
@@ -5216,7 +5559,8 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
{
// Simulate activating the autofill popup for the credit card field.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.CreditCard.WithOnlyServerData",
AutofillMetrics::FORM_EVENT_INTERACTED_ONCE, 1);
@@ -5232,7 +5576,8 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
{
// Simulate activating the autofill popup for the credit card field.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.CreditCard.WithOnlyServerData",
AutofillMetrics::FORM_EVENT_INTERACTED_ONCE, 1);
@@ -5248,7 +5593,8 @@ TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
{
// Simulate activating the autofill popup for the credit card field.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.CreditCard.WithBothServerAndLocalData",
AutofillMetrics::FORM_EVENT_INTERACTED_ONCE, 1);
@@ -5262,8 +5608,7 @@ TEST_F(AutofillMetricsTest, AddressFormEventsAreSegmented) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -5285,7 +5630,8 @@ TEST_F(AutofillMetricsTest, AddressFormEventsAreSegmented) {
{
// Simulate activating the autofill popup for the street field.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.Address.WithNoData",
AutofillMetrics::FORM_EVENT_INTERACTED_ONCE, 1);
@@ -5299,7 +5645,8 @@ TEST_F(AutofillMetricsTest, AddressFormEventsAreSegmented) {
{
// Simulate activating the autofill popup for the street field.
base::HistogramTester histogram_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
histogram_tester.ExpectUniqueSample(
"Autofill.FormEvents.Address.WithOnlyLocalData",
AutofillMetrics::FORM_EVENT_INTERACTED_ONCE, 1);
@@ -5349,8 +5696,7 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -5656,8 +6002,7 @@ TEST_F(
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -5725,8 +6070,9 @@ TEST_F(
TEST_F(AutofillMetricsTest, LogUserHappinessMetric_PasswordForm) {
{
base::HistogramTester histogram_tester;
- AutofillMetrics::LogUserHappinessMetric(AutofillMetrics::USER_DID_AUTOFILL,
- PASSWORD_FIELD);
+ AutofillMetrics::LogUserHappinessMetric(
+ AutofillMetrics::USER_DID_AUTOFILL, PASSWORD_FIELD,
+ security_state::SecurityLevel::SECURITY_LEVEL_COUNT);
histogram_tester.ExpectBucketCount("Autofill.UserHappiness",
AutofillMetrics::USER_DID_AUTOFILL, 1);
histogram_tester.ExpectBucketCount("Autofill.UserHappiness.Password",
@@ -5738,8 +6084,9 @@ TEST_F(AutofillMetricsTest, LogUserHappinessMetric_PasswordForm) {
{
base::HistogramTester histogram_tester;
- AutofillMetrics::LogUserHappinessMetric(AutofillMetrics::USER_DID_AUTOFILL,
- USERNAME_FIELD);
+ AutofillMetrics::LogUserHappinessMetric(
+ AutofillMetrics::USER_DID_AUTOFILL, USERNAME_FIELD,
+ security_state::SecurityLevel::SECURITY_LEVEL_COUNT);
histogram_tester.ExpectBucketCount("Autofill.UserHappiness",
AutofillMetrics::USER_DID_AUTOFILL, 1);
histogram_tester.ExpectBucketCount("Autofill.UserHappiness.Password",
@@ -5753,8 +6100,9 @@ TEST_F(AutofillMetricsTest, LogUserHappinessMetric_PasswordForm) {
TEST_F(AutofillMetricsTest, LogUserHappinessMetric_UnknownForm) {
{
base::HistogramTester histogram_tester;
- AutofillMetrics::LogUserHappinessMetric(AutofillMetrics::USER_DID_AUTOFILL,
- NO_GROUP);
+ AutofillMetrics::LogUserHappinessMetric(
+ AutofillMetrics::USER_DID_AUTOFILL, NO_GROUP,
+ security_state::SecurityLevel::SECURITY_LEVEL_COUNT);
histogram_tester.ExpectBucketCount("Autofill.UserHappiness",
AutofillMetrics::USER_DID_AUTOFILL, 1);
histogram_tester.ExpectBucketCount("Autofill.UserHappiness.Unknown",
@@ -5766,8 +6114,9 @@ TEST_F(AutofillMetricsTest, LogUserHappinessMetric_UnknownForm) {
{
base::HistogramTester histogram_tester;
- AutofillMetrics::LogUserHappinessMetric(AutofillMetrics::USER_DID_AUTOFILL,
- TRANSACTION);
+ AutofillMetrics::LogUserHappinessMetric(
+ AutofillMetrics::USER_DID_AUTOFILL, TRANSACTION,
+ security_state::SecurityLevel::SECURITY_LEVEL_COUNT);
histogram_tester.ExpectBucketCount("Autofill.UserHappiness",
AutofillMetrics::USER_DID_AUTOFILL, 1);
histogram_tester.ExpectBucketCount("Autofill.UserHappiness.Unknown",
@@ -5785,8 +6134,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_EmptyForm) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
std::vector<FormData> forms(1, form);
@@ -5812,8 +6160,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_CreditCardForm) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("https://example.com/form.html");
form.action = GURL("https://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
// Construct a valid credit card form.
FormFieldData field;
@@ -5982,8 +6329,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -6174,6 +6520,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
{{{UkmSuggestionFilledType::kRecordTypeName,
AutofillProfile::LOCAL_PROFILE},
{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0},
+ {UkmSuggestionFilledType::kIsForCreditCardName, false},
{UkmSuggestionFilledType::kFieldSignatureName,
Collapse(CalculateFieldSignatureForField(form.fields[0]))},
{UkmSuggestionFilledType::kFormSignatureName,
@@ -6194,7 +6541,11 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
{UkmTextFieldDidChangeType::kHtmlFieldModeName, HTML_MODE_NONE},
{UkmTextFieldDidChangeType::kIsAutofilledName, false},
{UkmTextFieldDidChangeType::kIsEmptyName, true},
- {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}},
+ {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0},
+ {UkmTextFieldDidChangeType::kFieldSignatureName,
+ Collapse(CalculateFieldSignatureForField(form.fields[0]))},
+ {UkmTextFieldDidChangeType::kFormSignatureName,
+ Collapse(CalculateFormSignature(form))}},
{{UkmTextFieldDidChangeType::kFieldTypeGroupName, NAME},
{UkmTextFieldDidChangeType::kHeuristicTypeName, NAME_FULL},
{UkmTextFieldDidChangeType::kServerTypeName, NO_SERVER_DATA},
@@ -6202,7 +6553,11 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
{UkmTextFieldDidChangeType::kHtmlFieldModeName, HTML_MODE_NONE},
{UkmTextFieldDidChangeType::kIsAutofilledName, true},
{UkmTextFieldDidChangeType::kIsEmptyName, true},
- {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}},
+ {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0},
+ {UkmTextFieldDidChangeType::kFieldSignatureName,
+ Collapse(CalculateFieldSignatureForField(form.fields[0]))},
+ {UkmTextFieldDidChangeType::kFormSignatureName,
+ Collapse(CalculateFormSignature(form))}},
{{UkmTextFieldDidChangeType::kFieldTypeGroupName, EMAIL},
{UkmTextFieldDidChangeType::kHeuristicTypeName, EMAIL_ADDRESS},
{UkmTextFieldDidChangeType::kServerTypeName, NO_SERVER_DATA},
@@ -6210,7 +6565,11 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
{UkmTextFieldDidChangeType::kHtmlFieldModeName, HTML_MODE_NONE},
{UkmTextFieldDidChangeType::kIsAutofilledName, true},
{UkmTextFieldDidChangeType::kIsEmptyName, true},
- {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}});
+ {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0},
+ {UkmTextFieldDidChangeType::kFieldSignatureName,
+ Collapse(CalculateFieldSignatureForField(form.fields[1]))},
+ {UkmTextFieldDidChangeType::kFormSignatureName,
+ Collapse(CalculateFormSignature(form))}}});
}
// Verify that we correctly log metrics tracking the duration of form fill.
@@ -6220,8 +6579,7 @@ TEST_F(AutofillMetricsTest, FormFillDuration) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
test::CreateTestFormField("Name", "name", "", "text", &field);
@@ -6616,8 +6974,7 @@ TEST_F(AutofillMetricsTest, ProfileActionOnFormSubmitted) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
// Create the form's fields.
FormFieldData field;
@@ -6796,7 +7153,7 @@ TEST_F(AutofillMetricsParseQueryResponseTest, ServerHasData) {
ASSERT_TRUE(response.SerializeToString(&response_string));
base::HistogramTester histogram_tester;
- FormStructure::ParseQueryResponse(response_string, forms_);
+ FormStructure::ParseQueryResponse(response_string, forms_, nullptr);
EXPECT_THAT(
histogram_tester.GetAllSamples("Autofill.ServerResponseHasDataForForm"),
ElementsAre(Bucket(true, 2)));
@@ -6815,7 +7172,7 @@ TEST_F(AutofillMetricsParseQueryResponseTest, OneFormNoServerData) {
ASSERT_TRUE(response.SerializeToString(&response_string));
base::HistogramTester histogram_tester;
- FormStructure::ParseQueryResponse(response_string, forms_);
+ FormStructure::ParseQueryResponse(response_string, forms_, nullptr);
EXPECT_THAT(
histogram_tester.GetAllSamples("Autofill.ServerResponseHasDataForForm"),
ElementsAre(Bucket(false, 1), Bucket(true, 1)));
@@ -6833,7 +7190,7 @@ TEST_F(AutofillMetricsParseQueryResponseTest, AllFormsNoServerData) {
ASSERT_TRUE(response.SerializeToString(&response_string));
base::HistogramTester histogram_tester;
- FormStructure::ParseQueryResponse(response_string, forms_);
+ FormStructure::ParseQueryResponse(response_string, forms_, nullptr);
EXPECT_THAT(
histogram_tester.GetAllSamples("Autofill.ServerResponseHasDataForForm"),
ElementsAre(Bucket(false, 2)));
@@ -6852,7 +7209,7 @@ TEST_F(AutofillMetricsParseQueryResponseTest, PartialNoServerData) {
ASSERT_TRUE(response.SerializeToString(&response_string));
base::HistogramTester histogram_tester;
- FormStructure::ParseQueryResponse(response_string, forms_);
+ FormStructure::ParseQueryResponse(response_string, forms_, nullptr);
EXPECT_THAT(
histogram_tester.GetAllSamples("Autofill.ServerResponseHasDataForForm"),
ElementsAre(Bucket(true, 2)));
@@ -6870,9 +7227,9 @@ TEST_F(AutofillMetricsTest, NonsecureCreditCardForm) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example.com/form.html");
form.action = GURL("http://example.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root.com/form.html"));
- autofill_client_.set_form_origin(form.origin);
+ 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);
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -6893,7 +7250,8 @@ TEST_F(AutofillMetricsTest, NonsecureCreditCardForm) {
// Simulate an Autofill query on a credit card field.
{
base::UserActionTester user_action_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledCreditCardSuggestions"));
}
@@ -6950,7 +7308,8 @@ TEST_F(AutofillMetricsTest,
// Simulate an Autofill query on a credit card field.
{
base::UserActionTester user_action_tester;
- autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::RectF(), /*autoselect_first_suggestion=*/false);
EXPECT_EQ(1, user_action_tester.GetActionCount(
"Autofill_PolledCreditCardSuggestions"));
}
@@ -6978,9 +7337,11 @@ TEST_F(AutofillMetricsTest,
TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric) {
GURL url("https://www.google.com");
int upload_decision = 1;
+ autofill_client_.set_form_origin(url);
- AutofillMetrics::LogCardUploadDecisionsUkm(&test_ukm_recorder_, url,
- upload_decision);
+ AutofillMetrics::LogCardUploadDecisionsUkm(&test_ukm_recorder_,
+ autofill_client_.GetUkmSourceId(),
+ url, upload_decision);
auto entries = test_ukm_recorder_.GetEntriesByName(
UkmCardUploadDecisionType::kEntryName);
EXPECT_EQ(1u, entries.size());
@@ -6997,10 +7358,11 @@ 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);
AutofillMetrics::LogDeveloperEngagementUkm(
- &test_ukm_recorder_, url, true, {FormType::CREDIT_CARD_FORM},
- form_structure_metric, form_signature);
+ &test_ukm_recorder_, autofill_client_.GetUkmSourceId(), url, true,
+ {FormType::CREDIT_CARD_FORM}, form_structure_metric, form_signature);
auto entries = test_ukm_recorder_.GetEntriesByName(
UkmDeveloperEngagementType::kEntryName);
EXPECT_EQ(1u, entries.size());
@@ -7023,7 +7385,8 @@ TEST_F(AutofillMetricsTest, RecordDeveloperEngagementMetric) {
// Tests that no UKM is logged when the URL is not valid.
TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_InvalidUrl) {
GURL url("");
- AutofillMetrics::LogCardUploadDecisionsUkm(&test_ukm_recorder_, url, 1);
+ test_ukm_recorder_.Purge();
+ AutofillMetrics::LogCardUploadDecisionsUkm(&test_ukm_recorder_, -1, url, 1);
EXPECT_EQ(0ul, test_ukm_recorder_.sources_count());
EXPECT_EQ(0ul, test_ukm_recorder_.entries_count());
}
@@ -7031,7 +7394,8 @@ TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_InvalidUrl) {
// Tests that no UKM is logged when the ukm service is null.
TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_NoUkmService) {
GURL url("https://www.google.com");
- AutofillMetrics::LogCardUploadDecisionsUkm(nullptr, url, 1);
+ test_ukm_recorder_.Purge();
+ AutofillMetrics::LogCardUploadDecisionsUkm(nullptr, -1, url, 1);
EXPECT_EQ(0ul, test_ukm_recorder_.sources_count());
EXPECT_EQ(0ul, test_ukm_recorder_.entries_count());
}
@@ -7045,8 +7409,7 @@ TEST_F(AutofillMetricsTest, AutofillSuggestionShownTest) {
form.name = ASCIIToUTF16("TestForm");
form.origin = GURL("http://example_cc.com/form.html");
form.action = GURL("http://example_cc.com/submit.html");
- form.main_frame_origin =
- url::Origin::Create(GURL("http://example_root_cc.com/form.html"));
+ form.main_frame_origin = url::Origin::Create(autofill_client_.form_origin());
FormFieldData field;
std::vector<ServerFieldType> field_types;
@@ -7079,8 +7442,7 @@ TEST_F(AutofillMetricsTest, AutofillSuggestionShownTest) {
TEST_F(AutofillMetricsTest, DynamicFormMetrics) {
scoped_feature_list_.InitWithFeatures(
{features::kAutofillDynamicForms},
- {features::kAutofillRequireSecureCreditCardContext,
- features::kAutofillRestrictUnownedFieldsToFormlessCheckout});
+ {features::kAutofillRestrictUnownedFieldsToFormlessCheckout});
// Set up our form data.
FormData form;
@@ -7173,4 +7535,237 @@ TEST_F(AutofillMetricsTest, DynamicFormMetrics) {
AutofillMetrics::FORM_EVENT_DYNAMIC_CHANGE_AFTER_REFILL, 1);
}
+TEST_F(AutofillMetricsTest, LocallySavedCardWithFirstAndLastName) {
+ base::HistogramTester histogram_tester;
+ // Save an imported card with a full name.
+ CreditCard card_full_name("10000000-0000-0000-0000-000000000001",
+ "https://www.example.com");
+ test::SetCreditCardInfo(&card_full_name, "Biggie Smalls",
+ "4111 1111 1111 1111" /* Visa */, "01", "2999", "");
+
+ personal_data_->OnAcceptedLocalCreditCardSave(card_full_name);
+
+ // Expect that the metric was not recorded.
+ histogram_tester.ExpectTotalCount(
+ "Autofill.SaveCardWithFirstAndLastNameComplete.Local", 0);
+
+ // Save an imported card with split names with first name first.
+ CreditCard card_split_names_first("10000000-0000-0000-0000-000000000002",
+ "https://www.example.com");
+ test::SetCreditCardInfo(&card_split_names_first, /*name_on_card=*/"",
+ "4111 1111 1111 1112" /* Visa */, "01", "2998", "");
+ card_split_names_first.SetRawInfo(CREDIT_CARD_NAME_FIRST,
+ base::UTF8ToUTF16("Smally"));
+ card_split_names_first.SetRawInfo(CREDIT_CARD_NAME_LAST,
+ base::UTF8ToUTF16("Bigs"));
+
+ personal_data_->OnAcceptedLocalCreditCardSave(card_split_names_first);
+
+ // Expect that the metric was recorded.
+ histogram_tester.ExpectTotalCount(
+ "Autofill.SaveCardWithFirstAndLastNameComplete.Local", 1);
+
+ // Save an imported card with split names with last name first.
+ CreditCard card_split_names_last("10000000-0000-0000-0000-000000000002",
+ "https://www.example.com");
+ test::SetCreditCardInfo(&card_split_names_last, /*name_on_card=*/"",
+ "4111 1111 1111 1113" /* Visa */, "01", "2997", "");
+ card_split_names_last.SetRawInfo(CREDIT_CARD_NAME_LAST,
+ base::UTF8ToUTF16("Biggsy"));
+ card_split_names_last.SetRawInfo(CREDIT_CARD_NAME_FIRST,
+ base::UTF8ToUTF16("Smalls"));
+
+ personal_data_->OnAcceptedLocalCreditCardSave(card_split_names_last);
+
+ // Expect that the metric was recorded.
+ histogram_tester.ExpectTotalCount(
+ "Autofill.SaveCardWithFirstAndLastNameComplete.Local", 2);
+}
+
+// Tests that the LogUserHappinessBySecurityLevel are recorded correctly.
+TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel) {
+ {
+ base::HistogramTester histogram_tester;
+ AutofillMetrics::LogUserHappinessBySecurityLevel(
+ AutofillMetrics::USER_DID_AUTOFILL, CREDIT_CARD_FORM,
+ security_state::SecurityLevel::SECURE);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.UserHappiness.CreditCard.SECURE",
+ AutofillMetrics::USER_DID_AUTOFILL, 1);
+ }
+
+ {
+ base::HistogramTester histogram_tester;
+ AutofillMetrics::LogUserHappinessBySecurityLevel(
+ AutofillMetrics::SUGGESTIONS_SHOWN, ADDRESS_FORM,
+ security_state::SecurityLevel::DANGEROUS);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.UserHappiness.Address.DANGEROUS",
+ AutofillMetrics::SUGGESTIONS_SHOWN, 1);
+ }
+
+ {
+ base::HistogramTester histogram_tester;
+ AutofillMetrics::LogUserHappinessBySecurityLevel(
+ AutofillMetrics::FIELD_WAS_AUTOFILLED, PASSWORD_FORM,
+ security_state::SecurityLevel::HTTP_SHOW_WARNING);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.UserHappiness.Password.HTTP_SHOW_WARNING",
+ AutofillMetrics::FIELD_WAS_AUTOFILLED, 1);
+ }
+
+ {
+ base::HistogramTester histogram_tester;
+ AutofillMetrics::LogUserHappinessBySecurityLevel(
+ AutofillMetrics::USER_DID_AUTOFILL_ONCE, UNKNOWN_FORM_TYPE,
+ security_state::SecurityLevel::EV_SECURE);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.UserHappiness.Unknown.EV_SECURE",
+ AutofillMetrics::USER_DID_AUTOFILL_ONCE, 1);
+ }
+
+ {
+ // No metric should be recorded if the security level is
+ // SECURITY_LEVEL_COUNT.
+ base::HistogramTester histogram_tester;
+ AutofillMetrics::LogUserHappinessBySecurityLevel(
+ AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_SOME,
+ CREDIT_CARD_FORM, security_state::SecurityLevel::SECURITY_LEVEL_COUNT);
+ histogram_tester.ExpectTotalCount("Autofill.UserHappiness.CreditCard.OTHER",
+ 0);
+ }
+}
+
+// Verify that we correctly log LogUserHappinessBySecurityLevel dealing form the
+// form event metrics.
+TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel_FromFormEvents) {
+ // Load a fillable form.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.origin = 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());
+
+ FormFieldData field;
+ test::CreateTestFormField("Name", "name", "", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Email", "email", "", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Phone", "phone", "", "text", &field);
+ form.fields.push_back(field);
+
+ std::vector<FormData> forms(1, form);
+
+ // Simulate seeing the form.
+ {
+ base::HistogramTester histogram_tester;
+ autofill_client_.set_security_level(
+ security_state::SecurityLevel::DANGEROUS);
+ autofill_manager_->OnFormsSeen(forms, TimeTicks());
+ histogram_tester.ExpectBucketCount(
+ "Autofill.UserHappiness.Address.DANGEROUS",
+ AutofillMetrics::FORMS_LOADED, 1);
+ }
+
+ // Simulate suggestions shown twice with separate popups.
+ {
+ base::HistogramTester histogram_tester;
+ autofill_client_.set_security_level(
+ security_state::SecurityLevel::HTTP_SHOW_WARNING);
+ autofill_manager_->DidShowSuggestions(true, form, field);
+ autofill_manager_->DidShowSuggestions(true, form, field);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.UserHappiness.Address.HTTP_SHOW_WARNING",
+ AutofillMetrics::SUGGESTIONS_SHOWN, 2);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.UserHappiness.Address.HTTP_SHOW_WARNING",
+ AutofillMetrics::SUGGESTIONS_SHOWN_ONCE, 1);
+ }
+}
+
+// Tests that the LogSaveCardPromptMetricBySecurityLevel are recorded correctly.
+TEST_F(AutofillMetricsTest, LogSaveCardPromptMetricBySecurityLevel) {
+ {
+ base::HistogramTester histogram_tester;
+ AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel(
+ AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, /*is_uploading=*/true,
+ security_state::SecurityLevel::SECURE);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.SaveCreditCardPrompt.Upload.SECURE",
+ AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1);
+ }
+
+ {
+ base::HistogramTester histogram_tester;
+ AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel(
+ AutofillMetrics::SAVE_CARD_PROMPT_END_DENIED, /*is_uploading=*/false,
+ security_state::SecurityLevel::DANGEROUS);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.SaveCreditCardPrompt.Local.DANGEROUS",
+ AutofillMetrics::SAVE_CARD_PROMPT_END_DENIED, 1);
+ }
+
+ {
+ base::HistogramTester histogram_tester;
+ AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel(
+ AutofillMetrics::SAVE_CARD_PROMPT_END_ACCEPTED, /*is_uploading=*/true,
+ security_state::SecurityLevel::HTTP_SHOW_WARNING);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.SaveCreditCardPrompt.Upload.HTTP_SHOW_WARNING",
+ AutofillMetrics::SAVE_CARD_PROMPT_END_ACCEPTED, 1);
+ }
+
+ {
+ base::HistogramTester histogram_tester;
+ AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel(
+ AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING,
+ /*is_uploading=*/false, security_state::SecurityLevel::EV_SECURE);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.SaveCreditCardPrompt.Local.EV_SECURE",
+ AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, 1);
+ }
+
+ {
+ // No metric should be recorded if the security level is
+ // SECURITY_LEVEL_COUNT.
+ base::HistogramTester histogram_tester;
+ AutofillMetrics::LogSaveCardPromptMetricBySecurityLevel(
+ AutofillMetrics::SAVE_CARD_PROMPT_CVC_FIX_FLOW_SHOWN,
+ /*is_uploading=*/true,
+ security_state::SecurityLevel::SECURITY_LEVEL_COUNT);
+ histogram_tester.ExpectTotalCount(
+ "Autofill.SaveCreditCardPrompt.Upload.OTHER", 0);
+ }
+}
+
+// Verify that we correctly log LogSaveCardPromptMetricBySecurityLevel from the
+// save card prompt metrics.
+TEST_F(AutofillMetricsTest,
+ LogSaveCardPromptMetricBySecurityLevel_FromSaveCardPromptMetric) {
+ {
+ base::HistogramTester histogram_tester;
+ AutofillMetrics::LogSaveCardPromptMetric(
+ AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING,
+ /*is_uploading=*/true, /*is_reshow=*/false,
+ /*is_requesting_cardholder_name=*/false,
+ /*previous_save_credit_card_prompt_user_decision=*/1,
+ security_state::SecurityLevel::EV_SECURE);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.SaveCreditCardPrompt.Upload.EV_SECURE",
+ AutofillMetrics::SAVE_CARD_PROMPT_END_NAVIGATION_SHOWING, 1);
+ }
+
+ {
+ base::HistogramTester histogram_tester;
+ AutofillMetrics::LogSaveCardPromptMetric(
+ AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, /*is_uploading=*/false,
+ /*is_reshow=*/true, /*is_requesting_cardholder_name=*/false,
+ /*previous_save_credit_card_prompt_user_decision=*/0,
+ security_state::SecurityLevel::SECURE);
+ histogram_tester.ExpectBucketCount(
+ "Autofill.SaveCreditCardPrompt.Local.SECURE",
+ AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1);
+ }
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_profile.cc b/chromium/components/autofill/core/browser/autofill_profile.cc
index 2154fa60b7c..ede6790f6f2 100644
--- a/chromium/components/autofill/core/browser/autofill_profile.cc
+++ b/chromium/components/autofill/core/browser/autofill_profile.cc
@@ -255,14 +255,14 @@ AutofillProfile::~AutofillProfile() {
}
AutofillProfile& AutofillProfile::operator=(const AutofillProfile& profile) {
+ if (this == &profile)
+ return *this;
+
set_use_count(profile.use_count());
set_use_date(profile.use_date());
set_previous_use_date(profile.previous_use_date());
set_modification_date(profile.modification_date());
- if (this == &profile)
- return *this;
-
set_guid(profile.guid());
set_origin(profile.origin());
@@ -404,6 +404,12 @@ bool AutofillProfile::EqualsForSyncPurposes(const AutofillProfile& profile)
EqualsSansGuid(profile);
}
+bool AutofillProfile::EqualsIncludingUsageStatsForTesting(
+ const AutofillProfile& profile) const {
+ return use_count() == profile.use_count() &&
+ use_date() == profile.use_date() && *this == profile;
+}
+
bool AutofillProfile::operator==(const AutofillProfile& profile) const {
return guid() == profile.guid() && EqualsSansGuid(profile);
}
@@ -460,6 +466,30 @@ bool AutofillProfile::IsSubsetOfForFieldSet(
return true;
}
+void AutofillProfile::OverwriteDataFrom(const AutofillProfile& profile) {
+ // Verified profiles should never be overwritten with unverified data.
+ DCHECK(!IsVerified() || profile.IsVerified());
+ DCHECK_EQ(guid(), profile.guid());
+
+ // Some fields should not got overwritten by empty values; back-up the
+ // values.
+ std::string language_code_value = language_code();
+ std::string origin_value = origin();
+ int validity_bitfield_value = GetValidityBitfieldValue();
+ base::string16 name_full_value = GetRawInfo(NAME_FULL);
+
+ *this = profile;
+
+ if (origin().empty())
+ set_origin(origin_value);
+ if (language_code().empty())
+ set_language_code(language_code_value);
+ if (GetValidityBitfieldValue() == 0)
+ SetValidityFromBitfieldValue(validity_bitfield_value);
+ if (!HasRawInfo(NAME_FULL))
+ SetRawInfo(NAME_FULL, name_full_value);
+}
+
bool AutofillProfile::MergeDataFrom(const AutofillProfile& profile,
const std::string& app_locale) {
// Verified profiles should never be overwritten with unverified data.
@@ -960,9 +990,9 @@ bool AutofillProfile::EqualsSansGuid(const AutofillProfile& profile) const {
Compare(profile) == 0;
}
-// So we can compare AutofillProfiles with EXPECT_EQ().
std::ostream& operator<<(std::ostream& os, const AutofillProfile& profile) {
return os << profile.guid() << " " << profile.origin() << " "
+ << UTF16ToUTF8(profile.GetRawInfo(NAME_FULL)) << " "
<< UTF16ToUTF8(profile.GetRawInfo(NAME_FIRST)) << " "
<< UTF16ToUTF8(profile.GetRawInfo(NAME_MIDDLE)) << " "
<< UTF16ToUTF8(profile.GetRawInfo(NAME_LAST)) << " "
@@ -970,6 +1000,7 @@ std::ostream& operator<<(std::ostream& os, const AutofillProfile& profile) {
<< UTF16ToUTF8(profile.GetRawInfo(COMPANY_NAME)) << " "
<< UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_LINE1)) << " "
<< UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_LINE2)) << " "
+ << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_LINE3)) << " "
<< UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY))
<< " " << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_CITY)) << " "
<< UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_STATE)) << " "
@@ -978,7 +1009,8 @@ std::ostream& operator<<(std::ostream& os, const AutofillProfile& profile) {
<< UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY)) << " "
<< profile.language_code() << " "
<< UTF16ToUTF8(profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER)) << " "
- << profile.GetValidityBitfieldValue();
+ << profile.GetValidityBitfieldValue() << " " << profile.use_count()
+ << " " << profile.use_date();
}
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_profile.h b/chromium/components/autofill/core/browser/autofill_profile.h
index c097d14cdcc..0353a1529c0 100644
--- a/chromium/components/autofill/core/browser/autofill_profile.h
+++ b/chromium/components/autofill/core/browser/autofill_profile.h
@@ -99,9 +99,13 @@ class AutofillProfile : public AutofillDataModel,
bool EqualsSansOrigin(const AutofillProfile& profile) const;
// Same as operator==, but ignores differences in guid and cares about
- // differences in usage stats and validity state.
+ // differences in usage stats.
bool EqualsForSyncPurposes(const AutofillProfile& profile) const;
+ // Same as operator==, but cares about differences in usage stats.
+ bool EqualsIncludingUsageStatsForTesting(
+ const AutofillProfile& profile) const;
+
// Equality operators compare GUIDs, origins, language code, and the contents
// in the comparison. Usage metadata (use count, use date, modification date)
// are NOT compared.
@@ -118,6 +122,10 @@ class AutofillProfile : public AutofillDataModel,
const std::string& app_locale,
const ServerFieldTypeSet& types) const;
+ // Overwrites the data of |this| profile with data from the given |profile|.
+ // Expects that the profiles have the same guid.
+ void OverwriteDataFrom(const AutofillProfile& profile);
+
// Merges the data from |this| profile and the given |profile| into |this|
// profile. Expects that |this| and |profile| have already been deemed
// mergeable by an AutofillProfileComparator.
diff --git a/chromium/components/autofill/core/browser/autofill_profile_sync_util.cc b/chromium/components/autofill/core/browser/autofill_profile_sync_util.cc
new file mode 100644
index 00000000000..f81dc6f0315
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_profile_sync_util.cc
@@ -0,0 +1,235 @@
+// 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/autofill_profile_sync_util.h"
+
+#include "base/guid.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/country_names.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/proto/autofill_sync.pb.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/sync/model/entity_data.h"
+
+using base::UTF16ToUTF8;
+using base::UTF8ToUTF16;
+using sync_pb::AutofillProfileSpecifics;
+using syncer::EntityData;
+
+namespace autofill {
+namespace {
+
+std::string TruncateUTF8(const std::string& data) {
+ std::string trimmed_value;
+ base::TruncateUTF8ToByteSize(data, AutofillTable::kMaxDataLength,
+ &trimmed_value);
+ return trimmed_value;
+}
+
+bool IsAutofillProfileSpecificsValid(
+ const AutofillProfileSpecifics& specifics) {
+ return base::IsValidGUID(specifics.guid());
+}
+
+} // namespace
+
+std::unique_ptr<EntityData> CreateEntityDataFromAutofillProfile(
+ const AutofillProfile& entry) {
+ // Validity of the guid is guaranteed by the database layer.
+ DCHECK(base::IsValidGUID(entry.guid()));
+
+ auto entity_data = std::make_unique<EntityData>();
+ entity_data->non_unique_name = entry.guid();
+ AutofillProfileSpecifics* specifics =
+ entity_data->specifics.mutable_autofill_profile();
+
+ specifics->set_guid(entry.guid());
+ specifics->set_origin(entry.origin());
+
+ specifics->set_use_count(entry.use_count());
+ specifics->set_use_date(entry.use_date().ToTimeT());
+ specifics->set_address_home_language_code(
+ TruncateUTF8(entry.language_code()));
+ specifics->set_validity_state_bitfield(entry.GetValidityBitfieldValue());
+
+ // Set repeated fields.
+ if (entry.HasRawInfo(NAME_FIRST)) {
+ specifics->add_name_first(
+ TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(NAME_FIRST))));
+ }
+ if (entry.HasRawInfo(NAME_MIDDLE)) {
+ specifics->add_name_middle(
+ TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(NAME_MIDDLE))));
+ }
+ if (entry.HasRawInfo(NAME_LAST)) {
+ specifics->add_name_last(
+ TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(NAME_LAST))));
+ }
+ if (entry.HasRawInfo(NAME_FULL)) {
+ specifics->add_name_full(
+ TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(NAME_FULL))));
+ }
+ if (entry.HasRawInfo(EMAIL_ADDRESS)) {
+ specifics->add_email_address(
+ TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(EMAIL_ADDRESS))));
+ }
+ if (entry.HasRawInfo(PHONE_HOME_WHOLE_NUMBER)) {
+ specifics->add_phone_home_whole_number(
+ TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(PHONE_HOME_WHOLE_NUMBER))));
+ }
+
+ // Set simple single-valued fields.
+ if (entry.HasRawInfo(COMPANY_NAME)) {
+ specifics->set_company_name(
+ TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(COMPANY_NAME))));
+ }
+ if (entry.HasRawInfo(ADDRESS_HOME_CITY)) {
+ specifics->set_address_home_city(
+ TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(ADDRESS_HOME_CITY))));
+ }
+ if (entry.HasRawInfo(ADDRESS_HOME_STATE)) {
+ specifics->set_address_home_state(
+ TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(ADDRESS_HOME_STATE))));
+ }
+ if (entry.HasRawInfo(ADDRESS_HOME_ZIP)) {
+ specifics->set_address_home_zip(
+ TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(ADDRESS_HOME_ZIP))));
+ }
+ if (entry.HasRawInfo(ADDRESS_HOME_SORTING_CODE)) {
+ specifics->set_address_home_sorting_code(
+ TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(ADDRESS_HOME_SORTING_CODE))));
+ }
+ if (entry.HasRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY)) {
+ specifics->set_address_home_dependent_locality(TruncateUTF8(
+ UTF16ToUTF8(entry.GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY))));
+ }
+ if (entry.HasRawInfo(ADDRESS_HOME_COUNTRY)) {
+ specifics->set_address_home_country(
+ TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(ADDRESS_HOME_COUNTRY))));
+ }
+ if (entry.HasRawInfo(ADDRESS_HOME_STREET_ADDRESS)) {
+ specifics->set_address_home_street_address(TruncateUTF8(
+ UTF16ToUTF8(entry.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS))));
+ }
+ if (entry.HasRawInfo(ADDRESS_HOME_LINE1)) {
+ specifics->set_address_home_line1(
+ TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(ADDRESS_HOME_LINE1))));
+ }
+ if (entry.HasRawInfo(ADDRESS_HOME_LINE2)) {
+ specifics->set_address_home_line2(
+ TruncateUTF8(UTF16ToUTF8(entry.GetRawInfo(ADDRESS_HOME_LINE2))));
+ }
+
+ return entity_data;
+}
+
+std::unique_ptr<AutofillProfile> CreateAutofillProfileFromSpecifics(
+ const AutofillProfileSpecifics& specifics) {
+ if (!IsAutofillProfileSpecificsValid(specifics)) {
+ return nullptr;
+ }
+ std::unique_ptr<AutofillProfile> profile =
+ std::make_unique<AutofillProfile>(specifics.guid(), specifics.origin());
+
+ // Set info that has a default value (and does not distinguish whether it is
+ // set or not).
+ profile->set_use_count(specifics.use_count());
+ profile->set_use_date(base::Time::FromTimeT(specifics.use_date()));
+ profile->set_language_code(specifics.address_home_language_code());
+ profile->SetValidityFromBitfieldValue(specifics.validity_state_bitfield());
+
+ // Set repeated fields.
+ if (specifics.name_first_size() > 0) {
+ profile->SetRawInfo(NAME_FIRST, UTF8ToUTF16(specifics.name_first(0)));
+ }
+ if (specifics.name_middle_size() > 0) {
+ profile->SetRawInfo(NAME_MIDDLE, UTF8ToUTF16(specifics.name_middle(0)));
+ }
+ if (specifics.name_last_size() > 0) {
+ profile->SetRawInfo(NAME_LAST, UTF8ToUTF16(specifics.name_last(0)));
+ }
+ if (specifics.name_full_size() > 0) {
+ profile->SetRawInfo(NAME_FULL, UTF8ToUTF16(specifics.name_full(0)));
+ }
+ if (specifics.email_address_size() > 0) {
+ profile->SetRawInfo(EMAIL_ADDRESS, UTF8ToUTF16(specifics.email_address(0)));
+ }
+ if (specifics.phone_home_whole_number_size() > 0) {
+ profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER,
+ UTF8ToUTF16(specifics.phone_home_whole_number(0)));
+ }
+
+ // Set simple single-valued fields.
+ if (specifics.has_company_name()) {
+ profile->SetRawInfo(COMPANY_NAME, UTF8ToUTF16(specifics.company_name()));
+ }
+ if (specifics.has_address_home_city()) {
+ profile->SetRawInfo(ADDRESS_HOME_CITY,
+ UTF8ToUTF16(specifics.address_home_city()));
+ }
+ if (specifics.has_address_home_state()) {
+ profile->SetRawInfo(ADDRESS_HOME_STATE,
+ UTF8ToUTF16(specifics.address_home_state()));
+ }
+ if (specifics.has_address_home_zip()) {
+ profile->SetRawInfo(ADDRESS_HOME_ZIP,
+ UTF8ToUTF16(specifics.address_home_zip()));
+ }
+ if (specifics.has_address_home_sorting_code()) {
+ profile->SetRawInfo(ADDRESS_HOME_SORTING_CODE,
+ UTF8ToUTF16(specifics.address_home_sorting_code()));
+ }
+ if (specifics.has_address_home_dependent_locality()) {
+ profile->SetRawInfo(
+ ADDRESS_HOME_DEPENDENT_LOCALITY,
+ UTF8ToUTF16(specifics.address_home_dependent_locality()));
+ }
+ if (specifics.has_address_home_country()) {
+ // Update the country field, which can contain either a country code (if set
+ // by a newer version of Chrome), or a country name (if set by an older
+ // version of Chrome).
+ // TODO(jkrcal): Move this migration logic into Address::SetRawInfo()?
+ base::string16 country_name_or_code =
+ base::ASCIIToUTF16(specifics.address_home_country());
+ std::string country_code =
+ CountryNames::GetInstance()->GetCountryCode(country_name_or_code);
+ profile->SetRawInfo(ADDRESS_HOME_COUNTRY, UTF8ToUTF16(country_code));
+ }
+ if (specifics.has_address_home_line1()) {
+ profile->SetRawInfo(ADDRESS_HOME_LINE1,
+ UTF8ToUTF16(specifics.address_home_line1()));
+ }
+ if (specifics.has_address_home_line2()) {
+ profile->SetRawInfo(ADDRESS_HOME_LINE2,
+ UTF8ToUTF16(specifics.address_home_line2()));
+ }
+ // Set first the deprecated subparts (line1 & line2) and only after that the
+ // full address (street_address) so that the latter wins in case of conflict.
+ // This is needed because all the address fields are backed by the same
+ // storage.
+ if (specifics.has_address_home_street_address()) {
+ profile->SetRawInfo(ADDRESS_HOME_STREET_ADDRESS,
+ UTF8ToUTF16(specifics.address_home_street_address()));
+ }
+
+ return profile;
+}
+
+std::string GetStorageKeyFromAutofillProfile(const AutofillProfile& entry) {
+ // Validity of the guid is guaranteed by the database layer.
+ DCHECK(base::IsValidGUID(entry.guid()));
+ return entry.guid();
+}
+
+std::string GetStorageKeyFromAutofillProfileSpecifics(
+ const AutofillProfileSpecifics& specifics) {
+ if (!IsAutofillProfileSpecificsValid(specifics)) {
+ return std::string();
+ }
+ return specifics.guid();
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_profile_sync_util.h b/chromium/components/autofill/core/browser/autofill_profile_sync_util.h
new file mode 100644
index 00000000000..a43595262bf
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_profile_sync_util.h
@@ -0,0 +1,45 @@
+// 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_AUTOFILL_PROFILE_SYNC_UTIL_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_PROFILE_SYNC_UTIL_H_
+
+#include <memory>
+#include <string>
+
+namespace syncer {
+struct EntityData;
+} // namespace syncer
+
+namespace sync_pb {
+class AutofillProfileSpecifics;
+} // namespace sync_pb
+
+namespace autofill {
+
+class AutofillProfile;
+
+// Converts the given |entry| into a syncer EntityData with equivalent
+// autofill profile specifics. Returns nullptr if |entry| is invalid.
+// Shortens all string fields to AutofillTable::kMaxDataLength.
+std::unique_ptr<syncer::EntityData> CreateEntityDataFromAutofillProfile(
+ const AutofillProfile& entry);
+
+// Converts the given autofill profile |specifics| into an equivalent
+// AutofillProfile. Returns nullptr if |specifics| is invalid.
+std::unique_ptr<AutofillProfile> CreateAutofillProfileFromSpecifics(
+ const sync_pb::AutofillProfileSpecifics& specifics);
+
+// Returns the storage key for given |entry|, to be used for storing in the
+// database. Returns an empty string if |entry| is invalid.
+std::string GetStorageKeyFromAutofillProfile(const AutofillProfile& entry);
+
+// Returns the storage key for given |specifics|, to be used for storing in the
+// database. Returns an empty string if |entry| is invalid.
+std::string GetStorageKeyFromAutofillProfileSpecifics(
+ const sync_pb::AutofillProfileSpecifics& specifics);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_PROFILE_SYNC_UTIL_H_
diff --git a/chromium/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc b/chromium/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc
new file mode 100644
index 00000000000..9204eca55cf
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc
@@ -0,0 +1,289 @@
+// 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/autofill_profile_sync_util.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/country_names.h"
+#include "components/autofill/core/browser/test_autofill_clock.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/autofill/core/common/autofill_constants.h"
+#include "components/sync/model/entity_data.h"
+#include "components/sync/protocol/sync.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+namespace {
+using base::ASCIIToUTF16;
+using base::UTF16ToUTF8;
+using base::UTF8ToUTF16;
+using sync_pb::AutofillProfileSpecifics;
+using syncer::EntityData;
+
+// Some guids for testing.
+const char kGuid[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44A";
+const char kGuidInvalid[] = "EDC609ED";
+
+const char kLocaleString[] = "en-US";
+const base::Time kJune2017 = base::Time::FromDoubleT(1497552271);
+
+// Returns a profile with all fields set. Contains identical data to the data
+// returned from ConstructCompleteSpecifics().
+AutofillProfile ConstructCompleteProfile() {
+ AutofillProfile profile(kGuid, "https://www.example.com/");
+
+ profile.set_use_count(7);
+ profile.set_use_date(base::Time::FromTimeT(1423182152));
+
+ profile.SetRawInfo(NAME_FULL, ASCIIToUTF16("John K. Doe, Jr."));
+ profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("K."));
+ profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Doe"));
+
+ profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("user@example.com"));
+ profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("1.800.555.1234"));
+
+ profile.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("123 Fake St.\n"
+ "Apt. 42"));
+ EXPECT_EQ(ASCIIToUTF16("123 Fake St."),
+ profile.GetRawInfo(ADDRESS_HOME_LINE1));
+ EXPECT_EQ(ASCIIToUTF16("Apt. 42"), profile.GetRawInfo(ADDRESS_HOME_LINE2));
+
+ profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Google, Inc."));
+ profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Mountain View"));
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("California"));
+ profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("94043"));
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+ profile.SetRawInfo(ADDRESS_HOME_SORTING_CODE, ASCIIToUTF16("CEDEX"));
+ profile.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY,
+ ASCIIToUTF16("Santa Clara"));
+ profile.set_language_code("en");
+ profile.SetValidityFromBitfieldValue(1984);
+ return profile;
+}
+
+// Returns AutofillProfileSpecifics with all Autofill profile fields set.
+// Contains identical data to the data returned from ConstructCompleteProfile().
+AutofillProfileSpecifics ConstructCompleteSpecifics() {
+ AutofillProfileSpecifics specifics;
+
+ specifics.set_guid(kGuid);
+ specifics.set_origin("https://www.example.com/");
+ specifics.set_use_count(7);
+ specifics.set_use_date(1423182152);
+
+ specifics.add_name_first("John");
+ specifics.add_name_middle("K.");
+ specifics.add_name_last("Doe");
+ specifics.add_name_full("John K. Doe, Jr.");
+
+ specifics.add_email_address("user@example.com");
+
+ specifics.add_phone_home_whole_number("1.800.555.1234");
+
+ specifics.set_address_home_line1("123 Fake St.");
+ specifics.set_address_home_line2("Apt. 42");
+ specifics.set_address_home_street_address(
+ "123 Fake St.\n"
+ "Apt. 42");
+
+ specifics.set_company_name("Google, Inc.");
+ specifics.set_address_home_city("Mountain View");
+ specifics.set_address_home_state("California");
+ specifics.set_address_home_zip("94043");
+ specifics.set_address_home_country("US");
+ specifics.set_address_home_sorting_code("CEDEX");
+ specifics.set_address_home_dependent_locality("Santa Clara");
+ specifics.set_address_home_language_code("en");
+ specifics.set_validity_state_bitfield(1984);
+
+ return specifics;
+}
+
+class AutofillProfileSyncUtilTest : public testing::Test {
+ public:
+ AutofillProfileSyncUtilTest() {
+ // Fix a time for implicitly constructed use_dates in AutofillProfile.
+ test_clock_.SetNow(kJune2017);
+ CountryNames::SetLocaleString(kLocaleString);
+ }
+
+ private:
+ autofill::TestAutofillClock test_clock_;
+};
+
+// Ensure that all profile fields are able to be synced up from the client to
+// the server.
+TEST_F(AutofillProfileSyncUtilTest, CreateEntityDataFromAutofillProfile) {
+ AutofillProfile profile = ConstructCompleteProfile();
+ AutofillProfileSpecifics specifics = ConstructCompleteSpecifics();
+
+ std::unique_ptr<EntityData> entity_data =
+ CreateEntityDataFromAutofillProfile(profile);
+ // The non-unique name should be set to the guid of the profile.
+ EXPECT_EQ(entity_data->non_unique_name, profile.guid());
+
+ EXPECT_EQ(specifics.SerializeAsString(),
+ entity_data->specifics.autofill_profile().SerializeAsString());
+}
+
+// Test that fields not set for the input are also not set on the output.
+TEST_F(AutofillProfileSyncUtilTest, CreateEntityDataFromAutofillProfile_Empty) {
+ AutofillProfile profile(kGuid, std::string());
+ ASSERT_FALSE(profile.HasRawInfo(NAME_FULL));
+ ASSERT_FALSE(profile.HasRawInfo(COMPANY_NAME));
+
+ std::unique_ptr<EntityData> entity_data =
+ CreateEntityDataFromAutofillProfile(profile);
+ EXPECT_EQ(0, entity_data->specifics.autofill_profile().name_full_size());
+ EXPECT_FALSE(entity_data->specifics.autofill_profile().has_company_name());
+}
+
+// Test that long fields get trimmed.
+TEST_F(AutofillProfileSyncUtilTest,
+ CreateEntityDataFromAutofillProfile_Trimmed) {
+ std::string kNameLong(AutofillTable::kMaxDataLength + 1, 'a');
+ std::string kNameTrimmed(AutofillTable::kMaxDataLength, 'a');
+
+ AutofillProfile profile(kGuid, std::string());
+ profile.SetRawInfo(NAME_FULL, ASCIIToUTF16(kNameLong));
+
+ std::unique_ptr<EntityData> entity_data =
+ CreateEntityDataFromAutofillProfile(profile);
+
+ EXPECT_EQ(kNameTrimmed,
+ entity_data->specifics.autofill_profile().name_full(0));
+}
+
+// Test that long non-ascii fields get correctly trimmed.
+TEST_F(AutofillProfileSyncUtilTest,
+ CreateEntityDataFromAutofillProfile_TrimmedNonASCII) {
+ // Make the UTF8 string have odd number of bytes and append many 2-bytes
+ // characters so that simple ASCII trimming would make the UTF8 string
+ // invalid.
+ std::string kNameLong("aä");
+ std::string kNameTrimmed("a");
+
+ for (unsigned int i = 0; i < AutofillTable::kMaxDataLength / 2 - 1; ++i) {
+ kNameLong += "ä";
+ kNameTrimmed += "ä";
+ }
+
+ AutofillProfile profile(kGuid, std::string());
+ profile.SetRawInfo(NAME_FULL, UTF8ToUTF16(kNameLong));
+
+ std::unique_ptr<EntityData> entity_data =
+ CreateEntityDataFromAutofillProfile(profile);
+
+ EXPECT_EQ(kNameTrimmed,
+ entity_data->specifics.autofill_profile().name_full(0));
+}
+
+// Ensure that all profile fields are able to be synced down from the server to
+// the client (and nothing gets uploaded back).
+TEST_F(AutofillProfileSyncUtilTest, CreateAutofillProfileFromSpecifics) {
+ // Fix a time for implicitly constructed use_dates in AutofillProfile.
+ autofill::TestAutofillClock test_clock;
+ test_clock.SetNow(kJune2017);
+ CountryNames::SetLocaleString(kLocaleString);
+
+ AutofillProfileSpecifics specifics = ConstructCompleteSpecifics();
+ AutofillProfile profile = ConstructCompleteProfile();
+
+ std::unique_ptr<AutofillProfile> converted_profile =
+ CreateAutofillProfileFromSpecifics(specifics);
+ EXPECT_TRUE(profile.EqualsIncludingUsageStatsForTesting(*converted_profile));
+}
+
+// Test that fields not set for the input are also not set on the output.
+TEST_F(AutofillProfileSyncUtilTest, CreateAutofillProfileFromSpecifics_Empty) {
+ AutofillProfileSpecifics specifics;
+ specifics.set_guid(kGuid);
+
+ std::unique_ptr<AutofillProfile> profile =
+ CreateAutofillProfileFromSpecifics(specifics);
+
+ EXPECT_FALSE(profile->HasRawInfo(NAME_FULL));
+ EXPECT_FALSE(profile->HasRawInfo(COMPANY_NAME));
+}
+
+// Test that nullptr is produced if the input guid is invalid.
+TEST_F(AutofillProfileSyncUtilTest,
+ CreateAutofillProfileFromSpecifics_Invalid) {
+ AutofillProfileSpecifics specifics;
+ specifics.set_guid(kGuidInvalid);
+
+ EXPECT_EQ(nullptr, CreateAutofillProfileFromSpecifics(specifics));
+}
+
+// Test that if conflicting info is set for address home, the (deprecated) line1
+// & line2 fields get overwritten by the street_address field.
+TEST_F(AutofillProfileSyncUtilTest,
+ CreateAutofillProfileFromSpecifics_HomeAddressWins) {
+ AutofillProfileSpecifics specifics;
+ specifics.set_guid(kGuid);
+
+ specifics.set_address_home_street_address(
+ "123 New St.\n"
+ "Apt. 42");
+ specifics.set_address_home_line1("456 Old St.");
+ specifics.set_address_home_line2("Apt. 43");
+
+ std::unique_ptr<AutofillProfile> profile =
+ CreateAutofillProfileFromSpecifics(specifics);
+
+ EXPECT_EQ("123 New St.",
+ UTF16ToUTF8(profile->GetRawInfo(ADDRESS_HOME_LINE1)));
+ EXPECT_EQ("Apt. 42", UTF16ToUTF8(profile->GetRawInfo(ADDRESS_HOME_LINE2)));
+}
+
+// Test that country names (used in the past for the field) get correctly parsed
+// into country code.
+TEST_F(AutofillProfileSyncUtilTest,
+ CreateAutofillProfileFromSpecifics_CountryNameParsed) {
+ CountryNames::SetLocaleString(kLocaleString);
+
+ AutofillProfileSpecifics specifics;
+ specifics.set_guid(kGuid);
+
+ specifics.set_address_home_country("Germany");
+ EXPECT_EQ("DE", UTF16ToUTF8(
+ CreateAutofillProfileFromSpecifics(specifics)->GetRawInfo(
+ ADDRESS_HOME_COUNTRY)));
+
+ specifics.set_address_home_country("united states");
+ EXPECT_EQ("US", UTF16ToUTF8(
+ CreateAutofillProfileFromSpecifics(specifics)->GetRawInfo(
+ ADDRESS_HOME_COUNTRY)));
+}
+
+// Tests that guid is returned as storage key.
+TEST_F(AutofillProfileSyncUtilTest, GetStorageKeyFromAutofillProfile) {
+ AutofillProfile profile(kGuid, std::string());
+
+ EXPECT_EQ(kGuid, GetStorageKeyFromAutofillProfile(profile));
+}
+
+// Tests that guid is returned as storage key.
+TEST_F(AutofillProfileSyncUtilTest, GetStorageKeyFromAutofillProfileSpecifics) {
+ AutofillProfileSpecifics specifics;
+ specifics.set_guid(kGuid);
+
+ EXPECT_EQ(kGuid, GetStorageKeyFromAutofillProfileSpecifics(specifics));
+}
+
+// Tests that empty string is returned for entry with invalid guid.
+TEST_F(AutofillProfileSyncUtilTest,
+ GetStorageKeyFromAutofillProfileSpecifics_Invalid) {
+ AutofillProfileSpecifics specifics;
+ specifics.set_guid(kGuidInvalid);
+
+ EXPECT_EQ(std::string(),
+ GetStorageKeyFromAutofillProfileSpecifics(specifics));
+}
+
+} // namespace
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_provider.h b/chromium/components/autofill/core/browser/autofill_provider.h
index 002b7051723..359c3b62995 100644
--- a/chromium/components/autofill/core/browser/autofill_provider.h
+++ b/chromium/components/autofill/core/browser/autofill_provider.h
@@ -28,7 +28,8 @@ class AutofillProvider {
int32_t id,
const FormData& form,
const FormFieldData& field,
- const gfx::RectF& bounding_box) = 0;
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion) = 0;
virtual void OnTextFieldDidChange(AutofillHandlerProxy* handler,
const FormData& form,
diff --git a/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.cc b/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.cc
index 9b91b1dc09d..4f022dd6432 100644
--- a/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.cc
+++ b/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.cc
@@ -30,16 +30,25 @@ AutofillSaveCardInfoBarDelegateMobile::AutofillSaveCardInfoBarDelegateMobile(
bool upload,
const CreditCard& card,
std::unique_ptr<base::DictionaryValue> legal_message,
- const base::Closure& save_card_callback,
+ base::OnceCallback<void(const base::string16&)> upload_save_card_callback,
+ base::Closure local_save_card_callback,
PrefService* pref_service)
: ConfirmInfoBarDelegate(),
upload_(upload),
- save_card_callback_(save_card_callback),
+ upload_save_card_callback_(std::move(upload_save_card_callback)),
+ local_save_card_callback_(local_save_card_callback),
pref_service_(pref_service),
had_user_interaction_(false),
issuer_icon_id_(CreditCard::IconResourceId(card.network())),
card_label_(card.NetworkAndLastFourDigits()),
card_sub_label_(card.AbbreviatedExpirationDateForDisplay()) {
+ if (upload) {
+ DCHECK(!upload_save_card_callback_.is_null());
+ DCHECK(local_save_card_callback_.is_null());
+ } else {
+ DCHECK(upload_save_card_callback_.is_null());
+ DCHECK(!local_save_card_callback_.is_null());
+ }
if (legal_message) {
if (!LegalMessageLine::Parse(*legal_message, &legal_messages_,
/*escape_apostrophes=*/true)) {
@@ -138,8 +147,12 @@ base::string16 AutofillSaveCardInfoBarDelegateMobile::GetButtonLabel(
}
bool AutofillSaveCardInfoBarDelegateMobile::Accept() {
- save_card_callback_.Run();
- save_card_callback_.Reset();
+ if (upload_) {
+ std::move(upload_save_card_callback_).Run(base::string16());
+ } else {
+ local_save_card_callback_.Run();
+ local_save_card_callback_.Reset();
+ }
LogUserAction(AutofillMetrics::INFOBAR_ACCEPTED);
return true;
}
diff --git a/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.h b/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.h
index 42b3b4e0a4c..4e0dfcebea0 100644
--- a/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.h
+++ b/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.h
@@ -32,7 +32,8 @@ class AutofillSaveCardInfoBarDelegateMobile : public ConfirmInfoBarDelegate {
bool upload,
const CreditCard& card,
std::unique_ptr<base::DictionaryValue> legal_message,
- const base::Closure& save_card_callback,
+ base::OnceCallback<void(const base::string16&)> upload_save_card_callback,
+ base::Closure local_save_card_callback,
PrefService* pref_service);
~AutofillSaveCardInfoBarDelegateMobile() override;
@@ -72,8 +73,13 @@ class AutofillSaveCardInfoBarDelegateMobile : public ConfirmInfoBarDelegate {
// Whether the action is an upload or a local save.
bool upload_;
- // The callback to save credit card if the user accepts the infobar.
- base::Closure save_card_callback_;
+ // The callback to save the credit card to Google Payments if |upload_| is
+ // true and the user accepts the infobar.
+ base::OnceCallback<void(const base::string16&)> upload_save_card_callback_;
+
+ // The callback to save the credit card locally to the device if |upload_| is
+ // false and the user accepts the infobar.
+ base::Closure local_save_card_callback_;
// Weak reference to read & write |kAutofillAcceptSaveCreditCardPromptState|,
PrefService* pref_service_;
diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.cc b/chromium/components/autofill/core/browser/autofill_test_utils.cc
index f3bac64510f..0140cc599e2 100644
--- a/chromium/components/autofill/core/browser/autofill_test_utils.cc
+++ b/chromium/components/autofill/core/browser/autofill_test_utils.cc
@@ -10,6 +10,7 @@
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/autofill_external_delegate.h"
#include "components/autofill/core/browser/autofill_manager.h"
@@ -193,6 +194,54 @@ void CreateTestPersonalInformationFormData(FormData* form) {
form->fields.push_back(field);
}
+void CreateTestCreditCardFormData(FormData* form,
+ bool is_https,
+ bool use_month_type,
+ bool split_names) {
+ form->name = ASCIIToUTF16("MyForm");
+ if (is_https) {
+ form->origin = GURL("https://myform.com/form.html");
+ form->action = GURL("https://myform.com/submit.html");
+ form->main_frame_origin =
+ url::Origin::Create(GURL("https://myform_root.com/form.html"));
+ } else {
+ form->origin = GURL("http://myform.com/form.html");
+ form->action = GURL("http://myform.com/submit.html");
+ form->main_frame_origin =
+ url::Origin::Create(GURL("http://myform_root.com/form.html"));
+ }
+
+ FormFieldData field;
+ if (split_names) {
+ test::CreateTestFormField("First Name on Card", "firstnameoncard", "",
+ "text", &field);
+ field.autocomplete_attribute = "cc-given-name";
+ form->fields.push_back(field);
+ test::CreateTestFormField("Last Name on Card", "lastnameoncard", "", "text",
+ &field);
+ field.autocomplete_attribute = "cc-family-name";
+ form->fields.push_back(field);
+ field.autocomplete_attribute = "";
+ } else {
+ 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);
+ if (use_month_type) {
+ test::CreateTestFormField("Expiration Date", "ccmonth", "", "month",
+ &field);
+ form->fields.push_back(field);
+ } else {
+ test::CreateTestFormField("Expiration Date", "ccmonth", "", "text", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("", "ccyear", "", "text", &field);
+ form->fields.push_back(field);
+ }
+ test::CreateTestFormField("CVC", "cvc", "", "text", &field);
+ form->fields.push_back(field);
+}
+
inline void check_and_set(
FormGroup* profile, ServerFieldType type, const char* value) {
if (value)
@@ -506,7 +555,8 @@ void GenerateTestAutofillPopup(
std::vector<Suggestion> suggestions;
suggestions.push_back(Suggestion(base::ASCIIToUTF16("Test suggestion")));
- autofill_external_delegate->OnSuggestionsReturned(query_id, suggestions);
+ autofill_external_delegate->OnSuggestionsReturned(
+ query_id, suggestions, /*autoselect_first_suggestion=*/false);
}
std::string ObfuscatedCardDigitsAsUTF8(const std::string& str) {
@@ -514,5 +564,16 @@ std::string ObfuscatedCardDigitsAsUTF8(const std::string& str) {
internal::GetObfuscatedStringForCardDigits(base::ASCIIToUTF16(str)));
}
+std::string NextYear() {
+ base::Time::Exploded now;
+ base::Time::Now().LocalExplode(&now);
+ return std::to_string(now.year + 1);
+}
+std::string LastYear() {
+ base::Time::Exploded now;
+ base::Time::Now().LocalExplode(&now);
+ return std::to_string(now.year - 1);
+}
+
} // namespace test
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.h b/chromium/components/autofill/core/browser/autofill_test_utils.h
index 80af947d034..38077c09d6c 100644
--- a/chromium/components/autofill/core/browser/autofill_test_utils.h
+++ b/chromium/components/autofill/core/browser/autofill_test_utils.h
@@ -72,6 +72,14 @@ void CreateTestAddressFormData(FormData* form,
// form, including name and email, but no address-related fields.
void CreateTestPersonalInformationFormData(FormData* form);
+// Populates |form| with data corresponding to a simple credit card form.
+// Note that this actually appends fields to the form data, which can be
+// useful for building up more complex test forms.
+void CreateTestCreditCardFormData(FormData* form,
+ bool is_https,
+ bool use_month_type,
+ bool split_names = false);
+
// Returns a full profile with valid info according to rules for Canada.
AutofillProfile GetFullValidProfileForCanada();
@@ -208,6 +216,9 @@ void GenerateTestAutofillPopup(
std::string ObfuscatedCardDigitsAsUTF8(const std::string& str);
+std::string NextYear();
+std::string LastYear();
+
} // namespace test
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.cc b/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.cc
index 1a094ec8afb..575c9e43120 100644
--- a/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.cc
+++ b/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller.cc
@@ -39,10 +39,6 @@ AutofillWalletDataTypeController::AutofillWalletDataTypeController(
autofill::prefs::kAutofillWalletImportEnabled,
base::Bind(&AutofillWalletDataTypeController::OnUserPrefChanged,
base::AsWeakPtr(this)));
- pref_registrar_.Add(
- autofill::prefs::kAutofillCreditCardEnabled,
- base::Bind(&AutofillWalletDataTypeController::OnUserPrefChanged,
- base::AsWeakPtr(this)));
}
AutofillWalletDataTypeController::~AutofillWalletDataTypeController() {}
@@ -124,8 +120,7 @@ bool AutofillWalletDataTypeController::IsEnabled() {
// Require the user-visible pref to be enabled to sync Wallet data/metadata,
// and also check that Autofill for credit cards is not disabled by policy.
PrefService* ps = sync_client_->GetPrefService();
- return ps->GetBoolean(autofill::prefs::kAutofillWalletImportEnabled) &&
- ps->GetBoolean(autofill::prefs::kAutofillCreditCardEnabled);
+ return ps->GetBoolean(autofill::prefs::kAutofillWalletImportEnabled);
}
void AutofillWalletDataTypeController::DisableForPolicy() {
if (state() != NOT_RUNNING && state() != STOPPING) {
diff --git a/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller_unittest.cc b/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller_unittest.cc
index c9f958d2945..b6d29b62219 100644
--- a/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_wallet_data_type_controller_unittest.cc
@@ -178,7 +178,7 @@ TEST_F(AutofillWalletDataTypeControllerTest, DatatypeDisabledWhileRunning) {
EXPECT_EQ(syncer::DataTypeController::RUNNING, autofill_wallet_dtc_->state());
EXPECT_FALSE(last_error_.IsSet());
EXPECT_EQ(syncer::AUTOFILL_WALLET_DATA, last_type_);
- GetPrefService()->SetBoolean(autofill::prefs::kAutofillCreditCardEnabled,
+ GetPrefService()->SetBoolean(autofill::prefs::kAutofillWalletImportEnabled,
false);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(last_error_.IsSet());
@@ -187,7 +187,7 @@ TEST_F(AutofillWalletDataTypeControllerTest, DatatypeDisabledWhileRunning) {
TEST_F(AutofillWalletDataTypeControllerTest, DatatypeDisabledAtStartup) {
SetStartExpectations();
web_data_service_->LoadDatabase();
- GetPrefService()->SetBoolean(autofill::prefs::kAutofillCreditCardEnabled,
+ GetPrefService()->SetBoolean(autofill::prefs::kAutofillWalletImportEnabled,
false);
EXPECT_EQ(syncer::DataTypeController::NOT_RUNNING,
autofill_wallet_dtc_->state());
diff --git a/chromium/components/autofill/core/browser/credit_card.cc b/chromium/components/autofill/core/browser/credit_card.cc
index 1cfce7ab19e..bf5ff7af86e 100644
--- a/chromium/components/autofill/core/browser/credit_card.cc
+++ b/chromium/components/autofill/core/browser/credit_card.cc
@@ -52,9 +52,6 @@ const base::char16 kMidlineEllipsis[] = {0x2022, 0x2006, 0x2022, 0x2006, 0x2022,
namespace {
const base::char16 kCreditCardObfuscationSymbol = '*';
-// Time format pattern for short month name (3 digits) and day in month (2
-// digits), e.g. in en-US locale, it can be used to generate "Feb 02".
-const char kTimeFormatPatternNoYearShortMonthDate[] = "MMMdd";
bool ConvertYear(const base::string16& year, int* num) {
// If the |year| is empty, clear the stored value.
@@ -211,89 +208,86 @@ const char* CreditCard::GetCardNetwork(const base::string16& number) {
// MIR 2200-2204 16
// UnionPay 62 16-19
- // Check for prefixes of length 1.
- if (number.empty())
- return kGenericCard;
-
- if (number[0] == '4')
- return kVisaCard;
+ // Determine the network for the given |number| by going from the longest
+ // (most specific) prefix to the shortest (most general) prefix.
+ base::string16 stripped_number = CreditCard::StripSeparators(number);
- // Check for prefixes of length 2.
- if (number.size() < 2)
- return kGenericCard;
+ // Check for prefixes of length 6.
+ if (stripped_number.size() >= 6) {
+ int first_six_digits = 0;
+ if (!base::StringToInt(stripped_number.substr(0, 6), &first_six_digits))
+ return kGenericCard;
+
+ if (first_six_digits == 431274 || first_six_digits == 451416 ||
+ first_six_digits == 627780 || first_six_digits == 636297)
+ return kEloCard;
+ }
- int first_two_digits = 0;
- if (!base::StringToInt(number.substr(0, 2), &first_two_digits))
- return kGenericCard;
+ // Check for prefixes of length 4.
+ if (stripped_number.size() >= 4) {
+ int first_four_digits = 0;
+ if (!base::StringToInt(stripped_number.substr(0, 4), &first_four_digits))
+ return kGenericCard;
- if (first_two_digits == 34 || first_two_digits == 37)
- return kAmericanExpressCard;
+ if (first_four_digits >= 2200 && first_four_digits <= 2204)
+ return kMirCard;
- if (first_two_digits == 36 ||
- first_two_digits == 38 ||
- first_two_digits == 39)
- return kDinersCard;
+ if (first_four_digits >= 2221 && first_four_digits <= 2720)
+ return kMasterCard;
- if (first_two_digits >= 51 && first_two_digits <= 55)
- return kMasterCard;
+ if (first_four_digits >= 3528 && first_four_digits <= 3589)
+ return kJCBCard;
- if (first_two_digits == 62)
- return kUnionPay;
+ if (first_four_digits == 5067 || first_four_digits == 5090)
+ return kEloCard;
- if (first_two_digits == 65)
- return kDiscoverCard;
+ if (first_four_digits == 6011)
+ return kDiscoverCard;
+ }
// Check for prefixes of length 3.
- if (number.size() < 3)
- return kGenericCard;
-
- int first_three_digits = 0;
- if (!base::StringToInt(number.substr(0, 3), &first_three_digits))
- return kGenericCard;
+ if (stripped_number.size() >= 3) {
+ int first_three_digits = 0;
+ if (!base::StringToInt(stripped_number.substr(0, 3), &first_three_digits))
+ return kGenericCard;
- if ((first_three_digits >= 300 && first_three_digits <= 305) ||
- first_three_digits == 309)
- return kDinersCard;
+ if ((first_three_digits >= 300 && first_three_digits <= 305) ||
+ first_three_digits == 309)
+ return kDinersCard;
- if (first_three_digits >= 644 && first_three_digits <= 649)
- return kDiscoverCard;
-
- // Check for prefixes of length 4.
- if (number.size() < 4)
- return kGenericCard;
-
- int first_four_digits = 0;
- if (!base::StringToInt(number.substr(0, 4), &first_four_digits))
- return kGenericCard;
+ if (first_three_digits >= 644 && first_three_digits <= 649)
+ return kDiscoverCard;
+ }
- if (first_four_digits >= 2200 && first_four_digits <= 2204)
- return kMirCard;
+ // Check for prefixes of length 2.
+ if (stripped_number.size() >= 2) {
+ int first_two_digits = 0;
+ if (!base::StringToInt(stripped_number.substr(0, 2), &first_two_digits))
+ return kGenericCard;
- if (first_four_digits >= 2221 && first_four_digits <= 2720)
- return kMasterCard;
+ if (first_two_digits == 34 || first_two_digits == 37)
+ return kAmericanExpressCard;
- if (first_four_digits >= 3528 && first_four_digits <= 3589)
- return kJCBCard;
+ if (first_two_digits == 36 || first_two_digits == 38 ||
+ first_two_digits == 39)
+ return kDinersCard;
- if (first_four_digits == 5067 || first_four_digits == 5090)
- return kEloCard;
+ if (first_two_digits >= 51 && first_two_digits <= 55)
+ return kMasterCard;
- if (first_four_digits == 6011)
- return kDiscoverCard;
+ if (first_two_digits == 62)
+ return kUnionPay;
- // Check for prefixes of length 6.
- if (number.size() < 6)
- return kGenericCard;
+ if (first_two_digits == 65)
+ return kDiscoverCard;
+ }
- int first_six_digits = 0;
- if (!base::StringToInt(number.substr(0, 6), &first_six_digits))
+ // Check for prefixes of length 1.
+ if (stripped_number.empty())
return kGenericCard;
- if (first_six_digits == 431274 ||
- first_six_digits == 451416 ||
- first_six_digits == 627780 ||
- first_six_digits == 636297)
- return kEloCard;
+ if (stripped_number[0] == '4')
+ return kVisaCard;
return kGenericCard;
}
@@ -374,6 +368,20 @@ void CreditCard::SetRawInfo(ServerFieldType type,
name_on_card_ = value;
break;
+ case CREDIT_CARD_NAME_FIRST:
+ temp_card_first_name_ = value;
+ if (!temp_card_last_name_.empty()) {
+ SetNameOnCardFromSeparateParts();
+ }
+ break;
+
+ case CREDIT_CARD_NAME_LAST:
+ temp_card_last_name_ = value;
+ if (!temp_card_first_name_.empty()) {
+ SetNameOnCardFromSeparateParts();
+ }
+ break;
+
case CREDIT_CARD_EXP_MONTH:
SetExpirationMonthFromString(value, std::string());
break;
@@ -500,6 +508,8 @@ void CreditCard::operator=(const CreditCard& credit_card) {
server_status_ = credit_card.server_status_;
billing_address_id_ = credit_card.billing_address_id_;
bank_name_ = credit_card.bank_name_;
+ temp_card_first_name_ = credit_card.temp_card_first_name_;
+ temp_card_last_name_ = credit_card.temp_card_last_name_;
set_guid(credit_card.guid());
set_origin(credit_card.origin());
@@ -777,69 +787,21 @@ base::string16 CreditCard::BankNameAndLastFourDigits() const {
}
base::string16 CreditCard::NetworkOrBankNameAndLastFourDigits() const {
- if (IsAutofillCreditCardBankNameDisplayExperimentEnabled() &&
- !bank_name_.empty()) {
- return BankNameAndLastFourDigits();
- }
- return NetworkAndLastFourDigits();
+ return bank_name_.empty() ? NetworkAndLastFourDigits()
+ : BankNameAndLastFourDigits();
}
base::string16 CreditCard::AbbreviatedExpirationDateForDisplay() const {
base::string16 month = ExpirationMonthAsString();
base::string16 year = Expiration2DigitYearAsString();
- return month.empty() || year.empty()
- ? base::string16()
- : l10n_util::GetStringFUTF16(
- IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_ABBR, month, year);
-}
-
-base::string16 CreditCard::GetLastUsedDateForDisplay(
- const std::string& app_locale) const {
- bool show_expiration_date =
- ShowExpirationDateInAutofillCreditCardLastUsedDate();
-
- DCHECK_LT(0U, use_count());
- // use_count() is initialized as 1 when the card is just added.
- if (use_count() == 1U) {
- return show_expiration_date
- ? l10n_util::GetStringFUTF16(
- IDS_AUTOFILL_CREDIT_CARD_EXP_AND_ADDED_DATE,
- GetInfo(AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR),
- app_locale),
- base::TimeFormatWithPattern(
- use_date(), kTimeFormatPatternNoYearShortMonthDate))
- : l10n_util::GetStringFUTF16(
- IDS_AUTOFILL_CREDIT_CARD_ADDED_DATE,
- base::TimeFormatWithPattern(
- use_date(), kTimeFormatPatternNoYearShortMonthDate));
- }
-
- // use_count() > 1 when the card has been used in autofill.
-
- // If the card was last used in autofill more than a year ago,
- // display "last used over a year ago" without showing date detail.
- if ((AutofillClock::Now() - use_date()).InDays() > 365) {
- return show_expiration_date
- ? l10n_util::GetStringFUTF16(
- IDS_AUTOFILL_CREDIT_CARD_EXP_AND_LAST_USED_YEAR_AGO,
- GetInfo(AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR),
- app_locale))
- : l10n_util::GetStringUTF16(
- IDS_AUTOFILL_CREDIT_CARD_LAST_USED_YEAR_AGO);
- }
+ if (month.empty() || year.empty())
+ return base::string16();
- // If the card was last used in autofill within a year, show date information.
- return show_expiration_date
- ? l10n_util::GetStringFUTF16(
- IDS_AUTOFILL_CREDIT_CARD_EXP_AND_LAST_USED_DATE,
- GetInfo(AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR),
- app_locale),
- base::TimeFormatWithPattern(
- use_date(), kTimeFormatPatternNoYearShortMonthDate))
- : l10n_util::GetStringFUTF16(
- IDS_AUTOFILL_CREDIT_CARD_LAST_USED_DATE,
- base::TimeFormatWithPattern(
- use_date(), kTimeFormatPatternNoYearShortMonthDate));
+ return l10n_util::GetStringFUTF16(
+ IsAutofillSaveCardDialogUnlabeledExpirationDateEnabled()
+ ? IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_ABBR_V2
+ : IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_ABBR,
+ month, year);
}
base::string16 CreditCard::ExpirationDateForDisplay() const {
@@ -869,6 +831,11 @@ base::string16 CreditCard::Expiration4DigitYearAsString() const {
return base::IntToString16(Expiration4DigitYear());
}
+bool CreditCard::HasFirstAndLastName() const {
+ return !temp_card_first_name_.empty() && !temp_card_last_name_.empty() &&
+ !name_on_card_.empty();
+}
+
base::string16 CreditCard::Expiration2DigitYearAsString() const {
if (expiration_year_ == 0)
return base::string16();
@@ -1016,6 +983,13 @@ std::ostream& operator<<(std::ostream& os, const CreditCard& credit_card) {
credit_card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
}
+void CreditCard::SetNameOnCardFromSeparateParts() {
+ DCHECK(name_on_card_.empty() && !temp_card_first_name_.empty() &&
+ !temp_card_last_name_.empty());
+ name_on_card_ =
+ temp_card_first_name_ + base::UTF8ToUTF16(" ") + temp_card_last_name_;
+}
+
const char kAmericanExpressCard[] = "americanExpressCC";
const char kDinersCard[] = "dinersCC";
const char kDiscoverCard[] = "discoverCC";
diff --git a/chromium/components/autofill/core/browser/credit_card.h b/chromium/components/autofill/core/browser/credit_card.h
index 74971b541b8..30c394ae53c 100644
--- a/chromium/components/autofill/core/browser/credit_card.h
+++ b/chromium/components/autofill/core/browser/credit_card.h
@@ -248,14 +248,16 @@ class CreditCard : public AutofillDataModel {
base::string16 NetworkOrBankNameAndLastFourDigits() const;
// Localized expiration for this card formatted as 'Exp: 06/17'.
base::string16 AbbreviatedExpirationDateForDisplay() const;
- // Returns the date when the card was last used in autofill.
- base::string16 GetLastUsedDateForDisplay(const std::string& app_locale) const;
// Formatted expiration date (e.g., 05/2020).
base::string16 ExpirationDateForDisplay() const;
// Expiration functions.
base::string16 ExpirationMonthAsString() const;
base::string16 Expiration4DigitYearAsString() const;
+ // Whether the cardholder name was created from separate first name and last
+ // name fields.
+ bool HasFirstAndLastName() const;
+
private:
FRIEND_TEST_ALL_PREFIXES(CreditCardTest, SetExpirationDateFromString);
FRIEND_TEST_ALL_PREFIXES(CreditCardTest, SetExpirationYearFromString);
@@ -281,6 +283,9 @@ class CreditCard : public AutofillDataModel {
// A label for this card formatted as 'BankName - 2345'.
base::string16 BankNameAndLastFourDigits() const;
+ // Sets the name_on_card_ value based on the saved name parts.
+ void SetNameOnCardFromSeparateParts();
+
// See enum definition above.
RecordType record_type_;
CardType card_type_;
@@ -313,6 +318,12 @@ class CreditCard : public AutofillDataModel {
// The identifier of the billing address for this card.
std::string billing_address_id_;
+
+ // The credit card holder's name parts. Used when creating a new card to hold
+ // on to the value until the credit card holder's other name part is set,
+ // since we only store the full name.
+ base::string16 temp_card_first_name_;
+ base::string16 temp_card_last_name_;
};
// So we can compare CreditCards with EXPECT_EQ().
diff --git a/chromium/components/autofill/core/browser/credit_card_save_manager.cc b/chromium/components/autofill/core/browser/credit_card_save_manager.cc
index 5eac76c7094..d0aba9954e5 100644
--- a/chromium/components/autofill/core/browser/credit_card_save_manager.cc
+++ b/chromium/components/autofill/core/browser/credit_card_save_manager.cc
@@ -34,6 +34,7 @@
#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_util.h"
#include "components/prefs/pref_service.h"
#include "services/identity/public/cpp/identity_manager.h"
@@ -86,11 +87,13 @@ CreditCardSaveManager::CreditCardSaveManager(
CreditCardSaveManager::~CreditCardSaveManager() {}
void CreditCardSaveManager::OfferCardLocalSave(const CreditCard& card) {
+ if (card.HasFirstAndLastName())
+ AutofillMetrics::LogSaveCardWithFirstAndLastNameOffered(/*is_local=*/true);
if (observer_for_testing_)
observer_for_testing_->OnOfferLocalSave();
client_->ConfirmSaveCreditCardLocally(
card, base::Bind(base::IgnoreResult(
- &PersonalDataManager::SaveImportedCreditCard),
+ &PersonalDataManager::OnAcceptedLocalCreditCardSave),
base::Unretained(personal_data_manager_), card));
}
@@ -98,25 +101,26 @@ void CreditCardSaveManager::AttemptToOfferCardUploadSave(
const FormStructure& submitted_form,
const CreditCard& card,
const bool uploading_local_card) {
+ // Abort the uploading if |payments_client_| is nullptr.
+ if (!payments_client_)
+ return;
+ payments_client_->SetSaveDelegate(this);
upload_request_ = payments::PaymentsClient::UploadRequestDetails();
upload_request_.card = card;
uploading_local_card_ = uploading_local_card;
- // Ideally, in order to upload a card, we must have both:
- // 1) Card with CVC
- // 2) 1+ recently-used or modified addresses that meet the client-side
- // validation rules
- // We perform all checks before returning or logging in order to know where we
- // stand with regards to card upload information. If the "send detected
- // values" experiment is disabled and any problems were found, we do not offer
- // to save the card. We could fall back to a local save, but we believe that
- // sometimes offering upload and sometimes offering local save is a confusing
- // user experience.
-
- // Alternatively, if the "send detected values" experiment is enabled, always
- // ping Google Payments regardless and ask if save is allowed. Include what
- // data was found as part of the request, and let Payments decide whether
- // upload save should be offered.
+ // In an ideal scenario, when uploading a card, we would have:
+ // 1) Card number and expiration
+ // 2) CVC
+ // 3) 1+ recently-used or modified addresses that meet validation rules (or
+ // only the address countries if the relevant feature is enabled).
+ // 4) Cardholder name or names on the address profiles
+ // At a minimum, only #1 (card number and expiration) is absolutely required
+ // in order to save a card to Google Payments. We perform all checks before
+ // returning or logging in order to know where we stand with regards to card
+ // upload information. Then, we ping Google Payments and ask if upload save
+ // should be offered with the given amount of information, letting Payments
+ // make the final offer-to-save decision.
found_cvc_field_ = false;
found_value_in_cvc_field_ = false;
found_cvc_value_in_non_cvc_field_ = false;
@@ -151,25 +155,7 @@ void CreditCardSaveManager::AttemptToOfferCardUploadSave(
upload_decision_metrics_ |= GetCVCCardUploadDecisionMetric();
}
- // If any problems were found across CVC/name/address,
- // |upload_decision_metrics_| will be non-zero. If the "send detected values"
- // experiment is on, continue anyway and just let Payments know that not
- // everything was found, as Payments may still allow the card to be saved.
- // If the experiment is off, follow the legacy logic of aborting upload save.
- if (upload_decision_metrics_ &&
- !IsAutofillUpstreamSendDetectedValuesExperimentEnabled()) {
- LogCardUploadDecisions(upload_decision_metrics_);
- pending_upload_request_origin_ = url::Origin();
- if (observer_for_testing_)
- observer_for_testing_->OnDecideToNotRequestUploadSave();
- return;
- }
-
// Add active experiments to the request payload.
- if (IsAutofillUpstreamSendDetectedValuesExperimentEnabled()) {
- upload_request_.active_experiments.push_back(
- kAutofillUpstreamSendDetectedValues.name);
- }
if (IsAutofillUpstreamSendPanFirstSixExperimentEnabled()) {
upload_request_.active_experiments.push_back(
kAutofillUpstreamSendPanFirstSix.name);
@@ -179,11 +165,22 @@ void CreditCardSaveManager::AttemptToOfferCardUploadSave(
kAutofillUpstreamUpdatePromptExplanation.name);
}
+ int detected_values = GetDetectedValues();
+ // If the user must provide cardholder name, log it and set
+ // |should_request_name_from_user_| so the offer-to-save dialog know to ask
+ // for it.
+ should_request_name_from_user_ = false;
+ if (detected_values & DetectedValue::USER_PROVIDED_NAME) {
+ upload_decision_metrics_ |=
+ AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME;
+ should_request_name_from_user_ = true;
+ }
+
// All required data is available, start the upload process.
if (observer_for_testing_)
observer_for_testing_->OnDecideToRequestUploadSave();
payments_client_->GetUploadDetails(
- upload_request_.profiles, GetDetectedValues(),
+ upload_request_.profiles, detected_values,
base::UTF16ToASCII(CreditCard::StripSeparators(card.number()))
.substr(0, 6),
upload_request_.active_experiments, app_locale_);
@@ -198,9 +195,28 @@ bool CreditCardSaveManager::IsCreditCardUploadEnabled() {
client_->GetIdentityManager()->GetPrimaryAccountInfo().email);
}
+bool CreditCardSaveManager::IsUploadEnabledForNetwork(
+ const std::string& network) {
+ if (network == kEloCard &&
+ base::FeatureList::IsEnabled(features::kAutofillUpstreamDisallowElo)) {
+ return false;
+ } else if (network == kJCBCard &&
+ base::FeatureList::IsEnabled(
+ features::kAutofillUpstreamDisallowJcb)) {
+ return false;
+ }
+ return true;
+}
+
void CreditCardSaveManager::OnDidUploadCard(
AutofillClient::PaymentsRpcResult result,
const std::string& server_id) {
+ if (result == AutofillClient::SUCCESS &&
+ upload_request_.card.HasFirstAndLastName()) {
+ AutofillMetrics::LogSaveCardWithFirstAndLastNameComplete(
+ /*is_local=*/false);
+ }
+
// We don't do anything user-visible if the upload attempt fails. If the
// upload succeeds and we can store unmasked cards on this OS, we will keep a
// copy of the card as a full server card on the device.
@@ -228,8 +244,9 @@ void CreditCardSaveManager::OnDidGetUploadDetails(
user_did_accept_upload_prompt_ = false;
client_->ConfirmSaveCreditCardToCloud(
upload_request_.card, std::move(legal_message),
- base::Bind(&CreditCardSaveManager::OnUserDidAcceptUpload,
- weak_ptr_factory_.GetWeakPtr()));
+ should_request_name_from_user_,
+ base::BindOnce(&CreditCardSaveManager::OnUserDidAcceptUpload,
+ weak_ptr_factory_.GetWeakPtr()));
client_->LoadRiskData(
base::Bind(&CreditCardSaveManager::OnDidGetUploadRiskData,
weak_ptr_factory_.GetWeakPtr()));
@@ -237,56 +254,32 @@ void CreditCardSaveManager::OnDidGetUploadDetails(
AutofillMetrics::LogUploadOfferedCardOriginMetric(
uploading_local_card_ ? AutofillMetrics::OFFERING_UPLOAD_OF_LOCAL_CARD
: AutofillMetrics::OFFERING_UPLOAD_OF_NEW_CARD);
+ if (upload_request_.card.HasFirstAndLastName()) {
+ AutofillMetrics::LogSaveCardWithFirstAndLastNameOffered(
+ /*is_local=*/false);
+ }
} else {
- // If the upload details request failed, fall back to a local save. The
- // reasoning here is as follows:
- // - This will sometimes fail intermittently, in which case it might be
- // better to not fall back, because sometimes offering upload and sometimes
- // offering local save is a poor user experience.
- // - However, in some cases, our local configuration limiting the feature to
- // countries that Payments is known to support will not match Payments' own
- // determination of what country the user is located in. In these cases,
- // the upload details request will consistently fail and if we don't fall
- // back to a local save then the user will never be offered any kind of
- // credit card save.
-
- // Additional note: If the "send detected values" experiment is enabled,
- // only offer to save locally if CVC + name + address were all found, as
- // this signifies a legacy decision of "Payments doesn't want this card".
- // We can revisit this decision in the future, but the reasoning here is as
- // follows:
- // - If any of them were not found, surfacing local save would begin to
- // create a scenario where different card types or different checkout forms
- // could reasonably surface different save dialogs to the user, and that
- // would cause unnecessary confusion.
- // - We already don't offer to save at all in these cases today, so this
- // decision doesn't disable any upload chances, it just enables *less*
- // upload chances.
+ // If the upload details request failed and we *know* we have all possible
+ // information (card number, expiration, cvc, name, and address), fall back
+ // to a local save. It indicates that "Payments doesn't want this card" or
+ // "Payments doesn't currently support this country", in which case the
+ // upload details request will consistently fail and if we don't fall back
+ // to a local save, the user will never be offered *any* kind of credit card
+ // save. (Note that this could intermittently backfire if there's a network
+ // breakdown or Payments outage, resulting in sometimes showing upload and
+ // sometimes offering local save, but such cases should be rare.)
int detected_values = GetDetectedValues();
bool found_name_and_postal_code_and_cvc =
(detected_values & DetectedValue::CARDHOLDER_NAME ||
detected_values & DetectedValue::ADDRESS_NAME) &&
detected_values & DetectedValue::POSTAL_CODE &&
detected_values & DetectedValue::CVC;
- if (!IsAutofillUpstreamSendDetectedValuesExperimentEnabled() ||
- found_name_and_postal_code_and_cvc) {
- if (observer_for_testing_)
- observer_for_testing_->OnOfferLocalSave();
- client_->ConfirmSaveCreditCardLocally(
- upload_request_.card,
- base::BindRepeating(
- base::IgnoreResult(&PersonalDataManager::SaveImportedCreditCard),
- base::Unretained(personal_data_manager_), upload_request_.card));
- }
+ if (found_name_and_postal_code_and_cvc)
+ OfferCardLocalSave(upload_request_.card);
upload_decision_metrics_ |=
AutofillMetrics::UPLOAD_NOT_OFFERED_GET_UPLOAD_DETAILS_FAILED;
}
- // Assert that we've either detected the CVC or the "send detected values"
- // experiment is enabled.
- DCHECK(IsAutofillUpstreamSendDetectedValuesExperimentEnabled() ||
- (found_cvc_field_ && found_value_in_cvc_field_));
-
LogCardUploadDecisions(upload_decision_metrics_);
pending_upload_request_origin_ = url::Origin();
}
@@ -394,17 +387,29 @@ void CreditCardSaveManager::SetProfilesForCreditCardUpload(
if (verified_zip.empty() && !candidate_profiles.empty())
upload_decision_metrics_ |= AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE;
- // Only set |upload_request->profiles| if upload is allowed (either all
- // required data was found, or "send detected values" experiment is enabled).
- if (!upload_decision_metrics_ ||
- IsAutofillUpstreamSendDetectedValuesExperimentEnabled()) {
- upload_request->profiles.assign(candidate_profiles.begin(),
- candidate_profiles.end());
- if (!has_modified_profile)
- for (const AutofillProfile& profile : candidate_profiles)
- UMA_HISTOGRAM_COUNTS_1000(
- "Autofill.DaysSincePreviousUseAtSubmission.Profile",
- (profile.use_date() - profile.previous_use_date()).InDays());
+ // If the relevant feature is enabled, only send the country of the
+ // recently-used addresses.
+ if (base::FeatureList::IsEnabled(
+ features::kAutofillSendOnlyCountryInGetUploadDetails)) {
+ for (size_t i = 0; i < candidate_profiles.size(); i++) {
+ AutofillProfile country_only;
+ country_only.SetInfo(
+ ADDRESS_HOME_COUNTRY,
+ candidate_profiles[i].GetInfo(ADDRESS_HOME_COUNTRY, app_locale_),
+ app_locale_);
+ candidate_profiles[i] = std::move(country_only);
+ }
+ }
+
+ // Set up |upload_request->profiles|.
+ upload_request->profiles.assign(candidate_profiles.begin(),
+ candidate_profiles.end());
+ if (!has_modified_profile) {
+ for (const AutofillProfile& profile : candidate_profiles) {
+ UMA_HISTOGRAM_COUNTS_1000(
+ "Autofill.DaysSincePreviousUseAtSubmission.Profile",
+ (profile.use_date() - profile.previous_use_date()).InDays());
+ }
}
}
@@ -464,10 +469,34 @@ int CreditCardSaveManager::GetDetectedValues() const {
prefs::kAutofillBillingCustomerNumber)) != 0)
detected_values |= DetectedValue::HAS_GOOGLE_PAYMENTS_ACCOUNT;
+ // If one of the following is true, signal that cardholder name will be
+ // explicitly requested in the offer-to-save bubble:
+ // 1) Name is conflicting/missing, and the user does NOT have a Google
+ // Payments account
+ // 2) The AutofillUpstreamAlwaysRequestCardholderName experiment is enabled
+ // (should only ever be used by testers, never launched)
+ if ((!(detected_values & DetectedValue::CARDHOLDER_NAME) &&
+ !(detected_values & DetectedValue::ADDRESS_NAME) &&
+ !(detected_values & DetectedValue::HAS_GOOGLE_PAYMENTS_ACCOUNT) &&
+ IsAutofillUpstreamEditableCardholderNameExperimentEnabled()) ||
+ IsAutofillUpstreamAlwaysRequestCardholderNameExperimentEnabled()) {
+ detected_values |= DetectedValue::USER_PROVIDED_NAME;
+ }
+
return detected_values;
}
-void CreditCardSaveManager::OnUserDidAcceptUpload() {
+void CreditCardSaveManager::OnUserDidAcceptUpload(
+ const base::string16& cardholder_name) {
+ // If cardholder name was explicitly requested for the user to enter/confirm,
+ // replace the name on |upload_request_.card| with the entered name. (Note
+ // that it is possible a name already existed on the card if conflicting names
+ // were found, which this intentionally overwrites.)
+ if (!cardholder_name.empty()) {
+ DCHECK(should_request_name_from_user_);
+ upload_request_.card.SetInfo(CREDIT_CARD_NAME_FULL, cardholder_name,
+ app_locale_);
+ }
user_did_accept_upload_prompt_ = true;
// Populating risk data and offering upload occur asynchronously.
// If |risk_data| has already been loaded, send the upload card request.
@@ -517,8 +546,8 @@ void CreditCardSaveManager::LogCardUploadDecisions(
int upload_decision_metrics) {
AutofillMetrics::LogCardUploadDecisionMetrics(upload_decision_metrics);
AutofillMetrics::LogCardUploadDecisionsUkm(
- client_->GetUkmRecorder(), pending_upload_request_origin_.GetURL(),
- upload_decision_metrics);
+ client_->GetUkmRecorder(), client_->GetUkmSourceId(),
+ pending_upload_request_origin_.GetURL(), upload_decision_metrics);
}
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/credit_card_save_manager.h b/chromium/components/autofill/core/browser/credit_card_save_manager.h
index 2e076c3b48a..4a32e67e617 100644
--- a/chromium/components/autofill/core/browser/credit_card_save_manager.h
+++ b/chromium/components/autofill/core/browser/credit_card_save_manager.h
@@ -54,6 +54,16 @@ class CreditCardSaveManager : public payments::PaymentsClientSaveDelegate {
COUNTRY_CODE = 1 << 7,
// Set if the user is already syncing data from a Google Payments account.
HAS_GOOGLE_PAYMENTS_ACCOUNT = 1 << 8,
+ // Card expiration month (not currently used).
+ CARD_EXPIRATION_MONTH = 1 << 9,
+ // Card expiration year (not currently used).
+ CARD_EXPIRATION_YEAR = 1 << 10,
+ // Phone number was found on any address (not currently used).
+ PHONE_NUMBER = 1 << 11,
+ // Set if cardholder name was explicitly requested in the offer-to-save
+ // dialog. In general, this should happen when name is conflicting/missing
+ // and the user does not have a Google Payments account.
+ USER_PROVIDED_NAME = 1 << 12,
};
// An observer class used by browsertests that gets notified whenever
@@ -62,7 +72,6 @@ class CreditCardSaveManager : public payments::PaymentsClientSaveDelegate {
public:
virtual void OnOfferLocalSave() = 0;
virtual void OnDecideToRequestUploadSave() = 0;
- virtual void OnDecideToNotRequestUploadSave() = 0;
virtual void OnReceivedGetUploadDetailsResponse() = 0;
virtual void OnSentUploadCardRequest() = 0;
};
@@ -87,6 +96,11 @@ class CreditCardSaveManager : public payments::PaymentsClientSaveDelegate {
// are satisfied.
virtual bool IsCreditCardUploadEnabled();
+ // Returns true if the given |network| is allowed for upload to Google
+ // Payments, false otherwise. Mainly used for blacklisting upload of certain
+ // networks.
+ bool IsUploadEnabledForNetwork(const std::string& network);
+
// For testing.
void SetAppLocale(std::string app_locale) { app_locale_ = app_locale; }
@@ -110,7 +124,9 @@ class CreditCardSaveManager : public payments::PaymentsClientSaveDelegate {
// to |upload_request.profiles|. If any problems are found when determining
// the candidate set of profiles, sets |upload_decision_metrics_| with the
// failure reasons. Appends any experiments that were triggered to
- // |upload_request.active_experiments|.
+ // |upload_request.active_experiments|. Note that if the relevant feature is
+ // enabled, the addresses being assigned to |upload_request.profiles| may only
+ // contain countries.
void SetProfilesForCreditCardUpload(
const CreditCard& card,
payments::PaymentsClient::UploadRequestDetails* upload_request);
@@ -121,8 +137,9 @@ class CreditCardSaveManager : public payments::PaymentsClientSaveDelegate {
int GetDetectedValues() const;
// Sets |user_did_accept_upload_prompt_| and calls SendUploadCardRequest if
- // the risk data is available.
- void OnUserDidAcceptUpload();
+ // the risk data is available. Sets the cardholder name on the upload request
+ // if |cardholder_name| is set.
+ void OnUserDidAcceptUpload(const base::string16& cardholder_name);
// Saves risk data in |uploading_risk_data_| and calls SendUploadCardRequest
// if the user has accepted the prompt.
@@ -177,6 +194,10 @@ class CreditCardSaveManager : public payments::PaymentsClientSaveDelegate {
// |true| if the user has opted to upload save their credit card to Google.
bool user_did_accept_upload_prompt_ = false;
+ // |should_request_name_from_user_| is |true| if the upload save dialog should
+ // request cardholder name from the user (prefilled with Google Account name).
+ bool should_request_name_from_user_ = false;
+
// |found_cvc_field_| is |true| if there exists a field that is determined to
// be a CVC field via heuristics.
bool found_cvc_field_ = false;
@@ -196,6 +217,10 @@ class CreditCardSaveManager : public payments::PaymentsClientSaveDelegate {
base::WeakPtrFactory<CreditCardSaveManager> weak_ptr_factory_;
+ FRIEND_TEST_ALL_PREFIXES(
+ CreditCardSaveManagerTest,
+ UploadCreditCard_ShouldRequestCardholderName_ResetBetweenConsecutiveSaves);
+
DISALLOW_COPY_AND_ASSIGN(CreditCardSaveManager);
};
diff --git a/chromium/components/autofill/core/browser/credit_card_save_manager_unittest.cc b/chromium/components/autofill/core/browser/credit_card_save_manager_unittest.cc
index 84c708b08f9..dd186e27cfb 100644
--- a/chromium/components/autofill/core/browser/credit_card_save_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/credit_card_save_manager_unittest.cc
@@ -16,7 +16,7 @@
#include "base/guid.h"
#include "base/metrics/metrics_hashes.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -39,6 +39,7 @@
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/autofill/core/common/autofill_clock.h"
+#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/prefs/pref_service.h"
@@ -47,6 +48,7 @@
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_test_util.h"
#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -67,12 +69,21 @@ using UkmDeveloperEngagementType = ukm::builders::Autofill_DeveloperEngagement;
const base::Time kArbitraryTime = base::Time::FromDoubleT(25);
const base::Time kMuchLaterTime = base::Time::FromDoubleT(5000);
+const char kEloCardNumber[] = "5067111111111112";
+const char kJcbCardNumber[] = "3528111111111110";
+
std::string NextYear() {
base::Time::Exploded now;
base::Time::Now().LocalExplode(&now);
return std::to_string(now.year + 1);
}
+std::string NextMonth() {
+ base::Time::Exploded now;
+ base::Time::Now().LocalExplode(&now);
+ return std::to_string(now.month % 12 + 1);
+}
+
class MockAutofillClient : public TestAutofillClient {
public:
MockAutofillClient() {}
@@ -92,15 +103,15 @@ class CreditCardSaveManagerTest : public testing::Test {
public:
void SetUp() override {
autofill_client_.SetPrefs(test::PrefServiceForTesting());
- personal_data_.set_database(autofill_client_.GetDatabase());
- personal_data_.SetPrefService(autofill_client_.GetPrefs());
+ personal_data_.Init(autofill_client_.GetDatabase(), nullptr,
+ autofill_client_.GetPrefs(), nullptr, false);
personal_data_.SetSyncServiceForTest(&sync_service_);
autofill_driver_.reset(new TestAutofillDriver());
request_context_ = new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get());
autofill_driver_->SetURLRequestContext(request_context_.get());
payments_client_ = new payments::TestPaymentsClient(
- autofill_driver_->GetURLRequestContext(), autofill_client_.GetPrefs(),
+ autofill_driver_->GetURLLoaderFactory(), autofill_client_.GetPrefs(),
autofill_client_.GetIdentityManager(),
/*unmask_delegate=*/nullptr,
// Will be set by CreditCardSaveManager's ctor
@@ -113,6 +124,7 @@ class CreditCardSaveManagerTest : public testing::Test {
std::unique_ptr<CreditCardSaveManager>(credit_card_save_manager_),
payments_client_));
autofill_manager_->SetExpectedObservedSubmission(true);
+ payments_client_->SetSaveDelegate(credit_card_save_manager_);
}
void TearDown() override {
@@ -121,25 +133,12 @@ class CreditCardSaveManagerTest : public testing::Test {
autofill_manager_.reset();
autofill_driver_.reset();
- // Remove the AutofillWebDataService so TestPersonalDataManager does not
- // need to care about removing self as an observer in destruction.
- personal_data_.set_database(scoped_refptr<AutofillWebDataService>(nullptr));
personal_data_.SetPrefService(nullptr);
personal_data_.ClearCreditCards();
request_context_ = nullptr;
}
- void DisableAutofillUpstreamSendDetectedValuesExperiment() {
- scoped_feature_list_.InitAndDisableFeature(
- kAutofillUpstreamSendDetectedValues);
- }
-
- void EnableAutofillUpstreamSendDetectedValuesExperiment() {
- scoped_feature_list_.InitAndEnableFeature(
- kAutofillUpstreamSendDetectedValues);
- }
-
void EnableAutofillUpstreamSendPanFirstSixExperiment() {
scoped_feature_list_.InitAndEnableFeature(kAutofillUpstreamSendPanFirstSix);
}
@@ -149,6 +148,11 @@ class CreditCardSaveManagerTest : public testing::Test {
kAutofillUpstreamUpdatePromptExplanation);
}
+ void DisableAutofillUpstreamUpdatePromptExplanationExperiment() {
+ scoped_feature_list_.InitAndDisableFeature(
+ kAutofillUpstreamUpdatePromptExplanation);
+ }
+
void FormsSeen(const std::vector<FormData>& forms) {
autofill_manager_->OnFormsSeen(forms, base::TimeTicks());
}
@@ -163,7 +167,8 @@ class CreditCardSaveManagerTest : public testing::Test {
// useful for building up more complex test forms.
void CreateTestCreditCardFormData(FormData* form,
bool is_https,
- bool use_month_type) {
+ bool use_month_type,
+ bool split_names = false) {
form->name = ASCIIToUTF16("MyForm");
if (is_https) {
form->origin = GURL("https://myform.com/form.html");
@@ -178,8 +183,21 @@ class CreditCardSaveManagerTest : public testing::Test {
}
FormFieldData field;
- test::CreateTestFormField("Name on Card", "nameoncard", "", "text", &field);
- form->fields.push_back(field);
+ if (split_names) {
+ test::CreateTestFormField("First Name on Card", "firstnameoncard", "",
+ "text", &field);
+ field.autocomplete_attribute = "cc-given-name";
+ form->fields.push_back(field);
+ test::CreateTestFormField("Last Name on Card", "lastnameoncard", "",
+ "text", &field);
+ field.autocomplete_attribute = "cc-family-name";
+ form->fields.push_back(field);
+ field.autocomplete_attribute = "";
+ } else {
+ 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);
if (use_month_type) {
@@ -231,7 +249,7 @@ class CreditCardSaveManagerTest : public testing::Test {
// Edit the data, and submit.
form.fields[1].value = ASCIIToUTF16("4111111111111111");
- form.fields[2].value = ASCIIToUTF16("11");
+ form.fields[2].value = ASCIIToUTF16(NextMonth());
form.fields[3].value = ASCIIToUTF16(NextYear());
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _));
FormSubmitted(form);
@@ -262,6 +280,13 @@ class CreditCardSaveManagerTest : public testing::Test {
ToHistogramSample(metric), 1);
}
+ void ExpectNoCardUploadDecision(
+ const base::HistogramTester& histogram_tester,
+ AutofillMetrics::CardUploadDecisionMetric metric) {
+ histogram_tester.ExpectBucketCount("Autofill.CardUploadDecisionMetric",
+ ToHistogramSample(metric), 0);
+ }
+
void ExpectCardUploadDecisionUkm(int expected_metric_value) {
ExpectMetric(UkmCardUploadDecisionType::kUploadDecisionName,
UkmCardUploadDecisionType::kEntryName, expected_metric_value,
@@ -358,7 +383,7 @@ TEST_F(CreditCardSaveManagerTest, MAYBE_CreditCardSavedWhenAutocompleteOff) {
// Edit the data, and submit.
form.fields[1].value = ASCIIToUTF16("4111111111111111");
- form.fields[2].value = ASCIIToUTF16("11");
+ form.fields[2].value = ASCIIToUTF16(NextMonth());
form.fields[3].value = ASCIIToUTF16(NextYear());
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _));
FormSubmitted(form);
@@ -377,7 +402,7 @@ TEST_F(CreditCardSaveManagerTest, InvalidCreditCardNumberIsNotSaved) {
std::string card("4408041234567890");
ASSERT_FALSE(autofill::IsValidCreditCardNumber(ASCIIToUTF16(card)));
form.fields[1].value = ASCIIToUTF16(card);
- form.fields[2].value = ASCIIToUTF16("11");
+ form.fields[2].value = ASCIIToUTF16(NextMonth());
form.fields[3].value = ASCIIToUTF16(NextYear());
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(form);
@@ -403,7 +428,7 @@ TEST_F(CreditCardSaveManagerTest, CreditCardDisabledDoesNotSave) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -418,7 +443,9 @@ TEST_F(CreditCardSaveManagerTest, CreditCardDisabledDoesNotSave) {
histogram_tester.ExpectTotalCount("Autofill.CardUploadDecisionMetric", 0);
}
-TEST_F(CreditCardSaveManagerTest, UploadCreditCard) {
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_FullAddresses) {
+ scoped_feature_list_.InitAndDisableFeature(
+ features::kAutofillSendOnlyCountryInGetUploadDetails);
personal_data_.ClearCreditCards();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -442,7 +469,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -451,11 +478,20 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard) {
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
- EXPECT_THAT(payments_client_->GetActiveExperimentsSetInRequest(),
- UnorderedElementsAre(kAutofillUpstreamSendDetectedValues.name));
+ EXPECT_THAT(
+ payments_client_->active_experiments_in_request(),
+ UnorderedElementsAre(kAutofillUpstreamUpdatePromptExplanation.name));
+
+ // Verify that one profile was saved, and it was included in the upload
+ // details request to payments.
+ EXPECT_EQ(1U, personal_data_.GetProfiles().size());
+ EXPECT_THAT(
+ payments_client_->addresses_in_upload_details(),
+ testing::UnorderedElementsAreArray({*personal_data_.GetProfiles()[0]}));
// Server did not send a server_id, expect copy of card is not stored.
EXPECT_TRUE(personal_data_.GetCreditCards().empty());
+
// Verify that the correct histogram entry (and only that) was logged.
ExpectUniqueCardUploadDecision(histogram_tester,
AutofillMetrics::UPLOAD_OFFERED);
@@ -470,19 +506,21 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard) {
"Autofill.DaysSincePreviousUseAtSubmission.Profile", 0);
}
-TEST_F(CreditCardSaveManagerTest, UploadCreditCardAndSaveCopy) {
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_OnlyCountryInAddresses) {
+ // When this feature is enabled, the addresses being sent in the upload
+ // details request will only contain the country.
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillSendOnlyCountryInGetUploadDetails);
personal_data_.ClearCreditCards();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
- const char* const server_id = "InstrumentData:1234";
- payments_client_->SetServerIdForCardUpload(server_id);
-
// Create, fill and submit an address form in order to establish a recent
// profile which can be selected for the upload request.
FormData address_form;
test::CreateTestAddressFormData(&address_form);
FormsSeen(std::vector<FormData>(1, address_form));
+ ExpectUniqueFillableFormParsedUkm();
ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
FormSubmitted(address_form);
@@ -491,73 +529,120 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCardAndSaveCopy) {
FormData credit_card_form;
CreateTestCreditCardFormData(&credit_card_form, true, false);
FormsSeen(std::vector<FormData>(1, credit_card_form));
+ ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */);
// Edit the data, and submit.
- const char* const card_number = "4111111111111111";
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
- credit_card_form.fields[1].value = ASCIIToUTF16(card_number);
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
- FormSubmitted(credit_card_form);
+ base::HistogramTester histogram_tester;
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+ FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
- EXPECT_TRUE(personal_data_.GetLocalCreditCards().empty());
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
- // See |OfferStoreUnmaskedCards|
+ EXPECT_THAT(
+ payments_client_->active_experiments_in_request(),
+ UnorderedElementsAre(kAutofillUpstreamUpdatePromptExplanation.name));
+
+ // 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());
+ AutofillProfile only_country;
+ only_country.SetInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"), "en-US");
+ EXPECT_EQ(1U, payments_client_->addresses_in_upload_details().size());
+ // AutofillProfile::Compare will ignore the difference in guid between our
+ // actual profile being sent and the expected one constructed here.
+ EXPECT_EQ(0, payments_client_->addresses_in_upload_details()[0].Compare(
+ only_country));
+
+ // Server did not send a server_id, expect copy of card is not stored.
EXPECT_TRUE(personal_data_.GetCreditCards().empty());
-#else
- ASSERT_EQ(1U, personal_data_.GetCreditCards().size());
- const CreditCard* const saved_card = personal_data_.GetCreditCards()[0];
- EXPECT_EQ(CreditCard::OK, saved_card->GetServerStatus());
- EXPECT_EQ(base::ASCIIToUTF16("1111"), saved_card->LastFourDigits());
- EXPECT_EQ(kVisaCard, saved_card->network());
- EXPECT_EQ(11, saved_card->expiration_month());
- EXPECT_EQ(std::stoi(NextYear()), saved_card->expiration_year());
- EXPECT_EQ(server_id, saved_card->server_id());
- EXPECT_EQ(CreditCard::FULL_SERVER_CARD, saved_card->record_type());
- EXPECT_EQ(base::ASCIIToUTF16(card_number), saved_card->number());
-#endif
+
+ // Verify that the correct histogram entry (and only that) was logged.
+ ExpectUniqueCardUploadDecision(histogram_tester,
+ AutofillMetrics::UPLOAD_OFFERED);
+ // Verify that the correct UKM was logged.
+ ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED);
+ // Verify the histogram entry for recent profile modification.
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.HasModifiedProfile.CreditCardFormSubmission", true, 1);
+ // Verify that UMA for "DaysSincePreviousUse" was not logged because we
+ // modified the profile.
+ histogram_tester.ExpectTotalCount(
+ "Autofill.DaysSincePreviousUseAtSubmission.Profile", 0);
}
-TEST_F(CreditCardSaveManagerTest, UploadCreditCard_FeatureNotEnabled) {
+// Tests that a credit card inferred from a form with a credit card first and
+// last name can be uploaded.
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_FirstAndLastName) {
+ personal_data_.ClearCreditCards();
personal_data_.ClearProfiles();
- credit_card_save_manager_->SetCreditCardUploadEnabled(false);
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
// Create, fill and submit an address form in order to establish a recent
// profile which can be selected for the upload request.
FormData address_form;
test::CreateTestAddressFormData(&address_form);
FormsSeen(std::vector<FormData>(1, address_form));
+ ExpectUniqueFillableFormParsedUkm();
+
ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
FormSubmitted(address_form);
- // Set up our credit card form data.
+ // Set up our credit card form data with credit card first and last name
+ // fields.
FormData credit_card_form;
- CreateTestCreditCardFormData(&credit_card_form, true, false);
+ CreateTestCreditCardFormData(&credit_card_form, /*is_https=*/true,
+ /*use_month_type=*/false, /*split_names=*/true);
FormsSeen(std::vector<FormData>(1, credit_card_form));
// Edit the data, and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16("123");
+ credit_card_form.fields[0].value = ASCIIToUTF16("Flo");
+ credit_card_form.fields[1].value = ASCIIToUTF16("Master");
+ credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111");
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[4].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[5].value = ASCIIToUTF16("123");
base::HistogramTester histogram_tester;
- // The save prompt should be shown instead of doing an upload.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _));
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+ EXPECT_THAT(
+ payments_client_->active_experiments_in_request(),
+ UnorderedElementsAre(kAutofillUpstreamUpdatePromptExplanation.name));
- // Verify that no histogram entry was logged.
- histogram_tester.ExpectTotalCount("Autofill.CardUploadDecisionMetric", 0);
+ // Server did not send a server_id, expect copy of card is not stored.
+ EXPECT_TRUE(personal_data_.GetCreditCards().empty());
+ // Verify that the correct histogram entry (and only that) was logged.
+ ExpectUniqueCardUploadDecision(histogram_tester,
+ AutofillMetrics::UPLOAD_OFFERED);
+ // Verify that the correct UKM was logged.
+ ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED);
+ // Verify the histogram entry for recent profile modification.
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.HasModifiedProfile.CreditCardFormSubmission", true, 1);
+ // Verify that UMA for "DaysSincePreviousUse" was not logged because we
+ // modified the profile.
+ histogram_tester.ExpectTotalCount(
+ "Autofill.DaysSincePreviousUseAtSubmission.Profile", 0);
+ histogram_tester.ExpectTotalCount(
+ "Autofill.SaveCardWithFirstAndLastNameOffered.Local", 0);
+ histogram_tester.ExpectTotalCount(
+ "Autofill.SaveCardWithFirstAndLastNameOffered.Server", 1);
+ histogram_tester.ExpectTotalCount(
+ "Autofill.SaveCardWithFirstAndLastNameComplete.Server", 1);
}
-TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CvcUnavailable) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
+// Tests that a credit card inferred from a form with a credit card first and
+// last name can be uploaded when the last name comes before first name on the
+// form.
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_LastAndFirstName) {
+ personal_data_.ClearCreditCards();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -571,48 +656,87 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CvcUnavailable) {
ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
FormSubmitted(address_form);
- // Set up our credit card form data.
+ // Set up our credit card form data with credit card first and last name
+ // fields.
FormData credit_card_form;
- CreateTestCreditCardFormData(&credit_card_form, true, false);
+ credit_card_form.name = ASCIIToUTF16("MyForm");
+ credit_card_form.origin = GURL("https://myform.com/form.html");
+ credit_card_form.action = GURL("https://myform.com/submit.html");
+ credit_card_form.main_frame_origin =
+ url::Origin::Create(GURL("https://myform_root.com/form.html"));
+
+ FormFieldData field;
+ test::CreateTestFormField("Last Name on Card", "lastnameoncard", "", "text",
+ &field);
+ field.autocomplete_attribute = "cc-family-name";
+ credit_card_form.fields.push_back(field);
+ test::CreateTestFormField("First Name on Card", "firstnameoncard", "", "text",
+ &field);
+ field.autocomplete_attribute = "cc-given-name";
+ credit_card_form.fields.push_back(field);
+ field.autocomplete_attribute = "";
+ test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field);
+ credit_card_form.fields.push_back(field);
+ test::CreateTestFormField("Expiration Date", "ccmonth", "", "text", &field);
+ credit_card_form.fields.push_back(field);
+ test::CreateTestFormField("", "ccyear", "", "text", &field);
+ credit_card_form.fields.push_back(field);
+ test::CreateTestFormField("CVC", "cvc", "", "text", &field);
+ credit_card_form.fields.push_back(field);
FormsSeen(std::vector<FormData>(1, credit_card_form));
- ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */);
// Edit the data, and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16(""); // CVC MISSING
+ credit_card_form.fields[0].value = ASCIIToUTF16("Master");
+ credit_card_form.fields[1].value = ASCIIToUTF16("Flo");
+ credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111");
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[4].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[5].value = ASCIIToUTF16("123");
base::HistogramTester histogram_tester;
- // With the offer-to-save decision deferred to Google Payments, Payments can
- // still decide to allow saving despite the missing CVC value.
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+ EXPECT_THAT(
+ payments_client_->active_experiments_in_request(),
+ UnorderedElementsAre(kAutofillUpstreamUpdatePromptExplanation.name));
- // Verify that the correct histogram entries were logged.
- ExpectCardUploadDecision(histogram_tester, AutofillMetrics::UPLOAD_OFFERED);
- ExpectCardUploadDecision(histogram_tester,
- AutofillMetrics::CVC_VALUE_NOT_FOUND);
+ // Server did not send a server_id, expect copy of card is not stored.
+ EXPECT_TRUE(personal_data_.GetCreditCards().empty());
+ // Verify that the correct histogram entry (and only that) was logged.
+ ExpectUniqueCardUploadDecision(histogram_tester,
+ AutofillMetrics::UPLOAD_OFFERED);
// Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED |
- AutofillMetrics::CVC_VALUE_NOT_FOUND);
+ ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED);
+ // Verify the histogram entry for recent profile modification.
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.HasModifiedProfile.CreditCardFormSubmission", true, 1);
+ // Verify that UMA for "DaysSincePreviousUse" was not logged because we
+ // modified the profile.
+ histogram_tester.ExpectTotalCount(
+ "Autofill.DaysSincePreviousUseAtSubmission.Profile", 0);
+ histogram_tester.ExpectTotalCount(
+ "Autofill.SaveCardWithFirstAndLastNameOffered.Local", 0);
+ histogram_tester.ExpectTotalCount(
+ "Autofill.SaveCardWithFirstAndLastNameOffered.Server", 1);
+ histogram_tester.ExpectTotalCount(
+ "Autofill.SaveCardWithFirstAndLastNameComplete.Server", 1);
}
-TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_CvcUnavailable_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
+TEST_F(CreditCardSaveManagerTest, UploadCreditCardAndSaveCopy) {
+ personal_data_.ClearCreditCards();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ const char* const server_id = "InstrumentData:1234";
+ payments_client_->SetServerIdForCardUpload(server_id);
+
// Create, fill and submit an address form in order to establish a recent
// profile which can be selected for the upload request.
FormData address_form;
test::CreateTestAddressFormData(&address_form);
FormsSeen(std::vector<FormData>(1, address_form));
- ExpectUniqueFillableFormParsedUkm();
ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
FormSubmitted(address_form);
@@ -621,30 +745,72 @@ TEST_F(CreditCardSaveManagerTest,
FormData credit_card_form;
CreateTestCreditCardFormData(&credit_card_form, true, false);
FormsSeen(std::vector<FormData>(1, credit_card_form));
- ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */);
+
+ // Edit the data, and submit.
+ const char* const card_number = "4111111111111111";
+ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
+ credit_card_form.fields[1].value = ASCIIToUTF16(card_number);
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+ FormSubmitted(credit_card_form);
+
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+ EXPECT_TRUE(personal_data_.GetLocalCreditCards().empty());
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ // See |OfferStoreUnmaskedCards|
+ EXPECT_TRUE(personal_data_.GetCreditCards().empty());
+#else
+ ASSERT_EQ(1U, personal_data_.GetCreditCards().size());
+ const CreditCard* const saved_card = personal_data_.GetCreditCards()[0];
+ EXPECT_EQ(CreditCard::OK, saved_card->GetServerStatus());
+ EXPECT_EQ(base::ASCIIToUTF16("1111"), saved_card->LastFourDigits());
+ EXPECT_EQ(kVisaCard, saved_card->network());
+ EXPECT_EQ(std::stoi(NextMonth()), saved_card->expiration_month());
+ EXPECT_EQ(std::stoi(NextYear()), saved_card->expiration_year());
+ EXPECT_EQ(server_id, saved_card->server_id());
+ EXPECT_EQ(CreditCard::FULL_SERVER_CARD, saved_card->record_type());
+ EXPECT_EQ(base::ASCIIToUTF16(card_number), saved_card->number());
+#endif
+}
+
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_FeatureNotEnabled) {
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(false);
+
+ // Create, fill and submit an address form in order to establish a recent
+ // profile which can be selected for the upload request.
+ FormData address_form;
+ test::CreateTestAddressFormData(&address_form);
+ FormsSeen(std::vector<FormData>(1, address_form));
+ ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
+ FormSubmitted(address_form);
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16(""); // CVC MISSING
+ credit_card_form.fields[4].value = ASCIIToUTF16("123");
base::HistogramTester histogram_tester;
- // Neither a local save nor an upload should happen in this case.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+ // The save prompt should be shown instead of doing an upload.
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _));
FormSubmitted(credit_card_form);
EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(histogram_tester,
- AutofillMetrics::CVC_VALUE_NOT_FOUND);
- // Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(AutofillMetrics::CVC_VALUE_NOT_FOUND);
+ // Verify that no histogram entry was logged.
+ histogram_tester.ExpectTotalCount("Autofill.CardUploadDecisionMetric", 0);
}
-TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CvcInvalidLength) {
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CvcUnavailable) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -653,6 +819,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CvcInvalidLength) {
FormData address_form;
test::CreateTestAddressFormData(&address_form);
FormsSeen(std::vector<FormData>(1, address_form));
+ ExpectUniqueFillableFormParsedUkm();
+
ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
FormSubmitted(address_form);
@@ -660,18 +828,19 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CvcInvalidLength) {
FormData credit_card_form;
CreateTestCreditCardFormData(&credit_card_form, true, false);
FormsSeen(std::vector<FormData>(1, credit_card_form));
+ ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */);
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16("1234");
+ credit_card_form.fields[4].value = ASCIIToUTF16(""); // CVC MISSING
base::HistogramTester histogram_tester;
// With the offer-to-save decision deferred to Google Payments, Payments can
- // still decide to allow saving despite the invalid CVC value.
+ // still decide to allow saving despite the missing CVC value.
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
@@ -679,15 +848,13 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CvcInvalidLength) {
// Verify that the correct histogram entries were logged.
ExpectCardUploadDecision(histogram_tester, AutofillMetrics::UPLOAD_OFFERED);
ExpectCardUploadDecision(histogram_tester,
- AutofillMetrics::INVALID_CVC_VALUE);
+ AutofillMetrics::CVC_VALUE_NOT_FOUND);
// Verify that the correct UKM was logged.
ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED |
- AutofillMetrics::INVALID_CVC_VALUE);
+ AutofillMetrics::CVC_VALUE_NOT_FOUND);
}
-TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_CvcInvalidLength_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CvcInvalidLength) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -707,22 +874,25 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("1234");
base::HistogramTester histogram_tester;
- // Neither a local save nor an upload should happen in this case.
+ // With the offer-to-save decision deferred to Google Payments, Payments can
+ // still decide to allow saving despite the invalid CVC value.
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(histogram_tester,
- AutofillMetrics::INVALID_CVC_VALUE);
+ // Verify that the correct histogram entries were logged.
+ ExpectCardUploadDecision(histogram_tester, AutofillMetrics::UPLOAD_OFFERED);
+ ExpectCardUploadDecision(histogram_tester,
+ AutofillMetrics::INVALID_CVC_VALUE);
// Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(AutofillMetrics::INVALID_CVC_VALUE);
+ ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED |
+ AutofillMetrics::INVALID_CVC_VALUE);
}
TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MultipleCvcFields) {
@@ -768,7 +938,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_MultipleCvcFields) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // CVC MISSING
credit_card_form.fields[5].value = ASCIIToUTF16("123");
@@ -826,7 +996,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoCvcFieldOnForm) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
base::HistogramTester histogram_tester;
@@ -847,64 +1017,6 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoCvcFieldOnForm) {
}
TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_NoCvcFieldOnForm_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
- credit_card_save_manager_->SetCreditCardUploadEnabled(true);
-
- // Remove the profiles that were created in the TestPersonalDataManager
- // constructor because they would result in conflicting names that would
- // prevent the upload.
- personal_data_.ClearProfiles();
-
- // Create, fill and submit an address form in order to establish a recent
- // profile which can be selected for the upload request.
- FormData address_form;
- test::CreateTestAddressFormData(&address_form);
- FormsSeen(std::vector<FormData>(1, address_form));
- ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
- FormSubmitted(address_form);
-
- // Set up our credit card form data. Note that CVC field is missing.
- FormData credit_card_form;
- credit_card_form.name = ASCIIToUTF16("MyForm");
- credit_card_form.origin = GURL("https://myform.com/form.html");
- credit_card_form.action = GURL("https://myform.com/submit.html");
- credit_card_form.main_frame_origin =
- url::Origin::Create(GURL("http://myform_root.com/form.html"));
-
- FormFieldData field;
- test::CreateTestFormField("Card Name", "cardname", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Expiration Month", "ccmonth", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Expiration Year", "ccyear", "", "text", &field);
- credit_card_form.fields.push_back(field);
-
- FormsSeen(std::vector<FormData>(1, credit_card_form));
-
- // Edit the data, and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
-
- base::HistogramTester histogram_tester;
-
- // Upload should not happen because CVC field was not found.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
- FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
-
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(histogram_tester,
- AutofillMetrics::CVC_FIELD_NOT_FOUND);
- // Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(AutofillMetrics::CVC_FIELD_NOT_FOUND);
-}
-
-TEST_F(CreditCardSaveManagerTest,
UploadCreditCard_NoCvcFieldOnForm_InvalidCvcInNonCvcField) {
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -946,7 +1058,7 @@ TEST_F(CreditCardSaveManagerTest,
// Enter an invalid cvc in "Random Field" and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("1234");
@@ -967,68 +1079,6 @@ TEST_F(CreditCardSaveManagerTest,
AutofillMetrics::CVC_FIELD_NOT_FOUND);
}
-TEST_F(
- CreditCardSaveManagerTest,
- UploadCreditCard_NoCvcFieldOnForm_InvalidCvcInNonCvcField_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
- credit_card_save_manager_->SetCreditCardUploadEnabled(true);
-
- // Remove the profiles that were created in the TestPersonalDataManager
- // constructor because they would result in conflicting names that would
- // prevent the upload.
- personal_data_.ClearProfiles();
-
- // Create, fill and submit an address form in order to establish a recent
- // profile which can be selected for the upload request.
- FormData address_form;
- test::CreateTestAddressFormData(&address_form);
- FormsSeen({address_form});
- ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
- FormSubmitted(address_form);
-
- // Set up our credit card form data. Note that CVC field is missing.
- FormData credit_card_form;
- credit_card_form.name = ASCIIToUTF16("MyForm");
- credit_card_form.origin = GURL("https://myform.com/form.html");
- credit_card_form.action = GURL("https://myform.com/submit.html");
- credit_card_form.main_frame_origin =
- url::Origin::Create(GURL("http://myform_root.com/form.html"));
-
- FormFieldData field;
- test::CreateTestFormField("Card Name", "cardname", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Expiration Month", "ccmonth", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Expiration Year", "ccyear", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Random Field", "random", "", "text", &field);
- credit_card_form.fields.push_back(field);
-
- FormsSeen({credit_card_form});
-
- // Enter an invalid cvc in "Random Field" and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16("1234");
-
- base::HistogramTester histogram_tester;
-
- // Upload should not happen because CVC field was not found.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
- FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
-
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(histogram_tester,
- AutofillMetrics::CVC_FIELD_NOT_FOUND);
- // Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(AutofillMetrics::CVC_FIELD_NOT_FOUND);
-}
-
TEST_F(CreditCardSaveManagerTest,
UploadCreditCard_NoCvcFieldOnForm_CvcInNonCvcField) {
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -1071,7 +1121,7 @@ TEST_F(CreditCardSaveManagerTest,
// Enter a valid cvc in "Random Field" and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -1095,69 +1145,6 @@ TEST_F(CreditCardSaveManagerTest,
}
TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_NoCvcFieldOnForm_CvcInNonCvcField_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
- credit_card_save_manager_->SetCreditCardUploadEnabled(true);
-
- // Remove the profiles that were created in the TestPersonalDataManager
- // constructor because they would result in conflicting names that would
- // prevent the upload.
- personal_data_.ClearProfiles();
-
- // Create, fill and submit an address form in order to establish a recent
- // profile which can be selected for the upload request.
- FormData address_form;
- test::CreateTestAddressFormData(&address_form);
- FormsSeen({address_form});
- ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
- FormSubmitted(address_form);
-
- // Set up our credit card form data. Note that CVC field is missing.
- FormData credit_card_form;
- credit_card_form.name = ASCIIToUTF16("MyForm");
- credit_card_form.origin = GURL("https://myform.com/form.html");
- credit_card_form.action = GURL("https://myform.com/submit.html");
- credit_card_form.main_frame_origin =
- url::Origin::Create(GURL("http://myform_root.com/form.html"));
-
- FormFieldData field;
- test::CreateTestFormField("Card Name", "cardname", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Expiration Month", "ccmonth", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Expiration Year", "ccyear", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Random Field", "random", "", "text", &field);
- credit_card_form.fields.push_back(field);
-
- FormsSeen({credit_card_form});
-
- // Enter a valid cvc in "Random Field" and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16("123");
-
- base::HistogramTester histogram_tester;
-
- // Upload should not happen because CVC field was not found.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
- FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
-
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(
- histogram_tester,
- AutofillMetrics::FOUND_POSSIBLE_CVC_VALUE_IN_NON_CVC_FIELD);
- // Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(
- AutofillMetrics::FOUND_POSSIBLE_CVC_VALUE_IN_NON_CVC_FIELD);
-}
-
-TEST_F(CreditCardSaveManagerTest,
UploadCreditCard_NoCvcFieldOnForm_CvcInAddressField) {
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -1199,7 +1186,7 @@ TEST_F(CreditCardSaveManagerTest,
// Enter a valid cvc in "Random Field" and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -1220,67 +1207,6 @@ TEST_F(CreditCardSaveManagerTest,
AutofillMetrics::CVC_FIELD_NOT_FOUND);
}
-TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_NoCvcFieldOnForm_CvcInAddressField_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
- credit_card_save_manager_->SetCreditCardUploadEnabled(true);
-
- // Remove the profiles that were created in the TestPersonalDataManager
- // constructor because they would result in conflicting names that would
- // prevent the upload.
- personal_data_.ClearProfiles();
-
- // Create, fill and submit an address form in order to establish a recent
- // profile which can be selected for the upload request.
- FormData address_form;
- test::CreateTestAddressFormData(&address_form);
- FormsSeen({address_form});
- ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
- FormSubmitted(address_form);
-
- // Set up our credit card form data. Note that CVC field is missing.
- FormData credit_card_form;
- credit_card_form.name = ASCIIToUTF16("MyForm");
- credit_card_form.origin = GURL("https://myform.com/form.html");
- credit_card_form.action = GURL("https://myform.com/submit.html");
- credit_card_form.main_frame_origin =
- url::Origin::Create(GURL("http://myform_root.com/form.html"));
-
- FormFieldData field;
- test::CreateTestFormField("Card Name", "cardname", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Expiration Month", "ccmonth", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Expiration Year", "ccyear", "", "text", &field);
- credit_card_form.fields.push_back(field);
- test::CreateTestFormField("Address Line 1", "addr1", "", "text", &field);
- credit_card_form.fields.push_back(field);
-
- FormsSeen({credit_card_form});
-
- // Enter a valid cvc in "Random Field" and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16("123");
-
- base::HistogramTester histogram_tester;
-
- // Upload should not happen because CVC field was not found.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
- FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
-
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(histogram_tester,
- AutofillMetrics::CVC_FIELD_NOT_FOUND);
- // Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(AutofillMetrics::CVC_FIELD_NOT_FOUND);
-}
-
TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoProfileAvailable) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -1295,7 +1221,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoProfileAvailable) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Bob Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -1317,41 +1243,6 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoProfileAvailable) {
AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS_PROFILE);
}
-TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_NoProfileAvailable_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
- personal_data_.ClearProfiles();
- credit_card_save_manager_->SetCreditCardUploadEnabled(true);
-
- // Don't fill or submit an address form.
-
- // Set up our credit card form data.
- FormData credit_card_form;
- CreateTestCreditCardFormData(&credit_card_form, true, false);
- FormsSeen(std::vector<FormData>(1, credit_card_form));
-
- // Edit the data, and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Bob Master");
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16("123");
-
- base::HistogramTester histogram_tester;
-
- // Neither a local save nor an upload should happen in this case.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
- FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
-
- // Verify that the correct histogram entries are logged.
- ExpectUniqueCardUploadDecision(
- histogram_tester, AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS_PROFILE);
- // Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(
- AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS_PROFILE);
-}
-
TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoRecentlyUsedProfile) {
// Create the test clock and set the time to a specific value.
TestAutofillClock test_clock;
@@ -1379,7 +1270,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoRecentlyUsedProfile) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -1406,58 +1297,6 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoRecentlyUsedProfile) {
}
TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_NoRecentlyUsedProfile_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
- // Create the test clock and set the time to a specific value.
- TestAutofillClock test_clock;
- test_clock.SetNow(kArbitraryTime);
-
- personal_data_.ClearProfiles();
- credit_card_save_manager_->SetCreditCardUploadEnabled(true);
-
- // Create, fill and submit an address form in order to establish a profile.
- FormData address_form;
- test::CreateTestAddressFormData(&address_form);
- FormsSeen({address_form});
-
- ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
- FormSubmitted(address_form);
-
- // Set the current time to another value.
- test_clock.SetNow(kMuchLaterTime);
-
- // Set up our credit card form data.
- FormData credit_card_form;
- CreateTestCreditCardFormData(&credit_card_form, true, false);
- FormsSeen(std::vector<FormData>(1, credit_card_form));
-
- // Edit the data, and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16("123");
-
- base::HistogramTester histogram_tester;
-
- // Neither a local save nor an upload should happen in this case.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
- FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
-
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(
- histogram_tester,
- AutofillMetrics::UPLOAD_NOT_OFFERED_NO_RECENTLY_USED_ADDRESS);
- // Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(
- AutofillMetrics::UPLOAD_NOT_OFFERED_NO_RECENTLY_USED_ADDRESS);
- // Verify the histogram entry for recent profile modification.
- histogram_tester.ExpectUniqueSample(
- "Autofill.HasModifiedProfile.CreditCardFormSubmission", false, 1);
-}
-
-TEST_F(CreditCardSaveManagerTest,
UploadCreditCard_CvcUnavailableAndNoProfileAvailable) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -1472,7 +1311,7 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // CVC MISSING
@@ -1496,46 +1335,6 @@ TEST_F(CreditCardSaveManagerTest,
AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS_PROFILE);
}
-TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_CvcUnavailableAndNoProfileAvailable_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
- personal_data_.ClearProfiles();
- credit_card_save_manager_->SetCreditCardUploadEnabled(true);
-
- // Don't fill or submit an address form.
-
- // Set up our credit card form data.
- FormData credit_card_form;
- CreateTestCreditCardFormData(&credit_card_form, true, false);
- FormsSeen(std::vector<FormData>(1, credit_card_form));
-
- // Edit the data, and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16(""); // CVC MISSING
-
- base::HistogramTester histogram_tester;
-
- // Neither a local save nor an upload should happen in this case.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
- FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
-
- // Verify that the correct histogram entries were logged.
- ExpectCardUploadDecision(histogram_tester,
- AutofillMetrics::CVC_VALUE_NOT_FOUND);
- ExpectCardUploadDecision(
- histogram_tester, AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS_PROFILE);
- // Verify that the correct UKM was logged.
- ExpectMetric(UkmCardUploadDecisionType::kUploadDecisionName,
- UkmCardUploadDecisionType::kEntryName,
- AutofillMetrics::CVC_VALUE_NOT_FOUND |
- AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS_PROFILE,
- 1 /* expected_num_matching_entries */);
-}
-
TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoNameAvailable) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -1556,7 +1355,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoNameAvailable) {
// Edit the data, but don't include a name, and submit.
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -1578,46 +1377,6 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoNameAvailable) {
}
TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_NoNameAvailable_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
- personal_data_.ClearProfiles();
- credit_card_save_manager_->SetCreditCardUploadEnabled(true);
-
- // Create, fill and submit an address form in order to establish a recent
- // profile which can be selected for the upload request.
- FormData address_form;
- test::CreateTestAddressFormData(&address_form);
- FormsSeen(std::vector<FormData>(1, address_form));
- // But omit the name:
- ManuallyFillAddressForm("", "", "77401", "US", &address_form);
- FormSubmitted(address_form);
-
- // Set up our credit card form data.
- FormData credit_card_form;
- CreateTestCreditCardFormData(&credit_card_form, true, false);
- FormsSeen(std::vector<FormData>(1, credit_card_form));
-
- // Edit the data, but don't include a name, and submit.
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16("123");
-
- base::HistogramTester histogram_tester;
-
- // Neither a local save nor an upload should happen in this case.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
- FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
-
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(histogram_tester,
- AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME);
- // Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME);
-}
-
-TEST_F(CreditCardSaveManagerTest,
UploadCreditCard_NoNameAvailableAndNoProfileAvailable) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -1631,7 +1390,7 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, but don't include a name, and submit.
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -1656,46 +1415,6 @@ TEST_F(CreditCardSaveManagerTest,
AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME);
}
-TEST_F(
- CreditCardSaveManagerTest,
- UploadCreditCard_NoNameAvailableAndNoProfileAvailable_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
- personal_data_.ClearProfiles();
- credit_card_save_manager_->SetCreditCardUploadEnabled(true);
-
- // Don't fill or submit an address form.
-
- // Set up our credit card form data.
- FormData credit_card_form;
- CreateTestCreditCardFormData(&credit_card_form, true, false);
- FormsSeen(std::vector<FormData>(1, credit_card_form));
-
- // Edit the data, but don't include a name, and submit.
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16("123");
-
- base::HistogramTester histogram_tester;
-
- // Neither a local save nor an upload should happen in this case.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
- FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
-
- // Verify that the correct histogram entries were logged.
- ExpectCardUploadDecision(
- histogram_tester, AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS_PROFILE);
- ExpectCardUploadDecision(histogram_tester,
- AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME);
- // Verify that the correct UKM was logged.
- ExpectMetric(UkmCardUploadDecisionType::kUploadDecisionName,
- UkmCardUploadDecisionType::kEntryName,
- AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS_PROFILE |
- AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME,
- 1 /* expected_num_matching_entries */);
-}
-
TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ZipCodesConflict) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -1726,7 +1445,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ZipCodesConflict) {
// Edit the data and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -1749,57 +1468,6 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ZipCodesConflict) {
}
TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_ZipCodesConflict_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
- personal_data_.ClearProfiles();
- credit_card_save_manager_->SetCreditCardUploadEnabled(true);
-
- // Create, fill and submit two address forms with different zip codes.
- FormData address_form1, address_form2;
- test::CreateTestAddressFormData(&address_form1);
- test::CreateTestAddressFormData(&address_form2);
-
- std::vector<FormData> address_forms;
- address_forms.push_back(address_form1);
- address_forms.push_back(address_form2);
- FormsSeen(address_forms);
- ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */);
-
- ManuallyFillAddressForm("Flo", "Master", "77401-8294", "US", &address_form1);
- FormSubmitted(address_form1);
-
- ManuallyFillAddressForm("Flo", "Master", "77401-1234", "US", &address_form2);
- FormSubmitted(address_form2);
-
- // Set up our credit card form data.
- FormData credit_card_form;
- CreateTestCreditCardFormData(&credit_card_form, true, false);
- FormsSeen(std::vector<FormData>(1, credit_card_form));
- ExpectFillableFormParsedUkm(3 /* num_fillable_forms_parsed */);
-
- // Edit the data and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16("123");
-
- base::HistogramTester histogram_tester;
-
- // Neither a local save nor an upload should happen in this case.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
- FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
-
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(
- histogram_tester, AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_ZIPS);
- // Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(
- AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_ZIPS);
-}
-
-TEST_F(CreditCardSaveManagerTest,
UploadCreditCard_ZipCodesDoNotDiscardWhitespace) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -1828,7 +1496,7 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -1850,53 +1518,6 @@ TEST_F(CreditCardSaveManagerTest,
AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_ZIPS);
}
-TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_ZipCodesDoNotDiscardWhitespace_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
- personal_data_.ClearProfiles();
- credit_card_save_manager_->SetCreditCardUploadEnabled(true);
-
- // Create two separate profiles with different zip codes. Must directly add
- // instead of submitting a form, because they're deduped on form submit.
- AutofillProfile profile1;
- profile1.set_guid("00000000-0000-0000-0000-000000000001");
- profile1.SetInfo(NAME_FULL, ASCIIToUTF16("Flo Master"), "en-US");
- profile1.SetInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("H3B2Y5"), "en-US");
- profile1.SetInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("CA"), "en-US");
- personal_data_.AddProfile(profile1);
-
- AutofillProfile profile2;
- profile2.set_guid("00000000-0000-0000-0000-000000000002");
- profile2.SetInfo(NAME_FULL, ASCIIToUTF16("Flo Master"), "en-US");
- profile2.SetInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("h3b 2y5"), "en-US");
- profile2.SetInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("CA"), "en-US");
- personal_data_.AddProfile(profile2);
-
- // Set up our credit card form data.
- FormData credit_card_form;
- CreateTestCreditCardFormData(&credit_card_form, true, false);
- FormsSeen({credit_card_form});
-
- // Edit the data and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16("123");
-
- base::HistogramTester histogram_tester;
-
- // Neither a local save nor an upload should happen in this case.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
- FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
- EXPECT_TRUE(payments_client_->GetActiveExperimentsSetInRequest().empty());
-
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(
- histogram_tester, AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_ZIPS);
-}
-
TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ZipCodesHavePrefixMatch) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -1925,7 +1546,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_ZipCodesHavePrefixMatch) {
// Edit the data and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -1970,7 +1591,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoZipCodeAvailable) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -1991,53 +1612,6 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoZipCodeAvailable) {
AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE);
}
-TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_NoZipCodeAvailable_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
- personal_data_.ClearProfiles();
- credit_card_save_manager_->SetCreditCardUploadEnabled(true);
-
- // Create, fill and submit an address form in order to establish a recent
- // profile which can be selected for the upload request.
- FormData address_form;
- test::CreateTestAddressFormData(&address_form);
- FormsSeen(std::vector<FormData>(1, address_form));
- // Autofill's validation requirements for Venezuala ("VE", see
- // src/components/autofill/core/browser/country_data.cc) do not require zip
- // codes. We use Venezuala here because to use the US (or one of many other
- // countries which autofill requires a zip code for) would result in no
- // address being imported at all, and then we never reach the check for
- // missing zip code in the upload code.
- ManuallyFillAddressForm("Flo", "Master", "" /* zip_code */, "Venezuela",
- &address_form);
- FormSubmitted(address_form);
-
- // Set up our credit card form data.
- FormData credit_card_form;
- CreateTestCreditCardFormData(&credit_card_form, true, false);
- FormsSeen(std::vector<FormData>(1, credit_card_form));
-
- // Edit the data, and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16("123");
-
- base::HistogramTester histogram_tester;
-
- // Neither a local save nor an upload should happen in this case.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
- FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
-
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(
- histogram_tester, AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE);
- // Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE);
-}
-
TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CCFormHasMiddleInitial) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2065,7 +1639,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CCFormHasMiddleInitial) {
// submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo W. Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -2107,7 +1681,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NoMiddleInitialInCCForm) {
// Edit the data, but do not use middle initial.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -2145,7 +1719,7 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the name by adding a middle name.
credit_card_form.fields[0].value = ASCIIToUTF16("John Quincy Adams");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -2167,17 +1741,15 @@ TEST_F(CreditCardSaveManagerTest,
AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES);
}
-TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_CCFormHasCardholderMiddleName_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CCFormHasAddressMiddleName) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
- // Create, fill and submit address form without middle name.
+ // Create, fill and submit address form with middle name.
FormData address_form;
test::CreateTestAddressFormData(&address_form);
FormsSeen({address_form});
- ManuallyFillAddressForm("John", "Adams", "77401", "US", &address_form);
+ ManuallyFillAddressForm("John Quincy", "Adams", "77401", "US", &address_form);
FormSubmitted(address_form);
// Set up our credit card form data.
@@ -2185,45 +1757,60 @@ TEST_F(CreditCardSaveManagerTest,
CreateTestCreditCardFormData(&credit_card_form, true, false);
FormsSeen({credit_card_form});
- // Edit the name by adding a middle name.
- credit_card_form.fields[0].value = ASCIIToUTF16("John Quincy Adams");
+ // Edit the name by removing middle name.
+ credit_card_form.fields[0].value = ASCIIToUTF16("John Adams");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
base::HistogramTester histogram_tester;
- // Names match loosely but middle name mismatches. Upload should not happen.
+ // With the offer-to-save decision deferred to Google Payments, Payments can
+ // still decide to allow saving despite the mismatching names.
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(
+ // Verify that the correct histogram entries were logged.
+ ExpectCardUploadDecision(histogram_tester, AutofillMetrics::UPLOAD_OFFERED);
+ ExpectCardUploadDecision(
histogram_tester, AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES);
+ // Verify that the correct UKM was logged.
+ ExpectCardUploadDecisionUkm(
+ AutofillMetrics::UPLOAD_OFFERED |
+ AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES);
}
-TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CCFormHasAddressMiddleName) {
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NamesCanMismatch) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
- // Create, fill and submit address form with middle name.
- FormData address_form;
- test::CreateTestAddressFormData(&address_form);
- FormsSeen({address_form});
- ManuallyFillAddressForm("John Quincy", "Adams", "77401", "US", &address_form);
- FormSubmitted(address_form);
+ // Create, fill and submit two address forms with different names.
+ FormData address_form1, address_form2;
+ test::CreateTestAddressFormData(&address_form1);
+ test::CreateTestAddressFormData(&address_form2);
+
+ std::vector<FormData> address_forms;
+ address_forms.push_back(address_form1);
+ address_forms.push_back(address_form2);
+ FormsSeen(address_forms);
+
+ ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form1);
+ FormSubmitted(address_form1);
+
+ ManuallyFillAddressForm("Master", "Blaster", "77401", "US", &address_form2);
+ FormSubmitted(address_form2);
// Set up our credit card form data.
FormData credit_card_form;
CreateTestCreditCardFormData(&credit_card_form, true, false);
- FormsSeen({credit_card_form});
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
- // Edit the name by removing middle name.
- credit_card_form.fields[0].value = ASCIIToUTF16("John Adams");
+ // Edit the data, but use yet another name, and submit.
+ credit_card_form.fields[0].value = ASCIIToUTF16("Bob Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -2245,192 +1832,476 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_CCFormHasAddressMiddleName) {
AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES);
}
-TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_CCFormHasAddressMiddleName_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_IgnoreOldProfiles) {
+ // Create the test clock and set the time to a specific value.
+ TestAutofillClock test_clock;
+ test_clock.SetNow(kArbitraryTime);
+
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
- // Create, fill and submit address form with middle name.
+ // Create, fill and submit two address forms with different names.
+ FormData address_form1, address_form2;
+ test::CreateTestAddressFormData(&address_form1);
+ test::CreateTestAddressFormData(&address_form2);
+ FormsSeen({address_form1, address_form2});
+
+ ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form1);
+ FormSubmitted(address_form1);
+
+ // Advance the current time. Since |address_form1| will not be a recently
+ // used address profile, we will not include it in the candidate profiles.
+ test_clock.SetNow(kMuchLaterTime);
+
+ ManuallyFillAddressForm("Master", "Blaster", "77401", "US", &address_form2);
+ FormSubmitted(address_form2);
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, but use yet another name, and submit.
+ credit_card_form.fields[0].value = ASCIIToUTF16("Master Blaster");
+ credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+ base::HistogramTester histogram_tester;
+
+ // Name matches recently used profile, should offer upload.
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+ FormSubmitted(credit_card_form);
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+
+ // Verify that the correct histogram entry (and only that) was logged.
+ ExpectUniqueCardUploadDecision(histogram_tester,
+ AutofillMetrics::UPLOAD_OFFERED);
+}
+
+// Requesting cardholder name currently not available on Android.
+#if !defined(OS_ANDROID)
+TEST_F(
+ CreditCardSaveManagerTest,
+ UploadCreditCard_RequestCardholderNameIfNameMissingAndNoPaymentsCustomer) {
+ scoped_feature_list_.InitAndEnableFeature(
+ kAutofillUpstreamEditableCardholderName);
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+
+ // Create, fill and submit an address form in order to establish a recent
+ // profile which can be selected for the upload request.
FormData address_form;
test::CreateTestAddressFormData(&address_form);
- FormsSeen({address_form});
- ManuallyFillAddressForm("John Quincy", "Adams", "77401", "US", &address_form);
+ FormsSeen(std::vector<FormData>(1, address_form));
+ // But omit the name:
+ ManuallyFillAddressForm("", "", "77401", "US", &address_form);
FormSubmitted(address_form);
// Set up our credit card form data.
FormData credit_card_form;
CreateTestCreditCardFormData(&credit_card_form, true, false);
- FormsSeen({credit_card_form});
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
- // Edit the name by removing middle name.
- credit_card_form.fields[0].value = ASCIIToUTF16("John Adams");
+ // Edit the data, but don't include a name, and submit.
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
base::HistogramTester histogram_tester;
- // Names match loosely but middle name mismatches. Upload should not happen.
+ // With the offer-to-save decision deferred to Google Payments, Payments can
+ // still decide to allow saving despite the missing name.
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(
- histogram_tester, AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES);
- // Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(
- AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES);
+ // Verify that the correct histogram entry and DetectedValue for "Cardholder
+ // name explicitly requested" was logged.
+ ExpectCardUploadDecision(
+ histogram_tester,
+ AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
+ EXPECT_TRUE(payments_client_->detected_values_in_upload_details() &
+ CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
}
-TEST_F(CreditCardSaveManagerTest, UploadCreditCard_NamesHaveToMatch) {
+TEST_F(
+ CreditCardSaveManagerTest,
+ UploadCreditCard_RequestCardholderNameIfNameConflictingAndNoPaymentsCustomer) {
+ scoped_feature_list_.InitAndEnableFeature(
+ kAutofillUpstreamEditableCardholderName);
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
- // Create, fill and submit two address forms with different names.
- FormData address_form1, address_form2;
- test::CreateTestAddressFormData(&address_form1);
- test::CreateTestAddressFormData(&address_form2);
+ // Create, fill and submit an address form in order to establish a recent
+ // profile which can be selected for the upload request.
+ FormData address_form;
+ test::CreateTestAddressFormData(&address_form);
+ FormsSeen(std::vector<FormData>(1, address_form));
+ ManuallyFillAddressForm("John", "Smith", "77401", "US", &address_form);
+ FormSubmitted(address_form);
- std::vector<FormData> address_forms;
- address_forms.push_back(address_form1);
- address_forms.push_back(address_form2);
- FormsSeen(address_forms);
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
- ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form1);
- FormSubmitted(address_form1);
+ // Edit the data, but include a conflicting name, and submit.
+ credit_card_form.fields[0].value = ASCIIToUTF16("Jane Doe");
+ credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[4].value = ASCIIToUTF16("123");
- ManuallyFillAddressForm("Master", "Blaster", "77401", "US", &address_form2);
- FormSubmitted(address_form2);
+ base::HistogramTester histogram_tester;
+
+ // With the offer-to-save decision deferred to Google Payments, Payments can
+ // still decide to allow saving despite the missing name.
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+ FormSubmitted(credit_card_form);
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+
+ // Verify that the correct histogram entry and DetectedValue for "Cardholder
+ // name explicitly requested" was logged.
+ ExpectCardUploadDecision(
+ histogram_tester,
+ AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
+ EXPECT_TRUE(payments_client_->detected_values_in_upload_details() &
+ CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
+}
+
+TEST_F(
+ CreditCardSaveManagerTest,
+ UploadCreditCard_DoNotRequestCardholderNameIfNameExistsAndNoPaymentsCustomer) {
+ scoped_feature_list_.InitAndEnableFeature(
+ kAutofillUpstreamEditableCardholderName);
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+
+ // Create, fill and submit an address form in order to establish a recent
+ // profile which can be selected for the upload request.
+ FormData address_form;
+ test::CreateTestAddressFormData(&address_form);
+ FormsSeen(std::vector<FormData>(1, address_form));
+ ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
+ FormSubmitted(address_form);
// Set up our credit card form data.
FormData credit_card_form;
CreateTestCreditCardFormData(&credit_card_form, true, false);
FormsSeen(std::vector<FormData>(1, credit_card_form));
- // Edit the data, but use yet another name, and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Bob Master");
+ // Edit the data, and submit.
+ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+ base::HistogramTester histogram_tester;
+
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+ FormSubmitted(credit_card_form);
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+
+ // Because everything went smoothly, verify that there was no histogram entry
+ // or DetectedValue for "Cardholder name explicitly requested" logged.
+ ExpectNoCardUploadDecision(
+ histogram_tester,
+ AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
+ EXPECT_FALSE(payments_client_->detected_values_in_upload_details() &
+ CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
+}
+
+TEST_F(
+ CreditCardSaveManagerTest,
+ UploadCreditCard_DoNotRequestCardholderNameIfNameMissingAndPaymentsCustomer) {
+ scoped_feature_list_.InitAndEnableFeature(
+ kAutofillUpstreamEditableCardholderName);
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+
+ // Set the billing_customer_number Priority Preference to designate existence
+ // of a Payments account.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
+
+ // Create, fill and submit an address form in order to establish a recent
+ // profile which can be selected for the upload request.
+ FormData address_form;
+ test::CreateTestAddressFormData(&address_form);
+ FormsSeen(std::vector<FormData>(1, address_form));
+ // But omit the name:
+ ManuallyFillAddressForm("", "", "77401", "US", &address_form);
+ FormSubmitted(address_form);
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, but don't include a name, and submit.
+ credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
base::HistogramTester histogram_tester;
// With the offer-to-save decision deferred to Google Payments, Payments can
- // still decide to allow saving despite the mismatching names.
+ // still decide to allow saving despite the missing name.
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
- // Verify that the correct histogram entries were logged.
- ExpectCardUploadDecision(histogram_tester, AutofillMetrics::UPLOAD_OFFERED);
- ExpectCardUploadDecision(
- histogram_tester, AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES);
- // Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(
- AutofillMetrics::UPLOAD_OFFERED |
- AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES);
+ // Verify that there was no histogram entry or DetectedValue for "Cardholder
+ // name explicitly requested" logged.
+ ExpectNoCardUploadDecision(
+ histogram_tester,
+ AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
+ EXPECT_FALSE(payments_client_->detected_values_in_upload_details() &
+ CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
}
-TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_NamesHaveToMatch_DetectedValuesOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
+TEST_F(
+ CreditCardSaveManagerTest,
+ UploadCreditCard_DoNotRequestCardholderNameIfNameConflictingAndPaymentsCustomer) {
+ scoped_feature_list_.InitAndEnableFeature(
+ kAutofillUpstreamEditableCardholderName);
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
- // Create, fill and submit two address forms with different names.
- FormData address_form1, address_form2;
- test::CreateTestAddressFormData(&address_form1);
- test::CreateTestAddressFormData(&address_form2);
+ // Set the billing_customer_number Priority Preference to designate existence
+ // of a Payments account.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
- std::vector<FormData> address_forms;
- address_forms.push_back(address_form1);
- address_forms.push_back(address_form2);
- FormsSeen(address_forms);
+ // Create, fill and submit an address form in order to establish a recent
+ // profile which can be selected for the upload request.
+ FormData address_form;
+ test::CreateTestAddressFormData(&address_form);
+ FormsSeen(std::vector<FormData>(1, address_form));
+ ManuallyFillAddressForm("John", "Smith", "77401", "US", &address_form);
+ FormSubmitted(address_form);
- ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form1);
- FormSubmitted(address_form1);
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
- ManuallyFillAddressForm("Master", "Blaster", "77401", "US", &address_form2);
- FormSubmitted(address_form2);
+ // Edit the data, but include a conflicting name, and submit.
+ credit_card_form.fields[0].value = ASCIIToUTF16("Jane Doe");
+ credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+ base::HistogramTester histogram_tester;
+
+ // With the offer-to-save decision deferred to Google Payments, Payments can
+ // still decide to allow saving despite the missing name.
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+ FormSubmitted(credit_card_form);
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+
+ // Verify that there was no histogram entry or DetectedValue for "Cardholder
+ // name explicitly requested" logged.
+ ExpectNoCardUploadDecision(
+ histogram_tester,
+ AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
+ EXPECT_FALSE(payments_client_->detected_values_in_upload_details() &
+ CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
+}
+
+TEST_F(
+ CreditCardSaveManagerTest,
+ UploadCreditCard_DoNotRequestCardholderNameIfNameMissingAndNoPaymentsCustomerExpOff) {
+ scoped_feature_list_.InitAndDisableFeature(
+ kAutofillUpstreamEditableCardholderName);
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+
+ // Create, fill and submit an address form in order to establish a recent
+ // profile which can be selected for the upload request.
+ FormData address_form;
+ test::CreateTestAddressFormData(&address_form);
+ FormsSeen(std::vector<FormData>(1, address_form));
+ // But omit the name:
+ ManuallyFillAddressForm("", "", "77401", "US", &address_form);
+ FormSubmitted(address_form);
// Set up our credit card form data.
FormData credit_card_form;
CreateTestCreditCardFormData(&credit_card_form, true, false);
FormsSeen(std::vector<FormData>(1, credit_card_form));
- // Edit the data, but use yet another name, and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Bob Master");
+ // Edit the data, but don't include a name, and submit.
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
base::HistogramTester histogram_tester;
- // Names are required to match, upload should not happen.
+ // With the offer-to-save decision deferred to Google Payments, Payments can
+ // still decide to allow saving despite the missing name.
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
- EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(
- histogram_tester, AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES);
- // Verify that the correct UKM was logged.
- ExpectCardUploadDecisionUkm(
- AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES);
+ // Verify that there was no histogram entry or DetectedValue for "Cardholder
+ // name explicitly requested" logged.
+ ExpectNoCardUploadDecision(
+ histogram_tester,
+ AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
+ EXPECT_FALSE(payments_client_->detected_values_in_upload_details() &
+ CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
}
-TEST_F(CreditCardSaveManagerTest, UploadCreditCard_IgnoreOldProfiles) {
- // Create the test clock and set the time to a specific value.
- TestAutofillClock test_clock;
- test_clock.SetNow(kArbitraryTime);
+TEST_F(
+ CreditCardSaveManagerTest,
+ UploadCreditCard_DoNotRequestCardholderNameIfNameConflictingAndNoPaymentsCustomerExpOff) {
+ scoped_feature_list_.InitAndDisableFeature(
+ kAutofillUpstreamEditableCardholderName);
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+
+ // Create, fill and submit an address form in order to establish a recent
+ // profile which can be selected for the upload request.
+ FormData address_form;
+ test::CreateTestAddressFormData(&address_form);
+ FormsSeen(std::vector<FormData>(1, address_form));
+ ManuallyFillAddressForm("John", "Smith", "77401", "US", &address_form);
+ FormSubmitted(address_form);
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, but include a conflicting name, and submit.
+ credit_card_form.fields[0].value = ASCIIToUTF16("Jane Doe");
+ credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+ base::HistogramTester histogram_tester;
+ // With the offer-to-save decision deferred to Google Payments, Payments can
+ // still decide to allow saving despite the missing name.
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+ FormSubmitted(credit_card_form);
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+
+ // Verify that there was no histogram entry or DetectedValue for "Cardholder
+ // name explicitly requested" logged.
+ ExpectNoCardUploadDecision(
+ histogram_tester,
+ AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
+ EXPECT_FALSE(payments_client_->detected_values_in_upload_details() &
+ CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
+}
+
+// This test ensures |should_request_name_from_user_| is reset between offers to
+// save.
+TEST_F(
+ CreditCardSaveManagerTest,
+ UploadCreditCard_ShouldRequestCardholderName_ResetBetweenConsecutiveSaves) {
+ scoped_feature_list_.InitAndEnableFeature(
+ kAutofillUpstreamEditableCardholderName);
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
- // Create, fill and submit two address forms with different names.
- FormData address_form1, address_form2;
- test::CreateTestAddressFormData(&address_form1);
- test::CreateTestAddressFormData(&address_form2);
- FormsSeen({address_form1, address_form2});
+ // Create, fill and submit an address form in order to establish a recent
+ // profile which can be selected for the upload request.
+ FormData address_form;
+ test::CreateTestAddressFormData(&address_form);
+ FormsSeen(std::vector<FormData>(1, address_form));
+ // But omit the name:
+ ManuallyFillAddressForm("", "", "77401", "US", &address_form);
+ FormSubmitted(address_form);
- ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form1);
- FormSubmitted(address_form1);
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
- // Advance the current time. Since |address_form1| will not be a recently
- // used address profile, we will not include it in the candidate profiles.
- test_clock.SetNow(kMuchLaterTime);
+ // Edit the data, but don't include a name, and submit.
+ credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[4].value = ASCIIToUTF16("123");
- ManuallyFillAddressForm("Master", "Blaster", "77401", "US", &address_form2);
- FormSubmitted(address_form2);
+ // With the offer-to-save decision deferred to Google Payments, Payments can
+ // still decide to allow saving despite the missing name.
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+ FormSubmitted(credit_card_form);
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+
+ // Verify the |credit_card_save_manager_| is requesting cardholder name.
+ EXPECT_TRUE(credit_card_save_manager_->should_request_name_from_user_);
+
+ // Simulate a Chrome/Payments sync where billing_customer_number was newly
+ // set.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
+ // 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();
+ FormSubmitted(credit_card_form);
+
+ // Verify the |credit_card_save_manager_| is NOT requesting cardholder name.
+ EXPECT_FALSE(credit_card_save_manager_->should_request_name_from_user_);
+}
+
+TEST_F(CreditCardSaveManagerTest,
+ UploadCreditCard_RequestCardholderNameIfTestingExperimentOn) {
+ scoped_feature_list_.InitAndEnableFeature(
+ kAutofillUpstreamAlwaysRequestCardholderName);
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+
+ // Create, fill and submit an address form in order to establish a recent
+ // profile which can be selected for the upload request.
+ FormData address_form;
+ test::CreateTestAddressFormData(&address_form);
+ FormsSeen(std::vector<FormData>(1, address_form));
+ ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
+ FormSubmitted(address_form);
// Set up our credit card form data.
FormData credit_card_form;
CreateTestCreditCardFormData(&credit_card_form, true, false);
FormsSeen(std::vector<FormData>(1, credit_card_form));
- // Edit the data, but use yet another name, and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Master Blaster");
+ // Edit the data, and submit.
+ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
base::HistogramTester histogram_tester;
- // Name matches recently used profile, should offer upload.
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
- // Verify that the correct histogram entry (and only that) was logged.
- ExpectUniqueCardUploadDecision(histogram_tester,
- AutofillMetrics::UPLOAD_OFFERED);
+ // Even though everything went smoothly, because the "always request
+ // cardholder name" experiment was enabled, verify that the correct histogram
+ // entry and DetectedValue for "Cardholder name explicitly requested" was
+ // logged.
+ ExpectCardUploadDecision(
+ histogram_tester,
+ AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
+ EXPECT_TRUE(payments_client_->detected_values_in_upload_details() &
+ CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
}
+#endif
TEST_F(CreditCardSaveManagerTest, UploadCreditCard_LogPreviousUseDate) {
// Create the test clock and set the time to a specific value.
@@ -2463,7 +2334,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_LogPreviousUseDate) {
// Edit the credit card form and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -2504,7 +2375,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadDetailsFails) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -2540,8 +2411,8 @@ TEST_F(CreditCardSaveManagerTest, DuplicateMaskedCreditCard_NoUpload) {
// Add a masked credit card whose |TypeAndLastFourDigits| matches what we will
// enter below.
CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123");
- test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11",
- NextYear().c_str(), "1");
+ test::SetCreditCardInfo(&credit_card, "Flo Master", "1111",
+ NextMonth().c_str(), NextYear().c_str(), "1");
credit_card.SetNetworkForMaskedCard(kVisaCard);
personal_data_.AddServerCreditCard(credit_card);
@@ -2553,7 +2424,7 @@ TEST_F(CreditCardSaveManagerTest, DuplicateMaskedCreditCard_NoUpload) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -2565,7 +2436,6 @@ TEST_F(CreditCardSaveManagerTest, DuplicateMaskedCreditCard_NoUpload) {
}
TEST_F(CreditCardSaveManagerTest, GetDetectedValues_NothingIfNothingFound) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2577,17 +2447,16 @@ TEST_F(CreditCardSaveManagerTest, GetDetectedValues_NothingIfNothingFound) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(), 0);
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(), 0);
}
TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectCvc) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2599,18 +2468,17 @@ TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectCvc) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
CreditCardSaveManager::DetectedValue::CVC);
}
TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectCardholderName) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2622,18 +2490,17 @@ TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectCardholderName) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("John Smith");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
CreditCardSaveManager::DetectedValue::CARDHOLDER_NAME);
}
TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectAddressName) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2651,19 +2518,18 @@ TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectAddressName) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
CreditCardSaveManager::DetectedValue::ADDRESS_NAME);
}
TEST_F(CreditCardSaveManagerTest,
GetDetectedValues_DetectCardholderAndAddressNameIfMatching) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2681,20 +2547,19 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("John Smith");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
CreditCardSaveManager::DetectedValue::CARDHOLDER_NAME |
CreditCardSaveManager::DetectedValue::ADDRESS_NAME);
}
TEST_F(CreditCardSaveManagerTest,
GetDetectedValues_DetectNoUniqueNameIfNamesConflict) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2712,17 +2577,16 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Miles Prower"); // Conflict!
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(), 0);
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(), 0);
}
TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectPostalCode) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2740,19 +2604,18 @@ TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectPostalCode) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
CreditCardSaveManager::DetectedValue::POSTAL_CODE);
}
TEST_F(CreditCardSaveManagerTest,
GetDetectedValues_DetectNoUniquePostalCodeIfZipsConflict) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2774,17 +2637,16 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(), 0);
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(), 0);
}
TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectAddressLine) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2802,18 +2664,17 @@ TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectAddressLine) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
CreditCardSaveManager::DetectedValue::ADDRESS_LINE);
}
TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectLocality) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2831,18 +2692,17 @@ TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectLocality) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
CreditCardSaveManager::DetectedValue::LOCALITY);
}
TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectAdministrativeArea) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2860,18 +2720,17 @@ TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectAdministrativeArea) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
CreditCardSaveManager::DetectedValue::ADMINISTRATIVE_AREA);
}
TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectCountryCode) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2889,19 +2748,18 @@ TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectCountryCode) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
CreditCardSaveManager::DetectedValue::COUNTRY_CODE);
}
TEST_F(CreditCardSaveManagerTest,
GetDetectedValues_DetectHasGooglePaymentAccount) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2918,18 +2776,17 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
CreditCardSaveManager::DetectedValue::HAS_GOOGLE_PAYMENTS_ACCOUNT);
}
TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectEverythingAtOnce) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2952,13 +2809,13 @@ TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectEverythingAtOnce) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("John Smith");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
CreditCardSaveManager::DetectedValue::CVC |
CreditCardSaveManager::DetectedValue::CARDHOLDER_NAME |
CreditCardSaveManager::DetectedValue::ADDRESS_NAME |
@@ -2971,7 +2828,6 @@ TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectEverythingAtOnce) {
TEST_F(CreditCardSaveManagerTest,
GetDetectedValues_DetectSubsetOfPossibleFields) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -2992,13 +2848,13 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Miles Prower"); // Conflict!
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
CreditCardSaveManager::DetectedValue::CVC |
CreditCardSaveManager::DetectedValue::LOCALITY |
CreditCardSaveManager::DetectedValue::POSTAL_CODE |
@@ -3010,7 +2866,6 @@ TEST_F(CreditCardSaveManagerTest,
// populated if even one address profile contains it.
TEST_F(CreditCardSaveManagerTest,
GetDetectedValues_DetectAddressComponentsAcrossProfiles) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -3041,13 +2896,13 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name set
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC set
// Submit the form and check what detected_values for an upload save would be.
FormSubmitted(credit_card_form);
- EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+ EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
CreditCardSaveManager::DetectedValue::ADDRESS_LINE |
CreditCardSaveManager::DetectedValue::LOCALITY |
CreditCardSaveManager::DetectedValue::ADMINISTRATIVE_AREA |
@@ -3055,35 +2910,7 @@ TEST_F(CreditCardSaveManagerTest,
}
TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_AddSendDetectedValuesFlagStateToRequestIfExperimentOn) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
- personal_data_.ClearProfiles();
- credit_card_save_manager_->SetCreditCardUploadEnabled(true);
-
- // Set up our credit card form data.
- FormData credit_card_form;
- CreateTestCreditCardFormData(&credit_card_form, true, false);
- FormsSeen(std::vector<FormData>(1, credit_card_form));
-
- // Edit the data, and submit.
- credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
- credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
- credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
- credit_card_form.fields[4].value = ASCIIToUTF16("123");
-
- // Confirm upload happened and the send detected values flag was sent in the
- // request.
- EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
- FormSubmitted(credit_card_form);
- EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
- EXPECT_THAT(payments_client_->GetActiveExperimentsSetInRequest(),
- UnorderedElementsAre(kAutofillUpstreamSendDetectedValues.name));
-}
-
-TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_LogAdditionalErrorsWithUploadDetailsFailureIfExpOn) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
+ UploadCreditCard_LogAdditionalErrorsWithUploadDetailsFailure) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -3108,7 +2935,7 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name!
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC!
@@ -3137,8 +2964,7 @@ TEST_F(CreditCardSaveManagerTest,
TEST_F(
CreditCardSaveManagerTest,
- UploadCreditCard_ShouldOfferLocalSaveIfEverythingDetectedAndPaymentsDeclinesIfExpOn) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
+ UploadCreditCard_ShouldOfferLocalSaveIfEverythingDetectedAndPaymentsDeclines) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -3165,7 +2991,7 @@ TEST_F(
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("John Smith");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -3181,8 +3007,60 @@ TEST_F(
TEST_F(
CreditCardSaveManagerTest,
- UploadCreditCard_ShouldNotOfferLocalSaveIfSomethingNotDetectedAndPaymentsDeclinesIfExpOn) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
+ UploadCreditCard_ShouldOfferLocalSaveIfEverythingDetectedAndPaymentsDeclines_WithFirstAndLastName) {
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+
+ // Anything other than "en-US" will cause GetUploadDetails to return a failure
+ // response.
+ credit_card_save_manager_->SetAppLocale("pt-BR");
+
+ // Set up a new address profile.
+ AutofillProfile profile;
+ profile.set_guid("00000000-0000-0000-0000-000000000200");
+ profile.SetInfo(NAME_FULL, ASCIIToUTF16("John Smith"), "en-US");
+ profile.SetInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("123 Testing St."), "en-US");
+ profile.SetInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Mountain View"), "en-US");
+ profile.SetInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("California"), "en-US");
+ profile.SetInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("94043"), "en-US");
+ profile.SetInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"), "en-US");
+ personal_data_.AddProfile(profile);
+
+ // Set up our credit card form data with credit card first and last name
+ // fields.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, /*is_https=*/true,
+ /*use_month_type=*/false, /*split_names=*/true);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ credit_card_form.fields[0].value = ASCIIToUTF16("John");
+ credit_card_form.fields[1].value = ASCIIToUTF16("Smith");
+ credit_card_form.fields[2].value = ASCIIToUTF16("4111111111111111");
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[4].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[5].value = ASCIIToUTF16("123");
+
+ base::HistogramTester histogram_tester;
+
+ // Because Payments rejects the offer to upload save but CVC + name + address
+ // were all found, the local save prompt should be shown instead of the upload
+ // prompt.
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _));
+ FormSubmitted(credit_card_form);
+ EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
+
+ histogram_tester.ExpectTotalCount(
+ "Autofill.SaveCardWithFirstAndLastNameOffered.Local", 1);
+ histogram_tester.ExpectTotalCount(
+ "Autofill.SaveCardWithFirstAndLastNameOffered.Server", 0);
+ histogram_tester.ExpectTotalCount(
+ "Autofill.SaveCardWithFirstAndLastNameComplete.Server", 0);
+}
+
+TEST_F(
+ CreditCardSaveManagerTest,
+ UploadCreditCard_ShouldNotOfferLocalSaveIfSomethingNotDetectedAndPaymentsDeclines) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -3207,7 +3085,7 @@ TEST_F(
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name!
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC!
@@ -3221,8 +3099,7 @@ TEST_F(
}
TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_PaymentsDecidesOfferToSaveIfNoCvcAndExpOn) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
+ UploadCreditCard_PaymentsDecidesOfferToSaveIfNoCvc) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -3242,15 +3119,14 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC!
base::HistogramTester histogram_tester;
- // Because the send detected values experiment is on, Payments should be asked
- // whether upload save can be offered. (Unit tests assume they reply yes and
- // save is successful.)
+ // Payments should be asked whether upload save can be offered.
+ // (Unit tests assume they reply yes and save is successful.)
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
@@ -3268,8 +3144,7 @@ TEST_F(CreditCardSaveManagerTest,
}
TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_PaymentsDecidesOfferToSaveIfNoNameAndExpOn) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
+ UploadCreditCard_PaymentsDecidesOfferToSaveIfNoName) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -3290,15 +3165,14 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name!
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
base::HistogramTester histogram_tester;
- // Because the send detected values experiment is on, Payments should be asked
- // whether upload save can be offered. (Unit tests assume they reply yes and
- // save is successful.)
+ // Payments should be asked whether upload save can be offered.
+ // (Unit tests assume they reply yes and save is successful.)
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
@@ -3316,8 +3190,7 @@ TEST_F(CreditCardSaveManagerTest,
}
TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_PaymentsDecidesOfferToSaveIfConflictingNamesAndExpOn) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
+ UploadCreditCard_PaymentsDecidesOfferToSaveIfConflictingNames) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -3337,15 +3210,14 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Miles Prower"); // Conflict!
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
base::HistogramTester histogram_tester;
- // Because the send detected values experiment is on, Payments should be asked
- // whether upload save can be offered. (Unit tests assume they reply yes and
- // save is successful.)
+ // Payments should be asked whether upload save can be offered.
+ // (Unit tests assume they reply yes and save is successful.)
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
@@ -3363,8 +3235,7 @@ TEST_F(CreditCardSaveManagerTest,
}
TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_PaymentsDecidesOfferToSaveIfNoZipAndExpOn) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
+ UploadCreditCard_PaymentsDecidesOfferToSaveIfNoZip) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -3386,15 +3257,14 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
base::HistogramTester histogram_tester;
- // Because the send detected values experiment is on, Payments should be asked
- // whether upload save can be offered. (Unit tests assume they reply yes and
- // save is successful.)
+ // Payments should be asked whether upload save can be offered.
+ // (Unit tests assume they reply yes and save is successful.)
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
@@ -3412,8 +3282,7 @@ TEST_F(CreditCardSaveManagerTest,
}
TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_PaymentsDecidesOfferToSaveIfConflictingZipsAndExpOn) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
+ UploadCreditCard_PaymentsDecidesOfferToSaveIfConflictingZips) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -3447,15 +3316,14 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
base::HistogramTester histogram_tester;
- // Because the send detected values experiment is on, Payments should be asked
- // whether upload save can be offered. (Unit tests assume they reply yes and
- // save is successful.)
+ // Payments should be asked whether upload save can be offered.
+ // (Unit tests assume they reply yes and save is successful.)
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
@@ -3473,8 +3341,7 @@ TEST_F(CreditCardSaveManagerTest,
}
TEST_F(CreditCardSaveManagerTest,
- UploadCreditCard_PaymentsDecidesOfferToSaveIfNothingFoundAndExpOn) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
+ UploadCreditCard_PaymentsDecidesOfferToSaveIfNothingFound) {
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -3495,15 +3362,14 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16(""); // No name!
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16(""); // No CVC!
base::HistogramTester histogram_tester;
- // Because the send detected values experiment is on, Payments should be asked
- // whether upload save can be offered. (Unit tests assume they reply yes and
- // save is successful.)
+ // Payments should be asked whether upload save can be offered.
+ // (Unit tests assume they reply yes and save is successful.)
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
@@ -3549,24 +3415,23 @@ TEST_F(
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4444333322221111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
- // Confirm that upload happened and that the enabled UpdatePromptExplanation
+ // Confirm upload happened and that the enabled UpdatePromptExplanation
// experiment flag state was sent in the request.
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
EXPECT_THAT(
- payments_client_->GetActiveExperimentsSetInRequest(),
- UnorderedElementsAre(kAutofillUpstreamSendDetectedValues.name,
- kAutofillUpstreamUpdatePromptExplanation.name));
+ payments_client_->active_experiments_in_request(),
+ UnorderedElementsAre(kAutofillUpstreamUpdatePromptExplanation.name));
}
TEST_F(CreditCardSaveManagerTest,
UploadCreditCard_DoNotAddAnyFlagStatesToRequestIfExperimentsOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
+ DisableAutofillUpstreamUpdatePromptExplanationExperiment();
personal_data_.ClearProfiles();
credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -3586,16 +3451,16 @@ TEST_F(CreditCardSaveManagerTest,
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
- // Confirm upload happened and that no experiment flag state was sent in the
- // request.
+ // Confirm that upload happened and that no experiment flag state was sent in
+ // the request.
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
- EXPECT_TRUE(payments_client_->GetActiveExperimentsSetInRequest().empty());
+ EXPECT_TRUE(payments_client_->active_experiments_in_request().empty());
}
TEST_F(CreditCardSaveManagerTest, UploadCreditCard_AddPanFirstSixToRequest) {
@@ -3619,7 +3484,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_AddPanFirstSixToRequest) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4444333322221111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -3628,12 +3493,13 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_AddPanFirstSixToRequest) {
EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
FormSubmitted(credit_card_form);
EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
- EXPECT_EQ(payments_client_->GetPanFirstSixSetInRequest(), "444433");
- // Confirm that the "send pan first six" experiment flag was sent in the
- // request.
- EXPECT_THAT(payments_client_->GetActiveExperimentsSetInRequest(),
- UnorderedElementsAre(kAutofillUpstreamSendDetectedValues.name,
- kAutofillUpstreamSendPanFirstSix.name));
+ EXPECT_EQ(payments_client_->pan_first_six_in_upload_details(), "444433");
+ // Confirm that the "send pan first six" experiment flag and enabled
+ // UpdatePromptExplanation experiment flag state was sent in the request.
+ EXPECT_THAT(
+ payments_client_->active_experiments_in_request(),
+ UnorderedElementsAre(kAutofillUpstreamSendPanFirstSix.name,
+ kAutofillUpstreamUpdatePromptExplanation.name));
}
TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfLocalCard) {
@@ -3644,8 +3510,8 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfLocalCard) {
// Add a local credit card whose |TypeAndLastFourDigits| matches what we will
// enter below.
CreditCard local_card;
- test::SetCreditCardInfo(&local_card, "Flo Master", "4111111111111111", "11",
- NextYear().c_str(), "1");
+ test::SetCreditCardInfo(&local_card, "Flo Master", "4111111111111111",
+ NextMonth().c_str(), NextYear().c_str(), "1");
local_card.set_record_type(CreditCard::LOCAL_CARD);
personal_data_.AddCreditCard(local_card);
@@ -3668,7 +3534,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfLocalCard) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -3713,7 +3579,7 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfNewCard) {
// Edit the data, and submit.
credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
- credit_card_form.fields[2].value = ASCIIToUTF16("11");
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
credit_card_form.fields[4].value = ASCIIToUTF16("123");
@@ -3733,4 +3599,173 @@ TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfNewCard) {
AutofillMetrics::USER_ACCEPTED_UPLOAD_OF_NEW_CARD, 1);
}
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_EloDisallowed) {
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillUpstreamDisallowElo);
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
+ credit_card_form.fields[1].value = ASCIIToUTF16(kEloCardNumber);
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+ base::HistogramTester histogram_tester;
+
+ // With Elo disallowed, local save should be offered and upload save should
+ // not.
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _));
+ FormSubmitted(credit_card_form);
+ EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
+
+ // Verify that the correct histogram entry was logged.
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.CreditCardUploadDisallowedForNetwork",
+ AutofillMetrics::DISALLOWED_ELO, 1);
+}
+
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_EloAllowed) {
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ scoped_feature_list_.InitAndDisableFeature(
+ features::kAutofillUpstreamDisallowElo);
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
+ credit_card_form.fields[1].value = ASCIIToUTF16(kEloCardNumber);
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+ base::HistogramTester histogram_tester;
+
+ // With the feature flag off, the Elo card should be allowed to be uploaded as
+ // normal.
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+ FormSubmitted(credit_card_form);
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+
+ // Verify that no histogram entry was logged.
+ histogram_tester.ExpectTotalCount(
+ "Autofill.CreditCardUploadDisallowedForNetwork", 0);
+}
+
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_JcbDisallowed) {
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillUpstreamDisallowJcb);
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
+ credit_card_form.fields[1].value = ASCIIToUTF16(kJcbCardNumber);
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+ base::HistogramTester histogram_tester;
+
+ // With JCB disallowed, local save should be offered and upload save should
+ // not.
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _));
+ FormSubmitted(credit_card_form);
+ EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
+
+ // Verify that the correct histogram entry was logged.
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.CreditCardUploadDisallowedForNetwork",
+ AutofillMetrics::DISALLOWED_JCB, 1);
+}
+
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_JcbAllowed) {
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ scoped_feature_list_.InitAndDisableFeature(
+ features::kAutofillUpstreamDisallowJcb);
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
+ credit_card_form.fields[1].value = ASCIIToUTF16(kJcbCardNumber);
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+ base::HistogramTester histogram_tester;
+
+ // With the feature flag off, the JCB card should be allowed to be uploaded as
+ // normal.
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+ FormSubmitted(credit_card_form);
+ EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+
+ // Verify that no histogram entry was logged.
+ histogram_tester.ExpectTotalCount(
+ "Autofill.CreditCardUploadDisallowedForNetwork", 0);
+}
+
+// We can't tell what network a card is until *after* FormDataImporter imports
+// it, making it possible to deny upload save for a pre-existing local card.
+// This test ensures that we do not offer local save (again) for the card that
+// FormDataImporter imported.
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_DisallowedLocalCard) {
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ scoped_feature_list_.InitAndEnableFeature(
+ features::kAutofillUpstreamDisallowElo);
+
+ // Add a local credit card that will match what we will enter below.
+ CreditCard local_card;
+ test::SetCreditCardInfo(&local_card, "Flo Master", kEloCardNumber,
+ NextMonth().c_str(), NextYear().c_str(), "1");
+ local_card.set_record_type(CreditCard::LOCAL_CARD);
+ personal_data_.AddCreditCard(local_card);
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
+ credit_card_form.fields[1].value = ASCIIToUTF16(kEloCardNumber);
+ credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
+ credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+ credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+ base::HistogramTester histogram_tester;
+
+ // The card is disallowed, but because it is already a local card, local save
+ // should not be offered again.
+ EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+ FormSubmitted(credit_card_form);
+ EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
+
+ // Verify that the correct histogram entry was logged.
+ histogram_tester.ExpectUniqueSample(
+ "Autofill.CreditCardUploadDisallowedForNetwork",
+ AutofillMetrics::DISALLOWED_ELO, 1);
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/credit_card_unittest.cc b/chromium/components/autofill/core/browser/credit_card_unittest.cc
index a2e5a935f26..05f85fe447e 100644
--- a/chromium/components/autofill/core/browser/credit_card_unittest.cc
+++ b/chromium/components/autofill/core/browser/credit_card_unittest.cc
@@ -8,7 +8,6 @@
#include "base/macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/autofill_experiments.h"
@@ -193,24 +192,7 @@ TEST(CreditCardTest, BankNameAndLastFourDigitsStrings) {
// Tests function NetworkOrBankNameAndLastFourDigits.
TEST(CreditCardTest, NetworkOrBankNameAndLastFourDigitsStrings) {
- // Case 1: Experiment off -> show network name.
- CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com/");
- test::SetCreditCardInfo(&credit_card1, "John Dillinger",
- "5105 1051 0510 5100" /* Mastercard */, "01", "2010",
- "1");
- credit_card1.set_bank_name("Chase");
- base::string16 obfuscated1 =
- credit_card1.NetworkOrBankNameAndLastFourDigits();
- EXPECT_FALSE(credit_card1.bank_name().empty());
- EXPECT_EQ(UTF8ToUTF16(std::string("Mastercard ") +
- test::ObfuscatedCardDigitsAsUTF8("5100")),
- obfuscated1);
-
- // Turn on feature flag.
- base::test::ScopedFeatureList scoped_feature_list_;
- scoped_feature_list_.InitAndEnableFeature(kAutofillCreditCardBankNameDisplay);
-
- // Case 2: Bank name is empty -> show network name.
+ // Case 1: Bank name is empty -> show network name.
CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com/");
test::SetCreditCardInfo(&credit_card2, "John Dillinger",
"5105 1051 0510 5100" /* Mastercard */, "01", "2010",
@@ -222,7 +204,7 @@ TEST(CreditCardTest, NetworkOrBankNameAndLastFourDigitsStrings) {
test::ObfuscatedCardDigitsAsUTF8("5100")),
obfuscated2);
- // Case 3: Experiment on && bank name not empty -> show bank name.
+ // Case 2: Bank name is not empty -> show bank name.
CreditCard credit_card3(base::GenerateGUID(), "https://www.example.com/");
test::SetCreditCardInfo(&credit_card3, "John Dillinger",
"5105 1051 0510 5100" /* Mastercard */, "01", "2010",
@@ -902,30 +884,65 @@ INSTANTIATE_TEST_CASE_P(
GetCardNetworkTestCase{"622384452162063648", kUnionPay, true},
GetCardNetworkTestCase{"2204883716636153", kMirCard, true},
GetCardNetworkTestCase{"2200111234567898", kMirCard, true},
- GetCardNetworkTestCase{"2200481349288130", kMirCard, true},
+ GetCardNetworkTestCase{"2200481349288130", kMirCard, true}));
+
+class GetCardNetworkTestBatch2
+ : public testing::TestWithParam<GetCardNetworkTestCase> {};
+
+TEST_P(GetCardNetworkTestBatch2, GetCardNetwork) {
+ auto test_case = GetParam();
+ base::string16 card_number = ASCIIToUTF16(test_case.card_number);
+ SCOPED_TRACE(card_number);
+ EXPECT_EQ(test_case.issuer_network, CreditCard::GetCardNetwork(card_number));
+ EXPECT_EQ(test_case.is_valid, IsValidCreditCardNumber(card_number));
+}
+INSTANTIATE_TEST_CASE_P(
+ CreditCardTest,
+ GetCardNetworkTestBatch2,
+ testing::Values(
// The relevant sample numbers from
// https://www.bincodes.com/bank-creditcard-generator/ and
// https://www.ebanx.com/business/en/developers/integrations/testing/credit-card-test-numbers
GetCardNetworkTestCase{"5067001446391275", kEloCard, true},
GetCardNetworkTestCase{"6362970000457013", kEloCard, true},
+ // These sample numbers were created by taking the expected card prefix,
+ // filling out the required number of digits, and editing the last digit
+ // so that the full number passes a Luhn check.
+ GetCardNetworkTestCase{"4312741111111112", kEloCard, true},
+ GetCardNetworkTestCase{"4514161111111119", kEloCard, true},
+ GetCardNetworkTestCase{"5090111111111113", kEloCard, true},
+ GetCardNetworkTestCase{"6277801111111112", kEloCard, true},
+
+ // Existence of separators should not change the result, especially for
+ // prefixes that go past the first separator.
+ GetCardNetworkTestCase{"4111 1111 1111 1111", kVisaCard, true},
+ GetCardNetworkTestCase{"4111-1111-1111-1111", kVisaCard, true},
+ GetCardNetworkTestCase{"4312 7411 1111 1112", kEloCard, true},
+ GetCardNetworkTestCase{"4312-7411-1111-1112", kEloCard, true},
+
// Empty string
GetCardNetworkTestCase{"", kGenericCard, false},
// Non-numeric
GetCardNetworkTestCase{"garbage", kGenericCard, false},
- GetCardNetworkTestCase{"4garbage", kVisaCard, false},
+ GetCardNetworkTestCase{"4garbage", kGenericCard, false},
// Fails Luhn check.
GetCardNetworkTestCase{"4111111111111112", kVisaCard, false},
GetCardNetworkTestCase{"6247130048162413", kUnionPay, false},
- GetCardNetworkTestCase{"2204883716636154", kMirCard, false}));
+ GetCardNetworkTestCase{"2204883716636154", kMirCard, false},
-class GetCardNetworkTestBatch2
+ // Invalid length.
+ GetCardNetworkTestCase{"3434343434343434", kAmericanExpressCard, false},
+ GetCardNetworkTestCase{"411111111111116", kVisaCard, false},
+ GetCardNetworkTestCase{"220011123456783", kMirCard, false}));
+
+class GetCardNetworkTestBatch3
: public testing::TestWithParam<GetCardNetworkTestCase> {};
-TEST_P(GetCardNetworkTestBatch2, GetCardNetwork) {
+TEST_P(GetCardNetworkTestBatch3, GetCardNetwork) {
auto test_case = GetParam();
base::string16 card_number = ASCIIToUTF16(test_case.card_number);
SCOPED_TRACE(card_number);
@@ -935,22 +952,16 @@ TEST_P(GetCardNetworkTestBatch2, GetCardNetwork) {
INSTANTIATE_TEST_CASE_P(
CreditCardTest,
- GetCardNetworkTestBatch2,
+ GetCardNetworkTestBatch3,
testing::Values(
- // Invalid length.
- GetCardNetworkTestCase{"3434343434343434", kAmericanExpressCard, false},
- GetCardNetworkTestCase{"411111111111116", kVisaCard, false},
- GetCardNetworkTestCase{"220011123456783", kMirCard, false},
-
// Issuer Identification Numbers (IINs) that Chrome recognizes.
- GetCardNetworkTestCase{"4", kVisaCard, false},
GetCardNetworkTestCase{"2200", kMirCard, false},
+ GetCardNetworkTestCase{"2201", kMirCard, false},
GetCardNetworkTestCase{"2202", kMirCard, false},
+ GetCardNetworkTestCase{"2203", kMirCard, false},
GetCardNetworkTestCase{"2204", kMirCard, false},
GetCardNetworkTestCase{"2221", kMasterCard, false},
GetCardNetworkTestCase{"2720", kMasterCard, false},
- GetCardNetworkTestCase{"34", kAmericanExpressCard, false},
- GetCardNetworkTestCase{"37", kAmericanExpressCard, false},
GetCardNetworkTestCase{"300", kDinersCard, false},
GetCardNetworkTestCase{"301", kDinersCard, false},
GetCardNetworkTestCase{"302", kDinersCard, false},
@@ -958,45 +969,40 @@ INSTANTIATE_TEST_CASE_P(
GetCardNetworkTestCase{"304", kDinersCard, false},
GetCardNetworkTestCase{"305", kDinersCard, false},
GetCardNetworkTestCase{"309", kDinersCard, false},
+ GetCardNetworkTestCase{"34", kAmericanExpressCard, false},
+ GetCardNetworkTestCase{"3528", kJCBCard, false},
+ GetCardNetworkTestCase{"3531", kJCBCard, false},
+ GetCardNetworkTestCase{"3589", kJCBCard, false},
GetCardNetworkTestCase{"36", kDinersCard, false},
+ GetCardNetworkTestCase{"37", kAmericanExpressCard, false},
GetCardNetworkTestCase{"38", kDinersCard, false},
GetCardNetworkTestCase{"39", kDinersCard, false},
- GetCardNetworkTestCase{"6011", kDiscoverCard, false},
- GetCardNetworkTestCase{"644", kDiscoverCard, false},
- GetCardNetworkTestCase{"645", kDiscoverCard, false},
- GetCardNetworkTestCase{"646", kDiscoverCard, false},
- GetCardNetworkTestCase{"647", kDiscoverCard, false},
- GetCardNetworkTestCase{"648", kDiscoverCard, false},
- GetCardNetworkTestCase{"649", kDiscoverCard, false},
- GetCardNetworkTestCase{"65", kDiscoverCard, false},
+ GetCardNetworkTestCase{"4", kVisaCard, false},
+ GetCardNetworkTestCase{"431274", kEloCard, false},
+ GetCardNetworkTestCase{"451416", kEloCard, false},
GetCardNetworkTestCase{"5067", kEloCard, false},
GetCardNetworkTestCase{"5090", kEloCard, false},
- GetCardNetworkTestCase{"636297", kEloCard, false},
- GetCardNetworkTestCase{"3528", kJCBCard, false},
- GetCardNetworkTestCase{"3531", kJCBCard, false},
- GetCardNetworkTestCase{"3589", kJCBCard, false},
GetCardNetworkTestCase{"51", kMasterCard, false},
GetCardNetworkTestCase{"52", kMasterCard, false},
GetCardNetworkTestCase{"53", kMasterCard, false},
GetCardNetworkTestCase{"54", kMasterCard, false},
GetCardNetworkTestCase{"55", kMasterCard, false},
+ GetCardNetworkTestCase{"6011", kDiscoverCard, false},
GetCardNetworkTestCase{"62", kUnionPay, false},
+ GetCardNetworkTestCase{"627780", kEloCard, false},
+ GetCardNetworkTestCase{"636297", kEloCard, false},
+ GetCardNetworkTestCase{"644", kDiscoverCard, false},
+ GetCardNetworkTestCase{"645", kDiscoverCard, false},
+ GetCardNetworkTestCase{"646", kDiscoverCard, false},
+ GetCardNetworkTestCase{"647", kDiscoverCard, false},
+ GetCardNetworkTestCase{"648", kDiscoverCard, false},
+ GetCardNetworkTestCase{"649", kDiscoverCard, false},
+ GetCardNetworkTestCase{"65", kDiscoverCard, false}));
- // Not enough data to determine an IIN uniquely.
- GetCardNetworkTestCase{"2", kGenericCard, false},
- GetCardNetworkTestCase{"3", kGenericCard, false},
- GetCardNetworkTestCase{"30", kGenericCard, false},
- GetCardNetworkTestCase{"35", kGenericCard, false},
- GetCardNetworkTestCase{"5", kGenericCard, false},
- GetCardNetworkTestCase{"6", kGenericCard, false},
- GetCardNetworkTestCase{"60", kGenericCard, false},
- GetCardNetworkTestCase{"601", kGenericCard, false},
- GetCardNetworkTestCase{"64", kGenericCard, false}));
-
-class GetCardNetworkTestBatch3
+class GetCardNetworkTestBatch4
: public testing::TestWithParam<GetCardNetworkTestCase> {};
-TEST_P(GetCardNetworkTestBatch3, GetCardNetwork) {
+TEST_P(GetCardNetworkTestBatch4, GetCardNetwork) {
auto test_case = GetParam();
base::string16 card_number = ASCIIToUTF16(test_case.card_number);
SCOPED_TRACE(card_number);
@@ -1006,8 +1012,19 @@ TEST_P(GetCardNetworkTestBatch3, GetCardNetwork) {
INSTANTIATE_TEST_CASE_P(
CreditCardTest,
- GetCardNetworkTestBatch3,
+ GetCardNetworkTestBatch4,
testing::Values(
+ // Not enough data to determine an IIN uniquely.
+ GetCardNetworkTestCase{"2", kGenericCard, false},
+ GetCardNetworkTestCase{"3", kGenericCard, false},
+ GetCardNetworkTestCase{"30", kGenericCard, false},
+ GetCardNetworkTestCase{"35", kGenericCard, false},
+ GetCardNetworkTestCase{"5", kGenericCard, false},
+ GetCardNetworkTestCase{"6", kGenericCard, false},
+ GetCardNetworkTestCase{"60", kGenericCard, false},
+ GetCardNetworkTestCase{"601", kGenericCard, false},
+ GetCardNetworkTestCase{"64", kGenericCard, false},
+
// Unknown IINs.
GetCardNetworkTestCase{"0", kGenericCard, false},
GetCardNetworkTestCase{"1", kGenericCard, false},
@@ -1218,82 +1235,4 @@ INSTANTIATE_TEST_CASE_P(
true, testingTimes.next_year_.month, testingTimes.next_year_.year,
CreditCard::FULL_SERVER_CARD, CreditCard::EXPIRED}));
-// TODO(wuandy): rewriting below test with INSTANTIATE_TEST_CASE_P seems to
-// trigger a complaint on windows compilers. Removing it and revert to
-// original test for now.
-
-// Test that credit card last used date suggestion can be generated correctly
-// in different variations.
-
-
-// TODO(scottmg): Disabling as sheriff. On Android, LastUsedDateForDisplay is
-// returning "Last used over a year ago", rather than "last used Nov 30" as of
-// today, Dec 1. https://crbug.com/791067.
-TEST(CreditCardTest, DISABLED_GetLastUsedDateForDisplay) {
- const base::Time::Exploded kTestDateTimeExploded = {
- 2016, 12, 6, 10, // Sat, Dec 10, 2016
- 15, 42, 7, 0 // 15:42:07.000
- };
- base::Time kArbitraryTime;
- EXPECT_TRUE(
- base::Time::FromLocalExploded(kTestDateTimeExploded, &kArbitraryTime));
-
- // Test for added to chrome/chromium.
- CreditCard credit_card0(base::GenerateGUID(), "https://www.example.com");
- credit_card0.set_use_count(1);
- credit_card0.set_use_date(kArbitraryTime - base::TimeDelta::FromDays(1));
- test::SetCreditCardInfo(&credit_card0, "John Dillinger",
- "423456789012" /* Visa */, "01", "2021", "1");
-
- // Test for last used date.
- CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com");
- test::SetCreditCardInfo(&credit_card1, "Clyde Barrow",
- "347666888555" /* American Express */, "04", "2021",
- "1");
- credit_card1.set_use_count(10);
- credit_card1.set_use_date(kArbitraryTime - base::TimeDelta::FromDays(10));
-
- // Test for last used more than one year ago.
- CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com");
- credit_card2.set_use_count(5);
- credit_card2.set_use_date(kArbitraryTime - base::TimeDelta::FromDays(366));
- test::SetCreditCardInfo(&credit_card2, "Bonnie Parker",
- "518765432109" /* Mastercard */, "12", "2021", "1");
-
- static const struct {
- const char* show_expiration_date;
- const std::string& app_locale;
- base::string16 added_to_autofill_date;
- base::string16 last_used_date;
- base::string16 last_used_year_ago;
- } kTestCases[] = {
- // only show last used date.
- {"false", "en_US", ASCIIToUTF16("Added Dec 09"),
- ASCIIToUTF16("Last used Nov 30"),
- ASCIIToUTF16("Last used over a year ago")},
- // show expiration date and last used date.
- {"true", "en_US", ASCIIToUTF16("Exp: 01/21, added Dec 09"),
- ASCIIToUTF16("Exp: 04/21, last used Nov 30"),
- ASCIIToUTF16("Exp: 12/21, last used over a year ago")},
- };
-
- variations::testing::VariationParamsManager variation_params_;
-
- for (const auto& test_case : kTestCases) {
- variation_params_.SetVariationParamsWithFeatureAssociations(
- kAutofillCreditCardLastUsedDateDisplay.name,
- {{kAutofillCreditCardLastUsedDateShowExpirationDateKey,
- test_case.show_expiration_date}},
- {kAutofillCreditCardLastUsedDateDisplay.name});
-
- EXPECT_EQ(test_case.added_to_autofill_date,
- credit_card0.GetLastUsedDateForDisplay(test_case.app_locale));
- EXPECT_EQ(test_case.last_used_date,
- credit_card1.GetLastUsedDateForDisplay(test_case.app_locale));
- EXPECT_EQ(test_case.last_used_year_ago,
- credit_card2.GetLastUsedDateForDisplay(test_case.app_locale));
- variation_params_.ClearAllVariationParams();
- }
-}
-
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/form_data_importer.cc b/chromium/components/autofill/core/browser/form_data_importer.cc
index da41dcdab12..543e5c7cabd 100644
--- a/chromium/components/autofill/core/browser/form_data_importer.cc
+++ b/chromium/components/autofill/core/browser/form_data_importer.cc
@@ -25,7 +25,6 @@
#include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/credit_card.h"
-#include "components/autofill/core/browser/credit_card_save_manager.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/phone_number.h"
@@ -96,41 +95,94 @@ FormDataImporter::FormDataImporter(AutofillClient* client,
payments::PaymentsClient* payments_client,
PersonalDataManager* personal_data_manager,
const std::string& app_locale)
- : credit_card_save_manager_(
+ : client_(client),
+ credit_card_save_manager_(
std::make_unique<CreditCardSaveManager>(client,
payments_client,
app_locale,
personal_data_manager)),
+ local_card_migration_manager_(
+ std::make_unique<LocalCardMigrationManager>(client,
+ payments_client,
+ app_locale,
+ personal_data_manager)),
+
personal_data_manager_(personal_data_manager),
app_locale_(app_locale) {}
FormDataImporter::~FormDataImporter() {}
void FormDataImporter::ImportFormData(const FormStructure& submitted_form,
+ bool profile_autofill_enabled,
bool credit_card_autofill_enabled) {
std::unique_ptr<CreditCard> imported_credit_card;
- if (!ImportFormData(submitted_form, credit_card_autofill_enabled,
- credit_card_save_manager_->IsCreditCardUploadEnabled(),
- &imported_credit_card))
+
+ bool is_credit_card_upstream_enabled =
+ credit_card_save_manager_->IsCreditCardUploadEnabled();
+ // ImportFormData will set the |imported_credit_card_record_type_|. If the
+ // imported card is invalid or already a server card, or if
+ // |credit_card_save_manager_| does not allow uploading,
+ // |imported_credit_card| will be nullptr.
+ ImportFormData(submitted_form, profile_autofill_enabled,
+ credit_card_autofill_enabled,
+ /*should_return_local_card=*/is_credit_card_upstream_enabled,
+ &imported_credit_card);
+ // If no card was successfully imported from the form, return.
+ if (imported_credit_card_record_type_ ==
+ ImportedCreditCardRecordType::NO_CARD) {
return;
+ }
+ // A credit card was successfully imported, but it's possible it is already a
+ // local or server card. First, check to see if we should offer local card
+ // migration in this case, as local cards could go either way.
+ if (local_card_migration_manager_ &&
+ local_card_migration_manager_->ShouldOfferLocalCardMigration(
+ imported_credit_card_record_type_)) {
+ local_card_migration_manager_->AttemptToOfferLocalCardMigration();
+ return;
+ }
- // No card available to offer save or upload.
+ // Local card migration will not be offered. If we do not have a new card to
+ // save (or a local card to upload save), return.
if (!imported_credit_card)
return;
- if (!credit_card_save_manager_->IsCreditCardUploadEnabled()) {
- // Offer local save.
- credit_card_save_manager_->OfferCardLocalSave(*imported_credit_card);
- } else {
- // Attempt to offer upload save. Because we pass IsCreditCardUploadEnabled()
- // to ImportFormData, this block can be reached on observing either a new
- // card or one already stored locally and whose |TypeAndLastFourDigits| do
- // not match a masked server card. We can offer to upload either kind, but
- // note that unless the "send detected values" experiment is enabled, they
- // must pass address/name/CVC validation requirements first.
+ // Check if the imported card is from a network that is currently disallowed,
+ // often due to Google Payments not accepting a certain card network. If so,
+ // don't offer to upload the card. We can fall back to local save as long as
+ // it's a new card instead of an existing local card.
+ if (!credit_card_save_manager_->IsUploadEnabledForNetwork(
+ imported_credit_card->network())) {
+ is_credit_card_upstream_enabled = false;
+ AutofillMetrics::LogUploadDisallowedForNetworkMetric(
+ imported_credit_card->network());
+ if (imported_credit_card_record_type_ ==
+ ImportedCreditCardRecordType::LOCAL_CARD) {
+ return;
+ }
+ }
+
+ // We have a card to save; decide what type of save flow to display.
+ if (is_credit_card_upstream_enabled) {
+ // Attempt to offer upload save. Because we pass
+ // |credit_card_upstream_enabled| to ImportFormData, this block can be
+ // reached on observing either a new card or one already stored locally
+ // which doesn't match an existing server card. If Google Payments declines
+ // allowing upload, |credit_card_save_manager_| is tasked with deciding if
+ // we should fall back to local save or not.
+ DCHECK(imported_credit_card_record_type_ ==
+ ImportedCreditCardRecordType::LOCAL_CARD ||
+ imported_credit_card_record_type_ ==
+ ImportedCreditCardRecordType::NEW_CARD);
credit_card_save_manager_->AttemptToOfferCardUploadSave(
submitted_form, *imported_credit_card,
- offering_upload_of_local_credit_card_);
+ /*uploading_local_card=*/imported_credit_card_record_type_ ==
+ ImportedCreditCardRecordType::LOCAL_CARD);
+ } else {
+ // If upload save is not allowed, new cards should be saved locally.
+ DCHECK(imported_credit_card_record_type_ ==
+ ImportedCreditCardRecordType::NEW_CARD);
+ credit_card_save_manager_->OfferCardLocalSave(*imported_credit_card);
}
}
@@ -163,6 +215,7 @@ bool FormDataImporter::IsValidLearnableProfile(const AutofillProfile& profile,
bool FormDataImporter::ImportFormData(
const FormStructure& submitted_form,
+ bool profile_autofill_enabled,
bool credit_card_autofill_enabled,
bool should_return_local_card,
std::unique_ptr<CreditCard>* imported_credit_card) {
@@ -170,6 +223,9 @@ bool FormDataImporter::ImportFormData(
// - ImportCreditCard may update an existing card, or fill
// |imported_credit_card| with an extracted card. See .h for details of
// |should_return_local_card|.
+ // Reset |imported_credit_card_record_type_| every time we import data from
+ // form no matter whether ImportCreditCard() is called or not.
+ imported_credit_card_record_type_ = ImportedCreditCardRecordType::NO_CARD;
bool cc_import = false;
if (credit_card_autofill_enabled) {
cc_import = ImportCreditCard(submitted_form, should_return_local_card,
@@ -177,7 +233,10 @@ bool FormDataImporter::ImportFormData(
}
// - ImportAddressProfiles may eventually save or update one or more address
// profiles.
- bool address_import = ImportAddressProfiles(submitted_form);
+ bool address_import = false;
+ if (profile_autofill_enabled) {
+ address_import = ImportAddressProfiles(submitted_form);
+ }
if (cc_import || address_import)
return true;
@@ -291,7 +350,14 @@ bool FormDataImporter::ImportAddressProfileForSection(
if (!IsValidLearnableProfile(candidate_profile, app_locale_))
return false;
- personal_data_manager_->SaveImportedProfile(candidate_profile);
+ // Delaying |SaveImportedProfile| is safe here because PersonalDataManager
+ // outlives this class.
+ client_->ConfirmSaveAutofillProfile(
+ candidate_profile,
+ base::BindOnce(
+ base::IgnoreResult(&PersonalDataManager::SaveImportedProfile),
+ base::Unretained(personal_data_manager_), candidate_profile));
+
return true;
}
@@ -326,6 +392,9 @@ bool FormDataImporter::ImportCreditCard(
}
AutofillMetrics::LogSubmittedCardStateMetric(
AutofillMetrics::HAS_CARD_NUMBER_AND_EXPIRATION_DATE);
+ // Can import one valid card per form. Start by treating it as NEW_CARD, but
+ // overwrite this type if we discover it is already a local or server card.
+ imported_credit_card_record_type_ = ImportedCreditCardRecordType::NEW_CARD;
// Attempt to merge with an existing credit card. Don't present a prompt if we
// have already saved this card number, unless |should_return_local_card| is
@@ -339,13 +408,14 @@ bool FormDataImporter::ImportCreditCard(
CreditCard card_copy(*card);
if (card_copy.UpdateFromImportedCard(candidate_credit_card, app_locale_)) {
personal_data_manager_->UpdateCreditCard(card_copy);
+ // Mark that the credit card imported from the submitted form is
+ // already a local card.
+ imported_credit_card_record_type_ =
+ ImportedCreditCardRecordType::LOCAL_CARD;
// If we should not return the local card, return that we merged it,
// without setting |imported_credit_card|.
if (!should_return_local_card)
return true;
- // Mark that the credit card potentially being offered to upload is
- // already a local card.
- offering_upload_of_local_credit_card_ = true;
break;
}
@@ -359,6 +429,9 @@ bool FormDataImporter::ImportCreditCard(
for (const CreditCard* card :
personal_data_manager_->GetServerCreditCards()) {
if (candidate_credit_card.HasSameNumberAs(*card)) {
+ // Mark that the imported credit card is a server card.
+ imported_credit_card_record_type_ =
+ ImportedCreditCardRecordType::SERVER_CARD;
// Record metric on whether expiration dates matched.
if (candidate_credit_card.expiration_month() ==
card->expiration_month() &&
@@ -378,7 +451,6 @@ bool FormDataImporter::ImportCreditCard(
return false;
}
}
-
*imported_credit_card = std::make_unique<CreditCard>(candidate_credit_card);
return true;
}
diff --git a/chromium/components/autofill/core/browser/form_data_importer.h b/chromium/components/autofill/core/browser/form_data_importer.h
index f60dc1b6794..596fd065c8d 100644
--- a/chromium/components/autofill/core/browser/form_data_importer.h
+++ b/chromium/components/autofill/core/browser/form_data_importer.h
@@ -15,6 +15,7 @@
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/credit_card_save_manager.h"
#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/local_card_migration_manager.h"
#include "components/autofill/core/browser/payments/payments_client.h"
#include "components/autofill/core/browser/personal_data_manager.h"
@@ -25,6 +26,18 @@ namespace autofill {
// Owned by AutofillManager.
class FormDataImporter {
public:
+ // Record type of the credit card imported from the form, if one exists.
+ enum ImportedCreditCardRecordType {
+ // No card was successfully imported from the form.
+ NO_CARD,
+ // The imported card is already stored locally on the device.
+ LOCAL_CARD,
+ // The imported card is already known to be a server card (either masked or
+ // unmasked).
+ SERVER_CARD,
+ // The imported card is not currently stored with the browser.
+ NEW_CARD,
+ };
// The parameters should outlive the FormDataImporter.
FormDataImporter(AutofillClient* client,
payments::PaymentsClient* payments_client,
@@ -37,6 +50,7 @@ class FormDataImporter {
// |credit_card_autofill_enabled| is set to |true|, also begins the process to
// offer local or upload credit card save.
void ImportFormData(const FormStructure& submitted_form,
+ bool profile_autofill_enabled,
bool credit_card_autofill_enabled);
// Extract credit card from the form structure. This function allows for
@@ -54,6 +68,12 @@ class FormDataImporter {
credit_card_save_manager_ = std::move(credit_card_save_manager);
}
+ // Exposed for testing.
+ void set_local_card_migration_manager(
+ std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager) {
+ local_card_migration_manager_ = std::move(local_card_migration_manager);
+ }
+
private:
// Scans the given |form| for importable Autofill data. If the form includes
// sufficient address data for a new profile, it is immediately imported. If
@@ -66,6 +86,7 @@ class FormDataImporter {
// to upload it. Returns |true| if sufficient address or credit card data
// was found. Exposed for testing.
bool ImportFormData(const FormStructure& form,
+ bool profile_autofill_enabled,
bool credit_card_autofill_enabled,
bool should_return_local_card,
std::unique_ptr<CreditCard>* imported_credit_card);
@@ -95,18 +116,25 @@ class FormDataImporter {
CreditCard ExtractCreditCardFromForm(const FormStructure& form,
bool* hasDuplicateFieldType);
+ // The associated autofill client. Weak reference.
+ AutofillClient* client_;
+
// Responsible for managing credit card save flows (local or upload).
std::unique_ptr<CreditCardSaveManager> credit_card_save_manager_;
+ // Responsible for migrating locally saved credit cards to Google Pay.
+ std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager_;
+
// The personal data manager, used to save and load personal data to/from the
// web database. This is overridden by the AutofillManagerTest.
// Weak reference.
// May be NULL. NULL indicates OTR.
PersonalDataManager* personal_data_manager_;
- // For metrics, to be passed to |credit_card_save_manager_|. Notes if the
- // credit card being offered for upload is already a locally-saved card.
- bool offering_upload_of_local_credit_card_ = false;
+ // Represents the type of the imported credit card from the submitted form.
+ // It will be used to determine whether to offer Upstream or card migration.
+ // Will be passed to |credit_card_save_manager_| for metrics.
+ ImportedCreditCardRecordType imported_credit_card_record_type_;
std::string app_locale_;
@@ -119,14 +147,36 @@ class FormDataImporter {
AllowDuplicateMaskedServerCardIfFlagEnabled);
FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest, DontDuplicateFullServerCard);
FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest, DontDuplicateMaskedServerCard);
+ FRIEND_TEST_ALL_PREFIXES(
+ FormDataImporterTest,
+ ImportFormData_ImportCreditCardRecordType_FullServerCard);
FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest,
- ImportCreditCard_TrackOfferingUploadOfLocalCard);
+ ImportFormData_ImportCreditCardRecordType_LocalCard);
+ FRIEND_TEST_ALL_PREFIXES(
+ FormDataImporterTest,
+ ImportFormData_ImportCreditCardRecordType_MaskedServerCard);
FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest,
- ImportCreditCard_TrackOfferingUploadOfNewCard);
+ ImportFormData_ImportCreditCardRecordType_NewCard);
+ FRIEND_TEST_ALL_PREFIXES(
+ FormDataImporterTest,
+ ImportFormData_ImportCreditCardRecordType_NoCard_ExpiredCard);
+ FRIEND_TEST_ALL_PREFIXES(
+ FormDataImporterTest,
+ ImportFormData_ImportCreditCardRecordType_NoCard_InvalidCardNumber);
+ FRIEND_TEST_ALL_PREFIXES(
+ FormDataImporterTest,
+ ImportFormData_ImportCreditCardRecordType_NoCard_NoCardOnForm);
FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest,
ImportFormData_OneAddressCreditCardDisabled);
FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest,
ImportFormData_OneAddressOneCreditCard);
+ FRIEND_TEST_ALL_PREFIXES(
+ FormDataImporterTest,
+ ImportFormData_SecondImportResetsCreditCardRecordType);
+ FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest,
+ ImportFormData_AddressesDisabledOneCreditCard);
+ FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest,
+ ImportFormData_AddressCreditCardDisabled);
FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest,
ImportFormData_TwoAddressesOneCreditCard);
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 40237ad636e..3c0c3300f53 100644
--- a/chromium/components/autofill/core/browser/form_data_importer_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_data_importer_unittest.cc
@@ -21,7 +21,7 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -34,6 +34,7 @@
#include "components/autofill/core/browser/form_structure.h"
#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/webdata/autofill_table.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/autofill/core/common/autofill_constants.h"
@@ -117,7 +118,7 @@ class FormDataImporterTestBase {
personal_data_manager_.reset(new PersonalDataManager("en"));
personal_data_manager_->Init(
scoped_refptr<AutofillWebDataService>(autofill_database_service_),
- prefs_.get(), nullptr, is_incognito);
+ nullptr, prefs_.get(), nullptr, is_incognito);
personal_data_manager_->AddObserver(&personal_data_observer_);
personal_data_manager_->OnSyncServiceInitialized(nullptr);
@@ -182,11 +183,13 @@ class FormDataImporterTestBase {
const char* exp_cc_month,
const char* exp_cc_year) {
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card));
ASSERT_TRUE(imported_credit_card);
- personal_data_manager_->SaveImportedCreditCard(*imported_credit_card);
+ personal_data_manager_->OnAcceptedLocalCreditCardSave(
+ *imported_credit_card);
base::RunLoop run_loop;
EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
@@ -218,6 +221,7 @@ class FormDataImporterTestBase {
scoped_refptr<WebDatabaseService> web_database_;
AutofillTable* autofill_table_; // weak ref
PersonalDataLoadedObserverMock personal_data_observer_;
+ std::unique_ptr<TestAutofillClient> autofill_client_;
std::unique_ptr<PersonalDataManager> personal_data_manager_;
std::unique_ptr<FormDataImporter> form_data_importer_;
};
@@ -243,11 +247,13 @@ class FormDataImporterTest : public FormDataImporterTestBase,
WebDataServiceBase::ProfileErrorCallback());
autofill_database_service_->Init();
+ autofill_client_ = std::make_unique<TestAutofillClient>();
+
test::DisableSystemServices(prefs_.get());
ResetPersonalDataManager(USER_MODE_NORMAL);
form_data_importer_.reset(
- new FormDataImporter(/*AutofillClient=*/nullptr,
+ new FormDataImporter(autofill_client_.get(),
/*payments::PaymentsClient=*/nullptr,
personal_data_manager_.get(), "en"));
@@ -288,7 +294,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles) {
test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_TRUE(ImportAddressProfiles(form_structure));
WaitForOnPersonalDataChanged();
@@ -325,7 +332,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_BadEmail) {
test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_FALSE(ImportAddressProfiles(form_structure));
ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size());
@@ -354,7 +362,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_TwoEmails) {
"example@example.com", "text", &field);
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_TRUE(ImportAddressProfiles(form_structure));
WaitForOnPersonalDataChanged();
@@ -385,7 +394,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_TwoDifferentEmails) {
&field);
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_FALSE(ImportAddressProfiles(form_structure));
ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size());
@@ -405,7 +415,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_NotEnoughFilledFields) {
"4111 1111 1111 1111", "text", &field);
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_FALSE(ImportAddressProfiles(form_structure));
ASSERT_EQ(0U, personal_data_manager_->GetProfiles().size());
@@ -431,7 +442,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MinimumAddressUSA) {
test::CreateTestFormField("Country:", "country", "USA", "text", &field);
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_TRUE(ImportAddressProfiles(form_structure));
WaitForOnPersonalDataChanged();
@@ -458,7 +470,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MinimumAddressGB) {
&field);
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_TRUE(ImportAddressProfiles(form_structure));
WaitForOnPersonalDataChanged();
@@ -480,7 +493,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MinimumAddressGI) {
test::CreateTestFormField("Country:", "country", "Gibraltar", "text", &field);
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_TRUE(ImportAddressProfiles(form_structure));
WaitForOnPersonalDataChanged();
@@ -520,7 +534,8 @@ TEST_F(FormDataImporterTest,
test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_TRUE(ImportAddressProfiles(form_structure));
WaitForOnPersonalDataChanged();
@@ -559,7 +574,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MultilineAddress) {
test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_TRUE(ImportAddressProfiles(form_structure));
WaitForOnPersonalDataChanged();
@@ -599,7 +615,8 @@ TEST_F(FormDataImporterTest,
form1.fields.push_back(field);
FormStructure form_structure1(form1);
- form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(ImportAddressProfiles(form_structure1));
WaitForOnPersonalDataChanged();
@@ -635,7 +652,8 @@ TEST_F(FormDataImporterTest,
form2.fields.push_back(field);
FormStructure form_structure2(form2);
- form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(ImportAddressProfiles(form_structure2));
WaitForOnPersonalDataChanged();
@@ -692,7 +710,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_TwoValidProfilesSameForm) {
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_TRUE(ImportAddressProfiles(form_structure));
WaitForOnPersonalDataChanged();
@@ -766,7 +785,8 @@ TEST_F(FormDataImporterTest,
// Still able to do the import.
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_TRUE(ImportAddressProfiles(form_structure));
WaitForOnPersonalDataChanged();
@@ -848,7 +868,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_ThreeValidProfilesSameForm) {
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_TRUE(ImportAddressProfiles(form_structure));
WaitForOnPersonalDataChanged();
@@ -902,7 +923,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_SameProfileWithConflict) {
form1.fields.push_back(field);
FormStructure form_structure1(form1);
- form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(ImportAddressProfiles(form_structure1));
WaitForOnPersonalDataChanged();
@@ -948,7 +970,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_SameProfileWithConflict) {
form2.fields.push_back(field);
FormStructure form_structure2(form2);
- form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(ImportAddressProfiles(form_structure2));
WaitForOnPersonalDataChanged();
@@ -985,7 +1008,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MissingInfoInOld) {
form1.fields.push_back(field);
FormStructure form_structure1(form1);
- form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(ImportAddressProfiles(form_structure1));
WaitForOnPersonalDataChanged();
@@ -1021,7 +1045,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MissingInfoInOld) {
form2.fields.push_back(field);
FormStructure form_structure2(form2);
- form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(ImportAddressProfiles(form_structure2));
WaitForOnPersonalDataChanged();
@@ -1065,7 +1090,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MissingInfoInNew) {
form1.fields.push_back(field);
FormStructure form_structure1(form1);
- form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(ImportAddressProfiles(form_structure1));
WaitForOnPersonalDataChanged();
@@ -1103,7 +1129,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_MissingInfoInNew) {
form2.fields.push_back(field);
FormStructure form_structure2(form2);
- form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(ImportAddressProfiles(form_structure2));
WaitForOnPersonalDataChanged();
@@ -1139,7 +1166,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_InsufficientAddress) {
form1.fields.push_back(field);
FormStructure form_structure1(form1);
- form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_FALSE(ImportAddressProfiles(form_structure1));
// Since no refresh is expected, reload the data from the database to make
@@ -1191,7 +1219,8 @@ TEST_F(FormDataImporterTest,
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_TRUE(ImportAddressProfiles(form_structure));
// Wait for the refresh, which in this case is a no-op.
@@ -1211,7 +1240,8 @@ TEST_F(FormDataImporterTest,
form.fields[0] = field;
FormStructure form_structure2(form);
- form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(ImportAddressProfiles(form_structure2));
// Wait for the refresh, which in this case is a no-op.
@@ -1251,7 +1281,8 @@ TEST_F(FormDataImporterTest, ImportAddressProfiles_UnrecognizedCountry) {
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_FALSE(ImportAddressProfiles(form_structure));
// Since no refresh is expected, reload the data from the database to make
@@ -1290,7 +1321,8 @@ TEST_F(FormDataImporterTest,
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_TRUE(ImportAddressProfiles(form_structure));
WaitForOnPersonalDataChanged();
@@ -1337,7 +1369,8 @@ TEST_F(FormDataImporterTest,
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_FALSE(ImportAddressProfiles(form_structure));
// Since no refresh is expected, reload the data from the database to make
@@ -1358,7 +1391,8 @@ TEST_F(FormDataImporterTest, ImportCreditCard_Valid) {
"2999");
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
base::HistogramTester histogram_tester;
EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card));
@@ -1366,7 +1400,7 @@ TEST_F(FormDataImporterTest, ImportCreditCard_Valid) {
histogram_tester.ExpectUniqueSample(
"Autofill.SubmittedCardState",
AutofillMetrics::HAS_CARD_NUMBER_AND_EXPIRATION_DATE, 1);
- personal_data_manager_->SaveImportedCreditCard(*imported_credit_card);
+ personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card);
WaitForOnPersonalDataChanged();
@@ -1386,7 +1420,8 @@ TEST_F(FormDataImporterTest, ImportCreditCard_InvalidCardNumber) {
"2999");
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
base::HistogramTester histogram_tester;
EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card));
@@ -1409,7 +1444,8 @@ TEST_F(FormDataImporterTest, ImportCreditCard_InvalidExpiryDate) {
"2999");
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
base::HistogramTester histogram_tester;
EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card));
@@ -1445,7 +1481,8 @@ TEST_F(FormDataImporterTest, ImportCreditCard_MonthSelectInvalidText) {
form.fields[2].option_contents = contents;
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
base::HistogramTester histogram_tester;
EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card));
@@ -1453,7 +1490,7 @@ TEST_F(FormDataImporterTest, ImportCreditCard_MonthSelectInvalidText) {
histogram_tester.ExpectUniqueSample(
"Autofill.SubmittedCardState",
AutofillMetrics::HAS_CARD_NUMBER_AND_EXPIRATION_DATE, 1);
- personal_data_manager_->SaveImportedCreditCard(*imported_credit_card);
+ personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card);
WaitForOnPersonalDataChanged();
@@ -1474,11 +1511,12 @@ TEST_F(FormDataImporterTest, ImportCreditCard_TwoValidCards) {
"2999");
FormStructure form_structure1(form1);
- form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card));
ASSERT_TRUE(imported_credit_card);
- personal_data_manager_->SaveImportedCreditCard(*imported_credit_card);
+ personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card);
WaitForOnPersonalDataChanged();
@@ -1495,11 +1533,12 @@ TEST_F(FormDataImporterTest, ImportCreditCard_TwoValidCards) {
AddFullCreditCardForm(&form2, "", "5500 0000 0000 0004", "02", "2999");
FormStructure form_structure2(form2);
- form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::unique_ptr<CreditCard> imported_credit_card2;
EXPECT_TRUE(ImportCreditCard(form_structure2, false, &imported_credit_card2));
ASSERT_TRUE(imported_credit_card2);
- personal_data_manager_->SaveImportedCreditCard(*imported_credit_card2);
+ personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card2);
WaitForOnPersonalDataChanged();
@@ -1613,7 +1652,8 @@ TEST_F(FormDataImporterTest,
// The card should not be offered to be saved locally because the feature flag
// is disabled.
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card));
ASSERT_FALSE(imported_credit_card);
@@ -1642,7 +1682,8 @@ TEST_F(FormDataImporterTest, ImportCreditCard_DuplicateServerCards_FullCard) {
// The card should not be offered to be saved locally because it only matches
// the full server card.
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card));
ASSERT_FALSE(imported_credit_card);
@@ -1655,11 +1696,12 @@ TEST_F(FormDataImporterTest, ImportCreditCard_SameCreditCardWithConflict) {
"2998");
FormStructure form_structure1(form1);
- form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card));
ASSERT_TRUE(imported_credit_card);
- personal_data_manager_->SaveImportedCreditCard(*imported_credit_card);
+ personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card);
WaitForOnPersonalDataChanged();
@@ -1678,7 +1720,8 @@ TEST_F(FormDataImporterTest, ImportCreditCard_SameCreditCardWithConflict) {
/* different year */ "2999");
FormStructure form_structure2(form2);
- form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::unique_ptr<CreditCard> imported_credit_card2;
EXPECT_TRUE(ImportCreditCard(form_structure2, false, &imported_credit_card2));
EXPECT_FALSE(imported_credit_card2);
@@ -1703,11 +1746,12 @@ TEST_F(FormDataImporterTest, ImportCreditCard_ShouldReturnLocalCard) {
"2998");
FormStructure form_structure1(form1);
- form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card));
ASSERT_TRUE(imported_credit_card);
- personal_data_manager_->SaveImportedCreditCard(*imported_credit_card);
+ personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card);
WaitForOnPersonalDataChanged();
@@ -1726,7 +1770,8 @@ TEST_F(FormDataImporterTest, ImportCreditCard_ShouldReturnLocalCard) {
/* different year */ "2999");
FormStructure form_structure2(form2);
- form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::unique_ptr<CreditCard> imported_credit_card2;
EXPECT_TRUE(ImportCreditCard(form_structure2,
/* should_return_local_card= */ true,
@@ -1754,11 +1799,12 @@ TEST_F(FormDataImporterTest, ImportCreditCard_EmptyCardWithConflict) {
"2998");
FormStructure form_structure1(form1);
- form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card));
ASSERT_TRUE(imported_credit_card);
- personal_data_manager_->SaveImportedCreditCard(*imported_credit_card);
+ personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card);
WaitForOnPersonalDataChanged();
@@ -1776,7 +1822,8 @@ TEST_F(FormDataImporterTest, ImportCreditCard_EmptyCardWithConflict) {
"2999");
FormStructure form_structure2(form2);
- form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::unique_ptr<CreditCard> imported_credit_card2;
EXPECT_FALSE(
ImportCreditCard(form_structure2, false, &imported_credit_card2));
@@ -1803,11 +1850,12 @@ TEST_F(FormDataImporterTest, ImportCreditCard_MissingInfoInNew) {
"2999");
FormStructure form_structure1(form1);
- form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure1.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card));
ASSERT_TRUE(imported_credit_card);
- personal_data_manager_->SaveImportedCreditCard(*imported_credit_card);
+ personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card);
WaitForOnPersonalDataChanged();
@@ -1826,7 +1874,8 @@ TEST_F(FormDataImporterTest, ImportCreditCard_MissingInfoInNew) {
"4111-1111-1111-1111", "01", "2999");
FormStructure form_structure2(form2);
- form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure2.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::unique_ptr<CreditCard> imported_credit_card2;
EXPECT_TRUE(ImportCreditCard(form_structure2, false, &imported_credit_card2));
EXPECT_FALSE(imported_credit_card2);
@@ -1851,7 +1900,8 @@ TEST_F(FormDataImporterTest, ImportCreditCard_MissingInfoInNew) {
/* no year */ nullptr);
FormStructure form_structure3(form3);
- form_structure3.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure3.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
std::unique_ptr<CreditCard> imported_credit_card3;
EXPECT_FALSE(
ImportCreditCard(form_structure3, false, &imported_credit_card3));
@@ -1893,7 +1943,8 @@ TEST_F(FormDataImporterTest, ImportCreditCard_MissingInfoInOld) {
/* different year */ "2999");
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card));
EXPECT_FALSE(imported_credit_card);
@@ -1934,7 +1985,8 @@ TEST_F(FormDataImporterTest, ImportCreditCard_SameCardWithSeparators) {
"2999");
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card));
EXPECT_FALSE(imported_credit_card);
@@ -1974,7 +2026,8 @@ TEST_F(FormDataImporterTest,
/* different year */ "2999");
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card));
ASSERT_FALSE(imported_credit_card);
@@ -1990,8 +2043,9 @@ TEST_F(FormDataImporterTest,
EXPECT_EQ(0, credit_card.Compare(*results[0]));
}
-// Ensures that |offering_upload_of_local_credit_card_| is set correctly.
-TEST_F(FormDataImporterTest, ImportCreditCard_TrackOfferingUploadOfLocalCard) {
+// Ensures that |imported_credit_card_record_type_| is set and reset correctly.
+TEST_F(FormDataImporterTest,
+ ImportFormData_SecondImportResetsCreditCardRecordType) {
// Start with a single valid credit card stored via the preferences.
CreditCard saved_credit_card(base::GenerateGUID(), test::kEmptyOrigin);
test::SetCreditCardInfo(&saved_credit_card, "Biggie Smalls",
@@ -2011,30 +2065,291 @@ TEST_F(FormDataImporterTest, ImportCreditCard_TrackOfferingUploadOfLocalCard) {
"2999");
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
- EXPECT_TRUE(ImportCreditCard(form_structure, true, &imported_credit_card));
+ EXPECT_TRUE(form_data_importer_->ImportFormData(
+ form_structure, /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
+ /*should_return_local_card=*/true, &imported_credit_card));
ASSERT_TRUE(imported_credit_card);
- // |offering_upload_of_local_credit_card_| should be true because upload was
+ // |imported_credit_card_record_type_| should be LOCAL_CARD because upload was
// offered and the card is a local card already on the device.
- ASSERT_TRUE(form_data_importer_->offering_upload_of_local_credit_card_);
+ ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ ==
+ FormDataImporter::ImportedCreditCardRecordType::LOCAL_CARD);
+
+ // Second form is filled with a new card so
+ // |imported_credit_card_record_type_| should be reset.
+ // Simulate a form submission with a new card.
+ FormData form2;
+ AddFullCreditCardForm(&form2, "Biggie Smalls", "4012888888881881", "01",
+ "2999");
+
+ FormStructure form_structure2(form2);
+ form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
+ std::unique_ptr<CreditCard> imported_credit_card2;
+ EXPECT_TRUE(form_data_importer_->ImportFormData(
+ form_structure2, /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
+ /*should_return_local_card=*/true, &imported_credit_card2));
+ ASSERT_TRUE(imported_credit_card2);
+ // |imported_credit_card_record_type_| should be NEW_CARD because the imported
+ // card is not already on the device.
+ ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ ==
+ FormDataImporter::ImportedCreditCardRecordType::NEW_CARD);
+
+ // Third form is an address form and set |credit_card_autofill_enabled| to be
+ // false so that the ImportCreditCard won't be called.
+ // |imported_credit_card_record_type_| should still be reset even if
+ // ImportCreditCard is not called. Simulate a form submission with no card.
+ FormData form3;
+ FormFieldData field;
+ test::CreateTestFormField("First name:", "first_name", "George", "text",
+ &field);
+ form3.fields.push_back(field);
+ test::CreateTestFormField("Last name:", "last_name", "Washington", "text",
+ &field);
+ form3.fields.push_back(field);
+ test::CreateTestFormField("Email:", "email", "bogus@example.com", "text",
+ &field);
+ form3.fields.push_back(field);
+ test::CreateTestFormField("Address:", "address1", "21 Laussat St", "text",
+ &field);
+ form3.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form3.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form3.fields.push_back(field);
+ test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
+ form3.fields.push_back(field);
+ FormStructure form_structure3(form3);
+ form_structure3.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
+ std::unique_ptr<CreditCard> imported_credit_card3;
+ EXPECT_TRUE(form_data_importer_->ImportFormData(
+ form_structure3, /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/false,
+ /*should_return_local_card=*/true, &imported_credit_card3));
+ // |imported_credit_card_record_type_| should be NO_CARD because no valid card
+ // was imported from the form.
+ ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ ==
+ FormDataImporter::ImportedCreditCardRecordType::NO_CARD);
}
-// Ensures that |offering_upload_of_local_credit_card_| is set correctly.
-TEST_F(FormDataImporterTest, ImportCreditCard_TrackOfferingUploadOfNewCard) {
+// Ensures that |imported_credit_card_record_type_| is set correctly.
+TEST_F(FormDataImporterTest,
+ ImportFormData_ImportCreditCardRecordType_NewCard) {
// Simulate a form submission with a new credit card.
FormData form;
AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01",
"2999");
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
+ std::unique_ptr<CreditCard> imported_credit_card;
+ EXPECT_TRUE(form_data_importer_->ImportFormData(
+ form_structure, /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
+ /*should_return_local_card=*/true, &imported_credit_card));
+ ASSERT_TRUE(imported_credit_card);
+ // |imported_credit_card_record_type_| should be NEW_CARD because the imported
+ // card is not already on the device.
+ ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ ==
+ FormDataImporter::ImportedCreditCardRecordType::NEW_CARD);
+}
+
+// Ensures that |imported_credit_card_record_type_| is set correctly.
+TEST_F(FormDataImporterTest,
+ ImportFormData_ImportCreditCardRecordType_LocalCard) {
+ // Start with a single valid credit card stored via the preferences.
+ CreditCard saved_credit_card(base::GenerateGUID(), test::kEmptyOrigin);
+ test::SetCreditCardInfo(&saved_credit_card, "Biggie Smalls",
+ "4111 1111 1111 1111" /* Visa */, "01", "2999", "");
+ personal_data_manager_->AddCreditCard(saved_credit_card);
+
+ WaitForOnPersonalDataChanged();
+
+ const std::vector<CreditCard*>& results =
+ personal_data_manager_->GetCreditCards();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, saved_credit_card.Compare(*results[0]));
+
+ // Simulate a form submission with the same card.
+ FormData form;
+ AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01",
+ "2999");
+
+ FormStructure form_structure(form);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
- EXPECT_TRUE(ImportCreditCard(form_structure, true, &imported_credit_card));
+ EXPECT_TRUE(form_data_importer_->ImportFormData(
+ form_structure, /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
+ /*should_return_local_card=*/true, &imported_credit_card));
ASSERT_TRUE(imported_credit_card);
- // |offering_upload_of_local_credit_card_| should be false because upload was
- // offered but the card is NOT a local card already on the device.
- ASSERT_FALSE(form_data_importer_->offering_upload_of_local_credit_card_);
+ // |imported_credit_card_record_type_| should be LOCAL_CARD because upload was
+ // offered and the card is a local card already on the device.
+ ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ ==
+ FormDataImporter::ImportedCreditCardRecordType::LOCAL_CARD);
+}
+
+// Ensures that |imported_credit_card_record_type_| is set correctly.
+TEST_F(FormDataImporterTest,
+ ImportFormData_ImportCreditCardRecordType_MaskedServerCard) {
+ // Add a masked server card.
+ std::vector<CreditCard> server_cards;
+ server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123"));
+ test::SetCreditCardInfo(&server_cards.back(), "Biggie Smalls",
+ "1111" /* Visa */, "01", "2999", "");
+ server_cards.back().SetNetworkForMaskedCard(kVisaCard);
+ test::SetServerCreditCards(autofill_table_, server_cards);
+
+ // Make sure everything is set up correctly.
+ personal_data_manager_->Refresh();
+ WaitForOnPersonalDataChanged();
+ EXPECT_EQ(1U, personal_data_manager_->GetCreditCards().size());
+
+ // Simulate a form submission with the same masked server card.
+ FormData form;
+ AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01",
+ "2999");
+
+ FormStructure form_structure(form);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
+ std::unique_ptr<CreditCard> imported_credit_card;
+ EXPECT_FALSE(form_data_importer_->ImportFormData(
+ form_structure, /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
+ /*should_return_local_card=*/true, &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+ // |imported_credit_card_record_type_| should be SERVER_CARD.
+ ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ ==
+ FormDataImporter::ImportedCreditCardRecordType::SERVER_CARD);
+}
+
+// Ensures that |imported_credit_card_record_type_| is set correctly.
+TEST_F(FormDataImporterTest,
+ ImportFormData_ImportCreditCardRecordType_FullServerCard) {
+ // Add a full server card.
+ std::vector<CreditCard> server_cards;
+ server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789"));
+ test::SetCreditCardInfo(&server_cards.back(), "Biggie Smalls",
+ "378282246310005" /* American Express */, "04",
+ "2999", "1");
+ test::SetServerCreditCards(autofill_table_, server_cards);
+
+ // Make sure everything is set up correctly.
+ personal_data_manager_->Refresh();
+ WaitForOnPersonalDataChanged();
+ EXPECT_EQ(1U, personal_data_manager_->GetCreditCards().size());
+
+ // Simulate a form submission with the same full server card.
+ FormData form;
+ AddFullCreditCardForm(&form, "Biggie Smalls", "378282246310005", "04",
+ "2999");
+
+ FormStructure form_structure(form);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
+ std::unique_ptr<CreditCard> imported_credit_card;
+ EXPECT_FALSE(form_data_importer_->ImportFormData(
+ form_structure, /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
+ /*should_return_local_card=*/true, &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+ // |imported_credit_card_record_type_| should be SERVER_CARD.
+ ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ ==
+ FormDataImporter::ImportedCreditCardRecordType::SERVER_CARD);
+}
+
+// Ensures that |imported_credit_card_record_type_| is set correctly.
+TEST_F(FormDataImporterTest,
+ ImportFormData_ImportCreditCardRecordType_NoCard_InvalidCardNumber) {
+ // Simulate a form submission using a credit card with an invalid card number.
+ FormData form;
+ AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1112", "01",
+ "2999");
+
+ FormStructure form_structure(form);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
+ std::unique_ptr<CreditCard> imported_credit_card;
+ EXPECT_FALSE(form_data_importer_->ImportFormData(
+ form_structure, /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
+ /*should_return_local_card=*/true, &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+ // |imported_credit_card_record_type_| should be NO_CARD because no valid card
+ // was successfully imported from the form.
+ ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ ==
+ FormDataImporter::ImportedCreditCardRecordType::NO_CARD);
+}
+
+// Ensures that |imported_credit_card_record_type_| is set correctly.
+TEST_F(FormDataImporterTest,
+ ImportFormData_ImportCreditCardRecordType_NoCard_ExpiredCard) {
+ // Simulate a form submission with an expired credit card.
+ FormData form;
+ AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01",
+ "1999");
+
+ FormStructure form_structure(form);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
+ std::unique_ptr<CreditCard> imported_credit_card;
+ EXPECT_FALSE(form_data_importer_->ImportFormData(
+ form_structure, /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
+ /*should_return_local_card=*/true, &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+ // |imported_credit_card_record_type_| should be NO_CARD because no valid card
+ // was successfully imported from the form.
+ ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ ==
+ FormDataImporter::ImportedCreditCardRecordType::NO_CARD);
+}
+
+// Ensures that |imported_credit_card_record_type_| is set correctly.
+TEST_F(FormDataImporterTest,
+ ImportFormData_ImportCreditCardRecordType_NoCard_NoCardOnForm) {
+ // Simulate a form submission with no credit card on form.
+ FormData form;
+ FormFieldData field;
+ test::CreateTestFormField("First name:", "first_name", "George", "text",
+ &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Last name:", "last_name", "Washington", "text",
+ &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Email:", "email", "bogus@example.com", "text",
+ &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Address:", "address1", "21 Laussat St", "text",
+ &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
+ std::unique_ptr<CreditCard> imported_credit_card;
+ EXPECT_TRUE(form_data_importer_->ImportFormData(
+ form_structure, /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
+ /*should_return_local_card=*/true, &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+ // |imported_credit_card_record_type_| should be NO_CARD because the form
+ // doesn't have credit card section.
+ ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ ==
+ FormDataImporter::ImportedCreditCardRecordType::NO_CARD);
}
// ImportFormData tests (both addresses and credit cards).
@@ -2069,14 +2384,16 @@ TEST_F(FormDataImporterTest, ImportFormData_OneAddressOneCreditCard) {
"2999");
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_TRUE(form_data_importer_->ImportFormData(
form_structure,
+ /*profile_autofill_enabled=*/true,
/*credit_card_autofill_enabled=*/true,
/*should_return_local_card=*/false, &imported_credit_card));
ASSERT_TRUE(imported_credit_card);
- personal_data_manager_->SaveImportedCreditCard(*imported_credit_card);
+ personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card);
WaitForOnPersonalDataChanged();
@@ -2146,14 +2463,16 @@ TEST_F(FormDataImporterTest, ImportFormData_TwoAddressesOneCreditCard) {
"2999");
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
// Still returns true because the credit card import was successful.
EXPECT_TRUE(form_data_importer_->ImportFormData(
- form_structure, /*credit_card_autofill_enabled=*/true,
+ form_structure, /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
/*should_return_local_card=*/false, &imported_credit_card));
ASSERT_TRUE(imported_credit_card);
- personal_data_manager_->SaveImportedCreditCard(*imported_credit_card);
+ personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card);
WaitForOnPersonalDataChanged();
@@ -2170,6 +2489,63 @@ TEST_F(FormDataImporterTest, ImportFormData_TwoAddressesOneCreditCard) {
EXPECT_EQ(0, expected_card.Compare(*results[0]));
}
+// Test that a form with both address and credit card sections imports only the
+// the credit card if addresses are disabled.
+TEST_F(FormDataImporterTest, ImportFormData_AddressesDisabledOneCreditCard) {
+ FormData form;
+ FormFieldData field;
+ // Address section.
+ test::CreateTestFormField("First name:", "first_name", "George", "text",
+ &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Last name:", "last_name", "Washington", "text",
+ &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Email:", "email", "theprez@gmail.com", "text",
+ &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Address:", "address1", "21 Laussat St", "text",
+ &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
+ form.fields.push_back(field);
+
+ // Credit card section.
+ AddFullCreditCardForm(&form, "Biggie Smalls", "4111-1111-1111-1111", "01",
+ "2999");
+
+ FormStructure form_structure(form);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
+ std::unique_ptr<CreditCard> imported_credit_card;
+ EXPECT_TRUE(form_data_importer_->ImportFormData(
+ form_structure, /*profile_autofill_enabled=*/false,
+ /*credit_card_autofill_enabled=*/true,
+ /*should_return_local_card=*/false, &imported_credit_card));
+ ASSERT_TRUE(imported_credit_card);
+ personal_data_manager_->OnAcceptedLocalCreditCardSave(*imported_credit_card);
+
+ WaitForOnPersonalDataChanged();
+
+ // Test that addresses were not saved.
+ EXPECT_EQ(0U, personal_data_manager_->GetProfiles().size());
+
+ // Test that the credit card has been saved.
+ CreditCard expected_card(base::GenerateGUID(), test::kEmptyOrigin);
+ test::SetCreditCardInfo(&expected_card, "Biggie Smalls", "4111111111111111",
+ "01", "2999", "");
+ const std::vector<CreditCard*>& results =
+ personal_data_manager_->GetCreditCards();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, expected_card.Compare(*results[0]));
+}
+
+// Test that a form with both address and credit card sections imports only the
+// the address if credit cards are disabled.
TEST_F(FormDataImporterTest, ImportFormData_OneAddressCreditCardDisabled) {
FormData form;
FormFieldData field;
@@ -2198,10 +2574,13 @@ TEST_F(FormDataImporterTest, ImportFormData_OneAddressCreditCardDisabled) {
"2999");
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_TRUE(form_data_importer_->ImportFormData(
- form_structure, /*credit_card_autofill_enabled=*/false,
+ form_structure,
+ /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/false,
/*should_return_local_card=*/false, &imported_credit_card));
ASSERT_FALSE(imported_credit_card);
@@ -2224,6 +2603,55 @@ TEST_F(FormDataImporterTest, ImportFormData_OneAddressCreditCardDisabled) {
ASSERT_EQ(0U, results_cards.size());
}
+// Test that a form with both address and credit card sections imports nothing
+// if both addressed and credit cards are disabled.
+TEST_F(FormDataImporterTest, ImportFormData_AddressCreditCardDisabled) {
+ FormData form;
+ FormFieldData field;
+ // Address section.
+ test::CreateTestFormField("First name:", "first_name", "George", "text",
+ &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Last name:", "last_name", "Washington", "text",
+ &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Email:", "email", "theprez@gmail.com", "text",
+ &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Address:", "address1", "21 Laussat St", "text",
+ &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
+ form.fields.push_back(field);
+
+ // Credit card section.
+ AddFullCreditCardForm(&form, "Biggie Smalls", "4111-1111-1111-1111", "01",
+ "2999");
+
+ FormStructure form_structure(form);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
+ std::unique_ptr<CreditCard> imported_credit_card;
+ EXPECT_FALSE(form_data_importer_->ImportFormData(
+ form_structure,
+ /*profile_autofill_enabled=*/false,
+ /*credit_card_autofill_enabled=*/false,
+ /*should_return_local_card=*/false, &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Test that addresses were not saved.
+ EXPECT_EQ(0U, personal_data_manager_->GetProfiles().size());
+
+ // Test that the credit card was not saved.
+ const std::vector<CreditCard*>& results_cards =
+ personal_data_manager_->GetCreditCards();
+ ASSERT_EQ(0U, results_cards.size());
+}
+
TEST_F(FormDataImporterTest, DontDuplicateMaskedServerCard) {
EnableWalletCardImport();
@@ -2261,10 +2689,12 @@ TEST_F(FormDataImporterTest, DontDuplicateMaskedServerCard) {
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_FALSE(form_data_importer_->ImportFormData(
- form_structure, /*credit_card_autofill_enabled=*/true,
+ form_structure, /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
/*should_return_local_card=*/false, &imported_credit_card));
ASSERT_FALSE(imported_credit_card);
}
@@ -2307,10 +2737,13 @@ TEST_F(FormDataImporterTest, DontDuplicateFullServerCard) {
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_FALSE(form_data_importer_->ImportFormData(
- form_structure, /*credit_card_autofill_enabled=*/true,
+ form_structure,
+ /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
/*should_return_local_card=*/false, &imported_credit_card));
EXPECT_FALSE(imported_credit_card);
}
@@ -2348,10 +2781,13 @@ TEST_F(FormDataImporterTest,
base::HistogramTester histogram_tester;
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_FALSE(form_data_importer_->ImportFormData(
- form_structure, /*credit_card_autofill_enabled=*/true,
+ form_structure,
+ /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
/*should_return_local_card=*/false, &imported_credit_card));
EXPECT_FALSE(imported_credit_card);
histogram_tester.ExpectUniqueSample(
@@ -2393,10 +2829,13 @@ TEST_F(FormDataImporterTest,
base::HistogramTester histogram_tester;
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_FALSE(form_data_importer_->ImportFormData(
- form_structure, /*credit_card_autofill_enabled=*/true,
+ form_structure,
+ /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
/*should_return_local_card=*/false, &imported_credit_card));
EXPECT_FALSE(imported_credit_card);
histogram_tester.ExpectUniqueSample(
@@ -2438,10 +2877,13 @@ TEST_F(FormDataImporterTest,
base::HistogramTester histogram_tester;
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_FALSE(form_data_importer_->ImportFormData(
- form_structure, /*credit_card_autofill_enabled=*/true,
+ form_structure,
+ /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
/*should_return_local_card=*/false, &imported_credit_card));
EXPECT_FALSE(imported_credit_card);
histogram_tester.ExpectUniqueSample(
@@ -2484,10 +2926,12 @@ TEST_F(FormDataImporterTest,
base::HistogramTester histogram_tester;
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
std::unique_ptr<CreditCard> imported_credit_card;
EXPECT_FALSE(form_data_importer_->ImportFormData(
- form_structure, /*credit_card_autofill_enabled=*/true,
+ form_structure, /*profile_autofill_enabled=*/true,
+ /*credit_card_autofill_enabled=*/true,
/*should_return_local_card=*/false, &imported_credit_card));
EXPECT_FALSE(imported_credit_card);
histogram_tester.ExpectUniqueSample(
diff --git a/chromium/components/autofill/core/browser/form_field.cc b/chromium/components/autofill/core/browser/form_field.cc
index 1c8e97639a0..c8d7bf171e3 100644
--- a/chromium/components/autofill/core/browser/form_field.cc
+++ b/chromium/components/autofill/core/browser/form_field.cc
@@ -23,6 +23,7 @@
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/name_field.h"
#include "components/autofill/core/browser/phone_field.h"
+#include "components/autofill/core/browser/search_field.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_regexes.h"
#include "components/autofill/core/common/autofill_util.h"
@@ -30,13 +31,14 @@
namespace autofill {
// There's an implicit precedence determined by the values assigned here. Email
-// is currently the most important followed by Phone, Address, Credit Card and
-// finally Name.
+// is currently the most important followed by Phone, Address, Credit Card,
+// Name, and Search.
const float FormField::kBaseEmailParserScore = 1.4f;
const float FormField::kBasePhoneParserScore = 1.3f;
const float FormField::kBaseAddressParserScore = 1.2f;
const float FormField::kBaseCreditCardParserScore = 1.1f;
const float FormField::kBaseNameParserScore = 1.0f;
+const float FormField::kBaseSearchParserScore = 0.9f;
// static
FieldCandidatesMap FormField::ParseFormFields(
@@ -77,6 +79,9 @@ FieldCandidatesMap FormField::ParseFormFields(
// Name pass.
ParseFormFieldsPass(NameField::Parse, processed_fields, &field_candidates);
+ // Search pass.
+ ParseFormFieldsPass(SearchField::Parse, processed_fields, &field_candidates);
+
// Do not autofill a form if there aren't enough fields. Otherwise, it is
// very easy to have false positives. See http://crbug.com/447332
// For <form> tags, make an exception for email fields, which are commonly
diff --git a/chromium/components/autofill/core/browser/form_field.h b/chromium/components/autofill/core/browser/form_field.h
index c721f4bed83..c14e88f2b98 100644
--- a/chromium/components/autofill/core/browser/form_field.h
+++ b/chromium/components/autofill/core/browser/form_field.h
@@ -37,23 +37,24 @@ class FormField {
// A bit-field used for matching specific parts of a field in question.
enum MatchType {
// Attributes.
- MATCH_LABEL = 1 << 0,
- MATCH_NAME = 1 << 1,
+ MATCH_LABEL = 1 << 0,
+ MATCH_NAME = 1 << 1,
// Input types.
- MATCH_TEXT = 1 << 2,
- MATCH_EMAIL = 1 << 3,
- MATCH_TELEPHONE = 1 << 4,
- MATCH_SELECT = 1 << 5,
- MATCH_TEXT_AREA = 1 << 6,
- MATCH_PASSWORD = 1 << 7,
- MATCH_NUMBER = 1 << 8,
- MATCH_ALL_INPUTS =
- MATCH_TEXT | MATCH_EMAIL | MATCH_TELEPHONE | MATCH_SELECT |
- MATCH_TEXT_AREA | MATCH_PASSWORD | MATCH_NUMBER,
+ MATCH_TEXT = 1 << 2,
+ MATCH_EMAIL = 1 << 3,
+ MATCH_TELEPHONE = 1 << 4,
+ MATCH_SELECT = 1 << 5,
+ MATCH_TEXT_AREA = 1 << 6,
+ MATCH_PASSWORD = 1 << 7,
+ MATCH_NUMBER = 1 << 8,
+ MATCH_SEARCH = 1 << 9,
+ MATCH_ALL_INPUTS = MATCH_TEXT | MATCH_EMAIL | MATCH_TELEPHONE |
+ MATCH_SELECT | MATCH_TEXT_AREA | MATCH_PASSWORD |
+ MATCH_NUMBER,
// By default match label and name for input/text types.
- MATCH_DEFAULT = MATCH_LABEL | MATCH_NAME | MATCH_TEXT,
+ MATCH_DEFAULT = MATCH_LABEL | MATCH_NAME | MATCH_TEXT,
};
// Initial values assigned to FieldCandidates by their corresponding parsers.
@@ -62,6 +63,7 @@ class FormField {
static const float kBaseAddressParserScore;
static const float kBaseCreditCardParserScore;
static const float kBaseNameParserScore;
+ static const float kBaseSearchParserScore;
// Only derived classes may instantiate.
FormField() {}
diff --git a/chromium/components/autofill/core/browser/form_field_unittest.cc b/chromium/components/autofill/core/browser/form_field_unittest.cc
index a5f89c7bca9..bd194a99e1c 100644
--- a/chromium/components/autofill/core/browser/form_field_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_field_unittest.cc
@@ -194,35 +194,4 @@ TEST(FormFieldTest, ParseFormFields) {
}
}
-// All parsers see the same form and should not modify it.
-// Furthermore, all parsers are allowed to cast their votes on what the
-// ServerFieldType for a given type should be, so for an ambiguous input more
-// than one candidate is expected.
-TEST(FormFieldTest, ParseFormFieldsImmutableForm) {
- const base::string16 unique_name = ASCIIToUTF16("blah");
- FormFieldData field_data;
- field_data.form_control_type = "text";
- field_data.name = ASCIIToUTF16("business_email_address");
-
- std::vector<std::unique_ptr<AutofillField>> fields;
- fields.push_back(std::make_unique<AutofillField>(field_data, unique_name));
-
- const FieldCandidatesMap field_candidates_map =
- FormField::ParseFormFields(fields, true);
-
- // The input form should not be modified.
- EXPECT_EQ(1U, fields.size());
-
- // The output should contain detected information for the sole field in the
- // input.
- EXPECT_EQ(1U, field_candidates_map.size());
- EXPECT_TRUE(field_candidates_map.find(unique_name) !=
- field_candidates_map.end());
-
- // Because we use a handcrafted field name, we can expect it to match more
- // than just one parser (at least email, but probably some more from the other
- // parsers).
- EXPECT_LT(1U, field_candidates_map.at(unique_name).field_candidates().size());
-}
-
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/form_group.cc b/chromium/components/autofill/core/browser/form_group.cc
index 1caf414bc8a..4645c24ee9e 100644
--- a/chromium/components/autofill/core/browser/form_group.cc
+++ b/chromium/components/autofill/core/browser/form_group.cc
@@ -45,6 +45,10 @@ void FormGroup::GetNonEmptyTypes(const std::string& app_locale,
}
}
+bool FormGroup::HasRawInfo(ServerFieldType type) const {
+ return !GetRawInfo(type).empty();
+}
+
base::string16 FormGroup::GetInfo(ServerFieldType type,
const std::string& app_locale) const {
return GetInfoImpl(AutofillType(type), app_locale);
diff --git a/chromium/components/autofill/core/browser/form_group.h b/chromium/components/autofill/core/browser/form_group.h
index ea00c32c24b..b4f7550051b 100644
--- a/chromium/components/autofill/core/browser/form_group.h
+++ b/chromium/components/autofill/core/browser/form_group.h
@@ -42,6 +42,10 @@ class FormGroup {
virtual void SetRawInfo(ServerFieldType type,
const base::string16& value) = 0;
+ // Returns true iff the string associated with |type| is nonempty (without
+ // canonicalizing its value).
+ bool HasRawInfo(ServerFieldType type) const;
+
// Returns the string that should be auto-filled into a text field given the
// type of that field, localized to the given |app_locale| if appropriate.
base::string16 GetInfo(ServerFieldType type,
diff --git a/chromium/components/autofill/core/browser/form_structure.cc b/chromium/components/autofill/core/browser/form_structure.cc
index 66c5757c8f8..f712a2d4983 100644
--- a/chromium/components/autofill/core/browser/form_structure.cc
+++ b/chromium/components/autofill/core/browser/form_structure.cc
@@ -43,6 +43,7 @@
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/form_field_data_predictions.h"
#include "components/autofill/core/common/signatures_util.h"
+#include "components/security_state/core/security_state.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "url/origin.h"
@@ -307,26 +308,30 @@ bool AllTypesCaptured(const FormStructure& form,
return true;
}
-// Encode password attributes |vote| into |upload|.
+// Encode password attributes and length into |upload|.
void EncodePasswordAttributesVote(
- const std::pair<PasswordAttribute, bool>& vote,
+ const std::pair<PasswordAttribute, bool>& password_attributes_vote,
+ const size_t password_length_vote,
AutofillUploadContents* upload) {
- switch (vote.first) {
+ switch (password_attributes_vote.first) {
case PasswordAttribute::kHasLowercaseLetter:
- upload->set_password_has_lowercase_letter(vote.second);
+ upload->set_password_has_lowercase_letter(
+ password_attributes_vote.second);
break;
case PasswordAttribute::kHasUppercaseLetter:
- upload->set_password_has_uppercase_letter(vote.second);
+ upload->set_password_has_uppercase_letter(
+ password_attributes_vote.second);
break;
case PasswordAttribute::kHasNumeric:
- upload->set_password_has_numeric(vote.second);
+ upload->set_password_has_numeric(password_attributes_vote.second);
break;
case PasswordAttribute::kHasSpecialSymbol:
- upload->set_password_has_special_symbol(vote.second);
+ upload->set_password_has_special_symbol(password_attributes_vote.second);
break;
case PasswordAttribute::kPasswordAttributesCount:
NOTREACHED();
}
+ upload->set_password_length(password_length_vote);
}
} // namespace
@@ -376,7 +381,8 @@ FormStructure::FormStructure(const FormData& form)
FormStructure::~FormStructure() {}
-void FormStructure::DetermineHeuristicTypes(ukm::UkmRecorder* ukm_recorder) {
+void FormStructure::DetermineHeuristicTypes(ukm::UkmRecorder* ukm_recorder,
+ ukm::SourceId source_id) {
const auto determine_heuristic_types_start_time = base::TimeTicks::Now();
// First, try to detect field types based on each field's |autocomplete|
@@ -420,8 +426,9 @@ void FormStructure::DetermineHeuristicTypes(ukm::UkmRecorder* ukm_recorder) {
if (developer_engagement_metrics) {
AutofillMetrics::LogDeveloperEngagementUkm(
- ukm_recorder, main_frame_origin().GetURL(), IsCompleteCreditCardForm(),
- GetFormTypes(), developer_engagement_metrics, form_signature());
+ ukm_recorder, source_id, main_frame_origin().GetURL(),
+ IsCompleteCreditCardForm(), GetFormTypes(),
+ developer_engagement_metrics, form_signature());
}
if (base::FeatureList::IsEnabled(kAutofillRationalizeFieldTypePredictions))
@@ -446,8 +453,10 @@ bool FormStructure::EncodeUploadRequest(
upload->set_autofill_used(form_was_autofilled);
upload->set_data_present(EncodeFieldTypes(available_field_types));
upload->set_passwords_revealed(passwords_were_revealed_);
- if (password_attributes_vote_)
- EncodePasswordAttributesVote(*password_attributes_vote_, upload);
+ if (password_attributes_vote_) {
+ EncodePasswordAttributesVote(*password_attributes_vote_,
+ password_length_vote_, upload);
+ }
if (IsAutofillFieldMetadataEnabled()) {
upload->set_action_signature(StrToHash64Bit(target_url_.host()));
@@ -502,7 +511,8 @@ bool FormStructure::EncodeQueryRequest(
// static
void FormStructure::ParseQueryResponse(
std::string payload,
- const std::vector<FormStructure*>& forms) {
+ const std::vector<FormStructure*>& forms,
+ AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) {
AutofillMetrics::LogServerQueryMetric(
AutofillMetrics::QUERY_RESPONSE_RECEIVED);
@@ -558,6 +568,9 @@ void FormStructure::ParseQueryResponse(
if (heuristic_type != field->Type().GetStorableType())
query_response_overrode_heuristics = true;
+ if (current_field->has_password_requirements())
+ field->SetPasswordRequirements(current_field->password_requirements());
+
++current_field;
}
@@ -565,6 +578,10 @@ void FormStructure::ParseQueryResponse(
!query_response_has_no_server_data);
form->UpdateAutofillCount();
+ if (base::FeatureList::IsEnabled(
+ features::kAutofillRationalizeRepeatedServerPredictions))
+ form->RationalizeRepeatedFields(form_interactions_ukm_logger);
+
if (base::FeatureList::IsEnabled(kAutofillRationalizeFieldTypePredictions))
form->RationalizeFieldTypePredictions();
@@ -788,7 +805,8 @@ void FormStructure::LogQualityMetrics(
auto* const field = this->field(i);
if (IsUPIVirtualPaymentAddress(field->value)) {
AutofillMetrics::LogUserHappinessMetric(
- AutofillMetrics::USER_DID_ENTER_UPI_VPA, field->Type().group());
+ AutofillMetrics::USER_DID_ENTER_UPI_VPA, field->Type().group(),
+ security_state::SecurityLevel::SECURITY_LEVEL_COUNT);
}
form_interactions_ukm_logger->LogFieldFillStatus(*this, *field,
@@ -868,9 +886,7 @@ void FormStructure::LogQualityMetrics(
GetFormTypes(), did_autofill_some_possible_fields, elapsed);
}
}
- if (form_interactions_ukm_logger->url() != main_frame_origin().GetURL())
- form_interactions_ukm_logger->UpdateSourceURL(
- main_frame_origin().GetURL());
+
AutofillMetrics::LogAutofillFormSubmittedState(
state, is_for_credit_card, GetFormTypes(), form_parsed_timestamp_,
form_signature(), form_interactions_ukm_logger);
@@ -1103,6 +1119,10 @@ bool FormStructure::operator!=(const FormData& form) const {
return !operator==(form);
}
+FormStructure::SectionedFieldsIndexes::SectionedFieldsIndexes() {}
+
+FormStructure::SectionedFieldsIndexes::~SectionedFieldsIndexes() {}
+
void FormStructure::RationalizeCreditCardFieldPredictions() {
bool cc_first_name_found = false;
bool cc_last_name_found = false;
@@ -1259,6 +1279,270 @@ void FormStructure::RationalizePhoneNumbersInSection(std::string section) {
phone_rationalized_[section] = true;
}
+void FormStructure::ApplyRationalizationsToFieldAndLog(
+ size_t field_index,
+ ServerFieldType new_type,
+ AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) {
+ if (field_index >= fields_.size())
+ return;
+ auto old_type = fields_[field_index]->Type().GetStorableType();
+ fields_[field_index]->SetTypeTo(AutofillType(new_type));
+ if (form_interactions_ukm_logger) {
+ form_interactions_ukm_logger->LogRepeatedServerTypePredictionRationalized(
+ form_signature_, *fields_[field_index], old_type);
+ }
+}
+
+void FormStructure::RationalizeAddressLineFields(
+ SectionedFieldsIndexes& sections_of_address_indexes,
+ AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) {
+ // The rationalization happens within sections.
+ for (sections_of_address_indexes.Reset();
+ !sections_of_address_indexes.IsFinished();
+ sections_of_address_indexes.WalkForwardToTheNextSection()) {
+ auto current_section = sections_of_address_indexes.CurrentSection();
+
+ // The rationalization only applies to sections that have 2 or 3 visible
+ // street address predictions.
+ if (current_section.size() != 2 && current_section.size() != 3) {
+ continue;
+ }
+
+ int nb_address_rationalized = 0;
+ for (auto field_index : current_section) {
+ switch (nb_address_rationalized) {
+ case 0:
+ ApplyRationalizationsToFieldAndLog(field_index, ADDRESS_HOME_LINE1,
+ form_interactions_ukm_logger);
+ break;
+ case 1:
+ ApplyRationalizationsToFieldAndLog(field_index, ADDRESS_HOME_LINE2,
+ form_interactions_ukm_logger);
+ break;
+ case 2:
+ ApplyRationalizationsToFieldAndLog(field_index, ADDRESS_HOME_LINE3,
+ form_interactions_ukm_logger);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ ++nb_address_rationalized;
+ }
+ }
+}
+
+void FormStructure::ApplyRationalizationsToHiddenSelects(
+ size_t field_index,
+ ServerFieldType new_type,
+ 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.
+ for (auto current_index = field_index + 1; current_index < fields_.size();
+ current_index++) {
+ if (fields_[current_index]->IsVisible() ||
+ fields_[current_index]->form_control_type != "select-one" ||
+ fields_[current_index]->Type().GetStorableType() != old_type)
+ break;
+ ApplyRationalizationsToFieldAndLog(current_index, new_type,
+ form_interactions_ukm_logger);
+ }
+
+ // Same for the fields coming right before the field_index. (No need to check
+ // for the fields appearing before the first field!)
+ if (field_index == 0)
+ return;
+ for (auto current_index = field_index - 1;; current_index--) {
+ if (fields_[current_index]->IsVisible() ||
+ fields_[current_index]->form_control_type != "select-one" ||
+ fields_[current_index]->Type().GetStorableType() != old_type)
+ break;
+ ApplyRationalizationsToFieldAndLog(current_index, new_type,
+ form_interactions_ukm_logger);
+ if (current_index == 0)
+ break;
+ }
+}
+
+bool FormStructure::HeuristicsPredictionsAreApplicable(
+ size_t upper_index,
+ size_t lower_index,
+ ServerFieldType first_type,
+ ServerFieldType second_type) {
+ // The predictions are applicable if one field has one of the two types, and
+ // the other has the other type.
+ if (fields_[upper_index]->heuristic_type() ==
+ fields_[lower_index]->heuristic_type())
+ return false;
+ if ((fields_[upper_index]->heuristic_type() == first_type ||
+ fields_[upper_index]->heuristic_type() == second_type) &&
+ (fields_[lower_index]->heuristic_type() == first_type ||
+ fields_[lower_index]->heuristic_type() == second_type))
+ return true;
+ return false;
+}
+
+void FormStructure::ApplyRationalizationsToFields(
+ size_t upper_index,
+ size_t lower_index,
+ 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
+ // '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.
+ ApplyRationalizationsToHiddenSelects(upper_index, upper_type,
+ form_interactions_ukm_logger);
+ ApplyRationalizationsToFieldAndLog(upper_index, upper_type,
+ form_interactions_ukm_logger);
+
+ ApplyRationalizationsToHiddenSelects(lower_index, lower_type,
+ form_interactions_ukm_logger);
+ ApplyRationalizationsToFieldAndLog(lower_index, lower_type,
+ form_interactions_ukm_logger);
+}
+
+bool FormStructure::FieldShouldBeRationalizedToCountry(size_t upper_index) {
+ // Upper field is country if and only if it's the first visible address field
+ // 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() &&
+ AutofillType(fields_[field_index]->Type().GetStorableType()).group() ==
+ ADDRESS_HOME &&
+ fields_[field_index]->section == fields_[upper_index]->section) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void FormStructure::RationalizeAddressStateCountry(
+ SectionedFieldsIndexes& sections_of_state_indexes,
+ SectionedFieldsIndexes& sections_of_country_indexes,
+ AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) {
+ // Walk on the sections of state and country indexes simultaneously. If they
+ // both point to the same section, it means that that section includes both
+ // the country and the state type. This means that no that rationalization is
+ // needed. So, walk both pointers forward. Otherwise, look at the section that
+ // appears earlier on the form. That section doesn't have any field of the
+ // other type. Rationalize the fields on the earlier section if needed. Walk
+ // the pointer that points to the earlier section forward. Stop when both
+ // sections of indexes are processed. (This resembles the merge in the merge
+ // sort.)
+ sections_of_state_indexes.Reset();
+ sections_of_country_indexes.Reset();
+
+ while (!sections_of_state_indexes.IsFinished() ||
+ !sections_of_country_indexes.IsFinished()) {
+ auto current_section_of_state_indexes =
+ sections_of_state_indexes.CurrentSection();
+ auto current_section_of_country_indexes =
+ sections_of_country_indexes.CurrentSection();
+ // If there are still sections left with both country and state type, and
+ // state and country current sections are equal, then that section has both
+ // state and country. No rationalization needed.
+ if (!sections_of_state_indexes.IsFinished() &&
+ !sections_of_country_indexes.IsFinished() &&
+ fields_[sections_of_state_indexes.CurrentIndex()]->section ==
+ fields_[sections_of_country_indexes.CurrentIndex()]->section) {
+ sections_of_state_indexes.WalkForwardToTheNextSection();
+ sections_of_country_indexes.WalkForwardToTheNextSection();
+ continue;
+ }
+
+ size_t upper_index = 0, lower_index = 0;
+
+ // If country section is before the state ones, it means that that section
+ // misses states, and the other way around.
+ if (current_section_of_state_indexes < current_section_of_country_indexes) {
+ // We only rationalize when we have exactly two visible fields of a kind.
+ if (current_section_of_state_indexes.size() == 2) {
+ upper_index = current_section_of_state_indexes[0];
+ lower_index = current_section_of_state_indexes[1];
+ }
+ sections_of_state_indexes.WalkForwardToTheNextSection();
+ } else {
+ // We only rationalize when we have exactly two visible fields of a kind.
+ if (current_section_of_country_indexes.size() == 2) {
+ upper_index = current_section_of_country_indexes[0];
+ lower_index = current_section_of_country_indexes[1];
+ }
+ sections_of_country_indexes.WalkForwardToTheNextSection();
+ }
+
+ // This is when upper and lower indexes are not changed, meaning that there
+ // is no need for rationalization.
+ if (upper_index == lower_index) {
+ continue;
+ }
+
+ if (HeuristicsPredictionsAreApplicable(upper_index, lower_index,
+ ADDRESS_HOME_STATE,
+ ADDRESS_HOME_COUNTRY)) {
+ ApplyRationalizationsToFields(
+ upper_index, lower_index, fields_[upper_index]->heuristic_type(),
+ fields_[lower_index]->heuristic_type(), form_interactions_ukm_logger);
+ continue;
+ }
+
+ if (FieldShouldBeRationalizedToCountry(upper_index)) {
+ ApplyRationalizationsToFields(upper_index, lower_index,
+ ADDRESS_HOME_COUNTRY, ADDRESS_HOME_STATE,
+ form_interactions_ukm_logger);
+ } else {
+ ApplyRationalizationsToFields(upper_index, lower_index,
+ ADDRESS_HOME_STATE, ADDRESS_HOME_COUNTRY,
+ form_interactions_ukm_logger);
+ }
+ }
+}
+
+void FormStructure::RationalizeRepeatedFields(
+ AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) {
+ // The type of every field whose index is in
+ // sectioned_field_indexes_by_type[|type|] is predicted by server as |type|.
+ // Example: sectioned_field_indexes_by_type[FULL_NAME] is a sectioned fields
+ // indexes of fields whose types are predicted as FULL_NAME by the server.
+ SectionedFieldsIndexes sectioned_field_indexes_by_type[MAX_VALID_FIELD_TYPE];
+
+ for (const auto& field : fields_) {
+ // The hidden fields are not considered when rationalizing.
+ if (!field->IsVisible())
+ continue;
+ // The billing and non-billing types are aggregated.
+ auto current_type = field->Type().GetStorableType();
+
+ if (current_type != UNKNOWN_TYPE && current_type < MAX_VALID_FIELD_TYPE) {
+ // Look at the sectioned field indexes for the current type, if the
+ // current field belongs to that section, then the field index should be
+ // added to that same section, otherwise, start a new section.
+ sectioned_field_indexes_by_type[current_type].AddFieldIndex(
+ &field - &fields_[0],
+ /*is_new_section*/ sectioned_field_indexes_by_type[current_type]
+ .Empty() ||
+ fields_[sectioned_field_indexes_by_type[current_type]
+ .LastFieldIndex()]
+ ->section != field->section);
+ }
+ }
+
+ RationalizeAddressLineFields(
+ sectioned_field_indexes_by_type[ADDRESS_HOME_STREET_ADDRESS],
+ form_interactions_ukm_logger);
+ // Since the billing types are mapped to the non-billing ones, no need to
+ // take care of ADDRESS_BILLING_STATE and .. .
+ RationalizeAddressStateCountry(
+ sectioned_field_indexes_by_type[ADDRESS_HOME_STATE],
+ sectioned_field_indexes_by_type[ADDRESS_HOME_COUNTRY],
+ form_interactions_ukm_logger);
+}
+
void FormStructure::RationalizeFieldTypePredictions() {
RationalizeCreditCardFieldPredictions();
for (const auto& field : fields_) {
@@ -1379,18 +1663,14 @@ void FormStructure::IdentifySections(bool has_author_specified_sections) {
field->section = "credit-card";
continue;
}
-
bool already_saw_current_type = seen_types.count(current_type) > 0;
-
// Forms often ask for multiple phone numbers -- e.g. both a daytime and
// evening phone number. Our phone number detection is also generally a
// little off. Hence, ignore this field type as a signal here.
if (AutofillType(current_type).group() == PHONE_HOME)
already_saw_current_type = false;
- bool ignored_field =
- !field->is_focusable ||
- field->role == FormFieldData::ROLE_ATTRIBUTE_PRESENTATION;
+ bool ignored_field = !field->IsVisible();
// This is the first visible field after a hidden section. Consider it as
// the continuation of the last visible section.
diff --git a/chromium/components/autofill/core/browser/form_structure.h b/chromium/components/autofill/core/browser/form_structure.h
index 9e3406e33de..d41617d668a 100644
--- a/chromium/components/autofill/core/browser/form_structure.h
+++ b/chromium/components/autofill/core/browser/form_structure.h
@@ -16,7 +16,6 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/optional.h"
-#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/autofill_metrics.h"
@@ -59,9 +58,10 @@ class FormStructure {
virtual ~FormStructure();
// Runs several heuristics against the form fields to determine their possible
- // types. If |ukm_recorder| is specified, logs UKM for the form structure
- // corresponding to |source_url_|.
- void DetermineHeuristicTypes(ukm::UkmRecorder* ukm_recorder);
+ // types. If |ukm_recorder| and |source_id| is specified, logs UKM for
+ // the form structure corresponding to the source mapped from the |source_id|.
+ void DetermineHeuristicTypes(ukm::UkmRecorder* ukm_recorder,
+ ukm::SourceId source_id);
// Encodes the proto |upload| request from this FormStructure.
// In some cases, a |login_form_signature| is included as part of the upload.
@@ -83,7 +83,8 @@ class FormStructure {
// Parses the field types from the server query response. |forms| must be the
// same as the one passed to EncodeQueryRequest when constructing the query.
static void ParseQueryResponse(std::string response,
- const std::vector<FormStructure*>& forms);
+ const std::vector<FormStructure*>& forms,
+ AutofillMetrics::FormInteractionsUkmLogger*);
// Returns predictions using the details from the given |form_structures| and
// their fields' predicted types.
@@ -270,6 +271,21 @@ class FormStructure {
}
#endif
+ void set_password_length_vote(const size_t noisified_password_length) {
+ DCHECK(password_attributes_vote_.has_value())
+ << "|password_length_vote_| doesn't make sense if "
+ "|password_attributes_vote_| has no value.";
+ password_length_vote_ = noisified_password_length;
+ }
+#if defined(UNIT_TEST)
+ size_t get_password_length_vote_for_testing() const {
+ DCHECK(password_attributes_vote_.has_value())
+ << "|password_length_vote_| doesn't make sense if "
+ "|password_attributes_vote_| has no value.";
+ return password_length_vote_;
+ }
+#endif
+
bool operator==(const FormData& form) const;
bool operator!=(const FormData& form) const;
@@ -286,13 +302,108 @@ class FormStructure {
FRIEND_TEST_ALL_PREFIXES(FormStructureTest, FindLongestCommonPrefix);
FRIEND_TEST_ALL_PREFIXES(FormStructureTest,
RationalizePhoneNumber_RunsOncePerSection);
+
+ class SectionedFieldsIndexes {
+ public:
+ SectionedFieldsIndexes();
+ ~SectionedFieldsIndexes();
+
+ size_t LastFieldIndex() const {
+ if (sectioned_indexes.empty())
+ return (size_t)-1; // Shouldn't happen.
+ return sectioned_indexes.back().back();
+ };
+
+ void AddFieldIndex(const size_t index, bool is_new_section) {
+ if (is_new_section || Empty()) {
+ sectioned_indexes.push_back(std::vector<size_t>(1, index));
+ return;
+ }
+ sectioned_indexes.back().push_back(index);
+ }
+
+ void WalkForwardToTheNextSection() { current_section_ptr++; }
+
+ bool IsFinished() const {
+ return current_section_ptr >= sectioned_indexes.size();
+ }
+
+ size_t CurrentIndex() const { return CurrentSection()[0]; }
+
+ std::vector<size_t> CurrentSection() const {
+ if (current_section_ptr < sectioned_indexes.size())
+ return sectioned_indexes[current_section_ptr];
+ return std::vector<size_t>(1, (size_t)-1); // To handle edge cases.
+ }
+
+ void Reset() { current_section_ptr = 0; }
+
+ bool Empty() const { return sectioned_indexes.empty(); }
+
+ private:
+ // A vector of sections. Each section is a vector of some of the indexes
+ // that belong to the same section. The sections and indexes are sorted by
+ // their order of appearance on the form.
+ std::vector<std::vector<size_t>> sectioned_indexes;
+ // Points to a vector of indexes that belong to the same section.
+ size_t current_section_ptr = 0;
+ };
+
// A function to fine tune the credit cards related predictions. For example:
// lone credit card fields in an otherwise non-credit-card related form is
// unlikely to be correct, the function will override that prediction.
void RationalizeCreditCardFieldPredictions();
+ // 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.
+ void ApplyRationalizationsToHiddenSelects(
+ size_t field_index,
+ ServerFieldType new_type,
+ AutofillMetrics::FormInteractionsUkmLogger*);
+
+ // Returns true if we can replace server predictions with the heuristics one.
+ bool HeuristicsPredictionsAreApplicable(size_t upper_index,
+ size_t lower_index,
+ ServerFieldType first_type,
+ ServerFieldType second_type);
+
+ // Applies upper type to upper field, and lower type to lower field, and
+ // applies the rationalization also to hidden select fields if necessary.
+ void ApplyRationalizationsToFields(
+ size_t upper_index,
+ size_t lower_index,
+ ServerFieldType upper_type,
+ ServerFieldType lower_type,
+ AutofillMetrics::FormInteractionsUkmLogger*);
+
+ // Returns true if the fields_[index] server type should be rationalized to
+ // ADDRESS_HOME_COUNTRY.
+ bool FieldShouldBeRationalizedToCountry(size_t index);
+
+ // Set fields_[|field_index|] to |new_type| and log this change.
+ void ApplyRationalizationsToFieldAndLog(
+ size_t field_index,
+ ServerFieldType new_type,
+ AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger);
+
+ // Two or three fields predicted as the whole address should be address lines
+ // 1, 2 and 3 instead.
+ void RationalizeAddressLineFields(
+ SectionedFieldsIndexes& sections_of_address_indexes,
+ AutofillMetrics::FormInteractionsUkmLogger*);
+
+ // Rationalize state and country interdependently.
+ void RationalizeAddressStateCountry(
+ SectionedFieldsIndexes& sections_of_state_indexes,
+ SectionedFieldsIndexes& sections_of_country_indexes,
+ AutofillMetrics::FormInteractionsUkmLogger*);
+
+ // Tunes the fields with identical predictions.
+ void RationalizeRepeatedFields(AutofillMetrics::FormInteractionsUkmLogger*);
+
// A helper function to review the predictions and do appropriate adjustments
- // when it considers neccessary.
+ // when it considers necessary.
void RationalizeFieldTypePredictions();
// Encodes information about this form and its fields into |query_form|.
@@ -408,6 +519,10 @@ class FormStructure {
// character).
base::Optional<std::pair<PasswordAttribute, bool>> password_attributes_vote_;
+ // Noisified password length for crowdsourcing. If |password_attributes_vote_|
+ // has no value, |password_length_vote_| should be ignored.
+ size_t password_length_vote_;
+
DISALLOW_COPY_AND_ASSIGN(FormStructure);
};
diff --git a/chromium/components/autofill/core/browser/form_structure_unittest.cc b/chromium/components/autofill/core/browser/form_structure_unittest.cc
index ff1b701dd36..ee8a503e718 100644
--- a/chromium/components/autofill/core/browser/form_structure_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_structure_unittest.cc
@@ -29,6 +29,7 @@ using base::ASCIIToUTF16;
using autofill::features::kAutofillEnforceMinRequiredFieldsForHeuristics;
using autofill::features::kAutofillEnforceMinRequiredFieldsForQuery;
using autofill::features::kAutofillEnforceMinRequiredFieldsForUpload;
+using autofill::features::kAutofillRationalizeRepeatedServerPredictions;
namespace autofill {
@@ -95,7 +96,8 @@ class FormStructureTest : public testing::Test {
InitFeature(&feature_list, kAutofillEnforceMinRequiredFieldsForHeuristics,
enforce_min_fields);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
return form_structure.IsAutofillable();
}
@@ -208,7 +210,8 @@ TEST_F(FormStructureTest, AutofillCount) {
// Only text and select fields that are heuristically matched are counted.
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_EQ(3U, form_structure->autofill_count());
// Add a field with should_autocomplete=false. This should not be considered a
@@ -220,7 +223,8 @@ TEST_F(FormStructureTest, AutofillCount) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_EQ(4U, form_structure->autofill_count());
}
@@ -466,7 +470,8 @@ TEST_F(FormStructureTest, DetermineHeuristicTypes_AutocompleteFalse) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->ShouldBeParsed());
EXPECT_EQ(3U, form_structure->autofill_count());
EXPECT_EQ(NAME_FULL, form_structure->field(0)->Type().GetStorableType());
@@ -520,7 +525,8 @@ TEST_F(FormStructureTest, HeuristicsContactInfo) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
@@ -577,7 +583,8 @@ TEST_F(FormStructureTest, HeuristicsAutocompleteAttribute) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
EXPECT_TRUE(form_structure->has_author_specified_types());
EXPECT_TRUE(form_structure->has_author_specified_upi_vpa_hint());
@@ -623,7 +630,8 @@ TEST_F(FormStructureTest, Heuristics_FormlessNonCheckoutForm) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
@@ -639,7 +647,8 @@ TEST_F(FormStructureTest, Heuristics_FormlessNonCheckoutForm) {
form.is_form_tag = false;
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
@@ -680,7 +689,8 @@ TEST_F(FormStructureTest, StripCommonNamePrefix) {
form.fields.push_back(field);
std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
@@ -720,7 +730,8 @@ TEST_F(FormStructureTest, StripCommonNamePrefix_SmallPrefix) {
form.fields.push_back(field);
std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
@@ -756,7 +767,8 @@ TEST_F(FormStructureTest, IsCompleteCreditCardForm_Minimal) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsCompleteCreditCardForm());
}
@@ -794,7 +806,8 @@ TEST_F(FormStructureTest, IsCompleteCreditCardForm_Full) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsCompleteCreditCardForm());
}
@@ -812,7 +825,8 @@ TEST_F(FormStructureTest, IsCompleteCreditCardForm_OnlyCCNumber) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_FALSE(form_structure->IsCompleteCreditCardForm());
}
@@ -853,7 +867,8 @@ TEST_F(FormStructureTest, IsCompleteCreditCardForm_AddressForm) {
field.name = base::string16();
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_FALSE(form_structure->IsCompleteCreditCardForm());
}
@@ -883,7 +898,8 @@ TEST_F(FormStructureTest, HeuristicsAutocompleteAttributePhoneTypes) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
@@ -923,7 +939,8 @@ TEST_F(FormStructureTest,
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
EXPECT_TRUE(form_structure->ShouldBeQueried());
EXPECT_TRUE(form_structure->ShouldBeUploaded());
@@ -962,7 +979,8 @@ TEST_F(FormStructureTest,
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
EXPECT_TRUE(form_structure->ShouldBeQueried());
EXPECT_TRUE(form_structure->ShouldBeUploaded());
@@ -1006,7 +1024,8 @@ TEST_F(FormStructureTest,
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
EXPECT_TRUE(form_structure->ShouldBeQueried());
@@ -1050,7 +1069,8 @@ TEST_F(FormStructureTest,
// Disabled.
{});
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
ASSERT_EQ(2U, form_structure.field_count());
ASSERT_EQ(0U, form_structure.autofill_count());
EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type());
@@ -1063,7 +1083,8 @@ TEST_F(FormStructureTest,
// Default configuration.
{
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
ASSERT_EQ(2U, form_structure.field_count());
ASSERT_EQ(0U, form_structure.autofill_count());
EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type());
@@ -1079,7 +1100,8 @@ TEST_F(FormStructureTest,
feature_list.InitAndDisableFeature(
kAutofillEnforceMinRequiredFieldsForHeuristics);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
ASSERT_EQ(2U, form_structure.field_count());
ASSERT_EQ(2U, form_structure.autofill_count());
EXPECT_EQ(NAME_FIRST, form_structure.field(0)->heuristic_type());
@@ -1128,7 +1150,8 @@ TEST_F(FormStructureTest,
// Disabled.
{});
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
ASSERT_EQ(2U, form_structure.field_count());
ASSERT_EQ(1U, form_structure.autofill_count());
EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type());
@@ -1144,7 +1167,8 @@ TEST_F(FormStructureTest,
feature_list.InitAndDisableFeature(
kAutofillEnforceMinRequiredFieldsForHeuristics);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
ASSERT_EQ(2U, form_structure.field_count());
ASSERT_EQ(2U, form_structure.autofill_count());
EXPECT_EQ(NAME_FIRST, form_structure.field(0)->heuristic_type());
@@ -1166,7 +1190,8 @@ TEST_F(FormStructureTest,
FormData form_copy = form;
form_copy.fields.pop_back();
FormStructure form_structure(form_copy);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
ASSERT_EQ(1U, form_structure.field_count());
ASSERT_EQ(1U, form_structure.autofill_count());
EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type());
@@ -1206,7 +1231,8 @@ TEST_F(FormStructureTest, PasswordFormShouldBeQueried) {
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_TRUE(form_structure.ShouldBeQueried());
EXPECT_TRUE(form_structure.ShouldBeUploaded());
}
@@ -1258,7 +1284,8 @@ TEST_F(FormStructureTest, HeuristicsAutocompleteAttributeWithSections) {
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
EXPECT_TRUE(form_structure.IsAutofillable());
// Expect the correct number of fields.
@@ -1303,7 +1330,8 @@ TEST_F(FormStructureTest,
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
// Expect the correct number of fields.
ASSERT_EQ(6U, form_structure.field_count());
@@ -1332,7 +1360,8 @@ TEST_F(FormStructureTest, HeuristicsAutocompleteAttributeWithSectionsRepeated) {
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
// Expect the correct number of fields.
ASSERT_EQ(2U, form_structure.field_count());
@@ -1369,7 +1398,8 @@ TEST_F(FormStructureTest, HeuristicsDontOverrideAutocompleteAttributeSections) {
form.fields.push_back(field);
FormStructure form_structure(form);
- form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
// Expect the correct number of fields.
ASSERT_EQ(4U, form_structure.field_count());
@@ -1432,7 +1462,8 @@ TEST_F(FormStructureTest, HeuristicsSample8) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(10U, form_structure->field_count());
ASSERT_EQ(9U, form_structure->autofill_count());
@@ -1498,7 +1529,8 @@ TEST_F(FormStructureTest, HeuristicsSample6) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(7U, form_structure->field_count());
ASSERT_EQ(6U, form_structure->autofill_count());
@@ -1563,7 +1595,8 @@ TEST_F(FormStructureTest, HeuristicsLabelsOnly) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(8U, form_structure->field_count());
ASSERT_EQ(7U, form_structure->autofill_count());
@@ -1620,7 +1653,8 @@ TEST_F(FormStructureTest, HeuristicsCreditCardInfo) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(6U, form_structure->field_count());
ASSERT_EQ(5U, form_structure->autofill_count());
@@ -1680,7 +1714,8 @@ TEST_F(FormStructureTest, HeuristicsCreditCardInfoWithUnknownCardField) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(7U, form_structure->field_count());
ASSERT_EQ(5U, form_structure->autofill_count());
@@ -1727,7 +1762,8 @@ TEST_F(FormStructureTest, ThreeAddressLines) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(4U, form_structure->field_count());
ASSERT_EQ(4U, form_structure->autofill_count());
@@ -1767,7 +1803,8 @@ TEST_F(FormStructureTest, SurplusAddressLinesIgnored) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
ASSERT_EQ(4U, form_structure->field_count());
ASSERT_EQ(3U, form_structure->autofill_count());
@@ -1810,7 +1847,8 @@ TEST_F(FormStructureTest, ThreeAddressLinesExpedia) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(4U, form_structure->field_count());
EXPECT_EQ(4U, form_structure->autofill_count());
@@ -1848,7 +1886,8 @@ TEST_F(FormStructureTest, TwoAddressLinesEbay) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(3U, form_structure->field_count());
ASSERT_EQ(3U, form_structure->autofill_count());
@@ -1881,7 +1920,8 @@ TEST_F(FormStructureTest, HeuristicsStateWithProvince) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(3U, form_structure->field_count());
ASSERT_EQ(3U, form_structure->autofill_count());
@@ -1947,7 +1987,8 @@ TEST_F(FormStructureTest, HeuristicsWithBilling) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(11U, form_structure->field_count());
ASSERT_EQ(11U, form_structure->autofill_count());
@@ -1996,7 +2037,8 @@ TEST_F(FormStructureTest, ThreePartPhoneNumber) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(4U, form_structure->field_count());
ASSERT_EQ(4U, form_structure->autofill_count());
@@ -2039,7 +2081,8 @@ TEST_F(FormStructureTest, HeuristicsInfernoCC) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
@@ -2093,7 +2136,8 @@ TEST_F(FormStructureTest, HeuristicsInferCCNames_NamesNotFirst) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
@@ -2151,7 +2195,8 @@ TEST_F(FormStructureTest, HeuristicsInferCCNames_NamesFirst) {
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
@@ -2334,7 +2379,8 @@ TEST_F(FormStructureTest, EncodeUploadRequest) {
std::vector<ServerFieldTypeSet> possible_field_types;
FormData form;
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
FormFieldData field;
field.form_control_type = "text";
@@ -2384,6 +2430,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest) {
form_structure.reset(new FormStructure(form));
form_structure->set_password_attributes_vote(
std::make_pair(PasswordAttribute::kHasNumeric, true));
+ form_structure->set_password_length_vote(10u);
ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
for (size_t i = 0; i < form_structure->field_count(); ++i)
@@ -2409,6 +2456,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest) {
upload.set_data_present("144200030e");
upload.set_passwords_revealed(false);
upload.set_password_has_numeric(true);
+ upload.set_password_length(10u);
upload.set_action_signature(15724779818122431245U);
test::FillUploadField(upload.add_field(), 3763331450U, "firstname", "text",
@@ -2460,6 +2508,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest) {
form_structure.reset(new FormStructure(form));
form_structure->set_password_attributes_vote(
std::make_pair(PasswordAttribute::kHasNumeric, true));
+ form_structure->set_password_length_vote(10u);
ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
for (size_t i = 0; i < form_structure->field_count(); ++i)
form_structure->field(i)->set_possible_types(possible_field_types[i]);
@@ -2517,7 +2566,8 @@ TEST_F(FormStructureTest,
std::vector<ServerFieldTypeSet> possible_field_types;
FormData form;
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
FormFieldData field;
field.label = ASCIIToUTF16("First Name");
@@ -2649,7 +2699,8 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithAutocomplete) {
std::vector<ServerFieldTypeSet> possible_field_types;
FormData form;
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
FormFieldData field;
field.form_control_type = "text";
@@ -2723,7 +2774,8 @@ TEST_F(FormStructureTest, EncodeUploadRequestWithPropertiesMask) {
std::vector<ServerFieldTypeSet> possible_field_types;
FormData form;
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
FormFieldData field;
field.form_control_type = "text";
@@ -2810,7 +2862,8 @@ TEST_F(FormStructureTest, EncodeUploadRequest_ObservedSubmissionFalse) {
std::vector<ServerFieldTypeSet> possible_field_types;
FormData form;
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
FormFieldData field;
field.form_control_type = "text";
@@ -2880,7 +2933,8 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithLabels) {
std::vector<ServerFieldTypeSet> possible_field_types;
FormData form;
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
FormFieldData field;
field.form_control_type = "text";
@@ -3019,7 +3073,8 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithFormName) {
// Setting the form name which we expect to see in the upload.
form.name = ASCIIToUTF16("myform");
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
FormFieldData field;
field.form_control_type = "text";
@@ -3082,7 +3137,8 @@ TEST_F(FormStructureTest, EncodeUploadRequestPartialMetadata) {
std::vector<ServerFieldTypeSet> possible_field_types;
FormData form;
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
FormFieldData field;
field.form_control_type = "text";
@@ -3156,7 +3212,8 @@ TEST_F(FormStructureTest, EncodeUploadRequest_DisabledMetadataTrial) {
std::vector<ServerFieldTypeSet> possible_field_types;
FormData form;
form_structure.reset(new FormStructure(form));
- form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
FormFieldData field;
field.form_control_type = "text";
@@ -4056,7 +4113,7 @@ TEST_F(FormStructureTest, ParseQueryResponse_UnknownType) {
form_data.fields.push_back(field);
FormStructure form(form_data);
- form.DetermineHeuristicTypes(nullptr /* ukm_service */);
+ form.DetermineHeuristicTypes(nullptr /* ukm_service */, 0 /* source_id */);
// Setup the query response.
AutofillQueryResponseContents response;
@@ -4068,7 +4125,7 @@ TEST_F(FormStructureTest, ParseQueryResponse_UnknownType) {
// Parse the response and update the field type predictions.
std::vector<FormStructure*> forms{&form};
- FormStructure::ParseQueryResponse(response_string, forms);
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
ASSERT_EQ(form.field_count(), 3U);
// Validate field 0.
@@ -4144,7 +4201,7 @@ TEST_F(FormStructureTest, ParseQueryResponse) {
std::string response_string;
ASSERT_TRUE(response.SerializeToString(&response_string));
- FormStructure::ParseQueryResponse(response_string, forms);
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
ASSERT_GE(forms[0]->field_count(), 2U);
ASSERT_GE(forms[1]->field_count(), 2U);
@@ -4183,7 +4240,8 @@ TEST_F(FormStructureTest, ParseQueryResponse_AuthorDefinedTypes) {
FormStructure form_structure(form);
std::vector<FormStructure*> forms;
forms.push_back(&form_structure);
- forms.front()->DetermineHeuristicTypes(nullptr /* ukm_service */);
+ forms.front()->DetermineHeuristicTypes(nullptr /* ukm_service */,
+ 0 /* source_id */);
AutofillQueryResponseContents response;
response.add_field()->set_overall_type_prediction(EMAIL_ADDRESS);
@@ -4191,7 +4249,7 @@ TEST_F(FormStructureTest, ParseQueryResponse_AuthorDefinedTypes) {
std::string response_string;
ASSERT_TRUE(response.SerializeToString(&response_string));
- FormStructure::ParseQueryResponse(response_string, forms);
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
ASSERT_GE(forms[0]->field_count(), 2U);
// Server type is parsed from the response and is the end result type.
@@ -4245,7 +4303,7 @@ TEST_F(FormStructureTest, ParseQueryResponse_RationalizeLoneField) {
feature_list.InitAndEnableFeature(
autofill::kAutofillRationalizeFieldTypePredictions);
- FormStructure::ParseQueryResponse(response_string, forms);
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
ASSERT_EQ(1U, forms.size());
ASSERT_EQ(4U, forms[0]->field_count());
EXPECT_EQ(NAME_FULL, forms[0]->field(0)->Type().GetStorableType());
@@ -4260,7 +4318,7 @@ TEST_F(FormStructureTest, ParseQueryResponse_RationalizeLoneField) {
feature_list.InitAndDisableFeature(
autofill::kAutofillRationalizeFieldTypePredictions);
- FormStructure::ParseQueryResponse(response_string, forms);
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
ASSERT_EQ(1U, forms.size());
ASSERT_EQ(4U, forms[0]->field_count());
EXPECT_EQ(NAME_FULL, forms[0]->field(0)->Type().GetStorableType());
@@ -4307,7 +4365,7 @@ TEST_F(FormStructureTest, ParseQueryResponse_RationalizeCCName) {
feature_list.InitAndEnableFeature(
autofill::kAutofillRationalizeFieldTypePredictions);
- FormStructure::ParseQueryResponse(response_string, forms);
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
ASSERT_EQ(1U, forms.size());
ASSERT_EQ(3U, forms[0]->field_count());
EXPECT_EQ(NAME_FIRST, forms[0]->field(0)->Type().GetStorableType());
@@ -4321,7 +4379,7 @@ TEST_F(FormStructureTest, ParseQueryResponse_RationalizeCCName) {
feature_list.InitAndDisableFeature(
autofill::kAutofillRationalizeFieldTypePredictions);
- FormStructure::ParseQueryResponse(response_string, forms);
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
ASSERT_EQ(1U, forms.size());
ASSERT_EQ(3U, forms[0]->field_count());
EXPECT_EQ(CREDIT_CARD_NAME_FIRST,
@@ -4380,7 +4438,7 @@ TEST_F(FormStructureTest, ParseQueryResponse_RationalizeMultiMonth_1) {
feature_list.InitAndEnableFeature(
autofill::kAutofillRationalizeFieldTypePredictions);
- FormStructure::ParseQueryResponse(response_string, forms);
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
ASSERT_EQ(1U, forms.size());
ASSERT_EQ(5U, forms[0]->field_count());
EXPECT_EQ(CREDIT_CARD_NAME_FULL,
@@ -4399,7 +4457,7 @@ TEST_F(FormStructureTest, ParseQueryResponse_RationalizeMultiMonth_1) {
feature_list.InitAndDisableFeature(
autofill::kAutofillRationalizeFieldTypePredictions);
- FormStructure::ParseQueryResponse(response_string, forms);
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
ASSERT_EQ(1U, forms.size());
ASSERT_EQ(5U, forms[0]->field_count());
EXPECT_EQ(CREDIT_CARD_NAME_FULL,
@@ -4456,7 +4514,7 @@ TEST_F(FormStructureTest, ParseQueryResponse_RationalizeMultiMonth_2) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
autofill::kAutofillRationalizeFieldTypePredictions);
- FormStructure::ParseQueryResponse(response_string, forms);
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
ASSERT_EQ(1U, forms.size());
ASSERT_EQ(4U, forms[0]->field_count());
EXPECT_EQ(CREDIT_CARD_NAME_FULL,
@@ -4472,7 +4530,7 @@ TEST_F(FormStructureTest, ParseQueryResponse_RationalizeMultiMonth_2) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(
autofill::kAutofillRationalizeFieldTypePredictions);
- FormStructure::ParseQueryResponse(response_string, forms);
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
ASSERT_EQ(1U, forms.size());
ASSERT_EQ(4U, forms[0]->field_count());
EXPECT_EQ(CREDIT_CARD_NAME_FULL,
@@ -4561,7 +4619,7 @@ TEST_F(FormStructureTest, RationalizePhoneNumber_RunsOncePerSection) {
FormStructure form_structure(form);
std::vector<FormStructure*> forms;
forms.push_back(&form_structure);
- FormStructure::ParseQueryResponse(response_string, forms);
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
EXPECT_FALSE(form_structure.phone_rationalized_["fullName_1-default"]);
form_structure.RationalizePhoneNumbersInSection("fullName_1-default");
@@ -4578,6 +4636,1171 @@ TEST_F(FormStructureTest, RationalizePhoneNumber_RunsOncePerSection) {
EXPECT_TRUE(forms[0]->field(3)->only_fill_when_focused());
}
+// Tests that a form that has only one address predicted as
+// ADDRESS_HOME_STREET_ADDRESS is not modified by the address rationalization.
+TEST_F(FormStructureTest, RationalizeRepeatedFields_OneAddress) {
+ base::test::ScopedFeatureList feature_list;
+ InitFeature(&feature_list, kAutofillRationalizeRepeatedServerPredictions,
+ true);
+
+ FormData form;
+ form.origin = GURL("http://foo.com");
+ FormFieldData field;
+ field.form_control_type = "text";
+ field.max_length = 10000;
+
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ form.fields.push_back(field);
+
+ AutofillQueryResponseContents response;
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+
+ // Will call RationalizeFieldTypePredictions
+ FormStructure::ParseQueryResponse(response_string, forms, 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_STREET_ADDRESS,
+ forms[0]->field(1)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(2)->Type().GetStorableType());
+}
+
+// Tests that a form that has two address predicted as
+// ADDRESS_HOME_STREET_ADDRESS is modified by the address rationalization to be
+// ADDRESS_HOME_LINE1 and ADDRESS_HOME_LINE2 instead.
+TEST_F(FormStructureTest, RationalizeRepreatedFields_TwoAddresses) {
+ base::test::ScopedFeatureList feature_list;
+ InitFeature(&feature_list, kAutofillRationalizeRepeatedServerPredictions,
+ true);
+
+ FormData form;
+ form.origin = GURL("http://foo.com");
+ FormFieldData field;
+ field.form_control_type = "text";
+ field.max_length = 10000;
+
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ form.fields.push_back(field);
+
+ AutofillQueryResponseContents response;
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+
+ // Will call RationalizeFieldTypePredictions
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
+
+ ASSERT_EQ(1U, forms.size());
+ ASSERT_EQ(4U, 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());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(3)->Type().GetStorableType());
+}
+
+// Tests that a form that has three address lines predicted as
+// ADDRESS_HOME_STREET_ADDRESS is modified by the address rationalization to be
+// ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2 and ADDRESS_HOME_LINE3 instead.
+TEST_F(FormStructureTest, RationalizeRepreatedFields_ThreeAddresses) {
+ base::test::ScopedFeatureList feature_list;
+ InitFeature(&feature_list, kAutofillRationalizeRepeatedServerPredictions,
+ true);
+
+ FormData form;
+ form.origin = GURL("http://foo.com");
+ FormFieldData field;
+ field.form_control_type = "text";
+ field.max_length = 10000;
+
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ form.fields.push_back(field);
+
+ AutofillQueryResponseContents response;
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+
+ // Will call RationalizeFieldTypePredictions
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
+
+ ASSERT_EQ(1U, forms.size());
+ ASSERT_EQ(5U, 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());
+ EXPECT_EQ(ADDRESS_HOME_LINE3, forms[0]->field(3)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(4)->Type().GetStorableType());
+}
+
+// Tests that a form that has four address lines predicted as
+// ADDRESS_HOME_STREET_ADDRESS is not modified by the address rationalization.
+// This doesn't happen in real world, bc four address lines mean multiple
+// sections according to the heuristics.
+TEST_F(FormStructureTest, RationalizeRepreatedFields_FourAddresses) {
+ base::test::ScopedFeatureList feature_list;
+ InitFeature(&feature_list, kAutofillRationalizeRepeatedServerPredictions,
+ true);
+ FormData form;
+ form.origin = GURL("http://foo.com");
+ FormFieldData field;
+ field.form_control_type = "text";
+ field.max_length = 10000;
+
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ form.fields.push_back(field);
+
+ AutofillQueryResponseContents response;
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+
+ // Will call RationalizeFieldTypePredictions
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
+
+ ASSERT_EQ(1U, forms.size());
+ ASSERT_EQ(6U, forms[0]->field_count());
+ EXPECT_EQ(NAME_FULL, forms[0]->field(0)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STREET_ADDRESS,
+ forms[0]->field(1)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STREET_ADDRESS,
+ forms[0]->field(2)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STREET_ADDRESS,
+ forms[0]->field(3)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STREET_ADDRESS,
+ forms[0]->field(4)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(5)->Type().GetStorableType());
+}
+
+// Tests that a form that has only one address in each section predicted as
+// ADDRESS_HOME_STREET_ADDRESS is not modified by the address rationalization.
+TEST_F(FormStructureTest, RationalizeRepreatedFields_OneAddressEachSection) {
+ base::test::ScopedFeatureList feature_list;
+ InitFeature(&feature_list, kAutofillRationalizeRepeatedServerPredictions,
+ true);
+
+ FormData form;
+ form.origin = GURL("http://foo.com");
+ FormFieldData field;
+ field.form_control_type = "text";
+ field.max_length = 10000;
+
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ field.section = "Billing";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ field.section = "Billing";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ field.section = "Billing";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ field.section = "Shipping";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ field.section = "Shipping";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ field.section = "Shipping";
+ form.fields.push_back(field);
+
+ AutofillQueryResponseContents response;
+ // Billing
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+ // Shipping
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+
+ // Will call RationalizeFieldTypePredictions
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
+ // Billing
+ ASSERT_EQ(1U, forms.size());
+ ASSERT_EQ(6U, forms[0]->field_count());
+ EXPECT_EQ(NAME_FULL, forms[0]->field(0)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STREET_ADDRESS,
+ forms[0]->field(1)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(2)->Type().GetStorableType());
+ // Shipping
+ EXPECT_EQ(NAME_FULL, forms[0]->field(3)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STREET_ADDRESS,
+ forms[0]->field(4)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(5)->Type().GetStorableType());
+}
+
+// Tests a form that has multiple sections with multiple number of address
+// fields predicted as ADDRESS_HOME_STREET_ADDRESS. The last section
+// doesn't happen in real world, bc it is in fact two sections according to
+// heuristics, and is only made for testing.
+TEST_F(
+ FormStructureTest,
+ RationalizeRepreatedFields_SectionTwoAddress_SectionThreeAddress_SectionFourAddresses) {
+ base::test::ScopedFeatureList feature_list;
+ InitFeature(&feature_list, kAutofillRationalizeRepeatedServerPredictions,
+ true);
+
+ FormData form;
+ form.origin = GURL("http://foo.com");
+ FormFieldData field;
+ field.form_control_type = "text";
+ field.max_length = 10000;
+
+ // Shipping
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ field.section = "Shipping";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ field.section = "Shipping";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ field.section = "Shipping";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ field.section = "Shipping";
+ form.fields.push_back(field);
+
+ // Billing
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ field.section = "Billing";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ field.section = "Billing";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ field.section = "Billing";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ field.section = "Billing";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ field.section = "Billing";
+ form.fields.push_back(field);
+
+ // Work address (not realistic)
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ field.section = "Work";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ field.section = "Work";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ field.section = "Work";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ field.section = "Work";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ field.section = "Work";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ field.section = "Work";
+ form.fields.push_back(field);
+
+ AutofillQueryResponseContents response;
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+
+ // Will call RationalizeFieldTypePredictions
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
+
+ ASSERT_EQ(1U, forms.size());
+ ASSERT_EQ(15U, 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());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(3)->Type().GetStorableType());
+
+ EXPECT_EQ(NAME_FULL, forms[0]->field(4)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_LINE1, forms[0]->field(5)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_LINE2, forms[0]->field(6)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_LINE3, forms[0]->field(7)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(8)->Type().GetStorableType());
+
+ EXPECT_EQ(NAME_FULL, forms[0]->field(9)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STREET_ADDRESS,
+ forms[0]->field(10)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STREET_ADDRESS,
+ forms[0]->field(11)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STREET_ADDRESS,
+ forms[0]->field(12)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STREET_ADDRESS,
+ forms[0]->field(13)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(14)->Type().GetStorableType());
+}
+
+// Tests that a form that has only one address in each section predicted as
+// ADDRESS_HOME_STREET_ADDRESS is not modified by the address rationalization,
+// while the sections are previously determined by the heuristics.
+TEST_F(FormStructureTest,
+ RationalizeRepreatedFields_MultipleSectionsByHeuristics_OneAddressEach) {
+ base::test::ScopedFeatureList feature_list;
+ InitFeature(&feature_list, kAutofillRationalizeRepeatedServerPredictions,
+ true);
+
+ FormData form;
+ form.origin = GURL("http://foo.com");
+ FormFieldData field;
+ field.form_control_type = "text";
+ field.max_length = 10000;
+
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+ // Will identify the sections based on the heuristics types.
+ form_structure.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
+
+ AutofillQueryResponseContents response;
+ // Billing
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+ // Shipping
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+
+ // Will call RationalizeFieldTypePredictions
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
+ // Billing
+ ASSERT_EQ(1U, forms.size());
+ ASSERT_EQ(6U, forms[0]->field_count());
+ EXPECT_EQ(NAME_FULL, forms[0]->field(0)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STREET_ADDRESS,
+ forms[0]->field(1)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(2)->Type().GetStorableType());
+ // Shipping
+ EXPECT_EQ(NAME_FULL, forms[0]->field(3)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STREET_ADDRESS,
+ forms[0]->field(4)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(5)->Type().GetStorableType());
+}
+
+// Tests a form that has multiple sections with multiple number of address
+// fields predicted as ADDRESS_HOME_STREET_ADDRES, while the sections are
+// identified by heuristics.
+TEST_F(
+ FormStructureTest,
+ RationalizeRepreatedFields_MultipleSectionsByHeuristics_TwoAddress_ThreeAddress) {
+ base::test::ScopedFeatureList feature_list;
+ InitFeature(&feature_list, kAutofillRationalizeRepeatedServerPredictions,
+ true);
+
+ FormData form;
+ form.origin = GURL("http://foo.com");
+ FormFieldData field;
+ field.form_control_type = "text";
+ field.max_length = 10000;
+
+ // Shipping
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ form.fields.push_back(field);
+
+ // Billing
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+ // Will identify the sections based on the heuristics types.
+ form_structure.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
+
+ AutofillQueryResponseContents response;
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(
+ ADDRESS_HOME_STREET_ADDRESS);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+ // Will call RationalizeFieldTypePredictions
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
+
+ ASSERT_EQ(1U, forms.size());
+ ASSERT_EQ(9U, 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());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(3)->Type().GetStorableType());
+
+ EXPECT_EQ(NAME_FULL, forms[0]->field(4)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_LINE1, forms[0]->field(5)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_LINE2, forms[0]->field(6)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_LINE3, forms[0]->field(7)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(8)->Type().GetStorableType());
+}
+
+TEST_F(FormStructureTest,
+ RationalizeRepreatedFields_StateCountry_NoRationalization) {
+ base::test::ScopedFeatureList feature_list;
+ InitFeature(&feature_list, kAutofillRationalizeRepeatedServerPredictions,
+ true);
+
+ FormData form;
+ form.origin = GURL("http://foo.com");
+ FormFieldData field;
+ field.form_control_type = "text";
+ field.max_length = 10000;
+ // First Section
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ form.fields.push_back(field);
+
+ // Second Section
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ form.fields.push_back(field);
+
+ // Third Section
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ form.fields.push_back(field);
+
+ // Fourth Section
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+
+ // Will identify the sections based on the heuristics types.
+ form_structure.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
+
+ AutofillQueryResponseContents response;
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_STATE);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+ // second section
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_STATE);
+ // third section
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_STATE);
+ // fourth section
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+
+ // Will call RationalizeFieldTypePredictions
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
+
+ ASSERT_EQ(1U, forms.size());
+ ASSERT_EQ(10U, forms[0]->field_count());
+ EXPECT_EQ(NAME_FULL, forms[0]->field(0)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(1)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(2)->Type().GetStorableType());
+ // second section
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(3)->Type().GetStorableType());
+ EXPECT_EQ(NAME_FULL, forms[0]->field(4)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(5)->Type().GetStorableType());
+ // third section
+ EXPECT_EQ(NAME_FULL, forms[0]->field(6)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(7)->Type().GetStorableType());
+ // fourth section
+ EXPECT_EQ(NAME_FULL, forms[0]->field(8)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(9)->Type().GetStorableType());
+}
+
+TEST_F(FormStructureTest, RationalizeRepreatedFields_CountryStateNoHeuristics) {
+ base::test::ScopedFeatureList feature_list;
+ InitFeature(&feature_list, kAutofillRationalizeRepeatedServerPredictions,
+ true);
+
+ FormData form;
+ form.origin = GURL("http://foo.com");
+ FormFieldData field;
+ field.form_control_type = "text";
+ field.max_length = 10000;
+
+ field.section = "shipping";
+
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ form.fields.push_back(field);
+
+ field.section = "billing";
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country2");
+ field.form_control_type = "select-one";
+ field.is_focusable = false; // hidden
+ form.fields.push_back(field);
+
+ field.is_focusable = true; // visible
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country2");
+ field.form_control_type = "select-one";
+ field.is_focusable = false; // hidden
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country2");
+ field.form_control_type = "select-one";
+ field.is_focusable = false; // hidden
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country2");
+ field.form_control_type = "select-one";
+ field.is_focusable = false; // hidden
+ form.fields.push_back(field);
+
+ field.is_focusable = true; // visible
+
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ form.fields.push_back(field);
+
+ field.section = "billing-2";
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+
+ AutofillQueryResponseContents response;
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_STATE);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_STATE);
+ // second section
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_STATE);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_STATE);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_STATE);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_STATE);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_STATE);
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(ADDRESS_BILLING_STATE);
+ // third section
+ response.add_field()->set_overall_type_prediction(ADDRESS_BILLING_STATE);
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(ADDRESS_BILLING_STATE);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+
+ // Will call RationalizeFieldTypePredictions
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
+
+ ASSERT_EQ(1U, forms.size());
+ ASSERT_EQ(14U, forms[0]->field_count());
+ EXPECT_EQ(NAME_FULL, forms[0]->field(0)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(1)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(2)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(3)->Type().GetStorableType());
+ // second section
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(4)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(5)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(6)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(7)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(8)->Type().GetStorableType());
+ EXPECT_EQ(NAME_FULL, forms[0]->field(9)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(10)->Type().GetStorableType());
+ // third section
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY,
+ forms[0]->field(11)->Type().GetStorableType());
+ EXPECT_EQ(NAME_FULL, forms[0]->field(12)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(13)->Type().GetStorableType());
+}
+
+TEST_F(FormStructureTest,
+ RationalizeRepreatedFields_StateCountryWithHeuristics) {
+ base::test::ScopedFeatureList feature_list;
+ InitFeature(&feature_list, kAutofillRationalizeRepeatedServerPredictions,
+ true);
+
+ FormData form;
+ form.origin = GURL("http://foo.com");
+ FormFieldData field;
+ field.form_control_type = "text";
+ field.max_length = 10000;
+ // First Section
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ field.form_control_type = "select-one";
+ field.is_focusable = false; // hidden
+ form.fields.push_back(field);
+
+ field.is_focusable = true; // visible
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country2");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("city");
+ field.name = ASCIIToUTF16("City");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state2");
+ field.form_control_type = "select-one";
+ field.role = AutofillField::ROLE_ATTRIBUTE_PRESENTATION; // hidden
+ form.fields.push_back(field);
+
+ field.role = AutofillField::ROLE_ATTRIBUTE_OTHER; // visible
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ form.fields.push_back(field);
+
+ // Second Section
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("city");
+ field.name = ASCIIToUTF16("City");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ form.fields.push_back(field);
+
+ // Third Section
+ field.label = ASCIIToUTF16("city");
+ field.name = ASCIIToUTF16("City");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state2");
+ field.form_control_type = "select-one";
+ field.role = AutofillField::ROLE_ATTRIBUTE_PRESENTATION; // hidden
+ form.fields.push_back(field);
+
+ field.role = AutofillField::ROLE_ATTRIBUTE_OTHER; // visible
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country2");
+ field.form_control_type = "select-one";
+ field.is_focusable = false; // hidden
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+
+ // Will identify the sections based on the heuristics types.
+ form_structure.DetermineHeuristicTypes(/*ukm_service=*/nullptr,
+ /*source_id=*/0);
+
+ AutofillQueryResponseContents response;
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+ // second section
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_BILLING_COUNTRY);
+ // third section
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_CITY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_BILLING_COUNTRY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_BILLING_COUNTRY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+
+ // Will call RationalizeFieldTypePredictions
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
+
+ ASSERT_EQ(1U, forms.size());
+ ASSERT_EQ(14U, forms[0]->field_count());
+ EXPECT_EQ(NAME_FULL, forms[0]->field(0)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(1)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(2)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(3)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(4)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(5)->Type().GetStorableType());
+ // second section
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(6)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(7)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(8)->Type().GetStorableType());
+ // third section
+ EXPECT_EQ(ADDRESS_HOME_CITY, forms[0]->field(9)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(10)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(11)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY,
+ forms[0]->field(12)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY,
+ forms[0]->field(13)->Type().GetStorableType());
+}
+
+TEST_F(FormStructureTest, RationalizeRepreatedFields_FirstFieldRationalized) {
+ base::test::ScopedFeatureList feature_list;
+ InitFeature(&feature_list, kAutofillRationalizeRepeatedServerPredictions,
+ true);
+
+ FormData form;
+ form.origin = GURL("http://foo.com");
+ FormFieldData field;
+ field.form_control_type = "text";
+ field.max_length = 10000;
+
+ field.section = "billing";
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country2");
+ field.form_control_type = "select-one";
+ field.is_focusable = false; // hidden
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country3");
+ field.form_control_type = "select-one";
+ field.is_focusable = false; // hidden
+ form.fields.push_back(field);
+
+ field.is_focusable = true; // visible
+
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ form.fields.push_back(field);
+
+ AutofillQueryResponseContents response;
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_STATE);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_STATE);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_STATE);
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(ADDRESS_BILLING_STATE);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+
+ // Will call RationalizeFieldTypePredictions
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
+
+ ASSERT_EQ(1U, forms.size());
+ ASSERT_EQ(5U, forms[0]->field_count());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(0)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(1)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(2)->Type().GetStorableType());
+ EXPECT_EQ(NAME_FULL, forms[0]->field(3)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(4)->Type().GetStorableType());
+}
+
+TEST_F(FormStructureTest, RationalizeRepreatedFields_LastFieldRationalized) {
+ base::test::ScopedFeatureList feature_list;
+ InitFeature(&feature_list, kAutofillRationalizeRepeatedServerPredictions,
+ true);
+
+ FormData form;
+ form.origin = GURL("http://foo.com");
+ FormFieldData field;
+ field.form_control_type = "text";
+ field.max_length = 10000;
+
+ field.section = "billing";
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country2");
+ field.form_control_type = "select-one";
+ field.is_focusable = false; // hidden
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country3");
+ field.form_control_type = "select-one";
+ field.is_focusable = false; // hidden
+ form.fields.push_back(field);
+
+ field.is_focusable = true; // visible
+
+ field.label = ASCIIToUTF16("Full Name");
+ field.name = ASCIIToUTF16("fullName");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ field.is_focusable = false; // hidden
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state2");
+ field.is_focusable = true; // visible
+ form.fields.push_back(field);
+
+ AutofillQueryResponseContents response;
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+ response.add_field()->set_overall_type_prediction(NAME_FULL);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+ response.add_field()->set_overall_type_prediction(ADDRESS_HOME_COUNTRY);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+
+ FormStructure form_structure(form);
+ std::vector<FormStructure*> forms;
+ forms.push_back(&form_structure);
+
+ // Will call RationalizeFieldTypePredictions
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
+
+ ASSERT_EQ(1U, forms.size());
+ ASSERT_EQ(6U, forms[0]->field_count());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(0)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(1)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, forms[0]->field(2)->Type().GetStorableType());
+ EXPECT_EQ(NAME_FULL, forms[0]->field(3)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(4)->Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_HOME_STATE, forms[0]->field(5)->Type().GetStorableType());
+}
+
TEST_F(FormStructureTest, AllowBigForms) {
FormData form;
form.origin = GURL("http://foo.com");
diff --git a/chromium/components/autofill/core/browser/local_card_migration_manager.cc b/chromium/components/autofill/core/browser/local_card_migration_manager.cc
new file mode 100644
index 00000000000..0040b5748c9
--- /dev/null
+++ b/chromium/components/autofill/core/browser/local_card_migration_manager.cc
@@ -0,0 +1,170 @@
+// 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/local_card_migration_manager.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "base/bind.h"
+#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/autofill_experiments.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/form_data_importer.h"
+#include "components/autofill/core/browser/payments/payments_client.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "services/identity/public/cpp/identity_manager.h"
+
+namespace autofill {
+
+LocalCardMigrationManager::LocalCardMigrationManager(
+ AutofillClient* client,
+ payments::PaymentsClient* payments_client,
+ const std::string& app_locale,
+ PersonalDataManager* personal_data_manager)
+ : client_(client),
+ payments_client_(payments_client),
+ app_locale_(app_locale),
+ personal_data_manager_(personal_data_manager) {
+ if (payments_client_)
+ payments_client_->SetSaveDelegate(this);
+}
+
+LocalCardMigrationManager::~LocalCardMigrationManager() {}
+
+bool LocalCardMigrationManager::ShouldOfferLocalCardMigration(
+ int imported_credit_card_record_type) {
+ // Must be an existing card. New cards always get Upstream or local save.
+ if (imported_credit_card_record_type !=
+ FormDataImporter::ImportedCreditCardRecordType::LOCAL_CARD &&
+ imported_credit_card_record_type !=
+ FormDataImporter::ImportedCreditCardRecordType::SERVER_CARD) {
+ return false;
+ }
+
+ if (!IsCreditCardMigrationEnabled())
+ return false;
+
+ std::vector<CreditCard*> local_credit_cards =
+ personal_data_manager_->GetLocalCreditCards();
+
+ // Empty previous state.
+ migratable_credit_cards_.clear();
+
+ // Initialize the local credit card list and queue for showing and uploading.
+ for (CreditCard* card : local_credit_cards) {
+ // If the card is valid (has a valid card number, expiration date, and is
+ // not expired) and is not a server card, add it to the list of migratable
+ // cards.
+ if (card->IsValid() && !IsServerCard(card))
+ migratable_credit_cards_.push_back(*card);
+ }
+
+ // If the form was submitted with a local card, only offer migration instead
+ // of Upstream if there are other local cards to migrate as well. If the form
+ // was submitted with a server card, offer migration if ANY local cards can be
+ // migrated.
+ return (imported_credit_card_record_type ==
+ FormDataImporter::ImportedCreditCardRecordType::LOCAL_CARD &&
+ migratable_credit_cards_.size() > 1) ||
+ (imported_credit_card_record_type ==
+ FormDataImporter::ImportedCreditCardRecordType::SERVER_CARD &&
+ !migratable_credit_cards_.empty());
+}
+
+void LocalCardMigrationManager::AttemptToOfferLocalCardMigration() {
+ // Abort the migration if |payments_client_| is nullptr.
+ if (!payments_client_)
+ return;
+ payments_client_->SetSaveDelegate(this);
+ upload_request_ = payments::PaymentsClient::UploadRequestDetails();
+
+ // Payments server determines which version of the legal message to show based
+ // on the existence of this experiment flag.
+ if (IsAutofillUpstreamUpdatePromptExplanationExperimentEnabled()) {
+ upload_request_.active_experiments.push_back(
+ kAutofillUpstreamUpdatePromptExplanation.name);
+ }
+
+ // Don't send pan_first_six, as potentially migrating multiple local cards at
+ // once will negate its usefulness.
+ payments_client_->GetUploadDetails(
+ upload_request_.profiles, GetDetectedValues(),
+ /*pan_first_six=*/std::string(), upload_request_.active_experiments,
+ app_locale_);
+}
+
+bool LocalCardMigrationManager::IsCreditCardMigrationEnabled() {
+ // Confirm that the user is signed in, syncing, and the proper experiment
+ // flags are enabled.
+ bool migration_experiment_enabled =
+ IsAutofillCreditCardLocalCardMigrationExperimentEnabled();
+ bool credit_card_upload_enabled = ::autofill::IsCreditCardUploadEnabled(
+ client_->GetPrefs(), client_->GetSyncService(),
+ client_->GetIdentityManager()->GetPrimaryAccountInfo().email);
+ bool has_google_payments_account =
+ (static_cast<int64_t>(payments_client_->GetPrefService()->GetDouble(
+ prefs::kAutofillBillingCustomerNumber)) != 0);
+ return migration_experiment_enabled && credit_card_upload_enabled &&
+ has_google_payments_account;
+}
+
+void LocalCardMigrationManager::OnDidGetUploadDetails(
+ AutofillClient::PaymentsRpcResult result,
+ const base::string16& context_token,
+ std::unique_ptr<base::DictionaryValue> legal_message) {
+ if (result == AutofillClient::SUCCESS) {
+ upload_request_.context_token = context_token;
+ legal_message_ = std::move(legal_message);
+ // If we successfully received the legal docs, trigger the offer-to-migrate
+ // dialog.
+ // TODO(crbug.com/852904): Show intermediate migration prompt here! Relies
+ // on CL/1117929 first.
+ }
+}
+
+// TODO(crbug.com/852904): Starts the upload of the next local card if one
+// exists.
+void LocalCardMigrationManager::OnDidUploadCard(
+ AutofillClient::PaymentsRpcResult result,
+ const std::string& server_id) {}
+
+int LocalCardMigrationManager::GetDetectedValues() const {
+ int detected_values = 0;
+
+ // If all cards to be migrated have a cardholder name, include it in the
+ // detected values.
+ bool all_cards_have_cardholder_name = true;
+ for (CreditCard card : migratable_credit_cards_) {
+ all_cards_have_cardholder_name &=
+ !card.GetInfo(AutofillType(CREDIT_CARD_NAME_FULL), app_locale_).empty();
+ }
+ if (all_cards_have_cardholder_name)
+ detected_values |= CreditCardSaveManager::DetectedValue::CARDHOLDER_NAME;
+
+ // Local card migration should ONLY be offered when the user already has a
+ // Google Payments account.
+ DCHECK(static_cast<int64_t>(payments_client_->GetPrefService()->GetDouble(
+ prefs::kAutofillBillingCustomerNumber)) != 0);
+ detected_values |=
+ CreditCardSaveManager::DetectedValue::HAS_GOOGLE_PAYMENTS_ACCOUNT;
+
+ return detected_values;
+}
+
+bool LocalCardMigrationManager::IsServerCard(CreditCard* local_card) const {
+ std::vector<CreditCard*> server_credit_cards =
+ personal_data_manager_->GetServerCreditCards();
+ // Check whether the current card is already uploaded.
+ for (const CreditCard* server_card : server_credit_cards) {
+ if (local_card->HasSameNumberAs(*server_card))
+ return true;
+ }
+ return false;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/local_card_migration_manager.h b/chromium/components/autofill/core/browser/local_card_migration_manager.h
new file mode 100644
index 00000000000..62802c86c47
--- /dev/null
+++ b/chromium/components/autofill/core/browser/local_card_migration_manager.h
@@ -0,0 +1,96 @@
+// 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_LOCAL_CARD_MIGRATION_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_LOCAL_CARD_MIGRATION_MANAGER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/payments/payments_client.h"
+
+namespace autofill {
+
+class CreditCard;
+class PersonalDataManager;
+
+// Manages logic for determining whether migration of locally saved credit cards
+// to Google Payments is available as well as multiple local card uploading.
+// Owned by FormDataImporter.
+class LocalCardMigrationManager : public payments::PaymentsClientSaveDelegate {
+ public:
+ // The parameters should outlive the LocalCardMigrationManager.
+ LocalCardMigrationManager(AutofillClient* client,
+ payments::PaymentsClient* payments_client,
+ const std::string& app_locale,
+ PersonalDataManager* personal_data_manager);
+ virtual ~LocalCardMigrationManager();
+
+ // Returns true if all of the conditions for allowing local credit card
+ // migration are satisfied. Initializes the local card list for upload.
+ bool ShouldOfferLocalCardMigration(int imported_credit_card_record_type);
+
+ // Called from FormDataImporter when all migration requirements are met.
+ // Fetches legal documents and triggers the OnDidGetUploadDetails callback.
+ void AttemptToOfferLocalCardMigration();
+
+ // Check that the user is signed in, syncing, and the proper experiment
+ // flags are enabled. Override in the test class.
+ virtual bool IsCreditCardMigrationEnabled();
+
+ // Determines what detected_values metadata to send (generally, cardholder
+ // name if it exists on all cards, and existence of Payments customer).
+ int GetDetectedValues() const;
+
+ protected:
+ // payments::PaymentsClientSaveDelegate:
+ // Callback after successfully getting the legal documents. On success,
+ // displays the offer-to-migrate dialog, which the user can accept or not.
+ void OnDidGetUploadDetails(
+ AutofillClient::PaymentsRpcResult result,
+ const base::string16& context_token,
+ std::unique_ptr<base::DictionaryValue> legal_message) override;
+
+ // payments::PaymentsClientSaveDelegate:
+ // Callback after a local card was uploaded. Starts the upload of the next
+ // local card if one exists.
+ // Exposed for testing.
+ void OnDidUploadCard(AutofillClient::PaymentsRpcResult result,
+ const std::string& server_id) override;
+
+ // Check whether a local card is already a server card.
+ bool IsServerCard(CreditCard* local_card) const;
+
+ AutofillClient* const client_;
+
+ // Handles Payments service requests.
+ // Owned by AutofillManager.
+ payments::PaymentsClient* payments_client_;
+
+ private:
+ std::unique_ptr<base::DictionaryValue> legal_message_;
+
+ std::string app_locale_;
+
+ // The personal data manager, used to save and load personal data to/from the
+ // web database. This is overridden by the AutofillManagerTest.
+ // Weak reference.
+ // May be NULL. NULL indicates OTR.
+ PersonalDataManager* personal_data_manager_;
+
+ // Collected information about a pending upload request.
+ payments::PaymentsClient::UploadRequestDetails upload_request_;
+
+ // The local credit cards to be uploaded.
+ std::vector<CreditCard> migratable_credit_cards_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalCardMigrationManager);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_LOCAL_CARD_MIGRATION_MANAGER_H_
diff --git a/chromium/components/autofill/core/browser/local_card_migration_manager_unittest.cc b/chromium/components/autofill/core/browser/local_card_migration_manager_unittest.cc
new file mode 100644
index 00000000000..cb6124be68f
--- /dev/null
+++ b/chromium/components/autofill/core/browser/local_card_migration_manager_unittest.cc
@@ -0,0 +1,599 @@
+// 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/local_card_migration_manager.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/guid.h"
+#include "base/metrics/metrics_hashes.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "components/autofill/core/browser/autofill_experiments.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/payments/test_payments_client.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"
+#include "components/autofill/core/browser/test_autofill_driver.h"
+#include "components/autofill/core/browser/test_autofill_manager.h"
+#include "components/autofill/core/browser/test_credit_card_save_manager.h"
+#include "components/autofill/core/browser/test_local_card_migration_manager.h"
+#include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "components/autofill/core/browser/test_sync_service.h"
+#include "components/autofill/core/browser/validation.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
+#include "components/autofill/core/common/autofill_clock.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/prefs/pref_service.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_test_util.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using base::ASCIIToUTF16;
+using testing::_;
+
+namespace autofill {
+
+class LocalCardMigrationManagerTest : public testing::Test {
+ public:
+ void SetUp() override {
+ autofill_client_.SetPrefs(test::PrefServiceForTesting());
+ personal_data_.SetPrefService(autofill_client_.GetPrefs());
+ personal_data_.SetSyncServiceForTest(&sync_service_);
+ autofill_driver_.reset(new TestAutofillDriver());
+ request_context_ = new net::TestURLRequestContextGetter(
+ base::ThreadTaskRunnerHandle::Get());
+ autofill_driver_->SetURLRequestContext(request_context_.get());
+ payments_client_ = new payments::TestPaymentsClient(
+ autofill_driver_->GetURLLoaderFactory(), autofill_client_.GetPrefs(),
+ autofill_client_.GetIdentityManager(),
+ /*unmask_delegate=*/nullptr,
+ /*save_delegate=*/nullptr);
+ credit_card_save_manager_ =
+ new TestCreditCardSaveManager(autofill_driver_.get(), &autofill_client_,
+ payments_client_, &personal_data_);
+ local_card_migration_manager_ = new TestLocalCardMigrationManager(
+ autofill_driver_.get(), &autofill_client_, payments_client_,
+ &personal_data_);
+ autofill_manager_.reset(new TestAutofillManager(
+ autofill_driver_.get(), &autofill_client_, &personal_data_,
+ std::unique_ptr<CreditCardSaveManager>(credit_card_save_manager_),
+ payments_client_,
+ std::unique_ptr<LocalCardMigrationManager>(
+ local_card_migration_manager_)));
+ autofill_manager_->SetExpectedObservedSubmission(true);
+ }
+
+ void TearDown() override {
+ // Order of destruction is important as AutofillManager relies on
+ // PersonalDataManager to be around when it gets destroyed.
+ autofill_manager_.reset();
+ autofill_driver_.reset();
+
+ personal_data_.SetPrefService(nullptr);
+ personal_data_.ClearCreditCards();
+
+ request_context_ = nullptr;
+ }
+
+ void EnableAutofillCreditCardLocalCardMigrationExperiment() {
+ scoped_feature_list_.InitAndEnableFeature(
+ kAutofillCreditCardLocalCardMigration);
+ }
+
+ void DisableAutofillCreditCardLocalCardMigrationExperiment() {
+ scoped_feature_list_.InitAndDisableFeature(
+ kAutofillCreditCardLocalCardMigration);
+ }
+
+ void FormsSeen(const std::vector<FormData>& forms) {
+ autofill_manager_->OnFormsSeen(forms, base::TimeTicks());
+ }
+
+ void FormSubmitted(const FormData& form) {
+ autofill_manager_->OnFormSubmitted(
+ form, false, SubmissionSource::FORM_SUBMISSION, base::TimeTicks::Now());
+ }
+
+ void EditCreditCardFrom(FormData& credit_card_form,
+ const char* name_on_card,
+ const char* card_number,
+ const char* expiration_month,
+ const char* expiration_year,
+ const char* cvc) {
+ DCHECK(credit_card_form.fields.size() >= 5);
+ credit_card_form.fields[0].value = ASCIIToUTF16(name_on_card);
+ credit_card_form.fields[1].value = ASCIIToUTF16(card_number);
+ credit_card_form.fields[2].value = ASCIIToUTF16(expiration_month);
+ credit_card_form.fields[3].value = ASCIIToUTF16(expiration_year);
+ credit_card_form.fields[4].value = ASCIIToUTF16(cvc);
+ }
+
+ void AddLocalCrediCard(TestPersonalDataManager& personal_data,
+ const char* name_on_card,
+ const char* card_number,
+ const char* expiration_month,
+ const char* expiration_year,
+ const std::string& billing_address_id) {
+ CreditCard local_card;
+ test::SetCreditCardInfo(&local_card, name_on_card, card_number,
+ expiration_month, expiration_year,
+ billing_address_id);
+ local_card.set_record_type(CreditCard::LOCAL_CARD);
+ personal_data.AddCreditCard(local_card);
+ }
+
+ protected:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
+ TestAutofillClient autofill_client_;
+ std::unique_ptr<TestAutofillDriver> autofill_driver_;
+ std::unique_ptr<TestAutofillManager> autofill_manager_;
+ scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+ TestPersonalDataManager personal_data_;
+ TestSyncService sync_service_;
+ base::test::ScopedFeatureList scoped_feature_list_;
+ // Ends up getting owned (and destroyed) by TestFormDataImporter:
+ TestCreditCardSaveManager* credit_card_save_manager_;
+ TestLocalCardMigrationManager* local_card_migration_manager_;
+ // Ends up getting owned (and destroyed) by TestAutofillManager:
+ payments::TestPaymentsClient* payments_client_;
+};
+
+// Having one local card on file and using it will not trigger migration.
+TEST_F(LocalCardMigrationManagerTest,
+ MigrateCreditCard_UseLocalCardWithOneLocal) {
+ EnableAutofillCreditCardLocalCardMigrationExperiment();
+ personal_data_.ClearCreditCards();
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ // Set the billing_customer_number Priority Preference to designate
+ // existence of a Payments account.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
+ // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+ // enter below.
+ AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "1");
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "123");
+ FormSubmitted(credit_card_form);
+ EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Having any number of local cards on file and using a new card will not
+// trigger migration.
+TEST_F(LocalCardMigrationManagerTest,
+ MigrateCreditCard_UseNewCardWithAnyLocal) {
+ EnableAutofillCreditCardLocalCardMigrationExperiment();
+ personal_data_.ClearCreditCards();
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ // Set the billing_customer_number Priority Preference to designate
+ // existence of a Payments account.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
+ // Add a local credit card (but it will not match what we will enter below).
+ AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "1");
+ // Add another local credit card (but it will not match what we will enter
+ // below).
+ AddLocalCrediCard(personal_data_, "Flo Master", "4444333322221111", "11",
+ test::NextYear().c_str(), "1");
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ EditCreditCardFrom(credit_card_form, "Flo Master", "5555555555554444", "11",
+ test::NextYear().c_str(), "123");
+ FormSubmitted(credit_card_form);
+ EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Use one local card with more valid local cards available, will trigger
+// migration.
+TEST_F(LocalCardMigrationManagerTest,
+ MigrateCreditCard_UseLocalCardWithMoreLocal) {
+ EnableAutofillCreditCardLocalCardMigrationExperiment();
+ personal_data_.ClearCreditCards();
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ // Set the billing_customer_number Priority Preference to designate
+ // existence of a Payments account.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
+ // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+ // enter below.
+ AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "1");
+ // Add another local credit card, so it will trigger migration.
+ AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ test::NextYear().c_str(), "1");
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "123");
+ FormSubmitted(credit_card_form);
+ EXPECT_TRUE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Using a local card will not trigger migration even if there are other local
+// cards as long as the other local cards are not eligible for migration.
+TEST_F(LocalCardMigrationManagerTest,
+ MigrateCreditCard_UseLocalCardWithInvalidLocal) {
+ EnableAutofillCreditCardLocalCardMigrationExperiment();
+ personal_data_.ClearCreditCards();
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ // Set the billing_customer_number Priority Preference to designate
+ // existence of a Payments account.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
+ // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+ // enter below.
+ AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "1");
+ // Add other invalid local credit cards(invalid card number or expired), so it
+ // will not trigger migration.
+ AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111112", "11",
+ test::NextYear().c_str(), "1");
+ AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ test::LastYear().c_str(), "1");
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "123");
+ FormSubmitted(credit_card_form);
+ EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Using a server card when any number of local cards are eligible for migration
+// will trigger migration.
+TEST_F(LocalCardMigrationManagerTest,
+ MigrateCreditCard_UseServerCardWithOneValidLocal) {
+ EnableAutofillCreditCardLocalCardMigrationExperiment();
+ personal_data_.ClearCreditCards();
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ // Set the billing_customer_number Priority Preference to designate
+ // existence of a Payments account.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
+ // Add a masked server credit card whose |TypeAndLastFourDigits| matches what
+ // we will enter below.
+ CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123");
+ test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11",
+ test::NextYear().c_str(), "1");
+ credit_card.SetNetworkForMaskedCard(kVisaCard);
+ personal_data_.AddServerCreditCard(credit_card);
+ // Add one valid local credit card, so it will trigger migration
+ AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ test::NextYear().c_str(), "1");
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "123");
+ FormSubmitted(credit_card_form);
+ EXPECT_TRUE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Using a server card will not trigger migration even if there are other local
+// cards as long as the other local cards are not eligible for migration.
+TEST_F(LocalCardMigrationManagerTest,
+ MigrateCreditCard_UseServerCardWithNoneValidLocal) {
+ EnableAutofillCreditCardLocalCardMigrationExperiment();
+ personal_data_.ClearCreditCards();
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ // Set the billing_customer_number Priority Preference to designate
+ // existence of a Payments account.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
+ // Add a masked credit card whose |TypeAndLastFourDigits| matches what we will
+ // enter below.
+ CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123");
+ test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11",
+ test::NextYear().c_str(), "1");
+ credit_card.SetNetworkForMaskedCard(kVisaCard);
+ personal_data_.AddServerCreditCard(credit_card);
+ // Add other invalid local credit cards(invalid card number or expired), so it
+ // will not trigger migration.
+ AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111112", "11",
+ test::NextYear().c_str(), "1");
+ AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ test::LastYear().c_str(), "1");
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "123");
+ FormSubmitted(credit_card_form);
+ EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Use one local card with more valid local cards available but experiment flag
+// is off, will not trigger migration.
+TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_FeatureNotEnabled) {
+ // Turn off the experiment flag.
+ DisableAutofillCreditCardLocalCardMigrationExperiment();
+ personal_data_.ClearCreditCards();
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ // Set the billing_customer_number Priority Preference to designate
+ // existence of a Payments account.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
+ // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+ // enter below.
+ AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "1");
+ // Add another local credit card.
+ AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ test::NextYear().c_str(), "1");
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "123");
+ FormSubmitted(credit_card_form);
+ EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Use one local card with more valid local cards available but billing customer
+// number is blank, will not trigger migration.
+TEST_F(LocalCardMigrationManagerTest, MigrateCreditCard_NoPaymentsAccount) {
+ EnableAutofillCreditCardLocalCardMigrationExperiment();
+ personal_data_.ClearCreditCards();
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+ // enter below.
+ AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "1");
+ // Add another local credit card.
+ AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ test::NextYear().c_str(), "1");
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "123");
+ FormSubmitted(credit_card_form);
+ EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Tests that local cards that match masked server cards do not count as
+// migratable.
+TEST_F(LocalCardMigrationManagerTest,
+ MigrateCreditCard_LocalCardMatchMaskedServerCard) {
+ EnableAutofillCreditCardLocalCardMigrationExperiment();
+ personal_data_.ClearCreditCards();
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ // Set the billing_customer_number Priority Preference to designate
+ // existence of a Payments account.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
+ // Add a masked server card whose |TypeAndLastFourDigits| matches a local
+ // card.
+ CreditCard server_card(CreditCard::MASKED_SERVER_CARD, "a123");
+ test::SetCreditCardInfo(&server_card, "Flo Master", "1111", "11",
+ test::NextYear().c_str(), "1");
+ server_card.SetNetworkForMaskedCard(kVisaCard);
+ personal_data_.AddServerCreditCard(server_card);
+ // Add a local card whose |TypeAndLastFourDigits| matches a masked server
+ // card.
+ AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "1");
+ // Add another local credit card
+ AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ test::NextYear().c_str(), "1");
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ EditCreditCardFrom(credit_card_form, "Flo Master", "5555555555554444", "11",
+ test::NextYear().c_str(), "123");
+ FormSubmitted(credit_card_form);
+ EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// Tests that local cards that match full server cards do not count as
+// migratable.
+TEST_F(LocalCardMigrationManagerTest,
+ MigrateCreditCard_LocalCardMatchFullServerCard) {
+ EnableAutofillCreditCardLocalCardMigrationExperiment();
+ personal_data_.ClearCreditCards();
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ // Set the billing_customer_number Priority Preference to designate
+ // existence of a Payments account.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
+ // 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);
+ // Add a local credit card whose number matches a full server card.
+ AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "1");
+ // Add another local credit card
+ AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ test::NextYear().c_str(), "1");
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ EditCreditCardFrom(credit_card_form, "Flo Master", "5555555555554444", "11",
+ test::NextYear().c_str(), "123");
+ FormSubmitted(credit_card_form);
+ EXPECT_FALSE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+}
+
+// GetDetectedValues() should includes cardholder name if all cards have it.
+TEST_F(LocalCardMigrationManagerTest, GetDetectedValues_AllWithCardHolderName) {
+ EnableAutofillCreditCardLocalCardMigrationExperiment();
+ personal_data_.ClearCreditCards();
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ // Set the billing_customer_number Priority Preference to designate
+ // existence of a Payments account.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
+ // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+ // enter below.
+ AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "1");
+ // Add another local credit card with a different cardholder name.
+ AddLocalCrediCard(personal_data_, "John Smith", "5555555555554444", "11",
+ test::NextYear().c_str(), "1");
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "123");
+ FormSubmitted(credit_card_form);
+ EXPECT_TRUE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+ EXPECT_TRUE(local_card_migration_manager_->GetDetectedValues() &
+ CreditCardSaveManager::DetectedValue::CARDHOLDER_NAME);
+}
+
+// GetDetectedValues() should not include cardholder name if not all cards have
+// a cardholder name.
+TEST_F(LocalCardMigrationManagerTest,
+ GetDetectedValues_OneCardWithoutCardHolderName) {
+ EnableAutofillCreditCardLocalCardMigrationExperiment();
+ personal_data_.ClearCreditCards();
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ // Set the billing_customer_number Priority Preference to designate
+ // existence of a Payments account.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
+ // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+ // enter below.
+ AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "1");
+ // Add another local credit card without card holder name.
+ AddLocalCrediCard(personal_data_, "", "5555555555554444", "11",
+ test::NextYear().c_str(), "1");
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "123");
+ FormSubmitted(credit_card_form);
+ EXPECT_TRUE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+ EXPECT_FALSE(local_card_migration_manager_->GetDetectedValues() &
+ CreditCardSaveManager::DetectedValue::CARDHOLDER_NAME);
+}
+
+// GetDetectedValues() should include the existence of a Google Payments
+// account.
+TEST_F(LocalCardMigrationManagerTest,
+ GetDetectedValues_IncludeGooglePaymentsAccount) {
+ EnableAutofillCreditCardLocalCardMigrationExperiment();
+ personal_data_.ClearCreditCards();
+ personal_data_.ClearProfiles();
+ credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+ // Set the billing_customer_number Priority Preference to designate
+ // existence of a Payments account.
+ autofill_client_.GetPrefs()->SetDouble(prefs::kAutofillBillingCustomerNumber,
+ 12345);
+ // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+ // enter below.
+ AddLocalCrediCard(personal_data_, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "1");
+ // Add another local credit card
+ AddLocalCrediCard(personal_data_, "Flo Master", "5555555555554444", "11",
+ test::NextYear().c_str(), "1");
+
+ // Set up our credit card form data.
+ FormData credit_card_form;
+ test::CreateTestCreditCardFormData(&credit_card_form, true, false);
+ FormsSeen(std::vector<FormData>(1, credit_card_form));
+
+ // Edit the data, and submit.
+ EditCreditCardFrom(credit_card_form, "Flo Master", "4111111111111111", "11",
+ test::NextYear().c_str(), "123");
+ FormSubmitted(credit_card_form);
+ EXPECT_TRUE(local_card_migration_manager_->LocalCardMigrationWasTriggered());
+ EXPECT_TRUE(
+ local_card_migration_manager_->GetDetectedValues() &
+ CreditCardSaveManager::DetectedValue::HAS_GOOGLE_PAYMENTS_ACCOUNT);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/password_generator.cc b/chromium/components/autofill/core/browser/password_generator.cc
index f456ec48b97..fbca4726f87 100644
--- a/chromium/components/autofill/core/browser/password_generator.cc
+++ b/chromium/components/autofill/core/browser/password_generator.cc
@@ -1,99 +1,218 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/password_generator.h"
-#include <stddef.h>
-
#include <algorithm>
+#include <limits>
+#include <map>
#include <vector>
+#include "base/logging.h"
#include "base/rand_util.h"
-#include "base/strings/string_util.h"
-#include "third_party/fips181/fips181.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/proto/password_requirements.pb.h"
-namespace {
+namespace autofill {
-const int kMinUpper = 65; // First upper case letter 'A'
-const int kMaxUpper = 90; // Last upper case letter 'Z'
-const int kMinLower = 97; // First lower case letter 'a'
-const int kMaxLower = 122; // Last lower case letter 'z'
-const int kMinDigit = 48; // First digit '0'
-const int kMaxDigit = 57; // Last digit '9'
-const int kMinPasswordLength = 4;
-const int kMaxPasswordLength = 15;
-
-// A helper function to get the length of the generated password from
-// |max_length| retrieved from input password field.
-int GetLengthFromHint(int max_length, int default_length) {
- if (max_length >= kMinPasswordLength && max_length <= kMaxPasswordLength)
- return max_length;
- return default_length;
-}
+const uint32_t kDefaultPasswordLength = 15;
-// We want the password to have uppercase, lowercase, and at least one number.
-bool VerifyPassword(const std::string& password) {
- int num_lower_case = 0;
- int num_upper_case = 0;
- int num_digits = 0;
-
- for (size_t i = 0; i < password.size(); ++i) {
- if (password[i] >= kMinUpper && password[i] <= kMaxUpper)
- ++num_upper_case;
- if (password[i] >= kMinLower && password[i] <= kMaxLower)
- ++num_lower_case;
- if (password[i] >= kMinDigit && password[i] <= kMaxDigit)
- ++num_digits;
- }
+namespace {
- return num_lower_case && num_upper_case && num_digits;
+// Default character sets used if the spec does not override the character set.
+// Removed characters due to visual similarity:
+// - l (lowercase L)
+// - I (capital i)
+// - 1 (one)
+// - O (capital o)
+// - 0 (zero)
+// - o (lowercase O)
+constexpr char kLowerCaseChars[] = "abcdefghijkmnpqrstuvwxyz";
+constexpr char kUpperCaseChars[] = "ABCDEFGHJKLMNPQRSTUVWXYZ";
+constexpr char kAlphabeticChars[] =
+ "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
+constexpr char kDigits[] = "23456789";
+constexpr char kSymbols[] = "-_.:!";
+
+// Returns a default password requirements specification that requires:
+// - at least one lower case letter
+// - at least one upper case letter
+// - at least one number
+// - no symbols
+PasswordRequirementsSpec BuildDefaultSpec() {
+ // Note the the fields below should be initialized in the order of their
+ // proto field numbers to reduce the risk of forgetting a field.
+ PasswordRequirementsSpec spec;
+ spec.set_priority(0);
+ spec.set_spec_version(1);
+ // spec.min_length and spec.max_length remain unset to fall back to
+ // PasswordGenerator::kDefaultPasswordLength.
+
+ spec.mutable_lower_case()->set_character_set(kLowerCaseChars);
+ spec.mutable_lower_case()->set_min(1);
+ spec.mutable_lower_case()->set_max(std::numeric_limits<int32_t>::max());
+
+ spec.mutable_upper_case()->set_character_set(kUpperCaseChars);
+ spec.mutable_upper_case()->set_min(1);
+ spec.mutable_upper_case()->set_max(std::numeric_limits<int32_t>::max());
+
+ spec.mutable_alphabetic()->set_character_set(kAlphabeticChars);
+ spec.mutable_alphabetic()->set_min(0);
+ spec.mutable_alphabetic()->set_max(0);
+
+ spec.mutable_numeric()->set_character_set(kDigits);
+ spec.mutable_numeric()->set_min(1);
+ spec.mutable_numeric()->set_max(std::numeric_limits<int32_t>::max());
+
+ spec.mutable_symbols()->set_character_set(kSymbols);
+ spec.mutable_symbols()->set_min(0);
+ spec.mutable_symbols()->set_max(0);
+ return spec;
}
-} // namespace
-
-namespace autofill {
+// Returns whether the password is difficult to read because it contains
+// sequences of '-' or '_' that are joined into long strokes on the screen
+// in many fonts.
+bool IsDifficultToRead(const base::string16& password) {
+ return std::adjacent_find(password.begin(), password.end(),
+ [](auto a, auto b) {
+ return a == b && (a == '-' || a == '_');
+ }) != password.end();
+}
-const int PasswordGenerator::kDefaultPasswordLength = 15;
+// Generates a password according to |spec| and tries to maximze the entropy
+// while not caring for pronounceable passwords.
+//
+// |spec| must contain values for at least all fields that are defined
+// in the spec of BuildDefaultSpec().
+base::string16 GenerateMaxEntropyPassword(PasswordRequirementsSpec spec) {
+ using CharacterClass = PasswordRequirementsSpec_CharacterClass;
+
+ // Determine target length.
+ uint32_t target_length = kDefaultPasswordLength;
+ if (spec.has_min_length())
+ target_length = std::max(target_length, spec.min_length());
+ if (spec.has_max_length())
+ target_length = std::min(target_length, spec.max_length());
+ // Avoid excessively long passwords.
+ target_length = std::min(target_length, 200u);
+
+ // The password that is being generated in this function.
+ base::string16 password;
+ password.reserve(target_length);
+
+ // A list of CharacterClasses that have not been fully used.
+ std::vector<CharacterClass*> classes;
+ // The list of allowed characters in a specific class. This map exists
+ // to calculate the string16 conversion only once.
+ std::map<CharacterClass*, base::string16> characters_of_class;
+
+ // These are guaranteed to exist because |spec| is an overlay of the default
+ // spec.
+ DCHECK(spec.has_lower_case());
+ DCHECK(spec.has_upper_case());
+ DCHECK(spec.has_alphabetic());
+ DCHECK(spec.has_numeric());
+ DCHECK(spec.has_symbols());
+
+ // Initialize |classes| and |characters_of_class| and sanitize |spec|
+ // if necessary.
+ for (CharacterClass* character_class :
+ {spec.mutable_lower_case(), spec.mutable_upper_case(),
+ spec.mutable_alphabetic(), spec.mutable_numeric(),
+ spec.mutable_symbols()}) {
+ DCHECK(character_class->has_character_set());
+ DCHECK(character_class->has_min());
+ DCHECK(character_class->has_max());
+
+ // If the character set is empty, we cannot generate characters from it.
+ if (character_class->character_set().empty())
+ character_class->set_max(0);
+
+ // The the maximum is smaller than the minimum, limit the minimum.
+ if (character_class->max() < character_class->min())
+ character_class->set_min(character_class->max());
+
+ if (character_class->max() > 0) {
+ classes.push_back(character_class);
+ characters_of_class[character_class] =
+ base::UTF8ToUTF16(character_class->character_set());
+ }
+ }
-void ForceFixPassword(std::string* password) {
- for (char& it : *password) {
- if (islower(it)) {
- it = base::ToUpperASCII(it);
- break;
+ // Generate a password that contains the minimum number of characters of the
+ // various character classes as per requirements. This stops when the target
+ // length is achieved. Note that this is just a graceful handling of a buggy
+ // spec. It should not happen that more characters are needed than can
+ // accommodated.
+ for (CharacterClass* character_class : classes) {
+ while (character_class->min() > 0 && password.length() < target_length) {
+ const base::string16& possible_chars =
+ characters_of_class[character_class];
+ password += possible_chars[base::RandGenerator(possible_chars.length())];
+ character_class->set_min(character_class->min() - 1);
+ character_class->set_max(character_class->max() - 1);
}
}
- for (std::string::reverse_iterator iter = password->rbegin();
- iter != password->rend(); ++iter) {
- if (islower(*iter)) {
- *iter = base::RandInt(kMinDigit, kMaxDigit);
+
+ // Now fill the rest of the password with random characters.
+ while (password.length() < target_length) {
+ // Determine how many different characters are in all remaining character
+ // classes.
+ size_t number_of_possible_chars = 0;
+ for (CharacterClass* character_class : classes) {
+ if (character_class->max() > 0) {
+ number_of_possible_chars +=
+ characters_of_class[character_class].length();
+ }
+ }
+ if (number_of_possible_chars == 0)
break;
+ uint64_t choice = base::RandGenerator(number_of_possible_chars);
+ // Now figure out which character was chosen and append it.
+ for (CharacterClass* character_class : classes) {
+ if (character_class->max() > 0) {
+ size_t size_of_class = characters_of_class[character_class].length();
+ if (choice < size_of_class) {
+ password += characters_of_class[character_class][choice];
+ character_class->set_max(character_class->max() - 1);
+ break;
+ } else {
+ choice -= size_of_class;
+ }
+ }
}
}
+
+ // So far the password contains the minimally required characters at the
+ // the beginning. Therefore, we create a random permutation.
+ // TODO(crbug.com/847200): Once the unittests allow controlling the generated
+ // string, test that '--' and '__' are eliminated.
+ int remaining_attempts = 5;
+ do {
+ base::RandomShuffle(password.begin(), password.end());
+ } while (IsDifficultToRead(password) && remaining_attempts-- > 0);
+
+ return password;
}
-PasswordGenerator::PasswordGenerator(int max_length)
- : password_length_(GetLengthFromHint(max_length, kDefaultPasswordLength)) {}
-PasswordGenerator::~PasswordGenerator() {}
+} // namespace
+
+base::string16 GeneratePassword(const PasswordRequirementsSpec& spec) {
+ PasswordRequirementsSpec actual_spec = BuildDefaultSpec();
-std::string PasswordGenerator::Generate() const {
- char password[255];
- char unused_hypenated_password[255];
- // Generate passwords that have numbers and upper and lower case letters.
- // No special characters included for now.
- unsigned int mode = S_NB | S_CL | S_SL;
+ // Override all fields that are set in |spec|. Character classes are merged
+ // recursively.
+ actual_spec.MergeFrom(spec);
- // Generate the password by gen_pron_pass(), if it is not conforming then
- // force fix it.
- gen_pron_pass(password, unused_hypenated_password, password_length_,
- password_length_, mode);
+ base::string16 password = GenerateMaxEntropyPassword(std::move(actual_spec));
- std::string str_password(password);
- if (VerifyPassword(str_password))
- return str_password;
+ // Catch cases where supplied spec is infeasible.
+ if (password.empty())
+ password = GenerateMaxEntropyPassword(BuildDefaultSpec());
- ForceFixPassword(&str_password);
- return str_password;
+ return password;
}
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/password_generator.h b/chromium/components/autofill/core/browser/password_generator.h
index e24058f66ba..4ff870f942d 100644
--- a/chromium/components/autofill/core/browser/password_generator.h
+++ b/chromium/components/autofill/core/browser/password_generator.h
@@ -1,52 +1,29 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_PASSWORD_GENERATOR_H_
#define COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_GENERATOR_H_
-#include <string>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
+#include "base/strings/string16.h"
namespace autofill {
-// Make sure that there is at least one upper case and one number in the
-// password. |password| must not be null, and must point to a string containing
-// at least 3 lower-case letters.
-extern void ForceFixPassword(std::string* password);
-
-// Class to generate random passwords. Currently we just use a generic algorithm
-// for all sites, but eventually we can incorporate additional information to
-// determine passwords that are likely to be accepted (i.e. use pattern field,
-// previous generated passwords, crowdsourcing, etc.)
-class PasswordGenerator {
- public:
- // |max_length| is used as a hint for the generated password's length.
- explicit PasswordGenerator(int max_length);
- ~PasswordGenerator();
-
- // Returns a random password such that:
- // (1) Each character is guaranteed to be a non-whitespace printable ASCII
- // character.
- // (2) The generated password will contain AT LEAST one upper case letter, one
- // lower case letter, and one digit.
- // (3) The password length will be equal to |password_length_| (see comment
- // for the constructor).
- // Not thread safe.
- std::string Generate() const;
-
- private:
- // Unit test also need to access |kDefaultPasswordLength|.
- static const int kDefaultPasswordLength;
- FRIEND_TEST_ALL_PREFIXES(PasswordGeneratorTest, PasswordLength);
-
- // The length of the generated password.
- const int password_length_;
-
- DISALLOW_COPY_AND_ASSIGN(PasswordGenerator);
-};
+class PasswordRequirementsSpec;
+
+extern const uint32_t kDefaultPasswordLength;
+
+// Returns a password that follows the |spec| as well as possible. If this is
+// impossible, a password that nearly meets the requirements can be returned.
+// In this case the user is asked to fix the password themselves.
+//
+// If |spec| is empty, a password of length |kDefaultPasswordLength| is
+// generated that contains
+// - at least 1 lower case latin character
+// - at least 1 upper case latin character
+// - at least 1 number (digit)
+// - no symbols
+base::string16 GeneratePassword(const PasswordRequirementsSpec& spec);
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/password_generator_fips181.cc b/chromium/components/autofill/core/browser/password_generator_fips181.cc
new file mode 100644
index 00000000000..3226576ea9b
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_generator_fips181.cc
@@ -0,0 +1,124 @@
+// 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/autofill/core/browser/password_generator_fips181.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "base/rand_util.h"
+#include "base/strings/string_util.h"
+#include "third_party/fips181/fips181.h"
+
+namespace {
+
+const int kMinUpper = 65; // First upper case letter 'A'
+const int kMaxUpper = 90; // Last upper case letter 'Z'
+const int kMinLower = 97; // First lower case letter 'a'
+const int kMaxLower = 122; // Last lower case letter 'z'
+const int kMinDigit = 48; // First digit '0'
+const int kMaxDigit = 57; // Last digit '9'
+const int kMinPasswordLength = 4;
+const int kMaxPasswordLength = 15;
+
+// A helper function to get the length of the generated password from
+// |max_length| retrieved from input password field.
+int GetLengthFromHint(int max_length, int default_length) {
+ if (max_length >= kMinPasswordLength && max_length <= kMaxPasswordLength)
+ return max_length;
+ return default_length;
+}
+
+// We want the password to have uppercase, lowercase, and at least one number.
+bool VerifyPassword(const std::string& password) {
+ int num_lower_case = 0;
+ int num_upper_case = 0;
+ int num_digits = 0;
+
+ for (size_t i = 0; i < password.size(); ++i) {
+ if (password[i] >= kMinUpper && password[i] <= kMaxUpper)
+ ++num_upper_case;
+ if (password[i] >= kMinLower && password[i] <= kMaxLower)
+ ++num_lower_case;
+ if (password[i] >= kMinDigit && password[i] <= kMaxDigit)
+ ++num_digits;
+ }
+
+ return num_lower_case && num_upper_case && num_digits;
+}
+
+// Password generation function for unit testing, default to nullptr.
+// If not null, ForceFixPassword() will also always use |kMinDigit| as the digit
+// replacement, instead of choosing randomly.
+int (*g_test_override_generator)(char* word,
+ char* hypenated_word,
+ unsigned short minlen,
+ unsigned short maxlen,
+ unsigned int pass_mode) = nullptr;
+
+} // namespace
+
+namespace autofill {
+
+const int PasswordGeneratorFips181::kDefaultPasswordLength = 15;
+
+void ForceFixPassword(std::string* password) {
+ for (char& it : *password) {
+ if (islower(it)) {
+ it = base::ToUpperASCII(it);
+ break;
+ }
+ }
+ for (std::string::reverse_iterator iter = password->rbegin();
+ iter != password->rend(); ++iter) {
+ if (islower(*iter)) {
+ // Tests will use |PasswordGeneratorFips181::SetGeneratorForTest| to put a
+ // non-random generator in |g_test_override_generator|. To eliminate the
+ // other source of randomness, always fix the chosen digit to |kMinDigit|
+ // in such case.
+ *iter = g_test_override_generator == nullptr
+ ? base::RandInt(kMinDigit, kMaxDigit)
+ : kMinDigit;
+ break;
+ }
+ }
+}
+
+PasswordGeneratorFips181::PasswordGeneratorFips181(int max_length)
+ : password_length_(GetLengthFromHint(max_length, kDefaultPasswordLength)) {}
+PasswordGeneratorFips181::~PasswordGeneratorFips181() {}
+
+void PasswordGeneratorFips181::SetGeneratorForTest(
+ int (*generator)(char* word,
+ char* hypenated_word,
+ unsigned short minlen,
+ unsigned short maxlen,
+ unsigned int pass_mode)) {
+ g_test_override_generator = generator;
+}
+
+std::string PasswordGeneratorFips181::Generate() const {
+ char password[255];
+ char unused_hypenated_password[255];
+ // Generate passwords that have numbers and upper and lower case letters.
+ // No special characters included for now.
+ unsigned int mode = S_NB | S_CL | S_SL;
+
+ // Generate the password and fix afterwards if needed.
+ auto generator =
+ g_test_override_generator ? g_test_override_generator : gen_pron_pass;
+ generator(password, unused_hypenated_password, password_length_,
+ password_length_, mode);
+ std::string str_password(password);
+
+ if (VerifyPassword(str_password))
+ return str_password;
+
+ ForceFixPassword(&str_password);
+ return str_password;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/password_generator_fips181.h b/chromium/components/autofill/core/browser/password_generator_fips181.h
new file mode 100644
index 00000000000..732cc732b8d
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_generator_fips181.h
@@ -0,0 +1,62 @@
+// 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_AUTOFILL_CORE_BROWSER_PASSWORD_GENERATOR_FIPS181_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_GENERATOR_FIPS181_H_
+
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+
+namespace autofill {
+
+// Make sure that there is at least one upper case and one number in the
+// password. |password| must not be null, and must point to a string containing
+// at least 3 lower-case letters.
+extern void ForceFixPassword(std::string* password);
+
+// Class to generate random passwords. Currently we just use a generic algorithm
+// for all sites, but eventually we can incorporate additional information to
+// determine passwords that are likely to be accepted (i.e. use pattern field,
+// previous generated passwords, crowdsourcing, etc.)
+class PasswordGeneratorFips181 {
+ public:
+ // |max_length| is used as a hint for the generated password's length.
+ explicit PasswordGeneratorFips181(int max_length);
+ ~PasswordGeneratorFips181();
+
+ // Returns a random password such that:
+ // (1) Each character is guaranteed to be a non-whitespace printable ASCII
+ // character.
+ // (2) The generated password will contain AT LEAST one upper case letter, one
+ // lower case letter, and one digit.
+ // (3) The password length will be equal to |password_length_| (see comment
+ // for the constructor).
+ // Not thread safe.
+ std::string Generate() const;
+
+ // This method allows to substitute a replacement for the fips181 method
+ // gen_pron_pass, used by the generator to generate the password. This is
+ // useful in tests, for providing a deterministic generator.
+ static void SetGeneratorForTest(int (*generator)(char* word,
+ char* hypenated_word,
+ unsigned short minlen,
+ unsigned short maxlen,
+ unsigned int pass_mode));
+
+ private:
+ // Unit test also need to access |kDefaultPasswordLength|.
+ static const int kDefaultPasswordLength;
+ FRIEND_TEST_ALL_PREFIXES(PasswordGeneratorFips181Test, PasswordLength);
+
+ // The length of the generated password.
+ const int password_length_;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordGeneratorFips181);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_GENERATOR_FIPS181_H_
diff --git a/chromium/components/autofill/core/browser/password_generator_fips181_fuzzer.cc b/chromium/components/autofill/core/browser/password_generator_fips181_fuzzer.cc
new file mode 100644
index 00000000000..83d6b2618d4
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_generator_fips181_fuzzer.cc
@@ -0,0 +1,47 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "components/autofill/core/browser/password_generator_fips181.h"
+
+namespace autofill {
+
+namespace {
+
+const char* g_password_text = nullptr;
+
+// The "PasswordGeneratorFips181" is a wrapper around Fips181's gen_pron_pass().
+// The former processes the random string from the latter and ensures that it
+// meets some constraints. GenerateForTest here substitutes for gen_pron_pass(),
+// so that the fuzzer tests the wrapper's logic rather than the third-party's
+// generator implementation.
+int GenerateForTest(char* word,
+ char* hypenated_word,
+ unsigned short minlen,
+ unsigned short maxlen,
+ unsigned int pass_mode) {
+ strncpy(word, g_password_text, maxlen);
+ g_password_text = nullptr;
+ // Resize password to |maxlen|.
+ word[maxlen] = '\0';
+ return static_cast<int>(strlen(word));
+}
+
+} // namespace
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ autofill::PasswordGeneratorFips181::SetGeneratorForTest(GenerateForTest);
+ std::string generator_string(reinterpret_cast<const char*>(data), size);
+ g_password_text = generator_string.c_str();
+ autofill::PasswordGeneratorFips181 pg(size);
+ std::string password = pg.Generate();
+ autofill::PasswordGeneratorFips181::SetGeneratorForTest(nullptr);
+ return 0;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/password_generator_fips181_unittest.cc b/chromium/components/autofill/core/browser/password_generator_fips181_unittest.cc
new file mode 100644
index 00000000000..6188aa74421
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_generator_fips181_unittest.cc
@@ -0,0 +1,117 @@
+// 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 <stddef.h>
+#include <string.h>
+
+#include <locale>
+
+#include "components/autofill/core/browser/password_generator_fips181.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void CheckPasswordCorrectness(const std::string& password) {
+ int num_upper_case_letters = 0;
+ int num_lower_case_letters = 0;
+ int num_digits = 0;
+ for (size_t i = 0; i < password.size(); i++) {
+ if (isupper(password[i]))
+ ++num_upper_case_letters;
+ else if (islower(password[i]))
+ ++num_lower_case_letters;
+ else if (isdigit(password[i]))
+ ++num_digits;
+ }
+ EXPECT_GT(num_upper_case_letters, 0) << password;
+ EXPECT_GT(num_lower_case_letters, 0) << password;
+ EXPECT_GT(num_digits, 0) << password;
+}
+
+const char* g_password_text = nullptr;
+
+int GenerateForTest(char* word,
+ char* hypenated_word,
+ unsigned short minlen,
+ unsigned short maxlen,
+ unsigned int pass_mode) {
+ EXPECT_LE(minlen, maxlen);
+ EXPECT_TRUE(word);
+ EXPECT_TRUE(hypenated_word);
+ EXPECT_TRUE(g_password_text) << "Set g_password_text before every call";
+ strncpy(word, g_password_text, maxlen);
+ g_password_text = nullptr;
+ // Resize password to |maxlen|.
+ word[maxlen] = '\0';
+ EXPECT_GE(strlen(word), minlen)
+ << "Make sure to provide enough characters in g_password_text";
+ return static_cast<int>(strlen(word));
+}
+
+class PasswordGeneratorFips181Test : public ::testing::Test {
+ public:
+ PasswordGeneratorFips181Test() {
+ autofill::PasswordGeneratorFips181::SetGeneratorForTest(GenerateForTest);
+ }
+ ~PasswordGeneratorFips181Test() override {
+ autofill::PasswordGeneratorFips181::SetGeneratorForTest(nullptr);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PasswordGeneratorFips181Test);
+};
+
+} // namespace
+
+namespace autofill {
+
+TEST_F(PasswordGeneratorFips181Test, PasswordLength) {
+ PasswordGeneratorFips181 pg1(10);
+ g_password_text = "Aa12345678901234567890";
+ std::string password = pg1.Generate();
+ EXPECT_EQ(password.size(), 10u);
+
+ PasswordGeneratorFips181 pg2(-1);
+ g_password_text = "Aa12345678901234567890";
+ password = pg2.Generate();
+ EXPECT_EQ(
+ password.size(),
+ static_cast<size_t>(PasswordGeneratorFips181::kDefaultPasswordLength));
+
+ PasswordGeneratorFips181 pg3(100);
+ g_password_text = "Aa12345678901234567890";
+ password = pg3.Generate();
+ EXPECT_EQ(
+ password.size(),
+ static_cast<size_t>(PasswordGeneratorFips181::kDefaultPasswordLength));
+}
+
+TEST_F(PasswordGeneratorFips181Test, PasswordPattern) {
+ PasswordGeneratorFips181 pg1(12);
+ g_password_text = "012345678jkl";
+ std::string password1 = pg1.Generate();
+ CheckPasswordCorrectness(password1);
+
+ PasswordGeneratorFips181 pg2(12);
+ g_password_text = "abcDEFGHIJKL";
+ std::string password2 = pg2.Generate();
+ CheckPasswordCorrectness(password2);
+
+ PasswordGeneratorFips181 pg3(12);
+ g_password_text = "abcdefghijkl";
+ std::string password3 = pg3.Generate();
+ CheckPasswordCorrectness(password3);
+}
+
+TEST_F(PasswordGeneratorFips181Test, ForceFixPasswordTest) {
+ std::string passwords_to_fix[] = {"nonumbersoruppercase",
+ "nonumbersWithuppercase",
+ "numbers3Anduppercase", "UmpAwgemHoc"};
+ for (auto& password : passwords_to_fix) {
+ ForceFixPassword(&password);
+ CheckPasswordCorrectness(password);
+ }
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/password_generator_unittest.cc b/chromium/components/autofill/core/browser/password_generator_unittest.cc
index eb041a0e62b..85343887168 100644
--- a/chromium/components/autofill/core/browser/password_generator_unittest.cc
+++ b/chromium/components/autofill/core/browser/password_generator_unittest.cc
@@ -1,76 +1,273 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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 <stddef.h>
-
-#include <locale>
-
#include "components/autofill/core/browser/password_generator.h"
+
+#include "base/logging.h"
+#include "components/autofill/core/browser/proto/password_requirements.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace autofill {
+
namespace {
-void CheckPasswordCorrectness(const std::string& password) {
- int num_upper_case_letters = 0;
- int num_lower_case_letters = 0;
- int num_digits = 0;
- for (size_t i = 0; i < password.size(); i++) {
- if (isupper(password[i]))
- ++num_upper_case_letters;
- else if (islower(password[i]))
- ++num_lower_case_letters;
- else if (isdigit(password[i]))
- ++num_digits;
+// These are strings instead of enums to have an easy way of logging them.
+constexpr char kLowerCase[] = "lower_case";
+constexpr char kUpperCase[] = "upper_case";
+constexpr char kAlphabetic[] = "alphabetic";
+constexpr char kNumeric[] = "numeric";
+constexpr char kSymbol[] = "symbol";
+
+constexpr const char* kAllClassesButSymbols[] = {kLowerCase, kUpperCase,
+ kAlphabetic, kNumeric};
+constexpr const char* kAllClassesButSymbolsAndAlphabetic[] = {
+ kLowerCase, kUpperCase, kNumeric};
+
+bool IsCharInClass(base::char16 c, const std::string& class_name) {
+ if (class_name == kLowerCase) {
+ return 'a' <= c && c <= 'z';
+ } else if (class_name == kUpperCase) {
+ return 'A' <= c && c <= 'Z';
+ } else if (class_name == kAlphabetic) {
+ return IsCharInClass(c, kLowerCase) || IsCharInClass(c, kUpperCase);
+ } else if (class_name == kNumeric) {
+ return '0' <= c && c <= '9';
}
- EXPECT_GT(num_upper_case_letters, 0) << password;
- EXPECT_GT(num_lower_case_letters, 0) << password;
- EXPECT_GT(num_digits, 0) << password;
+ // Symbols are not covered because there is not fixed definition and because
+ // symbols are treated like other character classes, so the importance of
+ // dealing with them here is limited.
+ NOTREACHED() << "Don't call IsCharInClass for symbols";
+ return false;
}
-} // namespace
+size_t CountCharsInClass(const base::string16& password,
+ const std::string& class_name) {
+ size_t num = 0;
+ for (base::char16 c : password) {
+ if (IsCharInClass(c, class_name))
+ ++num;
+ }
+ return num;
+}
-namespace autofill {
+PasswordRequirementsSpec_CharacterClass* GetMutableCharClass(
+ PasswordRequirementsSpec* spec,
+ const std::string& class_name) {
+ if (class_name == kLowerCase) {
+ return spec->mutable_lower_case();
+ } else if (class_name == kUpperCase) {
+ return spec->mutable_upper_case();
+ } else if (class_name == kAlphabetic) {
+ return spec->mutable_alphabetic();
+ } else if (class_name == kNumeric) {
+ return spec->mutable_numeric();
+ } else if (class_name == kSymbol) {
+ return spec->mutable_symbols();
+ }
+ NOTREACHED();
+ return nullptr;
+}
-TEST(PasswordGeneratorTest, PasswordLength) {
- PasswordGenerator pg1(10);
- std::string password = pg1.Generate();
- EXPECT_EQ(password.size(), 10u);
+class PasswordGeneratorTest : public testing::Test {
+ public:
+ PasswordGeneratorTest() { spec_.set_spec_version(1); }
+ ~PasswordGeneratorTest() override = default;
- PasswordGenerator pg2(-1);
- password = pg2.Generate();
- EXPECT_EQ(password.size(),
- static_cast<size_t>(PasswordGenerator::kDefaultPasswordLength));
+ PasswordRequirementsSpec spec_;
+};
- PasswordGenerator pg3(100);
- password = pg3.Generate();
- EXPECT_EQ(password.size(),
- static_cast<size_t>(PasswordGenerator::kDefaultPasswordLength));
+TEST_F(PasswordGeneratorTest, PasswordLengthDefault) {
+ EXPECT_EQ(kDefaultPasswordLength, GeneratePassword(spec_).length());
}
-TEST(PasswordGeneratorTest, PasswordPattern) {
- PasswordGenerator pg(12);
- std::string password = pg.Generate();
- CheckPasswordCorrectness(password);
+TEST_F(PasswordGeneratorTest, PasswordLengthMaxLength) {
+ // Limit length according to requirement.
+ spec_.set_max_length(kDefaultPasswordLength - 5u);
+ EXPECT_EQ(kDefaultPasswordLength - 5u, GeneratePassword(spec_).length());
+
+ // If max is higher than default, it does not matter.
+ spec_.set_max_length(kDefaultPasswordLength + 5);
+ EXPECT_EQ(kDefaultPasswordLength, GeneratePassword(spec_).length());
+}
+
+TEST_F(PasswordGeneratorTest, PasswordLengthMinLength) {
+ // If min is smaller than default, it does not matter.
+ spec_.set_min_length(kDefaultPasswordLength - 5u);
+ EXPECT_EQ(kDefaultPasswordLength, GeneratePassword(spec_).length());
+
+ // If a higher minimum length is explicitly set, use it.
+ spec_.set_min_length(kDefaultPasswordLength + 5u);
+ EXPECT_EQ(kDefaultPasswordLength + 5u, GeneratePassword(spec_).length());
+}
+
+TEST_F(PasswordGeneratorTest, PasswordLengthMinAndMax) {
+ // Configure a contradicting min and max length. The max length wins.
+ spec_.set_min_length(kDefaultPasswordLength + 5u);
+ spec_.set_max_length(kDefaultPasswordLength - 5u);
+ EXPECT_EQ(kDefaultPasswordLength - 5u, GeneratePassword(spec_).length());
}
-TEST(PasswordGeneratorTest, Printable) {
- PasswordGenerator pg(12);
- std::string password = pg.Generate();
- for (size_t i = 0; i < password.size(); i++) {
- // Make sure that the character is printable.
- EXPECT_TRUE(isgraph(password[i]));
+TEST_F(PasswordGeneratorTest, MinCharFrequenciesRespected) {
+ for (std::string char_class : kAllClassesButSymbols) {
+ SCOPED_TRACE(char_class);
+ spec_ = PasswordRequirementsSpec();
+ spec_.set_spec_version(1);
+ auto* char_class_config = GetMutableCharClass(&spec_, char_class);
+ char_class_config->set_min(10);
+ char_class_config->set_max(1000);
+
+ base::string16 password = GeneratePassword(spec_);
+ EXPECT_GE(CountCharsInClass(password, char_class), 10u);
}
}
-TEST(PasswordGeneratorTest, ForceFixPasswordTest) {
- std::string passwords_to_fix[] = {"nonumbersoruppercase",
- "nonumbersWithuppercase",
- "numbers3Anduppercase", "UmpAwgemHoc"};
- for (auto& password : passwords_to_fix) {
- ForceFixPassword(&password);
- CheckPasswordCorrectness(password);
+TEST_F(PasswordGeneratorTest, MinCharFrequenciesInsane) {
+ // Nothing breaks if the min frequencies are way beyond what's possible
+ // with the password length. In this case the generated passwor may contain
+ // just characters of one class but its target length does not increase.
+ for (std::string char_class : kAllClassesButSymbols) {
+ SCOPED_TRACE(char_class);
+ spec_ = PasswordRequirementsSpec();
+ spec_.set_spec_version(1);
+ auto* char_class_config = GetMutableCharClass(&spec_, char_class);
+ char_class_config->set_min(1000);
+ char_class_config->set_max(1000);
+
+ base::string16 password = GeneratePassword(spec_);
+ // At least some of the characters should be there.
+ EXPECT_GE(CountCharsInClass(password, char_class), 1u);
+ // But the password length does not change by minimum length requirements.
+ EXPECT_EQ(kDefaultPasswordLength, GeneratePassword(spec_).length());
}
}
+TEST_F(PasswordGeneratorTest, MinCharFrequenciesBiggerThanMax) {
+ spec_.set_min_length(15);
+ spec_.set_max_length(15);
+ for (std::string char_class : kAllClassesButSymbolsAndAlphabetic) {
+ auto* char_class_config = GetMutableCharClass(&spec_, char_class);
+ // Min is reduced to max --> each class should have 5 representatives.
+ char_class_config->set_min(10);
+ char_class_config->set_max(5);
+ }
+
+ base::string16 password = GeneratePassword(spec_);
+
+ for (std::string char_class : kAllClassesButSymbolsAndAlphabetic) {
+ SCOPED_TRACE(char_class);
+ // Check that each character class is represented by 5 characters.
+ EXPECT_EQ(5u, CountCharsInClass(password, char_class));
+ }
+ EXPECT_EQ(15u, password.length());
+}
+
+TEST_F(PasswordGeneratorTest, MaxFrequenciesRespected) {
+ // Limit the maximum occurrences of characters of some classes to 2 and
+ // validate that this is respected.
+ for (std::string char_class : kAllClassesButSymbolsAndAlphabetic) {
+ SCOPED_TRACE(char_class);
+ spec_ = PasswordRequirementsSpec();
+ spec_.set_spec_version(1);
+ auto* char_class_config = GetMutableCharClass(&spec_, char_class);
+ char_class_config->set_max(2);
+
+ base::string16 password = GeneratePassword(spec_);
+ EXPECT_LE(CountCharsInClass(password, char_class), 2u);
+ // Ensure that the other character classes that are not limited fill up the
+ // password to the desired length.
+ EXPECT_EQ(kDefaultPasswordLength, GeneratePassword(spec_).length());
+ }
+}
+
+TEST_F(PasswordGeneratorTest, MaxFrequenciesInsufficient) {
+ spec_.set_min_length(15);
+ spec_.set_max_length(15);
+ // Limit the maximum occurrences of characters of all classes to 2 which
+ // sums up to less than 15.
+ for (std::string char_class : kAllClassesButSymbolsAndAlphabetic) {
+ auto* char_class_config = GetMutableCharClass(&spec_, char_class);
+ char_class_config->set_max(2);
+ }
+ // The resulting password can contain only 6 characters.
+ EXPECT_EQ(6u, GeneratePassword(spec_).length());
+}
+
+TEST_F(PasswordGeneratorTest, CharacterSetCanBeOverridden) {
+ // Limit lower case chars to 'a' and 'b' and require exacty 5 of those.
+ spec_.mutable_lower_case()->set_character_set("ab");
+ spec_.mutable_lower_case()->set_min(5);
+ spec_.mutable_lower_case()->set_max(5);
+ base::string16 password = GeneratePassword(spec_);
+ // Then ensure that 'a's and 'b's are generated at the expected frequencies
+ // as an indicator that the override was respected.
+ size_t num_as_and_bs = 0;
+ for (base::char16 c : password) {
+ if (c == 'a' || c == 'b')
+ ++num_as_and_bs;
+ }
+ EXPECT_EQ(5u, num_as_and_bs);
+}
+
+TEST_F(PasswordGeneratorTest, AllCharactersAreGenerated) {
+ // Limit lower case chars to 'a' and 'b' and require exacty 5 of those.
+ spec_.mutable_lower_case()->set_character_set("ab");
+ spec_.mutable_lower_case()->set_min(5);
+ spec_.mutable_lower_case()->set_max(5);
+ bool success = false;
+ // The chance of not seing both an 'a' and a 'b' in one run are only 2/32 per
+ // run. Ultimately we should see success. If none of the 100 generated
+ // passwords contained an 'a' (or 'b'), then this would indicate that the
+ // generator does not fully use the character set (probably due to an
+ // off-by-one error).
+ for (size_t attempt = 0; attempt < 100; ++attempt) {
+ base::string16 password = GeneratePassword(spec_);
+ size_t num_as = 0;
+ size_t num_bs = 0;
+ for (base::char16 c : password) {
+ if (c == 'a')
+ ++num_as;
+ if (c == 'b')
+ ++num_bs;
+ }
+ if (num_as > 0u && num_bs > 0u) {
+ success = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(success);
+}
+
+TEST_F(PasswordGeneratorTest, PasswordCanBeGeneratedWithEmptyCharSet) {
+ // If the character set is empty, min and max should be ignored.
+ spec_.mutable_lower_case()->set_character_set("");
+ spec_.mutable_lower_case()->set_min(5);
+ spec_.mutable_lower_case()->set_max(5);
+ base::string16 password = GeneratePassword(spec_);
+ EXPECT_EQ(0u, CountCharsInClass(password, kLowerCase));
+ EXPECT_EQ(kDefaultPasswordLength, GeneratePassword(spec_).length());
+}
+
+TEST_F(PasswordGeneratorTest, AllCharactersForbidden) {
+ spec_.set_min_length(kDefaultPasswordLength + 2);
+ spec_.set_max_length(kDefaultPasswordLength + 2);
+ for (std::string char_class : kAllClassesButSymbolsAndAlphabetic) {
+ auto* char_class_config = GetMutableCharClass(&spec_, char_class);
+ char_class_config->set_max(0);
+ }
+ // If it is impossible to generate a password (due to max = 0), the generator
+ // delivers a password as per default spec and ignores everything else.
+ EXPECT_EQ(kDefaultPasswordLength, GeneratePassword(spec_).length());
+}
+
+TEST_F(PasswordGeneratorTest, ZeroLength) {
+ spec_.set_min_length(0);
+ spec_.set_max_length(0);
+ // If the generated password following the spec is empty, a default spec is
+ // applied.
+ EXPECT_EQ(kDefaultPasswordLength, GeneratePassword(spec_).length());
+}
+
+} // namespace
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/password_requirements_spec_fetcher.h b/chromium/components/autofill/core/browser/password_requirements_spec_fetcher.h
new file mode 100644
index 00000000000..d07997de389
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_requirements_spec_fetcher.h
@@ -0,0 +1,41 @@
+// 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_PASSWORD_REQUIREMENTS_SPEC_FETCHER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_REQUIREMENTS_SPEC_FETCHER_H_
+
+#include "base/callback.h"
+#include "url/gurl.h"
+
+namespace autofill {
+
+class PasswordRequirementsSpec;
+
+// Fetches PasswordRequirementsSpec for a specific origin.
+class PasswordRequirementsSpecFetcher {
+ public:
+ using FetchCallback =
+ base::OnceCallback<void(const PasswordRequirementsSpec&)>;
+
+ virtual ~PasswordRequirementsSpecFetcher() = default;
+
+ // Fetches a configuration for |origin|.
+ //
+ // |origin| references the origin in the PasswordForm for which rules need to
+ // be fetched.
+ //
+ // The |callback| must remain valid until called back, but this class may be
+ // destroyed before the |callback| has been triggered.
+ //
+ // Fetch() may be called multiple times concurrently. Requests are batched
+ // if possible.
+ //
+ // If the network request fails or times out, the callback receives an empty
+ // spec.
+ virtual void Fetch(GURL origin, FetchCallback callback) = 0;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_REQUIREMENTS_SPEC_FETCHER_H_
diff --git a/chromium/components/autofill/core/browser/password_requirements_spec_fetcher_impl.cc b/chromium/components/autofill/core/browser/password_requirements_spec_fetcher_impl.cc
new file mode 100644
index 00000000000..2c065752666
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_requirements_spec_fetcher_impl.cc
@@ -0,0 +1,314 @@
+// 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/password_requirements_spec_fetcher_impl.h"
+
+#include "base/logging.h"
+#include "base/md5.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "components/autofill/core/browser/password_requirements_spec_printer.h"
+#include "components/autofill/core/browser/proto/password_requirements.pb.h"
+#include "components/autofill/core/browser/proto/password_requirements_shard.pb.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.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/url_canon.h"
+
+namespace autofill {
+
+PasswordRequirementsSpecFetcherImpl::PasswordRequirementsSpecFetcherImpl(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ int version,
+ size_t prefix_length,
+ int timeout)
+ : url_loader_factory_(std::move(url_loader_factory)),
+ version_(version),
+ prefix_length_(prefix_length),
+ timeout_(timeout) {
+ DCHECK_GE(version_, 0);
+ DCHECK_LE(prefix_length_, 32u);
+ DCHECK_GE(timeout_, 0);
+}
+
+PasswordRequirementsSpecFetcherImpl::~PasswordRequirementsSpecFetcherImpl() =
+ default;
+
+PasswordRequirementsSpecFetcherImpl::LookupInFlight::LookupInFlight() = default;
+PasswordRequirementsSpecFetcherImpl::LookupInFlight::~LookupInFlight() =
+ default;
+
+namespace {
+
+// Hashes the eTLD+1 of |origin| via MD5 and returns a filename with the first
+// |prefix_length| bits populated. The returned value corresponds to the first
+// 4 bytes of the truncated MD5 prefix in hex notation.
+// For example:
+// "https://www.example.com" has a eTLD+1 of "example.com".
+// The MD5SUM of that is 5ababd603b22780302dd8d83498e5172.
+// Stripping this to the first 8 bits (prefix_length = 8) gives
+// 500000000000000000000000000000000. The file name is always cut to the first
+// four bytes, i.e. 5000 in this example.
+std::string GetHashPrefix(const GURL& origin, size_t prefix_length) {
+ DCHECK_LE(prefix_length, 32u);
+ std::string domain_and_registry =
+ net::registry_controlled_domains::GetDomainAndRegistry(
+ origin, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+
+ base::MD5Digest digest;
+ base::MD5Sum(domain_and_registry.data(), domain_and_registry.size(), &digest);
+
+ for (size_t i = 0; i < base::size(digest.a); ++i) {
+ if (prefix_length >= 8) {
+ prefix_length -= 8;
+ continue;
+ } else {
+ // Determine the |prefix_length| most significant bits by calculating
+ // the 8 - |prefix_length| least significant bits and inverting the
+ // result.
+ digest.a[i] &= ~((1 << (8 - prefix_length)) - 1);
+ prefix_length = 0;
+ }
+ }
+
+ return base::MD5DigestToBase16(digest).substr(0, 4);
+}
+
+// Returns the URL on gstatic.com where the passwords spec file can be found
+// that contains data for |hash_prefix|.
+GURL GetUrlForRequirementsSpec(int version, const std::string& hash_prefix) {
+ return GURL(base::StringPrintf(
+ "https://www.gstatic.com/chrome/autofill/password_generation_specs/%d/%s",
+ version, hash_prefix.c_str()));
+}
+
+} // namespace
+
+void PasswordRequirementsSpecFetcherImpl::Fetch(
+ GURL origin,
+ FetchCallback callback) {
+ DCHECK(origin.is_valid());
+ VLOG(1) << "Fetching password requirements spec for " << origin;
+
+ if (!url_loader_factory_) {
+ VLOG(1) << "No url_logger_factory_ available";
+ TriggerCallback(std::move(callback), ResultCode::kErrorNoUrlLoader,
+ PasswordRequirementsSpec());
+ return;
+ }
+
+ if (!url_loader_factory_) {
+ TriggerCallback(std::move(callback), ResultCode::kErrorNoUrlLoader,
+ PasswordRequirementsSpec());
+ return;
+ }
+
+ if (!url_loader_factory_) {
+ TriggerCallback(std::move(callback), ResultCode::kErrorNoUrlLoader,
+ PasswordRequirementsSpec());
+ return;
+ }
+
+ if (!origin.is_valid() || origin.HostIsIPAddress() ||
+ !origin.SchemeIsHTTPOrHTTPS()) {
+ VLOG(1) << "No valid origin";
+ TriggerCallback(std::move(callback), ResultCode::kErrorInvalidOrigin,
+ PasswordRequirementsSpec());
+ return;
+ }
+
+ // Canonicalize away trailing periods in hostname.
+ while (!origin.host().empty() && origin.host().back() == '.') {
+ std::string new_host = origin.host().substr(0, origin.host().length() - 1);
+ url::Replacements<char> replacements;
+ replacements.SetHost(new_host.c_str(),
+ url::Component(0, new_host.length()));
+ origin = origin.ReplaceComponents(replacements);
+ }
+
+ std::string hash_prefix = GetHashPrefix(origin, prefix_length_);
+
+ // If a lookup is happening already, just register another callback.
+ auto iter = lookups_in_flight_.find(hash_prefix);
+ if (iter != lookups_in_flight_.end()) {
+ iter->second->callbacks.push_back(
+ std::make_pair(origin, std::move(callback)));
+ VLOG(1) << "Lookup already in flight";
+ return;
+ }
+
+ // Start another lookup otherwise.
+ auto lookup = std::make_unique<LookupInFlight>();
+ lookup->callbacks.push_back(std::make_pair(origin, std::move(callback)));
+ lookup->start_of_request = base::TimeTicks::Now();
+
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("password_requirements_spec_fetch",
+ R"(
+ semantics {
+ sender: "Password requirements specification fetcher"
+ description:
+ "Fetches the password requirements for a set of domains whose origin "
+ "hash starts with a certain prefix."
+ trigger:
+ "When the user triggers a password generation (this can happen by "
+ "just focussing a password field)."
+ data:
+ "The URL encodes a hash prefix from which it is not possible to "
+ "derive the original origin. No user information is sent."
+ destination: WEBSITE
+ }
+ policy {
+ cookies_allowed: NO
+ setting: "Unconditionally enabled."
+ policy_exception_justification:
+ "Not implemented, considered not useful."
+ })");
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = GetUrlForRequirementsSpec(version_, hash_prefix);
+ resource_request->load_flags = net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SEND_AUTH_DATA;
+ lookup->url_loader = network::SimpleURLLoader::Create(
+ std::move(resource_request), traffic_annotation);
+ lookup->url_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&PasswordRequirementsSpecFetcherImpl::OnFetchComplete,
+ base::Unretained(this), hash_prefix));
+
+ lookup->download_timer.Start(
+ FROM_HERE, base::TimeDelta::FromMilliseconds(timeout_),
+ base::BindRepeating(&PasswordRequirementsSpecFetcherImpl::OnFetchTimeout,
+ base::Unretained(this), hash_prefix));
+
+ lookups_in_flight_[hash_prefix] = std::move(lookup);
+}
+
+void PasswordRequirementsSpecFetcherImpl::OnFetchComplete(
+ const std::string& hash_prefix,
+ std::unique_ptr<std::string> response_body) {
+ std::unique_ptr<LookupInFlight> lookup = RemoveLookupInFlight(hash_prefix);
+
+ lookup->download_timer.Stop();
+ UMA_HISTOGRAM_TIMES("PasswordManager.RequirementsSpecFetcher.NetworkDuration",
+ base::TimeTicks::Now() - lookup->start_of_request);
+ base::UmaHistogramSparse(
+ "PasswordManager.RequirementsSpecFetcher.NetErrorCode",
+ lookup->url_loader->NetError());
+ if (lookup->url_loader->ResponseInfo() &&
+ lookup->url_loader->ResponseInfo()->headers) {
+ base::UmaHistogramSparse(
+ "PasswordManager.RequirementsSpecFetcher.HttpResponseCode",
+ lookup->url_loader->ResponseInfo()->headers->response_code());
+ }
+
+ if (!response_body || lookup->url_loader->NetError() != net::Error::OK) {
+ VLOG(1) << "Fetch for " << hash_prefix << ": failed to fetch "
+ << lookup->url_loader->NetError();
+ TriggerCallbackToAll(&lookup->callbacks, ResultCode::kErrorFailedToFetch,
+ PasswordRequirementsSpec());
+ return;
+ }
+
+ PasswordRequirementsShard shard;
+ if (!shard.ParseFromString(*response_body)) {
+ VLOG(1) << "Fetch for " << hash_prefix << ": failed to parse response";
+ TriggerCallbackToAll(&lookup->callbacks, ResultCode::kErrorFailedToParse,
+ PasswordRequirementsSpec());
+ return;
+ }
+ for (auto& callback_pair : lookup->callbacks) {
+ const GURL& origin = callback_pair.first;
+ FetchCallback& callback_function = callback_pair.second;
+
+ // Search shard for matches for origin by looking up the (canonicalized)
+ // host name and then stripping domain prefixes until the eTLD+1 is reached.
+ DCHECK(!origin.HostIsIPAddress());
+ // |host| is a std::string instead of StringPiece as the protbuf::Map
+ // implementation does not support StringPieces as parameters for find.
+ std::string host = origin.host();
+ auto host_iter = shard.specs().find(host);
+ if (host_iter != shard.specs().end()) {
+ const PasswordRequirementsSpec& spec = host_iter->second;
+ VLOG(1) << "Found for " << host << ": " << spec;
+ TriggerCallback(std::move(callback_function), ResultCode::kFoundSpec,
+ spec);
+ continue;
+ }
+
+ bool found_entry = false;
+ const std::string domain_and_registry =
+ net::registry_controlled_domains::GetDomainAndRegistry(
+ origin,
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+ while (host.length() > 0 && host != domain_and_registry) {
+ size_t pos = host.find('.');
+ if (pos != std::string::npos) { // strip prefix
+ host = host.substr(pos + 1);
+ } else {
+ break;
+ }
+ // If an entry has ben found, exit with that.
+ auto it = shard.specs().find(host);
+ if (it != shard.specs().end()) {
+ const PasswordRequirementsSpec& spec = it->second;
+ found_entry = true;
+ VLOG(1) << "Found for " << host << ": " << spec;
+ TriggerCallback(std::move(callback_function), ResultCode::kFoundSpec,
+ spec);
+ break;
+ }
+ }
+
+ if (!found_entry) {
+ VLOG(1) << "Found no entry for " << host;
+ TriggerCallback(std::move(callback_function), ResultCode::kFoundNoSpec,
+ PasswordRequirementsSpec());
+ }
+ }
+}
+
+void PasswordRequirementsSpecFetcherImpl::OnFetchTimeout(
+ const std::string& hash_prefix) {
+ std::unique_ptr<LookupInFlight> lookup = RemoveLookupInFlight(hash_prefix);
+ UMA_HISTOGRAM_TIMES("PasswordManager.RequirementsSpecFetcher.NetworkDuration",
+ base::TimeTicks::Now() - lookup->start_of_request);
+ TriggerCallbackToAll(&lookup->callbacks, ResultCode::kErrorTimeout,
+ PasswordRequirementsSpec());
+}
+
+void PasswordRequirementsSpecFetcherImpl::TriggerCallbackToAll(
+ std::list<std::pair<GURL, FetchCallback>>* callbacks,
+ ResultCode result,
+ const PasswordRequirementsSpec& spec) {
+ for (auto& callback_pair : *callbacks) {
+ TriggerCallback(std::move(callback_pair.second), result, spec);
+ }
+}
+
+void PasswordRequirementsSpecFetcherImpl::TriggerCallback(
+ FetchCallback callback,
+ ResultCode result,
+ const PasswordRequirementsSpec& spec) {
+ UMA_HISTOGRAM_ENUMERATION("PasswordManager.RequirementsSpecFetcher.Result",
+ result);
+ std::move(callback).Run(spec);
+}
+
+std::unique_ptr<PasswordRequirementsSpecFetcherImpl::LookupInFlight>
+PasswordRequirementsSpecFetcherImpl::RemoveLookupInFlight(
+ const std::string& hash_prefix) {
+ DCHECK(lookups_in_flight_.find(hash_prefix) != lookups_in_flight_.end());
+ std::unique_ptr<LookupInFlight> lookup;
+ std::swap(lookup, lookups_in_flight_[hash_prefix]);
+ lookups_in_flight_.erase(hash_prefix);
+ return lookup;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/password_requirements_spec_fetcher_impl.h b/chromium/components/autofill/core/browser/password_requirements_spec_fetcher_impl.h
new file mode 100644
index 00000000000..3d22b0e8af9
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_requirements_spec_fetcher_impl.h
@@ -0,0 +1,143 @@
+// 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_PASSWORD_REQUIREMENTS_SPEC_FETCHER_IMPL_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_REQUIREMENTS_SPEC_FETCHER_IMPL_H_
+
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/timer/timer.h"
+#include "components/autofill/core/browser/password_requirements_spec_fetcher.h"
+#include "url/gurl.h"
+
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+} // namespace network
+
+namespace autofill {
+
+class PasswordRequirementsSpec;
+
+// A concrete implementation for PasswordRequirementsSpecFetcher that talks
+// to the network.
+class PasswordRequirementsSpecFetcherImpl
+ : public PasswordRequirementsSpecFetcher {
+ public:
+ // This enum is used in histograms. Do not change or reuse values.
+ enum class ResultCode {
+ // Fetched spec file, parsed it, but found no entry for the origin.
+ kFoundNoSpec = 0,
+ // Fetched spec file, parsed it and found an entry.
+ kFoundSpec = 1,
+ // The origin is an IP address, not HTTP/HTTPS, or not a valid URL.
+ kErrorInvalidOrigin = 2,
+ // Server responded with an empty document or an error code.
+ kErrorFailedToFetch = 3,
+ // Server timed out.
+ kErrorTimeout = 4,
+ // Server responded with a document but it could not be parsed.
+ kErrorFailedToParse = 5,
+ // No URL loader configured for the PasswordRequirementsSpecFetcher.
+ kErrorNoUrlLoader = 6,
+ kMaxValue = kErrorNoUrlLoader,
+ };
+
+ // See the member variables for explanations of these parameters.
+ PasswordRequirementsSpecFetcherImpl(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ int version,
+ size_t prefix_length,
+ int timeout);
+ ~PasswordRequirementsSpecFetcherImpl() override;
+
+ // Implementation for PasswordRequirementsSpecFetcher:
+ void Fetch(GURL origin, FetchCallback callback) override;
+
+ private:
+ // This structure bundles all data that are associated to a network request
+ // for a file with a specific hash prefix.
+ struct LookupInFlight {
+ LookupInFlight();
+ ~LookupInFlight();
+
+ // Callbacks to be called if the network request resolves or is aborted.
+ // The GURL represents the origin due to which a spec was fetched.
+ // Used a std::list instead of std::vector to grow this cheaply.
+ std::list<std::pair<GURL, FetchCallback>> callbacks;
+
+ // Timer to kill pending downloads after |timeout_|.
+ base::OneShotTimer download_timer;
+
+ std::unique_ptr<network::SimpleURLLoader> url_loader;
+
+ // Time when the network request is started.
+ base::TimeTicks start_of_request;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LookupInFlight);
+ };
+
+ // These are the two ways how a network request can end. The functions remove
+ // the entry corresponding to |hash_prefix| out of |lookups_in_flight_| as
+ // their first order of business.
+ void OnFetchComplete(const std::string& hash_prefix,
+ std::unique_ptr<std::string> response_body);
+ void OnFetchTimeout(const std::string& hash_prefix);
+
+ // Calls all |callbacks| in order. Note that these callbacks are OnceCallback
+ // instances. Therefore, entries are reset after this function returns.
+ void TriggerCallbackToAll(
+ std::list<std::pair<GURL, FetchCallback>>* callbacks,
+ ResultCode result,
+ const PasswordRequirementsSpec& spec);
+
+ // Calls the |callback| with the specific data and records some metrics.
+ void TriggerCallback(FetchCallback callback,
+ ResultCode result,
+ const PasswordRequirementsSpec& spec);
+
+ std::unique_ptr<LookupInFlight> RemoveLookupInFlight(
+ const std::string& hash_prefix);
+
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+ // A version counter for requirements specs. If data changes on the server,
+ // a new version number is pushed out to prevent that clients continue
+ // using old cached data. This allows setting the HTTP caching expiration to
+ // infinity.
+ int version_;
+
+ // The fetcher determines the URL of the spec file by first hashing the eTLD+1
+ // of |origin| and then taking the first |prefix_length_| bits of the hash
+ // value as part of the file name. (See the code for details.)
+ // |prefix_length_| should always be <= 32 as filenames are limited to the
+ // first 4 bytes of the hash prefix.
+ size_t prefix_length_;
+
+ // Timeout in milliseconds after which any ongoing fetch operation is
+ // canceled.
+ int timeout_;
+
+ // Data about network requests in flight.
+ // The key is the name of the file being fetched without the common URL prefix
+ // (e.g. "0000"). The value contains callbacks that should process the result
+ // and a timer to cancel the lookup after some time.
+ // The invariant of |lookups_in_flight_| is that entries exist from the
+ // time of starting the network request until receiving the response or a
+ // timeout.
+ std::map<std::string, std::unique_ptr<LookupInFlight>> lookups_in_flight_;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordRequirementsSpecFetcherImpl);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_REQUIREMENTS_SPEC_FETCHER_IMPL_H_
diff --git a/chromium/components/autofill/core/browser/password_requirements_spec_fetcher_unittest.cc b/chromium/components/autofill/core/browser/password_requirements_spec_fetcher_unittest.cc
new file mode 100644
index 00000000000..3f7bd199b1f
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_requirements_spec_fetcher_unittest.cc
@@ -0,0 +1,343 @@
+// 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/password_requirements_spec_fetcher_impl.h"
+
+#include "base/logging.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_task_environment.h"
+#include "components/autofill/core/browser/proto/password_requirements.pb.h"
+#include "components/autofill/core/browser/proto/password_requirements_shard.pb.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+namespace {
+
+// URL prefix for spec requests.
+#define SERVER_URL \
+ "https://www.gstatic.com/chrome/autofill/password_generation_specs/"
+
+TEST(PasswordRequirementsSpecFetcherTest, FetchData) {
+ using ResultCode = PasswordRequirementsSpecFetcherImpl::ResultCode;
+
+ // An empty spec is returned for all error cases (time outs, server responding
+ // with anything but HTTP_OK).
+ PasswordRequirementsSpec empty_spec;
+
+ PasswordRequirementsSpec success_spec_for_example_com;
+ success_spec_for_example_com.set_min_length(17);
+ PasswordRequirementsSpec success_spec_for_m_example_com;
+ success_spec_for_m_example_com.set_min_length(18);
+ PasswordRequirementsSpec spec_for_ip;
+ success_spec_for_m_example_com.set_min_length(19);
+ PasswordRequirementsSpec success_spec_for_uber_example_com;
+ success_spec_for_uber_example_com.set_min_length(20);
+
+ std::string serialized_shard;
+ PasswordRequirementsShard shard;
+ (*shard.mutable_specs())["example.com"] = success_spec_for_example_com;
+ (*shard.mutable_specs())["m.example.com"] = success_spec_for_m_example_com;
+ // This spec is stored in the buffer but is not expected to be processed.
+ // Only real hostnames are supposed to be parsed.
+ (*shard.mutable_specs())["192.168.1.1"] = spec_for_ip;
+ // Punycoded entry.
+ (*shard.mutable_specs())["xn--ber-example-shb.com"] =
+ success_spec_for_uber_example_com;
+ shard.SerializeToString(&serialized_shard);
+
+ // If this magic timeout value is set, simulate a timeout.
+ const int kMagicTimeout = 10;
+
+ struct {
+ // Name of the test for log output.
+ const char* test_name;
+
+ // Origin for which the spec is fetched.
+ const char* origin;
+
+ // Current configuration that would be set via Finch.
+ int generation = 1;
+ int prefix_length = 32;
+ int timeout = 1000;
+
+ // Handler for the spec requests.
+ const char* requested_url;
+ std::string response_content;
+ net::HttpStatusCode response_status = net::HTTP_OK;
+
+ // Expected spec.
+ PasswordRequirementsSpec* expected_spec;
+ ResultCode expected_result;
+ } tests[] = {
+ {
+ .test_name = "Business as usual",
+ .origin = "https://www.example.com",
+ // See echo -n example.com | md5sum | cut -b 1-4
+ .requested_url = SERVER_URL "1/5aba",
+ .response_content = serialized_shard,
+ .expected_spec = &success_spec_for_example_com,
+ .expected_result = ResultCode::kFoundSpec,
+ },
+ {
+ .test_name = "Parts before the eTLD+1 don't matter",
+ // m.example.com instead of www.example.com creates the same hash
+ // prefix.
+ .origin = "https://m.example.com",
+ .requested_url = SERVER_URL "1/5aba",
+ .response_content = serialized_shard,
+ // But shard contains a special entry for m.example.com, so verify
+ // that the more specific element is returned.
+ .expected_spec = &success_spec_for_m_example_com,
+ .expected_result = ResultCode::kFoundSpec,
+ },
+ {
+ .test_name = "The generation is encoded in the url",
+ .origin = "https://www.example.com",
+ // Here the test differs from the default:
+ .generation = 2,
+ .requested_url = SERVER_URL "2/5aba",
+ .response_content = serialized_shard,
+ .expected_spec = &success_spec_for_example_com,
+ .expected_result = ResultCode::kFoundSpec,
+ },
+ {
+ .test_name = "Shorter prefixes are reflected in the URL",
+ .origin = "https://m.example.com",
+ // The prefix "5abc" starts with 0b01011010. If the prefix is limited
+ // to the first 3 bits, b0100 = 0x4 remains and the rest is zeroed
+ // out.
+ .prefix_length = 3,
+ .requested_url = SERVER_URL "1/4000",
+ .response_content = serialized_shard,
+ .expected_spec = &success_spec_for_m_example_com,
+ .expected_result = ResultCode::kFoundSpec,
+ },
+ {
+ .test_name = "Simulate a 404 response",
+ .origin = "https://www.example.com",
+ .requested_url = SERVER_URL "1/5aba",
+ .response_content = "Not found",
+ // If a file is not found on the server, the spec should be empty.
+ .response_status = net::HTTP_NOT_FOUND,
+ .expected_spec = &empty_spec,
+ .expected_result = ResultCode::kErrorFailedToFetch,
+ },
+ {
+ .test_name = "Simulate a timeout",
+ .origin = "https://www.example.com",
+ // This simulates a timeout.
+ .timeout = kMagicTimeout,
+ // This makes sure that the server does not respond by itself as
+ // TestURLLoaderFactory reacts as if a response has been added to
+ // a URL.
+ .requested_url = SERVER_URL "dont_respond",
+ .response_content = serialized_shard,
+ .expected_spec = &empty_spec,
+ .expected_result = ResultCode::kErrorTimeout,
+ },
+ {
+ .test_name = "Zero prefix",
+ .origin = "https://www.example.com",
+ // A zero prefix will be the hard-coded Finch default and should work.
+ .prefix_length = 0,
+ .requested_url = SERVER_URL "1/0000",
+ .response_content = serialized_shard,
+ .expected_spec = &success_spec_for_example_com,
+ .expected_result = ResultCode::kFoundSpec,
+ },
+ {
+ .test_name = "IP addresses give the empty spec",
+ .origin = "http://192.168.1.1/",
+ // By setting the prefix to 0, the URL of the shard is predefined,
+ // but actually, not network request should be sent as password
+ // requirements are not supported for IP addresses.
+ .prefix_length = 0,
+ .requested_url = SERVER_URL "0/0000",
+ .response_content = serialized_shard,
+ .expected_spec = &empty_spec,
+ .expected_result = ResultCode::kErrorInvalidOrigin,
+ },
+ {
+ .test_name = "IP addresses give the empty spec",
+ .origin = "chrome://settings",
+ // By setting the prefix to 0, the URL of the shard is predefined,
+ // but actually, not network request should be sent as password
+ // requirements are not supported the chrome:// scheme.
+ .prefix_length = 0,
+ .requested_url = SERVER_URL "0/0000",
+ .response_content = serialized_shard,
+ .expected_spec = &empty_spec,
+ .expected_result = ResultCode::kErrorInvalidOrigin,
+ },
+ {
+ .test_name = "Trailing period is normalized",
+ // Despite the trailing '.', everything is like for example.com
+ .origin = "https://www.example.com.",
+ .requested_url = SERVER_URL "1/5aba",
+ .response_content = serialized_shard,
+ .expected_spec = &success_spec_for_example_com,
+ .expected_result = ResultCode::kFoundSpec,
+ },
+ {
+ .test_name = "Test punycoding",
+ .origin = "https://www.über-example.com",
+ // See echo -n xn--ber-example-shb.com | md5sum | cut -b 1-4
+ .requested_url = SERVER_URL "1/e5db",
+ .response_content = serialized_shard,
+ .expected_spec = &success_spec_for_uber_example_com,
+ .expected_result = ResultCode::kFoundSpec,
+ },
+ {
+ .test_name = "Test no entry",
+ .origin = "https://www.no-entry.com",
+ // See echo -n no-entry.com | md5sum | cut -b 1-4
+ .requested_url = SERVER_URL "1/7579",
+ .response_content = serialized_shard,
+ .expected_spec = &empty_spec,
+ .expected_result = ResultCode::kFoundNoSpec,
+ },
+ };
+
+ for (const auto& test : tests) {
+ SCOPED_TRACE(test.test_name);
+ base::HistogramTester histogram_tester;
+
+ base::test::ScopedTaskEnvironment environment(
+ base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME);
+ network::TestURLLoaderFactory loader_factory;
+ loader_factory.AddResponse(test.requested_url, test.response_content,
+ test.response_status);
+
+ // Data to be collected from the callback.
+ bool callback_called = false;
+ PasswordRequirementsSpec returned_spec;
+
+ // Trigger the network request and record data of the callback.
+ PasswordRequirementsSpecFetcherImpl fetcher(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &loader_factory),
+ test.generation, test.prefix_length, test.timeout);
+ auto callback =
+ base::BindLambdaForTesting([&](const PasswordRequirementsSpec& spec) {
+ callback_called = true;
+ returned_spec = spec;
+ });
+ fetcher.Fetch(GURL(test.origin), callback);
+
+ environment.RunUntilIdle();
+
+ if (test.timeout == kMagicTimeout) {
+ // Make sure that the request takes longer than the timeout and gets
+ // killed by the timer.
+ environment.FastForwardBy(
+ base::TimeDelta::FromMilliseconds(2 * kMagicTimeout));
+ environment.RunUntilIdle();
+ }
+
+ ASSERT_TRUE(callback_called);
+ EXPECT_EQ(test.expected_spec->SerializeAsString(),
+ returned_spec.SerializeAsString());
+ histogram_tester.ExpectUniqueSample(
+ "PasswordManager.RequirementsSpecFetcher.Result", test.expected_result,
+ 1u);
+ }
+}
+
+// Test two requests to fetch the same shard before the network responded.
+// In this case, only one network request should be sent.
+TEST(PasswordRequirementsSpecFetcherTest, FetchDataInterleaved) {
+ for (bool simulate_timeout : {false, true}) {
+ SCOPED_TRACE(::testing::Message()
+ << "Simulate timeout? " << simulate_timeout);
+
+ // Set up the data that will be served.
+ std::string serialized_shard;
+ PasswordRequirementsShard shard;
+ (*shard.mutable_specs())["a.com"].set_min_length(17);
+ (*shard.mutable_specs())["b.com"].set_min_length(18);
+ shard.SerializeToString(&serialized_shard);
+
+ base::test::ScopedTaskEnvironment environment(
+ base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME);
+ network::TestURLLoaderFactory loader_factory;
+
+ // Target into which data will be written by the callback.
+ PasswordRequirementsSpec spec_for_a;
+ PasswordRequirementsSpec spec_for_b;
+ // Set some values to see whether they get overridden by the callback.
+ spec_for_a.set_min_length(1);
+ spec_for_b.set_min_length(1);
+
+ const int kVersion = 1;
+ // With a prefix length of 0, we guarantee that only one shard exists
+ // and therefore that the requests go to the same endpoint and can be
+ // unified into one network request.
+ const size_t kPrefixLength = 0;
+ const int kTimeout = 1000;
+ PasswordRequirementsSpecFetcherImpl fetcher(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &loader_factory),
+ kVersion, kPrefixLength, kTimeout);
+
+ fetcher.Fetch(
+ GURL("http://a.com"),
+ base::BindLambdaForTesting(
+ [&](const PasswordRequirementsSpec& spec) { spec_for_a = spec; }));
+ fetcher.Fetch(
+ GURL("http://b.com"),
+ base::BindLambdaForTesting(
+ [&](const PasswordRequirementsSpec& spec) { spec_for_b = spec; }));
+
+ EXPECT_EQ(1, loader_factory.NumPending());
+
+ if (simulate_timeout) {
+ environment.FastForwardBy(
+ base::TimeDelta::FromMilliseconds(2 * kTimeout));
+ environment.RunUntilIdle();
+ EXPECT_FALSE(spec_for_a.has_min_length());
+ EXPECT_FALSE(spec_for_b.has_min_length());
+ } else {
+ loader_factory.AddResponse(SERVER_URL "1/0000", serialized_shard,
+ net::HTTP_OK);
+ environment.RunUntilIdle();
+
+ EXPECT_EQ(17u, spec_for_a.min_length());
+ EXPECT_EQ(18u, spec_for_b.min_length());
+ }
+ }
+}
+
+// In case of incognito mode, we won't have a URL loader factory.
+// Test that an empty spec is returned by the spec fetcher in this case.
+TEST(PasswordRequirementsSpecFetcherTest, FetchDataWithoutURLLoaderFactory) {
+ base::test::ScopedTaskEnvironment environment;
+
+ // Target into which data will be written by the callback.
+ PasswordRequirementsSpec received_spec;
+ // Set some values to see whether they get overridden by the callback.
+ received_spec.set_min_length(1);
+
+ const int kVersion = 1;
+ const size_t kPrefixLength = 0;
+ const int kTimeout = 1000;
+ PasswordRequirementsSpecFetcherImpl fetcher(nullptr, kVersion, kPrefixLength,
+ kTimeout);
+
+ fetcher.Fetch(
+ GURL("http://a.com"),
+ base::BindLambdaForTesting(
+ [&](const PasswordRequirementsSpec& spec) { received_spec = spec; }));
+ environment.RunUntilIdle();
+ EXPECT_FALSE(received_spec.has_min_length());
+}
+
+#undef SERVER_URL
+
+} // namespace
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/password_requirements_spec_printer.cc b/chromium/components/autofill/core/browser/password_requirements_spec_printer.cc
new file mode 100644
index 00000000000..7a4d72eeb3b
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_requirements_spec_printer.cc
@@ -0,0 +1,44 @@
+// 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/password_requirements_spec_printer.h"
+
+std::ostream& operator<<(
+ std::ostream& out,
+ const autofill::PasswordRequirementsSpec::CharacterClass& character_class) {
+ out << "{";
+ if (character_class.has_character_set())
+ out << "character_set: \"" << character_class.character_set() << "\", ";
+ if (character_class.has_min())
+ out << "min: " << character_class.min() << ", ";
+ if (character_class.has_max())
+ out << "max: " << character_class.max() << ", ";
+ out << "}";
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out,
+ const autofill::PasswordRequirementsSpec& spec) {
+ out << "{";
+ if (spec.has_priority())
+ out << "priority: " << spec.priority() << ", ";
+ if (spec.has_spec_version())
+ out << "spec_version: " << spec.spec_version() << ", ";
+ if (spec.has_min_length())
+ out << "min_length: " << spec.min_length() << ", ";
+ if (spec.has_max_length())
+ out << "max_length: " << spec.max_length() << ", ";
+ if (spec.has_lower_case())
+ out << "lower_case: " << spec.lower_case() << ", ";
+ if (spec.has_upper_case())
+ out << "upper_case: " << spec.upper_case() << ", ";
+ if (spec.has_alphabetic())
+ out << "alphabetic: " << spec.alphabetic() << ", ";
+ if (spec.has_numeric())
+ out << "numeric: " << spec.alphabetic() << ", ";
+ if (spec.has_symbols())
+ out << "symbols: " << spec.symbols() << ", ";
+ out << "}";
+ return out;
+}
diff --git a/chromium/components/autofill/core/browser/password_requirements_spec_printer.h b/chromium/components/autofill/core/browser/password_requirements_spec_printer.h
new file mode 100644
index 00000000000..55db287068c
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_requirements_spec_printer.h
@@ -0,0 +1,19 @@
+// 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_PASSWORD_REQUIREMENTS_SPEC_PRINTER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_REQUIREMENTS_SPEC_PRINTER_H_
+
+#include <ostream>
+
+#include "components/autofill/core/browser/proto/password_requirements.pb.h"
+
+std::ostream& operator<<(
+ std::ostream& out,
+ const autofill::PasswordRequirementsSpec::CharacterClass& character_class);
+
+std::ostream& operator<<(std::ostream& out,
+ const autofill::PasswordRequirementsSpec& spec);
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_REQUIREMENTS_SPEC_PRINTER_H_
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 3f95ca3a104..03d1b3eda65 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
@@ -22,6 +22,9 @@
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "net/url_request/url_request_test_util.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_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -68,14 +71,17 @@ class FullCardRequestTest : public testing::Test,
public:
FullCardRequestTest()
: request_context_(new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get())) {
+ base::ThreadTaskRunnerHandle::Get())),
+ test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)) {
std::unique_ptr<TestingPrefServiceSimple> pref_service(
new TestingPrefServiceSimple());
pref_service->registry()->RegisterDoublePref(
prefs::kAutofillBillingCustomerNumber, 0.0);
autofill_client_.SetPrefs(std::move(pref_service));
payments_client_ = std::make_unique<PaymentsClient>(
- request_context_.get(), autofill_client_.GetPrefs(),
+ test_shared_loader_factory_, autofill_client_.GetPrefs(),
autofill_client_.GetIdentityManager(), this, nullptr);
request_ = std::make_unique<FullCardRequest>(
&autofill_client_, payments_client_.get(), &personal_data_);
@@ -112,6 +118,8 @@ class FullCardRequestTest : public testing::Test,
MockUIDelegate ui_delegate_;
TestAutofillClient autofill_client_;
scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
std::unique_ptr<PaymentsClient> payments_client_;
std::unique_ptr<FullCardRequest> request_;
diff --git a/chromium/components/autofill/core/browser/payments/payments_client.cc b/chromium/components/autofill/core/browser/payments/payments_client.cc
index 0c84d91a47d..6785c51868f 100644
--- a/chromium/components/autofill/core/browser/payments/payments_client.cc
+++ b/chromium/components/autofill/core/browser/payments/payments_client.cc
@@ -30,9 +30,11 @@
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.h"
#include "services/identity/public/cpp/identity_manager.h"
+#include "services/identity/public/cpp/primary_account_access_token_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"
namespace autofill {
namespace payments {
@@ -292,19 +294,18 @@ class GetUploadDetailsRequest : public PaymentsRequest {
// 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).
- // The final parameter directs BuildAddressDictionary to omit names and
- // phone numbers, which aren't useful for these purposes.
+ // 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.Set("address", std::move(addresses));
- // If the "send detected values" experiment is enabled, it's possible we may
- // not have found name/address/CVC. The detected_values_ bitmask tells
- // Payments what was found, and Payments will decide if the provided data is
- // enough to offer upload save.
- if (IsAutofillUpstreamSendDetectedValuesExperimentEnabled())
- request_dict.SetInteger("detected_values", detected_values_);
+ // 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.SetInteger("detected_values", detected_values_);
if (IsAutofillUpstreamSendPanFirstSixExperimentEnabled() &&
!pan_first_six_.empty())
@@ -454,13 +455,14 @@ PaymentsClient::UploadRequestDetails::UploadRequestDetails(
const UploadRequestDetails& other) = default;
PaymentsClient::UploadRequestDetails::~UploadRequestDetails() {}
-PaymentsClient::PaymentsClient(net::URLRequestContextGetter* context_getter,
- PrefService* pref_service,
- identity::IdentityManager* identity_manager,
- PaymentsClientUnmaskDelegate* unmask_delegate,
- PaymentsClientSaveDelegate* save_delegate,
- bool is_off_the_record)
- : context_getter_(context_getter),
+PaymentsClient::PaymentsClient(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ PrefService* pref_service,
+ identity::IdentityManager* identity_manager,
+ PaymentsClientUnmaskDelegate* unmask_delegate,
+ PaymentsClientSaveDelegate* save_delegate,
+ bool is_off_the_record)
+ : url_loader_factory_(url_loader_factory),
pref_service_(pref_service),
identity_manager_(identity_manager),
unmask_delegate_(unmask_delegate),
@@ -514,102 +516,73 @@ void PaymentsClient::UploadCard(
true);
}
+void PaymentsClient::CancelRequest() {
+ request_.reset();
+ resource_request_.reset();
+ simple_url_loader_.reset();
+ token_fetcher_.reset();
+ access_token_.clear();
+ has_retried_authorization_ = false;
+}
+
+void PaymentsClient::set_url_loader_factory_for_testing(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+ url_loader_factory_ = std::move(url_loader_factory);
+}
+
void PaymentsClient::IssueRequest(std::unique_ptr<PaymentsRequest> request,
bool authenticate) {
request_ = std::move(request);
has_retried_authorization_ = false;
- InitializeUrlFetcher();
- if (!authenticate)
- url_fetcher_->Start();
- else if (access_token_.empty())
+ InitializeResourceRequest();
+
+ if (!authenticate) {
+ StartRequest();
+ } else if (access_token_.empty()) {
StartTokenFetch(false);
- else
+ } else {
SetOAuth2TokenAndStartRequest();
+ }
}
-void PaymentsClient::InitializeUrlFetcher() {
- net::NetworkTrafficAnnotationTag traffic_annotation =
- net::DefineNetworkTrafficAnnotation("payments_sync_cards", R"(
- semantics {
- sender: "Payments"
- description:
- "This service communicates with Google Payments servers to upload "
- "(save) or receive the user's credit card info."
- trigger:
- "Requests are triggered by a user action, such as selecting a "
- "masked server card from Chromium's credit card autofill dropdown, "
- "submitting a form which has credit card information, or accepting "
- "the prompt to save a credit card to Payments servers."
- data:
- "In case of save, a protocol buffer containing relevant address "
- "and credit card information which should be saved in Google "
- "Payments servers, along with user credentials. In case of load, a "
- "protocol buffer containing the id of the credit card to unmask, "
- "an encrypted cvc value, an optional updated card expiration date, "
- "and user credentials."
- destination: GOOGLE_OWNED_SERVICE
- }
- policy {
- cookies_allowed: NO
- setting:
- "Users can enable or disable this feature in Chromium settings by "
- "toggling 'Credit cards and addresses using Google Payments', "
- "under 'Advanced sync settings...'. This feature is enabled by "
- "default."
- chrome_policy {
- AutoFillEnabled {
- policy_options {mode: MANDATORY}
- AutoFillEnabled: false
- }
- }
- })");
- url_fetcher_ =
- net::URLFetcher::Create(0, GetRequestUrl(request_->GetRequestUrlPath()),
- net::URLFetcher::POST, this, traffic_annotation);
-
- data_use_measurement::DataUseUserData::AttachToFetcher(
- url_fetcher_.get(), data_use_measurement::DataUseUserData::AUTOFILL);
- url_fetcher_->SetRequestContext(context_getter_.get());
- url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DISABLE_CACHE);
-
- url_fetcher_->SetUploadData(request_->GetRequestContentType(),
- request_->GetRequestContent());
-
+void PaymentsClient::InitializeResourceRequest() {
+ resource_request_ = std::make_unique<network::ResourceRequest>();
+ resource_request_->url = GetRequestUrl(request_->GetRequestUrlPath());
+ resource_request_->load_flags = net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DISABLE_CACHE;
+ resource_request_->method = "POST";
if (base::FeatureList::IsEnabled(
features::kAutofillSendExperimentIdsInPaymentsRPCs)) {
// Add Chrome experiment state to the request headers.
net::HttpRequestHeaders headers;
// User is always signed-in to be able to upload card to Google Payments.
- variations::AppendVariationHeaders(url_fetcher_->GetOriginalURL(),
- is_off_the_record_
- ? variations::InIncognito::kYes
- : variations::InIncognito::kNo,
- variations::SignedIn::kYes, &headers);
- url_fetcher_->SetExtraRequestHeaders(headers.ToString());
+ variations::AppendVariationHeaders(
+ resource_request_->url,
+ is_off_the_record_ ? variations::InIncognito::kYes
+ : variations::InIncognito::kNo,
+ variations::SignedIn::kYes, &resource_request_->headers);
}
}
-void PaymentsClient::CancelRequest() {
- request_.reset();
- url_fetcher_.reset();
- token_fetcher_.reset();
- access_token_.clear();
- has_retried_authorization_ = false;
+void PaymentsClient::OnSimpleLoaderComplete(
+ std::unique_ptr<std::string> response_body) {
+ int response_code = -1;
+ if (simple_url_loader_->ResponseInfo() &&
+ simple_url_loader_->ResponseInfo()->headers) {
+ response_code =
+ simple_url_loader_->ResponseInfo()->headers->response_code();
+ }
+ std::string data;
+ if (response_body)
+ data = std::move(*response_body);
+ OnSimpleLoaderCompleteInternal(response_code, data);
}
-void PaymentsClient::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK_EQ(source, url_fetcher_.get());
-
- // |url_fetcher_|, which is aliased to |source|, might continue to be used in
- // this method, but should be freed once control leaves the method.
- std::unique_ptr<net::URLFetcher> scoped_url_fetcher(std::move(url_fetcher_));
+void PaymentsClient::OnSimpleLoaderCompleteInternal(int response_code,
+ const std::string& data) {
std::unique_ptr<base::DictionaryValue> response_dict;
- int response_code = source->GetResponseCode();
- std::string data;
- source->GetResponseAsString(&data);
VLOG(2) << "Got data: " << data;
AutofillClient::PaymentsRpcResult result = AutofillClient::SUCCESS;
@@ -641,7 +614,7 @@ void PaymentsClient::OnURLFetchComplete(const net::URLFetcher* source) {
}
has_retried_authorization_ = true;
- InitializeUrlFetcher();
+ InitializeResourceRequest();
StartTokenFetch(true);
return;
}
@@ -669,30 +642,27 @@ void PaymentsClient::OnURLFetchComplete(const net::URLFetcher* source) {
}
void PaymentsClient::AccessTokenFetchFinished(
- const GoogleServiceAuthError& error,
- const std::string& access_token) {
- // Delete the fetcher only after we leave this method (which is called from
- // the fetcher itself).
+ GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info) {
DCHECK(token_fetcher_);
- std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher>
- token_fetcher_deleter(std::move(token_fetcher_));
+ token_fetcher_.reset();
if (error.state() != GoogleServiceAuthError::NONE) {
AccessTokenError(error);
return;
}
- access_token_ = access_token;
- if (url_fetcher_)
+ access_token_ = access_token_info.token;
+ if (resource_request_)
SetOAuth2TokenAndStartRequest();
}
void PaymentsClient::AccessTokenError(const GoogleServiceAuthError& error) {
VLOG(1) << "Unhandled OAuth2 error: " << error.ToString();
- if (url_fetcher_) {
- url_fetcher_.reset();
+ if (simple_url_loader_)
+ simple_url_loader_.reset();
+ if (request_)
request_->RespondToDelegate(AutofillClient::PERMANENT_FAILURE);
- }
}
void PaymentsClient::StartTokenFetch(bool invalidate_old) {
@@ -705,22 +675,74 @@ void PaymentsClient::StartTokenFetch(bool invalidate_old) {
if (invalidate_old) {
DCHECK(!access_token_.empty());
identity_manager_->RemoveAccessTokenFromCache(
- identity_manager_->GetPrimaryAccountInfo(), payments_scopes,
+ identity_manager_->GetPrimaryAccountInfo().account_id, payments_scopes,
access_token_);
}
access_token_.clear();
- token_fetcher_ = identity_manager_->CreateAccessTokenFetcherForPrimaryAccount(
- kTokenFetchId, payments_scopes,
+ token_fetcher_ = std::make_unique<identity::PrimaryAccountAccessTokenFetcher>(
+ kTokenFetchId, identity_manager_, payments_scopes,
base::BindOnce(&PaymentsClient::AccessTokenFetchFinished,
base::Unretained(this)),
identity::PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
}
void PaymentsClient::SetOAuth2TokenAndStartRequest() {
- url_fetcher_->AddExtraRequestHeader(net::HttpRequestHeaders::kAuthorization +
- std::string(": Bearer ") + access_token_);
+ DCHECK(resource_request_);
+ resource_request_->headers.AddHeaderFromString(
+ net::HttpRequestHeaders::kAuthorization + std::string(": Bearer ") +
+ access_token_);
+ StartRequest();
+}
- url_fetcher_->Start();
+void PaymentsClient::StartRequest() {
+ DCHECK(resource_request_);
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("payments_sync_cards", R"(
+ semantics {
+ sender: "Payments"
+ description:
+ "This service communicates with Google Payments servers to upload "
+ "(save) or receive the user's credit card info."
+ trigger:
+ "Requests are triggered by a user action, such as selecting a "
+ "masked server card from Chromium's credit card autofill dropdown, "
+ "submitting a form which has credit card information, or accepting "
+ "the prompt to save a credit card to Payments servers."
+ data:
+ "In case of save, a protocol buffer containing relevant address "
+ "and credit card information which should be saved in Google "
+ "Payments servers, along with user credentials. In case of load, a "
+ "protocol buffer containing the id of the credit card to unmask, "
+ "an encrypted cvc value, an optional updated card expiration date, "
+ "and user credentials."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: NO
+ setting:
+ "Users can enable or disable this feature in Chromium settings by "
+ "toggling 'Credit cards and addresses using Google Payments', "
+ "under 'Advanced sync settings...'. This feature is enabled by "
+ "default."
+ chrome_policy {
+ AutoFillEnabled {
+ policy_options {mode: MANDATORY}
+ AutoFillEnabled: false
+ }
+ }
+ })");
+ // TODO(https://crbug.com/808498): Re-add data use measurement once
+ // SimpleURLLoader supports it.
+ // ID=data_use_measurement::DataUseUserData::AUTOFILL
+ simple_url_loader_ = network::SimpleURLLoader::Create(
+ std::move(resource_request_), traffic_annotation);
+ simple_url_loader_->AttachStringForUpload(request_->GetRequestContent(),
+ request_->GetRequestContentType());
+
+ simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&PaymentsClient::OnSimpleLoaderComplete,
+ base::Unretained(this)));
}
} // namespace payments
diff --git a/chromium/components/autofill/core/browser/payments/payments_client.h b/chromium/components/autofill/core/browser/payments/payments_client.h
index d68fdf59e31..e8eef9d974b 100644
--- a/chromium/components/autofill/core/browser/payments/payments_client.h
+++ b/chromium/components/autofill/core/browser/payments/payments_client.h
@@ -7,6 +7,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/autofill_profile.h"
@@ -14,17 +15,18 @@
#include "components/autofill/core/browser/credit_card.h"
#include "components/prefs/pref_service.h"
#include "google_apis/gaia/google_service_auth_error.h"
-#include "net/url_request/url_fetcher_delegate.h"
+#include "services/identity/public/cpp/access_token_info.h"
namespace identity {
class IdentityManager;
class PrimaryAccountAccessTokenFetcher;
} // namespace identity
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
-}
+namespace network {
+struct ResourceRequest;
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
+} // namespace network
namespace autofill {
@@ -62,7 +64,7 @@ class PaymentsClientSaveDelegate {
// request will cancel a pending request.
// Tests are located in
// src/components/autofill/content/browser/payments/payments_client_unittest.cc.
-class PaymentsClient : public net::URLFetcherDelegate {
+class PaymentsClient {
public:
// The names of the fields used to send non-location elements as part of an
// address. Used in the implementation and in tests which verify that these
@@ -100,19 +102,20 @@ class PaymentsClient : public net::URLFetcherDelegate {
std::vector<const char*> active_experiments;
};
- // |context_getter| is reference counted so it has no lifetime or ownership
- // requirements. |pref_service| is used to get the registered preference
- // value, |identity_manager|, |unmask_delegate| and |save_delegate| must all
- // outlive |this|. Either delegate might be nullptr. |is_off_the_record|
- // denotes incognito mode.
- PaymentsClient(net::URLRequestContextGetter* context_getter,
- PrefService* pref_service,
- identity::IdentityManager* identity_manager,
- PaymentsClientUnmaskDelegate* unmask_delegate,
- PaymentsClientSaveDelegate* save_delegate,
- bool is_off_the_record = false);
-
- ~PaymentsClient() override;
+ // |url_loader_factory| is reference counted so it has no lifetime or
+ // ownership requirements. |pref_service| is used to get the registered
+ // preference value, |identity_manager|, |unmask_delegate| and |save_delegate|
+ // must all outlive |this|. Either delegate might be nullptr.
+ // |is_off_the_record| denotes incognito mode.
+ PaymentsClient(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ PrefService* pref_service,
+ identity::IdentityManager* identity_manager,
+ PaymentsClientUnmaskDelegate* unmask_delegate,
+ PaymentsClientSaveDelegate* save_delegate,
+ bool is_off_the_record = false);
+
+ virtual ~PaymentsClient();
// Starts fetching the OAuth2 token in anticipation of future Payments
// requests. Called as an optimization, but not strictly necessary. Should
@@ -124,7 +127,7 @@ class PaymentsClient : public net::URLFetcherDelegate {
// Sets up the |save_delegate_|. Necessary because CreditCardSaveManager
// requires PaymentsClient during initialization, so PaymentsClient can't
// start with save_delegate_ initialized.
- void SetSaveDelegate(PaymentsClientSaveDelegate* save_delegate);
+ virtual void SetSaveDelegate(PaymentsClientSaveDelegate* save_delegate);
PrefService* GetPrefService() const;
@@ -156,34 +159,45 @@ class PaymentsClient : public net::URLFetcherDelegate {
// Cancels and clears the current |request_|.
void CancelRequest();
+ // Exposed for testing.
+ void set_url_loader_factory_for_testing(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+
private:
+ friend class PaymentsClientTest;
+
// Initiates a Payments request using the state in |request|. If
// |authenticate| is true, ensures that an OAuth token is avialble first.
// Takes ownership of |request|.
void IssueRequest(std::unique_ptr<PaymentsRequest> request,
bool authenticate);
- // net::URLFetcherDelegate:
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ // Creates |resource_request_| to be used later in StartRequest().
+ void InitializeResourceRequest();
+
+ // Callback from |simple_url_loader_|.
+ void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
+ void OnSimpleLoaderCompleteInternal(int response_code,
+ const std::string& data);
// Callback that handles a completed access token request.
- void AccessTokenFetchFinished(const GoogleServiceAuthError& error,
- const std::string& access_token);
+ void AccessTokenFetchFinished(GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info);
// Handles a completed access token request in the case of failure.
void AccessTokenError(const GoogleServiceAuthError& error);
- // Creates |url_fetcher_| based on the current state of |request_|.
- void InitializeUrlFetcher();
-
// Initiates a new OAuth2 token request.
void StartTokenFetch(bool invalidate_old);
- // Adds the token to |url_fetcher_| and starts the request.
+ // Adds the token to |simple_url_loader_| and starts the request.
void SetOAuth2TokenAndStartRequest();
- // The context for the request.
- scoped_refptr<net::URLRequestContextGetter> context_getter_;
+ // Creates |simple_url_loader_| and calls it to start the request.
+ void StartRequest();
+
+ // The URL loader factory for the request.
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// The pref service for this client.
PrefService* const pref_service_;
@@ -198,8 +212,11 @@ class PaymentsClient : public net::URLFetcherDelegate {
// The current request.
std::unique_ptr<PaymentsRequest> request_;
- // The fetcher being used to issue the current request.
- std::unique_ptr<net::URLFetcher> url_fetcher_;
+ // The resource request being used to issue the current request.
+ std::unique_ptr<network::ResourceRequest> resource_request_;
+
+ // The URL loader being used to issue the current request.
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
// The current OAuth2 token fetcher.
std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher> token_fetcher_;
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 8fffad918d6..2d2027de3be 100644
--- a/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc
+++ b/chromium/components/autofill/core/browser/payments/payments_client_unittest.cc
@@ -8,6 +8,8 @@
#include "base/command_line.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -23,9 +25,10 @@
#include "components/prefs/testing_pref_service.h"
#include "components/variations/variations_associated_data.h"
#include "components/variations/variations_http_header_provider.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.h"
#include "services/identity/public/cpp/identity_test_environment.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace autofill {
@@ -62,26 +65,22 @@ class PaymentsClientTest : public testing::Test,
real_pan_.clear();
legal_message_.reset();
- request_context_ = new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get());
+ factory()->SetInterceptor(base::BindLambdaForTesting(
+ [&](const network::ResourceRequest& request) {
+ intercepted_headers_ = request.headers;
+ intercepted_body_ = network::GetUploadData(request);
+ }));
+ test_shared_loader_factory_ =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_);
TestingPrefServiceSimple pref_service_;
- client_.reset(new PaymentsClient(request_context_.get(), &pref_service_,
- identity_test_env_.identity_manager(),
- this, this));
+ client_.reset(
+ new PaymentsClient(test_shared_loader_factory_, &pref_service_,
+ identity_test_env_.identity_manager(), this, this));
}
void TearDown() override { client_.reset(); }
- void DisableAutofillUpstreamSendDetectedValuesExperiment() {
- scoped_feature_list_.InitAndDisableFeature(
- kAutofillUpstreamSendDetectedValues);
- }
-
- void EnableAutofillUpstreamSendDetectedValuesExperiment() {
- scoped_feature_list_.InitAndEnableFeature(
- kAutofillUpstreamSendDetectedValues);
- }
-
void EnableAutofillUpstreamSendPanFirstSixExperiment() {
scoped_feature_list_.InitAndEnableFeature(kAutofillUpstreamSendPanFirstSix);
}
@@ -163,9 +162,11 @@ class PaymentsClientTest : public testing::Test,
client_->UploadCard(request_details);
}
- const std::string& GetUploadData() {
- return factory_.GetFetcherByID(0)->upload_data();
- }
+ network::TestURLLoaderFactory* factory() { return &test_url_loader_factory_; }
+
+ const std::string& GetUploadData() { return intercepted_body_; }
+
+ net::HttpRequestHeaders* GetRequestHeaders() { return &intercepted_headers_; }
void IssueOAuthToken() {
identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
@@ -173,23 +174,16 @@ class PaymentsClientTest : public testing::Test,
base::Time::Now() + base::TimeDelta::FromDays(10));
// Verify the auth header.
- net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
- net::HttpRequestHeaders request_headers;
- fetcher->GetExtraRequestHeaders(&request_headers);
std::string auth_header_value;
- EXPECT_TRUE(request_headers.GetHeader(
+ EXPECT_TRUE(intercepted_headers_.GetHeader(
net::HttpRequestHeaders::kAuthorization, &auth_header_value))
- << request_headers.ToString();
+ << intercepted_headers_.ToString();
EXPECT_EQ("Bearer totally_real_token", auth_header_value);
}
void ReturnResponse(net::HttpStatusCode response_code,
const std::string& response_body) {
- net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
- ASSERT_TRUE(fetcher);
- fetcher->set_response_code(response_code);
- fetcher->SetResponseString(response_body);
- fetcher->delegate()->OnURLFetchComplete(fetcher);
+ client_->OnSimpleLoaderCompleteInternal(response_code, response_body);
}
AutofillClient::PaymentsRpcResult result_;
@@ -198,11 +192,14 @@ class PaymentsClientTest : public testing::Test,
std::unique_ptr<base::DictionaryValue> legal_message_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
- net::TestURLFetcherFactory factory_;
- scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
std::unique_ptr<PaymentsClient> client_;
identity::IdentityTestEnvironment identity_test_env_;
+ net::HttpRequestHeaders intercepted_headers_;
+ std::string intercepted_body_;
+
private:
DISALLOW_COPY_AND_ASSIGN(PaymentsClientTest);
@@ -248,6 +245,7 @@ TEST_F(PaymentsClientTest, OAuthError) {
TEST_F(PaymentsClientTest,
UnmaskRequestIncludesBillingCustomerNumberInRequest) {
StartUnmasking();
+ IssueOAuthToken();
// Verify that the billing customer number is included in the request.
EXPECT_TRUE(
@@ -295,10 +293,7 @@ TEST_F(PaymentsClientTest, GetDetailsRemovesNonLocationData) {
EXPECT_TRUE(GetUploadData().find("0090") == std::string::npos);
}
-TEST_F(PaymentsClientTest,
- GetDetailsIncludesDetectedValuesInRequestIfExperimentOn) {
- EnableAutofillUpstreamSendDetectedValuesExperiment();
-
+TEST_F(PaymentsClientTest, GetDetailsIncludesDetectedValuesInRequest) {
StartGettingUploadDetails();
// Verify that the detected values were included in the request.
@@ -308,19 +303,6 @@ TEST_F(PaymentsClientTest,
std::string::npos);
}
-TEST_F(PaymentsClientTest,
- GetDetailsDoesNotIncludeDetectedValuesInRequestIfExperimentOff) {
- DisableAutofillUpstreamSendDetectedValuesExperiment();
-
- StartGettingUploadDetails();
-
- // Verify that the detected values were left out of the request.
- std::string detected_values_string =
- "\"detected_values\":" + std::to_string(kAllDetectableValues);
- EXPECT_TRUE(GetUploadData().find(detected_values_string) ==
- std::string::npos);
-}
-
TEST_F(PaymentsClientTest, GetUploadDetailsVariationsTest) {
// Register a trial and variation id, so that there is data in variations
// headers. Also, the variations header provider may have been registered to
@@ -330,15 +312,10 @@ TEST_F(PaymentsClientTest, GetUploadDetailsVariationsTest) {
CreateFieldTrialWithId("AutofillTest", "Group", 369);
StartGettingUploadDetails();
- net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
- net::HttpRequestHeaders headers;
- fetcher->GetExtraRequestHeaders(&headers);
std::string value;
- EXPECT_TRUE(headers.GetHeader("X-Client-Data", &value));
+ EXPECT_TRUE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
// Note that experiment information is stored in X-Client-Data.
EXPECT_FALSE(value.empty());
- // The fetcher's delegate is responsible for freeing the fetcher (and itself).
- fetcher->delegate()->OnURLFetchComplete(fetcher);
variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
}
@@ -353,15 +330,10 @@ TEST_F(PaymentsClientTest, GetUploadDetailsVariationsTestExperimentFlagOff) {
CreateFieldTrialWithId("AutofillTest", "Group", 369);
StartGettingUploadDetails();
- net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
- net::HttpRequestHeaders headers;
- fetcher->GetExtraRequestHeaders(&headers);
std::string value;
- EXPECT_FALSE(headers.GetHeader("X-Client-Data", &value));
+ EXPECT_FALSE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
// Note that experiment information is stored in X-Client-Data.
EXPECT_TRUE(value.empty());
- // The fetcher's delegate is responsible for freeing the fetcher (and itself).
- fetcher->delegate()->OnURLFetchComplete(fetcher);
variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
}
@@ -374,16 +346,12 @@ TEST_F(PaymentsClientTest, UploadCardVariationsTest) {
base::FieldTrialList field_trial_list_(nullptr);
CreateFieldTrialWithId("AutofillTest", "Group", 369);
StartUploading(/*include_cvc=*/true);
+ IssueOAuthToken();
- net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
- net::HttpRequestHeaders headers;
- fetcher->GetExtraRequestHeaders(&headers);
std::string value;
- EXPECT_TRUE(headers.GetHeader("X-Client-Data", &value));
+ EXPECT_TRUE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
// Note that experiment information is stored in X-Client-Data.
EXPECT_FALSE(value.empty());
- // The fetcher's delegate is responsible for freeing the fetcher (and itself).
- fetcher->delegate()->OnURLFetchComplete(fetcher);
variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
}
@@ -398,15 +366,10 @@ TEST_F(PaymentsClientTest, UploadCardVariationsTestExperimentFlagOff) {
CreateFieldTrialWithId("AutofillTest", "Group", 369);
StartUploading(/*include_cvc=*/true);
- net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
- net::HttpRequestHeaders headers;
- fetcher->GetExtraRequestHeaders(&headers);
std::string value;
- EXPECT_FALSE(headers.GetHeader("X-Client-Data", &value));
+ EXPECT_FALSE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
// Note that experiment information is stored in X-Client-Data.
EXPECT_TRUE(value.empty());
- // The fetcher's delegate is responsible for freeing the fetcher (and itself).
- fetcher->delegate()->OnURLFetchComplete(fetcher);
variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
}
@@ -419,16 +382,12 @@ TEST_F(PaymentsClientTest, UnmaskCardVariationsTest) {
base::FieldTrialList field_trial_list_(nullptr);
CreateFieldTrialWithId("AutofillTest", "Group", 369);
StartUnmasking();
+ IssueOAuthToken();
- net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
- net::HttpRequestHeaders headers;
- fetcher->GetExtraRequestHeaders(&headers);
std::string value;
- EXPECT_TRUE(headers.GetHeader("X-Client-Data", &value));
+ EXPECT_TRUE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
// Note that experiment information is stored in X-Client-Data.
EXPECT_FALSE(value.empty());
- // The fetcher's delegate is responsible for freeing the fetcher (and itself).
- fetcher->delegate()->OnURLFetchComplete(fetcher);
variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
}
@@ -443,15 +402,10 @@ TEST_F(PaymentsClientTest, UnmaskCardVariationsTestExperimentOff) {
CreateFieldTrialWithId("AutofillTest", "Group", 369);
StartUnmasking();
- net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
- net::HttpRequestHeaders headers;
- fetcher->GetExtraRequestHeaders(&headers);
std::string value;
- EXPECT_FALSE(headers.GetHeader("X-Client-Data", &value));
+ EXPECT_FALSE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
// Note that experiment information is stored in X-Client-Data.
EXPECT_TRUE(value.empty());
- // The fetcher's delegate is responsible for freeing the fetcher (and itself).
- fetcher->delegate()->OnURLFetchComplete(fetcher);
variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
}
@@ -494,6 +448,7 @@ TEST_F(PaymentsClientTest, UploadSuccessWithServerId) {
TEST_F(PaymentsClientTest, UploadIncludesNonLocationData) {
StartUploading(/*include_cvc=*/true);
+ IssueOAuthToken();
// Verify that the recipient name field and test names do appear in the upload
// data.
@@ -518,6 +473,7 @@ TEST_F(PaymentsClientTest, UploadIncludesNonLocationData) {
TEST_F(PaymentsClientTest,
UploadRequestIncludesBillingCustomerNumberInRequest) {
StartUploading(/*include_cvc=*/true);
+ IssueOAuthToken();
// Verify that the billing customer number is included in the request.
EXPECT_TRUE(
@@ -527,6 +483,7 @@ TEST_F(PaymentsClientTest,
TEST_F(PaymentsClientTest, UploadIncludesCvcInRequestIfProvided) {
StartUploading(/*include_cvc=*/true);
+ IssueOAuthToken();
// Verify that the encrypted_cvc and s7e_13_cvc parameters were included in
// the request.
diff --git a/chromium/components/autofill/core/browser/payments/test_payments_client.cc b/chromium/components/autofill/core/browser/payments/test_payments_client.cc
index 845b84d895d..104864965a2 100644
--- a/chromium/components/autofill/core/browser/payments/test_payments_client.cc
+++ b/chromium/components/autofill/core/browser/payments/test_payments_client.cc
@@ -5,17 +5,18 @@
#include "components/autofill/core/browser/payments/test_payments_client.h"
#include "base/strings/utf_string_conversions.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace autofill {
namespace payments {
TestPaymentsClient::TestPaymentsClient(
- net::URLRequestContextGetter* context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_,
PrefService* pref_service,
identity::IdentityManager* identity_manager,
payments::PaymentsClientUnmaskDelegate* unmask_delegate,
payments::PaymentsClientSaveDelegate* save_delegate)
- : PaymentsClient(context_getter,
+ : PaymentsClient(url_loader_factory_,
pref_service,
identity_manager,
unmask_delegate,
@@ -30,6 +31,7 @@ void TestPaymentsClient::GetUploadDetails(
const std::string& pan_first_six,
const std::vector<const char*>& active_experiments,
const std::string& app_locale) {
+ upload_details_addresses_ = addresses;
detected_values_ = detected_values;
pan_first_six_ = pan_first_six;
active_experiments_ = active_experiments;
@@ -56,18 +58,5 @@ void TestPaymentsClient::SetServerIdForCardUpload(std::string server_id) {
server_id_ = server_id;
}
-int TestPaymentsClient::GetDetectedValuesSetInRequest() const {
- return detected_values_;
-}
-
-std::string TestPaymentsClient::GetPanFirstSixSetInRequest() const {
- return pan_first_six_;
-}
-
-std::vector<const char*> TestPaymentsClient::GetActiveExperimentsSetInRequest()
- const {
- return active_experiments_;
-}
-
} // namespace payments
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/payments/test_payments_client.h b/chromium/components/autofill/core/browser/payments/test_payments_client.h
index f8b0f4a8392..13d24e010ba 100644
--- a/chromium/components/autofill/core/browser/payments/test_payments_client.h
+++ b/chromium/components/autofill/core/browser/payments/test_payments_client.h
@@ -10,16 +10,21 @@
#include "components/autofill/core/browser/payments/payments_client.h"
+namespace network {
+class SharedURLLoaderFactory;
+} // namespace network
+
namespace autofill {
namespace payments {
class TestPaymentsClient : public payments::PaymentsClient {
public:
- TestPaymentsClient(net::URLRequestContextGetter* context_getter,
- PrefService* pref_service,
- identity::IdentityManager* identity_manager,
- payments::PaymentsClientUnmaskDelegate* unmask_delegate,
- payments::PaymentsClientSaveDelegate* save_delegate);
+ TestPaymentsClient(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_,
+ PrefService* pref_service,
+ identity::IdentityManager* identity_manager,
+ payments::PaymentsClientUnmaskDelegate* unmask_delegate,
+ payments::PaymentsClientSaveDelegate* save_delegate);
~TestPaymentsClient() override;
@@ -32,17 +37,24 @@ class TestPaymentsClient : public payments::PaymentsClient {
void UploadCard(const payments::PaymentsClient::UploadRequestDetails&
request_details) override;
- void SetSaveDelegate(payments::PaymentsClientSaveDelegate* save_delegate);
+ void SetSaveDelegate(
+ payments::PaymentsClientSaveDelegate* save_delegate) override;
void SetServerIdForCardUpload(std::string);
- int GetDetectedValuesSetInRequest() const;
- std::string GetPanFirstSixSetInRequest() const;
- std::vector<const char*> GetActiveExperimentsSetInRequest() const;
+ int detected_values_in_upload_details() const { return detected_values_; }
+ const std::vector<AutofillProfile>& addresses_in_upload_details() const {
+ return upload_details_addresses_;
+ }
+ std::string pan_first_six_in_upload_details() const { return pan_first_six_; }
+ const std::vector<const char*>& active_experiments_in_request() const {
+ return active_experiments_;
+ }
private:
payments::PaymentsClientSaveDelegate* save_delegate_;
std::string server_id_;
+ std::vector<AutofillProfile> upload_details_addresses_;
int detected_values_;
std::string pan_first_six_;
std::vector<const char*> active_experiments_;
diff --git a/chromium/components/autofill/core/browser/personal_data_manager.cc b/chromium/components/autofill/core/browser/personal_data_manager.cc
index 1b683f584ec..a53c36fb032 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager.cc
+++ b/chromium/components/autofill/core/browser/personal_data_manager.cc
@@ -46,7 +46,6 @@
#include "components/prefs/pref_service.h"
#include "components/sync/driver/sync_service.h"
#include "components/sync/driver/sync_service_utils.h"
-#include "components/variations/variations_associated_data.h"
#include "components/version_info/version_info.h"
#include "services/identity/public/cpp/identity_manager.h"
#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
@@ -348,12 +347,116 @@ CreditCard CreateDisusedDeletableTestCreditCard(const std::string& locale) {
} // namespace
-const char kFrecencyFieldTrialName[] = "AutofillProfileOrderByFrecency";
-const char kFrecencyFieldTrialLimitParam[] = "limit";
+// Helper class to abstract the switching between account and profile storage
+// for server cards away from the rest of PersonalDataManager.
+class PersonalDatabaseHelper
+ : public AutofillWebDataServiceObserverOnUISequence {
+ public:
+ PersonalDatabaseHelper(PersonalDataManager* personal_data_manager)
+ : personal_data_manager_(personal_data_manager) {}
+
+ void Init(scoped_refptr<AutofillWebDataService> profile_database,
+ scoped_refptr<AutofillWebDataService> account_database) {
+ profile_database_ = profile_database;
+ account_database_ = account_database;
+
+ if (!profile_database_) {
+ // In some tests, there are no dbs.
+ return;
+ }
+
+ // Start observing the profile database. Don't observe the account database
+ // until we know that we should use it.
+ profile_database_->AddObserver(personal_data_manager_);
+
+ // If we don't have an account_database , we always use the profile database
+ // for server data.
+ if (!account_database_) {
+ server_database_ = profile_database_;
+ } else {
+ // Wait for the call to SetUseAccountStorageForServerCards to decide
+ // which database to use for server cards.
+ server_database_ = nullptr;
+ }
+ }
+
+ ~PersonalDatabaseHelper() override {
+ if (profile_database_) {
+ profile_database_->RemoveObserver(personal_data_manager_);
+ }
+
+ // If we have a different server database, also remove its observer.
+ if (server_database_ && server_database_ != profile_database_) {
+ server_database_->RemoveObserver(personal_data_manager_);
+ }
+ }
+
+ // Returns the database that should be used for storing local data.
+ // Until server addresses are using the server database, this should also
+ // be used for server addresses.
+ scoped_refptr<AutofillWebDataService> GetLocalDatabase() {
+ return profile_database_;
+ }
+
+ // Returns the database that should be used for storing server data.
+ // Until server addresses are using the server database, this should *not*
+ // be used for server addresses.
+ scoped_refptr<AutofillWebDataService> GetServerDatabase() {
+ return server_database_;
+ }
+
+ // Set whether this should use the passed in account storage for server
+ // addresses. If false, this will use the profile_storage.
+ // It's an error to call this if no account storage was passed in at
+ // construction time.
+ void SetUseAccountStorageForServerCards(
+ bool use_account_storage_for_server_cards) {
+ if (!profile_database_) {
+ // In some tests, there are no dbs.
+ return;
+ }
+ scoped_refptr<AutofillWebDataService> new_server_database =
+ use_account_storage_for_server_cards ? account_database_
+ : profile_database_;
+ DCHECK(new_server_database != nullptr)
+ << "SetUseAccountStorageForServerCards("
+ << use_account_storage_for_server_cards << "): storage not available.";
+
+ if (new_server_database == server_database_) {
+ // Nothing to do :)
+ return;
+ }
+
+ if (server_database_ != nullptr && server_database_ != profile_database_) {
+ // Remove the previous observer if we had any.
+ server_database_->RemoveObserver(personal_data_manager_);
+ personal_data_manager_->CancelPendingServerQueries();
+ }
+ server_database_ = new_server_database;
+ // We don't need to add an observer if server_database_ is equal to
+ // profile_database_, because we're already observing that.
+ if (server_database_ != profile_database_) {
+ server_database_->AddObserver(personal_data_manager_);
+ }
+ // Notify the manager that the database changed.
+ personal_data_manager_->Refresh();
+ }
+
+ private:
+ scoped_refptr<AutofillWebDataService> profile_database_;
+ scoped_refptr<AutofillWebDataService> account_database_;
+
+ // The database that should be used for server data. This will always be equal
+ // to either profile_database_, or account_database_.
+ scoped_refptr<AutofillWebDataService> server_database_;
+
+ PersonalDataManager* personal_data_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(PersonalDatabaseHelper);
+};
PersonalDataManager::PersonalDataManager(const std::string& app_locale)
- : database_(nullptr),
- is_data_loaded_(false),
+ : is_data_loaded_(false),
pending_profiles_query_(0),
pending_server_profiles_query_(0),
pending_creditcards_query_(0),
@@ -364,15 +467,18 @@ PersonalDataManager::PersonalDataManager(const std::string& app_locale)
sync_service_(nullptr),
is_off_the_record_(false),
has_logged_stored_profile_metrics_(false),
- has_logged_stored_credit_card_metrics_(false) {}
+ has_logged_stored_credit_card_metrics_(false) {
+ database_helper_ = std::make_unique<PersonalDatabaseHelper>(this);
+}
-void PersonalDataManager::Init(scoped_refptr<AutofillWebDataService> database,
- PrefService* pref_service,
- identity::IdentityManager* identity_manager,
- bool is_off_the_record) {
+void PersonalDataManager::Init(
+ scoped_refptr<AutofillWebDataService> profile_database,
+ scoped_refptr<AutofillWebDataService> account_database,
+ PrefService* pref_service,
+ identity::IdentityManager* identity_manager,
+ bool is_off_the_record) {
CountryNames::SetLocaleString(app_locale_);
-
- database_ = database;
+ database_helper_->Init(profile_database, account_database);
SetPrefService(pref_service);
identity_manager_ = identity_manager;
is_off_the_record_ = is_off_the_record;
@@ -381,13 +487,12 @@ void PersonalDataManager::Init(scoped_refptr<AutofillWebDataService> database,
AutofillMetrics::LogIsAutofillEnabledAtStartup(IsAutofillEnabled());
// WebDataService may not be available in tests.
- if (!database_)
+ if (!database_helper_->GetLocalDatabase()) {
return;
-
+ }
LoadProfiles();
LoadCreditCards();
- database_->AddObserver(this);
// Check if profile cleanup has already been performed this major version.
is_autofill_profile_cleanup_pending_ =
@@ -400,13 +505,10 @@ void PersonalDataManager::Init(scoped_refptr<AutofillWebDataService> database,
}
PersonalDataManager::~PersonalDataManager() {
- CancelPendingQuery(&pending_profiles_query_);
- CancelPendingQuery(&pending_server_profiles_query_);
- CancelPendingQuery(&pending_creditcards_query_);
- CancelPendingQuery(&pending_server_creditcards_query_);
-
- if (database_)
- database_->RemoveObserver(this);
+ CancelPendingLocalQuery(&pending_profiles_query_);
+ CancelPendingLocalQuery(&pending_server_profiles_query_);
+ CancelPendingLocalQuery(&pending_creditcards_query_);
+ CancelPendingServerQuery(&pending_server_creditcards_query_);
}
void PersonalDataManager::Shutdown() {
@@ -418,18 +520,6 @@ void PersonalDataManager::Shutdown() {
void PersonalDataManager::OnSyncServiceInitialized(
syncer::SyncService* sync_service) {
- // If the sync service is not enabled for autofill address profiles then run
- // address cleanup/startup code here. Otherwise, defer until after sync has
- // started.
- if (!IsSyncEnabledFor(sync_service, syncer::AUTOFILL_PROFILE))
- ApplyAddressFixesAndCleanups();
-
- // Similarly, if the sync service is not enabled for autofill credit cards
- // then run credit card address cleanup/startup code here. Otherwise, defer
- // until after sync has started.
- if (!IsSyncEnabledFor(sync_service, syncer::AUTOFILL_WALLET_DATA))
- ApplyCardFixesAndCleanups();
-
if (sync_service_ != sync_service) {
// Before the sync service pointer gets changed, remove the observer.
if (sync_service_)
@@ -466,6 +556,12 @@ void PersonalDataManager::OnSyncServiceInitialized(
}
}
+void PersonalDataManager::SetUseAccountStorageForServerCards(
+ bool use_account_storage_for_server_cards) {
+ database_helper_->SetUseAccountStorageForServerCards(
+ use_account_storage_for_server_cards);
+}
+
void PersonalDataManager::OnWebDataServiceRequestDone(
WebDataServiceBase::Handle h,
std::unique_ptr<WDTypedResult> result) {
@@ -489,6 +585,8 @@ void PersonalDataManager::OnWebDataServiceRequestDone(
ReceiveLoadedDbValues(h, result.get(), &pending_profiles_query_,
&web_profiles_);
} else {
+ DCHECK_EQ(h, pending_server_profiles_query_)
+ << "received profiles from invalid request.";
ReceiveLoadedDbValues(h, result.get(),
&pending_server_profiles_query_,
&server_profiles_);
@@ -499,6 +597,8 @@ void PersonalDataManager::OnWebDataServiceRequestDone(
ReceiveLoadedDbValues(h, result.get(), &pending_creditcards_query_,
&local_credit_cards_);
} else {
+ DCHECK_EQ(h, pending_server_creditcards_query_)
+ << "received creditcards from invalid request.";
ReceiveLoadedDbValues(h, result.get(),
&pending_server_creditcards_query_,
&server_credit_cards_);
@@ -515,12 +615,30 @@ void PersonalDataManager::OnWebDataServiceRequestDone(
}
// If all requests have responded, then all personal data is loaded.
+ // We need to check if the server database is set here, because we won't have
+ // the server cards yet if we don't have the database.
if (pending_profiles_query_ == 0 && pending_creditcards_query_ == 0 &&
pending_server_profiles_query_ == 0 &&
- pending_server_creditcards_query_ == 0) {
+ pending_server_creditcards_query_ == 0 &&
+ database_helper_->GetServerDatabase()) {
+ // On initial data load, is_data_loaded_ will be false here.
+ if (!is_data_loaded_) {
+ // If sync is enabled for addresses, defer running cleanups until address
+ // sync has started; otherwise, do it now.
+ if (!IsSyncEnabledFor(sync_service_, syncer::AUTOFILL_PROFILE))
+ ApplyAddressFixesAndCleanups();
+
+ // If sync is enabled for credit cards, defer running cleanups until card
+ // sync has started; otherwise, do it now.
+ if (!IsSyncEnabledFor(sync_service_, syncer::AUTOFILL_WALLET_DATA))
+ ApplyCardFixesAndCleanups();
+
+ // Log address and credit card startup metrics.
+ LogStoredProfileMetrics();
+ LogStoredCreditCardMetrics();
+ }
+
is_data_loaded_ = true;
- LogStoredProfileMetrics();
- LogStoredCreditCardMetrics();
NotifyPersonalDataChanged();
}
}
@@ -560,6 +678,12 @@ void PersonalDataManager::OnStateChanged(syncer::SyncService* sync_service) {
}
}
+void PersonalDataManager::OnSyncShutdown(syncer::SyncService* sync_service) {
+ DCHECK_EQ(sync_service_, sync_service);
+ sync_service_->RemoveObserver(this);
+ sync_service_ = nullptr;
+}
+
void PersonalDataManager::AddObserver(PersonalDataManagerObserver* observer) {
observers_.AddObserver(observer);
}
@@ -575,17 +699,25 @@ void PersonalDataManager::MarkObserversInsufficientFormDataForImport() {
}
void PersonalDataManager::RecordUseOf(const AutofillDataModel& data_model) {
- if (is_off_the_record_ || !database_)
+ if (is_off_the_record_)
return;
CreditCard* credit_card = GetCreditCardByGUID(data_model.guid());
if (credit_card) {
credit_card->RecordAndLogUse();
- if (credit_card->record_type() == CreditCard::LOCAL_CARD)
- database_->UpdateCreditCard(*credit_card);
- else
- database_->UpdateServerCardMetadata(*credit_card);
+ if (credit_card->record_type() == CreditCard::LOCAL_CARD) {
+ // Fail silently if there's no local database, because we need to support
+ // this for tests.
+ if (database_helper_->GetLocalDatabase()) {
+ database_helper_->GetLocalDatabase()->UpdateCreditCard(*credit_card);
+ }
+ } else {
+ DCHECK(database_helper_->GetServerDatabase())
+ << "Recording use of server card without server storage.";
+ database_helper_->GetServerDatabase()->UpdateServerCardMetadata(
+ *credit_card);
+ }
Refresh();
return;
@@ -595,16 +727,23 @@ void PersonalDataManager::RecordUseOf(const AutofillDataModel& data_model) {
if (profile) {
profile->RecordAndLogUse();
- if (profile->record_type() == AutofillProfile::LOCAL_PROFILE)
- database_->UpdateAutofillProfile(*profile);
- else if (profile->record_type() == AutofillProfile::SERVER_PROFILE)
- database_->UpdateServerAddressMetadata(*profile);
+ if (profile->record_type() == AutofillProfile::LOCAL_PROFILE) {
+ database_helper_->GetLocalDatabase()->UpdateAutofillProfile(*profile);
+ } else if (profile->record_type() == AutofillProfile::SERVER_PROFILE) {
+ // TODO(crbug.com/864519): Update this once addresses support account
+ // storage, and also use the server database.
+ database_helper_->GetLocalDatabase()->UpdateServerAddressMetadata(
+ *profile);
+ }
Refresh();
}
}
void PersonalDataManager::AddProfile(const AutofillProfile& profile) {
+ if (!IsAutofillProfileEnabled())
+ return;
+
if (is_off_the_record_)
return;
@@ -615,7 +754,7 @@ void PersonalDataManager::AddProfile(const AutofillProfile& profile) {
if (FindByGUID<AutofillProfile>(web_profiles_, profile.guid()))
return;
- if (!database_)
+ if (!database_helper_->GetLocalDatabase())
return;
// Don't add a duplicate.
@@ -623,7 +762,7 @@ void PersonalDataManager::AddProfile(const AutofillProfile& profile) {
return;
// Add the new profile to the web database.
- database_->AddAutofillProfile(profile);
+ database_helper_->GetLocalDatabase()->AddAutofillProfile(profile);
// Refresh our local cache and send notifications to observers.
Refresh();
@@ -646,11 +785,11 @@ void PersonalDataManager::UpdateProfile(const AutofillProfile& profile) {
return;
}
- if (!database_)
+ if (!database_helper_->GetLocalDatabase())
return;
// Make the update.
- database_->UpdateAutofillProfile(profile);
+ database_helper_->GetLocalDatabase()->UpdateAutofillProfile(profile);
// Refresh our local cache and send notifications to observers.
Refresh();
@@ -671,6 +810,9 @@ AutofillProfile* PersonalDataManager::GetProfileFromProfilesByGUID(
}
void PersonalDataManager::AddCreditCard(const CreditCard& credit_card) {
+ if (!IsAutofillCreditCardEnabled())
+ return;
+
if (is_off_the_record_)
return;
@@ -680,7 +822,7 @@ void PersonalDataManager::AddCreditCard(const CreditCard& credit_card) {
if (FindByGUID<CreditCard>(local_credit_cards_, credit_card.guid()))
return;
- if (!database_)
+ if (!database_helper_->GetLocalDatabase())
return;
// Don't add a duplicate.
@@ -688,7 +830,7 @@ void PersonalDataManager::AddCreditCard(const CreditCard& credit_card) {
return;
// Add the new credit card to the web database.
- database_->AddCreditCard(credit_card);
+ database_helper_->GetLocalDatabase()->AddCreditCard(credit_card);
// Refresh our local cache and send notifications to observers.
Refresh();
@@ -715,11 +857,11 @@ void PersonalDataManager::UpdateCreditCard(const CreditCard& credit_card) {
// Update the cached version.
*existing_credit_card = credit_card;
- if (!database_)
+ if (!database_helper_->GetLocalDatabase())
return;
// Make the update.
- database_->UpdateCreditCard(credit_card);
+ database_helper_->GetLocalDatabase()->UpdateCreditCard(credit_card);
// Refresh our local cache and send notifications to observers.
Refresh();
@@ -731,16 +873,19 @@ void PersonalDataManager::AddFullServerCreditCard(
DCHECK(!credit_card.IsEmpty(app_locale_));
DCHECK(!credit_card.server_id().empty());
- if (is_off_the_record_ || !database_)
+ if (is_off_the_record_)
return;
+ DCHECK(database_helper_->GetServerDatabase())
+ << "Adding server card without server storage.";
+
// Don't add a duplicate.
if (FindByGUID<CreditCard>(server_credit_cards_, credit_card.guid()) ||
FindByContents(server_credit_cards_, credit_card))
return;
// Add the new credit card to the web database.
- database_->AddFullServerCreditCard(credit_card);
+ database_helper_->GetServerDatabase()->AddFullServerCreditCard(credit_card);
// Refresh our local cache and send notifications to observers.
Refresh();
@@ -750,7 +895,7 @@ void PersonalDataManager::UpdateServerCreditCard(
const CreditCard& credit_card) {
DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type());
- if (is_off_the_record_ || !database_)
+ if (is_off_the_record_ || !database_helper_->GetServerDatabase())
return;
// Look up by server id, not GUID.
@@ -767,9 +912,11 @@ void PersonalDataManager::UpdateServerCreditCard(
DCHECK_NE(existing_credit_card->record_type(), credit_card.record_type());
DCHECK_EQ(existing_credit_card->Label(), credit_card.Label());
if (existing_credit_card->record_type() == CreditCard::MASKED_SERVER_CARD) {
- database_->UnmaskServerCreditCard(credit_card, credit_card.number());
+ database_helper_->GetServerDatabase()->UnmaskServerCreditCard(
+ credit_card, credit_card.number());
} else {
- database_->MaskServerCreditCard(credit_card.server_id());
+ database_helper_->GetServerDatabase()->MaskServerCreditCard(
+ credit_card.server_id());
}
Refresh();
@@ -779,10 +926,13 @@ void PersonalDataManager::UpdateServerCardMetadata(
const CreditCard& credit_card) {
DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type());
- if (is_off_the_record_ || !database_)
+ if (is_off_the_record_)
return;
- database_->UpdateServerCardMetadata(credit_card);
+ DCHECK(database_helper_->GetServerDatabase())
+ << "Updating server card metadata without server storage.";
+
+ database_helper_->GetServerDatabase()->UpdateServerCardMetadata(credit_card);
Refresh();
}
@@ -828,7 +978,17 @@ void PersonalDataManager::ClearAllServerData() {
// database on startup, and it could get called when the wallet pref is
// off (meaning this class won't even query for the server data) so don't
// check the server_credit_cards_/profiles_ before posting to the DB.
- database_->ClearAllServerData();
+ DCHECK(database_helper_->GetServerDatabase())
+ << "Updating server card metadata without server storage.";
+
+ database_helper_->GetServerDatabase()->ClearAllServerData();
+
+ // TODO(crbug.com/864519): Remove this call once addresses support account
+ // storage, and also use the database_helper_->GetServerDatabase()
+ if (database_helper_->GetServerDatabase() !=
+ database_helper_->GetLocalDatabase()) {
+ database_helper_->GetLocalDatabase()->ClearAllServerData();
+ }
// The above call will eventually clear our server data by notifying us
// that the data changed and then this class will re-fetch. Preemptively
@@ -837,6 +997,12 @@ void PersonalDataManager::ClearAllServerData() {
server_profiles_.clear();
}
+void PersonalDataManager::ClearAllLocalData() {
+ database_helper_->GetLocalDatabase()->ClearAllLocalData();
+ local_credit_cards_.clear();
+ web_profiles_.clear();
+}
+
void PersonalDataManager::AddServerCreditCardForTest(
std::unique_ptr<CreditCard> credit_card) {
server_credit_cards_.push_back(std::move(credit_card));
@@ -856,7 +1022,7 @@ void PersonalDataManager::SetSyncServiceForTest(
void PersonalDataManager::
RemoveAutofillProfileByGUIDAndBlankCreditCardReferecne(
const std::string& guid) {
- database_->RemoveAutofillProfile(guid);
+ database_helper_->GetLocalDatabase()->RemoveAutofillProfile(guid);
// Reset the billing_address_id of any card that refered to this profile.
for (CreditCard* credit_card : GetCreditCards()) {
@@ -864,9 +1030,13 @@ void PersonalDataManager::
credit_card->set_billing_address_id("");
if (credit_card->record_type() == CreditCard::LOCAL_CARD)
- database_->UpdateCreditCard(*credit_card);
- else
- database_->UpdateServerCardMetadata(*credit_card);
+ database_helper_->GetLocalDatabase()->UpdateCreditCard(*credit_card);
+ else {
+ DCHECK(database_helper_->GetServerDatabase())
+ << "Updating metadata on null server db.";
+ database_helper_->GetServerDatabase()->UpdateServerCardMetadata(
+ *credit_card);
+ }
}
}
}
@@ -881,11 +1051,11 @@ void PersonalDataManager::RemoveByGUID(const std::string& guid) {
if (!is_credit_card && !is_profile)
return;
- if (!database_)
+ if (!database_helper_->GetLocalDatabase())
return;
if (is_credit_card) {
- database_->RemoveCreditCard(guid);
+ database_helper_->GetLocalDatabase()->RemoveCreditCard(guid);
} else {
RemoveAutofillProfileByGUIDAndBlankCreditCardReferecne(guid);
}
@@ -935,6 +1105,8 @@ std::vector<AutofillProfile*> PersonalDataManager::GetProfiles() const {
std::vector<AutofillProfile*> PersonalDataManager::GetServerProfiles() const {
std::vector<AutofillProfile*> result;
+ if (!IsAutofillProfileEnabled())
+ return result;
result.reserve(server_profiles_.size());
for (const auto& profile : server_profiles_)
result.push_back(profile.get());
@@ -943,9 +1115,6 @@ std::vector<AutofillProfile*> PersonalDataManager::GetServerProfiles() const {
std::vector<CreditCard*> PersonalDataManager::GetLocalCreditCards() const {
std::vector<CreditCard*> result;
- if (!IsAutofillCreditCardEnabled())
- return result;
-
result.reserve(local_credit_cards_.size());
for (const auto& card : local_credit_cards_)
result.push_back(card.get());
@@ -954,7 +1123,7 @@ std::vector<CreditCard*> PersonalDataManager::GetLocalCreditCards() const {
std::vector<CreditCard*> PersonalDataManager::GetServerCreditCards() const {
std::vector<CreditCard*> result;
- if (!IsAutofillCreditCardEnabled() || !IsAutofillWalletImportEnabled())
+ if (!IsAutofillWalletImportEnabled())
return result;
result.reserve(server_credit_cards_.size());
@@ -965,8 +1134,6 @@ std::vector<CreditCard*> PersonalDataManager::GetServerCreditCards() const {
std::vector<CreditCard*> PersonalDataManager::GetCreditCards() const {
std::vector<CreditCard*> result;
- if (!IsAutofillCreditCardEnabled())
- return result;
result.reserve(local_credit_cards_.size() + server_credit_cards_.size());
for (const auto& card : local_credit_cards_)
@@ -985,6 +1152,9 @@ void PersonalDataManager::Refresh() {
std::vector<AutofillProfile*> PersonalDataManager::GetProfilesToSuggest()
const {
+ if (!IsAutofillProfileEnabled())
+ return std::vector<AutofillProfile*>{};
+
std::vector<AutofillProfile*> profiles = GetProfiles();
// Rank the suggestions by frecency (see AutofillDataModel for details).
@@ -1145,18 +1315,6 @@ std::vector<Suggestion> PersonalDataManager::GetProfileSuggestions(
for (size_t i = 0; i < labels.size(); i++)
unique_suggestions[i].label = labels[i];
- // Get the profile suggestions limit value set for the current frecency field
- // trial group or SIZE_MAX if no limit is defined.
- std::string limit_str = variations::GetVariationParamValue(
- kFrecencyFieldTrialName, kFrecencyFieldTrialLimitParam);
- size_t limit = SIZE_MAX;
- // Reassign SIZE_MAX to |limit| is needed after calling base::StringToSizeT,
- // as this method can modify |limit| even if it returns false.
- if (!base::StringToSizeT(limit_str, &limit))
- limit = SIZE_MAX;
-
- unique_suggestions.resize(std::min(unique_suggestions.size(), limit));
-
return unique_suggestions;
}
@@ -1164,6 +1322,9 @@ std::vector<Suggestion> PersonalDataManager::GetProfileSuggestions(
// with a vector instead of a list.
const std::vector<CreditCard*> PersonalDataManager::GetCreditCardsToSuggest(
bool include_server_cards) const {
+ if (!IsAutofillCreditCardEnabled())
+ return std::vector<CreditCard*>{};
+
std::vector<CreditCard*> credit_cards;
if (include_server_cards && ShouldSuggestServerCards()) {
credit_cards = GetCreditCards();
@@ -1240,6 +1401,10 @@ bool PersonalDataManager::IsAutofillEnabled() const {
return ::autofill::IsAutofillEnabled(pref_service_);
}
+bool PersonalDataManager::IsAutofillProfileEnabled() const {
+ return pref_service_->GetBoolean(prefs::kAutofillProfileEnabled);
+}
+
bool PersonalDataManager::IsAutofillCreditCardEnabled() const {
return pref_service_->GetBoolean(prefs::kAutofillCreditCardEnabled);
}
@@ -1248,6 +1413,25 @@ bool PersonalDataManager::IsAutofillWalletImportEnabled() const {
return pref_service_->GetBoolean(prefs::kAutofillWalletImportEnabled);
}
+bool PersonalDataManager::ShouldSuggestServerCards() const {
+ if (!IsAutofillWalletImportEnabled())
+ return false;
+
+ if (is_syncing_for_test_)
+ return true;
+
+ // Check if the feature to offer server cards on auth error is enabled.
+ if (base::FeatureList::IsEnabled(
+ features::kAutofillEnablePaymentsInteractionsOnAuthError)) {
+ return true;
+ }
+
+ // Server cards should be suggested if the sync service active.
+ return syncer::GetUploadToGoogleState(
+ sync_service_, syncer::ModelType::AUTOFILL_WALLET_DATA) ==
+ syncer::UploadState::ACTIVE;
+}
+
std::string PersonalDataManager::CountryCodeForCurrentTimezone() const {
return base::CountryCodeForCurrentTimezone();
}
@@ -1255,6 +1439,7 @@ std::string PersonalDataManager::CountryCodeForCurrentTimezone() const {
void PersonalDataManager::SetPrefService(PrefService* pref_service) {
enabled_pref_ = std::make_unique<BooleanPrefMember>();
wallet_enabled_pref_ = std::make_unique<BooleanPrefMember>();
+ profile_enabled_pref_ = std::make_unique<BooleanPrefMember>();
credit_card_enabled_pref_ = std::make_unique<BooleanPrefMember>();
pref_service_ = pref_service;
// |pref_service_| can be nullptr in tests.
@@ -1262,6 +1447,10 @@ void PersonalDataManager::SetPrefService(PrefService* pref_service) {
credit_card_enabled_pref_->Init(
prefs::kAutofillCreditCardEnabled, pref_service_,
base::Bind(&PersonalDataManager::Refresh, base::Unretained(this)));
+ profile_enabled_pref_->Init(
+ prefs::kAutofillProfileEnabled, pref_service_,
+ base::BindRepeating(&PersonalDataManager::Refresh,
+ base::Unretained(this)));
enabled_pref_->Init(prefs::kAutofillEnabled, pref_service_,
base::Bind(&PersonalDataManager::EnabledPrefChanged,
base::Unretained(this)));
@@ -1278,7 +1467,7 @@ void PersonalDataManager::ClearProfileNonSettingsOrigins() {
for (AutofillProfile* profile : GetProfiles()) {
if (profile->origin() != kSettingsOrigin && !profile->origin().empty()) {
profile->set_origin(std::string());
- database_->UpdateAutofillProfile(*profile);
+ database_helper_->GetLocalDatabase()->UpdateAutofillProfile(*profile);
has_updated = true;
}
}
@@ -1295,7 +1484,7 @@ void PersonalDataManager::ClearCreditCardNonSettingsOrigins() {
for (CreditCard* card : GetLocalCreditCards()) {
if (card->origin() != kSettingsOrigin && !card->origin().empty()) {
card->set_origin(std::string());
- database_->UpdateCreditCard(*card);
+ database_helper_->GetLocalDatabase()->UpdateCreditCard(*card);
has_updated = true;
}
}
@@ -1449,27 +1638,27 @@ void PersonalDataManager::SetProfiles(std::vector<AutofillProfile>* profiles) {
IsEmptyFunctor<AutofillProfile>(app_locale_)),
profiles->end());
- if (!database_)
+ if (!database_helper_->GetLocalDatabase())
return;
// Any profiles that are not in the new profile list should be removed from
// the web database.
for (const auto& it : web_profiles_) {
if (!FindByGUID<AutofillProfile>(*profiles, it->guid()))
- database_->RemoveAutofillProfile(it->guid());
+ database_helper_->GetLocalDatabase()->RemoveAutofillProfile(it->guid());
}
// Update the web database with the existing profiles.
for (const AutofillProfile& it : *profiles) {
if (FindByGUID<AutofillProfile>(web_profiles_, it.guid()))
- database_->UpdateAutofillProfile(it);
+ database_helper_->GetLocalDatabase()->UpdateAutofillProfile(it);
}
// Add the new profiles to the web database. Don't add a duplicate.
for (const AutofillProfile& it : *profiles) {
if (!FindByGUID<AutofillProfile>(web_profiles_, it.guid()) &&
!FindByContents(web_profiles_, it))
- database_->AddAutofillProfile(it);
+ database_helper_->GetLocalDatabase()->AddAutofillProfile(it);
}
// Copy in the new profiles.
@@ -1492,27 +1681,27 @@ void PersonalDataManager::SetCreditCards(
IsEmptyFunctor<CreditCard>(app_locale_)),
credit_cards->end());
- if (!database_)
+ if (!database_helper_->GetLocalDatabase())
return;
// Any credit cards that are not in the new credit card list should be
// removed.
for (const auto& card : local_credit_cards_) {
if (!FindByGUID<CreditCard>(*credit_cards, card->guid()))
- database_->RemoveCreditCard(card->guid());
+ database_helper_->GetLocalDatabase()->RemoveCreditCard(card->guid());
}
// Update the web database with the existing credit cards.
for (const CreditCard& card : *credit_cards) {
if (FindByGUID<CreditCard>(local_credit_cards_, card.guid()))
- database_->UpdateCreditCard(card);
+ database_helper_->GetLocalDatabase()->UpdateCreditCard(card);
}
// Add the new credit cards to the web database. Don't add a duplicate.
for (const CreditCard& card : *credit_cards) {
if (!FindByGUID<CreditCard>(local_credit_cards_, card.guid()) &&
!FindByContents(local_credit_cards_, card))
- database_->AddCreditCard(card);
+ database_helper_->GetLocalDatabase()->AddCreditCard(card);
}
// Copy in the new credit cards.
@@ -1525,43 +1714,69 @@ void PersonalDataManager::SetCreditCards(
}
void PersonalDataManager::LoadProfiles() {
- if (!database_) {
+ if (!database_helper_->GetLocalDatabase()) {
NOTREACHED();
return;
}
- CancelPendingQuery(&pending_profiles_query_);
- CancelPendingQuery(&pending_server_profiles_query_);
+ CancelPendingLocalQuery(&pending_profiles_query_);
+ CancelPendingLocalQuery(&pending_server_profiles_query_);
- pending_profiles_query_ = database_->GetAutofillProfiles(this);
- pending_server_profiles_query_ = database_->GetServerProfiles(this);
+ pending_profiles_query_ =
+ database_helper_->GetLocalDatabase()->GetAutofillProfiles(this);
+ pending_server_profiles_query_ =
+ database_helper_->GetLocalDatabase()->GetServerProfiles(this);
}
void PersonalDataManager::LoadCreditCards() {
- if (!database_) {
+ if (!database_helper_->GetLocalDatabase()) {
NOTREACHED();
return;
}
- CancelPendingQuery(&pending_creditcards_query_);
- CancelPendingQuery(&pending_server_creditcards_query_);
+ CancelPendingLocalQuery(&pending_creditcards_query_);
+ CancelPendingServerQuery(&pending_server_creditcards_query_);
- pending_creditcards_query_ = database_->GetCreditCards(this);
- pending_server_creditcards_query_ = database_->GetServerCreditCards(this);
+ pending_creditcards_query_ =
+ database_helper_->GetLocalDatabase()->GetCreditCards(this);
+ if (database_helper_->GetServerDatabase()) {
+ pending_server_creditcards_query_ =
+ database_helper_->GetServerDatabase()->GetServerCreditCards(this);
+ }
+}
+
+void PersonalDataManager::CancelPendingLocalQuery(
+ WebDataServiceBase::Handle* handle) {
+ if (*handle) {
+ if (!database_helper_->GetLocalDatabase()) {
+ NOTREACHED();
+ return;
+ }
+ database_helper_->GetLocalDatabase()->CancelRequest(*handle);
+ }
+ *handle = 0;
}
-void PersonalDataManager::CancelPendingQuery(
+void PersonalDataManager::CancelPendingServerQuery(
WebDataServiceBase::Handle* handle) {
if (*handle) {
- if (!database_) {
+ if (!database_helper_->GetServerDatabase()) {
NOTREACHED();
return;
}
- database_->CancelRequest(*handle);
+ database_helper_->GetServerDatabase()->CancelRequest(*handle);
}
*handle = 0;
}
+void PersonalDataManager::CancelPendingServerQueries() {
+ if (pending_server_creditcards_query_) {
+ CancelPendingServerQuery(&pending_server_creditcards_query_);
+ }
+ // TODO(crbug.com/864519): also cancel the server addresses query once they
+ // use the account storage.
+}
+
std::string PersonalDataManager::SaveImportedProfile(
const AutofillProfile& imported_profile) {
if (is_off_the_record_)
@@ -1586,12 +1801,20 @@ void PersonalDataManager::NotifyPersonalDataChanged() {
}
}
-std::string PersonalDataManager::SaveImportedCreditCard(
+std::string PersonalDataManager::OnAcceptedLocalCreditCardSave(
const CreditCard& imported_card) {
DCHECK(!imported_card.number().empty());
if (is_off_the_record_)
return std::string();
+ if (imported_card.HasFirstAndLastName())
+ AutofillMetrics::LogSaveCardWithFirstAndLastNameComplete(/*is_local=*/true);
+
+ return SaveImportedCreditCard(imported_card);
+}
+
+std::string PersonalDataManager::SaveImportedCreditCard(
+ const CreditCard& imported_card) {
// Set to true if |imported_card| is merged into the credit card list.
bool merged = false;
@@ -1753,15 +1976,8 @@ std::vector<Suggestion> PersonalDataManager::GetSuggestionsForCards(
// cardholder name. The label should never repeat the value.
if (type.GetStorableType() == CREDIT_CARD_NUMBER) {
suggestion->value = credit_card->NetworkOrBankNameAndLastFourDigits();
- if (IsAutofillCreditCardLastUsedDateDisplayExperimentEnabled()) {
- suggestion->label =
- credit_card->GetLastUsedDateForDisplay(app_locale_);
- } else {
- suggestion->label = credit_card->GetInfo(
- AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale_);
- }
- if (IsAutofillCreditCardPopupLayoutExperimentEnabled())
- ModifyAutofillCreditCardSuggestion(suggestion);
+ suggestion->label = credit_card->GetInfo(
+ AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale_);
} else if (credit_card->number().empty()) {
if (type.GetStorableType() != CREDIT_CARD_NAME_FULL) {
suggestion->label = credit_card->GetInfo(
@@ -1796,10 +2012,10 @@ void PersonalDataManager::RemoveOrphanAutofillTableRows() {
if (pref_service_->GetBoolean(prefs::kAutofillOrphanRowsRemoved))
return;
- if (!database_)
+ if (!database_helper_->GetLocalDatabase())
return;
- database_->RemoveOrphanAutofillTableRows();
+ database_helper_->GetLocalDatabase()->RemoveOrphanAutofillTableRows();
// Set the pref so that this fix is never run again.
pref_service_->SetBoolean(prefs::kAutofillOrphanRowsRemoved, true);
@@ -1840,10 +2056,11 @@ bool PersonalDataManager::ApplyDedupingRoutine() {
for (const auto& profile : web_profiles_) {
// If the profile was set to be deleted, remove it from the database.
if (profiles_to_delete.count(profile.get())) {
- database_->RemoveAutofillProfile(profile->guid());
+ database_helper_->GetLocalDatabase()->RemoveAutofillProfile(
+ profile->guid());
} else {
// Otherwise, update the profile in the database.
- database_->UpdateAutofillProfile(*profile);
+ database_helper_->GetLocalDatabase()->UpdateAutofillProfile(*profile);
}
}
@@ -1979,9 +2196,10 @@ void PersonalDataManager::UpdateCardsBillingAddressReference(
// If the card was modified, apply the changes to the database.
if (was_modified) {
if (credit_card->record_type() == CreditCard::LOCAL_CARD)
- database_->UpdateCreditCard(*credit_card);
+ database_helper_->GetLocalDatabase()->UpdateCreditCard(*credit_card);
else
- database_->UpdateServerCardMetadata(*credit_card);
+ database_helper_->GetServerDatabase()->UpdateServerCardMetadata(
+ *credit_card);
}
}
}
@@ -1993,7 +2211,7 @@ void PersonalDataManager::ConvertWalletAddressesAndUpdateWalletCards() {
// server addresses have a chance to merge into the non-verified local
// profiles.
std::vector<AutofillProfile> local_profiles;
- for (AutofillProfile* existing_profile : GetProfilesToSuggest()) {
+ for (AutofillProfile* existing_profile : GetProfiles()) {
local_profiles.push_back(*existing_profile);
}
@@ -2049,7 +2267,8 @@ bool PersonalDataManager::ConvertWalletAddressesToLocalProfiles(
// Update the wallet addresses metadata to record the conversion.
wallet_address->set_has_converted(true);
- database_->UpdateServerAddressMetadata(*wallet_address);
+ database_helper_->GetLocalDatabase()->UpdateServerAddressMetadata(
+ *wallet_address);
has_converted_addresses = true;
}
@@ -2222,7 +2441,7 @@ bool PersonalDataManager::DeleteDisusedCreditCards() {
size_t num_deleted_cards = guid_to_delete.size();
for (auto const guid : guid_to_delete) {
- database_->RemoveCreditCard(guid);
+ database_helper_->GetLocalDatabase()->RemoveCreditCard(guid);
}
if (num_deleted_cards > 0) {
@@ -2247,6 +2466,7 @@ bool PersonalDataManager::IsAddressDeletable(
bool PersonalDataManager::DeleteDisusedAddresses() {
if (!base::FeatureList::IsEnabled(kAutofillDeleteDisusedAddresses)) {
+ DVLOG(1) << "Deletion is disabled";
return false;
}
@@ -2268,6 +2488,7 @@ bool PersonalDataManager::DeleteDisusedAddresses() {
// Early exit when there are no profiles.
if (profiles.empty()) {
+ DVLOG(1) << "There are no profiles";
return true;
}
@@ -2300,25 +2521,6 @@ bool PersonalDataManager::DeleteDisusedAddresses() {
return true;
}
-bool PersonalDataManager::ShouldSuggestServerCards() const {
- if (!IsAutofillWalletImportEnabled())
- return false;
-
- if (is_syncing_for_test_)
- return true;
-
- // Check if the feature to offer server cards on auth error is enabled.
- if (base::FeatureList::IsEnabled(
- features::kAutofillEnablePaymentsInteractionsOnAuthError)) {
- return true;
- }
-
- // Server cards should be suggested if the sync service active.
- return syncer::GetUploadToGoogleState(
- sync_service_, syncer::ModelType::AUTOFILL_WALLET_DATA) ==
- syncer::UploadState::ACTIVE;
-}
-
void PersonalDataManager::ApplyAddressFixesAndCleanups() {
RemoveOrphanAutofillTableRows(); // One-time fix, otherwise NOP.
ApplyDedupingRoutine(); // Once per major version, otherwise NOP.
diff --git a/chromium/components/autofill/core/browser/personal_data_manager.h b/chromium/components/autofill/core/browser/personal_data_manager.h
index 9753af37f2d..b79c908f5a0 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager.h
+++ b/chromium/components/autofill/core/browser/personal_data_manager.h
@@ -38,6 +38,7 @@ namespace autofill {
class AutofillInteractiveTest;
class PersonalDataManagerObserver;
class PersonalDataManagerFactory;
+class PersonalDatabaseHelper;
} // namespace autofill
namespace autofill_helper {
@@ -71,10 +72,16 @@ class PersonalDataManager : public KeyedService,
~PersonalDataManager() override;
// Kicks off asynchronous loading of profiles and credit cards.
- // |pref_service| must outlive this instance. |is_off_the_record| informs
- // this instance whether the user is currently operating in an off-the-record
+ // |profile_database| is a profile-scoped database that will be used to save
+ // local cards. |account_database| is scoped to the currently signed-in
+ // account, and is wiped on signout and browser exit. This can be a nullptr
+ // if personal_data_manager should use |profile_database| for all data.
+ // If passed in, the |account_database| is used by default for server cards.
+ // |pref_service| must outlive this instance. |is_off_the_record| informs this
+ // instance whether the user is currently operating in an off-the-record
// context.
- void Init(scoped_refptr<AutofillWebDataService> database,
+ void Init(scoped_refptr<AutofillWebDataService> profile_database,
+ scoped_refptr<AutofillWebDataService> account_database,
PrefService* pref_service,
identity::IdentityManager* identity_manager,
bool is_off_the_record);
@@ -86,6 +93,13 @@ class PersonalDataManager : public KeyedService,
// not be started, but it's preferences can be queried.
virtual void OnSyncServiceInitialized(syncer::SyncService* sync_service);
+ // Set whether this should use the passed in account storage for server
+ // cards. If false, this will use the profile_storage.
+ // It's an error to call this if no account storage was passed in at
+ // initialization time.
+ void SetUseAccountStorageForServerCards(
+ bool use_account_storage_for_server_cards);
+
// WebDataServiceConsumer:
void OnWebDataServiceRequestDone(
WebDataServiceBase::Handle h,
@@ -97,6 +111,7 @@ class PersonalDataManager : public KeyedService,
// SyncServiceObserver:
void OnStateChanged(syncer::SyncService* sync) override;
+ void OnSyncShutdown(syncer::SyncService* sync) override;
// Adds a listener to be notified of PersonalDataManager events.
virtual void AddObserver(PersonalDataManagerObserver* observer);
@@ -117,9 +132,10 @@ class PersonalDataManager : public KeyedService,
virtual std::string SaveImportedProfile(
const AutofillProfile& imported_profile);
- // Saves |imported_credit_card| to the WebDB if it exists. Returns the guid of
- // of the new or updated card, or the empty string if no card was saved.
- virtual std::string SaveImportedCreditCard(
+ // Called when the user accepts the prompt to save the credit card locally.
+ // Records some metrics and attempts to save the imported card. Returns the
+ // guid of the new or updated card, or the empty string if no card was saved.
+ std::string OnAcceptedLocalCreditCardSave(
const CreditCard& imported_credit_card);
// Adds |profile| to the web database.
@@ -171,6 +187,9 @@ class PersonalDataManager : public KeyedService,
// Deletes all server profiles and cards (both masked and unmasked).
void ClearAllServerData();
+ // Deletes all local profiles and cards.
+ virtual void ClearAllLocalData();
+
// Sets a server credit card for test.
void AddServerCreditCardForTest(std::unique_ptr<CreditCard> credit_card);
@@ -307,6 +326,9 @@ class PersonalDataManager : public KeyedService,
// Notifies test observers that personal data has changed.
void NotifyPersonalDataChangedForTest() { NotifyPersonalDataChanged(); }
+ // Cancels any pending queries to the server web database.
+ void CancelPendingServerQueries();
+
// This function assumes |credit_card| contains the full PAN. Returns |true|
// if the card number of |credit_card| is equal to any local card or any
// unmasked server card known by the browser, or |TypeAndLastFourDigits| of
@@ -377,9 +399,18 @@ class PersonalDataManager : public KeyedService,
PersonalDataManagerTest,
DeleteDisusedCreditCards_OnlyDeleteExpiredDisusedLocalCards);
FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
+ GetProfileSuggestions_ProfileAutofillDisabled);
+ FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
+ GetProfileSuggestions_NoProfilesLoadedIfDisabled);
+ FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
+ GetProfileSuggestions_NoProfilesAddedIfDisabled);
+ FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
GetCreditCardSuggestions_CreditCardAutofillDisabled);
FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
GetCreditCardSuggestions_NoCardsLoadedIfDisabled);
+ FRIEND_TEST_ALL_PREFIXES(
+ PersonalDataManagerTest,
+ GetCreditCardSuggestions_NoCreditCardsAddedIfDisabled);
FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
ClearProfileNonSettingsOrigins);
FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
@@ -426,9 +457,13 @@ class PersonalDataManager : public KeyedService,
// Loads the saved credit cards from the web database.
virtual void LoadCreditCards();
- // Cancels a pending query to the web database. |handle| is a pointer to the
- // query handle.
- void CancelPendingQuery(WebDataServiceBase::Handle* handle);
+ // Cancels a pending query to the local web database. |handle| is a pointer
+ // to the query handle.
+ void CancelPendingLocalQuery(WebDataServiceBase::Handle* handle);
+
+ // Cancels a pending query to the server web database. |handle| is a pointer
+ // to the query handle.
+ void CancelPendingServerQuery(WebDataServiceBase::Handle* handle);
// Notifies observers that personal data has changed.
void NotifyPersonalDataChanged();
@@ -444,12 +479,18 @@ class PersonalDataManager : public KeyedService,
// Returns the value of the AutofillEnabled pref.
virtual bool IsAutofillEnabled() const;
+ // Returns the value of the AutofillEnabled pref.
+ virtual bool IsAutofillProfileEnabled() const;
+
// Returns the value of the AutofillCreditCardEnabled pref.
virtual bool IsAutofillCreditCardEnabled() const;
// Returns the value of the AutofillWalletImportEnabled pref.
virtual bool IsAutofillWalletImportEnabled() const;
+ // Whether the server cards are enabled and should be suggested to the user.
+ virtual bool ShouldSuggestServerCards() const;
+
// Overrideable for testing.
virtual std::string CountryCodeForCurrentTimezone() const;
@@ -462,12 +503,8 @@ class PersonalDataManager : public KeyedService,
void ClearProfileNonSettingsOrigins();
void ClearCreditCardNonSettingsOrigins();
- void set_database(scoped_refptr<AutofillWebDataService> database) {
- database_ = database;
- }
-
- // The backing database that this PersonalDataManager uses.
- scoped_refptr<AutofillWebDataService> database_;
+ // Decides which database type to use for server and local cards.
+ std::unique_ptr<PersonalDatabaseHelper> database_helper_;
// True if personal data has been loaded from the web database.
bool is_data_loaded_;
@@ -500,6 +537,11 @@ class PersonalDataManager : public KeyedService,
base::ObserverList<PersonalDataManagerObserver> observers_;
private:
+ // Saves |imported_credit_card| to the WebDB if it exists. Returns the guid of
+ // the new or updated card, or the empty string if no card was saved.
+ virtual std::string SaveImportedCreditCard(
+ const CreditCard& imported_credit_card);
+
// Finds the country code that occurs most frequently among all profiles.
// Prefers verified profiles over unverified ones.
std::string MostCommonCountryCodeFromProfiles() const;
@@ -607,9 +649,6 @@ class PersonalDataManager : public KeyedService,
// manually using the UI.
void MaybeCreateTestCreditCards();
- // Whether the server cards are enabled and should be suggested to the user.
- bool ShouldSuggestServerCards() const;
-
// Applies various fixes and cleanups on autofill addresses.
void ApplyAddressFixesAndCleanups();
@@ -643,6 +682,9 @@ class PersonalDataManager : public KeyedService,
// An observer to listen for changes to prefs::kAutofillCreditCardEnabled.
std::unique_ptr<BooleanPrefMember> credit_card_enabled_pref_;
+ // An observer to listen for changes to prefs::kAutofillProfileEnabled.
+ std::unique_ptr<BooleanPrefMember> profile_enabled_pref_;
+
// An observer to listen for changes to prefs::kAutofillEnabled.
std::unique_ptr<BooleanPrefMember> enabled_pref_;
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 e718d54a6bf..ad871584de3 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -24,7 +24,7 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -50,7 +50,6 @@
#include "components/os_crypt/os_crypt_mocker.h"
#include "components/prefs/pref_service.h"
#include "components/sync/driver/sync_service_utils.h"
-#include "components/variations/variations_params_manager.h"
#include "components/webdata/common/web_data_service_base.h"
#include "components/webdata/common/web_database_service.h"
#include "services/identity/public/cpp/identity_test_environment.h"
@@ -113,23 +112,82 @@ void ExpectSameElements(const std::vector<T*>& expectations,
class PersonalDataManagerTestBase {
protected:
- PersonalDataManagerTestBase() : autofill_table_(nullptr) {}
+ PersonalDataManagerTestBase() : profile_autofill_table_(nullptr) {}
- void ResetPersonalDataManager(UserMode user_mode) {
+ void SetUpTest() {
+ OSCryptMocker::SetUp();
+ prefs_ = test::PrefServiceForTesting();
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ base::FilePath path = temp_dir_.GetPath().AppendASCII("TestWebDB");
+ profile_web_database_ =
+ new WebDatabaseService(path, base::ThreadTaskRunnerHandle::Get(),
+ base::ThreadTaskRunnerHandle::Get());
+
+ // Hacky: hold onto a pointer but pass ownership.
+ profile_autofill_table_ = new AutofillTable;
+ profile_web_database_->AddTable(
+ std::unique_ptr<WebDatabaseTable>(profile_autofill_table_));
+ profile_web_database_->LoadDatabase();
+ profile_database_service_ = new AutofillWebDataService(
+ profile_web_database_, base::ThreadTaskRunnerHandle::Get(),
+ base::ThreadTaskRunnerHandle::Get(),
+ WebDataServiceBase::ProfileErrorCallback());
+ profile_database_service_->Init();
+
+ account_web_database_ =
+ new WebDatabaseService(base::FilePath(WebDatabase::kInMemoryPath),
+ base::ThreadTaskRunnerHandle::Get(),
+ base::ThreadTaskRunnerHandle::Get());
+ account_autofill_table_ = new AutofillTable;
+ account_web_database_->AddTable(
+ std::unique_ptr<WebDatabaseTable>(account_autofill_table_));
+ account_web_database_->LoadDatabase();
+ account_database_service_ = new AutofillWebDataService(
+ account_web_database_, base::ThreadTaskRunnerHandle::Get(),
+ base::ThreadTaskRunnerHandle::Get(),
+ WebDataServiceBase::ProfileErrorCallback());
+ account_database_service_->Init();
+
+ test::DisableSystemServices(prefs_.get());
+ ResetPersonalDataManager(USER_MODE_NORMAL);
+
+ // Reset the deduping pref to its default value.
+ personal_data_->pref_service_->SetInteger(
+ prefs::kAutofillLastVersionDeduped, 0);
+ }
+
+ void TearDownTest() {
+ // Order of destruction is important as AutofillManager relies on
+ // PersonalDataManager to be around when it gets destroyed.
+ test::ReenableSystemServices();
+ OSCryptMocker::TearDown();
+ }
+
+ void ResetPersonalDataManager(UserMode user_mode,
+ bool use_account_server_storage) {
bool is_incognito = (user_mode == USER_MODE_INCOGNITO);
personal_data_.reset(new PersonalDataManager("en"));
personal_data_->Init(
- scoped_refptr<AutofillWebDataService>(autofill_database_service_),
+ scoped_refptr<AutofillWebDataService>(profile_database_service_),
+ use_account_server_storage
+ ? scoped_refptr<AutofillWebDataService>(account_database_service_)
+ : nullptr,
prefs_.get(), identity_test_env_.identity_manager(), is_incognito);
personal_data_->AddObserver(&personal_data_observer_);
personal_data_->OnSyncServiceInitialized(&sync_service_);
+ personal_data_->SetUseAccountStorageForServerCards(
+ use_account_server_storage);
// Verify that the web database has been updated and the notification sent.
EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
- .WillOnce(QuitMainMessageLoop());
+ .WillRepeatedly(QuitMainMessageLoop());
base::RunLoop().Run();
}
+ void ResetPersonalDataManager(UserMode user_mode) {
+ ResetPersonalDataManager(user_mode, /*use_account_server_storage=*/true);
+ }
+
void ResetProfiles() {
std::vector<AutofillProfile> empty_profiles;
personal_data_->SetProfiles(&empty_profiles);
@@ -284,10 +342,14 @@ class PersonalDataManagerTestBase {
EXPECT_EQ(1U, personal_data_->GetProfiles().size());
}
+ void SetServerCards(std::vector<CreditCard> server_cards) {
+ test::SetServerCreditCards(account_autofill_table_, server_cards);
+ }
+
// Verifies that the web database has been updated and the notification sent.
void WaitForOnPersonalDataChanged() {
EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
- .WillOnce(QuitMainMessageLoop());
+ .WillRepeatedly(QuitMainMessageLoop());
base::RunLoop().Run();
}
@@ -298,50 +360,21 @@ class PersonalDataManagerTestBase {
std::unique_ptr<PrefService> prefs_;
identity::IdentityTestEnvironment identity_test_env_;
TestSyncService sync_service_;
- scoped_refptr<AutofillWebDataService> autofill_database_service_;
- scoped_refptr<WebDatabaseService> web_database_;
- AutofillTable* autofill_table_; // weak ref
+ scoped_refptr<AutofillWebDataService> profile_database_service_;
+ scoped_refptr<AutofillWebDataService> account_database_service_;
+ scoped_refptr<WebDatabaseService> profile_web_database_;
+ scoped_refptr<WebDatabaseService> account_web_database_;
+ AutofillTable* profile_autofill_table_; // weak ref
+ AutofillTable* account_autofill_table_; // weak ref
PersonalDataLoadedObserverMock personal_data_observer_;
std::unique_ptr<PersonalDataManager> personal_data_;
-
- variations::testing::VariationParamsManager variation_params_;
};
class PersonalDataManagerTest : public PersonalDataManagerTestBase,
public testing::Test {
- void SetUp() override {
- OSCryptMocker::SetUp();
- prefs_ = test::PrefServiceForTesting();
- ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- base::FilePath path = temp_dir_.GetPath().AppendASCII("TestWebDB");
- web_database_ =
- new WebDatabaseService(path, base::ThreadTaskRunnerHandle::Get(),
- base::ThreadTaskRunnerHandle::Get());
+ void SetUp() override { SetUpTest(); }
- // 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(),
- WebDataServiceBase::ProfileErrorCallback());
- autofill_database_service_->Init();
-
- test::DisableSystemServices(prefs_.get());
- ResetPersonalDataManager(USER_MODE_NORMAL);
-
- // Reset the deduping pref to its default value.
- personal_data_->pref_service_->SetInteger(
- prefs::kAutofillLastVersionDeduped, 0);
- }
-
- void TearDown() override {
- // Order of destruction is important as AutofillManager relies on
- // PersonalDataManager to be around when it gets destroyed.
- test::ReenableSystemServices();
- OSCryptMocker::TearDown();
- }
+ void TearDown() override { TearDownTest(); }
};
TEST_F(PersonalDataManagerTest, AddProfile) {
@@ -904,7 +937,7 @@ TEST_F(PersonalDataManagerTest, RefuseToStoreFullCard) {
test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow",
"378282246310005" /* American Express */, "04",
"2999", "1");
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
personal_data_->Refresh();
WaitForOnPersonalDataChanged();
@@ -975,7 +1008,7 @@ TEST_F(PersonalDataManagerTest, UpdateServerCreditCards) {
"378282246310005" /* American Express */, "04",
"2999", "1");
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
personal_data_->Refresh();
WaitForOnPersonalDataChanged();
@@ -1030,7 +1063,7 @@ TEST_F(PersonalDataManagerTest, SavesServerCardType) {
server_cards.back().set_card_type(CreditCard::CARD_TYPE_DEBIT);
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
personal_data_->Refresh();
WaitForOnPersonalDataChanged();
auto cards = personal_data_->GetCreditCards();
@@ -1225,7 +1258,7 @@ TEST_F(PersonalDataManagerTest, Refresh) {
"joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5",
"Orlando", "FL", "32801", "US", "19482937549");
- autofill_database_service_->AddAutofillProfile(profile2);
+ profile_database_service_->AddAutofillProfile(profile2);
personal_data_->Refresh();
@@ -1237,8 +1270,8 @@ TEST_F(PersonalDataManagerTest, Refresh) {
profiles.push_back(&profile2);
ExpectSameElements(profiles, personal_data_->GetProfiles());
- autofill_database_service_->RemoveAutofillProfile(profile1.guid());
- autofill_database_service_->RemoveAutofillProfile(profile2.guid());
+ profile_database_service_->RemoveAutofillProfile(profile1.guid());
+ profile_database_service_->RemoveAutofillProfile(profile2.guid());
// Before telling the PDM to refresh, simulate an edit to one of the deleted
// profiles via a SetProfile update (this would happen if the Autofill window
@@ -1298,8 +1331,9 @@ TEST_F(PersonalDataManagerTest, SaveImportedProfileWithVerifiedData) {
<< "result = {" << *results[0] << "} | expected = {" << expected << "}";
}
-// Ensure that verified credit cards can be saved via SaveImportedCreditCard.
-TEST_F(PersonalDataManagerTest, SaveImportedCreditCardWithVerifiedData) {
+// Ensure that verified credit cards can be saved via
+// OnAcceptedLocalCreditCardSave.
+TEST_F(PersonalDataManagerTest, OnAcceptedLocalCreditCardSaveWithVerifiedData) {
// Start with a verified credit card.
CreditCard credit_card(base::GenerateGUID(), kSettingsOrigin);
test::SetCreditCardInfo(&credit_card, "Biggie Smalls",
@@ -1319,7 +1353,7 @@ TEST_F(PersonalDataManagerTest, SaveImportedCreditCardWithVerifiedData) {
base::ASCIIToUTF16("B. Small"));
EXPECT_TRUE(new_verified_card.IsVerified());
- personal_data_->SaveImportedCreditCard(new_verified_card);
+ personal_data_->OnAcceptedLocalCreditCardSave(new_verified_card);
WaitForOnPersonalDataChanged();
@@ -1489,7 +1523,7 @@ TEST_F(PersonalDataManagerTest, IncognitoReadOnly) {
bill_gates.SetRawInfo(CREDIT_CARD_NAME_FULL,
base::ASCIIToUTF16("Bill Gates"));
- personal_data_->SaveImportedCreditCard(bill_gates);
+ personal_data_->OnAcceptedLocalCreditCardSave(bill_gates);
ResetPersonalDataManager(USER_MODE_INCOGNITO);
EXPECT_EQ(base::ASCIIToUTF16("Steven"),
@@ -1710,15 +1744,18 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions_HideSubsets) {
}
TEST_F(PersonalDataManagerTest,
- GetProfileSuggestions_NoSubsetsCheckingIfTooManyProfiles) {
- AutofillProfile profile(base::GenerateGUID(), "https://www.example.com");
+ GetProfileSuggestions_NoDeduplicationIfTooManyProfiles) {
+ // De-duplication of suggestions takes noticeable time when there are more
+ // than 15 or so suggestions. In that case, Auofill just shows them all to
+ // the user.
+ AutofillProfile profile(base::GenerateGUID(), test::kEmptyOrigin);
test::SetProfileInfo(&profile, "Marion", "Mitchell", "Morrison",
"johnwayne@me.xyz", "Fox",
"123 Zoo St.\nSecond Line\nThird line", "unit 5",
"Hollywood", "CA", "91601", "US", "12345678910");
personal_data_->AddProfile(profile);
- // 31 profiles in a total, expecting no subset removing.
+ // 16 profiles in a total, expecting no de-duplication.
for (int i = 0; i < 15; i++) {
AutofillProfile profile_no_state = profile;
profile_no_state.set_guid(base::GenerateGUID());
@@ -1736,7 +1773,6 @@ TEST_F(PersonalDataManagerTest,
AutofillType(ADDRESS_HOME_STREET_ADDRESS), base::ASCIIToUTF16("123"),
false, types);
ASSERT_EQ(16U, suggestions.size());
- EXPECT_EQ(base::ASCIIToUTF16("Hollywood"), suggestions[0].label);
}
// Tests that GetProfileSuggestions orders its suggestions based on the frecency
@@ -1781,8 +1817,7 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions_Ranking) {
EXPECT_EQ(suggestions[2].value, base::ASCIIToUTF16("Marion3"));
}
-// Tests that GetProfileSuggestions returns all profiles suggestions by default
-// and only two if the appropriate field trial is set.
+// Tests that GetProfileSuggestions returns all profiles suggestions.
TEST_F(PersonalDataManagerTest, GetProfileSuggestions_NumberOfSuggestions) {
// Set up 3 different profiles.
AutofillProfile profile1(base::GenerateGUID(), test::kEmptyOrigin);
@@ -1813,46 +1848,6 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions_NumberOfSuggestions) {
AutofillType(NAME_FIRST), base::string16(), false,
std::vector<ServerFieldType>());
EXPECT_EQ(3U, suggestions.size());
-
- // Verify that only two profiles are suggested.
- variation_params_.SetVariationParams(kFrecencyFieldTrialName,
- {{kFrecencyFieldTrialLimitParam, "2"}});
-
- suggestions = personal_data_->GetProfileSuggestions(
- AutofillType(NAME_FIRST), base::string16(), false,
- std::vector<ServerFieldType>());
- EXPECT_EQ(2U, suggestions.size());
-}
-
-// Tests that GetProfileSuggestions returns the right number of profile
-// suggestions when the limit to three field trial is set and there are less
-// than three profiles.
-TEST_F(PersonalDataManagerTest,
- GetProfileSuggestions_LimitIsMoreThanProfileSuggestions) {
- variation_params_.SetVariationParams(kFrecencyFieldTrialName,
- {{kFrecencyFieldTrialLimitParam, "3"}});
-
- // Set up 2 different profiles.
- AutofillProfile profile1(base::GenerateGUID(), "https://www.example.com");
- test::SetProfileInfo(&profile1, "Marion1", "Mitchell", "Morrison",
- "johnwayne@me.xyz", "Fox",
- "123 Zoo St.\nSecond Line\nThird line", "unit 5",
- "Hollywood", "CA", "91601", "US", "12345678910");
- personal_data_->AddProfile(profile1);
-
- AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com");
- test::SetProfileInfo(&profile2, "Marion2", "Mitchell", "Morrison",
- "johnwayne@me.xyz", "Fox",
- "123 Zoo St.\nSecond Line\nThird line", "unit 5",
- "Hollywood", "CA", "91601", "US", "12345678910");
- personal_data_->AddProfile(profile2);
-
- ResetPersonalDataManager(USER_MODE_NORMAL);
-
- std::vector<Suggestion> suggestions = personal_data_->GetProfileSuggestions(
- AutofillType(NAME_FIRST), base::string16(), false,
- std::vector<ServerFieldType>());
- EXPECT_EQ(2U, suggestions.size());
}
// Tests that disused profiles are suppressed when supression is enabled and
@@ -1984,6 +1979,123 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions_InvalidData) {
}
}
+// Test that local and server profiles are not shown if
+// |kAutofillProfileEnabled| is set to |false|.
+TEST_F(PersonalDataManagerTest, GetProfileSuggestions_ProfileAutofillDisabled) {
+ ///////////////////////////////////////////////////////////////////////
+ // Setup.
+ ///////////////////////////////////////////////////////////////////////
+ const std::string kServerAddressId("server_address1");
+
+ // Add two different profiles, a local and a server one.
+ AutofillProfile local_profile(base::GenerateGUID(), test::kEmptyOrigin);
+ test::SetProfileInfo(&local_profile, "Josephine", "Alicia", "Saenz",
+ "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5",
+ "Orlando", "FL", "32801", "US", "19482937549");
+ personal_data_->AddProfile(local_profile);
+
+ // Add a different server profile.
+ std::vector<AutofillProfile> GetServerProfiles;
+ GetServerProfiles.push_back(
+ AutofillProfile(AutofillProfile::SERVER_PROFILE, kServerAddressId));
+ test::SetProfileInfo(&GetServerProfiles.back(), "John", "", "Doe", "",
+ "ACME Corp", "500 Oak View", "Apt 8", "Houston", "TX",
+ "77401", "US", "");
+ // Wallet only provides a full name, so the above first and last names
+ // will be ignored when the profile is written to the DB.
+ GetServerProfiles.back().SetRawInfo(NAME_FULL,
+ base::ASCIIToUTF16("John Doe"));
+ profile_autofill_table_->SetServerProfiles(GetServerProfiles);
+
+ // Disable Profile autofill.
+ personal_data_->pref_service_->SetBoolean(prefs::kAutofillProfileEnabled,
+ false);
+ personal_data_->Refresh();
+ WaitForOnPersonalDataChanged();
+ personal_data_->ConvertWalletAddressesAndUpdateWalletCards();
+
+ // Check that profiles were saved.
+ EXPECT_EQ(2U, personal_data_->GetProfiles().size());
+ // Expect no autofilled values or suggestions.
+ EXPECT_EQ(0U, personal_data_->GetProfilesToSuggest().size());
+
+ std::vector<Suggestion> suggestions = personal_data_->GetProfileSuggestions(
+ AutofillType(ADDRESS_HOME_STREET_ADDRESS), base::ASCIIToUTF16("123"),
+ false, std::vector<ServerFieldType>());
+ ASSERT_EQ(0U, suggestions.size());
+}
+
+// Test that local and server profiles are not loaded into memory on start-up if
+// |kAutofillProfileEnabled| is set to |false|.
+TEST_F(PersonalDataManagerTest,
+ GetProfileSuggestions_NoProfilesLoadedIfDisabled) {
+ ///////////////////////////////////////////////////////////////////////
+ // Setup.
+ ///////////////////////////////////////////////////////////////////////
+ const std::string kServerAddressId("server_address1");
+
+ // Add two different profiles, a local and a server one.
+ AutofillProfile local_profile(base::GenerateGUID(), test::kEmptyOrigin);
+ test::SetProfileInfo(&local_profile, "Josephine", "Alicia", "Saenz",
+ "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5",
+ "Orlando", "FL", "32801", "US", "19482937549");
+ personal_data_->AddProfile(local_profile);
+
+ // Add a different server profile.
+ std::vector<AutofillProfile> GetServerProfiles;
+ GetServerProfiles.push_back(
+ AutofillProfile(AutofillProfile::SERVER_PROFILE, kServerAddressId));
+ test::SetProfileInfo(&GetServerProfiles.back(), "John", "", "Doe", "",
+ "ACME Corp", "500 Oak View", "Apt 8", "Houston", "TX",
+ "77401", "US", "");
+ // Wallet only provides a full name, so the above first and last names
+ // will be ignored when the profile is written to the DB.
+ GetServerProfiles.back().SetRawInfo(NAME_FULL,
+ base::ASCIIToUTF16("John Doe"));
+ profile_autofill_table_->SetServerProfiles(GetServerProfiles);
+
+ personal_data_->Refresh();
+ WaitForOnPersonalDataChanged();
+ personal_data_->ConvertWalletAddressesAndUpdateWalletCards();
+
+ // Expect 2 autofilled values or suggestions.
+ EXPECT_EQ(2U, personal_data_->GetProfiles().size());
+ EXPECT_EQ(2U, personal_data_->GetProfilesToSuggest().size());
+
+ // Disable CProfile autofill.
+ personal_data_->pref_service_->SetBoolean(prefs::kAutofillProfileEnabled,
+ false);
+ // Reload the database.
+ ResetPersonalDataManager(USER_MODE_NORMAL);
+
+ // Expect no profile values or suggestions were loaded.
+ EXPECT_EQ(0U, personal_data_->GetProfilesToSuggest().size());
+
+ std::vector<Suggestion> suggestions = personal_data_->GetProfileSuggestions(
+ AutofillType(ADDRESS_HOME_STREET_ADDRESS), base::ASCIIToUTF16("123"),
+ false, std::vector<ServerFieldType>());
+ ASSERT_EQ(0U, suggestions.size());
+}
+
+// Test that local profiles are not added if |kAutofillProfileEnabled| is set to
+// |false|.
+TEST_F(PersonalDataManagerTest,
+ GetProfileSuggestions_NoProfilesAddedIfDisabled) {
+ // Disable Profile autofill.
+ personal_data_->pref_service_->SetBoolean(prefs::kAutofillProfileEnabled,
+ false);
+
+ // Add a local profile.
+ AutofillProfile local_profile(base::GenerateGUID(), test::kEmptyOrigin);
+ test::SetProfileInfo(&local_profile, "Josephine", "Alicia", "Saenz",
+ "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5",
+ "Orlando", "FL", "32801", "US", "19482937549");
+ personal_data_->AddProfile(local_profile);
+
+ // Expect no profile values or suggestions were added.
+ EXPECT_EQ(0U, personal_data_->GetProfiles().size());
+}
+
TEST_F(PersonalDataManagerTest, IsKnownCard_MatchesMaskedServerCard) {
// Add a masked server card.
std::vector<CreditCard> server_cards;
@@ -1992,7 +2104,7 @@ TEST_F(PersonalDataManagerTest, IsKnownCard_MatchesMaskedServerCard) {
"2110" /* last 4 digits */, "12", "2999", "1");
server_cards.back().SetNetworkForMaskedCard(kVisaCard);
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
// Make sure everything is set up correctly.
personal_data_->Refresh();
@@ -2011,7 +2123,7 @@ TEST_F(PersonalDataManagerTest, IsKnownCard_MatchesFullServerCard) {
test::SetCreditCardInfo(&server_cards.back(), "Emmet Dalton",
"4234567890122110" /* Visa */, "12", "2999", "1");
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
// Make sure everything is set up correctly.
personal_data_->Refresh();
@@ -2094,7 +2206,7 @@ TEST_F(PersonalDataManagerTest,
"2999", "1");
server_cards.back().SetNetworkForMaskedCard(kVisaCard);
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
// Make sure everything is set up correctly.
personal_data_->Refresh();
@@ -2158,7 +2270,7 @@ TEST_F(PersonalDataManagerTest,
server_cards.back().set_use_date(AutofillClock::Now() -
base::TimeDelta::FromDays(1));
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
// Make sure everything is set up correctly.
personal_data_->Refresh();
@@ -2204,7 +2316,7 @@ TEST_F(PersonalDataManagerTest,
server_cards.back().set_use_date(AutofillClock::Now() -
base::TimeDelta::FromDays(1));
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
// Disable Credit card autofill.
personal_data_->pref_service_->SetBoolean(prefs::kAutofillCreditCardEnabled,
@@ -2212,8 +2324,12 @@ TEST_F(PersonalDataManagerTest,
personal_data_->Refresh();
WaitForOnPersonalDataChanged();
+ // Check that profiles were saved.
+ EXPECT_EQ(5U, personal_data_->GetCreditCards().size());
// Expect no autofilled values or suggestions.
- EXPECT_EQ(0U, personal_data_->GetCreditCards().size());
+ EXPECT_EQ(
+ 0U, personal_data_->GetCreditCardsToSuggest(/*include_server_cards=*/true)
+ .size());
std::vector<Suggestion> suggestions =
personal_data_->GetCreditCardSuggestions(
@@ -2247,7 +2363,7 @@ TEST_F(PersonalDataManagerTest,
server_cards.back().set_use_date(AutofillClock::Now() -
base::TimeDelta::FromDays(1));
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
personal_data_->Refresh();
WaitForOnPersonalDataChanged();
@@ -2262,7 +2378,9 @@ TEST_F(PersonalDataManagerTest,
ResetPersonalDataManager(USER_MODE_NORMAL);
// Expect no credit card values or suggestions were loaded.
- EXPECT_EQ(0U, personal_data_->GetCreditCards().size());
+ EXPECT_EQ(
+ 0U, personal_data_->GetCreditCardsToSuggest(/*include_server_cards=*/true)
+ .size());
std::vector<Suggestion> suggestions =
personal_data_->GetCreditCardSuggestions(
@@ -2272,6 +2390,26 @@ TEST_F(PersonalDataManagerTest,
ASSERT_EQ(0U, suggestions.size());
}
+// Test that local profiles are not added if |kAutofillProfileEnabled| is set to
+// |false|.
+TEST_F(PersonalDataManagerTest,
+ GetCreditCardSuggestions_NoCreditCardsAddedIfDisabled) {
+ // Disable Profile autofill.
+ personal_data_->pref_service_->SetBoolean(prefs::kAutofillCreditCardEnabled,
+ false);
+
+ // Add a local credit card.
+ CreditCard credit_card("002149C1-EE28-4213-A3B9-DA243FFF021B",
+ "https://www.example.com");
+ test::SetCreditCardInfo(&credit_card, "Bonnie Parker",
+ "5105105105105100" /* Mastercard */, "04", "2999",
+ "1");
+ personal_data_->AddCreditCard(credit_card);
+
+ // Expect no profile values or suggestions were added.
+ EXPECT_EQ(0U, personal_data_->GetCreditCards().size());
+}
+
// 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_F(PersonalDataManagerTest, GetCreditCardSuggestions_ExpiredCards) {
@@ -2357,7 +2495,7 @@ TEST_F(PersonalDataManagerTest,
std::vector<CreditCard> server_cards;
server_cards.push_back(credit_card1);
server_cards.push_back(credit_card2);
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
personal_data_->UpdateServerCardMetadata(credit_card1);
personal_data_->UpdateServerCardMetadata(credit_card2);
@@ -2541,7 +2679,7 @@ TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions_ServerDuplicates) {
server_cards.back().set_use_date(AutofillClock::Now() -
base::TimeDelta::FromDays(15));
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
// Make sure everything is set up correctly.
personal_data_->Refresh();
@@ -2592,7 +2730,7 @@ TEST_F(PersonalDataManagerTest,
"378282246310005" /* American Express */, "04",
"2999", "1");
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
// Make sure everything is set up correctly.
personal_data_->Refresh();
@@ -2621,14 +2759,9 @@ TEST_F(PersonalDataManagerTest,
ASSERT_EQ(3U, suggestions.size());
}
-// Tests that server cards will show bank name when bank name is available and
-// feature flag on.
+// Tests that server cards will show bank name when bank name is available.
TEST_F(PersonalDataManagerTest,
GetCreditCardSuggestions_ShowBankNameOfServerCards) {
- // Turn on feature flag.
- base::test::ScopedFeatureList scoped_feature_list_;
- scoped_feature_list_.InitAndEnableFeature(kAutofillCreditCardBankNameDisplay);
-
EnableWalletCardImport();
// Add a local card.
@@ -2663,7 +2796,7 @@ TEST_F(PersonalDataManagerTest,
server_cards.back().SetNetworkForMaskedCard(kVisaCard);
server_cards.back().set_bank_name("Chase");
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
// Make sure everything is set up correctly.
personal_data_->Refresh();
@@ -2902,7 +3035,7 @@ TEST_F(PersonalDataManagerTest, UpdateServerCreditCardUsageStats) {
TestAutofillClock test_clock;
test_clock.SetNow(kArbitraryTime);
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
// Make sure everything is set up correctly.
personal_data_->Refresh();
@@ -3002,8 +3135,9 @@ TEST_F(PersonalDataManagerTest, ClearAllServerData) {
test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
"3456" /* Visa */, "01", "2999", "1");
server_cards.back().SetNetworkForMaskedCard(kVisaCard);
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
personal_data_->Refresh();
+ WaitForOnPersonalDataChanged();
// Need to set the google services username
EnableWalletCardImport();
@@ -3019,6 +3153,25 @@ TEST_F(PersonalDataManagerTest, ClearAllServerData) {
EXPECT_TRUE(personal_data_->GetCreditCards().empty());
}
+TEST_F(PersonalDataManagerTest, ClearAllLocalData) {
+ // Add some local data.
+ personal_data_->AddProfile(test::GetFullProfile());
+ personal_data_->AddCreditCard(test::GetCreditCard());
+ personal_data_->Refresh();
+
+ // The card and profile should be there.
+ ResetPersonalDataManager(USER_MODE_NORMAL);
+ EXPECT_FALSE(personal_data_->GetCreditCards().empty());
+ EXPECT_FALSE(personal_data_->GetProfiles().empty());
+
+ personal_data_->ClearAllLocalData();
+
+ // Reload the database, everything should be gone.
+ ResetPersonalDataManager(USER_MODE_NORMAL);
+ EXPECT_TRUE(personal_data_->GetCreditCards().empty());
+ EXPECT_TRUE(personal_data_->GetProfiles().empty());
+}
+
// Tests the SaveImportedProfile method with different profiles to make sure the
// merge logic works correctly.
typedef struct {
@@ -3047,37 +3200,9 @@ class SaveImportedProfileTest
SaveImportedProfileTest() {}
~SaveImportedProfileTest() override {}
- void SetUp() override {
- OSCryptMocker::SetUp();
- prefs_ = test::PrefServiceForTesting();
- ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- base::FilePath path = temp_dir_.GetPath().AppendASCII("TestWebDB");
- 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(),
- WebDataServiceBase::ProfileErrorCallback());
- autofill_database_service_->Init();
+ void SetUp() override { SetUpTest(); }
- test::DisableSystemServices(prefs_.get());
- ResetPersonalDataManager(USER_MODE_NORMAL);
-
- // Reset the deduping pref to its default value.
- personal_data_->pref_service_->SetInteger(
- prefs::kAutofillLastVersionDeduped, 0);
- }
-
- void TearDown() override {
- test::DisableSystemServices(prefs_.get());
- OSCryptMocker::TearDown();
- }
+ void TearDown() override { TearDownTest(); }
};
TEST_P(SaveImportedProfileTest, SaveImportedProfile) {
@@ -4640,7 +4765,7 @@ TEST_F(PersonalDataManagerTest,
std::vector<CreditCard> server_cards;
server_cards.push_back(credit_card5);
server_cards.push_back(credit_card6);
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
personal_data_->UpdateServerCardMetadata(credit_card5);
personal_data_->UpdateServerCardMetadata(credit_card6);
@@ -4705,7 +4830,7 @@ TEST_F(PersonalDataManagerTest,
GetServerProfiles.back().SetRawInfo(NAME_FULL,
base::ASCIIToUTF16("John Doe"));
GetServerProfiles.back().set_use_count(100);
- autofill_table_->SetServerProfiles(GetServerProfiles);
+ profile_autofill_table_->SetServerProfiles(GetServerProfiles);
// Add a server and a local card that have the server address as billing
// address.
@@ -4724,7 +4849,7 @@ TEST_F(PersonalDataManagerTest,
"1111" /* Visa */, "01", "2999", "1");
server_cards.back().SetNetworkForMaskedCard(kVisaCard);
server_cards.back().set_billing_address_id(kServerAddressId);
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
// Make sure everything is set up correctly.
personal_data_->Refresh();
@@ -4809,7 +4934,7 @@ TEST_F(PersonalDataManagerTest,
GetServerProfiles.back().SetRawInfo(NAME_FULL,
base::ASCIIToUTF16("John Doe"));
GetServerProfiles.back().set_use_count(100);
- autofill_table_->SetServerProfiles(GetServerProfiles);
+ profile_autofill_table_->SetServerProfiles(GetServerProfiles);
// Add a server and a local card that have the server address as billing
// address.
@@ -4828,7 +4953,7 @@ TEST_F(PersonalDataManagerTest,
"1111" /* Visa */, "01", "2999", "1");
server_cards.back().SetNetworkForMaskedCard(kVisaCard);
server_cards.back().set_billing_address_id(kServerAddressId);
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
// Make sure everything is set up correctly.
personal_data_->Refresh();
@@ -4857,7 +4982,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 frequency to have a deterministic order.
std::vector<AutofillProfile*> profiles =
personal_data_->GetProfilesToSuggest();
@@ -4900,7 +5025,7 @@ TEST_F(PersonalDataManagerTest,
GetServerProfiles.back().set_has_converted(true);
// Wallet only provides a full name, so the above first and last names
// will be ignored when the profile is written to the DB.
- autofill_table_->SetServerProfiles(GetServerProfiles);
+ profile_autofill_table_->SetServerProfiles(GetServerProfiles);
// Make sure everything is set up correctly.
personal_data_->Refresh();
@@ -4976,7 +5101,7 @@ TEST_F(
GetServerProfiles.back().SetRawInfo(NAME_FULL,
base::ASCIIToUTF16("John Doe"));
GetServerProfiles.back().set_use_count(200);
- autofill_table_->SetServerProfiles(GetServerProfiles);
+ profile_autofill_table_->SetServerProfiles(GetServerProfiles);
// Add a server and a local card that have the first and second Wallet address
// as a billing address.
@@ -4995,7 +5120,7 @@ TEST_F(
"1111" /* Visa */, "01", "2999", "1");
server_cards.back().SetNetworkForMaskedCard(kVisaCard);
server_cards.back().set_billing_address_id(kServerAddressId2);
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
// Make sure everything is set up correctly.
personal_data_->Refresh();
@@ -5078,7 +5203,7 @@ TEST_F(
GetServerProfiles.back().SetRawInfo(NAME_FULL,
base::ASCIIToUTF16("John Doe"));
GetServerProfiles.back().set_use_count(100);
- autofill_table_->SetServerProfiles(GetServerProfiles);
+ profile_autofill_table_->SetServerProfiles(GetServerProfiles);
// Add a server card that have the server address as billing address.
std::vector<CreditCard> server_cards;
@@ -5088,7 +5213,7 @@ TEST_F(
"1111" /* Visa */, "01", "2999", "1");
server_cards.back().SetNetworkForMaskedCard(kVisaCard);
server_cards.back().set_billing_address_id(kServerAddressId);
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
// Make sure everything is set up correctly.
personal_data_->Refresh();
@@ -5122,7 +5247,7 @@ TEST_F(
"1112" /* Visa */, "01", "2888", "1");
server_cards.back().SetNetworkForMaskedCard(kVisaCard);
server_cards.back().set_billing_address_id(kServerAddressId);
- test::SetServerCreditCards(autofill_table_, server_cards);
+ test::SetServerCreditCards(account_autofill_table_, server_cards);
// Make sure everything is set up correctly.
personal_data_->Refresh();
@@ -5202,7 +5327,7 @@ TEST_F(PersonalDataManagerTest, RemoveByGUID_ResetsBillingAddress) {
personal_data_->AddProfile(profile1);
personal_data_->AddCreditCard(local_card0);
personal_data_->AddCreditCard(local_card1);
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
// Verify that the web database has been updated and the notification sent.
personal_data_->Refresh();
@@ -5471,13 +5596,13 @@ TEST_F(PersonalDataManagerTest, LogStoredCreditCardMetrics) {
}
}
- test::SetServerCreditCards(autofill_table_, server_cards);
+ SetServerCards(server_cards);
- // test::SetServerCreditCards modifies the metadata (use_count and use_date)
+ // SetServerCards modifies the metadata (use_count and use_date)
// of unmasked cards. Reset the server card metadata to match the data set
// up above.
for (const auto& card : server_cards)
- autofill_table_->UpdateServerCardMetadata(card);
+ account_autofill_table_->UpdateServerCardMetadata(card);
personal_data_->Refresh();
WaitForOnPersonalDataChanged();
@@ -5667,7 +5792,7 @@ TEST_F(PersonalDataManagerTest, RemoveExpiredCreditCardsNotUsedSinceTimestamp) {
TEST_F(PersonalDataManagerTest, CreateDataForTest) {
// Disable sync so the data gets created.
- sync_service_.SetPreferredDataTypes(syncer::ModelTypeSet());
+ sync_service_.SetDataTypes(syncer::ModelTypeSet());
// By default, the creation of test data is disabled.
ResetPersonalDataManager(USER_MODE_NORMAL);
@@ -6147,7 +6272,7 @@ TEST_F(PersonalDataManagerTest, ClearCreditCardNonSettingsOrigins) {
}
// Tests that all the non settings origins of autofill profiles are cleared even
-// if Autofill is disabled.
+// if sync is disabled.
TEST_F(
PersonalDataManagerTest,
SyncServiceInitializedWithAutofillDisabled_ClearProfileNonSettingsOrigins) {
@@ -6160,14 +6285,18 @@ TEST_F(
personal_data_->AddProfile(profile);
WaitForOnPersonalDataChanged();
- EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(1);
- prefs_->SetBoolean(prefs::kAutofillEnabled, false);
+ // Turn off autofill profile sync.
+ auto model_type_set = sync_service_.GetActiveDataTypes();
+ model_type_set.Remove(syncer::AUTOFILL_PROFILE);
+ sync_service_.SetDataTypes(model_type_set);
+ // The data should still exist.
ASSERT_EQ(1U, personal_data_->GetProfiles().size());
- personal_data_->OnSyncServiceInitialized(nullptr);
+ // Reload the personal data manager.
+ ResetPersonalDataManager(USER_MODE_NORMAL);
- WaitForOnPersonalDataChanged();
+ // The profile should still exist.
ASSERT_EQ(1U, personal_data_->GetProfiles().size());
// The profile's origin should be cleared
@@ -6175,7 +6304,7 @@ TEST_F(
}
// Tests that all the non settings origins of autofill credit cards are cleared
-// even if Autofill is disabled.
+// even if sync is disabled.
TEST_F(
PersonalDataManagerTest,
SyncServiceInitializedWithAutofillDisabled_ClearCreditCardNonSettingsOrigins) {
@@ -6187,18 +6316,101 @@ TEST_F(
personal_data_->AddCreditCard(credit_card);
WaitForOnPersonalDataChanged();
- EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(1);
- prefs_->SetBoolean(prefs::kAutofillEnabled, false);
+ // Turn off autofill profile sync.
+ auto model_type_set = sync_service_.GetActiveDataTypes();
+ model_type_set.Remove(syncer::AUTOFILL_WALLET_DATA);
+ model_type_set.Remove(syncer::AUTOFILL_WALLET_METADATA);
+ sync_service_.SetDataTypes(model_type_set);
+ // The credit card should still exist.
ASSERT_EQ(1U, personal_data_->GetCreditCards().size());
- personal_data_->OnSyncServiceInitialized(nullptr);
+ // Reload the personal data manager.
+ ResetPersonalDataManager(USER_MODE_NORMAL);
- WaitForOnPersonalDataChanged();
+ // The credit card should still exist.
ASSERT_EQ(1U, personal_data_->GetCreditCards().size());
// The card's origin should be cleared
EXPECT_TRUE(personal_data_->GetCreditCards()[0]->origin().empty());
}
+// Sanity check that the mode where we use the regular, persistent storage for
+// cards still works.
+TEST_F(PersonalDataManagerTest, UsePersistentServerStorage) {
+ ResetPersonalDataManager(USER_MODE_NORMAL,
+ /*use_account_server_storage=*/false);
+ SetUpThreeCardTypes();
+
+ // include_server_cards is set to false, therefore no server cards should be
+ // available for suggestion, but that the other calls to get the credit cards
+ // are unaffected.
+ EXPECT_EQ(3U, personal_data_->GetCreditCards().size());
+ EXPECT_EQ(1U, personal_data_
+ ->GetCreditCardsToSuggest(/*include_server_cards=*/false)
+ .size());
+ EXPECT_EQ(1U, personal_data_->GetLocalCreditCards().size());
+ EXPECT_EQ(2U, personal_data_->GetServerCreditCards().size());
+}
+
+// Sanity check that the mode where we use the regular, persistent storage for
+// cards still works.
+TEST_F(PersonalDataManagerTest, UseCorrectStorageForDifferentCards) {
+ ResetPersonalDataManager(USER_MODE_NORMAL);
+
+ // Add a server card
+ CreditCard server_card;
+ test::SetCreditCardInfo(&server_card, "Server Card",
+ "4234567890123456", // Visa
+ "04", "2999", "1");
+ server_card.set_guid("00000000-0000-0000-0000-000000000007");
+ server_card.set_record_type(CreditCard::FULL_SERVER_CARD);
+ server_card.set_server_id("server_id");
+ personal_data_->AddFullServerCreditCard(server_card);
+
+ // Set server card metadata.
+ server_card.set_use_count(15);
+ personal_data_->UpdateServerCardMetadata(server_card);
+
+ WaitForOnPersonalDataChanged();
+
+ // Expect that the server card is stored in the account autofill table.
+ std::vector<std::unique_ptr<CreditCard>> cards;
+ account_autofill_table_->GetServerCreditCards(&cards);
+ EXPECT_EQ(1U, cards.size());
+ EXPECT_EQ(server_card.LastFourDigits(), cards[0]->LastFourDigits());
+
+ // Add a local card
+ CreditCard local_card;
+ test::SetCreditCardInfo(&local_card, "Freddy Mercury",
+ "4234567890123463", // Visa
+ "08", "2999", "1");
+ local_card.set_guid("00000000-0000-0000-0000-000000000009");
+ local_card.set_record_type(CreditCard::LOCAL_CARD);
+ local_card.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(5));
+ personal_data_->AddCreditCard(local_card);
+
+ WaitForOnPersonalDataChanged();
+
+ // Expect that the local card is stored in the profile autofill table.
+ profile_autofill_table_->GetCreditCards(&cards);
+ EXPECT_EQ(1U, cards.size());
+ EXPECT_EQ(local_card.LastFourDigits(), cards[0]->LastFourDigits());
+
+ // Add a local profile
+ AutofillProfile profile(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile, "Marion", "Mitchell", "Morrison",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St", "unit 5",
+ "Hollywood", "CA", "91601", "US", "12345678910");
+ personal_data_->AddProfile(profile);
+
+ WaitForOnPersonalDataChanged();
+
+ std::vector<std::unique_ptr<AutofillProfile>> profiles;
+ // Expect that a profile is stored in the profile autofill table.
+ profile_autofill_table_->GetAutofillProfiles(&profiles);
+ EXPECT_EQ(1U, profiles.size());
+ EXPECT_EQ(profile, *profiles[0]);
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/popup_item_ids.h b/chromium/components/autofill/core/browser/popup_item_ids.h
index dca2dd0b036..31438f3b014 100644
--- a/chromium/components/autofill/core/browser/popup_item_ids.h
+++ b/chromium/components/autofill/core/browser/popup_item_ids.h
@@ -9,6 +9,8 @@
namespace autofill {
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.autofill
+// GENERATED_JAVA_PREFIX_TO_STRIP: POPUP_
enum PopupItemId {
POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY = 0,
POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE = -1,
@@ -23,7 +25,8 @@ enum PopupItemId {
POPUP_ITEM_ID_USERNAME_ENTRY = -11,
POPUP_ITEM_ID_CREATE_HINT = -12,
POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY = -13,
- POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY = -14,
+ POPUP_ITEM_ID_GOOGLE_PAY_BRANDING = -14,
+ POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY = -15,
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/proto/BUILD.gn b/chromium/components/autofill/core/browser/proto/BUILD.gn
index 2a8069f6378..c6cb42e9272 100644
--- a/chromium/components/autofill/core/browser/proto/BUILD.gn
+++ b/chromium/components/autofill/core/browser/proto/BUILD.gn
@@ -7,6 +7,8 @@ import("//third_party/protobuf/proto_library.gni")
proto_library("proto") {
sources = [
"autofill_sync.proto",
+ "password_requirements.proto",
+ "password_requirements_shard.proto",
"server.proto",
]
}
diff --git a/chromium/components/autofill/core/browser/proto/password_requirements.proto b/chromium/components/autofill/core/browser/proto/password_requirements.proto
new file mode 100644
index 00000000000..f49ab7e49ee
--- /dev/null
+++ b/chromium/components/autofill/core/browser/proto/password_requirements.proto
@@ -0,0 +1,89 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package autofill;
+
+// Requirements for generating passwords.
+message PasswordRequirementsSpec {
+ // A CharacterClass represents a type of characters (e.g. upper case,
+ // lower case, numbers, special symbols, ...).
+ //
+ // With min and max it is possible to specify the desired frequency:
+ // - Allowed: min = 0, max = MAX_INT32
+ // - Required: min = 1, max = MAX_INT32
+ // - Prohibited: min = 0, max = 0
+ //
+ // Note that character classes are meant to be handled independently of each
+ // other. Therefore, it is recommended not to set max > 0 for two overlapping
+ // character classes.
+ //
+ // It is also possible to express special rules like "at least two numbers"
+ // via min = 2, max = MAX_INT32, or "only numbers" by setting min = max = 0
+ // for all other character classes.
+ message CharacterClass {
+ // This is the pool of characters that make up the character set. For
+ // brevity, some defaults are assumed given the name/identity of the
+ // character class member (i.e., lower case vs upper case).
+ // Anything in UTF-8 is deemed acceptable here.
+ optional string character_set = 1;
+ // Minimum number of characters from this class.
+ optional uint32 min = 2;
+ // Maximum number of characters from this class.
+ optional uint32 max = 3;
+ }
+
+ // The priority of this specification.
+ //
+ // Autofill uses crowdsourcing to learn the requirements of passwords.
+ // It is possible to manually override the requirements for an entire domain
+ // or for a specific form. The priority field can be used to determine which
+ // specification wins. If multiple specificaitons are available, the one with
+ // the highest priority value wins. In that case all other specifications
+ // are discarded (i.e. completely overridden as if they were never there).
+ //
+ // Values:
+ // - 10 default for crowd sourced data.
+ // - 20 for hard-coded overrides by domain.
+ // - 30 for hard-coded overrides for a specific form.
+ optional uint32 priority = 1;
+
+ // A version number to allow older versions of Chrome to notice that they
+ // don't understand a new revision of the requirements spec.
+ optional uint32 spec_version = 2;
+
+ // Minimum and maximum length of generated passwords for a site. Note that
+ // these defaults may override the required minimum occurrences of character
+ // classes if the two contradict.
+ //
+ // Defaults may change over time.
+ optional uint32 min_length = 4;
+ optional uint32 max_length = 5;
+
+ // The following fields allow overriding requirements for specific character
+ // classes. If attributes of a CharacterClass are not set, the following
+ // defaults apply:
+
+ // All default character sets below are taken from 7-bit ASCII.
+
+ // Default: character_set = [a-z], min = 1, max = MAX_INT32
+ optional CharacterClass lower_case = 6;
+ // Default: character_set = [A-Z], min = 1, max = MAX_INT32
+ optional CharacterClass upper_case = 7;
+ // Alphabetic should not be used together with lower_case and upper_case.
+ // This is just an option for sites that don't differentiate the cases.
+ // If you wish to use alphabetic, you should set lower_case and upper_case
+ // to min = max = 0.
+ // Default: character_set = [a-zA-Z], min = 0, max = 0
+ optional CharacterClass alphabetic = 8;
+ // Default: character_set = [0-9], min = 1, max = MAX_INT32
+ optional CharacterClass numeric = 9;
+ // Default: character_set = some default that works often
+ // (e.g. [!@#$%^&*()_-+=]) but the default value may change over time!),
+ // min = 0, max = 0
+ optional CharacterClass symbols = 10;
+}
diff --git a/chromium/components/autofill/core/browser/proto/password_requirements_shard.proto b/chromium/components/autofill/core/browser/proto/password_requirements_shard.proto
new file mode 100644
index 00000000000..44dd2e97dd6
--- /dev/null
+++ b/chromium/components/autofill/core/browser/proto/password_requirements_shard.proto
@@ -0,0 +1,22 @@
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package autofill;
+
+import "password_requirements.proto";
+
+// A shard of password requirements for domains that can be served to Chrome
+// clients.
+message PasswordRequirementsShard {
+ // A map that uses a domain suffix as a key and a PasswordRequirementsSpec as
+ // a value.
+ //
+ // A key of "example.com" would mean the spec applies to "example.com",
+ // "www.example.com" but not "some-example.com".
+ //
+ // It is valid to have one entry with key "www.example.com" and another entry
+ // with key "example.com". In this case the most specific entry is applied by
+ // Chrome.
+ map<string, PasswordRequirementsSpec> specs = 1;
+}
diff --git a/chromium/components/autofill/core/browser/proto/server.proto b/chromium/components/autofill/core/browser/proto/server.proto
index 39fafca06b8..b450b8550f9 100644
--- a/chromium/components/autofill/core/browser/proto/server.proto
+++ b/chromium/components/autofill/core/browser/proto/server.proto
@@ -8,6 +8,8 @@ option optimize_for = LITE_RUNTIME;
package autofill;
+import "password_requirements.proto";
+
// The message is sent when a client needs to autofill forms on web pages and
// asks the server about known field types.
// Next available id: 11
@@ -25,21 +27,32 @@ message AutofillQueryContents {
// This message is the result of an Autofill query. It holds the field type
// information.
-// Next available id: 8
+// Next available id: 10
message AutofillQueryResponseContents {
optional bool upload_required = 1;
repeated group Field = 2 {
required fixed32 overall_type_prediction = 3;
// Detailed list of all possible predictions (including
// |overall_type_prediction| as the first item).
- message FieldPrediction { optional fixed32 type = 1; }
+ message FieldPrediction {
+ // The predicted field type.
+ optional fixed32 type = 1;
+
+ // True if the serverside classification believes that the field
+ // may be pre-filled with a placeholder in the value attribute.
+ optional bool may_use_prefilled_placeholder = 2;
+ }
repeated FieldPrediction predictions = 7;
+
+ // For fields of type NEW_PASSWORD and ACCOUNT_CREATION_PASSWORD, this may
+ // specify requirements for the generation of passwords.
+ optional PasswordRequirementsSpec password_requirements = 9;
}
}
// This message contains information about the field types in a single form.
// It is sent by the toolbar to contribute to the field type statistics.
-// Next available id: 29
+// Next available id: 30
message AutofillUploadContents {
required string client_version = 1;
required fixed64 form_signature = 2;
@@ -155,8 +168,10 @@ message AutofillUploadContents {
// True iff the the non-obfuscated password values were shown to the user.
optional bool passwords_revealed = 24;
- // The section of noisified data about password attributes.
- // Upload only one attribute and only when a password is saved first time.
+ // The section of noisified data about password.
+ // Upload only one of character class attributes (|password_has_*|). Noisified
+ // length is always uploaded.
+ // Upload only when a password is saved.
// Used to adjust the password generator's settings to site's requirements.
// Whether the password has any lowercase letter.
@@ -170,5 +185,8 @@ message AutofillUploadContents {
// Whether the password has any special symbol.
optional bool password_has_special_symbol = 28;
+
+ // Noisifed password length.
+ optional uint32 password_length = 29;
// The end of the section of password attributes.
}
diff --git a/chromium/components/autofill/core/browser/search_field.cc b/chromium/components/autofill/core/browser/search_field.cc
new file mode 100644
index 00000000000..f2b19b9461d
--- /dev/null
+++ b/chromium/components/autofill/core/browser/search_field.cc
@@ -0,0 +1,32 @@
+// 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/search_field.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_scanner.h"
+#include "components/autofill/core/common/autofill_regex_constants.h"
+
+namespace autofill {
+
+// static
+std::unique_ptr<FormField> SearchField::Parse(AutofillScanner* scanner) {
+ AutofillField* field;
+ if (ParseFieldSpecifics(scanner, base::UTF8ToUTF16(kSearchTermRe),
+ MATCH_DEFAULT | MATCH_SEARCH, &field)) {
+ return std::make_unique<SearchField>(field);
+ }
+
+ return nullptr;
+}
+
+SearchField::SearchField(const AutofillField* field) : field_(field) {}
+
+void SearchField::AddClassifications(
+ FieldCandidatesMap* field_candidates) const {
+ AddClassification(field_, SEARCH_TERM, kBaseSearchParserScore,
+ field_candidates);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/search_field.h b/chromium/components/autofill/core/browser/search_field.h
new file mode 100644
index 00000000000..41d0c396d1d
--- /dev/null
+++ b/chromium/components/autofill/core/browser/search_field.h
@@ -0,0 +1,37 @@
+// 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_SEARCH_FIELD_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_SEARCH_FIELD_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "components/autofill/core/browser/form_field.h"
+
+namespace autofill {
+
+// Search fields are not filled by autofill, but identifying them will help
+// to reduce the number of false positives.
+class SearchField : public FormField {
+ public:
+ static std::unique_ptr<FormField> Parse(AutofillScanner* scanner);
+ SearchField(const AutofillField* field);
+
+ protected:
+ void AddClassifications(FieldCandidatesMap* field_candidates) const override;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(SearchFieldTest, ParseSearchTerm);
+ FRIEND_TEST_ALL_PREFIXES(SearchFieldTest, ParseNonSearchTerm);
+
+ const AutofillField* field_;
+
+ DISALLOW_COPY_AND_ASSIGN(SearchField);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_SEARCH_FIELD_H_
diff --git a/chromium/components/autofill/core/browser/search_field_unittest.cc b/chromium/components/autofill/core/browser/search_field_unittest.cc
new file mode 100644
index 00000000000..4c65cb00fc0
--- /dev/null
+++ b/chromium/components/autofill/core/browser/search_field_unittest.cc
@@ -0,0 +1,77 @@
+// 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/search_field.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_scanner.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::ASCIIToUTF16;
+
+namespace autofill {
+
+class SearchFieldTest : public testing::Test {
+ public:
+ SearchFieldTest() {}
+
+ protected:
+ std::vector<std::unique_ptr<AutofillField>> list_;
+ std::unique_ptr<SearchField> field_;
+ FieldCandidatesMap field_candidates_map_;
+
+ // Downcast for tests.
+ static std::unique_ptr<SearchField> Parse(AutofillScanner* scanner) {
+ std::unique_ptr<FormField> field = SearchField::Parse(scanner);
+ return base::WrapUnique(static_cast<SearchField*>(field.release()));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SearchFieldTest);
+};
+
+TEST_F(SearchFieldTest, ParseSearchTerm) {
+ FormFieldData search_field;
+ search_field.form_control_type = "text";
+
+ search_field.label = ASCIIToUTF16("Search");
+ search_field.name = ASCIIToUTF16("search");
+
+ list_.push_back(
+ std::make_unique<AutofillField>(search_field, ASCIIToUTF16("search1")));
+
+ AutofillScanner scanner(list_);
+ field_ = Parse(&scanner);
+ ASSERT_NE(nullptr, field_.get());
+ field_->AddClassifications(&field_candidates_map_);
+ ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("search1")) !=
+ field_candidates_map_.end());
+ EXPECT_EQ(SEARCH_TERM,
+ field_candidates_map_[ASCIIToUTF16("search1")].BestHeuristicType());
+}
+
+TEST_F(SearchFieldTest, ParseNonSearchTerm) {
+ FormFieldData address_field;
+ address_field.form_control_type = "text";
+
+ address_field.label = ASCIIToUTF16("Address");
+ address_field.name = ASCIIToUTF16("address");
+
+ list_.push_back(
+ std::make_unique<AutofillField>(address_field, ASCIIToUTF16("address")));
+
+ AutofillScanner scanner(list_);
+ field_ = Parse(&scanner);
+ ASSERT_EQ(nullptr, field_.get());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/suggestion.cc b/chromium/components/autofill/core/browser/suggestion.cc
index 754ede930f8..5f36fe5be81 100644
--- a/chromium/components/autofill/core/browser/suggestion.cc
+++ b/chromium/components/autofill/core/browser/suggestion.cc
@@ -9,27 +9,23 @@
namespace autofill {
Suggestion::Suggestion()
- : frontend_id(0),
- match(PREFIX_MATCH),
- is_value_bold(false) {
-}
+ : frontend_id(0), match(PREFIX_MATCH), is_value_secondary(false) {}
Suggestion::Suggestion(const Suggestion& other)
: backend_id(other.backend_id),
frontend_id(other.frontend_id),
value(other.value),
label(other.label),
+ custom_icon(other.custom_icon),
icon(other.icon),
match(other.match),
- is_value_bold(other.is_value_bold) {
-}
+ is_value_secondary(other.is_value_secondary) {}
Suggestion::Suggestion(const base::string16& v)
: frontend_id(0),
value(v),
match(PREFIX_MATCH),
- is_value_bold(false) {
-}
+ is_value_secondary(false) {}
Suggestion::Suggestion(const std::string& v,
const std::string& l,
@@ -40,10 +36,8 @@ Suggestion::Suggestion(const std::string& v,
label(base::UTF8ToUTF16(l)),
icon(base::UTF8ToUTF16(i)),
match(PREFIX_MATCH),
- is_value_bold(false) {
-}
+ is_value_secondary(false) {}
-Suggestion::~Suggestion() {
-}
+Suggestion::~Suggestion() = default;
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/suggestion.h b/chromium/components/autofill/core/browser/suggestion.h
index 63522debfec..7c65c4750b6 100644
--- a/chromium/components/autofill/core/browser/suggestion.h
+++ b/chromium/components/autofill/core/browser/suggestion.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/strings/string16.h"
+#include "ui/gfx/image/image.h"
namespace autofill {
@@ -45,9 +46,12 @@ struct Suggestion {
base::string16 value;
base::string16 label;
+ // Contains an image to display for the suggestion.
+ gfx::Image custom_icon;
+ // If |custom_icon| is empty, the name of the fallback built-in icon.
base::string16 icon;
MatchMode match;
- bool is_value_bold; // true if |value| should be displayed in bold type face.
+ bool is_value_secondary; // |value| should be displayed as secondary text.
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/test_autofill_client.cc b/chromium/components/autofill/core/browser/test_autofill_client.cc
index 7bff6dfa86e..19a108d7b27 100644
--- a/chromium/components/autofill/core/browser/test_autofill_client.cc
+++ b/chromium/components/autofill/core/browser/test_autofill_client.cc
@@ -4,12 +4,13 @@
#include "components/autofill/core/browser/test_autofill_client.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
namespace autofill {
TestAutofillClient::TestAutofillClient()
- : form_origin_(GURL("https://example.test")) {}
+ : form_origin_(GURL("https://example.test")), source_id_(-1) {}
TestAutofillClient::~TestAutofillClient() {
}
@@ -38,9 +39,25 @@ ukm::UkmRecorder* TestAutofillClient::GetUkmRecorder() {
return ukm::UkmRecorder::Get();
}
+ukm::SourceId TestAutofillClient::GetUkmSourceId() {
+ if (source_id_ == -1) {
+ source_id_ = ukm::UkmRecorder::GetNewSourceID();
+ UpdateSourceURL(GetUkmRecorder(), source_id_, form_origin_);
+ }
+ return source_id_;
+}
+
+void TestAutofillClient::InitializeUKMSources() {
+ UpdateSourceURL(GetUkmRecorder(), source_id_, form_origin_);
+}
+
AddressNormalizer* TestAutofillClient::GetAddressNormalizer() {
- // TODO(crbug.com/788432): Should use a TestAddressNormalizer.
- return nullptr;
+ return &test_address_normalizer_;
+}
+
+security_state::SecurityLevel
+TestAutofillClient::GetSecurityLevelForUmaHistograms() {
+ return security_level_;
}
void TestAutofillClient::ShowAutofillSettings() {
@@ -55,6 +72,19 @@ void TestAutofillClient::ShowUnmaskPrompt(
void TestAutofillClient::OnUnmaskVerificationResult(PaymentsRpcResult result) {
}
+void TestAutofillClient::ShowLocalCardMigrationPrompt(
+ base::OnceClosure closure) {
+ std::move(closure).Run();
+}
+
+void TestAutofillClient::ConfirmSaveAutofillProfile(
+ const AutofillProfile& profile,
+ base::OnceClosure callback) {
+ // Since there is no confirmation needed to save an Autofill Profile,
+ // running |callback| will proceed with saving |profile|.
+ std::move(callback).Run();
+}
+
void TestAutofillClient::ConfirmSaveCreditCardLocally(
const CreditCard& card,
const base::Closure& callback) {
@@ -63,8 +93,9 @@ void TestAutofillClient::ConfirmSaveCreditCardLocally(
void TestAutofillClient::ConfirmSaveCreditCardToCloud(
const CreditCard& card,
std::unique_ptr<base::DictionaryValue> legal_message,
- const base::Closure& callback) {
- callback.Run();
+ bool should_request_name_from_user,
+ base::OnceCallback<void(const base::string16&)> callback) {
+ std::move(callback).Run(base::string16());
}
void TestAutofillClient::ConfirmCreditCardFillAssist(
@@ -90,8 +121,8 @@ void TestAutofillClient::ShowAutofillPopup(
const gfx::RectF& element_bounds,
base::i18n::TextDirection text_direction,
const std::vector<Suggestion>& suggestions,
- base::WeakPtr<AutofillPopupDelegate> delegate) {
-}
+ bool autoselect_first_suggestion,
+ base::WeakPtr<AutofillPopupDelegate> delegate) {}
void TestAutofillClient::UpdateAutofillPopupDataListValues(
const std::vector<base::string16>& values,
@@ -136,4 +167,20 @@ bool TestAutofillClient::AreServerCardsSupported() {
return true;
}
+void TestAutofillClient::set_form_origin(const GURL& url) {
+ form_origin_ = url;
+ // Also reset source_id_.
+ source_id_ = ukm::UkmRecorder::GetNewSourceID();
+ UpdateSourceURL(GetUkmRecorder(), source_id_, form_origin_);
+}
+
+// static
+void TestAutofillClient::UpdateSourceURL(ukm::UkmRecorder* ukm_recorder,
+ ukm::SourceId source_id,
+ GURL url) {
+ if (ukm_recorder) {
+ ukm_recorder->UpdateSourceURL(source_id, url);
+ }
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/test_autofill_client.h b/chromium/components/autofill/core/browser/test_autofill_client.h
index 6475c1c6d8a..cff3e370029 100644
--- a/chromium/components/autofill/core/browser/test_autofill_client.h
+++ b/chromium/components/autofill/core/browser/test_autofill_client.h
@@ -14,6 +14,7 @@
#include "base/i18n/rtl.h"
#include "base/macros.h"
#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/test_address_normalizer.h"
#include "components/prefs/pref_service.h"
#include "components/ukm/test_ukm_recorder.h"
#include "services/identity/public/cpp/identity_test_environment.h"
@@ -33,18 +34,24 @@ class TestAutofillClient : public AutofillClient {
syncer::SyncService* GetSyncService() override;
identity::IdentityManager* GetIdentityManager() override;
ukm::UkmRecorder* GetUkmRecorder() override;
+ ukm::SourceId GetUkmSourceId() override;
AddressNormalizer* GetAddressNormalizer() override;
+ security_state::SecurityLevel GetSecurityLevelForUmaHistograms() override;
void ShowAutofillSettings() override;
void ShowUnmaskPrompt(const CreditCard& card,
UnmaskCardReason reason,
base::WeakPtr<CardUnmaskDelegate> delegate) override;
void OnUnmaskVerificationResult(PaymentsRpcResult result) override;
+ void ShowLocalCardMigrationPrompt(base::OnceClosure closure) override;
+ void ConfirmSaveAutofillProfile(const AutofillProfile& profile,
+ base::OnceClosure callback) override;
void ConfirmSaveCreditCardLocally(const CreditCard& card,
const base::Closure& callback) override;
void ConfirmSaveCreditCardToCloud(
const CreditCard& card,
std::unique_ptr<base::DictionaryValue> legal_message,
- const base::Closure& callback) override;
+ bool should_request_name_from_user,
+ base::OnceCallback<void(const base::string16&)> callback) override;
void ConfirmCreditCardFillAssist(const CreditCard& card,
const base::Closure& callback) override;
void LoadRiskData(
@@ -55,6 +62,7 @@ class TestAutofillClient : public AutofillClient {
const gfx::RectF& element_bounds,
base::i18n::TextDirection text_direction,
const std::vector<Suggestion>& suggestions,
+ bool autoselect_first_suggestion,
base::WeakPtr<AutofillPopupDelegate> delegate) override;
void UpdateAutofillPopupDataListValues(
const std::vector<base::string16>& values,
@@ -75,24 +83,43 @@ class TestAutofillClient : public AutofillClient {
bool IsAutofillSupported() override;
bool AreServerCardsSupported() override;
void ExecuteCommand(int id) override;
+ // Initializes UKM source from form_origin_. This needs to be called
+ // in unittests after calling Purge for ukm recorder to re-initialize
+ // sources.
+ void InitializeUKMSources();
void SetPrefs(std::unique_ptr<PrefService> prefs) {
prefs_ = std::move(prefs);
}
- void set_form_origin(const GURL& url) { form_origin_ = url; }
+ void set_form_origin(const GURL& url);
void set_sync_service(syncer::SyncService* test_sync_service) {
test_sync_service_ = test_sync_service;
}
+ void set_security_level(security_state::SecurityLevel security_level) {
+ security_level_ = security_level;
+ }
+
+ GURL form_origin() { return form_origin_; }
+
+ static void UpdateSourceURL(ukm::UkmRecorder* ukm_recorder,
+ ukm::SourceId source_id,
+ GURL url);
+
private:
identity::IdentityTestEnvironment identity_test_env_;
syncer::SyncService* test_sync_service_ = nullptr;
+ TestAddressNormalizer test_address_normalizer_;
// NULL by default.
std::unique_ptr<PrefService> prefs_;
GURL form_origin_;
+ ukm::SourceId source_id_ = -1;
+
+ security_state::SecurityLevel security_level_ =
+ security_state::SecurityLevel::NONE;
DISALLOW_COPY_AND_ASSIGN(TestAutofillClient);
};
diff --git a/chromium/components/autofill/core/browser/test_autofill_driver.cc b/chromium/components/autofill/core/browser/test_autofill_driver.cc
index 94ef6316a2c..bd347eba1fd 100644
--- a/chromium/components/autofill/core/browser/test_autofill_driver.cc
+++ b/chromium/components/autofill/core/browser/test_autofill_driver.cc
@@ -3,12 +3,19 @@
// found in the LICENSE file.
#include "components/autofill/core/browser/test_autofill_driver.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_url_loader_factory.h"
#include "ui/gfx/geometry/rect_f.h"
namespace autofill {
-TestAutofillDriver::TestAutofillDriver() : url_request_context_(nullptr) {}
+TestAutofillDriver::TestAutofillDriver()
+ : url_request_context_(nullptr),
+ test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)) {}
TestAutofillDriver::~TestAutofillDriver() {}
@@ -20,6 +27,11 @@ net::URLRequestContextGetter* TestAutofillDriver::GetURLRequestContext() {
return url_request_context_;
}
+scoped_refptr<network::SharedURLLoaderFactory>
+TestAutofillDriver::GetURLLoaderFactory() {
+ return test_shared_loader_factory_;
+}
+
bool TestAutofillDriver::RendererIsAvailable() {
return true;
}
@@ -83,4 +95,9 @@ void TestAutofillDriver::SetURLRequestContext(
url_request_context_ = url_request_context;
}
+void TestAutofillDriver::SetSharedURLLoaderFactory(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+ test_shared_loader_factory_ = url_loader_factory;
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/test_autofill_driver.h b/chromium/components/autofill/core/browser/test_autofill_driver.h
index f5bbb55dced..4b826e0bd82 100644
--- a/chromium/components/autofill/core/browser/test_autofill_driver.h
+++ b/chromium/components/autofill/core/browser/test_autofill_driver.h
@@ -7,7 +7,9 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
#include "components/autofill/core/browser/autofill_driver.h"
+#include "services/network/test/test_url_loader_factory.h"
namespace autofill {
@@ -22,6 +24,7 @@ class TestAutofillDriver : public AutofillDriver {
// Returns the value passed in to the last call to |SetURLRequestContext()|
// or NULL if that method has never been called.
net::URLRequestContextGetter* GetURLRequestContext() override;
+ scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
bool RendererIsAvailable() override;
void SendFormDataToRenderer(int query_id,
RendererFormDataAction action,
@@ -54,9 +57,13 @@ class TestAutofillDriver : public AutofillDriver {
// Sets the URL request context for this instance. |url_request_context|
// should outlive this instance.
void SetURLRequestContext(net::URLRequestContextGetter* url_request_context);
+ void SetSharedURLLoaderFactory(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
private:
net::URLRequestContextGetter* url_request_context_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
bool is_incognito_ = false;
bool did_interact_with_credit_card_form_ = false;
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 009a63748ac..a5d8b8cbb37 100644
--- a/chromium/components/autofill/core/browser/test_autofill_external_delegate.cc
+++ b/chromium/components/autofill/core/browser/test_autofill_external_delegate.cc
@@ -48,10 +48,12 @@ void TestAutofillExternalDelegate::OnQuery(int query_id,
void TestAutofillExternalDelegate::OnSuggestionsReturned(
int query_id,
const std::vector<Suggestion>& suggestions,
+ bool autoselect_first_suggestion,
bool is_all_server_suggestions) {
on_suggestions_returned_seen_ = true;
query_id_ = query_id;
suggestions_ = suggestions;
+ autoselect_first_suggestion_ = autoselect_first_suggestion;
is_all_server_suggestions_ = is_all_server_suggestions;
// If necessary, call the superclass's OnSuggestionsReturned in order to
@@ -61,6 +63,15 @@ void TestAutofillExternalDelegate::OnSuggestionsReturned(
is_all_server_suggestions);
}
+bool TestAutofillExternalDelegate::HasActiveScreenReader() const {
+ return has_active_screen_reader_;
+}
+
+void TestAutofillExternalDelegate::OnAutofillAvailabilityEvent(
+ bool has_suggestions) {
+ has_suggestions_available_on_field_focus_ = has_suggestions;
+}
+
void TestAutofillExternalDelegate::WaitForPopupHidden() {
if (popup_hidden_)
return;
@@ -109,6 +120,10 @@ bool TestAutofillExternalDelegate::on_suggestions_returned_seen() const {
return on_suggestions_returned_seen_;
}
+bool TestAutofillExternalDelegate::autoselect_first_suggestion() const {
+ return autoselect_first_suggestion_;
+}
+
bool TestAutofillExternalDelegate::is_all_server_suggestions() const {
return is_all_server_suggestions_;
}
@@ -117,4 +132,14 @@ bool TestAutofillExternalDelegate::popup_hidden() const {
return popup_hidden_;
}
+void TestAutofillExternalDelegate::set_has_active_screen_reader(
+ bool has_active_screen_reader) {
+ has_active_screen_reader_ = has_active_screen_reader;
+}
+
+bool TestAutofillExternalDelegate::has_suggestions_available_on_field_focus()
+ const {
+ return has_suggestions_available_on_field_focus_;
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/test_autofill_external_delegate.h b/chromium/components/autofill/core/browser/test_autofill_external_delegate.h
index e61f111f5b8..7757ba9682e 100644
--- a/chromium/components/autofill/core/browser/test_autofill_external_delegate.h
+++ b/chromium/components/autofill/core/browser/test_autofill_external_delegate.h
@@ -28,7 +28,10 @@ class TestAutofillExternalDelegate : public AutofillExternalDelegate {
const gfx::RectF& bounds) override;
void OnSuggestionsReturned(int query_id,
const std::vector<Suggestion>& suggestions,
+ bool autoselect_first_suggestion,
bool is_all_server_suggestions) override;
+ bool HasActiveScreenReader() const override;
+ void OnAutofillAvailabilityEvent(bool has_suggestions) override;
// Functions unique to TestAutofillExternalDelegate.
@@ -51,10 +54,16 @@ class TestAutofillExternalDelegate : public AutofillExternalDelegate {
bool on_suggestions_returned_seen() const;
+ bool autoselect_first_suggestion() const;
+
bool is_all_server_suggestions() const;
bool popup_hidden() const;
+ void set_has_active_screen_reader(bool has_active_screen_reader);
+
+ bool has_suggestions_available_on_field_focus() const;
+
private:
// If true, calls AutofillExternalDelegate::OnQuery and
// AutofillExternalDelegate::OnSuggestionsReturned.
@@ -67,6 +76,9 @@ class TestAutofillExternalDelegate : public AutofillExternalDelegate {
// call to OnQuery.
bool on_suggestions_returned_seen_ = false;
+ // Records if the first suggestion should be auto-selected.
+ bool autoselect_first_suggestion_ = false;
+
// Records whether the Autofill suggestions all come from Google Payments.
bool is_all_server_suggestions_ = false;
@@ -79,6 +91,10 @@ class TestAutofillExternalDelegate : public AutofillExternalDelegate {
// |true| if the popup is hidden, |false| if the popup is shown.
bool popup_hidden_ = true;
+ bool has_active_screen_reader_ = true;
+
+ bool has_suggestions_available_on_field_focus_ = false;
+
base::RunLoop run_loop_;
DISALLOW_COPY_AND_ASSIGN(TestAutofillExternalDelegate);
diff --git a/chromium/components/autofill/core/browser/test_autofill_manager.cc b/chromium/components/autofill/core/browser/test_autofill_manager.cc
index a56f1d59c2f..dfbe4a78d74 100644
--- a/chromium/components/autofill/core/browser/test_autofill_manager.cc
+++ b/chromium/components/autofill/core/browser/test_autofill_manager.cc
@@ -12,6 +12,7 @@
#include "components/autofill/core/browser/test_form_data_importer.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"
#include "testing/gtest/include/gtest/gtest.h"
namespace autofill {
@@ -21,9 +22,10 @@ TestAutofillManager::TestAutofillManager(AutofillDriver* driver,
TestPersonalDataManager* personal_data)
: AutofillManager(driver, client, personal_data),
personal_data_(personal_data),
- context_getter_(driver->GetURLRequestContext()) {
+ url_loader_factory_(driver->GetURLLoaderFactory()),
+ client_(client) {
set_payments_client(new payments::PaymentsClient(
- context_getter_, client->GetPrefs(), client->GetIdentityManager(),
+ url_loader_factory_, client->GetPrefs(), client->GetIdentityManager(),
/*unmask_delegate=*/this,
/*save_delegate=*/nullptr));
}
@@ -33,7 +35,8 @@ TestAutofillManager::TestAutofillManager(
AutofillClient* client,
TestPersonalDataManager* personal_data,
std::unique_ptr<CreditCardSaveManager> credit_card_save_manager,
- payments::TestPaymentsClient* payments_client)
+ payments::TestPaymentsClient* payments_client,
+ std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager)
: AutofillManager(driver, client, personal_data),
personal_data_(personal_data),
test_form_data_importer_(
@@ -41,7 +44,9 @@ TestAutofillManager::TestAutofillManager(
payments_client,
std::move(credit_card_save_manager),
personal_data,
- "en-US")) {
+ "en-US",
+ std::move(local_card_migration_manager))),
+ client_(client) {
set_payments_client(payments_client);
set_form_data_importer(test_form_data_importer_);
}
@@ -52,7 +57,11 @@ bool TestAutofillManager::IsAutofillEnabled() const {
return autofill_enabled_;
}
-bool TestAutofillManager::IsCreditCardAutofillEnabled() {
+bool TestAutofillManager::IsProfileAutofillEnabled() const {
+ return profile_enabled_;
+}
+
+bool TestAutofillManager::IsCreditCardAutofillEnabled() const {
return credit_card_enabled_;
}
@@ -135,18 +144,18 @@ void TestAutofillManager::AddSeenForm(
form_structure->SetFieldTypes(heuristic_types, server_types);
AddSeenFormStructure(std::move(form_structure));
- form_interactions_ukm_logger()->OnFormsParsed(
- form.main_frame_origin.GetURL());
+ form_interactions_ukm_logger()->OnFormsParsed(form.main_frame_origin.GetURL(),
+ client_->GetUkmSourceId());
}
void TestAutofillManager::AddSeenFormStructure(
std::unique_ptr<FormStructure> form_structure) {
form_structure->set_form_parsed_timestamp(base::TimeTicks::Now());
- form_structures()->push_back(std::move(form_structure));
+ mutable_form_structures()->push_back(std::move(form_structure));
}
void TestAutofillManager::ClearFormStructures() {
- form_structures()->clear();
+ mutable_form_structures()->clear();
}
const std::string TestAutofillManager::GetSubmittedFormSignature() {
@@ -157,6 +166,13 @@ void TestAutofillManager::SetAutofillEnabled(bool autofill_enabled) {
autofill_enabled_ = autofill_enabled;
}
+void TestAutofillManager::SetProfileEnabled(bool profile_enabled) {
+ profile_enabled_ = profile_enabled;
+ if (!profile_enabled_)
+ // Profile data is refreshed when this pref is changed.
+ personal_data_->ClearProfiles();
+}
+
void TestAutofillManager::SetCreditCardEnabled(bool credit_card_enabled) {
credit_card_enabled_ = credit_card_enabled;
if (!credit_card_enabled_)
diff --git a/chromium/components/autofill/core/browser/test_autofill_manager.h b/chromium/components/autofill/core/browser/test_autofill_manager.h
index 5864523eeba..29a4ba8b225 100644
--- a/chromium/components/autofill/core/browser/test_autofill_manager.h
+++ b/chromium/components/autofill/core/browser/test_autofill_manager.h
@@ -16,8 +16,8 @@
using base::TimeTicks;
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
}
namespace autofill {
@@ -38,18 +38,21 @@ class TestAutofillManager : public AutofillManager {
TestAutofillManager(AutofillDriver* driver,
AutofillClient* client,
TestPersonalDataManager* personal_data);
- // Called by CreditCardSaveManagerTest.
+ // Called by CreditCardSaveManagerTest and LocalCardMigrationManagerTest.
TestAutofillManager(
AutofillDriver* driver,
AutofillClient* client,
TestPersonalDataManager* personal_data,
std::unique_ptr<CreditCardSaveManager> credit_card_save_manager,
- payments::TestPaymentsClient* payments_client);
+ payments::TestPaymentsClient* payments_client,
+ std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager =
+ nullptr);
~TestAutofillManager() override;
// AutofillManager overrides.
bool IsAutofillEnabled() const override;
- bool IsCreditCardAutofillEnabled() override;
+ bool IsProfileAutofillEnabled() const override;
+ bool IsCreditCardAutofillEnabled() const override;
void UploadFormData(const FormStructure& submitted_form,
bool observed_submission) override;
bool MaybeStartVoteUploadProcess(
@@ -78,6 +81,8 @@ class TestAutofillManager : public AutofillManager {
void SetAutofillEnabled(bool autofill_enabled);
+ void SetProfileEnabled(bool profile_enabled);
+
void SetCreditCardEnabled(bool credit_card_enabled);
void SetExpectedSubmittedFieldTypes(
@@ -89,9 +94,10 @@ class TestAutofillManager : public AutofillManager {
private:
TestPersonalDataManager* personal_data_; // Weak reference.
- net::URLRequestContextGetter* context_getter_ = nullptr; // Weak reference.
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
TestFormDataImporter* test_form_data_importer_ = nullptr;
bool autofill_enabled_ = true;
+ bool profile_enabled_ = true;
bool credit_card_enabled_ = true;
bool call_parent_upload_form_data_ = false;
base::Optional<bool> expected_observed_submission_;
@@ -100,6 +106,7 @@ class TestAutofillManager : public AutofillManager {
std::string submitted_form_signature_;
std::vector<ServerFieldTypeSet> expected_submitted_field_types_;
+ AutofillClient* client_;
DISALLOW_COPY_AND_ASSIGN(TestAutofillManager);
};
diff --git a/chromium/components/autofill/core/browser/test_autofill_provider.cc b/chromium/components/autofill/core/browser/test_autofill_provider.cc
index 47e77d6226f..ef5137b008c 100644
--- a/chromium/components/autofill/core/browser/test_autofill_provider.cc
+++ b/chromium/components/autofill/core/browser/test_autofill_provider.cc
@@ -11,7 +11,8 @@ void TestAutofillProvider::OnQueryFormFieldAutofill(
int32_t id,
const FormData& form,
const FormFieldData& field,
- const gfx::RectF& bounding_box) {}
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion) {}
void TestAutofillProvider::OnTextFieldDidChange(
AutofillHandlerProxy* handler,
diff --git a/chromium/components/autofill/core/browser/test_autofill_provider.h b/chromium/components/autofill/core/browser/test_autofill_provider.h
index 166556afa66..a9e620793c3 100644
--- a/chromium/components/autofill/core/browser/test_autofill_provider.h
+++ b/chromium/components/autofill/core/browser/test_autofill_provider.h
@@ -18,7 +18,8 @@ class TestAutofillProvider : public AutofillProvider {
int32_t id,
const FormData& form,
const FormFieldData& field,
- const gfx::RectF& bounding_box) override;
+ const gfx::RectF& bounding_box,
+ bool autoselect_first_suggestion) override;
void OnTextFieldDidChange(AutofillHandlerProxy* handler,
const FormData& form,
const FormFieldData& field,
diff --git a/chromium/components/autofill/core/browser/test_event_waiter.h b/chromium/components/autofill/core/browser/test_event_waiter.h
new file mode 100644
index 00000000000..b7f7102e80c
--- /dev/null
+++ b/chromium/components/autofill/core/browser/test_event_waiter.h
@@ -0,0 +1,90 @@
+// 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_TEST_EVENT_WAITER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_EVENT_WAITER_H_
+
+#include <list>
+
+#include "base/location.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+// EventWaiter is used to wait on specific events that may have occured
+// before the call to Wait(), or after, in which case a RunLoop is used.
+//
+// Usage:
+// waiter_ = std::make_unique<EventWaiter>({ ... });
+//
+// Do stuff, which (a)synchronously calls waiter_->OnEvent(...).
+//
+// waiter_->Wait(); <- Will either return right away if events were observed,
+// <- or use a RunLoop's Run/Quit to wait.
+//
+// Optionally, event waiters can be quit the RunLoop after timing out.
+template <typename Event>
+class EventWaiter {
+ public:
+ explicit EventWaiter(
+ std::list<Event> event_sequence,
+ base::TimeDelta timeout = base::TimeDelta::FromSeconds(0));
+ ~EventWaiter();
+
+ // Either returns right away if all events were observed between this
+ // object's construction and this call to Wait(), or use a RunLoop to wait
+ // for them.
+ bool Wait();
+
+ // Observes an event (quits the RunLoop if we are done waiting).
+ void OnEvent(Event event);
+
+ private:
+ std::list<Event> events_;
+ base::RunLoop run_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventWaiter);
+};
+
+template <typename Event>
+EventWaiter<Event>::EventWaiter(std::list<Event> event_sequence,
+ base::TimeDelta timeout)
+ : events_(std::move(event_sequence)) {
+ if (!timeout.is_zero()) {
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, run_loop_.QuitClosure(), timeout);
+ }
+}
+
+template <typename Event>
+EventWaiter<Event>::~EventWaiter() {}
+
+template <typename Event>
+bool EventWaiter<Event>::Wait() {
+ if (events_.empty())
+ return true;
+
+ DCHECK(!run_loop_.running());
+ run_loop_.Run();
+ return events_.empty();
+}
+
+template <typename Event>
+void EventWaiter<Event>::OnEvent(Event event) {
+ if (events_.empty())
+ return;
+
+ ASSERT_EQ(events_.front(), event);
+ events_.pop_front();
+ // Only quit the loop if no other events are expected.
+ if (events_.empty() && run_loop_.running())
+ run_loop_.Quit();
+}
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_EVENT_WAITER_H_
diff --git a/chromium/components/autofill/core/browser/test_form_data_importer.cc b/chromium/components/autofill/core/browser/test_form_data_importer.cc
index cd6943e124c..297f620213f 100644
--- a/chromium/components/autofill/core/browser/test_form_data_importer.cc
+++ b/chromium/components/autofill/core/browser/test_form_data_importer.cc
@@ -11,12 +11,14 @@ TestFormDataImporter::TestFormDataImporter(
payments::PaymentsClient* payments_client,
std::unique_ptr<CreditCardSaveManager> credit_card_save_manager,
PersonalDataManager* personal_data_manager,
- const std::string& app_locale)
+ const std::string& app_locale,
+ std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager)
: FormDataImporter(client,
payments_client,
personal_data_manager,
app_locale) {
set_credit_card_save_manager(std::move(credit_card_save_manager));
+ set_local_card_migration_manager(std::move(local_card_migration_manager));
}
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/test_form_data_importer.h b/chromium/components/autofill/core/browser/test_form_data_importer.h
index c84887da6d5..0125a8c3fe3 100644
--- a/chromium/components/autofill/core/browser/test_form_data_importer.h
+++ b/chromium/components/autofill/core/browser/test_form_data_importer.h
@@ -19,7 +19,9 @@ class TestFormDataImporter : public FormDataImporter {
payments::PaymentsClient* payments_client,
std::unique_ptr<CreditCardSaveManager> credit_card_save_manager,
PersonalDataManager* personal_data_manager,
- const std::string& app_locale);
+ const std::string& app_locale,
+ std::unique_ptr<LocalCardMigrationManager> local_card_migration_manager =
+ nullptr);
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/test_local_card_migration_manager.cc b/chromium/components/autofill/core/browser/test_local_card_migration_manager.cc
new file mode 100644
index 00000000000..9ef4888c8a0
--- /dev/null
+++ b/chromium/components/autofill/core/browser/test_local_card_migration_manager.cc
@@ -0,0 +1,54 @@
+// 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/test_local_card_migration_manager.h"
+
+#include "components/autofill/core/browser/autofill_experiments.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/payments/test_payments_client.h"
+#include "services/identity/public/cpp/identity_manager.h"
+
+namespace autofill {
+
+TestLocalCardMigrationManager::TestLocalCardMigrationManager(
+ AutofillDriver* driver,
+ AutofillClient* client,
+ payments::TestPaymentsClient* payments_client,
+ PersonalDataManager* personal_data_manager)
+ : LocalCardMigrationManager(client,
+ payments_client,
+ "en-US",
+ personal_data_manager),
+ test_payments_client_(payments_client) {
+ if (test_payments_client_)
+ test_payments_client_->SetSaveDelegate(this);
+}
+
+TestLocalCardMigrationManager::~TestLocalCardMigrationManager() {}
+
+bool TestLocalCardMigrationManager::IsCreditCardMigrationEnabled() {
+ bool migration_experiment_enabled =
+ IsAutofillCreditCardLocalCardMigrationExperimentEnabled();
+ bool has_google_payments_account =
+ (static_cast<int64_t>(payments_client_->GetPrefService()->GetDouble(
+ prefs::kAutofillBillingCustomerNumber)) != 0);
+ return migration_experiment_enabled && has_google_payments_account;
+}
+
+bool TestLocalCardMigrationManager::LocalCardMigrationWasTriggered() {
+ return local_card_migration_was_triggered_;
+}
+
+void TestLocalCardMigrationManager::OnDidGetUploadDetails(
+ AutofillClient::PaymentsRpcResult result,
+ const base::string16& context_token,
+ std::unique_ptr<base::DictionaryValue> legal_message) {
+ if (result == AutofillClient::SUCCESS) {
+ local_card_migration_was_triggered_ = true;
+ LocalCardMigrationManager::OnDidGetUploadDetails(result, context_token,
+ std::move(legal_message));
+ }
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/test_local_card_migration_manager.h b/chromium/components/autofill/core/browser/test_local_card_migration_manager.h
new file mode 100644
index 00000000000..07041a9dd45
--- /dev/null
+++ b/chromium/components/autofill/core/browser/test_local_card_migration_manager.h
@@ -0,0 +1,52 @@
+// 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_TEST_LOCAL_CARD_MIGRATION_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_LOCAL_CARD_MIGRATION_MANAGER_H_
+
+#include <string>
+
+#include "components/autofill/core/browser/local_card_migration_manager.h"
+
+namespace autofill {
+
+namespace payments {
+class TestPaymentsClient;
+} // namespace payments
+
+class AutofillClient;
+class AutofillDriver;
+class PersonalDataManager;
+
+class TestLocalCardMigrationManager : public LocalCardMigrationManager {
+ public:
+ TestLocalCardMigrationManager(AutofillDriver* driver,
+ AutofillClient* client,
+ payments::TestPaymentsClient* payments_client,
+ PersonalDataManager* personal_data_manager);
+ ~TestLocalCardMigrationManager() override;
+
+ // Override the base function. Checks the existnece of billing customer number
+ // and the experiment flag, but unlike the real class, does not check if the
+ // user is signed in/syncing.
+ bool IsCreditCardMigrationEnabled() override;
+
+ // Returns whether the first round migration pop-up window was triggered.
+ bool LocalCardMigrationWasTriggered();
+
+ private:
+ void OnDidGetUploadDetails(
+ AutofillClient::PaymentsRpcResult result,
+ const base::string16& context_token,
+ std::unique_ptr<base::DictionaryValue> legal_message) override;
+
+ payments::TestPaymentsClient* test_payments_client_; // Weak reference.
+ bool local_card_migration_was_triggered_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(TestLocalCardMigrationManager);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_LOCAL_CARD_MIGRATION_MANAGER_H_
diff --git a/chromium/components/autofill/core/browser/test_personal_data_manager.cc b/chromium/components/autofill/core/browser/test_personal_data_manager.cc
index 59f9398c3fb..583ac99edda 100644
--- a/chromium/components/autofill/core/browser/test_personal_data_manager.cc
+++ b/chromium/components/autofill/core/browser/test_personal_data_manager.cc
@@ -31,6 +31,7 @@ std::string TestPersonalDataManager::SaveImportedProfile(
std::string TestPersonalDataManager::SaveImportedCreditCard(
const CreditCard& imported_credit_card) {
+ num_times_save_imported_credit_card_called_++;
AddCreditCard(imported_credit_card);
return imported_credit_card.guid();
}
@@ -175,6 +176,14 @@ bool TestPersonalDataManager::IsAutofillEnabled() const {
return PersonalDataManager::IsAutofillEnabled();
}
+bool TestPersonalDataManager::IsAutofillProfileEnabled() const {
+ // Return the value of autofill_profile_enabled_ if it has been set,
+ // otherwise fall back to the normal behavior of checking the pref_service.
+ if (autofill_profile_enabled_.has_value())
+ return autofill_profile_enabled_.value();
+ return PersonalDataManager::IsAutofillProfileEnabled();
+}
+
bool TestPersonalDataManager::IsAutofillCreditCardEnabled() const {
// Return the value of autofill_credit_card_enabled_ if it has been set,
// otherwise fall back to the normal behavior of checking the pref_service.
@@ -191,11 +200,20 @@ bool TestPersonalDataManager::IsAutofillWalletImportEnabled() const {
return PersonalDataManager::IsAutofillWalletImportEnabled();
}
+bool TestPersonalDataManager::ShouldSuggestServerCards() const {
+ return IsAutofillCreditCardEnabled() && IsAutofillWalletImportEnabled();
+}
+
std::string TestPersonalDataManager::CountryCodeForCurrentTimezone()
const {
return timezone_country_code_;
}
+void TestPersonalDataManager::ClearAllLocalData() {
+ web_profiles_.clear();
+ local_credit_cards_.clear();
+}
+
bool TestPersonalDataManager::IsDataLoaded() const {
return true;
}
diff --git a/chromium/components/autofill/core/browser/test_personal_data_manager.h b/chromium/components/autofill/core/browser/test_personal_data_manager.h
index 411c07ac8e1..2c2b6cd9d01 100644
--- a/chromium/components/autofill/core/browser/test_personal_data_manager.h
+++ b/chromium/components/autofill/core/browser/test_personal_data_manager.h
@@ -20,7 +20,6 @@ class TestPersonalDataManager : public PersonalDataManager {
TestPersonalDataManager();
~TestPersonalDataManager() override;
- using PersonalDataManager::set_database;
using PersonalDataManager::SetPrefService;
// PersonalDataManager overrides. These functions are overridden as needed
@@ -44,9 +43,12 @@ class TestPersonalDataManager : public PersonalDataManager {
void LoadProfiles() override;
void LoadCreditCards() override;
bool IsAutofillEnabled() const override;
+ bool IsAutofillProfileEnabled() const override;
bool IsAutofillCreditCardEnabled() const override;
bool IsAutofillWalletImportEnabled() const override;
+ bool ShouldSuggestServerCards() const override;
std::string CountryCodeForCurrentTimezone() const override;
+ void ClearAllLocalData() override;
bool IsDataLoaded() const override;
// Unique to TestPersonalDataManager:
@@ -78,6 +80,10 @@ class TestPersonalDataManager : public PersonalDataManager {
return num_times_save_imported_profile_called_;
}
+ int num_times_save_imported_credit_card_called() const {
+ return num_times_save_imported_credit_card_called_;
+ }
+
void SetAutofillEnabled(bool autofill_enabled) {
autofill_enabled_ = autofill_enabled;
}
@@ -86,6 +92,10 @@ class TestPersonalDataManager : public PersonalDataManager {
autofill_credit_card_enabled_ = autofill_credit_card_enabled;
}
+ void SetAutofillProfileEnabled(bool autofill_profile_enabled) {
+ autofill_profile_enabled_ = autofill_profile_enabled;
+ }
+
void SetAutofillWalletImportEnabled(bool autofill_wallet_import_enabled) {
autofill_wallet_import_enabled_ = autofill_wallet_import_enabled;
}
@@ -94,7 +104,9 @@ class TestPersonalDataManager : public PersonalDataManager {
std::string timezone_country_code_;
std::string default_country_code_;
int num_times_save_imported_profile_called_ = 0;
+ int num_times_save_imported_credit_card_called_ = 0;
base::Optional<bool> autofill_enabled_;
+ base::Optional<bool> autofill_profile_enabled_;
base::Optional<bool> autofill_credit_card_enabled_;
base::Optional<bool> autofill_wallet_import_enabled_;
diff --git a/chromium/components/autofill/core/browser/test_sync_service.cc b/chromium/components/autofill/core/browser/test_sync_service.cc
index 0077449b04d..c1680e26a38 100644
--- a/chromium/components/autofill/core/browser/test_sync_service.cc
+++ b/chromium/components/autofill/core/browser/test_sync_service.cc
@@ -14,33 +14,32 @@
namespace autofill {
-TestSyncService::TestSyncService()
- : preferred_data_types_(syncer::ModelTypeSet::All()) {}
+TestSyncService::TestSyncService() : data_types_(syncer::ModelTypeSet::All()) {}
TestSyncService::~TestSyncService() {}
-bool TestSyncService::CanSyncStart() const {
- return can_sync_start_;
+int TestSyncService::GetDisableReasons() const {
+ return disable_reasons_;
}
syncer::ModelTypeSet TestSyncService::GetPreferredDataTypes() const {
- return preferred_data_types_;
+ return data_types_;
}
-bool TestSyncService::IsEngineInitialized() const {
- return is_engine_initialized_;
+syncer::ModelTypeSet TestSyncService::GetActiveDataTypes() const {
+ return data_types_;
}
-bool TestSyncService::IsUsingSecondaryPassphrase() const {
- return is_using_secondary_passphrase_;
+bool TestSyncService::IsEngineInitialized() const {
+ return is_engine_initialized_;
}
-bool TestSyncService::IsSyncActive() const {
- return is_sync_active_;
+bool TestSyncService::IsFirstSetupComplete() const {
+ return true;
}
-bool TestSyncService::ConfigurationDone() const {
- return configuration_done_;
+bool TestSyncService::IsUsingSecondaryPassphrase() const {
+ return is_using_secondary_passphrase_;
}
const GoogleServiceAuthError& TestSyncService::GetAuthError() const {
diff --git a/chromium/components/autofill/core/browser/test_sync_service.h b/chromium/components/autofill/core/browser/test_sync_service.h
index 0bf32d9405b..2be40aa0dec 100644
--- a/chromium/components/autofill/core/browser/test_sync_service.h
+++ b/chromium/components/autofill/core/browser/test_sync_service.h
@@ -16,22 +16,22 @@ class TestSyncService : public syncer::FakeSyncService {
~TestSyncService() override;
// FakeSyncService:
- bool CanSyncStart() const override;
+ int GetDisableReasons() const override;
syncer::ModelTypeSet GetPreferredDataTypes() const override;
+ syncer::ModelTypeSet GetActiveDataTypes() const override;
bool IsEngineInitialized() const override;
+ bool IsFirstSetupComplete() const override;
bool IsUsingSecondaryPassphrase() const override;
- bool IsSyncActive() const override;
- bool ConfigurationDone() const override;
syncer::SyncCycleSnapshot GetLastCycleSnapshot() const override;
const GoogleServiceAuthError& GetAuthError() const override;
syncer::SyncTokenStatus GetSyncTokenStatus() const override;
- void SetCanSyncStart(bool can_sync_start) {
- can_sync_start_ = can_sync_start;
+ void SetDisableReasons(int disable_reasons) {
+ disable_reasons_ = disable_reasons;
}
- void SetPreferredDataTypes(syncer::ModelTypeSet preferred_data_types) {
- preferred_data_types_ = preferred_data_types;
+ void SetDataTypes(syncer::ModelTypeSet data_types) {
+ data_types_ = data_types;
}
void SetIsEngineInitialized(bool is_engine_initialized) {
@@ -42,25 +42,16 @@ class TestSyncService : public syncer::FakeSyncService {
is_using_secondary_passphrase_ = is_using_secondary_passphrase;
}
- void SetIsSyncActive(bool is_sync_active) {
- is_sync_active_ = is_sync_active;
- }
-
- void SetConfigurationDone(bool configuration_done) {
- configuration_done_ = configuration_done;
- }
-
void SetSyncCycleComplete(bool complete) { sync_cycle_complete_ = complete; }
void SetInAuthError(bool is_in_auth_error);
private:
- bool can_sync_start_ = true;
- syncer::ModelTypeSet preferred_data_types_;
+ int disable_reasons_ = DISABLE_REASON_NONE;
+ // Used as both "preferred" and "active" data types.
+ syncer::ModelTypeSet data_types_;
bool is_engine_initialized_ = true;
bool is_using_secondary_passphrase_ = false;
- bool is_sync_active_ = true;
- bool configuration_done_ = true;
bool sync_cycle_complete_ = true;
GoogleServiceAuthError auth_error_;
bool is_in_auth_error_ = false;
diff --git a/chromium/components/autofill/core/browser/ui/DEPS b/chromium/components/autofill/core/browser/ui/DEPS
new file mode 100644
index 00000000000..ed07b2dd031
--- /dev/null
+++ b/chromium/components/autofill/core/browser/ui/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+ "save_card_bubble_controller\.h": [
+ "+components/signin/core/browser/account_info.h",
+ ]
+}
diff --git a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller.h b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller.h
index 6e63936060c..68e71be5927 100644
--- a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller.h
+++ b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller.h
@@ -6,6 +6,7 @@
#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_CARD_UNMASK_PROMPT_CONTROLLER_H_
#include "base/strings/string16.h"
+#include "components/autofill/core/browser/autofill_client.h"
namespace base {
class TimeDelta;
@@ -32,6 +33,7 @@ class CardUnmaskPromptController {
virtual bool CanStoreLocally() const = 0;
virtual bool GetStoreLocallyStartState() const = 0;
virtual base::TimeDelta GetSuccessMessageDuration() const = 0;
+ virtual AutofillClient::PaymentsRpcResult GetVerificationResult() const = 0;
// Utilities.
virtual bool InputCvcIsValid(const base::string16& input_text) const = 0;
diff --git a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc
index 8d023e8556f..16f4d9d4846 100644
--- a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc
+++ b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc
@@ -28,14 +28,7 @@ namespace autofill {
CardUnmaskPromptControllerImpl::CardUnmaskPromptControllerImpl(
PrefService* pref_service,
bool is_off_the_record)
- : pref_service_(pref_service),
- new_card_link_clicked_(false),
- is_off_the_record_(is_off_the_record),
- card_unmask_view_(nullptr),
- unmasking_result_(AutofillClient::NONE),
- unmasking_initial_should_store_pan_(false),
- unmasking_number_of_attempts_(0),
- weak_pointer_factory_(this) {}
+ : pref_service_(pref_service), is_off_the_record_(is_off_the_record) {}
CardUnmaskPromptControllerImpl::~CardUnmaskPromptControllerImpl() {
if (card_unmask_view_)
@@ -64,15 +57,6 @@ void CardUnmaskPromptControllerImpl::ShowPrompt(
AutofillMetrics::LogUnmaskPromptEvent(AutofillMetrics::UNMASK_PROMPT_SHOWN);
}
-bool CardUnmaskPromptControllerImpl::AllowsRetry(
- AutofillClient::PaymentsRpcResult result) {
- if (result == AutofillClient::NETWORK_ERROR ||
- result == AutofillClient::PERMANENT_FAILURE) {
- return false;
- }
- return true;
-}
-
void CardUnmaskPromptControllerImpl::OnVerificationResult(
AutofillClient::PaymentsRpcResult result) {
if (!card_unmask_view_)
@@ -117,70 +101,11 @@ void CardUnmaskPromptControllerImpl::OnVerificationResult(
void CardUnmaskPromptControllerImpl::OnUnmaskDialogClosed() {
card_unmask_view_ = nullptr;
LogOnCloseEvents();
+ unmasking_result_ = AutofillClient::NONE;
if (delegate_)
delegate_->OnUnmaskPromptClosed();
}
-void CardUnmaskPromptControllerImpl::LogOnCloseEvents() {
- AutofillMetrics::UnmaskPromptEvent close_reason_event = GetCloseReasonEvent();
- AutofillMetrics::LogUnmaskPromptEvent(close_reason_event);
- AutofillMetrics::LogUnmaskPromptEventDuration(
- AutofillClock::Now() - shown_timestamp_, close_reason_event);
-
- if (close_reason_event == AutofillMetrics::UNMASK_PROMPT_CLOSED_NO_ATTEMPTS)
- return;
-
- if (close_reason_event ==
- AutofillMetrics::UNMASK_PROMPT_CLOSED_ABANDON_UNMASKING) {
- AutofillMetrics::LogTimeBeforeAbandonUnmasking(AutofillClock::Now() -
- verify_timestamp_);
- }
-
- bool final_should_store_pan = pending_response_.should_store_pan;
- if (unmasking_result_ == AutofillClient::SUCCESS && final_should_store_pan) {
- AutofillMetrics::LogUnmaskPromptEvent(
- AutofillMetrics::UNMASK_PROMPT_SAVED_CARD_LOCALLY);
- }
-
- if (CanStoreLocally()) {
- // Tracking changes in local save preference.
- AutofillMetrics::UnmaskPromptEvent event;
- if (unmasking_initial_should_store_pan_ && final_should_store_pan) {
- event = AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_NOT_OPT_OUT;
- } else if (!unmasking_initial_should_store_pan_ &&
- !final_should_store_pan) {
- event = AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_NOT_OPT_IN;
- } else if (unmasking_initial_should_store_pan_ && !final_should_store_pan) {
- event = AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_OPT_OUT;
- } else {
- event = AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_OPT_IN;
- }
- AutofillMetrics::LogUnmaskPromptEvent(event);
- }
-}
-
-AutofillMetrics::UnmaskPromptEvent
-CardUnmaskPromptControllerImpl::GetCloseReasonEvent() {
- if (unmasking_number_of_attempts_ == 0)
- return AutofillMetrics::UNMASK_PROMPT_CLOSED_NO_ATTEMPTS;
-
- // If NONE and we have a pending request, we have a pending GetRealPan
- // request.
- if (unmasking_result_ == AutofillClient::NONE)
- return AutofillMetrics::UNMASK_PROMPT_CLOSED_ABANDON_UNMASKING;
-
- if (unmasking_result_ == AutofillClient::SUCCESS) {
- return unmasking_number_of_attempts_ == 1
- ? AutofillMetrics::UNMASK_PROMPT_UNMASKED_CARD_FIRST_ATTEMPT
- : AutofillMetrics::UNMASK_PROMPT_UNMASKED_CARD_AFTER_FAILED_ATTEMPTS;
- }
- return AllowsRetry(unmasking_result_)
- ? AutofillMetrics::
- UNMASK_PROMPT_CLOSED_FAILED_TO_UNMASK_RETRIABLE_FAILURE
- : AutofillMetrics::
- UNMASK_PROMPT_CLOSED_FAILED_TO_UNMASK_NON_RETRIABLE_FAILURE;
-}
-
void CardUnmaskPromptControllerImpl::OnUnmaskResponse(
const base::string16& cvc,
const base::string16& exp_month,
@@ -332,4 +257,79 @@ base::TimeDelta CardUnmaskPromptControllerImpl::GetSuccessMessageDuration()
? 0 : 500);
}
+AutofillClient::PaymentsRpcResult
+CardUnmaskPromptControllerImpl::GetVerificationResult() const {
+ return unmasking_result_;
+}
+
+bool CardUnmaskPromptControllerImpl::AllowsRetry(
+ AutofillClient::PaymentsRpcResult result) {
+ if (result == AutofillClient::NETWORK_ERROR ||
+ result == AutofillClient::PERMANENT_FAILURE) {
+ return false;
+ }
+ return true;
+}
+
+void CardUnmaskPromptControllerImpl::LogOnCloseEvents() {
+ AutofillMetrics::UnmaskPromptEvent close_reason_event = GetCloseReasonEvent();
+ AutofillMetrics::LogUnmaskPromptEvent(close_reason_event);
+ AutofillMetrics::LogUnmaskPromptEventDuration(
+ AutofillClock::Now() - shown_timestamp_, close_reason_event);
+
+ if (close_reason_event == AutofillMetrics::UNMASK_PROMPT_CLOSED_NO_ATTEMPTS)
+ return;
+
+ if (close_reason_event ==
+ AutofillMetrics::UNMASK_PROMPT_CLOSED_ABANDON_UNMASKING) {
+ AutofillMetrics::LogTimeBeforeAbandonUnmasking(AutofillClock::Now() -
+ verify_timestamp_);
+ }
+
+ bool final_should_store_pan = pending_response_.should_store_pan;
+ if (unmasking_result_ == AutofillClient::SUCCESS && final_should_store_pan) {
+ AutofillMetrics::LogUnmaskPromptEvent(
+ AutofillMetrics::UNMASK_PROMPT_SAVED_CARD_LOCALLY);
+ }
+
+ if (CanStoreLocally()) {
+ // Tracking changes in local save preference.
+ AutofillMetrics::UnmaskPromptEvent event;
+ if (unmasking_initial_should_store_pan_ && final_should_store_pan) {
+ event = AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_NOT_OPT_OUT;
+ } else if (!unmasking_initial_should_store_pan_ &&
+ !final_should_store_pan) {
+ event = AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_NOT_OPT_IN;
+ } else if (unmasking_initial_should_store_pan_ && !final_should_store_pan) {
+ event = AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_OPT_OUT;
+ } else {
+ event = AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_OPT_IN;
+ }
+ AutofillMetrics::LogUnmaskPromptEvent(event);
+ }
+}
+
+AutofillMetrics::UnmaskPromptEvent
+CardUnmaskPromptControllerImpl::GetCloseReasonEvent() {
+ if (unmasking_number_of_attempts_ == 0)
+ return AutofillMetrics::UNMASK_PROMPT_CLOSED_NO_ATTEMPTS;
+
+ // If NONE and we have a pending request, we have a pending GetRealPan
+ // request.
+ if (unmasking_result_ == AutofillClient::NONE)
+ return AutofillMetrics::UNMASK_PROMPT_CLOSED_ABANDON_UNMASKING;
+
+ if (unmasking_result_ == AutofillClient::SUCCESS) {
+ return unmasking_number_of_attempts_ == 1
+ ? AutofillMetrics::UNMASK_PROMPT_UNMASKED_CARD_FIRST_ATTEMPT
+ : AutofillMetrics::
+ UNMASK_PROMPT_UNMASKED_CARD_AFTER_FAILED_ATTEMPTS;
+ }
+ return AllowsRetry(unmasking_result_)
+ ? AutofillMetrics::
+ UNMASK_PROMPT_CLOSED_FAILED_TO_UNMASK_RETRIABLE_FAILURE
+ : AutofillMetrics::
+ UNMASK_PROMPT_CLOSED_FAILED_TO_UNMASK_NON_RETRIABLE_FAILURE;
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h
index f2717397cfd..c7e3fb311f4 100644
--- a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h
+++ b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h
@@ -10,7 +10,6 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
-#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/autofill_metrics.h"
#include "components/autofill/core/browser/card_unmask_delegate.h"
#include "components/autofill/core/browser/credit_card.h"
@@ -54,6 +53,7 @@ class CardUnmaskPromptControllerImpl : public CardUnmaskPromptController {
const base::string16& year) const override;
int GetExpectedCvcLength() const override;
base::TimeDelta GetSuccessMessageDuration() const override;
+ AutofillClient::PaymentsRpcResult GetVerificationResult() const override;
protected:
// Exposed for testing.
@@ -66,23 +66,24 @@ class CardUnmaskPromptControllerImpl : public CardUnmaskPromptController {
AutofillMetrics::UnmaskPromptEvent GetCloseReasonEvent();
PrefService* pref_service_;
- bool new_card_link_clicked_;
+ bool new_card_link_clicked_ = false;
bool is_off_the_record_;
CreditCard card_;
AutofillClient::UnmaskCardReason reason_;
base::WeakPtr<CardUnmaskDelegate> delegate_;
- CardUnmaskPromptView* card_unmask_view_;
+ CardUnmaskPromptView* card_unmask_view_ = nullptr;
- AutofillClient::PaymentsRpcResult unmasking_result_;
- bool unmasking_initial_should_store_pan_;
- int unmasking_number_of_attempts_;
+ AutofillClient::PaymentsRpcResult unmasking_result_ = AutofillClient::NONE;
+ bool unmasking_initial_should_store_pan_ = false;
+ int unmasking_number_of_attempts_ = 0;
base::Time shown_timestamp_;
// Timestamp of the last time the user clicked the Verify button.
base::Time verify_timestamp_;
CardUnmaskDelegate::UnmaskResponse pending_response_;
- base::WeakPtrFactory<CardUnmaskPromptControllerImpl> weak_pointer_factory_;
+ base::WeakPtrFactory<CardUnmaskPromptControllerImpl> weak_pointer_factory_{
+ this};
DISALLOW_COPY_AND_ASSIGN(CardUnmaskPromptControllerImpl);
};
diff --git a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl_unittest.cc b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl_unittest.cc
index 703b6404fa4..e34a39c3cf3 100644
--- a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl_unittest.cc
+++ b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl_unittest.cc
@@ -11,7 +11,7 @@
#include "base/bind.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/autofill_metrics.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
@@ -187,7 +187,11 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogClosedFailedToUnmaskRetriable) {
controller_->OnVerificationResult(AutofillClient::TRY_AGAIN_FAILURE);
base::HistogramTester histogram_tester;
+ EXPECT_EQ(AutofillClient::TRY_AGAIN_FAILURE,
+ controller_->GetVerificationResult());
controller_->OnUnmaskDialogClosed();
+ // State should be cleared when the dialog is closed.
+ EXPECT_EQ(AutofillClient::NONE, controller_->GetVerificationResult());
histogram_tester.ExpectBucketCount(
"Autofill.UnmaskPrompt.Events",
@@ -202,7 +206,11 @@ TEST_F(CardUnmaskPromptControllerImplTest,
controller_->OnVerificationResult(AutofillClient::PERMANENT_FAILURE);
base::HistogramTester histogram_tester;
+ EXPECT_EQ(AutofillClient::PERMANENT_FAILURE,
+ controller_->GetVerificationResult());
controller_->OnUnmaskDialogClosed();
+ // State should be cleared when the dialog is closed.
+ EXPECT_EQ(AutofillClient::NONE, controller_->GetVerificationResult());
histogram_tester.ExpectBucketCount(
"Autofill.UnmaskPrompt.Events",
@@ -216,7 +224,11 @@ TEST_F(CardUnmaskPromptControllerImplTest, LogUnmaskedCardFirstAttempt) {
base::HistogramTester histogram_tester;
controller_->OnVerificationResult(AutofillClient::SUCCESS);
+
+ EXPECT_EQ(AutofillClient::SUCCESS, controller_->GetVerificationResult());
controller_->OnUnmaskDialogClosed();
+ // State should be cleared when the dialog is closed.
+ EXPECT_EQ(AutofillClient::NONE, controller_->GetVerificationResult());
histogram_tester.ExpectBucketCount(
"Autofill.UnmaskPrompt.Events",
diff --git a/chromium/components/autofill/core/browser/ui/local_card_migration_bubble_controller.h b/chromium/components/autofill/core/browser/ui/local_card_migration_bubble_controller.h
new file mode 100644
index 00000000000..a2dc6206656
--- /dev/null
+++ b/chromium/components/autofill/core/browser/ui/local_card_migration_bubble_controller.h
@@ -0,0 +1,37 @@
+// 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_UI_LOCAL_CARD_MIGRATION_BUBBLE_CONTROLLER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_LOCAL_CARD_MIGRATION_BUBBLE_CONTROLLER_H_
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+
+namespace autofill {
+
+class LocalCardMigrationBubble;
+
+// Interface that exposes controller functionality to
+// LocalCardMigrationBubble. The bubble is shown to offer user an option
+// to upload credit cards stored in browser to Google Payments.
+class LocalCardMigrationBubbleController {
+ public:
+ LocalCardMigrationBubbleController() {}
+ virtual ~LocalCardMigrationBubbleController() {}
+
+ // Returns the title that should be displayed in the bubble.
+ virtual base::string16 GetWindowTitle() const = 0;
+
+ // Interaction.
+ virtual void OnConfirmButtonClicked() = 0;
+ virtual void OnCancelButtonClicked() = 0;
+ virtual void OnBubbleClosed() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LocalCardMigrationBubbleController);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_LOCAL_CARD_MIGRATION_BUBBLE_CONTROLLER_H_
diff --git a/chromium/components/autofill/core/browser/ui/save_card_bubble_controller.h b/chromium/components/autofill/core/browser/ui/save_card_bubble_controller.h
index 588810a9c5e..371fc523b0c 100644
--- a/chromium/components/autofill/core/browser/ui/save_card_bubble_controller.h
+++ b/chromium/components/autofill/core/browser/ui/save_card_bubble_controller.h
@@ -11,6 +11,7 @@
#include "base/macros.h"
#include "base/strings/string16.h"
#include "components/autofill/core/browser/legal_message_line.h"
+#include "components/signin/core/browser/account_info.h"
#include "url/gurl.h"
namespace autofill {
@@ -31,13 +32,22 @@ class SaveCardBubbleController {
// Returns an empty string if no message should be displayed.
virtual base::string16 GetExplanatoryMessage() const = 0;
+ // Returns the account info of the signed-in user.
+ virtual const AccountInfo& GetAccountInfo() const = 0;
+
// Returns the card that will be uploaded if the user accepts.
- virtual const CreditCard GetCard() const = 0;
+ virtual const CreditCard& GetCard() const = 0;
+
+ // Returns whether the dialog should include a textfield requesting the user
+ // to confirm/provide cardholder name.
+ virtual bool ShouldRequestNameFromUser() const = 0;
// Interaction.
- virtual void OnSaveButton() = 0;
+ // OnSaveButton takes in a string value representing the cardholder name
+ // confirmed/entered by the user if it was requested, or an empty string
+ // otherwise.
+ virtual void OnSaveButton(const base::string16& cardholder_name) = 0;
virtual void OnCancelButton() = 0;
- virtual void OnLearnMoreClicked() = 0;
virtual void OnLegalMessageLinkClicked(const GURL& url) = 0;
virtual void OnBubbleClosed() = 0;
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 6a723bf077a..088149b3c7d 100644
--- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc
+++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc
@@ -291,11 +291,10 @@ void AutocompleteSyncBridge::CreateForWebDataServiceAndBackend(
}
// static
-base::WeakPtr<ModelTypeSyncBridge> AutocompleteSyncBridge::FromWebDataService(
+ModelTypeSyncBridge* AutocompleteSyncBridge::FromWebDataService(
AutofillWebDataService* web_data_service) {
return static_cast<AutocompleteSyncBridge*>(
- web_data_service->GetDBUserData()->GetUserData(UserDataKey()))
- ->AsWeakPtr();
+ web_data_service->GetDBUserData()->GetUserData(UserDataKey()));
}
AutocompleteSyncBridge::AutocompleteSyncBridge(
@@ -388,7 +387,7 @@ void AutocompleteSyncBridge::AutocompleteSyncBridge::GetData(
std::move(callback).Run(std::move(batch));
}
-void AutocompleteSyncBridge::GetAllData(DataCallback callback) {
+void AutocompleteSyncBridge::GetAllDataForDebugging(DataCallback callback) {
DCHECK(thread_checker_.CalledOnValidThread());
std::vector<AutofillEntry> entries;
diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
index 2b53c23779d..a36fce9f138 100644
--- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
+++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h
@@ -9,7 +9,6 @@
#include <string>
#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/scoped_observer.h"
#include "base/supports_user_data.h"
@@ -41,7 +40,7 @@ class AutocompleteSyncBridge
AutofillWebDataService* web_data_service,
AutofillWebDataBackend* web_data_backend);
- static base::WeakPtr<syncer::ModelTypeSyncBridge> FromWebDataService(
+ static syncer::ModelTypeSyncBridge* FromWebDataService(
AutofillWebDataService* web_data_service);
// syncer::ModelTypeSyncBridge implementation.
@@ -54,7 +53,7 @@ class AutocompleteSyncBridge
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_changes) override;
void GetData(StorageKeyList storage_keys, DataCallback callback) override;
- void GetAllData(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;
diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
index 1add160d786..0dc61f9ed08 100644
--- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
+++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
@@ -10,11 +10,13 @@
#include <vector>
#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
@@ -22,17 +24,22 @@
#include "components/autofill/core/browser/webdata/autofill_table.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h"
+#include "components/sync/base/hash_util.h"
#include "components/sync/model/data_batch.h"
+#include "components/sync/model/data_type_activation_request.h"
#include "components/sync/model/metadata_batch.h"
+#include "components/sync/model/mock_model_type_change_processor.h"
#include "components/sync/model/model_error.h"
-#include "components/sync/model/recording_model_type_change_processor.h"
+#include "components/sync/model_impl/client_tag_based_model_type_processor.h"
+#include "components/sync/test/test_matchers.h"
#include "components/webdata/common/web_database.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-using base::UTF8ToUTF16;
using base::ScopedTempDir;
using base::Time;
using base::TimeDelta;
+using base::UTF8ToUTF16;
using sync_pb::AutofillSpecifics;
using sync_pb::EntityMetadata;
using sync_pb::EntitySpecifics;
@@ -42,13 +49,18 @@ using syncer::EntityChange;
using syncer::EntityChangeList;
using syncer::EntityData;
using syncer::EntityDataPtr;
-using syncer::FakeModelTypeChangeProcessor;
+using syncer::HasInitialSyncDone;
+using syncer::IsEmptyMetadataBatch;
using syncer::KeyAndData;
using syncer::ModelError;
using syncer::ModelType;
using syncer::ModelTypeChangeProcessor;
using syncer::ModelTypeSyncBridge;
-using syncer::RecordingModelTypeChangeProcessor;
+using syncer::MockModelTypeChangeProcessor;
+using testing::_;
+using testing::IsEmpty;
+using testing::Not;
+using testing::SizeIs;
namespace autofill {
@@ -57,32 +69,56 @@ namespace {
const char kNameFormat[] = "name %d";
const char kValueFormat[] = "value %d";
-void VerifyEqual(const AutofillSpecifics& s1, const AutofillSpecifics& s2) {
- // Instead of just comparing serialized strings, manually check fields to show
- // differences on failure.
- EXPECT_EQ(s1.name(), s2.name());
- EXPECT_EQ(s1.value(), s2.value());
- EXPECT_EQ(s1.usage_timestamp().size(), s2.usage_timestamp().size());
+MATCHER_P(HasSpecifics, expected, "") {
+ const AutofillSpecifics& s1 = arg->specifics.autofill();
+ const AutofillSpecifics& s2 = expected;
+
+ if (s1.usage_timestamp().size() != s2.usage_timestamp().size()) {
+ *result_listener << "usage_timstamp().size() not equal: "
+ << s1.usage_timestamp().size()
+ << "!=" << s2.usage_timestamp().size();
+ return false;
+ }
int size = std::min(s1.usage_timestamp().size(), s2.usage_timestamp().size());
for (int i = 0; i < size; ++i) {
- EXPECT_EQ(s1.usage_timestamp(i), s2.usage_timestamp(i))
- << "Values differ at index " << i;
+ if (s1.usage_timestamp(i) != s2.usage_timestamp(i)) {
+ *result_listener << "usage_timstamp(" << i
+ << ") not equal: " << s1.usage_timestamp(i)
+ << "!=" << s2.usage_timestamp(i);
+ return false;
+ }
+ }
+
+ if (s1.name() != s2.name()) {
+ *result_listener << "name() not equal: " << s1.name() << "!=" << s2.name();
+ return false;
}
- // Neither should have any profile data, so we don't have to compare it.
- EXPECT_FALSE(s1.has_profile());
- EXPECT_FALSE(s2.has_profile());
+
+ if (s1.value() != s2.value()) {
+ *result_listener << "value() not equal: " << s1.value()
+ << "!=" << s2.value();
+ return false;
+ }
+
+ if (s1.has_profile() != s2.has_profile()) {
+ *result_listener << "has_profile() not equal: " << s1.has_profile()
+ << "!=" << s2.has_profile();
+ return false;
+ }
+
+ return true;
}
void VerifyDataBatch(std::map<std::string, AutofillSpecifics> expected,
std::unique_ptr<DataBatch> batch) {
while (batch->HasNext()) {
- const KeyAndData& pair = batch->Next();
- auto iter = expected.find(pair.first);
- ASSERT_NE(iter, expected.end());
- VerifyEqual(iter->second, pair.second->specifics.autofill());
+ const KeyAndData& data_pair = batch->Next();
+ auto expected_iter = expected.find(data_pair.first);
+ ASSERT_NE(expected_iter, expected.end());
+ EXPECT_THAT(data_pair.second, HasSpecifics(expected_iter->second));
// Removing allows us to verify we don't see the same item multiple times,
// and that we saw everything we expected.
- expected.erase(iter);
+ expected.erase(expected_iter);
}
EXPECT_TRUE(expected.empty());
}
@@ -100,13 +136,6 @@ AutofillEntry CreateAutofillEntry(const AutofillSpecifics& autofill_specifics) {
return AutofillEntry(key, date_created, date_last_used);
}
-EntityDataPtr SpecificsToEntity(const AutofillSpecifics& specifics) {
- EntityData data;
- data.client_tag_hash = "ignored";
- *data.specifics.mutable_autofill() = specifics;
- return data.PassToPtr();
-}
-
class FakeAutofillBackend : public AutofillWebDataBackend {
public:
FakeAutofillBackend() {}
@@ -131,21 +160,51 @@ class FakeAutofillBackend : public AutofillWebDataBackend {
class AutocompleteSyncBridgeTest : public testing::Test {
public:
- AutocompleteSyncBridgeTest() {
- if (temp_dir_.CreateUniqueTempDir()) {
- db_.AddTable(&table_);
- db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase"));
- backend_.SetWebDatabase(&db_);
- ResetBridge();
- }
- }
+ AutocompleteSyncBridgeTest() {}
~AutocompleteSyncBridgeTest() override {}
- void ResetBridge(bool expect_error = false) {
+ void SetUp() override {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ db_.AddTable(&table_);
+ db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase"));
+ backend_.SetWebDatabase(&db_);
+ ResetProcessor();
+ ResetBridge();
+ }
+
+ void ResetProcessor() {
+ real_processor_ =
+ std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
+ syncer::AUTOFILL, /*dump_stack=*/base::DoNothing(),
+ /*commit_only=*/false);
+ mock_processor_.DelegateCallsByDefaultTo(real_processor_.get());
+ }
+
+ void ResetBridge() {
bridge_.reset(new AutocompleteSyncBridge(
- &backend_,
- RecordingModelTypeChangeProcessor::CreateProcessorAndAssignRawPointer(
- &processor_, expect_error)));
+ &backend_, mock_processor_.CreateForwardingProcessor()));
+ }
+
+ void StartSyncing(const std::vector<AutofillSpecifics>& remote_data = {}) {
+ base::RunLoop loop;
+ syncer::DataTypeActivationRequest request;
+ request.error_handler = base::DoNothing();
+ real_processor_->OnSyncStarting(
+ request,
+ base::BindLambdaForTesting(
+ [&loop](std::unique_ptr<syncer::DataTypeActivationResponse>) {
+ loop.Quit();
+ }));
+ loop.Run();
+
+ // Initialize the processor with initial_sync_done.
+ sync_pb::ModelTypeState state;
+ state.set_initial_sync_done(true);
+ syncer::UpdateResponseDataList initial_updates;
+ for (const AutofillSpecifics& specifics : remote_data) {
+ initial_updates.push_back(SpecificsToUpdateResponse(specifics));
+ }
+ real_processor_->OnUpdateReceived(state, initial_updates);
}
void SaveSpecificsToTable(
@@ -205,22 +264,31 @@ class AutocompleteSyncBridgeTest : public testing::Test {
return changes;
}
- void VerifyApplyChanges(const EntityChangeList& changes) {
- const auto error = bridge()->ApplySyncChanges(
- bridge()->CreateMetadataChangeList(), changes);
- EXPECT_FALSE(error);
+ EntityDataPtr SpecificsToEntity(const AutofillSpecifics& specifics) {
+ EntityData data;
+ *data.specifics.mutable_autofill() = specifics;
+ data.client_tag_hash = syncer::GenerateSyncableHash(
+ syncer::AUTOFILL, bridge()->GetClientTag(data));
+ return data.PassToPtr();
}
- void VerifyApplyAdds(const std::vector<AutofillSpecifics>& specifics) {
- VerifyApplyChanges(CreateEntityAddList(specifics));
+ syncer::UpdateResponseData SpecificsToUpdateResponse(
+ const AutofillSpecifics& specifics) {
+ syncer::UpdateResponseData data;
+ data.entity = SpecificsToEntity(specifics);
+ return data;
}
- void VerifyMerge(const std::vector<AutofillSpecifics>& specifics) {
- const auto error = bridge()->MergeSyncData(
- bridge()->CreateMetadataChangeList(), CreateEntityAddList(specifics));
+ void ApplyChanges(const EntityChangeList& changes) {
+ const auto error = bridge()->ApplySyncChanges(
+ bridge()->CreateMetadataChangeList(), changes);
EXPECT_FALSE(error);
}
+ void ApplyAdds(const std::vector<AutofillSpecifics>& specifics) {
+ ApplyChanges(CreateEntityAddList(specifics));
+ }
+
std::map<std::string, AutofillSpecifics> ExpectedMap(
const std::vector<AutofillSpecifics>& specifics_vector) {
std::map<std::string, AutofillSpecifics> map;
@@ -231,30 +299,15 @@ class AutocompleteSyncBridgeTest : public testing::Test {
}
void VerifyAllData(const std::vector<AutofillSpecifics>& expected) {
- bridge()->GetAllData(base::Bind(&VerifyDataBatch, ExpectedMap(expected)));
- }
-
- void VerifyProcessorRecordedPut(const AutofillSpecifics& specifics,
- int position = 0) {
- const std::string storage_key = GetStorageKey(specifics);
- auto recorded_specifics_iterator =
- processor().put_multimap().find(storage_key);
-
- EXPECT_NE(processor().put_multimap().end(), recorded_specifics_iterator);
- while (position > 0) {
- recorded_specifics_iterator++;
- EXPECT_NE(processor().put_multimap().end(), recorded_specifics_iterator);
- position--;
- }
-
- AutofillSpecifics recorded_specifics =
- recorded_specifics_iterator->second->specifics.autofill();
- VerifyEqual(recorded_specifics, specifics);
+ bridge()->GetAllDataForDebugging(
+ base::Bind(&VerifyDataBatch, ExpectedMap(expected)));
}
AutocompleteSyncBridge* bridge() { return bridge_.get(); }
- const RecordingModelTypeChangeProcessor& processor() { return *processor_; }
+ syncer::MockModelTypeChangeProcessor& mock_processor() {
+ return mock_processor_;
+ }
AutofillTable* table() { return &table_; }
@@ -267,9 +320,8 @@ class AutocompleteSyncBridgeTest : public testing::Test {
AutofillTable table_;
WebDatabase db_;
std::unique_ptr<AutocompleteSyncBridge> bridge_;
- // A non-owning pointer to the processor given to the bridge. Will be null
- // before being given to the bridge, to make ownership easier.
- RecordingModelTypeChangeProcessor* processor_ = nullptr;
+ testing::NiceMock<MockModelTypeChangeProcessor> mock_processor_;
+ std::unique_ptr<syncer::ClientTagBasedModelTypeProcessor> real_processor_;
DISALLOW_COPY_AND_ASSIGN(AutocompleteSyncBridgeTest);
};
@@ -397,7 +449,7 @@ TEST_F(AutocompleteSyncBridgeTest, GetAllData) {
TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesEmpty) {
// TODO(skym, crbug.com/672619): Ideally would like to verify that the db is
// not accessed.
- VerifyApplyAdds(std::vector<AutofillSpecifics>());
+ ApplyAdds(std::vector<AutofillSpecifics>());
}
TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesSimple) {
@@ -406,10 +458,10 @@ TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesSimple) {
ASSERT_NE(specifics1.SerializeAsString(), specifics2.SerializeAsString());
ASSERT_NE(GetStorageKey(specifics1), GetStorageKey(specifics2));
- VerifyApplyAdds({specifics1, specifics2});
+ ApplyAdds({specifics1, specifics2});
VerifyAllData({specifics1, specifics2});
- VerifyApplyChanges({EntityChange::CreateDelete(GetStorageKey(specifics1))});
+ ApplyChanges({EntityChange::CreateDelete(GetStorageKey(specifics1))});
VerifyAllData({specifics2});
}
@@ -417,43 +469,43 @@ TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesSimple) {
// existing ones.
TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesWrongChangeType) {
AutofillSpecifics specifics = CreateSpecifics(1, {1});
- VerifyApplyChanges({EntityChange::CreateDelete(GetStorageKey(specifics))});
+ ApplyChanges({EntityChange::CreateDelete(GetStorageKey(specifics))});
VerifyAllData(std::vector<AutofillSpecifics>());
- VerifyApplyChanges({EntityChange::CreateUpdate(
- GetStorageKey(specifics), SpecificsToEntity(specifics))});
+ ApplyChanges({EntityChange::CreateUpdate(GetStorageKey(specifics),
+ SpecificsToEntity(specifics))});
VerifyAllData({specifics});
specifics.add_usage_timestamp(Time::FromTimeT(2).ToInternalValue());
- VerifyApplyAdds({specifics});
+ ApplyAdds({specifics});
VerifyAllData({specifics});
}
// The format in the table has a fixed 2 timestamp format. Round tripping is
// lossy and the middle value should be thrown out.
TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesThreeTimestamps) {
- VerifyApplyAdds({CreateSpecifics(1, {1, 2, 3})});
+ ApplyAdds({CreateSpecifics(1, {1, 2, 3})});
VerifyAllData({CreateSpecifics(1, {1, 3})});
}
// The correct format of timestamps is that the first should be smaller and the
// second should be larger. Bad foreign data should be repaired.
TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesWrongOrder) {
- VerifyApplyAdds({CreateSpecifics(1, {3, 2})});
+ ApplyAdds({CreateSpecifics(1, {3, 2})});
VerifyAllData({CreateSpecifics(1, {2, 3})});
}
// In a minor attempt to save bandwidth, we only send one of the two timestamps
// when they share a value.
TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesRepeatedTime) {
- VerifyApplyAdds({CreateSpecifics(1, {2, 2})});
+ ApplyAdds({CreateSpecifics(1, {2, 2})});
VerifyAllData({CreateSpecifics(1, {2})});
}
// Again, the format in the table is lossy, and cannot distinguish between no
// time, and valid time zero.
TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoTime) {
- VerifyApplyAdds({CreateSpecifics(1, std::vector<int>())});
+ ApplyAdds({CreateSpecifics(1, std::vector<int>())});
VerifyAllData({CreateSpecifics(1, {0})});
}
@@ -462,7 +514,7 @@ TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoTime) {
TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoValue) {
AutofillSpecifics input = CreateSpecifics(1, {2, 3});
input.clear_value();
- VerifyApplyAdds({input});
+ ApplyAdds({input});
VerifyAllData(std::vector<AutofillSpecifics>());
}
@@ -471,7 +523,7 @@ TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoValue) {
TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoName) {
AutofillSpecifics input = CreateSpecifics(1, {2, 3});
input.clear_name();
- VerifyApplyAdds({input});
+ ApplyAdds({input});
VerifyAllData({input});
}
@@ -480,7 +532,7 @@ TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesNoName) {
TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesUTF) {
const AutofillSpecifics specifics =
CreateSpecifics(std::string("\n\0\x12\0", 4), "\xEC\xA4\x91", {1});
- VerifyApplyAdds({specifics});
+ ApplyAdds({specifics});
VerifyAllData({specifics});
}
@@ -488,13 +540,13 @@ TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesUTF) {
// usage time.
TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesMinMaxTimestamps) {
const AutofillSpecifics initial = CreateSpecifics(1, {3, 6});
- VerifyApplyAdds({initial});
+ ApplyAdds({initial});
VerifyAllData({initial});
- VerifyApplyAdds({CreateSpecifics(1, {2, 5})});
+ ApplyAdds({CreateSpecifics(1, {2, 5})});
VerifyAllData({CreateSpecifics(1, {2, 6})});
- VerifyApplyAdds({CreateSpecifics(1, {4, 7})});
+ ApplyAdds({CreateSpecifics(1, {4, 7})});
VerifyAllData({CreateSpecifics(1, {2, 7})});
}
@@ -514,6 +566,7 @@ TEST_F(AutocompleteSyncBridgeTest, ApplySyncChangesDatabaseFailure) {
}
TEST_F(AutocompleteSyncBridgeTest, LocalEntriesAdded) {
+ StartSyncing();
const AutofillSpecifics added_specifics1 = CreateSpecifics(1, {2, 3});
const AutofillSpecifics added_specifics2 = CreateSpecifics(2, {2, 3});
@@ -522,56 +575,44 @@ TEST_F(AutocompleteSyncBridgeTest, LocalEntriesAdded) {
table()->UpdateAutofillEntries({added_entry1, added_entry2});
+ EXPECT_CALL(mock_processor(), Put(_, HasSpecifics(added_specifics1), _));
+ EXPECT_CALL(mock_processor(), Put(_, HasSpecifics(added_specifics2), _));
bridge()->AutofillEntriesChanged(
{AutofillChange(AutofillChange::ADD, added_entry1.key()),
AutofillChange(AutofillChange::ADD, added_entry2.key())});
-
- EXPECT_EQ(2u, processor().put_multimap().size());
-
- VerifyProcessorRecordedPut(added_specifics1);
- VerifyProcessorRecordedPut(added_specifics2);
}
TEST_F(AutocompleteSyncBridgeTest, LocalEntryAddedThenUpdated) {
+ StartSyncing();
const AutofillSpecifics added_specifics = CreateSpecifics(1, {2, 3});
const AutofillEntry added_entry = CreateAutofillEntry(added_specifics);
table()->UpdateAutofillEntries({added_entry});
+ EXPECT_CALL(mock_processor(), Put(_, HasSpecifics(added_specifics), _));
bridge()->AutofillEntriesChanged(
{AutofillChange(AutofillChange::ADD, added_entry.key())});
- EXPECT_EQ(1u, processor().put_multimap().size());
-
- VerifyProcessorRecordedPut(added_specifics);
-
const AutofillSpecifics updated_specifics = CreateSpecifics(1, {2, 4});
const AutofillEntry updated_entry = CreateAutofillEntry(updated_specifics);
table()->UpdateAutofillEntries({updated_entry});
+ EXPECT_CALL(mock_processor(), Put(_, HasSpecifics(updated_specifics), _));
bridge()->AutofillEntriesChanged(
{AutofillChange(AutofillChange::UPDATE, updated_entry.key())});
-
- VerifyProcessorRecordedPut(updated_specifics, 1);
}
TEST_F(AutocompleteSyncBridgeTest, LocalEntryDeleted) {
+ StartSyncing();
const AutofillSpecifics deleted_specifics = CreateSpecifics(1, {2, 3});
const AutofillEntry deleted_entry = CreateAutofillEntry(deleted_specifics);
const std::string storage_key = GetStorageKey(deleted_specifics);
+ EXPECT_CALL(mock_processor(), Delete(storage_key, _));
bridge()->AutofillEntriesChanged(
{AutofillChange(AutofillChange::REMOVE, deleted_entry.key())});
-
- EXPECT_EQ(1u, processor().delete_set().size());
- EXPECT_NE(processor().delete_set().end(),
- processor().delete_set().find(storage_key));
}
TEST_F(AutocompleteSyncBridgeTest, LoadMetadataCalled) {
- EXPECT_NE(nullptr, processor().metadata());
- EXPECT_FALSE(processor().metadata()->GetModelTypeState().initial_sync_done());
- EXPECT_EQ(0u, processor().metadata()->TakeAllMetadata().size());
-
ModelTypeState model_type_state;
model_type_state.set_initial_sync_done(true);
EXPECT_TRUE(
@@ -579,51 +620,53 @@ TEST_F(AutocompleteSyncBridgeTest, LoadMetadataCalled) {
EXPECT_TRUE(
table()->UpdateSyncMetadata(syncer::AUTOFILL, "key", EntityMetadata()));
+ ResetProcessor();
+ EXPECT_CALL(mock_processor(), ModelReadyToSync(MetadataBatchContains(
+ /*state=*/HasInitialSyncDone(),
+ /*entities=*/SizeIs(1))));
ResetBridge();
-
- EXPECT_NE(nullptr, processor().metadata());
- EXPECT_TRUE(processor().metadata()->GetModelTypeState().initial_sync_done());
- EXPECT_EQ(1u, processor().metadata()->TakeAllMetadata().size());
}
TEST_F(AutocompleteSyncBridgeTest, LoadMetadataReportsErrorForMissingDB) {
backend()->SetWebDatabase(nullptr);
- // The processor's destructor will verify that an error has occured.
- ResetBridge(/*expect_error=*/true);
+ EXPECT_CALL(mock_processor(), ReportError(_));
+ ResetBridge();
}
TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataEmpty) {
- VerifyMerge(std::vector<AutofillSpecifics>());
+ EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+
+ StartSyncing(/*remote_data=*/std::vector<AutofillSpecifics>());
VerifyAllData(std::vector<AutofillSpecifics>());
- EXPECT_EQ(0u, processor().delete_set().size());
- EXPECT_EQ(0u, processor().put_multimap().size());
}
TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataRemoteOnly) {
const AutofillSpecifics specifics1 = CreateSpecifics(1, {2});
const AutofillSpecifics specifics2 = CreateSpecifics(2, {3, 4});
- VerifyMerge({specifics1, specifics2});
+ EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+
+ StartSyncing(/*remote_data=*/{specifics1, specifics2});
VerifyAllData({specifics1, specifics2});
- EXPECT_EQ(0u, processor().delete_set().size());
- EXPECT_EQ(0u, processor().put_multimap().size());
}
TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataLocalOnly) {
const AutofillSpecifics specifics1 = CreateSpecifics(1, {2});
const AutofillSpecifics specifics2 = CreateSpecifics(2, {3, 4});
- VerifyApplyAdds({specifics1, specifics2});
- VerifyAllData({specifics1, specifics2});
- VerifyMerge(std::vector<AutofillSpecifics>());
+ EXPECT_CALL(mock_processor(), Put(_, HasSpecifics(specifics1), _));
+ EXPECT_CALL(mock_processor(), Put(_, HasSpecifics(specifics2), _));
+ EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+
+ ApplyAdds({specifics1, specifics2});
+ VerifyAllData({specifics1, specifics2});
+ StartSyncing(/*remote_data=*/{});
VerifyAllData({specifics1, specifics2});
- EXPECT_EQ(2u, processor().put_multimap().size());
- VerifyProcessorRecordedPut(specifics1);
- VerifyProcessorRecordedPut(specifics2);
- EXPECT_EQ(0u, processor().delete_set().size());
}
TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataAllMerged) {
@@ -645,17 +688,18 @@ TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataAllMerged) {
const AutofillSpecifics merged4 = CreateSpecifics(4, {5, 8});
const AutofillSpecifics merged5 = local5;
const AutofillSpecifics merged6 = CreateSpecifics(6, {7, 10});
- VerifyApplyAdds({local1, local2, local3, local4, local5, local6});
- VerifyMerge({remote1, remote2, remote3, remote4, remote5, remote6});
+ EXPECT_CALL(mock_processor(), Put(_, HasSpecifics(merged3), _));
+ EXPECT_CALL(mock_processor(), Put(_, HasSpecifics(merged4), _));
+ EXPECT_CALL(mock_processor(), Put(_, HasSpecifics(merged5), _));
+ EXPECT_CALL(mock_processor(), Put(_, HasSpecifics(merged6), _));
+ EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+
+ ApplyAdds({local1, local2, local3, local4, local5, local6});
+ StartSyncing(
+ /*remote_data=*/{remote1, remote2, remote3, remote4, remote5, remote6});
VerifyAllData({merged1, merged2, merged3, merged4, merged5, merged6});
- EXPECT_EQ(4u, processor().put_multimap().size());
- VerifyProcessorRecordedPut(merged3);
- VerifyProcessorRecordedPut(merged4);
- VerifyProcessorRecordedPut(merged5);
- VerifyProcessorRecordedPut(merged6);
- EXPECT_EQ(0u, processor().delete_set().size());
}
TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataMixed) {
@@ -666,15 +710,15 @@ TEST_F(AutocompleteSyncBridgeTest, MergeSyncDataMixed) {
const AutofillSpecifics remote4 = CreateSpecifics(4, {2, 4});
const AutofillSpecifics merged4 = CreateSpecifics(4, {1, 4});
- VerifyApplyAdds({local1, specifics3, local4});
+ EXPECT_CALL(mock_processor(), Put(_, HasSpecifics(local1), _));
+ EXPECT_CALL(mock_processor(), Put(_, HasSpecifics(merged4), _));
+ EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+
+ ApplyAdds({local1, specifics3, local4});
- VerifyMerge({remote2, specifics3, remote4});
+ StartSyncing(/*remote_data=*/{remote2, specifics3, remote4});
VerifyAllData({local1, remote2, specifics3, merged4});
- EXPECT_EQ(2u, processor().put_multimap().size());
- VerifyProcessorRecordedPut(local1);
- VerifyProcessorRecordedPut(merged4);
- EXPECT_EQ(0u, processor().delete_set().size());
}
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
new file mode 100644
index 00000000000..777aaca47da
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.cc
@@ -0,0 +1,302 @@
+// 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_sync_bridge.h"
+
+#include <memory>
+#include <unordered_set>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/guid.h"
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_profile_sync_util.h"
+#include "components/autofill/core/browser/country_names.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/proto/autofill_sync.pb.h"
+#include "components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
+#include "components/sync/model/entity_data.h"
+#include "components/sync/model/metadata_change_list.h"
+#include "components/sync/model/model_error.h"
+#include "components/sync/model/model_type_change_processor.h"
+#include "components/sync/model/mutable_data_batch.h"
+#include "components/sync/model_impl/client_tag_based_model_type_processor.h"
+#include "components/sync/model_impl/sync_metadata_store_change_list.h"
+
+using base::Optional;
+using base::UTF16ToUTF8;
+using sync_pb::AutofillProfileSpecifics;
+using syncer::EntityData;
+using syncer::MetadataChangeList;
+using syncer::ModelError;
+
+namespace autofill {
+
+namespace {
+
+// Simplify checking for optional errors and returning only when present.
+#define RETURN_IF_ERROR(x) \
+ if (Optional<ModelError> ret_val = x) { \
+ return ret_val; \
+ }
+
+// Address to this variable used as the user data key.
+static int kAutofillProfileSyncBridgeUserDataKey = 0;
+
+} // namespace
+
+// static
+void AutofillProfileSyncBridge::CreateForWebDataServiceAndBackend(
+ const std::string& app_locale,
+ AutofillWebDataBackend* web_data_backend,
+ AutofillWebDataService* web_data_service) {
+ web_data_service->GetDBUserData()->SetUserData(
+ &kAutofillProfileSyncBridgeUserDataKey,
+ std::make_unique<AutofillProfileSyncBridge>(
+ std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
+ syncer::AUTOFILL_PROFILE,
+ /*dump_stack=*/base::RepeatingClosure()),
+ app_locale, web_data_backend));
+}
+
+// static
+syncer::ModelTypeSyncBridge* AutofillProfileSyncBridge::FromWebDataService(
+ AutofillWebDataService* web_data_service) {
+ return static_cast<AutofillProfileSyncBridge*>(
+ web_data_service->GetDBUserData()->GetUserData(
+ &kAutofillProfileSyncBridgeUserDataKey));
+}
+
+AutofillProfileSyncBridge::AutofillProfileSyncBridge(
+ std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
+ const std::string& app_locale,
+ AutofillWebDataBackend* backend)
+ : syncer::ModelTypeSyncBridge(std::move(change_processor)),
+ app_locale_(app_locale),
+ web_data_backend_(backend),
+ scoped_observer_(this) {
+ DCHECK(web_data_backend_);
+
+ scoped_observer_.Add(web_data_backend_);
+
+ LoadMetadata();
+}
+
+AutofillProfileSyncBridge::~AutofillProfileSyncBridge() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+std::unique_ptr<MetadataChangeList>
+AutofillProfileSyncBridge::CreateMetadataChangeList() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ return std::make_unique<syncer::SyncMetadataStoreChangeList>(
+ GetAutofillTable(), syncer::AUTOFILL_PROFILE);
+}
+
+Optional<syncer::ModelError> AutofillProfileSyncBridge::MergeSyncData(
+ std::unique_ptr<MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_data) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ AutofillProfileInitialSyncDifferenceTracker initial_sync_tracker(
+ GetAutofillTable());
+
+ for (const auto& change : entity_data) {
+ DCHECK(change.data().specifics.has_autofill_profile());
+ std::unique_ptr<AutofillProfile> remote =
+ CreateAutofillProfileFromSpecifics(
+ change.data().specifics.autofill_profile());
+ if (!remote) {
+ DVLOG(2) << "[AUTOFILL SYNC] Invalid remote specifics "
+ << change.data().specifics.autofill_profile().SerializeAsString()
+ << " received from the server in an initial sync.";
+ continue;
+ }
+ RETURN_IF_ERROR(
+ initial_sync_tracker.IncorporateRemoteProfile(std::move(remote)));
+ }
+
+ RETURN_IF_ERROR(
+ initial_sync_tracker.MergeSimilarEntriesForInitialSync(app_locale_));
+ RETURN_IF_ERROR(
+ FlushSyncTracker(std::move(metadata_change_list), &initial_sync_tracker));
+
+ web_data_backend_->NotifyThatSyncHasStarted(syncer::AUTOFILL_PROFILE);
+ return base::nullopt;
+}
+
+Optional<ModelError> AutofillProfileSyncBridge::ApplySyncChanges(
+ std::unique_ptr<MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_changes) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ AutofillProfileSyncDifferenceTracker tracker(GetAutofillTable());
+ for (const syncer::EntityChange& change : entity_changes) {
+ if (change.type() == syncer::EntityChange::ACTION_DELETE) {
+ RETURN_IF_ERROR(tracker.IncorporateRemoteDelete(change.storage_key()));
+ } else {
+ DCHECK(change.data().specifics.has_autofill_profile());
+ std::unique_ptr<AutofillProfile> remote =
+ CreateAutofillProfileFromSpecifics(
+ change.data().specifics.autofill_profile());
+ if (!remote) {
+ DVLOG(2)
+ << "[AUTOFILL SYNC] Invalid remote specifics "
+ << change.data().specifics.autofill_profile().SerializeAsString()
+ << " received from the server in an initial sync.";
+ continue;
+ }
+ RETURN_IF_ERROR(tracker.IncorporateRemoteProfile(std::move(remote)));
+ }
+ }
+
+ return FlushSyncTracker(std::move(metadata_change_list), &tracker);
+}
+
+void AutofillProfileSyncBridge::GetData(StorageKeyList storage_keys,
+ DataCallback callback) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ std::vector<std::unique_ptr<AutofillProfile>> entries;
+ if (!GetAutofillTable()->GetAutofillProfiles(&entries)) {
+ change_processor()->ReportError(
+ {FROM_HERE, "Failed to load entries from table."});
+ return;
+ }
+
+ std::unordered_set<std::string> keys_set(storage_keys.begin(),
+ storage_keys.end());
+ auto batch = std::make_unique<syncer::MutableDataBatch>();
+ for (const std::unique_ptr<AutofillProfile>& entry : entries) {
+ std::string key = GetStorageKeyFromAutofillProfile(*entry);
+ if (base::ContainsKey(keys_set, key)) {
+ batch->Put(key, CreateEntityDataFromAutofillProfile(*entry));
+ }
+ }
+ std::move(callback).Run(std::move(batch));
+}
+
+void AutofillProfileSyncBridge::GetAllDataForDebugging(DataCallback callback) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ std::vector<std::unique_ptr<AutofillProfile>> entries;
+ if (!GetAutofillTable()->GetAutofillProfiles(&entries)) {
+ change_processor()->ReportError(
+ {FROM_HERE, "Failed to load entries from table."});
+ return;
+ }
+
+ auto batch = std::make_unique<syncer::MutableDataBatch>();
+ for (const std::unique_ptr<AutofillProfile>& entry : entries) {
+ batch->Put(GetStorageKeyFromAutofillProfile(*entry),
+ CreateEntityDataFromAutofillProfile(*entry));
+ }
+ std::move(callback).Run(std::move(batch));
+}
+
+void AutofillProfileSyncBridge::ActOnLocalChange(
+ const AutofillProfileChange& change) {
+ DCHECK((change.type() == AutofillProfileChange::REMOVE) ==
+ (change.data_model() == nullptr));
+ if (!change_processor()->IsTrackingMetadata()) {
+ return;
+ }
+ if (change.data_model() &&
+ change.data_model()->record_type() != AutofillProfile::LOCAL_PROFILE) {
+ return;
+ }
+
+ auto metadata_change_list =
+ std::make_unique<syncer::SyncMetadataStoreChangeList>(
+ GetAutofillTable(), syncer::AUTOFILL_PROFILE);
+
+ switch (change.type()) {
+ case AutofillChange::ADD:
+ case AutofillChange::UPDATE:
+ change_processor()->Put(
+ change.key(),
+ CreateEntityDataFromAutofillProfile(*change.data_model()),
+ metadata_change_list.get());
+ break;
+ case AutofillChange::REMOVE:
+ change_processor()->Delete(change.key(), metadata_change_list.get());
+ break;
+ }
+
+ if (Optional<ModelError> error = metadata_change_list->TakeError()) {
+ change_processor()->ReportError(*error);
+ }
+}
+
+base::Optional<syncer::ModelError> AutofillProfileSyncBridge::FlushSyncTracker(
+ std::unique_ptr<MetadataChangeList> metadata_change_list,
+ AutofillProfileSyncDifferenceTracker* tracker) {
+ DCHECK(tracker);
+
+ RETURN_IF_ERROR(tracker->FlushToLocal(
+ base::BindOnce(&AutofillWebDataBackend::NotifyOfMultipleAutofillChanges,
+ base::Unretained(web_data_backend_))));
+
+ std::vector<std::unique_ptr<AutofillProfile>> profiles_to_upload_to_sync;
+ RETURN_IF_ERROR(tracker->FlushToSync(&profiles_to_upload_to_sync));
+ for (const std::unique_ptr<AutofillProfile>& entry :
+ profiles_to_upload_to_sync) {
+ change_processor()->Put(GetStorageKeyFromAutofillProfile(*entry),
+ CreateEntityDataFromAutofillProfile(*entry),
+ metadata_change_list.get());
+ }
+
+ return static_cast<syncer::SyncMetadataStoreChangeList*>(
+ metadata_change_list.get())
+ ->TakeError();
+}
+
+void AutofillProfileSyncBridge::LoadMetadata() {
+ if (!web_data_backend_ || !web_data_backend_->GetDatabase() ||
+ !GetAutofillTable()) {
+ change_processor()->ReportError(
+ {FROM_HERE, "Failed to load AutofillWebDatabase."});
+ return;
+ }
+
+ auto batch = std::make_unique<syncer::MetadataBatch>();
+ if (!GetAutofillTable()->GetAllSyncMetadata(syncer::AUTOFILL_PROFILE,
+ batch.get())) {
+ change_processor()->ReportError(
+ {FROM_HERE, "Failed reading autofill metadata from WebDatabase."});
+ return;
+ }
+ change_processor()->ModelReadyToSync(std::move(batch));
+}
+
+std::string AutofillProfileSyncBridge::GetClientTag(
+ const EntityData& entity_data) {
+ DCHECK(entity_data.specifics.has_autofill_profile());
+ // Must equal to guid of the entry. This is to maintain compatibility with the
+ // previous sync integration (Directory and SyncableService).
+ return entity_data.specifics.autofill_profile().guid();
+}
+
+std::string AutofillProfileSyncBridge::GetStorageKey(
+ const EntityData& entity_data) {
+ DCHECK(entity_data.specifics.has_autofill_profile());
+ return GetStorageKeyFromAutofillProfileSpecifics(
+ entity_data.specifics.autofill_profile());
+}
+
+void AutofillProfileSyncBridge::AutofillProfileChanged(
+ const AutofillProfileChange& change) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ ActOnLocalChange(change);
+}
+
+AutofillTable* AutofillProfileSyncBridge::GetAutofillTable() {
+ return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h
new file mode 100644
index 00000000000..a6e50098fe8
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h
@@ -0,0 +1,117 @@
+// 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_SYNC_BRIDGE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_SYNC_BRIDGE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/scoped_observer.h"
+#include "base/supports_user_data.h"
+#include "base/threading/thread_checker.h"
+#include "components/autofill/core/browser/webdata/autofill_change.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h"
+#include "components/sync/model/model_type_sync_bridge.h"
+
+namespace syncer {
+class MetadataChangeList;
+class ModelError;
+class ModelTypeChangeProcessor;
+} // namespace syncer
+
+namespace autofill {
+
+class AutofillProfileSyncDifferenceTracker;
+class AutofillTable;
+class AutofillWebDataBackend;
+class AutofillWebDataService;
+
+// Sync bridge implementation for AUTOFILL_PROFILE model type. Takes care of
+// propagating local autofill profiles to other clients as well as incorporating
+// profiles coming from other clients; and most notably resolving conflicts and
+// merging duplicates.
+//
+// This is achieved by implementing the interface ModelTypeSyncBridge, which
+// ClientTagBasedModelTypeProcessor will use to interact, ultimately, with the
+// sync server. See
+// https://chromium.googlesource.com/chromium/src/+/lkcr/docs/sync/model_api.md#Implementing-ModelTypeSyncBridge
+// for details.
+class AutofillProfileSyncBridge
+ : public base::SupportsUserData::Data,
+ public AutofillWebDataServiceObserverOnDBSequence,
+ public syncer::ModelTypeSyncBridge {
+ public:
+ AutofillProfileSyncBridge(
+ std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
+ const std::string& app_locale,
+ AutofillWebDataBackend* backend);
+ ~AutofillProfileSyncBridge() override;
+
+ // Constructor that hides dealing with change_processor and also stores the
+ // created bridge within |web_data_service|.
+ static void CreateForWebDataServiceAndBackend(
+ const std::string& app_locale,
+ AutofillWebDataBackend* web_data_backend,
+ AutofillWebDataService* web_data_service);
+
+ // Retrieves the bridge from |web_data_service| which owns it.
+ static syncer::ModelTypeSyncBridge* FromWebDataService(
+ AutofillWebDataService* web_data_service);
+
+ // syncer::ModelTypeSyncBridge implementation.
+ std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
+ override;
+ base::Optional<syncer::ModelError> MergeSyncData(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_data) override;
+ base::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;
+
+ // AutofillWebDataServiceObserverOnDBSequence implementation.
+ void AutofillProfileChanged(const AutofillProfileChange& change) override;
+
+ private:
+ // Returns the table associated with the |web_data_backend_|.
+ AutofillTable* GetAutofillTable();
+
+ // Respond to local autofill profile entry changing by notifying sync of the
+ // changes.
+ void ActOnLocalChange(const AutofillProfileChange& change);
+
+ // Flushes changes accumulated within |tracker| both to local and to sync.
+ base::Optional<syncer::ModelError> FlushSyncTracker(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ AutofillProfileSyncDifferenceTracker* tracker);
+
+ // Synchronously load sync metadata from the autofill table and pass it to the
+ // processor so that it can start tracking changes.
+ void LoadMetadata();
+
+ // The bridge should be used on the same sequence where it is constructed.
+ THREAD_CHECKER(thread_checker_);
+
+ // Locale needed for comparing autofill profiles when resolving conflicts.
+ const std::string app_locale_;
+
+ // AutofillProfileSyncBridge is owned by |web_data_backend_| through
+ // SupportsUserData, so it's guaranteed to outlive |this|.
+ AutofillWebDataBackend* const web_data_backend_;
+
+ ScopedObserver<AutofillWebDataBackend, AutofillProfileSyncBridge>
+ scoped_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncBridge);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_SYNC_BRIDGE_H_
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
new file mode 100644
index 00000000000..361c8f11023
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
@@ -0,0 +1,1256 @@
+// 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_sync_bridge.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/guid.h"
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/time/time.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_profile_sync_util.h"
+#include "components/autofill/core/browser/country_names.h"
+#include "components/autofill/core/browser/test_autofill_clock.h"
+#include "components/autofill/core/browser/webdata/autofill_change.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
+#include "components/autofill/core/common/autofill_constants.h"
+#include "components/sync/base/hash_util.h"
+#include "components/sync/model/data_batch.h"
+#include "components/sync/model/data_type_activation_request.h"
+#include "components/sync/model/entity_data.h"
+#include "components/sync/model/mock_model_type_change_processor.h"
+#include "components/sync/model/sync_data.h"
+#include "components/sync/model/sync_error_factory.h"
+#include "components/sync/model/sync_error_factory_mock.h"
+#include "components/sync/model_impl/client_tag_based_model_type_processor.h"
+#include "components/sync/protocol/sync.pb.h"
+#include "components/webdata/common/web_database.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+using base::ASCIIToUTF16;
+using base::ScopedTempDir;
+using base::UTF16ToUTF8;
+using base::UTF8ToUTF16;
+using sync_pb::AutofillProfileSpecifics;
+using syncer::DataBatch;
+using syncer::EntityChange;
+using syncer::EntityChangeList;
+using syncer::EntityData;
+using syncer::EntityDataPtr;
+using syncer::KeyAndData;
+using syncer::MockModelTypeChangeProcessor;
+using syncer::ModelType;
+using testing::_;
+using testing::DoAll;
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Property;
+using testing::Return;
+using testing::UnorderedElementsAre;
+
+namespace {
+
+// Some guids for testing.
+const char kGuidA[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44A";
+const char kGuidB[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44B";
+const char kGuidC[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44C";
+const char kGuidD[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44D";
+const char kGuidInvalid[] = "EDC609ED-7EEE-4F27-B00C";
+const char kHttpOrigin[] = "http://www.example.com/";
+const char kHttpsOrigin[] = "https://www.example.com/";
+const int kValidityStateBitfield = 1984;
+const char kLocaleString[] = "en-US";
+const base::Time kJune2017 = base::Time::FromDoubleT(1497552271);
+
+class FakeAutofillBackend : public AutofillWebDataBackend {
+ public:
+ FakeAutofillBackend() {}
+ ~FakeAutofillBackend() override {}
+ WebDatabase* GetDatabase() override { return db_; }
+ void AddObserver(
+ autofill::AutofillWebDataServiceObserverOnDBSequence* observer) override {
+ }
+ void RemoveObserver(
+ autofill::AutofillWebDataServiceObserverOnDBSequence* observer) override {
+ }
+ void RemoveExpiredFormElements() override {}
+ void NotifyOfMultipleAutofillChanges() override {}
+ void NotifyThatSyncHasStarted(ModelType model_type) override {}
+ void SetWebDatabase(WebDatabase* db) { db_ = db; }
+
+ private:
+ WebDatabase* db_;
+};
+
+AutofillProfile CreateAutofillProfile(
+ const AutofillProfileSpecifics& specifics) {
+ // As more copying does not hurt in tests, we prefer to use AutofillProfile
+ // instead of std::unique_ptr<AutofillProfile> because of code brevity.
+ return *CreateAutofillProfileFromSpecifics(specifics);
+}
+
+AutofillProfileSpecifics CreateAutofillProfileSpecifics(
+ const AutofillProfile& entry) {
+ // Reuse production code. We do not need EntityData, just take out the
+ // specifics.
+ std::unique_ptr<EntityData> entity_data =
+ CreateEntityDataFromAutofillProfile(entry);
+ return entity_data->specifics.autofill_profile();
+}
+
+AutofillProfileSpecifics CreateAutofillProfileSpecifics(
+ const std::string& guid,
+ const std::string& origin) {
+ AutofillProfileSpecifics specifics;
+ specifics.set_guid(guid);
+ specifics.set_origin(origin);
+ // Make it consistent with the constructor of AutofillProfile constructor (the
+ // clock value is overrided by TestAutofillClock in the test fixture).
+ specifics.set_use_count(1);
+ specifics.set_use_date(kJune2017.ToTimeT());
+ return specifics;
+}
+
+MATCHER_P(HasSpecifics, expected, "") {
+ AutofillProfile arg_profile =
+ CreateAutofillProfile(arg->specifics.autofill_profile());
+ AutofillProfile expected_profile = CreateAutofillProfile(expected);
+ if (!arg_profile.EqualsIncludingUsageStatsForTesting(expected_profile)) {
+ *result_listener << "entry\n[" << arg_profile << "]\n"
+ << "did not match expected\n[" << expected_profile << "]";
+ return false;
+ }
+ return true;
+}
+
+MATCHER_P(WithUsageStats, expected, "") {
+ if (!arg.EqualsIncludingUsageStatsForTesting(expected)) {
+ *result_listener << "entry\n[" << arg << "]\n"
+ << "did not match expected\n[" << expected << "]";
+ return false;
+ }
+ return true;
+}
+
+void ExtractAutofillProfilesFromDataBatch(
+ std::unique_ptr<DataBatch> batch,
+ std::vector<AutofillProfile>* output) {
+ while (batch->HasNext()) {
+ const KeyAndData& data_pair = batch->Next();
+ output->push_back(
+ CreateAutofillProfile(data_pair.second->specifics.autofill_profile()));
+ }
+}
+
+// Returns a profile with all fields set. Contains identical data to the data
+// returned from ConstructCompleteSpecifics().
+AutofillProfile ConstructCompleteProfile() {
+ AutofillProfile profile(kGuidA, kHttpsOrigin);
+
+ profile.set_use_count(7);
+ profile.set_use_date(base::Time::FromTimeT(1423182152));
+
+ profile.SetRawInfo(NAME_FULL, ASCIIToUTF16("John K. Doe, Jr."));
+ profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("K."));
+ profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Doe"));
+
+ profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("user@example.com"));
+ profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("1.800.555.1234"));
+
+ profile.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("123 Fake St.\n"
+ "Apt. 42"));
+ EXPECT_EQ(ASCIIToUTF16("123 Fake St."),
+ profile.GetRawInfo(ADDRESS_HOME_LINE1));
+ EXPECT_EQ(ASCIIToUTF16("Apt. 42"), profile.GetRawInfo(ADDRESS_HOME_LINE2));
+
+ profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Google, Inc."));
+ profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Mountain View"));
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("California"));
+ profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("94043"));
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+ profile.SetRawInfo(ADDRESS_HOME_SORTING_CODE, ASCIIToUTF16("CEDEX"));
+ profile.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY,
+ ASCIIToUTF16("Santa Clara"));
+ profile.set_language_code("en");
+ profile.SetValidityFromBitfieldValue(kValidityStateBitfield);
+ return profile;
+}
+
+// Returns AutofillProfileSpecifics with all Autofill profile fields set.
+// Contains identical data to the data returned from ConstructCompleteProfile().
+AutofillProfileSpecifics ConstructCompleteSpecifics() {
+ AutofillProfileSpecifics specifics;
+
+ specifics.set_guid(kGuidA);
+ specifics.set_origin(kHttpsOrigin);
+ specifics.set_use_count(7);
+ specifics.set_use_date(1423182152);
+
+ specifics.add_name_first("John");
+ specifics.add_name_middle("K.");
+ specifics.add_name_last("Doe");
+ specifics.add_name_full("John K. Doe, Jr.");
+
+ specifics.add_email_address("user@example.com");
+
+ specifics.add_phone_home_whole_number("1.800.555.1234");
+
+ specifics.set_address_home_line1("123 Fake St.");
+ specifics.set_address_home_line2("Apt. 42");
+ specifics.set_address_home_street_address(
+ "123 Fake St.\n"
+ "Apt. 42");
+
+ specifics.set_company_name("Google, Inc.");
+ specifics.set_address_home_city("Mountain View");
+ specifics.set_address_home_state("California");
+ specifics.set_address_home_zip("94043");
+ specifics.set_address_home_country("US");
+ specifics.set_address_home_sorting_code("CEDEX");
+ specifics.set_address_home_dependent_locality("Santa Clara");
+ specifics.set_address_home_language_code("en");
+ specifics.set_validity_state_bitfield(kValidityStateBitfield);
+
+ return specifics;
+}
+
+} // namespace
+
+class AutofillProfileSyncBridgeTest : public testing::Test {
+ public:
+ AutofillProfileSyncBridgeTest() {}
+ ~AutofillProfileSyncBridgeTest() override {}
+
+ void SetUp() override {
+ // Fix a time for implicitly constructed use_dates in AutofillProfile.
+ test_clock_.SetNow(kJune2017);
+ CountryNames::SetLocaleString(kLocaleString);
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ db_.AddTable(&table_);
+ db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase"));
+ backend_.SetWebDatabase(&db_);
+ ResetProcessor();
+ ResetBridge();
+ }
+
+ void ResetProcessor() {
+ real_processor_ =
+ std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
+ syncer::AUTOFILL_PROFILE, /*dump_stack=*/base::DoNothing(),
+ /*commit_only=*/false);
+ mock_processor_.DelegateCallsByDefaultTo(real_processor_.get());
+ }
+
+ void ResetBridge() {
+ bridge_.reset(new AutofillProfileSyncBridge(
+ mock_processor_.CreateForwardingProcessor(), kLocaleString, &backend_));
+ }
+
+ void StartSyncing(
+ const std::vector<AutofillProfileSpecifics>& remote_data = {}) {
+ base::RunLoop loop;
+ syncer::DataTypeActivationRequest request;
+ request.error_handler = base::DoNothing();
+ real_processor_->OnSyncStarting(
+ request,
+ base::BindLambdaForTesting(
+ [&loop](std::unique_ptr<syncer::DataTypeActivationResponse>) {
+ loop.Quit();
+ }));
+ loop.Run();
+
+ // Initialize the processor with initial_sync_done.
+ sync_pb::ModelTypeState state;
+ state.set_initial_sync_done(true);
+ syncer::UpdateResponseDataList initial_updates;
+ for (const AutofillProfileSpecifics& specifics : remote_data) {
+ initial_updates.push_back(SpecificsToUpdateResponse(specifics));
+ }
+ real_processor_->OnUpdateReceived(state, initial_updates);
+ }
+
+ void ApplySyncChanges(const EntityChangeList& changes) {
+ const base::Optional<syncer::ModelError> error = bridge()->ApplySyncChanges(
+ bridge()->CreateMetadataChangeList(), changes);
+ EXPECT_FALSE(error) << error->ToString();
+ }
+
+ void AddAutofillProfilesToTable(
+ const std::vector<AutofillProfile>& profile_list) {
+ for (const auto& profile : profile_list) {
+ table_.AddAutofillProfile(profile);
+ }
+ }
+
+ std::vector<AutofillProfile> GetAllLocalData() {
+ std::vector<AutofillProfile> data;
+ // Perform an async call synchronously for testing.
+ base::RunLoop loop;
+ bridge()->GetAllDataForDebugging(base::BindLambdaForTesting(
+ [&loop, &data](std::unique_ptr<DataBatch> batch) {
+ ExtractAutofillProfilesFromDataBatch(std::move(batch), &data);
+ loop.Quit();
+ }));
+ loop.Run();
+ return data;
+ }
+
+ EntityDataPtr SpecificsToEntity(const AutofillProfileSpecifics& specifics) {
+ EntityData data;
+ *data.specifics.mutable_autofill_profile() = specifics;
+ data.client_tag_hash = syncer::GenerateSyncableHash(
+ syncer::AUTOFILL_PROFILE, bridge()->GetClientTag(data));
+ return data.PassToPtr();
+ }
+
+ syncer::UpdateResponseData SpecificsToUpdateResponse(
+ const AutofillProfileSpecifics& specifics) {
+ syncer::UpdateResponseData data;
+ data.entity = SpecificsToEntity(specifics);
+ return data;
+ }
+
+ AutofillProfileSyncBridge* bridge() { return bridge_.get(); }
+
+ syncer::MockModelTypeChangeProcessor& mock_processor() {
+ return mock_processor_;
+ }
+
+ AutofillTable* table() { return &table_; }
+
+ FakeAutofillBackend* backend() { return &backend_; }
+
+ private:
+ autofill::TestAutofillClock test_clock_;
+ ScopedTempDir temp_dir_;
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ FakeAutofillBackend backend_;
+ AutofillTable table_;
+ WebDatabase db_;
+ testing::NiceMock<MockModelTypeChangeProcessor> mock_processor_;
+ std::unique_ptr<syncer::ClientTagBasedModelTypeProcessor> real_processor_;
+ std::unique_ptr<AutofillProfileSyncBridge> bridge_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncBridgeTest);
+};
+
+TEST_F(AutofillProfileSyncBridgeTest, AutofillProfileChanged_Added) {
+ StartSyncing({});
+
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane"));
+ AutofillProfileChange change(AutofillProfileChange::ADD, kGuidA, &local);
+
+ EXPECT_CALL(
+ mock_processor(),
+ Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local)), _));
+
+ bridge()->AutofillProfileChanged(change);
+}
+
+// Language code in autofill profiles should be synced to the server.
+TEST_F(AutofillProfileSyncBridgeTest,
+ AutofillProfileChanged_Added_LanguageCodePropagates) {
+ StartSyncing({});
+
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.set_language_code("en");
+ AutofillProfileChange change(AutofillProfileChange::ADD, kGuidA, &local);
+
+ EXPECT_CALL(
+ mock_processor(),
+ Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local)), _));
+
+ bridge()->AutofillProfileChanged(change);
+}
+
+// Validity state bitfield in autofill profiles should be synced to the server.
+TEST_F(AutofillProfileSyncBridgeTest,
+ AutofillProfileChanged_Added_LocalValidityBitfieldPropagates) {
+ StartSyncing({});
+
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.SetValidityFromBitfieldValue(kValidityStateBitfield);
+ AutofillProfileChange change(AutofillProfileChange::ADD, kGuidA, &local);
+
+ EXPECT_CALL(
+ mock_processor(),
+ Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local)), _));
+
+ bridge()->AutofillProfileChanged(change);
+}
+
+// Local updates should be properly propagated to the server.
+TEST_F(AutofillProfileSyncBridgeTest, AutofillProfileChanged_Updated) {
+ StartSyncing({});
+
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane"));
+ AutofillProfileChange change(AutofillProfileChange::UPDATE, kGuidA, &local);
+
+ EXPECT_CALL(
+ mock_processor(),
+ Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local)), _));
+
+ bridge()->AutofillProfileChanged(change);
+}
+
+// Usage stats should be updated by the client.
+TEST_F(AutofillProfileSyncBridgeTest,
+ AutofillProfileChanged_Updated_UsageStatsOverwrittenByClient) {
+ // Remote data has a profile with usage stats.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ remote.set_address_home_language_code("en");
+ remote.set_use_count(9);
+ remote.set_use_date(25);
+
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(),
+ ElementsAre(WithUsageStats(CreateAutofillProfile(remote))));
+
+ // Update to the usage stats for that profile.
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.set_language_code("en");
+ local.set_use_count(10U);
+ local.set_use_date(base::Time::FromTimeT(30));
+ AutofillProfileChange change(AutofillProfileChange::UPDATE, kGuidA, &local);
+
+ EXPECT_CALL(
+ mock_processor(),
+ Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local)), _));
+
+ bridge()->AutofillProfileChanged(change);
+}
+
+// Server profile updates should be ignored.
+TEST_F(AutofillProfileSyncBridgeTest,
+ AutofillProfileChanged_Updated_IgnoreServerProfiles) {
+ StartSyncing({});
+
+ AutofillProfile server_profile(AutofillProfile::SERVER_PROFILE, "server-id");
+ AutofillProfileChange change(AutofillProfileChange::UPDATE,
+ server_profile.guid(), &server_profile);
+
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ // Should not crash.
+ bridge()->AutofillProfileChanged(change);
+}
+
+TEST_F(AutofillProfileSyncBridgeTest, AutofillProfileChanged_Deleted) {
+ StartSyncing({});
+
+ AutofillProfileChange change(AutofillProfileChange::REMOVE, kGuidB, nullptr);
+ EXPECT_CALL(mock_processor(), Delete(kGuidB, _));
+
+ bridge()->AutofillProfileChanged(change);
+}
+
+TEST_F(AutofillProfileSyncBridgeTest, GetAllDataForDebugging) {
+ AutofillProfile local1 = AutofillProfile(kGuidA, kHttpsOrigin);
+ local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ AutofillProfile local2 = AutofillProfile(kGuidB, kHttpsOrigin);
+ local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
+ local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st"));
+ AddAutofillProfilesToTable({local1, local2});
+
+ EXPECT_THAT(GetAllLocalData(), UnorderedElementsAre(local1, local2));
+}
+
+TEST_F(AutofillProfileSyncBridgeTest, GetData) {
+ AutofillProfile local1 = AutofillProfile(kGuidA, kHttpsOrigin);
+ local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ AutofillProfile local2 = AutofillProfile(kGuidB, kHttpsOrigin);
+ local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
+ local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st"));
+ AddAutofillProfilesToTable({local1, local2});
+
+ std::vector<AutofillProfile> data;
+ base::RunLoop loop;
+ bridge()->GetData({kGuidA},
+ base::BindLambdaForTesting(
+ [&loop, &data](std::unique_ptr<DataBatch> batch) {
+ ExtractAutofillProfilesFromDataBatch(std::move(batch),
+ &data);
+ loop.Quit();
+ }));
+ loop.Run();
+
+ EXPECT_THAT(data, ElementsAre(local1));
+}
+
+TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData) {
+ AutofillProfile local1 = AutofillProfile(kGuidA, kHttpOrigin);
+ local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ AutofillProfile local2 = AutofillProfile(kGuidB, std::string());
+ local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
+ local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st"));
+ AddAutofillProfilesToTable({local1, local2});
+
+ AutofillProfileSpecifics remote1 =
+ CreateAutofillProfileSpecifics(kGuidC, kHttpsOrigin);
+ remote1.add_name_first("Jane");
+ AutofillProfileSpecifics remote2 =
+ CreateAutofillProfileSpecifics(kGuidD, kSettingsOrigin);
+ remote2.add_name_first("Harry");
+ // This one will have the name and origin updated.
+ AutofillProfileSpecifics remote3 =
+ CreateAutofillProfileSpecifics(kGuidB, kSettingsOrigin);
+ remote3.add_name_first("Tom Doe");
+
+ EXPECT_CALL(
+ mock_processor(),
+ Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local1)), _));
+ EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+
+ StartSyncing({remote1, remote2, remote3});
+
+ EXPECT_THAT(GetAllLocalData(),
+ UnorderedElementsAre(local1, CreateAutofillProfile(remote1),
+ CreateAutofillProfile(remote2),
+ CreateAutofillProfile(remote3)));
+}
+
+// Ensure that all profile fields are able to be synced up from the client to
+// the server.
+TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData_SyncAllFieldsToServer) {
+ AutofillProfile local = ConstructCompleteProfile();
+ AddAutofillProfilesToTable({local});
+
+ // This complete profile is fully uploaded to sync.
+ EXPECT_CALL(mock_processor(),
+ Put(_, HasSpecifics(ConstructCompleteSpecifics()), _));
+ StartSyncing({});
+
+ // No changes locally.
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(WithUsageStats(local)));
+}
+
+// Ensure that all profile fields are able to be synced down from the server to
+// the client (and nothing gets uploaded back).
+TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData_SyncAllFieldsToClient) {
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({ConstructCompleteSpecifics()});
+
+ EXPECT_THAT(GetAllLocalData(),
+ ElementsAre(WithUsageStats(ConstructCompleteProfile())));
+}
+
+TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData_IdenticalProfiles) {
+ AutofillProfile local1 = AutofillProfile(kGuidA, kHttpOrigin);
+ local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ AutofillProfile local2 = AutofillProfile(kGuidB, kSettingsOrigin);
+ local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
+ local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st"));
+ AddAutofillProfilesToTable({local1, local2});
+
+ // The synced profiles are identical to the local ones, except that the guids
+ // are different.
+ AutofillProfileSpecifics remote1 =
+ CreateAutofillProfileSpecifics(kGuidC, kHttpsOrigin);
+ remote1.add_name_first("John");
+ remote1.set_address_home_street_address("1 1st st");
+ AutofillProfileSpecifics remote2 =
+ CreateAutofillProfileSpecifics(kGuidD, kHttpsOrigin);
+ remote2.add_name_first("Tom");
+ remote2.set_address_home_street_address("2 2nd st");
+
+ // Both remote profiles win, only the verified origin is taken over for the
+ // second profile.
+ AutofillProfileSpecifics merged2(remote2);
+ merged2.set_origin(kSettingsOrigin);
+ EXPECT_CALL(mock_processor(), Put(kGuidD, HasSpecifics(merged2), _));
+
+ StartSyncing({remote1, remote2});
+
+ EXPECT_THAT(GetAllLocalData(),
+ UnorderedElementsAre(CreateAutofillProfile(remote1),
+ CreateAutofillProfile(merged2)));
+}
+
+TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData_NonSimilarProfiles) {
+ AutofillProfile local = ConstructCompleteProfile();
+ local.set_guid(kGuidA);
+ local.SetRawInfo(NAME_FULL, ASCIIToUTF16("John K. Doe, Jr."));
+ local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ local.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("K."));
+ local.SetRawInfo(NAME_LAST, ASCIIToUTF16("Doe"));
+ AddAutofillProfilesToTable({local});
+
+ // The remote profile are not similar as the names are different (all other
+ // fields except for guids are identical).
+ AutofillProfileSpecifics remote = ConstructCompleteSpecifics();
+ remote.set_guid(kGuidB);
+ remote.set_name_full(0, "Jane T. Roe, Sr.");
+ remote.set_name_first(0, "Jane");
+ remote.set_name_middle(0, "T.");
+ remote.set_name_last(0, "Roe");
+
+ // The profiles are not similar enough and thus do not get merged.
+ // Expect the local one being synced up and the remote one being added to the
+ // local database.
+ EXPECT_CALL(
+ mock_processor(),
+ Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local)), _));
+ EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+
+ StartSyncing({remote});
+
+ EXPECT_THAT(GetAllLocalData(),
+ UnorderedElementsAre(local, CreateAutofillProfile(remote)));
+}
+
+TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData_SimilarProfiles) {
+ AutofillProfile local1 = AutofillProfile(kGuidA, kHttpOrigin);
+ local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ local1.set_use_count(27);
+ AutofillProfile local2 = AutofillProfile(kGuidB, kSettingsOrigin);
+ local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
+ local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st"));
+ AddAutofillProfilesToTable({local1, local2});
+
+ // The synced profiles are identical to the local ones, except that the guids
+ // and use_count values are different.
+ AutofillProfileSpecifics remote1 =
+ CreateAutofillProfileSpecifics(kGuidC, kHttpsOrigin);
+ remote1.add_name_first("John");
+ remote1.set_address_home_street_address("1 1st st");
+ remote1.set_company_name("Frobbers, Inc.");
+ remote1.set_use_count(13);
+ AutofillProfileSpecifics remote2 =
+ CreateAutofillProfileSpecifics(kGuidD, kHttpsOrigin);
+ remote2.add_name_first("Tom");
+ remote2.set_address_home_street_address("2 2nd st");
+ remote2.set_company_name("Fizzbang, LLC.");
+ remote2.set_use_count(4);
+
+ // The first profile should have its origin updated.
+ // The second profile should remain as-is, because an unverified profile
+ // should never overwrite a verified one.
+ AutofillProfileSpecifics merged1(remote1);
+ merged1.set_origin(kHttpOrigin);
+ // TODO(jkrcal): This is taken over from the previous test suite without any
+ // reasoning why this happens. This indeed happens, deep in
+ // AutofillProfileComparator when merging profiles both without NAME_FULL, we
+ // obtain a profile with NAME_FULL. Not sure if intended.
+ merged1.add_name_full("John");
+ // Merging two profile takes their max use count.
+ merged1.set_use_count(27);
+
+ // Expect updating the first (merged) profile and adding the second local one.
+ EXPECT_CALL(mock_processor(), Put(kGuidC, HasSpecifics(merged1), _));
+ EXPECT_CALL(
+ mock_processor(),
+ Put(kGuidB, HasSpecifics(CreateAutofillProfileSpecifics(local2)), _));
+ EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+
+ StartSyncing({remote1, remote2});
+
+ EXPECT_THAT(GetAllLocalData(),
+ UnorderedElementsAre(
+ WithUsageStats(CreateAutofillProfile(merged1)), local2,
+ WithUsageStats(CreateAutofillProfile(remote2))));
+}
+
+// TODO(jkrcal): All the MergeSimilarProfiles_* tests need some diff in Info to
+// trigger the merge similar code path (we create the diff using phone number).
+// Otherwise, we trigger the merge same code path and none of the tests pass. Is
+// it desired?
+
+// Tests that MergeSimilarProfiles keeps the most recent use date of the two
+// profiles being merged.
+TEST_F(AutofillProfileSyncBridgeTest,
+ MergeSyncData_SimilarProfiles_OlderUseDate) {
+ // Different guids, same origin, difference in the phone number.
+ AutofillProfile local(kGuidA, kHttpOrigin);
+ local.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("650234567"));
+ local.set_use_date(base::Time::FromTimeT(30));
+ AddAutofillProfilesToTable({local});
+
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin);
+ // |local| has a more recent use date.
+ remote.set_use_date(25);
+
+ // The use date of |local| should replace the use date of |remote|.
+ AutofillProfileSpecifics merged(remote);
+ merged.set_use_date(30);
+ merged.add_phone_home_whole_number("650234567");
+ EXPECT_CALL(mock_processor(), Put(kGuidB, HasSpecifics(merged), _));
+
+ StartSyncing({remote});
+}
+
+// Tests that MergeSimilarProfiles keeps the most recent use date of the two
+// profiles being merged.
+TEST_F(AutofillProfileSyncBridgeTest,
+ MergeSyncData_SimilarProfiles_NewerUseDate) {
+ // Different guids, same origin, difference in the phone number.
+ AutofillProfile local(kGuidA, kHttpOrigin);
+ local.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("650234567"));
+ local.set_use_date(base::Time::FromTimeT(30));
+ AddAutofillProfilesToTable({local});
+
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin);
+ // |remote| has a more recent use date.
+ remote.set_use_date(35);
+
+ // The use date of |local| should _not_ replace the use date of |remote|.
+ AutofillProfileSpecifics merged(remote);
+ merged.add_phone_home_whole_number("650234567");
+ EXPECT_CALL(mock_processor(), Put(kGuidB, HasSpecifics(merged), _));
+
+ StartSyncing({remote});
+}
+
+// Tests that MergeSimilarProfiles saves the max of the use counts of the two
+// profiles in |remote|.
+TEST_F(AutofillProfileSyncBridgeTest,
+ MergeSyncData_SimilarProfiles_NonZeroUseCounts) {
+ // Different guids, same origin, difference in the phone number.
+ AutofillProfile local(kGuidA, kHttpOrigin);
+ local.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("650234567"));
+ local.set_use_count(12);
+ AddAutofillProfilesToTable({local});
+
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin);
+ remote.set_use_count(5);
+
+ // The use count of |local| should replace the use count of |remote|.
+ AutofillProfileSpecifics merged(remote);
+ merged.set_use_count(12);
+ merged.add_phone_home_whole_number("650234567");
+ EXPECT_CALL(mock_processor(), Put(kGuidB, HasSpecifics(merged), _));
+
+ StartSyncing({remote});
+}
+
+// Tests that when merging similar profiles for initial sync, we add the
+// additional information of |local| into |remote|.
+TEST_F(AutofillProfileSyncBridgeTest,
+ MergeSyncData_SimilarProfiles_LocalOriginPreserved) {
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("650234567"));
+ AddAutofillProfilesToTable({local});
+
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin);
+ remote.set_address_home_language_code("en");
+
+ // Expect that the resulting merged profile is written back to sync and that
+ // it has the phone number and origin from |local|.
+ AutofillProfileSpecifics merged(remote);
+ merged.set_origin(kHttpsOrigin);
+ merged.add_phone_home_whole_number("650234567");
+ // TODO(jkrcal): Is this expected that language code gets deleted? Not
+ // explicitly covered by previous tests but happens.
+ merged.set_address_home_language_code("");
+ EXPECT_CALL(mock_processor(), Put(kGuidB, HasSpecifics(merged), _));
+
+ StartSyncing({remote});
+}
+
+// Sync data without origin should not overwrite existing origin in local
+// autofill profile.
+TEST_F(AutofillProfileSyncBridgeTest,
+ MergeSyncData_SimilarProfiles_LocalExistingOriginPreserved) {
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ AddAutofillProfilesToTable({local});
+
+ // Remote data does not have an origin value.
+ AutofillProfileSpecifics remote = CreateAutofillProfileSpecifics(kGuidA, "");
+ remote.clear_origin();
+ remote.add_name_first("John");
+ ASSERT_FALSE(remote.has_origin());
+
+ // Expect no sync events to add origin to the remote data.
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({remote});
+
+ // Expect the local autofill profile to still have an origin after sync.
+ AutofillProfile merged(local);
+ merged.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
+}
+
+// Ensure that no Sync events are generated to fill in missing origins from Sync
+// with explicitly present empty ones. This ensures that the migration to add
+// origins to profiles does not generate lots of needless Sync updates.
+TEST_F(AutofillProfileSyncBridgeTest,
+ MergeSyncData_SimilarProfiles_LocalMissingOriginPreserved) {
+ AutofillProfile local = AutofillProfile(kGuidA, std::string());
+ local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ AddAutofillProfilesToTable({local});
+
+ // Create a Sync profile identical to |local|, except with no origin set.
+ AutofillProfileSpecifics remote = CreateAutofillProfileSpecifics(kGuidA, "");
+ remote.clear_origin();
+ remote.add_name_first("John");
+ ASSERT_FALSE(remote.has_origin());
+
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(local));
+}
+
+TEST_F(AutofillProfileSyncBridgeTest, ApplySyncChanges) {
+ AutofillProfile local = AutofillProfile(kGuidA, kHttpsOrigin);
+ AddAutofillProfilesToTable({local});
+
+ StartSyncing({});
+
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin);
+ remote.add_name_first("Jane");
+
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0);
+
+ ApplySyncChanges(
+ {EntityChange::CreateDelete(kGuidA),
+ EntityChange::CreateAdd(kGuidB, SpecificsToEntity(remote))});
+
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote)));
+}
+
+// Ensure that entries with invalid specifics are ignored.
+TEST_F(AutofillProfileSyncBridgeTest, ApplySyncChanges_OmitsInvalidSpecifics) {
+ StartSyncing({});
+
+ AutofillProfileSpecifics remote_valid =
+ CreateAutofillProfileSpecifics(kGuidA, std::string());
+ AutofillProfileSpecifics remote_invalid =
+ CreateAutofillProfileSpecifics(kGuidInvalid, std::string());
+
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ ApplySyncChanges(
+ {EntityChange::CreateAdd(kGuidA, SpecificsToEntity(remote_valid)),
+ EntityChange::CreateAdd(kGuidInvalid,
+ SpecificsToEntity(remote_invalid))});
+
+ EXPECT_THAT(GetAllLocalData(),
+ ElementsAre(CreateAutofillProfile(remote_valid)));
+}
+
+// Verifies that setting the street address field also sets the (deprecated)
+// address line 1 and line 2 fields.
+TEST_F(AutofillProfileSyncBridgeTest, StreetAddress_SplitAutomatically) {
+ AutofillProfile local;
+ local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("123 Example St.\n"
+ "Apt. 42"));
+ EXPECT_EQ(ASCIIToUTF16("123 Example St."),
+ local.GetRawInfo(ADDRESS_HOME_LINE1));
+ EXPECT_EQ(ASCIIToUTF16("Apt. 42"), local.GetRawInfo(ADDRESS_HOME_LINE2));
+
+ // The same does _not_ work for profile specifics.
+ AutofillProfileSpecifics remote;
+ remote.set_address_home_street_address(
+ "123 Example St.\n"
+ "Apt. 42");
+ EXPECT_FALSE(remote.has_address_home_line1());
+ EXPECT_FALSE(remote.has_address_home_line2());
+}
+
+// Verifies that setting the (deprecated) address line 1 and line 2 fields also
+// sets the street address.
+TEST_F(AutofillProfileSyncBridgeTest, StreetAddress_JointAutomatically) {
+ AutofillProfile local;
+ local.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("123 Example St."));
+ local.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("Apt. 42"));
+ EXPECT_EQ(ASCIIToUTF16("123 Example St.\n"
+ "Apt. 42"),
+ local.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS));
+
+ // The same does _not_ work for profile specifics.
+ AutofillProfileSpecifics remote;
+ remote.set_address_home_line1("123 Example St.");
+ remote.set_address_home_line2("Apt. 42");
+ EXPECT_FALSE(remote.has_address_home_street_address());
+}
+
+// Ensure that the street address field takes precedence over the (deprecated)
+// address line 1 and line 2 fields, even though these are expected to always be
+// in sync in practice.
+TEST_F(AutofillProfileSyncBridgeTest,
+ RemoteWithSameGuid_StreetAddress_TakesPrecedenceOverAddressLines) {
+ // Create remote entry with conflicting address data in the street address
+ // field vs. the address line 1 and address line 2 fields.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ remote.set_address_home_line1("123 Example St.");
+ remote.set_address_home_line2("Apt. 42");
+ remote.set_address_home_street_address(
+ "456 El Camino Real\n"
+ "Suite #1337");
+
+ StartSyncing({remote});
+
+ // Verify that full street address takes precedence over address lines.
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS,
+ ASCIIToUTF16("456 El Camino Real\n"
+ "Suite #1337"));
+ local.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("456 El Camino Real"));
+ local.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("Suite #1337"));
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(local));
+}
+
+// Ensure that no Sync events are generated to fill in missing street address
+// fields from Sync with explicitly present ones identical to the data stored in
+// the line1 and line2 fields. This ensures that the migration to add the
+// street address field to profiles does not generate lots of needless Sync
+// updates.
+TEST_F(AutofillProfileSyncBridgeTest,
+ RemoteWithSameGuid_StreetAddress_NoUpdateToEmptyStreetAddressSyncedUp) {
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("123 Example St.\n"
+ "Apt. 42"));
+ AddAutofillProfilesToTable({local});
+
+ // Create a Sync profile identical to |profile|, except without street address
+ // explicitly set.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ remote.set_address_home_line1("123 Example St.");
+ remote.set_address_home_line2("Apt. 42");
+
+ // No update to sync, no change in local data.
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(local));
+}
+
+// Missing language code field should not generate sync events.
+TEST_F(AutofillProfileSyncBridgeTest,
+ RemoteWithSameGuid_LanguageCode_MissingCodesNoSync) {
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ ASSERT_TRUE(local.language_code().empty());
+ AddAutofillProfilesToTable({local});
+
+ // Remote data does not have a language code value.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ ASSERT_FALSE(remote.has_address_home_language_code());
+
+ // No update to sync, no change in local data.
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(local));
+}
+
+// Empty language code should be overwritten by sync.
+TEST_F(AutofillProfileSyncBridgeTest,
+ RemoteWithSameGuid_LanguageCode_ExistingRemoteWinsOverMissingLocal) {
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ ASSERT_TRUE(local.language_code().empty());
+ AddAutofillProfilesToTable({local});
+
+ // Remote data has "en" language code.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ remote.set_address_home_language_code("en");
+
+ // No update to sync, remote language code overwrites the empty local one.
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote)));
+}
+
+// Local language code should be overwritten by remote one.
+TEST_F(AutofillProfileSyncBridgeTest,
+ RemoteWithSameGuid_LanguageCode_ExistingRemoteWinsOverExistingLocal) {
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.set_language_code("de");
+ AddAutofillProfilesToTable({local});
+
+ // Remote data has "en" language code.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ remote.set_address_home_language_code("en");
+
+ // No update to sync, remote language code overwrites the local one.
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote)));
+}
+
+// Sync data without language code should not overwrite existing language code
+// in local autofill profile.
+TEST_F(AutofillProfileSyncBridgeTest,
+ RemoteWithSameGuid_LanguageCode_ExistingLocalWinsOverMissingRemote) {
+ // Local autofill profile has "en" language code.
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.set_language_code("en");
+ AddAutofillProfilesToTable({local});
+
+ // Remote data does not have a language code value.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ remote.add_name_first("John");
+ ASSERT_FALSE(remote.has_address_home_language_code());
+
+ // Expect local autofill profile to still have "en" language code after
+ AutofillProfile merged(local);
+ merged.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+
+ // No update to sync, remote language code overwrites the local one.
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
+}
+
+// Missing validity state bitifield should not generate sync events.
+TEST_F(AutofillProfileSyncBridgeTest,
+ RemoteWithSameGuid_ValidityState_DefaultValueNoSync) {
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ ASSERT_EQ(0, local.GetValidityBitfieldValue());
+ AddAutofillProfilesToTable({local});
+
+ // Remote data does not have a validity state bitfield value.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ ASSERT_FALSE(remote.has_validity_state_bitfield());
+
+ // No update to sync, no change in local data.
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(local));
+}
+
+// Default validity state bitfield should be overwritten by sync.
+TEST_F(AutofillProfileSyncBridgeTest,
+ RemoteWithSameGuid_ValidityState_ExistingRemoteWinsOverMissingLocal) {
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ ASSERT_EQ(0, local.GetValidityBitfieldValue());
+ AddAutofillProfilesToTable({local});
+
+ // Remote data has a non default validity state bitfield value.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ remote.set_validity_state_bitfield(kValidityStateBitfield);
+ ASSERT_TRUE(remote.has_validity_state_bitfield());
+
+ // No update to sync, the validity bitfield should be stored to local.
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote)));
+}
+
+// Local validity state bitfield should be overwritten by sync.
+TEST_F(AutofillProfileSyncBridgeTest,
+ RemoteWithSameGuid_ValidityState_ExistingRemoteWinsOverExistingLocal) {
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.SetValidityFromBitfieldValue(kValidityStateBitfield + 1);
+ AddAutofillProfilesToTable({local});
+
+ // Remote data has a non default validity state bitfield value.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ remote.set_validity_state_bitfield(kValidityStateBitfield);
+ ASSERT_TRUE(remote.has_validity_state_bitfield());
+
+ // No update to sync, the remote validity bitfield should overwrite local.
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote)));
+}
+
+// Sync data without a default validity state bitfield should not overwrite
+// an existing validity state bitfield in local autofill profile.
+TEST_F(AutofillProfileSyncBridgeTest,
+ RemoteWithSameGuid_ValidityState_ExistingLocalWinsOverMissingRemote) {
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.SetValidityFromBitfieldValue(kValidityStateBitfield);
+ AddAutofillProfilesToTable({local});
+
+ // Remote data has a non default validity state bitfield value.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ remote.add_name_first("John");
+ ASSERT_FALSE(remote.has_validity_state_bitfield());
+
+ // Expect local autofill profile to still have the validity state after.
+ AutofillProfile merged(local);
+ merged.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+
+ // No update to sync, the local validity bitfield should stay untouched.
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
+}
+
+// Missing full name field should not generate sync events.
+TEST_F(AutofillProfileSyncBridgeTest,
+ RemoteWithSameGuid_FullName_MissingValueNoSync) {
+ // Local autofill profile has an empty full name.
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ AddAutofillProfilesToTable({local});
+
+ // Remote data does not have a full name value.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ remote.add_name_first(std::string("John"));
+
+ // No update to sync, no change in local data.
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(local));
+}
+
+TEST_F(AutofillProfileSyncBridgeTest,
+ RemoteWithSameGuid_FullName_ExistingLocalWinsOverMissingRemote) {
+ // Local autofill profile has a full name.
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.SetRawInfo(NAME_FULL, ASCIIToUTF16("John Jacob Smith, Jr"));
+ AddAutofillProfilesToTable({local});
+
+ // Remote data does not have a full name value.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ remote.add_name_first(std::string("John"));
+ remote.add_name_middle(std::string("Jacob"));
+ remote.add_name_last(std::string("Smith"));
+
+ // Expect local autofill profile to still have the same full name after.
+ AutofillProfile merged(local);
+ merged.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ merged.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Jacob"));
+ merged.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith"));
+
+ // No update to sync, no change in local data.
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
+}
+
+// Missing use_count/use_date fields should not generate sync events.
+TEST_F(AutofillProfileSyncBridgeTest,
+ RemoteWithSameGuid_UsageStats_MissingValueNoSync) {
+ // Local autofill profile has 0 for use_count/use_date.
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.set_language_code("en");
+ local.set_use_count(0);
+ local.set_use_date(base::Time());
+ AddAutofillProfilesToTable({local});
+
+ // Remote data does not have use_count/use_date.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ remote.clear_use_count();
+ remote.clear_use_date();
+ remote.set_address_home_language_code("en");
+
+ // No update to sync, no change in local data.
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(WithUsageStats(local)));
+}
+
+struct UpdatesUsageStatsTestCase {
+ size_t local_use_count;
+ base::Time local_use_date;
+ size_t remote_use_count;
+ int remote_use_date;
+ size_t merged_use_count;
+ base::Time merged_use_date;
+};
+
+class AutofillProfileSyncBridgeUpdatesUsageStatsTest
+ : public AutofillProfileSyncBridgeTest,
+ public testing::WithParamInterface<UpdatesUsageStatsTestCase> {
+ public:
+ AutofillProfileSyncBridgeUpdatesUsageStatsTest() {}
+ ~AutofillProfileSyncBridgeUpdatesUsageStatsTest() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncBridgeUpdatesUsageStatsTest);
+};
+
+TEST_P(AutofillProfileSyncBridgeUpdatesUsageStatsTest, UpdatesUsageStats) {
+ auto test_case = GetParam();
+
+ // Local data has usage stats.
+ AutofillProfile local(kGuidA, kHttpsOrigin);
+ local.set_language_code("en");
+ local.set_use_count(test_case.local_use_count);
+ local.set_use_date(test_case.local_use_date);
+ ASSERT_EQ(test_case.local_use_count, local.use_count());
+ ASSERT_EQ(test_case.local_use_date, local.use_date());
+ AddAutofillProfilesToTable({local});
+
+ // Remote data has usage stats.
+ AutofillProfileSpecifics remote =
+ CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
+ remote.set_address_home_language_code("en");
+ remote.set_use_count(test_case.remote_use_count);
+ remote.set_use_date(test_case.remote_use_date);
+ ASSERT_TRUE(remote.has_use_count());
+ ASSERT_TRUE(remote.has_use_date());
+
+ // Expect the local autofill profile to have usage stats after sync.
+ AutofillProfile merged(local);
+ merged.set_use_count(test_case.merged_use_count);
+ merged.set_use_date(test_case.merged_use_date);
+
+ // Expect no changes to remote data.
+ EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0);
+
+ StartSyncing({remote});
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(WithUsageStats(merged)));
+}
+
+INSTANTIATE_TEST_CASE_P(
+ AutofillProfileSyncBridgeTest,
+ AutofillProfileSyncBridgeUpdatesUsageStatsTest,
+ testing::Values(
+ // Local profile with default stats.
+ UpdatesUsageStatsTestCase{
+ /*local_use_count=*/0U,
+ /*local_use_date=*/base::Time(),
+ /*remote_use_count=*/9U,
+ /*remote_use_date=*/4321,
+ /*merged_use_count=*/9U,
+ /*merged_use_date=*/base::Time::FromTimeT(4321)},
+ // Local profile has older stats than the server.
+ UpdatesUsageStatsTestCase{
+ /*local_use_count=*/3U,
+ /*local_use_date=*/base::Time::FromTimeT(1234),
+ /*remote_use_count=*/9U, /*remote_use_date=*/4321,
+ /*merged_use_count=*/9U,
+ /*merged_use_date=*/base::Time::FromTimeT(4321)},
+ // Local profile has newer stats than the server
+ UpdatesUsageStatsTestCase{
+ /*local_use_count=*/10U,
+ /*local_use_date=*/base::Time::FromTimeT(9999),
+ /*remote_use_count=*/9U, /*remote_use_date=*/4321,
+ /*merged_use_count=*/9U,
+ /*merged_use_date=*/base::Time::FromTimeT(4321)}));
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc
new file mode 100644
index 00000000000..c5d3a8eebb4
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.cc
@@ -0,0 +1,296 @@
+// 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_sync_difference_tracker.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_profile_comparator.h"
+#include "components/autofill/core/browser/autofill_profile_sync_util.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/sync/model/model_error.h"
+
+namespace autofill {
+
+using base::Optional;
+using syncer::ModelError;
+
+AutofillProfileSyncDifferenceTracker::AutofillProfileSyncDifferenceTracker(
+ AutofillTable* table)
+ : table_(table) {}
+
+AutofillProfileSyncDifferenceTracker::~AutofillProfileSyncDifferenceTracker() {}
+
+Optional<ModelError>
+AutofillProfileSyncDifferenceTracker::IncorporateRemoteProfile(
+ std::unique_ptr<AutofillProfile> remote) {
+ const std::string remote_storage_key =
+ GetStorageKeyFromAutofillProfile(*remote);
+
+ if (!GetLocalOnlyEntries()) {
+ return ModelError(FROM_HERE, "Failed reading from WebDatabase.");
+ }
+
+ Optional<AutofillProfile> local_with_same_storage_key =
+ ReadEntry(remote_storage_key);
+
+ if (local_with_same_storage_key) {
+ // The remote profile already exists locally with the same key. Update
+ // the local entry with remote data.
+ std::unique_ptr<AutofillProfile> updated =
+ std::make_unique<AutofillProfile>(local_with_same_storage_key.value());
+ // We ignore remote updates to a verified profile because we want to keep
+ // the exact version that the user edited by hand.
+ if (local_with_same_storage_key->IsVerified() && !remote->IsVerified()) {
+ return base::nullopt;
+ }
+ updated->OverwriteDataFrom(*remote);
+ // TODO(jkrcal): if |updated| deviates from |remote|, we should sync it back
+ // up. The only way |updated| can differ is having some extra fields
+ // compared to |remote|. Thus, this cannot lead to an infinite loop of
+ // commits from two clients as each commit decreases the set of empty
+ // fields. This invariant depends on the implementation of
+ // OverwriteDataFrom() and thus should be enforced by a DCHECK.
+
+ if (!updated->EqualsForSyncPurposes(*local_with_same_storage_key)) {
+ // We need to write back locally new changes in this entry.
+ update_to_local_.push_back(std::move(updated));
+ }
+ GetLocalOnlyEntries()->erase(remote_storage_key);
+ return base::nullopt;
+ }
+
+ // Check if profile appears under a different storage key to be de-duplicated.
+ for (const auto& pair : *GetLocalOnlyEntries()) {
+ const std::string& local_storage_key = pair.first;
+ const AutofillProfile& local = *pair.second;
+
+ // Look for exact duplicates, compare only profile contents (and
+ // ignore origin and language code in comparison).
+ if (local.Compare(*remote) == 0) {
+ // We found a duplicate, we keep the new (remote) one and delete the
+ // local one.
+ DVLOG(2)
+ << "[AUTOFILL SYNC] The profile "
+ << base::UTF16ToUTF8(local.GetRawInfo(NAME_FIRST))
+ << base::UTF16ToUTF8(local.GetRawInfo(NAME_LAST))
+ << " already exists with a different storage key; keep the remote key"
+ << remote_storage_key << " and delete the local key "
+ << local_storage_key;
+
+ // Ensure that a verified profile can never revert back to an unverified
+ // one. In such a case, take over the local origin for the new (remote)
+ // entry.
+ if (local.IsVerified() && !remote->IsVerified()) {
+ remote->set_origin(local.origin());
+ // Save a copy of the remote profile also to sync.
+ save_to_sync_.push_back(std::make_unique<AutofillProfile>(*remote));
+ }
+ // Delete the local profile that gets replaced by |remote|.
+ DeleteFromLocal(local_storage_key);
+ break;
+ }
+ }
+
+ add_to_local_.push_back(std::move(remote));
+ return base::nullopt;
+}
+
+Optional<ModelError>
+AutofillProfileSyncDifferenceTracker::IncorporateRemoteDelete(
+ const std::string& storage_key) {
+ DCHECK(!storage_key.empty());
+ DeleteFromLocal(storage_key);
+ return base::nullopt;
+}
+
+Optional<ModelError> AutofillProfileSyncDifferenceTracker::FlushToLocal(
+ base::OnceClosure autofill_changes_callback) {
+ for (const std::string& storage_key : delete_from_local_) {
+ if (!table_->RemoveAutofillProfile(storage_key)) {
+ return ModelError(FROM_HERE, "Failed deleting from WebDatabase");
+ }
+ }
+ for (const std::unique_ptr<AutofillProfile>& entry : add_to_local_) {
+ if (!table_->AddAutofillProfile(*entry)) {
+ return ModelError(FROM_HERE, "Failed updating WebDatabase");
+ }
+ }
+ for (const std::unique_ptr<AutofillProfile>& entry : update_to_local_) {
+ if (!table_->UpdateAutofillProfile(*entry)) {
+ return ModelError(FROM_HERE, "Failed updating WebDatabase");
+ }
+ }
+ if (!delete_from_local_.empty() || !add_to_local_.empty() ||
+ !update_to_local_.empty()) {
+ std::move(autofill_changes_callback).Run();
+ }
+ return base::nullopt;
+}
+
+Optional<ModelError> AutofillProfileSyncDifferenceTracker::FlushToSync(
+ std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync) {
+ for (std::unique_ptr<AutofillProfile>& entry : save_to_sync_) {
+ profiles_to_upload_to_sync->push_back(std::move(entry));
+ }
+ return base::nullopt;
+}
+
+Optional<AutofillProfile> AutofillProfileSyncDifferenceTracker::ReadEntry(
+ const std::string& storage_key) {
+ DCHECK(GetLocalOnlyEntries());
+ auto iter = GetLocalOnlyEntries()->find(storage_key);
+ if (iter != GetLocalOnlyEntries()->end()) {
+ return *iter->second;
+ }
+ return base::nullopt;
+}
+
+void AutofillProfileSyncDifferenceTracker::DeleteFromLocal(
+ const std::string& storage_key) {
+ DCHECK(GetLocalOnlyEntries());
+ delete_from_local_.insert(storage_key);
+ GetLocalOnlyEntries()->erase(storage_key);
+}
+
+std::map<std::string, std::unique_ptr<AutofillProfile>>*
+AutofillProfileSyncDifferenceTracker::GetLocalOnlyEntries() {
+ if (!InitializeLocalOnlyEntriesIfNeeded()) {
+ return nullptr;
+ }
+ return &local_only_entries_;
+}
+
+bool AutofillProfileSyncDifferenceTracker::
+ InitializeLocalOnlyEntriesIfNeeded() {
+ if (local_only_entries_initialized_) {
+ return true;
+ }
+
+ std::vector<std::unique_ptr<AutofillProfile>> entries;
+ if (!table_->GetAutofillProfiles(&entries)) {
+ return false;
+ }
+
+ for (std::unique_ptr<AutofillProfile>& entry : entries) {
+ std::string storage_key = GetStorageKeyFromAutofillProfile(*entry);
+ local_only_entries_[storage_key] = std::move(entry);
+ }
+
+ local_only_entries_initialized_ = true;
+ return true;
+}
+
+AutofillProfileInitialSyncDifferenceTracker::
+ AutofillProfileInitialSyncDifferenceTracker(AutofillTable* table)
+ : AutofillProfileSyncDifferenceTracker(table) {}
+
+AutofillProfileInitialSyncDifferenceTracker::
+ ~AutofillProfileInitialSyncDifferenceTracker() {}
+
+Optional<ModelError>
+AutofillProfileInitialSyncDifferenceTracker::IncorporateRemoteDelete(
+ const std::string& storage_key) {
+ // Remote delete is not allowed in initial sync.
+ NOTREACHED();
+ return base::nullopt;
+}
+
+Optional<ModelError> AutofillProfileInitialSyncDifferenceTracker::FlushToSync(
+ std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync) {
+ // First, flush standard updates to sync.
+ AutofillProfileSyncDifferenceTracker::FlushToSync(profiles_to_upload_to_sync);
+
+ // For initial sync, we additionally need to upload all local only entries.
+ if (!GetLocalOnlyEntries()) {
+ return ModelError(FROM_HERE, "Failed reading from WebDatabase.");
+ }
+ for (auto& pair : *GetLocalOnlyEntries()) {
+ std::string storage_key = pair.first;
+ // No deletions coming from remote are allowed for initial sync.
+ DCHECK(delete_from_local_.count(storage_key) == 0);
+ profiles_to_upload_to_sync->push_back(std::move(pair.second));
+ }
+ return base::nullopt;
+}
+
+Optional<ModelError>
+AutofillProfileInitialSyncDifferenceTracker::MergeSimilarEntriesForInitialSync(
+ const std::string& app_locale) {
+ if (!GetLocalOnlyEntries()) {
+ return ModelError(FROM_HERE, "Failed reading from WebDatabase.");
+ }
+
+ // This merge cannot happen on the fly during IncorporateRemoteSpecifics().
+ // Namely, we do not want to merge a local entry with a _similar_ remote
+ // entry if anoter perfectly fitting remote entry comes later during the
+ // initial sync (a remote entry fits perfectly to a given local entry if
+ // it has fully equal data or even the same storage key). After all the calls
+ // to IncorporateRemoteSpecifics() are over, GetLocalOnlyEntries() only
+ // contains unmatched entries that can be safely merged with similar remote
+ // entries.
+
+ AutofillProfileComparator comparator(app_locale);
+ // Loop over all new remote entries to find merge candidates. Using
+ // non-const reference because we want to update |remote| in place if
+ // needed.
+ for (std::unique_ptr<AutofillProfile>& remote : add_to_local_) {
+ Optional<AutofillProfile> local =
+ FindMergeableLocalEntry(*remote, comparator);
+ if (!local) {
+ continue;
+ }
+
+ DVLOG(2)
+ << "[AUTOFILL SYNC] A similar profile to "
+ << base::UTF16ToUTF8(remote->GetRawInfo(NAME_FIRST))
+ << base::UTF16ToUTF8(remote->GetRawInfo(NAME_LAST))
+ << " already exists with a different storage key; keep the remote key"
+ << GetStorageKeyFromAutofillProfile(*remote)
+ << ", merge local data into it and delete the local key"
+ << GetStorageKeyFromAutofillProfile(*local);
+
+ // For similar profile pairs, the local profile is always removed and its
+ // content merged (if applicable) in the profile that came from sync.
+ AutofillProfile remote_before_merge = *remote;
+ remote->MergeDataFrom(*local, app_locale);
+ if (!remote->EqualsForSyncPurposes(remote_before_merge)) {
+ // We need to sync new changes in the entry back to the server.
+ save_to_sync_.push_back(std::make_unique<AutofillProfile>(*remote));
+ // |remote| is updated in place within |add_to_local_| so the newest
+ // merged version is stored to local.
+ }
+
+ DeleteFromLocal(GetStorageKeyFromAutofillProfile(*local));
+ }
+
+ return base::nullopt;
+}
+
+Optional<AutofillProfile>
+AutofillProfileInitialSyncDifferenceTracker::FindMergeableLocalEntry(
+ const AutofillProfile& remote,
+ const AutofillProfileComparator& comparator) {
+ DCHECK(GetLocalOnlyEntries());
+
+ // Both the remote and the local entry need to be non-verified to be
+ // mergeable.
+ if (remote.IsVerified()) {
+ return base::nullopt;
+ }
+
+ // Check if there is a mergeable local profile.
+ for (const auto& pair : *GetLocalOnlyEntries()) {
+ const AutofillProfile& local_candidate = *pair.second;
+ if (!local_candidate.IsVerified() &&
+ comparator.AreMergeable(local_candidate, remote)) {
+ return local_candidate;
+ }
+ }
+ return base::nullopt;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h
new file mode 100644
index 00000000000..4c7fbf7a4e6
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker.h
@@ -0,0 +1,139 @@
+// 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_SYNC_DIFFERENCE_TRACKER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_SYNC_DIFFERENCE_TRACKER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/optional.h"
+
+namespace syncer {
+class ModelError;
+} // namespace syncer
+
+namespace autofill {
+
+class AutofillProfile;
+class AutofillProfileComparator;
+class AutofillTable;
+
+// This is used to respond to ApplySyncChanges() and MergeSyncData(). Attempts
+// to lazily load local data, and then react to sync data by maintaining
+// internal state until flush calls are made, at which point the applicable
+// modification should be sent towards local and sync directions.
+class AutofillProfileSyncDifferenceTracker {
+ public:
+ explicit AutofillProfileSyncDifferenceTracker(AutofillTable* table);
+ virtual ~AutofillProfileSyncDifferenceTracker();
+
+ // Adds a new |remote| entry to the diff tracker, originating from the sync
+ // server. The provided |remote| entry must be valid.
+ base::Optional<syncer::ModelError> IncorporateRemoteProfile(
+ std::unique_ptr<AutofillProfile> remote);
+
+ // Informs the diff tracker that the entry with |storage_key| has been deleted
+ // from the sync server. |storage_key| must be non-empty.
+ virtual base::Optional<syncer::ModelError> IncorporateRemoteDelete(
+ const std::string& storage_key);
+
+ // Writes all local changes to the provided autofill |table_|. After flushing,
+ // not further remote changes should get incorporated.
+ base::Optional<syncer::ModelError> FlushToLocal(
+ base::OnceClosure autofill_changes_callback);
+
+ // Writes into |profiles_to_upload_to_sync| all autofill profiles to be sent
+ // to the sync server. After flushing, not further remote changes should get
+ // incorporated.
+ virtual base::Optional<syncer::ModelError> FlushToSync(
+ std::vector<std::unique_ptr<AutofillProfile>>*
+ profiles_to_upload_to_sync);
+
+ protected:
+ // If the entry is found, |entry| will be return, otherwise base::nullopt is
+ // returned.
+ base::Optional<AutofillProfile> ReadEntry(const std::string& storage_key);
+
+ // Tries to find a local entry that is mergeable with |remote| (according to
+ // |comparator|). If such an entry is found, it is returned. Otherwise,
+ // base::nullopt is returned.
+ base::Optional<AutofillProfile> FindMergeableLocalEntry(
+ const AutofillProfile& remote,
+ const AutofillProfileComparator& comparator);
+
+ // Informs the tracker that a local entry with |storage_key| should get
+ // deleted.
+ void DeleteFromLocal(const std::string& storage_key);
+
+ // Accessor for data that is only stored local. Initializes the data if
+ // needed. Returns nullptr if initialization failed.
+ std::map<std::string, std::unique_ptr<AutofillProfile>>*
+ GetLocalOnlyEntries();
+
+ // Helper function called by GetLocalOnlyEntries().
+ bool InitializeLocalOnlyEntriesIfNeeded();
+
+ // The table for reading local data.
+ AutofillTable* const table_;
+
+ // This class loads local data from |table_| lazily. This field tracks if that
+ // has happened or not yet.
+ bool local_only_entries_initialized_ = false;
+
+ // We use unique_ptrs for storing AutofillProfile to avoid unnecessary copies.
+
+ // Local data, mapped by storage key. Use unique_to_local() to access it.
+ std::map<std::string, std::unique_ptr<AutofillProfile>> local_only_entries_;
+
+ // Contain changes (originating from sync) that need to be saved to the local
+ // store.
+ std::set<std::string> delete_from_local_;
+ std::vector<std::unique_ptr<AutofillProfile>> add_to_local_;
+ std::vector<std::unique_ptr<AutofillProfile>> update_to_local_;
+
+ // Contains merged data for entries that existed on both sync and local sides
+ // and need to be saved back to sync.
+ std::vector<std::unique_ptr<AutofillProfile>> save_to_sync_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncDifferenceTracker);
+};
+
+class AutofillProfileInitialSyncDifferenceTracker
+ : public AutofillProfileSyncDifferenceTracker {
+ public:
+ explicit AutofillProfileInitialSyncDifferenceTracker(AutofillTable* table);
+ ~AutofillProfileInitialSyncDifferenceTracker() override;
+
+ base::Optional<syncer::ModelError> IncorporateRemoteDelete(
+ const std::string& storage_key) override;
+
+ base::Optional<syncer::ModelError> FlushToSync(
+ std::vector<std::unique_ptr<AutofillProfile>>* profiles_to_upload_to_sync)
+ override;
+
+ // Performs an additional pass through remote entries incorporated from sync
+ // to find any similarities with local entries. Should be run after all
+ // entries get incorporated but before flushing results to local/sync.
+ base::Optional<syncer::ModelError> MergeSimilarEntriesForInitialSync(
+ const std::string& app_locale);
+
+ private:
+ // Returns a local entry that is mergeable with |remote| if it exists.
+ // Otherwise, returns base::nullopt.
+ base::Optional<AutofillProfile> FindMergeableLocalEntry(
+ const AutofillProfile& remote,
+ const AutofillProfileComparator& comparator);
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillProfileInitialSyncDifferenceTracker);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_SYNC_DIFFERENCE_TRACKER_H_
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc
new file mode 100644
index 00000000000..779ad929f53
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_sync_difference_tracker_unittest.cc
@@ -0,0 +1,438 @@
+// 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_sync_difference_tracker.h"
+
+#include "base/bind_helpers.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/time/time.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_profile_sync_util.h"
+#include "components/autofill/core/browser/country_names.h"
+#include "components/autofill/core/browser/test_autofill_clock.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/autofill/core/common/autofill_constants.h"
+#include "components/sync/model/model_error.h"
+#include "components/webdata/common/web_database.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+namespace {
+
+using base::ASCIIToUTF16;
+using base::MockCallback;
+using testing::ElementsAre;
+using testing::IsEmpty;
+
+// Some guids for testing.
+const char kGuidA[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44A";
+const char kGuidB[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44B";
+const char kHttpOrigin[] = "http://www.example.com/";
+const char kHttpsOrigin[] = "https://www.example.com/";
+const char kLocaleString[] = "en-US";
+const base::Time kJune2017 = base::Time::FromDoubleT(1497552271);
+
+} // namespace
+
+class AutofillProfileSyncDifferenceTrackerTestBase : public testing::Test {
+ public:
+ AutofillProfileSyncDifferenceTrackerTestBase() {}
+ ~AutofillProfileSyncDifferenceTrackerTestBase() override {}
+
+ void SetUp() override {
+ // Fix a time for implicitly constructed use_dates in AutofillProfile.
+ test_clock_.SetNow(kJune2017);
+
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ db_.AddTable(&table_);
+ db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase"));
+ }
+
+ void AddAutofillProfilesToTable(
+ const std::vector<AutofillProfile>& profile_list) {
+ for (const auto& profile : profile_list) {
+ table_.AddAutofillProfile(profile);
+ }
+ }
+
+ void IncorporateRemoteProfile(const AutofillProfile& profile) {
+ EXPECT_EQ(base::nullopt, tracker()->IncorporateRemoteProfile(
+ std::make_unique<AutofillProfile>(profile)));
+ }
+
+ std::vector<AutofillProfile> FlushAndReturnProfilesToUploadToSync() {
+ EXPECT_EQ(base::nullopt,
+ tracker()->FlushToLocal(
+ /*autofill_changes_callback=*/base::DoNothing()));
+
+ std::vector<std::unique_ptr<AutofillProfile>> vector_of_unique_ptrs;
+ EXPECT_EQ(base::nullopt,
+ tracker()->FlushToSync(
+ /*profiles_to_upload_to_sync=*/&vector_of_unique_ptrs));
+
+ // Copy all the elements by value so that we have a vector that is easier to
+ // work with in the test.
+ std::vector<AutofillProfile> vector_of_values;
+ for (const std::unique_ptr<AutofillProfile>& entry :
+ vector_of_unique_ptrs) {
+ vector_of_values.push_back(*entry);
+ }
+ return vector_of_values;
+ }
+
+ std::vector<AutofillProfile> GetAllLocalData() {
+ std::vector<std::unique_ptr<AutofillProfile>> vector_of_unique_ptrs;
+ // Meant as an assertion but I cannot use ASSERT_TRUE in non-void function.
+ EXPECT_TRUE(table()->GetAutofillProfiles(&vector_of_unique_ptrs));
+
+ // Copy all the elements by value so that we have a vector that is easier to
+ // work with in the test.
+ std::vector<AutofillProfile> local_data;
+ for (const std::unique_ptr<AutofillProfile>& entry :
+ vector_of_unique_ptrs) {
+ local_data.push_back(*entry);
+ }
+ return local_data;
+ }
+
+ virtual AutofillProfileSyncDifferenceTracker* tracker() = 0;
+
+ AutofillTable* table() { return &table_; }
+
+ private:
+ autofill::TestAutofillClock test_clock_;
+ base::ScopedTempDir temp_dir_;
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ AutofillTable table_;
+ WebDatabase db_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncDifferenceTrackerTestBase);
+};
+
+class AutofillProfileSyncDifferenceTrackerTest
+ : public AutofillProfileSyncDifferenceTrackerTestBase {
+ public:
+ AutofillProfileSyncDifferenceTrackerTest() : tracker_(table()) {}
+ ~AutofillProfileSyncDifferenceTrackerTest() override {}
+
+ AutofillProfileSyncDifferenceTracker* tracker() override { return &tracker_; }
+
+ private:
+ AutofillProfileSyncDifferenceTracker tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncDifferenceTrackerTest);
+};
+
+TEST_F(AutofillProfileSyncDifferenceTrackerTest,
+ IncorporateRemoteProfileShouldOverwriteProfileWithSameKey) {
+ AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+ local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ AddAutofillProfilesToTable({local});
+
+ // The remote profile is completely different but it has the same key.
+ AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin);
+ remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
+
+ IncorporateRemoteProfile(remote);
+
+ // Nothing gets uploaded to sync and the remote profile wins.
+ EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
+}
+
+TEST_F(AutofillProfileSyncDifferenceTrackerTest,
+ IncorporateRemoteProfileShouldOverwriteUnverifiedProfileByVerified) {
+ AutofillProfile local = AutofillProfile(kGuidA, kHttpsOrigin);
+ local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ AddAutofillProfilesToTable({local});
+
+ // The remote profile has the same key but it is not verified.
+ AutofillProfile remote = AutofillProfile(kGuidA, kSettingsOrigin);
+ remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
+
+ IncorporateRemoteProfile(remote);
+
+ // Nothing gets uploaded to sync and the local profile wins.
+ EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
+}
+
+TEST_F(AutofillProfileSyncDifferenceTrackerTest,
+ IncorporateRemoteProfileShouldNotOverwriteVerifiedProfileByUnverified) {
+ AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin);
+ local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ AddAutofillProfilesToTable({local});
+
+ // The remote profile has the same key but it is not verified.
+ AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin);
+ remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom"));
+
+ IncorporateRemoteProfile(remote);
+
+ // Nothing gets uploaded to sync and the local profile wins.
+ EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(local));
+}
+
+TEST_F(AutofillProfileSyncDifferenceTrackerTest,
+ IncorporateRemoteProfileShouldNotOverwriteFullNameByEmptyString) {
+ AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+ local.SetRawInfo(NAME_FULL, ASCIIToUTF16("John"));
+ AddAutofillProfilesToTable({local});
+
+ // The remote profile has the same key.
+ AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin);
+ remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2st st"));
+
+ AutofillProfile merged(remote);
+ merged.SetRawInfo(NAME_FULL, ASCIIToUTF16("John"));
+
+ IncorporateRemoteProfile(remote);
+
+ // Nothing gets uploaded to sync and the remote profile wins except for the
+ // full name.
+ EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
+}
+
+TEST_F(AutofillProfileSyncDifferenceTrackerTest,
+ IncorporateRemoteProfileShouldMergeIdenticalProfilesWithDifferentKeys) {
+ AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+ local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ AddAutofillProfilesToTable({local});
+
+ // The remote profile is identical to the local one, except that the guids and
+ // origins are different.
+ AutofillProfile remote = AutofillProfile(kGuidB, kHttpsOrigin);
+ remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+
+ IncorporateRemoteProfile(remote);
+
+ // Nothing gets uploaded to sync and the remote profile wins.
+ EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
+}
+
+TEST_F(
+ AutofillProfileSyncDifferenceTrackerTest,
+ IncorporateRemoteProfileShouldMergeIdenticalProfilesWithDifferentKeysButKeepVerifiedOrigin) {
+ AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin);
+ local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ AddAutofillProfilesToTable({local});
+
+ // The remote profile has the same key.
+ AutofillProfile remote = AutofillProfile(kGuidB, kHttpsOrigin);
+ remote.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+
+ AutofillProfile merged(remote);
+ merged.set_origin(kSettingsOrigin);
+
+ IncorporateRemoteProfile(remote);
+
+ // Nothing gets uploaded to sync and the remote profile wins except for the
+ // full name.
+ EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(merged));
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
+}
+
+TEST_F(AutofillProfileSyncDifferenceTrackerTest,
+ FlushToLocalShouldNotCallbackWhenNotNeeded) {
+ MockCallback<base::OnceClosure> autofill_changes_callback;
+
+ EXPECT_CALL(autofill_changes_callback, Run()).Times(0);
+ EXPECT_EQ(base::nullopt,
+ tracker()->FlushToLocal(autofill_changes_callback.Get()));
+}
+
+TEST_F(AutofillProfileSyncDifferenceTrackerTest,
+ FlushToLocalShouldCallbackWhenProfileDeleted) {
+ AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin);
+ AddAutofillProfilesToTable({local});
+
+ tracker()->IncorporateRemoteDelete(kGuidA);
+
+ MockCallback<base::OnceClosure> autofill_changes_callback;
+ EXPECT_CALL(autofill_changes_callback, Run()).Times(1);
+ EXPECT_EQ(base::nullopt,
+ tracker()->FlushToLocal(autofill_changes_callback.Get()));
+
+ // On top of that, the profile should also get deleted.
+ EXPECT_THAT(GetAllLocalData(), IsEmpty());
+}
+
+TEST_F(AutofillProfileSyncDifferenceTrackerTest,
+ FlushToLocalShouldCallbackWhenProfileAdded) {
+ AutofillProfile remote = AutofillProfile(kGuidA, kSettingsOrigin);
+ IncorporateRemoteProfile(remote);
+
+ MockCallback<base::OnceClosure> autofill_changes_callback;
+ EXPECT_CALL(autofill_changes_callback, Run()).Times(1);
+ EXPECT_EQ(base::nullopt,
+ tracker()->FlushToLocal(autofill_changes_callback.Get()));
+
+ // On top of that, the profile should also get added.
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
+}
+
+TEST_F(AutofillProfileSyncDifferenceTrackerTest,
+ FlushToLocalShouldCallbackWhenProfileUpdated) {
+ AutofillProfile local = AutofillProfile(kGuidA, kHttpsOrigin);
+ AddAutofillProfilesToTable({local});
+
+ AutofillProfile remote = AutofillProfile(kGuidA, kHttpsOrigin);
+ remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ IncorporateRemoteProfile(remote);
+
+ MockCallback<base::OnceClosure> autofill_changes_callback;
+ EXPECT_CALL(autofill_changes_callback, Run()).Times(1);
+ EXPECT_EQ(base::nullopt,
+ tracker()->FlushToLocal(autofill_changes_callback.Get()));
+
+ // On top of that, the profile with key kGuidA should also get updated.
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
+}
+
+class AutofillProfileInitialSyncDifferenceTrackerTest
+ : public AutofillProfileSyncDifferenceTrackerTestBase {
+ public:
+ AutofillProfileInitialSyncDifferenceTrackerTest()
+ : initial_tracker_(table()) {}
+ ~AutofillProfileInitialSyncDifferenceTrackerTest() override {}
+
+ void MergeSimilarEntriesForInitialSync() {
+ initial_tracker_.MergeSimilarEntriesForInitialSync(kLocaleString);
+ }
+
+ AutofillProfileSyncDifferenceTracker* tracker() override {
+ return &initial_tracker_;
+ }
+
+ private:
+ AutofillProfileInitialSyncDifferenceTracker initial_tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillProfileInitialSyncDifferenceTrackerTest);
+};
+
+TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
+ MergeSimilarEntriesForInitialSyncShouldSyncUpChanges) {
+ AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+ local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ local.set_use_count(27);
+ AddAutofillProfilesToTable({local});
+
+ // The remote profile matches the local one (except for origin and use count).
+ AutofillProfile remote = AutofillProfile(kGuidB, kHttpsOrigin);
+ remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
+ remote.set_use_count(13);
+
+ // The remote profile wins (as regards the storage key).
+ AutofillProfile merged(remote);
+ // The local origin wins when merging.
+ merged.set_origin(kHttpOrigin);
+ // Merging two profile takes their max use count.
+ merged.set_use_count(27);
+
+ IncorporateRemoteProfile(remote);
+ MergeSimilarEntriesForInitialSync();
+
+ // The merged profile needs to get uploaded back to sync and stored locally.
+ EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(merged));
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(merged));
+}
+
+TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
+ MergeSimilarEntriesForInitialSyncShouldNotSyncUpWhenNotNeeded) {
+ AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+ local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ local.set_use_count(13);
+ AddAutofillProfilesToTable({local});
+
+ // The remote profile matches the local one and has some additional data.
+ AutofillProfile remote = AutofillProfile(kGuidB, kHttpOrigin);
+ remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
+ // Merging two profile takes their max use count, so use count of 27 is taken.
+ remote.set_use_count(27);
+
+ IncorporateRemoteProfile(remote);
+ MergeSimilarEntriesForInitialSync();
+
+ // Nothing gets uploaded to sync and the remote profile wins.
+ EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), IsEmpty());
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(remote));
+}
+
+TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
+ MergeSimilarEntriesForInitialSyncNotMatchNonsimilarEntries) {
+ AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+ local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ local.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
+ AddAutofillProfilesToTable({local});
+
+ // The remote profile has a different street address.
+ AutofillProfile remote = AutofillProfile(kGuidB, kHttpOrigin);
+ remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2st st"));
+ remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
+
+ IncorporateRemoteProfile(remote);
+ MergeSimilarEntriesForInitialSync();
+
+ // The local profile gets uploaded (due to initial sync) and the remote
+ // profile gets stored locally.
+ EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(local));
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(local, remote));
+}
+
+TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
+ MergeSimilarEntriesForInitialSyncDoesNotMatchLocalVerifiedEntry) {
+ // The local entry is verified, should not get merged.
+ AutofillProfile local = AutofillProfile(kGuidA, kSettingsOrigin);
+ local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ AddAutofillProfilesToTable({local});
+
+ // The remote profile is similar to the local one.
+ AutofillProfile remote = AutofillProfile(kGuidB, kHttpOrigin);
+ remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
+
+ IncorporateRemoteProfile(remote);
+ MergeSimilarEntriesForInitialSync();
+
+ // The local profile gets uploaded (due to initial sync) and the remote
+ // profile gets stored locally.
+ EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(local));
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(local, remote));
+}
+
+TEST_F(AutofillProfileInitialSyncDifferenceTrackerTest,
+ MergeSimilarEntriesForInitialSyncDoesNotMatchRemoteVerifiedEntry) {
+ AutofillProfile local = AutofillProfile(kGuidA, kHttpOrigin);
+ local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ AddAutofillProfilesToTable({local});
+
+ // The remote profile is similar to the local one but is verified and thus it
+ // should not get merged.
+ AutofillProfile remote = AutofillProfile(kGuidB, kSettingsOrigin);
+ remote.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st"));
+ remote.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Frobbers, Inc."));
+
+ IncorporateRemoteProfile(remote);
+ MergeSimilarEntriesForInitialSync();
+
+ // The local profile gets uploaded (due to initial sync) and the remote
+ // profile gets stored locally.
+ EXPECT_THAT(FlushAndReturnProfilesToUploadToSync(), ElementsAre(local));
+ EXPECT_THAT(GetAllLocalData(), ElementsAre(local, remote));
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.cc b/chromium/components/autofill/core/browser/webdata/autofill_table.cc
index 31cbe830a10..e64990856d6 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_table.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table.cc
@@ -478,6 +478,9 @@ bool AutofillTable::MigrateToVersion(int version,
case 75:
*update_compatible_version = false;
return MigrateToVersion75AddProfileValidityBitfieldColumn();
+ case 78:
+ *update_compatible_version = true;
+ return MigrateToVersion78AddModelTypeColumns();
}
return true;
}
@@ -703,24 +706,13 @@ bool AutofillTable::RemoveExpiredFormElements(
return true;
}
-bool AutofillTable::AddFormFieldValuesTime(
- const std::vector<FormFieldData>& elements,
- std::vector<AutofillChange>* changes,
- base::Time time) {
- // Only add one new entry for each unique element name. Use |seen_names| to
- // track this. Add up to |kMaximumUniqueNames| unique entries per form.
- const size_t kMaximumUniqueNames = 256;
- std::set<base::string16> seen_names;
- bool result = true;
- for (const FormFieldData& element : elements) {
- if (seen_names.size() >= kMaximumUniqueNames)
- break;
- if (base::ContainsKey(seen_names, element.name))
- continue;
- result = result && AddFormFieldValueTime(element, changes, time);
- seen_names.insert(element.name);
- }
- return result;
+bool AutofillTable::RemoveFormElement(const base::string16& name,
+ const base::string16& value) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM autofill WHERE name = ? AND value= ?"));
+ s.BindString16(0, name);
+ s.BindString16(1, value);
+ return s.Run();
}
int AutofillTable::GetCountOfValuesContainedBetween(const base::Time& begin,
@@ -804,77 +796,6 @@ bool AutofillTable::UpdateAutofillEntries(
return true;
}
-bool AutofillTable::InsertAutofillEntry(const AutofillEntry& entry) {
- std::string sql =
- "INSERT INTO autofill "
- "(name, value, value_lower, date_created, date_last_used, count) "
- "VALUES (?, ?, ?, ?, ?, ?)";
- sql::Statement s(db_->GetUniqueStatement(sql.c_str()));
- s.BindString16(0, entry.key().name());
- s.BindString16(1, entry.key().value());
- s.BindString16(2, base::i18n::ToLower(entry.key().value()));
- s.BindInt64(3, entry.date_created().ToTimeT());
- s.BindInt64(4, entry.date_last_used().ToTimeT());
- // TODO(isherman): The counts column is currently synced implicitly as the
- // number of timestamps. Sync the value explicitly instead, since the DB now
- // only saves the first and last timestamp, which makes counting timestamps
- // completely meaningless as a way to track frequency of usage.
- s.BindInt(5, entry.date_last_used() == entry.date_created() ? 1 : 2);
- return s.Run();
-}
-
-bool AutofillTable::AddFormFieldValueTime(const FormFieldData& element,
- std::vector<AutofillChange>* changes,
- base::Time time) {
- sql::Statement s_exists(db_->GetUniqueStatement(
- "SELECT COUNT(*) FROM autofill WHERE name = ? AND value = ?"));
- s_exists.BindString16(0, element.name);
- s_exists.BindString16(1, element.value);
- if (!s_exists.Step())
- return false;
-
- bool already_exists = s_exists.ColumnInt(0) > 0;
- if (already_exists) {
- sql::Statement s(db_->GetUniqueStatement(
- "UPDATE autofill SET date_last_used = ?, count = count + 1 "
- "WHERE name = ? AND value = ?"));
- s.BindInt64(0, time.ToTimeT());
- s.BindString16(1, element.name);
- s.BindString16(2, element.value);
- if (!s.Run())
- return false;
- } else {
- time_t time_as_time_t = time.ToTimeT();
- sql::Statement s(db_->GetUniqueStatement(
- "INSERT INTO autofill "
- "(name, value, value_lower, date_created, date_last_used, count) "
- "VALUES (?, ?, ?, ?, ?, ?)"));
- s.BindString16(0, element.name);
- s.BindString16(1, element.value);
- s.BindString16(2, base::i18n::ToLower(element.value));
- s.BindInt64(3, time_as_time_t);
- s.BindInt64(4, time_as_time_t);
- s.BindInt(5, 1);
- if (!s.Run())
- return false;
- }
-
- AutofillChange::Type change_type =
- already_exists ? AutofillChange::UPDATE : AutofillChange::ADD;
- changes->push_back(
- AutofillChange(change_type, AutofillKey(element.name, element.value)));
- return true;
-}
-
-bool AutofillTable::RemoveFormElement(const base::string16& name,
- const base::string16& value) {
- sql::Statement s(db_->GetUniqueStatement(
- "DELETE FROM autofill WHERE name = ? AND value= ?"));
- s.BindString16(0, name);
- s.BindString16(1, value);
- return s.Run();
-}
-
bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) {
if (IsAutofillGUIDInTrash(profile.guid()))
return true;
@@ -893,6 +814,70 @@ bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) {
return AddAutofillProfilePieces(profile, db_);
}
+bool AutofillTable::UpdateAutofillProfile(const AutofillProfile& profile) {
+ DCHECK(base::IsValidGUID(profile.guid()));
+
+ // Don't update anything until the trash has been emptied. There may be
+ // pending modifications to process.
+ if (!IsAutofillProfilesTrashEmpty())
+ return true;
+
+ std::unique_ptr<AutofillProfile> old_profile =
+ GetAutofillProfile(profile.guid());
+ if (!old_profile)
+ return false;
+
+ bool update_modification_date = *old_profile != profile;
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE autofill_profiles "
+ "SET guid=?, company_name=?, street_address=?, dependent_locality=?, "
+ " city=?, state=?, zipcode=?, sorting_code=?, country_code=?, "
+ " use_count=?, use_date=?, date_modified=?, origin=?, "
+ " language_code=?, validity_bitfield=? "
+ "WHERE guid=?"));
+ BindAutofillProfileToStatement(profile,
+ update_modification_date
+ ? AutofillClock::Now()
+ : old_profile->modification_date(),
+ &s);
+ s.BindString(15, profile.guid());
+
+ bool result = s.Run();
+ DCHECK_GT(db_->GetLastChangeCount(), 0);
+ if (!result)
+ return result;
+
+ // Remove the old names, emails, and phone numbers.
+ if (!RemoveAutofillProfilePieces(profile.guid(), db_))
+ return false;
+
+ return AddAutofillProfilePieces(profile, db_);
+}
+
+bool AutofillTable::RemoveAutofillProfile(const std::string& guid) {
+ DCHECK(base::IsValidGUID(guid));
+
+ if (IsAutofillGUIDInTrash(guid)) {
+ sql::Statement s_trash(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profiles_trash WHERE guid = ?"));
+ s_trash.BindString(0, guid);
+
+ bool success = s_trash.Run();
+ DCHECK_GT(db_->GetLastChangeCount(), 0) << "Expected item in trash";
+ return success;
+ }
+
+ sql::Statement s(
+ db_->GetUniqueStatement("DELETE FROM autofill_profiles WHERE guid = ?"));
+ s.BindString(0, guid);
+
+ if (!s.Run())
+ return false;
+
+ return RemoveAutofillProfilePieces(guid, db_);
+}
+
std::unique_ptr<AutofillProfile> AutofillTable::GetAutofillProfile(
const std::string& guid) {
DCHECK(base::IsValidGUID(guid));
@@ -1068,109 +1053,84 @@ void AutofillTable::SetServerProfiles(
transaction.Commit();
}
-bool AutofillTable::UpdateAutofillProfile(const AutofillProfile& profile) {
- DCHECK(base::IsValidGUID(profile.guid()));
+bool AutofillTable::AddCreditCard(const CreditCard& credit_card) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT INTO credit_cards"
+ "(guid, name_on_card, expiration_month, expiration_year, "
+ " card_number_encrypted, use_count, use_date, date_modified, origin,"
+ " billing_address_id)"
+ "VALUES (?,?,?,?,?,?,?,?,?,?)"));
+ BindCreditCardToStatement(credit_card, AutofillClock::Now(), &s,
+ *autofill_table_encryptor_);
- // Don't update anything until the trash has been emptied. There may be
- // pending modifications to process.
- if (!IsAutofillProfilesTrashEmpty())
- return true;
+ if (!s.Run())
+ return false;
- std::unique_ptr<AutofillProfile> old_profile =
- GetAutofillProfile(profile.guid());
- if (!old_profile)
+ DCHECK_GT(db_->GetLastChangeCount(), 0);
+ return true;
+}
+
+bool AutofillTable::UpdateCreditCard(const CreditCard& credit_card) {
+ DCHECK(base::IsValidGUID(credit_card.guid()));
+
+ std::unique_ptr<CreditCard> old_credit_card =
+ GetCreditCard(credit_card.guid());
+ if (!old_credit_card)
return false;
- bool update_modification_date = *old_profile != profile;
+ bool update_modification_date = *old_credit_card != credit_card;
sql::Statement s(db_->GetUniqueStatement(
- "UPDATE autofill_profiles "
- "SET guid=?, company_name=?, street_address=?, dependent_locality=?, "
- " city=?, state=?, zipcode=?, sorting_code=?, country_code=?, "
- " use_count=?, use_date=?, date_modified=?, origin=?, "
- " language_code=?, validity_bitfield=? "
- "WHERE guid=?"));
- BindAutofillProfileToStatement(profile,
- update_modification_date
- ? AutofillClock::Now()
- : old_profile->modification_date(),
- &s);
- s.BindString(15, profile.guid());
+ "UPDATE credit_cards "
+ "SET guid=?, name_on_card=?, expiration_month=?,"
+ "expiration_year=?, card_number_encrypted=?, use_count=?, use_date=?,"
+ "date_modified=?, origin=?, billing_address_id=?"
+ "WHERE guid=?1"));
+ BindCreditCardToStatement(credit_card,
+ update_modification_date
+ ? AutofillClock::Now()
+ : old_credit_card->modification_date(),
+ &s, *autofill_table_encryptor_);
bool result = s.Run();
DCHECK_GT(db_->GetLastChangeCount(), 0);
- if (!result)
- return result;
-
- // Remove the old names, emails, and phone numbers.
- if (!RemoveAutofillProfilePieces(profile.guid(), db_))
- return false;
-
- return AddAutofillProfilePieces(profile, db_);
+ return result;
}
-bool AutofillTable::RemoveAutofillProfile(const std::string& guid) {
+bool AutofillTable::RemoveCreditCard(const std::string& guid) {
DCHECK(base::IsValidGUID(guid));
-
- if (IsAutofillGUIDInTrash(guid)) {
- sql::Statement s_trash(db_->GetUniqueStatement(
- "DELETE FROM autofill_profiles_trash WHERE guid = ?"));
- s_trash.BindString(0, guid);
-
- bool success = s_trash.Run();
- DCHECK_GT(db_->GetLastChangeCount(), 0) << "Expected item in trash";
- return success;
- }
-
sql::Statement s(
- db_->GetUniqueStatement("DELETE FROM autofill_profiles WHERE guid = ?"));
+ db_->GetUniqueStatement("DELETE FROM credit_cards WHERE guid = ?"));
s.BindString(0, guid);
- if (!s.Run())
- return false;
-
- return RemoveAutofillProfilePieces(guid, db_);
+ return s.Run();
}
-bool AutofillTable::ClearAutofillProfiles() {
- sql::Statement s1(db_->GetUniqueStatement("DELETE FROM autofill_profiles"));
-
- if (!s1.Run())
- return false;
-
- sql::Statement s2(
- db_->GetUniqueStatement("DELETE FROM autofill_profile_names"));
-
- if (!s2.Run())
- return false;
-
- sql::Statement s3(
- db_->GetUniqueStatement("DELETE FROM autofill_profile_emails"));
+bool AutofillTable::AddFullServerCreditCard(const CreditCard& credit_card) {
+ DCHECK_EQ(CreditCard::FULL_SERVER_CARD, credit_card.record_type());
+ DCHECK(!credit_card.number().empty());
+ DCHECK(!credit_card.server_id().empty());
- if (!s3.Run())
+ sql::Transaction transaction(db_);
+ if (!transaction.Begin())
return false;
- sql::Statement s4(
- db_->GetUniqueStatement("DELETE FROM autofill_profile_phones"));
+ // Make sure there aren't duplicates for this card.
+ DeleteFromUnmaskedCreditCards(credit_card.server_id());
+ DeleteFromMaskedCreditCards(credit_card.server_id());
- return s4.Run();
-}
+ CreditCard masked(credit_card);
+ masked.set_record_type(CreditCard::MASKED_SERVER_CARD);
+ masked.SetNumber(credit_card.LastFourDigits());
+ masked.RecordAndLogUse();
+ DCHECK(!masked.network().empty());
+ AddMaskedCreditCards({masked});
-bool AutofillTable::AddCreditCard(const CreditCard& credit_card) {
- sql::Statement s(db_->GetUniqueStatement(
- "INSERT INTO credit_cards"
- "(guid, name_on_card, expiration_month, expiration_year, "
- " card_number_encrypted, use_count, use_date, date_modified, origin,"
- " billing_address_id)"
- "VALUES (?,?,?,?,?,?,?,?,?,?)"));
- BindCreditCardToStatement(credit_card, AutofillClock::Now(), &s,
- *autofill_table_encryptor_);
+ AddUnmaskedCreditCard(credit_card.server_id(), credit_card.number());
- if (!s.Run())
- return false;
+ transaction.Commit();
- DCHECK_GT(db_->GetLastChangeCount(), 0);
- return true;
+ return db_->GetLastChangeCount() > 0;
}
std::unique_ptr<CreditCard> AutofillTable::GetCreditCard(
@@ -1281,42 +1241,6 @@ bool AutofillTable::GetServerCreditCards(
return s.Succeeded();
}
-void AutofillTable::AddMaskedCreditCards(
- const std::vector<CreditCard>& credit_cards) {
- DCHECK_GT(db_->transaction_nesting(), 0);
- sql::Statement masked_insert(
- db_->GetUniqueStatement("INSERT INTO masked_credit_cards("
- "id," // 0
- "network," // 1
- "type," // 2
- "status," // 3
- "name_on_card," // 4
- "last_four," // 5
- "exp_month," // 6
- "exp_year," // 7
- "bank_name)" // 8
- "VALUES (?,?,?,?,?,?,?,?,?)"));
- for (const CreditCard& card : credit_cards) {
- DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type());
- masked_insert.BindString(0, card.server_id());
- masked_insert.BindString(1, card.network());
- masked_insert.BindInt(2, card.card_type());
- masked_insert.BindString(3,
- ServerStatusEnumToString(card.GetServerStatus()));
- masked_insert.BindString16(4, card.GetRawInfo(CREDIT_CARD_NAME_FULL));
- masked_insert.BindString16(5, card.LastFourDigits());
- masked_insert.BindString16(6, card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
- masked_insert.BindString16(7,
- card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
- masked_insert.BindString(8, card.bank_name());
- masked_insert.Run();
- masked_insert.Reset(true);
-
- // Save the use count and use date of the card.
- UpdateServerCardMetadata(card);
- }
-}
-
void AutofillTable::SetServerCreditCards(
const std::vector<CreditCard>& credit_cards) {
sql::Transaction transaction(db_);
@@ -1344,52 +1268,6 @@ void AutofillTable::SetServerCreditCards(
transaction.Commit();
}
-bool AutofillTable::AddFullServerCreditCard(const CreditCard& credit_card) {
- DCHECK_EQ(CreditCard::FULL_SERVER_CARD, credit_card.record_type());
- DCHECK(!credit_card.number().empty());
- DCHECK(!credit_card.server_id().empty());
-
- sql::Transaction transaction(db_);
- if (!transaction.Begin())
- return false;
-
- // Make sure there aren't duplicates for this card.
- DeleteFromUnmaskedCreditCards(credit_card.server_id());
- DeleteFromMaskedCreditCards(credit_card.server_id());
-
- CreditCard masked(credit_card);
- masked.set_record_type(CreditCard::MASKED_SERVER_CARD);
- masked.SetNumber(credit_card.LastFourDigits());
- masked.RecordAndLogUse();
- DCHECK(!masked.network().empty());
- AddMaskedCreditCards({masked});
-
- AddUnmaskedCreditCard(credit_card.server_id(), credit_card.number());
-
- transaction.Commit();
-
- return db_->GetLastChangeCount() > 0;
-}
-
-void AutofillTable::AddUnmaskedCreditCard(const std::string& id,
- const base::string16& full_number) {
- sql::Statement s(
- db_->GetUniqueStatement("INSERT INTO unmasked_credit_cards("
- "id,"
- "card_number_encrypted,"
- "unmask_date)"
- "VALUES (?,?,?)"));
- s.BindString(0, id);
-
- std::string encrypted_data;
- autofill_table_encryptor_->EncryptString16(full_number, &encrypted_data);
- s.BindBlob(1, encrypted_data.data(),
- static_cast<int>(encrypted_data.length()));
- s.BindInt64(2, AutofillClock::Now().ToInternalValue()); // unmask_date
-
- s.Run();
-}
-
bool AutofillTable::UnmaskServerCreditCard(const CreditCard& masked,
const base::string16& full_number) {
sql::Transaction transaction(db_);
@@ -1412,22 +1290,6 @@ bool AutofillTable::UnmaskServerCreditCard(const CreditCard& masked,
return db_->GetLastChangeCount() > 0;
}
-bool AutofillTable::DeleteFromMaskedCreditCards(const std::string& id) {
- sql::Statement s(
- db_->GetUniqueStatement("DELETE FROM masked_credit_cards WHERE id = ?"));
- s.BindString(0, id);
- s.Run();
- return db_->GetLastChangeCount() > 0;
-}
-
-bool AutofillTable::DeleteFromUnmaskedCreditCards(const std::string& id) {
- sql::Statement s(db_->GetUniqueStatement(
- "DELETE FROM unmasked_credit_cards WHERE id = ?"));
- s.BindString(0, id);
- s.Run();
- return db_->GetLastChangeCount() > 0;
-}
-
bool AutofillTable::MaskServerCreditCard(const std::string& id) {
return DeleteFromUnmaskedCreditCards(id);
}
@@ -1517,40 +1379,18 @@ bool AutofillTable::ClearAllServerData() {
return changed;
}
-bool AutofillTable::UpdateCreditCard(const CreditCard& credit_card) {
- DCHECK(base::IsValidGUID(credit_card.guid()));
-
- std::unique_ptr<CreditCard> old_credit_card =
- GetCreditCard(credit_card.guid());
- if (!old_credit_card)
- return false;
-
- bool update_modification_date = *old_credit_card != credit_card;
-
- sql::Statement s(db_->GetUniqueStatement(
- "UPDATE credit_cards "
- "SET guid=?, name_on_card=?, expiration_month=?,"
- "expiration_year=?, card_number_encrypted=?, use_count=?, use_date=?,"
- "date_modified=?, origin=?, billing_address_id=?"
- "WHERE guid=?1"));
- BindCreditCardToStatement(credit_card,
- update_modification_date
- ? AutofillClock::Now()
- : old_credit_card->modification_date(),
- &s, *autofill_table_encryptor_);
-
- bool result = s.Run();
- DCHECK_GT(db_->GetLastChangeCount(), 0);
- return result;
-}
+bool AutofillTable::ClearAllLocalData() {
+ sql::Transaction transaction(db_);
+ if (!transaction.Begin())
+ return false; // Some error, nothing was changed.
-bool AutofillTable::RemoveCreditCard(const std::string& guid) {
- DCHECK(base::IsValidGUID(guid));
- sql::Statement s(
- db_->GetUniqueStatement("DELETE FROM credit_cards WHERE guid = ?"));
- s.BindString(0, guid);
+ ClearAutofillProfiles();
+ bool changed = db_->GetLastChangeCount() > 0;
+ ClearCreditCards();
+ changed |= db_->GetLastChangeCount() > 0;
- return s.Run();
+ transaction.Commit();
+ return changed;
}
bool AutofillTable::RemoveAutofillDataModifiedBetween(
@@ -1727,25 +1567,39 @@ bool AutofillTable::AddAutofillGUIDToTrash(const std::string& guid) {
return s.Run();
}
-bool AutofillTable::IsAutofillProfilesTrashEmpty() {
- sql::Statement s(
- db_->GetUniqueStatement("SELECT guid FROM autofill_profiles_trash"));
+bool AutofillTable::ClearAutofillProfiles() {
+ sql::Statement s1(db_->GetUniqueStatement("DELETE FROM autofill_profiles"));
- return !s.Step();
-}
+ if (!s1.Run())
+ return false;
-bool AutofillTable::IsAutofillGUIDInTrash(const std::string& guid) {
- sql::Statement s(db_->GetUniqueStatement(
- "SELECT guid FROM autofill_profiles_trash WHERE guid = ?"));
- s.BindString(0, guid);
+ sql::Statement s2(
+ db_->GetUniqueStatement("DELETE FROM autofill_profile_names"));
- return s.Step();
+ if (!s2.Run())
+ return false;
+
+ sql::Statement s3(
+ db_->GetUniqueStatement("DELETE FROM autofill_profile_emails"));
+
+ if (!s3.Run())
+ return false;
+
+ sql::Statement s4(
+ db_->GetUniqueStatement("DELETE FROM autofill_profile_phones"));
+
+ return s4.Run();
+}
+
+bool AutofillTable::ClearCreditCards() {
+ sql::Statement s1(db_->GetUniqueStatement("DELETE FROM credit_cards"));
+ return s1.Run();
}
bool AutofillTable::GetAllSyncMetadata(syncer::ModelType model_type,
syncer::MetadataBatch* metadata_batch) {
- DCHECK_EQ(model_type, syncer::AUTOFILL)
- << "Only the AUTOFILL model type is supported";
+ DCHECK(SupportsMetadataForModelType(model_type))
+ << "Model type " << model_type << " not supported for metadata";
DCHECK(metadata_batch);
if (!GetAllSyncEntityMetadata(model_type, metadata_batch)) {
return false;
@@ -1759,97 +1613,61 @@ bool AutofillTable::GetAllSyncMetadata(syncer::ModelType model_type,
return true;
}
-bool AutofillTable::GetAllSyncEntityMetadata(
- syncer::ModelType model_type,
- syncer::MetadataBatch* metadata_batch) {
- DCHECK_EQ(model_type, syncer::AUTOFILL)
- << "Only the AUTOFILL model type is supported";
- DCHECK(metadata_batch);
-
- sql::Statement s(db_->GetUniqueStatement(
- "SELECT storage_key, value FROM autofill_sync_metadata"));
-
- while (s.Step()) {
- std::string storage_key = s.ColumnString(0);
- std::string serialized_metadata = s.ColumnString(1);
- sync_pb::EntityMetadata entity_metadata;
- if (entity_metadata.ParseFromString(serialized_metadata)) {
- metadata_batch->AddMetadata(storage_key, entity_metadata);
- } else {
- DLOG(WARNING) << "Failed to deserialize AUTOFILL model type "
- "sync_pb::EntityMetadata.";
- return false;
- }
- }
- return true;
-}
-
bool AutofillTable::UpdateSyncMetadata(
syncer::ModelType model_type,
const std::string& storage_key,
const sync_pb::EntityMetadata& metadata) {
- DCHECK_EQ(model_type, syncer::AUTOFILL)
- << "Only the AUTOFILL model type is supported";
+ DCHECK(SupportsMetadataForModelType(model_type))
+ << "Model type " << model_type << " not supported for metadata";
- sql::Statement s(
- db_->GetUniqueStatement("INSERT OR REPLACE INTO autofill_sync_metadata "
- "(storage_key, value) VALUES(?, ?)"));
- s.BindString(0, storage_key);
- s.BindString(1, metadata.SerializeAsString());
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT OR REPLACE INTO autofill_sync_metadata "
+ "(model_type, storage_key, value) VALUES(?, ?, ?)"));
+ s.BindInt(0, GetKeyValueForModelType(model_type));
+ s.BindString(1, storage_key);
+ s.BindString(2, metadata.SerializeAsString());
return s.Run();
}
bool AutofillTable::ClearSyncMetadata(syncer::ModelType model_type,
const std::string& storage_key) {
- DCHECK_EQ(model_type, syncer::AUTOFILL)
- << "Only the AUTOFILL model type is supported";
+ DCHECK(SupportsMetadataForModelType(model_type))
+ << "Model type " << model_type << " not supported for metadata";
- sql::Statement s(db_->GetUniqueStatement(
- "DELETE FROM autofill_sync_metadata WHERE storage_key=?"));
- s.BindString(0, storage_key);
+ sql::Statement s(
+ db_->GetUniqueStatement("DELETE FROM autofill_sync_metadata WHERE "
+ "model_type=? AND storage_key=?"));
+ s.BindInt(0, GetKeyValueForModelType(model_type));
+ s.BindString(1, storage_key);
return s.Run();
}
-bool AutofillTable::GetModelTypeState(syncer::ModelType model_type,
- sync_pb::ModelTypeState* state) {
- DCHECK_EQ(model_type, syncer::AUTOFILL)
- << "Only the AUTOFILL model type is supported";
-
- sql::Statement s(db_->GetUniqueStatement(
- "SELECT value FROM autofill_model_type_state WHERE id=1"));
-
- if (!s.Step()) {
- return true;
- }
-
- std::string serialized_state = s.ColumnString(0);
- return state->ParseFromString(serialized_state);
-}
-
bool AutofillTable::UpdateModelTypeState(
syncer::ModelType model_type,
const sync_pb::ModelTypeState& model_type_state) {
- DCHECK_EQ(model_type, syncer::AUTOFILL)
- << "Only the AUTOFILL model type is supported";
+ DCHECK(SupportsMetadataForModelType(model_type))
+ << "Model type " << model_type << " not supported for metadata";
// Hardcode the id to force a collision, ensuring that there remains only a
// single entry.
sql::Statement s(db_->GetUniqueStatement(
- "INSERT OR REPLACE INTO autofill_model_type_state (id, value) "
- "VALUES(1,?)"));
- s.BindString(0, model_type_state.SerializeAsString());
+ "INSERT OR REPLACE INTO autofill_model_type_state (model_type, value) "
+ "VALUES(?,?)"));
+ s.BindInt(0, GetKeyValueForModelType(model_type));
+ s.BindString(1, model_type_state.SerializeAsString());
return s.Run();
}
bool AutofillTable::ClearModelTypeState(syncer::ModelType model_type) {
- DCHECK_EQ(model_type, syncer::AUTOFILL)
- << "Only the AUTOFILL model type is supported";
+ DCHECK(SupportsMetadataForModelType(model_type))
+ << "Model type " << model_type << " not supported for metadata";
sql::Statement s(db_->GetUniqueStatement(
- "DELETE FROM autofill_model_type_state WHERE id=1"));
+ "DELETE FROM autofill_model_type_state WHERE model_type=?"));
+ s.BindInt(0, GetKeyValueForModelType(model_type));
return s.Run();
}
@@ -1879,232 +1697,6 @@ bool AutofillTable::RemoveOrphanAutofillTableRows() {
return true;
}
-bool AutofillTable::InitMainTable() {
- if (!db_->DoesTableExist("autofill")) {
- if (!db_->Execute("CREATE TABLE autofill ("
- "name VARCHAR, "
- "value VARCHAR, "
- "value_lower VARCHAR, "
- "date_created INTEGER DEFAULT 0, "
- "date_last_used INTEGER DEFAULT 0, "
- "count INTEGER DEFAULT 1, "
- "PRIMARY KEY (name, value))") ||
- !db_->Execute("CREATE INDEX autofill_name ON autofill (name)") ||
- !db_->Execute("CREATE INDEX autofill_name_value_lower ON "
- "autofill (name, value_lower)")) {
- NOTREACHED();
- return false;
- }
- }
- return true;
-}
-
-bool AutofillTable::InitCreditCardsTable() {
- if (!db_->DoesTableExist("credit_cards")) {
- if (!db_->Execute("CREATE TABLE credit_cards ( "
- "guid VARCHAR PRIMARY KEY, "
- "name_on_card VARCHAR, "
- "expiration_month INTEGER, "
- "expiration_year INTEGER, "
- "card_number_encrypted BLOB, "
- "date_modified INTEGER NOT NULL DEFAULT 0, "
- "origin VARCHAR DEFAULT '', "
- "use_count INTEGER NOT NULL DEFAULT 0, "
- "use_date INTEGER NOT NULL DEFAULT 0, "
- "billing_address_id VARCHAR) ")) {
- NOTREACHED();
- return false;
- }
- }
-
- return true;
-}
-
-bool AutofillTable::InitProfilesTable() {
- if (!db_->DoesTableExist("autofill_profiles")) {
- if (!db_->Execute("CREATE TABLE autofill_profiles ( "
- "guid VARCHAR PRIMARY KEY, "
- "company_name VARCHAR, "
- "street_address VARCHAR, "
- "dependent_locality VARCHAR, "
- "city VARCHAR, "
- "state VARCHAR, "
- "zipcode VARCHAR, "
- "sorting_code VARCHAR, "
- "country_code VARCHAR, "
- "date_modified INTEGER NOT NULL DEFAULT 0, "
- "origin VARCHAR DEFAULT '', "
- "language_code VARCHAR, "
- "use_count INTEGER NOT NULL DEFAULT 0, "
- "use_date INTEGER NOT NULL DEFAULT 0, "
- "validity_bitfield UNSIGNED NOT NULL DEFAULT 0) ")) {
- NOTREACHED();
- return false;
- }
- }
- return true;
-}
-
-bool AutofillTable::InitProfileNamesTable() {
- if (!db_->DoesTableExist("autofill_profile_names")) {
- if (!db_->Execute("CREATE TABLE autofill_profile_names ( "
- "guid VARCHAR, "
- "first_name VARCHAR, "
- "middle_name VARCHAR, "
- "last_name VARCHAR, "
- "full_name VARCHAR)")) {
- NOTREACHED();
- return false;
- }
- }
- return true;
-}
-
-bool AutofillTable::InitProfileEmailsTable() {
- if (!db_->DoesTableExist("autofill_profile_emails")) {
- if (!db_->Execute("CREATE TABLE autofill_profile_emails ( "
- "guid VARCHAR, "
- "email VARCHAR)")) {
- NOTREACHED();
- return false;
- }
- }
- return true;
-}
-
-bool AutofillTable::InitProfilePhonesTable() {
- if (!db_->DoesTableExist("autofill_profile_phones")) {
- if (!db_->Execute("CREATE TABLE autofill_profile_phones ( "
- "guid VARCHAR, "
- "number VARCHAR)")) {
- NOTREACHED();
- return false;
- }
- }
- return true;
-}
-
-bool AutofillTable::InitProfileTrashTable() {
- if (!db_->DoesTableExist("autofill_profiles_trash")) {
- if (!db_->Execute("CREATE TABLE autofill_profiles_trash ( "
- "guid VARCHAR)")) {
- NOTREACHED();
- return false;
- }
- }
- return true;
-}
-
-bool AutofillTable::InitMaskedCreditCardsTable() {
- if (!db_->DoesTableExist("masked_credit_cards")) {
- if (!db_->Execute("CREATE TABLE masked_credit_cards ("
- "id VARCHAR,"
- "status VARCHAR,"
- "name_on_card VARCHAR,"
- "network VARCHAR,"
- "last_four VARCHAR,"
- "exp_month INTEGER DEFAULT 0,"
- "exp_year INTEGER DEFAULT 0, "
- "bank_name VARCHAR, "
- "type INTEGER DEFAULT 0)")) {
- NOTREACHED();
- return false;
- }
- }
- return true;
-}
-
-bool AutofillTable::InitUnmaskedCreditCardsTable() {
- if (!db_->DoesTableExist("unmasked_credit_cards")) {
- if (!db_->Execute("CREATE TABLE unmasked_credit_cards ("
- "id VARCHAR,"
- "card_number_encrypted VARCHAR, "
- "use_count INTEGER NOT NULL DEFAULT 0, "
- "use_date INTEGER NOT NULL DEFAULT 0, "
- "unmask_date INTEGER NOT NULL DEFAULT 0)")) {
- NOTREACHED();
- return false;
- }
- }
- return true;
-}
-
-bool AutofillTable::InitServerCardMetadataTable() {
- if (!db_->DoesTableExist("server_card_metadata")) {
- if (!db_->Execute("CREATE TABLE server_card_metadata ("
- "id VARCHAR NOT NULL,"
- "use_count INTEGER NOT NULL DEFAULT 0, "
- "use_date INTEGER NOT NULL DEFAULT 0, "
- "billing_address_id VARCHAR)")) {
- NOTREACHED();
- return false;
- }
- }
- return true;
-}
-
-bool AutofillTable::InitServerAddressesTable() {
- if (!db_->DoesTableExist("server_addresses")) {
- // The space after language_code is necessary to match what sqlite does
- // when it appends the column in migration.
- if (!db_->Execute("CREATE TABLE server_addresses ("
- "id VARCHAR,"
- "company_name VARCHAR,"
- "street_address VARCHAR,"
- "address_1 VARCHAR,"
- "address_2 VARCHAR,"
- "address_3 VARCHAR,"
- "address_4 VARCHAR,"
- "postal_code VARCHAR,"
- "sorting_code VARCHAR,"
- "country_code VARCHAR,"
- "language_code VARCHAR, " // Space required.
- "recipient_name VARCHAR, " // Ditto.
- "phone_number VARCHAR)")) {
- NOTREACHED();
- return false;
- }
- }
- return true;
-}
-
-bool AutofillTable::InitServerAddressMetadataTable() {
- if (!db_->DoesTableExist("server_address_metadata")) {
- if (!db_->Execute("CREATE TABLE server_address_metadata ("
- "id VARCHAR NOT NULL,"
- "use_count INTEGER NOT NULL DEFAULT 0, "
- "use_date INTEGER NOT NULL DEFAULT 0, "
- "has_converted BOOL NOT NULL DEFAULT FALSE)")) {
- NOTREACHED();
- return false;
- }
- }
- return true;
-}
-
-bool AutofillTable::InitAutofillSyncMetadataTable() {
- if (!db_->DoesTableExist("autofill_sync_metadata")) {
- if (!db_->Execute("CREATE TABLE autofill_sync_metadata ("
- "storage_key VARCHAR PRIMARY KEY NOT NULL,"
- "value BLOB)")) {
- NOTREACHED();
- return false;
- }
- }
- return true;
-}
-
-bool AutofillTable::InitModelTypeStateTable() {
- if (!db_->DoesTableExist("autofill_model_type_state")) {
- if (!db_->Execute("CREATE TABLE autofill_model_type_state (id INTEGER "
- "PRIMARY KEY, value BLOB)")) {
- NOTREACHED();
- return false;
- }
- }
- return true;
-}
-
bool AutofillTable::MigrateToVersion54AddI18nFieldsAndRemoveDeprecatedFields() {
sql::Transaction transaction(db_);
if (!transaction.Begin())
@@ -2679,4 +2271,510 @@ bool AutofillTable::MigrateToVersion75AddProfileValidityBitfieldColumn() {
"NULL DEFAULT 0");
}
+bool AutofillTable::MigrateToVersion78AddModelTypeColumns() {
+ // Add the new model type columns to the autofill metadata tables.
+ sql::Transaction transaction(db_);
+ if (!transaction.Begin())
+ return false;
+
+ if (db_->DoesTableExist("autofill_sync_metadata_temp") &&
+ !db_->Execute("DROP TABLE autofill_sync_metadata_temp")) {
+ return false;
+ }
+
+ if (db_->DoesTableExist("autofill_model_type_state_temp") &&
+ !db_->Execute("DROP TABLE autofill_model_type_state_temp")) {
+ return false;
+ }
+
+ if (!db_->Execute("CREATE TABLE autofill_sync_metadata_temp ("
+ "model_type INTEGER NOT NULL, "
+ "storage_key VARCHAR NOT NULL, "
+ "value BLOB, "
+ "PRIMARY KEY (model_type, storage_key))") ||
+ !db_->Execute("CREATE TABLE autofill_model_type_state_temp ("
+ "model_type INTEGER NOT NULL PRIMARY KEY, value BLOB)")) {
+ return false;
+ }
+
+ sql::Statement insert_metadata(
+ db_->GetUniqueStatement("INSERT INTO autofill_sync_metadata_temp "
+ "(model_type, storage_key, value) "
+ "SELECT ?, storage_key, value "
+ "FROM autofill_sync_metadata"));
+ insert_metadata.BindInt(0, syncer::ModelTypeToHistogramInt(syncer::AUTOFILL));
+
+ // Prior to this migration, the table was a singleton, containing only one
+ // entry with id being hard-coded to 1.
+ sql::Statement insert_state(
+ db_->GetUniqueStatement("INSERT INTO autofill_model_type_state_temp "
+ "(model_type, value) SELECT ?, value "
+ "FROM autofill_model_type_state WHERE id=1"));
+ insert_state.BindInt(0, syncer::ModelTypeToHistogramInt(syncer::AUTOFILL));
+
+ if (!insert_metadata.Run() || !insert_state.Run()) {
+ return false;
+ }
+
+ return db_->Execute("DROP TABLE autofill_sync_metadata") &&
+ db_->Execute(
+ "ALTER TABLE autofill_sync_metadata_temp "
+ "RENAME TO autofill_sync_metadata") &&
+ db_->Execute("DROP TABLE autofill_model_type_state") &&
+ db_->Execute(
+ "ALTER TABLE autofill_model_type_state_temp "
+ "RENAME TO autofill_model_type_state") &&
+ transaction.Commit();
+}
+
+bool AutofillTable::AddFormFieldValuesTime(
+ const std::vector<FormFieldData>& elements,
+ std::vector<AutofillChange>* changes,
+ base::Time time) {
+ // Only add one new entry for each unique element name. Use |seen_names| to
+ // track this. Add up to |kMaximumUniqueNames| unique entries per form.
+ const size_t kMaximumUniqueNames = 256;
+ std::set<base::string16> seen_names;
+ bool result = true;
+ for (const FormFieldData& element : elements) {
+ if (seen_names.size() >= kMaximumUniqueNames)
+ break;
+ if (base::ContainsKey(seen_names, element.name))
+ continue;
+ result = result && AddFormFieldValueTime(element, changes, time);
+ seen_names.insert(element.name);
+ }
+ return result;
+}
+
+bool AutofillTable::AddFormFieldValueTime(const FormFieldData& element,
+ std::vector<AutofillChange>* changes,
+ base::Time time) {
+ sql::Statement s_exists(db_->GetUniqueStatement(
+ "SELECT COUNT(*) FROM autofill WHERE name = ? AND value = ?"));
+ s_exists.BindString16(0, element.name);
+ s_exists.BindString16(1, element.value);
+ if (!s_exists.Step())
+ return false;
+
+ bool already_exists = s_exists.ColumnInt(0) > 0;
+ if (already_exists) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE autofill SET date_last_used = ?, count = count + 1 "
+ "WHERE name = ? AND value = ?"));
+ s.BindInt64(0, time.ToTimeT());
+ s.BindString16(1, element.name);
+ s.BindString16(2, element.value);
+ if (!s.Run())
+ return false;
+ } else {
+ time_t time_as_time_t = time.ToTimeT();
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT INTO autofill "
+ "(name, value, value_lower, date_created, date_last_used, count) "
+ "VALUES (?, ?, ?, ?, ?, ?)"));
+ s.BindString16(0, element.name);
+ s.BindString16(1, element.value);
+ s.BindString16(2, base::i18n::ToLower(element.value));
+ s.BindInt64(3, time_as_time_t);
+ s.BindInt64(4, time_as_time_t);
+ s.BindInt(5, 1);
+ if (!s.Run())
+ return false;
+ }
+
+ AutofillChange::Type change_type =
+ already_exists ? AutofillChange::UPDATE : AutofillChange::ADD;
+ changes->push_back(
+ AutofillChange(change_type, AutofillKey(element.name, element.value)));
+ return true;
+}
+
+bool AutofillTable::SupportsMetadataForModelType(
+ syncer::ModelType model_type) const {
+ return (model_type == syncer::AUTOFILL ||
+ model_type == syncer::AUTOFILL_PROFILE);
+}
+
+int AutofillTable::GetKeyValueForModelType(syncer::ModelType model_type) const {
+ return syncer::ModelTypeToStableIdentifier(model_type);
+}
+
+bool AutofillTable::GetAllSyncEntityMetadata(
+ syncer::ModelType model_type,
+ syncer::MetadataBatch* metadata_batch) {
+ DCHECK(SupportsMetadataForModelType(model_type))
+ << "Model type " << model_type << " not supported for metadata";
+ DCHECK(metadata_batch);
+
+ sql::Statement s(
+ db_->GetUniqueStatement("SELECT storage_key, value FROM "
+ "autofill_sync_metadata WHERE model_type=?"));
+ s.BindInt(0, GetKeyValueForModelType(model_type));
+
+ while (s.Step()) {
+ std::string storage_key = s.ColumnString(0);
+ std::string serialized_metadata = s.ColumnString(1);
+ sync_pb::EntityMetadata entity_metadata;
+ if (entity_metadata.ParseFromString(serialized_metadata)) {
+ metadata_batch->AddMetadata(storage_key, entity_metadata);
+ } else {
+ DLOG(WARNING) << "Failed to deserialize AUTOFILL model type "
+ "sync_pb::EntityMetadata.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::GetModelTypeState(syncer::ModelType model_type,
+ sync_pb::ModelTypeState* state) {
+ DCHECK(SupportsMetadataForModelType(model_type))
+ << "Model type " << model_type << " not supported for metadata";
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT value FROM autofill_model_type_state WHERE model_type=?"));
+ s.BindInt(0, GetKeyValueForModelType(model_type));
+
+ if (!s.Step()) {
+ return true;
+ }
+
+ std::string serialized_state = s.ColumnString(0);
+ return state->ParseFromString(serialized_state);
+}
+
+bool AutofillTable::InsertAutofillEntry(const AutofillEntry& entry) {
+ std::string sql =
+ "INSERT INTO autofill "
+ "(name, value, value_lower, date_created, date_last_used, count) "
+ "VALUES (?, ?, ?, ?, ?, ?)";
+ sql::Statement s(db_->GetUniqueStatement(sql.c_str()));
+ s.BindString16(0, entry.key().name());
+ s.BindString16(1, entry.key().value());
+ s.BindString16(2, base::i18n::ToLower(entry.key().value()));
+ s.BindInt64(3, entry.date_created().ToTimeT());
+ s.BindInt64(4, entry.date_last_used().ToTimeT());
+ // TODO(isherman): The counts column is currently synced implicitly as the
+ // number of timestamps. Sync the value explicitly instead, since the DB now
+ // only saves the first and last timestamp, which makes counting timestamps
+ // completely meaningless as a way to track frequency of usage.
+ s.BindInt(5, entry.date_last_used() == entry.date_created() ? 1 : 2);
+ return s.Run();
+}
+
+bool AutofillTable::IsAutofillProfilesTrashEmpty() {
+ sql::Statement s(
+ db_->GetUniqueStatement("SELECT guid FROM autofill_profiles_trash"));
+
+ return !s.Step();
+}
+
+bool AutofillTable::IsAutofillGUIDInTrash(const std::string& guid) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid FROM autofill_profiles_trash WHERE guid = ?"));
+ s.BindString(0, guid);
+
+ return s.Step();
+}
+
+void AutofillTable::AddMaskedCreditCards(
+ const std::vector<CreditCard>& credit_cards) {
+ DCHECK_GT(db_->transaction_nesting(), 0);
+ sql::Statement masked_insert(
+ db_->GetUniqueStatement("INSERT INTO masked_credit_cards("
+ "id," // 0
+ "network," // 1
+ "type," // 2
+ "status," // 3
+ "name_on_card," // 4
+ "last_four," // 5
+ "exp_month," // 6
+ "exp_year," // 7
+ "bank_name)" // 8
+ "VALUES (?,?,?,?,?,?,?,?,?)"));
+ for (const CreditCard& card : credit_cards) {
+ DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type());
+ masked_insert.BindString(0, card.server_id());
+ masked_insert.BindString(1, card.network());
+ masked_insert.BindInt(2, card.card_type());
+ masked_insert.BindString(3,
+ ServerStatusEnumToString(card.GetServerStatus()));
+ masked_insert.BindString16(4, card.GetRawInfo(CREDIT_CARD_NAME_FULL));
+ masked_insert.BindString16(5, card.LastFourDigits());
+ masked_insert.BindString16(6, card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+ masked_insert.BindString16(7,
+ card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+ masked_insert.BindString(8, card.bank_name());
+ masked_insert.Run();
+ masked_insert.Reset(true);
+
+ // Save the use count and use date of the card.
+ UpdateServerCardMetadata(card);
+ }
+}
+
+void AutofillTable::AddUnmaskedCreditCard(const std::string& id,
+ const base::string16& full_number) {
+ sql::Statement s(
+ db_->GetUniqueStatement("INSERT INTO unmasked_credit_cards("
+ "id,"
+ "card_number_encrypted,"
+ "unmask_date)"
+ "VALUES (?,?,?)"));
+ s.BindString(0, id);
+
+ std::string encrypted_data;
+ autofill_table_encryptor_->EncryptString16(full_number, &encrypted_data);
+ s.BindBlob(1, encrypted_data.data(),
+ static_cast<int>(encrypted_data.length()));
+ s.BindInt64(2, AutofillClock::Now().ToInternalValue()); // unmask_date
+
+ s.Run();
+}
+
+bool AutofillTable::DeleteFromMaskedCreditCards(const std::string& id) {
+ sql::Statement s(
+ db_->GetUniqueStatement("DELETE FROM masked_credit_cards WHERE id = ?"));
+ s.BindString(0, id);
+ s.Run();
+ return db_->GetLastChangeCount() > 0;
+}
+
+bool AutofillTable::DeleteFromUnmaskedCreditCards(const std::string& id) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM unmasked_credit_cards WHERE id = ?"));
+ s.BindString(0, id);
+ s.Run();
+ return db_->GetLastChangeCount() > 0;
+}
+
+bool AutofillTable::InitMainTable() {
+ if (!db_->DoesTableExist("autofill")) {
+ if (!db_->Execute("CREATE TABLE autofill ("
+ "name VARCHAR, "
+ "value VARCHAR, "
+ "value_lower VARCHAR, "
+ "date_created INTEGER DEFAULT 0, "
+ "date_last_used INTEGER DEFAULT 0, "
+ "count INTEGER DEFAULT 1, "
+ "PRIMARY KEY (name, value))") ||
+ !db_->Execute("CREATE INDEX autofill_name ON autofill (name)") ||
+ !db_->Execute("CREATE INDEX autofill_name_value_lower ON "
+ "autofill (name, value_lower)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitCreditCardsTable() {
+ if (!db_->DoesTableExist("credit_cards")) {
+ if (!db_->Execute("CREATE TABLE credit_cards ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "name_on_card VARCHAR, "
+ "expiration_month INTEGER, "
+ "expiration_year INTEGER, "
+ "card_number_encrypted BLOB, "
+ "date_modified INTEGER NOT NULL DEFAULT 0, "
+ "origin VARCHAR DEFAULT '', "
+ "use_count INTEGER NOT NULL DEFAULT 0, "
+ "use_date INTEGER NOT NULL DEFAULT 0, "
+ "billing_address_id VARCHAR) ")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool AutofillTable::InitProfilesTable() {
+ if (!db_->DoesTableExist("autofill_profiles")) {
+ if (!db_->Execute("CREATE TABLE autofill_profiles ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "company_name VARCHAR, "
+ "street_address VARCHAR, "
+ "dependent_locality VARCHAR, "
+ "city VARCHAR, "
+ "state VARCHAR, "
+ "zipcode VARCHAR, "
+ "sorting_code VARCHAR, "
+ "country_code VARCHAR, "
+ "date_modified INTEGER NOT NULL DEFAULT 0, "
+ "origin VARCHAR DEFAULT '', "
+ "language_code VARCHAR, "
+ "use_count INTEGER NOT NULL DEFAULT 0, "
+ "use_date INTEGER NOT NULL DEFAULT 0, "
+ "validity_bitfield UNSIGNED NOT NULL DEFAULT 0) ")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitProfileNamesTable() {
+ if (!db_->DoesTableExist("autofill_profile_names")) {
+ if (!db_->Execute("CREATE TABLE autofill_profile_names ( "
+ "guid VARCHAR, "
+ "first_name VARCHAR, "
+ "middle_name VARCHAR, "
+ "last_name VARCHAR, "
+ "full_name VARCHAR)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitProfileEmailsTable() {
+ if (!db_->DoesTableExist("autofill_profile_emails")) {
+ if (!db_->Execute("CREATE TABLE autofill_profile_emails ( "
+ "guid VARCHAR, "
+ "email VARCHAR)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitProfilePhonesTable() {
+ if (!db_->DoesTableExist("autofill_profile_phones")) {
+ if (!db_->Execute("CREATE TABLE autofill_profile_phones ( "
+ "guid VARCHAR, "
+ "number VARCHAR)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitProfileTrashTable() {
+ if (!db_->DoesTableExist("autofill_profiles_trash")) {
+ if (!db_->Execute("CREATE TABLE autofill_profiles_trash ( "
+ "guid VARCHAR)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitMaskedCreditCardsTable() {
+ if (!db_->DoesTableExist("masked_credit_cards")) {
+ if (!db_->Execute("CREATE TABLE masked_credit_cards ("
+ "id VARCHAR,"
+ "status VARCHAR,"
+ "name_on_card VARCHAR,"
+ "network VARCHAR,"
+ "last_four VARCHAR,"
+ "exp_month INTEGER DEFAULT 0,"
+ "exp_year INTEGER DEFAULT 0, "
+ "bank_name VARCHAR, "
+ "type INTEGER DEFAULT 0)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitUnmaskedCreditCardsTable() {
+ if (!db_->DoesTableExist("unmasked_credit_cards")) {
+ if (!db_->Execute("CREATE TABLE unmasked_credit_cards ("
+ "id VARCHAR,"
+ "card_number_encrypted VARCHAR, "
+ "use_count INTEGER NOT NULL DEFAULT 0, "
+ "use_date INTEGER NOT NULL DEFAULT 0, "
+ "unmask_date INTEGER NOT NULL DEFAULT 0)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitServerCardMetadataTable() {
+ if (!db_->DoesTableExist("server_card_metadata")) {
+ if (!db_->Execute("CREATE TABLE server_card_metadata ("
+ "id VARCHAR NOT NULL,"
+ "use_count INTEGER NOT NULL DEFAULT 0, "
+ "use_date INTEGER NOT NULL DEFAULT 0, "
+ "billing_address_id VARCHAR)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitServerAddressesTable() {
+ if (!db_->DoesTableExist("server_addresses")) {
+ // The space after language_code is necessary to match what sqlite does
+ // when it appends the column in migration.
+ if (!db_->Execute("CREATE TABLE server_addresses ("
+ "id VARCHAR,"
+ "company_name VARCHAR,"
+ "street_address VARCHAR,"
+ "address_1 VARCHAR,"
+ "address_2 VARCHAR,"
+ "address_3 VARCHAR,"
+ "address_4 VARCHAR,"
+ "postal_code VARCHAR,"
+ "sorting_code VARCHAR,"
+ "country_code VARCHAR,"
+ "language_code VARCHAR, " // Space required.
+ "recipient_name VARCHAR, " // Ditto.
+ "phone_number VARCHAR)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitServerAddressMetadataTable() {
+ if (!db_->DoesTableExist("server_address_metadata")) {
+ if (!db_->Execute("CREATE TABLE server_address_metadata ("
+ "id VARCHAR NOT NULL,"
+ "use_count INTEGER NOT NULL DEFAULT 0, "
+ "use_date INTEGER NOT NULL DEFAULT 0, "
+ "has_converted BOOL NOT NULL DEFAULT FALSE)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitAutofillSyncMetadataTable() {
+ if (!db_->DoesTableExist("autofill_sync_metadata")) {
+ if (!db_->Execute("CREATE TABLE autofill_sync_metadata ("
+ "model_type INTEGER NOT NULL, "
+ "storage_key VARCHAR NOT NULL, "
+ "value BLOB, "
+ "PRIMARY KEY (model_type, storage_key))")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitModelTypeStateTable() {
+ if (!db_->DoesTableExist("autofill_model_type_state")) {
+ if (!db_->Execute("CREATE TABLE autofill_model_type_state ("
+ "model_type INTEGER NOT NULL PRIMARY KEY, value BLOB)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.h b/chromium/components/autofill/core/browser/webdata/autofill_table.h
index 2d720be9ada..bb34d9c4d51 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_table.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table.h
@@ -249,14 +249,19 @@ struct FormFieldData;
// autofill_sync_metadata
// Sync-specific metadata for autofill records.
//
+// model_type An int value corresponding to syncer::ModelType enum.
+// Added in version 78.
// storage_key A string that uniquely identifies the metadata record
// as well as the corresponding autofill record.
// value The serialized EntityMetadata record.
//
// autofill_model_type_state
-// Single row table that contains the sync ModelTypeState
-// for the autofill model type.
+// Contains sync ModelTypeStates for autofill model types.
//
+// model_type An int value corresponding to syncer::ModelType enum.
+// Added in version 78. Previously, the table was used only
+// for one model type, there was an id column with value 1
+// for the single entry.
// value The serialized ModelTypeState record.
class AutofillTable : public WebDatabaseTable,
@@ -398,6 +403,11 @@ class AutofillTable : public WebDatabaseTable,
// rather than "error").
bool ClearAllServerData();
+ // Deletes all data from the local card and profiles table. Returns true if
+ // any data was deleted, false if not (so false means "commit not needed"
+ // rather than "error").
+ bool ClearAllLocalData();
+
// Removes rows from autofill_profiles and credit_cards if they were created
// on or after |delete_begin| and strictly before |delete_end|. Returns the
// list of deleted profile guids in |profile_guids|. Return value is true if
@@ -435,6 +445,9 @@ class AutofillTable : public WebDatabaseTable,
// Clear all profiles.
bool ClearAutofillProfiles();
+ // Clear all credit cards.
+ bool ClearCreditCards();
+
// Read all the stored metadata for |model_type| and fill |metadata_batch|
// with it.
bool GetAllSyncMetadata(syncer::ModelType model_type,
@@ -479,6 +492,7 @@ class AutofillTable : public WebDatabaseTable,
bool MigrateToVersion73AddMaskedCardBankName();
bool MigrateToVersion74AddServerCardTypeColumn();
bool MigrateToVersion75AddProfileValidityBitfieldColumn();
+ bool MigrateToVersion78AddModelTypeColumns();
// Max data length saved in the table, AKA the maximum length allowed for
// form data.
@@ -535,6 +549,9 @@ class AutofillTable : public WebDatabaseTable,
std::vector<AutofillChange>* changes,
base::Time time);
+ bool SupportsMetadataForModelType(syncer::ModelType model_type) const;
+ int GetKeyValueForModelType(syncer::ModelType model_type) const;
+
bool GetAllSyncEntityMetadata(syncer::ModelType model_type,
syncer::MetadataBatch* metadata_batch);
@@ -564,7 +581,6 @@ class AutofillTable : public WebDatabaseTable,
bool InitMainTable();
bool InitCreditCardsTable();
- bool InitDatesTable();
bool InitProfilesTable();
bool InitProfileNamesTable();
bool InitProfileEmailsTable();
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 fed5711f60c..283dcc5c327 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc
@@ -2340,34 +2340,45 @@ INSTANTIATE_TEST_CASE_P(
0,
{nullptr, nullptr}}));
-TEST_F(AutofillTableTest, AutofillNoMetadata) {
+class AutofillTableTestPerModelType
+ : public AutofillTableTest,
+ public testing::WithParamInterface<syncer::ModelType> {
+ public:
+ AutofillTableTestPerModelType() {}
+ ~AutofillTableTestPerModelType() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutofillTableTestPerModelType);
+};
+
+TEST_P(AutofillTableTestPerModelType, AutofillNoMetadata) {
+ syncer::ModelType model_type = GetParam();
MetadataBatch metadata_batch;
- EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
+ EXPECT_TRUE(table_->GetAllSyncMetadata(model_type, &metadata_batch));
EXPECT_EQ(0u, metadata_batch.TakeAllMetadata().size());
EXPECT_EQ(ModelTypeState().SerializeAsString(),
metadata_batch.GetModelTypeState().SerializeAsString());
}
-TEST_F(AutofillTableTest, AutofillGetAllSyncMetadata) {
+TEST_P(AutofillTableTestPerModelType, AutofillGetAllSyncMetadata) {
+ syncer::ModelType model_type = GetParam();
EntityMetadata metadata;
std::string storage_key = "storage_key";
std::string storage_key2 = "storage_key2";
metadata.set_sequence_number(1);
- EXPECT_TRUE(
- table_->UpdateSyncMetadata(syncer::AUTOFILL, storage_key, metadata));
+ EXPECT_TRUE(table_->UpdateSyncMetadata(model_type, storage_key, metadata));
ModelTypeState model_type_state;
model_type_state.set_initial_sync_done(true);
- EXPECT_TRUE(table_->UpdateModelTypeState(syncer::AUTOFILL, model_type_state));
+ EXPECT_TRUE(table_->UpdateModelTypeState(model_type, model_type_state));
metadata.set_sequence_number(2);
- EXPECT_TRUE(
- table_->UpdateSyncMetadata(syncer::AUTOFILL, storage_key2, metadata));
+ EXPECT_TRUE(table_->UpdateSyncMetadata(model_type, storage_key2, metadata));
MetadataBatch metadata_batch;
- EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
+ EXPECT_TRUE(table_->GetAllSyncMetadata(model_type, &metadata_batch));
EXPECT_TRUE(metadata_batch.GetModelTypeState().initial_sync_done());
@@ -2379,13 +2390,14 @@ TEST_F(AutofillTableTest, AutofillGetAllSyncMetadata) {
// Now check that a model type state update replaces the old value
model_type_state.set_initial_sync_done(false);
- EXPECT_TRUE(table_->UpdateModelTypeState(syncer::AUTOFILL, model_type_state));
+ EXPECT_TRUE(table_->UpdateModelTypeState(model_type, model_type_state));
- EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
+ EXPECT_TRUE(table_->GetAllSyncMetadata(model_type, &metadata_batch));
EXPECT_FALSE(metadata_batch.GetModelTypeState().initial_sync_done());
}
-TEST_F(AutofillTableTest, AutofillWriteThenDeleteSyncMetadata) {
+TEST_P(AutofillTableTestPerModelType, AutofillWriteThenDeleteSyncMetadata) {
+ syncer::ModelType model_type = GetParam();
EntityMetadata metadata;
MetadataBatch metadata_batch;
std::string storage_key = "storage_key";
@@ -2396,47 +2408,55 @@ TEST_F(AutofillTableTest, AutofillWriteThenDeleteSyncMetadata) {
metadata.set_client_tag_hash("client_hash");
// Write the data into the store.
- EXPECT_TRUE(
- table_->UpdateSyncMetadata(syncer::AUTOFILL, storage_key, metadata));
- EXPECT_TRUE(table_->UpdateModelTypeState(syncer::AUTOFILL, model_type_state));
+ EXPECT_TRUE(table_->UpdateSyncMetadata(model_type, storage_key, metadata));
+ EXPECT_TRUE(table_->UpdateModelTypeState(model_type, model_type_state));
// Delete the data we just wrote.
- EXPECT_TRUE(table_->ClearSyncMetadata(syncer::AUTOFILL, storage_key));
+ EXPECT_TRUE(table_->ClearSyncMetadata(model_type, storage_key));
// It shouldn't be there any more.
- EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
+ EXPECT_TRUE(table_->GetAllSyncMetadata(model_type, &metadata_batch));
EntityMetadataMap metadata_records = metadata_batch.TakeAllMetadata();
EXPECT_EQ(metadata_records.size(), 0u);
// Now delete the model type state.
- EXPECT_TRUE(table_->ClearModelTypeState(syncer::AUTOFILL));
- EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
+ EXPECT_TRUE(table_->ClearModelTypeState(model_type));
+ EXPECT_TRUE(table_->GetAllSyncMetadata(model_type, &metadata_batch));
EXPECT_EQ(ModelTypeState().SerializeAsString(),
metadata_batch.GetModelTypeState().SerializeAsString());
}
-TEST_F(AutofillTableTest, AutofillCorruptSyncMetadata) {
+TEST_P(AutofillTableTestPerModelType, AutofillCorruptSyncMetadata) {
+ syncer::ModelType model_type = GetParam();
MetadataBatch metadata_batch;
sql::Statement s(db_->GetSQLConnection()->GetUniqueStatement(
"INSERT OR REPLACE INTO autofill_sync_metadata "
- "(storage_key, value) VALUES(?, ?)"));
- s.BindString(0, "storage_key");
- s.BindString(1, "unparseable");
+ "(model_type, storage_key, value) VALUES(?, ?, ?)"));
+ s.BindInt(0, syncer::ModelTypeToStableIdentifier(model_type));
+ s.BindString(1, "storage_key");
+ s.BindString(2, "unparseable");
EXPECT_TRUE(s.Run());
- EXPECT_FALSE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
+ EXPECT_FALSE(table_->GetAllSyncMetadata(model_type, &metadata_batch));
}
-TEST_F(AutofillTableTest, AutofillCorruptModelTypeState) {
+TEST_P(AutofillTableTestPerModelType, AutofillCorruptModelTypeState) {
+ syncer::ModelType model_type = GetParam();
MetadataBatch metadata_batch;
sql::Statement s(db_->GetSQLConnection()->GetUniqueStatement(
"INSERT OR REPLACE INTO autofill_model_type_state "
- "(rowid, value) VALUES(1, ?)"));
- s.BindString(0, "unparseable");
+ "(model_type, value) VALUES(?, ?)"));
+ s.BindInt(0, syncer::ModelTypeToStableIdentifier(model_type));
+ s.BindString(1, "unparseable");
EXPECT_TRUE(s.Run());
- EXPECT_FALSE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
+ EXPECT_FALSE(table_->GetAllSyncMetadata(model_type, &metadata_batch));
}
+INSTANTIATE_TEST_CASE_P(AutofillTableTest,
+ AutofillTableTestPerModelType,
+ testing::Values(syncer::AUTOFILL,
+ syncer::AUTOFILL_PROFILE));
+
TEST_F(AutofillTableTest, RemoveOrphanAutofillTableRows) {
// Populate the different tables.
ASSERT_TRUE(db_->GetSQLConnection()->Execute(
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc
index 3ff3a516adb..10f35b38615 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc
@@ -273,19 +273,22 @@ void AutofillWalletSyncableService::PopulateWalletCardsAndAddresses(
DCHECK_EQ(syncer::AUTOFILL_WALLET_DATA, data.GetDataType());
const sync_pb::AutofillWalletSpecifics& autofill_specifics =
data.GetSpecifics().autofill_wallet();
- if (autofill_specifics.type() ==
- sync_pb::AutofillWalletSpecifics::MASKED_CREDIT_CARD) {
- wallet_cards->push_back(
- CardFromSpecifics(autofill_specifics.masked_card()));
- } else {
- DCHECK_EQ(sync_pb::AutofillWalletSpecifics::POSTAL_ADDRESS,
- autofill_specifics.type());
- wallet_addresses->push_back(
- ProfileFromSpecifics(autofill_specifics.address()));
-
- // Map the sync billing address id to the profile's id.
- ids[autofill_specifics.address().id()] =
- wallet_addresses->back().server_id();
+ switch (autofill_specifics.type()) {
+ case sync_pb::AutofillWalletSpecifics::MASKED_CREDIT_CARD:
+ wallet_cards->push_back(
+ CardFromSpecifics(autofill_specifics.masked_card()));
+ break;
+ case sync_pb::AutofillWalletSpecifics::POSTAL_ADDRESS:
+ wallet_addresses->push_back(
+ ProfileFromSpecifics(autofill_specifics.address()));
+
+ // Map the sync billing address id to the profile's id.
+ ids[autofill_specifics.address().id()] =
+ wallet_addresses->back().server_id();
+ break;
+ case sync_pb::AutofillWalletSpecifics::UNKNOWN:
+ // Just ignore new entry types that the client doesn't know about.
+ break;
}
}
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
index d48dd083705..d57b01c6b04 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
@@ -429,6 +429,16 @@ WebDatabase::State AutofillWebDataBackendImpl::ClearAllServerData(
return WebDatabase::COMMIT_NOT_NEEDED;
}
+WebDatabase::State AutofillWebDataBackendImpl::ClearAllLocalData(
+ WebDatabase* db) {
+ DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
+ if (AutofillTable::FromWebDatabase(db)->ClearAllLocalData()) {
+ NotifyOfMultipleAutofillChanges();
+ return WebDatabase::COMMIT_NEEDED;
+ }
+ return WebDatabase::COMMIT_NOT_NEEDED;
+}
+
WebDatabase::State
AutofillWebDataBackendImpl::RemoveAutofillDataModifiedBetween(
const base::Time& delete_begin,
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 59d7b12c3bc..91c379f30d2 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
@@ -164,6 +164,7 @@ class AutofillWebDataBackendImpl
WebDatabase* db);
WebDatabase::State ClearAllServerData(WebDatabase* db);
+ WebDatabase::State ClearAllLocalData(WebDatabase* db);
// Removes Autofill records from the database. Valid only for local
// cards/profiles.
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc
index c9d3d8a5dd0..4d9db9dd58b 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc
@@ -220,6 +220,12 @@ void AutofillWebDataService::ClearAllServerData() {
autofill_backend_));
}
+void AutofillWebDataService::ClearAllLocalData() {
+ wdbs_->ScheduleDBTask(
+ FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::ClearAllLocalData, autofill_backend_));
+}
+
void AutofillWebDataService::UpdateServerCardMetadata(
const CreditCard& credit_card) {
wdbs_->ScheduleDBTask(
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h
index fe42a7e9972..8b26dc1c8ad 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h
@@ -99,6 +99,7 @@ class AutofillWebDataService : public AutofillWebData,
void MaskServerCreditCard(const std::string& id) override;
void ClearAllServerData();
+ void ClearAllLocalData();
void UpdateServerCardMetadata(const CreditCard& credit_card) override;
void UpdateServerAddressMetadata(const AutofillProfile& profile) override;
diff --git a/chromium/components/autofill/core/common/BUILD.gn b/chromium/components/autofill/core/common/BUILD.gn
index 8e15cd72652..32aa82cca98 100644
--- a/chromium/components/autofill/core/common/BUILD.gn
+++ b/chromium/components/autofill/core/common/BUILD.gn
@@ -24,6 +24,7 @@ static_library("common") {
"autofill_switches.h",
"autofill_util.cc",
"autofill_util.h",
+ "filling_status.h",
"form_data.cc",
"form_data.h",
"form_data_predictions.cc",
@@ -52,6 +53,8 @@ static_library("common") {
"//base",
"//base:i18n",
"//components/variations",
+ "//ui/base",
+ "//ui/gfx/geometry",
"//url",
]
diff --git a/chromium/components/autofill/core/common/autofill_features.cc b/chromium/components/autofill/core/common/autofill_features.cc
index 9846b16cc64..7534892decf 100644
--- a/chromium/components/autofill/core/common/autofill_features.cc
+++ b/chromium/components/autofill/core/common/autofill_features.cc
@@ -17,16 +17,44 @@ const base::Feature kAutofillAddressNormalizer{
const base::Feature kAutofillCacheQueryResponses{
"AutofillCacheQueryResponses", base::FEATURE_ENABLED_BY_DEFAULT};
+// Controls whether the credit card downstream keyboard accessory shows
+// the Google Pay logo animation on iOS.
+const base::Feature kAutofillDownstreamUseGooglePayBrandingOniOS{
+ "AutofillDownstreamUseGooglePayBrandingOniOS",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
// Controls whether Autofill attemps to fill dynamically changing forms.
const base::Feature kAutofillDynamicForms{"AutofillDynamicForms",
base::FEATURE_DISABLED_BY_DEFAULT};
+// Controls whether the server credit cards are saved only in the ephemeral
+// account-based storage, instead of the persistent local storage.
+const base::Feature kAutofillEnableAccountWalletStorage{
+ "AutofillEnableAccountWalletStorage", base::FEATURE_DISABLED_BY_DEFAULT};
+
// Controls whether the server credit cards are offered to be filled and
// uploaded to Google Pay if the sync service is in auth error.
const base::Feature kAutofillEnablePaymentsInteractionsOnAuthError{
"AutofillDontOfferServerCardsOnAuthError",
base::FEATURE_DISABLED_BY_DEFAULT};
+// Controls whether Autofill should fill fields previously filled by the
+// website.
+const base::Feature kAutofillPrefilledFields{"AutofillPrefilledFields",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
+// Controls whether autofill suggestions are filtered by field values previously
+// filled by website.
+const base::Feature kAutofillShowAllSuggestionsOnPrefilledForms{
+ "AutofillShowAllSuggestionsOnPrefilledForms",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Controls whether Autofill should rationalize repeated server type
+// predictions.
+const base::Feature kAutofillRationalizeRepeatedServerPredictions{
+ "kAutofillRationalizeRepeatedServerPredictions",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
// Controls whether or not a minimum number of fields is required before
// heuristic field type prediction is run for a form.
const base::Feature kAutofillEnforceMinRequiredFieldsForHeuristics{
@@ -45,9 +73,9 @@ const base::Feature kAutofillEnforceMinRequiredFieldsForUpload{
"AutofillEnforceMinRequiredFieldsForUpload",
base::FEATURE_ENABLED_BY_DEFAULT};
-// Controls whether credit card suggestions are made on insecure pages.
-const base::Feature kAutofillRequireSecureCreditCardContext{
- "AutofillRequireSecureCreditCardContext", base::FEATURE_ENABLED_BY_DEFAULT};
+// Controls whether the manual fill fallback will be present.
+const base::Feature kAutofillManualFallback{"AutofillManualFallback",
+ base::FEATURE_DISABLED_BY_DEFAULT};
// Controls whether Full Server credit cards should be reset when the sync
// service is in an auth error state.
@@ -68,6 +96,13 @@ const base::Feature kAutofillSendExperimentIdsInPaymentsRPCs{
"AutofillSendExperimentIdsInPaymentsRPCs",
base::FEATURE_ENABLED_BY_DEFAULT};
+// If enabled, only countries of recently-used addresses are sent in the
+// GetUploadDetails call to Payments. If disabled, whole recently-used addresses
+// are sent.
+const base::Feature kAutofillSendOnlyCountryInGetUploadDetails{
+ "AutofillSendOnlyCountryInGetUploadDetails",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
// Controls whether we show warnings in the Dev console for misused autocomplete
// types.
const base::Feature kAutofillShowAutocompleteConsoleWarnings{
@@ -84,11 +119,24 @@ const base::Feature kAutofillShowTypePredictions{
const base::Feature kAutofillSkipComparingInferredLabels{
"AutofillSkipComparingInferredLabels", base::FEATURE_DISABLED_BY_DEFAULT};
+// Controls whether ELO cards should be uploaded to Google Payments.
+const base::Feature kAutofillUpstreamDisallowElo{
+ "AutofillUpstreamDisallowElo", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Controls whether JCB cards should be uploaded to Google Payments.
+const base::Feature kAutofillUpstreamDisallowJcb{
+ "AutofillUpstreamDisallowJcb", base::FEATURE_DISABLED_BY_DEFAULT};
+
// Controls whether the credit card upload bubble shows the Google Pay logo and
// a shorter "Save card?" header message on mobile.
const base::Feature kAutofillUpstreamUseGooglePayBrandingOnMobile{
"AutofillUpstreamUseGooglePayOnAndroidBranding",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
+// Controls whether password generation is offered automatically on fields
+// percieved as eligible for generation.
+const base::Feature kAutomaticPasswordGeneration = {
+ "AutomaticPasswordGeneration", base::FEATURE_DISABLED_BY_DEFAULT};
// Controls whether or not the autofill UI triggers on a single click.
const base::Feature kSingleClickAutofill{"SingleClickAutofill",
diff --git a/chromium/components/autofill/core/common/autofill_features.h b/chromium/components/autofill/core/common/autofill_features.h
index b8e577e7f38..7da46eb4253 100644
--- a/chromium/components/autofill/core/common/autofill_features.h
+++ b/chromium/components/autofill/core/common/autofill_features.h
@@ -13,21 +13,30 @@ namespace features {
// All features in alphabetical order.
extern const base::Feature kAutofillAddressNormalizer;
extern const base::Feature kAutofillCacheQueryResponses;
+extern const base::Feature kAutofillDownstreamUseGooglePayBrandingOniOS;
extern const base::Feature kAutofillDynamicForms;
+extern const base::Feature kAutofillEnableAccountWalletStorage;
extern const base::Feature kAutofillEnablePaymentsInteractionsOnAuthError;
extern const base::Feature kAutofillEnforceMinRequiredFieldsForHeuristics;
extern const base::Feature kAutofillEnforceMinRequiredFieldsForQuery;
extern const base::Feature kAutofillEnforceMinRequiredFieldsForUpload;
-extern const base::Feature kAutofillRequireSecureCreditCardContext;
+extern const base::Feature kAutofillManualFallback;
+extern const base::Feature kAutofillPrefilledFields;
extern const base::Feature kAutofillResetFullServerCardsOnAuthError;
extern const base::Feature kAutofillRestrictUnownedFieldsToFormlessCheckout;
extern const base::Feature kAutofillSendExperimentIdsInPaymentsRPCs;
+extern const base::Feature kAutofillSendOnlyCountryInGetUploadDetails;
+extern const base::Feature kAutofillShowAllSuggestionsOnPrefilledForms;
extern const base::Feature kAutofillShowAutocompleteConsoleWarnings;
extern const base::Feature kAutofillShowTypePredictions;
extern const base::Feature kAutofillSkipComparingInferredLabels;
+extern const base::Feature kAutofillUpstreamDisallowElo;
+extern const base::Feature kAutofillUpstreamDisallowJcb;
extern const base::Feature kAutofillUpstreamUseGooglePayBrandingOnMobile;
+extern const base::Feature kAutomaticPasswordGeneration;
extern const base::Feature kSingleClickAutofill;
-
+extern const base::Feature kAutofillPrefilledFields;
+extern const base::Feature kAutofillRationalizeRepeatedServerPredictions;
} // namespace features
} // namespace autofill
diff --git a/chromium/components/autofill/core/common/autofill_l10n_util_unittest.cc b/chromium/components/autofill/core/common/autofill_l10n_util_unittest.cc
index b724c6b41d8..6692ebee5ba 100644
--- a/chromium/components/autofill/core/common/autofill_l10n_util_unittest.cc
+++ b/chromium/components/autofill/core/common/autofill_l10n_util_unittest.cc
@@ -4,7 +4,7 @@
#include "components/autofill/core/common/autofill_l10n_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/icu/source/common/unicode/locid.h"
diff --git a/chromium/components/autofill/core/common/autofill_pref_names.cc b/chromium/components/autofill/core/common/autofill_pref_names.cc
index cb9bf1d1db4..5cd3354a4c0 100644
--- a/chromium/components/autofill/core/common/autofill_pref_names.cc
+++ b/chromium/components/autofill/core/common/autofill_pref_names.cc
@@ -27,6 +27,9 @@ const char kAutofillCreditCardSigninPromoImpressionCount[] =
"autofill.credit_card_signin_promo_impression_count";
// Boolean that is true if Autofill is enabled and allowed to save profile data.
+const char kAutofillProfileEnabled[] = "autofill.profile_enabled";
+
+// Boolean that is true if Autofill is enabled and allowed to save data.
const char kAutofillEnabled[] = "autofill.enabled";
// Integer that is set to the last version where the profile deduping routine
diff --git a/chromium/components/autofill/core/common/autofill_pref_names.h b/chromium/components/autofill/core/common/autofill_pref_names.h
index 43c2ba84974..ee224e8aa8c 100644
--- a/chromium/components/autofill/core/common/autofill_pref_names.h
+++ b/chromium/components/autofill/core/common/autofill_pref_names.h
@@ -15,6 +15,7 @@ extern const char kAutofillAcceptSaveCreditCardPromptState[];
extern const char kAutofillBillingCustomerNumber[];
extern const char kAutofillCreditCardEnabled[];
extern const char kAutofillCreditCardSigninPromoImpressionCount[];
+extern const char kAutofillProfileEnabled[];
extern const char kAutofillEnabled[];
extern const char kAutofillLastVersionDeduped[];
extern const char kAutofillLastVersionDisusedAddressesDeleted[];
diff --git a/chromium/components/autofill/core/common/autofill_regex_constants.cc b/chromium/components/autofill/core/common/autofill_regex_constants.cc
index a55f28306f1..fe72126c7a2 100644
--- a/chromium/components/autofill/core/common/autofill_regex_constants.cc
+++ b/chromium/components/autofill/core/common/autofill_regex_constants.cc
@@ -43,7 +43,11 @@ const char kAddressLine1Re[] =
"|地址" // zh-CN
"|^주소.?$|주소.?1"; // ko-KR
const char kAddressLine1LabelRe[] =
- "address"
+ "(^\\W*address)"
+ "|(address\\W*$)"
+ "|(?:shipping|billing|mailing|pick.?up|drop.?off|delivery|sender|postal|"
+ "recipient|home|work|office|school|business|mail)[\\s\\-]+address"
+ "|address\\s+(of|for|to|from)"
"|adresse" // fr-FR
"|indirizzo" // it-IT
"|住所" // ja-JP
@@ -109,7 +113,7 @@ const char kCityRe[] =
"|分區" // zh-TW
"|^시[^도·・]|시[·・]?군[·・]?구"; // ko-KR
const char kStateRe[] =
- "(?<!united )state|county|region|province"
+ "(?<!(united|hist|history).?)state|county|region|province"
"|land" // de-DE
"|county|principality" // en-UK
"|都道府県" // ja-JP
@@ -119,8 +123,14 @@ const char kStateRe[] =
"|地區" // zh-TW
"|^시[·・]?도"; // ko-KR
+/////////////////////////////////////////////////////////////////////////////
+// search_field.cc
+/////////////////////////////////////////////////////////////////////////////
const char kSearchTermRe[] =
- "search"
+ "^q$"
+ "|search"
+ "|query"
+ "|qry"
"|suche.*" // de-DE
"|搜索" // zh-CN zh-TW
"|探す|検索" // ja-JP to search
diff --git a/chromium/components/autofill/core/common/autofill_switches.cc b/chromium/components/autofill/core/common/autofill_switches.cc
index c48c96a70b5..84d698777bb 100644
--- a/chromium/components/autofill/core/common/autofill_switches.cc
+++ b/chromium/components/autofill/core/common/autofill_switches.cc
@@ -17,19 +17,11 @@ const char kAutofillServerURL[] = "autofill-server-url";
const char kDisableOfferStoreUnmaskedWalletCards[] =
"disable-offer-store-unmasked-wallet-cards";
-// Disables password generation when we detect that the user is going through
-// account creation.
-const char kDisablePasswordGeneration[] = "disable-password-generation";
-
// Force showing the local save checkbox in the autofill dialog box for getting
// the full credit card number for a wallet card.
const char kEnableOfferStoreUnmaskedWalletCards[] =
"enable-offer-store-unmasked-wallet-cards";
-// Enables password generation when we detect that the user is going through
-// account creation.
-const char kEnablePasswordGeneration[] = "enable-password-generation";
-
// Enables suggestions with substring matching instead of prefix matching.
const char kEnableSuggestionsWithSubstringMatch[] =
"enable-suggestions-with-substring-match";
diff --git a/chromium/components/autofill/core/common/autofill_switches.h b/chromium/components/autofill/core/common/autofill_switches.h
index 240367104bb..edb0e96733f 100644
--- a/chromium/components/autofill/core/common/autofill_switches.h
+++ b/chromium/components/autofill/core/common/autofill_switches.h
@@ -14,9 +14,7 @@ namespace switches {
// alongside the definition of their values in the .cc file.
extern const char kAutofillServerURL[];
extern const char kDisableOfferStoreUnmaskedWalletCards[];
-extern const char kDisablePasswordGeneration[];
extern const char kEnableOfferStoreUnmaskedWalletCards[];
-extern const char kEnablePasswordGeneration[];
extern const char kEnableSuggestionsWithSubstringMatch[];
extern const char kIgnoreAutocompleteOffForAutofill[];
extern const char kLocalHeuristicsOnlyForPasswordGeneration[];
diff --git a/chromium/components/autofill/core/common/autofill_util.cc b/chromium/components/autofill/core/common/autofill_util.cc
index b19f35b23c8..44917ead351 100644
--- a/chromium/components/autofill/core/common/autofill_util.cc
+++ b/chromium/components/autofill/core/common/autofill_util.cc
@@ -201,4 +201,12 @@ bool SanitizedFieldIsEmpty(const base::string16& value) {
return (value.find_first_not_of(formatting) == base::StringPiece::npos);
}
+bool ShouldAutoselectFirstSuggestionOnArrowDown() {
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+ return true;
+#else
+ return false;
+#endif
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/common/autofill_util.h b/chromium/components/autofill/core/common/autofill_util.h
index 533945e9d24..92ddb896a0e 100644
--- a/chromium/components/autofill/core/common/autofill_util.h
+++ b/chromium/components/autofill/core/common/autofill_util.h
@@ -94,6 +94,10 @@ std::vector<std::string> LowercaseAndTokenizeAttributeString(
// entered by the website and not a real value entered by the user.
bool SanitizedFieldIsEmpty(const base::string16& value);
+// Returns true if the first suggestion should be autoselected when the autofill
+// dropdown is shown due to an arrow down event. Enabled on desktop only.
+bool ShouldAutoselectFirstSuggestionOnArrowDown();
+
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_UTIL_H_
diff --git a/chromium/components/autofill/core/common/filling_status.h b/chromium/components/autofill/core/common/filling_status.h
new file mode 100644
index 00000000000..addeadac2bd
--- /dev/null
+++ b/chromium/components/autofill/core/common/filling_status.h
@@ -0,0 +1,19 @@
+// 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_COMMON_FILLING_STATUS_H_
+#define COMPONENTS_AUTOFILL_CORE_COMMON_FILLING_STATUS_H_
+
+namespace autofill {
+
+// Describes how a manual filling attempt ended.
+enum class FillingStatus {
+ SUCCESS, // The selected credential was filled into the field.
+ ERROR_NO_VALID_FIELD, // The focused field was invalid or focus was lost.
+ ERROR_NOT_ALLOWED, // Filling not permitted (e.g. password in email field).
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_FILLING_STATUS_H_
diff --git a/chromium/components/autofill/core/common/form_data.cc b/chromium/components/autofill/core/common/form_data.cc
index 3fb7a74bb24..0bf0ff8ad6f 100644
--- a/chromium/components/autofill/core/common/form_data.cc
+++ b/chromium/components/autofill/core/common/form_data.cc
@@ -78,7 +78,8 @@ FormData::FormData(const FormData& data)
is_form_tag(data.is_form_tag),
is_formless_checkout(data.is_formless_checkout),
unique_renderer_id(data.unique_renderer_id),
- fields(data.fields) {}
+ fields(data.fields),
+ username_predictions(data.username_predictions) {}
FormData::~FormData() {
}
@@ -109,12 +110,23 @@ bool FormData::SimilarFormAs(const FormData& form) const {
return true;
}
+bool FormData::DynamicallySameFormAs(const FormData& form) const {
+ if (name != form.name || fields.size() != form.fields.size())
+ return false;
+ for (size_t i = 0; i < fields.size(); ++i) {
+ if (!fields[i].DynamicallySameFieldAs(form.fields[i]))
+ return false;
+ }
+ return true;
+}
+
bool FormData::operator==(const FormData& form) const {
return name == form.name && origin == form.origin && action == form.action &&
unique_renderer_id == form.unique_renderer_id &&
is_form_tag == form.is_form_tag &&
is_formless_checkout == form.is_formless_checkout &&
- fields == form.fields;
+ fields == form.fields &&
+ username_predictions == form.username_predictions;
}
bool FormData::operator!=(const FormData& form) const {
diff --git a/chromium/components/autofill/core/common/form_data.h b/chromium/components/autofill/core/common/form_data.h
index d817c21318b..1b7fe203ec6 100644
--- a/chromium/components/autofill/core/common/form_data.h
+++ b/chromium/components/autofill/core/common/form_data.h
@@ -32,6 +32,9 @@ struct FormData {
// compare fields.
bool SimilarFormAs(const FormData& other) const;
+ // If |form| is the same as this from the POV of dynamic refills.
+ bool DynamicallySameFormAs(const FormData& form) const;
+
// Note: operator==() performs a full-field-comparison(byte by byte), this is
// different from SameFormAs(), which ignores comparison for those "values" of
// all form fields, just like what FormFieldData::SameFieldAs() ignores.
@@ -61,6 +64,12 @@ struct FormData {
uint32_t unique_renderer_id = kNotSetFormRendererId;
// A vector of all the input fields in the form.
std::vector<FormFieldData> fields;
+ // Contains unique renderer IDs of text elements which are predicted to be
+ // usernames. The order matters: elements are sorted in descending likelihood
+ // of being a username (the first one is the most likely username). Can
+ // contain IDs of elements which are not in |fields|. This is only used during
+ // parsing into PasswordForm, and hence not serialised for storage.
+ std::vector<uint32_t> username_predictions;
};
// For testing.
diff --git a/chromium/components/autofill/core/common/form_field_data.cc b/chromium/components/autofill/core/common/form_field_data.cc
index 785c5ab152b..69f2f3e416e 100644
--- a/chromium/components/autofill/core/common/form_field_data.cc
+++ b/chromium/components/autofill/core/common/form_field_data.cc
@@ -189,6 +189,12 @@ bool FormFieldData::SimilarFieldAs(const FormFieldData& field) const {
IsCheckable(check_status) == IsCheckable(field.check_status);
}
+bool FormFieldData::DynamicallySameFieldAs(const FormFieldData& field) const {
+ return name == field.name && id == field.id && HaveSameLabel(*this, field) &&
+ IsVisible() == field.IsVisible() &&
+ form_control_type == field.form_control_type;
+}
+
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 ce882a709f4..8f9e3cd942d 100644
--- a/chromium/components/autofill/core/common/form_field_data.h
+++ b/chromium/components/autofill/core/common/form_field_data.h
@@ -28,13 +28,19 @@ enum FieldPropertiesFlags {
// being autofilled. This is different from
// WebFormControlElement::IsAutofilled(). It is meant to be used for password
// fields, to determine whether viewing the value needs user reauthentication.
- AUTOFILLED = 1u << 1,
+ AUTOFILLED_ON_USER_TRIGGER = 1u << 1,
+ // The field received focus at any moment.
HAD_FOCUS = 1u << 2,
// Use this flag, if some error occurred in flags processing.
ERROR_OCCURRED = 1u << 3,
// On submission, the value of the field was recognised as a value which is
// already stored.
- KNOWN_VALUE = 1u << 4
+ KNOWN_VALUE = 1u << 4,
+ // A value was autofilled on pageload. This means that at least one character
+ // of the field value comes from being autofilled.
+ AUTOFILLED_ON_PAGELOAD = 1u << 5,
+ // A value was autofilled on any of the triggers.
+ AUTOFILLED = AUTOFILLED_ON_USER_TRIGGER | AUTOFILLED_ON_PAGELOAD,
};
// FieldPropertiesMask is used to contain combinations of FieldPropertiesFlags
@@ -89,12 +95,20 @@ struct FormFieldData {
// other information isn't changed.
bool SimilarFieldAs(const FormFieldData& field) const;
+ // If |field| is the same as this from the POV of dynamic refills.
+ bool DynamicallySameFieldAs(const FormFieldData& field) const;
+
// Returns true for all of textfield-looking types such as text, password,
// search, email, url, and number. It must work the same way as Blink function
// WebInputElement::IsTextField(), and it returns false if |*this| represents
// a textarea.
bool IsTextInputElement() const;
+ // Returns true if the field is visible to the user.
+ bool IsVisible() const {
+ return is_focusable && role != ROLE_ATTRIBUTE_PRESENTATION;
+ };
+
// Note: operator==() performs a full-field-comparison(byte by byte), this is
// different from SameFieldAs(), which ignores comparison for those "values"
// not regarded as part of identity of the field, such as is_autofilled and
diff --git a/chromium/components/autofill/core/common/password_form.cc b/chromium/components/autofill/core/common/password_form.cc
index a465d90cac8..9fc16b198a2 100644
--- a/chromium/components/autofill/core/common/password_form.cc
+++ b/chromium/components/autofill/core/common/password_form.cc
@@ -28,7 +28,10 @@ void PasswordFormToJSON(const PasswordForm& form,
target->SetString("origin", form.origin.possibly_invalid_spec());
target->SetString("action", form.action.possibly_invalid_spec());
target->SetString("submit_element", form.submit_element);
- target->SetString("username_elem", form.username_element);
+ target->SetBoolean("has_renderer_ids", form.has_renderer_ids);
+ target->SetString("username_element", form.username_element);
+ target->SetInteger("username_element_renderer_id",
+ form.username_element_renderer_id);
target->SetBoolean("username_marked_by_site", form.username_marked_by_site);
target->SetString("username_value", form.username_value);
target->SetString("password_elem", form.password_element);
@@ -36,6 +39,8 @@ void PasswordFormToJSON(const PasswordForm& form,
target->SetBoolean("password_value_is_default",
form.password_value_is_default);
target->SetString("new_password_element", form.new_password_element);
+ target->SetInteger("password_element_renderer_id",
+ form.password_element_renderer_id);
target->SetString("new_password_value", form.new_password_value);
target->SetBoolean("new_password_value_is_default",
form.new_password_value_is_default);
@@ -121,13 +126,16 @@ bool PasswordForm::operator==(const PasswordForm& form) const {
return scheme == form.scheme && signon_realm == form.signon_realm &&
origin == form.origin && action == form.action &&
submit_element == form.submit_element &&
+ has_renderer_ids == form.has_renderer_ids &&
username_element == form.username_element &&
+ username_element_renderer_id == form.username_element_renderer_id &&
username_marked_by_site == form.username_marked_by_site &&
username_value == form.username_value &&
other_possible_usernames == form.other_possible_usernames &&
all_possible_passwords == form.all_possible_passwords &&
form_has_autofilled_value == form.form_has_autofilled_value &&
password_element == form.password_element &&
+ password_element_renderer_id == form.password_element_renderer_id &&
password_value == form.password_value &&
new_password_element == form.new_password_element &&
new_password_marked_by_site == form.new_password_marked_by_site &&
diff --git a/chromium/components/autofill/core/common/password_form.h b/chromium/components/autofill/core/common/password_form.h
index 76f0be0e876..cc168863b0f 100644
--- a/chromium/components/autofill/core/common/password_form.h
+++ b/chromium/components/autofill/core/common/password_form.h
@@ -161,11 +161,27 @@ struct PasswordForm {
// When parsing an HTML form, this must always be set.
base::string16 submit_element;
+ // True if renderer ids for username and password fields are present. Only set
+ // on form parsing, and not persisted.
+ // TODO(https://crbug.com/831123): Remove this field when old parsing is
+ // removed and filling by renderer ids is by default.
+ bool has_renderer_ids = false;
+
// The name of the username input element. Optional (improves scoring).
//
// When parsing an HTML form, this must always be set.
base::string16 username_element;
+ // The renderer id of the username input element. It is set during the new
+ // form parsing and not persisted.
+ uint32_t username_element_renderer_id =
+ FormFieldData::kNotSetFormControlRendererId;
+
+ // True if the server-side classification believes that the field may be
+ // pre-filled with a placeholder in the value attribute. It is set during
+ // form parsing and not persisted.
+ bool username_may_use_prefilled_placeholder = false;
+
// Whether the |username_element| has an autocomplete=username attribute. This
// is only used in parsed HTML forms.
bool username_marked_by_site;
@@ -199,6 +215,11 @@ struct PasswordForm {
// In these two cases the |new_password_element| will always be set.
base::string16 password_element;
+ // The renderer id of the password input element. It is set during the new
+ // form parsing and not persisted.
+ uint32_t password_element_renderer_id =
+ FormFieldData::kNotSetFormControlRendererId;
+
// The current password. Must be non-empty for PasswordForm instances that are
// meant to be persisted to the password store.
//
diff --git a/chromium/components/autofill/core/common/password_form_fill_data.cc b/chromium/components/autofill/core/common/password_form_fill_data.cc
index be1f4016818..bcc0766b2b2 100644
--- a/chromium/components/autofill/core/common/password_form_fill_data.cc
+++ b/chromium/components/autofill/core/common/password_form_fill_data.cc
@@ -27,8 +27,7 @@ PasswordFormFillData::PasswordFormFillData()
PasswordFormFillData::PasswordFormFillData(const PasswordFormFillData& other) =
default;
-PasswordFormFillData::~PasswordFormFillData() {
-}
+PasswordFormFillData::~PasswordFormFillData() = default;
void InitPasswordFormFillData(
const PasswordForm& form_on_page,
@@ -42,12 +41,17 @@ void InitPasswordFormFillData(
FormFieldData username_field;
username_field.name = form_on_page.username_element;
username_field.value = preferred_match->username_value;
+ username_field.unique_renderer_id = form_on_page.username_element_renderer_id;
+ result->username_may_use_prefilled_placeholder =
+ form_on_page.username_may_use_prefilled_placeholder;
FormFieldData password_field;
password_field.name = form_on_page.password_element;
password_field.value = preferred_match->password_value;
+ password_field.unique_renderer_id = form_on_page.password_element_renderer_id;
password_field.form_control_type = "password";
// Fill basic form data.
+ result->form_renderer_id = form_on_page.form_data.unique_renderer_id;
result->name = form_on_page.form_data.name;
result->origin = form_on_page.origin;
result->action = form_on_page.action;
@@ -56,6 +60,7 @@ void InitPasswordFormFillData(
result->wait_for_username = wait_for_username_before_autofill;
result->is_possible_change_password_form =
form_on_page.IsPossibleChangePasswordForm();
+ result->has_renderer_ids = form_on_page.has_renderer_ids;
if (preferred_match->is_public_suffix_match ||
preferred_match->is_affiliation_based_match)
diff --git a/chromium/components/autofill/core/common/password_form_fill_data.h b/chromium/components/autofill/core/common/password_form_fill_data.h
index e9c58d7b169..cc90089ee7e 100644
--- a/chromium/components/autofill/core/common/password_form_fill_data.h
+++ b/chromium/components/autofill/core/common/password_form_fill_data.h
@@ -38,6 +38,17 @@ struct PasswordFormFillData {
typedef std::map<UsernamesCollectionKey,
std::vector<base::string16> > UsernamesCollection;
+ // If |has_renderer_ids| == true then |form_renderer_id| contains the unique
+ // renderer form id. No special values for |has_renderer_ids| == false case
+ // was introduced because the absent of ids is just temprorary situation while
+ // the old form parsing still exists.
+ // If there is no form tag then |form_renderer_id| ==
+ // FormData::kNotSetFormRendererId.
+ // Username and Password elements renderer ids are in
+ // |username_field.unique_renderer_id| and |password_field.unique_renderer_id|
+ // correspondingly.
+ uint32_t form_renderer_id;
+
// The name of the form.
base::string16 name;
@@ -53,6 +64,10 @@ struct PasswordFormFillData {
FormFieldData username_field;
FormFieldData password_field;
+ // True if the server-side classification believes that the field may be
+ // pre-filled with a placeholder in the value attribute.
+ bool username_may_use_prefilled_placeholder = false;
+
// The signon realm of the preferred user/pass pair.
std::string preferred_realm;
@@ -69,6 +84,11 @@ struct PasswordFormFillData {
// True if this form is a change password form.
bool is_possible_change_password_form;
+ // True if renderer ids for form, username and password fields are present.
+ // TODO(https://crbug.com/831123): Remove this field when old parsing is
+ // removed and filling by renderer ids is by default.
+ bool has_renderer_ids = false;
+
PasswordFormFillData();
PasswordFormFillData(const PasswordFormFillData& other);
~PasswordFormFillData();
diff --git a/chromium/components/autofill/core/common/password_form_fill_data_unittest.cc b/chromium/components/autofill/core/common/password_form_fill_data_unittest.cc
index 883572c25cf..ffe37b17482 100644
--- a/chromium/components/autofill/core/common/password_form_fill_data_unittest.cc
+++ b/chromium/components/autofill/core/common/password_form_fill_data_unittest.cc
@@ -241,4 +241,44 @@ TEST(PasswordFormFillDataTest, TestAffiliationMatch) {
EXPECT_EQ(iter->second.realm, affiliated_match.signon_realm);
}
+// Tests that renderer ids are passed correctly.
+TEST(PasswordFormFillDataTest, RendererIDs) {
+ // Create the current form on the page.
+ PasswordForm form_on_page;
+ form_on_page.origin = GURL("https://foo.com/");
+ form_on_page.action = GURL("https://foo.com/login");
+ form_on_page.username_element = ASCIIToUTF16("username");
+ form_on_page.password_element = ASCIIToUTF16("password");
+ form_on_page.username_may_use_prefilled_placeholder = true;
+
+ // Create an exact match in the database.
+ PasswordForm preferred_match = form_on_page;
+ preferred_match.username_value = ASCIIToUTF16("test@gmail.com");
+ preferred_match.password_value = ASCIIToUTF16("test");
+ preferred_match.preferred = true;
+
+ // Set renderer id related fields.
+ FormData form_data;
+ form_data.unique_renderer_id = 42;
+ form_data.is_form_tag = true;
+ form_on_page.form_data = form_data;
+ form_on_page.has_renderer_ids = true;
+ form_on_page.username_element_renderer_id = 123;
+ form_on_page.password_element_renderer_id = 456;
+
+ std::map<base::string16, const PasswordForm*> matches;
+
+ PasswordFormFillData result;
+ InitPasswordFormFillData(form_on_page, matches, &preferred_match, true,
+ &result);
+
+ EXPECT_EQ(form_data.unique_renderer_id, result.form_renderer_id);
+ EXPECT_EQ(form_on_page.has_renderer_ids, result.has_renderer_ids);
+ EXPECT_EQ(form_on_page.username_element_renderer_id,
+ result.username_field.unique_renderer_id);
+ EXPECT_EQ(form_on_page.password_element_renderer_id,
+ result.password_field.unique_renderer_id);
+ EXPECT_TRUE(result.username_may_use_prefilled_placeholder);
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/common/password_generation_util.cc b/chromium/components/autofill/core/common/password_generation_util.cc
index 530f75f6e5c..c040a3ea40a 100644
--- a/chromium/components/autofill/core/common/password_generation_util.cc
+++ b/chromium/components/autofill/core/common/password_generation_util.cc
@@ -7,7 +7,8 @@
#include "base/command_line.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_macros.h"
-#include "components/autofill/core/common/autofill_switches.h"
+#include "components/autofill/core/common/autofill_features.h"
+#include "ui/base/ui_base_features.h"
namespace autofill {
namespace password_generation {
@@ -22,6 +23,22 @@ PasswordGenerationActions::PasswordGenerationActions()
PasswordGenerationActions::~PasswordGenerationActions() {
}
+PasswordGenerationUIData::PasswordGenerationUIData(
+ const gfx::RectF& bounds,
+ int max_length,
+ const base::string16& generation_element,
+ base::i18n::TextDirection text_direction,
+ const autofill::PasswordForm& password_form)
+ : bounds(bounds),
+ max_length(max_length),
+ generation_element(generation_element),
+ text_direction(text_direction),
+ password_form(password_form) {}
+
+PasswordGenerationUIData::PasswordGenerationUIData() = default;
+
+PasswordGenerationUIData::~PasswordGenerationUIData() = default;
+
void LogUserActions(PasswordGenerationActions actions) {
UserAction action = IGNORE_FEATURE;
if (actions.password_accepted) {
@@ -42,20 +59,14 @@ void LogPasswordGenerationEvent(PasswordGenerationEvent event) {
}
bool IsPasswordGenerationEnabled() {
- // Always fetch the field trial group to ensure it is reported correctly.
- // The command line flags will be associated with a group that is reported
- // so long as trial is actually queried.
- std::string group_name =
- base::FieldTrialList::FindFullName("PasswordGeneration");
-
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(switches::kDisablePasswordGeneration))
- return false;
+ if (base::FeatureList::IsEnabled(
+ autofill::features::kAutomaticPasswordGeneration))
+ return true;
- if (command_line->HasSwitch(switches::kEnablePasswordGeneration))
+ if (base::FeatureList::IsEnabled(::features::kExperimentalUi))
return true;
- return group_name != "Disabled";
+ return false;
}
} // namespace password_generation
diff --git a/chromium/components/autofill/core/common/password_generation_util.h b/chromium/components/autofill/core/common/password_generation_util.h
index 0d1547a779d..5543655fef3 100644
--- a/chromium/components/autofill/core/common/password_generation_util.h
+++ b/chromium/components/autofill/core/common/password_generation_util.h
@@ -5,7 +5,11 @@
#ifndef COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_GENERATION_UTIL_H_
#define COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_GENERATION_UTIL_H_
+#include "components/autofill/core/common/password_form.h"
+#include "ui/gfx/geometry/rect_f.h"
+
namespace autofill {
+
namespace password_generation {
// Enumerates various events related to the password generation process.
@@ -58,8 +62,7 @@ enum PasswordGenerationEvent {
// User focused the password field containing the generated password.
EDITING_POPUP_SHOWN,
- // Generation enabled because autocomplete attributes for username and
- // new-password are set.
+ // Generation enabled because autocomplete attributes for new-password is set.
AUTOCOMPLETE_ATTRIBUTES_ENABLED_GENERATION,
// Generation is triggered by the user from the context menu.
@@ -90,6 +93,33 @@ struct PasswordGenerationActions {
~PasswordGenerationActions();
};
+struct PasswordGenerationUIData {
+ PasswordGenerationUIData(const gfx::RectF& bounds,
+ int max_length,
+ const base::string16& generation_element,
+ base::i18n::TextDirection text_direction,
+ const autofill::PasswordForm& password_form);
+ PasswordGenerationUIData();
+ ~PasswordGenerationUIData();
+
+ // Location at which to display a popup if needed. This location is specified
+ // in the renderer's coordinate system. The popup will be anchored at
+ // |bounds|.
+ gfx::RectF bounds;
+
+ // Maximum length of the generated password.
+ int max_length;
+
+ // Name of the password field to which the generation popup is attached.
+ base::string16 generation_element;
+
+ // Direction of the text for |generation_element|.
+ base::i18n::TextDirection text_direction;
+
+ // The form associated with the password field.
+ autofill::PasswordForm password_form;
+};
+
void LogUserActions(PasswordGenerationActions actions);
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 17f6da45b27..32094bafc4a 100644
--- a/chromium/components/autofill/core/common/save_password_progress_logger.cc
+++ b/chromium/components/autofill/core/common/save_password_progress_logger.cc
@@ -10,14 +10,16 @@
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "components/autofill/core/common/password_form.h"
using base::checked_cast;
-using base::Value;
using base::DictionaryValue;
+using base::UintToString;
+using base::Value;
namespace autofill {
@@ -68,10 +70,22 @@ void SavePasswordProgressLogger::LogPasswordForm(
log.SetString(GetStringFromID(STRING_ACTION), ScrubURL(form.action));
log.SetString(GetStringFromID(STRING_USERNAME_ELEMENT),
ScrubElementID(form.username_element));
+ if (form.has_renderer_ids) {
+ log.SetString(GetStringFromID(STRING_USERNAME_ELEMENT_RENDERER_ID),
+ UintToString(form.username_element_renderer_id));
+ }
log.SetString(GetStringFromID(STRING_PASSWORD_ELEMENT),
ScrubElementID(form.password_element));
+ if (form.has_renderer_ids) {
+ log.SetString(GetStringFromID(STRING_PASSWORD_ELEMENT_RENDERER_ID),
+ UintToString(form.password_element_renderer_id));
+ }
log.SetString(GetStringFromID(STRING_NEW_PASSWORD_ELEMENT),
ScrubElementID(form.new_password_element));
+ if (!form.confirmation_password_element.empty()) {
+ log.SetString(GetStringFromID(STRING_CONFIRMATION_PASSWORD_ELEMENT),
+ ScrubElementID(form.confirmation_password_element));
+ }
log.SetBoolean(GetStringFromID(STRING_PASSWORD_GENERATED),
form.type == PasswordForm::TYPE_GENERATED);
log.SetInteger(GetStringFromID(STRING_TIMES_USED), form.times_used);
@@ -186,10 +200,16 @@ std::string SavePasswordProgressLogger::GetStringFromID(
return "Action";
case SavePasswordProgressLogger::STRING_USERNAME_ELEMENT:
return "Username element";
+ case SavePasswordProgressLogger::STRING_USERNAME_ELEMENT_RENDERER_ID:
+ return "Username element renderer id";
case SavePasswordProgressLogger::STRING_PASSWORD_ELEMENT:
return "Password element";
+ case SavePasswordProgressLogger::STRING_PASSWORD_ELEMENT_RENDERER_ID:
+ return "Password element renderer id";
case SavePasswordProgressLogger::STRING_NEW_PASSWORD_ELEMENT:
return "New password element";
+ case SavePasswordProgressLogger::STRING_CONFIRMATION_PASSWORD_ELEMENT:
+ return "Confirmation password element";
case SavePasswordProgressLogger::STRING_PASSWORD_GENERATED:
return "Password generated";
case SavePasswordProgressLogger::STRING_TIMES_USED:
@@ -371,9 +391,9 @@ std::string SavePasswordProgressLogger::GetStringFromID(
case SavePasswordProgressLogger::STRING_MATCH_IN_ADDITIONAL:
return "Match found in additional logins";
case SavePasswordProgressLogger::STRING_USERNAME_FILLED:
- return "Filled username element named";
+ return "Filled username element";
case SavePasswordProgressLogger::STRING_PASSWORD_FILLED:
- return "Filled password element named";
+ return "Filled password element";
case SavePasswordProgressLogger::STRING_FORM_NAME:
return "Form name";
case SavePasswordProgressLogger::STRING_FIELDS:
@@ -410,8 +430,10 @@ std::string SavePasswordProgressLogger::GetStringFromID(
return "Generation: eligible form found";
case STRING_GENERATION_RENDERER_NO_FIELD_FOUND:
return "Generation: fields for generation are not found";
- case STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP:
- return "Show generation popup";
+ case STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE:
+ return "Generation: automatic generation is available";
+ case STRING_GENERATION_RENDERER_SHOW_MANUAL_GENERATION_POPUP:
+ return "Show generation popup triggered manually";
case STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED:
return "Generated password accepted";
case STRING_SUCCESSFUL_SUBMISSION_INDICATOR_EVENT:
diff --git a/chromium/components/autofill/core/common/save_password_progress_logger.h b/chromium/components/autofill/core/common/save_password_progress_logger.h
index cb0523f4ac3..02f204266d9 100644
--- a/chromium/components/autofill/core/common/save_password_progress_logger.h
+++ b/chromium/components/autofill/core/common/save_password_progress_logger.h
@@ -51,8 +51,11 @@ class SavePasswordProgressLogger {
STRING_ORIGIN,
STRING_ACTION,
STRING_USERNAME_ELEMENT,
+ STRING_USERNAME_ELEMENT_RENDERER_ID,
STRING_PASSWORD_ELEMENT,
+ STRING_PASSWORD_ELEMENT_RENDERER_ID,
STRING_NEW_PASSWORD_ELEMENT,
+ STRING_CONFIRMATION_PASSWORD_ELEMENT,
STRING_PASSWORD_GENERATED,
STRING_TIMES_USED,
STRING_PSL_MATCH,
@@ -160,7 +163,8 @@ class SavePasswordProgressLogger {
STRING_GENERATION_RENDERER_NO_SERVER_SIGNAL,
STRING_GENERATION_RENDERER_ELIGIBLE_FORM_FOUND,
STRING_GENERATION_RENDERER_NO_FIELD_FOUND,
- STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP,
+ STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE,
+ STRING_GENERATION_RENDERER_SHOW_MANUAL_GENERATION_POPUP,
STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED,
STRING_SUCCESSFUL_SUBMISSION_INDICATOR_EVENT,
STRING_MAIN_FRAME_ORIGIN,
diff --git a/chromium/components/autofill/ios/browser/BUILD.gn b/chromium/components/autofill/ios/browser/BUILD.gn
index 0371187bd64..ab38995664f 100644
--- a/chromium/components/autofill/ios/browser/BUILD.gn
+++ b/chromium/components/autofill/ios/browser/BUILD.gn
@@ -35,10 +35,12 @@ source_set("browser") {
"//base",
"//components/autofill/core/browser",
"//components/autofill/core/common",
+ "//components/autofill/ios/form_util",
"//components/prefs:prefs",
"//components/prefs/ios",
"//google_apis",
"//ios/web",
+ "//services/network/public/cpp",
"//ui/gfx/geometry",
]
}
@@ -77,6 +79,7 @@ source_set("unit_tests") {
deps = [
":browser",
"//base",
+ "//base/test:test_support",
"//components/autofill/core/browser:test_support",
"//components/prefs",
"//ios/web/public",
diff --git a/chromium/components/autofill/ios/browser/DEPS b/chromium/components/autofill/ios/browser/DEPS
index 4dd63070bb3..c6de2be9f01 100644
--- a/chromium/components/autofill/ios/browser/DEPS
+++ b/chromium/components/autofill/ios/browser/DEPS
@@ -1,4 +1,5 @@
include_rules = [
"+ios/web/public",
+ "+services/network/public/cpp",
"+third_party/ocmock",
]
diff --git a/chromium/components/autofill/ios/browser/autofill_agent.h b/chromium/components/autofill/ios/browser/autofill_agent.h
index 10cc33e5a4b..dba1b58dec5 100644
--- a/chromium/components/autofill/ios/browser/autofill_agent.h
+++ b/chromium/components/autofill/ios/browser/autofill_agent.h
@@ -39,7 +39,7 @@ class WebState;
- (instancetype)init NS_UNAVAILABLE;
// Callback by AutofillController when suggestions are ready.
-- (void)onSuggestionsReady:(NSArray*)suggestions
+- (void)onSuggestionsReady:(NSArray<FormSuggestion*>*)suggestions
popupDelegate:
(const base::WeakPtr<autofill::AutofillPopupDelegate>&)
delegate;
diff --git a/chromium/components/autofill/ios/browser/autofill_agent.mm b/chromium/components/autofill/ios/browser/autofill_agent.mm
index 43c90590c63..1073e8642c5 100644
--- a/chromium/components/autofill/ios/browser/autofill_agent.mm
+++ b/chromium/components/autofill/ios/browser/autofill_agent.mm
@@ -35,6 +35,7 @@
#include "components/autofill/ios/browser/autofill_util.h"
#import "components/autofill/ios/browser/form_suggestion.h"
#import "components/autofill/ios/browser/js_autofill_manager.h"
+#import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
#import "components/prefs/ios/pref_observer_bridge.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
@@ -89,7 +90,9 @@ void GetFormAndField(autofill::FormData* form,
} // namespace
-@interface AutofillAgent ()<CRWWebStateObserver, PrefObserverDelegate>
+@interface AutofillAgent ()<CRWWebStateObserver,
+ FormActivityObserver,
+ PrefObserverDelegate>
// Notifies the autofill manager when forms are detected on a page.
- (void)notifyAutofillManager:(autofill::AutofillManager*)autofillManager
@@ -134,7 +137,7 @@ void GetFormAndField(autofill::FormData* form,
// Rearranges and filters the suggestions to move profile or credit card
// suggestions to the front if the user has selected one recently and remove
// key/value suggestions if the user hasn't started typing.
-- (NSArray*)processSuggestions:(NSArray*)suggestions;
+- (NSArray*)processSuggestions:(NSArray<FormSuggestion*>*)suggestions;
// Sends the the |formData| to the JavaScript manager of |webState_| to actually
// fill the data.
@@ -163,10 +166,6 @@ void GetFormAndField(autofill::FormData* form,
// focused form element in order to force filling of the currently selected
// form element, even if it's non-empty.
base::string16 pendingAutocompleteField_;
- // The identifier of the most recent suggestion accepted by the user. Only
- // used to reorder future suggestion lists, placing matching suggestions first
- // in the list.
- NSInteger mostRecentSelectedIdentifier_;
// Suggestions state:
// The most recent form suggestions.
@@ -191,6 +190,10 @@ void GetFormAndField(autofill::FormData* form,
std::unique_ptr<PrefObserverBridge> prefObserverBridge_;
// Registrar for pref changes notifications.
PrefChangeRegistrar prefChangeRegistrar_;
+
+ // Bridge to observe form activity in |webState_|.
+ std::unique_ptr<autofill::FormActivityObserverBridge>
+ formActivityObserverBridge_;
}
- (instancetype)initWithPrefService:(PrefService*)prefService
@@ -204,6 +207,8 @@ void GetFormAndField(autofill::FormData* form,
webStateObserverBridge_ =
std::make_unique<web::WebStateObserverBridge>(self);
webState_->AddObserver(webStateObserverBridge_.get());
+ formActivityObserverBridge_ =
+ std::make_unique<autofill::FormActivityObserverBridge>(webState_, self);
prefObserverBridge_ = std::make_unique<PrefObserverBridge>(self);
prefChangeRegistrar_.Init(prefService);
prefObserverBridge_->ObserveChangesForPreference(
@@ -222,6 +227,7 @@ void GetFormAndField(autofill::FormData* form,
- (void)dealloc {
if (webState_) {
+ formActivityObserverBridge_.reset();
webState_->RemoveObserver(webStateObserverBridge_.get());
webStateObserverBridge_.reset();
webState_ = nullptr;
@@ -232,6 +238,7 @@ void GetFormAndField(autofill::FormData* form,
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (webState_) {
+ formActivityObserverBridge_.reset();
webState_->RemoveObserver(webStateObserverBridge_.get());
webStateObserverBridge_.reset();
webState_ = nullptr;
@@ -297,27 +304,11 @@ void GetFormAndField(autofill::FormData* form,
}];
}
-- (NSArray*)processSuggestions:(NSArray*)suggestions {
+- (NSArray*)processSuggestions:(NSArray<FormSuggestion*>*)suggestions {
// The suggestion array is cloned (to claim ownership) and to slightly
// reorder; a future improvement is to base order on text typed in other
// fields by users as well as accepted suggestions (crbug.com/245261).
- NSMutableArray* suggestionsCopy = [suggestions mutableCopy];
-
- // If the most recently selected suggestion was a profile or credit card
- // suggestion, move it to the front of the suggestions.
- if (mostRecentSelectedIdentifier_ > 0) {
- NSUInteger idx = [suggestionsCopy
- indexOfObjectPassingTest:^BOOL(id obj, NSUInteger, BOOL*) {
- FormSuggestion* suggestion = obj;
- return suggestion.identifier == mostRecentSelectedIdentifier_;
- }];
-
- if (idx != NSNotFound) {
- FormSuggestion* suggestion = suggestionsCopy[idx];
- [suggestionsCopy removeObjectAtIndex:idx];
- [suggestionsCopy insertObject:suggestion atIndex:0];
- }
- }
+ NSMutableArray<FormSuggestion*>* suggestionsCopy = [suggestions mutableCopy];
// Filter out any key/value suggestions if the user hasn't typed yet.
if ([typedValue_ length] == 0) {
@@ -330,12 +321,17 @@ void GetFormAndField(autofill::FormData* form,
}
// If "clear form" entry exists then move it to the front of the suggestions.
+ // If "GPay branding" icon is present, it remains as the first suggestion.
for (NSInteger idx = [suggestionsCopy count] - 1; idx > 0; idx--) {
FormSuggestion* suggestion = suggestionsCopy[idx];
if (suggestion.identifier == autofill::POPUP_ITEM_ID_CLEAR_FORM) {
- FormSuggestion* suggestionToMove = suggestionsCopy[idx];
+ BOOL hasGPayBranding = suggestionsCopy[0].identifier ==
+ autofill::POPUP_ITEM_ID_GOOGLE_PAY_BRANDING;
+
+ FormSuggestion* clearFormSuggestion = suggestionsCopy[idx];
[suggestionsCopy removeObjectAtIndex:idx];
- [suggestionsCopy insertObject:suggestionToMove atIndex:0];
+ [suggestionsCopy insertObject:clearFormSuggestion
+ atIndex:hasGPayBranding ? 1 : 0];
break;
}
}
@@ -343,7 +339,7 @@ void GetFormAndField(autofill::FormData* form,
return suggestionsCopy;
}
-- (void)onSuggestionsReady:(NSArray*)suggestions
+- (void)onSuggestionsReady:(NSArray<FormSuggestion*>*)suggestions
popupDelegate:
(const base::WeakPtr<autofill::AutofillPopupDelegate>&)
delegate {
@@ -384,7 +380,9 @@ void GetFormAndField(autofill::FormData* form,
// Query the AutofillManager for suggestions. Results will arrive in
// [AutofillController showAutofillPopup].
- autofillManager->OnQueryFormFieldAutofill(queryId, form, field, gfx::RectF());
+ autofillManager->OnQueryFormFieldAutofill(
+ queryId, form, field, gfx::RectF(),
+ /*autoselect_first_suggestion=*/false);
}
- (void)checkIfSuggestionsAvailableForForm:(NSString*)formName
@@ -394,6 +392,7 @@ void GetFormAndField(autofill::FormData* form,
type:(NSString*)type
typedValue:(NSString*)typedValue
isMainFrame:(BOOL)isMainFrame
+ hasUserGesture:(BOOL)hasUserGesture
webState:(web::WebState*)webState
completionHandler:
(SuggestionsAvailableCompletion)completion {
@@ -410,6 +409,12 @@ void GetFormAndField(autofill::FormData* form,
return;
}
+ // Check for suggestions if the form activity is initiated by the user.
+ if (!hasUserGesture) {
+ completion(NO);
+ return;
+ }
+
// Once the active form and field are extracted, send a query to the
// AutofillManager for suggestions.
__weak AutofillAgent* weakSelf = self;
@@ -462,7 +467,6 @@ void GetFormAndField(autofill::FormData* form,
completionHandler:(SuggestionHandledCompletion)completion {
[[UIDevice currentDevice] playInputClick];
suggestionHandledCompletion_ = [completion copy];
- mostRecentSelectedIdentifier_ = suggestion.identifier;
if (suggestion.identifier > 0) {
pendingAutocompleteField_ = base::SysNSStringToUTF16(fieldIdentifier);
@@ -478,8 +482,9 @@ void GetFormAndField(autofill::FormData* form,
value:base::SysNSStringToUTF16(suggestion.value)];
} else if (suggestion.identifier == autofill::POPUP_ITEM_ID_CLEAR_FORM) {
[jsAutofillManager_
- clearAutofilledFieldsForFormNamed:formName
- completionHandler:suggestionHandledCompletion_];
+ clearAutofilledFieldsForFormName:formName
+ fieldIdentifier:fieldIdentifier
+ completionHandler:suggestionHandledCompletion_];
suggestionHandledCompletion_ = nil;
} else {
NOTREACHED() << "unknown identifier " << suggestion.identifier;
@@ -503,51 +508,6 @@ void GetFormAndField(autofill::FormData* form,
}
- (void)webState:(web::WebState*)webState
- didSubmitDocumentWithFormNamed:(const std::string&)formName
- userInitiated:(BOOL)userInitiated
- isMainFrame:(BOOL)isMainFrame {
- if (!isMainFrame) {
- // Saving from iframes is not implemented.
- return;
- }
-
- if (![self isAutofillEnabled])
- return;
-
- __weak AutofillAgent* weakSelf = self;
- id completionHandler = ^(BOOL success, const FormDataVector& forms) {
- AutofillAgent* strongSelf = weakSelf;
- if (!strongSelf || !success)
- return;
- autofill::AutofillManager* autofillManager =
- [strongSelf autofillManagerFromWebState:webState];
- if (!autofillManager || forms.empty())
- return;
- if (forms.size() > 1) {
- DLOG(WARNING) << "Only one form should be extracted.";
- return;
- }
- [strongSelf notifyAutofillManager:autofillManager
- ofFormsSubmitted:forms
- userInitiated:userInitiated];
-
- };
-
- web::URLVerificationTrustLevel trustLevel;
- const GURL pageURL(webState->GetCurrentURL(&trustLevel));
-
- // This code is racing against the new page loading and will not get the
- // password form data if the page has changed. In most cases this code wins
- // the race.
- // TODO(crbug.com/418827): Fix this by passing in more data from the JS side.
- [self fetchFormsFiltered:YES
- withName:base::UTF8ToUTF16(formName)
- minimumRequiredFieldsCount:1
- pageURL:pageURL
- completionHandler:completionHandler];
-}
-
-- (void)webState:(web::WebState*)webState
didStartNavigation:(web::NavigationContext*)navigation {
// Ignore navigations within the same document, e.g., history.pushState().
if (navigation->IsSameDocument())
@@ -586,6 +546,10 @@ void GetFormAndField(autofill::FormData* form,
web::URLVerificationTrustLevel trustLevel;
const GURL pageURL(webState->GetCurrentURL(&trustLevel));
[jsAutofillManager_ toggleTrackingFormMutations:YES];
+
+ [jsAutofillManager_ toggleTrackingUserEditedFields:
+ base::FeatureList::IsEnabled(
+ autofill::features::kAutofillPrefilledFields)];
[self scanFormsInPage:webState pageURL:pageURL];
}
@@ -613,8 +577,11 @@ void GetFormAndField(autofill::FormData* form,
completionHandler:completionHandler];
}
+#pragma mark -
+#pragma mark FormActivityObserver
+
- (void)webState:(web::WebState*)webState
- didRegisterFormActivity:(const web::FormActivityParams&)params {
+ registeredFormActivity:(const web::FormActivityParams&)params {
if (![self isAutofillEnabled])
return;
@@ -674,6 +641,51 @@ void GetFormAndField(autofill::FormData* form,
completionHandler:completionHandler];
}
+- (void)webState:(web::WebState*)webState
+ submittedDocumentWithFormNamed:(const std::string&)formName
+ hasUserGesture:(BOOL)hasUserGesture
+ formInMainFrame:(BOOL)formInMainFrame {
+ if (!formInMainFrame) {
+ // Saving from iframes is not implemented.
+ return;
+ }
+
+ if (![self isAutofillEnabled])
+ return;
+
+ __weak AutofillAgent* weakSelf = self;
+ id completionHandler = ^(BOOL success, const FormDataVector& forms) {
+ AutofillAgent* strongSelf = weakSelf;
+ if (!strongSelf || !success)
+ return;
+ autofill::AutofillManager* autofillManager =
+ [strongSelf autofillManagerFromWebState:webState];
+ if (!autofillManager || forms.empty())
+ return;
+ if (forms.size() > 1) {
+ DLOG(WARNING) << "Only one form should be extracted.";
+ return;
+ }
+ [strongSelf notifyAutofillManager:autofillManager
+ ofFormsSubmitted:forms
+ userInitiated:hasUserGesture];
+
+ };
+
+ web::URLVerificationTrustLevel trustLevel;
+ const GURL pageURL(webState->GetCurrentURL(&trustLevel));
+
+ // This code is racing against the new page loading and will not get the
+ // password form data if the page has changed. In most cases this code wins
+ // the race.
+ // TODO(crbug.com/418827): Fix this by passing in more data from the JS side.
+ [self fetchFormsFiltered:YES
+ withName:base::UTF8ToUTF16(formName)
+ minimumRequiredFieldsCount:1
+ pageURL:pageURL
+ completionHandler:completionHandler];
+}
+
#pragma mark - PrefObserverDelegate
- (void)onPreferenceChanged:(const std::string&)preferenceName {
@@ -735,7 +747,10 @@ void GetFormAndField(autofill::FormData* form,
if (field.value.empty() || !field.is_autofilled)
continue;
- fieldsData.SetKey(base::UTF16ToUTF8(field.id), base::Value(field.value));
+ base::Value fieldData(base::Value::Type::DICTIONARY);
+ fieldData.SetKey("value", base::Value(field.value));
+ fieldData.SetKey("section", base::Value(field.section));
+ fieldsData.SetKey(base::UTF16ToUTF8(field.id), std::move(fieldData));
}
autofillData.SetKey("fields", std::move(fieldsData));
diff --git a/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm b/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm
index 05b985dc457..5f8c2b44a20 100644
--- a/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm
+++ b/chromium/components/autofill/ios/browser/autofill_agent_unittests.mm
@@ -5,7 +5,10 @@
#import "components/autofill/ios/browser/autofill_agent.h"
#include "base/strings/utf_string_conversions.h"
+#import "base/test/ios/wait_util.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/popup_item_ids.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
#include "components/autofill/core/common/form_data.h"
#import "components/autofill/ios/browser/js_autofill_manager.h"
#include "components/prefs/pref_service.h"
@@ -17,11 +20,16 @@
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#include "third_party/ocmock/gtest_support.h"
+#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
+using autofill::POPUP_ITEM_ID_CLEAR_FORM;
+using autofill::POPUP_ITEM_ID_GOOGLE_PAY_BRANDING;
+using base::test::ios::WaitUntilCondition;
+
// Test fixture for AutofillAgent testing.
class AutofillAgentTests : public PlatformTest {
public:
@@ -34,8 +42,11 @@ class AutofillAgentTests : public PlatformTest {
mock_js_injection_receiver_ =
[OCMockObject mockForClass:[CRWJSInjectionReceiver class]];
test_web_state_.SetJSInjectionReceiver(mock_js_injection_receiver_);
+ test_web_state_.SetContentIsHTML(true);
+ test_web_state_.SetCurrentURL(GURL("https://example.com"));
prefs_ = autofill::test::PrefServiceForTesting();
+ prefs_->SetBoolean(autofill::prefs::kAutofillEnabled, true);
autofill_agent_ =
[[AutofillAgent alloc] initWithPrefService:prefs_.get()
webState:&test_web_state_];
@@ -93,8 +104,10 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTest) {
// Fields are in alphabetical order.
[[mock_js_injection_receiver_ expect]
executeJavaScript:
- @"__gCrWeb.autofill.fillForm({\"fields\":{\"name\":\"name_value\","
- @"\"number\":\"number_value\"},\"formName\":\"CC form\"}, \"\");"
+ @"__gCrWeb.autofill.fillForm({\"fields\":{\"name\":{\"section\":\"\","
+ @"\"value\":\"name_value\"},"
+ @"\"number\":{\"section\":\"\",\"value\":\"number_value\"}},"
+ @"\"formName\":\"CC form\"}, \"\");"
completionHandler:[OCMArg any]];
[autofill_agent_ onFormDataFilled:form];
test_web_state_.WasShown();
@@ -132,11 +145,181 @@ TEST_F(AutofillAgentTests, OnFormDataFilledWithNameCollisionTest) {
// Fields are in alphabetical order.
[[mock_js_injection_receiver_ expect]
executeJavaScript:
- @"__gCrWeb.autofill.fillForm({\"fields\":{\"field1\":\"value "
- @"2\",\"region\":\"California\"},\"formName\":\"\"}, \"\");"
+ @"__gCrWeb.autofill.fillForm({\"fields\":{\"field1\":{\"section\":"
+ @"\"\",\"value\":\"value "
+ @"2\"},\"region\":{\"section\":\"\",\"value\":\"California\"}},"
+ @"\"formName\":\"\"}, \"\");"
completionHandler:[OCMArg any]];
[autofill_agent_ onFormDataFilled:form];
test_web_state_.WasShown();
EXPECT_OCMOCK_VERIFY(mock_js_injection_receiver_);
}
+
+// Tests that when a user initiated form activity is registered the script to
+// extract forms is executed.
+TEST_F(AutofillAgentTests, CheckIfSuggestionsAvailable_UserInitiatedActivity) {
+ [[mock_js_injection_receiver_ expect]
+ executeJavaScript:@"__gCrWeb.autofill.extractForms(1, true);"
+ completionHandler:[OCMArg any]];
+ [autofill_agent_ checkIfSuggestionsAvailableForForm:@"form"
+ fieldName:@"address"
+ fieldIdentifier:@"address"
+ fieldType:@"text"
+ type:@"focus"
+ typedValue:@""
+ isMainFrame:YES
+ hasUserGesture:YES
+ webState:&test_web_state_
+ completionHandler:nil];
+ test_web_state_.WasShown();
+
+ EXPECT_OCMOCK_VERIFY(mock_js_injection_receiver_);
+}
+
+// Tests that when a non user initiated form activity is registered the
+// completion callback passed to the call to check if suggestions are available
+// is invoked with no suggestions.
+TEST_F(AutofillAgentTests,
+ CheckIfSuggestionsAvailable_NonUserInitiatedActivity) {
+ __block BOOL completion_handler_success = NO;
+ __block BOOL completion_handler_called = NO;
+
+ [autofill_agent_ checkIfSuggestionsAvailableForForm:@"form"
+ fieldName:@"address"
+ fieldIdentifier:@"address"
+ fieldType:@"text"
+ type:@"focus"
+ typedValue:@""
+ isMainFrame:YES
+ hasUserGesture:NO
+ webState:&test_web_state_
+ completionHandler:^(BOOL success) {
+ completion_handler_success = success;
+ completion_handler_called = YES;
+ }];
+ test_web_state_.WasShown();
+
+ // Wait until the expected handler is called.
+ WaitUntilCondition(^bool() {
+ return completion_handler_called;
+ });
+ EXPECT_FALSE(completion_handler_success);
+}
+
+// Tests that when Autofill suggestions are made available to AutofillManager
+// "Clear Form" is moved to the start of the list and the order of other
+// suggestions remains unchanged.
+TEST_F(AutofillAgentTests, onSuggestionsReady_ClearForm) {
+ __block NSArray<FormSuggestion*>* completion_handler_suggestions = nil;
+ __block BOOL completion_handler_called = NO;
+
+ // Make the suggestions available to AutofillManager.
+ NSArray* suggestions = @[
+ [FormSuggestion suggestionWithValue:@""
+ displayDescription:nil
+ icon:@""
+ identifier:123],
+ [FormSuggestion suggestionWithValue:@""
+ displayDescription:nil
+ icon:@""
+ identifier:321],
+ [FormSuggestion suggestionWithValue:@""
+ displayDescription:nil
+ icon:@""
+ identifier:autofill::POPUP_ITEM_ID_CLEAR_FORM]
+ ];
+ [autofill_agent_
+ onSuggestionsReady:suggestions
+ popupDelegate:base::WeakPtr<autofill::AutofillPopupDelegate>()];
+
+ // Retrieves the suggestions.
+ auto completionHandler = ^(NSArray<FormSuggestion*>* suggestions,
+ id<FormSuggestionProvider> delegate) {
+ completion_handler_suggestions = [suggestions copy];
+ completion_handler_called = YES;
+ };
+ [autofill_agent_ retrieveSuggestionsForForm:@"form"
+ fieldName:@"address"
+ fieldIdentifier:@"address"
+ fieldType:@"text"
+ type:@"focus"
+ typedValue:@""
+ webState:&test_web_state_
+ completionHandler:completionHandler];
+ test_web_state_.WasShown();
+
+ // Wait until the expected handler is called.
+ WaitUntilCondition(^bool() {
+ return completion_handler_called;
+ });
+
+ // "Clear Form" should appear as the first suggestion. Otherwise, the order of
+ // suggestions should not change.
+ EXPECT_EQ(3U, completion_handler_suggestions.count);
+ EXPECT_EQ(POPUP_ITEM_ID_CLEAR_FORM,
+ completion_handler_suggestions[0].identifier);
+ EXPECT_EQ(123, completion_handler_suggestions[1].identifier);
+ EXPECT_EQ(321, completion_handler_suggestions[2].identifier);
+}
+
+// Tests that when Autofill suggestions are made available to AutofillManager
+// GPay icon remains as the first suggestion.
+TEST_F(AutofillAgentTests, onSuggestionsReady_ClearFormWithGPay) {
+ __block NSArray<FormSuggestion*>* completion_handler_suggestions = nil;
+ __block BOOL completion_handler_called = NO;
+
+ // Make the suggestions available to AutofillManager.
+ NSArray* suggestions = @[
+ [FormSuggestion suggestionWithValue:@""
+ displayDescription:nil
+ icon:@""
+ identifier:POPUP_ITEM_ID_GOOGLE_PAY_BRANDING],
+ [FormSuggestion suggestionWithValue:@""
+ displayDescription:nil
+ icon:@""
+ identifier:123],
+ [FormSuggestion suggestionWithValue:@""
+ displayDescription:nil
+ icon:@""
+ identifier:321],
+ [FormSuggestion suggestionWithValue:@""
+ displayDescription:nil
+ icon:@""
+ identifier:POPUP_ITEM_ID_CLEAR_FORM]
+ ];
+ [autofill_agent_
+ onSuggestionsReady:suggestions
+ popupDelegate:base::WeakPtr<autofill::AutofillPopupDelegate>()];
+
+ // Retrieves the suggestions.
+ auto completionHandler = ^(NSArray<FormSuggestion*>* suggestions,
+ id<FormSuggestionProvider> delegate) {
+ completion_handler_suggestions = [suggestions copy];
+ completion_handler_called = YES;
+ };
+ [autofill_agent_ retrieveSuggestionsForForm:@"form"
+ fieldName:@"address"
+ fieldIdentifier:@"address"
+ fieldType:@"text"
+ type:@"focus"
+ typedValue:@""
+ webState:&test_web_state_
+ completionHandler:completionHandler];
+ test_web_state_.WasShown();
+
+ // Wait until the expected handler is called.
+ WaitUntilCondition(^bool() {
+ return completion_handler_called;
+ });
+
+ // GPay icon should appear as the first suggestion followed by "Clear Form".
+ // Otherwise, the order of suggestions should not change.
+ EXPECT_EQ(4U, completion_handler_suggestions.count);
+ EXPECT_EQ(POPUP_ITEM_ID_GOOGLE_PAY_BRANDING,
+ completion_handler_suggestions[0].identifier);
+ EXPECT_EQ(POPUP_ITEM_ID_CLEAR_FORM,
+ completion_handler_suggestions[1].identifier);
+ EXPECT_EQ(123, completion_handler_suggestions[2].identifier);
+ EXPECT_EQ(321, completion_handler_suggestions[3].identifier);
+}
diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios.h b/chromium/components/autofill/ios/browser/autofill_driver_ios.h
index a39cbc34c76..471cf9c2ebb 100644
--- a/chromium/components/autofill/ios/browser/autofill_driver_ios.h
+++ b/chromium/components/autofill/ios/browser/autofill_driver_ios.h
@@ -38,6 +38,7 @@ class AutofillDriverIOS : public AutofillDriver,
// AutofillDriver:
bool IsIncognito() const override;
net::URLRequestContextGetter* GetURLRequestContext() override;
+ scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
bool RendererIsAvailable() override;
void SendFormDataToRenderer(int query_id,
RendererFormDataAction action,
diff --git a/chromium/components/autofill/ios/browser/autofill_driver_ios.mm b/chromium/components/autofill/ios/browser/autofill_driver_ios.mm
index d94e80e6bd8..1df6ec5eb2e 100644
--- a/chromium/components/autofill/ios/browser/autofill_driver_ios.mm
+++ b/chromium/components/autofill/ios/browser/autofill_driver_ios.mm
@@ -10,6 +10,8 @@
#include "ios/web/public/browser_state.h"
#import "ios/web/public/origin_util.h"
#include "ios/web/public/web_state/web_state.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "ui/gfx/geometry/rect_f.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -59,6 +61,12 @@ net::URLRequestContextGetter* AutofillDriverIOS::GetURLRequestContext() {
return web_state_->GetBrowserState()->GetRequestContext();
}
+scoped_refptr<network::SharedURLLoaderFactory>
+AutofillDriverIOS::GetURLLoaderFactory() {
+ return base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ web_state_->GetBrowserState()->GetURLLoaderFactory());
+}
+
bool AutofillDriverIOS::RendererIsAvailable() {
return true;
}
diff --git a/chromium/components/autofill/ios/browser/autofill_util.mm b/chromium/components/autofill/ios/browser/autofill_util.mm
index 869efa7b637..36b3c10320c 100644
--- a/chromium/components/autofill/ios/browser/autofill_util.mm
+++ b/chromium/components/autofill/ios/browser/autofill_util.mm
@@ -28,7 +28,7 @@ bool IsContextSecureForWebState(web::WebState* web_state) {
// the iOS web view blocks active mixed content without an option to run it,
// there is no need to check for active mixed content here.
web::NavigationManager* manager = web_state->GetNavigationManager();
- web::NavigationItem* nav_item = manager->GetLastCommittedItem();
+ const web::NavigationItem* nav_item = manager->GetLastCommittedItem();
if (!nav_item)
return false;
diff --git a/chromium/components/autofill/ios/browser/fake_autofill_agent.mm b/chromium/components/autofill/ios/browser/fake_autofill_agent.mm
index 87bb7c2948d..b486c25412c 100644
--- a/chromium/components/autofill/ios/browser/fake_autofill_agent.mm
+++ b/chromium/components/autofill/ios/browser/fake_autofill_agent.mm
@@ -4,7 +4,7 @@
#import "components/autofill/ios/browser/fake_autofill_agent.h"
-#import "base/mac/bind_objc_block.h"
+#include "base/bind.h"
#include "ios/web/public/web_thread.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -59,11 +59,12 @@
type:(NSString*)type
typedValue:(NSString*)typedValue
isMainFrame:(BOOL)isMainFrame
+ hasUserGesture:(BOOL)hasUserGesture
webState:(web::WebState*)webState
completionHandler:
(SuggestionsAvailableCompletion)completion {
web::WebThread::PostTask(
- web::WebThread::UI, FROM_HERE, base::BindBlockArc(^{
+ web::WebThread::UI, FROM_HERE, base::BindOnce(^{
NSString* key =
[self keyForFormName:formName fieldIdentifier:fieldIdentifier];
completion([_suggestionsByFormAndFieldName[key] count] ? YES : NO);
@@ -79,7 +80,7 @@
webState:(web::WebState*)webState
completionHandler:(SuggestionsReadyCompletion)completion {
web::WebThread::PostTask(
- web::WebThread::UI, FROM_HERE, base::BindBlockArc(^{
+ web::WebThread::UI, FROM_HERE, base::BindOnce(^{
NSString* key =
[self keyForFormName:formName fieldIdentifier:fieldIdentifier];
completion(_suggestionsByFormAndFieldName[key], self);
@@ -92,7 +93,7 @@
form:(NSString*)formName
completionHandler:(SuggestionHandledCompletion)completion {
web::WebThread::PostTask(
- web::WebThread::UI, FROM_HERE, base::BindBlockArc(^{
+ web::WebThread::UI, FROM_HERE, base::BindOnce(^{
NSString* key =
[self keyForFormName:formName fieldIdentifier:fieldIdentifier];
_selectedSuggestionByFormAndFieldName[key] = suggestion;
diff --git a/chromium/components/autofill/ios/browser/fake_js_autofill_manager.h b/chromium/components/autofill/ios/browser/fake_js_autofill_manager.h
index 3d2c5532f36..e7f3ad7b879 100644
--- a/chromium/components/autofill/ios/browser/fake_js_autofill_manager.h
+++ b/chromium/components/autofill/ios/browser/fake_js_autofill_manager.h
@@ -13,9 +13,13 @@
@interface FakeJSAutofillManager : JsAutofillManager
// The name of the form that was most recently passed to
-// |clearAutofilledFieldsForFormNamed:completionHandler:|.
+// |clearAutofilledFieldsForFormName:fieldIdentifier:completionHandler:|.
@property(nonatomic, copy, readonly) NSString* lastClearedFormName;
+// The field identifier that was most recently passed to
+// |clearAutofilledFieldsForFormName:fieldIdentifier:completionHandler:|.
+@property(nonatomic, copy, readonly) NSString* lastClearedFieldIdentifier;
+
@end
#endif // COMPONENTS_AUTOFILL_IOS_BROWSER_FAKE_JS_AUTOFILL_MANAGER_H_
diff --git a/chromium/components/autofill/ios/browser/fake_js_autofill_manager.mm b/chromium/components/autofill/ios/browser/fake_js_autofill_manager.mm
index 42baa95eeaa..4d7203db8ee 100644
--- a/chromium/components/autofill/ios/browser/fake_js_autofill_manager.mm
+++ b/chromium/components/autofill/ios/browser/fake_js_autofill_manager.mm
@@ -4,7 +4,7 @@
#import "components/autofill/ios/browser/fake_js_autofill_manager.h"
-#import "base/mac/bind_objc_block.h"
+#include "base/bind.h"
#include "ios/web/public/web_thread.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -14,11 +14,15 @@
@implementation FakeJSAutofillManager
@synthesize lastClearedFormName = _lastClearedFormName;
+@synthesize lastClearedFieldIdentifier = _lastClearedFieldIdentifier;
-- (void)clearAutofilledFieldsForFormNamed:(NSString*)formName
- completionHandler:(ProceduralBlock)completionHandler {
- web::WebThread::PostTask(web::WebThread::UI, FROM_HERE, base::BindBlockArc(^{
+- (void)clearAutofilledFieldsForFormName:(NSString*)formName
+ fieldIdentifier:(NSString*)fieldIdentifier
+ completionHandler:(ProceduralBlock)completionHandler {
+ web::WebThread::PostTask(web::WebThread::UI, FROM_HERE, base::BindOnce(^{
_lastClearedFormName = [formName copy];
+ _lastClearedFieldIdentifier =
+ [fieldIdentifier copy];
completionHandler();
}));
}
diff --git a/chromium/components/autofill/ios/browser/form_suggestion_provider.h b/chromium/components/autofill/ios/browser/form_suggestion_provider.h
index 6d2b33be1b8..e06720dea0c 100644
--- a/chromium/components/autofill/ios/browser/form_suggestion_provider.h
+++ b/chromium/components/autofill/ios/browser/form_suggestion_provider.h
@@ -14,8 +14,9 @@ class WebState;
} // namespace web
typedef void (^SuggestionsAvailableCompletion)(BOOL suggestionsAvailable);
-typedef void (^SuggestionsReadyCompletion)(NSArray* suggestions,
- id<FormSuggestionProvider> delegate);
+typedef void (^SuggestionsReadyCompletion)(
+ NSArray<FormSuggestion*>* suggestions,
+ id<FormSuggestionProvider> delegate);
typedef void (^SuggestionHandledCompletion)(void);
// Provides user-selectable suggestions for an input field of a web form
@@ -32,6 +33,7 @@ typedef void (^SuggestionHandledCompletion)(void);
type:(NSString*)type
typedValue:(NSString*)typedValue
isMainFrame:(BOOL)isMainFrame
+ hasUserGesture:(BOOL)hasUserGesture
webState:(web::WebState*)webState
completionHandler:
(SuggestionsAvailableCompletion)completion;
diff --git a/chromium/components/autofill/ios/browser/js_autofill_manager.h b/chromium/components/autofill/ios/browser/js_autofill_manager.h
index 898a289c1ff..9adc6f924c4 100644
--- a/chromium/components/autofill/ios/browser/js_autofill_manager.h
+++ b/chromium/components/autofill/ios/browser/js_autofill_manager.h
@@ -41,10 +41,12 @@
// autofilled are not modified. Field contents are cleared, and Autofill flag
// and styling are removed. 'change' events are sent for fields whose contents
// changed.
+// |fieldIdentifier| identifies the field that initiated the clear action.
// |completionHandler| is called after the forms are filled. |completionHandler|
// cannot be nil.
-- (void)clearAutofilledFieldsForFormNamed:(NSString*)formName
- completionHandler:(ProceduralBlock)completionHandler;
+- (void)clearAutofilledFieldsForFormName:(NSString*)formName
+ fieldIdentifier:(NSString*)fieldIdentifier
+ completionHandler:(ProceduralBlock)completionHandler;
// Marks up the form with autofill field prediction data (diagnostic tool).
- (void)fillPredictionData:(NSString*)dataString;
@@ -55,6 +57,9 @@
// Toggles tracking form related changes in the page.
- (void)toggleTrackingFormMutations:(BOOL)state;
+// Toggles tracking the source of the input events in the page.
+- (void)toggleTrackingUserEditedFields:(BOOL)state;
+
// Designated initializer. |receiver| should not be nil.
- (instancetype)initWithReceiver:(CRWJSInjectionReceiver*)receiver
NS_DESIGNATED_INITIALIZER;
diff --git a/chromium/components/autofill/ios/browser/js_autofill_manager.mm b/chromium/components/autofill/ios/browser/js_autofill_manager.mm
index 92e2e73e3e8..1c318417663 100644
--- a/chromium/components/autofill/ios/browser/js_autofill_manager.mm
+++ b/chromium/components/autofill/ios/browser/js_autofill_manager.mm
@@ -91,6 +91,13 @@
[_receiver executeJavaScript:script completionHandler:nil];
}
+- (void)toggleTrackingUserEditedFields:(BOOL)state {
+ NSString* script = [NSString
+ stringWithFormat:@"__gCrWeb.form.toggleTrackingUserEditedFields(%s);",
+ state ? "true" : "false"];
+ [_receiver executeJavaScript:script completionHandler:nil];
+}
+
- (void)fillForm:(NSString*)dataString
forceFillFieldIdentifier:(NSString*)forceFillFieldIdentifier
completionHandler:(ProceduralBlock)completionHandler {
@@ -109,13 +116,18 @@
}];
}
-- (void)clearAutofilledFieldsForFormNamed:(NSString*)formName
- completionHandler:(ProceduralBlock)completionHandler {
+- (void)clearAutofilledFieldsForFormName:(NSString*)formName
+ fieldIdentifier:(NSString*)fieldIdentifier
+ completionHandler:(ProceduralBlock)completionHandler {
DCHECK(completionHandler);
- NSString* script =
- [NSString stringWithFormat:
- @"__gCrWeb.autofill.clearAutofilledFields(%s);",
- base::GetQuotedJSONString([formName UTF8String]).c_str()];
+ NSString* script = [NSString
+ stringWithFormat:@"__gCrWeb.autofill.clearAutofilledFields(%s, %s);",
+ base::GetQuotedJSONString(
+ base::SysNSStringToUTF8(formName))
+ .c_str(),
+ base::GetQuotedJSONString(
+ base::SysNSStringToUTF8(fieldIdentifier))
+ .c_str()];
[_receiver executeJavaScript:script
completionHandler:^(id, NSError*) {
completionHandler();
diff --git a/chromium/components/autofill/ios/browser/resources/autofill_controller.js b/chromium/components/autofill/ios/browser/resources/autofill_controller.js
index 5bb6f60d0ea..4798433972b 100644
--- a/chromium/components/autofill/ios/browser/resources/autofill_controller.js
+++ b/chromium/components/autofill/ios/browser/resources/autofill_controller.js
@@ -21,7 +21,7 @@ goog.provide('__crWeb.autofill');
* The autofill data for a form.
* @typedef {{
* formName: string,
- * fields: !Object<string, string>,
+ * fields: !Object<string, !Object<string, string>>,
* }}
*/
var FormData;
@@ -279,10 +279,10 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) {
if (__gCrWeb.fill.isCheckableElement(element))
continue;
- // Skip fields if autofill data is missing.
+ // Skip fields for which autofill data is missing.
var fieldIdentifier = __gCrWeb.form.getFieldIdentifier(element);
- var value = data.fields[fieldIdentifier];
- if (!value)
+ var fieldData = data.fields[fieldIdentifier];
+ if (!fieldData)
continue;
// Skip non-empty fields unless:
@@ -290,7 +290,8 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) {
// b) The element is a 'select-one' element. 'select-one' elements are
// always autofilled; see AutofillManager::FillOrPreviewDataModelForm().
// c) The "value" or "placeholder" attributes match the value, if any; or
- if (element.value &&
+ // d) The value has not been set by the user.
+ if (element.value && __gCrWeb.form.fieldWasEditedByUser(element) &&
!__gCrWeb.autofill.sanitizedFieldIsEmpty(element.value) &&
fieldIdentifier !== forceFillFieldIdentifier &&
!__gCrWeb.fill.isSelectElement(element) &&
@@ -302,15 +303,16 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) {
continue;
}
- (function(_element, _value, _delay) {
+ (function(_element, _value, _section, _delay) {
window.setTimeout(function() {
__gCrWeb.fill.setInputElementValue(_value, _element, function() {
_element.setAttribute('chrome-autofilled', '');
_element.isAutofilled = true;
+ _element.autofillSection = _section;
_element.addEventListener('input', controlElementInputListener_);
});
}, _delay);
- })(element, value, delay);
+ })(element, fieldData.value, fieldData.section, delay);
}
if (form) {
@@ -329,30 +331,45 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) {
}
};
-// TODO(crbug.com/816941): Clear should only clear the current section and not
-// the whole form.
/**
* Clear autofilled fields of the specified form section. Fields that are not
- * currently autofilled are not modified.
+ * currently autofilled or do not belong to the same section as that of the
+ * field with |fieldIdentifier| are not modified. If the field identified by
+ * |fieldIdentifier| cannot be found all autofilled form fields get cleared.
* Field contents are cleared, and Autofill flag and styling are removed.
* 'change' events are sent for fields whose contents changed.
* Based on FormCache::ClearSectionWithElement().
*
* @param {string} formName Identifier for form element (from
* getFormIdentifier).
+ * @param {string} fieldIdentifier Identifier for form field initiating the
+ * clear action.
*/
-__gCrWeb.autofill['clearAutofilledFields'] = function(formName) {
+__gCrWeb.autofill['clearAutofilledFields'] = function(
+ formName, fieldIdentifier) {
var form = __gCrWeb.form.getFormElementFromIdentifier(formName);
var controlElements = form ?
__gCrWeb.form.getFormControlElements(form) :
getUnownedAutofillableFormFieldElements_(document.all, /*fieldsets=*/[]);
+ var formField = null;
+ for (var i = 0; i < controlElements.length; ++i) {
+ if (__gCrWeb.form.getFieldIdentifier(controlElements[i]) ==
+ fieldIdentifier) {
+ formField = controlElements[i];
+ break;
+ }
+ }
+
for (var i = 0, delay = 0; i < controlElements.length;
++i, delay += __gCrWeb.autofill.delayBetweenFieldFillingMs) {
var element = controlElements[i];
if (!element.isAutofilled || element.disabled)
continue;
+ if (formField && formField.autofillSection != element.autofillSection)
+ continue;
+
var value = null;
if (__gCrWeb.fill.isTextInput(element) ||
__gCrWeb.fill.isTextAreaElement(element)) {
diff --git a/chromium/components/autofill/ios/fill/BUILD.gn b/chromium/components/autofill/ios/fill/BUILD.gn
deleted file mode 100644
index 8797ef1de55..00000000000
--- a/chromium/components/autofill/ios/fill/BUILD.gn
+++ /dev/null
@@ -1,35 +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.
-
-import("//ios/web/js_compile.gni")
-import("//testing/test.gni")
-
-source_set("unit_tests") {
- testonly = true
- configs += [ "//build/config/compiler:enable_arc" ]
- sources = [
- "fill_js_unittest.mm",
- "form_unittest.mm",
- ]
- deps = [
- ":form_js",
- "//base",
- "//ios/chrome/app:tests_fake_hook",
- "//ios/chrome/browser/browser_state:test_support",
- "//ios/chrome/browser/tabs:tabs_internal",
- "//ios/chrome/browser/web:test_support",
- "//ios/web/public/test",
- "//ios/web/public/test/fakes",
- "//ios/web/web_state/js",
- "//testing/gtest",
- ]
-}
-
-js_compile_checked("form_js") {
- visibility = [ ":unit_tests" ]
- testonly = true
- sources = [
- "resources/form.js",
- ]
-}
diff --git a/chromium/components/autofill/ios/form_util/BUILD.gn b/chromium/components/autofill/ios/form_util/BUILD.gn
new file mode 100644
index 00000000000..3b0c7ee87ba
--- /dev/null
+++ b/chromium/components/autofill/ios/form_util/BUILD.gn
@@ -0,0 +1,73 @@
+# 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.
+
+import("//ios/web/js_compile.gni")
+import("//testing/test.gni")
+
+source_set("form_util") {
+ configs += [ "//build/config/compiler:enable_arc" ]
+ sources = [
+ "form_activity_observer.h",
+ "form_activity_observer_bridge.h",
+ "form_activity_observer_bridge.mm",
+ "form_activity_tab_helper.h",
+ "form_activity_tab_helper.mm",
+ ]
+ deps = [
+ "//base",
+ "//ios/web/public",
+ ]
+}
+
+source_set("test_support") {
+ testonly = true
+ configs += [ "//build/config/compiler:enable_arc" ]
+ sources = [
+ "test_form_activity_observer.h",
+ "test_form_activity_observer.mm",
+ "test_form_activity_tab_helper.h",
+ "test_form_activity_tab_helper.mm",
+ ]
+ deps = [
+ ":form_util",
+ "//base",
+ "//ios/web/public",
+ "//testing/gtest",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ configs += [ "//build/config/compiler:enable_arc" ]
+ sources = [
+ "fill_js_unittest.mm",
+ "form_activity_observer_bridge_unittest.mm",
+ "form_activity_tab_helper_unittest.mm",
+ "form_unittest.mm",
+ ]
+ deps = [
+ ":form_js",
+ ":form_util",
+ ":test_support",
+ "//base",
+ "//base/test:test_support",
+ "//ios/chrome/app:tests_fake_hook",
+ "//ios/chrome/browser/browser_state:test_support",
+ "//ios/chrome/browser/tabs:tabs_internal",
+ "//ios/chrome/browser/web:test_support",
+ "//ios/testing:ios_test_support",
+ "//ios/web/public/test",
+ "//ios/web/public/test/fakes",
+ "//ios/web/web_state/js",
+ "//testing/gtest",
+ ]
+}
+
+js_compile_checked("form_js") {
+ visibility = [ ":unit_tests" ]
+ testonly = true
+ sources = [
+ "resources/form.js",
+ ]
+}
diff --git a/chromium/components/autofill/ios/fill/DEPS b/chromium/components/autofill/ios/form_util/DEPS
index 8ef00148a88..4c30da6e895 100644
--- a/chromium/components/autofill/ios/fill/DEPS
+++ b/chromium/components/autofill/ios/form_util/DEPS
@@ -1,4 +1,5 @@
specific_include_rules = {
+ # Unittests using form.js need page_script_util to inject the script.
"form_unittest\.mm": [
"+ios/web/web_state/js/page_script_util.h",
],
diff --git a/chromium/components/autofill/ios/fill/fill_js_unittest.mm b/chromium/components/autofill/ios/form_util/fill_js_unittest.mm
index c3d22a6f313..7381bce3d1d 100644
--- a/chromium/components/autofill/ios/fill/fill_js_unittest.mm
+++ b/chromium/components/autofill/ios/form_util/fill_js_unittest.mm
@@ -6,6 +6,7 @@
#include <stddef.h>
#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/sys_string_conversions.h"
#import "ios/web/public/test/web_js_test.h"
#import "ios/web/public/test/web_test_with_web_state.h"
@@ -43,7 +44,7 @@ TEST_F(FillJsTest, GetCanonicalActionForForm) {
{@"javascript:login()", @"javascript:login()"},
};
- for (size_t i = 0; i < arraysize(test_data); i++) {
+ for (size_t i = 0; i < base::size(test_data); i++) {
TestData& data = test_data[i];
NSString* html_action =
data.html_action == nil
diff --git a/chromium/components/autofill/ios/form_util/form_activity_observer.h b/chromium/components/autofill/ios/form_util/form_activity_observer.h
new file mode 100644
index 00000000000..538d616bd30
--- /dev/null
+++ b/chromium/components/autofill/ios/form_util/form_activity_observer.h
@@ -0,0 +1,58 @@
+// 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_IOS_FORM_UTIL_FORM_ACTIVITY_OBSERVER_H_
+#define COMPONENTS_AUTOFILL_IOS_FORM_UTIL_FORM_ACTIVITY_OBSERVER_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace web {
+class WebState;
+struct FormActivityParams;
+} // namespace web
+
+namespace autofill {
+
+// Interface for observing form activity.
+// It is the responsibility of the observer to unregister if the web_state
+// becomes invalid.
+class FormActivityObserver {
+ public:
+ FormActivityObserver() {}
+ virtual ~FormActivityObserver() {}
+
+ // Called when the user is typing on a form field in the main frame or in a
+ // same-origin iframe. |params.input_missing| is indicating if there is any
+ // error when parsing the form field information.
+ // TODO(crbug.com/823285): during the transition from WebStateObserver
+ // to FormActivityObserver, some class will inherit from both interface
+ // so the method need to use a different name. Once the transition is
+ // complete and the methods removed from WebStateObserver, this method
+ // will be renamed to FormActivityRegistered.
+ virtual void OnFormActivity(web::WebState* web_state,
+ const web::FormActivityParams& params) {}
+
+ // Called on form submission in the main frame or in a same-origin iframe.
+ // |has_user_gesture| is true if the user interacted with the page.
+ // |form_in_main_frame| is true if the submitted form is hosted in the main
+ // frame.
+ // TODO(crbug.com/823285): during the transition from WebStateObserver
+ // to FormActivityObserver, some class will inherit from both interface
+ // so the method need to use a different name. Once the transition is
+ // complete and the methods removed from WebStateObserver, this method
+ // will be renamed to DocumentSubmitted.
+ virtual void DidSubmitDocument(web::WebState* web_state,
+ const std::string& form_name,
+ bool has_user_gesture,
+ bool form_in_main_frame) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FormActivityObserver);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_IOS_FORM_UTIL_FORM_ACTIVITY_OBSERVER_H_
diff --git a/chromium/components/autofill/ios/form_util/form_activity_observer_bridge.h b/chromium/components/autofill/ios/form_util/form_activity_observer_bridge.h
new file mode 100644
index 00000000000..382788fe78a
--- /dev/null
+++ b/chromium/components/autofill/ios/form_util/form_activity_observer_bridge.h
@@ -0,0 +1,71 @@
+// 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_IOS_FORM_UTIL_FORM_ACTIVITY_OBSERVER_BRIDGE_H_
+#define COMPONENTS_AUTOFILL_IOS_FORM_UTIL_FORM_ACTIVITY_OBSERVER_BRIDGE_H_
+
+#import <Foundation/Foundation.h>
+
+#include "base/macros.h"
+#include "components/autofill/ios/form_util/form_activity_observer.h"
+
+@protocol FormActivityObserver<NSObject>
+@optional
+// Invoked by WebStateObserverBridge::FormActivity.
+// TODO(crbug.com/823285): during the transition from CRWWebStateObserver
+// to FormActivityObserver, some class will implement from both protocols
+// so the method need to use a different name. Once the transition is
+// complete and the methods removed from CRWWebStateObserver, this method
+// will be renamed to didRegisterFormActivity.
+- (void)webState:(web::WebState*)webState
+ registeredFormActivity:(const web::FormActivityParams&)params;
+
+// Invoked by WebStateObserverBridge::DidSubmitDocument.
+// TODO(crbug.com/823285): during the transition from CRWWebStateObserver
+// to FormActivityObserver, some class will implement from both protocols
+// so the method need to use a different name. Once the transition is
+// complete and the methods removed from CRWWebStateObserver, this method
+// will be renamed to didSubmitDocumentWithFormNamed.
+- (void)webState:(web::WebState*)webState
+ submittedDocumentWithFormNamed:(const std::string&)formName
+ hasUserGesture:(BOOL)hasUserGesture
+ formInMainFrame:(BOOL)formInMainFrame;
+
+@end
+
+namespace autofill {
+
+// Use this class to be notified of the form activity in an Objective-C class.
+// Implement the |FormActivityObserver| activity protocol and create a strong
+// member FormActivityObserverBridge
+// form_activity_obserber_bridge_ =
+// std::make_unique<FormActivityObserverBridge>(web_state, self);
+// It is the responsibility of the owner class to delete this bridge if the
+// web_state becomes invalid.
+class FormActivityObserverBridge : public FormActivityObserver {
+ public:
+ // |owner| will not be retained.
+ FormActivityObserverBridge(web::WebState* web_state,
+ id<FormActivityObserver> owner);
+ ~FormActivityObserverBridge() override;
+
+ // FormActivityObserver overrides:
+ void OnFormActivity(web::WebState* web_state,
+ const web::FormActivityParams& params) override;
+
+ void DidSubmitDocument(web::WebState* web_state,
+ const std::string& form_name,
+ bool has_user_gesture,
+ bool form_in_main_frame) override;
+
+ private:
+ web::WebState* web_state_ = nullptr;
+ __weak id<FormActivityObserver> owner_ = nil;
+
+ DISALLOW_COPY_AND_ASSIGN(FormActivityObserverBridge);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_IOS_FORM_UTIL_FORM_ACTIVITY_OBSERVER_BRIDGE_H_
diff --git a/chromium/components/autofill/ios/form_util/form_activity_observer_bridge.mm b/chromium/components/autofill/ios/form_util/form_activity_observer_bridge.mm
new file mode 100644
index 00000000000..0e8bb73a5fc
--- /dev/null
+++ b/chromium/components/autofill/ios/form_util/form_activity_observer_bridge.mm
@@ -0,0 +1,50 @@
+// 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/ios/form_util/form_activity_observer_bridge.h"
+
+#include "base/logging.h"
+#include "components/autofill/ios/form_util/form_activity_tab_helper.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace autofill {
+FormActivityObserverBridge::FormActivityObserverBridge(
+ web::WebState* web_state,
+ id<FormActivityObserver> owner)
+ : web_state_(web_state), owner_(owner) {
+ FormActivityTabHelper::GetOrCreateForWebState(web_state)->AddObserver(this);
+}
+
+FormActivityObserverBridge::~FormActivityObserverBridge() {
+ FormActivityTabHelper::GetOrCreateForWebState(web_state_)
+ ->RemoveObserver(this);
+}
+
+void FormActivityObserverBridge::OnFormActivity(
+ web::WebState* web_state,
+ const web::FormActivityParams& params) {
+ DCHECK_EQ(web_state, web_state_);
+ if ([owner_ respondsToSelector:@selector(webState:registeredFormActivity:)]) {
+ [owner_ webState:web_state registeredFormActivity:params];
+ }
+}
+
+void FormActivityObserverBridge::DidSubmitDocument(web::WebState* web_state,
+ const std::string& form_name,
+ bool has_user_gesture,
+ bool form_in_main_frame) {
+ DCHECK_EQ(web_state, web_state_);
+ if ([owner_ respondsToSelector:@selector
+ (webState:submittedDocumentWithFormNamed:hasUserGesture
+ :formInMainFrame:)]) {
+ [owner_ webState:web_state
+ submittedDocumentWithFormNamed:form_name
+ hasUserGesture:has_user_gesture
+ formInMainFrame:form_in_main_frame];
+ }
+}
+} // namespace autofill
diff --git a/chromium/components/autofill/ios/form_util/form_activity_observer_bridge_unittest.mm b/chromium/components/autofill/ios/form_util/form_activity_observer_bridge_unittest.mm
new file mode 100644
index 00000000000..6ea94774fda
--- /dev/null
+++ b/chromium/components/autofill/ios/form_util/form_activity_observer_bridge_unittest.mm
@@ -0,0 +1,113 @@
+// 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.
+
+#import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
+
+#include "components/autofill/ios/form_util/test_form_activity_observer.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface FakeFormActivityObserver : NSObject<FormActivityObserver>
+// Arguments passed to |webState:didSubmitDocumentWithFormNamed:userInitiated:|.
+@property(nonatomic, readonly)
+ autofill::TestSubmitDocumentInfo* submitDocumentInfo;
+// Arguments passed to
+// |webState:didRegisterFormActivity:|.
+@property(nonatomic, readonly) autofill::TestFormActivityInfo* formActivityInfo;
+@end
+
+@implementation FakeFormActivityObserver {
+ // Arguments passed to
+ // |webState:submittedDocumentWithFormNamed:hasUserGesture:formInMainFrame:|.
+ std::unique_ptr<autofill::TestSubmitDocumentInfo> _submitDocumentInfo;
+ // Arguments passed to
+ // |webState:registeredFormActivity:|.
+ std::unique_ptr<autofill::TestFormActivityInfo> _formActivityInfo;
+}
+
+- (autofill::TestSubmitDocumentInfo*)submitDocumentInfo {
+ return _submitDocumentInfo.get();
+}
+
+- (autofill::TestFormActivityInfo*)formActivityInfo {
+ return _formActivityInfo.get();
+}
+
+- (void)webState:(web::WebState*)webState
+ submittedDocumentWithFormNamed:(const std::string&)formName
+ hasUserGesture:(BOOL)hasUserGesture
+ formInMainFrame:(BOOL)formInMainFrame {
+ _submitDocumentInfo = std::make_unique<autofill::TestSubmitDocumentInfo>();
+ _submitDocumentInfo->web_state = webState;
+ _submitDocumentInfo->form_name = formName;
+ _submitDocumentInfo->has_user_gesture = hasUserGesture;
+ _submitDocumentInfo->form_in_main_frame = formInMainFrame;
+}
+
+- (void)webState:(web::WebState*)webState
+ registeredFormActivity:(const web::FormActivityParams&)params {
+ _formActivityInfo = std::make_unique<autofill::TestFormActivityInfo>();
+ _formActivityInfo->web_state = webState;
+ _formActivityInfo->form_activity = params;
+}
+
+@end
+
+// Test fixture to test WebStateObserverBridge class.
+class FormActivityObserverBridgeTest : public PlatformTest {
+ public:
+ FormActivityObserverBridgeTest()
+ : observer_([[FakeFormActivityObserver alloc] init]),
+ observer_bridge_(&test_web_state_, observer_) {}
+
+ protected:
+ web::TestWebState test_web_state_;
+ FakeFormActivityObserver* observer_;
+ autofill::FormActivityObserverBridge observer_bridge_;
+};
+
+// Tests |webState:didRegisterFormActivityWithParams:| forwarding.
+TEST_F(FormActivityObserverBridgeTest, DocumentSubmitted) {
+ ASSERT_FALSE([observer_ submitDocumentInfo]);
+ std::string kTestFormName("form-name");
+ bool has_user_gesture = true;
+ bool form_in_main_frame = true;
+ observer_bridge_.DidSubmitDocument(&test_web_state_, kTestFormName,
+ has_user_gesture, form_in_main_frame);
+ ASSERT_TRUE([observer_ submitDocumentInfo]);
+ EXPECT_EQ(&test_web_state_, [observer_ submitDocumentInfo]->web_state);
+ EXPECT_EQ(kTestFormName, [observer_ submitDocumentInfo]->form_name);
+ EXPECT_EQ(has_user_gesture, [observer_ submitDocumentInfo]->has_user_gesture);
+ EXPECT_EQ(form_in_main_frame,
+ [observer_ submitDocumentInfo]->form_in_main_frame);
+}
+
+// Tests |webState:didRegisterFormActivity:...| forwarding.
+TEST_F(FormActivityObserverBridgeTest, FormActivityRegistered) {
+ ASSERT_FALSE([observer_ formActivityInfo]);
+
+ web::FormActivityParams params;
+ params.form_name = "form-name";
+ params.field_name = "field-name";
+ params.field_type = "field-type";
+ params.type = "type";
+ params.value = "value";
+ params.input_missing = true;
+ observer_bridge_.OnFormActivity(&test_web_state_, params);
+ ASSERT_TRUE([observer_ formActivityInfo]);
+ EXPECT_EQ(&test_web_state_, [observer_ formActivityInfo]->web_state);
+ EXPECT_EQ(params.form_name,
+ [observer_ formActivityInfo]->form_activity.form_name);
+ EXPECT_EQ(params.field_name,
+ [observer_ formActivityInfo]->form_activity.field_name);
+ EXPECT_EQ(params.field_type,
+ [observer_ formActivityInfo]->form_activity.field_type);
+ EXPECT_EQ(params.type, [observer_ formActivityInfo]->form_activity.type);
+ EXPECT_EQ(params.value, [observer_ formActivityInfo]->form_activity.value);
+ EXPECT_TRUE([observer_ formActivityInfo]->form_activity.input_missing);
+}
diff --git a/chromium/components/autofill/ios/form_util/form_activity_tab_helper.h b/chromium/components/autofill/ios/form_util/form_activity_tab_helper.h
new file mode 100644
index 00000000000..ef98869b5d8
--- /dev/null
+++ b/chromium/components/autofill/ios/form_util/form_activity_tab_helper.h
@@ -0,0 +1,74 @@
+// 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_IOS_FORM_UTIL_FORM_ACTIVITY_TAB_HELPER_H_
+#define COMPONENTS_AUTOFILL_IOS_FORM_UTIL_FORM_ACTIVITY_TAB_HELPER_H_
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/values.h"
+#import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
+#include "ios/web/public/web_state/web_state_observer.h"
+#import "ios/web/public/web_state/web_state_user_data.h"
+
+namespace autofill {
+
+class FormActivityObserver;
+
+// Observes user activity on web page forms and forwards form activity event to
+// FormActivityObserver.
+class FormActivityTabHelper
+ : public web::WebStateObserver,
+ public web::WebStateUserData<FormActivityTabHelper> {
+ public:
+ ~FormActivityTabHelper() override;
+
+ static FormActivityTabHelper* GetOrCreateForWebState(
+ web::WebState* web_state);
+
+ // Observer registration methods.
+ virtual void AddObserver(FormActivityObserver* observer);
+ virtual void RemoveObserver(FormActivityObserver* observer);
+
+ private:
+ friend class web::WebStateUserData<FormActivityTabHelper>;
+ // TestFormActivityTabHelper can be used by tests that want to simulate form
+ // events without loading page and executing JavaScript.
+ // To trigger events, TestFormActivityTabHelper will access |observer_|.
+ friend class TestFormActivityTabHelper;
+
+ explicit FormActivityTabHelper(web::WebState* web_state);
+
+ // WebStateObserver implementation.
+ void WebStateDestroyed(web::WebState* web_state) override;
+
+ // Handler for "form.activity" JavaScript command.
+ bool HandleFormActivity(const base::DictionaryValue& message,
+ bool has_user_gesture,
+ bool form_in_main_frame);
+
+ // Handler for "form.submit" JavaScript command.
+ bool FormSubmissionHandler(const base::DictionaryValue& message,
+ bool has_user_gesture,
+ bool form_in_main_frame);
+
+ // Handler for "form.*" JavaScript command. Dispatch to more specific handler.
+ bool OnFormCommand(const base::DictionaryValue& message,
+ const GURL& url,
+ bool has_user_gesture,
+ bool form_in_main_frame);
+
+ // The WebState this instance is observing. Will be null after
+ // WebStateDestroyed has been called.
+ web::WebState* web_state_ = nullptr;
+
+ // The observers.
+ base::ObserverList<FormActivityObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(FormActivityTabHelper);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_IOS_FORM_UTIL_FORM_ACTIVITY_TAB_HELPER_H_
diff --git a/chromium/components/autofill/ios/form_util/form_activity_tab_helper.mm b/chromium/components/autofill/ios/form_util/form_activity_tab_helper.mm
new file mode 100644
index 00000000000..0a30ff60405
--- /dev/null
+++ b/chromium/components/autofill/ios/form_util/form_activity_tab_helper.mm
@@ -0,0 +1,133 @@
+// 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/ios/form_util/form_activity_tab_helper.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/values.h"
+#include "components/autofill/ios/form_util/form_activity_observer.h"
+#include "ios/web/public/web_state/form_activity_params.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+DEFINE_WEB_STATE_USER_DATA_KEY(autofill::FormActivityTabHelper);
+
+namespace autofill {
+
+namespace {
+// Prefix for the form activity event commands. Must be kept in sync with
+// form.js.
+const char kCommandPrefix[] = "form";
+}
+
+// static
+FormActivityTabHelper* FormActivityTabHelper::GetOrCreateForWebState(
+ web::WebState* web_state) {
+ FormActivityTabHelper* helper = FromWebState(web_state);
+ if (!helper) {
+ CreateForWebState(web_state);
+ helper = FromWebState(web_state);
+ DCHECK(helper);
+ }
+ return helper;
+}
+
+FormActivityTabHelper::FormActivityTabHelper(web::WebState* web_state)
+ : web_state_(web_state) {
+ web_state_->AddObserver(this);
+ web_state_->AddScriptCommandCallback(
+ base::BindRepeating(&FormActivityTabHelper::OnFormCommand,
+ base::Unretained(this)),
+ kCommandPrefix);
+}
+
+FormActivityTabHelper::~FormActivityTabHelper() {
+ if (web_state_) {
+ web_state_->RemoveObserver(this);
+ web_state_->RemoveScriptCommandCallback(kCommandPrefix);
+ web_state_ = nullptr;
+ }
+}
+
+void FormActivityTabHelper::AddObserver(FormActivityObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void FormActivityTabHelper::RemoveObserver(FormActivityObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool FormActivityTabHelper::OnFormCommand(const base::DictionaryValue& message,
+ const GURL& url,
+ bool has_user_gesture,
+ bool form_in_main_frame) {
+ std::string command;
+ if (!message.GetString("command", &command)) {
+ DLOG(WARNING) << "JS message parameter not found: command";
+ return NO;
+ }
+ if (command == "form.submit") {
+ return FormSubmissionHandler(message, has_user_gesture, form_in_main_frame);
+ }
+ if (command == "form.activity") {
+ return HandleFormActivity(message, has_user_gesture, form_in_main_frame);
+ }
+ return false;
+}
+
+bool FormActivityTabHelper::HandleFormActivity(
+ const base::DictionaryValue& message,
+ bool has_user_gesture,
+ bool form_in_main_frame) {
+ web::FormActivityParams params;
+ if (!message.GetString("formName", &params.form_name) ||
+ !message.GetString("fieldName", &params.field_name) ||
+ !message.GetString("fieldIdentifier", &params.field_identifier) ||
+ !message.GetString("fieldType", &params.field_type) ||
+ !message.GetString("type", &params.type) ||
+ !message.GetString("value", &params.value) ||
+ !message.GetBoolean("hasUserGesture", &params.has_user_gesture)) {
+ params.input_missing = true;
+ }
+
+ params.is_main_frame = form_in_main_frame;
+ for (auto& observer : observers_)
+ observer.OnFormActivity(web_state_, params);
+ return true;
+}
+
+bool FormActivityTabHelper::FormSubmissionHandler(
+ const base::DictionaryValue& message,
+ bool has_user_gesture,
+ bool form_in_main_frame) {
+ std::string href;
+ if (!message.GetString("href", &href)) {
+ DLOG(WARNING) << "JS message parameter not found: href";
+ return false;
+ }
+ std::string form_name;
+ message.GetString("formName", &form_name);
+ // We decide the form is user-submitted if the user has interacted with
+ // the main page (using logic from the popup blocker), or if the keyboard
+ // is visible.
+ BOOL submitted_by_user =
+ has_user_gesture || [web_state_->GetWebViewProxy() keyboardAccessory];
+
+ for (auto& observer : observers_)
+ observer.DidSubmitDocument(web_state_, form_name, submitted_by_user,
+ form_in_main_frame);
+ return true;
+}
+
+void FormActivityTabHelper::WebStateDestroyed(web::WebState* web_state) {
+ DCHECK_EQ(web_state_, web_state);
+ web_state_->RemoveScriptCommandCallback(kCommandPrefix);
+ web_state_->RemoveObserver(this);
+ web_state_ = nullptr;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm b/chromium/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm
new file mode 100644
index 00000000000..27731e1b829
--- /dev/null
+++ b/chromium/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm
@@ -0,0 +1,92 @@
+// 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.
+
+#import "components/autofill/ios/form_util/form_activity_tab_helper.h"
+
+#import "base/test/ios/wait_util.h"
+#import "components/autofill/ios/form_util/form_activity_observer.h"
+#import "components/autofill/ios/form_util/test_form_activity_observer.h"
+#import "ios/web/public/test/fakes/test_web_client.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
+#import "ios/web/public/test/fakes/test_web_state_observer_util.h"
+#import "ios/web/public/test/web_js_test.h"
+#import "ios/web/public/test/web_test_with_web_state.h"
+#include "testing/platform_test.h"
+
+// Test fixture for autofill::FormActivityTabHelper class.
+class FormActivityTabHelperTest
+ : public web::WebJsTest<web::WebTestWithWebState> {
+ public:
+ FormActivityTabHelperTest()
+ : web::WebJsTest<web::WebTestWithWebState>(
+ @[ @"chrome_bundle_all_frames" ]) {}
+
+ void SetUp() override {
+ web::WebJsTest<web::WebTestWithWebState>::SetUp();
+ autofill::FormActivityTabHelper* tab_helper =
+ autofill::FormActivityTabHelper::GetOrCreateForWebState(web_state());
+ observer_ =
+ std::make_unique<autofill::TestFormActivityObserver>(web_state());
+ tab_helper->AddObserver(observer_.get());
+ }
+
+ void TearDown() override {
+ autofill::FormActivityTabHelper* tab_helper =
+ autofill::FormActivityTabHelper::GetOrCreateForWebState(web_state());
+ tab_helper->RemoveObserver(observer_.get());
+ web::WebJsTest<web::WebTestWithWebState>::TearDown();
+ }
+
+ protected:
+ std::unique_ptr<autofill::TestFormActivityObserver> observer_;
+};
+
+// Test that observer is called on form submission.
+TEST_F(FormActivityTabHelperTest, TestObserverDocumentSubmitted) {
+ LoadHtmlAndInject(
+ @"<form name='form-name'>"
+ "<input type='submit' id='submit'/>"
+ "</form>");
+ ASSERT_FALSE(observer_->submit_document_info());
+ const std::string kTestFormName("form-name");
+ bool has_user_gesture = false;
+ bool form_in_main_frame = true;
+
+ ExecuteJavaScript(@"document.getElementById('submit').click();");
+ ASSERT_TRUE(observer_->submit_document_info());
+ EXPECT_EQ(web_state(), observer_->submit_document_info()->web_state);
+ EXPECT_EQ(kTestFormName, observer_->submit_document_info()->form_name);
+ EXPECT_EQ(has_user_gesture,
+ observer_->submit_document_info()->has_user_gesture);
+ EXPECT_EQ(form_in_main_frame,
+ observer_->submit_document_info()->form_in_main_frame);
+}
+
+// Test that observer is called on form activity (input event).
+TEST_F(FormActivityTabHelperTest, TestObserverFormActivity) {
+ LoadHtmlAndInject(
+ @"<form name='form-name'>"
+ "<input type='input' name='field-name' id='fieldid'/>"
+ "</form>");
+ ASSERT_FALSE(observer_->form_activity_info());
+ // First call will set document.activeElement (which is usually set by user
+ // action. Second call will trigger the message.
+ ExecuteJavaScript(@"document.getElementById('fieldid').focus();");
+ ASSERT_FALSE(observer_->form_activity_info());
+ ExecuteJavaScript(@"document.getElementById('fieldid').focus();");
+ EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(5, ^bool {
+ return observer_->form_activity_info() != nullptr;
+ }));
+ EXPECT_EQ(web_state(), observer_->form_activity_info()->web_state);
+ EXPECT_EQ("form-name",
+ observer_->form_activity_info()->form_activity.form_name);
+ EXPECT_EQ("field-name",
+ observer_->form_activity_info()->form_activity.field_name);
+ EXPECT_EQ("text", observer_->form_activity_info()->form_activity.field_type);
+ EXPECT_EQ("focus", observer_->form_activity_info()->form_activity.type);
+ EXPECT_EQ("", observer_->form_activity_info()->form_activity.value);
+ EXPECT_FALSE(observer_->form_activity_info()->form_activity.input_missing);
+ EXPECT_TRUE(observer_->form_activity_info()->form_activity.is_main_frame);
+ EXPECT_TRUE(observer_->form_activity_info()->form_activity.has_user_gesture);
+}
diff --git a/chromium/components/autofill/ios/fill/form_unittest.mm b/chromium/components/autofill/ios/form_util/form_unittest.mm
index 4bf728d5108..6ff183c2143 100644
--- a/chromium/components/autofill/ios/fill/form_unittest.mm
+++ b/chromium/components/autofill/ios/form_util/form_unittest.mm
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "components/autofill/ios/form_util/form_activity_tab_helper.h"
+#include "components/autofill/ios/form_util/test_form_activity_observer.h"
#import "ios/web/public/browser_state.h"
#import "ios/web/public/test/fakes/test_web_client.h"
#include "ios/web/public/test/fakes/test_web_state_observer.h"
@@ -28,24 +30,40 @@ class FormJsTest : public web::WebJsTest<web::WebTestWithWebState> {
FormJsTest()
: web::WebJsTest<web::WebTestWithWebState>(
std::make_unique<FormTestClient>()) {}
+
+ void SetUp() override {
+ web::WebJsTest<web::WebTestWithWebState>::SetUp();
+ observer_ =
+ std::make_unique<autofill::TestFormActivityObserver>(web_state());
+ autofill::FormActivityTabHelper::GetOrCreateForWebState(web_state())
+ ->AddObserver(observer_.get());
+ }
+
+ void TearDown() override {
+ autofill::FormActivityTabHelper::GetOrCreateForWebState(web_state())
+ ->RemoveObserver(observer_.get());
+ web::WebJsTest<web::WebTestWithWebState>::TearDown();
+ }
+
+ protected:
+ std::unique_ptr<autofill::TestFormActivityObserver> observer_;
};
// Tests that keyup event correctly delivered to WebStateObserver if the element
// is focused.
TEST_F(FormJsTest, KeyUpEventFocused) {
- web::TestWebStateObserver observer(web_state());
LoadHtml(@"<p><input id='test'/></p>");
- ASSERT_FALSE(observer.form_activity_info());
+ ASSERT_FALSE(observer_->form_activity_info());
ExecuteJavaScript(
@"var e = document.getElementById('test');"
"e.focus();"
"var ev = new KeyboardEvent('keyup', {bubbles:true});"
"e.dispatchEvent(ev);");
- web::TestWebStateObserver* block_observer = &observer;
+ autofill::TestFormActivityObserver* block_observer = observer_.get();
WaitForCondition(^bool {
return block_observer->form_activity_info() != nullptr;
});
- web::TestFormActivityInfo* info = observer.form_activity_info();
+ autofill::TestFormActivityInfo* info = observer_->form_activity_info();
ASSERT_TRUE(info);
EXPECT_EQ("keyup", info->form_activity.type);
EXPECT_FALSE(info->form_activity.input_missing);
@@ -54,33 +72,31 @@ TEST_F(FormJsTest, KeyUpEventFocused) {
// Tests that keyup event is not delivered to WebStateObserver if the element is
// not focused.
TEST_F(FormJsTest, KeyUpEventNotFocused) {
- web::TestWebStateObserver observer(web_state());
LoadHtml(@"<p><input id='test'/></p>");
- ASSERT_FALSE(observer.form_activity_info());
+ ASSERT_FALSE(observer_->form_activity_info());
ExecuteJavaScript(
@"var e = document.getElementById('test');"
"var ev = new KeyboardEvent('keyup', {bubbles:true});"
"e.dispatchEvent(ev);");
WaitForBackgroundTasks();
- web::TestFormActivityInfo* info = observer.form_activity_info();
+ autofill::TestFormActivityInfo* info = observer_->form_activity_info();
ASSERT_FALSE(info);
}
// Tests that focus event correctly delivered to WebStateObserver.
TEST_F(FormJsTest, FocusMainFrame) {
- web::TestWebStateObserver observer(web_state());
LoadHtml(
@"<form>"
"<input type='text' name='username' id='id1'>"
"<input type='password' name='password' id='id2'>"
"</form>");
- ASSERT_FALSE(observer.form_activity_info());
+ ASSERT_FALSE(observer_->form_activity_info());
ExecuteJavaScript(@"document.getElementById('id1').focus();");
- web::TestWebStateObserver* block_observer = &observer;
+ autofill::TestFormActivityObserver* block_observer = observer_.get();
WaitForCondition(^bool {
return block_observer->form_activity_info() != nullptr;
});
- web::TestFormActivityInfo* info = observer.form_activity_info();
+ autofill::TestFormActivityInfo* info = observer_->form_activity_info();
ASSERT_TRUE(info);
EXPECT_EQ("focus", info->form_activity.type);
EXPECT_FALSE(info->form_activity.input_missing);
@@ -88,15 +104,14 @@ TEST_F(FormJsTest, FocusMainFrame) {
// Tests that submit event correctly delivered to WebStateObserver.
TEST_F(FormJsTest, FormSubmitMainFrame) {
- web::TestWebStateObserver observer(web_state());
LoadHtml(
@"<form id='form1'>"
"<input type='password'>"
"<input type='submit' id='submit_input'/>"
"</form>");
- ASSERT_FALSE(observer.submit_document_info());
+ ASSERT_FALSE(observer_->submit_document_info());
ExecuteJavaScript(@"document.getElementById('submit_input').click();");
- web::TestSubmitDocumentInfo* info = observer.submit_document_info();
+ autofill::TestSubmitDocumentInfo* info = observer_->submit_document_info();
ASSERT_TRUE(info);
EXPECT_EQ("form1", info->form_name);
}
@@ -104,7 +119,6 @@ TEST_F(FormJsTest, FormSubmitMainFrame) {
// Tests that focus event from same-origin iframe correctly delivered to
// WebStateObserver.
TEST_F(FormJsTest, FocusSameOriginIFrame) {
- web::TestWebStateObserver observer(web_state());
LoadHtml(@"<iframe id='frame1'></iframe>");
ExecuteJavaScript(
@"document.getElementById('frame1').contentDocument.body.innerHTML = "
@@ -116,11 +130,11 @@ TEST_F(FormJsTest, FocusSameOriginIFrame) {
ExecuteJavaScript(
@"document.getElementById('frame1').contentDocument.getElementById('id1')"
@".focus()");
- web::TestWebStateObserver* block_observer = &observer;
+ autofill::TestFormActivityObserver* block_observer = observer_.get();
WaitForCondition(^bool {
return block_observer->form_activity_info() != nullptr;
});
- web::TestFormActivityInfo* info = observer.form_activity_info();
+ autofill::TestFormActivityInfo* info = observer_->form_activity_info();
ASSERT_TRUE(info);
EXPECT_EQ("focus", info->form_activity.type);
EXPECT_FALSE(info->form_activity.input_missing);
@@ -129,7 +143,6 @@ TEST_F(FormJsTest, FocusSameOriginIFrame) {
// Tests that submit event from same-origin iframe correctly delivered to
// WebStateObserver.
TEST_F(FormJsTest, FormSameOriginIFrame) {
- web::TestWebStateObserver observer(web_state());
LoadHtml(@"<iframe id='frame1'></iframe>");
ExecuteJavaScript(
@"document.getElementById('frame1').contentDocument.body.innerHTML = "
@@ -140,22 +153,21 @@ TEST_F(FormJsTest, FormSameOriginIFrame) {
ExecuteJavaScript(
@"document.getElementById('frame1').contentDocument.getElementById('"
@"submit_input').click();");
- web::TestSubmitDocumentInfo* info = observer.submit_document_info();
+ autofill::TestSubmitDocumentInfo* info = observer_->submit_document_info();
ASSERT_TRUE(info);
EXPECT_EQ("form1", info->form_name);
}
// Tests that a new form triggers form_changed event.
TEST_F(FormJsTest, AddForm) {
- web::TestWebStateObserver observer(web_state());
LoadHtml(@"<body></body>");
ExecuteJavaScript(
@"__gCrWeb.form.trackFormMutations(10);"
@"var form = document.createElement('form');"
@"document.body.appendChild(form);");
- web::TestWebStateObserver* block_observer = &observer;
- __block web::TestFormActivityInfo* info = nil;
+ autofill::TestFormActivityObserver* block_observer = observer_.get();
+ __block autofill::TestFormActivityInfo* info = nil;
WaitForCondition(^{
info = block_observer->form_activity_info();
return info != nil;
@@ -166,15 +178,14 @@ TEST_F(FormJsTest, AddForm) {
// Tests that a new input element triggers form_changed event.
TEST_F(FormJsTest, AddInput) {
- web::TestWebStateObserver observer(web_state());
LoadHtml(@"<form id='formId'/>");
ExecuteJavaScript(
@"__gCrWeb.form.trackFormMutations(10);"
@"var input = document.createElement('input');"
@"document.getElementById('formId').appendChild(input);");
- web::TestWebStateObserver* block_observer = &observer;
- __block web::TestFormActivityInfo* info = nil;
+ autofill::TestFormActivityObserver* block_observer = observer_.get();
+ __block autofill::TestFormActivityInfo* info = nil;
WaitForCondition(^{
info = block_observer->form_activity_info();
return info != nil;
@@ -185,15 +196,14 @@ TEST_F(FormJsTest, AddInput) {
// Tests that a new select element triggers form_changed event.
TEST_F(FormJsTest, AddSelect) {
- web::TestWebStateObserver observer(web_state());
LoadHtml(@"<form id='formId'/>");
ExecuteJavaScript(
@"__gCrWeb.form.trackFormMutations(10);"
@"var select = document.createElement('select');"
@"document.getElementById('formId').appendChild(select);");
- web::TestWebStateObserver* block_observer = &observer;
- __block web::TestFormActivityInfo* info = nil;
+ autofill::TestFormActivityObserver* block_observer = observer_.get();
+ __block autofill::TestFormActivityInfo* info = nil;
WaitForCondition(^{
info = block_observer->form_activity_info();
return info != nil;
@@ -204,7 +214,6 @@ TEST_F(FormJsTest, AddSelect) {
// Tests that a new option element triggers form_changed event.
TEST_F(FormJsTest, AddOption) {
- web::TestWebStateObserver observer(web_state());
LoadHtml(
@"<form>"
"<select id='select1'><option value='CA'>CA</option></select>"
@@ -214,8 +223,8 @@ TEST_F(FormJsTest, AddOption) {
@"__gCrWeb.form.trackFormMutations(10);"
@"var option = document.createElement('option');"
@"document.getElementById('select1').appendChild(option);");
- web::TestWebStateObserver* block_observer = &observer;
- __block web::TestFormActivityInfo* info = nil;
+ autofill::TestFormActivityObserver* block_observer = observer_.get();
+ __block autofill::TestFormActivityInfo* info = nil;
WaitForCondition(^{
info = block_observer->form_activity_info();
return info != nil;
diff --git a/chromium/components/autofill/ios/fill/resources/fill.js b/chromium/components/autofill/ios/form_util/resources/fill.js
index 0baae455648..fea4fc29f50 100644
--- a/chromium/components/autofill/ios/fill/resources/fill.js
+++ b/chromium/components/autofill/ios/form_util/resources/fill.js
@@ -178,14 +178,15 @@ function setInputElementAngularValue_(value, input) {
}
angular_element.val(value);
var angular_model = angular_element.data && angular_element.data('ngModel');
- if (!angular_model) {
+ var angular_scope = angular_element.scope();
+ if (!angular_model || !angular_scope) {
return;
}
angular_element.injector().invoke([
'$parse',
function(parse) {
var setter = parse(angular_model);
- setter.assign(angular_element.scope(), value);
+ setter.assign(angular_scope, value);
}
]);
}
diff --git a/chromium/components/autofill/ios/fill/resources/form.js b/chromium/components/autofill/ios/form_util/resources/form.js
index e3e717a1a8e..efb0aaf6a59 100644
--- a/chromium/components/autofill/ios/fill/resources/form.js
+++ b/chromium/components/autofill/ios/form_util/resources/form.js
@@ -64,11 +64,18 @@ __gCrWeb.form.formMutationMessageToSend = null;
__gCrWeb.form.messageToSend = null;
/**
- * The last HTML element that was focused by the user.
+ * The last HTML element that had focus.
*/
__gCrWeb.form.lastFocusedElement = null;
/**
+ * A WeakMap to track if the current value of a field was entered by user or
+ * programmatically.
+ * If the map is null, the source of changed is not track.
+ */
+ __gCrWeb.form.wasEditedByUser = null;
+
+/**
* Based on Element::isFormControlElement() (WebKit)
* @param {Element} element A DOM element.
* @return {boolean} true if the |element| is a form control element.
@@ -253,7 +260,8 @@ __gCrWeb.form.getFormIdentifier = function(form) {
return name;
}
name = form.getAttribute('id');
- if (name) {
+ if (name && name.length != 0 &&
+ form.ownerDocument.getElementById(name) === form) {
return name;
}
// A form name must be supplied, because the element will later need to be
@@ -335,6 +343,9 @@ var formActivity_ = function(evt) {
if (evt.type != 'blur') {
__gCrWeb.form.lastFocusedElement = document.activeElement;
}
+ if (['change', 'input'].includes(evt.type)) {
+ __gCrWeb.form.wasEditedByUser.set(target, evt.isTrusted);
+ }
if (target != __gCrWeb.form.lastFocusedElement) return;
var msg = {
'command': 'form.activity',
@@ -343,30 +354,17 @@ var formActivity_ = function(evt) {
'fieldIdentifier': __gCrWeb.form.getFieldIdentifier(target),
'fieldType': fieldType,
'type': evt.type,
- 'value': value
+ 'value': value,
+ 'hasUserGesture': evt.isTrusted
};
sendMessageOnNextLoop_(msg);
};
-/**
- * Focus events performed on the 'capture' phase otherwise they are often
- * not received.
- */
-document.addEventListener('focus', formActivity_, true);
-document.addEventListener('blur', formActivity_, true);
-document.addEventListener('change', formActivity_, true);
-
-/**
- * Text input is watched at the bubbling phase as this seems adequate in
- * practice and it is less obtrusive to page scripts than capture phase.
- */
-document.addEventListener('input', formActivity_, false);
-document.addEventListener('keyup', formActivity_, false);
/**
* Capture form submit actions.
*/
-document.addEventListener('submit', function(evt) {
+var submitHandler_ = function(evt) {
var action;
if (evt['defaultPrevented']) return;
action = evt.target.getAttribute('action');
@@ -375,11 +373,12 @@ document.addEventListener('submit', function(evt) {
action = document.location.href;
}
__gCrWeb.message.invokeOnHost({
- 'command': 'document.submit',
+ 'command': 'form.submit',
'formName': __gCrWeb.form.getFormIdentifier(evt.srcElement),
'href': getFullyQualifiedUrl_(action)
});
-}, false);
+};
+
/** @private
* @param {string} originalURL
@@ -408,6 +407,33 @@ var sendFormMutationMessageAfterDelay_ = function(msg, delay) {
}, delay);
};
+var attachListeners_ = function() {
+ /**
+ * Focus events performed on the 'capture' phase otherwise they are often
+ * not received.
+ * Input and change performed on the 'capture' phase as they are needed to
+ * detect if the current value is entered by the user.
+ */
+ document.addEventListener('focus', formActivity_, true);
+ document.addEventListener('blur', formActivity_, true);
+ document.addEventListener('change', formActivity_, true);
+ document.addEventListener('input', formActivity_, true);
+
+ /**
+ * Other events are watched at the bubbling phase as this seems adequate in
+ * practice and it is less obtrusive to page scripts than capture phase.
+ */
+ document.addEventListener('keyup', formActivity_, false);
+ document.addEventListener('submit',submitHandler_, false);
+};
+
+// Attach the listeners immediatly to try to catch early actions of the user.
+attachListeners_();
+
+// Initial page loading can remove the listeners. Schedule a reattach after page
+// build.
+setTimeout(attachListeners_, 1000);
+
/**
* Installs a MutationObserver to track form related changes. Waits |delay|
* milliseconds before sending a message to browser. A delay is used because
@@ -452,7 +478,8 @@ __gCrWeb.form['trackFormMutations'] = function(delay) {
'fieldIdentifier': '',
'fieldType': '',
'type': 'form_changed',
- 'value': ''
+ 'value': '',
+ 'hasUserGesture': false
};
return sendFormMutationMessageAfterDelay_(msg, delay);
}
@@ -462,6 +489,34 @@ __gCrWeb.form['trackFormMutations'] = function(delay) {
document, {childList: true, subtree: true});
};
+/**
+ * Enables or disables the tracking of input event sources.
+ */
+__gCrWeb.form['toggleTrackingUserEditedFields'] = function(track) {
+ if (track) {
+ __gCrWeb.form.wasEditedByUser =
+ __gCrWeb.form.wasEditedByUser || new WeakMap();
+ } else {
+ __gCrWeb.form.wasEditedByUser = null;
+ }
+}
+
+/**
+ * Returns whether the last |input| or |change| event on |element| was triggered
+ * by a user action (was "trusted").
+ */
+__gCrWeb.form['fieldWasEditedByUser'] = function(element) {
+ if (__gCrWeb.form.wasEditedByUser === null) {
+ // Input event sources is not tracked.
+ // Return true to preserve previous behavior.
+ return true;
+ }
+ if (!__gCrWeb.form.wasEditedByUser.has(element)) {
+ return false;
+ }
+ return __gCrWeb.form.wasEditedByUser.get(element);
+}
+
/** Flush the message queue. */
if (__gCrWeb.message) {
__gCrWeb.message.invokeQueues();
diff --git a/chromium/components/autofill/ios/form_util/test_form_activity_observer.h b/chromium/components/autofill/ios/form_util/test_form_activity_observer.h
new file mode 100644
index 00000000000..c22d9221a75
--- /dev/null
+++ b/chromium/components/autofill/ios/form_util/test_form_activity_observer.h
@@ -0,0 +1,57 @@
+// 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_IOS_FORM_UTIL_TEST_FORM_ACTIVITY_OBSERVER_H_
+#define COMPONENTS_AUTOFILL_IOS_FORM_UTIL_TEST_FORM_ACTIVITY_OBSERVER_H_
+
+#include "components/autofill/ios/form_util/form_activity_observer.h"
+#include "ios/web/public/web_state/form_activity_params.h"
+
+namespace web {
+class WebState;
+}
+
+namespace autofill {
+// Arguments passed to |DocumentSubmitted|.
+struct TestSubmitDocumentInfo {
+ web::WebState* web_state;
+ std::string form_name;
+ bool has_user_gesture;
+ bool form_in_main_frame;
+};
+
+// Arguments passed to |FormActivityRegistered|.
+struct TestFormActivityInfo {
+ web::WebState* web_state;
+ web::FormActivityParams form_activity;
+};
+
+class TestFormActivityObserver : public autofill::FormActivityObserver {
+ public:
+ explicit TestFormActivityObserver(web::WebState* web_state);
+ ~TestFormActivityObserver() override;
+
+ // Arguments passed to |DocumentSubmitted|.
+ TestSubmitDocumentInfo* submit_document_info();
+
+ // Arguments passed to |FormActivityRegistered|.
+ TestFormActivityInfo* form_activity_info();
+
+ void DidSubmitDocument(web::WebState* web_state,
+ const std::string& form_name,
+ bool has_user_gesture,
+ bool form_in_main_frame) override;
+
+ void OnFormActivity(web::WebState* web_state,
+ const web::FormActivityParams& params) override;
+
+ private:
+ web::WebState* web_state_ = nullptr;
+ std::unique_ptr<TestSubmitDocumentInfo> submit_document_info_;
+ std::unique_ptr<TestFormActivityInfo> form_activity_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestFormActivityObserver);
+};
+} // namespace autofill
+#endif // COMPONENTS_AUTOFILL_IOS_FORM_UTIL_TEST_FORM_ACTIVITY_OBSERVER_H_
diff --git a/chromium/components/autofill/ios/form_util/test_form_activity_observer.mm b/chromium/components/autofill/ios/form_util/test_form_activity_observer.mm
new file mode 100644
index 00000000000..0af0e68bf0b
--- /dev/null
+++ b/chromium/components/autofill/ios/form_util/test_form_activity_observer.mm
@@ -0,0 +1,48 @@
+// 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/ios/form_util/test_form_activity_observer.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace autofill {
+
+TestFormActivityObserver::TestFormActivityObserver(web::WebState* web_state)
+ : web_state_(web_state) {}
+TestFormActivityObserver::~TestFormActivityObserver() {}
+
+TestSubmitDocumentInfo* TestFormActivityObserver::submit_document_info() {
+ return submit_document_info_.get();
+}
+
+TestFormActivityInfo* TestFormActivityObserver::form_activity_info() {
+ return form_activity_info_.get();
+}
+
+void TestFormActivityObserver::DidSubmitDocument(web::WebState* web_state,
+ const std::string& form_name,
+ bool has_user_gesture,
+ bool form_in_main_frame) {
+ ASSERT_EQ(web_state_, web_state);
+ submit_document_info_ = std::make_unique<TestSubmitDocumentInfo>();
+ submit_document_info_->web_state = web_state;
+ submit_document_info_->form_name = form_name;
+ submit_document_info_->has_user_gesture = has_user_gesture;
+ submit_document_info_->form_in_main_frame = form_in_main_frame;
+}
+
+void TestFormActivityObserver::OnFormActivity(
+ web::WebState* web_state,
+ const web::FormActivityParams& params) {
+ ASSERT_EQ(web_state_, web_state);
+ form_activity_info_ = std::make_unique<TestFormActivityInfo>();
+ form_activity_info_->web_state = web_state;
+ form_activity_info_->form_activity = params;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/ios/form_util/test_form_activity_tab_helper.h b/chromium/components/autofill/ios/form_util/test_form_activity_tab_helper.h
new file mode 100644
index 00000000000..010ad0a1eac
--- /dev/null
+++ b/chromium/components/autofill/ios/form_util/test_form_activity_tab_helper.h
@@ -0,0 +1,37 @@
+// 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_IOS_FORM_UTIL_TEST_FORM_ACTIVITY_TAB_HELPER_H_
+#define COMPONENTS_AUTOFILL_IOS_FORM_UTIL_TEST_FORM_ACTIVITY_TAB_HELPER_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace web {
+struct FormActivityParams;
+class WebState;
+} // namespace web
+
+namespace autofill {
+
+class TestFormActivityTabHelper {
+ public:
+ explicit TestFormActivityTabHelper(web::WebState* web_state);
+ ~TestFormActivityTabHelper();
+
+ void OnFormActivity(const web::FormActivityParams& params);
+ void OnDocumentSubmitted(const std::string& form_name,
+ bool has_user_gesture,
+ bool form_in_main_frame);
+
+ private:
+ web::WebState* web_state_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(TestFormActivityTabHelper);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_IOS_FORM_UTIL_TEST_FORM_ACTIVITY_TAB_HELPER_H_
diff --git a/chromium/components/autofill/ios/form_util/test_form_activity_tab_helper.mm b/chromium/components/autofill/ios/form_util/test_form_activity_tab_helper.mm
new file mode 100644
index 00000000000..2aecd541db0
--- /dev/null
+++ b/chromium/components/autofill/ios/form_util/test_form_activity_tab_helper.mm
@@ -0,0 +1,43 @@
+// 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/ios/form_util/test_form_activity_tab_helper.h"
+
+#include "base/observer_list.h"
+#include "components/autofill/ios/form_util/form_activity_observer.h"
+#include "components/autofill/ios/form_util/form_activity_tab_helper.h"
+#include "ios/web/public/web_state/form_activity_params.h"
+#include "ios/web/public/web_state/web_state.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace autofill {
+TestFormActivityTabHelper::TestFormActivityTabHelper(web::WebState* web_state)
+ : web_state_(web_state) {}
+
+TestFormActivityTabHelper::~TestFormActivityTabHelper() {}
+
+void TestFormActivityTabHelper::OnFormActivity(
+ web::FormActivityParams const& params) {
+ autofill::FormActivityTabHelper* form_activity_tab_helper =
+ autofill::FormActivityTabHelper::GetOrCreateForWebState(web_state_);
+ for (auto& observer : form_activity_tab_helper->observers_) {
+ observer.OnFormActivity(web_state_, params);
+ }
+}
+
+void TestFormActivityTabHelper::OnDocumentSubmitted(
+ const std::string& form_name,
+ bool has_user_gesture,
+ bool form_in_main_frame) {
+ autofill::FormActivityTabHelper* form_activity_tab_helper =
+ autofill::FormActivityTabHelper::GetOrCreateForWebState(web_state_);
+ for (auto& observer : form_activity_tab_helper->observers_) {
+ observer.DidSubmitDocument(web_state_, form_name, has_user_gesture,
+ form_in_main_frame);
+ }
+}
+} // namespace autofill
diff --git a/chromium/components/autofill_strings.grdp b/chromium/components/autofill_strings.grdp
index 2de9b8c9cce..5d9cbe7ae47 100644
--- a/chromium/components/autofill_strings.grdp
+++ b/chromium/components/autofill_strings.grdp
@@ -60,6 +60,9 @@
<message name="IDS_AUTOFILL_CC_ELO" desc="Elo credit card name.">
Elo
</message>
+ <message name="IDS_AUTOFILL_CC_GOOGLE_PAY" desc="Google pay brand name">
+ Google Pay
+ </message>
<message name="IDS_AUTOFILL_CC_JCB" desc="JCB credit card name." formatter_data="android_java">
JCB
</message>
@@ -122,10 +125,6 @@
Postal code
</message>
- <message name="IDS_AUTOFILL_SETTINGS_POPUP" desc="The text displayed in the Autofill popup to direct the user to the Autofill settings UI.">
- Autofill settings
- </message>
-
<message name="IDS_AUTOFILL_MANAGE" desc="The text displayed at the bottom of the Autofill popup to allow the user to manage their Autofill preferences for addresses, credit cards, and passwords. Imperative. When the user clicks on it, opens the Autofill settings page." meaning="Manage addresses, credit cards and passwords in the user profile that can be used by Chrome Autofill.">
Manage...
</message>
@@ -142,18 +141,10 @@
Manage passwords...
</message>
- <message name="IDS_AUTOFILL_OPTIONS_CONTENT_DESCRIPTION" desc="The text verbalised by a screen reader for the button that directs the user to the Autofill settings UI. This string is not displayed.">
- settings
- </message>
-
<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
</message>
- <message name="IDS_AUTOFILL_PASSWORD_FIELD_SUGGESTIONS_TITLE" desc="Text shown as the title of the suggestion drop down when a password field is clicked.">
- Use password for:
- </message>
-
<if expr="not use_titlecase">
<message name="IDS_AUTOFILL_SHOW_ALL_SAVED_FALLBACK" desc="The text shown as an option in the suggestion drop down when a password field is clicked">
Show all saved passwords
@@ -164,9 +155,6 @@
Show All Saved Passwords
</message>
</if>
- <message name="IDS_AUTOFILL_GENERATE_PASSWORD_FALLBACK" desc="The text shown as an option in the suggestion drop down when a password field is clicked">
- Generate a strong password...
- </message>
<!-- Autofill Credit Card Assisted Filling Infobar -->
<if expr="is_android">
@@ -237,40 +225,47 @@
</message>
</else>
</if>
+ <if expr="is_linux and not chromeos">
+ <then>
+ <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>
+ </then>
+ <else>
+ <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 and also saved locally, according to April 2018 UI guidelines. The prompt can be either a bubble or an infobar.">
+ To pay faster next time, save your card, name, and billing address to your Google Account and to this device.
+ </message>
+ </else>
+ </if>
+ <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_CARDHOLDER_NAME" desc="The label text for the cardholder name textfield.">
+ Cardholder name
+ </message>
+ <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_CARDHOLDER_NAME_TOOLTIP" desc="The tooltip text for the cardholder name textfield.">
+ This name is from your Google Account.
+ </message>
+
+ <!-- Autofill Local card migration bubble or prompt -->
+ <if expr="not is_ios and not is_android">
+ <message name="IDS_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_TITLE" desc="Text to show in the Autofill local card migration intermediate bubble.">
+ Do you want to have all your cards in one place?
+ </message>
+ <message name="IDS_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_BUTTON_LABEL" desc="Text to show in the Autofill local card migration intermediate bubble button.">
+ Continue
+ </message>
+ </if>
<!-- Autofill credit card suggestion popup -->
<message name="IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_ABBR" desc="Abbreviated label for credit card expiration date. [CHAR-LIMIT=32]">
Exp: <ph name="EXPIRATION_MONTH">$1<ex>06</ex></ph>/<ph name="EXPIRATION_YEAR">$2<ex>17</ex></ph>
</message>
+ <message name="IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_ABBR_V2" desc="Abbreviated label for credit card expiration date. This will be presented as either YY/MM or MM/YY depending on how credit card expiration dates are more commonly presented in the locale. [CHAR-LIMIT=32]" meaning="Label for credit card expiration date for the save card dialog.">
+ <ph name="EXPIRATION_MONTH">$1<ex>06</ex></ph>/<ph name="EXPIRATION_YEAR">$2<ex>17</ex></ph>
+ </message>
<message name="IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_LABEL_AND_ABBR" desc="Text displayed in the Autofill Credit Card popup before the credit card expiration date and the abbreviated expiration date.">
, exp <ph name="EXPIRATION_DATE_ABBR">$1<ex>06/17</ex></ph>
</message>
- <message name="IDS_AUTOFILL_CREDIT_CARD_EXP_AND_LAST_USED_DATE" desc="Text displayed in the Autofill Credit Card popup to indicate the credit card expiration date and the date when this card was last used.">
- Exp: <ph name="EXPIRATION_DATE_ABBR">$1<ex>06/17</ex></ph>, last used <ph name="LAST_USED_DATE_NO_DETAIL">$2<ex>Jan 10</ex></ph>
- </message>
-
- <message name="IDS_AUTOFILL_CREDIT_CARD_LAST_USED_DATE" desc="Text displayed in the Autofill Credit Card popup to indicate the date when this card was last used.">
- Last used <ph name="LAST_USED_MONTH">$1<ex>Jan 10</ex></ph>
- </message>
-
- <message name="IDS_AUTOFILL_CREDIT_CARD_EXP_AND_ADDED_DATE" desc="Text displayed in the Autofill Credit Card popup to indicate the credit card expiration date and the date when this card was added to autofill.">
- Exp: <ph name="EXPIRATION_DATE_ABBR">$1<ex>06/17</ex></ph>, added <ph name="ADDED_TO_AUTOFILL_MONTH">$2<ex>Jan 10</ex></ph>
- </message>
-
- <message name="IDS_AUTOFILL_CREDIT_CARD_ADDED_DATE" desc="Text displayed in the Autofill Credit Card popup to indicate the date when this card was added to autofill.">
- Added <ph name="ADDED_TO_AUTOFILL_MONTH">$1<ex>Jan 10</ex></ph>
- </message>
-
- <message name="IDS_AUTOFILL_CREDIT_CARD_EXP_AND_LAST_USED_YEAR_AGO" desc="Text displayed in the Autofill Credit Card popup to indicate the credit card expiration date and this card was used in autofill more than a year ago.">
- Exp: <ph name="EXPIRATION_DATE_ABBR">$1<ex>06/17</ex></ph>, last used over a year ago
- </message>
-
- <message name="IDS_AUTOFILL_CREDIT_CARD_LAST_USED_YEAR_AGO" desc="Text displayed in the Autofill Credit Card popup to indicate this card was used in autofill more than a year ago.">
- Last used over a year ago
- </message>
-
<!-- Autofill credit card unmask prompt -->
<message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN_CVC" desc="Error message that encourages the user to try to re-enter their credit card CVC after a previous failed attempt." formatter_data="android_java">
Check your CVC and try again
@@ -287,16 +282,9 @@
<message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN_EXPIRATION_YEAR" desc="Error message that encourages the user to try to re-enter their credit card expiration year after a previous failed attempt." formatter_data="android_java">
Check your expiration year and try again
</message>
- <if expr="_google_chrome">
- <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_PERMANENT" desc="Error message to show when a credit card cannot be verified and the user isn't allowed to retry.">
- Chrome was unable to confirm your card at this time. Please try again later.
- </message>
- </if>
- <if expr="not _google_chrome">
- <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_PERMANENT" desc="Error message to show when a credit card cannot be verified and the user isn't allowed to retry.">
- Chromium was unable to confirm your card at this time. Please try again later.
- </message>
- </if>
+ <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_PERMANENT" desc="Error message to show when a credit card cannot be verified and the user isn't allowed to retry.">
+ This card can't be verified right now
+ </message>
<message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_NETWORK" desc="Error message to show when a credit card cannot be verified because Wallet servers can't be reached.">
There was a problem confirming your card. Check your internet connection and try again.
</message>
@@ -371,7 +359,7 @@
Year
</message>
<message name="IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_IN_PROGRESS" desc="Message displayed while credit card is being verified." formatter_data="android_java">
- Confirming card
+ Confirming card...
</message>
<message name="IDS_AUTOFILL_CARD_UNMASK_VERIFICATION_SUCCESS" desc="Message displayed after successful verification of credit card CVC." formatter_data="android_java">
Your card is confirmed
@@ -383,7 +371,7 @@
/
</message>
<message name="IDS_AUTOFILL_CARD_UNMASK_NEW_CARD_LINK" desc="Text for link that prompts user to update their credit card after it may have been re-issued." formatter_data="android_java">
- New card?
+ Update card
</message>
<message name="IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC" desc="The placeholder/label text for credit card verification code in the requestAutocomplete dialog.">
CVC
diff --git a/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_NEW_CARD_LINK.png.sha1 b/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_NEW_CARD_LINK.png.sha1
new file mode 100644
index 00000000000..a4e68358714
--- /dev/null
+++ b/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_NEW_CARD_LINK.png.sha1
@@ -0,0 +1 @@
+01aaf2a4fd2262081fc889b4178b5b9118f1dede \ No newline at end of file
diff --git a/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_PERMANENT.png.sha1 b/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_PERMANENT.png.sha1
new file mode 100644
index 00000000000..210e108e4be
--- /dev/null
+++ b/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_PERMANENT.png.sha1
@@ -0,0 +1 @@
+b9b83044a099d77931c48a77796d0ba55e8e7ed2 \ No newline at end of file
diff --git a/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN_CVC.png.sha1 b/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN_CVC.png.sha1
new file mode 100644
index 00000000000..3fb8a990291
--- /dev/null
+++ b/chromium/components/autofill_strings_grdp/IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN_CVC.png.sha1
@@ -0,0 +1 @@
+e2c40932baf9f4d4e1d27c28038254a0cfd04dad \ No newline at end of file
diff --git a/chromium/components/blacklist/OWNERS b/chromium/components/blacklist/OWNERS
new file mode 100644
index 00000000000..75baba8b67f
--- /dev/null
+++ b/chromium/components/blacklist/OWNERS
@@ -0,0 +1,3 @@
+file://components/data_reduction_proxy/OWNERS
+
+# COMPONENT: Blink>Previews \ No newline at end of file
diff --git a/chromium/components/blacklist/README.md b/chromium/components/blacklist/README.md
new file mode 100644
index 00000000000..bf4656596ff
--- /dev/null
+++ b/chromium/components/blacklist/README.md
@@ -0,0 +1,58 @@
+#Blacklist component#
+
+The goal of the blacklist component is to provide various blacklists that allow
+different policies for features to consume. Currently, the only implemented
+blacklist is the opt out blacklist.
+
+##Opt out blacklist##
+The opt out blacklist makes decisions based on user history actions. Each user
+action is evaluated based on action type, time of the evaluation, host name of
+the action (can be any string representation), and previous action history.
+
+###Expected feature behavior###
+When a feature action is allowed, the feature may perform said action. After
+performing the action, the user interaction should be determined to be an opt
+out (the user did not like the action) or a non-opt out (the user was not
+opposed to the action). The action, type, host name, and whether it was an opt
+out should be reported back to the blacklist to build user action history.
+
+For example, a feature may wish to show an InfoBar (or different types of
+InfoBars) displaying information about the page a user is on. After querying the
+opt out blacklist for action eligibility, an InfoBar may be allowed to be shown.
+If it is shown, the user may interact with it in a number of ways. If the user
+dismisses the InfoBar, that could be considered an opt out; if the user does
+not dismiss the InfoBar that could be considered a non-opt out. All of the
+information related to that action should be reported to the blacklist.
+
+###Supported evaluation policies###
+In general, policies follow a specific form: the most recent _n_ actions are
+evaluated, and if _t_ or more of them are opt outs the action will not be
+allowed for a specified duration, _d_. For each policy, the feature specifies
+whether the policy is enabled, and, if it is, the feature specifies _n_
+(history), _t_ (threshold), and _d_ (duration) for each policy.
+
+* Session policy: This policy only applies across all types and host names, but
+is limited to actions that happened within the current session. The beginning of
+a session is defined as the creation of the blacklist object or when the
+blacklist is cleared (see below for details on clearing the blacklist).
+
+* Persistent policy: This policy applies across all sessions, types and host
+names.
+
+* Host policy: This policy applies across all session and types, but keeps a
+separate history for each host names. This rule allows specific host names to be
+prevented from having an action performed for the specific user. When this
+policy is enabled, the feature specifies a number of hosts that are stored in
+memory (to limit memory footprint, query time, etc.)
+
+* Type policy: This policy applies across all session and host names, but keeps
+a separate history for each type. This rule allows specific types to be
+prevented from having an action performed for the specific user. The feature
+specifies a set of enabled types and versions for each type. This allows
+removing past versions of types to be removed from the backing store.
+
+###Clearing the blacklist###
+Because many actions should be cleared when user clears history, the opt out
+blacklist allows clearing history in certain time ranges. All entries are
+cleared for the specified time range, and the data in memory is repopulated
+from the backing store.
diff --git a/chromium/components/blacklist/opt_out_blacklist/BUILD.gn b/chromium/components/blacklist/opt_out_blacklist/BUILD.gn
new file mode 100644
index 00000000000..05ab868e866
--- /dev/null
+++ b/chromium/components/blacklist/opt_out_blacklist/BUILD.gn
@@ -0,0 +1,35 @@
+# 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.
+
+static_library("opt_out_blacklist") {
+ sources = [
+ "opt_out_blacklist.cc",
+ "opt_out_blacklist.h",
+ "opt_out_blacklist_data.cc",
+ "opt_out_blacklist_data.h",
+ "opt_out_blacklist_item.cc",
+ "opt_out_blacklist_item.h",
+ "opt_out_store.h",
+ ]
+
+ deps = [
+ "//base",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "opt_out_blacklist_item_unittest.cc",
+ "opt_out_blacklist_unittest.cc",
+ ]
+
+ deps = [
+ ":opt_out_blacklist",
+ "//base",
+ "//base/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist.cc b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist.cc
new file mode 100644
index 00000000000..ba4a43c2041
--- /dev/null
+++ b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist.cc
@@ -0,0 +1,222 @@
+// 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/blacklist/opt_out_blacklist/opt_out_blacklist.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram.h"
+#include "base/optional.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/clock.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_delegate.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_item.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_store.h"
+
+namespace blacklist {
+
+OptOutBlacklist::OptOutBlacklist(std::unique_ptr<OptOutStore> opt_out_store,
+ base::Clock* clock,
+ OptOutBlacklistDelegate* blacklist_delegate)
+ : loaded_(false),
+ opt_out_store_(std::move(opt_out_store)),
+ clock_(clock),
+ blacklist_delegate_(blacklist_delegate),
+ weak_factory_(this) {
+ DCHECK(clock_);
+ DCHECK(blacklist_delegate_);
+}
+
+OptOutBlacklist::~OptOutBlacklist() = default;
+
+void OptOutBlacklist::Init() {
+ DCHECK(!loaded_);
+ DCHECK(!blacklist_data_);
+ base::TimeDelta duration;
+ size_t history = 0;
+ int threshold = 0;
+
+ std::unique_ptr<BlacklistData::Policy> session_policy;
+ if (ShouldUseSessionPolicy(&duration, &history, &threshold)) {
+ session_policy =
+ std::make_unique<BlacklistData::Policy>(duration, history, threshold);
+ }
+
+ std::unique_ptr<BlacklistData::Policy> persistent_policy;
+ if (ShouldUsePersistentPolicy(&duration, &history, &threshold)) {
+ persistent_policy =
+ std::make_unique<BlacklistData::Policy>(duration, history, threshold);
+ }
+
+ size_t max_hosts = 0;
+ std::unique_ptr<BlacklistData::Policy> host_policy;
+ if (ShouldUseHostPolicy(&duration, &history, &threshold, &max_hosts)) {
+ host_policy =
+ std::make_unique<BlacklistData::Policy>(duration, history, threshold);
+ }
+
+ std::unique_ptr<BlacklistData::Policy> type_policy;
+ if (ShouldUseTypePolicy(&duration, &history, &threshold)) {
+ type_policy =
+ std::make_unique<BlacklistData::Policy>(duration, history, threshold);
+ }
+
+ auto blacklist_data = std::make_unique<BlacklistData>(
+ std::move(session_policy), std::move(persistent_policy),
+ std::move(host_policy), std::move(type_policy), max_hosts,
+ GetAllowedTypes());
+
+ if (opt_out_store_) {
+ opt_out_store_->LoadBlackList(
+ std::move(blacklist_data),
+ base::BindOnce(&OptOutBlacklist::LoadBlackListDone,
+ weak_factory_.GetWeakPtr()));
+ } else {
+ LoadBlackListDone(std::move(blacklist_data));
+ }
+}
+
+base::Time OptOutBlacklist::AddEntry(const std::string& host_name,
+ bool opt_out,
+ int type) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ base::Time now = clock_->Now();
+
+ // If the |blacklist_data| has been loaded from |opt_out_store_|, synchronous
+ // operations will be accurate. Otherwise, queue the task to run
+ // asynchronously.
+ if (loaded_) {
+ AddEntrySync(host_name, opt_out, type, now);
+ } else {
+ QueuePendingTask(base::BindOnce(&OptOutBlacklist::AddEntrySync,
+ base::Unretained(this), host_name, opt_out,
+ type, now));
+ }
+
+ return now;
+}
+
+void OptOutBlacklist::AddEntrySync(const std::string& host_name,
+ bool opt_out,
+ int type,
+ base::Time time) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(loaded_);
+
+ bool host_was_blacklisted =
+ blacklist_data_->IsHostBlacklisted(host_name, time);
+ bool user_was_blacklisted = blacklist_data_->IsUserOptedOutInGeneral(time);
+ blacklist_data_->AddEntry(host_name, opt_out, type, time, false);
+
+ if (!host_was_blacklisted &&
+ blacklist_data_->IsHostBlacklisted(host_name, time)) {
+ // Notify |blacklist_delegate_| about a new blacklisted host.
+ blacklist_delegate_->OnNewBlacklistedHost(host_name, time);
+ }
+
+ if (user_was_blacklisted != blacklist_data_->IsUserOptedOutInGeneral(time)) {
+ // Notify |blacklist_delegate_| about a new blacklisted host.
+ blacklist_delegate_->OnUserBlacklistedStatusChange(
+ blacklist_data_->IsUserOptedOutInGeneral(time));
+ }
+
+ if (!opt_out_store_)
+ return;
+ opt_out_store_->AddEntry(opt_out, host_name, type, time);
+}
+
+BlacklistReason OptOutBlacklist::IsLoadedAndAllowed(
+ const std::string& host_name,
+ int type,
+ bool ignore_long_term_black_list_rules,
+ std::vector<BlacklistReason>* passed_reasons) const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (!loaded_)
+ return BlacklistReason::kBlacklistNotLoaded;
+ passed_reasons->push_back(BlacklistReason::kBlacklistNotLoaded);
+
+ return blacklist_data_->IsAllowed(host_name, type,
+ ignore_long_term_black_list_rules,
+ clock_->Now(), passed_reasons);
+}
+
+void OptOutBlacklist::ClearBlackList(base::Time begin_time,
+ base::Time end_time) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_LE(begin_time, end_time);
+ // If the |blacklist_data| has been loaded from |opt_out_store_|,
+ // synchronous operations will be accurate. Otherwise, queue the task to run
+ // asynchronously.
+ if (loaded_) {
+ ClearBlackListSync(begin_time, end_time);
+ } else {
+ QueuePendingTask(base::BindOnce(&OptOutBlacklist::ClearBlackListSync,
+ base::Unretained(this), begin_time,
+ end_time));
+ }
+}
+
+void OptOutBlacklist::ClearBlackListSync(base::Time begin_time,
+ base::Time end_time) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(loaded_);
+ DCHECK_LE(begin_time, end_time);
+
+ // Clear the in-memory rules entirely.
+ blacklist_data_->ClearData();
+ loaded_ = false;
+
+ // Notify |blacklist_delegate_| that the blacklist is cleared.
+ blacklist_delegate_->OnBlacklistCleared(clock_->Now());
+
+ // Delete relevant entries and reload the blacklist into memory.
+ if (opt_out_store_) {
+ opt_out_store_->ClearBlackList(begin_time, end_time);
+ opt_out_store_->LoadBlackList(
+ std::move(blacklist_data_),
+ base::BindOnce(&OptOutBlacklist::LoadBlackListDone,
+ weak_factory_.GetWeakPtr()));
+ } else {
+ LoadBlackListDone(std::move(blacklist_data_));
+ }
+}
+
+void OptOutBlacklist::QueuePendingTask(base::OnceClosure callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!loaded_);
+ DCHECK(!callback.is_null());
+ pending_callbacks_.push(std::move(callback));
+}
+
+void OptOutBlacklist::LoadBlackListDone(
+ std::unique_ptr<BlacklistData> blacklist_data) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(blacklist_data);
+ DCHECK(!loaded_);
+ DCHECK(!blacklist_data_);
+ loaded_ = true;
+ blacklist_data_ = std::move(blacklist_data);
+
+ // Notify |blacklist_delegate_| on current user blacklisted status.
+ blacklist_delegate_->OnUserBlacklistedStatusChange(
+ blacklist_data_->IsUserOptedOutInGeneral(clock_->Now()));
+
+ // Notify the |blacklist_delegate_| on historical blacklisted hosts.
+ for (const auto& entry : blacklist_data_->black_list_item_host_map()) {
+ if (blacklist_data_->IsHostBlacklisted(entry.first, clock_->Now())) {
+ blacklist_delegate_->OnNewBlacklistedHost(
+ entry.first, entry.second.most_recent_opt_out_time().value());
+ }
+ }
+
+ // Run all pending tasks. |loaded_| may change if ClearBlackList is queued.
+ while (pending_callbacks_.size() > 0 && loaded_) {
+ std::move(pending_callbacks_.front()).Run();
+ pending_callbacks_.pop();
+ }
+}
+
+} // namespace blacklist
diff --git a/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist.h b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist.h
new file mode 100644
index 00000000000..4989b3d9d7e
--- /dev/null
+++ b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist.h
@@ -0,0 +1,183 @@
+// 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_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_BLACKLIST_H_
+#define COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_BLACKLIST_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/containers/queue.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h"
+
+namespace base {
+class Clock;
+}
+
+namespace blacklist {
+
+class BlacklistData;
+class OptOutBlacklistDelegate;
+class OptOutStore;
+
+class OptOutBlacklist {
+ public:
+ // |opt_out_store| is the backing store to retrieve and store blacklist
+ // information, and can be null. When |opt_out_store| is null, the in-memory
+ // data will be immediately loaded to empty. If |opt_out_store| is non-null,
+ // it will be used to load the in-memory map asynchronously.
+ // |blacklist_delegate| is a single object listening for blacklist events, and
+ // it is guaranteed to outlive the life time of |this|.
+ OptOutBlacklist(std::unique_ptr<OptOutStore> opt_out_store,
+ base::Clock* clock,
+ OptOutBlacklistDelegate* blacklist_delegate);
+ virtual ~OptOutBlacklist();
+
+ // Creates the BlacklistData that backs the blacklist.
+ void Init();
+
+ // Asynchronously deletes all entries in the in-memory blacklist. Informs
+ // the backing store to delete entries between |begin_time| and |end_time|,
+ // and reloads entries into memory from the backing store. If the embedder
+ // passed in a null store, resets all history in the in-memory blacklist.
+ void ClearBlackList(base::Time begin_time, base::Time end_time);
+
+ // Asynchronously adds a new navigation to to the in-memory blacklist and
+ // backing store. |opt_out| is whether the user opted out of the action. If
+ // the in memory map has reached the max number of hosts allowed, and
+ // |host_name| is a new host, a host will be evicted based on recency of the
+ // hosts most recent opt out. It returns the time used for recording the
+ // moment when the navigation is added for logging.
+ base::Time AddEntry(const std::string& host_name, bool opt_out, int type);
+
+ // Synchronously determines if the action should be allowed for |host_name|
+ // and |type|. Returns the reason the blacklist disallowed the action, or
+ // kAllowed if the action is allowed. Record checked reasons in
+ // |passed_reasons|. |ignore_long_term_black_list_rules| will cause session,
+ // type, and host rules, but the session rule will still be queried.
+ BlacklistReason IsLoadedAndAllowed(
+ const std::string& host_name,
+ int type,
+ bool ignore_long_term_black_list_rules,
+ std::vector<BlacklistReason>* passed_reasons) const;
+
+ protected:
+ // Whether the session rule should be enabled. |duration| specifies how long a
+ // user remains blacklisted. |history| specifies how many entries should be
+ // evaluated; |threshold| specifies how many opt outs would cause
+ // blacklisting. I.e., the most recent |history| are looked at and if
+ // |threshold| (or more) of them are opt outs, the user is considered
+ // blacklisted unless the most recent opt out was longer than |duration| ago.
+ // This rule only considers entries within this session (it does not use the
+ // data that was persisted in previous sessions). When the blacklist is
+ // cleared, this rule is reset as if it were a new session. Queried in Init().
+ virtual bool ShouldUseSessionPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const = 0;
+
+ // Whether the persistent rule should be enabled. |duration| specifies how
+ // long a user remains blacklisted. |history| specifies how many entries
+ // should be evaluated; |threshold| specifies how many opt outs would cause
+ // blacklisting. I.e., the most recent |history| are looked at and if
+ // |threshold| (or more) of them are opt outs, the user is considered
+ // blacklisted unless the most recent opt out was longer than |duration| ago.
+ // Queried in Init().
+ virtual bool ShouldUsePersistentPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const = 0;
+
+ // Whether the host rule should be enabled. |duration| specifies how long a
+ // host remains blacklisted. |history| specifies how many entries should be
+ // evaluated per host; |threshold| specifies how many opt outs would cause
+ // blacklisting. I.e., the most recent |history| entries per host are looked
+ // at and if |threshold| (or more) of them are opt outs, the host is
+ // considered blacklisted unless the most recent opt out was longer than
+ // |duration| ago. |max_hosts| will limit the number of hosts stored in this
+ // class when non-zero.
+ // Queried in Init().
+ virtual bool ShouldUseHostPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold,
+ size_t* max_hosts) const = 0;
+
+ // Whether the type rule should be enabled. |duration| specifies how long a
+ // type remains blacklisted. |history| specifies how many entries should be
+ // evaluated per type; |threshold| specifies how many opt outs would cause
+ // blacklisting.
+ // I.e., the most recent |history| entries per type are looked at and if
+ // |threshold| (or more) of them are opt outs, the type is considered
+ // blacklisted unless the most recent opt out was longer than |duration| ago.
+ // Queried in Init().
+ virtual bool ShouldUseTypePolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const = 0;
+
+ // The allowed types and what version they are. Should be empty unless the
+ // caller will not be using the blacklist in the session. It is used to remove
+ // stale entries from the database and to DCHECK that other methods are not
+ // using disallowed types. Queried in Init().
+ virtual BlacklistData::AllowedTypesAndVersions GetAllowedTypes() const = 0;
+
+ private:
+ // Synchronous version of AddEntry method. |time| is the time
+ // stamp of when the navigation was determined to be an opt-out or non-opt
+ // out.
+ void AddEntrySync(const std::string& host_name,
+ bool opt_out,
+ int type,
+ base::Time time);
+
+ // Synchronous version of ClearBlackList method.
+ void ClearBlackListSync(base::Time begin_time, base::Time end_time);
+
+ // Callback passed to the backing store when loading black list information.
+ // Takes ownership of |blacklist_data|.
+ void LoadBlackListDone(std::unique_ptr<BlacklistData> blacklist_data);
+
+ // Called while waiting for the blacklist to be loaded from the backing
+ // store.
+ // Enqueues a task to run when when loading blacklist information has
+ // completed. Maintains the order that tasks were called in.
+ void QueuePendingTask(base::OnceClosure callback);
+
+ // An in-memory representation of the various rules of the blacklist. This is
+ // null while reading from the backing store.
+ std::unique_ptr<BlacklistData> blacklist_data_;
+
+ // Whether the blacklist is done being loaded from the backing store.
+ bool loaded_;
+
+ // The backing store of the blacklist information.
+ std::unique_ptr<OptOutStore> opt_out_store_;
+
+ // Callbacks to be run after loading information from the backing store has
+ // completed.
+ base::queue<base::OnceClosure> pending_callbacks_;
+
+ base::Clock* clock_;
+
+ // The delegate listening to this blacklist. |blacklist_delegate_| lifetime is
+ // guaranteed to outlive |this|.
+ OptOutBlacklistDelegate* blacklist_delegate_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ base::WeakPtrFactory<OptOutBlacklist> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(OptOutBlacklist);
+};
+
+} // namespace blacklist
+
+#endif // COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_BLACKLIST_H_
diff --git a/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_data.cc b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_data.cc
new file mode 100644
index 00000000000..8b519016629
--- /dev/null
+++ b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_data.cc
@@ -0,0 +1,176 @@
+// 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/blacklist/opt_out_blacklist/opt_out_blacklist_data.h"
+
+#include "base/memory/ptr_util.h"
+
+namespace blacklist {
+
+BlacklistData::BlacklistData(std::unique_ptr<Policy> session_policy,
+ std::unique_ptr<Policy> persistent_policy,
+ std::unique_ptr<Policy> host_policy,
+ std::unique_ptr<Policy> type_policy,
+ size_t max_hosts,
+ AllowedTypesAndVersions allowed_types)
+ : session_policy_(std::move(session_policy)),
+ persistent_policy_(std::move(persistent_policy)),
+ host_policy_(std::move(host_policy)),
+ max_hosts_(max_hosts),
+ type_policy_(std::move(type_policy)),
+ allowed_types_(std::move(allowed_types)) {
+ DCHECK_GE(100u, max_hosts);
+}
+BlacklistData::~BlacklistData() {}
+
+void BlacklistData::ClearData() {
+ session_black_list_item_.reset();
+ persistent_black_list_item_.reset();
+ black_list_item_host_map_.clear();
+ black_list_item_type_map_.clear();
+}
+
+void BlacklistData::AddEntry(const std::string& host_name,
+ bool opt_out,
+ int type,
+ base::Time time,
+ bool is_from_persistent_storage) {
+ // Add to the session based rule if it is enabled.
+ if (session_policy_ && !is_from_persistent_storage) {
+ if (!session_black_list_item_) {
+ session_black_list_item_ = std::make_unique<OptOutBlacklistItem>(
+ session_policy_->history, session_policy_->threshold,
+ session_policy_->duration);
+ }
+ session_black_list_item_->AddEntry(opt_out, time);
+ }
+
+ // Add to the persistent rule if it is enabled.
+ if (persistent_policy_) {
+ if (!persistent_black_list_item_) {
+ persistent_black_list_item_ = std::make_unique<OptOutBlacklistItem>(
+ persistent_policy_->history, persistent_policy_->threshold,
+ persistent_policy_->duration);
+ }
+ persistent_black_list_item_->AddEntry(opt_out, time);
+ }
+
+ // Add to the host rule if it is enabled. Remove hosts if there are more than
+ // |max_hosts_| in the map.
+ if (host_policy_) {
+ auto item = black_list_item_host_map_.find(host_name);
+ if (item == black_list_item_host_map_.end()) {
+ auto value = black_list_item_host_map_.emplace(
+ std::piecewise_construct, std::forward_as_tuple(host_name),
+ std::forward_as_tuple(host_policy_->history, host_policy_->threshold,
+ host_policy_->duration));
+ DCHECK(value.second);
+ item = value.first;
+ }
+ item->second.AddEntry(opt_out, time);
+ if (max_hosts_ > 0 && black_list_item_host_map_.size() > max_hosts_)
+ EvictOldestHost();
+ }
+
+ // Only allowed types should be recorded.
+ DCHECK(allowed_types_.find(type) != allowed_types_.end());
+
+ if (type_policy_) {
+ auto item = black_list_item_type_map_.find(type);
+ if (item == black_list_item_type_map_.end()) {
+ auto value = black_list_item_type_map_.emplace(
+ std::piecewise_construct, std::forward_as_tuple(type),
+ std::forward_as_tuple(type_policy_->history, type_policy_->threshold,
+ type_policy_->duration));
+ DCHECK(value.second);
+ item = value.first;
+ }
+ item->second.AddEntry(opt_out, time);
+ }
+}
+
+BlacklistReason BlacklistData::IsAllowed(
+ const std::string& host_name,
+ int type,
+ bool ignore_long_term_black_list_rules,
+ base::Time time,
+ std::vector<BlacklistReason>* passed_reasons) const {
+ // Check the session rule.
+ if (session_policy_) {
+ if (session_black_list_item_ &&
+ session_black_list_item_->IsBlackListed(time)) {
+ return BlacklistReason::kUserOptedOutInSession;
+ }
+ passed_reasons->push_back(BlacklistReason::kUserOptedOutInSession);
+ }
+
+ // Check whether the persistent rules should be checked this time.
+ if (ignore_long_term_black_list_rules)
+ return BlacklistReason::kAllowed;
+
+ // Check the persistent rule.
+ if (persistent_policy_) {
+ if (IsUserOptedOutInGeneral(time)) {
+ return BlacklistReason::kUserOptedOutInGeneral;
+ }
+ passed_reasons->push_back(BlacklistReason::kUserOptedOutInGeneral);
+ }
+
+ // Check the host rule.
+ if (host_policy_) {
+ if (IsHostBlacklisted(host_name, time))
+ return BlacklistReason::kUserOptedOutOfHost;
+ passed_reasons->push_back(BlacklistReason::kUserOptedOutOfHost);
+ }
+
+ // Only allowed types should be recorded.
+ DCHECK(allowed_types_.find(type) != allowed_types_.end());
+
+ // Check the type rule.
+ if (type_policy_) {
+ auto item = black_list_item_type_map_.find(type);
+ if (item != black_list_item_type_map_.end() &&
+ item->second.IsBlackListed(time)) {
+ return BlacklistReason::kUserOptedOutOfType;
+ }
+ passed_reasons->push_back(BlacklistReason::kUserOptedOutOfType);
+ }
+
+ return BlacklistReason::kAllowed;
+}
+
+void BlacklistData::EvictOldestHost() {
+ DCHECK_LT(max_hosts_, black_list_item_host_map_.size());
+ base::Optional<base::Time> oldest_opt_out;
+ std::string key_to_delete;
+ for (auto& item : black_list_item_host_map_) {
+ base::Optional<base::Time> most_recent_opt_out =
+ item.second.most_recent_opt_out_time();
+ if (!most_recent_opt_out) {
+ // If there is no opt out time, this is a good choice to evict.
+ key_to_delete = item.first;
+ break;
+ }
+ if (!oldest_opt_out ||
+ most_recent_opt_out.value() < oldest_opt_out.value()) {
+ oldest_opt_out = most_recent_opt_out.value();
+ key_to_delete = item.first;
+ }
+ }
+ black_list_item_host_map_.erase(key_to_delete);
+}
+
+bool BlacklistData::IsHostBlacklisted(const std::string& host_name,
+ base::Time time) const {
+ auto item = black_list_item_host_map_.find(host_name);
+ return item != black_list_item_host_map_.end() &&
+ item->second.IsBlackListed(time);
+}
+
+bool BlacklistData::IsUserOptedOutInGeneral(base::Time time) const {
+ return persistent_black_list_item_ &&
+ persistent_black_list_item_->IsBlackListed(time);
+}
+
+} // namespace blacklist
diff --git a/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h
new file mode 100644
index 00000000000..5740d793c4f
--- /dev/null
+++ b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h
@@ -0,0 +1,176 @@
+// 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_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_BLACKLIST_DATA_H_
+#define COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_BLACKLIST_DATA_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_item.h"
+
+namespace blacklist {
+
+// The various reasons the Blacklist may tell that the user is blacklisted.
+// This should remain synchronized with enums.xml
+enum class BlacklistReason {
+ // The blacklist may not be loaded very early in the session or when the user
+ // has cleared the blacklist history (usually by clearing their browsing
+ // history).
+ kBlacklistNotLoaded = 0,
+ kUserOptedOutInSession = 1,
+ kUserOptedOutInGeneral = 2,
+ kUserOptedOutOfHost = 3,
+ kUserOptedOutOfType = 4,
+ kAllowed = 5,
+ kMaxValue = kAllowed,
+
+};
+
+// This class describes all of the data used to determine whether an action is
+// allowed based on four possible rules: Session: if the user has opted out
+// of j of the last k entries this session, the action will be blacklisted for a
+// set duration. Persistent: if the user has opted out of j of the last k
+// entries, the action will be blacklisted for a set duration. Host: if the user
+// has opted out of threshold of the last history entries for a specific host,
+// the action will be blacklisted for a set duration. Type: if the user has
+// opted out of j of the last k entries for a specific type, the action will be
+// blacklisted for a set duration. This is the in-memory version of the black
+// list policy. This object is moved from the embedder thread to a background
+// thread, It is not safe to access concurrently on two threads.
+class BlacklistData {
+ public:
+ // A struct describing the general blacklisting pattern used by all of the
+ // blacklisting rules.
+ // The most recent |history| entries are looked at and if |threshold| (or
+ // more) of them are opt outs, new actions are considered blacklisted unless
+ // the most recent opt out was longer than |duration| ago.
+ struct Policy {
+ Policy(base::TimeDelta duration, size_t history, int threshold)
+ : duration(duration), history(history), threshold(threshold) {}
+
+ ~Policy() = default;
+
+ // Specifies how long the blacklisting rule lasts after the most recent opt
+ // out.
+ const base::TimeDelta duration;
+ // Amount of entries evaluated for the rule.
+ const size_t history;
+ // The number of opt outs that will trigger blacklisting for the rule.
+ const int threshold;
+ };
+
+ // A map of types that are allowed to be used in the blacklist as well as the
+ // version that those types are in. Versioning allows removals from persistent
+ // memory at session start.
+ using AllowedTypesAndVersions = std::map<int, int>;
+
+ // |session_policy| if non-null, is the policy that is not persisted across
+ // sessions and is not specific to host or type. |persistent_policy| if
+ // non-null, is the policy that is persisted across sessions and is not
+ // specific to host or type. |host_policy| if non-null, is the policy that is
+ // persisted across sessions applies at the per-host level. |host_policy| if
+ // non-null, is the policy that is persisted across sessions and applies at
+ // the per-type level. |max_hosts| is the maximum number of hosts stored in
+ // memory. |allowed_types| contains the action types that are allowed in the
+ // session and their corresponding versions. Conversioning is used to clear
+ // stale data from the persistent storage.
+ BlacklistData(std::unique_ptr<Policy> session_policy,
+ std::unique_ptr<Policy> persistent_policy,
+ std::unique_ptr<Policy> host_policy,
+ std::unique_ptr<Policy> type_policy,
+ size_t max_hosts,
+ AllowedTypesAndVersions allowed_types);
+ ~BlacklistData();
+
+ // Adds a new entry for all rules to use when evaluating blacklisting state.
+ // |is_from_persistent_storage| is used to delineate between data added from
+ // this session, and previous sessions.
+ void AddEntry(const std::string& host_name,
+ bool opt_out,
+ int type,
+ base::Time time,
+ bool is_from_persistent_storage);
+
+ // Whether the user is opted out when considering all enabled rules. if
+ // |ignore_long_term_black_list_rules| is true, this will only check the
+ // session rule. For every reason that is checked, but does not trigger
+ // blacklisting, a new reason will be appended to the end |passed_reasons|.
+ // |time| is the time that decision should be evaluated at (usually now).
+ BlacklistReason IsAllowed(const std::string& host_name,
+ int type,
+ bool ignore_long_term_black_list_rules,
+ base::Time time,
+ std::vector<BlacklistReason>* passed_reasons) const;
+
+ // This clears all data in all rules.
+ void ClearData();
+
+ // The allowed types and what version they are. If it is non-empty, it is used
+ // to remove stale entries from the database and to DCHECK that other methods
+ // are not using disallowed types.
+ const AllowedTypesAndVersions& allowed_types() const {
+ return allowed_types_;
+ }
+
+ // Whether the specific |host_name| is blacklisted based only on the host
+ // rule.
+ bool IsHostBlacklisted(const std::string& host_name, base::Time time) const;
+
+ // Whether the user is opted out based solely on the persistent blacklist
+ // rule.
+ bool IsUserOptedOutInGeneral(base::Time time) const;
+
+ // Exposed for logging purposes only.
+ const std::map<std::string, OptOutBlacklistItem>& black_list_item_host_map()
+ const {
+ return black_list_item_host_map_;
+ }
+
+ private:
+ // Removes the oldest (or safest) host item from |black_list_item_host_map_|.
+ // Oldest is defined by most recent opt out time, and safest is defined as an
+ // item with no opt outs.
+ void EvictOldestHost();
+
+ // The session rule policy. If non-null the session rule is enforced.
+ std::unique_ptr<Policy> session_policy_;
+ // The session rule history.
+ std::unique_ptr<OptOutBlacklistItem> session_black_list_item_;
+
+ // The persistent rule policy. If non-null the persistent rule is enforced.
+ std::unique_ptr<Policy> persistent_policy_;
+ // The persistent rule history.
+ std::unique_ptr<OptOutBlacklistItem> persistent_black_list_item_;
+
+ // The host rule policy. If non-null the host rule is enforced.
+ std::unique_ptr<Policy> host_policy_;
+ // The maximum number of hosts allowed in the host blacklist.
+ size_t max_hosts_;
+ // The host rule history. Each host is stored as a separate blacklist history.
+ std::map<std::string, OptOutBlacklistItem> black_list_item_host_map_;
+
+ // The type rule policy. If non-null the type rule is enforced.
+ std::unique_ptr<Policy> type_policy_;
+ // The type rule history. Each type is stored as a separate blacklist history.
+ std::map<int, OptOutBlacklistItem> black_list_item_type_map_;
+
+ // The allowed types and what version they are. If it is non-empty, it is used
+ // to remove stale entries from the database and to DCHECK that other methods
+ // are not using disallowed types.
+ AllowedTypesAndVersions allowed_types_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlacklistData);
+};
+
+} // namespace blacklist
+
+#endif // COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_BLACKLIST_DATA_H_
diff --git a/chromium/components/previews/core/previews_black_list_delegate.h b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_delegate.h
index b229897ae51..2c6dc91dd9a 100644
--- a/chromium/components/previews/core/previews_black_list_delegate.h
+++ b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_delegate.h
@@ -2,23 +2,23 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_PREVIEWS_CORE_PREVIEWS_BLACK_LIST_DELEGATE_H_
-#define COMPONENTS_PREVIEWS_CORE_PREVIEWS_BLACK_LIST_DELEGATE_H_
+#ifndef COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_BLACKLIST_DELEGATE_H_
+#define COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_BLACKLIST_DELEGATE_H_
#include <string>
#include "base/macros.h"
#include "base/time/time.h"
-namespace previews {
+namespace blacklist {
-// An interface for PreviewsBlackList delegate. This interface is for responding
-// to events occur in PreviewsBlackList (e.g. New blacklisted host and user is
-// blacklisted).
-class PreviewsBlacklistDelegate {
+// An interface for a delegate to the opt out blacklist. This interface is for
+// responding to events occurring in the opt out blacklist (e.g. New blacklisted
+// host and user is blacklisted).
+class OptOutBlacklistDelegate {
public:
- PreviewsBlacklistDelegate() {}
- virtual ~PreviewsBlacklistDelegate() {}
+ OptOutBlacklistDelegate() {}
+ virtual ~OptOutBlacklistDelegate() {}
// Notifies |this| that |host| has been blacklisted at |time|. This method is
// guaranteed to be called when a previously whitelisted host is now
@@ -34,6 +34,6 @@ class PreviewsBlacklistDelegate {
virtual void OnBlacklistCleared(base::Time time) = 0;
};
-} // namespace previews
+} // namespace blacklist
-#endif // COMPONENTS_PREVIEWS_CORE_PREVIEWS_BLACK_LIST_DELEGATE_H_
+#endif // COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_BLACKLIST_DELEGATE_H_
diff --git a/chromium/components/previews/core/previews_black_list_item.cc b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_item.cc
index 8f0d016102f..c59d4a8249c 100644
--- a/chromium/components/previews/core/previews_black_list_item.cc
+++ b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_item.cc
@@ -2,46 +2,44 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/previews/core/previews_black_list_item.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_item.h"
#include <algorithm>
#include <tuple>
-#include "components/previews/core/previews_opt_out_store.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_store.h"
-namespace previews {
+namespace blacklist {
-PreviewsBlackListItem::OptOutRecord::OptOutRecord(base::Time entry_time,
- bool opt_out)
+OptOutBlacklistItem::OptOutRecord::OptOutRecord(base::Time entry_time,
+ bool opt_out)
: entry_time_(entry_time), opt_out_(opt_out) {}
-PreviewsBlackListItem::OptOutRecord::~OptOutRecord() {}
+OptOutBlacklistItem::OptOutRecord::~OptOutRecord() {}
-PreviewsBlackListItem::OptOutRecord::OptOutRecord(OptOutRecord&&) noexcept =
+OptOutBlacklistItem::OptOutRecord::OptOutRecord(OptOutRecord&&) noexcept =
default;
-PreviewsBlackListItem::OptOutRecord& PreviewsBlackListItem::OptOutRecord::
-operator=(OptOutRecord&&) noexcept = default;
+OptOutBlacklistItem::OptOutRecord& OptOutBlacklistItem::OptOutRecord::operator=(
+ OptOutRecord&&) noexcept = default;
-bool PreviewsBlackListItem::OptOutRecord::operator<(
+bool OptOutBlacklistItem::OptOutRecord::operator<(
const OptOutRecord& other) const {
// Fresher entries are lower priority to evict, as are non-opt-outs.
return std::tie(entry_time_, opt_out_) >
std::tie(other.entry_time_, other.opt_out_);
}
-PreviewsBlackListItem::PreviewsBlackListItem(
- size_t stored_history_length,
- int opt_out_black_list_threshold,
- base::TimeDelta black_list_duration)
+OptOutBlacklistItem::OptOutBlacklistItem(size_t stored_history_length,
+ int opt_out_black_list_threshold,
+ base::TimeDelta black_list_duration)
: max_stored_history_length_(stored_history_length),
opt_out_black_list_threshold_(opt_out_black_list_threshold),
max_black_list_duration_(black_list_duration),
total_opt_out_(0) {}
-PreviewsBlackListItem::~PreviewsBlackListItem() {}
+OptOutBlacklistItem::~OptOutBlacklistItem() {}
-void PreviewsBlackListItem::AddPreviewNavigation(bool opt_out,
- base::Time entry_time) {
+void OptOutBlacklistItem::AddEntry(bool opt_out, base::Time entry_time) {
DCHECK_LE(opt_out_records_.size(), max_stored_history_length_);
opt_out_records_.emplace(entry_time, opt_out);
@@ -62,15 +60,15 @@ void PreviewsBlackListItem::AddPreviewNavigation(bool opt_out,
DCHECK_LE(opt_out_records_.size(), max_stored_history_length_);
}
-bool PreviewsBlackListItem::IsBlackListed(base::Time now) const {
+bool OptOutBlacklistItem::IsBlackListed(base::Time now) const {
DCHECK_LE(opt_out_records_.size(), max_stored_history_length_);
return most_recent_opt_out_time_ &&
now - most_recent_opt_out_time_.value() < max_black_list_duration_ &&
total_opt_out_ >= opt_out_black_list_threshold_;
}
-size_t PreviewsBlackListItem::OptOutRecordsSizeForTesting() const {
+size_t OptOutBlacklistItem::OptOutRecordsSizeForTesting() const {
return opt_out_records_.size();
}
-} // namespace previews
+} // namespace blacklist
diff --git a/chromium/components/previews/core/previews_black_list_item.h b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_item.h
index 8c23c8f9de7..beaf434af11 100644
--- a/chromium/components/previews/core/previews_black_list_item.h
+++ b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_item.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_PREVIEWS_CORE_PREVIEWS_BLACK_LIST_ITEM_H_
-#define COMPONENTS_PREVIEWS_CORE_PREVIEWS_BLACK_LIST_ITEM_H_
+#ifndef COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_BLACKLIST_ITEM_H_
+#define COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_BLACKLIST_ITEM_H_
#include <stdint.h>
@@ -17,27 +17,26 @@
#include "base/optional.h"
#include "base/time/time.h"
-namespace previews {
+namespace blacklist {
// Stores the recent black list history for a single host. Stores
-// |stored_history_length| of the most recent previews navigations. To determine
-// previews eligiblity fewer than |opt_out_black_list_threshold| out of the past
+// |stored_history_length| of the most recent actions. To determine action
+// eligibility fewer than |opt_out_black_list_threshold| out of the past
// |stored_history_length| navigations must be opt outs. |black_list_duration|
// is the amount of time that elapses until the host is no longer on the black
// list.
-class PreviewsBlackListItem {
+class OptOutBlacklistItem {
public:
- PreviewsBlackListItem(size_t stored_history_length,
- int opt_out_black_list_threshold,
- base::TimeDelta black_list_duration);
+ OptOutBlacklistItem(size_t stored_history_length,
+ int opt_out_black_list_threshold,
+ base::TimeDelta black_list_duration);
- ~PreviewsBlackListItem();
+ ~OptOutBlacklistItem();
// Adds a new navigation at the specified |entry_time|.
- void AddPreviewNavigation(bool opt_out, base::Time entry_time);
+ void AddEntry(bool opt_out, base::Time entry_time);
- // Whether the host name corresponding to |this| should be disallowed from
- // showing previews.
+ // Whether the action corresponding to |this| should be disallowed.
bool IsBlackListed(base::Time now) const;
base::Optional<base::Time> most_recent_opt_out_time() const {
@@ -47,8 +46,8 @@ class PreviewsBlackListItem {
size_t OptOutRecordsSizeForTesting() const;
private:
- // A previews navigation to this host is represented by time and whether the
- // navigation was an opt out.
+ // An action to |this| is represented by time and whether the action was an
+ // opt out.
class OptOutRecord {
public:
OptOutRecord(base::Time entry_time, bool opt_out);
@@ -62,28 +61,28 @@ class PreviewsBlackListItem {
// The time that the opt out state was determined.
base::Time entry_time() const { return entry_time_; }
- // Whether the user opted out of the preview.
+ // Whether the user opted out of the action.
bool opt_out() const { return opt_out_; }
private:
// The time that the opt out state was determined.
base::Time entry_time_;
- // Whether the user opted out of the preview.
+ // Whether the user opted out of the action.
bool opt_out_;
DISALLOW_COPY_AND_ASSIGN(OptOutRecord);
};
- // The number of entries to store to determine preview eligibility.
+ // The number of entries to store to determine action eligibility.
const size_t max_stored_history_length_;
// The number opt outs in recent history that will trigger blacklisting.
const int opt_out_black_list_threshold_;
// The amount of time to black list a domain after the most recent opt out.
const base::TimeDelta max_black_list_duration_;
- // The |max_stored_history_length_| most recent previews navigation. Is
- // maintained as a priority queue that has high priority for items that should
- // be evicted (i.e., they are old).
+ // The |max_stored_history_length_| most recent action. Is maintained as a
+ // priority queue that has high priority for items that should be evicted
+ // (i.e., they are old).
std::priority_queue<OptOutRecord> opt_out_records_;
// Time of the most recent opt out.
@@ -92,9 +91,9 @@ class PreviewsBlackListItem {
// The total number of opt outs currently in |opt_out_records_|.
int total_opt_out_;
- DISALLOW_COPY_AND_ASSIGN(PreviewsBlackListItem);
+ DISALLOW_COPY_AND_ASSIGN(OptOutBlacklistItem);
};
-} // namespace previews
+} // namespace blacklist
-#endif // COMPONENTS_PREVIEWS_CORE_PREVIEWS_BLACK_LIST_ITEM_H_
+#endif // COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_BLACKLIST_ITEM_H_
diff --git a/chromium/components/previews/core/previews_black_list_item_unittest.cc b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_item_unittest.cc
index fb9be62b4e6..eec8091b8b8 100644
--- a/chromium/components/previews/core/previews_black_list_item_unittest.cc
+++ b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_item_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/previews/core/previews_black_list_item.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_item.h"
#include <memory>
@@ -12,13 +12,13 @@
namespace {
-using PreviewsBlackListItemTest = testing::Test;
+using OptOutBlacklistItemTest = testing::Test;
} // namespace
-namespace previews {
+namespace blacklist {
-TEST_F(PreviewsBlackListItemTest, BlackListState) {
+TEST_F(OptOutBlacklistItemTest, BlackListState) {
const int history = 4;
const int threshold = 2;
const base::TimeDelta max_blacklist_duration =
@@ -28,18 +28,18 @@ TEST_F(PreviewsBlackListItemTest, BlackListState) {
const base::Time later =
now + max_blacklist_duration + (delay_between_entries * 3);
- PreviewsBlackListItem black_list_item(history, threshold,
- max_blacklist_duration);
+ OptOutBlacklistItem black_list_item(history, threshold,
+ max_blacklist_duration);
// Empty black list item should report that the host is allowed.
EXPECT_FALSE(black_list_item.IsBlackListed(now));
EXPECT_FALSE(black_list_item.IsBlackListed(later));
EXPECT_FALSE(black_list_item.most_recent_opt_out_time());
- black_list_item.AddPreviewNavigation(false, now);
+ black_list_item.AddEntry(false, now);
EXPECT_FALSE(black_list_item.most_recent_opt_out_time());
- black_list_item.AddPreviewNavigation(true, now);
+ black_list_item.AddEntry(true, now);
EXPECT_TRUE(black_list_item.most_recent_opt_out_time());
EXPECT_EQ(now, black_list_item.most_recent_opt_out_time().value());
// Black list item of size less that |threshold| should report that the host
@@ -47,43 +47,34 @@ TEST_F(PreviewsBlackListItemTest, BlackListState) {
EXPECT_FALSE(black_list_item.IsBlackListed(now));
EXPECT_FALSE(black_list_item.IsBlackListed(later));
- black_list_item.AddPreviewNavigation(true, now + delay_between_entries);
+ black_list_item.AddEntry(true, now + delay_between_entries);
// Black list item with |threshold| fresh entries should report the host as
// disallowed.
EXPECT_TRUE(black_list_item.IsBlackListed(now));
// Black list item with only entries from longer than |duration| ago should
// report the host is allowed.
EXPECT_FALSE(black_list_item.IsBlackListed(later));
- black_list_item.AddPreviewNavigation(true,
- later - (delay_between_entries * 2));
+ black_list_item.AddEntry(true, later - (delay_between_entries * 2));
// Black list item with a fresh opt out and total number of opt outs larger
// than |threshold| should report the host is disallowed.
EXPECT_TRUE(black_list_item.IsBlackListed(later));
// The black list item should maintain entries based on time, so adding
// |history| entries should not push out newer entries.
- black_list_item.AddPreviewNavigation(true, later - delay_between_entries * 2);
- black_list_item.AddPreviewNavigation(false,
- later - delay_between_entries * 3);
- black_list_item.AddPreviewNavigation(false,
- later - delay_between_entries * 3);
- black_list_item.AddPreviewNavigation(false,
- later - delay_between_entries * 3);
- black_list_item.AddPreviewNavigation(false,
- later - delay_between_entries * 3);
+ black_list_item.AddEntry(true, later - delay_between_entries * 2);
+ black_list_item.AddEntry(false, later - delay_between_entries * 3);
+ black_list_item.AddEntry(false, later - delay_between_entries * 3);
+ black_list_item.AddEntry(false, later - delay_between_entries * 3);
+ black_list_item.AddEntry(false, later - delay_between_entries * 3);
EXPECT_TRUE(black_list_item.IsBlackListed(later));
// The black list item should maintain entries based on time, so adding
// |history| newer entries should push out older entries.
- black_list_item.AddPreviewNavigation(false,
- later - delay_between_entries * 1);
- black_list_item.AddPreviewNavigation(false,
- later - delay_between_entries * 1);
- black_list_item.AddPreviewNavigation(false,
- later - delay_between_entries * 1);
- black_list_item.AddPreviewNavigation(false,
- later - delay_between_entries * 1);
+ black_list_item.AddEntry(false, later - delay_between_entries * 1);
+ black_list_item.AddEntry(false, later - delay_between_entries * 1);
+ black_list_item.AddEntry(false, later - delay_between_entries * 1);
+ black_list_item.AddEntry(false, later - delay_between_entries * 1);
EXPECT_FALSE(black_list_item.IsBlackListed(later));
}
-} // namespace previews
+} // namespace blacklist
diff --git a/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_unittest.cc b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_unittest.cc
new file mode 100644
index 00000000000..44fa9f656f7
--- /dev/null
+++ b/chromium/components/blacklist/opt_out_blacklist/opt_out_blacklist_unittest.cc
@@ -0,0 +1,1189 @@
+// 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/blacklist/opt_out_blacklist/opt_out_blacklist.h"
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/simple_test_clock.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_delegate.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_item.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blacklist {
+
+namespace {
+
+const char kTestHost1[] = "testhost1.com";
+const char kTestHost2[] = "testhost2.com";
+
+// Mock class to test that OptOutBlacklist notifies the delegate with correct
+// events (e.g. New host blacklisted, user blacklisted, and blacklist cleared).
+class TestOptOutBlacklistDelegate : public OptOutBlacklistDelegate {
+ public:
+ TestOptOutBlacklistDelegate()
+ : user_blacklisted_(false),
+ blacklist_cleared_(false),
+ blacklist_cleared_time_(base::Time::Now()) {}
+
+ // OptOutBlacklistDelegate:
+ void OnNewBlacklistedHost(const std::string& host, base::Time time) override {
+ blacklisted_hosts_[host] = time;
+ }
+ void OnUserBlacklistedStatusChange(bool blacklisted) override {
+ user_blacklisted_ = blacklisted;
+ }
+ void OnBlacklistCleared(base::Time time) override {
+ blacklist_cleared_ = true;
+ blacklist_cleared_time_ = time;
+ }
+
+ // Gets the set of blacklisted hosts recorded.
+ const std::unordered_map<std::string, base::Time>& blacklisted_hosts() const {
+ return blacklisted_hosts_;
+ }
+
+ // Gets the state of user blacklisted status.
+ bool user_blacklisted() const { return user_blacklisted_; }
+
+ // Gets the state of blacklisted cleared status of |this| for testing.
+ bool blacklist_cleared() const { return blacklist_cleared_; }
+
+ // Gets the event time of blacklist is as cleared.
+ base::Time blacklist_cleared_time() const { return blacklist_cleared_time_; }
+
+ private:
+ // The user blacklisted status of |this| blacklist_delegate.
+ bool user_blacklisted_;
+
+ // Check if the blacklist is notified as cleared on |this| blacklist_delegate.
+ bool blacklist_cleared_;
+
+ // The time when blacklist is cleared.
+ base::Time blacklist_cleared_time_;
+
+ // |this| blacklist_delegate's collection of blacklisted hosts.
+ std::unordered_map<std::string, base::Time> blacklisted_hosts_;
+};
+
+class TestOptOutStore : public OptOutStore {
+ public:
+ TestOptOutStore() : clear_blacklist_count_(0) {}
+ ~TestOptOutStore() override {}
+
+ int clear_blacklist_count() { return clear_blacklist_count_; }
+
+ void SetBlacklistData(std::unique_ptr<BlacklistData> data) {
+ data_ = std::move(data);
+ }
+
+ private:
+ // OptOutStore implementation:
+ void AddEntry(bool opt_out,
+ const std::string& host_name,
+ int type,
+ base::Time now) override {}
+
+ void LoadBlackList(std::unique_ptr<BlacklistData> blacklist_data,
+ LoadBlackListCallback callback) override {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(callback),
+ data_ ? std::move(data_) : std::move(blacklist_data)));
+ }
+
+ void ClearBlackList(base::Time begin_time, base::Time end_time) override {
+ ++clear_blacklist_count_;
+ }
+
+ int clear_blacklist_count_;
+
+ std::unique_ptr<BlacklistData> data_;
+};
+
+class TestOptOutBlacklist : public OptOutBlacklist {
+ public:
+ TestOptOutBlacklist(std::unique_ptr<OptOutStore> opt_out_store,
+ base::Clock* clock,
+ OptOutBlacklistDelegate* blacklist_delegate)
+ : OptOutBlacklist(std::move(opt_out_store), clock, blacklist_delegate) {}
+ ~TestOptOutBlacklist() override {}
+
+ void SetSessionRule(std::unique_ptr<BlacklistData::Policy> policy) {
+ session_policy_ = std::move(policy);
+ }
+
+ void SetPersistentRule(std::unique_ptr<BlacklistData::Policy> policy) {
+ persistent_policy_ = std::move(policy);
+ }
+
+ void SetHostRule(std::unique_ptr<BlacklistData::Policy> policy,
+ size_t max_hosts) {
+ host_policy_ = std::move(policy);
+ max_hosts_ = max_hosts;
+ }
+
+ void SetTypeRule(std::unique_ptr<BlacklistData::Policy> policy) {
+ type_policy_ = std::move(policy);
+ }
+
+ void SetAllowedTypes(BlacklistData::AllowedTypesAndVersions allowed_types) {
+ allowed_types_ = std::move(allowed_types);
+ }
+
+ private:
+ bool ShouldUseSessionPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const override {
+ if (!session_policy_)
+ return false;
+ *duration = session_policy_->duration;
+ *history = session_policy_->history;
+ *threshold = session_policy_->threshold;
+
+ return true;
+ }
+
+ bool ShouldUsePersistentPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const override {
+ if (!persistent_policy_)
+ return false;
+ *duration = persistent_policy_->duration;
+ *history = persistent_policy_->history;
+ *threshold = persistent_policy_->threshold;
+
+ return true;
+ }
+
+ bool ShouldUseHostPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold,
+ size_t* max_hosts) const override {
+ if (!host_policy_)
+ return false;
+ *duration = host_policy_->duration;
+ *history = host_policy_->history;
+ *threshold = host_policy_->threshold;
+ *max_hosts = max_hosts_;
+
+ return true;
+ }
+
+ bool ShouldUseTypePolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const override {
+ if (!type_policy_)
+ return false;
+ *duration = type_policy_->duration;
+ *history = type_policy_->history;
+ *threshold = type_policy_->threshold;
+
+ return true;
+ }
+
+ BlacklistData::AllowedTypesAndVersions GetAllowedTypes() const override {
+ return allowed_types_;
+ }
+
+ std::unique_ptr<BlacklistData::Policy> session_policy_;
+ std::unique_ptr<BlacklistData::Policy> persistent_policy_;
+ std::unique_ptr<BlacklistData::Policy> host_policy_;
+ std::unique_ptr<BlacklistData::Policy> type_policy_;
+
+ size_t max_hosts_ = 0;
+
+ BlacklistData::AllowedTypesAndVersions allowed_types_;
+};
+
+class OptOutBlacklistTest : public testing::Test {
+ public:
+ OptOutBlacklistTest() {}
+ ~OptOutBlacklistTest() override {}
+
+ void StartTest(bool null_opt_out_store) {
+ std::unique_ptr<TestOptOutStore> opt_out_store =
+ null_opt_out_store ? nullptr : std::make_unique<TestOptOutStore>();
+ opt_out_store_ = opt_out_store.get();
+
+ black_list_ = std::make_unique<TestOptOutBlacklist>(
+ std::move(opt_out_store), &test_clock_, &blacklist_delegate_);
+ if (session_policy_) {
+ black_list_->SetSessionRule(std::move(session_policy_));
+ }
+ if (persistent_policy_) {
+ black_list_->SetPersistentRule(std::move(persistent_policy_));
+ }
+ if (host_policy_) {
+ black_list_->SetHostRule(std::move(host_policy_), max_hosts_);
+ }
+ if (type_policy_) {
+ black_list_->SetTypeRule(std::move(type_policy_));
+ }
+
+ black_list_->SetAllowedTypes(std::move(allowed_types_));
+ black_list_->Init();
+
+ start_ = test_clock_.Now();
+
+ passed_reasons_ = {};
+ }
+
+ void SetSessionRule(std::unique_ptr<BlacklistData::Policy> policy) {
+ session_policy_ = std::move(policy);
+ }
+
+ void SetPersistentRule(std::unique_ptr<BlacklistData::Policy> policy) {
+ persistent_policy_ = std::move(policy);
+ }
+
+ void SetHostRule(std::unique_ptr<BlacklistData::Policy> policy,
+ size_t max_hosts) {
+ host_policy_ = std::move(policy);
+ max_hosts_ = max_hosts;
+ }
+
+ void SetTypeRule(std::unique_ptr<BlacklistData::Policy> policy) {
+ type_policy_ = std::move(policy);
+ }
+
+ void SetAllowedTypes(BlacklistData::AllowedTypesAndVersions allowed_types) {
+ allowed_types_ = std::move(allowed_types);
+ }
+
+ protected:
+ base::MessageLoop loop_;
+
+ // Observer to |black_list_|.
+ TestOptOutBlacklistDelegate blacklist_delegate_;
+
+ base::SimpleTestClock test_clock_;
+ TestOptOutStore* opt_out_store_;
+ base::Time start_;
+
+ std::unique_ptr<TestOptOutBlacklist> black_list_;
+ std::vector<BlacklistReason> passed_reasons_;
+
+ private:
+ std::unique_ptr<BlacklistData::Policy> session_policy_;
+ std::unique_ptr<BlacklistData::Policy> persistent_policy_;
+ std::unique_ptr<BlacklistData::Policy> host_policy_;
+ std::unique_ptr<BlacklistData::Policy> type_policy_;
+
+ size_t max_hosts_ = 0;
+
+ BlacklistData::AllowedTypesAndVersions allowed_types_;
+
+ DISALLOW_COPY_AND_ASSIGN(OptOutBlacklistTest);
+};
+
+TEST_F(OptOutBlacklistTest, HostBlackListNoStore) {
+ // Tests the black list behavior when a null OptOutStore is passed in.
+ auto host_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(365), 4u, 2);
+ SetHostRule(std::move(host_policy), 5);
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(true /* null_opt_out */);
+
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost2, 1, false, &passed_reasons_));
+
+ black_list_->AddEntry(kTestHost1, true, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost1, true, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfHost,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, true, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost2, 1, false, &passed_reasons_));
+
+ black_list_->AddEntry(kTestHost2, true, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost2, true, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfHost,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, true, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfHost,
+ black_list_->IsLoadedAndAllowed(kTestHost2, 1, false, &passed_reasons_));
+
+ black_list_->AddEntry(kTestHost2, false, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost2, false, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost2, false, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfHost,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, true, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost2, 1, false, &passed_reasons_));
+
+ black_list_->ClearBlackList(start_, test_clock_.Now());
+
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost2, 1, false, &passed_reasons_));
+}
+
+TEST_F(OptOutBlacklistTest, TypeBlackListWithStore) {
+ // Tests the black list behavior when a non-null OptOutStore is passed in.
+
+ auto type_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(365), 4u, 2);
+ SetTypeRule(std::move(type_policy));
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ allowed_types.insert({2, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(false /* null_opt_out */);
+
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ EXPECT_EQ(
+ BlacklistReason::kBlacklistNotLoaded,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kBlacklistNotLoaded,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kBlacklistNotLoaded,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 2, false, &passed_reasons_));
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 2, false, &passed_reasons_));
+
+ black_list_->AddEntry(kTestHost1, true, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost1, true, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfType,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfType,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 2, true, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 2, false, &passed_reasons_));
+
+ black_list_->AddEntry(kTestHost1, true, 2);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost1, true, 2);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfType,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfType,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfType,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 2, false, &passed_reasons_));
+
+ black_list_->AddEntry(kTestHost1, false, 2);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost1, false, 2);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost1, false, 2);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfType,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfType,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 2, false, &passed_reasons_));
+
+ EXPECT_EQ(0, opt_out_store_->clear_blacklist_count());
+ black_list_->ClearBlackList(start_, base::Time::Now());
+ EXPECT_EQ(1, opt_out_store_->clear_blacklist_count());
+
+ EXPECT_EQ(
+ BlacklistReason::kBlacklistNotLoaded,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kBlacklistNotLoaded,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kBlacklistNotLoaded,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 2, false, &passed_reasons_));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, opt_out_store_->clear_blacklist_count());
+
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 2, false, &passed_reasons_));
+}
+
+TEST_F(OptOutBlacklistTest, TypeBlackListNoStore) {
+ // Tests the black list behavior when a null OptOutStore is passed in.
+ auto type_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(365), 4u, 2);
+ SetTypeRule(std::move(type_policy));
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ allowed_types.insert({2, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(true /* null_opt_out */);
+
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 2, false, &passed_reasons_));
+
+ black_list_->AddEntry(kTestHost1, true, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost1, true, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfType,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, true, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 2, false, &passed_reasons_));
+
+ black_list_->AddEntry(kTestHost1, true, 2);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost1, true, 2);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfType,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, true, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfType,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 2, false, &passed_reasons_));
+
+ black_list_->AddEntry(kTestHost1, false, 2);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost1, false, 2);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost1, false, 2);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfType,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, true, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 2, false, &passed_reasons_));
+
+ black_list_->ClearBlackList(start_, test_clock_.Now());
+
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 2, false, &passed_reasons_));
+}
+
+TEST_F(OptOutBlacklistTest, HostIndifferentBlacklist) {
+ // Tests the black list behavior when a null OptOutStore is passed in.
+ const std::string hosts[] = {
+ "url_0.com", "url_1.com", "url_2.com", "url_3.com",
+ };
+
+ int host_indifferent_threshold = 4;
+
+ auto persistent_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(365), 4u, host_indifferent_threshold);
+ SetPersistentRule(std::move(persistent_policy));
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(true /* null_opt_out */);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(hosts[0], 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(hosts[1], 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(hosts[2], 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(hosts[3], 1, false, &passed_reasons_));
+
+ for (int i = 0; i < host_indifferent_threshold; i++) {
+ black_list_->AddEntry(hosts[i], true, 1);
+ EXPECT_EQ(
+ i != 3 ? BlacklistReason::kAllowed
+ : BlacklistReason::kUserOptedOutInGeneral,
+ black_list_->IsLoadedAndAllowed(hosts[0], 1, false, &passed_reasons_));
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ }
+
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutInGeneral,
+ black_list_->IsLoadedAndAllowed(hosts[0], 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutInGeneral,
+ black_list_->IsLoadedAndAllowed(hosts[1], 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutInGeneral,
+ black_list_->IsLoadedAndAllowed(hosts[2], 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutInGeneral,
+ black_list_->IsLoadedAndAllowed(hosts[3], 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(hosts[3], 1, true, &passed_reasons_));
+
+ black_list_->AddEntry(hosts[3], false, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ // New non-opt-out entry will cause these to be allowed now.
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(hosts[0], 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(hosts[1], 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(hosts[2], 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(hosts[3], 1, false, &passed_reasons_));
+}
+
+TEST_F(OptOutBlacklistTest, QueueBehavior) {
+ // Tests the black list asynchronous queue behavior. Methods called while
+ // loading the opt-out store are queued and should run in the order they were
+ // queued.
+
+ std::vector<bool> test_opt_out{true, false};
+
+ for (auto opt_out : test_opt_out) {
+ auto host_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(365), 4u, 2);
+ SetHostRule(std::move(host_policy), 5);
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(false /* null_opt_out */);
+
+ EXPECT_EQ(BlacklistReason::kBlacklistNotLoaded,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false,
+ &passed_reasons_));
+ black_list_->AddEntry(kTestHost1, opt_out, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost1, opt_out, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(BlacklistReason::kBlacklistNotLoaded,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false,
+ &passed_reasons_));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(opt_out ? BlacklistReason::kUserOptedOutOfHost
+ : BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false,
+ &passed_reasons_));
+ black_list_->AddEntry(kTestHost1, opt_out, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost1, opt_out, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(0, opt_out_store_->clear_blacklist_count());
+ black_list_->ClearBlackList(
+ start_, test_clock_.Now() + base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(1, opt_out_store_->clear_blacklist_count());
+ black_list_->AddEntry(kTestHost2, opt_out, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost2, opt_out, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, opt_out_store_->clear_blacklist_count());
+
+ EXPECT_EQ(BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false,
+ &passed_reasons_));
+ EXPECT_EQ(opt_out ? BlacklistReason::kUserOptedOutOfHost
+ : BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost2, 1, false,
+ &passed_reasons_));
+ }
+}
+
+TEST_F(OptOutBlacklistTest, MaxHosts) {
+ // Test that the black list only stores n hosts, and it stores the correct n
+ // hosts.
+ const std::string test_host_3("host3.com");
+ const std::string test_host_4("host4.com");
+ const std::string test_host_5("host5.com");
+
+ auto host_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(365), 1u, 1);
+ SetHostRule(std::move(host_policy), 2);
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(true /* null_opt_out */);
+
+ black_list_->AddEntry(kTestHost1, true, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost2, false, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(test_host_3, false, 1);
+ // kTestHost1 should stay in the map, since it has an opt out time.
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfHost,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost2, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(test_host_3, 1, false, &passed_reasons_));
+
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(test_host_4, true, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(test_host_5, true, 1);
+ // test_host_4 and test_host_5 should remain in the map, but host should be
+ // evicted.
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfHost,
+ black_list_->IsLoadedAndAllowed(test_host_4, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfHost,
+ black_list_->IsLoadedAndAllowed(test_host_5, 1, false, &passed_reasons_));
+}
+
+TEST_F(OptOutBlacklistTest, SingleOptOut) {
+ // Test that when a user opts out of an action, actions won't be allowed until
+ // |single_opt_out_duration| has elapsed.
+ int single_opt_out_duration = 5;
+ const std::string test_host_3("host3.com");
+
+ auto session_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromSeconds(single_opt_out_duration), 1u, 1);
+ SetSessionRule(std::move(session_policy));
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(true /* null_opt_out */);
+
+ black_list_->AddEntry(kTestHost1, false, 1);
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(test_host_3, 1, false, &passed_reasons_));
+
+ test_clock_.Advance(
+ base::TimeDelta::FromSeconds(single_opt_out_duration + 1));
+
+ black_list_->AddEntry(kTestHost2, true, 1);
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutInSession,
+ black_list_->IsLoadedAndAllowed(kTestHost2, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutInSession,
+ black_list_->IsLoadedAndAllowed(test_host_3, 1, false, &passed_reasons_));
+
+ test_clock_.Advance(
+ base::TimeDelta::FromSeconds(single_opt_out_duration - 1));
+
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutInSession,
+ black_list_->IsLoadedAndAllowed(kTestHost2, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutInSession,
+ black_list_->IsLoadedAndAllowed(test_host_3, 1, false, &passed_reasons_));
+
+ test_clock_.Advance(
+ base::TimeDelta::FromSeconds(single_opt_out_duration + 1));
+
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost2, 1, false, &passed_reasons_));
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(test_host_3, 1, false, &passed_reasons_));
+}
+
+TEST_F(OptOutBlacklistTest, ClearingBlackListClearsRecentNavigation) {
+ // Tests that clearing the black list for a long amount of time (relative to
+ // "single_opt_out_duration_in_seconds") resets the blacklist's recent opt out
+ // rule.
+
+ auto session_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromSeconds(5), 1u, 1);
+ SetSessionRule(std::move(session_policy));
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(false /* null_opt_out */);
+
+ black_list_->AddEntry(kTestHost1, true /* opt_out */, 1);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->ClearBlackList(start_, test_clock_.Now());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+}
+
+TEST_F(OptOutBlacklistTest, ObserverIsNotifiedOnHostBlacklisted) {
+ // Tests the black list behavior when a null OptOutStore is passed in.
+
+ auto host_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(365), 4u, 2);
+ SetHostRule(std::move(host_policy), 5);
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(true /* null_opt_out */);
+
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+
+ // Observer is not notified as blacklisted when the threshold does not met.
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost1, true, 1);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_THAT(blacklist_delegate_.blacklisted_hosts(), ::testing::SizeIs(0));
+
+ // Observer is notified as blacklisted when the threshold is met.
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost1, true, 1);
+ base::RunLoop().RunUntilIdle();
+ const base::Time blacklisted_time = test_clock_.Now();
+ EXPECT_THAT(blacklist_delegate_.blacklisted_hosts(), ::testing::SizeIs(1));
+ EXPECT_EQ(blacklisted_time,
+ blacklist_delegate_.blacklisted_hosts().find(kTestHost1)->second);
+
+ // Observer is not notified when the host is already blacklisted.
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(kTestHost1, true, 1);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_THAT(blacklist_delegate_.blacklisted_hosts(), ::testing::SizeIs(1));
+ EXPECT_EQ(blacklisted_time,
+ blacklist_delegate_.blacklisted_hosts().find(kTestHost1)->second);
+
+ // Observer is notified when blacklist is cleared.
+ EXPECT_FALSE(blacklist_delegate_.blacklist_cleared());
+
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->ClearBlackList(start_, test_clock_.Now());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(blacklist_delegate_.blacklist_cleared());
+ EXPECT_EQ(test_clock_.Now(), blacklist_delegate_.blacklist_cleared_time());
+}
+
+TEST_F(OptOutBlacklistTest, ObserverIsNotifiedOnUserBlacklisted) {
+ // Tests the black list behavior when a null OptOutStore is passed in.
+ const std::string hosts[] = {
+ "url_0.com", "url_1.com", "url_2.com", "url_3.com",
+ };
+
+ int host_indifferent_threshold = 4;
+
+ auto persistent_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(30), 4u, host_indifferent_threshold);
+ SetPersistentRule(std::move(persistent_policy));
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(true /* null_opt_out */);
+
+ // Initially no host is blacklisted, and user is not blacklisted.
+ EXPECT_THAT(blacklist_delegate_.blacklisted_hosts(), ::testing::SizeIs(0));
+ EXPECT_FALSE(blacklist_delegate_.user_blacklisted());
+
+ for (int i = 0; i < host_indifferent_threshold; ++i) {
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(hosts[i], true, 1);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_THAT(blacklist_delegate_.blacklisted_hosts(), ::testing::SizeIs(0));
+ // Observer is notified when number of recently opt out meets
+ // |host_indifferent_threshold|.
+ EXPECT_EQ(i >= host_indifferent_threshold - 1,
+ blacklist_delegate_.user_blacklisted());
+ }
+
+ // Observer is notified when the user is no longer blacklisted.
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+ black_list_->AddEntry(hosts[3], false, 1);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(blacklist_delegate_.user_blacklisted());
+}
+
+TEST_F(OptOutBlacklistTest, ObserverIsNotifiedWhenLoadBlacklistDone) {
+ int host_indifferent_threshold = 4;
+ size_t host_indifferent_history = 4u;
+ auto persistent_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(30), host_indifferent_history,
+ host_indifferent_threshold);
+ SetPersistentRule(std::move(persistent_policy));
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(false /* null_opt_out */);
+
+ allowed_types.clear();
+ allowed_types[0] = 0;
+ std::unique_ptr<BlacklistData> data = std::make_unique<BlacklistData>(
+ nullptr,
+ std::make_unique<BlacklistData::Policy>(base::TimeDelta::FromSeconds(365),
+ host_indifferent_history,
+ host_indifferent_threshold),
+ nullptr, nullptr, 0, std::move(allowed_types));
+ base::SimpleTestClock test_clock;
+
+ for (int i = 0; i < host_indifferent_threshold; ++i) {
+ test_clock.Advance(base::TimeDelta::FromSeconds(1));
+ data->AddEntry(kTestHost1, true, 0, test_clock.Now(), true);
+ }
+
+ std::unique_ptr<TestOptOutStore> opt_out_store =
+ std::make_unique<TestOptOutStore>();
+ opt_out_store->SetBlacklistData(std::move(data));
+
+ EXPECT_FALSE(blacklist_delegate_.user_blacklisted());
+ allowed_types.clear();
+ allowed_types[1] = 0;
+ auto black_list = std::make_unique<TestOptOutBlacklist>(
+ std::move(opt_out_store), &test_clock, &blacklist_delegate_);
+ black_list->SetAllowedTypes(std::move(allowed_types));
+
+ persistent_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(30), host_indifferent_history,
+ host_indifferent_threshold);
+ black_list->SetPersistentRule(std::move(persistent_policy));
+
+ black_list->Init();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(blacklist_delegate_.user_blacklisted());
+}
+
+TEST_F(OptOutBlacklistTest, ObserverIsNotifiedOfHistoricalBlacklistedHosts) {
+ // Tests the black list behavior when a non-null OptOutStore is passed in.
+ int host_indifferent_threshold = 2;
+ size_t host_indifferent_history = 4u;
+ auto host_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(365), host_indifferent_history,
+ host_indifferent_threshold);
+ SetHostRule(std::move(host_policy), 5);
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(false /* null_opt_out */);
+
+ base::SimpleTestClock test_clock;
+
+ allowed_types.clear();
+ allowed_types[static_cast<int>(1)] = 0;
+ std::unique_ptr<BlacklistData> data = std::make_unique<BlacklistData>(
+ nullptr, nullptr,
+ std::make_unique<BlacklistData::Policy>(base::TimeDelta::FromDays(365),
+ host_indifferent_history,
+ host_indifferent_threshold),
+ nullptr, 2, std::move(allowed_types));
+
+ test_clock.Advance(base::TimeDelta::FromSeconds(1));
+ data->AddEntry(kTestHost1, true, static_cast<int>(1), test_clock.Now(), true);
+ test_clock.Advance(base::TimeDelta::FromSeconds(1));
+ data->AddEntry(kTestHost1, true, static_cast<int>(1), test_clock.Now(), true);
+ base::Time blacklisted_time = test_clock.Now();
+
+ base::RunLoop().RunUntilIdle();
+ std::vector<BlacklistReason> reasons;
+ EXPECT_NE(BlacklistReason::kAllowed,
+ data->IsAllowed(kTestHost1, static_cast<int>(1), false,
+ test_clock.Now(), &reasons));
+
+ // Host |url_b| is not blacklisted.
+ test_clock.Advance(base::TimeDelta::FromSeconds(1));
+ data->AddEntry(kTestHost2, true, static_cast<int>(1), test_clock.Now(), true);
+
+ std::unique_ptr<TestOptOutStore> opt_out_store =
+ std::make_unique<TestOptOutStore>();
+ opt_out_store->SetBlacklistData(std::move(data));
+
+ allowed_types.clear();
+ allowed_types[static_cast<int>(1)] = 0;
+ auto black_list = std::make_unique<TestOptOutBlacklist>(
+ std::move(opt_out_store), &test_clock, &blacklist_delegate_);
+ black_list->SetAllowedTypes(std::move(allowed_types));
+
+ host_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(30), host_indifferent_history,
+ host_indifferent_threshold);
+ black_list->SetPersistentRule(std::move(host_policy));
+
+ black_list->Init();
+
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_THAT(blacklist_delegate_.blacklisted_hosts(), ::testing::SizeIs(1));
+ EXPECT_EQ(blacklisted_time,
+ blacklist_delegate_.blacklisted_hosts().find(kTestHost1)->second);
+}
+
+TEST_F(OptOutBlacklistTest, PassedReasonsWhenBlacklistDataNotLoaded) {
+ // Test that IsLoadedAndAllow, push checked BlacklistReasons to the
+ // |passed_reasons| vector.
+
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+ StartTest(false /* null_opt_out */);
+
+ EXPECT_EQ(
+ BlacklistReason::kBlacklistNotLoaded,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+
+ EXPECT_EQ(0UL, passed_reasons_.size());
+}
+
+TEST_F(OptOutBlacklistTest, PassedReasonsWhenUserRecentlyOptedOut) {
+ // Test that IsLoadedAndAllow, push checked BlacklistReasons to the
+ // |passed_reasons| vector.
+
+ auto session_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromSeconds(5), 1u, 1);
+ SetSessionRule(std::move(session_policy));
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(true /* null_opt_out */);
+
+ black_list_->AddEntry(kTestHost1, true, 1);
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutInSession,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+ EXPECT_EQ(1UL, passed_reasons_.size());
+ EXPECT_EQ(BlacklistReason::kBlacklistNotLoaded, passed_reasons_[0]);
+}
+
+TEST_F(OptOutBlacklistTest, PassedReasonsWhenUserBlacklisted) {
+ // Test that IsLoadedAndAllow, push checked BlacklistReasons to the
+ // |passed_reasons| vector.
+ const std::string hosts[] = {
+ "http://www.url_0.com", "http://www.url_1.com", "http://www.url_2.com",
+ "http://www.url_3.com",
+ };
+
+ auto session_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromSeconds(1), 1u, 1);
+ SetSessionRule(std::move(session_policy));
+ auto persistent_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(365), 4u, 4);
+ SetPersistentRule(std::move(persistent_policy));
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(true /* null_opt_out */);
+ test_clock_.Advance(base::TimeDelta::FromSeconds(1));
+
+ for (auto host : hosts) {
+ black_list_->AddEntry(host, true, 1);
+ }
+
+ test_clock_.Advance(base::TimeDelta::FromSeconds(2));
+
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutInGeneral,
+ black_list_->IsLoadedAndAllowed(hosts[0], 1, false, &passed_reasons_));
+
+ BlacklistReason expected_reasons[] = {
+ BlacklistReason::kBlacklistNotLoaded,
+ BlacklistReason::kUserOptedOutInSession,
+ };
+ EXPECT_EQ(base::size(expected_reasons), passed_reasons_.size());
+ for (size_t i = 0; i < passed_reasons_.size(); i++) {
+ EXPECT_EQ(expected_reasons[i], passed_reasons_[i]);
+ }
+}
+
+TEST_F(OptOutBlacklistTest, PassedReasonsWhenHostBlacklisted) {
+ // Test that IsLoadedAndAllow, push checked BlacklistReasons to the
+ // |passed_reasons| vector.
+
+ auto session_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(5), 3u, 3);
+ SetSessionRule(std::move(session_policy));
+ auto persistent_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(365), 4u, 4);
+ SetPersistentRule(std::move(persistent_policy));
+ auto host_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(30), 4u, 2);
+ SetHostRule(std::move(host_policy), 2);
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(true /* null_opt_out */);
+
+ black_list_->AddEntry(kTestHost1, true, 1);
+ black_list_->AddEntry(kTestHost1, true, 1);
+
+ EXPECT_EQ(
+ BlacklistReason::kUserOptedOutOfHost,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+
+ BlacklistReason expected_reasons[] = {
+ BlacklistReason::kBlacklistNotLoaded,
+ BlacklistReason::kUserOptedOutInSession,
+ BlacklistReason::kUserOptedOutInGeneral,
+ };
+ EXPECT_EQ(base::size(expected_reasons), passed_reasons_.size());
+ for (size_t i = 0; i < passed_reasons_.size(); i++) {
+ EXPECT_EQ(expected_reasons[i], passed_reasons_[i]);
+ }
+}
+
+TEST_F(OptOutBlacklistTest, PassedReasonsWhenAllowed) {
+ // Test that IsLoadedAndAllow, push checked BlacklistReasons to the
+ // |passed_reasons| vector.
+
+ auto session_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromSeconds(1), 1u, 1);
+ SetSessionRule(std::move(session_policy));
+ auto persistent_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(365), 4u, 4);
+ SetPersistentRule(std::move(persistent_policy));
+ auto host_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(30), 4u, 4);
+ SetHostRule(std::move(host_policy), 1);
+ auto type_policy = std::make_unique<BlacklistData::Policy>(
+ base::TimeDelta::FromDays(30), 4u, 4);
+ SetTypeRule(std::move(type_policy));
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetAllowedTypes(std::move(allowed_types));
+
+ StartTest(true /* null_opt_out */);
+
+ EXPECT_EQ(
+ BlacklistReason::kAllowed,
+ black_list_->IsLoadedAndAllowed(kTestHost1, 1, false, &passed_reasons_));
+
+ BlacklistReason expected_reasons[] = {
+ BlacklistReason::kBlacklistNotLoaded,
+ BlacklistReason::kUserOptedOutInSession,
+ BlacklistReason::kUserOptedOutInGeneral,
+ BlacklistReason::kUserOptedOutOfHost,
+ BlacklistReason::kUserOptedOutOfType,
+ };
+ EXPECT_EQ(base::size(expected_reasons), passed_reasons_.size());
+ for (size_t i = 0; i < passed_reasons_.size(); i++) {
+ EXPECT_EQ(expected_reasons[i], passed_reasons_[i]);
+ }
+}
+
+} // namespace
+
+} // namespace blacklist
diff --git a/chromium/components/blacklist/opt_out_blacklist/opt_out_store.h b/chromium/components/blacklist/opt_out_blacklist/opt_out_store.h
new file mode 100644
index 00000000000..d5a493267a4
--- /dev/null
+++ b/chromium/components/blacklist/opt_out_blacklist/opt_out_store.h
@@ -0,0 +1,49 @@
+// 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_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_STORE_H_
+#define COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_STORE_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/time/time.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h"
+
+namespace blacklist {
+
+typedef base::OnceCallback<void(std::unique_ptr<BlacklistData>)>
+ LoadBlackListCallback;
+
+// OptOutStore keeps opt out information for the blacklist.
+// Ability to create multiple instances of the store as well as behavior of
+// asynchronous operations when the object is being destroyed, before such
+// operation finishes will depend on implementation. It is possible to issue
+// multiple asynchronous operations in parallel and maintain ordering.
+class OptOutStore {
+ public:
+ virtual ~OptOutStore() {}
+
+ // Adds a new navigation to the store. |opt_out| is whether the user opted out
+ // of the action.
+ virtual void AddEntry(bool opt_out,
+ const std::string& host_name,
+ int type,
+ base::Time now) = 0;
+
+ // Asynchronously loads a map of host names to OptOutBlacklistItem for that
+ // host from the store. And runs |callback| once loading is finished.
+ virtual void LoadBlackList(std::unique_ptr<BlacklistData> blacklist_data,
+ LoadBlackListCallback callback) = 0;
+
+ // Deletes all history in the store between |begin_time| and |end_time|.
+ virtual void ClearBlackList(base::Time begin_time, base::Time end_time) = 0;
+};
+
+} // namespace blacklist
+
+#endif // COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_OPT_OUT_STORE_H_
diff --git a/chromium/components/blacklist/opt_out_blacklist/sql/BUILD.gn b/chromium/components/blacklist/opt_out_blacklist/sql/BUILD.gn
new file mode 100644
index 00000000000..a115a7a25bb
--- /dev/null
+++ b/chromium/components/blacklist/opt_out_blacklist/sql/BUILD.gn
@@ -0,0 +1,34 @@
+# 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.
+
+static_library("opt_out_blacklist_sql") {
+ sources = [
+ "opt_out_store_sql.cc",
+ "opt_out_store_sql.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/blacklist/opt_out_blacklist:opt_out_blacklist",
+ "//sql",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "opt_out_store_sql_unittest.cc",
+ ]
+
+ deps = [
+ ":opt_out_blacklist_sql",
+ "//base",
+ "//base/test:test_support",
+ "//components/blacklist/opt_out_blacklist:opt_out_blacklist",
+ "//sql",
+ "//sql:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/blacklist/opt_out_blacklist/sql/DEPS b/chromium/components/blacklist/opt_out_blacklist/sql/DEPS
new file mode 100644
index 00000000000..6fff87d325a
--- /dev/null
+++ b/chromium/components/blacklist/opt_out_blacklist/sql/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+sql",
+]
diff --git a/chromium/components/blacklist/opt_out_blacklist/sql/opt_out_store_sql.cc b/chromium/components/blacklist/opt_out_blacklist/sql/opt_out_store_sql.cc
new file mode 100644
index 00000000000..f8e7f5ecc05
--- /dev/null
+++ b/chromium/components/blacklist/opt_out_blacklist/sql/opt_out_store_sql.cc
@@ -0,0 +1,423 @@
+// 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/blacklist/opt_out_blacklist/sql/opt_out_store_sql.h"
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h"
+#include "sql/connection.h"
+#include "sql/recovery.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+
+namespace blacklist {
+
+namespace {
+
+// Command line switch to change the entry per host DB size.
+const char kMaxRowsPerHost[] = "max-opt-out-rows-per-host";
+
+// Command line switch to change the DB size.
+const char kMaxRows[] = "max-opt-out-rows";
+
+// Returns the maximum number of table rows allowed per host for the sql
+// opt out store. This is enforced during insertion of new navigation entries.
+int MaxRowsPerHostInOptOutDB() {
+ std::string max_rows =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ kMaxRowsPerHost);
+ int value;
+ return base::StringToInt(max_rows, &value) ? value : 32;
+}
+
+// Returns the maximum number of table rows allowed for the blacklist opt out
+// store. This is enforced during load time; thus the database can grow
+// larger than this temporarily.
+int MaxRowsInOptOutDB() {
+ std::string max_rows =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kMaxRows);
+ int value;
+ return base::StringToInt(max_rows, &value) ? value : 3200;
+}
+
+// Table names use a macro instead of a const, so they can be used inline in
+// other SQL statements below.
+
+// The Opt Out table holds entries for hosts that should not use a specified
+// type. Historically, this was named previews_v1.
+#define OPT_OUT_TABLE_NAME "previews_v1"
+
+// The Enabled types table hold the list of enabled types
+// treatments with a version for that enabled treatment. If the version
+// changes or the type becomes disabled, then any entries in the Opt Out
+// table for that treatment type should be cleared. Historically, this was named
+// enabled_previews_v1.
+#define ENABLED_TYPES_TABLE_NAME "enabled_previews_v1"
+
+void CreateSchema(sql::Connection* db) {
+ static const char kSqlCreateTable[] =
+ "CREATE TABLE IF NOT EXISTS " OPT_OUT_TABLE_NAME
+ " (host_name VARCHAR NOT NULL,"
+ " time INTEGER NOT NULL,"
+ " opt_out INTEGER NOT NULL,"
+ " type INTEGER NOT NULL,"
+ " PRIMARY KEY(host_name, time DESC, opt_out, type))";
+ if (!db->Execute(kSqlCreateTable))
+ return;
+
+ static const char kSqlCreateEnabledTypeVersionTable[] =
+ "CREATE TABLE IF NOT EXISTS " ENABLED_TYPES_TABLE_NAME
+ " (type INTEGER NOT NULL,"
+ " version INTEGER NOT NULL,"
+ " PRIMARY KEY(type))";
+ if (!db->Execute(kSqlCreateEnabledTypeVersionTable))
+ return;
+}
+
+void DatabaseErrorCallback(sql::Connection* db,
+ const base::FilePath& db_path,
+ int extended_error,
+ sql::Statement* stmt) {
+ if (sql::Recovery::ShouldRecover(extended_error)) {
+ // Prevent reentrant calls.
+ 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.
+ sql::Recovery::RecoverDatabase(db, db_path);
+
+ // The DLOG(WARNING) below is intended to draw immediate attention to errors
+ // in newly-written code. Database corruption is generally a result of OS
+ // or hardware issues, not coding errors at the client level, so displaying
+ // the error would probably lead to confusion. The ignored call signals the
+ // test-expectation framework that the error was handled.
+ ignore_result(sql::Connection::IsExpectedSqliteError(extended_error));
+ return;
+ }
+}
+
+void InitDatabase(sql::Connection* db, base::FilePath path) {
+ // The entry size should be between 11 and 10 + x bytes, where x is the the
+ // length of the host name string in bytes.
+ // The total number of entries per host is bounded at 32, and the total number
+ // of hosts is currently unbounded (but typically expected to be under 100).
+ // Assuming average of 100 bytes per entry, and 100 hosts, the total size will
+ // be 4096 * 78. 250 allows room for extreme cases such as many host names
+ // or very long host names.
+ // The average case should be much smaller as users rarely visit hosts that
+ // are not in their top 20 hosts. It should be closer to 32 * 100 * 20 for
+ // most users, which is about 4096 * 15.
+ // The total size of the database will be capped at 3200 entries.
+ db->set_page_size(4096);
+ db->set_cache_size(250);
+ db->set_histogram_tag("OptOutBlacklist");
+ db->set_exclusive_locking();
+
+ db->set_error_callback(base::BindRepeating(&DatabaseErrorCallback, db, path));
+
+ base::File::Error err;
+ if (!base::CreateDirectoryAndGetError(path.DirName(), &err)) {
+ return;
+ }
+ if (!db->Open(path)) {
+ return;
+ }
+
+ CreateSchema(db);
+}
+
+// Adds a new OptOut entry to the data base.
+void AddEntryToDataBase(sql::Connection* db,
+ bool opt_out,
+ const std::string& host_name,
+ int type,
+ base::Time now) {
+ // Adds the new entry.
+ static const char kSqlInsert[] = "INSERT INTO " OPT_OUT_TABLE_NAME
+ " (host_name, time, opt_out, type)"
+ " VALUES "
+ " (?, ?, ?, ?)";
+
+ sql::Statement statement_insert(
+ db->GetCachedStatement(SQL_FROM_HERE, kSqlInsert));
+ statement_insert.BindString(0, host_name);
+ statement_insert.BindInt64(1, (now - base::Time()).InMicroseconds());
+ statement_insert.BindBool(2, opt_out);
+ statement_insert.BindInt(3, type);
+ statement_insert.Run();
+}
+
+// Removes OptOut entries for |host_name| if the per-host row limit is exceeded.
+// Removes OptOut entries if per data base row limit is exceeded.
+void MaybeEvictHostEntryFromDataBase(sql::Connection* db,
+ const std::string& host_name) {
+ // Delete the oldest entries if there are more than |MaxRowsPerHostInOptOutDB|
+ // for |host_name|.
+ // DELETE ... LIMIT -1 OFFSET x means delete all but the first x entries.
+ static const char kSqlDeleteByHost[] =
+ "DELETE FROM " OPT_OUT_TABLE_NAME
+ " WHERE ROWID IN"
+ " (SELECT ROWID from " OPT_OUT_TABLE_NAME
+ " WHERE host_name == ?"
+ " ORDER BY time DESC"
+ " LIMIT -1 OFFSET ?)";
+
+ sql::Statement statement_delete_by_host(
+ db->GetCachedStatement(SQL_FROM_HERE, kSqlDeleteByHost));
+ statement_delete_by_host.BindString(0, host_name);
+ statement_delete_by_host.BindInt(1, MaxRowsPerHostInOptOutDB());
+ statement_delete_by_host.Run();
+}
+
+// Deletes every entry for |type|.
+void ClearBlacklistForTypeInDataBase(sql::Connection* db, int type) {
+ static const char kSql[] =
+ "DELETE FROM " OPT_OUT_TABLE_NAME " WHERE type == ?";
+ sql::Statement statement(db->GetUniqueStatement(kSql));
+ statement.BindInt(0, type);
+ statement.Run();
+}
+
+// Retrieves the list of previously enabled types with their version from the
+// Enabled table.
+BlacklistData::AllowedTypesAndVersions GetStoredEntries(sql::Connection* db) {
+ static const char kSqlLoadEnabledTypesVersions[] =
+ "SELECT type, version FROM " ENABLED_TYPES_TABLE_NAME;
+
+ sql::Statement statement(
+ db->GetUniqueStatement(kSqlLoadEnabledTypesVersions));
+
+ BlacklistData::AllowedTypesAndVersions stored_entries;
+ while (statement.Step()) {
+ int type = statement.ColumnInt(0);
+ int version = statement.ColumnInt(1);
+ stored_entries.insert({type, version});
+ }
+ return stored_entries;
+}
+
+// Adds a newly enabled |type| with its |version| to the Enabled types table.
+void InsertEnabledTypesInDataBase(sql::Connection* db, int type, int version) {
+ static const char kSqlInsert[] = "INSERT INTO " ENABLED_TYPES_TABLE_NAME
+ " (type, version)"
+ " VALUES "
+ " (?, ?)";
+
+ sql::Statement statement_insert(db->GetUniqueStatement(kSqlInsert));
+ statement_insert.BindInt(0, type);
+ statement_insert.BindInt(1, version);
+ statement_insert.Run();
+}
+
+// Updates the |version| of an enabled |type| in the Enabled table.
+void UpdateEnabledTypesInDataBase(sql::Connection* db, int type, int version) {
+ static const char kSqlUpdate[] = "UPDATE " ENABLED_TYPES_TABLE_NAME
+ " SET version = ?"
+ " WHERE type = ?";
+
+ sql::Statement statement_update(
+ db->GetCachedStatement(SQL_FROM_HERE, kSqlUpdate));
+ statement_update.BindInt(0, version);
+ statement_update.BindInt(1, type);
+ statement_update.Run();
+}
+
+// Deletes a previously enabled |type| from the Enabled table.
+void DeleteEnabledTypesInDataBase(sql::Connection* db, int type) {
+ static const char kSqlDelete[] =
+ "DELETE FROM " ENABLED_TYPES_TABLE_NAME " WHERE type == ?";
+
+ sql::Statement statement_delete(db->GetUniqueStatement(kSqlDelete));
+ statement_delete.BindInt(0, type);
+ statement_delete.Run();
+}
+
+// Checks the current set of enabled types (with their current version)
+// and where a type is now disabled or has a different version, cleans up
+// any associated blacklist entries.
+void CheckAndReconcileEnabledTypesWithDataBase(
+ sql::Connection* db,
+ const BlacklistData::AllowedTypesAndVersions& allowed_types) {
+ BlacklistData::AllowedTypesAndVersions stored_entries = GetStoredEntries(db);
+
+ for (auto enabled_it : allowed_types) {
+ int type = enabled_it.first;
+ int current_version = enabled_it.second;
+ auto stored_it = stored_entries.find(type);
+ if (stored_it == stored_entries.end()) {
+ InsertEnabledTypesInDataBase(db, type, current_version);
+ } else {
+ if (stored_it->second != current_version) {
+ DCHECK_GE(current_version, stored_it->second);
+ ClearBlacklistForTypeInDataBase(db, type);
+ UpdateEnabledTypesInDataBase(db, type, current_version);
+ }
+ // Erase entry from the local map to detect any newly disabled types.
+ stored_entries.erase(stored_it);
+ }
+ }
+
+ // Now check for any types that are no longer enabled.
+ for (auto stored_it : stored_entries) {
+ int type = stored_it.first;
+ ClearBlacklistForTypeInDataBase(db, type);
+ DeleteEnabledTypesInDataBase(db, type);
+ }
+}
+
+void LoadBlackListFromDataBase(
+ sql::Connection* db,
+ std::unique_ptr<BlacklistData> blacklist_data,
+ scoped_refptr<base::SingleThreadTaskRunner> runner,
+ LoadBlackListCallback callback) {
+ // First handle any update needed wrt enabled types and their versions.
+ CheckAndReconcileEnabledTypesWithDataBase(db,
+ blacklist_data->allowed_types());
+
+ // Gets the table sorted by host and time. Limits the number of hosts using
+ // most recent opt_out time as the limiting function. Sorting is free due to
+ // the table structure, and it improves performance in the loop below.
+ static const char kSql[] =
+ "SELECT host_name, time, opt_out, type"
+ " FROM " OPT_OUT_TABLE_NAME " ORDER BY host_name, time DESC";
+
+ sql::Statement statement(db->GetUniqueStatement(kSql));
+
+ int count = 0;
+ while (statement.Step()) {
+ ++count;
+ blacklist_data->AddEntry(statement.ColumnString(0), statement.ColumnBool(2),
+ statement.ColumnInt64(3),
+ base::Time() + base::TimeDelta::FromMicroseconds(
+ statement.ColumnInt64(1)),
+ true);
+ }
+
+ if (count > MaxRowsInOptOutDB()) {
+ // Delete the oldest entries if there are more than |kMaxEntriesInDB|.
+ // DELETE ... LIMIT -1 OFFSET x means delete all but the first x entries.
+ static const char kSqlDeleteByDBSize[] =
+ "DELETE FROM " OPT_OUT_TABLE_NAME
+ " WHERE ROWID IN"
+ " (SELECT ROWID from " OPT_OUT_TABLE_NAME
+ " ORDER BY time DESC"
+ " LIMIT -1 OFFSET ?)";
+
+ sql::Statement statement_delete(
+ db->GetCachedStatement(SQL_FROM_HERE, kSqlDeleteByDBSize));
+ statement_delete.BindInt(0, MaxRowsInOptOutDB());
+ statement_delete.Run();
+ }
+
+ runner->PostTask(FROM_HERE, base::BindOnce(std::move(callback),
+ std::move(blacklist_data)));
+}
+
+// Synchronous implementations, these are run on the background thread
+// and actually do the work to access the SQL data base.
+void LoadBlackListSync(sql::Connection* db,
+ const base::FilePath& path,
+ std::unique_ptr<BlacklistData> blacklist_data,
+ scoped_refptr<base::SingleThreadTaskRunner> runner,
+ LoadBlackListCallback callback) {
+ if (!db->is_open())
+ InitDatabase(db, path);
+
+ LoadBlackListFromDataBase(db, std::move(blacklist_data), runner,
+ std::move(callback));
+}
+
+// Deletes every row in the table that has entry time between |begin_time| and
+// |end_time|.
+void ClearBlackListSync(sql::Connection* db,
+ base::Time begin_time,
+ base::Time end_time) {
+ static const char kSql[] =
+ "DELETE FROM " OPT_OUT_TABLE_NAME " WHERE time >= ? and time <= ?";
+
+ sql::Statement statement(db->GetUniqueStatement(kSql));
+ statement.BindInt64(0, (begin_time - base::Time()).InMicroseconds());
+ statement.BindInt64(1, (end_time - base::Time()).InMicroseconds());
+ statement.Run();
+}
+
+void AddEntrySync(bool opt_out,
+ const std::string& host_name,
+ int type,
+ base::Time now,
+ sql::Connection* db) {
+ sql::Transaction transaction(db);
+ if (!transaction.Begin())
+ return;
+ AddEntryToDataBase(db, opt_out, host_name, type, now);
+ MaybeEvictHostEntryFromDataBase(db, host_name);
+ transaction.Commit();
+}
+
+} // namespace
+
+OptOutStoreSQL::OptOutStoreSQL(
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+ const base::FilePath& path)
+ : io_task_runner_(io_task_runner),
+ background_task_runner_(background_task_runner),
+ db_file_path_(path) {}
+
+OptOutStoreSQL::~OptOutStoreSQL() {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+ if (db_) {
+ background_task_runner_->DeleteSoon(FROM_HERE, db_.release());
+ }
+}
+
+void OptOutStoreSQL::AddEntry(bool opt_out,
+ const std::string& host_name,
+ int type,
+ base::Time now) {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+ DCHECK(db_);
+ background_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AddEntrySync, opt_out, host_name, type, now, db_.get()));
+}
+
+void OptOutStoreSQL::ClearBlackList(base::Time begin_time,
+ base::Time end_time) {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+ DCHECK(db_);
+ background_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&ClearBlackListSync, db_.get(), begin_time, end_time));
+}
+
+void OptOutStoreSQL::LoadBlackList(
+ std::unique_ptr<BlacklistData> blacklist_data,
+ LoadBlackListCallback callback) {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+ if (!db_)
+ db_ = std::make_unique<sql::Connection>();
+ background_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&LoadBlackListSync, db_.get(), db_file_path_,
+ std::move(blacklist_data),
+ base::ThreadTaskRunnerHandle::Get(), std::move(callback)));
+}
+
+} // namespace blacklist
diff --git a/chromium/components/previews/core/previews_opt_out_store_sql.h b/chromium/components/blacklist/opt_out_blacklist/sql/opt_out_store_sql.h
index 0d3b6389e0f..a9a5618a9ef 100644
--- a/chromium/components/previews/core/previews_opt_out_store_sql.h
+++ b/chromium/components/blacklist/opt_out_blacklist/sql/opt_out_store_sql.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_PREVIEWS_PREVIEWS_OPT_OUT_STORE_SQL_H_
-#define COMPONENTS_PREVIEWS_PREVIEWS_OPT_OUT_STORE_SQL_H_
+#ifndef COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_SQL_OPT_OUT_STORE_SQL_H_
+#define COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_SQL_OPT_OUT_STORE_SQL_H_
#include <stdint.h>
@@ -15,38 +15,37 @@
#include "base/macros.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
-#include "components/previews/core/previews_experiments.h"
-#include "components/previews/core/previews_opt_out_store.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_store.h"
namespace base {
class SequencedTaskRunner;
class SingleThreadTaskRunner;
-}
+} // namespace base
namespace sql {
class Connection;
}
-namespace previews {
+namespace blacklist {
-// PreviewsOptOutStoreSQL is an instance of PreviewsOptOutStore
+// OptOutStoreSQL is an instance of OptOutStore
// which is implemented using a SQLite database.
-class PreviewsOptOutStoreSQL : public PreviewsOptOutStore {
+class OptOutStoreSQL : public OptOutStore {
public:
- PreviewsOptOutStoreSQL(
+ OptOutStoreSQL(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_refptr<base::SequencedTaskRunner> background_task_runner,
- const base::FilePath& database_dir,
- std::unique_ptr<PreviewsTypeList> enabled_previews);
- ~PreviewsOptOutStoreSQL() override;
-
- // PreviewsOptOutStore implementation:
- void AddPreviewNavigation(bool opt_out,
- const std::string& host_name,
- PreviewsType type,
- base::Time now) override;
+ const base::FilePath& database_dir);
+ ~OptOutStoreSQL() override;
+
+ // OptOutStore implementation:
+ void AddEntry(bool opt_out,
+ const std::string& host_name,
+ int type,
+ base::Time now) override;
void ClearBlackList(base::Time begin_time, base::Time end_time) override;
- void LoadBlackList(LoadBlackListCallback callback) override;
+ void LoadBlackList(std::unique_ptr<BlacklistData> blacklist_data,
+ LoadBlackListCallback callback) override;
private:
// Thread this object is accessed on.
@@ -61,12 +60,9 @@ class PreviewsOptOutStoreSQL : public PreviewsOptOutStore {
// SQL connection to the SQLite database.
std::unique_ptr<sql::Connection> db_;
- // All enabled previews and versions.
- const std::unique_ptr<PreviewsTypeList> enabled_previews_;
-
- DISALLOW_COPY_AND_ASSIGN(PreviewsOptOutStoreSQL);
+ DISALLOW_COPY_AND_ASSIGN(OptOutStoreSQL);
};
-} // namespace previews
+} // namespace blacklist
-#endif // COMPONENTS_PREVIEWS_PREVIEWS_OPT_OUT_STORE_SQL_H_
+#endif // COMPONENTS_BLACKLIST_OPT_OUT_BLACKLIST_SQL_OPT_OUT_STORE_SQL_H_
diff --git a/chromium/components/blacklist/opt_out_blacklist/sql/opt_out_store_sql_unittest.cc b/chromium/components/blacklist/opt_out_blacklist/sql/opt_out_store_sql_unittest.cc
new file mode 100644
index 00000000000..9caad5abcfe
--- /dev/null
+++ b/chromium/components/blacklist/opt_out_blacklist/sql/opt_out_store_sql_unittest.cc
@@ -0,0 +1,352 @@
+// 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/blacklist/opt_out_blacklist/sql/opt_out_store_sql.h"
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/simple_test_clock.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_item.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_store.h"
+#include "sql/test/test_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blacklist {
+
+namespace {
+
+const base::FilePath::CharType kOptOutFilename[] = FILE_PATH_LITERAL("OptOut");
+
+} // namespace
+
+class OptOutStoreSQLTest : public testing::Test {
+ public:
+ OptOutStoreSQLTest() {}
+ ~OptOutStoreSQLTest() override {}
+
+ // Called when |store_| is done loading.
+ void OnLoaded(std::unique_ptr<BlacklistData> blacklist_data) {
+ blacklist_data_ = std::move(blacklist_data);
+ }
+
+ // Initializes the store and get the data from it.
+ void Load() {
+ // Choose reasonable constants.
+ std::unique_ptr<BlacklistData> data = std::make_unique<BlacklistData>(
+ std::make_unique<BlacklistData::Policy>(base::TimeDelta::FromMinutes(5),
+ 1, 1),
+ std::make_unique<BlacklistData::Policy>(base::TimeDelta::FromDays(30),
+ 10, 6u),
+ std::make_unique<BlacklistData::Policy>(base::TimeDelta::FromDays(30),
+ 4, 2u),
+ nullptr, 10, allowed_types_);
+
+ store_->LoadBlackList(
+ std::move(data),
+ base::BindOnce(&OptOutStoreSQLTest::OnLoaded, base::Unretained(this)));
+ base::RunLoop().RunUntilIdle();
+ }
+
+ // Destroys the database connection and |store_|.
+ void DestroyStore() {
+ store_.reset();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ // Creates a store that operates on one thread.
+ void Create() {
+ store_ = std::make_unique<OptOutStoreSQL>(
+ base::ThreadTaskRunnerHandle::Get(),
+ base::ThreadTaskRunnerHandle::Get(),
+ temp_dir_.GetPath().Append(kOptOutFilename));
+ }
+
+ // Sets up initialization of |store_|.
+ void CreateAndLoad() {
+ Create();
+ Load();
+ }
+
+ void SetEnabledTypes(BlacklistData::AllowedTypesAndVersions allowed_types) {
+ allowed_types_ = std::move(allowed_types);
+ }
+
+ // Creates a directory for the test.
+ void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
+
+ // Delete |store_| if it hasn't been deleted.
+ void TearDown() override { DestroyStore(); }
+
+ protected:
+ base::MessageLoop message_loop_;
+
+ // The backing SQL store.
+ std::unique_ptr<OptOutStoreSQL> store_;
+
+ // The map returned from |store_|.
+ std::unique_ptr<BlacklistData> blacklist_data_;
+
+ // The directory for the database.
+ base::ScopedTempDir temp_dir_;
+
+ private:
+ BlacklistData::AllowedTypesAndVersions allowed_types_;
+};
+
+TEST_F(OptOutStoreSQLTest, TestErrorRecovery) {
+ // Creates the database and corrupt to test the recovery method.
+ std::string test_host = "host.com";
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+ store_->AddEntry(true, test_host, 1, base::Time::Now());
+ base::RunLoop().RunUntilIdle();
+ DestroyStore();
+
+ // Corrupts the database by adjusting the header size.
+ EXPECT_TRUE(sql::test::CorruptSizeInHeader(
+ temp_dir_.GetPath().Append(kOptOutFilename)));
+ base::RunLoop().RunUntilIdle();
+
+ allowed_types.clear();
+ allowed_types.insert({1, 0});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+ // The data should be recovered.
+ EXPECT_EQ(1U, blacklist_data_->black_list_item_host_map().size());
+ const auto& iter =
+ blacklist_data_->black_list_item_host_map().find(test_host);
+
+ EXPECT_NE(blacklist_data_->black_list_item_host_map().end(), iter);
+ EXPECT_EQ(1U, iter->second.OptOutRecordsSizeForTesting());
+}
+
+TEST_F(OptOutStoreSQLTest, TestPersistance) {
+ // Tests if data is stored as expected in the SQLite database.
+ std::string test_host = "host.com";
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+ base::Time now = base::Time::Now();
+ store_->AddEntry(true, test_host, 1, now);
+ base::RunLoop().RunUntilIdle();
+
+ // Replace the store effectively destroying the current one and forcing it
+ // to write its data to disk.
+ DestroyStore();
+
+ // Reload and test for persistence
+ allowed_types.clear();
+ allowed_types.insert({1, 0});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+ EXPECT_EQ(1U, blacklist_data_->black_list_item_host_map().size());
+ const auto& iter =
+ blacklist_data_->black_list_item_host_map().find(test_host);
+
+ EXPECT_NE(blacklist_data_->black_list_item_host_map().end(), iter);
+ EXPECT_EQ(1U, iter->second.OptOutRecordsSizeForTesting());
+ EXPECT_EQ(now, iter->second.most_recent_opt_out_time().value());
+}
+
+TEST_F(OptOutStoreSQLTest, TestMaxRows) {
+ // Tests that the number of rows are culled down to the row limit at each
+ // load.
+ std::string test_host_a = "host_a.com";
+ std::string test_host_b = "host_b.com";
+ std::string test_host_c = "host_c.com";
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ size_t row_limit = 2;
+ std::string row_limit_string = base::NumberToString(row_limit);
+ command_line->AppendSwitchASCII("max-opt-out-rows", row_limit_string);
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+ base::SimpleTestClock clock;
+
+ // Create three different entries with different hosts.
+ store_->AddEntry(true, test_host_a, 1, clock.Now());
+ clock.Advance(base::TimeDelta::FromSeconds(1));
+
+ store_->AddEntry(true, test_host_b, 1, clock.Now());
+ base::Time host_b_time = clock.Now();
+ clock.Advance(base::TimeDelta::FromSeconds(1));
+
+ store_->AddEntry(false, test_host_c, 1, clock.Now());
+ base::RunLoop().RunUntilIdle();
+ // Replace the store effectively destroying the current one and forcing it
+ // to write its data to disk.
+ DestroyStore();
+
+ // Reload and test for persistence
+ allowed_types.clear();
+ allowed_types.insert({1, 0});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+ // The delete happens after the load, so it is possible to load more than
+ // |row_limit| into the in memory map.
+ EXPECT_EQ(row_limit + 1, blacklist_data_->black_list_item_host_map().size());
+
+ DestroyStore();
+ allowed_types.clear();
+ allowed_types.insert({1, 0});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+
+ EXPECT_EQ(row_limit, blacklist_data_->black_list_item_host_map().size());
+ const auto& iter_host_b =
+ blacklist_data_->black_list_item_host_map().find(test_host_b);
+ const auto& iter_host_c =
+ blacklist_data_->black_list_item_host_map().find(test_host_c);
+
+ EXPECT_EQ(blacklist_data_->black_list_item_host_map().end(),
+ blacklist_data_->black_list_item_host_map().find(test_host_a));
+ EXPECT_NE(blacklist_data_->black_list_item_host_map().end(), iter_host_b);
+ EXPECT_NE(blacklist_data_->black_list_item_host_map().end(), iter_host_c);
+ EXPECT_EQ(host_b_time,
+ iter_host_b->second.most_recent_opt_out_time().value());
+ EXPECT_EQ(1U, iter_host_b->second.OptOutRecordsSizeForTesting());
+}
+
+TEST_F(OptOutStoreSQLTest, TestMaxRowsPerHost) {
+ // Tests that each host is limited to |row_limit| rows.
+ std::string test_host = "host.com";
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ size_t row_limit = 2;
+ std::string row_limit_string = base::NumberToString(row_limit);
+ command_line->AppendSwitchASCII("max-opt-out-rows-per-host",
+ row_limit_string);
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+ base::SimpleTestClock clock;
+
+ base::Time last_opt_out_time;
+ for (size_t i = 0; i < row_limit; i++) {
+ store_->AddEntry(true, test_host, 1, clock.Now());
+ last_opt_out_time = clock.Now();
+ clock.Advance(base::TimeDelta::FromSeconds(1));
+ }
+
+ clock.Advance(base::TimeDelta::FromSeconds(1));
+ store_->AddEntry(false, test_host, 1, clock.Now());
+
+ base::RunLoop().RunUntilIdle();
+ // Replace the store effectively destroying the current one and forcing it
+ // to write its data to disk.
+ DestroyStore();
+
+ // Reload and test for persistence.
+ allowed_types.clear();
+ allowed_types.insert({1, 0});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+
+ EXPECT_EQ(1U, blacklist_data_->black_list_item_host_map().size());
+ const auto& iter =
+ blacklist_data_->black_list_item_host_map().find(test_host);
+
+ EXPECT_NE(blacklist_data_->black_list_item_host_map().end(), iter);
+ EXPECT_EQ(last_opt_out_time, iter->second.most_recent_opt_out_time().value());
+ EXPECT_EQ(row_limit, iter->second.OptOutRecordsSizeForTesting());
+ clock.Advance(base::TimeDelta::FromSeconds(1));
+ // If both entries' opt out states are stored correctly, then this should not
+ // be black listed.
+ EXPECT_FALSE(iter->second.IsBlackListed(clock.Now()));
+}
+
+TEST_F(OptOutStoreSQLTest, TestTypesDisabledClearsBlacklistEntry) {
+ // Tests if data is cleared for type when it is disabled.
+ std::map<std::string, std::string> params;
+ std::string test_host = "host.com";
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 0});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+ base::Time now = base::Time::Now();
+ store_->AddEntry(true, test_host, 1, now);
+ base::RunLoop().RunUntilIdle();
+
+ // Force data write to database then reload it and verify black list entry
+ // is present.
+ DestroyStore();
+ allowed_types.clear();
+ allowed_types.insert({1, 0});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+ const auto& iter =
+ blacklist_data_->black_list_item_host_map().find(test_host);
+ EXPECT_NE(blacklist_data_->black_list_item_host_map().end(), iter);
+ EXPECT_EQ(1U, iter->second.OptOutRecordsSizeForTesting());
+
+ DestroyStore();
+ allowed_types.clear();
+ allowed_types.insert({2, 0});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+ const auto& iter2 =
+ blacklist_data_->black_list_item_host_map().find(test_host);
+ EXPECT_EQ(blacklist_data_->black_list_item_host_map().end(), iter2);
+
+ DestroyStore();
+ allowed_types.clear();
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+ const auto& iter3 =
+ blacklist_data_->black_list_item_host_map().find(test_host);
+ EXPECT_EQ(blacklist_data_->black_list_item_host_map().end(), iter3);
+}
+
+TEST_F(OptOutStoreSQLTest, TestTypesVersionUpdateClearsBlacklistEntry) {
+ // Tests if data is cleared for new version of type.
+ std::string test_host = "host.com";
+ BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types.insert({1, 1});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+ base::Time now = base::Time::Now();
+ store_->AddEntry(true, test_host, 1, now);
+ base::RunLoop().RunUntilIdle();
+
+ // Force data write to database then reload it and verify black list entry
+ // is present.
+ DestroyStore();
+ allowed_types.clear();
+ allowed_types.insert({1, 1});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+ const auto& iter =
+ blacklist_data_->black_list_item_host_map().find(test_host);
+ EXPECT_NE(blacklist_data_->black_list_item_host_map().end(), iter);
+ EXPECT_EQ(1U, iter->second.OptOutRecordsSizeForTesting());
+
+ DestroyStore();
+ allowed_types.clear();
+ allowed_types.insert({1, 2});
+ SetEnabledTypes(std::move(allowed_types));
+ CreateAndLoad();
+ const auto& iter2 =
+ blacklist_data_->black_list_item_host_map().find(test_host);
+ EXPECT_EQ(blacklist_data_->black_list_item_host_map().end(), iter2);
+}
+
+} // namespace blacklist
diff --git a/chromium/components/bookmarks/browser/BUILD.gn b/chromium/components/bookmarks/browser/BUILD.gn
index fd9c3db8772..ca6cf0cb33b 100644
--- a/chromium/components/bookmarks/browser/BUILD.gn
+++ b/chromium/components/bookmarks/browser/BUILD.gn
@@ -19,6 +19,8 @@ static_library("browser") {
"bookmark_undo_delegate.h",
"bookmark_undo_provider.h",
"bookmark_utils.h",
+ "history_bookmark_model.h",
+ "model_loader.h",
"scoped_group_bookmark_actions.h",
"startup_task_runner_service.h",
"titled_url_index.h",
@@ -41,6 +43,7 @@ static_library("browser") {
"bookmark_pasteboard_helper_mac.mm",
"bookmark_storage.cc",
"bookmark_utils.cc",
+ "model_loader.cc",
"scoped_group_bookmark_actions.cc",
"startup_task_runner_service.cc",
"titled_url_index.cc",
diff --git a/chromium/components/bookmarks/browser/bookmark_client.h b/chromium/components/bookmarks/browser/bookmark_client.h
index 348f6604507..a9996695685 100644
--- a/chromium/components/bookmarks/browser/bookmark_client.h
+++ b/chromium/components/bookmarks/browser/bookmark_client.h
@@ -89,6 +89,19 @@ class BookmarkClient {
// should give the client a means to temporarily disable those checks.
// http://crbug.com/49598
virtual bool CanBeEditedByUser(const BookmarkNode* node) = 0;
+
+ // Encodes the bookmark sync data into a string blob. It's used by the
+ // bookmark model to persist the sync metadata together with the bookmark
+ // model.
+ virtual std::string EncodeBookmarkSyncMetadata() = 0;
+
+ // Decodes a string represeting the sync metadata stored in |metadata_str|.
+ // The model calls this method after it has loaded the model data.
+ // |schedule_save_closure| is a repeating call back to trigger a model and
+ // metadata persistence process.
+ virtual void DecodeBookmarkSyncMetadata(
+ const std::string& metadata_str,
+ const base::RepeatingClosure& schedule_save_closure) = 0;
};
} // namespace bookmarks
diff --git a/chromium/components/bookmarks/browser/bookmark_codec.cc b/chromium/components/bookmarks/browser/bookmark_codec.cc
index 04adafb1f38..40a6a43a344 100644
--- a/chromium/components/bookmarks/browser/bookmark_codec.cc
+++ b/chromium/components/bookmarks/browser/bookmark_codec.cc
@@ -10,6 +10,7 @@
#include <memory>
#include <utility>
+#include "base/base64.h"
#include "base/json/json_string_value_serializer.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
@@ -43,6 +44,7 @@ const char BookmarkCodec::kSyncTransactionVersion[] =
"sync_transaction_version";
const char BookmarkCodec::kTypeURL[] = "url";
const char BookmarkCodec::kTypeFolder[] = "folder";
+const char BookmarkCodec::kSyncMetadata[] = "sync_metadata";
// Current version of the file.
static const int kCurrentVersion = 1;
@@ -57,12 +59,13 @@ BookmarkCodec::BookmarkCodec()
BookmarkCodec::~BookmarkCodec() {}
-std::unique_ptr<base::Value> BookmarkCodec::Encode(BookmarkModel* model) {
- return Encode(model->bookmark_bar_node(),
- model->other_node(),
- model->mobile_node(),
- model->root_node()->GetMetaInfoMap(),
- model->root_node()->sync_transaction_version());
+std::unique_ptr<base::Value> BookmarkCodec::Encode(
+ BookmarkModel* model,
+ const std::string& sync_metadata_str) {
+ return Encode(model->bookmark_bar_node(), model->other_node(),
+ model->mobile_node(), model->root_node()->GetMetaInfoMap(),
+ model->root_node()->sync_transaction_version(),
+ sync_metadata_str);
}
std::unique_ptr<base::Value> BookmarkCodec::Encode(
@@ -70,7 +73,8 @@ std::unique_ptr<base::Value> BookmarkCodec::Encode(
const BookmarkNode* other_folder_node,
const BookmarkNode* mobile_folder_node,
const BookmarkNode::MetaInfoMap* model_meta_info_map,
- int64_t sync_transaction_version) {
+ int64_t sync_transaction_version,
+ const std::string& sync_metadata_str) {
ids_reassigned_ = false;
InitializeChecksum();
auto roots = std::make_unique<base::DictionaryValue>();
@@ -92,14 +96,20 @@ std::unique_ptr<base::Value> BookmarkCodec::Encode(
stored_checksum_ = computed_checksum_;
main->SetString(kChecksumKey, computed_checksum_);
main->Set(kRootsKey, std::move(roots));
+ if (!sync_metadata_str.empty()) {
+ std::string sync_metadata_str_base64;
+ base::Base64Encode(sync_metadata_str, &sync_metadata_str_base64);
+ main->SetString(kSyncMetadata, std::move(sync_metadata_str_base64));
+ }
return std::move(main);
}
-bool BookmarkCodec::Decode(BookmarkNode* bb_node,
+bool BookmarkCodec::Decode(const base::Value& value,
+ BookmarkNode* bb_node,
BookmarkNode* other_folder_node,
BookmarkNode* mobile_folder_node,
int64_t* max_id,
- const base::Value& value) {
+ std::string* sync_metadata_str) {
ids_.clear();
ids_reassigned_ = false;
ids_valid_ = true;
@@ -107,7 +117,7 @@ bool BookmarkCodec::Decode(BookmarkNode* bb_node,
stored_checksum_.clear();
InitializeChecksum();
bool success = DecodeHelper(bb_node, other_folder_node, mobile_folder_node,
- value);
+ value, sync_metadata_str);
FinalizeChecksum();
// If either the checksums differ or some IDs were missing/not unique,
// reassign IDs.
@@ -167,7 +177,8 @@ std::unique_ptr<base::Value> BookmarkCodec::EncodeMetaInfo(
bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node,
BookmarkNode* other_folder_node,
BookmarkNode* mobile_folder_node,
- const base::Value& value) {
+ const base::Value& value,
+ std::string* sync_metadata_str) {
const base::DictionaryValue* d_value = nullptr;
if (!value.GetAsDictionary(&d_value))
return false; // Unexpected type.
@@ -234,6 +245,12 @@ bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node,
&model_sync_transaction_version_))
return false;
+ std::string sync_metadata_str_base64;
+ if (sync_metadata_str &&
+ d_value->GetString(kSyncMetadata, &sync_metadata_str_base64)) {
+ base::Base64Decode(sync_metadata_str_base64, sync_metadata_str);
+ }
+
// Need to reset the type as decoding resets the type to FOLDER. Similarly
// we need to reset the title as the title is persisted and restored from
// the file.
diff --git a/chromium/components/bookmarks/browser/bookmark_codec.h b/chromium/components/bookmarks/browser/bookmark_codec.h
index 313729ef96d..e3fee209d7e 100644
--- a/chromium/components/bookmarks/browser/bookmark_codec.h
+++ b/chromium/components/bookmarks/browser/bookmark_codec.h
@@ -42,7 +42,8 @@ class BookmarkCodec {
// returned object. This is invoked to encode the contents of the bookmark bar
// model and is currently a convenience to invoking Encode that takes the
// bookmark bar node and other folder node.
- std::unique_ptr<base::Value> Encode(BookmarkModel* model);
+ std::unique_ptr<base::Value> Encode(BookmarkModel* model,
+ const std::string& sync_metadata_str);
// Encodes the bookmark bar and other folders returning the JSON value.
std::unique_ptr<base::Value> Encode(
@@ -50,18 +51,20 @@ class BookmarkCodec {
const BookmarkNode* other_folder_node,
const BookmarkNode* mobile_folder_node,
const BookmarkNode::MetaInfoMap* model_meta_info_map,
- int64_t sync_transaction_version);
+ int64_t sync_transaction_version,
+ const std::string& sync_metadata_str);
// Decodes the previously encoded value to the specified nodes as well as
// setting |max_node_id| to the greatest node id. Returns true on success,
// false otherwise. If there is an error (such as unexpected version) all
// children are removed from the bookmark bar and other folder nodes. On exit
// |max_node_id| is set to the max id of the nodes.
- bool Decode(BookmarkNode* bb_node,
+ bool Decode(const base::Value& value,
+ BookmarkNode* bb_node,
BookmarkNode* other_folder_node,
BookmarkNode* mobile_folder_node,
int64_t* max_node_id,
- const base::Value& value);
+ std::string* sync_metadata_str);
// Returns the checksum computed during last encoding/decoding call.
const std::string& computed_checksum() const { return computed_checksum_; }
@@ -103,6 +106,9 @@ class BookmarkCodec {
static const char kChildrenKey[];
static const char kMetaInfo[];
static const char kSyncTransactionVersion[];
+ // Allows the BookmarkClient to read and a write a string blob from the JSON
+ // file. That string captures the bookmarks sync metadata.
+ static const char kSyncMetadata[];
// Possible values for kTypeKey.
static const char kTypeURL[];
@@ -120,7 +126,8 @@ class BookmarkCodec {
bool DecodeHelper(BookmarkNode* bb_node,
BookmarkNode* other_folder_node,
BookmarkNode* mobile_folder_node,
- const base::Value& value);
+ const base::Value& value,
+ std::string* sync_metadata_str);
// Decodes the children of the specified node. Returns true on success.
bool DecodeChildren(const base::ListValue& child_value_list,
diff --git a/chromium/components/bookmarks/browser/bookmark_codec_unittest.cc b/chromium/components/bookmarks/browser/bookmark_codec_unittest.cc
index 11d51d5adc6..8040fd6e067 100644
--- a/chromium/components/bookmarks/browser/bookmark_codec_unittest.cc
+++ b/chromium/components/bookmarks/browser/bookmark_codec_unittest.cc
@@ -145,13 +145,17 @@ class BookmarkCodecTest : public testing::Test {
child_value->GetAsDictionary(result_value);
}
- base::Value* EncodeHelper(BookmarkModel* model, std::string* checksum) {
+ std::unique_ptr<base::Value> EncodeHelper(
+ BookmarkModel* model,
+ const std::string& sync_metadata_str,
+ std::string* checksum) {
BookmarkCodec encoder;
// Computed and stored checksums should be empty.
EXPECT_EQ("", encoder.computed_checksum());
EXPECT_EQ("", encoder.stored_checksum());
- std::unique_ptr<base::Value> value(encoder.Encode(model));
+ std::unique_ptr<base::Value> value(
+ encoder.Encode(model, sync_metadata_str));
const std::string& computed_checksum = encoder.computed_checksum();
const std::string& stored_checksum = encoder.stored_checksum();
@@ -161,18 +165,18 @@ class BookmarkCodecTest : public testing::Test {
EXPECT_EQ(computed_checksum, stored_checksum);
*checksum = computed_checksum;
- return value.release();
+ return value;
}
bool Decode(BookmarkCodec* codec,
+ const base::Value& value,
BookmarkModel* model,
- const base::Value& value) {
+ std::string* sync_metadata_str) {
int64_t max_id;
- bool result = codec->Decode(AsMutable(model->bookmark_bar_node()),
+ bool result = codec->Decode(value, AsMutable(model->bookmark_bar_node()),
AsMutable(model->other_node()),
- AsMutable(model->mobile_node()),
- &max_id,
- value);
+ AsMutable(model->mobile_node()), &max_id,
+ sync_metadata_str);
model->set_next_node_id(max_id);
AsMutable(model->root_node())->SetMetaInfoMap(codec->model_meta_info_map());
AsMutable(model->root_node())
@@ -181,17 +185,20 @@ class BookmarkCodecTest : public testing::Test {
return result;
}
- BookmarkModel* DecodeHelper(const base::Value& value,
- const std::string& expected_stored_checksum,
- std::string* computed_checksum,
- bool expected_changes) {
+ std::unique_ptr<BookmarkModel> DecodeHelper(
+ const base::Value& value,
+ const std::string& expected_stored_checksum,
+ std::string* computed_checksum,
+ bool expected_changes,
+ std::string* sync_metadata_str) {
BookmarkCodec decoder;
// Computed and stored checksums should be empty.
EXPECT_EQ("", decoder.computed_checksum());
EXPECT_EQ("", decoder.stored_checksum());
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
- EXPECT_TRUE(Decode(&decoder, model.get(), value));
+ EXPECT_TRUE(Decode(&decoder, value, model.get(),
+ /*sync_metadata_str=*/sync_metadata_str));
*computed_checksum = decoder.computed_checksum();
const std::string& stored_checksum = decoder.stored_checksum();
@@ -210,7 +217,7 @@ class BookmarkCodecTest : public testing::Test {
else
EXPECT_EQ(*computed_checksum, stored_checksum);
- return model.release();
+ return model;
}
void CheckIDs(const BookmarkNode* node, std::set<int64_t>* assigned_ids) {
@@ -233,14 +240,16 @@ class BookmarkCodecTest : public testing::Test {
TEST_F(BookmarkCodecTest, ChecksumEncodeDecodeTest) {
std::unique_ptr<BookmarkModel> model_to_encode(CreateTestModel1());
std::string enc_checksum;
- std::unique_ptr<base::Value> value(
- EncodeHelper(model_to_encode.get(), &enc_checksum));
+ std::unique_ptr<base::Value> value =
+ EncodeHelper(model_to_encode.get(), /*sync_metadata_str=*/std::string(),
+ &enc_checksum);
EXPECT_TRUE(value.get() != nullptr);
std::string dec_checksum;
- std::unique_ptr<BookmarkModel> decoded_model(
- DecodeHelper(*value.get(), enc_checksum, &dec_checksum, false));
+ std::unique_ptr<BookmarkModel> decoded_model =
+ DecodeHelper(*value.get(), enc_checksum, &dec_checksum, false,
+ /*sync_metadata_str=*/nullptr);
}
TEST_F(BookmarkCodecTest, ChecksumEncodeIdenticalModelsTest) {
@@ -248,14 +257,14 @@ TEST_F(BookmarkCodecTest, ChecksumEncodeIdenticalModelsTest) {
// as the data is the same.
std::unique_ptr<BookmarkModel> model1(CreateTestModel1());
std::string enc_checksum1;
- std::unique_ptr<base::Value> value1(
- EncodeHelper(model1.get(), &enc_checksum1));
+ std::unique_ptr<base::Value> value1 = EncodeHelper(
+ model1.get(), /*sync_metadata_str=*/std::string(), &enc_checksum1);
EXPECT_TRUE(value1.get() != nullptr);
std::unique_ptr<BookmarkModel> model2(CreateTestModel1());
std::string enc_checksum2;
- std::unique_ptr<base::Value> value2(
- EncodeHelper(model2.get(), &enc_checksum2));
+ std::unique_ptr<base::Value> value2 = EncodeHelper(
+ model2.get(), /*sync_metadata_str=*/std::string(), &enc_checksum2);
EXPECT_TRUE(value2.get() != nullptr);
ASSERT_EQ(enc_checksum1, enc_checksum2);
@@ -264,8 +273,9 @@ TEST_F(BookmarkCodecTest, ChecksumEncodeIdenticalModelsTest) {
TEST_F(BookmarkCodecTest, ChecksumManualEditTest) {
std::unique_ptr<BookmarkModel> model_to_encode(CreateTestModel1());
std::string enc_checksum;
- std::unique_ptr<base::Value> value(
- EncodeHelper(model_to_encode.get(), &enc_checksum));
+ std::unique_ptr<base::Value> value =
+ EncodeHelper(model_to_encode.get(), /*sync_metadata_str=*/std::string(),
+ &enc_checksum);
EXPECT_TRUE(value.get() != nullptr);
@@ -277,13 +287,15 @@ TEST_F(BookmarkCodecTest, ChecksumManualEditTest) {
child1_value->SetString(BookmarkCodec::kNameKey, title + "1");
std::string dec_checksum;
- std::unique_ptr<BookmarkModel> decoded_model1(
- DecodeHelper(*value.get(), enc_checksum, &dec_checksum, true));
+ std::unique_ptr<BookmarkModel> decoded_model1 =
+ DecodeHelper(*value.get(), enc_checksum, &dec_checksum, true,
+ /*sync_metadata_str=*/nullptr);
// Undo the change and make sure the checksum is same as original.
child1_value->SetString(BookmarkCodec::kNameKey, title);
- std::unique_ptr<BookmarkModel> decoded_model2(
- DecodeHelper(*value.get(), enc_checksum, &dec_checksum, false));
+ std::unique_ptr<BookmarkModel> decoded_model2 =
+ DecodeHelper(*value.get(), enc_checksum, &dec_checksum, false,
+ /*sync_metadata_str=*/nullptr);
}
TEST_F(BookmarkCodecTest, ChecksumManualEditIDsTest) {
@@ -295,8 +307,9 @@ TEST_F(BookmarkCodecTest, ChecksumManualEditIDsTest) {
ASSERT_GT(bb_child_count, 1);
std::string enc_checksum;
- std::unique_ptr<base::Value> value(
- EncodeHelper(model_to_encode.get(), &enc_checksum));
+ std::unique_ptr<base::Value> value =
+ EncodeHelper(model_to_encode.get(), /*sync_metadata_str=*/std::string(),
+ &enc_checksum);
EXPECT_TRUE(value.get() != nullptr);
@@ -310,8 +323,9 @@ TEST_F(BookmarkCodecTest, ChecksumManualEditIDsTest) {
}
std::string dec_checksum;
- std::unique_ptr<BookmarkModel> decoded_model(
- DecodeHelper(*value.get(), enc_checksum, &dec_checksum, true));
+ std::unique_ptr<BookmarkModel> decoded_model =
+ DecodeHelper(*value.get(), enc_checksum, &dec_checksum, true,
+ /*sync_metadata_str=*/nullptr);
ExpectIDsUnique(decoded_model.get());
@@ -329,12 +343,13 @@ TEST_F(BookmarkCodecTest, PersistIDsTest) {
std::unique_ptr<BookmarkModel> model_to_encode(CreateTestModel3());
BookmarkCodec encoder;
std::unique_ptr<base::Value> model_value(
- encoder.Encode(model_to_encode.get()));
+ encoder.Encode(model_to_encode.get(), std::string()));
std::unique_ptr<BookmarkModel> decoded_model(
TestBookmarkClient::CreateModel());
BookmarkCodec decoder;
- ASSERT_TRUE(Decode(&decoder, decoded_model.get(), *model_value.get()));
+ ASSERT_TRUE(Decode(&decoder, *model_value.get(), decoded_model.get(),
+ /*sync_metadata_str=*/nullptr));
ASSERT_NO_FATAL_FAILURE(
AssertModelsEqual(model_to_encode.get(), decoded_model.get()));
@@ -352,12 +367,13 @@ TEST_F(BookmarkCodecTest, PersistIDsTest) {
BookmarkCodec encoder2;
std::unique_ptr<base::Value> model_value2(
- encoder2.Encode(decoded_model.get()));
+ encoder2.Encode(decoded_model.get(), std::string()));
std::unique_ptr<BookmarkModel> decoded_model2(
TestBookmarkClient::CreateModel());
BookmarkCodec decoder2;
- ASSERT_TRUE(Decode(&decoder2, decoded_model2.get(), *model_value2.get()));
+ ASSERT_TRUE(Decode(&decoder2, *model_value2.get(), decoded_model2.get(),
+ /*sync_metadata_str=*/nullptr));
ASSERT_NO_FATAL_FAILURE(
AssertModelsEqual(decoded_model.get(), decoded_model2.get()));
}
@@ -374,7 +390,8 @@ TEST_F(BookmarkCodecTest, CanDecodeModelWithoutMobileBookmarks) {
std::unique_ptr<BookmarkModel> decoded_model(
TestBookmarkClient::CreateModel());
BookmarkCodec decoder;
- ASSERT_TRUE(Decode(&decoder, decoded_model.get(), *root.get()));
+ ASSERT_TRUE(Decode(&decoder, *root.get(), decoded_model.get(),
+ /*sync_metadata_str=*/nullptr));
ExpectIDsUnique(decoded_model.get());
const BookmarkNode* bbn = decoded_model->bookmark_bar_node();
@@ -411,11 +428,13 @@ TEST_F(BookmarkCodecTest, EncodeAndDecodeMetaInfo) {
model->SetNodeMetaInfo(
model->bookmark_bar_node()->GetChild(0), "node_info", "value2");
std::string checksum;
- std::unique_ptr<base::Value> value(EncodeHelper(model.get(), &checksum));
+ std::unique_ptr<base::Value> value =
+ EncodeHelper(model.get(), /*sync_metadata_str=*/std::string(), &checksum);
ASSERT_TRUE(value.get() != nullptr);
// Decode and check for meta info.
- model.reset(DecodeHelper(*value, checksum, &checksum, false));
+ model = DecodeHelper(*value, checksum, &checksum, false,
+ /*sync_metadata_str=*/nullptr);
std::string meta_value;
EXPECT_TRUE(model->root_node()->GetMetaInfo("model_info", &meta_value));
EXPECT_EQ("value1", meta_value);
@@ -436,11 +455,13 @@ TEST_F(BookmarkCodecTest, EncodeAndDecodeSyncTransactionVersion) {
model->SetNodeSyncTransactionVersion(bbn->GetChild(1), 42);
std::string checksum;
- std::unique_ptr<base::Value> value(EncodeHelper(model.get(), &checksum));
+ std::unique_ptr<base::Value> value =
+ EncodeHelper(model.get(), /*sync_metadata_str=*/std::string(), &checksum);
ASSERT_TRUE(value.get() != nullptr);
// Decode and verify.
- model.reset(DecodeHelper(*value, checksum, &checksum, false));
+ model = DecodeHelper(*value, checksum, &checksum, false,
+ /*sync_metadata_str=*/nullptr);
EXPECT_EQ(1, model->root_node()->sync_transaction_version());
bbn = model->bookmark_bar_node();
EXPECT_EQ(42, bbn->GetChild(1)->sync_transaction_version());
@@ -461,7 +482,8 @@ TEST_F(BookmarkCodecTest, CanDecodeMetaInfoAsString) {
std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel());
BookmarkCodec decoder;
- ASSERT_TRUE(Decode(&decoder, model.get(), *root.get()));
+ ASSERT_TRUE(Decode(&decoder, *root.get(), model.get(),
+ /*sync_metadata_str=*/nullptr));
EXPECT_EQ(1, model->root_node()->sync_transaction_version());
const BookmarkNode* bbn = model->bookmark_bar_node();
@@ -485,4 +507,20 @@ TEST_F(BookmarkCodecTest, CanDecodeMetaInfoAsString) {
EXPECT_EQ("value3", meta_value);
}
+TEST_F(BookmarkCodecTest, EncodeAndDecodeSyncMetadata) {
+ std::unique_ptr<BookmarkModel> model(CreateTestModel1());
+
+ // Since metadata str serialized proto, it could contain no ASCII characters.
+ std::string sync_metadata_str("a/2'\"");
+ std::string checksum;
+ std::unique_ptr<base::Value> value =
+ EncodeHelper(model.get(), sync_metadata_str, &checksum);
+ ASSERT_TRUE(value.get() != nullptr);
+
+ std::string decoded_sync_metadata_str;
+ // Decode and verify.
+ DecodeHelper(*value, checksum, &checksum, false, &decoded_sync_metadata_str);
+ EXPECT_EQ(sync_metadata_str, decoded_sync_metadata_str);
+}
+
} // namespace bookmarks
diff --git a/chromium/components/bookmarks/browser/bookmark_model.cc b/chromium/components/bookmarks/browser/bookmark_model.cc
index 9d316114bfb..a448ffc612f 100644
--- a/chromium/components/bookmarks/browser/bookmark_model.cc
+++ b/chromium/components/bookmarks/browser/bookmark_model.cc
@@ -23,6 +23,7 @@
#include "components/bookmarks/browser/bookmark_storage.h"
#include "components/bookmarks/browser/bookmark_undo_delegate.h"
#include "components/bookmarks/browser/bookmark_utils.h"
+#include "components/bookmarks/browser/model_loader.h"
#include "components/bookmarks/browser/titled_url_index.h"
#include "components/bookmarks/browser/titled_url_match.h"
#include "components/bookmarks/browser/typed_count_sorter.h"
@@ -113,23 +114,6 @@ class EmptyUndoDelegate : public BookmarkUndoDelegate {
DISALLOW_COPY_AND_ASSIGN(EmptyUndoDelegate);
};
-void FinishedLoadOnMainThread(
- base::OnceCallback<void(std::unique_ptr<BookmarkLoadDetails>)> callback,
- std::unique_ptr<BookmarkLoadDetails> details) {
- std::move(callback).Run(std::move(details));
-}
-
-void DoLoadOnBackgroundThread(
- const base::FilePath& profile_path,
- scoped_refptr<base::SequencedTaskRunner> result_task_runner,
- base::OnceCallback<void(std::unique_ptr<BookmarkLoadDetails>)> callback,
- std::unique_ptr<BookmarkLoadDetails> details) {
- LoadBookmarks(profile_path, details.get());
- result_task_runner->PostTask(
- FROM_HERE, base::BindOnce(&FinishedLoadOnMainThread, std::move(callback),
- std::move(details)));
-}
-
} // namespace
// BookmarkModel --------------------------------------------------------------
@@ -155,15 +139,6 @@ BookmarkModel::~BookmarkModel() {
}
}
-void BookmarkModel::Shutdown() {
- if (loaded_)
- return;
-
- // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
- // details. It is also called when the BookmarkModel is deleted.
- loaded_signal_.Signal();
-}
-
void BookmarkModel::Load(
PrefService* pref_service,
const base::FilePath& profile_path,
@@ -178,14 +153,11 @@ void BookmarkModel::Load(
store_ = std::make_unique<BookmarkStorage>(this, profile_path,
io_task_runner.get());
- auto done_loading_callback =
- base::BindOnce(&BookmarkModel::DoneLoading, weak_factory_.GetWeakPtr());
- io_task_runner->PostTask(
- FROM_HERE,
- base::BindOnce(&DoLoadOnBackgroundThread,
- profile_path.Append(kBookmarksFileName), ui_task_runner,
- std::move(done_loading_callback),
- std::make_unique<BookmarkLoadDetails>(client_.get())));
+ // Creating ModelLoader schedules the load on |io_task_runner|.
+ model_loader_ = base::MakeRefCounted<ModelLoader>(
+ profile_path.Append(kBookmarksFileName), io_task_runner.get(),
+ std::make_unique<BookmarkLoadDetails>(client_.get()),
+ base::BindOnce(&BookmarkModel::DoneLoading, weak_factory_.GetWeakPtr()));
}
void BookmarkModel::AddObserver(BookmarkModelObserver* observer) {
@@ -591,10 +563,6 @@ void BookmarkModel::GetBookmarks(std::vector<UrlAndTitle>* bookmarks) {
url_index_->GetBookmarks(bookmarks);
}
-void BookmarkModel::BlockTillLoaded() {
- loaded_signal_.Wait();
-}
-
const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
int index,
const base::string16& title) {
@@ -827,7 +795,7 @@ void BookmarkModel::DoneLoading(std::unique_ptr<BookmarkLoadDetails> details) {
}
index_ = details->owned_index();
- url_index_ = details->owned_url_index();
+ url_index_ = details->url_index();
root_ = details->root_node();
// See declaration for details on why |owned_root_| is reset.
owned_root_.reset();
@@ -846,8 +814,11 @@ void BookmarkModel::DoneLoading(std::unique_ptr<BookmarkLoadDetails> details) {
details->model_sync_transaction_version());
loaded_ = true;
-
- loaded_signal_.Signal();
+ client_->DecodeBookmarkSyncMetadata(
+ details->sync_metadata_str(),
+ store_ ? base::BindRepeating(&BookmarkStorage::ScheduleSave,
+ base::Unretained(store_.get()))
+ : base::DoNothing());
// Notify our direct observers.
for (BookmarkModelObserver& observer : observers_)
diff --git a/chromium/components/bookmarks/browser/bookmark_model.h b/chromium/components/bookmarks/browser/bookmark_model.h
index 868d8baf75e..cbfe5833527 100644
--- a/chromium/components/bookmarks/browser/bookmark_model.h
+++ b/chromium/components/bookmarks/browser/bookmark_model.h
@@ -15,11 +15,10 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/strings/string16.h"
-#include "base/synchronization/waitable_event.h"
#include "components/bookmarks/browser/bookmark_client.h"
#include "components/bookmarks/browser/bookmark_node.h"
#include "components/bookmarks/browser/bookmark_undo_provider.h"
@@ -50,6 +49,7 @@ class BookmarkLoadDetails;
class BookmarkModelObserver;
class BookmarkStorage;
class BookmarkUndoDelegate;
+class ModelLoader;
class ScopedGroupBookmarkActions;
class TestBookmarkClient;
class TitledUrlIndex;
@@ -74,9 +74,6 @@ class BookmarkModel : public BookmarkUndoProvider,
explicit BookmarkModel(std::unique_ptr<BookmarkClient> client);
~BookmarkModel() override;
- // KeyedService:
- void Shutdown() override;
-
// Loads the bookmarks. This is called upon creation of the BookmarkModel. You
// need not invoke this directly. All load operations will be executed on
// |io_task_runner|. |ui_task_runner| is the task runner the model runs on.
@@ -88,6 +85,9 @@ class BookmarkModel : public BookmarkUndoProvider,
// Returns true if the model finished loading.
bool loaded() const { return loaded_; }
+ // Returns the object responsible for tracking loading.
+ ModelLoader* model_loader() { return model_loader_.get(); }
+
// Returns the root node. The 'bookmark bar' node and 'other' node are
// children of the root node.
const BookmarkNode* root_node() const { return root_; }
@@ -192,10 +192,6 @@ class BookmarkModel : public BookmarkUndoProvider,
// If not on the main thread you *must* invoke BlockTillLoaded first.
void GetBookmarks(std::vector<UrlAndTitle>* urls);
- // Blocks until loaded. This is intended for usage on a thread other than
- // the main thread.
- void BlockTillLoaded();
-
// Adds a new folder node at the specified position.
const BookmarkNode* AddFolder(const BookmarkNode* parent,
int index,
@@ -413,9 +409,13 @@ class BookmarkModel : public BookmarkUndoProvider,
std::unique_ptr<BookmarkStorage> store_;
std::unique_ptr<TitledUrlIndex> index_;
- std::unique_ptr<UrlIndex> url_index_;
- base::WaitableEvent loaded_signal_;
+ // Owned by |model_loader_|.
+ // WARNING: in some tests this does *not* refer to
+ // |ModelLoader::history_bookmark_model_|. This is because some tests
+ // directly call DoneLoading().
+ // TODO: this is confusing, fix tests not to circumvent ModelLoader.
+ scoped_refptr<UrlIndex> url_index_;
// See description of IsDoingExtensiveChanges above.
int extensive_changes_ = 0;
@@ -427,6 +427,8 @@ class BookmarkModel : public BookmarkUndoProvider,
BookmarkUndoDelegate* undo_delegate_ = nullptr;
std::unique_ptr<BookmarkUndoDelegate> empty_undo_delegate_;
+ scoped_refptr<ModelLoader> model_loader_;
+
base::WeakPtrFactory<BookmarkModel> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(BookmarkModel);
diff --git a/chromium/components/bookmarks/browser/bookmark_model_unittest.cc b/chromium/components/bookmarks/browser/bookmark_model_unittest.cc
index 12472c7795c..adb042999f3 100644
--- a/chromium/components/bookmarks/browser/bookmark_model_unittest.cc
+++ b/chromium/components/bookmarks/browser/bookmark_model_unittest.cc
@@ -19,6 +19,7 @@
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/run_loop.h"
+#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
@@ -1358,8 +1359,7 @@ class BookmarkModelFaviconTest : public testing::Test,
}
bool WasNodeUpdated(const BookmarkNode* node) {
- return std::find(updated_nodes_.begin(), updated_nodes_.end(), node) !=
- updated_nodes_.end();
+ return base::ContainsValue(updated_nodes_, node);
}
void ClearUpdatedNodes() {
diff --git a/chromium/components/bookmarks/browser/bookmark_node_data.cc b/chromium/components/bookmarks/browser/bookmark_node_data.cc
index 60327e113a6..d8301de4c03 100644
--- a/chromium/components/bookmarks/browser/bookmark_node_data.cc
+++ b/chromium/components/bookmarks/browser/bookmark_node_data.cc
@@ -162,6 +162,12 @@ void BookmarkNodeData::WriteToClipboard(ui::ClipboardType clipboard_type) {
clipboard_type == ui::CLIPBOARD_TYPE_SELECTION);
ui::ScopedClipboardWriter scw(clipboard_type);
+#if defined(OS_WIN)
+ const base::string16 kEOL(L"\r\n");
+#else
+ const base::string16 kEOL = base::ASCIIToUTF16("\n");
+#endif
+
// If there is only one element and it is a URL, write the URL to the
// clipboard.
if (has_single_url()) {
@@ -185,7 +191,7 @@ void BookmarkNodeData::WriteToClipboard(ui::ClipboardType clipboard_type) {
// and folders.
base::string16 text;
for (size_t i = 0; i < size(); i++) {
- text += i == 0 ? base::ASCIIToUTF16("") : base::ASCIIToUTF16("\n");
+ text += i == 0 ? base::ASCIIToUTF16("") : kEOL;
if (!elements[i].is_url) {
// Then it's a folder. Only copy the name of the folder.
const base::string16 title = elements[i].title;
diff --git a/chromium/components/bookmarks/browser/bookmark_node_data_unittest.cc b/chromium/components/bookmarks/browser/bookmark_node_data_unittest.cc
index 12fe592f2d5..1b545b7c9bf 100644
--- a/chromium/components/bookmarks/browser/bookmark_node_data_unittest.cc
+++ b/chromium/components/bookmarks/browser/bookmark_node_data_unittest.cc
@@ -11,6 +11,7 @@
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_task_environment.h"
+#include "build/build_config.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/test/bookmark_test_helpers.h"
#include "components/bookmarks/test/test_bookmark_client.h"
@@ -310,7 +311,11 @@ TEST_F(BookmarkNodeDataTest, WriteToClipboardMultipleURLs) {
// Now read the data back in.
base::string16 combined_text;
+#if defined(OS_WIN)
+ base::string16 new_line = base::ASCIIToUTF16("\r\n");
+#else
base::string16 new_line = base::ASCIIToUTF16("\n");
+#endif
combined_text = base::UTF8ToUTF16(url.spec()) + new_line
+ base::UTF8ToUTF16(url2.spec());
base::string16 clipboard_result;
@@ -369,7 +374,11 @@ TEST_F(BookmarkNodeDataTest, WriteToClipboardFolderAndURL) {
// Now read the data back in.
base::string16 combined_text;
+#if defined(OS_WIN)
+ base::string16 new_line = base::ASCIIToUTF16("\r\n");
+#else
base::string16 new_line = base::ASCIIToUTF16("\n");
+#endif
base::string16 folder_title = ASCIIToUTF16("g1");
combined_text = base::ASCIIToUTF16(url.spec()) + new_line + folder_title;
base::string16 clipboard_result;
diff --git a/chromium/components/bookmarks/browser/bookmark_storage.cc b/chromium/components/bookmarks/browser/bookmark_storage.cc
index 1633ba1df2a..0cf205f038b 100644
--- a/chromium/components/bookmarks/browser/bookmark_storage.cc
+++ b/chromium/components/bookmarks/browser/bookmark_storage.cc
@@ -74,10 +74,13 @@ void LoadBookmarks(const base::FilePath& path, BookmarkLoadDetails* details) {
// Building the index can take a while, so we do it on the background
// thread.
int64_t max_node_id = 0;
+ std::string sync_metadata_str;
BookmarkCodec codec;
TimeTicks start_time = TimeTicks::Now();
- codec.Decode(details->bb_node(), details->other_folder_node(),
- details->mobile_folder_node(), &max_node_id, *root);
+ codec.Decode(*root, details->bb_node(), details->other_folder_node(),
+ details->mobile_folder_node(), &max_node_id,
+ &sync_metadata_str);
+ details->set_sync_metadata_str(std::move(sync_metadata_str));
details->set_max_id(std::max(max_node_id, details->max_id()));
details->set_computed_checksum(codec.computed_checksum());
details->set_stored_checksum(codec.stored_checksum());
@@ -155,7 +158,7 @@ bool BookmarkLoadDetails::LoadExtraNodes() {
}
void BookmarkLoadDetails::CreateUrlIndex() {
- url_index_ = std::make_unique<UrlIndex>(std::move(root_node_));
+ url_index_ = base::MakeRefCounted<UrlIndex>(std::move(root_node_));
}
BookmarkPermanentNode* BookmarkLoadDetails::CreatePermanentNode(
@@ -243,7 +246,8 @@ void BookmarkStorage::BookmarkModelDeleted() {
bool BookmarkStorage::SerializeData(std::string* output) {
BookmarkCodec codec;
- std::unique_ptr<base::Value> value(codec.Encode(model_));
+ std::unique_ptr<base::Value> value(
+ codec.Encode(model_, model_->client()->EncodeBookmarkSyncMetadata()));
JSONStringValueSerializer serializer(output);
serializer.set_pretty_print(true);
return serializer.Serialize(*(value.get()));
diff --git a/chromium/components/bookmarks/browser/bookmark_storage.h b/chromium/components/bookmarks/browser/bookmark_storage.h
index 08df5bb65d8..1abc6da1079 100644
--- a/chromium/components/bookmarks/browser/bookmark_storage.h
+++ b/chromium/components/bookmarks/browser/bookmark_storage.h
@@ -16,6 +16,7 @@
#include "base/files/important_file_writer.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "components/bookmarks/browser/bookmark_node.h"
#include "components/bookmarks/browser/titled_url_index.h"
@@ -103,8 +104,15 @@ class BookmarkLoadDetails {
void set_ids_reassigned(bool value) { ids_reassigned_ = value; }
bool ids_reassigned() const { return ids_reassigned_; }
+ // Returns the string blob representing the sync metadata in the json file.
+ // The string blob is set during decode time upon the call to Bookmark::Load.
+ void set_sync_metadata_str(std::string sync_metadata_str) {
+ sync_metadata_str_ = std::move(sync_metadata_str);
+ }
+ const std::string& sync_metadata_str() const { return sync_metadata_str_; }
+
void CreateUrlIndex();
- std::unique_ptr<UrlIndex> owned_url_index() { return std::move(url_index_); }
+ UrlIndex* url_index() { return url_index_.get(); }
private:
// Creates one of the possible permanent nodes (bookmark bar node, other node
@@ -125,7 +133,9 @@ class BookmarkLoadDetails {
std::string computed_checksum_;
std::string stored_checksum_;
bool ids_reassigned_ = false;
- std::unique_ptr<UrlIndex> url_index_;
+ scoped_refptr<UrlIndex> url_index_;
+ // A string blob represetning the sync metadata stored in the json file.
+ std::string sync_metadata_str_;
DISALLOW_COPY_AND_ASSIGN(BookmarkLoadDetails);
};
diff --git a/chromium/components/bookmarks/browser/bookmark_utils.cc b/chromium/components/bookmarks/browser/bookmark_utils.cc
index dd4346fabd9..af04fd3f986 100644
--- a/chromium/components/bookmarks/browser/bookmark_utils.cc
+++ b/chromium/components/bookmarks/browser/bookmark_utils.cc
@@ -15,6 +15,7 @@
#include "base/i18n/string_search.h"
#include "base/macros.h"
#include "base/metrics/user_metrics_action.h"
+#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
@@ -374,7 +375,7 @@ std::vector<const BookmarkNode*> GetMostRecentlyModifiedUserFolders(
for (int i = 0; i < root_node->child_count(); ++i) {
const BookmarkNode* node = root_node->GetChild(i);
if (node->IsVisible() && model->client()->CanBeEditedByUser(node) &&
- std::find(nodes.begin(), nodes.end(), node) == nodes.end()) {
+ !base::ContainsValue(nodes, node)) {
nodes.push_back(node);
if (nodes.size() == max_count)
diff --git a/chromium/components/bookmarks/browser/history_bookmark_model.h b/chromium/components/bookmarks/browser/history_bookmark_model.h
new file mode 100644
index 00000000000..e8beb2d6dbf
--- /dev/null
+++ b/chromium/components/bookmarks/browser/history_bookmark_model.h
@@ -0,0 +1,41 @@
+// 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_BOOKMARKS_BROWSER_HISTORY_BOOKMARK_MODEL_H_
+#define COMPONENTS_BOOKMARKS_BROWSER_HISTORY_BOOKMARK_MODEL_H_
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+
+class GURL;
+
+namespace bookmarks {
+
+struct UrlAndTitle;
+
+// Defines the interface use by history. History accesses these functions on a
+// background thread.
+class HistoryBookmarkModel
+ : public base::RefCountedThreadSafe<HistoryBookmarkModel> {
+ public:
+ HistoryBookmarkModel() {}
+
+ virtual bool IsBookmarked(const GURL& url) = 0;
+
+ // Returns, by reference in |bookmarks|, the set of bookmarked urls and their
+ // titles. This returns the unique set of URLs. For example, if two bookmarks
+ // reference the same URL only one entry is added not matter the titles are
+ // same or not.
+ virtual void GetBookmarks(std::vector<UrlAndTitle>* urls) = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<HistoryBookmarkModel>;
+
+ virtual ~HistoryBookmarkModel() {}
+};
+
+} // namespace bookmarks
+
+#endif // COMPONENTS_BOOKMARKS_BROWSER_HISTORY_BOOKMARK_MODEL_H_
diff --git a/chromium/components/bookmarks/browser/model_loader.cc b/chromium/components/bookmarks/browser/model_loader.cc
new file mode 100644
index 00000000000..a20019ba3b4
--- /dev/null
+++ b/chromium/components/bookmarks/browser/model_loader.cc
@@ -0,0 +1,52 @@
+// 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/bookmarks/browser/model_loader.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/bookmarks/browser/bookmark_storage.h"
+#include "components/bookmarks/browser/url_index.h"
+
+namespace bookmarks {
+
+ModelLoader::ModelLoader(const base::FilePath& profile_path,
+ base::SequencedTaskRunner* load_sequenced_task_runner,
+ std::unique_ptr<BookmarkLoadDetails> details,
+ LoadCallback callback)
+ : loaded_signal_(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED) {
+ load_sequenced_task_runner->PostTask(
+ FROM_HERE,
+ base::BindOnce(&ModelLoader::DoLoadOnBackgroundThread, this, profile_path,
+ base::ThreadTaskRunnerHandle::Get(), std::move(details),
+ std::move(callback)));
+}
+
+void ModelLoader::BlockTillLoaded() {
+ loaded_signal_.Wait();
+}
+
+ModelLoader::~ModelLoader() = default;
+
+void ModelLoader::DoLoadOnBackgroundThread(
+ const base::FilePath& profile_path,
+ scoped_refptr<base::SequencedTaskRunner> main_sequenced_task_runner,
+ std::unique_ptr<BookmarkLoadDetails> details,
+ LoadCallback callback) {
+ LoadBookmarks(profile_path, details.get());
+ history_bookmark_model_ = details->url_index();
+ loaded_signal_.Signal();
+ main_sequenced_task_runner->PostTask(
+ FROM_HERE, base::BindOnce(&ModelLoader::OnFinishedLoad, this,
+ std::move(details), std::move(callback)));
+}
+
+void ModelLoader::OnFinishedLoad(std::unique_ptr<BookmarkLoadDetails> details,
+ LoadCallback callback) {
+ std::move(callback).Run(std::move(details));
+}
+
+} // namespace bookmarks
diff --git a/chromium/components/bookmarks/browser/model_loader.h b/chromium/components/bookmarks/browser/model_loader.h
new file mode 100644
index 00000000000..924c17e9905
--- /dev/null
+++ b/chromium/components/bookmarks/browser/model_loader.h
@@ -0,0 +1,75 @@
+// 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_BOOKMARKS_BROWSER_MODEL_LOADER_H_
+#define COMPONENTS_BOOKMARKS_BROWSER_MODEL_LOADER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace base {
+class FilePath;
+class SequencedTaskRunner;
+} // namespace base
+
+namespace bookmarks {
+
+class BookmarkLoadDetails;
+class HistoryBookmarkModel;
+
+// ModelLoader is created by BookmarkModel to track loading of BookmarkModel.
+// ModelLoader may be used on multiple threads. ModelLoader may outlive
+// BookmarkModel.
+class ModelLoader : public base::RefCountedThreadSafe<ModelLoader> {
+ public:
+ using LoadCallback =
+ base::OnceCallback<void(std::unique_ptr<BookmarkLoadDetails>)>;
+ // Creates the ModelLoader, and schedules loading on
+ // |load_sequenced_task_runner|. |callback| is run once loading
+ // completes (on the main thread).
+ ModelLoader(const base::FilePath& profile_path,
+ base::SequencedTaskRunner* load_sequenced_task_runner,
+ std::unique_ptr<BookmarkLoadDetails> details,
+ LoadCallback callback);
+
+ // Blocks until loaded. This is intended for usage on a thread other than
+ // the main thread.
+ void BlockTillLoaded();
+
+ // Returns null until the model has loaded. Use BlockTillLoaded() to ensure
+ // this returns non-null.
+ HistoryBookmarkModel* history_bookmark_model() {
+ return history_bookmark_model_.get();
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<ModelLoader>;
+ ~ModelLoader();
+
+ // Performs the load on a background thread.
+ void DoLoadOnBackgroundThread(
+ const base::FilePath& profile_path,
+ scoped_refptr<base::SequencedTaskRunner> main_sequenced_task_runner,
+ std::unique_ptr<BookmarkLoadDetails> details,
+ LoadCallback callback);
+
+ void OnFinishedLoad(std::unique_ptr<BookmarkLoadDetails> details,
+ LoadCallback callback);
+
+ scoped_refptr<HistoryBookmarkModel> history_bookmark_model_;
+
+ // Signaled once loading completes.
+ base::WaitableEvent loaded_signal_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModelLoader);
+};
+
+} // namespace bookmarks
+
+#endif // COMPONENTS_BOOKMARKS_BROWSER_MODEL_LOADER_H_
diff --git a/chromium/components/bookmarks/browser/url_index.cc b/chromium/components/bookmarks/browser/url_index.cc
index b8cbf91d174..44aecac0338 100644
--- a/chromium/components/bookmarks/browser/url_index.cc
+++ b/chromium/components/bookmarks/browser/url_index.cc
@@ -14,8 +14,6 @@ UrlIndex::UrlIndex(std::unique_ptr<BookmarkNode> root)
AddImpl(root_.get());
}
-UrlIndex::~UrlIndex() = default;
-
void UrlIndex::Add(BookmarkNode* parent,
int index,
std::unique_ptr<BookmarkNode> node) {
@@ -100,6 +98,8 @@ void UrlIndex::GetBookmarks(std::vector<UrlAndTitle>* bookmarks) {
}
}
+UrlIndex::~UrlIndex() = default;
+
bool UrlIndex::IsBookmarkedNoLock(const GURL& url) {
url_lock_.AssertAcquired();
BookmarkNode tmp_node(url);
diff --git a/chromium/components/bookmarks/browser/url_index.h b/chromium/components/bookmarks/browser/url_index.h
index 0386be37905..1200247ea3e 100644
--- a/chromium/components/bookmarks/browser/url_index.h
+++ b/chromium/components/bookmarks/browser/url_index.h
@@ -12,6 +12,7 @@
#include "base/macros.h"
#include "base/synchronization/lock.h"
#include "components/bookmarks/browser/bookmark_node.h"
+#include "components/bookmarks/browser/history_bookmark_model.h"
#include "url/gurl.h"
namespace bookmarks {
@@ -22,18 +23,19 @@ struct UrlAndTitle;
// UrlIndex maintains the bookmark nodes of type url. The nodes are ordered by
// url for lookup using the url. The mapping is done based on the nodes in the
-// model.
+// model. This class may outlive the BookmarkModel, necessitating this class
+// owning all the nodes.
//
// This class is accessed on multiple threads, so all mutation to the underlying
// set must be done while holding a lock. While this class is accessed on
// multiple threads, all mutation happens on main thread.
//
// This class is an implementation detail of BookmarkModel and is not intended
-// to be public.
-class UrlIndex {
+// to be public. The public functions implemented by way of
+// HistoryBookmarkModel define the public API of this class.
+class UrlIndex : public HistoryBookmarkModel {
public:
explicit UrlIndex(std::unique_ptr<BookmarkNode> root);
- ~UrlIndex();
BookmarkNode* root() { return root_.get(); }
@@ -56,11 +58,15 @@ class UrlIndex {
// Returns true if there is at least one bookmark.
bool HasBookmarks();
- bool IsBookmarked(const GURL& url);
-
- void GetBookmarks(std::vector<UrlAndTitle>* bookmarks);
+ // HistoryBookmarkModel:
+ bool IsBookmarked(const GURL& url) override;
+ void GetBookmarks(std::vector<UrlAndTitle>* bookmarks) override;
private:
+ friend class base::RefCountedThreadSafe<UrlIndex>;
+
+ ~UrlIndex() override;
+
// Used to order BookmarkNodes by URL.
class NodeUrlComparator {
public:
diff --git a/chromium/components/browser_sync/BUILD.gn b/chromium/components/browser_sync/BUILD.gn
index a7643e27149..da5c5e3b95b 100644
--- a/chromium/components/browser_sync/BUILD.gn
+++ b/chromium/components/browser_sync/BUILD.gn
@@ -46,6 +46,7 @@ static_library("browser_sync") {
"//google_apis",
"//net",
"//services/identity/public/cpp",
+ "//services/network/public/cpp",
]
}
@@ -57,6 +58,7 @@ source_set("unit_tests") {
"profile_sync_service_bookmark_unittest.cc",
"profile_sync_service_startup_unittest.cc",
"profile_sync_service_unittest.cc",
+ "sync_auth_manager_unittest.cc",
]
deps = [
@@ -91,6 +93,8 @@ source_set("unit_tests") {
"//google_apis",
"//net",
"//services/identity/public/cpp:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//testing/gmock",
"//testing/gtest",
]
@@ -131,6 +135,8 @@ static_library("test_support") {
"//google_apis",
"//net:test_support",
"//services/identity/public/cpp",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//testing/gmock",
]
}
diff --git a/chromium/components/browser_sync/DEPS b/chromium/components/browser_sync/DEPS
index 3bbcc3d8893..dbb03800b9a 100644
--- a/chromium/components/browser_sync/DEPS
+++ b/chromium/components/browser_sync/DEPS
@@ -24,4 +24,6 @@ include_rules = [
"+google_apis",
"+net",
"+services/identity/public/cpp",
+ "+services/network/public/cpp",
+ "+services/network/test",
]
diff --git a/chromium/components/browser_sync/abstract_profile_sync_service_test.cc b/chromium/components/browser_sync/abstract_profile_sync_service_test.cc
index 8c0b263579c..f335b835afb 100644
--- a/chromium/components/browser_sync/abstract_profile_sync_service_test.cc
+++ b/chromium/components/browser_sync/abstract_profile_sync_service_test.cc
@@ -11,7 +11,7 @@
#include "base/files/file_path.h"
#include "base/location.h"
#include "base/run_loop.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "components/browser_sync/test_http_bridge_factory.h"
#include "components/browser_sync/test_profile_sync_service.h"
#include "components/sync/driver/glue/sync_backend_host_core.h"
@@ -105,7 +105,7 @@ void SyncEngineForProfileSyncTest::ConfigureDataTypes(ConfigureParams params) {
// send back the list of newly configured types instead and hope it doesn't
// break anything.
// Posted to avoid re-entrancy issues.
- base::ThreadTaskRunnerHandle::Get()->PostTask(
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(
&SyncEngineForProfileSyncTest::FinishConfigureDataTypesOnFrontendLoop,
diff --git a/chromium/components/browser_sync/profile_sync_components_factory_impl.cc b/chromium/components/browser_sync/profile_sync_components_factory_impl.cc
index 156c5829386..6a78faf9dc7 100644
--- a/chromium/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/chromium/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -13,6 +13,7 @@
#include "components/autofill/core/browser/autofill_wallet_data_type_controller.h"
#include "components/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
#include "components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h"
+#include "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/autofill/core/browser/webdata/web_data_model_type_controller.h"
#include "components/browser_sync/browser_sync_switches.h"
@@ -37,8 +38,8 @@
#include "components/sync_bookmarks/bookmark_change_processor.h"
#include "components/sync_bookmarks/bookmark_data_type_controller.h"
#include "components/sync_bookmarks/bookmark_model_associator.h"
-#include "components/sync_bookmarks/bookmark_model_type_controller.h"
#include "components/sync_sessions/session_data_type_controller.h"
+#include "components/sync_sessions/session_model_type_controller.h"
using base::FeatureList;
using bookmarks::BookmarkModel;
@@ -58,28 +59,30 @@ namespace browser_sync {
namespace {
-syncer::ModelTypeSet GetDisabledTypesFromCommandLine(
- const base::CommandLine& command_line) {
- syncer::ModelTypeSet disabled_types;
- std::string disabled_types_str =
- command_line.GetSwitchValueASCII(switches::kDisableSyncTypes);
-
- disabled_types = syncer::ModelTypeSetFromString(disabled_types_str);
- return disabled_types;
-}
-
-// This helper function only wraps
-// autofill::AutocompleteSyncBridge::FromWebDataService(). This way, it
-// simplifies life for the compiler which cannot directly cast
+// These helper functions only wrap the factory functions of the bridges. This
+// way, it simplifies life for the compiler which cannot directly cast
// "WeakPtr<ModelTypeSyncBridge> (AutofillWebDataService*)" to
// "WeakPtr<ModelTypeControllerDelegate> (AutofillWebDataService*)".
-base::WeakPtr<syncer::ModelTypeControllerDelegate> DelegateFromDataService(
- autofill::AutofillWebDataService* service) {
+base::WeakPtr<syncer::ModelTypeControllerDelegate>
+AutocompleteDelegateFromDataService(autofill::AutofillWebDataService* service) {
+ // TODO(jkrcal): Deal with the (probably rare) race condition when we call
+ // bridges' FromWebDataService() before calling
+ // CreateForWebDataServiceAndBackend() in WebDataServiceWrapper. This TODO
+ // also applies to the second function below and to analogous code in
+ // SyncClient::GetControllerDelegateForModelType().
return autofill::AutocompleteSyncBridge::FromWebDataService(service)
->change_processor()
->GetControllerDelegateOnUIThread();
}
+base::WeakPtr<syncer::ModelTypeControllerDelegate>
+AutofillProfileDelegateFromDataService(
+ autofill::AutofillWebDataService* service) {
+ return autofill::AutofillProfileSyncBridge::FromWebDataService(service)
+ ->change_processor()
+ ->GetControllerDelegateOnUIThread();
+}
+
} // namespace
ProfileSyncComponentsFactoryImpl::ProfileSyncComponentsFactoryImpl(
@@ -87,7 +90,6 @@ ProfileSyncComponentsFactoryImpl::ProfileSyncComponentsFactoryImpl(
version_info::Channel channel,
const std::string& version,
bool is_tablet,
- const base::CommandLine& command_line,
const char* history_disabled_pref,
const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread,
const scoped_refptr<base::SingleThreadTaskRunner>& db_thread,
@@ -97,7 +99,6 @@ ProfileSyncComponentsFactoryImpl::ProfileSyncComponentsFactoryImpl(
channel_(channel),
version_(version),
is_tablet_(is_tablet),
- command_line_(command_line),
history_disabled_pref_(history_disabled_pref),
ui_thread_(ui_thread),
db_thread_(db_thread),
@@ -106,68 +107,62 @@ ProfileSyncComponentsFactoryImpl::ProfileSyncComponentsFactoryImpl(
ProfileSyncComponentsFactoryImpl::~ProfileSyncComponentsFactoryImpl() {}
-void ProfileSyncComponentsFactoryImpl::RegisterDataTypes(
- syncer::SyncService* sync_service,
- const RegisterDataTypesMethod& register_platform_types_method) {
- syncer::ModelTypeSet disabled_types =
- GetDisabledTypesFromCommandLine(command_line_);
- RegisterCommonDataTypes(sync_service, disabled_types);
- if (!register_platform_types_method.is_null()) {
- syncer::ModelTypeSet enabled_types;
- register_platform_types_method.Run(sync_service, disabled_types,
- enabled_types);
- }
-}
-
-void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes(
- syncer::SyncService* sync_service,
- syncer::ModelTypeSet disabled_types) {
+syncer::DataTypeController::TypeVector
+ProfileSyncComponentsFactoryImpl::CreateCommonDataTypeControllers(
+ syncer::ModelTypeSet disabled_types,
+ syncer::LocalDeviceInfoProvider* local_device_info_provider) {
+ syncer::DataTypeController::TypeVector controllers;
base::Closure error_callback =
- base::Bind(&syncer::ReportUnrecoverableError, channel_);
+ base::BindRepeating(&syncer::ReportUnrecoverableError, channel_);
// TODO(stanisc): can DEVICE_INFO be one of disabled datatypes?
// Use an error callback that always uploads a stacktrace if it can to help
// get USS as stable as possible.
- sync_service->RegisterDataTypeController(
- std::make_unique<ModelTypeController>(syncer::DEVICE_INFO, sync_client_,
- ui_thread_));
+ controllers.push_back(std::make_unique<ModelTypeController>(
+ syncer::DEVICE_INFO, sync_client_, ui_thread_));
// These features are enabled only if there's a DB thread to post tasks to.
if (db_thread_) {
// Autocomplete sync is enabled by default. Register unless explicitly
// disabled.
if (!disabled_types.Has(syncer::AUTOFILL)) {
- sync_service->RegisterDataTypeController(
+ controllers.push_back(
std::make_unique<autofill::WebDataModelTypeController>(
syncer::AUTOFILL, sync_client_, db_thread_, web_data_service_,
- base::Bind(&DelegateFromDataService)));
+ base::BindRepeating(&AutocompleteDelegateFromDataService)));
}
// Autofill sync is enabled by default. Register unless explicitly
// disabled.
if (!disabled_types.Has(syncer::AUTOFILL_PROFILE)) {
- sync_service->RegisterDataTypeController(
- std::make_unique<AutofillProfileDataTypeController>(
- db_thread_, error_callback, sync_client_, web_data_service_));
+ if (FeatureList::IsEnabled(switches::kSyncUSSAutofillProfile)) {
+ controllers.push_back(
+ std::make_unique<autofill::WebDataModelTypeController>(
+ syncer::AUTOFILL_PROFILE, sync_client_, db_thread_,
+ web_data_service_,
+ base::BindRepeating(&AutofillProfileDelegateFromDataService)));
+ } else {
+ controllers.push_back(
+ std::make_unique<AutofillProfileDataTypeController>(
+ db_thread_, error_callback, sync_client_, web_data_service_));
+ }
}
// Wallet data sync is enabled by default, but behind a syncer experiment
// enforced by the datatype controller. Register unless explicitly disabled.
bool wallet_disabled = disabled_types.Has(syncer::AUTOFILL_WALLET_DATA);
if (!wallet_disabled) {
- sync_service->RegisterDataTypeController(
- std::make_unique<AutofillWalletDataTypeController>(
- syncer::AUTOFILL_WALLET_DATA, db_thread_, error_callback,
- sync_client_, web_data_service_));
+ controllers.push_back(std::make_unique<AutofillWalletDataTypeController>(
+ syncer::AUTOFILL_WALLET_DATA, db_thread_, error_callback,
+ sync_client_, web_data_service_));
}
// Wallet metadata sync depends on Wallet data sync. Register if Wallet data
// is syncing and metadata sync is not explicitly disabled.
if (!wallet_disabled &&
!disabled_types.Has(syncer::AUTOFILL_WALLET_METADATA)) {
- sync_service->RegisterDataTypeController(
- std::make_unique<AutofillWalletDataTypeController>(
- syncer::AUTOFILL_WALLET_METADATA, db_thread_, error_callback,
- sync_client_, web_data_service_));
+ controllers.push_back(std::make_unique<AutofillWalletDataTypeController>(
+ syncer::AUTOFILL_WALLET_METADATA, db_thread_, error_callback,
+ sync_client_, web_data_service_));
}
}
@@ -175,13 +170,11 @@ void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes(
// disabled.
if (!disabled_types.Has(syncer::BOOKMARKS)) {
if (FeatureList::IsEnabled(switches::kSyncUSSBookmarks)) {
- sync_service->RegisterDataTypeController(
- std::make_unique<sync_bookmarks::BookmarkModelTypeController>(
- sync_client_));
+ controllers.push_back(std::make_unique<ModelTypeController>(
+ syncer::BOOKMARKS, sync_client_, ui_thread_));
} else {
- sync_service->RegisterDataTypeController(
- std::make_unique<BookmarkDataTypeController>(error_callback,
- sync_client_));
+ controllers.push_back(std::make_unique<BookmarkDataTypeController>(
+ error_callback, sync_client_));
}
}
@@ -190,14 +183,14 @@ void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes(
// TypedUrl sync is enabled by default. Register unless explicitly
// disabled.
if (!disabled_types.Has(syncer::TYPED_URLS)) {
- sync_service->RegisterDataTypeController(
+ controllers.push_back(
std::make_unique<history::TypedURLModelTypeController>(
sync_client_, history_disabled_pref_));
}
// Delete directive sync is enabled by default.
if (!disabled_types.Has(syncer::HISTORY_DELETE_DIRECTIVES)) {
- sync_service->RegisterDataTypeController(
+ controllers.push_back(
std::make_unique<HistoryDeleteDirectivesDataTypeController>(
error_callback, sync_client_));
}
@@ -206,18 +199,16 @@ void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes(
// disabled because the tab sync data is added to the web history on the
// server.
if (!disabled_types.Has(syncer::PROXY_TABS)) {
- sync_service->RegisterDataTypeController(
+ controllers.push_back(
std::make_unique<ProxyDataTypeController>(syncer::PROXY_TABS));
if (FeatureList::IsEnabled(switches::kSyncUSSSessions)) {
- sync_service->RegisterDataTypeController(
- std::make_unique<ModelTypeController>(syncer::SESSIONS,
- sync_client_, ui_thread_));
+ controllers.push_back(
+ std::make_unique<sync_sessions::SessionModelTypeController>(
+ sync_client_, ui_thread_, history_disabled_pref_));
} else {
- sync_service->RegisterDataTypeController(
- std::make_unique<SessionDataTypeController>(
- error_callback, sync_client_,
- sync_service->GetLocalDeviceInfoProvider(),
- history_disabled_pref_));
+ controllers.push_back(std::make_unique<SessionDataTypeController>(
+ error_callback, sync_client_, local_device_info_provider,
+ history_disabled_pref_));
}
}
@@ -225,59 +216,51 @@ void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes(
if (!disabled_types.Has(syncer::FAVICON_IMAGES) &&
!disabled_types.Has(syncer::FAVICON_TRACKING)) {
// crbug/384552. We disable error uploading for this data types for now.
- sync_service->RegisterDataTypeController(
- std::make_unique<AsyncDirectoryTypeController>(
- syncer::FAVICON_IMAGES, base::Closure(), sync_client_,
- syncer::GROUP_UI, ui_thread_));
- sync_service->RegisterDataTypeController(
- std::make_unique<AsyncDirectoryTypeController>(
- syncer::FAVICON_TRACKING, base::Closure(), sync_client_,
- syncer::GROUP_UI, ui_thread_));
+ controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
+ syncer::FAVICON_IMAGES, base::RepeatingClosure(), sync_client_,
+ syncer::GROUP_UI, ui_thread_));
+ controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
+ syncer::FAVICON_TRACKING, base::RepeatingClosure(), sync_client_,
+ syncer::GROUP_UI, ui_thread_));
}
}
// Password sync is enabled by default. Register unless explicitly
// disabled.
if (!disabled_types.Has(syncer::PASSWORDS)) {
- sync_service->RegisterDataTypeController(
- std::make_unique<PasswordDataTypeController>(
- error_callback, sync_client_,
- sync_client_->GetPasswordStateChangedCallback(), password_store_));
+ controllers.push_back(std::make_unique<PasswordDataTypeController>(
+ error_callback, sync_client_,
+ sync_client_->GetPasswordStateChangedCallback(), password_store_));
}
if (!disabled_types.Has(syncer::PREFERENCES)) {
if (!override_prefs_controller_to_uss_for_test_) {
- sync_service->RegisterDataTypeController(
- std::make_unique<AsyncDirectoryTypeController>(
- syncer::PREFERENCES, error_callback, sync_client_,
- syncer::GROUP_UI, ui_thread_));
+ controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
+ syncer::PREFERENCES, error_callback, sync_client_, syncer::GROUP_UI,
+ ui_thread_));
} else {
- sync_service->RegisterDataTypeController(
- std::make_unique<ModelTypeController>(syncer::PREFERENCES,
- sync_client_, ui_thread_));
+ controllers.push_back(std::make_unique<ModelTypeController>(
+ syncer::PREFERENCES, sync_client_, ui_thread_));
}
}
if (!disabled_types.Has(syncer::PRIORITY_PREFERENCES)) {
- sync_service->RegisterDataTypeController(
- std::make_unique<AsyncDirectoryTypeController>(
- syncer::PRIORITY_PREFERENCES, error_callback, sync_client_,
- syncer::GROUP_UI, ui_thread_));
+ controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
+ syncer::PRIORITY_PREFERENCES, error_callback, sync_client_,
+ syncer::GROUP_UI, ui_thread_));
}
// Article sync is disabled by default. Register only if explicitly enabled.
if (dom_distiller::IsEnableSyncArticlesSet()) {
- sync_service->RegisterDataTypeController(
- std::make_unique<AsyncDirectoryTypeController>(
- syncer::ARTICLES, error_callback, sync_client_, syncer::GROUP_UI,
- ui_thread_));
+ controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
+ syncer::ARTICLES, error_callback, sync_client_, syncer::GROUP_UI,
+ ui_thread_));
}
#if defined(OS_CHROMEOS)
if (!disabled_types.Has(syncer::PRINTERS)) {
- sync_service->RegisterDataTypeController(
- std::make_unique<ModelTypeController>(syncer::PRINTERS, sync_client_,
- ui_thread_));
+ controllers.push_back(std::make_unique<ModelTypeController>(
+ syncer::PRINTERS, sync_client_, ui_thread_));
}
#endif
@@ -285,17 +268,22 @@ void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes(
// Reading List or Reading List Sync is explicitly disabled.
if (!disabled_types.Has(syncer::READING_LIST) &&
reading_list::switches::IsReadingListEnabled()) {
- sync_service->RegisterDataTypeController(
- std::make_unique<ModelTypeController>(syncer::READING_LIST,
- sync_client_, ui_thread_));
+ controllers.push_back(std::make_unique<ModelTypeController>(
+ syncer::READING_LIST, sync_client_, ui_thread_));
}
if (!disabled_types.Has(syncer::USER_EVENTS) &&
FeatureList::IsEnabled(switches::kSyncUserEvents)) {
- sync_service->RegisterDataTypeController(
- std::make_unique<ModelTypeController>(syncer::USER_EVENTS, sync_client_,
- ui_thread_));
+ controllers.push_back(std::make_unique<ModelTypeController>(
+ syncer::USER_EVENTS, sync_client_, ui_thread_));
}
+
+ if (base::FeatureList::IsEnabled(switches::kSyncUserConsentSeparateType)) {
+ controllers.push_back(std::make_unique<ModelTypeController>(
+ syncer::USER_CONSENTS, sync_client_, ui_thread_));
+ }
+
+ return controllers;
}
std::unique_ptr<DataTypeManager>
@@ -330,24 +318,26 @@ ProfileSyncComponentsFactoryImpl::CreateLocalDeviceInfoProvider() {
syncer::SyncApiComponentFactory::SyncComponents
ProfileSyncComponentsFactoryImpl::CreateBookmarkSyncComponents(
- syncer::SyncService* sync_service,
std::unique_ptr<syncer::DataTypeErrorHandler> error_handler) {
- BookmarkModel* bookmark_model =
- sync_service->GetSyncClient()->GetBookmarkModel();
- syncer::UserShare* user_share = sync_service->GetUserShare();
+ BookmarkModel* bookmark_model = sync_client_->GetBookmarkModel();
+ syncer::UserShare* user_share =
+ sync_client_->GetSyncService()->GetUserShare();
// TODO(akalin): We may want to propagate this switch up eventually.
#if defined(OS_ANDROID) || defined(OS_IOS)
const bool kExpectMobileBookmarksFolder = true;
#else
const bool kExpectMobileBookmarksFolder = false;
#endif
- BookmarkModelAssociator* model_associator = new BookmarkModelAssociator(
- bookmark_model, sync_service->GetSyncClient(), user_share,
- error_handler->Copy(), kExpectMobileBookmarksFolder);
- BookmarkChangeProcessor* change_processor =
- new BookmarkChangeProcessor(sync_service->GetSyncClient(),
- model_associator, std::move(error_handler));
- return SyncComponents(model_associator, change_processor);
+
+ auto model_associator = std::make_unique<BookmarkModelAssociator>(
+ bookmark_model, sync_client_, user_share, error_handler->Copy(),
+ kExpectMobileBookmarksFolder);
+
+ SyncComponents components;
+ components.change_processor = std::make_unique<BookmarkChangeProcessor>(
+ sync_client_, model_associator.get(), std::move(error_handler));
+ components.model_associator = std::move(model_associator);
+ return components;
}
// static
diff --git a/chromium/components/browser_sync/profile_sync_components_factory_impl.h b/chromium/components/browser_sync/profile_sync_components_factory_impl.h
index 3d27b5ecbf5..acac43bb6df 100644
--- a/chromium/components/browser_sync/profile_sync_components_factory_impl.h
+++ b/chromium/components/browser_sync/profile_sync_components_factory_impl.h
@@ -8,7 +8,6 @@
#include <memory>
#include <string>
-#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
@@ -16,6 +15,10 @@
#include "components/sync/driver/sync_api_component_factory.h"
#include "components/version_info/version_info.h"
+namespace syncer {
+class SyncClient;
+}
+
namespace autofill {
class AutofillWebDataService;
}
@@ -34,7 +37,6 @@ class ProfileSyncComponentsFactoryImpl
version_info::Channel channel,
const std::string& version,
bool is_tablet,
- const base::CommandLine& command_line,
const char* history_disabled_pref,
const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread,
const scoped_refptr<base::SingleThreadTaskRunner>& db_thread,
@@ -43,9 +45,9 @@ class ProfileSyncComponentsFactoryImpl
~ProfileSyncComponentsFactoryImpl() override;
// SyncApiComponentFactory implementation:
- void RegisterDataTypes(
- syncer::SyncService* sync_service,
- const RegisterDataTypesMethod& register_platform_types_method) override;
+ syncer::DataTypeController::TypeVector CreateCommonDataTypeControllers(
+ syncer::ModelTypeSet disabled_types,
+ syncer::LocalDeviceInfoProvider* local_device_info_provider) override;
std::unique_ptr<syncer::DataTypeManager> CreateDataTypeManager(
syncer::ModelTypeSet initial_types,
const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
@@ -62,7 +64,6 @@ class ProfileSyncComponentsFactoryImpl
std::unique_ptr<syncer::LocalDeviceInfoProvider>
CreateLocalDeviceInfoProvider() override;
syncer::SyncApiComponentFactory::SyncComponents CreateBookmarkSyncComponents(
- syncer::SyncService* sync_service,
std::unique_ptr<syncer::DataTypeErrorHandler> error_handler) override;
// Sets a bit that determines whether PREFERENCES should be registered with a
@@ -70,18 +71,11 @@ class ProfileSyncComponentsFactoryImpl
static void OverridePrefsForUssTest(bool use_uss);
private:
- // Register data types which are enabled on both desktop and mobile.
- // |disabled_types| corresponds only to those types being explicitly disabled
- // by the command line.
- void RegisterCommonDataTypes(syncer::SyncService* sync_service,
- syncer::ModelTypeSet disabled_types);
-
// Client/platform specific members.
syncer::SyncClient* const sync_client_;
const version_info::Channel channel_;
const std::string version_;
const bool is_tablet_;
- const base::CommandLine command_line_;
const char* history_disabled_pref_;
const scoped_refptr<base::SingleThreadTaskRunner> ui_thread_;
const scoped_refptr<base::SingleThreadTaskRunner> db_thread_;
diff --git a/chromium/components/browser_sync/profile_sync_service.cc b/chromium/components/browser_sync/profile_sync_service.cc
index 348bcf10291..de446289040 100644
--- a/chromium/components/browser_sync/profile_sync_service.cc
+++ b/chromium/components/browser_sync/profile_sync_service.cc
@@ -55,6 +55,7 @@
#include "components/sync/js/js_event_details.h"
#include "components/sync/model/change_processor.h"
#include "components/sync/model/model_type_change_processor.h"
+#include "components/sync/model/model_type_store_service.h"
#include "components/sync/model/sync_error.h"
#include "components/sync/model_impl/client_tag_based_model_type_processor.h"
#include "components/sync/syncable/directory.h"
@@ -67,52 +68,62 @@
#include "components/sync_sessions/sync_sessions_client.h"
#include "components/version_info/version_info_values.h"
#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
-using syncer::BackendMigrator;
-using syncer::ClientTagBasedModelTypeProcessor;
using syncer::DataTypeController;
using syncer::DataTypeManager;
-using syncer::DataTypeStatusTable;
-using syncer::DeviceInfoSyncBridge;
using syncer::EngineComponentsFactory;
using syncer::EngineComponentsFactoryImpl;
-using syncer::JsBackend;
-using syncer::JsController;
-using syncer::JsEventHandler;
-using syncer::ModelSafeRoutingInfo;
-using syncer::ModelType;
-using syncer::ModelTypeSet;
-using syncer::ModelTypeStore;
-using syncer::ProtocolEventObserver;
-using syncer::SyncBackendRegistrar;
-using syncer::SyncClient;
-using syncer::SyncCredentials;
-using syncer::SyncEngine;
-using syncer::SyncManagerFactory;
-using syncer::SyncProtocolError;
-using syncer::WeakHandle;
namespace browser_sync {
namespace {
-constexpr char kSyncUnrecoverableErrorHistogram[] = "Sync.UnrecoverableErrors";
-
-constexpr base::FilePath::CharType kSyncDataFolderName[] =
- FILE_PATH_LITERAL("Sync Data");
-
-constexpr base::FilePath::CharType kLevelDBFolderName[] =
- FILE_PATH_LITERAL("LevelDB");
+// The initial state of sync, for the Sync.InitialState histogram. Even if
+// this value is CAN_START, sync startup might fail for reasons that we may
+// want to consider logging in the future, such as a passphrase needed for
+// decryption, or the version of Chrome being too old. This enum is used to
+// back a UMA histogram, and should therefore be treated as append-only.
+enum SyncInitialState {
+ CAN_START, // Sync can attempt to start up.
+ NOT_SIGNED_IN, // There is no signed in user.
+ NOT_REQUESTED, // The user turned off sync.
+ NOT_REQUESTED_NOT_SETUP, // The user turned off sync and setup completed
+ // is false. Might indicate a stop-and-clear.
+ NEEDS_CONFIRMATION, // The user must confirm sync settings.
+ NOT_ALLOWED_BY_POLICY, // Sync is disallowed by enterprise policy.
+ NOT_ALLOWED_BY_PLATFORM, // Sync is disallowed by the platform.
+ SYNC_INITIAL_STATE_LIMIT
+};
-base::FilePath FormatSyncDataPath(const base::FilePath& base_directory) {
- return base_directory.Append(base::FilePath(kSyncDataFolderName));
+void RecordSyncInitialState(int disable_reasons, bool first_setup_complete) {
+ SyncInitialState sync_state = CAN_START;
+ if (disable_reasons & ProfileSyncService::DISABLE_REASON_NOT_SIGNED_IN) {
+ sync_state = NOT_SIGNED_IN;
+ } else if (disable_reasons &
+ ProfileSyncService::DISABLE_REASON_ENTERPRISE_POLICY) {
+ sync_state = NOT_ALLOWED_BY_POLICY;
+ } else if (disable_reasons &
+ ProfileSyncService::DISABLE_REASON_PLATFORM_OVERRIDE) {
+ // This case means either a command-line flag or Android's "MasterSync"
+ // toggle. However, the latter is not plumbed into ProfileSyncService until
+ // after this method, so currently we only get here for the command-line
+ // case. See http://crbug.com/568771.
+ sync_state = NOT_ALLOWED_BY_PLATFORM;
+ } else if (disable_reasons & ProfileSyncService::DISABLE_REASON_USER_CHOICE) {
+ if (first_setup_complete) {
+ sync_state = NOT_REQUESTED;
+ } else {
+ sync_state = NOT_REQUESTED_NOT_SETUP;
+ }
+ } else if (!first_setup_complete) {
+ sync_state = NEEDS_CONFIRMATION;
+ }
+ UMA_HISTOGRAM_ENUMERATION("Sync.InitialState", sync_state,
+ SYNC_INITIAL_STATE_LIMIT);
}
-base::FilePath FormatSharedModelTypeStorePath(
- const base::FilePath& base_directory) {
- return FormatSyncDataPath(base_directory)
- .Append(base::FilePath(kLevelDBFolderName));
-}
+constexpr char kSyncUnrecoverableErrorHistogram[] = "Sync.UnrecoverableErrors";
EngineComponentsFactory::Switches EngineSwitchesFromCommandLine() {
EngineComponentsFactory::Switches factory_switches = {
@@ -135,6 +146,18 @@ EngineComponentsFactory::Switches EngineSwitchesFromCommandLine() {
return factory_switches;
}
+DataTypeController::TypeMap BuildDataTypeControllerMap(
+ DataTypeController::TypeVector controllers) {
+ DataTypeController::TypeMap type_map;
+ for (std::unique_ptr<DataTypeController>& controller : controllers) {
+ DCHECK(controller);
+ syncer::ModelType type = controller->type();
+ DCHECK_EQ(0U, type_map.count(type));
+ type_map[type] = std::move(controller);
+ }
+ return type_map;
+}
+
} // namespace
ProfileSyncService::InitParams::InitParams() = default;
@@ -146,21 +169,25 @@ ProfileSyncService::ProfileSyncService(InitParams init_params)
sync_prefs_(sync_client_->GetPrefService()),
signin_(std::move(init_params.signin_wrapper)),
auth_manager_(std::make_unique<SyncAuthManager>(
- this,
&sync_prefs_,
signin_ ? signin_->GetIdentityManager() : nullptr,
- init_params.oauth2_token_service)),
+ base::BindRepeating(&ProfileSyncService::AccountStateChanged,
+ base::Unretained(this)),
+ base::BindRepeating(&ProfileSyncService::CredentialsChanged,
+ base::Unretained(this)))),
channel_(init_params.channel),
- base_directory_(init_params.base_directory),
debug_identifier_(init_params.debug_identifier),
sync_service_url_(
syncer::GetSyncServiceURL(*base::CommandLine::ForCurrentProcess(),
init_params.channel)),
+ user_events_separate_pref_group_(
+ init_params.user_events_separate_pref_group),
signin_scoped_device_id_callback_(
init_params.signin_scoped_device_id_callback),
network_time_update_callback_(
std::move(init_params.network_time_update_callback)),
url_request_context_(init_params.url_request_context),
+ url_loader_factory_(std::move(init_params.url_loader_factory)),
is_first_time_sync_configure_(false),
engine_initialized_(false),
sync_disabled_by_admin_(false),
@@ -189,12 +216,6 @@ ProfileSyncService::ProfileSyncService(InitParams init_params)
current_version.substr(0, current_version.find('.'))) {
passphrase_prompt_triggered_by_version_ = true;
}
-
- if (init_params.model_type_store_factory.is_null()) {
- model_type_store_factory_ = GetModelTypeStoreFactory(base_directory_);
- } else {
- model_type_store_factory_ = init_params.model_type_store_factory;
- }
}
ProfileSyncService::~ProfileSyncService() {
@@ -206,37 +227,38 @@ ProfileSyncService::~ProfileSyncService() {
DCHECK(!engine_initialized_);
}
-bool ProfileSyncService::CanSyncStart() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return (IsSyncAllowed() && IsSyncRequested() &&
- (IsLocalSyncEnabled() || IsSignedIn()));
-}
-
void ProfileSyncService::Initialize() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sync_client_->Initialize();
- // We don't pass StartupController an Unretained reference to future-proof
- // against the controller impl changing to post tasks.
+ syncer::ModelTypeStoreService* model_type_store_service =
+ sync_client_->GetModelTypeStoreService();
+ DCHECK(model_type_store_service);
+ syncer::RepeatingModelTypeStoreFactory model_type_store_factory =
+ model_type_store_service->GetStoreFactory();
+
startup_controller_ = std::make_unique<syncer::StartupController>(
- &sync_prefs_,
- base::BindRepeating(&ProfileSyncService::CanEngineStart,
+ base::BindRepeating(&ProfileSyncService::GetPreferredDataTypes,
+ base::Unretained(this)),
+ base::BindRepeating(&ProfileSyncService::ShouldSyncStart,
base::Unretained(this)),
base::BindRepeating(&ProfileSyncService::StartUpSlowEngineComponents,
- weak_factory_.GetWeakPtr()));
+ base::Unretained(this)));
local_device_ = sync_client_->GetSyncApiComponentFactory()
->CreateLocalDeviceInfoProvider();
+ DCHECK(local_device_);
sync_stopped_reporter_ = std::make_unique<syncer::SyncStoppedReporter>(
- sync_service_url_, local_device_->GetSyncUserAgent(),
- url_request_context_, syncer::SyncStoppedReporter::ResultCallback());
+ sync_service_url_, local_device_->GetSyncUserAgent(), url_loader_factory_,
+ syncer::SyncStoppedReporter::ResultCallback());
if (base::FeatureList::IsEnabled(switches::kSyncUSSSessions)) {
+ DCHECK(sync_client_->GetSyncSessionsClient());
sessions_sync_manager_ = std::make_unique<sync_sessions::SessionSyncBridge>(
sync_client_->GetSyncSessionsClient(), &sync_prefs_,
- local_device_.get(), model_type_store_factory_,
+ local_device_.get(), model_type_store_factory,
base::BindRepeating(&ProfileSyncService::NotifyForeignSessionUpdated,
sync_enabled_weak_factory_.GetWeakPtr()),
- std::make_unique<ClientTagBasedModelTypeProcessor>(
+ std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
syncer::SESSIONS,
base::BindRepeating(&syncer::ReportUnrecoverableError, channel_)));
} else {
@@ -249,18 +271,15 @@ void ProfileSyncService::Initialize() {
sync_enabled_weak_factory_.GetWeakPtr()));
}
- device_info_sync_bridge_ = std::make_unique<DeviceInfoSyncBridge>(
- local_device_.get(), model_type_store_factory_,
- std::make_unique<ClientTagBasedModelTypeProcessor>(
+ device_info_sync_bridge_ = std::make_unique<syncer::DeviceInfoSyncBridge>(
+ local_device_.get(), model_type_store_factory,
+ std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
syncer::DEVICE_INFO,
/*dump_stack=*/base::BindRepeating(&syncer::ReportUnrecoverableError,
channel_)));
- syncer::SyncApiComponentFactory::RegisterDataTypesMethod
- register_platform_types_callback =
- sync_client_->GetRegisterPlatformTypesCallback();
- sync_client_->GetSyncApiComponentFactory()->RegisterDataTypes(
- this, register_platform_types_callback);
+ data_type_controllers_ = BuildDataTypeControllerMap(
+ sync_client_->CreateDataTypeControllers(local_device_.get()));
if (gaia_cookie_manager_service_)
gaia_cookie_manager_service_->AddObserver(this);
@@ -272,31 +291,15 @@ void ProfileSyncService::Initialize() {
sync_prefs_.AddSyncPrefObserver(this);
- SyncInitialState sync_state = CAN_START;
- if (!IsLocalSyncEnabled() && !IsSignedIn()) {
- sync_state = NOT_SIGNED_IN;
- } else if (IsManaged()) {
- sync_state = IS_MANAGED;
- } else if (!IsSyncAllowedByPlatform()) {
- // This case should currently never be hit, as Android's master sync isn't
- // plumbed into PSS until after this function. See http://crbug.com/568771.
- sync_state = NOT_ALLOWED_BY_PLATFORM;
- } else if (!IsSyncRequested()) {
- if (IsFirstSetupComplete()) {
- sync_state = NOT_REQUESTED;
- } else {
- sync_state = NOT_REQUESTED_NOT_SETUP;
- }
- } else if (!IsFirstSetupComplete()) {
- sync_state = NEEDS_CONFIRMATION;
- }
- UMA_HISTOGRAM_ENUMERATION("Sync.InitialState", sync_state,
- SYNC_INITIAL_STATE_LIMIT);
+ int disable_reasons = GetDisableReasons();
+ RecordSyncInitialState(disable_reasons, IsFirstSetupComplete());
// If sync isn't allowed, the only thing to do is to turn it off.
- if (!IsSyncAllowed()) {
+ if ((disable_reasons & DISABLE_REASON_PLATFORM_OVERRIDE) ||
+ (disable_reasons & DISABLE_REASON_ENTERPRISE_POLICY)) {
// Only clear data if disallowed by policy.
- RequestStop(IsManaged() ? CLEAR_DATA : KEEP_DATA);
+ StopImpl((disable_reasons & DISABLE_REASON_ENTERPRISE_POLICY) ? CLEAR_DATA
+ : KEEP_DATA);
return;
}
@@ -327,16 +330,13 @@ void ProfileSyncService::Initialize() {
memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
base::BindRepeating(&ProfileSyncService::OnMemoryPressure,
sync_enabled_weak_factory_.GetWeakPtr()));
- startup_controller_->Reset(GetRegisteredDataTypes());
// Auto-start means the first time the profile starts up, sync should start up
// immediately.
- if (start_behavior_ == AUTO_START && IsSyncRequested() &&
- !IsFirstSetupComplete()) {
- startup_controller_->TryStartImmediately();
- } else {
- startup_controller_->TryStart();
- }
+ bool force_immediate = (start_behavior_ == AUTO_START &&
+ !HasDisableReason(DISABLE_REASON_USER_CHOICE) &&
+ !IsFirstSetupComplete());
+ startup_controller_->TryStart(force_immediate);
}
void ProfileSyncService::StartSyncingWithServer() {
@@ -356,14 +356,6 @@ void ProfileSyncService::StartSyncingWithServer() {
engine_->StartSyncingWithServer();
}
-void ProfileSyncService::RegisterDataTypeController(
- std::unique_ptr<DataTypeController> data_type_controller) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK_EQ(data_type_controllers_.count(data_type_controller->type()), 0U);
- data_type_controllers_[data_type_controller->type()] =
- std::move(data_type_controller);
-}
-
bool ProfileSyncService::IsDataTypeControllerRunning(
syncer::ModelType type) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -397,29 +389,12 @@ syncer::DeviceInfoTracker* ProfileSyncService::GetDeviceInfoTracker() const {
return device_info_sync_bridge_.get();
}
-syncer::LocalDeviceInfoProvider*
+const syncer::LocalDeviceInfoProvider*
ProfileSyncService::GetLocalDeviceInfoProvider() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return local_device_.get();
}
-void ProfileSyncService::OnSessionRestoreComplete() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DataTypeController::TypeMap::const_iterator iter =
- data_type_controllers_.find(syncer::SESSIONS);
- if (iter == data_type_controllers_.end()) {
- return;
- }
- DCHECK(iter->second);
-
- if (base::FeatureList::IsEnabled(switches::kSyncUSSSessions)) {
- sessions_sync_manager_->OnSessionRestoreComplete();
- } else {
- static_cast<sync_sessions::SessionDataTypeController*>(iter->second.get())
- ->OnSessionRestoreComplete();
- }
-}
-
syncer::WeakHandle<syncer::JsEventHandler>
ProfileSyncService::GetJsEventHandler() {
return syncer::MakeWeakHandle(sync_js_controller_.AsWeakPtr());
@@ -438,12 +413,50 @@ ProfileSyncService::GetUnrecoverableErrorHandler() {
return syncer::MakeWeakHandle(sync_enabled_weak_factory_.GetWeakPtr());
}
+void ProfileSyncService::AccountStateChanged() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (!IsSignedIn()) {
+ sync_disabled_by_admin_ = false;
+ StopImpl(CLEAR_DATA);
+ DCHECK(!engine_);
+ } else {
+ DCHECK(!engine_);
+ startup_controller_->TryStart(IsSetupInProgress());
+ }
+}
+
+void ProfileSyncService::CredentialsChanged() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ syncer::SyncCredentials credentials = auth_manager_->GetCredentials();
+
+ if (engine_) {
+ if (credentials.sync_token.empty()) {
+ engine_->InvalidateCredentials();
+ } else {
+ engine_->UpdateCredentials(credentials);
+ }
+ }
+
+ NotifyObservers();
+}
+
+bool ProfileSyncService::ShouldSyncStart(bool bypass_first_setup_check) {
+ if (!CanSyncStart()) {
+ return false;
+ }
+ return bypass_first_setup_check || IsFirstSetupComplete();
+}
+
void ProfileSyncService::ResetCryptoState() {
crypto_ = std::make_unique<syncer::SyncServiceCrypto>(
base::BindRepeating(&ProfileSyncService::NotifyObservers,
base::Unretained(this)),
base::BindRepeating(&ProfileSyncService::GetPreferredDataTypes,
base::Unretained(this)),
+ base::BindRepeating(&ProfileSyncService::CanConfigureDataTypes,
+ base::Unretained(this)),
&sync_prefs_);
}
@@ -518,27 +531,17 @@ void ProfileSyncService::OnDataTypeRequestsSyncStartup(syncer::ModelType type) {
}
void ProfileSyncService::StartUpSlowEngineComponents() {
- invalidation::InvalidationService* invalidator =
- sync_client_->GetInvalidationService();
+ DCHECK(CanSyncStart());
engine_ = sync_client_->GetSyncApiComponentFactory()->CreateSyncEngine(
- debug_identifier_, invalidator, sync_prefs_.AsWeakPtr(),
- FormatSyncDataPath(base_directory_));
+ debug_identifier_, sync_client_->GetInvalidationService(),
+ sync_prefs_.AsWeakPtr(),
+ sync_client_->GetModelTypeStoreService()->GetSyncDataPath());
// Clear any old errors the first time sync starts.
if (!IsFirstSetupComplete())
ClearStaleErrors();
- InitializeEngine();
-
- UpdateFirstSyncTimePref();
-
- ReportPreviousSessionMemoryWarningCount();
-}
-
-void ProfileSyncService::InitializeEngine() {
- DCHECK(engine_);
-
if (!sync_thread_) {
sync_thread_ = std::make_unique<base::Thread>("Chrome_SyncThread");
base::Thread::Options options;
@@ -547,12 +550,12 @@ void ProfileSyncService::InitializeEngine() {
DCHECK(success);
}
- SyncEngine::InitParams params;
+ syncer::SyncEngine::InitParams params;
params.sync_task_runner = sync_thread_->task_runner();
params.host = this;
- params.registrar = std::make_unique<SyncBackendRegistrar>(
+ params.registrar = std::make_unique<syncer::SyncBackendRegistrar>(
debug_identifier_,
- base::BindRepeating(&SyncClient::CreateModelWorkerForGroup,
+ base::BindRepeating(&syncer::SyncClient::CreateModelWorkerForGroup,
base::Unretained(sync_client_.get())));
params.encryption_observer_proxy = crypto_->GetEncryptionObserverProxy();
params.extensions_activity = sync_client_->GetExtensionsActivity();
@@ -566,7 +569,7 @@ void ProfileSyncService::InitializeEngine() {
sync_client_->GetInvalidationService();
params.invalidator_client_id =
invalidator ? invalidator->GetInvalidatorClientId() : "",
- params.sync_manager_factory = std::make_unique<SyncManagerFactory>();
+ params.sync_manager_factory = std::make_unique<syncer::SyncManagerFactory>();
// The first time we start up the engine we want to ensure we have a clean
// directory, so delete any old one that might be there.
params.delete_sync_data_folder = !IsFirstSetupComplete();
@@ -596,42 +599,16 @@ void ProfileSyncService::InitializeEngine() {
}
engine_->Initialize(std::move(params));
-}
-
-void ProfileSyncService::AccessTokenFetched(
- const GoogleServiceAuthError& error) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
- if (error.state() == GoogleServiceAuthError::NONE) {
- if (HasSyncingEngine()) {
- engine_->UpdateCredentials(auth_manager_->GetCredentials());
- } else {
- startup_controller_->TryStart();
- }
- }
- // Else: Some error happened. SyncAuthManager takes care of that (retry if
- // appropriate etc), so there's nothing for us to do here.
-
- NotifyObservers();
-}
-
-void ProfileSyncService::OnRefreshTokenAvailable() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
- if (HasSyncingEngine()) {
- auth_manager_->RequestAccessToken();
- } else {
- startup_controller_->TryStart();
- }
-}
-void ProfileSyncService::OnRefreshTokenRevoked() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ UpdateFirstSyncTimePref();
- if (HasSyncingEngine())
- engine_->InvalidateCredentials();
+ ReportPreviousSessionMemoryWarningCount();
- NotifyObservers();
+ // TODO(treib): Consider kicking off an access token fetch here. Currently,
+ // the flow goes as follows: The SyncEngine tries to connect to the server,
+ // but has no access token, so it ends up calling OnConnectionStatusChange(
+ // syncer::CONNECTION_AUTH_ERROR) which in turn causes SyncAuthManager to
+ // request a new access token. That seems needlessly convoluted.
}
void ProfileSyncService::Shutdown() {
@@ -639,16 +616,21 @@ void ProfileSyncService::Shutdown() {
ShutdownImpl(syncer::BROWSER_SHUTDOWN);
NotifyShutdown();
- // TODO(treib): DCHECK that all observers are gone now. All KeyedServices
- // should have unregistered their observers already before, in their own
- // Shutdown(), and all others should have done it now when they got the
- // shutdown notification.
+
if (sync_error_controller_) {
// Destroy the SyncErrorController when the service shuts down for good.
RemoveObserver(sync_error_controller_.get());
sync_error_controller_.reset();
}
+ // All observers must be gone now: All KeyedServices should have unregistered
+ // their observers already before, in their own Shutdown(), and all others
+ // should have done it now when they got the shutdown notification.
+ // Note: "might_have_observers" sounds like it might be inaccurate, but it can
+ // only return false positives while an iteration over the ObserverList is
+ // ongoing.
+ DCHECK(!observers_.might_have_observers());
+
auth_manager_.reset();
signin_scoped_device_id_callback_.Reset();
@@ -664,8 +646,9 @@ void ProfileSyncService::ShutdownImpl(syncer::ShutdownReason reason) {
// the data directory needs to be cleaned up here.
sync_thread_->task_runner()->PostTask(
FROM_HERE,
- base::BindOnce(&syncer::syncable::Directory::DeleteDirectoryFiles,
- FormatSyncDataPath(base_directory_)));
+ base::BindOnce(
+ &syncer::syncable::Directory::DeleteDirectoryFiles,
+ sync_client_->GetModelTypeStoreService()->GetSyncDataPath()));
}
return;
}
@@ -690,7 +673,7 @@ void ProfileSyncService::ShutdownImpl(syncer::ShutdownReason reason) {
// When aborting as part of shutdown, we should expect an aborted sync
// configure result, else we'll dcheck when we try to read the sync error.
expect_sync_configuration_aborted_ = true;
- data_type_manager_->Stop();
+ data_type_manager_->Stop(reason);
}
data_type_manager_.reset();
}
@@ -698,7 +681,7 @@ void ProfileSyncService::ShutdownImpl(syncer::ShutdownReason reason) {
// Shutdown the migrator before the engine to ensure it doesn't pull a null
// snapshot.
migrator_.reset();
- sync_js_controller_.AttachJsBackend(WeakHandle<syncer::JsBackend>());
+ sync_js_controller_.AttachJsBackend(syncer::WeakHandle<syncer::JsBackend>());
engine_->Shutdown(reason);
engine_.reset();
@@ -708,7 +691,7 @@ void ProfileSyncService::ShutdownImpl(syncer::ShutdownReason reason) {
sync_enabled_weak_factory_.InvalidateWeakPtrs();
- startup_controller_->Reset(GetRegisteredDataTypes());
+ startup_controller_->Reset();
// If the sync DB is getting destroyed, the local DeviceInfo is no longer
// valid and should be cleared from the cache.
@@ -744,6 +727,96 @@ void ProfileSyncService::StopImpl(SyncStopDataFate data_fate) {
}
}
+int ProfileSyncService::GetDisableReasons() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ int result = DISABLE_REASON_NONE;
+ if (!IsSyncAllowedByFlag() || !IsSyncAllowedByPlatform()) {
+ result = result | DISABLE_REASON_PLATFORM_OVERRIDE;
+ }
+ if (sync_prefs_.IsManaged() || sync_disabled_by_admin_) {
+ result = result | DISABLE_REASON_ENTERPRISE_POLICY;
+ }
+ // Local sync doesn't require sign-in.
+ if (!IsSignedIn() && !IsLocalSyncEnabled()) {
+ result = result | DISABLE_REASON_NOT_SIGNED_IN;
+ }
+ // When local sync is on sync should be considered requsted or otherwise it
+ // will not resume after the policy or the flag has been removed.
+ if (!sync_prefs_.IsSyncRequested() && !IsLocalSyncEnabled()) {
+ result = result | DISABLE_REASON_USER_CHOICE;
+ }
+ if (unrecoverable_error_reason_ != ERROR_REASON_UNSET) {
+ result = result | DISABLE_REASON_UNRECOVERABLE_ERROR;
+ }
+ return result;
+}
+
+syncer::SyncService::State ProfileSyncService::GetState() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (GetDisableReasons() != DISABLE_REASON_NONE) {
+ // We shouldn't have an engine while in a disabled state, with one
+ // exception: When encountering an unrecoverable error, we post a task to
+ // shut down instead of doing it immediately, so there's a brief timeframe
+ // where we have an unrecoverable error but the engine still exists.
+ // TODO(crbug.com/839834): See if we can change this by either shutting down
+ // immediately (not posting a task), or setting the unrecoverable error as
+ // part of the posted task.
+ DCHECK(HasDisableReason(DISABLE_REASON_UNRECOVERABLE_ERROR) || !engine_);
+ return State::DISABLED;
+ }
+
+ // Since there is no disable reason, Sync can start in principle.
+ DCHECK(CanSyncStart());
+
+ // Typically, Sync won't start until the initial setup is at least in
+ // progress. StartupController::TryStartImmediately bypasses the first setup
+ // check though, so we first have to check whether the engine is initialized.
+ if (!IsEngineInitialized()) {
+ switch (startup_controller_->GetState()) {
+ case syncer::StartupController::State::NOT_STARTED:
+ DCHECK(!engine_);
+ return State::WAITING_FOR_START_REQUEST;
+ case syncer::StartupController::State::STARTING_DEFERRED:
+ DCHECK(!engine_);
+ return State::START_DEFERRED;
+ case syncer::StartupController::State::STARTED:
+ DCHECK(engine_);
+ return State::INITIALIZING;
+ }
+ NOTREACHED();
+ }
+ DCHECK(engine_);
+ // The DataTypeManager gets created once the engine is initialized.
+ DCHECK(data_type_manager_);
+
+ // At this point we should usually be able to configure our data types (and
+ // once the data types can be configured, they must actually get configured).
+ // However, if the initial setup hasn't been completed, then we can't
+ // configure the data types. Also if a later (non-initial) setup happens to be
+ // in progress, we won't configure them right now.
+ if (data_type_manager_->state() == DataTypeManager::STOPPED) {
+ DCHECK(!CanConfigureDataTypes());
+ return State::PENDING_DESIRED_CONFIGURATION;
+ }
+
+ // The DataTypeManager shouldn't get configured (i.e. leave the STOPPED state)
+ // before the initial setup is complete.
+ DCHECK(IsFirstSetupComplete());
+
+ // Note that if a setup is started after the data types have been configured,
+ // then they'll stay configured even though CanConfigureDataTypes will be
+ // false.
+ DCHECK(CanConfigureDataTypes() || IsSetupInProgress());
+
+ if (data_type_manager_->state() != DataTypeManager::CONFIGURED) {
+ return State::CONFIGURING;
+ }
+
+ return State::ACTIVE;
+}
+
bool ProfileSyncService::IsFirstSetupComplete() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return sync_prefs_.IsFirstSetupComplete();
@@ -760,7 +833,8 @@ void ProfileSyncService::SetFirstSetupComplete() {
bool ProfileSyncService::IsSyncConfirmationNeeded() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return (!IsLocalSyncEnabled() && IsSignedIn()) && !IsFirstSetupInProgress() &&
- !IsFirstSetupComplete() && IsSyncRequested();
+ !IsFirstSetupComplete() &&
+ !HasDisableReason(DISABLE_REASON_USER_CHOICE);
}
void ProfileSyncService::UpdateLastSyncedTime() {
@@ -790,7 +864,7 @@ void ProfileSyncService::NotifyShutdown() {
void ProfileSyncService::ClearStaleErrors() {
ClearUnrecoverableError();
- last_actionable_error_ = SyncProtocolError();
+ last_actionable_error_ = syncer::SyncProtocolError();
// Clear the data type errors as well.
if (data_type_manager_)
data_type_manager_->ResetDataTypeErrors();
@@ -810,14 +884,13 @@ void ProfileSyncService::OnUnrecoverableError(const base::Location& from_here,
// Unrecoverable errors that arrive via the syncer::UnrecoverableErrorHandler
// interface are assumed to originate within the syncer.
unrecoverable_error_reason_ = ERROR_REASON_SYNCER;
- OnUnrecoverableErrorImpl(from_here, message, true);
+ OnUnrecoverableErrorImpl(from_here, message);
}
void ProfileSyncService::OnUnrecoverableErrorImpl(
const base::Location& from_here,
- const std::string& message,
- bool delete_sync_database) {
- DCHECK(HasUnrecoverableError());
+ const std::string& message) {
+ DCHECK_NE(unrecoverable_error_reason_, ERROR_REASON_UNSET);
unrecoverable_error_message_ = message;
unrecoverable_error_location_ = from_here;
@@ -826,12 +899,13 @@ void ProfileSyncService::OnUnrecoverableErrorImpl(
LOG(ERROR) << "Unrecoverable error detected at " << from_here.ToString()
<< " -- ProfileSyncService unusable: " << message;
+ NotifyObservers();
+
// Shut all data types down.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&ProfileSyncService::ShutdownImpl,
sync_enabled_weak_factory_.GetWeakPtr(),
- delete_sync_database ? syncer::DISABLE_SYNC
- : syncer::STOP_SYNC));
+ syncer::DISABLE_SYNC));
}
void ProfileSyncService::ReenableDatatype(syncer::ModelType type) {
@@ -841,9 +915,7 @@ void ProfileSyncService::ReenableDatatype(syncer::ModelType type) {
data_type_manager_->ReenableType(type);
}
-void ProfileSyncService::UpdateEngineInitUMA(bool success) {
- is_first_time_sync_configure_ = !IsFirstSetupComplete();
-
+void ProfileSyncService::UpdateEngineInitUMA(bool success) const {
if (is_first_time_sync_configure_) {
UMA_HISTOGRAM_BOOLEAN("Sync.BackendInitializeFirstTimeSuccess", success);
} else {
@@ -861,29 +933,22 @@ void ProfileSyncService::UpdateEngineInitUMA(bool success) {
}
void ProfileSyncService::OnEngineInitialized(
- ModelTypeSet initial_types,
+ syncer::ModelTypeSet initial_types,
const syncer::WeakHandle<syncer::JsBackend>& js_backend,
const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
debug_info_listener,
const std::string& cache_guid,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ is_first_time_sync_configure_ = !IsFirstSetupComplete();
+
UpdateEngineInitUMA(success);
if (!success) {
// Something went unexpectedly wrong. Play it safe: stop syncing at once
// and surface error UI to alert the user sync has stopped.
- // Keep the directory around for now so that on restart we will retry
- // again and potentially succeed in presence of transient file IO failures
- // or permissions issues, etc.
- //
- // TODO(rlarocque): Consider making this UnrecoverableError less special.
- // Unlike every other UnrecoverableError, it does not delete our sync data.
- // This exception made sense at the time it was implemented, but our new
- // directory corruption recovery mechanism makes it obsolete. By the time
- // we get here, we will have already tried and failed to delete the
- // directory. It would be no big deal if we tried to delete it again.
- OnInternalUnrecoverableError(FROM_HERE, "BackendInitialize failure", false,
+ OnInternalUnrecoverableError(FROM_HERE, "BackendInitialize failure",
ERROR_REASON_ENGINE_INIT_FAILURE);
return;
}
@@ -1001,7 +1066,8 @@ void ProfileSyncService::OnMigrationNeededForTypes(syncer::ModelTypeSet types) {
migrator_->MigrateTypes(types);
}
-void ProfileSyncService::OnActionableError(const SyncProtocolError& error) {
+void ProfileSyncService::OnActionableError(
+ const syncer::SyncProtocolError& error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
last_actionable_error_ = error;
DCHECK_NE(last_actionable_error_.action, syncer::UNKNOWN_ACTION);
@@ -1013,20 +1079,23 @@ void ProfileSyncService::OnActionableError(const SyncProtocolError& error) {
// TODO(lipalani) : if setup in progress we want to display these
// actions in the popup. The current experience might not be optimal for
// the user. We just dismiss the dialog.
- if (startup_controller_->IsSetupInProgress()) {
- RequestStop(CLEAR_DATA);
+ if (IsSetupInProgress()) {
+ StopImpl(CLEAR_DATA);
expect_sync_configuration_aborted_ = true;
}
// Trigger an unrecoverable error to stop syncing.
OnInternalUnrecoverableError(FROM_HERE,
last_actionable_error_.error_description,
- true, ERROR_REASON_ACTIONABLE_ERROR);
+ ERROR_REASON_ACTIONABLE_ERROR);
break;
case syncer::DISABLE_SYNC_ON_CLIENT:
if (error.error_type == syncer::NOT_MY_BIRTHDAY) {
UMA_HISTOGRAM_ENUMERATION("Sync.StopSource", syncer::BIRTHDAY_ERROR,
syncer::STOP_SOURCE_LIMIT);
}
+ // Note: Here we explicitly want RequestStop (rather than StopImpl), so
+ // that IsSyncRequested gets set to false, and Sync won't start again on
+ // the next browser startup.
RequestStop(CLEAR_DATA);
#if !defined(OS_CHROMEOS)
// On every platform except ChromeOS, sign out the user after a dashboard
@@ -1046,13 +1115,13 @@ void ProfileSyncService::OnActionableError(const SyncProtocolError& error) {
break;
case syncer::RESET_LOCAL_SYNC_DATA:
ShutdownImpl(syncer::DISABLE_SYNC);
- startup_controller_->TryStart();
+ startup_controller_->TryStart(IsSetupInProgress());
UMA_HISTOGRAM_ENUMERATION(
"Sync.ClearServerDataEvents",
syncer::CLEAR_SERVER_DATA_RESET_LOCAL_DATA_RECEIVED,
syncer::CLEAR_SERVER_DATA_MAX);
break;
- default:
+ case syncer::UNKNOWN_ACTION:
NOTREACHED();
}
NotifyObservers();
@@ -1076,7 +1145,7 @@ void ProfileSyncService::OnClearServerDataDone() {
// Shutdown sync, delete the Directory, then restart, restoring the cached
// nigori state.
ShutdownImpl(syncer::DISABLE_SYNC);
- startup_controller_->TryStart();
+ startup_controller_->TryStart(IsSetupInProgress());
UMA_HISTOGRAM_ENUMERATION("Sync.ClearServerDataEvents",
syncer::CLEAR_SERVER_DATA_SUCCEEDED,
syncer::CLEAR_SERVER_DATA_MAX);
@@ -1093,7 +1162,7 @@ void ProfileSyncService::ClearServerDataForTest(const base::Closure& callback) {
void ProfileSyncService::OnConfigureDone(
const DataTypeManager::ConfigureResult& result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- data_type_status_table_ = result.data_type_status_table;
+ data_type_error_map_ = result.data_type_status_table.GetAllErrors();
if (!sync_configure_start_time_.is_null()) {
if (result.status == DataTypeManager::OK) {
@@ -1138,17 +1207,18 @@ void ProfileSyncService::OnConfigureDone(
}
// Something catastrophic had happened. We should only have one
// error representing it.
- syncer::SyncError error = data_type_status_table_.GetUnrecoverableError();
+ syncer::SyncError error =
+ result.data_type_status_table.GetUnrecoverableError();
DCHECK(error.IsSet());
std::string message =
"Sync configuration failed with status " +
DataTypeManager::ConfigureStatusToString(result.status) +
" caused by " +
syncer::ModelTypeSetToString(
- data_type_status_table_.GetUnrecoverableErrorTypes()) +
+ result.data_type_status_table.GetUnrecoverableErrorTypes()) +
": " + error.message();
LOG(ERROR) << "ProfileSyncService error: " << message;
- OnInternalUnrecoverableError(error.location(), message, true,
+ OnInternalUnrecoverableError(error.location(), message,
ERROR_REASON_CONFIGURATION_FAILURE);
return;
}
@@ -1157,8 +1227,7 @@ void ProfileSyncService::OnConfigureDone(
// We should never get in a state where we have no encrypted datatypes
// enabled, and yet we still think we require a passphrase for decryption.
- DCHECK(
- !(IsPassphraseRequiredForDecryption() && !IsEncryptedDatatypeEnabled()));
+ DCHECK(!IsPassphraseRequiredForDecryption() || IsEncryptedDatatypeEnabled());
// This must be done before we start syncing with the server to avoid
// sending unencrypted data up on a first time sync.
@@ -1166,7 +1235,7 @@ void ProfileSyncService::OnConfigureDone(
engine_->EnableEncryptEverything();
NotifyObservers();
- if (migrator_.get() && migrator_->state() != BackendMigrator::IDLE) {
+ if (migrator_.get() && migrator_->state() != syncer::BackendMigrator::IDLE) {
// Migration in progress. Let the migrator know we just finished
// configuring something. It will be up to the migrator to call
// StartSyncingWithServer() if migration is now finished.
@@ -1191,42 +1260,19 @@ void ProfileSyncService::OnConfigureStart() {
NotifyObservers();
}
-std::string ProfileSyncService::QuerySyncStatusSummaryString() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
- if (HasUnrecoverableError())
- return "Unrecoverable error detected";
- if (!engine_)
- return "Syncing not enabled";
- if (!IsFirstSetupComplete())
- return "First time sync setup incomplete";
- if (data_type_manager_ &&
- data_type_manager_->state() == DataTypeManager::STOPPED) {
- return "Datatypes not fully initialized";
- }
- if (IsSyncActive())
- return "Sync service initialized";
-
- return "Status unknown: Internal error?";
-}
-
-std::string ProfileSyncService::GetEngineInitializationStateString() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return startup_controller_->GetEngineInitializationStateString();
-}
-
bool ProfileSyncService::IsSetupInProgress() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return startup_controller_->IsSetupInProgress();
+ return outstanding_setup_in_progress_handles_ > 0;
}
-bool ProfileSyncService::QueryDetailedSyncStatus(SyncEngine::Status* result) {
+bool ProfileSyncService::QueryDetailedSyncStatus(
+ syncer::SyncEngine::Status* result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (engine_ && engine_initialized_) {
*result = engine_->GetDetailedStatus();
return true;
}
- SyncEngine::Status status;
+ syncer::SyncEngine::Status status;
status.sync_protocol_error = last_actionable_error_;
*result = status;
return false;
@@ -1243,7 +1289,7 @@ bool ProfileSyncService::CanConfigureDataTypes() const {
bool ProfileSyncService::IsFirstSetupInProgress() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return !IsFirstSetupComplete() && startup_controller_->IsSetupInProgress();
+ return !IsFirstSetupComplete() && IsSetupInProgress();
}
std::unique_ptr<syncer::SyncSetupInProgressHandle>
@@ -1251,8 +1297,7 @@ ProfileSyncService::GetSetupInProgressHandle() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (++outstanding_setup_in_progress_handles_ == 1) {
- DCHECK(!startup_controller_->IsSetupInProgress());
- startup_controller_->SetSetupInProgress(true);
+ startup_controller_->TryStart(/*force_immediate=*/true);
NotifyObservers();
}
@@ -1262,17 +1307,6 @@ ProfileSyncService::GetSetupInProgressHandle() {
weak_factory_.GetWeakPtr()));
}
-bool ProfileSyncService::IsSyncAllowed() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return IsSyncAllowedByFlag() && !IsManaged() && IsSyncAllowedByPlatform();
-}
-
-bool ProfileSyncService::IsSyncActive() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return engine_initialized_ && data_type_manager_ &&
- data_type_manager_->state() != DataTypeManager::STOPPED;
-}
-
bool ProfileSyncService::IsLocalSyncEnabled() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return sync_prefs_.IsLocalSyncEnabled();
@@ -1289,33 +1323,11 @@ bool ProfileSyncService::IsSignedIn() const {
return !GetAuthenticatedAccountInfo().account_id.empty();
}
-bool ProfileSyncService::CanEngineStart() const {
- if (IsLocalSyncEnabled())
- return true;
- return CanSyncStart() && auth_manager_->RefreshTokenIsAvailable();
-}
-
bool ProfileSyncService::IsEngineInitialized() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return engine_initialized_;
}
-bool ProfileSyncService::ConfigurationDone() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return data_type_manager_ &&
- data_type_manager_->state() == DataTypeManager::CONFIGURED;
-}
-
-bool ProfileSyncService::waiting_for_auth() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return auth_manager_->IsAuthInProgress();
-}
-
-bool ProfileSyncService::HasUnrecoverableError() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return unrecoverable_error_reason_ != ERROR_REASON_UNSET;
-}
-
bool ProfileSyncService::IsPassphraseRequired() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return crypto_->passphrase_required_reason() !=
@@ -1358,13 +1370,17 @@ void ProfileSyncService::UpdateSelectedTypesHistogram(
#if BUILDFLAG(ENABLE_READING_LIST)
syncer::user_selectable_type::READING_LIST,
#endif
+ syncer::user_selectable_type::USER_EVENTS,
syncer::user_selectable_type::PROXY_TABS,
};
- static_assert(41 == syncer::MODEL_TYPE_COUNT,
- "If adding a user selectable type, update "
- "UserSelectableSyncType in user_selectable_sync_type.h and "
- "histograms.xml.");
+ static_assert(42 == syncer::MODEL_TYPE_COUNT,
+ "If adding a user selectable type (that is exposed to the user "
+ "via the sync preferences UI), update "
+ "1) The user_selectable_types[] above;"
+ "2) UserSelectableSyncType in user_selectable_sync_type.h and "
+ "histograms.xml; "
+ "3) UserSelectableTypes() in sync/syncable/model_type.h.");
if (!sync_everything) {
const syncer::ModelTypeSet current_types = GetPreferredDataTypes();
@@ -1394,7 +1410,7 @@ void ProfileSyncService::OnUserChoseDatatypes(
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(syncer::UserSelectableTypes().HasAll(chosen_types));
- if (!engine_ && !HasUnrecoverableError()) {
+ if (!engine_ && !HasDisableReason(DISABLE_REASON_UNRECOVERABLE_ERROR)) {
NOTREACHED();
return;
}
@@ -1404,16 +1420,11 @@ void ProfileSyncService::OnUserChoseDatatypes(
if (data_type_manager_)
data_type_manager_->ResetDataTypeErrors();
- ChangePreferredDataTypes(chosen_types);
-}
-void ProfileSyncService::ChangePreferredDataTypes(
- syncer::ModelTypeSet preferred_types) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DVLOG(1) << "ChangePreferredDataTypes invoked";
const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes();
// Will only enable those types that are registered and preferred.
- sync_prefs_.SetPreferredDataTypes(registered_types, preferred_types);
+ sync_prefs_.SetPreferredDataTypes(registered_types, chosen_types,
+ user_events_separate_pref_group_);
// Now reconfigure the DTM.
ReconfigureDatatypeManager();
@@ -1451,7 +1462,8 @@ syncer::ModelTypeSet ProfileSyncService::GetPreferredDataTypes() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes();
const syncer::ModelTypeSet preferred_types =
- Union(sync_prefs_.GetPreferredDataTypes(registered_types),
+ Union(sync_prefs_.GetPreferredDataTypes(registered_types,
+ user_events_separate_pref_group_),
syncer::ControlTypes());
const syncer::ModelTypeSet enforced_types =
Intersection(GetDataTypesFromPreferenceProviders(), registered_types);
@@ -1514,16 +1526,6 @@ void ProfileSyncService::SetPlatformSyncAllowedProvider(
platform_sync_allowed_provider_ = platform_sync_allowed_provider;
}
-// static
-syncer::RepeatingModelTypeStoreFactory
-ProfileSyncService::GetModelTypeStoreFactory(const base::FilePath& base_path) {
- // TODO(skym): Verify using AsUTF8Unsafe is okay here. Should work as long
- // as the Local State file is guaranteed to be UTF-8.
- const std::string path =
- FormatSharedModelTypeStorePath(base_path).AsUTF8Unsafe();
- return base::BindRepeating(&ModelTypeStore::CreateStore, path);
-}
-
void ProfileSyncService::ConfigureDataTypeManager() {
// Don't configure datatypes if the setup UI is still on the screen - this
// is to help multi-screen setting UIs (like iOS) where they don't want to
@@ -1542,7 +1544,7 @@ void ProfileSyncService::ConfigureDataTypeManager() {
restart = true;
// We create the migrator at the same time.
- migrator_ = std::make_unique<BackendMigrator>(
+ migrator_ = std::make_unique<syncer::BackendMigrator>(
debug_identifier_, GetUserShare(), this, data_type_manager_.get(),
base::BindRepeating(&ProfileSyncService::StartSyncingWithServer,
base::Unretained(this)));
@@ -1557,7 +1559,9 @@ void ProfileSyncService::ConfigureDataTypeManager() {
// previous configuration left off).
// TODO(sync): consider detecting configuration recovery and setting
// the reason here appropriately.
- reason = syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE;
+ reason = is_first_time_sync_configure_
+ ? syncer::CONFIGURE_REASON_NEW_CLIENT
+ : syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE;
} else {
// The user initiated a reconfiguration (either to add or remove types).
reason = syncer::CONFIGURE_REASON_RECONFIGURATION;
@@ -1583,12 +1587,12 @@ syncer::SyncCycleSnapshot ProfileSyncService::GetLastCycleSnapshot() const {
void ProfileSyncService::HasUnsyncedItemsForTest(
base::OnceCallback<void(bool)> cb) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(HasSyncingEngine());
+ DCHECK(engine_);
DCHECK(engine_initialized_);
engine_->HasUnsyncedItemsForTest(std::move(cb));
}
-BackendMigrator* ProfileSyncService::GetBackendMigratorForTest() {
+syncer::BackendMigrator* ProfileSyncService::GetBackendMigratorForTest() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return migrator_.get();
}
@@ -1601,9 +1605,10 @@ std::unique_ptr<base::Value> ProfileSyncService::GetTypeStatusMap() {
return std::move(result);
}
- SyncEngine::Status detailed_status = engine_->GetDetailedStatus();
- const ModelTypeSet& throttled_types(detailed_status.throttled_types);
- const ModelTypeSet& backed_off_types(detailed_status.backed_off_types);
+ syncer::SyncEngine::Status detailed_status = engine_->GetDetailedStatus();
+ const syncer::ModelTypeSet& throttled_types(detailed_status.throttled_types);
+ const syncer::ModelTypeSet& backed_off_types(
+ detailed_status.backed_off_types);
std::unique_ptr<base::DictionaryValue> type_status_header(
new base::DictionaryValue());
@@ -1616,21 +1621,20 @@ std::unique_ptr<base::Value> ProfileSyncService::GetTypeStatusMap() {
type_status_header->SetString("group_type", "Group Type");
result->Append(std::move(type_status_header));
- const DataTypeStatusTable::TypeErrorMap error_map =
- data_type_status_table_.GetAllErrors();
- ModelSafeRoutingInfo routing_info;
+ syncer::ModelSafeRoutingInfo routing_info;
engine_->GetModelSafeRoutingInfo(&routing_info);
- const ModelTypeSet registered = GetRegisteredDataTypes();
- for (ModelTypeSet::Iterator it = registered.First(); it.Good(); it.Inc()) {
- ModelType type = it.Get();
+ const syncer::ModelTypeSet registered = GetRegisteredDataTypes();
+ for (syncer::ModelTypeSet::Iterator it = registered.First(); it.Good();
+ it.Inc()) {
+ syncer::ModelType type = it.Get();
auto type_status = std::make_unique<base::DictionaryValue>();
type_status->SetString("name", ModelTypeToString(type));
type_status->SetString("group_type",
ModelSafeGroupToString(routing_info[type]));
- if (error_map.find(type) != error_map.end()) {
- const syncer::SyncError& error = error_map.find(type)->second;
+ if (data_type_error_map_.find(type) != data_type_error_map_.end()) {
+ const syncer::SyncError& error = data_type_error_map_.find(type)->second;
DCHECK(error.IsSet());
switch (error.GetSeverity()) {
case syncer::SyncError::SYNC_ERROR_SEVERITY_ERROR:
@@ -1735,22 +1739,10 @@ void ProfileSyncService::OnSyncManagedPrefChange(bool is_sync_managed) {
StopImpl(CLEAR_DATA);
} else {
// Sync is no longer disabled by policy. Try starting it up if appropriate.
- startup_controller_->TryStart();
+ startup_controller_->TryStart(IsSetupInProgress());
}
}
-void ProfileSyncService::OnPrimaryAccountSet() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(!engine_);
-}
-
-void ProfileSyncService::OnPrimaryAccountCleared() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- sync_disabled_by_admin_ = false;
- RequestStop(CLEAR_DATA);
- DCHECK(!engine_);
-}
-
void ProfileSyncService::OnGaiaAccountsInCookieUpdated(
const std::vector<gaia::ListedAccount>& accounts,
const std::vector<gaia::ListedAccount>& signed_out_accounts,
@@ -1785,19 +1777,19 @@ bool ProfileSyncService::HasCookieJarMismatch(
}
void ProfileSyncService::AddProtocolEventObserver(
- ProtocolEventObserver* observer) {
+ syncer::ProtocolEventObserver* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
protocol_event_observers_.AddObserver(observer);
- if (HasSyncingEngine()) {
+ if (engine_) {
engine_->RequestBufferedProtocolEventsAndEnableForwarding();
}
}
void ProfileSyncService::RemoveProtocolEventObserver(
- ProtocolEventObserver* observer) {
+ syncer::ProtocolEventObserver* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
protocol_event_observers_.RemoveObserver(observer);
- if (HasSyncingEngine() && !protocol_event_observers_.might_have_observers()) {
+ if (engine_ && !protocol_event_observers_.might_have_observers()) {
engine_->DisableProtocolEventForwarding();
}
}
@@ -1917,13 +1909,14 @@ void ProfileSyncService::GetAllNodes(
return;
}
- ModelTypeSet all_types = GetActiveDataTypes();
+ syncer::ModelTypeSet all_types = GetActiveDataTypes();
all_types.PutAll(syncer::ControlTypes());
scoped_refptr<GetAllNodesRequestHelper> helper =
new GetAllNodesRequestHelper(all_types, callback);
- for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) {
- ModelType type = it.Get();
+ for (syncer::ModelTypeSet::Iterator it = all_types.First(); it.Good();
+ it.Inc()) {
+ syncer::ModelType type = it.Get();
const auto dtc_iter = data_type_controllers_.find(type);
if (dtc_iter != data_type_controllers_.end()) {
dtc_iter->second->GetAllNodes(base::BindRepeating(
@@ -1969,36 +1962,25 @@ bool ProfileSyncService::IsSyncAllowedByPlatform() const {
platform_sync_allowed_provider_.Run();
}
-bool ProfileSyncService::IsManaged() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return sync_prefs_.IsManaged() || sync_disabled_by_admin_;
-}
-
void ProfileSyncService::RequestStop(SyncStopDataFate data_fate) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sync_prefs_.SetSyncRequested(false);
StopImpl(data_fate);
}
-bool ProfileSyncService::IsSyncRequested() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // When local sync is on sync should be considered requsted or otherwise it
- // will not resume after the policy or the flag has been removed.
- return sync_prefs_.IsSyncRequested() || sync_prefs_.IsLocalSyncEnabled();
-}
-
void ProfileSyncService::RequestStart() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!IsSyncAllowed()) {
+ if (HasDisableReason(DISABLE_REASON_PLATFORM_OVERRIDE) ||
+ HasDisableReason(DISABLE_REASON_ENTERPRISE_POLICY)) {
// Sync cannot be requested if it's not allowed.
return;
}
DCHECK(sync_client_);
- if (!IsSyncRequested()) {
+ if (!sync_prefs_.IsSyncRequested()) {
sync_prefs_.SetSyncRequested(true);
NotifyObservers();
}
- startup_controller_->TryStartImmediately();
+ startup_controller_->TryStart(/*force_immediate=*/true);
}
void ProfileSyncService::ReconfigureDatatypeManager() {
@@ -2008,7 +1990,7 @@ void ProfileSyncService::ReconfigureDatatypeManager() {
if (engine_initialized_) {
DCHECK(engine_);
ConfigureDataTypeManager();
- } else if (HasUnrecoverableError()) {
+ } else if (HasDisableReason(DISABLE_REASON_UNRECOVERABLE_ERROR)) {
// There is nothing more to configure. So inform the listeners,
NotifyObservers();
@@ -2030,19 +2012,13 @@ syncer::ModelTypeSet ProfileSyncService::GetDataTypesFromPreferenceProviders()
return types;
}
-const DataTypeStatusTable& ProfileSyncService::data_type_status_table() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return data_type_status_table_;
-}
-
void ProfileSyncService::OnInternalUnrecoverableError(
const base::Location& from_here,
const std::string& message,
- bool delete_sync_database,
UnrecoverableErrorReason reason) {
- DCHECK(!HasUnrecoverableError());
+ DCHECK_EQ(unrecoverable_error_reason_, ERROR_REASON_UNSET);
unrecoverable_error_reason_ = reason;
- OnUnrecoverableErrorImpl(from_here, message, delete_sync_database);
+ OnUnrecoverableErrorImpl(from_here, message);
}
bool ProfileSyncService::IsRetryingAccessTokenFetchForTest() const {
@@ -2087,11 +2063,29 @@ syncer::SyncTokenStatus ProfileSyncService::GetSyncTokenStatus() const {
void ProfileSyncService::OverrideNetworkResourcesForTest(
std::unique_ptr<syncer::NetworkResources> network_resources) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // If the engine has already been created, then it holds a pointer to the
+ // previous |network_resources_| which will become invalid. In that case, shut
+ // down and recreate the engine, so that it gets the correct (overridden)
+ // NetworkResources.
+ // This is a horrible hack; the proper fix would be to inject the
+ // NetworkResources in the ctor instead of adding them retroactively.
+ bool restart = false;
+ if (engine_) {
+ StopImpl(KEEP_DATA);
+ restart = true;
+ }
+ DCHECK(!engine_);
+
+ // If a previous request (with the wrong network resources) already failed,
+ // the next one would be backed off, which breaks tests. So reset the backoff.
+ auth_manager_->ResetRequestAccessTokenBackoffForTest();
+
network_resources_ = std::move(network_resources);
-}
-bool ProfileSyncService::HasSyncingEngine() const {
- return engine_ != nullptr;
+ if (restart) {
+ RequestStart();
+ DCHECK(engine_);
+ }
}
void ProfileSyncService::UpdateFirstSyncTimePref() {
@@ -2111,11 +2105,6 @@ void ProfileSyncService::FlushDirectory() const {
engine_->FlushDirectory();
}
-base::FilePath ProfileSyncService::GetDirectoryPathForTest() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return FormatSyncDataPath(base_directory_);
-}
-
base::MessageLoop* ProfileSyncService::GetSyncLoopForTest() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return sync_thread_ ? sync_thread_->message_loop() : nullptr;
@@ -2171,9 +2160,9 @@ void ProfileSyncService::ReportPreviousSessionMemoryWarningCount() {
}
void ProfileSyncService::RecordMemoryUsageHistograms() {
- ModelTypeSet active_types = GetActiveDataTypes();
- for (ModelTypeSet::Iterator type_it = active_types.First(); type_it.Good();
- type_it.Inc()) {
+ syncer::ModelTypeSet active_types = GetActiveDataTypes();
+ for (syncer::ModelTypeSet::Iterator type_it = active_types.First();
+ type_it.Good(); type_it.Inc()) {
auto dtc_it = data_type_controllers_.find(type_it.Get());
if (dtc_it != data_type_controllers_.end())
dtc_it->second->RecordMemoryUsageHistogram();
@@ -2202,9 +2191,6 @@ void ProfileSyncService::OnSetupInProgressHandleDestroyed() {
if (--outstanding_setup_in_progress_handles_ != 0)
return;
- DCHECK(startup_controller_->IsSetupInProgress());
- startup_controller_->SetSetupInProgress(false);
-
if (IsEngineInitialized())
ReconfigureDatatypeManager();
NotifyObservers();
diff --git a/chromium/components/browser_sync/profile_sync_service.h b/chromium/components/browser_sync/profile_sync_service.h
index 63f93473298..d97259718bc 100644
--- a/chromium/components/browser_sync/profile_sync_service.h
+++ b/chromium/components/browser_sync/profile_sync_service.h
@@ -41,18 +41,20 @@
#include "components/sync/engine/sync_engine.h"
#include "components/sync/engine/sync_engine_host.h"
#include "components/sync/js/sync_js_controller.h"
-#include "components/sync/model/model_type_store.h"
#include "components/version_info/version_info.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "url/gurl.h"
-class ProfileOAuth2TokenService;
class SigninManagerWrapper;
namespace base {
class MessageLoop;
}
+namespace network {
+class SharedURLLoaderFactory;
+} // namespace network
+
namespace sync_sessions {
class AbstractSessionsSyncManager;
class FaviconCache;
@@ -91,11 +93,7 @@ class SyncAuthManager;
//
// When a datatype is registered, the user has the option of syncing it.
// The sync opt-in UI will show only registered types; a checkbox should
-// never be shown for an unregistered type, and nor should it ever be
-// synced.
-//
-// A datatype is considered registered once RegisterDataTypeController
-// has been called with that datatype's DataTypeController.
+// never be shown for an unregistered type, nor can it ever be synced.
//
// 'Preferred' (user preferences and opt-out for a datatype)
//
@@ -104,7 +102,7 @@ class SyncAuthManager;
// If a user has opted out of syncing a particular datatype, it will
// be registered, but not preferred.
//
-// This state is controlled by the ConfigurePreferredDataTypes and
+// This state is controlled by OnUserChoseDatatypes and
// GetPreferredDataTypes. They are stored in the preferences system,
// and persist; though if a datatype is not registered, it cannot
// be a preferred datatype.
@@ -211,15 +209,14 @@ class ProfileSyncService : public syncer::SyncService,
std::unique_ptr<syncer::SyncClient> sync_client;
std::unique_ptr<SigninManagerWrapper> signin_wrapper;
SigninScopedDeviceIdCallback signin_scoped_device_id_callback;
- ProfileOAuth2TokenService* oauth2_token_service = nullptr;
GaiaCookieManagerService* gaia_cookie_manager_service = nullptr;
StartBehavior start_behavior = MANUAL_START;
syncer::NetworkTimeUpdateCallback network_time_update_callback;
- base::FilePath base_directory;
scoped_refptr<net::URLRequestContextGetter> url_request_context;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory;
std::string debug_identifier;
version_info::Channel channel = version_info::Channel::UNKNOWN;
- syncer::RepeatingModelTypeStoreFactory model_type_store_factory;
+ bool user_events_separate_pref_group = false;
private:
DISALLOW_COPY_AND_ASSIGN(InitParams);
@@ -234,13 +231,12 @@ class ProfileSyncService : public syncer::SyncService,
void Initialize();
// syncer::SyncService implementation
+ int GetDisableReasons() const override;
+ State GetState() const override;
bool IsFirstSetupComplete() const override;
- bool IsSyncAllowed() const override;
- bool IsSyncActive() const override;
bool IsLocalSyncEnabled() const override;
void TriggerRefresh(const syncer::ModelTypeSet& types) override;
void OnDataTypeRequestsSyncStartup(syncer::ModelType type) override;
- bool CanSyncStart() const override;
void RequestStop(SyncStopDataFate data_fate) override;
void RequestStart() override;
syncer::ModelTypeSet GetActiveDataTypes() const override;
@@ -256,9 +252,7 @@ class ProfileSyncService : public syncer::SyncService,
std::unique_ptr<syncer::SyncSetupInProgressHandle> GetSetupInProgressHandle()
override;
bool IsSetupInProgress() const override;
- bool ConfigurationDone() const override;
const GoogleServiceAuthError& GetAuthError() const override;
- bool HasUnrecoverableError() const override;
bool IsEngineInitialized() const override;
sync_sessions::OpenTabsUIDelegate* GetOpenTabsUIDelegate() override;
bool IsPassphraseRequiredForDecryption() const override;
@@ -273,15 +267,12 @@ class ProfileSyncService : public syncer::SyncService,
bool IsCryptographerReady(
const syncer::BaseTransaction* trans) const override;
syncer::UserShare* GetUserShare() const override;
- syncer::LocalDeviceInfoProvider* GetLocalDeviceInfoProvider() const override;
- void RegisterDataTypeController(std::unique_ptr<syncer::DataTypeController>
- data_type_controller) override;
+ const syncer::LocalDeviceInfoProvider* GetLocalDeviceInfoProvider()
+ const override;
void ReenableDatatype(syncer::ModelType type) override;
syncer::SyncTokenStatus GetSyncTokenStatus() const override;
- std::string QuerySyncStatusSummaryString() override;
bool QueryDetailedSyncStatus(syncer::SyncStatus* result) override;
base::Time GetLastSyncedTime() const override;
- std::string GetEngineInitializationStateString() const override;
syncer::SyncCycleSnapshot GetLastCycleSnapshot() const override;
std::unique_ptr<base::Value> GetTypeStatusMap() override;
const GURL& sync_service_url() const override;
@@ -312,8 +303,8 @@ class ProfileSyncService : public syncer::SyncService,
syncer::SyncTypePreferenceProvider* provider) const;
// Returns the SyncableService or USS bridge for syncer::SESSIONS.
- virtual syncer::SyncableService* GetSessionsSyncableService();
- virtual base::WeakPtr<syncer::ModelTypeControllerDelegate>
+ syncer::SyncableService* GetSessionsSyncableService();
+ base::WeakPtr<syncer::ModelTypeControllerDelegate>
GetSessionSyncControllerDelegateOnUIThread();
// Returns the ModelTypeControllerDelegate for syncer::DEVICE_INFO.
@@ -323,9 +314,6 @@ class ProfileSyncService : public syncer::SyncService,
// Returns synced devices tracker.
virtual syncer::DeviceInfoTracker* GetDeviceInfoTracker() const;
- // Called when asynchronous session restore has completed.
- void OnSessionRestoreComplete();
-
// SyncEngineHost implementation.
void OnEngineInitialized(
syncer::ModelTypeSet initial_types,
@@ -359,12 +347,6 @@ class ProfileSyncService : public syncer::SyncService,
bool IsPassphraseRequired() const override;
syncer::ModelTypeSet GetEncryptedDataTypes() const override;
- // Called by the SyncAuthManager when the primary account changes.
- // TODO(crbug.com/842697): Make these private and pass a callback to the
- // SyncAuthManager.
- void OnPrimaryAccountSet();
- void OnPrimaryAccountCleared();
-
// GaiaCookieManagerService::Observer implementation.
void OnGaiaAccountsInCookieUpdated(
const std::vector<gaia::ListedAccount>& accounts,
@@ -384,23 +366,15 @@ class ProfileSyncService : public syncer::SyncService,
// Reconfigures the data type manager with the latest enabled types.
// Note: Does not initialize the engine if it is not already initialized.
// This function needs to be called only after sync has been initialized
- // (i.e.,only for reconfigurations). The reason we don't initialize the
+ // (i.e., only for reconfigurations). The reason we don't initialize the
// engine is because if we had encountered an unrecoverable error we don't
// want to startup once more.
- // This function is called by |SetSetupInProgress|.
virtual void ReconfigureDatatypeManager();
- syncer::PassphraseRequiredReason passphrase_required_reason() const {
+ syncer::PassphraseRequiredReason passphrase_required_reason_for_test() const {
return crypto_->passphrase_required_reason();
}
- // Returns true if sync is requested to be running by the user.
- // Note that this does not mean that sync WILL be running; e.g. if
- // IsSyncAllowed() is false then sync won't start, and if the user
- // doesn't confirm their settings (IsFirstSetupComplete), sync will
- // never become active. Use IsSyncActive to see if sync is running.
- virtual bool IsSyncRequested() const;
-
// Record stats on various events.
static void SyncEvent(SyncEventCodes code);
@@ -410,24 +384,16 @@ class ProfileSyncService : public syncer::SyncService,
// assume it's running on the UI thread.
static bool IsSyncAllowedByFlag();
- // Returns whether sync is currently allowed on this platform.
- bool IsSyncAllowedByPlatform() const;
-
// Whether sync is currently blocked from starting because the sync
- // confirmation dialog hasn't been confirmed.
+ // confirmation dialog hasn't been shown. Note that once the dialog is
+ // showing (i.e. IsFirstSetupInProgress() is true), this will return false.
+ // TODO(crbug.com/839834): This method is somewhat misnamed.
virtual bool IsSyncConfirmationNeeded() const;
- // Returns whether sync is managed, i.e. controlled by configuration
- // management. If so, the user is not allowed to configure sync.
- virtual bool IsManaged() const;
-
// syncer::UnrecoverableErrorHandler implementation.
void OnUnrecoverableError(const base::Location& from_here,
const std::string& message) override;
- // The functions below (until ActivateDataType()) should only be
- // called if IsEngineInitialized() is true.
-
// Returns whether or not the underlying sync engine has made any
// local changes to items that have not yet been synced with the
// server.
@@ -445,15 +411,9 @@ class ProfileSyncService : public syncer::SyncService,
// SyncPrefObserver implementation.
void OnSyncManagedPrefChange(bool is_sync_managed) override;
- // Changes which data types we're going to be syncing to |preferred_types|.
- // If it is running, the DataTypeManager will be instructed to reconfigure
- // the sync engine so that exactly these datatypes are actively synced. See
- // class comment for more on what it means for a datatype to be Preferred.
- virtual void ChangePreferredDataTypes(syncer::ModelTypeSet preferred_types);
-
// Returns the set of types which are enforced programmatically and can not
// be disabled by the user.
- virtual syncer::ModelTypeSet GetForcedDataTypes() const;
+ syncer::ModelTypeSet GetForcedDataTypes() const;
// Gets the set of all data types that could be allowed (the set that
// should be advertised to the user). These will typically only change
@@ -467,32 +427,12 @@ class ProfileSyncService : public syncer::SyncService,
virtual void SetEncryptEverythingAllowed(bool allowed);
// Returns true if the syncer is waiting for new datatypes to be encrypted.
- virtual bool encryption_pending() const;
+ bool encryption_pending() const;
syncer::SyncErrorController* sync_error_controller() {
return sync_error_controller_.get();
}
- // TODO(sync): This is only used in tests. Can we remove it?
- const syncer::DataTypeStatusTable& data_type_status_table() const;
-
- // If true, the ProfileSyncService has detected that a new GAIA signin has
- // succeeded, and is waiting for initialization to complete. This is used by
- // the UI to differentiate between a new auth error (encountered as part of
- // the initialization process) and a pre-existing auth error that just hasn't
- // been cleared yet. Virtual for testing purposes.
- virtual bool waiting_for_auth() const;
-
- // Called by the SyncAuthManager when the refresh token state changes.
- // TODO(crbug.com/842697): Make these private and pass a callback to the
- // SyncAuthManager.
- void OnRefreshTokenAvailable();
- void OnRefreshTokenRevoked();
-
- // Called by SyncAuthManager when an access token fetch attempt finishes
- // (successfully or not).
- void AccessTokenFetched(const GoogleServiceAuthError& error);
-
// KeyedService implementation. This must be called exactly
// once (before this object is destroyed).
void Shutdown() override;
@@ -521,14 +461,6 @@ class ProfileSyncService : public syncer::SyncService,
void SetPlatformSyncAllowedProvider(
const PlatformSyncAllowedProvider& platform_sync_allowed_provider);
- // Returns a function that will create a ModelTypeStore that shares
- // the sync LevelDB backend. |base_path| should be set to profile path.
- static syncer::RepeatingModelTypeStoreFactory GetModelTypeStoreFactory(
- const base::FilePath& base_path);
-
- // Needed to test whether the directory is deleted properly.
- base::FilePath GetDirectoryPathForTest() const;
-
// Sometimes we need to wait for tasks on the sync thread in tests.
base::MessageLoop* GetSyncLoopForTest() const;
@@ -546,6 +478,13 @@ class ProfileSyncService : public syncer::SyncService,
syncer::WeakHandle<syncer::UnrecoverableErrorHandler>
GetUnrecoverableErrorHandler();
+ // Callbacks for SyncAuthManager.
+ void AccountStateChanged();
+ void CredentialsChanged();
+
+ // Callback for StartupController.
+ bool ShouldSyncStart(bool bypass_first_setup_check);
+
// Destroys the |crypto_| object and creates a new one with fresh state.
void ResetCryptoState();
@@ -559,25 +498,11 @@ class ProfileSyncService : public syncer::SyncService,
ERROR_REASON_LIMIT
};
- // The initial state of sync, for the Sync.InitialState histogram. Even if
- // this value is CAN_START, sync startup might fail for reasons that we may
- // want to consider logging in the future, such as a passphrase needed for
- // decryption, or the version of Chrome being too old. This enum is used to
- // back a UMA histogram, and should therefore be treated as append-only.
- enum SyncInitialState {
- CAN_START, // Sync can attempt to start up.
- NOT_SIGNED_IN, // There is no signed in user.
- NOT_REQUESTED, // The user turned off sync.
- NOT_REQUESTED_NOT_SETUP, // The user turned off sync and setup completed
- // is false. Might indicate a stop-and-clear.
- NEEDS_CONFIRMATION, // The user must confirm sync settings.
- IS_MANAGED, // Sync is disallowed by enterprise policy.
- NOT_ALLOWED_BY_PLATFORM, // Sync is disallowed by the platform.
- SYNC_INITIAL_STATE_LIMIT
- };
-
friend class TestProfileSyncService;
+ // Returns whether sync is currently allowed on this platform.
+ bool IsSyncAllowedByPlatform() const;
+
// Helper to install and configure a data type manager.
void ConfigureDataTypeManager();
@@ -589,12 +514,9 @@ class ProfileSyncService : public syncer::SyncService,
// Helper method for managing encryption UI.
bool IsEncryptedDatatypeEnabled() const;
- // Helper for OnUnrecoverableError.
- // TODO(tim): Use an enum for |delete_sync_database| here, in ShutdownImpl,
- // and in SyncEngine::Shutdown.
+ // Helper for OnUnrecoverableError and OnInternalUnrecoverableError.
void OnUnrecoverableErrorImpl(const base::Location& from_here,
- const std::string& message,
- bool delete_sync_database);
+ const std::string& message);
// Stops the sync engine. Does NOT set IsSyncRequested to false. Use
// RequestStop for that. |data_fate| controls whether the local sync data is
@@ -619,11 +541,8 @@ class ProfileSyncService : public syncer::SyncService,
void ClearUnrecoverableError();
- // Starts up the engine sync components.
- virtual void StartUpSlowEngineComponents();
-
// Kicks off asynchronous initialization of the SyncEngine.
- void InitializeEngine();
+ virtual void StartUpSlowEngineComponents();
// Collects preferred sync data types from |preference_providers_|.
syncer::ModelTypeSet GetDataTypesFromPreferenceProviders() const;
@@ -638,25 +557,14 @@ class ProfileSyncService : public syncer::SyncService,
// Sync.UnrecoverableErrors histogram.
void OnInternalUnrecoverableError(const base::Location& from_here,
const std::string& message,
- bool delete_sync_database,
UnrecoverableErrorReason reason);
// Update UMA for syncing engine.
- void UpdateEngineInitUMA(bool success);
+ void UpdateEngineInitUMA(bool success) const;
// Whether sync has been authenticated with an account ID.
bool IsSignedIn() const;
- // The engine can only start if sync can start and has an auth token. This is
- // different fron CanSyncStart because it represents whether the engine can
- // be started at this moment, whereas CanSyncStart represents whether sync can
- // conceptually start without further user action (acquiring a token is an
- // automatic process).
- bool CanEngineStart() const;
-
- // True if a syncing engine exists.
- bool HasSyncingEngine() const;
-
// Update first sync time stored in preferences
void UpdateFirstSyncTimePref();
@@ -706,21 +614,23 @@ class ProfileSyncService : public syncer::SyncService,
// email address.
const std::unique_ptr<SigninManagerWrapper> signin_;
+ // Handles tracking of the authenticated account and acquiring access tokens.
+ // Only null after Shutdown().
std::unique_ptr<SyncAuthManager> auth_manager_;
// The product channel of the embedder.
const version_info::Channel channel_;
- // The path to the base directory under which sync should store its
- // information.
- const base::FilePath base_directory_;
-
// An identifier representing this instance for debugging purposes.
const std::string debug_identifier_;
// This specifies where to find the sync server.
const GURL sync_service_url_;
+ // Whether USER_EVENTS model type has a separate pref group instead of
+ // being bundled with the TYPED_URLS model type.
+ const bool user_events_separate_pref_group_;
+
// A utility object containing logic and state relating to encryption. It is
// never null.
std::unique_ptr<syncer::SyncServiceCrypto> crypto_;
@@ -754,6 +664,9 @@ class ProfileSyncService : public syncer::SyncService,
// The request context in which sync should operate.
scoped_refptr<net::URLRequestContextGetter> url_request_context_;
+ // The URL loader factory for the sync.
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
// Indicates if this is the first time sync is being configured. This value
// is equal to !IsFirstSetupComplete() at the time of OnEngineInitialized().
bool is_first_time_sync_configure_;
@@ -768,9 +681,9 @@ class ProfileSyncService : public syncer::SyncService,
// Whether the SyncEngine has been initialized.
bool engine_initialized_;
- // Set when sync receives DISABLED_BY_ADMIN error from server. Prevents
- // ProfileSyncService from starting engine till browser restarted or user
- // signed out.
+ // Set when sync receives STOP_SYNC_FOR_DISABLED_ACCOUNT error from server.
+ // Prevents ProfileSyncService from starting engine till browser restarted
+ // or user signed out.
bool sync_disabled_by_admin_;
// Information describing an unrecoverable error.
@@ -805,7 +718,7 @@ class ProfileSyncService : public syncer::SyncService,
// Tracks the set of failed data types (those that encounter an error
// or must delay loading for some reason).
- syncer::DataTypeStatusTable data_type_status_table_;
+ syncer::DataTypeStatusTable::TypeErrorMap data_type_error_map_;
// The set of currently enabled sync experiments.
syncer::Experiments current_experiments_;
@@ -819,6 +732,7 @@ class ProfileSyncService : public syncer::SyncService,
// Locally owned SyncableService or ModelTypeSyncBridge implementations.
std::unique_ptr<sync_sessions::AbstractSessionsSyncManager>
sessions_sync_manager_;
+
std::unique_ptr<syncer::DeviceInfoSyncBridge> device_info_sync_bridge_;
std::unique_ptr<syncer::NetworkResources> network_resources_;
@@ -841,12 +755,6 @@ class ProfileSyncService : public syncer::SyncService,
// platform.
PlatformSyncAllowedProvider platform_sync_allowed_provider_;
- // The factory used to initialize the ModelTypeStore passed to
- // sync bridges created by the ProfileSyncService. The default factory
- // creates an on disk leveldb-backed ModelTypeStore; one might override this
- // default to, e.g., use an in-memory db for unit tests.
- syncer::RepeatingModelTypeStoreFactory model_type_store_factory_;
-
// This weak factory invalidates its issued pointers when Sync is disabled.
base::WeakPtrFactory<ProfileSyncService> sync_enabled_weak_factory_;
diff --git a/chromium/components/browser_sync/profile_sync_service_autofill_unittest.cc b/chromium/components/browser_sync/profile_sync_service_autofill_unittest.cc
index c33feb4f3ef..254144d429e 100644
--- a/chromium/components/browser_sync/profile_sync_service_autofill_unittest.cc
+++ b/chromium/components/browser_sync/profile_sync_service_autofill_unittest.cc
@@ -95,6 +95,7 @@ namespace {
void RegisterAutofillPrefs(user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterBooleanPref(autofill::prefs::kAutofillCreditCardEnabled,
true);
+ registry->RegisterBooleanPref(autofill::prefs::kAutofillProfileEnabled, true);
registry->RegisterBooleanPref(autofill::prefs::kAutofillEnabled, true);
registry->RegisterBooleanPref(autofill::prefs::kAutofillWalletImportEnabled,
true);
@@ -176,7 +177,7 @@ class MockAutofillBackend : public autofill::AutofillWebDataBackend {
void NotifyThatSyncHasStarted(syncer::ModelType model_type) override {
DCHECK(!ui_task_runner_->RunsTasksInCurrentSequence());
ui_task_runner_->PostTask(FROM_HERE,
- base::Bind(on_sync_started_, model_type));
+ base::BindOnce(on_sync_started_, model_type));
}
private:
@@ -242,9 +243,9 @@ class WebDataServiceFake : public AutofillWebDataService {
&WebDataServiceFake::NotifySyncStartedOnUISequence, AsWeakPtr());
db_task_runner_->PostTask(
- FROM_HERE, base::Bind(&WebDataServiceFake::CreateSyncableService,
- base::Unretained(this), on_changed_callback,
- std::move(on_sync_started_callback)));
+ FROM_HERE, base::BindOnce(&WebDataServiceFake::CreateSyncableService,
+ base::Unretained(this), on_changed_callback,
+ std::move(on_sync_started_callback)));
syncable_service_created_or_destroyed_.Wait();
}
@@ -252,8 +253,8 @@ class WebDataServiceFake : public AutofillWebDataService {
// The |autofill_profile_syncable_service_| must be destructed on the DB
// sequence.
db_task_runner_->PostTask(
- FROM_HERE, base::Bind(&WebDataServiceFake::DestroySyncableService,
- base::Unretained(this)));
+ FROM_HERE, base::BindOnce(&WebDataServiceFake::DestroySyncableService,
+ base::Unretained(this)));
syncable_service_created_or_destroyed_.Wait();
}
@@ -359,7 +360,7 @@ class ProfileSyncServiceAutofillTest
new TokenWebDataServiceFake(base::ThreadTaskRunnerHandle::Get(),
data_type_thread()->task_runner()));
web_data_service_ = static_cast<WebDataServiceFake*>(
- web_data_wrapper_->GetAutofillWebData().get());
+ web_data_wrapper_->GetProfileAutofillWebData().get());
web_data_service_->SetDatabase(web_database_.get());
personal_data_manager_ = std::make_unique<MockPersonalDataManager>();
@@ -368,6 +369,7 @@ class ProfileSyncServiceAutofillTest
EXPECT_CALL(personal_data_manager(), LoadCreditCards());
personal_data_manager_->Init(web_data_service_,
+ /*account_database=*/nullptr,
profile_sync_service_bundle()->pref_service(),
/*identity_manager=*/nullptr,
/*is_off_the_record=*/false);
@@ -378,9 +380,9 @@ class ProfileSyncServiceAutofillTest
profile_sync_service_bundle());
builder.SetPersonalDataManager(personal_data_manager_.get());
builder.SetSyncServiceCallback(GetSyncServiceCallback());
- builder.SetSyncableServiceCallback(
- base::Bind(&ProfileSyncServiceAutofillTest::GetSyncableServiceForType,
- base::Unretained(this)));
+ builder.SetSyncableServiceCallback(base::BindRepeating(
+ &ProfileSyncServiceAutofillTest::GetSyncableServiceForType,
+ base::Unretained(this)));
builder.set_activate_model_creation();
sync_client_owned_ = builder.Build();
sync_client_ = sync_client_owned_.get();
@@ -415,6 +417,16 @@ class ProfileSyncServiceAutofillTest
CreateSyncService(std::move(sync_client_owned_), std::move(callback));
EXPECT_CALL(*profile_sync_service_bundle()->component_factory(),
+ CreateCommonDataTypeControllers(_, _))
+ .WillOnce(testing::InvokeWithoutArgs([=]() {
+ syncer::DataTypeController::TypeVector controllers;
+ controllers.push_back(
+ std::make_unique<AutofillProfileDataTypeController>(
+ data_type_thread()->task_runner(), base::DoNothing(),
+ sync_client_, web_data_service_));
+ return controllers;
+ }));
+ EXPECT_CALL(*profile_sync_service_bundle()->component_factory(),
CreateDataTypeManager(_, _, _, _, _, _))
.WillOnce(ReturnNewDataTypeManagerWithDebugListener(
sync_client_,
@@ -423,10 +435,6 @@ class ProfileSyncServiceAutofillTest
EXPECT_CALL(personal_data_manager(), IsDataLoaded())
.WillRepeatedly(Return(true));
- sync_service()->RegisterDataTypeController(
- std::make_unique<AutofillProfileDataTypeController>(
- data_type_thread()->task_runner(), base::DoNothing(), sync_client_,
- web_data_service_));
sync_service()->Initialize();
base::RunLoop().Run();
diff --git a/chromium/components/browser_sync/profile_sync_service_mock.cc b/chromium/components/browser_sync/profile_sync_service_mock.cc
index 56ed2496ba9..0b5b3077f8d 100644
--- a/chromium/components/browser_sync/profile_sync_service_mock.cc
+++ b/chromium/components/browser_sync/profile_sync_service_mock.cc
@@ -9,9 +9,7 @@
namespace browser_sync {
ProfileSyncServiceMock::ProfileSyncServiceMock(InitParams init_params)
- : ProfileSyncService(std::move(init_params)) {
- ON_CALL(*this, IsSyncRequested()).WillByDefault(testing::Return(true));
-}
+ : ProfileSyncService(std::move(init_params)) {}
ProfileSyncServiceMock::ProfileSyncServiceMock(InitParams* init_params)
: ProfileSyncServiceMock(std::move(*init_params)) {}
diff --git a/chromium/components/browser_sync/profile_sync_service_mock.h b/chromium/components/browser_sync/profile_sync_service_mock.h
index 627e4c6cbd9..d709fdaa0a9 100644
--- a/chromium/components/browser_sync/profile_sync_service_mock.h
+++ b/chromium/components/browser_sync/profile_sync_service_mock.h
@@ -64,38 +64,33 @@ class ProfileSyncServiceMock : public ProfileSyncService {
MOCK_CONST_METHOD0(IsEncryptEverythingEnabled, bool());
MOCK_METHOD0(EnableEncryptEverything, void());
- MOCK_METHOD1(ChangePreferredDataTypes,
- void(syncer::ModelTypeSet preferred_types));
MOCK_CONST_METHOD0(GetActiveDataTypes, syncer::ModelTypeSet());
MOCK_CONST_METHOD0(GetPreferredDataTypes, syncer::ModelTypeSet());
MOCK_CONST_METHOD0(GetRegisteredDataTypes, syncer::ModelTypeSet());
MOCK_CONST_METHOD0(GetLastCycleSnapshot, syncer::SyncCycleSnapshot());
+ MOCK_CONST_METHOD0(GetDisableReasons, int());
+ MOCK_CONST_METHOD0(GetState, State());
MOCK_METHOD1(QueryDetailedSyncStatus,
bool(syncer::SyncEngine::Status* result));
MOCK_CONST_METHOD0(GetAuthError, const GoogleServiceAuthError&());
MOCK_CONST_METHOD0(IsFirstSetupInProgress, bool());
MOCK_CONST_METHOD0(GetLastSyncedTime, base::Time());
- MOCK_CONST_METHOD0(HasUnrecoverableError, bool());
- MOCK_CONST_METHOD0(IsSyncActive, bool());
MOCK_CONST_METHOD0(IsEngineInitialized, bool());
- MOCK_CONST_METHOD0(IsSyncRequested, bool());
MOCK_CONST_METHOD0(IsSyncConfirmationNeeded, bool());
- MOCK_CONST_METHOD0(waiting_for_auth, bool());
MOCK_METHOD1(OnActionableError, void(const syncer::SyncProtocolError&));
MOCK_CONST_METHOD1(IsDataTypeControllerRunning, bool(syncer::ModelType));
MOCK_METHOD0(GetOpenTabsUIDelegateMock, sync_sessions::OpenTabsUIDelegate*());
sync_sessions::OpenTabsUIDelegate* GetOpenTabsUIDelegate() override;
+ MOCK_METHOD0(StartUpSlowEngineComponents, void());
+
// DataTypeManagerObserver mocks.
MOCK_METHOD1(OnConfigureDone,
void(const syncer::DataTypeManager::ConfigureResult&));
MOCK_METHOD0(OnConfigureStart, void());
- MOCK_CONST_METHOD0(CanSyncStart, bool());
- MOCK_CONST_METHOD0(IsManaged, bool());
-
MOCK_CONST_METHOD0(IsPassphraseRequired, bool());
MOCK_CONST_METHOD0(IsPassphraseRequiredForDecryption, bool());
MOCK_CONST_METHOD0(IsUsingSecondaryPassphrase, bool());
diff --git a/chromium/components/browser_sync/profile_sync_service_startup_unittest.cc b/chromium/components/browser_sync/profile_sync_service_startup_unittest.cc
index 519b4f9cc26..1ea8d3b3f1c 100644
--- a/chromium/components/browser_sync/profile_sync_service_startup_unittest.cc
+++ b/chromium/components/browser_sync/profile_sync_service_startup_unittest.cc
@@ -4,27 +4,15 @@
#include "components/browser_sync/profile_sync_service.h"
-#include "base/files/file_util.h"
-#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_task_environment.h"
-#include "base/threading/thread_task_runner_handle.h"
#include "components/browser_sync/profile_sync_test_util.h"
#include "components/prefs/pref_service.h"
-#include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
-#include "components/signin/core/browser/profile_oauth2_token_service.h"
-#include "components/signin/core/browser/signin_manager.h"
-#include "components/signin/core/browser/signin_pref_names.h"
#include "components/sync/base/pref_names.h"
#include "components/sync/driver/data_type_manager_mock.h"
#include "components/sync/driver/fake_data_type_controller.h"
#include "components/sync/driver/sync_api_component_factory_mock.h"
-#include "components/sync/driver/sync_service_observer.h"
#include "components/sync/engine/fake_sync_engine.h"
-#include "components/sync_preferences/pref_service_syncable.h"
-#include "google_apis/gaia/gaia_auth_consumer.h"
-#include "google_apis/gaia/gaia_constants.h"
-#include "net/url_request/url_request_test_util.h"
+#include "components/sync/engine/mock_sync_engine.h"
#include "services/identity/public/cpp/identity_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -32,31 +20,28 @@
using syncer::DataTypeManager;
using syncer::DataTypeManagerMock;
using syncer::FakeSyncEngine;
+using syncer::MockSyncEngine;
using testing::_;
-using testing::AnyNumber;
using testing::ByMove;
using testing::DoAll;
+using testing::InvokeWithoutArgs;
using testing::Mock;
+using testing::NiceMock;
using testing::Return;
namespace browser_sync {
namespace {
-const char kGaiaId[] = "12345";
const char kEmail[] = "test_user@gmail.com";
-class SyncServiceObserverMock : public syncer::SyncServiceObserver {
- public:
- SyncServiceObserverMock();
- ~SyncServiceObserverMock() override;
-
- MOCK_METHOD1(OnStateChanged, void(syncer::SyncService*));
-};
-
-SyncServiceObserverMock::SyncServiceObserverMock() {}
-
-SyncServiceObserverMock::~SyncServiceObserverMock() {}
+void SetError(DataTypeManager::ConfigureResult* result) {
+ syncer::DataTypeStatusTable::TypeErrorMap errors;
+ errors[syncer::BOOKMARKS] =
+ syncer::SyncError(FROM_HERE, syncer::SyncError::UNRECOVERABLE_ERROR,
+ "Error", syncer::BOOKMARKS);
+ result->data_type_status_table.UpdateFailedDataTypes(errors);
+}
} // namespace
@@ -74,255 +59,350 @@ ACTION_P3(InvokeOnConfigureDone, sync_service, error_callback, result) {
class ProfileSyncServiceStartupTest : public testing::Test {
public:
- ProfileSyncServiceStartupTest() {
+ ProfileSyncServiceStartupTest()
+ : scoped_task_environment_(
+ base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {
profile_sync_service_bundle_.auth_service()
->set_auto_post_fetch_response_on_message_loop(true);
}
~ProfileSyncServiceStartupTest() override {
- sync_service_->RemoveObserver(&observer_);
sync_service_->Shutdown();
}
- void CreateSyncService(ProfileSyncService::StartBehavior start_behavior) {
- component_factory_ = profile_sync_service_bundle_.component_factory();
+ void CreateSyncService(ProfileSyncService::StartBehavior start_behavior,
+ syncer::ModelTypeSet registered_types =
+ syncer::ModelTypeSet(syncer::BOOKMARKS)) {
ProfileSyncServiceBundle::SyncClientBuilder builder(
&profile_sync_service_bundle_);
ProfileSyncService::InitParams init_params =
profile_sync_service_bundle_.CreateBasicInitParams(start_behavior,
builder.Build());
+ ON_CALL(*component_factory(), CreateCommonDataTypeControllers(_, _))
+ .WillByDefault(InvokeWithoutArgs([=]() {
+ syncer::DataTypeController::TypeVector controllers;
+ for (syncer::ModelTypeSet::Iterator it = registered_types.First();
+ it.Good(); it.Inc()) {
+ controllers.push_back(
+ std::make_unique<syncer::FakeDataTypeController>(it.Get()));
+ }
+ return controllers;
+ }));
+
sync_service_ =
std::make_unique<ProfileSyncService>(std::move(init_params));
- sync_service_->RegisterDataTypeController(
- std::make_unique<syncer::FakeDataTypeController>(syncer::BOOKMARKS));
- sync_service_->AddObserver(&observer_);
}
- void SetError(DataTypeManager::ConfigureResult* result) {
- syncer::DataTypeStatusTable::TypeErrorMap errors;
- errors[syncer::BOOKMARKS] =
- syncer::SyncError(FROM_HERE, syncer::SyncError::UNRECOVERABLE_ERROR,
- "Error", syncer::BOOKMARKS);
- result->data_type_status_table.UpdateFailedDataTypes(errors);
- }
-
- protected:
- virtual void SimulateTestUserSignin() {
+ void SimulateTestUserSignin() {
identity::MakePrimaryAccountAvailable(
profile_sync_service_bundle_.signin_manager(),
profile_sync_service_bundle_.auth_service(),
profile_sync_service_bundle_.identity_manager(), kEmail);
}
- DataTypeManagerMock* SetUpDataTypeManager() {
- auto data_type_manager = std::make_unique<DataTypeManagerMock>();
+ void SimulateTestUserSigninWithoutRefreshToken() {
+ // Set the primary account *without* providing an OAuth token.
+ identity::SetPrimaryAccount(profile_sync_service_bundle_.signin_manager(),
+ profile_sync_service_bundle_.identity_manager(),
+ kEmail);
+ }
+
+ void UpdateCredentials() {
+ identity::SetRefreshTokenForPrimaryAccount(
+ profile_sync_service_bundle_.auth_service(),
+ profile_sync_service_bundle_.identity_manager());
+ }
+
+ DataTypeManagerMock* SetUpDataTypeManagerMock() {
+ auto data_type_manager = std::make_unique<NiceMock<DataTypeManagerMock>>();
DataTypeManagerMock* data_type_manager_raw = data_type_manager.get();
- EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
- .WillOnce(Return(ByMove(std::move(data_type_manager))));
+ ON_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillByDefault(Return(ByMove(std::move(data_type_manager))));
return data_type_manager_raw;
}
- FakeSyncEngine* SetUpSyncEngine() {
+ FakeSyncEngine* SetUpFakeSyncEngine() {
auto sync_engine = std::make_unique<FakeSyncEngine>();
FakeSyncEngine* sync_engine_raw = sync_engine.get();
- EXPECT_CALL(*component_factory_, CreateSyncEngine(_, _, _, _))
- .WillOnce(Return(ByMove(std::move(sync_engine))));
+ ON_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillByDefault(Return(ByMove(std::move(sync_engine))));
return sync_engine_raw;
}
+ MockSyncEngine* SetUpMockSyncEngine() {
+ auto sync_engine = std::make_unique<NiceMock<MockSyncEngine>>();
+ MockSyncEngine* sync_engine_raw = sync_engine.get();
+ ON_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillByDefault(Return(ByMove(std::move(sync_engine))));
+ return sync_engine_raw;
+ }
+
+ ProfileSyncService* sync_service() { return sync_service_.get(); }
+
PrefService* pref_service() {
return profile_sync_service_bundle_.pref_service();
}
- base::test::ScopedTaskEnvironment scoped_task_environment_;
- ProfileSyncServiceBundle profile_sync_service_bundle_;
- std::unique_ptr<ProfileSyncService> sync_service_;
- SyncServiceObserverMock observer_;
- syncer::DataTypeStatusTable data_type_status_table_;
- syncer::SyncApiComponentFactoryMock* component_factory_ = nullptr;
-};
-
-class ProfileSyncServiceStartupCrosTest : public ProfileSyncServiceStartupTest {
- public:
- ProfileSyncServiceStartupCrosTest() {
- CreateSyncService(ProfileSyncService::AUTO_START);
- // Set the primary account *without* providing an OAuth token.
- // TODO(https://crbug.com/814787): Change this flow to go through a
- // mainstream Identity Service API once that API exists. Note that this
- // might require supplying a valid refresh token here as opposed to an
- // empty string.
- profile_sync_service_bundle_.identity_manager()
- ->SetPrimaryAccountSynchronously(kGaiaId, kEmail,
- /*refresh_token=*/std::string());
- EXPECT_TRUE(
- profile_sync_service_bundle_.signin_manager()->IsAuthenticated());
+ syncer::SyncApiComponentFactoryMock* component_factory() {
+ return profile_sync_service_bundle_.component_factory();
}
- void SimulateTestUserSignin() override {
- // We already populated the primary account above, all that's left to do
- // is provide a refresh token.
- profile_sync_service_bundle_.auth_service()->UpdateCredentials(
- profile_sync_service_bundle_.identity_manager()
- ->GetPrimaryAccountInfo()
- .account_id,
- "oauth2_login_token");
+ void FastForwardUntilNoTasksRemain() {
+ scoped_task_environment_.FastForwardUntilNoTasksRemain();
}
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ ProfileSyncServiceBundle profile_sync_service_bundle_;
+ std::unique_ptr<ProfileSyncService> sync_service_;
};
+// ChromeOS does not support sign-in after startup (in particular,
+// IdentityManager::Observer::OnPrimaryAccountSet never gets called).
+#if !defined(OS_CHROMEOS)
TEST_F(ProfileSyncServiceStartupTest, StartFirstTime) {
// We've never completed startup.
pref_service()->ClearPref(syncer::prefs::kSyncFirstSetupComplete);
+
CreateSyncService(ProfileSyncService::MANUAL_START);
- SetUpSyncEngine();
- DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
+ SetUpFakeSyncEngine();
+ DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
EXPECT_CALL(*data_type_manager, Configure(_, _)).Times(0);
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::STOPPED));
// Should not actually start, rather just clean things up and wait
// to be enabled.
- EXPECT_CALL(observer_, OnStateChanged(_)).Times(AnyNumber());
- sync_service_->Initialize();
+ sync_service()->Initialize();
+ EXPECT_FALSE(sync_service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NOT_SIGNED_IN,
+ sync_service()->GetDisableReasons());
+ EXPECT_EQ(syncer::SyncService::State::DISABLED, sync_service()->GetState());
// Preferences should be back to defaults.
EXPECT_EQ(0, pref_service()->GetInt64(syncer::prefs::kSyncLastSyncedTime));
EXPECT_FALSE(
pref_service()->GetBoolean(syncer::prefs::kSyncFirstSetupComplete));
- Mock::VerifyAndClearExpectations(data_type_manager);
-
- // Then start things up.
- EXPECT_CALL(*data_type_manager, Configure(_, _)).Times(1);
- EXPECT_CALL(*data_type_manager, state())
- .WillOnce(Return(DataTypeManager::CONFIGURED))
- .WillOnce(Return(DataTypeManager::CONFIGURED));
- EXPECT_CALL(*data_type_manager, Stop()).Times(1);
- EXPECT_CALL(observer_, OnStateChanged(_)).Times(AnyNumber());
-
- auto sync_blocker = sync_service_->GetSetupInProgressHandle();
// Confirmation isn't needed before sign in occurs.
- EXPECT_FALSE(sync_service_->IsSyncConfirmationNeeded());
-
- // Simulate successful signin as test_user.
+ EXPECT_FALSE(sync_service()->IsSyncConfirmationNeeded());
+
+ // This tells the ProfileSyncService that setup is now in progress, which
+ // causes it to try starting up the engine. We're not signed in yet though, so
+ // that won't work.
+ auto sync_blocker = sync_service()->GetSetupInProgressHandle();
+ EXPECT_FALSE(sync_service()->IsSyncActive());
+ EXPECT_FALSE(sync_service()->IsEngineInitialized());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NOT_SIGNED_IN,
+ sync_service()->GetDisableReasons());
+ EXPECT_EQ(syncer::SyncService::State::DISABLED, sync_service()->GetState());
+
+ // Confirmation isn't needed before sign in occurs, or when setup is already
+ // in progress.
+ EXPECT_FALSE(sync_service()->IsSyncConfirmationNeeded());
+
+ // Simulate successful signin. This will cause ProfileSyncService to start,
+ // since all conditions are now fulfilled.
SimulateTestUserSignin();
- // Simulate the UI telling sync it has finished setting up.
+ // Now we're signed in, so the engine can start. There's already a setup in
+ // progress, so we don't go into the WAITING_FOR_START_REQUEST state. Engine
+ // initialization is immediate in this test, so we also bypass the
+ // INITIALIZING state.
+ EXPECT_TRUE(sync_service()->IsEngineInitialized());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
+ sync_service()->GetDisableReasons());
+ EXPECT_FALSE(sync_service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::PENDING_DESIRED_CONFIGURATION,
+ sync_service()->GetState());
+
+ // Setup is already in progress, so confirmation still isn't needed.
+ EXPECT_FALSE(sync_service()->IsSyncConfirmationNeeded());
+
+ // Simulate the UI telling sync it has finished setting up. Note that this is
+ // a two-step process: Releasing the SetupInProgressHandle, and marking first
+ // setup complete.
sync_blocker.reset();
- sync_service_->SetFirstSetupComplete();
- EXPECT_TRUE(sync_service_->IsSyncActive());
+ // Now setup isn't in progress anymore, but Sync is still waiting to be told
+ // that the initial setup was completed.
+ ASSERT_FALSE(sync_service()->IsSetupInProgress());
+ EXPECT_TRUE(sync_service()->IsSyncConfirmationNeeded());
+ EXPECT_FALSE(sync_service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::PENDING_DESIRED_CONFIGURATION,
+ sync_service()->GetState());
+
+ // Marking first setup complete will let ProfileSyncService configure the
+ // DataTypeManager.
+ EXPECT_CALL(*data_type_manager, Configure(_, _));
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::CONFIGURED));
+ sync_service()->SetFirstSetupComplete();
+
+ // This should have fully enabled sync.
+ EXPECT_TRUE(sync_service()->IsSyncActive());
+ EXPECT_FALSE(sync_service()->IsSyncConfirmationNeeded());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, sync_service()->GetState());
+
+ EXPECT_CALL(*data_type_manager, Stop(syncer::BROWSER_SHUTDOWN));
}
+#endif // OS_CHROMEOS
+
+TEST_F(ProfileSyncServiceStartupTest, StartNoCredentials) {
+ // We're already signed in, but don't have a refresh token.
+ SimulateTestUserSigninWithoutRefreshToken();
-// TODO(pavely): Reenable test once android is switched to oauth2.
-TEST_F(ProfileSyncServiceStartupTest, DISABLED_StartNoCredentials) {
- // We've never completed startup.
- pref_service()->ClearPref(syncer::prefs::kSyncFirstSetupComplete);
CreateSyncService(ProfileSyncService::MANUAL_START);
- // Should not actually start, rather just clean things up and wait
- // to be enabled.
- EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
- .Times(0);
- EXPECT_CALL(observer_, OnStateChanged(_)).Times(AnyNumber());
- sync_service_->Initialize();
+ sync_service()->SetFirstSetupComplete();
+ SetUpFakeSyncEngine();
+ DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
+ EXPECT_CALL(*data_type_manager, Configure(_, _));
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::CONFIGURED));
+
+ sync_service()->Initialize();
+
+ // ProfileSyncService should now be active, but of course not have an access
+ // token.
+ EXPECT_TRUE(sync_service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, sync_service()->GetState());
+ EXPECT_TRUE(sync_service()->GetAccessTokenForTest().empty());
+ // Note that ProfileSyncService is not in an auth error state - no auth was
+ // attempted, so no error.
+}
- // Preferences should be back to defaults.
- EXPECT_EQ(0, pref_service()->GetInt64(syncer::prefs::kSyncLastSyncedTime));
- EXPECT_FALSE(
- pref_service()->GetBoolean(syncer::prefs::kSyncFirstSetupComplete));
+TEST_F(ProfileSyncServiceStartupTest, StartInvalidCredentials) {
+ SimulateTestUserSignin();
- // Then start things up.
- auto sync_blocker = sync_service_->GetSetupInProgressHandle();
+ CreateSyncService(ProfileSyncService::MANUAL_START);
- // Simulate successful signin as test_user.
- SimulateTestUserSignin();
+ sync_service()->SetFirstSetupComplete();
- sync_blocker.reset();
- // ProfileSyncService should try to start by requesting access token.
- // This request should fail as login token was not issued.
- EXPECT_FALSE(sync_service_->IsSyncActive());
- EXPECT_EQ(GoogleServiceAuthError::USER_NOT_SIGNED_UP,
- sync_service_->GetAuthError().state());
+ // Tell the engine to stall while downloading control types (simulating an
+ // auth error).
+ FakeSyncEngine* fake_engine = SetUpFakeSyncEngine();
+ fake_engine->set_fail_initial_download(true);
+ // Note: Since engine initialization will fail, the DataTypeManager should not
+ // get created at all here.
+
+ sync_service()->Initialize();
+
+ EXPECT_FALSE(sync_service()->IsSyncActive());
+ // Engine initialization failures puts the service into an unrecoverable error
+ // state. It'll take either a browser restart or a full sign-out+sign-in to
+ // get out of this.
+ EXPECT_TRUE(sync_service()->HasUnrecoverableError());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_UNRECOVERABLE_ERROR,
+ sync_service()->GetDisableReasons());
+ EXPECT_EQ(syncer::SyncService::State::DISABLED, sync_service()->GetState());
}
-// TODO(pavely): Reenable test once android is switched to oauth2.
-TEST_F(ProfileSyncServiceStartupTest, DISABLED_StartInvalidCredentials) {
- CreateSyncService(ProfileSyncService::MANUAL_START);
- SimulateTestUserSignin();
- FakeSyncEngine* mock_sbh = SetUpSyncEngine();
+TEST_F(ProfileSyncServiceStartupTest, StartCrosNoCredentials) {
+ pref_service()->ClearPref(syncer::prefs::kSyncFirstSetupComplete);
- // Tell the backend to stall while downloading control types (simulating an
- // auth error).
- mock_sbh->set_fail_initial_download(true);
+ // On ChromeOS, the user is always immediately signed in, but a refresh token
+ // isn't necessarily available yet.
+ SimulateTestUserSigninWithoutRefreshToken();
- DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
- EXPECT_CALL(*data_type_manager, Configure(_, _)).Times(0);
+ CreateSyncService(ProfileSyncService::AUTO_START);
- EXPECT_CALL(observer_, OnStateChanged(_)).Times(AnyNumber());
- sync_service_->Initialize();
- EXPECT_FALSE(sync_service_->IsSyncActive());
- Mock::VerifyAndClearExpectations(data_type_manager);
+ SetUpFakeSyncEngine();
+ DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
- // Update the credentials, unstalling the backend.
+ // Calling Initialize should cause the service to immediately create and
+ // initialize the engine, and configure the DataTypeManager.
EXPECT_CALL(*data_type_manager, Configure(_, _));
- EXPECT_CALL(*data_type_manager, state())
- .WillRepeatedly(Return(DataTypeManager::CONFIGURED));
- EXPECT_CALL(*data_type_manager, Stop()).Times(1);
- EXPECT_CALL(observer_, OnStateChanged(_)).Times(AnyNumber());
- auto sync_blocker = sync_service_->GetSetupInProgressHandle();
+ sync_service()->Initialize();
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::CONFIGURED));
+
+ // Sync should be considered active, even though there is no refresh token.
+ EXPECT_TRUE(sync_service()->IsSyncActive());
+ // Since we're in AUTO_START mode, FirstSetupComplete gets set automatically.
+ EXPECT_TRUE(sync_service()->IsFirstSetupComplete());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, sync_service()->GetState());
+}
- // Simulate successful signin.
- SimulateTestUserSignin();
+TEST_F(ProfileSyncServiceStartupTest, StartCrosFirstTime) {
+ // On ChromeOS, the user is always immediately signed in, but a refresh token
+ // isn't necessarily available yet.
+ SimulateTestUserSigninWithoutRefreshToken();
- sync_blocker.reset();
+ CreateSyncService(ProfileSyncService::AUTO_START);
- // Verify we successfully finish startup and configuration.
- EXPECT_TRUE(sync_service_->IsSyncActive());
-}
-
-TEST_F(ProfileSyncServiceStartupCrosTest, StartCrosNoCredentials) {
- EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
- .Times(0);
- EXPECT_CALL(*component_factory_, CreateSyncEngine(_, _, _, _)).Times(0);
+ SetUpFakeSyncEngine();
+ DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
pref_service()->ClearPref(syncer::prefs::kSyncFirstSetupComplete);
- EXPECT_CALL(observer_, OnStateChanged(_)).Times(AnyNumber());
+ EXPECT_CALL(*data_type_manager, Configure(_, _));
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::CONFIGURED));
+
+ // The primary account is already populated, all that's left to do is provide
+ // a refresh token.
+ UpdateCredentials();
+ sync_service()->Initialize();
+ EXPECT_TRUE(sync_service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, sync_service()->GetState());
+ EXPECT_CALL(*data_type_manager, Stop(syncer::BROWSER_SHUTDOWN));
+}
- sync_service_->Initialize();
- // Sync should not start because there are no tokens yet.
- EXPECT_FALSE(sync_service_->IsSyncActive());
- sync_service_->SetFirstSetupComplete();
+TEST_F(ProfileSyncServiceStartupTest, StartNormal) {
+ // We have previously completed the initial Sync setup, and the user is
+ // already signed in.
+ pref_service()->SetBoolean(syncer::prefs::kSyncFirstSetupComplete, true);
+ SimulateTestUserSignin();
- // Sync should not start because there are still no tokens.
- EXPECT_FALSE(sync_service_->IsSyncActive());
-}
+ CreateSyncService(ProfileSyncService::MANUAL_START);
+ SetUpFakeSyncEngine();
+ DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
+ ON_CALL(*data_type_manager, IsNigoriEnabled()).WillByDefault(Return(true));
-TEST_F(ProfileSyncServiceStartupCrosTest, StartFirstTime) {
- SetUpSyncEngine();
- DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
- pref_service()->ClearPref(syncer::prefs::kSyncFirstSetupComplete);
+ // Since all conditions for starting Sync are already fulfilled, calling
+ // Initialize should immediately create and initialize the engine and
+ // configure the DataTypeManager. In this test, all of these operations are
+ // synchronous.
EXPECT_CALL(*data_type_manager, Configure(_, _));
- EXPECT_CALL(*data_type_manager, state())
- .WillRepeatedly(Return(DataTypeManager::CONFIGURED));
- EXPECT_CALL(*data_type_manager, Stop());
- EXPECT_CALL(observer_, OnStateChanged(_)).Times(AnyNumber());
+ sync_service()->Initialize();
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::CONFIGURED));
+
+ EXPECT_TRUE(sync_service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, sync_service()->GetState());
+
+ EXPECT_CALL(*data_type_manager, Stop(syncer::BROWSER_SHUTDOWN));
+}
+TEST_F(ProfileSyncServiceStartupTest, StopSync) {
+ CreateSyncService(ProfileSyncService::MANUAL_START);
SimulateTestUserSignin();
- sync_service_->Initialize();
- EXPECT_TRUE(sync_service_->IsSyncActive());
+ sync_service()->SetFirstSetupComplete();
+ SetUpFakeSyncEngine();
+ DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::CONFIGURED));
+ ON_CALL(*data_type_manager, IsNigoriEnabled()).WillByDefault(Return(true));
+
+ sync_service()->Initialize();
+
+ EXPECT_CALL(*data_type_manager, Stop(syncer::STOP_SYNC));
+ sync_service()->RequestStop(syncer::SyncService::KEEP_DATA);
}
-TEST_F(ProfileSyncServiceStartupTest, StartNormal) {
+TEST_F(ProfileSyncServiceStartupTest, DisableSync) {
CreateSyncService(ProfileSyncService::MANUAL_START);
SimulateTestUserSignin();
- sync_service_->SetFirstSetupComplete();
- SetUpSyncEngine();
- DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
- EXPECT_CALL(*data_type_manager, Configure(_, _));
- EXPECT_CALL(*data_type_manager, state())
- .WillRepeatedly(Return(DataTypeManager::CONFIGURED));
- EXPECT_CALL(*data_type_manager, Stop()).Times(1);
- EXPECT_CALL(observer_, OnStateChanged(_)).Times(AnyNumber());
+ sync_service()->SetFirstSetupComplete();
+ SetUpFakeSyncEngine();
+ DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::CONFIGURED));
ON_CALL(*data_type_manager, IsNigoriEnabled()).WillByDefault(Return(true));
- sync_service_->Initialize();
+ sync_service()->Initialize();
+
+ EXPECT_CALL(*data_type_manager, Stop(syncer::DISABLE_SYNC));
+ sync_service()->RequestStop(syncer::SyncService::CLEAR_DATA);
}
// Test that we can recover from a case where a bug in the code resulted in
@@ -340,17 +420,15 @@ TEST_F(ProfileSyncServiceStartupTest, StartRecoverDatatypePrefs) {
CreateSyncService(ProfileSyncService::MANUAL_START);
SimulateTestUserSignin();
- sync_service_->SetFirstSetupComplete();
- SetUpSyncEngine();
- DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
+ sync_service()->SetFirstSetupComplete();
+ SetUpFakeSyncEngine();
+ DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
EXPECT_CALL(*data_type_manager, Configure(_, _));
- EXPECT_CALL(*data_type_manager, state())
- .WillRepeatedly(Return(DataTypeManager::CONFIGURED));
- EXPECT_CALL(*data_type_manager, Stop()).Times(1);
- EXPECT_CALL(observer_, OnStateChanged(_)).Times(AnyNumber());
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::CONFIGURED));
ON_CALL(*data_type_manager, IsNigoriEnabled()).WillByDefault(Return(true));
- sync_service_->Initialize();
+ sync_service()->Initialize();
EXPECT_TRUE(
pref_service()->GetBoolean(syncer::prefs::kSyncKeepEverythingSynced));
@@ -365,16 +443,14 @@ TEST_F(ProfileSyncServiceStartupTest, StartDontRecoverDatatypePrefs) {
CreateSyncService(ProfileSyncService::MANUAL_START);
SimulateTestUserSignin();
- sync_service_->SetFirstSetupComplete();
- SetUpSyncEngine();
- DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
+ sync_service()->SetFirstSetupComplete();
+ SetUpFakeSyncEngine();
+ DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
EXPECT_CALL(*data_type_manager, Configure(_, _));
- EXPECT_CALL(*data_type_manager, state())
- .WillRepeatedly(Return(DataTypeManager::CONFIGURED));
- EXPECT_CALL(*data_type_manager, Stop()).Times(1);
- EXPECT_CALL(observer_, OnStateChanged(_)).Times(AnyNumber());
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::CONFIGURED));
ON_CALL(*data_type_manager, IsNigoriEnabled()).WillByDefault(Return(true));
- sync_service_->Initialize();
+ sync_service()->Initialize();
EXPECT_FALSE(
pref_service()->GetBoolean(syncer::prefs::kSyncKeepEverythingSynced));
@@ -382,91 +458,240 @@ TEST_F(ProfileSyncServiceStartupTest, StartDontRecoverDatatypePrefs) {
TEST_F(ProfileSyncServiceStartupTest, ManagedStartup) {
// Service should not be started by Initialize() since it's managed.
- pref_service()->SetString(prefs::kGoogleServicesAccountId, kEmail);
+ SimulateTestUserSignin();
CreateSyncService(ProfileSyncService::MANUAL_START);
// Disable sync through policy.
+ ASSERT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
+ sync_service()->GetDisableReasons());
pref_service()->SetBoolean(syncer::prefs::kSyncManaged, true);
- EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
+ ASSERT_EQ(syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY,
+ sync_service()->GetDisableReasons());
+ EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _)).Times(0);
+ EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
.Times(0);
- EXPECT_CALL(observer_, OnStateChanged(_)).Times(AnyNumber());
- sync_service_->Initialize();
+ sync_service()->Initialize();
}
TEST_F(ProfileSyncServiceStartupTest, SwitchManaged) {
CreateSyncService(ProfileSyncService::MANUAL_START);
SimulateTestUserSignin();
- sync_service_->SetFirstSetupComplete();
- SetUpSyncEngine();
- DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
+ sync_service()->SetFirstSetupComplete();
+ SetUpFakeSyncEngine();
+ DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
EXPECT_CALL(*data_type_manager, Configure(_, _));
- EXPECT_CALL(*data_type_manager, state())
- .WillRepeatedly(Return(DataTypeManager::CONFIGURED));
- EXPECT_CALL(observer_, OnStateChanged(_)).Times(AnyNumber());
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::CONFIGURED));
ON_CALL(*data_type_manager, IsNigoriEnabled()).WillByDefault(Return(true));
- sync_service_->Initialize();
- EXPECT_TRUE(sync_service_->IsEngineInitialized());
- EXPECT_TRUE(sync_service_->IsSyncActive());
+ sync_service()->Initialize();
+ EXPECT_TRUE(sync_service()->IsEngineInitialized());
+ EXPECT_TRUE(sync_service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
+ sync_service()->GetDisableReasons());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, sync_service()->GetState());
// The service should stop when switching to managed mode.
Mock::VerifyAndClearExpectations(data_type_manager);
EXPECT_CALL(*data_type_manager, state())
.WillOnce(Return(DataTypeManager::CONFIGURED));
- EXPECT_CALL(*data_type_manager, Stop()).Times(1);
+ EXPECT_CALL(*data_type_manager, Stop(syncer::DISABLE_SYNC));
pref_service()->SetBoolean(syncer::prefs::kSyncManaged, true);
- EXPECT_FALSE(sync_service_->IsEngineInitialized());
+ ASSERT_EQ(syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY,
+ sync_service()->GetDisableReasons());
+ EXPECT_FALSE(sync_service()->IsEngineInitialized());
+ EXPECT_EQ(syncer::SyncService::State::DISABLED, sync_service()->GetState());
// Note that PSS no longer references |data_type_manager| after stopping.
// When switching back to unmanaged, the state should change but sync should
- // not start automatically because IsFirstSetupComplete() will be false.
+ // not start automatically because IsFirstSetupComplete() will be false and
+ // no setup is in progress.
// A new DataTypeManager should not be created.
Mock::VerifyAndClearExpectations(data_type_manager);
- EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
+ EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
.Times(0);
pref_service()->ClearPref(syncer::prefs::kSyncManaged);
- EXPECT_FALSE(sync_service_->IsEngineInitialized());
- EXPECT_FALSE(sync_service_->IsSyncActive());
+ ASSERT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
+ sync_service()->GetDisableReasons());
+ EXPECT_FALSE(sync_service()->IsEngineInitialized());
+ EXPECT_FALSE(sync_service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::WAITING_FOR_START_REQUEST,
+ sync_service()->GetState());
}
TEST_F(ProfileSyncServiceStartupTest, StartFailure) {
CreateSyncService(ProfileSyncService::MANUAL_START);
SimulateTestUserSignin();
- sync_service_->SetFirstSetupComplete();
- SetUpSyncEngine();
- DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
+ sync_service()->SetFirstSetupComplete();
+ SetUpFakeSyncEngine();
+ DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
DataTypeManager::ConfigureStatus status = DataTypeManager::ABORTED;
DataTypeManager::ConfigureResult result(status, syncer::ModelTypeSet());
EXPECT_CALL(*data_type_manager, Configure(_, _))
.WillRepeatedly(
- DoAll(InvokeOnConfigureStart(sync_service_.get()),
- InvokeOnConfigureDone(
- sync_service_.get(),
- base::Bind(&ProfileSyncServiceStartupTest::SetError,
- base::Unretained(this)),
- result)));
+ DoAll(InvokeOnConfigureStart(sync_service()),
+ InvokeOnConfigureDone(sync_service(),
+ base::BindRepeating(&SetError), result)));
EXPECT_CALL(*data_type_manager, state())
.WillOnce(Return(DataTypeManager::STOPPED));
- EXPECT_CALL(observer_, OnStateChanged(_)).Times(AnyNumber());
ON_CALL(*data_type_manager, IsNigoriEnabled()).WillByDefault(Return(true));
- sync_service_->Initialize();
- EXPECT_TRUE(sync_service_->HasUnrecoverableError());
+ sync_service()->Initialize();
+ EXPECT_TRUE(sync_service()->HasUnrecoverableError());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_UNRECOVERABLE_ERROR,
+ sync_service()->GetDisableReasons());
}
TEST_F(ProfileSyncServiceStartupTest, StartDownloadFailed) {
CreateSyncService(ProfileSyncService::MANUAL_START);
SimulateTestUserSignin();
- FakeSyncEngine* mock_sbh = SetUpSyncEngine();
- mock_sbh->set_fail_initial_download(true);
+ FakeSyncEngine* fake_engine = SetUpFakeSyncEngine();
+ fake_engine->set_fail_initial_download(true);
pref_service()->ClearPref(syncer::prefs::kSyncFirstSetupComplete);
- EXPECT_CALL(observer_, OnStateChanged(_)).Times(AnyNumber());
- sync_service_->Initialize();
+ sync_service()->Initialize();
- auto sync_blocker = sync_service_->GetSetupInProgressHandle();
+ auto sync_blocker = sync_service()->GetSetupInProgressHandle();
sync_blocker.reset();
- EXPECT_FALSE(sync_service_->IsSyncActive());
+ EXPECT_FALSE(sync_service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_UNRECOVERABLE_ERROR,
+ sync_service()->GetDisableReasons());
+ EXPECT_EQ(syncer::SyncService::State::DISABLED, sync_service()->GetState());
+}
+
+// ChromeOS does not support sign-in after startup (in particular,
+// IdentityManager::Observer::OnPrimaryAccountSet never gets called).
+#if !defined(OS_CHROMEOS)
+TEST_F(ProfileSyncServiceStartupTest, FullStartupSequenceFirstTime) {
+ // We've never completed startup.
+ pref_service()->ClearPref(syncer::prefs::kSyncFirstSetupComplete);
+
+ MockSyncEngine* sync_engine = SetUpMockSyncEngine();
+ EXPECT_CALL(*sync_engine, Initialize(_)).Times(0);
+
+ DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
+ EXPECT_CALL(*data_type_manager, Configure(_, _)).Times(0);
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::STOPPED));
+
+ // Note: Deferred startup is only enabled if SESSIONS is among the preferred
+ // data types.
+ CreateSyncService(ProfileSyncService::MANUAL_START,
+ syncer::ModelTypeSet(syncer::SESSIONS));
+ sync_service()->Initialize();
+
+ // There is no signed-in user, but nothing else prevents Sync from starting.
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NOT_SIGNED_IN,
+ sync_service()->GetDisableReasons());
+ EXPECT_EQ(syncer::SyncService::State::DISABLED, sync_service()->GetState());
+
+ // Sign in. Now Sync is ready to start, just waiting for a prod.
+ SimulateTestUserSignin();
+ EXPECT_EQ(syncer::SyncService::State::WAITING_FOR_START_REQUEST,
+ sync_service()->GetState());
+
+ // Once we give the service a prod by initiating Sync setup, it'll start and
+ // initialize the engine. Since this is the initial Sync start, this will not
+ // be deferred.
+ EXPECT_CALL(*sync_engine, Initialize(_));
+ auto setup_in_progress_handle = sync_service()->GetSetupInProgressHandle();
+ EXPECT_EQ(syncer::SyncService::State::INITIALIZING,
+ sync_service()->GetState());
+
+ // Once the engine calls back and says it's initialized, we're just waiting
+ // for the user to finish the initial configuration (choosing data types etc.)
+ // before actually syncing data.
+ sync_service()->OnEngineInitialized(
+ syncer::ModelTypeSet(), syncer::WeakHandle<syncer::JsBackend>(),
+ syncer::WeakHandle<syncer::DataTypeDebugInfoListener>(), "test-guid",
+ /*success=*/true);
+ ASSERT_TRUE(sync_service()->IsEngineInitialized());
+ EXPECT_EQ(syncer::SyncService::State::PENDING_DESIRED_CONFIGURATION,
+ sync_service()->GetState());
+
+ // Once the user finishes the initial setup, the service can actually start
+ // configuring the data types. Just marking the initial setup as complete
+ // isn't enough though, because setup is still considered in progress (we
+ // haven't released the setup-in-progress handle).
+ sync_service()->SetFirstSetupComplete();
+ EXPECT_EQ(syncer::SyncService::State::PENDING_DESIRED_CONFIGURATION,
+ sync_service()->GetState());
+
+ // Releasing the setup in progress handle lets the service actually configure
+ // the DataTypeManager.
+ EXPECT_CALL(*data_type_manager, Configure(_, _))
+ .WillOnce(InvokeWithoutArgs(sync_service(),
+ &ProfileSyncService::OnConfigureStart));
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::CONFIGURING));
+ setup_in_progress_handle.reset();
+ // While DataTypeManager configuration is ongoing, the overall state is still
+ // CONFIGURING.
+ EXPECT_EQ(syncer::SyncService::State::CONFIGURING,
+ sync_service()->GetState());
+
+ // Finally, once the DataTypeManager says it's done with configuration, Sync
+ // is actually fully up and running.
+ DataTypeManager::ConfigureResult configure_result(
+ DataTypeManager::OK, syncer::ModelTypeSet(syncer::SESSIONS));
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::CONFIGURED));
+ sync_service()->OnConfigureDone(configure_result);
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, sync_service()->GetState());
+}
+#endif // OS_CHROMEOS
+
+TEST_F(ProfileSyncServiceStartupTest, FullStartupSequenceNthTime) {
+ // The user is already signed in and has completed Sync setup before.
+ SimulateTestUserSignin();
+ pref_service()->SetBoolean(syncer::prefs::kSyncFirstSetupComplete, true);
+
+ MockSyncEngine* sync_engine = SetUpMockSyncEngine();
+ EXPECT_CALL(*sync_engine, Initialize(_)).Times(0);
+
+ DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
+ EXPECT_CALL(*data_type_manager, Configure(_, _)).Times(0);
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::STOPPED));
+
+ // Note: Deferred startup is only enabled if SESSIONS is among the preferred
+ // data types.
+ CreateSyncService(ProfileSyncService::MANUAL_START,
+ syncer::ModelTypeSet(syncer::SESSIONS));
+ sync_service()->Initialize();
+
+ // Nothing is preventing Sync from starting, but it should be deferred so as
+ // to now slow down browser startup.
+ EXPECT_EQ(syncer::SyncService::State::START_DEFERRED,
+ sync_service()->GetState());
+
+ // Wait for the deferred startup timer to expire. The Sync service will start
+ // and initialize the engine.
+ EXPECT_CALL(*sync_engine, Initialize(_));
+ FastForwardUntilNoTasksRemain();
+ EXPECT_EQ(syncer::SyncService::State::INITIALIZING,
+ sync_service()->GetState());
+
+ // Once the engine calls back and says it's initialized, the DataTypeManager
+ // will get configured, since initial setup is already done.
+ EXPECT_CALL(*data_type_manager, Configure(_, _));
+ sync_service()->OnEngineInitialized(
+ syncer::ModelTypeSet(), syncer::WeakHandle<syncer::JsBackend>(),
+ syncer::WeakHandle<syncer::DataTypeDebugInfoListener>(), "test-guid",
+ /*success=*/true);
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::CONFIGURING));
+ EXPECT_EQ(syncer::SyncService::State::CONFIGURING,
+ sync_service()->GetState());
+
+ // Finally, once the DataTypeManager says it's done with configuration, Sync
+ // is actually fully up and running.
+ DataTypeManager::ConfigureResult configure_result(
+ DataTypeManager::OK, syncer::ModelTypeSet(syncer::SESSIONS));
+ ON_CALL(*data_type_manager, state())
+ .WillByDefault(Return(DataTypeManager::CONFIGURED));
+ sync_service()->OnConfigureDone(configure_result);
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, sync_service()->GetState());
}
} // namespace browser_sync
diff --git a/chromium/components/browser_sync/profile_sync_service_unittest.cc b/chromium/components/browser_sync/profile_sync_service_unittest.cc
index 0b9ca2fbcbf..4ac188d5474 100644
--- a/chromium/components/browser_sync/profile_sync_service_unittest.cc
+++ b/chromium/components/browser_sync/profile_sync_service_unittest.cc
@@ -9,16 +9,15 @@
#include "base/callback.h"
#include "base/command_line.h"
#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
-#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "components/browser_sync/browser_sync_switches.h"
#include "components/browser_sync/profile_sync_test_util.h"
#include "components/signin/core/browser/account_tracker_service.h"
#include "components/signin/core/browser/fake_signin_manager.h"
#include "components/sync/base/pref_names.h"
-#include "components/sync/device_info/local_device_info_provider.h"
#include "components/sync/driver/fake_data_type_controller.h"
#include "components/sync/driver/signin_manager_wrapper.h"
#include "components/sync/driver/sync_api_component_factory_mock.h"
@@ -27,7 +26,6 @@
#include "components/sync/driver/sync_token_status.h"
#include "components/sync/driver/sync_util.h"
#include "components/sync/engine/fake_sync_engine.h"
-#include "components/sync/model/model_type_store_test_util.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/version_info/version_info_values.h"
#include "google_apis/gaia/oauth2_token_service_delegate.h"
@@ -35,10 +33,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-using syncer::DataTypeController;
-using syncer::FakeSyncEngine;
-using syncer::ModelTypeSet;
-using syncer::SyncMergeResult;
+using testing::_;
using testing::ByMove;
using testing::Return;
@@ -48,15 +43,17 @@ namespace {
class FakeDataTypeManager : public syncer::DataTypeManager {
public:
- using ConfigureCalled = base::Callback<void(syncer::ConfigureReason)>;
+ using ConfigureCalled =
+ base::RepeatingCallback<void(syncer::ConfigureReason)>;
explicit FakeDataTypeManager(const ConfigureCalled& configure_called)
- : configure_called_(configure_called) {}
+ : configure_called_(configure_called), state_(STOPPED) {}
~FakeDataTypeManager() override {}
void Configure(syncer::ModelTypeSet desired_types,
syncer::ConfigureReason reason) override {
+ state_ = CONFIGURED;
DCHECK(!configure_called_.is_null());
configure_called_.Run(reason);
}
@@ -65,23 +62,22 @@ class FakeDataTypeManager : public syncer::DataTypeManager {
void ResetDataTypeErrors() override {}
void PurgeForMigration(syncer::ModelTypeSet undesired_types,
syncer::ConfigureReason reason) override {}
- void Stop() override {}
- ModelTypeSet GetActiveDataTypes() const override { return ModelTypeSet(); }
+ void Stop(syncer::ShutdownReason reason) override {}
+ syncer::ModelTypeSet GetActiveDataTypes() const override {
+ return syncer::ModelTypeSet();
+ }
bool IsNigoriEnabled() const override { return true; }
- State state() const override { return syncer::DataTypeManager::CONFIGURED; }
+ State state() const override { return state_; }
private:
ConfigureCalled configure_called_;
+ State state_;
};
-ACTION_P(ReturnNewDataTypeManager, configure_called) {
+ACTION_P(ReturnNewFakeDataTypeManager, configure_called) {
return std::make_unique<FakeDataTypeManager>(configure_called);
}
-using testing::Return;
-using testing::StrictMock;
-using testing::_;
-
class TestSyncServiceObserver : public syncer::SyncServiceObserver {
public:
TestSyncServiceObserver()
@@ -103,31 +99,44 @@ class TestSyncServiceObserver : public syncer::SyncServiceObserver {
// A variant of the FakeSyncEngine that won't automatically call back when asked
// to initialized. Allows us to test things that could happen while backend init
// is in progress.
-class SyncEngineNoReturn : public FakeSyncEngine {
+class FakeSyncEngineNoReturn : public syncer::FakeSyncEngine {
void Initialize(InitParams params) override {}
};
-class FakeSyncEngineCollectDeleteDirParam : public FakeSyncEngine {
+// FakeSyncEngine that stores the SyncCredentials passed into Initialize(), and
+// optionally also whether InvalidateCredentials was called.
+class FakeSyncEngineCollectCredentials : public syncer::FakeSyncEngine {
public:
- explicit FakeSyncEngineCollectDeleteDirParam(
- std::vector<bool>* delete_dir_param)
- : delete_dir_param_(delete_dir_param) {}
+ explicit FakeSyncEngineCollectCredentials(
+ syncer::SyncCredentials* init_credentials,
+ const base::RepeatingClosure& invalidate_credentials_callback)
+ : init_credentials_(init_credentials),
+ invalidate_credentials_callback_(invalidate_credentials_callback) {}
void Initialize(InitParams params) override {
- delete_dir_param_->push_back(params.delete_sync_data_folder);
- FakeSyncEngine::Initialize(std::move(params));
+ *init_credentials_ = params.credentials;
+ syncer::FakeSyncEngine::Initialize(std::move(params));
+ }
+
+ void InvalidateCredentials() override {
+ if (invalidate_credentials_callback_) {
+ invalidate_credentials_callback_.Run();
+ }
+ syncer::FakeSyncEngine::InvalidateCredentials();
}
private:
- std::vector<bool>* delete_dir_param_;
+ syncer::SyncCredentials* init_credentials_;
+ base::RepeatingClosure invalidate_credentials_callback_;
};
// FakeSyncEngine that calls an external callback when ClearServerData is
// called.
-class SyncEngineCaptureClearServerData : public FakeSyncEngine {
+class FakeSyncEngineCaptureClearServerData : public syncer::FakeSyncEngine {
public:
- using ClearServerDataCalled = base::Callback<void(const base::Closure&)>;
- explicit SyncEngineCaptureClearServerData(
+ using ClearServerDataCalled =
+ base::RepeatingCallback<void(const base::Closure&)>;
+ explicit FakeSyncEngineCaptureClearServerData(
const ClearServerDataCalled& clear_server_data_called)
: clear_server_data_called_(clear_server_data_called) {}
@@ -139,24 +148,8 @@ class SyncEngineCaptureClearServerData : public FakeSyncEngine {
ClearServerDataCalled clear_server_data_called_;
};
-// FakeSyncEngine that calls an external callback when InvalidateCredentials is
-// called.
-class SyncEngineCaptureInvalidateCredentials : public FakeSyncEngine {
- public:
- explicit SyncEngineCaptureInvalidateCredentials(
- const base::RepeatingClosure& invalidate_credentials_called)
- : invalidate_credentials_called_(invalidate_credentials_called) {}
-
- void InvalidateCredentials() override {
- invalidate_credentials_called_.Run();
- }
-
- private:
- base::RepeatingClosure invalidate_credentials_called_;
-};
-
ACTION(ReturnNewFakeSyncEngine) {
- return std::make_unique<FakeSyncEngine>();
+ return std::make_unique<syncer::FakeSyncEngine>();
}
void OnClearServerDataCalled(base::Closure* captured_callback,
@@ -165,13 +158,13 @@ void OnClearServerDataCalled(base::Closure* captured_callback,
}
// A test harness that uses a real ProfileSyncService and in most cases a
-// MockSyncEngine.
+// FakeSyncEngine.
//
// This is useful if we want to test the ProfileSyncService and don't care about
// testing the SyncEngine.
class ProfileSyncServiceTest : public ::testing::Test {
protected:
- ProfileSyncServiceTest() : component_factory_(nullptr) {}
+ ProfileSyncServiceTest() {}
~ProfileSyncServiceTest() override {}
void SetUp() override {
@@ -181,35 +174,43 @@ class ProfileSyncServiceTest : public ::testing::Test {
void TearDown() override {
// Kill the service before the profile.
- if (service_)
- service_->Shutdown();
-
- service_.reset();
+ ShutdownAndDeleteService();
}
- void IssueTestTokens() {
+ void SignIn() {
identity::MakePrimaryAccountAvailable(signin_manager(), auth_service(),
identity_manager(),
"test_user@gmail.com");
}
void CreateService(ProfileSyncService::StartBehavior behavior) {
- component_factory_ = profile_sync_service_bundle_.component_factory();
+ DCHECK(!service_);
+
ProfileSyncServiceBundle::SyncClientBuilder builder(
&profile_sync_service_bundle_);
ProfileSyncService::InitParams init_params =
profile_sync_service_bundle_.CreateBasicInitParams(behavior,
builder.Build());
- init_params.model_type_store_factory =
- syncer::ModelTypeStoreTestUtil::FactoryForInMemoryStoreForTest();
-
service_ = std::make_unique<ProfileSyncService>(std::move(init_params));
- service_->RegisterDataTypeController(
- std::make_unique<syncer::FakeDataTypeController>(syncer::BOOKMARKS));
+
+ ON_CALL(*component_factory(), CreateCommonDataTypeControllers(_, _))
+ .WillByDefault(testing::InvokeWithoutArgs([=]() {
+ syncer::DataTypeController::TypeVector controllers;
+ controllers.push_back(
+ std::make_unique<syncer::FakeDataTypeController>(
+ syncer::BOOKMARKS));
+ return controllers;
+ }));
+ ON_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillByDefault(ReturnNewFakeSyncEngine());
+ ON_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillByDefault(
+ ReturnNewFakeDataTypeManager(GetDefaultConfigureCalledCallback()));
}
void CreateServiceWithLocalSyncBackend() {
- component_factory_ = profile_sync_service_bundle_.component_factory();
+ DCHECK(!service_);
+
ProfileSyncServiceBundle::SyncClientBuilder builder(
&profile_sync_service_bundle_);
ProfileSyncService::InitParams init_params =
@@ -217,13 +218,24 @@ class ProfileSyncServiceTest : public ::testing::Test {
ProfileSyncService::AUTO_START, builder.Build());
prefs()->SetBoolean(syncer::prefs::kEnableLocalSyncBackend, true);
- init_params.oauth2_token_service = nullptr;
init_params.gaia_cookie_manager_service = nullptr;
init_params.signin_wrapper.reset();
service_ = std::make_unique<ProfileSyncService>(std::move(init_params));
- service_->RegisterDataTypeController(
- std::make_unique<syncer::FakeDataTypeController>(syncer::BOOKMARKS));
+
+ ON_CALL(*component_factory(), CreateCommonDataTypeControllers(_, _))
+ .WillByDefault(testing::InvokeWithoutArgs([=]() {
+ syncer::DataTypeController::TypeVector controllers;
+ controllers.push_back(
+ std::make_unique<syncer::FakeDataTypeController>(
+ syncer::BOOKMARKS));
+ return controllers;
+ }));
+ ON_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillByDefault(ReturnNewFakeSyncEngine());
+ ON_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillByDefault(
+ ReturnNewFakeDataTypeManager(GetDefaultConfigureCalledCallback()));
}
void ShutdownAndDeleteService() {
@@ -265,52 +277,12 @@ class ProfileSyncServiceTest : public ::testing::Test {
base::Unretained(this));
}
- void OnConfigureCalledRecordReason(syncer::ConfigureReason* reason_dest,
- syncer::ConfigureReason reason) {
- DCHECK(reason_dest);
- *reason_dest = reason;
- }
-
FakeDataTypeManager::ConfigureCalled GetRecordingConfigureCalledCallback(
- syncer::ConfigureReason* reason) {
- return base::Bind(&ProfileSyncServiceTest::OnConfigureCalledRecordReason,
- base::Unretained(this), reason);
- }
-
- void ExpectDataTypeManagerCreation(
- int times,
- const FakeDataTypeManager::ConfigureCalled& callback) {
- EXPECT_CALL(*component_factory_, CreateDataTypeManager(_, _, _, _, _, _))
- .Times(times)
- .WillRepeatedly(ReturnNewDataTypeManager(callback));
- }
-
- void ExpectSyncEngineCreation(int times) {
- EXPECT_CALL(*component_factory_, CreateSyncEngine(_, _, _, _))
- .Times(times)
- .WillRepeatedly(ReturnNewFakeSyncEngine());
- }
-
- void ExpectSyncEngineCreationCaptureClearServerData(
- base::Closure* captured_callback) {
- EXPECT_CALL(*component_factory_, CreateSyncEngine(_, _, _, _))
- .WillOnce(
- Return(ByMove(std::make_unique<SyncEngineCaptureClearServerData>(
- base::BindRepeating(&OnClearServerDataCalled,
- base::Unretained(captured_callback))))));
- }
-
- void ExpectSyncEngineCreationCaptureInvalidateCredentials(
- const base::RepeatingClosure& callback) {
- EXPECT_CALL(*component_factory_, CreateSyncEngine(_, _, _, _))
- .WillOnce(Return(
- ByMove(std::make_unique<SyncEngineCaptureInvalidateCredentials>(
- callback))));
- }
-
- void PrepareDelayedInitSyncEngine() {
- EXPECT_CALL(*component_factory_, CreateSyncEngine(_, _, _, _))
- .WillOnce(Return(ByMove(std::make_unique<SyncEngineNoReturn>())));
+ syncer::ConfigureReason* reason_dest) {
+ return base::BindLambdaForTesting(
+ [reason_dest](syncer::ConfigureReason reason) {
+ *reason_dest = reason;
+ });
}
AccountTrackerService* account_tracker() {
@@ -342,25 +314,13 @@ class ProfileSyncServiceTest : public ::testing::Test {
}
syncer::SyncApiComponentFactoryMock* component_factory() {
- return component_factory_;
- }
-
- protected:
- void PumpLoop() {
- base::RunLoop run_loop;
- base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- run_loop.QuitClosure());
- run_loop.Run();
+ return profile_sync_service_bundle_.component_factory();
}
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
ProfileSyncServiceBundle profile_sync_service_bundle_;
std::unique_ptr<ProfileSyncService> service_;
-
- // The current component factory used by sync. May be null if the server
- // hasn't been created yet.
- syncer::SyncApiComponentFactoryMock* component_factory_;
};
// Verify that the server URLs are sane.
@@ -372,38 +332,40 @@ TEST_F(ProfileSyncServiceTest, InitialState) {
url == syncer::internal::kSyncDevServerUrl);
}
-// Verify a successful initialization.
TEST_F(ProfileSyncServiceTest, SuccessfulInitialization) {
- prefs()->SetManagedPref(syncer::prefs::kSyncManaged,
- std::make_unique<base::Value>(false));
- IssueTestTokens();
+ SignIn();
CreateService(ProfileSyncService::AUTO_START);
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
+ EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillOnce(ReturnNewFakeSyncEngine());
+ EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillOnce(
+ ReturnNewFakeDataTypeManager(GetDefaultConfigureCalledCallback()));
InitializeForNthSync();
- EXPECT_FALSE(service()->IsManaged());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
+ service()->GetDisableReasons());
EXPECT_TRUE(service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
}
-// Verify a successful initialization.
TEST_F(ProfileSyncServiceTest, SuccessfulLocalBackendInitialization) {
- prefs()->SetManagedPref(syncer::prefs::kSyncManaged,
- std::make_unique<base::Value>(false));
CreateServiceWithLocalSyncBackend();
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
+ EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillOnce(ReturnNewFakeSyncEngine());
+ EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillOnce(
+ ReturnNewFakeDataTypeManager(GetDefaultConfigureCalledCallback()));
InitializeForNthSync();
- EXPECT_FALSE(service()->IsManaged());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
+ service()->GetDisableReasons());
EXPECT_TRUE(service()->IsSyncActive());
EXPECT_FALSE(service()->IsSyncConfirmationNeeded());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
}
// Verify that an initialization where first setup is not complete does not
// start up the backend.
TEST_F(ProfileSyncServiceTest, NeedsConfirmation) {
- prefs()->SetManagedPref(syncer::prefs::kSyncManaged,
- std::make_unique<base::Value>(false));
- IssueTestTokens();
+ SignIn();
CreateService(ProfileSyncService::MANUAL_START);
syncer::SyncPrefs sync_prefs(prefs());
@@ -411,8 +373,23 @@ TEST_F(ProfileSyncServiceTest, NeedsConfirmation) {
sync_prefs.SetLastSyncedTime(now);
sync_prefs.SetKeepEverythingSynced(true);
service()->Initialize();
+
EXPECT_FALSE(service()->IsSyncActive());
EXPECT_TRUE(service()->IsSyncConfirmationNeeded());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
+ service()->GetDisableReasons());
+ // Note: At this point the engine *can* start, but nothing has kicked it off
+ // (usually that happens via getting and then releasing a
+ // SyncSetupInProgressHandle), so the state is still WAITING_FOR_START_REQUEST
+ // and not PENDING_DESIRED_CONFIGURATION.
+ EXPECT_EQ(syncer::SyncService::State::WAITING_FOR_START_REQUEST,
+ service()->GetState());
+
+ // Once we kick off initialization by getting and releasing a setup handle,
+ // the state goes to PENDING_DESIRED_CONFIGURATION.
+ service()->GetSetupInProgressHandle();
+ EXPECT_EQ(syncer::SyncService::State::PENDING_DESIRED_CONFIGURATION,
+ service()->GetState());
// The last sync time shouldn't be cleared.
// TODO(zea): figure out a way to check that the directory itself wasn't
@@ -441,41 +418,48 @@ TEST_F(ProfileSyncServiceTest, SetupInProgress) {
TEST_F(ProfileSyncServiceTest, DisabledByPolicyBeforeInit) {
prefs()->SetManagedPref(syncer::prefs::kSyncManaged,
std::make_unique<base::Value>(true));
- IssueTestTokens();
+ SignIn();
CreateService(ProfileSyncService::AUTO_START);
InitializeForNthSync();
- EXPECT_TRUE(service()->IsManaged());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY,
+ service()->GetDisableReasons());
EXPECT_FALSE(service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::DISABLED, service()->GetState());
}
// Verify that disable by enterprise policy works even after the backend has
// been initialized.
TEST_F(ProfileSyncServiceTest, DisabledByPolicyAfterInit) {
- IssueTestTokens();
+ SignIn();
CreateService(ProfileSyncService::AUTO_START);
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
InitializeForNthSync();
- ASSERT_FALSE(service()->IsManaged());
+ ASSERT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
+ service()->GetDisableReasons());
ASSERT_TRUE(service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
prefs()->SetManagedPref(syncer::prefs::kSyncManaged,
std::make_unique<base::Value>(true));
- EXPECT_TRUE(service()->IsManaged());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY,
+ service()->GetDisableReasons());
EXPECT_FALSE(service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::DISABLED, service()->GetState());
}
-// Exercies the ProfileSyncService's code paths related to getting shut down
+// Exercises the ProfileSyncService's code paths related to getting shut down
// before the backend initialize call returns.
TEST_F(ProfileSyncServiceTest, AbortedByShutdown) {
CreateService(ProfileSyncService::AUTO_START);
- PrepareDelayedInitSyncEngine();
+ ON_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillByDefault(
+ Return(ByMove(std::make_unique<FakeSyncEngineNoReturn>())));
- IssueTestTokens();
+ SignIn();
InitializeForNthSync();
ASSERT_FALSE(service()->IsSyncActive());
+ ASSERT_EQ(syncer::SyncService::State::INITIALIZING, service()->GetState());
ShutdownAndDeleteService();
}
@@ -483,47 +467,55 @@ TEST_F(ProfileSyncServiceTest, AbortedByShutdown) {
// Test RequestStop() before we've initialized the backend.
TEST_F(ProfileSyncServiceTest, EarlyRequestStop) {
CreateService(ProfileSyncService::AUTO_START);
- IssueTestTokens();
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
+ SignIn();
service()->RequestStop(ProfileSyncService::KEEP_DATA);
- EXPECT_FALSE(service()->IsSyncRequested());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_USER_CHOICE,
+ service()->GetDisableReasons());
+ EXPECT_EQ(syncer::SyncService::State::DISABLED, service()->GetState());
// Because sync is not requested, this should fail.
InitializeForNthSync();
- EXPECT_FALSE(service()->IsSyncRequested());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_USER_CHOICE,
+ service()->GetDisableReasons());
EXPECT_FALSE(service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::DISABLED, service()->GetState());
// Request start. This should be enough to allow init to happen.
service()->RequestStart();
- EXPECT_TRUE(service()->IsSyncRequested());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
+ service()->GetDisableReasons());
EXPECT_TRUE(service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
}
// Test RequestStop() after we've initialized the backend.
TEST_F(ProfileSyncServiceTest, DisableAndEnableSyncTemporarily) {
CreateService(ProfileSyncService::AUTO_START);
- IssueTestTokens();
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
+ SignIn();
InitializeForNthSync();
ASSERT_TRUE(service()->IsSyncActive());
ASSERT_FALSE(prefs()->GetBoolean(syncer::prefs::kSyncSuppressStart));
+ ASSERT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
+ service()->GetDisableReasons());
+ ASSERT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
testing::Mock::VerifyAndClearExpectations(component_factory());
service()->RequestStop(ProfileSyncService::KEEP_DATA);
EXPECT_FALSE(service()->IsSyncActive());
EXPECT_TRUE(prefs()->GetBoolean(syncer::prefs::kSyncSuppressStart));
-
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_USER_CHOICE,
+ service()->GetDisableReasons());
+ EXPECT_EQ(syncer::SyncService::State::DISABLED, service()->GetState());
service()->RequestStart();
EXPECT_TRUE(service()->IsSyncActive());
EXPECT_FALSE(prefs()->GetBoolean(syncer::prefs::kSyncSuppressStart));
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
+ service()->GetDisableReasons());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
}
// Certain ProfileSyncService tests don't apply to Chrome OS, for example
@@ -531,27 +523,30 @@ TEST_F(ProfileSyncServiceTest, DisableAndEnableSyncTemporarily) {
#if !defined(OS_CHROMEOS)
TEST_F(ProfileSyncServiceTest, EnableSyncAndSignOut) {
CreateService(ProfileSyncService::AUTO_START);
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
- IssueTestTokens();
+ SignIn();
InitializeForNthSync();
EXPECT_TRUE(service()->IsSyncActive());
EXPECT_FALSE(prefs()->GetBoolean(syncer::prefs::kSyncSuppressStart));
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
+ service()->GetDisableReasons());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
signin_manager()->SignOut(signin_metrics::SIGNOUT_TEST,
signin_metrics::SignoutDelete::IGNORE_METRIC);
// Wait for PSS to be notified that the primary account has gone away.
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NOT_SIGNED_IN,
+ service()->GetDisableReasons());
+ EXPECT_EQ(syncer::SyncService::State::DISABLED, service()->GetState());
}
#endif // !defined(OS_CHROMEOS)
TEST_F(ProfileSyncServiceTest, GetSyncTokenStatus) {
CreateService(ProfileSyncService::AUTO_START);
- IssueTestTokens();
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
+
+ SignIn();
InitializeForNthSync();
// Initial status.
@@ -565,8 +560,7 @@ TEST_F(ProfileSyncServiceTest, GetSyncTokenStatus) {
service()->OnConnectionStatusChange(syncer::CONNECTION_AUTH_ERROR);
// The token request will take the form of a posted task. Run it.
- base::RunLoop loop;
- loop.RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
token_status = service()->GetSyncTokenStatus();
EXPECT_EQ(syncer::CONNECTION_AUTH_ERROR, token_status.connection_status);
@@ -584,16 +578,32 @@ TEST_F(ProfileSyncServiceTest, GetSyncTokenStatus) {
}
TEST_F(ProfileSyncServiceTest, RevokeAccessTokenFromTokenService) {
+ syncer::SyncCredentials init_credentials;
+
CreateService(ProfileSyncService::AUTO_START);
- IssueTestTokens();
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
+ SignIn();
+ EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillOnce(
+ Return(ByMove(std::make_unique<FakeSyncEngineCollectCredentials>(
+ &init_credentials, base::RepeatingClosure()))));
InitializeForNthSync();
ASSERT_TRUE(service()->IsSyncActive());
+ ASSERT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
- std::string primary_account_id =
+ const std::string primary_account_id =
signin_manager()->GetAuthenticatedAccountId();
- auth_service()->LoadCredentials(primary_account_id);
+
+ // Make sure the expected credentials (correct account_id, empty access token)
+ // were passed to the SyncEngine.
+ ASSERT_EQ(primary_account_id, init_credentials.account_id);
+ ASSERT_TRUE(init_credentials.sync_token.empty());
+
+ // At this point, the real SyncEngine would try to connect to the server, fail
+ // (because it has no access token), and eventually call
+ // OnConnectionStatusChange(CONNECTION_AUTH_ERROR). Since our fake SyncEngine
+ // doesn't do any of this, call that explicitly here.
+ service()->OnConnectionStatusChange(syncer::CONNECTION_AUTH_ERROR);
+
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(service()->GetAccessTokenForTest().empty());
@@ -613,23 +623,43 @@ TEST_F(ProfileSyncServiceTest, RevokeAccessTokenFromTokenService) {
// Checks that CREDENTIALS_REJECTED_BY_CLIENT resets the access token and stops
// Sync. Regression test for https://crbug.com/824791.
TEST_F(ProfileSyncServiceTest, CredentialsRejectedByClient) {
+ syncer::SyncCredentials init_credentials;
bool invalidate_credentials_called = false;
base::RepeatingClosure invalidate_credentials_callback =
base::BindRepeating([](bool* called) { *called = true; },
base::Unretained(&invalidate_credentials_called));
+
CreateService(ProfileSyncService::AUTO_START);
- IssueTestTokens();
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreationCaptureInvalidateCredentials(
- invalidate_credentials_callback);
+ SignIn();
+ EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillOnce(
+ Return(ByMove(std::make_unique<FakeSyncEngineCollectCredentials>(
+ &init_credentials, invalidate_credentials_callback))));
InitializeForNthSync();
ASSERT_TRUE(service()->IsSyncActive());
+ ASSERT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
- std::string primary_account_id =
+ TestSyncServiceObserver observer;
+ service()->AddObserver(&observer);
+
+ const std::string primary_account_id =
signin_manager()->GetAuthenticatedAccountId();
- auth_service()->LoadCredentials(primary_account_id);
+
+ // Make sure the expected credentials (correct account_id, empty access token)
+ // were passed to the SyncEngine.
+ ASSERT_EQ(primary_account_id, init_credentials.account_id);
+ ASSERT_TRUE(init_credentials.sync_token.empty());
+
+ // At this point, the real SyncEngine would try to connect to the server, fail
+ // (because it has no access token), and eventually call
+ // OnConnectionStatusChange(CONNECTION_AUTH_ERROR). Since our fake SyncEngine
+ // doesn't do any of this, call that explicitly here.
+ service()->OnConnectionStatusChange(syncer::CONNECTION_AUTH_ERROR);
+
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(service()->GetAccessTokenForTest().empty());
+ ASSERT_EQ(GoogleServiceAuthError::AuthErrorNone(), service()->GetAuthError());
+ ASSERT_EQ(GoogleServiceAuthError::AuthErrorNone(), observer.auth_error());
// Simulate the credentials getting locally rejected by the client by setting
// the refresh token to a special invalid value.
@@ -643,21 +673,44 @@ TEST_F(ProfileSyncServiceTest, CredentialsRejectedByClient) {
auth_service()->GetAuthError(primary_account_id));
EXPECT_TRUE(service()->GetAccessTokenForTest().empty());
EXPECT_TRUE(invalidate_credentials_called);
+
+ // The observer should have been notified of the auth error state.
+ EXPECT_EQ(rejected_by_client, observer.auth_error());
+ // The overall state should remain ACTIVE.
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
+
+ service()->RemoveObserver(&observer);
}
// CrOS does not support signout.
#if !defined(OS_CHROMEOS)
TEST_F(ProfileSyncServiceTest, SignOutRevokeAccessToken) {
+ syncer::SyncCredentials init_credentials;
+
CreateService(ProfileSyncService::AUTO_START);
- IssueTestTokens();
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
+ SignIn();
+ EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillOnce(
+ Return(ByMove(std::make_unique<FakeSyncEngineCollectCredentials>(
+ &init_credentials, base::RepeatingClosure()))));
InitializeForNthSync();
- EXPECT_TRUE(service()->IsSyncActive());
+ ASSERT_TRUE(service()->IsSyncActive());
+ ASSERT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
- std::string primary_account_id =
+ const std::string primary_account_id =
signin_manager()->GetAuthenticatedAccountId();
- auth_service()->LoadCredentials(primary_account_id);
+
+ // Make sure the expected credentials (correct account_id, empty access token)
+ // were passed to the SyncEngine.
+ ASSERT_EQ(primary_account_id, init_credentials.account_id);
+ ASSERT_TRUE(init_credentials.sync_token.empty());
+
+ // At this point, the real SyncEngine would try to connect to the server, fail
+ // (because it has no access token), and eventually call
+ // OnConnectionStatusChange(CONNECTION_AUTH_ERROR). Since our fake SyncEngine
+ // doesn't do any of this, call that explicitly here.
+ service()->OnConnectionStatusChange(syncer::CONNECTION_AUTH_ERROR);
+
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(service()->GetAccessTokenForTest().empty());
@@ -669,19 +722,18 @@ TEST_F(ProfileSyncServiceTest, SignOutRevokeAccessToken) {
// Verify that LastSyncedTime and local DeviceInfo is cleared on sign out.
TEST_F(ProfileSyncServiceTest, ClearDataOnSignOut) {
- IssueTestTokens();
+ SignIn();
CreateService(ProfileSyncService::AUTO_START);
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
InitializeForNthSync();
ASSERT_TRUE(service()->IsSyncActive());
+ ASSERT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
ASSERT_LT(base::Time::Now() - service()->GetLastSyncedTime(),
base::TimeDelta::FromMinutes(1));
ASSERT_TRUE(service()->GetLocalDeviceInfoProvider()->GetLocalDeviceInfo());
// Sign out.
service()->RequestStop(ProfileSyncService::CLEAR_DATA);
- PumpLoop();
+ base::RunLoop().RunUntilIdle();
EXPECT_TRUE(service()->GetLastSyncedTime().is_null());
EXPECT_FALSE(service()->GetLocalDeviceInfoProvider()->GetLocalDeviceInfo());
@@ -693,21 +745,36 @@ TEST_F(ProfileSyncServiceTest, CredentialErrorReturned) {
// automatic replies to access token requests.
auth_service()->set_auto_post_fetch_response_on_message_loop(false);
+ syncer::SyncCredentials init_credentials;
+
CreateService(ProfileSyncService::AUTO_START);
- IssueTestTokens();
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
+ SignIn();
+ EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillOnce(
+ Return(ByMove(std::make_unique<FakeSyncEngineCollectCredentials>(
+ &init_credentials, base::RepeatingClosure()))));
InitializeForNthSync();
ASSERT_TRUE(service()->IsSyncActive());
+ ASSERT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
+
+ const std::string primary_account_id =
+ signin_manager()->GetAuthenticatedAccountId();
+
+ // Make sure the expected credentials (correct account_id, empty access token)
+ // were passed to the SyncEngine.
+ ASSERT_EQ(primary_account_id, init_credentials.account_id);
+ ASSERT_TRUE(init_credentials.sync_token.empty());
TestSyncServiceObserver observer;
service()->AddObserver(&observer);
- std::string primary_account_id =
- signin_manager()->GetAuthenticatedAccountId();
- auth_service()->LoadCredentials(primary_account_id);
- // Wait for ProfileSyncService to be notified of the loaded credentials and
- // send an access token request.
+ // At this point, the real SyncEngine would try to connect to the server, fail
+ // (because it has no access token), and eventually call
+ // OnConnectionStatusChange(CONNECTION_AUTH_ERROR). Since our fake SyncEngine
+ // doesn't do any of this, call that explicitly here.
+ service()->OnConnectionStatusChange(syncer::CONNECTION_AUTH_ERROR);
+
+ // Wait for ProfileSyncService to send an access token request.
base::RunLoop().RunUntilIdle();
auth_service()->IssueAllTokensForAccount(primary_account_id, "access token",
base::Time::Max());
@@ -727,6 +794,8 @@ TEST_F(ProfileSyncServiceTest, CredentialErrorReturned) {
service()->GetAuthError().state());
EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
observer.auth_error().state());
+ // The overall state should remain ACTIVE.
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
service()->RemoveObserver(&observer);
}
@@ -738,21 +807,36 @@ TEST_F(ProfileSyncServiceTest, CredentialErrorClearsOnNewToken) {
// automatic replies to access token requests.
auth_service()->set_auto_post_fetch_response_on_message_loop(false);
+ syncer::SyncCredentials init_credentials;
+
CreateService(ProfileSyncService::AUTO_START);
- IssueTestTokens();
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
+ SignIn();
+ EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillOnce(
+ Return(ByMove(std::make_unique<FakeSyncEngineCollectCredentials>(
+ &init_credentials, base::RepeatingClosure()))));
InitializeForNthSync();
ASSERT_TRUE(service()->IsSyncActive());
+ ASSERT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
+
+ const std::string primary_account_id =
+ signin_manager()->GetAuthenticatedAccountId();
+
+ // Make sure the expected credentials (correct account_id, empty access token)
+ // were passed to the SyncEngine.
+ ASSERT_EQ(primary_account_id, init_credentials.account_id);
+ ASSERT_TRUE(init_credentials.sync_token.empty());
TestSyncServiceObserver observer;
service()->AddObserver(&observer);
- std::string primary_account_id =
- signin_manager()->GetAuthenticatedAccountId();
- auth_service()->LoadCredentials(primary_account_id);
- // Wait for ProfileSyncService to be notified of the loaded credentials and
- // send an access token request.
+ // At this point, the real SyncEngine would try to connect to the server, fail
+ // (because it has no access token), and eventually call
+ // OnConnectionStatusChange(CONNECTION_AUTH_ERROR). Since our fake SyncEngine
+ // doesn't do any of this, call that explicitly here.
+ service()->OnConnectionStatusChange(syncer::CONNECTION_AUTH_ERROR);
+
+ // Wait for ProfileSyncService to send an access token request.
base::RunLoop().RunUntilIdle();
auth_service()->IssueAllTokensForAccount(primary_account_id, "access token",
base::Time::Max());
@@ -771,6 +855,8 @@ TEST_F(ProfileSyncServiceTest, CredentialErrorClearsOnNewToken) {
// Check that the invalid token is returned from sync.
ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
service()->GetAuthError().state());
+ // The overall state should remain ACTIVE.
+ ASSERT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
// Now emulate Chrome receiving a new, valid LST.
auth_service()->UpdateCredentials(primary_account_id, "totally valid token");
@@ -782,6 +868,7 @@ TEST_F(ProfileSyncServiceTest, CredentialErrorClearsOnNewToken) {
// Check that sync auth error state cleared.
EXPECT_EQ(GoogleServiceAuthError::NONE, service()->GetAuthError().state());
EXPECT_EQ(GoogleServiceAuthError::NONE, observer.auth_error().state());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
service()->RemoveObserver(&observer);
}
@@ -800,13 +887,12 @@ TEST_F(ProfileSyncServiceTest, NoDisableSyncFlag) {
// Test Sync will stop after receive memory pressure
TEST_F(ProfileSyncServiceTest, MemoryPressureRecording) {
CreateService(ProfileSyncService::AUTO_START);
- IssueTestTokens();
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
+ SignIn();
InitializeForNthSync();
ASSERT_TRUE(service()->IsSyncActive());
ASSERT_FALSE(prefs()->GetBoolean(syncer::prefs::kSyncSuppressStart));
+ ASSERT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
testing::Mock::VerifyAndClearExpectations(component_factory());
@@ -844,7 +930,7 @@ TEST_F(ProfileSyncServiceTest, OnLocalSetPassphraseEncryption) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
switches::kSyncClearDataOnPassphraseEncryption);
- IssueTestTokens();
+ SignIn();
CreateService(ProfileSyncService::AUTO_START);
base::Closure captured_callback;
@@ -853,11 +939,17 @@ TEST_F(ProfileSyncServiceTest, OnLocalSetPassphraseEncryption) {
// Initialize sync, ensure that both DataTypeManager and SyncEngine are
// initialized and DTM::Configure is called with
// CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE.
- ExpectSyncEngineCreationCaptureClearServerData(&captured_callback);
- ExpectDataTypeManagerCreation(
- 1, GetRecordingConfigureCalledCallback(&configure_reason));
+ EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillOnce(
+ Return(ByMove(std::make_unique<FakeSyncEngineCaptureClearServerData>(
+ base::BindRepeating(&OnClearServerDataCalled,
+ base::Unretained(&captured_callback))))));
+ EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillOnce(ReturnNewFakeDataTypeManager(
+ GetRecordingConfigureCalledCallback(&configure_reason)));
InitializeForNthSync();
ASSERT_TRUE(service()->IsSyncActive());
+ ASSERT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
testing::Mock::VerifyAndClearExpectations(component_factory());
ASSERT_EQ(syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE, configure_reason);
syncer::DataTypeManager::ConfigureResult result;
@@ -882,9 +974,9 @@ TEST_F(ProfileSyncServiceTest, OnLocalSetPassphraseEncryption) {
// Once SBH::ClearServerData finishes successfully ensure that sync is
// restarted.
configure_reason = syncer::CONFIGURE_REASON_UNKNOWN;
- ExpectSyncEngineCreation(1);
- ExpectDataTypeManagerCreation(
- 1, GetRecordingConfigureCalledCallback(&configure_reason));
+ EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillOnce(ReturnNewFakeDataTypeManager(
+ GetRecordingConfigureCalledCallback(&configure_reason)));
captured_callback.Run();
testing::Mock::VerifyAndClearExpectations(component_factory());
EXPECT_EQ(syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE, configure_reason);
@@ -899,11 +991,11 @@ TEST_F(ProfileSyncServiceTest,
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
switches::kSyncClearDataOnPassphraseEncryption);
- IssueTestTokens();
+ SignIn();
CreateService(ProfileSyncService::AUTO_START);
- ExpectSyncEngineCreation(1);
- ExpectDataTypeManagerCreation(
- 1, GetRecordingConfigureCalledCallback(&configure_reason));
+ EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillOnce(ReturnNewFakeDataTypeManager(
+ GetRecordingConfigureCalledCallback(&configure_reason)));
InitializeForNthSync();
testing::Mock::VerifyAndClearExpectations(component_factory());
ASSERT_EQ(syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE, configure_reason);
@@ -922,9 +1014,14 @@ TEST_F(ProfileSyncServiceTest,
ShutdownAndDeleteService();
CreateService(ProfileSyncService::AUTO_START);
base::Closure captured_callback;
- ExpectSyncEngineCreationCaptureClearServerData(&captured_callback);
- ExpectDataTypeManagerCreation(
- 1, GetRecordingConfigureCalledCallback(&configure_reason));
+ EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillOnce(
+ Return(ByMove(std::make_unique<FakeSyncEngineCaptureClearServerData>(
+ base::BindRepeating(&OnClearServerDataCalled,
+ base::Unretained(&captured_callback))))));
+ EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillOnce(ReturnNewFakeDataTypeManager(
+ GetRecordingConfigureCalledCallback(&configure_reason)));
InitializeForNthSync();
testing::Mock::VerifyAndClearExpectations(component_factory());
EXPECT_EQ(syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE, configure_reason);
@@ -942,9 +1039,9 @@ TEST_F(ProfileSyncServiceTest,
result.was_catch_up_configure = false;
EXPECT_FALSE(captured_callback.is_null());
- ExpectSyncEngineCreation(1);
- ExpectDataTypeManagerCreation(
- 1, GetRecordingConfigureCalledCallback(&configure_reason));
+ EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillOnce(ReturnNewFakeDataTypeManager(
+ GetRecordingConfigureCalledCallback(&configure_reason)));
captured_callback.Run();
testing::Mock::VerifyAndClearExpectations(component_factory());
EXPECT_EQ(syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE, configure_reason);
@@ -959,10 +1056,13 @@ TEST_F(ProfileSyncServiceTest,
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
switches::kSyncClearDataOnPassphraseEncryption);
- IssueTestTokens();
+ SignIn();
CreateService(ProfileSyncService::AUTO_START);
- ExpectSyncEngineCreationCaptureClearServerData(&captured_callback);
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
+ EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillOnce(
+ Return(ByMove(std::make_unique<FakeSyncEngineCaptureClearServerData>(
+ base::BindRepeating(&OnClearServerDataCalled,
+ base::Unretained(&captured_callback))))));
InitializeForNthSync();
testing::Mock::VerifyAndClearExpectations(component_factory());
@@ -976,9 +1076,14 @@ TEST_F(ProfileSyncServiceTest,
// Simulate browser restart. First configuration is a regular one.
ShutdownAndDeleteService();
CreateService(ProfileSyncService::AUTO_START);
- ExpectSyncEngineCreationCaptureClearServerData(&captured_callback);
- ExpectDataTypeManagerCreation(
- 1, GetRecordingConfigureCalledCallback(&configure_reason));
+ EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillOnce(
+ Return(ByMove(std::make_unique<FakeSyncEngineCaptureClearServerData>(
+ base::BindRepeating(&OnClearServerDataCalled,
+ base::Unretained(&captured_callback))))));
+ EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillOnce(ReturnNewFakeDataTypeManager(
+ GetRecordingConfigureCalledCallback(&configure_reason)));
InitializeForNthSync();
testing::Mock::VerifyAndClearExpectations(component_factory());
EXPECT_EQ(syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE, configure_reason);
@@ -998,9 +1103,9 @@ TEST_F(ProfileSyncServiceTest,
result.was_catch_up_configure = false;
EXPECT_FALSE(captured_callback.is_null());
- ExpectSyncEngineCreation(1);
- ExpectDataTypeManagerCreation(
- 1, GetRecordingConfigureCalledCallback(&configure_reason));
+ EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillOnce(ReturnNewFakeDataTypeManager(
+ GetRecordingConfigureCalledCallback(&configure_reason)));
captured_callback.Run();
testing::Mock::VerifyAndClearExpectations(component_factory());
EXPECT_EQ(syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE, configure_reason);
@@ -1009,10 +1114,8 @@ TEST_F(ProfileSyncServiceTest,
// Test that the passphrase prompt due to version change logic gets triggered
// on a datatype type requesting startup, but only happens once.
TEST_F(ProfileSyncServiceTest, PassphrasePromptDueToVersion) {
- IssueTestTokens();
+ SignIn();
CreateService(ProfileSyncService::AUTO_START);
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
InitializeForNthSync();
syncer::SyncPrefs sync_prefs(service()->GetSyncClient()->GetPrefService());
@@ -1040,12 +1143,19 @@ TEST_F(ProfileSyncServiceTest, PassphrasePromptDueToVersion) {
// Test that when ProfileSyncService receives actionable error
// RESET_LOCAL_SYNC_DATA it restarts sync.
TEST_F(ProfileSyncServiceTest, ResetSyncData) {
- IssueTestTokens();
+ SignIn();
CreateService(ProfileSyncService::AUTO_START);
// Backend should get initialized two times: once during initialization and
// once when handling actionable error.
- ExpectDataTypeManagerCreation(2, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(2);
+ EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillOnce(
+ ReturnNewFakeDataTypeManager(GetDefaultConfigureCalledCallback()))
+ .WillOnce(
+ ReturnNewFakeDataTypeManager(GetDefaultConfigureCalledCallback()));
+ EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _, _))
+ .WillOnce(ReturnNewFakeSyncEngine())
+ .WillOnce(ReturnNewFakeSyncEngine());
+
InitializeForNthSync();
syncer::SyncProtocolError client_cmd;
@@ -1056,13 +1166,12 @@ TEST_F(ProfileSyncServiceTest, ResetSyncData) {
// Test that when ProfileSyncService receives actionable error
// DISABLE_SYNC_ON_CLIENT it disables sync and signs out.
TEST_F(ProfileSyncServiceTest, DisableSyncOnClient) {
- IssueTestTokens();
+ SignIn();
CreateService(ProfileSyncService::AUTO_START);
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
InitializeForNthSync();
ASSERT_TRUE(service()->IsSyncActive());
+ ASSERT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
ASSERT_LT(base::Time::Now() - service()->GetLastSyncedTime(),
base::TimeDelta::FromMinutes(1));
ASSERT_TRUE(service()->GetLocalDeviceInfoProvider()->GetLocalDeviceInfo());
@@ -1074,11 +1183,17 @@ TEST_F(ProfileSyncServiceTest, DisableSyncOnClient) {
// CrOS does not support signout.
#if !defined(OS_CHROMEOS)
EXPECT_TRUE(signin_manager()->GetAuthenticatedAccountId().empty());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_USER_CHOICE |
+ syncer::SyncService::DISABLE_REASON_NOT_SIGNED_IN,
+ service()->GetDisableReasons());
#else
EXPECT_FALSE(signin_manager()->GetAuthenticatedAccountId().empty());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_USER_CHOICE,
+ service()->GetDisableReasons());
#endif
EXPECT_FALSE(service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::DISABLED, service()->GetState());
EXPECT_TRUE(service()->GetLastSyncedTime().is_null());
EXPECT_FALSE(service()->GetLocalDeviceInfoProvider()->GetLocalDeviceInfo());
}
@@ -1088,61 +1203,73 @@ TEST_F(ProfileSyncServiceTest, LocalBackendDisabledByPolicy) {
prefs()->SetManagedPref(syncer::prefs::kSyncManaged,
std::make_unique<base::Value>(false));
CreateServiceWithLocalSyncBackend();
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
InitializeForNthSync();
- EXPECT_FALSE(service()->IsManaged());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
+ service()->GetDisableReasons());
EXPECT_TRUE(service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
prefs()->SetManagedPref(syncer::prefs::kSyncManaged,
std::make_unique<base::Value>(true));
- EXPECT_TRUE(service()->IsManaged());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY,
+ service()->GetDisableReasons());
EXPECT_FALSE(service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::DISABLED, service()->GetState());
prefs()->SetManagedPref(syncer::prefs::kSyncManaged,
std::make_unique<base::Value>(false));
- ExpectDataTypeManagerCreation(1, GetDefaultConfigureCalledCallback());
- ExpectSyncEngineCreation(1);
-
service()->RequestStart();
- EXPECT_FALSE(service()->IsManaged());
+ EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
+ service()->GetDisableReasons());
EXPECT_TRUE(service()->IsSyncActive());
+ EXPECT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
}
-// Regression test for crbug/555434. The issue is that check for sessions DTC in
-// OnSessionRestoreComplete was creating map entry with nullptr which later was
-// dereferenced in OnSyncCycleCompleted. The fix is to use find() to check if
-// entry for sessions exists in map.
-TEST_F(ProfileSyncServiceTest, ValidPointersInDTCMap) {
+// Test ConfigureDataTypeManagerReason on First and Nth start.
+TEST_F(ProfileSyncServiceTest, ConfigureDataTypeManagerReason) {
+ const syncer::DataTypeManager::ConfigureResult configure_result(
+ syncer::DataTypeManager::OK, syncer::ModelTypeSet());
+ syncer::ConfigureReason configure_reason = syncer::CONFIGURE_REASON_UNKNOWN;
+
+ SignIn();
+
+ // First sync.
CreateService(ProfileSyncService::AUTO_START);
- service()->OnSessionRestoreComplete();
- service()->OnSyncCycleCompleted(syncer::SyncCycleSnapshot(
- syncer::ModelNeutralState(), syncer::ProgressMarkerMap(), false, 0, 0, 0,
- false, 0, base::Time::Now(), base::Time::Now(),
- std::vector<int>(syncer::MODEL_TYPE_COUNT, 0),
- std::vector<int>(syncer::MODEL_TYPE_COUNT, 0),
- sync_pb::SyncEnums::UNKNOWN_ORIGIN,
- /*short_poll_interval=*/base::TimeDelta::FromMinutes(30),
- /*long_poll_interval=*/base::TimeDelta::FromMinutes(180),
- /*has_remaining_local_changes=*/false));
-}
+ EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillOnce(ReturnNewFakeDataTypeManager(
+ GetRecordingConfigureCalledCallback(&configure_reason)));
+ InitializeForFirstSync();
+ ASSERT_TRUE(service()->IsSyncActive());
+ ASSERT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
+ ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(component_factory()));
+ EXPECT_EQ(syncer::CONFIGURE_REASON_NEW_CLIENT, configure_reason);
+ service()->OnConfigureDone(configure_result);
+
+ // Reconfiguration.
+ service()->ReconfigureDatatypeManager();
+ EXPECT_EQ(syncer::CONFIGURE_REASON_RECONFIGURATION, configure_reason);
+ service()->OnConfigureDone(configure_result);
+ ShutdownAndDeleteService();
-// The OpenTabsUIDelegate should only be accessable when PROXY_TABS is enabled.
-TEST_F(ProfileSyncServiceTest, GetOpenTabsUIDelegate) {
+ // Nth sync.
CreateService(ProfileSyncService::AUTO_START);
+ EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
+ .WillOnce(ReturnNewFakeDataTypeManager(
+ GetRecordingConfigureCalledCallback(&configure_reason)));
InitializeForNthSync();
- EXPECT_EQ(nullptr, service()->GetOpenTabsUIDelegate());
-
- auto controller =
- std::make_unique<syncer::FakeDataTypeController>(syncer::PROXY_TABS);
- // Progress the controller to RUNNING first, which is how the service
- // determines whether a type is enabled.
- controller->StartAssociating(base::DoNothing());
- controller->FinishStart(DataTypeController::OK_FIRST_RUN);
- service()->RegisterDataTypeController(std::move(controller));
- EXPECT_NE(nullptr, service()->GetOpenTabsUIDelegate());
+ ASSERT_TRUE(service()->IsSyncActive());
+ ASSERT_EQ(syncer::SyncService::State::ACTIVE, service()->GetState());
+ ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(component_factory()));
+ EXPECT_EQ(syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE, configure_reason);
+ service()->OnConfigureDone(configure_result);
+
+ // Reconfiguration.
+ service()->ReconfigureDatatypeManager();
+ EXPECT_EQ(syncer::CONFIGURE_REASON_RECONFIGURATION, configure_reason);
+ service()->OnConfigureDone(configure_result);
+ ShutdownAndDeleteService();
}
} // namespace
diff --git a/chromium/components/browser_sync/profile_sync_test_util.cc b/chromium/components/browser_sync/profile_sync_test_util.cc
index b2b6123a000..69d9cf038c3 100644
--- a/chromium/components/browser_sync/profile_sync_test_util.cc
+++ b/chromium/components/browser_sync/profile_sync_test_util.cc
@@ -8,7 +8,9 @@
#include <utility>
#include "base/bind.h"
+#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/history/core/browser/history_model_worker.h"
@@ -21,16 +23,31 @@
#include "components/sync/engine/sequenced_model_worker.h"
#include "components/sync/engine/ui_model_worker.h"
#include "components/sync/model/model_type_store_test_util.h"
+#include "components/sync_sessions/local_session_event_router.h"
#include "net/url_request/url_request_test_util.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_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
namespace browser_sync {
namespace {
+class DummyRouter : public sync_sessions::LocalSessionEventRouter {
+ public:
+ DummyRouter() {}
+ ~DummyRouter() override {}
+ void StartRoutingTo(
+ sync_sessions::LocalSessionEventHandler* handler) override {}
+ void Stop() override {}
+};
+
class BundleSyncClient : public syncer::FakeSyncClient {
public:
BundleSyncClient(syncer::SyncApiComponentFactory* factory,
PrefService* pref_service,
+ syncer::ModelTypeStoreService* model_type_store_service,
sync_sessions::SyncSessionsClient* sync_sessions_client,
autofill::PersonalDataManager* personal_data_manager,
const base::Callback<base::WeakPtr<syncer::SyncableService>(
@@ -39,8 +56,8 @@ class BundleSyncClient : public syncer::FakeSyncClient {
get_sync_service_callback,
const base::Callback<bookmarks::BookmarkModel*(void)>&
get_bookmark_model_callback,
- scoped_refptr<base::SingleThreadTaskRunner> db_thread,
- scoped_refptr<base::SingleThreadTaskRunner> file_thread,
+ scoped_refptr<base::SequencedTaskRunner> db_thread,
+ scoped_refptr<base::SequencedTaskRunner> file_thread,
history::HistoryService* history_service);
~BundleSyncClient() override;
@@ -51,6 +68,7 @@ class BundleSyncClient : public syncer::FakeSyncClient {
base::WeakPtr<syncer::SyncableService> GetSyncableServiceForType(
syncer::ModelType type) override;
syncer::SyncService* GetSyncService() override;
+ syncer::ModelTypeStoreService* GetModelTypeStoreService() override;
scoped_refptr<syncer::ModelSafeWorker> CreateModelWorkerForGroup(
syncer::ModelSafeGroup group) override;
history::HistoryService* GetHistoryService() override;
@@ -58,6 +76,7 @@ class BundleSyncClient : public syncer::FakeSyncClient {
private:
PrefService* const pref_service_;
+ syncer::ModelTypeStoreService* const model_type_store_service_;
sync_sessions::SyncSessionsClient* const sync_sessions_client_;
autofill::PersonalDataManager* const personal_data_manager_;
const base::Callback<base::WeakPtr<syncer::SyncableService>(
@@ -67,14 +86,15 @@ class BundleSyncClient : public syncer::FakeSyncClient {
const base::Callback<bookmarks::BookmarkModel*(void)>
get_bookmark_model_callback_;
// These task runners, if not null, are used in CreateModelWorkerForGroup.
- const scoped_refptr<base::SingleThreadTaskRunner> db_thread_;
- const scoped_refptr<base::SingleThreadTaskRunner> file_thread_;
+ const scoped_refptr<base::SequencedTaskRunner> db_thread_;
+ const scoped_refptr<base::SequencedTaskRunner> file_thread_;
history::HistoryService* history_service_;
};
BundleSyncClient::BundleSyncClient(
syncer::SyncApiComponentFactory* factory,
PrefService* pref_service,
+ syncer::ModelTypeStoreService* model_type_store_service,
sync_sessions::SyncSessionsClient* sync_sessions_client,
autofill::PersonalDataManager* personal_data_manager,
const base::Callback<base::WeakPtr<syncer::SyncableService>(
@@ -82,11 +102,12 @@ BundleSyncClient::BundleSyncClient(
const base::Callback<syncer::SyncService*(void)>& get_sync_service_callback,
const base::Callback<bookmarks::BookmarkModel*(void)>&
get_bookmark_model_callback,
- scoped_refptr<base::SingleThreadTaskRunner> db_thread,
- scoped_refptr<base::SingleThreadTaskRunner> file_thread,
+ scoped_refptr<base::SequencedTaskRunner> db_thread,
+ scoped_refptr<base::SequencedTaskRunner> file_thread,
history::HistoryService* history_service)
: syncer::FakeSyncClient(factory),
pref_service_(pref_service),
+ model_type_store_service_(model_type_store_service),
sync_sessions_client_(sync_sessions_client),
personal_data_manager_(personal_data_manager),
get_syncable_service_callback_(get_syncable_service_callback),
@@ -152,6 +173,10 @@ BundleSyncClient::CreateModelWorkerForGroup(syncer::ModelSafeGroup group) {
}
}
+syncer::ModelTypeStoreService* BundleSyncClient::GetModelTypeStoreService() {
+ return model_type_store_service_;
+}
+
history::HistoryService* BundleSyncClient::GetHistoryService() {
if (history_service_)
return history_service_;
@@ -187,7 +212,7 @@ void ProfileSyncServiceBundle::SyncClientBuilder::SetPersonalDataManager(
// The client will call this callback to produce the service.
void ProfileSyncServiceBundle::SyncClientBuilder::SetSyncableServiceCallback(
- const base::Callback<base::WeakPtr<syncer::SyncableService>(
+ const base::RepeatingCallback<base::WeakPtr<syncer::SyncableService>(
syncer::ModelType type)>& get_syncable_service_callback) {
get_syncable_service_callback_ = get_syncable_service_callback;
}
@@ -214,16 +239,17 @@ std::unique_ptr<syncer::FakeSyncClient>
ProfileSyncServiceBundle::SyncClientBuilder::Build() {
return std::make_unique<BundleSyncClient>(
bundle_->component_factory(), bundle_->pref_service(),
- bundle_->sync_sessions_client(), personal_data_manager_,
- get_syncable_service_callback_, get_sync_service_callback_,
- get_bookmark_model_callback_,
+ &bundle_->model_type_store_service_, bundle_->sync_sessions_client(),
+ personal_data_manager_, get_syncable_service_callback_,
+ get_sync_service_callback_, get_bookmark_model_callback_,
activate_model_creation_ ? bundle_->db_thread() : nullptr,
- activate_model_creation_ ? base::ThreadTaskRunnerHandle::Get() : nullptr,
+ activate_model_creation_ ? base::SequencedTaskRunnerHandle::Get()
+ : nullptr,
history_service_);
}
ProfileSyncServiceBundle::ProfileSyncServiceBundle()
- : db_thread_(base::ThreadTaskRunnerHandle::Get()),
+ : db_thread_(base::SequencedTaskRunnerHandle::Get()),
signin_client_(&pref_service_),
#if defined(OS_CHROMEOS)
signin_manager_(&signin_client_, &account_tracker_),
@@ -233,13 +259,22 @@ ProfileSyncServiceBundle::ProfileSyncServiceBundle()
&account_tracker_,
nullptr),
#endif
- identity_manager_(&signin_manager_, &auth_service_),
+ gaia_cookie_manager_service_(&auth_service_,
+ "profile_sync_service_bundle",
+ &signin_client_),
+ identity_manager_(&signin_manager_,
+ &auth_service_,
+ &account_tracker_,
+ &gaia_cookie_manager_service_),
url_request_context_(new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get())) {
RegisterPrefsForProfileSyncService(pref_service_.registry());
auth_service_.set_auto_post_fetch_response_on_message_loop(true);
account_tracker_.Initialize(&signin_client_);
signin_manager_.Initialize(&pref_service_);
+ local_session_event_router_ = std::make_unique<DummyRouter>();
+ ON_CALL(sync_sessions_client_, GetLocalSessionEventRouter())
+ .WillByDefault(testing::Return(local_session_event_router_.get()));
}
ProfileSyncServiceBundle::~ProfileSyncServiceBundle() {}
@@ -255,16 +290,13 @@ ProfileSyncService::InitParams ProfileSyncServiceBundle::CreateBasicInitParams(
identity_manager(), signin_manager());
init_params.signin_scoped_device_id_callback =
base::BindRepeating([]() { return std::string(); });
- init_params.oauth2_token_service = auth_service();
init_params.network_time_update_callback = base::DoNothing();
- if (!base_directory_.IsValid())
- EXPECT_TRUE(base_directory_.CreateUniqueTempDir());
- init_params.base_directory = base_directory_.GetPath();
init_params.url_request_context = url_request_context();
+ init_params.url_loader_factory =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_);
init_params.debug_identifier = "dummyDebugName";
init_params.channel = version_info::Channel::UNKNOWN;
- init_params.model_type_store_factory =
- syncer::ModelTypeStoreTestUtil::FactoryForInMemoryStoreForTest();
return init_params;
}
diff --git a/chromium/components/browser_sync/profile_sync_test_util.h b/chromium/components/browser_sync/profile_sync_test_util.h
index a576c343267..9bacf1f34d9 100644
--- a/chromium/components/browser_sync/profile_sync_test_util.h
+++ b/chromium/components/browser_sync/profile_sync_test_util.h
@@ -8,20 +8,22 @@
#include <memory>
#include "base/callback.h"
-#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "components/browser_sync/profile_sync_service.h"
#include "components/invalidation/impl/fake_invalidation_service.h"
#include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
#include "components/signin/core/browser/fake_signin_manager.h"
#include "components/signin/core/browser/test_signin_client.h"
#include "components/sync/driver/fake_sync_client.h"
#include "components/sync/driver/sync_api_component_factory_mock.h"
+#include "components/sync/model/test_model_type_store_service.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/sync_sessions/mock_sync_sessions_client.h"
#include "services/identity/public/cpp/identity_manager.h"
+#include "services/network/test/test_url_loader_factory.h"
namespace history {
class HistoryService;
@@ -35,6 +37,10 @@ namespace user_prefs {
class PrefRegistrySyncable;
}
+namespace sync_sessions {
+class LocalSessionEventRouter;
+}
+
namespace browser_sync {
// Call this to register preferences needed for ProfileSyncService creation.
@@ -74,7 +80,7 @@ class ProfileSyncServiceBundle {
// The client will call this callback to produce the SyncableService
// specific to |type|.
void SetSyncableServiceCallback(
- const base::Callback<base::WeakPtr<syncer::SyncableService>(
+ const base::RepeatingCallback<base::WeakPtr<syncer::SyncableService>(
syncer::ModelType type)>& get_syncable_service_callback);
// The client will call this callback to produce the SyncService for the
@@ -125,6 +131,10 @@ class ProfileSyncServiceBundle {
return url_request_context_.get();
}
+ network::TestURLLoaderFactory* url_loader_factory() {
+ return &test_url_loader_factory_;
+ }
+
sync_preferences::TestingPrefServiceSyncable* pref_service() {
return &pref_service_;
}
@@ -149,27 +159,33 @@ class ProfileSyncServiceBundle {
return &fake_invalidation_service_;
}
- base::SingleThreadTaskRunner* db_thread() { return db_thread_.get(); }
+ base::SequencedTaskRunner* db_thread() { return db_thread_.get(); }
void set_db_thread(
- const scoped_refptr<base::SingleThreadTaskRunner>& db_thread) {
+ const scoped_refptr<base::SequencedTaskRunner>& db_thread) {
db_thread_ = db_thread;
}
private:
- scoped_refptr<base::SingleThreadTaskRunner> db_thread_;
+ scoped_refptr<base::SequencedTaskRunner> db_thread_;
sync_preferences::TestingPrefServiceSyncable pref_service_;
+ syncer::TestModelTypeStoreService model_type_store_service_;
TestSigninClient signin_client_;
AccountTrackerService account_tracker_;
FakeSigninManagerType signin_manager_;
FakeProfileOAuth2TokenService auth_service_;
+ FakeGaiaCookieManagerService gaia_cookie_manager_service_;
identity::IdentityManager identity_manager_;
- syncer::SyncApiComponentFactoryMock component_factory_;
+ testing::NiceMock<syncer::SyncApiComponentFactoryMock> component_factory_;
+ std::unique_ptr<sync_sessions::LocalSessionEventRouter>
+ local_session_event_router_;
testing::NiceMock<sync_sessions::MockSyncSessionsClient>
sync_sessions_client_;
invalidation::FakeInvalidationService fake_invalidation_service_;
+ // TODO(https://crbug.com/844968): Remove references to url_request_context_
+ // once the rest of the sync engine is migrated to network service.
scoped_refptr<net::URLRequestContextGetter> url_request_context_;
- base::ScopedTempDir base_directory_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceBundle);
};
diff --git a/chromium/components/browser_sync/signin_confirmation_helper.cc b/chromium/components/browser_sync/signin_confirmation_helper.cc
index 1270e0aedae..f7f9f30ffbe 100644
--- a/chromium/components/browser_sync/signin_confirmation_helper.cc
+++ b/chromium/components/browser_sync/signin_confirmation_helper.cc
@@ -5,10 +5,13 @@
#include "components/browser_sync/signin_confirmation_helper.h"
#include <memory>
+#include <utility>
-#include "base/single_thread_task_runner.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/sequenced_task_runner.h"
#include "base/strings/string16.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "components/history/core/browser/history_backend.h"
#include "components/history/core/browser/history_db_task.h"
#include "components/history/core/browser/history_service.h"
@@ -21,8 +24,9 @@ namespace {
// Determines whether there are any typed URLs in a history backend.
class HasTypedURLsTask : public history::HistoryDBTask {
public:
- explicit HasTypedURLsTask(const base::Callback<void(bool)>& cb)
- : has_typed_urls_(false), cb_(cb) {}
+ explicit HasTypedURLsTask(base::OnceCallback<void(bool)> cb)
+ : has_typed_urls_(false), cb_(std::move(cb)) {}
+ ~HasTypedURLsTask() override {}
bool RunOnDBThread(history::HistoryBackend* backend,
history::HistoryDatabase* db) override {
@@ -36,27 +40,25 @@ class HasTypedURLsTask : public history::HistoryDBTask {
return true;
}
- void DoneRunOnMainThread() override { cb_.Run(has_typed_urls_); }
+ void DoneRunOnMainThread() override { std::move(cb_).Run(has_typed_urls_); }
private:
- ~HasTypedURLsTask() override {}
-
bool has_typed_urls_;
- base::Callback<void(bool)> cb_;
+ base::OnceCallback<void(bool)> cb_;
};
} // namespace
SigninConfirmationHelper::SigninConfirmationHelper(
history::HistoryService* history_service,
- const base::Callback<void(bool)>& return_result)
- : origin_thread_(base::ThreadTaskRunnerHandle::Get()),
+ base::OnceCallback<void(bool)> return_result)
+ : origin_sequence_(base::SequencedTaskRunnerHandle::Get()),
history_service_(history_service),
pending_requests_(0),
- return_result_(return_result) {}
+ return_result_(std::move(return_result)) {}
SigninConfirmationHelper::~SigninConfirmationHelper() {
- DCHECK(origin_thread_->BelongsToCurrentThread());
+ DCHECK(origin_sequence_->RunsTasksInCurrentSequence());
}
void SigninConfirmationHelper::OnHistoryQueryResults(
@@ -95,23 +97,23 @@ void SigninConfirmationHelper::CheckHasTypedURLs() {
}
history_service_->ScheduleDBTask(
FROM_HERE,
- std::unique_ptr<history::HistoryDBTask>(new HasTypedURLsTask(base::Bind(
- &SigninConfirmationHelper::ReturnResult, base::Unretained(this)))),
+ std::make_unique<HasTypedURLsTask>(base::BindOnce(
+ &SigninConfirmationHelper::ReturnResult, base::Unretained(this))),
&task_tracker_);
}
void SigninConfirmationHelper::PostResult(bool result) {
- origin_thread_->PostTask(FROM_HERE,
- base::Bind(&SigninConfirmationHelper::ReturnResult,
- base::Unretained(this), result));
+ origin_sequence_->PostTask(
+ FROM_HERE, base::BindOnce(&SigninConfirmationHelper::ReturnResult,
+ base::Unretained(this), result));
}
void SigninConfirmationHelper::ReturnResult(bool result) {
- DCHECK(origin_thread_->BelongsToCurrentThread());
+ DCHECK(origin_sequence_->RunsTasksInCurrentSequence());
// Pass |true| into the callback as soon as one of the tasks passes a
// result of |true|, otherwise pass the last returned result.
if (--pending_requests_ == 0 || result) {
- return_result_.Run(result);
+ std::move(return_result_).Run(result);
// This leaks at shutdown if the HistoryService is destroyed, but
// the process is going to die anyway.
diff --git a/chromium/components/browser_sync/signin_confirmation_helper.h b/chromium/components/browser_sync/signin_confirmation_helper.h
index c006653df91..4260aa9e1e7 100644
--- a/chromium/components/browser_sync/signin_confirmation_helper.h
+++ b/chromium/components/browser_sync/signin_confirmation_helper.h
@@ -12,7 +12,7 @@
#include "base/task/cancelable_task_tracker.h"
namespace base {
-class SingleThreadTaskRunner;
+class SequencedTaskRunner;
}
namespace history {
@@ -28,7 +28,7 @@ namespace browser_sync {
class SigninConfirmationHelper {
public:
SigninConfirmationHelper(history::HistoryService* history_service,
- const base::Callback<void(bool)>& return_result);
+ base::OnceCallback<void(bool)> return_result);
// This helper checks if there are history entries in the history service.
void CheckHasHistory(int max_entries);
@@ -44,15 +44,15 @@ class SigninConfirmationHelper {
void OnHistoryQueryResults(size_t max_entries,
history::QueryResults* results);
- // Posts the given result to the origin thread.
+ // Posts the given result to the origin sequence.
void PostResult(bool result);
// Calls |return_result_| if |result| == true or if it's the result of the
// last pending check.
void ReturnResult(bool result);
- // The task runner for the thread this object was constructed on.
- const scoped_refptr<base::SingleThreadTaskRunner> origin_thread_;
+ // The task runner for the sequence this object was constructed on.
+ const scoped_refptr<base::SequencedTaskRunner> origin_sequence_;
// Pointer to the history service.
history::HistoryService* history_service_;
@@ -64,7 +64,7 @@ class SigninConfirmationHelper {
int pending_requests_;
// Callback to pass the result back to the caller.
- const base::Callback<void(bool)> return_result_;
+ base::OnceCallback<void(bool)> return_result_;
DISALLOW_COPY_AND_ASSIGN(SigninConfirmationHelper);
};
diff --git a/chromium/components/browser_sync/sync_auth_manager.cc b/chromium/components/browser_sync/sync_auth_manager.cc
index 8ff2fe90700..0bb12985a0b 100644
--- a/chromium/components/browser_sync/sync_auth_manager.cc
+++ b/chromium/components/browser_sync/sync_auth_manager.cc
@@ -8,7 +8,6 @@
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
-#include "components/browser_sync/profile_sync_service.h"
#include "components/sync/base/stop_source.h"
#include "components/sync/base/sync_prefs.h"
#include "components/sync/engine/sync_credentials.h"
@@ -51,27 +50,24 @@ constexpr net::BackoffEntry::Policy kRequestAccessTokenBackoffPolicy = {
} // namespace
-SyncAuthManager::SyncAuthManager(ProfileSyncService* sync_service,
- syncer::SyncPrefs* sync_prefs,
- identity::IdentityManager* identity_manager,
- OAuth2TokenService* token_service)
- : sync_service_(sync_service),
- sync_prefs_(sync_prefs),
+SyncAuthManager::SyncAuthManager(
+ syncer::SyncPrefs* sync_prefs,
+ identity::IdentityManager* identity_manager,
+ const AccountStateChangedCallback& account_state_changed,
+ const CredentialsChangedCallback& credentials_changed)
+ : sync_prefs_(sync_prefs),
identity_manager_(identity_manager),
- token_service_(token_service),
+ account_state_changed_callback_(account_state_changed),
+ credentials_changed_callback_(credentials_changed),
registered_for_auth_notifications_(false),
- is_auth_in_progress_(false),
request_access_token_backoff_(&kRequestAccessTokenBackoffPolicy),
weak_ptr_factory_(this) {
- DCHECK(sync_service_);
DCHECK(sync_prefs_);
- // |identity_manager_| and |token_service_| can be null if local Sync is
- // enabled.
+ // |identity_manager_|can be null if local Sync is enabled.
}
SyncAuthManager::~SyncAuthManager() {
if (registered_for_auth_notifications_) {
- token_service_->RemoveObserver(this);
identity_manager_->RemoveObserver(this);
}
}
@@ -79,7 +75,6 @@ SyncAuthManager::~SyncAuthManager() {
void SyncAuthManager::RegisterForAuthNotifications() {
DCHECK(!registered_for_auth_notifications_);
identity_manager_->AddObserver(this);
- token_service_->AddObserver(this);
registered_for_auth_notifications_ = true;
}
@@ -88,12 +83,6 @@ AccountInfo SyncAuthManager::GetAuthenticatedAccountInfo() const {
: AccountInfo();
}
-bool SyncAuthManager::RefreshTokenIsAvailable() const {
- std::string account_id = GetAuthenticatedAccountInfo().account_id;
- return !account_id.empty() &&
- token_service_->RefreshTokenIsAvailable(account_id);
-}
-
const syncer::SyncTokenStatus& SyncAuthManager::GetSyncTokenStatus() const {
return token_status_;
}
@@ -164,11 +153,13 @@ void SyncAuthManager::ConnectionStatusChanged(syncer::ConnectionStatus status) {
if (!request_access_token_retry_timer_.IsRunning()) {
request_access_token_backoff_.Reset();
}
- ClearAuthError();
+ last_auth_error_ = GoogleServiceAuthError::AuthErrorNone();
break;
case syncer::CONNECTION_SERVER_ERROR:
- UpdateAuthErrorState(
- GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
+ // TODO(crbug.com/839834): Verify whether CONNECTION_FAILED is really an
+ // appropriate auth error here; maybe SERVICE_ERROR would be better?
+ last_auth_error_ =
+ GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED);
break;
case syncer::CONNECTION_NOT_ATTEMPTED:
// The connection status should never change to "not attempted".
@@ -177,16 +168,6 @@ void SyncAuthManager::ConnectionStatusChanged(syncer::ConnectionStatus status) {
}
}
-void SyncAuthManager::UpdateAuthErrorState(
- const GoogleServiceAuthError& error) {
- is_auth_in_progress_ = false;
- last_auth_error_ = error;
-}
-
-void SyncAuthManager::ClearAuthError() {
- UpdateAuthErrorState(GoogleServiceAuthError::AuthErrorNone());
-}
-
void SyncAuthManager::ClearAccessTokenAndRequest() {
access_token_.clear();
request_access_token_retry_timer_.Stop();
@@ -196,93 +177,91 @@ void SyncAuthManager::ClearAccessTokenAndRequest() {
}
void SyncAuthManager::Clear() {
- ClearAuthError();
+ // TODO(crbug.com/839834): Clearing the auth error here isn't quite right.
+ // It makes sense to clear any auth error we got from the Sync server, but we
+ // should probably retain any errors from the identity manager.
+ last_auth_error_ = GoogleServiceAuthError::AuthErrorNone();
ClearAccessTokenAndRequest();
}
void SyncAuthManager::OnPrimaryAccountSet(
const AccountInfo& primary_account_info) {
- // Track the fact that we're still waiting for auth to complete.
- DCHECK(!is_auth_in_progress_);
- is_auth_in_progress_ = true;
-
- sync_service_->OnPrimaryAccountSet();
-
- if (token_service_->RefreshTokenIsAvailable(
- primary_account_info.account_id)) {
- OnRefreshTokenAvailable(primary_account_info.account_id);
- }
+ account_state_changed_callback_.Run();
}
void SyncAuthManager::OnPrimaryAccountCleared(
const AccountInfo& previous_primary_account_info) {
UMA_HISTOGRAM_ENUMERATION("Sync.StopSource", syncer::SIGN_OUT,
syncer::STOP_SOURCE_LIMIT);
- is_auth_in_progress_ = false;
- sync_service_->OnPrimaryAccountCleared();
+ account_state_changed_callback_.Run();
}
-void SyncAuthManager::OnRefreshTokenAvailable(const std::string& account_id) {
- if (account_id != GetAuthenticatedAccountInfo().account_id) {
+void SyncAuthManager::OnRefreshTokenUpdatedForAccount(
+ const AccountInfo& account_info,
+ bool is_valid) {
+ if (account_info.account_id != GetAuthenticatedAccountInfo().account_id) {
return;
}
- GoogleServiceAuthError token_error =
- token_service_->GetAuthError(GetAuthenticatedAccountInfo().account_id);
- if (token_error == GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
- GoogleServiceAuthError::InvalidGaiaCredentialsReason::
- CREDENTIALS_REJECTED_BY_CLIENT)) {
- is_auth_in_progress_ = false;
- // When the refresh token is replaced by a new token with a
- // CREDENTIALS_REJECTED_BY_CLIENT error, Sync must be stopped immediately,
- // even if the current access token is still valid. This happens e.g. when
- // the user signs out of the web with Dice enabled.
- // It is not necessary to do this when the refresh token is
- // CREDENTIALS_REJECTED_BY_SERVER, because in that case the access token
- // will be rejected by the server too.
- // We only do this in OnRefreshTokensLoaded(), as opposed to
- // OAuth2TokenService::Observer::OnAuthErrorChanged(), because
- // CREDENTIALS_REJECTED_BY_CLIENT is only set by the signin component when
- // the refresh token is created.
+ if (!is_valid) {
+ // When the refresh token is replaced by an invalid token, Sync must be
+ // stopped immediately, even if the current access token is still valid.
+ // This happens e.g. when the user signs out of the web with Dice enabled.
ClearAccessTokenAndRequest();
- // TODO(treib): Should we also set our auth error state?
- sync_service_->OnRefreshTokenRevoked();
- // TODO(treib): We can probably early-out here - no point in also calling
- // OnRefreshTokenAvailable on the ProfileSyncService.
+ // Set the last auth error to the one that is specified in
+ // google_service_auth_error.h to correspond to this case (token was
+ // invalidated client-side).
+ // TODO(blundell): Long-term, it would be nicer if Sync didn't have to
+ // cache signin-level authentication errors.
+ GoogleServiceAuthError invalid_token_error =
+ GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
+ GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+ CREDENTIALS_REJECTED_BY_CLIENT);
+ last_auth_error_ = invalid_token_error;
+
+ credentials_changed_callback_.Run();
+ return;
}
- sync_service_->OnRefreshTokenAvailable();
+ // If we already have an access token or previously failed to retrieve one
+ // (and hence the retry timer is running), then request a fresh access token
+ // now. This will also drop the current access token.
+ if (!access_token_.empty() || request_access_token_retry_timer_.IsRunning()) {
+ DCHECK(!ongoing_access_token_fetch_);
+ RequestAccessToken();
+ } else if (last_auth_error_ != GoogleServiceAuthError::AuthErrorNone()) {
+ // If we were in an auth error state, then now's also a good time to try
+ // again. In this case it's possible that there is already a pending
+ // request, in which case RequestAccessToken will simply do nothing.
+ RequestAccessToken();
+ }
}
-void SyncAuthManager::OnRefreshTokenRevoked(const std::string& account_id) {
- if (account_id != GetAuthenticatedAccountInfo().account_id) {
+void SyncAuthManager::OnRefreshTokenRemovedForAccount(
+ const AccountInfo& account_info) {
+ if (account_info.account_id != GetAuthenticatedAccountInfo().account_id) {
return;
}
- UpdateAuthErrorState(
- GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED));
+ // TODO(crbug.com/839834): REQUEST_CANCELED doesn't seem like the right auth
+ // error to use here. Maybe INVALID_GAIA_CREDENTIALS?
+ last_auth_error_ =
+ GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
ClearAccessTokenAndRequest();
- sync_service_->OnRefreshTokenRevoked();
-}
-
-void SyncAuthManager::OnRefreshTokensLoaded() {
- // This notification gets fired when OAuth2TokenService loads the tokens from
- // storage. Initialize the engine if sync is enabled. If the sync token was
- // not loaded, GetCredentials() will generate invalid credentials to cause the
- // engine to generate an auth error (https://crbug.com/121755).
- // TODO(treib): Is this necessary? Either we actually have a refresh token, in
- // which case this was already called from OnRefreshTokenAvailable above, or
- // there is no refresh token, in which case Sync can't start anyway.
- sync_service_->OnRefreshTokenAvailable();
+ credentials_changed_callback_.Run();
}
bool SyncAuthManager::IsRetryingAccessTokenFetchForTest() const {
return request_access_token_retry_timer_.IsRunning();
}
+void SyncAuthManager::ResetRequestAccessTokenBackoffForTest() {
+ request_access_token_backoff_.Reset();
+}
+
void SyncAuthManager::RequestAccessToken() {
// Only one active request at a time.
if (ongoing_access_token_fetch_) {
@@ -297,38 +276,39 @@ void SyncAuthManager::RequestAccessToken() {
// Invalidate previous token, otherwise token service will return the same
// token again.
if (!access_token_.empty()) {
- identity_manager_->RemoveAccessTokenFromCache(GetAuthenticatedAccountInfo(),
- oauth2_scopes, access_token_);
+ identity_manager_->RemoveAccessTokenFromCache(
+ GetAuthenticatedAccountInfo().account_id, oauth2_scopes, access_token_);
+
+ access_token_.clear();
+ credentials_changed_callback_.Run();
}
- access_token_.clear();
token_status_.token_request_time = base::Time::Now();
token_status_.token_receive_time = base::Time();
token_status_.next_token_request_time = base::Time();
- ongoing_access_token_fetch_ =
- identity_manager_->CreateAccessTokenFetcherForPrimaryAccount(
- kSyncOAuthConsumerName, oauth2_scopes,
- base::BindOnce(&SyncAuthManager::AccessTokenFetched,
- base::Unretained(this)),
- identity::PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
+ ongoing_access_token_fetch_ = std::make_unique<
+ identity::PrimaryAccountAccessTokenFetcher>(
+ kSyncOAuthConsumerName, identity_manager_, oauth2_scopes,
+ base::BindOnce(&SyncAuthManager::AccessTokenFetched,
+ base::Unretained(this)),
+ identity::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable);
}
-void SyncAuthManager::AccessTokenFetched(const GoogleServiceAuthError& error,
- const std::string& access_token) {
+void SyncAuthManager::AccessTokenFetched(
+ GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info) {
DCHECK(ongoing_access_token_fetch_);
+ ongoing_access_token_fetch_.reset();
- std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher> fetcher_deleter(
- std::move(ongoing_access_token_fetch_));
-
- access_token_ = access_token;
+ access_token_ = access_token_info.token;
token_status_.last_get_token_error = error;
switch (error.state()) {
case GoogleServiceAuthError::NONE:
token_status_.token_receive_time = base::Time::Now();
sync_prefs_->SetSyncAuthError(false);
- ClearAuthError();
+ last_auth_error_ = GoogleServiceAuthError::AuthErrorNone();
break;
case GoogleServiceAuthError::CONNECTION_FAILED:
case GoogleServiceAuthError::REQUEST_CANCELED:
@@ -346,14 +326,14 @@ void SyncAuthManager::AccessTokenFetched(const GoogleServiceAuthError& error,
break;
case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
sync_prefs_->SetSyncAuthError(true);
- UpdateAuthErrorState(error);
+ last_auth_error_ = error;
break;
default:
LOG(ERROR) << "Unexpected persistent error: " << error.ToString();
- UpdateAuthErrorState(error);
+ last_auth_error_ = error;
}
- sync_service_->AccessTokenFetched(error);
+ credentials_changed_callback_.Run();
}
} // namespace browser_sync
diff --git a/chromium/components/browser_sync/sync_auth_manager.h b/chromium/components/browser_sync/sync_auth_manager.h
index 219c7d052e6..7d8d9d3dad9 100644
--- a/chromium/components/browser_sync/sync_auth_manager.h
+++ b/chromium/components/browser_sync/sync_auth_manager.h
@@ -8,13 +8,14 @@
#include <memory>
#include <string>
+#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "components/signin/core/browser/account_info.h"
#include "components/sync/driver/sync_token_status.h"
+#include "components/sync/engine/connection_status.h"
#include "google_apis/gaia/google_service_auth_error.h"
-#include "google_apis/gaia/oauth2_token_service.h"
#include "net/base/backoff_entry.h"
#include "services/identity/public/cpp/identity_manager.h"
@@ -29,22 +30,24 @@ class SyncPrefs;
namespace browser_sync {
-class ProfileSyncService;
-
// SyncAuthManager tracks the primary (i.e. blessed-for-sync) account and its
// authentication state.
-class SyncAuthManager : public identity::IdentityManager::Observer,
- public OAuth2TokenService::Observer {
+class SyncAuthManager : public identity::IdentityManager::Observer {
public:
- // |sync_service| and |sync_prefs| must not be null and must outlive this.
- // |identity_manager| and |token_service| may be null (this is the case if
- // local Sync is enabled), but if non-null, must outlive this object.
- // TODO(crbug.com/842697): Don't pass the ProfileSyncService in here. Instead,
- // pass a callback ("AccountStateChanged(new_state)").
- SyncAuthManager(ProfileSyncService* sync_service,
- syncer::SyncPrefs* sync_prefs,
+ // Called when the existence of an authenticated account changes. Call
+ // GetAuthenticatedAccountInfo to get the new state.
+ using AccountStateChangedCallback = base::RepeatingClosure;
+ // Called when the credential state changes, i.e. an access token was
+ // added/changed/removed. Call GetCredentials to get the new state.
+ using CredentialsChangedCallback = base::RepeatingClosure;
+
+ // |sync_prefs| must not be null and must outlive this.
+ // |identity_manager| may be null (this is the case if local Sync is enabled),
+ // but if non-null, must outlive this object.
+ SyncAuthManager(syncer::SyncPrefs* sync_prefs,
identity::IdentityManager* identity_manager,
- OAuth2TokenService* token_service);
+ const AccountStateChangedCallback& account_state_changed,
+ const CredentialsChangedCallback& credentials_changed);
~SyncAuthManager() override;
// Tells the tracker to start listening for changes to the account/sign-in
@@ -56,15 +59,9 @@ class SyncAuthManager : public identity::IdentityManager::Observer,
// an empty AccountInfo if there isn't one.
AccountInfo GetAuthenticatedAccountInfo() const;
- // Returns whether a refresh token is available for the primary account.
- // TODO(crbug.com/842697, crbug.com/825190): ProfileSyncService shouldn't have
- // to care about the refresh token state.
- bool RefreshTokenIsAvailable() const;
-
const GoogleServiceAuthError& GetLastAuthError() const {
return last_auth_error_;
}
- bool IsAuthInProgress() const { return is_auth_in_progress_; }
// Returns the credentials to be passed to the SyncEngine.
syncer::SyncCredentials GetCredentials() const;
@@ -79,10 +76,6 @@ class SyncAuthManager : public identity::IdentityManager::Observer,
// server changed. Updates auth error state accordingly.
void ConnectionStatusChanged(syncer::ConnectionStatus status);
- // TODO(crbug.com/842697, crbug.com/825190): Make this private once
- // ProfileSyncService doesn't care about the refresh token state anymore.
- void RequestAccessToken();
-
// Clears all auth-related state (error, cached access token etc). Called
// when Sync is turned off.
void Clear();
@@ -91,39 +84,42 @@ class SyncAuthManager : public identity::IdentityManager::Observer,
void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
void OnPrimaryAccountCleared(
const AccountInfo& previous_primary_account_info) override;
+ void OnRefreshTokenUpdatedForAccount(const AccountInfo& account_info,
+ bool is_valid) override;
+ void OnRefreshTokenRemovedForAccount(
+ const AccountInfo& account_info) override;
- // OAuth2TokenService::Observer implementation.
- void OnRefreshTokenAvailable(const std::string& account_id) override;
- void OnRefreshTokenRevoked(const std::string& account_id) override;
- void OnRefreshTokensLoaded() override;
-
- // Test-only method for inspecting internal state.
+ // Test-only methods for inspecting/modifying internal state.
bool IsRetryingAccessTokenFetchForTest() const;
+ void ResetRequestAccessTokenBackoffForTest();
private:
- void UpdateAuthErrorState(const GoogleServiceAuthError& error);
- void ClearAuthError();
-
void ClearAccessTokenAndRequest();
- void AccessTokenFetched(const GoogleServiceAuthError& error,
- const std::string& access_token);
+ void RequestAccessToken();
+
+ void AccessTokenFetched(GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info);
- ProfileSyncService* const sync_service_;
syncer::SyncPrefs* const sync_prefs_;
identity::IdentityManager* const identity_manager_;
- OAuth2TokenService* const token_service_;
+
+ const AccountStateChangedCallback account_state_changed_callback_;
+ const CredentialsChangedCallback credentials_changed_callback_;
bool registered_for_auth_notifications_;
// This is a cache of the last authentication response we received either
// from the sync server or from Chrome's identity/token management system.
+ // TODO(crbug.com/839834): Differentiate between these types of auth errors,
+ // since their semantics and lifetimes are quite different: e.g. the former
+ // can only exist while the Sync engine is initialized; the latter exists
+ // independent of Sync state, and probably shouldn't get reset in Clear().
GoogleServiceAuthError last_auth_error_;
- // Set to true if a signin has completed but we're still waiting for the
- // engine to refresh its credentials.
- bool is_auth_in_progress_;
-
+ // The current access token. This is mutually exclusive with
+ // |ongoing_access_token_fetch_| and |request_access_token_retry_timer_|:
+ // We either have an access token OR a pending request OR a pending retry.
std::string access_token_;
// Pending request for an access token. Non-null iff there is a request
diff --git a/chromium/components/browser_sync/sync_auth_manager_unittest.cc b/chromium/components/browser_sync/sync_auth_manager_unittest.cc
new file mode 100644
index 00000000000..2c6f9f788dd
--- /dev/null
+++ b/chromium/components/browser_sync/sync_auth_manager_unittest.cc
@@ -0,0 +1,479 @@
+// 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/browser_sync/sync_auth_manager.h"
+
+#include "base/bind_helpers.h"
+#include "base/run_loop.h"
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_task_environment.h"
+#include "build/build_config.h"
+#include "components/sync/base/sync_prefs.h"
+#include "components/sync/engine/connection_status.h"
+#include "components/sync/engine/sync_credentials.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "net/base/net_errors.h"
+#include "services/identity/public/cpp/identity_test_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace browser_sync {
+
+namespace {
+
+class SyncAuthManagerTest : public testing::Test {
+ protected:
+ using AccountStateChangedCallback =
+ SyncAuthManager::AccountStateChangedCallback;
+ using CredentialsChangedCallback =
+ SyncAuthManager::CredentialsChangedCallback;
+
+ SyncAuthManagerTest() {
+ syncer::SyncPrefs::RegisterProfilePrefs(pref_service_.registry());
+ sync_prefs_ = std::make_unique<syncer::SyncPrefs>(&pref_service_);
+ }
+
+ ~SyncAuthManagerTest() override {}
+
+ std::unique_ptr<SyncAuthManager> CreateAuthManager() {
+ return CreateAuthManager(base::DoNothing(), base::DoNothing());
+ }
+
+ std::unique_ptr<SyncAuthManager> CreateAuthManager(
+ const AccountStateChangedCallback& account_state_changed,
+ const CredentialsChangedCallback& credentials_changed) {
+ return std::make_unique<SyncAuthManager>(
+ sync_prefs_.get(), identity_env_.identity_manager(),
+ account_state_changed, credentials_changed);
+ }
+
+ std::unique_ptr<SyncAuthManager> CreateAuthManagerForLocalSync() {
+ return std::make_unique<SyncAuthManager>(
+ sync_prefs_.get(), nullptr, base::DoNothing(), base::DoNothing());
+ }
+
+ identity::IdentityTestEnvironment* identity_env() { return &identity_env_; }
+
+ private:
+ base::test::ScopedTaskEnvironment task_environment_;
+ identity::IdentityTestEnvironment identity_env_;
+ sync_preferences::TestingPrefServiceSyncable pref_service_;
+ std::unique_ptr<syncer::SyncPrefs> sync_prefs_;
+};
+
+TEST_F(SyncAuthManagerTest, ProvidesNothingInLocalSyncMode) {
+ auto auth_manager = CreateAuthManagerForLocalSync();
+ EXPECT_TRUE(auth_manager->GetAuthenticatedAccountInfo().IsEmpty());
+ syncer::SyncCredentials credentials = auth_manager->GetCredentials();
+ EXPECT_TRUE(credentials.account_id.empty());
+ EXPECT_TRUE(credentials.email.empty());
+ EXPECT_TRUE(credentials.sync_token.empty());
+ EXPECT_TRUE(auth_manager->access_token().empty());
+ // Note: Calling RegisterForAuthNotifications is illegal in local Sync mode,
+ // so we don't test that.
+ // Calling Clear() does nothing, but shouldn't crash.
+ auth_manager->Clear();
+}
+
+// ChromeOS doesn't support sign-in/sign-out.
+#if !defined(OS_CHROMEOS)
+TEST_F(SyncAuthManagerTest, IgnoresEventsIfNotRegistered) {
+ base::MockCallback<AccountStateChangedCallback> account_state_changed;
+ base::MockCallback<CredentialsChangedCallback> credentials_changed;
+ EXPECT_CALL(account_state_changed, Run()).Times(0);
+ EXPECT_CALL(credentials_changed, Run()).Times(0);
+ auto auth_manager =
+ CreateAuthManager(account_state_changed.Get(), credentials_changed.Get());
+
+ // Fire some auth events. We haven't called RegisterForAuthNotifications, so
+ // none of this should result in any callback calls.
+ std::string account_id =
+ identity_env()->MakePrimaryAccountAvailable("test@email.com").account_id;
+ ASSERT_EQ(auth_manager->GetAuthenticatedAccountInfo().account_id, account_id);
+ identity_env()->SetRefreshTokenForPrimaryAccount();
+ identity_env()->ClearPrimaryAccount();
+ ASSERT_TRUE(auth_manager->GetAuthenticatedAccountInfo().account_id.empty());
+}
+
+TEST_F(SyncAuthManagerTest, ForwardsPrimaryAccountEvents) {
+ // Start out already signed in before the SyncAuthManager is created.
+ std::string account_id =
+ identity_env()->MakePrimaryAccountAvailable("test@email.com").account_id;
+
+ base::MockCallback<AccountStateChangedCallback> account_state_changed;
+ base::MockCallback<CredentialsChangedCallback> credentials_changed;
+ EXPECT_CALL(account_state_changed, Run()).Times(0);
+ EXPECT_CALL(credentials_changed, Run()).Times(0);
+ auto auth_manager =
+ CreateAuthManager(account_state_changed.Get(), credentials_changed.Get());
+
+ ASSERT_EQ(auth_manager->GetAuthenticatedAccountInfo().account_id, account_id);
+
+ auth_manager->RegisterForAuthNotifications();
+
+ // Sign out of the account.
+ EXPECT_CALL(account_state_changed, Run());
+ // Note: The ordering of removing the refresh token and the actual sign-out is
+ // undefined, see comment on IdentityManager::Observer. So we might or might
+ // not get a |credentials_changed| call here.
+ EXPECT_CALL(credentials_changed, Run()).Times(testing::AtMost(1));
+ identity_env()->ClearPrimaryAccount();
+ EXPECT_TRUE(auth_manager->GetAuthenticatedAccountInfo().account_id.empty());
+
+ // Sign in to a different account.
+ EXPECT_CALL(account_state_changed, Run());
+ std::string second_account_id =
+ identity_env()->MakePrimaryAccountAvailable("test@email.com").account_id;
+ EXPECT_EQ(auth_manager->GetAuthenticatedAccountInfo().account_id,
+ second_account_id);
+}
+#endif // !OS_CHROMEOS
+
+TEST_F(SyncAuthManagerTest, ForwardsCredentialsEvents) {
+ // Start out already signed in before the SyncAuthManager is created.
+ std::string account_id =
+ identity_env()->MakePrimaryAccountAvailable("test@email.com").account_id;
+
+ base::MockCallback<AccountStateChangedCallback> account_state_changed;
+ base::MockCallback<CredentialsChangedCallback> credentials_changed;
+ EXPECT_CALL(account_state_changed, Run()).Times(0);
+ EXPECT_CALL(credentials_changed, Run()).Times(0);
+ auto auth_manager =
+ CreateAuthManager(account_state_changed.Get(), credentials_changed.Get());
+
+ ASSERT_EQ(auth_manager->GetAuthenticatedAccountInfo().account_id, account_id);
+
+ auth_manager->RegisterForAuthNotifications();
+
+ // During Sync startup, the SyncEngine attempts to connect to the server
+ // without an access token, resulting in a call to ConnectionStatusChanged
+ // with CONNECTION_AUTH_ERROR. This is what kicks off the initial access token
+ // fetch.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_AUTH_ERROR);
+
+ // Once an access token is available, the callback should get run.
+ EXPECT_CALL(credentials_changed, Run());
+ identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ "access_token", base::Time::Now() + base::TimeDelta::FromHours(1));
+ ASSERT_EQ(auth_manager->GetCredentials().sync_token, "access_token");
+
+ // Now the refresh token gets updated. The access token will get dropped, so
+ // this should cause another notification.
+ EXPECT_CALL(credentials_changed, Run());
+ identity_env()->SetRefreshTokenForPrimaryAccount();
+ ASSERT_TRUE(auth_manager->GetCredentials().sync_token.empty());
+
+ // Once a new token is available, there's another notification.
+ EXPECT_CALL(credentials_changed, Run());
+ identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ "access_token_2", base::Time::Now() + base::TimeDelta::FromHours(1));
+ ASSERT_EQ(auth_manager->GetCredentials().sync_token, "access_token_2");
+
+ // Revoking the refresh token should also cause the access token to get
+ // dropped.
+ EXPECT_CALL(credentials_changed, Run());
+ identity_env()->RemoveRefreshTokenForPrimaryAccount();
+ EXPECT_TRUE(auth_manager->GetCredentials().sync_token.empty());
+}
+
+TEST_F(SyncAuthManagerTest, RequestsAccessTokenOnSyncStartup) {
+ std::string account_id =
+ identity_env()->MakePrimaryAccountAvailable("test@email.com").account_id;
+ auto auth_manager = CreateAuthManager();
+ ASSERT_EQ(auth_manager->GetAuthenticatedAccountInfo().account_id, account_id);
+ auth_manager->RegisterForAuthNotifications();
+
+ // During Sync startup, the SyncEngine attempts to connect to the server
+ // without an access token, resulting in a call to ConnectionStatusChanged
+ // with CONNECTION_AUTH_ERROR. This is what kicks off the initial access token
+ // fetch.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_AUTH_ERROR);
+
+ identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ "access_token", base::Time::Now() + base::TimeDelta::FromHours(1));
+
+ EXPECT_EQ(auth_manager->GetCredentials().sync_token, "access_token");
+}
+
+TEST_F(SyncAuthManagerTest,
+ RetriesAccessTokenFetchWithBackoffOnTransientFailure) {
+ std::string account_id =
+ identity_env()->MakePrimaryAccountAvailable("test@email.com").account_id;
+ auto auth_manager = CreateAuthManager();
+ ASSERT_EQ(auth_manager->GetAuthenticatedAccountInfo().account_id, account_id);
+ auth_manager->RegisterForAuthNotifications();
+
+ // During Sync startup, the SyncEngine attempts to connect to the server
+ // without an access token, resulting in a call to ConnectionStatusChanged
+ // with CONNECTION_AUTH_ERROR. This is what kicks off the initial access token
+ // fetch.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_AUTH_ERROR);
+
+ identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
+ GoogleServiceAuthError::FromConnectionError(net::ERR_TIMED_OUT));
+
+ // The access token fetch should get retried (with backoff, hence no actual
+ // request yet), without exposing an auth error.
+ EXPECT_TRUE(auth_manager->IsRetryingAccessTokenFetchForTest());
+ EXPECT_EQ(auth_manager->GetLastAuthError(),
+ GoogleServiceAuthError::AuthErrorNone());
+}
+
+TEST_F(SyncAuthManagerTest, AbortsAccessTokenFetchOnPersistentFailure) {
+ std::string account_id =
+ identity_env()->MakePrimaryAccountAvailable("test@email.com").account_id;
+ auto auth_manager = CreateAuthManager();
+ ASSERT_EQ(auth_manager->GetAuthenticatedAccountInfo().account_id, account_id);
+ auth_manager->RegisterForAuthNotifications();
+
+ // During Sync startup, the SyncEngine attempts to connect to the server
+ // without an access token, resulting in a call to ConnectionStatusChanged
+ // with CONNECTION_AUTH_ERROR. This is what kicks off the initial access token
+ // fetch.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_AUTH_ERROR);
+
+ GoogleServiceAuthError auth_error =
+ GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
+ GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+ CREDENTIALS_REJECTED_BY_SERVER);
+ identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
+ auth_error);
+
+ // Auth error should get exposed; no retry.
+ EXPECT_FALSE(auth_manager->IsRetryingAccessTokenFetchForTest());
+ EXPECT_EQ(auth_manager->GetLastAuthError(), auth_error);
+}
+
+TEST_F(SyncAuthManagerTest, FetchesNewAccessTokenWithBackoffOnServerError) {
+ std::string account_id =
+ identity_env()->MakePrimaryAccountAvailable("test@email.com").account_id;
+ auto auth_manager = CreateAuthManager();
+ ASSERT_EQ(auth_manager->GetAuthenticatedAccountInfo().account_id, account_id);
+ auth_manager->RegisterForAuthNotifications();
+
+ // During Sync startup, the SyncEngine attempts to connect to the server
+ // without an access token, resulting in a call to ConnectionStatusChanged
+ // with CONNECTION_AUTH_ERROR. This is what kicks off the initial access token
+ // fetch.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_AUTH_ERROR);
+ identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ "access_token", base::Time::Now() + base::TimeDelta::FromHours(1));
+ ASSERT_EQ(auth_manager->GetCredentials().sync_token, "access_token");
+
+ // But now the server is still returning AUTH_ERROR - maybe something's wrong
+ // with the token.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_AUTH_ERROR);
+
+ // The access token fetch should get retried (with backoff, hence no actual
+ // request yet), without exposing an auth error.
+ EXPECT_TRUE(auth_manager->IsRetryingAccessTokenFetchForTest());
+ EXPECT_EQ(auth_manager->GetLastAuthError(),
+ GoogleServiceAuthError::AuthErrorNone());
+}
+
+TEST_F(SyncAuthManagerTest, ExposesServerError) {
+ std::string account_id =
+ identity_env()->MakePrimaryAccountAvailable("test@email.com").account_id;
+ auto auth_manager = CreateAuthManager();
+ ASSERT_EQ(auth_manager->GetAuthenticatedAccountInfo().account_id, account_id);
+ auth_manager->RegisterForAuthNotifications();
+
+ // During Sync startup, the SyncEngine attempts to connect to the server
+ // without an access token, resulting in a call to ConnectionStatusChanged
+ // with CONNECTION_AUTH_ERROR. This is what kicks off the initial access token
+ // fetch.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_AUTH_ERROR);
+ identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ "access_token", base::Time::Now() + base::TimeDelta::FromHours(1));
+ ASSERT_EQ(auth_manager->GetCredentials().sync_token, "access_token");
+
+ // Now a server error happens.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_SERVER_ERROR);
+
+ // The error should be reported.
+ EXPECT_NE(auth_manager->GetLastAuthError(),
+ GoogleServiceAuthError::AuthErrorNone());
+ // But the access token should still be there - this might just be some
+ // non-auth-related problem with the server.
+ EXPECT_EQ(auth_manager->GetCredentials().sync_token, "access_token");
+}
+
+TEST_F(SyncAuthManagerTest, RequestsNewAccessTokenOnExpiry) {
+ std::string account_id =
+ identity_env()->MakePrimaryAccountAvailable("test@email.com").account_id;
+ auto auth_manager = CreateAuthManager();
+ ASSERT_EQ(auth_manager->GetAuthenticatedAccountInfo().account_id, account_id);
+ auth_manager->RegisterForAuthNotifications();
+
+ // During Sync startup, the SyncEngine attempts to connect to the server
+ // without an access token, resulting in a call to ConnectionStatusChanged
+ // with CONNECTION_AUTH_ERROR. This is what kicks off the initial access token
+ // fetch.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_AUTH_ERROR);
+ identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ "access_token", base::Time::Now() + base::TimeDelta::FromHours(1));
+ ASSERT_EQ(auth_manager->GetCredentials().sync_token, "access_token");
+
+ // Now everything is okay for a while.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_OK);
+ ASSERT_EQ(auth_manager->GetCredentials().sync_token, "access_token");
+ ASSERT_EQ(auth_manager->GetLastAuthError(),
+ GoogleServiceAuthError::AuthErrorNone());
+
+ // But then the token expires, resulting in an auth error from the server.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_AUTH_ERROR);
+
+ // Should immediately drop the access token and fetch a new one (no backoff).
+ EXPECT_TRUE(auth_manager->GetCredentials().sync_token.empty());
+
+ identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ "access_token_2", base::Time::Now() + base::TimeDelta::FromHours(1));
+ EXPECT_EQ(auth_manager->GetCredentials().sync_token, "access_token_2");
+}
+
+TEST_F(SyncAuthManagerTest, RequestsNewAccessTokenOnRefreshTokenUpdate) {
+ std::string account_id =
+ identity_env()->MakePrimaryAccountAvailable("test@email.com").account_id;
+ auto auth_manager = CreateAuthManager();
+ ASSERT_EQ(auth_manager->GetAuthenticatedAccountInfo().account_id, account_id);
+ auth_manager->RegisterForAuthNotifications();
+
+ // During Sync startup, the SyncEngine attempts to connect to the server
+ // without an access token, resulting in a call to ConnectionStatusChanged
+ // with CONNECTION_AUTH_ERROR. This is what kicks off the initial access token
+ // fetch.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_AUTH_ERROR);
+ identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ "access_token", base::Time::Now() + base::TimeDelta::FromHours(1));
+ ASSERT_EQ(auth_manager->GetCredentials().sync_token, "access_token");
+
+ // Now everything is okay for a while.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_OK);
+ ASSERT_EQ(auth_manager->GetCredentials().sync_token, "access_token");
+ ASSERT_EQ(auth_manager->GetLastAuthError(),
+ GoogleServiceAuthError::AuthErrorNone());
+
+ // But then the refresh token changes.
+ identity_env()->SetRefreshTokenForPrimaryAccount();
+
+ // Should immediately drop the access token and fetch a new one (no backoff).
+ EXPECT_TRUE(auth_manager->GetCredentials().sync_token.empty());
+
+ identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ "access_token_2", base::Time::Now() + base::TimeDelta::FromHours(1));
+ EXPECT_EQ(auth_manager->GetCredentials().sync_token, "access_token_2");
+}
+
+TEST_F(SyncAuthManagerTest, DoesNotRequestAccessTokenAutonomously) {
+ std::string account_id =
+ identity_env()->MakePrimaryAccountAvailable("test@email.com").account_id;
+ auto auth_manager = CreateAuthManager();
+ ASSERT_EQ(auth_manager->GetAuthenticatedAccountInfo().account_id, account_id);
+ auth_manager->RegisterForAuthNotifications();
+
+ // Do *not* call ConnectionStatusChanged here (which is what usually kicks off
+ // the token fetch).
+
+ // Now the refresh token gets updated. If we already had an access token
+ // before, then this should trigger a new fetch. But since that initial fetch
+ // never happened (e.g. because Sync is turned off), this should do nothing.
+ base::MockCallback<base::OnceClosure> access_token_requested;
+ EXPECT_CALL(access_token_requested, Run()).Times(0);
+ identity_env()->SetCallbackForNextAccessTokenRequest(
+ access_token_requested.Get());
+ identity_env()->SetRefreshTokenForPrimaryAccount();
+
+ // Make sure no access token request was sent. Since the request goes through
+ // posted tasks, we have to spin the message loop.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(auth_manager->GetCredentials().sync_token.empty());
+}
+
+TEST_F(SyncAuthManagerTest, ClearsCredentialsOnRefreshTokenRemoval) {
+ std::string account_id =
+ identity_env()->MakePrimaryAccountAvailable("test@email.com").account_id;
+ auto auth_manager = CreateAuthManager();
+ ASSERT_EQ(auth_manager->GetAuthenticatedAccountInfo().account_id, account_id);
+ auth_manager->RegisterForAuthNotifications();
+
+ // During Sync startup, the SyncEngine attempts to connect to the server
+ // without an access token, resulting in a call to ConnectionStatusChanged
+ // with CONNECTION_AUTH_ERROR. This is what kicks off the initial access token
+ // fetch.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_AUTH_ERROR);
+ identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ "access_token", base::Time::Now() + base::TimeDelta::FromHours(1));
+ ASSERT_EQ(auth_manager->GetCredentials().sync_token, "access_token");
+
+ // Now everything is okay for a while.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_OK);
+ ASSERT_EQ(auth_manager->GetCredentials().sync_token, "access_token");
+ ASSERT_EQ(auth_manager->GetLastAuthError(),
+ GoogleServiceAuthError::AuthErrorNone());
+
+ // But then the refresh token gets revoked. No new access token should get
+ // requested due to this.
+ base::MockCallback<base::OnceClosure> access_token_requested;
+ EXPECT_CALL(access_token_requested, Run()).Times(0);
+ identity_env()->SetCallbackForNextAccessTokenRequest(
+ access_token_requested.Get());
+ identity_env()->RemoveRefreshTokenForPrimaryAccount();
+
+ // Should immediately drop the access token and expose an auth error.
+ EXPECT_TRUE(auth_manager->GetCredentials().sync_token.empty());
+ EXPECT_NE(auth_manager->GetLastAuthError(),
+ GoogleServiceAuthError::AuthErrorNone());
+
+ // No new access token should have been requested. Since the request goes
+ // through posted tasks, we have to spin the message loop.
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(SyncAuthManagerTest, ClearsCredentialsOnInvalidRefreshToken) {
+ std::string account_id =
+ identity_env()->MakePrimaryAccountAvailable("test@email.com").account_id;
+ auto auth_manager = CreateAuthManager();
+ ASSERT_EQ(auth_manager->GetAuthenticatedAccountInfo().account_id, account_id);
+ auth_manager->RegisterForAuthNotifications();
+
+ // During Sync startup, the SyncEngine attempts to connect to the server
+ // without an access token, resulting in a call to ConnectionStatusChanged
+ // with CONNECTION_AUTH_ERROR. This is what kicks off the initial access token
+ // fetch.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_AUTH_ERROR);
+ identity_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ "access_token", base::Time::Now() + base::TimeDelta::FromHours(1));
+ ASSERT_EQ(auth_manager->GetCredentials().sync_token, "access_token");
+
+ // Now everything is okay for a while.
+ auth_manager->ConnectionStatusChanged(syncer::CONNECTION_OK);
+ ASSERT_EQ(auth_manager->GetCredentials().sync_token, "access_token");
+ ASSERT_EQ(auth_manager->GetLastAuthError(),
+ GoogleServiceAuthError::AuthErrorNone());
+
+ // But now an invalid refresh token gets set. No new access token should get
+ // requested due to this.
+ base::MockCallback<base::OnceClosure> access_token_requested;
+ EXPECT_CALL(access_token_requested, Run()).Times(0);
+ identity_env()->SetCallbackForNextAccessTokenRequest(
+ access_token_requested.Get());
+ identity_env()->SetInvalidRefreshTokenForPrimaryAccount();
+
+ // Should immediately drop the access token and expose a special auth error.
+ EXPECT_TRUE(auth_manager->GetCredentials().sync_token.empty());
+ GoogleServiceAuthError invalid_token_error =
+ GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
+ GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+ CREDENTIALS_REJECTED_BY_CLIENT);
+ EXPECT_EQ(auth_manager->GetLastAuthError(), invalid_token_error);
+
+ // No new access token should have been requested. Since the request goes
+ // through posted tasks, we have to spin the message loop.
+ base::RunLoop().RunUntilIdle();
+}
+
+} // namespace
+
+} // namespace browser_sync
diff --git a/chromium/components/browser_sync_strings.grdp b/chromium/components/browser_sync_strings.grdp
deleted file mode 100644
index 9a8d96266ae..00000000000
--- a/chromium/components/browser_sync_strings.grdp
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<grit-part>
-
- <!-- Sync time strings -->
- <message name="IDS_SYNC_TIME_NEVER" desc="Indicates that the first sync has never completed.">
- Never
- </message>
- <message name="IDS_SYNC_TIME_JUST_NOW" desc="Indicates that a sync cycle just finished.">
- Just now
- </message>
-
-</grit-part>
diff --git a/chromium/components/browser_watcher/postmortem_report_collector_unittest.cc b/chromium/components/browser_watcher/postmortem_report_collector_unittest.cc
index fdb11f6c1ab..86e2ee7df9d 100644
--- a/chromium/components/browser_watcher/postmortem_report_collector_unittest.cc
+++ b/chromium/components/browser_watcher/postmortem_report_collector_unittest.cc
@@ -24,7 +24,7 @@
#include "base/metrics/persistent_memory_allocator.h"
#include "base/process/process_handle.h"
#include "base/stl_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/threading/platform_thread.h"
#include "components/browser_watcher/stability_data_names.h"
#include "components/browser_watcher/stability_report_extractor.h"
diff --git a/chromium/components/browser_watcher/watcher_metrics_provider_win_unittest.cc b/chromium/components/browser_watcher/watcher_metrics_provider_win_unittest.cc
index 65ba549ab04..f2c70b2bc67 100644
--- a/chromium/components/browser_watcher/watcher_metrics_provider_win_unittest.cc
+++ b/chromium/components/browser_watcher/watcher_metrics_provider_win_unittest.cc
@@ -12,7 +12,7 @@
#include "base/process/process_handle.h"
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/test_reg_util_win.h"
#include "base/win/registry.h"
diff --git a/chromium/components/browser_watcher/window_hang_monitor_win.cc b/chromium/components/browser_watcher/window_hang_monitor_win.cc
index ca89e6ab1fe..739a46ea054 100644
--- a/chromium/components/browser_watcher/window_hang_monitor_win.cc
+++ b/chromium/components/browser_watcher/window_hang_monitor_win.cc
@@ -60,7 +60,6 @@ WindowHangMonitor::WindowHangMonitor(base::TimeDelta ping_interval,
: callback_(callback),
ping_interval_(ping_interval),
hang_timeout_(timeout),
- timer_(false /* don't retain user task */, false /* don't repeat */),
outstanding_ping_(nullptr) {
}
diff --git a/chromium/components/browser_watcher/window_hang_monitor_win.h b/chromium/components/browser_watcher/window_hang_monitor_win.h
index 5f2b4156d32..e3916f35616 100644
--- a/chromium/components/browser_watcher/window_hang_monitor_win.h
+++ b/chromium/components/browser_watcher/window_hang_monitor_win.h
@@ -95,7 +95,7 @@ class WindowHangMonitor {
base::TimeDelta hang_timeout_;
// The timer used to schedule polls.
- base::Timer timer_;
+ base::OneShotTimer timer_;
// Non-null when there is an outstanding ping.
// This is intentionally leaked when a hang is detected.
diff --git a/chromium/components/browsing_data/content/DEPS b/chromium/components/browsing_data/content/DEPS
index 13343d7c5d5..90930440aa6 100644
--- a/chromium/components/browsing_data/content/DEPS
+++ b/chromium/components/browsing_data/content/DEPS
@@ -2,5 +2,8 @@ include_rules = [
"+components/content_settings/core/browser",
"+components/content_settings/core/common",
"+content/public/browser",
- "+net"
+ "+mojo/public/cpp/bindings",
+ "+net",
+ "+services/network/public/cpp",
+ "+services/network/public/mojom",
]
diff --git a/chromium/components/browsing_data/content/conditional_cache_counting_helper.cc b/chromium/components/browsing_data/content/conditional_cache_counting_helper.cc
index 7d7d518d454..985a4e76c17 100644
--- a/chromium/components/browsing_data/content/conditional_cache_counting_helper.cc
+++ b/chromium/components/browsing_data/content/conditional_cache_counting_helper.cc
@@ -4,36 +4,34 @@
#include "components/browsing_data/content/conditional_cache_counting_helper.h"
+#include <utility>
+
#include "base/callback.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
+#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/features.h"
+#include "services/network/public/mojom/network_context.mojom.h"
using content::BrowserThread;
namespace browsing_data {
-// static.
-ConditionalCacheCountingHelper* ConditionalCacheCountingHelper::CreateForRange(
- content::StoragePartition* storage_partition,
- base::Time begin_time,
- base::Time end_time) {
- return new ConditionalCacheCountingHelper(
- begin_time, end_time, storage_partition->GetURLRequestContext(),
- storage_partition->GetMediaURLRequestContext());
-}
-
ConditionalCacheCountingHelper::ConditionalCacheCountingHelper(
base::Time begin_time,
base::Time end_time,
net::URLRequestContextGetter* main_context_getter,
- net::URLRequestContextGetter* media_context_getter)
+ net::URLRequestContextGetter* media_context_getter,
+ CacheCountCallback result_callback)
: calculation_result_(0),
+ is_upper_limit_(false),
+ result_callback_(std::move(result_callback)),
begin_time_(begin_time),
end_time_(end_time),
is_finished_(false),
@@ -41,8 +39,7 @@ ConditionalCacheCountingHelper::ConditionalCacheCountingHelper(
media_context_getter_(media_context_getter),
next_cache_state_(CacheState::NONE),
cache_(nullptr),
- iterator_(nullptr),
- weak_ptr_factory_(this) {
+ iterator_(nullptr) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
@@ -50,27 +47,51 @@ ConditionalCacheCountingHelper::~ConditionalCacheCountingHelper() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
-base::WeakPtr<ConditionalCacheCountingHelper>
-ConditionalCacheCountingHelper::CountAndDestroySelfWhenFinished(
- const CacheCountCallback& result_callback) {
+// static
+void ConditionalCacheCountingHelper::Count(
+ content::StoragePartition* storage_partition,
+ base::Time begin_time,
+ base::Time end_time,
+ CacheCountCallback result_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!result_callback.is_null());
- result_callback_ = result_callback;
- calculation_result_ = 0;
- is_upper_limit_ = false;
-
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::Bind(&ConditionalCacheCountingHelper::CountHttpCacheOnIOThread,
- base::Unretained(this)));
- return weak_ptr_factory_.GetWeakPtr();
+
+ // The new path generally can't be used with network service off, since it
+ // would only count the main cache, missing the media cache. (There is a way
+ // of disabling that separately, but as the feature is in chrome/, we can't be
+ // aware of that here).
+ //
+ // See https://crbug.com/789657 for the bug on media cache and network
+ // service.
+ //
+ // TODO(morlovich): If the media cache goes away, this class can be simplified
+ // to just the "network service" path.
+ if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+ storage_partition->GetNetworkContext()->ComputeHttpCacheSize(
+ begin_time, end_time,
+ mojo::WrapCallbackWithDefaultInvokeIfNotRun(
+ std::move(result_callback),
+ /* is_upper_limit = */ false,
+ /* result_or_error = */ net::ERR_FAILED));
+ } else {
+ ConditionalCacheCountingHelper* instance =
+ new ConditionalCacheCountingHelper(
+ begin_time, end_time, storage_partition->GetURLRequestContext(),
+ storage_partition->GetMediaURLRequestContext(),
+ std::move(result_callback));
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(
+ &ConditionalCacheCountingHelper::CountHttpCacheOnIOThread,
+ base::Unretained(instance)));
+ }
}
void ConditionalCacheCountingHelper::Finished() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!is_finished_);
is_finished_ = true;
- result_callback_.Run(is_upper_limit_, calculation_result_);
+ std::move(result_callback_).Run(is_upper_limit_, calculation_result_);
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
}
diff --git a/chromium/components/browsing_data/content/conditional_cache_counting_helper.h b/chromium/components/browsing_data/content/conditional_cache_counting_helper.h
index 6c3742f1da8..6ea777b119b 100644
--- a/chromium/components/browsing_data/content/conditional_cache_counting_helper.h
+++ b/chromium/components/browsing_data/content/conditional_cache_counting_helper.h
@@ -27,20 +27,16 @@ class ConditionalCacheCountingHelper {
public:
// Returns if this value is an upper estimate and the number bytes in the
// selected range.
- typedef base::Callback<void(bool, int64_t)> CacheCountCallback;
+ typedef base::OnceCallback<void(bool, int64_t)> CacheCountCallback;
- static ConditionalCacheCountingHelper* CreateForRange(
- content::StoragePartition* storage_partition,
- base::Time begin_time,
- base::Time end_time);
-
- // Count the cache entries according to the specified time range. Destroys
- // this instance of ConditionalCacheCountingHelper when finished.
- // Must be called on the UI thread!
+ // Counts the cache entries according to the specified time range.
+ // Must be called on the UI thread.
//
// The |completion_callback| will be invoked when the operation completes.
- base::WeakPtr<ConditionalCacheCountingHelper> CountAndDestroySelfWhenFinished(
- const CacheCountCallback& result_callback);
+ static void Count(content::StoragePartition* storage_partition,
+ base::Time begin_time,
+ base::Time end_time,
+ CacheCountCallback result_callback);
private:
enum class CacheState {
@@ -58,7 +54,8 @@ class ConditionalCacheCountingHelper {
base::Time begin_time,
base::Time end_time,
net::URLRequestContextGetter* main_context_getter,
- net::URLRequestContextGetter* media_context_getter);
+ net::URLRequestContextGetter* media_context_getter,
+ CacheCountCallback result_callback);
~ConditionalCacheCountingHelper();
void Finished();
@@ -66,6 +63,8 @@ class ConditionalCacheCountingHelper {
void CountHttpCacheOnIOThread();
void DoCountCache(int rv);
+ // State used for legacy path. Will eventually go away.
+
// Stores the cache size computation result before it can be returned
// via a callback. This is either the sum of size of the the two cache
// backends, or an error code if the calculation failed.
@@ -86,8 +85,6 @@ class ConditionalCacheCountingHelper {
std::unique_ptr<disk_cache::Backend::Iterator> iterator_;
- base::WeakPtrFactory<ConditionalCacheCountingHelper> weak_ptr_factory_;
-
DISALLOW_COPY_AND_ASSIGN(ConditionalCacheCountingHelper);
};
diff --git a/chromium/components/browsing_data/core/BUILD.gn b/chromium/components/browsing_data/core/BUILD.gn
index 4e1a1b8e25b..0d22030a526 100644
--- a/chromium/components/browsing_data/core/BUILD.gn
+++ b/chromium/components/browsing_data/core/BUILD.gn
@@ -70,16 +70,16 @@ source_set("unit_tests") {
deps = [
":core",
- "//base",
+ "//base/test:test_support",
"//components/autofill/core/browser:browser",
"//components/history/core/test:test",
"//components/password_manager/core/browser:test_support",
- "//components/signin/core/browser:test_support",
+ "//components/prefs:test_support",
"//components/sync",
"//components/sync:test_support_driver",
"//components/sync_preferences:test_support",
"//components/version_info:version_info",
- "//net",
+ "//net:test_support",
"//testing/gtest",
]
}
diff --git a/chromium/components/browsing_data/core/DEPS b/chromium/components/browsing_data/core/DEPS
index d88a839b72f..3b68fd569bd 100644
--- a/chromium/components/browsing_data/core/DEPS
+++ b/chromium/components/browsing_data/core/DEPS
@@ -7,7 +7,6 @@ include_rules = [
"+components/password_manager/core/browser",
"+components/pref_registry",
"+components/prefs",
- "+components/signin",
"+components/strings/grit/components_strings.h",
"+components/sync/base",
"+components/sync/driver",
diff --git a/chromium/components/browsing_data/core/counters/passwords_counter.cc b/chromium/components/browsing_data/core/counters/passwords_counter.cc
index 717d54d0e10..c58ecdd4bfa 100644
--- a/chromium/components/browsing_data/core/counters/passwords_counter.cc
+++ b/chromium/components/browsing_data/core/counters/passwords_counter.cc
@@ -17,7 +17,7 @@ bool IsPasswordSyncEnabled(const syncer::SyncService* sync_service) {
if (!sync_service)
return false;
return password_manager_util::GetPasswordSyncState(sync_service) !=
- password_manager::PasswordSyncState::NOT_SYNCING_PASSWORDS;
+ password_manager::SyncState::NOT_SYNCING;
}
} // namespace
diff --git a/chromium/components/browsing_data/core/history_notice_utils_unittest.cc b/chromium/components/browsing_data/core/history_notice_utils_unittest.cc
index 8da708cb4bb..604f041c801 100644
--- a/chromium/components/browsing_data/core/history_notice_utils_unittest.cc
+++ b/chromium/components/browsing_data/core/history_notice_utils_unittest.cc
@@ -25,7 +25,16 @@ namespace {
class TestSyncService : public syncer::FakeSyncService {
public:
// Getters (FakeSyncService implementation). ---------------------------------
- bool IsSyncActive() const override { return sync_active_; }
+ int GetDisableReasons() const override { return DISABLE_REASON_NONE; }
+
+ State GetState() const override {
+ return IsFirstSetupComplete() ? State::ACTIVE
+ : State::PENDING_DESIRED_CONFIGURATION;
+ }
+
+ bool IsFirstSetupComplete() const override {
+ return is_first_setup_complete_;
+ }
syncer::ModelTypeSet GetActiveDataTypes() const override {
return active_data_types_;
@@ -36,8 +45,8 @@ class TestSyncService : public syncer::FakeSyncService {
}
// Setters. ------------------------------------------------------------------
- void set_sync_active(bool active) {
- sync_active_ = active;
+ void set_first_setup_complete(bool complete) {
+ is_first_setup_complete_ = complete;
}
void set_active_data_types(syncer::ModelTypeSet data_types) {
@@ -49,9 +58,9 @@ class TestSyncService : public syncer::FakeSyncService {
}
private:
+ bool is_first_setup_complete_ = false;
syncer::ModelTypeSet active_data_types_;
bool using_secondary_passphrase_ = false;
- bool sync_active_ = false;
};
} // namespace
@@ -63,8 +72,7 @@ class HistoryNoticeUtilsTest : public ::testing::Test {
void SetUp() override {
sync_service_.reset(new TestSyncService());
- history_service_.reset(new history::FakeWebHistoryService(
- url_request_context_));
+ history_service_ = std::make_unique<history::FakeWebHistoryService>();
history_service_->SetupFakeResponse(true /* success */, net::HTTP_OK);
}
@@ -108,7 +116,7 @@ TEST_F(HistoryNoticeUtilsTest, NotSyncing) {
}
TEST_F(HistoryNoticeUtilsTest, SyncingWithWrongParameters) {
- sync_service()->set_sync_active(true);
+ sync_service()->set_first_setup_complete(true);
// Regardless of the state of the web history...
history_service()->SetWebAndAppActivityEnabled(true);
@@ -128,7 +136,7 @@ TEST_F(HistoryNoticeUtilsTest, SyncingWithWrongParameters) {
TEST_F(HistoryNoticeUtilsTest, WebHistoryStates) {
// If history Sync is active...
- sync_service()->set_sync_active(true);
+ sync_service()->set_first_setup_complete(true);
sync_service()->set_active_data_types(syncer::ModelTypeSet::All());
// ...the result is true if both web history queries return true...
diff --git a/chromium/components/bubble/bubble_manager.cc b/chromium/components/bubble/bubble_manager.cc
index bf15e87f30a..e00a01b1579 100644
--- a/chromium/components/bubble/bubble_manager.cc
+++ b/chromium/components/bubble/bubble_manager.cc
@@ -81,6 +81,10 @@ void BubbleManager::RemoveBubbleManagerObserver(
observers_.RemoveObserver(observer);
}
+size_t BubbleManager::GetBubbleCountForTesting() const {
+ return controllers_.size();
+}
+
void BubbleManager::FinalizePendingRequests() {
// Return if already "Finalized".
if (manager_state_ == NO_MORE_BUBBLES)
diff --git a/chromium/components/bubble/bubble_manager.h b/chromium/components/bubble/bubble_manager.h
index 5dc1000383b..935a8b08e02 100644
--- a/chromium/components/bubble/bubble_manager.h
+++ b/chromium/components/bubble/bubble_manager.h
@@ -70,6 +70,9 @@ class BubbleManager {
// Remove an observer from this BubbleManager.
void RemoveBubbleManagerObserver(BubbleManagerObserver* observer);
+ // Returns the number of bubbles currently being managed.
+ size_t GetBubbleCountForTesting() const;
+
protected:
// Will close any open bubbles and prevent new ones from being shown.
void FinalizePendingRequests();
diff --git a/chromium/components/captive_portal/captive_portal_detector.cc b/chromium/components/captive_portal/captive_portal_detector.cc
index 90a39046e20..f06d2fe75b6 100644
--- a/chromium/components/captive_portal/captive_portal_detector.cc
+++ b/chromium/components/captive_portal/captive_portal_detector.cc
@@ -27,13 +27,13 @@ CaptivePortalDetector::~CaptivePortalDetector() {
void CaptivePortalDetector::DetectCaptivePortal(
const GURL& url,
- const DetectionCallback& detection_callback,
+ DetectionCallback detection_callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!FetchingURL());
DCHECK(detection_callback_.is_null());
- detection_callback_ = detection_callback;
+ detection_callback_ = std::move(detection_callback);
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = url;
@@ -85,10 +85,8 @@ void CaptivePortalDetector::OnSimpleLoaderCompleteInternal(
Results results;
GetCaptivePortalResultFromResponse(net_error, response_code, url, headers,
&results);
- DetectionCallback callback = detection_callback_;
simple_loader_.reset();
- detection_callback_.Reset();
- callback.Run(results);
+ std::move(detection_callback_).Run(results);
}
void CaptivePortalDetector::GetCaptivePortalResultFromResponse(
diff --git a/chromium/components/captive_portal/captive_portal_detector.h b/chromium/components/captive_portal/captive_portal_detector.h
index e4d3fbd3894..069f26d0355 100644
--- a/chromium/components/captive_portal/captive_portal_detector.h
+++ b/chromium/components/captive_portal/captive_portal_detector.h
@@ -33,7 +33,7 @@ class CAPTIVE_PORTAL_EXPORT CaptivePortalDetector {
GURL landing_url;
};
- typedef base::Callback<void(const Results& results)> DetectionCallback;
+ typedef base::OnceCallback<void(const Results& results)> DetectionCallback;
// The test URL. When connected to the Internet, it should return a
// blank page with a 204 status code. When behind a captive portal,
@@ -50,7 +50,7 @@ class CAPTIVE_PORTAL_EXPORT CaptivePortalDetector {
// |callback|.
void DetectCaptivePortal(
const GURL& url,
- const DetectionCallback& callback,
+ DetectionCallback callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation);
// Cancels captive portal check.
diff --git a/chromium/components/captive_portal/captive_portal_detector_unittest.cc b/chromium/components/captive_portal/captive_portal_detector_unittest.cc
index b14b093d021..badce529de7 100644
--- a/chromium/components/captive_portal/captive_portal_detector_unittest.cc
+++ b/chromium/components/captive_portal/captive_portal_detector_unittest.cc
@@ -74,8 +74,8 @@ class CaptivePortalDetectorTest : public testing::Test,
detector()->DetectCaptivePortal(
url,
- base::Bind(&CaptivePortalClient::OnPortalDetectionCompleted,
- base::Unretained(&client)),
+ base::BindOnce(&CaptivePortalClient::OnPortalDetectionCompleted,
+ base::Unretained(&client)),
TRAFFIC_ANNOTATION_FOR_TESTS);
ASSERT_TRUE(FetchingURL());
@@ -100,8 +100,8 @@ class CaptivePortalDetectorTest : public testing::Test,
detector()->DetectCaptivePortal(
url,
- base::Bind(&CaptivePortalClient::OnPortalDetectionCompleted,
- base::Unretained(&client)),
+ base::BindOnce(&CaptivePortalClient::OnPortalDetectionCompleted,
+ base::Unretained(&client)),
TRAFFIC_ANNOTATION_FOR_TESTS);
ASSERT_TRUE(FetchingURL());
diff --git a/chromium/components/cast_certificate/cast_cert_validator.cc b/chromium/components/cast_certificate/cast_cert_validator.cc
index f70455e861f..ffcd2b11841 100644
--- a/chromium/components/cast_certificate/cast_cert_validator.cc
+++ b/chromium/components/cast_certificate/cast_cert_validator.cc
@@ -105,7 +105,10 @@ net::der::Input AudioOnlyPolicyOid() {
// It will also require RSA keys have a modulus at least 2048-bits long.
class CastPathBuilderDelegate : public net::SimplePathBuilderDelegate {
public:
- CastPathBuilderDelegate() : SimplePathBuilderDelegate(2048) {}
+ CastPathBuilderDelegate()
+ : SimplePathBuilderDelegate(
+ 2048,
+ SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1) {}
};
class CertVerificationContextImpl : public CertVerificationContext {
diff --git a/chromium/components/cast_certificate/cast_crl.cc b/chromium/components/cast_certificate/cast_crl.cc
index 9a4e6eb6fa4..c2f38e4398e 100644
--- a/chromium/components/cast_certificate/cast_crl.cc
+++ b/chromium/components/cast_certificate/cast_crl.cc
@@ -134,7 +134,8 @@ bool VerifyCRL(const Crl& crl,
// SimplePathBuilderDelegate will enforce required signature algorithms of
// RSASSA PKCS#1 v1.5 with SHA-256, and RSA keys 2048-bits or longer.
- net::SimplePathBuilderDelegate path_builder_delegate(2048);
+ net::SimplePathBuilderDelegate path_builder_delegate(
+ 2048, net::SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1);
net::CertPathBuilder::Result result;
net::CertPathBuilder path_builder(
diff --git a/chromium/components/cast_channel/OWNERS b/chromium/components/cast_channel/OWNERS
index 04c09783e45..3590d525052 100644
--- a/chromium/components/cast_channel/OWNERS
+++ b/chromium/components/cast_channel/OWNERS
@@ -1,4 +1,3 @@
-amp@chromium.org
imcheng@chromium.org
mfoltz@chromium.org
diff --git a/chromium/components/cast_channel/cast_socket.cc b/chromium/components/cast_channel/cast_socket.cc
index 5ef5fb1b1ba..29e12018b45 100644
--- a/chromium/components/cast_channel/cast_socket.cc
+++ b/chromium/components/cast_channel/cast_socket.cc
@@ -78,7 +78,7 @@ class FakeCertVerifier : public net::CertVerifier {
int Verify(const RequestParams& params,
net::CRLSet*,
net::CertVerifyResult* verify_result,
- const net::CompletionCallback&,
+ net::CompletionOnceCallback,
std::unique_ptr<Request>*,
const net::NetLogWithSource&) override {
verify_result->Reset();
@@ -623,7 +623,7 @@ void CastSocketImpl::CloseInternal() {
SetReadyState(ReadyState::CLOSED);
}
-base::Timer* CastSocketImpl::GetTimer() {
+base::OneShotTimer* CastSocketImpl::GetTimer() {
return connect_timeout_timer_.get();
}
diff --git a/chromium/components/cast_channel/cast_socket.h b/chromium/components/cast_channel/cast_socket.h
index aad67ad8988..c9f7a862d03 100644
--- a/chromium/components/cast_channel/cast_socket.h
+++ b/chromium/components/cast_channel/cast_socket.h
@@ -328,7 +328,7 @@ class CastSocketImpl : public CastSocket {
// Runs the external connection callback and resets it.
void DoConnectCallback();
- virtual base::Timer* GetTimer();
+ virtual base::OneShotTimer* GetTimer();
void SetConnectState(ConnectionState connect_state);
void SetReadyState(ReadyState ready_state);
@@ -378,7 +378,7 @@ class CastSocketImpl : public CastSocket {
base::CancelableClosure connect_timeout_callback_;
// Timer invoked when the connection has timed out.
- std::unique_ptr<base::Timer> connect_timeout_timer_;
+ std::unique_ptr<base::OneShotTimer> connect_timeout_timer_;
// Set when a timeout is triggered and the connection process has
// canceled.
diff --git a/chromium/components/cast_channel/cast_socket_unittest.cc b/chromium/components/cast_channel/cast_socket_unittest.cc
index 75c46359e49..d9452caa442 100644
--- a/chromium/components/cast_channel/cast_socket_unittest.cc
+++ b/chromium/components/cast_channel/cast_socket_unittest.cc
@@ -146,7 +146,7 @@ class TestCastSocketBase : public CastSocketImpl {
extract_cert_result_(true),
verify_challenge_result_(true),
verify_challenge_disallow_(false),
- mock_timer_(new base::MockTimer(false, false)) {}
+ mock_timer_(new base::MockOneShotTimer()) {}
void SetExtractCertResult(bool value) { extract_cert_result_ = value; }
@@ -178,7 +178,7 @@ class TestCastSocketBase : public CastSocketImpl {
return verify_challenge_result_;
}
- base::Timer* GetTimer() override { return mock_timer_.get(); }
+ base::OneShotTimer* GetTimer() override { return mock_timer_.get(); }
net::IPEndPoint ip_;
// Simulated result of peer cert extraction.
@@ -186,7 +186,7 @@ class TestCastSocketBase : public CastSocketImpl {
// Simulated result of verifying challenge reply.
bool verify_challenge_result_;
bool verify_challenge_disallow_;
- std::unique_ptr<base::MockTimer> mock_timer_;
+ std::unique_ptr<base::MockOneShotTimer> mock_timer_;
private:
DISALLOW_COPY_AND_ASSIGN(TestCastSocketBase);
diff --git a/chromium/components/cast_channel/keep_alive_delegate.cc b/chromium/components/cast_channel/keep_alive_delegate.cc
index 082ecc838d8..e052ecb98e1 100644
--- a/chromium/components/cast_channel/keep_alive_delegate.cc
+++ b/chromium/components/cast_channel/keep_alive_delegate.cc
@@ -40,8 +40,8 @@ KeepAliveDelegate::KeepAliveDelegate(
KeepAliveDelegate::~KeepAliveDelegate() {}
void KeepAliveDelegate::SetTimersForTest(
- std::unique_ptr<base::Timer> injected_ping_timer,
- std::unique_ptr<base::Timer> injected_liveness_timer) {
+ std::unique_ptr<base::RetainingOneShotTimer> injected_ping_timer,
+ std::unique_ptr<base::RetainingOneShotTimer> injected_liveness_timer) {
ping_timer_ = std::move(injected_ping_timer);
liveness_timer_ = std::move(injected_liveness_timer);
}
@@ -56,10 +56,10 @@ void KeepAliveDelegate::Start() {
// Use injected mock timers, if provided.
if (!ping_timer_) {
- ping_timer_.reset(new base::Timer(true, false));
+ ping_timer_.reset(new base::RetainingOneShotTimer());
}
if (!liveness_timer_) {
- liveness_timer_.reset(new base::Timer(true, false));
+ liveness_timer_.reset(new base::RetainingOneShotTimer());
}
ping_timer_->Start(
diff --git a/chromium/components/cast_channel/keep_alive_delegate.h b/chromium/components/cast_channel/keep_alive_delegate.h
index 943cbfda273..e1818464dae 100644
--- a/chromium/components/cast_channel/keep_alive_delegate.h
+++ b/chromium/components/cast_channel/keep_alive_delegate.h
@@ -40,8 +40,9 @@ class KeepAliveDelegate : public CastTransport::Delegate {
~KeepAliveDelegate() override;
- void SetTimersForTest(std::unique_ptr<base::Timer> injected_ping_timer,
- std::unique_ptr<base::Timer> injected_liveness_timer);
+ void SetTimersForTest(
+ std::unique_ptr<base::RetainingOneShotTimer> injected_ping_timer,
+ std::unique_ptr<base::RetainingOneShotTimer> injected_liveness_timer);
// CastTransport::Delegate implementation.
void Start() override;
@@ -87,10 +88,10 @@ class KeepAliveDelegate : public CastTransport::Delegate {
base::TimeDelta ping_interval_;
// Fired when |ping_interval_| is exceeded or when triggered by test code.
- std::unique_ptr<base::Timer> ping_timer_;
+ std::unique_ptr<base::RetainingOneShotTimer> ping_timer_;
// Fired when |liveness_timer_| is exceeded.
- std::unique_ptr<base::Timer> liveness_timer_;
+ std::unique_ptr<base::RetainingOneShotTimer> liveness_timer_;
// The PING message to send over the wire.
const CastMessage ping_message_;
diff --git a/chromium/components/cast_channel/keep_alive_delegate_unittest.cc b/chromium/components/cast_channel/keep_alive_delegate_unittest.cc
index 8cc31d880cc..d6f1ca5ff79 100644
--- a/chromium/components/cast_channel/keep_alive_delegate_unittest.cc
+++ b/chromium/components/cast_channel/keep_alive_delegate_unittest.cc
@@ -46,10 +46,9 @@ CastMessage CreateNonKeepAliveMessage(const std::string& message_type) {
// Extends MockTimer with a mockable method ResetTriggered() which permits
// test code to set GMock expectations for Timer::Reset().
-class MockTimerWithMonitoredReset : public base::MockTimer {
+class MockTimerWithMonitoredReset : public base::MockRetainingOneShotTimer {
public:
- MockTimerWithMonitoredReset(bool retain_user_task, bool is_repeating)
- : base::MockTimer(retain_user_task, is_repeating) {}
+ MockTimerWithMonitoredReset() {}
~MockTimerWithMonitoredReset() override {}
// Instrumentation point for determining how many times Reset() was called.
@@ -59,12 +58,12 @@ class MockTimerWithMonitoredReset : public base::MockTimer {
// Passes through the Reset call to the base MockTimer and visits the mock
// ResetTriggered method.
void Reset() override {
- base::MockTimer::Reset();
+ base::MockRetainingOneShotTimer::Reset();
ResetTriggered();
}
void Stop() override {
- base::MockTimer::Stop();
+ base::MockRetainingOneShotTimer::Stop();
StopTriggered();
}
};
@@ -84,8 +83,8 @@ class KeepAliveDelegateTest : public testing::Test {
&socket_, logger_, base::WrapUnique(inner_delegate_),
base::TimeDelta::FromMilliseconds(kTestPingTimeoutMillis),
base::TimeDelta::FromMilliseconds(kTestLivenessTimeoutMillis)));
- liveness_timer_ = new MockTimerWithMonitoredReset(true, false);
- ping_timer_ = new MockTimerWithMonitoredReset(true, false);
+ liveness_timer_ = new MockTimerWithMonitoredReset;
+ ping_timer_ = new MockTimerWithMonitoredReset;
EXPECT_CALL(*liveness_timer_, StopTriggered()).Times(0);
EXPECT_CALL(*ping_timer_, StopTriggered()).Times(0);
keep_alive_->SetTimersForTest(base::WrapUnique(ping_timer_),
@@ -232,10 +231,10 @@ TEST_F(KeepAliveDelegateTest, TestPassthroughMessagesAfterError) {
TEST_F(KeepAliveDelegateTest, TestLivenessTimerResetAfterSendingMessage) {
scoped_refptr<base::TestMockTimeTaskRunner> mock_time_task_runner(
new base::TestMockTimeTaskRunner());
- std::unique_ptr<base::Timer> liveness_timer = std::make_unique<base::Timer>(
- true, false, mock_time_task_runner->GetMockTickClock());
- std::unique_ptr<base::Timer> ping_timer = std::make_unique<base::Timer>(
- true, false, mock_time_task_runner->GetMockTickClock());
+ auto liveness_timer = std::make_unique<base::RetainingOneShotTimer>(
+ mock_time_task_runner->GetMockTickClock());
+ auto ping_timer = std::make_unique<base::RetainingOneShotTimer>(
+ mock_time_task_runner->GetMockTickClock());
ping_timer->SetTaskRunner(mock_time_task_runner);
liveness_timer->SetTaskRunner(mock_time_task_runner);
keep_alive_->SetTimersForTest(std::move(ping_timer),
diff --git a/chromium/components/cbor/cbor_values.cc b/chromium/components/cbor/cbor_values.cc
index 42454aac633..370050ae885 100644
--- a/chromium/components/cbor/cbor_values.cc
+++ b/chromium/components/cbor/cbor_values.cc
@@ -63,8 +63,9 @@ CBORValue::CBORValue(int64_t integer_value) : integer_value_(integer_value) {
type_ = integer_value >= 0 ? Type::UNSIGNED : Type::NEGATIVE;
}
-CBORValue::CBORValue(const BinaryValue& in_bytes)
- : type_(Type::BYTE_STRING), bytestring_value_(in_bytes) {}
+CBORValue::CBORValue(base::span<const uint8_t> in_bytes)
+ : type_(Type::BYTE_STRING),
+ bytestring_value_(in_bytes.begin(), in_bytes.end()) {}
CBORValue::CBORValue(BinaryValue&& in_bytes) noexcept
: type_(Type::BYTE_STRING), bytestring_value_(std::move(in_bytes)) {}
diff --git a/chromium/components/cbor/cbor_values.h b/chromium/components/cbor/cbor_values.h
index 6003054c488..e035faee48d 100644
--- a/chromium/components/cbor/cbor_values.h
+++ b/chromium/components/cbor/cbor_values.h
@@ -12,6 +12,7 @@
#include <vector>
#include "base/containers/flat_map.h"
+#include "base/containers/span.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
#include "components/cbor/cbor_export.h"
@@ -113,7 +114,7 @@ class CBOR_EXPORT CBORValue {
explicit CBORValue(int64_t integer_value);
explicit CBORValue(uint64_t integer_value) = delete;
- explicit CBORValue(const BinaryValue& in_bytes);
+ explicit CBORValue(base::span<const uint8_t> in_bytes);
explicit CBORValue(BinaryValue&& in_bytes) noexcept;
explicit CBORValue(const char* in_string, Type type = Type::STRING);
diff --git a/chromium/components/cdm/browser/media_drm_storage_impl.cc b/chromium/components/cdm/browser/media_drm_storage_impl.cc
index 0360962c013..b1caa86a1f3 100644
--- a/chromium/components/cdm/browser/media_drm_storage_impl.cc
+++ b/chromium/components/cdm/browser/media_drm_storage_impl.cc
@@ -32,6 +32,12 @@
// },
// # more origin map...
// }
+//
+// "creation_time" is the UTC time when "origin_id" was generated. When
+// provisioning for this origin completes, "creation_time" is updated to the UTC
+// time at which that occured. Since the value is only used to optimize the
+// clear media licenses process, and in practice the difference between the two
+// values is small, it's OK to replace the generation time with completion time.
namespace cdm {
@@ -44,16 +50,32 @@ const char kKeySetId[] = "key_set_id";
const char kMimeType[] = "mime_type";
const char kOriginId[] = "origin_id";
+bool GetStringFromDict(const base::Value& dict,
+ const std::string& key,
+ std::string* value_out) {
+ DCHECK(dict.is_dict());
+ DCHECK(value_out);
+
+ const base::Value* value = dict.FindKeyOfType(key, base::Value::Type::STRING);
+ if (!value)
+ return false;
+
+ *value_out = value->GetString();
+ return true;
+}
+
// Extract base::Time from |dict| with key kCreationTime. Returns true if |dict|
// contains a valid time value.
-bool GetTimeFromDict(const base::DictionaryValue& dict, base::Time* time) {
+bool GetCreationTimeFromDict(const base::Value& dict, base::Time* time) {
+ DCHECK(dict.is_dict());
DCHECK(time);
- double time_double = 0.;
- if (!dict.GetDouble(kCreationTime, &time_double))
+ const base::Value* time_value =
+ dict.FindKeyOfType(kCreationTime, base::Value::Type::DOUBLE);
+ if (!time_value)
return false;
- base::Time time_maybe_null = base::Time::FromDoubleT(time_double);
+ base::Time time_maybe_null = base::Time::FromDoubleT(time_value->GetDouble());
if (time_maybe_null.is_null())
return false;
@@ -73,11 +95,12 @@ class OriginData {
base::Time provision_time() const { return provision_time_; }
- std::unique_ptr<base::DictionaryValue> ToDictValue() const {
- auto dict = std::make_unique<base::DictionaryValue>();
+ base::Value ToDictValue() const {
+ base::Value dict(base::Value::Type::DICTIONARY);
- dict->Set(kOriginId, base::CreateUnguessableTokenValue(origin_id_));
- dict->SetDouble(kCreationTime, provision_time_.ToDoubleT());
+ dict.SetKey(kOriginId, base::Value::FromUniquePtrValue(
+ base::CreateUnguessableTokenValue(origin_id_)));
+ dict.SetKey(kCreationTime, base::Value(provision_time_.ToDoubleT()));
return dict;
}
@@ -86,9 +109,12 @@ class OriginData {
// related to origin provision. Return nullptr if |origin_dict| has any
// corruption, e.g. format error, missing fields, invalid value.
static std::unique_ptr<OriginData> FromDictValue(
- const base::DictionaryValue& origin_dict) {
- const base::Value* origin_id_value = nullptr;
- if (!origin_dict.Get(kOriginId, &origin_id_value))
+ const base::Value& origin_dict) {
+ DCHECK(origin_dict.is_dict());
+
+ const base::Value* origin_id_value =
+ origin_dict.FindKeyOfType(kOriginId, base::Value::Type::STRING);
+ if (!origin_id_value)
return nullptr;
base::UnguessableToken origin_id;
@@ -96,7 +122,7 @@ class OriginData {
return nullptr;
base::Time time;
- if (!GetTimeFromDict(origin_dict, &time))
+ if (!GetCreationTimeFromDict(origin_dict, &time))
return nullptr;
return base::WrapUnique(new OriginData(origin_id, time));
@@ -119,15 +145,15 @@ class SessionData {
base::Time creation_time() const { return creation_time_; }
- std::unique_ptr<base::DictionaryValue> ToDictValue() const {
- auto dict = std::make_unique<base::DictionaryValue>();
+ base::Value ToDictValue() const {
+ base::Value dict(base::Value::Type::DICTIONARY);
- dict->SetString(
- kKeySetId,
- std::string(reinterpret_cast<const char*>(key_set_id_.data()),
- key_set_id_.size()));
- dict->SetString(kMimeType, mime_type_);
- dict->SetDouble(kCreationTime, creation_time_.ToDoubleT());
+ dict.SetKey(kKeySetId,
+ base::Value(std::string(
+ reinterpret_cast<const char*>(key_set_id_.data()),
+ key_set_id_.size())));
+ dict.SetKey(kMimeType, base::Value(mime_type_));
+ dict.SetKey(kCreationTime, base::Value(creation_time_.ToDoubleT()));
return dict;
}
@@ -140,17 +166,19 @@ class SessionData {
// for an offline license session. Return nullptr if |session_dict| has any
// corruption, e.g. format error, missing fields, invalid data.
static std::unique_ptr<SessionData> FromDictValue(
- const base::DictionaryValue& session_dict) {
+ const base::Value& session_dict) {
+ DCHECK(session_dict.is_dict());
+
std::string key_set_id_string;
- if (!session_dict.GetString(kKeySetId, &key_set_id_string))
+ if (!GetStringFromDict(session_dict, kKeySetId, &key_set_id_string))
return nullptr;
std::string mime_type;
- if (!session_dict.GetString(kMimeType, &mime_type))
+ if (!GetStringFromDict(session_dict, kMimeType, &mime_type))
return nullptr;
base::Time time;
- if (!GetTimeFromDict(session_dict, &time))
+ if (!GetCreationTimeFromDict(session_dict, &time))
return nullptr;
return base::WrapUnique(
@@ -182,16 +210,18 @@ DictValue* GetSessionsDictFromStorageDict(DictValue* storage_dict,
return nullptr;
}
- DictValue* origin_dict = nullptr;
- // The origin string may contain dots. Do not use path expansion.
- storage_dict->GetDictionaryWithoutPathExpansion(origin_string, &origin_dict);
+ DCHECK(storage_dict->is_dict());
+
+ DictValue* origin_dict =
+ storage_dict->FindKeyOfType(origin_string, base::Value::Type::DICTIONARY);
if (!origin_dict) {
DVLOG(1) << __func__ << ": No entry for origin " << origin_string;
return nullptr;
}
- DictValue* sessions_dict = nullptr;
- if (!origin_dict->GetDictionary(kSessions, &sessions_dict)) {
+ DictValue* sessions_dict =
+ origin_dict->FindKeyOfType(kSessions, base::Value::Type::DICTIONARY);
+ if (!sessions_dict) {
DVLOG(1) << __func__ << ": No sessions entry for origin " << origin_string;
return nullptr;
}
@@ -199,33 +229,34 @@ DictValue* GetSessionsDictFromStorageDict(DictValue* storage_dict,
return sessions_dict;
}
-// Create origin dict with empty sessions dict. It returns the sessions dict for
-// caller to write session information.
+// Create origin dict with |origin_id|, current time as creation time, and empty
+// sessions dict. It returns the sessions dict for caller to write session
+// information. Note that this clears any existing session information.
base::Value* CreateOriginDictAndReturnSessionsDict(
base::Value* storage_dict,
const std::string& origin,
const base::UnguessableToken& origin_id) {
DCHECK(storage_dict);
+ DCHECK(storage_dict->is_dict());
- // TODO(yucliu): Change to base::Value::SetKey.
- return storage_dict
- ->SetKey(origin, base::Value::FromUniquePtrValue(
- OriginData(origin_id).ToDictValue()))
+ return storage_dict->SetKey(origin, OriginData(origin_id).ToDictValue())
->SetKey(kSessions, base::Value(base::Value::Type::DICTIONARY));
}
// Clear sessions whose creation time falls in [start, end] from
// |sessions_dict|. This function also cleans corruption data and should never
// fail.
-void ClearSessionDataForTimePeriod(base::DictionaryValue* sessions_dict,
+void ClearSessionDataForTimePeriod(base::Value* sessions_dict,
base::Time start,
base::Time end) {
+ DCHECK(sessions_dict->is_dict());
+
std::vector<std::string> sessions_to_clear;
- for (const auto& key_value : *sessions_dict) {
+ for (const auto& key_value : sessions_dict->DictItems()) {
const std::string& session_id = key_value.first;
- base::DictionaryValue* session_dict;
- if (!key_value.second->GetAsDictionary(&session_dict)) {
+ base::Value* session_dict = &key_value.second;
+ if (!session_dict->is_dict()) {
DLOG(WARNING) << "Session dict for " << session_id
<< " is corrupted, removing.";
sessions_to_clear.push_back(session_id);
@@ -250,7 +281,7 @@ void ClearSessionDataForTimePeriod(base::DictionaryValue* sessions_dict,
// Remove session data.
for (const auto& session_id : sessions_to_clear)
- sessions_dict->RemoveWithoutPathExpansion(session_id, nullptr);
+ sessions_dict->RemoveKey(session_id);
}
// 1. Removes the session data from origin dict if the session's creation time
@@ -265,14 +296,14 @@ std::vector<base::UnguessableToken> ClearMatchingLicenseData(
std::vector<std::string> origins_to_delete;
std::vector<base::UnguessableToken> origin_ids_to_unprovision;
- for (const auto& key_value : *storage_dict) {
+ for (const auto& key_value : storage_dict->DictItems()) {
const std::string& origin_str = key_value.first;
if (filter && !filter.Run(GURL(origin_str)))
continue;
- base::DictionaryValue* origin_dict;
- if (!key_value.second->GetAsDictionary(&origin_dict)) {
+ base::Value* origin_dict = &key_value.second;
+ if (!origin_dict->is_dict()) {
DLOG(WARNING) << "Origin dict for " << origin_str
<< " is corrupted, removing.";
origins_to_delete.push_back(origin_str);
@@ -291,8 +322,9 @@ std::vector<base::UnguessableToken> ClearMatchingLicenseData(
if (origin_data->provision_time() > end)
continue;
- base::DictionaryValue* sessions;
- if (!origin_dict->GetDictionary(kSessions, &sessions)) {
+ base::Value* sessions =
+ origin_dict->FindKeyOfType(kSessions, base::Value::Type::DICTIONARY);
+ if (!sessions) {
// The origin is provisioned, but no persistent license is installed.
origins_to_delete.push_back(origin_str);
origin_ids_to_unprovision.push_back(origin_data->origin_id());
@@ -301,7 +333,7 @@ std::vector<base::UnguessableToken> ClearMatchingLicenseData(
ClearSessionDataForTimePeriod(sessions, start, end);
- if (sessions->empty()) {
+ if (sessions->DictEmpty()) {
// Session data will be removed when removing origin data.
origins_to_delete.push_back(origin_str);
origin_ids_to_unprovision.push_back(origin_data->origin_id());
@@ -310,7 +342,7 @@ std::vector<base::UnguessableToken> ClearMatchingLicenseData(
// Remove origin data.
for (const auto& origin_str : origins_to_delete)
- storage_dict->RemoveWithoutPathExpansion(origin_str, nullptr);
+ storage_dict->RemoveKey(origin_str);
return origin_ids_to_unprovision;
}
@@ -377,20 +409,21 @@ MediaDrmStorageImpl::~MediaDrmStorageImpl() {
void MediaDrmStorageImpl::Initialize(InitializeCallback callback) {
DVLOG(1) << __func__ << ": origin = " << origin();
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(!origin_id_);
- const base::DictionaryValue* storage_dict =
- pref_service_->GetDictionary(kMediaDrmStorage);
+ if (IsInitialized()) {
+ std::move(callback).Run(origin_id_);
+ return;
+ }
+
+ DictionaryPrefUpdate update(pref_service_, kMediaDrmStorage);
+ base::DictionaryValue* storage_dict = update.Get();
+ DCHECK(storage_dict);
- const base::DictionaryValue* origin_dict = nullptr;
- // The origin string may contain dots. Do not use path expansion.
- bool exist = storage_dict && storage_dict->GetDictionaryWithoutPathExpansion(
- origin().Serialize(), &origin_dict);
+ const base::Value* origin_dict = storage_dict->FindKeyOfType(
+ origin().Serialize(), base::Value::Type::DICTIONARY);
base::UnguessableToken origin_id;
- if (exist) {
- DCHECK(origin_dict);
-
+ if (origin_dict) {
std::unique_ptr<OriginData> origin_data =
OriginData::FromDictValue(*origin_dict);
if (origin_data)
@@ -398,14 +431,22 @@ void MediaDrmStorageImpl::Initialize(InitializeCallback callback) {
}
// |origin_id| can be empty even if |origin_dict| exists. This can happen if
- // |origin_dict| is created with an old version app.
- if (origin_id.is_empty())
+ // |origin_dict| was created with an old version of Chrome. When this happens,
+ // there's no origin ID. Generate a new one and store it in the storage.
+ if (origin_id.is_empty()) {
origin_id = base::UnguessableToken::Create();
+ // Persist the origin ID into storage now. If multiple MediaDrmStorage
+ // instances from the same web origin call Initialize concurrently, they can
+ // get the same origin ID.
+ CreateOriginDictAndReturnSessionsDict(storage_dict, origin().Serialize(),
+ origin_id);
+ }
+
origin_id_ = origin_id;
- DCHECK(origin_id);
- std::move(callback).Run(origin_id);
+ DCHECK(origin_id_);
+ std::move(callback).Run(origin_id_);
}
void MediaDrmStorageImpl::OnProvisioned(OnProvisionedCallback callback) {
@@ -422,11 +463,9 @@ void MediaDrmStorageImpl::OnProvisioned(OnProvisionedCallback callback) {
base::DictionaryValue* storage_dict = update.Get();
DCHECK(storage_dict);
- // The origin string may contain dots. Do not use path expansion.
- DVLOG_IF(1, storage_dict->FindKey(origin().Serialize()))
- << __func__ << ": Entry for origin " << origin()
- << " already exists and will be cleared";
-
+ // Update origin dict once origin provisioning completes. There may be
+ // orphaned session info from a previous provisioning. Clear them by
+ // recreating the dicts.
CreateOriginDictAndReturnSessionsDict(storage_dict, origin().Serialize(),
origin_id_);
std::move(callback).Run(true);
@@ -449,9 +488,8 @@ void MediaDrmStorageImpl::SavePersistentSession(
base::DictionaryValue* storage_dict = update.Get();
DCHECK(storage_dict);
- base::Value* sessions_dict =
- GetSessionsDictFromStorageDict<base::DictionaryValue>(
- storage_dict, origin().Serialize());
+ base::Value* sessions_dict = GetSessionsDictFromStorageDict<base::Value>(
+ storage_dict, origin().Serialize());
// This could happen if the profile is removed, but the device is still
// provisioned for the origin. In this case, just create a new entry.
@@ -467,10 +505,9 @@ void MediaDrmStorageImpl::SavePersistentSession(
DVLOG_IF(1, sessions_dict->FindKey(session_id))
<< __func__ << ": Session ID already exists and will be replaced.";
- sessions_dict->SetKey(session_id, base::Value::FromUniquePtrValue(
- SessionData(session_data->key_set_id,
- session_data->mime_type)
- .ToDictValue()));
+ sessions_dict->SetKey(
+ session_id, SessionData(session_data->key_set_id, session_data->mime_type)
+ .ToDictValue());
std::move(callback).Run(true);
}
@@ -487,17 +524,17 @@ void MediaDrmStorageImpl::LoadPersistentSession(
return;
}
- const base::DictionaryValue* sessions_dict =
- GetSessionsDictFromStorageDict<const base::DictionaryValue>(
+ const base::Value* sessions_dict =
+ GetSessionsDictFromStorageDict<const base::Value>(
pref_service_->GetDictionary(kMediaDrmStorage), origin().Serialize());
if (!sessions_dict) {
std::move(callback).Run(nullptr);
return;
}
- const base::DictionaryValue* session_dict = nullptr;
- if (!sessions_dict->GetDictionaryWithoutPathExpansion(session_id,
- &session_dict)) {
+ const base::Value* session_dict =
+ sessions_dict->FindKeyOfType(session_id, base::Value::Type::DICTIONARY);
+ if (!session_dict) {
DVLOG(1) << __func__ << ": No session " << session_id << " for origin "
<< origin();
std::move(callback).Run(nullptr);
@@ -529,16 +566,15 @@ void MediaDrmStorageImpl::RemovePersistentSession(
DictionaryPrefUpdate update(pref_service_, kMediaDrmStorage);
- base::DictionaryValue* sessions_dict =
- GetSessionsDictFromStorageDict<base::DictionaryValue>(
- update.Get(), origin().Serialize());
+ base::Value* sessions_dict = GetSessionsDictFromStorageDict<base::Value>(
+ update.Get(), origin().Serialize());
if (!sessions_dict) {
std::move(callback).Run(true);
return;
}
- sessions_dict->RemoveWithoutPathExpansion(session_id, nullptr);
+ sessions_dict->RemoveKey(session_id);
std::move(callback).Run(true);
}
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 1dc150a7a75..31826f318bf 100644
--- a/chromium/components/cdm/browser/media_drm_storage_impl_unittest.cc
+++ b/chromium/components/cdm/browser/media_drm_storage_impl_unittest.cc
@@ -25,13 +25,13 @@ namespace {
const char kMediaDrmStorage[] = "media.media_drm_storage";
const char kTestOrigin[] = "https://www.testorigin.com:80";
+const char kTestOrigin2[] = "https://www.testorigin2.com:80";
-content::RenderFrameHost* SimulateNavigation(content::RenderFrameHost* rfh,
- const GURL& url) {
- auto navigation_simulator =
- content::NavigationSimulator::CreateRendererInitiated(url, rfh);
- navigation_simulator->Commit();
- return navigation_simulator->GetFinalRenderFrameHost();
+void OnMediaDrmStorageInit(base::UnguessableToken* out_origin_id,
+ const base::UnguessableToken& origin_id) {
+ DCHECK(out_origin_id);
+ DCHECK(origin_id);
+ *out_origin_id = origin_id;
}
} // namespace
@@ -47,7 +47,8 @@ class MediaDrmStorageImplTest : public content::RenderViewHostTestHarness {
PrefRegistrySimple* registry = pref_service_->registry();
MediaDrmStorageImpl::RegisterProfilePrefs(registry);
- media_drm_storage_ = CreateAndInitMediaDrmStorage(&origin_id_);
+ media_drm_storage_ =
+ CreateAndInitMediaDrmStorage(GURL(kTestOrigin), &origin_id_);
}
void TearDown() override {
@@ -58,37 +59,52 @@ class MediaDrmStorageImplTest : public content::RenderViewHostTestHarness {
protected:
using SessionData = media::MediaDrmStorage::SessionData;
- std::unique_ptr<media::MediaDrmStorage> CreateAndInitMediaDrmStorage(
- base::UnguessableToken* origin_id) {
- DCHECK(origin_id);
-
+ std::unique_ptr<media::MediaDrmStorage> CreateMediaDrmStorage(
+ content::RenderFrameHost* rfh) {
media::mojom::MediaDrmStoragePtr media_drm_storage_ptr;
auto request = mojo::MakeRequest(&media_drm_storage_ptr);
auto media_drm_storage = std::make_unique<media::MojoMediaDrmStorage>(
std::move(media_drm_storage_ptr));
- content::RenderFrameHost* rfh = web_contents()->GetMainFrame();
- content::RenderFrameHostTester::For(rfh)->InitializeRenderFrameIfNeeded();
- rfh = SimulateNavigation(rfh, GURL(kTestOrigin));
-
// The created object will be destroyed on connection error.
new MediaDrmStorageImpl(rfh, pref_service_.get(), std::move(request));
- media_drm_storage->Initialize(base::BindOnce(
- [](base::UnguessableToken* out_origin_id,
- const base::UnguessableToken& origin_id) {
- DCHECK(origin_id);
- *out_origin_id = origin_id;
- },
- origin_id));
+ return std::move(media_drm_storage);
+ }
+
+ std::unique_ptr<media::MediaDrmStorage> CreateAndInitMediaDrmStorage(
+ const GURL& origin,
+ base::UnguessableToken* origin_id) {
+ DCHECK(origin_id);
+
+ std::unique_ptr<media::MediaDrmStorage> media_drm_storage =
+ CreateMediaDrmStorage(SimulateNavigation(origin));
+
+ media_drm_storage->Initialize(
+ base::BindOnce(OnMediaDrmStorageInit, origin_id));
base::RunLoop().RunUntilIdle();
+ // Verify the origin dictionary is created.
+ const base::DictionaryValue* storage_dict =
+ pref_service_->GetDictionary(kMediaDrmStorage);
+ EXPECT_TRUE(storage_dict->FindKey(kTestOrigin));
+
DCHECK(*origin_id);
return media_drm_storage;
}
+ content::RenderFrameHost* SimulateNavigation(const GURL& url) {
+ content::RenderFrameHost* rfh = web_contents()->GetMainFrame();
+ content::RenderFrameHostTester::For(rfh)->InitializeRenderFrameIfNeeded();
+
+ auto navigation_simulator =
+ content::NavigationSimulator::CreateRendererInitiated(url, rfh);
+ navigation_simulator->Commit();
+ return navigation_simulator->GetFinalRenderFrameHost();
+ }
+
void OnProvisioned() {
media_drm_storage_->OnProvisioned(ExpectResult(true));
}
@@ -159,15 +175,49 @@ class MediaDrmStorageImplTest : public content::RenderViewHostTestHarness {
base::UnguessableToken origin_id_;
};
+// MediaDrmStorageImpl should write origin ID to persistent storage when
+// Initialize is called. Later call to Initialize should return the same origin
+// ID. The second MediaDrmStorage won't call Initialize until the first one is
+// fully initialized.
// TODO(yucliu): Test origin ID is re-generated after clearing licenses.
TEST_F(MediaDrmStorageImplTest, Initialize_OriginIdNotChanged) {
- OnProvisioned();
- base::RunLoop().RunUntilIdle();
+ base::UnguessableToken original_origin_id = origin_id_;
+ ASSERT_TRUE(original_origin_id);
base::UnguessableToken origin_id;
std::unique_ptr<media::MediaDrmStorage> storage =
- CreateAndInitMediaDrmStorage(&origin_id);
- EXPECT_EQ(origin_id, origin_id_);
+ CreateAndInitMediaDrmStorage(GURL(kTestOrigin), &origin_id);
+ EXPECT_EQ(origin_id, original_origin_id);
+}
+
+// Two MediaDrmStorage call Initialize concurrently. The second MediaDrmStorage
+// will NOT wait for the first one to be initialized. Both instances should get
+// the same origin ID.
+TEST_F(MediaDrmStorageImplTest, Initialize_Concurrent) {
+ content::RenderFrameHost* rfh = SimulateNavigation(GURL(kTestOrigin2));
+
+ std::unique_ptr<media::MediaDrmStorage> storage1 = CreateMediaDrmStorage(rfh);
+ std::unique_ptr<media::MediaDrmStorage> storage2 = CreateMediaDrmStorage(rfh);
+
+ base::UnguessableToken origin_id_1;
+ storage1->Initialize(base::BindOnce(OnMediaDrmStorageInit, &origin_id_1));
+ base::UnguessableToken origin_id_2;
+ storage2->Initialize(base::BindOnce(OnMediaDrmStorageInit, &origin_id_2));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(origin_id_1, origin_id_2);
+}
+
+TEST_F(MediaDrmStorageImplTest, Initialize_DifferentOrigins) {
+ base::UnguessableToken origin_id_1 = origin_id_;
+ ASSERT_TRUE(origin_id_1);
+
+ base::UnguessableToken origin_id_2;
+ auto storage2 =
+ CreateAndInitMediaDrmStorage(GURL(kTestOrigin2), &origin_id_2);
+ ASSERT_TRUE(origin_id_2);
+
+ EXPECT_NE(origin_id_1, origin_id_2);
}
TEST_F(MediaDrmStorageImplTest, OnProvisioned) {
@@ -177,8 +227,7 @@ TEST_F(MediaDrmStorageImplTest, OnProvisioned) {
// Verify the origin dictionary is created.
const base::DictionaryValue* storage_dict =
pref_service_->GetDictionary(kMediaDrmStorage);
- EXPECT_TRUE(
- storage_dict->GetDictionaryWithoutPathExpansion(kTestOrigin, nullptr));
+ EXPECT_TRUE(storage_dict->FindKey(kTestOrigin));
}
TEST_F(MediaDrmStorageImplTest, OnProvisioned_Twice) {
diff --git a/chromium/components/cdm/renderer/android_key_systems.cc b/chromium/components/cdm/renderer/android_key_systems.cc
index 6eaa2ba963d..3afe736b59b 100644
--- a/chromium/components/cdm/renderer/android_key_systems.cc
+++ b/chromium/components/cdm/renderer/android_key_systems.cc
@@ -57,9 +57,11 @@ class AndroidPlatformKeySystemProperties : public KeySystemProperties {
return false;
}
- bool IsEncryptionSchemeSupported(
+ EmeConfigRule GetEncryptionSchemeConfigRule(
media::EncryptionMode encryption_scheme) const override {
- return encryption_scheme == media::EncryptionMode::kCenc;
+ return encryption_scheme == media::EncryptionMode::kCenc
+ ? EmeConfigRule::SUPPORTED
+ : EmeConfigRule::NOT_SUPPORTED;
}
SupportedCodecs GetSupportedCodecs() const override {
@@ -76,7 +78,7 @@ class AndroidPlatformKeySystemProperties : public KeySystemProperties {
EmeSessionTypeSupport GetPersistentLicenseSessionSupport() const override {
return EmeSessionTypeSupport::NOT_SUPPORTED;
}
- EmeSessionTypeSupport GetPersistentReleaseMessageSessionSupport()
+ EmeSessionTypeSupport GetPersistentUsageRecordSessionSupport()
const override {
return EmeSessionTypeSupport::NOT_SUPPORTED;
}
@@ -113,8 +115,12 @@ SupportedKeySystemResponse QueryKeySystemSupport(
void AddAndroidWidevine(
std::vector<std::unique_ptr<KeySystemProperties>>* concrete_key_systems) {
- SupportedKeySystemResponse response =
- QueryKeySystemSupport(kWidevineKeySystem);
+ auto response = QueryKeySystemSupport(kWidevineKeySystem);
+
+ auto codecs = response.non_secure_codecs;
+
+ // 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
@@ -125,28 +131,29 @@ void AddAndroidWidevine(
? EmeSessionTypeSupport::SUPPORTED_WITH_IDENTIFIER
: EmeSessionTypeSupport::NOT_SUPPORTED;
- if (response.non_secure_codecs != media::EME_CODEC_NONE) {
+ if (codecs != media::EME_CODEC_NONE) {
DVLOG(3) << __func__ << " Widevine supported.";
// TODO(crbug.com/813845): Determine 'cbcs' support, which may vary by
// Android version.
- base::flat_set<media::EncryptionMode> supported_encryption_schemes = {
+ base::flat_set<media::EncryptionMode> encryption_schemes = {
media::EncryptionMode::kCenc};
concrete_key_systems->emplace_back(new WidevineKeySystemProperties(
- supported_encryption_schemes, // Encryption schemes.
- response.non_secure_codecs, // Regular codecs.
- response.secure_codecs, // Hardware-secure codecs.
- Robustness::HW_SECURE_CRYPTO, // Max audio robustness.
- Robustness::HW_SECURE_ALL, // Max video robustness.
- persistent_license_support, // persistent-license.
+ 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.
EmeSessionTypeSupport::NOT_SUPPORTED, // persistent-release-message.
EmeFeatureSupport::ALWAYS_ENABLED, // Persistent state.
EmeFeatureSupport::ALWAYS_ENABLED)); // Distinctive identifier.
} else {
- // It doesn't make sense to support secure codecs but not regular codecs.
+ // It doesn't make sense to support hw secure codecs but not regular codecs.
DVLOG(3) << __func__ << " Widevine NOT supported.";
- DCHECK(response.secure_codecs == media::EME_CODEC_NONE);
+ DCHECK(hw_secure_codecs == media::EME_CODEC_NONE);
}
}
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 828d628aceb..2101a4ad5b4 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
@@ -34,17 +34,17 @@ bool ExternalClearKeyProperties::IsSupportedInitDataType(
return false;
}
-bool ExternalClearKeyProperties::IsEncryptionSchemeSupported(
+media::EmeConfigRule ExternalClearKeyProperties::GetEncryptionSchemeConfigRule(
media::EncryptionMode encryption_scheme) const {
switch (encryption_scheme) {
case media::EncryptionMode::kCenc:
case media::EncryptionMode::kCbcs:
- return true;
+ return media::EmeConfigRule::SUPPORTED;
case media::EncryptionMode::kUnencrypted:
break;
}
NOTREACHED();
- return false;
+ return media::EmeConfigRule::NOT_SUPPORTED;
}
media::SupportedCodecs ExternalClearKeyProperties::GetSupportedCodecs() const {
@@ -65,7 +65,7 @@ ExternalClearKeyProperties::GetPersistentLicenseSessionSupport() const {
}
media::EmeSessionTypeSupport
-ExternalClearKeyProperties::GetPersistentReleaseMessageSessionSupport() const {
+ExternalClearKeyProperties::GetPersistentUsageRecordSessionSupport() const {
return media::EmeSessionTypeSupport::NOT_SUPPORTED;
}
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 224f22d4f6e..6fe015f97f7 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
@@ -22,7 +22,7 @@ class ExternalClearKeyProperties : public media::KeySystemProperties {
std::string GetKeySystemName() const override;
bool IsSupportedInitDataType(
media::EmeInitDataType init_data_type) const override;
- bool IsEncryptionSchemeSupported(
+ media::EmeConfigRule GetEncryptionSchemeConfigRule(
media::EncryptionMode encryption_scheme) const override;
media::SupportedCodecs GetSupportedCodecs() const override;
media::EmeConfigRule GetRobustnessConfigRule(
@@ -30,7 +30,7 @@ class ExternalClearKeyProperties : public media::KeySystemProperties {
const std::string& requested_robustness) const override;
media::EmeSessionTypeSupport GetPersistentLicenseSessionSupport()
const override;
- media::EmeSessionTypeSupport GetPersistentReleaseMessageSessionSupport()
+ media::EmeSessionTypeSupport GetPersistentUsageRecordSessionSupport()
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 60f4bb07d9c..df22038d07f 100644
--- a/chromium/components/cdm/renderer/widevine_key_system_properties.cc
+++ b/chromium/components/cdm/renderer/widevine_key_system_properties.cc
@@ -38,29 +38,26 @@ Robustness ConvertRobustness(const std::string& robustness) {
} // namespace
WidevineKeySystemProperties::WidevineKeySystemProperties(
- base::flat_set<media::EncryptionMode> supported_encryption_schemes,
- media::SupportedCodecs supported_codecs,
-#if defined(OS_ANDROID)
- media::SupportedCodecs supported_secure_codecs,
-#endif // defined(OS_ANDROID)
+ media::SupportedCodecs codecs,
+ base::flat_set<media::EncryptionMode> encryption_schemes,
+ media::SupportedCodecs hw_secure_codecs,
+ base::flat_set<media::EncryptionMode> hw_secure_encryption_schemes,
Robustness max_audio_robustness,
Robustness max_video_robustness,
media::EmeSessionTypeSupport persistent_license_support,
media::EmeSessionTypeSupport persistent_release_message_support,
media::EmeFeatureSupport persistent_state_support,
media::EmeFeatureSupport distinctive_identifier_support)
- : supported_encryption_schemes_(std::move(supported_encryption_schemes)),
- supported_codecs_(supported_codecs),
-#if defined(OS_ANDROID)
- supported_secure_codecs_(supported_secure_codecs),
-#endif // defined(OS_ANDROID)
+ : codecs_(codecs),
+ encryption_schemes_(std::move(encryption_schemes)),
+ hw_secure_codecs_(hw_secure_codecs),
+ hw_secure_encryption_schemes_(std::move(hw_secure_encryption_schemes)),
max_audio_robustness_(max_audio_robustness),
max_video_robustness_(max_video_robustness),
persistent_license_support_(persistent_license_support),
persistent_release_message_support_(persistent_release_message_support),
persistent_state_support_(persistent_state_support),
- distinctive_identifier_support_(distinctive_identifier_support) {
-}
+ distinctive_identifier_support_(distinctive_identifier_support) {}
WidevineKeySystemProperties::~WidevineKeySystemProperties() = default;
@@ -74,27 +71,37 @@ bool WidevineKeySystemProperties::IsSupportedInitDataType(
// associated initialization data type. KeySystems handles validating
// |init_data_type| x |container| pairings.
if (init_data_type == EmeInitDataType::WEBM)
- return (supported_codecs_ & media::EME_CODEC_WEBM_ALL) != 0;
+ return (codecs_ & media::EME_CODEC_WEBM_ALL) != 0;
if (init_data_type == EmeInitDataType::CENC)
- return (supported_codecs_ & media::EME_CODEC_MP4_ALL) != 0;
+ return (codecs_ & media::EME_CODEC_MP4_ALL) != 0;
return false;
}
-bool WidevineKeySystemProperties::IsEncryptionSchemeSupported(
+EmeConfigRule WidevineKeySystemProperties::GetEncryptionSchemeConfigRule(
media::EncryptionMode encryption_scheme) const {
- return supported_encryption_schemes_.count(encryption_scheme) != 0;
+ bool is_supported = encryption_schemes_.count(encryption_scheme);
+ bool is_hw_secure_supported =
+ hw_secure_encryption_schemes_.count(encryption_scheme);
+
+ 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;
}
SupportedCodecs WidevineKeySystemProperties::GetSupportedCodecs() const {
- return supported_codecs_;
+ return codecs_;
}
-#if defined(OS_ANDROID)
-SupportedCodecs WidevineKeySystemProperties::GetSupportedSecureCodecs() const {
- return supported_secure_codecs_;
+SupportedCodecs WidevineKeySystemProperties::GetSupportedHwSecureCodecs()
+ const {
+ return hw_secure_codecs_;
}
-#endif
EmeConfigRule WidevineKeySystemProperties::GetRobustnessConfigRule(
EmeMediaType media_type,
@@ -139,12 +146,19 @@ EmeConfigRule WidevineKeySystemProperties::GetRobustnessConfigRule(
return EmeConfigRule::IDENTIFIER_RECOMMENDED;
}
#elif defined(OS_ANDROID)
- // Require hardware secure codecs when SW_SECURE_DECODE or above is specified.
+ // On Android, require hardware secure codecs for SW_SECURE_DECODE and above.
if (robustness >= Robustness::SW_SECURE_DECODE) {
return EmeConfigRule::HW_SECURE_CODECS_REQUIRED;
}
+#else
+ // On Linux/Mac/Win, require hardware secure codecs for HW_SECURE_CRYPTO and
+ // above.
+ if (robustness >= Robustness::HW_SECURE_CRYPTO) {
+ return EmeConfigRule::HW_SECURE_CODECS_REQUIRED;
+ }
#endif // defined(OS_CHROMEOS)
+ // TODO(crbug.com/848532): Handle HW_SECURE* levels for Windows.
return EmeConfigRule::SUPPORTED;
}
@@ -154,7 +168,7 @@ WidevineKeySystemProperties::GetPersistentLicenseSessionSupport() const {
}
EmeSessionTypeSupport
-WidevineKeySystemProperties::GetPersistentReleaseMessageSessionSupport() const {
+WidevineKeySystemProperties::GetPersistentUsageRecordSessionSupport() const {
return persistent_release_message_support_;
}
diff --git a/chromium/components/cdm/renderer/widevine_key_system_properties.h b/chromium/components/cdm/renderer/widevine_key_system_properties.h
index 05b88a659d0..fa5163896b8 100644
--- a/chromium/components/cdm/renderer/widevine_key_system_properties.h
+++ b/chromium/components/cdm/renderer/widevine_key_system_properties.h
@@ -9,7 +9,6 @@
#include <vector>
#include "base/containers/flat_set.h"
-#include "build/build_config.h"
#include "media/base/key_system_properties.h"
namespace cdm {
@@ -30,11 +29,10 @@ class WidevineKeySystemProperties : public media::KeySystemProperties {
};
WidevineKeySystemProperties(
- base::flat_set<media::EncryptionMode> supported_encryption_schemes,
- media::SupportedCodecs supported_codecs,
-#if defined(OS_ANDROID)
- media::SupportedCodecs supported_secure_codecs,
-#endif // defined(OS_ANDROID)
+ media::SupportedCodecs codecs,
+ base::flat_set<media::EncryptionMode> encryption_schemes,
+ media::SupportedCodecs hw_secure_codecs,
+ base::flat_set<media::EncryptionMode> hw_secure_encryption_schemes,
Robustness max_audio_robustness,
Robustness max_video_robustness,
media::EmeSessionTypeSupport persistent_license_support,
@@ -46,30 +44,25 @@ class WidevineKeySystemProperties : public media::KeySystemProperties {
std::string GetKeySystemName() const override;
bool IsSupportedInitDataType(
media::EmeInitDataType init_data_type) const override;
- bool IsEncryptionSchemeSupported(
+ media::EmeConfigRule GetEncryptionSchemeConfigRule(
media::EncryptionMode encryption_scheme) const override;
-
media::SupportedCodecs GetSupportedCodecs() const override;
-#if defined(OS_ANDROID)
- media::SupportedCodecs GetSupportedSecureCodecs() const override;
-#endif
-
+ media::SupportedCodecs GetSupportedHwSecureCodecs() const override;
media::EmeConfigRule GetRobustnessConfigRule(
media::EmeMediaType media_type,
const std::string& requested_robustness) const override;
media::EmeSessionTypeSupport GetPersistentLicenseSessionSupport()
const override;
- media::EmeSessionTypeSupport GetPersistentReleaseMessageSessionSupport()
+ media::EmeSessionTypeSupport GetPersistentUsageRecordSessionSupport()
const override;
media::EmeFeatureSupport GetPersistentStateSupport() const override;
media::EmeFeatureSupport GetDistinctiveIdentifierSupport() const override;
private:
- const base::flat_set<media::EncryptionMode> supported_encryption_schemes_;
- const media::SupportedCodecs supported_codecs_;
-#if defined(OS_ANDROID)
- const media::SupportedCodecs supported_secure_codecs_;
-#endif // defined(OS_ANDROID)
+ const media::SupportedCodecs codecs_;
+ const base::flat_set<media::EncryptionMode> encryption_schemes_;
+ const media::SupportedCodecs hw_secure_codecs_;
+ const base::flat_set<media::EncryptionMode> hw_secure_encryption_schemes_;
const Robustness max_audio_robustness_;
const Robustness max_video_robustness_;
const media::EmeSessionTypeSupport persistent_license_support_;
diff --git a/chromium/components/certificate_transparency/chrome_require_ct_delegate.cc b/chromium/components/certificate_transparency/chrome_require_ct_delegate.cc
index c75aec77af0..f5361dc09a4 100644
--- a/chromium/components/certificate_transparency/chrome_require_ct_delegate.cc
+++ b/chromium/components/certificate_transparency/chrome_require_ct_delegate.cc
@@ -17,6 +17,7 @@
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner.h"
+#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/values.h"
@@ -110,7 +111,7 @@ bool ParseOrganizationBoundName(net::der::Input dn_without_sequence,
bool AreCertsSameOrganization(const net::RDNSequence& leaf_rdn_sequence,
CRYPTO_BUFFER* org_cert) {
scoped_refptr<net::ParsedCertificate> parsed_org =
- net::ParsedCertificate::Create(net::x509_util::DupCryptoBuffer(org_cert),
+ net::ParsedCertificate::Create(bssl::UpRef(org_cert),
net::ParseCertificateOptions(), nullptr);
if (!parsed_org)
return false;
@@ -295,7 +296,7 @@ bool ChromeRequireCTDelegate::MatchSPKI(const net::X509Certificate* chain,
// the organization information to itself.
net::HashValue hash;
if (net::x509_util::CalculateSha256SpkiHash(leaf_cert, &hash) &&
- std::find(matches.begin(), matches.end(), hash) != matches.end()) {
+ base::ContainsValue(matches, hash)) {
*ct_required = false;
return true;
}
@@ -305,7 +306,7 @@ bool ChromeRequireCTDelegate::MatchSPKI(const net::X509Certificate* chain,
std::vector<CRYPTO_BUFFER*> candidates;
for (const auto& buffer : chain->intermediate_buffers()) {
if (net::x509_util::CalculateSha256SpkiHash(buffer.get(), &hash) &&
- std::find(matches.begin(), matches.end(), hash) != matches.end()) {
+ base::ContainsValue(matches, hash)) {
candidates.push_back(buffer.get());
}
}
@@ -314,7 +315,7 @@ bool ChromeRequireCTDelegate::MatchSPKI(const net::X509Certificate* chain,
return false;
scoped_refptr<net::ParsedCertificate> parsed_leaf =
- net::ParsedCertificate::Create(net::x509_util::DupCryptoBuffer(leaf_cert),
+ net::ParsedCertificate::Create(bssl::UpRef(leaf_cert),
net::ParseCertificateOptions(), nullptr);
if (!parsed_leaf)
return false;
diff --git a/chromium/components/certificate_transparency/chrome_require_ct_delegate_unittest.cc b/chromium/components/certificate_transparency/chrome_require_ct_delegate_unittest.cc
index a9e16288e7b..474ca18c185 100644
--- a/chromium/components/certificate_transparency/chrome_require_ct_delegate_unittest.cc
+++ b/chromium/components/certificate_transparency/chrome_require_ct_delegate_unittest.cc
@@ -305,12 +305,10 @@ TEST_F(ChromeRequireCTDelegateTest, SupportsOrgRestrictions) {
hashes.push_back(std::move(intermediate_hash));
std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
- intermediates.push_back(
- net::x509_util::DupCryptoBuffer(intermediate->cert_buffer()));
+ intermediates.push_back(bssl::UpRef(intermediate->cert_buffer()));
leaf = net::X509Certificate::CreateFromBuffer(
- net::x509_util::DupCryptoBuffer(leaf->cert_buffer()),
- std::move(intermediates));
+ bssl::UpRef(leaf->cert_buffer()), std::move(intermediates));
}
delegate.UpdateCTPolicies({}, {}, {}, {});
diff --git a/chromium/components/certificate_transparency/single_tree_tracker.h b/chromium/components/certificate_transparency/single_tree_tracker.h
index c71985dbc1f..fa6b9d1e1f5 100644
--- a/chromium/components/certificate_transparency/single_tree_tracker.h
+++ b/chromium/components/certificate_transparency/single_tree_tracker.h
@@ -13,10 +13,8 @@
#include "base/memory/memory_pressure_monitor.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
-#include "components/certificate_transparency/sth_observer.h"
#include "net/base/hash_value.h"
#include "net/base/network_change_notifier.h"
-#include "net/cert/ct_verifier.h"
#include "net/cert/signed_tree_head.h"
#include "net/log/net_log_with_source.h"
@@ -97,11 +95,10 @@ enum SCTCanBeCheckedForInclusion {
// SCTs, the SCTs (and their corresponding entries) are present in the log.
//
// To accomplish this, this class needs to be notified of when new SCTs are
-// observed (which it does by implementing net::CTVerifier::Observer) and when
-// new STHs are observed (which it does by implementing STHObserver).
-// Once connected to sources providing that data, the status for a given SCT
-// can be queried by calling GetLogEntryInclusionCheck.
-class SingleTreeTracker : public net::CTVerifier::Observer, public STHObserver {
+// observed and when new STHs are observed. Once both SCTs and at least one
+// STH have been provided, the status for a given SCT can be queried by
+// calling GetLogEntryInclusionCheck.
+class SingleTreeTracker {
public:
enum SCTInclusionStatus {
// SCT was not observed by this class and is not currently pending
@@ -132,10 +129,8 @@ class SingleTreeTracker : public net::CTVerifier::Observer, public STHObserver {
LogDnsClient* dns_client,
net::HostResolver* host_resolver,
net::NetLog* net_log);
- ~SingleTreeTracker() override;
+ ~SingleTreeTracker();
- // net::ct::CTVerifier::Observer implementation.
- // TODO(eranm): Extract CTVerifier::Observer to SCTObserver
// Enqueues |sct| for later inclusion checking of the given |cert|, so long as
// both of the following are true:
// a) The latest STH known for this log is older than |sct.timestamp| +
@@ -151,13 +146,12 @@ class SingleTreeTracker : public net::CTVerifier::Observer, public STHObserver {
// here as this callback is invoked during certificate validation.
void OnSCTVerified(base::StringPiece hostname,
net::X509Certificate* cert,
- const net::ct::SignedCertificateTimestamp* sct) override;
+ const net::ct::SignedCertificateTimestamp* sct);
- // STHObserver implementation.
// After verification of the signature over the |sth|, uses this
// STH for future inclusion checks.
// Must only be called for STHs issued by the log this instance tracks.
- void NewSTHObserved(const net::ct::SignedTreeHead& sth) override;
+ void NewSTHObserved(const net::ct::SignedTreeHead& sth);
// Returns the status of a given log entry that is assembled from
// |cert| and |sct|. If |cert| and |sct| were not previously observed,
diff --git a/chromium/components/certificate_transparency/single_tree_tracker_unittest.cc b/chromium/components/certificate_transparency/single_tree_tracker_unittest.cc
index a84a78d1ce3..3f25923f5f8 100644
--- a/chromium/components/certificate_transparency/single_tree_tracker_unittest.cc
+++ b/chromium/components/certificate_transparency/single_tree_tracker_unittest.cc
@@ -14,7 +14,7 @@
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/base32/base32.h"
#include "components/certificate_transparency/log_dns_client.h"
#include "components/certificate_transparency/mock_log_dns_traffic.h"
diff --git a/chromium/components/certificate_transparency/sth_distributor_unittest.cc b/chromium/components/certificate_transparency/sth_distributor_unittest.cc
index 6e59cf761f8..d4ade2e5bb1 100644
--- a/chromium/components/certificate_transparency/sth_distributor_unittest.cc
+++ b/chromium/components/certificate_transparency/sth_distributor_unittest.cc
@@ -7,7 +7,7 @@
#include <map>
#include <string>
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/certificate_transparency/sth_observer.h"
#include "crypto/sha2.h"
#include "net/cert/signed_tree_head.h"
diff --git a/chromium/components/certificate_transparency/tree_state_tracker.cc b/chromium/components/certificate_transparency/tree_state_tracker.cc
index 7d29f495f26..288f9451b24 100644
--- a/chromium/components/certificate_transparency/tree_state_tracker.cc
+++ b/chromium/components/certificate_transparency/tree_state_tracker.cc
@@ -17,7 +17,6 @@
#include "net/cert/signed_tree_head.h"
#include "net/cert/x509_certificate.h"
#include "net/dns/dns_client.h"
-#include "net/dns/dns_config_service.h"
#include "net/log/net_log.h"
using net::X509Certificate;
diff --git a/chromium/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom b/chromium/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom
index bd4cf278802..5677ea2230b 100644
--- a/chromium/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom
+++ b/chromium/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All Rights Reserved.
+// 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.
@@ -37,6 +37,11 @@ struct RegistryKey {
array<uint16> value;
};
+struct ExtensionId {
+ // A 32-character extension ID.
+ array<uint16> value;
+};
+
// Service provided by Chrome to prompt the user to start a cleanup if the
// Chrome Cleanup Tool detects unwanted software on the system.
interface ChromePrompt {
@@ -45,9 +50,12 @@ interface ChromePrompt {
// be deleted by the Chrome Cleanup Tool.
// - registry_keys: list of fully-qualified paths of the registry keys that
// will be changed or deleted by the Chrome Cleanup Tool.
+ // - extension_ids: list of IDs of extensions that will be removed by the
+ // Chrome Cleanup Tool.
// Returns:
// - prompt_acceptance: indicates if the user accepted the prompt.
PromptUser(array<FilePath> files_to_delete,
- [MinVersion=1] array<RegistryKey>? registry_keys)
+ [MinVersion=1] array<RegistryKey>? registry_keys,
+ [MinVersion=2] array<ExtensionId>? extension_ids)
=> (PromptAcceptance prompt_acceptance);
};
diff --git a/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt.typemap b/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt.typemap
index 762d29afd89..079592c0df2 100644
--- a/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt.typemap
+++ b/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt.typemap
@@ -7,7 +7,9 @@ public_headers = [
"//base/files/file_path.h",
"//base/strings/string16.h",
]
-traits_headers = [ "//components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h" ]
+traits_headers = [
+ "//components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h",
+]
sources = [
"//components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc",
]
@@ -15,4 +17,5 @@ sources = [
type_mappings = [
"chrome_cleaner.mojom.FilePath=base::FilePath",
"chrome_cleaner.mojom.RegistryKey=base::string16",
+ "chrome_cleaner.mojom.ExtensionId=base::string16",
]
diff --git a/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc b/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc
index 1c4301d461a..e4b64121841 100644
--- a/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc
+++ b/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc
@@ -69,4 +69,34 @@ bool StructTraits<chrome_cleaner::mojom::RegistryKeyDataView, base::string16>::
#endif
}
+// static
+base::span<const uint16_t>
+StructTraits<chrome_cleaner::mojom::ExtensionIdDataView, base::string16>::value(
+ const base::string16& extension_id) {
+#if defined(OS_WIN)
+ return base::make_span(reinterpret_cast<const uint16_t*>(extension_id.data()),
+ extension_id.size());
+#else
+ NOTREACHED();
+ return base::span<const uint16_t>();
+#endif
+}
+
+// static
+bool StructTraits<chrome_cleaner::mojom::ExtensionIdDataView, base::string16>::
+ Read(chrome_cleaner::mojom::ExtensionIdDataView extension_id_view,
+ base::string16* out) {
+#if defined(OS_WIN)
+ ArrayDataView<uint16_t> view;
+ extension_id_view.GetValueDataView(&view);
+ base::string16 extension_id = base::string16(
+ reinterpret_cast<const base::char16*>(view.data()), view.size());
+ *out = std::move(extension_id);
+ return true;
+#else
+ NOTREACHED();
+ return false;
+#endif
+}
+
} // namespace mojo
diff --git a/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h b/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h
index 5d064a201c4..7f9f6e0a173 100644
--- a/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h
+++ b/chromium/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h
@@ -27,6 +27,14 @@ struct StructTraits<chrome_cleaner::mojom::RegistryKeyDataView,
base::string16* out);
};
+template <>
+struct StructTraits<chrome_cleaner::mojom::ExtensionIdDataView,
+ base::string16> {
+ static base::span<const uint16_t> value(const base::string16& extension_id);
+ static bool Read(chrome_cleaner::mojom::ExtensionIdDataView extension_id_view,
+ base::string16* out);
+};
+
} // namespace mojo
#endif // COMPONENTS_CHROME_CLEANER_PUBLIC_TYPEMAPS_CHROME_CLEANER_STRUCT_TRAITS_H_
diff --git a/chromium/components/component_updater/BUILD.gn b/chromium/components/component_updater/BUILD.gn
index 0f854c6225f..321bc8d0eb3 100644
--- a/chromium/components/component_updater/BUILD.gn
+++ b/chromium/components/component_updater/BUILD.gn
@@ -23,12 +23,16 @@ static_library("component_updater") {
"pref_names.h",
"timer.cc",
"timer.h",
+ "timer_update_scheduler.cc",
+ "timer_update_scheduler.h",
+ "update_scheduler.h",
]
deps = [
"//base",
"//components/update_client",
"//components/version_info",
+ "//third_party/boringssl:boringssl",
"//ui/base",
"//url",
]
@@ -76,5 +80,6 @@ source_set("unit_tests") {
"//services/service_manager/public/cpp",
"//testing/gmock",
"//testing/gtest",
+ "//third_party/boringssl:boringssl",
]
}
diff --git a/chromium/components/component_updater/component_installer_unittest.cc b/chromium/components/component_updater/component_installer_unittest.cc
index daf2ff738bd..c87f451c105 100644
--- a/chromium/components/component_updater/component_installer_unittest.cc
+++ b/chromium/components/component_updater/component_installer_unittest.cc
@@ -168,6 +168,16 @@ class MockInstallerPolicy : public ComponentInstallerPolicy {
}
};
+class MockUpdateScheduler : public UpdateScheduler {
+ public:
+ MOCK_METHOD4(Schedule,
+ void(const base::TimeDelta& initial_delay,
+ const base::TimeDelta& delay,
+ const UserTask& user_task,
+ const OnStopTaskCallback& on_stop));
+ MOCK_METHOD0(Stop, void());
+};
+
class ComponentInstallerTest : public testing::Test {
public:
ComponentInstallerTest();
@@ -179,6 +189,7 @@ class ComponentInstallerTest : public testing::Test {
}
scoped_refptr<TestConfigurator> configurator() const { return config_; }
base::OnceClosure quit_closure() { return runloop_.QuitClosure(); }
+ MockUpdateScheduler& scheduler() { return *scheduler_; }
protected:
void RunThreads();
@@ -189,6 +200,10 @@ class ComponentInstallerTest : public testing::Test {
private:
void UnpackComplete(const ComponentUnpacker::Result& result);
+ void Schedule(const base::TimeDelta& initial_delay,
+ const base::TimeDelta& delay,
+ const UpdateScheduler::UserTask& user_task,
+ const UpdateScheduler::OnStopTaskCallback& on_stop);
const scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_ =
base::ThreadTaskRunnerHandle::Get();
@@ -196,6 +211,7 @@ class ComponentInstallerTest : public testing::Test {
scoped_refptr<TestConfigurator> config_ =
base::MakeRefCounted<TestConfigurator>();
+ MockUpdateScheduler* scheduler_ = nullptr;
scoped_refptr<MockUpdateClient> update_client_ =
base::MakeRefCounted<MockUpdateClient>();
std::unique_ptr<ComponentUpdateService> component_updater_;
@@ -204,8 +220,12 @@ class ComponentInstallerTest : public testing::Test {
ComponentInstallerTest::ComponentInstallerTest() {
EXPECT_CALL(update_client(), AddObserver(_)).Times(1);
- component_updater_ =
- std::make_unique<CrxUpdateService>(config_, update_client_);
+ auto scheduler = std::make_unique<MockUpdateScheduler>();
+ scheduler_ = scheduler.get();
+ ON_CALL(*scheduler_, Schedule(_, _, _, _))
+ .WillByDefault(Invoke(this, &ComponentInstallerTest::Schedule));
+ component_updater_ = std::make_unique<CrxUpdateService>(
+ config_, std::move(scheduler), update_client_);
}
ComponentInstallerTest::~ComponentInstallerTest() {
@@ -237,6 +257,14 @@ void ComponentInstallerTest::UnpackComplete(
main_thread_task_runner_->PostTask(FROM_HERE, quit_closure());
}
+void ComponentInstallerTest::Schedule(
+ const base::TimeDelta& initial_delay,
+ const base::TimeDelta& delay,
+ const UpdateScheduler::UserTask& user_task,
+ const UpdateScheduler::OnStopTaskCallback& on_stop) {
+ user_task.Run(base::DoNothing());
+}
+
} // namespace
// Tests that the component metadata is propagated from the component installer
@@ -272,6 +300,8 @@ TEST_F(ComponentInstallerTest, RegisterComponent) {
EXPECT_CALL(update_client(), GetCrxUpdateState(id, _)).Times(1);
EXPECT_CALL(update_client(), Stop()).Times(1);
+ EXPECT_CALL(scheduler(), Schedule(_, _, _, _)).Times(1);
+ EXPECT_CALL(scheduler(), Stop()).Times(1);
auto installer = base::MakeRefCounted<ComponentInstaller>(
std::make_unique<MockInstallerPolicy>());
@@ -324,6 +354,7 @@ TEST_F(ComponentInstallerTest, UnpackPathInstallSuccess) {
EXPECT_FALSE(base::PathExists(unpack_path));
EXPECT_CALL(update_client(), Stop()).Times(1);
+ EXPECT_CALL(scheduler(), Stop()).Times(1);
}
// Tests that the unpack path is removed when the install failed.
@@ -354,6 +385,7 @@ TEST_F(ComponentInstallerTest, UnpackPathInstallError) {
EXPECT_FALSE(base::PathExists(unpack_path));
EXPECT_CALL(update_client(), Stop()).Times(1);
+ EXPECT_CALL(scheduler(), Stop()).Times(1);
}
} // namespace component_updater
diff --git a/chromium/components/component_updater/component_updater_command_line_config_policy.cc b/chromium/components/component_updater/component_updater_command_line_config_policy.cc
index 00ff80c4a23..c9b60934b7d 100644
--- a/chromium/components/component_updater/component_updater_command_line_config_policy.cc
+++ b/chromium/components/component_updater/component_updater_command_line_config_policy.cc
@@ -9,6 +9,7 @@
#include "base/command_line.h"
#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/sys_string_conversions.h"
#include "build/build_config.h"
@@ -37,6 +38,10 @@ const char kSwitchUrlSource[] = "url-source";
// Disables differential updates.
const char kSwitchDisableDeltaUpdates[] = "disable-delta-updates";
+// Configures the initial delay before the first component update check. The
+// value is in seconds.
+const char kInitialDelay[] = "initial-delay";
+
#if defined(OS_WIN)
// Disables background downloads.
const char kSwitchDisableBackgroundDownloads[] = "disable-background-downloads";
@@ -91,6 +96,12 @@ ComponentUpdaterCommandLineConfigPolicy::
url_source_override_ = GURL(switch_url_source);
DCHECK(url_source_override_.is_valid());
}
+
+ const std::string initial_delay =
+ GetSwitchArgument(switch_values, kInitialDelay);
+ int initial_delay_seconds = 0;
+ if (base::StringToInt(initial_delay, &initial_delay_seconds))
+ initial_delay_ = initial_delay_seconds;
}
bool ComponentUpdaterCommandLineConfigPolicy::BackgroundDownloadsEnabled()
@@ -118,4 +129,8 @@ GURL ComponentUpdaterCommandLineConfigPolicy::UrlSourceOverride() const {
return url_source_override_;
}
+int ComponentUpdaterCommandLineConfigPolicy::InitialDelay() const {
+ return initial_delay_;
+}
+
} // namespace component_updater
diff --git a/chromium/components/component_updater/component_updater_command_line_config_policy.h b/chromium/components/component_updater/component_updater_command_line_config_policy.h
index e3dbc6701af..e2c037012f5 100644
--- a/chromium/components/component_updater/component_updater_command_line_config_policy.h
+++ b/chromium/components/component_updater/component_updater_command_line_config_policy.h
@@ -29,6 +29,7 @@ class ComponentUpdaterCommandLineConfigPolicy final
bool PingsEnabled() const override;
bool TestRequest() const override;
GURL UrlSourceOverride() const override;
+ int InitialDelay() const override;
private:
bool background_downloads_enabled_ = false;
@@ -36,6 +37,11 @@ class ComponentUpdaterCommandLineConfigPolicy final
bool fast_update_ = false;
bool pings_enabled_ = true;
bool test_request_ = false;
+
+ // If non-zero, time interval in seconds until the first component
+ // update check.
+ int initial_delay_ = 0;
+
GURL url_source_override_;
DISALLOW_COPY_AND_ASSIGN(ComponentUpdaterCommandLineConfigPolicy);
diff --git a/chromium/components/component_updater/component_updater_service.cc b/chromium/components/component_updater/component_updater_service.cc
index b684882da8c..5833ba3ebf3 100644
--- a/chromium/components/component_updater/component_updater_service.cc
+++ b/chromium/components/component_updater/component_updater_service.cc
@@ -23,7 +23,6 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/component_updater/component_updater_service_internal.h"
-#include "components/component_updater/timer.h"
#include "components/update_client/configurator.h"
#include "components/update_client/crx_update_item.h"
#include "components/update_client/update_client.h"
@@ -56,8 +55,11 @@ ComponentInfo::ComponentInfo(ComponentInfo&& other) = default;
ComponentInfo::~ComponentInfo() {}
CrxUpdateService::CrxUpdateService(scoped_refptr<Configurator> config,
+ std::unique_ptr<UpdateScheduler> scheduler,
scoped_refptr<UpdateClient> update_client)
- : config_(config), update_client_(update_client) {
+ : config_(config),
+ scheduler_(std::move(scheduler)),
+ update_client_(update_client) {
AddObserver(this);
}
@@ -91,18 +93,20 @@ void CrxUpdateService::Start() {
<< "Next update attempt will take place in "
<< config_->NextCheckDelay() << " seconds. ";
- timer_.Start(
+ scheduler_->Schedule(
base::TimeDelta::FromSeconds(config_->InitialDelay()),
base::TimeDelta::FromSeconds(config_->NextCheckDelay()),
base::Bind(base::IgnoreResult(&CrxUpdateService::CheckForUpdates),
- base::Unretained(this)));
+ base::Unretained(this)),
+ // TODO: Stop component update if requested.
+ base::DoNothing());
}
// Stops the update loop. In flight operations will be completed.
void CrxUpdateService::Stop() {
DCHECK(thread_checker_.CalledOnValidThread());
VLOG(1) << "CrxUpdateService stopping";
- timer_.Stop();
+ scheduler_->Stop();
update_client_->Stop();
}
@@ -251,6 +255,7 @@ void CrxUpdateService::MaybeThrottle(const std::string& id,
}
void CrxUpdateService::OnDemandUpdate(const std::string& id,
+ Priority priority,
Callback callback) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -263,7 +268,7 @@ void CrxUpdateService::OnDemandUpdate(const std::string& id,
return;
}
- OnDemandUpdateInternal(id, std::move(callback));
+ OnDemandUpdateInternal(id, priority, std::move(callback));
}
bool CrxUpdateService::OnDemandUpdateWithCooldown(const std::string& id) {
@@ -280,28 +285,41 @@ bool CrxUpdateService::OnDemandUpdateWithCooldown(const std::string& id) {
return false;
}
- OnDemandUpdateInternal(id, Callback());
+ OnDemandUpdateInternal(id, Priority::FOREGROUND, Callback());
return true;
}
void CrxUpdateService::OnDemandUpdateInternal(const std::string& id,
+ Priority priority,
Callback callback) {
DCHECK(thread_checker_.CalledOnValidThread());
UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_MANUAL,
UPDATE_TYPE_COUNT);
- update_client_->Install(
- id,
- base::BindOnce(&CrxUpdateService::GetCrxComponents,
- base::Unretained(this)),
- base::BindOnce(&CrxUpdateService::OnUpdateComplete,
- base::Unretained(this), std::move(callback),
- base::TimeTicks::Now()));
+
+ auto crx_data_callback = base::BindOnce(&CrxUpdateService::GetCrxComponents,
+ base::Unretained(this));
+ auto update_complete_callback = base::BindOnce(
+ &CrxUpdateService::OnUpdateComplete, base::Unretained(this),
+ std::move(callback), base::TimeTicks::Now());
+
+ if (priority == Priority::FOREGROUND)
+ update_client_->Install(id, std::move(crx_data_callback),
+ std::move(update_complete_callback));
+ else if (priority == Priority::BACKGROUND)
+ update_client_->Update({id}, std::move(crx_data_callback), false,
+ std::move(update_complete_callback));
+ else
+ NOTREACHED();
}
-bool CrxUpdateService::CheckForUpdates() {
+bool CrxUpdateService::CheckForUpdates(
+ UpdateScheduler::OnFinishedCallback on_finished) {
DCHECK(thread_checker_.CalledOnValidThread());
+ // TODO(xiaochu): remove this log after https://crbug.com/851151 is fixed.
+ VLOG(1) << "CheckForUpdates: automatic updatecheck for components.";
+
UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_AUTOMATIC,
UPDATE_TYPE_COUNT);
@@ -317,24 +335,38 @@ bool CrxUpdateService::CheckForUpdates() {
unsecure_ids.push_back(id);
}
+ if (unsecure_ids.empty() && secure_ids.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), Callback(),
- base::TimeTicks::Now()));
+ 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), Callback(),
- base::TimeTicks::Now()));
+ 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()));
}
return true;
@@ -381,6 +413,8 @@ void CrxUpdateService::OnUpdateComplete(Callback callback,
UMA_HISTOGRAM_BOOLEAN("ComponentUpdater.UpdateCompleteResult",
error != update_client::Error::NONE);
+ UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.UpdateCompleteError", error,
+ update_client::Error::MAX_VALUE);
UMA_HISTOGRAM_LONG_TIMES_100("ComponentUpdater.UpdateCompleteTime",
base::TimeTicks::Now() - start_time);
@@ -437,10 +471,13 @@ void CrxUpdateService::OnEvent(Events event, const std::string& id) {
// is the job of the browser process.
// TODO(sorin): consider making this a singleton.
std::unique_ptr<ComponentUpdateService> ComponentUpdateServiceFactory(
- scoped_refptr<Configurator> config) {
+ scoped_refptr<Configurator> config,
+ std::unique_ptr<UpdateScheduler> scheduler) {
DCHECK(config);
+ DCHECK(scheduler);
auto update_client = update_client::UpdateClientFactory(config);
- return std::make_unique<CrxUpdateService>(config, std::move(update_client));
+ return std::make_unique<CrxUpdateService>(config, std::move(scheduler),
+ std::move(update_client));
}
} // namespace component_updater
diff --git a/chromium/components/component_updater/component_updater_service.h b/chromium/components/component_updater/component_updater_service.h
index 718e7fbb0dc..3ef6a1d1074 100644
--- a/chromium/components/component_updater/component_updater_service.h
+++ b/chromium/components/component_updater/component_updater_service.h
@@ -39,6 +39,7 @@ namespace component_updater {
using Callback = update_client::Callback;
class OnDemandUpdater;
+class UpdateScheduler;
using Configurator = update_client::Configurator;
using CrxComponent = update_client::CrxComponent;
@@ -150,6 +151,11 @@ using ServiceObserver = ComponentUpdateService::Observer;
class OnDemandUpdater {
public:
+ // The priority of the on demand update. Calls with |BACKGROUND| priority may
+ // be queued up but calls with |FOREGROUND| priority may be processed right
+ // away.
+ enum class Priority { BACKGROUND = 0, FOREGROUND = 1 };
+
virtual ~OnDemandUpdater() {}
private:
@@ -170,12 +176,15 @@ class OnDemandUpdater {
// the update will be applied. The caller can subscribe to component update
// service notifications and provide an optional callback to get the result
// of the call. The function does not implement any cooldown interval.
- virtual void OnDemandUpdate(const std::string& id, Callback callback) = 0;
+ virtual void OnDemandUpdate(const std::string& id,
+ Priority priority,
+ Callback callback) = 0;
};
// Creates the component updater.
std::unique_ptr<ComponentUpdateService> ComponentUpdateServiceFactory(
- scoped_refptr<Configurator> config);
+ scoped_refptr<Configurator> config,
+ std::unique_ptr<UpdateScheduler> scheduler);
} // namespace component_updater
diff --git a/chromium/components/component_updater/component_updater_service_internal.h b/chromium/components/component_updater/component_updater_service_internal.h
index 1094ffa79bd..38db35c4a46 100644
--- a/chromium/components/component_updater/component_updater_service_internal.h
+++ b/chromium/components/component_updater/component_updater_service_internal.h
@@ -13,7 +13,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/threading/thread_checker.h"
-#include "components/component_updater/timer.h"
+#include "components/component_updater/update_scheduler.h"
namespace base {
class TimeTicks;
@@ -37,6 +37,7 @@ class CrxUpdateService : public ComponentUpdateService,
public:
CrxUpdateService(scoped_refptr<Configurator> config,
+ std::unique_ptr<UpdateScheduler> scheduler,
scoped_refptr<UpdateClient> update_client);
~CrxUpdateService() override;
@@ -59,15 +60,19 @@ class CrxUpdateService : public ComponentUpdateService,
void OnEvent(Events event, const std::string& id) override;
// Overrides for OnDemandUpdater.
- void OnDemandUpdate(const std::string& id, Callback callback) override;
+ void OnDemandUpdate(const std::string& id,
+ Priority priority,
+ Callback callback) override;
private:
void Start();
void Stop();
- bool CheckForUpdates();
+ bool CheckForUpdates(UpdateScheduler::OnFinishedCallback on_finished);
- void OnDemandUpdateInternal(const std::string& id, Callback callback);
+ void OnDemandUpdateInternal(const std::string& id,
+ Priority priority,
+ Callback callback);
bool OnDemandUpdateWithCooldown(const std::string& id);
bool DoUnregisterComponent(const CrxComponent& component);
@@ -85,11 +90,10 @@ class CrxUpdateService : public ComponentUpdateService,
base::ThreadChecker thread_checker_;
scoped_refptr<Configurator> config_;
+ std::unique_ptr<UpdateScheduler> scheduler_;
scoped_refptr<UpdateClient> update_client_;
- Timer timer_;
-
// A collection of every registered component.
using Components = std::map<std::string, CrxComponent>;
Components components_;
diff --git a/chromium/components/component_updater/component_updater_service_unittest.cc b/chromium/components/component_updater/component_updater_service_unittest.cc
index 864d27fc2f7..ac5c214c3f3 100644
--- a/chromium/components/component_updater/component_updater_service_unittest.cc
+++ b/chromium/components/component_updater/component_updater_service_unittest.cc
@@ -11,13 +11,15 @@
#include <vector>
#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
+#include "base/stl_util.h"
#include "base/task_scheduler/post_task.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
@@ -76,7 +78,7 @@ class MockUpdateClient : public UpdateClient {
void Install(const std::string& id,
CrxDataCallback crx_data_callback,
Callback callback) override {
- DoInstall(id, std::move(crx_data_callback));
+ DoInstall(id);
std::move(callback).Run(update_client::Error::NONE);
}
@@ -87,7 +89,7 @@ class MockUpdateClient : public UpdateClient {
// All update calls initiated by the component update service are
// automatically triggered as background updates without user intervention.
EXPECT_FALSE(is_foreground);
- DoUpdate(ids, std::move(crx_data_callback));
+ DoUpdate(ids);
std::move(callback).Run(update_client::Error::NONE);
}
@@ -101,12 +103,8 @@ class MockUpdateClient : public UpdateClient {
MOCK_METHOD1(AddObserver, void(Observer* observer));
MOCK_METHOD1(RemoveObserver, void(Observer* observer));
- MOCK_METHOD2(DoInstall,
- void(const std::string& id,
- const CrxDataCallback& crx_data_callback));
- MOCK_METHOD2(DoUpdate,
- void(const std::vector<std::string>& ids,
- const CrxDataCallback& crx_data_callback));
+ MOCK_METHOD1(DoInstall, void(const std::string& id));
+ MOCK_METHOD1(DoUpdate, void(const std::vector<std::string>& ids));
MOCK_CONST_METHOD2(GetCrxUpdateState,
bool(const std::string& id, CrxUpdateItem* update_item));
MOCK_CONST_METHOD1(IsUpdating, bool(const std::string& id));
@@ -128,6 +126,16 @@ class MockServiceObserver : public ServiceObserver {
MOCK_METHOD2(OnEvent, void(Events event, const std::string&));
};
+class MockUpdateScheduler : public UpdateScheduler {
+ public:
+ MOCK_METHOD4(Schedule,
+ void(const base::TimeDelta& initial_delay,
+ const base::TimeDelta& delay,
+ const UserTask& user_task,
+ const OnStopTaskCallback& on_stop));
+ MOCK_METHOD0(Stop, void());
+};
+
class ComponentUpdaterTest : public testing::Test {
public:
ComponentUpdaterTest();
@@ -144,17 +152,25 @@ class ComponentUpdaterTest : public testing::Test {
ComponentUpdateService& component_updater() { return *component_updater_; }
scoped_refptr<TestConfigurator> configurator() const { return config_; }
base::OnceClosure quit_closure() { return runloop_.QuitClosure(); }
+ MockUpdateScheduler& scheduler() { return *scheduler_; }
static void ReadyCallback() {}
protected:
void RunThreads();
private:
+ void RunUpdateTask(const UpdateScheduler::UserTask& user_task);
+ void Schedule(const base::TimeDelta& initial_delay,
+ const base::TimeDelta& delay,
+ const UpdateScheduler::UserTask& user_task,
+ const UpdateScheduler::OnStopTaskCallback& on_stop);
+
base::test::ScopedTaskEnvironment scoped_task_environment_;
base::RunLoop runloop_;
scoped_refptr<TestConfigurator> config_ =
base::MakeRefCounted<TestConfigurator>();
+ MockUpdateScheduler* scheduler_;
scoped_refptr<MockUpdateClient> update_client_ =
base::MakeRefCounted<MockUpdateClient>();
std::unique_ptr<ComponentUpdateService> component_updater_;
@@ -164,7 +180,9 @@ class ComponentUpdaterTest : public testing::Test {
class OnDemandTester {
public:
- void OnDemand(ComponentUpdateService* cus, const std::string& id);
+ void OnDemand(ComponentUpdateService* cus,
+ const std::string& id,
+ OnDemandUpdater::Priority priority);
update_client::Error error() const { return error_; }
private:
@@ -192,10 +210,12 @@ MockServiceObserver::~MockServiceObserver() {
}
void OnDemandTester::OnDemand(ComponentUpdateService* cus,
- const std::string& id) {
+ const std::string& id,
+ OnDemandUpdater::Priority priority) {
cus->GetOnDemandUpdater().OnDemandUpdate(
- id, base::BindOnce(&OnDemandTester::OnDemandComplete,
- base::Unretained(this)));
+ id, priority,
+ base::BindOnce(&OnDemandTester::OnDemandComplete,
+ base::Unretained(this)));
}
void OnDemandTester::OnDemandComplete(update_client::Error error) {
@@ -206,13 +226,18 @@ std::unique_ptr<ComponentUpdateService> TestComponentUpdateServiceFactory(
scoped_refptr<Configurator> config) {
DCHECK(config);
return std::make_unique<CrxUpdateService>(
- config, base::MakeRefCounted<MockUpdateClient>());
+ config, std::make_unique<MockUpdateScheduler>(),
+ base::MakeRefCounted<MockUpdateClient>());
}
ComponentUpdaterTest::ComponentUpdaterTest() {
EXPECT_CALL(update_client(), AddObserver(_)).Times(1);
- component_updater_ =
- std::make_unique<CrxUpdateService>(config_, update_client_);
+ auto scheduler = std::make_unique<MockUpdateScheduler>();
+ scheduler_ = scheduler.get();
+ ON_CALL(*scheduler_, Schedule(_, _, _, _))
+ .WillByDefault(Invoke(this, &ComponentUpdaterTest::Schedule));
+ component_updater_ = std::make_unique<CrxUpdateService>(
+ config_, std::move(scheduler), update_client_);
}
ComponentUpdaterTest::~ComponentUpdaterTest() {
@@ -230,10 +255,35 @@ void ComponentUpdaterTest::RunThreads() {
runloop_.Run();
}
+void ComponentUpdaterTest::RunUpdateTask(
+ const UpdateScheduler::UserTask& user_task) {
+ scoped_task_environment_.GetMainThreadTaskRunner()->PostTask(
+ FROM_HERE, base::BindRepeating(
+ [](const UpdateScheduler::UserTask& user_task,
+ ComponentUpdaterTest* test) {
+ user_task.Run(base::BindOnce(
+ [](const UpdateScheduler::UserTask& user_task,
+ ComponentUpdaterTest* test) {
+ test->RunUpdateTask(user_task);
+ },
+ user_task, base::Unretained(test)));
+ },
+ user_task, base::Unretained(this)));
+}
+
+void ComponentUpdaterTest::Schedule(
+ const base::TimeDelta& initial_delay,
+ const base::TimeDelta& delay,
+ const UpdateScheduler::UserTask& user_task,
+ const UpdateScheduler::OnStopTaskCallback& on_stop) {
+ RunUpdateTask(user_task);
+}
+
TEST_F(ComponentUpdaterTest, AddObserver) {
MockServiceObserver observer;
EXPECT_CALL(update_client(), AddObserver(&observer)).Times(1);
EXPECT_CALL(update_client(), Stop()).Times(1);
+ EXPECT_CALL(scheduler(), Stop()).Times(1);
component_updater().AddObserver(&observer);
}
@@ -241,6 +291,7 @@ TEST_F(ComponentUpdaterTest, RemoveObserver) {
MockServiceObserver observer;
EXPECT_CALL(update_client(), RemoveObserver(&observer)).Times(1);
EXPECT_CALL(update_client(), Stop()).Times(1);
+ EXPECT_CALL(scheduler(), Stop()).Times(1);
component_updater().RemoveObserver(&observer);
}
@@ -253,7 +304,7 @@ TEST_F(ComponentUpdaterTest, RegisterComponent) {
LoopHandler(int max_cnt, base::OnceClosure quit_closure)
: max_cnt_(max_cnt), quit_closure_(std::move(quit_closure)) {}
- void OnUpdate(const std::vector<std::string>& ids, Unused) {
+ void OnUpdate(const std::vector<std::string>& ids) {
static int cnt = 0;
++cnt;
if (cnt >= max_cnt_)
@@ -281,22 +332,24 @@ TEST_F(ComponentUpdaterTest, RegisterComponent) {
ids.push_back(id2);
CrxComponent crx_component1;
- crx_component1.pk_hash.assign(abag_hash, abag_hash + arraysize(abag_hash));
+ crx_component1.pk_hash.assign(abag_hash, abag_hash + base::size(abag_hash));
crx_component1.version = base::Version("1.0");
crx_component1.installer = installer;
CrxComponent crx_component2;
- crx_component2.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
+ crx_component2.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
crx_component2.version = base::Version("0.9");
crx_component2.installer = installer;
// Quit after two update checks have fired.
LoopHandler loop_handler(2, quit_closure());
- EXPECT_CALL(update_client(), DoUpdate(ids, _))
+ EXPECT_CALL(update_client(), DoUpdate(ids))
.WillRepeatedly(Invoke(&loop_handler, &LoopHandler::OnUpdate));
EXPECT_CALL(update_client(), IsUpdating(id1)).Times(1);
EXPECT_CALL(update_client(), Stop()).Times(1);
+ EXPECT_CALL(scheduler(), Schedule(_, _, _, _)).Times(1);
+ EXPECT_CALL(scheduler(), Stop()).Times(1);
EXPECT_TRUE(component_updater().RegisterComponent(crx_component1));
EXPECT_TRUE(component_updater().RegisterComponent(crx_component2));
@@ -315,10 +368,18 @@ TEST_F(ComponentUpdaterTest, OnDemandUpdate) {
public:
explicit LoopHandler(int max_cnt) : max_cnt_(max_cnt) {}
- void OnInstall(const std::string& ids, Unused) {
- static int cnt = 0;
- ++cnt;
- if (cnt >= max_cnt_) {
+ void OnInstall(const std::string& ids) {
+ ++cnt_;
+ if (cnt_ >= max_cnt_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&LoopHandler::Quit, base::Unretained(this)));
+ }
+ }
+
+ void OnUpdate(const std::vector<std::string>& ids) {
+ ++cnt_;
+ if (cnt_ >= max_cnt_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&LoopHandler::Quit, base::Unretained(this)));
@@ -328,13 +389,14 @@ TEST_F(ComponentUpdaterTest, OnDemandUpdate) {
private:
void Quit() { base::RunLoop::QuitCurrentWhenIdleDeprecated(); }
+ int cnt_ = 0;
const int max_cnt_;
};
base::HistogramTester ht;
- auto config = configurator();
- config->SetInitialDelay(3600);
+ // Don't run periodic update task.
+ ON_CALL(scheduler(), Schedule(_, _, _, _)).WillByDefault(Return());
auto& cus = component_updater();
@@ -343,34 +405,56 @@ TEST_F(ComponentUpdaterTest, OnDemandUpdate) {
// component was not registered, the call is ignored for UMA metrics.
OnDemandTester ondemand_tester_component_not_registered;
ondemand_tester_component_not_registered.OnDemand(
- &cus, "ihfokbkgjpifnbbojhneepfflplebdkc");
-
- const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
-
- using update_client::jebg_hash;
- CrxComponent crx_component;
- crx_component.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
- crx_component.version = base::Version("0.9");
- crx_component.installer = base::MakeRefCounted<MockInstaller>();
-
- LoopHandler loop_handler(1);
- EXPECT_CALL(update_client(), DoInstall("jebgalgnebhfojomionfpkfelancnnkf", _))
+ &cus, "ihfokbkgjpifnbbojhneepfflplebdkc",
+ OnDemandUpdater::Priority::FOREGROUND);
+
+ // Register two components, then call |OnDemand| for each component, with
+ // foreground and background priorities. Expect calls to |Schedule| because
+ // components have registered, calls to |Install| and |Update| corresponding
+ // to each |OnDemand| invocation, and calls to |Stop| when the mocks are
+ // torn down.
+ LoopHandler loop_handler(2);
+ EXPECT_CALL(scheduler(), Schedule(_, _, _, _)).Times(1);
+ EXPECT_CALL(update_client(), DoInstall("jebgalgnebhfojomionfpkfelancnnkf"))
.WillOnce(Invoke(&loop_handler, &LoopHandler::OnInstall));
+ EXPECT_CALL(
+ update_client(),
+ DoUpdate(std::vector<std::string>({"abagagagagagagagagagagagagagagag"})))
+ .WillOnce(Invoke(&loop_handler, &LoopHandler::OnUpdate));
EXPECT_CALL(update_client(), Stop()).Times(1);
+ EXPECT_CALL(scheduler(), Stop()).Times(1);
+
+ {
+ using update_client::jebg_hash;
+ CrxComponent crx_component;
+ crx_component.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+ crx_component.version = base::Version("0.9");
+ crx_component.installer = base::MakeRefCounted<MockInstaller>();
+ EXPECT_TRUE(cus.RegisterComponent(crx_component));
+ }
+ {
+ using update_client::abag_hash;
+ CrxComponent crx_component;
+ crx_component.pk_hash.assign(abag_hash, abag_hash + base::size(abag_hash));
+ crx_component.version = base::Version("0.9");
+ crx_component.installer = base::MakeRefCounted<MockInstaller>();
+ EXPECT_TRUE(cus.RegisterComponent(crx_component));
+ }
- EXPECT_TRUE(cus.RegisterComponent(crx_component));
OnDemandTester ondemand_tester;
- ondemand_tester.OnDemand(&cus, id);
-
+ ondemand_tester.OnDemand(&cus, "jebgalgnebhfojomionfpkfelancnnkf",
+ OnDemandUpdater::Priority::FOREGROUND);
+ ondemand_tester.OnDemand(&cus, "abagagagagagagagagagagagagagagag",
+ OnDemandUpdater::Priority::BACKGROUND);
base::RunLoop().Run();
EXPECT_EQ(update_client::Error::INVALID_ARGUMENT,
ondemand_tester_component_not_registered.error());
EXPECT_EQ(update_client::Error::NONE, ondemand_tester.error());
- ht.ExpectUniqueSample("ComponentUpdater.Calls", 0, 1);
- ht.ExpectUniqueSample("ComponentUpdater.UpdateCompleteResult", 0, 1);
- ht.ExpectTotalCount("ComponentUpdater.UpdateCompleteTime", 1);
+ ht.ExpectUniqueSample("ComponentUpdater.Calls", 0, 2);
+ ht.ExpectUniqueSample("ComponentUpdater.UpdateCompleteResult", 0, 2);
+ ht.ExpectTotalCount("ComponentUpdater.UpdateCompleteTime", 2);
}
// Tests that throttling an update invokes UpdateClient::Install.
@@ -380,7 +464,7 @@ TEST_F(ComponentUpdaterTest, MaybeThrottle) {
LoopHandler(int max_cnt, base::OnceClosure quit_closure)
: max_cnt_(max_cnt), quit_closure_(std::move(quit_closure)) {}
- void OnInstall(const std::string& ids, Unused) {
+ void OnInstall(const std::string& ids) {
static int cnt = 0;
++cnt;
if (cnt >= max_cnt_)
@@ -394,22 +478,24 @@ TEST_F(ComponentUpdaterTest, MaybeThrottle) {
base::HistogramTester ht;
- auto config = configurator();
- config->SetInitialDelay(3600);
+ // Don't run periodic update task.
+ ON_CALL(scheduler(), Schedule(_, _, _, _)).WillByDefault(Return());
scoped_refptr<MockInstaller> installer =
base::MakeRefCounted<MockInstaller>();
using update_client::jebg_hash;
CrxComponent crx_component;
- crx_component.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
+ crx_component.pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
crx_component.version = base::Version("0.9");
crx_component.installer = installer;
LoopHandler loop_handler(1, quit_closure());
- EXPECT_CALL(update_client(), DoInstall("jebgalgnebhfojomionfpkfelancnnkf", _))
+ EXPECT_CALL(update_client(), DoInstall("jebgalgnebhfojomionfpkfelancnnkf"))
.WillOnce(Invoke(&loop_handler, &LoopHandler::OnInstall));
EXPECT_CALL(update_client(), Stop()).Times(1);
+ EXPECT_CALL(scheduler(), Schedule(_, _, _, _)).Times(1);
+ EXPECT_CALL(scheduler(), Stop()).Times(1);
EXPECT_TRUE(component_updater().RegisterComponent(crx_component));
component_updater().MaybeThrottle(
diff --git a/chromium/components/component_updater/configurator_impl.cc b/chromium/components/component_updater/configurator_impl.cc
index e2182691e55..2a631301d4b 100644
--- a/chromium/components/component_updater/configurator_impl.cc
+++ b/chromium/components/component_updater/configurator_impl.cc
@@ -41,7 +41,8 @@ ConfiguratorImpl::ConfiguratorImpl(
fast_update_(config_policy.FastUpdate()),
pings_enabled_(config_policy.PingsEnabled()),
require_encryption_(require_encryption),
- url_source_override_(config_policy.UrlSourceOverride()) {
+ url_source_override_(config_policy.UrlSourceOverride()),
+ initial_delay_(config_policy.InitialDelay()) {
if (config_policy.TestRequest())
extra_info_ += "testrequest=\"1\"";
}
@@ -49,6 +50,8 @@ ConfiguratorImpl::ConfiguratorImpl(
ConfiguratorImpl::~ConfiguratorImpl() {}
int ConfiguratorImpl::InitialDelay() const {
+ if (initial_delay_)
+ return initial_delay_;
return fast_update_ ? 10 : (6 * kDelayOneMinute);
}
@@ -119,4 +122,11 @@ std::vector<uint8_t> ConfiguratorImpl::GetRunActionKeyHash() const {
0x32, 0x76, 0xd9, 0x93, 0xb5, 0xa3, 0xce, 0x02};
}
+// The default implementation for most embedders returns an empty string.
+// Desktop embedders, such as the Windows component updater can provide a
+// meaningful implementation for this function.
+std::string ConfiguratorImpl::GetAppGuid() const {
+ return {};
+}
+
} // namespace component_updater
diff --git a/chromium/components/component_updater/configurator_impl.h b/chromium/components/component_updater/configurator_impl.h
index 24a68e6e98e..73b4d3c3988 100644
--- a/chromium/components/component_updater/configurator_impl.h
+++ b/chromium/components/component_updater/configurator_impl.h
@@ -85,14 +85,19 @@ class ConfiguratorImpl {
// Returns the key hash corresponding to a CRX trusted by ActionRun.
std::vector<uint8_t> GetRunActionKeyHash() const;
+ // Returns the app GUID with which Chrome is registered with Google Update, or
+ // an empty string if this brand does not integrate with Google Update.
+ std::string GetAppGuid() const;
+
private:
std::string extra_info_;
- bool background_downloads_enabled_;
- bool deltas_enabled_;
- bool fast_update_;
- bool pings_enabled_;
- bool require_encryption_;
- GURL url_source_override_;
+ const bool background_downloads_enabled_;
+ const bool deltas_enabled_;
+ const bool fast_update_;
+ const bool pings_enabled_;
+ const bool require_encryption_;
+ const GURL url_source_override_;
+ const int initial_delay_;
DISALLOW_COPY_AND_ASSIGN(ConfiguratorImpl);
};
diff --git a/chromium/components/component_updater/configurator_impl_unittest.cc b/chromium/components/component_updater/configurator_impl_unittest.cc
index b0b10f5361b..5200eb9de06 100644
--- a/chromium/components/component_updater/configurator_impl_unittest.cc
+++ b/chromium/components/component_updater/configurator_impl_unittest.cc
@@ -88,4 +88,55 @@ TEST_F(ComponentUpdaterConfiguratorImplTest, FastUpdateWithCustomPolicy) {
CHECK_EQ(10, config->UpdateDelay());
}
+TEST_F(ComponentUpdaterConfiguratorImplTest, InitialDelay) {
+ std::unique_ptr<ConfiguratorImpl> config = std::make_unique<ConfiguratorImpl>(
+ update_client::CommandLineConfigPolicy(), false);
+ CHECK_EQ(6 * kDelayOneMinute, config->InitialDelay());
+
+ class CommandLineConfigPolicy
+ : public update_client::CommandLineConfigPolicy {
+ public:
+ CommandLineConfigPolicy() {}
+
+ // update_client::CommandLineConfigPolicy overrides.
+ bool BackgroundDownloadsEnabled() const override { return false; }
+ bool DeltaUpdatesEnabled() const override { return false; }
+ bool FastUpdate() const override { return fast_update_; }
+ bool PingsEnabled() const override { return false; }
+ bool TestRequest() const override { return false; }
+ GURL UrlSourceOverride() const override { return GURL(); }
+ int InitialDelay() const override { return initial_delay_; };
+
+ void set_fast_update(bool fast_update) { fast_update_ = fast_update; }
+ void set_initial_delay(int initial_delay) {
+ initial_delay_ = initial_delay;
+ }
+
+ private:
+ int initial_delay_ = 0;
+ bool fast_update_ = false;
+ };
+
+ {
+ CommandLineConfigPolicy clcp;
+ clcp.set_fast_update(true);
+ config = std::make_unique<ConfiguratorImpl>(clcp, false);
+ CHECK_EQ(10, config->InitialDelay());
+ }
+
+ {
+ CommandLineConfigPolicy clcp;
+ clcp.set_fast_update(false);
+ config = std::make_unique<ConfiguratorImpl>(clcp, false);
+ CHECK_EQ(6 * kDelayOneMinute, config->InitialDelay());
+ }
+
+ {
+ CommandLineConfigPolicy clcp;
+ clcp.set_initial_delay(kDelayOneMinute);
+ config = std::make_unique<ConfiguratorImpl>(clcp, false);
+ CHECK_EQ(kDelayOneMinute, config->InitialDelay());
+ }
+}
+
} // namespace component_updater
diff --git a/chromium/components/component_updater/timer.cc b/chromium/components/component_updater/timer.cc
index 39948387c8c..966c3ace467 100644
--- a/chromium/components/component_updater/timer.cc
+++ b/chromium/components/component_updater/timer.cc
@@ -9,8 +9,7 @@
namespace component_updater {
-Timer::Timer() : timer_(false, false) {
-}
+Timer::Timer() {}
Timer::~Timer() {
DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/chromium/components/component_updater/timer.h b/chromium/components/component_updater/timer.h
index 8b728996a96..12c6c5107cc 100644
--- a/chromium/components/component_updater/timer.h
+++ b/chromium/components/component_updater/timer.h
@@ -29,7 +29,7 @@ class Timer {
base::ThreadChecker thread_checker_;
- base::Timer timer_;
+ base::OneShotTimer timer_;
base::TimeDelta delay_;
base::Closure user_task_;
diff --git a/chromium/components/component_updater/timer_update_scheduler.cc b/chromium/components/component_updater/timer_update_scheduler.cc
new file mode 100644
index 00000000000..51708525631
--- /dev/null
+++ b/chromium/components/component_updater/timer_update_scheduler.cc
@@ -0,0 +1,27 @@
+// 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/component_updater/timer_update_scheduler.h"
+
+namespace component_updater {
+
+TimerUpdateScheduler::TimerUpdateScheduler() = default;
+TimerUpdateScheduler::~TimerUpdateScheduler() = default;
+
+void TimerUpdateScheduler::Schedule(const base::TimeDelta& initial_delay,
+ const base::TimeDelta& delay,
+ const UserTask& user_task,
+ const OnStopTaskCallback& on_stop) {
+ timer_.Start(
+ initial_delay, delay,
+ base::BindRepeating(
+ [](const UserTask& user_task) { user_task.Run(base::DoNothing()); },
+ user_task));
+}
+
+void TimerUpdateScheduler::Stop() {
+ timer_.Stop();
+}
+
+} // namespace component_updater \ No newline at end of file
diff --git a/chromium/components/component_updater/timer_update_scheduler.h b/chromium/components/component_updater/timer_update_scheduler.h
new file mode 100644
index 00000000000..3394572c71d
--- /dev/null
+++ b/chromium/components/component_updater/timer_update_scheduler.h
@@ -0,0 +1,37 @@
+// 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_COMPONENT_UPDATER_TIMER_UPDATE_SCHEDULER_H_
+#define COMPONENTS_COMPONENT_UPDATER_TIMER_UPDATE_SCHEDULER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "components/component_updater/timer.h"
+#include "components/component_updater/update_scheduler.h"
+
+namespace component_updater {
+
+// Scheduler that uses base::Timer to schedule updates.
+class TimerUpdateScheduler : public UpdateScheduler {
+ public:
+ TimerUpdateScheduler();
+ ~TimerUpdateScheduler() override;
+
+ // UpdateScheduler:
+ void Schedule(const base::TimeDelta& initial_delay,
+ const base::TimeDelta& delay,
+ const UserTask& user_task,
+ const OnStopTaskCallback& on_stop) override;
+ void Stop() override;
+
+ private:
+ Timer timer_;
+ base::RepeatingClosure user_task_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimerUpdateScheduler);
+};
+
+} // namespace component_updater
+
+#endif // COMPONENTS_COMPONENT_UPDATER_TIMER_UPDATE_SCHEDULER_H_
diff --git a/chromium/components/component_updater/update_scheduler.h b/chromium/components/component_updater/update_scheduler.h
new file mode 100644
index 00000000000..ce42eedc506
--- /dev/null
+++ b/chromium/components/component_updater/update_scheduler.h
@@ -0,0 +1,39 @@
+// 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_COMPONENT_UPDATER_UPDATE_SCHEDULER_H_
+#define COMPONENTS_COMPONENT_UPDATER_UPDATE_SCHEDULER_H_
+
+#include "base/callback_forward.h"
+#include "base/time/time.h"
+
+namespace component_updater {
+
+// Abstract interface for an update task scheduler.
+class UpdateScheduler {
+ public:
+ using OnFinishedCallback = base::OnceCallback<void()>;
+ // Type of task to be run by the scheduler. The task can start asynchronous
+ // operations and must call |on_finished| when all operations have completed.
+ using UserTask =
+ base::RepeatingCallback<void(OnFinishedCallback on_finished)>;
+ using OnStopTaskCallback = base::RepeatingCallback<void()>;
+
+ virtual ~UpdateScheduler() = default;
+
+ // Schedules |user_task| to be run periodically with at least an interval of
+ // |delay|. The first time |user_task| will be run after at least
+ // |initial_delay|. If the execution of |user_task| must be stopped before it
+ // called its |on_finished| callback, |on_stop| will be called.
+ virtual void Schedule(const base::TimeDelta& initial_delay,
+ const base::TimeDelta& delay,
+ const UserTask& user_task,
+ const OnStopTaskCallback& on_stop) = 0;
+ // Stops to periodically run |user_task| previously scheduled with |Schedule|.
+ virtual void Stop() = 0;
+};
+
+} // namespace component_updater
+
+#endif // COMPONENTS_COMPONENT_UPDATER_UPDATE_SCHEDULER_H_
diff --git a/chromium/components/components_strings.grd b/chromium/components/components_strings.grd
index 4ee9c7b4d40..2fbd4cc3ee3 100644
--- a/chromium/components/components_strings.grd
+++ b/chromium/components/components_strings.grd
@@ -186,7 +186,6 @@
<part file="autofill_strings.grdp" />
<part file="bookmark_bar_strings.grdp" />
<part file="bookmark_component_strings.grdp" />
- <part file="browser_sync_strings.grdp" />
<part file="browsing_data_strings.grdp" />
<part file="components_settings_strings.grdp" />
<part file="crash_strings.grdp" />
@@ -197,6 +196,7 @@
<part file="history_strings.grdp" />
<part file="login_dialog_strings.grdp" />
<part file="new_or_sad_tab_strings.grdp" />
+ <part file="nux_google_apps_strings.grdp" />
<part file="ntp_snippets_strings.grdp" />
<part file="omnibox_strings.grdp" />
<part file="page_info_strings.grdp" />
diff --git a/chromium/components/consent_auditor/BUILD.gn b/chromium/components/consent_auditor/BUILD.gn
index 5667f6440ac..b11c6d0d221 100644
--- a/chromium/components/consent_auditor/BUILD.gn
+++ b/chromium/components/consent_auditor/BUILD.gn
@@ -10,6 +10,11 @@ static_library("consent_auditor") {
sources = [
"consent_auditor.cc",
"consent_auditor.h",
+ "consent_auditor_impl.cc",
+ "consent_auditor_impl.h",
+ "consent_sync_bridge.h",
+ "consent_sync_bridge_impl.cc",
+ "consent_sync_bridge_impl.h",
"pref_names.cc",
"pref_names.h",
]
@@ -45,7 +50,8 @@ source_set("test_support") {
source_set("unit_tests") {
testonly = true
sources = [
- "consent_auditor_unittest.cc",
+ "consent_auditor_impl_unittest.cc",
+ "consent_sync_bridge_impl_unittest.cc",
]
deps = [
@@ -53,6 +59,10 @@ source_set("unit_tests") {
"//base/test:test_support",
"//components/prefs:test_support",
"//components/sync",
+ "//components/sync:test_support_driver",
+ "//components/sync:test_support_model",
+ "//components/variations:test_support",
+ "//testing/gmock",
"//testing/gtest",
]
}
diff --git a/chromium/components/consent_auditor/DEPS b/chromium/components/consent_auditor/DEPS
index e85ccffd280..c6d9382d566 100644
--- a/chromium/components/consent_auditor/DEPS
+++ b/chromium/components/consent_auditor/DEPS
@@ -2,4 +2,5 @@ include_rules = [
"+components/keyed_service",
"+components/prefs",
"+components/sync",
+ "+components/variations",
]
diff --git a/chromium/components/consent_auditor/consent_auditor.cc b/chromium/components/consent_auditor/consent_auditor.cc
index 24f7f1fc47f..2d33d93fe13 100644
--- a/chromium/components/consent_auditor/consent_auditor.cc
+++ b/chromium/components/consent_auditor/consent_auditor.cc
@@ -4,152 +4,10 @@
#include "components/consent_auditor/consent_auditor.h"
-#include <memory>
-
-#include "base/metrics/histogram_macros.h"
-#include "base/values.h"
-#include "components/consent_auditor/pref_names.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/pref_service.h"
-#include "components/prefs/scoped_user_pref_update.h"
-#include "components/sync/driver/sync_driver_switches.h"
-#include "components/sync/model/model_type_sync_bridge.h"
-#include "components/sync/user_events/user_event_service.h"
-
-using sync_pb::UserConsentTypes;
-using sync_pb::UserEventSpecifics;
-
namespace consent_auditor {
-namespace {
-
-const char kLocalConsentDescriptionKey[] = "description";
-const char kLocalConsentConfirmationKey[] = "confirmation";
-const char kLocalConsentVersionKey[] = "version";
-const char kLocalConsentLocaleKey[] = "locale";
-
-UserEventSpecifics::UserConsent::Feature FeatureToProtoEnum(
- consent_auditor::Feature feature) {
- switch (feature) {
- case consent_auditor::Feature::CHROME_SYNC:
- return UserEventSpecifics::UserConsent::CHROME_SYNC;
- case consent_auditor::Feature::PLAY_STORE:
- return UserEventSpecifics::UserConsent::PLAY_STORE;
- case consent_auditor::Feature::BACKUP_AND_RESTORE:
- return UserEventSpecifics::UserConsent::BACKUP_AND_RESTORE;
- case consent_auditor::Feature::GOOGLE_LOCATION_SERVICE:
- return UserEventSpecifics::UserConsent::GOOGLE_LOCATION_SERVICE;
- }
- NOTREACHED();
- return UserEventSpecifics::UserConsent::FEATURE_UNSPECIFIED;
-}
-
-UserConsentTypes::ConsentStatus StatusToProtoEnum(
- consent_auditor::ConsentStatus status) {
- switch (status) {
- case consent_auditor::ConsentStatus::NOT_GIVEN:
- return UserConsentTypes::NOT_GIVEN;
- case consent_auditor::ConsentStatus::GIVEN:
- return UserConsentTypes::GIVEN;
- }
- NOTREACHED();
- return UserConsentTypes::CONSENT_STATUS_UNSPECIFIED;
-}
-
-} // namespace
-
-ConsentAuditor::ConsentAuditor(PrefService* pref_service,
- syncer::UserEventService* user_event_service,
- const std::string& app_version,
- const std::string& app_locale)
- : pref_service_(pref_service),
- user_event_service_(user_event_service),
- app_version_(app_version),
- app_locale_(app_locale) {
- DCHECK(pref_service_);
- DCHECK(user_event_service_);
-}
+ConsentAuditor::ConsentAuditor() {}
ConsentAuditor::~ConsentAuditor() {}
-void ConsentAuditor::Shutdown() {
- user_event_service_ = nullptr;
-}
-
-// static
-void ConsentAuditor::RegisterProfilePrefs(PrefRegistrySimple* registry) {
- registry->RegisterDictionaryPref(prefs::kLocalConsentsDictionary);
-}
-
-void ConsentAuditor::RecordGaiaConsent(
- const std::string& account_id,
- Feature feature,
- const std::vector<int>& description_grd_ids,
- int confirmation_grd_id,
- ConsentStatus status) {
- DCHECK(!account_id.empty()) << "No signed-in account specified.";
-
- if (!base::FeatureList::IsEnabled(switches::kSyncUserConsentEvents))
- return;
-
- DCHECK_LE(feature, consent_auditor::Feature::FEATURE_LAST);
-
- switch (status) {
- case ConsentStatus::GIVEN:
- UMA_HISTOGRAM_ENUMERATION(
- "Privacy.ConsentAuditor.ConsentGiven.Feature", feature,
- static_cast<int>(consent_auditor::Feature::FEATURE_LAST) + 1);
- break;
- case ConsentStatus::NOT_GIVEN:
- UMA_HISTOGRAM_ENUMERATION(
- "Privacy.ConsentAuditor.ConsentNotGiven.Feature", feature,
- static_cast<int>(consent_auditor::Feature::FEATURE_LAST) + 1);
- break;
- }
-
- // TODO(msramek): Pass in the actual account id.
- std::unique_ptr<sync_pb::UserEventSpecifics> specifics = ConstructUserConsent(
- account_id, feature, description_grd_ids, confirmation_grd_id, status);
- user_event_service_->RecordUserEvent(std::move(specifics));
-}
-
-std::unique_ptr<sync_pb::UserEventSpecifics>
-ConsentAuditor::ConstructUserConsent(
- const std::string& account_id,
- Feature feature,
- const std::vector<int>& description_grd_ids,
- int confirmation_grd_id,
- ConsentStatus status) {
- auto specifics = std::make_unique<sync_pb::UserEventSpecifics>();
- specifics->set_event_time_usec(
- base::Time::Now().since_origin().InMicroseconds());
- auto* consent = specifics->mutable_user_consent();
- consent->set_account_id(account_id);
- consent->set_feature(FeatureToProtoEnum(feature));
- for (int id : description_grd_ids) {
- consent->add_description_grd_ids(id);
- }
- consent->set_confirmation_grd_id(confirmation_grd_id);
- consent->set_locale(app_locale_);
- consent->set_status(StatusToProtoEnum(status));
- return specifics;
-}
-
-void ConsentAuditor::RecordLocalConsent(const std::string& feature,
- const std::string& description_text,
- const std::string& confirmation_text) {
- DictionaryPrefUpdate consents_update(pref_service_,
- prefs::kLocalConsentsDictionary);
- base::DictionaryValue* consents = consents_update.Get();
- DCHECK(consents);
-
- base::DictionaryValue record;
- record.SetKey(kLocalConsentDescriptionKey, base::Value(description_text));
- record.SetKey(kLocalConsentConfirmationKey, base::Value(confirmation_text));
- record.SetKey(kLocalConsentVersionKey, base::Value(app_version_));
- record.SetKey(kLocalConsentLocaleKey, base::Value(app_locale_));
-
- consents->SetKey(feature, std::move(record));
-}
-
} // namespace consent_auditor
diff --git a/chromium/components/consent_auditor/consent_auditor.h b/chromium/components/consent_auditor/consent_auditor.h
index e91a547b4af..f803b642270 100644
--- a/chromium/components/consent_auditor/consent_auditor.h
+++ b/chromium/components/consent_auditor/consent_auditor.h
@@ -10,19 +10,13 @@
#include <vector>
#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
#include "components/keyed_service/core/keyed_service.h"
namespace syncer {
-class UserEventService;
+class ModelTypeControllerDelegate;
}
-namespace sync_pb {
-class UserEventSpecifics;
-}
-
-class PrefService;
-class PrefRegistrySimple;
-
namespace consent_auditor {
// Feature for which a consent moment is to be recorded.
@@ -38,8 +32,9 @@ enum class Feature {
PLAY_STORE = 1,
BACKUP_AND_RESTORE = 2,
GOOGLE_LOCATION_SERVICE = 3,
+ CHROME_UNIFIED_CONSENT = 4,
- FEATURE_LAST = GOOGLE_LOCATION_SERVICE
+ FEATURE_LAST = CHROME_UNIFIED_CONSENT
};
// Whether a consent is given or not given.
@@ -48,20 +43,13 @@ enum class Feature {
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.consent_auditor
enum class ConsentStatus { NOT_GIVEN, GIVEN };
+// TODO(vitaliii): Delete user-event-related code once USER_CONSENTS type is
+// fully launched.
class ConsentAuditor : public KeyedService {
public:
- ConsentAuditor(PrefService* pref_service,
- syncer::UserEventService* user_event_service,
- const std::string& app_version,
- const std::string& app_locale);
+ ConsentAuditor();
~ConsentAuditor() override;
- // KeyedService:
- void Shutdown() override;
-
- // Registers the preferences needed by this service.
- static void RegisterProfilePrefs(PrefRegistrySimple* registry);
-
// Records a consent for |feature| for the signed-in GAIA account with
// the ID |account_id| (as defined in AccountInfo).
// Consent text consisted of strings with |consent_grd_ids|, and the UI
@@ -71,29 +59,21 @@ class ConsentAuditor : public KeyedService {
Feature feature,
const std::vector<int>& description_grd_ids,
int confirmation_grd_id,
- ConsentStatus status);
+ ConsentStatus status) = 0;
// Records that the user consented to a |feature|. The user was presented with
// |description_text| and accepted it by interacting |confirmation_text|
// (e.g. clicking on a button; empty if not applicable).
// Returns true if successful.
- void RecordLocalConsent(const std::string& feature,
- const std::string& description_text,
- const std::string& confirmation_text);
-
- private:
- std::unique_ptr<sync_pb::UserEventSpecifics> ConstructUserConsent(
- const std::string& account_id,
- Feature feature,
- const std::vector<int>& description_grd_ids,
- int confirmation_grd_id,
- ConsentStatus status);
+ virtual void RecordLocalConsent(const std::string& feature,
+ const std::string& description_text,
+ const std::string& confirmation_text) = 0;
- PrefService* pref_service_;
- syncer::UserEventService* user_event_service_;
- std::string app_version_;
- std::string app_locale_;
+ // Returns the underlying Sync integration point.
+ virtual base::WeakPtr<syncer::ModelTypeControllerDelegate>
+ GetControllerDelegateOnUIThread() = 0;
+ private:
DISALLOW_COPY_AND_ASSIGN(ConsentAuditor);
};
diff --git a/chromium/components/consent_auditor/consent_auditor_impl.cc b/chromium/components/consent_auditor/consent_auditor_impl.cc
new file mode 100644
index 00000000000..913319df10e
--- /dev/null
+++ b/chromium/components/consent_auditor/consent_auditor_impl.cc
@@ -0,0 +1,230 @@
+// 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/consent_auditor/consent_auditor_impl.h"
+
+#include <memory>
+
+#include "base/metrics/histogram_macros.h"
+#include "base/values.h"
+#include "components/consent_auditor/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/sync/driver/sync_driver_switches.h"
+#include "components/sync/model/model_type_sync_bridge.h"
+#include "components/sync/user_events/user_event_service.h"
+
+using sync_pb::UserConsentTypes;
+using sync_pb::UserConsentSpecifics;
+using sync_pb::UserEventSpecifics;
+
+namespace consent_auditor {
+
+namespace {
+
+const char kLocalConsentDescriptionKey[] = "description";
+const char kLocalConsentConfirmationKey[] = "confirmation";
+const char kLocalConsentVersionKey[] = "version";
+const char kLocalConsentLocaleKey[] = "locale";
+
+bool IsSeparateConsentTypeEnabled() {
+ return base::FeatureList::IsEnabled(switches::kSyncUserConsentSeparateType);
+}
+
+UserEventSpecifics::UserConsent::Feature FeatureToUserEventProtoEnum(
+ consent_auditor::Feature feature) {
+ switch (feature) {
+ case consent_auditor::Feature::CHROME_SYNC:
+ return UserEventSpecifics::UserConsent::CHROME_SYNC;
+ case consent_auditor::Feature::PLAY_STORE:
+ return UserEventSpecifics::UserConsent::PLAY_STORE;
+ case consent_auditor::Feature::BACKUP_AND_RESTORE:
+ return UserEventSpecifics::UserConsent::BACKUP_AND_RESTORE;
+ case consent_auditor::Feature::GOOGLE_LOCATION_SERVICE:
+ return UserEventSpecifics::UserConsent::GOOGLE_LOCATION_SERVICE;
+ case consent_auditor::Feature::CHROME_UNIFIED_CONSENT:
+ return UserEventSpecifics::UserConsent::CHROME_UNIFIED_CONSENT;
+ }
+ NOTREACHED();
+ return UserEventSpecifics::UserConsent::FEATURE_UNSPECIFIED;
+}
+
+UserConsentTypes::ConsentStatus StatusToProtoEnum(
+ consent_auditor::ConsentStatus status) {
+ switch (status) {
+ case consent_auditor::ConsentStatus::NOT_GIVEN:
+ return UserConsentTypes::NOT_GIVEN;
+ case consent_auditor::ConsentStatus::GIVEN:
+ return UserConsentTypes::GIVEN;
+ }
+ NOTREACHED();
+ return UserConsentTypes::CONSENT_STATUS_UNSPECIFIED;
+}
+
+UserConsentSpecifics::Feature FeatureToUserConsentProtoEnum(
+ consent_auditor::Feature feature) {
+ switch (feature) {
+ case consent_auditor::Feature::CHROME_SYNC:
+ return UserConsentSpecifics::CHROME_SYNC;
+ case consent_auditor::Feature::PLAY_STORE:
+ return UserConsentSpecifics::PLAY_STORE;
+ case consent_auditor::Feature::BACKUP_AND_RESTORE:
+ return UserConsentSpecifics::BACKUP_AND_RESTORE;
+ case consent_auditor::Feature::GOOGLE_LOCATION_SERVICE:
+ return UserConsentSpecifics::GOOGLE_LOCATION_SERVICE;
+ case consent_auditor::Feature::CHROME_UNIFIED_CONSENT:
+ return UserConsentSpecifics::CHROME_UNIFIED_CONSENT;
+ }
+ NOTREACHED();
+ return UserConsentSpecifics::FEATURE_UNSPECIFIED;
+}
+
+} // namespace
+
+ConsentAuditorImpl::ConsentAuditorImpl(
+ PrefService* pref_service,
+ std::unique_ptr<syncer::ConsentSyncBridge> consent_sync_bridge,
+ syncer::UserEventService* user_event_service,
+ const std::string& app_version,
+ const std::string& app_locale)
+ : pref_service_(pref_service),
+ consent_sync_bridge_(std::move(consent_sync_bridge)),
+ user_event_service_(user_event_service),
+ app_version_(app_version),
+ app_locale_(app_locale) {
+ if (IsSeparateConsentTypeEnabled()) {
+ DCHECK(consent_sync_bridge_ && !user_event_service_);
+ } else {
+ DCHECK(user_event_service_ && !consent_sync_bridge_);
+ }
+ DCHECK(pref_service_);
+}
+
+ConsentAuditorImpl::~ConsentAuditorImpl() {}
+
+void ConsentAuditorImpl::Shutdown() {
+ user_event_service_ = nullptr;
+}
+
+// static
+void ConsentAuditorImpl::RegisterProfilePrefs(PrefRegistrySimple* registry) {
+ registry->RegisterDictionaryPref(prefs::kLocalConsentsDictionary);
+}
+
+void ConsentAuditorImpl::RecordGaiaConsent(
+ const std::string& account_id,
+ Feature feature,
+ const std::vector<int>& description_grd_ids,
+ int confirmation_grd_id,
+ ConsentStatus status) {
+ DCHECK(!account_id.empty()) << "No signed-in account specified.";
+
+ if (!base::FeatureList::IsEnabled(switches::kSyncUserConsentEvents))
+ return;
+
+ DCHECK_LE(feature, consent_auditor::Feature::FEATURE_LAST);
+
+ switch (status) {
+ case ConsentStatus::GIVEN:
+ UMA_HISTOGRAM_ENUMERATION(
+ "Privacy.ConsentAuditor.ConsentGiven.Feature", feature,
+ static_cast<int>(consent_auditor::Feature::FEATURE_LAST) + 1);
+ break;
+ case ConsentStatus::NOT_GIVEN:
+ UMA_HISTOGRAM_ENUMERATION(
+ "Privacy.ConsentAuditor.ConsentNotGiven.Feature", feature,
+ static_cast<int>(consent_auditor::Feature::FEATURE_LAST) + 1);
+ break;
+ }
+
+ if (IsSeparateConsentTypeEnabled()) {
+ // TODO(msramek): Pass in the actual account id.
+ std::unique_ptr<sync_pb::UserConsentSpecifics> specifics =
+ ConstructUserConsentSpecifics(account_id, feature, description_grd_ids,
+ confirmation_grd_id, status);
+ consent_sync_bridge_->RecordConsent(std::move(specifics));
+ } else {
+ // TODO(msramek): Pass in the actual account id.
+ std::unique_ptr<sync_pb::UserEventSpecifics> specifics =
+ ConstructUserEventSpecifics(account_id, feature, description_grd_ids,
+ confirmation_grd_id, status);
+ user_event_service_->RecordUserEvent(std::move(specifics));
+ }
+}
+
+std::unique_ptr<sync_pb::UserEventSpecifics>
+ConsentAuditorImpl::ConstructUserEventSpecifics(
+ const std::string& account_id,
+ Feature feature,
+ const std::vector<int>& description_grd_ids,
+ int confirmation_grd_id,
+ ConsentStatus status) {
+ DCHECK(!IsSeparateConsentTypeEnabled());
+
+ auto specifics = std::make_unique<sync_pb::UserEventSpecifics>();
+ specifics->set_event_time_usec(
+ base::Time::Now().since_origin().InMicroseconds());
+ auto* consent = specifics->mutable_user_consent();
+ consent->set_account_id(account_id);
+ consent->set_feature(FeatureToUserEventProtoEnum(feature));
+ for (int id : description_grd_ids) {
+ consent->add_description_grd_ids(id);
+ }
+ consent->set_confirmation_grd_id(confirmation_grd_id);
+ consent->set_locale(app_locale_);
+ consent->set_status(StatusToProtoEnum(status));
+ return specifics;
+}
+
+std::unique_ptr<sync_pb::UserConsentSpecifics>
+ConsentAuditorImpl::ConstructUserConsentSpecifics(
+ const std::string& account_id,
+ Feature feature,
+ const std::vector<int>& description_grd_ids,
+ int confirmation_grd_id,
+ ConsentStatus status) {
+ DCHECK(IsSeparateConsentTypeEnabled());
+
+ auto specifics = std::make_unique<sync_pb::UserConsentSpecifics>();
+ specifics->set_client_consent_time_usec(
+ base::Time::Now().since_origin().InMicroseconds());
+ specifics->set_account_id(account_id);
+ specifics->set_feature(FeatureToUserConsentProtoEnum(feature));
+ for (int id : description_grd_ids) {
+ specifics->add_description_grd_ids(id);
+ }
+ specifics->set_confirmation_grd_id(confirmation_grd_id);
+ specifics->set_locale(app_locale_);
+ specifics->set_status(StatusToProtoEnum(status));
+ return specifics;
+}
+
+void ConsentAuditorImpl::RecordLocalConsent(
+ const std::string& feature,
+ const std::string& description_text,
+ const std::string& confirmation_text) {
+ DictionaryPrefUpdate consents_update(pref_service_,
+ prefs::kLocalConsentsDictionary);
+ base::DictionaryValue* consents = consents_update.Get();
+ DCHECK(consents);
+
+ base::DictionaryValue record;
+ record.SetKey(kLocalConsentDescriptionKey, base::Value(description_text));
+ record.SetKey(kLocalConsentConfirmationKey, base::Value(confirmation_text));
+ record.SetKey(kLocalConsentVersionKey, base::Value(app_version_));
+ record.SetKey(kLocalConsentLocaleKey, base::Value(app_locale_));
+
+ consents->SetKey(feature, std::move(record));
+}
+
+base::WeakPtr<syncer::ModelTypeControllerDelegate>
+ConsentAuditorImpl::GetControllerDelegateOnUIThread() {
+ if (consent_sync_bridge_) {
+ return consent_sync_bridge_->GetControllerDelegateOnUIThread();
+ }
+ return base::WeakPtr<syncer::ModelTypeControllerDelegate>();
+}
+
+} // namespace consent_auditor
diff --git a/chromium/components/consent_auditor/consent_auditor_impl.h b/chromium/components/consent_auditor/consent_auditor_impl.h
new file mode 100644
index 00000000000..acad0161455
--- /dev/null
+++ b/chromium/components/consent_auditor/consent_auditor_impl.h
@@ -0,0 +1,102 @@
+// 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_CONSENT_AUDITOR_CONSENT_AUDITOR_IMPL_H_
+#define COMPONENTS_CONSENT_AUDITOR_CONSENT_AUDITOR_IMPL_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/consent_auditor/consent_auditor.h"
+#include "components/consent_auditor/consent_sync_bridge.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace syncer {
+class ModelTypeControllerDelegate;
+class UserEventService;
+} // namespace syncer
+
+namespace sync_pb {
+class UserConsentSpecifics;
+class UserEventSpecifics;
+} // namespace sync_pb
+
+class PrefService;
+class PrefRegistrySimple;
+
+namespace consent_auditor {
+
+// TODO(vitaliii): Delete user-event-related code once USER_CONSENTS type is
+// fully launched.
+class ConsentAuditorImpl : public ConsentAuditor {
+ public:
+ ConsentAuditorImpl(
+ PrefService* pref_service,
+ std::unique_ptr<syncer::ConsentSyncBridge> consent_sync_bridge,
+ syncer::UserEventService* user_event_service,
+ const std::string& app_version,
+ const std::string& app_locale);
+ ~ConsentAuditorImpl() override;
+
+ // KeyedService (through ConsentAuditor) implementation.
+ void Shutdown() override;
+
+ // Registers the preferences needed by this service.
+ static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+ // Consent auditor implementation.
+
+ // Records a consent for |feature| for the signed-in GAIA account with
+ // the ID |account_id| (as defined in AccountInfo).
+ // Consent text consisted of strings with |consent_grd_ids|, and the UI
+ // element the user clicked had the ID |confirmation_grd_id|.
+ // Whether the consent was GIVEN or NOT_GIVEN is passed as |status|.
+ void RecordGaiaConsent(const std::string& account_id,
+ Feature feature,
+ const std::vector<int>& description_grd_ids,
+ int confirmation_grd_id,
+ ConsentStatus status) override;
+
+ // Records that the user consented to a |feature|. The user was presented with
+ // |description_text| and accepted it by interacting |confirmation_text|
+ // (e.g. clicking on a button; empty if not applicable).
+ // Returns true if successful.
+ void RecordLocalConsent(const std::string& feature,
+ const std::string& description_text,
+ const std::string& confirmation_text) override;
+
+ // Returns the underlying Sync integration point.
+ base::WeakPtr<syncer::ModelTypeControllerDelegate>
+ GetControllerDelegateOnUIThread() override;
+
+ private:
+ std::unique_ptr<sync_pb::UserEventSpecifics> ConstructUserEventSpecifics(
+ const std::string& account_id,
+ Feature feature,
+ const std::vector<int>& description_grd_ids,
+ int confirmation_grd_id,
+ ConsentStatus status);
+
+ std::unique_ptr<sync_pb::UserConsentSpecifics> ConstructUserConsentSpecifics(
+ const std::string& account_id,
+ Feature feature,
+ const std::vector<int>& description_grd_ids,
+ int confirmation_grd_id,
+ ConsentStatus status);
+
+ PrefService* pref_service_;
+ std::unique_ptr<syncer::ConsentSyncBridge> consent_sync_bridge_;
+ syncer::UserEventService* user_event_service_;
+ std::string app_version_;
+ std::string app_locale_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConsentAuditorImpl);
+};
+
+} // namespace consent_auditor
+
+#endif // COMPONENTS_CONSENT_AUDITOR_CONSENT_AUDITOR_IMPL_H_
diff --git a/chromium/components/consent_auditor/consent_auditor_unittest.cc b/chromium/components/consent_auditor/consent_auditor_impl_unittest.cc
index d23f278f8d5..b0d9ec655b0 100644
--- a/chromium/components/consent_auditor/consent_auditor_unittest.cc
+++ b/chromium/components/consent_auditor/consent_auditor_impl_unittest.cc
@@ -1,18 +1,23 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// 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/consent_auditor/consent_auditor.h"
+#include "components/consent_auditor/consent_auditor_impl.h"
+#include <map>
#include <memory>
+#include <string>
+#include "base/memory/weak_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "components/consent_auditor/pref_names.h"
#include "components/prefs/testing_pref_service.h"
#include "components/sync/driver/sync_driver_switches.h"
+#include "components/sync/model/fake_model_type_controller_delegate.h"
#include "components/sync/user_events/fake_user_event_service.h"
#include "testing/gtest/include/gtest/gtest.h"
+using sync_pb::UserConsentSpecifics;
using sync_pb::UserEventSpecifics;
namespace consent_auditor {
@@ -65,44 +70,107 @@ void LoadEntriesFromLocalConsentRecord(const base::Value* consents,
*locale = locale_entry->GetString();
}
+class FakeConsentSyncBridge : public syncer::ConsentSyncBridge {
+ public:
+ ~FakeConsentSyncBridge() override = default;
+
+ // ConsentSyncBridge implementation.
+ void RecordConsent(
+ std::unique_ptr<sync_pb::UserConsentSpecifics> specifics) override {
+ DCHECK(specifics);
+ recorded_user_consents_.push_back(*specifics);
+ }
+
+ base::WeakPtr<syncer::ModelTypeControllerDelegate>
+ GetControllerDelegateOnUIThread() override {
+ return delegate_;
+ }
+
+ // Fake methods.
+ void SetControllerDelegateOnUIThread(
+ base::WeakPtr<syncer::ModelTypeControllerDelegate> delegate) {
+ delegate_ = delegate;
+ }
+
+ const std::vector<UserConsentSpecifics>& GetRecordedUserConsents() const {
+ return recorded_user_consents_;
+ }
+
+ private:
+ base::WeakPtr<syncer::ModelTypeControllerDelegate> delegate_;
+ std::vector<UserConsentSpecifics> recorded_user_consents_;
+};
+
} // namespace
-class ConsentAuditorTest : public testing::Test {
+class ConsentAuditorImplTest : public testing::Test {
public:
void SetUp() override {
pref_service_ = std::make_unique<TestingPrefServiceSimple>();
- user_event_service_ = std::make_unique<syncer::FakeUserEventService>();
- ConsentAuditor::RegisterProfilePrefs(pref_service_->registry());
- consent_auditor_ = std::make_unique<ConsentAuditor>(
- pref_service_.get(), user_event_service_.get(), kCurrentAppVersion,
- kCurrentAppLocale);
+ if (base::FeatureList::IsEnabled(switches::kSyncUserConsentSeparateType)) {
+ consent_sync_bridge_ = std::make_unique<FakeConsentSyncBridge>();
+ } else {
+ user_event_service_ = std::make_unique<syncer::FakeUserEventService>();
+ }
+ ConsentAuditorImpl::RegisterProfilePrefs(pref_service_->registry());
+ app_version_ = kCurrentAppVersion;
+ app_locale_ = kCurrentAppLocale;
+ BuildConsentAuditorImpl();
}
- void UpdateAppVersionAndLocale(const std::string& new_product_version,
- const std::string& new_app_locale) {
- // We'll have to recreate |consent_auditor| in order to update the version
- // and locale. This is not a problem, as in reality we'd have to restart
- // Chrome to update both, let alone just recreate this class.
- consent_auditor_ = std::make_unique<ConsentAuditor>(
- pref_service_.get(), user_event_service_.get(), new_product_version,
- new_app_locale);
+ // TODO(vitaliii): Add a real builder class instead.
+ void BuildConsentAuditorImpl() {
+ consent_auditor_ = std::make_unique<ConsentAuditorImpl>(
+ pref_service_.get(), std::move(consent_sync_bridge_),
+ user_event_service_.get(), app_version_, app_locale_);
}
- ConsentAuditor* consent_auditor() { return consent_auditor_.get(); }
+ // These have no effect before |BuildConsentAuditorImpl|.
+ void SetAppVersion(const std::string& new_app_version) {
+ app_version_ = new_app_version;
+ }
+ void SetAppLocale(const std::string& new_app_locale) {
+ app_locale_ = new_app_locale;
+ }
+ void SetConsentSyncBridge(std::unique_ptr<syncer::ConsentSyncBridge> bridge) {
+ consent_sync_bridge_ = std::move(bridge);
+ }
+ void SetUserEventService(
+ std::unique_ptr<syncer::FakeUserEventService> service) {
+ user_event_service_ = std::move(service);
+ }
- PrefService* pref_service() const { return pref_service_.get(); }
+ void SetIsSeparateConsentTypeEnabledFeature(bool new_value) {
+ feature_list_.InitWithFeatureState(switches::kSyncUserConsentSeparateType,
+ new_value);
+ }
+ ConsentAuditorImpl* consent_auditor() { return consent_auditor_.get(); }
+ PrefService* pref_service() const { return pref_service_.get(); }
syncer::FakeUserEventService* user_event_service() {
return user_event_service_.get();
}
private:
- std::unique_ptr<ConsentAuditor> consent_auditor_;
+ std::unique_ptr<ConsentAuditorImpl> consent_auditor_;
+
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
std::unique_ptr<syncer::FakeUserEventService> user_event_service_;
+ std::string app_version_;
+ std::string app_locale_;
+ std::unique_ptr<syncer::ConsentSyncBridge> consent_sync_bridge_;
+
+ base::test::ScopedFeatureList feature_list_;
};
-TEST_F(ConsentAuditorTest, LocalConsentPrefRepresentation) {
+TEST_F(ConsentAuditorImplTest, LocalConsentPrefRepresentation) {
+ SetIsSeparateConsentTypeEnabledFeature(true);
+ SetAppVersion(kCurrentAppVersion);
+ SetAppLocale(kCurrentAppLocale);
+ SetConsentSyncBridge(std::make_unique<FakeConsentSyncBridge>());
+ SetUserEventService(nullptr);
+ BuildConsentAuditorImpl();
+
// No consents are written at first.
EXPECT_FALSE(pref_service()->HasPrefPath(prefs::kLocalConsentsDictionary));
@@ -149,7 +217,14 @@ TEST_F(ConsentAuditorTest, LocalConsentPrefRepresentation) {
const std::string kFeature2NewConfirmation = "Yes again.";
const std::string kFeature2NewAppVersion = "5.6.7.8";
const std::string kFeature2NewAppLocale = "de";
- UpdateAppVersionAndLocale(kFeature2NewAppVersion, kFeature2NewAppLocale);
+ SetAppVersion(kFeature2NewAppVersion);
+ SetAppLocale(kFeature2NewAppLocale);
+ SetConsentSyncBridge(std::make_unique<FakeConsentSyncBridge>());
+ SetUserEventService(nullptr);
+ // We rebuild consent auditor to emulate restarting Chrome. This is the only
+ // way to change app version or app locale.
+ BuildConsentAuditorImpl();
+
consent_auditor()->RecordLocalConsent("feature2", kFeature2NewDescription,
kFeature2NewConfirmation);
LoadEntriesFromLocalConsentRecord(consents, "feature2", &description,
@@ -163,14 +238,24 @@ TEST_F(ConsentAuditorTest, LocalConsentPrefRepresentation) {
EXPECT_EQ(2u, consents->size());
}
-TEST_F(ConsentAuditorTest, RecordingEnabled) {
+TEST_F(ConsentAuditorImplTest, RecordingEnabled) {
+ SetIsSeparateConsentTypeEnabledFeature(false);
+ SetConsentSyncBridge(nullptr);
+ SetUserEventService(std::make_unique<syncer::FakeUserEventService>());
+ BuildConsentAuditorImpl();
+
consent_auditor()->RecordGaiaConsent(kAccountId, Feature::CHROME_SYNC, {}, 0,
ConsentStatus::GIVEN);
auto& events = user_event_service()->GetRecordedUserEvents();
EXPECT_EQ(1U, events.size());
}
-TEST_F(ConsentAuditorTest, RecordingDisabled) {
+TEST_F(ConsentAuditorImplTest, RecordingDisabled) {
+ SetIsSeparateConsentTypeEnabledFeature(false);
+ SetConsentSyncBridge(nullptr);
+ SetUserEventService(std::make_unique<syncer::FakeUserEventService>());
+ BuildConsentAuditorImpl();
+
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(switches::kSyncUserConsentEvents);
consent_auditor()->RecordGaiaConsent(kAccountId, Feature::CHROME_SYNC, {}, 0,
@@ -179,7 +264,14 @@ TEST_F(ConsentAuditorTest, RecordingDisabled) {
EXPECT_EQ(0U, events.size());
}
-TEST_F(ConsentAuditorTest, RecordGaiaConsent) {
+TEST_F(ConsentAuditorImplTest, RecordGaiaConsentAsUserEvent) {
+ SetIsSeparateConsentTypeEnabledFeature(false);
+ SetConsentSyncBridge(nullptr);
+ SetUserEventService(std::make_unique<syncer::FakeUserEventService>());
+ SetAppVersion(kCurrentAppVersion);
+ SetAppLocale(kCurrentAppLocale);
+ BuildConsentAuditorImpl();
+
std::vector<int> kDescriptionMessageIds = {12, 37, 42};
int kConfirmationMessageId = 47;
base::Time t1 = base::Time::Now();
@@ -204,4 +296,75 @@ TEST_F(ConsentAuditorTest, RecordGaiaConsent) {
EXPECT_EQ(kCurrentAppLocale, consent.locale());
}
+TEST_F(ConsentAuditorImplTest, RecordGaiaConsentAsUserConsent) {
+ SetIsSeparateConsentTypeEnabledFeature(true);
+
+ auto wrapped_fake_bridge = std::make_unique<FakeConsentSyncBridge>();
+ FakeConsentSyncBridge* fake_bridge = wrapped_fake_bridge.get();
+
+ SetConsentSyncBridge(std::move(wrapped_fake_bridge));
+ SetUserEventService(nullptr);
+ SetAppVersion(kCurrentAppVersion);
+ SetAppLocale(kCurrentAppLocale);
+ BuildConsentAuditorImpl();
+
+ std::vector<int> kDescriptionMessageIds = {12, 37, 42};
+ int kConfirmationMessageId = 47;
+ // TODO(vitaliii): Inject a fake clock instead.
+ base::Time time_before = base::Time::Now();
+ consent_auditor()->RecordGaiaConsent(
+ kAccountId, Feature::CHROME_SYNC, kDescriptionMessageIds,
+ kConfirmationMessageId, ConsentStatus::GIVEN);
+ base::Time time_after = base::Time::Now();
+
+ std::vector<UserConsentSpecifics> consents =
+ fake_bridge->GetRecordedUserConsents();
+ ASSERT_EQ(1U, consents.size());
+ UserConsentSpecifics consent = consents[0];
+
+ EXPECT_LE(time_before.since_origin().InMicroseconds(),
+ consent.client_consent_time_usec());
+ EXPECT_GE(time_after.since_origin().InMicroseconds(),
+ consent.client_consent_time_usec());
+ EXPECT_EQ(kAccountId, consent.account_id());
+ EXPECT_EQ(UserConsentSpecifics::CHROME_SYNC, consent.feature());
+ EXPECT_EQ(3, consent.description_grd_ids_size());
+ EXPECT_EQ(kDescriptionMessageIds[0], consent.description_grd_ids(0));
+ EXPECT_EQ(kDescriptionMessageIds[1], consent.description_grd_ids(1));
+ EXPECT_EQ(kDescriptionMessageIds[2], consent.description_grd_ids(2));
+ EXPECT_EQ(kConfirmationMessageId, consent.confirmation_grd_id());
+ EXPECT_EQ(kCurrentAppLocale, consent.locale());
+}
+
+TEST_F(ConsentAuditorImplTest, ShouldReturnNoSyncDelegateWhenNoBridge) {
+ SetIsSeparateConsentTypeEnabledFeature(false);
+ SetConsentSyncBridge(nullptr);
+ SetUserEventService(std::make_unique<syncer::FakeUserEventService>());
+ BuildConsentAuditorImpl();
+
+ // There is no bridge (i.e. separate sync type for consents is disabled),
+ // thus, there should be no delegate as well.
+ EXPECT_EQ(nullptr, consent_auditor()->GetControllerDelegateOnUIThread());
+}
+
+TEST_F(ConsentAuditorImplTest, ShouldReturnSyncDelegateWhenBridgePresent) {
+ SetIsSeparateConsentTypeEnabledFeature(true);
+ auto fake_bridge = std::make_unique<FakeConsentSyncBridge>();
+
+ syncer::FakeModelTypeControllerDelegate fake_delegate(
+ syncer::ModelType::USER_CONSENTS);
+ auto expected_delegate_ptr = fake_delegate.GetWeakPtr();
+ DCHECK(expected_delegate_ptr);
+ fake_bridge->SetControllerDelegateOnUIThread(expected_delegate_ptr);
+
+ SetConsentSyncBridge(std::move(fake_bridge));
+ SetUserEventService(nullptr);
+ BuildConsentAuditorImpl();
+
+ // There is a bridge (i.e. separate sync type for consents is enabled), thus,
+ // there should be a delegate as well.
+ EXPECT_EQ(expected_delegate_ptr.get(),
+ consent_auditor()->GetControllerDelegateOnUIThread().get());
+}
+
} // namespace consent_auditor
diff --git a/chromium/components/consent_auditor/consent_sync_bridge.h b/chromium/components/consent_auditor/consent_sync_bridge.h
new file mode 100644
index 00000000000..49a2f3a5ad9
--- /dev/null
+++ b/chromium/components/consent_auditor/consent_sync_bridge.h
@@ -0,0 +1,30 @@
+// 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_CONSENT_AUDITOR_CONSENT_SYNC_BRIDGE_H_
+#define COMPONENTS_CONSENT_AUDITOR_CONSENT_SYNC_BRIDGE_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "components/sync/model/model_type_controller_delegate.h"
+
+namespace syncer {
+
+class ConsentSyncBridge {
+ public:
+ ConsentSyncBridge() = default;
+ virtual ~ConsentSyncBridge() = default;
+
+ virtual void RecordConsent(
+ std::unique_ptr<sync_pb::UserConsentSpecifics> specifics) = 0;
+
+ // Returns the delegate for the controller, i.e. sync integration point.
+ virtual base::WeakPtr<syncer::ModelTypeControllerDelegate>
+ GetControllerDelegateOnUIThread() = 0;
+};
+
+} // namespace syncer
+
+#endif // COMPONENTS_CONSENT_AUDITOR_CONSENT_SYNC_BRIDGE_H_
diff --git a/chromium/components/consent_auditor/consent_sync_bridge_impl.cc b/chromium/components/consent_auditor/consent_sync_bridge_impl.cc
new file mode 100644
index 00000000000..05c60ed3fa8
--- /dev/null
+++ b/chromium/components/consent_auditor/consent_sync_bridge_impl.cc
@@ -0,0 +1,313 @@
+// 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/consent_auditor/consent_sync_bridge_impl.h"
+
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/big_endian.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+#include "components/sync/model/data_type_activation_request.h"
+#include "components/sync/model/entity_change.h"
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/model/mutable_data_batch.h"
+#include "components/sync/protocol/sync.pb.h"
+
+namespace syncer {
+
+using sync_pb::UserConsentSpecifics;
+using IdList = ModelTypeStore::IdList;
+using Record = ModelTypeStore::Record;
+using RecordList = ModelTypeStore::RecordList;
+using WriteBatch = ModelTypeStore::WriteBatch;
+
+namespace {
+
+std::string GetStorageKeyFromSpecifics(const UserConsentSpecifics& specifics) {
+ // Force Big Endian, this means newly created keys are last in sort order,
+ // which allows leveldb to append new writes, which it is best at.
+ // TODO(skym): Until we force |event_time_usec| to never conflict, this has
+ // the potential for errors.
+ std::string key(8, 0);
+ base::WriteBigEndian(&key[0], specifics.client_consent_time_usec());
+ return key;
+}
+
+std::unique_ptr<EntityData> MoveToEntityData(
+ std::unique_ptr<UserConsentSpecifics> specifics) {
+ auto entity_data = std::make_unique<EntityData>();
+ entity_data->non_unique_name =
+ base::Int64ToString(specifics->client_consent_time_usec());
+ entity_data->specifics.set_allocated_user_consent(specifics.release());
+ return entity_data;
+}
+
+// TODO(vitaliii): Delete this function both here and in UserEventSyncBridge.
+std::unique_ptr<EntityData> CopyToEntityData(
+ const UserConsentSpecifics specifics) {
+ auto entity_data = std::make_unique<EntityData>();
+ entity_data->non_unique_name =
+ base::Int64ToString(specifics.client_consent_time_usec());
+ *entity_data->specifics.mutable_user_consent() = specifics;
+ return entity_data;
+}
+
+} // namespace
+
+ConsentSyncBridgeImpl::ConsentSyncBridgeImpl(
+ OnceModelTypeStoreFactory store_factory,
+ std::unique_ptr<ModelTypeChangeProcessor> change_processor)
+ : ModelTypeSyncBridge(std::move(change_processor)),
+ weak_ptr_factory_(this) {
+ std::move(store_factory)
+ .Run(USER_CONSENTS, base::BindOnce(&ConsentSyncBridgeImpl::OnStoreCreated,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+ConsentSyncBridgeImpl::~ConsentSyncBridgeImpl() {
+ if (!deferred_consents_while_initializing_.empty())
+ LOG(ERROR) << "Non-empty event queue at shutdown!";
+}
+
+std::unique_ptr<MetadataChangeList>
+ConsentSyncBridgeImpl::CreateMetadataChangeList() {
+ return WriteBatch::CreateMetadataChangeList();
+}
+
+base::Optional<ModelError> ConsentSyncBridgeImpl::MergeSyncData(
+ std::unique_ptr<MetadataChangeList> metadata_change_list,
+ EntityChangeList entity_data) {
+ NOTREACHED();
+ return {};
+}
+
+base::Optional<ModelError> ConsentSyncBridgeImpl::ApplySyncChanges(
+ std::unique_ptr<MetadataChangeList> metadata_change_list,
+ EntityChangeList entity_changes) {
+ std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch();
+ for (EntityChange& change : entity_changes) {
+ DCHECK_EQ(EntityChange::ACTION_DELETE, change.type());
+ batch->DeleteData(change.storage_key());
+ }
+
+ batch->TakeMetadataChangesFrom(std::move(metadata_change_list));
+ store_->CommitWriteBatch(std::move(batch),
+ base::BindOnce(&ConsentSyncBridgeImpl::OnCommit,
+ weak_ptr_factory_.GetWeakPtr()));
+ return {};
+}
+
+void ConsentSyncBridgeImpl::GetData(StorageKeyList storage_keys,
+ DataCallback callback) {
+ store_->ReadData(
+ storage_keys,
+ base::BindOnce(&ConsentSyncBridgeImpl::OnReadData,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void ConsentSyncBridgeImpl::GetAllDataForDebugging(DataCallback callback) {
+ store_->ReadAllData(base::BindOnce(&ConsentSyncBridgeImpl::OnReadAllData,
+ weak_ptr_factory_.GetWeakPtr(),
+ std::move(callback)));
+}
+
+std::string ConsentSyncBridgeImpl::GetClientTag(const EntityData& entity_data) {
+ return GetStorageKey(entity_data);
+}
+
+std::string ConsentSyncBridgeImpl::GetStorageKey(
+ const EntityData& entity_data) {
+ return GetStorageKeyFromSpecifics(entity_data.specifics.user_consent());
+}
+
+void ConsentSyncBridgeImpl::OnSyncStarting(
+ const DataTypeActivationRequest& request) {
+ DCHECK(!request.authenticated_account_id.empty());
+ DCHECK(syncing_account_id_.empty());
+
+ syncing_account_id_ = request.authenticated_account_id;
+
+ if (store_ && change_processor()->IsTrackingMetadata()) {
+ ReadAllDataAndResubmit();
+ }
+}
+
+ModelTypeSyncBridge::StopSyncResponse
+ConsentSyncBridgeImpl::ApplyStopSyncChanges(
+ std::unique_ptr<MetadataChangeList> delete_metadata_change_list) {
+ // Sync can only be stopped after initialization.
+ DCHECK(deferred_consents_while_initializing_.empty());
+
+ syncing_account_id_.clear();
+
+ if (delete_metadata_change_list) {
+ // Preserve all consents in the store, but delete their metadata, because it
+ // may become invalid when the sync is reenabled. It is important to report
+ // all user consents, thus, they are persisted for some time even after
+ // signout. We will try to resubmit these consents once the sync is enabled
+ // again. This may lead to same consent being submitted multiple times, but
+ // this is allowed.
+ std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch();
+ batch->TakeMetadataChangesFrom(std::move(delete_metadata_change_list));
+
+ store_->CommitWriteBatch(std::move(batch),
+ base::BindOnce(&ConsentSyncBridgeImpl::OnCommit,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+
+ return StopSyncResponse::kModelStillReadyToSync;
+}
+
+void ConsentSyncBridgeImpl::ReadAllDataAndResubmit() {
+ DCHECK(!syncing_account_id_.empty());
+ DCHECK(change_processor()->IsTrackingMetadata());
+ DCHECK(store_);
+ store_->ReadAllData(
+ base::BindOnce(&ConsentSyncBridgeImpl::OnReadAllDataToResubmit,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ConsentSyncBridgeImpl::OnReadAllDataToResubmit(
+ const base::Optional<ModelError>& error,
+ std::unique_ptr<RecordList> data_records) {
+ if (syncing_account_id_.empty()) {
+ // Meanwhile the sync has been disabled. We will try next time.
+ return;
+ }
+ DCHECK(change_processor()->IsTrackingMetadata());
+
+ if (error) {
+ change_processor()->ReportError(*error);
+ return;
+ }
+
+ for (const Record& r : *data_records) {
+ auto specifics = std::make_unique<UserConsentSpecifics>();
+ if (specifics->ParseFromString(r.value) &&
+ specifics->account_id() == syncing_account_id_) {
+ RecordConsentImpl(std::move(specifics));
+ }
+ }
+}
+
+void ConsentSyncBridgeImpl::RecordConsent(
+ std::unique_ptr<UserConsentSpecifics> specifics) {
+ // TODO(vitaliii): Sanity-check specifics->account_id() against
+ // syncing_account_id_, maybe DCHECK.
+ DCHECK(!specifics->account_id().empty());
+ if (change_processor()->IsTrackingMetadata()) {
+ RecordConsentImpl(std::move(specifics));
+ return;
+ }
+ deferred_consents_while_initializing_.push_back(std::move(specifics));
+}
+
+void ConsentSyncBridgeImpl::RecordConsentImpl(
+ std::unique_ptr<UserConsentSpecifics> specifics) {
+ DCHECK(store_);
+ DCHECK(change_processor()->IsTrackingMetadata());
+
+ std::string storage_key = GetStorageKeyFromSpecifics(*specifics);
+ std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch();
+ batch->WriteData(storage_key, specifics->SerializeAsString());
+
+ change_processor()->Put(storage_key, MoveToEntityData(std::move(specifics)),
+ batch->GetMetadataChangeList());
+ store_->CommitWriteBatch(std::move(batch),
+ base::BindOnce(&ConsentSyncBridgeImpl::OnCommit,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+base::WeakPtr<syncer::ModelTypeControllerDelegate>
+ConsentSyncBridgeImpl::GetControllerDelegateOnUIThread() {
+ return change_processor()->GetControllerDelegateOnUIThread();
+}
+
+void ConsentSyncBridgeImpl::ProcessQueuedEvents() {
+ DCHECK(change_processor()->IsTrackingMetadata());
+ for (std::unique_ptr<sync_pb::UserConsentSpecifics>& event :
+ deferred_consents_while_initializing_) {
+ RecordConsentImpl(std::move(event));
+ }
+ deferred_consents_while_initializing_.clear();
+}
+
+void ConsentSyncBridgeImpl::OnStoreCreated(
+ const base::Optional<ModelError>& error,
+ std::unique_ptr<ModelTypeStore> store) {
+ if (error) {
+ change_processor()->ReportError(*error);
+ return;
+ }
+
+ // TODO(vitaliii): Garbage collect old consents if sync is disabled.
+
+ store_ = std::move(store);
+ store_->ReadAllMetadata(
+ base::BindOnce(&ConsentSyncBridgeImpl::OnReadAllMetadata,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ConsentSyncBridgeImpl::OnReadAllMetadata(
+ const base::Optional<ModelError>& error,
+ std::unique_ptr<MetadataBatch> metadata_batch) {
+ if (error) {
+ change_processor()->ReportError(*error);
+ } else {
+ change_processor()->ModelReadyToSync(std::move(metadata_batch));
+ DCHECK(change_processor()->IsTrackingMetadata());
+ if (!syncing_account_id_.empty()) {
+ ReadAllDataAndResubmit();
+ }
+ ProcessQueuedEvents();
+ }
+}
+
+void ConsentSyncBridgeImpl::OnCommit(const base::Optional<ModelError>& error) {
+ if (error) {
+ change_processor()->ReportError(*error);
+ }
+}
+
+void ConsentSyncBridgeImpl::OnReadData(
+ DataCallback callback,
+ const base::Optional<ModelError>& error,
+ std::unique_ptr<RecordList> data_records,
+ std::unique_ptr<IdList> missing_id_list) {
+ OnReadAllData(std::move(callback), error, std::move(data_records));
+}
+
+void ConsentSyncBridgeImpl::OnReadAllData(
+ DataCallback callback,
+ const base::Optional<ModelError>& error,
+ std::unique_ptr<RecordList> data_records) {
+ if (error) {
+ change_processor()->ReportError(*error);
+ return;
+ }
+
+ auto batch = std::make_unique<MutableDataBatch>();
+ UserConsentSpecifics specifics;
+ for (const Record& r : *data_records) {
+ if (specifics.ParseFromString(r.value)) {
+ DCHECK_EQ(r.id, GetStorageKeyFromSpecifics(specifics));
+ batch->Put(r.id, CopyToEntityData(specifics));
+ } else {
+ change_processor()->ReportError(
+ {FROM_HERE, "Failed deserializing user events."});
+ return;
+ }
+ }
+ std::move(callback).Run(std::move(batch));
+}
+
+} // namespace syncer
diff --git a/chromium/components/consent_auditor/consent_sync_bridge_impl.h b/chromium/components/consent_auditor/consent_sync_bridge_impl.h
new file mode 100644
index 00000000000..442fd5cd2db
--- /dev/null
+++ b/chromium/components/consent_auditor/consent_sync_bridge_impl.h
@@ -0,0 +1,101 @@
+// 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_CONSENT_AUDITOR_CONSENT_SYNC_BRIDGE_IMPL_H_
+#define COMPONENTS_CONSENT_AUDITOR_CONSENT_SYNC_BRIDGE_IMPL_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "components/consent_auditor/consent_sync_bridge.h"
+#include "components/sync/model/model_type_change_processor.h"
+#include "components/sync/model/model_type_store.h"
+#include "components/sync/model/model_type_sync_bridge.h"
+
+namespace syncer {
+
+class ConsentSyncBridgeImpl : public ConsentSyncBridge,
+ public ModelTypeSyncBridge {
+ public:
+ ConsentSyncBridgeImpl(
+ OnceModelTypeStoreFactory store_factory,
+ std::unique_ptr<ModelTypeChangeProcessor> change_processor);
+ ~ConsentSyncBridgeImpl() override;
+
+ // ModelTypeSyncBridge implementation.
+ void OnSyncStarting(const DataTypeActivationRequest& request) override;
+ std::unique_ptr<MetadataChangeList> CreateMetadataChangeList() override;
+ base::Optional<ModelError> MergeSyncData(
+ std::unique_ptr<MetadataChangeList> metadata_change_list,
+ EntityChangeList entity_data) override;
+ base::Optional<ModelError> ApplySyncChanges(
+ std::unique_ptr<MetadataChangeList> metadata_change_list,
+ EntityChangeList entity_changes) override;
+ void GetData(StorageKeyList storage_keys, DataCallback callback) override;
+ void GetAllDataForDebugging(DataCallback callback) override;
+ std::string GetClientTag(const EntityData& entity_data) override;
+ std::string GetStorageKey(const EntityData& entity_data) override;
+ StopSyncResponse ApplyStopSyncChanges(
+ std::unique_ptr<MetadataChangeList> delete_metadata_change_list) override;
+
+ // ConsentSyncBridge implementation.
+ void RecordConsent(
+ std::unique_ptr<sync_pb::UserConsentSpecifics> specifics) override;
+ base::WeakPtr<syncer::ModelTypeControllerDelegate>
+ GetControllerDelegateOnUIThread() override;
+
+ private:
+ void RecordConsentImpl(
+ std::unique_ptr<sync_pb::UserConsentSpecifics> specifics);
+ // Record events in the deferred queue and clear the queue.
+ void ProcessQueuedEvents();
+
+ void OnStoreCreated(const base::Optional<ModelError>& error,
+ std::unique_ptr<ModelTypeStore> store);
+ void OnReadAllMetadata(const base::Optional<ModelError>& error,
+ std::unique_ptr<MetadataBatch> metadata_batch);
+ void OnCommit(const base::Optional<ModelError>& error);
+ void OnReadData(DataCallback callback,
+ const base::Optional<ModelError>& error,
+ std::unique_ptr<ModelTypeStore::RecordList> data_records,
+ std::unique_ptr<ModelTypeStore::IdList> missing_id_list);
+ void OnReadAllData(DataCallback callback,
+ const base::Optional<ModelError>& error,
+ std::unique_ptr<ModelTypeStore::RecordList> data_records);
+
+ // Resubmit all the consents persisted in the store to sync consents, which
+ // were preserved when sync was disabled. This may resubmit entities that the
+ // processor already knows about (i.e. with metadata), but it is allowed.
+ void ReadAllDataAndResubmit();
+ void OnReadAllDataToResubmit(
+ const base::Optional<ModelError>& error,
+ std::unique_ptr<ModelTypeStore::RecordList> data_records);
+
+ // Persistent storage for in flight consents. Should remain quite small, as we
+ // delete upon commit confirmation. May contain consents without metadata
+ // (e.g. persisted when sync was disabled).
+ std::unique_ptr<ModelTypeStore> store_;
+
+ // Used to store consents while the store or change processor are not
+ // ready.
+ std::vector<std::unique_ptr<sync_pb::UserConsentSpecifics>>
+ deferred_consents_while_initializing_;
+
+ // Empty if sync not running.
+ std::string syncing_account_id_;
+
+ base::WeakPtrFactory<ConsentSyncBridgeImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConsentSyncBridgeImpl);
+};
+
+} // namespace syncer
+
+#endif // COMPONENTS_CONSENT_AUDITOR_CONSENT_SYNC_BRIDGE_IMPL_H_
diff --git a/chromium/components/consent_auditor/consent_sync_bridge_impl_unittest.cc b/chromium/components/consent_auditor/consent_sync_bridge_impl_unittest.cc
new file mode 100644
index 00000000000..f446f3cdbfb
--- /dev/null
+++ b/chromium/components/consent_auditor/consent_sync_bridge_impl_unittest.cc
@@ -0,0 +1,491 @@
+// 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/consent_auditor/consent_sync_bridge_impl.h"
+
+#include <map>
+#include <set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "components/sync/model/data_batch.h"
+#include "components/sync/model/data_type_activation_request.h"
+#include "components/sync/model/mock_model_type_change_processor.h"
+#include "components/sync/model/model_type_store_test_util.h"
+#include "components/sync/protocol/sync.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncer {
+namespace {
+
+using sync_pb::UserConsentSpecifics;
+using testing::_;
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Invoke;
+using testing::IsEmpty;
+using testing::IsNull;
+using testing::NotNull;
+using testing::Pair;
+using testing::Pointee;
+using testing::Property;
+using testing::Return;
+using testing::SaveArg;
+using testing::SizeIs;
+using testing::UnorderedElementsAre;
+using testing::WithArg;
+using WriteBatch = ModelTypeStore::WriteBatch;
+
+MATCHER_P(MatchesUserConsent, expected, "") {
+ if (!arg.has_user_consent()) {
+ *result_listener << "which is not a user consent";
+ return false;
+ }
+ const UserConsentSpecifics& actual = arg.user_consent();
+ if (actual.client_consent_time_usec() !=
+ expected.client_consent_time_usec()) {
+ return false;
+ }
+ return true;
+}
+
+UserConsentSpecifics CreateSpecifics(int64_t client_consent_time_usec) {
+ UserConsentSpecifics specifics;
+ specifics.set_client_consent_time_usec(client_consent_time_usec);
+ specifics.set_account_id("account_id");
+ return specifics;
+}
+
+std::unique_ptr<UserConsentSpecifics> SpecificsUniquePtr(
+ int64_t client_consent_time_usec) {
+ return std::make_unique<UserConsentSpecifics>(
+ CreateSpecifics(client_consent_time_usec));
+}
+
+DataTypeActivationRequest CreateActivationRequest(
+ const std::string& account_id) {
+ DataTypeActivationRequest request;
+ request.authenticated_account_id = account_id;
+ return request;
+}
+
+class ConsentSyncBridgeImplTest : public testing::Test {
+ protected:
+ ConsentSyncBridgeImplTest() {
+ bridge_ = std::make_unique<ConsentSyncBridgeImpl>(
+ ModelTypeStoreTestUtil::FactoryForInMemoryStoreForTest(),
+ mock_processor_.CreateForwardingProcessor());
+ ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(true));
+ }
+
+ std::string GetStorageKey(const UserConsentSpecifics& specifics) {
+ EntityData entity_data;
+ *entity_data.specifics.mutable_user_consent() = specifics;
+ return bridge()->GetStorageKey(entity_data);
+ }
+
+ ConsentSyncBridgeImpl* bridge() { return bridge_.get(); }
+ MockModelTypeChangeProcessor* processor() { return &mock_processor_; }
+
+ std::map<std::string, sync_pb::EntitySpecifics> GetAllData() {
+ base::RunLoop loop;
+ std::unique_ptr<DataBatch> batch;
+ bridge_->GetAllDataForDebugging(base::BindOnce(
+ [](base::RunLoop* loop, std::unique_ptr<DataBatch>* out_batch,
+ std::unique_ptr<DataBatch> batch) {
+ *out_batch = std::move(batch);
+ loop->Quit();
+ },
+ &loop, &batch));
+ loop.Run();
+ EXPECT_NE(nullptr, batch);
+
+ std::map<std::string, sync_pb::EntitySpecifics> storage_key_to_specifics;
+ if (batch != nullptr) {
+ while (batch->HasNext()) {
+ const syncer::KeyAndData& pair = batch->Next();
+ storage_key_to_specifics[pair.first] = pair.second->specifics;
+ }
+ }
+ return storage_key_to_specifics;
+ }
+
+ std::unique_ptr<sync_pb::EntitySpecifics> GetData(
+ const std::string& storage_key) {
+ base::RunLoop loop;
+ std::unique_ptr<DataBatch> batch;
+ bridge_->GetData(
+ {storage_key},
+ base::BindOnce(
+ [](base::RunLoop* loop, std::unique_ptr<DataBatch>* out_batch,
+ std::unique_ptr<DataBatch> batch) {
+ *out_batch = std::move(batch);
+ loop->Quit();
+ },
+ &loop, &batch));
+ loop.Run();
+ EXPECT_NE(nullptr, batch);
+
+ std::unique_ptr<sync_pb::EntitySpecifics> specifics;
+ if (batch != nullptr && batch->HasNext()) {
+ const syncer::KeyAndData& pair = batch->Next();
+ specifics =
+ std::make_unique<sync_pb::EntitySpecifics>(pair.second->specifics);
+ EXPECT_FALSE(batch->HasNext());
+ }
+ return specifics;
+ }
+
+ private:
+ std::unique_ptr<ConsentSyncBridgeImpl> bridge_;
+ testing::NiceMock<MockModelTypeChangeProcessor> mock_processor_;
+ base::MessageLoop message_loop_;
+};
+
+TEST_F(ConsentSyncBridgeImplTest, ShouldCallModelReadyToSyncOnStartup) {
+ EXPECT_CALL(*processor(), ModelReadyToSync(NotNull()));
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(ConsentSyncBridgeImplTest, ShouldRecordSingleConsent) {
+ const UserConsentSpecifics specifics(
+ CreateSpecifics(/*client_consent_time_usec=*/1u));
+ std::string storage_key;
+ EXPECT_CALL(*processor(), Put(_, _, _))
+ .WillOnce(WithArg<0>(SaveArg<0>(&storage_key)));
+ bridge()->RecordConsent(std::make_unique<UserConsentSpecifics>(specifics));
+
+ EXPECT_THAT(GetData(storage_key), Pointee(MatchesUserConsent(specifics)));
+ EXPECT_THAT(GetData("bogus"), IsNull());
+ EXPECT_THAT(GetAllData(),
+ ElementsAre(Pair(storage_key, MatchesUserConsent(specifics))));
+}
+
+TEST_F(ConsentSyncBridgeImplTest, ShouldNotDeleteConsentsWhenSyncIsDisabled) {
+ UserConsentSpecifics user_consent_specifics(
+ CreateSpecifics(/*client_consent_time_usec=*/2u));
+ bridge()->RecordConsent(
+ std::make_unique<UserConsentSpecifics>(user_consent_specifics));
+ ASSERT_THAT(GetAllData(), SizeIs(1));
+
+ EXPECT_THAT(
+ bridge()->ApplyStopSyncChanges(WriteBatch::CreateMetadataChangeList()),
+ Eq(ModelTypeSyncBridge::StopSyncResponse::kModelStillReadyToSync));
+ // The bridge may asynchronously query the store to choose what to delete.
+ base::RunLoop().RunUntilIdle();
+
+ // User consent specific must be persisted when sync is disabled.
+ EXPECT_THAT(GetAllData(),
+ ElementsAre(Pair(_, MatchesUserConsent(user_consent_specifics))));
+}
+
+TEST_F(ConsentSyncBridgeImplTest,
+ ShouldRecordMultipleConsentsAndDeduplicateByTime) {
+ std::set<std::string> unique_storage_keys;
+ EXPECT_CALL(*processor(), Put(_, _, _))
+ .Times(4)
+ .WillRepeatedly(
+ [&unique_storage_keys](const std::string& storage_key,
+ std::unique_ptr<EntityData> entity_data,
+ MetadataChangeList* metadata_change_list) {
+ unique_storage_keys.insert(storage_key);
+ });
+
+ bridge()->RecordConsent(SpecificsUniquePtr(/*client_consent_time_usec=*/1u));
+ bridge()->RecordConsent(SpecificsUniquePtr(/*client_consent_time_usec=*/1u));
+ bridge()->RecordConsent(SpecificsUniquePtr(/*client_consent_time_usec=*/1u));
+ bridge()->RecordConsent(SpecificsUniquePtr(/*client_consent_time_usec=*/2u));
+
+ EXPECT_EQ(2u, unique_storage_keys.size());
+ EXPECT_THAT(GetAllData(), SizeIs(2));
+}
+
+TEST_F(ConsentSyncBridgeImplTest,
+ ShouldDeleteCommitedConsentsAfterApplySyncChanges) {
+ std::string first_storage_key;
+ std::string second_storage_key;
+ EXPECT_CALL(*processor(), Put(_, _, _))
+ .WillOnce(WithArg<0>(SaveArg<0>(&first_storage_key)))
+ .WillOnce(WithArg<0>(SaveArg<0>(&second_storage_key)));
+
+ bridge()->RecordConsent(SpecificsUniquePtr(/*client_consent_time_usec=*/1u));
+ bridge()->RecordConsent(SpecificsUniquePtr(/*client_consent_time_usec=*/2u));
+ ASSERT_THAT(GetAllData(), SizeIs(2));
+
+ auto error_on_delete = bridge()->ApplySyncChanges(
+ bridge()->CreateMetadataChangeList(),
+ {EntityChange::CreateDelete(first_storage_key)});
+ EXPECT_FALSE(error_on_delete);
+ EXPECT_THAT(GetAllData(), SizeIs(1));
+ EXPECT_THAT(GetData(first_storage_key), IsNull());
+ EXPECT_THAT(GetData(second_storage_key), NotNull());
+}
+
+TEST_F(ConsentSyncBridgeImplTest,
+ ShouldRecordConsentsEvenBeforeProcessorInitialization) {
+ // Processor initializations depends on the store initialization. The consent
+ // may be recorded before the store is initialized.
+ ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(false));
+ // The consent must be recorded, but not propagated anywhere while the
+ // initilization is in progress.
+ EXPECT_CALL(*processor(), Put(_, _, _)).Times(0);
+ bridge()->RecordConsent(SpecificsUniquePtr(/*client_consent_time_usec=*/1u));
+
+ // When store is fully initialized, the consent should be reported to the
+ // processor.
+ ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(true));
+ EXPECT_CALL(*processor(), Put(_, _, _));
+ base::RunLoop().RunUntilIdle();
+}
+
+// User consents should be buffered if the store and processor is not fully
+// initialized.
+TEST_F(ConsentSyncBridgeImplTest,
+ ShouldSubmitBufferedConsentsWhenStoreIsInitialized) {
+ // Wait until bridge() is ready to avoid interference with processor() mock.
+ base::RunLoop().RunUntilIdle();
+
+ UserConsentSpecifics first_consent =
+ CreateSpecifics(/*client_consent_time_usec=*/1u);
+ first_consent.set_account_id("account_id");
+ UserConsentSpecifics second_consent =
+ CreateSpecifics(/*client_consent_time_usec=*/2u);
+ second_consent.set_account_id("account_id");
+
+ ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(false));
+ ModelType store_init_type;
+ ModelTypeStore::InitCallback store_init_callback;
+ ConsentSyncBridgeImpl late_init_bridge(
+ base::BindLambdaForTesting(
+ [&](ModelType type, ModelTypeStore::InitCallback callback) {
+ store_init_type = type;
+ store_init_callback = std::move(callback);
+ }),
+ processor()->CreateForwardingProcessor());
+
+ // Record consent before the store is initialized.
+ late_init_bridge.RecordConsent(
+ std::make_unique<UserConsentSpecifics>(first_consent));
+
+ // Initialize the store.
+ EXPECT_CALL(*processor(), ModelReadyToSync(NotNull()));
+ ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(true));
+ std::move(store_init_callback)
+ .Run(/*error=*/base::nullopt,
+ ModelTypeStoreTestUtil::CreateInMemoryStoreForTest(store_init_type));
+ base::RunLoop().RunUntilIdle();
+
+ late_init_bridge.OnSyncStarting(CreateActivationRequest("account_id"));
+
+ // Record consent after initializaiton is done.
+ late_init_bridge.RecordConsent(
+ std::make_unique<UserConsentSpecifics>(second_consent));
+
+ // Both the pre-initialization and post-initialization consents must be
+ // handled after initialization as usual.
+ base::RunLoop().RunUntilIdle();
+ ASSERT_THAT(GetAllData(),
+ UnorderedElementsAre(Pair(GetStorageKey(first_consent),
+ MatchesUserConsent(first_consent)),
+ Pair(GetStorageKey(second_consent),
+ MatchesUserConsent(second_consent))));
+}
+
+TEST_F(ConsentSyncBridgeImplTest,
+ ShouldReportPreviouslyPersistedConsentsWhenSyncIsReenabled) {
+ UserConsentSpecifics consent =
+ CreateSpecifics(/*client_consent_time_usec=*/1u);
+ consent.set_account_id("account_id");
+
+ bridge()->RecordConsent(std::make_unique<UserConsentSpecifics>(consent));
+
+ // User disables sync, hovewer, the consent hasn't been submitted yet. It is
+ // preserved to be submitted when sync is re-enabled.
+ EXPECT_THAT(
+ bridge()->ApplyStopSyncChanges(WriteBatch::CreateMetadataChangeList()),
+ Eq(ModelTypeSyncBridge::StopSyncResponse::kModelStillReadyToSync));
+ // The bridge may asynchronously query the store to choose what to delete.
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_THAT(GetAllData(), SizeIs(1));
+
+ // Reenable sync.
+ ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(true));
+ std::string storage_key;
+ EXPECT_CALL(*processor(), Put(_, _, _))
+ .WillOnce(WithArg<0>(SaveArg<0>(&storage_key)));
+ bridge()->OnSyncStarting(CreateActivationRequest("account_id"));
+
+ // The bridge may asynchronously query the store to choose what to resubmit.
+ base::RunLoop().RunUntilIdle();
+
+ // The previously preserved consent should be resubmitted to the processor
+ // when sync is re-enabled.
+ EXPECT_THAT(storage_key, Eq(GetStorageKey(consent)));
+}
+
+TEST_F(ConsentSyncBridgeImplTest,
+ ShouldReportPersistedConsentsOnStartupEvenWithLateStoreInitialization) {
+ // Wait until bridge() is ready to avoid interference with processor() mock.
+ base::RunLoop().RunUntilIdle();
+
+ UserConsentSpecifics consent =
+ CreateSpecifics(/*client_consent_time_usec=*/1u);
+ consent.set_account_id("account_id");
+
+ ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(false));
+ ModelType store_init_type;
+ ModelTypeStore::InitCallback store_init_callback;
+ ConsentSyncBridgeImpl late_init_bridge(
+ base::BindLambdaForTesting(
+ [&](ModelType type, ModelTypeStore::InitCallback callback) {
+ store_init_type = type;
+ store_init_callback = std::move(callback);
+ }),
+ processor()->CreateForwardingProcessor());
+
+ // Sync is active, but the store is not ready yet.
+ EXPECT_CALL(*processor(), ModelReadyToSync(_)).Times(0);
+ late_init_bridge.OnSyncStarting(CreateActivationRequest("account_id"));
+
+ // Initialize the store.
+ std::unique_ptr<ModelTypeStore> store =
+ ModelTypeStoreTestUtil::CreateInMemoryStoreForTest(store_init_type);
+
+ // TODO(vitaliii): Try to avoid putting the data directly into the store (e.g.
+ // by using a forwarding store), because this is an implementation detail.
+ // However, currently the bridge owns the store and there is no obvious way to
+ // preserve it.
+
+ // Put the consent manually to simulate a restart with disabled sync.
+ auto batch = store->CreateWriteBatch();
+ batch->WriteData(GetStorageKey(consent), consent.SerializeAsString());
+ store->CommitWriteBatch(std::move(batch), base::DoNothing());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_CALL(*processor(), ModelReadyToSync(NotNull()));
+ ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(true));
+ std::string storage_key;
+ EXPECT_CALL(*processor(), Put(_, _, _))
+ .WillOnce(WithArg<0>(SaveArg<0>(&storage_key)));
+ std::move(store_init_callback)
+ .Run(/*error=*/base::nullopt,
+ ModelTypeStoreTestUtil::CreateInMemoryStoreForTest(store_init_type));
+
+ // The bridge may asynchronously query the store to choose what to resubmit.
+ base::RunLoop().RunUntilIdle();
+
+ // The previously preserved consent should be resubmitted to the processor
+ // when the store is initilized, because the sync has already been re-enabled.
+ EXPECT_THAT(storage_key, Eq(GetStorageKey(consent)));
+}
+
+TEST_F(ConsentSyncBridgeImplTest,
+ ShouldReportPersistedConsentsOnStartupEvenWithLateSyncInitialization) {
+ // Wait until bridge() is ready to avoid interference with processor() mock.
+ base::RunLoop().RunUntilIdle();
+
+ UserConsentSpecifics consent =
+ CreateSpecifics(/*client_consent_time_usec=*/1u);
+ consent.set_account_id("account_id");
+
+ ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(false));
+ ModelType store_init_type;
+ ModelTypeStore::InitCallback store_init_callback;
+ ConsentSyncBridgeImpl late_init_bridge(
+ base::BindLambdaForTesting(
+ [&](ModelType type, ModelTypeStore::InitCallback callback) {
+ store_init_type = type;
+ store_init_callback = std::move(callback);
+ }),
+ processor()->CreateForwardingProcessor());
+
+ // Initialize the store.
+ std::unique_ptr<ModelTypeStore> store =
+ ModelTypeStoreTestUtil::CreateInMemoryStoreForTest(store_init_type);
+
+ // TODO(vitaliii): Try to avoid putting the data directly into the store (e.g.
+ // by using a forwarding store), because this is an implementation detail.
+ // However, currently the bridge owns the store and there is no obvious way to
+ // preserve it.
+
+ // Put the consent manually to simulate a restart with disabled sync.
+ auto batch = store->CreateWriteBatch();
+ batch->WriteData(GetStorageKey(consent), consent.SerializeAsString());
+ store->CommitWriteBatch(std::move(batch), base::DoNothing());
+ base::RunLoop().RunUntilIdle();
+
+ // The store has been initialized, but the sync is not active yet.
+ ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(true));
+ EXPECT_CALL(*processor(), ModelReadyToSync(NotNull()));
+ std::move(store_init_callback)
+ .Run(/*error=*/base::nullopt,
+ ModelTypeStoreTestUtil::CreateInMemoryStoreForTest(store_init_type));
+ base::RunLoop().RunUntilIdle();
+
+ late_init_bridge.OnSyncStarting(CreateActivationRequest("account_id"));
+
+ std::string storage_key;
+ EXPECT_CALL(*processor(), Put(_, _, _))
+ .WillOnce(WithArg<0>(SaveArg<0>(&storage_key)));
+ // The bridge may asynchronously query the store to choose what to resubmit.
+ base::RunLoop().RunUntilIdle();
+
+ // The previously preserved consent should be resubmitted to the processor
+ // when sync is re-enabled, because the store has already been initialized.
+ EXPECT_THAT(storage_key, Eq(GetStorageKey(consent)));
+}
+
+TEST_F(ConsentSyncBridgeImplTest,
+ ShouldResubmitPersistedConsentOnlyIfSameAccount) {
+ // This consent is being recorded while sync is stopped.
+ UserConsentSpecifics user_consent_specifics(
+ CreateSpecifics(/*client_consent_time_usec=*/2u));
+ user_consent_specifics.set_account_id("first_account");
+ bridge()->RecordConsent(
+ std::make_unique<UserConsentSpecifics>(user_consent_specifics));
+ ASSERT_THAT(GetAllData(), SizeIs(1));
+
+ EXPECT_THAT(
+ bridge()->ApplyStopSyncChanges(WriteBatch::CreateMetadataChangeList()),
+ Eq(ModelTypeSyncBridge::StopSyncResponse::kModelStillReadyToSync));
+ // The bridge may asynchronously query the store to choose what to delete.
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_THAT(GetAllData(),
+ ElementsAre(Pair(_, MatchesUserConsent(user_consent_specifics))));
+
+ // A new user signs in and enables sync.
+ // The previous account consent should not be resubmited, because the new sync
+ // account is different.
+ EXPECT_CALL(*processor(), Put(_, _, _)).Times(0);
+ ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(true));
+ bridge()->OnSyncStarting(CreateActivationRequest("second_account"));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_THAT(
+ bridge()->ApplyStopSyncChanges(WriteBatch::CreateMetadataChangeList()),
+ Eq(ModelTypeSyncBridge::StopSyncResponse::kModelStillReadyToSync));
+ base::RunLoop().RunUntilIdle();
+
+ std::string storage_key;
+ EXPECT_CALL(*processor(), Put(_, _, _))
+ .WillOnce(WithArg<0>(SaveArg<0>(&storage_key)));
+ bridge()->OnSyncStarting(CreateActivationRequest("first_account"));
+ // The bridge may asynchronously query the store to choose what to resubmit.
+ base::RunLoop().RunUntilIdle();
+
+ // This time their consent should be resubmitted, because it is for the same
+ // account.
+ EXPECT_THAT(storage_key, Eq(GetStorageKey(user_consent_specifics)));
+}
+
+} // namespace
+
+} // namespace syncer
diff --git a/chromium/components/consent_auditor/fake_consent_auditor.cc b/chromium/components/consent_auditor/fake_consent_auditor.cc
index 1f7e8f62dd3..fd99e13955e 100644
--- a/chromium/components/consent_auditor/fake_consent_auditor.cc
+++ b/chromium/components/consent_auditor/fake_consent_auditor.cc
@@ -8,13 +8,7 @@
namespace consent_auditor {
-FakeConsentAuditor::FakeConsentAuditor(
- PrefService* pref_service,
- syncer::UserEventService* user_event_service)
- : ConsentAuditor(pref_service,
- user_event_service,
- std::string(),
- std::string()) {}
+FakeConsentAuditor::FakeConsentAuditor() {}
FakeConsentAuditor::~FakeConsentAuditor() {}
@@ -25,11 +19,23 @@ void FakeConsentAuditor::RecordGaiaConsent(
int confirmation_grd_id,
consent_auditor::ConsentStatus status) {
account_id_ = account_id;
- std::vector<int> ids = description_grd_ids;
- ids.push_back(confirmation_grd_id);
- recorded_id_vectors_.push_back(std::move(ids));
+ recorded_id_vectors_.push_back(description_grd_ids);
+ recorded_confirmation_ids_.push_back(confirmation_grd_id);
recorded_features_.push_back(feature);
recorded_statuses_.push_back(status);
}
+void FakeConsentAuditor::RecordLocalConsent(
+ const std::string& feature,
+ const std::string& description_text,
+ const std::string& confirmation_text) {
+ NOTIMPLEMENTED();
+}
+
+base::WeakPtr<syncer::ModelTypeControllerDelegate>
+FakeConsentAuditor::GetControllerDelegateOnUIThread() {
+ NOTIMPLEMENTED();
+ return nullptr;
+}
+
} // namespace consent_auditor
diff --git a/chromium/components/consent_auditor/fake_consent_auditor.h b/chromium/components/consent_auditor/fake_consent_auditor.h
index d8cf63bf4c5..82310e8e4a0 100644
--- a/chromium/components/consent_auditor/fake_consent_auditor.h
+++ b/chromium/components/consent_auditor/fake_consent_auditor.h
@@ -10,32 +10,36 @@
#include "base/macros.h"
#include "components/consent_auditor/consent_auditor.h"
-namespace syncer {
-class UserEventService;
-}
-
-class PrefService;
-
namespace consent_auditor {
class FakeConsentAuditor : public ConsentAuditor {
public:
- FakeConsentAuditor(PrefService* pref_service,
- syncer::UserEventService* user_event_service);
+ FakeConsentAuditor();
~FakeConsentAuditor() override;
+ // ConsentAuditor implementation.
void RecordGaiaConsent(const std::string& account_id,
consent_auditor::Feature feature,
const std::vector<int>& description_grd_ids,
int confirmation_grd_id,
consent_auditor::ConsentStatus status) override;
+ void RecordLocalConsent(const std::string& feature,
+ const std::string& description_text,
+ const std::string& confirmation_text) override;
+ base::WeakPtr<syncer::ModelTypeControllerDelegate>
+ GetControllerDelegateOnUIThread() override;
+ // Methods for fake.
const std::string& account_id() const { return account_id_; }
const std::vector<std::vector<int>>& recorded_id_vectors() {
return recorded_id_vectors_;
}
+ const std::vector<int>& recorded_confirmation_ids() const {
+ return recorded_confirmation_ids_;
+ }
+
const std::vector<Feature>& recorded_features() { return recorded_features_; }
const std::vector<ConsentStatus>& recorded_statuses() {
@@ -45,6 +49,7 @@ class FakeConsentAuditor : public ConsentAuditor {
private:
std::string account_id_;
std::vector<std::vector<int>> recorded_id_vectors_;
+ std::vector<int> recorded_confirmation_ids_;
std::vector<Feature> recorded_features_;
std::vector<ConsentStatus> recorded_statuses_;
diff --git a/chromium/components/constrained_window/native_web_contents_modal_dialog_manager_views.cc b/chromium/components/constrained_window/native_web_contents_modal_dialog_manager_views.cc
index cf4b32748c3..2056cf3c60e 100644
--- a/chromium/components/constrained_window/native_web_contents_modal_dialog_manager_views.cc
+++ b/chromium/components/constrained_window/native_web_contents_modal_dialog_manager_views.cc
@@ -106,6 +106,13 @@ void NativeWebContentsModalDialogManagerViews::Show() {
// shadows are below the constrained dialog in z-order when we do this.
shown_widgets_.insert(widget);
#endif
+
+#if !defined(USE_AURA)
+ // Don't re-animate when switching tabs. Note this is done on Mac only after
+ // the initial ShowWidget() call above, and then "sticks" for later calls.
+ // TODO(tapted): Consolidate this codepath with Aura.
+ widget->SetVisibilityAnimationTransition(views::Widget::ANIMATE_HIDE);
+#endif
}
void NativeWebContentsModalDialogManagerViews::Hide() {
diff --git a/chromium/components/content_settings/core/browser/BUILD.gn b/chromium/components/content_settings/core/browser/BUILD.gn
index 47f3584df29..94eac105b83 100644
--- a/chromium/components/content_settings/core/browser/BUILD.gn
+++ b/chromium/components/content_settings/core/browser/BUILD.gn
@@ -11,6 +11,8 @@ static_library("browser") {
"content_settings_default_provider.h",
"content_settings_details.cc",
"content_settings_details.h",
+ "content_settings_ephemeral_provider.cc",
+ "content_settings_ephemeral_provider.h",
"content_settings_global_value_map.cc",
"content_settings_global_value_map.h",
"content_settings_info.cc",
@@ -68,6 +70,7 @@ static_library("browser") {
source_set("unit_tests") {
testonly = true
sources = [
+ "content_settings_ephemeral_provider_unittest.cc",
"content_settings_registry_unittest.cc",
"content_settings_rule_unittest.cc",
"content_settings_utils_unittest.cc",
diff --git a/chromium/components/content_settings/core/browser/content_settings_ephemeral_provider.cc b/chromium/components/content_settings/core/browser/content_settings_ephemeral_provider.cc
new file mode 100644
index 00000000000..7e80a0617bd
--- /dev/null
+++ b/chromium/components/content_settings/core/browser/content_settings_ephemeral_provider.cc
@@ -0,0 +1,133 @@
+// 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/content_settings/core/browser/content_settings_ephemeral_provider.h"
+
+#include "base/stl_util.h"
+#include "base/time/default_clock.h"
+#include "components/content_settings/core/browser/content_settings_registry.h"
+#include "components/content_settings/core/browser/website_settings_info.h"
+#include "components/content_settings/core/browser/website_settings_registry.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_pattern.h"
+
+namespace content_settings {
+
+// ////////////////////////////////////////////////////////////////////////////
+// EphemeralProvider:
+//
+
+EphemeralProvider::EphemeralProvider(bool store_last_modified)
+ : store_last_modified_(store_last_modified),
+ clock_(base::DefaultClock::GetInstance()) {
+ ContentSettingsRegistry* content_settings =
+ ContentSettingsRegistry::GetInstance();
+ WebsiteSettingsRegistry* website_settings =
+ WebsiteSettingsRegistry::GetInstance();
+ for (const WebsiteSettingsInfo* info : *website_settings) {
+ const ContentSettingsInfo* content_type_info =
+ content_settings->Get(info->type());
+ // If this is an ephemeral content setting, handle it in this class.
+ if (content_type_info && content_type_info->storage_behavior() ==
+ ContentSettingsInfo::EPHEMERAL) {
+ supported_types_.insert(info->type());
+ }
+ }
+}
+
+EphemeralProvider::~EphemeralProvider() {}
+
+std::unique_ptr<RuleIterator> EphemeralProvider::GetRuleIterator(
+ ContentSettingsType content_type,
+ const ResourceIdentifier& resource_identifier,
+ bool incognito) const {
+ return content_settings_rules_.GetRuleIterator(content_type,
+ resource_identifier, nullptr);
+}
+
+bool EphemeralProvider::SetWebsiteSetting(
+ const ContentSettingsPattern& primary_pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ ContentSettingsType content_type,
+ const ResourceIdentifier& resource_identifier,
+ base::Value* in_value) {
+ DCHECK(CalledOnValidThread());
+
+ if (!base::ContainsKey(supported_types_, content_type))
+ return false;
+
+ // Default settings are set using a wildcard pattern for both
+ // |primary_pattern| and |secondary_pattern|. Don't store default settings in
+ // the EphemeralProvider. The EphemeralProvider handles settings for
+ // specific sites/origins defined by the |primary_pattern| and the
+ // |secondary_pattern|. Default settings are handled by the DefaultProvider.
+ if (primary_pattern == ContentSettingsPattern::Wildcard() &&
+ secondary_pattern == ContentSettingsPattern::Wildcard() &&
+ resource_identifier.empty()) {
+ return false;
+ }
+
+ if (in_value) {
+ content_settings_rules_.SetValue(
+ primary_pattern, secondary_pattern, content_type, resource_identifier,
+ store_last_modified_ ? clock_->Now() : base::Time(), in_value);
+ NotifyObservers(primary_pattern, secondary_pattern, content_type,
+ resource_identifier);
+ } else {
+ // If the value exists, delete it.
+ if (content_settings_rules_.GetLastModified(
+ primary_pattern, secondary_pattern, content_type,
+ resource_identifier) != base::Time()) {
+ content_settings_rules_.DeleteValue(primary_pattern, secondary_pattern,
+ content_type, resource_identifier);
+ NotifyObservers(primary_pattern, secondary_pattern, content_type,
+ resource_identifier);
+ }
+ }
+ return true;
+}
+
+base::Time EphemeralProvider::GetWebsiteSettingLastModified(
+ const ContentSettingsPattern& primary_pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ ContentSettingsType content_type,
+ const ResourceIdentifier& resource_identifier) {
+ DCHECK(CalledOnValidThread());
+
+ return content_settings_rules_.GetLastModified(
+ primary_pattern, secondary_pattern, content_type, resource_identifier);
+}
+
+void EphemeralProvider::ClearAllContentSettingsRules(
+ ContentSettingsType content_type) {
+ DCHECK(CalledOnValidThread());
+
+ // Get all resource identifiers for this |content_type|.
+ std::set<ResourceIdentifier> resource_identifiers;
+ for (OriginIdentifierValueMap::EntryMap::const_iterator entry =
+ content_settings_rules_.begin();
+ entry != content_settings_rules_.end(); entry++) {
+ if (entry->first.content_type == content_type)
+ resource_identifiers.insert(entry->first.resource_identifier);
+ }
+
+ for (const ResourceIdentifier& resource_identifier : resource_identifiers)
+ content_settings_rules_.DeleteValues(content_type, resource_identifier);
+
+ if (!resource_identifiers.empty()) {
+ NotifyObservers(ContentSettingsPattern(), ContentSettingsPattern(),
+ content_type, ResourceIdentifier());
+ }
+}
+
+void EphemeralProvider::ShutdownOnUIThread() {
+ DCHECK(CalledOnValidThread());
+ RemoveAllObservers();
+}
+
+void EphemeralProvider::SetClockForTesting(base::Clock* clock) {
+ clock_ = clock;
+}
+
+} // namespace content_settings
diff --git a/chromium/components/content_settings/core/browser/content_settings_ephemeral_provider.h b/chromium/components/content_settings/core/browser/content_settings_ephemeral_provider.h
new file mode 100644
index 00000000000..c960a0f5499
--- /dev/null
+++ b/chromium/components/content_settings/core/browser/content_settings_ephemeral_provider.h
@@ -0,0 +1,65 @@
+// 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_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_EPHEMERAL_PROVIDER_H_
+#define COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_EPHEMERAL_PROVIDER_H_
+
+#include <set>
+
+#include "components/content_settings/core/browser/content_settings_origin_identifier_value_map.h"
+#include "components/content_settings/core/browser/user_modifiable_provider.h"
+
+namespace base {
+class Clock;
+}
+
+namespace content_settings {
+
+// A user-modifiable content settings provider that doesn't store its settings
+// on disk.
+class EphemeralProvider : public UserModifiableProvider {
+ public:
+ EphemeralProvider(bool store_last_modified);
+ ~EphemeralProvider() override;
+
+ // UserModifiableProvider implementations.
+ std::unique_ptr<RuleIterator> GetRuleIterator(
+ ContentSettingsType content_type,
+ const ResourceIdentifier& resource_identifier,
+ bool incognito) const override;
+ bool SetWebsiteSetting(const ContentSettingsPattern& primary_pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ ContentSettingsType content_type,
+ const ResourceIdentifier& resource_identifier,
+ base::Value* value) override;
+ void ClearAllContentSettingsRules(ContentSettingsType content_type) override;
+ void ShutdownOnUIThread() override;
+ base::Time GetWebsiteSettingLastModified(
+ const ContentSettingsPattern& primary_pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ ContentSettingsType content_type,
+ const ResourceIdentifier& resource_identifier) override;
+
+ void SetClockForTesting(base::Clock* clock);
+ void SetSupportedTypesForTesting(
+ std::set<ContentSettingsType>& supported_types) {
+ supported_types_ = supported_types;
+ }
+ size_t GetCountForTesting() { return content_settings_rules_.size(); }
+
+ private:
+ bool store_last_modified_;
+
+ OriginIdentifierValueMap content_settings_rules_;
+
+ std::set<ContentSettingsType> supported_types_;
+
+ base::Clock* clock_;
+
+ DISALLOW_COPY_AND_ASSIGN(EphemeralProvider);
+};
+
+} // namespace content_settings
+
+#endif // COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_EPHEMERAL_PROVIDER_H_
diff --git a/chromium/components/content_settings/core/browser/content_settings_ephemeral_provider_unittest.cc b/chromium/components/content_settings/core/browser/content_settings_ephemeral_provider_unittest.cc
new file mode 100644
index 00000000000..33e6b6d0e3c
--- /dev/null
+++ b/chromium/components/content_settings/core/browser/content_settings_ephemeral_provider_unittest.cc
@@ -0,0 +1,179 @@
+// 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/content_settings/core/browser/content_settings_ephemeral_provider.h"
+
+#include <set>
+
+#include "base/test/simple_test_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content_settings {
+
+class ContentSettingsEphemeralProviderTest : public testing::Test {
+ public:
+ ContentSettingsEphemeralProviderTest() {
+ persistent_type_ = static_cast<ContentSettingsType>(1);
+ ephemeral_types_[0] = static_cast<ContentSettingsType>(0);
+ ephemeral_types_[1] = static_cast<ContentSettingsType>(2);
+ Reset();
+ }
+
+ void Reset() {
+ provider_.reset(new EphemeralProvider(true));
+
+ std::set<ContentSettingsType> supported_types;
+ supported_types.insert(ephemeral_types_[0]);
+ supported_types.insert(ephemeral_types_[1]);
+ provider()->SetSupportedTypesForTesting(supported_types);
+ }
+
+ EphemeralProvider* provider() { return provider_.get(); }
+ ContentSettingsType persistent_type() { return persistent_type_; }
+ ContentSettingsType ephemeral_type(int index) {
+ return ephemeral_types_[index];
+ }
+
+ private:
+ std::unique_ptr<EphemeralProvider> provider_;
+ ContentSettingsType persistent_type_;
+ ContentSettingsType ephemeral_types_[2];
+};
+
+// Tests if the ephemeral provider starts with an empty state.
+TEST_F(ContentSettingsEphemeralProviderTest, EmptyStart) {
+ EXPECT_EQ((size_t)0, provider()->GetCountForTesting());
+}
+
+// Tests if an ephemeral preference is stored and retrieved.
+TEST_F(ContentSettingsEphemeralProviderTest, EphemeralTypeStorageAndRetrieval) {
+ ContentSettingsPattern site_pattern =
+ ContentSettingsPattern::FromString("https://example.com");
+
+ EXPECT_TRUE(provider()->SetWebsiteSetting(
+ site_pattern, site_pattern, ephemeral_type(0), std::string(),
+ new base::Value(CONTENT_SETTING_ALLOW)));
+
+ std::unique_ptr<RuleIterator> rule_iterator =
+ provider()->GetRuleIterator(ephemeral_type(0), std::string(), false);
+ EXPECT_NE(nullptr, rule_iterator);
+ EXPECT_TRUE(rule_iterator->HasNext());
+ content_settings::Rule rule = rule_iterator->Next();
+ EXPECT_EQ(base::Value(CONTENT_SETTING_ALLOW), *rule.value);
+
+ // Overwrite previous value.
+ EXPECT_TRUE(provider()->SetWebsiteSetting(
+ site_pattern, site_pattern, ephemeral_type(0), std::string(),
+ new base::Value(CONTENT_SETTING_BLOCK)));
+
+ rule_iterator =
+ provider()->GetRuleIterator(ephemeral_type(0), std::string(), false);
+ EXPECT_NE(nullptr, rule_iterator);
+ EXPECT_TRUE(rule_iterator->HasNext());
+ rule = rule_iterator->Next();
+ EXPECT_EQ(base::Value(CONTENT_SETTING_BLOCK), *rule.value);
+}
+
+// Tests if storage of a persistent perference is rejected.
+TEST_F(ContentSettingsEphemeralProviderTest, PersistentTypeRejection) {
+ ContentSettingsPattern site_pattern =
+ ContentSettingsPattern::FromString("https://example.com");
+
+ std::unique_ptr<base::Value> value(new base::Value(false));
+ EXPECT_FALSE(provider()->SetWebsiteSetting(site_pattern, site_pattern,
+ persistent_type(), std::string(),
+ value.get()));
+ std::unique_ptr<RuleIterator> rule_iterator =
+ provider()->GetRuleIterator(persistent_type(), std::string(), false);
+ EXPECT_EQ(nullptr, rule_iterator);
+}
+
+// Tests if the last modified time of a stored preference is correctly returned.
+TEST_F(ContentSettingsEphemeralProviderTest, LastModifiedTime) {
+ ContentSettingsPattern site_pattern =
+ ContentSettingsPattern::FromString("https://example.com");
+
+ base::SimpleTestClock test_clock;
+ test_clock.SetNow(base::Time::Now());
+ provider()->SetClockForTesting(&test_clock);
+ base::Time t1 = test_clock.Now();
+
+ provider()->SetWebsiteSetting(site_pattern, site_pattern, ephemeral_type(0),
+ std::string(),
+ new base::Value(CONTENT_SETTING_BLOCK));
+ base::Time last_modified = provider()->GetWebsiteSettingLastModified(
+ site_pattern, site_pattern, ephemeral_type(0), std::string());
+ EXPECT_EQ(t1, last_modified);
+}
+
+// Tests if clearing all rules results in deletion of all stored preference.
+TEST_F(ContentSettingsEphemeralProviderTest, ClearAll) {
+ ContentSettingsPattern site_pattern1 =
+ ContentSettingsPattern::FromString("https://example1.com");
+ ContentSettingsPattern site_pattern2 =
+ ContentSettingsPattern::FromString("https://example2.com");
+
+ provider()->SetWebsiteSetting(site_pattern1, site_pattern1, ephemeral_type(0),
+ std::string(),
+ new base::Value(CONTENT_SETTING_BLOCK));
+ provider()->SetWebsiteSetting(site_pattern2, site_pattern2, ephemeral_type(0),
+ std::string(),
+ new base::Value(CONTENT_SETTING_ALLOW));
+ provider()->ClearAllContentSettingsRules(ephemeral_type(0));
+ std::unique_ptr<RuleIterator> rule_iterator =
+ provider()->GetRuleIterator(ephemeral_type(0), std::string(), false);
+ EXPECT_EQ(nullptr, rule_iterator);
+}
+
+// Tests if clearing all rules of one type doesn't effect other types.
+TEST_F(ContentSettingsEphemeralProviderTest, SelectiveClear) {
+ ContentSettingsPattern site_pattern =
+ ContentSettingsPattern::FromString("https://example.com");
+
+ provider()->SetWebsiteSetting(site_pattern, site_pattern, ephemeral_type(0),
+ std::string(),
+ new base::Value(CONTENT_SETTING_ALLOW));
+ provider()->SetWebsiteSetting(site_pattern, site_pattern, ephemeral_type(1),
+ std::string(),
+ new base::Value(CONTENT_SETTING_ALLOW));
+ provider()->ClearAllContentSettingsRules(ephemeral_type(0));
+ std::unique_ptr<RuleIterator> rule_iterator =
+ provider()->GetRuleIterator(ephemeral_type(1), std::string(), false);
+ EXPECT_NE(nullptr, rule_iterator);
+}
+
+// Tests if the stored preference is ephemeral.
+TEST_F(ContentSettingsEphemeralProviderTest, StorageIsEphemeral) {
+ ContentSettingsPattern site_pattern =
+ ContentSettingsPattern::FromString("https://example.com");
+
+ provider()->SetWebsiteSetting(site_pattern, site_pattern, ephemeral_type(0),
+ std::string(),
+ new base::Value(CONTENT_SETTING_BLOCK));
+ Reset();
+ std::unique_ptr<RuleIterator> rule_iterator =
+ provider()->GetRuleIterator(ephemeral_type(0), std::string(), false);
+ EXPECT_EQ(nullptr, rule_iterator);
+}
+
+// Tests if a pattern can be deleted by passing null value.
+TEST_F(ContentSettingsEphemeralProviderTest, DeleteValueByPassingNull) {
+ ContentSettingsPattern site_pattern =
+ ContentSettingsPattern::FromString("https://example.com");
+
+ provider()->SetWebsiteSetting(site_pattern, site_pattern, ephemeral_type(0),
+ std::string(),
+ new base::Value(CONTENT_SETTING_ALLOW));
+ std::unique_ptr<RuleIterator> rule_iterator =
+ provider()->GetRuleIterator(ephemeral_type(0), std::string(), false);
+ EXPECT_NE(nullptr, rule_iterator);
+
+ provider()->SetWebsiteSetting(site_pattern, site_pattern, ephemeral_type(0),
+ std::string(), nullptr);
+ rule_iterator =
+ provider()->GetRuleIterator(ephemeral_type(0), std::string(), false);
+ EXPECT_EQ(nullptr, rule_iterator);
+}
+
+} // namespace content_settings
diff --git a/chromium/components/content_settings/core/browser/content_settings_info.cc b/chromium/components/content_settings/core/browser/content_settings_info.cc
index e2af9be9ada..ba3eee9c5fe 100644
--- a/chromium/components/content_settings/core/browser/content_settings_info.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_info.cc
@@ -14,11 +14,13 @@ ContentSettingsInfo::ContentSettingsInfo(
const WebsiteSettingsInfo* website_settings_info,
const std::vector<std::string>& whitelisted_schemes,
const std::set<ContentSetting>& valid_settings,
- IncognitoBehavior incognito_behavior)
+ IncognitoBehavior incognito_behavior,
+ StorageBehavior storage_behavior)
: website_settings_info_(website_settings_info),
whitelisted_schemes_(whitelisted_schemes),
valid_settings_(valid_settings),
- incognito_behavior_(incognito_behavior) {}
+ incognito_behavior_(incognito_behavior),
+ storage_behavior_(storage_behavior) {}
ContentSettingsInfo::~ContentSettingsInfo() {}
diff --git a/chromium/components/content_settings/core/browser/content_settings_info.h b/chromium/components/content_settings/core/browser/content_settings_info.h
index 8f2451178c0..a7f691b39e5 100644
--- a/chromium/components/content_settings/core/browser/content_settings_info.h
+++ b/chromium/components/content_settings/core/browser/content_settings_info.h
@@ -32,11 +32,19 @@ class ContentSettingsInfo {
INHERIT_IF_LESS_PERMISSIVE
};
+ enum StorageBehavior {
+ // The setting is stored and used in future sessions.
+ PERSISTENT,
+ // The setting is only valid throughout the current session.
+ EPHEMERAL,
+ };
+
// This object does not take ownership of |website_settings_info|.
ContentSettingsInfo(const WebsiteSettingsInfo* website_settings_info,
const std::vector<std::string>& whitelisted_schemes,
const std::set<ContentSetting>& valid_settings,
- IncognitoBehavior incognito_behavior);
+ IncognitoBehavior incognito_behavior,
+ StorageBehavior storage_behavior);
~ContentSettingsInfo();
const WebsiteSettingsInfo* website_settings_info() const {
@@ -53,12 +61,14 @@ class ContentSettingsInfo {
bool IsDefaultSettingValid(ContentSetting setting) const;
IncognitoBehavior incognito_behavior() const { return incognito_behavior_; }
+ StorageBehavior storage_behavior() const { return storage_behavior_; }
private:
const WebsiteSettingsInfo* website_settings_info_;
const std::vector<std::string> whitelisted_schemes_;
const std::set<ContentSetting> valid_settings_;
const IncognitoBehavior incognito_behavior_;
+ const StorageBehavior storage_behavior_;
DISALLOW_COPY_AND_ASSIGN(ContentSettingsInfo);
};
diff --git a/chromium/components/content_settings/core/browser/content_settings_observer.h b/chromium/components/content_settings/core/browser/content_settings_observer.h
index 41868578942..db53e74272e 100644
--- a/chromium/components/content_settings/core/browser/content_settings_observer.h
+++ b/chromium/components/content_settings/core/browser/content_settings_observer.h
@@ -18,7 +18,7 @@ class Observer {
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsType content_type,
- std::string resource_identifier) = 0;
+ const std::string& resource_identifier) = 0;
protected:
virtual ~Observer() {}
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 5e4ed353a4e..0fc61567586 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
@@ -15,11 +15,12 @@
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/default_clock.h"
+#include "components/content_settings/core/browser/content_settings_info.h"
#include "components/content_settings/core/browser/content_settings_pref.h"
+#include "components/content_settings/core/browser/content_settings_registry.h"
#include "components/content_settings/core/browser/content_settings_rule.h"
#include "components/content_settings/core/browser/content_settings_utils.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "components/content_settings/core/browser/website_settings_info.h"
#include "components/content_settings/core/browser/website_settings_registry.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
@@ -107,15 +108,31 @@ PrefProvider::PrefProvider(PrefService* prefs,
pref_change_registrar_.Init(prefs_);
+ ContentSettingsRegistry* content_settings =
+ ContentSettingsRegistry::GetInstance();
WebsiteSettingsRegistry* website_settings =
WebsiteSettingsRegistry::GetInstance();
for (const WebsiteSettingsInfo* info : *website_settings) {
- content_settings_prefs_.insert(std::make_pair(
- info->type(),
- std::make_unique<ContentSettingsPref>(
- info->type(), prefs_, &pref_change_registrar_, info->pref_name(),
- is_incognito_,
- base::Bind(&PrefProvider::Notify, base::Unretained(this)))));
+ const ContentSettingsInfo* content_type_info =
+ content_settings->Get(info->type());
+ // If it's not a content setting, or it's persistent, handle it in this
+ // class.
+ if (!content_type_info || content_type_info->storage_behavior() ==
+ ContentSettingsInfo::PERSISTENT) {
+ content_settings_prefs_.insert(std::make_pair(
+ info->type(),
+ std::make_unique<ContentSettingsPref>(
+ info->type(), prefs_, &pref_change_registrar_, info->pref_name(),
+ is_incognito_,
+ base::Bind(&PrefProvider::Notify, base::Unretained(this)))));
+ } else if (info->type() == CONTENT_SETTINGS_TYPE_PLUGINS) {
+ // TODO(https://crbug.com/850062): Remove after M71, two milestones after
+ // migration of the Flash permissions to ephemeral provider.
+ flash_content_settings_pref_ = std::make_unique<ContentSettingsPref>(
+ info->type(), prefs_, &pref_change_registrar_, info->pref_name(),
+ is_incognito_,
+ base::Bind(&PrefProvider::Notify, base::Unretained(this)));
+ }
}
if (!is_incognito_) {
@@ -136,6 +153,9 @@ std::unique_ptr<RuleIterator> PrefProvider::GetRuleIterator(
ContentSettingsType content_type,
const ResourceIdentifier& resource_identifier,
bool incognito) const {
+ if (!supports_type(content_type))
+ return nullptr;
+
return GetPref(content_type)->GetRuleIterator(resource_identifier, incognito);
}
@@ -148,6 +168,9 @@ bool PrefProvider::SetWebsiteSetting(
DCHECK(CalledOnValidThread());
DCHECK(prefs_);
+ if (!supports_type(content_type))
+ return false;
+
// Default settings are set using a wildcard pattern for both
// |primary_pattern| and |secondary_pattern|. Don't store default settings in
// the |PrefProvider|. The |PrefProvider| handles settings for specific
@@ -175,6 +198,9 @@ base::Time PrefProvider::GetWebsiteSettingLastModified(
DCHECK(CalledOnValidThread());
DCHECK(prefs_);
+ if (!supports_type(content_type))
+ return base::Time();
+
return GetPref(content_type)
->GetWebsiteSettingLastModified(primary_pattern, secondary_pattern,
resource_identifier);
@@ -185,7 +211,17 @@ void PrefProvider::ClearAllContentSettingsRules(
DCHECK(CalledOnValidThread());
DCHECK(prefs_);
- GetPref(content_type)->ClearAllContentSettingsRules();
+ if (supports_type(content_type))
+ GetPref(content_type)->ClearAllContentSettingsRules();
+
+ // TODO(https://crbug.com/850062): Remove after M71, two milestones after
+ // migration of the Flash permissions to ephemeral provider.
+ // |flash_content_settings_pref_| is not null only if Flash permissions are
+ // ephemeral and handled in EphemeralProvider.
+ if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS &&
+ flash_content_settings_pref_) {
+ flash_content_settings_pref_->ClearAllContentSettingsRules();
+ }
}
void PrefProvider::ShutdownOnUIThread() {
diff --git a/chromium/components/content_settings/core/browser/content_settings_pref_provider.h b/chromium/components/content_settings/core/browser/content_settings_pref_provider.h
index 5eb0f07d04f..2dcdfbacda4 100644
--- a/chromium/components/content_settings/core/browser/content_settings_pref_provider.h
+++ b/chromium/components/content_settings/core/browser/content_settings_pref_provider.h
@@ -74,6 +74,12 @@ class PrefProvider : public UserModifiableProvider {
// Clean up the obsolete preferences from the user's profile.
void DiscardObsoletePreferences();
+ // Returns true if this provider supports the given |content_type|.
+ bool supports_type(ContentSettingsType content_type) const {
+ return content_settings_prefs_.find(content_type) !=
+ content_settings_prefs_.end();
+ }
+
// Weak; owned by the Profile and reset in ShutdownOnUIThread.
PrefService* prefs_;
@@ -86,6 +92,10 @@ class PrefProvider : public UserModifiableProvider {
std::map<ContentSettingsType, std::unique_ptr<ContentSettingsPref>>
content_settings_prefs_;
+ // TODO(https://crbug.com/850062): Remove after M71, two milestones after
+ // migration of the Flash permissions to ephemeral provider.
+ std::unique_ptr<ContentSettingsPref> flash_content_settings_pref_;
+
base::ThreadChecker thread_checker_;
base::Clock* clock_;
diff --git a/chromium/components/content_settings/core/browser/content_settings_registry.cc b/chromium/components/content_settings/core/browser/content_settings_registry.cc
index 6a0478a2e2d..46de20df689 100644
--- a/chromium/components/content_settings/core/browser/content_settings_registry.cc
+++ b/chromium/components/content_settings/core/browser/content_settings_registry.cc
@@ -7,6 +7,7 @@
#include <memory>
#include <utility>
+#include "base/feature_list.h"
#include "base/macros.h"
#include "base/stl_util.h"
#include "base/values.h"
@@ -14,6 +15,7 @@
#include "components/content_settings/core/browser/content_settings_utils.h"
#include "components/content_settings/core/browser/website_settings_registry.h"
#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/features.h"
namespace content_settings {
@@ -124,7 +126,8 @@ void ContentSettingsRegistry::Init() {
CONTENT_SETTING_SESSION_ONLY),
WebsiteSettingsInfo::REQUESTING_DOMAIN_ONLY_SCOPE,
WebsiteSettingsRegistry::ALL_PLATFORMS,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IN_INCOGNITO,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_IMAGES, "images", CONTENT_SETTING_ALLOW,
WebsiteSettingsInfo::SYNCABLE,
@@ -133,7 +136,8 @@ void ContentSettingsRegistry::Init() {
ValidSettings(CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK),
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IN_INCOGNITO,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_JAVASCRIPT, "javascript",
CONTENT_SETTING_ALLOW, WebsiteSettingsInfo::SYNCABLE,
@@ -143,18 +147,22 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
-
- Register(CONTENT_SETTINGS_TYPE_PLUGINS, "plugins",
- CONTENT_SETTING_DETECT_IMPORTANT_CONTENT,
- WebsiteSettingsInfo::SYNCABLE,
- WhitelistedSchemes(kChromeUIScheme, kChromeDevToolsScheme),
- ValidSettings(CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK,
- CONTENT_SETTING_ASK,
- CONTENT_SETTING_DETECT_IMPORTANT_CONTENT),
- WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
- WebsiteSettingsRegistry::DESKTOP,
- ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+ ContentSettingsInfo::INHERIT_IN_INCOGNITO,
+ ContentSettingsInfo::PERSISTENT);
+
+ Register(
+ CONTENT_SETTINGS_TYPE_PLUGINS, "plugins",
+ CONTENT_SETTING_DETECT_IMPORTANT_CONTENT, WebsiteSettingsInfo::SYNCABLE,
+ WhitelistedSchemes(kChromeUIScheme, kChromeDevToolsScheme),
+ ValidSettings(CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK,
+ CONTENT_SETTING_ASK,
+ CONTENT_SETTING_DETECT_IMPORTANT_CONTENT),
+ WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
+ WebsiteSettingsRegistry::DESKTOP,
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+ base::FeatureList::IsEnabled(features::kEnableEphemeralFlashPermission)
+ ? ContentSettingsInfo::EPHEMERAL
+ : ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_POPUPS, "popups", CONTENT_SETTING_BLOCK,
WebsiteSettingsInfo::SYNCABLE,
@@ -163,7 +171,8 @@ void ContentSettingsRegistry::Init() {
ValidSettings(CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK),
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::ALL_PLATFORMS,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IN_INCOGNITO,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_GEOLOCATION, "geolocation",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -173,7 +182,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_AND_TOP_LEVEL_ORIGIN_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, "notifications",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -185,7 +195,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsRegistry::PLATFORM_ANDROID,
// See also NotificationPermissionContext::DecidePermission which
// implements additional incognito exceptions.
- ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, "media-stream-mic",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -195,7 +206,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, "media-stream-camera",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -205,7 +217,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_PPAPI_BROKER, "ppapi-broker",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -214,7 +227,8 @@ void ContentSettingsRegistry::Init() {
CONTENT_SETTING_ASK),
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP,
- ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, "automatic-downloads",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::SYNCABLE,
@@ -225,7 +239,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IN_INCOGNITO,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_MIDI_SYSEX, "midi-sysex", CONTENT_SETTING_ASK,
WebsiteSettingsInfo::SYNCABLE, WhitelistedSchemes(),
@@ -234,7 +249,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_AND_TOP_LEVEL_ORIGIN_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER,
"protected-media-identifier", CONTENT_SETTING_ASK,
@@ -244,7 +260,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_AND_TOP_LEVEL_ORIGIN_SCOPE,
WebsiteSettingsRegistry::PLATFORM_ANDROID |
WebsiteSettingsRegistry::PLATFORM_CHROMEOS,
- ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_DURABLE_STORAGE, "durable-storage",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -254,7 +271,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IN_INCOGNITO,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC, "background-sync",
CONTENT_SETTING_ALLOW, WebsiteSettingsInfo::UNSYNCABLE,
@@ -263,7 +281,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IN_INCOGNITO,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_AUTOPLAY, "autoplay", CONTENT_SETTING_ALLOW,
WebsiteSettingsInfo::UNSYNCABLE, WhitelistedSchemes(),
@@ -271,7 +290,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IN_INCOGNITO,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_SOUND, "sound", CONTENT_SETTING_ALLOW,
WebsiteSettingsInfo::UNSYNCABLE, WhitelistedSchemes(),
@@ -279,7 +299,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IN_INCOGNITO,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_ADS, "subresource-filter",
CONTENT_SETTING_BLOCK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -288,7 +309,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IN_INCOGNITO,
+ ContentSettingsInfo::PERSISTENT);
// Content settings that aren't used to store any data. TODO(raymes): use a
// different mechanism rather than content settings to represent these.
@@ -299,14 +321,16 @@ void ContentSettingsRegistry::Init() {
WhitelistedSchemes(), ValidSettings(),
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP,
- ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_MIXEDSCRIPT, "mixed-script",
CONTENT_SETTING_DEFAULT, WebsiteSettingsInfo::UNSYNCABLE,
WhitelistedSchemes(), ValidSettings(),
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP,
- ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_BLUETOOTH_GUARD, "bluetooth-guard",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -315,7 +339,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_ACCESSIBILITY_EVENTS, "accessibility-events",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -325,7 +350,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_SENSORS, "sensors", CONTENT_SETTING_ALLOW,
WebsiteSettingsInfo::UNSYNCABLE, WhitelistedSchemes(),
@@ -333,7 +359,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IN_INCOGNITO,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_CLIPBOARD_READ, "clipboard",
CONTENT_SETTING_ASK, WebsiteSettingsInfo::UNSYNCABLE,
@@ -343,7 +370,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_PAYMENT_HANDLER, "payment-handler",
CONTENT_SETTING_ALLOW, WebsiteSettingsInfo::UNSYNCABLE,
@@ -352,7 +380,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IN_INCOGNITO);
+ ContentSettingsInfo::INHERIT_IN_INCOGNITO,
+ ContentSettingsInfo::PERSISTENT);
Register(CONTENT_SETTINGS_TYPE_USB_GUARD, "usb-guard", CONTENT_SETTING_ASK,
WebsiteSettingsInfo::UNSYNCABLE, WhitelistedSchemes(),
@@ -360,7 +389,8 @@ void ContentSettingsRegistry::Init() {
WebsiteSettingsInfo::TOP_LEVEL_ORIGIN_ONLY_SCOPE,
WebsiteSettingsRegistry::DESKTOP |
WebsiteSettingsRegistry::PLATFORM_ANDROID,
- ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE);
+ ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
+ ContentSettingsInfo::PERSISTENT);
}
void ContentSettingsRegistry::Register(
@@ -372,7 +402,8 @@ void ContentSettingsRegistry::Register(
const std::set<ContentSetting>& valid_settings,
WebsiteSettingsInfo::ScopingType scoping_type,
Platforms platforms,
- ContentSettingsInfo::IncognitoBehavior incognito_behavior) {
+ ContentSettingsInfo::IncognitoBehavior incognito_behavior,
+ ContentSettingsInfo::StorageBehavior storage_behavior) {
// Ensure that nothing has been registered yet for the given type.
DCHECK(!website_settings_registry_->Get(type));
std::unique_ptr<base::Value> default_value(
@@ -391,7 +422,7 @@ void ContentSettingsRegistry::Register(
DCHECK(!base::ContainsKey(content_settings_info_, type));
content_settings_info_[type] = std::make_unique<ContentSettingsInfo>(
website_settings_info, whitelisted_schemes, valid_settings,
- incognito_behavior);
+ incognito_behavior, storage_behavior);
}
} // namespace content_settings
diff --git a/chromium/components/content_settings/core/browser/content_settings_registry.h b/chromium/components/content_settings/core/browser/content_settings_registry.h
index 14675088fd8..b8d1068089e 100644
--- a/chromium/components/content_settings/core/browser/content_settings_registry.h
+++ b/chromium/components/content_settings/core/browser/content_settings_registry.h
@@ -66,7 +66,8 @@ class ContentSettingsRegistry {
const std::set<ContentSetting>& valid_settings,
WebsiteSettingsInfo::ScopingType scoping_type,
Platforms platforms,
- ContentSettingsInfo::IncognitoBehavior incognito_behavior);
+ ContentSettingsInfo::IncognitoBehavior incognito_behavior,
+ ContentSettingsInfo::StorageBehavior storage_behavior);
Map content_settings_info_;
WebsiteSettingsRegistry* website_settings_registry_;
diff --git a/chromium/components/content_settings/core/browser/cookie_settings.cc b/chromium/components/content_settings/core/browser/cookie_settings.cc
index a1252ad44ce..47430eec6e8 100644
--- a/chromium/components/content_settings/core/browser/cookie_settings.cc
+++ b/chromium/components/content_settings/core/browser/cookie_settings.cc
@@ -16,25 +16,8 @@
#include "extensions/buildflags/buildflags.h"
#include "net/base/net_errors.h"
#include "net/base/static_cookie_policy.h"
-#include "net/cookies/cookie_util.h"
#include "url/gurl.h"
-namespace {
-
-bool IsValidSetting(ContentSetting setting) {
- return (setting == CONTENT_SETTING_ALLOW ||
- setting == CONTENT_SETTING_SESSION_ONLY ||
- setting == CONTENT_SETTING_BLOCK);
-}
-
-bool IsAllowed(ContentSetting setting) {
- DCHECK(IsValidSetting(setting));
- return (setting == CONTENT_SETTING_ALLOW ||
- setting == CONTENT_SETTING_SESSION_ONLY);
-}
-
-} // namespace
-
namespace content_settings {
CookieSettings::CookieSettings(
@@ -58,50 +41,6 @@ ContentSetting CookieSettings::GetDefaultCookieSetting(
CONTENT_SETTINGS_TYPE_COOKIES, provider_id);
}
-bool CookieSettings::IsCookieAccessAllowed(const GURL& url,
- const GURL& first_party_url) const {
- ContentSetting setting;
- GetCookieSetting(url, first_party_url, nullptr, &setting);
- return IsAllowed(setting);
-}
-
-bool CookieSettings::IsCookieSessionOnly(const GURL& origin) const {
- ContentSetting setting;
- GetCookieSetting(origin, origin, nullptr, &setting);
- DCHECK(IsValidSetting(setting));
- return (setting == CONTENT_SETTING_SESSION_ONLY);
-}
-
-bool CookieSettings::ShouldDeleteCookieOnExit(
- const ContentSettingsForOneType& cookie_settings,
- const std::string& domain,
- bool is_https) const {
- GURL origin = net::cookie_util::CookieOriginToURL(domain, is_https);
- ContentSetting setting;
- GetCookieSetting(origin, origin, nullptr, &setting);
- DCHECK(IsValidSetting(setting));
- if (setting == CONTENT_SETTING_ALLOW)
- return false;
- // Non-secure cookies are readable by secure sites. We need to check for
- // https pattern if http is not allowed. The section below is independent
- // of the scheme so we can just retry from here.
- if (!is_https)
- return ShouldDeleteCookieOnExit(cookie_settings, domain, true);
- // Check if there is a more precise rule that "domain matches" this cookie.
- bool matches_session_only_rule = false;
- for (const auto& entry : cookie_settings) {
- const std::string& host = entry.primary_pattern.GetHost();
- if (net::cookie_util::IsDomainMatch(domain, host)) {
- if (entry.GetContentSetting() == CONTENT_SETTING_ALLOW) {
- return false;
- } else if (entry.GetContentSetting() == CONTENT_SETTING_SESSION_ONLY) {
- matches_session_only_rule = true;
- }
- }
- }
- return setting == CONTENT_SETTING_SESSION_ONLY || matches_session_only_rule;
-}
-
void CookieSettings::GetCookieSettings(
ContentSettingsForOneType* settings) const {
host_content_settings_map_->GetSettingsForOneType(
diff --git a/chromium/components/content_settings/core/browser/cookie_settings.h b/chromium/components/content_settings/core/browser/cookie_settings.h
index 11b67680f6a..6d3abef0773 100644
--- a/chromium/components/content_settings/core/browser/cookie_settings.h
+++ b/chromium/components/content_settings/core/browser/cookie_settings.h
@@ -14,6 +14,7 @@
#include "base/threading/thread_checker.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/cookie_settings_base.h"
#include "components/keyed_service/core/refcounted_keyed_service.h"
#include "components/prefs/pref_change_registrar.h"
@@ -28,7 +29,8 @@ const char kDummyExtensionScheme[] = ":no-extension-scheme:";
// A frontend to the cookie settings of |HostContentSettingsMap|. Handles
// cookie-specific logic such as blocking third-party cookies. Written on the UI
// thread and read on any thread.
-class CookieSettings : public RefcountedKeyedService {
+class CookieSettings : public CookieSettingsBase,
+ public RefcountedKeyedService {
public:
// Creates a new CookieSettings instance.
// The caller is responsible for ensuring that |extension_scheme| is valid for
@@ -45,34 +47,6 @@ class CookieSettings : public RefcountedKeyedService {
// This may be called on any thread.
ContentSetting GetDefaultCookieSetting(std::string* provider_id) const;
- // Returns true if the page identified by (|url|, |first_party_url|) is
- // allowed to access (i.e., read or write) cookies.
- //
- // This may be called on any thread.
- bool IsCookieAccessAllowed(const GURL& url,
- const GURL& first_party_url) const;
-
- // Returns true if the cookie set by a page identified by |url| should be
- // session only. Querying this only makes sense if |IsCookieAccessAllowed|
- // has returned true.
- //
- // This may be called on any thread.
- bool IsCookieSessionOnly(const GURL& url) const;
-
- // Returns true if the cookie associated with |domain| should be deleted
- // on exit.
- // This uses domain matching as described in section 5.1.3 of RFC 6265 to
- // identify content setting rules that could have influenced the cookie
- // when it was created.
- // As |cookie_settings| can be expensive to create, it should be cached if
- // multiple calls to ShouldDeleteCookieOnExit() are made.
- //
- // This may be called on any thread.
- bool ShouldDeleteCookieOnExit(
- const ContentSettingsForOneType& cookie_settings,
- const std::string& domain,
- bool is_https) const;
-
// Returns all patterns with a non-default cookie setting, mapped to their
// actual settings, in the precedence order of the setting rules. |settings|
// must be a non-nullptr outparam.
@@ -103,11 +77,11 @@ class CookieSettings : public RefcountedKeyedService {
// called.
void ShutdownOnUIThread() override;
- // A helper for applying third party cookie blocking rules.
+ // content_settings::CookieSettingsBase:
void GetCookieSetting(const GURL& url,
const GURL& first_party_url,
content_settings::SettingSource* source,
- ContentSetting* cookie_setting) const;
+ ContentSetting* cookie_setting) const override;
static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
diff --git a/chromium/components/content_settings/core/browser/host_content_settings_map.cc b/chromium/components/content_settings/core/browser/host_content_settings_map.cc
index d45b95a6d6d..a5f6366cb19 100644
--- a/chromium/components/content_settings/core/browser/host_content_settings_map.cc
+++ b/chromium/components/content_settings/core/browser/host_content_settings_map.cc
@@ -22,6 +22,7 @@
#include "build/build_config.h"
#include "components/content_settings/core/browser/content_settings_default_provider.h"
#include "components/content_settings/core/browser/content_settings_details.h"
+#include "components/content_settings/core/browser/content_settings_ephemeral_provider.h"
#include "components/content_settings/core/browser/content_settings_info.h"
#include "components/content_settings/core/browser/content_settings_observable_provider.h"
#include "components/content_settings/core/browser/content_settings_policy_provider.h"
@@ -34,6 +35,7 @@
#include "components/content_settings/core/browser/website_settings_registry.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/content_settings/core/common/content_settings_utils.h"
+#include "components/content_settings/core/common/features.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
@@ -65,6 +67,7 @@ constexpr ProviderNamesSourceMapEntry kProviderNamesSourceMap[] = {
{"supervised_user", content_settings::SETTING_SOURCE_SUPERVISED},
{"extension", content_settings::SETTING_SOURCE_EXTENSION},
{"notification_android", content_settings::SETTING_SOURCE_USER},
+ {"ephemeral", content_settings::SETTING_SOURCE_USER},
{"preference", content_settings::SETTING_SOURCE_USER},
{"default", content_settings::SETTING_SOURCE_USER},
{"tests", content_settings::SETTING_SOURCE_USER},
@@ -185,6 +188,13 @@ content_settings::PatternPair GetPatternsForContentSettingsType(
return patterns;
}
+// This enum is used to collect Flash permission data.
+enum class FlashPermissions {
+ kFirstTime = 0,
+ kRepeated = 1,
+ kMaxValue = kRepeated,
+};
+
} // namespace
HostContentSettingsMap::HostContentSettingsMap(PrefService* prefs,
@@ -219,6 +229,13 @@ HostContentSettingsMap::HostContentSettingsMap(PrefService* prefs,
if (is_guest_profile)
pref_provider_->ClearPrefs();
+ content_settings::EphemeralProvider* ephemeral_provider =
+ new content_settings::EphemeralProvider(store_last_modified_);
+ content_settings_providers_[EPHEMERAL_PROVIDER] =
+ base::WrapUnique(ephemeral_provider);
+ user_modifiable_providers_.push_back(ephemeral_provider);
+ ephemeral_provider->AddObserver(this);
+
auto default_provider = std::make_unique<content_settings::DefaultProvider>(
prefs_, is_incognito_);
default_provider->AddObserver(this);
@@ -501,6 +518,23 @@ void HostContentSettingsMap::SetContentSettingCustomScope(
DCHECK(content_settings::ContentSettingsRegistry::GetInstance()->Get(
content_type));
+ // Record stats on Flash permission grants with ephemeral storage.
+ if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS &&
+ setting == CONTENT_SETTING_ALLOW &&
+ base::FeatureList::IsEnabled(
+ content_settings::features::kEnableEphemeralFlashPermission)) {
+ GURL url(primary_pattern.ToString());
+ ContentSettingsPattern temp_patterns[2];
+ std::unique_ptr<base::Value> value(GetContentSettingValueAndPatterns(
+ content_settings_providers_[PREF_PROVIDER].get(), url, url,
+ CONTENT_SETTINGS_TYPE_PLUGINS_DATA, resource_identifier, is_incognito_,
+ temp_patterns, temp_patterns + 1));
+
+ UMA_HISTOGRAM_ENUMERATION(
+ "ContentSettings.EphemeralFlashPermission",
+ value ? FlashPermissions::kRepeated : FlashPermissions::kFirstTime);
+ }
+
std::unique_ptr<base::Value> value;
// A value of CONTENT_SETTING_DEFAULT implies deleting the content setting.
if (setting != CONTENT_SETTING_DEFAULT) {
@@ -662,7 +696,7 @@ void HostContentSettingsMap::OnContentSettingChanged(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsType content_type,
- std::string resource_identifier) {
+ const std::string& resource_identifier) {
for (content_settings::Observer& observer : observers_) {
observer.OnContentSettingChanged(primary_pattern, secondary_pattern,
content_type, resource_identifier);
diff --git a/chromium/components/content_settings/core/browser/host_content_settings_map.h b/chromium/components/content_settings/core/browser/host_content_settings_map.h
index cbb6c009640..1b65f228ad8 100644
--- a/chromium/components/content_settings/core/browser/host_content_settings_map.h
+++ b/chromium/components/content_settings/core/browser/host_content_settings_map.h
@@ -59,6 +59,7 @@ class HostContentSettingsMap : public content_settings::Observer,
SUPERVISED_PROVIDER,
CUSTOM_EXTENSION_PROVIDER,
NOTIFICATION_ANDROID_PROVIDER,
+ EPHEMERAL_PROVIDER,
PREF_PROVIDER,
DEFAULT_PROVIDER,
@@ -274,7 +275,7 @@ class HostContentSettingsMap : public content_settings::Observer,
void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsType content_type,
- std::string resource_identifier) override;
+ const std::string& resource_identifier) override;
// Returns the ProviderType associated with the given source string.
// TODO(estade): I regret adding this. At the moment there are no legitimate
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 5c74c36aa39..67e410af09e 100644
--- a/chromium/components/content_settings/core/browser/website_settings_registry.cc
+++ b/chromium/components/content_settings/core/browser/website_settings_registry.cc
@@ -10,6 +10,7 @@
#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/features.h"
namespace {
@@ -158,11 +159,11 @@ void WebsiteSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
DESKTOP | PLATFORM_ANDROID,
WebsiteSettingsInfo::INHERIT_IN_INCOGNITO);
- Register(
- CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, "password-protection", nullptr,
- WebsiteSettingsInfo::UNSYNCABLE, WebsiteSettingsInfo::NOT_LOSSY,
- WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
- DESKTOP | PLATFORM_ANDROID, WebsiteSettingsInfo::INHERIT_IN_INCOGNITO);
+ Register(CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, "password-protection",
+ nullptr, WebsiteSettingsInfo::UNSYNCABLE,
+ WebsiteSettingsInfo::NOT_LOSSY,
+ WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE, DESKTOP,
+ WebsiteSettingsInfo::INHERIT_IN_INCOGNITO);
// Set when an origin is activated for subresource filtering and the
// associated UI is shown to the user. Cleared when a site is de-activated or
// the first URL matching the origin is removed from history.
@@ -181,10 +182,17 @@ void WebsiteSettingsRegistry::Init() {
WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE,
DESKTOP | PLATFORM_ANDROID,
WebsiteSettingsInfo::DONT_INHERIT_IN_INCOGNITO);
- Register(CONTENT_SETTINGS_TYPE_PLUGINS_DATA, "flash-data", nullptr,
- WebsiteSettingsInfo::UNSYNCABLE, WebsiteSettingsInfo::NOT_LOSSY,
- WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE, DESKTOP,
- WebsiteSettingsInfo::INHERIT_IN_INCOGNITO);
+ Register(
+ CONTENT_SETTINGS_TYPE_PLUGINS_DATA, "flash-data", nullptr,
+ // To counteract the reduced usability of the Flash permission
+ // when it becomes ephemeral, we sync the bit indicating that
+ // the Flash permission should be displayed in the page info.
+ base::FeatureList::IsEnabled(features::kEnableEphemeralFlashPermission)
+ ? WebsiteSettingsInfo::SYNCABLE
+ : WebsiteSettingsInfo::UNSYNCABLE,
+ WebsiteSettingsInfo::NOT_LOSSY,
+ WebsiteSettingsInfo::REQUESTING_ORIGIN_ONLY_SCOPE, DESKTOP,
+ WebsiteSettingsInfo::INHERIT_IN_INCOGNITO);
}
} // namespace content_settings
diff --git a/chromium/components/content_settings/core/common/BUILD.gn b/chromium/components/content_settings/core/common/BUILD.gn
index e5ca4c079dd..b706236db94 100644
--- a/chromium/components/content_settings/core/common/BUILD.gn
+++ b/chromium/components/content_settings/core/common/BUILD.gn
@@ -15,6 +15,10 @@ static_library("common") {
"content_settings_types.h",
"content_settings_utils.cc",
"content_settings_utils.h",
+ "cookie_settings_base.cc",
+ "cookie_settings_base.h",
+ "features.cc",
+ "features.h",
"pref_names.cc",
"pref_names.h",
]
@@ -35,10 +39,12 @@ source_set("unit_tests") {
sources = [
"content_settings_pattern_parser_unittest.cc",
"content_settings_pattern_unittest.cc",
+ "cookie_settings_base_unittest.cc",
]
deps = [
":common",
+ "//base",
"//net",
"//testing/gmock",
"//testing/gtest",
diff --git a/chromium/components/content_settings/core/common/DEPS b/chromium/components/content_settings/core/common/DEPS
index 6542949df6b..7aab7130bcd 100644
--- a/chromium/components/content_settings/core/common/DEPS
+++ b/chromium/components/content_settings/core/common/DEPS
@@ -2,6 +2,7 @@ include_rules = [
"+mojo/public/cpp/base",
"+mojo/public/cpp/bindings",
"+net/base",
+ "+net/cookies/cookie_util.h",
"+testing",
"+url",
]
diff --git a/chromium/components/content_settings/core/common/cookie_settings_base.cc b/chromium/components/content_settings/core/common/cookie_settings_base.cc
new file mode 100644
index 00000000000..e4909a9b80e
--- /dev/null
+++ b/chromium/components/content_settings/core/common/cookie_settings_base.cc
@@ -0,0 +1,70 @@
+// 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/content_settings/core/common/cookie_settings_base.h"
+#include "net/cookies/cookie_util.h"
+#include "url/gurl.h"
+
+namespace content_settings {
+
+bool CookieSettingsBase::ShouldDeleteCookieOnExit(
+ const ContentSettingsForOneType& cookie_settings,
+ const std::string& domain,
+ bool is_https) const {
+ GURL origin = net::cookie_util::CookieOriginToURL(domain, is_https);
+ ContentSetting setting;
+ GetCookieSetting(origin, origin, nullptr, &setting);
+ DCHECK(IsValidSetting(setting));
+ if (setting == CONTENT_SETTING_ALLOW)
+ return false;
+ // Non-secure cookies are readable by secure sites. We need to check for
+ // https pattern if http is not allowed. The section below is independent
+ // of the scheme so we can just retry from here.
+ if (!is_https)
+ return ShouldDeleteCookieOnExit(cookie_settings, domain, true);
+ // Check if there is a more precise rule that "domain matches" this cookie.
+ bool matches_session_only_rule = false;
+ for (const auto& entry : cookie_settings) {
+ const std::string& host = entry.primary_pattern.GetHost();
+ if (net::cookie_util::IsDomainMatch(domain, host)) {
+ if (entry.GetContentSetting() == CONTENT_SETTING_ALLOW) {
+ return false;
+ } else if (entry.GetContentSetting() == CONTENT_SETTING_SESSION_ONLY) {
+ matches_session_only_rule = true;
+ }
+ }
+ }
+ return setting == CONTENT_SETTING_SESSION_ONLY || matches_session_only_rule;
+}
+
+bool CookieSettingsBase::IsCookieAccessAllowed(
+ const GURL& url,
+ const GURL& first_party_url) const {
+ ContentSetting setting;
+ GetCookieSetting(url, first_party_url, nullptr, &setting);
+ return IsAllowed(setting);
+}
+
+bool CookieSettingsBase::IsCookieSessionOnly(const GURL& origin) const {
+ ContentSetting setting;
+ GetCookieSetting(origin, origin, nullptr, &setting);
+ DCHECK(IsValidSetting(setting));
+ return setting == CONTENT_SETTING_SESSION_ONLY;
+}
+
+// static
+bool CookieSettingsBase::IsValidSetting(ContentSetting setting) {
+ return (setting == CONTENT_SETTING_ALLOW ||
+ setting == CONTENT_SETTING_SESSION_ONLY ||
+ setting == CONTENT_SETTING_BLOCK);
+}
+
+// static
+bool CookieSettingsBase::IsAllowed(ContentSetting setting) {
+ DCHECK(IsValidSetting(setting));
+ return (setting == CONTENT_SETTING_ALLOW ||
+ setting == CONTENT_SETTING_SESSION_ONLY);
+}
+
+} // namespace content_settings
diff --git a/chromium/components/content_settings/core/common/cookie_settings_base.h b/chromium/components/content_settings/core/common/cookie_settings_base.h
new file mode 100644
index 00000000000..e23181772fe
--- /dev/null
+++ b/chromium/components/content_settings/core/common/cookie_settings_base.h
@@ -0,0 +1,64 @@
+// 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_CONTENT_SETTINGS_CORE_COMMON_COOKIE_SETTINGS_BASE_H_
+#define COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_COOKIE_SETTINGS_BASE_H_
+
+#include <string>
+
+#include "components/content_settings/core/common/content_settings.h"
+
+namespace content_settings {
+
+class CookieSettingsBase {
+ public:
+ CookieSettingsBase() = default;
+ virtual ~CookieSettingsBase() = default;
+
+ // Returns true if the cookie associated with |domain| should be deleted
+ // on exit.
+ // This uses domain matching as described in section 5.1.3 of RFC 6265 to
+ // identify content setting rules that could have influenced the cookie
+ // when it was created.
+ // As |cookie_settings| can be expensive to create, it should be cached if
+ // multiple calls to ShouldDeleteCookieOnExit() are made.
+ //
+ // This may be called on any thread.
+ bool ShouldDeleteCookieOnExit(
+ const ContentSettingsForOneType& cookie_settings,
+ const std::string& domain,
+ bool is_https) const;
+
+ // Returns true if the page identified by (|url|, |first_party_url|) is
+ // allowed to access (i.e., read or write) cookies.
+ //
+ // This may be called on any thread.
+ bool IsCookieAccessAllowed(const GURL& url,
+ const GURL& first_party_url) const;
+
+ // Returns true if the cookie set by a page identified by |url| should be
+ // session only. Querying this only makes sense if |IsCookieAccessAllowed|
+ // has returned true.
+ //
+ // This may be called on any thread.
+ bool IsCookieSessionOnly(const GURL& url) const;
+
+ // A helper for applying third party cookie blocking rules.
+ virtual void GetCookieSetting(const GURL& url,
+ const GURL& first_party_url,
+ content_settings::SettingSource* source,
+ ContentSetting* cookie_setting) const = 0;
+
+ // Determines whether |setting| is a valid content setting for cookies.
+ static bool IsValidSetting(ContentSetting setting);
+ // Determines whether |setting| means the cookie should be allowed.
+ static bool IsAllowed(ContentSetting setting);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CookieSettingsBase);
+};
+
+} // namespace content_settings
+
+#endif // COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_COOKIE_SETTINGS_BASE_H_
diff --git a/chromium/components/content_settings/core/common/cookie_settings_base_unittest.cc b/chromium/components/content_settings/core/common/cookie_settings_base_unittest.cc
new file mode 100644
index 00000000000..0d960de2392
--- /dev/null
+++ b/chromium/components/content_settings/core/common/cookie_settings_base_unittest.cc
@@ -0,0 +1,153 @@
+// 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/content_settings/core/common/cookie_settings_base.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace content_settings {
+namespace {
+
+constexpr char kDomain[] = "foo.com";
+
+using GetSettingCallback = base::RepeatingCallback<ContentSetting(const GURL&)>;
+
+ContentSettingPatternSource CreateSetting(ContentSetting setting) {
+ return ContentSettingPatternSource(
+ ContentSettingsPattern::FromString(kDomain),
+ ContentSettingsPattern::Wildcard(), base::Value(setting), std::string(),
+ false);
+}
+
+class CallbackCookieSettings : public CookieSettingsBase {
+ public:
+ explicit CallbackCookieSettings(GetSettingCallback callback)
+ : callback_(std::move(callback)) {}
+
+ // CookieSettingsBase:
+ void GetCookieSetting(const GURL& url,
+ const GURL& first_party_url,
+ content_settings::SettingSource* source,
+ ContentSetting* cookie_setting) const override {
+ *cookie_setting = callback_.Run(url);
+ }
+
+ private:
+ GetSettingCallback callback_;
+};
+
+TEST(CookieSettingsBaseTest, ShouldDeleteSessionOnly) {
+ CallbackCookieSettings settings(base::BindRepeating(
+ [](const GURL&) { return CONTENT_SETTING_SESSION_ONLY; }));
+ EXPECT_TRUE(settings.ShouldDeleteCookieOnExit({}, kDomain, false));
+}
+
+TEST(CookieSettingsBaseTest, ShouldNotDeleteAllowed) {
+ CallbackCookieSettings settings(
+ base::BindRepeating([](const GURL&) { return CONTENT_SETTING_ALLOW; }));
+ EXPECT_FALSE(settings.ShouldDeleteCookieOnExit({}, kDomain, false));
+}
+
+TEST(CookieSettingsBaseTest, ShouldNotDeleteAllowedHttps) {
+ CallbackCookieSettings settings(base::BindRepeating([](const GURL& url) {
+ return url.SchemeIsCryptographic() ? CONTENT_SETTING_ALLOW
+ : CONTENT_SETTING_BLOCK;
+ }));
+ EXPECT_FALSE(settings.ShouldDeleteCookieOnExit({}, kDomain, false));
+ EXPECT_FALSE(settings.ShouldDeleteCookieOnExit({}, kDomain, true));
+}
+
+TEST(CookieSettingsBaseTest, ShouldDeleteDomainSettingSessionOnly) {
+ CallbackCookieSettings settings(
+ base::BindRepeating([](const GURL&) { return CONTENT_SETTING_BLOCK; }));
+ EXPECT_TRUE(settings.ShouldDeleteCookieOnExit(
+ {CreateSetting(CONTENT_SETTING_SESSION_ONLY)}, kDomain, false));
+}
+
+TEST(CookieSettingsBaseTest, ShouldNotDeleteDomainSettingAllow) {
+ CallbackCookieSettings settings(
+ base::BindRepeating([](const GURL&) { return CONTENT_SETTING_BLOCK; }));
+ EXPECT_FALSE(settings.ShouldDeleteCookieOnExit(
+ {CreateSetting(CONTENT_SETTING_ALLOW)}, kDomain, false));
+}
+
+TEST(CookieSettingsBaseTest,
+ ShouldNotDeleteDomainSettingAllowAfterSessionOnly) {
+ CallbackCookieSettings settings(
+ base::BindRepeating([](const GURL&) { return CONTENT_SETTING_BLOCK; }));
+ EXPECT_FALSE(settings.ShouldDeleteCookieOnExit(
+ {CreateSetting(CONTENT_SETTING_SESSION_ONLY),
+ CreateSetting(CONTENT_SETTING_ALLOW)},
+ kDomain, false));
+}
+
+TEST(CookieSettingsBaseTest, ShouldNotDeleteDomainSettingBlock) {
+ CallbackCookieSettings settings(
+ base::BindRepeating([](const GURL&) { return CONTENT_SETTING_BLOCK; }));
+ EXPECT_FALSE(settings.ShouldDeleteCookieOnExit(
+ {CreateSetting(CONTENT_SETTING_BLOCK)}, kDomain, false));
+}
+
+TEST(CookieSettingsBaseTest, ShouldNotDeleteNoDomainMatch) {
+ CallbackCookieSettings settings(
+ base::BindRepeating([](const GURL&) { return CONTENT_SETTING_BLOCK; }));
+ EXPECT_FALSE(settings.ShouldDeleteCookieOnExit(
+ {CreateSetting(CONTENT_SETTING_SESSION_ONLY)}, "other.com", false));
+}
+
+TEST(CookieSettingsBaseTest, CookieAccessNotAllowedWithBlockedSetting) {
+ CallbackCookieSettings settings(
+ base::BindRepeating([](const GURL&) { return CONTENT_SETTING_BLOCK; }));
+ EXPECT_FALSE(settings.IsCookieAccessAllowed(GURL(), GURL()));
+}
+
+TEST(CookieSettingsBaseTest, CookieAccessAllowedWithAllowSetting) {
+ CallbackCookieSettings settings(
+ base::BindRepeating([](const GURL&) { return CONTENT_SETTING_ALLOW; }));
+ EXPECT_TRUE(settings.IsCookieAccessAllowed(GURL(), GURL()));
+}
+
+TEST(CookieSettingsBaseTest, CookieAccessAllowedWithSessionOnlySetting) {
+ CallbackCookieSettings settings(base::BindRepeating(
+ [](const GURL&) { return CONTENT_SETTING_SESSION_ONLY; }));
+ EXPECT_TRUE(settings.IsCookieAccessAllowed(GURL(), GURL()));
+}
+
+TEST(CookieSettingsBaseTest, IsCookieSessionOnlyWithAllowSetting) {
+ CallbackCookieSettings settings(
+ base::BindRepeating([](const GURL&) { return CONTENT_SETTING_ALLOW; }));
+ EXPECT_FALSE(settings.IsCookieSessionOnly(GURL()));
+}
+
+TEST(CookieSettingsBaseTest, IsCookieSessionOnlyWithBlockSetting) {
+ CallbackCookieSettings settings(
+ base::BindRepeating([](const GURL&) { return CONTENT_SETTING_BLOCK; }));
+ EXPECT_FALSE(settings.IsCookieSessionOnly(GURL()));
+}
+
+TEST(CookieSettingsBaseTest, IsCookieSessionOnlySessionWithOnlySetting) {
+ CallbackCookieSettings settings(base::BindRepeating(
+ [](const GURL&) { return CONTENT_SETTING_SESSION_ONLY; }));
+ EXPECT_TRUE(settings.IsCookieSessionOnly(GURL()));
+}
+
+TEST(CookieSettingsBaseTest, IsValidSetting) {
+ EXPECT_FALSE(CookieSettingsBase::IsValidSetting(CONTENT_SETTING_DEFAULT));
+ EXPECT_FALSE(CookieSettingsBase::IsValidSetting(CONTENT_SETTING_ASK));
+ EXPECT_TRUE(CookieSettingsBase::IsValidSetting(CONTENT_SETTING_ALLOW));
+ EXPECT_TRUE(CookieSettingsBase::IsValidSetting(CONTENT_SETTING_BLOCK));
+ EXPECT_TRUE(CookieSettingsBase::IsValidSetting(CONTENT_SETTING_SESSION_ONLY));
+}
+
+TEST(CookieSettingsBaseTest, IsAllowed) {
+ EXPECT_FALSE(CookieSettingsBase::IsAllowed(CONTENT_SETTING_BLOCK));
+ EXPECT_TRUE(CookieSettingsBase::IsAllowed(CONTENT_SETTING_ALLOW));
+ EXPECT_TRUE(CookieSettingsBase::IsAllowed(CONTENT_SETTING_SESSION_ONLY));
+}
+
+} // namespace
+} // namespace content_settings
diff --git a/chromium/components/content_settings/core/common/features.cc b/chromium/components/content_settings/core/common/features.cc
new file mode 100644
index 00000000000..e372175f607
--- /dev/null
+++ b/chromium/components/content_settings/core/common/features.cc
@@ -0,0 +1,18 @@
+// 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.
+
+// This file defines all the public base::FeatureList features for the chrome
+// module.
+
+#include "components/content_settings/core/common/features.h"
+
+namespace content_settings {
+namespace features {
+
+// Makes Flash plugin permissions persistent only through the current session.
+const base::Feature kEnableEphemeralFlashPermission{
+ "EnableEphemeralFlashPermission", base::FEATURE_ENABLED_BY_DEFAULT};
+
+} // namespace features
+} // namespace content_settings
diff --git a/chromium/components/content_settings/core/common/features.h b/chromium/components/content_settings/core/common/features.h
new file mode 100644
index 00000000000..ac2bafd2f84
--- /dev/null
+++ b/chromium/components/content_settings/core/common/features.h
@@ -0,0 +1,21 @@
+// 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.
+
+// This file defines all the public base::FeatureList features for the chrome
+// module.
+
+#ifndef COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_FEATURES_H_
+#define COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_FEATURES_H_
+
+#include "base/feature_list.h"
+
+namespace content_settings {
+namespace features {
+
+extern const base::Feature kEnableEphemeralFlashPermission;
+
+} // namespace features
+} // namespace content_settings
+
+#endif // COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_FEATURES_H_
diff --git a/chromium/components/content_view/BUILD.gn b/chromium/components/content_view/BUILD.gn
deleted file mode 100644
index c50f552d3c5..00000000000
--- a/chromium/components/content_view/BUILD.gn
+++ /dev/null
@@ -1,15 +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.
-
-import("//build/config/android/rules.gni")
-
-android_library("content_view_java") {
- deps = [
- "//base:base_java",
- "//content/public/android:content_java",
- "//ui/android:ui_java",
- ]
- java_files =
- [ "java/src/org/chromium/components/content_view/ContentView.java" ]
-}
diff --git a/chromium/components/content_view/README b/chromium/components/content_view/README
deleted file mode 100644
index e8196ae12f9..00000000000
--- a/chromium/components/content_view/README
+++ /dev/null
@@ -1,2 +0,0 @@
-- ContentView exists in Android UI view hierarchy, and exposes various
-view functionality to content layer.
diff --git a/chromium/components/crash/android/OWNERS b/chromium/components/crash/android/OWNERS
index 9c568c2eec8..1c0302aaf81 100644
--- a/chromium/components/crash/android/OWNERS
+++ b/chromium/components/crash/android/OWNERS
@@ -1,7 +1,3 @@
-# Java readability and UMA
-mariakhomenko@chromium.org
-
-# Crash uploading mechanism
gsennton@chromium.org
isherman@chromium.org
diff --git a/chromium/components/crash/content/app/BUILD.gn b/chromium/components/crash/content/app/BUILD.gn
index 4d9d30aa891..d4fa4c3e6de 100644
--- a/chromium/components/crash/content/app/BUILD.gn
+++ b/chromium/components/crash/content/app/BUILD.gn
@@ -74,6 +74,7 @@ static_library("app") {
"//content/public/common:result_codes",
"//sandbox",
"//third_party/breakpad:client",
+ "//third_party/crashpad/crashpad/snapshot",
]
# Clang's -mstackrealign doesn't work well with
diff --git a/chromium/components/crash/content/app/breakpad_linux.cc b/chromium/components/crash/content/app/breakpad_linux.cc
index 76f15cb5b51..a7378204ffa 100644
--- a/chromium/components/crash/content/app/breakpad_linux.cc
+++ b/chromium/components/crash/content/app/breakpad_linux.cc
@@ -55,6 +55,7 @@
#include <sys/stat.h>
#include "base/android/build_info.h"
+#include "base/android/java_exception_reporter.h"
#include "base/android/path_utils.h"
#include "base/debug/leak_annotations.h"
#endif
@@ -109,6 +110,21 @@ uint32_t g_dumps_suppressed = 0;
char* g_process_type = nullptr;
ExceptionHandler* g_microdump = nullptr;
int g_signal_code_pipe_fd = -1;
+char* g_java_exception_info = nullptr;
+
+void SetJavaExceptionInfo(const char* exception) {
+ if (g_java_exception_info) {
+ // The old exception should be cleared before setting a new one.
+ DCHECK(!exception);
+ free(g_java_exception_info);
+ }
+
+ if (exception) {
+ g_java_exception_info = strndup(exception, 5 * 4096);
+ } else {
+ g_java_exception_info = nullptr;
+ }
+}
class MicrodumpInfo {
public:
@@ -1745,9 +1761,8 @@ void HandleCrashDump(const BreakpadInfo& info) {
WriteAndroidPackage(writer, android_build_info);
writer.AddBoundary();
}
- if (android_build_info->java_exception_info() != nullptr) {
- writer.AddPairString(exception_info,
- android_build_info->java_exception_info());
+ if (g_java_exception_info != nullptr) {
+ writer.AddPairString(exception_info, g_java_exception_info);
writer.AddBoundary();
}
#endif
@@ -1942,6 +1957,8 @@ void InitCrashReporter(const std::string& process_type,
void InitCrashReporter(const std::string& process_type) {
#endif // defined(OS_ANDROID)
#if defined(OS_ANDROID)
+ base::android::SetJavaExceptionCallback(SetJavaExceptionInfo);
+
// This will guarantee that the BuildInfo has been initialized and subsequent
// calls will not require memory allocation.
base::android::BuildInfo::GetInstance();
@@ -2029,6 +2046,8 @@ void InitNonBrowserCrashReporterForAndroid(
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
+ base::android::SetJavaExceptionCallback(SetJavaExceptionInfo);
+
// Handler registration is LIFO. Install the microdump handler first, such
// that if conventional minidump crash reporting is enabled below, it takes
// precedence (i.e. its handler is run first) over the microdump handler.
diff --git a/chromium/components/crash/content/app/crash_reporter_client.cc b/chromium/components/crash/content/app/crash_reporter_client.cc
index 464fe2efc0d..f69c6c3ff26 100644
--- a/chromium/components/crash/content/app/crash_reporter_client.cc
+++ b/chromium/components/crash/content/app/crash_reporter_client.cc
@@ -171,6 +171,17 @@ bool CrashReporterClient::ShouldEnableBreakpadMicrodumps() {
}
#endif
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+void CrashReporterClient::GetSanitizationInformation(
+ const char* const** annotations_whitelist,
+ void** target_module,
+ bool* sanitize_stacks) {
+ *annotations_whitelist = nullptr;
+ *target_module = nullptr;
+ *sanitize_stacks = false;
+}
+#endif
+
bool CrashReporterClient::ShouldMonitorCrashHandlerExpensively() {
return false;
}
diff --git a/chromium/components/crash/content/app/crash_reporter_client.h b/chromium/components/crash/content/app/crash_reporter_client.h
index e6a09126bdb..485c2c8bf63 100644
--- a/chromium/components/crash/content/app/crash_reporter_client.h
+++ b/chromium/components/crash/content/app/crash_reporter_client.h
@@ -165,6 +165,22 @@ class CrashReporterClient {
virtual bool ShouldEnableBreakpadMicrodumps();
#endif
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+ // Configures sanitization of crash dumps.
+ // |annotations_whitelist| is a nullptr terminated array of NUL-terminated
+ // strings of allowed annotation names or nullptr if all annotations are
+ // allowed. |target_module| is a pointer to a location inside a module to
+ // target or nullptr if there is no target module. Crash dumps are not
+ // produced when the crashing thread's stack and program counter do not
+ // reference the target module. |sanitize_stacks| is true if stacks should be
+ // sanitized for possible PII. If they are sanitized, only small integers and
+ // pointers to modules and stacks will be preserved.
+ virtual void GetSanitizationInformation(
+ const char* const** annotations_whitelist,
+ void** target_module,
+ bool* sanitize_stacks);
+#endif
+
// This method should return true to configure a crash reporter capable of
// monitoring itself for its own crashes to do so, even if self-monitoring
// would be expensive. "Expensive" self-monitoring dedicates an additional
diff --git a/chromium/components/crash/content/app/crashpad_linux.cc b/chromium/components/crash/content/app/crashpad_linux.cc
index d494877ca88..c8f87a85607 100644
--- a/chromium/components/crash/content/app/crashpad_linux.cc
+++ b/chromium/components/crash/content/app/crashpad_linux.cc
@@ -16,22 +16,43 @@
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/macros.h"
+#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/global_descriptors.h"
+#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "components/crash/content/app/crash_reporter_client.h"
#include "content/public/common/content_descriptors.h"
#include "sandbox/linux/services/syscall_wrappers.h"
+#include "third_party/crashpad/crashpad/client/annotation.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
+#include "third_party/crashpad/crashpad/snapshot/sanitized/sanitization_information.h"
#include "third_party/crashpad/crashpad/util/linux/exception_handler_client.h"
#include "third_party/crashpad/crashpad/util/linux/exception_information.h"
#include "third_party/crashpad/crashpad/util/misc/from_pointer_cast.h"
#include "third_party/crashpad/crashpad/util/posix/signals.h"
+#if defined(OS_ANDROID)
+#include "base/android/build_info.h"
+#include "base/android/java_exception_reporter.h"
+#endif // OS_ANDROID
+
namespace crashpad {
namespace {
+bool SetSanitizationInfo(SanitizationInformation* info) {
+ const char* const* whitelist = nullptr;
+ void* target_module = nullptr;
+ bool sanitize_stacks = false;
+ crash_reporter::GetCrashReporterClient()->GetSanitizationInformation(
+ &whitelist, &target_module, &sanitize_stacks);
+ info->annotations_whitelist_address = FromPointerCast<VMAddress>(whitelist);
+ info->target_module_address = FromPointerCast<VMAddress>(target_module);
+ info->sanitize_stacks = sanitize_stacks;
+ return whitelist != nullptr || target_module != nullptr || sanitize_stacks;
+}
+
// A signal handler for non-browser processes in the sandbox.
// Sends a message to a crashpad::CrashHandlerHost to handle the crash.
class SandboxedHandler {
@@ -42,6 +63,7 @@ class SandboxedHandler {
}
bool Initialize() {
+ SetSanitizationInfo(&sanitization_);
server_fd_ = base::GlobalDescriptors::GetInstance()->Get(
service_manager::kCrashDumpSignal);
@@ -107,6 +129,10 @@ class SandboxedHandler {
FromPointerCast<decltype(info.exception_information_address)>(
&exception_information);
+ info.sanitization_information_address =
+ FromPointerCast<decltype(info.sanitization_information_address)>(
+ &state->sanitization_);
+
ExceptionHandlerClient handler_client(connection.get());
handler_client.SetCanSetPtracer(false);
handler_client.RequestCrashDump(info);
@@ -115,6 +141,7 @@ class SandboxedHandler {
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
}
+ SanitizationInformation sanitization_;
int server_fd_;
DISALLOW_COPY_AND_ASSIGN(SandboxedHandler);
@@ -123,6 +150,45 @@ class SandboxedHandler {
} // namespace
} // namespace crashpad
+#if defined(OS_ANDROID)
+
+namespace {
+
+void SetJavaExceptionInfo(const char* info_string) {
+ static crashpad::StringAnnotation<5 * 4096> exception_info("exception_info");
+ if (info_string) {
+ exception_info.Set(info_string);
+ } else {
+ exception_info.Clear();
+ }
+}
+
+void SetBuildInfoAnnotations(std::map<std::string, std::string>* annotations) {
+ base::android::BuildInfo* info = base::android::BuildInfo::GetInstance();
+
+ (*annotations)["android_build_id"] = info->android_build_id();
+ (*annotations)["android_build_fp"] = info->android_build_fp();
+ (*annotations)["device"] = info->device();
+ (*annotations)["model"] = info->model();
+ (*annotations)["brand"] = info->brand();
+ (*annotations)["board"] = info->board();
+ (*annotations)["installer_package_name"] = info->installer_package_name();
+ (*annotations)["abi_name"] = info->abi_name();
+ (*annotations)["custom_themes"] = info->custom_themes();
+ (*annotations)["resources_verison"] = info->resources_version();
+ (*annotations)["gms_core_version"] = info->gms_version_code();
+
+ if (info->firebase_app_id()[0] != '\0') {
+ (*annotations)["package"] = std::string(info->firebase_app_id()) + " v" +
+ info->package_version_code() + " (" +
+ info->package_version_name() + ")";
+ }
+}
+
+} // namespace
+
+#endif // OS_ANDROID
+
namespace crash_reporter {
namespace internal {
@@ -196,6 +262,10 @@ bool BuildHandlerArgs(base::FilePath* handler_path,
(*process_annotations)["prod"] = product_name;
(*process_annotations)["ver"] = product_version;
+#if defined(OS_ANDROID)
+ SetBuildInfoAnnotations(process_annotations);
+#endif // OS_ANDROID
+
#if defined(GOOGLE_CHROME_BUILD)
// Empty means stable.
const bool allow_empty_channel = true;
@@ -234,6 +304,10 @@ base::FilePath PlatformCrashpadInitialization(bool initial_client,
DCHECK(!embedded_handler);
DCHECK(exe_path.empty());
+#if defined(OS_ANDROID)
+ base::android::SetJavaExceptionCallback(SetJavaExceptionInfo);
+#endif // OS_ANDROID
+
if (browser_process) {
base::FilePath handler_path;
base::FilePath database_path;
@@ -246,6 +320,13 @@ base::FilePath PlatformCrashpadInitialization(bool initial_client,
return base::FilePath();
}
+ static base::NoDestructor<crashpad::SanitizationInformation>
+ sanitization_info;
+ if (crashpad::SetSanitizationInfo(sanitization_info.get())) {
+ arguments.push_back(base::StringPrintf("--sanitization-information=%p",
+ sanitization_info.get()));
+ }
+
bool result = GetCrashpadClient().StartHandlerAtCrash(
handler_path, database_path, metrics_path, url, process_annotations,
arguments);
diff --git a/chromium/components/crash/content/browser/BUILD.gn b/chromium/components/crash/content/browser/BUILD.gn
index 64761ead918..68c98d4cf66 100644
--- a/chromium/components/crash/content/browser/BUILD.gn
+++ b/chromium/components/crash/content/browser/BUILD.gn
@@ -11,12 +11,14 @@ assert(!is_fuchsia)
source_set("browser") {
sources = [
+ "child_exit_observer_android.cc",
+ "child_exit_observer_android.h",
"child_process_crash_observer_android.cc",
"child_process_crash_observer_android.h",
"crash_dump_manager_android.cc",
"crash_dump_manager_android.h",
- "crash_dump_observer_android.cc",
- "crash_dump_observer_android.h",
+ "crash_metrics_reporter_android.cc",
+ "crash_metrics_reporter_android.h",
]
deps = [
@@ -57,6 +59,7 @@ source_set("unit_tests") {
testonly = true
sources = [
"crash_dump_manager_android_unittest.cc",
+ "crash_metrics_reporter_android_unittest.cc",
]
deps = [
":browser",
diff --git a/chromium/components/crash/content/browser/crash_dump_observer_android.cc b/chromium/components/crash/content/browser/child_exit_observer_android.cc
index 7e2883d9c39..aa028dfe36a 100644
--- a/chromium/components/crash/content/browser/crash_dump_observer_android.cc
+++ b/chromium/components/crash/content/browser/child_exit_observer_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/crash/content/browser/crash_dump_observer_android.h"
+#include "components/crash/content/browser/child_exit_observer_android.h"
#include <unistd.h>
@@ -18,48 +18,54 @@
using content::BrowserThread;
-namespace breakpad {
+namespace crash_reporter {
namespace {
-base::LazyInstance<CrashDumpObserver>::DestructorAtExit g_instance =
+base::LazyInstance<ChildExitObserver>::DestructorAtExit g_instance =
LAZY_INSTANCE_INITIALIZER;
void PopulateTerminationInfo(
const content::ChildProcessTerminationInfo& content_info,
- CrashDumpObserver::TerminationInfo* info) {
- info->has_oom_protection_bindings = content_info.has_oom_protection_bindings;
+ ChildExitObserver::TerminationInfo* info) {
+ info->binding_state = content_info.binding_state;
info->was_killed_intentionally_by_browser =
content_info.was_killed_intentionally_by_browser;
+ info->remaining_process_with_strong_binding =
+ content_info.remaining_process_with_strong_binding;
+ info->remaining_process_with_moderate_binding =
+ content_info.remaining_process_with_moderate_binding;
+ info->remaining_process_with_waived_binding =
+ content_info.remaining_process_with_waived_binding;
info->was_oom_protected_status =
content_info.status == base::TERMINATION_STATUS_OOM_PROTECTED;
}
} // namespace
-CrashDumpObserver::TerminationInfo::TerminationInfo() = default;
-CrashDumpObserver::TerminationInfo::TerminationInfo(
+ChildExitObserver::TerminationInfo::TerminationInfo() = default;
+ChildExitObserver::TerminationInfo::TerminationInfo(
const TerminationInfo& other) = default;
-CrashDumpObserver::TerminationInfo& CrashDumpObserver::TerminationInfo::
+ChildExitObserver::TerminationInfo& ChildExitObserver::TerminationInfo::
operator=(const TerminationInfo& other) = default;
// static
-void CrashDumpObserver::Create() {
+void ChildExitObserver::Create() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// If this DCHECK fails in a unit test then a previously executing
- // test that makes use of CrashDumpObserver forgot to create a
+ // test that makes use of ChildExitObserver forgot to create a
// ShadowingAtExitManager.
DCHECK(!g_instance.IsCreated());
g_instance.Get();
}
// static
-CrashDumpObserver* CrashDumpObserver::GetInstance() {
+ChildExitObserver* ChildExitObserver::GetInstance() {
DCHECK(g_instance.IsCreated());
return g_instance.Pointer();
}
-CrashDumpObserver::CrashDumpObserver() {
+ChildExitObserver::ChildExitObserver() {
notification_registrar_.Add(this,
content::NOTIFICATION_RENDERER_PROCESS_CREATED,
content::NotificationService::AllSources());
@@ -72,17 +78,17 @@ CrashDumpObserver::CrashDumpObserver() {
BrowserChildProcessObserver::Add(this);
}
-CrashDumpObserver::~CrashDumpObserver() {
+ChildExitObserver::~ChildExitObserver() {
BrowserChildProcessObserver::Remove(this);
}
-void CrashDumpObserver::RegisterClient(std::unique_ptr<Client> client) {
+void ChildExitObserver::RegisterClient(std::unique_ptr<Client> client) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::AutoLock auto_lock(registered_clients_lock_);
registered_clients_.push_back(std::move(client));
}
-void CrashDumpObserver::OnChildExit(const TerminationInfo& info) {
+void ChildExitObserver::OnChildExit(const TerminationInfo& info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::vector<Client*> registered_clients_copy;
{
@@ -95,7 +101,7 @@ void CrashDumpObserver::OnChildExit(const TerminationInfo& info) {
}
}
-void CrashDumpObserver::BrowserChildProcessStarted(
+void ChildExitObserver::BrowserChildProcessStarted(
int process_host_id,
content::PosixFileDescriptorInfo* mappings) {
std::vector<Client*> registered_clients_copy;
@@ -109,7 +115,7 @@ void CrashDumpObserver::BrowserChildProcessStarted(
}
}
-void CrashDumpObserver::BrowserChildProcessHostDisconnected(
+void ChildExitObserver::BrowserChildProcessHostDisconnected(
const content::ChildProcessData& data) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TerminationInfo info;
@@ -127,7 +133,7 @@ void CrashDumpObserver::BrowserChildProcessHostDisconnected(
OnChildExit(info);
}
-void CrashDumpObserver::BrowserChildProcessKilled(
+void ChildExitObserver::BrowserChildProcessKilled(
const content::ChildProcessData& data,
const content::ChildProcessTerminationInfo& content_info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -142,7 +148,7 @@ void CrashDumpObserver::BrowserChildProcessKilled(
// Subsequent BrowserChildProcessHostDisconnected will call OnChildExit.
}
-void CrashDumpObserver::Observe(int type,
+void ChildExitObserver::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -193,4 +199,4 @@ void CrashDumpObserver::Observe(int type,
OnChildExit(info);
}
-} // namespace breakpad
+} // namespace crash_reporter
diff --git a/chromium/components/crash/content/browser/crash_dump_observer_android.h b/chromium/components/crash/content/browser/child_exit_observer_android.h
index 2564e1ecbae..5e9f239c309 100644
--- a/chromium/components/crash/content/browser/crash_dump_observer_android.h
+++ b/chromium/components/crash/content/browser/child_exit_observer_android.h
@@ -2,13 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_OBSERVER_ANDROID_H_
-#define COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_OBSERVER_ANDROID_H_
+#ifndef COMPONENTS_CRASH_CONTENT_BROWSER_CHILD_EXIT_OBSERVER_ANDROID_H_
+#define COMPONENTS_CRASH_CONTENT_BROWSER_CHILD_EXIT_OBSERVER_ANDROID_H_
#include <memory>
#include <vector>
#include "base/android/application_status_listener.h"
+#include "base/android/child_process_binding_types.h"
#include "base/lazy_instance.h"
#include "base/memory/ref_counted.h"
#include "base/process/process.h"
@@ -23,12 +24,12 @@ namespace content {
struct ChildProcessTerminationInfo;
}
-namespace breakpad {
+namespace crash_reporter {
// This class centralises the observation of child processes for the
// purpose of reacting to child process crashes.
-// The CrashDumpObserver instance exists on the browser main thread.
-class CrashDumpObserver : public content::BrowserChildProcessObserver,
+// The ChildExitObserver instance exists on the browser main thread.
+class ChildExitObserver : public content::BrowserChildProcessObserver,
public content::NotificationObserver {
public:
struct TerminationInfo {
@@ -49,8 +50,12 @@ class CrashDumpObserver : public content::BrowserChildProcessObserver,
// Values from ChildProcessTerminationInfo.
// Note base::TerminationStatus and exit_code are missing intentionally
// because those fields hold no useful information on Android.
- bool has_oom_protection_bindings = false;
+ base::android::ChildBindingState binding_state =
+ base::android::ChildBindingState::UNBOUND;
bool was_killed_intentionally_by_browser = false;
+ int remaining_process_with_strong_binding = 0;
+ int remaining_process_with_moderate_binding = 0;
+ int remaining_process_with_waived_binding = 0;
// Note this is slightly different |has_oom_protection_bindings|.
// This is equivalent to status == TERMINATION_STATUS_NORMAL_TERMINATION,
@@ -70,7 +75,7 @@ class CrashDumpObserver : public content::BrowserChildProcessObserver,
bool renderer_was_subframe = false;
};
- // CrashDumpObserver client interface.
+ // ChildExitObserver client interface.
// Client methods will be called synchronously in the order in which
// clients were registered. It is the implementer's responsibility
// to post tasks to the appropriate threads if required (and be
@@ -93,31 +98,31 @@ class CrashDumpObserver : public content::BrowserChildProcessObserver,
virtual ~Client() {}
};
- // The global CrashDumpObserver instance is created by calling
+ // The global ChildExitObserver instance is created by calling
// Create (on the UI thread), and lives until process exit. Tests
// making use of this class should register an AtExitManager.
static void Create();
- // Fetch a pointer to the global CrashDumpObserver instance. The
+ // Fetch a pointer to the global ChildExitObserver instance. The
// global instance must have been created by the time GetInstance is
// called.
- static CrashDumpObserver* GetInstance();
+ static ChildExitObserver* GetInstance();
void RegisterClient(std::unique_ptr<Client> client);
// BrowserChildProcessStarted must be called from
// ContentBrowserClient::GetAdditionalMappedFilesForChildProcess
- // overrides, to notify the CrashDumpObserver of child process
+ // overrides, to notify the ChildExitObserver of child process
// creation, and to allow clients to register any fd mappings they
// need.
void BrowserChildProcessStarted(int process_host_id,
content::PosixFileDescriptorInfo* mappings);
private:
- friend struct base::LazyInstanceTraitsBase<CrashDumpObserver>;
+ friend struct base::LazyInstanceTraitsBase<ChildExitObserver>;
- CrashDumpObserver();
- ~CrashDumpObserver() override;
+ ChildExitObserver();
+ ~ChildExitObserver() override;
// content::BrowserChildProcessObserver implementation:
void BrowserChildProcessHostDisconnected(
@@ -145,9 +150,9 @@ class CrashDumpObserver : public content::BrowserChildProcessObserver,
// Key is process_host_id. Only used for BrowserChildProcessHost.
std::map<int, TerminationInfo> browser_child_process_info_;
- DISALLOW_COPY_AND_ASSIGN(CrashDumpObserver);
+ DISALLOW_COPY_AND_ASSIGN(ChildExitObserver);
};
-} // namespace breakpad
+} // namespace crash_reporter
-#endif // COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_OBSERVER_ANDROID_H_
+#endif // COMPONENTS_CRASH_CONTENT_BROWSER_CHILD_EXIT_OBSERVER_ANDROID_H_
diff --git a/chromium/components/crash/content/browser/child_process_crash_observer_android.cc b/chromium/components/crash/content/browser/child_process_crash_observer_android.cc
index 83d10f0c0d2..28aada7b761 100644
--- a/chromium/components/crash/content/browser/child_process_crash_observer_android.cc
+++ b/chromium/components/crash/content/browser/child_process_crash_observer_android.cc
@@ -12,7 +12,7 @@
#include "components/crash/content/app/breakpad_linux.h"
#include "components/crash/content/browser/crash_dump_manager_android.h"
-namespace breakpad {
+namespace crash_reporter {
ChildProcessCrashObserver::ChildProcessCrashObserver(
const base::FilePath crash_dump_dir,
@@ -28,23 +28,23 @@ void ChildProcessCrashObserver::OnChildStart(
return;
base::ScopedFD file(
- CrashDumpManager::GetInstance()->CreateMinidumpFileForChild(
+ breakpad::CrashDumpManager::GetInstance()->CreateMinidumpFileForChild(
process_host_id));
if (file.is_valid())
mappings->Transfer(descriptor_id_, std::move(file));
}
void ChildProcessCrashObserver::OnChildExit(
- const CrashDumpObserver::TerminationInfo& info) {
+ const ChildExitObserver::TerminationInfo& info) {
// This might be called twice for a given child process, with a
// NOTIFICATION_RENDERER_PROCESS_TERMINATED and then with
// NOTIFICATION_RENDERER_PROCESS_CLOSED.
base::PostTaskWithTraits(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
- base::Bind(&CrashDumpManager::ProcessMinidumpFileFromChild,
- base::Unretained(CrashDumpManager::GetInstance()),
+ base::Bind(&breakpad::CrashDumpManager::ProcessMinidumpFileFromChild,
+ base::Unretained(breakpad::CrashDumpManager::GetInstance()),
crash_dump_dir_, info));
}
-} // namespace breakpad
+} // namespace crash_reporter
diff --git a/chromium/components/crash/content/browser/child_process_crash_observer_android.h b/chromium/components/crash/content/browser/child_process_crash_observer_android.h
index 07a119cee8e..7710e6f9d9b 100644
--- a/chromium/components/crash/content/browser/child_process_crash_observer_android.h
+++ b/chromium/components/crash/content/browser/child_process_crash_observer_android.h
@@ -6,20 +6,21 @@
#define COMPONENTS_CRASH_CONTENT_BROWSER_CHILD_PROCESS_CRASH_OBSERVER_ANDROID_H_
#include "base/files/file_path.h"
-#include "components/crash/content/browser/crash_dump_observer_android.h"
+#include "components/crash/content/browser/child_exit_observer_android.h"
-namespace breakpad {
+namespace crash_reporter {
-class ChildProcessCrashObserver : public breakpad::CrashDumpObserver::Client {
+class ChildProcessCrashObserver
+ : public crash_reporter::ChildExitObserver::Client {
public:
ChildProcessCrashObserver(const base::FilePath crash_dump_dir,
int descriptor_id);
~ChildProcessCrashObserver() override;
- // breakpad::CrashDumpObserver::Client implementation:
+ // crash_reporter::ChildExitObserver::Client implementation:
void OnChildStart(int process_host_id,
content::PosixFileDescriptorInfo* mappings) override;
- void OnChildExit(const CrashDumpObserver::TerminationInfo& info) override;
+ void OnChildExit(const ChildExitObserver::TerminationInfo& info) override;
private:
base::FilePath crash_dump_dir_;
@@ -30,6 +31,6 @@ class ChildProcessCrashObserver : public breakpad::CrashDumpObserver::Client {
DISALLOW_COPY_AND_ASSIGN(ChildProcessCrashObserver);
};
-} // namespace breakpad
+} // namespace crash_reporter
#endif // COMPONENTS_CRASH_CONTENT_BROWSER_CHILD_PROCESS_CRASH_OBSERVER_ANDROID_H_
diff --git a/chromium/components/crash/content/browser/crash_dump_manager_android.cc b/chromium/components/crash/content/browser/crash_dump_manager_android.cc
index 1cc00de0bb2..3e743de1503 100644
--- a/chromium/components/crash/content/browser/crash_dump_manager_android.cc
+++ b/chromium/components/crash/content/browser/crash_dump_manager_android.cc
@@ -22,133 +22,42 @@
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "components/crash/content/app/breakpad_linux.h"
+#include "components/crash/content/browser/crash_metrics_reporter_android.h"
#include "jni/CrashDumpManager_jni.h"
namespace breakpad {
namespace {
+
base::LazyInstance<CrashDumpManager>::Leaky g_instance =
LAZY_INSTANCE_INITIALIZER;
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class ProcessedCrashCounts {
- kGpuForegroundOom = 0,
- kRendererForegroundVisibleOom = 1,
- kRendererForegroundIntentionalKill = 2,
- kRendererForegroundVisibleSubframeOom = 3,
- kRendererForegroundVisibleSubframeIntentionalKill = 4,
- kRendererForegroundVisibleCrash = 5,
- kRendererForegroundVisibleSubframeCrash = 6,
- kGpuCrashAll = 7,
- kRendererCrashAll = 8,
- kMaxValue = kRendererCrashAll
-};
-
-void LogCount(ProcessedCrashCounts type) {
- UMA_HISTOGRAM_ENUMERATION("Stability.Android.ProcessedCrashCounts", type);
-}
-
-void LogProcessedMetrics(const CrashDumpObserver::TerminationInfo& info,
- bool has_crash_dump) {
- const bool app_foreground =
- info.app_state ==
- base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES ||
- info.app_state == base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES;
- const bool intentional_kill = info.was_killed_intentionally_by_browser;
- const bool android_oom_kill = !info.was_killed_intentionally_by_browser &&
- !has_crash_dump && !info.normal_termination;
- const bool renderer_visible = info.renderer_has_visible_clients;
- const bool renderer_sub_frame = info.renderer_was_subframe;
-
- if (info.process_type == content::PROCESS_TYPE_GPU) {
- if (app_foreground && android_oom_kill) {
- LogCount(ProcessedCrashCounts::kGpuForegroundOom);
- }
- if (has_crash_dump) {
- LogCount(ProcessedCrashCounts::kGpuCrashAll);
- }
- }
-
- if (info.process_type == content::PROCESS_TYPE_RENDERER) {
- if (app_foreground && renderer_visible) {
- if (android_oom_kill) {
- LogCount(
- renderer_sub_frame
- ? ProcessedCrashCounts::kRendererForegroundVisibleSubframeOom
- : ProcessedCrashCounts::kRendererForegroundVisibleOom);
- }
- if (has_crash_dump) {
- LogCount(
- renderer_sub_frame
- ? ProcessedCrashCounts::kRendererForegroundVisibleSubframeCrash
- : ProcessedCrashCounts::kRendererForegroundVisibleCrash);
- }
- }
- if (app_foreground && intentional_kill) {
- LogCount(ProcessedCrashCounts::kRendererForegroundIntentionalKill);
- if (renderer_visible && renderer_sub_frame) {
- LogCount(ProcessedCrashCounts::
- kRendererForegroundVisibleSubframeIntentionalKill);
- }
- }
- if (has_crash_dump) {
- LogCount(ProcessedCrashCounts::kRendererCrashAll);
- }
+class DefaultUploader : public CrashDumpManager::Uploader {
+ public:
+ DefaultUploader() = default;
+ ~DefaultUploader() override = default;
+
+ // CrashDumpManager::Uploader:
+ void TryToUploadCrashDump(const base::FilePath& crash_dump_path) override {
+ // Hop over to Java to attempt to attach the logcat to the crash. This may
+ // fail, which is ok -- if it does, the crash will still be uploaded on the
+ // next browser start.
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::ScopedJavaLocalRef<jstring> j_dump_path =
+ base::android::ConvertUTF8ToJavaString(env, crash_dump_path.value());
+ Java_CrashDumpManager_tryToUploadMinidump(env, j_dump_path);
}
-}
+};
} // namespace
-CrashDumpManager::CrashDumpDetails::CrashDumpDetails(
- int process_host_id,
- content::ProcessType process_type,
- bool was_oom_protected_status,
- base::android::ApplicationState app_state)
- : process_host_id(process_host_id),
- process_type(process_type),
- was_oom_protected_status(was_oom_protected_status),
- app_state(app_state) {}
-
-CrashDumpManager::CrashDumpDetails::CrashDumpDetails() {}
-CrashDumpManager::CrashDumpDetails::~CrashDumpDetails() {}
-
-CrashDumpManager::CrashDumpDetails::CrashDumpDetails(
- const CrashDumpManager::CrashDumpDetails& other)
- : CrashDumpDetails(other.process_host_id,
- other.process_type,
- other.was_oom_protected_status,
- other.app_state) {
- file_size = other.file_size;
- status = other.status;
-}
-
// static
CrashDumpManager* CrashDumpManager::GetInstance() {
return g_instance.Pointer();
}
-// static
-bool CrashDumpManager::IsForegroundOom(const CrashDumpDetails& details) {
- // If the crash is size 0, it is an OOM.
- return details.process_type == content::PROCESS_TYPE_RENDERER &&
- details.was_oom_protected_status && details.file_size == 0 &&
- details.app_state ==
- base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
-}
-
-void CrashDumpManager::AddObserver(Observer* observer) {
- async_observers_->AddObserver(observer);
-}
-
-void CrashDumpManager::RemoveObserver(Observer* observer) {
- async_observers_->RemoveObserver(observer);
-}
-
CrashDumpManager::CrashDumpManager()
- : async_observers_(
- base::MakeRefCounted<
- base::ObserverListThreadSafe<CrashDumpManager::Observer>>()) {}
+ : uploader_(std::make_unique<DefaultUploader>()) {}
CrashDumpManager::~CrashDumpManager() {}
@@ -177,88 +86,48 @@ base::ScopedFD CrashDumpManager::CreateMinidumpFileForChild(
void CrashDumpManager::ProcessMinidumpFileFromChild(
base::FilePath crash_dump_dir,
- const CrashDumpObserver::TerminationInfo& info) {
+ const crash_reporter::ChildExitObserver::TerminationInfo& info) {
+ CrashDumpManager::CrashDumpStatus status =
+ ProcessMinidumpFileFromChildInternal(crash_dump_dir, info);
+ crash_reporter::CrashMetricsReporter::GetInstance()->CrashDumpProcessed(
+ info, status);
+}
+
+CrashDumpManager::CrashDumpStatus
+CrashDumpManager::ProcessMinidumpFileFromChildInternal(
+ base::FilePath crash_dump_dir,
+ const crash_reporter::ChildExitObserver::TerminationInfo& info) {
base::AssertBlockingAllowed();
- CrashDumpDetails details(info.process_host_id, info.process_type,
- info.was_oom_protected_status, info.app_state);
base::FilePath minidump_path;
// If the minidump for a given child process has already been
// processed, then there is no more work to do.
if (!GetMinidumpPath(info.process_host_id, &minidump_path)) {
- NotifyObservers(details);
- return;
+ return CrashDumpStatus::kMissingDump;
}
int64_t file_size = 0;
if (!base::PathExists(minidump_path)) {
LOG(ERROR) << "minidump does not exist " << minidump_path.value();
- return;
+ return CrashDumpStatus::kMissingDump;
}
int r = base::GetFileSize(minidump_path, &file_size);
DCHECK(r) << "Failed to retrieve size for minidump " << minidump_path.value();
- // TODO(wnwen): If these numbers match up to TabWebContentsObserver's
- // TabRendererCrashStatus histogram, then remove that one as this is more
- // accurate with more detail.
- if ((info.process_type == content::PROCESS_TYPE_RENDERER ||
- info.process_type == content::PROCESS_TYPE_GPU) &&
- info.app_state != base::android::APPLICATION_STATE_UNKNOWN) {
- ExitStatus exit_status;
- bool is_running = (info.app_state ==
- base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES);
- bool is_paused = (info.app_state ==
- base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES);
- if (file_size == 0) {
- if (is_running) {
- exit_status = EMPTY_MINIDUMP_WHILE_RUNNING;
- } else if (is_paused) {
- exit_status = EMPTY_MINIDUMP_WHILE_PAUSED;
- } else {
- exit_status = EMPTY_MINIDUMP_WHILE_BACKGROUND;
- }
- } else {
- if (is_running) {
- exit_status = VALID_MINIDUMP_WHILE_RUNNING;
- } else if (is_paused) {
- exit_status = VALID_MINIDUMP_WHILE_PAUSED;
- } else {
- exit_status = VALID_MINIDUMP_WHILE_BACKGROUND;
- }
- }
- if (info.process_type == content::PROCESS_TYPE_RENDERER) {
- if (info.was_oom_protected_status) {
- UMA_HISTOGRAM_ENUMERATION("Tab.RendererDetailedExitStatus", exit_status,
- ExitStatus::MINIDUMP_STATUS_COUNT);
- } else {
- UMA_HISTOGRAM_ENUMERATION("Tab.RendererDetailedExitStatusUnbound",
- exit_status,
- ExitStatus::MINIDUMP_STATUS_COUNT);
- }
- } else if (info.process_type == content::PROCESS_TYPE_GPU) {
- UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessDetailedExitStatus", exit_status,
- ExitStatus::MINIDUMP_STATUS_COUNT);
- }
- }
-
- LogProcessedMetrics(info, file_size != 0);
-
if (file_size == 0) {
// Empty minidump, this process did not crash. Just remove the file.
r = base::DeleteFile(minidump_path, false);
DCHECK(r) << "Failed to delete temporary minidump file "
<< minidump_path.value();
- details.status = CrashDumpStatus::kEmptyDump;
- NotifyObservers(details);
- return;
+ return CrashDumpStatus::kEmptyDump;
}
// We are dealing with a valid minidump. Copy it to the crash report
// directory from where Java code will upload it later on.
if (crash_dump_dir.empty()) {
NOTREACHED() << "Failed to retrieve the crash dump directory.";
- return;
+ return CrashDumpStatus::kDumpProcessingFailed;
}
const uint64_t rand = base::RandUint64();
const std::string filename =
@@ -270,24 +139,12 @@ void CrashDumpManager::ProcessMinidumpFileFromChild(
LOG(ERROR) << "Failed to move crash dump from " << minidump_path.value()
<< " to " << dest_path.value();
base::DeleteFile(minidump_path, false);
- return;
+ return CrashDumpStatus::kDumpProcessingFailed;
}
VLOG(1) << "Crash minidump successfully generated: " << dest_path.value();
- // Hop over to Java to attempt to attach the logcat to the crash. This may
- // fail, which is ok -- if it does, the crash will still be uploaded on the
- // next browser start.
- JNIEnv* env = base::android::AttachCurrentThread();
- base::android::ScopedJavaLocalRef<jstring> j_dest_path =
- base::android::ConvertUTF8ToJavaString(env, dest_path.value());
- Java_CrashDumpManager_tryToUploadMinidump(env, j_dest_path);
- details.status = CrashDumpStatus::kValidDump;
- NotifyObservers(details);
-}
-
-void CrashDumpManager::NotifyObservers(const CrashDumpDetails& details) {
- async_observers_->Notify(
- FROM_HERE, &CrashDumpManager::Observer::OnCrashDumpProcessed, details);
+ uploader_->TryToUploadCrashDump(dest_path);
+ return CrashDumpStatus::kValidDump;
}
void CrashDumpManager::SetMinidumpPath(int process_host_id,
diff --git a/chromium/components/crash/content/browser/crash_dump_manager_android.h b/chromium/components/crash/content/browser/crash_dump_manager_android.h
index 96f14e1b2c9..bc10af0c95e 100644
--- a/chromium/components/crash/content/browser/crash_dump_manager_android.h
+++ b/chromium/components/crash/content/browser/crash_dump_manager_android.h
@@ -6,15 +6,16 @@
#define COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_DUMP_MANAGER_ANDROID_H_
#include <map>
+#include <memory>
+#include <utility>
#include "base/android/application_status_listener.h"
#include "base/files/file_path.h"
#include "base/files/platform_file.h"
#include "base/lazy_instance.h"
#include "base/memory/ref_counted.h"
-#include "base/observer_list_threadsafe.h"
#include "base/synchronization/lock.h"
-#include "components/crash/content/browser/crash_dump_observer_android.h"
+#include "components/crash/content/browser/child_exit_observer_android.h"
#include "content/public/common/child_process_host.h"
#include "content/public/common/process_type.h"
@@ -32,78 +33,55 @@ namespace breakpad {
// terminates.
class CrashDumpManager {
public:
- // This enum is used to back a UMA histogram, and must be treated as
- // append-only.
- enum ExitStatus {
- EMPTY_MINIDUMP_WHILE_RUNNING,
- EMPTY_MINIDUMP_WHILE_PAUSED,
- EMPTY_MINIDUMP_WHILE_BACKGROUND,
- VALID_MINIDUMP_WHILE_RUNNING,
- VALID_MINIDUMP_WHILE_PAUSED,
- VALID_MINIDUMP_WHILE_BACKGROUND,
- MINIDUMP_STATUS_COUNT
- };
-
enum class CrashDumpStatus {
// The dump for this process did not have a path set. This can happen if the
// dump was already processed or if crash dump generation is not turned on.
- kNoDump,
+ kMissingDump,
// The crash dump was empty.
kEmptyDump,
+ // Minidump file was found, but could not be copied to crash dir.
+ kDumpProcessingFailed,
+
// The crash dump is valid.
kValidDump,
};
- struct CrashDumpDetails {
- CrashDumpDetails(int process_host_id,
- content::ProcessType process_type,
- bool was_oom_protected_status,
- base::android::ApplicationState app_state);
- CrashDumpDetails();
- ~CrashDumpDetails();
- CrashDumpDetails(const CrashDumpDetails& other);
-
- int process_host_id = content::ChildProcessHost::kInvalidUniqueID;
-
- content::ProcessType process_type = content::PROCESS_TYPE_UNKNOWN;
- bool was_oom_protected_status = false;
- base::android::ApplicationState app_state;
- int64_t file_size = 0;
- CrashDumpStatus status = CrashDumpStatus::kNoDump;
- };
- // Careful note: the CrashDumpManager observers are asynchronous, and are
- // notified via PostTask. This could be problematic with a large number of
- // observers. Consider using a middle-layer observer to fan out synchronously
- // to leaf observers if you need many objects listening to these messages.
- class Observer {
+ // Class which aids in uploading a crash dump.
+ class Uploader {
public:
- virtual void OnCrashDumpProcessed(const CrashDumpDetails& details) {}
+ virtual ~Uploader() = default;
+
+ // Attempts to upload the specified child process minidump.
+ virtual void TryToUploadCrashDump(
+ const base::FilePath& crash_dump_path) = 0;
};
static CrashDumpManager* GetInstance();
- // True when |details| is a foreground out of memory crash.
- static bool IsForegroundOom(const CrashDumpDetails& details);
-
- // Can be called on any thread.
- void AddObserver(Observer* observer);
- void RemoveObserver(Observer* observer);
-
void ProcessMinidumpFileFromChild(
base::FilePath crash_dump_dir,
- const CrashDumpObserver::TerminationInfo& info);
+ const crash_reporter::ChildExitObserver::TerminationInfo& info);
base::ScopedFD CreateMinidumpFileForChild(int process_host_id);
+ // Careful, |uploader_| is accessed on one of the task scheduler threads.
+ // Tests should set this before any other threads are spawned.
+ void set_uploader_for_testing(std::unique_ptr<Uploader> uploader) {
+ DCHECK(uploader);
+ uploader_ = std::move(uploader);
+ }
+
private:
friend struct base::LazyInstanceTraitsBase<CrashDumpManager>;
CrashDumpManager();
~CrashDumpManager();
- void NotifyObservers(const CrashDumpDetails& details);
+ CrashDumpStatus ProcessMinidumpFileFromChildInternal(
+ base::FilePath crash_dump_dir,
+ const crash_reporter::ChildExitObserver::TerminationInfo& info);
typedef std::map<int, base::FilePath> ChildProcessIDToMinidumpPath;
@@ -111,8 +89,8 @@ class CrashDumpManager {
const base::FilePath& minidump_path);
bool GetMinidumpPath(int process_host_id, base::FilePath* minidump_path);
- scoped_refptr<base::ObserverListThreadSafe<CrashDumpManager::Observer>>
- async_observers_;
+ // Should never be nullptr.
+ std::unique_ptr<Uploader> uploader_;
// This map should only be accessed with its lock aquired as it is accessed
// from the PROCESS_LAUNCHER and UI threads.
diff --git a/chromium/components/crash/content/browser/crash_dump_manager_android_unittest.cc b/chromium/components/crash/content/browser/crash_dump_manager_android_unittest.cc
index a3ad2e198dc..83fd396f189 100644
--- a/chromium/components/crash/content/browser/crash_dump_manager_android_unittest.cc
+++ b/chromium/components/crash/content/browser/crash_dump_manager_android_unittest.cc
@@ -4,6 +4,7 @@
#include "components/crash/content/browser/crash_dump_manager_android.h"
+#include <string>
#include <utility>
#include "base/at_exit.h"
@@ -11,19 +12,63 @@
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/files/file.h"
+#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/run_loop.h"
+#include "base/sequenced_task_runner.h"
#include "base/task_scheduler/post_task.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_restrictions.h"
+#include "components/crash/content/browser/crash_metrics_reporter_android.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace breakpad {
+class CrashDumpManagerTest;
+
+class CrashMetricsReporterObserver
+ : public crash_reporter::CrashMetricsReporter::Observer {
+ public:
+ CrashMetricsReporterObserver() {}
+ ~CrashMetricsReporterObserver() {}
+
+ // crash_reporter::CrashMetricsReporter::Observer:
+ void OnCrashDumpProcessed(
+ int rph_id,
+ const crash_reporter::CrashMetricsReporter::ReportedCrashTypeSet&
+ reported_counts) override {
+ wait_run_loop_.QuitClosure().Run();
+ }
+
+ void WaitForProcessed() { wait_run_loop_.Run(); }
+
+ private:
+ base::RunLoop wait_run_loop_;
+ DISALLOW_COPY_AND_ASSIGN(CrashMetricsReporterObserver);
+};
+
+class NoOpUploader : public CrashDumpManager::Uploader {
+ public:
+ NoOpUploader(scoped_refptr<base::SequencedTaskRunner> test_runner,
+ CrashDumpManagerTest* test_harness)
+ : test_runner_(std::move(test_runner)), test_harness_(test_harness) {}
+ ~NoOpUploader() override = default;
+
+ // CrashDumpManager::Uploader:
+ void TryToUploadCrashDump(const base::FilePath& crash_dump_path) override;
+
+ private:
+ scoped_refptr<base::SequencedTaskRunner> test_runner_;
+ CrashDumpManagerTest* test_harness_;
+ DISALLOW_COPY_AND_ASSIGN(NoOpUploader);
+};
+
class CrashDumpManagerTest : public testing::Test {
public:
CrashDumpManagerTest()
@@ -34,130 +79,110 @@ class CrashDumpManagerTest : public testing::Test {
void SetUp() override {
// Initialize the manager.
ASSERT_TRUE(CrashDumpManager::GetInstance());
+ CrashDumpManager::GetInstance()->set_uploader_for_testing(
+ std::make_unique<NoOpUploader>(base::SequencedTaskRunnerHandle::Get(),
+ this));
}
- // TODO(csharrison): This test harness is not robust enough. Namely, it does
- // not support actually processing a non-empty crash dump, due to JNI calls.
- static void CreateAndProcessEmptyMinidump(
- const CrashDumpObserver::TerminationInfo& info) {
+ void OnUploadedCrashDump() {
+ dumps_uploaded_++;
+ if (!upload_waiter_.is_null())
+ std::move(upload_waiter_).Run();
+ }
+
+ void WaitForCrashDumpsUploaded(int num_dumps) {
+ EXPECT_LE(num_dumps, dumps_uploaded_);
+ while (num_dumps < dumps_uploaded_) {
+ base::RunLoop run_loop;
+ upload_waiter_ = run_loop.QuitClosure();
+ run_loop.Run();
+ }
+ }
+
+ static void CreateAndProcessCrashDump(
+ const crash_reporter::ChildExitObserver::TerminationInfo& info,
+ const std::string& data) {
base::ScopedFD fd =
CrashDumpManager::GetInstance()->CreateMinidumpFileForChild(
info.process_host_id);
EXPECT_TRUE(fd.is_valid());
+ base::WriteFileDescriptor(fd.get(), data.data(), data.size());
base::ScopedTempDir dump_dir;
EXPECT_TRUE(dump_dir.CreateUniqueTempDir());
CrashDumpManager::GetInstance()->ProcessMinidumpFileFromChild(
dump_dir.GetPath(), info);
}
+ protected:
+ int dumps_uploaded_ = 0;
+
private:
base::ShadowingAtExitManager at_exit_;
base::test::ScopedTaskEnvironment scoped_environment_;
+ base::OnceClosure upload_waiter_;
DISALLOW_COPY_AND_ASSIGN(CrashDumpManagerTest);
};
-class CrashDumpManagerObserver : public CrashDumpManager::Observer {
- public:
- CrashDumpManagerObserver() {}
- ~CrashDumpManagerObserver() {}
-
- // CrashDumpManager::Observer:
- void OnCrashDumpProcessed(
- const CrashDumpManager::CrashDumpDetails& details) override {
- last_details_ = details;
- if (!wait_closure_.is_null())
- std::move(wait_closure_).Run();
- }
-
- const CrashDumpManager::CrashDumpDetails& last_details() const {
- return last_details_.value();
- }
-
- void WaitForProcessed() {
- base::RunLoop run_loop;
- wait_closure_ = run_loop.QuitClosure();
- run_loop.Run();
- }
-
- private:
- base::OnceClosure wait_closure_;
- base::Optional<CrashDumpManager::CrashDumpDetails> last_details_;
- DISALLOW_COPY_AND_ASSIGN(CrashDumpManagerObserver);
-};
+void NoOpUploader::TryToUploadCrashDump(const base::FilePath& crash_dump_path) {
+ test_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&CrashDumpManagerTest::OnUploadedCrashDump,
+ base::Unretained(test_harness_)));
+}
-TEST_F(CrashDumpManagerTest, SimpleOOM) {
+TEST_F(CrashDumpManagerTest, NoDumpCreated) {
base::HistogramTester histogram_tester;
CrashDumpManager* manager = CrashDumpManager::GetInstance();
- CrashDumpManagerObserver crash_dump_observer;
- manager->AddObserver(&crash_dump_observer);
+ CrashMetricsReporterObserver observer;
+ crash_reporter::CrashMetricsReporter::GetInstance()->AddObserver(&observer);
- CrashDumpObserver::TerminationInfo termination_info;
+ crash_reporter::ChildExitObserver::TerminationInfo termination_info;
termination_info.process_host_id = 1;
termination_info.pid = base::kNullProcessHandle;
termination_info.process_type = content::PROCESS_TYPE_RENDERER;
termination_info.app_state =
base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
termination_info.normal_termination = false;
- termination_info.has_oom_protection_bindings = true;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
termination_info.was_killed_intentionally_by_browser = false;
termination_info.was_oom_protected_status = true;
base::PostTaskWithTraits(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
- base::Bind(&CrashDumpManagerTest::CreateAndProcessEmptyMinidump,
- termination_info));
- crash_dump_observer.WaitForProcessed();
-
- const CrashDumpManager::CrashDumpDetails& details =
- crash_dump_observer.last_details();
- EXPECT_EQ(termination_info.process_host_id, details.process_host_id);
- EXPECT_EQ(content::PROCESS_TYPE_RENDERER, details.process_type);
- EXPECT_TRUE(details.was_oom_protected_status);
- EXPECT_EQ(base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES,
- details.app_state);
- EXPECT_EQ(0, details.file_size);
- EXPECT_EQ(CrashDumpManager::CrashDumpStatus::kEmptyDump, details.status);
+ base::BindOnce(&CrashDumpManager::ProcessMinidumpFileFromChild,
+ base::Unretained(manager), base::FilePath(),
+ termination_info));
+ observer.WaitForProcessed();
- histogram_tester.ExpectUniqueSample(
- "Tab.RendererDetailedExitStatus",
- CrashDumpManager::EMPTY_MINIDUMP_WHILE_RUNNING, 1);
+ histogram_tester.ExpectTotalCount("Tab.RendererDetailedExitStatus", 0);
+ EXPECT_EQ(0, dumps_uploaded_);
}
-TEST_F(CrashDumpManagerTest, NoDumpCreated) {
+TEST_F(CrashDumpManagerTest, NonOomCrash) {
base::HistogramTester histogram_tester;
- CrashDumpManager* manager = CrashDumpManager::GetInstance();
- CrashDumpManagerObserver crash_dump_observer;
- manager->AddObserver(&crash_dump_observer);
+ CrashMetricsReporterObserver observer;
+ crash_reporter::CrashMetricsReporter::GetInstance()->AddObserver(&observer);
- CrashDumpObserver::TerminationInfo termination_info;
+ crash_reporter::ChildExitObserver::TerminationInfo termination_info;
termination_info.process_host_id = 1;
termination_info.pid = base::kNullProcessHandle;
termination_info.process_type = content::PROCESS_TYPE_RENDERER;
termination_info.app_state =
base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
termination_info.normal_termination = false;
- termination_info.has_oom_protection_bindings = true;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
termination_info.was_killed_intentionally_by_browser = false;
termination_info.was_oom_protected_status = true;
base::PostTaskWithTraits(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
- base::Bind(&CrashDumpManager::ProcessMinidumpFileFromChild,
- base::Unretained(manager), base::FilePath(),
- termination_info));
- crash_dump_observer.WaitForProcessed();
-
- const CrashDumpManager::CrashDumpDetails& details =
- crash_dump_observer.last_details();
- EXPECT_EQ(termination_info.process_host_id, details.process_host_id);
- EXPECT_EQ(content::PROCESS_TYPE_RENDERER, details.process_type);
- EXPECT_TRUE(details.was_oom_protected_status);
- EXPECT_EQ(base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES,
- details.app_state);
- EXPECT_EQ(0, details.file_size);
- EXPECT_EQ(CrashDumpManager::CrashDumpStatus::kNoDump, details.status);
+ base::BindOnce(&CrashDumpManagerTest::CreateAndProcessCrashDump,
+ termination_info, "Some non-empty crash data"));
+ observer.WaitForProcessed();
- histogram_tester.ExpectTotalCount("Tab.RendererDetailedExitStatus", 0);
+ histogram_tester.ExpectUniqueSample(
+ "Tab.RendererDetailedExitStatus",
+ crash_reporter::CrashMetricsReporter::VALID_MINIDUMP_WHILE_RUNNING, 1);
+ WaitForCrashDumpsUploaded(1);
}
} // namespace breakpad
diff --git a/chromium/components/crash/content/browser/crash_handler_host_linux.cc b/chromium/components/crash/content/browser/crash_handler_host_linux.cc
index 57f710ab886..e672add8df1 100644
--- a/chromium/components/crash/content/browser/crash_handler_host_linux.cc
+++ b/chromium/components/crash/content/browser/crash_handler_host_linux.cc
@@ -48,7 +48,7 @@
#if !defined(OS_CHROMEOS)
#include "components/crash/content/app/crashpad.h"
-#include "third_party/crashpad/crashpad/client/crashpad_client.h"
+#include "third_party/crashpad/crashpad/client/crashpad_client.h" // nogncheck
#endif
using content::BrowserThread;
diff --git a/chromium/components/crash/content/browser/crash_metrics_reporter_android.cc b/chromium/components/crash/content/browser/crash_metrics_reporter_android.cc
new file mode 100644
index 00000000000..3c2babbd6b6
--- /dev/null
+++ b/chromium/components/crash/content/browser/crash_metrics_reporter_android.cc
@@ -0,0 +1,271 @@
+// 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/crash/content/browser/crash_metrics_reporter_android.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/optional.h"
+#include "components/crash/content/browser/crash_dump_manager_android.h"
+
+namespace crash_reporter {
+namespace {
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class BindingStateCombo {
+ kNoWaivedNoModerateNoStrong = 0,
+ kNoWaivedNoModerateHasStrong = 1,
+ kNoWaivedHasModerateNoStrong = 2,
+ kNoWaivedHasModerateHasStrong = 3,
+ kHasWaivedNoModerateNoStrong = 4,
+ kHasWaivedNoModerateHasStrong = 5,
+ kHasWaivedHasModerateNoStrong = 6,
+ kHasWaivedHasModerateHasStrong = 7,
+ kMaxValue = kHasWaivedHasModerateHasStrong
+};
+
+void ReportCrashCount(CrashMetricsReporter::ProcessedCrashCounts crash_type,
+ CrashMetricsReporter::ReportedCrashTypeSet* counts) {
+ UMA_HISTOGRAM_ENUMERATION("Stability.Android.ProcessedCrashCounts",
+ crash_type);
+ counts->insert(crash_type);
+}
+
+void ReportLegacyCrashUma(const ChildExitObserver::TerminationInfo& info,
+ bool has_valid_dump) {
+ // TODO(wnwen): If these numbers match up to TabWebContentsObserver's
+ // TabRendererCrashStatus histogram, then remove that one as this is more
+ // accurate with more detail.
+ if ((info.process_type == content::PROCESS_TYPE_RENDERER ||
+ info.process_type == content::PROCESS_TYPE_GPU) &&
+ info.app_state != base::android::APPLICATION_STATE_UNKNOWN) {
+ CrashMetricsReporter::ExitStatus exit_status;
+ bool is_running = (info.app_state ==
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES);
+ bool is_paused = (info.app_state ==
+ base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES);
+ if (!has_valid_dump) {
+ if (is_running) {
+ exit_status = CrashMetricsReporter::EMPTY_MINIDUMP_WHILE_RUNNING;
+ } else if (is_paused) {
+ exit_status = CrashMetricsReporter::EMPTY_MINIDUMP_WHILE_PAUSED;
+ } else {
+ exit_status = CrashMetricsReporter::EMPTY_MINIDUMP_WHILE_BACKGROUND;
+ }
+ } else {
+ if (is_running) {
+ exit_status = CrashMetricsReporter::VALID_MINIDUMP_WHILE_RUNNING;
+ } else if (is_paused) {
+ exit_status = CrashMetricsReporter::VALID_MINIDUMP_WHILE_PAUSED;
+ } else {
+ exit_status = CrashMetricsReporter::VALID_MINIDUMP_WHILE_BACKGROUND;
+ }
+ }
+ if (info.process_type == content::PROCESS_TYPE_RENDERER) {
+ if (info.was_oom_protected_status) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Tab.RendererDetailedExitStatus", exit_status,
+ CrashMetricsReporter::ExitStatus::MINIDUMP_STATUS_COUNT);
+ } else {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Tab.RendererDetailedExitStatusUnbound", exit_status,
+ CrashMetricsReporter::ExitStatus::MINIDUMP_STATUS_COUNT);
+ }
+ } else if (info.process_type == content::PROCESS_TYPE_GPU) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "GPU.GPUProcessDetailedExitStatus", exit_status,
+ CrashMetricsReporter::ExitStatus::MINIDUMP_STATUS_COUNT);
+ }
+ }
+}
+
+} // namespace
+
+// static
+CrashMetricsReporter* CrashMetricsReporter::GetInstance() {
+ static CrashMetricsReporter* instance = new CrashMetricsReporter();
+ return instance;
+}
+
+CrashMetricsReporter::CrashMetricsReporter()
+ : async_observers_(
+ base::MakeRefCounted<
+ base::ObserverListThreadSafe<CrashMetricsReporter::Observer>>()) {
+}
+
+CrashMetricsReporter::~CrashMetricsReporter() {}
+
+void CrashMetricsReporter::AddObserver(
+ CrashMetricsReporter::Observer* observer) {
+ async_observers_->AddObserver(observer);
+}
+
+void CrashMetricsReporter::RemoveObserver(
+ CrashMetricsReporter::Observer* observer) {
+ async_observers_->RemoveObserver(observer);
+}
+
+void CrashMetricsReporter::CrashDumpProcessed(
+ const ChildExitObserver::TerminationInfo& info,
+ breakpad::CrashDumpManager::CrashDumpStatus status) {
+ ReportedCrashTypeSet reported_counts;
+ if (status == breakpad::CrashDumpManager::CrashDumpStatus::kMissingDump) {
+ NotifyObservers(info.process_host_id, reported_counts);
+ return;
+ }
+
+ bool has_valid_dump = false;
+ switch (status) {
+ case breakpad::CrashDumpManager::CrashDumpStatus::kMissingDump:
+ NOTREACHED();
+ break;
+ case breakpad::CrashDumpManager::CrashDumpStatus::kEmptyDump:
+ has_valid_dump = false;
+ break;
+ case breakpad::CrashDumpManager::CrashDumpStatus::kValidDump:
+ case breakpad::CrashDumpManager::CrashDumpStatus::kDumpProcessingFailed:
+ has_valid_dump = true;
+ break;
+ }
+ const bool app_foreground =
+ info.app_state ==
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES ||
+ info.app_state == base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES;
+ const bool intentional_kill = info.was_killed_intentionally_by_browser;
+ const bool android_oom_kill = !info.was_killed_intentionally_by_browser &&
+ !has_valid_dump && !info.normal_termination;
+ const bool renderer_visible = info.renderer_has_visible_clients;
+ const bool renderer_subframe = info.renderer_was_subframe;
+
+ if (info.process_type == content::PROCESS_TYPE_GPU && app_foreground &&
+ android_oom_kill) {
+ ReportCrashCount(ProcessedCrashCounts::kGpuForegroundOom, &reported_counts);
+ }
+
+ if (info.process_type == content::PROCESS_TYPE_RENDERER && app_foreground) {
+ if (renderer_visible) {
+ if (has_valid_dump) {
+ ReportCrashCount(
+ renderer_subframe
+ ? ProcessedCrashCounts::kRendererForegroundVisibleSubframeCrash
+ : ProcessedCrashCounts::kRendererForegroundVisibleCrash,
+ &reported_counts);
+ } else if (intentional_kill) {
+ ReportCrashCount(
+ renderer_subframe
+ ? ProcessedCrashCounts::
+ kRendererForegroundVisibleSubframeIntentionalKill
+ : ProcessedCrashCounts::
+ kRendererForegroundVisibleMainFrameIntentionalKill,
+ &reported_counts);
+ } else if (info.normal_termination) {
+ ReportCrashCount(ProcessedCrashCounts::
+ kRendererForegroundVisibleNormalTermNoMinidump,
+ &reported_counts);
+ } else {
+ DCHECK(android_oom_kill);
+ if (renderer_subframe) {
+ ReportCrashCount(
+ ProcessedCrashCounts::kRendererForegroundVisibleSubframeOom,
+ &reported_counts);
+ } else {
+ ReportCrashCount(ProcessedCrashCounts::kRendererForegroundVisibleOom,
+ &reported_counts);
+ base::RecordAction(
+ base::UserMetricsAction("RendererForegroundMainFrameOOM"));
+ }
+ }
+ } else if (!has_valid_dump) {
+ // Record stats when renderer is not visible, but the process has oom
+ // protected bindings. This case occurs when a tab is switched or closed,
+ // the bindings are updated later than visibility on web contents.
+ switch (info.binding_state) {
+ case base::android::ChildBindingState::UNBOUND:
+ case base::android::ChildBindingState::WAIVED:
+ break;
+ case base::android::ChildBindingState::STRONG:
+ if (intentional_kill || info.normal_termination) {
+ ReportCrashCount(
+ ProcessedCrashCounts::
+ kRendererForegroundInvisibleWithStrongBindingKilled,
+ &reported_counts);
+ } else {
+ ReportCrashCount(
+ ProcessedCrashCounts::
+ kRendererForegroundInvisibleWithStrongBindingOom,
+ &reported_counts);
+ }
+ break;
+ case base::android::ChildBindingState::MODERATE:
+ if (intentional_kill || info.normal_termination) {
+ ReportCrashCount(
+ ProcessedCrashCounts::
+ kRendererForegroundInvisibleWithModerateBindingKilled,
+ &reported_counts);
+ } else {
+ ReportCrashCount(
+ ProcessedCrashCounts::
+ kRendererForegroundInvisibleWithModerateBindingOom,
+ &reported_counts);
+ }
+ break;
+ }
+ }
+ }
+
+ if (info.process_type == content::PROCESS_TYPE_RENDERER &&
+ (info.app_state ==
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES ||
+ info.app_state ==
+ base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES) &&
+ info.was_killed_intentionally_by_browser) {
+ ReportCrashCount(ProcessedCrashCounts::kRendererForegroundIntentionalKill,
+ &reported_counts);
+ }
+
+ if (has_valid_dump) {
+ ReportCrashCount(info.process_type == content::PROCESS_TYPE_GPU
+ ? ProcessedCrashCounts::kGpuCrashAll
+ : ProcessedCrashCounts::kRendererCrashAll,
+ &reported_counts);
+ }
+
+ if (app_foreground && android_oom_kill &&
+ info.binding_state == base::android::ChildBindingState::STRONG) {
+ const bool has_waived = info.remaining_process_with_waived_binding > 0;
+ const bool has_moderate = info.remaining_process_with_moderate_binding > 0;
+ const bool has_strong = info.remaining_process_with_strong_binding > 0;
+ BindingStateCombo combo;
+ if (has_waived && has_moderate) {
+ combo = has_strong ? BindingStateCombo::kHasWaivedHasModerateHasStrong
+ : BindingStateCombo::kHasWaivedHasModerateNoStrong;
+ } else if (has_waived) {
+ combo = has_strong ? BindingStateCombo::kHasWaivedNoModerateHasStrong
+ : BindingStateCombo::kHasWaivedNoModerateNoStrong;
+ } else if (has_moderate) {
+ combo = has_strong ? BindingStateCombo::kNoWaivedHasModerateHasStrong
+ : BindingStateCombo::kNoWaivedHasModerateNoStrong;
+ } else {
+ combo = has_strong ? BindingStateCombo::kNoWaivedNoModerateHasStrong
+ : BindingStateCombo::kNoWaivedNoModerateNoStrong;
+ }
+ UMA_HISTOGRAM_ENUMERATION(
+ "Stability.Android.StrongBindingOomRemainingBindingState", combo);
+ }
+
+ ReportLegacyCrashUma(info, has_valid_dump);
+ NotifyObservers(info.process_host_id, reported_counts);
+}
+
+void CrashMetricsReporter::NotifyObservers(
+ int rph_id,
+ const CrashMetricsReporter::ReportedCrashTypeSet& reported_counts) {
+ async_observers_->Notify(
+ FROM_HERE, &CrashMetricsReporter::Observer::OnCrashDumpProcessed, rph_id,
+ reported_counts);
+}
+
+} // namespace crash_reporter
diff --git a/chromium/components/crash/content/browser/crash_metrics_reporter_android.h b/chromium/components/crash/content/browser/crash_metrics_reporter_android.h
new file mode 100644
index 00000000000..3dcf044cabb
--- /dev/null
+++ b/chromium/components/crash/content/browser/crash_metrics_reporter_android.h
@@ -0,0 +1,91 @@
+// 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_CRASH_CONTENT_BROWSER_CRASH_METRICS_REPORTER_ANDROID_H_
+#define COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_METRICS_REPORTER_ANDROID_H_
+
+#include "base/containers/flat_set.h"
+#include "base/observer_list_threadsafe.h"
+#include "components/crash/content/browser/child_exit_observer_android.h"
+#include "components/crash/content/browser/crash_dump_manager_android.h"
+
+namespace crash_reporter {
+
+// Reports crash metrics about child processes to UMA, which is used as ground
+// truth for child process stability. This class should be used by any code that
+// wants to observe reason for the death of a child process.
+class CrashMetricsReporter {
+ public:
+ // This enum is used to back a UMA histogram, and must be treated as
+ // append-only.
+ enum ExitStatus {
+ EMPTY_MINIDUMP_WHILE_RUNNING,
+ EMPTY_MINIDUMP_WHILE_PAUSED,
+ EMPTY_MINIDUMP_WHILE_BACKGROUND,
+ VALID_MINIDUMP_WHILE_RUNNING,
+ VALID_MINIDUMP_WHILE_PAUSED,
+ VALID_MINIDUMP_WHILE_BACKGROUND,
+ MINIDUMP_STATUS_COUNT
+ };
+
+ // These values are persisted to logs. Entries should not be renumbered and
+ // numeric values should never be reused.
+ enum class ProcessedCrashCounts {
+ kGpuForegroundOom = 0,
+ kRendererForegroundVisibleOom = 1,
+ kRendererForegroundIntentionalKill = 2,
+ kRendererForegroundVisibleSubframeOom = 3,
+ kRendererForegroundVisibleSubframeIntentionalKill = 4,
+ kRendererForegroundVisibleCrash = 5,
+ kRendererForegroundVisibleSubframeCrash = 6,
+ kGpuCrashAll = 7,
+ kRendererCrashAll = 8,
+ kRendererForegroundVisibleMainFrameIntentionalKill = 9,
+ kRendererForegroundVisibleNormalTermNoMinidump = 10,
+ kRendererForegroundInvisibleWithStrongBindingKilled = 11,
+ kRendererForegroundInvisibleWithStrongBindingOom = 12,
+ kRendererForegroundInvisibleWithModerateBindingKilled = 13,
+ kRendererForegroundInvisibleWithModerateBindingOom = 14,
+ kMaxValue = kRendererForegroundInvisibleWithModerateBindingOom
+ };
+ using ReportedCrashTypeSet = base::flat_set<ProcessedCrashCounts>;
+
+ // Careful note: the CrashMetricsReporter observers are asynchronous, and are
+ // notified via PostTask. This could be problematic with a large number of
+ // observers. Consider using a middle-layer observer to fan out synchronously
+ // to leaf observers if you need many objects listening to these messages.
+ class Observer {
+ public:
+ // Called when child process is dead and minidump was processed.
+ // |reported_counts| is a set of recorded metrics about child process
+ // crashes. It could be empty if no metrics were recorded.
+ virtual void OnCrashDumpProcessed(
+ int rph_id,
+ const ReportedCrashTypeSet& reported_counts) = 0;
+ };
+
+ static CrashMetricsReporter* GetInstance();
+
+ // Can be called on any thread.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ void CrashDumpProcessed(const ChildExitObserver::TerminationInfo& info,
+ breakpad::CrashDumpManager::CrashDumpStatus status);
+
+ private:
+ CrashMetricsReporter();
+ ~CrashMetricsReporter();
+
+ void NotifyObservers(int rph_id, const ReportedCrashTypeSet& reported_counts);
+
+ scoped_refptr<base::ObserverListThreadSafe<CrashMetricsReporter::Observer>>
+ async_observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashMetricsReporter);
+};
+
+} // namespace crash_reporter
+
+#endif // COMPONENTS_CRASH_CONTENT_BROWSER_CRASH_METRICS_REPORTER_ANDROID_H_
diff --git a/chromium/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc b/chromium/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc
new file mode 100644
index 00000000000..4367690dcdc
--- /dev/null
+++ b/chromium/components/crash/content/browser/crash_metrics_reporter_android_unittest.cc
@@ -0,0 +1,255 @@
+// 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/crash/content/browser/crash_metrics_reporter_android.h"
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace crash_reporter {
+
+class CrashMetricsReporterObserver : public CrashMetricsReporter::Observer {
+ public:
+ CrashMetricsReporterObserver() {}
+ ~CrashMetricsReporterObserver() {}
+
+ // CrashMetricsReporter::Observer:
+ void OnCrashDumpProcessed(int rph_id,
+ const CrashMetricsReporter::ReportedCrashTypeSet&
+ reported_counts) override {
+ recorded_crash_types_ = reported_counts;
+ wait_run_loop_.QuitClosure().Run();
+ }
+
+ const CrashMetricsReporter::ReportedCrashTypeSet& recorded_crash_types()
+ const {
+ return recorded_crash_types_;
+ }
+
+ void WaitForProcessed() { wait_run_loop_.Run(); }
+
+ private:
+ base::RunLoop wait_run_loop_;
+ CrashMetricsReporter::ReportedCrashTypeSet recorded_crash_types_;
+ DISALLOW_COPY_AND_ASSIGN(CrashMetricsReporterObserver);
+};
+
+class CrashMetricsReporterTest : public testing::Test {
+ public:
+ CrashMetricsReporterTest()
+ : scoped_environment_(
+ base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
+ ~CrashMetricsReporterTest() override {}
+
+ static void CreateAndProcessCrashDump(
+ const ChildExitObserver::TerminationInfo& info,
+ const std::string& data) {
+ base::ScopedFD fd =
+ breakpad::CrashDumpManager::GetInstance()->CreateMinidumpFileForChild(
+ info.process_host_id);
+ EXPECT_TRUE(fd.is_valid());
+ base::WriteFileDescriptor(fd.get(), data.data(), data.size());
+ base::ScopedTempDir dump_dir;
+ EXPECT_TRUE(dump_dir.CreateUniqueTempDir());
+ breakpad::CrashDumpManager::GetInstance()->ProcessMinidumpFileFromChild(
+ dump_dir.GetPath(), info);
+ }
+
+ protected:
+ void TestOomCrashProcessing(
+ const ChildExitObserver::TerminationInfo& termination_info,
+ CrashMetricsReporter::ReportedCrashTypeSet expected_crash_types,
+ const char* histogram_name) {
+ base::HistogramTester histogram_tester;
+
+ CrashMetricsReporterObserver crash_dump_observer;
+ CrashMetricsReporter::GetInstance()->AddObserver(&crash_dump_observer);
+
+ CrashMetricsReporter::GetInstance()->CrashDumpProcessed(
+ termination_info,
+ breakpad::CrashDumpManager::CrashDumpStatus::kEmptyDump);
+ crash_dump_observer.WaitForProcessed();
+
+ EXPECT_EQ(expected_crash_types, crash_dump_observer.recorded_crash_types());
+
+ if (histogram_name) {
+ histogram_tester.ExpectUniqueSample(
+ histogram_name, CrashMetricsReporter::EMPTY_MINIDUMP_WHILE_RUNNING,
+ 1);
+ }
+ }
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_environment_;
+ DISALLOW_COPY_AND_ASSIGN(CrashMetricsReporterTest);
+};
+
+TEST_F(CrashMetricsReporterTest, RendereMainFrameOOM) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_RENDERER;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = false;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
+ termination_info.was_killed_intentionally_by_browser = false;
+ termination_info.was_oom_protected_status = true;
+ termination_info.renderer_has_visible_clients = true;
+ TestOomCrashProcessing(termination_info,
+ {CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundVisibleOom},
+ "Tab.RendererDetailedExitStatus");
+}
+
+TEST_F(CrashMetricsReporterTest, GpuProcessOOM) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_GPU;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = false;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
+ termination_info.was_killed_intentionally_by_browser = false;
+ termination_info.was_oom_protected_status = true;
+ termination_info.renderer_has_visible_clients = true;
+ TestOomCrashProcessing(
+ termination_info,
+ {CrashMetricsReporter::ProcessedCrashCounts::kGpuForegroundOom},
+ "GPU.GPUProcessDetailedExitStatus");
+}
+
+TEST_F(CrashMetricsReporterTest, RendererSubframeOOM) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_RENDERER;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = false;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
+ termination_info.was_killed_intentionally_by_browser = false;
+ termination_info.was_oom_protected_status = true;
+ termination_info.renderer_has_visible_clients = true;
+ termination_info.renderer_was_subframe = true;
+ TestOomCrashProcessing(termination_info,
+ {CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundVisibleSubframeOom},
+ "Tab.RendererDetailedExitStatus");
+}
+
+TEST_F(CrashMetricsReporterTest, RendererNonVisibleStrongOOM) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_RENDERER;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = false;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
+ termination_info.was_oom_protected_status = true;
+ termination_info.was_killed_intentionally_by_browser = false;
+ termination_info.renderer_has_visible_clients = false;
+ TestOomCrashProcessing(termination_info,
+ {CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundInvisibleWithStrongBindingOom},
+ "Tab.RendererDetailedExitStatus");
+}
+
+TEST_F(CrashMetricsReporterTest, RendererNonVisibleModerateOOM) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_RENDERER;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = false;
+ termination_info.binding_state = base::android::ChildBindingState::MODERATE;
+ termination_info.was_oom_protected_status = true;
+ termination_info.was_killed_intentionally_by_browser = false;
+ termination_info.renderer_has_visible_clients = false;
+ TestOomCrashProcessing(
+ termination_info,
+ {CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundInvisibleWithModerateBindingOom},
+ "Tab.RendererDetailedExitStatus");
+}
+
+TEST_F(CrashMetricsReporterTest, IntentionalKillIsNotOOM) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_RENDERER;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = false;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
+ termination_info.was_killed_intentionally_by_browser = true;
+ termination_info.was_oom_protected_status = true;
+ termination_info.renderer_has_visible_clients = true;
+ TestOomCrashProcessing(
+ termination_info,
+ {CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundIntentionalKill,
+ CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundVisibleMainFrameIntentionalKill},
+ "Tab.RendererDetailedExitStatus");
+}
+
+TEST_F(CrashMetricsReporterTest, NormalTerminationIsNotOOM) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_RENDERER;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = true;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
+ termination_info.was_killed_intentionally_by_browser = false;
+ termination_info.was_oom_protected_status = true;
+ termination_info.renderer_has_visible_clients = true;
+ TestOomCrashProcessing(termination_info,
+ {CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundVisibleNormalTermNoMinidump},
+ nullptr);
+}
+
+TEST_F(CrashMetricsReporterTest, RendererForegroundCrash) {
+ ChildExitObserver::TerminationInfo termination_info;
+ termination_info.process_host_id = 1;
+ termination_info.pid = base::kNullProcessHandle;
+ termination_info.process_type = content::PROCESS_TYPE_RENDERER;
+ termination_info.app_state =
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+ termination_info.normal_termination = true;
+ termination_info.binding_state = base::android::ChildBindingState::STRONG;
+ termination_info.was_killed_intentionally_by_browser = true;
+ termination_info.was_oom_protected_status = true;
+ termination_info.renderer_has_visible_clients = true;
+
+ CrashMetricsReporterObserver crash_dump_observer;
+ CrashMetricsReporter::GetInstance()->AddObserver(&crash_dump_observer);
+
+ CrashMetricsReporter::GetInstance()->CrashDumpProcessed(
+ termination_info,
+ breakpad::CrashDumpManager::CrashDumpStatus::kValidDump);
+ crash_dump_observer.WaitForProcessed();
+
+ EXPECT_EQ(
+ CrashMetricsReporter::ReportedCrashTypeSet(
+ {CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundIntentionalKill,
+ CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundVisibleCrash,
+ CrashMetricsReporter::ProcessedCrashCounts::kRendererCrashAll}),
+ crash_dump_observer.recorded_crash_types());
+}
+
+} // namespace crash_reporter
diff --git a/chromium/components/crash/content/tools/generate_breakpad_symbols.py b/chromium/components/crash/content/tools/generate_breakpad_symbols.py
index 24250f81fa1..2032f1991ad 100755
--- a/chromium/components/crash/content/tools/generate_breakpad_symbols.py
+++ b/chromium/components/crash/content/tools/generate_breakpad_symbols.py
@@ -29,24 +29,6 @@ BINARY_INFO = collections.namedtuple('BINARY_INFO',
['platform', 'arch', 'hash', 'name'])
-def GetCommandOutput(command, env=None):
- """Runs the command list, returning its output (stdout).
-
- Args:
- command: (list of strings) a command with arguments
-
- env: (dict or None) optional environment for the command. If None,
- inherits the existing environment, otherwise completely overrides it.
-
- From chromium_utils.
- """
- devnull = open(os.devnull, 'w')
- proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=devnull,
- bufsize=1, env=env)
- output = proc.communicate()[0]
- return output
-
-
def GetDumpSymsBinary(build_dir=None):
"""Returns the path to the dump_syms binary."""
DUMP_SYMS = 'dump_syms'
@@ -70,8 +52,7 @@ def Resolve(path, exe_path, loader_path, rpaths):
path = path.replace('@executable_path', exe_path)
if path.find('@rpath') != -1:
for rpath in rpaths:
- new_path = Resolve(path.replace('@rpath', rpath), exe_path, loader_path,
- [])
+ new_path = path.replace('@rpath', rpath)
if os.access(new_path, os.X_OK):
return new_path
return ''
@@ -82,7 +63,10 @@ def GetSharedLibraryDependenciesLinux(binary):
"""Return absolute paths to all shared library dependecies of the binary.
This implementation assumes that we're running on a Linux system."""
- ldd = GetCommandOutput(['ldd', binary])
+ # TODO(thakis): Figure out how to make this work for android
+ # (https://crbug.com/849904) and use check_output().
+ p = subprocess.Popen(['ldd', binary], stdout=subprocess.PIPE)
+ ldd = p.communicate()[0]
lib_re = re.compile('\t.* => (.+) \(.*\)$')
result = []
for line in ldd.splitlines():
@@ -106,7 +90,7 @@ def GetDeveloperDirMac():
if 'DEVELOPER_DIR' in os.environ:
candidate_paths.append(os.environ['DEVELOPER_DIR'])
candidate_paths.extend([
- GetCommandOutput(['xcode-select', '-p']).strip(),
+ subprocess.check_output(['xcode-select', '-p']).strip(),
# Most Mac 10.1[0-2] bots have at least one Xcode installed.
'/Applications/Xcode.app',
'/Applications/Xcode9.0.app',
@@ -125,27 +109,57 @@ def GetSharedLibraryDependenciesMac(binary, exe_path):
"""Return absolute paths to all shared library dependecies of the binary.
This implementation assumes that we're running on a Mac system."""
- loader_path = os.path.dirname(binary)
+ # realpath() serves two purposes:
+ # 1. If an executable is linked against a framework, it links against
+ # Framework.framework/Framework, which is a symlink to
+ # Framework.framework/Framework/Versions/A/Framework. rpaths are relative
+ # to the real location, so resolving the symlink is important.
+ # 2. It converts binary to an absolute path. If binary is just
+ # "foo.dylib" in the current directory, dirname() would return an empty
+ # string, causing "@loader_path/foo" to incorrectly expand to "/foo".
+ loader_path = os.path.dirname(os.path.realpath(binary))
env = os.environ.copy()
developer_dir = GetDeveloperDirMac()
if developer_dir:
env['DEVELOPER_DIR'] = developer_dir
- otool = GetCommandOutput(['otool', '-l', binary], env=env).splitlines()
+ otool = subprocess.check_output(['otool', '-l', binary], env=env).splitlines()
rpaths = []
+ dylib_id = None
for idx, line in enumerate(otool):
if line.find('cmd LC_RPATH') != -1:
m = re.match(' *path (.*) \(offset .*\)$', otool[idx+2])
- rpaths.append(m.group(1))
-
- otool = GetCommandOutput(['otool', '-L', binary], env=env).splitlines()
+ rpath = m.group(1)
+ rpath = rpath.replace('@loader_path', loader_path)
+ rpath = rpath.replace('@executable_path', exe_path)
+ rpaths.append(rpath)
+ elif line.find('cmd LC_ID_DYLIB') != -1:
+ m = re.match(' *name (.*) \(offset .*\)$', otool[idx+2])
+ dylib_id = m.group(1)
+ # `man dyld` says that @rpath is resolved against a stack of LC_RPATHs from
+ # all executable images leading to the load of the current module. This is
+ # intentionally not implemented here, since we require that every .dylib
+ # contains all the rpaths it needs on its own, without relying on rpaths of
+ # the loading executables.
+
+ otool = subprocess.check_output(['otool', '-L', binary], env=env).splitlines()
lib_re = re.compile('\t(.*) \(compatibility .*\)$')
deps = []
for line in otool:
m = lib_re.match(line)
if m:
+ # For frameworks and shared libraries, `otool -L` prints the LC_ID_DYLIB
+ # as the first line. Filter that out.
+ if m.group(1) == dylib_id:
+ continue
dep = Resolve(m.group(1), exe_path, loader_path, rpaths)
if dep:
deps.append(os.path.normpath(dep))
+ else:
+ print >>sys.stderr, (
+ 'ERROR: failed to resolve %s, exe_path %s, loader_path %s, '
+ 'rpaths %s' % (m.group(1), exe_path, loader_path,
+ ', '.join(rpaths)))
+ sys.exit(1)
return deps
@@ -211,7 +225,7 @@ def GenerateSymbols(options, binaries):
break
binary_info = GetBinaryInfoFromHeaderInfo(
- GetCommandOutput([dump_syms, '-i', binary]).splitlines()[0])
+ subprocess.check_output([dump_syms, '-i', binary]).splitlines()[0])
if not binary_info:
should_dump_syms = False
reason = "Could not obtain binary information."
diff --git a/chromium/components/cronet/BUILD.gn b/chromium/components/cronet/BUILD.gn
index 8665bc24d7a..59522b73577 100644
--- a/chromium/components/cronet/BUILD.gn
+++ b/chromium/components/cronet/BUILD.gn
@@ -24,6 +24,8 @@ source_set("cronet_common") {
"cronet_global_state.h",
"cronet_prefs_manager.cc",
"cronet_prefs_manager.h",
+ "cronet_upload_data_stream.cc",
+ "cronet_upload_data_stream.h",
"cronet_url_request.cc",
"cronet_url_request.h",
"cronet_url_request_context.cc",
@@ -84,10 +86,6 @@ if (!is_ios && !is_android) {
shared_library("cronet") {
deps = [
"//base",
-
- # Explicitly add the exe_and_shlib_deps, otherwise the library will fail
- # to link in component builds, due to missing libc++ symbols.
- "//build/config:exe_and_shlib_deps",
"//components/cronet:cronet_common",
"//components/cronet/native:cronet_native_impl",
"//net",
diff --git a/chromium/components/cronet/android/BUILD.gn b/chromium/components/cronet/android/BUILD.gn
index fc1f39aaee3..120352c9388 100644
--- a/chromium/components/cronet/android/BUILD.gn
+++ b/chromium/components/cronet/android/BUILD.gn
@@ -13,8 +13,6 @@ import("//third_party/netty4/netty4.gni")
import("//third_party/protobuf/proto_library.gni")
import("//url/features.gni")
-assert(!is_component_build, "Cronet requires static library build.")
-
generate_jni("cronet_jni_headers") {
sources = [
"java/src/org/chromium/net/impl/CronetBidirectionalStream.java",
@@ -146,23 +144,8 @@ zip("cronet_impl_version_srcjar") {
_cronet_version_header_include_dir = "$target_gen_dir/cronet_version_header"
-proto_library("cronet_android_cert_proto") {
- visibility = [ "//components/cronet/android/*" ]
-
- sources = [
- "//components/cronet/android/cert/proto/cert_verification.proto",
- ]
-
- deps = [
- "//third_party/protobuf:protobuf_lite",
- ]
-
- extra_configs = [ "//build/config/compiler:wexit_time_destructors" ]
-}
-
source_set("cronet_static") {
deps = [
- ":cronet_android_cert_proto",
":cronet_jni_headers",
":cronet_jni_registration",
"//base",
@@ -173,18 +156,15 @@ source_set("cronet_static") {
"//components/metrics",
"//components/prefs",
"//net",
+ "//third_party/zlib:zlib",
"//url",
"//url:url_features",
]
sources = [
- "//components/cronet/android/cert/cert_verifier_cache_serializer.cc",
- "//components/cronet/android/cert/cert_verifier_cache_serializer.h",
"//components/cronet/android/cronet_bidirectional_stream_adapter.cc",
"//components/cronet/android/cronet_bidirectional_stream_adapter.h",
"//components/cronet/android/cronet_library_loader.cc",
"//components/cronet/android/cronet_library_loader.h",
- "//components/cronet/android/cronet_upload_data_stream.cc",
- "//components/cronet/android/cronet_upload_data_stream.h",
"//components/cronet/android/cronet_upload_data_stream_adapter.cc",
"//components/cronet/android/cronet_upload_data_stream_adapter.h",
"//components/cronet/android/cronet_url_request_adapter.cc",
@@ -222,7 +202,6 @@ config("hide_all_but_jni_onload_and_cronet") {
}
_cronet_shared_lib_name = "cronet.$chrome_version_full"
-_cronet_shared_lib_file_name = "lib" + _cronet_shared_lib_name + ".so"
shared_library("cronet") {
output_name = _cronet_shared_lib_name
@@ -454,467 +433,212 @@ android_apk("cronet_sample_apk") {
]
}
-# cronet_sample_test_apk_resources is identical to
-# cronet_sample_apk_resources. The two have to be different targets because
-# targets which are common between the "instrumentation test apk" and the
-# "tested apk" are removed from the "instrumentation test apk".
-android_resources("cronet_sample_test_apk_resources") {
- resource_dirs = [ "sample/res" ]
- android_manifest = "sample/javatests/AndroidManifest.xml"
-}
-
-instrumentation_test_apk("cronet_sample_test_apk") {
- apk_name = "CronetSampleTest"
- apk_under_test = ":cronet_sample_apk"
- android_manifest = "sample/javatests/AndroidManifest.xml"
- java_files = [
- "sample/javatests/src/org/chromium/cronet_sample_apk/CronetSampleTest.java",
- ]
- deps = [
- "//third_party/android_support_test_runner:rules_java",
- "//third_party/android_support_test_runner:runner_java",
- "//third_party/junit",
- ]
-
- proguard_enabled = true
- proguard_configs = [ "sample/javatests/proguard.cfg" ]
-}
-
-generate_jni("cronet_tests_jni_headers") {
- testonly = true
- sources = [
- "test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java",
- "test/javatests/src/org/chromium/net/CronetUrlRequestTest.java",
- "test/javatests/src/org/chromium/net/ExperimentalOptionsTest.java",
- "test/src/org/chromium/net/CronetTestUtil.java",
- "test/src/org/chromium/net/MockCertVerifier.java",
- "test/src/org/chromium/net/MockUrlRequestJobFactory.java",
- "test/src/org/chromium/net/NativeTestServer.java",
- "test/src/org/chromium/net/QuicTestServer.java",
- "test/src/org/chromium/net/TestUploadDataStreamHandler.java",
- ]
- jni_package = "cronet_tests"
-}
-
-shared_library("cronet_tests") {
- testonly = true
+action("cronet_combine_proguard_flags") {
+ script = "//components/cronet/tools/generate_proguard_file.py"
sources = [
- # While "cronet_tests" cannot depend on "cronet_static", and hence cannot
- # call any Cronet functions, it can access fields of Cronet objects, so add
- # Cronet header files to facilitate accessing these fields.
- "//components/cronet/android/cronet_url_request_adapter.h",
- "//components/cronet/android/cronet_url_request_context_adapter.h",
- "//components/cronet/cronet_url_request.h",
- "//components/cronet/cronet_url_request_context.h",
- "//components/cronet/url_request_context_config.h",
- "test/cronet_test_jni.cc",
- "test/cronet_test_util.cc",
- "test/cronet_test_util.h",
- "test/cronet_url_request_context_config_test.cc",
- "test/cronet_url_request_context_config_test.h",
- "test/cronet_url_request_test.cc",
- "test/experimental_options_test.cc",
- "test/mock_cert_verifier.cc",
- "test/mock_url_request_job_factory.cc",
- "test/native_test_server.cc",
- "test/quic_test_server.cc",
- "test/test_upload_data_stream_handler.cc",
- "test/test_upload_data_stream_handler.h",
- ]
-
- deps = [
- ":cronet",
- ":cronet_tests_jni_headers",
- "//base",
- "//base:i18n",
- "//base/test:test_support",
- "//components/cronet:cronet_version_header",
- "//components/cronet/test:test_support",
- "//components/prefs",
- "//net",
- "//net:simple_quic_tools",
- "//net:test_support",
- "//third_party/icu",
+ "//base/android/proguard/chromium_code.flags",
+ "//components/cronet/android/cronet_impl_native_proguard.cfg",
]
-
- include_dirs = [ _cronet_version_header_include_dir ]
-
- configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
- configs += [ "//build/config/android:hide_all_but_jni" ]
-}
-
-android_resources("cronet_test_apk_resources") {
- testonly = true
- resource_dirs = [
- "test/res",
- "test/smoketests/res/native",
+ outputs = [
+ "$target_gen_dir/cronet_impl_native_proguard.cfg",
]
- android_manifest = "test/AndroidManifest.xml"
+ args = [ "--output-file" ] + rebase_path(outputs, root_build_dir) +
+ rebase_path(sources, root_build_dir)
}
-android_library("cronet_test_apk_java") {
- testonly = true
-
- java_files = [
- "test/src/org/chromium/net/CronetTestApplication.java",
- "test/src/org/chromium/net/CronetTestUtil.java",
- "test/src/org/chromium/net/Http2TestHandler.java",
- "test/src/org/chromium/net/Http2TestServer.java",
- "test/src/org/chromium/net/MockCertVerifier.java",
- "test/src/org/chromium/net/MockUrlRequestJobFactory.java",
- "test/src/org/chromium/net/NativeTestServer.java",
- "test/src/org/chromium/net/QuicTestServer.java",
- "test/src/org/chromium/net/TestFilesInstaller.java",
- "test/src/org/chromium/net/TestUploadDataStreamHandler.java",
- ]
+_package_dir = "$root_out_dir/cronet"
+# These package_* targets represent how Cronet is used in production code.
+# Using these targets is preferred to using the underlying targets like
+# :cronet_api_java or :cronet_impl_all_java, as it better tests production
+# usage.
+android_java_prebuilt("package_api_java") {
+ jar_path = "$_package_dir/cronet_api.jar"
deps = [
- ":cronet_api_java",
- ":cronet_impl_all_java",
- "//base:base_java",
- "//base:base_java_test_support",
- "//net/android:net_java_test_support",
- "//third_party/junit",
- "//third_party/netty4:netty_all_java",
+ ":copy_cronet_java8_jars_cronet_api_javaX",
]
}
-cronet_smoketests_platform_only_common_srcs = [
- "test/smoketests/src/org/chromium/net/smoke/ChromiumPlatformOnlyTestSupport.java",
- "test/smoketests/src/org/chromium/net/smoke/CronetSmokeTestRule.java",
- "test/smoketests/src/org/chromium/net/smoke/HttpTestServer.java",
- "test/smoketests/src/org/chromium/net/smoke/SmokeTestRequestCallback.java",
- "test/smoketests/src/org/chromium/net/smoke/TestSupport.java",
-]
-
-cronet_smoketests_native_common_srcs = cronet_smoketests_platform_only_common_srcs + [
- "test/smoketests/src/org/chromium/net/smoke/ChromiumNativeTestSupport.java",
- "test/smoketests/src/org/chromium/net/smoke/NativeCronetTestRule.java",
- ]
-
-android_library("cronet_smoketests_native_java") {
- testonly = true
- java_files = [
- "test/smoketests/src/org/chromium/net/smoke/Http2Test.java",
- "test/smoketests/src/org/chromium/net/smoke/QuicTest.java",
- ] + cronet_smoketests_native_common_srcs
-
+android_java_prebuilt("package_impl_common_java") {
+ jar_path = "$_package_dir/cronet_impl_common_java.jar"
deps = [
- ":cronet_api_java",
- ":cronet_test_apk_java",
- "//base:base_java",
- "//base:base_java_test_support",
- "//third_party/android_support_test_runner:runner_java",
- "//third_party/junit",
- "//third_party/netty4:netty_all_java",
+ ":package_api_java",
+ ":repackage_extracted_common_jars",
]
}
-android_apk("cronet_test_apk") {
- testonly = true
- apk_name = "CronetTest"
- android_manifest = "test/AndroidManifest.xml"
- shared_libraries = [
- ":cronet",
- ":cronet_tests",
- ]
- loadable_modules = [ "$root_out_dir/libnetty-tcnative.so" ]
-
+android_java_prebuilt("package_impl_native_java") {
+ jar_path = "$_package_dir/cronet_impl_native_java.jar"
deps = [
- ":cronet_combine_proguard_flags",
- ":cronet_test_apk_resources",
- "//base:base_java",
- "//third_party/netty-tcnative:netty-tcnative-so",
- ]
-
- proguard_enabled = true
-
- proguard_configs = [
- "$target_gen_dir/cronet_impl_native_proguard.cfg",
- "cronet_impl_common_proguard.cfg",
- "cronet_impl_platform_proguard.cfg",
+ ":package_api_java",
+ ":package_impl_common_java",
+ ":repackage_extracted_native_jars",
+ "//third_party/jsr-305:jsr_305_javalib",
]
}
-cronet_javatests_deps_to_package = [
- ":cronet_test_apk_java",
- "//base:base_java",
- "//base:base_java_test_support",
- "//net/android:embedded_test_server_aidl_java",
- "//net/android:net_java",
- "//net/android:net_java_test_support",
- "//url:url_java",
-]
-
-android_library("cronet_javatests") {
- testonly = true
-
- java_files = [
- "test/javatests/src/org/chromium/net/BidirectionalStreamQuicTest.java",
- "test/javatests/src/org/chromium/net/BidirectionalStreamTest.java",
- "test/javatests/src/org/chromium/net/BrotliTest.java",
- "test/javatests/src/org/chromium/net/Criteria.java",
- "test/javatests/src/org/chromium/net/CronetEngineBuilderTest.java",
- "test/javatests/src/org/chromium/net/CronetStressTest.java",
- "test/javatests/src/org/chromium/net/CronetTestRule.java",
- "test/javatests/src/org/chromium/net/CronetTestRuleTest.java",
- "test/javatests/src/org/chromium/net/CronetUploadTest.java",
- "test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java",
- "test/javatests/src/org/chromium/net/CronetUrlRequestTest.java",
- "test/javatests/src/org/chromium/net/DiskStorageTest.java",
- "test/javatests/src/org/chromium/net/ExperimentalOptionsTest.java",
- "test/javatests/src/org/chromium/net/GetStatusTest.java",
- "test/javatests/src/org/chromium/net/MetricsTestUtil.java",
- "test/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java",
- "test/javatests/src/org/chromium/net/NQETest.java",
- "test/javatests/src/org/chromium/net/PkpTest.java",
- "test/javatests/src/org/chromium/net/QuicTest.java",
- "test/javatests/src/org/chromium/net/RequestFinishedInfoTest.java",
- "test/javatests/src/org/chromium/net/TestBidirectionalStreamCallback.java",
- "test/javatests/src/org/chromium/net/TestDrivenDataProvider.java",
- "test/javatests/src/org/chromium/net/TestNetworkQualityRttListener.java",
- "test/javatests/src/org/chromium/net/TestNetworkQualityThroughputListener.java",
- "test/javatests/src/org/chromium/net/TestUploadDataProvider.java",
- "test/javatests/src/org/chromium/net/TestUrlRequestCallback.java",
- "test/javatests/src/org/chromium/net/UploadDataProvidersTest.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",
- "test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLConnectionTest.java",
- "test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandlerTest.java",
- "test/javatests/src/org/chromium/net/urlconnection/CronetURLStreamHandlerFactoryTest.java",
- "test/javatests/src/org/chromium/net/urlconnection/MessageLoopTest.java",
- "test/javatests/src/org/chromium/net/urlconnection/QuicUploadTest.java",
- "test/javatests/src/org/chromium/net/urlconnection/TestUtil.java",
- "test/javatests/src/org/chromium/net/UrlResponseInfoTest.java",
- ]
-
- # Adding deps here won't include those deps in the cronet_tests_java.jar.
- # Please add to cronet_javatests_deps_to_package instead.
+android_java_prebuilt("package_impl_platform_java") {
+ jar_path = "$_package_dir/cronet_impl_platform_java.jar"
deps = [
- ":cronet_api_java",
- ":cronet_impl_all_java",
- "//third_party/android_support_test_runner:runner_java",
- "//third_party/junit",
- ]
- deps += cronet_javatests_deps_to_package
- data = [
- "//components/cronet/test/data/",
+ ":copy_cronet_java8_jars_cronet_impl_platform_base_javaX",
+ ":package_api_java",
+ ":package_impl_common_java",
]
}
-instrumentation_test_apk("cronet_test_instrumentation_apk") {
- apk_name = "CronetTestInstrumentation"
- apk_under_test = ":cronet_test_apk"
- android_manifest = "test/javatests/AndroidManifest.xml"
-
- deps = [
- ":cronet_api_java",
- ":cronet_impl_all_java",
- ":cronet_javatests",
- ":cronet_smoketests_native_java",
- ":cronet_test_apk_java",
- "//base:base_java",
- "//base:base_java_test_support",
- "//net/android:net_java",
- "//net/android:net_java_test_support",
- "//third_party/android_support_test_runner:runner_java",
- "//third_party/hamcrest:hamcrest_core_java",
- ]
- deps += android_extra_test_deps
- additional_apks = [ "//net/android:net_test_support_apk" ]
-
- data_deps = [
- "//net:test_support",
- ]
-
- proguard_enabled = true
-
- proguard_configs = [ "test/proguard.cfg" ]
-}
+template("jar_src") {
+ action(target_name) {
+ _rebased_src_search_dirs =
+ rebase_path(invoker.src_search_dirs, root_build_dir)
-android_resources("cronet_smoketests_platform_only_apk_resources") {
- testonly = true
- resource_dirs = [ "test/smoketests/res/platform_only" ]
- android_manifest = "test/AndroidManifest.xml"
-}
+ script = "//components/cronet/tools/jar_src.py"
+ depfile = "$target_gen_dir/$target_name.d"
+ outputs = [
+ invoker.jar_path,
+ ]
+ args = [
+ "--src-search-dirs=${_rebased_src_search_dirs}",
+ "--jar-path",
+ rebase_path(invoker.jar_path, root_build_dir),
+ "--depfile",
+ rebase_path(depfile, root_build_dir),
+ ]
-android_library("cronet_smoketests_platform_only_java") {
- testonly = true
- java_files = [ "test/smoketests/src/org/chromium/net/smoke/PlatformOnlyEngineTest.java" ] + cronet_smoketests_platform_only_common_srcs
- deps = [
- ":cronet_api_java",
- "//base:base_java_test_support",
- "//third_party/android_support_test_runner:runner_java",
- "//third_party/junit",
- "//third_party/netty4:netty_all_java",
- ]
-}
+ deps = []
+ if (defined(invoker.deps)) {
+ deps += invoker.deps
+ }
-android_apk("cronet_smoketests_platform_only_apk") {
- testonly = true
- apk_name = "PlatformOnlyEngineSmokeTest"
- android_manifest = "test/AndroidManifest.xml"
- java_files = [ "test/src/org/chromium/net/CronetTestApplication.java" ]
+ _excluded_patterns = []
+ if (defined(invoker.excluded_patterns)) {
+ _excluded_patterns = invoker.excluded_patterns
+ }
+ _src_jars = []
- proguard_enabled = true
- proguard_configs = [
- "cronet_impl_common_proguard.cfg",
- "cronet_impl_platform_proguard.cfg",
- ]
+ # Add src-jar files that are listed in "src_jars".
+ if (defined(invoker.src_jars)) {
+ _rebased_src_jars = rebase_path(invoker.src_jars, root_build_dir)
+ _src_jars += _rebased_src_jars
+ }
- deps = [
- ":cronet_api_java",
- ":cronet_impl_common_base_java",
- ":cronet_impl_platform_base_java",
- ":cronet_smoketests_platform_only_apk_resources",
- ]
-}
+ # Add src-jar files that are generated by dependencies in "srcjar_deps".
+ if (defined(invoker.srcjar_deps)) {
+ foreach(_srcjar_dep, invoker.srcjar_deps) {
+ _dep_gen_dir = get_label_info(_srcjar_dep, "target_gen_dir")
+ _dep_name = get_label_info(_srcjar_dep, "name")
+ _src_jars += rebase_path([ "$_dep_gen_dir/$_dep_name.srcjar" ])
+ deps += [ _srcjar_dep ]
+ }
+ }
-instrumentation_test_apk(
- "cronet_smoketests_platform_only_instrumentation_apk") {
- apk_name = "PlatformOnlyEngineSmokeTestInstrumentation"
- apk_under_test = ":cronet_smoketests_platform_only_apk"
- android_manifest = "test/javatests/AndroidManifest.xml"
- deps = [
- ":cronet_smoketests_platform_only_java",
- "//base:base_java_test_support",
- "//third_party/android_support_test_runner:runner_java",
- ]
- deps += android_extra_test_deps
+ # Create the list of all source files that are given in "src_files".
+ _src_files = []
+ if (defined(invoker.src_files)) {
+ _src_files += invoker.src_files
+ }
- proguard_enabled = true
+ # Handle "source_deps".
+ _src_list_files = []
+ if (defined(invoker.source_deps)) {
+ foreach(_source_dep, invoker.source_deps) {
+ _dep_gen_dir = get_label_info(_source_dep, "target_gen_dir")
+ _dep_name = get_label_info(_source_dep, "name")
+ _src_list_files += rebase_path([ "$_dep_gen_dir/$_dep_name.sources" ])
+ deps += [ _source_dep ]
+ }
+ }
+ args += [ "--src-jar=${_src_jars}" ]
+ args += [ "--src-files=${_src_files}" ]
+ args += [ "--src-list-files=${_src_list_files}" ]
+ args += [ "--excluded-classes=$_excluded_patterns" ]
- proguard_configs = [ "test/proguard.cfg" ]
+ inputs = _src_jars
+ inputs += _src_files
+ inputs += _src_list_files
+ }
}
-android_library("cronet_smoketests_missing_native_library_java") {
- testonly = true
- java_files = [ "test/smoketests/src/org/chromium/net/smoke/MissingNativeLibraryTest.java" ] + cronet_smoketests_native_common_srcs
- deps = [
- ":cronet_api_java",
- ":cronet_test_apk_java",
- "//base:base_java",
- "//base:base_java_test_support",
- "//third_party/android_support_test_runner:runner_java",
- "//third_party/junit",
- "//third_party/netty4:netty_all_java",
- ]
+jar_src("jar_cronet_api_source") {
+ src_search_dirs = [ "api/src" ]
+ source_deps = [ ":cronet_api_java" ]
+ srcjar_deps = cronet_api_srcjar_deps
+ jar_path = "$_package_dir/cronet_api-src.jar"
}
-android_apk("cronet_smoketests_missing_native_library_apk") {
- testonly = true
- apk_name = "MissingNativeLibrarySmokeTest"
- android_manifest = "test/AndroidManifest.xml"
- deps = [
- ":cronet_api_java",
- ":cronet_combine_proguard_flags",
- ":cronet_impl_common_base_java",
- ":cronet_impl_platform_base_java",
- ":cronet_test_apk_resources",
- ]
-
- proguard_enabled = true
- proguard_configs = [
- "$target_gen_dir/cronet_impl_native_proguard.cfg",
- "cronet_impl_common_proguard.cfg",
- "cronet_impl_platform_proguard.cfg",
- ]
+jar_src("jar_cronet_impl_common_java_source") {
+ src_search_dirs = [ "java/src" ]
+ source_deps = [ ":cronet_impl_common_base_java" ]
+ srcjar_deps = cronet_impl_common_java_srcjar_deps
+ jar_path = "$_package_dir/cronet_impl_common_java-src.jar"
}
-instrumentation_test_apk(
- "cronet_smoketests_missing_native_library_instrumentation_apk") {
- apk_name = "MissingNativeLibrarySmokeTestInstrumentation"
- apk_under_test = ":cronet_smoketests_missing_native_library_apk"
- android_manifest = "test/javatests/AndroidManifest.xml"
-
- deps = [
- ":cronet_smoketests_missing_native_library_java",
- ]
- deps += android_extra_test_deps
-
- proguard_enabled = true
-
- proguard_configs = [ "test/proguard.cfg" ]
+jar_src("jar_cronet_impl_platform_java_source") {
+ src_search_dirs = [ "java/src" ]
+ source_deps = [ ":cronet_impl_platform_base_java" ]
+ jar_path = "$_package_dir/cronet_impl_platform_java-src.jar"
}
-android_library("cronet_perf_test_apk_java") {
- testonly = true
- android_manifest_for_lint = "test/javaperftests/AndroidManifest.xml"
- java_files =
- [ "test/javaperftests/src/org/chromium/net/CronetPerfTestActivity.java" ]
+template("copy_java8_jars") {
+ _deps = []
+ foreach(_dep, invoker.deps) {
+ _dep_name = get_label_info(_dep, "name")
+ _source_jar =
+ get_label_info(_dep, "target_gen_dir") + "/" + _dep_name + ".javac.jar"
+ _output_jar = "$_package_dir/" + _dep_name + ".jar"
- deps = [
- ":cronet_api_java",
- ":cronet_impl_all_java",
- ":cronet_javatests",
- ":cronet_test_apk_java",
- "//base:base_java",
- ]
-}
+ # Adjust file names that are different from the target name that builds it.
+ if (_output_jar == "$_package_dir/" + "cronet_api_java.jar") {
+ _output_jar = "$_package_dir/" + "cronet_api.jar"
+ }
+ if (_output_jar == "$_package_dir/" + "cronet_impl_platform_base_java.jar") {
+ _output_jar = "$_package_dir/" + "cronet_impl_platform_java.jar"
+ }
-android_apk("cronet_perf_test_apk") {
- testonly = true
- apk_name = "CronetPerfTest"
- android_manifest = "test/javaperftests/AndroidManifest.xml"
- shared_libraries = [
- ":cronet",
- ":cronet_tests",
- ]
+ # _deps have targets which match the java target whitelist. Add a
+ # trailing X to avoid the copy() target matching the whitelist.
+ # See _java_target_whitelist in build/config/android/internal_rules.gni.
+ _copy_target_name = "${target_name}_${_dep_name}X"
+ copy(_copy_target_name) {
+ sources = [
+ _source_jar,
+ ]
+ outputs = [
+ _output_jar,
+ ]
+ deps = [
+ ":$_dep_name",
+ ]
+ }
- deps = [
- ":cronet_combine_proguard_flags",
- ":cronet_perf_test_apk_java",
- ":cronet_test_apk_java",
- "//base:base_java",
- ]
- deps += android_extra_test_deps
+ _deps += [ ":" + _copy_target_name ]
+ }
- proguard_enabled = true
- proguard_configs = [
- "$target_gen_dir/cronet_impl_native_proguard.cfg",
- "cronet_impl_common_proguard.cfg",
- "test/proguard.cfg",
- "//base/android/proguard/chromium_apk.flags",
- ]
+ group(target_name) {
+ deps = _deps
+ }
}
-test("cronet_unittests_android") {
- sources = [
- "//components/cronet/android/cert/cert_verifier_cache_serializer_unittest.cc",
- ]
-
+copy_java8_jars("copy_cronet_java8_jars") {
deps = [
- ":cronet_android_cert_proto",
- ":cronet_impl_native_base_java",
- ":cronet_static",
- "//base",
- "//base/test:test_support",
- "//components/cronet:cronet_common_unittests",
- "//components/cronet/native:cronet_native_unittests",
- "//components/cronet/native/test:cronet_native_tests",
- "//components/metrics",
- "//components/prefs:test_support",
- "//net",
- "//net:test_support",
- "//net/android:net_java",
- "//testing/gtest",
- ]
-
- data = [
- "//components/cronet/test/data/",
+ ":cronet_api_java",
+ ":cronet_impl_platform_base_java",
]
-
- if (is_android) {
- shard_timeout = 180
- }
}
-_package_dir = "$root_out_dir/cronet"
-_test_package_dir = "$root_out_dir/cronet/test"
_extract_cronet_native_jars_dir = "$target_gen_dir/cronet_native_jar_extract"
_extract_cronet_common_jars_dir = "$target_gen_dir/cronet_common_jar_extract"
-_extract_cronet_test_jars_dir = "$target_gen_dir/cronet_test_jar_extract"
+
+# List of patterns of .class files to exclude from the jar.
+jar_excluded_patterns = [
+ # Excludes Android support libraries crbug.com/832770.
+ "android/*",
+ "*/library_loader/*.class",
+ "*/multidex/*.class",
+ "*/process_launcher/*.class",
+ "*/SysUtils.class",
+ "*/CachedMetrics*.class",
+ "org/chromium/base/memory/MemoryPressureMonitor*.class",
+]
action("extract_cronet_native_jars") {
# extract_from_jars.py deletes the target directory before extracting.
@@ -960,17 +684,6 @@ action("repackage_extracted_native_jars") {
_output_jar,
]
- jar_excluded_patterns = [
- # Excludes Android support libraries crbug.com/832770.
- "android/*",
- "*/library_loader/*.class",
- "*/multidex/*.class",
- "*/process_launcher/*.class",
- "*/SysUtils.class",
- "*/CachedMetrics*.class",
- "org/chromium/base/memory/MemoryPressureMonitor*.class",
- ]
-
args = [
"--classes-dir",
rebase_path(_extract_cronet_native_jars_dir, root_build_dir),
@@ -1040,773 +753,1048 @@ action("repackage_extracted_common_jars") {
]
}
-cronet_test_deps = [ ":cronet_javatests" ]
-cronet_test_deps += cronet_javatests_deps_to_package
+if (!is_component_build) {
+ _cronet_shared_lib_file_name = "lib" + _cronet_shared_lib_name + ".so"
-action("extract_cronet_test_jars") {
- # extract_from_jars.py deletes the target directory before extracting.
- script = "//components/cronet/tools/extract_from_jars.py"
- depfile = "$target_gen_dir/$target_name.d"
- testonly = true
-
- sources = [
- NETTY4_JAR_FILE,
- ]
-
- # Extract pre-desugared jar for each cronet_test_deps.
- foreach(dep, cronet_test_deps) {
- sources += [ get_label_info(dep, "target_gen_dir") + "/" +
- get_label_info(dep, "name") + ".javac.jar" ]
+ # cronet_sample_test_apk_resources is identical to
+ # cronet_sample_apk_resources. The two have to be different targets because
+ # targets which are common between the "instrumentation test apk" and the
+ # "tested apk" are removed from the "instrumentation test apk".
+ android_resources("cronet_sample_test_apk_resources") {
+ resource_dirs = [ "sample/res" ]
+ android_manifest = "sample/javatests/AndroidManifest.xml"
}
- _stamp_file = "$target_gen_dir/$target_name.stamp"
- outputs = [
- _stamp_file,
- ]
-
- _rebased_sources = rebase_path(sources, root_build_dir)
-
- args = [
- "--classes-dir",
- rebase_path(_extract_cronet_test_jars_dir, root_build_dir),
- "--jars=${_rebased_sources}",
- "--depfile",
- rebase_path(depfile, root_build_dir),
- "--stamp",
- rebase_path(_stamp_file, root_build_dir),
- ]
+ instrumentation_test_apk("cronet_sample_test_apk") {
+ apk_name = "CronetSampleTest"
+ apk_under_test = ":cronet_sample_apk"
+ android_manifest = "sample/javatests/AndroidManifest.xml"
+ java_files = [ "sample/javatests/src/org/chromium/cronet_sample_apk/CronetSampleTest.java" ]
+ deps = [
+ "//third_party/android_support_test_runner:rules_java",
+ "//third_party/android_support_test_runner:runner_java",
+ "//third_party/junit",
+ ]
- deps = [
- "//third_party/netty4:netty_all_java",
- ]
- deps += cronet_test_deps
-}
+ proguard_enabled = true
+ proguard_configs = [ "sample/javatests/proguard.cfg" ]
+ }
-action("repackage_extracted_test_jars") {
- _output_jar = "$_test_package_dir/cronet_tests_java.jar"
- testonly = true
+ generate_jni("cronet_tests_jni_headers") {
+ testonly = true
+ sources = [
+ "test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java",
+ "test/javatests/src/org/chromium/net/CronetUrlRequestTest.java",
+ "test/javatests/src/org/chromium/net/ExperimentalOptionsTest.java",
+ "test/src/org/chromium/net/CronetTestUtil.java",
+ "test/src/org/chromium/net/MockCertVerifier.java",
+ "test/src/org/chromium/net/MockUrlRequestJobFactory.java",
+ "test/src/org/chromium/net/NativeTestServer.java",
+ "test/src/org/chromium/net/QuicTestServer.java",
+ "test/src/org/chromium/net/TestUploadDataStreamHandler.java",
+ ]
+ jni_package = "cronet_tests"
+ }
- script = "//build/android/gyp/jar.py"
- outputs = [
- _output_jar,
- ]
+ shared_library("cronet_tests") {
+ testonly = true
+ sources = [
+ # While "cronet_tests" cannot depend on "cronet_static", and hence cannot
+ # call any Cronet functions, it can access fields of Cronet objects, so add
+ # Cronet header files to facilitate accessing these fields.
+ "//components/cronet/android/cronet_url_request_adapter.h",
+ "//components/cronet/android/cronet_url_request_context_adapter.h",
+ "//components/cronet/cronet_url_request.h",
+ "//components/cronet/cronet_url_request_context.h",
+ "//components/cronet/url_request_context_config.h",
+ "test/cronet_test_jni.cc",
+ "test/cronet_test_util.cc",
+ "test/cronet_test_util.h",
+ "test/cronet_url_request_context_config_test.cc",
+ "test/cronet_url_request_context_config_test.h",
+ "test/cronet_url_request_test.cc",
+ "test/experimental_options_test.cc",
+ "test/mock_cert_verifier.cc",
+ "test/mock_url_request_job_factory.cc",
+ "test/native_test_server.cc",
+ "test/quic_test_server.cc",
+ "test/test_upload_data_stream_handler.cc",
+ "test/test_upload_data_stream_handler.h",
+ ]
- args = [
- "--classes-dir",
- rebase_path(_extract_cronet_test_jars_dir, root_build_dir),
- "--jar-path",
- rebase_path(_output_jar, root_build_dir),
- ]
+ deps = [
+ ":cronet",
+ ":cronet_tests_jni_headers",
+ "//base",
+ "//base:i18n",
+ "//base/test:test_support",
+ "//components/cronet:cronet_version_header",
+ "//components/cronet/test:test_support",
+ "//components/prefs",
+ "//net",
+ "//net:simple_quic_tools",
+ "//net:test_support",
+ "//third_party/icu",
+ ]
- deps = [
- ":extract_cronet_test_jars",
- ]
-}
+ include_dirs = [ _cronet_version_header_include_dir ]
-template("jar_src") {
- action(target_name) {
- _rebased_src_search_dirs =
- rebase_path(invoker.src_search_dirs, root_build_dir)
+ configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
+ configs += [ "//build/config/android:hide_all_but_jni" ]
+ }
- script = "//components/cronet/tools/jar_src.py"
- depfile = "$target_gen_dir/$target_name.d"
- outputs = [
- invoker.jar_path,
- ]
- args = [
- "--src-search-dirs=${_rebased_src_search_dirs}",
- "--jar-path",
- rebase_path(invoker.jar_path, root_build_dir),
- "--depfile",
- rebase_path(depfile, root_build_dir),
+ android_resources("cronet_test_apk_resources") {
+ testonly = true
+ resource_dirs = [
+ "test/res",
+ "test/smoketests/res/native",
]
+ android_manifest = "test/AndroidManifest.xml"
+ }
- deps = []
- if (defined(invoker.deps)) {
- deps += invoker.deps
- }
+ android_library("cronet_test_apk_java") {
+ testonly = true
- _src_jars = []
+ java_files = [
+ "test/src/org/chromium/net/CronetTestApplication.java",
+ "test/src/org/chromium/net/CronetTestUtil.java",
+ "test/src/org/chromium/net/Http2TestHandler.java",
+ "test/src/org/chromium/net/Http2TestServer.java",
+ "test/src/org/chromium/net/MockCertVerifier.java",
+ "test/src/org/chromium/net/MockUrlRequestJobFactory.java",
+ "test/src/org/chromium/net/NativeTestServer.java",
+ "test/src/org/chromium/net/QuicTestServer.java",
+ "test/src/org/chromium/net/TestFilesInstaller.java",
+ "test/src/org/chromium/net/TestUploadDataStreamHandler.java",
+ ]
- # Add src-jar files that are listed in "src_jars".
- if (defined(invoker.src_jars)) {
- _rebased_src_jars = rebase_path(invoker.src_jars, root_build_dir)
- _src_jars += _rebased_src_jars
- }
+ deps = [
+ ":cronet_api_java",
+ ":cronet_impl_all_java",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//net/android:net_java_test_support",
+ "//third_party/junit",
+ "//third_party/netty4:netty_all_java",
+ ]
+ }
- # Add src-jar files that are generated by dependencies in "srcjar_deps".
- if (defined(invoker.srcjar_deps)) {
- foreach(_srcjar_dep, invoker.srcjar_deps) {
- _dep_gen_dir = get_label_info(_srcjar_dep, "target_gen_dir")
- _dep_name = get_label_info(_srcjar_dep, "name")
- _src_jars += rebase_path([ "$_dep_gen_dir/$_dep_name.srcjar" ])
- deps += [ _srcjar_dep ]
- }
- }
+ cronet_smoketests_platform_only_common_srcs = [
+ "test/smoketests/src/org/chromium/net/smoke/ChromiumPlatformOnlyTestSupport.java",
+ "test/smoketests/src/org/chromium/net/smoke/CronetSmokeTestRule.java",
+ "test/smoketests/src/org/chromium/net/smoke/HttpTestServer.java",
+ "test/smoketests/src/org/chromium/net/smoke/SmokeTestRequestCallback.java",
+ "test/smoketests/src/org/chromium/net/smoke/TestSupport.java",
+ ]
- # Create the list of all source files that are given in "src_files".
- _src_files = []
- if (defined(invoker.src_files)) {
- _src_files += invoker.src_files
- }
+ cronet_smoketests_native_common_srcs = cronet_smoketests_platform_only_common_srcs + [
+ "test/smoketests/src/org/chromium/net/smoke/ChromiumNativeTestSupport.java",
+ "test/smoketests/src/org/chromium/net/smoke/NativeCronetTestRule.java",
+ ]
- # Handle "source_deps".
- _src_list_files = []
- if (defined(invoker.source_deps)) {
- foreach(_source_dep, invoker.source_deps) {
- _dep_gen_dir = get_label_info(_source_dep, "target_gen_dir")
- _dep_name = get_label_info(_source_dep, "name")
- _src_list_files += rebase_path([ "$_dep_gen_dir/$_dep_name.sources" ])
- deps += [ _source_dep ]
- }
- }
- args += [ "--src-jar=${_src_jars}" ]
- args += [ "--src-files=${_src_files}" ]
- args += [ "--src-list-files=${_src_list_files}" ]
+ android_library("cronet_smoketests_native_java") {
+ testonly = true
+ java_files = [
+ "test/smoketests/src/org/chromium/net/smoke/Http2Test.java",
+ "test/smoketests/src/org/chromium/net/smoke/QuicTest.java",
+ ] + cronet_smoketests_native_common_srcs
- inputs = _src_jars
- inputs += _src_files
- inputs += _src_list_files
+ deps = [
+ ":cronet_api_java",
+ ":cronet_test_apk_java",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//third_party/android_support_test_runner:runner_java",
+ "//third_party/junit",
+ "//third_party/netty4:netty_all_java",
+ ]
}
-}
-jar_src("jar_cronet_api_source") {
- src_search_dirs = [ "api/src" ]
- source_deps = [ ":cronet_api_java" ]
- srcjar_deps = cronet_api_srcjar_deps
- jar_path = "$_package_dir/cronet_api-src.jar"
-}
+ android_apk("cronet_test_apk") {
+ testonly = true
+ apk_name = "CronetTest"
+ android_manifest = "test/AndroidManifest.xml"
+ shared_libraries = [
+ ":cronet",
+ ":cronet_tests",
+ ]
+ loadable_modules = [ "$root_out_dir/libnetty-tcnative.so" ]
-jar_src("jar_cronet_impl_common_java_source") {
- src_search_dirs = [ "java/src" ]
- source_deps = [ ":cronet_impl_common_base_java" ]
- srcjar_deps = cronet_impl_common_java_srcjar_deps
- jar_path = "$_package_dir/cronet_impl_common_java-src.jar"
-}
+ deps = [
+ ":cronet_combine_proguard_flags",
+ ":cronet_test_apk_resources",
+ "//base:base_java",
+ "//third_party/netty-tcnative:netty-tcnative-so",
+ ]
-jar_src("jar_cronet_impl_platform_java_source") {
- src_search_dirs = [ "java/src" ]
- source_deps = [ ":cronet_impl_platform_base_java" ]
- jar_path = "$_package_dir/cronet_impl_platform_java-src.jar"
-}
+ proguard_enabled = true
-zip("jar_cronet_sample_source") {
- inputs = [
- "sample/AndroidManifest.xml",
- "sample/javatests/AndroidManifest.xml",
- "sample/javatests/proguard.cfg",
- "sample/javatests/src/org/chromium/cronet_sample_apk/CronetSampleTest.java",
- "sample/README",
- "sample/res/layout/activity_main.xml",
- "sample/res/layout/dialog_url.xml",
- "sample/res/values/dimens.xml",
- "sample/res/values/strings.xml",
- "sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java",
- "sample/src/org/chromium/cronet_sample_apk/CronetSampleApplication.java",
- ]
- output = "$_package_dir/cronet-sample-src.jar"
- base_dir = "sample"
-}
+ proguard_configs = [
+ "$target_gen_dir/cronet_impl_native_proguard.cfg",
+ "cronet_impl_common_proguard.cfg",
+ "cronet_impl_platform_proguard.cfg",
+ ]
+ }
-jar_src("jar_cronet_impl_native_java_source") {
- src_search_dirs = [
- "//base/android/java/src",
- "//components/cronet/android/java/src",
- "//net/android/java/src",
- "//url/android/java/src",
- ]
- source_deps = [
- ":cronet_impl_native_base_java",
+ cronet_javatests_deps_to_package = [
+ ":cronet_test_apk_java",
"//base:base_java",
+ "//base:base_java_test_support",
+ "//net/android:embedded_test_server_aidl_java",
"//net/android:net_java",
+ "//net/android:net_java_test_support",
"//url:url_java",
]
- srcjar_deps = cronet_impl_native_java_srcjar_deps + [
- "//base:base_android_java_enums_srcjar",
- "//net/android:net_android_java_enums_srcjar",
- "//net/android:net_errors_java",
- ]
- jar_path = "$_package_dir/cronet_impl_native_java-src.jar"
-}
-action("generate_licenses") {
- _license_path = "$_package_dir/LICENSE"
+ android_library("cronet_javatests") {
+ testonly = true
- script = "//tools/licenses.py"
- outputs = [
- _license_path,
- ]
- args = [
- "license_file",
- rebase_path(_license_path, root_build_dir),
- "--gn-target",
- "//components/cronet/android:cronet",
- "--gn-out-dir",
- ".",
- ]
-}
+ java_files = [
+ "test/javatests/src/org/chromium/net/BidirectionalStreamQuicTest.java",
+ "test/javatests/src/org/chromium/net/BidirectionalStreamTest.java",
+ "test/javatests/src/org/chromium/net/BrotliTest.java",
+ "test/javatests/src/org/chromium/net/Criteria.java",
+ "test/javatests/src/org/chromium/net/CronetEngineBuilderTest.java",
+ "test/javatests/src/org/chromium/net/CronetStressTest.java",
+ "test/javatests/src/org/chromium/net/CronetTestRule.java",
+ "test/javatests/src/org/chromium/net/CronetTestRuleTest.java",
+ "test/javatests/src/org/chromium/net/CronetUploadTest.java",
+ "test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java",
+ "test/javatests/src/org/chromium/net/CronetUrlRequestTest.java",
+ "test/javatests/src/org/chromium/net/DiskStorageTest.java",
+ "test/javatests/src/org/chromium/net/ExperimentalOptionsTest.java",
+ "test/javatests/src/org/chromium/net/GetStatusTest.java",
+ "test/javatests/src/org/chromium/net/MetricsTestUtil.java",
+ "test/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java",
+ "test/javatests/src/org/chromium/net/NQETest.java",
+ "test/javatests/src/org/chromium/net/PkpTest.java",
+ "test/javatests/src/org/chromium/net/QuicTest.java",
+ "test/javatests/src/org/chromium/net/RequestFinishedInfoTest.java",
+ "test/javatests/src/org/chromium/net/TestBidirectionalStreamCallback.java",
+ "test/javatests/src/org/chromium/net/TestDrivenDataProvider.java",
+ "test/javatests/src/org/chromium/net/TestNetworkQualityRttListener.java",
+ "test/javatests/src/org/chromium/net/TestNetworkQualityThroughputListener.java",
+ "test/javatests/src/org/chromium/net/TestUploadDataProvider.java",
+ "test/javatests/src/org/chromium/net/TestUrlRequestCallback.java",
+ "test/javatests/src/org/chromium/net/UploadDataProvidersTest.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",
+ "test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLConnectionTest.java",
+ "test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLStreamHandlerTest.java",
+ "test/javatests/src/org/chromium/net/urlconnection/CronetURLStreamHandlerFactoryTest.java",
+ "test/javatests/src/org/chromium/net/urlconnection/MessageLoopTest.java",
+ "test/javatests/src/org/chromium/net/urlconnection/QuicUploadTest.java",
+ "test/javatests/src/org/chromium/net/urlconnection/TestUtil.java",
+ "test/javatests/src/org/chromium/net/UrlResponseInfoTest.java",
+ ]
-action("generate_javadoc") {
- script = "//components/cronet/tools/generate_javadoc.py"
- depfile = "$target_gen_dir/$target_name.d"
- _zip_file = "$target_gen_dir/$target_name.zip"
- outputs = [
- _zip_file,
- ]
- args = [
- "--output-dir",
- rebase_path(_package_dir, root_build_dir),
- "--input-dir",
- rebase_path("//components/cronet", root_build_dir),
- "--overview-file",
- rebase_path("$_package_dir/README.md.html", root_build_dir),
- "--readme-file",
- rebase_path("//components/cronet/README.md", root_build_dir),
- "--depfile",
- rebase_path(depfile, root_build_dir),
- "--zip-file",
- rebase_path(_zip_file, root_build_dir),
+ # Adding deps here won't include those deps in the cronet_tests_java.jar.
+ # Please add to cronet_javatests_deps_to_package instead.
+ deps = [
+ ":cronet_api_java",
+ ":cronet_impl_all_java",
+ "//third_party/android_support_test_runner:runner_java",
+ "//third_party/junit",
+ ]
+ deps += cronet_javatests_deps_to_package
+ data = [
+ "//components/cronet/test/data/",
+ ]
+ }
- "--android-sdk-jar",
- rebase_path(android_sdk_jar, root_build_dir),
+ instrumentation_test_apk("cronet_test_instrumentation_apk") {
+ apk_name = "CronetTestInstrumentation"
+ apk_under_test = ":cronet_test_apk"
+ android_manifest = "test/javatests/AndroidManifest.xml"
- # JavaDoc is generated from Cronet's API source jar.
- "--input-src-jar",
- rebase_path("$_package_dir/cronet_api-src.jar", root_build_dir),
- ]
- deps = [
- ":jar_cronet_api_source",
- ]
-}
+ deps = [
+ ":cronet_api_java",
+ ":cronet_impl_all_java",
+ ":cronet_javatests",
+ ":cronet_smoketests_native_java",
+ ":cronet_test_apk_java",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//net/android:net_java",
+ "//net/android:net_java_test_support",
+ "//third_party/android_support_test_runner:runner_java",
+ "//third_party/android_tools:android_test_mock_java",
+ "//third_party/hamcrest:hamcrest_core_java",
+ ]
+ additional_apks = [ "//net/android:net_test_support_apk" ]
-copy("cronet_package_copy") {
- sources = [
- "$target_gen_dir/cronet_impl_native_proguard.cfg",
- "//AUTHORS",
- "//chrome/VERSION",
- "api_version.txt",
- "cronet_impl_common_proguard.cfg",
- "cronet_impl_platform_proguard.cfg",
- ]
- outputs = [
- "$_package_dir/{{source_file_part}}",
- ]
+ data_deps = [
+ "//net:test_support",
+ ]
- deps = [
- ":cronet_api_java",
- ":cronet_combine_proguard_flags",
- ":cronet_impl_common_base_java",
- ":cronet_impl_platform_base_java",
- ]
-}
+ proguard_enabled = true
-template("copy_java8_jars") {
- _deps = []
- foreach(_dep, invoker.deps) {
- _dep_name = get_label_info(_dep, "name")
- _source_jar =
- get_label_info(_dep, "target_gen_dir") + "/" + _dep_name + ".javac.jar"
- _output_jar = "$_package_dir/" + _dep_name + ".jar"
+ proguard_configs = [ "test/proguard.cfg" ]
+ }
- # Adjust file names that are different from the target name that builds it.
- if (_output_jar == "$_package_dir/" + "cronet_api_java.jar") {
- _output_jar = "$_package_dir/" + "cronet_api.jar"
- }
- if (_output_jar == "$_package_dir/" + "cronet_impl_platform_base_java.jar") {
- _output_jar = "$_package_dir/" + "cronet_impl_platform_java.jar"
- }
+ android_resources("cronet_smoketests_platform_only_apk_resources") {
+ testonly = true
+ resource_dirs = [ "test/smoketests/res/platform_only" ]
+ android_manifest = "test/AndroidManifest.xml"
+ }
- # _deps have targets which match the java target whitelist. Add a
- # trailing X to avoid the copy() target matching the whitelist.
- # See _java_target_whitelist in build/config/android/internal_rules.gni.
- _copy_target_name = "${target_name}_${_dep_name}X"
- copy(_copy_target_name) {
- sources = [
- _source_jar,
- ]
- outputs = [
- _output_jar,
- ]
- deps = [
- ":$_dep_name",
- ]
- }
+ android_library("cronet_smoketests_platform_only_java") {
+ testonly = true
+ java_files = [ "test/smoketests/src/org/chromium/net/smoke/PlatformOnlyEngineTest.java" ] + cronet_smoketests_platform_only_common_srcs
+ deps = [
+ ":cronet_api_java",
+ "//base:base_java_test_support",
+ "//third_party/android_support_test_runner:runner_java",
+ "//third_party/junit",
+ "//third_party/netty4:netty_all_java",
+ ]
+ }
- _deps += [ ":" + _copy_target_name ]
+ android_apk("cronet_smoketests_platform_only_apk") {
+ testonly = true
+ apk_name = "PlatformOnlyEngineSmokeTest"
+ android_manifest = "test/AndroidManifest.xml"
+ java_files = [ "test/src/org/chromium/net/CronetTestApplication.java" ]
+
+ proguard_enabled = true
+ proguard_configs = [
+ "cronet_impl_common_proguard.cfg",
+ "cronet_impl_platform_proguard.cfg",
+ ]
+
+ deps = [
+ ":cronet_api_java",
+ ":cronet_impl_common_base_java",
+ ":cronet_impl_platform_base_java",
+ ":cronet_smoketests_platform_only_apk_resources",
+ ]
}
- group(target_name) {
- deps = _deps
+ instrumentation_test_apk(
+ "cronet_smoketests_platform_only_instrumentation_apk") {
+ apk_name = "PlatformOnlyEngineSmokeTestInstrumentation"
+ apk_under_test = ":cronet_smoketests_platform_only_apk"
+ android_manifest = "test/javatests/AndroidManifest.xml"
+ deps = [
+ ":cronet_smoketests_platform_only_java",
+ "//base:base_java_test_support",
+ "//third_party/android_support_test_runner:runner_java",
+ "//third_party/android_tools:android_test_mock_java",
+ ]
+
+ proguard_enabled = true
+
+ proguard_configs = [ "test/proguard.cfg" ]
}
-}
-copy_java8_jars("copy_cronet_java8_jars") {
- deps = [
- ":cronet_api_java",
- ":cronet_impl_platform_base_java",
- ]
-}
+ android_library("cronet_smoketests_missing_native_library_java") {
+ testonly = true
+ java_files = [ "test/smoketests/src/org/chromium/net/smoke/MissingNativeLibraryTest.java" ] + cronet_smoketests_native_common_srcs
+ deps = [
+ ":cronet_api_java",
+ ":cronet_test_apk_java",
+ "//base:base_java",
+ "//base:base_java_test_support",
+ "//third_party/android_support_test_runner:runner_java",
+ "//third_party/junit",
+ "//third_party/netty4:netty_all_java",
+ ]
+ }
-action("cronet_combine_proguard_flags") {
- script = "//components/cronet/tools/generate_proguard_file.py"
- sources = [
- "//base/android/proguard/chromium_code.flags",
- "//components/cronet/android/cronet_impl_native_proguard.cfg",
- ]
- outputs = [
- "$target_gen_dir/cronet_impl_native_proguard.cfg",
- ]
- args = [ "--output-file" ] + rebase_path(outputs, root_build_dir) +
- rebase_path(sources, root_build_dir)
-}
+ android_apk("cronet_smoketests_missing_native_library_apk") {
+ testonly = true
+ apk_name = "MissingNativeLibrarySmokeTest"
+ android_manifest = "test/AndroidManifest.xml"
+ deps = [
+ ":cronet_api_java",
+ ":cronet_combine_proguard_flags",
+ ":cronet_impl_common_base_java",
+ ":cronet_impl_platform_base_java",
+ ":cronet_test_apk_resources",
+ ]
-copy("cronet_package_copy_native_lib") {
- sources = [
- "$root_out_dir/" + _cronet_shared_lib_file_name,
- ]
- outputs = [
- "$_package_dir/libs/${android_app_abi}/" + _cronet_shared_lib_file_name,
- ]
- deps = [
- ":cronet",
- ]
-}
+ proguard_enabled = true
+ proguard_configs = [
+ "$target_gen_dir/cronet_impl_native_proguard.cfg",
+ "cronet_impl_common_proguard.cfg",
+ "cronet_impl_platform_proguard.cfg",
+ ]
+ }
-copy("cronet_package_copy_native_lib_unstripped") {
- sources = [
- "$root_out_dir/lib.unstripped/" + _cronet_shared_lib_file_name,
- ]
- outputs = [
- "$_package_dir/symbols/${android_app_abi}/" + _cronet_shared_lib_file_name,
- ]
- deps = [
- ":cronet",
- ]
-}
+ instrumentation_test_apk(
+ "cronet_smoketests_missing_native_library_instrumentation_apk") {
+ apk_name = "MissingNativeLibrarySmokeTestInstrumentation"
+ apk_under_test = ":cronet_smoketests_missing_native_library_apk"
+ android_manifest = "test/javatests/AndroidManifest.xml"
-copy("cronet_package_copy_native_test_lib") {
- testonly = true
- sources = [
- "$root_out_dir/libcronet_tests.so",
- "$root_out_dir/libnetty-tcnative.so",
- ]
- outputs = [
- "$_test_package_dir/libs/${android_app_abi}/{{source_file_part}}",
- ]
- deps = [
- ":cronet_tests",
- "//third_party/netty-tcnative:netty-tcnative-so",
- ]
-}
+ deps = [
+ ":cronet_smoketests_missing_native_library_java",
+ "//third_party/android_tools:android_test_mock_java",
+ ]
-copy("cronet_package_copy_native_test_lib_unstripped") {
- testonly = true
- sources = [
- "$root_out_dir/lib.unstripped/libcronet_tests.so",
- "$root_out_dir/lib.unstripped/libnetty-tcnative.so",
- ]
- outputs = [
- "$_test_package_dir/symbols/${android_app_abi}/{{source_file_part}}",
- ]
- deps = [
- ":cronet_tests",
- "//third_party/netty-tcnative:netty-tcnative-so",
- ]
-}
+ proguard_enabled = true
-copy("cronet_package_copy_test_assets") {
- testonly = true
- sources = [
- "//components/cronet/test/data",
- ]
- outputs = [
- "$_test_package_dir/assets/test",
- ]
-}
+ proguard_configs = [ "test/proguard.cfg" ]
+ }
-copy("cronet_package_copy_test_support_apks") {
- testonly = true
- sources = [
- # Provides EmbeddedTestServer.
- "$root_out_dir/apks/ChromiumNetTestSupport.apk",
- ]
- outputs = [
- "$_test_package_dir/apks/${android_app_abi}/{{source_file_part}}",
- ]
- deps = [
- "//net/android:net_test_support_apk",
- ]
-}
+ android_library("cronet_perf_test_apk_java") {
+ testonly = true
+ android_manifest_for_lint = "test/javaperftests/AndroidManifest.xml"
+ java_files = [
+ "test/javaperftests/src/org/chromium/net/CronetPerfTestActivity.java",
+ ]
-copy("cronet_package_copy_test_files") {
- testonly = true
- sources = [
- "//net/data/ssl/certificates/quic-chain.pem",
- "//net/data/ssl/certificates/quic-leaf-cert.key",
- "//net/data/ssl/certificates/quic-leaf-cert.key.pkcs8.pem",
- ]
- outputs = [
- "$_test_package_dir/assets/test_files/net/data/ssl/certificates/{{source_file_part}}",
- ]
- deps = [
- # Not really dependent, but builds can fail if these two targets attempt
- # to create the "assets" subdirectory simultaneously.
- ":cronet_package_copy_test_assets",
- ]
-}
+ deps = [
+ ":cronet_api_java",
+ ":cronet_impl_all_java",
+ ":cronet_javatests",
+ ":cronet_test_apk_java",
+ "//base:base_java",
+ ]
+ }
-copy("cronet_package_copy_resources") {
- sources = [
- "api/res/raw/keep_cronet_api.xml",
- ]
- outputs = [
- "$_package_dir/res/raw/{{source_file_part}}",
- ]
-}
+ android_apk("cronet_perf_test_apk") {
+ testonly = true
+ apk_name = "CronetPerfTest"
+ android_manifest = "test/javaperftests/AndroidManifest.xml"
+ shared_libraries = [
+ ":cronet",
+ ":cronet_tests",
+ ]
-# These package_* targets represent how Cronet is used in production code.
-# Using these targets is preferred to using the underlying targets like
-# :cronet_api_java or :cronet_impl_all_java, as it better tests production
-# usage.
-android_java_prebuilt("package_api_java") {
- jar_path = "$_package_dir/cronet_api.jar"
- deps = [
- ":copy_cronet_java8_jars_cronet_api_javaX",
- ]
-}
+ deps = [
+ ":cronet_combine_proguard_flags",
+ ":cronet_perf_test_apk_java",
+ ":cronet_test_apk_java",
+ "//base:base_java",
+ "//third_party/android_tools:android_test_mock_java",
+ ]
-android_java_prebuilt("package_impl_common_java") {
- jar_path = "$_package_dir/cronet_impl_common_java.jar"
- deps = [
- ":package_api_java",
- ":repackage_extracted_common_jars",
- ]
-}
+ proguard_enabled = true
+ proguard_configs = [
+ "$target_gen_dir/cronet_impl_native_proguard.cfg",
+ "cronet_impl_common_proguard.cfg",
+ "test/proguard.cfg",
+ "//base/android/proguard/chromium_apk.flags",
+ ]
+ }
-android_java_prebuilt("package_impl_native_java") {
- jar_path = "$_package_dir/cronet_impl_native_java.jar"
- deps = [
- ":package_api_java",
- ":package_impl_common_java",
- ":repackage_extracted_native_jars",
- "//third_party/jsr-305:jsr_305_javalib",
- ]
-}
+ test("cronet_unittests_android") {
+ deps = [
+ ":cronet_impl_native_base_java",
+ ":cronet_static",
+ "//base",
+ "//base/test:test_support",
+ "//components/cronet:cronet_common_unittests",
+ "//components/cronet/native:cronet_native_unittests",
+ "//components/cronet/native/test:cronet_native_tests",
+ "//components/metrics",
+ "//components/prefs:test_support",
+ "//net",
+ "//net:test_support",
+ "//net/android:net_java",
+ "//testing/gtest",
+ ]
-android_java_prebuilt("package_impl_platform_java") {
- jar_path = "$_package_dir/cronet_impl_platform_java.jar"
- deps = [
- ":copy_cronet_java8_jars_cronet_impl_platform_base_javaX",
- ":package_api_java",
- ":package_impl_common_java",
- ]
-}
+ data = [
+ "//components/cronet/test/data/",
+ ]
+
+ if (is_android) {
+ shard_timeout = 180
+ }
+ }
-# Enforce that ARM Neon is not used when building for ARMv7
-if (target_cpu == "arm" && arm_version == 7 && !arm_use_neon) {
- action("enforce_no_neon") {
- script = "//components/cronet/tools/check_no_neon.py"
+ _test_package_dir = "$root_out_dir/cronet/test"
+ _extract_cronet_test_jars_dir = "$target_gen_dir/cronet_test_jar_extract"
+
+ cronet_test_deps = [ ":cronet_javatests" ]
+ cronet_test_deps += cronet_javatests_deps_to_package
+
+ action("extract_cronet_test_jars") {
+ # extract_from_jars.py deletes the target directory before extracting.
+ script = "//components/cronet/tools/extract_from_jars.py"
+ depfile = "$target_gen_dir/$target_name.d"
+ testonly = true
+
+ sources = [
+ NETTY4_JAR_FILE,
+ ]
+
+ # Extract pre-desugared jar for each cronet_test_deps.
+ foreach(dep, cronet_test_deps) {
+ sources += [ get_label_info(dep, "target_gen_dir") + "/" +
+ get_label_info(dep, "name") + ".javac.jar" ]
+ }
+
+ _stamp_file = "$target_gen_dir/$target_name.stamp"
outputs = [
- "$target_gen_dir/$target_name.stamp",
+ _stamp_file,
]
- args = [
- rebase_path("${android_tool_prefix}objdump", root_build_dir),
- # libcronet.so may contain ARM Neon instructions from BoringSSL, but these
- # are only used after checking whether the CPU supports NEON at runtime,
- # so instead check base/ as it represents a large swath of code that only
- # contains Neon instructions when Neon is enabled by default.
- rebase_path("$root_out_dir/obj/base/base/*.o", root_build_dir),
+ _rebased_sources = rebase_path(sources, root_build_dir)
+
+ args = [
+ "--classes-dir",
+ rebase_path(_extract_cronet_test_jars_dir, root_build_dir),
+ "--jars=${_rebased_sources}",
+ "--depfile",
+ rebase_path(depfile, root_build_dir),
"--stamp",
- rebase_path(outputs[0], root_build_dir),
+ rebase_path(_stamp_file, root_build_dir),
]
+
deps = [
- "//base:base",
+ "//third_party/netty4:netty_all_java",
]
+ deps += cronet_test_deps
}
-}
-# Enforce restrictions for API<->impl boundary.
-action("api_static_checks") {
- script = "//components/cronet/tools/api_static_checks.py"
- outputs = [
- "$target_gen_dir/$target_name.stamp",
- ]
- args = [
- "--api_jar",
- rebase_path(
- "$root_out_dir/lib.java/components/cronet/android/cronet_api_java.jar",
- root_build_dir),
- "--impl_jar",
- rebase_path(
- "$root_out_dir/lib.java/components/cronet/android/cronet_impl_common_base_java.jar",
- root_build_dir),
- "--impl_jar",
- rebase_path(
- "$root_out_dir/lib.java/components/cronet/android/cronet_impl_platform_base_java.jar",
- root_build_dir),
- "--impl_jar",
- rebase_path(
- "$root_out_dir/lib.java/components/cronet/android/cronet_impl_native_base_java.jar",
- root_build_dir),
- "--stamp",
- rebase_path(outputs[0], root_build_dir),
- ]
- deps = [
- ":cronet_api_java",
- ":cronet_impl_common_base_java",
- ":cronet_impl_native_base_java",
- ":cronet_impl_platform_base_java",
- ]
- inputs = [
- "//components/cronet/tools/update_api.py",
- ]
- sources = [
- "//components/cronet/android/api.txt",
- "//components/cronet/android/api_version.txt",
- ]
-}
+ action("repackage_extracted_test_jars") {
+ _output_jar = "$_test_package_dir/cronet_tests_java.jar"
+ testonly = true
-group("cronet_package") {
- # Marked as testonly as it contains test-only targets too.
- testonly = true
+ script = "//build/android/gyp/jar.py"
+ outputs = [
+ _output_jar,
+ ]
+
+ args = [
+ "--classes-dir",
+ rebase_path(_extract_cronet_test_jars_dir, root_build_dir),
+ "--jar-path",
+ rebase_path(_output_jar, root_build_dir),
+ ]
- # Enforce building with ICU alternatives, crbug.com/611621.
- # Enforce that arm_use_neon==false when building for ARMv7 by
- # not including any deps in cronet_package target otherwise.
- if (use_platform_icu_alternatives &&
- (!(target_cpu == "arm" && arm_version == 7) || !arm_use_neon)) {
deps = [
- ":api_static_checks",
- ":copy_cronet_java8_jars",
- ":cronet_package_copy",
- ":cronet_package_copy_native_lib",
- ":cronet_package_copy_native_lib_unstripped",
- ":cronet_package_copy_resources",
- ":cronet_test_package",
- ":generate_javadoc",
- ":generate_licenses",
- ":jar_cronet_api_source",
- ":jar_cronet_impl_common_java_source",
- ":jar_cronet_impl_native_java_source",
- ":jar_cronet_impl_platform_java_source",
- ":jar_cronet_sample_source",
- ":repackage_extracted_common_jars",
- ":repackage_extracted_native_jars",
- ]
- if (current_cpu == "arm" && arm_version == 7) {
- deps += [ ":enforce_no_neon" ]
- }
+ ":extract_cronet_test_jars",
+ ]
}
-}
-group("cronet_test_package") {
- testonly = true
+ zip("jar_cronet_sample_source") {
+ inputs = [
+ "sample/AndroidManifest.xml",
+ "sample/javatests/AndroidManifest.xml",
+ "sample/javatests/proguard.cfg",
+ "sample/javatests/src/org/chromium/cronet_sample_apk/CronetSampleTest.java",
+ "sample/README",
+ "sample/res/layout/activity_main.xml",
+ "sample/res/layout/dialog_url.xml",
+ "sample/res/values/dimens.xml",
+ "sample/res/values/strings.xml",
+ "sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java",
+ "sample/src/org/chromium/cronet_sample_apk/CronetSampleApplication.java",
+ ]
+ output = "$_package_dir/cronet-sample-src.jar"
+ base_dir = "sample"
+ }
- # Don't build for MIPS where tests aren't run.
- if (current_cpu != "mipsel" && current_cpu != "mips64el") {
+ jar_src("jar_cronet_impl_native_java_source") {
+ src_search_dirs = [
+ "//base/android/java/src",
+ "//components/cronet/android/java/src",
+ "//net/android/java/src",
+ "//url/android/java/src",
+ "//third_party/android_async_task/java/src",
+ ]
+ source_deps = [
+ ":cronet_impl_native_base_java",
+ "//base:base_java",
+ "//net/android:net_java",
+ "//url:url_java",
+ ]
+ srcjar_deps = cronet_impl_native_java_srcjar_deps + [
+ "//base:base_android_java_enums_srcjar",
+ "//net/android:net_android_java_enums_srcjar",
+ "//net/android:net_errors_java",
+ ]
+ excluded_patterns = jar_excluded_patterns
+ jar_path = "$_package_dir/cronet_impl_native_java-src.jar"
+ }
+
+ action("generate_licenses") {
+ _license_path = "$_package_dir/LICENSE"
+
+ script = "//tools/licenses.py"
+ outputs = [
+ _license_path,
+ ]
+ args = [
+ "license_file",
+ rebase_path(_license_path, root_build_dir),
+ "--gn-target",
+ "//components/cronet/android:cronet",
+ "--gn-out-dir",
+ ".",
+ ]
+ }
+
+ action("generate_javadoc") {
+ script = "//components/cronet/tools/generate_javadoc.py"
+ depfile = "$target_gen_dir/$target_name.d"
+ _zip_file = "$target_gen_dir/$target_name.zip"
+ outputs = [
+ _zip_file,
+ ]
+ args = [
+ "--output-dir",
+ rebase_path(_package_dir, root_build_dir),
+ "--input-dir",
+ rebase_path("//components/cronet", root_build_dir),
+ "--overview-file",
+ rebase_path("$_package_dir/README.md.html", root_build_dir),
+ "--readme-file",
+ rebase_path("//components/cronet/README.md", root_build_dir),
+ "--depfile",
+ rebase_path(depfile, root_build_dir),
+ "--zip-file",
+ rebase_path(_zip_file, root_build_dir),
+
+ "--android-sdk-jar",
+ rebase_path(android_sdk_jar, root_build_dir),
+
+ # JavaDoc is generated from Cronet's API source jar.
+ "--input-src-jar",
+ rebase_path("$_package_dir/cronet_api-src.jar", root_build_dir),
+ ]
deps = [
- ":cronet_package_copy_native_test_lib",
- ":cronet_package_copy_native_test_lib_unstripped",
- ":cronet_package_copy_test_assets",
- ":cronet_package_copy_test_files",
- ":cronet_package_copy_test_support_apks",
- ":repackage_extracted_test_jars",
+ ":jar_cronet_api_source",
]
}
-}
-_maven_dir = "$_package_dir/maven-$current_cpu"
-_maven_modules_dir = "$_maven_dir/org/chromium/net"
-_maven_test_dir = "$_maven_dir/test"
-_maven_version =
- "$chrome_version_major.$chrome_version_build.$chrome_version_patch"
+ copy("cronet_package_copy") {
+ sources = [
+ "$target_gen_dir/cronet_impl_native_proguard.cfg",
+ "//AUTHORS",
+ "//chrome/VERSION",
+ "api_version.txt",
+ "cronet_impl_common_proguard.cfg",
+ "cronet_impl_platform_proguard.cfg",
+ ]
+ outputs = [
+ "$_package_dir/{{source_file_part}}",
+ ]
-copy("cronet_maven_test_copy") {
- sources = [
- "maven/build.gradle",
- "maven/local.properties",
- "maven/settings.gradle",
- ]
- outputs = [
- "$_maven_test_dir/{{source_file_part}}",
- ]
-}
+ deps = [
+ ":cronet_api_java",
+ ":cronet_combine_proguard_flags",
+ ":cronet_impl_common_base_java",
+ ":cronet_impl_platform_base_java",
+ ]
+ }
-process_version("cronet_maven_build_embedded_copy") {
- template_file = "maven/test/build_embedded.gradle.template"
- sources = [
- "//build/util/LASTCHANGE",
- "//chrome/VERSION",
- ]
- output = "$_maven_test_dir/test_embedded/build.gradle"
-}
+ copy("cronet_package_copy_native_lib") {
+ sources = [
+ "$root_out_dir/" + _cronet_shared_lib_file_name,
+ ]
+ outputs = [
+ "$_package_dir/libs/${android_app_abi}/" + _cronet_shared_lib_file_name,
+ ]
+ deps = [
+ ":cronet",
+ ]
+ }
-process_version("cronet_maven_build_fallback_copy") {
- template_file = "maven/test/build_fallback.gradle.template"
- sources = [
- "//build/util/LASTCHANGE",
- "//chrome/VERSION",
- ]
- output = "$_maven_test_dir/test_fallback/build.gradle"
-}
+ copy("cronet_package_copy_native_lib_unstripped") {
+ sources = [
+ "$root_out_dir/lib.unstripped/" + _cronet_shared_lib_file_name,
+ ]
+ outputs = [
+ "$_package_dir/symbols/${android_app_abi}/" +
+ _cronet_shared_lib_file_name,
+ ]
+ deps = [
+ ":cronet",
+ ]
+ }
-if (use_platform_icu_alternatives) {
- action("cronet_maven_test_build") {
- script = "maven/test/build_with_gradle.py"
- depfile = "$target_gen_dir/$target_name.d"
+ copy("cronet_package_copy_native_test_lib") {
+ testonly = true
+ sources = [
+ "$root_out_dir/libcronet_tests.so",
+ "$root_out_dir/libnetty-tcnative.so",
+ ]
+ outputs = [
+ "$_test_package_dir/libs/${android_app_abi}/{{source_file_part}}",
+ ]
deps = [
- ":cronet_maven_build_embedded_copy",
- ":cronet_maven_build_fallback_copy",
- ":cronet_maven_modules",
- ":cronet_maven_test_copy",
+ ":cronet_tests",
+ "//third_party/netty-tcnative:netty-tcnative-so",
+ ]
+ }
- # These deps ensure all source files of the sample app are deps.
- ":cronet_sample_apk",
- ":cronet_sample_test_apk",
+ copy("cronet_package_copy_native_test_lib_unstripped") {
+ testonly = true
+ sources = [
+ "$root_out_dir/lib.unstripped/libcronet_tests.so",
+ "$root_out_dir/lib.unstripped/libnetty-tcnative.so",
]
- _stamp_file = "$target_gen_dir/$target_name.stamp"
outputs = [
- _stamp_file,
- "$_maven_test_dir/build/outputs/apk/debug/test-debug.apk",
- "$_maven_test_dir/build/outputs/apk/androidTest/debug/test-debug-androidTest.apk",
+ "$_test_package_dir/symbols/${android_app_abi}/{{source_file_part}}",
]
- args = [
- "--project-dir",
- rebase_path("$_maven_test_dir"),
- "--depfile",
- rebase_path(depfile, root_build_dir),
- "--stamp",
- rebase_path(_stamp_file, root_build_dir),
+ deps = [
+ ":cronet_tests",
+ "//third_party/netty-tcnative:netty-tcnative-so",
+ ]
+ }
+
+ copy("cronet_package_copy_test_assets") {
+ testonly = true
+ sources = [
+ "//components/cronet/test/data",
+ ]
+ outputs = [
+ "$_test_package_dir/assets/test",
]
+ }
+
+ copy("cronet_package_copy_test_support_apks") {
testonly = true
+ sources = [
+ # Provides EmbeddedTestServer.
+ "$root_out_dir/apks/ChromiumNetTestSupport.apk",
+ ]
+ outputs = [
+ "$_test_package_dir/apks/${android_app_abi}/{{source_file_part}}",
+ ]
+ deps = [
+ "//net/android:net_test_support_apk",
+ ]
}
-}
-# Builds a maven module.
-template("cronet_maven_module") {
- _module_name = invoker.module_name
+ copy("cronet_package_copy_test_files") {
+ testonly = true
+ sources = [
+ "//net/data/ssl/certificates/quic-chain.pem",
+ "//net/data/ssl/certificates/quic-leaf-cert.key",
+ "//net/data/ssl/certificates/quic-leaf-cert.key.pkcs8.pem",
+ ]
+ outputs = [
+ "$_test_package_dir/assets/test_files/net/data/ssl/certificates/{{source_file_part}}",
+ ]
+ deps = [
+ # Not really dependent, but builds can fail if these two targets attempt
+ # to create the "assets" subdirectory simultaneously.
+ ":cronet_package_copy_test_assets",
+ ]
+ }
- # Build POM file.
- _pom_target_name = "pom_$target_name"
- _deps = [ ":$_pom_target_name" ]
- process_version(_pom_target_name) {
- template_file = "maven/$_module_name.pom.template"
+ copy("cronet_package_copy_resources") {
sources = [
- "//build/util/LASTCHANGE",
- "//chrome/VERSION",
+ "api/res/raw/keep_cronet_api.xml",
+ ]
+ outputs = [
+ "$_package_dir/res/raw/{{source_file_part}}",
]
- output = "$_maven_modules_dir/$_module_name/$_maven_version/$_module_name-$_maven_version.pom"
}
- if (defined(invoker.resource_dir)) {
- android_resources(invoker.module_name + "_resources") {
- resource_dirs = [ invoker.resource_dir ]
- android_manifest = "../../../build/android/AndroidManifest.xml"
+ # Enforce that ARM Neon is not used when building for ARMv7
+ if (target_cpu == "arm" && arm_version == 7 && !arm_use_neon) {
+ action("enforce_no_neon") {
+ script = "//components/cronet/tools/check_no_neon.py"
+ outputs = [
+ "$target_gen_dir/$target_name.stamp",
+ ]
+ args = [
+ rebase_path("${android_tool_prefix}objdump", root_build_dir),
+
+ # libcronet.so may contain ARM Neon instructions from BoringSSL, but these
+ # are only used after checking whether the CPU supports NEON at runtime,
+ # so instead check base/ as it represents a large swath of code that only
+ # contains Neon instructions when Neon is enabled by default.
+ rebase_path("$root_out_dir/obj/base/base/*.o", root_build_dir),
+ "--stamp",
+ rebase_path(outputs[0], root_build_dir),
+ ]
+ deps = [
+ "//base:base",
+ ]
}
}
- # Build AAR file.
- _aar_target_name = "aar_$target_name"
- dist_aar(_aar_target_name) {
+ # Enforce restrictions for API<->impl boundary.
+ action("api_static_checks") {
+ script = "//components/cronet/tools/api_static_checks.py"
+ outputs = [
+ "$target_gen_dir/$target_name.stamp",
+ ]
+ args = [
+ "--api_jar",
+ rebase_path(
+ "$root_out_dir/lib.java/components/cronet/android/cronet_api_java.jar",
+ root_build_dir),
+ "--impl_jar",
+ rebase_path(
+ "$root_out_dir/lib.java/components/cronet/android/cronet_impl_common_base_java.jar",
+ root_build_dir),
+ "--impl_jar",
+ rebase_path(
+ "$root_out_dir/lib.java/components/cronet/android/cronet_impl_platform_base_java.jar",
+ root_build_dir),
+ "--impl_jar",
+ rebase_path(
+ "$root_out_dir/lib.java/components/cronet/android/cronet_impl_native_base_java.jar",
+ root_build_dir),
+ "--stamp",
+ rebase_path(outputs[0], root_build_dir),
+ ]
deps = [
- ":cronet_package_copy",
- invoker.aar_jar_dep,
+ ":cronet_api_java",
+ ":cronet_impl_common_base_java",
+ ":cronet_impl_native_base_java",
+ ":cronet_impl_platform_base_java",
]
- direct_deps_only = true
- if (defined(invoker.aar_proguard_config)) {
- proguard_configs = [ invoker.aar_proguard_config ]
- }
- if (defined(invoker.aar_native_lib_dep)) {
- native_libraries = get_target_outputs(invoker.aar_native_lib_dep)
- deps += [ invoker.aar_native_lib_dep ]
+ inputs = [
+ "//components/cronet/tools/update_api.py",
+ ]
+ sources = [
+ "//components/cronet/android/api.txt",
+ "//components/cronet/android/api_version.txt",
+ ]
+ }
+
+ group("cronet_package") {
+ # Marked as testonly as it contains test-only targets too.
+ testonly = true
+
+ # Enforce building with ICU alternatives, crbug.com/611621.
+ # Enforce that arm_use_neon==false when building for ARMv7 by
+ # not including any deps in cronet_package target otherwise.
+ if (use_platform_icu_alternatives &&
+ (!(target_cpu == "arm" && arm_version == 7) || !arm_use_neon)) {
+ deps = [
+ ":api_static_checks",
+ ":copy_cronet_java8_jars",
+ ":cronet_package_copy",
+ ":cronet_package_copy_native_lib",
+ ":cronet_package_copy_native_lib_unstripped",
+ ":cronet_package_copy_resources",
+ ":cronet_test_package",
+ ":generate_javadoc",
+ ":generate_licenses",
+ ":jar_cronet_api_source",
+ ":jar_cronet_impl_common_java_source",
+ ":jar_cronet_impl_native_java_source",
+ ":jar_cronet_impl_platform_java_source",
+ ":jar_cronet_sample_source",
+ ":repackage_extracted_common_jars",
+ ":repackage_extracted_native_jars",
+ ]
+ if (current_cpu == "arm" && arm_version == 7) {
+ deps += [ ":enforce_no_neon" ]
+ }
}
- if (defined(invoker.resource_dir)) {
- deps += [ ":" + invoker.module_name + "_resources" ]
+ }
+
+ group("cronet_test_package") {
+ testonly = true
+
+ # Don't build for MIPS where tests aren't run.
+ if (current_cpu != "mipsel" && current_cpu != "mips64el") {
+ deps = [
+ ":cronet_package_copy_native_test_lib",
+ ":cronet_package_copy_native_test_lib_unstripped",
+ ":cronet_package_copy_test_assets",
+ ":cronet_package_copy_test_files",
+ ":cronet_package_copy_test_support_apks",
+ ":repackage_extracted_test_jars",
+ ]
}
- output = "$root_build_dir/$_aar_target_name.aar"
}
- _aar_copy_target_name = "$target_name.aar"
- _deps += [ ":$_aar_copy_target_name" ]
- copy(_aar_copy_target_name) {
- sources = get_target_outputs(":$_aar_target_name")
+ _maven_dir = "$_package_dir/maven-$current_cpu"
+ _maven_modules_dir = "$_maven_dir/org/chromium/net"
+ _maven_test_dir = "$_maven_dir/test"
+ _maven_version =
+ "$chrome_version_major.$chrome_version_build.$chrome_version_patch"
+
+ copy("cronet_maven_test_copy") {
+ sources = [
+ "maven/build.gradle",
+ "maven/local.properties",
+ "maven/settings.gradle",
+ ]
outputs = [
- "$_maven_modules_dir/$_module_name/$_maven_version/$_module_name-$_maven_version.aar",
+ "$_maven_test_dir/{{source_file_part}}",
]
- deps = [
- ":$_aar_target_name",
+ }
+
+ process_version("cronet_maven_build_embedded_copy") {
+ template_file = "maven/test/build_embedded.gradle.template"
+ sources = [
+ "//build/util/LASTCHANGE",
+ "//chrome/VERSION",
]
+ output = "$_maven_test_dir/test_embedded/build.gradle"
}
- if (defined(invoker.src_jar_dep)) {
- _copy_target_name = "$target_name-sources.jar"
- _deps += [ ":$_copy_target_name" ]
- copy(_copy_target_name) {
- sources = get_target_outputs(invoker.src_jar_dep)
+ process_version("cronet_maven_build_fallback_copy") {
+ template_file = "maven/test/build_fallback.gradle.template"
+ sources = [
+ "//build/util/LASTCHANGE",
+ "//chrome/VERSION",
+ ]
+ output = "$_maven_test_dir/test_fallback/build.gradle"
+ }
+
+ if (use_platform_icu_alternatives) {
+ action("cronet_maven_test_build") {
+ script = "maven/test/build_with_gradle.py"
+ depfile = "$target_gen_dir/$target_name.d"
+ deps = [
+ ":cronet_maven_build_embedded_copy",
+ ":cronet_maven_build_fallback_copy",
+ ":cronet_maven_modules",
+ ":cronet_maven_test_copy",
+
+ # These deps ensure all source files of the sample app are deps.
+ ":cronet_sample_apk",
+ ":cronet_sample_test_apk",
+ ]
+ _stamp_file = "$target_gen_dir/$target_name.stamp"
outputs = [
- "$_maven_modules_dir/$_module_name/$_maven_version/$_module_name-$_maven_version-sources.jar",
+ _stamp_file,
+ "$_maven_test_dir/build/outputs/apk/debug/test-debug.apk",
+ "$_maven_test_dir/build/outputs/apk/androidTest/debug/test-debug-androidTest.apk",
]
- deps = [
- invoker.src_jar_dep,
+ args = [
+ "--project-dir",
+ rebase_path("$_maven_test_dir"),
+ "--depfile",
+ rebase_path(depfile, root_build_dir),
+ "--stamp",
+ rebase_path(_stamp_file, root_build_dir),
]
+ testonly = true
}
}
- if (defined(invoker.javadoc_jar_dep)) {
- _copy_target_name = "$target_name-javadoc.jar"
- _deps += [ ":$_copy_target_name" ]
- copy(_copy_target_name) {
- sources = get_target_outputs(invoker.javadoc_jar_dep)
+ # Builds a maven module.
+ template("cronet_maven_module") {
+ _module_name = invoker.module_name
+
+ # Build POM file.
+ _pom_target_name = "pom_$target_name"
+ _deps = [ ":$_pom_target_name" ]
+ process_version(_pom_target_name) {
+ template_file = "maven/$_module_name.pom.template"
+ sources = [
+ "//build/util/LASTCHANGE",
+ "//chrome/VERSION",
+ ]
+ output = "$_maven_modules_dir/$_module_name/$_maven_version/$_module_name-$_maven_version.pom"
+ }
+
+ if (defined(invoker.resource_dir)) {
+ android_resources(invoker.module_name + "_resources") {
+ resource_dirs = [ invoker.resource_dir ]
+ android_manifest = "../../../build/android/AndroidManifest.xml"
+ }
+ }
+
+ # Build AAR file.
+ _aar_target_name = "aar_$target_name"
+ dist_aar(_aar_target_name) {
+ deps = [
+ ":cronet_package_copy",
+ invoker.aar_jar_dep,
+ ]
+ direct_deps_only = true
+ if (defined(invoker.aar_proguard_config)) {
+ proguard_configs = [ invoker.aar_proguard_config ]
+ }
+ if (defined(invoker.aar_native_lib_dep)) {
+ native_libraries = get_target_outputs(invoker.aar_native_lib_dep)
+ deps += [ invoker.aar_native_lib_dep ]
+ }
+ if (defined(invoker.resource_dir)) {
+ deps += [ ":" + invoker.module_name + "_resources" ]
+ }
+ output = "$root_build_dir/$_aar_target_name.aar"
+ }
+
+ _aar_copy_target_name = "$target_name.aar"
+ _deps += [ ":$_aar_copy_target_name" ]
+ copy(_aar_copy_target_name) {
+ sources = get_target_outputs(":$_aar_target_name")
outputs = [
- "$_maven_modules_dir/$_module_name/$_maven_version/$_module_name-$_maven_version-javadoc.jar",
+ "$_maven_modules_dir/$_module_name/$_maven_version/$_module_name-$_maven_version.aar",
]
deps = [
- invoker.javadoc_jar_dep,
+ ":$_aar_target_name",
]
}
- }
- group(target_name) {
- deps = _deps
+ if (defined(invoker.src_jar_dep)) {
+ _copy_target_name = "$target_name-sources.jar"
+ _deps += [ ":$_copy_target_name" ]
+ copy(_copy_target_name) {
+ sources = get_target_outputs(invoker.src_jar_dep)
+ outputs = [
+ "$_maven_modules_dir/$_module_name/$_maven_version/$_module_name-$_maven_version-sources.jar",
+ ]
+ deps = [
+ invoker.src_jar_dep,
+ ]
+ }
+ }
+
+ if (defined(invoker.javadoc_jar_dep)) {
+ _copy_target_name = "$target_name-javadoc.jar"
+ _deps += [ ":$_copy_target_name" ]
+ copy(_copy_target_name) {
+ sources = get_target_outputs(invoker.javadoc_jar_dep)
+ outputs = [
+ "$_maven_modules_dir/$_module_name/$_maven_version/$_module_name-$_maven_version-javadoc.jar",
+ ]
+ deps = [
+ invoker.javadoc_jar_dep,
+ ]
+ }
+ }
+
+ group(target_name) {
+ deps = _deps
+ }
}
-}
-cronet_maven_module("cronet_maven_api_module") {
- module_name = "cronet-api"
- aar_jar_dep = ":package_api_java"
- src_jar_dep = ":jar_cronet_api_source"
- javadoc_jar_dep = ":generate_javadoc"
- resource_dir = "api/res"
-}
+ cronet_maven_module("cronet_maven_api_module") {
+ module_name = "cronet-api"
+ aar_jar_dep = ":package_api_java"
+ src_jar_dep = ":jar_cronet_api_source"
+ javadoc_jar_dep = ":generate_javadoc"
+ resource_dir = "api/res"
+ }
-cronet_maven_module("cronet_maven_impl_common_module") {
- module_name = "cronet-common"
- aar_jar_dep = ":package_impl_common_java"
- aar_proguard_config = "$_package_dir/cronet_impl_common_proguard.cfg"
- src_jar_dep = ":jar_cronet_impl_common_java_source"
-}
+ cronet_maven_module("cronet_maven_impl_common_module") {
+ module_name = "cronet-common"
+ aar_jar_dep = ":package_impl_common_java"
+ aar_proguard_config = "$_package_dir/cronet_impl_common_proguard.cfg"
+ src_jar_dep = ":jar_cronet_impl_common_java_source"
+ }
-cronet_maven_module("cronet_maven_impl_native_module") {
- module_name = "cronet-embedded"
- aar_jar_dep = ":package_impl_native_java"
- aar_proguard_config = "$_package_dir/cronet_impl_native_proguard.cfg"
- aar_native_lib_dep = ":cronet_package_copy_native_lib"
- src_jar_dep = ":jar_cronet_impl_native_java_source"
-}
+ cronet_maven_module("cronet_maven_impl_native_module") {
+ module_name = "cronet-embedded"
+ aar_jar_dep = ":package_impl_native_java"
+ aar_proguard_config = "$_package_dir/cronet_impl_native_proguard.cfg"
+ aar_native_lib_dep = ":cronet_package_copy_native_lib"
+ src_jar_dep = ":jar_cronet_impl_native_java_source"
+ }
-cronet_maven_module("cronet_maven_impl_platform_module") {
- module_name = "cronet-fallback"
- aar_jar_dep = ":package_impl_platform_java"
- aar_proguard_config = "$_package_dir/cronet_impl_platform_proguard.cfg"
- src_jar_dep = ":jar_cronet_impl_platform_java_source"
-}
+ cronet_maven_module("cronet_maven_impl_platform_module") {
+ module_name = "cronet-fallback"
+ aar_jar_dep = ":package_impl_platform_java"
+ aar_proguard_config = "$_package_dir/cronet_impl_platform_proguard.cfg"
+ src_jar_dep = ":jar_cronet_impl_platform_java_source"
+ }
-group("cronet_maven_modules") {
- deps = [
- ":cronet_maven_api_module",
- ":cronet_maven_impl_common_module",
- ":cronet_maven_impl_native_module",
- ":cronet_maven_impl_platform_module",
- ]
+ group("cronet_maven_modules") {
+ deps = [
+ ":cronet_maven_api_module",
+ ":cronet_maven_impl_common_module",
+ ":cronet_maven_impl_native_module",
+ ":cronet_maven_impl_platform_module",
+ ]
+ }
}
diff --git a/chromium/components/cryptauth/BUILD.gn b/chromium/components/cryptauth/BUILD.gn
index 791f0b9ef20..a8350c1d91d 100644
--- a/chromium/components/cryptauth/BUILD.gn
+++ b/chromium/components/cryptauth/BUILD.gn
@@ -16,9 +16,6 @@ static_library("cryptauth") {
"background_eid_generator.h",
"connection.cc",
"connection.h",
- "cryptauth_access_token_fetcher.h",
- "cryptauth_access_token_fetcher_impl.cc",
- "cryptauth_access_token_fetcher_impl.h",
"cryptauth_api_call_flow.cc",
"cryptauth_api_call_flow.h",
"cryptauth_client.h",
@@ -53,18 +50,20 @@ static_library("cryptauth") {
"device_to_device_initiator_helper.h",
"device_to_device_secure_context.cc",
"device_to_device_secure_context.h",
+ "expiring_remote_device_cache.cc",
+ "expiring_remote_device_cache.h",
"foreground_eid_generator.cc",
"foreground_eid_generator.h",
"gcm_device_info_provider.h",
"local_device_data_provider.cc",
"local_device_data_provider.h",
+ "network_request_error.cc",
+ "network_request_error.h",
"pref_names.cc",
"pref_names.h",
"raw_eid_generator.h",
"raw_eid_generator_impl.cc",
"raw_eid_generator_impl.h",
- "remote_beacon_seed_fetcher.cc",
- "remote_beacon_seed_fetcher.h",
"remote_device.cc",
"remote_device.h",
"remote_device_cache.cc",
@@ -89,6 +88,7 @@ static_library("cryptauth") {
"software_feature_manager.h",
"software_feature_manager_impl.cc",
"software_feature_manager_impl.h",
+ "software_feature_state.cc",
"software_feature_state.h",
"switches.cc",
"switches.h",
@@ -105,6 +105,7 @@ static_library("cryptauth") {
"//chromeos",
"//chromeos/components/proximity_auth/logging",
"//components/cryptauth/ble",
+ "//components/cryptauth/proto:util",
"//components/gcm_driver",
"//components/gcm_driver/common",
"//components/prefs",
@@ -112,6 +113,7 @@ static_library("cryptauth") {
"//crypto",
"//google_apis",
"//net",
+ "//services/identity/public/cpp",
]
public_deps = [
@@ -162,8 +164,6 @@ static_library("test_support") {
"mock_foreground_eid_generator.h",
"mock_local_device_data_provider.cc",
"mock_local_device_data_provider.h",
- "mock_remote_beacon_seed_fetcher.cc",
- "mock_remote_beacon_seed_fetcher.h",
"mock_sync_scheduler.cc",
"mock_sync_scheduler.h",
"remote_device_test_util.cc",
@@ -187,7 +187,6 @@ source_set("unit_tests") {
sources = [
"background_eid_generator_unittest.cc",
"connection_unittest.cc",
- "cryptauth_access_token_fetcher_impl_unittest.cc",
"cryptauth_api_call_flow_unittest.cc",
"cryptauth_client_impl_unittest.cc",
"cryptauth_device_manager_impl_unittest.cc",
@@ -197,11 +196,11 @@ source_set("unit_tests") {
"device_to_device_authenticator_unittest.cc",
"device_to_device_operations_unittest.cc",
"device_to_device_secure_context_unittest.cc",
+ "expiring_remote_device_cache_unittest.cc",
"fake_secure_message_delegate_unittest.cc",
"foreground_eid_generator_unittest.cc",
"local_device_data_provider_unittest.cc",
"raw_eid_generator_impl_unittest.cc",
- "remote_beacon_seed_fetcher_unittest.cc",
"remote_device_cache_unittest.cc",
"remote_device_loader_unittest.cc",
"remote_device_provider_impl_unittest.cc",
@@ -222,6 +221,7 @@ source_set("unit_tests") {
"//components/prefs:test_support",
"//google_apis:test_support",
"//net:test_support",
+ "//services/identity/public/cpp:test_support",
"//testing/gtest",
]
}
diff --git a/chromium/components/cryptauth/DEPS b/chromium/components/cryptauth/DEPS
index 0779a592fa3..2193bca9922 100644
--- a/chromium/components/cryptauth/DEPS
+++ b/chromium/components/cryptauth/DEPS
@@ -12,5 +12,6 @@ include_rules = [
"+device/bluetooth",
"+google_apis",
"+net",
+ "+services/identity/public/cpp",
"+third_party/cros_system_api/dbus/service_constants.h",
]
diff --git a/chromium/components/cryptauth/background_eid_generator.cc b/chromium/components/cryptauth/background_eid_generator.cc
index 88b145ec29f..ef54c9534af 100644
--- a/chromium/components/cryptauth/background_eid_generator.cc
+++ b/chromium/components/cryptauth/background_eid_generator.cc
@@ -14,7 +14,6 @@
#include "components/cryptauth/proto/cryptauth_api.pb.h"
#include "components/cryptauth/raw_eid_generator.h"
#include "components/cryptauth/raw_eid_generator_impl.h"
-#include "components/cryptauth/remote_beacon_seed_fetcher.h"
#include "components/cryptauth/remote_device_ref.h"
namespace cryptauth {
@@ -94,9 +93,8 @@ std::unique_ptr<DataWithTimestamp> BackgroundEidGenerator::GenerateEid(
}
std::string BackgroundEidGenerator::IdentifyRemoteDeviceByAdvertisement(
- cryptauth::RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher,
const std::string& advertisement_service_data,
- const std::vector<std::string>& device_ids) const {
+ const RemoteDeviceRefList& remote_devices) const {
// Resize the service data to analyze only the first |kNumBytesInEidValue|
// bytes. If there are any bytes after those first |kNumBytesInEidValue|
// bytes, they are flags, so they are not needed to identify the device which
@@ -104,20 +102,11 @@ std::string BackgroundEidGenerator::IdentifyRemoteDeviceByAdvertisement(
std::string service_data_without_flags = advertisement_service_data;
service_data_without_flags.resize(RawEidGenerator::kNumBytesInEidValue);
- const auto device_id_it = std::find_if(
- device_ids.begin(), device_ids.end(),
- [this, remote_beacon_seed_fetcher,
- &service_data_without_flags](auto device_id) {
- std::vector<BeaconSeed> beacon_seeds;
- if (!remote_beacon_seed_fetcher->FetchSeedsForDeviceId(device_id,
- &beacon_seeds)) {
- PA_LOG(WARNING) << "Error fetching beacon seeds for device with ID "
- << RemoteDeviceRef::TruncateDeviceIdForLogs(
- device_id);
- return false;
- }
-
- std::vector<DataWithTimestamp> eids = GenerateNearestEids(beacon_seeds);
+ const auto remote_device_it = std::find_if(
+ remote_devices.begin(), remote_devices.end(),
+ [this, &service_data_without_flags](const auto& remote_device) {
+ std::vector<DataWithTimestamp> eids =
+ GenerateNearestEids(remote_device.beacon_seeds());
const auto eid_it = std::find_if(
eids.begin(), eids.end(), [&service_data_without_flags](auto eid) {
return eid.data == service_data_without_flags;
@@ -127,7 +116,9 @@ std::string BackgroundEidGenerator::IdentifyRemoteDeviceByAdvertisement(
});
// Return empty string if no matching device is found.
- return device_id_it != device_ids.end() ? *device_id_it : std::string();
+ return remote_device_it != remote_devices.end()
+ ? remote_device_it->GetDeviceId()
+ : std::string();
}
} // cryptauth
diff --git a/chromium/components/cryptauth/background_eid_generator.h b/chromium/components/cryptauth/background_eid_generator.h
index 47887235ebe..6f1463975e1 100644
--- a/chromium/components/cryptauth/background_eid_generator.h
+++ b/chromium/components/cryptauth/background_eid_generator.h
@@ -12,12 +12,12 @@
#include "base/macros.h"
#include "base/time/clock.h"
#include "components/cryptauth/data_with_timestamp.h"
+#include "components/cryptauth/remote_device_ref.h"
namespace cryptauth {
class BeaconSeed;
class RawEidGenerator;
-class RemoteBeaconSeedFetcher;
// Generates ephemeral ID (EID) values that are broadcast for background BLE
// advertisements in the ProximityAuth protocol.
@@ -43,14 +43,13 @@ class BackgroundEidGenerator {
virtual std::vector<DataWithTimestamp> GenerateNearestEids(
const std::vector<BeaconSeed>& beacon_seed) const;
- // Given an incoming background advertisement with |service_data|, identifies
- // which device (if any) sent the advertisement. Returns a device ID which
- // identifies the device. If no device can be identified, returns an empty
- // string.
+ // Given an incoming background advertisement with
+ // |advertisement_service_data|, identifies which device (if any) sent the
+ // advertisement. Returns a device ID which identifies the device. If no
+ // device can be identified, returns an empty string.
virtual std::string IdentifyRemoteDeviceByAdvertisement(
- cryptauth::RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher,
const std::string& advertisement_service_data,
- const std::vector<std::string>& device_ids) const;
+ const RemoteDeviceRefList& remote_devices) const;
private:
friend class CryptAuthBackgroundEidGeneratorTest;
@@ -59,7 +58,7 @@ class BackgroundEidGenerator {
// Helper function to generate the EID for any |timestamp_ms|, properly
// calculating the start of the period. Returns nullptr if |timestamp_ms| is
- // outside of range of |beacon_seeds|.
+ // outside the range of |beacon_seeds|.
std::unique_ptr<DataWithTimestamp> GenerateEid(
int64_t timestamp_ms,
const std::vector<BeaconSeed>& beacon_seeds) const;
diff --git a/chromium/components/cryptauth/background_eid_generator_unittest.cc b/chromium/components/cryptauth/background_eid_generator_unittest.cc
index 70bfdeb060e..7e028b9277d 100644
--- a/chromium/components/cryptauth/background_eid_generator_unittest.cc
+++ b/chromium/components/cryptauth/background_eid_generator_unittest.cc
@@ -11,10 +11,10 @@
#include "base/strings/string_util.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
-#include "components/cryptauth/mock_remote_beacon_seed_fetcher.h"
#include "components/cryptauth/proto/cryptauth_api.pb.h"
#include "components/cryptauth/raw_eid_generator_impl.h"
#include "components/cryptauth/remote_device_ref.h"
+#include "components/cryptauth/remote_device_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -95,14 +95,19 @@ class CryptAuthBackgroundEidGeneratorTest : public testing::Test {
beacon_seeds_.push_back(CreateBeaconSeed(
kFourthSeed, kStartPeriodMs + 2 * kBeaconSeedDurationMs,
kStartPeriodMs + 3 * kBeaconSeedDurationMs));
+
+ RemoteDeviceRef device_1 = RemoteDeviceRefBuilder()
+ .SetPublicKey("publicKey1")
+ .SetBeaconSeeds(beacon_seeds_)
+ .Build();
+ RemoteDeviceRef device_2 =
+ RemoteDeviceRefBuilder().SetPublicKey("publicKey2").Build();
+ test_remote_devices_ = {device_1, device_2};
}
void SetUp() override {
SetTestTime(kCurrentTimeMs);
- mock_seed_fetcher_ =
- std::make_unique<cryptauth::MockRemoteBeaconSeedFetcher>();
-
eid_generator_.reset(new BackgroundEidGenerator(
std::make_unique<TestRawEidGenerator>(), &test_clock_));
}
@@ -114,9 +119,9 @@ class CryptAuthBackgroundEidGeneratorTest : public testing::Test {
}
std::unique_ptr<BackgroundEidGenerator> eid_generator_;
- std::unique_ptr<MockRemoteBeaconSeedFetcher> mock_seed_fetcher_;
base::SimpleTestClock test_clock_;
std::vector<BeaconSeed> beacon_seeds_;
+ RemoteDeviceRefList test_remote_devices_;
};
TEST_F(CryptAuthBackgroundEidGeneratorTest,
@@ -228,11 +233,10 @@ TEST_F(CryptAuthBackgroundEidGeneratorTest,
SetTestTime(kStartPeriodMs + kEidPeriodMs / 2);
DataWithTimestamp advertisement_eid = CreateDataWithTimestamp(
beacon_seeds_[0].data(), kStartPeriodMs - kEidPeriodMs);
- mock_seed_fetcher_->SetSeedsForDeviceId(kDeviceId1, &beacon_seeds_);
- EXPECT_EQ(std::string(), eid_generator_->IdentifyRemoteDeviceByAdvertisement(
- mock_seed_fetcher_.get(), advertisement_eid.data,
- {kDeviceId2}));
+ EXPECT_EQ(std::string(),
+ eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+ advertisement_eid.data, {test_remote_devices_[1]}));
}
TEST_F(CryptAuthBackgroundEidGeneratorTest,
@@ -240,11 +244,10 @@ TEST_F(CryptAuthBackgroundEidGeneratorTest,
SetTestTime(kStartPeriodMs + kEidPeriodMs / 2);
DataWithTimestamp advertisement_eid = CreateDataWithTimestamp(
beacon_seeds_[0].data(), kStartPeriodMs - kEidPeriodMs);
- mock_seed_fetcher_->SetSeedsForDeviceId(kDeviceId1, &beacon_seeds_);
- EXPECT_EQ(kDeviceId1, eid_generator_->IdentifyRemoteDeviceByAdvertisement(
- mock_seed_fetcher_.get(), advertisement_eid.data,
- {kDeviceId1, kDeviceId2}));
+ EXPECT_EQ(test_remote_devices_[0].GetDeviceId(),
+ eid_generator_->IdentifyRemoteDeviceByAdvertisement(
+ advertisement_eid.data, test_remote_devices_));
}
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/ble/ble_advertisement_generator.cc b/chromium/components/cryptauth/ble/ble_advertisement_generator.cc
index b9eea585cc1..b5cf567543b 100644
--- a/chromium/components/cryptauth/ble/ble_advertisement_generator.cc
+++ b/chromium/components/cryptauth/ble/ble_advertisement_generator.cc
@@ -8,8 +8,6 @@
#include <vector>
#include "chromeos/components/proximity_auth/logging/logging.h"
-#include "components/cryptauth/local_device_data_provider.h"
-#include "components/cryptauth/remote_beacon_seed_fetcher.h"
#include "components/cryptauth/remote_device_ref.h"
namespace cryptauth {
@@ -20,14 +18,13 @@ BleAdvertisementGenerator* BleAdvertisementGenerator::instance_ = nullptr;
// static
std::unique_ptr<DataWithTimestamp>
BleAdvertisementGenerator::GenerateBleAdvertisement(
- const std::string& device_id,
- LocalDeviceDataProvider* local_device_data_provider,
- RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher) {
+ RemoteDeviceRef remote_device,
+ const std::string& local_device_public_key) {
if (!instance_)
instance_ = new BleAdvertisementGenerator();
- return instance_->GenerateBleAdvertisementInternal(
- device_id, local_device_data_provider, remote_beacon_seed_fetcher);
+ return instance_->GenerateBleAdvertisementInternal(remote_device,
+ local_device_public_key);
}
// static
@@ -43,48 +40,27 @@ BleAdvertisementGenerator::~BleAdvertisementGenerator() {}
std::unique_ptr<DataWithTimestamp>
BleAdvertisementGenerator::GenerateBleAdvertisementInternal(
- const std::string& device_id,
- LocalDeviceDataProvider* local_device_data_provider,
- RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher) {
- std::string local_device_public_key;
- if (!local_device_data_provider->GetLocalDeviceData(&local_device_public_key,
- nullptr)) {
- PA_LOG(WARNING) << "Error fetching the local device's public key. Cannot "
- << "advertise without the public key.";
- return nullptr;
- }
-
+ RemoteDeviceRef remote_device,
+ const std::string& local_device_public_key) {
if (local_device_public_key.empty()) {
PA_LOG(WARNING) << "Local device's public key is empty. Cannot advertise "
<< "with an invalid key.";
return nullptr;
}
- std::vector<BeaconSeed> remote_beacon_seeds;
- if (!remote_beacon_seed_fetcher->FetchSeedsForDeviceId(
- device_id, &remote_beacon_seeds)) {
- PA_LOG(WARNING) << "Error fetching beacon seeds for device with ID "
- << RemoteDeviceRef::TruncateDeviceIdForLogs(device_id)
- << ". "
- << "Cannot advertise without seeds.";
- return nullptr;
- }
-
- if (remote_beacon_seeds.empty()) {
+ if (remote_device.beacon_seeds().empty()) {
PA_LOG(WARNING) << "No synced seeds exist for device with ID "
- << RemoteDeviceRef::TruncateDeviceIdForLogs(device_id)
- << ". "
+ << remote_device.GetTruncatedDeviceIdForLogs() << ". "
<< "Cannot advertise without seeds.";
return nullptr;
}
std::unique_ptr<DataWithTimestamp> service_data =
eid_generator_->GenerateAdvertisement(local_device_public_key,
- remote_beacon_seeds);
+ remote_device.beacon_seeds());
if (!service_data) {
PA_LOG(WARNING) << "Error generating advertisement for device with ID "
- << RemoteDeviceRef::TruncateDeviceIdForLogs(device_id)
- << ". "
+ << remote_device.GetTruncatedDeviceIdForLogs() << ". "
<< "Cannot advertise.";
return nullptr;
}
diff --git a/chromium/components/cryptauth/ble/ble_advertisement_generator.h b/chromium/components/cryptauth/ble/ble_advertisement_generator.h
index 38b8d8f01f9..7a5acba2af7 100644
--- a/chromium/components/cryptauth/ble/ble_advertisement_generator.h
+++ b/chromium/components/cryptauth/ble/ble_advertisement_generator.h
@@ -11,27 +11,29 @@
#include "components/cryptauth/foreground_eid_generator.h"
namespace chromeos {
+namespace secure_channel {
+class SecureChannelBleServiceDataHelperImplTest;
+} // namespace secure_channel
namespace tether {
class BleAdvertiserImplTest;
+class BleServiceDataHelperImplTest;
class AdHocBleAdvertiserImplTest;
} // namespace tether
} // namespace chromeos
namespace cryptauth {
-class LocalDeviceDataProvider;
-class RemoteBeaconSeedFetcher;
+class RemoteDeviceRef;
// Generates advertisements for the ProximityAuth BLE advertisement scheme.
class BleAdvertisementGenerator {
public:
- // Generates an advertisement from the current device to the device with ID
- // |device_id|. The generated advertisement should be used immediately since
- // it is based on the current timestamp.
+ // Generates an advertisement from the current device to |remote_device|. The
+ // generated advertisement should be used immediately since it is based on the
+ // current timestamp.
static std::unique_ptr<DataWithTimestamp> GenerateBleAdvertisement(
- const std::string& device_id,
- LocalDeviceDataProvider* local_device_data_provider,
- RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher);
+ RemoteDeviceRef remote_device,
+ const std::string& local_device_public_key);
virtual ~BleAdvertisementGenerator();
@@ -39,13 +41,15 @@ class BleAdvertisementGenerator {
BleAdvertisementGenerator();
virtual std::unique_ptr<DataWithTimestamp> GenerateBleAdvertisementInternal(
- const std::string& device_id,
- LocalDeviceDataProvider* local_device_data_provider,
- RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher);
+ RemoteDeviceRef remote_device,
+ const std::string& local_device_public_key);
private:
friend class CryptAuthBleAdvertisementGeneratorTest;
+ friend class chromeos::secure_channel::
+ SecureChannelBleServiceDataHelperImplTest;
friend class chromeos::tether::BleAdvertiserImplTest;
+ friend class chromeos::tether::BleServiceDataHelperImplTest;
friend class chromeos::tether::AdHocBleAdvertiserImplTest;
static BleAdvertisementGenerator* instance_;
diff --git a/chromium/components/cryptauth/ble/ble_advertisement_generator_unittest.cc b/chromium/components/cryptauth/ble/ble_advertisement_generator_unittest.cc
index 9a2cb064605..568abdd0129 100644
--- a/chromium/components/cryptauth/ble/ble_advertisement_generator_unittest.cc
+++ b/chromium/components/cryptauth/ble/ble_advertisement_generator_unittest.cc
@@ -11,8 +11,6 @@
#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
#include "components/cryptauth/mock_foreground_eid_generator.h"
-#include "components/cryptauth/mock_local_device_data_provider.h"
-#include "components/cryptauth/mock_remote_beacon_seed_fetcher.h"
#include "components/cryptauth/proto/cryptauth_api.pb.h"
#include "components/cryptauth/remote_device_ref.h"
#include "components/cryptauth/remote_device_test_util.h"
@@ -25,17 +23,17 @@ namespace cryptauth {
namespace {
-const char kFakePublicKey[] = "fakePublicKey";
+const char kLocalDevicePublicKey[] = "localDevicePublicKey";
-std::vector<BeaconSeed> CreateFakeBeaconSeedsForDevice(
- RemoteDeviceRef remote_device) {
+std::vector<BeaconSeed> CreateBeaconSeedsForDevice(
+ const std::string& device_id) {
BeaconSeed seed1;
- seed1.set_data("seed1Data" + remote_device.GetTruncatedDeviceIdForLogs());
+ seed1.set_data("seed1Data" + device_id);
seed1.set_start_time_millis(1000L);
seed1.set_start_time_millis(2000L);
BeaconSeed seed2;
- seed2.set_data("seed2Data" + remote_device.GetTruncatedDeviceIdForLogs());
+ seed2.set_data("seed2Data" + device_id);
seed2.set_start_time_millis(2000L);
seed2.set_start_time_millis(3000L);
@@ -48,20 +46,13 @@ std::vector<BeaconSeed> CreateFakeBeaconSeedsForDevice(
class CryptAuthBleAdvertisementGeneratorTest : public testing::Test {
protected:
CryptAuthBleAdvertisementGeneratorTest()
- : fake_device_(CreateRemoteDeviceRefListForTest(1)[0]),
+ : test_remote_device_(
+ RemoteDeviceRefBuilder()
+ .SetBeaconSeeds(CreateBeaconSeedsForDevice("remote device id"))
+ .Build()),
fake_advertisement_("advertisement1", 1000L, 2000L) {}
void SetUp() override {
- mock_seed_fetcher_ = std::make_unique<MockRemoteBeaconSeedFetcher>();
- std::vector<BeaconSeed> device_0_beacon_seeds =
- CreateFakeBeaconSeedsForDevice(fake_device_);
- mock_seed_fetcher_->SetSeedsForDeviceId(fake_device_.GetDeviceId(),
- &device_0_beacon_seeds);
-
- mock_local_data_provider_ = std::make_unique<MockLocalDeviceDataProvider>();
- mock_local_data_provider_->SetPublicKey(
- std::make_unique<std::string>(kFakePublicKey));
-
generator_ = base::WrapUnique(new BleAdvertisementGenerator());
mock_eid_generator_ = new MockForegroundEidGenerator();
@@ -71,18 +62,16 @@ class CryptAuthBleAdvertisementGeneratorTest : public testing::Test {
void TearDown() override { generator_.reset(); }
- std::unique_ptr<DataWithTimestamp> GenerateBleAdvertisement() {
+ std::unique_ptr<DataWithTimestamp> CallGenerateBleAdvertisement(
+ RemoteDeviceRef remote_device,
+ const std::string& local_device_public_key) {
return generator_->GenerateBleAdvertisementInternal(
- fake_device_.GetDeviceId(), mock_local_data_provider_.get(),
- mock_seed_fetcher_.get());
+ remote_device, local_device_public_key);
}
- const RemoteDeviceRef fake_device_;
+ const RemoteDeviceRef test_remote_device_;
const DataWithTimestamp fake_advertisement_;
- std::unique_ptr<MockRemoteBeaconSeedFetcher> mock_seed_fetcher_;
- std::unique_ptr<MockLocalDeviceDataProvider> mock_local_data_provider_;
-
MockForegroundEidGenerator* mock_eid_generator_;
std::unique_ptr<BleAdvertisementGenerator> generator_;
@@ -91,37 +80,28 @@ class CryptAuthBleAdvertisementGeneratorTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(CryptAuthBleAdvertisementGeneratorTest);
};
-TEST_F(CryptAuthBleAdvertisementGeneratorTest, TestCannotFetchPublicKey) {
- mock_local_data_provider_->SetPublicKey(nullptr);
- EXPECT_EQ(nullptr, GenerateBleAdvertisement());
-}
-
TEST_F(CryptAuthBleAdvertisementGeneratorTest, EmptyPublicKey) {
- mock_local_data_provider_->SetPublicKey(std::make_unique<std::string>(""));
- EXPECT_EQ(nullptr, GenerateBleAdvertisement());
-}
-
-TEST_F(CryptAuthBleAdvertisementGeneratorTest, NoBeaconSeeds) {
- mock_seed_fetcher_->SetSeedsForDeviceId(fake_device_.GetDeviceId(), nullptr);
- EXPECT_EQ(nullptr, GenerateBleAdvertisement());
+ EXPECT_FALSE(
+ CallGenerateBleAdvertisement(test_remote_device_, std::string()));
}
TEST_F(CryptAuthBleAdvertisementGeneratorTest, EmptyBeaconSeeds) {
- std::vector<BeaconSeed> empty_seeds;
- mock_seed_fetcher_->SetSeedsForDeviceId(fake_device_.GetDeviceId(),
- &empty_seeds);
- EXPECT_EQ(nullptr, GenerateBleAdvertisement());
+ EXPECT_FALSE(CallGenerateBleAdvertisement(CreateRemoteDeviceRefForTest(),
+ kLocalDevicePublicKey));
}
TEST_F(CryptAuthBleAdvertisementGeneratorTest, CannotGenerateAdvertisement) {
mock_eid_generator_->set_advertisement(nullptr);
- EXPECT_EQ(nullptr, GenerateBleAdvertisement());
+ EXPECT_FALSE(
+ CallGenerateBleAdvertisement(test_remote_device_, kLocalDevicePublicKey));
}
TEST_F(CryptAuthBleAdvertisementGeneratorTest, AdvertisementGenerated) {
mock_eid_generator_->set_advertisement(
std::make_unique<DataWithTimestamp>(fake_advertisement_));
- EXPECT_EQ(fake_advertisement_, *GenerateBleAdvertisement());
+ EXPECT_EQ(fake_advertisement_,
+ *CallGenerateBleAdvertisement(test_remote_device_,
+ kLocalDevicePublicKey));
}
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.cc b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.cc
index ae753447aa4..d060e2350ff 100644
--- a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.cc
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.cc
@@ -9,6 +9,7 @@
#include <utility>
#include "base/bind.h"
+#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/metrics/histogram_macros.h"
#include "base/stl_util.h"
@@ -269,18 +270,6 @@ void BluetoothLowEnergyWeaveClientConnection::DestroyConnection(
RecordBleWeaveConnectionResult(result);
}
- if (result ==
- BleWeaveConnectionResult::
- BLE_WEAVE_CONNECTION_RESULT_TIMEOUT_FINDING_GATT_CHARACTERISTICS ||
- result ==
- BleWeaveConnectionResult::
- BLE_WEAVE_CONNECTION_RESULT_ERROR_FINDING_GATT_CHARACTERISTICS ||
- result ==
- BleWeaveConnectionResult::
- BLE_WEAVE_CONNECTION_RESULT_ERROR_GATT_CHARACTERISTIC_NOT_AVAILABLE) {
- NotifyGattCharacteristicsNotAvailable();
- }
-
if (adapter_) {
adapter_->RemoveObserver(this);
adapter_ = nullptr;
@@ -371,7 +360,7 @@ void BluetoothLowEnergyWeaveClientConnection::OnTimeoutForSubStatus(
void BluetoothLowEnergyWeaveClientConnection::SetupTestDoubles(
scoped_refptr<base::TaskRunner> test_task_runner,
- std::unique_ptr<base::Timer> test_timer,
+ std::unique_ptr<base::OneShotTimer> test_timer,
std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> test_generator,
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> test_receiver) {
task_runner_ = test_task_runner;
@@ -875,6 +864,34 @@ std::string BluetoothLowEnergyWeaveClientConnection::GetDeviceAddress() {
: bluetooth_device_->GetAddress();
}
+void BluetoothLowEnergyWeaveClientConnection::GetConnectionRssi(
+ base::OnceCallback<void(base::Optional<int32_t>)> callback) {
+ device::BluetoothDevice* device = GetBluetoothDevice();
+
+ if (!device || !device->IsConnected()) {
+ std::move(callback).Run(base::nullopt);
+ return;
+ }
+
+ // device::BluetoothDevice has not converted to using a base::OnceCallback
+ // instead of a base::Callback, so use a wrapper for now.
+ auto callback_holder = base::AdaptCallbackForRepeating(std::move(callback));
+ device->GetConnectionInfo(
+ base::Bind(&BluetoothLowEnergyWeaveClientConnection::OnConnectionInfo,
+ weak_ptr_factory_.GetWeakPtr(), callback_holder));
+}
+
+void BluetoothLowEnergyWeaveClientConnection::OnConnectionInfo(
+ base::RepeatingCallback<void(base::Optional<int32_t>)> rssi_callback,
+ const device::BluetoothDevice::ConnectionInfo& connection_info) {
+ if (connection_info.rssi == device::BluetoothDevice::kUnknownPower) {
+ std::move(rssi_callback).Run(base::nullopt);
+ return;
+ }
+
+ std::move(rssi_callback).Run(connection_info.rssi);
+}
+
device::BluetoothDevice*
BluetoothLowEnergyWeaveClientConnection::GetBluetoothDevice() {
return bluetooth_device_;
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.h b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.h
index 54d3bf96fdd..716a5384911 100644
--- a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.h
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection.h
@@ -15,6 +15,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/cryptauth/ble/bluetooth_low_energy_characteristics_finder.h"
@@ -103,6 +104,8 @@ class BluetoothLowEnergyWeaveClientConnection
void Connect() override;
void Disconnect() override;
std::string GetDeviceAddress() override;
+ void GetConnectionRssi(
+ base::OnceCallback<void(base::Optional<int32_t>)> callback) override;
protected:
enum BleWeaveConnectionResult {
@@ -134,7 +137,7 @@ class BluetoothLowEnergyWeaveClientConnection
void SetupTestDoubles(
scoped_refptr<base::TaskRunner> test_task_runner,
- std::unique_ptr<base::Timer> test_timer,
+ std::unique_ptr<base::OneShotTimer> test_timer,
std::unique_ptr<BluetoothLowEnergyWeavePacketGenerator> test_generator,
std::unique_ptr<BluetoothLowEnergyWeavePacketReceiver> test_receiver);
@@ -298,6 +301,10 @@ class BluetoothLowEnergyWeaveClientConnection
void SetSubStatus(SubStatus status);
void OnTimeoutForSubStatus(SubStatus status);
+ void OnConnectionInfo(
+ base::RepeatingCallback<void(base::Optional<int32_t>)> rssi_callback,
+ const device::BluetoothDevice::ConnectionInfo& connection_info);
+
// These functions are used to set up the connection so that it is ready to
// send/receive data.
void SetConnectionLatency();
@@ -374,7 +381,7 @@ class BluetoothLowEnergyWeaveClientConnection
RemoteAttribute tx_characteristic_;
RemoteAttribute rx_characteristic_;
scoped_refptr<base::TaskRunner> task_runner_;
- std::unique_ptr<base::Timer> timer_;
+ std::unique_ptr<base::OneShotTimer> timer_;
// These pointers start out null and are created during the connection
// process.
diff --git a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc
index d95625a3caf..3a260fe5f61 100644
--- a/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc
+++ b/chromium/components/cryptauth/ble/bluetooth_low_energy_weave_client_connection_unittest.cc
@@ -13,7 +13,7 @@
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_simple_task_runner.h"
#include "base/timer/mock_timer.h"
#include "base/timer/timer.h"
@@ -268,7 +268,6 @@ class MockConnectionObserver : public ConnectionObserver {
MockConnectionObserver(Connection* connection)
: connection_(connection),
num_send_completed_(0),
- num_gatt_services_unavailable_events_(0),
delete_on_disconnect_(false),
delete_on_message_sent_(false) {}
@@ -280,10 +279,6 @@ class MockConnectionObserver : public ConnectionObserver {
int num_send_completed() { return num_send_completed_; }
- int num_gatt_services_unavailable_events() {
- return num_gatt_services_unavailable_events_;
- }
-
bool delete_on_disconnect() { return delete_on_disconnect_; }
void set_delete_on_disconnect(bool delete_on_disconnect) {
@@ -316,16 +311,11 @@ class MockConnectionObserver : public ConnectionObserver {
delete connection_;
}
- void OnGattCharacteristicsNotAvailable() override {
- ++num_gatt_services_unavailable_events_;
- }
-
private:
Connection* connection_;
std::string last_deserialized_message_;
bool last_send_success_;
int num_send_completed_;
- int num_gatt_services_unavailable_events_;
bool delete_on_disconnect_;
bool delete_on_message_sent_;
};
@@ -347,7 +337,6 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
generator_ = nullptr;
receiver_ = nullptr;
has_verified_connection_result_ = false;
- has_verified_gatt_services_event_ = false;
connection_observer_.reset();
adapter_ = base::MakeRefCounted<NiceMock<device::MockBluetoothAdapter>>();
@@ -377,6 +366,11 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
.WillByDefault(Return(mock_bluetooth_device_.get()));
ON_CALL(*mock_bluetooth_device_, GetGattService(kServiceID))
.WillByDefault(Return(service_.get()));
+ ON_CALL(*mock_bluetooth_device_, IsConnected()).WillByDefault(Return(true));
+ ON_CALL(*mock_bluetooth_device_, GetConnectionInfo(_))
+ .WillByDefault(
+ Invoke(this, &CryptAuthBluetoothLowEnergyWeaveClientConnectionTest::
+ MockGetConnectionInfo));
ON_CALL(*service_, GetCharacteristic(kRXCharacteristicID))
.WillByDefault(Return(rx_characteristic_.get()));
ON_CALL(*service_, GetCharacteristic(kTXCharacteristicID))
@@ -386,11 +380,6 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
}
void TearDown() override {
- ASSERT_TRUE(has_verified_connection_result_);
- if (connection_observer_ && !has_verified_gatt_services_event_) {
- EXPECT_EQ(0,
- connection_observer_->num_gatt_services_unavailable_events());
- }
connection_observer_.reset();
}
@@ -414,8 +403,7 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
base::WrapUnique(new MockConnectionObserver(connection.get()));
connection->AddObserver(connection_observer_.get());
- test_timer_ = new base::MockTimer(false /* retains_user_task */,
- false /* is_repeating */);
+ test_timer_ = new base::MockOneShotTimer();
generator_ = new NiceMock<MockBluetoothLowEnergyWeavePacketGenerator>();
receiver_ = new NiceMock<MockBluetoothLowEnergyWeavePacketReceiver>();
connection->SetupTestDoubles(task_runner_, base::WrapUnique(test_timer_),
@@ -634,11 +622,6 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
has_verified_connection_result_ = true;
}
- void VerifyGattServicesUnavailableEventSent() {
- EXPECT_EQ(1, connection_observer_->num_gatt_services_unavailable_events());
- has_verified_gatt_services_event_ = true;
- }
-
BluetoothLowEnergyWeaveClientConnection::GattServiceOperationResult
GattServiceOperationResultSuccessOrFailure(bool success) {
return success ? BluetoothLowEnergyWeaveClientConnection::
@@ -649,6 +632,18 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
GATT_SERVICE_OPERATION_RESULT_GATT_ERROR_UNKNOWN;
}
+ base::Optional<int32_t> GetRssi(
+ TestBluetoothLowEnergyWeaveClientConnection* connection) {
+ connection->GetConnectionRssi(base::BindOnce(
+ &CryptAuthBluetoothLowEnergyWeaveClientConnectionTest::OnConnectionRssi,
+ base::Unretained(this)));
+
+ base::Optional<int32_t> rssi = rssi_;
+ rssi_.reset();
+
+ return rssi;
+ }
+
protected:
const RemoteDeviceRef remote_device_;
const device::BluetoothUUID service_uuid_;
@@ -657,7 +652,7 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
const proximity_auth::ScopedDisableLoggingForTesting disable_logging_;
scoped_refptr<device::MockBluetoothAdapter> adapter_;
- base::MockTimer* test_timer_;
+ base::MockOneShotTimer* test_timer_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
std::unique_ptr<device::MockBluetoothDevice> mock_bluetooth_device_;
@@ -666,9 +661,9 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
std::unique_ptr<device::MockBluetoothGattCharacteristic> rx_characteristic_;
std::vector<uint8_t> last_value_written_on_tx_characteristic_;
base::MessageLoop message_loop_;
+ int32_t rssi_for_channel_ = device::BluetoothDevice::kUnknownPower;
bool last_wire_message_success_;
bool has_verified_connection_result_;
- bool has_verified_gatt_services_event_;
NiceMock<MockBluetoothLowEnergyWeavePacketGenerator>* generator_;
NiceMock<MockBluetoothLowEnergyWeavePacketReceiver>* receiver_;
std::unique_ptr<MockConnectionObserver> connection_observer_;
@@ -698,6 +693,16 @@ class CryptAuthBluetoothLowEnergyWeaveClientConnectionTest
base::HistogramTester histogram_tester_;
private:
+ void MockGetConnectionInfo(
+ const device::BluetoothDevice::ConnectionInfoCallback& callback) {
+ callback.Run(device::BluetoothDevice::ConnectionInfo(
+ rssi_for_channel_, 0 /* transmit_power */, 0 /* max_transmit_power */));
+ }
+
+ void OnConnectionRssi(base::Optional<int32_t> rssi) { rssi_ = rssi; }
+
+ base::Optional<int32_t> rssi_;
+
DISALLOW_COPY_AND_ASSIGN(
CryptAuthBluetoothLowEnergyWeaveClientConnectionTest);
};
@@ -853,7 +858,6 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
EXPECT_EQ(connection->status(), Connection::Status::DISCONNECTED);
- VerifyGattServicesUnavailableEventSent();
VerifyBleWeaveConnectionResult(
BluetoothLowEnergyWeaveClientConnection::BleWeaveConnectionResult::
BLE_WEAVE_CONNECTION_RESULT_ERROR_FINDING_GATT_CHARACTERISTICS);
@@ -880,7 +884,6 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
EXPECT_EQ(connection->status(), Connection::Status::DISCONNECTED);
- VerifyGattServicesUnavailableEventSent();
VerifyBleWeaveConnectionResult(
BluetoothLowEnergyWeaveClientConnection::BleWeaveConnectionResult::
BLE_WEAVE_CONNECTION_RESULT_ERROR_GATT_CHARACTERISTIC_NOT_AVAILABLE);
@@ -1509,7 +1512,6 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
EXPECT_EQ(connection->sub_status(), SubStatus::DISCONNECTED);
EXPECT_EQ(connection->status(), Connection::Status::DISCONNECTED);
- VerifyGattServicesUnavailableEventSent();
VerifyBleWeaveConnectionResult(
BluetoothLowEnergyWeaveClientConnection::BleWeaveConnectionResult::
BLE_WEAVE_CONNECTION_RESULT_TIMEOUT_FINDING_GATT_CHARACTERISTICS);
@@ -1578,6 +1580,22 @@ TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest,
BLE_WEAVE_CONNECTION_RESULT_TIMEOUT_WAITING_FOR_MESSAGE_TO_SEND);
}
+TEST_F(CryptAuthBluetoothLowEnergyWeaveClientConnectionTest, GetRssi) {
+ std::unique_ptr<TestBluetoothLowEnergyWeaveClientConnection> connection(
+ CreateConnection(true /* should_set_low_connection_latency */));
+
+ EXPECT_FALSE(GetRssi(connection.get()));
+
+ rssi_for_channel_ = -50;
+ EXPECT_EQ(-50, GetRssi(connection.get()));
+
+ rssi_for_channel_ = -40;
+ EXPECT_EQ(-40, GetRssi(connection.get()));
+
+ rssi_for_channel_ = -30;
+ EXPECT_EQ(-30, GetRssi(connection.get()));
+}
+
} // namespace weave
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/ble/fake_ble_advertisement_generator.cc b/chromium/components/cryptauth/ble/fake_ble_advertisement_generator.cc
index f95ad9e4e4c..369175d7761 100644
--- a/chromium/components/cryptauth/ble/fake_ble_advertisement_generator.cc
+++ b/chromium/components/cryptauth/ble/fake_ble_advertisement_generator.cc
@@ -4,6 +4,8 @@
#include "components/cryptauth/ble/fake_ble_advertisement_generator.h"
+#include "components/cryptauth/remote_device_ref.h"
+
namespace cryptauth {
FakeBleAdvertisementGenerator::FakeBleAdvertisementGenerator() {}
@@ -12,9 +14,8 @@ FakeBleAdvertisementGenerator::~FakeBleAdvertisementGenerator() {}
std::unique_ptr<DataWithTimestamp>
FakeBleAdvertisementGenerator::GenerateBleAdvertisementInternal(
- const std::string& device_id,
- LocalDeviceDataProvider* local_device_data_provider,
- RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher) {
+ RemoteDeviceRef remote_device,
+ const std::string& local_device_public_key) {
return std::move(advertisement_);
}
diff --git a/chromium/components/cryptauth/ble/fake_ble_advertisement_generator.h b/chromium/components/cryptauth/ble/fake_ble_advertisement_generator.h
index 1dad8026a72..d74290f8623 100644
--- a/chromium/components/cryptauth/ble/fake_ble_advertisement_generator.h
+++ b/chromium/components/cryptauth/ble/fake_ble_advertisement_generator.h
@@ -27,9 +27,8 @@ class FakeBleAdvertisementGenerator : public BleAdvertisementGenerator {
protected:
std::unique_ptr<DataWithTimestamp> GenerateBleAdvertisementInternal(
- const std::string& device_id,
- LocalDeviceDataProvider* local_device_data_provider,
- RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher) override;
+ RemoteDeviceRef remote_device,
+ const std::string& local_device_public_key) override;
private:
std::unique_ptr<DataWithTimestamp> advertisement_;
diff --git a/chromium/components/cryptauth/connection.cc b/chromium/components/cryptauth/connection.cc
index 84158ffe58f..8d8beff64ef 100644
--- a/chromium/components/cryptauth/connection.cc
+++ b/chromium/components/cryptauth/connection.cc
@@ -7,6 +7,7 @@
#include <sstream>
#include <utility>
+#include "base/callback.h"
#include "base/logging.h"
#include "chromeos/components/proximity_auth/logging/logging.h"
#include "components/cryptauth/connection_observer.h"
@@ -50,6 +51,11 @@ void Connection::RemoveObserver(ConnectionObserver* observer) {
observers_.RemoveObserver(observer);
}
+void Connection::GetConnectionRssi(
+ base::OnceCallback<void(base::Optional<int32_t>)> callback) {
+ std::move(callback).Run(base::nullopt);
+}
+
void Connection::SetStatus(Status status) {
if (status_ == status)
return;
@@ -104,11 +110,6 @@ std::unique_ptr<WireMessage> Connection::DeserializeWireMessage(
return WireMessage::Deserialize(received_bytes_, is_incomplete_message);
}
-void Connection::NotifyGattCharacteristicsNotAvailable() {
- for (auto& observer : observers_)
- observer.OnGattCharacteristicsNotAvailable();
-}
-
std::string Connection::GetDeviceInfoLogString() {
std::stringstream ss;
ss << "{id: \"" << remote_device().GetTruncatedDeviceIdForLogs()
diff --git a/chromium/components/cryptauth/connection.h b/chromium/components/cryptauth/connection.h
index 84298049061..678376867f2 100644
--- a/chromium/components/cryptauth/connection.h
+++ b/chromium/components/cryptauth/connection.h
@@ -8,9 +8,11 @@
#include <memory>
#include <ostream>
+#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
+#include "base/optional.h"
#include "components/cryptauth/remote_device_ref.h"
namespace cryptauth {
@@ -48,6 +50,11 @@ class Connection {
RemoteDeviceRef remote_device() const { return remote_device_; }
+ // Returns the RSSI of the connection; if no derived class overrides this
+ // function, base::nullopt is returned.
+ virtual void GetConnectionRssi(
+ base::OnceCallback<void(base::Optional<int32_t>)> callback);
+
// Abstract methods that subclasses should implement:
// Attempts to connect to the remote device if not already connected.
@@ -91,8 +98,6 @@ class Connection {
virtual std::unique_ptr<WireMessage> DeserializeWireMessage(
bool* is_incomplete_message);
- void NotifyGattCharacteristicsNotAvailable();
-
// Returns a string describing the associated device for logging purposes.
std::string GetDeviceInfoLogString();
diff --git a/chromium/components/cryptauth/connection_observer.h b/chromium/components/cryptauth/connection_observer.h
index 566eb68497d..3ac1a99c4ef 100644
--- a/chromium/components/cryptauth/connection_observer.h
+++ b/chromium/components/cryptauth/connection_observer.h
@@ -32,13 +32,6 @@ class ConnectionObserver {
virtual void OnSendCompleted(const Connection& connection,
const WireMessage& message,
bool success) {}
-
- // Called when GATT characteristics are not available. This observer function
- // is a temporary work-around (see crbug.com/784968).
- // TODO(khorimoto): This observer function is specific to only one Connection
- // implementation, so it is hacky to include it as part of the observer
- // for Connection. Remove this work-around when it is no longer necessary.
- virtual void OnGattCharacteristicsNotAvailable() {}
};
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/connection_unittest.cc b/chromium/components/cryptauth/connection_unittest.cc
index 92ba0feb998..07a6db9a3b1 100644
--- a/chromium/components/cryptauth/connection_unittest.cc
+++ b/chromium/components/cryptauth/connection_unittest.cc
@@ -4,8 +4,11 @@
#include "components/cryptauth/connection.h"
+#include "base/bind.h"
+#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
+#include "base/optional.h"
#include "components/cryptauth/connection_observer.h"
#include "components/cryptauth/remote_device_ref.h"
#include "components/cryptauth/remote_device_test_util.h"
@@ -81,7 +84,7 @@ class MockConnectionObserver : public ConnectionObserver {
class TestWireMessage : public WireMessage {
public:
TestWireMessage() : WireMessage("payload", "feature") {}
- ~TestWireMessage() override {}
+ ~TestWireMessage() override = default;
private:
DISALLOW_COPY_AND_ASSIGN(TestWireMessage);
@@ -91,14 +94,28 @@ class TestWireMessage : public WireMessage {
class CryptAuthConnectionTest : public testing::Test {
protected:
- CryptAuthConnectionTest() {}
- ~CryptAuthConnectionTest() override {}
+ CryptAuthConnectionTest() = default;
+ ~CryptAuthConnectionTest() override = default;
+
+ base::Optional<int32_t> GetRssi(Connection* connection) {
+ connection->GetConnectionRssi(base::Bind(
+ &CryptAuthConnectionTest::OnConnectionRssi, base::Unretained(this)));
+
+ base::Optional<int32_t> rssi = rssi_;
+ rssi_.reset();
+
+ return rssi;
+ }
private:
+ void OnConnectionRssi(base::Optional<int32_t> rssi) { rssi_ = rssi; }
+
+ base::Optional<int32_t> rssi_;
+
DISALLOW_COPY_AND_ASSIGN(CryptAuthConnectionTest);
};
-TEST(CryptAuthConnectionTest, IsConnected) {
+TEST_F(CryptAuthConnectionTest, IsConnected) {
StrictMock<MockConnection> connection;
EXPECT_FALSE(connection.IsConnected());
@@ -112,7 +129,7 @@ TEST(CryptAuthConnectionTest, IsConnected) {
EXPECT_FALSE(connection.IsConnected());
}
-TEST(CryptAuthConnectionTest, SendMessage_FailsWhenNotConnected) {
+TEST_F(CryptAuthConnectionTest, SendMessage_FailsWhenNotConnected) {
StrictMock<MockConnection> connection;
connection.SetStatus(Connection::Status::IN_PROGRESS);
@@ -121,8 +138,8 @@ TEST(CryptAuthConnectionTest, SendMessage_FailsWhenNotConnected) {
connection.SendMessage(std::unique_ptr<WireMessage>());
}
-TEST(CryptAuthConnectionTest,
- SendMessage_FailsWhenAnotherMessageSendIsInProgress) {
+TEST_F(CryptAuthConnectionTest,
+ SendMessage_FailsWhenAnotherMessageSendIsInProgress) {
NiceMock<MockConnection> connection;
connection.SetStatus(Connection::Status::CONNECTED);
connection.SendMessage(std::unique_ptr<WireMessage>());
@@ -131,7 +148,7 @@ TEST(CryptAuthConnectionTest,
connection.SendMessage(std::unique_ptr<WireMessage>());
}
-TEST(CryptAuthConnectionTest, SendMessage_SucceedsWhenConnected) {
+TEST_F(CryptAuthConnectionTest, SendMessage_SucceedsWhenConnected) {
StrictMock<MockConnection> connection;
connection.SetStatus(Connection::Status::CONNECTED);
@@ -139,8 +156,8 @@ TEST(CryptAuthConnectionTest, SendMessage_SucceedsWhenConnected) {
connection.SendMessage(std::unique_ptr<WireMessage>());
}
-TEST(CryptAuthConnectionTest,
- SendMessage_SucceedsAfterPreviousMessageSendCompletes) {
+TEST_F(CryptAuthConnectionTest,
+ SendMessage_SucceedsAfterPreviousMessageSendCompletes) {
NiceMock<MockConnection> connection;
connection.SetStatus(Connection::Status::CONNECTED);
connection.SendMessage(std::unique_ptr<WireMessage>());
@@ -150,7 +167,7 @@ TEST(CryptAuthConnectionTest,
connection.SendMessage(std::unique_ptr<WireMessage>());
}
-TEST(CryptAuthConnectionTest, SetStatus_NotifiesObserversOfStatusChange) {
+TEST_F(CryptAuthConnectionTest, SetStatus_NotifiesObserversOfStatusChange) {
StrictMock<MockConnection> connection;
EXPECT_EQ(Connection::Status::DISCONNECTED, connection.status());
@@ -163,8 +180,8 @@ TEST(CryptAuthConnectionTest, SetStatus_NotifiesObserversOfStatusChange) {
connection.SetStatus(Connection::Status::CONNECTED);
}
-TEST(CryptAuthConnectionTest,
- SetStatus_DoesntNotifyObserversIfStatusUnchanged) {
+TEST_F(CryptAuthConnectionTest,
+ SetStatus_DoesntNotifyObserversIfStatusUnchanged) {
StrictMock<MockConnection> connection;
EXPECT_EQ(Connection::Status::DISCONNECTED, connection.status());
@@ -175,8 +192,8 @@ TEST(CryptAuthConnectionTest,
connection.SetStatus(Connection::Status::DISCONNECTED);
}
-TEST(CryptAuthConnectionTest,
- OnDidSendMessage_NotifiesObserversIfMessageSendInProgress) {
+TEST_F(CryptAuthConnectionTest,
+ OnDidSendMessage_NotifiesObserversIfMessageSendInProgress) {
NiceMock<MockConnection> connection;
connection.SetStatus(Connection::Status::CONNECTED);
connection.SendMessage(std::unique_ptr<WireMessage>());
@@ -188,8 +205,8 @@ TEST(CryptAuthConnectionTest,
connection.OnDidSendMessage(TestWireMessage(), true /* success */);
}
-TEST(CryptAuthConnectionTest,
- OnDidSendMessage_DoesntNotifyObserversIfNoMessageSendInProgress) {
+TEST_F(CryptAuthConnectionTest,
+ OnDidSendMessage_DoesntNotifyObserversIfNoMessageSendInProgress) {
NiceMock<MockConnection> connection;
connection.SetStatus(Connection::Status::CONNECTED);
@@ -200,8 +217,8 @@ TEST(CryptAuthConnectionTest,
connection.OnDidSendMessage(TestWireMessage(), true /* success */);
}
-TEST(CryptAuthConnectionTest,
- OnBytesReceived_NotifiesObserversOnValidMessage) {
+TEST_F(CryptAuthConnectionTest,
+ OnBytesReceived_NotifiesObserversOnValidMessage) {
NiceMock<MockConnection> connection;
connection.SetStatus(Connection::Status::CONNECTED);
@@ -215,8 +232,8 @@ TEST(CryptAuthConnectionTest,
connection.OnBytesReceived(std::string());
}
-TEST(CryptAuthConnectionTest,
- OnBytesReceived_DoesntNotifyObserversIfNotConnected) {
+TEST_F(CryptAuthConnectionTest,
+ OnBytesReceived_DoesntNotifyObserversIfNotConnected) {
StrictMock<MockConnection> connection;
connection.SetStatus(Connection::Status::IN_PROGRESS);
@@ -228,8 +245,8 @@ TEST(CryptAuthConnectionTest,
connection.OnBytesReceived(std::string());
}
-TEST(CryptAuthConnectionTest,
- OnBytesReceived_DoesntNotifyObserversIfMessageIsIncomplete) {
+TEST_F(CryptAuthConnectionTest,
+ OnBytesReceived_DoesntNotifyObserversIfMessageIsIncomplete) {
NiceMock<MockConnection> connection;
connection.SetStatus(Connection::Status::CONNECTED);
@@ -242,8 +259,8 @@ TEST(CryptAuthConnectionTest,
connection.OnBytesReceived(std::string());
}
-TEST(CryptAuthConnectionTest,
- OnBytesReceived_DoesntNotifyObserversIfMessageIsInvalid) {
+TEST_F(CryptAuthConnectionTest,
+ OnBytesReceived_DoesntNotifyObserversIfMessageIsInvalid) {
NiceMock<MockConnection> connection;
connection.SetStatus(Connection::Status::CONNECTED);
@@ -256,4 +273,10 @@ TEST(CryptAuthConnectionTest,
connection.OnBytesReceived(std::string());
}
+TEST_F(CryptAuthConnectionTest, GetConnectionRssi) {
+ NiceMock<MockConnection> connection;
+ connection.SetStatus(Connection::Status::CONNECTED);
+ EXPECT_EQ(base::nullopt, GetRssi(&connection));
+}
+
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_access_token_fetcher.h b/chromium/components/cryptauth/cryptauth_access_token_fetcher.h
deleted file mode 100644
index d79e7bdad02..00000000000
--- a/chromium/components/cryptauth/cryptauth_access_token_fetcher.h
+++ /dev/null
@@ -1,30 +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_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_H_
-#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_H_
-
-#include <string>
-
-#include "base/callback_forward.h"
-
-namespace cryptauth {
-
-// Simple interface for fetching the OAuth2 access token that authorizes
-// CryptAuth API calls. Do not reuse this after calling FetchAccessToken();
-// instead, create a new instance.
-class CryptAuthAccessTokenFetcher {
- public:
- virtual ~CryptAuthAccessTokenFetcher() {}
-
- // Fetches the access token asynchronously, invoking the callback upon
- // completion. If the fetch fails, the callback will be invoked with an empty
- // string.
- typedef base::Callback<void(const std::string&)> AccessTokenCallback;
- virtual void FetchAccessToken(const AccessTokenCallback& callback) = 0;
-};
-
-} // namespace cryptauth
-
-#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_H_
diff --git a/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.cc b/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.cc
deleted file mode 100644
index f9ba9260088..00000000000
--- a/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.cc
+++ /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.
-
-#include "components/cryptauth/cryptauth_access_token_fetcher_impl.h"
-
-namespace cryptauth {
-
-namespace {
-
-// Returns the set of OAuth2 scopes that CryptAuth uses.
-OAuth2TokenService::ScopeSet GetScopes() {
- OAuth2TokenService::ScopeSet scopes;
- scopes.insert("https://www.googleapis.com/auth/cryptauth");
- return scopes;
-}
-
-} // namespace
-
-CryptAuthAccessTokenFetcherImpl::CryptAuthAccessTokenFetcherImpl(
- OAuth2TokenService* token_service,
- const std::string& account_id)
- : OAuth2TokenService::Consumer("cryptauth_access_token_fetcher"),
- token_service_(token_service),
- account_id_(account_id),
- fetch_started_(false) {
-}
-
-CryptAuthAccessTokenFetcherImpl::~CryptAuthAccessTokenFetcherImpl() {
-}
-
-void CryptAuthAccessTokenFetcherImpl::FetchAccessToken(
- const AccessTokenCallback& callback) {
- if (fetch_started_) {
- LOG(WARNING) << "Create an instance for each token fetched. Do not reuse.";
- callback.Run(std::string());
- return;
- }
-
- fetch_started_ = true;
- callback_ = callback;
- // This request will return a cached result if it is available, saving a
- // network round trip every time we fetch the access token.
- token_request_ = token_service_->StartRequest(account_id_, GetScopes(), this);
-}
-
-void CryptAuthAccessTokenFetcherImpl::OnGetTokenSuccess(
- const OAuth2TokenService::Request* request,
- const std::string& access_token,
- const base::Time& expiration_time) {
- callback_.Run(access_token);
-}
-
-void CryptAuthAccessTokenFetcherImpl::OnGetTokenFailure(
- const OAuth2TokenService::Request* request,
- const GoogleServiceAuthError& error) {
- callback_.Run(std::string());
-}
-
-} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.h b/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.h
deleted file mode 100644
index f683b54c038..00000000000
--- a/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl.h
+++ /dev/null
@@ -1,59 +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_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_IMPL_H_
-#define COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_IMPL_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "components/cryptauth/cryptauth_access_token_fetcher.h"
-#include "google_apis/gaia/oauth2_token_service.h"
-
-namespace cryptauth {
-
-// Implementation of CryptAuthAccessTokenFetcher fetching an access token for a
-// given account using the provided OAuth2TokenService.
-class CryptAuthAccessTokenFetcherImpl : public CryptAuthAccessTokenFetcher,
- public OAuth2TokenService::Consumer {
- public:
- // |token_service| is not owned, and must outlive this object.
- CryptAuthAccessTokenFetcherImpl(OAuth2TokenService* token_service,
- const std::string& account_id);
- ~CryptAuthAccessTokenFetcherImpl() override;
-
- // CryptAuthAccessTokenFetcher:
- void FetchAccessToken(const AccessTokenCallback& callback) override;
-
- private:
- // OAuth2TokenService::Consumer:
- void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
- const std::string& access_token,
- const base::Time& expiration_time) override;
- void OnGetTokenFailure(const OAuth2TokenService::Request* request,
- const GoogleServiceAuthError& error) override;
-
- // System service that caches and fetches tokens for a given account.
- // Not owned.
- OAuth2TokenService* token_service_;
-
- // The account id for whom to mint the token.
- std::string account_id_;
-
- // True if FetchAccessToken() has been called.
- bool fetch_started_;
-
- // Stores the request from |token_service_| to mint the token.
- std::unique_ptr<OAuth2TokenService::Request> token_request_;
-
- // Callback to invoke when the token fetch succeeds or fails.
- AccessTokenCallback callback_;
-
- DISALLOW_COPY_AND_ASSIGN(CryptAuthAccessTokenFetcherImpl);
-};
-
-} // namespace cryptauth
-
-#endif // COMPONENTS_CRYPTAUTH_CRYPTAUTH_ACCESS_TOKEN_FETCHER_IMPL_H_
diff --git a/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl_unittest.cc b/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl_unittest.cc
deleted file mode 100644
index 3e72c230cc0..00000000000
--- a/chromium/components/cryptauth/cryptauth_access_token_fetcher_impl_unittest.cc
+++ /dev/null
@@ -1,76 +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/cryptauth/cryptauth_access_token_fetcher_impl.h"
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "google_apis/gaia/fake_oauth2_token_service.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace cryptauth {
-
-namespace {
-
-const char kAccountId[] = "account_id";
-const char kAccessToken[] = "access_token";
-const char kInvalidResult[] = "invalid_result";
-
-// Callback that saves the fetched access token to the first argument.
-void SaveAccessToken(std::string* out_token, const std::string& in_token) {
- *out_token = in_token;
-}
-
-} // namespace
-
-class CryptAuthAccessTokenFetcherTest : public testing::Test {
- protected:
- CryptAuthAccessTokenFetcherTest()
- : fetcher_(&token_service_, kAccountId) {
- token_service_.AddAccount(kAccountId);
- }
-
- FakeOAuth2TokenService token_service_;
- CryptAuthAccessTokenFetcherImpl fetcher_;
-
- DISALLOW_COPY_AND_ASSIGN(CryptAuthAccessTokenFetcherTest);
-};
-
-TEST_F(CryptAuthAccessTokenFetcherTest, FetchSuccess) {
- std::string result;
- fetcher_.FetchAccessToken(base::Bind(SaveAccessToken, &result));
- token_service_.IssueAllTokensForAccount(kAccountId, kAccessToken,
- base::Time::Max());
-
- EXPECT_EQ(kAccessToken, result);
-}
-
-TEST_F(CryptAuthAccessTokenFetcherTest, FetchFailure) {
- std::string result(kInvalidResult);
- fetcher_.FetchAccessToken(base::Bind(SaveAccessToken, &result));
- token_service_.IssueErrorForAllPendingRequestsForAccount(
- kAccountId,
- GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_ERROR));
-
- EXPECT_EQ(std::string(), result);
-}
-
-TEST_F(CryptAuthAccessTokenFetcherTest, FetcherReuse) {
- std::string result1;
- fetcher_.FetchAccessToken(base::Bind(SaveAccessToken, &result1));
-
- {
- std::string result2(kInvalidResult);
- fetcher_.FetchAccessToken(base::Bind(SaveAccessToken, &result2));
- EXPECT_EQ(std::string(), result2);
- }
-
- token_service_.IssueAllTokensForAccount(kAccountId, kAccessToken,
- base::Time::Max());
- EXPECT_EQ(kAccessToken, result1);
-}
-
-} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_api_call_flow.cc b/chromium/components/cryptauth/cryptauth_api_call_flow.cc
index 40023c46251..4f6f17cb0f8 100644
--- a/chromium/components/cryptauth/cryptauth_api_call_flow.cc
+++ b/chromium/components/cryptauth/cryptauth_api_call_flow.cc
@@ -4,6 +4,7 @@
#include "components/cryptauth/cryptauth_api_call_flow.h"
+#include "base/optional.h"
#include "base/strings/string_number_conversions.h"
#include "chromeos/components/proximity_auth/logging/logging.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
@@ -13,9 +14,21 @@ namespace cryptauth {
namespace {
-const char kResponseBodyError[] = "Failed to get response body";
-const char kRequestFailedError[] = "Request failed";
-const char kHttpStatusErrorPrefix[] = "HTTP status: ";
+NetworkRequestError GetErrorForHttpResponseCode(int response_code) {
+ if (response_code == 400)
+ return NetworkRequestError::kBadRequest;
+
+ if (response_code == 403)
+ return NetworkRequestError::kAuthenticationError;
+
+ if (response_code == 404)
+ return NetworkRequestError::kEndpointNotFound;
+
+ if (response_code >= 500 && response_code < 600)
+ return NetworkRequestError::kInternalServerError;
+
+ return NetworkRequestError::kUnknown;
+}
} // namespace
@@ -59,7 +72,7 @@ void CryptAuthApiCallFlow::ProcessApiCallSuccess(
const net::URLFetcher* source) {
std::string serialized_response;
if (!source->GetResponseAsString(&serialized_response)) {
- error_callback_.Run(kResponseBodyError);
+ error_callback_.Run(NetworkRequestError::kResponseMalformed);
return;
}
result_callback_.Run(serialized_response);
@@ -67,18 +80,17 @@ void CryptAuthApiCallFlow::ProcessApiCallSuccess(
void CryptAuthApiCallFlow::ProcessApiCallFailure(
const net::URLFetcher* source) {
- std::string error_message;
+ base::Optional<NetworkRequestError> error;
if (source->GetStatus().status() == net::URLRequestStatus::SUCCESS) {
- error_message =
- kHttpStatusErrorPrefix + base::IntToString(source->GetResponseCode());
+ error = GetErrorForHttpResponseCode(source->GetResponseCode());
} else {
- error_message = kRequestFailedError;
+ error = NetworkRequestError::kOffline;
}
std::string response;
source->GetResponseAsString(&response);
PA_LOG(INFO) << "API call failed:\n" << response;
- error_callback_.Run(error_message);
+ error_callback_.Run(*error);
}
net::PartialNetworkTrafficAnnotationTag
diff --git a/chromium/components/cryptauth/cryptauth_api_call_flow.h b/chromium/components/cryptauth/cryptauth_api_call_flow.h
index ce5e34d294e..1afe771b3ca 100644
--- a/chromium/components/cryptauth/cryptauth_api_call_flow.h
+++ b/chromium/components/cryptauth/cryptauth_api_call_flow.h
@@ -9,6 +9,7 @@
#include "base/callback.h"
#include "base/macros.h"
+#include "components/cryptauth/network_request_error.h"
#include "google_apis/gaia/oauth2_api_call_flow.h"
namespace cryptauth {
@@ -20,7 +21,7 @@ class CryptAuthApiCallFlow : public OAuth2ApiCallFlow {
public:
typedef base::Callback<void(const std::string& serialized_response)>
ResultCallback;
- typedef base::Callback<void(const std::string& error_message)> ErrorCallback;
+ typedef base::Callback<void(NetworkRequestError error)> ErrorCallback;
CryptAuthApiCallFlow();
~CryptAuthApiCallFlow() override;
diff --git a/chromium/components/cryptauth/cryptauth_api_call_flow_unittest.cc b/chromium/components/cryptauth/cryptauth_api_call_flow_unittest.cc
index 1c7b4b74424..68941acab83 100644
--- a/chromium/components/cryptauth/cryptauth_api_call_flow_unittest.cc
+++ b/chromium/components/cryptauth/cryptauth_api_call_flow_unittest.cc
@@ -8,6 +8,7 @@
#include "base/macros.h"
#include "base/test/test_simple_task_runner.h"
+#include "components/cryptauth/network_request_error.h"
#include "net/base/net_errors.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_status.h"
@@ -62,9 +63,9 @@ class CryptAuthApiCallFlowTest
result_.reset(new std::string(result));
}
- void OnError(const std::string& error_message) {
- EXPECT_FALSE(error_message_);
- error_message_.reset(new std::string(error_message));
+ void OnError(NetworkRequestError network_error) {
+ EXPECT_FALSE(network_error_);
+ network_error_.reset(new NetworkRequestError(network_error));
}
void CheckCryptAuthHttpRequest(const std::string& serialized_request) {
@@ -105,7 +106,7 @@ class CryptAuthApiCallFlowTest
net::TestURLFetcher* url_fetcher_;
std::unique_ptr<std::string> result_;
- std::unique_ptr<std::string> error_message_;
+ std::unique_ptr<NetworkRequestError> network_error_;
private:
scoped_refptr<net::TestURLRequestContextGetter> url_request_context_getter_;
@@ -119,14 +120,14 @@ TEST_F(CryptAuthApiCallFlowTest, RequestSuccess) {
StartApiCallFlow();
CompleteCurrentRequest(net::OK, net::HTTP_OK, kSerializedResponseProto);
EXPECT_EQ(kSerializedResponseProto, *result_);
- EXPECT_FALSE(error_message_);
+ EXPECT_FALSE(network_error_);
}
TEST_F(CryptAuthApiCallFlowTest, RequestFailure) {
StartApiCallFlow();
CompleteCurrentRequest(net::ERR_FAILED, 0, std::string());
EXPECT_FALSE(result_);
- EXPECT_EQ("Request failed", *error_message_);
+ EXPECT_EQ(NetworkRequestError::kOffline, *network_error_);
}
TEST_F(CryptAuthApiCallFlowTest, RequestStatus500) {
@@ -134,7 +135,7 @@ TEST_F(CryptAuthApiCallFlowTest, RequestStatus500) {
CompleteCurrentRequest(net::OK, net::HTTP_INTERNAL_SERVER_ERROR,
"CryptAuth Meltdown.");
EXPECT_FALSE(result_);
- EXPECT_EQ("HTTP status: 500", *error_message_);
+ EXPECT_EQ(NetworkRequestError::kInternalServerError, *network_error_);
}
// The empty string is a valid protocol buffer message serialization.
@@ -142,7 +143,7 @@ TEST_F(CryptAuthApiCallFlowTest, RequestWithNoBody) {
StartApiCallFlowWithRequest(std::string());
CompleteCurrentRequest(net::OK, net::HTTP_OK, kSerializedResponseProto);
EXPECT_EQ(kSerializedResponseProto, *result_);
- EXPECT_FALSE(error_message_);
+ EXPECT_FALSE(network_error_);
}
// The empty string is a valid protocol buffer message serialization.
@@ -150,7 +151,7 @@ TEST_F(CryptAuthApiCallFlowTest, ResponseWithNoBody) {
StartApiCallFlow();
CompleteCurrentRequest(net::OK, net::HTTP_OK, std::string());
EXPECT_EQ(std::string(), *result_);
- EXPECT_FALSE(error_message_);
+ EXPECT_FALSE(network_error_);
}
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_client.h b/chromium/components/cryptauth/cryptauth_client.h
index 1fe98a91bb6..868748fc347 100644
--- a/chromium/components/cryptauth/cryptauth_client.h
+++ b/chromium/components/cryptauth/cryptauth_client.h
@@ -10,6 +10,7 @@
#include "base/callback_forward.h"
#include "base/macros.h"
+#include "components/cryptauth/network_request_error.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
namespace cryptauth {
@@ -39,7 +40,7 @@ namespace cryptauth {
// components/cryptauth/proto/cryptauth_api.proto
class CryptAuthClient {
public:
- typedef base::Callback<void(const std::string&)> ErrorCallback;
+ typedef base::Callback<void(NetworkRequestError)> ErrorCallback;
virtual ~CryptAuthClient() {}
diff --git a/chromium/components/cryptauth/cryptauth_client_impl.cc b/chromium/components/cryptauth/cryptauth_client_impl.cc
index f16b4a29bf1..5ab24f1b74e 100644
--- a/chromium/components/cryptauth/cryptauth_client_impl.cc
+++ b/chromium/components/cryptauth/cryptauth_client_impl.cc
@@ -10,8 +10,10 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/memory/ptr_util.h"
-#include "components/cryptauth/cryptauth_access_token_fetcher_impl.h"
+#include "chromeos/components/proximity_auth/logging/logging.h"
#include "components/cryptauth/switches.h"
+#include "services/identity/public/cpp/identity_manager.h"
+#include "services/identity/public/cpp/primary_account_access_token_fetcher.h"
namespace cryptauth {
@@ -53,11 +55,11 @@ GURL CreateRequestUrl(const std::string& request_path) {
CryptAuthClientImpl::CryptAuthClientImpl(
std::unique_ptr<CryptAuthApiCallFlow> api_call_flow,
- std::unique_ptr<CryptAuthAccessTokenFetcher> access_token_fetcher,
+ identity::IdentityManager* identity_manager,
scoped_refptr<net::URLRequestContextGetter> url_request_context,
const DeviceClassifier& device_classifier)
: api_call_flow_(std::move(api_call_flow)),
- access_token_fetcher_(std::move(access_token_fetcher)),
+ identity_manager_(identity_manager),
url_request_context_(url_request_context),
device_classifier_(device_classifier),
has_call_started_(false),
@@ -255,8 +257,9 @@ void CryptAuthClientImpl::MakeApiCall(
const ErrorCallback& error_callback,
const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
if (has_call_started_) {
- error_callback.Run(
- "Client has been used for another request. Do not reuse.");
+ PA_LOG(ERROR) << "CryptAuthClientImpl::MakeApiCall(): Tried to make an API "
+ << "call, but the client had already been used.";
+ NOTREACHED();
return;
}
has_call_started_ = true;
@@ -270,32 +273,45 @@ void CryptAuthClientImpl::MakeApiCall(
std::string serialized_request;
if (!request_copy.SerializeToString(&serialized_request)) {
- error_callback.Run(std::string("Failed to serialize ") +
- request_proto.GetTypeName() + " proto.");
+ PA_LOG(ERROR) << "CryptAuthClientImpl::MakeApiCall(): Failure serializing "
+ << "request proto.";
+ NOTREACHED();
return;
}
request_path_ = request_path;
error_callback_ = error_callback;
- access_token_fetcher_->FetchAccessToken(base::Bind(
- &CryptAuthClientImpl::OnAccessTokenFetched<ResponseProto>,
- weak_ptr_factory_.GetWeakPtr(), serialized_request, response_callback));
+
+ OAuth2TokenService::ScopeSet scopes;
+ scopes.insert("https://www.googleapis.com/auth/cryptauth");
+
+ access_token_fetcher_ =
+ std::make_unique<identity::PrimaryAccountAccessTokenFetcher>(
+ "cryptauth_client", identity_manager_, scopes,
+ base::BindOnce(
+ &CryptAuthClientImpl::OnAccessTokenFetched<ResponseProto>,
+ weak_ptr_factory_.GetWeakPtr(), serialized_request,
+ response_callback),
+ identity::PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
}
template <class ResponseProto>
void CryptAuthClientImpl::OnAccessTokenFetched(
const std::string& serialized_request,
const base::Callback<void(const ResponseProto&)>& response_callback,
- const std::string& access_token) {
- if (access_token.empty()) {
- OnApiCallFailed("Failed to get a valid access token.");
+ GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info) {
+ access_token_fetcher_.reset();
+
+ if (error.state() != GoogleServiceAuthError::NONE) {
+ OnApiCallFailed(NetworkRequestError::kAuthenticationError);
return;
}
- access_token_used_ = access_token;
+ access_token_used_ = access_token_info.token;
api_call_flow_->Start(
- CreateRequestUrl(request_path_), url_request_context_.get(), access_token,
- serialized_request,
+ CreateRequestUrl(request_path_), url_request_context_.get(),
+ access_token_used_, serialized_request,
base::Bind(&CryptAuthClientImpl::OnFlowSuccess<ResponseProto>,
weak_ptr_factory_.GetWeakPtr(), response_callback),
base::Bind(&CryptAuthClientImpl::OnApiCallFailed,
@@ -308,36 +324,31 @@ void CryptAuthClientImpl::OnFlowSuccess(
const std::string& serialized_response) {
ResponseProto response;
if (!response.ParseFromString(serialized_response)) {
- OnApiCallFailed("Failed to parse response proto.");
+ OnApiCallFailed(NetworkRequestError::kResponseMalformed);
return;
}
result_callback.Run(response);
};
-void CryptAuthClientImpl::OnApiCallFailed(const std::string& error_message) {
- error_callback_.Run(error_message);
+void CryptAuthClientImpl::OnApiCallFailed(NetworkRequestError error) {
+ error_callback_.Run(error);
}
// CryptAuthClientFactoryImpl
CryptAuthClientFactoryImpl::CryptAuthClientFactoryImpl(
- OAuth2TokenService* token_service,
- const std::string& account_id,
+ identity::IdentityManager* identity_manager,
scoped_refptr<net::URLRequestContextGetter> url_request_context,
const DeviceClassifier& device_classifier)
- : token_service_(token_service),
- account_id_(account_id),
+ : identity_manager_(identity_manager),
url_request_context_(url_request_context),
- device_classifier_(device_classifier) {
-}
+ device_classifier_(device_classifier) {}
CryptAuthClientFactoryImpl::~CryptAuthClientFactoryImpl() {
}
std::unique_ptr<CryptAuthClient> CryptAuthClientFactoryImpl::CreateInstance() {
return std::make_unique<CryptAuthClientImpl>(
- base::WrapUnique(new CryptAuthApiCallFlow()),
- base::WrapUnique(
- new CryptAuthAccessTokenFetcherImpl(token_service_, account_id_)),
+ base::WrapUnique(new CryptAuthApiCallFlow()), identity_manager_,
url_request_context_, device_classifier_);
}
diff --git a/chromium/components/cryptauth/cryptauth_client_impl.h b/chromium/components/cryptauth/cryptauth_client_impl.h
index 3ccea85148d..fe5902a2d1f 100644
--- a/chromium/components/cryptauth/cryptauth_client_impl.h
+++ b/chromium/components/cryptauth/cryptauth_client_impl.h
@@ -7,14 +7,18 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
-#include "components/cryptauth/cryptauth_access_token_fetcher.h"
#include "components/cryptauth/cryptauth_api_call_flow.h"
#include "components/cryptauth/cryptauth_client.h"
#include "components/cryptauth/proto/cryptauth_api.pb.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "net/url_request/url_request_context_getter.h"
+#include "services/identity/public/cpp/access_token_info.h"
-class OAuth2TokenService;
+namespace identity {
+class IdentityManager;
+class PrimaryAccountAccessTokenFetcher;
+} // namespace identity
+class GoogleServiceAuthError;
namespace cryptauth {
@@ -23,16 +27,12 @@ namespace cryptauth {
// messages. CryptAuthClient will fill this field for all requests.
class CryptAuthClientImpl : public CryptAuthClient {
public:
- typedef base::Callback<void(const std::string&)> ErrorCallback;
-
// Creates the client using |url_request_context| to make the HTTP request
- // through |api_call_flow|. CryptAuthClientImpl takes ownership of
- // |access_token_fetcher|, which provides the access token authorizing
- // CryptAuth requests. The |device_classifier| argument contains basic device
- // information of the caller (e.g. version and device type).
+ // through |api_call_flow|. The |device_classifier| argument contains basic
+ // device information of the caller (e.g. version and device type).
CryptAuthClientImpl(
std::unique_ptr<CryptAuthApiCallFlow> api_call_flow,
- std::unique_ptr<CryptAuthAccessTokenFetcher> access_token_fetcher,
+ identity::IdentityManager* identity_manager,
scoped_refptr<net::URLRequestContextGetter> url_request_context,
const DeviceClassifier& device_classifier);
~CryptAuthClientImpl() override;
@@ -85,7 +85,8 @@ class CryptAuthClientImpl : public CryptAuthClient {
void OnAccessTokenFetched(
const std::string& serialized_request,
const base::Callback<void(const ResponseProto&)>& response_callback,
- const std::string& access_token);
+ GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info);
// Called with CryptAuthApiCallFlow completes successfully to deserialize and
// return the result.
@@ -95,13 +96,16 @@ class CryptAuthClientImpl : public CryptAuthClient {
const std::string& serialized_response);
// Called when the current API call fails at any step.
- void OnApiCallFailed(const std::string& error_message);
+ void OnApiCallFailed(NetworkRequestError error);
// Constructs and executes the actual HTTP request.
std::unique_ptr<CryptAuthApiCallFlow> api_call_flow_;
+ identity::IdentityManager* identity_manager_;
+
// Fetches the access token authorizing the API calls.
- std::unique_ptr<CryptAuthAccessTokenFetcher> access_token_fetcher_;
+ std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher>
+ access_token_fetcher_;
// The context for network requests.
scoped_refptr<net::URLRequestContextGetter> url_request_context_;
@@ -131,14 +135,12 @@ class CryptAuthClientImpl : public CryptAuthClient {
// Implementation of CryptAuthClientFactory.
class CryptAuthClientFactoryImpl : public CryptAuthClientFactory {
public:
- // |token_service|: Gets the user's access token.
- // Not owned, so |token_service| needs to outlive this object.
- // |account_id|: The account id of the user.
+ // |identity_manager|: Gets the user's access token.
+ // Not owned, so |identity_manager| needs to outlive this object.
// |url_request_context|: The request context to make the HTTP requests.
// |device_classifier|: Contains basic device information of the client.
CryptAuthClientFactoryImpl(
- OAuth2TokenService* token_service,
- const std::string& account_id,
+ identity::IdentityManager* identity_manager,
scoped_refptr<net::URLRequestContextGetter> url_request_context,
const DeviceClassifier& device_classifier);
~CryptAuthClientFactoryImpl() override;
@@ -147,8 +149,7 @@ class CryptAuthClientFactoryImpl : public CryptAuthClientFactory {
std::unique_ptr<CryptAuthClient> CreateInstance() override;
private:
- OAuth2TokenService* token_service_;
- const std::string account_id_;
+ identity::IdentityManager* identity_manager_;
const scoped_refptr<net::URLRequestContextGetter> url_request_context_;
const DeviceClassifier device_classifier_;
diff --git a/chromium/components/cryptauth/cryptauth_client_impl_unittest.cc b/chromium/components/cryptauth/cryptauth_client_impl_unittest.cc
index 960278d0699..caa24bcb2a4 100644
--- a/chromium/components/cryptauth/cryptauth_client_impl_unittest.cc
+++ b/chromium/components/cryptauth/cryptauth_client_impl_unittest.cc
@@ -7,15 +7,16 @@
#include "base/command_line.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
+#include "base/test/gtest_util.h"
#include "base/test/null_task_runner.h"
-#include "components/cryptauth/cryptauth_access_token_fetcher.h"
+#include "base/test/scoped_task_environment.h"
#include "components/cryptauth/cryptauth_api_call_flow.h"
#include "components/cryptauth/proto/cryptauth_api.pb.h"
#include "components/cryptauth/switches.h"
-#include "google_apis/gaia/fake_oauth2_token_service.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_test_util.h"
+#include "services/identity/public/cpp/identity_test_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -32,6 +33,7 @@ namespace {
const char kTestGoogleApisUrl[] = "https://www.testgoogleapis.com";
const char kAccessToken[] = "access_token";
+const char kEmail[] = "test@gmail.com";
const char kPublicKey1[] = "public_key1";
const char kPublicKey2[] = "public_key2";
const char kBluetoothAddress1[] = "AA:AA:AA:AA:AA:AA";
@@ -43,24 +45,6 @@ const int kDeviceSoftwareVersionCode = 200;
const char kDeviceSoftwarePackage[] = "cryptauth_client_unittest";
const DeviceType kDeviceType = CHROME;
-// CryptAuthAccessTokenFetcher implementation simply returning a predetermined
-// access token.
-class FakeCryptAuthAccessTokenFetcher : public CryptAuthAccessTokenFetcher {
- public:
- FakeCryptAuthAccessTokenFetcher() : access_token_(kAccessToken) {}
-
- void FetchAccessToken(const AccessTokenCallback& callback) override {
- callback.Run(access_token_);
- }
-
- void set_access_token(const std::string& access_token) {
- access_token_ = access_token;
- };
-
- private:
- std::string access_token_;
-};
-
// Mock CryptAuthApiCallFlow, which handles the HTTP requests to CryptAuth.
class MockCryptAuthApiCallFlow : public CryptAuthApiCallFlow {
public:
@@ -83,13 +67,25 @@ class MockCryptAuthApiCallFlow : public CryptAuthApiCallFlow {
// Callback that should never be invoked.
template <class T>
-void NotCalled(const T& type) {
+void NotCalled(T type) {
EXPECT_TRUE(false);
}
+// Callback that should never be invoked.
+template <class T>
+void NotCalledConstRef(const T& type) {
+ EXPECT_TRUE(false);
+}
+
+// Callback that saves the result returned by CryptAuthClient.
+template <class T>
+void SaveResult(T* out, T result) {
+ *out = result;
+}
+
// Callback that saves the result returned by CryptAuthClient.
template <class T>
-void SaveResult(T* out, const T& result) {
+void SaveResultConstRef(T* out, const T& result) {
*out = result;
}
@@ -98,8 +94,7 @@ void SaveResult(T* out, const T& result) {
class CryptAuthClientTest : public testing::Test {
protected:
CryptAuthClientTest()
- : access_token_fetcher_(new FakeCryptAuthAccessTokenFetcher()),
- api_call_flow_(new StrictMock<MockCryptAuthApiCallFlow>()),
+ : api_call_flow_(new StrictMock<MockCryptAuthApiCallFlow>()),
url_request_context_(
new net::TestURLRequestContextGetter(new base::NullTaskRunner())),
serialized_request_(std::string()) {}
@@ -115,9 +110,11 @@ class CryptAuthClientTest : public testing::Test {
device_classifier.set_device_software_package(kDeviceSoftwarePackage);
device_classifier.set_device_type(kDeviceType);
+ identity_test_environment_.MakePrimaryAccountAvailable(kEmail);
+
client_.reset(
new CryptAuthClientImpl(base::WrapUnique(api_call_flow_),
- base::WrapUnique(access_token_fetcher_),
+ identity_test_environment_.identity_manager(),
url_request_context_, device_classifier));
}
@@ -138,15 +135,15 @@ class CryptAuthClientTest : public testing::Test {
flow_result_callback_.Run(response_proto->SerializeAsString());
}
- // Ends the current API request with |error_message|. ExpectResult() must have
- // been called first.
- void FailApiCallFlow(const std::string& error_message) {
- flow_error_callback_.Run(error_message);
+ // Ends the current API request with |error|. ExpectResult() must have been
+ // called first.
+ void FailApiCallFlow(NetworkRequestError error) {
+ flow_error_callback_.Run(error);
}
protected:
- // Owned by |client_|.
- FakeCryptAuthAccessTokenFetcher* access_token_fetcher_;
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ identity::IdentityTestEnvironment identity_test_environment_;
// Owned by |client_|.
StrictMock<MockCryptAuthApiCallFlow>* api_call_flow_;
@@ -168,9 +165,12 @@ TEST_F(CryptAuthClientTest, GetMyDevicesSuccess) {
request_proto.set_allow_stale_read(true);
client_->GetMyDevices(
request_proto,
- base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
- base::Bind(&NotCalled<std::string>),
+ base::Bind(&SaveResultConstRef<GetMyDevicesResponse>, &result_proto),
+ base::Bind(&NotCalled<NetworkRequestError>),
PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ kAccessToken, base::Time::Max());
GetMyDevicesRequest expected_request;
EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
@@ -204,15 +204,17 @@ TEST_F(CryptAuthClientTest, GetMyDevicesFailure) {
"https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
"getmydevices?alt=proto");
- std::string error_message;
+ NetworkRequestError error;
client_->GetMyDevices(GetMyDevicesRequest(),
- base::Bind(&NotCalled<GetMyDevicesResponse>),
- base::Bind(&SaveResult<std::string>, &error_message),
+ base::Bind(&NotCalledConstRef<GetMyDevicesResponse>),
+ base::Bind(&SaveResult<NetworkRequestError>, &error),
PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ kAccessToken, base::Time::Max());
- std::string kStatus500Error("HTTP status: 500");
- FailApiCallFlow(kStatus500Error);
- EXPECT_EQ(kStatus500Error, error_message);
+ FailApiCallFlow(NetworkRequestError::kInternalServerError);
+ EXPECT_EQ(NetworkRequestError::kInternalServerError, error);
}
TEST_F(CryptAuthClientTest, FindEligibleUnlockDevicesSuccess) {
@@ -225,9 +227,12 @@ TEST_F(CryptAuthClientTest, FindEligibleUnlockDevicesSuccess) {
request_proto.set_callback_bluetooth_address(kBluetoothAddress2);
client_->FindEligibleUnlockDevices(
request_proto,
- base::Bind(&SaveResult<FindEligibleUnlockDevicesResponse>,
+ base::Bind(&SaveResultConstRef<FindEligibleUnlockDevicesResponse>,
&result_proto),
- base::Bind(&NotCalled<std::string>));
+ base::Bind(&NotCalled<NetworkRequestError>));
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ kAccessToken, base::Time::Max());
FindEligibleUnlockDevicesRequest expected_request;
EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
@@ -264,17 +269,19 @@ TEST_F(CryptAuthClientTest, FindEligibleUnlockDevicesFailure) {
"https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
"findeligibleunlockdevices?alt=proto");
- std::string error_message;
+ NetworkRequestError error;
FindEligibleUnlockDevicesRequest request_proto;
request_proto.set_callback_bluetooth_address(kBluetoothAddress1);
client_->FindEligibleUnlockDevices(
request_proto,
- base::Bind(&NotCalled<FindEligibleUnlockDevicesResponse>),
- base::Bind(&SaveResult<std::string>, &error_message));
-
- std::string kStatus403Error("HTTP status: 403");
- FailApiCallFlow(kStatus403Error);
- EXPECT_EQ(kStatus403Error, error_message);
+ base::Bind(&NotCalledConstRef<FindEligibleUnlockDevicesResponse>),
+ base::Bind(&SaveResult<NetworkRequestError>, &error));
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ kAccessToken, base::Time::Max());
+
+ FailApiCallFlow(NetworkRequestError::kAuthenticationError);
+ EXPECT_EQ(NetworkRequestError::kAuthenticationError, error);
}
TEST_F(CryptAuthClientTest, FindEligibleForPromotionSuccess) {
@@ -285,8 +292,12 @@ TEST_F(CryptAuthClientTest, FindEligibleForPromotionSuccess) {
FindEligibleForPromotionResponse result_proto;
client_->FindEligibleForPromotion(
FindEligibleForPromotionRequest(),
- base::Bind(&SaveResult<FindEligibleForPromotionResponse>, &result_proto),
- base::Bind(&NotCalled<std::string>));
+ base::Bind(&SaveResultConstRef<FindEligibleForPromotionResponse>,
+ &result_proto),
+ base::Bind(&NotCalled<NetworkRequestError>));
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ kAccessToken, base::Time::Max());
FindEligibleForPromotionRequest expected_request;
EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
@@ -303,9 +314,13 @@ TEST_F(CryptAuthClientTest, SendDeviceSyncTickleSuccess) {
SendDeviceSyncTickleResponse result_proto;
client_->SendDeviceSyncTickle(
SendDeviceSyncTickleRequest(),
- base::Bind(&SaveResult<SendDeviceSyncTickleResponse>, &result_proto),
- base::Bind(&NotCalled<std::string>),
+ base::Bind(&SaveResultConstRef<SendDeviceSyncTickleResponse>,
+ &result_proto),
+ base::Bind(&NotCalled<NetworkRequestError>),
PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ kAccessToken, base::Time::Max());
SendDeviceSyncTickleRequest expected_request;
EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
@@ -326,9 +341,11 @@ TEST_F(CryptAuthClientTest, ToggleEasyUnlockSuccess) {
request_proto.set_public_key(kPublicKey1);
client_->ToggleEasyUnlock(
request_proto,
- base::Bind(&SaveResult<ToggleEasyUnlockResponse>,
- &result_proto),
- base::Bind(&NotCalled<std::string>));
+ base::Bind(&SaveResultConstRef<ToggleEasyUnlockResponse>, &result_proto),
+ base::Bind(&NotCalled<NetworkRequestError>));
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ kAccessToken, base::Time::Max());
ToggleEasyUnlockRequest expected_request;
EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
@@ -356,9 +373,12 @@ TEST_F(CryptAuthClientTest, SetupEnrollmentSuccess) {
request_proto.add_types("gcmV1");
request_proto.add_types("testProtocol");
client_->SetupEnrollment(
- request_proto, base::Bind(&SaveResult<SetupEnrollmentResponse>,
- &result_proto),
- base::Bind(&NotCalled<std::string>));
+ request_proto,
+ base::Bind(&SaveResultConstRef<SetupEnrollmentResponse>, &result_proto),
+ base::Bind(&NotCalled<NetworkRequestError>));
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ kAccessToken, base::Time::Max());
SetupEnrollmentRequest expected_request;
EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
@@ -401,9 +421,11 @@ TEST_F(CryptAuthClientTest, FinishEnrollmentSuccess) {
request_proto.set_device_ephemeral_key(kDeviceEphemeralKey);
client_->FinishEnrollment(
request_proto,
- base::Bind(&SaveResult<FinishEnrollmentResponse>,
- &result_proto),
- base::Bind(&NotCalled<const std::string&>));
+ base::Bind(&SaveResultConstRef<FinishEnrollmentResponse>, &result_proto),
+ base::Bind(&NotCalled<NetworkRequestError>));
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ kAccessToken, base::Time::Max());
FinishEnrollmentRequest expected_request;
EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
@@ -420,15 +442,16 @@ TEST_F(CryptAuthClientTest, FinishEnrollmentSuccess) {
}
TEST_F(CryptAuthClientTest, FetchAccessTokenFailure) {
- access_token_fetcher_->set_access_token("");
-
- std::string error_message;
+ NetworkRequestError error;
client_->GetMyDevices(GetMyDevicesRequest(),
- base::Bind(&NotCalled<GetMyDevicesResponse>),
- base::Bind(&SaveResult<std::string>, &error_message),
+ base::Bind(&NotCalledConstRef<GetMyDevicesResponse>),
+ base::Bind(&SaveResult<NetworkRequestError>, &error),
PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
+ GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
- EXPECT_EQ("Failed to get a valid access token.", error_message);
+ EXPECT_EQ(NetworkRequestError::kAuthenticationError, error);
}
TEST_F(CryptAuthClientTest, ParseResponseProtoFailure) {
@@ -436,14 +459,17 @@ TEST_F(CryptAuthClientTest, ParseResponseProtoFailure) {
"https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
"getmydevices?alt=proto");
- std::string error_message;
+ NetworkRequestError error;
client_->GetMyDevices(GetMyDevicesRequest(),
- base::Bind(&NotCalled<GetMyDevicesResponse>),
- base::Bind(&SaveResult<std::string>, &error_message),
+ base::Bind(&NotCalledConstRef<GetMyDevicesResponse>),
+ base::Bind(&SaveResult<NetworkRequestError>, &error),
PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ kAccessToken, base::Time::Max());
flow_result_callback_.Run("Not a valid serialized response message.");
- EXPECT_EQ("Failed to parse response proto.", error_message);
+ EXPECT_EQ(NetworkRequestError::kResponseMalformed, error);
}
TEST_F(CryptAuthClientTest,
@@ -456,19 +482,20 @@ TEST_F(CryptAuthClientTest,
GetMyDevicesResponse result_proto;
client_->GetMyDevices(
GetMyDevicesRequest(),
- base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
- base::Bind(&NotCalled<std::string>),
+ base::Bind(&SaveResultConstRef<GetMyDevicesResponse>, &result_proto),
+ base::Bind(&NotCalled<NetworkRequestError>),
PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ kAccessToken, base::Time::Max());
// With request pending, make second request.
{
- std::string error_message;
- client_->FindEligibleUnlockDevices(
+ NetworkRequestError error;
+ EXPECT_DCHECK_DEATH(client_->FindEligibleUnlockDevices(
FindEligibleUnlockDevicesRequest(),
- base::Bind(&NotCalled<FindEligibleUnlockDevicesResponse>),
- base::Bind(&SaveResult<std::string>, &error_message));
- EXPECT_EQ("Client has been used for another request. Do not reuse.",
- error_message);
+ base::Bind(&NotCalledConstRef<FindEligibleUnlockDevicesResponse>),
+ base::Bind(&SaveResult<NetworkRequestError>, &error)));
}
// Complete first request.
@@ -484,36 +511,6 @@ TEST_F(CryptAuthClientTest,
}
TEST_F(CryptAuthClientTest,
- MakeSecondRequestBeforeFirstRequestFails) {
- ExpectRequest(
- "https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
- "getmydevices?alt=proto");
-
- // Make first request.
- std::string error_message;
- client_->GetMyDevices(GetMyDevicesRequest(),
- base::Bind(&NotCalled<GetMyDevicesResponse>),
- base::Bind(&SaveResult<std::string>, &error_message),
- PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
-
- // With request pending, make second request.
- {
- std::string error_message;
- client_->FindEligibleUnlockDevices(
- FindEligibleUnlockDevicesRequest(),
- base::Bind(&NotCalled<FindEligibleUnlockDevicesResponse>),
- base::Bind(&SaveResult<std::string>, &error_message));
- EXPECT_EQ("Client has been used for another request. Do not reuse.",
- error_message);
- }
-
- // Fail first request.
- std::string kStatus429Error = "HTTP status: 429";
- FailApiCallFlow(kStatus429Error);
- EXPECT_EQ(kStatus429Error, error_message);
-}
-
-TEST_F(CryptAuthClientTest,
MakeSecondRequestAfterFirstRequestSucceeds) {
// Make first request successfully.
{
@@ -523,9 +520,12 @@ TEST_F(CryptAuthClientTest,
GetMyDevicesResponse result_proto;
client_->GetMyDevices(
GetMyDevicesRequest(),
- base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
- base::Bind(&NotCalled<std::string>),
+ base::Bind(&SaveResultConstRef<GetMyDevicesResponse>, &result_proto),
+ base::Bind(&NotCalled<NetworkRequestError>),
PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ kAccessToken, base::Time::Max());
GetMyDevicesResponse response_proto;
response_proto.add_devices();
@@ -537,13 +537,11 @@ TEST_F(CryptAuthClientTest,
// Second request fails.
{
- std::string error_message;
- client_->FindEligibleUnlockDevices(
+ NetworkRequestError error;
+ EXPECT_DCHECK_DEATH(client_->FindEligibleUnlockDevices(
FindEligibleUnlockDevicesRequest(),
- base::Bind(&NotCalled<FindEligibleUnlockDevicesResponse>),
- base::Bind(&SaveResult<std::string>, &error_message));
- EXPECT_EQ("Client has been used for another request. Do not reuse.",
- error_message);
+ base::Bind(&NotCalledConstRef<FindEligibleUnlockDevicesResponse>),
+ base::Bind(&SaveResult<NetworkRequestError>, &error)));
}
}
@@ -557,9 +555,12 @@ TEST_F(CryptAuthClientTest, DeviceClassifierIsSet) {
request_proto.set_allow_stale_read(true);
client_->GetMyDevices(
request_proto,
- base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
- base::Bind(&NotCalled<std::string>),
+ base::Bind(&SaveResultConstRef<GetMyDevicesResponse>, &result_proto),
+ base::Bind(&NotCalled<NetworkRequestError>),
PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ kAccessToken, base::Time::Max());
GetMyDevicesRequest expected_request;
EXPECT_TRUE(expected_request.ParseFromString(serialized_request_));
@@ -575,6 +576,7 @@ TEST_F(CryptAuthClientTest, DeviceClassifierIsSet) {
TEST_F(CryptAuthClientTest, GetAccessTokenUsed) {
EXPECT_TRUE(client_->GetAccessTokenUsed().empty());
+
ExpectRequest(
"https://www.testgoogleapis.com/cryptauth/v1/deviceSync/"
"getmydevices?alt=proto");
@@ -584,9 +586,13 @@ TEST_F(CryptAuthClientTest, GetAccessTokenUsed) {
request_proto.set_allow_stale_read(true);
client_->GetMyDevices(
request_proto,
- base::Bind(&SaveResult<GetMyDevicesResponse>, &result_proto),
- base::Bind(&NotCalled<std::string>),
+ base::Bind(&SaveResultConstRef<GetMyDevicesResponse>, &result_proto),
+ base::Bind(&NotCalled<NetworkRequestError>),
PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS);
+ identity_test_environment_
+ .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ kAccessToken, base::Time::Max());
+
EXPECT_EQ(kAccessToken, client_->GetAccessTokenUsed());
}
diff --git a/chromium/components/cryptauth/cryptauth_device_manager_impl.cc b/chromium/components/cryptauth/cryptauth_device_manager_impl.cc
index cc07d6fd88a..989dc93f777 100644
--- a/chromium/components/cryptauth/cryptauth_device_manager_impl.cc
+++ b/chromium/components/cryptauth/cryptauth_device_manager_impl.cc
@@ -110,7 +110,9 @@ void RecordDeviceSyncSoftwareFeaturesResult(bool success) {
std::unique_ptr<base::DictionaryValue>
SupportedAndEnabledSoftwareFeaturesToDictionaryValue(
const google::protobuf::RepeatedField<int>& supported_software_features,
- const google::protobuf::RepeatedField<int>& enabled_software_features) {
+ const google::protobuf::RepeatedField<int>& enabled_software_features,
+ bool legacy_unlock_key,
+ bool legacy_mobile_hotspot_supported) {
std::unique_ptr<base::DictionaryValue> dictionary =
std::make_unique<base::DictionaryValue>();
@@ -140,6 +142,25 @@ SupportedAndEnabledSoftwareFeaturesToDictionaryValue(
static_cast<int>(SoftwareFeatureState::kEnabled));
}
+ // If software features for EASY_UNLOCK_HOST or MAGIC_TETHER_HOST have not
+ // been set, check to see if the deprecated corresponding booleans are
+ // enabled. This can happen if the CryptAuth server is not yet serving
+ // software features, and only serving the deprecated booleans.
+ int software_feature_state;
+ std::string software_feature_key;
+ software_feature_key = std::to_string(SoftwareFeature::EASY_UNLOCK_HOST);
+ if (legacy_unlock_key &&
+ !dictionary->GetInteger(software_feature_key, &software_feature_state)) {
+ dictionary->SetInteger(software_feature_key,
+ static_cast<int>(SoftwareFeatureState::kEnabled));
+ }
+ software_feature_key = std::to_string(SoftwareFeature::MAGIC_TETHER_HOST);
+ if (legacy_mobile_hotspot_supported &&
+ !dictionary->GetInteger(software_feature_key, &software_feature_state)) {
+ dictionary->SetInteger(software_feature_key,
+ static_cast<int>(SoftwareFeatureState::kSupported));
+ }
+
return dictionary;
}
@@ -180,10 +201,6 @@ std::unique_ptr<base::DictionaryValue> UnlockKeyToDictionary(
bluetooth_address_b64);
}
- if (device.has_unlock_key()) {
- dictionary->SetBoolean(kExternalDeviceKeyUnlockKey, device.unlock_key());
- }
-
if (device.has_unlockable()) {
dictionary->SetBoolean(kExternalDeviceKeyUnlockable, device.unlockable());
}
@@ -193,11 +210,6 @@ std::unique_ptr<base::DictionaryValue> UnlockKeyToDictionary(
std::to_string(device.last_update_time_millis()));
}
- if (device.has_mobile_hotspot_supported()) {
- dictionary->SetBoolean(kExternalDeviceKeyMobileHotspotSupported,
- device.mobile_hotspot_supported());
- }
-
if (device.has_device_type() && DeviceType_IsValid(device.device_type())) {
dictionary->SetInteger(kExternalDeviceKeyDeviceType, device.device_type());
}
@@ -214,10 +226,19 @@ std::unique_ptr<base::DictionaryValue> UnlockKeyToDictionary(
dictionary->SetBoolean(kExternalDeviceKeyPixelPhone, device.pixel_phone());
}
+ // In the case that the CryptAuth server is not yet serving SoftwareFeatures,
+ // but only the deprecated booleans, |unlock_key| and
+ // |mobile_hotspot_supported|, pass in the legacy values in order to correctly
+ // populate the SoftwareFeatures.
+ bool legacy_unlock_key = device.has_unlock_key() && device.unlock_key();
+ bool legacy_mobile_hotspot_supported =
+ device.has_mobile_hotspot_supported() &&
+ device.mobile_hotspot_supported();
dictionary->Set(kDictionaryKeySoftwareFeatures,
SupportedAndEnabledSoftwareFeaturesToDictionaryValue(
device.supported_software_features(),
- device.enabled_software_features()));
+ device.enabled_software_features(), legacy_unlock_key,
+ legacy_mobile_hotspot_supported));
return dictionary;
}
@@ -270,7 +291,9 @@ void AddBeaconSeedsToExternalDevice(const base::ListValue& beacon_seeds,
void AddSoftwareFeaturesToExternalDevice(
const base::DictionaryValue& software_features_dictionary,
- ExternalDeviceInfo* external_device) {
+ ExternalDeviceInfo* external_device,
+ bool old_unlock_key_value_from_prefs,
+ bool old_mobile_hotspot_supported_from_prefs) {
for (const auto& it : software_features_dictionary.DictItems()) {
int software_feature_state;
if (!it.second.GetAsInteger(&software_feature_state)) {
@@ -291,6 +314,31 @@ void AddSoftwareFeaturesToExternalDevice(
break;
}
}
+
+ // ExternalDeviceInfos's |unlock_key| and |mobile_hotspot_supported| fields
+ // are deprecated, but it may be the case that after an update to Chrome, the
+ // prefs reflect the old style of using these deprecated fields, instead of
+ // software features. To work around this, these pref values are migrated to
+ // software features locally.
+ if (old_unlock_key_value_from_prefs) {
+ if (!base::ContainsValue(external_device->supported_software_features(),
+ SoftwareFeature::EASY_UNLOCK_HOST)) {
+ external_device->add_supported_software_features(
+ SoftwareFeature::EASY_UNLOCK_HOST);
+ }
+ if (!base::ContainsValue(external_device->enabled_software_features(),
+ SoftwareFeature::EASY_UNLOCK_HOST)) {
+ external_device->add_enabled_software_features(
+ SoftwareFeature::EASY_UNLOCK_HOST);
+ }
+ }
+ if (old_mobile_hotspot_supported_from_prefs) {
+ if (!base::ContainsValue(external_device->supported_software_features(),
+ SoftwareFeature::MAGIC_TETHER_HOST)) {
+ external_device->add_supported_software_features(
+ SoftwareFeature::MAGIC_TETHER_HOST);
+ }
+ }
}
// Converts an unlock key dictionary stored in user prefs to an
@@ -336,10 +384,8 @@ bool DictionaryToUnlockKey(const base::DictionaryValue& dictionary,
}
}
- bool unlock_key;
- if (dictionary.GetBoolean(kExternalDeviceKeyUnlockKey, &unlock_key))
- external_device->set_unlock_key(unlock_key);
-
+ // TODO(crbug.com/848477): Migrate |unlockable| into
+ // |supported_software_features|.
bool unlockable;
if (dictionary.GetBoolean(kExternalDeviceKeyUnlockable, &unlockable))
external_device->set_unlockable(unlockable);
@@ -357,12 +403,6 @@ bool DictionaryToUnlockKey(const base::DictionaryValue& dictionary,
}
}
- bool mobile_hotspot_supported;
- if (dictionary.GetBoolean(kExternalDeviceKeyMobileHotspotSupported,
- &mobile_hotspot_supported)) {
- external_device->set_mobile_hotspot_supported(mobile_hotspot_supported);
- }
-
int device_type;
if (dictionary.GetInteger(kExternalDeviceKeyDeviceType, &device_type) &&
DeviceType_IsValid(device_type)) {
@@ -381,11 +421,18 @@ bool DictionaryToUnlockKey(const base::DictionaryValue& dictionary,
if (dictionary.GetBoolean(kExternalDeviceKeyPixelPhone, &pixel_phone))
external_device->set_pixel_phone(pixel_phone);
+ bool unlock_key = false;
+ dictionary.GetBoolean(kExternalDeviceKeyUnlockKey, &unlock_key);
+ bool mobile_hotspot_supported = false;
+ dictionary.GetBoolean(kExternalDeviceKeyMobileHotspotSupported,
+ &mobile_hotspot_supported);
+
const base::DictionaryValue* software_features_dictionary;
if (dictionary.GetDictionary(kDictionaryKeySoftwareFeatures,
&software_features_dictionary)) {
AddSoftwareFeaturesToExternalDevice(*software_features_dictionary,
- external_device);
+ external_device, unlock_key,
+ mobile_hotspot_supported);
}
return true;
@@ -514,8 +561,10 @@ std::vector<ExternalDeviceInfo> CryptAuthDeviceManagerImpl::GetUnlockKeys()
const {
std::vector<ExternalDeviceInfo> unlock_keys;
for (const auto& device : synced_devices_) {
- if (device.unlock_key())
+ if (base::ContainsValue(device.enabled_software_features(),
+ SoftwareFeature::EASY_UNLOCK_HOST)) {
unlock_keys.push_back(device);
+ }
}
return unlock_keys;
}
@@ -524,8 +573,11 @@ std::vector<ExternalDeviceInfo> CryptAuthDeviceManagerImpl::GetPixelUnlockKeys()
const {
std::vector<ExternalDeviceInfo> unlock_keys;
for (const auto& device : synced_devices_) {
- if (device.unlock_key() && device.pixel_phone())
+ if (base::ContainsValue(device.enabled_software_features(),
+ SoftwareFeature::EASY_UNLOCK_HOST) &&
+ device.pixel_phone()) {
unlock_keys.push_back(device);
+ }
}
return unlock_keys;
}
@@ -534,8 +586,10 @@ std::vector<ExternalDeviceInfo> CryptAuthDeviceManagerImpl::GetTetherHosts()
const {
std::vector<ExternalDeviceInfo> tether_hosts;
for (const auto& device : synced_devices_) {
- if (device.mobile_hotspot_supported())
+ if (base::ContainsValue(device.supported_software_features(),
+ SoftwareFeature::MAGIC_TETHER_HOST)) {
tether_hosts.push_back(device);
+ }
}
return tether_hosts;
}
@@ -544,7 +598,9 @@ std::vector<ExternalDeviceInfo>
CryptAuthDeviceManagerImpl::GetPixelTetherHosts() const {
std::vector<ExternalDeviceInfo> tether_hosts;
for (const auto& device : synced_devices_) {
- if (device.mobile_hotspot_supported() && device.pixel_phone())
+ if (base::ContainsValue(device.supported_software_features(),
+ SoftwareFeature::MAGIC_TETHER_HOST) &&
+ device.pixel_phone())
tether_hosts.push_back(device);
}
return tether_hosts;
@@ -596,7 +652,7 @@ void CryptAuthDeviceManagerImpl::OnGetMyDevicesSuccess(
}
void CryptAuthDeviceManagerImpl::OnGetMyDevicesFailure(
- const std::string& error) {
+ NetworkRequestError error) {
PA_LOG(ERROR) << "GetMyDevices API failed: " << error;
pref_service_->SetBoolean(prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure,
true);
diff --git a/chromium/components/cryptauth/cryptauth_device_manager_impl.h b/chromium/components/cryptauth/cryptauth_device_manager_impl.h
index 128a81616a2..936cd97c4a0 100644
--- a/chromium/components/cryptauth/cryptauth_device_manager_impl.h
+++ b/chromium/components/cryptauth/cryptauth_device_manager_impl.h
@@ -13,6 +13,7 @@
#include "base/time/time.h"
#include "components/cryptauth/cryptauth_device_manager.h"
#include "components/cryptauth/cryptauth_gcm_manager.h"
+#include "components/cryptauth/network_request_error.h"
#include "components/cryptauth/proto/cryptauth_api.pb.h"
#include "components/cryptauth/sync_scheduler.h"
@@ -94,7 +95,7 @@ class CryptAuthDeviceManagerImpl : public CryptAuthDeviceManager,
// Callback when |cryptauth_client_| completes with the response.
void OnGetMyDevicesSuccess(const GetMyDevicesResponse& response);
- void OnGetMyDevicesFailure(const std::string& error);
+ void OnGetMyDevicesFailure(NetworkRequestError error);
// Used to determine the time.
base::Clock* clock_;
diff --git a/chromium/components/cryptauth/cryptauth_device_manager_impl_unittest.cc b/chromium/components/cryptauth/cryptauth_device_manager_impl_unittest.cc
index 432a24ea84b..ae860b71c9c 100644
--- a/chromium/components/cryptauth/cryptauth_device_manager_impl_unittest.cc
+++ b/chromium/components/cryptauth/cryptauth_device_manager_impl_unittest.cc
@@ -19,6 +19,7 @@
#include "components/cryptauth/fake_cryptauth_gcm_manager.h"
#include "components/cryptauth/mock_cryptauth_client.h"
#include "components/cryptauth/mock_sync_scheduler.h"
+#include "components/cryptauth/network_request_error.h"
#include "components/cryptauth/pref_names.h"
#include "components/cryptauth/software_feature_state.h"
#include "components/prefs/scoped_user_pref_update.h"
@@ -46,20 +47,16 @@ const double kLaterTimeNowSeconds = kInitialTimeNowSeconds + 30;
const double kLastSyncTimeSeconds = kInitialTimeNowSeconds - (60 * 60 * 5);
// Unlock key fields originally stored in the user prefs.
-const char kStoredPublicKey[] = "AAPL";
-const char kStoredDeviceName[] = "iPhone 6";
+const char kStoredPublicKey[] = "storedPublicKey";
+const char kStoredDeviceName[] = "Pixel 2";
const char kStoredBluetoothAddress[] = "12:34:56:78:90:AB";
-const bool kStoredUnlockKey = true;
const bool kStoredUnlockable = false;
-const bool kStoredMobileHotspotSupported = true;
// ExternalDeviceInfo fields for the synced unlock key.
const char kPublicKey1[] = "GOOG";
const char kDeviceName1[] = "Pixel XL";
const char kBluetoothAddress1[] = "aa:bb:cc:ee:dd:ff";
-const bool kUnlockKey1 = true;
const bool kUnlockable1 = false;
-const bool kMobileHotspotSupported1 = true;
const char kBeaconSeed1Data[] = "beaconSeed1Data";
const int64_t kBeaconSeed1StartTime = 123456;
const int64_t kBeaconSeed1EndTime = 123457;
@@ -70,11 +67,9 @@ const bool kArcPlusPlus1 = true;
const bool kPixelPhone1 = true;
// ExternalDeviceInfo fields for a non-synced unlockable device.
-const char kPublicKey2[] = "MSFT";
-const char kDeviceName2[] = "Surface Pro 3";
-const bool kUnlockKey2 = false;
+const char kPublicKey2[] = "CROS";
+const char kDeviceName2[] = "Pixelbook";
const bool kUnlockable2 = true;
-const bool kMobileHotspotSupported2 = false;
const char kBeaconSeed3Data[] = "beaconSeed3Data";
const int64_t kBeaconSeed3StartTime = 123456;
const int64_t kBeaconSeed3EndTime = 123457;
@@ -149,19 +144,20 @@ void ExpectSyncedDevicesAreEqual(
EXPECT_EQ(expected_device.has_pixel_phone(), device.has_pixel_phone());
EXPECT_EQ(expected_device.pixel_phone(), device.pixel_phone());
- ASSERT_EQ(expected_device.supported_software_features_size(),
+ EXPECT_EQ(expected_device.supported_software_features_size(),
device.supported_software_features_size());
- for (int i = 0; i < expected_device.supported_software_features_size();
- i++) {
- EXPECT_EQ(expected_device.supported_software_features(i),
- device.supported_software_features(i));
+ for (const auto& software_feature :
+ expected_device.supported_software_features()) {
+ EXPECT_TRUE(base::ContainsValue(device.supported_software_features(),
+ software_feature));
}
- ASSERT_EQ(expected_device.enabled_software_features_size(),
+ EXPECT_EQ(expected_device.enabled_software_features_size(),
device.enabled_software_features_size());
- for (int i = 0; i < expected_device.enabled_software_features_size(); i++) {
- EXPECT_EQ(expected_device.enabled_software_features(i),
- device.enabled_software_features(i));
+ for (const auto& software_feature :
+ expected_device.enabled_software_features()) {
+ EXPECT_TRUE(base::ContainsValue(device.enabled_software_features(),
+ software_feature));
}
}
}
@@ -410,9 +406,7 @@ class CryptAuthDeviceManagerImplTest
unlock_key.set_public_key(kPublicKey1);
unlock_key.set_friendly_device_name(kDeviceName1);
unlock_key.set_bluetooth_address(kBluetoothAddress1);
- unlock_key.set_unlock_key(kUnlockKey1);
unlock_key.set_unlockable(kUnlockable1);
- unlock_key.set_mobile_hotspot_supported(kMobileHotspotSupported1);
BeaconSeed* seed1 = unlock_key.add_beacon_seeds();
seed1->set_data(kBeaconSeed1Data);
seed1->set_start_time_millis(kBeaconSeed1StartTime);
@@ -424,6 +418,9 @@ class CryptAuthDeviceManagerImplTest
unlock_key.set_arc_plus_plus(kArcPlusPlus1);
unlock_key.set_pixel_phone(kPixelPhone1);
unlock_key.add_supported_software_features(
+ SoftwareFeature::EASY_UNLOCK_HOST);
+ unlock_key.add_enabled_software_features(SoftwareFeature::EASY_UNLOCK_HOST);
+ unlock_key.add_supported_software_features(
SoftwareFeature::BETTER_TOGETHER_HOST);
unlock_key.add_supported_software_features(
SoftwareFeature::BETTER_TOGETHER_CLIENT);
@@ -434,9 +431,7 @@ class CryptAuthDeviceManagerImplTest
ExternalDeviceInfo unlockable_device;
unlockable_device.set_public_key(kPublicKey2);
unlockable_device.set_friendly_device_name(kDeviceName2);
- unlockable_device.set_unlock_key(kUnlockKey2);
unlockable_device.set_unlockable(kUnlockable2);
- unlockable_device.set_mobile_hotspot_supported(kMobileHotspotSupported2);
BeaconSeed* seed3 = unlockable_device.add_beacon_seeds();
seed3->set_data(kBeaconSeed3Data);
seed3->set_start_time_millis(kBeaconSeed3StartTime);
@@ -447,11 +442,11 @@ class CryptAuthDeviceManagerImplTest
seed4->set_end_time_millis(kBeaconSeed4EndTime);
unlockable_device.set_arc_plus_plus(kArcPlusPlus2);
unlockable_device.set_pixel_phone(kPixelPhone2);
- unlock_key.add_supported_software_features(
+ unlockable_device.add_supported_software_features(
SoftwareFeature::MAGIC_TETHER_HOST);
- unlock_key.add_supported_software_features(
+ unlockable_device.add_supported_software_features(
SoftwareFeature::MAGIC_TETHER_CLIENT);
- unlock_key.add_enabled_software_features(
+ unlockable_device.add_enabled_software_features(
SoftwareFeature::MAGIC_TETHER_HOST);
devices_in_response_.push_back(unlockable_device);
}
@@ -490,13 +485,11 @@ class CryptAuthDeviceManagerImplTest
device_dictionary->SetString("public_key", public_key_b64);
device_dictionary->SetString("device_name", device_name_b64);
device_dictionary->SetString("bluetooth_address", bluetooth_address_b64);
- device_dictionary->SetBoolean("unlock_key", kStoredUnlockKey);
device_dictionary->SetBoolean("unlockable", kStoredUnlockable);
- device_dictionary->SetBoolean("mobile_hotspot_supported",
- kStoredMobileHotspotSupported);
device_dictionary->Set("beacon_seeds", std::make_unique<base::ListValue>());
device_dictionary->Set("software_features",
std::make_unique<base::DictionaryValue>());
+
{
ListPrefUpdate update(&pref_service_,
prefs::kCryptAuthDeviceSyncUnlockKeys);
@@ -657,10 +650,60 @@ TEST_F(CryptAuthDeviceManagerImplTest, InitWithExistingPrefs) {
EXPECT_EQ(kStoredPublicKey, synced_devices[0].public_key());
EXPECT_EQ(kStoredDeviceName, synced_devices[0].friendly_device_name());
EXPECT_EQ(kStoredBluetoothAddress, synced_devices[0].bluetooth_address());
- EXPECT_EQ(kStoredUnlockKey, synced_devices[0].unlock_key());
EXPECT_EQ(kStoredUnlockable, synced_devices[0].unlockable());
}
+// ExternalDeviceInfos's |unlock_key| and |mobile_hotspot_supported| fields
+// are deprecated, but it may be the case that after an update to Chrome, the
+// prefs reflect the old style of using these deprecated fields, instead of
+// software features. This test ensures the CryptAuthDeviceManager considers
+// these deprecated booleans, and populates the correct software features.
+TEST_F(
+ CryptAuthDeviceManagerImplTest,
+ InitWithExistingPrefs_MigrateDeprecateBooleansFromPrefsToSoftwareFeature) {
+ ListPrefUpdate update_clear(&pref_service_,
+ prefs::kCryptAuthDeviceSyncUnlockKeys);
+ update_clear.Get()->Clear();
+
+ // Simulate a deprecated device being persisted to prefs.
+ auto device_dictionary = std::make_unique<base::DictionaryValue>();
+ std::string public_key_b64;
+ base::Base64UrlEncode(kStoredPublicKey,
+ base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+ &public_key_b64);
+ device_dictionary->SetString("public_key", public_key_b64);
+ device_dictionary->SetBoolean("unlock_key", true);
+ device_dictionary->SetBoolean("mobile_hotspot_supported", true);
+ device_dictionary->Set("software_features",
+ std::make_unique<base::DictionaryValue>());
+
+ ListPrefUpdate update(&pref_service_, prefs::kCryptAuthDeviceSyncUnlockKeys);
+ update.Get()->Append(std::move(device_dictionary));
+
+ device_manager_.reset(new TestCryptAuthDeviceManager(
+ &clock_, client_factory_.get(), &gcm_manager_, &pref_service_));
+ device_manager_->Start();
+
+ // Ensure that the deprecated booleans are not exposed in the final
+ // ExternalDeviceInfo, but rather in the correct software features.
+ auto synced_devices = device_manager_->GetSyncedDevices();
+ ASSERT_EQ(1u, synced_devices.size());
+ EXPECT_EQ(kStoredPublicKey, synced_devices[0].public_key());
+ EXPECT_FALSE(synced_devices[0].unlock_key());
+ EXPECT_FALSE(synced_devices[0].mobile_hotspot_supported());
+
+ EXPECT_EQ(2, synced_devices[0].supported_software_features().size());
+ EXPECT_TRUE(
+ base::ContainsValue(synced_devices[0].supported_software_features(),
+ SoftwareFeature::EASY_UNLOCK_HOST));
+ EXPECT_TRUE(
+ base::ContainsValue(synced_devices[0].supported_software_features(),
+ SoftwareFeature::MAGIC_TETHER_HOST));
+ EXPECT_EQ(1, synced_devices[0].enabled_software_features().size());
+ EXPECT_TRUE(base::ContainsValue(synced_devices[0].enabled_software_features(),
+ SoftwareFeature::EASY_UNLOCK_HOST));
+}
+
TEST_F(CryptAuthDeviceManagerImplTest, SyncSucceedsForFirstTime) {
pref_service_.ClearPref(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds);
device_manager_->Start();
@@ -714,7 +757,7 @@ TEST_F(CryptAuthDeviceManagerImplTest, ForceSyncFailsThenSucceeds) {
OnSyncFinishedProxy(
CryptAuthDeviceManager::SyncResult::FAILURE,
CryptAuthDeviceManager::DeviceChangeResult::UNCHANGED));
- error_callback_.Run("404");
+ error_callback_.Run(NetworkRequestError::kEndpointNotFound);
EXPECT_EQ(old_sync_time, device_manager_->GetLastSyncTime());
EXPECT_TRUE(pref_service_.GetBoolean(
prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure));
@@ -755,7 +798,7 @@ TEST_F(CryptAuthDeviceManagerImplTest, PeriodicSyncFailsThenSucceeds) {
OnSyncFinishedProxy(
CryptAuthDeviceManager::SyncResult::FAILURE,
CryptAuthDeviceManager::DeviceChangeResult::UNCHANGED));
- error_callback_.Run("401");
+ error_callback_.Run(NetworkRequestError::kAuthenticationError);
EXPECT_EQ(old_sync_time, device_manager_->GetLastSyncTime());
EXPECT_TRUE(pref_service_.GetBoolean(
prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure));
@@ -798,9 +841,7 @@ TEST_F(CryptAuthDeviceManagerImplTest, SyncSameDevice) {
synced_device.set_public_key(kStoredPublicKey);
synced_device.set_friendly_device_name(kStoredDeviceName);
synced_device.set_bluetooth_address(kStoredBluetoothAddress);
- synced_device.set_unlock_key(kStoredUnlockKey);
synced_device.set_unlockable(kStoredUnlockable);
- synced_device.set_mobile_hotspot_supported(kStoredMobileHotspotSupported);
GetMyDevicesResponse get_my_devices_response;
get_my_devices_response.add_devices()->CopyFrom(synced_device);
success_callback_.Run(get_my_devices_response);
@@ -834,7 +875,11 @@ TEST_F(CryptAuthDeviceManagerImplTest, SyncThreeDevices) {
synced_device2.set_public_key("new public key");
synced_device2.set_friendly_device_name("new device name");
synced_device2.set_bluetooth_address("aa:bb:cc:dd:ee:ff");
- synced_device2.set_unlock_key(true);
+ synced_device2.add_supported_software_features(
+ SoftwareFeature::EASY_UNLOCK_HOST);
+ synced_device2.add_enabled_software_features(
+ SoftwareFeature::EASY_UNLOCK_HOST);
+
response.add_devices()->CopyFrom(synced_device2);
std::vector<ExternalDeviceInfo> expected_devices;
@@ -899,11 +944,10 @@ TEST_F(CryptAuthDeviceManagerImplTest, SyncFullyDetailedExternalDeviceInfos) {
// all fields filled out.
ExternalDeviceInfo device_with_only_public_key;
device_with_only_public_key.set_public_key("publicKey1");
- // Currently, CryptAuthDeviceManager only stores devices which are unlock
- // keys, so set_unlock_key(true) must be called here for storage to work.
- // TODO(khorimoto): Remove this when support for storing all types of devices
- // is added.
- device_with_only_public_key.set_unlock_key(true);
+ device_with_only_public_key.add_supported_software_features(
+ SoftwareFeature::EASY_UNLOCK_HOST);
+ device_with_only_public_key.add_enabled_software_features(
+ SoftwareFeature::EASY_UNLOCK_HOST);
// Second, use a device with all fields filled out. This ensures that all
// device details are properly saved.
@@ -911,10 +955,8 @@ TEST_F(CryptAuthDeviceManagerImplTest, SyncFullyDetailedExternalDeviceInfos) {
device_with_all_fields.set_public_key("publicKey2");
device_with_all_fields.set_friendly_device_name("deviceName");
device_with_all_fields.set_bluetooth_address("aa:bb:cc:dd:ee:ff");
- device_with_all_fields.set_unlock_key(true);
device_with_all_fields.set_unlockable(true);
device_with_all_fields.set_last_update_time_millis(123456789L);
- device_with_all_fields.set_mobile_hotspot_supported(true);
device_with_all_fields.set_device_type(DeviceType::ANDROIDOS);
BeaconSeed seed1;
@@ -938,7 +980,7 @@ TEST_F(CryptAuthDeviceManagerImplTest, SyncFullyDetailedExternalDeviceInfos) {
SoftwareFeature::MAGIC_TETHER_HOST);
device_with_all_fields.add_enabled_software_features(
- SoftwareFeature::MAGIC_TETHER_HOST);
+ SoftwareFeature::EASY_UNLOCK_HOST);
std::vector<ExternalDeviceInfo> expected_devices;
expected_devices.push_back(device_with_only_public_key);
@@ -981,8 +1023,82 @@ TEST_F(CryptAuthDeviceManagerImplTest, SubsetsOfSyncedDevices) {
// Only tether hosts.
ExpectSyncedDevicesAreEqual(
- std::vector<ExternalDeviceInfo>(1, devices_in_response_[0]),
+ std::vector<ExternalDeviceInfo>(1, devices_in_response_[1]),
device_manager_->GetTetherHosts());
}
+TEST_F(CryptAuthDeviceManagerImplTest,
+ TestDeprecatedBooleansArePersistedOnlyAsSoftwareFeatures) {
+ device_manager_->Start();
+
+ ExternalDeviceInfo device;
+ device.set_public_key("public key");
+ device.set_friendly_device_name("deprecated device");
+ device.set_unlock_key(true);
+ device.set_mobile_hotspot_supported(true);
+
+ devices_in_response_.push_back(device);
+ get_my_devices_response_.add_devices()->CopyFrom(device);
+
+ FireSchedulerForSync(INVOCATION_REASON_PERIODIC);
+ ASSERT_FALSE(success_callback_.is_null());
+ EXPECT_CALL(*this, OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::SUCCESS,
+ CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+ success_callback_.Run(get_my_devices_response_);
+
+ ExternalDeviceInfo synced_device = device_manager_->GetSyncedDevices()[2];
+
+ EXPECT_FALSE(synced_device.unlock_key());
+ EXPECT_FALSE(synced_device.mobile_hotspot_supported());
+
+ EXPECT_TRUE(base::ContainsValue(synced_device.supported_software_features(),
+ SoftwareFeature::EASY_UNLOCK_HOST));
+ EXPECT_TRUE(base::ContainsValue(synced_device.enabled_software_features(),
+ SoftwareFeature::EASY_UNLOCK_HOST));
+ EXPECT_TRUE(base::ContainsValue(synced_device.supported_software_features(),
+ SoftwareFeature::MAGIC_TETHER_HOST));
+ EXPECT_FALSE(base::ContainsValue(synced_device.enabled_software_features(),
+ SoftwareFeature::MAGIC_TETHER_HOST));
+}
+
+TEST_F(CryptAuthDeviceManagerImplTest,
+ TestIgnoreDeprecatedBooleansIfSoftwareFeaturesArePresent) {
+ device_manager_->Start();
+
+ ExternalDeviceInfo device;
+ device.set_public_key("public key");
+ device.set_friendly_device_name("deprecated device");
+ device.set_unlock_key(false);
+ device.set_mobile_hotspot_supported(false);
+
+ device.add_supported_software_features(SoftwareFeature::EASY_UNLOCK_HOST);
+ device.add_enabled_software_features(SoftwareFeature::EASY_UNLOCK_HOST);
+ device.add_supported_software_features(SoftwareFeature::MAGIC_TETHER_HOST);
+
+ devices_in_response_.push_back(device);
+ get_my_devices_response_.add_devices()->CopyFrom(device);
+
+ FireSchedulerForSync(INVOCATION_REASON_PERIODIC);
+ ASSERT_FALSE(success_callback_.is_null());
+ EXPECT_CALL(*this, OnSyncFinishedProxy(
+ CryptAuthDeviceManager::SyncResult::SUCCESS,
+ CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+ success_callback_.Run(get_my_devices_response_);
+
+ ExternalDeviceInfo synced_device = device_manager_->GetSyncedDevices()[2];
+
+ EXPECT_FALSE(synced_device.unlock_key());
+ EXPECT_FALSE(synced_device.mobile_hotspot_supported());
+
+ EXPECT_TRUE(base::ContainsValue(synced_device.supported_software_features(),
+ SoftwareFeature::EASY_UNLOCK_HOST));
+ EXPECT_TRUE(base::ContainsValue(synced_device.enabled_software_features(),
+ SoftwareFeature::EASY_UNLOCK_HOST));
+ EXPECT_TRUE(base::ContainsValue(synced_device.supported_software_features(),
+ SoftwareFeature::MAGIC_TETHER_HOST));
+ EXPECT_FALSE(base::ContainsValue(synced_device.enabled_software_features(),
+ SoftwareFeature::MAGIC_TETHER_HOST));
+}
+
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/cryptauth_enroller_impl.cc b/chromium/components/cryptauth/cryptauth_enroller_impl.cc
index be227cf384f..88e025f5385 100644
--- a/chromium/components/cryptauth/cryptauth_enroller_impl.cc
+++ b/chromium/components/cryptauth/cryptauth_enroller_impl.cc
@@ -134,7 +134,8 @@ void CryptAuthEnrollerImpl::OnSetupEnrollmentSuccess(
weak_ptr_factory_.GetWeakPtr()));
}
-void CryptAuthEnrollerImpl::OnSetupEnrollmentFailure(const std::string& error) {
+void CryptAuthEnrollerImpl::OnSetupEnrollmentFailure(
+ NetworkRequestError error) {
PA_LOG(WARNING) << "SetupEnrollment API failed with error: " << error;
callback_.Run(false);
}
@@ -224,7 +225,7 @@ void CryptAuthEnrollerImpl::OnFinishEnrollmentSuccess(
}
void CryptAuthEnrollerImpl::OnFinishEnrollmentFailure(
- const std::string& error) {
+ NetworkRequestError error) {
PA_LOG(WARNING) << "FinishEnrollment API failed with error: " << error;
callback_.Run(false);
}
diff --git a/chromium/components/cryptauth/cryptauth_enroller_impl.h b/chromium/components/cryptauth/cryptauth_enroller_impl.h
index cf90b1b53b5..aa3c6e326f6 100644
--- a/chromium/components/cryptauth/cryptauth_enroller_impl.h
+++ b/chromium/components/cryptauth/cryptauth_enroller_impl.h
@@ -11,6 +11,7 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "components/cryptauth/cryptauth_enroller.h"
+#include "components/cryptauth/network_request_error.h"
#include "components/cryptauth/proto/cryptauth_api.pb.h"
namespace cryptauth {
@@ -47,12 +48,12 @@ class CryptAuthEnrollerImpl : public CryptAuthEnroller {
// Callbacks for SetupEnrollment.
void OnSetupEnrollmentSuccess(
const SetupEnrollmentResponse& response);
- void OnSetupEnrollmentFailure(const std::string& error);
+ void OnSetupEnrollmentFailure(NetworkRequestError error);
// Callbacks for FinishEnrollment.
void OnFinishEnrollmentSuccess(
const FinishEnrollmentResponse& response);
- void OnFinishEnrollmentFailure(const std::string& error);
+ void OnFinishEnrollmentFailure(NetworkRequestError error);
// Callbacks for SecureMessageDelegate operations.
void OnKeyPairGenerated(const std::string& public_key,
diff --git a/chromium/components/cryptauth/cryptauth_enroller_impl_unittest.cc b/chromium/components/cryptauth/cryptauth_enroller_impl_unittest.cc
index dc2332c0afe..4834502a53a 100644
--- a/chromium/components/cryptauth/cryptauth_enroller_impl_unittest.cc
+++ b/chromium/components/cryptauth/cryptauth_enroller_impl_unittest.cc
@@ -10,6 +10,7 @@
#include "components/cryptauth/cryptauth_enrollment_utils.h"
#include "components/cryptauth/fake_secure_message_delegate.h"
#include "components/cryptauth/mock_cryptauth_client.h"
+#include "components/cryptauth/network_request_error.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
@@ -292,7 +293,7 @@ TEST_F(CryptAuthEnrollerTest, SetupEnrollmentApiCallError) {
EXPECT_TRUE(setup_request_.get());
ASSERT_FALSE(error_callback_.is_null());
- error_callback_.Run("Setup enrollment failed network");
+ error_callback_.Run(NetworkRequestError::kBadRequest);
EXPECT_TRUE(finish_callback_.is_null());
ASSERT_TRUE(enroller_result_.get());
@@ -326,7 +327,7 @@ TEST_F(CryptAuthEnrollerTest, FinishEnrollmentApiCallError) {
StartEnroller(GetDeviceInfo());
setup_callback_.Run(GetSetupEnrollmentResponse(true));
ASSERT_FALSE(error_callback_.is_null());
- error_callback_.Run("finish enrollment oauth error");
+ error_callback_.Run(NetworkRequestError::kAuthenticationError);
ASSERT_TRUE(enroller_result_.get());
EXPECT_FALSE(*enroller_result_);
}
diff --git a/chromium/components/cryptauth/cryptauth_enrollment_manager_impl.cc b/chromium/components/cryptauth/cryptauth_enrollment_manager_impl.cc
index a956b139535..ea84cc3438a 100644
--- a/chromium/components/cryptauth/cryptauth_enrollment_manager_impl.cc
+++ b/chromium/components/cryptauth/cryptauth_enrollment_manager_impl.cc
@@ -5,6 +5,7 @@
#include "components/cryptauth/cryptauth_enrollment_manager_impl.h"
#include <memory>
+#include <sstream>
#include <utility>
#include "base/base64url.h"
@@ -14,6 +15,7 @@
#include "chromeos/components/proximity_auth/logging/logging.h"
#include "components/cryptauth/cryptauth_enroller.h"
#include "components/cryptauth/pref_names.h"
+#include "components/cryptauth/proto/enum_string_util.h"
#include "components/cryptauth/secure_message_delegate.h"
#include "components/cryptauth/sync_scheduler_impl.h"
#include "components/prefs/pref_registry_simple.h"
@@ -51,6 +53,23 @@ std::unique_ptr<SyncScheduler> CreateSyncScheduler(
kEnrollmentMaxJitterRatio, "CryptAuth Enrollment");
}
+std::string GenerateSupportedFeaturesString(const GcmDeviceInfo& info) {
+ std::stringstream ss;
+ ss << "[";
+
+ bool logged_feature = false;
+ for (int i = 0; i < info.supported_software_features_size(); ++i) {
+ logged_feature = true;
+ ss << info.supported_software_features(i) << ", ";
+ }
+
+ if (logged_feature)
+ ss.seekp(-2, ss.cur); // Remove last ", " from the stream.
+
+ ss << "]";
+ return ss.str();
+}
+
} // namespace
// static
@@ -314,8 +333,9 @@ void CryptAuthEnrollmentManagerImpl::DoCryptAuthEnrollmentWithKeys() {
PA_LOG(INFO) << "Making enrollment:\n"
<< " public_key: " << public_key_b64 << "\n"
<< " invocation_reason: " << invocation_reason << "\n"
- << " gcm_registration_id: "
- << device_info.gcm_registration_id();
+ << " gcm_registration_id: " << device_info.gcm_registration_id()
+ << " supported features: "
+ << GenerateSupportedFeaturesString(device_info);
cryptauth_enroller_ = enroller_factory_->CreateInstance();
cryptauth_enroller_->Enroll(
diff --git a/chromium/components/cryptauth/data_with_timestamp.h b/chromium/components/cryptauth/data_with_timestamp.h
index f4e36a671bc..e341ede98b2 100644
--- a/chromium/components/cryptauth/data_with_timestamp.h
+++ b/chromium/components/cryptauth/data_with_timestamp.h
@@ -29,13 +29,13 @@ struct DataWithTimestamp {
bool operator==(const DataWithTimestamp& other) const;
// The data valid for a given time period.
- const std::string data;
+ std::string data;
// The start time for which the data is valid, inclusive.
- const int64_t start_timestamp_ms;
+ int64_t start_timestamp_ms;
// The end time for which the data is valid, exclusive.
- const int64_t end_timestamp_ms;
+ int64_t end_timestamp_ms;
};
} // namespace
diff --git a/chromium/components/cryptauth/device_to_device_authenticator.cc b/chromium/components/cryptauth/device_to_device_authenticator.cc
index e723cf183cb..3e7b038469a 100644
--- a/chromium/components/cryptauth/device_to_device_authenticator.cc
+++ b/chromium/components/cryptauth/device_to_device_authenticator.cc
@@ -122,7 +122,7 @@ void DeviceToDeviceAuthenticator::OnKeyPairGenerated(
weak_ptr_factory_.GetWeakPtr()));
}
-std::unique_ptr<base::Timer> DeviceToDeviceAuthenticator::CreateTimer() {
+std::unique_ptr<base::OneShotTimer> DeviceToDeviceAuthenticator::CreateTimer() {
return std::make_unique<base::OneShotTimer>();
}
diff --git a/chromium/components/cryptauth/device_to_device_authenticator.h b/chromium/components/cryptauth/device_to_device_authenticator.h
index ac34a9c7e15..79ba0378d22 100644
--- a/chromium/components/cryptauth/device_to_device_authenticator.h
+++ b/chromium/components/cryptauth/device_to_device_authenticator.h
@@ -17,7 +17,7 @@
#include "components/cryptauth/session_keys.h"
namespace base {
-class Timer;
+class OneShotTimer;
};
namespace cryptauth {
@@ -85,8 +85,8 @@ class DeviceToDeviceAuthenticator : public Authenticator,
void Authenticate(const AuthenticationCallback& callback) override;
protected:
- // Creates a base::Timer instance. Exposed for testing.
- virtual std::unique_ptr<base::Timer> CreateTimer();
+ // Creates a base::OneShotTimer instance. Exposed for testing.
+ virtual std::unique_ptr<base::OneShotTimer> CreateTimer();
private:
// The current state of the authentication flow.
@@ -164,7 +164,7 @@ class DeviceToDeviceAuthenticator : public Authenticator,
AuthenticationCallback callback_;
// Used for timing out when waiting for [Remote Auth] from the remote device.
- std::unique_ptr<base::Timer> timer_;
+ std::unique_ptr<base::OneShotTimer> timer_;
// The bytes of the [Hello] message sent to the remote device.
std::string hello_message_;
diff --git a/chromium/components/cryptauth/device_to_device_authenticator_unittest.cc b/chromium/components/cryptauth/device_to_device_authenticator_unittest.cc
index 2f1d06762e9..a6adc18c0ba 100644
--- a/chromium/components/cryptauth/device_to_device_authenticator_unittest.cc
+++ b/chromium/components/cryptauth/device_to_device_authenticator_unittest.cc
@@ -122,23 +122,18 @@ class DeviceToDeviceAuthenticatorForTest : public DeviceToDeviceAuthenticator {
timer_(nullptr) {}
~DeviceToDeviceAuthenticatorForTest() override {}
- base::MockTimer* timer() { return timer_; }
+ base::MockOneShotTimer* timer() { return timer_; }
private:
// DeviceToDeviceAuthenticator:
- std::unique_ptr<base::Timer> CreateTimer() override {
- bool retain_user_task = false;
- bool is_repeating = false;
-
- std::unique_ptr<base::MockTimer> timer(
- new base::MockTimer(retain_user_task, is_repeating));
-
+ std::unique_ptr<base::OneShotTimer> CreateTimer() override {
+ auto timer = std::make_unique<base::MockOneShotTimer>();
timer_ = timer.get();
return std::move(timer);
}
// This instance is owned by the super class.
- base::MockTimer* timer_;
+ base::MockOneShotTimer* timer_;
DISALLOW_COPY_AND_ASSIGN(DeviceToDeviceAuthenticatorForTest);
};
diff --git a/chromium/components/cryptauth/expiring_remote_device_cache.cc b/chromium/components/cryptauth/expiring_remote_device_cache.cc
new file mode 100644
index 00000000000..562fa67ed56
--- /dev/null
+++ b/chromium/components/cryptauth/expiring_remote_device_cache.cc
@@ -0,0 +1,50 @@
+// 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/cryptauth/expiring_remote_device_cache.h"
+
+#include "base/stl_util.h"
+#include "components/cryptauth/remote_device_cache.h"
+
+namespace cryptauth {
+
+ExpiringRemoteDeviceCache::ExpiringRemoteDeviceCache()
+ : remote_device_cache_(RemoteDeviceCache::Factory::Get()->BuildInstance()) {
+}
+
+ExpiringRemoteDeviceCache::~ExpiringRemoteDeviceCache() = default;
+
+void ExpiringRemoteDeviceCache::SetRemoteDevicesAndInvalidateOldEntries(
+ const RemoteDeviceList& remote_devices) {
+ remote_device_cache_->SetRemoteDevices(remote_devices);
+
+ device_ids_from_last_set_call_.clear();
+ for (const auto& device : remote_devices)
+ device_ids_from_last_set_call_.insert(device.GetDeviceId());
+}
+
+RemoteDeviceRefList ExpiringRemoteDeviceCache::GetNonExpiredRemoteDevices()
+ const {
+ // Only add to the output list if the entry is not stale.
+ RemoteDeviceRefList remote_devices;
+ for (auto device : remote_device_cache_->GetRemoteDevices()) {
+ if (device_ids_from_last_set_call_.count(device.GetDeviceId()) != 0)
+ remote_devices.push_back(device);
+ }
+
+ return remote_devices;
+}
+
+void ExpiringRemoteDeviceCache::UpdateRemoteDevice(
+ const RemoteDevice& remote_device) {
+ remote_device_cache_->SetRemoteDevices({remote_device});
+ device_ids_from_last_set_call_.insert(remote_device.GetDeviceId());
+}
+
+base::Optional<RemoteDeviceRef> ExpiringRemoteDeviceCache::GetRemoteDevice(
+ const std::string& device_id) const {
+ return remote_device_cache_->GetRemoteDevice(device_id);
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/expiring_remote_device_cache.h b/chromium/components/cryptauth/expiring_remote_device_cache.h
new file mode 100644
index 00000000000..a96beaeef0c
--- /dev/null
+++ b/chromium/components/cryptauth/expiring_remote_device_cache.h
@@ -0,0 +1,54 @@
+// 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_CRYPTAUTH_EXPIRING_REMOTE_DEVICE_CACHE_H_
+#define COMPONENTS_CRYPTAUTH_EXPIRING_REMOTE_DEVICE_CACHE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "components/cryptauth/remote_device.h"
+#include "components/cryptauth/remote_device_ref.h"
+
+namespace cryptauth {
+
+class RemoteDeviceCache;
+
+// A helper class around RemoteDeviceCache which keeps track of which devices
+// have been removed from, or "expired" in, the cache.
+//
+// If the set of devices provided to SetRemoteDevicesAndInvalidateOldEntries()
+// is different from the set provided to a previous call to
+// SetRemoteDevicesAndInvalidateOldEntries(), then the devices in the previous
+// call which are not in the new call will be marked as stale. Stale devices are
+// still valid RemoteDeviceRefs (preventing clients from segfaulting), but will
+// not be returned by GetNonExpiredRemoteDevices().
+class ExpiringRemoteDeviceCache {
+ public:
+ ExpiringRemoteDeviceCache();
+ virtual ~ExpiringRemoteDeviceCache();
+
+ void SetRemoteDevicesAndInvalidateOldEntries(
+ const RemoteDeviceList& remote_devices);
+
+ RemoteDeviceRefList GetNonExpiredRemoteDevices() const;
+
+ // Add or update a RemoteDevice without marking any other devices in the cache
+ // as stale.
+ void UpdateRemoteDevice(const RemoteDevice& remote_device);
+
+ base::Optional<RemoteDeviceRef> GetRemoteDevice(
+ const std::string& device_id) const;
+
+ private:
+ std::unique_ptr<RemoteDeviceCache> remote_device_cache_;
+ std::set<std::string> device_ids_from_last_set_call_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExpiringRemoteDeviceCache);
+};
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_EXPIRING_REMOTE_DEVICE_CACHE_H_
diff --git a/chromium/components/cryptauth/expiring_remote_device_cache_unittest.cc b/chromium/components/cryptauth/expiring_remote_device_cache_unittest.cc
new file mode 100644
index 00000000000..3a510c85571
--- /dev/null
+++ b/chromium/components/cryptauth/expiring_remote_device_cache_unittest.cc
@@ -0,0 +1,74 @@
+// 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/cryptauth/expiring_remote_device_cache.h"
+
+#include <algorithm>
+
+#include "components/cryptauth/remote_device_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cryptauth {
+
+class ExpiringRemoteDeviceCacheTest : public testing::Test {
+ protected:
+ ExpiringRemoteDeviceCacheTest()
+ : test_remote_device_list_(CreateRemoteDeviceListForTest(5)),
+ test_remote_device_ref_list_(CreateRemoteDeviceRefListForTest(5)){};
+
+ // testing::Test:
+ void SetUp() override {
+ cache_ = std::make_unique<ExpiringRemoteDeviceCache>();
+ }
+
+ void VerifyCacheRemoteDevices(
+ RemoteDeviceRefList expected_remote_device_ref_list) {
+ RemoteDeviceRefList remote_device_ref_list =
+ cache_->GetNonExpiredRemoteDevices();
+ std::sort(remote_device_ref_list.begin(), remote_device_ref_list.end(),
+ [](const auto& device_1, const auto& device_2) {
+ return device_1 < device_2;
+ });
+
+ EXPECT_EQ(expected_remote_device_ref_list, remote_device_ref_list);
+ }
+
+ const RemoteDeviceList test_remote_device_list_;
+ const RemoteDeviceRefList test_remote_device_ref_list_;
+ std::unique_ptr<ExpiringRemoteDeviceCache> cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExpiringRemoteDeviceCacheTest);
+};
+
+TEST_F(ExpiringRemoteDeviceCacheTest,
+ TestSetRemoteDevices_RemoteDeviceRefsRemoved) {
+ cache_->SetRemoteDevicesAndInvalidateOldEntries(test_remote_device_list_);
+
+ VerifyCacheRemoteDevices(test_remote_device_ref_list_);
+
+ cache_->SetRemoteDevicesAndInvalidateOldEntries(RemoteDeviceList());
+
+ VerifyCacheRemoteDevices(RemoteDeviceRefList());
+}
+
+TEST_F(ExpiringRemoteDeviceCacheTest,
+ TestSetRemoteDevices_DeviceRemovedAndAddedBack) {
+ cache_->SetRemoteDevicesAndInvalidateOldEntries(test_remote_device_list_);
+ cache_->SetRemoteDevicesAndInvalidateOldEntries(RemoteDeviceList());
+ cache_->SetRemoteDevicesAndInvalidateOldEntries(test_remote_device_list_);
+
+ VerifyCacheRemoteDevices(test_remote_device_ref_list_);
+}
+
+TEST_F(ExpiringRemoteDeviceCacheTest, TestUpdateRemoteDevice) {
+ cache_->SetRemoteDevicesAndInvalidateOldEntries(test_remote_device_list_);
+
+ VerifyCacheRemoteDevices(test_remote_device_ref_list_);
+
+ cache_->UpdateRemoteDevice(test_remote_device_list_[0]);
+
+ VerifyCacheRemoteDevices(test_remote_device_ref_list_);
+}
+
+} // namespace cryptauth \ No newline at end of file
diff --git a/chromium/components/cryptauth/fake_background_eid_generator.cc b/chromium/components/cryptauth/fake_background_eid_generator.cc
index 5af0e760a2a..9a8a1881c89 100644
--- a/chromium/components/cryptauth/fake_background_eid_generator.cc
+++ b/chromium/components/cryptauth/fake_background_eid_generator.cc
@@ -17,9 +17,8 @@ std::vector<DataWithTimestamp> FakeBackgroundEidGenerator::GenerateNearestEids(
}
std::string FakeBackgroundEidGenerator::IdentifyRemoteDeviceByAdvertisement(
- cryptauth::RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher,
const std::string& advertisement_service_data,
- const std::vector<std::string>& device_ids) const {
+ const RemoteDeviceRefList& remote_devices) const {
// Increment num_identify_calls_. Since this overrides a const method, some
// hacking is needed to modify the num_identify_calls_ instance variable.
int* num_identify_calls_ptr = const_cast<int*>(&num_identify_calls_);
diff --git a/chromium/components/cryptauth/fake_background_eid_generator.h b/chromium/components/cryptauth/fake_background_eid_generator.h
index 31fe19f8d5d..ae45a58fc5e 100644
--- a/chromium/components/cryptauth/fake_background_eid_generator.h
+++ b/chromium/components/cryptauth/fake_background_eid_generator.h
@@ -25,9 +25,8 @@ class FakeBackgroundEidGenerator : public BackgroundEidGenerator {
std::vector<DataWithTimestamp> GenerateNearestEids(
const std::vector<BeaconSeed>& beacon_seed) const override;
std::string IdentifyRemoteDeviceByAdvertisement(
- cryptauth::RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher,
const std::string& advertisement_service_data,
- const std::vector<std::string>& device_ids) const override;
+ const RemoteDeviceRefList& remote_devices) const override;
void set_nearest_eids_(
std::unique_ptr<std::vector<DataWithTimestamp>> nearest_eids) {
diff --git a/chromium/components/cryptauth/fake_connection.cc b/chromium/components/cryptauth/fake_connection.cc
index d68d600c93b..6303d27c56c 100644
--- a/chromium/components/cryptauth/fake_connection.cc
+++ b/chromium/components/cryptauth/fake_connection.cc
@@ -7,6 +7,7 @@
#include <memory>
#include <utility>
+#include "base/callback.h"
#include "components/cryptauth/wire_message.h"
namespace cryptauth {
@@ -54,6 +55,11 @@ void FakeConnection::RemoveObserver(ConnectionObserver* observer) {
Connection::RemoveObserver(observer);
}
+void FakeConnection::GetConnectionRssi(
+ base::OnceCallback<void(base::Optional<int32_t>)> callback) {
+ std::move(callback).Run(rssi_to_return_);
+}
+
void FakeConnection::CompleteInProgressConnection(bool success) {
DCHECK(!should_auto_connect_);
DCHECK(status() == Status::IN_PROGRESS);
@@ -82,10 +88,6 @@ void FakeConnection::ReceiveMessage(
pending_payload_.clear();
}
-void FakeConnection::NotifyGattCharacteristicsNotAvailable() {
- Connection::NotifyGattCharacteristicsNotAvailable();
-}
-
void FakeConnection::SendMessageImpl(std::unique_ptr<WireMessage> message) {
CHECK(!current_message_);
current_message_ = std::move(message);
diff --git a/chromium/components/cryptauth/fake_connection.h b/chromium/components/cryptauth/fake_connection.h
index 981599dff59..026d11fa19c 100644
--- a/chromium/components/cryptauth/fake_connection.h
+++ b/chromium/components/cryptauth/fake_connection.h
@@ -6,6 +6,7 @@
#define COMPONENTS_CRYPTAUTH_FAKE_CONNECTION_H_
#include "base/macros.h"
+#include "base/optional.h"
#include "components/cryptauth/connection.h"
namespace cryptauth {
@@ -19,12 +20,18 @@ class FakeConnection : public Connection {
FakeConnection(RemoteDeviceRef remote_device, bool should_auto_connect);
~FakeConnection() override;
+ void set_rssi_to_return(const base::Optional<int32_t>& rssi_to_return) {
+ rssi_to_return_ = rssi_to_return;
+ }
+
// Connection:
void Connect() override;
void Disconnect() override;
std::string GetDeviceAddress() override;
void AddObserver(ConnectionObserver* observer) override;
void RemoveObserver(ConnectionObserver* observer) override;
+ void GetConnectionRssi(
+ base::OnceCallback<void(base::Optional<int32_t>)> callback) override;
// Completes a connection attempt which was originally started via a call to
// |Connect()|. If |success| is true, the connection's status shifts to
@@ -39,9 +46,6 @@ class FakeConnection : public Connection {
// container WireMessage format.
void ReceiveMessage(const std::string& feature, const std::string& payload);
- // Notifies observers that GATT characteristics are unavailable.
- void NotifyGattCharacteristicsNotAvailable();
-
// Returns the current message in progress of being sent.
WireMessage* current_message() { return current_message_.get(); }
@@ -68,6 +72,7 @@ class FakeConnection : public Connection {
std::vector<ConnectionObserver*> observers_;
+ base::Optional<int32_t> rssi_to_return_;
const bool should_auto_connect_;
DISALLOW_COPY_AND_ASSIGN(FakeConnection);
diff --git a/chromium/components/cryptauth/fake_secure_channel.cc b/chromium/components/cryptauth/fake_secure_channel.cc
index 9fbe29c831d..0d0fc25806d 100644
--- a/chromium/components/cryptauth/fake_secure_channel.cc
+++ b/chromium/components/cryptauth/fake_secure_channel.cc
@@ -4,6 +4,8 @@
#include "components/cryptauth/fake_secure_channel.h"
+#include <utility>
+
#include "base/logging.h"
namespace cryptauth {
@@ -12,11 +14,13 @@ FakeSecureChannel::SentMessage::SentMessage(const std::string& feature,
const std::string& payload)
: feature(feature), payload(payload) {}
-FakeSecureChannel::FakeSecureChannel(std::unique_ptr<Connection> connection,
- CryptAuthService* cryptauth_service)
- : SecureChannel(std::move(connection), cryptauth_service) {}
+FakeSecureChannel::FakeSecureChannel(std::unique_ptr<Connection> connection)
+ : SecureChannel(std::move(connection)) {}
-FakeSecureChannel::~FakeSecureChannel() {}
+FakeSecureChannel::~FakeSecureChannel() {
+ if (destructor_callback_)
+ std::move(destructor_callback_).Run();
+}
void FakeSecureChannel::ChangeStatus(const Status& new_status) {
Status old_status = status_;
@@ -45,14 +49,8 @@ void FakeSecureChannel::CompleteSendingMessage(int sequence_number) {
observer->OnMessageSent(this, sequence_number);
}
-void FakeSecureChannel::NotifyGattCharacteristicsNotAvailable() {
- // Copy to prevent channel from being removed during handler.
- std::vector<Observer*> observers_copy = observers_;
- for (auto* observer : observers_copy)
- observer->OnGattCharacteristicsNotAvailable();
-}
-
void FakeSecureChannel::Initialize() {
+ was_initialized_ = true;
ChangeStatus(Status::CONNECTING);
}
@@ -81,4 +79,13 @@ void FakeSecureChannel::RemoveObserver(Observer* observer) {
observers_.end());
}
+void FakeSecureChannel::GetConnectionRssi(
+ base::OnceCallback<void(base::Optional<int32_t>)> callback) {
+ std::move(callback).Run(rssi_to_return_);
+}
+
+base::Optional<std::string> FakeSecureChannel::GetChannelBindingData() {
+ return channel_binding_data_;
+}
+
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/fake_secure_channel.h b/chromium/components/cryptauth/fake_secure_channel.h
index 0ec559ffeed..1c2f9c834af 100644
--- a/chromium/components/cryptauth/fake_secure_channel.h
+++ b/chromium/components/cryptauth/fake_secure_channel.h
@@ -5,20 +5,35 @@
#ifndef COMPONENTS_CRYPTAUTH_FAKE_SECURE_CHANNEL_H_
#define COMPONENTS_CRYPTAUTH_FAKE_SECURE_CHANNEL_H_
+#include "base/callback.h"
#include "base/macros.h"
+#include "base/optional.h"
+#include "components/cryptauth/connection.h"
#include "components/cryptauth/secure_channel.h"
namespace cryptauth {
-class CryptAuthService;
-
// A fake implementation of SecureChannel to use in tests.
class FakeSecureChannel : public SecureChannel {
public:
- FakeSecureChannel(std::unique_ptr<Connection> connection,
- CryptAuthService* cryptauth_service);
+ FakeSecureChannel(std::unique_ptr<Connection> connection);
~FakeSecureChannel() override;
+ void set_destructor_callback(base::OnceClosure destructor_callback) {
+ destructor_callback_ = std::move(destructor_callback);
+ }
+
+ bool was_initialized() { return was_initialized_; }
+
+ void set_rssi_to_return(const base::Optional<int32_t>& rssi_to_return) {
+ rssi_to_return_ = rssi_to_return;
+ }
+
+ void set_channel_binding_data(
+ const base::Optional<std::string>& channel_binding_data) {
+ channel_binding_data_ = channel_binding_data;
+ }
+
struct SentMessage {
SentMessage(const std::string& feature, const std::string& payload);
@@ -29,7 +44,6 @@ class FakeSecureChannel : public SecureChannel {
void ChangeStatus(const Status& new_status);
void ReceiveMessage(const std::string& feature, const std::string& payload);
void CompleteSendingMessage(int sequence_number);
- void NotifyGattCharacteristicsNotAvailable();
std::vector<Observer*> observers() { return observers_; }
@@ -42,11 +56,19 @@ class FakeSecureChannel : public SecureChannel {
void Disconnect() override;
void AddObserver(Observer* observer) override;
void RemoveObserver(Observer* observer) override;
+ void GetConnectionRssi(
+ base::OnceCallback<void(base::Optional<int32_t>)> callback) override;
+ base::Optional<std::string> GetChannelBindingData() override;
private:
int next_sequence_number_ = 0;
+ bool was_initialized_ = false;
std::vector<Observer*> observers_;
std::vector<SentMessage> sent_messages_;
+ base::Optional<int32_t> rssi_to_return_;
+ base::Optional<std::string> channel_binding_data_;
+
+ base::OnceClosure destructor_callback_;
DISALLOW_COPY_AND_ASSIGN(FakeSecureChannel);
};
diff --git a/chromium/components/cryptauth/fake_secure_context.cc b/chromium/components/cryptauth/fake_secure_context.cc
index 1592302d188..c9117530fec 100644
--- a/chromium/components/cryptauth/fake_secure_context.cc
+++ b/chromium/components/cryptauth/fake_secure_context.cc
@@ -24,7 +24,7 @@ FakeSecureContext::FakeSecureContext()
FakeSecureContext::~FakeSecureContext() {}
std::string FakeSecureContext::GetChannelBindingData() const {
- return kChannelBindingData;
+ return channel_binding_data_ ? *channel_binding_data_ : kChannelBindingData;
}
SecureContext::ProtocolVersion FakeSecureContext::GetProtocolVersion() const {
diff --git a/chromium/components/cryptauth/fake_secure_context.h b/chromium/components/cryptauth/fake_secure_context.h
index fec5a68c799..78bd4dc8fe6 100644
--- a/chromium/components/cryptauth/fake_secure_context.h
+++ b/chromium/components/cryptauth/fake_secure_context.h
@@ -9,6 +9,7 @@
#include "base/callback.h"
#include "base/macros.h"
+#include "base/optional.h"
#include "components/cryptauth/secure_context.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -31,8 +32,13 @@ class FakeSecureContext : public SecureContext {
protocol_version_ = protocol_version;
}
+ void set_channel_binding_data(const std::string channel_binding_data) {
+ channel_binding_data_ = channel_binding_data;
+ }
+
private:
ProtocolVersion protocol_version_;
+ base::Optional<std::string> channel_binding_data_;
DISALLOW_COPY_AND_ASSIGN(FakeSecureContext);
};
diff --git a/chromium/components/cryptauth/fake_software_feature_manager.cc b/chromium/components/cryptauth/fake_software_feature_manager.cc
index a7625dc179d..2fe10a7b7dd 100644
--- a/chromium/components/cryptauth/fake_software_feature_manager.cc
+++ b/chromium/components/cryptauth/fake_software_feature_manager.cc
@@ -12,7 +12,7 @@ FakeSoftwareFeatureManager::SetSoftwareFeatureStateArgs::
SoftwareFeature software_feature,
bool enabled,
const base::Closure& success_callback,
- const base::Callback<void(const std::string&)>& error_callback,
+ const base::Callback<void(NetworkRequestError)>& error_callback,
bool is_exclusive)
: public_key(public_key),
software_feature(software_feature),
@@ -29,7 +29,7 @@ FakeSoftwareFeatureManager::FindEligibleDevicesArgs::FindEligibleDevicesArgs(
const base::Callback<void(const std::vector<ExternalDeviceInfo>&,
const std::vector<IneligibleDevice>&)>&
success_callback,
- const base::Callback<void(const std::string&)>& error_callback)
+ const base::Callback<void(NetworkRequestError)>& error_callback)
: software_feature(software_feature),
success_callback(success_callback),
error_callback(error_callback) {}
@@ -46,7 +46,7 @@ void FakeSoftwareFeatureManager::SetSoftwareFeatureState(
SoftwareFeature software_feature,
bool enabled,
const base::Closure& success_callback,
- const base::Callback<void(const std::string&)>& error_callback,
+ const base::Callback<void(NetworkRequestError)>& error_callback,
bool is_exclusive) {
set_software_feature_state_calls_.emplace_back(
std::make_unique<SetSoftwareFeatureStateArgs>(
@@ -62,7 +62,7 @@ void FakeSoftwareFeatureManager::FindEligibleDevices(
const base::Callback<void(const std::vector<ExternalDeviceInfo>&,
const std::vector<IneligibleDevice>&)>&
success_callback,
- const base::Callback<void(const std::string&)>& error_callback) {
+ const base::Callback<void(NetworkRequestError)>& error_callback) {
find_eligible_multidevice_host_calls_.emplace_back(
std::make_unique<FindEligibleDevicesArgs>(
software_feature, success_callback, error_callback));
diff --git a/chromium/components/cryptauth/fake_software_feature_manager.h b/chromium/components/cryptauth/fake_software_feature_manager.h
index e5a670a6e70..3fa9da2cc82 100644
--- a/chromium/components/cryptauth/fake_software_feature_manager.h
+++ b/chromium/components/cryptauth/fake_software_feature_manager.h
@@ -9,6 +9,7 @@
#include <vector>
#include "base/callback.h"
+#include "components/cryptauth/network_request_error.h"
#include "components/cryptauth/proto/cryptauth_api.pb.h"
#include "components/cryptauth/software_feature_manager.h"
@@ -30,7 +31,7 @@ class FakeSoftwareFeatureManager : public SoftwareFeatureManager {
SoftwareFeature software_feature,
bool enabled,
const base::Closure& success_callback,
- const base::Callback<void(const std::string&)>& error_callback,
+ const base::Callback<void(NetworkRequestError)>& error_callback,
bool is_exclusive);
~SetSoftwareFeatureStateArgs();
@@ -38,7 +39,7 @@ class FakeSoftwareFeatureManager : public SoftwareFeatureManager {
SoftwareFeature software_feature;
bool enabled;
base::Closure success_callback;
- base::Callback<void(const std::string&)> error_callback;
+ base::Callback<void(NetworkRequestError)> error_callback;
bool is_exclusive;
private:
@@ -51,14 +52,14 @@ class FakeSoftwareFeatureManager : public SoftwareFeatureManager {
const base::Callback<void(const std::vector<ExternalDeviceInfo>&,
const std::vector<IneligibleDevice>&)>&
success_callback,
- const base::Callback<void(const std::string&)>& error_callback);
+ const base::Callback<void(NetworkRequestError)>& error_callback);
~FindEligibleDevicesArgs();
SoftwareFeature software_feature;
base::Callback<void(const std::vector<ExternalDeviceInfo>&,
const std::vector<IneligibleDevice>&)>
success_callback;
- base::Callback<void(const std::string&)> error_callback;
+ base::Callback<void(NetworkRequestError)> error_callback;
private:
DISALLOW_COPY_AND_ASSIGN(FindEligibleDevicesArgs);
@@ -85,14 +86,14 @@ class FakeSoftwareFeatureManager : public SoftwareFeatureManager {
SoftwareFeature software_feature,
bool enabled,
const base::Closure& success_callback,
- const base::Callback<void(const std::string&)>& error_callback,
+ const base::Callback<void(NetworkRequestError)>& error_callback,
bool is_exclusive = false) override;
void FindEligibleDevices(
SoftwareFeature software_feature,
const base::Callback<void(const std::vector<ExternalDeviceInfo>&,
const std::vector<IneligibleDevice>&)>&
success_callback,
- const base::Callback<void(const std::string&)>& error_callback) override;
+ const base::Callback<void(NetworkRequestError)>& error_callback) override;
private:
Delegate* delegate_ = nullptr;
diff --git a/chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.cc b/chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.cc
deleted file mode 100644
index 3988077e62f..00000000000
--- a/chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.cc
+++ /dev/null
@@ -1,43 +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/cryptauth/mock_remote_beacon_seed_fetcher.h"
-
-#include "components/cryptauth/cryptauth_device_manager.h"
-
-namespace cryptauth {
-
-MockRemoteBeaconSeedFetcher::MockRemoteBeaconSeedFetcher()
- : RemoteBeaconSeedFetcher(nullptr) {}
-
-MockRemoteBeaconSeedFetcher::~MockRemoteBeaconSeedFetcher() {}
-
-bool MockRemoteBeaconSeedFetcher::FetchSeedsForDeviceId(
- const std::string& device_id,
- std::vector<BeaconSeed>* beacon_seeds_out) const {
- const auto& seeds_iter = device_id_to_beacon_seeds_map_.find(device_id);
-
- if (seeds_iter == device_id_to_beacon_seeds_map_.end())
- return false;
-
- *beacon_seeds_out = seeds_iter->second;
- return true;
-}
-
-void MockRemoteBeaconSeedFetcher::SetSeedsForDeviceId(
- const std::string& device_id,
- const std::vector<BeaconSeed>* beacon_seeds) {
- if (!beacon_seeds) {
- const auto& it = device_id_to_beacon_seeds_map_.find(device_id);
-
- if (it != device_id_to_beacon_seeds_map_.end())
- device_id_to_beacon_seeds_map_.erase(it);
-
- return;
- }
-
- device_id_to_beacon_seeds_map_[device_id] = *beacon_seeds;
-}
-
-} // namespace cryptauth
diff --git a/chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.h b/chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.h
deleted file mode 100644
index 84dc7a91e5c..00000000000
--- a/chromium/components/cryptauth/mock_remote_beacon_seed_fetcher.h
+++ /dev/null
@@ -1,39 +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_CRYPTAUTH_MOCK_BEACON_SEED_FETCHER_H_
-#define COMPONENTS_CRYPTAUTH_MOCK_BEACON_SEED_FETCHER_H_
-
-#include <map>
-#include <vector>
-
-#include "components/cryptauth/proto/cryptauth_api.pb.h"
-#include "components/cryptauth/remote_beacon_seed_fetcher.h"
-
-namespace cryptauth {
-
-class MockRemoteBeaconSeedFetcher : public RemoteBeaconSeedFetcher {
- public:
- MockRemoteBeaconSeedFetcher();
- ~MockRemoteBeaconSeedFetcher() override;
-
- // If |beacon_seeds| is null, FetchSeedsForDevice() will fail for the same
- // |remote_device|.
- void SetSeedsForDeviceId(const std::string& device_id,
- const std::vector<BeaconSeed>* beacon_seeds);
-
- // RemoteBeaconSeedFetcher:
- bool FetchSeedsForDeviceId(
- const std::string& device_id,
- std::vector<BeaconSeed>* beacon_seeds_out) const override;
-
- private:
- std::map<std::string, std::vector<BeaconSeed>> device_id_to_beacon_seeds_map_;
-
- DISALLOW_COPY_AND_ASSIGN(MockRemoteBeaconSeedFetcher);
-};
-
-} // namespace cryptauth
-
-#endif // COMPONENTS_CRYPTAUTH_MOCK_BEACON_SEED_FETCHER_H_
diff --git a/chromium/components/cryptauth/network_request_error.cc b/chromium/components/cryptauth/network_request_error.cc
new file mode 100644
index 00000000000..3ce2b161b25
--- /dev/null
+++ b/chromium/components/cryptauth/network_request_error.cc
@@ -0,0 +1,37 @@
+// 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/cryptauth/network_request_error.h"
+
+namespace cryptauth {
+
+std::ostream& operator<<(std::ostream& stream,
+ const NetworkRequestError& error) {
+ switch (error) {
+ case NetworkRequestError::kOffline:
+ stream << "[offline]";
+ break;
+ case NetworkRequestError::kEndpointNotFound:
+ stream << "[endpoint not found]";
+ break;
+ case NetworkRequestError::kAuthenticationError:
+ stream << "[authentication error]";
+ break;
+ case NetworkRequestError::kBadRequest:
+ stream << "[bad request]";
+ break;
+ case NetworkRequestError::kResponseMalformed:
+ stream << "[response malformed]";
+ break;
+ case NetworkRequestError::kInternalServerError:
+ stream << "[internal server error]";
+ break;
+ case NetworkRequestError::kUnknown:
+ stream << "[unknown]";
+ break;
+ }
+ return stream;
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/network_request_error.h b/chromium/components/cryptauth/network_request_error.h
new file mode 100644
index 00000000000..51d011fd823
--- /dev/null
+++ b/chromium/components/cryptauth/network_request_error.h
@@ -0,0 +1,41 @@
+// 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_CRYPTAUTH_NETWORK_REQUEST_ERROR_H_
+#define COMPONENTS_CRYPTAUTH_NETWORK_REQUEST_ERROR_H_
+
+#include <ostream>
+
+namespace cryptauth {
+
+enum class NetworkRequestError {
+ // Request could not be completed because the device is offline or has issues
+ // sending the HTTP request.
+ kOffline,
+
+ // Server endpoint could not be found.
+ kEndpointNotFound,
+
+ // Authentication error contacting back-end.
+ kAuthenticationError,
+
+ // Request was invalid.
+ kBadRequest,
+
+ // The server responded, but the response was not formatted correctly.
+ kResponseMalformed,
+
+ // Internal server error.
+ kInternalServerError,
+
+ // Unknown result.
+ kUnknown
+};
+
+std::ostream& operator<<(std::ostream& stream,
+ const NetworkRequestError& error);
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_NETWORK_REQUEST_ERROR_H_
diff --git a/chromium/components/cryptauth/proto/BUILD.gn b/chromium/components/cryptauth/proto/BUILD.gn
index 9affa372285..58743b4f566 100644
--- a/chromium/components/cryptauth/proto/BUILD.gn
+++ b/chromium/components/cryptauth/proto/BUILD.gn
@@ -10,3 +10,18 @@ proto_library("proto") {
"securemessage.proto",
]
}
+
+static_library("util") {
+ sources = [
+ "enum_string_util.cc",
+ "enum_string_util.h",
+ ]
+
+ public_deps = [
+ ":proto",
+ ]
+
+ deps = [
+ "//base",
+ ]
+}
diff --git a/chromium/components/cryptauth/proto/enum_string_util.cc b/chromium/components/cryptauth/proto/enum_string_util.cc
new file mode 100644
index 00000000000..73ba6031244
--- /dev/null
+++ b/chromium/components/cryptauth/proto/enum_string_util.cc
@@ -0,0 +1,43 @@
+// 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/cryptauth/proto/enum_string_util.h"
+
+namespace cryptauth {
+
+std::ostream& operator<<(std::ostream& stream,
+ const SoftwareFeature& software_feature) {
+ switch (software_feature) {
+ case SoftwareFeature::BETTER_TOGETHER_HOST:
+ stream << "[Better Together host]";
+ break;
+ case SoftwareFeature::BETTER_TOGETHER_CLIENT:
+ stream << "[Better Together client]";
+ break;
+ case SoftwareFeature::EASY_UNLOCK_HOST:
+ stream << "[EasyUnlock host]";
+ break;
+ case SoftwareFeature::EASY_UNLOCK_CLIENT:
+ stream << "[EasyUnlock client]";
+ break;
+ case SoftwareFeature::MAGIC_TETHER_HOST:
+ stream << "[Instant Tethering host]";
+ break;
+ case SoftwareFeature::MAGIC_TETHER_CLIENT:
+ stream << "[Instant Tethering client]";
+ break;
+ case SoftwareFeature::SMS_CONNECT_HOST:
+ stream << "[SMS Connect host]";
+ break;
+ case SoftwareFeature::SMS_CONNECT_CLIENT:
+ stream << "[SMS Connect client]";
+ break;
+ default:
+ stream << "[unknown software feature]";
+ break;
+ }
+ return stream;
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/proto/enum_string_util.h b/chromium/components/cryptauth/proto/enum_string_util.h
new file mode 100644
index 00000000000..6bb0fc901d4
--- /dev/null
+++ b/chromium/components/cryptauth/proto/enum_string_util.h
@@ -0,0 +1,19 @@
+// 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_CRYPTAUTH_PROTO_ENUM_STRING_UTIL_H_
+#define COMPONENTS_CRYPTAUTH_PROTO_ENUM_STRING_UTIL_H_
+
+#include <ostream>
+
+#include "components/cryptauth/proto/cryptauth_api.pb.h"
+
+namespace cryptauth {
+
+std::ostream& operator<<(std::ostream& stream,
+ const SoftwareFeature& software_fature);
+
+} // namespace cryptauth
+
+#endif // COMPONENTS_CRYPTAUTH_PROTO_ENUM_STRING_UTIL_H_
diff --git a/chromium/components/cryptauth/remote_beacon_seed_fetcher.cc b/chromium/components/cryptauth/remote_beacon_seed_fetcher.cc
deleted file mode 100644
index 16c743ad00a..00000000000
--- a/chromium/components/cryptauth/remote_beacon_seed_fetcher.cc
+++ /dev/null
@@ -1,39 +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/cryptauth/remote_beacon_seed_fetcher.h"
-
-#include "components/cryptauth/cryptauth_device_manager.h"
-#include "components/cryptauth/remote_device_ref.h"
-
-namespace cryptauth {
-
-RemoteBeaconSeedFetcher::RemoteBeaconSeedFetcher(
- const CryptAuthDeviceManager* device_manager)
- : device_manager_(device_manager) {}
-
-RemoteBeaconSeedFetcher::~RemoteBeaconSeedFetcher() {}
-
-bool RemoteBeaconSeedFetcher::FetchSeedsForDeviceId(
- const std::string& device_id,
- std::vector<BeaconSeed>* beacon_seeds_out) const {
- for(const auto& device_info : device_manager_->GetSyncedDevices()) {
- if (RemoteDeviceRef::GenerateDeviceId(device_info.public_key()) ==
- device_id) {
- if (device_info.beacon_seeds_size() == 0) {
- return false;
- }
-
- beacon_seeds_out->clear();
- for (int i = 0; i < device_info.beacon_seeds_size(); i++) {
- beacon_seeds_out->push_back(device_info.beacon_seeds(i));
- }
- return true;
- }
- }
-
- return false;
-}
-
-} // namespace cryptauth
diff --git a/chromium/components/cryptauth/remote_beacon_seed_fetcher.h b/chromium/components/cryptauth/remote_beacon_seed_fetcher.h
deleted file mode 100644
index 5c53e90259f..00000000000
--- a/chromium/components/cryptauth/remote_beacon_seed_fetcher.h
+++ /dev/null
@@ -1,39 +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_CRYPTAUTH_REMOTE_BEACON_SEED_FETCHER_H_
-#define COMPONENTS_CRYPTAUTH_REMOTE_BEACON_SEED_FETCHER_H_
-
-#include <vector>
-
-#include "base/macros.h"
-#include "components/cryptauth/proto/cryptauth_api.pb.h"
-
-namespace cryptauth {
-
-class CryptAuthDeviceManager;
-
-// Fetches |BeaconSeed|s corresponding to a given |RemoteDevice|. Note that an
-// alternate solution would be to embed a list of |BeaconSeed|s in each
-// |RemoteDevice| object; however, because |BeaconSeed| objects take up almost
-// 1kB of memory apiece, that approach adds unnecessary memory overhead.
-class RemoteBeaconSeedFetcher {
- public:
- RemoteBeaconSeedFetcher(const CryptAuthDeviceManager* device_manager);
- virtual ~RemoteBeaconSeedFetcher();
-
- virtual bool FetchSeedsForDeviceId(
- const std::string& device_id,
- std::vector<BeaconSeed>* beacon_seeds_out) const;
-
- private:
- // Not owned by this instance and must outlive it.
- const CryptAuthDeviceManager* device_manager_;
-
- DISALLOW_COPY_AND_ASSIGN(RemoteBeaconSeedFetcher);
-};
-
-} // namespace cryptauth
-
-#endif // COMPONENTS_CRYPTAUTH_REMOTE_BEACON_SEED_FETCHER_H_
diff --git a/chromium/components/cryptauth/remote_beacon_seed_fetcher_unittest.cc b/chromium/components/cryptauth/remote_beacon_seed_fetcher_unittest.cc
deleted file mode 100644
index 92d81fea528..00000000000
--- a/chromium/components/cryptauth/remote_beacon_seed_fetcher_unittest.cc
+++ /dev/null
@@ -1,148 +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/cryptauth/remote_beacon_seed_fetcher.h"
-
-#include <memory>
-
-#include "base/macros.h"
-#include "components/cryptauth/cryptauth_client.h"
-#include "components/cryptauth/fake_cryptauth_device_manager.h"
-#include "components/cryptauth/remote_device_ref.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::StrictMock;
-using testing::Return;
-
-namespace cryptauth {
-
-namespace {
-
-const std::string fake_beacon_seed1_data = "fakeBeaconSeed1Data";
-const int64_t fake_beacon_seed1_start_ms = 1000L;
-const int64_t fake_beacon_seed1_end_ms = 2000L;
-
-const std::string fake_beacon_seed2_data = "fakeBeaconSeed2Data";
-const int64_t fake_beacon_seed2_start_ms = 2000L;
-const int64_t fake_beacon_seed2_end_ms = 3000L;
-
-const std::string fake_beacon_seed3_data = "fakeBeaconSeed3Data";
-const int64_t fake_beacon_seed3_start_ms = 1000L;
-const int64_t fake_beacon_seed3_end_ms = 2000L;
-
-const std::string fake_beacon_seed4_data = "fakeBeaconSeed4Data";
-const int64_t fake_beacon_seed4_start_ms = 2000L;
-const int64_t fake_beacon_seed4_end_ms = 3000L;
-
-const std::string public_key1 = "publicKey1";
-const std::string public_key2 = "publicKey2";
-
-ExternalDeviceInfo CreateFakeInfo1() {
- BeaconSeed seed1;
- seed1.set_data(fake_beacon_seed1_data);
- seed1.set_start_time_millis(fake_beacon_seed1_start_ms);
- seed1.set_end_time_millis(fake_beacon_seed1_end_ms);
-
- BeaconSeed seed2;
- seed2.set_data(fake_beacon_seed2_data);
- seed2.set_start_time_millis(fake_beacon_seed2_start_ms);
- seed2.set_end_time_millis(fake_beacon_seed2_end_ms);
-
- ExternalDeviceInfo info1;
- info1.set_public_key(public_key1);
- info1.add_beacon_seeds()->CopyFrom(seed1);
- info1.add_beacon_seeds()->CopyFrom(seed2);
- return info1;
-}
-
-ExternalDeviceInfo CreateFakeInfo2() {
- BeaconSeed seed3;
- seed3.set_data(fake_beacon_seed3_data);
- seed3.set_start_time_millis(fake_beacon_seed3_start_ms);
- seed3.set_end_time_millis(fake_beacon_seed3_end_ms);
-
- BeaconSeed seed4;
- seed4.set_data(fake_beacon_seed4_data);
- seed4.set_start_time_millis(fake_beacon_seed4_start_ms);
- seed4.set_end_time_millis(fake_beacon_seed4_end_ms);
-
- ExternalDeviceInfo info2;
- info2.set_public_key(public_key2);
- info2.add_beacon_seeds()->CopyFrom(seed3);
- info2.add_beacon_seeds()->CopyFrom(seed4);
- return info2;
-}
-
-} // namespace
-
-class CryptAuthRemoteBeaconSeedFetcherTest : public testing::Test {
- protected:
- CryptAuthRemoteBeaconSeedFetcherTest()
- : fake_info1_(CreateFakeInfo1()), fake_info2_(CreateFakeInfo2()) {}
-
- void SetUp() override {
- fake_device_manager_ = std::make_unique<FakeCryptAuthDeviceManager>();
- fetcher_ = std::make_unique<StrictMock<RemoteBeaconSeedFetcher>>(
- fake_device_manager_.get());
- }
-
- std::unique_ptr<RemoteBeaconSeedFetcher> fetcher_;
- std::unique_ptr<FakeCryptAuthDeviceManager> fake_device_manager_;
-
- const ExternalDeviceInfo fake_info1_;
- const ExternalDeviceInfo fake_info2_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CryptAuthRemoteBeaconSeedFetcherTest);
-};
-
-TEST_F(CryptAuthRemoteBeaconSeedFetcherTest, TestRemoteDeviceWithNoPublicKey) {
- std::vector<BeaconSeed> seeds;
- EXPECT_FALSE(fetcher_->FetchSeedsForDeviceId(std::string(), &seeds));
-}
-
-TEST_F(CryptAuthRemoteBeaconSeedFetcherTest, TestNoSyncedDevices) {
- std::vector<BeaconSeed> seeds;
- EXPECT_FALSE(fetcher_->FetchSeedsForDeviceId(
- RemoteDeviceRef::GenerateDeviceId(public_key1), &seeds));
-}
-
-TEST_F(CryptAuthRemoteBeaconSeedFetcherTest, TestDeviceHasDifferentPublicKey) {
- fake_device_manager_->set_synced_devices(
- std::vector<ExternalDeviceInfo>{fake_info1_, fake_info2_});
-
- std::vector<BeaconSeed> seeds;
- EXPECT_FALSE(fetcher_->FetchSeedsForDeviceId(
- RemoteDeviceRef::GenerateDeviceId("differentPublicKey"), &seeds));
-}
-
-TEST_F(CryptAuthRemoteBeaconSeedFetcherTest, TestSuccess) {
- fake_device_manager_->set_synced_devices(
- std::vector<ExternalDeviceInfo>{fake_info1_, fake_info2_});
-
- std::vector<BeaconSeed> seeds1;
- ASSERT_TRUE(fetcher_->FetchSeedsForDeviceId(
- RemoteDeviceRef::GenerateDeviceId(public_key1), &seeds1));
- ASSERT_EQ(2u, seeds1.size());
- EXPECT_EQ(fake_beacon_seed1_data, seeds1[0].data());
- EXPECT_EQ(fake_beacon_seed1_start_ms, seeds1[0].start_time_millis());
- EXPECT_EQ(fake_beacon_seed1_end_ms, seeds1[0].end_time_millis());
- EXPECT_EQ(fake_beacon_seed2_data, seeds1[1].data());
- EXPECT_EQ(fake_beacon_seed2_start_ms, seeds1[1].start_time_millis());
- EXPECT_EQ(fake_beacon_seed2_end_ms, seeds1[1].end_time_millis());
-
- std::vector<BeaconSeed> seeds2;
- ASSERT_TRUE(fetcher_->FetchSeedsForDeviceId(
- RemoteDeviceRef::GenerateDeviceId(public_key2), &seeds2));
- ASSERT_EQ(2u, seeds2.size());
- EXPECT_EQ(fake_beacon_seed3_data, seeds2[0].data());
- EXPECT_EQ(fake_beacon_seed3_start_ms, seeds2[0].start_time_millis());
- EXPECT_EQ(fake_beacon_seed3_end_ms, seeds2[0].end_time_millis());
- EXPECT_EQ(fake_beacon_seed4_data, seeds2[1].data());
- EXPECT_EQ(fake_beacon_seed4_start_ms, seeds2[1].start_time_millis());
- EXPECT_EQ(fake_beacon_seed4_end_ms, seeds2[1].end_time_millis());
-}
-
-} // namespace cryptauth
diff --git a/chromium/components/cryptauth/remote_device.cc b/chromium/components/cryptauth/remote_device.cc
index 16b740e425d..c1fb898e174 100644
--- a/chromium/components/cryptauth/remote_device.cc
+++ b/chromium/components/cryptauth/remote_device.cc
@@ -39,61 +39,39 @@ std::string RemoteDevice::GenerateDeviceId(const std::string& public_key) {
return device_id;
}
-RemoteDevice::RemoteDevice()
- : unlock_key(false),
- supports_mobile_hotspot(false),
- last_update_time_millis(0L) {}
+RemoteDevice::RemoteDevice() : last_update_time_millis(0L) {}
RemoteDevice::RemoteDevice(
const std::string& user_id,
const std::string& name,
const std::string& public_key,
const std::string& persistent_symmetric_key,
- bool unlock_key,
- bool supports_mobile_hotspot,
int64_t last_update_time_millis,
- const std::map<SoftwareFeature, SoftwareFeatureState>& software_features)
+ const std::map<SoftwareFeature, SoftwareFeatureState>& software_features,
+ const std::vector<BeaconSeed>& beacon_seeds)
: user_id(user_id),
name(name),
public_key(public_key),
persistent_symmetric_key(persistent_symmetric_key),
- unlock_key(unlock_key),
- supports_mobile_hotspot(supports_mobile_hotspot),
last_update_time_millis(last_update_time_millis),
- software_features(software_features) {}
+ software_features(software_features),
+ beacon_seeds(beacon_seeds) {}
RemoteDevice::RemoteDevice(const RemoteDevice& other) = default;
RemoteDevice::~RemoteDevice() {}
-void RemoteDevice::LoadBeaconSeeds(
- const std::vector<BeaconSeed>& beacon_seeds) {
- this->are_beacon_seeds_loaded = true;
- this->beacon_seeds = beacon_seeds;
-}
-
std::string RemoteDevice::GetDeviceId() const {
return RemoteDevice::GenerateDeviceId(public_key);
}
bool RemoteDevice::operator==(const RemoteDevice& other) const {
- // Only compare |beacon_seeds| if they are loaded.
- bool are_beacon_seeds_equal = false;
- if (are_beacon_seeds_loaded) {
- are_beacon_seeds_equal =
- other.are_beacon_seeds_loaded &&
- AreBeaconSeedsEqual(beacon_seeds, other.beacon_seeds);
- } else {
- are_beacon_seeds_equal = !other.are_beacon_seeds_loaded;
- }
-
return user_id == other.user_id && name == other.name &&
public_key == other.public_key &&
persistent_symmetric_key == other.persistent_symmetric_key &&
- unlock_key == other.unlock_key &&
- supports_mobile_hotspot == other.supports_mobile_hotspot &&
last_update_time_millis == other.last_update_time_millis &&
- software_features == other.software_features && are_beacon_seeds_equal;
+ software_features == other.software_features &&
+ AreBeaconSeedsEqual(beacon_seeds, other.beacon_seeds);
}
bool RemoteDevice::operator<(const RemoteDevice& other) const {
diff --git a/chromium/components/cryptauth/remote_device.h b/chromium/components/cryptauth/remote_device.h
index 1745bc9c94a..157b3816766 100644
--- a/chromium/components/cryptauth/remote_device.h
+++ b/chromium/components/cryptauth/remote_device.h
@@ -23,14 +23,8 @@ struct RemoteDevice {
std::string name;
std::string public_key;
std::string persistent_symmetric_key;
- bool unlock_key;
- bool supports_mobile_hotspot;
int64_t last_update_time_millis;
std::map<SoftwareFeature, SoftwareFeatureState> software_features;
-
- // Note: To save space, the BeaconSeeds may not necessarily be included in
- // this object.
- bool are_beacon_seeds_loaded = false;
std::vector<BeaconSeed> beacon_seeds;
RemoteDevice();
@@ -39,16 +33,12 @@ struct RemoteDevice {
const std::string& name,
const std::string& public_key,
const std::string& persistent_symmetric_key,
- bool unlock_key,
- bool supports_mobile_hotspot,
int64_t last_update_time_millis,
- const std::map<SoftwareFeature, SoftwareFeatureState>& software_features);
+ const std::map<SoftwareFeature, SoftwareFeatureState>& software_features,
+ const std::vector<BeaconSeed>& beacon_seeds);
RemoteDevice(const RemoteDevice& other);
~RemoteDevice();
- // Loads a vector of BeaconSeeds for the RemoteDevice.
- void LoadBeaconSeeds(const std::vector<BeaconSeed>& beacon_seeds);
-
std::string GetDeviceId() const;
bool operator==(const RemoteDevice& other) const;
diff --git a/chromium/components/cryptauth/remote_device_cache.cc b/chromium/components/cryptauth/remote_device_cache.cc
index 735c8850c5e..b4d87aa1a17 100644
--- a/chromium/components/cryptauth/remote_device_cache.cc
+++ b/chromium/components/cryptauth/remote_device_cache.cc
@@ -4,10 +4,35 @@
#include "components/cryptauth/remote_device_cache.h"
+#include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
#include "base/stl_util.h"
namespace cryptauth {
+// static
+RemoteDeviceCache::Factory* RemoteDeviceCache::Factory::test_factory_ = nullptr;
+
+// static
+RemoteDeviceCache::Factory* RemoteDeviceCache::Factory::Get() {
+ if (test_factory_)
+ return test_factory_;
+
+ static base::NoDestructor<Factory> factory;
+ return factory.get();
+}
+
+// static
+void RemoteDeviceCache::Factory::SetFactoryForTesting(Factory* test_factory) {
+ test_factory_ = test_factory;
+}
+
+RemoteDeviceCache::Factory::~Factory() = default;
+
+std::unique_ptr<RemoteDeviceCache> RemoteDeviceCache::Factory::BuildInstance() {
+ return base::WrapUnique(new RemoteDeviceCache());
+}
+
RemoteDeviceCache::RemoteDeviceCache() = default;
RemoteDeviceCache::~RemoteDeviceCache() = default;
@@ -15,16 +40,23 @@ RemoteDeviceCache::~RemoteDeviceCache() = default;
void RemoteDeviceCache::SetRemoteDevices(
const RemoteDeviceList& remote_devices) {
for (const auto& remote_device : remote_devices) {
- if (!base::ContainsKey(remote_device_map_, remote_device.GetDeviceId())) {
+ if (base::ContainsKey(remote_device_map_, remote_device.GetDeviceId())) {
+ // Skip if the incoming remote device object contains
+ // a stale timestamp.
+ if (remote_device.last_update_time_millis <=
+ remote_device_map_[remote_device.GetDeviceId()]
+ ->last_update_time_millis) {
+ continue;
+ }
+
+ // Keep the same shared_ptr object, and simply
+ // update the RemoteDevice it references. This transparently updates
+ // the RemoteDeviceRefs used by clients.
+ *remote_device_map_[remote_device.GetDeviceId()] = remote_device;
+ } else {
remote_device_map_[remote_device.GetDeviceId()] =
std::make_shared<RemoteDevice>(remote_device);
- continue;
}
-
- // Keep the same |shared_ptr| obect, and simply update the RemoteDevice it
- // references. This transparently updates the RemoteDeviceRefs used by
- // clients.
- *remote_device_map_[remote_device.GetDeviceId()] = remote_device;
}
// Intentionally leave behind devices in the map which weren't in
@@ -48,4 +80,4 @@ base::Optional<RemoteDeviceRef> RemoteDeviceCache::GetRemoteDevice(
return RemoteDeviceRef(remote_device_map_.at(device_id));
}
-} // namespace cryptauth \ No newline at end of file
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/remote_device_cache.h b/chromium/components/cryptauth/remote_device_cache.h
index 2e9cfc3ddea..b693520b38d 100644
--- a/chromium/components/cryptauth/remote_device_cache.h
+++ b/chromium/components/cryptauth/remote_device_cache.h
@@ -21,7 +21,17 @@ namespace cryptauth {
// devices are not deleted from the cache).
class RemoteDeviceCache {
public:
- RemoteDeviceCache();
+ class Factory {
+ public:
+ static Factory* Get();
+ static void SetFactoryForTesting(Factory* test_factory);
+ virtual ~Factory();
+ virtual std::unique_ptr<RemoteDeviceCache> BuildInstance();
+
+ private:
+ static Factory* test_factory_;
+ };
+
virtual ~RemoteDeviceCache();
void SetRemoteDevices(const RemoteDeviceList& remote_devices);
@@ -32,6 +42,8 @@ class RemoteDeviceCache {
const std::string& device_id) const;
private:
+ RemoteDeviceCache();
+
std::unordered_map<std::string, std::shared_ptr<RemoteDevice>>
remote_device_map_;
diff --git a/chromium/components/cryptauth/remote_device_cache_unittest.cc b/chromium/components/cryptauth/remote_device_cache_unittest.cc
index 28e3b3d3d0e..63b8f211615 100644
--- a/chromium/components/cryptauth/remote_device_cache_unittest.cc
+++ b/chromium/components/cryptauth/remote_device_cache_unittest.cc
@@ -18,7 +18,9 @@ class RemoteDeviceCacheTest : public testing::Test {
test_remote_device_ref_list_(CreateRemoteDeviceRefListForTest(5)){};
// testing::Test:
- void SetUp() override { cache_ = std::make_unique<RemoteDeviceCache>(); }
+ void SetUp() override {
+ cache_ = RemoteDeviceCache::Factory::Get()->BuildInstance();
+ }
void VerifyCacheRemoteDevices(
RemoteDeviceRefList expected_remote_device_ref_list) {
@@ -65,21 +67,49 @@ TEST_F(RemoteDeviceCacheTest,
}
TEST_F(RemoteDeviceCacheTest,
- TestSetRemoteDevices_RemoteDeviceRefsRemainValidAfterCacheUpdate) {
+ TestSetRemoteDevices_RemoteDeviceRefsRemainValidAfterValidCacheUpdate) {
+ // Store the device with a last update time of 1000.
cryptauth::RemoteDevice remote_device =
cryptauth::CreateRemoteDeviceForTest();
+ remote_device.last_update_time_millis = 1000;
cache_->SetRemoteDevices({remote_device});
cryptauth::RemoteDeviceRef remote_device_ref =
*cache_->GetRemoteDevice(remote_device.GetDeviceId());
EXPECT_EQ(remote_device.name, remote_device_ref.name());
+ // Update the device's name and update time. Since the incoming remote device
+ // has a newer update time, the entry should successfully update.
remote_device.name = "new name";
+ remote_device.last_update_time_millis = 2000;
cache_->SetRemoteDevices({remote_device});
EXPECT_EQ(remote_device.name, remote_device_ref.name());
}
-// GetRemoteDevice
+TEST_F(
+ RemoteDeviceCacheTest,
+ TestSetRemoteDevices_RemoteDeviceCacheDoesNotUpdateWithStaleRemoteDevice) {
+ // Store the device with a last update time of 1000.
+ cryptauth::RemoteDevice remote_device =
+ cryptauth::CreateRemoteDeviceForTest();
+ remote_device.last_update_time_millis = 1000;
+ cache_->SetRemoteDevices({remote_device});
+
+ cryptauth::RemoteDeviceRef remote_device_ref =
+ *cache_->GetRemoteDevice(remote_device.GetDeviceId());
+ EXPECT_EQ(remote_device.name, remote_device_ref.name());
+
+ // Update the device's name and update time, this time reducing the
+ // last update time to 500. Since this is less than 1000, adding the
+ // device to the cache should not cause it to overwrite the previous
+ // entry, since this entry is older.
+ std::string prev_name = remote_device.name;
+ remote_device.last_update_time_millis = 500;
+ remote_device.name = "new name";
+ cache_->SetRemoteDevices({remote_device});
+
+ EXPECT_EQ(prev_name, remote_device_ref.name());
+}
-} // namespace cryptauth \ No newline at end of file
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/remote_device_loader.cc b/chromium/components/cryptauth/remote_device_loader.cc
index 566bcf4f111..87fbe3887d3 100644
--- a/chromium/components/cryptauth/remote_device_loader.cc
+++ b/chromium/components/cryptauth/remote_device_loader.cc
@@ -80,8 +80,7 @@ RemoteDeviceLoader::RemoteDeviceLoader(
const std::string& user_id,
const std::string& user_private_key,
std::unique_ptr<cryptauth::SecureMessageDelegate> secure_message_delegate)
- : should_load_beacon_seeds_(false),
- remaining_devices_(device_info_list),
+ : remaining_devices_(device_info_list),
user_id_(user_id),
user_private_key_(user_private_key),
secure_message_delegate_(std::move(secure_message_delegate)),
@@ -89,10 +88,8 @@ RemoteDeviceLoader::RemoteDeviceLoader(
RemoteDeviceLoader::~RemoteDeviceLoader() {}
-void RemoteDeviceLoader::Load(bool should_load_beacon_seeds,
- const RemoteDeviceCallback& callback) {
+void RemoteDeviceLoader::Load(const RemoteDeviceCallback& callback) {
DCHECK(callback_.is_null());
- should_load_beacon_seeds_ = should_load_beacon_seeds;
callback_ = callback;
PA_LOG(INFO) << "Loading " << remaining_devices_.size()
<< " remote devices";
@@ -126,18 +123,14 @@ void RemoteDeviceLoader::OnPSKDerived(
DCHECK(iterator != remaining_devices_.end());
remaining_devices_.erase(iterator);
+ std::vector<BeaconSeed> beacon_seeds;
+ for (const BeaconSeed& beacon_seed : device.beacon_seeds())
+ beacon_seeds.push_back(beacon_seed);
+
RemoteDevice remote_device(
user_id_, device.friendly_device_name(), device.public_key(), psk,
- device.unlock_key(), device.mobile_hotspot_supported(),
- device.last_update_time_millis(), GetSoftwareFeatureToStateMap(device));
-
- if (should_load_beacon_seeds_) {
- std::vector<BeaconSeed> beacon_seeds;
- for (const BeaconSeed& beacon_seed : device.beacon_seeds()) {
- beacon_seeds.push_back(beacon_seed);
- }
- remote_device.LoadBeaconSeeds(beacon_seeds);
- }
+ device.last_update_time_millis(), GetSoftwareFeatureToStateMap(device),
+ beacon_seeds);
remote_devices_.push_back(remote_device);
diff --git a/chromium/components/cryptauth/remote_device_loader.h b/chromium/components/cryptauth/remote_device_loader.h
index 8dbf46c2476..035ba11cf3d 100644
--- a/chromium/components/cryptauth/remote_device_loader.h
+++ b/chromium/components/cryptauth/remote_device_loader.h
@@ -62,12 +62,9 @@ class RemoteDeviceLoader {
virtual ~RemoteDeviceLoader();
// Loads the RemoteDevice objects. |callback| will be invoked upon completion.
- // For space considerations, BeaconSeeds will only be loaded if
- // |should_load_beacon_seeds| is true.
typedef base::Callback<void(const cryptauth::RemoteDeviceList&)>
RemoteDeviceCallback;
- virtual void Load(bool should_load_beacon_seeds,
- const RemoteDeviceCallback& callback);
+ virtual void Load(const RemoteDeviceCallback& callback);
private:
// Called when the PSK is derived for each device. If the PSKs for all devices
@@ -75,9 +72,6 @@ class RemoteDeviceLoader {
void OnPSKDerived(const cryptauth::ExternalDeviceInfo& device,
const std::string& psk);
- // True if the loader should include BeaconSeeds in the loaded RemoteDevices.
- bool should_load_beacon_seeds_;
-
// The remaining devices whose PSK we're waiting on.
std::vector<cryptauth::ExternalDeviceInfo> remaining_devices_;
diff --git a/chromium/components/cryptauth/remote_device_loader_unittest.cc b/chromium/components/cryptauth/remote_device_loader_unittest.cc
index 680ea880886..57504566ce8 100644
--- a/chromium/components/cryptauth/remote_device_loader_unittest.cc
+++ b/chromium/components/cryptauth/remote_device_loader_unittest.cc
@@ -88,13 +88,13 @@ TEST_F(CryptAuthRemoteDeviceLoaderTest, LoadZeroDevices) {
EXPECT_CALL(*this, LoadCompleted());
loader.Load(
- false, base::Bind(&CryptAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
- base::Unretained(this)));
+ base::Bind(&CryptAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
+ base::Unretained(this)));
EXPECT_EQ(0u, remote_devices_.size());
}
-TEST_F(CryptAuthRemoteDeviceLoaderTest, LoadOneDeviceWithBeaconSeeds) {
+TEST_F(CryptAuthRemoteDeviceLoaderTest, LoadOneDevice) {
std::vector<cryptauth::ExternalDeviceInfo> device_infos(
1, CreateDeviceInfo("0"));
RemoteDeviceLoader loader(device_infos, user_private_key_, kUserId,
@@ -102,14 +102,13 @@ TEST_F(CryptAuthRemoteDeviceLoaderTest, LoadOneDeviceWithBeaconSeeds) {
EXPECT_CALL(*this, LoadCompleted());
loader.Load(
- true, base::Bind(&CryptAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
- base::Unretained(this)));
+ base::Bind(&CryptAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
+ base::Unretained(this)));
EXPECT_EQ(1u, remote_devices_.size());
EXPECT_FALSE(remote_devices_[0].persistent_symmetric_key.empty());
EXPECT_EQ(device_infos[0].friendly_device_name(), remote_devices_[0].name);
EXPECT_EQ(device_infos[0].public_key(), remote_devices_[0].public_key);
- EXPECT_TRUE(remote_devices_[0].are_beacon_seeds_loaded);
ASSERT_EQ(1u, remote_devices_[0].beacon_seeds.size());
const BeaconSeed& beacon_seed = remote_devices_[0].beacon_seeds[0];
@@ -118,65 +117,6 @@ TEST_F(CryptAuthRemoteDeviceLoaderTest, LoadOneDeviceWithBeaconSeeds) {
EXPECT_EQ(kBeaconSeedEndTimeMs, beacon_seed.end_time_millis());
}
-TEST_F(CryptAuthRemoteDeviceLoaderTest, LoadDevicesWithAndWithoutBeaconSeeds) {
- std::vector<cryptauth::ExternalDeviceInfo> device_infos(
- 1, CreateDeviceInfo("0"));
-
- RemoteDeviceLoader loader1(device_infos, user_private_key_, kUserId,
- std::make_unique<FakeSecureMessageDelegate>());
- EXPECT_CALL(*this, LoadCompleted());
- loader1.Load(
- false /* should_load_beacon_seeds */,
- base::Bind(&CryptAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
- base::Unretained(this)));
- RemoteDevice remote_device_without_beacon_seed = remote_devices_[0];
-
- RemoteDeviceLoader loader2(device_infos, user_private_key_, kUserId,
- std::make_unique<FakeSecureMessageDelegate>());
- EXPECT_CALL(*this, LoadCompleted());
- loader2.Load(
- true /* should_load_beacon_seeds */,
- base::Bind(&CryptAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
- base::Unretained(this)));
- RemoteDevice remote_device_with_beacon_seed = remote_devices_[0];
-
- EXPECT_EQ(remote_device_without_beacon_seed,
- remote_device_without_beacon_seed);
- EXPECT_EQ(remote_device_with_beacon_seed, remote_device_with_beacon_seed);
- EXPECT_FALSE(remote_device_with_beacon_seed ==
- remote_device_without_beacon_seed);
-}
-
-TEST_F(CryptAuthRemoteDeviceLoaderTest, BooleanAttributes) {
- cryptauth::ExternalDeviceInfo first = CreateDeviceInfo("0");
- first.set_unlock_key(true);
- first.set_mobile_hotspot_supported(true);
-
- cryptauth::ExternalDeviceInfo second = CreateDeviceInfo("1");
- second.set_unlock_key(false);
- second.set_mobile_hotspot_supported(false);
-
- std::vector<cryptauth::ExternalDeviceInfo> device_infos{first, second};
-
- RemoteDeviceLoader loader(device_infos, user_private_key_, kUserId,
- std::move(secure_message_delegate_));
-
- EXPECT_CALL(*this, LoadCompleted());
- loader.Load(
- false, base::Bind(&CryptAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
- base::Unretained(this)));
-
- EXPECT_EQ(2u, remote_devices_.size());
-
- EXPECT_FALSE(remote_devices_[0].persistent_symmetric_key.empty());
- EXPECT_TRUE(remote_devices_[0].unlock_key);
- EXPECT_TRUE(remote_devices_[0].supports_mobile_hotspot);
-
- EXPECT_FALSE(remote_devices_[1].persistent_symmetric_key.empty());
- EXPECT_FALSE(remote_devices_[1].unlock_key);
- EXPECT_FALSE(remote_devices_[1].supports_mobile_hotspot);
-}
-
TEST_F(CryptAuthRemoteDeviceLoaderTest, LastUpdateTimeMillis) {
cryptauth::ExternalDeviceInfo first = CreateDeviceInfo("0");
first.set_last_update_time_millis(1000);
@@ -191,8 +131,8 @@ TEST_F(CryptAuthRemoteDeviceLoaderTest, LastUpdateTimeMillis) {
EXPECT_CALL(*this, LoadCompleted());
loader.Load(
- false, base::Bind(&CryptAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
- base::Unretained(this)));
+ base::Bind(&CryptAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
+ base::Unretained(this)));
EXPECT_EQ(2u, remote_devices_.size());
@@ -220,8 +160,8 @@ TEST_F(CryptAuthRemoteDeviceLoaderTest, SoftwareFeatures) {
EXPECT_CALL(*this, LoadCompleted());
loader.Load(
- false, base::Bind(&CryptAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
- base::Unretained(this)));
+ base::Bind(&CryptAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
+ base::Unretained(this)));
EXPECT_EQ(1u, remote_devices_.size());
diff --git a/chromium/components/cryptauth/remote_device_provider_impl.cc b/chromium/components/cryptauth/remote_device_provider_impl.cc
index 967bb448a9d..b31c5f4f16e 100644
--- a/chromium/components/cryptauth/remote_device_provider_impl.cc
+++ b/chromium/components/cryptauth/remote_device_provider_impl.cc
@@ -58,7 +58,6 @@ RemoteDeviceProviderImpl::RemoteDeviceProviderImpl(
device_manager->GetSyncedDevices(), user_id, user_private_key,
cryptauth::SecureMessageDelegateImpl::Factory::NewInstance());
remote_device_loader_->Load(
- false /* should_load_beacon_seeds */,
base::Bind(&RemoteDeviceProviderImpl::OnRemoteDevicesLoaded,
weak_ptr_factory_.GetWeakPtr()));
}
@@ -78,7 +77,6 @@ void RemoteDeviceProviderImpl::OnSyncFinished(
cryptauth::SecureMessageDelegateImpl::Factory::NewInstance());
remote_device_loader_->Load(
- false /* should_load_beacon_seeds */,
base::Bind(&RemoteDeviceProviderImpl::OnRemoteDevicesLoaded,
weak_ptr_factory_.GetWeakPtr()));
}
diff --git a/chromium/components/cryptauth/remote_device_provider_impl_unittest.cc b/chromium/components/cryptauth/remote_device_provider_impl_unittest.cc
index 54174ebc76a..03559464815 100644
--- a/chromium/components/cryptauth/remote_device_provider_impl_unittest.cc
+++ b/chromium/components/cryptauth/remote_device_provider_impl_unittest.cc
@@ -143,8 +143,7 @@ class FakeDeviceLoader final : public cryptauth::RemoteDeviceLoader {
TestRemoteDeviceLoaderFactory* remote_device_loader_factory_;
- void Load(bool should_load_beacon_seeds,
- const RemoteDeviceCallback& callback) override {
+ void Load(const RemoteDeviceCallback& callback) override {
remote_device_loader_factory_->QueueCallback(callback);
}
};
diff --git a/chromium/components/cryptauth/remote_device_ref.cc b/chromium/components/cryptauth/remote_device_ref.cc
index 28587515518..ee4c237f511 100644
--- a/chromium/components/cryptauth/remote_device_ref.cc
+++ b/chromium/components/cryptauth/remote_device_ref.cc
@@ -60,8 +60,16 @@ bool RemoteDeviceRef::operator==(const RemoteDeviceRef& other) const {
return *remote_device_ == *other.remote_device_;
}
+bool RemoteDeviceRef::operator!=(const RemoteDeviceRef& other) const {
+ return !(*this == other);
+}
+
bool RemoteDeviceRef::operator<(const RemoteDeviceRef& other) const {
return *remote_device_ < *other.remote_device_;
}
-} // namespace cryptauth \ No newline at end of file
+const RemoteDevice& RemoteDeviceRef::GetRemoteDevice() const {
+ return *remote_device_;
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/remote_device_ref.h b/chromium/components/cryptauth/remote_device_ref.h
index f02ec6c334e..65b485e0e50 100644
--- a/chromium/components/cryptauth/remote_device_ref.h
+++ b/chromium/components/cryptauth/remote_device_ref.h
@@ -16,6 +16,12 @@
namespace chromeos {
class EasyUnlockServiceRegular;
+namespace multidevice_setup {
+class MultiDeviceSetupImpl;
+} // namespace multidevice_setup
+namespace secure_channel {
+class SecureChannelClientImpl;
+} // namespace secure_channel
namespace tether {
class TetherHostFetcherImpl;
class TetherHostFetcherImplTest;
@@ -29,8 +35,6 @@ class ProximityAuthWebUIHandler;
namespace cryptauth {
-class RemoteDeviceCache;
-
// Contains metadata specific to a device associated with a user's account.
// Because this metadata contains large and expensive data types, and that data
// can become stale if a Device Sync occurs during a client application's
@@ -59,19 +63,12 @@ class RemoteDeviceRef {
const std::string& persistent_symmetric_key() const {
return remote_device_->persistent_symmetric_key;
}
- bool unlock_key() const { return remote_device_->unlock_key; }
- bool supports_mobile_hotspot() const {
- return remote_device_->supports_mobile_hotspot;
- }
int64_t last_update_time_millis() const {
return remote_device_->last_update_time_millis;
}
const std::vector<BeaconSeed>& beacon_seeds() const {
return remote_device_->beacon_seeds;
}
- bool are_beacon_seeds_loaded() const {
- return remote_device_->are_beacon_seeds_loaded;
- }
std::string GetDeviceId() const;
SoftwareFeatureState GetSoftwareFeatureState(
@@ -83,15 +80,19 @@ class RemoteDeviceRef {
std::string GetTruncatedDeviceIdForLogs() const;
bool operator==(const RemoteDeviceRef& other) const;
-
- // This function is necessary in order to use |RemoteDeviceRef| as a key of a
- // std::map.
+ bool operator!=(const RemoteDeviceRef& other) const;
bool operator<(const RemoteDeviceRef& other) const;
private:
+ friend class chromeos::multidevice_setup::MultiDeviceSetupImpl;
+ friend class chromeos::secure_channel::SecureChannelClientImpl;
friend class RemoteDeviceCache;
friend class RemoteDeviceRefBuilder;
friend class RemoteDeviceRefTest;
+ friend bool IsSameDevice(const cryptauth::RemoteDevice& remote_device,
+ cryptauth::RemoteDeviceRef remote_device_ref);
+ friend RemoteDevice* GetMutableRemoteDevice(
+ const RemoteDeviceRef& remote_device_ref);
FRIEND_TEST_ALL_PREFIXES(RemoteDeviceRefTest, TestFields);
FRIEND_TEST_ALL_PREFIXES(RemoteDeviceRefTest, TestCopyAndAssign);
@@ -105,6 +106,11 @@ class RemoteDeviceRef {
explicit RemoteDeviceRef(std::shared_ptr<RemoteDevice> remote_device);
+ // Returns the raw RemoteDevice object. Should only be used when passing
+ // RemoteDevice objects through a Mojo API, which requires that the raw type
+ // is passed instead of the RemoteDeviceRef wrapper object.
+ const RemoteDevice& GetRemoteDevice() const;
+
std::shared_ptr<const RemoteDevice> remote_device_;
};
@@ -112,4 +118,4 @@ typedef std::vector<RemoteDeviceRef> RemoteDeviceRefList;
} // namespace cryptauth
-#endif // COMPONENTS_CRYPTAUTH_REMOTE_DEVICE_REF_H_ \ No newline at end of file
+#endif // COMPONENTS_CRYPTAUTH_REMOTE_DEVICE_REF_H_
diff --git a/chromium/components/cryptauth/remote_device_ref_unittest.cc b/chromium/components/cryptauth/remote_device_ref_unittest.cc
index 2b06c00a643..0485e86ccfc 100644
--- a/chromium/components/cryptauth/remote_device_ref_unittest.cc
+++ b/chromium/components/cryptauth/remote_device_ref_unittest.cc
@@ -27,12 +27,13 @@ class RemoteDeviceRefTest : public testing::Test {
[cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST] =
cryptauth::SoftwareFeatureState::kEnabled;
+ std::vector<BeaconSeed> beacon_seeds({BeaconSeed(), BeaconSeed()});
+
remote_device_ = std::make_shared<RemoteDevice>(
"user_id", "name", "public_key", "persistent_symmetric_key",
- true /* unlock_key */, true /* supports_mobile_hotspot */,
42000 /* last_update_time_millis */,
- software_feature_to_state_map /* software_features */);
- remote_device_->LoadBeaconSeeds({BeaconSeed(), BeaconSeed()});
+ software_feature_to_state_map /* software_features */,
+ beacon_seeds /* beacon_seeds */);
}
std::shared_ptr<RemoteDevice> remote_device_;
@@ -48,9 +49,6 @@ TEST_F(RemoteDeviceRefTest, TestFields) {
EXPECT_EQ(remote_device_->public_key, remote_device_ref.public_key());
EXPECT_EQ(remote_device_->persistent_symmetric_key,
remote_device_ref.persistent_symmetric_key());
- EXPECT_EQ(remote_device_->unlock_key, remote_device_ref.unlock_key());
- EXPECT_EQ(remote_device_->supports_mobile_hotspot,
- remote_device_ref.supports_mobile_hotspot());
EXPECT_EQ(remote_device_->last_update_time_millis,
remote_device_ref.last_update_time_millis());
EXPECT_EQ(&remote_device_->beacon_seeds, &remote_device_ref.beacon_seeds());
diff --git a/chromium/components/cryptauth/remote_device_test_util.cc b/chromium/components/cryptauth/remote_device_test_util.cc
index 062dfedfbe4..859a393080c 100644
--- a/chromium/components/cryptauth/remote_device_test_util.cc
+++ b/chromium/components/cryptauth/remote_device_test_util.cc
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <map>
+#include <string>
+
#include "components/cryptauth/remote_device_test_util.h"
#include "base/base64.h"
@@ -13,8 +16,6 @@ const char kTestRemoteDeviceUserId[] = "example@gmail.com";
const char kTestRemoteDeviceName[] = "remote device";
const char kTestRemoteDevicePublicKey[] = "public key";
const char kTestRemoteDevicePSK[] = "remote device psk";
-const bool kTestRemoteDeviceUnlockKey = true;
-const bool kTestRemoteDeviceSupportsMobileHotspot = true;
const int64_t kTestRemoteDeviceLastUpdateTimeMillis = 0L;
RemoteDeviceRefBuilder::RemoteDeviceRefBuilder() {
@@ -43,7 +44,17 @@ RemoteDeviceRefBuilder& RemoteDeviceRefBuilder::SetPublicKey(
RemoteDeviceRefBuilder& RemoteDeviceRefBuilder::SetSupportsMobileHotspot(
bool supports_mobile_hotspot) {
- remote_device_->supports_mobile_hotspot = supports_mobile_hotspot;
+ remote_device_
+ ->software_features[cryptauth::SoftwareFeature::MAGIC_TETHER_HOST] =
+ supports_mobile_hotspot ? cryptauth::SoftwareFeatureState::kSupported
+ : cryptauth::SoftwareFeatureState::kNotSupported;
+ return *this;
+}
+
+RemoteDeviceRefBuilder& RemoteDeviceRefBuilder::SetSoftwareFeatureState(
+ const SoftwareFeature feature,
+ const SoftwareFeatureState new_state) {
+ remote_device_->software_features[feature] = new_state;
return *this;
}
@@ -53,17 +64,28 @@ RemoteDeviceRefBuilder& RemoteDeviceRefBuilder::SetLastUpdateTimeMillis(
return *this;
}
+RemoteDeviceRefBuilder& RemoteDeviceRefBuilder::SetBeaconSeeds(
+ const std::vector<BeaconSeed>& beacon_seeds) {
+ remote_device_->beacon_seeds = beacon_seeds;
+ return *this;
+}
+
RemoteDeviceRef RemoteDeviceRefBuilder::Build() {
return RemoteDeviceRef(remote_device_);
}
RemoteDevice CreateRemoteDeviceForTest() {
+ std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>
+ software_features;
+ software_features[cryptauth::SoftwareFeature::EASY_UNLOCK_HOST] =
+ cryptauth::SoftwareFeatureState::kEnabled;
+ software_features[cryptauth::SoftwareFeature::MAGIC_TETHER_HOST] =
+ cryptauth::SoftwareFeatureState::kSupported;
+
return RemoteDevice(kTestRemoteDeviceUserId, kTestRemoteDeviceName,
kTestRemoteDevicePublicKey, kTestRemoteDevicePSK,
- kTestRemoteDeviceUnlockKey,
- kTestRemoteDeviceSupportsMobileHotspot,
- kTestRemoteDeviceLastUpdateTimeMillis,
- std::map<SoftwareFeature, SoftwareFeatureState>());
+ kTestRemoteDeviceLastUpdateTimeMillis, software_features,
+ {} /* beacon_seeds */);
}
RemoteDeviceRef CreateRemoteDeviceRefForTest() {
@@ -96,4 +118,17 @@ RemoteDeviceList CreateRemoteDeviceListForTest(size_t num_to_create) {
return generated_devices;
}
+RemoteDevice* GetMutableRemoteDevice(const RemoteDeviceRef& remote_device_ref) {
+ const RemoteDevice* remote_device = remote_device_ref.remote_device_.get();
+ return const_cast<RemoteDevice*>(remote_device);
+}
+
+bool IsSameDevice(const cryptauth::RemoteDevice& remote_device,
+ cryptauth::RemoteDeviceRef remote_device_ref) {
+ if (!remote_device_ref.remote_device_)
+ return false;
+
+ return remote_device == *remote_device_ref.remote_device_;
+}
+
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/remote_device_test_util.h b/chromium/components/cryptauth/remote_device_test_util.h
index b372c3e9a5b..b66a222d428 100644
--- a/chromium/components/cryptauth/remote_device_test_util.h
+++ b/chromium/components/cryptauth/remote_device_test_util.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_CRYPTAUTH_REMOTE_DEVICE_TEST_UTIL_H_
#define COMPONENTS_CRYPTAUTH_REMOTE_DEVICE_TEST_UTIL_H_
+#include <memory>
+#include <string>
#include <vector>
#include "components/cryptauth/remote_device_ref.h"
@@ -24,8 +26,13 @@ class RemoteDeviceRefBuilder {
RemoteDeviceRefBuilder& SetPublicKey(const std::string& public_key);
RemoteDeviceRefBuilder& SetSupportsMobileHotspot(
bool supports_mobile_hotspot);
+ RemoteDeviceRefBuilder& SetSoftwareFeatureState(
+ const SoftwareFeature feature,
+ const SoftwareFeatureState new_state);
RemoteDeviceRefBuilder& SetLastUpdateTimeMillis(
int64_t last_update_time_millis);
+ RemoteDeviceRefBuilder& SetBeaconSeeds(
+ const std::vector<BeaconSeed>& beacon_seeds);
RemoteDeviceRef Build();
private:
@@ -40,6 +47,11 @@ RemoteDeviceList CreateRemoteDeviceListForTest(size_t num_to_create);
RemoteDeviceRefList CreateRemoteDeviceRefListForTest(size_t num_to_create);
+RemoteDevice* GetMutableRemoteDevice(const RemoteDeviceRef& remote_device_ref);
+
+bool IsSameDevice(const cryptauth::RemoteDevice& remote_device,
+ cryptauth::RemoteDeviceRef remote_device_ref);
+
} // namespace cryptauth
#endif // COMPONENTS_CRYPTAUTH_REMOTE_DEVICE_TEST_UTIL_H_
diff --git a/chromium/components/cryptauth/secure_channel.cc b/chromium/components/cryptauth/secure_channel.cc
index 57b540ca024..7135b0ecc62 100644
--- a/chromium/components/cryptauth/secure_channel.cc
+++ b/chromium/components/cryptauth/secure_channel.cc
@@ -9,7 +9,6 @@
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "chromeos/components/proximity_auth/logging/logging.h"
-#include "components/cryptauth/cryptauth_service.h"
#include "components/cryptauth/secure_message_delegate_impl.h"
#include "components/cryptauth/wire_message.h"
@@ -20,13 +19,11 @@ SecureChannel::Factory* SecureChannel::Factory::factory_instance_ = nullptr;
// static
std::unique_ptr<SecureChannel> SecureChannel::Factory::NewInstance(
- std::unique_ptr<Connection> connection,
- CryptAuthService* cryptauth_service) {
+ std::unique_ptr<Connection> connection) {
if (!factory_instance_) {
factory_instance_ = new Factory();
}
- return factory_instance_->BuildInstance(std::move(connection),
- cryptauth_service);
+ return factory_instance_->BuildInstance(std::move(connection));
}
// static
@@ -35,10 +32,8 @@ void SecureChannel::Factory::SetInstanceForTesting(Factory* factory) {
}
std::unique_ptr<SecureChannel> SecureChannel::Factory::BuildInstance(
- std::unique_ptr<Connection> connection,
- CryptAuthService* cryptauth_service) {
- return base::WrapUnique(
- new SecureChannel(std::move(connection), cryptauth_service));
+ std::unique_ptr<Connection> connection) {
+ return base::WrapUnique(new SecureChannel(std::move(connection)));
}
// static
@@ -68,11 +63,9 @@ SecureChannel::PendingMessage::PendingMessage(const std::string& feature,
SecureChannel::PendingMessage::~PendingMessage() {}
-SecureChannel::SecureChannel(std::unique_ptr<Connection> connection,
- CryptAuthService* cryptauth_service)
+SecureChannel::SecureChannel(std::unique_ptr<Connection> connection)
: status_(Status::DISCONNECTED),
connection_(std::move(connection)),
- cryptauth_service_(cryptauth_service),
weak_ptr_factory_(this) {
connection_->AddObserver(this);
}
@@ -123,6 +116,23 @@ void SecureChannel::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
+void SecureChannel::GetConnectionRssi(
+ base::OnceCallback<void(base::Optional<int32_t>)> callback) {
+ if (!connection_) {
+ std::move(callback).Run(base::nullopt);
+ return;
+ }
+
+ connection_->GetConnectionRssi(std::move(callback));
+}
+
+base::Optional<std::string> SecureChannel::GetChannelBindingData() {
+ if (secure_context_)
+ return secure_context_->GetChannelBindingData();
+
+ return base::nullopt;
+}
+
void SecureChannel::OnConnectionStatusChanged(
Connection* connection,
Connection::Status old_status,
@@ -219,15 +229,6 @@ void SecureChannel::OnSendCompleted(const cryptauth::Connection& connection,
Disconnect();
}
-void SecureChannel::OnGattCharacteristicsNotAvailable() {
- NotifyGattCharacteristicsNotAvailable();
-}
-
-void SecureChannel::NotifyGattCharacteristicsNotAvailable() {
- for (auto& observer : observer_list_)
- observer.OnGattCharacteristicsNotAvailable();
-}
-
void SecureChannel::TransitionToStatus(const Status& new_status) {
if (new_status == status_) {
// Only report changes to state.
diff --git a/chromium/components/cryptauth/secure_channel.h b/chromium/components/cryptauth/secure_channel.h
index 201aa22c4a1..12044ccf1b3 100644
--- a/chromium/components/cryptauth/secure_channel.h
+++ b/chromium/components/cryptauth/secure_channel.h
@@ -17,8 +17,6 @@
namespace cryptauth {
-class CryptAuthService;
-
// An authenticated bi-directional channel for exchanging messages with remote
// devices. |SecureChannel| manages a |Connection| by initializing it and
// authenticating it via a security handshake once the connection has occurred.
@@ -51,42 +49,30 @@ class SecureChannel : public ConnectionObserver {
class Observer {
public:
- virtual void OnSecureChannelStatusChanged(
- SecureChannel* secure_channel,
- const Status& old_status,
- const Status& new_status) = 0;
+ virtual void OnSecureChannelStatusChanged(SecureChannel* secure_channel,
+ const Status& old_status,
+ const Status& new_status) {}
- virtual void OnMessageReceived(
- SecureChannel* secure_channel,
- const std::string& feature,
- const std::string& payload) = 0;
+ virtual void OnMessageReceived(SecureChannel* secure_channel,
+ const std::string& feature,
+ const std::string& payload) {}
// Called when a message has been sent successfully; |sequence_number|
// corresponds to the value returned by an earlier call to SendMessage().
virtual void OnMessageSent(SecureChannel* secure_channel,
int sequence_number) {}
-
- // Called when GATT characteristics are not available. This observer
- // function is a temporary work-around (see crbug.com/784968).
- // TODO(khorimoto): This observer function is specific to only one
- // SecureChannel implementation, so it is hacky to include it as part of
- // the observer for SecureChannel. Remove this work-around when it is no
- // longer necessary.
- virtual void OnGattCharacteristicsNotAvailable() {}
};
class Factory {
public:
static std::unique_ptr<SecureChannel> NewInstance(
- std::unique_ptr<Connection> connection,
- CryptAuthService* cryptauth_service);
+ std::unique_ptr<Connection> connection);
static void SetInstanceForTesting(Factory* factory);
protected:
virtual std::unique_ptr<SecureChannel> BuildInstance(
- std::unique_ptr<Connection> connection,
- CryptAuthService* cryptauth_service);
+ std::unique_ptr<Connection> connection);
private:
static Factory* factory_instance_;
@@ -107,6 +93,15 @@ class SecureChannel : public ConnectionObserver {
virtual void AddObserver(Observer* observer);
virtual void RemoveObserver(Observer* observer);
+ // Returns the RSSI of the connection; if no derived class overrides this
+ // function, base::nullopt is returned.
+ virtual void GetConnectionRssi(
+ base::OnceCallback<void(base::Optional<int32_t>)> callback);
+
+ // The |responder_auth| message. Returns null if |secure_context_| is null or
+ // status() != AUTHENTICATED.
+ virtual base::Optional<std::string> GetChannelBindingData();
+
Status status() const {
return status_;
}
@@ -120,13 +115,9 @@ class SecureChannel : public ConnectionObserver {
void OnSendCompleted(const cryptauth::Connection& connection,
const cryptauth::WireMessage& wire_message,
bool success) override;
- void OnGattCharacteristicsNotAvailable() override;
protected:
- SecureChannel(std::unique_ptr<Connection> connection,
- CryptAuthService* cryptauth_service);
-
- void NotifyGattCharacteristicsNotAvailable();
+ SecureChannel(std::unique_ptr<Connection> connection);
Status status_;
@@ -160,7 +151,6 @@ class SecureChannel : public ConnectionObserver {
std::unique_ptr<SecureContext> secure_context);
std::unique_ptr<Connection> connection_;
- CryptAuthService* cryptauth_service_; // Outlives this instance.
std::unique_ptr<Authenticator> authenticator_;
std::unique_ptr<SecureContext> secure_context_;
base::queue<std::unique_ptr<PendingMessage>> queued_messages_;
diff --git a/chromium/components/cryptauth/secure_channel_unittest.cc b/chromium/components/cryptauth/secure_channel_unittest.cc
index 8aeb0a58c33..33b2c77693a 100644
--- a/chromium/components/cryptauth/secure_channel_unittest.cc
+++ b/chromium/components/cryptauth/secure_channel_unittest.cc
@@ -12,7 +12,6 @@
#include "base/memory/weak_ptr.h"
#include "components/cryptauth/fake_authenticator.h"
#include "components/cryptauth/fake_connection.h"
-#include "components/cryptauth/fake_cryptauth_service.h"
#include "components/cryptauth/fake_secure_context.h"
#include "components/cryptauth/fake_secure_message_delegate.h"
#include "components/cryptauth/remote_device_ref.h"
@@ -69,10 +68,6 @@ class TestObserver final : public SecureChannel::Observer {
return sent_sequence_numbers_;
}
- int num_gatt_services_unavailable_events() {
- return num_gatt_services_unavailable_events_;
- }
-
// SecureChannel::Observer:
void OnSecureChannelStatusChanged(
SecureChannel* secure_channel,
@@ -96,16 +91,11 @@ class TestObserver final : public SecureChannel::Observer {
sent_sequence_numbers_.push_back(sequence_number);
}
- void OnGattCharacteristicsNotAvailable() override {
- ++num_gatt_services_unavailable_events_;
- }
-
private:
SecureChannel* secure_channel_;
std::vector<SecureChannelStatusChange> connection_status_changes_;
std::vector<ReceivedMessage> received_messages_;
std::vector<int> sent_sequence_numbers_;
- int num_gatt_services_unavailable_events_ = 0;
};
// Observer used in the ObserverDeletesChannel test. This Observer deletes the
@@ -172,8 +162,6 @@ class CryptAuthSecureChannelTest : public testing::Test {
weak_ptr_factory_(this) {}
void SetUp() override {
- has_verified_gatt_services_event_ = false;
-
test_authenticator_factory_ = std::make_unique<TestAuthenticatorFactory>();
DeviceToDeviceAuthenticator::Factory::SetInstanceForTesting(
test_authenticator_factory_.get());
@@ -185,14 +173,12 @@ class CryptAuthSecureChannelTest : public testing::Test {
fake_secure_context_ = nullptr;
- fake_cryptauth_service_ = std::make_unique<FakeCryptAuthService>();
-
fake_connection_ =
new FakeConnection(test_device_, /* should_auto_connect */ false);
EXPECT_FALSE(fake_connection_->observers().size());
- secure_channel_ = base::WrapUnique(new SecureChannel(
- base::WrapUnique(fake_connection_), fake_cryptauth_service_.get()));
+ secure_channel_ =
+ base::WrapUnique(new SecureChannel(base::WrapUnique(fake_connection_)));
EXPECT_EQ(static_cast<size_t>(1), fake_connection_->observers().size());
EXPECT_EQ(secure_channel_.get(), fake_connection_->observers()[0]);
@@ -212,9 +198,6 @@ class CryptAuthSecureChannelTest : public testing::Test {
if (secure_channel_)
VerifyNoMessageBeingSent();
- if (!has_verified_gatt_services_event_)
- EXPECT_EQ(0, test_observer_->num_gatt_services_unavailable_events());
-
cryptauth::SecureMessageDelegateImpl::Factory::SetInstanceForTesting(
nullptr);
}
@@ -362,12 +345,19 @@ class CryptAuthSecureChannelTest : public testing::Test {
EXPECT_EQ(expected_payload, wire_message->payload());
}
- void VerifyGattServicesUnavailableEvent() {
- EXPECT_EQ(1, test_observer_->num_gatt_services_unavailable_events());
- has_verified_gatt_services_event_ = true;
+ void VerifyRssi(base::Optional<int32_t> expected_rssi) {
+ fake_connection_->set_rssi_to_return(expected_rssi);
+
+ secure_channel_->GetConnectionRssi(base::Bind(
+ &CryptAuthSecureChannelTest::OnConnectionRssi, base::Unretained(this)));
+
+ base::Optional<int32_t> rssi = rssi_;
+ rssi_.reset();
+
+ EXPECT_EQ(expected_rssi, rssi);
}
- bool has_verified_gatt_services_event_;
+ void OnConnectionRssi(base::Optional<int32_t> rssi) { rssi_ = rssi; }
// Owned by secure_channel_.
FakeConnection* fake_connection_;
@@ -375,8 +365,6 @@ class CryptAuthSecureChannelTest : public testing::Test {
std::unique_ptr<FakeSecureMessageDelegateFactory>
fake_secure_message_delegate_factory_;
- std::unique_ptr<FakeCryptAuthService> fake_cryptauth_service_;
-
// Owned by secure_channel_ once authentication has completed successfully.
FakeSecureContext* fake_secure_context_;
@@ -392,9 +380,10 @@ class CryptAuthSecureChannelTest : public testing::Test {
const RemoteDeviceRef test_device_;
+ base::Optional<int32_t> rssi_;
+
base::WeakPtrFactory<CryptAuthSecureChannelTest> weak_ptr_factory_;
- private:
DISALLOW_COPY_AND_ASSIGN(CryptAuthSecureChannelTest);
};
@@ -416,21 +405,6 @@ TEST_F(CryptAuthSecureChannelTest, ConnectionAttemptFails) {
});
}
-TEST_F(CryptAuthSecureChannelTest, GattServicesUnavailable) {
- secure_channel_->Initialize();
- VerifyConnectionStateChanges(std::vector<SecureChannelStatusChange>{
- {SecureChannel::Status::DISCONNECTED,
- SecureChannel::Status::CONNECTING}});
-
- fake_connection_->NotifyGattCharacteristicsNotAvailable();
- VerifyGattServicesUnavailableEvent();
-
- fake_connection_->CompleteInProgressConnection(/* success */ false);
- VerifyConnectionStateChanges(std::vector<SecureChannelStatusChange>{
- {SecureChannel::Status::CONNECTING,
- SecureChannel::Status::DISCONNECTED}});
-}
-
TEST_F(CryptAuthSecureChannelTest, DisconnectBeforeAuthentication) {
secure_channel_->Initialize();
VerifyConnectionStateChanges(std::vector<SecureChannelStatusChange> {
@@ -674,4 +648,18 @@ TEST_F(CryptAuthSecureChannelTest, ObserverDeletesChannel) {
EXPECT_FALSE(secure_channel_);
}
+TEST_F(CryptAuthSecureChannelTest, GetRssi) {
+ // Test a few different values.
+ VerifyRssi(-50 /* expected_rssi */);
+ VerifyRssi(-40 /* expected_rssi */);
+ VerifyRssi(-30 /* expected_rssi */);
+}
+
+TEST_F(CryptAuthSecureChannelTest, GetChannelBindingData) {
+ ConnectAndAuthenticate();
+
+ fake_secure_context_->set_channel_binding_data("channel_binding_data");
+ EXPECT_EQ("channel_binding_data", secure_channel_->GetChannelBindingData());
+}
+
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/session_keys.cc b/chromium/components/cryptauth/session_keys.cc
index 74783a2d30b..04705437296 100644
--- a/chromium/components/cryptauth/session_keys.cc
+++ b/chromium/components/cryptauth/session_keys.cc
@@ -30,15 +30,10 @@ const char kResponderPurpose[] = "responder";
SessionKeys::SessionKeys(const std::string& master_symmetric_key) {
std::string salt(reinterpret_cast<const char*>(&kSalt[0]), sizeof(kSalt));
- // We set the key_length to the length of the expected output and then take
- // the result from the first key, which is the client write key.
- crypto::HKDF initiator_encode(master_symmetric_key, salt, kInitiatorPurpose,
- kKeySize, 0, 0);
- initiator_encode_key_ = initiator_encode.client_write_key().as_string();
-
- crypto::HKDF responder_encode(master_symmetric_key, salt, kResponderPurpose,
- kKeySize, 0, 0);
- responder_encode_key_ = responder_encode.client_write_key().as_string();
+ initiator_encode_key_ = crypto::HkdfSha256(master_symmetric_key, salt,
+ kInitiatorPurpose, kKeySize);
+ responder_encode_key_ = crypto::HkdfSha256(master_symmetric_key, salt,
+ kResponderPurpose, kKeySize);
}
SessionKeys::SessionKeys() {}
diff --git a/chromium/components/cryptauth/software_feature_manager.h b/chromium/components/cryptauth/software_feature_manager.h
index d676c8a5624..e87f5db6e8a 100644
--- a/chromium/components/cryptauth/software_feature_manager.h
+++ b/chromium/components/cryptauth/software_feature_manager.h
@@ -6,6 +6,7 @@
#define COMPONENTS_CRYPTAUTH_SOFTWARE_FEATURE_MANAGER_H_
#include "base/callback.h"
+#include "components/cryptauth/network_request_error.h"
#include "components/cryptauth/proto/cryptauth_api.pb.h"
namespace cryptauth {
@@ -20,12 +21,16 @@ class SoftwareFeatureManager {
// |public_key|. If |enabled| and |is_exclusive| are both true, then all other
// devices associated with this account will have |sofware_feature| disabled.
// |is_exclusive| is ignored if |enabled| is false.
+ //
+ // Note: In the special case of passing |software_feature| =
+ // SoftwareFeature::EASY_UNLOCK_HOST and |enabled| = false, |public_key| is
+ // ignored.
virtual void SetSoftwareFeatureState(
const std::string& public_key,
SoftwareFeature software_feature,
bool enabled,
const base::Closure& success_callback,
- const base::Callback<void(const std::string&)>& error_callback,
+ const base::Callback<void(NetworkRequestError)>& error_callback,
bool is_exclusive = false) = 0;
// Finds eligible devices associated with the logged-in account which support
@@ -35,7 +40,7 @@ class SoftwareFeatureManager {
const base::Callback<void(const std::vector<ExternalDeviceInfo>&,
const std::vector<IneligibleDevice>&)>&
success_callback,
- const base::Callback<void(const std::string&)>& error_callback) = 0;
+ const base::Callback<void(NetworkRequestError)>& error_callback) = 0;
};
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/software_feature_manager_impl.cc b/chromium/components/cryptauth/software_feature_manager_impl.cc
index 6aa36e6a731..95437005acf 100644
--- a/chromium/components/cryptauth/software_feature_manager_impl.cc
+++ b/chromium/components/cryptauth/software_feature_manager_impl.cc
@@ -45,7 +45,7 @@ SoftwareFeatureManagerImpl::Factory::BuildInstance(
SoftwareFeatureManagerImpl::Request::Request(
std::unique_ptr<ToggleEasyUnlockRequest> toggle_request,
const base::Closure& set_software_success_callback,
- const base::Callback<void(const std::string&)> error_callback)
+ const base::Callback<void(NetworkRequestError)> error_callback)
: error_callback(error_callback),
toggle_request(std::move(toggle_request)),
set_software_success_callback(set_software_success_callback) {}
@@ -55,7 +55,7 @@ SoftwareFeatureManagerImpl::Request::Request(
const base::Callback<void(const std::vector<ExternalDeviceInfo>&,
const std::vector<IneligibleDevice>&)>
find_hosts_success_callback,
- const base::Callback<void(const std::string&)> error_callback)
+ const base::Callback<void(NetworkRequestError)> error_callback)
: error_callback(error_callback),
find_request(std::move(find_request)),
find_hosts_success_callback(find_hosts_success_callback) {}
@@ -74,20 +74,22 @@ void SoftwareFeatureManagerImpl::SetSoftwareFeatureState(
SoftwareFeature software_feature,
bool enabled,
const base::Closure& success_callback,
- const base::Callback<void(const std::string&)>& error_callback,
+ const base::Callback<void(NetworkRequestError)>& error_callback,
bool is_exclusive) {
// Note: For legacy reasons, this proto message mentions "ToggleEasyUnlock"
// instead of "SetSoftwareFeature" in its name.
auto request = std::make_unique<ToggleEasyUnlockRequest>();
- request->set_public_key(public_key);
request->set_feature(software_feature);
request->set_enable(enabled);
request->set_is_exclusive(enabled && is_exclusive);
// Special case for EasyUnlock: if EasyUnlock is being disabled, set the
- // apply_to_all property to true.
- request->set_apply_to_all(!enabled && software_feature ==
- SoftwareFeature::EASY_UNLOCK_HOST);
+ // apply_to_all property to true, and do not set the public_key field.
+ bool turn_off_easy_unlock_special_case =
+ !enabled && software_feature == SoftwareFeature::EASY_UNLOCK_HOST;
+ request->set_apply_to_all(turn_off_easy_unlock_special_case);
+ if (!turn_off_easy_unlock_special_case)
+ request->set_public_key(public_key);
pending_requests_.emplace(std::make_unique<Request>(
std::move(request), success_callback, error_callback));
@@ -99,7 +101,7 @@ void SoftwareFeatureManagerImpl::FindEligibleDevices(
const base::Callback<void(const std::vector<ExternalDeviceInfo>&,
const std::vector<IneligibleDevice>&)>&
success_callback,
- const base::Callback<void(const std::string&)>& error_callback) {
+ const base::Callback<void(NetworkRequestError)>& error_callback) {
// Note: For legacy reasons, this proto message mentions "UnlockDevices"
// instead of "MultiDeviceHosts" in its name.
auto request = std::make_unique<FindEligibleUnlockDevicesRequest>();
@@ -168,9 +170,9 @@ void SoftwareFeatureManagerImpl::OnFindEligibleUnlockDevicesResponse(
ProcessRequestQueue();
}
-void SoftwareFeatureManagerImpl::OnErrorResponse(const std::string& response) {
+void SoftwareFeatureManagerImpl::OnErrorResponse(NetworkRequestError error) {
current_cryptauth_client_.reset();
- current_request_->error_callback.Run(response);
+ current_request_->error_callback.Run(error);
current_request_.reset();
ProcessRequestQueue();
}
diff --git a/chromium/components/cryptauth/software_feature_manager_impl.h b/chromium/components/cryptauth/software_feature_manager_impl.h
index 3f7ec6c1d69..e2333031e11 100644
--- a/chromium/components/cryptauth/software_feature_manager_impl.h
+++ b/chromium/components/cryptauth/software_feature_manager_impl.h
@@ -44,14 +44,14 @@ class SoftwareFeatureManagerImpl : public SoftwareFeatureManager {
SoftwareFeature software_feature,
bool enabled,
const base::Closure& success_callback,
- const base::Callback<void(const std::string&)>& error_callback,
+ const base::Callback<void(NetworkRequestError)>& error_callback,
bool is_exclusive = false) override;
void FindEligibleDevices(
SoftwareFeature software_feature,
const base::Callback<void(const std::vector<ExternalDeviceInfo>&,
const std::vector<IneligibleDevice>&)>&
success_callback,
- const base::Callback<void(const std::string&)>& error_callback) override;
+ const base::Callback<void(NetworkRequestError)>& error_callback) override;
private:
enum class RequestType {
@@ -63,18 +63,18 @@ class SoftwareFeatureManagerImpl : public SoftwareFeatureManager {
// Used for SET_SOFTWARE_FEATURE Requests.
Request(std::unique_ptr<ToggleEasyUnlockRequest> toggle_request,
const base::Closure& set_software_success_callback,
- const base::Callback<void(const std::string&)> error_callback);
+ const base::Callback<void(NetworkRequestError)> error_callback);
// Used for FIND_ELIGIBLE_MULTIDEVICE_HOSTS Requests.
Request(std::unique_ptr<FindEligibleUnlockDevicesRequest> find_request,
const base::Callback<void(const std::vector<ExternalDeviceInfo>&,
const std::vector<IneligibleDevice>&)>
find_hosts_success_callback,
- const base::Callback<void(const std::string&)> error_callback);
+ const base::Callback<void(NetworkRequestError)> error_callback);
~Request();
- const base::Callback<void(const std::string&)> error_callback;
+ const base::Callback<void(NetworkRequestError)> error_callback;
// Set for SET_SOFTWARE_FEATURE; unset otherwise.
std::unique_ptr<ToggleEasyUnlockRequest> toggle_request;
@@ -96,7 +96,7 @@ class SoftwareFeatureManagerImpl : public SoftwareFeatureManager {
void OnToggleEasyUnlockResponse(const ToggleEasyUnlockResponse& response);
void OnFindEligibleUnlockDevicesResponse(
const FindEligibleUnlockDevicesResponse& response);
- void OnErrorResponse(const std::string& response);
+ void OnErrorResponse(NetworkRequestError response);
CryptAuthClientFactory* crypt_auth_client_factory_;
diff --git a/chromium/components/cryptauth/software_feature_manager_impl_unittest.cc b/chromium/components/cryptauth/software_feature_manager_impl_unittest.cc
index 2d951fa505a..34f3d0616dc 100644
--- a/chromium/components/cryptauth/software_feature_manager_impl_unittest.cc
+++ b/chromium/components/cryptauth/software_feature_manager_impl_unittest.cc
@@ -19,9 +19,13 @@ namespace cryptauth {
namespace {
-const char kSuccessResult[] = "success";
-const char kErrorSetSoftwareFeatureState[] = "setSoftwareFeatureStateError";
-const char kErrorFindEligibleDevices[] = "findEligibleDevicesError";
+enum class Result { kSuccess, kErrorSettingFeature, kErrorFindingEligible };
+
+// Arbitrarily choose different error types which match to Result types.
+const NetworkRequestError kErrorSettingFeatureNetworkRequestError =
+ NetworkRequestError::kOffline;
+const NetworkRequestError kErrorFindingEligibleNetworkRequestError =
+ NetworkRequestError::kEndpointNotFound;
std::vector<cryptauth::ExternalDeviceInfo>
CreateExternalDeviceInfosForRemoteDevices(
@@ -86,7 +90,7 @@ class CryptAuthSoftwareFeatureManagerImplTest
last_toggle_request_ = request;
toggle_easy_unlock_callback_ = callback;
error_callback_ = error_callback;
- error_code_ = kErrorSetSoftwareFeatureState;
+ error_code_ = kErrorSettingFeatureNetworkRequestError;
}
// Mock CryptAuthClient::FindEligibleUnlockDevices() implementation.
@@ -97,7 +101,7 @@ class CryptAuthSoftwareFeatureManagerImplTest
last_find_request_ = request;
find_eligible_unlock_devices_callback_ = callback;
error_callback_ = error_callback;
- error_code_ = kErrorFindEligibleDevices;
+ error_code_ = kErrorFindingEligibleNetworkRequestError;
}
FindEligibleUnlockDevicesResponse CreateFindEligibleUnlockDevicesResponse() {
@@ -166,17 +170,24 @@ class CryptAuthSoftwareFeatureManagerImplTest
base::Unretained(this)));
}
- void OnSoftwareFeatureStateSet() { result_ = kSuccessResult; }
+ void OnSoftwareFeatureStateSet() { result_ = Result::kSuccess; }
void OnEligibleDevicesFound(
const std::vector<ExternalDeviceInfo>& eligible_devices,
const std::vector<IneligibleDevice>& ineligible_devices) {
- result_ = kSuccessResult;
+ result_ = Result::kSuccess;
result_eligible_devices_ = eligible_devices;
result_ineligible_devices_ = ineligible_devices;
}
- void OnError(const std::string& error_message) { result_ = error_message; }
+ void OnError(NetworkRequestError error) {
+ if (error == kErrorSettingFeatureNetworkRequestError)
+ result_ = Result::kErrorSettingFeature;
+ else if (error == kErrorFindingEligibleNetworkRequestError)
+ result_ = Result::kErrorFindingEligible;
+ else
+ NOTREACHED();
+ }
void InvokeSetSoftwareFeatureCallback() {
CryptAuthClient::ToggleEasyUnlockCallback success_callback =
@@ -199,12 +210,13 @@ class CryptAuthSoftwareFeatureManagerImplTest
CryptAuthClient::ErrorCallback error_callback = error_callback_;
ASSERT_TRUE(!error_callback.is_null());
error_callback_.Reset();
- error_callback.Run(error_code_);
+ error_callback.Run(*error_code_);
}
- std::string GetResultAndReset() {
- std::string result;
- result.swap(result_);
+ Result GetResultAndReset() {
+ EXPECT_TRUE(result_);
+ Result result = *result_;
+ result_.reset();
return result;
}
@@ -220,11 +232,11 @@ class CryptAuthSoftwareFeatureManagerImplTest
// Set when a CryptAuthClient function returns. If empty, no callback has been
// invoked.
- std::string result_;
+ base::Optional<Result> result_;
// The code passed to the error callback; varies depending on what
// CryptAuthClient function is invoked.
- std::string error_code_;
+ base::Optional<NetworkRequestError> error_code_;
// For SetSoftwareFeatureState() tests.
ToggleEasyUnlockRequest last_toggle_request_;
@@ -256,12 +268,12 @@ TEST_F(CryptAuthSoftwareFeatureManagerImplTest, TestOrderUponMultipleRequests) {
EXPECT_EQ(true, last_toggle_request_.enable());
EXPECT_EQ(false, last_toggle_request_.is_exclusive());
InvokeSetSoftwareFeatureCallback();
- EXPECT_EQ(kSuccessResult, GetResultAndReset());
+ EXPECT_EQ(Result::kSuccess, GetResultAndReset());
EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST,
last_find_request_.feature());
InvokeFindEligibleDevicesCallback(CreateFindEligibleUnlockDevicesResponse());
- EXPECT_EQ(kSuccessResult, GetResultAndReset());
+ EXPECT_EQ(Result::kSuccess, GetResultAndReset());
VerifyDeviceEligibility();
EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_CLIENT,
@@ -269,12 +281,12 @@ TEST_F(CryptAuthSoftwareFeatureManagerImplTest, TestOrderUponMultipleRequests) {
EXPECT_EQ(false, last_toggle_request_.enable());
EXPECT_EQ(false, last_toggle_request_.is_exclusive());
InvokeSetSoftwareFeatureCallback();
- EXPECT_EQ(kSuccessResult, GetResultAndReset());
+ EXPECT_EQ(Result::kSuccess, GetResultAndReset());
EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_CLIENT,
last_find_request_.feature());
InvokeFindEligibleDevicesCallback(CreateFindEligibleUnlockDevicesResponse());
- EXPECT_EQ(kSuccessResult, GetResultAndReset());
+ EXPECT_EQ(Result::kSuccess, GetResultAndReset());
VerifyDeviceEligibility();
}
@@ -295,21 +307,21 @@ TEST_F(CryptAuthSoftwareFeatureManagerImplTest,
EXPECT_EQ(true, last_toggle_request_.enable());
EXPECT_EQ(false, last_toggle_request_.is_exclusive());
InvokeErrorCallback();
- EXPECT_EQ(kErrorSetSoftwareFeatureState, GetResultAndReset());
+ EXPECT_EQ(Result::kErrorSettingFeature, GetResultAndReset());
EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_CLIENT,
last_toggle_request_.feature());
EXPECT_EQ(false, last_toggle_request_.enable());
EXPECT_EQ(false, last_toggle_request_.is_exclusive());
InvokeSetSoftwareFeatureCallback();
- EXPECT_EQ(kSuccessResult, GetResultAndReset());
+ EXPECT_EQ(Result::kSuccess, GetResultAndReset());
EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST,
last_toggle_request_.feature());
EXPECT_EQ(true, last_toggle_request_.enable());
EXPECT_EQ(false, last_toggle_request_.is_exclusive());
InvokeSetSoftwareFeatureCallback();
- EXPECT_EQ(kSuccessResult, GetResultAndReset());
+ EXPECT_EQ(Result::kSuccess, GetResultAndReset());
}
TEST_F(CryptAuthSoftwareFeatureManagerImplTest,
@@ -321,18 +333,18 @@ TEST_F(CryptAuthSoftwareFeatureManagerImplTest,
EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST,
last_find_request_.feature());
InvokeFindEligibleDevicesCallback(CreateFindEligibleUnlockDevicesResponse());
- EXPECT_EQ(kSuccessResult, GetResultAndReset());
+ EXPECT_EQ(Result::kSuccess, GetResultAndReset());
VerifyDeviceEligibility();
EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_CLIENT,
last_find_request_.feature());
InvokeErrorCallback();
- EXPECT_EQ(kErrorFindEligibleDevices, GetResultAndReset());
+ EXPECT_EQ(Result::kErrorFindingEligible, GetResultAndReset());
EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST,
last_find_request_.feature());
InvokeFindEligibleDevicesCallback(CreateFindEligibleUnlockDevicesResponse());
- EXPECT_EQ(kSuccessResult, GetResultAndReset());
+ EXPECT_EQ(Result::kSuccess, GetResultAndReset());
VerifyDeviceEligibility();
}
@@ -345,12 +357,12 @@ TEST_F(CryptAuthSoftwareFeatureManagerImplTest, TestOrderViaMultipleErrors) {
EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST,
last_toggle_request_.feature());
InvokeErrorCallback();
- EXPECT_EQ(kErrorSetSoftwareFeatureState, GetResultAndReset());
+ EXPECT_EQ(Result::kErrorSettingFeature, GetResultAndReset());
EXPECT_EQ(SoftwareFeature::BETTER_TOGETHER_HOST,
last_find_request_.feature());
InvokeErrorCallback();
- EXPECT_EQ(kErrorFindEligibleDevices, GetResultAndReset());
+ EXPECT_EQ(Result::kErrorFindingEligible, GetResultAndReset());
}
TEST_F(CryptAuthSoftwareFeatureManagerImplTest, TestIsExclusive) {
@@ -363,7 +375,7 @@ TEST_F(CryptAuthSoftwareFeatureManagerImplTest, TestIsExclusive) {
EXPECT_EQ(true, last_toggle_request_.enable());
EXPECT_EQ(true, last_toggle_request_.is_exclusive());
InvokeErrorCallback();
- EXPECT_EQ(kErrorSetSoftwareFeatureState, GetResultAndReset());
+ EXPECT_EQ(Result::kErrorSettingFeature, GetResultAndReset());
}
TEST_F(CryptAuthSoftwareFeatureManagerImplTest, TestEasyUnlockSpecialCase) {
@@ -375,8 +387,9 @@ TEST_F(CryptAuthSoftwareFeatureManagerImplTest, TestEasyUnlockSpecialCase) {
EXPECT_EQ(false, last_toggle_request_.enable());
// apply_to_all() should be false when disabling EasyUnlock host capabilities.
EXPECT_EQ(true, last_toggle_request_.apply_to_all());
+ EXPECT_FALSE(last_toggle_request_.has_public_key());
InvokeErrorCallback();
- EXPECT_EQ(kErrorSetSoftwareFeatureState, GetResultAndReset());
+ EXPECT_EQ(Result::kErrorSettingFeature, GetResultAndReset());
}
} // namespace cryptauth
diff --git a/chromium/components/cryptauth/software_feature_state.cc b/chromium/components/cryptauth/software_feature_state.cc
new file mode 100644
index 00000000000..915b0d59d4c
--- /dev/null
+++ b/chromium/components/cryptauth/software_feature_state.cc
@@ -0,0 +1,25 @@
+// 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/cryptauth/software_feature_state.h"
+
+namespace cryptauth {
+
+std::ostream& operator<<(std::ostream& stream,
+ const SoftwareFeatureState& state) {
+ switch (state) {
+ case SoftwareFeatureState::kNotSupported:
+ stream << "[not supported]";
+ break;
+ case SoftwareFeatureState::kSupported:
+ stream << "[supported]";
+ break;
+ case SoftwareFeatureState::kEnabled:
+ stream << "[enabled]";
+ break;
+ }
+ return stream;
+}
+
+} // namespace cryptauth
diff --git a/chromium/components/cryptauth/software_feature_state.h b/chromium/components/cryptauth/software_feature_state.h
index bd21b37b2df..0e2a891524b 100644
--- a/chromium/components/cryptauth/software_feature_state.h
+++ b/chromium/components/cryptauth/software_feature_state.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_CRYPTAUTH_SOFTWARE_FEATURE_STATE_H_
#define COMPONENTS_CRYPTAUTH_SOFTWARE_FEATURE_STATE_H_
+#include <ostream>
+
namespace cryptauth {
enum class SoftwareFeatureState {
@@ -13,6 +15,9 @@ enum class SoftwareFeatureState {
kEnabled = 2
};
+std::ostream& operator<<(std::ostream& stream,
+ const SoftwareFeatureState& state);
+
} // namespace cryptauth
-#endif // COMPONENTS_CRYPTAUTH_SOFTWARE_FEATURE_STATE_H_ \ No newline at end of file
+#endif // COMPONENTS_CRYPTAUTH_SOFTWARE_FEATURE_STATE_H_
diff --git a/chromium/components/cryptauth/sync_scheduler_impl.cc b/chromium/components/cryptauth/sync_scheduler_impl.cc
index 2fdd9d700c0..c5f5c311a8d 100644
--- a/chromium/components/cryptauth/sync_scheduler_impl.cc
+++ b/chromium/components/cryptauth/sync_scheduler_impl.cc
@@ -116,10 +116,8 @@ void SyncSchedulerImpl::OnTimerFired() {
std::make_unique<SyncRequest>(weak_ptr_factory_.GetWeakPtr()));
}
-std::unique_ptr<base::Timer> SyncSchedulerImpl::CreateTimer() {
- bool retain_user_task = false;
- bool is_repeating = false;
- return std::make_unique<base::Timer>(retain_user_task, is_repeating);
+std::unique_ptr<base::OneShotTimer> SyncSchedulerImpl::CreateTimer() {
+ return std::make_unique<base::OneShotTimer>();
}
void SyncSchedulerImpl::ScheduleNextSync(const base::TimeDelta& sync_delta) {
diff --git a/chromium/components/cryptauth/sync_scheduler_impl.h b/chromium/components/cryptauth/sync_scheduler_impl.h
index 3d61bb0015f..77ffcd8d0ce 100644
--- a/chromium/components/cryptauth/sync_scheduler_impl.h
+++ b/chromium/components/cryptauth/sync_scheduler_impl.h
@@ -42,8 +42,8 @@ class SyncSchedulerImpl : public SyncScheduler {
SyncState GetSyncState() const override;
protected:
- // Creates and returns a base::Timer object. Exposed for testing.
- virtual std::unique_ptr<base::Timer> CreateTimer();
+ // Creates and returns a base::OneShotTimer object. Exposed for testing.
+ virtual std::unique_ptr<base::OneShotTimer> CreateTimer();
private:
// SyncScheduler:
@@ -92,7 +92,7 @@ class SyncSchedulerImpl : public SyncScheduler {
size_t failure_count_;
// Timer firing for the next sync request.
- std::unique_ptr<base::Timer> timer_;
+ std::unique_ptr<base::OneShotTimer> timer_;
base::WeakPtrFactory<SyncSchedulerImpl> weak_ptr_factory_;
diff --git a/chromium/components/cryptauth/sync_scheduler_impl_unittest.cc b/chromium/components/cryptauth/sync_scheduler_impl_unittest.cc
index 77b037c5da3..cc3ae12cfda 100644
--- a/chromium/components/cryptauth/sync_scheduler_impl_unittest.cc
+++ b/chromium/components/cryptauth/sync_scheduler_impl_unittest.cc
@@ -40,7 +40,7 @@ bool IsTimeDeltaWithinJitter(const base::TimeDelta& base_time_delta,
return percentage_of_base < max_jitter_ratio;
}
-// Test harness for the SyncSchedulerImpl to create MockTimers.
+// Test harness for the SyncSchedulerImpl to create MockOneShotTimers.
class TestSyncSchedulerImpl : public SyncSchedulerImpl {
public:
TestSyncSchedulerImpl(Delegate* delegate,
@@ -55,18 +55,16 @@ class TestSyncSchedulerImpl : public SyncSchedulerImpl {
~TestSyncSchedulerImpl() override {}
- base::MockTimer* timer() { return mock_timer_; }
+ base::MockOneShotTimer* timer() { return mock_timer_; }
private:
- std::unique_ptr<base::Timer> CreateTimer() override {
- bool retain_user_task = false;
- bool is_repeating = false;
- mock_timer_ = new base::MockTimer(retain_user_task, is_repeating);
+ std::unique_ptr<base::OneShotTimer> CreateTimer() override {
+ mock_timer_ = new base::MockOneShotTimer();
return base::WrapUnique(mock_timer_);
}
// A timer instance for testing. Owned by the parent scheduler.
- base::MockTimer* mock_timer_;
+ base::MockOneShotTimer* mock_timer_;
DISALLOW_COPY_AND_ASSIGN(TestSyncSchedulerImpl);
};
@@ -93,7 +91,7 @@ class CryptAuthSyncSchedulerImplTest : public testing::Test,
sync_request_ = std::move(sync_request);
}
- base::MockTimer* timer() { return scheduler_->timer(); }
+ base::MockOneShotTimer* timer() { return scheduler_->timer(); }
// The time deltas used to configure |scheduler_|.
base::TimeDelta refresh_period_;
diff --git a/chromium/components/data_reduction_proxy/DEPS b/chromium/components/data_reduction_proxy/DEPS
index d2649794511..23dc1567769 100644
--- a/chromium/components/data_reduction_proxy/DEPS
+++ b/chromium/components/data_reduction_proxy/DEPS
@@ -7,6 +7,7 @@ include_rules = [
"+crypto",
"+google_apis",
"+net",
+ "+services/network/public/cpp",
# Data Reduction Proxy is a layered component; subdirectories must explicitly
# introduce the ability to use the content layer as appropriate.
diff --git a/chromium/components/data_reduction_proxy/content/browser/BUILD.gn b/chromium/components/data_reduction_proxy/content/browser/BUILD.gn
index e13a6b01828..997a9fda97f 100644
--- a/chromium/components/data_reduction_proxy/content/browser/BUILD.gn
+++ b/chromium/components/data_reduction_proxy/content/browser/BUILD.gn
@@ -25,6 +25,7 @@ static_library("browser") {
"//content/public/browser",
"//content/public/common",
"//net",
+ "//services/network/public/cpp",
]
}
@@ -47,6 +48,8 @@ source_set("unit_tests") {
"//content/public/common",
"//content/test:test_support",
"//net:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//testing/gmock",
"//testing/gtest",
]
diff --git a/chromium/components/data_reduction_proxy/content/browser/DEPS b/chromium/components/data_reduction_proxy/content/browser/DEPS
index 59114cf53be..97be08347d6 100644
--- a/chromium/components/data_reduction_proxy/content/browser/DEPS
+++ b/chromium/components/data_reduction_proxy/content/browser/DEPS
@@ -3,4 +3,6 @@ include_rules = [
"+components/data_use_measurement/core",
"+components/previews/core",
"+net",
-] \ No newline at end of file
+ "+services/network/public/cpp",
+ "+services/network/test",
+]
diff --git a/chromium/components/data_reduction_proxy/content/browser/content_lofi_decider.cc b/chromium/components/data_reduction_proxy/content/browser/content_lofi_decider.cc
index 511767beb9a..8b6cd2c4be8 100644
--- a/chromium/components/data_reduction_proxy/content/browser/content_lofi_decider.cc
+++ b/chromium/components/data_reduction_proxy/content/browser/content_lofi_decider.cc
@@ -193,23 +193,4 @@ bool ContentLoFiDecider::IsClientLoFiAutoReloadRequest(
(request_info->GetPreviewsState() & content::CLIENT_LOFI_AUTO_RELOAD);
}
-void ContentLoFiDecider::MaybeApplyAMPPreview(
- net::URLRequest* request,
- GURL* new_url,
- previews::PreviewsDecider* previews_decider) const {
- const content::ResourceRequestInfo* request_info =
- content::ResourceRequestInfo::ForRequest(request);
- if (!request_info ||
- request_info->GetResourceType() != content::RESOURCE_TYPE_MAIN_FRAME ||
- !previews::params::IsAMPRedirectionPreviewEnabled() ||
- !request->url().has_host() ||
- !previews_decider->ShouldAllowPreview(
- *request, previews::PreviewsType::AMP_REDIRECTION)) {
- return;
- }
-
- // TODO(rajendrant): Apply the matching logic for |request| and update
- // |new_url| to its AMP version.
-}
-
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/content/browser/content_lofi_decider.h b/chromium/components/data_reduction_proxy/content/browser/content_lofi_decider.h
index db0d3a2e29b..7ae587952df 100644
--- a/chromium/components/data_reduction_proxy/content/browser/content_lofi_decider.h
+++ b/chromium/components/data_reduction_proxy/content/browser/content_lofi_decider.h
@@ -11,17 +11,11 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
#include "content/public/common/previews_state.h"
-class GURL;
-
namespace net {
class HttpRequestHeaders;
class URLRequest;
}
-namespace previews {
-class PreviewsDecider;
-}
-
namespace data_reduction_proxy {
// Class responsible for deciding whether a request should be requested with low
@@ -58,11 +52,6 @@ class ContentLoFiDecider : public LoFiDecider {
bool IsClientLoFiAutoReloadRequest(
const net::URLRequest& request) const override;
- void MaybeApplyAMPPreview(
- net::URLRequest* request,
- GURL* new_url,
- previews::PreviewsDecider* previews_decider) const override;
-
private:
DISALLOW_COPY_AND_ASSIGN(ContentLoFiDecider);
};
diff --git a/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.cc b/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.cc
index 17184edb683..94b24feefa9 100644
--- a/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.cc
+++ b/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.cc
@@ -68,4 +68,13 @@ void ContentResourceTypeProvider::SetContentType(
resource_content_types_.Put(request.url().spec(), content_type);
}
+bool ContentResourceTypeProvider::IsNonContentInitiatedRequest(
+ const net::URLRequest& request) const {
+ const auto* resource_request_info =
+ content::ResourceRequestInfo::ForRequest(&request);
+ return !resource_request_info ||
+ (resource_request_info->GetGlobalRequestID() ==
+ content::GlobalRequestID());
+}
+
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.h b/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.h
index d4ed06ef208..07f1c0baac0 100644
--- a/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.h
+++ b/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider.h
@@ -30,6 +30,8 @@ class ContentResourceTypeProvider : public ResourceTypeProvider {
void SetContentType(const net::URLRequest& request) override;
ResourceTypeProvider::ContentType GetContentType(
const GURL& url) const override;
+ bool IsNonContentInitiatedRequest(
+ const net::URLRequest& request) const override;
// Map that evicts entries lazily based on the recency of being added.
base::MRUCache<std::string, ResourceTypeProvider::ContentType>
diff --git a/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider_unittest.cc b/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider_unittest.cc
index 36ab5b0c750..d2cc859714a 100644
--- a/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider_unittest.cc
+++ b/chromium/components/data_reduction_proxy/content/browser/content_resource_type_provider_unittest.cc
@@ -9,7 +9,7 @@
#include "base/message_loop/message_loop.h"
#include "base/metrics/field_trial.h"
#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
diff --git a/chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.cc b/chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.cc
index 025379c983d..07c102ab58d 100644
--- a/chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.cc
+++ b/chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.cc
@@ -26,9 +26,9 @@
#include "net/base/load_flags.h"
#include "net/nqe/effective_connection_type.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_status.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"
namespace data_reduction_proxy {
@@ -149,10 +149,32 @@ void AddDataToPageloadMetrics(const DataReductionProxyData& request_data,
} else if (request_data.lite_page_received()) {
request->set_previews_type(PageloadMetrics_PreviewsType_LITE_PAGE);
was_preview_shown = true;
+ } else if (request_data.black_listed()) {
+ request->set_previews_type(
+ PageloadMetrics_PreviewsType_CLIENT_BLACKLIST_PREVENTED_PREVIEW);
} else {
request->set_previews_type(PageloadMetrics_PreviewsType_NONE);
}
+ for (auto request_info_data : request_data.request_info()) {
+ RequestInfo* request_info = request->add_main_frame_network_request();
+ request_info->set_protocol(
+ protobuf_parser::ProtoRequestInfoProtocolFromRequestInfoProtocol(
+ request_info_data.protocol));
+ request_info->set_proxy_bypass(request_info_data.proxy_bypass);
+ request_info->set_allocated_dns_time(
+ protobuf_parser::CreateDurationFromTimeDelta(request_info_data.dns_time)
+ .release());
+ request_info->set_allocated_connect_time(
+ protobuf_parser::CreateDurationFromTimeDelta(
+ request_info_data.connect_time)
+ .release());
+ request_info->set_allocated_http_time(
+ protobuf_parser::CreateDurationFromTimeDelta(
+ request_info_data.http_time)
+ .release());
+ }
+
// Only report opt out information if a server preview was shown (otherwise,
// report opt out unknown). Similarly, if app background (Android) caused this
// report to be sent before the page load is terminated, do not report opt out
@@ -189,18 +211,18 @@ std::string AddBatchInfoAndSerializeRequest(
} // namespace
DataReductionProxyPingbackClientImpl::DataReductionProxyPingbackClientImpl(
- net::URLRequestContextGetter* url_request_context,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
- : url_request_context_(url_request_context),
+ : url_loader_factory_(std::move(url_loader_factory)),
pingback_url_(util::AddApiKeyToUrl(params::GetPingbackURL())),
pingback_reporting_fraction_(0.0),
- current_fetcher_message_count_(0u),
- current_fetcher_crash_count_(0u),
+ current_loader_message_count_(0u),
+ current_loader_crash_count_(0u),
ui_task_runner_(std::move(ui_task_runner)),
#if defined(OS_ANDROID)
scoped_observer_(this),
weak_factory_(this) {
- auto* crash_manager = breakpad::CrashDumpManager::GetInstance();
+ auto* crash_manager = crash_reporter::CrashMetricsReporter::GetInstance();
DCHECK(crash_manager);
scoped_observer_.Add(crash_manager);
#else
@@ -212,31 +234,28 @@ DataReductionProxyPingbackClientImpl::~DataReductionProxyPingbackClientImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
-void DataReductionProxyPingbackClientImpl::OnURLFetchComplete(
- const net::URLFetcher* source) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(source == current_fetcher_.get());
+void DataReductionProxyPingbackClientImpl::OnSimpleLoaderComplete(
+ std::unique_ptr<std::string> response_body) {
+ bool is_success = !!response_body;
// For each message in the batched message, we should report UMA.
// Historically, batched requests are not common, so this loop usually only
// has 1 iteration.
- for (size_t message = 0u; message < current_fetcher_message_count_;
+ for (size_t message = 0u; message < current_loader_message_count_;
++message) {
- UMA_HISTOGRAM_BOOLEAN(kHistogramSucceeded,
- source->GetStatus().is_success());
+ UMA_HISTOGRAM_BOOLEAN(kHistogramSucceeded, is_success);
}
// For each crash we should report UMA.
- for (size_t crash = 0u; crash < current_fetcher_crash_count_; ++crash) {
- UMA_HISTOGRAM_ENUMERATION(
- kHistogramCrash,
- (source->GetStatus().is_success() ? CrashAction::kSentSuccessfully
+ for (size_t crash = 0u; crash < current_loader_crash_count_; ++crash) {
+ UMA_HISTOGRAM_ENUMERATION(kHistogramCrash,
+ (is_success ? CrashAction::kSentSuccessfully
: CrashAction::kSendUnuccessful),
- CrashAction::kLast);
+ CrashAction::kLast);
}
- current_fetcher_.reset();
+ current_loader_.reset();
if (metrics_request_.pageloads_size() > 0) {
- CreateFetcherForDataAndStart();
+ CreateLoaderForDataAndStart();
}
}
@@ -270,9 +289,11 @@ void DataReductionProxyPingbackClientImpl::SendPingback(
#if defined(OS_ANDROID)
void DataReductionProxyPingbackClientImpl::OnCrashDumpProcessed(
- const breakpad::CrashDumpManager::CrashDumpDetails& details) {
+ int rph_id,
+ const crash_reporter::CrashMetricsReporter::ReportedCrashTypeSet&
+ reported_counts) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- auto iter = crash_map_.find(details.process_host_id);
+ auto iter = crash_map_.find(rph_id);
if (iter == crash_map_.end())
return;
const CrashPageLoadInformation& crash_page_load_information = iter->second;
@@ -280,8 +301,10 @@ void DataReductionProxyPingbackClientImpl::OnCrashDumpProcessed(
UMA_HISTOGRAM_ENUMERATION(kHistogramCrash, CrashAction::kAnalsisSucceeded,
CrashAction::kLast);
- bool renderer_foreground_oom =
- breakpad::CrashDumpManager::IsForegroundOom(details);
+ // Record only main frame OOMs.
+ bool renderer_foreground_oom = reported_counts.count(
+ crash_reporter::CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundVisibleOom);
CreateReport(std::get<0>(crash_page_load_information),
std::get<1>(crash_page_load_information),
renderer_foreground_oom
@@ -295,9 +318,9 @@ void DataReductionProxyPingbackClientImpl::AddRequestToCrashMap(
const DataReductionProxyPageLoadTiming& timing) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// It is guaranteed that |AddRequestToCrashMap| is called before
- // |OnCrashDumpProcessed| due to the nature of both events being triggered
- // from the channel closing, and SendPingback being called on the same stack,
- // while OnCrashDumpProcessed is called from a PostTask.
+ // |OnCrashDumpProcessed| due to the nature of both events being
+ // triggered from the channel closing, and SendPingback being called on the
+ // same stack, while OnCrashDumpProcessed is called from a PostTask.
crash_map_.insert(
std::make_pair(timing.host_id, std::make_tuple(request_data, timing)));
// If the crash hasn't been processed in 5 seconds, send the report without it
@@ -337,24 +360,24 @@ void DataReductionProxyPingbackClientImpl::CreateReport(
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
PageloadMetrics* pageload_metrics = metrics_request_.add_pageloads();
AddDataToPageloadMetrics(request_data, timing, crash_type, pageload_metrics);
- if (current_fetcher_)
+ if (current_loader_)
return;
DCHECK_EQ(1, metrics_request_.pageloads_size());
- CreateFetcherForDataAndStart();
+ CreateLoaderForDataAndStart();
}
-void DataReductionProxyPingbackClientImpl::CreateFetcherForDataAndStart() {
- DCHECK(!current_fetcher_);
+void DataReductionProxyPingbackClientImpl::CreateLoaderForDataAndStart() {
+ DCHECK(!current_loader_);
DCHECK_GE(metrics_request_.pageloads_size(), 1);
std::string serialized_request =
AddBatchInfoAndSerializeRequest(&metrics_request_, CurrentTime());
- current_fetcher_message_count_ = metrics_request_.pageloads_size();
- current_fetcher_crash_count_ = 0u;
+ current_loader_message_count_ = metrics_request_.pageloads_size();
+ current_loader_crash_count_ = 0u;
for (const auto& iter : metrics_request_.pageloads()) {
if (iter.renderer_crash_type() !=
PageloadMetrics_RendererCrashType_NO_CRASH) {
- ++current_fetcher_crash_count_;
+ ++current_loader_crash_count_;
}
}
@@ -384,30 +407,33 @@ void DataReductionProxyPingbackClientImpl::CreateFetcherForDataAndStart() {
"enabled, this feature cannot be disabled by settings."
policy_exception_justification: "Not implemented."
})");
- current_fetcher_ = net::URLFetcher::Create(
- pingback_url_, net::URLFetcher::POST, this, traffic_annotation);
- data_use_measurement::DataUseUserData::AttachToFetcher(
- current_fetcher_.get(),
- data_use_measurement::DataUseUserData::DATA_REDUCTION_PROXY);
- current_fetcher_->SetLoadFlags(net::LOAD_BYPASS_PROXY |
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = pingback_url_;
+ resource_request->load_flags = net::LOAD_BYPASS_PROXY |
net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES);
- current_fetcher_->SetUploadData("application/x-protobuf", serialized_request);
- current_fetcher_->SetRequestContext(url_request_context_);
- // |current_fetcher_| should not retry on 5xx errors since the server may
+ net::LOAD_DO_NOT_SAVE_COOKIES;
+ resource_request->method = "POST";
+ // Attach variations headers.
+ variations::AppendVariationHeaders(
+ pingback_url_, variations::InIncognito::kNo, variations::SignedIn::kNo,
+ &resource_request->headers);
+ // TODO(https://crbug.com/808498): Re-add data use measurement once
+ // SimpleURLLoader supports it.
+ // ID=data_use_measurement::DataUseUserData::DATA_REDUCTION_PROXY
+ current_loader_ = network::SimpleURLLoader::Create(
+ std::move(resource_request), traffic_annotation);
+ current_loader_->AttachStringForUpload(serialized_request,
+ "application/x-protobuf");
+ // |current_loader_| should not retry on 5xx errors since the server may
// already be overloaded.
static const int kMaxRetries = 5;
- current_fetcher_->SetAutomaticallyRetryOnNetworkChanges(kMaxRetries);
-
- // Attach variations headers.
- net::HttpRequestHeaders headers;
- variations::AppendVariationHeaders(pingback_url_,
- variations::InIncognito::kNo,
- variations::SignedIn::kNo, &headers);
- if (!headers.IsEmpty())
- current_fetcher_->SetExtraRequestHeaders(headers.ToString());
-
- current_fetcher_->Start();
+ current_loader_->SetRetryOptions(
+ kMaxRetries, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
+ current_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(
+ &DataReductionProxyPingbackClientImpl::OnSimpleLoaderComplete,
+ base::Unretained(this)));
}
bool DataReductionProxyPingbackClientImpl::ShouldSendPingback() const {
diff --git a/chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.h b/chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.h
index 259cf68cbf9..524c2b5a58a 100644
--- a/chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.h
+++ b/chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.h
@@ -17,21 +17,20 @@
#include "build/build_config.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client.h"
#include "components/data_reduction_proxy/proto/pageload_metrics.pb.h"
-#include "net/url_request/url_fetcher_delegate.h"
#include "url/gurl.h"
#if defined(OS_ANDROID)
-#include "components/crash/content/browser/crash_dump_manager_android.h"
+#include "components/crash/content/browser/crash_metrics_reporter_android.h"
#endif
namespace base {
class Time;
}
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
-} // namespace net
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+} // namespace network
namespace data_reduction_proxy {
class DataReductionProxyData;
@@ -40,18 +39,15 @@ struct DataReductionProxyPageLoadTiming;
// Manages pingbacks about page load timing information to the data saver proxy
// server. This class is not thread safe.
class DataReductionProxyPingbackClientImpl
- : public DataReductionProxyPingbackClient,
- public net::URLFetcherDelegate
+ : public DataReductionProxyPingbackClient
#if defined(OS_ANDROID)
,
- public breakpad::CrashDumpManager::Observer
+ public crash_reporter::CrashMetricsReporter::Observer
#endif
{
public:
- // The caller must ensure that |url_request_context| remains alive for the
- // lifetime of the |DataReductionProxyPingbackClientImpl| instance.
DataReductionProxyPingbackClientImpl(
- net::URLRequestContextGetter* url_request_context,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
~DataReductionProxyPingbackClientImpl() override;
@@ -68,8 +64,8 @@ class DataReductionProxyPingbackClientImpl
const DataReductionProxyPageLoadTiming& timing) override;
void SetPingbackReportingFraction(float pingback_reporting_fraction) override;
- // URLFetcherDelegate implmentation:
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ // Called when |current_loader_| completes its network request.
+ void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
// Whether a pingback should be sent.
bool ShouldSendPingback() const;
@@ -77,7 +73,9 @@ class DataReductionProxyPingbackClientImpl
#if defined(OS_ANDROID)
// CrashDumpManager::Observer:
void OnCrashDumpProcessed(
- const breakpad::CrashDumpManager::CrashDumpDetails& details) override;
+ int rph_id,
+ const crash_reporter::CrashMetricsReporter::ReportedCrashTypeSet&
+ reported_counts) override;
// Creates a pending pingback report that waits for the crash dump to be
// processed. If the dump is not processed in 5 seconds, the report is sent
@@ -96,19 +94,19 @@ class DataReductionProxyPingbackClientImpl
const DataReductionProxyPageLoadTiming& timing,
PageloadMetrics_RendererCrashType crash_type);
- // Creates an URLFetcher that will POST to |secure_proxy_url_| using
- // |url_request_context_|. The max retries is set to 5.
- // |data_to_send_| will be used to fill the body of the Fetcher, and will be
+ // Creates a SimpleURLLoader that will POST to |secure_proxy_url_| using
+ // |url_loader_factory_|. The max retries is set to 5.
+ // |data_to_send_| will be used to fill the body of the loader, and will be
// reset to an empty RecordPageloadMetricsRequest.
- void CreateFetcherForDataAndStart();
+ void CreateLoaderForDataAndStart();
- net::URLRequestContextGetter* url_request_context_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// The URL for the data saver proxy's ping back service.
const GURL pingback_url_;
- // The currently running fetcher.
- std::unique_ptr<net::URLFetcher> current_fetcher_;
+ // The currently running loader.
+ std::unique_ptr<network::SimpleURLLoader> current_loader_;
// Serialized data to send to the data saver proxy server.
RecordPageloadMetricsRequest metrics_request_;
@@ -116,11 +114,11 @@ class DataReductionProxyPingbackClientImpl
// The probability of sending a pingback to the server.
float pingback_reporting_fraction_;
- // The number of pageload messages in the current fetcher.
- size_t current_fetcher_message_count_;
+ // The number of pageload messages in the current loader.
+ size_t current_loader_message_count_;
- // The number of pageload crash messages in the current fetcher.
- size_t current_fetcher_crash_count_;
+ // The number of pageload crash messages in the current loader.
+ size_t current_loader_crash_count_;
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
@@ -129,13 +127,13 @@ class DataReductionProxyPingbackClientImpl
CrashPageLoadInformation;
// Maps host process ID to information for the pingback. Items are added to
- // the crash map when the renderer process crashes. If OnCrashDumpProcessed is
- // not called within 5 seconds, the report is sent without the cause of the
- // crash.
+ // the crash map when the renderer process crashes. If
+ // OnCrashDumpProcessed is not called within 5 seconds, the report is
+ // sent without the cause of the crash.
std::map<int, CrashPageLoadInformation> crash_map_;
- ScopedObserver<breakpad::CrashDumpManager,
- breakpad::CrashDumpManager::Observer>
+ ScopedObserver<crash_reporter::CrashMetricsReporter,
+ crash_reporter::CrashMetricsReporter::Observer>
scoped_observer_;
#endif
diff --git a/chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl_unittest.cc b/chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl_unittest.cc
index 7761afa8e99..869de5a9348 100644
--- a/chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl_unittest.cc
+++ b/chromium/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl_unittest.cc
@@ -16,13 +16,15 @@
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/sys_info.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_page_load_timing.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
#include "components/data_reduction_proxy/proto/client_config.pb.h"
#include "components/data_reduction_proxy/proto/pageload_metrics.pb.h"
@@ -30,9 +32,10 @@
#include "net/base/net_errors.h"
#include "net/base/network_change_notifier.h"
#include "net/nqe/effective_connection_type.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_test_util.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_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -52,6 +55,19 @@ static const int64_t kTotalPageSizeBytes = 20000;
static const float kCachedFraction = 0.5;
static const int kCrashProcessId = 1;
static const int64_t kRendererMemory = 1024;
+static const int kNumRequestInfo = 2;
+static const DataReductionProxyData::RequestInfo first_request_info(
+ DataReductionProxyData::RequestInfo::Protocol::HTTP,
+ false,
+ base::TimeDelta::FromMilliseconds(1000),
+ base::TimeDelta::FromMilliseconds(1100),
+ base::TimeDelta::FromMilliseconds(1200));
+static const DataReductionProxyData::RequestInfo second_request_info(
+ DataReductionProxyData::RequestInfo::Protocol::HTTPS,
+ true,
+ base::TimeDelta::FromMilliseconds(1300),
+ base::TimeDelta::FromMilliseconds(1400),
+ base::TimeDelta::FromMilliseconds(1500));
} // namespace
@@ -60,9 +76,9 @@ class TestDataReductionProxyPingbackClientImpl
: public DataReductionProxyPingbackClientImpl {
public:
TestDataReductionProxyPingbackClientImpl(
- net::URLRequestContextGetter* url_request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
scoped_refptr<base::SingleThreadTaskRunner> thread_task_runner)
- : DataReductionProxyPingbackClientImpl(url_request_context_getter,
+ : DataReductionProxyPingbackClientImpl(url_loader_factory,
std::move(thread_task_runner)),
should_override_random_(false),
override_value_(0.0f),
@@ -108,12 +124,24 @@ class DataReductionProxyPingbackClientImplTest : public testing::Test {
return pingback_client_.get();
}
+ GURL pingback_url() { return util::AddApiKeyToUrl(params::GetPingbackURL()); }
+
void Init() {
- request_context_getter_ = new net::TestURLRequestContextGetter(
- scoped_task_environment_.GetMainThreadTaskRunner());
+ factory()->AddResponse(pingback_url().spec(), "");
+ num_network_requests_ = 0;
+ factory()->SetInterceptor(base::BindLambdaForTesting(
+ [&](const network::ResourceRequest& request) {
+ intercepted_url_ = request.url;
+ intercepted_headers_ = request.headers;
+ intercepted_body_ = network::GetUploadData(request);
+ ++num_network_requests_;
+ }));
+ test_shared_loader_factory_ =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_);
pingback_client_ =
std::make_unique<TestDataReductionProxyPingbackClientImpl>(
- request_context_getter_.get(),
+ test_shared_loader_factory_,
scoped_task_environment_.GetMainThreadTaskRunner());
page_id_ = 0u;
}
@@ -123,7 +151,8 @@ class DataReductionProxyPingbackClientImplTest : public testing::Test {
bool lite_page_received,
bool app_background_occurred,
bool opt_out_occurred,
- bool crash) {
+ bool crash,
+ bool black_listed) {
timing_ = std::make_unique<DataReductionProxyPageLoadTiming>(
base::Time::FromJsTime(1500) /* navigation_start */,
base::Optional<base::TimeDelta>(
@@ -156,28 +185,38 @@ class DataReductionProxyPingbackClientImplTest : public testing::Test {
request_data.set_connection_type(
net::NetworkChangeNotifier::CONNECTION_UNKNOWN);
request_data.set_lofi_received(lofi_received);
+ request_data.set_black_listed(black_listed);
request_data.set_client_lofi_requested(client_lofi_requested);
request_data.set_lite_page_received(lite_page_received);
request_data.set_page_id(page_id_);
- factory()->set_remove_fetcher_on_delete(true);
+ request_data.add_request_info(first_request_info);
+ request_data.add_request_info(second_request_info);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SendPingback(request_data, *timing_);
page_id_++;
}
- // Send a fake crash report from breakpad.
+ void WaitForPingbackResponse() {
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+
+ // Send a fake crash report from crash_reporter.
void ReportCrash(bool oom) {
#if defined(OS_ANDROID)
- breakpad::CrashDumpManager::CrashDumpDetails details = {
- kCrashProcessId, content::PROCESS_TYPE_RENDERER, oom,
- base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES};
- details.file_size = oom ? 0 : 1;
- static_cast<breakpad::CrashDumpManager::Observer*>(pingback_client_.get())
- ->OnCrashDumpProcessed(details);
+ crash_reporter::ChildExitObserver::TerminationInfo info;
+ crash_reporter::CrashMetricsReporter::ReportedCrashTypeSet types(
+ {oom ? crash_reporter::CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundVisibleOom
+ : crash_reporter::CrashMetricsReporter::ProcessedCrashCounts::
+ kRendererForegroundVisibleCrash});
+ static_cast<crash_reporter::CrashMetricsReporter::Observer*>(
+ pingback_client_.get())
+ ->OnCrashDumpProcessed(kCrashProcessId, types);
#endif
}
- net::TestURLFetcherFactory* factory() { return &factory_; }
+ network::TestURLLoaderFactory* factory() { return &test_url_loader_factory_; }
const DataReductionProxyPageLoadTiming& timing() { return *timing_; }
@@ -185,21 +224,38 @@ class DataReductionProxyPingbackClientImplTest : public testing::Test {
uint64_t page_id() const { return page_id_; }
+ GURL intercepted_url() { return intercepted_url_; }
+
+ std::string upload_content_type() {
+ std::string content_type;
+ intercepted_headers_.GetHeader(net::HttpRequestHeaders::kContentType,
+ &content_type);
+ return content_type;
+ }
+
+ std::string upload_data() { return intercepted_body_; }
+
+ int num_network_requests() { return num_network_requests_; }
+
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
private:
- scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
std::unique_ptr<TestDataReductionProxyPingbackClientImpl> pingback_client_;
- net::TestURLFetcherFactory factory_;
std::unique_ptr<DataReductionProxyPageLoadTiming> timing_;
base::HistogramTester histogram_tester_;
uint64_t page_id_;
+ GURL intercepted_url_;
+ net::HttpRequestHeaders intercepted_headers_;
+ std::string intercepted_body_;
+ int num_network_requests_;
};
TEST_F(DataReductionProxyPingbackClientImplTest, VerifyPingbackContent) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(1.0f);
@@ -209,13 +265,13 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyPingbackContent) {
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, false /* renderer_crash */);
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
+ EXPECT_EQ(num_network_requests(), 1);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 1);
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- EXPECT_EQ(test_fetcher->upload_content_type(), "application/x-protobuf");
+ EXPECT_EQ(upload_content_type(), "application/x-protobuf");
RecordPageloadMetricsRequest batched_request;
- batched_request.ParseFromString(test_fetcher->upload_data());
+ batched_request.ParseFromString(upload_data());
EXPECT_EQ(batched_request.pageloads_size(), 1);
EXPECT_EQ(current_time, protobuf_parser::TimestampToTime(
batched_request.metrics_sent_time()));
@@ -255,6 +311,37 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyPingbackContent) {
EXPECT_EQ(kTotalPageSizeBytes, pageload_metrics.total_page_size_bytes());
EXPECT_EQ(kCachedFraction, pageload_metrics.cached_fraction());
EXPECT_EQ(data_page_id, pageload_metrics.page_id());
+ EXPECT_EQ(kNumRequestInfo,
+ pageload_metrics.main_frame_network_request_size());
+ EXPECT_EQ(protobuf_parser::ProtoRequestInfoProtocolFromRequestInfoProtocol(
+ first_request_info.protocol),
+ pageload_metrics.main_frame_network_request(0).protocol());
+ EXPECT_EQ(first_request_info.proxy_bypass,
+ pageload_metrics.main_frame_network_request(0).proxy_bypass());
+ EXPECT_EQ(first_request_info.dns_time,
+ protobuf_parser::DurationToTimeDelta(
+ pageload_metrics.main_frame_network_request(0).dns_time()));
+ EXPECT_EQ(first_request_info.connect_time,
+ protobuf_parser::DurationToTimeDelta(
+ pageload_metrics.main_frame_network_request(0).connect_time()));
+ EXPECT_EQ(first_request_info.http_time,
+ protobuf_parser::DurationToTimeDelta(
+ pageload_metrics.main_frame_network_request(0).http_time()));
+
+ EXPECT_EQ(protobuf_parser::ProtoRequestInfoProtocolFromRequestInfoProtocol(
+ second_request_info.protocol),
+ pageload_metrics.main_frame_network_request(1).protocol());
+ EXPECT_EQ(second_request_info.proxy_bypass,
+ pageload_metrics.main_frame_network_request(1).proxy_bypass());
+ EXPECT_EQ(second_request_info.dns_time,
+ protobuf_parser::DurationToTimeDelta(
+ pageload_metrics.main_frame_network_request(1).dns_time()));
+ EXPECT_EQ(second_request_info.connect_time,
+ protobuf_parser::DurationToTimeDelta(
+ pageload_metrics.main_frame_network_request(1).connect_time()));
+ EXPECT_EQ(second_request_info.http_time,
+ protobuf_parser::DurationToTimeDelta(
+ pageload_metrics.main_frame_network_request(1).http_time()));
EXPECT_EQ(PageloadMetrics_PreviewsType_NONE,
pageload_metrics.previews_type());
@@ -273,9 +360,8 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyPingbackContent) {
EXPECT_EQ(base::SysInfo::AmountOfPhysicalMemory() / 1024,
batched_request.device_info().total_device_memory_kb());
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
+ WaitForPingbackResponse();
histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 1);
- EXPECT_FALSE(factory()->GetFetcherByID(0));
}
TEST_F(DataReductionProxyPingbackClientImplTest, VerifyHoldback) {
@@ -283,32 +369,31 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyHoldback) {
ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
"DataCompressionProxyHoldback", "Enabled"));
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(1.0f);
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, false /* renderer_crash */);
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
+ EXPECT_EQ(num_network_requests(), 1);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 1);
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- EXPECT_EQ(test_fetcher->upload_content_type(), "application/x-protobuf");
+ EXPECT_EQ(upload_content_type(), "application/x-protobuf");
RecordPageloadMetricsRequest batched_request;
- batched_request.ParseFromString(test_fetcher->upload_data());
+ batched_request.ParseFromString(upload_data());
EXPECT_EQ(batched_request.pageloads_size(), 1);
PageloadMetrics pageload_metrics = batched_request.pageloads(0);
EXPECT_EQ("Enabled", pageload_metrics.holdback_group());
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
+ WaitForPingbackResponse();
histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 1);
- EXPECT_FALSE(factory()->GetFetcherByID(0));
}
TEST_F(DataReductionProxyPingbackClientImplTest,
VerifyTwoPingbacksBatchedContent) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(1.0f);
@@ -318,7 +403,8 @@ TEST_F(DataReductionProxyPingbackClientImplTest,
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, false /* renderer_crash */);
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 1);
// Two more pingbacks batched together.
@@ -327,26 +413,24 @@ TEST_F(DataReductionProxyPingbackClientImplTest,
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, false /* renderer_crash */);
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 2);
page_ids.push_back(page_id());
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, false /* renderer_crash */);
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 3);
+ EXPECT_EQ(num_network_requests(), 1);
- // Ignore the first pingback.
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
- histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 1);
+ WaitForPingbackResponse();
// Check the state of the second pingback.
- test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- EXPECT_EQ(test_fetcher->upload_content_type(), "application/x-protobuf");
+ EXPECT_EQ(upload_content_type(), "application/x-protobuf");
RecordPageloadMetricsRequest batched_request;
- batched_request.ParseFromString(test_fetcher->upload_data());
+ batched_request.ParseFromString(upload_data());
EXPECT_EQ(batched_request.pageloads_size(), 2);
EXPECT_EQ(current_time, protobuf_parser::TimestampToTime(
batched_request.metrics_sent_time()));
@@ -390,6 +474,40 @@ TEST_F(DataReductionProxyPingbackClientImplTest,
EXPECT_EQ(kBytesOriginal, pageload_metrics.original_page_size_bytes());
EXPECT_EQ(kTotalPageSizeBytes, pageload_metrics.total_page_size_bytes());
EXPECT_EQ(kCachedFraction, pageload_metrics.cached_fraction());
+ EXPECT_EQ(kNumRequestInfo,
+ pageload_metrics.main_frame_network_request_size());
+ EXPECT_EQ(protobuf_parser::ProtoRequestInfoProtocolFromRequestInfoProtocol(
+ first_request_info.protocol),
+ pageload_metrics.main_frame_network_request(0).protocol());
+ EXPECT_EQ(first_request_info.proxy_bypass,
+ pageload_metrics.main_frame_network_request(0).proxy_bypass());
+ EXPECT_EQ(first_request_info.dns_time,
+ protobuf_parser::DurationToTimeDelta(
+ pageload_metrics.main_frame_network_request(0).dns_time()));
+ EXPECT_EQ(
+ first_request_info.connect_time,
+ protobuf_parser::DurationToTimeDelta(
+ pageload_metrics.main_frame_network_request(0).connect_time()));
+ EXPECT_EQ(first_request_info.http_time,
+ protobuf_parser::DurationToTimeDelta(
+ pageload_metrics.main_frame_network_request(0).http_time()));
+
+ EXPECT_EQ(protobuf_parser::ProtoRequestInfoProtocolFromRequestInfoProtocol(
+ second_request_info.protocol),
+ pageload_metrics.main_frame_network_request(1).protocol());
+ EXPECT_EQ(second_request_info.proxy_bypass,
+ pageload_metrics.main_frame_network_request(1).proxy_bypass());
+ EXPECT_EQ(second_request_info.dns_time,
+ protobuf_parser::DurationToTimeDelta(
+ pageload_metrics.main_frame_network_request(1).dns_time()));
+ EXPECT_EQ(
+ second_request_info.connect_time,
+ protobuf_parser::DurationToTimeDelta(
+ pageload_metrics.main_frame_network_request(1).connect_time()));
+ EXPECT_EQ(second_request_info.http_time,
+ protobuf_parser::DurationToTimeDelta(
+ pageload_metrics.main_frame_network_request(1).http_time()));
+
EXPECT_EQ(page_ids.front(), pageload_metrics.page_id());
page_ids.pop_front();
EXPECT_EQ(
@@ -400,57 +518,53 @@ TEST_F(DataReductionProxyPingbackClientImplTest,
EXPECT_EQ(kRendererMemory, pageload_metrics.renderer_memory_usage_kb());
}
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 3);
- EXPECT_FALSE(factory()->GetFetcherByID(0));
}
TEST_F(DataReductionProxyPingbackClientImplTest, SendTwoPingbacks) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(1.0f);
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, false /* renderer_crash */);
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 1);
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, false /* renderer_crash */);
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 2);
+ EXPECT_EQ(num_network_requests(), 1);
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
- histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 1);
- EXPECT_TRUE(factory()->GetFetcherByID(0));
- test_fetcher = factory()->GetFetcherByID(0);
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
+ WaitForPingbackResponse();
histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 2);
- EXPECT_FALSE(factory()->GetFetcherByID(0));
histogram_tester().ExpectTotalCount(kHistogramAttempted, 2);
}
TEST_F(DataReductionProxyPingbackClientImplTest, NoPingbackSent) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(0.0f);
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, false /* renderer_crash */);
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, false, 1);
histogram_tester().ExpectTotalCount(kHistogramSucceeded, 0);
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
}
TEST_F(DataReductionProxyPingbackClientImplTest, VerifyReportingBehvaior) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
// Verify that if the random number is less than the reporting fraction, the
// pingback is created.
@@ -460,11 +574,11 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyReportingBehvaior) {
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, false /* renderer_crash */);
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 1);
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
+ EXPECT_EQ(num_network_requests(), 1);
+ WaitForPingbackResponse();
histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 1);
// Verify that if the random number is greater than the reporting fraction,
@@ -473,10 +587,10 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyReportingBehvaior) {
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, false /* renderer_crash */);
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
histogram_tester().ExpectBucketCount(kHistogramAttempted, false, 1);
- test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_FALSE(test_fetcher);
+ EXPECT_EQ(num_network_requests(), 1);
// Verify that if the random number is equal to the reporting fraction, the
// pingback is not created. Specifically, if the reporting fraction is zero,
@@ -487,10 +601,10 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyReportingBehvaior) {
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, false /* renderer_crash */);
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
histogram_tester().ExpectBucketCount(kHistogramAttempted, false, 2);
- test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_FALSE(test_fetcher);
+ EXPECT_EQ(num_network_requests(), 1);
// Verify that the command line flag forces a pingback.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
@@ -501,37 +615,37 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyReportingBehvaior) {
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, false /* renderer_crash */);
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
histogram_tester().ExpectBucketCount(kHistogramAttempted, true, 2);
- test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
+ EXPECT_EQ(num_network_requests(), 2);
+ WaitForPingbackResponse();
histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 2);
}
TEST_F(DataReductionProxyPingbackClientImplTest, FailedPingback) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(1.0f);
+ // Simulate a network error.
+ factory()->ClearResponses();
+ factory()->AddResponse(pingback_url().spec(), "", net::HTTP_UNAUTHORIZED);
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, false /* renderer_crash */);
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 1);
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- // Simulate a network error.
- test_fetcher->set_status(net::URLRequestStatus(
- net::URLRequestStatus::FAILED, net::ERR_INVALID_AUTH_CREDENTIALS));
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
+ EXPECT_EQ(num_network_requests(), 1);
+ WaitForPingbackResponse();
histogram_tester().ExpectUniqueSample(kHistogramSucceeded, false, 1);
}
TEST_F(DataReductionProxyPingbackClientImplTest, VerifyLoFiContentNoOptOut) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(1.0f);
@@ -540,27 +654,24 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyLoFiContentNoOptOut) {
CreateAndSendPingback(
true /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, false /* renderer_crash */);
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
+ EXPECT_EQ(num_network_requests(), 1);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 1);
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- EXPECT_EQ(test_fetcher->upload_content_type(), "application/x-protobuf");
+ EXPECT_EQ(upload_content_type(), "application/x-protobuf");
RecordPageloadMetricsRequest batched_request;
- batched_request.ParseFromString(test_fetcher->upload_data());
+ batched_request.ParseFromString(upload_data());
EXPECT_EQ(batched_request.pageloads_size(), 1);
PageloadMetrics pageload_metrics = batched_request.pageloads(0);
EXPECT_EQ(PageloadMetrics_PreviewsType_LOFI,
pageload_metrics.previews_type());
EXPECT_EQ(PageloadMetrics_PreviewsOptOut_NON_OPT_OUT,
pageload_metrics.previews_opt_out());
-
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
- EXPECT_FALSE(factory()->GetFetcherByID(0));
}
TEST_F(DataReductionProxyPingbackClientImplTest, VerifyLoFiContentOptOut) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(1.0f);
@@ -569,28 +680,25 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyLoFiContentOptOut) {
CreateAndSendPingback(
true /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- true /* opt_out_occurred */, false /* renderer_crash */);
+ true /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
+ EXPECT_EQ(num_network_requests(), 1);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 1);
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- EXPECT_EQ(test_fetcher->upload_content_type(), "application/x-protobuf");
+ EXPECT_EQ(upload_content_type(), "application/x-protobuf");
RecordPageloadMetricsRequest batched_request;
- batched_request.ParseFromString(test_fetcher->upload_data());
+ batched_request.ParseFromString(upload_data());
EXPECT_EQ(batched_request.pageloads_size(), 1);
PageloadMetrics pageload_metrics = batched_request.pageloads(0);
EXPECT_EQ(PageloadMetrics_PreviewsType_LOFI,
pageload_metrics.previews_type());
EXPECT_EQ(PageloadMetrics_PreviewsOptOut_OPT_OUT,
pageload_metrics.previews_opt_out());
-
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
- EXPECT_FALSE(factory()->GetFetcherByID(0));
}
TEST_F(DataReductionProxyPingbackClientImplTest,
VerifyClientLoFiContentOptOut) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(1.0f);
@@ -599,27 +707,24 @@ TEST_F(DataReductionProxyPingbackClientImplTest,
CreateAndSendPingback(
false /* lofi_received */, true /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- true /* opt_out_occurred */, false /* renderer_crash */);
+ true /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
+ EXPECT_EQ(num_network_requests(), 1);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 1);
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- EXPECT_EQ(test_fetcher->upload_content_type(), "application/x-protobuf");
+ EXPECT_EQ(upload_content_type(), "application/x-protobuf");
RecordPageloadMetricsRequest batched_request;
- batched_request.ParseFromString(test_fetcher->upload_data());
+ batched_request.ParseFromString(upload_data());
EXPECT_EQ(batched_request.pageloads_size(), 1);
PageloadMetrics pageload_metrics = batched_request.pageloads(0);
EXPECT_EQ(PageloadMetrics_PreviewsType_LOFI,
pageload_metrics.previews_type());
EXPECT_EQ(PageloadMetrics_PreviewsOptOut_OPT_OUT,
pageload_metrics.previews_opt_out());
-
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
- EXPECT_FALSE(factory()->GetFetcherByID(0));
}
TEST_F(DataReductionProxyPingbackClientImplTest, VerifyLoFiContentBackground) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(1.0f);
@@ -628,27 +733,50 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyLoFiContentBackground) {
CreateAndSendPingback(
true /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, true /* app_background_occurred */,
- true /* opt_out_occurred */, false /* renderer_crash */);
+ true /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
+ EXPECT_EQ(num_network_requests(), 1);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 1);
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- EXPECT_EQ(test_fetcher->upload_content_type(), "application/x-protobuf");
+ EXPECT_EQ(upload_content_type(), "application/x-protobuf");
RecordPageloadMetricsRequest batched_request;
- batched_request.ParseFromString(test_fetcher->upload_data());
+ batched_request.ParseFromString(upload_data());
EXPECT_EQ(batched_request.pageloads_size(), 1);
PageloadMetrics pageload_metrics = batched_request.pageloads(0);
EXPECT_EQ(PageloadMetrics_PreviewsType_LOFI,
pageload_metrics.previews_type());
EXPECT_EQ(PageloadMetrics_PreviewsOptOut_UNKNOWN,
pageload_metrics.previews_opt_out());
+}
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+TEST_F(DataReductionProxyPingbackClientImplTest, VerifyBlackListContent) {
+ Init();
+ EXPECT_EQ(num_network_requests(), 0);
+ pingback_client()->OverrideRandom(true, 0.5f);
+ static_cast<DataReductionProxyPingbackClient*>(pingback_client())
+ ->SetPingbackReportingFraction(1.0f);
+ base::Time current_time = base::Time::UnixEpoch();
+ pingback_client()->set_current_time(current_time);
+ CreateAndSendPingback(
+ false /* lofi_received */, false /* client_lofi_requested */,
+ false /* lite_page_received */, false /* app_background_occurred */,
+ false /* opt_out_occurred */, false /* renderer_crash */,
+ true /* black_listed */);
+ EXPECT_EQ(num_network_requests(), 1);
+ histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 1);
+ EXPECT_EQ(upload_content_type(), "application/x-protobuf");
+ RecordPageloadMetricsRequest batched_request;
+ batched_request.ParseFromString(upload_data());
+ EXPECT_EQ(batched_request.pageloads_size(), 1);
+ PageloadMetrics pageload_metrics = batched_request.pageloads(0);
+ EXPECT_EQ(PageloadMetrics_PreviewsType_CLIENT_BLACKLIST_PREVENTED_PREVIEW,
+ pageload_metrics.previews_type());
+ EXPECT_EQ(PageloadMetrics_PreviewsOptOut_UNKNOWN,
+ pageload_metrics.previews_opt_out());
}
TEST_F(DataReductionProxyPingbackClientImplTest, VerifyLitePageContent) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(1.0f);
@@ -657,27 +785,24 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyLitePageContent) {
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
true /* lite_page_received */, false /* app_background_occurred */,
- true /* opt_out_occurred */, false /* renderer_crash */);
+ true /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
+ EXPECT_EQ(num_network_requests(), 1);
histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 1);
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- EXPECT_EQ(test_fetcher->upload_content_type(), "application/x-protobuf");
+ EXPECT_EQ(upload_content_type(), "application/x-protobuf");
RecordPageloadMetricsRequest batched_request;
- batched_request.ParseFromString(test_fetcher->upload_data());
+ batched_request.ParseFromString(upload_data());
EXPECT_EQ(batched_request.pageloads_size(), 1);
PageloadMetrics pageload_metrics = batched_request.pageloads(0);
EXPECT_EQ(PageloadMetrics_PreviewsType_LITE_PAGE,
pageload_metrics.previews_type());
EXPECT_EQ(PageloadMetrics_PreviewsOptOut_OPT_OUT,
pageload_metrics.previews_opt_out());
-
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
- EXPECT_FALSE(factory()->GetFetcherByID(0));
}
TEST_F(DataReductionProxyPingbackClientImplTest, VerifyTwoLitePagePingbacks) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(1.0f);
@@ -686,41 +811,37 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyTwoLitePagePingbacks) {
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
true /* lite_page_received */, false /* app_background_occurred */,
- true /* opt_out_occurred */, false /* renderer_crash */);
- CreateAndSendPingback(
- false /* lofi_received */, false /* client_lofi_requested */,
- true /* lite_page_received */, false /* app_background_occurred */,
- true /* opt_out_occurred */, false /* renderer_crash */);
- histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 2);
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- EXPECT_EQ(test_fetcher->upload_content_type(), "application/x-protobuf");
+ true /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
+ EXPECT_EQ(num_network_requests(), 1);
+ EXPECT_EQ(upload_content_type(), "application/x-protobuf");
RecordPageloadMetricsRequest batched_request;
- batched_request.ParseFromString(test_fetcher->upload_data());
+ batched_request.ParseFromString(upload_data());
EXPECT_EQ(batched_request.pageloads_size(), 1);
PageloadMetrics pageload_metrics = batched_request.pageloads(0);
EXPECT_EQ(PageloadMetrics_PreviewsType_LITE_PAGE,
pageload_metrics.previews_type());
EXPECT_EQ(PageloadMetrics_PreviewsOptOut_OPT_OUT,
pageload_metrics.previews_opt_out());
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
- test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- EXPECT_EQ(test_fetcher->upload_content_type(), "application/x-protobuf");
- batched_request.ParseFromString(test_fetcher->upload_data());
+ CreateAndSendPingback(
+ false /* lofi_received */, false /* client_lofi_requested */,
+ true /* lite_page_received */, false /* app_background_occurred */,
+ true /* opt_out_occurred */, false /* renderer_crash */,
+ false /* black_listed */);
+ histogram_tester().ExpectUniqueSample(kHistogramAttempted, true, 2);
+ EXPECT_EQ(upload_content_type(), "application/x-protobuf");
+ batched_request.ParseFromString(upload_data());
EXPECT_EQ(batched_request.pageloads_size(), 1);
pageload_metrics = batched_request.pageloads(0);
EXPECT_EQ(PageloadMetrics_PreviewsType_LITE_PAGE,
pageload_metrics.previews_type());
EXPECT_EQ(PageloadMetrics_PreviewsOptOut_OPT_OUT,
pageload_metrics.previews_opt_out());
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
- EXPECT_FALSE(factory()->GetFetcherByID(0));
}
TEST_F(DataReductionProxyPingbackClientImplTest, VerifyCrashOomBehavior) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(1.0f);
@@ -728,15 +849,14 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyCrashOomBehavior) {
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, true /* renderer_crash */);
+ false /* opt_out_occurred */, true /* renderer_crash */,
+ false /* black_listed */);
ReportCrash(true /* oom */);
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- EXPECT_EQ(test_fetcher->upload_content_type(), "application/x-protobuf");
+ EXPECT_EQ(upload_content_type(), "application/x-protobuf");
RecordPageloadMetricsRequest batched_request;
- batched_request.ParseFromString(test_fetcher->upload_data());
+ batched_request.ParseFromString(upload_data());
EXPECT_EQ(batched_request.pageloads_size(), 1);
PageloadMetrics pageload_metrics = batched_request.pageloads(0);
#if defined(OS_ANDROID)
@@ -746,14 +866,13 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyCrashOomBehavior) {
EXPECT_EQ(PageloadMetrics_RendererCrashType_NOT_ANALYZED,
pageload_metrics.renderer_crash_type());
#endif
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
+ WaitForPingbackResponse();
histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 1);
- EXPECT_FALSE(factory()->GetFetcherByID(0));
}
TEST_F(DataReductionProxyPingbackClientImplTest, VerifyCrashNotOomBehavior) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(1.0f);
@@ -761,15 +880,14 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyCrashNotOomBehavior) {
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, true /* renderer_crash */);
+ false /* opt_out_occurred */, true /* renderer_crash */,
+ false /* black_listed */);
ReportCrash(false /* oom */);
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- EXPECT_EQ(test_fetcher->upload_content_type(), "application/x-protobuf");
+ EXPECT_EQ(upload_content_type(), "application/x-protobuf");
RecordPageloadMetricsRequest batched_request;
- batched_request.ParseFromString(test_fetcher->upload_data());
+ batched_request.ParseFromString(upload_data());
EXPECT_EQ(batched_request.pageloads_size(), 1);
PageloadMetrics pageload_metrics = batched_request.pageloads(0);
#if defined(OS_ANDROID)
@@ -779,15 +897,14 @@ TEST_F(DataReductionProxyPingbackClientImplTest, VerifyCrashNotOomBehavior) {
EXPECT_EQ(PageloadMetrics_RendererCrashType_NOT_ANALYZED,
pageload_metrics.renderer_crash_type());
#endif
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
+ WaitForPingbackResponse();
histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 1);
- EXPECT_FALSE(factory()->GetFetcherByID(0));
}
TEST_F(DataReductionProxyPingbackClientImplTest,
VerifyCrashNotAnalyzedBehavior) {
Init();
- EXPECT_FALSE(factory()->GetFetcherByID(0));
+ EXPECT_EQ(num_network_requests(), 0);
pingback_client()->OverrideRandom(true, 0.5f);
static_cast<DataReductionProxyPingbackClient*>(pingback_client())
->SetPingbackReportingFraction(1.0f);
@@ -795,23 +912,21 @@ TEST_F(DataReductionProxyPingbackClientImplTest,
CreateAndSendPingback(
false /* lofi_received */, false /* client_lofi_requested */,
false /* lite_page_received */, false /* app_background_occurred */,
- false /* opt_out_occurred */, true /* renderer_crash */);
+ false /* opt_out_occurred */, true /* renderer_crash */,
+ false /* black_listed */);
// Don't report the crash dump details.
scoped_task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(5));
- net::TestURLFetcher* test_fetcher = factory()->GetFetcherByID(0);
- EXPECT_TRUE(test_fetcher);
- EXPECT_EQ(test_fetcher->upload_content_type(), "application/x-protobuf");
+ EXPECT_EQ(upload_content_type(), "application/x-protobuf");
RecordPageloadMetricsRequest batched_request;
- batched_request.ParseFromString(test_fetcher->upload_data());
+ batched_request.ParseFromString(upload_data());
EXPECT_EQ(batched_request.pageloads_size(), 1);
PageloadMetrics pageload_metrics = batched_request.pageloads(0);
EXPECT_EQ(PageloadMetrics_RendererCrashType_NOT_ANALYZED,
pageload_metrics.renderer_crash_type());
- test_fetcher->delegate()->OnURLFetchComplete(test_fetcher);
+ WaitForPingbackResponse();
histogram_tester().ExpectUniqueSample(kHistogramSucceeded, true, 1);
- EXPECT_FALSE(factory()->GetFetcherByID(0));
}
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
index 460aceb4fb7..7298f1abd52 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
@@ -8,6 +8,7 @@
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h"
@@ -17,6 +18,7 @@
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
+#include "components/data_reduction_proxy/proto/client_config.pb.h"
#include "net/base/load_flags.h"
#include "net/base/proxy_server.h"
#include "net/http/http_response_headers.h"
@@ -69,6 +71,13 @@ void MarkProxiesAsBad(const net::URLRequest& request,
request.net_log());
}
+void ReportResponseProxyServerStatusHistogram(
+ DataReductionProxyBypassProtocol::ResponseProxyServerStatus status) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "DataReductionProxy.ResponseProxyServerStatus", status,
+ DataReductionProxyBypassProtocol::RESPONSE_PROXY_SERVER_STATUS_MAX);
+}
+
} // namespace
DataReductionProxyBypassProtocol::DataReductionProxyBypassProtocol(
@@ -92,19 +101,56 @@ bool DataReductionProxyBypassProtocol::MaybeBypassProxyAndPrepareToRetry(
const net::HttpResponseHeaders* response_headers =
request->response_info().headers.get();
bool retry;
+
+ std::vector<DataReductionProxyServer> placeholder_proxy_servers;
+ base::Optional<DataReductionProxyTypeInfo> proxy_type_info =
+ config_->FindConfiguredDataReductionProxy(request->proxy_server());
+
if (!response_headers) {
retry = HandleInvalidResponseHeadersCase(
- *request, data_reduction_proxy_info, &bypass_type);
+ *request, proxy_type_info, data_reduction_proxy_info, &bypass_type);
} else {
+ if (!proxy_type_info) {
+ if (!request->proxy_server().is_valid() ||
+ request->proxy_server().is_direct() ||
+ request->proxy_server().host_port_pair().IsEmpty()) {
+ ReportResponseProxyServerStatusHistogram(
+ RESPONSE_PROXY_SERVER_STATUS_EMPTY);
+ return false;
+ }
+
+ if (!HasDataReductionProxyViaHeader(*response_headers, nullptr)) {
+ ReportResponseProxyServerStatusHistogram(
+ RESPONSE_PROXY_SERVER_STATUS_NON_DRP_NO_VIA);
+ return false;
+ }
+
+ ReportResponseProxyServerStatusHistogram(
+ RESPONSE_PROXY_SERVER_STATUS_NON_DRP_WITH_VIA);
+
+ // If the |proxy_server| doesn't match any of the currently configured
+ // Data Reduction Proxies, but it still has the Data Reduction Proxy via
+ // header, then apply the bypass logic regardless.
+ // TODO(sclittle): Remove this workaround once http://crbug.com/876776 is
+ // fixed.
+ placeholder_proxy_servers.push_back(DataReductionProxyServer(
+ request->proxy_server(), ProxyServer_ProxyType_CORE));
+ proxy_type_info.emplace(DataReductionProxyTypeInfo(
+ placeholder_proxy_servers, 0U /* proxy_index */));
+ } else {
+ ReportResponseProxyServerStatusHistogram(
+ RESPONSE_PROXY_SERVER_STATUS_DRP);
+ }
+
+ DCHECK(proxy_type_info);
retry = HandleValidResponseHeadersCase(
- *request, proxy_bypass_type, data_reduction_proxy_info, &bypass_type);
+ *request, *proxy_type_info, proxy_bypass_type,
+ data_reduction_proxy_info, &bypass_type);
}
if (!retry)
return false;
if (data_reduction_proxy_info->mark_proxies_as_bad) {
- base::Optional<DataReductionProxyTypeInfo> proxy_type_info =
- config_->FindConfiguredDataReductionProxy(request->proxy_server());
DCHECK(proxy_type_info);
MarkProxiesAsBad(*request, *data_reduction_proxy_info, *proxy_type_info);
} else {
@@ -118,6 +164,8 @@ bool DataReductionProxyBypassProtocol::MaybeBypassProxyAndPrepareToRetry(
bool DataReductionProxyBypassProtocol::HandleInvalidResponseHeadersCase(
const net::URLRequest& request,
+ const base::Optional<DataReductionProxyTypeInfo>&
+ data_reduction_proxy_type_info,
DataReductionProxyInfo* data_reduction_proxy_info,
DataReductionProxyBypassType* bypass_type) const {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -130,7 +178,7 @@ bool DataReductionProxyBypassProtocol::HandleInvalidResponseHeadersCase(
return false;
}
- if (!config_->FindConfiguredDataReductionProxy(request.proxy_server()))
+ if (!data_reduction_proxy_type_info)
return false;
DCHECK(request.url().SchemeIs(url::kHttpScheme));
@@ -173,6 +221,7 @@ bool DataReductionProxyBypassProtocol::HandleInvalidResponseHeadersCase(
bool DataReductionProxyBypassProtocol::HandleValidResponseHeadersCase(
const net::URLRequest& request,
+ const DataReductionProxyTypeInfo& data_reduction_proxy_type_info,
DataReductionProxyBypassType* proxy_bypass_type,
DataReductionProxyInfo* data_reduction_proxy_info,
DataReductionProxyBypassType* bypass_type) const {
@@ -185,15 +234,10 @@ bool DataReductionProxyBypassProtocol::HandleValidResponseHeadersCase(
DCHECK(response_headers);
- base::Optional<DataReductionProxyTypeInfo> data_reduction_proxy_type_info =
- config_->FindConfiguredDataReductionProxy(request.proxy_server());
- if (!data_reduction_proxy_type_info)
- return false;
-
// At this point, the response is expected to have the data reduction proxy
// via header, so detect and report cases where the via header is missing.
DataReductionProxyBypassStats::DetectAndRecordMissingViaHeaderResponseCode(
- data_reduction_proxy_type_info->proxy_index == 0U, *response_headers);
+ data_reduction_proxy_type_info.proxy_index == 0U, *response_headers);
// GetDataReductionProxyBypassType will only log a net_log event if a bypass
// command was sent via the data reduction proxy headers.
@@ -207,12 +251,12 @@ bool DataReductionProxyBypassProtocol::HandleValidResponseHeadersCase(
DCHECK(request.context());
DCHECK(request.context()->proxy_resolution_service());
- DCHECK_GT(data_reduction_proxy_type_info->proxy_servers.size(),
- data_reduction_proxy_type_info->proxy_index);
+ DCHECK_GT(data_reduction_proxy_type_info.proxy_servers.size(),
+ data_reduction_proxy_type_info.proxy_index);
const net::ProxyServer& proxy_server =
data_reduction_proxy_type_info
- ->proxy_servers[data_reduction_proxy_type_info->proxy_index]
+ .proxy_servers[data_reduction_proxy_type_info.proxy_index]
.proxy_server();
// Only record UMA if the proxy isn't already on the retry list.
@@ -220,7 +264,7 @@ bool DataReductionProxyBypassProtocol::HandleValidResponseHeadersCase(
request.context()->proxy_resolution_service()->proxy_retry_info(),
proxy_server, nullptr)) {
DataReductionProxyBypassStats::RecordDataReductionProxyBypassInfo(
- data_reduction_proxy_type_info->proxy_index == 0U,
+ data_reduction_proxy_type_info.proxy_index == 0U,
data_reduction_proxy_info->bypass_all, proxy_server, *bypass_type);
}
return true;
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h
index af3bd0b0bff..9491cb8f3cd 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h
@@ -6,6 +6,7 @@
#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_BYPASS_PROTOCOL_H_
#include "base/macros.h"
+#include "base/optional.h"
#include "base/threading/thread_checker.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
@@ -16,12 +17,25 @@ class URLRequest;
namespace data_reduction_proxy {
class DataReductionProxyConfig;
+struct DataReductionProxyTypeInfo;
// Class responsible for determining when a response should or should not cause
// the data reduction proxy to be bypassed, and to what degree. Owned by the
// DataReductionProxyInterceptor.
class DataReductionProxyBypassProtocol {
public:
+ // Enum values that can be reported for the
+ // DataReductionProxy.ResponseProxyServerStatus histogram. These values must
+ // be kept in sync with their counterparts in histograms.xml. Visible here for
+ // testing purposes.
+ enum ResponseProxyServerStatus {
+ RESPONSE_PROXY_SERVER_STATUS_EMPTY = 0,
+ RESPONSE_PROXY_SERVER_STATUS_DRP,
+ RESPONSE_PROXY_SERVER_STATUS_NON_DRP_NO_VIA,
+ RESPONSE_PROXY_SERVER_STATUS_NON_DRP_WITH_VIA,
+ RESPONSE_PROXY_SERVER_STATUS_MAX
+ };
+
// Constructs a DataReductionProxyBypassProtocol object. |config| must be
// non-NULL and outlive |this|.
DataReductionProxyBypassProtocol(DataReductionProxyConfig* config);
@@ -46,6 +60,8 @@ class DataReductionProxyBypassProtocol {
// response headers.
bool HandleInvalidResponseHeadersCase(
const net::URLRequest& request,
+ const base::Optional<DataReductionProxyTypeInfo>&
+ data_reduction_proxy_type_info,
DataReductionProxyInfo* data_reduction_proxy_info,
DataReductionProxyBypassType* bypass_type) const;
@@ -55,6 +71,7 @@ class DataReductionProxyBypassProtocol {
// non-null response headers.
bool HandleValidResponseHeadersCase(
const net::URLRequest& request,
+ const DataReductionProxyTypeInfo& data_reduction_proxy_type_info,
DataReductionProxyBypassType* proxy_bypass_type,
DataReductionProxyInfo* data_reduction_proxy_info,
DataReductionProxyBypassType* bypass_type) const;
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc
index 04b5c042a48..ff476d6c371 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc
@@ -9,6 +9,7 @@
#include <memory>
#include <utility>
+#include "base/containers/span.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
@@ -17,7 +18,7 @@
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_entropy_provider.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
@@ -1092,4 +1093,177 @@ TEST_F(DataReductionProxyProtocolTest,
TestBadProxies(0, -1, "", "");
}
+class DataReductionProxyBypassProtocolEndToEndTest : public testing::Test {
+ public:
+ DataReductionProxyBypassProtocolEndToEndTest() {}
+
+ void ResetDependencies() {
+ drp_test_context_.reset();
+ mock_socket_factory_.reset();
+ storage_.reset();
+
+ context_.reset(new net::TestURLRequestContext(true));
+ storage_.reset(new net::URLRequestContextStorage(context_.get()));
+ mock_socket_factory_.reset(new net::MockClientSocketFactory());
+ context_->set_client_socket_factory(mock_socket_factory_.get());
+ drp_test_context_ =
+ DataReductionProxyTestContext::Builder()
+ .WithMockClientSocketFactory(mock_socket_factory_.get())
+ .WithURLRequestContext(context_.get())
+ .Build();
+ }
+
+ void AttachToContextAndInit() {
+ drp_test_context_->AttachToURLRequestContext(storage_.get());
+ context_->set_proxy_delegate(
+ drp_test_context_->io_data()->proxy_delegate());
+ context_->Init();
+ }
+
+ net::TestURLRequestContext* context() { return context_.get(); }
+ net::URLRequestContextStorage* storage() { return storage_.get(); }
+ net::MockClientSocketFactory* mock_socket_factory() {
+ return mock_socket_factory_.get();
+ }
+ DataReductionProxyTestContext* drp_test_context() {
+ return drp_test_context_.get();
+ }
+
+ private:
+ base::MessageLoopForIO loop_;
+ std::unique_ptr<net::TestURLRequestContext> context_;
+ std::unique_ptr<net::URLRequestContextStorage> storage_;
+ std::unique_ptr<net::MockClientSocketFactory> mock_socket_factory_;
+ std::unique_ptr<DataReductionProxyTestContext> drp_test_context_;
+
+ DISALLOW_COPY_AND_ASSIGN(DataReductionProxyBypassProtocolEndToEndTest);
+};
+
+TEST_F(DataReductionProxyBypassProtocolEndToEndTest,
+ BypassLogicAlwaysAppliesWhenViaHeaderPresent) {
+ const struct {
+ const char* first_response;
+ bool expected_retry;
+ bool expected_bad_proxy;
+ DataReductionProxyBypassType expected_bypass_type;
+ } test_cases[] = {
+ {"HTTP/1.1 200 OK\r\n"
+ "Server: proxy\r\n"
+ "Chrome-Proxy: block=0\r\n"
+ "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
+ true, true, BYPASS_EVENT_TYPE_MEDIUM},
+ {"HTTP/1.1 200 OK\r\n"
+ "Server: proxy\r\n"
+ "Chrome-Proxy: bypass=0\r\n"
+ "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
+ true, true, BYPASS_EVENT_TYPE_MEDIUM},
+ {"HTTP/1.1 502 Bad Gateway\r\n"
+ "Server: proxy\r\n"
+ "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
+ true, true, BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY},
+ {"HTTP/1.1 200 OK\r\n"
+ "Server: proxy\r\n"
+ "Chrome-Proxy: block=0\r\n\r\n",
+ false, false, BYPASS_EVENT_TYPE_MAX},
+ {"HTTP/1.1 502 Bad Gateway\r\n"
+ "Server: proxy\r\n\r\n",
+ false, false, BYPASS_EVENT_TYPE_MAX},
+ };
+
+ for (const auto& test : test_cases) {
+ const std::string kPrimary = "https://unrecognized-drp.net:443";
+
+ ResetDependencies();
+ storage()->set_proxy_resolution_service(ProxyResolutionService::CreateFixed(
+ kPrimary + ",direct://", TRAFFIC_ANNOTATION_FOR_TESTS));
+ AttachToContextAndInit();
+
+ // The proxy is an HTTPS proxy, so set up the fake SSL socket data.
+ net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+ mock_socket_factory()->AddSSLSocketDataProvider(&ssl_socket);
+
+ MockRead first_reads[] = {MockRead(test.first_response), MockRead(""),
+ MockRead(net::SYNCHRONOUS, net::OK)};
+ net::StaticSocketDataProvider first_socket(first_reads,
+ base::span<MockWrite>());
+ mock_socket_factory()->AddSocketDataProvider(&first_socket);
+
+ MockRead retry_reads[] = {MockRead("HTTP/1.1 200 OK\n\r\n\r"), MockRead(""),
+ MockRead(net::SYNCHRONOUS, net::OK)};
+ net::StaticSocketDataProvider retry_socket(retry_reads,
+ base::span<MockWrite>());
+ if (test.expected_retry)
+ mock_socket_factory()->AddSocketDataProvider(&retry_socket);
+
+ net::TestDelegate delegate;
+ std::unique_ptr<net::URLRequest> url_request(
+ context()->CreateRequest(GURL("http://www.google.com"), net::IDLE,
+ &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
+ url_request->Start();
+ drp_test_context()->RunUntilIdle();
+
+ EXPECT_EQ(test.expected_bypass_type,
+ drp_test_context()->io_data()->bypass_stats()->GetBypassType());
+ // Check the bad proxy list.
+ EXPECT_EQ(test.expected_bad_proxy,
+ base::ContainsKey(
+ context()->proxy_resolution_service()->proxy_retry_info(),
+ kPrimary));
+ }
+}
+
+TEST_F(DataReductionProxyBypassProtocolEndToEndTest,
+ ResponseProxyServerStateHistogram) {
+ const struct {
+ const char* proxy_rules;
+ bool enable_data_reduction_proxy;
+ const char* response_headers;
+ // |RESPONSE_PROXY_SERVER_STATUS_MAX| indicates no expected value.
+ DataReductionProxyBypassProtocol::ResponseProxyServerStatus expected_status;
+ } test_cases[] = {
+ {"direct://", false, "HTTP/1.1 200 OK\r\n\r\n",
+ DataReductionProxyBypassProtocol::RESPONSE_PROXY_SERVER_STATUS_EMPTY},
+ {"direct://", true,
+ "HTTP/1.1 200 OK\r\nVia: 1.1 Chrome-Compression-Proxy\r\n\r\n",
+ DataReductionProxyBypassProtocol::RESPONSE_PROXY_SERVER_STATUS_DRP},
+ {"unrecognized-drp.net", false, "HTTP/1.1 200 OK\r\n\r\n",
+ DataReductionProxyBypassProtocol::
+ RESPONSE_PROXY_SERVER_STATUS_NON_DRP_NO_VIA},
+ {"unrecognized-drp.net", false,
+ "HTTP/1.1 200 OK\r\nVia: 1.1 Chrome-Compression-Proxy\r\n\r\n",
+ DataReductionProxyBypassProtocol::
+ RESPONSE_PROXY_SERVER_STATUS_NON_DRP_WITH_VIA},
+ };
+
+ for (const auto& test : test_cases) {
+ ResetDependencies();
+ storage()->set_proxy_resolution_service(
+ net::ProxyResolutionService::CreateFixed(test.proxy_rules,
+ TRAFFIC_ANNOTATION_FOR_TESTS));
+ AttachToContextAndInit();
+ if (test.enable_data_reduction_proxy) {
+ drp_test_context()->DisableWarmupURLFetch();
+ drp_test_context()->EnableDataReductionProxyWithSecureProxyCheckSuccess();
+ }
+ drp_test_context()->config()->test_params()->UseNonSecureProxiesForHttp();
+
+ MockRead reads[] = {MockRead(test.response_headers), MockRead(""),
+ MockRead(net::SYNCHRONOUS, net::OK)};
+ net::StaticSocketDataProvider socket(reads, base::span<MockWrite>());
+ mock_socket_factory()->AddSocketDataProvider(&socket);
+
+ base::HistogramTester histogram_tester;
+ net::TestDelegate delegate;
+ std::unique_ptr<net::URLRequest> request(
+ context()->CreateRequest(GURL("http://google.com"), net::IDLE,
+ &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
+ request->Start();
+ drp_test_context()->RunUntilIdle();
+
+ histogram_tester.ExpectUniqueSample(
+ "DataReductionProxy.ResponseProxyServerStatus", test.expected_status,
+ 1);
+ }
+}
+
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc
index 338c3a873ba..bec30208e69 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats_unittest.cc
@@ -18,7 +18,7 @@
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
index 9ac471c6fa7..5907b706d09 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
@@ -4,6 +4,7 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h"
+#include <cmath>
#include <utility>
#include <vector>
@@ -13,9 +14,11 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
+#include "base/metrics/sparse_histogram.h"
#include "base/strings/string_number_conversions.h"
#include "base/trace_event/trace_event.h"
#include "base/values.h"
+#include "build/build_config.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
#include "components/data_reduction_proxy/core/browser/data_usage_store.h"
@@ -27,6 +30,10 @@
#include "components/prefs/scoped_user_pref_update.h"
#include "net/base/mime_util.h"
+#if defined(OS_ANDROID)
+#include "base/android/application_status_listener.h"
+#endif
+
namespace data_reduction_proxy {
namespace {
@@ -43,6 +50,9 @@ namespace {
UMA_HISTOGRAM_COUNTS(uma, UNIQUE_VARNAME >> 10); \
}
+const double kSecondsPerWeek =
+ base::Time::kMicrosecondsPerWeek / base::Time::kMicrosecondsPerSecond;
+
// Returns the value at |index| of |list_value| as an int64_t.
int64_t GetInt64PrefValue(const base::ListValue& list_value, size_t index) {
int64_t val = 0;
@@ -209,6 +219,89 @@ void RecordSavingsClearedMetric(DataReductionProxySavingsClearedReason reason) {
DataReductionProxySavingsClearedReason::REASON_COUNT);
}
+// Returns the week number for the current time. The epoch time is treated as
+// week=0.
+int32_t GetCurrentWeekNumber(const base::Time& now) {
+ double now_in_seconds = now.ToDoubleT();
+ return now_in_seconds / kSecondsPerWeek;
+}
+
+// Adds |value| to the item at |key| in the preference dictionary found at
+// |pref|. If |key| is not found it will be inserted.
+void AddToDictionaryPref(PrefService* pref_service,
+ const std::string& pref,
+ int key,
+ int value) {
+ DictionaryPrefUpdate pref_update(pref_service, pref);
+ base::DictionaryValue* pref_dict = pref_update.Get();
+ const std::string key_str = base::IntToString(key);
+ base::Value* dict_value = pref_dict->FindKey(key_str);
+ if (dict_value)
+ value += dict_value->GetInt();
+ pref_dict->SetKey(key_str, base::Value(value));
+}
+
+// Moves the dictionary stored in preference |pref_src| to |pref_dst|, and
+// clears the preference |pref_src|.
+void MoveAndClearDictionaryPrefs(PrefService* pref_service,
+ const std::string& pref_dst,
+ const std::string& pref_src) {
+ DictionaryPrefUpdate pref_update_dst(pref_service, pref_dst);
+ DictionaryPrefUpdate pref_update_src(pref_service, pref_src);
+ pref_update_dst->Clear();
+ pref_update_dst->Swap(pref_update_src.Get());
+ DCHECK(pref_update_src->empty());
+}
+
+void MaybeInitWeeklyAggregateDataUsePrefs(const base::Time& now,
+ PrefService* pref_service) {
+ int saved_week = pref_service->GetInteger(prefs::kThisWeekNumber);
+ int current_week = GetCurrentWeekNumber(now);
+
+ if (saved_week == current_week)
+ return;
+
+ pref_service->SetInteger(prefs::kThisWeekNumber, current_week);
+ if (current_week == saved_week + 1) {
+ // The next week has just started. Move the data use aggregate prefs for
+ // this week to last week, and clear the prefs for this week.
+ MoveAndClearDictionaryPrefs(pref_service,
+ prefs::kLastWeekServicesDownstreamBackgroundKB,
+ prefs::kThisWeekServicesDownstreamBackgroundKB);
+ MoveAndClearDictionaryPrefs(pref_service,
+ prefs::kLastWeekServicesDownstreamForegroundKB,
+ prefs::kThisWeekServicesDownstreamForegroundKB);
+ MoveAndClearDictionaryPrefs(
+ pref_service, prefs::kLastWeekUserTrafficContentTypeDownstreamKB,
+ prefs::kThisWeekUserTrafficContentTypeDownstreamKB);
+ } else {
+ // Current week is too different than the last time data use aggregate prefs
+ // were updated. This may happen if Chrome was opened after a long time, or
+ // due to system clock being changed backward or forward. Clear all prefs in
+ // this case.
+ pref_service->ClearPref(prefs::kLastWeekServicesDownstreamBackgroundKB);
+ pref_service->ClearPref(prefs::kLastWeekServicesDownstreamForegroundKB);
+ pref_service->ClearPref(prefs::kLastWeekUserTrafficContentTypeDownstreamKB);
+ pref_service->ClearPref(prefs::kThisWeekServicesDownstreamBackgroundKB);
+ pref_service->ClearPref(prefs::kThisWeekServicesDownstreamForegroundKB);
+ pref_service->ClearPref(prefs::kThisWeekUserTrafficContentTypeDownstreamKB);
+ }
+}
+
+// Records the key-value pairs in the dictionary in a sparse histogram.
+void RecordDictionaryToHistogram(const std::string& histogram_name,
+ const base::DictionaryValue* dictionary) {
+ base::HistogramBase* histogram = base::SparseHistogram::FactoryGet(
+ histogram_name, base::HistogramBase::kUmaTargetedHistogramFlag);
+ for (const auto& entry : *dictionary) {
+ int key;
+ int value = entry.second->GetInt();
+ if (value > 0 && base::StringToInt(entry.first, &key)) {
+ histogram->AddCount(key, value);
+ }
+ }
+}
+
} // namespace
class DataReductionProxyCompressionStats::DailyContentLengthUpdate {
@@ -376,6 +469,8 @@ void DataReductionProxyCompressionStats::Init() {
weak_factory_.GetWeakPtr()));
}
+ InitializeWeeklyAggregateDataUse(base::Time::Now());
+
if (delay_.is_zero())
return;
@@ -431,7 +526,10 @@ void DataReductionProxyCompressionStats::RecordDataUseWithMimeType(
int64_t original_size,
bool data_saver_enabled,
DataReductionProxyRequestType request_type,
- const std::string& mime_type) {
+ const std::string& mime_type,
+ bool is_user_traffic,
+ data_use_measurement::DataUseUserData::DataUseContentType content_type,
+ int32_t service_hash_code) {
DCHECK(thread_checker_.CalledOnValidThread());
TRACE_EVENT0("loader",
"DataReductionProxyCompressionStats::RecordDataUseWithMimeType")
@@ -445,6 +543,9 @@ void DataReductionProxyCompressionStats::RecordDataUseWithMimeType(
RecordRequestSizePrefs(data_used, original_size, data_saver_enabled,
request_type, mime_type, base::Time::Now());
+ RecordWeeklyAggregateDataUse(
+ base::Time::Now(), std::round(static_cast<double>(data_used) / 1024),
+ is_user_traffic, content_type, service_hash_code);
}
void DataReductionProxyCompressionStats::InitInt64Pref(const char* pref) {
@@ -1298,6 +1399,80 @@ void DataReductionProxyCompressionStats::OnDataUsageReportingPrefChanged() {
}
}
+void DataReductionProxyCompressionStats::InitializeWeeklyAggregateDataUse(
+ const base::Time& now) {
+#if defined(OS_ANDROID) && defined(ARCH_CPU_X86)
+ // TODO(rajendrant): Enable aggregate metrics recording in x86 Android.
+ // http://crbug.com/865373
+ return;
+#endif
+
+ MaybeInitWeeklyAggregateDataUsePrefs(now, pref_service_);
+ // Record the histograms that will show up in the user feedback.
+ RecordDictionaryToHistogram(
+ "DataReductionProxy.ThisWeekAggregateKB.Services.Downstream.Background",
+ pref_service_->GetDictionary(
+ prefs::kThisWeekServicesDownstreamBackgroundKB));
+ RecordDictionaryToHistogram(
+ "DataReductionProxy.ThisWeekAggregateKB.Services.Downstream.Foreground",
+ pref_service_->GetDictionary(
+ prefs::kThisWeekServicesDownstreamForegroundKB));
+ RecordDictionaryToHistogram(
+ "DataReductionProxy.ThisWeekAggregateKB.UserTraffic.Downstream."
+ "ContentType",
+ pref_service_->GetDictionary(
+ prefs::kThisWeekUserTrafficContentTypeDownstreamKB));
+ RecordDictionaryToHistogram(
+ "DataReductionProxy.LastWeekAggregateKB.Services.Downstream.Background",
+ pref_service_->GetDictionary(
+ prefs::kLastWeekServicesDownstreamBackgroundKB));
+ RecordDictionaryToHistogram(
+ "DataReductionProxy.LastWeekAggregateKB.Services.Downstream.Foreground",
+ pref_service_->GetDictionary(
+ prefs::kLastWeekServicesDownstreamForegroundKB));
+ RecordDictionaryToHistogram(
+ "DataReductionProxy.LastWeekAggregateKB.UserTraffic.Downstream."
+ "ContentType",
+ pref_service_->GetDictionary(
+ prefs::kLastWeekUserTrafficContentTypeDownstreamKB));
+}
+
+void DataReductionProxyCompressionStats::RecordWeeklyAggregateDataUse(
+ const base::Time& now,
+ int32_t data_used_kb,
+ bool is_user_request,
+ data_use_measurement::DataUseUserData::DataUseContentType content_type,
+ int32_t service_hash_code) {
+#if defined(OS_ANDROID) && defined(ARCH_CPU_X86)
+ // TODO(rajendrant): Enable aggregate metrics recording in x86 Android.
+ // http://crbug.com/865373
+ return;
+#endif
+ // Update the prefs if this is a new week. This can happen when chrome is open
+ // for weeks without being closed.
+ MaybeInitWeeklyAggregateDataUsePrefs(now, pref_service_);
+ if (is_user_request) {
+ AddToDictionaryPref(pref_service_,
+ prefs::kThisWeekUserTrafficContentTypeDownstreamKB,
+ content_type, data_used_kb);
+ } else {
+ bool is_app_foreground = true;
+#if defined(OS_ANDROID)
+ is_app_foreground = base::android::ApplicationStatusListener::GetState() ==
+ base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES;
+#endif
+ if (is_app_foreground) {
+ AddToDictionaryPref(pref_service_,
+ prefs::kThisWeekServicesDownstreamForegroundKB,
+ service_hash_code, data_used_kb);
+ } else {
+ AddToDictionaryPref(pref_service_,
+ prefs::kThisWeekServicesDownstreamBackgroundKB,
+ service_hash_code, data_used_kb);
+ }
+ }
+}
+
// static
std::string DataReductionProxyCompressionStats::NormalizeHostname(
const std::string& host) {
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h
index c7917917889..82e53290d05 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h
@@ -22,6 +22,7 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
#include "components/data_reduction_proxy/core/browser/db_data_owner.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
+#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/prefs/pref_member.h"
class PrefService;
@@ -81,11 +82,15 @@ class DataReductionProxyCompressionStats {
// Records detailed data usage broken down by |mime_type|. Also records daily
// data savings statistics to prefs and reports data savings UMA. |data_used|
// and |original_size| are measured in bytes.
- void RecordDataUseWithMimeType(int64_t compressed_size,
- int64_t original_size,
- bool data_reduction_proxy_enabled,
- DataReductionProxyRequestType request_type,
- const std::string& mime_type);
+ void RecordDataUseWithMimeType(
+ int64_t compressed_size,
+ int64_t original_size,
+ bool data_reduction_proxy_enabled,
+ DataReductionProxyRequestType request_type,
+ const std::string& mime_type,
+ bool is_user_traffic,
+ data_use_measurement::DataUseUserData::DataUseContentType content_type,
+ int32_t service_hash_code);
// Record data usage and original size of request broken down by host.
// |original_request_size| and |data_used| are in bytes. |time| is the time at
@@ -254,6 +259,20 @@ class DataReductionProxyCompressionStats {
// persists data usage to memory when pref is disabled.
void OnDataUsageReportingPrefChanged();
+ // Initialize the weekly data use prefs for the current week, and records the
+ // weekly aggregate data use histograms.
+ void InitializeWeeklyAggregateDataUse(const base::Time& now);
+
+ // Records |data_used_kb| to the current week data use pref. |is_user_request|
+ // indicates if this is user-initiated traffic or chrome services traffic, and
+ // |service_hash_code| uniquely identifies the corresponding chrome service.
+ void RecordWeeklyAggregateDataUse(
+ const base::Time& now,
+ int32_t data_used_kb,
+ bool is_user_request,
+ data_use_measurement::DataUseUserData::DataUseContentType content_type,
+ int32_t service_hash_code);
+
// Normalizes the hostname for data usage attribution. Returns a substring
// without the protocol.
// Example: "http://www.finance.google.com" -> "www.finance.google.com"
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc
index 0ec7dbdb450..493b3a5fd80 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats_unittest.cc
@@ -14,7 +14,7 @@
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/time.h"
#include "base/values.h"
@@ -472,6 +472,32 @@ class DataReductionProxyCompressionStatsTest : public testing::Test {
return drp_test_context_->IsDataReductionProxyEnabled();
}
+ void InitializeWeeklyAggregateDataUse(const base::Time& now) {
+ compression_stats_->InitializeWeeklyAggregateDataUse(now);
+ }
+
+ void RecordWeeklyAggregateDataUse(
+ const base::Time& now,
+ int32_t received_kb,
+ bool is_user_request,
+ data_use_measurement::DataUseUserData::DataUseContentType content_type,
+ int32_t service_hash_code) {
+ compression_stats_->RecordWeeklyAggregateDataUse(
+ now, received_kb, is_user_request, content_type, service_hash_code);
+ }
+
+ void VerifyDictionaryPref(const std::string& pref,
+ int key,
+ int expected_value) const {
+ const base::DictionaryValue* dict =
+ compression_stats_->pref_service_->GetDictionary(pref);
+ EXPECT_EQ(expected_value != 0, dict->HasKey(base::IntToString(key)));
+ if (expected_value) {
+ EXPECT_EQ(expected_value,
+ dict->FindKey(base::IntToString(key))->GetInt());
+ }
+ }
+
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
std::unique_ptr<DataReductionProxyTestContext> drp_test_context_;
@@ -569,7 +595,8 @@ TEST_F(DataReductionProxyCompressionStatsTest, TotalLengths) {
compression_stats()->RecordDataUseWithMimeType(
kReceivedLength, kOriginalLength, IsDataReductionProxyEnabled(),
- UNKNOWN_TYPE, std::string());
+ UNKNOWN_TYPE, std::string(), true,
+ data_use_measurement::DataUseUserData::OTHER, 0);
EXPECT_EQ(kReceivedLength,
GetInt64(data_reduction_proxy::prefs::kHttpReceivedContentLength));
@@ -580,7 +607,8 @@ TEST_F(DataReductionProxyCompressionStatsTest, TotalLengths) {
// Record the same numbers again, and total lengths should be doubled.
compression_stats()->RecordDataUseWithMimeType(
kReceivedLength, kOriginalLength, IsDataReductionProxyEnabled(),
- UNKNOWN_TYPE, std::string());
+ UNKNOWN_TYPE, std::string(), true,
+ data_use_measurement::DataUseUserData::OTHER, 0);
EXPECT_EQ(kReceivedLength * 2,
GetInt64(data_reduction_proxy::prefs::kHttpReceivedContentLength));
@@ -1294,4 +1322,134 @@ TEST_F(DataReductionProxyCompressionStatsTest, ClearDataSavingStatistics) {
nullptr, 0, 0);
}
+TEST_F(DataReductionProxyCompressionStatsTest, WeeklyAggregateDataUse) {
+ const int32_t kDataUseKB = 100;
+ base::HistogramTester histogram_tester;
+
+ InitializeWeeklyAggregateDataUse(base::Time::Now());
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.ThisWeekAggregateKB.Services.Downstream.Background",
+ 0);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.ThisWeekAggregateKB.Services.Downstream.Foreground",
+ 0);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.LastWeekAggregateKB.Services.Downstream.Background",
+ 0);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.LastWeekAggregateKB.Services.Downstream.Foreground",
+ 0);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.ThisWeekAggregateKB.UserTraffic.Downstream."
+ "ContentType",
+ 0);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.LastWeekAggregateKB.UserTraffic.Downstream."
+ "ContentType",
+ 0);
+
+ RecordWeeklyAggregateDataUse(
+ base::Time::Now(), kDataUseKB, true,
+ data_use_measurement::DataUseUserData::MAIN_FRAME_HTML, 0);
+ VerifyDictionaryPref(prefs::kThisWeekUserTrafficContentTypeDownstreamKB,
+ data_use_measurement::DataUseUserData::MAIN_FRAME_HTML,
+ kDataUseKB);
+
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.ThisWeekAggregateKB.UserTraffic.Downstream."
+ "ContentType",
+ 0);
+
+ InitializeWeeklyAggregateDataUse(base::Time::Now());
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.ThisWeekAggregateKB.UserTraffic.Downstream."
+ "ContentType",
+ data_use_measurement::DataUseUserData::MAIN_FRAME_HTML, kDataUseKB);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.LastWeekAggregateKB.UserTraffic.Downstream."
+ "ContentType",
+ data_use_measurement::DataUseUserData::MAIN_FRAME_HTML, 0);
+}
+
+TEST_F(DataReductionProxyCompressionStatsTest, AggregateDataUseForwardWeeks) {
+ const int32_t kMainFrameKB = 100;
+ const int32_t kNonMainFrameKB = 101;
+ base::HistogramTester histogram_tester;
+
+ base::Time fake_time_now = base::Time::Now();
+
+ InitializeWeeklyAggregateDataUse(fake_time_now);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.ThisWeekAggregateKB.UserTraffic.Downstream."
+ "ContentType",
+ 0);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.LastWeekAggregateKB.UserTraffic.Downstream."
+ "ContentType",
+ 0);
+
+ RecordWeeklyAggregateDataUse(
+ fake_time_now, kMainFrameKB, true,
+ data_use_measurement::DataUseUserData::MAIN_FRAME_HTML, 0);
+ VerifyDictionaryPref(prefs::kThisWeekUserTrafficContentTypeDownstreamKB,
+ data_use_measurement::DataUseUserData::MAIN_FRAME_HTML,
+ kMainFrameKB);
+ RecordWeeklyAggregateDataUse(
+ fake_time_now, kNonMainFrameKB, true,
+ data_use_measurement::DataUseUserData::NON_MAIN_FRAME_HTML, 0);
+ VerifyDictionaryPref(
+ prefs::kThisWeekUserTrafficContentTypeDownstreamKB,
+ data_use_measurement::DataUseUserData::NON_MAIN_FRAME_HTML,
+ kNonMainFrameKB);
+
+ // Fast forward 7 days, and verify that the last week histograms are recorded.
+ fake_time_now += base::TimeDelta::FromDays(7);
+ InitializeWeeklyAggregateDataUse(fake_time_now);
+ VerifyDictionaryPref(prefs::kLastWeekUserTrafficContentTypeDownstreamKB,
+ data_use_measurement::DataUseUserData::MAIN_FRAME_HTML,
+ kMainFrameKB);
+ VerifyDictionaryPref(
+ prefs::kLastWeekUserTrafficContentTypeDownstreamKB,
+ data_use_measurement::DataUseUserData::NON_MAIN_FRAME_HTML,
+ kNonMainFrameKB);
+
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.LastWeekAggregateKB.UserTraffic.Downstream."
+ "ContentType",
+ data_use_measurement::DataUseUserData::MAIN_FRAME_HTML, kMainFrameKB);
+ histogram_tester.ExpectBucketCount(
+ "DataReductionProxy.LastWeekAggregateKB.UserTraffic.Downstream."
+ "ContentType",
+ data_use_measurement::DataUseUserData::NON_MAIN_FRAME_HTML,
+ kNonMainFrameKB);
+ histogram_tester.ExpectTotalCount(
+ "DataReductionProxy.ThisWeekAggregateKB.UserTraffic.Downstream."
+ "ContentType",
+ 0);
+
+ // Subsequent data use should be recorded to the current week prefs.
+ RecordWeeklyAggregateDataUse(
+ fake_time_now, kMainFrameKB, true,
+ data_use_measurement::DataUseUserData::MAIN_FRAME_HTML, 0);
+ VerifyDictionaryPref(prefs::kThisWeekUserTrafficContentTypeDownstreamKB,
+ data_use_measurement::DataUseUserData::MAIN_FRAME_HTML,
+ kMainFrameKB);
+
+ // Fast forward by more than two weeks, and the prefs will be cleared.
+ fake_time_now += base::TimeDelta::FromDays(15);
+ InitializeWeeklyAggregateDataUse(fake_time_now);
+ VerifyDictionaryPref(prefs::kLastWeekUserTrafficContentTypeDownstreamKB,
+ data_use_measurement::DataUseUserData::MAIN_FRAME_HTML,
+ 0);
+ VerifyDictionaryPref(
+ prefs::kLastWeekUserTrafficContentTypeDownstreamKB,
+ data_use_measurement::DataUseUserData::NON_MAIN_FRAME_HTML, 0);
+ VerifyDictionaryPref(prefs::kThisWeekUserTrafficContentTypeDownstreamKB,
+ data_use_measurement::DataUseUserData::MAIN_FRAME_HTML,
+ 0);
+ VerifyDictionaryPref(
+ prefs::kThisWeekUserTrafficContentTypeDownstreamKB,
+ data_use_measurement::DataUseUserData::NON_MAIN_FRAME_HTML, 0);
+}
+
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
index 04ff7e8fcd3..420249d350f 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
@@ -24,6 +24,7 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/task_runner_util.h"
+#include "base/task_scheduler/lazy_task_runner.h"
#include "base/time/default_tick_clock.h"
#include "build/build_config.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
@@ -59,6 +60,17 @@ using base::FieldTrialList;
namespace {
+#if defined(OS_CHROMEOS)
+// SequencedTaskRunner to get the network id. A SequencedTaskRunner is used
+// rather than parallel tasks to avoid having many threads getting the network
+// id concurrently.
+base::LazySequencedTaskRunner g_get_network_id_task_runner =
+ LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER(
+ base::TaskTraits(base::MayBlock(),
+ base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN));
+#endif
+
// Values of the UMA DataReductionProxy.Protocol.NotAcceptingTransform histogram
// defined in metrics/histograms/histograms.xml. This enum must remain
// synchronized with DataReductionProxyProtocolNotAcceptingTransformReason in
@@ -184,6 +196,7 @@ DataReductionProxyConfig::DataReductionProxyConfig(
configurator_(configurator),
event_creator_(event_creator),
connection_type_(net::NetworkChangeNotifier::GetConnectionType()),
+ ignore_long_term_black_list_rules_(false),
network_properties_manager_(nullptr),
weak_factory_(this) {
DCHECK(io_task_runner_);
@@ -616,16 +629,18 @@ void DataReductionProxyConfig::OnNetworkChanged(
connection_type_ = type;
RecordNetworkChangeEvent(NETWORK_CHANGED);
- if (!get_network_id_task_runner_) {
- ContinueNetworkChanged(GetCurrentNetworkID());
+#if defined(OS_CHROMEOS)
+ if (get_network_id_asynchronously_) {
+ base::PostTaskAndReplyWithResult(
+ g_get_network_id_task_runner.Get().get(), FROM_HERE,
+ base::BindOnce(&DoGetCurrentNetworkID),
+ base::BindOnce(&DataReductionProxyConfig::ContinueNetworkChanged,
+ weak_factory_.GetWeakPtr()));
return;
}
+#endif // defined(OS_CHROMEOS)
- base::PostTaskAndReplyWithResult(
- get_network_id_task_runner_.get(), FROM_HERE,
- base::BindOnce(&DoGetCurrentNetworkID),
- base::BindOnce(&DataReductionProxyConfig::ContinueNetworkChanged,
- weak_factory_.GetWeakPtr()));
+ ContinueNetworkChanged(GetCurrentNetworkID());
}
void DataReductionProxyConfig::ContinueNetworkChanged(
@@ -752,7 +767,7 @@ bool DataReductionProxyConfig::IsBlackListedOrDisabled(
// TODO(crbug.com/720102): Consider new method to just check blacklist.
return !previews_decider.ShouldAllowPreviewAtECT(
request, previews_type, net::EFFECTIVE_CONNECTION_TYPE_4G,
- std::vector<std::string>());
+ std::vector<std::string>(), ignore_long_term_black_list_rules_);
}
bool DataReductionProxyConfig::ShouldAcceptServerPreview(
@@ -760,7 +775,7 @@ bool DataReductionProxyConfig::ShouldAcceptServerPreview(
const previews::PreviewsDecider& previews_decider) const {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK((request.load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED) != 0);
- DCHECK(!request.url().SchemeIsCryptographic());
+ DCHECK(request.url().SchemeIsHTTPOrHTTPS());
if (!previews::params::ArePreviewsAllowed() ||
!base::FeatureList::IsEnabled(
@@ -832,4 +847,21 @@ DataReductionProxyConfig::GetInFlightWarmupProxyDetails() const {
warmup_url_fetch_in_flight_core_proxy_);
}
+#if defined(OS_CHROMEOS)
+void DataReductionProxyConfig::EnableGetNetworkIdAsynchronously() {
+ get_network_id_asynchronously_ = true;
+}
+#endif // defined(OS_CHROMEOS)
+
+void DataReductionProxyConfig::SetIgnoreLongTermBlackListRules(
+ bool ignore_long_term_black_list_rules) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ignore_long_term_black_list_rules_ = ignore_long_term_black_list_rules;
+}
+
+bool DataReductionProxyConfig::IgnoreBlackListLongTermRulesForTesting() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return ignore_long_term_black_list_rules_;
+}
+
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
index 46ad67237cc..f6bec706d4c 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
@@ -19,6 +19,7 @@
#include "base/optional.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
+#include "build/build_config.h"
#include "components/data_reduction_proxy/core/browser/secure_proxy_checker.h"
#include "components/data_reduction_proxy/core/browser/warmup_url_fetcher.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
@@ -192,10 +193,20 @@ class DataReductionProxyConfig
std::pair<bool /* is_secure_proxy */, bool /*is_core_proxy */>>
GetInFlightWarmupProxyDetails() const;
- void set_get_network_id_task_runner(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
- get_network_id_task_runner_ = task_runner;
- }
+#if defined(OS_CHROMEOS)
+ // Enables getting the network id asynchronously when
+ // GatherEstimatesForNextConnectionType(). This should always be called in
+ // production, because getting the network id involves a blocking call to
+ // recv() in AddressTrackerLinux, and the IO thread should never be blocked.
+ // TODO(https://crbug.com/821607): Remove after the bug is resolved.
+ void EnableGetNetworkIdAsynchronously();
+#endif // defined(OS_CHROMEOS)
+
+ // When triggering previews, prevent long term black list rules.
+ void SetIgnoreLongTermBlackListRules(bool ignore_long_term_black_list_rules);
+
+ // Returns the value set in SetIgnoreLongTermBlackListRules.
+ bool IgnoreBlackListLongTermRulesForTesting() const;
protected:
virtual base::TimeTicks GetTicksNow() const;
@@ -264,7 +275,7 @@ class DataReductionProxyConfig
net::NetworkChangeNotifier::ConnectionType type) override;
// Invoked to continue network changed handling after the network id is
- // retrieved. If |get_network_id_task_runner_| is set, the network id is
+ // retrieved. If |get_network_id_asynchronously_| is set, the network id is
// fetched on the worker thread. Otherwise, OnNetworkChanged calls this
// directly. This is a workaround for https://crbug.com/821607 where
// net::GetWifiSSID() call gets stuck.
@@ -327,8 +338,10 @@ class DataReductionProxyConfig
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
- // Optional task runner for GetCurrentNetworkID.
- scoped_refptr<base::SingleThreadTaskRunner> get_network_id_task_runner_;
+#if defined(OS_CHROMEOS)
+ // Whether the network id should be obtained on a worker thread.
+ bool get_network_id_asynchronously_ = false;
+#endif
// The caller must ensure that the |net_log_|, if set, outlives this instance.
// It is used to create new instances of |net_log_with_source_| on secure
@@ -356,6 +369,9 @@ class DataReductionProxyConfig
bool warmup_url_fetch_in_flight_secure_proxy_;
bool warmup_url_fetch_in_flight_core_proxy_;
+ // When triggerring previews, prevent long term black list rules.
+ bool ignore_long_term_black_list_rules_;
+
// Should be accessed only on the IO thread. Guaranteed to be non-null during
// the lifetime of |this| if accessed on the IO thread.
NetworkPropertiesManager* network_properties_manager_;
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
index 004072cdb06..b62bcafc25c 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
@@ -11,6 +11,7 @@
#include "base/base64.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
+#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/json/json_writer.h"
#include "base/location.h"
@@ -30,6 +31,7 @@
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
#include "components/data_reduction_proxy/proto/client_config.pb.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/variations/net/variations_http_headers.h"
@@ -167,7 +169,8 @@ DataReductionProxyConfigServiceClient::DataReductionProxyConfigServiceClient(
#endif
previous_request_failed_authentication_(false),
failed_attempts_before_success_(0),
- fetch_in_progress_(false) {
+ fetch_in_progress_(false),
+ client_config_override_used_(false) {
DCHECK(request_options);
DCHECK(config_values);
DCHECK(config);
@@ -175,13 +178,19 @@ DataReductionProxyConfigServiceClient::DataReductionProxyConfigServiceClient(
DCHECK(io_data);
DCHECK(net_log);
DCHECK(config_service_url_.is_valid());
+
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
+ client_config_override_ = command_line.GetSwitchValueASCII(
+ switches::kDataReductionProxyServerClientConfig);
+
// Constructed on the UI thread, but should be checked on the IO thread.
thread_checker_.DetachFromThread();
}
DataReductionProxyConfigServiceClient::
~DataReductionProxyConfigServiceClient() {
- net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
+ net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
}
base::TimeDelta
@@ -221,7 +230,7 @@ void DataReductionProxyConfigServiceClient::InitializeOnIOThread(
&DataReductionProxyConfigServiceClient::OnApplicationStateChange,
base::Unretained(this))));
#endif
- net::NetworkChangeNotifier::AddIPAddressObserver(this);
+ net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
url_request_context_getter_ = url_request_context_getter;
}
@@ -235,6 +244,29 @@ void DataReductionProxyConfigServiceClient::RetrieveConfig() {
if (!enabled_)
return;
+ if (!client_config_override_.empty()) {
+ // Return fast if the override has already been attempted.
+ if (client_config_override_used_) {
+ return;
+ }
+ // Set this flag so that we only attempt to apply the given config once. If
+ // there are parse errors, the DCHECKs will catch them in a debug build.
+ client_config_override_used_ = true;
+
+ std::string override_config;
+ bool b64_decode_ok =
+ base::Base64Decode(client_config_override_, &override_config);
+ LOG_IF(DFATAL, !b64_decode_ok)
+ << "The given ClientConfig is not valid base64";
+
+ ClientConfig config;
+ bool was_valid_config = config.ParseFromString(override_config);
+ LOG_IF(DFATAL, !was_valid_config) << "The given ClientConfig was invalid.";
+ if (was_valid_config)
+ ParseAndApplyProxyConfig(config);
+ return;
+ }
+
net_log_with_source_ = net::NetLogWithSource::Make(
net_log_, net::NetLogSourceType::DATA_REDUCTION_PROXY);
// Strip off query string parameters
@@ -260,6 +292,9 @@ void DataReductionProxyConfigServiceClient::ApplySerializedConfig(
if (RemoteConfigApplied())
return;
+ if (!client_config_override_.empty())
+ return;
+
std::string decoded_config;
if (base::Base64Decode(config_value, &decoded_config)) {
ClientConfig config;
@@ -356,8 +391,13 @@ base::Time DataReductionProxyConfigServiceClient::Now() {
return base::Time::Now();
}
-void DataReductionProxyConfigServiceClient::OnIPAddressChanged() {
+void DataReductionProxyConfigServiceClient::OnNetworkChanged(
+ net::NetworkChangeNotifier::ConnectionType type) {
DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (type == net::NetworkChangeNotifier::CONNECTION_NONE)
+ return;
+
GetBackoffEntry()->Reset();
last_ip_address_change_ = base::TimeTicks::Now();
failed_attempts_before_success_ = 0;
@@ -555,6 +595,9 @@ bool DataReductionProxyConfigServiceClient::ParseAndApplyProxyConfig(
if (!config.has_proxy_config())
return false;
+ config_->SetIgnoreLongTermBlackListRules(
+ config.ignore_long_term_black_list_rules());
+
// An empty proxy config is OK, and allows the server to effectively turn off
// DataSaver if needed. See http://crbug.com/840978.
std::vector<DataReductionProxyServer> proxies =
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h
index da936ce7303..87aa6a525d7 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h
@@ -83,7 +83,7 @@ const net::BackoffEntry::Policy& GetBackoffPolicy();
// fetch policy is different if Chrome is in the background. Every time a config
// is fetched, it is written to the disk.
class DataReductionProxyConfigServiceClient
- : public net::NetworkChangeNotifier::IPAddressObserver,
+ : public net::NetworkChangeNotifier::NetworkChangeObserver,
public net::URLFetcherDelegate {
public:
// The caller must ensure that all parameters remain alive for the lifetime of
@@ -162,8 +162,9 @@ class DataReductionProxyConfigServiceClient
const base::TimeDelta& config_expiration,
const base::TimeDelta& backoff_delay);
- // Override of net::NetworkChangeNotifier::IPAddressObserver.
- void OnIPAddressChanged() override;
+ // Override of net::NetworkChangeNotifier::NetworkChangeObserver.
+ void OnNetworkChanged(
+ net::NetworkChangeNotifier::ConnectionType type) override;
// Override of net::URLFetcherDelegate.
void OnURLFetchComplete(const net::URLFetcher* source) override;
@@ -283,6 +284,15 @@ class DataReductionProxyConfigServiceClient
// True if a client config fetch is in progress.
bool fetch_in_progress_;
+ // If given on the command line with kDataReductionProxyServerClientConfig,
+ // this base64 binary-encoded ClientConfig will always be used instead of
+ // fetching one remotely, regardless of authentication error or expiration. If
+ // the value fails to parse as a valid ClientConfig, it will not be used.
+ std::string client_config_override_;
+
+ // True if |client_config_override_| has been applied to |this|.
+ bool client_config_override_used_;
+
// Enforce usage on the IO thread.
base::ThreadChecker thread_checker_;
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
index becb0e43ff4..a7853c588f8 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
@@ -16,7 +16,7 @@
#include "base/message_loop/message_loop.h"
#include "base/metrics/field_trial.h"
#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_entropy_provider.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
@@ -91,7 +91,8 @@ ClientConfig CreateConfig(const std::string& session_key,
const std::string& secondary_host,
int secondary_port,
const ProxyServer_ProxyType& secondary_proxy_type,
- float reporting_fraction) {
+ float reporting_fraction,
+ bool ignore_long_term_black_list_rules) {
ClientConfig config;
config.set_session_key(session_key);
@@ -104,6 +105,8 @@ ClientConfig CreateConfig(const std::string& session_key,
config.mutable_pageload_metrics_config()->set_reporting_fraction(
reporting_fraction);
}
+ config.set_ignore_long_term_black_list_rules(
+ ignore_long_term_black_list_rules);
ProxyServer* primary_proxy =
config.mutable_proxy_config()->add_http_proxy_servers();
primary_proxy->set_scheme(primary_scheme);
@@ -172,11 +175,11 @@ class DataReductionProxyConfigServiceClientTest : public testing::Test {
ASSERT_NE(nullptr, context_->network_delegate());
// Set up the various test ClientConfigs.
- ClientConfig config =
- CreateConfig(kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
- ProxyServer_ProxyScheme_HTTPS, "origin.net", 443,
- ProxyServer::CORE, ProxyServer_ProxyScheme_HTTP,
- "fallback.net", 80, ProxyServer::UNSPECIFIED_TYPE, 0.5f);
+ ClientConfig config = CreateConfig(
+ kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
+ ProxyServer_ProxyScheme_HTTPS, "origin.net", 443, ProxyServer::CORE,
+ ProxyServer_ProxyScheme_HTTP, "fallback.net", 80,
+ ProxyServer::UNSPECIFIED_TYPE, 0.5f, false);
config.SerializeToString(&config_);
encoded_config_ = EncodeConfig(config);
@@ -184,21 +187,21 @@ class DataReductionProxyConfigServiceClientTest : public testing::Test {
kOldSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
ProxyServer_ProxyScheme_HTTPS, "old.origin.net", 443, ProxyServer::CORE,
ProxyServer_ProxyScheme_HTTP, "old.fallback.net", 80,
- ProxyServer::UNSPECIFIED_TYPE, 0.0f);
+ ProxyServer::UNSPECIFIED_TYPE, 0.0f, false);
previous_config.SerializeToString(&previous_config_);
- ClientConfig persisted =
- CreateConfig(kPersistedSessionKey, kConfigRefreshDurationSeconds, 0,
- ProxyServer_ProxyScheme_HTTPS, "persisted.net", 443,
- ProxyServer::CORE, ProxyServer_ProxyScheme_HTTP,
- "persisted.net", 80, ProxyServer::UNSPECIFIED_TYPE, 0.0f);
+ ClientConfig persisted = CreateConfig(
+ kPersistedSessionKey, kConfigRefreshDurationSeconds, 0,
+ ProxyServer_ProxyScheme_HTTPS, "persisted.net", 443, ProxyServer::CORE,
+ ProxyServer_ProxyScheme_HTTP, "persisted.net", 80,
+ ProxyServer::UNSPECIFIED_TYPE, 0.0f, false);
loaded_config_ = EncodeConfig(persisted);
- ClientConfig zero_reporting_fraction_config =
- CreateConfig(kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
- ProxyServer_ProxyScheme_HTTPS, "origin.net", 443,
- ProxyServer::CORE, ProxyServer_ProxyScheme_HTTP,
- "origin.net", 0, ProxyServer::UNSPECIFIED_TYPE, 0.0f);
+ ClientConfig zero_reporting_fraction_config = CreateConfig(
+ kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
+ ProxyServer_ProxyScheme_HTTPS, "origin.net", 443, ProxyServer::CORE,
+ ProxyServer_ProxyScheme_HTTP, "origin.net", 0,
+ ProxyServer::UNSPECIFIED_TYPE, 0.0f, false);
zero_reporting_fraction_encoded_config_ =
EncodeConfig(zero_reporting_fraction_config);
@@ -206,29 +209,36 @@ class DataReductionProxyConfigServiceClientTest : public testing::Test {
CreateConfig(kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
ProxyServer_ProxyScheme_HTTPS, "", 443, ProxyServer::CORE,
ProxyServer_ProxyScheme_HTTP, "", 0,
- ProxyServer::UNSPECIFIED_TYPE, 1.0f);
+ ProxyServer::UNSPECIFIED_TYPE, 1.0f, false);
one_reporting_fraction_encoded_config_ =
EncodeConfig(one_reporting_fraction_config);
// Passing in -1.0f as the reporting fraction causes the
// |empty_reporting_fraction_config| to have no pageload_metrics_config()
// set.
- ClientConfig empty_reporting_fraction_config =
- CreateConfig(kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
- ProxyServer_ProxyScheme_HTTPS, "origin.net", 443,
- ProxyServer::CORE, ProxyServer_ProxyScheme_HTTP,
- "origin.net", 0, ProxyServer::UNSPECIFIED_TYPE, -1.0f);
+ ClientConfig empty_reporting_fraction_config = CreateConfig(
+ kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
+ ProxyServer_ProxyScheme_HTTPS, "origin.net", 443, ProxyServer::CORE,
+ ProxyServer_ProxyScheme_HTTP, "origin.net", 0,
+ ProxyServer::UNSPECIFIED_TYPE, -1.0f, false);
empty_reporting_fraction_encoded_config_ =
EncodeConfig(empty_reporting_fraction_config);
- ClientConfig half_reporting_fraction_config =
- CreateConfig(kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
- ProxyServer_ProxyScheme_HTTPS, "origin.net", 443,
- ProxyServer::CORE, ProxyServer_ProxyScheme_HTTP,
- "origin.net", 0, ProxyServer::UNSPECIFIED_TYPE, 0.5f);
+ ClientConfig half_reporting_fraction_config = CreateConfig(
+ kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
+ ProxyServer_ProxyScheme_HTTPS, "origin.net", 443, ProxyServer::CORE,
+ ProxyServer_ProxyScheme_HTTP, "origin.net", 0,
+ ProxyServer::UNSPECIFIED_TYPE, 0.5f, false);
half_reporting_fraction_encoded_config_ =
EncodeConfig(half_reporting_fraction_config);
+ ClientConfig ignore_black_list_config = CreateConfig(
+ kSuccessSessionKey, kConfigRefreshDurationSeconds, 0,
+ ProxyServer_ProxyScheme_HTTPS, "origin.net", 443, ProxyServer::CORE,
+ ProxyServer_ProxyScheme_HTTP, "origin.net", 0,
+ ProxyServer::UNSPECIFIED_TYPE, 0.5f, true);
+ ignore_black_list_encoded_config_ = EncodeConfig(ignore_black_list_config);
+
ClientConfig no_proxies_config;
no_proxies_config.set_session_key(kSuccessSessionKey);
no_proxies_config.mutable_refresh_duration()->set_seconds(
@@ -454,6 +464,9 @@ class DataReductionProxyConfigServiceClientTest : public testing::Test {
const std::string& half_reporting_fraction_encoded_config() const {
return half_reporting_fraction_encoded_config_;
}
+ const std::string& ignore_black_list_encoded_config() const {
+ return ignore_black_list_encoded_config_;
+ }
const std::string& no_proxies_config() const { return no_proxies_config_; }
const std::string& loaded_config() const { return loaded_config_; }
@@ -498,6 +511,9 @@ class DataReductionProxyConfigServiceClientTest : public testing::Test {
// A configuration where the pingback reporting fraction is set to 0.5f.
std::string half_reporting_fraction_encoded_config_;
+ // A configuration where the black list rules are ignored.
+ std::string ignore_black_list_encoded_config_;
+
// A configuration where no proxies are configured.
std::string no_proxies_config_;
@@ -687,7 +703,8 @@ TEST_F(DataReductionProxyConfigServiceClientTest, OnIPAddressChange) {
EXPECT_EQ(kFailureCount, config_client()->GetBackoffErrorCount());
// IP address change should reset.
- config_client()->OnIPAddressChanged();
+ config_client()->OnNetworkChanged(
+ net::NetworkChangeNotifier::CONNECTION_WIFI);
EXPECT_EQ(0, config_client()->GetBackoffErrorCount());
EXPECT_EQ(i == 0, persisted_config().empty());
EXPECT_EQ(i == 0, persisted_config_retrieval_time().is_null());
@@ -729,7 +746,8 @@ TEST_F(DataReductionProxyConfigServiceClientTest,
EXPECT_EQ(kFailureCount, config_client()->GetBackoffErrorCount());
// IP address change should reset.
- config_client()->OnIPAddressChanged();
+ config_client()->OnNetworkChanged(
+ net::NetworkChangeNotifier::CONNECTION_WIFI);
EXPECT_EQ(0, config_client()->GetBackoffErrorCount());
EXPECT_TRUE(persisted_config().empty());
EXPECT_TRUE(persisted_config_retrieval_time().is_null());
@@ -779,7 +797,8 @@ TEST_F(DataReductionProxyConfigServiceClientTest, OnIPAddressChangeDisabled) {
}
EXPECT_EQ(0, config_client()->GetBackoffErrorCount());
- config_client()->OnIPAddressChanged();
+ config_client()->OnNetworkChanged(
+ net::NetworkChangeNotifier::CONNECTION_WIFI);
EXPECT_EQ(0, config_client()->GetBackoffErrorCount());
config_client()->RetrieveConfig();
@@ -1245,6 +1264,33 @@ TEST_F(DataReductionProxyConfigServiceClientTest, HTTPRequests) {
}
}
+// Tests that the config is overriden by kDataReductionProxyServerClientConfig.
+TEST_F(DataReductionProxyConfigServiceClientTest, ApplyClientConfigOverride) {
+ const std::string override_key = "OverrideSecureSession";
+ std::string encoded_config;
+ ClientConfig config = CreateConfig(
+ override_key, kConfigRefreshDurationSeconds, 0,
+ ProxyServer_ProxyScheme_HTTPS, "origin.net", 443, ProxyServer::CORE,
+ ProxyServer_ProxyScheme_HTTP, "fallback.net", 80,
+ ProxyServer::UNSPECIFIED_TYPE, 0.5f, false);
+ config.SerializeToString(&encoded_config);
+ base::Base64Encode(encoded_config, &encoded_config);
+
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ data_reduction_proxy::switches::kDataReductionProxyServerClientConfig,
+ encoded_config);
+ Init(true);
+
+ AddMockSuccess();
+ SetDataReductionProxyEnabled(true, true);
+ config_client()->RetrieveConfig();
+ RunUntilIdle();
+ // Make sure repeated fetches won't change the overridden config.
+ config_client()->RetrieveConfig();
+ RunUntilIdle();
+ EXPECT_EQ(request_options()->GetSecureSession(), override_key);
+}
+
// Tests that remote config can be applied after the serialized config has
// been applied.
TEST_F(DataReductionProxyConfigServiceClientTest, ApplySerializedConfig) {
@@ -1368,6 +1414,15 @@ TEST_F(DataReductionProxyConfigServiceClientTest,
config_client()->ApplySerializedConfig(
half_reporting_fraction_encoded_config());
EXPECT_EQ(0.5f, pingback_reporting_fraction());
+ EXPECT_FALSE(config()->IgnoreBlackListLongTermRulesForTesting());
+}
+
+TEST_F(DataReductionProxyConfigServiceClientTest,
+ ApplySerializedConfigIgnoreBlackList) {
+ Init(true);
+
+ config_client()->ApplySerializedConfig(ignore_black_list_encoded_config());
+ EXPECT_TRUE(config()->IgnoreBlackListLongTermRulesForTesting());
}
TEST_F(DataReductionProxyConfigServiceClientTest, EmptyConfigDisablesDRP) {
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
index 6687ddeb505..d04bb6b9e92 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
@@ -24,7 +24,7 @@
#include "base/strings/safe_sprintf.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/threading/platform_thread.h"
@@ -879,8 +879,10 @@ TEST_F(DataReductionProxyConfigTest,
net::TestURLRequestContext context;
net::TestDelegate delegate;
- std::unique_ptr<net::URLRequest> request = context.CreateRequest(
- GURL(), net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
+ std::unique_ptr<net::URLRequest> request =
+ context.CreateRequest(GURL("http://origin.net:80"
+ ""),
+ net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->SetLoadFlags(request->load_flags() |
net::LOAD_MAIN_FRAME_DEPRECATED);
std::unique_ptr<previews::TestPreviewsDecider> previews_decider =
@@ -898,8 +900,10 @@ TEST_F(DataReductionProxyConfigTest,
net::TestURLRequestContext context;
net::TestDelegate delegate;
- std::unique_ptr<net::URLRequest> request = context.CreateRequest(
- GURL(), net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
+ std::unique_ptr<net::URLRequest> request =
+ context.CreateRequest(GURL("http://origin.net:80"
+ ""),
+ net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->SetLoadFlags(request->load_flags() |
net::LOAD_MAIN_FRAME_DEPRECATED);
std::unique_ptr<previews::TestPreviewsDecider> previews_decider =
@@ -921,8 +925,9 @@ TEST_F(DataReductionProxyConfigTest, ShouldAcceptServerPreview) {
base::HistogramTester histogram_tester;
net::TestURLRequestContext context_;
net::TestDelegate delegate_;
- std::unique_ptr<net::URLRequest> request = context_.CreateRequest(
- GURL(), net::IDLE, &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
+ std::unique_ptr<net::URLRequest> request =
+ context_.CreateRequest(GURL("http://origin.net:80"), net::IDLE,
+ &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
request->SetLoadFlags(request->load_flags() |
net::LOAD_MAIN_FRAME_DEPRECATED);
std::unique_ptr<previews::TestPreviewsDecider> previews_decider =
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc
index f03df0d2b09..a964b50a4ea 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_unittest.cc
@@ -8,7 +8,7 @@
#include <string>
#include <vector>
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/default_clock.h"
#include "base/values.h"
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data.cc
index 1d489b60ade..f8fb87b9ec9 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data.cc
@@ -12,6 +12,24 @@ namespace data_reduction_proxy {
const void* const kDataReductionProxyUserDataKey =
&kDataReductionProxyUserDataKey;
+DataReductionProxyData::RequestInfo::RequestInfo(Protocol protocol,
+ bool proxy_bypass,
+ base::TimeDelta dns_time,
+ base::TimeDelta connect_time,
+ base::TimeDelta http_time)
+ : protocol(protocol),
+ proxy_bypass(proxy_bypass),
+ dns_time(dns_time),
+ connect_time(connect_time),
+ http_time(http_time) {}
+
+DataReductionProxyData::RequestInfo::RequestInfo(const RequestInfo& other)
+ : protocol(other.protocol),
+ proxy_bypass(other.proxy_bypass),
+ dns_time(other.dns_time),
+ connect_time(other.connect_time),
+ http_time(other.http_time) {}
+
DataReductionProxyData::DataReductionProxyData()
: used_data_reduction_proxy_(false),
lofi_requested_(false),
@@ -19,8 +37,10 @@ DataReductionProxyData::DataReductionProxyData()
lite_page_received_(false),
lofi_policy_received_(false),
lofi_received_(false),
+ black_listed_(false),
effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
- connection_type_(net::NetworkChangeNotifier::CONNECTION_UNKNOWN) {}
+ connection_type_(net::NetworkChangeNotifier::CONNECTION_UNKNOWN),
+ request_info_(std::vector<DataReductionProxyData::RequestInfo>()) {}
DataReductionProxyData::~DataReductionProxyData() {}
@@ -55,4 +75,44 @@ void DataReductionProxyData::ClearData(net::URLRequest* request) {
request->RemoveUserData(kDataReductionProxyUserDataKey);
}
+std::vector<DataReductionProxyData::RequestInfo>
+DataReductionProxyData::TakeRequestInfo() {
+ return std::move(request_info_);
+}
+
+std::unique_ptr<DataReductionProxyData::RequestInfo>
+DataReductionProxyData::CreateRequestInfoFromRequest(net::URLRequest* request,
+ bool did_bypass_proxy) {
+ DCHECK(request);
+
+ auto timing_info = std::make_unique<net::LoadTimingInfo>();
+ request->GetLoadTimingInfo(timing_info.get());
+ if (timing_info) {
+ base::TimeDelta dns_time = timing_info->connect_timing.dns_end -
+ timing_info->connect_timing.dns_start;
+ base::TimeDelta connect_time = timing_info->connect_timing.connect_end -
+ timing_info->connect_timing.connect_start;
+ base::TimeDelta http_time =
+ timing_info->receive_headers_end - timing_info->send_start;
+ DataReductionProxyData::RequestInfo::Protocol protocol;
+ switch (request->proxy_server().scheme()) {
+ case net::ProxyServer::SCHEME_HTTP:
+ protocol = DataReductionProxyData::RequestInfo::Protocol::HTTP;
+ break;
+ case net::ProxyServer::SCHEME_HTTPS:
+ protocol = DataReductionProxyData::RequestInfo::Protocol::HTTPS;
+ break;
+ case net::ProxyServer::SCHEME_QUIC:
+ protocol = DataReductionProxyData::RequestInfo::Protocol::QUIC;
+ break;
+ default:
+ protocol = DataReductionProxyData::RequestInfo::Protocol::UNKNOWN;
+ break;
+ }
+ return std::make_unique<DataReductionProxyData::RequestInfo>(
+ protocol, did_bypass_proxy, dns_time, connect_time, http_time);
+ }
+ return nullptr;
+}
+
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h
index a87b465b740..c6ab7978aee 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h
@@ -13,6 +13,7 @@
#include "base/macros.h"
#include "base/optional.h"
#include "base/supports_user_data.h"
+#include "base/time/time.h"
#include "net/base/network_change_notifier.h"
#include "net/nqe/effective_connection_type.h"
#include "url/gurl.h"
@@ -27,6 +28,37 @@ namespace data_reduction_proxy {
// storage vehicles to associate this data with the object that owns it.
class DataReductionProxyData : public base::SupportsUserData::Data {
public:
+ // Holds connection timing data for each network request while loading the
+ // associated resource.
+ struct RequestInfo {
+ enum Protocol { HTTP, HTTPS, QUIC, UNKNOWN };
+
+ RequestInfo(Protocol protocol,
+ bool proxy_fallback,
+ base::TimeDelta dns_time,
+ base::TimeDelta connect_time,
+ base::TimeDelta http_time);
+
+ RequestInfo(const RequestInfo& other);
+
+ const Protocol protocol;
+
+ // If this request caused the proxy to fallback.
+ const bool proxy_bypass;
+
+ // See https://www.w3.org/TR/resource-timing/ for definitions.
+ const base::TimeDelta dns_time;
+ const base::TimeDelta connect_time;
+ const base::TimeDelta http_time;
+
+ // Used for testing.
+ bool operator==(const RequestInfo& other) const {
+ return protocol == other.protocol && proxy_bypass == other.proxy_bypass &&
+ dns_time == other.dns_time && connect_time == other.connect_time &&
+ http_time == other.http_time;
+ }
+ };
+
DataReductionProxyData();
~DataReductionProxyData() override;
@@ -111,6 +143,25 @@ class DataReductionProxyData : public base::SupportsUserData::Data {
const base::Optional<uint64_t>& page_id() const { return page_id_; }
void set_page_id(uint64_t page_id) { page_id_ = page_id; }
+ // Whether the blacklist prevented a preview.
+ bool black_listed() const { return black_listed_; }
+ void set_black_listed(bool black_listed) { black_listed_ = black_listed; }
+
+ // Holds connection timing data for each redirect while loading the associated
+ // resource.
+ std::vector<RequestInfo> request_info() const { return request_info_; }
+ void set_request_info(std::vector<RequestInfo> request_info) {
+ request_info_ = std::move(request_info);
+ }
+ // Adds an additional |RequestInfo| to the end of the list.
+ void add_request_info(const RequestInfo& info) {
+ request_info_.push_back(info);
+ }
+
+ // Passes ownership of |request_info_| to the caller so it can be preserved
+ // when |this| is deleted.
+ std::vector<RequestInfo> TakeRequestInfo();
+
// Removes |this| from |request|.
static void ClearData(net::URLRequest* request);
@@ -122,6 +173,12 @@ class DataReductionProxyData : public base::SupportsUserData::Data {
static DataReductionProxyData* GetDataAndCreateIfNecessary(
net::URLRequest* request);
+ // Given a URLRequest, pull out the necessary timing information and returns a
+ // fully populated |RequestInfo| struct.
+ static std::unique_ptr<RequestInfo> CreateRequestInfoFromRequest(
+ net::URLRequest* request,
+ bool did_bypass_proxy);
+
// Create a brand new instance of DataReductionProxyData that could be used in
// a different thread. Several of deep copies may occur per navigation, so
// this is inexpensive.
@@ -153,6 +210,9 @@ class DataReductionProxyData : public base::SupportsUserData::Data {
// Whether a lite page response was seen for the request or navigation.
bool lofi_received_;
+ // Whether the blacklist prevented a preview.
+ bool black_listed_;
+
// The session key used for this request or navigation.
std::string session_key_;
@@ -172,6 +232,10 @@ class DataReductionProxyData : public base::SupportsUserData::Data {
// data saver session. Only present on main frame requests.
base::Optional<uint64_t> page_id_;
+ // Lists the connection timing data for each network request while loading the
+ // main frame html. Used in PLM pingbacks.
+ std::vector<RequestInfo> request_info_;
+
DISALLOW_ASSIGN(DataReductionProxyData);
};
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_unittest.cc
index 447b117f21b..55633fb21ce 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_unittest.cc
@@ -56,6 +56,12 @@ TEST_F(DataReductionProxyDataTest, BasicSettersAndGetters) {
data->set_lofi_received(false);
EXPECT_FALSE(data->lofi_received());
+ EXPECT_FALSE(data->black_listed());
+ data->set_black_listed(true);
+ EXPECT_TRUE(data->black_listed());
+ data->set_black_listed(false);
+ EXPECT_FALSE(data->black_listed());
+
EXPECT_EQ(std::string(), data->session_key());
std::string session_key = "test-key";
data->set_session_key(session_key);
@@ -72,6 +78,31 @@ TEST_F(DataReductionProxyDataTest, BasicSettersAndGetters) {
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_OFFLINE,
data->effective_connection_type());
+ EXPECT_EQ(net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
+ data->connection_type());
+ data->set_connection_type(net::NetworkChangeNotifier::CONNECTION_WIFI);
+ EXPECT_EQ(net::NetworkChangeNotifier::CONNECTION_WIFI,
+ data->connection_type());
+
+ EXPECT_EQ(std::vector<DataReductionProxyData::RequestInfo>(),
+ data->request_info());
+ DataReductionProxyData::RequestInfo request_info_1(
+ DataReductionProxyData::RequestInfo::Protocol::HTTP, false,
+ base::TimeDelta(), base::TimeDelta(), base::TimeDelta());
+ DataReductionProxyData::RequestInfo request_info_2(
+ DataReductionProxyData::RequestInfo::Protocol::HTTPS, true,
+ base::TimeDelta(), base::TimeDelta(), base::TimeDelta());
+ std::vector<DataReductionProxyData::RequestInfo> test_vector;
+ data->add_request_info(request_info_1);
+ test_vector.push_back(request_info_1);
+ EXPECT_EQ(test_vector, data->request_info());
+ data->add_request_info(request_info_2);
+ test_vector.push_back(request_info_2);
+ EXPECT_EQ(test_vector, data->request_info());
+ data->set_request_info(std::vector<DataReductionProxyData::RequestInfo>());
+ EXPECT_EQ(std::vector<DataReductionProxyData::RequestInfo>(),
+ data->request_info());
+
EXPECT_FALSE(data->page_id());
uint64_t page_id = 1;
data->set_page_id(page_id);
@@ -117,24 +148,35 @@ TEST_F(DataReductionProxyDataTest, DeepCopy) {
for (size_t i = 0; i < arraysize(tests); ++i) {
static const char kSessionKey[] = "test-key";
static const GURL kTestURL("test-url");
+ std::vector<DataReductionProxyData::RequestInfo> request_info;
+ request_info.push_back(DataReductionProxyData::RequestInfo(
+ DataReductionProxyData::RequestInfo::Protocol::HTTP, false,
+ base::TimeDelta(), base::TimeDelta(), base::TimeDelta()));
std::unique_ptr<DataReductionProxyData> data(new DataReductionProxyData());
data->set_used_data_reduction_proxy(tests[i].data_reduction_used);
data->set_lofi_requested(tests[i].lofi_test_value);
data->set_lite_page_received(tests[i].lofi_test_value);
data->set_lofi_received(tests[i].lofi_test_value);
+ data->set_black_listed(tests[i].lofi_test_value);
data->set_session_key(kSessionKey);
data->set_request_url(kTestURL);
data->set_effective_connection_type(net::EFFECTIVE_CONNECTION_TYPE_OFFLINE);
+ data->set_connection_type(net::NetworkChangeNotifier::CONNECTION_WIFI);
+ data->set_request_info(request_info);
data->set_page_id(2u);
std::unique_ptr<DataReductionProxyData> copy = data->DeepCopy();
EXPECT_EQ(tests[i].lofi_test_value, copy->lofi_requested());
EXPECT_EQ(tests[i].lofi_test_value, copy->lite_page_received());
EXPECT_EQ(tests[i].lofi_test_value, copy->lofi_received());
+ EXPECT_EQ(tests[i].lofi_test_value, copy->black_listed());
EXPECT_EQ(tests[i].data_reduction_used, copy->used_data_reduction_proxy());
EXPECT_EQ(kSessionKey, copy->session_key());
EXPECT_EQ(kTestURL, copy->request_url());
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_OFFLINE,
copy->effective_connection_type());
+ EXPECT_EQ(net::NetworkChangeNotifier::CONNECTION_WIFI,
+ copy->connection_type());
+ EXPECT_EQ(request_info, copy->request_info());
EXPECT_EQ(2u, data->page_id().value());
}
}
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer.cc
index 53120a1828e..43fabeb62f4 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer.cc
@@ -12,6 +12,7 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
#include "components/data_reduction_proxy/core/common/lofi_decider.h"
#include "components/data_use_measurement/core/data_use.h"
#include "components/data_use_measurement/core/data_use_ascriber.h"
@@ -43,10 +44,6 @@ class DataUseUserDataBytes : public base::SupportsUserData::Data {
int64_t original_bytes_;
};
-// Hostname used for the other bucket which consists of chrome-services traffic.
-// This should be in sync with the same in DataReductionSiteBreakdownView.java
-const char kOtherHostName[] = "Other";
-
// static
const void* const DataUseUserDataBytes::kUserDataKey =
&DataUseUserDataBytes::kUserDataKey;
@@ -63,7 +60,8 @@ DataReductionProxyDataUseObserver::DataReductionProxyDataUseObserver(
: data_reduction_proxy_io_data_(data_reduction_proxy_io_data),
data_use_ascriber_(data_use_ascriber) {
DCHECK(data_reduction_proxy_io_data_);
- data_use_ascriber_->AddObserver(this);
+ if (!data_reduction_proxy::params::IsDataSaverSiteBreakdownUsingPLMEnabled())
+ data_use_ascriber_->AddObserver(this);
}
DataReductionProxyDataUseObserver::~DataReductionProxyDataUseObserver() {
@@ -131,12 +129,14 @@ void DataReductionProxyDataUseObserver::OnPageResourceLoad(
network_bytes, original_bytes));
}
} else {
+ // Report the datause that cannot be scoped to a page load to the other
+ // host. These include chrome services, service-worker, Downloads, etc.
data_reduction_proxy_io_data_->UpdateDataUseForHost(
network_bytes, original_bytes,
data_use->traffic_type() ==
data_use_measurement::DataUse::TrafficType::USER_TRAFFIC
? data_use->url().HostNoBrackets()
- : kOtherHostName);
+ : util::GetSiteBreakdownOtherHostName());
}
}
@@ -170,7 +170,8 @@ void DataReductionProxyDataUseObserver::OnPageDidFinishLoad(
DCHECK(data_use->url().SchemeIs(url::kHttpsScheme));
data_reduction_proxy_io_data_->UpdateContentLengths(
0, total_inflated_bytes, data_reduction_proxy_io_data_->IsEnabled(),
- HTTPS, std::string());
+ HTTPS, std::string(), true,
+ data_use_measurement::DataUseUserData::OTHER, 0);
// Report for host usage.
data_reduction_proxy_io_data_->UpdateDataUseForHost(
0, total_inflated_bytes, data_use->url().HostNoBrackets());
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer_unittest.cc
index fef4937c31a..37a3c5b4b26 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer_unittest.cc
@@ -104,7 +104,7 @@ TEST_F(DataReductionProxyDataUseObserverTest,
// Verify with no committed preview.
previews::PreviewsUserData previews_user_data(3 /* page_id */);
SetPreviewsUserData(data_use.get(), &previews_user_data);
- EXPECT_CALL(*mock_drp_service(), UpdateContentLengths(_, _, _, _, _))
+ EXPECT_CALL(*mock_drp_service(), UpdateContentLengths(_, _, _, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*mock_drp_service(), UpdateDataUseForHost(_, _, _)).Times(0);
DidFinishLoad(data_use.get());
@@ -114,7 +114,7 @@ TEST_F(DataReductionProxyDataUseObserverTest,
// Now verify with LOFI as committed preview.
previews_user_data.SetCommittedPreviewsType(previews::PreviewsType::LOFI);
SetPreviewsUserData(data_use.get(), &previews_user_data);
- EXPECT_CALL(*mock_drp_service(), UpdateContentLengths(_, _, _, _, _))
+ EXPECT_CALL(*mock_drp_service(), UpdateContentLengths(_, _, _, _, _, _, _, _))
.Times(0);
EXPECT_CALL(*mock_drp_service(), UpdateDataUseForHost(_, _, _)).Times(0);
DidFinishLoad(data_use.get());
@@ -134,7 +134,7 @@ TEST_F(DataReductionProxyDataUseObserverTest,
int inflation_value = 500000 * kInflationPercent / 100 + kInflationBytes;
EXPECT_EQ(251000, inflation_value);
EXPECT_CALL(*mock_drp_service(),
- UpdateContentLengths(0, inflation_value, true, _, _))
+ UpdateContentLengths(0, inflation_value, true, _, _, _, _, _))
.Times(1);
EXPECT_CALL(*mock_drp_service(),
UpdateDataUseForHost(0, inflation_value, "testsite.com"))
@@ -157,7 +157,7 @@ TEST_F(DataReductionProxyDataUseObserverTest,
int inflation_value = 500000 * 80 / 100;
EXPECT_EQ(400000, inflation_value);
EXPECT_CALL(*mock_drp_service(),
- UpdateContentLengths(0, inflation_value, true, _, _))
+ UpdateContentLengths(0, inflation_value, true, _, _, _, _, _))
.Times(1);
EXPECT_CALL(*mock_drp_service(),
UpdateDataUseForHost(0, inflation_value, "testsite.com"))
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
index 21a0b7bafbb..c58414ad9ea 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
@@ -58,14 +58,14 @@ DataReductionProxyDelegate::DataReductionProxyDelegate(
DataReductionProxyDelegate::~DataReductionProxyDelegate() {
DCHECK(thread_checker_.CalledOnValidThread());
- net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
+ net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
}
void DataReductionProxyDelegate::InitializeOnIOThread(
DataReductionProxyIOData* io_data) {
DCHECK(io_data);
DCHECK(thread_checker_.CalledOnValidThread());
- net::NetworkChangeNotifier::AddIPAddressObserver(this);
+ net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
io_data_ = io_data;
}
@@ -238,8 +238,13 @@ void DataReductionProxyDelegate::RecordQuicProxyStatus(
QUIC_PROXY_STATUS_BOUNDARY);
}
-void DataReductionProxyDelegate::OnIPAddressChanged() {
+void DataReductionProxyDelegate::OnNetworkChanged(
+ net::NetworkChangeNotifier::ConnectionType type) {
DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (type == net::NetworkChangeNotifier::CONNECTION_NONE)
+ return;
+
last_network_change_time_ = tick_clock_->NowTicks();
}
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h
index 3f94d38502b..dd575589b03 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h
@@ -34,7 +34,7 @@ class DataReductionProxyIOData;
class DataReductionProxyDelegate
: public net::ProxyDelegate,
- public net::NetworkChangeNotifier::IPAddressObserver {
+ public net::NetworkChangeNotifier::NetworkChangeObserver {
public:
// ProxyDelegate instance is owned by io_thread. |auth_handler| and |config|
// outlives this class instance.
@@ -77,8 +77,9 @@ class DataReductionProxyDelegate
// Records the availability status of data reduction proxy.
void RecordQuicProxyStatus(QuicProxyStatus status) const;
- // NetworkChangeNotifier::IPAddressObserver:
- void OnIPAddressChanged() override;
+ // NetworkChangeNotifier::NetworkChangeObserver:
+ void OnNetworkChanged(
+ net::NetworkChangeNotifier::ConnectionType type) override;
// Checks if the first proxy server in |result| supports QUIC and if so
// adds an alternative proxy configuration to |result|.
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
index 795fde3cd19..967c584adc0 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
@@ -19,7 +19,7 @@
#include "base/numerics/safe_conversions.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_entropy_provider.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/time/time.h"
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc
index cdc095c7e3d..ad7786f7686 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc
@@ -7,6 +7,7 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
@@ -111,6 +112,14 @@ DataReductionProxyInterceptor::MaybeInterceptResponseOrRedirect(
should_retry);
}
+ DataReductionProxyData* data = DataReductionProxyData::GetData(*request);
+ std::unique_ptr<DataReductionProxyData::RequestInfo> request_info =
+ DataReductionProxyData::CreateRequestInfoFromRequest(request,
+ should_retry);
+ if (data && request_info) {
+ data->add_request_info(*request_info.get());
+ }
+
if (!should_retry)
return nullptr;
// Returning non-NULL has the effect of restarting the request with the
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc
index 2ff1a8cac8f..a78df3f4751 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc
@@ -13,7 +13,7 @@
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
index ac85b385e5e..75ba84f2b6d 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
@@ -32,6 +32,7 @@
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
#include "components/previews/core/previews_decider.h"
#include "net/base/load_flags.h"
+#include "net/http/http_request_headers.h"
#include "net/url_request/http_user_agent_settings.h"
#include "net/url_request/static_http_user_agent_settings.h"
#include "net/url_request/url_request_context.h"
@@ -145,6 +146,13 @@ DataReductionProxyIOData::DataReductionProxyIOData(
request_options_.reset(
new DataReductionProxyRequestOptions(client_, config_.get()));
request_options_->Init();
+ // It is safe to use base::Unretained here, since it gets executed
+ // synchronously on the IO thread, and |this| outlives the caller (since the
+ // caller is owned by |this|.
+ request_options_->SetUpdateHeaderCallback(
+ base::BindRepeating(&DataReductionProxyIOData::UpdateProxyRequestHeaders,
+ base::Unretained(this)));
+
if (use_config_client) {
// It is safe to use base::Unretained here, since it gets executed
// synchronously on the IO thread, and |this| outlives the caller (since the
@@ -320,8 +328,7 @@ bool DataReductionProxyIOData::ShouldAcceptServerPreview(
previews::PreviewsDecider* previews_decider) {
DCHECK(previews_decider);
DCHECK((request.load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED) != 0);
- if (!config_ || (config_->IsBypassedByDataReductionProxyLocalRules(
- request, configurator_->GetProxyConfig()))) {
+ if (!config_ || !request.url().SchemeIsHTTPOrHTTPS()) {
return false;
}
return config_->ShouldAcceptServerPreview(request, *previews_decider);
@@ -342,14 +349,18 @@ void DataReductionProxyIOData::UpdateContentLengths(
int64_t original_size,
bool data_reduction_proxy_enabled,
DataReductionProxyRequestType request_type,
- const std::string& mime_type) {
+ const std::string& mime_type,
+ bool is_user_traffic,
+ data_use_measurement::DataUseUserData::DataUseContentType content_type,
+ int32_t service_hash_code) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
ui_task_runner_->PostTask(
FROM_HERE,
- base::Bind(&DataReductionProxyService::UpdateContentLengths, service_,
- data_used, original_size, data_reduction_proxy_enabled,
- request_type, mime_type));
+ base::BindOnce(&DataReductionProxyService::UpdateContentLengths, service_,
+ data_used, original_size, data_reduction_proxy_enabled,
+ request_type, mime_type, is_user_traffic, content_type,
+ service_hash_code));
}
void DataReductionProxyIOData::AddEvent(std::unique_ptr<base::Value> event) {
@@ -441,4 +452,12 @@ void DataReductionProxyIOData::SetPreviewsDecider(
previews_decider_ = previews_decider;
}
+void DataReductionProxyIOData::UpdateProxyRequestHeaders(
+ net::HttpRequestHeaders headers) {
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&DataReductionProxyService::SetProxyRequestHeaders,
+ service_, std::move(headers)));
+}
+
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
index ad60792d4d4..e5d8ba295e1 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
@@ -25,6 +25,7 @@
#include "components/data_reduction_proxy/core/common/lofi_decider.h"
#include "components/data_reduction_proxy/core/common/lofi_ui_service.h"
#include "components/data_reduction_proxy/core/common/resource_type_provider.h"
+#include "components/data_use_measurement/core/data_use_user_data.h"
namespace base {
class Value;
@@ -119,7 +120,10 @@ class DataReductionProxyIOData : public DataReductionProxyEventStorageDelegate {
int64_t original_size,
bool data_reduction_proxy_enabled,
DataReductionProxyRequestType request_type,
- const std::string& mime_type);
+ const std::string& mime_type,
+ bool is_user_traffic,
+ data_use_measurement::DataUseUserData::DataUseContentType content_type,
+ int32_t service_hash_code);
// Overrides of DataReductionProxyEventStorageDelegate. Bridges to the UI
// thread objects.
@@ -146,6 +150,9 @@ class DataReductionProxyIOData : public DataReductionProxyEventStorageDelegate {
// cleared.
void OnCacheCleared(const base::Time start, const base::Time end);
+ // Forwards proxy authentication headers to the UI thread.
+ void UpdateProxyRequestHeaders(net::HttpRequestHeaders headers);
+
// Various accessor methods.
DataReductionProxyConfigurator* configurator() const {
return configurator_.get();
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc
index a8975739d80..4474cbd9d9f 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc
@@ -46,7 +46,7 @@ class CountingNetworkDelegate : public net::NetworkDelegateImpl {
}
int OnBeforeURLRequest(net::URLRequest* request,
- const net::CompletionCallback& callback,
+ net::CompletionOnceCallback callback,
GURL* new_url) final {
created_requests_++;
return net::OK;
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
index da6a0a7eb66..18e7f0a66e2 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
@@ -145,10 +145,6 @@ void RecordContentLengthHistograms(bool is_https,
is_video, request_type,
original_content_length);
- UMA_HISTOGRAM_COUNTS_1M("Net.HttpOriginalContentLength",
- original_content_length);
- UMA_HISTOGRAM_COUNTS_1M("Net.HttpContentLengthDifference",
- original_content_length - received_content_length);
UMA_HISTOGRAM_CUSTOM_COUNTS("Net.HttpContentFreshnessLifetime",
freshness_lifetime.InSeconds(),
base::TimeDelta::FromHours(1).InSeconds(),
@@ -282,23 +278,8 @@ void DataReductionProxyNetworkDelegate::InitIODataAndUMA(
data_reduction_proxy_bypass_stats_ = bypass_stats;
}
-void DataReductionProxyNetworkDelegate::OnBeforeURLRequestInternal(
- net::URLRequest* request,
- const net::CompletionCallback& callback,
- GURL* new_url) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- if (data_reduction_proxy_io_data_ &&
- data_reduction_proxy_io_data_->lofi_decider() &&
- data_reduction_proxy_io_data_->IsEnabled()) {
- data_reduction_proxy_io_data_->lofi_decider()->MaybeApplyAMPPreview(
- request, new_url, data_reduction_proxy_io_data_->previews_decider());
- }
-}
-
void DataReductionProxyNetworkDelegate::OnBeforeStartTransactionInternal(
net::URLRequest* request,
- const net::CompletionCallback& callback,
net::HttpRequestHeaders* headers) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -347,6 +328,12 @@ void DataReductionProxyNetworkDelegate::OnBeforeSendHeadersInternal(
data_reduction_proxy_request_options_->GetSecureSession()) {
page_id = data->page_id();
}
+ // Always persist data's |request_info| since it tracks connection pingback
+ // data for redirects on main frame requests. It should include re-issued
+ // requests and client redirects.
+ std::vector<DataReductionProxyData::RequestInfo> request_info;
+ if (data)
+ request_info = data->TakeRequestInfo();
// Reset |request|'s DataReductionProxyData.
DataReductionProxyData::ClearData(request);
@@ -401,6 +388,7 @@ void DataReductionProxyNetworkDelegate::OnBeforeSendHeadersInternal(
page_id = data_reduction_proxy_request_options_->GeneratePageId();
}
data->set_page_id(page_id.value());
+ data->set_request_info(std::move(request_info));
}
}
@@ -446,6 +434,12 @@ void DataReductionProxyNetworkDelegate::OnBeforeRedirectInternal(
page_id = data->page_id();
}
+ // Persist data's |request_info| since it tracks connection pingback data for
+ // redirects on main frame requests.
+ std::vector<DataReductionProxyData::RequestInfo> request_info;
+ if (data)
+ request_info = data->TakeRequestInfo();
+
DataReductionProxyData::ClearData(request);
if (page_id) {
@@ -453,17 +447,16 @@ void DataReductionProxyNetworkDelegate::OnBeforeRedirectInternal(
data->set_page_id(page_id.value());
data->set_session_key(
data_reduction_proxy_request_options_->GetSecureSession());
+ data->set_request_info(std::move(request_info));
}
}
void DataReductionProxyNetworkDelegate::OnCompletedInternal(
net::URLRequest* request,
- bool started) {
+ bool started,
+ int net_error) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(request);
- // TODO(maksims): remove this once OnCompletedInternal() has net_error in
- // arguments.
- int net_error = request->status().error();
DCHECK_NE(net::ERR_IO_PENDING, net_error);
if (data_reduction_proxy_bypass_stats_)
data_reduction_proxy_bypass_stats_->OnUrlRequestCompleted(request, started,
@@ -516,7 +509,6 @@ void DataReductionProxyNetworkDelegate::OnCompletedInternal(
void DataReductionProxyNetworkDelegate::OnHeadersReceivedInternal(
net::URLRequest* request,
- const net::CompletionCallback& callback,
const net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
GURL* allowed_unsafe_redirect_url) {
@@ -570,21 +562,41 @@ void DataReductionProxyNetworkDelegate::CalculateAndRecordDataUsage(
if (request.response_headers())
request.response_headers()->GetMimeType(&mime_type);
- AccumulateDataUsage(data_used, original_size, request_type, mime_type);
+ AccumulateDataUsage(
+ data_used, original_size, request_type, mime_type,
+ data_use_measurement::DataUseMeasurement::IsUserRequest(request),
+ data_use_measurement::DataUseMeasurement::GetContentTypeForRequest(
+ request),
+ request.traffic_annotation().unique_id_hash_code);
+
+ if (params::IsDataSaverSiteBreakdownUsingPLMEnabled() &&
+ data_reduction_proxy_io_data_ &&
+ data_reduction_proxy_io_data_->resource_type_provider() &&
+ data_reduction_proxy_io_data_->resource_type_provider()
+ ->IsNonContentInitiatedRequest(request)) {
+ // Record non-content initiated traffic to the Other bucket for data saver
+ // site-breakdown.
+ data_reduction_proxy_io_data_->UpdateDataUseForHost(
+ data_used, original_size, util::GetSiteBreakdownOtherHostName());
+ }
}
void DataReductionProxyNetworkDelegate::AccumulateDataUsage(
int64_t data_used,
int64_t original_size,
DataReductionProxyRequestType request_type,
- const std::string& mime_type) {
+ const std::string& mime_type,
+ bool is_user_traffic,
+ data_use_measurement::DataUseUserData::DataUseContentType content_type,
+ int32_t service_hash_code) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_GE(data_used, 0);
DCHECK_GE(original_size, 0);
if (data_reduction_proxy_io_data_) {
data_reduction_proxy_io_data_->UpdateContentLengths(
data_used, original_size, data_reduction_proxy_io_data_->IsEnabled(),
- request_type, mime_type);
+ request_type, mime_type, is_user_traffic, content_type,
+ service_hash_code);
}
}
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h
index e9a1f9deb5b..12489dc22b1 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h
@@ -15,6 +15,7 @@
#include "base/memory/ref_counted.h"
#include "base/threading/thread_checker.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
+#include "components/data_use_measurement/core/data_use_user_data.h"
#include "net/base/completion_callback.h"
#include "net/base/layered_network_delegate.h"
#include "net/proxy_resolution/proxy_retry_info.h"
@@ -77,17 +78,11 @@ class DataReductionProxyNetworkDelegate : public net::LayeredNetworkDelegate {
private:
friend class DataReductionProxyTestContext;
- // Resets if Lo-Fi has been used for the last main frame load to false.
- void OnBeforeURLRequestInternal(net::URLRequest* request,
- const net::CompletionCallback& callback,
- GURL* new_url) override;
-
// Called before an HTTP transaction is started. Allows the delegate to
// modify the Chrome-Proxy-Accept-Transform header to convey acceptable
// content transformations.
void OnBeforeStartTransactionInternal(
net::URLRequest* request,
- const net::CompletionCallback& callback,
net::HttpRequestHeaders* headers) override;
// Called after connection. Allows the delegate to read/write
@@ -108,13 +103,13 @@ class DataReductionProxyNetworkDelegate : public net::LayeredNetworkDelegate {
// |started| indicates whether the request has been started. If false,
// some information like the socket address is not available.
void OnCompletedInternal(net::URLRequest* request,
- bool started) override;
+ bool started,
+ int net_error) override;
// Checks if a LoFi or Lite Pages response was received and sets the state on
// DataReductionProxyData for |request|.
void OnHeadersReceivedInternal(
net::URLRequest* request,
- const net::CompletionCallback& callback,
const net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
GURL* allowed_unsafe_redirect_url) override;
@@ -128,10 +123,14 @@ class DataReductionProxyNetworkDelegate : public net::LayeredNetworkDelegate {
// Posts to the UI thread to UpdateContentLengthPrefs in the data reduction
// proxy metrics and updates |received_content_length_| and
// |original_content_length_|.
- void AccumulateDataUsage(int64_t data_used,
- int64_t original_size,
- DataReductionProxyRequestType request_type,
- const std::string& mime_type);
+ void AccumulateDataUsage(
+ int64_t data_used,
+ int64_t original_size,
+ DataReductionProxyRequestType request_type,
+ const std::string& mime_type,
+ bool is_user_traffic,
+ data_use_measurement::DataUseUserData::DataUseContentType content_type,
+ int32_t service_hash_code);
// Record information such as histograms related to the Content-Length of
// |request|. |original_content_length| is the length of the resource if
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
index 1c30339f2f6..607070800fb 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
@@ -25,7 +25,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_entropy_provider.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
@@ -36,6 +36,7 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.h"
@@ -45,6 +46,7 @@
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
#include "components/data_reduction_proxy/core/common/lofi_decider.h"
#include "components/data_reduction_proxy/proto/client_config.pb.h"
+#include "components/data_reduction_proxy/proto/data_store.pb.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_features.h"
#include "components/previews/core/test_previews_decider.h"
@@ -158,8 +160,6 @@ const char kReceivedVideoSecureBypassedHistogramName[] =
"Net.HttpContentLengthV2.Https.BypassedDRP.Video";
const char kReceivedVideoSecureOtherHistogramName[] =
"Net.HttpContentLengthV2.Https.Other.Video";
-const char kOriginalHistogramName[] = "Net.HttpOriginalContentLength";
-const char kDifferenceHistogramName[] = "Net.HttpContentLengthDifference";
const char kFreshnessLifetimeHistogramName[] =
"Net.HttpContentFreshnessLifetime";
const int64_t kResponseContentLength = 100;
@@ -240,13 +240,6 @@ class TestLoFiDecider : public LoFiDecider {
return false;
}
- void MaybeApplyAMPPreview(
- net::URLRequest* request,
- GURL* new_url,
- previews::PreviewsDecider* previews_decider) const override {
- return;
- }
-
void RemoveAcceptTransformHeader(
net::HttpRequestHeaders* headers) const override {
if (ignore_is_using_data_reduction_proxy_check_)
@@ -295,6 +288,28 @@ class TestLoFiUIService : public LoFiUIService {
bool on_lofi_response_;
};
+class TestResourceTypeProvider : public ResourceTypeProvider {
+ public:
+ void SetContentType(const net::URLRequest& request) override {}
+
+ ContentType GetContentType(const GURL& url) const override {
+ return ResourceTypeProvider::CONTENT_TYPE_UNKNOWN;
+ }
+
+ bool IsNonContentInitiatedRequest(
+ const net::URLRequest& request) const override {
+ return is_non_content_initiated_request_;
+ }
+
+ void set_is_non_content_initiated_request(
+ bool is_non_content_initiated_request) {
+ is_non_content_initiated_request_ = is_non_content_initiated_request;
+ }
+
+ private:
+ bool is_non_content_initiated_request_ = false;
+};
+
enum ProxyTestConfig { USE_SECURE_PROXY, USE_INSECURE_PROXY, BYPASS_PROXY };
class DataReductionProxyNetworkDelegateTest : public testing::Test {
@@ -790,6 +805,23 @@ class DataReductionProxyNetworkDelegateTest : public testing::Test {
request, data_reduction_proxy_info, proxy_retry_info, headers);
}
+ void EnableDataUsageReporting() {
+ test_context_->pref_service()->SetBoolean(prefs::kDataUsageReportingEnabled,
+ true);
+ // Give the setting notification a chance to propagate.
+ base::RunLoop().RunUntilIdle();
+ }
+
+ size_t GetOtherHostDataUsage() {
+ const auto& data_usage_map = test_context_->data_reduction_proxy_service()
+ ->compression_stats()
+ ->DataUsageMapForTesting();
+ const auto& it = data_usage_map.find(util::GetSiteBreakdownOtherHostName());
+ if (it != data_usage_map.end())
+ return it->second->data_used();
+ return 0;
+ }
+
net::MockClientSocketFactory* mock_socket_factory() {
return mock_socket_factory_.get();
}
@@ -1165,6 +1197,11 @@ TEST_F(DataReductionProxyNetworkDelegateTest, RedirectRequestDataCleared) {
request.get(), data_reduction_proxy_info, proxy_retry_info,
&headers_original);
DataReductionProxyData* data = DataReductionProxyData::GetData(*request);
+ uint64_t original_page_id = data->page_id().value();
+ // Artificially add a RequestInfo for sake of testing that it is persisted.
+ data->add_request_info(DataReductionProxyData::RequestInfo(
+ DataReductionProxyData::RequestInfo::Protocol::HTTP, false,
+ base::TimeDelta(), base::TimeDelta(), base::TimeDelta()));
EXPECT_TRUE(data);
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_OFFLINE,
@@ -1181,6 +1218,9 @@ TEST_F(DataReductionProxyNetworkDelegateTest, RedirectRequestDataCleared) {
network_delegate()->NotifyBeforeRedirect(request.get(), GURL(kTestURL));
data = DataReductionProxyData::GetData(*request);
EXPECT_FALSE(data && data->used_data_reduction_proxy());
+ // page_id and request_info should be persisted across redirects.
+ EXPECT_EQ(original_page_id, data->page_id().value());
+ EXPECT_EQ(1u, data->request_info().size());
// Call NotifyBeforeSendHeaders again with different proxy info to check that
// new data isn't added. Use a new set of headers since the redirected HTTP
@@ -1244,11 +1284,6 @@ TEST_F(DataReductionProxyNetworkDelegateTest, NetHistograms) {
0);
histogram_tester.ExpectUniqueSample(kReceivedInsecureViaDRPHistogramName,
kResponseContentLength, 1);
- histogram_tester.ExpectUniqueSample(kOriginalHistogramName,
- kOriginalContentLength, 1);
- histogram_tester.ExpectUniqueSample(
- kDifferenceHistogramName,
- kOriginalContentLength - kResponseContentLength, 1);
histogram_tester.ExpectUniqueSample(kFreshnessLifetimeHistogramName,
freshness_lifetime.InSeconds(), 1);
}
@@ -2247,6 +2282,32 @@ TEST_F(DataReductionProxyNetworkDelegateTest, TestAcceptTransformHistogram) {
9 /* UNKNOWN_TRANSFORM_RECEIVED */, 1);
}
+TEST_F(DataReductionProxyNetworkDelegateTest, RecordNonContentToOtherHost) {
+ const std::string response_headers =
+ "HTTP/1.1 200 OK\r\n"
+ "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n"
+ "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n"
+ "Via: 1.1 Chrome-Compression-Proxy\r\n"
+ "\r\n";
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ data_reduction_proxy::features::
+ kDataSaverSiteBreakdownUsingPageLoadMetrics);
+ Init(USE_INSECURE_PROXY, false);
+ EnableDataUsageReporting();
+ auto test_resource_type_provider =
+ std::make_unique<TestResourceTypeProvider>();
+ test_resource_type_provider->set_is_non_content_initiated_request(true);
+ io_data()->set_resource_type_provider(std::move(test_resource_type_provider));
+
+ FetchURLRequest(GURL(kTestURL), nullptr, response_headers,
+ kResponseContentLength, 0);
+ base::RunLoop().RunUntilIdle();
+ DCHECK_EQ(response_headers.size() + kResponseContentLength,
+ GetOtherHostDataUsage());
+}
+
} // namespace
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.cc
index d730066b74b..4d9afb991a4 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.cc
@@ -91,6 +91,22 @@ void RegisterSyncableProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterInt64Pref(prefs::kDataReductionProxyLastConfigRetrievalTime,
0L);
registry->RegisterDictionaryPref(prefs::kNetworkProperties);
+
+ registry->RegisterIntegerPref(prefs::kThisWeekNumber, false);
+ registry->RegisterDictionaryPref(
+ prefs::kThisWeekServicesDownstreamBackgroundKB, PrefRegistry::LOSSY_PREF);
+ registry->RegisterDictionaryPref(
+ prefs::kThisWeekServicesDownstreamForegroundKB, PrefRegistry::LOSSY_PREF);
+ registry->RegisterDictionaryPref(
+ prefs::kLastWeekServicesDownstreamBackgroundKB, PrefRegistry::LOSSY_PREF);
+ registry->RegisterDictionaryPref(
+ prefs::kLastWeekServicesDownstreamForegroundKB, PrefRegistry::LOSSY_PREF);
+ registry->RegisterDictionaryPref(
+ prefs::kThisWeekUserTrafficContentTypeDownstreamKB,
+ PrefRegistry::LOSSY_PREF);
+ registry->RegisterDictionaryPref(
+ prefs::kLastWeekUserTrafficContentTypeDownstreamKB,
+ PrefRegistry::LOSSY_PREF);
}
void RegisterSimpleProfilePrefs(PrefRegistrySimple* registry) {
@@ -172,6 +188,22 @@ void RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterInt64Pref(prefs::kDataReductionProxyLastConfigRetrievalTime,
0L);
registry->RegisterDictionaryPref(prefs::kNetworkProperties);
+
+ registry->RegisterIntegerPref(prefs::kThisWeekNumber, false);
+ registry->RegisterDictionaryPref(
+ prefs::kThisWeekServicesDownstreamBackgroundKB, PrefRegistry::LOSSY_PREF);
+ registry->RegisterDictionaryPref(
+ prefs::kThisWeekServicesDownstreamForegroundKB, PrefRegistry::LOSSY_PREF);
+ registry->RegisterDictionaryPref(
+ prefs::kLastWeekServicesDownstreamBackgroundKB, PrefRegistry::LOSSY_PREF);
+ registry->RegisterDictionaryPref(
+ prefs::kLastWeekServicesDownstreamForegroundKB, PrefRegistry::LOSSY_PREF);
+ registry->RegisterDictionaryPref(
+ prefs::kThisWeekUserTrafficContentTypeDownstreamKB,
+ PrefRegistry::LOSSY_PREF);
+ registry->RegisterDictionaryPref(
+ prefs::kLastWeekUserTrafficContentTypeDownstreamKB,
+ PrefRegistry::LOSSY_PREF);
}
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
index fc20bdcdcd4..ea806682f9c 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
@@ -297,6 +297,12 @@ void DataReductionProxyRequestOptions::RegenerateRequestHeaderValue() {
headers.push_back(FormatOption(kExperimentsOption, experiment));
header_value_ = base::JoinString(headers, ", ");
+
+ if (update_header_callback_) {
+ net::HttpRequestHeaders headers;
+ headers.SetHeader(chrome_proxy_header(), header_value_);
+ update_header_callback_.Run(std::move(headers));
+ }
}
std::string DataReductionProxyRequestOptions::GetSessionKeyFromRequestHeaders(
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
index e0b356166bf..5f54832607f 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
@@ -11,6 +11,7 @@
#include <string>
#include <vector>
+#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/optional.h"
@@ -19,6 +20,7 @@
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h"
+#include "net/http/http_request_headers.h"
namespace net {
class HttpRequestHeaders;
@@ -40,6 +42,9 @@ extern const char kAndroidWebViewProtocolVersion[];
class DataReductionProxyConfig;
+typedef base::RepeatingCallback<void(net::HttpRequestHeaders)>
+ UpdateHeaderCallback;
+
class DataReductionProxyRequestOptions {
public:
static bool IsKeySetOnCommandLine();
@@ -74,6 +79,11 @@ class DataReductionProxyRequestOptions {
// Sets the credentials for sending to the Data Reduction Proxy.
void SetSecureSession(const std::string& secure_session);
+ // Set the callback to call when the proxy request headers are updated.
+ void SetUpdateHeaderCallback(UpdateHeaderCallback callback) {
+ update_header_callback_ = callback;
+ }
+
// Retrieves the credentials for sending to the Data Reduction Proxy.
const std::string& GetSecureSession() const;
@@ -166,6 +176,10 @@ class DataReductionProxyRequestOptions {
// The page identifier that was last generated for data saver proxy server.
uint64_t current_page_id_;
+ // Callback to expose the chrome_proxy header to the UI thread. Called
+ // whenever the chrome_proxy header value changes. Can be null.
+ UpdateHeaderCallback update_header_callback_;
+
// Enforce usage on the IO thread.
base::ThreadChecker thread_checker_;
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
index dcf0efb4ff7..9b786ddc20a 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
@@ -9,11 +9,13 @@
#include <string>
#include <vector>
+#include "base/callback.h"
#include "base/command_line.h"
#include "base/md5.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/field_trial.h"
#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "build/build_config.h"
@@ -153,6 +155,17 @@ class DataReductionProxyRequestOptionsTest : public testing::Test {
request_options_->Init();
}
+ void CreateRequestOptionsWithCallback(const std::string& version) {
+ CreateRequestOptions(version);
+ request_options_->SetUpdateHeaderCallback(base::BindRepeating(
+ &DataReductionProxyRequestOptionsTest::UpdateHeaderCallback,
+ base::Unretained(this)));
+ }
+
+ void UpdateHeaderCallback(net::HttpRequestHeaders headers) {
+ callback_headers_ = headers;
+ }
+
TestDataReductionProxyParams* params() {
return test_context_->config()->test_params();
}
@@ -161,6 +174,8 @@ class DataReductionProxyRequestOptionsTest : public testing::Test {
return request_options_.get();
}
+ net::HttpRequestHeaders callback_headers() { return callback_headers_; }
+
void VerifyExpectedHeader(const std::string& expected_header,
uint64_t page_id) {
test_context_->RunUntilIdle();
@@ -179,6 +194,7 @@ class DataReductionProxyRequestOptionsTest : public testing::Test {
base::MessageLoopForIO message_loop_;
std::unique_ptr<TestDataReductionProxyRequestOptions> request_options_;
std::unique_ptr<DataReductionProxyTestContext> test_context_;
+ net::HttpRequestHeaders callback_headers_;
};
TEST_F(DataReductionProxyRequestOptionsTest, AuthHashForSalt) {
@@ -245,6 +261,25 @@ TEST_F(DataReductionProxyRequestOptionsTest, SecureSession) {
VerifyExpectedHeader(expected_header, kPageIdValue);
}
+TEST_F(DataReductionProxyRequestOptionsTest, CallsHeaderCallback) {
+ std::string expected_header;
+ SetHeaderExpectations(std::string(), std::string(), kSecureSession,
+ kClientStr, kExpectedBuild, kExpectedPatch, kPageId,
+ std::vector<std::string>(), &expected_header);
+
+ CreateRequestOptionsWithCallback(kVersion);
+ request_options()->SetSecureSession(kSecureSession);
+ VerifyExpectedHeader(expected_header, kPageIdValue);
+
+ std::string callback_header;
+ callback_headers().GetHeader(kChromeProxyHeader, &callback_header);
+ // |callback_header| does not include a page id. Since the page id is always
+ // the last element in the header, check that |callback_header| is the prefix
+ // of |expected_header|.
+ EXPECT_TRUE(base::StartsWith(expected_header, callback_header,
+ base::CompareCase::SENSITIVE));
+}
+
TEST_F(DataReductionProxyRequestOptionsTest, ParseExperiments) {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
data_reduction_proxy::switches::kDataReductionProxyExperiment,
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
index 3f140df6ba3..f94177e0ddc 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
@@ -132,12 +132,15 @@ void DataReductionProxyService::UpdateContentLengths(
int64_t original_size,
bool data_reduction_proxy_enabled,
DataReductionProxyRequestType request_type,
- const std::string& mime_type) {
+ const std::string& mime_type,
+ bool is_user_traffic,
+ data_use_measurement::DataUseUserData::DataUseContentType content_type,
+ int32_t service_hash_code) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (compression_stats_) {
- compression_stats_->RecordDataUseWithMimeType(data_used, original_size,
- data_reduction_proxy_enabled,
- request_type, mime_type);
+ compression_stats_->RecordDataUseWithMimeType(
+ data_used, original_size, data_reduction_proxy_enabled, request_type,
+ mime_type, is_user_traffic, content_type, service_hash_code);
}
}
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
index c93dab6e9c9..ae7040804ae 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
@@ -20,6 +20,8 @@
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
#include "components/data_reduction_proxy/core/browser/db_data_owner.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h"
+#include "components/data_use_measurement/core/data_use_user_data.h"
+#include "net/http/http_request_headers.h"
class PrefService;
@@ -84,11 +86,15 @@ class DataReductionProxyService
// Records daily data savings statistics in |compression_stats_|.
// Virtual for testing.
- virtual void UpdateContentLengths(int64_t data_used,
- int64_t original_size,
- bool data_reduction_proxy_enabled,
- DataReductionProxyRequestType request_type,
- const std::string& mime_type);
+ virtual void UpdateContentLengths(
+ int64_t data_used,
+ int64_t original_size,
+ bool data_reduction_proxy_enabled,
+ DataReductionProxyRequestType request_type,
+ const std::string& mime_type,
+ bool is_user_traffic,
+ data_use_measurement::DataUseUserData::DataUseContentType content_type,
+ int32_t service_hash_code);
// Overrides of DataReductionProxyEventStorageDelegate.
void AddEvent(std::unique_ptr<base::Value> event) override;
@@ -132,6 +138,17 @@ class DataReductionProxyService
// cleared.
void OnCacheCleared(const base::Time start, const base::Time end);
+ // Sets |proxy_request_headers_| with a forwarded value from the IO thread.
+ void SetProxyRequestHeaders(net::HttpRequestHeaders headers) {
+ proxy_request_headers_ = headers;
+ }
+
+ // Returns |proxy_request_headers_|. Note: The chrome-proxy header does not
+ // include the page id.
+ const net::HttpRequestHeaders GetProxyRequestHeaders() const {
+ return proxy_request_headers_;
+ }
+
// Accessor methods.
DataReductionProxyCompressionStats* compression_stats() const {
return compression_stats_.get();
@@ -186,6 +203,10 @@ class DataReductionProxyService
base::ObserverList<DataReductionProxyServiceObserver> observer_list_;
+ // Authentication headers for the Data Reduction Proxy, if any. This is
+ // forwarded from the IO thread in PostTask.
+ net::HttpRequestHeaders proxy_request_headers_;
+
bool initialized_;
SEQUENCE_CHECKER(sequence_checker_);
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
index 6cb54bcc0c9..8beb52406da 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
@@ -15,7 +15,7 @@
#include "base/message_loop/message_loop.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_samples.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_entropy_provider.h"
#include "base/test/simple_test_clock.h"
#include "base/time/clock.h"
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
index 4c02b03a040..96eac346eb5 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
@@ -109,7 +109,7 @@ class TestDataReductionProxyConfigServiceClient
~TestDataReductionProxyConfigServiceClient() override;
- using DataReductionProxyConfigServiceClient::OnIPAddressChanged;
+ using DataReductionProxyConfigServiceClient::OnNetworkChanged;
void SetNow(const base::Time& time);
@@ -188,13 +188,17 @@ class MockDataReductionProxyService : public DataReductionProxyService {
~MockDataReductionProxyService() override;
MOCK_METHOD2(SetProxyPrefs, void(bool enabled, bool at_startup));
- MOCK_METHOD5(
+ MOCK_METHOD8(
UpdateContentLengths,
void(int64_t data_used,
int64_t original_size,
bool data_reduction_proxy_enabled,
data_reduction_proxy::DataReductionProxyRequestType request_type,
- const std::string& mime_type));
+ const std::string& mime_type,
+ bool is_user_traffic,
+ data_use_measurement::DataUseUserData::DataUseContentType
+ content_type,
+ int32_t service_hash_code));
MOCK_METHOD3(UpdateDataUseForHost,
void(int64_t network_bytes,
int64_t original_bytes,
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc
index 77dfa992c73..2deca8cdbc9 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc
@@ -35,6 +35,10 @@ namespace {
const char kApiKeyName[] = "key";
#endif
+// Hostname used for the other bucket which consists of chrome-services traffic.
+// This should be in sync with the same in DataReductionSiteBreakdownView.java
+const char kOtherHostName[] = "Other";
+
// Scales |byte_count| by the ratio of |numerator|:|denomenator|.
int64_t ScaleByteCountByRatio(int64_t byte_count,
int64_t numerator,
@@ -162,7 +166,7 @@ GURL AddApiKeyToUrl(const GURL& url) {
GURL new_url = url;
#if defined(USE_GOOGLE_API_KEYS)
std::string api_key = google_apis::GetAPIKey();
- if (google_apis::HasKeysConfigured() && !api_key.empty()) {
+ if (google_apis::HasAPIKeyConfigured() && !api_key.empty()) {
new_url = net::AppendOrReplaceQueryParameter(url, kApiKeyName, api_key);
}
#endif
@@ -284,6 +288,10 @@ ProxyScheme ConvertNetProxySchemeToProxyScheme(
}
}
+const char* GetSiteBreakdownOtherHostName() {
+ return kOtherHostName;
+}
+
} // namespace util
namespace protobuf_parser {
@@ -332,6 +340,20 @@ PageloadMetrics_ConnectionType ProtoConnectionTypeFromConnectionType(
}
}
+RequestInfo_Protocol ProtoRequestInfoProtocolFromRequestInfoProtocol(
+ DataReductionProxyData::RequestInfo::Protocol protocol) {
+ switch (protocol) {
+ case DataReductionProxyData::RequestInfo::Protocol::HTTP:
+ return RequestInfo_Protocol_HTTP;
+ case DataReductionProxyData::RequestInfo::Protocol::HTTPS:
+ return RequestInfo_Protocol_HTTPS;
+ case DataReductionProxyData::RequestInfo::Protocol::QUIC:
+ return RequestInfo_Protocol_QUIC;
+ case DataReductionProxyData::RequestInfo::Protocol::UNKNOWN:
+ return RequestInfo_Protocol_UNKNOWN;
+ }
+}
+
net::ProxyServer::Scheme SchemeFromProxyScheme(
ProxyServer_ProxyScheme proxy_scheme) {
switch (proxy_scheme) {
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h
index 8245d91cebe..e8b534e8c28 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h
+++ b/chromium/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h
@@ -8,6 +8,7 @@
#include <memory>
#include <string>
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
#include "components/data_reduction_proxy/proto/client_config.pb.h"
#include "components/data_reduction_proxy/proto/pageload_metrics.pb.h"
#include "net/base/network_change_notifier.h"
@@ -121,6 +122,10 @@ int64_t EstimateOriginalReceivedBytes(const net::URLRequest& request,
// Converts net::ProxyServer::Scheme to type ProxyScheme.
ProxyScheme ConvertNetProxySchemeToProxyScheme(net::ProxyServer::Scheme scheme);
+// Returns the hostname used for the other bucket to record datause not scoped
+// to a page load such as chrome-services traffic, service worker, Downloads.
+const char* GetSiteBreakdownOtherHostName();
+
} // namespace util
namespace protobuf_parser {
@@ -140,6 +145,10 @@ ProtoEffectiveConnectionTypeFromEffectiveConnectionType(
PageloadMetrics_ConnectionType ProtoConnectionTypeFromConnectionType(
net::NetworkChangeNotifier::ConnectionType connection_type);
+// Returns the RequestInfo_Protocol equivalent of |protocol|.
+RequestInfo_Protocol ProtoRequestInfoProtocolFromRequestInfoProtocol(
+ DataReductionProxyData::RequestInfo::Protocol protocol);
+
// Returns the |net::ProxyServer::Scheme| for a ProxyServer_ProxyScheme.
net::ProxyServer::Scheme SchemeFromProxyScheme(
ProxyServer_ProxyScheme proxy_scheme);
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_store_impl.cc b/chromium/components/data_reduction_proxy/core/browser/data_store_impl.cc
index 2ed201ea84c..043ed02b896 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_store_impl.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_store_impl.cc
@@ -12,6 +12,7 @@
#include "base/metrics/histogram_macros.h"
#include "components/data_reduction_proxy/proto/data_store.pb.h"
#include "third_party/leveldatabase/env_chromium.h"
+#include "third_party/leveldatabase/leveldb_chrome.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#include "third_party/leveldatabase/src/include/leveldb/options.h"
#include "third_party/leveldatabase/src/include/leveldb/status.h"
@@ -146,8 +147,11 @@ DataStore::Status DataStoreImpl::OpenDB() {
DataStore::Status DataStoreImpl::RecreateDB() {
DCHECK(sequence_checker_.CalledOnValidSequence());
- db_.reset(nullptr);
- base::DeleteFile(profile_path_.Append(kDBName), true);
+ db_.reset();
+ const base::FilePath db_path = profile_path_.Append(kDBName);
+ leveldb::Status s = leveldb_chrome::DeleteDB(db_path, leveldb::Options());
+ if (!s.ok())
+ return LevelDbToDRPStoreStatus(s);
return OpenDB();
}
diff --git a/chromium/components/data_reduction_proxy/core/browser/data_usage_store_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/data_usage_store_unittest.cc
index ecc7e6afd41..d47a96c2f4a 100644
--- a/chromium/components/data_reduction_proxy/core/browser/data_usage_store_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/data_usage_store_unittest.cc
@@ -11,7 +11,7 @@
#include <string>
#include "base/strings/stringprintf.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
#include "components/data_reduction_proxy/core/browser/data_store.h"
diff --git a/chromium/components/data_reduction_proxy/core/browser/network_properties_manager_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/network_properties_manager_unittest.cc
index 8194b3e509a..69ab293b279 100644
--- a/chromium/components/data_reduction_proxy/core/browser/network_properties_manager_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/network_properties_manager_unittest.cc
@@ -10,7 +10,7 @@
#include "base/message_loop/message_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/threading/thread_task_runner_handle.h"
diff --git a/chromium/components/data_reduction_proxy/core/browser/warmup_url_fetcher.cc b/chromium/components/data_reduction_proxy/core/browser/warmup_url_fetcher.cc
index 02cd3d47d89..670bb016d8a 100644
--- a/chromium/components/data_reduction_proxy/core/browser/warmup_url_fetcher.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/warmup_url_fetcher.cc
@@ -213,7 +213,7 @@ base::TimeDelta WarmupURLFetcher::GetFetchTimeout() const {
const base::TimeDelta min_timeout =
base::TimeDelta::FromSeconds(GetFieldTrialParamByFeatureAsInt(
features::kDataReductionProxyRobustConnection,
- "warmup_url_fetch_min_timeout_seconds", 8));
+ "warmup_url_fetch_min_timeout_seconds", 10));
const base::TimeDelta max_timeout =
base::TimeDelta::FromSeconds(GetFieldTrialParamByFeatureAsInt(
features::kDataReductionProxyRobustConnection,
@@ -226,17 +226,11 @@ base::TimeDelta WarmupURLFetcher::GetFetchTimeout() const {
// has been tried.
size_t http_rtt_multiplier = GetFieldTrialParamByFeatureAsInt(
features::kDataReductionProxyRobustConnection,
- "warmup_url_fetch_init_http_rtt_multiplier", 5);
+ "warmup_url_fetch_init_http_rtt_multiplier", 12);
if (previous_attempt_counts_ == 1) {
- http_rtt_multiplier = GetFieldTrialParamByFeatureAsInt(
- features::kDataReductionProxyRobustConnection,
- "warmup_url_fetch_init_http_rtt_multiplier", 5) *
- 2;
+ http_rtt_multiplier *= 2;
} else if (previous_attempt_counts_ == 2) {
- http_rtt_multiplier = GetFieldTrialParamByFeatureAsInt(
- features::kDataReductionProxyRobustConnection,
- "warmup_url_fetch_init_http_rtt_multiplier", 5) *
- 4;
+ http_rtt_multiplier *= 4;
}
// Sanity checks.
DCHECK_LT(0u, http_rtt_multiplier);
diff --git a/chromium/components/data_reduction_proxy/core/browser/warmup_url_fetcher_unittest.cc b/chromium/components/data_reduction_proxy/core/browser/warmup_url_fetcher_unittest.cc
index 6a891bc1936..9b43650be46 100644
--- a/chromium/components/data_reduction_proxy/core/browser/warmup_url_fetcher_unittest.cc
+++ b/chromium/components/data_reduction_proxy/core/browser/warmup_url_fetcher_unittest.cc
@@ -13,7 +13,7 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/test/gtest_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/platform_thread.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h"
@@ -525,7 +525,7 @@ TEST(WarmupURLFetcherTest, TestSuccessfulFetchWarmupURLWithDelay) {
TEST(WarmupURLFetcherTest, TestFetchTimeoutIncreasing) {
// Must remain in sync with warmup_url_fetcher.cc.
- constexpr base::TimeDelta kMinTimeout = base::TimeDelta::FromSeconds(8);
+ constexpr base::TimeDelta kMinTimeout = base::TimeDelta::FromSeconds(10);
constexpr base::TimeDelta kMaxTimeout = base::TimeDelta::FromSeconds(60);
base::HistogramTester histogram_tester;
@@ -549,20 +549,20 @@ TEST(WarmupURLFetcherTest, TestFetchTimeoutIncreasing) {
base::TimeDelta http_rtt = base::TimeDelta::FromSeconds(2);
estimator.SetStartTimeNullHttpRtt(http_rtt);
- EXPECT_EQ(http_rtt * 5, warmup_url_fetcher.GetFetchTimeout());
+ EXPECT_EQ(http_rtt * 12, warmup_url_fetcher.GetFetchTimeout());
warmup_url_fetcher.FetchWarmupURL(1);
- EXPECT_EQ(http_rtt * 10, warmup_url_fetcher.GetFetchTimeout());
+ EXPECT_EQ(http_rtt * 24, warmup_url_fetcher.GetFetchTimeout());
warmup_url_fetcher.FetchWarmupURL(2);
- EXPECT_EQ(http_rtt * 20, warmup_url_fetcher.GetFetchTimeout());
+ EXPECT_EQ(kMaxTimeout, warmup_url_fetcher.GetFetchTimeout());
http_rtt = base::TimeDelta::FromSeconds(5);
estimator.SetStartTimeNullHttpRtt(http_rtt);
EXPECT_EQ(kMaxTimeout, warmup_url_fetcher.GetFetchTimeout());
warmup_url_fetcher.FetchWarmupURL(0);
- EXPECT_EQ(http_rtt * 5, warmup_url_fetcher.GetFetchTimeout());
+ EXPECT_EQ(http_rtt * 12, warmup_url_fetcher.GetFetchTimeout());
}
TEST(WarmupURLFetcherTest, TestFetchTimeoutIncreasingWithFieldTrial) {
diff --git a/chromium/components/data_reduction_proxy/core/common/BUILD.gn b/chromium/components/data_reduction_proxy/core/common/BUILD.gn
index 97bc64a9cbb..5fa1ee698c9 100644
--- a/chromium/components/data_reduction_proxy/core/common/BUILD.gn
+++ b/chromium/components/data_reduction_proxy/core/common/BUILD.gn
@@ -45,6 +45,7 @@ template("common_tmpl") {
"//components/data_reduction_proxy/proto:data_reduction_proxy_proto",
"//components/variations",
"//google_apis",
+ "//services/network/public/cpp",
]
defines = [ "USE_GOOGLE_API_KEYS" ]
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc
index 3b007a4b722..75e628bd59b 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc
@@ -44,5 +44,11 @@ const base::Feature kDataReductionProxyRobustConnection{
const base::Feature kDogfood{"DataReductionProxyDogfood",
base::FEATURE_DISABLED_BY_DEFAULT};
+// Enables recording of the site-breakdown metrics using the page load metrics
+// harness, and disables the observer for data use ascriber.
+const base::Feature kDataSaverSiteBreakdownUsingPageLoadMetrics{
+ "DataSaverSiteBreakdownUsingPageLoadMetrics",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
} // namespace features
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h
index d9711e1d0f3..668251391ab 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h
@@ -16,6 +16,7 @@ extern const base::Feature kDataReductionProxyLowMemoryDevicePromo;
extern const base::Feature kMissingViaHeaderShortDuration;
extern const base::Feature kDataReductionProxyRobustConnection;
extern const base::Feature kDogfood;
+extern const base::Feature kDataSaverSiteBreakdownUsingPageLoadMetrics;
} // namespace features
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc
index 03bd90a1784..aeb892b85d3 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc
@@ -24,6 +24,7 @@
#include "net/http/http_status_code.h"
#include "net/http/http_util.h"
#include "net/url_request/url_request.h"
+#include "services/network/public/cpp/resource_response.h"
namespace {
@@ -484,4 +485,20 @@ int64_t GetDataReductionProxyOFCL(const net::HttpResponseHeaders* headers) {
return -1;
}
+double EstimateCompressionRatioFromHeaders(
+ const network::ResourceResponseHead* response_head) {
+ if (!response_head->network_accessed || !response_head->headers ||
+ response_head->headers->GetContentLength() <= 0) {
+ return 1.0; // No compression
+ }
+
+ int64_t original_content_length =
+ GetDataReductionProxyOFCL(response_head->headers.get());
+ if (original_content_length > 0) {
+ return static_cast<double>(original_content_length) /
+ static_cast<double>(response_head->headers->GetContentLength());
+ }
+ return 1.0; // No compression
+}
+
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h
index c895cf4366b..c97170e27c7 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h
@@ -21,6 +21,10 @@ namespace net {
class HttpResponseHeaders;
} // namespace net
+namespace network {
+struct ResourceResponseHead;
+} // namespace network
+
namespace data_reduction_proxy {
// Transform directives that may be parsed out of http headers.
@@ -176,9 +180,19 @@ bool ParseHeadersAndSetBypassDuration(const net::HttpResponseHeaders* headers,
base::StringPiece action_prefix,
base::TimeDelta* bypass_duration);
-// Returns the OFCL value in the Chrome-Proxy header. Returns -1 in case of
-// of error or if OFCL does not exist. |headers| must be non-null.
+// Returns the Original-Full-Content-Length(OFCL) value in the Chrome-Proxy
+// header. Returns -1 in case of of error or if OFCL does not exist. |headers|
+// must be non-null.
int64_t GetDataReductionProxyOFCL(const net::HttpResponseHeaders* headers);
+// Returns an estimate of the compression ratio from the Content-Length and
+// Chrome-Proxy Original-Full-Content-Length(OFCL) response headers. These may
+// not be populated for responses which are streamed from the origin which will
+// be treated as a no compression case. Notably, only the response body size is
+// used to compute the ratio, and headers are excluded, since this is only an
+// estimate for response that is beginning to arrive.
+double EstimateCompressionRatioFromHeaders(
+ const network::ResourceResponseHead* response_head);
+
} // namespace data_reduction_proxy
#endif // COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_HEADERS_H_
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
index 574c7117541..a8ed2fefd3a 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
@@ -407,6 +407,12 @@ GURL GetSecureProxyCheckURL() {
return GURL(secure_proxy_check_url);
}
+bool IsDataSaverSiteBreakdownUsingPLMEnabled() {
+ return base::FeatureList::IsEnabled(
+ data_reduction_proxy::features::
+ kDataSaverSiteBreakdownUsingPageLoadMetrics);
+}
+
} // namespace params
DataReductionProxyParams::DataReductionProxyParams() {
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
index a40d20b135e..978b73d9142 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
@@ -147,6 +147,10 @@ const char* GetWarmupCallbackParamName();
// Returns the experiment parameter name to disable missing via header bypasses.
const char* GetMissingViaBypassParamName();
+// Returns if site-breakdown metrics should be recorded using the page load
+// metrics harness.
+bool IsDataSaverSiteBreakdownUsingPLMEnabled();
+
// Returns the experiment parameter name to discard the cached result for canary
// check probe.
const char* GetDiscardCanaryCheckResultParam();
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.cc b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.cc
index 0acc086d85f..8e0f36d9215 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.cc
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.cc
@@ -213,5 +213,27 @@ const char kDataReductionProxyLastConfigRetrievalTime[] =
// map of network IDs and their respective network properties.
const char kNetworkProperties[] = "data_reduction.network_properties";
+// An integer pref that stores the number of the week when the weekly data use
+// prefs were updated.
+const char kThisWeekNumber[] = "data_reduction.this_week_number";
+
+// Dictionary pref that stores the data use of services. The key will be the
+// service hash code, and the value will be the KB that service used.
+const char kThisWeekServicesDownstreamBackgroundKB[] =
+ "data_reduction.this_week_services_downstream_background_kb";
+const char kThisWeekServicesDownstreamForegroundKB[] =
+ "data_reduction.this_week_services_downstream_foreground_kb";
+const char kLastWeekServicesDownstreamBackgroundKB[] =
+ "data_reduction.last_week_services_downstream_background_kb";
+const char kLastWeekServicesDownstreamForegroundKB[] =
+ "data_reduction.last_week_services_downstream_foreground_kb";
+
+// Dictionary pref that stores the content-type of user-initiated traffic. The
+// key will be the content-type, and the value will be the data usage in KB.
+const char kThisWeekUserTrafficContentTypeDownstreamKB[] =
+ "data_reduction.this_week_user_traffic_contenttype_downstream_kb";
+const char kLastWeekUserTrafficContentTypeDownstreamKB[] =
+ "data_reduction.last_week_user_traffic_contenttype_downstream_kb";
+
} // namespace prefs
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h
index 5738c307ca5..aae949e447b 100644
--- a/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h
+++ b/chromium/components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h
@@ -54,6 +54,14 @@ extern const char kHttpReceivedContentLength[];
extern const char kDataReductionProxyLastConfigRetrievalTime[];
extern const char kNetworkProperties[];
+extern const char kThisWeekNumber[];
+extern const char kThisWeekServicesDownstreamBackgroundKB[];
+extern const char kThisWeekServicesDownstreamForegroundKB[];
+extern const char kLastWeekServicesDownstreamBackgroundKB[];
+extern const char kLastWeekServicesDownstreamForegroundKB[];
+extern const char kThisWeekUserTrafficContentTypeDownstreamKB[];
+extern const char kLastWeekUserTrafficContentTypeDownstreamKB[];
+
} // namespace prefs
} // namespace data_reduction_proxy
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
index 9ae5784107f..95c65471263 100644
--- 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
@@ -78,5 +78,11 @@ const char kEnableDataReductionProxySavingsPromo[] =
const char kDisableDataReductionProxyWarmupURLFetch[] =
"disable-data-reduction-proxy-warmup-url-fetch";
+// Uses the encoded ClientConfig instead of fetching one from the config server.
+// This value is always used, regardless of error or expiration. The value
+// should be a base64 encoded binary protobuf.
+const char kDataReductionProxyServerClientConfig[] =
+ "data-reduction-proxy-client-config";
+
} // 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
index e8e008e650e..d06ac70f75d 100644
--- 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
@@ -30,6 +30,7 @@ extern const char kDataReductionProxyServerAlternative7[];
extern const char kDataReductionProxyServerAlternative8[];
extern const char kDataReductionProxyServerAlternative9[];
extern const char kDataReductionProxyServerAlternative10[];
+extern const char kDataReductionProxyServerClientConfig[];
extern const char kEnableDataReductionProxy[];
extern const char kEnableDataReductionProxyBypassWarning[];
extern const char kEnableDataReductionProxyForcePingback[];
diff --git a/chromium/components/data_reduction_proxy/core/common/lofi_decider.h b/chromium/components/data_reduction_proxy/core/common/lofi_decider.h
index a898c0b1de5..ee3e3090ff9 100644
--- a/chromium/components/data_reduction_proxy/core/common/lofi_decider.h
+++ b/chromium/components/data_reduction_proxy/core/common/lofi_decider.h
@@ -7,17 +7,11 @@
#include "base/macros.h"
-class GURL;
-
namespace net {
class HttpRequestHeaders;
class URLRequest;
}
-namespace previews {
-class PreviewsDecider;
-}
-
namespace data_reduction_proxy {
// Interface to determine if a request should be made for a low fidelity version
@@ -61,12 +55,6 @@ class LoFiDecider {
// automatically reloaded because of a decoding error.
virtual bool IsClientLoFiAutoReloadRequest(
const net::URLRequest& request) const = 0;
-
- // Applies the AMP redirection preview by changing the |new_url|.
- virtual void MaybeApplyAMPPreview(
- net::URLRequest* request,
- GURL* new_url,
- previews::PreviewsDecider* previews_decider) const = 0;
};
} // namespace data_reduction_proxy
diff --git a/chromium/components/data_reduction_proxy/core/common/resource_type_provider.h b/chromium/components/data_reduction_proxy/core/common/resource_type_provider.h
index 60ec2faa409..a507b7ccd90 100644
--- a/chromium/components/data_reduction_proxy/core/common/resource_type_provider.h
+++ b/chromium/components/data_reduction_proxy/core/common/resource_type_provider.h
@@ -32,6 +32,12 @@ class ResourceTypeProvider {
virtual ContentType GetContentType(const GURL& url) const = 0;
+ // Returns if the request is initiated via non-content that cannot be scoped
+ // to a page load. These ResourceInfo-less requests can be issued by chrome
+ // services, service-worker, Downloads, etc.
+ virtual bool IsNonContentInitiatedRequest(
+ const net::URLRequest& request) const = 0;
+
protected:
ResourceTypeProvider() {}
diff --git a/chromium/components/data_reduction_proxy/proto/client_config.proto b/chromium/components/data_reduction_proxy/proto/client_config.proto
index 4eb11ff5a3d..a3127e4fff0 100644
--- a/chromium/components/data_reduction_proxy/proto/client_config.proto
+++ b/chromium/components/data_reduction_proxy/proto/client_config.proto
@@ -27,6 +27,9 @@ message ClientConfig {
optional Duration refresh_duration = 4;
// Configuration information for reporting pageload metrics.
optional PageloadMetricsConfig pageload_metrics_config = 5;
+ // Prevents the host base and user base blacklisting behaviors for lite pages
+ // and server LoFi.
+ optional bool ignore_long_term_black_list_rules = 7;
}
// The configuration for reporting pageload metrics.
@@ -169,4 +172,4 @@ message VersionInfo {
// The production channel, e.g. "canary", "dev", "beta", "stable".
optional string channel = 4;
-} \ No newline at end of file
+}
diff --git a/chromium/components/data_reduction_proxy/proto/pageload_metrics.proto b/chromium/components/data_reduction_proxy/proto/pageload_metrics.proto
index 9fb91cd5026..37e5bf71877 100644
--- a/chromium/components/data_reduction_proxy/proto/pageload_metrics.proto
+++ b/chromium/components/data_reduction_proxy/proto/pageload_metrics.proto
@@ -27,6 +27,33 @@ message RecordPageloadMetricsRequest {
optional PageloadDeviceInfo device_info = 3;
}
+// Information about each network transaction used to load the main frame HTML.
+message RequestInfo {
+ enum Protocol {
+ UNKNOWN = 0;
+ HTTP = 1;
+ HTTPS = 2;
+ QUIC = 3;
+ };
+
+ // The protocol used to connect to the Data Saver proxy.
+ optional Protocol protocol = 1;
+
+ // True if this request received a proxy bypass.
+ optional bool proxy_bypass = 2;
+
+ // Time to resolve DNS. e.g., to resolve proxy.googlezip.net.
+ optional Duration dns_time = 3;
+
+ // For TCP protocols, this is TCP connect + TLS setup.
+ // For 1RTT QUIC, this is the QUIC connection time (which includes TLS).
+ // For 0RTT QUIC (or TLS1.3 with fast-open), this will be zero.
+ optional Duration connect_time = 4;
+
+ // Time between sending the HTTP request and receiving the response.
+ optional Duration http_time = 5;
+}
+
// Metrics for a single pageload.
message PageloadMetrics {
// Next ID: 22
@@ -69,12 +96,17 @@ message PageloadMetrics {
// The various server previews that can be shown.
enum PreviewsType {
- // No server preview was applied.
+ // No server preview was applied, but the URL/navigation was not
+ // blacklisted.
NONE = 0;
// Image placeholders were used on the page.
LOFI = 1;
// The main resource was a lite page.
LITE_PAGE = 2;
+ // Blacklisting rules caused no server preview to be requested. It's
+ // possible in the future that client previews might be shown when server
+ // previews are blacklisted, currently this is not possible.
+ CLIENT_BLACKLIST_PREVENTED_PREVIEW = 3;
}
// What type of crash occured on the page.
@@ -171,4 +203,11 @@ message PageloadMetrics {
// The queuing delay for the first user input on the page.
optional Duration first_input_delay = 25;
+
+ // Connection timing information for each network transaction to load the main
+ // frame HTML. If the main frame HTML was reached after N redirects, then this
+ // array has N+1 values. In most cases we expect this will have 1 value. The
+ // first value is the original request and each following value is the next
+ // redirect in order.
+ repeated RequestInfo main_frame_network_request = 26;
}
diff --git a/chromium/components/data_usage/OWNERS b/chromium/components/data_usage/OWNERS
deleted file mode 100644
index 49780b69be6..00000000000
--- a/chromium/components/data_usage/OWNERS
+++ /dev/null
@@ -1,9 +0,0 @@
-# Primary
-rajendrant@chromium.org
-sclittle@chromium.org
-tbansal@chromium.org
-
-# Secondary
-bengr@chromium.org
-
-# COMPONENT: Internals>Network>DataUse \ No newline at end of file
diff --git a/chromium/components/data_usage/android/BUILD.gn b/chromium/components/data_usage/android/BUILD.gn
deleted file mode 100644
index a664d6362d1..00000000000
--- a/chromium/components/data_usage/android/BUILD.gn
+++ /dev/null
@@ -1,35 +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.
-
-source_set("android") {
- sources = [
- "traffic_stats_amortizer.cc",
- "traffic_stats_amortizer.h",
- ]
- deps = [
- "//base",
- "//components/data_usage/core",
- "//components/variations",
- "//net",
- "//url",
- ]
-}
-
-source_set("unit_tests") {
- testonly = true
- sources = [
- "traffic_stats_amortizer_unittest.cc",
- ]
-
- configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
-
- deps = [
- ":android",
- "//base",
- "//base/test:test_support",
- "//components/data_usage/core",
- "//net:test_support",
- "//testing/gtest",
- ]
-}
diff --git a/chromium/components/data_usage/android/DEPS b/chromium/components/data_usage/android/DEPS
deleted file mode 100644
index fec846543a8..00000000000
--- a/chromium/components/data_usage/android/DEPS
+++ /dev/null
@@ -1,6 +0,0 @@
-include_rules = [
- "+components/data_usage/core",
- "+components/variations",
- "+net",
- "+url",
-]
diff --git a/chromium/components/data_usage/android/traffic_stats_amortizer.cc b/chromium/components/data_usage/android/traffic_stats_amortizer.cc
deleted file mode 100644
index 51a3094b23d..00000000000
--- a/chromium/components/data_usage/android/traffic_stats_amortizer.cc
+++ /dev/null
@@ -1,389 +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/data_usage/android/traffic_stats_amortizer.h"
-
-#include <algorithm> // For std::min.
-#include <cmath> // For std::modf.
-#include <set>
-#include <utility>
-
-#include "base/location.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/time/default_tick_clock.h"
-#include "base/timer/timer.h"
-#include "components/data_usage/core/data_use.h"
-#include "components/variations/variations_associated_data.h"
-#include "net/android/traffic_stats.h"
-
-namespace data_usage {
-namespace android {
-
-namespace {
-
-// Convenience typedef.
-typedef std::vector<std::pair<std::unique_ptr<DataUse>,
- DataUseAmortizer::AmortizationCompleteCallback>>
- DataUseBuffer;
-
-// Name of the field trial.
-const char kExternalDataUseObserverFieldTrial[] = "ExternalDataUseObserver";
-
-// The delay between receiving DataUse and querying TrafficStats byte counts for
-// amortization.
-const int64_t kDefaultTrafficStatsQueryDelayMs = 50;
-
-// The longest amount of time that an amortization run can be delayed for.
-const int64_t kDefaultMaxAmortizationDelayMs = 500;
-
-// The maximum allowed size of the DataUse buffer. If the buffer ever exceeds
-// this size, then DataUse will be amortized immediately and the buffer will be
-// flushed.
-const size_t kDefaultMaxDataUseBufferSize = 128;
-
-base::TimeDelta GetTrafficStatsQueryDelay() {
- int64_t duration_ms = kDefaultTrafficStatsQueryDelayMs;
- std::string variation_value = variations::GetVariationParamValue(
- kExternalDataUseObserverFieldTrial, "traffic_stats_query_delay_ms");
- if (!variation_value.empty() &&
- base::StringToInt64(variation_value, &duration_ms) && duration_ms >= 0) {
- return base::TimeDelta::FromMilliseconds(duration_ms);
- }
- return base::TimeDelta::FromMilliseconds(kDefaultTrafficStatsQueryDelayMs);
-}
-
-base::TimeDelta GetMaxAmortizationDelay() {
- int64_t duration_ms = kDefaultMaxAmortizationDelayMs;
- std::string variation_value = variations::GetVariationParamValue(
- kExternalDataUseObserverFieldTrial, "max_amortization_delay_ms");
- if (!variation_value.empty() &&
- base::StringToInt64(variation_value, &duration_ms) && duration_ms >= 0) {
- return base::TimeDelta::FromMilliseconds(duration_ms);
- }
- return base::TimeDelta::FromMilliseconds(kDefaultMaxAmortizationDelayMs);
-}
-
-size_t GetMaxDataUseBufferSize() {
- size_t max_buffer_size = kDefaultMaxDataUseBufferSize;
- std::string variation_value = variations::GetVariationParamValue(
- kExternalDataUseObserverFieldTrial, "max_data_use_buffer_size");
- if (!variation_value.empty() &&
- base::StringToSizeT(variation_value, &max_buffer_size)) {
- return max_buffer_size;
- }
- return kDefaultMaxDataUseBufferSize;
-}
-
-// Returns |byte_count| as a histogram sample capped at the maximum histogram
-// sample value that's suitable for being recorded without overflowing.
-base::HistogramBase::Sample GetByteCountAsHistogramSample(int64_t byte_count) {
- DCHECK_GE(byte_count, 0);
- if (byte_count >= base::HistogramBase::kSampleType_MAX) {
- // Return kSampleType_MAX - 1 because it's invalid to record
- // kSampleType_MAX, which would cause a CHECK to fail in the histogram code.
- return base::HistogramBase::kSampleType_MAX - 1;
- }
- return static_cast<base::HistogramBase::Sample>(byte_count);
-}
-
-// Scales |bytes| by |ratio|, using |remainder| to hold the running rounding
-// error. |bytes| must be non-negative, and multiplying |bytes| by |ratio| must
-// yield a number that's representable within the bounds of a non-negative
-// int64_t.
-int64_t ScaleByteCount(int64_t bytes, double ratio, double* remainder) {
- DCHECK_GE(bytes, 0);
- DCHECK_GE(ratio, 0.0);
- DCHECK_LE(ratio, static_cast<double>(INT64_MAX));
- DCHECK_GE(*remainder, 0.0);
- DCHECK_LT(*remainder, 1.0);
-
- double intpart;
- *remainder =
- std::modf(static_cast<double>(bytes) * ratio + (*remainder), &intpart);
-
- DCHECK_GE(intpart, 0.0);
- DCHECK_LE(intpart, static_cast<double>(INT64_MAX));
- DCHECK_GE(*remainder, 0.0);
- DCHECK_LT(*remainder, 1.0);
-
- // Due to floating point error, casting the double |intpart| to an int64_t
- // could cause it to overflow, even though it's already been checked to be
- // less than the double representation of INT64_MAX. If this happens, cap the
- // scaled value at INT64_MAX.
- uint64_t scaled_bytes = std::min(static_cast<uint64_t>(intpart),
- static_cast<uint64_t>(INT64_MAX));
- return static_cast<int64_t>(scaled_bytes);
-}
-
-// Amortizes the difference between |desired_post_amortization_total| and
-// |pre_amortization_total| into each of the DataUse objects in
-// |data_use_sequence| by scaling the byte counts determined by the
-// |get_byte_count_fn| function (e.g. tx_bytes, rx_bytes) for each DataUse
-// appropriately. |pre_amortization_total| must not be 0.
-void AmortizeByteCountSequence(DataUseBuffer* data_use_sequence,
- int64_t* (*get_byte_count_fn)(DataUse*),
- int64_t pre_amortization_total,
- int64_t desired_post_amortization_total) {
- DCHECK_GT(pre_amortization_total, 0);
- DCHECK_GE(desired_post_amortization_total, 0);
-
- const double ratio = static_cast<double>(desired_post_amortization_total) /
- static_cast<double>(pre_amortization_total);
-
- double remainder = 0.0;
- for (auto& data_use_buffer_pair : *data_use_sequence) {
- int64_t* byte_count = get_byte_count_fn(data_use_buffer_pair.first.get());
- *byte_count = ScaleByteCount(*byte_count, ratio, &remainder);
- }
-}
-
-int64_t* GetTxBytes(DataUse* data_use) {
- return &data_use->tx_bytes;
-}
-int64_t* GetRxBytes(DataUse* data_use) {
- return &data_use->rx_bytes;
-}
-
-// Returns the total transmitted bytes contained in |data_use_sequence|.
-int64_t GetTotalTxBytes(const DataUseBuffer& data_use_sequence) {
- int64_t sum = 0;
- for (const auto& data_use_buffer_pair : data_use_sequence)
- sum += data_use_buffer_pair.first->tx_bytes;
- return sum;
-}
-
-// Returns the total received bytes contained in |data_use_sequence|.
-int64_t GetTotalRxBytes(const DataUseBuffer& data_use_sequence) {
- int64_t sum = 0;
- for (const auto& data_use_buffer_pair : data_use_sequence)
- sum += data_use_buffer_pair.first->rx_bytes;
- return sum;
-}
-
-void RecordConcurrentTabsHistogram(const DataUseBuffer& data_use_buffer) {
- std::set<SessionID> unique_tabs;
- for (const auto& data_use_buffer_pair : data_use_buffer)
- unique_tabs.insert(data_use_buffer_pair.first->tab_id);
- UMA_HISTOGRAM_COUNTS_100("TrafficStatsAmortizer.ConcurrentTabs",
- unique_tabs.size());
-}
-
-} // namespace
-
-TrafficStatsAmortizer::TrafficStatsAmortizer()
- : TrafficStatsAmortizer(
- base::DefaultTickClock::GetInstance(),
- std::unique_ptr<base::Timer>(new base::Timer(false, false)),
- GetTrafficStatsQueryDelay(),
- GetMaxAmortizationDelay(),
- GetMaxDataUseBufferSize()) {}
-
-TrafficStatsAmortizer::~TrafficStatsAmortizer() {}
-
-void TrafficStatsAmortizer::AmortizeDataUse(
- std::unique_ptr<DataUse> data_use,
- const AmortizationCompleteCallback& callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(!callback.is_null());
- int64_t tx_bytes = data_use->tx_bytes, rx_bytes = data_use->rx_bytes;
-
- // As an optimization, combine consecutive buffered DataUse objects that are
- // identical except for byte counts and have the same callback.
- if (!buffered_data_use_.empty() &&
- buffered_data_use_.back().first->CanCombineWith(*data_use) &&
- buffered_data_use_.back().second.Equals(callback)) {
- buffered_data_use_.back().first->tx_bytes += data_use->tx_bytes;
- buffered_data_use_.back().first->rx_bytes += data_use->rx_bytes;
- } else {
- buffered_data_use_.push_back(
- std::pair<std::unique_ptr<DataUse>, AmortizationCompleteCallback>(
- std::move(data_use), callback));
- }
-
- AddPreAmortizationBytes(tx_bytes, rx_bytes);
-}
-
-void TrafficStatsAmortizer::OnExtraBytes(int64_t extra_tx_bytes,
- int64_t extra_rx_bytes) {
- DCHECK(thread_checker_.CalledOnValidThread());
- AddPreAmortizationBytes(extra_tx_bytes, extra_rx_bytes);
-}
-
-base::WeakPtr<TrafficStatsAmortizer> TrafficStatsAmortizer::GetWeakPtr() {
- DCHECK(thread_checker_.CalledOnValidThread());
- return weak_ptr_factory_.GetWeakPtr();
-}
-
-TrafficStatsAmortizer::TrafficStatsAmortizer(
- const base::TickClock* tick_clock,
- std::unique_ptr<base::Timer> traffic_stats_query_timer,
- const base::TimeDelta& traffic_stats_query_delay,
- const base::TimeDelta& max_amortization_delay,
- size_t max_data_use_buffer_size)
- : tick_clock_(tick_clock),
- traffic_stats_query_timer_(std::move(traffic_stats_query_timer)),
- traffic_stats_query_delay_(traffic_stats_query_delay),
- max_amortization_delay_(max_amortization_delay),
- max_data_use_buffer_size_(max_data_use_buffer_size),
- is_amortization_in_progress_(false),
- are_last_amortization_traffic_stats_available_(false),
- last_amortization_traffic_stats_tx_bytes_(-1),
- last_amortization_traffic_stats_rx_bytes_(-1),
- pre_amortization_tx_bytes_(0),
- pre_amortization_rx_bytes_(0),
- weak_ptr_factory_(this) {}
-
-bool TrafficStatsAmortizer::QueryTrafficStats(int64_t* tx_bytes,
- int64_t* rx_bytes) const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return net::android::traffic_stats::GetCurrentUidTxBytes(tx_bytes) &&
- net::android::traffic_stats::GetCurrentUidRxBytes(rx_bytes);
-}
-
-void TrafficStatsAmortizer::AddPreAmortizationBytes(int64_t tx_bytes,
- int64_t rx_bytes) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_GE(tx_bytes, 0);
- DCHECK_GE(rx_bytes, 0);
- base::TimeTicks now_ticks = tick_clock_->NowTicks();
-
- if (!is_amortization_in_progress_) {
- is_amortization_in_progress_ = true;
- current_amortization_run_start_time_ = now_ticks;
- }
-
- pre_amortization_tx_bytes_ += tx_bytes;
- pre_amortization_rx_bytes_ += rx_bytes;
-
- if (buffered_data_use_.size() > max_data_use_buffer_size_) {
- // Enforce a maximum limit on the size of |buffered_data_use_| to avoid
- // hogging memory. Note that this will likely cause the post-amortization
- // byte counts calculated here to be less accurate than if the amortizer
- // waited to perform amortization.
- traffic_stats_query_timer_->Stop();
- AmortizeNow();
- return;
- }
-
- // Cap any amortization delay to |max_amortization_delay_|. Note that if
- // |max_amortization_delay_| comes earlier, then this will likely cause the
- // post-amortization byte counts calculated here to be less accurate than if
- // the amortizer waited to perform amortization.
- base::TimeDelta query_delay = std::min(
- traffic_stats_query_delay_, current_amortization_run_start_time_ +
- max_amortization_delay_ - now_ticks);
-
- // Set the timer to query TrafficStats and amortize after a delay, so that
- // it's more likely that TrafficStats will be queried when the network is
- // idle. If the timer was already set, then this overrides the previous delay.
- traffic_stats_query_timer_->Start(
- FROM_HERE, query_delay,
- base::Bind(&TrafficStatsAmortizer::AmortizeNow, GetWeakPtr()));
-}
-
-void TrafficStatsAmortizer::AmortizeNow() {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(is_amortization_in_progress_);
-
- if (!buffered_data_use_.empty()) {
- // Record histograms for the pre-amortization byte counts of the DataUse
- // objects.
- UMA_HISTOGRAM_COUNTS(
- "TrafficStatsAmortizer.PreAmortizationRunDataUseBytes.Tx",
- GetByteCountAsHistogramSample(GetTotalTxBytes(buffered_data_use_)));
- UMA_HISTOGRAM_COUNTS(
- "TrafficStatsAmortizer.PreAmortizationRunDataUseBytes.Rx",
- GetByteCountAsHistogramSample(GetTotalRxBytes(buffered_data_use_)));
- }
-
- int64_t current_traffic_stats_tx_bytes = -1;
- int64_t current_traffic_stats_rx_bytes = -1;
- bool are_current_traffic_stats_available = QueryTrafficStats(
- &current_traffic_stats_tx_bytes, &current_traffic_stats_rx_bytes);
-
- if (are_current_traffic_stats_available &&
- are_last_amortization_traffic_stats_available_ &&
- !buffered_data_use_.empty()) {
- // These TrafficStats byte counts are guaranteed to increase monotonically
- // since device boot.
- DCHECK_GE(current_traffic_stats_tx_bytes,
- last_amortization_traffic_stats_tx_bytes_);
- DCHECK_GE(current_traffic_stats_rx_bytes,
- last_amortization_traffic_stats_rx_bytes_);
-
- // Only attempt to amortize network overhead from TrafficStats if any of
- // those bytes are reflected in the pre-amortization byte totals. Otherwise,
- // that network overhead will be amortized in a later amortization run.
- if (pre_amortization_tx_bytes_ != 0) {
- AmortizeByteCountSequence(&buffered_data_use_, &GetTxBytes,
- pre_amortization_tx_bytes_,
- current_traffic_stats_tx_bytes -
- last_amortization_traffic_stats_tx_bytes_);
- }
- if (pre_amortization_rx_bytes_ != 0) {
- AmortizeByteCountSequence(&buffered_data_use_, &GetRxBytes,
- pre_amortization_rx_bytes_,
- current_traffic_stats_rx_bytes -
- last_amortization_traffic_stats_rx_bytes_);
- }
- }
-
- if (!buffered_data_use_.empty()) {
- // Record histograms for the post-amortization byte counts of the DataUse
- // objects.
- UMA_HISTOGRAM_COUNTS(
- "TrafficStatsAmortizer.PostAmortizationRunDataUseBytes.Tx",
- GetByteCountAsHistogramSample(GetTotalTxBytes(buffered_data_use_)));
- UMA_HISTOGRAM_COUNTS(
- "TrafficStatsAmortizer.PostAmortizationRunDataUseBytes.Rx",
- GetByteCountAsHistogramSample(GetTotalRxBytes(buffered_data_use_)));
- RecordConcurrentTabsHistogram(buffered_data_use_);
- }
-
- UMA_HISTOGRAM_TIMES(
- "TrafficStatsAmortizer.AmortizationDelay",
- tick_clock_->NowTicks() - current_amortization_run_start_time_);
-
- UMA_HISTOGRAM_COUNTS_1000("TrafficStatsAmortizer.BufferSizeOnFlush",
- buffered_data_use_.size());
-
- // Reset state now that the amortization run has finished.
- is_amortization_in_progress_ = false;
- current_amortization_run_start_time_ = base::TimeTicks();
-
- // Don't update the previous amortization run's TrafficStats byte counts if
- // none of the bytes since then are reflected in the pre-amortization byte
- // totals. This way, the overhead that wasn't handled in this amortization run
- // can be handled in a later amortization run that actually has bytes in that
- // direction. This mitigates the problem of losing TrafficStats overhead bytes
- // on slow networks due to TrafficStats seeing the bytes much earlier than the
- // network stack reports them, or vice versa.
- if (!are_last_amortization_traffic_stats_available_ ||
- pre_amortization_tx_bytes_ != 0) {
- last_amortization_traffic_stats_tx_bytes_ = current_traffic_stats_tx_bytes;
- }
- if (!are_last_amortization_traffic_stats_available_ ||
- pre_amortization_rx_bytes_ != 0) {
- last_amortization_traffic_stats_rx_bytes_ = current_traffic_stats_rx_bytes;
- }
-
- are_last_amortization_traffic_stats_available_ =
- are_current_traffic_stats_available;
-
- pre_amortization_tx_bytes_ = 0;
- pre_amortization_rx_bytes_ = 0;
-
- DataUseBuffer data_use_sequence;
- data_use_sequence.swap(buffered_data_use_);
-
- // Pass post-amortization DataUse objects to their respective callbacks.
- for (auto& data_use_buffer_pair : data_use_sequence)
- data_use_buffer_pair.second.Run(std::move(data_use_buffer_pair.first));
-}
-
-} // namespace android
-} // namespace data_usage
diff --git a/chromium/components/data_usage/android/traffic_stats_amortizer.h b/chromium/components/data_usage/android/traffic_stats_amortizer.h
deleted file mode 100644
index a247e65a1bb..00000000000
--- a/chromium/components/data_usage/android/traffic_stats_amortizer.h
+++ /dev/null
@@ -1,166 +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_DATA_USAGE_ANDROID_TRAFFIC_STATS_AMORTIZER_H_
-#define COMPONENTS_DATA_USAGE_ANDROID_TRAFFIC_STATS_AMORTIZER_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "base/time/time.h"
-#include "components/data_usage/core/data_use_amortizer.h"
-
-namespace base {
-class TickClock;
-class Timer;
-}
-
-namespace data_usage {
-
-struct DataUse;
-
-namespace android {
-
-// Class that uses Android TrafficStats to amortize any unincluded overhead
-// (e.g. network layer, TLS, DNS) into the data usage reported by the network
-// stack. Should only be used on the IO thread. Since TrafficStats measurements
-// are global for the entire application, a TrafficStatsAmortizer should be
-// notified of every byte possible, or else it might mistakenly classify the
-// corresponding additional TrafficStats bytes for those as overhead. The
-// TrafficStats API has been available in Android since API level 8 (Android
-// 2.2).
-class TrafficStatsAmortizer : public DataUseAmortizer {
- public:
- TrafficStatsAmortizer();
- ~TrafficStatsAmortizer() override;
-
- // Amortizes any unincluded network bytes overhead for |data_use| into
- // |data_use|, and passes the updated |data_use| to |callback| once
- // amortization is complete. The TrafficStatsAmortizer may combine together
- // consecutive |data_use| objects that have the same |callback| if the
- // |data_use| objects are identical in all ways but their byte counts, such
- // that |callback| will only be called once with the single combined DataUse
- // object.
- void AmortizeDataUse(std::unique_ptr<DataUse> data_use,
- const AmortizationCompleteCallback& callback) override;
-
- // Notifies the amortizer that some extra bytes have been transferred that
- // aren't associated with any DataUse objects (e.g. off-the-record traffic),
- // so that the TrafficStatsAmortizer can avoid mistakenly counting these bytes
- // as overhead.
- void OnExtraBytes(int64_t extra_tx_bytes, int64_t extra_rx_bytes) override;
-
- base::WeakPtr<TrafficStatsAmortizer> GetWeakPtr();
-
- protected:
- // Constructor for testing purposes, allowing for tests to take full control
- // over the timing of the TrafficStatsAmortizer and the byte counts returned
- // from TrafficStats. |traffic_stats_query_timer| must not be a repeating
- // timer.
- TrafficStatsAmortizer(const base::TickClock* tick_clock,
- std::unique_ptr<base::Timer> traffic_stats_query_timer,
- const base::TimeDelta& traffic_stats_query_delay,
- const base::TimeDelta& max_amortization_delay,
- size_t max_data_use_buffer_size);
-
- // Queries the total transmitted and received bytes for the application from
- // TrafficStats. Stores the byte counts in |tx_bytes| and |rx_bytes|
- // respectively and returns true if both values are available from
- // TrafficStats, otherwise returns false. |tx_bytes| and |rx_bytes| must not
- // be NULL.
- // Virtual for testing.
- virtual bool QueryTrafficStats(int64_t* tx_bytes, int64_t* rx_bytes) const;
-
- private:
- // Adds |tx_bytes| and |rx_bytes| as data usage that should not be counted as
- // overhead (i.e. bytes from DataUse objects and extra bytes reported to this
- // TrafficStatsAmortizer), and schedules amortization to happen later.
- void AddPreAmortizationBytes(int64_t tx_bytes, int64_t rx_bytes);
-
- // Amortizes any additional overhead from TrafficStats byte counts into the
- // |buffered_data_use_|, then passes the post-amortization DataUse objects to
- // their respective callbacks, flushing |buffered_data_use_|. Overhead is
- // calculated as the difference between the TrafficStats byte counts and the
- // pre-amortization byte counts.
- void AmortizeNow();
-
- base::ThreadChecker thread_checker_;
-
- // TickClock for determining the current time tick.
- const base::TickClock* tick_clock_;
-
- // One-shot timer used to wait a short time after receiving DataUse before
- // querying TrafficStats, to give TrafficStats time to update and give the
- // network stack time to finish reporting multiple DataUse objects that happen
- // in rapid succession. This must not be a repeating timer.
- // |traffic_stats_query_timer_| is owned as a scoped_ptr so that fake timers
- // can be passed in for tests.
- std::unique_ptr<base::Timer> traffic_stats_query_timer_;
-
- // The delay between data usage being reported to the amortizer before
- // querying TrafficStats. Used with |traffic_stats_query_timer_|.
- const base::TimeDelta traffic_stats_query_delay_;
-
- // The maximum amount of time that the TrafficStatsAmortizer is allowed to
- // spend waiting to perform amortization. Used with
- // |traffic_stats_query_timer_|.
- const base::TimeDelta max_amortization_delay_;
-
- // The maximum allowed size of the |buffered_data_use_| buffer, to prevent the
- // buffer from hogging memory.
- const size_t max_data_use_buffer_size_;
-
- // Indicates whether or not the TrafficStatsAmortizer currently has
- // pre-amortization bytes waiting for amortization to be performed.
- bool is_amortization_in_progress_;
-
- // The time when the first pre-amortization bytes for the current amortization
- // run were given to this TrafficStatsAmortizer.
- base::TimeTicks current_amortization_run_start_time_;
-
- // Buffer of pre-amortization data use that has accumulated since the last
- // time amortization was performed, paired with the callbacks for each DataUse
- // object.
- std::vector<std::pair<std::unique_ptr<DataUse>, AmortizationCompleteCallback>>
- buffered_data_use_;
-
- // Indicates if TrafficStats byte counts were available during the last time
- // amortization was performed.
- bool are_last_amortization_traffic_stats_available_;
-
- // The total transmitted bytes according to TrafficStats during the last time
- // amortization was performed, if they were available.
- int64_t last_amortization_traffic_stats_tx_bytes_;
-
- // The total received bytes according to TrafficStats during the last time
- // amortization was performed, if they were available.
- int64_t last_amortization_traffic_stats_rx_bytes_;
-
- // Total pre-amortization transmitted bytes since the last time amortization
- // was performed, including bytes from |buffered_data_use_| and any extra
- // bytes that were added.
- int64_t pre_amortization_tx_bytes_;
-
- // Total pre-amortization received bytes since the last time amortization was
- // performed, including bytes from |buffered_data_use_| and any extra bytes
- // that were added.
- int64_t pre_amortization_rx_bytes_;
-
- base::WeakPtrFactory<TrafficStatsAmortizer> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(TrafficStatsAmortizer);
-};
-
-} // namespace android
-
-} // namespace data_usage
-
-#endif // COMPONENTS_DATA_USAGE_ANDROID_TRAFFIC_STATS_AMORTIZER_H_
diff --git a/chromium/components/data_usage/android/traffic_stats_amortizer_unittest.cc b/chromium/components/data_usage/android/traffic_stats_amortizer_unittest.cc
deleted file mode 100644
index a91d9642253..00000000000
--- a/chromium/components/data_usage/android/traffic_stats_amortizer_unittest.cc
+++ /dev/null
@@ -1,848 +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/data_usage/android/traffic_stats_amortizer.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
-#include "base/metrics/histogram_base.h"
-#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "base/time/tick_clock.h"
-#include "base/time/time.h"
-#include "base/timer/mock_timer.h"
-#include "base/timer/timer.h"
-#include "components/data_usage/core/data_use.h"
-#include "components/data_usage/core/data_use_amortizer.h"
-#include "net/base/network_change_notifier.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace data_usage {
-namespace android {
-
-namespace {
-
-// The delay between receiving DataUse and querying TrafficStats byte counts for
-// amortization.
-const base::TimeDelta kTrafficStatsQueryDelay =
- base::TimeDelta::FromMilliseconds(50);
-
-// The longest amount of time that an amortization run can be delayed for.
-const base::TimeDelta kMaxAmortizationDelay =
- base::TimeDelta::FromMilliseconds(200);
-
-// The maximum allowed size of the DataUse buffer.
-const size_t kMaxDataUseBufferSize = 8;
-
-const char kPreAmortizationTxHistogram[] =
- "TrafficStatsAmortizer.PreAmortizationRunDataUseBytes.Tx";
-const char kPreAmortizationRxHistogram[] =
- "TrafficStatsAmortizer.PreAmortizationRunDataUseBytes.Rx";
-const char kPostAmortizationTxHistogram[] =
- "TrafficStatsAmortizer.PostAmortizationRunDataUseBytes.Tx";
-const char kPostAmortizationRxHistogram[] =
- "TrafficStatsAmortizer.PostAmortizationRunDataUseBytes.Rx";
-const char kAmortizationDelayHistogram[] =
- "TrafficStatsAmortizer.AmortizationDelay";
-const char kBufferSizeOnFlushHistogram[] =
- "TrafficStatsAmortizer.BufferSizeOnFlush";
-const char kConcurrentTabs[] = "TrafficStatsAmortizer.ConcurrentTabs";
-
-// The maximum sample value that can be recorded in a histogram.
-const base::HistogramBase::Sample kMaxRecordableSample =
- base::HistogramBase::kSampleType_MAX - 1;
-
-// Converts a |delay| into a histogram sample of the number of milliseconds in
-// the delay.
-base::HistogramBase::Sample GetDelaySample(const base::TimeDelta& delay) {
- return static_cast<base::HistogramBase::Sample>(delay.InMilliseconds());
-}
-
-// Synthesizes a fake std::unique_ptr<DataUse> with the given |url|, |tab_id|,
-// |tx_bytes| and |rx_bytes|, using arbitrary values for all other fields.
-std::unique_ptr<DataUse> CreateDataUseWithURLAndTab(const GURL& url,
- SessionID tab_id,
- int64_t tx_bytes,
- int64_t rx_bytes) {
- return std::unique_ptr<DataUse>(
- new DataUse(url, base::TimeTicks() /* request_start */,
- GURL("http://examplefirstparty.com"), tab_id,
- net::NetworkChangeNotifier::CONNECTION_2G, "example_mcc_mnc",
- tx_bytes, rx_bytes));
-}
-
-// Synthesizes a fake std::unique_ptr<DataUse> with the given |url|, |tx_bytes|
-// and
-// |rx_bytes|, using arbitrary values for all other fields.
-std::unique_ptr<DataUse> CreateDataUseWithURL(const GURL& url,
- int64_t tx_bytes,
- int64_t rx_bytes) {
- return CreateDataUseWithURLAndTab(url, SessionID::FromSerializedValue(10),
- tx_bytes, rx_bytes);
-}
-
-// Synthesizes a fake std::unique_ptr<DataUse> with the given |tab_id|,
-// |tx_bytes|
-// and |rx_bytes|, using arbitrary values for all other fields.
-std::unique_ptr<DataUse> CreateDataUseWithTab(SessionID tab_id,
- int64_t tx_bytes,
- int64_t rx_bytes) {
- return CreateDataUseWithURLAndTab(GURL("http://example.com"), tab_id,
- tx_bytes, rx_bytes);
-}
-
-// Synthesizes a fake std::unique_ptr<DataUse> with the given |tx_bytes| and
-// |rx_bytes|, using arbitrary values for all other fields.
-std::unique_ptr<DataUse> CreateDataUse(int64_t tx_bytes, int64_t rx_bytes) {
- return CreateDataUseWithURL(GURL("http://example.com"), tx_bytes, rx_bytes);
-}
-
-// Appends |data_use| to |data_use_sequence|. |data_use_sequence| must not be
-// NULL.
-void AppendDataUseToSequence(
- std::vector<std::unique_ptr<DataUse>>* data_use_sequence,
- std::unique_ptr<DataUse> data_use) {
- data_use_sequence->push_back(std::move(data_use));
-}
-
-// Class that represents a base::MockTimer with an attached base::TickClock, so
-// that it can update its |desired_run_time()| according to the current time
-// when the timer is reset.
-class MockTimerWithTickClock : public base::MockTimer {
- public:
- MockTimerWithTickClock(bool retain_user_task,
- bool is_repeating,
- const base::TickClock* tick_clock)
- : base::MockTimer(retain_user_task, is_repeating),
- tick_clock_(tick_clock) {}
-
- ~MockTimerWithTickClock() override {}
-
- void Reset() override {
- base::MockTimer::Reset();
- set_desired_run_time(tick_clock_->NowTicks() + GetCurrentDelay());
- }
-
- private:
- const base::TickClock* tick_clock_;
-
- DISALLOW_COPY_AND_ASSIGN(MockTimerWithTickClock);
-};
-
-// A TrafficStatsAmortizer for testing that allows for tests to simulate the
-// byte counts returned from TrafficStats.
-class TestTrafficStatsAmortizer : public TrafficStatsAmortizer {
- public:
- TestTrafficStatsAmortizer(
- const base::TickClock* tick_clock,
- std::unique_ptr<base::Timer> traffic_stats_query_timer)
- : TrafficStatsAmortizer(tick_clock,
- std::move(traffic_stats_query_timer),
- kTrafficStatsQueryDelay,
- kMaxAmortizationDelay,
- kMaxDataUseBufferSize),
- next_traffic_stats_available_(false),
- next_traffic_stats_tx_bytes_(-1),
- next_traffic_stats_rx_bytes_(-1) {}
-
- ~TestTrafficStatsAmortizer() override {}
-
- void SetNextTrafficStats(bool available, int64_t tx_bytes, int64_t rx_bytes) {
- next_traffic_stats_available_ = available;
- next_traffic_stats_tx_bytes_ = tx_bytes;
- next_traffic_stats_rx_bytes_ = rx_bytes;
- }
-
- void AddTrafficStats(int64_t tx_bytes, int64_t rx_bytes) {
- next_traffic_stats_tx_bytes_ += tx_bytes;
- next_traffic_stats_rx_bytes_ += rx_bytes;
- }
-
- protected:
- bool QueryTrafficStats(int64_t* tx_bytes, int64_t* rx_bytes) const override {
- *tx_bytes = next_traffic_stats_tx_bytes_;
- *rx_bytes = next_traffic_stats_rx_bytes_;
- return next_traffic_stats_available_;
- }
-
- private:
- bool next_traffic_stats_available_;
- int64_t next_traffic_stats_tx_bytes_;
- int64_t next_traffic_stats_rx_bytes_;
-
- DISALLOW_COPY_AND_ASSIGN(TestTrafficStatsAmortizer);
-};
-
-class TrafficStatsAmortizerTest : public testing::Test {
- public:
- TrafficStatsAmortizerTest()
- : mock_timer_(
- new MockTimerWithTickClock(false, false, &test_tick_clock_)),
- amortizer_(&test_tick_clock_,
- std::unique_ptr<base::Timer>(mock_timer_)),
- data_use_callback_call_count_(0) {}
-
- ~TrafficStatsAmortizerTest() override {
- EXPECT_FALSE(mock_timer_->IsRunning());
- }
-
- // Simulates the passage of time by |delta|, firing timers when appropriate.
- void AdvanceTime(const base::TimeDelta& delta) {
- const base::TimeTicks end_time = test_tick_clock_.NowTicks() + delta;
- base::RunLoop().RunUntilIdle();
-
- while (test_tick_clock_.NowTicks() < end_time) {
- PumpMockTimer();
-
- // If |mock_timer_| is scheduled to fire in the future before |end_time|,
- // advance to that time.
- if (mock_timer_->IsRunning() &&
- mock_timer_->desired_run_time() < end_time) {
- test_tick_clock_.Advance(mock_timer_->desired_run_time() -
- test_tick_clock_.NowTicks());
- } else {
- // Otherwise, advance to |end_time|.
- test_tick_clock_.Advance(end_time - test_tick_clock_.NowTicks());
- }
- }
- PumpMockTimer();
- }
-
- // Skip the first amortization run where TrafficStats byte count deltas are
- // unavailable, for convenience.
- void SkipFirstAmortizationRun() {
- // The initial values of TrafficStats shouldn't matter.
- amortizer()->SetNextTrafficStats(true, 0, 0);
-
- // Do the first amortization run with TrafficStats unavailable.
- amortizer()->OnExtraBytes(100, 1000);
- AdvanceTime(kTrafficStatsQueryDelay);
- EXPECT_EQ(0, data_use_callback_call_count());
- }
-
- // Expects that |expected| and |actual| are equivalent.
- void ExpectDataUse(std::unique_ptr<DataUse> expected,
- std::unique_ptr<DataUse> actual) {
- ++data_use_callback_call_count_;
-
- // Have separate checks for the |tx_bytes| and |rx_bytes|, since those are
- // calculated with floating point arithmetic.
- EXPECT_DOUBLE_EQ(static_cast<double>(expected->tx_bytes),
- static_cast<double>(actual->tx_bytes));
- EXPECT_DOUBLE_EQ(static_cast<double>(expected->rx_bytes),
- static_cast<double>(actual->rx_bytes));
-
- // Copy the byte counts over from |expected| just in case they're only
- // slightly different due to floating point error, so that this doesn't
- // cause the equality comparison below to fail.
- actual->tx_bytes = expected->tx_bytes;
- actual->rx_bytes = expected->rx_bytes;
- EXPECT_EQ(*expected, *actual);
- }
-
- // Creates an ExpectDataUse callback, as a convenience.
- DataUseAmortizer::AmortizationCompleteCallback ExpectDataUseCallback(
- std::unique_ptr<DataUse> expected) {
- return base::Bind(&TrafficStatsAmortizerTest::ExpectDataUse,
- base::Unretained(this), base::Passed(&expected));
- }
-
- base::TimeTicks NowTicks() { return test_tick_clock_.NowTicks(); }
-
- TestTrafficStatsAmortizer* amortizer() { return &amortizer_; }
-
- int data_use_callback_call_count() const {
- return data_use_callback_call_count_;
- }
-
- private:
- // Pumps |mock_timer_|, firing it while it's scheduled to run now or in the
- // past. After calling this, |mock_timer_| is either not running or is
- // scheduled to run in the future.
- void PumpMockTimer() {
- // Fire the |mock_timer_| if the time has come up. Use a while loop in case
- // the fired task started the timer again to fire immediately.
- while (mock_timer_->IsRunning() &&
- mock_timer_->desired_run_time() <= test_tick_clock_.NowTicks()) {
- mock_timer_->Fire();
- base::RunLoop().RunUntilIdle();
- }
- }
-
- base::MessageLoop message_loop_;
-
- base::SimpleTestTickClock test_tick_clock_;
-
- // Weak, owned by |amortizer_|.
- MockTimerWithTickClock* mock_timer_;
-
- TestTrafficStatsAmortizer amortizer_;
-
- // The number of times ExpectDataUse has been called.
- int data_use_callback_call_count_;
-
- DISALLOW_COPY_AND_ASSIGN(TrafficStatsAmortizerTest);
-};
-
-TEST_F(TrafficStatsAmortizerTest, AmortizeWithTrafficStatsAlwaysUnavailable) {
- amortizer()->SetNextTrafficStats(false, -1, -1);
- // Do it three times for good measure.
- for (int i = 0; i < 3; ++i) {
- base::HistogramTester histogram_tester;
-
- // Extra bytes should be ignored since TrafficStats are unavailable.
- amortizer()->OnExtraBytes(1337, 9001);
- // The original DataUse should be unchanged.
- amortizer()->AmortizeDataUse(
- CreateDataUse(100, 1000),
- ExpectDataUseCallback(CreateDataUse(100, 1000)));
-
- AdvanceTime(kTrafficStatsQueryDelay);
- EXPECT_EQ(i + 1, data_use_callback_call_count());
-
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 100, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 1000, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 100, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 1000, 1);
- histogram_tester.ExpectUniqueSample(kAmortizationDelayHistogram,
- GetDelaySample(kTrafficStatsQueryDelay),
- 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
- }
-}
-
-TEST_F(TrafficStatsAmortizerTest, AmortizeDataUse) {
- // Simulate the first amortization run.
- {
- base::HistogramTester histogram_tester;
-
- // The initial values of TrafficStats shouldn't matter.
- amortizer()->SetNextTrafficStats(true, 1337, 9001);
-
- // The first amortization run should not change any byte counts because
- // there's no TrafficStats delta to work with.
- amortizer()->AmortizeDataUse(CreateDataUse(50, 500),
- ExpectDataUseCallback(CreateDataUse(50, 500)));
- amortizer()->AmortizeDataUse(
- CreateDataUse(100, 1000),
- ExpectDataUseCallback(CreateDataUse(100, 1000)));
- AdvanceTime(kTrafficStatsQueryDelay);
- EXPECT_EQ(2, data_use_callback_call_count());
-
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 150, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 1500, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 150, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 1500, 1);
- histogram_tester.ExpectUniqueSample(kAmortizationDelayHistogram,
- GetDelaySample(kTrafficStatsQueryDelay),
- 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 2, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
- }
-
- // Simulate the second amortization run.
- {
- base::HistogramTester histogram_tester;
-
- // This amortization run, tx_bytes and rx_bytes should be doubled.
- amortizer()->AmortizeDataUse(
- CreateDataUse(50, 500),
- ExpectDataUseCallback(CreateDataUse(100, 1000)));
- AdvanceTime(kTrafficStatsQueryDelay / 2);
-
- // Another DataUse is reported before the amortizer queries TrafficStats.
- amortizer()->AmortizeDataUse(
- CreateDataUse(100, 1000),
- ExpectDataUseCallback(CreateDataUse(200, 2000)));
- AdvanceTime(kTrafficStatsQueryDelay / 2);
-
- // Then, the TrafficStats values update with the new bytes. The second run
- // callbacks should not have been called yet.
- amortizer()->AddTrafficStats(300, 3000);
- EXPECT_EQ(2, data_use_callback_call_count());
-
- // The callbacks should fire once kTrafficStatsQueryDelay has passed since
- // the DataUse was passed to the amortizer.
- AdvanceTime(kTrafficStatsQueryDelay / 2);
- EXPECT_EQ(4, data_use_callback_call_count());
-
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 150, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 1500, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 300, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 3000, 1);
- histogram_tester.ExpectUniqueSample(
- kAmortizationDelayHistogram,
- GetDelaySample(kTrafficStatsQueryDelay + kTrafficStatsQueryDelay / 2),
- 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 2, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
- }
-}
-
-TEST_F(TrafficStatsAmortizerTest, AmortizeWithExtraBytes) {
- SkipFirstAmortizationRun();
- base::HistogramTester histogram_tester;
-
- // Byte counts should double.
- amortizer()->AmortizeDataUse(CreateDataUse(50, 500),
- ExpectDataUseCallback(CreateDataUse(100, 1000)));
- amortizer()->OnExtraBytes(500, 5000);
- amortizer()->AddTrafficStats(1100, 11000);
- AdvanceTime(kTrafficStatsQueryDelay);
- EXPECT_EQ(1, data_use_callback_call_count());
-
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 50, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 500, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 100, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 1000, 1);
- histogram_tester.ExpectUniqueSample(
- kAmortizationDelayHistogram, GetDelaySample(kTrafficStatsQueryDelay), 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
-}
-
-TEST_F(TrafficStatsAmortizerTest, AmortizeWithNegativeOverhead) {
- SkipFirstAmortizationRun();
- base::HistogramTester histogram_tester;
-
- // Byte counts should halve.
- amortizer()->AmortizeDataUse(CreateDataUse(50, 500),
- ExpectDataUseCallback(CreateDataUse(25, 250)));
- amortizer()->AddTrafficStats(25, 250);
- AdvanceTime(kTrafficStatsQueryDelay);
- EXPECT_EQ(1, data_use_callback_call_count());
-
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 50, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 500, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 25, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 250, 1);
- histogram_tester.ExpectUniqueSample(
- kAmortizationDelayHistogram, GetDelaySample(kTrafficStatsQueryDelay), 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
-}
-
-TEST_F(TrafficStatsAmortizerTest, AmortizeWithMaxIntByteCounts) {
- SkipFirstAmortizationRun();
- base::HistogramTester histogram_tester;
-
- // Byte counts should be unchanged.
- amortizer()->AmortizeDataUse(
- CreateDataUse(INT64_MAX, INT64_MAX),
- ExpectDataUseCallback(CreateDataUse(INT64_MAX, INT64_MAX)));
- amortizer()->SetNextTrafficStats(true, INT64_MAX, INT64_MAX);
- AdvanceTime(kTrafficStatsQueryDelay);
- EXPECT_EQ(1, data_use_callback_call_count());
-
- // Byte count samples should be capped at the maximum Sample value that's
- // valid to be recorded.
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram,
- kMaxRecordableSample, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram,
- kMaxRecordableSample, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram,
- kMaxRecordableSample, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram,
- kMaxRecordableSample, 1);
- histogram_tester.ExpectUniqueSample(
- kAmortizationDelayHistogram, GetDelaySample(kTrafficStatsQueryDelay), 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
-}
-
-TEST_F(TrafficStatsAmortizerTest, AmortizeWithMaxIntScaleFactor) {
- SkipFirstAmortizationRun();
- base::HistogramTester histogram_tester;
-
- // Byte counts should be scaled up to INT64_MAX.
- amortizer()->AmortizeDataUse(
- CreateDataUse(1, 1),
- ExpectDataUseCallback(CreateDataUse(INT64_MAX, INT64_MAX)));
- amortizer()->SetNextTrafficStats(true, INT64_MAX, INT64_MAX);
- AdvanceTime(kTrafficStatsQueryDelay);
- EXPECT_EQ(1, data_use_callback_call_count());
-
- // Post-amortization byte count samples should be capped at the maximum Sample
- // value that's valid to be recorded.
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram,
- kMaxRecordableSample, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram,
- kMaxRecordableSample, 1);
- histogram_tester.ExpectUniqueSample(
- kAmortizationDelayHistogram, GetDelaySample(kTrafficStatsQueryDelay), 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
-}
-
-TEST_F(TrafficStatsAmortizerTest, AmortizeWithZeroScaleFactor) {
- SkipFirstAmortizationRun();
- base::HistogramTester histogram_tester;
-
- // Byte counts should be scaled down to 0.
- amortizer()->AmortizeDataUse(CreateDataUse(INT64_MAX, INT64_MAX),
- ExpectDataUseCallback(CreateDataUse(0, 0)));
- amortizer()->SetNextTrafficStats(true, 0, 0);
- AdvanceTime(kTrafficStatsQueryDelay);
- EXPECT_EQ(1, data_use_callback_call_count());
-
- // Pre-amortization byte count samples should be capped at the maximum Sample
- // value that's valid to be recorded.
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram,
- kMaxRecordableSample, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram,
- kMaxRecordableSample, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(
- kAmortizationDelayHistogram, GetDelaySample(kTrafficStatsQueryDelay), 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
-}
-
-TEST_F(TrafficStatsAmortizerTest, AmortizeWithZeroPreAmortizationBytes) {
- SkipFirstAmortizationRun();
-
- {
- base::HistogramTester histogram_tester;
-
- // Both byte counts should stay 0, even though TrafficStats saw bytes, which
- // should be reflected in the next amortization run instead.
- amortizer()->AmortizeDataUse(CreateDataUse(0, 0),
- ExpectDataUseCallback(CreateDataUse(0, 0)));
- // Add the TrafficStats byte counts for this and the next amortization run.
- amortizer()->AddTrafficStats(100, 1000);
- AdvanceTime(kTrafficStatsQueryDelay);
- EXPECT_EQ(1, data_use_callback_call_count());
-
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(kAmortizationDelayHistogram,
- GetDelaySample(kTrafficStatsQueryDelay),
- 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
- }
-
- {
- base::HistogramTester histogram_tester;
-
- // Both byte counts should double, even though the TrafficStats byte counts
- // actually updated during the previous amortization run.
- amortizer()->AmortizeDataUse(
- CreateDataUse(50, 500),
- ExpectDataUseCallback(CreateDataUse(100, 1000)));
- AdvanceTime(kTrafficStatsQueryDelay);
- EXPECT_EQ(2, data_use_callback_call_count());
-
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 50, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 500, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 100, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 1000, 1);
- histogram_tester.ExpectUniqueSample(kAmortizationDelayHistogram,
- GetDelaySample(kTrafficStatsQueryDelay),
- 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
- }
-}
-
-TEST_F(TrafficStatsAmortizerTest, AmortizeWithZeroTxPreAmortizationBytes) {
- SkipFirstAmortizationRun();
-
- {
- base::HistogramTester histogram_tester;
-
- // The count of transmitted bytes starts at 0, so it should stay 0, and be
- // amortized in the next amortization run instead.
- amortizer()->AmortizeDataUse(CreateDataUse(0, 500),
- ExpectDataUseCallback(CreateDataUse(0, 1000)));
- // Add the TrafficStats byte counts for this and the next amortization run.
- amortizer()->AddTrafficStats(100, 1000);
- AdvanceTime(kTrafficStatsQueryDelay);
- EXPECT_EQ(1, data_use_callback_call_count());
-
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 500, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 1000, 1);
- histogram_tester.ExpectUniqueSample(kAmortizationDelayHistogram,
- GetDelaySample(kTrafficStatsQueryDelay),
- 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
- }
-
- {
- base::HistogramTester histogram_tester;
-
- // The count of transmitted bytes should double, even though they actually
- // updated during the previous amortization run.
- amortizer()->AmortizeDataUse(CreateDataUse(50, 0),
- ExpectDataUseCallback(CreateDataUse(100, 0)));
- AdvanceTime(kTrafficStatsQueryDelay);
- EXPECT_EQ(2, data_use_callback_call_count());
-
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 50, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 100, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(kAmortizationDelayHistogram,
- GetDelaySample(kTrafficStatsQueryDelay),
- 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
- }
-}
-
-TEST_F(TrafficStatsAmortizerTest, AmortizeWithZeroRxPreAmortizationBytes) {
- SkipFirstAmortizationRun();
-
- {
- base::HistogramTester histogram_tester;
-
- // The count of received bytes starts at 0, so it should stay 0, and be
- // amortized in the next amortization run instead.
- amortizer()->AmortizeDataUse(CreateDataUse(50, 0),
- ExpectDataUseCallback(CreateDataUse(100, 0)));
- // Add the TrafficStats byte counts for this and the next amortization run.
- amortizer()->AddTrafficStats(100, 1000);
- AdvanceTime(kTrafficStatsQueryDelay);
- EXPECT_EQ(1, data_use_callback_call_count());
-
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 50, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 100, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(kAmortizationDelayHistogram,
- GetDelaySample(kTrafficStatsQueryDelay),
- 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
- }
-
- {
- base::HistogramTester histogram_tester;
-
- // The count of received bytes should double, even though they actually
- // updated during the previous amortization run.
- amortizer()->AmortizeDataUse(CreateDataUse(0, 500),
- ExpectDataUseCallback(CreateDataUse(0, 1000)));
- AdvanceTime(kTrafficStatsQueryDelay);
- EXPECT_EQ(2, data_use_callback_call_count());
-
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 500, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 1000, 1);
- histogram_tester.ExpectUniqueSample(kAmortizationDelayHistogram,
- GetDelaySample(kTrafficStatsQueryDelay),
- 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
- }
-}
-
-TEST_F(TrafficStatsAmortizerTest, AmortizeAtMaxDelay) {
- SkipFirstAmortizationRun();
- base::HistogramTester histogram_tester;
-
- // Byte counts should double.
- amortizer()->AddTrafficStats(1000, 10000);
- amortizer()->AmortizeDataUse(CreateDataUse(50, 500),
- ExpectDataUseCallback(CreateDataUse(100, 1000)));
-
- // kSmallDelay is a delay that's shorter than the delay before TrafficStats
- // would be queried, where kMaxAmortizationDelay is a multiple of kSmallDelay.
- const base::TimeDelta kSmallDelay = kMaxAmortizationDelay / 10;
- EXPECT_LT(kSmallDelay, kTrafficStatsQueryDelay);
-
- // Simulate multiple cases of extra bytes being reported, each before
- // TrafficStats would be queried, until kMaxAmortizationDelay has elapsed.
- AdvanceTime(kSmallDelay);
- for (int64_t i = 0; i < kMaxAmortizationDelay / kSmallDelay - 1; ++i) {
- EXPECT_EQ(0, data_use_callback_call_count());
- amortizer()->OnExtraBytes(50, 500);
- AdvanceTime(kSmallDelay);
- }
-
- // The final time, the amortizer should have given up on waiting to query
- // TrafficStats and just have amortized as soon as it hit the deadline of
- // kMaxAmortizationDelay.
- EXPECT_EQ(1, data_use_callback_call_count());
-
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 50, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 500, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 100, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 1000, 1);
- histogram_tester.ExpectUniqueSample(kAmortizationDelayHistogram,
- GetDelaySample(kMaxAmortizationDelay), 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 1, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
-}
-
-TEST_F(TrafficStatsAmortizerTest, AmortizeAtMaxBufferSize) {
- SkipFirstAmortizationRun();
- base::HistogramTester histogram_tester;
-
- // Report (max buffer size + 1) consecutive DataUse objects, which will be
- // amortized immediately once the buffer exceeds maximum size.
- amortizer()->AddTrafficStats(100 * (kMaxDataUseBufferSize + 1),
- 1000 * (kMaxDataUseBufferSize + 1));
- for (size_t i = 0; i < kMaxDataUseBufferSize + 1; ++i) {
- EXPECT_EQ(0, data_use_callback_call_count());
- amortizer()->AmortizeDataUse(
- CreateDataUse(50, 500),
- ExpectDataUseCallback(CreateDataUse(100, 1000)));
- }
-
- const int kExpectedBufSize = static_cast<int>(kMaxDataUseBufferSize + 1);
- EXPECT_EQ(kExpectedBufSize, data_use_callback_call_count());
-
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram,
- 50 * kExpectedBufSize, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram,
- 500 * kExpectedBufSize, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram,
- 100 * kExpectedBufSize, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram,
- 1000 * kExpectedBufSize, 1);
- histogram_tester.ExpectUniqueSample(kAmortizationDelayHistogram, 0, 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram,
- kExpectedBufSize, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
-}
-
-TEST_F(TrafficStatsAmortizerTest, AmortizeCombinedDataUse) {
- SkipFirstAmortizationRun();
- base::HistogramTester histogram_tester;
-
- const GURL foo_url("http://foo.com");
- const GURL bar_url("http://bar.com");
-
- std::vector<std::unique_ptr<DataUse>> baz_sequence;
- const DataUseAmortizer::AmortizationCompleteCallback baz_callback =
- base::Bind(&AppendDataUseToSequence, &baz_sequence);
-
- std::vector<std::unique_ptr<DataUse>> qux_sequence;
- const DataUseAmortizer::AmortizationCompleteCallback qux_callback =
- base::Bind(&AppendDataUseToSequence, &qux_sequence);
-
- // Byte counts should double, with some DataUse objects combined together.
-
- // Two consecutive DataUse objects that are identical except for byte counts
- // and with the same callback should be combined.
- amortizer()->AmortizeDataUse(CreateDataUseWithURL(foo_url, 50, 500),
- baz_callback);
- amortizer()->AmortizeDataUse(CreateDataUseWithURL(foo_url, 100, 1000),
- baz_callback);
-
- // This DataUse object should not be combined with the previous one because it
- // has a different URL.
- amortizer()->AmortizeDataUse(CreateDataUseWithURL(bar_url, 50, 500),
- baz_callback);
-
- // This DataUse object should not be combined with the previous one because it
- // has a different callback.
- amortizer()->AmortizeDataUse(CreateDataUseWithURL(bar_url, 50, 500),
- qux_callback);
-
- // This DataUse object should not be combined with the previous foo/baz
- // DataUse objects because other DataUse objects were reported in-between.
- amortizer()->AmortizeDataUse(CreateDataUseWithURL(foo_url, 50, 500),
- baz_callback);
-
- // Simulate that TrafficStats saw double the number of reported bytes across
- // all reported DataUse.
- amortizer()->AddTrafficStats(600, 6000);
- AdvanceTime(kTrafficStatsQueryDelay);
-
- EXPECT_EQ(3U, baz_sequence.size());
- ExpectDataUse(CreateDataUseWithURL(foo_url, 300, 3000),
- std::move(baz_sequence[0]));
- ExpectDataUse(CreateDataUseWithURL(bar_url, 100, 1000),
- std::move(baz_sequence[1]));
- ExpectDataUse(CreateDataUseWithURL(foo_url, 100, 1000),
- std::move(baz_sequence[2]));
-
- EXPECT_EQ(1U, qux_sequence.size());
- ExpectDataUse(CreateDataUseWithURL(bar_url, 100, 1000),
- std::move(qux_sequence[0]));
-
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 300, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 3000, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 600, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 6000, 1);
- histogram_tester.ExpectUniqueSample(
- kAmortizationDelayHistogram, GetDelaySample(kTrafficStatsQueryDelay), 1);
- histogram_tester.ExpectUniqueSample(kBufferSizeOnFlushHistogram, 4, 1);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 1, 1);
-}
-
-TEST_F(TrafficStatsAmortizerTest, ConcurrentTabsHistogram) {
- SessionID kTabId1 = SessionID::FromSerializedValue(1);
- SessionID kTabId2 = SessionID::FromSerializedValue(2);
-
- SkipFirstAmortizationRun();
-
- {
- // Test data usage reported multiple times for two tabs.
- base::HistogramTester histogram_tester;
- amortizer()->SetNextTrafficStats(true, 0, 0);
- amortizer()->AmortizeDataUse(
- CreateDataUseWithTab(kTabId1, 50, 500),
- ExpectDataUseCallback(CreateDataUseWithTab(kTabId1, 100, 1000)));
- amortizer()->AmortizeDataUse(
- CreateDataUseWithTab(kTabId2, 100, 1000),
- ExpectDataUseCallback(CreateDataUseWithTab(kTabId2, 200, 2000)));
- amortizer()->AmortizeDataUse(
- CreateDataUseWithTab(kTabId1, 50, 500),
- ExpectDataUseCallback(CreateDataUseWithTab(kTabId1, 100, 1000)));
- amortizer()->AmortizeDataUse(
- CreateDataUseWithTab(kTabId2, 100, 1000),
- ExpectDataUseCallback(CreateDataUseWithTab(kTabId2, 200, 2000)));
- amortizer()->SetNextTrafficStats(true, 600, 6000);
- AdvanceTime(kTrafficStatsQueryDelay);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, 2, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationTxHistogram, 300, 1);
- histogram_tester.ExpectUniqueSample(kPreAmortizationRxHistogram, 3000, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationTxHistogram, 600, 1);
- histogram_tester.ExpectUniqueSample(kPostAmortizationRxHistogram, 6000, 1);
- }
-
- // Test data usage for 1-5 tabs.
- for (int32_t total_tabs = 1; total_tabs <= 5; ++total_tabs) {
- base::HistogramTester histogram_tester;
-
- for (int32_t i = 1; i <= total_tabs; ++i) {
- SessionID tab_id = SessionID::FromSerializedValue(i);
- amortizer()->AmortizeDataUse(
- CreateDataUseWithTab(tab_id, 100, 1000),
- ExpectDataUseCallback(CreateDataUseWithTab(tab_id, 200, 2000)));
- }
- amortizer()->AddTrafficStats(total_tabs * 200, total_tabs * 2000);
- AdvanceTime(kTrafficStatsQueryDelay);
- histogram_tester.ExpectUniqueSample(kConcurrentTabs, total_tabs, 1);
- }
-}
-
-} // namespace
-
-} // namespace android
-} // namespace data_usage
diff --git a/chromium/components/data_usage/core/BUILD.gn b/chromium/components/data_usage/core/BUILD.gn
deleted file mode 100644
index 6ce7f9863fb..00000000000
--- a/chromium/components/data_usage/core/BUILD.gn
+++ /dev/null
@@ -1,37 +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("core") {
- sources = [
- "data_use.cc",
- "data_use.h",
- "data_use_aggregator.cc",
- "data_use_aggregator.h",
- "data_use_amortizer.h",
- "data_use_annotator.h",
- ]
- deps = [
- "//base",
- "//components/sessions",
- "//net",
- "//url",
- ]
-}
-
-source_set("unit_tests") {
- testonly = true
- sources = [
- "data_use_aggregator_unittest.cc",
- ]
-
- configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
-
- deps = [
- ":core",
- "//base",
- "//base/test:test_support",
- "//net:test_support",
- "//testing/gtest",
- ]
-}
diff --git a/chromium/components/data_usage/core/DEPS b/chromium/components/data_usage/core/DEPS
deleted file mode 100644
index f9ac0a46ef9..00000000000
--- a/chromium/components/data_usage/core/DEPS
+++ /dev/null
@@ -1,5 +0,0 @@
-include_rules = [
- "+components/sessions",
- "+net",
- "+url",
-]
diff --git a/chromium/components/data_usage/core/data_use.cc b/chromium/components/data_usage/core/data_use.cc
deleted file mode 100644
index 8399884333f..00000000000
--- a/chromium/components/data_usage/core/data_use.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/data_usage/core/data_use.h"
-
-namespace data_usage {
-
-namespace {
-
-bool AreNonByteCountFieldsEqual(const DataUse& a, const DataUse& b) {
- return a.url == b.url && a.request_start == b.request_start &&
- a.site_for_cookies == b.site_for_cookies && a.tab_id == b.tab_id &&
- a.main_frame_global_request_id == b.main_frame_global_request_id &&
- a.connection_type == b.connection_type && a.mcc_mnc == b.mcc_mnc;
-}
-
-bool AreByteCountFieldsEqual(const DataUse& a, const DataUse& b) {
- return a.tx_bytes == b.tx_bytes && a.rx_bytes == b.rx_bytes;
-}
-
-} // namespace
-
-// static
-const DataUse::MainFrameGlobalRequestID
- DataUse::kInvalidMainFrameGlobalRequestID(-1, -1);
-
-DataUse::DataUse(const GURL& url,
- const base::TimeTicks& request_start,
- const GURL& site_for_cookies,
- SessionID tab_id,
- net::NetworkChangeNotifier::ConnectionType connection_type,
- const std::string& mcc_mnc,
- int64_t tx_bytes,
- int64_t rx_bytes)
- : url(url),
- request_start(request_start),
- site_for_cookies(site_for_cookies),
- tab_id(tab_id),
- main_frame_global_request_id(DataUse::kInvalidMainFrameGlobalRequestID),
- connection_type(connection_type),
- mcc_mnc(mcc_mnc),
- tx_bytes(tx_bytes),
- rx_bytes(rx_bytes) {}
-
-DataUse::DataUse(const DataUse& other) = default;
-
-DataUse::~DataUse() {}
-
-bool DataUse::operator==(const DataUse& other) const {
- return AreNonByteCountFieldsEqual(*this, other) &&
- AreByteCountFieldsEqual(*this, other);
-}
-
-bool DataUse::CanCombineWith(const DataUse& other) const {
- return AreNonByteCountFieldsEqual(*this, other);
-}
-
-} // namespace data_usage
diff --git a/chromium/components/data_usage/core/data_use.h b/chromium/components/data_usage/core/data_use.h
deleted file mode 100644
index 1762745f281..00000000000
--- a/chromium/components/data_usage/core/data_use.h
+++ /dev/null
@@ -1,75 +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_DATA_USAGE_CORE_DATA_USE_H_
-#define COMPONENTS_DATA_USAGE_CORE_DATA_USE_H_
-
-#include <stdint.h>
-
-#include <string>
-#include <utility>
-
-#include "base/time/time.h"
-#include "components/sessions/core/session_id.h"
-#include "net/base/network_change_notifier.h"
-#include "url/gurl.h"
-
-namespace data_usage {
-
-struct DataUse {
- // Wraps the content::GlobalRequestID.
- typedef std::pair<int, int> MainFrameGlobalRequestID;
-
- static const MainFrameGlobalRequestID kInvalidMainFrameGlobalRequestID;
-
- DataUse(const GURL& url,
- const base::TimeTicks& request_start,
- const GURL& site_for_cookies,
- SessionID tab_id,
- net::NetworkChangeNotifier::ConnectionType connection_type,
- const std::string& mcc_mnc,
- int64_t tx_bytes,
- int64_t rx_bytes);
-
- DataUse(const DataUse& other);
-
- ~DataUse();
-
- bool operator==(const DataUse& other) const;
-
- // Returns true if |this| and |other| are identical except for byte counts.
- bool CanCombineWith(const DataUse& other) const;
-
- GURL url;
- // The TimeTicks when the request that is associated with these bytes was
- // started.
- base::TimeTicks request_start;
- GURL site_for_cookies;
-
- // Tab id where the data use happens. When PlzNavigate is enabled tab id could
- // be invalid(-1) for the mainframe request since tab cannot be retrieved yet.
- // Could be invalid(-1) if the data use does not belong to a tab, for example
- // chrome-services traffic.
- SessionID tab_id;
-
- // content::GlobalRequestID of the mainframe request. This is populated only
- // when tab id cannot be retrieved of a mainframe request, and used to
- // attribute to its tab ID later.
- MainFrameGlobalRequestID main_frame_global_request_id;
-
- net::NetworkChangeNotifier::ConnectionType connection_type;
- // MCC+MNC (mobile country code + mobile network code) of the provider of the
- // SIM when the network traffic was exchanged. Set to empty string if SIM is
- // not present. |mcc_mnc| is set even if data was not exchanged on the
- // cellular network. For dual SIM phones, this is set to the MCC/MNC of the
- // SIM in slot 0.
- std::string mcc_mnc;
-
- int64_t tx_bytes;
- int64_t rx_bytes;
-};
-
-} // namespace data_usage
-
-#endif // COMPONENTS_DATA_USAGE_CORE_DATA_USE_H_
diff --git a/chromium/components/data_usage/core/data_use_aggregator.cc b/chromium/components/data_usage/core/data_use_aggregator.cc
deleted file mode 100644
index 33facf87cce..00000000000
--- a/chromium/components/data_usage/core/data_use_aggregator.cc
+++ /dev/null
@@ -1,135 +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/data_usage/core/data_use_aggregator.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "build/build_config.h"
-#include "components/data_usage/core/data_use.h"
-#include "net/base/load_timing_info.h"
-#include "net/base/network_change_notifier.h"
-#include "net/url_request/url_request.h"
-
-#if defined(OS_ANDROID)
-#include "net/android/network_library.h"
-#endif // OS_ANDROID
-
-namespace data_usage {
-
-DataUseAggregator::DataUseAggregator(
- std::unique_ptr<DataUseAnnotator> annotator,
- std::unique_ptr<DataUseAmortizer> amortizer)
- : annotator_(std::move(annotator)),
- amortizer_(std::move(amortizer)),
- connection_type_(net::NetworkChangeNotifier::GetConnectionType()),
- weak_ptr_factory_(this) {
-#if defined(OS_ANDROID)
- mcc_mnc_ = net::android::GetTelephonySimOperator();
-#endif // OS_ANDROID
- net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
-}
-
-DataUseAggregator::~DataUseAggregator() {
- net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
-}
-
-void DataUseAggregator::AddObserver(Observer* observer) {
- DCHECK(thread_checker_.CalledOnValidThread());
- observer_list_.AddObserver(observer);
-}
-
-void DataUseAggregator::RemoveObserver(Observer* observer) {
- DCHECK(thread_checker_.CalledOnValidThread());
- observer_list_.RemoveObserver(observer);
-}
-
-void DataUseAggregator::ReportDataUse(net::URLRequest* request,
- int64_t tx_bytes,
- int64_t rx_bytes) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- net::LoadTimingInfo load_timing_info;
- request->GetLoadTimingInfo(&load_timing_info);
-
- std::unique_ptr<DataUse> data_use(new DataUse(
- request->url(), load_timing_info.request_start,
- request->site_for_cookies(), /*tab_id=*/SessionID::InvalidValue(),
- connection_type_, mcc_mnc_, tx_bytes, rx_bytes));
-
- if (!annotator_) {
- PassDataUseToAmortizer(std::move(data_use));
- return;
- }
-
- // As an optimization, re-use a lazily initialized callback object for every
- // call into |annotator_|, so that a new callback object doesn't have to be
- // allocated and held onto every time.
- if (annotation_callback_.is_null()) {
- annotation_callback_ =
- base::Bind(&DataUseAggregator::PassDataUseToAmortizer, GetWeakPtr());
- }
- annotator_->Annotate(request, std::move(data_use), annotation_callback_);
-}
-
-void DataUseAggregator::ReportOffTheRecordDataUse(int64_t tx_bytes,
- int64_t rx_bytes) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (!amortizer_)
- return;
-
- amortizer_->OnExtraBytes(tx_bytes, rx_bytes);
-}
-
-base::WeakPtr<DataUseAggregator> DataUseAggregator::GetWeakPtr() {
- DCHECK(thread_checker_.CalledOnValidThread());
- return weak_ptr_factory_.GetWeakPtr();
-}
-
-void DataUseAggregator::OnNetworkChanged(
- net::NetworkChangeNotifier::ConnectionType type) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- connection_type_ = type;
-#if defined(OS_ANDROID)
- mcc_mnc_ = net::android::GetTelephonySimOperator();
-#endif // OS_ANDROID
-}
-
-void DataUseAggregator::SetMccMncForTests(const std::string& mcc_mnc) {
- DCHECK(thread_checker_.CalledOnValidThread());
- mcc_mnc_ = mcc_mnc;
-}
-
-void DataUseAggregator::PassDataUseToAmortizer(
- std::unique_ptr<DataUse> data_use) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(data_use);
-
- if (!amortizer_) {
- OnAmortizationComplete(std::move(data_use));
- return;
- }
-
- // As an optimization, re-use a lazily initialized callback object for every
- // call into |amortizer_|, so that a new callback object doesn't have to be
- // allocated and held onto every time. This also allows the |amortizer_| to
- // combine together similar DataUse objects in its buffer if applicable.
- if (amortization_callback_.is_null()) {
- amortization_callback_ =
- base::Bind(&DataUseAggregator::OnAmortizationComplete, GetWeakPtr());
- }
- amortizer_->AmortizeDataUse(std::move(data_use), amortization_callback_);
-}
-
-void DataUseAggregator::OnAmortizationComplete(
- std::unique_ptr<DataUse> amortized_data_use) {
- DCHECK(thread_checker_.CalledOnValidThread());
- for (Observer& observer : observer_list_)
- observer.OnDataUse(*amortized_data_use);
-}
-
-} // namespace data_usage
diff --git a/chromium/components/data_usage/core/data_use_aggregator.h b/chromium/components/data_usage/core/data_use_aggregator.h
deleted file mode 100644
index 7e6088f9f19..00000000000
--- a/chromium/components/data_usage/core/data_use_aggregator.h
+++ /dev/null
@@ -1,108 +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_DATA_USAGE_CORE_DATA_USE_AGGREGATOR_H_
-#define COMPONENTS_DATA_USAGE_CORE_DATA_USE_AGGREGATOR_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/observer_list.h"
-#include "base/threading/thread_checker.h"
-#include "components/data_usage/core/data_use_amortizer.h"
-#include "components/data_usage/core/data_use_annotator.h"
-#include "net/base/network_change_notifier.h"
-
-namespace net {
-class URLRequest;
-}
-
-namespace data_usage {
-
-struct DataUse;
-
-// Class that collects and aggregates network usage, reporting the usage to
-// observers. Should only be used on the IO thread.
-class DataUseAggregator
- : public net::NetworkChangeNotifier::NetworkChangeObserver {
- public:
- class Observer {
- public:
- virtual ~Observer() {}
- virtual void OnDataUse(const DataUse& data_use) = 0;
- };
-
- // Constructs a new DataUseAggregator with the given |annotator| and
- // |amortizer|. A NULL |annotator| will be treated as a no-op annotator, and a
- // NULL |amortizer| will be treated as a no-op amortizer.
- DataUseAggregator(std::unique_ptr<DataUseAnnotator> annotator,
- std::unique_ptr<DataUseAmortizer> amortizer);
-
- ~DataUseAggregator() override;
-
- void AddObserver(Observer* observer);
- void RemoveObserver(Observer* observer);
-
- // Virtual for testing.
- virtual void ReportDataUse(net::URLRequest* request,
- int64_t tx_bytes,
- int64_t rx_bytes);
-
- // Account for off-the-record data use. This usage is only kept track of here
- // so that it can be taken out of any amortized data usage calculations, and a
- // per-request breakdown of off-the-record data usage will never leave the
- // DataUseAggregator.
- // Virtual for testing.
- virtual void ReportOffTheRecordDataUse(int64_t tx_bytes, int64_t rx_bytes);
-
- base::WeakPtr<DataUseAggregator> GetWeakPtr();
-
- protected:
- // net::NetworkChangeNotifier::NetworkChangeObserver implementation.
- // Protected for testing.
- void OnNetworkChanged(
- net::NetworkChangeNotifier::ConnectionType type) override;
-
- // Protected for testing.
- void SetMccMncForTests(const std::string& mcc_mnc);
-
- private:
- // Passes |data_use| to |amortizer_| if it exists, or calls
- // OnAmortizationComplete directly if |amortizer_| doesn't exist.
- void PassDataUseToAmortizer(std::unique_ptr<DataUse> data_use);
-
- // Notifies observers with the data use from |amortized_data_use|.
- void OnAmortizationComplete(std::unique_ptr<DataUse> amortized_data_use);
-
- base::ThreadChecker thread_checker_;
- std::unique_ptr<DataUseAnnotator> annotator_;
- std::unique_ptr<DataUseAmortizer> amortizer_;
- base::ObserverList<Observer> observer_list_;
-
- // Current connection type as notified by NetworkChangeNotifier.
- net::NetworkChangeNotifier::ConnectionType connection_type_;
-
- // MCC+MNC (mobile country code + mobile network code) of the current SIM
- // provider. Set to empty string if SIM is not present. |mcc_mnc_| is set
- // even if the current active network is not a cellular network.
- std::string mcc_mnc_;
-
- // As an optimization, re-use the same callbacks to avoid creating and
- // allocating a new Callback object for each call into the |annotator_| or
- // |amortizer_|. These callbacks are lazily initialized.
- DataUseAnnotator::DataUseConsumerCallback annotation_callback_;
- DataUseAmortizer::AmortizationCompleteCallback amortization_callback_;
-
- base::WeakPtrFactory<DataUseAggregator> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(DataUseAggregator);
-};
-
-} // namespace data_usage
-
-#endif // COMPONENTS_DATA_USAGE_CORE_DATA_USE_AGGREGATOR_H_
diff --git a/chromium/components/data_usage/core/data_use_aggregator_unittest.cc b/chromium/components/data_usage/core/data_use_aggregator_unittest.cc
deleted file mode 100644
index 01dd0a8dd99..00000000000
--- a/chromium/components/data_usage/core/data_use_aggregator_unittest.cc
+++ /dev/null
@@ -1,434 +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/data_usage/core/data_use_aggregator.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/containers/span.h"
-#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/time/time.h"
-#include "components/data_usage/core/data_use.h"
-#include "components/data_usage/core/data_use_amortizer.h"
-#include "components/data_usage/core/data_use_annotator.h"
-#include "net/base/load_timing_info.h"
-#include "net/base/network_change_notifier.h"
-#include "net/base/network_delegate_impl.h"
-#include "net/socket/socket_test_util.h"
-#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace data_usage {
-
-namespace {
-
-base::TimeTicks GetRequestStart(const net::URLRequest& request) {
- net::LoadTimingInfo load_timing_info;
- request.GetLoadTimingInfo(&load_timing_info);
- return load_timing_info.request_start;
-}
-
-// Test class that can set the network operator's MCCMNC.
-class TestDataUseAggregator : public DataUseAggregator {
- public:
- TestDataUseAggregator(std::unique_ptr<DataUseAnnotator> annotator,
- std::unique_ptr<DataUseAmortizer> amortizer)
- : DataUseAggregator(std::move(annotator), std::move(amortizer)) {}
-
- ~TestDataUseAggregator() override {}
-
- private:
- friend class TestNetworkChangeNotifier;
- using DataUseAggregator::OnNetworkChanged;
- using DataUseAggregator::SetMccMncForTests;
-};
-
-// Override NetworkChangeNotifier to simulate connection type changes for tests.
-class TestNetworkChangeNotifier : public net::NetworkChangeNotifier {
- public:
- explicit TestNetworkChangeNotifier(TestDataUseAggregator* data_use_aggregator)
- : data_use_aggregator_(data_use_aggregator),
- connection_type_to_return_(
- net::NetworkChangeNotifier::CONNECTION_UNKNOWN) {}
-
- // Simulates a change of the connection type to |type|.
- void SimulateNetworkConnectionChange(ConnectionType type,
- const std::string& mcc_mnc) {
- connection_type_to_return_ = type;
- data_use_aggregator_->OnNetworkChanged(type);
- data_use_aggregator_->SetMccMncForTests(mcc_mnc);
- }
-
- ConnectionType GetCurrentConnectionType() const override {
- return connection_type_to_return_;
- }
-
- private:
- TestDataUseAggregator* data_use_aggregator_;
-
- // The currently simulated network connection type.
- ConnectionType connection_type_to_return_;
-
- DISALLOW_COPY_AND_ASSIGN(TestNetworkChangeNotifier);
-};
-
-// A fake DataUseAnnotator that sets the tab ID of DataUse objects to a
-// predetermined fake tab ID.
-class FakeDataUseAnnotator : public DataUseAnnotator {
- public:
- FakeDataUseAnnotator() : tab_id_(SessionID::InvalidValue()) {}
- ~FakeDataUseAnnotator() override {}
-
- void Annotate(
- net::URLRequest* request,
- std::unique_ptr<DataUse> data_use,
- const base::Callback<void(std::unique_ptr<DataUse>)>& callback) override {
- data_use->tab_id = tab_id_;
- callback.Run(std::move(data_use));
- }
-
- void set_tab_id(SessionID tab_id) { tab_id_ = tab_id; }
-
- private:
- SessionID tab_id_;
-
- DISALLOW_COPY_AND_ASSIGN(FakeDataUseAnnotator);
-};
-
-// Test DataUseAmortizer that doubles the bytes of all DataUse objects it sees.
-class DoublingAmortizer : public DataUseAmortizer {
- public:
- DoublingAmortizer() {}
- ~DoublingAmortizer() override {}
-
- void AmortizeDataUse(std::unique_ptr<DataUse> data_use,
- const AmortizationCompleteCallback& callback) override {
- data_use->tx_bytes *= 2;
- data_use->rx_bytes *= 2;
- callback.Run(std::move(data_use));
- }
-
- void OnExtraBytes(int64_t extra_tx_bytes, int64_t extra_rx_bytes) override {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DoublingAmortizer);
-};
-
-// A network delegate that reports all received and sent network bytes to a
-// DataUseAggregator.
-class ReportingNetworkDelegate : public net::NetworkDelegateImpl {
- public:
- // The simulated context for the data usage of a net::URLRequest.
- struct DataUseContext {
- DataUseContext()
- : tab_id(SessionID::InvalidValue()),
- connection_type(net::NetworkChangeNotifier::CONNECTION_UNKNOWN) {}
-
- DataUseContext(SessionID tab_id,
- net::NetworkChangeNotifier::ConnectionType connection_type,
- const std::string& mcc_mnc)
- : tab_id(tab_id), connection_type(connection_type), mcc_mnc(mcc_mnc) {}
-
- SessionID tab_id;
- net::NetworkChangeNotifier::ConnectionType connection_type;
- std::string mcc_mnc;
- };
-
- typedef std::map<const net::URLRequest*, DataUseContext> DataUseContextMap;
-
- // Constructs a ReportingNetworkDelegate. |fake_data_use_annotator| can be
- // NULL, indicating that no annotator is in use and no requests should be
- // annotated with tab IDs.
- ReportingNetworkDelegate(
- TestDataUseAggregator* data_use_aggregator,
- FakeDataUseAnnotator* fake_data_use_annotator,
- TestNetworkChangeNotifier* test_network_change_notifier)
- : data_use_aggregator_(data_use_aggregator),
- fake_data_use_annotator_(fake_data_use_annotator),
- test_network_change_notifier_(test_network_change_notifier) {}
-
- ~ReportingNetworkDelegate() override {}
-
- void set_data_use_context_map(const DataUseContextMap& data_use_context_map) {
- data_use_context_map_ = data_use_context_map;
- }
-
- private:
- void UpdateDataUseContext(const net::URLRequest& request) {
- DataUseContextMap::const_iterator data_use_context_it =
- data_use_context_map_.find(&request);
- DataUseContext data_use_context =
- data_use_context_it == data_use_context_map_.end()
- ? DataUseContext()
- : data_use_context_it->second;
-
- if (fake_data_use_annotator_)
- fake_data_use_annotator_->set_tab_id(data_use_context.tab_id);
-
- if (test_network_change_notifier_->GetCurrentConnectionType() !=
- data_use_context.connection_type) {
- test_network_change_notifier_->SimulateNetworkConnectionChange(
- data_use_context.connection_type, data_use_context.mcc_mnc);
- }
- }
-
- void OnNetworkBytesReceived(net::URLRequest* request,
- int64_t bytes_received) override {
- UpdateDataUseContext(*request);
- data_use_aggregator_->ReportDataUse(request, 0 /* tx_bytes */,
- bytes_received);
- }
-
- void OnNetworkBytesSent(net::URLRequest* request,
- int64_t bytes_sent) override {
- UpdateDataUseContext(*request);
- data_use_aggregator_->ReportDataUse(request, bytes_sent, 0 /* rx_bytes */);
- }
-
- TestDataUseAggregator* data_use_aggregator_;
- FakeDataUseAnnotator* fake_data_use_annotator_;
- TestNetworkChangeNotifier* test_network_change_notifier_;
- DataUseContextMap data_use_context_map_;
-
- DISALLOW_COPY_AND_ASSIGN(ReportingNetworkDelegate);
-};
-
-// An observer that keeps track of all the data use it observed.
-class TestObserver : public DataUseAggregator::Observer {
- public:
- explicit TestObserver(DataUseAggregator* data_use_aggregator)
- : data_use_aggregator_(data_use_aggregator) {
- data_use_aggregator_->AddObserver(this);
- }
-
- ~TestObserver() override { data_use_aggregator_->RemoveObserver(this); }
-
- void OnDataUse(const DataUse& data_use) override {
- observed_data_use_.push_back(data_use);
- }
-
- const std::vector<DataUse>& observed_data_use() const {
- return observed_data_use_;
- }
-
- private:
- DataUseAggregator* data_use_aggregator_;
- std::vector<DataUse> observed_data_use_;
-
- DISALLOW_COPY_AND_ASSIGN(TestObserver);
-};
-
-class DataUseAggregatorTest : public testing::Test {
- public:
- DataUseAggregatorTest() {}
- ~DataUseAggregatorTest() override {}
-
- void Initialize(std::unique_ptr<FakeDataUseAnnotator> annotator,
- std::unique_ptr<DataUseAmortizer> amortizer) {
- // Destroy objects that have dependencies on other objects here in the
- // reverse order that they are created.
- context_.reset();
- reporting_network_delegate_.reset();
- mock_socket_factory_.reset();
- test_network_change_notifier_.reset();
- test_observer_.reset();
-
- // Initialize testing objects.
- FakeDataUseAnnotator* fake_data_use_annotator = annotator.get();
- data_use_aggregator_.reset(
- new TestDataUseAggregator(std::move(annotator), std::move(amortizer)));
- test_observer_.reset(new TestObserver(data_use_aggregator_.get()));
- test_network_change_notifier_.reset(
- new TestNetworkChangeNotifier(data_use_aggregator_.get()));
- mock_socket_factory_.reset(new net::MockClientSocketFactory());
- reporting_network_delegate_.reset(new ReportingNetworkDelegate(
- data_use_aggregator_.get(), fake_data_use_annotator,
- test_network_change_notifier_.get()));
-
- context_.reset(new net::TestURLRequestContext(true));
- context_->set_client_socket_factory(mock_socket_factory_.get());
- context_->set_network_delegate(reporting_network_delegate_.get());
- context_->Init();
- }
-
- std::unique_ptr<net::URLRequest> ExecuteRequest(
- const GURL& url,
- const GURL& site_for_cookies,
- SessionID tab_id,
- net::NetworkChangeNotifier::ConnectionType connection_type,
- const std::string& mcc_mnc) {
- net::MockRead reads[] = {
- net::MockRead("HTTP/1.1 200 OK\r\n\r\n"), net::MockRead("hello world"),
- net::MockRead(net::SYNCHRONOUS, net::OK),
- };
- net::StaticSocketDataProvider socket(reads, base::span<net::MockWrite>());
- mock_socket_factory_->AddSocketDataProvider(&socket);
-
- net::TestDelegate delegate;
- std::unique_ptr<net::URLRequest> request = context_->CreateRequest(
- url, net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
- request->set_site_for_cookies(site_for_cookies);
-
- ReportingNetworkDelegate::DataUseContextMap data_use_context_map;
- data_use_context_map[request.get()] =
- ReportingNetworkDelegate::DataUseContext(tab_id, connection_type,
- mcc_mnc);
- reporting_network_delegate_->set_data_use_context_map(data_use_context_map);
-
- request->Start();
- base::RunLoop().RunUntilIdle();
-
- return request;
- }
-
- ReportingNetworkDelegate* reporting_network_delegate() {
- return reporting_network_delegate_.get();
- }
-
- DataUseAggregator* data_use_aggregator() {
- return data_use_aggregator_.get();
- }
-
- net::MockClientSocketFactory* mock_socket_factory() {
- return mock_socket_factory_.get();
- }
-
- net::TestURLRequestContext* context() { return context_.get(); }
-
- TestObserver* test_observer() { return test_observer_.get(); }
-
- private:
- base::MessageLoopForIO loop_;
- std::unique_ptr<TestDataUseAggregator> data_use_aggregator_;
- std::unique_ptr<TestObserver> test_observer_;
- std::unique_ptr<TestNetworkChangeNotifier> test_network_change_notifier_;
- std::unique_ptr<net::MockClientSocketFactory> mock_socket_factory_;
- std::unique_ptr<ReportingNetworkDelegate> reporting_network_delegate_;
- std::unique_ptr<net::TestURLRequestContext> context_;
-
- DISALLOW_COPY_AND_ASSIGN(DataUseAggregatorTest);
-};
-
-TEST_F(DataUseAggregatorTest, ReportDataUse) {
- const struct {
- bool use_annotator;
- bool use_amortizer;
- bool expect_tab_ids;
- int64_t expected_amortization_multiple;
- } kTestCases[] = {
- {false, false, false, 1},
- {false, true, false, 2},
- {true, false, true, 1},
- {true, true, true, 2},
- };
-
- for (const auto& test_case : kTestCases) {
- std::unique_ptr<FakeDataUseAnnotator> annotator(
- test_case.use_annotator ? new FakeDataUseAnnotator() : nullptr);
- std::unique_ptr<DataUseAmortizer> amortizer(
- test_case.use_amortizer ? new DoublingAmortizer() : nullptr);
-
- Initialize(std::move(annotator), std::move(amortizer));
-
- const SessionID kFooTabId = SessionID::FromSerializedValue(10);
- const net::NetworkChangeNotifier::ConnectionType kFooConnectionType =
- net::NetworkChangeNotifier::CONNECTION_2G;
- const std::string kFooMccMnc = "foo_mcc_mnc";
- std::unique_ptr<net::URLRequest> foo_request =
- ExecuteRequest(GURL("http://foo.com"), GURL("http://foofirstparty.com"),
- kFooTabId, kFooConnectionType, kFooMccMnc);
-
- const SessionID kBarTabId = SessionID::FromSerializedValue(20);
- const net::NetworkChangeNotifier::ConnectionType kBarConnectionType =
- net::NetworkChangeNotifier::CONNECTION_WIFI;
- const std::string kBarMccMnc = "bar_mcc_mnc";
- std::unique_ptr<net::URLRequest> bar_request =
- ExecuteRequest(GURL("http://bar.com"), GURL("http://barfirstparty.com"),
- kBarTabId, kBarConnectionType, kBarMccMnc);
-
- auto data_use_it = test_observer()->observed_data_use().begin();
-
- // First, the |foo_request| data use should have happened.
- int64_t observed_foo_tx_bytes = 0, observed_foo_rx_bytes = 0;
- while (data_use_it != test_observer()->observed_data_use().end() &&
- data_use_it->url == "http://foo.com/") {
- EXPECT_EQ(GetRequestStart(*foo_request), data_use_it->request_start);
- EXPECT_EQ(GURL("http://foofirstparty.com"),
- data_use_it->site_for_cookies);
-
- if (test_case.expect_tab_ids)
- EXPECT_EQ(kFooTabId, data_use_it->tab_id);
- else
- EXPECT_FALSE(data_use_it->tab_id.is_valid());
-
- EXPECT_EQ(kFooConnectionType, data_use_it->connection_type);
- EXPECT_EQ(kFooMccMnc, data_use_it->mcc_mnc);
-
- observed_foo_tx_bytes += data_use_it->tx_bytes;
- observed_foo_rx_bytes += data_use_it->rx_bytes;
- ++data_use_it;
- }
- EXPECT_EQ(foo_request->GetTotalSentBytes() *
- test_case.expected_amortization_multiple,
- observed_foo_tx_bytes);
- EXPECT_EQ(foo_request->GetTotalReceivedBytes() *
- test_case.expected_amortization_multiple,
- observed_foo_rx_bytes);
-
- // Then, the |bar_request| data use should have happened.
- int64_t observed_bar_tx_bytes = 0, observed_bar_rx_bytes = 0;
- while (data_use_it != test_observer()->observed_data_use().end()) {
- EXPECT_EQ(GURL("http://bar.com"), data_use_it->url);
- EXPECT_EQ(GetRequestStart(*bar_request), data_use_it->request_start);
- EXPECT_EQ(GURL("http://barfirstparty.com"),
- data_use_it->site_for_cookies);
-
- if (test_case.expect_tab_ids)
- EXPECT_EQ(kBarTabId, data_use_it->tab_id);
- else
- EXPECT_FALSE(data_use_it->tab_id.is_valid());
-
- EXPECT_EQ(kBarConnectionType, data_use_it->connection_type);
- EXPECT_EQ(kBarMccMnc, data_use_it->mcc_mnc);
-
- observed_bar_tx_bytes += data_use_it->tx_bytes;
- observed_bar_rx_bytes += data_use_it->rx_bytes;
- ++data_use_it;
- }
- EXPECT_EQ(bar_request->GetTotalSentBytes() *
- test_case.expected_amortization_multiple,
- observed_bar_tx_bytes);
- EXPECT_EQ(bar_request->GetTotalReceivedBytes() *
- test_case.expected_amortization_multiple,
- observed_bar_rx_bytes);
- }
-}
-
-TEST_F(DataUseAggregatorTest, ReportOffTheRecordDataUse) {
- Initialize(std::unique_ptr<FakeDataUseAnnotator>(new FakeDataUseAnnotator()),
- std::unique_ptr<DataUseAmortizer>(new DoublingAmortizer()));
-
- // Off the record data use should not be reported to observers.
- data_use_aggregator()->ReportOffTheRecordDataUse(1000, 1000);
- base::RunLoop().RunUntilIdle();
- EXPECT_EQ(static_cast<size_t>(0),
- test_observer()->observed_data_use().size());
-}
-
-} // namespace
-
-} // namespace data_usage
diff --git a/chromium/components/data_usage/core/data_use_amortizer.h b/chromium/components/data_usage/core/data_use_amortizer.h
deleted file mode 100644
index 7cdb7c2724a..00000000000
--- a/chromium/components/data_usage/core/data_use_amortizer.h
+++ /dev/null
@@ -1,43 +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_DATA_USAGE_CORE_DATA_USE_AMORTIZER_H_
-#define COMPONENTS_DATA_USAGE_CORE_DATA_USE_AMORTIZER_H_
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/callback.h"
-
-namespace data_usage {
-
-struct DataUse;
-
-// Class that takes in DataUse and amortizes any extra data usage overhead
-// across DataUse objects.
-class DataUseAmortizer {
- public:
- typedef base::Callback<void(std::unique_ptr<DataUse>)>
- AmortizationCompleteCallback;
-
- virtual ~DataUseAmortizer() {}
-
- // Amortizes overhead into |data_use|, then passes the it to |callback| once
- // amortization is complete. Amortizers that perform buffering may combine
- // together |data_use| objects with the same |callback| if the |data_use|
- // objects are identical in all ways but their byte counts.
- virtual void AmortizeDataUse(
- std::unique_ptr<DataUse> data_use,
- const AmortizationCompleteCallback& callback) = 0;
-
- // Notifies the DataUseAmortizer that some extra bytes have been transferred
- // that aren't associated with any DataUse objects (e.g. off-the-record
- // traffic).
- virtual void OnExtraBytes(int64_t extra_tx_bytes, int64_t extra_rx_bytes) = 0;
-};
-
-} // namespace data_usage
-
-#endif // COMPONENTS_DATA_USAGE_CORE_DATA_USE_AMORTIZER_H_
diff --git a/chromium/components/data_usage/core/data_use_annotator.h b/chromium/components/data_usage/core/data_use_annotator.h
deleted file mode 100644
index 402743cc90b..00000000000
--- a/chromium/components/data_usage/core/data_use_annotator.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_DATA_USAGE_CORE_DATA_USE_ANNOTATOR_H_
-#define COMPONENTS_DATA_USAGE_CORE_DATA_USE_ANNOTATOR_H_
-
-#include <memory>
-
-#include "base/callback.h"
-
-namespace net {
-class URLRequest;
-}
-
-namespace data_usage {
-
-struct DataUse;
-
-// Interface for an object that can annotate DataUse objects with additional
-// information.
-class DataUseAnnotator {
- public:
- typedef base::Callback<void(std::unique_ptr<DataUse>)>
- DataUseConsumerCallback;
-
- virtual ~DataUseAnnotator() {}
-
- // Annotate |data_use| with additional information, possibly from |request|,
- // before passing the annotated |data_use| to |callback|. |request| is passed
- // in as a non-const pointer so that the DataUseAnnotator can add UserData on
- // to the |request| if desired.
- virtual void Annotate(net::URLRequest* request,
- std::unique_ptr<DataUse> data_use,
- const DataUseConsumerCallback& callback) = 0;
-};
-
-} // namespace data_usage
-
-#endif // COMPONENTS_DATA_USAGE_CORE_DATA_USE_ANNOTATOR_H_
diff --git a/chromium/components/data_use_measurement/core/data_use_measurement.cc b/chromium/components/data_use_measurement/core/data_use_measurement.cc
index 7468983e747..bbb32dd1fe5 100644
--- a/chromium/components/data_use_measurement/core/data_use_measurement.cc
+++ b/chromium/components/data_use_measurement/core/data_use_measurement.cc
@@ -186,6 +186,15 @@ void DataUseMeasurement::OnCompleted(const net::URLRequest& request,
RecordFavIconDataUse(request);
}
+// static
+DataUseUserData::DataUseContentType
+DataUseMeasurement::GetContentTypeForRequest(const net::URLRequest& request) {
+ DataUseUserData* attached_user_data = static_cast<DataUseUserData*>(
+ request.GetUserData(DataUseUserData::kUserDataKey));
+ return attached_user_data ? attached_user_data->content_type()
+ : DataUseUserData::OTHER;
+}
+
void DataUseMeasurement::ReportDataUseUMA(const net::URLRequest& request,
TrafficDirection dir,
int64_t bytes) {
@@ -434,22 +443,12 @@ void DataUseMeasurement::RecordContentTypeHistogram(
: (!is_tab_visible ? DataUseUserData::VIDEO_TABBACKGROUND
: DataUseUserData::VIDEO);
}
- // Use the more primitive STATIC_HISTOGRAM_POINTER_BLOCK macro because the
- // simple UMA_HISTOGRAM_ENUMERATION macros don't expose 'AddKiB'.
if (is_user_traffic) {
- STATIC_HISTOGRAM_POINTER_BLOCK(
- "DataUse.ContentType.UserTrafficKB", AddKiB(content_type, bytes),
- base::LinearHistogram::FactoryGet(
- "DataUse.ContentType.UserTrafficKB", 1, DataUseUserData::TYPE_MAX,
- DataUseUserData::TYPE_MAX + 1,
- base::HistogramBase::kUmaTargetedHistogramFlag));
+ UMA_HISTOGRAM_SCALED_ENUMERATION("DataUse.ContentType.UserTrafficKB",
+ content_type, bytes, 1024);
} else {
- STATIC_HISTOGRAM_POINTER_BLOCK(
- "DataUse.ContentType.ServicesKB", AddKiB(content_type, bytes),
- base::LinearHistogram::FactoryGet(
- "DataUse.ContentType.ServicesKB", 1, DataUseUserData::TYPE_MAX,
- DataUseUserData::TYPE_MAX + 1,
- base::HistogramBase::kUmaTargetedHistogramFlag));
+ UMA_HISTOGRAM_SCALED_ENUMERATION("DataUse.ContentType.ServicesKB",
+ content_type, bytes, 1024);
}
}
@@ -465,7 +464,8 @@ void DataUseMeasurement::RecordPageTransitionUMA(
}
}
-bool DataUseMeasurement::IsUserRequest(const net::URLRequest& request) const {
+// static
+bool DataUseMeasurement::IsUserRequest(const net::URLRequest& request) {
static const std::set<int32_t> kUserInitiatedTrafficAnnotations = {
COMPUTE_NETWORK_TRAFFIC_ANNOTATION_ID_HASH(
"blink_extension_resource_loader"),
diff --git a/chromium/components/data_use_measurement/core/data_use_measurement.h b/chromium/components/data_use_measurement/core/data_use_measurement.h
index f72dc860416..0dee70c6587 100644
--- a/chromium/components/data_use_measurement/core/data_use_measurement.h
+++ b/chromium/components/data_use_measurement/core/data_use_measurement.h
@@ -41,6 +41,14 @@ class URLRequestClassifier;
// http://crbug.com/527460
class DataUseMeasurement {
public:
+ // Returns true if the |request| is initiated by user traffic.
+ static bool IsUserRequest(const net::URLRequest& request);
+
+ // Returns the content-type saved in the request userdata when the response
+ // headers were received.
+ static DataUseUserData::DataUseContentType GetContentTypeForRequest(
+ const net::URLRequest& request);
+
DataUseMeasurement(
std::unique_ptr<URLRequestClassifier> url_request_classifier,
const metrics::UpdateUsagePrefCallbackType& metrics_data_use_forwarder,
@@ -160,9 +168,6 @@ class DataUseMeasurement {
bool is_tab_visible,
int64_t bytes);
- // Returns true if the |request| is initiated by user traffic.
- bool IsUserRequest(const net::URLRequest& request) const;
-
// Classifier for identifying if an URL request is user initiated.
std::unique_ptr<URLRequestClassifier> url_request_classifier_;
diff --git a/chromium/components/data_use_measurement/core/data_use_measurement_unittest.cc b/chromium/components/data_use_measurement/core/data_use_measurement_unittest.cc
index 599909eb2d3..cf6c15ecd9f 100644
--- a/chromium/components/data_use_measurement/core/data_use_measurement_unittest.cc
+++ b/chromium/components/data_use_measurement/core/data_use_measurement_unittest.cc
@@ -10,7 +10,7 @@
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "build/build_config.h"
#include "components/data_use_measurement/core/data_use_ascriber.h"
#include "components/data_use_measurement/core/data_use_recorder.h"
diff --git a/chromium/components/data_use_measurement/core/data_use_network_delegate.cc b/chromium/components/data_use_measurement/core/data_use_network_delegate.cc
index 3a5f1d9e8c4..0ab9e375b5b 100644
--- a/chromium/components/data_use_measurement/core/data_use_network_delegate.cc
+++ b/chromium/components/data_use_measurement/core/data_use_network_delegate.cc
@@ -29,7 +29,6 @@ DataUseNetworkDelegate::~DataUseNetworkDelegate() {}
void DataUseNetworkDelegate::OnBeforeURLRequestInternal(
net::URLRequest* request,
- const net::CompletionCallback& callback,
GURL* new_url) {
ascriber_->OnBeforeUrlRequest(request);
data_use_measurement_.OnBeforeURLRequest(request);
@@ -43,7 +42,6 @@ void DataUseNetworkDelegate::OnBeforeRedirectInternal(
void DataUseNetworkDelegate::OnHeadersReceivedInternal(
net::URLRequest* request,
- const net::CompletionCallback& callback,
const net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
GURL* allowed_unsafe_redirect_url) {
@@ -65,7 +63,8 @@ void DataUseNetworkDelegate::OnNetworkBytesSentInternal(
}
void DataUseNetworkDelegate::OnCompletedInternal(net::URLRequest* request,
- bool started) {
+ bool started,
+ int net_error) {
ascriber_->OnUrlRequestCompleted(request, started);
data_use_measurement_.OnCompleted(*request, started);
}
diff --git a/chromium/components/data_use_measurement/core/data_use_network_delegate.h b/chromium/components/data_use_measurement/core/data_use_network_delegate.h
index 014c683d71b..5a80d9aa95c 100644
--- a/chromium/components/data_use_measurement/core/data_use_network_delegate.h
+++ b/chromium/components/data_use_measurement/core/data_use_network_delegate.h
@@ -39,7 +39,6 @@ class DataUseNetworkDelegate : public net::LayeredNetworkDelegate {
// LayeredNetworkDelegate:
void OnBeforeURLRequestInternal(net::URLRequest* request,
- const net::CompletionCallback& callback,
GURL* new_url) override;
void OnBeforeRedirectInternal(net::URLRequest* request,
@@ -47,7 +46,6 @@ class DataUseNetworkDelegate : public net::LayeredNetworkDelegate {
void OnHeadersReceivedInternal(
net::URLRequest* request,
- const net::CompletionCallback& callback,
const net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
GURL* allowed_unsafe_redirect_url) override;
@@ -58,7 +56,9 @@ class DataUseNetworkDelegate : public net::LayeredNetworkDelegate {
void OnNetworkBytesSentInternal(net::URLRequest* request,
int64_t bytes_sent) override;
- void OnCompletedInternal(net::URLRequest* request, bool started) override;
+ void OnCompletedInternal(net::URLRequest* request,
+ bool started,
+ int net_error) override;
void OnURLRequestDestroyedInternal(net::URLRequest* request) override;
diff --git a/chromium/components/data_use_measurement/core/data_use_network_delegate_unittest.cc b/chromium/components/data_use_measurement/core/data_use_network_delegate_unittest.cc
index f7794235dfa..077f0cfc5c6 100644
--- a/chromium/components/data_use_measurement/core/data_use_network_delegate_unittest.cc
+++ b/chromium/components/data_use_measurement/core/data_use_network_delegate_unittest.cc
@@ -8,7 +8,7 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/data_use_measurement/core/data_use_ascriber.h"
#include "components/data_use_measurement/core/url_request_classifier.h"
#include "components/metrics/data_use_tracker.h"
@@ -100,7 +100,6 @@ std::unique_ptr<net::URLRequest> RequestURL(
response_mock_reads, base::span<net::MockWrite>());
socket_factory->AddSocketDataProvider(&response_socket_data_provider);
net::TestDelegate test_delegate;
- test_delegate.set_quit_on_complete(true);
std::unique_ptr<net::URLRequest> request(
context->CreateRequest(GURL("http://example.com"), net::DEFAULT_PRIORITY,
&test_delegate, traffic_annotation));
@@ -113,7 +112,7 @@ std::unique_ptr<net::URLRequest> RequestURL(
data_use_measurement::DataUseUserData::FOREGROUND));
}
request->Start();
- base::RunLoop().RunUntilIdle();
+ test_delegate.RunUntilComplete();
return request;
}
diff --git a/chromium/components/data_use_measurement/core/data_use_user_data.h b/chromium/components/data_use_measurement/core/data_use_user_data.h
index b8338a2ef9c..a6e110dc767 100644
--- a/chromium/components/data_use_measurement/core/data_use_user_data.h
+++ b/chromium/components/data_use_measurement/core/data_use_user_data.h
@@ -88,7 +88,7 @@ class DataUseUserData : public base::SupportsUserData::Data {
VIDEO_APPBACKGROUND = 10,
VIDEO_TABBACKGROUND = 11,
VIDEO = 12,
- TYPE_MAX = 13,
+ kMaxValue = 13,
};
// The state of the application. Only available on Android and on other
diff --git a/chromium/components/discardable_memory/OWNERS b/chromium/components/discardable_memory/OWNERS
index 157836abab7..08bb2dd3f93 100644
--- a/chromium/components/discardable_memory/OWNERS
+++ b/chromium/components/discardable_memory/OWNERS
@@ -1 +1,2 @@
reveman@chromium.org
+penghuang@chromium.org
diff --git a/chromium/components/discardable_memory/common/discardable_shared_memory_heap_unittest.cc b/chromium/components/discardable_memory/common/discardable_shared_memory_heap_unittest.cc
index 117cb9b5b72..663d6cb0b64 100644
--- a/chromium/components/discardable_memory/common/discardable_shared_memory_heap_unittest.cc
+++ b/chromium/components/discardable_memory/common/discardable_shared_memory_heap_unittest.cc
@@ -319,7 +319,7 @@ TEST(DiscardableSharedMemoryHeapTest, CreateMemoryAllocatorDumpTest) {
// Check if allocator dump is created when span exists.
std::unique_ptr<base::trace_event::ProcessMemoryDump> pmd(
new base::trace_event::ProcessMemoryDump(
- nullptr, {base::trace_event::MemoryDumpLevelOfDetail::DETAILED}));
+ {base::trace_event::MemoryDumpLevelOfDetail::DETAILED}));
EXPECT_TRUE(heap.CreateMemoryAllocatorDump(span.get(), "discardable/test1",
pmd.get()));
diff --git a/chromium/components/dom_distiller/DEPS b/chromium/components/dom_distiller/DEPS
index 35ae9ad146c..d34ee2cfa64 100644
--- a/chromium/components/dom_distiller/DEPS
+++ b/chromium/components/dom_distiller/DEPS
@@ -17,6 +17,8 @@ include_rules = [
"+net/http",
"+net/traffic_annotation",
"+net/url_request",
+ "+services/network/public/cpp",
+ "+services/network/test",
"+ui/base/l10n",
"+ui/base/resource",
"+ui/gfx",
diff --git a/chromium/components/dom_distiller/core/BUILD.gn b/chromium/components/dom_distiller/core/BUILD.gn
index 63fe88e0519..ecd5d50a6b4 100644
--- a/chromium/components/dom_distiller/core/BUILD.gn
+++ b/chromium/components/dom_distiller/core/BUILD.gn
@@ -71,6 +71,7 @@ static_library("core") {
"//components/strings",
"//components/sync",
"//components/variations",
+ "//services/network/public/cpp:cpp",
"//skia",
"//third_party/re2",
"//ui/base",
@@ -152,6 +153,8 @@ source_set("unit_tests") {
"//components/sync",
"//components/sync_preferences:test_support",
"//net:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp:cpp",
"//testing/gmock",
"//testing/gtest",
"//ui/base",
diff --git a/chromium/components/dom_distiller/core/distiller_unittest.cc b/chromium/components/dom_distiller/core/distiller_unittest.cc
index 3420e542d1b..b27dd92abac 100644
--- a/chromium/components/dom_distiller/core/distiller_unittest.cc
+++ b/chromium/components/dom_distiller/core/distiller_unittest.cc
@@ -30,6 +30,7 @@
#include "components/dom_distiller/core/proto/distilled_article.pb.h"
#include "components/dom_distiller/core/proto/distilled_page.pb.h"
#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/dom_distiller_js/dom_distiller.pb.h"
diff --git a/chromium/components/dom_distiller/core/distiller_url_fetcher.cc b/chromium/components/dom_distiller/core/distiller_url_fetcher.cc
index 2cf48ec7711..035d40c08b6 100644
--- a/chromium/components/dom_distiller/core/distiller_url_fetcher.cc
+++ b/chromium/components/dom_distiller/core/distiller_url_fetcher.cc
@@ -4,34 +4,27 @@
#include "components/dom_distiller/core/distiller_url_fetcher.h"
-#include "components/data_use_measurement/core/data_use_user_data.h"
-#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_status.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
#include "url/gurl.h"
-using net::URLFetcher;
-
namespace dom_distiller {
DistillerURLFetcherFactory::DistillerURLFetcherFactory(
- net::URLRequestContextGetter* context_getter)
- : context_getter_(context_getter) {
-}
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+ : url_loader_factory_(url_loader_factory) {}
+
+DistillerURLFetcherFactory::~DistillerURLFetcherFactory() {}
DistillerURLFetcher*
DistillerURLFetcherFactory::CreateDistillerURLFetcher() const {
- return new DistillerURLFetcher(context_getter_);
+ return new DistillerURLFetcher(url_loader_factory_);
}
-
DistillerURLFetcher::DistillerURLFetcher(
- net::URLRequestContextGetter* context_getter)
- : context_getter_(context_getter) {
-}
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+ : url_loader_factory_(url_loader_factory) {}
DistillerURLFetcher::~DistillerURLFetcher() {
}
@@ -39,14 +32,16 @@ DistillerURLFetcher::~DistillerURLFetcher() {
void DistillerURLFetcher::FetchURL(const std::string& url,
const URLFetcherCallback& callback) {
// Don't allow a fetch if one is pending.
- DCHECK(!url_fetcher_ || !url_fetcher_->GetStatus().is_io_pending());
+ DCHECK(!url_loader_);
callback_ = callback;
- url_fetcher_ = CreateURLFetcher(context_getter_, url);
- url_fetcher_->Start();
+ url_loader_ = CreateURLFetcher(url);
+ url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&DistillerURLFetcher::OnURLLoadComplete,
+ base::Unretained(this)));
}
-std::unique_ptr<URLFetcher> DistillerURLFetcher::CreateURLFetcher(
- net::URLRequestContextGetter* context_getter,
+std::unique_ptr<network::SimpleURLLoader> DistillerURLFetcher::CreateURLFetcher(
const std::string& url) {
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("dom_distiller", R"(
@@ -79,24 +74,30 @@ std::unique_ptr<URLFetcher> DistillerURLFetcher::CreateURLFetcher(
"Not implemented, considered not useful as no content is being "
"uploaded; this request merely downloads the resources on the web."
})");
- std::unique_ptr<net::URLFetcher> fetcher =
- URLFetcher::Create(GURL(url), URLFetcher::GET, this, traffic_annotation);
- data_use_measurement::DataUseUserData::AttachToFetcher(
- fetcher.get(), data_use_measurement::DataUseUserData::DOM_DISTILLER);
- fetcher->SetRequestContext(context_getter);
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = GURL(url);
+ resource_request->method = "GET";
+
+ // TODO(crbug.com/808498): Restore the data use measurement when bug is fixed.
+ auto url_loader = network::SimpleURLLoader::Create(
+ std::move(resource_request), traffic_annotation);
static const int kMaxRetries = 5;
- fetcher->SetMaxRetriesOn5xx(kMaxRetries);
- return fetcher;
+ url_loader->SetRetryOptions(kMaxRetries,
+ network::SimpleURLLoader::RETRY_ON_5XX);
+
+ return url_loader;
}
-void DistillerURLFetcher::OnURLFetchComplete(
- const URLFetcher* source) {
+void DistillerURLFetcher::OnURLLoadComplete(
+ std::unique_ptr<std::string> response_body) {
+ // The loader is not needed at this point anymore.
+ url_loader_.reset();
+
std::string response;
- if (source && source->GetStatus().is_success() &&
- source->GetResponseCode() == net::HTTP_OK) {
+ if (response_body) {
// Only copy over the data if the request was successful. Insert
// an empty string into the proto otherwise.
- source->GetResponseAsString(&response);
+ response = std::move(*response_body);
}
callback_.Run(response);
}
diff --git a/chromium/components/dom_distiller/core/distiller_url_fetcher.h b/chromium/components/dom_distiller/core/distiller_url_fetcher.h
index d0178e0dbba..c883dca239e 100644
--- a/chromium/components/dom_distiller/core/distiller_url_fetcher.h
+++ b/chromium/components/dom_distiller/core/distiller_url_fetcher.h
@@ -9,9 +9,11 @@
#include "base/callback.h"
#include "base/macros.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_context_getter.h"
+
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+} // namespace network
namespace dom_distiller {
@@ -20,21 +22,23 @@ class DistillerURLFetcher;
// Class for creating a DistillerURLFetcher.
class DistillerURLFetcherFactory {
public:
- explicit DistillerURLFetcherFactory(
- net::URLRequestContextGetter* context_getter);
- virtual ~DistillerURLFetcherFactory() {}
+ DistillerURLFetcherFactory(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+ virtual ~DistillerURLFetcherFactory();
virtual DistillerURLFetcher* CreateDistillerURLFetcher() const;
private:
- net::URLRequestContextGetter* context_getter_;
+ friend class TestDistillerURLFetcherFactory;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
};
-// This class fetches a URL, and notifies the caller when the operation
+// This class loads a URL, and notifies the caller when the operation
// completes or fails. If the request fails, an empty string will be returned.
-class DistillerURLFetcher : public net::URLFetcherDelegate {
+class DistillerURLFetcher {
public:
- explicit DistillerURLFetcher(net::URLRequestContextGetter* context_getter);
- ~DistillerURLFetcher() override;
+ explicit DistillerURLFetcher(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+ virtual ~DistillerURLFetcher();
// Indicates when a fetch is done.
typedef base::Callback<void(const std::string& data)> URLFetcherCallback;
@@ -44,17 +48,15 @@ class DistillerURLFetcher : public net::URLFetcherDelegate {
const URLFetcherCallback& callback);
protected:
- virtual std::unique_ptr<net::URLFetcher> CreateURLFetcher(
- net::URLRequestContextGetter* context_getter,
+ virtual std::unique_ptr<network::SimpleURLLoader> CreateURLFetcher(
const std::string& url);
private:
- // net::URLFetcherDelegate:
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ void OnURLLoadComplete(std::unique_ptr<std::string> response_body);
- std::unique_ptr<net::URLFetcher> url_fetcher_;
+ std::unique_ptr<network::SimpleURLLoader> url_loader_;
URLFetcherCallback callback_;
- net::URLRequestContextGetter* context_getter_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
DISALLOW_COPY_AND_ASSIGN(DistillerURLFetcher);
};
diff --git a/chromium/components/dom_distiller/core/distiller_url_fetcher_unittest.cc b/chromium/components/dom_distiller/core/distiller_url_fetcher_unittest.cc
index 7454119c832..a19e6a05322 100644
--- a/chromium/components/dom_distiller/core/distiller_url_fetcher_unittest.cc
+++ b/chromium/components/dom_distiller/core/distiller_url_fetcher_unittest.cc
@@ -2,16 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "components/dom_distiller/core/distiller_url_fetcher.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
-#include "components/dom_distiller/core/distiller_url_fetcher.h"
-#include "net/http/http_status_code.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_status.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -23,6 +21,11 @@ const char kTestPageBResponse[] = { 'a', 'b', 'c' };
class DistillerURLFetcherTest : public testing::Test {
public:
+ DistillerURLFetcherTest()
+ : test_shared_url_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)) {}
+
void FetcherCallback(const std::string& response) {
response_ = response;
}
@@ -30,18 +33,16 @@ class DistillerURLFetcherTest : public testing::Test {
protected:
// testing::Test implementation:
void SetUp() override {
- url_fetcher_.reset(new dom_distiller::DistillerURLFetcher(nullptr));
- factory_.reset(new net::FakeURLFetcherFactory(nullptr));
- factory_->SetFakeResponse(
- GURL(kTestPageA),
- std::string(kTestPageAResponse, sizeof(kTestPageAResponse)),
- net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- factory_->SetFakeResponse(
+ url_fetcher_.reset(new dom_distiller::DistillerURLFetcher(
+ test_shared_url_loader_factory_));
+ test_url_loader_factory_.AddResponse(
+ kTestPageA,
+ std::string(kTestPageAResponse, sizeof(kTestPageAResponse)));
+ test_url_loader_factory_.AddResponse(
GURL(kTestPageB),
+ network::CreateResourceResponseHead(net::HTTP_INTERNAL_SERVER_ERROR),
std::string(kTestPageBResponse, sizeof(kTestPageBResponse)),
- net::HTTP_INTERNAL_SERVER_ERROR,
- net::URLRequestStatus::SUCCESS);
+ network::URLLoaderCompletionStatus(net::OK));
}
void Fetch(const std::string& url,
@@ -56,7 +57,9 @@ class DistillerURLFetcherTest : public testing::Test {
}
std::unique_ptr<dom_distiller::DistillerURLFetcher> url_fetcher_;
- std::unique_ptr<net::FakeURLFetcherFactory> factory_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory>
+ test_shared_url_loader_factory_;
std::string response_;
};
diff --git a/chromium/components/dom_distiller/standalone/content_extractor_browsertest.cc b/chromium/components/dom_distiller/standalone/content_extractor_browsertest.cc
index 5acc5f380e6..1286ea07524 100644
--- a/chromium/components/dom_distiller/standalone/content_extractor_browsertest.cc
+++ b/chromium/components/dom_distiller/standalone/content_extractor_browsertest.cc
@@ -142,7 +142,7 @@ std::unique_ptr<DomDistillerService> CreateDomDistillerService(
std::unique_ptr<DistillerURLFetcherFactory> distiller_url_fetcher_factory(
new DistillerURLFetcherFactory(
content::BrowserContext::GetDefaultStoragePartition(context)
- ->GetURLRequestContext()));
+ ->GetURLLoaderFactoryForBrowserProcess()));
dom_distiller::proto::DomDistillerOptions options;
if (base::CommandLine::ForCurrentProcess()->HasSwitch(kExtractTextOnly)) {
diff --git a/chromium/components/dom_distiller/webui/resources/about_dom_distiller.html b/chromium/components/dom_distiller/webui/resources/about_dom_distiller.html
index 91c4f49d9b4..e602355e5da 100644
--- a/chromium/components/dom_distiller/webui/resources/about_dom_distiller.html
+++ b/chromium/components/dom_distiller/webui/resources/about_dom_distiller.html
@@ -48,6 +48,5 @@ found in the LICENSE file.
</div>
</div>
<script src="chrome://resources/js/i18n_template.js"></script>
- <script src="chrome://resources/js/jstemplate_compiled.js"></script>
</body>
</html>
diff --git a/chromium/components/domain_reliability/OWNERS b/chromium/components/domain_reliability/OWNERS
index 22db3afd6f2..fdcdcec2149 100644
--- a/chromium/components/domain_reliability/OWNERS
+++ b/chromium/components/domain_reliability/OWNERS
@@ -1,5 +1,6 @@
davidben@chromium.org
-juliatuttle@chromium.org
+
+file://net/OWNERS
per-file quic_error_mapping*=rch@chromium.org
per-file quic_error_mapping*=zhongyi@chromium.org
diff --git a/chromium/components/domain_reliability/quic_error_mapping.cc b/chromium/components/domain_reliability/quic_error_mapping.cc
index f7d54f6afaf..c43881a7704 100644
--- a/chromium/components/domain_reliability/quic_error_mapping.cc
+++ b/chromium/components/domain_reliability/quic_error_mapping.cc
@@ -9,261 +9,295 @@ namespace domain_reliability {
namespace {
const struct QuicErrorMapping {
- net::QuicErrorCode quic_error;
+ quic::QuicErrorCode quic_error;
const char* beacon_quic_error;
} kQuicErrorMap[] = {
// Connection has reached an invalid state.
- {net::QUIC_INTERNAL_ERROR, "quic.internal_error"},
+ {quic::QUIC_INTERNAL_ERROR, "quic.internal_error"},
// There were data frames after the a fin or reset.
- {net::QUIC_STREAM_DATA_AFTER_TERMINATION,
+ {quic::QUIC_STREAM_DATA_AFTER_TERMINATION,
"quic.stream_data.after_termination"},
// Control frame is malformed.
- {net::QUIC_INVALID_PACKET_HEADER, "quic.invalid.packet_header"},
+ {quic::QUIC_INVALID_PACKET_HEADER, "quic.invalid.packet_header"},
// Frame data is malformed.
- {net::QUIC_INVALID_FRAME_DATA, "quic.invalid_frame_data"},
+ {quic::QUIC_INVALID_FRAME_DATA, "quic.invalid_frame_data"},
// The packet contained no payload.
- {net::QUIC_MISSING_PAYLOAD, "quic.missing.payload"},
+ {quic::QUIC_MISSING_PAYLOAD, "quic.missing.payload"},
// FEC data is malformed.
- {net::QUIC_INVALID_FEC_DATA, "quic.invalid.fec_data"},
+ {quic::QUIC_INVALID_FEC_DATA, "quic.invalid.fec_data"},
// STREAM frame data is malformed.
- {net::QUIC_INVALID_STREAM_DATA, "quic.invalid.stream_data"},
+ {quic::QUIC_INVALID_STREAM_DATA, "quic.invalid.stream_data"},
// STREAM frame data is not encrypted.
- {net::QUIC_UNENCRYPTED_STREAM_DATA, "quic.unencrypted.stream_data"},
+ {quic::QUIC_UNENCRYPTED_STREAM_DATA, "quic.unencrypted.stream_data"},
// Attempt to send unencrypted STREAM frame.
- {net::QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA,
+ {quic::QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA,
"quic.attempt.to.unencrypted.stream.data"},
// Received a frame which is likely the result of memory corruption.
- {net::QUIC_MAYBE_CORRUPTED_MEMORY, "quic.maybe.corrupted.momery"},
+ {quic::QUIC_MAYBE_CORRUPTED_MEMORY, "quic.maybe.corrupted.momery"},
// FEC frame data is not encrypted.
- {net::QUIC_UNENCRYPTED_FEC_DATA, "quic.unencrypted.fec.data"},
+ {quic::QUIC_UNENCRYPTED_FEC_DATA, "quic.unencrypted.fec.data"},
// RST_STREAM frame data is malformed.
- {net::QUIC_INVALID_RST_STREAM_DATA, "quic.invalid.rst_stream_data"},
+ {quic::QUIC_INVALID_RST_STREAM_DATA, "quic.invalid.rst_stream_data"},
// CONNECTION_CLOSE frame data is malformed.
- {net::QUIC_INVALID_CONNECTION_CLOSE_DATA,
+ {quic::QUIC_INVALID_CONNECTION_CLOSE_DATA,
"quic.invalid.connection_close_data"},
// GOAWAY frame data is malformed.
- {net::QUIC_INVALID_GOAWAY_DATA, "quic.invalid.goaway_data"},
+ {quic::QUIC_INVALID_GOAWAY_DATA, "quic.invalid.goaway_data"},
// WINDOW_UPDATE frame data is malformed.
- {net::QUIC_INVALID_WINDOW_UPDATE_DATA, "quic.invalid.window_update_data"},
+ {quic::QUIC_INVALID_WINDOW_UPDATE_DATA, "quic.invalid.window_update_data"},
// BLOCKED frame data is malformed.
- {net::QUIC_INVALID_BLOCKED_DATA, "quic.invalid.blocked_data"},
+ {quic::QUIC_INVALID_BLOCKED_DATA, "quic.invalid.blocked_data"},
// STOP_WAITING frame data is malformed.
- {net::QUIC_INVALID_STOP_WAITING_DATA, "quic.invalid.stop_waiting_data"},
+ {quic::QUIC_INVALID_STOP_WAITING_DATA, "quic.invalid.stop_waiting_data"},
// PATH_CLOSE frame data is malformed.
- {net::QUIC_INVALID_PATH_CLOSE_DATA, "quic.invalid_path_close_data"},
+ {quic::QUIC_INVALID_PATH_CLOSE_DATA, "quic.invalid_path_close_data"},
// ACK frame data is malformed.
- {net::QUIC_INVALID_ACK_DATA, "quic.invalid.ack_data"},
+ {quic::QUIC_INVALID_ACK_DATA, "quic.invalid.ack_data"},
// Version negotiation packet is malformed.
- {net::QUIC_INVALID_VERSION_NEGOTIATION_PACKET,
+ {quic::QUIC_INVALID_VERSION_NEGOTIATION_PACKET,
"quic_invalid_version_negotiation_packet"},
// Public RST packet is malformed.
- {net::QUIC_INVALID_PUBLIC_RST_PACKET, "quic.invalid.public_rst_packet"},
+ {quic::QUIC_INVALID_PUBLIC_RST_PACKET, "quic.invalid.public_rst_packet"},
// There was an error decrypting.
- {net::QUIC_DECRYPTION_FAILURE, "quic.decryption.failure"},
+ {quic::QUIC_DECRYPTION_FAILURE, "quic.decryption.failure"},
// There was an error encrypting.
- {net::QUIC_ENCRYPTION_FAILURE, "quic.encryption.failure"},
+ {quic::QUIC_ENCRYPTION_FAILURE, "quic.encryption.failure"},
// The packet exceeded kMaxPacketSize.
- {net::QUIC_PACKET_TOO_LARGE, "quic.packet.too_large"},
+ {quic::QUIC_PACKET_TOO_LARGE, "quic.packet.too_large"},
// The peer is going away. May be a client or server.
- {net::QUIC_PEER_GOING_AWAY, "quic.peer_going_away"},
+ {quic::QUIC_PEER_GOING_AWAY, "quic.peer_going_away"},
// A stream ID was invalid.
- {net::QUIC_INVALID_STREAM_ID, "quic.invalid_stream_id"},
+ {quic::QUIC_INVALID_STREAM_ID, "quic.invalid_stream_id"},
// A priority was invalid.
- {net::QUIC_INVALID_PRIORITY, "quic.invalid_priority"},
+ {quic::QUIC_INVALID_PRIORITY, "quic.invalid_priority"},
// Too many streams already open.
- {net::QUIC_TOO_MANY_OPEN_STREAMS, "quic.too_many_open_streams"},
+ {quic::QUIC_TOO_MANY_OPEN_STREAMS, "quic.too_many_open_streams"},
// The peer created too many available streams.
- {net::QUIC_TOO_MANY_AVAILABLE_STREAMS, "quic.too_many_available_streams"},
+ {quic::QUIC_TOO_MANY_AVAILABLE_STREAMS, "quic.too_many_available_streams"},
// Received public reset for this connection.
- {net::QUIC_PUBLIC_RESET, "quic.public_reset"},
+ {quic::QUIC_PUBLIC_RESET, "quic.public_reset"},
// Invalid protocol version.
- {net::QUIC_INVALID_VERSION, "quic.invalid_version"},
+ {quic::QUIC_INVALID_VERSION, "quic.invalid_version"},
// The Header ID for a stream was too far from the previous.
- {net::QUIC_INVALID_HEADER_ID, "quic.invalid_header_id"},
+ {quic::QUIC_INVALID_HEADER_ID, "quic.invalid_header_id"},
// Negotiable parameter received during handshake had invalid value.
- {net::QUIC_INVALID_NEGOTIATED_VALUE, "quic.invalid_negotiated_value"},
+ {quic::QUIC_INVALID_NEGOTIATED_VALUE, "quic.invalid_negotiated_value"},
// There was an error decompressing data.
- {net::QUIC_DECOMPRESSION_FAILURE, "quic.decompression_failure"},
+ {quic::QUIC_DECOMPRESSION_FAILURE, "quic.decompression_failure"},
// We hit our prenegotiated (or default) timeout
- {net::QUIC_NETWORK_IDLE_TIMEOUT, "quic.connection.idle_time_out"},
+ {quic::QUIC_NETWORK_IDLE_TIMEOUT, "quic.connection.idle_time_out"},
// We hit our overall connection timeout
- {net::QUIC_HANDSHAKE_TIMEOUT, "quic.connection.handshake_timed_out"},
+ {quic::QUIC_HANDSHAKE_TIMEOUT, "quic.connection.handshake_timed_out"},
// There was an error encountered migrating addresses.
- {net::QUIC_ERROR_MIGRATING_ADDRESS, "quic.error_migrating_address"},
+ {quic::QUIC_ERROR_MIGRATING_ADDRESS, "quic.error_migrating_address"},
// There was an error encountered migrating port only.
- {net::QUIC_ERROR_MIGRATING_PORT, "quic.error_migrating_port"},
+ {quic::QUIC_ERROR_MIGRATING_PORT, "quic.error_migrating_port"},
// There was an error while writing to the socket.
- {net::QUIC_PACKET_WRITE_ERROR, "quic.packet.write_error"},
+ {quic::QUIC_PACKET_WRITE_ERROR, "quic.packet.write_error"},
// There was an error while reading from the socket.
- {net::QUIC_PACKET_READ_ERROR, "quic.packet.read_error"},
+ {quic::QUIC_PACKET_READ_ERROR, "quic.packet.read_error"},
// We received a STREAM_FRAME with no data and no fin flag set.
- {net::QUIC_EMPTY_STREAM_FRAME_NO_FIN, "quic.empty_stream_frame_no_fin"},
+ {quic::QUIC_EMPTY_STREAM_FRAME_NO_FIN, "quic.empty_stream_frame_no_fin"},
// We received invalid data on the headers stream.
- {net::QUIC_INVALID_HEADERS_STREAM_DATA, "quic.invalid_headers_stream_data"},
+ {quic::QUIC_INVALID_HEADERS_STREAM_DATA,
+ "quic.invalid_headers_stream_data"},
// The peer received too much data, violating flow control.
- {net::QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA,
+ {quic::QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA,
"quic.flow_control.received_too_much_data"},
// The peer sent too much data, violating flow control.
- {net::QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA,
+ {quic::QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA,
"quic.flow_control.sent_too_much_data"},
// The peer received an invalid flow control window.
- {net::QUIC_FLOW_CONTROL_INVALID_WINDOW, "quic.flow_control.invalid_window"},
+ {quic::QUIC_FLOW_CONTROL_INVALID_WINDOW,
+ "quic.flow_control.invalid_window"},
// The connection has been IP pooled into an existing connection.
- {net::QUIC_CONNECTION_IP_POOLED, "quic.connection.ip_pooled"},
+ {quic::QUIC_CONNECTION_IP_POOLED, "quic.connection.ip_pooled"},
// The connection has too many outstanding sent packets.
- {net::QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS,
+ {quic::QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS,
"quic.too_many_outstanding_sent_packets"},
// The connection has too many outstanding received packets.
- {net::QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS,
+ {quic::QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS,
"quic.too_many_outstanding_received_packets"},
// The quic connection job to load server config is cancelled.
- {net::QUIC_CONNECTION_CANCELLED, "quic.connection.cancelled"},
+ {quic::QUIC_CONNECTION_CANCELLED, "quic.connection.cancelled"},
// Disabled QUIC because of high packet loss rate.
- {net::QUIC_BAD_PACKET_LOSS_RATE, "quic.bad_packet_loss_rate"},
+ {quic::QUIC_BAD_PACKET_LOSS_RATE, "quic.bad_packet_loss_rate"},
// Disabled QUIC because of too many PUBLIC_RESETs post handshake.
- {net::QUIC_PUBLIC_RESETS_POST_HANDSHAKE,
+ {quic::QUIC_PUBLIC_RESETS_POST_HANDSHAKE,
"quic.public_resets_post_handshake"},
// Closed because we failed to serialize a packet.
- {net::QUIC_FAILED_TO_SERIALIZE_PACKET, "quic.failed_to_serialize_packet"},
+ {quic::QUIC_FAILED_TO_SERIALIZE_PACKET, "quic.failed_to_serialize_packet"},
// QUIC timed out after too many RTOs.
- {net::QUIC_TOO_MANY_RTOS, "quic.too_many_rtos"},
+ {quic::QUIC_TOO_MANY_RTOS, "quic.too_many_rtos"},
// Crypto errors.
// Hanshake failed.
- {net::QUIC_HANDSHAKE_FAILED, "quic.handshake_failed"},
+ {quic::QUIC_HANDSHAKE_FAILED, "quic.handshake_failed"},
// Handshake message contained out of order tags.
- {net::QUIC_CRYPTO_TAGS_OUT_OF_ORDER, "quic.crypto.tags_out_of_order"},
+ {quic::QUIC_CRYPTO_TAGS_OUT_OF_ORDER, "quic.crypto.tags_out_of_order"},
// Handshake message contained too many entries.
- {net::QUIC_CRYPTO_TOO_MANY_ENTRIES, "quic.crypto.too_many_entries"},
+ {quic::QUIC_CRYPTO_TOO_MANY_ENTRIES, "quic.crypto.too_many_entries"},
// Handshake message contained an invalid value length.
- {net::QUIC_CRYPTO_INVALID_VALUE_LENGTH, "quic.crypto.invalid_value_length"},
+ {quic::QUIC_CRYPTO_INVALID_VALUE_LENGTH,
+ "quic.crypto.invalid_value_length"},
// A crypto message was received after the handshake was complete.
- {net::QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE,
+ {quic::QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE,
"quic.crypto_message_after_handshake_complete"},
// A crypto message was received with an illegal message tag.
- {net::QUIC_INVALID_CRYPTO_MESSAGE_TYPE, "quic.invalid_crypto_message_type"},
+ {quic::QUIC_INVALID_CRYPTO_MESSAGE_TYPE,
+ "quic.invalid_crypto_message_type"},
// A crypto message was received with an illegal parameter.
- {net::QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ {quic::QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
"quic.invalid_crypto_message_parameter"},
// An invalid channel id signature was supplied.
- {net::QUIC_INVALID_CHANNEL_ID_SIGNATURE,
+ {quic::QUIC_INVALID_CHANNEL_ID_SIGNATURE,
"quic.invalid_channel_id_signature"},
// A crypto message was received with a mandatory parameter missing.
- {net::QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND,
+ {quic::QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND,
"quic.crypto_message.parameter_not_found"},
// A crypto message was received with a parameter that has no overlap
// with the local parameter.
- {net::QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP,
+ {quic::QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP,
"quic.crypto_message.parameter_no_overlap"},
// A crypto message was received that contained a parameter with too few
// values.
- {net::QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND,
+ {quic::QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND,
"quic_crypto_message_index_not_found"},
// A demand for an unsupport proof type was received.
- {net::QUIC_UNSUPPORTED_PROOF_DEMAND, "quic.unsupported_proof_demand"},
+ {quic::QUIC_UNSUPPORTED_PROOF_DEMAND, "quic.unsupported_proof_demand"},
// An internal error occured in crypto processing.
- {net::QUIC_CRYPTO_INTERNAL_ERROR, "quic.crypto.internal_error"},
+ {quic::QUIC_CRYPTO_INTERNAL_ERROR, "quic.crypto.internal_error"},
// A crypto handshake message specified an unsupported version.
- {net::QUIC_CRYPTO_VERSION_NOT_SUPPORTED,
+ {quic::QUIC_CRYPTO_VERSION_NOT_SUPPORTED,
"quic.crypto.version_not_supported"},
// A crypto handshake message resulted in a stateless reject.
- {net::QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT,
+ {quic::QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT,
"quic.crypto.handshake_stateless_reject"},
// There was no intersection between the crypto primitives supported by the
// peer and ourselves.
- {net::QUIC_CRYPTO_NO_SUPPORT, "quic.crypto.no_support"},
+ {quic::QUIC_CRYPTO_NO_SUPPORT, "quic.crypto.no_support"},
// The server rejected our client hello messages too many times.
- {net::QUIC_CRYPTO_TOO_MANY_REJECTS, "quic.crypto.too_many_rejects"},
+ {quic::QUIC_CRYPTO_TOO_MANY_REJECTS, "quic.crypto.too_many_rejects"},
// The client rejected the server's certificate chain or signature.
- {net::QUIC_PROOF_INVALID, "quic.proof_invalid"},
+ {quic::QUIC_PROOF_INVALID, "quic.proof_invalid"},
// A crypto message was received with a duplicate tag.
- {net::QUIC_CRYPTO_DUPLICATE_TAG, "quic.crypto.duplicate_tag"},
+ {quic::QUIC_CRYPTO_DUPLICATE_TAG, "quic.crypto.duplicate_tag"},
// A crypto message was received with the wrong encryption level (i.e. it
// should have been encrypted but was not.)
- {net::QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT,
+ {quic::QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT,
"quic.crypto.encryption_level_incorrect"},
// The server config for a server has expired.
- {net::QUIC_CRYPTO_SERVER_CONFIG_EXPIRED,
+ {quic::QUIC_CRYPTO_SERVER_CONFIG_EXPIRED,
"quic.crypto.server_config_expired"},
// We failed to setup the symmetric keys for a connection.
- {net::QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED,
+ {quic::QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED,
"quic.crypto.symmetric_key_setup_failed"},
// A handshake message arrived, but we are still validating the
// previous handshake message.
- {net::QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO,
+ {quic::QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO,
"quic.crypto_message_while_validating_client_hello"},
// A server config update arrived before the handshake is complete.
- {net::QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE,
+ {quic::QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE,
"quic.crypto.update_before_handshake_complete"},
// CHLO cannot fit in one packet.
- {net::QUIC_CRYPTO_CHLO_TOO_LARGE, "quic.crypto.chlo_too_large"},
+ {quic::QUIC_CRYPTO_CHLO_TOO_LARGE, "quic.crypto.chlo_too_large"},
// This connection involved a version negotiation which appears to have been
// tampered with.
- {net::QUIC_VERSION_NEGOTIATION_MISMATCH,
+ {quic::QUIC_VERSION_NEGOTIATION_MISMATCH,
"quic.version_negotiation_mismatch"},
// Multipath is not enabled, but a packet with multipath flag on is
// received.
- {net::QUIC_BAD_MULTIPATH_FLAG, "quic.bad_multipath_flag"},
+ {quic::QUIC_BAD_MULTIPATH_FLAG, "quic.bad_multipath_flag"},
// A path is supposed to exist but does not.
- {net::QUIC_MULTIPATH_PATH_DOES_NOT_EXIST,
+ {quic::QUIC_MULTIPATH_PATH_DOES_NOT_EXIST,
"quic.quic_multipath_path_does_not_exist"},
// A path is supposed to be active but is not.
- {net::QUIC_MULTIPATH_PATH_NOT_ACTIVE,
+ {quic::QUIC_MULTIPATH_PATH_NOT_ACTIVE,
"quic.quic_multipath_path_not_active"},
// Network change and connection migration errors.
// IP address changed causing connection close.
- {net::QUIC_IP_ADDRESS_CHANGED, "quic.ip_address_changed"},
+ {quic::QUIC_IP_ADDRESS_CHANGED, "quic.ip_address_changed"},
// Network changed, but connection had no migratable streams.
- {net::QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS,
+ {quic::QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS,
"quic.connection_migration_no_migratable_streams"},
// Connection changed networks too many times.
- {net::QUIC_CONNECTION_MIGRATION_TOO_MANY_CHANGES,
+ {quic::QUIC_CONNECTION_MIGRATION_TOO_MANY_CHANGES,
"quic.connection_migration_too_many_changes"},
// Connection migration was attempted, but there was no new network to
// migrate to.
- {net::QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK,
+ {quic::QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK,
"quic.connection_migration_no_new_network"},
// Network changed, but connection had one or more non-migratable streams.
- {net::QUIC_CONNECTION_MIGRATION_NON_MIGRATABLE_STREAM,
+ {quic::QUIC_CONNECTION_MIGRATION_NON_MIGRATABLE_STREAM,
"quic.connection_migration_non_migratable_stream"},
// Network changed, but connection migration was disabled by config.
- {net::QUIC_CONNECTION_MIGRATION_DISABLED_BY_CONFIG,
+ {quic::QUIC_CONNECTION_MIGRATION_DISABLED_BY_CONFIG,
"quic.connection_migration_disabled_by_config"},
// Network changed, but error was encountered on the alternative network.
- {net::QUIC_CONNECTION_MIGRATION_INTERNAL_ERROR,
+ {quic::QUIC_CONNECTION_MIGRATION_INTERNAL_ERROR,
"quic.connection_migration_internal_error"},
+ // Network changed, but error was encountered on the alternative network.
+ {quic::QUIC_CONNECTION_MIGRATION_HANDSHAKE_UNCONFIRMED,
+ "quic.connection_migration_handshake_unconfirmed"},
// Stream frame overlaps with buffered data.
- {net::QUIC_OVERLAPPING_STREAM_DATA, "quic.overlapping_stream_data"},
+ {quic::QUIC_OVERLAPPING_STREAM_DATA, "quic.overlapping_stream_data"},
// Stream frames arrived too discontiguously so that stream sequencer buffer
// maintains too many intervals.
- {net::QUIC_TOO_MANY_STREAM_DATA_INTERVALS,
+ {quic::QUIC_TOO_MANY_STREAM_DATA_INTERVALS,
"quic.too_many_stream_data_intervals"},
// Sequencer buffer get into weird state where continuing read/write
// will lead to crash.
- {net::QUIC_STREAM_SEQUENCER_INVALID_STATE,
+ {quic::QUIC_STREAM_SEQUENCER_INVALID_STATE,
"quic.stream_sequencer_invalid_state"},
// Connection closed because of server hits max number of sessions allowed.
- {net::QUIC_TOO_MANY_SESSIONS_ON_SERVER, "quic.too_many_sessions_on_server"},
+ {quic::QUIC_TOO_MANY_SESSIONS_ON_SERVER,
+ "quic.too_many_sessions_on_server"},
// There was an error decompressing data.
- {net::QUIC_DECOMPRESSION_FAILURE, "quic.decompression_failure"},
+ {quic::QUIC_DECOMPRESSION_FAILURE, "quic.decompression_failure"},
// Receive a RST_STREAM with offset larger than kMaxStreamLength.
- {net::QUIC_STREAM_LENGTH_OVERFLOW, "quic.stream_length_overflow"},
+ {quic::QUIC_STREAM_LENGTH_OVERFLOW, "quic.stream_length_overflow"},
+ // APPLICATION_CLOSE frame data is malformed.
+ {quic::QUIC_INVALID_APPLICATION_CLOSE_DATA,
+ "quic.invalid.application_close_data"},
+ // Received a MAX DATA frame with errors.
+ {quic::QUIC_INVALID_MAX_DATA_FRAME_DATA,
+ "quic.invalid.max_data_frame_data"},
+ // Received a MAX STREAM DATA frame with errors.
+ {quic::QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA,
+ "quic.invalid.max_stream_data_frame_data"},
+ // Received a MAX STREAM ID frame with bad data
+ {quic::QUIC_MAX_STREAM_ID_DATA, "quic.max_stream_id_data"},
+ // Received a STREAM ID BLOCKED frame with bad data
+ {quic::QUIC_STREAM_ID_BLOCKED_DATA, "quic.stream_id_blocked_data"},
+ // Error deframing a STREAM BLOCKED frame.
+ {quic::QUIC_INVALID_STREAM_BLOCKED_DATA,
+ "quic.invalid.stream_blocked_data"},
+ // NEW CONNECTION ID frame data is malformed.
+ {quic::QUIC_INVALID_NEW_CONNECTION_ID_DATA,
+ "quic.invalid.new_connection_id_data"},
+ // Received a MAX STREAM DATA frame with errors.
+ {quic::QUIC_INVALID_STOP_SENDING_FRAME_DATA,
+ "quic.invalid.stop_sending_frame_data"},
+ // Error deframing PATH CHALLENGE or PATH RESPONSE frames.
+ {quic::QUIC_INVALID_PATH_CHALLENGE_DATA,
+ "quic.invalid.path_challenge_data"},
+ {quic::QUIC_INVALID_PATH_RESPONSE_DATA, "quic.invalid.path_response_data"},
// No error. Used as bound while iterating.
- {net::QUIC_LAST_ERROR, "quic.last_error"}};
+ {quic::QUIC_LAST_ERROR, "quic.last_error"}};
-// Must be updated any time a net::QuicErrorCode is deprecated in
+// Must be updated any time a quic::QuicErrorCode is deprecated in
// net/quic/core/quic_packets.h.
const int kDeprecatedQuicErrorCount = 5;
const int kActiveQuicErrorCount =
- net::QUIC_LAST_ERROR - kDeprecatedQuicErrorCount;
+ quic::QUIC_LAST_ERROR - kDeprecatedQuicErrorCount;
static_assert(arraysize(kQuicErrorMap) == kActiveQuicErrorCount,
"quic_error_map is not in sync with quic protocol!");
@@ -271,9 +305,9 @@ static_assert(arraysize(kQuicErrorMap) == kActiveQuicErrorCount,
} // namespace
// static
-bool GetDomainReliabilityBeaconQuicError(net::QuicErrorCode quic_error,
+bool GetDomainReliabilityBeaconQuicError(quic::QuicErrorCode quic_error,
std::string* beacon_quic_error_out) {
- if (quic_error != net::QUIC_NO_ERROR) {
+ if (quic_error != quic::QUIC_NO_ERROR) {
// Convert a QUIC error.
// TODO(juliatuttle): Consider sorting and using binary search?
for (size_t i = 0; i < arraysize(kQuicErrorMap); i++) {
diff --git a/chromium/components/domain_reliability/quic_error_mapping.h b/chromium/components/domain_reliability/quic_error_mapping.h
index 48e5b238dfb..26bf389f372 100644
--- a/chromium/components/domain_reliability/quic_error_mapping.h
+++ b/chromium/components/domain_reliability/quic_error_mapping.h
@@ -18,7 +18,7 @@ namespace domain_reliability {
// that should be recorded in a beacon. Returns true and parse the QUIC error
// code in |beacon_quic_error_out| if it could.
// Returns false and clear |beacon_quic_error_out| otherwise.
-bool GetDomainReliabilityBeaconQuicError(net::QuicErrorCode quic_error,
+bool GetDomainReliabilityBeaconQuicError(quic::QuicErrorCode quic_error,
std::string* beacon_quic_error_out);
} // namespace domain_reliability
diff --git a/chromium/components/domain_reliability/service.cc b/chromium/components/domain_reliability/service.cc
index 3efe542ef57..388e4a24473 100644
--- a/chromium/components/domain_reliability/service.cc
+++ b/chromium/components/domain_reliability/service.cc
@@ -16,7 +16,7 @@
#include "components/domain_reliability/context.h"
#include "components/domain_reliability/monitor.h"
#include "content/public/browser/browser_context.h"
-#include "content/public/browser/permission_manager.h"
+#include "content/public/browser/permission_controller.h"
#include "content/public/browser/permission_type.h"
#include "net/url_request/url_request_context_getter.h"
#include "third_party/blink/public/platform/modules/permissions/permission_status.mojom.h"
@@ -152,17 +152,12 @@ class DomainReliabilityServiceImpl : public DomainReliabilityService {
base::SingleThreadTaskRunner* network_task_runner,
const GURL& origin,
base::OnceCallback<void(bool)> callback) {
- bool allowed = false;
-
- content::PermissionManager* permission_manager =
- browser_context_->GetPermissionManager();
-
- if (permission_manager) {
- allowed = permission_manager->GetPermissionStatus(
- content::PermissionType::BACKGROUND_SYNC, origin, origin) ==
- blink::mojom::PermissionStatus::GRANTED;
- }
-
+ content::PermissionController* permission_controller =
+ content::BrowserContext::GetPermissionController(browser_context_);
+ DCHECK(permission_controller);
+ bool allowed = permission_controller->GetPermissionStatus(
+ content::PermissionType::BACKGROUND_SYNC, origin,
+ origin) == blink::mojom::PermissionStatus::GRANTED;
network_task_runner->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), allowed));
}
diff --git a/chromium/components/domain_reliability/service_unittest.cc b/chromium/components/domain_reliability/service_unittest.cc
index 84a1b1b459b..a115f15791b 100644
--- a/chromium/components/domain_reliability/service_unittest.cc
+++ b/chromium/components/domain_reliability/service_unittest.cc
@@ -12,7 +12,8 @@
#include "components/domain_reliability/monitor.h"
#include "components/domain_reliability/test_util.h"
#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/permission_manager.h"
+#include "content/public/browser/permission_controller.h"
+#include "content/public/browser/permission_controller_delegate.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
@@ -27,7 +28,7 @@ namespace domain_reliability {
namespace {
-class TestPermissionManager : public content::PermissionManager {
+class TestPermissionManager : public content::PermissionControllerDelegate {
public:
TestPermissionManager() : get_permission_status_count_(0) {}
@@ -78,7 +79,7 @@ class TestPermissionManager : public content::PermissionManager {
const base::Callback<void(blink::mojom::PermissionStatus)>& callback)
override {
NOTIMPLEMENTED();
- return kNoPendingOperation;
+ return content::PermissionController::kNoPendingOperation;
}
int RequestPermissions(
@@ -90,7 +91,7 @@ class TestPermissionManager : public content::PermissionManager {
void(const std::vector<blink::mojom::PermissionStatus>&)>& callback)
override {
NOTIMPLEMENTED();
- return kNoPendingOperation;
+ return content::PermissionController::kNoPendingOperation;
}
void ResetPermission(content::PermissionType permission,
@@ -146,7 +147,7 @@ class DomainReliabilityServiceTest : public testing::Test {
content::BrowserThread::IO);
url_request_context_getter_ =
new net::TestURLRequestContextGetter(network_task_runner);
- browser_context_.SetPermissionManager(
+ browser_context_.SetPermissionControllerDelegate(
base::WrapUnique(permission_manager_));
service_ = base::WrapUnique(DomainReliabilityService::Create(
upload_reporter_string_, &browser_context_));
diff --git a/chromium/components/domain_reliability/util.cc b/chromium/components/domain_reliability/util.cc
index 27ebf275d9c..c79802a5185 100644
--- a/chromium/components/domain_reliability/util.cc
+++ b/chromium/components/domain_reliability/util.cc
@@ -131,6 +131,7 @@ std::string GetDomainReliabilityProtocol(
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_41:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_42:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_43:
+ case net::HttpResponseInfo::CONNECTION_INFO_QUIC_44:
case net::HttpResponseInfo::CONNECTION_INFO_QUIC_99:
return "QUIC";
case net::HttpResponseInfo::NUM_OF_CONNECTION_INFOS:
@@ -210,9 +211,7 @@ namespace {
class ActualTimer : public MockableTime::Timer {
public:
- // Initialize base timer with retain_user_info and is_repeating false.
- ActualTimer() : base_timer_(false, false) {}
-
+ ActualTimer() {}
~ActualTimer() override {}
// MockableTime::Timer implementation:
@@ -227,7 +226,7 @@ class ActualTimer : public MockableTime::Timer {
bool IsRunning() override { return base_timer_.IsRunning(); }
private:
- base::Timer base_timer_;
+ base::OneShotTimer base_timer_;
};
} // namespace
diff --git a/chromium/components/download/BUILD.gn b/chromium/components/download/BUILD.gn
index 668f4e62b73..737c02e9dd7 100644
--- a/chromium/components/download/BUILD.gn
+++ b/chromium/components/download/BUILD.gn
@@ -9,7 +9,7 @@ group("unit_tests") {
deps = [
"//components/download/content/internal:unit_tests",
"//components/download/content/public:unit_tests",
- "//components/download/downloader/in_progress:unit_tests",
+ "//components/download/database:unit_tests",
"//components/download/internal/background_service:unit_tests",
"//components/download/internal/common:unit_tests",
"//components/download/quarantine:unit_tests",
diff --git a/chromium/components/download/database/BUILD.gn b/chromium/components/download/database/BUILD.gn
new file mode 100644
index 00000000000..73d775e774c
--- /dev/null
+++ b/chromium/components/download/database/BUILD.gn
@@ -0,0 +1,64 @@
+# 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.
+
+if (is_android) {
+ import("//build/config/android/config.gni")
+ import("//build/config/android/rules.gni")
+}
+
+source_set("database") {
+ sources = [
+ "download_db.h",
+ "download_db_conversions.cc",
+ "download_db_conversions.h",
+ "download_db_entry.cc",
+ "download_db_entry.h",
+ "download_db_impl.cc",
+ "download_db_impl.h",
+ "download_info.cc",
+ "download_info.h",
+ "download_namespace.cc",
+ "download_namespace.h",
+ "in_progress/download_entry.cc",
+ "in_progress/download_entry.h",
+ "in_progress/in_progress_cache.h",
+ "in_progress/in_progress_cache_impl.cc",
+ "in_progress/in_progress_cache_impl.h",
+ "in_progress/in_progress_info.cc",
+ "in_progress/in_progress_info.h",
+ "in_progress/ukm_info.cc",
+ "in_progress/ukm_info.h",
+ "switches.cc",
+ "switches.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/download/database/proto",
+ "//components/leveldb_proto",
+ "//net",
+ "//services/metrics/public/cpp:metrics_cpp",
+ "//services/network/public/mojom",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+
+ sources = [
+ "download_db_conversions_unittest.cc",
+ "download_db_impl_unittest.cc",
+ "in_progress/in_progress_cache_impl_unittest.cc",
+ ]
+
+ deps = [
+ ":database",
+ "//base/test:test_support",
+ "//components/download/database/proto",
+ "//components/leveldb_proto:test_support",
+ "//content/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/download/downloader/in_progress/DEPS b/chromium/components/download/database/DEPS
index 74bead6e775..4e8777c36fb 100644
--- a/chromium/components/download/downloader/in_progress/DEPS
+++ b/chromium/components/download/database/DEPS
@@ -1,4 +1,5 @@
include_rules = [
"+components/download/public/common",
+ "+components/leveldb_proto",
"+services/metrics/public"
]
diff --git a/chromium/components/download/database/download_db.h b/chromium/components/download/database/download_db.h
new file mode 100644
index 00000000000..4400854acb7
--- /dev/null
+++ b/chromium/components/download/database/download_db.h
@@ -0,0 +1,51 @@
+// 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_DOWNLOAD_DATABASE_DOWNLOAD_DB_H_
+#define COMPONENTS_DOWNLOAD_DATABASE_DOWNLOAD_DB_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/optional.h"
+#include "components/download/database/download_namespace.h"
+
+namespace download {
+
+struct DownloadDBEntry;
+
+// A backing storage for persisting DownloadDBEntry objects.
+class DownloadDB {
+ public:
+ using LoadEntriesCallback = base::OnceCallback<void(
+ bool success,
+ std::unique_ptr<std::vector<DownloadDBEntry>> entries)>;
+ using InitializeCallback = base::OnceCallback<void(bool success)>;
+
+ virtual ~DownloadDB() = default;
+
+ // Returns whether or not this object is initialized and can be interracted
+ // with.
+ virtual bool IsInitialized() = 0;
+
+ // Initializes this db asynchronously, callback will be run on completion.
+ virtual void Initialize(InitializeCallback callback) = 0;
+
+ // Adds or updates |entry| in the storage.
+ virtual void AddOrReplace(const DownloadDBEntry& entry) = 0;
+
+ // Adds or updates multiple entries in the storage.
+ virtual void AddOrReplaceEntries(
+ const std::vector<DownloadDBEntry>& entry) = 0;
+
+ // Retrieves all entries with the given |download_namespace|.
+ virtual void LoadEntries(LoadEntriesCallback callback) = 0;
+
+ // Removes the Entry associated with |guid| from the storage.
+ virtual void Remove(const std::string& guid) = 0;
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_DATABASE_DOWNLOAD_DB_H_
diff --git a/chromium/components/download/downloader/in_progress/in_progress_conversions.cc b/chromium/components/download/database/download_db_conversions.cc
index 5620961d2c6..17c2d6c9ffe 100644
--- a/chromium/components/download/downloader/in_progress/in_progress_conversions.cc
+++ b/chromium/components/download/database/download_db_conversions.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/download/downloader/in_progress/in_progress_conversions.h"
+#include "components/download/database/download_db_conversions.h"
#include <utility>
#include "base/logging.h"
@@ -10,8 +10,8 @@
namespace download {
-DownloadEntry InProgressConversions::DownloadEntryFromProto(
- const metadata_pb::DownloadEntry& proto) {
+DownloadEntry DownloadDBConversions::DownloadEntryFromProto(
+ const download_pb::DownloadEntry& proto) {
DownloadEntry entry;
entry.guid = proto.guid();
entry.request_origin = proto.request_origin();
@@ -25,9 +25,9 @@ DownloadEntry InProgressConversions::DownloadEntryFromProto(
return entry;
}
-metadata_pb::DownloadEntry InProgressConversions::DownloadEntryToProto(
+download_pb::DownloadEntry DownloadDBConversions::DownloadEntryToProto(
const DownloadEntry& entry) {
- metadata_pb::DownloadEntry proto;
+ download_pb::DownloadEntry proto;
proto.set_guid(entry.guid);
proto.set_request_origin(entry.request_origin);
proto.set_download_source(DownloadSourceToProto(entry.download_source));
@@ -42,28 +42,28 @@ metadata_pb::DownloadEntry InProgressConversions::DownloadEntryToProto(
}
// static
-DownloadSource InProgressConversions::DownloadSourceFromProto(
- metadata_pb::DownloadSource download_source) {
+DownloadSource DownloadDBConversions::DownloadSourceFromProto(
+ download_pb::DownloadSource download_source) {
switch (download_source) {
- case metadata_pb::DownloadSource::UNKNOWN:
+ case download_pb::DownloadSource::UNKNOWN:
return DownloadSource::UNKNOWN;
- case metadata_pb::DownloadSource::NAVIGATION:
+ case download_pb::DownloadSource::NAVIGATION:
return DownloadSource::NAVIGATION;
- case metadata_pb::DownloadSource::DRAG_AND_DROP:
+ case download_pb::DownloadSource::DRAG_AND_DROP:
return DownloadSource::DRAG_AND_DROP;
- case metadata_pb::DownloadSource::FROM_RENDERER:
+ case download_pb::DownloadSource::FROM_RENDERER:
return DownloadSource::FROM_RENDERER;
- case metadata_pb::DownloadSource::EXTENSION_API:
+ case download_pb::DownloadSource::EXTENSION_API:
return DownloadSource::EXTENSION_API;
- case metadata_pb::DownloadSource::EXTENSION_INSTALLER:
+ case download_pb::DownloadSource::EXTENSION_INSTALLER:
return DownloadSource::EXTENSION_INSTALLER;
- case metadata_pb::DownloadSource::INTERNAL_API:
+ case download_pb::DownloadSource::INTERNAL_API:
return DownloadSource::INTERNAL_API;
- case metadata_pb::DownloadSource::WEB_CONTENTS_API:
+ case download_pb::DownloadSource::WEB_CONTENTS_API:
return DownloadSource::WEB_CONTENTS_API;
- case metadata_pb::DownloadSource::OFFLINE_PAGE:
+ case download_pb::DownloadSource::OFFLINE_PAGE:
return DownloadSource::OFFLINE_PAGE;
- case metadata_pb::DownloadSource::CONTEXT_MENU:
+ case download_pb::DownloadSource::CONTEXT_MENU:
return DownloadSource::CONTEXT_MENU;
}
NOTREACHED();
@@ -71,56 +71,56 @@ DownloadSource InProgressConversions::DownloadSourceFromProto(
}
// static
-metadata_pb::DownloadSource InProgressConversions::DownloadSourceToProto(
+download_pb::DownloadSource DownloadDBConversions::DownloadSourceToProto(
DownloadSource download_source) {
switch (download_source) {
case DownloadSource::UNKNOWN:
- return metadata_pb::DownloadSource::UNKNOWN;
+ return download_pb::DownloadSource::UNKNOWN;
case DownloadSource::NAVIGATION:
- return metadata_pb::DownloadSource::NAVIGATION;
+ return download_pb::DownloadSource::NAVIGATION;
case DownloadSource::DRAG_AND_DROP:
- return metadata_pb::DownloadSource::DRAG_AND_DROP;
+ return download_pb::DownloadSource::DRAG_AND_DROP;
case DownloadSource::FROM_RENDERER:
- return metadata_pb::DownloadSource::FROM_RENDERER;
+ return download_pb::DownloadSource::FROM_RENDERER;
case DownloadSource::EXTENSION_API:
- return metadata_pb::DownloadSource::EXTENSION_API;
+ return download_pb::DownloadSource::EXTENSION_API;
case DownloadSource::EXTENSION_INSTALLER:
- return metadata_pb::DownloadSource::EXTENSION_INSTALLER;
+ return download_pb::DownloadSource::EXTENSION_INSTALLER;
case DownloadSource::INTERNAL_API:
- return metadata_pb::DownloadSource::INTERNAL_API;
+ return download_pb::DownloadSource::INTERNAL_API;
case DownloadSource::WEB_CONTENTS_API:
- return metadata_pb::DownloadSource::WEB_CONTENTS_API;
+ return download_pb::DownloadSource::WEB_CONTENTS_API;
case DownloadSource::OFFLINE_PAGE:
- return metadata_pb::DownloadSource::OFFLINE_PAGE;
+ return download_pb::DownloadSource::OFFLINE_PAGE;
case DownloadSource::CONTEXT_MENU:
- return metadata_pb::DownloadSource::CONTEXT_MENU;
+ return download_pb::DownloadSource::CONTEXT_MENU;
}
NOTREACHED();
- return metadata_pb::DownloadSource::UNKNOWN;
+ return download_pb::DownloadSource::UNKNOWN;
}
-std::vector<DownloadEntry> InProgressConversions::DownloadEntriesFromProto(
- const metadata_pb::DownloadEntries& proto) {
+std::vector<DownloadEntry> DownloadDBConversions::DownloadEntriesFromProto(
+ const download_pb::DownloadEntries& proto) {
std::vector<DownloadEntry> entries;
for (int i = 0; i < proto.entries_size(); i++)
entries.push_back(DownloadEntryFromProto(proto.entries(i)));
return entries;
}
-metadata_pb::DownloadEntries InProgressConversions::DownloadEntriesToProto(
+download_pb::DownloadEntries DownloadDBConversions::DownloadEntriesToProto(
const std::vector<DownloadEntry>& entries) {
- metadata_pb::DownloadEntries proto;
+ download_pb::DownloadEntries proto;
for (size_t i = 0; i < entries.size(); i++) {
- metadata_pb::DownloadEntry* proto_entry = proto.add_entries();
+ download_pb::DownloadEntry* proto_entry = proto.add_entries();
*proto_entry = DownloadEntryToProto(entries[i]);
}
return proto;
}
// static
-metadata_pb::HttpRequestHeader InProgressConversions::HttpRequestHeaderToProto(
+download_pb::HttpRequestHeader DownloadDBConversions::HttpRequestHeaderToProto(
const std::pair<std::string, std::string>& header) {
- metadata_pb::HttpRequestHeader proto;
+ download_pb::HttpRequestHeader proto;
if (header.first.empty())
return proto;
@@ -131,8 +131,8 @@ metadata_pb::HttpRequestHeader InProgressConversions::HttpRequestHeaderToProto(
// static
std::pair<std::string, std::string>
-InProgressConversions::HttpRequestHeaderFromProto(
- const metadata_pb::HttpRequestHeader& proto) {
+DownloadDBConversions::HttpRequestHeaderFromProto(
+ const download_pb::HttpRequestHeader& proto) {
if (proto.key().empty())
return std::pair<std::string, std::string>();
@@ -140,11 +140,15 @@ InProgressConversions::HttpRequestHeaderFromProto(
}
// static
-metadata_pb::InProgressInfo InProgressConversions::InProgressInfoToProto(
+download_pb::InProgressInfo DownloadDBConversions::InProgressInfoToProto(
const InProgressInfo& in_progress_info) {
- metadata_pb::InProgressInfo proto;
+ download_pb::InProgressInfo proto;
for (size_t i = 0; i < in_progress_info.url_chain.size(); ++i)
proto.add_url_chain(in_progress_info.url_chain[i].spec());
+ proto.set_referrer_url(in_progress_info.referrer_url.spec());
+ proto.set_site_url(in_progress_info.site_url.spec());
+ proto.set_tab_url(in_progress_info.tab_url.spec());
+ proto.set_tab_referrer_url(in_progress_info.tab_referrer_url.spec());
proto.set_fetch_error_body(in_progress_info.fetch_error_body);
for (const auto& header : in_progress_info.request_headers) {
auto* proto_header = proto.add_request_headers();
@@ -152,6 +156,8 @@ metadata_pb::InProgressInfo InProgressConversions::InProgressInfoToProto(
}
proto.set_etag(in_progress_info.etag);
proto.set_last_modified(in_progress_info.last_modified);
+ proto.set_mime_type(in_progress_info.mime_type);
+ proto.set_original_mime_type(in_progress_info.original_mime_type);
proto.set_total_bytes(in_progress_info.total_bytes);
base::Pickle current_path;
in_progress_info.current_path.WriteToPickle(&current_path);
@@ -160,10 +166,17 @@ metadata_pb::InProgressInfo InProgressConversions::InProgressInfoToProto(
in_progress_info.target_path.WriteToPickle(&target_path);
proto.set_target_path(target_path.data(), target_path.size());
proto.set_received_bytes(in_progress_info.received_bytes);
- proto.set_end_time(
- in_progress_info.end_time.ToDeltaSinceWindowsEpoch().InMilliseconds());
+ proto.set_start_time(
+ in_progress_info.start_time.is_null()
+ ? -1
+ : in_progress_info.start_time.ToDeltaSinceWindowsEpoch()
+ .InMilliseconds());
+ proto.set_end_time(in_progress_info.end_time.is_null()
+ ? -1
+ : in_progress_info.end_time.ToDeltaSinceWindowsEpoch()
+ .InMilliseconds());
for (size_t i = 0; i < in_progress_info.received_slices.size(); ++i) {
- metadata_pb::ReceivedSlice* slice = proto.add_received_slices();
+ download_pb::ReceivedSlice* slice = proto.add_received_slices();
slice->set_received_bytes(
in_progress_info.received_slices[i].received_bytes);
slice->set_offset(in_progress_info.received_slices[i].offset);
@@ -176,22 +189,27 @@ metadata_pb::InProgressInfo InProgressConversions::InProgressInfoToProto(
proto.set_interrupt_reason(in_progress_info.interrupt_reason);
proto.set_paused(in_progress_info.paused);
proto.set_metered(in_progress_info.metered);
- proto.set_request_origin(in_progress_info.request_origin);
proto.set_bytes_wasted(in_progress_info.bytes_wasted);
return proto;
}
// static
-InProgressInfo InProgressConversions::InProgressInfoFromProto(
- const metadata_pb::InProgressInfo& proto) {
+InProgressInfo DownloadDBConversions::InProgressInfoFromProto(
+ const download_pb::InProgressInfo& proto) {
InProgressInfo info;
for (const auto& url : proto.url_chain())
info.url_chain.emplace_back(url);
+ info.referrer_url = GURL(proto.referrer_url());
+ info.site_url = GURL(proto.site_url());
+ info.tab_url = GURL(proto.tab_url());
+ info.tab_referrer_url = GURL(proto.tab_referrer_url());
info.fetch_error_body = proto.fetch_error_body();
for (const auto& header : proto.request_headers())
info.request_headers.emplace_back(HttpRequestHeaderFromProto(header));
info.etag = proto.etag();
info.last_modified = proto.last_modified();
+ info.mime_type = proto.mime_type();
+ info.original_mime_type = proto.original_mime_type();
info.total_bytes = proto.total_bytes();
base::PickleIterator current_path(
base::Pickle(proto.current_path().data(), proto.current_path().size()));
@@ -200,8 +218,16 @@ InProgressInfo InProgressConversions::InProgressInfoFromProto(
base::Pickle(proto.target_path().data(), proto.target_path().size()));
info.target_path.ReadFromPickle(&target_path);
info.received_bytes = proto.received_bytes();
- info.end_time = base::Time::FromDeltaSinceWindowsEpoch(
- base::TimeDelta::FromMilliseconds(proto.end_time()));
+ info.start_time =
+ proto.start_time() == -1
+ ? base::Time()
+ : base::Time::FromDeltaSinceWindowsEpoch(
+ base::TimeDelta::FromMilliseconds(proto.start_time()));
+ info.end_time =
+ proto.end_time() == -1
+ ? base::Time()
+ : base::Time::FromDeltaSinceWindowsEpoch(
+ base::TimeDelta::FromMilliseconds(proto.end_time()));
for (int i = 0; i < proto.received_slices_size(); ++i) {
info.received_slices.emplace_back(proto.received_slices(i).offset(),
@@ -216,31 +242,31 @@ InProgressInfo InProgressConversions::InProgressInfoFromProto(
static_cast<DownloadInterruptReason>(proto.interrupt_reason());
info.paused = proto.paused();
info.metered = proto.metered();
- info.request_origin = proto.request_origin();
info.bytes_wasted = proto.bytes_wasted();
return info;
}
-UkmInfo InProgressConversions::UkmInfoFromProto(
- const metadata_pb::UkmInfo& proto) {
+UkmInfo DownloadDBConversions::UkmInfoFromProto(
+ const download_pb::UkmInfo& proto) {
UkmInfo info;
info.download_source = DownloadSourceFromProto(proto.download_source());
info.ukm_download_id = proto.ukm_download_id();
return info;
}
-metadata_pb::UkmInfo InProgressConversions::UkmInfoToProto(
+download_pb::UkmInfo DownloadDBConversions::UkmInfoToProto(
const UkmInfo& info) {
- metadata_pb::UkmInfo proto;
+ download_pb::UkmInfo proto;
proto.set_download_source(DownloadSourceToProto(info.download_source));
proto.set_ukm_download_id(info.ukm_download_id);
return proto;
}
-DownloadInfo InProgressConversions::DownloadInfoFromProto(
- const metadata_pb::DownloadInfo& proto) {
+DownloadInfo DownloadDBConversions::DownloadInfoFromProto(
+ const download_pb::DownloadInfo& proto) {
DownloadInfo info;
info.guid = proto.guid();
+ info.id = proto.id();
if (proto.has_ukm_info())
info.ukm_info = UkmInfoFromProto(proto.ukm_info());
if (proto.has_in_progress_info())
@@ -248,38 +274,37 @@ DownloadInfo InProgressConversions::DownloadInfoFromProto(
return info;
}
-metadata_pb::DownloadInfo InProgressConversions::DownloadInfoToProto(
+download_pb::DownloadInfo DownloadDBConversions::DownloadInfoToProto(
const DownloadInfo& info) {
- metadata_pb::DownloadInfo proto;
+ download_pb::DownloadInfo proto;
proto.set_guid(info.guid);
+ proto.set_id(info.id);
if (info.ukm_info.has_value()) {
- auto ukm_info = std::make_unique<metadata_pb::UkmInfo>(
+ auto ukm_info = std::make_unique<download_pb::UkmInfo>(
UkmInfoToProto(info.ukm_info.value()));
proto.set_allocated_ukm_info(ukm_info.release());
}
if (info.in_progress_info.has_value()) {
- auto in_progress_info = std::make_unique<metadata_pb::InProgressInfo>(
+ auto in_progress_info = std::make_unique<download_pb::InProgressInfo>(
InProgressInfoToProto(info.in_progress_info.value()));
proto.set_allocated_in_progress_info(in_progress_info.release());
}
return proto;
}
-DownloadDBEntry InProgressConversions::DownloadDBEntryFromProto(
- const metadata_pb::DownloadDBEntry& proto) {
+DownloadDBEntry DownloadDBConversions::DownloadDBEntryFromProto(
+ const download_pb::DownloadDBEntry& proto) {
DownloadDBEntry entry;
- entry.id = proto.id();
if (proto.has_download_info())
entry.download_info = DownloadInfoFromProto(proto.download_info());
return entry;
}
-metadata_pb::DownloadDBEntry InProgressConversions::DownloadDBEntryToProto(
+download_pb::DownloadDBEntry DownloadDBConversions::DownloadDBEntryToProto(
const DownloadDBEntry& info) {
- metadata_pb::DownloadDBEntry proto;
- proto.set_id(info.id);
+ download_pb::DownloadDBEntry proto;
if (info.download_info.has_value()) {
- auto download_info = std::make_unique<metadata_pb::DownloadInfo>(
+ auto download_info = std::make_unique<download_pb::DownloadInfo>(
DownloadInfoToProto(info.download_info.value()));
proto.set_allocated_download_info(download_info.release());
}
diff --git a/chromium/components/download/database/download_db_conversions.h b/chromium/components/download/database/download_db_conversions.h
new file mode 100644
index 00000000000..600e6196fcc
--- /dev/null
+++ b/chromium/components/download/database/download_db_conversions.h
@@ -0,0 +1,71 @@
+// 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_DOWNLOAD_DATABASE_DOWNLOAD_DB_CONVERSIONS_H_
+#define COMPONENTS_DOWNLOAD_DATABASE_DOWNLOAD_DB_CONVERSIONS_H_
+
+#include "base/macros.h"
+#include "components/download/database/download_db_entry.h"
+#include "components/download/database/download_info.h"
+#include "components/download/database/download_namespace.h"
+#include "components/download/database/in_progress/download_entry.h"
+#include "components/download/database/in_progress/in_progress_info.h"
+#include "components/download/database/in_progress/ukm_info.h"
+#include "components/download/database/proto/download_entry.pb.h"
+#include "components/download/database/proto/download_source.pb.h"
+
+namespace download {
+
+class DownloadDBConversions {
+ public:
+ static DownloadEntry DownloadEntryFromProto(
+ const download_pb::DownloadEntry& proto);
+
+ static download_pb::DownloadEntry DownloadEntryToProto(
+ const DownloadEntry& entry);
+
+ static DownloadSource DownloadSourceFromProto(
+ download_pb::DownloadSource download_source);
+
+ static download_pb::DownloadSource DownloadSourceToProto(
+ DownloadSource download_source);
+
+ static std::vector<DownloadEntry> DownloadEntriesFromProto(
+ const download_pb::DownloadEntries& proto);
+
+ static download_pb::DownloadEntries DownloadEntriesToProto(
+ const std::vector<DownloadEntry>& entries);
+
+ static download_pb::HttpRequestHeader HttpRequestHeaderToProto(
+ const std::pair<std::string, std::string>& header);
+
+ static std::pair<std::string, std::string> HttpRequestHeaderFromProto(
+ const download_pb::HttpRequestHeader& proto);
+
+ static download_pb::InProgressInfo InProgressInfoToProto(
+ const InProgressInfo& in_progress_info);
+
+ static InProgressInfo InProgressInfoFromProto(
+ const download_pb::InProgressInfo& proto);
+
+ static download_pb::UkmInfo UkmInfoToProto(const UkmInfo& ukm_info);
+
+ static UkmInfo UkmInfoFromProto(const download_pb::UkmInfo& proto);
+
+ static download_pb::DownloadInfo DownloadInfoToProto(
+ const DownloadInfo& download_info);
+
+ static DownloadInfo DownloadInfoFromProto(
+ const download_pb::DownloadInfo& proto);
+
+ static download_pb::DownloadDBEntry DownloadDBEntryToProto(
+ const DownloadDBEntry& entry);
+
+ static DownloadDBEntry DownloadDBEntryFromProto(
+ const download_pb::DownloadDBEntry& proto);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_DATABASE_DOWNLOAD_DB_CONVERSIONS_H_
diff --git a/chromium/components/download/downloader/in_progress/in_progress_conversions_unittest.cc b/chromium/components/download/database/download_db_conversions_unittest.cc
index df5fb673b1c..b82cd5e35fc 100644
--- a/chromium/components/download/downloader/in_progress/in_progress_conversions_unittest.cc
+++ b/chromium/components/download/database/download_db_conversions_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/download/downloader/in_progress/in_progress_conversions.h"
+#include "components/download/database/download_db_conversions.h"
#include "components/download/public/common/download_url_parameters.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -18,10 +18,17 @@ InProgressInfo CreateInProgressInfo() {
info.target_path = base::FilePath(FILE_PATH_LITERAL("/tmp"));
info.url_chain.emplace_back("http://foo");
info.url_chain.emplace_back("http://foo2");
- info.end_time = base::Time::NowFromSystemTime().LocalMidnight();
+ info.referrer_url = GURL("http://foo1.com");
+ info.site_url = GURL("http://foo.com");
+ info.tab_url = GURL("http://foo.com");
+ info.tab_referrer_url = GURL("http://abc.com");
+ info.start_time = base::Time::NowFromSystemTime().LocalMidnight();
+ info.end_time = base::Time();
info.etag = "A";
info.last_modified = "Wed, 1 Oct 2018 07:00:00 GMT";
info.received_bytes = 1000;
+ info.mime_type = "text/html";
+ info.original_mime_type = "text/html";
info.total_bytes = 10000;
info.state = DownloadItem::IN_PROGRESS;
info.danger_type = DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS;
@@ -32,7 +39,6 @@ InProgressInfo CreateInProgressInfo() {
info.metered = true;
info.received_slices.emplace_back(0, 500, false);
info.received_slices.emplace_back(5000, 500, false);
- info.request_origin = "request origin";
info.bytes_wasted = 1234;
info.fetch_error_body = true;
info.request_headers.emplace_back(
@@ -44,6 +50,8 @@ InProgressInfo CreateInProgressInfo() {
DownloadInfo CreateDownloadInfo() {
DownloadInfo info;
+ info.guid = "abcdefg";
+ info.id = 1234567;
info.in_progress_info = CreateInProgressInfo();
info.ukm_info = UkmInfo(DownloadSource::FROM_RENDERER, 100);
return info;
@@ -51,13 +59,13 @@ DownloadInfo CreateDownloadInfo() {
} // namespace
-class InProgressConversionsTest : public testing::Test,
- public InProgressConversions {
+class DownloadDBConversionsTest : public testing::Test,
+ public DownloadDBConversions {
public:
- ~InProgressConversionsTest() override {}
+ ~DownloadDBConversionsTest() override {}
};
-TEST_F(InProgressConversionsTest, DownloadEntry) {
+TEST_F(DownloadDBConversionsTest, DownloadEntry) {
// Entry with no fields.
DownloadEntry entry;
EXPECT_EQ(false, entry.fetch_error_body);
@@ -78,7 +86,7 @@ TEST_F(InProgressConversionsTest, DownloadEntry) {
EXPECT_EQ(entry, DownloadEntryFromProto(DownloadEntryToProto(entry)));
}
-TEST_F(InProgressConversionsTest, DownloadEntries) {
+TEST_F(DownloadDBConversionsTest, DownloadEntries) {
// Entries vector with no entries.
std::vector<DownloadEntry> entries;
EXPECT_EQ(entries, DownloadEntriesFromProto(DownloadEntriesToProto(entries)));
@@ -99,7 +107,7 @@ TEST_F(InProgressConversionsTest, DownloadEntries) {
EXPECT_EQ(entries, DownloadEntriesFromProto(DownloadEntriesToProto(entries)));
}
-TEST_F(InProgressConversionsTest, DownloadSource) {
+TEST_F(DownloadDBConversionsTest, DownloadSource) {
DownloadSource sources[] = {
DownloadSource::UNKNOWN, DownloadSource::NAVIGATION,
DownloadSource::DRAG_AND_DROP, DownloadSource::FROM_RENDERER,
@@ -112,7 +120,7 @@ TEST_F(InProgressConversionsTest, DownloadSource) {
}
}
-TEST_F(InProgressConversionsTest, HttpRequestHeaders) {
+TEST_F(DownloadDBConversionsTest, HttpRequestHeaders) {
std::pair<std::string, std::string> header;
EXPECT_EQ(header,
HttpRequestHeaderFromProto(HttpRequestHeaderToProto(header)));
@@ -121,7 +129,7 @@ TEST_F(InProgressConversionsTest, HttpRequestHeaders) {
HttpRequestHeaderFromProto(HttpRequestHeaderToProto(header)));
}
-TEST_F(InProgressConversionsTest, InProgressInfo) {
+TEST_F(DownloadDBConversionsTest, InProgressInfo) {
// InProgressInfo with no fields.
InProgressInfo info;
EXPECT_EQ(false, info.fetch_error_body);
@@ -133,12 +141,12 @@ TEST_F(InProgressConversionsTest, InProgressInfo) {
EXPECT_EQ(info, InProgressInfoFromProto(InProgressInfoToProto(info)));
}
-TEST_F(InProgressConversionsTest, UkmInfo) {
+TEST_F(DownloadDBConversionsTest, UkmInfo) {
UkmInfo info(DownloadSource::FROM_RENDERER, 100);
EXPECT_EQ(info, UkmInfoFromProto(UkmInfoToProto(info)));
}
-TEST_F(InProgressConversionsTest, DownloadInfo) {
+TEST_F(DownloadDBConversionsTest, DownloadInfo) {
DownloadInfo info;
EXPECT_EQ(info, DownloadInfoFromProto(DownloadInfoToProto(info)));
@@ -146,11 +154,10 @@ TEST_F(InProgressConversionsTest, DownloadInfo) {
EXPECT_EQ(info, DownloadInfoFromProto(DownloadInfoToProto(info)));
}
-TEST_F(InProgressConversionsTest, DownloadDBEntry) {
+TEST_F(DownloadDBConversionsTest, DownloadDBEntry) {
DownloadDBEntry entry;
EXPECT_EQ(entry, DownloadDBEntryFromProto(DownloadDBEntryToProto(entry)));
- entry.id = "abc";
entry.download_info = CreateDownloadInfo();
EXPECT_EQ(entry, DownloadDBEntryFromProto(DownloadDBEntryToProto(entry)));
}
diff --git a/chromium/components/download/downloader/in_progress/download_db_entry.cc b/chromium/components/download/database/download_db_entry.cc
index b36192a39a6..2a679274dfb 100644
--- a/chromium/components/download/downloader/in_progress/download_db_entry.cc
+++ b/chromium/components/download/database/download_db_entry.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/download/downloader/in_progress/download_db_entry.h"
+#include "components/download/database/download_db_entry.h"
namespace download {
@@ -13,7 +13,17 @@ DownloadDBEntry::DownloadDBEntry(const DownloadDBEntry& other) = default;
DownloadDBEntry::~DownloadDBEntry() = default;
bool DownloadDBEntry::operator==(const DownloadDBEntry& other) const {
- return id == other.id && download_info == other.download_info;
+ return download_info == other.download_info;
+}
+
+std::string DownloadDBEntry::GetGuid() const {
+ if (!download_info)
+ return std::string();
+ return download_info->guid;
+}
+
+bool DownloadDBEntry::operator!=(const DownloadDBEntry& other) const {
+ return !(*this == other);
}
} // namespace download
diff --git a/chromium/components/download/downloader/in_progress/download_db_entry.h b/chromium/components/download/database/download_db_entry.h
index 0aee2cf6175..84f6130e9dd 100644
--- a/chromium/components/download/downloader/in_progress/download_db_entry.h
+++ b/chromium/components/download/database/download_db_entry.h
@@ -2,13 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_DOWNLOAD_DOWNLOADER_IN_PROGRESS_DOWNLOAD_DB_ENTRY_H_
-#define COMPONENTS_DOWNLOAD_DOWNLOADER_IN_PROGRESS_DOWNLOAD_DB_ENTRY_H_
+#ifndef COMPONENTS_DOWNLOAD_DATABASE_DOWNLOAD_DB_ENTRY_H_
+#define COMPONENTS_DOWNLOAD_DATABASE_DOWNLOAD_DB_ENTRY_H_
#include <string>
#include "base/optional.h"
-#include "components/download/downloader/in_progress/download_info.h"
+#include "components/download/database/download_info.h"
+#include "components/download/database/download_namespace.h"
namespace download {
@@ -20,9 +21,10 @@ struct DownloadDBEntry {
~DownloadDBEntry();
bool operator==(const DownloadDBEntry& other) const;
+ bool operator!=(const DownloadDBEntry& other) const;
- // ID of the entry, this should be namespace + GUID of the download.
- std::string id;
+ // Gets a unique ID for this entry.
+ std::string GetGuid() const;
// Information about a regular download.
base::Optional<DownloadInfo> download_info;
@@ -30,4 +32,4 @@ struct DownloadDBEntry {
} // namespace download
-#endif // COMPONENTS_DOWNLOAD_DOWNLOADER_IN_PROGRESS_DOWNLOAD_DB_ENTRY_H_
+#endif // COMPONENTS_DOWNLOAD_DATABASE_DOWNLOAD_DB_ENTRY_H_
diff --git a/chromium/components/download/database/download_db_impl.cc b/chromium/components/download/database/download_db_impl.cc
new file mode 100644
index 00000000000..f33b52ae9b5
--- /dev/null
+++ b/chromium/components/download/database/download_db_impl.cc
@@ -0,0 +1,173 @@
+// 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/download/database/download_db_impl.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/task_scheduler/post_task.h"
+#include "components/download/database/download_db_conversions.h"
+#include "components/download/database/download_db_entry.h"
+#include "components/download/database/proto/download_entry.pb.h"
+#include "components/leveldb_proto/proto_database_impl.h"
+
+namespace download {
+
+namespace {
+
+const char kDatabaseClientName[] = "DownloadDB";
+using ProtoKeyVector = std::vector<std::string>;
+using ProtoEntryVector = std::vector<download_pb::DownloadDBEntry>;
+using ProtoKeyEntryVector =
+ std::vector<std::pair<std::string, download_pb::DownloadDBEntry>>;
+
+// Returns the prefix to all keys in the database.
+std::string GetDatabaseKeyPrefix(DownloadNamespace download_namespace) {
+ return DownloadNamespaceToString(download_namespace) + ",";
+}
+
+// Check if an input string is under a given namespace.
+bool IsUnderNameSpace(DownloadNamespace download_namespace,
+ const std::string& key) {
+ return base::StartsWith(key, GetDatabaseKeyPrefix(download_namespace),
+ base::CompareCase::INSENSITIVE_ASCII);
+}
+
+} // namespace
+
+DownloadDBImpl::DownloadDBImpl(DownloadNamespace download_namespace,
+ const base::FilePath& database_dir)
+ : DownloadDBImpl(
+ download_namespace,
+ database_dir,
+ std::make_unique<
+ leveldb_proto::ProtoDatabaseImpl<download_pb::DownloadDBEntry>>(
+ base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))) {}
+
+DownloadDBImpl::DownloadDBImpl(
+ DownloadNamespace download_namespace,
+ const base::FilePath& database_dir,
+ std::unique_ptr<leveldb_proto::ProtoDatabase<download_pb::DownloadDBEntry>>
+ db)
+ : database_dir_(database_dir),
+ db_(std::move(db)),
+ is_initialized_(false),
+ download_namespace_(download_namespace),
+ weak_factory_(this) {}
+
+DownloadDBImpl::~DownloadDBImpl() = default;
+
+bool DownloadDBImpl::IsInitialized() {
+ return is_initialized_;
+}
+
+void DownloadDBImpl::Initialize(InitializeCallback callback) {
+ DCHECK(!IsInitialized());
+ // These options reduce memory consumption.
+ leveldb_env::Options options = leveldb_proto::CreateSimpleOptions();
+ options.reuse_logs = false;
+ options.write_buffer_size = 64 << 10; // 64 KiB
+ db_->Init(kDatabaseClientName, database_dir_, options,
+ base::BindOnce(&DownloadDBImpl::OnDatabaseInitialized,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+ // TODO(qinmin): migrate all the data from InProgressCache into this database.
+}
+
+void DownloadDBImpl::DestroyAndReinitialize(InitializeCallback callback) {
+ is_initialized_ = false;
+ db_->Destroy(base::BindOnce(&DownloadDBImpl::OnDatabaseDestroyed,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void DownloadDBImpl::AddOrReplace(const DownloadDBEntry& entry) {
+ AddOrReplaceEntries(std::vector<DownloadDBEntry>{entry});
+}
+
+void DownloadDBImpl::AddOrReplaceEntries(
+ const std::vector<DownloadDBEntry>& entries) {
+ DCHECK(IsInitialized());
+ auto entries_to_save = std::make_unique<ProtoKeyEntryVector>();
+ for (const auto& entry : entries) {
+ download_pb::DownloadDBEntry proto =
+ DownloadDBConversions::DownloadDBEntryToProto(entry);
+ entries_to_save->emplace_back(GetEntryKey(entry.GetGuid()),
+ std::move(proto));
+ }
+ db_->UpdateEntries(std::move(entries_to_save),
+ std::make_unique<ProtoKeyVector>(),
+ base::BindOnce(&DownloadDBImpl::OnUpdateDone,
+ weak_factory_.GetWeakPtr()));
+}
+
+void DownloadDBImpl::LoadEntries(LoadEntriesCallback callback) {
+ db_->LoadEntriesWithFilter(
+ base::BindRepeating(&IsUnderNameSpace, download_namespace_),
+ base::BindOnce(&DownloadDBImpl::OnAllEntriesLoaded,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void DownloadDBImpl::Remove(const std::string& guid) {
+ DCHECK(IsInitialized());
+ auto keys_to_remove = std::make_unique<ProtoKeyVector>();
+ keys_to_remove->push_back(GetEntryKey(guid));
+ db_->UpdateEntries(std::make_unique<ProtoKeyEntryVector>(),
+ std::move(keys_to_remove),
+ base::BindOnce(&DownloadDBImpl::OnRemoveDone,
+ weak_factory_.GetWeakPtr()));
+}
+
+std::string DownloadDBImpl::GetEntryKey(const std::string& guid) const {
+ return GetDatabaseKeyPrefix(download_namespace_) + guid;
+}
+
+void DownloadDBImpl::OnAllEntriesLoaded(
+ LoadEntriesCallback callback,
+ bool success,
+ std::unique_ptr<ProtoEntryVector> entries) {
+ auto result = std::make_unique<std::vector<DownloadDBEntry>>();
+ if (!success) {
+ std::move(callback).Run(success, std::move(result));
+ return;
+ }
+
+ for (const auto& entry : *entries.get()) {
+ result->emplace_back(
+ DownloadDBConversions::DownloadDBEntryFromProto(entry));
+ }
+ std::move(callback).Run(success, std::move(result));
+}
+
+void DownloadDBImpl::OnDatabaseInitialized(InitializeCallback callback,
+ bool success) {
+ is_initialized_ = success;
+ std::move(callback).Run(success);
+}
+
+void DownloadDBImpl::OnDatabaseDestroyed(InitializeCallback callback,
+ bool success) {
+ if (!success) {
+ std::move(callback).Run(success);
+ return;
+ }
+
+ Initialize(std::move(callback));
+}
+
+void DownloadDBImpl::OnUpdateDone(bool success) {
+ // TODO(qinmin): add UMA for this.
+ if (!success)
+ LOG(ERROR) << "Update Download DB failed.";
+}
+
+void DownloadDBImpl::OnRemoveDone(bool success) {
+ // TODO(qinmin): add UMA for this.
+ if (!success)
+ LOG(ERROR) << "Remove entry from Download DB failed.";
+}
+
+} // namespace download
diff --git a/chromium/components/download/database/download_db_impl.h b/chromium/components/download/database/download_db_impl.h
new file mode 100644
index 00000000000..fe1a07c25e8
--- /dev/null
+++ b/chromium/components/download/database/download_db_impl.h
@@ -0,0 +1,90 @@
+// 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_DOWNLOAD_DATABASE_DOWNLOAD_DB_IMPL_H_
+#define COMPONENTS_DOWNLOAD_DATABASE_DOWNLOAD_DB_IMPL_H_
+
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/download/database/download_db.h"
+#include "components/leveldb_proto/proto_database.h"
+
+namespace download_pb {
+class DownloadDBEntry;
+}
+
+namespace download {
+
+// A protodb Implementation of DownloadDB.
+class DownloadDBImpl : public DownloadDB {
+ public:
+ DownloadDBImpl(DownloadNamespace download_namespace,
+ const base::FilePath& database_dir);
+ DownloadDBImpl(
+ DownloadNamespace download_namespace,
+ const base::FilePath& database_dir,
+ std::unique_ptr<
+ leveldb_proto::ProtoDatabase<download_pb::DownloadDBEntry>> db);
+ ~DownloadDBImpl() override;
+
+ // DownloadDB implementation.
+ bool IsInitialized() override;
+ void Initialize(InitializeCallback callback) override;
+ void AddOrReplace(const DownloadDBEntry& entry) override;
+ void AddOrReplaceEntries(
+ const std::vector<DownloadDBEntry>& entries) override;
+ void LoadEntries(LoadEntriesCallback callback) override;
+ void Remove(const std::string& guid) override;
+
+ private:
+ friend class DownloadDBTest;
+
+ void DestroyAndReinitialize(InitializeCallback callback);
+
+ // Returns the key of the db entry.
+ std::string GetEntryKey(const std::string& guid) const;
+
+ // Called when database is initialized.
+ void OnDatabaseInitialized(InitializeCallback callback, bool success);
+
+ // Called when database is destroyed.
+ void OnDatabaseDestroyed(InitializeCallback callback, bool success);
+
+ // Called when entry is updated.
+ void OnUpdateDone(bool success);
+
+ // Called when entry is removed.
+ void OnRemoveDone(bool success);
+
+ // Called when all database entries are loaded.
+ void OnAllEntriesLoaded(
+ LoadEntriesCallback callback,
+ bool success,
+ std::unique_ptr<std::vector<download_pb::DownloadDBEntry>> entries);
+
+ // Directory to store |db_|.
+ base::FilePath database_dir_;
+
+ // Proto db for storing all the entries.
+ std::unique_ptr<leveldb_proto::ProtoDatabase<download_pb::DownloadDBEntry>>
+ db_;
+
+ // Whether the object is initialized.
+ bool is_initialized_;
+
+ // Namespace of this db.
+ DownloadNamespace download_namespace_;
+
+ base::WeakPtrFactory<DownloadDBImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadDBImpl);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_DOWNLOAD_DB_IMPL_H_
diff --git a/chromium/components/download/database/download_db_impl_unittest.cc b/chromium/components/download/database/download_db_impl_unittest.cc
new file mode 100644
index 00000000000..6c35db93e5f
--- /dev/null
+++ b/chromium/components/download/database/download_db_impl_unittest.cc
@@ -0,0 +1,250 @@
+// 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/download/database/download_db_impl.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/guid.h"
+#include "components/download/database/download_db_conversions.h"
+#include "components/download/database/download_db_entry.h"
+#include "components/download/database/proto/download_entry.pb.h"
+#include "components/leveldb_proto/testing/fake_db.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+namespace download {
+
+namespace {
+
+DownloadDBEntry CreateDownloadDBEntry() {
+ DownloadDBEntry entry;
+ DownloadInfo download_info;
+ download_info.guid = base::GenerateGUID();
+ entry.download_info = download_info;
+ return entry;
+}
+
+std::string GetKey(const std::string& guid) {
+ return DownloadNamespaceToString(
+ DownloadNamespace::NAMESPACE_BROWSER_DOWNLOAD) +
+ "," + guid;
+}
+
+} // namespace
+
+class DownloadDBTest : public testing::Test {
+ public:
+ DownloadDBTest() : db_(nullptr), init_success_(false) {}
+
+ ~DownloadDBTest() override = default;
+
+ void CreateDatabase() {
+ auto db = std::make_unique<
+ leveldb_proto::test::FakeDB<download_pb::DownloadDBEntry>>(
+ &db_entries_);
+ db_ = db.get();
+ download_db_.reset(new DownloadDBImpl(
+ DownloadNamespace::NAMESPACE_BROWSER_DOWNLOAD,
+ base::FilePath(FILE_PATH_LITERAL("/test/db/fakepath")), std::move(db)));
+ }
+
+ void InitCallback(bool success) { init_success_ = success; }
+
+ void LoadCallback(std::vector<DownloadDBEntry>* loaded_entries,
+ bool success,
+ std::unique_ptr<std::vector<DownloadDBEntry>> entries) {
+ loaded_entries->swap(*entries);
+ }
+
+ void PrepopulateSampleEntries() {
+ DownloadDBEntry first = CreateDownloadDBEntry();
+ DownloadDBEntry second = CreateDownloadDBEntry();
+ DownloadDBEntry third = CreateDownloadDBEntry();
+ db_entries_.insert(
+ std::make_pair("unknown," + first.GetGuid(),
+ DownloadDBConversions::DownloadDBEntryToProto(first)));
+ db_entries_.insert(
+ std::make_pair(GetKey(second.GetGuid()),
+ DownloadDBConversions::DownloadDBEntryToProto(second)));
+ db_entries_.insert(
+ std::make_pair(GetKey(third.GetGuid()),
+ DownloadDBConversions::DownloadDBEntryToProto(third)));
+ }
+
+ void DestroyAndReinitialize() {
+ download_db_->DestroyAndReinitialize(
+ base::BindOnce(&DownloadDBTest::InitCallback, base::Unretained(this)));
+ ASSERT_FALSE(download_db_->IsInitialized());
+ }
+
+ protected:
+ std::map<std::string, download_pb::DownloadDBEntry> db_entries_;
+ leveldb_proto::test::FakeDB<download_pb::DownloadDBEntry>* db_;
+ std::unique_ptr<DownloadDBImpl> download_db_;
+ bool init_success_;
+ DISALLOW_COPY_AND_ASSIGN(DownloadDBTest);
+};
+
+TEST_F(DownloadDBTest, InitializeSucceeded) {
+ CreateDatabase();
+ ASSERT_FALSE(download_db_->IsInitialized());
+
+ download_db_->Initialize(
+ base::BindOnce(&DownloadDBTest::InitCallback, base::Unretained(this)));
+ db_->InitCallback(true);
+
+ ASSERT_TRUE(download_db_->IsInitialized());
+ ASSERT_TRUE(init_success_);
+}
+
+TEST_F(DownloadDBTest, InitializeFailed) {
+ CreateDatabase();
+ ASSERT_FALSE(download_db_->IsInitialized());
+
+ download_db_->Initialize(
+ base::BindOnce(&DownloadDBTest::InitCallback, base::Unretained(this)));
+ db_->InitCallback(false);
+
+ ASSERT_FALSE(download_db_->IsInitialized());
+ ASSERT_FALSE(init_success_);
+}
+
+TEST_F(DownloadDBTest, LoadEntries) {
+ PrepopulateSampleEntries();
+ CreateDatabase();
+ download_db_->Initialize(
+ base::BindOnce(&DownloadDBTest::InitCallback, base::Unretained(this)));
+ db_->InitCallback(true);
+ ASSERT_TRUE(download_db_->IsInitialized());
+
+ std::vector<DownloadDBEntry> loaded_entries;
+ download_db_->LoadEntries(base::BindOnce(
+ &DownloadDBTest::LoadCallback, base::Unretained(this), &loaded_entries));
+ db_->LoadCallback(true);
+ ASSERT_EQ(2u, loaded_entries.size());
+ for (auto db_entry : loaded_entries) {
+ ASSERT_EQ(db_entry,
+ DownloadDBConversions::DownloadDBEntryFromProto(
+ db_entries_.find(GetKey(db_entry.GetGuid()))->second));
+ }
+}
+
+TEST_F(DownloadDBTest, AddEntry) {
+ PrepopulateSampleEntries();
+ CreateDatabase();
+ download_db_->Initialize(
+ base::BindOnce(&DownloadDBTest::InitCallback, base::Unretained(this)));
+ db_->InitCallback(true);
+ ASSERT_TRUE(download_db_->IsInitialized());
+
+ DownloadDBEntry entry = CreateDownloadDBEntry();
+ download_db_->AddOrReplace(entry);
+ db_->UpdateCallback(true);
+
+ std::vector<DownloadDBEntry> loaded_entries;
+ download_db_->LoadEntries(base::BindOnce(
+ &DownloadDBTest::LoadCallback, base::Unretained(this), &loaded_entries));
+ db_->LoadCallback(true);
+ ASSERT_EQ(3u, loaded_entries.size());
+ for (auto db_entry : loaded_entries) {
+ ASSERT_EQ(db_entry,
+ DownloadDBConversions::DownloadDBEntryFromProto(
+ db_entries_.find(GetKey(db_entry.GetGuid()))->second));
+ }
+}
+
+TEST_F(DownloadDBTest, ReplaceEntry) {
+ DownloadDBEntry first = CreateDownloadDBEntry();
+ DownloadDBEntry second = CreateDownloadDBEntry();
+ db_entries_.insert(
+ std::make_pair(GetKey(first.GetGuid()),
+ DownloadDBConversions::DownloadDBEntryToProto(first)));
+ db_entries_.insert(
+ std::make_pair(GetKey(second.GetGuid()),
+ DownloadDBConversions::DownloadDBEntryToProto(second)));
+ CreateDatabase();
+ download_db_->Initialize(
+ base::BindOnce(&DownloadDBTest::InitCallback, base::Unretained(this)));
+ db_->InitCallback(true);
+ ASSERT_TRUE(download_db_->IsInitialized());
+
+ InProgressInfo in_progress_info;
+ in_progress_info.current_path =
+ base::FilePath(FILE_PATH_LITERAL("/tmp.crdownload"));
+ in_progress_info.target_path = base::FilePath(FILE_PATH_LITERAL("/tmp"));
+ in_progress_info.url_chain.emplace_back("http://foo");
+ in_progress_info.url_chain.emplace_back("http://foo2");
+ first.download_info->in_progress_info = in_progress_info;
+ download_db_->AddOrReplace(first);
+ db_->UpdateCallback(true);
+
+ std::vector<DownloadDBEntry> loaded_entries;
+ download_db_->LoadEntries(base::BindOnce(
+ &DownloadDBTest::LoadCallback, base::Unretained(this), &loaded_entries));
+ db_->LoadCallback(true);
+ ASSERT_EQ(2u, loaded_entries.size());
+ for (auto db_entry : loaded_entries) {
+ ASSERT_EQ(db_entry,
+ DownloadDBConversions::DownloadDBEntryFromProto(
+ db_entries_.find(GetKey(db_entry.GetGuid()))->second));
+ }
+}
+
+TEST_F(DownloadDBTest, Remove) {
+ DownloadDBEntry first = CreateDownloadDBEntry();
+ DownloadDBEntry second = CreateDownloadDBEntry();
+ db_entries_.insert(
+ std::make_pair(GetKey(first.GetGuid()),
+ DownloadDBConversions::DownloadDBEntryToProto(first)));
+ db_entries_.insert(
+ std::make_pair(GetKey(second.GetGuid()),
+ DownloadDBConversions::DownloadDBEntryToProto(second)));
+ CreateDatabase();
+ download_db_->Initialize(
+ base::BindOnce(&DownloadDBTest::InitCallback, base::Unretained(this)));
+ db_->InitCallback(true);
+ ASSERT_TRUE(download_db_->IsInitialized());
+
+ download_db_->Remove(first.GetGuid());
+ db_->UpdateCallback(true);
+
+ std::vector<DownloadDBEntry> loaded_entries;
+ download_db_->LoadEntries(base::BindOnce(
+ &DownloadDBTest::LoadCallback, base::Unretained(this), &loaded_entries));
+ db_->LoadCallback(true);
+ ASSERT_EQ(1u, loaded_entries.size());
+ ASSERT_EQ(loaded_entries[0],
+ DownloadDBConversions::DownloadDBEntryFromProto(
+ db_entries_.find(GetKey(loaded_entries[0].GetGuid()))->second));
+}
+
+TEST_F(DownloadDBTest, DestroyAndReinitialize) {
+ PrepopulateSampleEntries();
+ CreateDatabase();
+ download_db_->Initialize(
+ base::BindOnce(&DownloadDBTest::InitCallback, base::Unretained(this)));
+ db_->InitCallback(true);
+ ASSERT_TRUE(download_db_->IsInitialized());
+
+ std::vector<DownloadDBEntry> loaded_entries;
+ download_db_->LoadEntries(base::BindOnce(
+ &DownloadDBTest::LoadCallback, base::Unretained(this), &loaded_entries));
+ db_->LoadCallback(true);
+ ASSERT_EQ(2u, loaded_entries.size());
+
+ DestroyAndReinitialize();
+
+ db_->DestroyCallback(true);
+ download_db_->LoadEntries(base::BindOnce(
+ &DownloadDBTest::LoadCallback, base::Unretained(this), &loaded_entries));
+ db_->LoadCallback(true);
+ ASSERT_EQ(0u, loaded_entries.size());
+}
+
+} // namespace download
diff --git a/chromium/components/download/downloader/in_progress/download_info.cc b/chromium/components/download/database/download_info.cc
index 80b7f6257d6..fec67b4048e 100644
--- a/chromium/components/download/downloader/in_progress/download_info.cc
+++ b/chromium/components/download/database/download_info.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/download/downloader/in_progress/download_info.h"
+#include "components/download/database/download_info.h"
namespace download {
@@ -13,7 +13,7 @@ DownloadInfo::DownloadInfo(const DownloadInfo& other) = default;
DownloadInfo::~DownloadInfo() = default;
bool DownloadInfo::operator==(const DownloadInfo& other) const {
- return guid == other.guid && ukm_info == other.ukm_info &&
+ return guid == other.guid && id == other.id && ukm_info == other.ukm_info &&
in_progress_info == other.in_progress_info;
}
diff --git a/chromium/components/download/downloader/in_progress/download_info.h b/chromium/components/download/database/download_info.h
index 80085ee1406..483115fb72e 100644
--- a/chromium/components/download/downloader/in_progress/download_info.h
+++ b/chromium/components/download/database/download_info.h
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_DOWNLOAD_DOWNLOADER_IN_PROGRESS_DOWNLOAD_INFO_H_
-#define COMPONENTS_DOWNLOAD_DOWNLOADER_IN_PROGRESS_DOWNLOAD_INFO_H_
+#ifndef COMPONENTS_DOWNLOAD_DATABASE_DOWNLOAD_INFO_H_
+#define COMPONENTS_DOWNLOAD_DATABASE_DOWNLOAD_INFO_H_
#include <string>
#include "base/optional.h"
-#include "components/download/downloader/in_progress/in_progress_info.h"
-#include "components/download/downloader/in_progress/ukm_info.h"
+#include "components/download/database/in_progress/in_progress_info.h"
+#include "components/download/database/in_progress/ukm_info.h"
namespace download {
@@ -25,6 +25,10 @@ struct DownloadInfo {
// Download GUID.
std::string guid;
+ // Download ID.
+ // Deprecated, only kept for the purpose of download extension API.
+ int id = -1;
+
// UKM information for reporting.
base::Optional<UkmInfo> ukm_info;
@@ -34,4 +38,4 @@ struct DownloadInfo {
} // namespace download
-#endif // COMPONENTS_DOWNLOAD_DOWNLOADER_IN_PROGRESS_DOWNLOAD_INFO_H_
+#endif // COMPONENTS_DOWNLOAD_DATABASE_DOWNLOAD_INFO_H_
diff --git a/chromium/components/download/database/download_namespace.cc b/chromium/components/download/database/download_namespace.cc
new file mode 100644
index 00000000000..ac9554a43d2
--- /dev/null
+++ b/chromium/components/download/database/download_namespace.cc
@@ -0,0 +1,30 @@
+// 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/download/database/download_namespace.h"
+
+namespace download {
+
+namespace {
+const char kBrowserDownloadNamespace[] = "download";
+const char kUnknownNamespace[] = "unknown";
+} // namespace
+
+std::string DownloadNamespaceToString(DownloadNamespace download_namespace) {
+ switch (download_namespace) {
+ case DownloadNamespace::NAMESPACE_BROWSER_DOWNLOAD:
+ return kBrowserDownloadNamespace;
+ default:
+ return kUnknownNamespace;
+ }
+}
+
+DownloadNamespace DownloadNamespaceFromString(
+ const std::string& namespace_string) {
+ if (namespace_string == kBrowserDownloadNamespace)
+ return DownloadNamespace::NAMESPACE_BROWSER_DOWNLOAD;
+ return DownloadNamespace::NAMESPACE_UNKNOWN;
+}
+
+} // namespace download
diff --git a/chromium/components/download/database/download_namespace.h b/chromium/components/download/database/download_namespace.h
new file mode 100644
index 00000000000..923f61e01c1
--- /dev/null
+++ b/chromium/components/download/database/download_namespace.h
@@ -0,0 +1,30 @@
+// 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_DOWNLOAD_DATABASE_DOWNLOAD_NAMESPACE_H_
+#define COMPONENTS_DOWNLOAD_DATABASE_DOWNLOAD_NAMESPACE_H_
+
+#include <string>
+
+namespace download {
+
+// The namespace of the download, used for classifying the download.
+// Entries in this enum can only be appended instead of being deleted or reused.
+enum class DownloadNamespace {
+ NAMESPACE_UNKNOWN = 0,
+ // Regular browser downloads, either through context menu or navigation.
+ NAMESPACE_BROWSER_DOWNLOAD,
+};
+
+// Converts a namespace to its string representation.
+std::string DownloadNamespaceToString(DownloadNamespace download_namespace);
+
+// Converts a string representation of a namespace to its namespace, or
+// NAMESPACE_UNKNOWN if the string doesn't map to one.
+DownloadNamespace DownloadNamespaceFromString(
+ const std::string& namespace_string);
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_DATABASE_DOWNLOAD_NAMESPACE_H_
diff --git a/chromium/components/download/downloader/in_progress/download_entry.cc b/chromium/components/download/database/in_progress/download_entry.cc
index 7bf35cba5d8..176b90db04e 100644
--- a/chromium/components/download/downloader/in_progress/download_entry.cc
+++ b/chromium/components/download/database/in_progress/download_entry.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/download/downloader/in_progress/download_entry.h"
+#include "components/download/database/in_progress/download_entry.h"
namespace download {
diff --git a/chromium/components/download/downloader/in_progress/download_entry.h b/chromium/components/download/database/in_progress/download_entry.h
index c06ac01e531..a68a0b6e08a 100644
--- a/chromium/components/download/downloader/in_progress/download_entry.h
+++ b/chromium/components/download/database/in_progress/download_entry.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_DOWNLOAD_IN_PROGRESS_DOWNLOAD_ENTRY_H_
-#define COMPONENTS_DOWNLOAD_IN_PROGRESS_DOWNLOAD_ENTRY_H_
+#ifndef COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_DOWNLOAD_ENTRY_H_
+#define COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_DOWNLOAD_ENTRY_H_
#include <string>
@@ -59,4 +59,4 @@ struct DownloadEntry {
} // namespace download
-#endif // COMPONENTS_DOWNLOAD_IN_PROGRESS_DOWNLOAD_ENTRY_H_
+#endif // COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_DOWNLOAD_ENTRY_H_
diff --git a/chromium/components/download/downloader/in_progress/in_progress_cache.h b/chromium/components/download/database/in_progress/in_progress_cache.h
index 02459c0e026..705e37153fc 100644
--- a/chromium/components/download/downloader/in_progress/in_progress_cache.h
+++ b/chromium/components/download/database/in_progress/in_progress_cache.h
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_DOWNLOAD_IN_PROGRESS_CACHE_H_
-#define COMPONENTS_DOWNLOAD_IN_PROGRESS_CACHE_H_
+#ifndef COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_IN_PROGRESS_CACHE_H_
+#define COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_IN_PROGRESS_CACHE_H_
#include <string>
#include "base/optional.h"
-#include "components/download/downloader/in_progress/download_entry.h"
+#include "components/download/database/in_progress/download_entry.h"
namespace download {
@@ -38,4 +38,4 @@ class InProgressCache {
} // namespace download
-#endif // COMPONENTS_DOWNLOAD_IN_PROGRESS_CACHE_H_
+#endif // COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_IN_PROGRESS_CACHE_H_
diff --git a/chromium/components/download/downloader/in_progress/in_progress_cache_impl.cc b/chromium/components/download/database/in_progress/in_progress_cache_impl.cc
index 6088307f523..a02ec9390fd 100644
--- a/chromium/components/download/downloader/in_progress/in_progress_cache_impl.cc
+++ b/chromium/components/download/database/in_progress/in_progress_cache_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/download/downloader/in_progress/in_progress_cache_impl.h"
+#include "components/download/database/in_progress/in_progress_cache_impl.h"
#include "base/bind.h"
#include "base/files/file.h"
@@ -10,7 +10,7 @@
#include "base/files/important_file_writer.h"
#include "base/task_runner_util.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/download/downloader/in_progress/in_progress_conversions.h"
+#include "components/download/database/download_db_conversions.h"
namespace download {
@@ -20,7 +20,7 @@ const base::FilePath::CharType kDownloadMetadataStoreFilename[] =
namespace {
// Helper functions for |entries_| related operations.
-int GetIndexFromEntries(const metadata_pb::DownloadEntries& entries,
+int GetIndexFromEntries(const download_pb::DownloadEntries& entries,
const std::string& guid) {
int size_of_entries = entries.entries_size();
for (int i = 0; i < size_of_entries; i++) {
@@ -30,28 +30,29 @@ int GetIndexFromEntries(const metadata_pb::DownloadEntries& entries,
return -1;
}
-void AddOrReplaceEntryInEntries(metadata_pb::DownloadEntries& entries,
+void AddOrReplaceEntryInEntries(download_pb::DownloadEntries& entries,
const DownloadEntry& entry) {
- metadata_pb::DownloadEntry metadata_entry =
- InProgressConversions::DownloadEntryToProto(entry);
+ download_pb::DownloadEntry metadata_entry =
+ DownloadDBConversions::DownloadEntryToProto(entry);
int entry_index = GetIndexFromEntries(entries, metadata_entry.guid());
- metadata_pb::DownloadEntry* entry_ptr =
+ download_pb::DownloadEntry* entry_ptr =
(entry_index < 0) ? entries.add_entries()
: entries.mutable_entries(entry_index);
*entry_ptr = metadata_entry;
}
base::Optional<DownloadEntry> GetEntryFromEntries(
- const metadata_pb::DownloadEntries& entries,
+ const download_pb::DownloadEntries& entries,
const std::string& guid) {
int entry_index = GetIndexFromEntries(entries, guid);
if (entry_index < 0)
return base::nullopt;
- return InProgressConversions::DownloadEntryFromProto(
+
+ return DownloadDBConversions::DownloadEntryFromProto(
entries.entries(entry_index));
}
-void RemoveEntryFromEntries(metadata_pb::DownloadEntries& entries,
+void RemoveEntryFromEntries(download_pb::DownloadEntries& entries,
const std::string& guid) {
int entry_index = GetIndexFromEntries(entries, guid);
if (entry_index >= 0)
@@ -103,7 +104,7 @@ std::vector<char> ReadEntriesFromFile(base::FilePath file_path) {
return file_data;
}
-std::string EntriesToString(const metadata_pb::DownloadEntries& entries) {
+std::string EntriesToString(const download_pb::DownloadEntries& entries) {
std::string entries_string;
if (!entries.SerializeToString(&entries_string)) {
// TODO(crbug.com/778425): Have more robust error-handling for serialization
diff --git a/chromium/components/download/downloader/in_progress/in_progress_cache_impl.h b/chromium/components/download/database/in_progress/in_progress_cache_impl.h
index 23687e0b7b8..e3218c777c4 100644
--- a/chromium/components/download/downloader/in_progress/in_progress_cache_impl.h
+++ b/chromium/components/download/database/in_progress/in_progress_cache_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_DOWNLOAD_IN_PROGRESS_CACHE_IMPL_H_
-#define COMPONENTS_DOWNLOAD_IN_PROGRESS_CACHE_IMPL_H_
+#ifndef COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_IN_PROGRESS_CACHE_IMPL_H_
+#define COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_IN_PROGRESS_CACHE_IMPL_H_
#include <string>
@@ -11,9 +11,9 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/sequenced_task_runner.h"
-#include "components/download/downloader/in_progress/download_entry.h"
-#include "components/download/downloader/in_progress/in_progress_cache.h"
-#include "components/download/downloader/in_progress/proto/download_entry.pb.h"
+#include "components/download/database/in_progress/download_entry.h"
+#include "components/download/database/in_progress/in_progress_cache.h"
+#include "components/download/database/proto/download_entry.pb.h"
namespace download {
@@ -46,7 +46,7 @@ class InProgressCacheImpl : public InProgressCache {
void OnInitialized(base::OnceClosure callback,
const std::vector<char>& entries);
- metadata_pb::DownloadEntries entries_;
+ download_pb::DownloadEntries entries_;
base::FilePath file_path_;
InitializationStatus initialization_status_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
@@ -57,4 +57,4 @@ class InProgressCacheImpl : public InProgressCache {
} // namespace download
-#endif // COMPONENTS_DOWNLOAD_IN_PROGRESS_CACHE_IMPL_H_
+#endif // COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_IN_PROGRESS_CACHE_IMPL_H_
diff --git a/chromium/components/download/downloader/in_progress/in_progress_cache_impl_unittest.cc b/chromium/components/download/database/in_progress/in_progress_cache_impl_unittest.cc
index 8150815bf31..9bad19700d8 100644
--- a/chromium/components/download/downloader/in_progress/in_progress_cache_impl_unittest.cc
+++ b/chromium/components/download/database/in_progress/in_progress_cache_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/download/downloader/in_progress/in_progress_cache_impl.h"
+#include "components/download/database/in_progress/in_progress_cache_impl.h"
#include <memory>
diff --git a/chromium/components/download/downloader/in_progress/in_progress_info.cc b/chromium/components/download/database/in_progress/in_progress_info.cc
index 2a622e6da5f..15f6dd4ae4a 100644
--- a/chromium/components/download/downloader/in_progress/in_progress_info.cc
+++ b/chromium/components/download/database/in_progress/in_progress_info.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/download/downloader/in_progress/in_progress_info.h"
+#include "components/download/database/in_progress/in_progress_info.h"
namespace download {
@@ -13,20 +13,23 @@ InProgressInfo::InProgressInfo(const InProgressInfo& other) = default;
InProgressInfo::~InProgressInfo() = default;
bool InProgressInfo::operator==(const InProgressInfo& other) const {
- return url_chain == other.url_chain &&
+ return url_chain == other.url_chain && site_url == other.site_url &&
+ referrer_url == other.referrer_url && tab_url == other.tab_url &&
+ tab_referrer_url == other.tab_referrer_url &&
fetch_error_body == other.fetch_error_body &&
request_headers == other.request_headers && etag == other.etag &&
last_modified == other.last_modified &&
- total_bytes == other.total_bytes &&
+ total_bytes == other.total_bytes && mime_type == other.mime_type &&
+ original_mime_type == other.original_mime_type &&
current_path == other.current_path &&
target_path == other.target_path &&
- received_bytes == other.received_bytes && end_time == other.end_time &&
+ received_bytes == other.received_bytes &&
+ start_time == other.start_time && end_time == other.end_time &&
received_slices == other.received_slices && hash == other.hash &&
transient == other.transient && state == other.state &&
danger_type == other.danger_type &&
interrupt_reason == other.interrupt_reason && paused == other.paused &&
- metered == other.metered && request_origin == other.request_origin &&
- bytes_wasted == other.bytes_wasted;
+ metered == other.metered && bytes_wasted == other.bytes_wasted;
}
} // namespace download
diff --git a/chromium/components/download/downloader/in_progress/in_progress_info.h b/chromium/components/download/database/in_progress/in_progress_info.h
index b075e9bd336..4f6295420dc 100644
--- a/chromium/components/download/downloader/in_progress/in_progress_info.h
+++ b/chromium/components/download/database/in_progress/in_progress_info.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_DOWNLOAD_DOWNLOADER_IN_PROGRESS_IN_PROGRESS_INFO_H_
-#define COMPONENTS_DOWNLOAD_DOWNLOADER_IN_PROGRESS_IN_PROGRESS_INFO_H_
+#ifndef COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_IN_PROGRESS_INFO_H_
+#define COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_IN_PROGRESS_INFO_H_
#include <string>
#include <vector>
@@ -30,6 +30,18 @@ struct InProgressInfo {
// The url chain.
std::vector<GURL> url_chain;
+ // Referrer url.
+ GURL referrer_url;
+
+ // Site url.
+ GURL site_url;
+
+ // Tab url.
+ GURL tab_url;
+
+ // Tab referrer url.
+ GURL tab_referrer_url;
+
// If the entity body of unsuccessful HTTP response, like HTTP 404, will be
// downloaded.
bool fetch_error_body = false;
@@ -49,6 +61,12 @@ struct InProgressInfo {
// The total number of bytes in the download.
int64_t total_bytes = 0;
+ // Mime type.
+ std::string mime_type;
+
+ // Original mime type before all redirections.
+ std::string original_mime_type;
+
// destination info --------------------------------------------------------
// The current path to the download (potentially different from final if
@@ -61,6 +79,9 @@ struct InProgressInfo {
// The number of bytes received (so far).
int64_t received_bytes = 0;
+ // The time when the download started.
+ base::Time start_time;
+
// The time when the download completed.
base::Time end_time;
@@ -90,16 +111,13 @@ struct InProgressInfo {
// Whether this download is paused.
bool paused = false;
- // Whether the download is initiated on a metered network
- bool metered = false;
-
- // Represents the origin information for this download. Used by offline pages.
- std::string request_origin;
-
// Count for how many (extra) bytes were used (including resumption).
int64_t bytes_wasted = 0;
+
+ // Whether the download is initiated on a metered network
+ bool metered = false;
};
} // namespace download
-#endif // COMPONENTS_DOWNLOAD_DOWNLOADER_IN_PROGRESS_IN_PROGRESS_INFO_H_
+#endif // COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_IN_PROGRESS_INFO_H_
diff --git a/chromium/components/download/downloader/in_progress/ukm_info.cc b/chromium/components/download/database/in_progress/ukm_info.cc
index a02851403ac..1ce3dab413b 100644
--- a/chromium/components/download/downloader/in_progress/ukm_info.cc
+++ b/chromium/components/download/database/in_progress/ukm_info.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/download/downloader/in_progress/ukm_info.h"
+#include "components/download/database/in_progress/ukm_info.h"
namespace download {
diff --git a/chromium/components/download/downloader/in_progress/ukm_info.h b/chromium/components/download/database/in_progress/ukm_info.h
index ac3c73ff17d..079b32ff1c7 100644
--- a/chromium/components/download/downloader/in_progress/ukm_info.h
+++ b/chromium/components/download/database/in_progress/ukm_info.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_DOWNLOAD_DOWNLOADER_IN_PROGRESS_UKM_INFO_H_
-#define COMPONENTS_DOWNLOAD_DOWNLOADER_IN_PROGRESS_UKM_INFO_H_
+#ifndef COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_UKM_INFO_H_
+#define COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_UKM_INFO_H_
#include <stdint.h>
@@ -31,4 +31,4 @@ struct UkmInfo {
} // namespace download
-#endif // COMPONENTS_DOWNLOAD_DOWNLOADER_IN_PROGRESS_UKM_INFO_H_
+#endif // COMPONENTS_DOWNLOAD_DATABASE_IN_PROGRESS_UKM_INFO_H_
diff --git a/chromium/components/download/downloader/in_progress/proto/BUILD.gn b/chromium/components/download/database/proto/BUILD.gn
index c7151fe8c5f..8e2baf932e8 100644
--- a/chromium/components/download/downloader/in_progress/proto/BUILD.gn
+++ b/chromium/components/download/database/proto/BUILD.gn
@@ -5,7 +5,7 @@
import("//third_party/protobuf/proto_library.gni")
proto_library("proto") {
- visibility = [ "//components/download/downloader/in_progress:*" ]
+ visibility = [ "//components/download/database:*" ]
sources = [
"download_entry.proto",
diff --git a/chromium/components/download/downloader/in_progress/proto/download_entry.proto b/chromium/components/download/database/proto/download_entry.proto
index ddf18d4a196..026c2aaf918 100644
--- a/chromium/components/download/downloader/in_progress/proto/download_entry.proto
+++ b/chromium/components/download/database/proto/download_entry.proto
@@ -6,7 +6,7 @@ syntax = "proto2";
option optimize_for = LITE_RUNTIME;
-package metadata_pb;
+package download_pb;
import "download_source.proto";
@@ -47,38 +47,44 @@ message UkmInfo {
// Information about an in progress download.
message InProgressInfo {
repeated string url_chain = 1;
- optional bool fetch_error_body = 2;
- repeated HttpRequestHeader request_headers = 3;
- optional string etag = 4;
- optional string last_modified = 5;
- optional int64 total_bytes = 6;
- optional bytes current_path = 7; // Serialized pickles to support string16
- optional bytes target_path = 8; // Serialized pickles to support string16
- optional int64 received_bytes = 9;
- optional int64 end_time = 10;
- repeated ReceivedSlice received_slices = 11;
- optional string hash = 12;
- optional bool transient = 13;
- optional int32 state = 14;
- optional int32 danger_type = 15;
- optional int32 interrupt_reason = 16;
- optional bool paused = 17;
- optional bool metered = 18;
- optional string request_origin = 19;
- optional int64 bytes_wasted = 20;
+ optional string referrer_url = 2;
+ optional string site_url = 3;
+ optional string tab_url = 4;
+ optional string tab_referrer_url = 5;
+ optional bool fetch_error_body = 6;
+ repeated HttpRequestHeader request_headers = 7;
+ optional string etag = 8;
+ optional string last_modified = 9;
+ optional int64 total_bytes = 10;
+ optional string mime_type = 11;
+ optional string original_mime_type = 12;
+ optional bytes current_path = 13; // Serialized pickles to support string16
+ optional bytes target_path = 14; // Serialized pickles to support string16
+ optional int64 received_bytes = 15;
+ optional int64 start_time = 16;
+ optional int64 end_time = 17;
+ repeated ReceivedSlice received_slices = 18;
+ optional string hash = 19;
+ optional bool transient = 20;
+ optional int32 state = 21;
+ optional int32 danger_type = 22;
+ optional int32 interrupt_reason = 23;
+ optional bool paused = 24;
+ optional bool metered = 25;
+ optional int64 bytes_wasted = 26;
}
-// Stores various in-progress metadata related to a download.
+// Stores various metadata related to a download.
// WIP and will replace DownloadEntry.
message DownloadInfo {
optional string guid = 1;
- optional UkmInfo ukm_info = 2;
- optional InProgressInfo in_progress_info = 3;
+ optional int32 id = 2;
+ optional UkmInfo ukm_info = 3;
+ optional InProgressInfo in_progress_info = 4;
}
// database entry for download information.
message DownloadDBEntry {
- optional string id = 1;
// Add field for offline page download.
- oneof entry { DownloadInfo download_info = 2; }
-} \ No newline at end of file
+ oneof entry { DownloadInfo download_info = 1; }
+}
diff --git a/chromium/components/download/downloader/in_progress/proto/download_source.proto b/chromium/components/download/database/proto/download_source.proto
index 375b059f639..188ef571bc4 100644
--- a/chromium/components/download/downloader/in_progress/proto/download_source.proto
+++ b/chromium/components/download/database/proto/download_source.proto
@@ -6,7 +6,7 @@ syntax = "proto2";
option optimize_for = LITE_RUNTIME;
-package metadata_pb;
+package download_pb;
// This should stay in sync with the download::DownloadSource enum in
// components/download/public/core/download_source.h.
diff --git a/chromium/components/download/database/switches.cc b/chromium/components/download/database/switches.cc
new file mode 100644
index 00000000000..66416607439
--- /dev/null
+++ b/chromium/components/download/database/switches.cc
@@ -0,0 +1,17 @@
+// 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/download/database/switches.h"
+
+namespace download {
+
+namespace switches {
+
+// Enables using the default search engine country to show country specific
+// popular sites on the NTP.
+const char kEnableDownloadDB[] = "enable-download-db";
+
+} // namespace switches
+
+} // namespace download
diff --git a/chromium/components/download/database/switches.h b/chromium/components/download/database/switches.h
new file mode 100644
index 00000000000..14ce4bdcbdb
--- /dev/null
+++ b/chromium/components/download/database/switches.h
@@ -0,0 +1,19 @@
+// 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_DOWNLOAD_DATABASE_SWITCHES_H_
+#define COMPONENTS_DOWNLOAD_DATABASE_SWITCHES_H_
+
+namespace download {
+
+namespace switches {
+
+// Enables the use of download database to store download metadata.
+extern const char kEnableDownloadDB[];
+
+} // namespace switches
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_DATABASE_SWITCHES_H_
diff --git a/chromium/components/download/downloader/in_progress/BUILD.gn b/chromium/components/download/downloader/in_progress/BUILD.gn
deleted file mode 100644
index fea2be16b81..00000000000
--- a/chromium/components/download/downloader/in_progress/BUILD.gn
+++ /dev/null
@@ -1,54 +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.
-
-if (is_android) {
- import("//build/config/android/config.gni")
- import("//build/config/android/rules.gni")
-}
-
-source_set("in_progress") {
- sources = [
- "download_db_entry.cc",
- "download_db_entry.h",
- "download_entry.cc",
- "download_entry.h",
- "download_info.cc",
- "download_info.h",
- "in_progress_cache.h",
- "in_progress_cache_impl.cc",
- "in_progress_cache_impl.h",
- "in_progress_conversions.cc",
- "in_progress_conversions.h",
- "in_progress_info.cc",
- "in_progress_info.h",
- "ukm_info.cc",
- "ukm_info.h",
- ]
-
- deps = [
- "//base",
- "//components/download/downloader/in_progress/proto",
- "//net",
- "//services/metrics/public/cpp:metrics_cpp",
- "//services/network/public/mojom",
- ]
-}
-
-source_set("unit_tests") {
- testonly = true
-
- sources = [
- "in_progress_cache_impl_unittest.cc",
- "in_progress_conversions_unittest.cc",
- ]
-
- deps = [
- ":in_progress",
- "//base/test:test_support",
- "//components/download/downloader/in_progress/proto",
- "//content/test:test_support",
- "//testing/gmock",
- "//testing/gtest",
- ]
-}
diff --git a/chromium/components/download/downloader/in_progress/in_progress_conversions.h b/chromium/components/download/downloader/in_progress/in_progress_conversions.h
deleted file mode 100644
index e95f6084bba..00000000000
--- a/chromium/components/download/downloader/in_progress/in_progress_conversions.h
+++ /dev/null
@@ -1,70 +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_DOWNLOAD_IN_PROGRESS_IN_PROGRESS_CONVERSIONS_H_
-#define COMPONENTS_DOWNLOAD_IN_PROGRESS_IN_PROGRESS_CONVERSIONS_H_
-
-#include "base/macros.h"
-#include "components/download/downloader/in_progress/download_db_entry.h"
-#include "components/download/downloader/in_progress/download_entry.h"
-#include "components/download/downloader/in_progress/download_info.h"
-#include "components/download/downloader/in_progress/in_progress_info.h"
-#include "components/download/downloader/in_progress/proto/download_entry.pb.h"
-#include "components/download/downloader/in_progress/proto/download_source.pb.h"
-#include "components/download/downloader/in_progress/ukm_info.h"
-
-namespace download {
-
-class InProgressConversions {
- public:
- static DownloadEntry DownloadEntryFromProto(
- const metadata_pb::DownloadEntry& proto);
-
- static metadata_pb::DownloadEntry DownloadEntryToProto(
- const DownloadEntry& entry);
-
- static DownloadSource DownloadSourceFromProto(
- metadata_pb::DownloadSource download_source);
-
- static metadata_pb::DownloadSource DownloadSourceToProto(
- DownloadSource download_source);
-
- static std::vector<DownloadEntry> DownloadEntriesFromProto(
- const metadata_pb::DownloadEntries& proto);
-
- static metadata_pb::DownloadEntries DownloadEntriesToProto(
- const std::vector<DownloadEntry>& entries);
-
- static metadata_pb::HttpRequestHeader HttpRequestHeaderToProto(
- const std::pair<std::string, std::string>& header);
-
- static std::pair<std::string, std::string> HttpRequestHeaderFromProto(
- const metadata_pb::HttpRequestHeader& proto);
-
- static metadata_pb::InProgressInfo InProgressInfoToProto(
- const InProgressInfo& in_progress_info);
-
- static InProgressInfo InProgressInfoFromProto(
- const metadata_pb::InProgressInfo& proto);
-
- static metadata_pb::UkmInfo UkmInfoToProto(const UkmInfo& ukm_info);
-
- static UkmInfo UkmInfoFromProto(const metadata_pb::UkmInfo& proto);
-
- static metadata_pb::DownloadInfo DownloadInfoToProto(
- const DownloadInfo& download_info);
-
- static DownloadInfo DownloadInfoFromProto(
- const metadata_pb::DownloadInfo& proto);
-
- static metadata_pb::DownloadDBEntry DownloadDBEntryToProto(
- const DownloadDBEntry& entry);
-
- static DownloadDBEntry DownloadDBEntryFromProto(
- const metadata_pb::DownloadDBEntry& proto);
-};
-
-} // namespace download
-
-#endif // COMPONENTS_DOWNLOAD_IN_PROGRESS_IN_PROGRESS_CONVERSIONS_H_
diff --git a/chromium/components/download/internal/background_service/controller.h b/chromium/components/download/internal/background_service/controller.h
index e4725da4ef3..6c358504072 100644
--- a/chromium/components/download/internal/background_service/controller.h
+++ b/chromium/components/download/internal/background_service/controller.h
@@ -102,7 +102,7 @@ class Controller {
// See DownloadService::OnStartScheduledTask.
virtual void OnStartScheduledTask(DownloadTaskType task_type,
- const TaskFinishedCallback& callback) = 0;
+ TaskFinishedCallback callback) = 0;
// See DownloadService::OnStopScheduledTask.
virtual bool OnStopScheduledTask(DownloadTaskType task_type) = 0;
diff --git a/chromium/components/download/internal/background_service/controller_impl.cc b/chromium/components/download/internal/background_service/controller_impl.cc
index 2150eca0b17..2d4d7806205 100644
--- a/chromium/components/download/internal/background_service/controller_impl.cc
+++ b/chromium/components/download/internal/background_service/controller_impl.cc
@@ -5,9 +5,8 @@
#include "components/download/internal/background_service/controller_impl.h"
#include <inttypes.h>
-
-#include <string>
-#include <vector>
+#include <algorithm>
+#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
@@ -304,10 +303,9 @@ DownloadClient ControllerImpl::GetOwnerOfDownload(const std::string& guid) {
return entry ? entry->client : DownloadClient::INVALID;
}
-void ControllerImpl::OnStartScheduledTask(
- DownloadTaskType task_type,
- const TaskFinishedCallback& callback) {
- task_finished_callbacks_[task_type] = callback;
+void ControllerImpl::OnStartScheduledTask(DownloadTaskType task_type,
+ TaskFinishedCallback callback) {
+ task_finished_callbacks_[task_type] = std::move(callback);
switch (controller_state_) {
case State::READY:
diff --git a/chromium/components/download/internal/background_service/controller_impl.h b/chromium/components/download/internal/background_service/controller_impl.h
index 8c3b375426d..379d8c6b430 100644
--- a/chromium/components/download/internal/background_service/controller_impl.h
+++ b/chromium/components/download/internal/background_service/controller_impl.h
@@ -8,6 +8,8 @@
#include <map>
#include <memory>
#include <set>
+#include <string>
+#include <vector>
#include "base/cancelable_callback.h"
#include "base/macros.h"
@@ -78,7 +80,7 @@ class ControllerImpl : public Controller,
const SchedulingParams& params) override;
DownloadClient GetOwnerOfDownload(const std::string& guid) override;
void OnStartScheduledTask(DownloadTaskType task_type,
- const TaskFinishedCallback& callback) override;
+ TaskFinishedCallback callback) override;
bool OnStopScheduledTask(DownloadTaskType task_type) override;
private:
diff --git a/chromium/components/download/internal/background_service/controller_impl_unittest.cc b/chromium/components/download/internal/background_service/controller_impl_unittest.cc
index 1e4f96bab34..228181ce706 100644
--- a/chromium/components/download/internal/background_service/controller_impl_unittest.cc
+++ b/chromium/components/download/internal/background_service/controller_impl_unittest.cc
@@ -4,14 +4,16 @@
#include "components/download/internal/background_service/controller_impl.h"
+#include <stdint.h>
#include <algorithm>
#include <memory>
+#include <utility>
#include "base/bind.h"
#include "base/guid.h"
#include "base/macros.h"
#include "base/strings/string_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -104,7 +106,7 @@ class MockTaskScheduler : public TaskScheduler {
// TaskScheduler implementation.
MOCK_METHOD6(ScheduleTask,
- void(DownloadTaskType, bool, bool, int, long, long));
+ void(DownloadTaskType, bool, bool, int, int64_t, int64_t));
MOCK_METHOD1(CancelTask, void(DownloadTaskType));
};
@@ -496,7 +498,7 @@ TEST_F(DownloadServiceControllerImplTest,
store_->TriggerInit(true, std::make_unique<std::vector<Entry>>(entries));
file_monitor_->TriggerInit(true);
controller_->OnStartScheduledTask(DownloadTaskType::CLEANUP_TASK,
- base::Bind(&NotifyTaskFinished));
+ base::BindOnce(&NotifyTaskFinished));
task_runner_->RunUntilIdle();
controller_->OnStopScheduledTask(DownloadTaskType::CLEANUP_TASK);
@@ -1025,7 +1027,7 @@ TEST_F(DownloadServiceControllerImplTest, OnDownloadSucceeded) {
base::Time now = base::Time::Now();
done_dentry.completion_time = now;
- long start_time = 0;
+ int64_t start_time = 0;
EXPECT_CALL(*task_scheduler_,
ScheduleTask(DownloadTaskType::CLEANUP_TASK, _, _, _, _, _))
.WillOnce(SaveArg<4>(&start_time));
@@ -1889,7 +1891,7 @@ TEST_F(DownloadServiceControllerImplTest, DownloadTaskQueuesAfterFinish) {
// Simulate a task start, which should limit our calls to Reschedule() because
// we are in a task.
controller_->OnStartScheduledTask(DownloadTaskType::DOWNLOAD_TASK,
- base::Bind(&NotifyTaskFinished));
+ base::BindOnce(&NotifyTaskFinished));
// Set up new expectations to start a new download.
ON_CALL(*scheduler_, Next(_, _))
@@ -1954,7 +1956,7 @@ TEST_F(DownloadServiceControllerImplTest, CleanupTaskQueuesAfterFinish) {
ScheduleTask(DownloadTaskType::CLEANUP_TASK, _, _, _, _, _))
.Times(0);
controller_->OnStartScheduledTask(DownloadTaskType::CLEANUP_TASK,
- base::Bind(&NotifyTaskFinished));
+ base::BindOnce(&NotifyTaskFinished));
// Trigger download succeed events, which should not schedule a cleanup until
// the existing cleanup has finished.
diff --git a/chromium/components/download/internal/background_service/download_service_impl.cc b/chromium/components/download/internal/background_service/download_service_impl.cc
index 3ed181bb6fb..9ea5ff70950 100644
--- a/chromium/components/download/internal/background_service/download_service_impl.cc
+++ b/chromium/components/download/internal/background_service/download_service_impl.cc
@@ -4,6 +4,8 @@
#include "components/download/internal/background_service/download_service_impl.h"
+#include <utility>
+
#include "base/bind.h"
#include "base/strings/string_util.h"
#include "components/download/internal/background_service/controller.h"
@@ -31,17 +33,16 @@ const ServiceConfig& DownloadServiceImpl::GetConfig() {
return service_config_;
}
-void DownloadServiceImpl::OnStartScheduledTask(
- DownloadTaskType task_type,
- const TaskFinishedCallback& callback) {
+void DownloadServiceImpl::OnStartScheduledTask(DownloadTaskType task_type,
+ TaskFinishedCallback callback) {
if (startup_completed_) {
- controller_->OnStartScheduledTask(task_type, callback);
+ controller_->OnStartScheduledTask(task_type, std::move(callback));
return;
}
- pending_tasks_[task_type] =
- base::Bind(&Controller::OnStartScheduledTask,
- base::Unretained(controller_.get()), task_type, callback);
+ pending_tasks_[task_type] = base::BindOnce(
+ &Controller::OnStartScheduledTask, base::Unretained(controller_.get()),
+ task_type, base::Passed(&callback));
}
bool DownloadServiceImpl::OnStopScheduledTask(DownloadTaskType task_type) {
@@ -53,7 +54,7 @@ bool DownloadServiceImpl::OnStopScheduledTask(DownloadTaskType task_type) {
if (iter != pending_tasks_.end()) {
// We still need to run the callback in order to properly cleanup and notify
// the system by running the respective task finished callbacks.
- iter->second.Run();
+ std::move(iter->second).Run();
pending_tasks_.erase(iter);
}
@@ -81,9 +82,9 @@ void DownloadServiceImpl::StartDownload(const DownloadParams& download_params) {
if (startup_completed_) {
controller_->StartDownload(download_params);
} else {
- pending_actions_.push_back(base::Bind(&Controller::StartDownload,
- base::Unretained(controller_.get()),
- download_params));
+ pending_actions_.push_back(
+ base::BindOnce(&Controller::StartDownload,
+ base::Unretained(controller_.get()), download_params));
}
}
@@ -93,7 +94,7 @@ void DownloadServiceImpl::PauseDownload(const std::string& guid) {
if (startup_completed_) {
controller_->PauseDownload(guid);
} else {
- pending_actions_.push_back(base::Bind(
+ pending_actions_.push_back(base::BindOnce(
&Controller::PauseDownload, base::Unretained(controller_.get()), guid));
}
}
@@ -104,9 +105,9 @@ void DownloadServiceImpl::ResumeDownload(const std::string& guid) {
if (startup_completed_) {
controller_->ResumeDownload(guid);
} else {
- pending_actions_.push_back(base::Bind(&Controller::ResumeDownload,
- base::Unretained(controller_.get()),
- guid));
+ pending_actions_.push_back(
+ base::BindOnce(&Controller::ResumeDownload,
+ base::Unretained(controller_.get()), guid));
}
}
@@ -116,9 +117,9 @@ void DownloadServiceImpl::CancelDownload(const std::string& guid) {
if (startup_completed_) {
controller_->CancelDownload(guid);
} else {
- pending_actions_.push_back(base::Bind(&Controller::CancelDownload,
- base::Unretained(controller_.get()),
- guid));
+ pending_actions_.push_back(
+ base::BindOnce(&Controller::CancelDownload,
+ base::Unretained(controller_.get()), guid));
}
}
@@ -130,9 +131,9 @@ void DownloadServiceImpl::ChangeDownloadCriteria(
if (startup_completed_) {
controller_->ChangeDownloadCriteria(guid, params);
} else {
- pending_actions_.push_back(base::Bind(&Controller::ChangeDownloadCriteria,
- base::Unretained(controller_.get()),
- guid, params));
+ pending_actions_.push_back(
+ base::BindOnce(&Controller::ChangeDownloadCriteria,
+ base::Unretained(controller_.get()), guid, params));
}
}
@@ -142,14 +143,14 @@ Logger* DownloadServiceImpl::GetLogger() {
void DownloadServiceImpl::OnControllerInitialized() {
while (!pending_actions_.empty()) {
- auto callback = pending_actions_.front();
- callback.Run();
+ auto callback = std::move(pending_actions_.front());
pending_actions_.pop_front();
+ std::move(callback).Run();
}
while (!pending_tasks_.empty()) {
auto iter = pending_tasks_.begin();
- iter->second.Run();
+ std::move(iter->second).Run();
pending_tasks_.erase(iter);
}
diff --git a/chromium/components/download/internal/background_service/download_service_impl.h b/chromium/components/download/internal/background_service/download_service_impl.h
index 888bcd93ff2..b99b3f197ad 100644
--- a/chromium/components/download/internal/background_service/download_service_impl.h
+++ b/chromium/components/download/internal/background_service/download_service_impl.h
@@ -34,7 +34,7 @@ class DownloadServiceImpl : public DownloadService {
// DownloadService implementation.
const ServiceConfig& GetConfig() override;
void OnStartScheduledTask(DownloadTaskType task_type,
- const TaskFinishedCallback& callback) override;
+ TaskFinishedCallback callback) override;
bool OnStopScheduledTask(DownloadTaskType task_type) override;
ServiceStatus GetStatus() override;
void StartDownload(const DownloadParams& download_params) override;
@@ -56,8 +56,8 @@ class DownloadServiceImpl : public DownloadService {
std::unique_ptr<Controller> controller_;
ServiceConfigImpl service_config_;
- base::circular_deque<base::Closure> pending_actions_;
- std::map<DownloadTaskType, base::Closure> pending_tasks_;
+ base::circular_deque<base::OnceClosure> pending_actions_;
+ std::map<DownloadTaskType, base::OnceClosure> pending_tasks_;
bool startup_completed_;
DISALLOW_COPY_AND_ASSIGN(DownloadServiceImpl);
diff --git a/chromium/components/download/internal/background_service/download_service_impl_unittest.cc b/chromium/components/download/internal/background_service/download_service_impl_unittest.cc
index 796d3ddc492..9a41b3955b4 100644
--- a/chromium/components/download/internal/background_service/download_service_impl_unittest.cc
+++ b/chromium/components/download/internal/background_service/download_service_impl_unittest.cc
@@ -8,7 +8,7 @@
#include "base/macros.h"
#include "base/strings/string_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/download/internal/background_service/startup_status.h"
diff --git a/chromium/components/download/internal/background_service/empty_task_scheduler.cc b/chromium/components/download/internal/background_service/empty_task_scheduler.cc
index 79faee51fb5..445d62299bc 100644
--- a/chromium/components/download/internal/background_service/empty_task_scheduler.cc
+++ b/chromium/components/download/internal/background_service/empty_task_scheduler.cc
@@ -14,8 +14,8 @@ void EmptyTaskScheduler::ScheduleTask(DownloadTaskType task_type,
bool require_unmetered_network,
bool require_charging,
int optimal_battery_percentage,
- long window_start_time_seconds,
- long window_end_time_seconds) {}
+ int64_t window_start_time_seconds,
+ int64_t window_end_time_seconds) {}
void EmptyTaskScheduler::CancelTask(DownloadTaskType task_type) {}
diff --git a/chromium/components/download/internal/background_service/empty_task_scheduler.h b/chromium/components/download/internal/background_service/empty_task_scheduler.h
index df32d19b458..814ade7159e 100644
--- a/chromium/components/download/internal/background_service/empty_task_scheduler.h
+++ b/chromium/components/download/internal/background_service/empty_task_scheduler.h
@@ -5,9 +5,10 @@
#ifndef COMPONENTS_DOWNLOAD_INTERNAL_BACKGROUND_SERVICE_EMPTY_TASK_SCHEDULER_H_
#define COMPONENTS_DOWNLOAD_INTERNAL_BACKGROUND_SERVICE_EMPTY_TASK_SCHEDULER_H_
-#include "components/download/public/background_service/task_scheduler.h"
+#include <stdint.h>
#include "base/macros.h"
+#include "components/download/public/background_service/task_scheduler.h"
namespace download {
@@ -23,8 +24,8 @@ class EmptyTaskScheduler : public TaskScheduler {
bool require_unmetered_network,
bool require_charging,
int optimal_battery_percentage,
- long window_start_time_seconds,
- long window_end_time_seconds) override;
+ int64_t window_start_time_seconds,
+ int64_t window_end_time_seconds) override;
void CancelTask(DownloadTaskType task_type) override;
DISALLOW_COPY_AND_ASSIGN(EmptyTaskScheduler);
diff --git a/chromium/components/download/internal/background_service/in_memory_download.cc b/chromium/components/download/internal/background_service/in_memory_download.cc
index 630fcca9ff0..9aea2b34144 100644
--- a/chromium/components/download/internal/background_service/in_memory_download.cc
+++ b/chromium/components/download/internal/background_service/in_memory_download.cc
@@ -179,16 +179,38 @@ void InMemoryDownloadImpl::SendRequest() {
request->headers = request_params_.request_headers;
request->load_flags = net::LOAD_DISABLE_CACHE;
+ url_chain_.push_back(request_params_.url);
+
loader_ =
network::SimpleURLLoader::Create(std::move(request), traffic_annotation_);
+ loader_->SetOnRedirectCallback(base::BindRepeating(
+ &InMemoryDownloadImpl::OnRedirect, weak_ptr_factory_.GetWeakPtr()));
+ loader_->SetOnResponseStartedCallback(
+ base::BindRepeating(&InMemoryDownloadImpl::OnResponseStarted,
+ weak_ptr_factory_.GetWeakPtr()));
// TODO(xingliu): Use SimpleURLLoader's retry when it won't hit CHECK in
// SharedURLLoaderFactory.
loader_->DownloadAsStream(url_loader_factory_, this);
}
+void InMemoryDownloadImpl::OnRedirect(
+ const net::RedirectInfo& redirect_info,
+ const network::ResourceResponseHead& response_head,
+ std::vector<std::string>* to_be_removed_headers) {
+ url_chain_.push_back(redirect_info.new_url);
+}
+
+void InMemoryDownloadImpl::OnResponseStarted(
+ const GURL& final_url,
+ const network::ResourceResponseHead& response_head) {
+ response_headers_ = response_head.headers;
+}
+
void InMemoryDownloadImpl::Reset() {
data_.clear();
+ url_chain_.clear();
+ response_headers_.reset();
bytes_downloaded_ = 0u;
completion_notified_ = false;
resume_callback_.Reset();
diff --git a/chromium/components/download/internal/background_service/in_memory_download.h b/chromium/components/download/internal/background_service/in_memory_download.h
index 69d0e5ad211..df8298aa9a6 100644
--- a/chromium/components/download/internal/background_service/in_memory_download.h
+++ b/chromium/components/download/internal/background_service/in_memory_download.h
@@ -7,6 +7,7 @@
#include <memory>
#include <string>
+#include <vector>
#include "base/callback.h"
#include "base/macros.h"
@@ -18,6 +19,8 @@
#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
+class GURL;
+
namespace net {
struct NetworkTrafficAnnotationTag;
} // namespace net
@@ -102,6 +105,7 @@ class InMemoryDownload {
State state() const { return state_; }
bool paused() const { return paused_; }
const base::Time& completion_time() const { return completion_time_; }
+ const std::vector<GURL>& url_chain() const { return url_chain_; }
scoped_refptr<const net::HttpResponseHeaders> response_headers() const {
return response_headers_;
}
@@ -120,6 +124,9 @@ class InMemoryDownload {
// Completion time of download when data is saved as blob.
base::Time completion_time_;
+ // The URL request chain of this download.
+ std::vector<GURL> url_chain_;
+
// HTTP response headers.
scoped_refptr<const net::HttpResponseHeaders> response_headers_;
@@ -179,6 +186,15 @@ class InMemoryDownloadImpl : public network::SimpleURLLoaderStreamConsumer,
// Sends a new network request.
void SendRequest();
+ // Called when the server redirects to another URL.
+ void OnRedirect(const net::RedirectInfo& redirect_info,
+ const network::ResourceResponseHead& response_head,
+ std::vector<std::string>* to_be_removed_headers);
+
+ // Called when the response of the final URL is received.
+ void OnResponseStarted(const GURL& final_url,
+ const network::ResourceResponseHead& response_head);
+
// Resets local states.
void Reset();
diff --git a/chromium/components/download/internal/background_service/in_memory_download_driver.cc b/chromium/components/download/internal/background_service/in_memory_download_driver.cc
index 50eeda0460c..d59aa4f7dcd 100644
--- a/chromium/components/download/internal/background_service/in_memory_download_driver.cc
+++ b/chromium/components/download/internal/background_service/in_memory_download_driver.cc
@@ -35,6 +35,7 @@ DriverEntry CreateDriverEntry(const InMemoryDownload& download) {
entry.done = entry.state == DriverEntry::State::COMPLETE ||
entry.state == DriverEntry::State::CANCELLED;
entry.bytes_downloaded = download.bytes_downloaded();
+ entry.url_chain = download.url_chain();
entry.response_headers = download.response_headers();
if (entry.response_headers) {
entry.expected_total_size = entry.response_headers->GetContentLength();
diff --git a/chromium/components/download/internal/background_service/in_memory_download_unittest.cc b/chromium/components/download/internal/background_service/in_memory_download_unittest.cc
index e04ec21317e..bd2b4cc22ee 100644
--- a/chromium/components/download/internal/background_service/in_memory_download_unittest.cc
+++ b/chromium/components/download/internal/background_service/in_memory_download_unittest.cc
@@ -18,6 +18,8 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using testing::NiceMock;
+
namespace download {
namespace {
@@ -151,7 +153,7 @@ class InMemoryDownloadTest : public testing::Test {
base::test::ScopedTaskEnvironment scoped_task_environment_;
std::unique_ptr<InMemoryDownloadImpl> download_;
- MockDelegate mock_delegate_;
+ NiceMock<MockDelegate> mock_delegate_;
// Used by SimpleURLLoader network backend.
network::TestURLLoaderFactory url_loader_factory_;
@@ -176,6 +178,46 @@ TEST_F(InMemoryDownloadTest, DownloadTest) {
VerifyBlobData(kTestDownloadData, blob.get());
}
+TEST_F(InMemoryDownloadTest, RedirectResponseHeaders) {
+ RequestParams request_params;
+ request_params.url = GURL("https://example.com/firsturl");
+ CreateDownload(request_params);
+
+ // Add a redirect.
+ net::RedirectInfo redirect_info;
+ redirect_info.new_url = GURL("https://example.com/redirect12345");
+ network::TestURLLoaderFactory::Redirects redirects = {
+ {redirect_info, network::ResourceResponseHead()}};
+
+ // Add some random header.
+ network::ResourceResponseHead response_head;
+ response_head.headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ response_head.headers->AddHeader("X-Random-Test-Header: 123");
+
+ // The size must match for download as stream from SimpleUrlLoader.
+ network::URLLoaderCompletionStatus status;
+ status.decoded_body_length = base::size(kTestDownloadData) - 1;
+
+ url_loader_factory()->AddResponse(request_params.url, response_head,
+ kTestDownloadData, status, redirects);
+
+ download()->Start();
+ delegate()->WaitForCompletion();
+ EXPECT_EQ(InMemoryDownload::State::COMPLETE, download()->state());
+
+ // Verify the response headers and URL chain. The URL chain should contain
+ // the original URL and redirect URL, and should not contain the final URL.
+ std::vector<GURL> expected_url_chain = {request_params.url,
+ redirect_info.new_url};
+ EXPECT_EQ(download()->url_chain(), expected_url_chain);
+ EXPECT_EQ(download()->response_headers()->raw_headers(),
+ response_head.headers->raw_headers());
+
+ // Verfiy the data persisted to disk after redirect chain.
+ auto blob = download()->ResultAsBlob();
+ VerifyBlobData(kTestDownloadData, blob.get());
+}
+
} // namespace
} // namespace download
diff --git a/chromium/components/download/internal/background_service/model_impl_unittest.cc b/chromium/components/download/internal/background_service/model_impl_unittest.cc
index 63b91e24e39..6a9a6405223 100644
--- a/chromium/components/download/internal/background_service/model_impl_unittest.cc
+++ b/chromium/components/download/internal/background_service/model_impl_unittest.cc
@@ -10,7 +10,7 @@
#include "base/bind.h"
#include "base/guid.h"
#include "base/macros.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/download/internal/background_service/entry.h"
#include "components/download/internal/background_service/stats.h"
#include "components/download/internal/background_service/test/entry_utils.h"
diff --git a/chromium/components/download/internal/background_service/scheduler/scheduler_impl_unittest.cc b/chromium/components/download/internal/background_service/scheduler/scheduler_impl_unittest.cc
index d39cfc36551..10b5fafe4d9 100644
--- a/chromium/components/download/internal/background_service/scheduler/scheduler_impl_unittest.cc
+++ b/chromium/components/download/internal/background_service/scheduler/scheduler_impl_unittest.cc
@@ -4,6 +4,7 @@
#include "components/download/internal/background_service/scheduler/scheduler_impl.h"
+#include <stdint.h>
#include <memory>
#include "base/strings/string_number_conversions.h"
@@ -26,7 +27,7 @@ class MockTaskScheduler : public TaskScheduler {
~MockTaskScheduler() override = default;
MOCK_METHOD6(ScheduleTask,
- void(DownloadTaskType, bool, bool, int, long, long));
+ void(DownloadTaskType, bool, bool, int, int64_t, int64_t));
MOCK_METHOD1(CancelTask, void(DownloadTaskType));
};
diff --git a/chromium/components/download/internal/common/BUILD.gn b/chromium/components/download/internal/common/BUILD.gn
index 42ed3007fc8..51213976577 100644
--- a/chromium/components/download/internal/common/BUILD.gn
+++ b/chromium/components/download/internal/common/BUILD.gn
@@ -16,6 +16,8 @@ source_set("internal") {
"base_file.cc",
"base_file_win.cc",
"download_create_info.cc",
+ "download_db_cache.cc",
+ "download_db_cache.h",
"download_file_factory.cc",
"download_file_impl.cc",
"download_interrupt_reasons_impl.cc",
@@ -46,6 +48,7 @@ source_set("internal") {
"save_package_download_job.h",
"stream_handle_input_stream.cc",
"url_download_handler_factory.cc",
+ "url_download_request_handle.cc",
]
public_deps = [
@@ -54,9 +57,10 @@ source_set("internal") {
deps = [
"//base",
- "//components/download/downloader/in_progress",
+ "//components/download/database",
"//components/download/public/common:interfaces",
"//components/download/quarantine",
+ "//components/leveldb_proto",
"//mojo/public/c/system",
"//net",
"//services/metrics/public/cpp:ukm_builders",
@@ -90,6 +94,7 @@ source_set("unit_tests") {
sources = [
"base_file_unittest.cc",
"base_file_win_unittest.cc",
+ "download_db_cache_unittest.cc",
"download_file_unittest.cc",
"download_item_impl_unittest.cc",
"download_stats_unittest.cc",
@@ -102,7 +107,10 @@ source_set("unit_tests") {
deps = [
":for_tests",
"//base/test:test_support",
+ "//components/download/database",
"//components/download/public/common:test_support",
+ "//components/leveldb_proto",
+ "//components/leveldb_proto:test_support",
"//components/ukm:test_support",
"//crypto",
"//net",
diff --git a/chromium/components/download/internal/common/DEPS b/chromium/components/download/internal/common/DEPS
index a5df1addd5a..13ed39c890f 100644
--- a/chromium/components/download/internal/common/DEPS
+++ b/chromium/components/download/internal/common/DEPS
@@ -3,6 +3,7 @@ include_rules = [
"+components/download/downloader/in_progress",
"+components/download/public/common",
"+components/download/quarantine",
+ "+components/leveldb_proto",
"+components/ukm/test_ukm_recorder.h",
"+crypto",
"+mojo/public/c/system",
diff --git a/chromium/components/download/internal/common/OWNERS b/chromium/components/download/internal/common/OWNERS
new file mode 100644
index 00000000000..42c52dcf1fb
--- /dev/null
+++ b/chromium/components/download/internal/common/OWNERS
@@ -0,0 +1,2 @@
+# For adding new file types
+per-file download_stats.cc=file://chrome/browser/resources/safe_browsing/OWNERS
diff --git a/chromium/components/download/internal/common/download_create_info.cc b/chromium/components/download/internal/common/download_create_info.cc
index eb4a1ca9d16..ebc8df19a11 100644
--- a/chromium/components/download/internal/common/download_create_info.cc
+++ b/chromium/components/download/internal/common/download_create_info.cc
@@ -16,7 +16,9 @@ namespace download {
DownloadCreateInfo::DownloadCreateInfo(
const base::Time& start_time,
std::unique_ptr<DownloadSaveInfo> save_info)
- : download_id(DownloadItem::kInvalidId),
+ : is_new_download(true),
+ referrer_policy(net::URLRequest::
+ CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE),
start_time(start_time),
total_bytes(0),
offset(0),
diff --git a/chromium/components/download/internal/common/download_db_cache.cc b/chromium/components/download/internal/common/download_db_cache.cc
new file mode 100644
index 00000000000..b514e4e6f3e
--- /dev/null
+++ b/chromium/components/download/internal/common/download_db_cache.cc
@@ -0,0 +1,246 @@
+// 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/download/internal/common/download_db_cache.h"
+
+#include "components/download/database/download_db.h"
+#include "components/download/database/download_db_entry.h"
+#include "components/download/public/common/download_utils.h"
+
+namespace download {
+
+namespace {
+
+// Interval between download DB updates.
+// TODO(qinmin): make this finch configurable.
+const int kUpdateDBIntervalMs = 10000;
+
+enum class ShouldUpdateDownloadDBResult {
+ NO_UPDATE,
+ UPDATE,
+ UPDATE_IMMEDIATELY,
+};
+
+ShouldUpdateDownloadDBResult ShouldUpdateDownloadDB(
+ base::Optional<DownloadDBEntry> previous,
+ const DownloadDBEntry& current) {
+ if (!previous)
+ return ShouldUpdateDownloadDBResult::UPDATE_IMMEDIATELY;
+
+ base::Optional<InProgressInfo> previous_info;
+ if (previous->download_info)
+ previous_info = previous->download_info->in_progress_info;
+ base::FilePath previous_path =
+ previous_info ? previous_info->current_path : base::FilePath();
+
+ base::Optional<InProgressInfo> current_info;
+ if (current.download_info)
+ current_info = current.download_info->in_progress_info;
+
+ base::FilePath current_path =
+ current_info ? current_info->current_path : base::FilePath();
+
+ // When download path is determined, Chrome should commit the history
+ // immediately. Otherwise the file will be left permanently on the external
+ // storage if Chrome crashes right away.
+ if (current_path != previous_path)
+ return ShouldUpdateDownloadDBResult::UPDATE_IMMEDIATELY;
+
+ if (previous.value() == current)
+ return ShouldUpdateDownloadDBResult::NO_UPDATE;
+
+ return ShouldUpdateDownloadDBResult::UPDATE;
+}
+
+bool GetFetchErrorBody(base::Optional<DownloadDBEntry> entry) {
+ if (!entry)
+ return false;
+
+ if (!entry->download_info)
+ return false;
+
+ base::Optional<InProgressInfo>& in_progress_info =
+ entry->download_info->in_progress_info;
+ if (!in_progress_info)
+ return false;
+
+ return in_progress_info->fetch_error_body;
+}
+
+DownloadUrlParameters::RequestHeadersType GetRequestHeadersType(
+ base::Optional<DownloadDBEntry> entry) {
+ DownloadUrlParameters::RequestHeadersType ret;
+ if (!entry)
+ return ret;
+
+ if (!entry->download_info)
+ return ret;
+
+ base::Optional<InProgressInfo>& in_progress_info =
+ entry->download_info->in_progress_info;
+ if (!in_progress_info)
+ return ret;
+
+ return in_progress_info->request_headers;
+}
+
+DownloadSource GetDownloadSource(base::Optional<DownloadDBEntry> entry) {
+ if (!entry)
+ return DownloadSource::UNKNOWN;
+
+ if (!entry->download_info)
+ return DownloadSource::UNKNOWN;
+
+ base::Optional<UkmInfo>& ukm_info = entry->download_info->ukm_info;
+ if (!ukm_info)
+ return DownloadSource::UNKNOWN;
+
+ return ukm_info->download_source;
+}
+
+void CleanUpInProgressEntry(DownloadDBEntry& entry) {
+ if (!entry.download_info)
+ return;
+
+ base::Optional<InProgressInfo>& in_progress_info =
+ entry.download_info->in_progress_info;
+ if (!in_progress_info)
+ return;
+
+ if (in_progress_info->state == DownloadItem::DownloadState::IN_PROGRESS) {
+ in_progress_info->state = DownloadItem::DownloadState::INTERRUPTED;
+ in_progress_info->interrupt_reason =
+ download::DOWNLOAD_INTERRUPT_REASON_CRASH;
+ }
+}
+
+} // namespace
+
+DownloadDBCache::DownloadDBCache(std::unique_ptr<DownloadDB> download_db)
+ : initialized_(false),
+ download_db_(std::move(download_db)),
+ weak_factory_(this) {}
+
+DownloadDBCache::~DownloadDBCache() = default;
+
+void DownloadDBCache::Initialize(InitializeCallback callback) {
+ // TODO(qinmin): migrate all the data from InProgressCache into
+ // |download_db_|.
+ if (!initialized_) {
+ download_db_->Initialize(
+ base::BindOnce(&DownloadDBCache::OnDownloadDBInitialized,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+ return;
+ }
+
+ auto entries = std::make_unique<std::vector<DownloadDBEntry>>();
+ for (auto it = entries_.begin(); it != entries_.end(); ++it) {
+ entries->emplace_back(it->second);
+ }
+ std::move(callback).Run(std::move(entries));
+}
+
+base::Optional<DownloadDBEntry> DownloadDBCache::RetrieveEntry(
+ const std::string& guid) {
+ auto iter = entries_.find(guid);
+ if (iter != entries_.end())
+ return iter->second;
+ return base::nullopt;
+}
+
+void DownloadDBCache::AddOrReplaceEntry(const DownloadDBEntry& entry) {
+ if (!entry.download_info)
+ return;
+ const std::string& guid = entry.download_info->guid;
+ ShouldUpdateDownloadDBResult result =
+ ShouldUpdateDownloadDB(RetrieveEntry(guid), entry);
+ if (result == ShouldUpdateDownloadDBResult::NO_UPDATE)
+ return;
+ if (!update_timer_.IsRunning() &&
+ result == ShouldUpdateDownloadDBResult::UPDATE) {
+ update_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kUpdateDBIntervalMs),
+ this, &DownloadDBCache::UpdateDownloadDB);
+ }
+
+ entries_[guid] = entry;
+ updated_guids_.emplace(guid);
+ if (result == ShouldUpdateDownloadDBResult::UPDATE_IMMEDIATELY) {
+ UpdateDownloadDB();
+ update_timer_.Stop();
+ }
+}
+
+void DownloadDBCache::RemoveEntry(const std::string& guid) {
+ entries_.erase(guid);
+ updated_guids_.erase(guid);
+ if (download_db_)
+ download_db_->Remove(guid);
+}
+
+void DownloadDBCache::UpdateDownloadDB() {
+ DCHECK(!updated_guids_.empty());
+
+ std::vector<DownloadDBEntry> entries;
+ for (auto guid : updated_guids_) {
+ base::Optional<DownloadDBEntry> entry = RetrieveEntry(guid);
+ DCHECK(entry);
+ entries.emplace_back(entry.value());
+ }
+ download_db_->AddOrReplaceEntries(entries);
+}
+
+void DownloadDBCache::OnDownloadUpdated(DownloadItem* download) {
+ // TODO(crbug.com/778425): Properly handle fail/resume/retry for downloads
+ // that are in the INTERRUPTED state for a long time.
+ if (!download_db_)
+ return;
+
+ base::Optional<DownloadDBEntry> current = RetrieveEntry(download->GetGuid());
+ bool fetch_error_body = GetFetchErrorBody(current);
+ DownloadUrlParameters::RequestHeadersType request_header_type =
+ GetRequestHeadersType(current);
+ DownloadSource download_source = GetDownloadSource(current);
+ DownloadDBEntry entry = CreateDownloadDBEntryFromItem(
+ *download, download_source, fetch_error_body, request_header_type);
+ AddOrReplaceEntry(entry);
+}
+
+void DownloadDBCache::OnDownloadRemoved(DownloadItem* download) {
+ RemoveEntry(download->GetGuid());
+}
+
+void DownloadDBCache::OnDownloadDBInitialized(InitializeCallback callback,
+ bool success) {
+ if (success) {
+ download_db_->LoadEntries(
+ base::BindOnce(&DownloadDBCache::OnDownloadDBEntriesLoaded,
+ weak_factory_.GetWeakPtr(), std::move(callback)));
+ }
+ // TODO(qinmin): Recreate the database if |success| is false.
+ // http://crbug.com/847661.
+}
+
+void DownloadDBCache::OnDownloadDBEntriesLoaded(
+ InitializeCallback callback,
+ bool success,
+ std::unique_ptr<std::vector<DownloadDBEntry>> entries) {
+ if (success) {
+ initialized_ = true;
+ for (auto& entry : *entries) {
+ CleanUpInProgressEntry(entry);
+ entries_[entry.download_info->guid] = entry;
+ }
+ std::move(callback).Run(std::move(entries));
+ }
+ // TODO(qinmin): Recreate the database if |success| is false.
+ // http://crbug.com/847661.
+}
+
+void DownloadDBCache::SetTimerTaskRunnerForTesting(
+ scoped_refptr<base::SequencedTaskRunner> task_runner) {
+ update_timer_.SetTaskRunner(task_runner);
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/common/download_db_cache.h b/chromium/components/download/internal/common/download_db_cache.h
new file mode 100644
index 00000000000..edf0ed7086a
--- /dev/null
+++ b/chromium/components/download/internal/common/download_db_cache.h
@@ -0,0 +1,88 @@
+// 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_DOWNLOAD_INTERNAL_COMMON_DOWNLOAD_DB_CACHE_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_COMMON_DOWNLOAD_DB_CACHE_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/timer/timer.h"
+#include "components/download/public/common/download_export.h"
+#include "components/download/public/common/download_item.h"
+
+namespace download {
+
+class DownloadDB;
+struct DownloadDBEntry;
+
+// Responsible for caching the metadata of all in progress downloads.
+class COMPONENTS_DOWNLOAD_EXPORT DownloadDBCache
+ : public DownloadItem::Observer {
+ public:
+ explicit DownloadDBCache(std::unique_ptr<DownloadDB> download_db);
+ ~DownloadDBCache() override;
+
+ using InitializeCallback =
+ base::OnceCallback<void(std::unique_ptr<std::vector<DownloadDBEntry>>)>;
+ void Initialize(InitializeCallback callback);
+
+ base::Optional<DownloadDBEntry> RetrieveEntry(const std::string& guid);
+ void AddOrReplaceEntry(const DownloadDBEntry& entry);
+
+ // Remove an entry from the DownloadDB.
+ void RemoveEntry(const std::string& guid);
+
+ private:
+ friend class DownloadDBCacheTest;
+ friend class InProgressDownloadManager;
+
+ // Update all the entries in |download_db_|.
+ void UpdateDownloadDB();
+
+ // DownloadItem::Observer
+ void OnDownloadUpdated(DownloadItem* download) override;
+ void OnDownloadRemoved(DownloadItem* download) override;
+
+ // Called when the |download_db_| is initialized.
+ void OnDownloadDBInitialized(InitializeCallback callback, bool success);
+
+ // Called when all the download db entries are loaded.
+ void OnDownloadDBEntriesLoaded(
+ InitializeCallback callback,
+ bool success,
+ std::unique_ptr<std::vector<DownloadDBEntry>> entries);
+
+ void SetTimerTaskRunnerForTesting(
+ scoped_refptr<base::SequencedTaskRunner> task_runner);
+
+ // Whether this object has already been initialized.
+ bool initialized_;
+
+ // Database for storing in-progress metadata.
+ std::unique_ptr<DownloadDB> download_db_;
+
+ using DownloadDBEntryMap = std::map<std::string, DownloadDBEntry>;
+ // All in progress downloads stored in |download_db_|.
+ DownloadDBEntryMap entries_;
+
+ // GUIDs of updated entries.
+ std::set<std::string> updated_guids_;
+
+ // Used to trigger db updates.
+ base::OneShotTimer update_timer_;
+
+ base::WeakPtrFactory<DownloadDBCache> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadDBCache);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_INTERNAL_COMMON_DOWNLOAD_DB_CACHE_H_
diff --git a/chromium/components/download/internal/common/download_db_cache_unittest.cc b/chromium/components/download/internal/common/download_db_cache_unittest.cc
new file mode 100644
index 00000000000..9c1ed9b4f7e
--- /dev/null
+++ b/chromium/components/download/internal/common/download_db_cache_unittest.cc
@@ -0,0 +1,236 @@
+// 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/download/internal/common/download_db_cache.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/guid.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "components/download/database/download_db_conversions.h"
+#include "components/download/database/download_db_entry.h"
+#include "components/download/database/download_db_impl.h"
+#include "components/leveldb_proto/testing/fake_db.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+namespace download {
+
+namespace {
+
+DownloadDBEntry CreateDownloadDBEntry() {
+ DownloadDBEntry entry;
+ DownloadInfo download_info;
+ download_info.guid = base::GenerateGUID();
+ entry.download_info = download_info;
+ return entry;
+}
+
+std::string GetKey(const std::string& guid) {
+ return DownloadNamespaceToString(
+ DownloadNamespace::NAMESPACE_BROWSER_DOWNLOAD) +
+ "," + guid;
+}
+
+} // namespace
+
+class DownloadDBCacheTest : public testing::Test {
+ public:
+ DownloadDBCacheTest()
+ : db_(nullptr), task_runner_(new base::TestMockTimeTaskRunner) {}
+
+ ~DownloadDBCacheTest() override = default;
+
+ void CreateDBCache() {
+ auto db = std::make_unique<
+ leveldb_proto::test::FakeDB<download_pb::DownloadDBEntry>>(
+ &db_entries_);
+ db_ = db.get();
+ auto download_db = std::make_unique<DownloadDBImpl>(
+ DownloadNamespace::NAMESPACE_BROWSER_DOWNLOAD,
+ base::FilePath(FILE_PATH_LITERAL("/test/db/fakepath")), std::move(db));
+ db_cache_ = std::make_unique<DownloadDBCache>(std::move(download_db));
+ db_cache_->SetTimerTaskRunnerForTesting(task_runner_);
+ }
+
+ void InitCallback(std::vector<DownloadDBEntry>* loaded_entries,
+ bool success,
+ std::unique_ptr<std::vector<DownloadDBEntry>> entries) {
+ loaded_entries->swap(*entries);
+ }
+
+ void PrepopulateSampleEntries() {
+ DownloadDBEntry first = CreateDownloadDBEntry();
+ DownloadDBEntry second = CreateDownloadDBEntry();
+ DownloadDBEntry third = CreateDownloadDBEntry();
+ db_entries_.insert(
+ std::make_pair("unknown," + first.GetGuid(),
+ DownloadDBConversions::DownloadDBEntryToProto(first)));
+ db_entries_.insert(
+ std::make_pair(GetKey(second.GetGuid()),
+ DownloadDBConversions::DownloadDBEntryToProto(second)));
+ db_entries_.insert(
+ std::make_pair(GetKey(third.GetGuid()),
+ DownloadDBConversions::DownloadDBEntryToProto(third)));
+ }
+
+ DownloadDB* GetDownloadDB() { return db_cache_->download_db_.get(); }
+
+ protected:
+ std::map<std::string, download_pb::DownloadDBEntry> db_entries_;
+ leveldb_proto::test::FakeDB<download_pb::DownloadDBEntry>* db_;
+ std::unique_ptr<DownloadDBCache> db_cache_;
+ scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ DISALLOW_COPY_AND_ASSIGN(DownloadDBCacheTest);
+};
+
+TEST_F(DownloadDBCacheTest, InitializeAndRetrieve) {
+ PrepopulateSampleEntries();
+ CreateDBCache();
+ std::vector<DownloadDBEntry> loaded_entries;
+ db_cache_->Initialize(base::BindOnce(&DownloadDBCacheTest::InitCallback,
+ base::Unretained(this), &loaded_entries,
+ true));
+ db_->InitCallback(true);
+ db_->LoadCallback(true);
+ ASSERT_EQ(loaded_entries.size(), 2u);
+
+ for (auto db_entry : loaded_entries) {
+ ASSERT_EQ(db_entry, db_cache_->RetrieveEntry(db_entry.GetGuid()));
+ ASSERT_EQ(db_entry,
+ DownloadDBConversions::DownloadDBEntryFromProto(
+ db_entries_.find(GetKey(db_entry.GetGuid()))->second));
+ }
+}
+
+// Test that new entry is added immediately to the database
+TEST_F(DownloadDBCacheTest, AddNewEntry) {
+ PrepopulateSampleEntries();
+ CreateDBCache();
+ std::vector<DownloadDBEntry> loaded_entries;
+ db_cache_->Initialize(base::BindOnce(&DownloadDBCacheTest::InitCallback,
+ base::Unretained(this), &loaded_entries,
+ true));
+ db_->InitCallback(true);
+ db_->LoadCallback(true);
+ ASSERT_EQ(loaded_entries.size(), 2u);
+
+ DownloadDBEntry new_entry = CreateDownloadDBEntry();
+ db_cache_->AddOrReplaceEntry(new_entry);
+ ASSERT_EQ(new_entry, db_cache_->RetrieveEntry(new_entry.GetGuid()));
+ db_->UpdateCallback(true);
+ loaded_entries.clear();
+ DownloadDB* download_db = GetDownloadDB();
+ download_db->LoadEntries(base::BindOnce(&DownloadDBCacheTest::InitCallback,
+ base::Unretained(this),
+ &loaded_entries));
+ db_->LoadCallback(true);
+ ASSERT_EQ(loaded_entries.size(), 3u);
+}
+
+// Test that modifying an existing entry could take some time to update the DB.
+TEST_F(DownloadDBCacheTest, ModifyExistingEntry) {
+ PrepopulateSampleEntries();
+ CreateDBCache();
+ std::vector<DownloadDBEntry> loaded_entries;
+ db_cache_->Initialize(base::BindOnce(&DownloadDBCacheTest::InitCallback,
+ base::Unretained(this), &loaded_entries,
+ true));
+ db_->InitCallback(true);
+ db_->LoadCallback(true);
+ ASSERT_EQ(loaded_entries.size(), 2u);
+
+ loaded_entries[0].download_info->id = 100;
+ loaded_entries[1].download_info->id = 100;
+ db_cache_->AddOrReplaceEntry(loaded_entries[0]);
+ db_cache_->AddOrReplaceEntry(loaded_entries[1]);
+
+ ASSERT_EQ(task_runner_->GetPendingTaskCount(), 1u);
+ ASSERT_GT(task_runner_->NextPendingTaskDelay(), base::TimeDelta());
+ task_runner_->FastForwardUntilNoTasksRemain();
+ db_->UpdateCallback(true);
+
+ loaded_entries.clear();
+ DownloadDB* download_db = GetDownloadDB();
+ download_db->LoadEntries(base::BindOnce(&DownloadDBCacheTest::InitCallback,
+ base::Unretained(this),
+ &loaded_entries));
+ db_->LoadCallback(true);
+ ASSERT_EQ(loaded_entries.size(), 2u);
+ ASSERT_EQ(loaded_entries[0].download_info->id, 100);
+ ASSERT_EQ(loaded_entries[1].download_info->id, 100);
+}
+
+// Test that modifying current path will immediately update the DB.
+TEST_F(DownloadDBCacheTest, FilePathChange) {
+ DownloadDBEntry entry = CreateDownloadDBEntry();
+ InProgressInfo info;
+ base::FilePath test_path = base::FilePath(FILE_PATH_LITERAL("/tmp"));
+ info.current_path = test_path;
+ entry.download_info->in_progress_info = info;
+ db_entries_.insert(
+ std::make_pair(GetKey(entry.GetGuid()),
+ DownloadDBConversions::DownloadDBEntryToProto(entry)));
+ CreateDBCache();
+ std::vector<DownloadDBEntry> loaded_entries;
+ db_cache_->Initialize(base::BindOnce(&DownloadDBCacheTest::InitCallback,
+ base::Unretained(this), &loaded_entries,
+ true));
+ db_->InitCallback(true);
+ db_->LoadCallback(true);
+ ASSERT_EQ(loaded_entries.size(), 1u);
+ ASSERT_EQ(loaded_entries[0].download_info->in_progress_info->current_path,
+ test_path);
+
+ test_path = base::FilePath(FILE_PATH_LITERAL("/test"));
+ loaded_entries[0].download_info->in_progress_info->current_path = test_path;
+ db_cache_->AddOrReplaceEntry(loaded_entries[0]);
+ db_->UpdateCallback(true);
+
+ loaded_entries.clear();
+ DownloadDB* download_db = GetDownloadDB();
+ download_db->LoadEntries(base::BindOnce(&DownloadDBCacheTest::InitCallback,
+ base::Unretained(this),
+ &loaded_entries));
+ db_->LoadCallback(true);
+ ASSERT_EQ(loaded_entries.size(), 1u);
+ ASSERT_EQ(loaded_entries[0].download_info->in_progress_info->current_path,
+ test_path);
+}
+
+TEST_F(DownloadDBCacheTest, RemoveEntry) {
+ PrepopulateSampleEntries();
+ CreateDBCache();
+ std::vector<DownloadDBEntry> loaded_entries;
+ db_cache_->Initialize(base::BindOnce(&DownloadDBCacheTest::InitCallback,
+ base::Unretained(this), &loaded_entries,
+ true));
+ db_->InitCallback(true);
+ db_->LoadCallback(true);
+ ASSERT_EQ(loaded_entries.size(), 2u);
+
+ std::string guid = loaded_entries[0].GetGuid();
+ std::string guid2 = loaded_entries[1].GetGuid();
+ db_cache_->RemoveEntry(loaded_entries[0].GetGuid());
+ db_->UpdateCallback(true);
+ ASSERT_FALSE(db_cache_->RetrieveEntry(guid));
+ ASSERT_TRUE(db_cache_->RetrieveEntry(guid2));
+
+ loaded_entries.clear();
+ DownloadDB* download_db = GetDownloadDB();
+ download_db->LoadEntries(base::BindOnce(&DownloadDBCacheTest::InitCallback,
+ base::Unretained(this),
+ &loaded_entries));
+ db_->LoadCallback(true);
+ ASSERT_EQ(loaded_entries.size(), 1u);
+ ASSERT_EQ(guid2, loaded_entries[0].GetGuid());
+}
+
+} // namespace download
diff --git a/chromium/components/download/internal/common/download_item_impl.cc b/chromium/components/download/internal/common/download_item_impl.cc
index 817deacd108..8c4705f3bf3 100644
--- a/chromium/components/download/internal/common/download_item_impl.cc
+++ b/chromium/components/download/internal/common/download_item_impl.cc
@@ -31,6 +31,7 @@
#include "base/files/file_util.h"
#include "base/format_macros.h"
#include "base/guid.h"
+#include "base/json/string_escape.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/optional.h"
@@ -39,7 +40,7 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task_runner_util.h"
-#include "components/download/downloader/in_progress/download_entry.h"
+#include "components/download/database/in_progress/download_entry.h"
#include "components/download/internal/common/download_job_impl.h"
#include "components/download/internal/common/parallel_download_utils.h"
#include "components/download/public/common/download_danger_type.h"
@@ -182,12 +183,15 @@ class DownloadItemActivatedData
out->append(base::StringPrintf(
"\"type\":\"%s\",", GetDownloadTypeNames(download_type_).c_str()));
out->append(base::StringPrintf("\"id\":\"%d\",", download_id_));
- out->append(
- base::StringPrintf("\"original_url\":\"%s\",", original_url_.c_str()));
- out->append(
- base::StringPrintf("\"final_url\":\"%s\",", final_url_.c_str()));
- out->append(
- base::StringPrintf("\"file_name\":\"%s\",", file_name_.c_str()));
+ out->append("\"original_url\":");
+ base::EscapeJSONString(original_url_, true, out);
+ out->append(",");
+ out->append("\"final_url\":");
+ base::EscapeJSONString(final_url_, true, out);
+ out->append(",");
+ out->append("\"file_name\":");
+ base::EscapeJSONString(file_name_, true, out);
+ out->append(",");
out->append(
base::StringPrintf("\"danger_type\":\"%s\",",
GetDownloadDangerNames(danger_type_).c_str()));
@@ -425,7 +429,6 @@ DownloadItemImpl::~DownloadItemImpl() {
for (auto& observer : observers_)
observer.OnDownloadDestroyed(this);
- delegate_->AssertStateConsistent(this);
delegate_->Detach();
}
@@ -590,10 +593,8 @@ void DownloadItemImpl::Remove() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
RecordDownloadDeletion(GetEndTime(), GetMimeType());
- delegate_->AssertStateConsistent(this);
InterruptAndDiscardPartialState(DOWNLOAD_INTERRUPT_REASON_USER_CANCELED);
UpdateObservers();
- delegate_->AssertStateConsistent(this);
NotifyRemoved();
delegate_->DownloadRemoved(this);
@@ -1146,6 +1147,7 @@ ResumeMode DownloadItemImpl::GetResumeMode() const {
case DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED:
case DOWNLOAD_INTERRUPT_REASON_SERVER_CERT_PROBLEM:
case DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN:
+ case DOWNLOAD_INTERRUPT_REASON_SERVER_CROSS_ORIGIN_REDIRECT:
case DOWNLOAD_INTERRUPT_REASON_FILE_SAME_AS_SOURCE:
return ResumeMode::INVALID;
}
@@ -1340,6 +1342,12 @@ void DownloadItemImpl::DestinationCompleted(
MaybeCompleteDownload();
}
+void DownloadItemImpl::SetDelegate(DownloadItemImplDelegate* delegate) {
+ delegate_->Detach();
+ delegate_ = delegate;
+ delegate_->Attach();
+}
+
// **** Download progression cascade
void DownloadItemImpl::Init(bool active,
@@ -2341,6 +2349,7 @@ void DownloadItemImpl::ResumeInterruptedDownload(
download_params->set_etag(GetETag());
download_params->set_hash_of_partial_file(GetHash());
download_params->set_hash_state(std::move(hash_state_));
+ download_params->set_guid(guid_);
// TODO(xingliu): Read |fetch_error_body| and |request_headers_| from the
// cache, and don't copy them into DownloadItemImpl.
@@ -2375,7 +2384,7 @@ void DownloadItemImpl::ResumeInterruptedDownload(
GetResumeMode(), time_since_start);
}
- delegate_->ResumeInterruptedDownload(std::move(download_params), GetId(),
+ delegate_->ResumeInterruptedDownload(std::move(download_params),
request_info_.site_url);
if (job_)
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 363d564d996..bc3f9c3c289 100644
--- a/chromium/components/download/internal/common/download_item_impl_delegate.cc
+++ b/chromium/components/download/internal/common/download_item_impl_delegate.cc
@@ -5,7 +5,7 @@
#include "components/download/public/common/download_item_impl_delegate.h"
#include "base/logging.h"
-#include "components/download/downloader/in_progress/download_entry.h"
+#include "components/download/database/in_progress/download_entry.h"
#include "components/download/public/common/download_danger_type.h"
#include "components/download/public/common/download_item_impl.h"
@@ -65,7 +65,6 @@ std::string DownloadItemImplDelegate::GetApplicationClientIdForFileScanning()
void DownloadItemImplDelegate::ResumeInterruptedDownload(
std::unique_ptr<DownloadUrlParameters> params,
- uint32_t id,
const GURL& site_url) {}
void DownloadItemImplDelegate::UpdatePersistence(DownloadItemImpl* download) {}
@@ -82,9 +81,6 @@ void DownloadItemImplDelegate::ShowDownloadInShell(DownloadItemImpl* download) {
void DownloadItemImplDelegate::DownloadRemoved(DownloadItemImpl* download) {}
-void DownloadItemImplDelegate::AssertStateConsistent(
- DownloadItemImpl* download) const {}
-
void DownloadItemImplDelegate::DownloadInterrupted(DownloadItemImpl* download) {
}
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 1503ec33b07..d1689fa07fe 100644
--- a/chromium/components/download/internal/common/download_item_impl_unittest.cc
+++ b/chromium/components/download/internal/common/download_item_impl_unittest.cc
@@ -19,7 +19,7 @@
#include "base/containers/queue.h"
#include "base/files/file_util.h"
#include "base/memory/ptr_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread.h"
#include "components/download/public/common/download_create_info.h"
@@ -79,16 +79,14 @@ class MockDelegate : public DownloadItemImplDelegate {
MOCK_METHOD1(CheckForFileRemoval, void(DownloadItemImpl*));
void ResumeInterruptedDownload(std::unique_ptr<DownloadUrlParameters> params,
- uint32_t id,
const GURL& site_url) override {
- MockResumeInterruptedDownload(params.get(), id);
+ MockResumeInterruptedDownload(params.get());
}
- MOCK_METHOD2(MockResumeInterruptedDownload,
- void(DownloadUrlParameters* params, uint32_t id));
+ MOCK_METHOD1(MockResumeInterruptedDownload,
+ void(DownloadUrlParameters* params));
MOCK_METHOD1(DownloadOpened, void(DownloadItemImpl*));
MOCK_METHOD1(DownloadRemoved, void(DownloadItemImpl*));
- MOCK_CONST_METHOD1(AssertStateConsistent, void(DownloadItemImpl*));
MOCK_CONST_METHOD0(IsOffTheRecord, bool());
void VerifyAndClearExpectations() {
@@ -98,7 +96,6 @@ class MockDelegate : public DownloadItemImplDelegate {
private:
void SetDefaultExpectations() {
- EXPECT_CALL(*this, AssertStateConsistent(_)).WillRepeatedly(Return());
EXPECT_CALL(*this, ShouldOpenFileBasedOnExtension(_))
.WillRepeatedly(Return(false));
EXPECT_CALL(*this, ShouldOpenDownload(_, _)).WillRepeatedly(Return(true));
@@ -274,9 +271,8 @@ class DownloadItemTest : public testing::Test {
// be torn down at the end of the test unless DestroyDownloadItem is
// called.
DownloadItemImpl* CreateDownloadItem() {
- create_info_->download_id = ++next_download_id_;
DownloadItemImpl* download = new DownloadItemImpl(
- mock_delegate(), create_info_->download_id, *create_info_);
+ mock_delegate(), ++next_download_id_, *create_info_);
allocated_downloads_[download] = base::WrapUnique(download);
return download;
}
@@ -311,8 +307,6 @@ class DownloadItemTest : public testing::Test {
// So that we don't have a function writing to a stack variable
// lying around if the above failed.
mock_delegate()->VerifyAndClearExpectations();
- EXPECT_CALL(*mock_delegate(), AssertStateConsistent(_))
- .WillRepeatedly(Return());
EXPECT_CALL(*mock_delegate(), ShouldOpenFileBasedOnExtension(_))
.WillRepeatedly(Return(false));
EXPECT_CALL(*mock_delegate(), ShouldOpenDownload(_, _))
@@ -465,7 +459,7 @@ TEST_F(DownloadItemTest, NotificationAfterInterrupted) {
EXPECT_CALL(*download_file, Cancel());
TestDownloadItemObserver observer(item);
- EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_, _)).Times(0);
+ EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_)).Times(0);
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, 0,
@@ -622,13 +616,12 @@ TEST_F(DownloadItemTest, AutomaticResumption_Continue) {
EXPECT_CALL(*download_file, Detach());
// Resumption attempt should pass the intermediate file along.
- EXPECT_CALL(*mock_delegate(),
- MockResumeInterruptedDownload(
- AllOf(Property(&DownloadUrlParameters::file_path,
- Property(&base::FilePath::value,
- kDummyIntermediatePath)),
- Property(&DownloadUrlParameters::offset, 1)),
- _));
+ EXPECT_CALL(
+ *mock_delegate(),
+ MockResumeInterruptedDownload(AllOf(
+ Property(&DownloadUrlParameters::file_path,
+ Property(&base::FilePath::value, kDummyIntermediatePath)),
+ Property(&DownloadUrlParameters::offset, 1))));
base::HistogramTester histogram_tester;
item->DestinationObserverAsWeakPtr()->DestinationError(
@@ -672,11 +665,9 @@ TEST_F(DownloadItemTest, AutomaticResumption_Restart) {
EXPECT_EQ(kDummyIntermediatePath, item->GetFullPath().value());
// Resumption attempt should have discarded intermediate file.
- EXPECT_CALL(*mock_delegate(),
- MockResumeInterruptedDownload(
- Property(&DownloadUrlParameters::file_path,
- Property(&base::FilePath::empty, true)),
- _));
+ EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(Property(
+ &DownloadUrlParameters::file_path,
+ Property(&base::FilePath::empty, true))));
base::HistogramTester histogram_tester;
item->DestinationObserverAsWeakPtr()->DestinationError(
@@ -743,13 +734,12 @@ TEST_F(DownloadItemTest, AutomaticResumption_ContentLengthMismatch) {
EXPECT_CALL(*download_file, Detach());
// Resumption attempt should pass the intermediate file along.
- EXPECT_CALL(*mock_delegate(),
- MockResumeInterruptedDownload(
- AllOf(Property(&DownloadUrlParameters::file_path,
- Property(&base::FilePath::value,
- kDummyIntermediatePath)),
- Property(&DownloadUrlParameters::offset, 1)),
- _));
+ EXPECT_CALL(
+ *mock_delegate(),
+ MockResumeInterruptedDownload(AllOf(
+ Property(&DownloadUrlParameters::file_path,
+ Property(&base::FilePath::value, kDummyIntermediatePath)),
+ Property(&DownloadUrlParameters::offset, 1))));
base::HistogramTester histogram_tester;
item->DestinationObserverAsWeakPtr()->DestinationError(
@@ -821,13 +811,12 @@ TEST_F(DownloadItemTest, AutomaticResumption_AttemptLimit) {
.WillRepeatedly(SaveArg<1>(&callback));
// All attempts at resumption should pass along the intermediate file.
- EXPECT_CALL(*mock_delegate(),
- MockResumeInterruptedDownload(
- AllOf(Property(&DownloadUrlParameters::file_path,
- Property(&base::FilePath::value,
- kDummyIntermediatePath)),
- Property(&DownloadUrlParameters::offset, 1)),
- _))
+ EXPECT_CALL(
+ *mock_delegate(),
+ MockResumeInterruptedDownload(AllOf(
+ Property(&DownloadUrlParameters::file_path,
+ Property(&base::FilePath::value, kDummyIntermediatePath)),
+ Property(&DownloadUrlParameters::offset, 1))))
.Times(DownloadItemImpl::kMaxAutoResumeAttempts);
for (int i = 0; i < (DownloadItemImpl::kMaxAutoResumeAttempts + 1); ++i) {
SCOPED_TRACE(::testing::Message() << "Iteration " << i);
@@ -914,13 +903,12 @@ TEST_F(DownloadItemTest, FailedResumptionDoesntUpdateOriginState) {
EXPECT_EQ(kFirstURL, item->GetURL().spec());
EXPECT_EQ(kMimeType, item->GetMimeType());
- EXPECT_CALL(*mock_delegate(),
- MockResumeInterruptedDownload(
- AllOf(Property(&DownloadUrlParameters::file_path,
- Property(&base::FilePath::value,
- kDummyIntermediatePath)),
- Property(&DownloadUrlParameters::offset, 1)),
- _));
+ EXPECT_CALL(
+ *mock_delegate(),
+ MockResumeInterruptedDownload(AllOf(
+ Property(&DownloadUrlParameters::file_path,
+ Property(&base::FilePath::value, kDummyIntermediatePath)),
+ Property(&DownloadUrlParameters::offset, 1))));
EXPECT_CALL(*download_file, Detach());
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR, 1,
@@ -991,7 +979,7 @@ TEST_F(DownloadItemTest, SucceededResumptionUpdatesOriginState) {
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
- EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_, _));
+ EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_));
EXPECT_CALL(*download_file, Detach());
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR, 0,
@@ -1042,7 +1030,7 @@ TEST_F(DownloadItemTest, ClearReceivedSliceIfEtagChanged) {
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
- EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_, _));
+ EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_));
EXPECT_CALL(*download_file, Detach());
item->DestinationObserverAsWeakPtr()->DestinationUpdate(10, 100,
@@ -1068,6 +1056,45 @@ TEST_F(DownloadItemTest, ClearReceivedSliceIfEtagChanged) {
CleanupItem(item, download_file, DownloadItem::IN_PROGRESS);
}
+// Ensure when a network socket error happens on resumption, the received slices
+// info should be kept if the download is not restarted from beginning, so the
+// download progress will not move backward.
+TEST_F(DownloadItemTest, KeepReceivedSliceIfNetworkError) {
+ const char kFirstETag[] = "ABC";
+ const DownloadItem::ReceivedSlices kReceivedSlice = {
+ DownloadItem::ReceivedSlice(0, 10), DownloadItem::ReceivedSlice(20, 30)};
+ create_info()->etag = kFirstETag;
+
+ DownloadItemImpl* item = CreateDownloadItem();
+ MockDownloadFile* download_file =
+ DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
+
+ EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_));
+ EXPECT_CALL(*download_file, Detach());
+
+ item->DestinationObserverAsWeakPtr()->DestinationUpdate(20, 100,
+ kReceivedSlice);
+ EXPECT_EQ(kReceivedSlice, item->GetReceivedSlices());
+ EXPECT_EQ(20, item->GetReceivedBytes());
+
+ item->DestinationObserverAsWeakPtr()->DestinationError(
+ DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR, 20 /* bytes_so_far */,
+ std::unique_ptr<crypto::SecureHash>());
+ task_environment_.RunUntilIdle();
+
+ // Simulate a socket error, and start the download.
+ create_info()->result = DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT;
+ DownloadItemImplDelegate::DownloadTargetCallback target_callback;
+ download_file = CallDownloadItemStart(item, &target_callback);
+
+ // After starting the download, the slice info and received bytes should not
+ // change.
+ EXPECT_EQ(kReceivedSlice, item->GetReceivedSlices());
+ EXPECT_EQ(20, item->GetReceivedBytes());
+
+ CleanupItem(item, download_file, DownloadItem::IN_PROGRESS);
+}
+
// Test that resumption uses the final URL in a URL chain when resuming.
TEST_F(DownloadItemTest, ResumeUsesFinalURL) {
create_info()->save_info->prompt_for_save_location = false;
@@ -1085,10 +1112,9 @@ TEST_F(DownloadItemTest, ResumeUsesFinalURL) {
EXPECT_CALL(*download_file, FullPath())
.WillOnce(ReturnRefOfCopy(base::FilePath()));
EXPECT_CALL(*download_file, Detach());
- EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(
- Property(&DownloadUrlParameters::url,
- GURL("http://example.com/c")),
- _))
+ EXPECT_CALL(*mock_delegate(),
+ MockResumeInterruptedDownload(Property(
+ &DownloadUrlParameters::url, GURL("http://example.com/c"))))
.Times(1);
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR, 1,
diff --git a/chromium/components/download/internal/common/download_response_handler.cc b/chromium/components/download/internal/common/download_response_handler.cc
index 941a5479498..a0080bd0195 100644
--- a/chromium/components/download/internal/common/download_response_handler.cc
+++ b/chromium/components/download/internal/common/download_response_handler.cc
@@ -53,6 +53,7 @@ DownloadResponseHandler::DownloadResponseHandler(
bool is_parallel_request,
bool is_transient,
bool fetch_error_body,
+ bool follow_cross_origin_redirects,
const DownloadUrlParameters::RequestHeadersType& request_headers,
const std::string& request_origin,
DownloadSource download_source,
@@ -63,8 +64,11 @@ DownloadResponseHandler::DownloadResponseHandler(
url_chain_(std::move(url_chain)),
method_(resource_request->method),
referrer_(resource_request->referrer),
+ referrer_policy_(resource_request->referrer_policy),
is_transient_(is_transient),
fetch_error_body_(fetch_error_body),
+ follow_cross_origin_redirects_(follow_cross_origin_redirects),
+ first_origin_(url::Origin::Create(resource_request->url)),
request_headers_(request_headers),
request_origin_(request_origin),
download_source_(download_source),
@@ -81,8 +85,7 @@ DownloadResponseHandler::DownloadResponseHandler(
DownloadResponseHandler::~DownloadResponseHandler() = default;
void DownloadResponseHandler::OnReceiveResponse(
- const network::ResourceResponseHead& head,
- network::mojom::DownloadedTempFilePtr downloaded_file) {
+ const network::ResourceResponseHead& head) {
create_info_ = CreateDownloadCreateInfo(head);
cert_status_ = head.cert_status;
@@ -130,6 +133,7 @@ DownloadResponseHandler::CreateDownloadCreateInfo(
create_info->connection_info = head.connection_info;
create_info->url_chain = url_chain_;
create_info->referrer_url = referrer_;
+ create_info->referrer_policy = referrer_policy_;
create_info->transient = is_transient_;
create_info->response_headers = head.headers;
create_info->offset = create_info->save_info->offset;
@@ -146,6 +150,17 @@ DownloadResponseHandler::CreateDownloadCreateInfo(
void DownloadResponseHandler::OnReceiveRedirect(
const net::RedirectInfo& redirect_info,
const network::ResourceResponseHead& head) {
+ if (!follow_cross_origin_redirects_ &&
+ !first_origin_.IsSameOriginWith(
+ url::Origin::Create(redirect_info.new_url))) {
+ abort_reason_ = DOWNLOAD_INTERRUPT_REASON_SERVER_CROSS_ORIGIN_REDIRECT;
+ url_chain_.push_back(redirect_info.new_url);
+ method_ = redirect_info.new_method;
+ referrer_ = GURL(redirect_info.new_referrer);
+ referrer_policy_ = redirect_info.new_referrer_policy;
+ OnComplete(network::URLLoaderCompletionStatus(net::OK));
+ return;
+ }
if (is_partial_request_) {
// A redirect while attempting a partial resumption indicates a potential
// middle box. Trigger another interruption so that the
@@ -157,12 +172,10 @@ void DownloadResponseHandler::OnReceiveRedirect(
url_chain_.push_back(redirect_info.new_url);
method_ = redirect_info.new_method;
referrer_ = GURL(redirect_info.new_referrer);
+ referrer_policy_ = redirect_info.new_referrer_policy;
delegate_->OnReceiveRedirect();
}
-void DownloadResponseHandler::OnDataDownloaded(int64_t data_length,
- int64_t encoded_length) {}
-
void DownloadResponseHandler::OnUploadProgress(
int64_t current_position,
int64_t total_size,
@@ -197,8 +210,10 @@ void DownloadResponseHandler::OnComplete(
ConvertInterruptReasonToMojoNetworkRequestStatus(reason));
}
- if (started_)
+ if (started_) {
+ delegate_->OnResponseCompleted();
return;
+ }
// OnComplete() called without OnReceiveResponse(). This should only
// happen when the request was aborted.
@@ -206,6 +221,7 @@ void DownloadResponseHandler::OnComplete(
create_info_->result = reason;
OnResponseStarted(mojom::DownloadStreamHandlePtr());
+ delegate_->OnResponseCompleted();
}
void DownloadResponseHandler::OnResponseStarted(
diff --git a/chromium/components/download/internal/common/download_stats.cc b/chromium/components/download/internal/common/download_stats.cc
index fb06e932988..96a21a2e7c6 100644
--- a/chromium/components/download/internal/common/download_stats.cc
+++ b/chromium/components/download/internal/common/download_stats.cc
@@ -235,6 +235,11 @@ constexpr const base::FilePath::CharType* kDangerousFileTypes[] = {
FILE_PATH_LITERAL(".dhtml"), FILE_PATH_LITERAL(".dhtm"), // 304
FILE_PATH_LITERAL(".dht"), FILE_PATH_LITERAL(".shtml"), // 306
FILE_PATH_LITERAL(".shtm"), FILE_PATH_LITERAL(".sht"), // 308
+ FILE_PATH_LITERAL(".slk"), // 309
+ FILE_PATH_LITERAL(".applescript"), FILE_PATH_LITERAL(".scpt"), // 311
+ FILE_PATH_LITERAL(".scptd"), FILE_PATH_LITERAL(".seplugin"), // 313
+ FILE_PATH_LITERAL(".osas"), FILE_PATH_LITERAL(".osax"), // 315
+ FILE_PATH_LITERAL(".settingcontent-ms"), FILE_PATH_LITERAL(".oxt"), // 317
// NOTE! When you add a type here, please add the UMA value as a comment.
// These must all match DownloadItem.DangerousFileType in
// enums.xml. From 263 onward, they should also match
diff --git a/chromium/components/download/internal/common/download_stats_unittest.cc b/chromium/components/download/internal/common/download_stats_unittest.cc
index 555bb2486a6..e49323d188b 100644
--- a/chromium/components/download/internal/common/download_stats_unittest.cc
+++ b/chromium/components/download/internal/common/download_stats_unittest.cc
@@ -4,7 +4,7 @@
#include "components/download/public/common/download_stats.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/components/download/internal/common/download_task_runner.cc b/chromium/components/download/internal/common/download_task_runner.cc
index 55db38f64b6..7d688dc735c 100644
--- a/chromium/components/download/internal/common/download_task_runner.cc
+++ b/chromium/components/download/internal/common/download_task_runner.cc
@@ -45,20 +45,13 @@ scoped_refptr<base::SequencedTaskRunner> GetDownloadTaskRunner() {
void SetIOTaskRunner(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
+ DCHECK(task_runner);
+
base::AutoLock auto_lock(GetIOTaskRunnerLock());
- static int count = 0;
- if (task_runner) {
- DCHECK(!g_io_task_runner.Get() ||
- task_runner.get() == g_io_task_runner.Get().get());
- count++;
- g_io_task_runner.Get() = task_runner;
+ if (g_io_task_runner.Get())
return;
- }
- count--;
- DCHECK_GE(count, 0);
- if (count == 0)
- g_io_task_runner.Get() = nullptr;
+ g_io_task_runner.Get() = task_runner;
}
scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner() {
diff --git a/chromium/components/download/internal/common/download_ukm_helper.cc b/chromium/components/download/internal/common/download_ukm_helper.cc
index 8f071e66c56..5974a36f20c 100644
--- a/chromium/components/download/internal/common/download_ukm_helper.cc
+++ b/chromium/components/download/internal/common/download_ukm_helper.cc
@@ -84,11 +84,4 @@ void DownloadUkmHelper::RecordDownloadCompleted(
.Record(ukm::UkmRecorder::Get());
}
-void DownloadUkmHelper::UpdateSourceURL(ukm::UkmRecorder* ukm_recorder,
- ukm::SourceId source_id,
- const GURL& url) {
- if (ukm_recorder)
- ukm_recorder->UpdateSourceURL(source_id, url);
-}
-
} // namespace download
diff --git a/chromium/components/download/internal/common/download_utils.cc b/chromium/components/download/internal/common/download_utils.cc
index cc9f500041b..efd6eeffcdc 100644
--- a/chromium/components/download/internal/common/download_utils.cc
+++ b/chromium/components/download/internal/common/download_utils.cc
@@ -359,6 +359,67 @@ DownloadEntry CreateDownloadEntryFromItem(
GetUniqueDownloadId());
}
+DownloadDBEntry CreateDownloadDBEntryFromItem(
+ const DownloadItem& item,
+ DownloadSource download_source,
+ bool fetch_error_body,
+ const DownloadUrlParameters::RequestHeadersType& request_headers) {
+ DownloadDBEntry entry;
+ DownloadInfo download_info;
+ download_info.guid = item.GetGuid();
+ download_info.id = item.GetId();
+ InProgressInfo in_progress_info;
+ in_progress_info.url_chain = item.GetUrlChain();
+ in_progress_info.referrer_url = item.GetReferrerUrl();
+ in_progress_info.site_url = item.GetSiteUrl();
+ in_progress_info.tab_url = item.GetTabUrl();
+ in_progress_info.tab_referrer_url = item.GetTabReferrerUrl();
+ in_progress_info.fetch_error_body = fetch_error_body;
+ in_progress_info.request_headers = request_headers;
+ in_progress_info.etag = item.GetETag();
+ in_progress_info.last_modified = item.GetLastModifiedTime();
+ in_progress_info.mime_type = item.GetMimeType();
+ in_progress_info.original_mime_type = item.GetOriginalMimeType();
+ in_progress_info.total_bytes = item.GetTotalBytes();
+ in_progress_info.current_path = item.GetFullPath();
+ in_progress_info.target_path = item.GetTargetFilePath();
+ in_progress_info.received_bytes = item.GetReceivedBytes();
+ in_progress_info.start_time = item.GetStartTime();
+ in_progress_info.end_time = item.GetEndTime();
+ in_progress_info.received_slices = item.GetReceivedSlices();
+ in_progress_info.hash = item.GetHash();
+ in_progress_info.transient = item.IsTransient();
+ in_progress_info.state = item.GetState();
+ in_progress_info.danger_type = item.GetDangerType();
+ in_progress_info.interrupt_reason = item.GetLastReason();
+ in_progress_info.paused = item.IsPaused();
+ in_progress_info.bytes_wasted = item.GetBytesWasted();
+
+ download_info.in_progress_info = in_progress_info;
+
+ UkmInfo ukm_info(download_source, GetUniqueDownloadId());
+ download_info.ukm_info = ukm_info;
+ entry.download_info = download_info;
+ return entry;
+}
+
+base::Optional<DownloadEntry> CreateDownloadEntryFromDownloadDBEntry(
+ base::Optional<DownloadDBEntry> entry) {
+ if (!entry || !entry->download_info)
+ return base::Optional<DownloadEntry>();
+
+ base::Optional<InProgressInfo> in_progress_info =
+ entry->download_info->in_progress_info;
+ base::Optional<UkmInfo> ukm_info = entry->download_info->ukm_info;
+ if (!ukm_info || !in_progress_info)
+ return base::Optional<DownloadEntry>();
+
+ return base::Optional<DownloadEntry>(DownloadEntry(
+ entry->download_info->guid, std::string(), ukm_info->download_source,
+ in_progress_info->fetch_error_body, in_progress_info->request_headers,
+ ukm_info->ukm_download_id));
+}
+
uint64_t GetUniqueDownloadId() {
// Get a new UKM download_id that is not 0.
uint64_t download_id = 0;
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 9ed647a5b05..ca5a426dfe3 100644
--- a/chromium/components/download/internal/common/in_progress_download_manager.cc
+++ b/chromium/components/download/internal/common/in_progress_download_manager.cc
@@ -4,13 +4,20 @@
#include "components/download/public/common/in_progress_download_manager.h"
+#include "base/command_line.h"
#include "base/optional.h"
#include "base/task_scheduler/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "components/download/downloader/in_progress/in_progress_cache_impl.h"
+#include "components/download/database/download_db_entry.h"
+#include "components/download/database/download_db_impl.h"
+#include "components/download/database/download_namespace.h"
+#include "components/download/database/in_progress/in_progress_cache_impl.h"
+#include "components/download/database/switches.h"
+#include "components/download/internal/common/download_db_cache.h"
#include "components/download/internal/common/resource_downloader.h"
#include "components/download/public/common/download_file.h"
#include "components/download/public/common/download_item_impl.h"
+#include "components/download/public/common/download_start_observer.h"
#include "components/download/public/common/download_stats.h"
#include "components/download/public/common/download_task_runner.h"
#include "components/download/public/common/download_url_loader_factory_getter.h"
@@ -24,6 +31,32 @@ namespace download {
namespace {
+std::unique_ptr<DownloadItemImpl> CreateDownloadItemImpl(
+ DownloadItemImplDelegate* delegate,
+ const DownloadDBEntry entry) {
+ if (!entry.download_info)
+ return nullptr;
+
+ base::Optional<InProgressInfo> in_progress_info =
+ entry.download_info->in_progress_info;
+ if (!in_progress_info)
+ return nullptr;
+
+ return std::make_unique<DownloadItemImpl>(
+ delegate, entry.download_info->guid, entry.download_info->id,
+ in_progress_info->current_path, in_progress_info->target_path,
+ in_progress_info->url_chain, in_progress_info->referrer_url,
+ in_progress_info->site_url, in_progress_info->tab_url,
+ in_progress_info->tab_referrer_url, in_progress_info->mime_type,
+ in_progress_info->original_mime_type, in_progress_info->start_time,
+ in_progress_info->end_time, in_progress_info->etag,
+ in_progress_info->last_modified, in_progress_info->received_bytes,
+ in_progress_info->total_bytes, in_progress_info->hash,
+ in_progress_info->state, in_progress_info->danger_type,
+ in_progress_info->interrupt_reason, false, base::Time(),
+ in_progress_info->transient, in_progress_info->received_slices);
+}
+
void OnUrlDownloadHandlerCreated(
UrlDownloadHandler::UniqueUrlDownloadHandlerPtr downloader,
base::WeakPtr<InProgressDownloadManager> download_manager,
@@ -38,7 +71,7 @@ void BeginResourceDownload(
std::unique_ptr<DownloadUrlParameters> params,
std::unique_ptr<network::ResourceRequest> request,
scoped_refptr<DownloadURLLoaderFactoryGetter> url_loader_factory_getter,
- uint32_t download_id,
+ bool is_new_download,
base::WeakPtr<InProgressDownloadManager> download_manager,
const GURL& site_url,
const GURL& tab_url,
@@ -49,7 +82,7 @@ void BeginResourceDownload(
ResourceDownloader::BeginDownload(
download_manager, std::move(params), std::move(request),
std::move(url_loader_factory_getter), site_url, tab_url,
- tab_referrer_url, download_id, false, main_task_runner)
+ tab_referrer_url, is_new_download, false, main_task_runner)
.release(),
base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get()));
@@ -159,11 +192,16 @@ void InProgressDownloadObserver::OnDownloadRemoved(DownloadItem* download) {
InProgressDownloadManager::InProgressDownloadManager(
Delegate* delegate,
+ const base::FilePath& metadata_cache_dir,
const IsOriginSecureCallback& is_origin_secure_cb)
- : delegate_(delegate),
+ : is_initialized_(false),
+ delegate_(delegate),
file_factory_(new DownloadFileFactory()),
+ download_start_observer_(nullptr),
is_origin_secure_cb_(is_origin_secure_cb),
- weak_factory_(this) {}
+ weak_factory_(this) {
+ Initialize(metadata_cache_dir);
+}
InProgressDownloadManager::~InProgressDownloadManager() = default;
@@ -196,7 +234,7 @@ void InProgressDownloadManager::OnUrlDownloadHandlerCreated(
void InProgressDownloadManager::BeginDownload(
std::unique_ptr<DownloadUrlParameters> params,
scoped_refptr<DownloadURLLoaderFactoryGetter> url_loader_factory_getter,
- uint32_t download_id,
+ bool is_new_download,
const GURL& site_url,
const GURL& tab_url,
const GURL& tab_referrer_url) {
@@ -206,8 +244,9 @@ void InProgressDownloadManager::BeginDownload(
FROM_HERE,
base::BindOnce(&BeginResourceDownload, std::move(params),
std::move(request), std::move(url_loader_factory_getter),
- download_id, weak_factory_.GetWeakPtr(), site_url, tab_url,
- tab_referrer_url, base::ThreadTaskRunnerHandle::Get()));
+ is_new_download, weak_factory_.GetWeakPtr(), site_url,
+ tab_url, tab_referrer_url,
+ base::ThreadTaskRunnerHandle::Get()));
}
void InProgressDownloadManager::InterceptDownloadFromNavigation(
@@ -235,36 +274,82 @@ void InProgressDownloadManager::InterceptDownloadFromNavigation(
}
void InProgressDownloadManager::Initialize(
- const base::FilePath& metadata_cache_dir,
- base::OnceClosure callback) {
- download_metadata_cache_ = std::make_unique<InProgressCacheImpl>(
- metadata_cache_dir.empty() ? base::FilePath() :
- metadata_cache_dir.Append(kDownloadMetadataStoreFilename),
- base::CreateSequencedTaskRunnerWithTraits(
- {base::MayBlock(), base::TaskPriority::BACKGROUND,
- base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}));
-
- download_metadata_cache_->Initialize(std::move(callback));
+ const base::FilePath& metadata_cache_dir) {
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableDownloadDB)) {
+ // TODO(qinmin): migrate all the data from InProgressCache into
+ // |download_db_|.
+ download_db_cache_ =
+ std::make_unique<DownloadDBCache>(std::make_unique<DownloadDBImpl>(
+ DownloadNamespace::NAMESPACE_BROWSER_DOWNLOAD, metadata_cache_dir));
+ download_db_cache_->Initialize(base::BindOnce(
+ &InProgressDownloadManager::OnInitialized, weak_factory_.GetWeakPtr()));
+ } else {
+ download_metadata_cache_ = std::make_unique<InProgressCacheImpl>(
+ metadata_cache_dir.empty()
+ ? base::FilePath()
+ : metadata_cache_dir.Append(kDownloadMetadataStoreFilename),
+ base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}));
+ download_metadata_cache_->Initialize(base::BindOnce(
+ &InProgressDownloadManager::OnInitialized, weak_factory_.GetWeakPtr(),
+ std::make_unique<std::vector<DownloadDBEntry>>()));
+ }
}
void InProgressDownloadManager::ShutDown() {
url_download_handlers_.clear();
}
+void InProgressDownloadManager::DetermineDownloadTarget(
+ DownloadItemImpl* download,
+ const DownloadTargetCallback& callback) {
+ // TODO(http://crbug.com/851581): handle the case that |target_path| and
+ // |intermediate_path| are empty.
+ base::FilePath target_path = download->GetTargetFilePath().empty()
+ ? download->GetForcedFilePath()
+ : download->GetTargetFilePath();
+ base::FilePath intermediate_path = download->GetFullPath().empty()
+ ? download->GetForcedFilePath()
+ : download->GetFullPath();
+ callback.Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+ download->GetDangerType(), intermediate_path,
+ DOWNLOAD_INTERRUPT_REASON_NONE);
+}
+
void InProgressDownloadManager::ResumeInterruptedDownload(
std::unique_ptr<DownloadUrlParameters> params,
- uint32_t id,
- const GURL& site_url) {}
+ const GURL& site_url) {
+ if (!url_loader_factory_getter_)
+ return;
+
+ BeginDownload(std::move(params), url_loader_factory_getter_, false, site_url,
+ GURL(), GURL());
+}
+
+bool InProgressDownloadManager::ShouldOpenDownload(
+ DownloadItemImpl* item,
+ const ShouldOpenDownloadCallback& callback) {
+ return true;
+}
base::Optional<DownloadEntry> InProgressDownloadManager::GetInProgressEntry(
DownloadItemImpl* download) {
- if (!download || !download_metadata_cache_)
+ if (!download || !download_metadata_cache_ || !download_db_cache_)
return base::Optional<DownloadEntry>();
- return download_metadata_cache_->RetrieveEntry(download->GetGuid());
+ if (download_metadata_cache_)
+ return download_metadata_cache_->RetrieveEntry(download->GetGuid());
+
+ return CreateDownloadEntryFromDownloadDBEntry(
+ download_db_cache_->RetrieveEntry(download->GetGuid()));
}
void InProgressDownloadManager::ReportBytesWasted(DownloadItemImpl* download) {
+ if (download_db_cache_)
+ download_db_cache_->OnDownloadUpdated(download);
+
if (!download_metadata_cache_)
return;
base::Optional<DownloadEntry> entry_opt =
@@ -276,6 +361,12 @@ void InProgressDownloadManager::ReportBytesWasted(DownloadItemImpl* download) {
}
}
+void InProgressDownloadManager::RemoveInProgressDownload(
+ const std::string& guid) {
+ if (download_db_cache_)
+ download_db_cache_->RemoveEntry(guid);
+}
+
void InProgressDownloadManager::StartDownload(
std::unique_ptr<DownloadCreateInfo> info,
std::unique_ptr<InputStream> stream,
@@ -283,9 +374,10 @@ void InProgressDownloadManager::StartDownload(
const DownloadUrlParameters::OnStartedCallback& on_started) {
DCHECK(info);
- uint32_t download_id = info->download_id;
- bool new_download = (download_id == DownloadItem::kInvalidId);
- if (new_download && info->result == DOWNLOAD_INTERRUPT_REASON_NONE) {
+ if (info->is_new_download &&
+ (info->result == DOWNLOAD_INTERRUPT_REASON_NONE ||
+ info->result ==
+ DOWNLOAD_INTERRUPT_REASON_SERVER_CROSS_ORIGIN_REDIRECT)) {
if (delegate_ && delegate_->InterceptDownload(*info)) {
GetDownloadTaskRunner()->DeleteSoon(FROM_HERE, stream.release());
return;
@@ -303,45 +395,37 @@ void InProgressDownloadManager::StartDownload(
std::vector<GURL> url_chain = info->url_chain;
std::string mime_type = info->mime_type;
- if (new_download) {
+ if (info->is_new_download) {
RecordDownloadConnectionSecurity(info->url(), info->url_chain);
RecordDownloadContentTypeSecurity(info->url(), info->url_chain,
info->mime_type, is_origin_secure_cb_);
}
- base::RepeatingCallback<void(uint32_t)> got_id(base::BindRepeating(
- &InProgressDownloadManager::StartDownloadWithId,
- weak_factory_.GetWeakPtr(), base::Passed(&info), base::Passed(&stream),
- std::move(url_loader_factory_getter), on_started, new_download));
- if (new_download) {
- // TODO(qinmin): use GUID as the key for downloads history table so we don't
- // rely on the delegate to provide the next download ID.
- if (delegate_)
- delegate_->GetNextId(std::move(got_id));
+ if (delegate_) {
+ delegate_->StartDownloadItem(
+ std::move(info), on_started,
+ base::BindOnce(&InProgressDownloadManager::StartDownloadWithItem,
+ weak_factory_.GetWeakPtr(), std::move(stream),
+ std::move(url_loader_factory_getter)));
} else {
- std::move(got_id).Run(download_id);
+ std::string guid = info->guid;
+ StartDownloadWithItem(std::move(stream),
+ std::move(url_loader_factory_getter), std::move(info),
+ GetInProgressDownload(guid));
}
}
-void InProgressDownloadManager::StartDownloadWithId(
- std::unique_ptr<DownloadCreateInfo> info,
+void InProgressDownloadManager::StartDownloadWithItem(
std::unique_ptr<InputStream> stream,
scoped_refptr<DownloadURLLoaderFactoryGetter> url_loader_factory_getter,
- const DownloadUrlParameters::OnStartedCallback& on_started,
- bool new_download,
- uint32_t id) {
- DCHECK_NE(DownloadItem::kInvalidId, id);
- DownloadItemImpl* download =
- delegate_ ? delegate_->GetDownloadItem(id, new_download, *info) : nullptr;
-
+ std::unique_ptr<DownloadCreateInfo> info,
+ DownloadItemImpl* download) {
if (!download) {
// If the download is no longer known to the DownloadManager, then it was
// removed after it was resumed. Ignore. If the download is cancelled
// while resuming, then also ignore the request.
if (info->request_handle)
info->request_handle->CancelRequest(true);
- if (!on_started.is_null())
- on_started.Run(nullptr, DOWNLOAD_INTERRUPT_REASON_USER_CANCELED);
// The ByteStreamReader lives and dies on the download sequence.
if (info->result == DOWNLOAD_INTERRUPT_REASON_NONE)
GetDownloadTaskRunner()->DeleteSoon(FROM_HERE, stream.release());
@@ -362,21 +446,31 @@ void InProgressDownloadManager::StartDownloadWithId(
}
}
- if (!in_progress_download_observer_) {
- in_progress_download_observer_ =
- std::make_unique<InProgressDownloadObserver>(
- download_metadata_cache_.get());
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableDownloadDB)) {
+ download_db_cache_->AddOrReplaceEntry(CreateDownloadDBEntryFromItem(
+ *download, info->download_source, info->fetch_error_body,
+ info->request_headers));
+ download->RemoveObserver(download_db_cache_.get());
+ download->AddObserver(download_db_cache_.get());
+ } else {
+ if (!in_progress_download_observer_) {
+ in_progress_download_observer_ =
+ std::make_unique<InProgressDownloadObserver>(
+ download_metadata_cache_.get());
+ }
+ // May already observe this item, remove observer first.
+ download->RemoveObserver(in_progress_download_observer_.get());
+ download->AddObserver(in_progress_download_observer_.get());
}
- // May already observe this item, remove observer first.
- download->RemoveObserver(in_progress_download_observer_.get());
- download->AddObserver(in_progress_download_observer_.get());
std::unique_ptr<DownloadFile> download_file;
if (info->result == DOWNLOAD_INTERRUPT_REASON_NONE) {
DCHECK(stream);
download_file.reset(file_factory_->CreateFile(
std::move(info->save_info), default_download_directory,
- std::move(stream), id, download->DestinationObserverAsWeakPtr()));
+ std::move(stream), download->GetId(),
+ download->DestinationObserverAsWeakPtr()));
}
// It is important to leave info->save_info intact in the case of an interrupt
// so that the DownloadItem can salvage what it can out of a failed
@@ -387,16 +481,45 @@ void InProgressDownloadManager::StartDownloadWithId(
std::move(url_loader_factory_getter),
delegate_ ? delegate_->GetURLRequestContextGetter(*info) : nullptr);
- // For interrupted downloads, Start() will transition the state to
- // IN_PROGRESS and consumers will be notified via OnDownloadUpdated().
- // For new downloads, we notify here, rather than earlier, so that
- // the download_file is bound to download and all the usual
- // setters (e.g. Cancel) work.
- if (new_download && delegate_)
- delegate_->OnNewDownloadStarted(download);
+ if (download_start_observer_)
+ download_start_observer_->OnDownloadStarted(download);
+}
- if (!on_started.is_null())
- on_started.Run(download, DOWNLOAD_INTERRUPT_REASON_NONE);
+void InProgressDownloadManager::OnInitialized(
+ std::unique_ptr<std::vector<DownloadDBEntry>> entries) {
+ for (const auto& entry : *entries) {
+ auto item = CreateDownloadItemImpl(this, entry);
+ item->AddObserver(download_db_cache_.get());
+ in_progress_downloads_.emplace_back(std::move(item));
+ }
+ is_initialized_ = true;
+ for (auto& callback : on_initialized_callbacks_)
+ std::move(*callback).Run();
+ on_initialized_callbacks_.clear();
}
+DownloadItemImpl* InProgressDownloadManager::GetInProgressDownload(
+ const std::string& guid) {
+ for (auto& item : in_progress_downloads_) {
+ if (item->GetGuid() == guid)
+ return item.get();
+ }
+ return nullptr;
+}
+
+void InProgressDownloadManager::NotifyWhenInitialized(
+ base::OnceClosure on_initialized_cb) {
+ if (is_initialized_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ std::move(on_initialized_cb));
+ return;
+ }
+ on_initialized_callbacks_.emplace_back(
+ std::make_unique<base::OnceClosure>(std::move(on_initialized_cb)));
+}
+
+std::vector<std::unique_ptr<download::DownloadItemImpl>>
+InProgressDownloadManager::TakeInProgressDownloads() {
+ return std::move(in_progress_downloads_);
+}
} // namespace download
diff --git a/chromium/components/download/internal/common/resource_downloader.cc b/chromium/components/download/internal/common/resource_downloader.cc
index 988cf5cef88..5d958e95049 100644
--- a/chromium/components/download/internal/common/resource_downloader.cc
+++ b/chromium/components/download/internal/common/resource_downloader.cc
@@ -8,6 +8,7 @@
#include "components/download/public/common/download_url_loader_factory_getter.h"
#include "components/download/public/common/stream_handle_input_stream.h"
+#include "components/download/public/common/url_download_request_handle.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace network {
@@ -26,12 +27,9 @@ class URLLoaderStatusMonitor : public network::mojom::URLLoaderClient {
~URLLoaderStatusMonitor() override = default;
// network::mojom::URLLoaderClient
- void OnReceiveResponse(
- const network::ResourceResponseHead& head,
- network::mojom::DownloadedTempFilePtr downloaded_file) override {}
+ void OnReceiveResponse(const network::ResourceResponseHead& head) override {}
void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
const network::ResourceResponseHead& head) override {}
- void OnDataDownloaded(int64_t data_length, int64_t encoded_length) override {}
void OnUploadProgress(int64_t current_position,
int64_t total_size,
OnUploadProgressCallback callback) override {}
@@ -65,13 +63,13 @@ std::unique_ptr<ResourceDownloader> ResourceDownloader::BeginDownload(
const GURL& site_url,
const GURL& tab_url,
const GURL& tab_referrer_url,
- uint32_t download_id,
+ bool is_new_download,
bool is_parallel_request,
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
auto downloader = std::make_unique<ResourceDownloader>(
delegate, std::move(request), params->render_process_host_id(),
params->render_frame_host_routing_id(), site_url, tab_url,
- tab_referrer_url, download_id, task_runner,
+ tab_referrer_url, is_new_download, task_runner,
std::move(url_loader_factory_getter));
downloader->Start(std::move(params), is_parallel_request);
@@ -97,8 +95,8 @@ ResourceDownloader::InterceptNavigationResponse(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
auto downloader = std::make_unique<ResourceDownloader>(
delegate, std::move(resource_request), render_process_id, render_frame_id,
- site_url, tab_url, tab_referrer_url, download::DownloadItem::kInvalidId,
- task_runner, std::move(url_loader_factory_getter));
+ site_url, tab_url, tab_referrer_url, true, task_runner,
+ std::move(url_loader_factory_getter));
downloader->InterceptResponse(std::move(response), std::move(url_chain),
cert_status,
std::move(url_loader_client_endpoints));
@@ -113,13 +111,13 @@ ResourceDownloader::ResourceDownloader(
const GURL& site_url,
const GURL& tab_url,
const GURL& tab_referrer_url,
- uint32_t download_id,
+ bool is_new_download,
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
scoped_refptr<download::DownloadURLLoaderFactoryGetter>
url_loader_factory_getter)
: delegate_(delegate),
resource_request_(std::move(resource_request)),
- download_id_(download_id),
+ is_new_download_(is_new_download),
render_process_id_(render_process_id),
render_frame_id_(render_frame_id),
site_url_(site_url),
@@ -144,6 +142,7 @@ void ResourceDownloader::Start(
download_url_parameters->GetSaveInfo()),
is_parallel_request, download_url_parameters->is_transient(),
download_url_parameters->fetch_error_body(),
+ download_url_parameters->follow_cross_origin_redirects(),
download_url_parameters->request_headers(),
download_url_parameters->request_origin(),
download_url_parameters->download_source(),
@@ -182,14 +181,14 @@ void ResourceDownloader::InterceptResponse(
false, /* is_parallel_request */
false, /* is_transient */
false, /* fetch_error_body */
+ true, /* follow_cross_origin_redirects */
download::DownloadUrlParameters::RequestHeadersType(),
std::string(), /* request_origin */
download::DownloadSource::NAVIGATION, std::move(url_chain));
// Simulate on the new URLLoaderClient calls that happened on the old client.
response->head.cert_status = cert_status;
- url_loader_client_->OnReceiveResponse(
- response->head, network::mojom::DownloadedTempFilePtr());
+ url_loader_client_->OnReceiveResponse(response->head);
// Bind the new client.
url_loader_client_binding_ =
@@ -200,7 +199,9 @@ void ResourceDownloader::InterceptResponse(
void ResourceDownloader::OnResponseStarted(
std::unique_ptr<DownloadCreateInfo> download_create_info,
mojom::DownloadStreamHandlePtr stream_handle) {
- download_create_info->download_id = download_id_;
+ download_create_info->request_handle.reset(new UrlDownloadRequestHandle(
+ weak_ptr_factory_.GetWeakPtr(), base::SequencedTaskRunnerHandle::Get()));
+ download_create_info->is_new_download = is_new_download_;
download_create_info->guid = guid_;
download_create_info->site_url = site_url_;
download_create_info->tab_url = tab_url_;
@@ -218,7 +219,22 @@ void ResourceDownloader::OnResponseStarted(
}
void ResourceDownloader::OnReceiveRedirect() {
- url_loader_->FollowRedirect(base::nullopt);
+ url_loader_->FollowRedirect(base::nullopt, base::nullopt);
+}
+
+void ResourceDownloader::OnResponseCompleted() {
+ Destroy();
+}
+
+void ResourceDownloader::CancelRequest() {
+ Destroy();
+}
+
+void ResourceDownloader::Destroy() {
+ delegate_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&UrlDownloadHandler::Delegate::OnUrlDownloadStopped,
+ delegate_, this));
}
} // namespace download
diff --git a/chromium/components/download/internal/common/resource_downloader.h b/chromium/components/download/internal/common/resource_downloader.h
index ce6eef63834..a3557f86d0a 100644
--- a/chromium/components/download/internal/common/resource_downloader.h
+++ b/chromium/components/download/internal/common/resource_downloader.h
@@ -31,7 +31,7 @@ class COMPONENTS_DOWNLOAD_EXPORT ResourceDownloader
const GURL& site_url,
const GURL& tab_url,
const GURL& tab_referrer_url,
- uint32_t download_id,
+ bool is_new_download,
bool is_parallel_request,
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
@@ -62,7 +62,7 @@ class COMPONENTS_DOWNLOAD_EXPORT ResourceDownloader
const GURL& site_url,
const GURL& tab_url,
const GURL& tab_referrer_url,
- uint32_t download_id,
+ bool is_new_download,
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
scoped_refptr<download::DownloadURLLoaderFactoryGetter>
url_loader_factory_getter);
@@ -73,6 +73,7 @@ class COMPONENTS_DOWNLOAD_EXPORT ResourceDownloader
std::unique_ptr<download::DownloadCreateInfo> download_create_info,
download::mojom::DownloadStreamHandlePtr stream_handle) override;
void OnReceiveRedirect() override;
+ void OnResponseCompleted() override;
private:
// Helper method to start the network request.
@@ -87,6 +88,12 @@ class COMPONENTS_DOWNLOAD_EXPORT ResourceDownloader
net::CertStatus cert_status,
network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints);
+ // UrlDownloadHandler implementations.
+ void CancelRequest() override;
+
+ // Ask the |delegate_| to destroy this object.
+ void Destroy();
+
base::WeakPtr<download::UrlDownloadHandler::Delegate> delegate_;
// The ResourceRequest for this object.
@@ -102,9 +109,8 @@ class COMPONENTS_DOWNLOAD_EXPORT ResourceDownloader
// URLLoader for sending out the request.
network::mojom::URLLoaderPtr url_loader_;
- // ID of the download, or download::DownloadItem::kInvalidId if this is a new
- // download.
- uint32_t download_id_;
+ // Whether this is a new download.
+ bool is_new_download_;
// GUID of the download, or empty if this is a new download.
std::string guid_;
diff --git a/chromium/components/download/internal/common/stream_handle_input_stream.cc b/chromium/components/download/internal/common/stream_handle_input_stream.cc
index 12a536b93bc..c6c0c7f7ee4 100644
--- a/chromium/components/download/internal/common/stream_handle_input_stream.cc
+++ b/chromium/components/download/internal/common/stream_handle_input_stream.cc
@@ -100,6 +100,11 @@ DownloadInterruptReason StreamHandleInputStream::GetCompletionStatus() {
void StreamHandleInputStream::OnStreamCompleted(
mojom::NetworkRequestStatus status) {
+ // This method could get called again when the URLLoader is being destroyed.
+ // However, if the response is already completed, don't set the
+ // |completion_status_| again.
+ if (is_response_completed_)
+ return;
// This can be called before or after data pipe is completely drained.
completion_status_ = ConvertMojoNetworkRequestStatusToInterruptReason(status);
is_response_completed_ = true;
diff --git a/chromium/components/download/internal/common/url_download_handler_factory.cc b/chromium/components/download/internal/common/url_download_handler_factory.cc
index 625f6da4ffd..9a3a5f4d3a9 100644
--- a/chromium/components/download/internal/common/url_download_handler_factory.cc
+++ b/chromium/components/download/internal/common/url_download_handler_factory.cc
@@ -34,8 +34,8 @@ class DefaultUrlDownloadHandlerFactory : public UrlDownloadHandlerFactory {
return UrlDownloadHandler::UniqueUrlDownloadHandlerPtr(
download::ResourceDownloader::BeginDownload(
delegate, std::move(params), std::move(request),
- std::move(url_loader_factory_getter), GURL(), GURL(), GURL(),
- download::DownloadItem::kInvalidId, true, task_runner)
+ std::move(url_loader_factory_getter), GURL(), GURL(), GURL(), true,
+ true, task_runner)
.release(),
base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get()));
}
diff --git a/chromium/components/download/internal/common/url_download_request_handle.cc b/chromium/components/download/internal/common/url_download_request_handle.cc
new file mode 100644
index 00000000000..7fa6bd84eb7
--- /dev/null
+++ b/chromium/components/download/internal/common/url_download_request_handle.cc
@@ -0,0 +1,47 @@
+// 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/download/public/common/url_download_request_handle.h"
+
+namespace download {
+
+UrlDownloadRequestHandle::UrlDownloadRequestHandle(
+ base::WeakPtr<UrlDownloadHandler> downloader,
+ scoped_refptr<base::SequencedTaskRunner> downloader_task_runner)
+ : downloader_(downloader),
+ downloader_task_runner_(downloader_task_runner) {}
+
+UrlDownloadRequestHandle::UrlDownloadRequestHandle(
+ UrlDownloadRequestHandle&& other)
+ : downloader_(std::move(other.downloader_)),
+ downloader_task_runner_(std::move(other.downloader_task_runner_)) {}
+
+UrlDownloadRequestHandle& UrlDownloadRequestHandle::operator=(
+ UrlDownloadRequestHandle&& other) {
+ downloader_ = std::move(other.downloader_);
+ downloader_task_runner_ = std::move(other.downloader_task_runner_);
+ return *this;
+}
+
+UrlDownloadRequestHandle::~UrlDownloadRequestHandle() = default;
+
+void UrlDownloadRequestHandle::PauseRequest() {
+ downloader_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&UrlDownloadHandler::PauseRequest, downloader_));
+}
+
+void UrlDownloadRequestHandle::ResumeRequest() {
+ downloader_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&UrlDownloadHandler::ResumeRequest, downloader_));
+}
+
+void UrlDownloadRequestHandle::CancelRequest(bool user_cancel) {
+ downloader_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&UrlDownloadHandler::CancelRequest, downloader_));
+}
+
+} // namespace download
diff --git a/chromium/components/download/public/background_service/download_service.h b/chromium/components/download/public/background_service/download_service.h
index 91793af507e..736666e0918 100644
--- a/chromium/components/download/public/background_service/download_service.h
+++ b/chromium/components/download/public/background_service/download_service.h
@@ -25,7 +25,7 @@ class ServiceConfig;
struct DownloadParams;
struct SchedulingParams;
-using TaskFinishedCallback = base::Callback<void(bool)>;
+using TaskFinishedCallback = base::OnceCallback<void(bool)>;
// A service responsible for helping facilitate the scheduling and downloading
// of file content from the web. See |DownloadParams| for more details on the
@@ -62,7 +62,7 @@ class DownloadService : public KeyedService {
// or OnStopScheduledTask is invoked by the system. Do not call this method
// directly.
virtual void OnStartScheduledTask(DownloadTaskType task_type,
- const TaskFinishedCallback& callback) = 0;
+ TaskFinishedCallback callback) = 0;
// Callback method to run by the service if the system decides to stop the
// task. Returns true if the task needs to be rescheduled. Any pending
diff --git a/chromium/components/download/public/background_service/task_scheduler.h b/chromium/components/download/public/background_service/task_scheduler.h
index 26afbf49699..409af4b53b3 100644
--- a/chromium/components/download/public/background_service/task_scheduler.h
+++ b/chromium/components/download/public/background_service/task_scheduler.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_DOWNLOAD_PUBLIC_BACKGROUND_SERVICE_TASK_SCHEDULER_H_
#define COMPONENTS_DOWNLOAD_PUBLIC_BACKGROUND_SERVICE_TASK_SCHEDULER_H_
+#include <stdint.h>
+
#include "components/download/public/background_service/download_task_types.h"
namespace download {
@@ -23,8 +25,8 @@ class TaskScheduler {
bool require_unmetered_network,
bool require_charging,
int optimal_battery_percentage,
- long window_start_time_seconds,
- long window_end_time_seconds) = 0;
+ int64_t window_start_time_seconds,
+ int64_t window_end_time_seconds) = 0;
// Cancels a pre-scheduled task of type |task_type|.
virtual void CancelTask(DownloadTaskType task_type) = 0;
diff --git a/chromium/components/download/public/common/BUILD.gn b/chromium/components/download/public/common/BUILD.gn
index 16f35f36623..5c8c8717680 100644
--- a/chromium/components/download/public/common/BUILD.gn
+++ b/chromium/components/download/public/common/BUILD.gn
@@ -41,6 +41,7 @@ component("public") {
"download_save_info.cc",
"download_save_info.h",
"download_source.h",
+ "download_start_observer.h",
"download_stats.h",
"download_task_runner.h",
"download_ukm_helper.h",
@@ -56,6 +57,7 @@ component("public") {
"resume_mode.h",
"stream_handle_input_stream.h",
"url_download_handler_factory.h",
+ "url_download_request_handle.h",
]
configs += [ ":components_download_implementation" ]
@@ -66,7 +68,7 @@ component("public") {
deps = [
"//base",
- "//components/download/downloader/in_progress",
+ "//components/download/database",
"//components/download/internal/common:internal",
"//crypto",
"//net",
@@ -78,7 +80,7 @@ component("public") {
allow_circular_includes_from = [
"//components/download/internal/common:internal",
- "//components/download/downloader/in_progress",
+ "//components/download/database",
]
}
diff --git a/chromium/components/download/public/common/download_create_info.h b/chromium/components/download/public/common/download_create_info.h
index 70ea821d540..7c82695416b 100644
--- a/chromium/components/download/public/common/download_create_info.h
+++ b/chromium/components/download/public/common/download_create_info.h
@@ -23,6 +23,7 @@
#include "components/download/public/common/download_source.h"
#include "components/download/public/common/download_url_parameters.h"
#include "net/http/http_response_info.h"
+#include "net/url_request/url_request.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "ui/base/page_transition_types.h"
#include "url/gurl.h"
@@ -41,21 +42,21 @@ struct COMPONENTS_DOWNLOAD_EXPORT DownloadCreateInfo {
DownloadCreateInfo();
~DownloadCreateInfo();
+ bool is_new_download;
+
// The URL from which we are downloading. This is the final URL after any
// redirection by the server for |url_chain|.
const GURL& url() const;
- // The ID of the download. (Deprecated)
- uint32_t download_id;
-
// The unique identifier for the download.
std::string guid;
// The chain of redirects that leading up to and including the final URL.
std::vector<GURL> url_chain;
- // The URL that referred us.
+ // The URL and referrer policy that referred us.
GURL referrer_url;
+ net::URLRequest::ReferrerPolicy referrer_policy;
// Site URL for the site instance that initiated the download.
GURL site_url;
diff --git a/chromium/components/download/public/common/download_interrupt_reason_values.h b/chromium/components/download/public/common/download_interrupt_reason_values.h
index 02cd84e1aac..5b8da469fe9 100644
--- a/chromium/components/download/public/common/download_interrupt_reason_values.h
+++ b/chromium/components/download/public/common/download_interrupt_reason_values.h
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// no-include-guard-because-multiply-included
+
// Note that the embedder is welcome to persist these values across
// invocations of the browser, and possibly across browser versions.
// Thus individual errors may be deprecated and new errors added, but
@@ -122,6 +124,9 @@ INTERRUPT_REASON(SERVER_UNREACHABLE, 37)
// Otherwise, it is treated as finished.
INTERRUPT_REASON(SERVER_CONTENT_LENGTH_MISMATCH, 38)
+// An unexpected cross-origin redirect happened.
+INTERRUPT_REASON(SERVER_CROSS_ORIGIN_REDIRECT, 39)
+
// User input.
// The user canceled the download.
diff --git a/chromium/components/download/public/common/download_item_impl.h b/chromium/components/download/public/common/download_item_impl.h
index bb8ae8623e1..7c5ed9eb615 100644
--- a/chromium/components/download/public/common/download_item_impl.h
+++ b/chromium/components/download/public/common/download_item_impl.h
@@ -344,6 +344,8 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImpl
int64_t total_bytes,
std::unique_ptr<crypto::SecureHash> hash_state) override;
+ void SetDelegate(DownloadItemImplDelegate* delegate);
+
private:
// Fine grained states of a download.
//
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 863786a93e3..6a6f3dca0be 100644
--- a/chromium/components/download/public/common/download_item_impl_delegate.h
+++ b/chromium/components/download/public/common/download_item_impl_delegate.h
@@ -78,7 +78,6 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImplDelegate {
// Called when an interrupted download is resumed.
virtual void ResumeInterruptedDownload(
std::unique_ptr<DownloadUrlParameters> params,
- uint32_t id,
const GURL& site_url);
// Update the persistent store with our information.
@@ -98,9 +97,6 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadItemImplDelegate {
// DownloadItem.
virtual void DownloadRemoved(DownloadItemImpl* download);
- // Assert consistent state for delgate object at various transitions.
- virtual void AssertStateConsistent(DownloadItemImpl* download) const;
-
// Called when the download is interrupted.
virtual void DownloadInterrupted(DownloadItemImpl* download);
diff --git a/chromium/components/download/public/common/download_response_handler.h b/chromium/components/download/public/common/download_response_handler.h
index 0105074895c..09487452e32 100644
--- a/chromium/components/download/public/common/download_response_handler.h
+++ b/chromium/components/download/public/common/download_response_handler.h
@@ -26,13 +26,14 @@ namespace download {
class COMPONENTS_DOWNLOAD_EXPORT DownloadResponseHandler
: public network::mojom::URLLoaderClient {
public:
- // Class for handling the stream once response starts.
+ // Class for handling the stream response.
class Delegate {
public:
virtual void OnResponseStarted(
std::unique_ptr<DownloadCreateInfo> download_create_info,
mojom::DownloadStreamHandlePtr stream_handle) = 0;
virtual void OnReceiveRedirect() = 0;
+ virtual void OnResponseCompleted() = 0;
};
DownloadResponseHandler(
@@ -42,6 +43,7 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadResponseHandler
bool is_parallel_request,
bool is_transient,
bool fetch_error_body,
+ bool follow_cross_origin_redirects,
const DownloadUrlParameters::RequestHeadersType& request_headers,
const std::string& request_origin,
DownloadSource download_source,
@@ -49,12 +51,9 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadResponseHandler
~DownloadResponseHandler() override;
// network::mojom::URLLoaderClient
- void OnReceiveResponse(
- const network::ResourceResponseHead& head,
- network::mojom::DownloadedTempFilePtr downloaded_file) override;
+ void OnReceiveResponse(const network::ResourceResponseHead& head) override;
void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
const network::ResourceResponseHead& head) override;
- void OnDataDownloaded(int64_t data_length, int64_t encoded_length) override;
void OnUploadProgress(int64_t current_position,
int64_t total_size,
OnUploadProgressCallback callback) override;
@@ -82,8 +81,11 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadResponseHandler
std::vector<GURL> url_chain_;
std::string method_;
GURL referrer_;
+ net::URLRequest::ReferrerPolicy referrer_policy_;
bool is_transient_;
bool fetch_error_body_;
+ bool follow_cross_origin_redirects_;
+ url::Origin first_origin_;
DownloadUrlParameters::RequestHeadersType request_headers_;
std::string request_origin_;
DownloadSource download_source_;
diff --git a/chromium/components/download/public/common/download_save_info.cc b/chromium/components/download/public/common/download_save_info.cc
index 075ba24545e..2b25b628c44 100644
--- a/chromium/components/download/public/common/download_save_info.cc
+++ b/chromium/components/download/public/common/download_save_info.cc
@@ -9,19 +9,10 @@ namespace download {
// static
const int64_t DownloadSaveInfo::kLengthFullContent = 0;
-DownloadSaveInfo::DownloadSaveInfo()
- : offset(0), length(kLengthFullContent), prompt_for_save_location(false) {}
+DownloadSaveInfo::DownloadSaveInfo() = default;
-DownloadSaveInfo::~DownloadSaveInfo() {}
+DownloadSaveInfo::~DownloadSaveInfo() = default;
-DownloadSaveInfo::DownloadSaveInfo(DownloadSaveInfo&& that)
- : file_path(std::move(that.file_path)),
- suggested_name(std::move(that.suggested_name)),
- file(std::move(that.file)),
- offset(that.offset),
- length(that.length),
- hash_state(std::move(that.hash_state)),
- hash_of_partial_file(std::move(that.hash_of_partial_file)),
- prompt_for_save_location(that.prompt_for_save_location) {}
+DownloadSaveInfo::DownloadSaveInfo(DownloadSaveInfo&& that) = default;
} // namespace download
diff --git a/chromium/components/download/public/common/download_save_info.h b/chromium/components/download/public/common/download_save_info.h
index b43d0f9746c..a1e00c8755a 100644
--- a/chromium/components/download/public/common/download_save_info.h
+++ b/chromium/components/download/public/common/download_save_info.h
@@ -42,15 +42,14 @@ struct COMPONENTS_DOWNLOAD_EXPORT DownloadSaveInfo {
// If valid, contains the source data stream for the file contents.
base::File file;
- // The file offset at which to start the download. May be 0.
- int64_t offset;
+ // The file offset at which to start the download.
+ int64_t offset = 0;
- // The number of the bytes to download from |offset|. Set to
- // |kLengthFullContent| by default.
+ // The number of the bytes to download from |offset|.
// Ask to retrieve segment of the download file when length is greater than 0.
// Request the rest of the file starting from |offset|, when length is
// |kLengthFullContent|.
- int64_t length;
+ int64_t length = kLengthFullContent;
// The state of the hash. If specified, this hash state must indicate the
// state of the partial file for the first |offset| bytes.
@@ -66,8 +65,7 @@ struct COMPONENTS_DOWNLOAD_EXPORT DownloadSaveInfo {
// the user will be prompted for a location to save the download. Otherwise,
// the location will be determined automatically using |file_path| as a
// basis if |file_path| is not empty.
- // |prompt_for_save_location| defaults to false.
- bool prompt_for_save_location;
+ bool prompt_for_save_location = false;
private:
DISALLOW_COPY_AND_ASSIGN(DownloadSaveInfo);
diff --git a/chromium/components/download/public/common/download_start_observer.h b/chromium/components/download/public/common/download_start_observer.h
new file mode 100644
index 00000000000..eec41e8dbfb
--- /dev/null
+++ b/chromium/components/download/public/common/download_start_observer.h
@@ -0,0 +1,24 @@
+// 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_DOWNLOAD_PUBLIC_COMMON_DOWNLOAD_START_OBSERVER_H_
+#define COMPONENTS_DOWNLOAD_PUBLIC_COMMON_DOWNLOAD_START_OBSERVER_H_
+
+#include "components/download/public/common/download_export.h"
+
+namespace download {
+
+class DownloadItem;
+
+// Class for listening to download start event.
+class COMPONENTS_DOWNLOAD_EXPORT DownloadStartObserver {
+ public:
+ // Called when a download is started, either from a new download or
+ // a resumed download. Must be called on the UI thread.
+ virtual void OnDownloadStarted(DownloadItem* download_item) = 0;
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_PUBLIC_COMMON_DOWNLOAD_START_OBSERVER_H_
diff --git a/chromium/components/download/public/common/download_ukm_helper.h b/chromium/components/download/public/common/download_ukm_helper.h
index bdd37d895e0..97ce4597edd 100644
--- a/chromium/components/download/public/common/download_ukm_helper.h
+++ b/chromium/components/download/public/common/download_ukm_helper.h
@@ -12,7 +12,6 @@
#include "components/download/public/common/download_source.h"
#include "components/download/public/common/resume_mode.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
-#include "url/gurl.h"
namespace download {
@@ -51,11 +50,6 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadUkmHelper {
const base::TimeDelta& time_since_start,
int64_t bytes_wasted);
- // Friended Helper for recording main frame URLs to UKM.
- static void UpdateSourceURL(ukm::UkmRecorder* ukm_recorder,
- ukm::SourceId source_id,
- const GURL& url);
-
private:
DownloadUkmHelper();
~DownloadUkmHelper();
diff --git a/chromium/components/download/public/common/download_url_parameters.cc b/chromium/components/download/public/common/download_url_parameters.cc
index d30ea271cbb..7e355144199 100644
--- a/chromium/components/download/public/common/download_url_parameters.cc
+++ b/chromium/components/download/public/common/download_url_parameters.cc
@@ -38,6 +38,7 @@ DownloadUrlParameters::DownloadUrlParameters(
url_request_context_getter_(url_request_context_getter),
url_(url),
do_not_prompt_for_login_(false),
+ follow_cross_origin_redirects_(true),
fetch_error_body_(false),
transient_(false),
traffic_annotation_(traffic_annotation),
diff --git a/chromium/components/download/public/common/download_url_parameters.h b/chromium/components/download/public/common/download_url_parameters.h
index a0ac97baef3..d9f0455239b 100644
--- a/chromium/components/download/public/common/download_url_parameters.h
+++ b/chromium/components/download/public/common/download_url_parameters.h
@@ -211,6 +211,13 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadUrlParameters {
do_not_prompt_for_login_ = do_not_prompt;
}
+ // If |follow_cross_origin_redirects| is true, we will follow cross origin
+ // redirects while downloading, otherwise, we'll attempt to navigate to the
+ // URL.
+ void set_follow_cross_origin_redirects(bool follow_cross_origin_redirects) {
+ follow_cross_origin_redirects_ = follow_cross_origin_redirects;
+ }
+
// Sets whether to download the response body even if the server returns
// non-successful HTTP response code, like "HTTP NOT FOUND".
void set_fetch_error_body(bool fetch_error_body) {
@@ -284,6 +291,9 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadUrlParameters {
bool prompt() const { return save_info_.prompt_for_save_location; }
const GURL& url() const { return url_; }
bool do_not_prompt_for_login() const { return do_not_prompt_for_login_; }
+ bool follow_cross_origin_redirects() const {
+ return follow_cross_origin_redirects_;
+ }
bool fetch_error_body() const { return fetch_error_body_; }
bool is_transient() const { return transient_; }
std::string guid() const { return guid_; }
@@ -321,6 +331,7 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadUrlParameters {
DownloadSaveInfo save_info_;
GURL url_;
bool do_not_prompt_for_login_;
+ bool follow_cross_origin_redirects_;
bool fetch_error_body_;
bool transient_;
std::string guid_;
diff --git a/chromium/components/download/public/common/download_utils.h b/chromium/components/download/public/common/download_utils.h
index c3e4b9f246b..b7d56e4b8cd 100644
--- a/chromium/components/download/public/common/download_utils.h
+++ b/chromium/components/download/public/common/download_utils.h
@@ -5,7 +5,8 @@
#ifndef COMPONENTS_DOWNLOAD_PUBLIC_COMMON_DOWNLOAD_UTILS_H_
#define COMPONENTS_DOWNLOAD_PUBLIC_COMMON_DOWNLOAD_UTILS_H_
-#include "components/download/downloader/in_progress/download_entry.h"
+#include "components/download/database/download_db_entry.h"
+#include "components/download/database/in_progress/download_entry.h"
#include "components/download/public/common/download_export.h"
#include "components/download/public/common/download_interrupt_reasons.h"
#include "components/download/public/common/download_item.h"
@@ -68,6 +69,18 @@ COMPONENTS_DOWNLOAD_EXPORT DownloadEntry CreateDownloadEntryFromItem(
bool fetch_error_body,
const DownloadUrlParameters::RequestHeadersType& request_headers);
+// Helper functions for DownloadItem -> DownloadDBEntry for DownloadDB.
+COMPONENTS_DOWNLOAD_EXPORT DownloadDBEntry CreateDownloadDBEntryFromItem(
+ const DownloadItem& item,
+ DownloadSource download_source,
+ bool fetch_error_body,
+ const DownloadUrlParameters::RequestHeadersType& request_headers);
+
+// Helper function to convert DownloadDBEntry to DownloadEntry.
+// TODO(qinmin): remove this function after DownloadEntry is deprecated.
+COMPONENTS_DOWNLOAD_EXPORT base::Optional<DownloadEntry>
+CreateDownloadEntryFromDownloadDBEntry(base::Optional<DownloadDBEntry> entry);
+
COMPONENTS_DOWNLOAD_EXPORT uint64_t GetUniqueDownloadId();
} // namespace download
diff --git a/chromium/components/download/public/common/in_progress_download_manager.h b/chromium/components/download/public/common/in_progress_download_manager.h
index 9dba8c67b1d..aea103385da 100644
--- a/chromium/components/download/public/common/in_progress_download_manager.h
+++ b/chromium/components/download/public/common/in_progress_download_manager.h
@@ -27,16 +27,21 @@ struct ResourceResponse;
namespace download {
+class DownloadDBCache;
+class DownloadStartObserver;
class DownloadURLLoaderFactoryGetter;
class DownloadUrlParameters;
class InProgressCache;
+struct DownloadDBEntry;
// Manager for handling all active downloads.
class COMPONENTS_DOWNLOAD_EXPORT InProgressDownloadManager
: public UrlDownloadHandler::Delegate,
public DownloadItemImplDelegate {
public:
- using DownloadIdCallback = base::RepeatingCallback<void(uint32_t)>;
+ using StartDownloadItemCallback =
+ base::OnceCallback<void(std::unique_ptr<DownloadCreateInfo> info,
+ DownloadItemImpl*)>;
// Class to be notified when download starts/stops.
class COMPONENTS_DOWNLOAD_EXPORT Delegate {
@@ -46,39 +51,33 @@ class COMPONENTS_DOWNLOAD_EXPORT InProgressDownloadManager
virtual bool InterceptDownload(
const DownloadCreateInfo& download_create_info) = 0;
- // Called to get an ID for a new download. |callback| may be called
- // synchronously.
- virtual void GetNextId(const DownloadIdCallback& callback) = 0;
-
// Gets the default download directory.
virtual base::FilePath GetDefaultDownloadDirectory() = 0;
- // Gets the download item for the given id.
+ // Gets the download item for the given |download_create_info|.
// TODO(qinmin): remove this method and let InProgressDownloadManager
// create the DownloadItem from in-progress cache.
- virtual DownloadItemImpl* GetDownloadItem(
- uint32_t id,
- bool new_download,
- const DownloadCreateInfo& download_create_info) = 0;
+ virtual void StartDownloadItem(
+ std::unique_ptr<DownloadCreateInfo> info,
+ const DownloadUrlParameters::OnStartedCallback& on_started,
+ StartDownloadItemCallback callback) = 0;
// Gets the URLRequestContextGetter for sending requests.
// TODO(qinmin): remove this once network service is fully enabled.
virtual net::URLRequestContextGetter* GetURLRequestContextGetter(
const DownloadCreateInfo& download_create_info) = 0;
-
- // Called when a new download is started.
- virtual void OnNewDownloadStarted(DownloadItem* download) = 0;
};
using IsOriginSecureCallback = base::RepeatingCallback<bool(const GURL&)>;
InProgressDownloadManager(Delegate* delegate,
+ const base::FilePath& metadata_cache_dir,
const IsOriginSecureCallback& is_origin_secure_cb);
~InProgressDownloadManager() override;
// Called to start a download.
void BeginDownload(
std::unique_ptr<DownloadUrlParameters> params,
scoped_refptr<DownloadURLLoaderFactoryGetter> url_loader_factory_getter,
- uint32_t download_id,
+ bool is_new_download,
const GURL& site_url,
const GURL& tab_url,
const GURL& tab_referrer_url);
@@ -97,9 +96,6 @@ class COMPONENTS_DOWNLOAD_EXPORT InProgressDownloadManager
network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
scoped_refptr<DownloadURLLoaderFactoryGetter> url_loader_factory_getter);
- void Initialize(const base::FilePath& metadata_cache_dir,
- base::OnceClosure callback);
-
void StartDownload(
std::unique_ptr<DownloadCreateInfo> info,
std::unique_ptr<InputStream> stream,
@@ -110,19 +106,50 @@ class COMPONENTS_DOWNLOAD_EXPORT InProgressDownloadManager
void ShutDown();
// DownloadItemImplDelegate implementations.
+ void DetermineDownloadTarget(DownloadItemImpl* download,
+ const DownloadTargetCallback& callback) override;
void ResumeInterruptedDownload(std::unique_ptr<DownloadUrlParameters> params,
- uint32_t id,
const GURL& site_url) override;
+ bool ShouldOpenDownload(DownloadItemImpl* item,
+ const ShouldOpenDownloadCallback& callback) override;
base::Optional<DownloadEntry> GetInProgressEntry(
DownloadItemImpl* download) override;
void ReportBytesWasted(DownloadItemImpl* download) override;
+ // Called to remove an in-progress download.
+ void RemoveInProgressDownload(const std::string& guid);
+
+ // Called to retrieve an in-progress download.
+ DownloadItemImpl* GetInProgressDownload(const std::string& guid);
+
+ // Run |on_initialized_cb| once this object is initialized.
+ void NotifyWhenInitialized(base::OnceClosure on_initialized_cb);
+
+ void set_download_start_observer(DownloadStartObserver* observer) {
+ download_start_observer_ = observer;
+ }
+
+ // Called to get all in-progress DownloadItemImpl.
+ // TODO(qinmin): remove this method once InProgressDownloadManager owns
+ // all in-progress downloads.
+ virtual std::vector<std::unique_ptr<download::DownloadItemImpl>>
+ TakeInProgressDownloads();
+
void set_file_factory(std::unique_ptr<DownloadFileFactory> file_factory) {
file_factory_ = std::move(file_factory);
}
DownloadFileFactory* file_factory() { return file_factory_.get(); }
+ void set_url_loader_factory_getter(
+ scoped_refptr<DownloadURLLoaderFactoryGetter> url_loader_factory_getter) {
+ url_loader_factory_getter_ = std::move(url_loader_factory_getter);
+ }
+
+ void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+
private:
+ void Initialize(const base::FilePath& metadata_cache_dir);
+
// UrlDownloadHandler::Delegate implementations.
void OnUrlDownloadStarted(
std::unique_ptr<DownloadCreateInfo> download_create_info,
@@ -133,14 +160,18 @@ class COMPONENTS_DOWNLOAD_EXPORT InProgressDownloadManager
void OnUrlDownloadHandlerCreated(
UrlDownloadHandler::UniqueUrlDownloadHandlerPtr downloader) override;
- // Start a download with given ID.
- void StartDownloadWithId(
- std::unique_ptr<DownloadCreateInfo> info,
+ // Called when the object is initialized.
+ void OnInitialized(std::unique_ptr<std::vector<DownloadDBEntry>> entries);
+
+ // Start a DownloadItemImpl.
+ void StartDownloadWithItem(
std::unique_ptr<InputStream> stream,
scoped_refptr<DownloadURLLoaderFactoryGetter> url_loader_factory_getter,
- const DownloadUrlParameters::OnStartedCallback& on_started,
- bool new_download,
- uint32_t id);
+ std::unique_ptr<DownloadCreateInfo> info,
+ DownloadItemImpl* download);
+
+ // Whether |download_db_cache_| is initialized.
+ bool is_initialized_;
// Active download handlers.
std::vector<UrlDownloadHandler::UniqueUrlDownloadHandlerPtr>
@@ -155,12 +186,29 @@ class COMPONENTS_DOWNLOAD_EXPORT InProgressDownloadManager
// Cache for storing metadata about in progress downloads.
std::unique_ptr<InProgressCache> download_metadata_cache_;
+ // Cache for DownloadDB.
+ std::unique_ptr<DownloadDBCache> download_db_cache_;
+
// listens to information about in-progress download items.
std::unique_ptr<DownloadItem::Observer> in_progress_download_observer_;
+ // Observer to notify when a download starts.
+ DownloadStartObserver* download_start_observer_;
+
+ // Callbacks to call once this object is initialized.
+ std::vector<std::unique_ptr<base::OnceClosure>> on_initialized_callbacks_;
+
// callback to check if an origin is secure.
IsOriginSecureCallback is_origin_secure_cb_;
+ // A list of in-progress download items, could be null if DownloadManagerImpl
+ // is managing all downloads.
+ std::vector<std::unique_ptr<DownloadItemImpl>> in_progress_downloads_;
+
+ // URLLoaderFactoryGetter for issuing network request when DownloadMangerImpl
+ // is not available.
+ scoped_refptr<DownloadURLLoaderFactoryGetter> url_loader_factory_getter_;
+
base::WeakPtrFactory<InProgressDownloadManager> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(InProgressDownloadManager);
diff --git a/chromium/components/download/public/common/url_download_handler.h b/chromium/components/download/public/common/url_download_handler.h
index ce059d92313..79725204200 100644
--- a/chromium/components/download/public/common/url_download_handler.h
+++ b/chromium/components/download/public/common/url_download_handler.h
@@ -40,6 +40,15 @@ class COMPONENTS_DOWNLOAD_EXPORT UrlDownloadHandler {
UrlDownloadHandler() = default;
virtual ~UrlDownloadHandler() = default;
+ // Called on the io thread to pause the url request.
+ virtual void PauseRequest() {}
+
+ // Called on the io thread to resume the url request.
+ virtual void ResumeRequest() {}
+
+ // Called on the io thread to cancel the url request.
+ virtual void CancelRequest() {}
+
DISALLOW_COPY_AND_ASSIGN(UrlDownloadHandler);
};
diff --git a/chromium/components/download/public/common/url_download_request_handle.h b/chromium/components/download/public/common/url_download_request_handle.h
new file mode 100644
index 00000000000..46c77564efa
--- /dev/null
+++ b/chromium/components/download/public/common/url_download_request_handle.h
@@ -0,0 +1,40 @@
+// 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_DOWNLOAD_PUBLIC_COMMON_URL_DOWNLOAD_REQUEST_HANDLE_H_
+#define COMPONENTS_DOWNLOAD_PUBLIC_COMMON_URL_DOWNLOAD_REQUEST_HANDLE_H_
+
+#include "base/memory/weak_ptr.h"
+#include "components/download/public/common/download_export.h"
+#include "components/download/public/common/download_request_handle_interface.h"
+#include "components/download/public/common/url_download_handler.h"
+
+namespace download {
+
+// Implementation of the DownloadRequestHandleInterface to handle url download.
+class COMPONENTS_DOWNLOAD_EXPORT UrlDownloadRequestHandle
+ : public DownloadRequestHandleInterface {
+ public:
+ UrlDownloadRequestHandle(
+ base::WeakPtr<UrlDownloadHandler> downloader,
+ scoped_refptr<base::SequencedTaskRunner> downloader_task_runner);
+ UrlDownloadRequestHandle(UrlDownloadRequestHandle&& other);
+ UrlDownloadRequestHandle& operator=(UrlDownloadRequestHandle&& other);
+ ~UrlDownloadRequestHandle() override;
+
+ // DownloadRequestHandleInterface
+ void PauseRequest() override;
+ void ResumeRequest() override;
+ void CancelRequest(bool user_cancel) override;
+
+ private:
+ base::WeakPtr<UrlDownloadHandler> downloader_;
+ scoped_refptr<base::SequencedTaskRunner> downloader_task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(UrlDownloadRequestHandle);
+};
+
+} // namespace download
+
+#endif // COMPONENTS_DOWNLOAD_PUBLIC_COMMON_URL_DOWNLOAD_REQUEST_HANDLE_H_
diff --git a/chromium/components/download/quarantine/quarantine_win_unittest.cc b/chromium/components/download/quarantine/quarantine_win_unittest.cc
index c169a66996f..160f2e4dc68 100644
--- a/chromium/components/download/quarantine/quarantine_win_unittest.cc
+++ b/chromium/components/download/quarantine/quarantine_win_unittest.cc
@@ -10,7 +10,7 @@
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_file_util.h"
#include "components/download/quarantine/quarantine.h"
#include "net/base/filename_util.h"
diff --git a/chromium/components/drive/BUILD.gn b/chromium/components/drive/BUILD.gn
index 62924eff032..2d88976a45a 100644
--- a/chromium/components/drive/BUILD.gn
+++ b/chromium/components/drive/BUILD.gn
@@ -166,8 +166,13 @@ if (is_chromeos) {
"chromeos/sync/remove_performer.h",
"chromeos/sync_client.cc",
"chromeos/sync_client.h",
+ "chromeos/team_drive.cc",
+ "chromeos/team_drive.h",
+ "chromeos/team_drive_change_list_loader.cc",
+ "chromeos/team_drive_change_list_loader.h",
"chromeos/team_drive_list_loader.cc",
"chromeos/team_drive_list_loader.h",
+ "chromeos/team_drive_list_observer.h",
]
deps = [
":drive",
diff --git a/chromium/components/web_contents_delegate_android/BUILD.gn b/chromium/components/embedder_support/android/BUILD.gn
index 7c83085d8fe..c7128e1df42 100644
--- a/chromium/components/web_contents_delegate_android/BUILD.gn
+++ b/chromium/components/embedder_support/android/BUILD.gn
@@ -1,10 +1,54 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# 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.
import("//build/config/android/rules.gni")
-java_strings_grd("web_contents_delegate_android_strings_grd") {
+static_library("view") {
+ sources = [
+ "view/content_view_render_view.cc",
+ "view/content_view_render_view.h",
+ ]
+
+ deps = [
+ ":view_jni_headers",
+ "//base",
+ "//content/public/browser",
+ "//ui/android",
+ "//ui/base",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ ]
+}
+
+android_library("content_view_java") {
+ deps = [
+ "//base:base_java",
+ "//content/public/android:content_java",
+ "//ui/android:ui_java",
+ ]
+ java_files = [
+ "java/src/org/chromium/components/embedder_support/view/ContentView.java",
+ ]
+}
+
+android_library("view_java") {
+ deps = [
+ "//base:base_java",
+ "//content/public/android:content_java",
+ "//ui/android:ui_java",
+ ]
+ java_files = [ "java/src/org/chromium/components/embedder_support/view/ContentViewRenderView.java" ]
+}
+
+generate_jni("view_jni_headers") {
+ sources = [
+ "java/src/org/chromium/components/embedder_support/view/ContentViewRenderView.java",
+ ]
+ jni_package = "view"
+}
+
+java_strings_grd("web_contents_delegate_strings_grd") {
grd_file = "java/strings/web_contents_delegate_android_strings.grd"
outputs = [
"values-am/web_contents_delegate_android_strings.xml",
@@ -54,16 +98,16 @@ java_strings_grd("web_contents_delegate_android_strings_grd") {
]
}
-static_library("web_contents_delegate_android") {
+static_library("web_contents_delegate") {
sources = [
- "color_chooser_android.cc",
- "color_chooser_android.h",
- "web_contents_delegate_android.cc",
- "web_contents_delegate_android.h",
+ "delegate/color_chooser_android.cc",
+ "delegate/color_chooser_android.h",
+ "delegate/web_contents_delegate_android.cc",
+ "delegate/web_contents_delegate_android.h",
]
deps = [
- ":web_contents_delegate_android_jni_headers",
+ ":web_contents_delegate_jni_headers",
"//base",
"//content/public/browser",
"//content/public/common",
@@ -76,39 +120,39 @@ static_library("web_contents_delegate_android") {
]
}
-android_resources("web_contents_delegate_android_java_resources") {
- custom_package = "org.chromium.components.web_contents_delegate_android"
+android_resources("web_contents_delegate_java_resources") {
+ custom_package = "org.chromium.components.embedder_support.delegate"
resource_dirs = [ "java/res" ]
deps = [
- ":web_contents_delegate_android_strings_grd",
+ ":web_contents_delegate_strings_grd",
]
}
-android_library("web_contents_delegate_android_java") {
+android_library("web_contents_delegate_java") {
deps = [
- ":web_contents_delegate_android_java_resources",
+ ":web_contents_delegate_java_resources",
"//base:base_java",
"//content/public/android:content_java",
"//ui/android:ui_java",
]
java_files = [
- "java/src/org/chromium/components/web_contents_delegate_android/ColorChooserAndroid.java",
- "java/src/org/chromium/components/web_contents_delegate_android/ColorPickerAdvanced.java",
- "java/src/org/chromium/components/web_contents_delegate_android/ColorPickerAdvancedComponent.java",
- "java/src/org/chromium/components/web_contents_delegate_android/ColorPickerDialog.java",
- "java/src/org/chromium/components/web_contents_delegate_android/ColorPickerMoreButton.java",
- "java/src/org/chromium/components/web_contents_delegate_android/ColorPickerSimple.java",
- "java/src/org/chromium/components/web_contents_delegate_android/ColorSuggestion.java",
- "java/src/org/chromium/components/web_contents_delegate_android/ColorSuggestionListAdapter.java",
- "java/src/org/chromium/components/web_contents_delegate_android/WebContentsDelegateAndroid.java",
- "java/src/org/chromium/components/web_contents_delegate_android/OnColorChangedListener.java",
+ "java/src/org/chromium/components/embedder_support/delegate/ColorChooserAndroid.java",
+ "java/src/org/chromium/components/embedder_support/delegate/ColorPickerAdvanced.java",
+ "java/src/org/chromium/components/embedder_support/delegate/ColorPickerAdvancedComponent.java",
+ "java/src/org/chromium/components/embedder_support/delegate/ColorPickerDialog.java",
+ "java/src/org/chromium/components/embedder_support/delegate/ColorPickerMoreButton.java",
+ "java/src/org/chromium/components/embedder_support/delegate/ColorPickerSimple.java",
+ "java/src/org/chromium/components/embedder_support/delegate/ColorSuggestion.java",
+ "java/src/org/chromium/components/embedder_support/delegate/ColorSuggestionListAdapter.java",
+ "java/src/org/chromium/components/embedder_support/delegate/WebContentsDelegateAndroid.java",
+ "java/src/org/chromium/components/embedder_support/delegate/OnColorChangedListener.java",
]
}
-generate_jni("web_contents_delegate_android_jni_headers") {
+generate_jni("web_contents_delegate_jni_headers") {
sources = [
- "java/src/org/chromium/components/web_contents_delegate_android/ColorChooserAndroid.java",
- "java/src/org/chromium/components/web_contents_delegate_android/WebContentsDelegateAndroid.java",
+ "java/src/org/chromium/components/embedder_support/delegate/ColorChooserAndroid.java",
+ "java/src/org/chromium/components/embedder_support/delegate/WebContentsDelegateAndroid.java",
]
jni_package = "web_contents_delegate_android"
}
diff --git a/chromium/components/web_contents_delegate_android/java/DEPS b/chromium/components/embedder_support/android/DEPS
index f20cebbb3be..0e8e2ccd9ba 100644
--- a/chromium/components/web_contents_delegate_android/java/DEPS
+++ b/chromium/components/embedder_support/android/DEPS
@@ -2,5 +2,10 @@ include_rules = [
"-content/public/android/java",
"+content/public/android/java/src/org/chromium/content_public",
- "+ui/android/java",
+ "+cc",
+ "+content/public/browser",
+ "+jni",
+ "+ui/android",
+ "+ui/base",
+ "+ui/gfx"
]
diff --git a/chromium/components/content_view/OWNERS b/chromium/components/embedder_support/android/OWNERS
index 7bf23a78474..7bf23a78474 100644
--- a/chromium/components/content_view/OWNERS
+++ b/chromium/components/embedder_support/android/OWNERS
diff --git a/chromium/components/embedder_support/android/README b/chromium/components/embedder_support/android/README
new file mode 100644
index 00000000000..27b915da595
--- /dev/null
+++ b/chromium/components/embedder_support/android/README
@@ -0,0 +1,2 @@
+Collection of Android libraries that provides embedders with
+typical implementations of interfaces of content layer.
diff --git a/chromium/components/web_contents_delegate_android/DEPS b/chromium/components/embedder_support/android/delegate/DEPS
index 6de2a9d26d8..6de2a9d26d8 100644
--- a/chromium/components/web_contents_delegate_android/DEPS
+++ b/chromium/components/embedder_support/android/delegate/DEPS
diff --git a/chromium/components/embedder_support/android/delegate/OWNERS b/chromium/components/embedder_support/android/delegate/OWNERS
new file mode 100644
index 00000000000..9fd8a623f73
--- /dev/null
+++ b/chromium/components/embedder_support/android/delegate/OWNERS
@@ -0,0 +1,2 @@
+boliu@chromium.org
+tedchoc@chromium.org \ No newline at end of file
diff --git a/chromium/components/web_contents_delegate_android/color_chooser_android.cc b/chromium/components/embedder_support/android/delegate/color_chooser_android.cc
index 8e89372b1f2..c6cf78d4ef0 100644
--- a/chromium/components/web_contents_delegate_android/color_chooser_android.cc
+++ b/chromium/components/embedder_support/android/delegate/color_chooser_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/web_contents_delegate_android/color_chooser_android.h"
+#include "components/embedder_support/android/delegate/color_chooser_android.h"
#include <stddef.h>
@@ -50,8 +50,7 @@ ColorChooserAndroid::ColorChooserAndroid(
OnColorChosen(env, j_color_chooser_, initial_color);
}
-ColorChooserAndroid::~ColorChooserAndroid() {
-}
+ColorChooserAndroid::~ColorChooserAndroid() {}
void ColorChooserAndroid::End() {
if (!j_color_chooser_.is_null()) {
diff --git a/chromium/components/web_contents_delegate_android/color_chooser_android.h b/chromium/components/embedder_support/android/delegate/color_chooser_android.h
index c8359168540..d05421b37e0 100644
--- a/chromium/components/web_contents_delegate_android/color_chooser_android.h
+++ b/chromium/components/embedder_support/android/delegate/color_chooser_android.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_WEB_CONTENTS_DELEGATE_ANDROID_COLOR_CHOOSER_ANDROID_H_
-#define COMPONENTS_WEB_CONTENTS_DELEGATE_ANDROID_COLOR_CHOOSER_ANDROID_H_
+#ifndef COMPONENTS_EMBEDDER_SUPPORT_ANDROID_DELEGATE_COLOR_CHOOSER_ANDROID_H_
+#define COMPONENTS_EMBEDDER_SUPPORT_ANDROID_DELEGATE_COLOR_CHOOSER_ANDROID_H_
#include <vector>
@@ -52,4 +52,4 @@ class ColorChooserAndroid : public content::ColorChooser {
} // namespace web_contents_delegate_android
-#endif // COMPONENTS_WEB_CONTENTS_DELEGATE_ANDROID_COLOR_CHOOSER_ANDROID_H_
+#endif // COMPONENTS_EMBEDDER_SUPPORT_ANDROID_DELEGATE_COLOR_CHOOSER_ANDROID_H_
diff --git a/chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc b/chromium/components/embedder_support/android/delegate/web_contents_delegate_android.cc
index 33afe2048c5..f53ce4ec16b 100644
--- a/chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc
+++ b/chromium/components/embedder_support/android/delegate/web_contents_delegate_android.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/web_contents_delegate_android/web_contents_delegate_android.h"
+#include "components/embedder_support/android/delegate/web_contents_delegate_android.h"
#include <android/keycodes.h>
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
-#include "components/web_contents_delegate_android/color_chooser_android.h"
+#include "components/embedder_support/android/delegate/color_chooser_android.h"
#include "content/public/browser/color_chooser.h"
#include "content/public/browser/global_request_id.h"
#include "content/public/browser/invalidate_type.h"
@@ -38,14 +38,12 @@ using content::WebContentsDelegate;
namespace web_contents_delegate_android {
WebContentsDelegateAndroid::WebContentsDelegateAndroid(JNIEnv* env, jobject obj)
- : weak_java_delegate_(env, obj) {
-}
+ : weak_java_delegate_(env, obj) {}
-WebContentsDelegateAndroid::~WebContentsDelegateAndroid() {
-}
+WebContentsDelegateAndroid::~WebContentsDelegateAndroid() {}
-ScopedJavaLocalRef<jobject>
-WebContentsDelegateAndroid::GetJavaDelegate(JNIEnv* env) const {
+ScopedJavaLocalRef<jobject> WebContentsDelegateAndroid::GetJavaDelegate(
+ JNIEnv* env) const {
return weak_java_delegate_.get(env);
}
@@ -89,7 +87,7 @@ WebContents* WebContentsDelegateAndroid::OpenURLFromTab(
ScopedJavaLocalRef<jstring> java_url =
ConvertUTF8ToJavaString(env, url.spec());
ScopedJavaLocalRef<jstring> extra_headers =
- ConvertUTF8ToJavaString(env, params.extra_headers);
+ ConvertUTF8ToJavaString(env, params.extra_headers);
ScopedJavaLocalRef<jobject> post_data;
if (params.uses_post && params.post_data) {
post_data = content::ConvertResourceRequestBodyToJavaObject(
@@ -124,7 +122,8 @@ WebContents* WebContentsDelegateAndroid::OpenURLFromTab(
}
void WebContentsDelegateAndroid::NavigationStateChanged(
- WebContents* source, content::InvalidateTypes changed_flags) {
+ WebContents* source,
+ content::InvalidateTypes changed_flags) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
if (obj.is_null())
@@ -150,7 +149,8 @@ void WebContentsDelegateAndroid::ActivateContents(WebContents* contents) {
Java_WebContentsDelegateAndroid_activateContents(env, obj);
}
-void WebContentsDelegateAndroid::LoadingStateChanged(WebContents* source,
+void WebContentsDelegateAndroid::LoadingStateChanged(
+ WebContents* source,
bool to_different_document) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
@@ -169,7 +169,8 @@ void WebContentsDelegateAndroid::LoadProgressChanged(WebContents* source,
void WebContentsDelegateAndroid::RendererUnresponsive(
WebContents* source,
- content::RenderWidgetHost* render_widget_host) {
+ content::RenderWidgetHost* render_widget_host,
+ base::RepeatingClosure hang_monitor_restarter) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
if (obj.is_null())
@@ -245,8 +246,8 @@ void WebContentsDelegateAndroid::CloseContents(WebContents* source) {
Java_WebContentsDelegateAndroid_closeContents(env, obj);
}
-void WebContentsDelegateAndroid::MoveContents(WebContents* source,
- const gfx::Rect& pos) {
+void WebContentsDelegateAndroid::SetContentsBounds(WebContents* source,
+ const gfx::Rect& bounds) {
// Do nothing.
}
@@ -333,23 +334,14 @@ void WebContentsDelegateAndroid::ShowRepostFormWarningDialog(
Java_WebContentsDelegateAndroid_showRepostFormWarningDialog(env, obj);
}
-ScopedJavaLocalRef<jobject>
-WebContentsDelegateAndroid::GetContentVideoViewEmbedder() {
- JNIEnv* env = AttachCurrentThread();
- ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
- if (obj.is_null())
- return ScopedJavaLocalRef<jobject>();
-
- return Java_WebContentsDelegateAndroid_getContentVideoViewEmbedder(env, obj);
-}
-
bool WebContentsDelegateAndroid::ShouldBlockMediaRequest(const GURL& url) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
if (obj.is_null())
return false;
ScopedJavaLocalRef<jstring> j_url = ConvertUTF8ToJavaString(env, url.spec());
- return Java_WebContentsDelegateAndroid_shouldBlockMediaRequest(env, obj, j_url);
+ return Java_WebContentsDelegateAndroid_shouldBlockMediaRequest(env, obj,
+ j_url);
}
void WebContentsDelegateAndroid::EnterFullscreenModeForTab(
@@ -383,8 +375,7 @@ bool WebContentsDelegateAndroid::IsFullscreenForTabOrPending(
}
void WebContentsDelegateAndroid::RequestAppBannerFromDevTools(
- content::WebContents* web_contents) {
-}
+ content::WebContents* web_contents) {}
void WebContentsDelegateAndroid::OnDidBlockFramebust(
content::WebContents* web_contents,
diff --git a/chromium/components/web_contents_delegate_android/web_contents_delegate_android.h b/chromium/components/embedder_support/android/delegate/web_contents_delegate_android.h
index 91de953e570..643068dd6c3 100644
--- a/chromium/components/web_contents_delegate_android/web_contents_delegate_android.h
+++ b/chromium/components/embedder_support/android/delegate/web_contents_delegate_android.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_WEB_CONTENTS_DELEGATE_ANDROID_WEB_CONTENTS_DELEGATE_ANDROID_H_
-#define COMPONENTS_WEB_CONTENTS_DELEGATE_ANDROID_WEB_CONTENTS_DELEGATE_ANDROID_H_
+#ifndef COMPONENTS_EMBEDDER_SUPPORT_ANDROID_DELEGATE_WEB_CONTENTS_DELEGATE_ANDROID_H_
+#define COMPONENTS_EMBEDDER_SUPPORT_ANDROID_DELEGATE_WEB_CONTENTS_DELEGATE_ANDROID_H_
#include <stdint.h>
@@ -21,7 +21,7 @@ class WebContents;
class WebContentsDelegate;
struct NativeWebKeyboardEvent;
struct OpenURLParams;
-}
+} // namespace content
namespace web_contents_delegate_android {
@@ -36,7 +36,6 @@ enum WebContentsDelegateLogLevel {
WEB_CONTENTS_DELEGATE_LOG_LEVEL_ERROR = 3,
};
-
// Native underpinnings of WebContentsDelegateAndroid.java. Provides a default
// delegate for WebContents to forward calls to the java peer. The embedding
// application may subclass and override methods on either the C++ or Java side
@@ -65,7 +64,8 @@ class WebContentsDelegateAndroid : public content::WebContentsDelegate {
double load_progress) override;
void RendererUnresponsive(
content::WebContents* source,
- content::RenderWidgetHost* render_widget_host) override;
+ content::RenderWidgetHost* render_widget_host,
+ base::RepeatingClosure hang_monitor_restarter) override;
void RendererResponsive(
content::WebContents* source,
content::RenderWidgetHost* render_widget_host) override;
@@ -89,8 +89,8 @@ class WebContentsDelegateAndroid : public content::WebContentsDelegate {
const std::string& partition_id,
content::SessionStorageNamespace* session_storage_namespace) override;
void CloseContents(content::WebContents* source) override;
- void MoveContents(content::WebContents* source,
- const gfx::Rect& pos) override;
+ void SetContentsBounds(content::WebContents* source,
+ const gfx::Rect& bounds) override;
bool DidAddMessageToConsole(content::WebContents* source,
int32_t level,
const base::string16& message,
@@ -102,8 +102,6 @@ class WebContentsDelegateAndroid : public content::WebContentsDelegate {
const content::NativeWebKeyboardEvent& event) override;
bool TakeFocus(content::WebContents* source, bool reverse) override;
void ShowRepostFormWarningDialog(content::WebContents* source) override;
- base::android::ScopedJavaLocalRef<jobject>
- GetContentVideoViewEmbedder() override;
bool ShouldBlockMediaRequest(const GURL& url) override;
void EnterFullscreenModeForTab(
content::WebContents* web_contents,
@@ -132,4 +130,4 @@ class WebContentsDelegateAndroid : public content::WebContentsDelegate {
} // namespace web_contents_delegate_android
-#endif // COMPONENTS_WEB_CONTENTS_DELEGATE_ANDROID_WEB_CONTENTS_DELEGATE_ANDROID_H_
+#endif // COMPONENTS_EMBEDDER_SUPPORT_ANDROID_DELEGATE_WEB_CONTENTS_DELEGATE_ANDROID_H_
diff --git a/chromium/components/web_contents_delegate_android/java/res/drawable-hdpi/color_picker_advanced_select_handle.png b/chromium/components/embedder_support/android/java/res/drawable-hdpi/color_picker_advanced_select_handle.png
index 3994d635773..3994d635773 100644
--- a/chromium/components/web_contents_delegate_android/java/res/drawable-hdpi/color_picker_advanced_select_handle.png
+++ b/chromium/components/embedder_support/android/java/res/drawable-hdpi/color_picker_advanced_select_handle.png
Binary files differ
diff --git a/chromium/components/web_contents_delegate_android/java/res/drawable-mdpi/color_picker_advanced_select_handle.png b/chromium/components/embedder_support/android/java/res/drawable-mdpi/color_picker_advanced_select_handle.png
index 0ba0946f91d..0ba0946f91d 100644
--- a/chromium/components/web_contents_delegate_android/java/res/drawable-mdpi/color_picker_advanced_select_handle.png
+++ b/chromium/components/embedder_support/android/java/res/drawable-mdpi/color_picker_advanced_select_handle.png
Binary files differ
diff --git a/chromium/components/web_contents_delegate_android/java/res/drawable-xhdpi/color_picker_advanced_select_handle.png b/chromium/components/embedder_support/android/java/res/drawable-xhdpi/color_picker_advanced_select_handle.png
index 1d00ca6f3b6..1d00ca6f3b6 100644
--- a/chromium/components/web_contents_delegate_android/java/res/drawable-xhdpi/color_picker_advanced_select_handle.png
+++ b/chromium/components/embedder_support/android/java/res/drawable-xhdpi/color_picker_advanced_select_handle.png
Binary files differ
diff --git a/chromium/components/web_contents_delegate_android/java/res/drawable/color_button_background.xml b/chromium/components/embedder_support/android/java/res/drawable/color_button_background.xml
index 66bcce2e4d2..66bcce2e4d2 100644
--- a/chromium/components/web_contents_delegate_android/java/res/drawable/color_button_background.xml
+++ b/chromium/components/embedder_support/android/java/res/drawable/color_button_background.xml
diff --git a/chromium/components/web_contents_delegate_android/java/res/drawable/color_picker_border.xml b/chromium/components/embedder_support/android/java/res/drawable/color_picker_border.xml
index 6cd6bbfbd68..6cd6bbfbd68 100644
--- a/chromium/components/web_contents_delegate_android/java/res/drawable/color_picker_border.xml
+++ b/chromium/components/embedder_support/android/java/res/drawable/color_picker_border.xml
diff --git a/chromium/components/web_contents_delegate_android/java/res/layout/color_picker_advanced_component.xml b/chromium/components/embedder_support/android/java/res/layout/color_picker_advanced_component.xml
index a51c055b1b1..a51c055b1b1 100644
--- a/chromium/components/web_contents_delegate_android/java/res/layout/color_picker_advanced_component.xml
+++ b/chromium/components/embedder_support/android/java/res/layout/color_picker_advanced_component.xml
diff --git a/chromium/components/web_contents_delegate_android/java/res/layout/color_picker_dialog_content.xml b/chromium/components/embedder_support/android/java/res/layout/color_picker_dialog_content.xml
index 552b27c66f3..85111fd115f 100644
--- a/chromium/components/web_contents_delegate_android/java/res/layout/color_picker_dialog_content.xml
+++ b/chromium/components/embedder_support/android/java/res/layout/color_picker_dialog_content.xml
@@ -15,13 +15,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <org.chromium.components.web_contents_delegate_android.ColorPickerAdvanced
+ <org.chromium.components.embedder_support.delegate.ColorPickerAdvanced
android:id="@+id/color_picker_advanced"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>
- <org.chromium.components.web_contents_delegate_android.ColorPickerSimple
+ <org.chromium.components.embedder_support.delegate.ColorPickerSimple
android:id="@+id/color_picker_simple"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -39,7 +39,7 @@
android:background="@drawable/color_picker_border"
android:padding="1px">
- <org.chromium.components.web_contents_delegate_android.ColorPickerMoreButton
+ <org.chromium.components.embedder_support.delegate.ColorPickerMoreButton
android:id="@+id/more_colors_button"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
diff --git a/chromium/components/web_contents_delegate_android/java/res/layout/color_picker_dialog_title.xml b/chromium/components/embedder_support/android/java/res/layout/color_picker_dialog_title.xml
index 52aa08eb8fb..52aa08eb8fb 100644
--- a/chromium/components/web_contents_delegate_android/java/res/layout/color_picker_dialog_title.xml
+++ b/chromium/components/embedder_support/android/java/res/layout/color_picker_dialog_title.xml
diff --git a/chromium/components/web_contents_delegate_android/java/res/values/colors.xml b/chromium/components/embedder_support/android/java/res/values/colors.xml
index 4e3b0fbfdd8..4e3b0fbfdd8 100644
--- a/chromium/components/web_contents_delegate_android/java/res/values/colors.xml
+++ b/chromium/components/embedder_support/android/java/res/values/colors.xml
diff --git a/chromium/components/web_contents_delegate_android/java/res/values/dimens.xml b/chromium/components/embedder_support/android/java/res/values/dimens.xml
index 92c60b10245..92c60b10245 100644
--- a/chromium/components/web_contents_delegate_android/java/res/values/dimens.xml
+++ b/chromium/components/embedder_support/android/java/res/values/dimens.xml
diff --git a/chromium/components/content_view/DEPS b/chromium/components/embedder_support/android/java/src/org/chromium/components/embedder_support/delegate/DEPS
index f20cebbb3be..f20cebbb3be 100644
--- a/chromium/components/content_view/DEPS
+++ b/chromium/components/embedder_support/android/java/src/org/chromium/components/embedder_support/delegate/DEPS
diff --git a/chromium/components/web_contents_delegate_android/OWNERS b/chromium/components/embedder_support/android/java/src/org/chromium/components/embedder_support/delegate/OWNERS
index be737980458..be737980458 100644
--- a/chromium/components/web_contents_delegate_android/OWNERS
+++ b/chromium/components/embedder_support/android/java/src/org/chromium/components/embedder_support/delegate/OWNERS
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_am.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_am.xtb
index b97457bb5d8..b97457bb5d8 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_am.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_am.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ar.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ar.xtb
index 014ab4cb108..014ab4cb108 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ar.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ar.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_bg.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_bg.xtb
index 66a42e7da39..66a42e7da39 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_bg.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_bg.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ca.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ca.xtb
index dce1297ba40..dce1297ba40 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ca.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ca.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_cs.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_cs.xtb
index 1b8d8388c83..1b8d8388c83 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_cs.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_cs.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_da.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_da.xtb
index 1e8dad8510b..1e8dad8510b 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_da.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_da.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_de.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_de.xtb
index c8a6c550320..c8a6c550320 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_de.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_de.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_el.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_el.xtb
index 46782d2f55e..46782d2f55e 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_el.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_el.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_en-GB.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_en-GB.xtb
index d6ec75b4772..d6ec75b4772 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_en-GB.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_en-GB.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_es-419.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_es-419.xtb
index 77c880444b6..77c880444b6 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_es-419.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_es-419.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_es.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_es.xtb
index d5f44cb867e..d5f44cb867e 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_es.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_es.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_fa.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_fa.xtb
index 374834152a7..374834152a7 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_fa.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_fa.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_fi.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_fi.xtb
index 6c0fff99a44..6c0fff99a44 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_fi.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_fi.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_fil.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_fil.xtb
index 38fd3acb352..38fd3acb352 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_fil.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_fil.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_fr.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_fr.xtb
index 79702bfdb8f..79702bfdb8f 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_fr.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_fr.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_hi.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_hi.xtb
index d3d4cc7832a..d3d4cc7832a 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_hi.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_hi.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_hr.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_hr.xtb
index 724d1ecd3b6..724d1ecd3b6 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_hr.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_hr.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_hu.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_hu.xtb
index a572e7313f4..a572e7313f4 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_hu.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_hu.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_id.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_id.xtb
index b476691f85e..b476691f85e 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_id.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_id.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_it.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_it.xtb
index 26d68d11395..26d68d11395 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_it.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_it.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_iw.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_iw.xtb
index c9c1c2e3e30..c9c1c2e3e30 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_iw.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_iw.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ja.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ja.xtb
index 2c5bdc05451..2c5bdc05451 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ja.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ja.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ko.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ko.xtb
index 6970fc9d28e..6970fc9d28e 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ko.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ko.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_lt.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_lt.xtb
index 4f2e4d1b650..4f2e4d1b650 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_lt.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_lt.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_lv.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_lv.xtb
index aa5d59cb277..aa5d59cb277 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_lv.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_lv.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_nl.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_nl.xtb
index dbc50fdaa53..dbc50fdaa53 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_nl.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_nl.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_no.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_no.xtb
index abb6d313cdd..abb6d313cdd 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_no.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_no.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_pl.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_pl.xtb
index 75dfdd640b5..75dfdd640b5 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_pl.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_pl.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_pt-BR.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_pt-BR.xtb
index 7eab6e0bbbf..7eab6e0bbbf 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_pt-BR.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_pt-BR.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_pt-PT.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_pt-PT.xtb
index d86e46d1407..d86e46d1407 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_pt-PT.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_pt-PT.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ro.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ro.xtb
index b3a135a6ef2..b3a135a6ef2 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ro.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ro.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ru.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ru.xtb
index d93955edcfb..d93955edcfb 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_ru.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_ru.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sk.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sk.xtb
index f8b1a8595f5..f8b1a8595f5 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sk.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sk.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sl.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sl.xtb
index 16b97e7d118..16b97e7d118 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sl.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sl.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sr.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sr.xtb
index b1b1b6e814a..b1b1b6e814a 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sr.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sr.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sv.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sv.xtb
index d82147b35dc..d82147b35dc 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sv.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sv.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sw.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sw.xtb
index ebea446806f..ebea446806f 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_sw.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_sw.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_th.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_th.xtb
index 44f904b2088..44f904b2088 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_th.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_th.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_tr.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_tr.xtb
index 8207ecb0e29..8207ecb0e29 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_tr.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_tr.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_uk.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_uk.xtb
index 14d5e75061b..14d5e75061b 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_uk.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_uk.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_vi.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_vi.xtb
index 1089abef96f..1089abef96f 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_vi.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_vi.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_zh-CN.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_zh-CN.xtb
index 25dd741eca5..25dd741eca5 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_zh-CN.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_zh-CN.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_zh-TW.xtb b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_zh-TW.xtb
index 55275965530..55275965530 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/translations/web_contents_delegate_android_strings_zh-TW.xtb
+++ b/chromium/components/embedder_support/android/java/strings/translations/web_contents_delegate_android_strings_zh-TW.xtb
diff --git a/chromium/components/web_contents_delegate_android/java/strings/web_contents_delegate_android_strings.grd b/chromium/components/embedder_support/android/java/strings/web_contents_delegate_android_strings.grd
index fa0a4f4b9b4..fa0a4f4b9b4 100644
--- a/chromium/components/web_contents_delegate_android/java/strings/web_contents_delegate_android_strings.grd
+++ b/chromium/components/embedder_support/android/java/strings/web_contents_delegate_android_strings.grd
diff --git a/chromium/components/embedder_support/android/view/content_view_render_view.cc b/chromium/components/embedder_support/android/view/content_view_render_view.cc
new file mode 100644
index 00000000000..3c4bdc3e560
--- /dev/null
+++ b/chromium/components/embedder_support/android/view/content_view_render_view.cc
@@ -0,0 +1,137 @@
+// Copyright (c) 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/embedder_support/android/view/content_view_render_view.h"
+#include <android/bitmap.h>
+#include <android/native_window_jni.h>
+
+#include <memory>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "cc/layers/layer.h"
+#include "content/public/browser/android/compositor.h"
+#include "content/public/browser/web_contents.h"
+#include "jni/ContentViewRenderView_jni.h"
+#include "ui/android/view_android.h"
+#include "ui/android/window_android.h"
+#include "ui/gfx/android/java_bitmap.h"
+#include "ui/gfx/geometry/size.h"
+
+using base::android::JavaParamRef;
+using base::android::ScopedJavaLocalRef;
+
+namespace embedder_support {
+
+ContentViewRenderView::ContentViewRenderView(JNIEnv* env,
+ jobject obj,
+ gfx::NativeWindow root_window)
+ : root_window_(root_window), current_surface_format_(0) {
+ java_obj_.Reset(env, obj);
+}
+
+ContentViewRenderView::~ContentViewRenderView() {}
+
+// static
+static jlong JNI_ContentViewRenderView_Init(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ const JavaParamRef<jobject>& jroot_window_android) {
+ gfx::NativeWindow root_window =
+ ui::WindowAndroid::FromJavaWindowAndroid(jroot_window_android);
+ ContentViewRenderView* content_view_render_view =
+ new ContentViewRenderView(env, obj, root_window);
+ return reinterpret_cast<intptr_t>(content_view_render_view);
+}
+
+void ContentViewRenderView::Destroy(JNIEnv* env,
+ const JavaParamRef<jobject>& obj) {
+ delete this;
+}
+
+void ContentViewRenderView::SetCurrentWebContents(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ const JavaParamRef<jobject>& jweb_contents) {
+ InitCompositor();
+ content::WebContents* web_contents =
+ content::WebContents::FromJavaWebContents(jweb_contents);
+ compositor_->SetRootLayer(web_contents
+ ? web_contents->GetNativeView()->GetLayer()
+ : scoped_refptr<cc::Layer>());
+}
+
+void ContentViewRenderView::OnPhysicalBackingSizeChanged(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ const JavaParamRef<jobject>& jweb_contents,
+ jint width,
+ jint height) {
+ content::WebContents* web_contents =
+ content::WebContents::FromJavaWebContents(jweb_contents);
+ gfx::Size size(width, height);
+ web_contents->GetNativeView()->OnPhysicalBackingSizeChanged(size);
+}
+
+void ContentViewRenderView::SurfaceCreated(JNIEnv* env,
+ const JavaParamRef<jobject>& obj) {
+ current_surface_format_ = 0;
+ InitCompositor();
+}
+
+void ContentViewRenderView::SurfaceDestroyed(JNIEnv* env,
+ const JavaParamRef<jobject>& obj) {
+ compositor_->SetSurface(NULL);
+ current_surface_format_ = 0;
+}
+
+void ContentViewRenderView::SurfaceChanged(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ jint format,
+ jint width,
+ jint height,
+ const JavaParamRef<jobject>& surface) {
+ if (current_surface_format_ != format) {
+ current_surface_format_ = format;
+ compositor_->SetSurface(surface);
+ }
+ compositor_->SetWindowBounds(gfx::Size(width, height));
+}
+
+void ContentViewRenderView::SetOverlayVideoMode(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ bool enabled) {
+ compositor_->SetRequiresAlphaChannel(enabled);
+ compositor_->SetBackgroundColor(enabled ? SK_ColorTRANSPARENT
+ : SK_ColorWHITE);
+ compositor_->SetNeedsComposite();
+}
+
+void ContentViewRenderView::SetNeedsComposite(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj) {
+ compositor_->SetNeedsComposite();
+}
+
+void ContentViewRenderView::UpdateLayerTreeHost() {
+ // TODO(wkorman): Rename Layout to UpdateLayerTreeHost in all Android
+ // Compositor related classes.
+}
+
+void ContentViewRenderView::DidSwapFrame(int pending_frames) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_ContentViewRenderView_didSwapFrame(env, java_obj_);
+}
+
+void ContentViewRenderView::InitCompositor() {
+ if (!compositor_)
+ compositor_.reset(content::Compositor::Create(this, root_window_));
+}
+
+} // namespace embedder_support
diff --git a/chromium/components/embedder_support/android/view/content_view_render_view.h b/chromium/components/embedder_support/android/view/content_view_render_view.h
new file mode 100644
index 00000000000..75a9b379db4
--- /dev/null
+++ b/chromium/components/embedder_support/android/view/content_view_render_view.h
@@ -0,0 +1,78 @@
+// Copyright (c) 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_EMBEDDER_SUPPORT_ANDROID_VIEW_CONTENT_VIEW_RENDER_VIEW_H_
+#define COMPONENTS_EMBEDDER_SUPPORT_ANDROID_VIEW_CONTENT_VIEW_RENDER_VIEW_H_
+
+#include <memory>
+
+#include "base/android/jni_weak_ref.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/android/compositor_client.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace content {
+class Compositor;
+} // namespace content
+
+namespace embedder_support {
+
+class ContentViewRenderView : public content::CompositorClient {
+ public:
+ ContentViewRenderView(JNIEnv* env,
+ jobject obj,
+ gfx::NativeWindow root_window);
+
+ // Methods called from Java via JNI -----------------------------------------
+ void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
+ void SetCurrentWebContents(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj,
+ const base::android::JavaParamRef<jobject>& jweb_contents);
+ void OnPhysicalBackingSizeChanged(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj,
+ const base::android::JavaParamRef<jobject>& jweb_contents,
+ jint width,
+ jint height);
+ void SurfaceCreated(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj);
+ void SurfaceDestroyed(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj);
+ void SurfaceChanged(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj,
+ jint format,
+ jint width,
+ jint height,
+ const base::android::JavaParamRef<jobject>& surface);
+ void SetOverlayVideoMode(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj,
+ bool enabled);
+ void SetNeedsComposite(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj);
+
+ // CompositorClient implementation
+ void UpdateLayerTreeHost() override;
+ void DidSwapFrame(int pending_frames) override;
+
+ private:
+ ~ContentViewRenderView() override;
+
+ void InitCompositor();
+
+ base::android::ScopedJavaGlobalRef<jobject> java_obj_;
+
+ std::unique_ptr<content::Compositor> compositor_;
+
+ gfx::NativeWindow root_window_;
+ int current_surface_format_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContentViewRenderView);
+};
+
+} // namespace embedder_support
+
+#endif // COMPONENTS_EMBEDDER_SUPPORT_ANDROID_VIEW_CONTENT_VIEW_RENDER_VIEW_H_
diff --git a/chromium/components/encrypted_messages/message_encrypter.cc b/chromium/components/encrypted_messages/message_encrypter.cc
index ce210f17a13..3d5f5aacb66 100644
--- a/chromium/components/encrypted_messages/message_encrypter.cc
+++ b/chromium/components/encrypted_messages/message_encrypter.cc
@@ -25,12 +25,9 @@ bool GetHkdfSubkeySecret(size_t subkey_length,
if (!X25519(shared_secret, private_key, public_key))
return false;
- crypto::HKDF hkdf(base::StringPiece(reinterpret_cast<char*>(shared_secret),
- sizeof(shared_secret)),
- "" /* salt */, hkdf_label, 0 /* key bytes */,
- 0 /* iv bytes */, subkey_length);
-
- *secret = hkdf.subkey_secret().as_string();
+ base::StringPiece hkdf_input(reinterpret_cast<char*>(shared_secret),
+ sizeof(shared_secret));
+ *secret = crypto::HkdfSha256(hkdf_input, "", hkdf_label, subkey_length);
return true;
}
diff --git a/chromium/components/error_page/OWNERS b/chromium/components/error_page/OWNERS
index bebb2c48565..9475512b50a 100644
--- a/chromium/components/error_page/OWNERS
+++ b/chromium/components/error_page/OWNERS
@@ -1,8 +1,10 @@
+# Preferred reviewer.
mmenke@chromium.org
-juliatuttle@chromium.org
# If changing network error strings, please visit go/chrome-neterror-strings
# (Google internal) or contact edwardjung@chromium.org and rachelis@chromium.org
per-file error_page_strings.grdp=edwardjung@chromium.org
+file://net/OWNERS
+
# COMPONENT: Internals>Network
diff --git a/chromium/components/error_page/common/localized_error.cc b/chromium/components/error_page/common/localized_error.cc
index be109af74d4..dfb40c84c9c 100644
--- a/chromium/components/error_page/common/localized_error.cc
+++ b/chromium/components/error_page/common/localized_error.cc
@@ -84,7 +84,7 @@ struct LocalizedErrorMap {
// mouse over when the error is in a frame.
unsigned int summary_resource_id;
int suggestions; // Bitmap of SUGGEST_* values.
- int buttons; // Which buttons if any to show.
+ int buttons; // Bitmap of which buttons if any to show.
};
// clang-format off
@@ -972,7 +972,7 @@ void LocalizedError::GetStrings(
// If no parameters were provided, use the defaults.
if (!params) {
params.reset(new error_page::ErrorPageParams());
- params->suggest_reload = !!(options.buttons && SHOW_BUTTON_RELOAD);
+ params->suggest_reload = !!(options.buttons & SHOW_BUTTON_RELOAD);
}
base::ListValue* suggestions_details = nullptr;
diff --git a/chromium/components/exo/BUILD.gn b/chromium/components/exo/BUILD.gn
index be7d79a9876..6e4c0204110 100644
--- a/chromium/components/exo/BUILD.gn
+++ b/chromium/components/exo/BUILD.gn
@@ -27,6 +27,9 @@ source_set("exo") {
"display.h",
"gaming_seat.cc",
"gaming_seat.h",
+ "input_method_surface.cc",
+ "input_method_surface.h",
+ "input_method_surface_manager.h",
"keyboard.cc",
"keyboard.h",
"keyboard_delegate.h",
@@ -200,7 +203,7 @@ test("exo_unittests") {
"//cc:test_support",
"//components/viz/test:test_support",
"//device/gamepad:test_helpers",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//testing/gtest",
"//ui/aura",
"//ui/aura:test_support",
diff --git a/chromium/components/exo/DEPS b/chromium/components/exo/DEPS
index 4f81d0ee5b5..2fc1da6787e 100644
--- a/chromium/components/exo/DEPS
+++ b/chromium/components/exo/DEPS
@@ -15,7 +15,7 @@ include_rules = [
specific_include_rules = {
"run_all_unittests\.cc": [
- "+mojo/edk/embedder/embedder.h",
+ "+mojo/core/embedder/embedder.h",
],
"surface_unittest\.cc": [
diff --git a/chromium/components/exo/OWNERS b/chromium/components/exo/OWNERS
index 157836abab7..a2f4fac66dd 100644
--- a/chromium/components/exo/OWNERS
+++ b/chromium/components/exo/OWNERS
@@ -1 +1,3 @@
reveman@chromium.org
+oshima@chromium.org
+dcastagna@chromium.org
diff --git a/chromium/components/exo/buffer.cc b/chromium/components/exo/buffer.cc
index 5ff479846d7..e18f6618f13 100644
--- a/chromium/components/exo/buffer.cc
+++ b/chromium/components/exo/buffer.cc
@@ -23,6 +23,7 @@
#include "components/exo/layer_tree_frame_sink_holder.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 "components/viz/common/resources/single_release_callback.h"
#include "gpu/command_buffer/client/context_support.h"
#include "gpu/command_buffer/client/gles2_interface.h"
@@ -82,14 +83,6 @@ unsigned CreateGLTexture(gpu::gles2::GLES2Interface* gles2, GLenum target) {
return texture_id;
}
-void CreateGLMailbox(gpu::gles2::GLES2Interface* gles2,
- unsigned texture_id,
- GLenum target,
- gpu::Mailbox* mailbox) {
- gles2->GenMailboxCHROMIUM(mailbox->name);
- gles2->ProduceTextureDirectCHROMIUM(texture_id, mailbox->name);
-}
-
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -109,7 +102,8 @@ class Buffer::Texture : public ui::ContextFactoryObserver {
~Texture() override;
// Overridden from ui::ContextFactoryObserver:
- void OnLostResources() override;
+ void OnLostSharedContext() override;
+ void OnLostVizProcess() override;
// Returns true if GLES2 resources for texture have been lost.
bool IsLost();
@@ -178,7 +172,7 @@ Buffer::Texture::Texture(ui::ContextFactory* context_factory,
gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
texture_id_ = CreateGLTexture(gles2, texture_target_);
// Generate a crypto-secure random mailbox name.
- CreateGLMailbox(gles2, texture_id_, texture_target_, &mailbox_);
+ gles2->ProduceTextureDirectCHROMIUM(texture_id_, mailbox_.name);
// Provides a notification when |context_provider_| is lost.
context_factory_->AddObserver(this);
}
@@ -216,13 +210,15 @@ Buffer::Texture::~Texture() {
context_factory_->RemoveObserver(this);
}
-void Buffer::Texture::OnLostResources() {
+void Buffer::Texture::OnLostSharedContext() {
DestroyResources();
context_factory_->RemoveObserver(this);
context_provider_ = nullptr;
context_factory_ = nullptr;
}
+void Buffer::Texture::OnLostVizProcess() {}
+
bool Buffer::Texture::IsLost() {
if (context_provider_) {
gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
@@ -255,7 +251,7 @@ gpu::SyncToken Buffer::Texture::BindTexImage() {
gles2->BindTexImage2DCHROMIUM(texture_target_, image_id_);
// Generate a crypto-secure random mailbox name if not already done.
if (mailbox_.IsZero())
- CreateGLMailbox(gles2, texture_id_, texture_target_, &mailbox_);
+ gles2->ProduceTextureDirectCHROMIUM(texture_id_, mailbox_.name);
// Create and return a sync token that can be used to ensure that the
// BindTexImage2DCHROMIUM call is processed before issuing any commands
// that will read from the texture on a different context.
@@ -466,7 +462,7 @@ bool Buffer::ProduceTransferableResource(
resource->mailbox_holder = gpu::MailboxHolder(contents_texture->mailbox(),
sync_token, texture_target_);
resource->is_overlay_candidate = is_overlay_candidate_;
- resource->buffer_format = gpu_memory_buffer_->GetFormat();
+ resource->format = viz::GetResourceFormat(gpu_memory_buffer_->GetFormat());
// The contents texture will be released when no longer used by the
// compositor.
diff --git a/chromium/components/exo/buffer_unittest.cc b/chromium/components/exo/buffer_unittest.cc
index 460d61aa896..85a9ca91093 100644
--- a/chromium/components/exo/buffer_unittest.cc
+++ b/chromium/components/exo/buffer_unittest.cc
@@ -141,7 +141,7 @@ TEST_F(BufferTest, OnLostResources) {
static_cast<ui::InProcessContextFactory*>(
aura::Env::GetInstance()->context_factory())
- ->SendOnLostResources();
+ ->SendOnLostSharedContext();
}
TEST_F(BufferTest, SurfaceTreeHostDestruction) {
diff --git a/chromium/components/exo/client_controlled_shell_surface.cc b/chromium/components/exo/client_controlled_shell_surface.cc
index 08891072923..88350bf0915 100644
--- a/chromium/components/exo/client_controlled_shell_surface.cc
+++ b/chromium/components/exo/client_controlled_shell_surface.cc
@@ -4,6 +4,7 @@
#include "components/exo/client_controlled_shell_surface.h"
+#include <map>
#include <utility>
#include "ash/frame/caption_buttons/caption_button_model.h"
@@ -33,9 +34,11 @@
#include "base/trace_event/trace_event_argument.h"
#include "components/exo/surface.h"
#include "components/exo/wm_helper.h"
+#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
+#include "ui/aura/window_observer.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/class_property.h"
#include "ui/compositor/compositor_lock.h"
@@ -232,6 +235,53 @@ class CaptionButtonModel : public ash::CaptionButtonModel {
DISALLOW_COPY_AND_ASSIGN(CaptionButtonModel);
};
+// EventTargetingBlocker blocks the event targeting by settnig NONE targeting
+// policy to the window subtrees. It resets to the origial policy upon deletion.
+class EventTargetingBlocker : aura::WindowObserver {
+ public:
+ EventTargetingBlocker() = default;
+
+ ~EventTargetingBlocker() override {
+ if (window_)
+ Unregister(window_);
+ }
+
+ void Block(aura::Window* window) {
+ window_ = window;
+ Register(window);
+ }
+
+ private:
+ void Register(aura::Window* window) {
+ window->AddObserver(this);
+ auto policy = window->event_targeting_policy();
+ window->SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy::NONE);
+ policy_map_.emplace(window, policy);
+ for (auto* child : window->children())
+ Register(child);
+ }
+
+ void Unregister(aura::Window* window) {
+ window->RemoveObserver(this);
+ DCHECK(policy_map_.find(window) != policy_map_.end());
+ window->SetEventTargetingPolicy(policy_map_[window]);
+ for (auto* child : window->children())
+ Unregister(child);
+ }
+
+ void OnWindowDestroying(aura::Window* window) override {
+ auto it = policy_map_.find(window);
+ DCHECK(it != policy_map_.end());
+ policy_map_.erase(it);
+ window->RemoveObserver(this);
+ }
+
+ std::map<aura::Window*, ui::mojom::EventTargetingPolicy> policy_map_;
+ aura::Window* window_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(EventTargetingBlocker);
+};
+
} // namespace
class ClientControlledShellSurface::ScopedSetBoundsLocally {
@@ -276,11 +326,9 @@ ClientControlledShellSurface::ClientControlledShellSurface(Surface* surface,
}
ClientControlledShellSurface::~ClientControlledShellSurface() {
- if (wide_frame_)
- wide_frame_->Close();
-
- WMHelper::GetInstance()->RemoveDisplayConfigurationObserver(this);
+ wide_frame_.reset();
display::Screen::GetScreen()->RemoveObserver(this);
+ WMHelper::GetInstance()->RemoveDisplayConfigurationObserver(this);
}
void ClientControlledShellSurface::SetMaximized() {
@@ -408,7 +456,7 @@ void ClientControlledShellSurface::OnWindowStateChangeEvent(
void ClientControlledShellSurface::StartDrag(int component,
const gfx::Point& location) {
- TRACE_EVENT2("exo", "ClientControlledShellSurface::StartResize", "component",
+ TRACE_EVENT2("exo", "ClientControlledShellSurface::StartDrag", "component",
component, "location", location.ToString());
if (!widget_ || (client_controlled_move_resize_ && component != HTCAPTION))
@@ -448,7 +496,8 @@ void ClientControlledShellSurface::SetCanMaximize(bool can_maximize) {
void ClientControlledShellSurface::UpdateAutoHideFrame() {
if (immersive_fullscreen_controller_) {
bool enabled = (frame_type_ == SurfaceFrameType::AUTOHIDE &&
- GetWindowState()->IsMaximizedOrFullscreenOrPinned());
+ (GetWindowState()->IsMaximizedOrFullscreenOrPinned() ||
+ GetWindowState()->IsSnapped()));
immersive_fullscreen_controller_->SetEnabled(
ash::ImmersiveFullscreenController::WINDOW_TYPE_OTHER, enabled);
}
@@ -472,10 +521,12 @@ void ClientControlledShellSurface::SetExtraTitle(
const base::string16& extra_title) {
TRACE_EVENT1("exo", "ClientControlledShellSurface::SetExtraTitle",
"extra_title", base::UTF16ToUTF8(extra_title));
- // The extra title is used in the window frame.
- extra_title_ = extra_title;
- if (widget_)
- widget_->UpdateWindowTitle();
+
+ if (!widget_)
+ return;
+
+ GetFrameView()->GetHeaderView()->GetFrameHeader()->SetFrameTextOverride(
+ extra_title);
}
void ClientControlledShellSurface::SetOrientationLock(
@@ -508,7 +559,7 @@ void ClientControlledShellSurface::OnBoundsChangeEvent(
// The client's geometry uses fullscreen in client controlled,
// (but the surface is placed under the frame), so just use
- // the window bounds instead for maximixed stte.
+ // the window bounds instead for maximixed state.
gfx::Rect client_bounds =
widget_->IsMaximized()
? window_bounds
@@ -611,9 +662,20 @@ bool ClientControlledShellSurface::IsInputEnabled(Surface* surface) const {
}
void ClientControlledShellSurface::OnSetFrame(SurfaceFrameType type) {
+ // TODO(oshima): We shouldn't send the synthesized motion event when just
+ // changing the frame type. The better solution would be to keep the window
+ // position regardless of the frame state, but that won't be available until
+ // next arc version.
+ // This is a stopgap solution not to generate the event until it is resolved.
+ EventTargetingBlocker blocker;
+ bool suppress_mouse_event = frame_type_ != type && widget_;
+ if (suppress_mouse_event)
+ blocker.Block(widget_->GetNativeWindow());
ShellSurfaceBase::OnSetFrame(type);
- frame_type_ = type;
UpdateAutoHideFrame();
+
+ if (suppress_mouse_event)
+ UpdateSurfaceBounds();
}
void ClientControlledShellSurface::OnSetFrameColors(SkColor active_color,
@@ -769,10 +831,8 @@ void ClientControlledShellSurface::CompositorLockTimedOut() {
// ShellSurface overrides:
void ClientControlledShellSurface::SetWidgetBounds(const gfx::Rect& bounds) {
-
if (((!client_controlled_move_resize_ && !GetWindowState()->is_dragged()) ||
- (client_controlled_move_resize_ &&
- (!resizer_ || resizer_->details().window_component != HTCAPTION))) &&
+ client_controlled_move_resize_) &&
!client_controlled_state_->set_bounds_locally()) {
{
// Calculate a minimum window visibility required bounds.
@@ -826,10 +886,6 @@ void ClientControlledShellSurface::SetWidgetBounds(const gfx::Rect& bounds) {
widget_->GetNativeWindow()->SetBounds(gfx::Rect(origin, bounds.size()));
}
UpdateSurfaceBounds();
-
- // Render phantom windows when beyond the current display.
- if (resizer_)
- resizer_->Drag(GetMouseLocation(), 0);
}
gfx::Rect ClientControlledShellSurface::GetShadowBounds() const {
@@ -880,41 +936,6 @@ float ClientControlledShellSurface::GetScale() const {
return scale_;
}
-aura::Window* ClientControlledShellSurface::GetDragWindow() {
- // Set capture on the root surface rather than the focus surface, because
- // the client may destroy the latter during dragging/resizing.
- return root_surface() ? root_surface()->window() : nullptr;
-}
-
-std::unique_ptr<ash::WindowResizer>
-ClientControlledShellSurface::CreateWindowResizer(aura::Window* window,
- int component) {
- ash::wm::WindowState* window_state = GetWindowState();
- DCHECK(!window_state->drag_details());
- window_state->CreateDragDetails(GetMouseLocation(), component,
- wm::WINDOW_MOVE_SOURCE_MOUSE);
-
- std::unique_ptr<ash::WindowResizer> resizer =
- std::make_unique<CustomWindowResizer>(window_state);
-
- if (component == HTCAPTION) {
- // Chained with a CustomWindowResizer, DragWindowResizer does not handle
- // dragging. It only renders phantom windows and moves the window to the
- // target root window when dragging ends.
- resizer.reset(
- ash::DragWindowResizer::Create(resizer.release(), window_state));
- }
-
- return resizer;
-}
-
-bool ClientControlledShellSurface::OnMouseDragged(const ui::MouseEvent&) {
- // TODO(domlaskowski): When VKEY_ESCAPE is pressed during dragging, the client
- // destroys the window, but should instead revert the drag to be consistent
- // with ShellSurface::OnKeyEvent. See crbug.com/699746.
- return false;
-}
-
gfx::Rect ClientControlledShellSurface::GetWidgetBounds() const {
const ash::CustomFrameViewAsh* frame_view = GetFrameView();
if (frame_view->visible()) {
@@ -946,7 +967,7 @@ gfx::Point ClientControlledShellSurface::GetSurfaceOrigin() const {
// ClientControlledShellSurface, private:
void ClientControlledShellSurface::UpdateFrame() {
- if (!widget_ || !GetFrameView()->visible())
+ if (!widget_)
return;
gfx::Rect work_area =
display::Screen::GetScreen()
@@ -957,19 +978,18 @@ void ClientControlledShellSurface::UpdateFrame() {
if (window_state->IsMaximizedOrFullscreenOrPinned() &&
work_area.width() != geometry().width()) {
if (!wide_frame_) {
- wide_frame_ = ash::WideFrameView::Create(widget_);
+ wide_frame_ = std::make_unique<ash::WideFrameView>(widget_);
immersive_fullscreen_controller_->SetEnabled(
ash::ImmersiveFullscreenController::WINDOW_TYPE_OTHER, false);
wide_frame_->Init(immersive_fullscreen_controller_.get());
- wide_frame_->Show();
+ wide_frame_->GetWidget()->Show();
UpdateCaptionButtonModel();
}
} else {
if (wide_frame_) {
- wide_frame_->Close();
- wide_frame_ = nullptr;
immersive_fullscreen_controller_->SetEnabled(
ash::ImmersiveFullscreenController::WINDOW_TYPE_OTHER, false);
+ wide_frame_.reset();
GetFrameView()->InitImmersiveFullscreenControllerForView(
immersive_fullscreen_controller_.get());
UpdateCaptionButtonModel();
@@ -993,11 +1013,11 @@ void ClientControlledShellSurface::UpdateCaptionButtonModel() {
void ClientControlledShellSurface::UpdateBackdrop() {
aura::Window* window = widget_->GetNativeWindow();
- const display::Display display =
- display::Screen::GetScreen()->GetDisplayNearestWindow(window);
- bool enable_backdrop =
- (widget_->IsFullscreen() || widget_->IsMaximized()) &&
- !widget_->GetWindowBoundsInScreen().Contains(display.work_area());
+
+ // Always create a backdrop regardless of the geometry because
+ // maximized/fullscreen widget's geometry can be cropped.
+ bool enable_backdrop = (widget_->IsFullscreen() || widget_->IsMaximized());
+
ash::BackdropWindowMode target_backdrop_mode =
enable_backdrop ? ash::BackdropWindowMode::kEnabled
: ash::BackdropWindowMode::kAuto;
diff --git a/chromium/components/exo/client_controlled_shell_surface.h b/chromium/components/exo/client_controlled_shell_surface.h
index fcc906bdb0b..1c87f625f74 100644
--- a/chromium/components/exo/client_controlled_shell_surface.h
+++ b/chromium/components/exo/client_controlled_shell_surface.h
@@ -102,6 +102,10 @@ class ClientControlledShellSurface
bounds_changed_callback_ = bounds_changed_callback;
}
+ bool has_bounds_changed_callback() const {
+ return static_cast<bool>(bounds_changed_callback_);
+ }
+
// Set the callback to run when the drag operation started.
using DragStartedCallback = base::RepeatingCallback<void(int direction)>;
void set_drag_started_callback(const DragStartedCallback& callback) {
@@ -226,7 +230,7 @@ class ClientControlledShellSurface
static void SetClientControlledStateDelegateFactoryForTest(
const DelegateFactoryCallback& callback);
- ash::WideFrameView* wide_frame_for_test() { return wide_frame_; }
+ ash::WideFrameView* wide_frame_for_test() { return wide_frame_.get(); }
private:
class ScopedSetBoundsLocally;
@@ -237,11 +241,6 @@ class ClientControlledShellSurface
gfx::Rect GetShadowBounds() const override;
void InitializeWindowState(ash::wm::WindowState* window_state) override;
float GetScale() const override;
- aura::Window* GetDragWindow() override;
- std::unique_ptr<ash::WindowResizer> CreateWindowResizer(
- aura::Window* window,
- int component) override;
- bool OnMouseDragged(const ui::MouseEvent& event) override;
gfx::Rect GetWidgetBounds() const override;
gfx::Point GetSurfaceOrigin() const override;
@@ -300,7 +299,7 @@ class ClientControlledShellSurface
std::unique_ptr<ash::ImmersiveFullscreenController>
immersive_fullscreen_controller_;
- ash::WideFrameView* wide_frame_ = nullptr;
+ std::unique_ptr<ash::WideFrameView> wide_frame_;
std::unique_ptr<ui::CompositorLock> orientation_compositor_lock_;
diff --git a/chromium/components/exo/client_controlled_shell_surface_unittest.cc b/chromium/components/exo/client_controlled_shell_surface_unittest.cc
index f5075de535f..b252d6c2a0c 100644
--- a/chromium/components/exo/client_controlled_shell_surface_unittest.cc
+++ b/chromium/components/exo/client_controlled_shell_surface_unittest.cc
@@ -10,19 +10,25 @@
#include "ash/frame/custom_frame_view_ash.h"
#include "ash/frame/header_view.h"
#include "ash/frame/wide_frame_view.h"
+#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/public/interfaces/window_pin_type.mojom.h"
#include "ash/shell.h"
#include "ash/shell_test_api.h"
#include "ash/system/tray/system_tray.h"
+#include "ash/system/unified/unified_system_tray.h"
+#include "ash/wm/drag_window_resizer.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/window_positioning_utils.h"
+#include "ash/wm/window_resizer.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ash/wm/wm_event.h"
#include "ash/wm/workspace_controller_test_api.h"
+#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
+#include "cc/paint/display_item_list.h"
#include "components/exo/buffer.h"
#include "components/exo/display.h"
#include "components/exo/pointer.h"
@@ -31,7 +37,9 @@
#include "components/exo/test/exo_test_base.h"
#include "components/exo/test/exo_test_helper.h"
#include "components/exo/wm_helper.h"
+#include "third_party/skia/include/utils/SkNoDrawCanvas.h"
#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tree_host.h"
@@ -41,6 +49,7 @@
#include "ui/events/base_event_utils.h"
#include "ui/events/event_targeter.h"
#include "ui/events/test/event_generator.h"
+#include "ui/views/paint_info.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/shadow_controller.h"
#include "ui/wm/core/shadow_types.h"
@@ -71,6 +80,27 @@ void EnableTabletMode(bool enable) {
enable);
}
+// A canvas that just logs when a text blob is drawn.
+class TestCanvas : public SkNoDrawCanvas {
+ public:
+ TestCanvas() : SkNoDrawCanvas(100, 100) {}
+ ~TestCanvas() override {}
+
+ void onDrawTextBlob(const SkTextBlob*,
+ SkScalar,
+ SkScalar,
+ const SkPaint&) override {
+ text_was_drawn_ = true;
+ }
+
+ bool text_was_drawn() const { return text_was_drawn_; }
+
+ private:
+ bool text_was_drawn_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(TestCanvas);
+};
+
} // namespace
TEST_F(ClientControlledShellSurfaceTest, SetPinned) {
@@ -489,6 +519,7 @@ TEST_F(ClientControlledShellSurfaceTest, Frame) {
// AutoHide
surface->SetFrame(SurfaceFrameType::AUTOHIDE);
+ surface->Commit();
EXPECT_TRUE(frame_view->visible());
EXPECT_EQ(fullscreen_bounds, widget->GetWindowBoundsInScreen());
EXPECT_EQ(fullscreen_bounds,
@@ -521,6 +552,73 @@ TEST_F(ClientControlledShellSurfaceTest, Frame) {
EXPECT_EQ(client_bounds, widget->GetWindowBoundsInScreen());
EXPECT_EQ(client_bounds,
frame_view->GetClientBoundsForWindowBounds(client_bounds));
+
+ // Test NONE -> AUTOHIDE -> NONE
+ shell_surface->SetMaximized();
+ shell_surface->SetGeometry(fullscreen_bounds);
+ surface->SetFrame(SurfaceFrameType::AUTOHIDE);
+ surface->Commit();
+ EXPECT_TRUE(frame_view->visible());
+ EXPECT_TRUE(frame_view->GetHeaderView()->in_immersive_mode());
+ surface->SetFrame(SurfaceFrameType::NONE);
+ surface->Commit();
+ EXPECT_FALSE(frame_view->visible());
+ EXPECT_FALSE(frame_view->GetHeaderView()->in_immersive_mode());
+}
+
+namespace {
+
+class TestEventHandler : public ui::EventHandler {
+ public:
+ TestEventHandler() = default;
+ ~TestEventHandler() override = default;
+
+ // ui::EventHandler:
+ void OnMouseEvent(ui::MouseEvent* event) override { received_event_ = true; }
+
+ bool received_event() const { return received_event_; }
+
+ private:
+ bool received_event_ = false;
+ DISALLOW_COPY_AND_ASSIGN(TestEventHandler);
+};
+
+} // namespace
+
+TEST_F(ClientControlledShellSurfaceTest, NoSynthesizedEventOnFrameChange) {
+ UpdateDisplay("800x600");
+
+ gfx::Size buffer_size(256, 256);
+ std::unique_ptr<Buffer> buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+ std::unique_ptr<Surface> surface(new Surface);
+
+ gfx::Rect fullscreen_bounds(0, 0, 800, 600);
+
+ auto shell_surface =
+ exo_test_helper()->CreateClientControlledShellSurface(surface.get());
+ surface->Attach(buffer.get());
+ surface->SetFrame(SurfaceFrameType::NORMAL);
+ surface->Commit();
+
+ // Maximized
+ shell_surface->SetMaximized();
+ shell_surface->SetGeometry(fullscreen_bounds);
+ surface->Commit();
+
+ // AutoHide
+ base::RunLoop().RunUntilIdle();
+ auto* env = aura::Env::GetInstance();
+ gfx::Rect cropped_fullscreen_bounds(0, 0, 800, 400);
+ env->SetLastMouseLocation(gfx::Point(100, 30));
+ TestEventHandler handler;
+ env->AddPreTargetHandler(&handler);
+ surface->SetFrame(SurfaceFrameType::AUTOHIDE);
+ shell_surface->SetGeometry(cropped_fullscreen_bounds);
+ surface->Commit();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(handler.received_event());
+ env->RemovePreTargetHandler(&handler);
}
TEST_F(ClientControlledShellSurfaceTest, CompositorLockInRotation) {
@@ -563,6 +661,10 @@ TEST_F(ClientControlledShellSurfaceTest, CompositorLockInRotation) {
// If system tray is shown by click. It should be activated if user presses tab
// key while shell surface is active.
TEST_F(ClientControlledShellSurfaceTest, KeyboardNavigationWithSystemTray) {
+ // This is old SystemTray version.
+ if (ash::features::IsSystemTrayUnifiedEnabled())
+ return;
+
const gfx::Size buffer_size(800, 600);
std::unique_ptr<Buffer> buffer(
new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
@@ -588,9 +690,9 @@ TEST_F(ClientControlledShellSurfaceTest, KeyboardNavigationWithSystemTray) {
system_tray->GetSystemBubble()->bubble_view()->GetWidget()->IsActive());
// Send tab key event.
- ui::test::EventGenerator& event_generator = GetEventGenerator();
- event_generator.PressKey(ui::VKEY_TAB, ui::EF_NONE);
- event_generator.ReleaseKey(ui::VKEY_TAB, ui::EF_NONE);
+ ui::test::EventGenerator* event_generator = GetEventGenerator();
+ event_generator->PressKey(ui::VKEY_TAB, ui::EF_NONE);
+ event_generator->ReleaseKey(ui::VKEY_TAB, ui::EF_NONE);
// Confirm that system tray is activated.
EXPECT_FALSE(shell_surface->GetWidget()->IsActive());
@@ -598,6 +700,47 @@ TEST_F(ClientControlledShellSurfaceTest, KeyboardNavigationWithSystemTray) {
system_tray->GetSystemBubble()->bubble_view()->GetWidget()->IsActive());
}
+// If system tray is shown by click. It should be activated if user presses tab
+// key while shell surface is active.
+TEST_F(ClientControlledShellSurfaceTest,
+ KeyboardNavigationWithUnifiedSystemTray) {
+ // This is UnifiedSystemTray version.
+ if (!ash::features::IsSystemTrayUnifiedEnabled())
+ return;
+
+ const gfx::Size buffer_size(800, 600);
+ std::unique_ptr<Buffer> buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+ std::unique_ptr<Surface> surface(new Surface());
+ auto shell_surface =
+ exo_test_helper()->CreateClientControlledShellSurface(surface.get());
+
+ surface->Attach(buffer.get());
+ surface->Commit();
+
+ EXPECT_TRUE(shell_surface->GetWidget()->IsActive());
+
+ // Show system tray by perfoming a gesture tap at tray.
+ ash::UnifiedSystemTray* system_tray = GetPrimaryUnifiedSystemTray();
+ ui::GestureEvent tap(0, 0, 0, base::TimeTicks(),
+ ui::GestureEventDetails(ui::ET_GESTURE_TAP));
+ system_tray->PerformAction(tap);
+ ASSERT_TRUE(system_tray->GetWidget());
+
+ // Confirm that system tray is not active at this time.
+ EXPECT_TRUE(shell_surface->GetWidget()->IsActive());
+ EXPECT_FALSE(system_tray->IsBubbleActive());
+
+ // Send tab key event.
+ ui::test::EventGenerator* event_generator = GetEventGenerator();
+ event_generator->PressKey(ui::VKEY_TAB, ui::EF_NONE);
+ event_generator->ReleaseKey(ui::VKEY_TAB, ui::EF_NONE);
+
+ // Confirm that system tray is activated.
+ EXPECT_FALSE(shell_surface->GetWidget()->IsActive());
+ EXPECT_TRUE(system_tray->IsBubbleActive());
+}
+
TEST_F(ClientControlledShellSurfaceTest, Maximize) {
gfx::Size buffer_size(256, 256);
std::unique_ptr<Buffer> buffer(
@@ -615,11 +758,11 @@ TEST_F(ClientControlledShellSurfaceTest, Maximize) {
EXPECT_TRUE(HasBackdrop());
EXPECT_TRUE(shell_surface->GetWidget()->IsMaximized());
- // Enable backdrop only if the shell surface doesn't cover the display.
+ // We always show backdrop because the window may be cropped.
display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
shell_surface->SetGeometry(display.bounds());
surface->Commit();
- EXPECT_FALSE(HasBackdrop());
+ EXPECT_TRUE(HasBackdrop());
shell_surface->SetGeometry(gfx::Rect(0, 0, 100, display.bounds().height()));
surface->Commit();
@@ -678,11 +821,11 @@ TEST_F(ClientControlledShellSurfaceTest, SetFullscreen) {
surface->Commit();
EXPECT_TRUE(HasBackdrop());
- // Enable backdrop only if the shell surface doesn't cover the display.
+ // We always show backdrop becaues the window can be cropped.
display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
shell_surface->SetGeometry(display.bounds());
surface->Commit();
- EXPECT_FALSE(HasBackdrop());
+ EXPECT_TRUE(HasBackdrop());
shell_surface->SetGeometry(gfx::Rect(0, 0, 100, display.bounds().height()));
surface->Commit();
@@ -975,21 +1118,188 @@ TEST_F(ClientControlledShellSurfaceTest, ClientIniatedResize) {
ASSERT_FALSE(window_state->is_dragged());
// Client can start drag only when the mouse is pressed on the widget.
- ui::test::EventGenerator& event_generator = GetEventGenerator();
- event_generator.MoveMouseToCenterOf(window);
- event_generator.PressLeftButton();
+ ui::test::EventGenerator* event_generator = GetEventGenerator();
+ event_generator->MoveMouseToCenterOf(window);
+ event_generator->PressLeftButton();
shell_surface->StartDrag(HTTOP, gfx::Point(0, 0));
ASSERT_TRUE(window_state->is_dragged());
- event_generator.ReleaseLeftButton();
+ event_generator->ReleaseLeftButton();
ASSERT_FALSE(window_state->is_dragged());
// Press pressed outside of the window.
- event_generator.MoveMouseTo(gfx::Point(200, 50));
- event_generator.PressLeftButton();
+ event_generator->MoveMouseTo(gfx::Point(200, 50));
+ event_generator->PressLeftButton();
shell_surface->StartDrag(HTTOP, gfx::Point(0, 0));
ASSERT_FALSE(window_state->is_dragged());
}
+namespace {
+
+class ClientControlledShellSurfaceDisplayTest : public test::ExoTestBase {
+ public:
+ ClientControlledShellSurfaceDisplayTest() = default;
+ ~ClientControlledShellSurfaceDisplayTest() override = default;
+
+ static ash::WindowResizer* CreateDragWindowResizer(
+ aura::Window* window,
+ const gfx::Point& point_in_parent,
+ int window_component) {
+ return ash::CreateWindowResizer(window, point_in_parent, window_component,
+ ::wm::WINDOW_MOVE_SOURCE_MOUSE)
+ .release();
+ }
+
+ int bounds_change_count() const { return bounds_change_count_; }
+
+ const std::vector<gfx::Rect>& requested_bounds() const {
+ return requested_bounds_;
+ }
+
+ const std::vector<int64_t>& requested_display_ids() const {
+ return requested_display_ids_;
+ }
+
+ void OnBoundsChangeEvent(ash::mojom::WindowStateType current_state,
+ ash::mojom::WindowStateType requested_state,
+ int64_t display_id,
+ const gfx::Rect& bounds,
+ bool is_resize,
+ int bounds_change) {
+ bounds_change_count_++;
+ requested_bounds_.push_back(bounds);
+ requested_display_ids_.push_back(display_id);
+ }
+
+ void Reset() {
+ bounds_change_count_ = 0;
+ requested_bounds_.clear();
+ requested_display_ids_.clear();
+ }
+
+ gfx::Point CalculateDragPoint(const ash::WindowResizer& resizer,
+ int delta_x,
+ int delta_y) {
+ gfx::Point location = resizer.GetInitialLocation();
+ location.set_x(location.x() + delta_x);
+ location.set_y(location.y() + delta_y);
+ return location;
+ }
+
+ private:
+ int bounds_change_count_ = 0;
+ std::vector<gfx::Rect> requested_bounds_;
+ std::vector<int64_t> requested_display_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClientControlledShellSurfaceDisplayTest);
+};
+
+} // namespace
+
+TEST_F(ClientControlledShellSurfaceDisplayTest, MoveToAnotherDisplayByDrag) {
+ UpdateDisplay("800x600,800x600");
+ aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
+ std::unique_ptr<Surface> surface(new Surface);
+ auto shell_surface =
+ exo_test_helper()->CreateClientControlledShellSurface(surface.get());
+ shell_surface->set_client_controlled_move_resize(false);
+
+ gfx::Size window_size(200, 200);
+ std::unique_ptr<Buffer> desktop_buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(window_size)));
+ surface->Attach(desktop_buffer.get());
+ gfx::Rect initial_bounds(-150, 10, 200, 200);
+ shell_surface->SetGeometry(initial_bounds);
+ surface->Commit();
+ shell_surface->GetWidget()->Show();
+
+ EXPECT_EQ(initial_bounds,
+ shell_surface->GetWidget()->GetWindowBoundsInScreen());
+
+ aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
+ EXPECT_EQ(root_windows[0], window->GetRootWindow());
+
+ std::unique_ptr<ash::WindowResizer> resizer(
+ CreateDragWindowResizer(window, gfx::Point(), HTCAPTION));
+
+ // Drag the pointer to the right. Once it reaches the right edge of the
+ // primary display, it warps to the secondary.
+ resizer->Drag(CalculateDragPoint(*resizer, 800, 0), 0);
+
+ shell_surface->set_bounds_changed_callback(base::BindRepeating(
+ &ClientControlledShellSurfaceDisplayTest::OnBoundsChangeEvent,
+ base::Unretained(this)));
+
+ resizer->CompleteDrag();
+
+ EXPECT_EQ(root_windows[1], window->GetRootWindow());
+ ASSERT_EQ(1, bounds_change_count());
+ EXPECT_EQ(gfx::Rect(650, 10, 200, 200), requested_bounds()[0]);
+
+ display::Display secondary_display =
+ display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]);
+ EXPECT_EQ(secondary_display.id(), requested_display_ids()[0]);
+}
+
+TEST_F(ClientControlledShellSurfaceDisplayTest,
+ MoveToAnotherDisplayByShortcut) {
+ UpdateDisplay("400x600,800x600");
+ aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
+ std::unique_ptr<Surface> surface(new Surface);
+ auto shell_surface =
+ exo_test_helper()->CreateClientControlledShellSurface(surface.get());
+ shell_surface->set_client_controlled_move_resize(false);
+
+ gfx::Size window_size(200, 200);
+ std::unique_ptr<Buffer> desktop_buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(window_size)));
+ surface->Attach(desktop_buffer.get());
+ gfx::Rect initial_bounds(-174, 10, 200, 200);
+ shell_surface->SetGeometry(initial_bounds);
+ surface->Commit();
+ shell_surface->GetWidget()->Show();
+
+ EXPECT_EQ(initial_bounds,
+ shell_surface->GetWidget()->GetWindowBoundsInScreen());
+
+ aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
+ EXPECT_EQ(root_windows[0], window->GetRootWindow());
+
+ shell_surface->set_bounds_changed_callback(base::BindRepeating(
+ &ClientControlledShellSurfaceDisplayTest::OnBoundsChangeEvent,
+ base::Unretained(this)));
+
+ display::Display primary_display =
+ display::Screen::GetScreen()->GetPrimaryDisplay();
+ display::Display secondary_display =
+ display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]);
+
+ EXPECT_TRUE(ash::wm::MoveWindowToDisplay(window, secondary_display.id()));
+
+ EXPECT_EQ(root_windows[1], window->GetRootWindow());
+ ASSERT_EQ(1, bounds_change_count());
+ EXPECT_EQ(gfx::Rect(226, 10, 200, 200), requested_bounds()[0]);
+ EXPECT_EQ(secondary_display.id(), requested_display_ids()[0]);
+
+ gfx::Rect secondary_position(1100, 10, 200, 200);
+ shell_surface->SetGeometry(secondary_position);
+ surface->Commit();
+ EXPECT_EQ(secondary_position, window->GetBoundsInScreen());
+
+ Reset();
+
+ // Moving to the outside of another display. It should first try to
+ // move to outside, then move it back to inside.
+ EXPECT_TRUE(ash::wm::MoveWindowToDisplay(window, primary_display.id()));
+ EXPECT_EQ(root_windows[0], window->GetRootWindow());
+ ASSERT_EQ(2, bounds_change_count());
+ // Should fit in the primary display.
+ EXPECT_EQ(gfx::Rect(700, 10, 200, 200), requested_bounds()[0]);
+ EXPECT_EQ(primary_display.id(), requested_display_ids()[0]);
+
+ EXPECT_EQ(gfx::Rect(374, 10, 200, 200), requested_bounds()[1]);
+ EXPECT_EQ(primary_display.id(), requested_display_ids()[1]);
+}
+
TEST_F(ClientControlledShellSurfaceTest, CaptionButtonModel) {
std::unique_ptr<Surface> surface(new Surface);
auto shell_surface =
@@ -1053,33 +1363,57 @@ TEST_F(ClientControlledShellSurfaceTest, CaptionButtonModel) {
EXPECT_TRUE(container->model()->InZoomMode());
}
+// Makes sure that the "extra title" is respected by the window frame. When not
+// set, there should be no text in the window frame, but the window's name
+// should still be set (for overview mode, accessibility, etc.). When the debug
+// text is set, the window frame should paint it.
TEST_F(ClientControlledShellSurfaceTest, SetExtraTitle) {
std::unique_ptr<Buffer> buffer(
- new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(gfx::Size(64, 64))));
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(gfx::Size(640, 64))));
std::unique_ptr<Surface> surface(new Surface);
auto shell_surface =
exo_test_helper()->CreateClientControlledShellSurface(surface.get());
surface->Attach(buffer.get());
surface->Commit();
+ shell_surface->GetWidget()->Show();
- // The window title should include the debugging info, if any, and should only
- // be shown (in the frame) when there is debugging info. See
- // https://crbug.com/831383.
+ const base::string16 window_title(base::ASCIIToUTF16("title"));
+ shell_surface->SetTitle(window_title);
const aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
- const views::WidgetDelegate* widget_delegate =
- shell_surface->GetWidget()->widget_delegate();
-
- shell_surface->SetExtraTitle(base::ASCIIToUTF16("extra"));
- EXPECT_EQ(base::ASCIIToUTF16(" (extra)"), window->GetTitle());
- EXPECT_TRUE(widget_delegate->ShouldShowWindowTitle());
+ EXPECT_EQ(window_title, window->GetTitle());
+ EXPECT_FALSE(
+ shell_surface->GetWidget()->widget_delegate()->ShouldShowWindowTitle());
+
+ // Paints the frame and returns whether text was drawn. Unforunately the text
+ // is a blob so its actual value can't be detected.
+ auto paint_does_draw_text = [&shell_surface]() {
+ TestCanvas canvas;
+ shell_surface->OnSetFrame(SurfaceFrameType::NORMAL);
+ ash::CustomFrameViewAsh* frame_view = static_cast<ash::CustomFrameViewAsh*>(
+ shell_surface->GetWidget()->non_client_view()->frame_view());
+ frame_view->SetVisible(true);
+ // Paint to a layer so we can pass a root PaintInfo.
+ frame_view->GetHeaderView()->SetPaintToLayer();
+ gfx::Rect bounds(100, 100);
+ auto list = base::MakeRefCounted<cc::DisplayItemList>();
+ frame_view->GetHeaderView()->Paint(views::PaintInfo::CreateRootPaintInfo(
+ ui::PaintContext(list.get(), 1.f, bounds, false), bounds.size()));
+ list->Finalize();
+ list->Raster(&canvas);
+ return canvas.text_was_drawn();
+ };
- shell_surface->SetTitle(base::ASCIIToUTF16("title"));
- EXPECT_EQ(base::ASCIIToUTF16("title (extra)"), window->GetTitle());
- EXPECT_TRUE(widget_delegate->ShouldShowWindowTitle());
+ EXPECT_FALSE(paint_does_draw_text());
+ EXPECT_FALSE(
+ shell_surface->GetWidget()->widget_delegate()->ShouldShowWindowTitle());
- shell_surface->SetExtraTitle(base::string16());
- EXPECT_EQ(base::ASCIIToUTF16("title"), window->GetTitle());
- EXPECT_FALSE(widget_delegate->ShouldShowWindowTitle());
+ // Setting the extra title/debug text won't change the window's title, but it
+ // will be drawn by the frame header.
+ shell_surface->SetExtraTitle(base::ASCIIToUTF16("extra"));
+ EXPECT_EQ(window_title, window->GetTitle());
+ EXPECT_TRUE(paint_does_draw_text());
+ EXPECT_FALSE(
+ shell_surface->GetWidget()->widget_delegate()->ShouldShowWindowTitle());
}
TEST_F(ClientControlledShellSurfaceTest, WideFrame) {
@@ -1099,12 +1433,22 @@ TEST_F(ClientControlledShellSurfaceTest, WideFrame) {
ASSERT_TRUE(wide_frame);
EXPECT_FALSE(wide_frame->header_view()->in_immersive_mode());
- // Set AutoHide mode.
+ // Test AUTOHIDE -> NORMAL
surface->SetFrame(SurfaceFrameType::AUTOHIDE);
+ surface->Commit();
EXPECT_TRUE(wide_frame->header_view()->in_immersive_mode());
- // Exit AutoHide mode.
surface->SetFrame(SurfaceFrameType::NORMAL);
+ surface->Commit();
+ EXPECT_FALSE(wide_frame->header_view()->in_immersive_mode());
+
+ // Test AUTOHIDE -> NONE
+ surface->SetFrame(SurfaceFrameType::AUTOHIDE);
+ surface->Commit();
+ EXPECT_TRUE(wide_frame->header_view()->in_immersive_mode());
+
+ surface->SetFrame(SurfaceFrameType::NONE);
+ surface->Commit();
EXPECT_FALSE(wide_frame->header_view()->in_immersive_mode());
// Unmaximize it and the frame should be normal.
@@ -1225,4 +1569,32 @@ TEST_F(ClientControlledShellSurfaceTest, AdjustBoundsLocally) {
EXPECT_EQ(gfx::Rect(774, 0, 200, 300), requested_bounds);
}
+TEST_F(ClientControlledShellSurfaceTest, SnappedInTabletMode) {
+ gfx::Size buffer_size(256, 256);
+ std::unique_ptr<Buffer> buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+ std::unique_ptr<Surface> surface(new Surface);
+ auto shell_surface(
+ exo_test_helper()->CreateClientControlledShellSurface(surface.get()));
+ surface->Attach(buffer.get());
+ surface->Commit();
+ shell_surface->GetWidget()->Show();
+ auto* window = shell_surface->GetWidget()->GetNativeWindow();
+ auto* window_state = ash::wm::GetWindowState(window);
+
+ EnableTabletMode(true);
+
+ ash::wm::WMEvent event(ash::wm::WM_EVENT_SNAP_LEFT);
+ window_state->OnWMEvent(&event);
+ EXPECT_EQ(window_state->GetStateType(),
+ ash::mojom::WindowStateType::LEFT_SNAPPED);
+
+ ash::CustomFrameViewAsh* frame_view = static_cast<ash::CustomFrameViewAsh*>(
+ shell_surface->GetWidget()->non_client_view()->frame_view());
+ // Snapped window can also use auto hide.
+ surface->SetFrame(SurfaceFrameType::AUTOHIDE);
+ EXPECT_TRUE(frame_view->visible());
+ EXPECT_TRUE(frame_view->GetHeaderView()->in_immersive_mode());
+}
+
} // namespace exo
diff --git a/chromium/components/exo/display.cc b/chromium/components/exo/display.cc
index ebc4804b37f..7b515e70c87 100644
--- a/chromium/components/exo/display.cc
+++ b/chromium/components/exo/display.cc
@@ -15,6 +15,8 @@
#include "components/exo/client_controlled_shell_surface.h"
#include "components/exo/data_device.h"
#include "components/exo/file_helper.h"
+#include "components/exo/input_method_surface.h"
+#include "components/exo/input_method_surface_manager.h"
#include "components/exo/notification_surface.h"
#include "components/exo/notification_surface_manager.h"
#include "components/exo/shared_memory.h"
@@ -40,11 +42,13 @@ namespace exo {
////////////////////////////////////////////////////////////////////////////////
// Display, public:
-Display::Display() : Display(nullptr, std::unique_ptr<FileHelper>()) {}
+Display::Display() : Display(nullptr, nullptr, std::unique_ptr<FileHelper>()) {}
Display::Display(NotificationSurfaceManager* notification_surface_manager,
+ InputMethodSurfaceManager* input_method_surface_manager,
std::unique_ptr<FileHelper> file_helper)
: notification_surface_manager_(notification_surface_manager),
+ input_method_surface_manager_(input_method_surface_manager),
file_helper_(std::move(file_helper))
#if defined(USE_OZONE)
,
@@ -202,4 +206,24 @@ std::unique_ptr<DataDevice> Display::CreateDataDevice(
return std::make_unique<DataDevice>(delegate, &seat_, file_helper_.get());
}
+std::unique_ptr<InputMethodSurface> Display::CreateInputMethodSurface(
+ Surface* surface,
+ double default_device_scale_factor) {
+ TRACE_EVENT1("exo", "Display::CreateInputMethodSurface", "surface",
+ surface->AsTracedValue());
+
+ if (!input_method_surface_manager_) {
+ DLOG(ERROR) << "Input method surface cannot be registered";
+ return nullptr;
+ }
+
+ if (surface->HasSurfaceDelegate()) {
+ DLOG(ERROR) << "Surface has already been assigned a role";
+ return nullptr;
+ }
+
+ return std::make_unique<InputMethodSurface>(
+ input_method_surface_manager_, surface, default_device_scale_factor);
+}
+
} // namespace exo
diff --git a/chromium/components/exo/display.h b/chromium/components/exo/display.h
index 25a3bfffc56..8e2632f9fb0 100644
--- a/chromium/components/exo/display.h
+++ b/chromium/components/exo/display.h
@@ -30,6 +30,8 @@ class ClientControlledShellSurface;
class DataDevice;
class DataDeviceDelegate;
class FileHelper;
+class InputMethodSurface;
+class InputMethodSurfaceManager;
class NotificationSurface;
class NotificationSurfaceManager;
class SharedMemory;
@@ -49,6 +51,7 @@ class Display {
public:
Display();
Display(NotificationSurfaceManager* notification_surface_manager,
+ InputMethodSurfaceManager* input_method_surface_manager,
std::unique_ptr<FileHelper> file_helper);
~Display();
@@ -96,11 +99,17 @@ class Display {
// Creates a data device for a |delegate|.
std::unique_ptr<DataDevice> CreateDataDevice(DataDeviceDelegate* delegate);
+ // Creates a input method surface for a surface.
+ std::unique_ptr<InputMethodSurface> CreateInputMethodSurface(
+ Surface* surface,
+ double default_device_scale_factor);
+
// Obtains seat instance.
Seat* seat() { return &seat_; }
private:
NotificationSurfaceManager* const notification_surface_manager_;
+ InputMethodSurfaceManager* const input_method_surface_manager_;
std::unique_ptr<FileHelper> file_helper_;
Seat seat_;
diff --git a/chromium/components/exo/display_unittest.cc b/chromium/components/exo/display_unittest.cc
index fd80b228f84..7a598364ab3 100644
--- a/chromium/components/exo/display_unittest.cc
+++ b/chromium/components/exo/display_unittest.cc
@@ -240,7 +240,7 @@ class TestFileHelper : public FileHelper {
TEST_F(DisplayTest, CreateDataDevice) {
TestDataDeviceDelegate device_delegate;
- Display display(nullptr, std::make_unique<TestFileHelper>());
+ Display display(nullptr, nullptr, std::make_unique<TestFileHelper>());
std::unique_ptr<DataDevice> device =
display.CreateDataDevice(&device_delegate);
diff --git a/chromium/components/exo/input_method_surface.cc b/chromium/components/exo/input_method_surface.cc
new file mode 100644
index 00000000000..e24072d3094
--- /dev/null
+++ b/chromium/components/exo/input_method_surface.cc
@@ -0,0 +1,39 @@
+// 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/input_method_surface.h"
+
+#include "ash/public/cpp/shell_window_ids.h"
+#include "components/exo/input_method_surface_manager.h"
+
+namespace exo {
+
+InputMethodSurface::InputMethodSurface(InputMethodSurfaceManager* manager,
+ Surface* surface,
+ double default_device_scale_factor)
+ : ClientControlledShellSurface(
+ surface,
+ true /* can_minimize */,
+ ash::kShellWindowId_ArcVirtualKeyboardContainer),
+ manager_(manager),
+ added_to_manager_(false) {
+ SetScale(default_device_scale_factor);
+ host_window()->SetName("ExoInputMethodSurface");
+}
+
+InputMethodSurface::~InputMethodSurface() {
+ if (added_to_manager_)
+ manager_->RemoveSurface(this);
+}
+
+void InputMethodSurface::OnSurfaceCommit() {
+ ClientControlledShellSurface::OnSurfaceCommit();
+
+ if (!added_to_manager_) {
+ added_to_manager_ = true;
+ manager_->AddSurface(this);
+ }
+}
+
+} // namespace exo
diff --git a/chromium/components/exo/input_method_surface.h b/chromium/components/exo/input_method_surface.h
new file mode 100644
index 00000000000..fe2a7009a7b
--- /dev/null
+++ b/chromium/components/exo/input_method_surface.h
@@ -0,0 +1,37 @@
+// 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_INPUT_METHOD_SURFACE_H_
+#define COMPONENTS_EXO_INPUT_METHOD_SURFACE_H_
+
+#include "base/macros.h"
+#include "components/exo/client_controlled_shell_surface.h"
+#include "components/exo/surface_delegate.h"
+#include "components/exo/surface_observer.h"
+
+namespace exo {
+
+class InputMethodSurfaceManager;
+
+// Handles input method surface role of a given surface.
+class InputMethodSurface : public ClientControlledShellSurface {
+ public:
+ InputMethodSurface(InputMethodSurfaceManager* manager,
+ Surface* surface,
+ double default_device_scale_factor);
+ ~InputMethodSurface() override;
+
+ // Overridden from SurfaceDelegate:
+ void OnSurfaceCommit() override;
+
+ private:
+ InputMethodSurfaceManager* const manager_;
+ bool added_to_manager_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(InputMethodSurface);
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_INPUT_METHOD_SURFACE_H_
diff --git a/chromium/components/exo/input_method_surface_manager.h b/chromium/components/exo/input_method_surface_manager.h
new file mode 100644
index 00000000000..f9ea3b7d340
--- /dev/null
+++ b/chromium/components/exo/input_method_surface_manager.h
@@ -0,0 +1,28 @@
+// 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_INPUT_METHOD_SURFACE_MANAGER_H_
+#define COMPONENTS_EXO_INPUT_METHOD_SURFACE_MANAGER_H_
+
+namespace exo {
+
+class InputMethodSurface;
+
+class InputMethodSurfaceManager {
+ public:
+ virtual ~InputMethodSurfaceManager() = default;
+
+ // Gets the InputMethodSurface currently used.
+ virtual InputMethodSurface* GetSurface() const = 0;
+
+ // Adds an InputMethodSurface to the manager.
+ virtual void AddSurface(InputMethodSurface* surface) = 0;
+
+ // Removes a InputMethodSurface from the manager.
+ virtual void RemoveSurface(InputMethodSurface* surface) = 0;
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_INPUT_METHOD_SURFACE_MANAGER_H_
diff --git a/chromium/components/exo/keyboard.cc b/chromium/components/exo/keyboard.cc
index 2d3b14a5300..3efdfa27712 100644
--- a/chromium/components/exo/keyboard.cc
+++ b/chromium/components/exo/keyboard.cc
@@ -4,12 +4,15 @@
#include "components/exo/keyboard.h"
+#include "ash/public/cpp/app_types.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/exo/keyboard_delegate.h"
#include "components/exo/keyboard_device_configuration_delegate.h"
#include "components/exo/seat.h"
+#include "components/exo/shell_surface.h"
#include "components/exo/surface.h"
#include "components/exo/wm_helper.h"
+#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/window.h"
#include "ui/base/ime/input_method.h"
@@ -137,6 +140,20 @@ bool ProcessAcceleratorIfReserved(Surface* surface, ui::KeyEvent* event) {
return IsReservedAccelerator(event) && ProcessAccelerator(surface, event);
}
+// Returns true if surface belongs to an ARC application.
+// TODO(yhanada, https://crbug.com/847500): Remove this when we find a way
+// to fix https://crbug.com/847500 without breaking ARC++ apps.
+bool IsArcSurface(Surface* surface) {
+ aura::Window* window = surface->window();
+ for (; window; window = window->parent()) {
+ if (window->GetProperty(aura::client::kAppType) ==
+ static_cast<int>(ash::AppType::ARC_APP)) {
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -217,6 +234,10 @@ void Keyboard::OnKeyEvent(ui::KeyEvent* event) {
if (!focus_)
return;
+ // Ignore synthetic key repeat events.
+ if (event->is_repeat())
+ return;
+
// Process reserved accelerators before sending it to client.
if (ProcessAcceleratorIfReserved(focus_, event)) {
// Discard a key press event if it's a reserved accelerator and it's
@@ -236,12 +257,24 @@ void Keyboard::OnKeyEvent(ui::KeyEvent* event) {
delegate_->OnKeyboardModifiers(modifier_flags_);
}
+ // TODO(yhanada): This is a quick fix for https://crbug.com/859071. Remove
+ // ARC-specific code path once we can find a way to manage press/release
+ // events pair for synthetic events.
+ ui::DomCode physical_code =
+ seat_->physical_code_for_currently_processing_event();
+ if (physical_code == ui::DomCode::NONE && focus_belongs_to_arc_app_) {
+ // This key event is a synthetic event.
+ // Consider DomCode field of the event as a physical code
+ // for synthetic events when focus surface belongs to an ARC application.
+ physical_code = event->code();
+ }
+
switch (event->type()) {
- case ui::ET_KEY_PRESSED:
- // Process key press event if not already handled and not
- // already pressed.
- if (!consumed_by_ime && !event->handled() &&
- pressed_keys_.insert(event->code()).second) {
+ case ui::ET_KEY_PRESSED: {
+ // Process key press event if not already handled and not already pressed.
+ auto it = pressed_keys_.find(physical_code);
+ if (it == pressed_keys_.end() && !consumed_by_ime && !event->handled() &&
+ physical_code != ui::DomCode::NONE) {
uint32_t serial =
delegate_->OnKeyboardKey(event->time_stamp(), event->code(), true);
if (are_keyboard_key_acks_needed_) {
@@ -251,13 +284,21 @@ void Keyboard::OnKeyEvent(ui::KeyEvent* event) {
expiration_delay_for_pending_key_acks_}});
event->SetHandled();
}
+ // Keep track of both the physical code and potentially re-written
+ // code that this event generated.
+ pressed_keys_.insert({physical_code, event->code()});
}
- break;
- case ui::ET_KEY_RELEASED:
+ } break;
+ case ui::ET_KEY_RELEASED: {
// Process key release event if currently pressed.
- if (pressed_keys_.erase(event->code())) {
+ auto it = pressed_keys_.find(physical_code);
+ if (it != pressed_keys_.end()) {
+ // We use the code that was generate when the physical key was
+ // pressed rather than the current event code. This allows events
+ // to be re-written before dispatch, while still allowing the
+ // client to track the state of the physical keyboard.
uint32_t serial =
- delegate_->OnKeyboardKey(event->time_stamp(), event->code(), false);
+ delegate_->OnKeyboardKey(event->time_stamp(), it->second, false);
if (are_keyboard_key_acks_needed_) {
pending_key_acks_.insert(
{serial,
@@ -265,8 +306,9 @@ void Keyboard::OnKeyEvent(ui::KeyEvent* event) {
expiration_delay_for_pending_key_acks_}});
event->SetHandled();
}
+ pressed_keys_.erase(it);
}
- break;
+ } break;
default:
NOTREACHED();
break;
@@ -342,6 +384,7 @@ void Keyboard::SetFocus(Surface* surface) {
delegate_->OnKeyboardEnter(surface, pressed_keys_);
focus_ = surface;
focus_->AddSurfaceObserver(this);
+ focus_belongs_to_arc_app_ = IsArcSurface(surface);
}
}
diff --git a/chromium/components/exo/keyboard.h b/chromium/components/exo/keyboard.h
index 539e3fd4e4c..e2bef8e4548 100644
--- a/chromium/components/exo/keyboard.h
+++ b/chromium/components/exo/keyboard.h
@@ -111,8 +111,10 @@ class Keyboard : public ui::EventHandler,
// The current focus surface for the keyboard.
Surface* focus_ = nullptr;
- // Set of currently pressed keys.
- base::flat_set<ui::DomCode> pressed_keys_;
+ // Set of currently pressed keys. First value is a platform code and second
+ // value is the code that was delivered to client. See Seat.h for more
+ // details.
+ base::flat_map<ui::DomCode, ui::DomCode> pressed_keys_;
// Current set of modifier flags.
int modifier_flags_ = 0;
@@ -127,6 +129,11 @@ class Keyboard : public ui::EventHandler,
// Delay until a key state change expected to be acknowledged is expired.
const base::TimeDelta expiration_delay_for_pending_key_acks_;
+ // True when the ARC app window is focused.
+ // TODO(yhanada, https://crbug.com/847500): Remove this when we find a way to
+ // fix https://crbug.com/847500 without breaking ARC++ apps.
+ bool focus_belongs_to_arc_app_ = false;
+
base::ObserverList<KeyboardObserver> observer_list_;
base::WeakPtrFactory<Keyboard> weak_ptr_factory_;
diff --git a/chromium/components/exo/keyboard_delegate.h b/chromium/components/exo/keyboard_delegate.h
index fce7f17e6d9..3d7d6941ce2 100644
--- a/chromium/components/exo/keyboard_delegate.h
+++ b/chromium/components/exo/keyboard_delegate.h
@@ -25,7 +25,7 @@ class KeyboardDelegate {
// Called when keyboard focus enters a new valid target surface.
virtual void OnKeyboardEnter(
Surface* surface,
- const base::flat_set<ui::DomCode>& pressed_keys) = 0;
+ const base::flat_map<ui::DomCode, ui::DomCode>& pressed_keys) = 0;
// Called when keyboard focus leaves a valid target surface.
virtual void OnKeyboardLeave(Surface* surface) = 0;
diff --git a/chromium/components/exo/keyboard_unittest.cc b/chromium/components/exo/keyboard_unittest.cc
index da22538c409..8b21633907a 100644
--- a/chromium/components/exo/keyboard_unittest.cc
+++ b/chromium/components/exo/keyboard_unittest.cc
@@ -36,7 +36,7 @@ class MockKeyboardDelegate : public KeyboardDelegate {
MOCK_METHOD1(OnKeyboardDestroying, void(Keyboard*));
MOCK_CONST_METHOD1(CanAcceptKeyboardEventsForSurface, bool(Surface*));
MOCK_METHOD2(OnKeyboardEnter,
- void(Surface*, const base::flat_set<ui::DomCode>&));
+ void(Surface*, const base::flat_map<ui::DomCode, ui::DomCode>&));
MOCK_METHOD1(OnKeyboardLeave, void(Surface*));
MOCK_METHOD3(OnKeyboardKey, uint32_t(base::TimeTicks, ui::DomCode, bool));
MOCK_METHOD1(OnKeyboardModifiers, void(int));
@@ -80,6 +80,8 @@ TEST_F(KeyboardTest, OnKeyboardEnter) {
// Pressing key before Keyboard instance is created and surface has
// received focus.
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
+ seat.set_physical_code_for_currently_processing_event_for_testing(
+ ui::DomCode::US_A);
generator.PressKey(ui::VKEY_A, ui::EF_SHIFT_DOWN);
aura::client::FocusClient* focus_client =
@@ -95,9 +97,10 @@ TEST_F(KeyboardTest, OnKeyboardEnter) {
EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface.get()))
.WillOnce(testing::Return(true));
EXPECT_CALL(delegate, OnKeyboardModifiers(ui::EF_SHIFT_DOWN));
- EXPECT_CALL(delegate, OnKeyboardEnter(
- surface.get(),
- base::flat_set<ui::DomCode>({ui::DomCode::US_A})));
+ EXPECT_CALL(delegate,
+ OnKeyboardEnter(surface.get(),
+ base::flat_map<ui::DomCode, ui::DomCode>(
+ {{ui::DomCode::US_A, ui::DomCode::US_A}})));
focus_client->FocusWindow(nullptr);
focus_client->FocusWindow(surface->window());
// Surface should maintain keyboard focus when moved to top-level window.
@@ -112,7 +115,8 @@ TEST_F(KeyboardTest, OnKeyboardEnter) {
.WillOnce(testing::Return(true));
EXPECT_CALL(delegate, OnKeyboardModifiers(ui::EF_SHIFT_DOWN));
EXPECT_CALL(delegate,
- OnKeyboardEnter(surface.get(), base::flat_set<ui::DomCode>()));
+ OnKeyboardEnter(surface.get(),
+ base::flat_map<ui::DomCode, ui::DomCode>()));
focus_client->FocusWindow(surface->window()->GetToplevelWindow());
keyboard.reset();
@@ -139,7 +143,8 @@ TEST_F(KeyboardTest, OnKeyboardLeave) {
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(delegate, OnKeyboardModifiers(0));
EXPECT_CALL(delegate,
- OnKeyboardEnter(surface.get(), base::flat_set<ui::DomCode>()));
+ OnKeyboardEnter(surface.get(),
+ base::flat_map<ui::DomCode, ui::DomCode>()));
focus_client->FocusWindow(surface->window());
EXPECT_CALL(delegate, OnKeyboardLeave(surface.get()));
@@ -147,7 +152,8 @@ TEST_F(KeyboardTest, OnKeyboardLeave) {
EXPECT_CALL(delegate, OnKeyboardModifiers(0));
EXPECT_CALL(delegate,
- OnKeyboardEnter(surface.get(), base::flat_set<ui::DomCode>()));
+ OnKeyboardEnter(surface.get(),
+ base::flat_map<ui::DomCode, ui::DomCode>()));
focus_client->FocusWindow(surface->window());
EXPECT_CALL(delegate, OnKeyboardLeave(surface.get()));
@@ -178,12 +184,15 @@ TEST_F(KeyboardTest, OnKeyboardKey) {
.WillOnce(testing::Return(true));
EXPECT_CALL(delegate, OnKeyboardModifiers(0));
EXPECT_CALL(delegate,
- OnKeyboardEnter(surface.get(), base::flat_set<ui::DomCode>()));
+ OnKeyboardEnter(surface.get(),
+ base::flat_map<ui::DomCode, ui::DomCode>()));
focus_client->FocusWindow(surface->window());
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
// This should only generate a press event for KEY_A.
EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_A, true));
+ seat.set_physical_code_for_currently_processing_event_for_testing(
+ ui::DomCode::US_A);
generator.PressKey(ui::VKEY_A, 0);
// This should not generate another press event for KEY_A.
@@ -193,18 +202,35 @@ TEST_F(KeyboardTest, OnKeyboardKey) {
EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_A, false));
generator.ReleaseKey(ui::VKEY_A, 0);
+ // Test key event rewriting. In this case, ARROW_DOWN is rewritten to KEY_END
+ // as a result of ALT being pressed.
+ EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::END, true));
+ EXPECT_CALL(delegate, OnKeyboardModifiers(ui::EF_ALT_DOWN));
+ seat.set_physical_code_for_currently_processing_event_for_testing(
+ ui::DomCode::ARROW_DOWN);
+ generator.PressKey(ui::VKEY_END, ui::EF_ALT_DOWN);
+
+ // This should generate a release event for KEY_END as that is the key
+ // associated with the key press.
+ EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::END, false));
+ EXPECT_CALL(delegate, OnKeyboardModifiers(0));
+ generator.ReleaseKey(ui::VKEY_DOWN, 0);
+
// Press accelerator after surface lost focus.
EXPECT_CALL(delegate, OnKeyboardLeave(surface.get()));
focus_client->FocusWindow(nullptr);
+ seat.set_physical_code_for_currently_processing_event_for_testing(
+ ui::DomCode::US_W);
generator.PressKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
// Key should be pressed when focus returns.
EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface.get()))
.WillOnce(testing::Return(true));
EXPECT_CALL(delegate, OnKeyboardModifiers(ui::EF_CONTROL_DOWN));
- EXPECT_CALL(delegate, OnKeyboardEnter(
- surface.get(),
- base::flat_set<ui::DomCode>({ui::DomCode::US_W})));
+ EXPECT_CALL(delegate,
+ OnKeyboardEnter(surface.get(),
+ base::flat_map<ui::DomCode, ui::DomCode>(
+ {{ui::DomCode::US_W, ui::DomCode::US_W}})));
focus_client->FocusWindow(surface->window());
// Releasing accelerator when surface has focus should generate event.
@@ -235,19 +261,24 @@ TEST_F(KeyboardTest, OnKeyboardModifiers) {
.WillOnce(testing::Return(true));
EXPECT_CALL(delegate, OnKeyboardModifiers(0));
EXPECT_CALL(delegate,
- OnKeyboardEnter(surface.get(), base::flat_set<ui::DomCode>()));
+ OnKeyboardEnter(surface.get(),
+ base::flat_map<ui::DomCode, ui::DomCode>()));
focus_client->FocusWindow(surface->window());
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
// This should generate a modifier event.
EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_A, true));
EXPECT_CALL(delegate, OnKeyboardModifiers(ui::EF_SHIFT_DOWN));
+ seat.set_physical_code_for_currently_processing_event_for_testing(
+ ui::DomCode::US_A);
generator.PressKey(ui::VKEY_A, ui::EF_SHIFT_DOWN);
// This should generate another modifier event.
EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_B, true));
EXPECT_CALL(delegate,
OnKeyboardModifiers(ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN));
+ seat.set_physical_code_for_currently_processing_event_for_testing(
+ ui::DomCode::US_B);
generator.PressKey(ui::VKEY_B, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN);
// This should generate a third modifier event.
@@ -375,7 +406,8 @@ TEST_F(KeyboardTest, AckKeyboardKey) {
.WillOnce(testing::Return(true));
EXPECT_CALL(delegate, OnKeyboardModifiers(0));
EXPECT_CALL(delegate,
- OnKeyboardEnter(surface.get(), base::flat_set<ui::DomCode>()));
+ OnKeyboardEnter(surface.get(),
+ base::flat_map<ui::DomCode, ui::DomCode>()));
focus_client->FocusWindow(surface->window());
// If we don't set NeedKeyboardAckKeys to true, accelerators are always passed
@@ -387,6 +419,8 @@ TEST_F(KeyboardTest, AckKeyboardKey) {
ui::VKEY_W, ui::EF_CONTROL_DOWN,
ui::Accelerator::KeyState::PRESSED)))
.WillOnce(testing::Return(true));
+ seat.set_physical_code_for_currently_processing_event_for_testing(
+ ui::DomCode::US_W);
generator.PressKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
// Release KEY_W.
@@ -454,7 +488,8 @@ TEST_F(KeyboardTest, AckKeyboardKeyMoveFocus) {
.WillOnce(testing::Return(true));
EXPECT_CALL(delegate, OnKeyboardModifiers(0)).Times(1);
EXPECT_CALL(delegate,
- OnKeyboardEnter(surface.get(), base::flat_set<ui::DomCode>()));
+ OnKeyboardEnter(surface.get(),
+ base::flat_map<ui::DomCode, ui::DomCode>()));
focus_client->FocusWindow(surface->window());
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
@@ -464,6 +499,8 @@ TEST_F(KeyboardTest, AckKeyboardKeyMoveFocus) {
EXPECT_CALL(delegate, OnKeyboardModifiers(4)).Times(1);
EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_W, true))
.WillOnce(testing::Return(1));
+ seat.set_physical_code_for_currently_processing_event_for_testing(
+ ui::DomCode::US_W);
generator.PressKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
// Move focus from the window
@@ -497,7 +534,8 @@ TEST_F(KeyboardTest, AckKeyboardKeyExpired) {
.WillOnce(testing::Return(true));
EXPECT_CALL(delegate, OnKeyboardModifiers(0));
EXPECT_CALL(delegate,
- OnKeyboardEnter(surface.get(), base::flat_set<ui::DomCode>()));
+ OnKeyboardEnter(surface.get(),
+ base::flat_map<ui::DomCode, ui::DomCode>()));
focus_client->FocusWindow(surface->window());
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
@@ -507,6 +545,8 @@ TEST_F(KeyboardTest, AckKeyboardKeyExpired) {
EXPECT_CALL(delegate, OnKeyboardModifiers(4));
EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_W, true))
.WillOnce(testing::Return(1));
+ seat.set_physical_code_for_currently_processing_event_for_testing(
+ ui::DomCode::US_W);
generator.PressKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
// Keyboard processes pending events as if it's not handled if ack isnt' sent.
@@ -570,7 +610,8 @@ TEST_F(KeyboardTest, AckKeyboardKeyExpiredWithMovingFocusAccelerator) {
.WillOnce(testing::Return(true));
EXPECT_CALL(delegate, OnKeyboardModifiers(0));
EXPECT_CALL(delegate,
- OnKeyboardEnter(surface.get(), base::flat_set<ui::DomCode>()));
+ OnKeyboardEnter(surface.get(),
+ base::flat_map<ui::DomCode, ui::DomCode>()));
focus_client->FocusWindow(surface->window());
ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
@@ -580,6 +621,8 @@ TEST_F(KeyboardTest, AckKeyboardKeyExpiredWithMovingFocusAccelerator) {
EXPECT_CALL(delegate, OnKeyboardModifiers(4));
EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_W, true))
.WillOnce(testing::Return(1));
+ seat.set_physical_code_for_currently_processing_event_for_testing(
+ ui::DomCode::US_W);
generator.PressKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
// Wait until |ProcessExpiredPendingKeyAcks| is fired.
diff --git a/chromium/components/exo/layer_tree_frame_sink_holder.cc b/chromium/components/exo/layer_tree_frame_sink_holder.cc
index b9b95363e1f..2d916ed5b76 100644
--- a/chromium/components/exo/layer_tree_frame_sink_holder.cc
+++ b/chromium/components/exo/layer_tree_frame_sink_holder.cc
@@ -5,6 +5,7 @@
#include "components/exo/layer_tree_frame_sink_holder.h"
#include "ash/shell.h"
+#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "cc/trees/layer_tree_frame_sink.h"
#include "components/exo/surface_tree_host.h"
@@ -114,12 +115,6 @@ base::WeakPtr<LayerTreeFrameSinkHolder> LayerTreeFrameSinkHolder::GetWeakPtr() {
////////////////////////////////////////////////////////////////////////////////
// cc::LayerTreeFrameSinkClient overrides:
-void LayerTreeFrameSinkHolder::SetBeginFrameSource(
- viz::BeginFrameSource* source) {
- if (surface_tree_host_)
- surface_tree_host_->SetBeginFrameSource(source);
-}
-
base::Optional<viz::HitTestRegionList>
LayerTreeFrameSinkHolder::BuildHitTestData() {
return {};
@@ -130,8 +125,7 @@ void LayerTreeFrameSinkHolder::ReclaimResources(
for (auto& resource : resources) {
// Skip resources that are also in last frame. This can happen if
// the frame sink id changed.
- if (std::find(last_frame_resources_.begin(), last_frame_resources_.end(),
- resource.id) != last_frame_resources_.end()) {
+ if (base::ContainsValue(last_frame_resources_, resource.id)) {
continue;
}
auto it = release_callbacks_.find(resource.id);
@@ -153,18 +147,9 @@ void LayerTreeFrameSinkHolder::DidReceiveCompositorFrameAck() {
void LayerTreeFrameSinkHolder::DidPresentCompositorFrame(
uint32_t presentation_token,
- base::TimeTicks time,
- base::TimeDelta refresh,
- uint32_t flags) {
- if (surface_tree_host_)
- surface_tree_host_->DidPresentCompositorFrame(presentation_token, time,
- refresh, flags);
-}
-
-void LayerTreeFrameSinkHolder::DidDiscardCompositorFrame(
- uint32_t presentation_token) {
+ const gfx::PresentationFeedback& feedback) {
if (surface_tree_host_)
- surface_tree_host_->DidDiscardCompositorFrame(presentation_token);
+ surface_tree_host_->DidPresentCompositorFrame(presentation_token, feedback);
}
void LayerTreeFrameSinkHolder::DidLoseLayerTreeFrameSink() {
diff --git a/chromium/components/exo/layer_tree_frame_sink_holder.h b/chromium/components/exo/layer_tree_frame_sink_holder.h
index 4ffc4d49de8..89df17f4f6d 100644
--- a/chromium/components/exo/layer_tree_frame_sink_holder.h
+++ b/chromium/components/exo/layer_tree_frame_sink_holder.h
@@ -50,21 +50,20 @@ class LayerTreeFrameSinkHolder : public cc::LayerTreeFrameSinkClient,
base::WeakPtr<LayerTreeFrameSinkHolder> GetWeakPtr();
// Overridden from cc::LayerTreeFrameSinkClient:
- void SetBeginFrameSource(viz::BeginFrameSource* source) override;
+ void SetBeginFrameSource(viz::BeginFrameSource* source) override {}
base::Optional<viz::HitTestRegionList> BuildHitTestData() override;
void ReclaimResources(
const std::vector<viz::ReturnedResource>& resources) override;
void SetTreeActivationCallback(const base::Closure& callback) override {}
void DidReceiveCompositorFrameAck() override;
- void DidPresentCompositorFrame(uint32_t presentation_token,
- base::TimeTicks time,
- base::TimeDelta refresh,
- uint32_t flags) override;
- void DidDiscardCompositorFrame(uint32_t presentation_token) override;
+ void DidPresentCompositorFrame(
+ uint32_t presentation_token,
+ const gfx::PresentationFeedback& feedback) override;
void DidLoseLayerTreeFrameSink() override;
void OnDraw(const gfx::Transform& transform,
const gfx::Rect& viewport,
- bool resourceless_software_draw) override {}
+ bool resourceless_software_draw,
+ bool skip_draw) override {}
void SetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) override {}
void SetExternalTilePriorityConstraints(
const gfx::Rect& viewport_rect,
diff --git a/chromium/components/exo/notification_surface.cc b/chromium/components/exo/notification_surface.cc
index 6c3209a5e28..d589a7224b2 100644
--- a/chromium/components/exo/notification_surface.cc
+++ b/chromium/components/exo/notification_surface.cc
@@ -4,9 +4,11 @@
#include "components/exo/notification_surface.h"
+#include "ash/public/cpp/app_types.h"
#include "components/exo/notification_surface_manager.h"
#include "components/exo/shell_surface.h"
#include "components/exo/surface.h"
+#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/gfx/geometry/rect.h"
@@ -20,6 +22,8 @@ NotificationSurface::NotificationSurface(NotificationSurfaceManager* manager,
notification_key_(notification_key) {
surface->AddSurfaceObserver(this);
SetRootSurface(surface);
+ host_window()->SetProperty(aura::client::kAppType,
+ static_cast<int>(ash::AppType::ARC_APP));
host_window()->Show();
}
diff --git a/chromium/components/exo/pointer.cc b/chromium/components/exo/pointer.cc
index 6b6fd69fac4..08ec41d2fda 100644
--- a/chromium/components/exo/pointer.cc
+++ b/chromium/components/exo/pointer.cc
@@ -9,6 +9,7 @@
#include "ash/public/cpp/shell_window_ids.h"
#include "components/exo/pointer_delegate.h"
#include "components/exo/pointer_gesture_pinch_delegate.h"
+#include "components/exo/shell_surface_base.h"
#include "components/exo/surface.h"
#include "components/exo/wm_helper.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
@@ -44,13 +45,8 @@ const float kLargeCursorScale = 2.8f;
const double kLocatedEventEpsilonSquared = 1.0 / (2000.0 * 2000.0);
-// Synthesized events typically lack floating point precision so to avoid
-// generating mouse event jitter we consider the location of these events
-// to be the same as |location| if floored values match.
-bool SameLocation(const ui::LocatedEvent* event, const gfx::PointF& location) {
- if (event->flags() & ui::EF_IS_SYNTHESIZED)
- return event->location() == gfx::ToFlooredPoint(location);
-
+bool SameLocation(const gfx::PointF& location_in_target,
+ const gfx::PointF& location) {
// In general, it is good practice to compare floats using an epsilon.
// In particular, the mouse location_f() could differ between the
// MOUSE_PRESSED and MOUSE_RELEASED events. At MOUSE_RELEASED, it will have a
@@ -58,7 +54,7 @@ bool SameLocation(const ui::LocatedEvent* event, const gfx::PointF& location) {
// calculate it passing through all the hierarchy of windows, and that could
// generate rounding error. std::numeric_limits<float>::epsilon() is not big
// enough to catch this rounding error.
- gfx::Vector2dF offset = event->location_f() - location;
+ gfx::Vector2dF offset = location_in_target - location;
return offset.LengthSquared() < (2 * kLocatedEventEpsilonSquared);
}
@@ -159,6 +155,10 @@ void Pointer::SetCursor(Surface* surface, const gfx::Point& hotspot) {
}
void Pointer::SetCursorType(ui::CursorType cursor_type) {
+ // Early out if the pointer doesn't have a surface in focus.
+ if (!focus_surface_)
+ return;
+
if (cursor_ == cursor_type)
return;
cursor_ = cursor_type;
@@ -203,10 +203,16 @@ void Pointer::OnSurfaceDestroying(Surface* surface) {
void Pointer::OnMouseEvent(ui::MouseEvent* event) {
Surface* target = GetEffectiveTargetForEvent(event);
+ gfx::PointF location_in_target = event->location_f();
+ if (target) {
+ aura::Window::ConvertPointToTarget(
+ static_cast<aura::Window*>(event->target()), target->window(),
+ &location_in_target);
+ }
// Update focus if target is different than the current pointer focus.
if (target != focus_surface_)
- SetFocus(target, event->location_f(), event->button_flags());
+ SetFocus(target, location_in_target, event->button_flags());
if (!focus_surface_)
return;
@@ -218,8 +224,15 @@ void Pointer::OnMouseEvent(ui::MouseEvent* event) {
// here as mouse movement can generate both "moved" and "entered" events
// but OnPointerMotion should only be called if location changed since
// OnPointerEnter was called.
- if (!SameLocation(event, location_)) {
- location_ = event->location_f();
+ // For synthesized events, they typically lack floating point precision
+ // so to avoid generating mouse event jitter we consider the location of
+ // these events to be the same as |location| if floored values match.
+ bool same_location = !event->IsSynthesized()
+ ? SameLocation(location_in_target, location_)
+ : gfx::ToFlooredPoint(location_in_target) ==
+ gfx::ToFlooredPoint(location_);
+ if (!same_location) {
+ location_ = location_in_target;
delegate_->OnPointerMotion(event->time_stamp(), location_);
delegate_->OnPointerFrame();
}
@@ -334,6 +347,9 @@ void Pointer::OnCursorSizeChanged(ui::CursorSize cursor_size) {
void Pointer::OnCursorDisplayChanged(const display::Display& display) {
auto* cursor_client = WMHelper::GetInstance()->GetCursorClient();
+ // TODO(crbug.com/631103): CursorClient does not exist in mash yet.
+ if (!cursor_client)
+ return;
if (cursor_ == ui::CursorType::kCustom &&
cursor_client->GetCursor() == cursor_client->GetCursor()) {
// If the current cursor is still the one created by us,
@@ -357,9 +373,9 @@ void Pointer::OnDisplayConfigurationChanged() {
////////////////////////////////////////////////////////////////////////////////
// Pointer, private:
-Surface* Pointer::GetEffectiveTargetForEvent(ui::Event* event) const {
- Surface* target =
- Surface::AsSurface(static_cast<aura::Window*>(event->target()));
+Surface* Pointer::GetEffectiveTargetForEvent(ui::LocatedEvent* event) const {
+ Surface* target = ShellSurfaceBase::GetTargetSurfaceForLocatedEvent(event);
+
if (!target)
return nullptr;
@@ -459,6 +475,9 @@ void Pointer::OnCursorCaptured(const gfx::Point& hotspot,
void Pointer::UpdateCursor() {
auto* helper = WMHelper::GetInstance();
aura::client::CursorClient* cursor_client = helper->GetCursorClient();
+ // TODO(crbug.com/631103): CursorClient does not exist in mash yet.
+ if (!cursor_client)
+ return;
if (cursor_ == ui::CursorType::kCustom) {
SkBitmap bitmap = cursor_bitmap_;
diff --git a/chromium/components/exo/pointer.h b/chromium/components/exo/pointer.h
index a5dd5c88ac4..ee8dfc12c04 100644
--- a/chromium/components/exo/pointer.h
+++ b/chromium/components/exo/pointer.h
@@ -27,7 +27,7 @@ class CopyOutputResult;
}
namespace ui {
-class Event;
+class LocatedEvent;
class MouseEvent;
}
@@ -84,7 +84,7 @@ class Pointer : public SurfaceTreeHost,
private:
// Returns the effective target for |event|.
- Surface* GetEffectiveTargetForEvent(ui::Event* event) const;
+ Surface* GetEffectiveTargetForEvent(ui::LocatedEvent* event) const;
// Change pointer focus to |surface|.
void SetFocus(Surface* surface,
diff --git a/chromium/components/exo/pointer_unittest.cc b/chromium/components/exo/pointer_unittest.cc
index 00a3a0d9e50..9056fea7034 100644
--- a/chromium/components/exo/pointer_unittest.cc
+++ b/chromium/components/exo/pointer_unittest.cc
@@ -27,8 +27,6 @@
namespace exo {
namespace {
-using PointerTest = test::ExoTestBase;
-
class MockPointerDelegate : public PointerDelegate {
public:
MockPointerDelegate() {}
@@ -46,7 +44,29 @@ class MockPointerDelegate : public PointerDelegate {
MOCK_METHOD0(OnPointerFrame, void());
};
-TEST_F(PointerTest, SetCursor) {
+// Flaky on Linux Chromium OS ASan LSan. http://crbug.com/859020.
+#if defined(OS_CHROMEOS) && defined(ADDRESS_SANITIZER)
+#define MAYBE_PointerTest DISABLED_PointerTest
+#else
+#define MAYBE_PointerTest PointerTest
+#endif
+class MAYBE_PointerTest : public test::ExoTestBase {
+ public:
+ MAYBE_PointerTest() = default;
+
+ void SetUp() override {
+ test::ExoTestBase::SetUp();
+ // Sometimes underlying infra (i.e. X11 / Xvfb) may emit pointer events
+ // which can break MockPointerDelegate's expectations, so they should be
+ // consumed before starting. See https://crbug.com/854674.
+ RunAllPendingInMessageLoop();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MAYBE_PointerTest);
+};
+
+TEST_F(MAYBE_PointerTest, SetCursor) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
gfx::Size buffer_size(10, 10);
@@ -114,7 +134,7 @@ TEST_F(PointerTest, SetCursor) {
pointer.reset();
}
-TEST_F(PointerTest, SetCursorNull) {
+TEST_F(MAYBE_PointerTest, SetCursorNull) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
gfx::Size buffer_size(10, 10);
@@ -145,7 +165,7 @@ TEST_F(PointerTest, SetCursorNull) {
pointer.reset();
}
-TEST_F(PointerTest, SetCursorType) {
+TEST_F(MAYBE_PointerTest, SetCursorType) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
gfx::Size buffer_size(10, 10);
@@ -206,7 +226,39 @@ TEST_F(PointerTest, SetCursorType) {
pointer.reset();
}
-TEST_F(PointerTest, SetCursorAndSetCursorType) {
+TEST_F(MAYBE_PointerTest, SetCursorTypeOutsideOfSurface) {
+ 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();
+
+ MockPointerDelegate delegate;
+ std::unique_ptr<Pointer> pointer(new Pointer(&delegate));
+ ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
+
+ EXPECT_CALL(delegate, CanAcceptPointerEventsForSurface(surface.get()))
+ .WillRepeatedly(testing::Return(true));
+ generator.MoveMouseTo(surface->window()->GetBoundsInScreen().origin() -
+ gfx::Vector2d(1, 1));
+
+ pointer->SetCursorType(ui::CursorType::kIBeam);
+ RunAllPendingInMessageLoop();
+
+ EXPECT_EQ(nullptr, pointer->root_surface());
+ aura::client::CursorClient* cursor_client = aura::client::GetCursorClient(
+ shell_surface->GetWidget()->GetNativeWindow()->GetRootWindow());
+ // The cursor type shouldn't be the specified one, since the pointer is
+ // located outside of the surface.
+ EXPECT_NE(ui::CursorType::kIBeam, cursor_client->GetCursor().native_type());
+
+ EXPECT_CALL(delegate, OnPointerDestroying(pointer.get()));
+ pointer.reset();
+}
+
+TEST_F(MAYBE_PointerTest, SetCursorAndSetCursorType) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
gfx::Size buffer_size(10, 10);
@@ -274,7 +326,7 @@ TEST_F(PointerTest, SetCursorAndSetCursorType) {
pointer.reset();
}
-TEST_F(PointerTest, SetCursorNullAndSetCursorType) {
+TEST_F(MAYBE_PointerTest, SetCursorNullAndSetCursorType) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
gfx::Size buffer_size(10, 10);
@@ -318,7 +370,7 @@ TEST_F(PointerTest, SetCursorNullAndSetCursorType) {
pointer.reset();
}
-TEST_F(PointerTest, OnPointerEnter) {
+TEST_F(MAYBE_PointerTest, OnPointerEnter) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
gfx::Size buffer_size(10, 10);
@@ -341,7 +393,7 @@ TEST_F(PointerTest, OnPointerEnter) {
pointer.reset();
}
-TEST_F(PointerTest, OnPointerLeave) {
+TEST_F(MAYBE_PointerTest, OnPointerLeave) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
gfx::Size buffer_size(10, 10);
@@ -374,7 +426,7 @@ TEST_F(PointerTest, OnPointerLeave) {
pointer.reset();
}
-TEST_F(PointerTest, OnPointerMotion) {
+TEST_F(MAYBE_PointerTest, OnPointerMotion) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
gfx::Size buffer_size(10, 10);
@@ -447,7 +499,7 @@ TEST_F(PointerTest, OnPointerMotion) {
pointer.reset();
}
-TEST_F(PointerTest, OnPointerButton) {
+TEST_F(MAYBE_PointerTest, OnPointerButton) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
gfx::Size buffer_size(10, 10);
@@ -477,7 +529,7 @@ TEST_F(PointerTest, OnPointerButton) {
pointer.reset();
}
-TEST_F(PointerTest, OnPointerScroll) {
+TEST_F(MAYBE_PointerTest, OnPointerScroll) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
gfx::Size buffer_size(10, 10);
@@ -512,7 +564,7 @@ TEST_F(PointerTest, OnPointerScroll) {
pointer.reset();
}
-TEST_F(PointerTest, OnPointerScrollDiscrete) {
+TEST_F(MAYBE_PointerTest, OnPointerScrollDiscrete) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
gfx::Size buffer_size(10, 10);
@@ -540,7 +592,7 @@ TEST_F(PointerTest, OnPointerScrollDiscrete) {
pointer.reset();
}
-TEST_F(PointerTest, IgnorePointerEventDuringModal) {
+TEST_F(MAYBE_PointerTest, IgnorePointerEventDuringModal) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
std::unique_ptr<Buffer> buffer(
diff --git a/chromium/components/exo/seat.cc b/chromium/components/exo/seat.cc
index a8b36f9d9ab..eae1f607133 100644
--- a/chromium/components/exo/seat.cc
+++ b/chromium/components/exo/seat.cc
@@ -15,6 +15,8 @@
#include "ui/aura/client/focus_client.h"
#include "ui/base/clipboard/clipboard_monitor.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
+#include "ui/events/event_utils.h"
+#include "ui/events/platform/platform_event_source.h"
namespace exo {
namespace {
@@ -40,6 +42,10 @@ Seat::Seat() : changing_clipboard_data_to_selection_source_(false) {
// Prepend handler as it's critical that we see all events.
WMHelper::GetInstance()->PrependPreTargetHandler(this);
ui::ClipboardMonitor::GetInstance()->AddObserver(this);
+ // TODO(reveman): Need to handle the mus case where PlatformEventSource is
+ // null. https://crbug.com/856230
+ if (ui::PlatformEventSource::GetInstance())
+ ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this);
}
Seat::~Seat() {
@@ -48,6 +54,8 @@ Seat::~Seat() {
->RemoveObserver(this);
WMHelper::GetInstance()->RemovePreTargetHandler(this);
ui::ClipboardMonitor::GetInstance()->RemoveObserver(this);
+ if (ui::PlatformEventSource::GetInstance())
+ ui::PlatformEventSource::GetInstance()->RemovePlatformEventObserver(this);
}
void Seat::AddObserver(SeatObserver* observer) {
@@ -104,20 +112,51 @@ void Seat::OnWindowFocused(aura::Window* gained_focus,
}
////////////////////////////////////////////////////////////////////////////////
-// ui::EventHandler overrides:
+// ui::PlatformEventObserver overrides:
-void Seat::OnKeyEvent(ui::KeyEvent* event) {
- switch (event->type()) {
+void Seat::WillProcessEvent(const ui::PlatformEvent& event) {
+ switch (ui::EventTypeFromNative(event)) {
case ui::ET_KEY_PRESSED:
- pressed_keys_.insert(event->code());
+ case ui::ET_KEY_RELEASED:
+ physical_code_for_currently_processing_event_ = ui::CodeFromNative(event);
+ break;
+ default:
break;
+ }
+}
+
+void Seat::DidProcessEvent(const ui::PlatformEvent& event) {
+ switch (ui::EventTypeFromNative(event)) {
+ case ui::ET_KEY_PRESSED:
case ui::ET_KEY_RELEASED:
- pressed_keys_.erase(event->code());
+ physical_code_for_currently_processing_event_ = ui::DomCode::NONE;
break;
default:
- NOTREACHED();
break;
}
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ui::EventHandler overrides:
+
+void Seat::OnKeyEvent(ui::KeyEvent* event) {
+ // Ignore synthetic key repeat events.
+ if (event->is_repeat())
+ return;
+ if (physical_code_for_currently_processing_event_ != ui::DomCode::NONE) {
+ switch (event->type()) {
+ case ui::ET_KEY_PRESSED:
+ pressed_keys_.insert(
+ {physical_code_for_currently_processing_event_, event->code()});
+ break;
+ case ui::ET_KEY_RELEASED:
+ pressed_keys_.erase(physical_code_for_currently_processing_event_);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
modifier_flags_ = event->flags();
}
diff --git a/chromium/components/exo/seat.h b/chromium/components/exo/seat.h
index c2c59e5472c..3a2d268ea2c 100644
--- a/chromium/components/exo/seat.h
+++ b/chromium/components/exo/seat.h
@@ -5,7 +5,7 @@
#ifndef COMPONENTS_EXO_SEAT_H_
#define COMPONENTS_EXO_SEAT_H_
-#include "base/containers/flat_set.h"
+#include "base/containers/flat_map.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "components/exo/data_source_observer.h"
@@ -13,6 +13,8 @@
#include "ui/aura/client/focus_change_observer.h"
#include "ui/base/clipboard/clipboard_observer.h"
#include "ui/events/event_handler.h"
+#include "ui/events/keycodes/dom/dom_codes.h"
+#include "ui/events/platform/platform_event_observer.h"
namespace ui {
enum class DomCode;
@@ -27,6 +29,7 @@ class Surface;
// Seat object represent a group of input devices such as keyboard, pointer and
// touch devices and keeps track of input focus.
class Seat : public aura::client::FocusChangeObserver,
+ public ui::PlatformEventObserver,
public ui::EventHandler,
public ui::ClipboardObserver,
public DataSourceObserver {
@@ -42,13 +45,18 @@ class Seat : public aura::client::FocusChangeObserver,
virtual Surface* GetFocusedSurface();
// Returns currently pressed keys.
- const base::flat_set<ui::DomCode>& pressed_keys() const {
+ const base::flat_map<ui::DomCode, ui::DomCode>& pressed_keys() const {
return pressed_keys_;
}
// Returns current set of modifier flags.
int modifier_flags() const { return modifier_flags_; }
+ // Returns physical code for the currently processing event.
+ ui::DomCode physical_code_for_currently_processing_event() const {
+ return physical_code_for_currently_processing_event_;
+ }
+
// Sets clipboard data from |source|.
void SetSelection(DataSource* source);
@@ -56,6 +64,10 @@ class Seat : public aura::client::FocusChangeObserver,
void OnWindowFocused(aura::Window* gained_focus,
aura::Window* lost_focus) override;
+ // Overridden from ui::PlatformEventObserver:
+ void WillProcessEvent(const ui::PlatformEvent& event) override;
+ void DidProcessEvent(const ui::PlatformEvent& event) override;
+
// Overridden from ui::EventHandler:
void OnKeyEvent(ui::KeyEvent* event) override;
@@ -65,6 +77,12 @@ class Seat : public aura::client::FocusChangeObserver,
// Overridden from DataSourceObserver:
void OnDataSourceDestroying(DataSource* source) override;
+ void set_physical_code_for_currently_processing_event_for_testing(
+ ui::DomCode physical_code_for_currently_processing_event) {
+ physical_code_for_currently_processing_event_ =
+ physical_code_for_currently_processing_event;
+ }
+
private:
// Called when data is read from FD passed from a client.
// |data| is read data. |source| is source of the data, or nullptr if
@@ -72,7 +90,11 @@ class Seat : public aura::client::FocusChangeObserver,
void OnDataRead(const std::vector<uint8_t>& data);
base::ObserverList<SeatObserver> observers_;
- base::flat_set<ui::DomCode> pressed_keys_;
+ // The platform code is the key in this map as it represents the physical
+ // key that was pressed. The value is a potentially rewritten code that the
+ // physical key press generated.
+ base::flat_map<ui::DomCode, ui::DomCode> pressed_keys_;
+ ui::DomCode physical_code_for_currently_processing_event_ = ui::DomCode::NONE;
int modifier_flags_ = 0;
// Data source being used as a clipboard content.
diff --git a/chromium/components/exo/shared_memory.cc b/chromium/components/exo/shared_memory.cc
index 5247897556a..d444236fc70 100644
--- a/chromium/components/exo/shared_memory.cc
+++ b/chromium/components/exo/shared_memory.cc
@@ -69,7 +69,7 @@ std::unique_ptr<Buffer> SharedMemory::CreateBuffer(const gfx::Size& size,
std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
gpu::GpuMemoryBufferImplSharedMemory::CreateFromHandle(
- handle, size, format, gfx::BufferUsage::GPU_READ,
+ std::move(handle), size, format, gfx::BufferUsage::GPU_READ,
gpu::GpuMemoryBufferImpl::DestructionCallback());
if (!gpu_memory_buffer) {
LOG(ERROR) << "Failed to create GpuMemoryBuffer from handle";
diff --git a/chromium/components/exo/shell_surface.cc b/chromium/components/exo/shell_surface.cc
index 960ba6b9cfb..e41a4749e86 100644
--- a/chromium/components/exo/shell_surface.cc
+++ b/chromium/components/exo/shell_surface.cc
@@ -6,15 +6,22 @@
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/public/cpp/window_state_type.h"
+#include "ash/shell.h"
+#include "ash/wm/toplevel_window_event_handler.h"
#include "ash/wm/window_resizer.h"
#include "ash/wm/window_state.h"
+#include "base/bind.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "components/exo/wm_helper.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/cursor_client.h"
+#include "ui/aura/env.h"
#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/aura/window_tree_host.h"
#include "ui/views/widget/widget.h"
+#include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/window_util.h"
namespace exo {
@@ -140,8 +147,28 @@ void ShellSurface::SetFullscreen(bool fullscreen) {
widget_->SetFullscreen(fullscreen);
}
-void ShellSurface::Resize(int component) {
- TRACE_EVENT1("exo", "ShellSurface::Resize", "component", component);
+void ShellSurface::SetPopup() {
+ DCHECK(!widget_);
+ is_popup_ = true;
+}
+
+void ShellSurface::Grab() {
+ DCHECK(is_popup_);
+ DCHECK(!widget_);
+ has_grab_ = true;
+}
+
+void ShellSurface::StartMove() {
+ TRACE_EVENT0("exo", "ShellSurface::StartMove");
+
+ if (!widget_)
+ return;
+
+ AttemptToStartDrag(HTCAPTION);
+}
+
+void ShellSurface::StartResize(int component) {
+ TRACE_EVENT1("exo", "ShellSurface::StartResize", "component", component);
if (!widget_)
return;
@@ -209,4 +236,59 @@ void ShellSurface::OnPostWindowStateTypeChange(
scoped_animations_disabled_.reset();
}
+void ShellSurface::AttemptToStartDrag(int component) {
+ ash::wm::WindowState* window_state =
+ ash::wm::GetWindowState(widget_->GetNativeWindow());
+
+ // Ignore if surface is already being dragged.
+ if (window_state->is_dragged())
+ return;
+
+ aura::Window* target = widget_->GetNativeWindow();
+ ash::ToplevelWindowEventHandler* toplevel_handler =
+ ash::Shell::Get()->toplevel_window_event_handler();
+ aura::Window* mouse_pressed_handler =
+ target->GetHost()->dispatcher()->mouse_pressed_handler();
+ // Start dragging only if:
+ // 1) touch guesture is in progress.
+ // 2) mouse was pressed on the target or its subsurfaces.
+ aura::Window* gesture_target = toplevel_handler->gesture_target();
+ if (!gesture_target && !mouse_pressed_handler &&
+ target->Contains(mouse_pressed_handler)) {
+ return;
+ }
+ auto end_drag = [](ShellSurface* shell_surface,
+ ash::wm::WmToplevelWindowEventHandler::DragResult result) {
+ shell_surface->EndDrag();
+ };
+
+ if (gesture_target) {
+ gfx::Point location = toplevel_handler->event_location_in_gesture_target();
+ aura::Window::ConvertPointToTarget(
+ gesture_target, widget_->GetNativeWindow()->GetRootWindow(), &location);
+ toplevel_handler->AttemptToStartDrag(
+ target, location, component,
+ base::BindOnce(end_drag, base::Unretained(this)));
+ } else {
+ gfx::Point location = aura::Env::GetInstance()->last_mouse_location();
+ ::wm::ConvertPointFromScreen(widget_->GetNativeWindow()->GetRootWindow(),
+ &location);
+ toplevel_handler->AttemptToStartDrag(
+ target, location, component,
+ base::BindOnce(end_drag, base::Unretained(this)));
+ }
+ // Notify client that resizing state has changed.
+ if (IsResizing())
+ Configure();
+}
+
+void ShellSurface::EndDrag() {
+ if (resize_component_ != HTCAPTION) {
+ // Clear the drag details here as Configure uses it to decide if
+ // the window is being dragged.
+ ash::wm::GetWindowState(widget_->GetNativeWindow())->DeleteDragDetails();
+ Configure();
+ }
+}
+
} // namespace exo
diff --git a/chromium/components/exo/shell_surface.h b/chromium/components/exo/shell_surface.h
index 10e753899f0..1c12e296fd1 100644
--- a/chromium/components/exo/shell_surface.h
+++ b/chromium/components/exo/shell_surface.h
@@ -6,6 +6,7 @@
#define COMPONENTS_EXO_SHELL_SURFACE_H_
#include "ash/wm/window_state_observer.h"
+#include "ash/wm/wm_toplevel_window_event_handler.h"
#include "base/macros.h"
#include "components/exo/shell_surface_base.h"
@@ -43,10 +44,19 @@ class ShellSurface : public ShellSurfaceBase,
// Set fullscreen state for shell surface.
void SetFullscreen(bool fullscreen);
+ // Make the shell surface popup type.
+ void SetPopup();
+
+ // Set event grab on the surface.
+ void Grab();
+
// Start an interactive resize of surface. |component| is one of the windows
// HT constants (see ui/base/hit_test.h) and describes in what direction the
// surface should be resized.
- void Resize(int component);
+ void StartResize(int component);
+
+ // Start an interactive move of surface.
+ void StartMove();
// Overridden from ShellSurfaceBase:
void InitializeWindowState(ash::wm::WindowState* window_state) override;
@@ -62,6 +72,10 @@ class ShellSurface : public ShellSurfaceBase,
private:
class ScopedAnimationsDisabled;
+ void AttemptToStartDrag(int component);
+
+ void EndDrag();
+
std::unique_ptr<ScopedAnimationsDisabled> scoped_animations_disabled_;
DISALLOW_COPY_AND_ASSIGN(ShellSurface);
diff --git a/chromium/components/exo/shell_surface_base.cc b/chromium/components/exo/shell_surface_base.cc
index e3d8a7ea15e..9c7cf069607 100644
--- a/chromium/components/exo/shell_surface_base.cc
+++ b/chromium/components/exo/shell_surface_base.cc
@@ -29,10 +29,12 @@
#include "components/exo/surface.h"
#include "components/exo/wm_helper.h"
#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
+#include "ui/accessibility/ax_node_data.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
+#include "ui/aura/window_observer.h"
#include "ui/aura/window_targeter.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/accelerators/accelerator.h"
@@ -45,6 +47,7 @@
#include "ui/gfx/geometry/vector2d_conversions.h"
#include "ui/gfx/path.h"
#include "ui/views/widget/widget.h"
+#include "ui/wm/core/capture_controller.h"
#include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/shadow_controller.h"
#include "ui/wm/core/shadow_types.h"
@@ -62,6 +65,8 @@ DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::string, kApplicationIdKey, nullptr);
// Application Id set by the client.
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::string, kStartupIdKey, nullptr);
+const int32_t kInvalidChildAxTreeId = -1;
+
// The accelerator keys used to close ShellSurfaces.
const struct {
ui::KeyboardCode keycode;
@@ -91,7 +96,8 @@ class ShellSurfaceWidget : public views::Widget {
DISALLOW_COPY_AND_ASSIGN(ShellSurfaceWidget);
};
-class CustomFrameView : public ash::CustomFrameViewAsh {
+class CustomFrameView : public ash::CustomFrameViewAsh,
+ public aura::WindowObserver {
public:
using ShapeRects = std::vector<gfx::Rect>;
@@ -236,7 +242,10 @@ class CustomWindowTargeter : public aura::WindowTargeter {
if (!surface)
return false;
- int component = widget_->non_client_view()->NonClientHitTest(local_point);
+ int component =
+ widget_->non_client_view()
+ ? widget_->non_client_view()->NonClientHitTest(local_point)
+ : HTNOWHERE;
if (component != HTNOWHERE && component != HTCLIENT &&
component != HTBORDER) {
return true;
@@ -394,8 +403,6 @@ ShellSurfaceBase::ShellSurfaceBase(Surface* surface,
ShellSurfaceBase::~ShellSurfaceBase() {
DCHECK(!scoped_configure_);
- if (resizer_)
- EndDrag(false /* revert */);
// Remove activation observer before hiding widget to prevent it from
// casuing the configure callback to be called.
WMHelper::GetInstance()->RemoveActivationObserver(this);
@@ -412,6 +419,8 @@ ShellSurfaceBase::~ShellSurfaceBase() {
parent_->RemoveObserver(this);
if (root_surface())
root_surface()->RemoveSurfaceObserver(this);
+ if (has_grab_)
+ wm::CaptureController::Get()->RemoveObserver(this);
}
void ShellSurfaceBase::AcknowledgeConfigure(uint32_t serial) {
@@ -501,15 +510,6 @@ void ShellSurfaceBase::SetSystemModal(bool system_modal) {
non_system_modal_window_was_active_ = non_system_modal_window_was_active;
}
-void ShellSurfaceBase::Move() {
- TRACE_EVENT0("exo", "ShellSurfaceBase::Move");
-
- if (!widget_)
- return;
-
- AttemptToStartDrag(HTCAPTION);
-}
-
void ShellSurfaceBase::UpdateSystemModal() {
DCHECK(widget_);
DCHECK_EQ(container_, ash::kShellWindowId_SystemModalContainer);
@@ -575,6 +575,16 @@ void ShellSurfaceBase::SetStartupId(const char* startup_id) {
SetStartupId(widget_->GetNativeWindow(), startup_id_);
}
+void ShellSurfaceBase::SetChildAxTreeId(int32_t child_ax_tree_id) {
+ // We don't expect that child ax tree id is changed once it's set.
+ DCHECK_EQ(child_ax_tree_id_, kInvalidChildAxTreeId);
+ DCHECK_NE(child_ax_tree_id, kInvalidChildAxTreeId);
+
+ child_ax_tree_id_ = child_ax_tree_id;
+
+ this->NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged, false);
+}
+
void ShellSurfaceBase::Close() {
if (!close_callback_.is_null())
close_callback_.Run();
@@ -650,6 +660,41 @@ Surface* ShellSurfaceBase::GetMainSurface(const aura::Window* window) {
return window->GetProperty(kMainSurfaceKey);
}
+// static
+Surface* ShellSurfaceBase::GetTargetSurfaceForLocatedEvent(
+ ui::LocatedEvent* event) {
+ aura::Window* window = wm::CaptureController::Get()->GetCaptureWindow();
+ gfx::PointF location_in_target = event->location_f();
+
+ if (!window)
+ return Surface::AsSurface(static_cast<aura::Window*>(event->target()));
+
+ Surface* main_surface = ShellSurfaceBase::GetMainSurface(window);
+ // Skip if the event is captured by non exo windwows.
+ if (!main_surface)
+ return nullptr;
+
+ while (true) {
+ aura::Window* focused = window->GetEventHandlerForPoint(
+ gfx::ToFlooredPoint(location_in_target));
+
+ if (focused) {
+ aura::Window::ConvertPointToTarget(window, focused, &location_in_target);
+ return Surface::AsSurface(focused);
+ }
+
+ aura::Window* parent_window = wm::GetTransientParent(window);
+
+ if (!parent_window) {
+ location_in_target = event->location_f();
+ return main_surface;
+ }
+ aura::Window::ConvertPointToTarget(window, parent_window,
+ &location_in_target);
+ window = parent_window;
+ }
+}
+
std::unique_ptr<base::trace_event::TracedValue>
ShellSurfaceBase::AsTracedValue() const {
std::unique_ptr<base::trace_event::TracedValue> value(
@@ -746,6 +791,9 @@ void ShellSurfaceBase::OnSurfaceCommit() {
DCHECK(!widget_->IsVisible());
pending_show_widget_ = false;
widget_->Show();
+ if (has_grab_)
+ StartCapture();
+
if (container_ == ash::kShellWindowId_SystemModalContainer)
UpdateSystemModal();
}
@@ -762,6 +810,12 @@ bool ShellSurfaceBase::IsInputEnabled(Surface*) const {
}
void ShellSurfaceBase::OnSetFrame(SurfaceFrameType frame_type) {
+ if (is_popup_) {
+ // TODO(oshima): Consider supporting shadow type.
+ DLOG(WARNING) << "popup does not support frame decoration";
+ return;
+ }
+
bool frame_was_disabled = !frame_enabled();
frame_type_ = frame_type;
switch (frame_type) {
@@ -822,9 +876,6 @@ void ShellSurfaceBase::OnSetParent(Surface* parent,
if (container_ == ash::kShellWindowId_DefaultContainer)
SetParentWindow(parent_widget->GetNativeWindow());
- if (resizer_)
- return;
-
origin_ = position;
views::View::ConvertPointToScreen(
parent_widget->widget_delegate()->GetContentsView(), &origin_);
@@ -832,6 +883,11 @@ void ShellSurfaceBase::OnSetParent(Surface* parent,
if (!widget_)
return;
+ ash::wm::WindowState* window_state =
+ ash::wm::GetWindowState(widget_->GetNativeWindow());
+ if (window_state->is_dragged())
+ return;
+
gfx::Rect widget_bounds = widget_->GetWindowBoundsInScreen();
gfx::Rect new_widget_bounds(origin_, widget_bounds.size());
if (new_widget_bounds != widget_bounds) {
@@ -861,8 +917,6 @@ void ShellSurfaceBase::OnSurfaceDestroying(Surface* surface) {
surface->RemoveSurfaceObserver(this);
SetRootSurface(nullptr);
- if (resizer_)
- EndDrag(false /* revert */);
if (widget_)
SetMainSurface(widget_->GetNativeWindow(), nullptr);
@@ -908,17 +962,11 @@ bool ShellSurfaceBase::CanMinimize() const {
}
base::string16 ShellSurfaceBase::GetWindowTitle() const {
- if (extra_title_.empty())
- return title_;
-
- // TODO(estade): revisit how the extra title is shown in the window frame and
- // other surfaces like overview mode.
- return title_ + base::ASCIIToUTF16(" (") + extra_title_ +
- base::ASCIIToUTF16(")");
+ return title_;
}
bool ShellSurfaceBase::ShouldShowWindowTitle() const {
- return !extra_title_.empty();
+ return false;
}
gfx::ImageSkia ShellSurfaceBase::GetWindowIcon() {
@@ -926,8 +974,6 @@ gfx::ImageSkia ShellSurfaceBase::GetWindowIcon() {
}
void ShellSurfaceBase::WindowClosing() {
- if (resizer_)
- EndDrag(true /* revert */);
SetEnabled(false);
widget_ = nullptr;
}
@@ -976,6 +1022,29 @@ void ShellSurfaceBase::GetWidgetHitTestMask(gfx::Path* mask) const {
mask->transform(matrix);
}
+void ShellSurfaceBase::OnCaptureChanged(aura::Window* lost_capture,
+ aura::Window* gained_capture) {
+ if (lost_capture == widget_->GetNativeWindow() && is_popup_) {
+ wm::CaptureController::Get()->RemoveObserver(this);
+ if (gained_capture &&
+ lost_capture == wm::GetTransientParent(gained_capture)) {
+ // Don't close if the capture has been transferred to the child popup.
+ return;
+ }
+ aura::Window* parent = wm::GetTransientParent(lost_capture);
+ if (parent) {
+ // The capture needs to be transferred to the parent if it had grab.
+ views::Widget* parent_widget =
+ views::Widget::GetWidgetForNativeWindow(parent);
+ ShellSurfaceBase* parent_shell_surface = static_cast<ShellSurfaceBase*>(
+ parent_widget->widget_delegate()->GetContentsView());
+ if (parent_shell_surface->has_grab_)
+ parent_shell_surface->StartCapture();
+ }
+ widget_->Close();
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
// views::Views overrides:
@@ -996,6 +1065,16 @@ gfx::Size ShellSurfaceBase::GetMaximumSize() const {
return maximum_size_;
}
+void ShellSurfaceBase::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+ node_data->role = ax::mojom::Role::kClient;
+
+ if (child_ax_tree_id_ == kInvalidChildAxTreeId)
+ return;
+
+ node_data->AddIntAttribute(ax::mojom::IntAttribute::kChildTreeId,
+ child_ax_tree_id_);
+}
+
////////////////////////////////////////////////////////////////////////////////
// aura::WindowObserver overrides:
@@ -1049,119 +1128,13 @@ void ShellSurfaceBase::OnWindowActivated(ActivationReason reason,
if (gained_active == widget_->GetNativeWindow() ||
lost_active == widget_->GetNativeWindow()) {
- DCHECK(activatable_);
+ DCHECK(CanActivate());
Configure();
UpdateShadow();
}
}
////////////////////////////////////////////////////////////////////////////////
-// ui::EventHandler overrides:
-
-void ShellSurfaceBase::OnKeyEvent(ui::KeyEvent* event) {
- if (!resizer_) {
- views::View::OnKeyEvent(event);
- return;
- }
-
- if (event->type() == ui::ET_KEY_PRESSED &&
- event->key_code() == ui::VKEY_ESCAPE) {
- EndDrag(true /* revert */);
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// ui::EventHandler overrides:
-
-void ShellSurfaceBase::OnMouseEvent(ui::MouseEvent* event) {
- if (!resizer_) {
- views::View::OnMouseEvent(event);
- return;
- }
-
- if (event->handled())
- return;
-
- if ((event->flags() &
- (ui::EF_MIDDLE_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON)) != 0)
- return;
-
- if (event->type() == ui::ET_MOUSE_CAPTURE_CHANGED) {
- // We complete the drag instead of reverting it, as reverting it will
- // result in a weird behavior when a client produces a modal dialog
- // while the drag is in progress.
- EndDrag(false /* revert */);
- return;
- }
-
- switch (event->type()) {
- case ui::ET_MOUSE_DRAGGED: {
- if (OnMouseDragged(*event))
- event->StopPropagation();
- break;
- }
- case ui::ET_MOUSE_RELEASED: {
- ScopedConfigure scoped_configure(this, false);
- EndDrag(false /* revert */);
- break;
- }
- case ui::ET_MOUSE_MOVED:
- case ui::ET_MOUSE_PRESSED:
- case ui::ET_MOUSE_ENTERED:
- case ui::ET_MOUSE_EXITED:
- case ui::ET_MOUSEWHEEL:
- case ui::ET_MOUSE_CAPTURE_CHANGED:
- break;
- default:
- NOTREACHED();
- break;
- }
-}
-
-void ShellSurfaceBase::OnGestureEvent(ui::GestureEvent* event) {
- if (!resizer_) {
- views::View::OnGestureEvent(event);
- return;
- }
-
- if (event->handled())
- return;
-
- // TODO(domlaskowski): Handle touch dragging/resizing. See crbug.com/738606.
- switch (event->type()) {
- case ui::ET_GESTURE_END: {
- ScopedConfigure scoped_configure(this, false);
- EndDrag(false /* revert */);
- break;
- }
- case ui::ET_GESTURE_SCROLL_BEGIN:
- case ui::ET_GESTURE_SCROLL_END:
- case ui::ET_GESTURE_SCROLL_UPDATE:
- case ui::ET_GESTURE_TAP:
- case ui::ET_GESTURE_TAP_DOWN:
- case ui::ET_GESTURE_TAP_CANCEL:
- case ui::ET_GESTURE_TAP_UNCONFIRMED:
- case ui::ET_GESTURE_DOUBLE_TAP:
- case ui::ET_GESTURE_BEGIN:
- case ui::ET_GESTURE_TWO_FINGER_TAP:
- case ui::ET_GESTURE_PINCH_BEGIN:
- case ui::ET_GESTURE_PINCH_END:
- case ui::ET_GESTURE_PINCH_UPDATE:
- case ui::ET_GESTURE_LONG_PRESS:
- case ui::ET_GESTURE_LONG_TAP:
- case ui::ET_GESTURE_SWIPE:
- case ui::ET_GESTURE_SHOW_PRESS:
- case ui::ET_SCROLL:
- case ui::ET_SCROLL_FLING_START:
- case ui::ET_SCROLL_FLING_CANCEL:
- break;
- default:
- NOTREACHED();
- break;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
// ui::AcceleratorTarget overrides:
bool ShellSurfaceBase::AcceleratorPressed(const ui::Accelerator& accelerator) {
@@ -1184,7 +1157,8 @@ void ShellSurfaceBase::CreateShellSurfaceWidget(
DCHECK(!widget_);
views::Widget::InitParams params;
- params.type = views::Widget::InitParams::TYPE_WINDOW;
+ params.type = is_popup_ ? views::Widget::InitParams::TYPE_POPUP
+ : views::Widget::InitParams::TYPE_WINDOW;
params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
params.delegate = this;
params.shadow_type = views::Widget::InitParams::SHADOW_TYPE_NONE;
@@ -1193,13 +1167,17 @@ void ShellSurfaceBase::CreateShellSurfaceWidget(
// Make shell surface a transient child if |parent_| has been set.
params.parent =
parent_ ? parent_
- : WMHelper::GetInstance()->GetPrimaryDisplayContainer(container_);
+ : ash::Shell::GetContainer(
+ ash::Shell::GetRootWindowForNewWindows(), container_);
params.bounds = gfx::Rect(origin_, gfx::Size());
bool activatable = activatable_;
// ShellSurfaces in system modal container are only activatable if input
// region is non-empty. See OnCommitSurface() for more details.
if (container_ == ash::kShellWindowId_SystemModalContainer)
activatable &= HasHitTestRegion();
+ // Transient child needs to have an application id to be activatable.
+ if (parent_)
+ activatable &= application_id_.has_value();
params.activatable = activatable ? views::Widget::InitParams::ACTIVATABLE_YES
: views::Widget::InitParams::ACTIVATABLE_NO;
// Note: NativeWidget owns this widget.
@@ -1208,8 +1186,6 @@ void ShellSurfaceBase::CreateShellSurfaceWidget(
aura::Window* window = widget_->GetNativeWindow();
window->SetName("ExoShellSurface");
- window->SetProperty(aura::client::kAccessibilityFocusFallsbackToWidgetKey,
- false);
window->AddChild(host_window());
// Use DESCENDANTS_ONLY event targeting policy for mus/mash.
// TODO(https://crbug.com/839521): Revisit after event dispatching code is
@@ -1233,7 +1209,7 @@ void ShellSurfaceBase::CreateShellSurfaceWidget(
window_state->SetHideShelfWhenFullscreen(false);
// Fade visibility animations for non-activatable windows.
- if (!activatable_) {
+ if (!CanActivate()) {
wm::SetWindowVisibilityAnimationType(
window, wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
}
@@ -1273,9 +1249,8 @@ void ShellSurfaceBase::Configure() {
uint32_t serial = 0;
if (!configure_callback_.is_null()) {
if (widget_) {
- const views::NonClientView* non_client_view = widget_->non_client_view();
serial = configure_callback_.Run(
- non_client_view->frame_view()->GetBoundsForClientView().size(),
+ GetClientViewBounds().size(),
ash::wm::GetWindowState(widget_->GetNativeWindow())->GetStateType(),
IsResizing(), widget_->IsActive(), origin_offset);
} else {
@@ -1306,9 +1281,9 @@ bool ShellSurfaceBase::IsResizing() const {
ash::wm::GetWindowState(widget_->GetNativeWindow());
if (!window_state->is_dragged())
return false;
-
- return window_state->drag_details()->bounds_change &
- ash::WindowResizer::kBoundsChange_Resizes;
+ return window_state->drag_details() &&
+ (window_state->drag_details()->bounds_change &
+ ash::WindowResizer::kBoundsChange_Resizes);
}
void ShellSurfaceBase::UpdateWidgetBounds() {
@@ -1354,10 +1329,7 @@ void ShellSurfaceBase::SetWidgetBounds(const gfx::Rect& bounds) {
}
void ShellSurfaceBase::UpdateSurfaceBounds() {
- gfx::Point origin = widget_->non_client_view()
- ->frame_view()
- ->GetBoundsForClientView()
- .origin();
+ gfx::Point origin = GetClientViewBounds().origin();
origin += GetSurfaceOrigin().OffsetFromOrigin();
origin -= ToFlooredVector2d(ScaleVector2d(
@@ -1392,7 +1364,7 @@ void ShellSurfaceBase::UpdateShadow() {
shadow->SetContentBounds(GetShadowBounds());
// Surfaces that can't be activated are usually menus and tooltips. Use a
// small style shadow for them.
- if (!activatable_)
+ if (!CanActivate())
shadow->SetElevation(wm::kShadowElevationMenuOrTooltip);
// We don't have rounded corners unless frame is enabled.
if (!frame_enabled())
@@ -1418,6 +1390,14 @@ gfx::Point ShellSurfaceBase::GetMouseLocation() const {
return location;
}
+gfx::Rect ShellSurfaceBase::GetClientViewBounds() const {
+ return widget_->non_client_view()
+ ? widget_->non_client_view()
+ ->frame_view()
+ ->GetBoundsForClientView()
+ : gfx::Rect(widget_->GetWindowBoundsInScreen().size());
+}
+
gfx::Rect ShellSurfaceBase::GetShadowBounds() const {
return shadow_bounds_->IsEmpty()
? gfx::Rect(widget_->GetNativeWindow()->bounds().size())
@@ -1431,133 +1411,14 @@ float ShellSurfaceBase::GetScale() const {
return 1.f;
}
-aura::Window* ShellSurfaceBase::GetDragWindow() {
- return movement_disabled_ ? nullptr : widget_->GetNativeWindow();
-}
-
-std::unique_ptr<ash::WindowResizer> ShellSurfaceBase::CreateWindowResizer(
- aura::Window* window,
- int component) {
- // Set the cursor before calling CreateWindowResizer, as that will
- // eventually call LockCursor and prevent the cursor from changing.
- aura::client::CursorClient* cursor_client =
- aura::client::GetCursorClient(window->GetRootWindow());
- if (!cursor_client)
- return nullptr;
-
- switch (component) {
- case HTCAPTION:
- cursor_client->SetCursor(ui::CursorType::kPointer);
- break;
- case HTTOP:
- cursor_client->SetCursor(ui::CursorType::kNorthResize);
- break;
- case HTTOPRIGHT:
- cursor_client->SetCursor(ui::CursorType::kNorthEastResize);
- break;
- case HTRIGHT:
- cursor_client->SetCursor(ui::CursorType::kEastResize);
- break;
- case HTBOTTOMRIGHT:
- cursor_client->SetCursor(ui::CursorType::kSouthEastResize);
- break;
- case HTBOTTOM:
- cursor_client->SetCursor(ui::CursorType::kSouthResize);
- break;
- case HTBOTTOMLEFT:
- cursor_client->SetCursor(ui::CursorType::kSouthWestResize);
- break;
- case HTLEFT:
- cursor_client->SetCursor(ui::CursorType::kWestResize);
- break;
- case HTTOPLEFT:
- cursor_client->SetCursor(ui::CursorType::kNorthWestResize);
- break;
- default:
- NOTREACHED();
- break;
- }
-
- std::unique_ptr<ash::WindowResizer> resizer = ash::CreateWindowResizer(
- window, GetMouseLocation(), component, wm::WINDOW_MOVE_SOURCE_MOUSE);
-
- if (!resizer)
- return nullptr;
-
- // Apply pending origin offsets and resize direction before starting a
- // new resize operation. These can still be pending if the client has
- // acknowledged the configure request but has not yet committed.
- origin_offset_ += pending_origin_offset_;
- pending_origin_offset_ = gfx::Vector2d();
- resize_component_ = pending_resize_component_;
-
- return resizer;
-}
-
-bool ShellSurfaceBase::OnMouseDragged(const ui::MouseEvent& event) {
- gfx::Point location(event.location());
- aura::Window::ConvertPointToTarget(widget_->GetNativeWindow(),
- widget_->GetNativeWindow()->parent(),
- &location);
- ScopedConfigure scoped_configure(this, false);
- resizer_->Drag(location, event.flags());
- return true;
-}
-
-void ShellSurfaceBase::AttemptToStartDrag(int component) {
- DCHECK(widget_);
-
- // Cannot start another drag if one is already taking place.
- if (resizer_)
- return;
-
- aura::Window* window = GetDragWindow();
- if (!window || window->HasCapture())
- return;
-
- resizer_ = CreateWindowResizer(window, component);
- if (!resizer_)
- return;
-
- WMHelper::GetInstance()->AddPreTargetHandler(this);
- window->SetCapture();
-
- // Notify client that resizing state has changed.
- if (IsResizing())
- Configure();
-}
-
-void ShellSurfaceBase::EndDrag(bool revert) {
- DCHECK(widget_);
- DCHECK(resizer_);
-
- aura::Window* window = GetDragWindow();
- DCHECK(window);
- DCHECK(window->HasCapture());
-
- bool was_resizing = IsResizing();
-
- if (revert)
- resizer_->RevertDrag();
- else
- resizer_->CompleteDrag();
-
- WMHelper::GetInstance()->RemovePreTargetHandler(this);
- window->ReleaseCapture();
- resizer_.reset();
-
- // Notify client that resizing state has changed.
- if (was_resizing)
- Configure();
-
- UpdateWidgetBounds();
-}
-
gfx::Rect ShellSurfaceBase::GetWidgetBounds() const {
gfx::Rect visible_bounds = GetVisibleBounds();
gfx::Rect new_widget_bounds =
- widget_->non_client_view()->GetWindowBoundsForClientBounds(
- visible_bounds);
+ widget_->non_client_view()
+ ? widget_->non_client_view()->GetWindowBoundsForClientBounds(
+ visible_bounds)
+ : visible_bounds;
+
if (movement_disabled_) {
new_widget_bounds.set_origin(origin_);
} else if (resize_component_ == HTCAPTION) {
@@ -1579,8 +1440,8 @@ gfx::Point ShellSurfaceBase::GetSurfaceOrigin() const {
DCHECK(!movement_disabled_ || resize_component_ == HTCAPTION);
gfx::Rect visible_bounds = GetVisibleBounds();
- gfx::Rect client_bounds =
- widget_->non_client_view()->frame_view()->GetBoundsForClientView();
+ gfx::Rect client_bounds = GetClientViewBounds();
+
switch (resize_component_) {
case HTCAPTION:
return gfx::Point() + origin_offset_ - visible_bounds.OffsetFromOrigin();
@@ -1624,4 +1485,11 @@ void ShellSurfaceBase::SetParentWindow(aura::Window* parent) {
widget_->OnSizeConstraintsChanged();
}
+void ShellSurfaceBase::StartCapture() {
+ widget_->set_auto_release_capture(false);
+ wm::CaptureController::Get()->AddObserver(this);
+ // Just capture on the window.
+ widget_->SetCapture(nullptr /* view */);
+}
+
} // namespace exo
diff --git a/chromium/components/exo/shell_surface_base.h b/chromium/components/exo/shell_surface_base.h
index 64b6489f0d3..e1be0900173 100644
--- a/chromium/components/exo/shell_surface_base.h
+++ b/chromium/components/exo/shell_surface_base.h
@@ -17,6 +17,7 @@
#include "base/strings/string16.h"
#include "components/exo/surface_observer.h"
#include "components/exo/surface_tree_host.h"
+#include "ui/aura/client/capture_client_observer.h"
#include "ui/aura/window_observer.h"
#include "ui/base/hit_test.h"
#include "ui/compositor/compositor_lock.h"
@@ -28,7 +29,6 @@
#include "ui/wm/public/activation_change_observer.h"
namespace ash {
-class WindowResizer;
namespace wm {
class WindowState;
}
@@ -53,6 +53,7 @@ class Surface;
class ShellSurfaceBase : public SurfaceTreeHost,
public SurfaceObserver,
public aura::WindowObserver,
+ public aura::client::CaptureClientObserver,
public views::WidgetDelegate,
public views::View,
public wm::ActivationChangeObserver {
@@ -109,10 +110,6 @@ class ShellSurfaceBase : public SurfaceTreeHost,
// Sets the system modality.
void SetSystemModal(bool system_modal);
- // Start an interactive move of surface.
- // TODO(oshima): Move this to ShellSurface.
- void Move();
-
// Sets the application ID for the window. The application ID identifies the
// general class of applications to which the window belongs.
static void SetApplicationId(aura::Window* window,
@@ -131,6 +128,9 @@ class ShellSurfaceBase : public SurfaceTreeHost,
// Set the startup ID for the surface.
void SetStartupId(const char* startup_id);
+ // Set the child ax tree ID for the surface.
+ void SetChildAxTreeId(int32_t child_ax_tree_id);
+
// Signal a request to close the window. It is up to the implementation to
// actually decide to do so though.
void Close();
@@ -166,6 +166,12 @@ class ShellSurfaceBase : public SurfaceTreeHost,
// |window| must not be nullptr.
static Surface* GetMainSurface(const aura::Window* window);
+ // Returns the target surface for the located event |event|. If an
+ // event handling is grabbed by an window, it'll first examine that
+ // window, then traverse to its transeitn parent if the parent also
+ // requested grab.
+ static Surface* GetTargetSurfaceForLocatedEvent(ui::LocatedEvent* event);
+
// Returns a trace value representing the state of the surface.
std::unique_ptr<base::trace_event::TracedValue> AsTracedValue() const;
@@ -181,6 +187,10 @@ class ShellSurfaceBase : public SurfaceTreeHost,
// Overridden from SurfaceObserver:
void OnSurfaceDestroying(Surface* surface) override;
+ // Overridden from CaptureClientObserver:
+ void OnCaptureChanged(aura::Window* lost_capture,
+ aura::Window* gained_capture) override;
+
// Overridden from views::WidgetDelegate:
bool CanResize() const override;
bool CanMaximize() const override;
@@ -201,6 +211,7 @@ class ShellSurfaceBase : public SurfaceTreeHost,
gfx::Size CalculatePreferredSize() const override;
gfx::Size GetMinimumSize() const override;
gfx::Size GetMaximumSize() const override;
+ void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
// Overridden from aura::WindowObserver:
void OnWindowBoundsChanged(aura::Window* window,
@@ -214,14 +225,14 @@ class ShellSurfaceBase : public SurfaceTreeHost,
aura::Window* gained_active,
aura::Window* lost_active) override;
- // Overridden from ui::EventHandler:
- void OnKeyEvent(ui::KeyEvent* event) override;
- void OnMouseEvent(ui::MouseEvent* event) override;
- void OnGestureEvent(ui::GestureEvent* event) override;
-
// Overridden from ui::AcceleratorTarget:
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
+ bool frame_enabled() const {
+ return frame_type_ != SurfaceFrameType::NONE &&
+ frame_type_ != SurfaceFrameType::SHADOW;
+ }
+
Surface* surface_for_testing() { return root_surface(); }
protected:
@@ -278,17 +289,18 @@ class ShellSurfaceBase : public SurfaceTreeHost,
// In the coordinate system of the parent root window.
gfx::Point GetMouseLocation() const;
+ // Returns the bounds of the client area.nnn
+ gfx::Rect GetClientViewBounds() const;
+
// In the local coordinate system of the window.
virtual gfx::Rect GetShadowBounds() const;
- // Attempt to start a drag operation. The type of drag operation to start is
- // determined by |component|.
- // TODO(oshima): Move this to ShellSurface.
- void AttemptToStartDrag(int component);
-
// Set the parent window of this surface.
void SetParentWindow(aura::Window* parent);
+ // Start the event capture on this surface.
+ void StartCapture();
+
const gfx::Rect& geometry() const { return geometry_; }
views::Widget* widget_ = nullptr;
@@ -308,21 +320,15 @@ class ShellSurfaceBase : public SurfaceTreeHost,
gfx::Rect pending_geometry_;
base::Optional<gfx::Rect> shadow_bounds_;
bool shadow_bounds_changed_ = false;
- std::unique_ptr<ash::WindowResizer> resizer_;
base::string16 title_;
- // The debug title string shown in the window frame (title bar).
- base::string16 extra_title_;
std::unique_ptr<ui::CompositorLock> configure_compositor_lock_;
ConfigureCallback configure_callback_;
// TODO(oshima): Remove this once the transition to new drag/resize
// complete. https://crbug.com/801666.
bool client_controlled_move_resize_ = true;
SurfaceFrameType frame_type_ = SurfaceFrameType::NONE;
-
- bool frame_enabled() const {
- return frame_type_ != SurfaceFrameType::NONE &&
- frame_type_ != SurfaceFrameType::SHADOW;
- }
+ bool is_popup_ = false;
+ bool has_grab_ = false;
private:
struct Config;
@@ -334,20 +340,6 @@ class ShellSurfaceBase : public SurfaceTreeHost,
// Returns the scale of the surface tree relative to the shell surface.
virtual float GetScale() const;
- // Returns the window that has capture during dragging.
- virtual aura::Window* GetDragWindow();
-
- // Creates the resizer for a dragging/resizing operation.
- virtual std::unique_ptr<ash::WindowResizer> CreateWindowResizer(
- aura::Window* window,
- int component);
-
- // Overridden from ui::EventHandler:
- bool OnMouseDragged(const ui::MouseEvent& event) override;
-
- // End current drag operation.
- void EndDrag(bool revert);
-
// Return the bounds of the widget/origin of surface taking visible
// bounds and current resize direction into account.
virtual gfx::Rect GetWidgetBounds() const;
@@ -372,6 +364,7 @@ class ShellSurfaceBase : public SurfaceTreeHost,
gfx::Size pending_minimum_size_;
gfx::Size maximum_size_;
gfx::Size pending_maximum_size_;
+ int32_t child_ax_tree_id_ = -1;
DISALLOW_COPY_AND_ASSIGN(ShellSurfaceBase);
};
diff --git a/chromium/components/exo/shell_surface_unittest.cc b/chromium/components/exo/shell_surface_unittest.cc
index 79cb6cbe8aa..9f30ff92810 100644
--- a/chromium/components/exo/shell_surface_unittest.cc
+++ b/chromium/components/exo/shell_surface_unittest.cc
@@ -33,7 +33,10 @@
#include "ui/display/display.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/event.h"
#include "ui/views/widget/widget.h"
+#include "ui/wm/core/capture_controller.h"
#include "ui/wm/core/window_util.h"
namespace exo {
@@ -57,6 +60,18 @@ uint32_t ConfigureFullscreen(uint32_t serial,
return serial;
}
+std::unique_ptr<ShellSurface> CreatePopupShellSurface(
+ Surface* popup_surface,
+ ShellSurface* parent,
+ const gfx::Point& origin) {
+ auto popup_shell_surface = std::make_unique<ShellSurface>(popup_surface);
+ popup_shell_surface->DisableMovement();
+ popup_shell_surface->SetPopup();
+ popup_shell_surface->SetParent(parent);
+ popup_shell_surface->SetOrigin(origin);
+ return popup_shell_surface;
+}
+
TEST_F(ShellSurfaceTest, AcknowledgeConfigure) {
gfx::Size buffer_size(32, 32);
std::unique_ptr<Buffer> buffer(
@@ -129,6 +144,7 @@ TEST_F(ShellSurfaceTest, SetParent) {
gfx::PointAtOffsetFromOrigin(gfx::Point(10, 10) - parent_origin));
EXPECT_EQ(gfx::Rect(10, 10, 256, 256),
shell_surface->GetWidget()->GetWindowBoundsInScreen());
+ EXPECT_FALSE(shell_surface->CanActivate());
}
TEST_F(ShellSurfaceTest, Maximize) {
@@ -296,7 +312,7 @@ TEST_F(ShellSurfaceTest, SetStartupId) {
EXPECT_EQ(nullptr, ShellSurface::GetStartupId(window));
}
-TEST_F(ShellSurfaceTest, Move) {
+TEST_F(ShellSurfaceTest, StartMove) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
@@ -304,13 +320,13 @@ TEST_F(ShellSurfaceTest, Move) {
surface->Commit();
// The interactive move should end when surface is destroyed.
- shell_surface->Move();
+ shell_surface->StartMove();
// Test that destroying the shell surface before move ends is OK.
shell_surface.reset();
}
-TEST_F(ShellSurfaceTest, Resize) {
+TEST_F(ShellSurfaceTest, StartResize) {
std::unique_ptr<Surface> surface(new Surface);
std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
@@ -318,7 +334,7 @@ TEST_F(ShellSurfaceTest, Resize) {
surface->Commit();
// The interactive resize should end when surface is destroyed.
- shell_surface->Resize(HTBOTTOMRIGHT);
+ shell_surface->StartResize(HTBOTTOMRIGHT);
// Test that destroying the surface before resize ends is OK.
surface.reset();
@@ -443,14 +459,17 @@ uint32_t Configure(gfx::Size* suggested_size,
}
TEST_F(ShellSurfaceTest, ConfigureCallback) {
- std::unique_ptr<Surface> surface(new Surface);
- std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
-
+ // Must be before shell_surface so it outlives it, for shell_surface's
+ // destructor calls Configure() referencing these 4 variables.
gfx::Size suggested_size;
ash::mojom::WindowStateType has_state_type =
ash::mojom::WindowStateType::NORMAL;
bool is_resizing = false;
bool is_active = false;
+
+ std::unique_ptr<Surface> surface(new Surface);
+ std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
+
shell_surface->set_configure_callback(
base::Bind(&Configure, base::Unretained(&suggested_size),
base::Unretained(&has_state_type),
@@ -488,7 +507,7 @@ TEST_F(ShellSurfaceTest, ConfigureCallback) {
EXPECT_FALSE(is_active);
EXPECT_FALSE(is_resizing);
- shell_surface->Resize(HTBOTTOMRIGHT);
+ shell_surface->StartResize(HTBOTTOMRIGHT);
shell_surface->AcknowledgeConfigure(0);
EXPECT_TRUE(is_resizing);
}
@@ -583,5 +602,90 @@ TEST_F(ShellSurfaceTest, CycleSnap) {
shell_surface->GetWidget()->GetWindowBoundsInScreen().width());
}
+TEST_F(ShellSurfaceTest, Popup) {
+ gfx::Size buffer_size(256, 256);
+ std::unique_ptr<Buffer> buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+ std::unique_ptr<Surface> surface(new Surface);
+ std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
+
+ surface->Attach(buffer.get());
+ surface->Commit();
+ shell_surface->GetWidget()->SetBounds(gfx::Rect(0, 0, 256, 256));
+
+ std::unique_ptr<Buffer> popup_buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+ std::unique_ptr<Surface> popup_surface(new Surface);
+ popup_surface->Attach(popup_buffer.get());
+ std::unique_ptr<ShellSurface> popup_shell_surface(CreatePopupShellSurface(
+ popup_surface.get(), shell_surface.get(), gfx::Point(50, 50)));
+ popup_shell_surface->Grab();
+ popup_surface->Commit();
+ ASSERT_EQ(gfx::Rect(50, 50, 256, 256),
+ popup_shell_surface->GetWidget()->GetWindowBoundsInScreen());
+
+ // Verify that created shell surface is popup and has capture.
+ EXPECT_EQ(aura::client::WINDOW_TYPE_POPUP,
+ popup_shell_surface->GetWidget()->GetNativeWindow()->type());
+ EXPECT_EQ(wm::CaptureController::Get()->GetCaptureWindow(),
+ popup_shell_surface->GetWidget()->GetNativeWindow());
+
+ // Setting frame type on popup should have no effect.
+ popup_surface->SetFrame(SurfaceFrameType::NORMAL);
+ EXPECT_FALSE(popup_shell_surface->frame_enabled());
+
+ // ShellSurface can capture the event even after it is craeted.
+ std::unique_ptr<Buffer> sub_popup_buffer(
+ new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+ std::unique_ptr<Surface> sub_popup_surface(new Surface);
+ sub_popup_surface->Attach(popup_buffer.get());
+ std::unique_ptr<ShellSurface> sub_popup_shell_surface(CreatePopupShellSurface(
+ sub_popup_surface.get(), popup_shell_surface.get(), gfx::Point(100, 50)));
+ sub_popup_shell_surface->Grab();
+ sub_popup_surface->Commit();
+ ASSERT_EQ(gfx::Rect(100, 50, 256, 256),
+ sub_popup_shell_surface->GetWidget()->GetWindowBoundsInScreen());
+
+ // The capture should be on sub_popup_shell_surface.
+ EXPECT_EQ(wm::CaptureController::Get()->GetCaptureWindow(),
+ sub_popup_shell_surface->GetWidget()->GetNativeWindow());
+ EXPECT_EQ(aura::client::WINDOW_TYPE_POPUP,
+ sub_popup_shell_surface->GetWidget()->GetNativeWindow()->type());
+
+ {
+ // Mouse is on the top most popup.
+ ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0),
+ gfx::Point(100, 50), ui::EventTimeForNow(), 0, 0);
+ EXPECT_EQ(sub_popup_surface.get(),
+ ShellSurfaceBase::GetTargetSurfaceForLocatedEvent(&event));
+ }
+ {
+ // Move the mouse to the parent popup.
+ ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(-25, 0),
+ gfx::Point(75, 50), ui::EventTimeForNow(), 0, 0);
+ EXPECT_EQ(popup_surface.get(),
+ ShellSurfaceBase::GetTargetSurfaceForLocatedEvent(&event));
+ }
+ {
+ // Move the mouse to the main window.
+ ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(-25, -25),
+ gfx::Point(75, 25), ui::EventTimeForNow(), 0, 0);
+ EXPECT_EQ(surface.get(),
+ ShellSurfaceBase::GetTargetSurfaceForLocatedEvent(&event));
+ }
+
+ // Removing top most popup moves the grab to parent popup.
+ sub_popup_shell_surface.reset();
+ EXPECT_EQ(wm::CaptureController::Get()->GetCaptureWindow(),
+ popup_shell_surface->GetWidget()->GetNativeWindow());
+ {
+ // Targetting should still work.
+ ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0),
+ gfx::Point(50, 50), ui::EventTimeForNow(), 0, 0);
+ EXPECT_EQ(popup_surface.get(),
+ ShellSurfaceBase::GetTargetSurfaceForLocatedEvent(&event));
+ }
+}
+
} // namespace
} // namespace exo
diff --git a/chromium/components/exo/surface.cc b/chromium/components/exo/surface.cc
index 65d24608e12..895f76bb15a 100644
--- a/chromium/components/exo/surface.cc
+++ b/chromium/components/exo/surface.cc
@@ -42,6 +42,7 @@
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/gfx/path.h"
+#include "ui/gfx/presentation_feedback.h"
#include "ui/gfx/transform_util.h"
#include "ui/views/widget/widget.h"
@@ -215,7 +216,7 @@ Surface::~Surface() {
presentation_callbacks_.splice(presentation_callbacks_.end(),
pending_presentation_callbacks_);
for (const auto& presentation_callback : presentation_callbacks_)
- presentation_callback.Run(base::TimeTicks(), base::TimeDelta(), 0);
+ presentation_callback.Run(gfx::PresentationFeedback());
WMHelper::GetInstance()->ResetDragDropDelegate(window_.get());
}
diff --git a/chromium/components/exo/surface.h b/chromium/components/exo/surface.h
index 4f070a90dab..4c960a4d1b8 100644
--- a/chromium/components/exo/surface.h
+++ b/chromium/components/exo/surface.h
@@ -84,9 +84,7 @@ class Surface final : public ui::PropertyHandler {
// Request notification when the next frame is displayed. Useful for
// throttling redrawing operations, and driving animations.
using PresentationCallback =
- base::Callback<void(base::TimeTicks presentation_time,
- base::TimeDelta refresh,
- uint32_t flags)>;
+ base::Callback<void(const gfx::PresentationFeedback&)>;
void RequestPresentationCallback(const PresentationCallback& callback);
// This sets the region of the surface that contains opaque content.
diff --git a/chromium/components/exo/surface_tree_host.cc b/chromium/components/exo/surface_tree_host.cc
index 2bb0cf3d51c..53912d0debc 100644
--- a/chromium/components/exo/surface_tree_host.cc
+++ b/chromium/components/exo/surface_tree_host.cc
@@ -26,6 +26,7 @@
#include "ui/display/screen.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/path.h"
+#include "ui/gfx/presentation_feedback.h"
namespace exo {
@@ -120,19 +121,17 @@ void SurfaceTreeHost::SetRootSurface(Surface* root_surface) {
root_surface_->SurfaceHierarchyResourcesLost();
root_surface_ = nullptr;
- active_frame_callbacks_.splice(active_frame_callbacks_.end(),
- frame_callbacks_);
// Call all frame callbacks with a null frame time to indicate that they
// have been cancelled.
- while (!active_frame_callbacks_.empty()) {
- active_frame_callbacks_.front().Run(base::TimeTicks());
- active_frame_callbacks_.pop_front();
+ while (!frame_callbacks_.empty()) {
+ frame_callbacks_.front().Run(base::TimeTicks());
+ frame_callbacks_.pop_front();
}
DCHECK(presentation_callbacks_.empty());
for (auto entry : active_presentation_callbacks_) {
while (!entry.second.empty()) {
- entry.second.front().Run(base::TimeTicks(), base::TimeDelta(), 0);
+ entry.second.front().Run(gfx::PresentationFeedback());
entry.second.pop_front();
}
}
@@ -158,51 +157,22 @@ void SurfaceTreeHost::GetHitTestMask(gfx::Path* mask) const {
}
void SurfaceTreeHost::DidReceiveCompositorFrameAck() {
- active_frame_callbacks_.splice(active_frame_callbacks_.end(),
- frame_callbacks_);
- UpdateNeedsBeginFrame();
+ while (!frame_callbacks_.empty()) {
+ frame_callbacks_.front().Run(base::TimeTicks::Now());
+ frame_callbacks_.pop_front();
+ }
}
-void SurfaceTreeHost::DidPresentCompositorFrame(uint32_t presentation_token,
- base::TimeTicks time,
- base::TimeDelta refresh,
- uint32_t flags) {
+void SurfaceTreeHost::DidPresentCompositorFrame(
+ uint32_t presentation_token,
+ const gfx::PresentationFeedback& feedback) {
auto it = active_presentation_callbacks_.find(presentation_token);
DCHECK(it != active_presentation_callbacks_.end());
for (auto callback : it->second)
- callback.Run(time, refresh, flags);
+ callback.Run(feedback);
active_presentation_callbacks_.erase(it);
}
-void SurfaceTreeHost::DidDiscardCompositorFrame(uint32_t presentation_token) {
- DidPresentCompositorFrame(presentation_token, base::TimeTicks(),
- base::TimeDelta(), 0);
-}
-
-void SurfaceTreeHost::SetBeginFrameSource(
- viz::BeginFrameSource* begin_frame_source) {
- if (needs_begin_frame_) {
- DCHECK(begin_frame_source_);
- begin_frame_source_->RemoveObserver(this);
- needs_begin_frame_ = false;
- }
- begin_frame_source_ = begin_frame_source;
- UpdateNeedsBeginFrame();
-}
-
-void SurfaceTreeHost::UpdateNeedsBeginFrame() {
- if (!begin_frame_source_)
- return;
- bool needs_begin_frame = !active_frame_callbacks_.empty();
- if (needs_begin_frame == needs_begin_frame_)
- return;
- needs_begin_frame_ = needs_begin_frame;
- if (needs_begin_frame_)
- begin_frame_source_->AddObserver(this);
- else
- begin_frame_source_->RemoveObserver(this);
-}
-
////////////////////////////////////////////////////////////////////////////////
// SurfaceDelegate overrides:
@@ -223,62 +193,33 @@ bool SurfaceTreeHost::IsInputEnabled(Surface*) const {
}
////////////////////////////////////////////////////////////////////////////////
-// cc::BeginFrameObserverBase overrides:
-
-bool SurfaceTreeHost::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) {
- current_begin_frame_ack_ =
- viz::BeginFrameAck(args.source_id, args.sequence_number, false);
-
- if (!frame_callbacks_.empty()) {
- // In this case, the begin frame arrives just before
- // |DidReceivedCompositorFrameAck()|, we need more begin frames to run
- // |frame_callbacks_| which will be moved to |active_frame_callbacks_| by
- // |DidReceivedCompositorFrameAck()| shortly.
- layer_tree_frame_sink_holder_->DidNotProduceFrame(current_begin_frame_ack_);
- current_begin_frame_ack_.sequence_number =
- viz::BeginFrameArgs::kInvalidFrameNumber;
- begin_frame_source_->DidFinishFrame(this);
- }
-
- while (!active_frame_callbacks_.empty()) {
- active_frame_callbacks_.front().Run(args.frame_time);
- active_frame_callbacks_.pop_front();
- }
- return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
// ui::ContextFactoryObserver overrides:
-void SurfaceTreeHost::OnLostResources() {
+void SurfaceTreeHost::OnLostSharedContext() {
if (!host_window_->GetSurfaceId().is_valid() || !root_surface_)
return;
root_surface_->SurfaceHierarchyResourcesLost();
SubmitCompositorFrame();
}
+void SurfaceTreeHost::OnLostVizProcess() {}
+
////////////////////////////////////////////////////////////////////////////////
// SurfaceTreeHost, protected:
void SurfaceTreeHost::SubmitCompositorFrame() {
DCHECK(root_surface_);
viz::CompositorFrame frame;
- // If we commit while we don't have an active BeginFrame, we acknowledge a
- // manual one.
- if (current_begin_frame_ack_.sequence_number ==
- viz::BeginFrameArgs::kInvalidFrameNumber) {
- current_begin_frame_ack_ = viz::BeginFrameAck::CreateManualAckWithDamage();
- } else {
- current_begin_frame_ack_.has_damage = true;
- }
- frame.metadata.begin_frame_ack = current_begin_frame_ack_;
+ frame.metadata.begin_frame_ack =
+ viz::BeginFrameAck::CreateManualAckWithDamage();
root_surface_->AppendSurfaceHierarchyCallbacks(&frame_callbacks_,
&presentation_callbacks_);
if (!presentation_callbacks_.empty()) {
// If overflow happens, we increase it again.
if (!++presentation_token_)
++presentation_token_;
- frame.metadata.presentation_token = presentation_token_;
+ frame.metadata.frame_token = presentation_token_;
+ frame.metadata.request_presentation_feedback = true;
DCHECK_EQ(active_presentation_callbacks_.count(presentation_token_), 0u);
active_presentation_callbacks_[presentation_token_] =
std::move(presentation_callbacks_);
@@ -319,18 +260,6 @@ void SurfaceTreeHost::SubmitCompositorFrame() {
}
layer_tree_frame_sink_holder_->SubmitCompositorFrame(std::move(frame));
-
- if (current_begin_frame_ack_.sequence_number !=
- viz::BeginFrameArgs::kInvalidFrameNumber) {
- if (!current_begin_frame_ack_.has_damage) {
- layer_tree_frame_sink_holder_->DidNotProduceFrame(
- current_begin_frame_ack_);
- }
- current_begin_frame_ack_.sequence_number =
- viz::BeginFrameArgs::kInvalidFrameNumber;
- if (begin_frame_source_)
- begin_frame_source_->DidFinishFrame(this);
- }
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/chromium/components/exo/surface_tree_host.h b/chromium/components/exo/surface_tree_host.h
index 08719ace3c4..89bae81df29 100644
--- a/chromium/components/exo/surface_tree_host.h
+++ b/chromium/components/exo/surface_tree_host.h
@@ -11,7 +11,6 @@
#include "components/exo/layer_tree_frame_sink_holder.h"
#include "components/exo/surface.h"
#include "components/exo/surface_delegate.h"
-#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "ui/gfx/geometry/rect.h"
namespace aura {
@@ -22,17 +21,12 @@ namespace gfx {
class Path;
} // namespace gfx
-namespace viz {
-class BeginFrameSource;
-} // namespace viz
-
namespace exo {
class LayerTreeFrameSinkHolder;
// This class provides functionality for hosting a surface tree. The surface
// tree is hosted in the |host_window_|.
class SurfaceTreeHost : public SurfaceDelegate,
- public viz::BeginFrameObserverBase,
public ui::ContextFactoryObserver {
public:
explicit SurfaceTreeHost(const std::string& window_name);
@@ -56,20 +50,7 @@ class SurfaceTreeHost : public SurfaceDelegate,
// Call this to indicate that the CompositorFrame with given
// |presentation_token| has been first time presented to user.
void DidPresentCompositorFrame(uint32_t presentation_token,
- base::TimeTicks time,
- base::TimeDelta refresh,
- uint32_t flags);
-
- // Call this to indicate that the CompositorFrame with given
- // |presentation_token| has been discard. It has not been and will not be
- // presented to user.
- void DidDiscardCompositorFrame(uint32_t presentation_token);
-
- // Called when the begin frame source has changed.
- void SetBeginFrameSource(viz::BeginFrameSource* begin_frame_source);
-
- // Adds/Removes begin frame observer based on state.
- void UpdateNeedsBeginFrame();
+ const gfx::PresentationFeedback& feedback);
aura::Window* host_window() { return host_window_.get(); }
const aura::Window* host_window() const { return host_window_.get(); }
@@ -94,12 +75,9 @@ class SurfaceTreeHost : public SurfaceDelegate,
void OnSetStartupId(const char* startup_id) override {}
void OnSetApplicationId(const char* application_id) override {}
- // Overridden from cc::BeginFrameObserverBase:
- bool OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) override;
- void OnBeginFrameSourcePausedChanged(bool paused) override {}
-
// Overridden from ui::ContextFactoryObserver:
- void OnLostResources() override;
+ void OnLostSharedContext() override;
+ void OnLostVizProcess() override;
protected:
// Call this to submit a compositor frame.
@@ -117,18 +95,11 @@ class SurfaceTreeHost : public SurfaceDelegate,
std::unique_ptr<aura::Window> host_window_;
std::unique_ptr<LayerTreeFrameSinkHolder> layer_tree_frame_sink_holder_;
- // The begin frame source being observed.
- viz::BeginFrameSource* begin_frame_source_ = nullptr;
- bool needs_begin_frame_ = false;
- viz::BeginFrameAck current_begin_frame_ack_;
-
- // These lists contain the callbacks to notify the client when it is a good
+ // This list contains the callbacks to notify the client when it is a good
// time to start producing a new frame. These callbacks move to
- // |frame_callbacks_| when Commit() is called. Later they are moved to
- // |active_frame_callbacks_| when the effect of the Commit() is scheduled to
- // be drawn. They fire at the first begin frame notification after this.
+ // |frame_callbacks_| when Commit() is called. They fire when the effect
+ // of the Commit() is scheduled to be drawn.
std::list<Surface::FrameCallback> frame_callbacks_;
- std::list<Surface::FrameCallback> active_frame_callbacks_;
// These lists contains the callbacks to notify the client when surface
// contents have been presented.
diff --git a/chromium/components/exo/surface_unittest.cc b/chromium/components/exo/surface_unittest.cc
index cec5dc9b96d..27afc3804fe 100644
--- a/chromium/components/exo/surface_unittest.cc
+++ b/chromium/components/exo/surface_unittest.cc
@@ -168,9 +168,12 @@ void SetFrameTime(base::TimeTicks* result, base::TimeTicks frame_time) {
}
TEST_P(SurfaceTest, RequestFrameCallback) {
+ // Must be before surface so it outlives it, for surface's destructor calls
+ // SetFrameTime() referencing this.
+ base::TimeTicks frame_time;
+
std::unique_ptr<Surface> surface(new Surface);
- base::TimeTicks frame_time;
surface->RequestFrameCallback(
base::Bind(&SetFrameTime, base::Unretained(&frame_time)));
surface->Commit();
@@ -179,7 +182,13 @@ TEST_P(SurfaceTest, RequestFrameCallback) {
EXPECT_TRUE(frame_time.is_null());
}
-TEST_P(SurfaceTest, SetOpaqueRegion) {
+// Disabled due to flakiness: crbug.com/856145
+#if defined(LEAK_SANITIZER)
+#define MAYBE_SetOpaqueRegion DISABLED_SetOpaqueRegion
+#else
+#define MAYBE_SetOpaqueRegion SetOpaqueRegion
+#endif
+TEST_P(SurfaceTest, MAYBE_SetOpaqueRegion) {
gfx::Size buffer_size(1, 1);
auto buffer = std::make_unique<Buffer>(
exo_test_helper()->CreateGpuMemoryBuffer(buffer_size));
@@ -366,7 +375,13 @@ TEST_P(SurfaceTest, SetBufferScale) {
frame.render_pass_list.back()->damage_rect);
}
-TEST_P(SurfaceTest, SetBufferTransform) {
+// Disabled due to flakiness: crbug.com/856145
+#if defined(LEAK_SANITIZER)
+#define MAYBE_SetBufferTransform DISABLED_SetBufferTransform
+#else
+#define MAYBE_SetBufferTransform SetBufferTransform
+#endif
+TEST_P(SurfaceTest, MAYBE_SetBufferTransform) {
gfx::Size buffer_size(256, 512);
auto buffer = std::make_unique<Buffer>(
exo_test_helper()->CreateGpuMemoryBuffer(buffer_size));
@@ -519,7 +534,13 @@ TEST_P(SurfaceTest, SetCrop) {
frame.render_pass_list.back()->damage_rect);
}
-TEST_P(SurfaceTest, SetCropAndBufferTransform) {
+// Disabled due to flakiness: crbug.com/856145
+#if defined(LEAK_SANITIZER)
+#define MAYBE_SetCropAndBufferTransform DISABLED_SetCropAndBufferTransform
+#else
+#define MAYBE_SetCropAndBufferTransform SetCropAndBufferTransform
+#endif
+TEST_P(SurfaceTest, MAYBE_SetCropAndBufferTransform) {
gfx::Size buffer_size(128, 64);
auto buffer = std::make_unique<Buffer>(
exo_test_helper()->CreateGpuMemoryBuffer(buffer_size));
@@ -812,44 +833,6 @@ TEST_P(SurfaceTest, Commit) {
surface->Commit();
}
-TEST_P(SurfaceTest, SendsBeginFrameAcks) {
- viz::FakeExternalBeginFrameSource source(0.f, false);
- gfx::Size buffer_size(1, 1);
- auto buffer = std::make_unique<Buffer>(
- exo_test_helper()->CreateGpuMemoryBuffer(buffer_size), GL_TEXTURE_2D, 0,
- true, true);
- auto surface = std::make_unique<Surface>();
- auto shell_surface = std::make_unique<ShellSurface>(surface.get());
- shell_surface->SetBeginFrameSource(&source);
- surface->Attach(buffer.get());
-
- // Request a frame callback so that Surface now needs BeginFrames.
- base::TimeTicks frame_time;
- surface->RequestFrameCallback(
- base::Bind(&SetFrameTime, base::Unretained(&frame_time)));
- surface->Commit(); // Move callback from pending callbacks to current ones.
- RunAllPendingInMessageLoop();
-
- // Surface should add itself as observer during
- // DidReceiveCompositorFrameAck().
- shell_surface->DidReceiveCompositorFrameAck();
- EXPECT_EQ(1u, source.num_observers());
-
- viz::BeginFrameArgs args(source.CreateBeginFrameArgs(BEGINFRAME_FROM_HERE));
- args.frame_time = base::TimeTicks::FromInternalValue(100);
- source.TestOnBeginFrame(args); // Runs the frame callback.
- EXPECT_EQ(args.frame_time, frame_time);
-
- surface->Commit(); // Acknowledges the BeginFrame via CompositorFrame.
- RunAllPendingInMessageLoop();
-
- const viz::CompositorFrame& frame = GetFrameFromSurface(shell_surface.get());
- viz::BeginFrameAck expected_ack(args.source_id, args.sequence_number, true);
- EXPECT_EQ(expected_ack, frame.metadata.begin_frame_ack);
-
- // TODO(eseckler): Add test for DidNotProduceFrame plumbing.
-}
-
TEST_P(SurfaceTest, RemoveSubSurface) {
gfx::Size buffer_size(256, 256);
std::unique_ptr<Buffer> buffer(
diff --git a/chromium/components/exo/touch.cc b/chromium/components/exo/touch.cc
index 2ebdbb5950d..0f7359065c6 100644
--- a/chromium/components/exo/touch.cc
+++ b/chromium/components/exo/touch.cc
@@ -4,12 +4,15 @@
#include "components/exo/touch.h"
+#include "components/exo/shell_surface_base.h"
#include "components/exo/surface.h"
#include "components/exo/touch_delegate.h"
#include "components/exo/touch_stylus_delegate.h"
#include "components/exo/wm_helper.h"
#include "ui/aura/window.h"
#include "ui/events/event.h"
+#include "ui/wm/core/capture_controller.h"
+#include "ui/wm/core/window_util.h"
namespace exo {
namespace {
@@ -190,9 +193,9 @@ void Touch::OnSurfaceDestroying(Surface* surface) {
////////////////////////////////////////////////////////////////////////////////
// Touch, private:
-Surface* Touch::GetEffectiveTargetForEvent(ui::Event* event) const {
- Surface* target =
- Surface::AsSurface(static_cast<aura::Window*>(event->target()));
+Surface* Touch::GetEffectiveTargetForEvent(ui::LocatedEvent* event) const {
+ Surface* target = ShellSurfaceBase::GetTargetSurfaceForLocatedEvent(event);
+
if (!target)
return nullptr;
diff --git a/chromium/components/exo/touch.h b/chromium/components/exo/touch.h
index 88d1048ca2d..bfe7878494d 100644
--- a/chromium/components/exo/touch.h
+++ b/chromium/components/exo/touch.h
@@ -13,6 +13,7 @@
#include "ui/gfx/geometry/point_f.h"
namespace ui {
+class LocatedEvent;
class TouchEvent;
}
@@ -41,7 +42,7 @@ class Touch : public ui::EventHandler, public SurfaceObserver {
private:
// Returns the effective target for |event|.
- Surface* GetEffectiveTargetForEvent(ui::Event* event) const;
+ Surface* GetEffectiveTargetForEvent(ui::LocatedEvent* event) const;
// The delegate instance that all events are dispatched to.
TouchDelegate* const delegate_;
diff --git a/chromium/components/exo/wayland/BUILD.gn b/chromium/components/exo/wayland/BUILD.gn
index 0129450b78f..4000a2bec0e 100644
--- a/chromium/components/exo/wayland/BUILD.gn
+++ b/chromium/components/exo/wayland/BUILD.gn
@@ -162,7 +162,6 @@ executable("wayland_rects_client") {
deps = [
":client_support",
"//base",
- "//build/config:exe_and_shlib_deps",
"//skia",
"//third_party/wayland:wayland_client",
"//third_party/wayland-protocols:input_timestamps_protocol",
@@ -215,7 +214,6 @@ executable("wayland_simple_client") {
":client_support",
":simple",
"//base",
- "//build/config:exe_and_shlib_deps",
]
}
@@ -227,7 +225,6 @@ executable("wayland_subsurface_client") {
deps = [
":client_support",
"//base",
- "//build/config:exe_and_shlib_deps",
"//skia",
"//third_party/wayland:wayland_client",
"//third_party/wayland-protocols:linux_dmabuf_protocol",
@@ -275,7 +272,6 @@ executable("wayland_blur_client") {
":blur",
":client_support",
"//base",
- "//build/config:exe_and_shlib_deps",
]
}
@@ -287,7 +283,6 @@ executable("wayland_info_client") {
deps = [
":client_support",
"//base",
- "//build/config:exe_and_shlib_deps",
"//third_party/wayland:wayland_client",
"//ui/gfx/geometry",
]
@@ -316,7 +311,7 @@ test("wayland_client_perftests") {
"//cc:test_support",
"//components/exo",
"//components/viz/test:test_support",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//testing/perf",
"//ui/aura",
"//ui/aura:test_support",
@@ -343,7 +338,6 @@ if (ozone_platform_gbm) {
deps = [
":client_support",
"//base",
- "//build/config:exe_and_shlib_deps",
"//skia",
"//third_party/wayland:wayland_client",
"//third_party/wayland-protocols:linux_dmabuf_protocol",
diff --git a/chromium/components/exo/wayland/clients/client_base.cc b/chromium/components/exo/wayland/clients/client_base.cc
index 4dc5557e1c2..ec9728e4219 100644
--- a/chromium/components/exo/wayland/clients/client_base.cc
+++ b/chromium/components/exo/wayland/clients/client_base.cc
@@ -379,8 +379,8 @@ bool ClientBase::Init(const InitParams& params) {
LOG(ERROR) << "wl_display_connect failed";
return false;
}
- wl_registry* registry = wl_display_get_registry(display_.get());
- wl_registry_add_listener(registry, &g_registry_listener, &globals_);
+ registry_.reset(wl_display_get_registry(display_.get()));
+ wl_registry_add_listener(registry_.get(), &g_registry_listener, &globals_);
wl_display_roundtrip(display_.get());
diff --git a/chromium/components/exo/wayland/clients/client_base.h b/chromium/components/exo/wayland/clients/client_base.h
index 4b395dbf4aa..28df03908de 100644
--- a/chromium/components/exo/wayland/clients/client_base.h
+++ b/chromium/components/exo/wayland/clients/client_base.h
@@ -116,6 +116,7 @@ class ClientBase {
bool transparent_background_ = false;
std::unique_ptr<wl_display> display_;
+ std::unique_ptr<wl_registry> registry_;
std::unique_ptr<wl_surface> surface_;
std::unique_ptr<wl_shell_surface> shell_surface_;
Globals globals_;
diff --git a/chromium/components/exo/wayland/clients/client_helper.cc b/chromium/components/exo/wayland/clients/client_helper.cc
index 7d0a1ecaf75..a732f898d94 100644
--- a/chromium/components/exo/wayland/clients/client_helper.cc
+++ b/chromium/components/exo/wayland/clients/client_helper.cc
@@ -32,6 +32,7 @@ DEFAULT_DELETER(wl_compositor, wl_compositor_destroy)
DEFAULT_DELETER(wl_display, wl_display_disconnect)
DEFAULT_DELETER(wl_pointer, wl_pointer_destroy)
DEFAULT_DELETER(wl_region, wl_region_destroy)
+DEFAULT_DELETER(wl_registry, wl_registry_destroy)
DEFAULT_DELETER(wl_seat, wl_seat_destroy)
DEFAULT_DELETER(wl_shell, wl_shell_destroy)
DEFAULT_DELETER(wl_shell_surface, wl_shell_surface_destroy)
diff --git a/chromium/components/exo/wayland/clients/client_helper.h b/chromium/components/exo/wayland/clients/client_helper.h
index 310c18bfbe2..f7b0b86a4d8 100644
--- a/chromium/components/exo/wayland/clients/client_helper.h
+++ b/chromium/components/exo/wayland/clients/client_helper.h
@@ -38,6 +38,7 @@ DEFAULT_DELETER_FDECL(wl_compositor)
DEFAULT_DELETER_FDECL(wl_display)
DEFAULT_DELETER_FDECL(wl_pointer)
DEFAULT_DELETER_FDECL(wl_region)
+DEFAULT_DELETER_FDECL(wl_registry)
DEFAULT_DELETER_FDECL(wl_seat)
DEFAULT_DELETER_FDECL(wl_shell)
DEFAULT_DELETER_FDECL(wl_shell_surface)
diff --git a/chromium/components/exo/wayland/server.cc b/chromium/components/exo/wayland/server.cc
index 49bb13341a5..2bdfa1b4970 100644
--- a/chromium/components/exo/wayland/server.cc
+++ b/chromium/components/exo/wayland/server.cc
@@ -71,6 +71,7 @@
#include "components/exo/gamepad_delegate.h"
#include "components/exo/gaming_seat.h"
#include "components/exo/gaming_seat_delegate.h"
+#include "components/exo/input_method_surface.h"
#include "components/exo/keyboard.h"
#include "components/exo/keyboard_delegate.h"
#include "components/exo/keyboard_device_configuration_delegate.h"
@@ -1085,7 +1086,7 @@ void shell_surface_move(wl_client* client,
wl_resource* resource,
wl_resource* seat_resource,
uint32_t serial) {
- GetUserDataAs<ShellSurface>(resource)->Move();
+ GetUserDataAs<ShellSurface>(resource)->StartMove();
}
void shell_surface_resize(wl_client* client,
@@ -1376,49 +1377,137 @@ void bind_output(wl_client* client, void* data, uint32_t version, uint32_t id) {
////////////////////////////////////////////////////////////////////////////////
// xdg_positioner_interface:
+uint32_t InvertBitfield(uint32_t bitfield, uint32_t mask) {
+ return (bitfield & ~mask) | ((bitfield & mask) ^ mask);
+}
+
+// TODO(oshima): propagate x/y flip state to children.
struct WaylandPositioner {
- // Calculate and return position from current state.
- gfx::Point CalculatePosition() const {
- gfx::Point position;
+ static constexpr uint32_t kHorizontalAnchors =
+ ZXDG_POSITIONER_V6_ANCHOR_LEFT | ZXDG_POSITIONER_V6_ANCHOR_RIGHT;
+ static constexpr uint32_t kVerticalAnchors =
+ ZXDG_POSITIONER_V6_ANCHOR_TOP | ZXDG_POSITIONER_V6_ANCHOR_BOTTOM;
+ static constexpr uint32_t kHorizontalGravities =
+ ZXDG_POSITIONER_V6_GRAVITY_LEFT | ZXDG_POSITIONER_V6_GRAVITY_RIGHT;
+ static constexpr uint32_t kVerticalGravities =
+ ZXDG_POSITIONER_V6_GRAVITY_TOP | ZXDG_POSITIONER_V6_GRAVITY_BOTTOM;
+
+ static int CalculateX(const gfx::Size& size,
+ const gfx::Rect& anchor_rect,
+ uint32_t anchor,
+ uint32_t gravity,
+ int offset,
+ bool flipped) {
+ if (flipped) {
+ anchor = InvertBitfield(anchor, kHorizontalAnchors);
+ gravity = InvertBitfield(gravity, kHorizontalGravities);
+ offset = -offset;
+ }
+ int x = offset;
if (anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT)
- position.set_x(anchor_rect.x());
+ x += anchor_rect.x();
else if (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT)
- position.set_x(anchor_rect.right());
+ x += anchor_rect.right();
else
- position.set_x(anchor_rect.CenterPoint().x());
+ x += anchor_rect.CenterPoint().x();
+
+ if (gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT)
+ return x - size.width();
+ if (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)
+ return x;
+ return x - size.width() / 2;
+ }
+
+ static int CalculateY(const gfx::Size& size,
+ const gfx::Rect& anchor_rect,
+ uint32_t anchor,
+ uint32_t gravity,
+ int offset,
+ bool flipped) {
+ if (flipped) {
+ anchor = InvertBitfield(anchor, kVerticalAnchors);
+ gravity = InvertBitfield(gravity, kVerticalGravities);
+ offset = -offset;
+ }
+ int y = offset;
if (anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP)
- position.set_y(anchor_rect.y());
+ y += anchor_rect.y();
else if (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM)
- position.set_y(anchor_rect.bottom());
- else
- position.set_y(anchor_rect.CenterPoint().y());
-
- gfx::Vector2d gravity_offset;
-
- if (gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT)
- gravity_offset.set_x(size.width());
- else if (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)
- gravity_offset.set_x(0);
+ y += anchor_rect.bottom();
else
- gravity_offset.set_x(size.width() / 2);
+ y += anchor_rect.CenterPoint().y();
if (gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP)
- gravity_offset.set_y(size.height());
- else if (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)
- gravity_offset.set_y(0);
- else
- gravity_offset.set_y(size.height() / 2);
+ return y - size.height();
+ if (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)
+ return y;
+ return y - size.height() / 2;
+ }
+
+ // Calculate and return position from current state.
+ gfx::Point CalculatePosition(const gfx::Rect& work_area) {
+ // TODO(oshima): The size must be smaller than work area.
+
+ gfx::Rect bounds(gfx::Point(CalculateX(size, anchor_rect, anchor, gravity,
+ offset.x(), x_flipped),
+ CalculateY(size, anchor_rect, anchor, gravity,
+ offset.y(), y_flipped)),
+ size);
+
+ // Adjust x position if the bounds are not fully contained by the work area.
+ if (work_area.x() > bounds.x() || work_area.right() < bounds.right()) {
+ // Allow sliding horizontally if the surface is attached below
+ // or above the parent surface.
+ bool can_slide_x = (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM &&
+ gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) ||
+ (anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP &&
+ gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP);
+ if (adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X &&
+ can_slide_x) {
+ if (bounds.x() < work_area.x())
+ bounds.set_x(work_area.x());
+ else if (bounds.right() > work_area.right())
+ bounds.set_x(work_area.right() - size.width());
+ } else if (adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X) {
+ x_flipped = !x_flipped;
+ bounds.set_x(CalculateX(size, anchor_rect, anchor, gravity, offset.x(),
+ x_flipped));
+ }
+ }
- return position + offset - gravity_offset;
+ // Adjust y position if the bounds are not fully contained by the work area.
+ if (work_area.y() > bounds.y() || work_area.bottom() < bounds.bottom()) {
+ // Allow sliding vertically if the surface is attached left or
+ // right of the parent surface.
+ bool can_slide_y = (anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT &&
+ gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) ||
+ (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT &&
+ gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT);
+ if (adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y &&
+ can_slide_y) {
+ if (bounds.y() < work_area.y())
+ bounds.set_y(work_area.y());
+ else if (bounds.bottom() > work_area.bottom())
+ bounds.set_y(work_area.bottom() - size.height());
+ } else if (adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y) {
+ y_flipped = !y_flipped;
+ bounds.set_y(CalculateY(size, anchor_rect, anchor, gravity, offset.y(),
+ y_flipped));
+ }
+ }
+ return bounds.origin();
}
gfx::Size size;
gfx::Rect anchor_rect;
uint32_t anchor = ZXDG_POSITIONER_V6_ANCHOR_NONE;
uint32_t gravity = ZXDG_POSITIONER_V6_GRAVITY_NONE;
+ uint32_t adjustment = ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE;
gfx::Vector2d offset;
+ bool y_flipped = false;
+ bool x_flipped = false;
};
void xdg_positioner_v6_destroy(wl_client* client, wl_resource* resource) {
@@ -1484,11 +1573,10 @@ void xdg_positioner_v6_set_gravity(wl_client* client,
GetUserDataAs<WaylandPositioner>(resource)->gravity = gravity;
}
-void xdg_positioner_v6_set_constraint_adjustment(
- wl_client* client,
- wl_resource* resource,
- uint32_t constraint_adjustment) {
- NOTIMPLEMENTED();
+void xdg_positioner_v6_set_constraint_adjustment(wl_client* client,
+ wl_resource* resource,
+ uint32_t adjustment) {
+ GetUserDataAs<WaylandPositioner>(resource)->adjustment = adjustment;
}
void xdg_positioner_v6_set_offset(wl_client* client,
@@ -1607,7 +1695,7 @@ class WaylandToplevel : public aura::WindowObserver {
void Move() {
if (shell_surface_)
- shell_surface_->Move();
+ shell_surface_->StartMove();
}
void Resize(int component) {
@@ -1615,7 +1703,7 @@ class WaylandToplevel : public aura::WindowObserver {
return;
if (component != HTNOWHERE)
- shell_surface_->Resize(component);
+ shell_surface_->StartResize(component);
}
void SetMaximumSize(const gfx::Size& size) {
@@ -1792,18 +1880,43 @@ const struct zxdg_toplevel_v6_interface xdg_toplevel_v6_implementation = {
// Wrapper around shell surface that allows us to handle the case where the
// xdg surface resource is destroyed before the popup resource.
-class WaylandPopup {
+class WaylandPopup : aura::WindowObserver {
public:
WaylandPopup(wl_resource* resource, wl_resource* surface_resource)
- : resource_(resource), weak_ptr_factory_(this) {
- ShellSurface* shell_surface = GetUserDataAs<ShellSurface>(surface_resource);
- shell_surface->set_close_callback(
+ : resource_(resource),
+ shell_surface_(GetUserDataAs<ShellSurface>(surface_resource)),
+ weak_ptr_factory_(this) {
+ shell_surface_->host_window()->AddObserver(this);
+ shell_surface_->set_close_callback(
base::Bind(&WaylandPopup::OnClose, weak_ptr_factory_.GetWeakPtr()));
- shell_surface->set_configure_callback(
+ shell_surface_->set_configure_callback(
base::Bind(&HandleXdgSurfaceV6ConfigureCallback, surface_resource,
base::Bind(&WaylandPopup::OnConfigure,
weak_ptr_factory_.GetWeakPtr())));
}
+ ~WaylandPopup() override {
+ if (shell_surface_)
+ shell_surface_->host_window()->RemoveObserver(this);
+ }
+
+ void Grab() {
+ if (!shell_surface_) {
+ wl_resource_post_error(resource_, ZXDG_POPUP_V6_ERROR_INVALID_GRAB,
+ "the surface has already been destroyed");
+ return;
+ }
+ if (shell_surface_->GetWidget()) {
+ wl_resource_post_error(resource_, ZXDG_POPUP_V6_ERROR_INVALID_GRAB,
+ "grab must be called before construction");
+ return;
+ }
+ shell_surface_->Grab();
+ }
+
+ // Overridden from aura::WindowObserver:
+ void OnWindowDestroying(aura::Window* window) override {
+ shell_surface_ = nullptr;
+ }
private:
void OnClose() {
@@ -1819,6 +1932,7 @@ class WaylandPopup {
}
wl_resource* const resource_;
+ ShellSurface* shell_surface_;
base::WeakPtrFactory<WaylandPopup> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(WaylandPopup);
@@ -1832,7 +1946,7 @@ void xdg_popup_v6_grab(wl_client* client,
wl_resource* resource,
wl_resource* seat,
uint32_t serial) {
- NOTIMPLEMENTED();
+ GetUserDataAs<WaylandPopup>(resource)->Grab();
}
const struct zxdg_popup_v6_interface xdg_popup_v6_implementation = {
@@ -1878,15 +1992,37 @@ void xdg_surface_v6_get_popup(wl_client* client,
return;
}
- ShellSurface* parent = GetUserDataAs<ShellSurface>(parent_resource);
+ XdgShellSurface* parent = GetUserDataAs<XdgShellSurface>(parent_resource);
if (!parent->GetWidget()) {
wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED,
"popup parent not constructed");
return;
}
- gfx::Point position = GetUserDataAs<WaylandPositioner>(positioner_resource)
- ->CalculatePosition();
+ if (shell_surface->GetWidget()) {
+ wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
+ "get_popup is called after constructed");
+ return;
+ }
+
+ display::Display display =
+ display::Screen::GetScreen()->GetDisplayNearestWindow(
+ parent->GetWidget()->GetNativeWindow());
+ gfx::Rect work_area = display.work_area();
+ wm::ConvertRectFromScreen(parent->GetWidget()->GetNativeWindow(), &work_area);
+
+ WaylandPositioner* positioner =
+ GetUserDataAs<WaylandPositioner>(positioner_resource);
+ // Try layout using parent's flip state.
+ positioner->x_flipped = parent->x_flipped();
+ positioner->y_flipped = parent->y_flipped();
+
+ gfx::Point position = positioner->CalculatePosition(work_area);
+
+ // Remember the new flip state for its child popups.
+ shell_surface->set_x_flipped(positioner->x_flipped);
+ shell_surface->set_y_flipped(positioner->y_flipped);
+
// |position| is relative to the parent's contents view origin, and |origin|
// is in screen coordinates.
gfx::Point origin = position;
@@ -1897,6 +2033,8 @@ void xdg_surface_v6_get_popup(wl_client* client,
shell_surface->DisableMovement();
shell_surface->SetActivatable(false);
shell_surface->SetCanMinimize(false);
+ shell_surface->SetParent(parent);
+ shell_surface->SetPopup();
shell_surface->SetEnabled(true);
wl_resource* xdg_popup_resource =
@@ -2160,7 +2298,7 @@ void remote_surface_ack_configure(wl_client* client,
}
void remote_surface_move(wl_client* client, wl_resource* resource) {
- GetUserDataAs<ClientControlledShellSurface>(resource)->Move();
+ // DEPRECATED
}
void remote_surface_set_window_type(wl_client* client,
@@ -2169,7 +2307,7 @@ void remote_surface_set_window_type(wl_client* client,
if (type == ZCR_REMOTE_SURFACE_V1_WINDOW_TYPE_SYSTEM_UI) {
auto* widget = GetUserDataAs<ShellSurfaceBase>(resource)->GetWidget();
if (widget) {
- widget->GetNativeWindow()->SetProperty(ash::kShowInOverviewKey, false);
+ widget->GetNativeWindow()->SetProperty(ash::kHideInOverviewKey, true);
wm::SetWindowVisibilityAnimationType(
widget->GetNativeWindow(), wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
@@ -2359,6 +2497,16 @@ const struct zcr_notification_surface_v1_interface
notification_surface_set_app_id};
////////////////////////////////////////////////////////////////////////////////
+// input_method_surface_interface:
+
+void input_method_surface_destroy(wl_client* client, wl_resource* resource) {
+ wl_resource_destroy(resource);
+}
+
+const struct zcr_input_method_surface_v1_interface
+ input_method_surface_implementation = {input_method_surface_destroy};
+
+////////////////////////////////////////////////////////////////////////////////
// remote_shell_interface:
// Implements remote shell interface and monitors workspace state needed
@@ -2418,6 +2566,13 @@ class WaylandRemoteShell : public ash::TabletModeObserver,
return display_->CreateNotificationSurface(surface, notification_key);
}
+ std::unique_ptr<InputMethodSurface> CreateInputMethodSurface(
+ Surface* surface,
+ double default_device_scale_factor) {
+ return display_->CreateInputMethodSurface(surface,
+ default_device_scale_factor);
+ }
+
// Overridden from display::DisplayObserver:
void OnDisplayAdded(const display::Display& new_display) override {
ScheduleSendDisplayMetrics(0);
@@ -2633,10 +2788,15 @@ void HandleRemoteSurfaceBoundsChangedCallback(
reason = ZCR_REMOTE_SURFACE_V1_BOUNDS_CHANGE_REASON_DRAG_RESIZE;
} else if (bounds_change & ash::WindowResizer::kBoundsChange_Repositions) {
reason = ZCR_REMOTE_SURFACE_V1_BOUNDS_CHANGE_REASON_DRAG_MOVE;
- } else if (requested_state == ash::mojom::WindowStateType::LEFT_SNAPPED) {
- reason = ZCR_REMOTE_SURFACE_V1_BOUNDS_CHANGE_REASON_SNAP_TO_LEFT;
- } else if (requested_state == ash::mojom::WindowStateType::RIGHT_SNAPPED) {
- reason = ZCR_REMOTE_SURFACE_V1_BOUNDS_CHANGE_REASON_SNAP_TO_RIGHT;
+ }
+ // Override the reason only if the window enters snapped mode. If the window
+ // resizes by dragging in snapped mode, we need to keep the original reason.
+ if (requested_state != current_state) {
+ if (requested_state == ash::mojom::WindowStateType::LEFT_SNAPPED) {
+ reason = ZCR_REMOTE_SURFACE_V1_BOUNDS_CHANGE_REASON_SNAP_TO_LEFT;
+ } else if (requested_state == ash::mojom::WindowStateType::RIGHT_SNAPPED) {
+ reason = ZCR_REMOTE_SURFACE_V1_BOUNDS_CHANGE_REASON_SNAP_TO_RIGHT;
+ }
}
zcr_remote_surface_v1_send_bounds_changed(
resource, static_cast<uint32_t>(display_id >> 32),
@@ -2772,11 +2932,39 @@ void remote_shell_get_notification_surface(wl_client* client,
std::move(notification_surface));
}
+void remote_shell_get_input_method_surface(wl_client* client,
+ wl_resource* resource,
+ uint32_t id,
+ wl_resource* surface) {
+ if (GetUserDataAs<Surface>(surface)->HasSurfaceDelegate()) {
+ wl_resource_post_error(resource, ZCR_REMOTE_SHELL_V1_ERROR_ROLE,
+ "surface has already been assigned a role");
+ return;
+ }
+
+ std::unique_ptr<ClientControlledShellSurface> input_method_surface =
+ GetUserDataAs<WaylandRemoteShell>(resource)->CreateInputMethodSurface(
+ GetUserDataAs<Surface>(surface), GetDefaultDeviceScaleFactor());
+ if (!input_method_surface) {
+ wl_resource_post_error(resource, ZCR_REMOTE_SHELL_V1_ERROR_ROLE,
+ "Cannot create an IME surface");
+ return;
+ }
+
+ wl_resource* input_method_surface_resource =
+ wl_resource_create(client, &zcr_input_method_surface_v1_interface,
+ wl_resource_get_version(resource), id);
+ SetImplementation(input_method_surface_resource,
+ &input_method_surface_implementation,
+ std::move(input_method_surface));
+}
+
const struct zcr_remote_shell_v1_interface remote_shell_implementation = {
remote_shell_destroy, remote_shell_get_remote_surface,
- remote_shell_get_notification_surface};
+ remote_shell_get_notification_surface,
+ remote_shell_get_input_method_surface};
-const uint32_t remote_shell_version = 16;
+const uint32_t remote_shell_version = 17;
void bind_remote_shell(wl_client* client,
void* data,
@@ -3625,16 +3813,16 @@ class WaylandKeyboardDelegate : public WaylandInputDelegate,
}
void OnKeyboardEnter(
Surface* surface,
- const base::flat_set<ui::DomCode>& pressed_keys) override {
+ const base::flat_map<ui::DomCode, ui::DomCode>& pressed_keys) override {
wl_resource* surface_resource = GetSurfaceResource(surface);
DCHECK(surface_resource);
wl_array keys;
wl_array_init(&keys);
- for (auto key : pressed_keys) {
+ for (const auto& entry : pressed_keys) {
uint32_t* value =
static_cast<uint32_t*>(wl_array_add(&keys, sizeof(uint32_t)));
DCHECK(value);
- *value = DomCodeToKey(key);
+ *value = DomCodeToKey(entry.second);
}
wl_keyboard_send_enter(keyboard_resource_, next_serial(), surface_resource,
&keys);
@@ -4072,14 +4260,13 @@ void bind_viewporter(wl_client* client,
////////////////////////////////////////////////////////////////////////////////
// presentation_interface:
-void HandleSurfacePresentationCallback(wl_resource* resource,
- base::TimeTicks presentation_time,
- base::TimeDelta refresh,
- uint32_t flags) {
- if (presentation_time.is_null()) {
+void HandleSurfacePresentationCallback(
+ wl_resource* resource,
+ const gfx::PresentationFeedback& feedback) {
+ if (feedback.timestamp.is_null()) {
wp_presentation_feedback_send_discarded(resource);
} else {
- int64_t presentation_time_us = presentation_time.ToInternalValue();
+ int64_t presentation_time_us = feedback.timestamp.ToInternalValue();
int64_t seconds = presentation_time_us / base::Time::kMicrosecondsPerSecond;
int64_t microseconds =
presentation_time_us % base::Time::kMicrosecondsPerSecond;
@@ -4103,8 +4290,9 @@ void HandleSurfacePresentationCallback(wl_resource* resource,
wp_presentation_feedback_send_presented(
resource, seconds >> 32, seconds & 0xffffffff,
microseconds * base::Time::kNanosecondsPerMicrosecond,
- refresh.InMicroseconds() * base::Time::kNanosecondsPerMicrosecond, 0, 0,
- flags);
+ feedback.interval.InMicroseconds() *
+ base::Time::kNanosecondsPerMicrosecond,
+ 0, 0, feedback.flags);
}
wl_client_flush(wl_resource_get_client(resource));
}
@@ -4122,8 +4310,8 @@ void presentation_feedback(wl_client* client,
wl_resource_get_version(resource), id);
// base::Unretained is safe as the resource owns the callback.
- auto cancelable_callback = std::make_unique<base::CancelableCallback<void(
- base::TimeTicks, base::TimeDelta, uint32_t)>>(
+ auto cancelable_callback = std::make_unique<
+ base::CancelableCallback<void(const gfx::PresentationFeedback&)>>(
base::Bind(&HandleSurfacePresentationCallback,
base::Unretained(presentation_feedback_resource)));
diff --git a/chromium/components/exo/xdg_shell_surface.h b/chromium/components/exo/xdg_shell_surface.h
index 9a031f5ed89..f8f4c8ebd7c 100644
--- a/chromium/components/exo/xdg_shell_surface.h
+++ b/chromium/components/exo/xdg_shell_surface.h
@@ -50,7 +50,18 @@ class XdgShellSurface : public ShellSurface {
int container);
~XdgShellSurface() override;
+ bool x_flipped() const { return x_flipped_; }
+ void set_x_flipped(bool flipped) { x_flipped_ = flipped; }
+
+ bool y_flipped() const { return y_flipped_; }
+ void set_y_flipped(bool flipped) { y_flipped_ = flipped; }
+
private:
+ // Used by positioner to layout cascading menus in opposite
+ // direction when the layout does not fit to the work area.
+ bool y_flipped_ = false;
+ bool x_flipped_ = false;
+
DISALLOW_COPY_AND_ASSIGN(XdgShellSurface);
};
diff --git a/chromium/components/favicon/content/content_favicon_driver.cc b/chromium/components/favicon/content/content_favicon_driver.cc
index 08260fda1b8..e0f79a29e31 100644
--- a/chromium/components/favicon/content/content_favicon_driver.cc
+++ b/chromium/components/favicon/content/content_favicon_driver.cc
@@ -30,7 +30,7 @@ void ExtractManifestIcons(
const GURL& manifest_url,
const blink::Manifest& manifest) {
std::vector<FaviconURL> candidates;
- for (const blink::Manifest::Icon& icon : manifest.icons) {
+ for (const auto& icon : manifest.icons) {
candidates.emplace_back(icon.src, favicon_base::IconType::kWebManifestIcon,
icon.sizes);
}
diff --git a/chromium/components/favicon/content/content_favicon_driver_unittest.cc b/chromium/components/favicon/content/content_favicon_driver_unittest.cc
index 9ce888d5f01..77f043974c9 100644
--- a/chromium/components/favicon/content/content_favicon_driver_unittest.cc
+++ b/chromium/components/favicon/content/content_favicon_driver_unittest.cc
@@ -9,7 +9,7 @@
#include "base/macros.h"
#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/favicon/core/favicon_client.h"
#include "components/favicon/core/favicon_handler.h"
#include "components/favicon/core/test/mock_favicon_service.h"
diff --git a/chromium/components/favicon/core/favicon_handler_unittest.cc b/chromium/components/favicon/core/favicon_handler_unittest.cc
index 406c043157e..67ce9a4c979 100644
--- a/chromium/components/favicon/core/favicon_handler_unittest.cc
+++ b/chromium/components/favicon/core/favicon_handler_unittest.cc
@@ -15,7 +15,7 @@
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/test_simple_task_runner.h"
diff --git a/chromium/components/favicon/core/large_icon_service_unittest.cc b/chromium/components/favicon/core/large_icon_service_unittest.cc
index acf0f2d75e6..8ef55f2b3f5 100644
--- a/chromium/components/favicon/core/large_icon_service_unittest.cc
+++ b/chromium/components/favicon/core/large_icon_service_unittest.cc
@@ -12,7 +12,7 @@
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted_memory.h"
#include "base/task/cancelable_task_tracker.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
diff --git a/chromium/components/favicon/ios/DEPS b/chromium/components/favicon/ios/DEPS
index 715e1402f93..2c094958c55 100644
--- a/chromium/components/favicon/ios/DEPS
+++ b/chromium/components/favicon/ios/DEPS
@@ -1,6 +1,7 @@
include_rules = [
"+components/image_fetcher/ios",
"+ios/web/public",
+ "+services/network/public/cpp",
# Only parts of skia are compiled on iOS, so we explicitly list the
# files that can be included to avoid bringing in more code.
diff --git a/chromium/components/favicon/ios/web_favicon_driver.mm b/chromium/components/favicon/ios/web_favicon_driver.mm
index e46e39c3184..9b708fff823 100644
--- a/chromium/components/favicon/ios/web_favicon_driver.mm
+++ b/chromium/components/favicon/ios/web_favicon_driver.mm
@@ -15,6 +15,7 @@
#include "ios/web/public/navigation_manager.h"
#include "ios/web/public/web_state/navigation_context.h"
#include "ios/web/public/web_state/web_state.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "skia/ext/skia_utils_ios.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/image/image.h"
@@ -75,7 +76,7 @@ int WebFaviconDriver::DownloadImage(const GURL& url,
GURL local_url(url);
__block ImageDownloadCallback local_callback = std::move(callback);
- image_fetcher::IOSImageDataFetcherCallback ios_callback =
+ image_fetcher::ImageDataFetcherBlock ios_callback =
^(NSData* data, const image_fetcher::RequestMetadata& metadata) {
if (metadata.http_response_code ==
image_fetcher::RequestMetadata::RESPONSE_CODE_INVALID)
@@ -154,7 +155,7 @@ WebFaviconDriver::WebFaviconDriver(web::WebState* web_state,
FaviconService* favicon_service,
history::HistoryService* history_service)
: FaviconDriverImpl(favicon_service, history_service),
- image_fetcher_(web_state->GetBrowserState()->GetRequestContext()),
+ image_fetcher_(web_state->GetBrowserState()->GetSharedURLLoaderFactory()),
web_state_(web_state) {
web_state_->AddObserver(this);
}
diff --git a/chromium/components/feature_engagement/README.md b/chromium/components/feature_engagement/README.md
index 5e8d5cd0f36..ea4a571140b 100644
--- a/chromium/components/feature_engagement/README.md
+++ b/chromium/components/feature_engagement/README.md
@@ -241,17 +241,18 @@ configuration:
1. Add feature to the histogram suffix `IPHFeatures` in:
`//tools/metrics/histograms/histograms.xml`.
* The suffix must match the `base::Feature` `name` member of your feature.
-1. Add feature to the actions file (actions do not support automatic suffixes):
- `//tools/metrics/actions/actions.xml`.
+1. Add feature to the actions file at: `//tools/metrics/actions/actions.xml`.
* The suffix must match the `base::Feature` `name` member.
- * Use an old example to ensure you configure and describe it correctly.
- * For `IPH_MyFunFeature` it would look like this:
- * `<action name="InProductHelp.NotifyEvent.IPH_MyFunFeature">`
- * `<action name="InProductHelp.NotifyUsedEvent.IPH_MyFunFeature">`
- * `<action name="InProductHelp.ShouldTriggerHelpUI.IPH_MyFunFeature">`
- * `<action name="InProductHelp.ShouldTriggerHelpUIResult.NotTriggered.IPH_MyFunFeature">`
- * `<action name="InProductHelp.ShouldTriggerHelpUIResult.Triggered.IPH_MyFunFeature">`
- * `<action name="InProductHelp.ShouldTriggerHelpUIResult.WouldHaveTriggered.IPH_MyFunFeature">`
+ * Find the `<action-suffix>` entry at the end of the file, where the
+ following `<affected-action>`s are listed:
+ * `InProductHelp.NotifyEvent.IPH`
+ * `InProductHelp.NotifyUsedEvent.IPH`
+ * `InProductHelp.ShouldTriggerHelpUI.IPH`
+ * `InProductHelp.ShouldTriggerHelpUIResult.NotTriggered.IPH`
+ * `InProductHelp.ShouldTriggerHelpUIResult.Triggered.IPH`
+ * `InProductHelp.ShouldTriggerHelpUIResult.WouldHaveTriggered.IPH`
+ * Add an alphebetically sorted entry to the list of `<suffix>`es like:
+ `<suffix name="MyFunFeature" label="For MyFunFeature feature."/>`
### Adding a local field trial testing configuration
diff --git a/chromium/components/feature_engagement/internal/android/tracker_impl_android.cc b/chromium/components/feature_engagement/internal/android/tracker_impl_android.cc
index a0c6dcbba97..b6e39d7887f 100644
--- a/chromium/components/feature_engagement/internal/android/tracker_impl_android.cc
+++ b/chromium/components/feature_engagement/internal/android/tracker_impl_android.cc
@@ -159,12 +159,9 @@ void TrackerImplAndroid::AddOnInitializedCallback(
JNIEnv* env,
const base::android::JavaRef<jobject>& jobj,
const base::android::JavaParamRef<jobject>& j_callback_obj) {
- // Disambiguate RunCallbackAndroid to get the reference to the bool version.
- void (*runBoolCallback)(const base::android::JavaRef<jobject>&, bool) =
- &base::android::RunCallbackAndroid;
- tracker_impl_->AddOnInitializedCallback(
- base::Bind(runBoolCallback,
- base::android::ScopedJavaGlobalRef<jobject>(j_callback_obj)));
+ tracker_impl_->AddOnInitializedCallback(base::BindOnce(
+ &base::android::RunBooleanCallbackAndroid,
+ base::android::ScopedJavaGlobalRef<jobject>(j_callback_obj)));
}
DisplayLockHandleAndroid::DisplayLockHandleAndroid(
diff --git a/chromium/components/feature_engagement/internal/chrome_variations_configuration_unittest.cc b/chromium/components/feature_engagement/internal/chrome_variations_configuration_unittest.cc
index 6b7f43303de..222cf1f791d 100644
--- a/chromium/components/feature_engagement/internal/chrome_variations_configuration_unittest.cc
+++ b/chromium/components/feature_engagement/internal/chrome_variations_configuration_unittest.cc
@@ -12,7 +12,7 @@
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_param_associator.h"
#include "base/metrics/field_trial_params.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "components/feature_engagement/internal/configuration.h"
#include "components/feature_engagement/internal/stats.h"
diff --git a/chromium/components/feature_engagement/internal/feature_config_condition_validator.cc b/chromium/components/feature_engagement/internal/feature_config_condition_validator.cc
index cb5b7eebe21..32d1f5f5e79 100644
--- a/chromium/components/feature_engagement/internal/feature_config_condition_validator.cc
+++ b/chromium/components/feature_engagement/internal/feature_config_condition_validator.cc
@@ -9,6 +9,7 @@
#include <vector>
#include "base/feature_list.h"
+#include "base/stl_util.h"
#include "components/feature_engagement/internal/availability_model.h"
#include "components/feature_engagement/internal/configuration.h"
#include "components/feature_engagement/internal/display_lock_controller.h"
@@ -79,8 +80,7 @@ void FeatureConfigConditionValidator::NotifyIsShowing(
DCHECK(config.session_rate_impact.affected_features.has_value());
for (const std::string& feature_name :
config.session_rate_impact.affected_features.value()) {
- DCHECK(std::find(all_feature_names.begin(), all_feature_names.end(),
- feature_name) != all_feature_names.end());
+ DCHECK(base::ContainsValue(all_feature_names, feature_name));
++times_shown_for_feature_[feature_name];
}
break;
diff --git a/chromium/components/feature_engagement/internal/persistent_event_store_unittest.cc b/chromium/components/feature_engagement/internal/persistent_event_store_unittest.cc
index c2b11acc861..149cb76c1e0 100644
--- a/chromium/components/feature_engagement/internal/persistent_event_store_unittest.cc
+++ b/chromium/components/feature_engagement/internal/persistent_event_store_unittest.cc
@@ -8,7 +8,7 @@
#include "base/files/file_path.h"
#include "base/optional.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/feature_engagement/internal/proto/event.pb.h"
#include "components/feature_engagement/internal/stats.h"
#include "components/feature_engagement/internal/test/event_util.h"
diff --git a/chromium/components/feature_engagement/internal/tracker_impl.cc b/chromium/components/feature_engagement/internal/tracker_impl.cc
index 971c7b5cbbb..457030ed350 100644
--- a/chromium/components/feature_engagement/internal/tracker_impl.cc
+++ b/chromium/components/feature_engagement/internal/tracker_impl.cc
@@ -248,11 +248,11 @@ bool TrackerImpl::IsInitialized() const {
void TrackerImpl::AddOnInitializedCallback(OnInitializedCallback callback) {
if (IsInitializationFinished()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(callback, IsInitialized()));
+ FROM_HERE, base::BindOnce(std::move(callback), IsInitialized()));
return;
}
- on_initialized_callbacks_.push_back(callback);
+ on_initialized_callbacks_.push_back(std::move(callback));
}
void TrackerImpl::OnEventModelInitializationFinished(bool success) {
@@ -286,7 +286,7 @@ void TrackerImpl::MaybePostInitializedCallbacks() {
for (auto& callback : on_initialized_callbacks_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(callback, IsInitialized()));
+ FROM_HERE, base::BindOnce(std::move(callback), IsInitialized()));
}
on_initialized_callbacks_.clear();
diff --git a/chromium/components/feature_engagement/internal/tracker_impl_unittest.cc b/chromium/components/feature_engagement/internal/tracker_impl_unittest.cc
index 5fb4c8bab23..45482486e02 100644
--- a/chromium/components/feature_engagement/internal/tracker_impl_unittest.cc
+++ b/chromium/components/feature_engagement/internal/tracker_impl_unittest.cc
@@ -4,6 +4,7 @@
#include "components/feature_engagement/internal/tracker_impl.h"
+#include <map>
#include <memory>
#include <utility>
@@ -14,8 +15,8 @@
#include "base/run_loop.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
-#include "base/test/histogram_tester.h"
-#include "base/test/user_action_tester.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/metrics/user_action_tester.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/feature_engagement/internal/availability_model_impl.h"
#include "components/feature_engagement/internal/display_lock_controller.h"
@@ -458,7 +459,7 @@ TEST_F(TrackerImplTest, TestInitialization) {
EXPECT_FALSE(tracker_->IsInitialized());
StoringInitializedCallback callback;
- tracker_->AddOnInitializedCallback(base::Bind(
+ tracker_->AddOnInitializedCallback(base::BindOnce(
&StoringInitializedCallback::OnInitialized, base::Unretained(&callback)));
EXPECT_FALSE(callback.invoked());
@@ -477,11 +478,11 @@ TEST_F(TrackerImplTest, TestInitializationMultipleCallbacks) {
StoringInitializedCallback callback2;
tracker_->AddOnInitializedCallback(
- base::Bind(&StoringInitializedCallback::OnInitialized,
- base::Unretained(&callback1)));
+ base::BindOnce(&StoringInitializedCallback::OnInitialized,
+ base::Unretained(&callback1)));
tracker_->AddOnInitializedCallback(
- base::Bind(&StoringInitializedCallback::OnInitialized,
- base::Unretained(&callback2)));
+ base::BindOnce(&StoringInitializedCallback::OnInitialized,
+ base::Unretained(&callback2)));
EXPECT_FALSE(callback1.invoked());
EXPECT_FALSE(callback2.invoked());
@@ -504,7 +505,7 @@ TEST_F(TrackerImplTest, TestAddingCallbackAfterInitFinished) {
EXPECT_TRUE(tracker_->IsInitialized());
StoringInitializedCallback callback;
- tracker_->AddOnInitializedCallback(base::Bind(
+ tracker_->AddOnInitializedCallback(base::BindOnce(
&StoringInitializedCallback::OnInitialized, base::Unretained(&callback)));
EXPECT_FALSE(callback.invoked());
@@ -523,8 +524,8 @@ TEST_F(TrackerImplTest, TestAddingCallbackBeforeAndAfterInitFinished) {
StoringInitializedCallback callback_before;
tracker_->AddOnInitializedCallback(
- base::Bind(&StoringInitializedCallback::OnInitialized,
- base::Unretained(&callback_before)));
+ base::BindOnce(&StoringInitializedCallback::OnInitialized,
+ base::Unretained(&callback_before)));
EXPECT_FALSE(callback_before.invoked());
base::RunLoop().RunUntilIdle();
@@ -533,8 +534,8 @@ TEST_F(TrackerImplTest, TestAddingCallbackBeforeAndAfterInitFinished) {
StoringInitializedCallback callback_after;
tracker_->AddOnInitializedCallback(
- base::Bind(&StoringInitializedCallback::OnInitialized,
- base::Unretained(&callback_after)));
+ base::BindOnce(&StoringInitializedCallback::OnInitialized,
+ base::Unretained(&callback_after)));
EXPECT_FALSE(callback_after.invoked());
base::RunLoop().RunUntilIdle();
@@ -546,7 +547,7 @@ TEST_F(FailingStoreInitTrackerImplTest, TestFailingInitialization) {
EXPECT_FALSE(tracker_->IsInitialized());
StoringInitializedCallback callback;
- tracker_->AddOnInitializedCallback(base::Bind(
+ tracker_->AddOnInitializedCallback(base::BindOnce(
&StoringInitializedCallback::OnInitialized, base::Unretained(&callback)));
EXPECT_FALSE(callback.invoked());
@@ -565,11 +566,11 @@ TEST_F(FailingStoreInitTrackerImplTest,
StoringInitializedCallback callback1;
StoringInitializedCallback callback2;
tracker_->AddOnInitializedCallback(
- base::Bind(&StoringInitializedCallback::OnInitialized,
- base::Unretained(&callback1)));
+ base::BindOnce(&StoringInitializedCallback::OnInitialized,
+ base::Unretained(&callback1)));
tracker_->AddOnInitializedCallback(
- base::Bind(&StoringInitializedCallback::OnInitialized,
- base::Unretained(&callback2)));
+ base::BindOnce(&StoringInitializedCallback::OnInitialized,
+ base::Unretained(&callback2)));
EXPECT_FALSE(callback1.invoked());
EXPECT_FALSE(callback2.invoked());
@@ -587,7 +588,7 @@ TEST_F(FailingAvailabilityModelInitTrackerImplTest, AvailabilityModelNotReady) {
EXPECT_FALSE(tracker_->IsInitialized());
StoringInitializedCallback callback;
- tracker_->AddOnInitializedCallback(base::Bind(
+ tracker_->AddOnInitializedCallback(base::BindOnce(
&StoringInitializedCallback::OnInitialized, base::Unretained(&callback)));
EXPECT_FALSE(callback.invoked());
@@ -602,7 +603,7 @@ TEST_F(FailingAvailabilityModelInitTrackerImplTest, AvailabilityModelNotReady) {
TEST_F(TrackerImplTest, TestTriggering) {
// Ensure all initialization is finished.
StoringInitializedCallback callback;
- tracker_->AddOnInitializedCallback(base::Bind(
+ tracker_->AddOnInitializedCallback(base::BindOnce(
&StoringInitializedCallback::OnInitialized, base::Unretained(&callback)));
base::RunLoop().RunUntilIdle();
base::UserActionTester user_action_tester;
@@ -671,7 +672,7 @@ TEST_F(TrackerImplTest, TestTriggering) {
TEST_F(TrackerImplTest, TestTrackingOnlyTriggering) {
// Ensure all initialization is finished.
StoringInitializedCallback callback;
- tracker_->AddOnInitializedCallback(base::Bind(
+ tracker_->AddOnInitializedCallback(base::BindOnce(
&StoringInitializedCallback::OnInitialized, base::Unretained(&callback)));
base::RunLoop().RunUntilIdle();
base::UserActionTester user_action_tester;
@@ -720,7 +721,7 @@ TEST_F(TrackerImplTest, TestTrackingOnlyTriggering) {
TEST_F(TrackerImplTest, TestWouldTriggerInspection) {
// Ensure all initialization is finished.
StoringInitializedCallback callback;
- tracker_->AddOnInitializedCallback(base::Bind(
+ tracker_->AddOnInitializedCallback(base::BindOnce(
&StoringInitializedCallback::OnInitialized, base::Unretained(&callback)));
base::RunLoop().RunUntilIdle();
base::UserActionTester user_action_tester;
@@ -782,7 +783,7 @@ TEST_F(TrackerImplTest, TestTriggerStateInspection) {
// Ensure all initialization is finished.
StoringInitializedCallback callback;
- tracker_->AddOnInitializedCallback(base::Bind(
+ tracker_->AddOnInitializedCallback(base::BindOnce(
&StoringInitializedCallback::OnInitialized, base::Unretained(&callback)));
base::RunLoop().RunUntilIdle();
base::UserActionTester user_action_tester;
@@ -823,7 +824,7 @@ TEST_F(TrackerImplTest, TestTriggerStateInspection) {
TEST_F(TrackerImplTest, TestNotifyEvent) {
StoringInitializedCallback callback;
- tracker_->AddOnInitializedCallback(base::Bind(
+ tracker_->AddOnInitializedCallback(base::BindOnce(
&StoringInitializedCallback::OnInitialized, base::Unretained(&callback)));
base::RunLoop().RunUntilIdle();
base::UserActionTester user_action_tester;
diff --git a/chromium/components/feature_engagement/public/event_constants.cc b/chromium/components/feature_engagement/public/event_constants.cc
index b0e351a465f..3700e129e9c 100644
--- a/chromium/components/feature_engagement/public/event_constants.cc
+++ b/chromium/components/feature_engagement/public/event_constants.cc
@@ -33,6 +33,7 @@ const char kChromeOpened[] = "chrome_opened";
const char kIncognitoTabOpened[] = "incognito_tab_opened";
const char kClearedBrowsingData[] = "cleared_browsing_data";
const char kViewedReadingList[] = "viewed_reading_list";
+const char kBottomToolbarOpened[] = "bottom_toolbar_opened";
#endif // defined(OS_IOS)
} // namespace events
diff --git a/chromium/components/feature_engagement/public/event_constants.h b/chromium/components/feature_engagement/public/event_constants.h
index 1b95ad1b7ab..d45d76a4fa6 100644
--- a/chromium/components/feature_engagement/public/event_constants.h
+++ b/chromium/components/feature_engagement/public/event_constants.h
@@ -64,6 +64,9 @@ extern const char kClearedBrowsingData[];
// The user has viewed their reading list.
extern const char kViewedReadingList[];
+
+// The user has viewed the the BottomToolbar tip.
+extern const char kBottomToolbarOpened[];
#endif // defined(OS_IOS)
} // namespace events
diff --git a/chromium/components/feature_engagement/public/feature_constants.cc b/chromium/components/feature_engagement/public/feature_constants.cc
index 1ecbaf9b26b..d04bdcf1118 100644
--- a/chromium/components/feature_engagement/public/feature_constants.cc
+++ b/chromium/components/feature_engagement/public/feature_constants.cc
@@ -63,12 +63,16 @@ const base::Feature kIPHNewTabFeature{"IPH_NewTab",
#endif // BUILDFLAG(ENABLE_DESKTOP_IPH)
#if defined(OS_IOS)
+const base::Feature kIPHBottomToolbarTipFeature{
+ "IPH_BottomToolbarTip", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kIPHLongPressToolbarTipFeature{
+ "IPH_LongPressToolbarTip", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHNewTabTipFeature{"IPH_NewTabTip",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHNewIncognitoTabTipFeature{
"IPH_NewIncognitoTabTip", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHBadgedReadingListFeature{
- "IPH_BadgedReadingList", base::FEATURE_DISABLED_BY_DEFAULT};
+ "IPH_BadgedReadingList", base::FEATURE_ENABLED_BY_DEFAULT};
#endif // defined(OS_IOS)
} // namespace feature_engagement
diff --git a/chromium/components/feature_engagement/public/feature_constants.h b/chromium/components/feature_engagement/public/feature_constants.h
index fb9333c383a..cd8c7664a8b 100644
--- a/chromium/components/feature_engagement/public/feature_constants.h
+++ b/chromium/components/feature_engagement/public/feature_constants.h
@@ -48,6 +48,8 @@ extern const base::Feature kIPHNewTabFeature;
#endif // BUILDFLAG(ENABLE_DESKTOP_IPH)
#if defined(OS_IOS)
+extern const base::Feature kIPHBottomToolbarTipFeature;
+extern const base::Feature kIPHLongPressToolbarTipFeature;
extern const base::Feature kIPHNewTabTipFeature;
extern const base::Feature kIPHNewIncognitoTabTipFeature;
extern const base::Feature kIPHBadgedReadingListFeature;
diff --git a/chromium/components/feature_engagement/public/feature_list.cc b/chromium/components/feature_engagement/public/feature_list.cc
index 3b602b105cf..3b59190e477 100644
--- a/chromium/components/feature_engagement/public/feature_list.cc
+++ b/chromium/components/feature_engagement/public/feature_list.cc
@@ -41,6 +41,8 @@ const base::Feature* const kAllFeatures[] = {
&kIPHNewTabFeature,
#endif // BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
#if defined(OS_IOS)
+ &kIPHBottomToolbarTipFeature,
+ &kIPHLongPressToolbarTipFeature,
&kIPHNewTabTipFeature,
&kIPHNewIncognitoTabTipFeature,
&kIPHBadgedReadingListFeature,
diff --git a/chromium/components/feature_engagement/public/feature_list.h b/chromium/components/feature_engagement/public/feature_list.h
index 65174b4b588..69e73a29557 100644
--- a/chromium/components/feature_engagement/public/feature_list.h
+++ b/chromium/components/feature_engagement/public/feature_list.h
@@ -80,6 +80,9 @@ DEFINE_VARIATION_PARAM(kIPHIncognitoWindowFeature, "IPH_IncognitoWindow");
DEFINE_VARIATION_PARAM(kIPHNewTabFeature, "IPH_NewTab");
#endif // BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
#if defined(OS_IOS)
+DEFINE_VARIATION_PARAM(kIPHBottomToolbarTipFeature, "IPH_BottomToolbarTip");
+DEFINE_VARIATION_PARAM(kIPHLongPressToolbarTipFeature,
+ "IPH_LongPressToolbarTip");
DEFINE_VARIATION_PARAM(kIPHNewTabTipFeature, "IPH_NewTabTip");
DEFINE_VARIATION_PARAM(kIPHNewIncognitoTabTipFeature, "IPH_NewIncognitoTabTip");
DEFINE_VARIATION_PARAM(kIPHBadgedReadingListFeature, "IPH_BadgedReadingList");
@@ -116,6 +119,8 @@ constexpr flags_ui::FeatureEntry::FeatureVariation
VARIATION_ENTRY(kIPHIncognitoWindowFeature),
VARIATION_ENTRY(kIPHNewTabFeature),
#elif defined(OS_IOS)
+ VARIATION_ENTRY(kIPHBottomToolbarTipFeature),
+ VARIATION_ENTRY(kIPHLongPressToolbarTipFeature),
VARIATION_ENTRY(kIPHNewTabTipFeature),
VARIATION_ENTRY(kIPHNewIncognitoTabTipFeature),
VARIATION_ENTRY(kIPHBadgedReadingListFeature),
diff --git a/chromium/components/feature_engagement/public/tracker.h b/chromium/components/feature_engagement/public/tracker.h
index 99039d3a665..4147bafc85f 100644
--- a/chromium/components/feature_engagement/public/tracker.h
+++ b/chromium/components/feature_engagement/public/tracker.h
@@ -65,7 +65,7 @@ class Tracker : public KeyedService {
// Invoked when the tracker has been initialized. The |success| parameter
// indicates that the initialization was a success and the tracker is ready to
// receive calls.
- using OnInitializedCallback = base::Callback<void(bool success)>;
+ using OnInitializedCallback = base::OnceCallback<void(bool success)>;
// The |storage_dir| is the path to where all local storage will be.
// The |bakground_task_runner| will be used for all disk reads and writes.
diff --git a/chromium/components/feed/DEPS b/chromium/components/feed/DEPS
index 6ef6ce4d5ab..8bbbd796747 100644
--- a/chromium/components/feed/DEPS
+++ b/chromium/components/feed/DEPS
@@ -3,8 +3,10 @@ include_rules = [
"+components/image_fetcher",
"+components/keyed_service/core",
"+components/leveldb_proto",
+ "+components/prefs",
"+components/variations",
"+components/version_info",
+ "+components/web_resource",
"+net/base",
"+net/http",
"+net/traffic_annotation",
diff --git a/chromium/components/feed/OWNERS b/chromium/components/feed/OWNERS
index c8ef00b899e..7f1733c213f 100644
--- a/chromium/components/feed/OWNERS
+++ b/chromium/components/feed/OWNERS
@@ -3,5 +3,6 @@ pavely@chromium.org
pnoland@chromium.org
zea@chromium.org
+per-file *Test.java=aluo@chromium.org
# Team: chrome-jardin-team@google.com
# COMPONENT: UI>Browser>ContentSuggestions>Feed
diff --git a/chromium/components/feed/core/BUILD.gn b/chromium/components/feed/core/BUILD.gn
index 74be816a7db..f7b47dd0b13 100644
--- a/chromium/components/feed/core/BUILD.gn
+++ b/chromium/components/feed/core/BUILD.gn
@@ -2,6 +2,10 @@
# 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")
+}
+
source_set("feed_core") {
sources = [
"feed_host_service.cc",
@@ -12,12 +16,21 @@ source_set("feed_core") {
"feed_image_manager.h",
"feed_networking_host.cc",
"feed_networking_host.h",
+ "feed_scheduler_host.cc",
+ "feed_scheduler_host.h",
+ "feed_storage_database.cc",
+ "feed_storage_database.h",
+ "pref_names.cc",
+ "pref_names.h",
"time_serialization.cc",
"time_serialization.h",
+ "user_classifier.cc",
+ "user_classifier.h",
]
public_deps = [
"//base",
+ "//components/feed:feature_list",
"//components/feed/core/proto",
"//components/image_fetcher/core:core",
"//components/keyed_service/core",
@@ -27,9 +40,11 @@ source_set("feed_core") {
deps = [
"//components/data_use_measurement/core",
+ "//components/prefs",
"//components/variations",
"//components/variations/net",
"//components/variations/service",
+ "//components/web_resource",
"//google_apis",
"//services/identity/public/cpp",
"//services/network/public/cpp",
@@ -38,12 +53,23 @@ source_set("feed_core") {
]
}
+if (is_android) {
+ java_cpp_enum("feed_core_java_enums_srcjar") {
+ sources = [
+ "feed_scheduler_host.h",
+ ]
+ }
+}
+
source_set("core_unit_tests") {
testonly = true
sources = [
"feed_image_database_unittest.cc",
"feed_image_manager_unittest.cc",
"feed_networking_host_unittest.cc",
+ "feed_scheduler_host_unittest.cc",
+ "feed_storage_database_unittest.cc",
+ "user_classifier_unittest.cc",
]
deps = [
@@ -51,6 +77,9 @@ source_set("core_unit_tests") {
"//base",
"//base/test:test_support",
"//components/leveldb_proto:test_support",
+ "//components/prefs:test_support",
+ "//components/variations:test_support",
+ "//components/web_resource",
"//net:test_support",
"//services/identity/public/cpp",
"//services/identity/public/cpp:test_support",
diff --git a/chromium/components/feed/core/feed_host_service.cc b/chromium/components/feed/core/feed_host_service.cc
index 72e985c4b47..fe202d6ff4c 100644
--- a/chromium/components/feed/core/feed_host_service.cc
+++ b/chromium/components/feed/core/feed_host_service.cc
@@ -10,9 +10,13 @@ namespace feed {
FeedHostService::FeedHostService(
std::unique_ptr<FeedImageManager> image_manager,
- std::unique_ptr<FeedNetworkingHost> networking_host)
+ std::unique_ptr<FeedNetworkingHost> networking_host,
+ std::unique_ptr<FeedSchedulerHost> scheduler_host,
+ std::unique_ptr<FeedStorageDatabase> storage_database)
: image_manager_(std::move(image_manager)),
- networking_host_(std::move(networking_host)) {}
+ networking_host_(std::move(networking_host)),
+ scheduler_host_(std::move(scheduler_host)),
+ storage_database_(std::move(storage_database)) {}
FeedHostService::~FeedHostService() = default;
@@ -24,4 +28,12 @@ FeedNetworkingHost* FeedHostService::GetNetworkingHost() {
return networking_host_.get();
}
+FeedSchedulerHost* FeedHostService::GetSchedulerHost() {
+ return scheduler_host_.get();
+}
+
+FeedStorageDatabase* FeedHostService::GetStorageDatabase() {
+ return storage_database_.get();
+}
+
} // namespace feed
diff --git a/chromium/components/feed/core/feed_host_service.h b/chromium/components/feed/core/feed_host_service.h
index 4270b317f14..223f668750a 100644
--- a/chromium/components/feed/core/feed_host_service.h
+++ b/chromium/components/feed/core/feed_host_service.h
@@ -10,6 +10,8 @@
#include "base/macros.h"
#include "components/feed/core/feed_image_manager.h"
#include "components/feed/core/feed_networking_host.h"
+#include "components/feed/core/feed_scheduler_host.h"
+#include "components/feed/core/feed_storage_database.h"
#include "components/keyed_service/core/keyed_service.h"
namespace feed {
@@ -22,15 +24,21 @@ namespace feed {
class FeedHostService : public KeyedService {
public:
FeedHostService(std::unique_ptr<FeedImageManager> image_manager,
- std::unique_ptr<FeedNetworkingHost> networking_host);
+ std::unique_ptr<FeedNetworkingHost> networking_host,
+ std::unique_ptr<FeedSchedulerHost> scheduler_host,
+ std::unique_ptr<FeedStorageDatabase> storage_database);
~FeedHostService() override;
FeedImageManager* GetImageManager();
FeedNetworkingHost* GetNetworkingHost();
+ FeedSchedulerHost* GetSchedulerHost();
+ FeedStorageDatabase* GetStorageDatabase();
private:
std::unique_ptr<FeedImageManager> image_manager_;
std::unique_ptr<FeedNetworkingHost> networking_host_;
+ std::unique_ptr<FeedSchedulerHost> scheduler_host_;
+ std::unique_ptr<FeedStorageDatabase> storage_database_;
DISALLOW_COPY_AND_ASSIGN(FeedHostService);
};
diff --git a/chromium/components/feed/core/feed_image_database.cc b/chromium/components/feed/core/feed_image_database.cc
index ab5934b331c..d0ec8acfc1c 100644
--- a/chromium/components/feed/core/feed_image_database.cc
+++ b/chromium/components/feed/core/feed_image_database.cc
@@ -78,7 +78,7 @@ void FeedImageDatabase::SaveImage(const std::string& url,
image_proto.set_data(image_data);
image_proto.set_last_used_time(ToDatabaseTime(base::Time::Now()));
- SaveImageImpl(url, std::move(image_proto));
+ SaveImageImpl(url, image_proto);
}
void FeedImageDatabase::LoadImage(const std::string& url,
@@ -142,10 +142,10 @@ void FeedImageDatabase::ProcessPendingImageLoads() {
pending_image_callbacks_.clear();
}
-void FeedImageDatabase::SaveImageImpl(const std::string& url,
- CachedImageProto image_proto) {
+void FeedImageDatabase::SaveImageImpl(std::string url,
+ const CachedImageProto& image_proto) {
auto entries_to_save = std::make_unique<ImageKeyEntryVector>();
- entries_to_save->emplace_back(url, std::move(image_proto));
+ entries_to_save->emplace_back(std::move(url), image_proto);
image_database_->UpdateEntries(
std::move(entries_to_save), std::make_unique<std::vector<std::string>>(),
@@ -168,7 +168,7 @@ void FeedImageDatabase::OnImageLoaded(std::string url,
// Update timestamp for image.
entry->set_last_used_time(ToDatabaseTime(base::Time::Now()));
- SaveImageImpl(url, std::move(*entry));
+ SaveImageImpl(std::move(url), *entry);
}
void FeedImageDatabase::LoadImageImpl(const std::string& url,
diff --git a/chromium/components/feed/core/feed_image_database.h b/chromium/components/feed/core/feed_image_database.h
index 9aa594e7775..16fbcc77fa6 100644
--- a/chromium/components/feed/core/feed_image_database.h
+++ b/chromium/components/feed/core/feed_image_database.h
@@ -5,6 +5,11 @@
#ifndef COMPONENTS_FEED_CORE_FEED_IMAGE_DATABASE_H_
#define COMPONENTS_FEED_CORE_FEED_IMAGE_DATABASE_H_
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
#include "base/memory/weak_ptr.h"
#include "components/leveldb_proto/proto_database.h"
@@ -25,7 +30,8 @@ class FeedImageDatabase {
};
// Returns the resulting raw image data as std::string of a |LoadImage| call.
- using FeedImageDatabaseCallback = base::OnceCallback<void(std::string)>;
+ using FeedImageDatabaseCallback =
+ base::OnceCallback<void(const std::string&)>;
using FeedImageDatabaseOperationCallback = base::OnceCallback<void(bool)>;
@@ -77,7 +83,7 @@ class FeedImageDatabase {
void ProcessPendingImageLoads();
// Saving
- void SaveImageImpl(const std::string& url, CachedImageProto image_proto);
+ void SaveImageImpl(std::string url, const CachedImageProto& image_proto);
void OnImageUpdated(bool success);
// Loading
@@ -108,6 +114,7 @@ class FeedImageDatabase {
std::vector<std::pair<std::string, FeedImageDatabaseCallback>>
pending_image_callbacks_;
+ // Used to check that functions are called on the correct sequence.
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<FeedImageDatabase> weak_ptr_factory_;
diff --git a/chromium/components/feed/core/feed_image_database_unittest.cc b/chromium/components/feed/core/feed_image_database_unittest.cc
index 5052f560656..7eb17e47fc3 100644
--- a/chromium/components/feed/core/feed_image_database_unittest.cc
+++ b/chromium/components/feed/core/feed_image_database_unittest.cc
@@ -20,8 +20,10 @@ using testing::_;
namespace feed {
namespace {
-const std::string kImageURL = "http://pie.com/";
-const std::string kImageData = "pie image";
+
+constexpr char kImageURL[] = "http://pie.com/";
+constexpr char kImageData[] = "pie image";
+
} // namespace
class FeedImageDatabaseTest : public testing::Test {
@@ -63,7 +65,7 @@ class FeedImageDatabaseTest : public testing::Test {
void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
- MOCK_METHOD1(OnImageLoaded, void(std::string));
+ MOCK_METHOD1(OnImageLoaded, void(const std::string&));
MOCK_METHOD1(OnGarbageCollected, void(bool));
private:
diff --git a/chromium/components/feed/core/feed_image_manager.cc b/chromium/components/feed/core/feed_image_manager.cc
index 719b7774e4e..59b4960978f 100644
--- a/chromium/components/feed/core/feed_image_manager.cc
+++ b/chromium/components/feed/core/feed_image_manager.cc
@@ -4,10 +4,7 @@
#include "components/feed/core/feed_image_manager.h"
-#include <memory>
-#include <string>
#include <utility>
-#include <vector>
#include "base/bind.h"
#include "components/feed/core/time_serialization.h"
@@ -86,10 +83,11 @@ void FeedImageManager::FetchImagesFromDatabase(size_t url_index,
std::move(urls), std::move(callback)));
}
-void FeedImageManager::OnImageFetchedFromDatabase(size_t url_index,
- std::vector<std::string> urls,
- ImageFetchedCallback callback,
- std::string image_data) {
+void FeedImageManager::OnImageFetchedFromDatabase(
+ size_t url_index,
+ std::vector<std::string> urls,
+ ImageFetchedCallback callback,
+ const std::string& image_data) {
if (image_data.empty()) {
// Fetching from the DB failed; start a network fetch.
FetchImageFromNetwork(url_index, std::move(urls), std::move(callback));
diff --git a/chromium/components/feed/core/feed_image_manager.h b/chromium/components/feed/core/feed_image_manager.h
index 4618b3f9938..13af33b8dcc 100644
--- a/chromium/components/feed/core/feed_image_manager.h
+++ b/chromium/components/feed/core/feed_image_manager.h
@@ -5,6 +5,10 @@
#ifndef COMPONENTS_FEED_CORE_FEED_IMAGE_MANAGER_H_
#define COMPONENTS_FEED_CORE_FEED_IMAGE_MANAGER_H_
+#include <memory>
+#include <string>
+#include <vector>
+
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "components/feed/core/feed_image_database.h"
@@ -51,7 +55,7 @@ class FeedImageManager {
void OnImageFetchedFromDatabase(size_t url_index,
std::vector<std::string> urls,
ImageFetchedCallback callback,
- std::string image_data);
+ const std::string& image_data);
void OnImageDecodedFromDatabase(size_t url_index,
std::vector<std::string> urls,
ImageFetchedCallback callback,
diff --git a/chromium/components/feed/core/feed_image_manager_unittest.cc b/chromium/components/feed/core/feed_image_manager_unittest.cc
index bc3fd3812f2..26315409256 100644
--- a/chromium/components/feed/core/feed_image_manager_unittest.cc
+++ b/chromium/components/feed/core/feed_image_manager_unittest.cc
@@ -16,8 +16,9 @@
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/image_fetcher/core/image_decoder.h"
#include "components/image_fetcher/core/image_fetcher_impl.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.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_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/image/image.h"
@@ -59,7 +60,7 @@ class FakeImageDecoder : public image_fetcher::ImageDecoder {
class FeedImageManagerTest : public testing::Test {
public:
- FeedImageManagerTest() : fake_url_fetcher_factory_(nullptr) {}
+ FeedImageManagerTest() {}
~FeedImageManagerTest() override {
feed_image_manager_.reset();
@@ -75,17 +76,17 @@ class FeedImageManagerTest : public testing::Test {
std::make_unique<FeedImageDatabase>(database_dir_.GetPath());
image_database_ = image_database.get();
- request_context_getter_ = scoped_refptr<net::TestURLRequestContextGetter>(
- new net::TestURLRequestContextGetter(
- scoped_task_environment_.GetMainThreadTaskRunner()));
+ shared_factory_ =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_);
auto decoder = std::make_unique<FakeImageDecoder>();
decoder->SetExpectedData(kImageData);
fake_image_decoder_ = decoder.get();
feed_image_manager_ = std::make_unique<FeedImageManager>(
- std::make_unique<image_fetcher::ImageFetcherImpl>(
- std::move(decoder), request_context_getter_.get()),
+ std::make_unique<image_fetcher::ImageFetcherImpl>(std::move(decoder),
+ shared_factory_),
std::move(image_database));
RunUntilIdle();
@@ -111,19 +112,19 @@ class FeedImageManagerTest : public testing::Test {
FakeImageDecoder* fake_image_decoder() { return fake_image_decoder_; }
- net::FakeURLFetcherFactory* fake_url_fetcher_factory() {
- return &fake_url_fetcher_factory_;
+ network::TestURLLoaderFactory* test_url_loader_factory() {
+ return &test_url_loader_factory_;
}
- MOCK_METHOD1(OnImageLoaded, void(std::string));
+ MOCK_METHOD1(OnImageLoaded, void(const std::string&));
private:
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> shared_factory_;
std::unique_ptr<FeedImageManager> feed_image_manager_;
FeedImageDatabase* image_database_;
base::ScopedTempDir database_dir_;
FakeImageDecoder* fake_image_decoder_;
- scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
- net::FakeURLFetcherFactory fake_url_fetcher_factory_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
DISALLOW_COPY_AND_ASSIGN(FeedImageManagerTest);
@@ -158,9 +159,7 @@ TEST_F(FeedImageManagerTest, FetchImageFromCache) {
TEST_F(FeedImageManagerTest, FetchImagePopulatesCache) {
// Expect the image to be fetched by URL.
{
- fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
- net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ test_url_loader_factory()->AddResponse(kImageURL, kImageData);
base::MockCallback<ImageFetchedCallback> image_callback;
EXPECT_CALL(image_callback, Run(testing::Property(&gfx::Image::IsEmpty,
testing::Eq(false))));
@@ -179,7 +178,7 @@ TEST_F(FeedImageManagerTest, FetchImagePopulatesCache) {
}
// Fetch again. The cache should be populated, no network request is needed.
{
- fake_url_fetcher_factory()->ClearFakeResponses();
+ test_url_loader_factory()->ClearResponses();
base::MockCallback<ImageFetchedCallback> image_callback;
EXPECT_CALL(image_callback, Run(testing::Property(&gfx::Image::IsEmpty,
testing::Eq(false))));
@@ -193,12 +192,9 @@ TEST_F(FeedImageManagerTest, FetchImagePopulatesCache) {
TEST_F(FeedImageManagerTest, FetchSecondImageIfFirstFailed) {
// Expect the image to be fetched by URL.
{
- fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
- net::HTTP_NOT_FOUND,
- net::URLRequestStatus::FAILED);
- fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL2), kImageData2,
- net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ test_url_loader_factory()->AddResponse(kImageURL, kImageData,
+ net::HTTP_NOT_FOUND);
+ test_url_loader_factory()->AddResponse(kImageURL2, kImageData2);
base::MockCallback<ImageFetchedCallback> image_callback;
EXPECT_CALL(image_callback, Run(testing::Property(&gfx::Image::IsEmpty,
testing::Eq(false))));
@@ -224,9 +220,7 @@ TEST_F(FeedImageManagerTest, DecodingErrorWillDeleteCache) {
image_database()->SaveImage(kImageURL, kImageData);
RunUntilIdle();
{
- fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
- net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ test_url_loader_factory()->AddResponse(kImageURL, kImageData);
// Set decoding always error.
fake_image_decoder()->SetDecodingValid(false);
base::MockCallback<ImageFetchedCallback> image_callback;
diff --git a/chromium/components/feed/core/feed_networking_host.cc b/chromium/components/feed/core/feed_networking_host.cc
index f665392ee4a..cd94af0a4a7 100644
--- a/chromium/components/feed/core/feed_networking_host.cc
+++ b/chromium/components/feed/core/feed_networking_host.cc
@@ -28,13 +28,12 @@ using IdentityManager = identity::IdentityManager;
namespace {
-static constexpr char kApiKeyQueryParam[] = "key";
-// todo(pnoland, https://crbug.com/808131): Decide on the correct auth scope and
-// change the below constant to that value.
-static constexpr char kAuthenticationScope[] = "ntp_snippets;";
-static constexpr char kContentEncoding[] = "Content-Encoding";
-static constexpr char kContentType[] = "application/octet-stream";
-static constexpr char kGzip[] = "gzip";
+constexpr char kApiKeyQueryParam[] = "key";
+constexpr char kAuthenticationScope[] =
+ "https://www.googleapis.com/auth/googlenow";
+constexpr char kContentEncoding[] = "Content-Encoding";
+constexpr char kContentType[] = "application/octet-stream";
+constexpr char kGzip[] = "gzip";
} // namespace
@@ -54,18 +53,18 @@ class NetworkFetch {
network::SharedURLLoaderFactory* loader_factory,
const std::string& api_key);
- void Start(FeedNetworkingHost::ResponseCallback done);
+ void Start(FeedNetworkingHost::ResponseCallback done_callback);
private:
void StartAccessTokenFetch();
- void AccessTokenFetchFinished(const GoogleServiceAuthError& error,
- const std::string& access_token);
+ void AccessTokenFetchFinished(GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info);
void StartLoader(const std::string& access_token);
std::unique_ptr<network::SimpleURLLoader> MakeLoader(
const std::string& access_token);
net::HttpRequestHeaders MakeHeaders(const std::string& auth_header) const;
void PopulateRequestBody(network::SimpleURLLoader* loader);
- void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
+ void OnSimpleLoaderComplete(std::unique_ptr<std::string> response);
const GURL url_;
const std::string request_type_;
@@ -108,18 +107,19 @@ void NetworkFetch::StartAccessTokenFetch() {
OAuth2TokenService::ScopeSet scopes{kAuthenticationScope};
// It's safe to pass base::Unretained(this) since deleting the token fetcher
// will prevent the callback from being completed.
- token_fetcher_ = identity_manager_->CreateAccessTokenFetcherForPrimaryAccount(
- "feed", scopes,
+ token_fetcher_ = std::make_unique<identity::PrimaryAccountAccessTokenFetcher>(
+ "feed", identity_manager_, scopes,
base::BindOnce(&NetworkFetch::AccessTokenFetchFinished,
base::Unretained(this)),
identity::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable);
}
-void NetworkFetch::AccessTokenFetchFinished(const GoogleServiceAuthError& error,
- const std::string& access_token) {
+void NetworkFetch::AccessTokenFetchFinished(
+ GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info) {
UMA_HISTOGRAM_ENUMERATION("ContentSuggestions.Feed.TokenFetchStatus",
error.state(), GoogleServiceAuthError::NUM_STATES);
- StartLoader(access_token);
+ StartLoader(access_token_info.token);
}
void NetworkFetch::StartLoader(const std::string& access_token) {
@@ -199,7 +199,7 @@ net::HttpRequestHeaders NetworkFetch::MakeHeaders(
void NetworkFetch::PopulateRequestBody(network::SimpleURLLoader* loader) {
std::string compressed_request_body;
- if (request_body_.size() > 0) {
+ if (!request_body_.empty()) {
std::string uncompressed_request_body(
reinterpret_cast<const char*>(request_body_.data()),
request_body_.size());
@@ -218,11 +218,9 @@ void NetworkFetch::PopulateRequestBody(network::SimpleURLLoader* loader) {
void NetworkFetch::OnSimpleLoaderComplete(
std::unique_ptr<std::string> response) {
int32_t status_code = simple_loader_->NetError();
- net::HttpResponseHeaders* headers = nullptr;
std::vector<uint8_t> response_body;
if (response) {
- headers = simple_loader_->ResponseInfo()->headers.get();
status_code = simple_loader_->ResponseInfo()->headers->response_code();
const uint8_t* begin = reinterpret_cast<const uint8_t*>(response->data());
@@ -249,7 +247,7 @@ FeedNetworkingHost::FeedNetworkingHost(
scoped_refptr<network::SharedURLLoaderFactory> loader_factory)
: identity_manager_(identity_manager),
api_key_(api_key),
- loader_factory_(loader_factory) {}
+ loader_factory_(std::move(loader_factory)) {}
FeedNetworkingHost::~FeedNetworkingHost() = default;
diff --git a/chromium/components/feed/core/feed_networking_host_unittest.cc b/chromium/components/feed/core/feed_networking_host_unittest.cc
index 228276029a3..5c51361e07f 100644
--- a/chromium/components/feed/core/feed_networking_host_unittest.cc
+++ b/chromium/components/feed/core/feed_networking_host_unittest.cc
@@ -8,14 +8,14 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_mock_time_task_runner.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/identity/public/cpp/identity_test_environment.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/url_loader_completion_status.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -45,42 +45,6 @@ class MockResponseDoneCallback {
std::vector<uint8_t> response_bytes;
};
-// Class that wraps a TestURLLoaderFactory and implements the interface of
-// SharedURLLoaderFactory, allowing one to produce SharedURLLoaderFactory
-// instances that can be mocked.
-class TestSharedURLLoaderFactory : public SharedURLLoaderFactory {
- public:
- void CreateLoaderAndStart(network::mojom::URLLoaderRequest request,
- int32_t routing_id,
- int32_t request_id,
- uint32_t options,
- const network::ResourceRequest& url_request,
- network::mojom::URLLoaderClientPtr client,
- const net::MutableNetworkTrafficAnnotationTag&
- traffic_annotation) override {
- test_factory_.CreateLoaderAndStart(std::move(request), routing_id,
- request_id, options, url_request,
- std::move(client), traffic_annotation);
- }
-
- void Clone(network::mojom::URLLoaderFactoryRequest request) override {
- NOTREACHED();
- }
-
- std::unique_ptr<SharedURLLoaderFactoryInfo> Clone() override {
- NOTREACHED();
- return nullptr;
- }
-
- TestURLLoaderFactory* test_factory() { return &test_factory_; }
-
- protected:
- ~TestSharedURLLoaderFactory() override = default;
-
- private:
- TestURLLoaderFactory test_factory_;
-};
-
} // namespace
class FeedNetworkingHostTest : public testing::Test {
@@ -95,9 +59,12 @@ class FeedNetworkingHostTest : public testing::Test {
~FeedNetworkingHostTest() override {}
void SetUp() override {
+ shared_url_loader_factory_ =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_factory_);
net_service_ = std::make_unique<FeedNetworkingHost>(
identity_test_env_.identity_manager(), "dummy_api_key",
- test_loader_factory_);
+ shared_url_loader_factory_);
}
FeedNetworkingHost* service() { return net_service_.get(); }
@@ -120,8 +87,7 @@ class FeedNetworkingHostTest : public testing::Test {
status.decoded_body_length = response_string.length();
}
- test_loader_factory_->test_factory()->AddResponse(url, head,
- response_string, status);
+ test_factory_.AddResponse(url, head, response_string, status);
RunUntilEmpty();
}
@@ -165,8 +131,8 @@ class FeedNetworkingHostTest : public testing::Test {
scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
identity::IdentityTestEnvironment identity_test_env_;
std::unique_ptr<FeedNetworkingHost> net_service_;
- scoped_refptr<TestSharedURLLoaderFactory> test_loader_factory_ =
- base::MakeRefCounted<TestSharedURLLoaderFactory>();
+ network::TestURLLoaderFactory test_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
DISALLOW_COPY_AND_ASSIGN(FeedNetworkingHostTest);
};
diff --git a/chromium/components/feed/core/feed_scheduler_host.cc b/chromium/components/feed/core/feed_scheduler_host.cc
new file mode 100644
index 00000000000..30230d7cef1
--- /dev/null
+++ b/chromium/components/feed/core/feed_scheduler_host.cc
@@ -0,0 +1,403 @@
+// 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/feed/core/feed_scheduler_host.h"
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "components/feed/core/pref_names.h"
+#include "components/feed/core/time_serialization.h"
+#include "components/feed/feed_feature_list.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/web_resource/web_resource_pref_names.h"
+#include "net/base/network_change_notifier.h"
+
+namespace feed {
+
+namespace {
+
+using TriggerType = FeedSchedulerHost::TriggerType;
+using UserClass = UserClassifier::UserClass;
+
+struct ParamPair {
+ std::string name;
+ double default_value;
+};
+
+const base::FeatureParam<int> kSuppressRefreshDurationMinutes{
+ &kInterestFeedContentSuggestions, "suppress_refresh_duration_minutes", 30};
+const base::FeatureParam<std::string> kDisableTriggerTypes{
+ &kInterestFeedContentSuggestions, "disable_trigger_types", ""};
+
+// The Cartesian product of TriggerType and UserClass each need a different
+// param name in case we decide to change it via a config change. This nested
+// switch lookup ensures that all combinations are defined, along with a
+// default value.
+ParamPair LookupParam(UserClass user_class, TriggerType trigger) {
+ switch (user_class) {
+ case UserClass::kRareNtpUser:
+ switch (trigger) {
+ case TriggerType::kNtpShown:
+ return {"ntp_shown_hours_rare_ntp_user", 4.0};
+ case TriggerType::kForegrounded:
+ return {"foregrounded_hours_rare_ntp_user", 24.0};
+ case TriggerType::kFixedTimer:
+ return {"fixed_timer_hours_rare_ntp_user", 96.0};
+ }
+ case UserClass::kActiveNtpUser:
+ switch (trigger) {
+ case TriggerType::kNtpShown:
+ return {"ntp_shown_hours_active_ntp_user", 4.0};
+ case TriggerType::kForegrounded:
+ return {"foregrounded_hours_active_ntp_user", 24.0};
+ case TriggerType::kFixedTimer:
+ return {"fixed_timer_hours_active_ntp_user", 48.0};
+ }
+ case UserClass::kActiveSuggestionsConsumer:
+ switch (trigger) {
+ case TriggerType::kNtpShown:
+ return {"ntp_shown_hours_active_suggestions_consumer", 1.0};
+ case TriggerType::kForegrounded:
+ return {"foregrounded_hours_active_suggestions_consumer", 12.0};
+ case TriggerType::kFixedTimer:
+ return {"fixed_timer_hours_active_suggestions_consumer", 24.0};
+ }
+ }
+}
+
+// Coverts from base::StringPiece to TriggerType and adds it to the set if the
+// trigger is recognized. Otherwise it is ignored.
+void TryAddTriggerType(base::StringPiece trigger_as_string_piece,
+ std::set<TriggerType>* trigger_set) {
+ static_assert(static_cast<unsigned int>(TriggerType::kMaxValue) == 2,
+ "New TriggerTypes must be handled below.");
+ if (trigger_as_string_piece == "ntp_shown") {
+ trigger_set->insert(TriggerType::kNtpShown);
+ } else if (trigger_as_string_piece == "foregrounded") {
+ trigger_set->insert(TriggerType::kForegrounded);
+ } else if (trigger_as_string_piece == "fixed_timer") {
+ trigger_set->insert(TriggerType::kFixedTimer);
+ }
+}
+
+// Generates a set of disabled triggers.
+std::set<TriggerType> GetDisabledTriggerTypes() {
+ std::set<TriggerType> disabled_triggers;
+
+ // Do not in-line FeatureParam::Get(), |param_value| must stay alive while
+ // StringPieces reference segments.
+ std::string param_value = kDisableTriggerTypes.Get();
+
+ for (auto token :
+ base::SplitStringPiece(param_value, ",", base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ TryAddTriggerType(token, &disabled_triggers);
+ }
+ return disabled_triggers;
+}
+
+// Run the given closure if it is valid.
+void TryRun(base::OnceClosure closure) {
+ if (closure) {
+ std::move(closure).Run();
+ }
+}
+
+// Converts UserClassifier::UserClass to a string that corresponds to the
+// entries in histogram suffix "UserClasses".
+std::string UserClassToHistogramSuffix(UserClassifier::UserClass user_class) {
+ switch (user_class) {
+ case UserClassifier::UserClass::kRareNtpUser:
+ return "RareNTPUser";
+ case UserClassifier::UserClass::kActiveNtpUser:
+ return "ActiveNTPUser";
+ case UserClassifier::UserClass::kActiveSuggestionsConsumer:
+ return "ActiveSuggestionsConsumer";
+ }
+}
+
+// This has a small performance penalty because it is looking up the histogram
+// dynamically, which avoids a significantly amount of boilerplate code for the
+// various |qualified_trigger| and user class strings. This is reasonable
+// because this method is only called as a result of a direct user interaction,
+// like opening the NTP or foregrounding the browser.
+void ReportAgeWithSuffix(const std::string& qualified_trigger,
+ UserClassifier::UserClass user_class,
+ base::TimeDelta sample) {
+ std::string name = base::StringPrintf(
+ "NewTabPage.ContentSuggestions.%s.%s", qualified_trigger.c_str(),
+ UserClassToHistogramSuffix(user_class).c_str());
+ base::UmaHistogramCustomTimes(name, sample, base::TimeDelta::FromSeconds(1),
+ base::TimeDelta::FromDays(7),
+ /*bucket_count=*/50);
+}
+
+} // namespace
+
+FeedSchedulerHost::FeedSchedulerHost(PrefService* profile_prefs,
+ PrefService* local_state,
+ base::Clock* clock)
+ : profile_prefs_(profile_prefs),
+ clock_(clock),
+ user_classifier_(profile_prefs, clock),
+ disabled_triggers_(GetDisabledTriggerTypes()),
+ eula_accepted_notifier_(
+ web_resource::EulaAcceptedNotifier::Create(local_state)) {
+ if (eula_accepted_notifier_) {
+ eula_accepted_notifier_->Init(this);
+ }
+}
+
+FeedSchedulerHost::~FeedSchedulerHost() = default;
+
+// static
+void FeedSchedulerHost::RegisterProfilePrefs(PrefRegistrySimple* registry) {
+ registry->RegisterTimePref(prefs::kLastFetchAttemptTime, base::Time());
+ registry->RegisterTimeDeltaPref(prefs::kBackgroundRefreshPeriod,
+ base::TimeDelta());
+}
+
+void FeedSchedulerHost::Initialize(
+ base::RepeatingClosure refresh_callback,
+ ScheduleBackgroundTaskCallback schedule_background_task_callback) {
+ // There should only ever be one scheduler host and bridge created. Neither
+ // are ever destroyed before shutdown, and this method should only be called
+ // once as the bridge is constructed.
+ DCHECK(!refresh_callback_);
+ DCHECK(!schedule_background_task_callback_);
+
+ refresh_callback_ = std::move(refresh_callback);
+ schedule_background_task_callback_ =
+ std::move(schedule_background_task_callback);
+
+ base::TimeDelta old_period =
+ profile_prefs_->GetTimeDelta(prefs::kBackgroundRefreshPeriod);
+ base::TimeDelta new_period = GetTriggerThreshold(TriggerType::kFixedTimer);
+ if (old_period != new_period) {
+ ScheduleFixedTimerWakeUp(new_period);
+ }
+}
+
+NativeRequestBehavior FeedSchedulerHost::ShouldSessionRequestData(
+ bool has_content,
+ base::Time content_creation_date_time,
+ bool has_outstanding_request) {
+ // The scheduler may not always know of outstanding requests, but the Feed
+ // should know about them all, and the scheduler should be notified upon
+ // completion of all requests. We should never encounter a scenario where only
+ // the scheduler thinks there is an outstanding request.
+
+ // TODO(skym): Resolve ambiguity around this expectation.
+ // DCHECK(has_outstanding_request || !tracking_oustanding_request_);
+
+ tracking_oustanding_request_ |= has_outstanding_request;
+
+ NativeRequestBehavior behavior;
+ if (ShouldRefresh(TriggerType::kNtpShown)) {
+ if (!has_content) {
+ behavior = kRequestWithWait;
+ } else if (IsContentStale(content_creation_date_time)) {
+ behavior = kRequestWithTimeout;
+ } else {
+ behavior = kRequestWithContent;
+ }
+ } else {
+ // Note that kNoRequestWithWait is used to show a blank article section
+ // even when no request is being made. The user will be given the ability to
+ // force a refresh but this scheduler is not driving it.
+ if (!has_content) {
+ behavior = kNoRequestWithWait;
+ } else if (IsContentStale(content_creation_date_time) &&
+ has_outstanding_request) {
+ // This needs to check |has_outstanding_request|, it does not make sense
+ // to use a timeout when no request is being made. Just show the stale
+ // content, since nothing better is on the way.
+ behavior = kNoRequestWithTimeout;
+ } else {
+ behavior = kNoRequestWithContent;
+ }
+ }
+
+ user_classifier_.OnEvent(UserClassifier::Event::kNtpOpened);
+ DVLOG(2) << "Specifying NativeRequestBehavior of "
+ << static_cast<int>(behavior);
+ UMA_HISTOGRAM_ENUMERATION("Feed.Scheduler.RequestBehavior", behavior);
+ return behavior;
+}
+
+void FeedSchedulerHost::OnReceiveNewContent(
+ base::Time content_creation_date_time) {
+ profile_prefs_->SetTime(prefs::kLastFetchAttemptTime,
+ content_creation_date_time);
+ TryRun(std::move(fixed_timer_completion_));
+ ScheduleFixedTimerWakeUp(GetTriggerThreshold(TriggerType::kFixedTimer));
+ tracking_oustanding_request_ = false;
+ time_until_first_shown_trigger_reported_ = false;
+ time_until_first_foregrounded_trigger_reported_ = false;
+ DVLOG(2) << "Received OnReceiveNewContent with time "
+ << content_creation_date_time;
+}
+
+void FeedSchedulerHost::OnRequestError(int network_response_code) {
+ profile_prefs_->SetTime(prefs::kLastFetchAttemptTime, clock_->Now());
+ TryRun(std::move(fixed_timer_completion_));
+ tracking_oustanding_request_ = false;
+ time_until_first_shown_trigger_reported_ = false;
+ time_until_first_foregrounded_trigger_reported_ = false;
+ DVLOG(2) << "Received OnRequestError with code " << network_response_code;
+}
+
+void FeedSchedulerHost::OnForegrounded() {
+ DCHECK(refresh_callback_);
+ if (ShouldRefresh(TriggerType::kForegrounded)) {
+ refresh_callback_.Run();
+ }
+}
+
+void FeedSchedulerHost::OnFixedTimer(base::OnceClosure on_completion) {
+ DCHECK(refresh_callback_);
+ if (ShouldRefresh(TriggerType::kFixedTimer)) {
+ // There shouldn't typically be anything in |fixed_timer_completion_| right
+ // now, but if there was, run it before we replace it.
+ TryRun(std::move(fixed_timer_completion_));
+
+ fixed_timer_completion_ = std::move(on_completion);
+ refresh_callback_.Run();
+ } else {
+ // The task driving this doesn't need to stay around, since no work is being
+ // done on its behalf.
+ TryRun(std::move(on_completion));
+ }
+}
+
+void FeedSchedulerHost::OnTaskReschedule() {
+ ScheduleFixedTimerWakeUp(GetTriggerThreshold(TriggerType::kFixedTimer));
+}
+
+void FeedSchedulerHost::OnSuggestionConsumed() {
+ user_classifier_.OnEvent(UserClassifier::Event::kSuggestionsUsed);
+}
+
+void FeedSchedulerHost::OnHistoryCleared() {
+ // Due to privacy, we should not fetch for a while (unless the user explicitly
+ // asks for new suggestions) to give sync the time to propagate the changes in
+ // history to the server.
+ suppress_refreshes_until_ =
+ clock_->Now() +
+ base::TimeDelta::FromMinutes(kSuppressRefreshDurationMinutes.Get());
+ // After that time elapses, we should fetch as soon as possible.
+ profile_prefs_->ClearPref(prefs::kLastFetchAttemptTime);
+}
+
+void FeedSchedulerHost::OnEulaAccepted() {
+ OnForegrounded();
+}
+
+bool FeedSchedulerHost::ShouldRefresh(TriggerType trigger) {
+ if (tracking_oustanding_request_) {
+ DVLOG(2) << "Outstanding request stopped refresh from trigger "
+ << static_cast<int>(trigger);
+ return false;
+ }
+
+ if (base::ContainsKey(disabled_triggers_, trigger)) {
+ DVLOG(2) << "Disabled trigger stopped refresh from trigger "
+ << static_cast<int>(trigger);
+ return false;
+ }
+
+ if (net::NetworkChangeNotifier::IsOffline()) {
+ DVLOG(2) << "Network is offline stopped refresh from trigger "
+ << static_cast<int>(trigger);
+ return false;
+ }
+
+ if (eula_accepted_notifier_ && !eula_accepted_notifier_->IsEulaAccepted()) {
+ DVLOG(2) << "EULA not being accepted stopped refresh from trigger "
+ << static_cast<int>(trigger);
+ return false;
+ }
+
+ base::TimeDelta attempt_age =
+ clock_->Now() - profile_prefs_->GetTime(prefs::kLastFetchAttemptTime);
+ UserClassifier::UserClass user_class = user_classifier_.GetUserClass();
+
+ if (trigger == TriggerType::kNtpShown &&
+ !time_until_first_shown_trigger_reported_) {
+ time_until_first_shown_trigger_reported_ = true;
+ ReportAgeWithSuffix("TimeUntilFirstShownTrigger", user_class, attempt_age);
+ }
+
+ if (trigger == TriggerType::kForegrounded &&
+ !time_until_first_foregrounded_trigger_reported_) {
+ time_until_first_foregrounded_trigger_reported_ = true;
+ ReportAgeWithSuffix("TimeUntilFirstStartupTrigger", user_class,
+ attempt_age);
+ }
+
+ if (clock_->Now() < suppress_refreshes_until_) {
+ DVLOG(2) << "Refresh suppression until " << suppress_refreshes_until_
+ << " stopped refresh from trigger " << static_cast<int>(trigger);
+ return false;
+ }
+
+ if (attempt_age < GetTriggerThreshold(trigger)) {
+ DVLOG(2) << "Last attempt age of " << attempt_age
+ << " stopped refresh from trigger " << static_cast<int>(trigger);
+ return false;
+ }
+
+ // TODO(skym): Check with throttler.
+
+ switch (trigger) {
+ case TriggerType::kNtpShown:
+ ReportAgeWithSuffix("TimeUntilSoftFetch", user_class, attempt_age);
+ break;
+ case TriggerType::kForegrounded:
+ ReportAgeWithSuffix("TimeUntilStartupFetch", user_class, attempt_age);
+ break;
+ case TriggerType::kFixedTimer:
+ ReportAgeWithSuffix("TimeUntilPersistentFetch", user_class, attempt_age);
+ break;
+ }
+
+ DVLOG(2) << "Requesting refresh from trigger " << static_cast<int>(trigger);
+ UMA_HISTOGRAM_ENUMERATION("Feed.Scheduler.RefreshTrigger", trigger);
+ tracking_oustanding_request_ = true;
+ return true;
+}
+
+bool FeedSchedulerHost::IsContentStale(base::Time content_creation_date_time) {
+ return (clock_->Now() - content_creation_date_time) >
+ GetTriggerThreshold(TriggerType::kForegrounded);
+}
+
+base::TimeDelta FeedSchedulerHost::GetTriggerThreshold(TriggerType trigger) {
+ UserClass user_class = user_classifier_.GetUserClass();
+ ParamPair param = LookupParam(user_class, trigger);
+ double value_hours = base::GetFieldTrialParamByFeatureAsDouble(
+ kInterestFeedContentSuggestions, param.name, param.default_value);
+
+ // Use FromSecondsD in case one of the values contained a decimal.
+ return base::TimeDelta::FromSecondsD(value_hours * 3600.0);
+}
+
+void FeedSchedulerHost::ScheduleFixedTimerWakeUp(base::TimeDelta period) {
+ profile_prefs_->SetTimeDelta(prefs::kBackgroundRefreshPeriod, period);
+ schedule_background_task_callback_.Run(period);
+}
+
+} // namespace feed
diff --git a/chromium/components/feed/core/feed_scheduler_host.h b/chromium/components/feed/core/feed_scheduler_host.h
new file mode 100644
index 00000000000..a1e2c2eae68
--- /dev/null
+++ b/chromium/components/feed/core/feed_scheduler_host.h
@@ -0,0 +1,189 @@
+// 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_FEED_CORE_FEED_SCHEDULER_HOST_H_
+#define COMPONENTS_FEED_CORE_FEED_SCHEDULER_HOST_H_
+
+#include <memory>
+#include <set>
+
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/feed/core/user_classifier.h"
+#include "components/web_resource/eula_accepted_notifier.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace base {
+class Clock;
+class Time;
+class TimeDelta;
+} // namespace base
+
+namespace feed {
+
+// The enum values and names are kept in sync with SchedulerApi.RequestBehavior
+// through Java unit tests, new values however must be manually added. If any
+// new values are added, also update FeedSchedulerBridgeTest.java as well as
+// the corresponding definition in enums.xml.
+// A Java counterpart will be generated for this enum.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.feed
+enum NativeRequestBehavior {
+ kUnknown = 0,
+ kRequestWithWait = 1,
+ kRequestWithContent = 2,
+ kRequestWithTimeout = 3,
+ kNoRequestWithWait = 4,
+ kNoRequestWithContent = 5,
+ kNoRequestWithTimeout = 6,
+ kMaxValue = kNoRequestWithTimeout
+};
+
+// Implementation of the Feed Scheduler Host API. The scheduler host decides
+// what content is allowed to be shown, based on its age, and when to fetch new
+// content.
+class FeedSchedulerHost : web_resource::EulaAcceptedNotifier::Observer {
+ public:
+ // The TriggerType enum specifies values for the events that can trigger
+ // refreshing articles. When adding values, be certain to also update the
+ // corresponding definition in enums.xml.
+ enum class TriggerType {
+ kNtpShown = 0,
+ kForegrounded = 1,
+ kFixedTimer = 2,
+ kMaxValue = kFixedTimer
+ };
+
+ FeedSchedulerHost(PrefService* profile_prefs,
+ PrefService* local_state,
+ base::Clock* clock);
+ ~FeedSchedulerHost() override;
+
+ using ScheduleBackgroundTaskCallback =
+ base::RepeatingCallback<void(base::TimeDelta)>;
+
+ static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+ // Provide dependent pieces of functionality the scheduler relies on. Should
+ // be called exactly once before other public methods are called. This is
+ // separate from the constructor because the FeedHostService owns and creates
+ // this class, while these providers need to be bridged through the JNI into a
+ // specific place that the FeedHostService does not know of.
+ void Initialize(
+ base::RepeatingClosure refresh_callback,
+ ScheduleBackgroundTaskCallback schedule_background_task_callback);
+
+ // Called when the NTP is opened to decide how to handle displaying and
+ // refreshing content.
+ NativeRequestBehavior ShouldSessionRequestData(
+ bool has_content,
+ base::Time content_creation_date_time,
+ bool has_outstanding_request);
+
+ // Called when a successful refresh completes.
+ void OnReceiveNewContent(base::Time content_creation_date_time);
+
+ // Called when an unsuccessful refresh completes.
+ void OnRequestError(int network_response_code);
+
+ // Called when browser is opened, launched, or foregrounded.
+ void OnForegrounded();
+
+ // Called when the scheduled fixed timer wakes up, |on_completion| will be
+ // invoked when the refresh completes, regardless of success. If no refresh
+ // is started for this trigger, |on_completion| is run immediately.
+ void OnFixedTimer(base::OnceClosure on_completion);
+
+ // Called when the background task may need to be rescheduled, such as on OS
+ // upgrades that change the way tasks are stored.
+ void OnTaskReschedule();
+
+ // Called when a suggestion is consumed to update what kind of user the
+ // scheduler should be optimizing for.
+ void OnSuggestionConsumed();
+
+ // When the user clears history, the scheduler will clear out some stored data
+ // and stop requesting refreshes for a period of time.
+ void OnHistoryCleared();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(FeedSchedulerHostTest, GetTriggerThreshold);
+
+ // web_resource::EulaAcceptedNotifier::Observer:
+ void OnEulaAccepted() override;
+
+ // Determines whether a refresh should be performed for the given |trigger|.
+ // If this method is called and returns true we presume the refresh will
+ // happen, therefore we report metrics respectively and update
+ // |tracking_oustanding_request_|.
+ bool ShouldRefresh(TriggerType trigger);
+
+ // Decides if content whose age is the difference between now and
+ // |content_creation_date_time| is old enough to be considered stale.
+ bool IsContentStale(base::Time content_creation_date_time);
+
+ // Returns the time threshold for content or previous refresh attempt to be
+ // considered old enough for a given trigger to warrant a refresh.
+ base::TimeDelta GetTriggerThreshold(TriggerType trigger);
+
+ // Schedules a task to wakeup and try to refresh. Overrides previously
+ // scheduled tasks.
+ void ScheduleFixedTimerWakeUp(base::TimeDelta period);
+
+ // Non-owning reference to pref service providing durable storage.
+ PrefService* profile_prefs_;
+
+ // Non-owning reference to clock to get current time.
+ base::Clock* clock_;
+
+ // Persists NTP and article usage over time and provides a classification.
+ UserClassifier user_classifier_;
+
+ // Callback to request that an async refresh be started.
+ base::RepeatingClosure refresh_callback_;
+
+ // Provides ability to schedule and cancel persistent background fixed timer
+ // wake ups that will call into OnFixedTimer().
+ ScheduleBackgroundTaskCallback schedule_background_task_callback_;
+
+ // When a background wake up has caused a fixed timer refresh, this callback
+ // will be valid and holds a way to inform the task driving this wake up that
+ // the refresh has completed. Is called on refresh success or failure.
+ base::OnceClosure fixed_timer_completion_;
+
+ // Set of triggers that should be ignored. By default this is empty.
+ std::set<TriggerType> disabled_triggers_;
+
+ // In some circumstances, such as when history is cleared, the scheduler will
+ // stop requesting refreshes for a given period. During this time, only direct
+ // user interaction with the NTP (and outside of the scheduler's control)
+ // should cause a refresh to occur.
+ base::Time suppress_refreshes_until_;
+
+ // Whether the scheduler is aware of an outstanding refresh or not. There are
+ // cases where a refresh may be occurring without the scheduler knowing about
+ // it, such as user interaction with UI on the NTP. If this field holds a
+ // value of true, it is expected that either OnReceiveNewContent or
+ // OnRequestError will be called eventually, somewhere on the order of seconds
+ // from now, assuming the browser does not shut down.
+ bool tracking_oustanding_request_ = false;
+
+ // May hold a nullptr if the platform does not show the user a EULA. Will only
+ // notify if IsEulaAccepted() is called and it returns false.
+ std::unique_ptr<web_resource::EulaAcceptedNotifier> eula_accepted_notifier_;
+
+ // Variables to allow metrics to be reported the first time a given trigger
+ // occurs after a refresh.
+ bool time_until_first_shown_trigger_reported_ = false;
+ bool time_until_first_foregrounded_trigger_reported_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(FeedSchedulerHost);
+};
+
+} // namespace feed
+
+#endif // COMPONENTS_FEED_CORE_FEED_SCHEDULER_HOST_H_
diff --git a/chromium/components/feed/core/feed_scheduler_host_unittest.cc b/chromium/components/feed/core/feed_scheduler_host_unittest.cc
new file mode 100644
index 00000000000..7d5e4fba086
--- /dev/null
+++ b/chromium/components/feed/core/feed_scheduler_host_unittest.cc
@@ -0,0 +1,877 @@
+// 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/feed/core/feed_scheduler_host.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/weak_ptr.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/simple_test_clock.h"
+#include "components/feed/core/pref_names.h"
+#include "components/feed/core/time_serialization.h"
+#include "components/feed/core/user_classifier.h"
+#include "components/feed/feed_feature_list.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/variations/variations_params_manager.h"
+#include "components/web_resource/web_resource_pref_names.h"
+#include "net/base/network_change_notifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace feed {
+
+// Fixed "now" to make tests more deterministic.
+char kNowString[] = "2018-06-11 15:41";
+
+using base::Time;
+using base::TimeDelta;
+
+class ForceDeviceOffline : public net::NetworkChangeNotifier {
+ public:
+ ConnectionType GetCurrentConnectionType() const override {
+ return NetworkChangeNotifier::CONNECTION_NONE;
+ }
+};
+
+class FeedSchedulerHostTest : public ::testing::Test {
+ public:
+ void FixedTimerCompletion() { fixed_timer_completion_count_++; }
+
+ protected:
+ FeedSchedulerHostTest() : weak_factory_(this) {
+ FeedSchedulerHost::RegisterProfilePrefs(profile_prefs_.registry());
+ UserClassifier::RegisterProfilePrefs(profile_prefs_.registry());
+ local_state()->registry()->RegisterBooleanPref(::prefs::kEulaAccepted,
+ true);
+
+ Time now;
+ EXPECT_TRUE(Time::FromUTCString(kNowString, &now));
+ test_clock_.SetNow(now);
+
+ NewScheduler();
+ }
+
+ void InitializeScheduler(FeedSchedulerHost* scheduler) {
+ scheduler->Initialize(
+ base::BindRepeating(&FeedSchedulerHostTest::TriggerRefresh,
+ base::Unretained(this)),
+ base::BindRepeating(&FeedSchedulerHostTest::ScheduleWakeUp,
+ base::Unretained(this)));
+ }
+
+ // Recreates a new copy of the scheduler. This is useful if a test case needs
+ // to change some global state like prefs or params before the scheduler's
+ // constructor runs.
+ void NewScheduler() {
+ scheduler_ = std::make_unique<FeedSchedulerHost>(
+ &profile_prefs_, &local_state_, &test_clock_);
+ InitializeScheduler(scheduler());
+ }
+
+ // Note: Time will be advanced.
+ void ClassifyAsRareNtpUser() {
+ // By moving time forward from initial seed events, the user will be moved
+ // into kRareNtpUser classification.
+ test_clock()->Advance(TimeDelta::FromDays(7));
+ }
+
+ // Note: Time will be advanced.
+ void ClassifyAsActiveSuggestionsConsumer() {
+ // Click on some articles to move the user into kActiveSuggestionsConsumer
+ // classification. Separate by at least 30 minutes for different sessions.
+ scheduler()->OnSuggestionConsumed();
+ test_clock()->Advance(TimeDelta::FromMinutes(31));
+ scheduler()->OnSuggestionConsumed();
+ }
+
+ // Many test cases want to ask the scheduler multiple times in a row to see
+ // which of the different triggers or under which conditions the scheduler
+ // will request a refresh. However the scheduler updates internal state when
+ // it decides a refresh must be made, most importantly, it sets
+ // |tracking_oustanding_request_| to true. Any subsequent trigger would then
+ // not start a refresh. To get around this, this method clears out
+ // |tracking_oustanding_request_|.
+ void ResetRefreshState(Time last_attempt) {
+ // OnRequestError() has the side effect of setting kLastFetchAttemptTime to
+ // the scheduler's clock's now. This typically is not helpful to most test
+ // cases, so override it.
+ scheduler()->OnRequestError(0);
+ profile_prefs()->SetTime(prefs::kLastFetchAttemptTime, last_attempt);
+ }
+
+ bool PlatformSupportsEula() {
+ return web_resource::EulaAcceptedNotifier::Create(local_state()) != nullptr;
+ }
+
+ // This helper method sets prefs::kLastFetchAttemptTime to the same value
+ // that's about to be passed into ShouldSessionRequestData(). This is what the
+ // scheduler will typically experience when refreshes are successful. Also
+ // clears out |tracking_oustanding_request_| through OnRequestError().
+ NativeRequestBehavior ShouldSessionRequestData(
+ bool has_content,
+ Time content_creation_date_time,
+ bool has_outstanding_request) {
+ ResetRefreshState(content_creation_date_time);
+ return scheduler()->ShouldSessionRequestData(
+ has_content, content_creation_date_time, has_outstanding_request);
+ }
+
+ TestingPrefServiceSimple* profile_prefs() { return &profile_prefs_; }
+ TestingPrefServiceSimple* local_state() { return &local_state_; }
+ base::SimpleTestClock* test_clock() { return &test_clock_; }
+ FeedSchedulerHost* scheduler() { return scheduler_.get(); }
+ int refresh_call_count() { return refresh_call_count_; }
+ const std::vector<TimeDelta>& schedule_wake_up_times() {
+ return schedule_wake_up_times_;
+ }
+ int cancel_wake_up_call_count() { return cancel_wake_up_call_count_; }
+ int fixed_timer_completion_count() { return fixed_timer_completion_count_; }
+
+ private:
+ void TriggerRefresh() { refresh_call_count_++; }
+
+ void ScheduleWakeUp(TimeDelta threshold_ms) {
+ schedule_wake_up_times_.push_back(threshold_ms);
+ }
+
+ void CancelWakeUp() { cancel_wake_up_call_count_++; }
+
+ TestingPrefServiceSimple profile_prefs_;
+ TestingPrefServiceSimple local_state_;
+ base::SimpleTestClock test_clock_;
+ std::unique_ptr<FeedSchedulerHost> scheduler_;
+ int refresh_call_count_ = 0;
+ std::vector<TimeDelta> schedule_wake_up_times_;
+ int cancel_wake_up_call_count_ = 0;
+ int fixed_timer_completion_count_ = 0;
+ base::WeakPtrFactory<FeedSchedulerHostTest> weak_factory_;
+};
+
+TEST_F(FeedSchedulerHostTest, GetTriggerThreshold) {
+ // Make sure that there is no missing configuration in the Cartesian product
+ // of states between TriggerType and UserClass.
+ std::vector<FeedSchedulerHost::TriggerType> triggers = {
+ FeedSchedulerHost::TriggerType::kNtpShown,
+ FeedSchedulerHost::TriggerType::kForegrounded,
+ FeedSchedulerHost::TriggerType::kFixedTimer};
+
+ // Classification starts out as an active NTP user.
+ for (FeedSchedulerHost::TriggerType trigger : triggers) {
+ EXPECT_FALSE(scheduler()->GetTriggerThreshold(trigger).is_zero());
+ }
+
+ ClassifyAsRareNtpUser();
+ for (FeedSchedulerHost::TriggerType trigger : triggers) {
+ EXPECT_FALSE(scheduler()->GetTriggerThreshold(trigger).is_zero());
+ }
+
+ ClassifyAsActiveSuggestionsConsumer();
+ for (FeedSchedulerHost::TriggerType trigger : triggers) {
+ EXPECT_FALSE(scheduler()->GetTriggerThreshold(trigger).is_zero());
+ }
+}
+
+TEST_F(FeedSchedulerHostTest, ShouldSessionRequestDataSimple) {
+ // For an kActiveNtpUser, refreshes on NTP_OPEN should be triggered after 4
+ // hours, and staleness should be at 24 hours. Each case tests a range of
+ // values.
+ Time no_refresh_large = test_clock()->Now() - TimeDelta::FromHours(3);
+ Time refresh_only_small = test_clock()->Now() - TimeDelta::FromHours(5);
+ Time refresh_only_large = test_clock()->Now() - TimeDelta::FromHours(23);
+ Time stale_small = test_clock()->Now() - TimeDelta::FromHours(25);
+
+ EXPECT_EQ(kRequestWithWait,
+ ShouldSessionRequestData(
+ /*has_content*/ false, /*content_creation_date_time*/ Time(),
+ /*has_outstanding_request*/ false));
+
+ EXPECT_EQ(kRequestWithContent,
+ ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ refresh_only_small,
+ /*has_outstanding_request*/ false));
+ EXPECT_EQ(kRequestWithContent,
+ ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ refresh_only_large,
+ /*has_outstanding_request*/ false));
+
+ EXPECT_EQ(kRequestWithTimeout, ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ stale_small,
+ /*has_outstanding_request*/ false));
+ EXPECT_EQ(kRequestWithTimeout,
+ ShouldSessionRequestData(
+ /*has_content*/ true, /*content_creation_date_time*/ Time(),
+ /*has_outstanding_request*/ false));
+
+ // |content_creation_date_time| should be ignored when |has_content| is false.
+ EXPECT_EQ(kNoRequestWithWait,
+ ShouldSessionRequestData(
+ /*has_content*/ false,
+ /*content_creation_date_time*/ test_clock()->Now(),
+ /*has_outstanding_request*/ true));
+ EXPECT_EQ(kNoRequestWithWait,
+ ShouldSessionRequestData(
+ /*has_content*/ false, /*content_creation_date_time*/ Time(),
+ /*has_outstanding_request*/ true));
+
+ EXPECT_EQ(kNoRequestWithContent,
+ ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ test_clock()->Now(),
+ /*has_outstanding_request*/ false));
+ EXPECT_EQ(kNoRequestWithContent,
+ ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ no_refresh_large,
+ /*has_outstanding_request*/ false));
+ EXPECT_EQ(kNoRequestWithContent,
+ ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ test_clock()->Now(),
+ /*has_outstanding_request*/ true));
+ EXPECT_EQ(kNoRequestWithContent,
+ ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ refresh_only_large,
+ /*has_outstanding_request*/ true));
+
+ EXPECT_EQ(
+ kNoRequestWithTimeout,
+ ShouldSessionRequestData(
+ /*has_content*/ true, /*content_creation_date_time*/ stale_small,
+ /*has_outstanding_request*/ true));
+ EXPECT_EQ(kNoRequestWithTimeout,
+ ShouldSessionRequestData(
+ /*has_content*/ true, /*content_creation_date_time*/ Time(),
+ /*has_outstanding_request*/ true));
+}
+
+TEST_F(FeedSchedulerHostTest, ShouldSessionRequestDataDivergentTimes) {
+ // If a request fails, then the |content_creation_date_time| and the value of
+ // prefs::kLastFetchAttemptTime may diverge. This is okay, and will typically
+ // mean that refreshes are not taken. Staleness should continue to track
+ // |content_creation_date_time|, but because staleness uses a bigger threshold
+ // than NTP_OPEN, this will not affect much.
+
+ // Like above case, the user is an kActiveNtpUser, staleness at 24 hours and
+ // refresh at 4.
+ Time refresh = test_clock()->Now() - TimeDelta::FromHours(5);
+ Time no_refresh = test_clock()->Now() - TimeDelta::FromHours(3);
+ Time stale = test_clock()->Now() - TimeDelta::FromHours(25);
+ Time not_stale = test_clock()->Now() - TimeDelta::FromHours(23);
+
+ ResetRefreshState(no_refresh);
+ EXPECT_EQ(kNoRequestWithContent, scheduler()->ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ stale,
+ /*has_outstanding_request*/ false));
+
+ ResetRefreshState(refresh);
+ EXPECT_EQ(kRequestWithContent, scheduler()->ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ not_stale,
+ /*has_outstanding_request*/ false));
+
+ ResetRefreshState(refresh);
+ EXPECT_EQ(kRequestWithTimeout, scheduler()->ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ stale,
+ /*has_outstanding_request*/ false));
+
+ // This shouldn't be possible, since last attempt is farther back than
+ // |content_creation_date_time| which updates on success, but verify scheduler
+ // handles it reasonably.
+ ResetRefreshState(Time());
+ EXPECT_EQ(kRequestWithContent,
+ scheduler()->ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ test_clock()->Now(),
+ /*has_outstanding_request*/ false));
+
+ // By changing the foregrounded threshold, staleness calculation changes.
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"foregrounded_hours_active_ntp_user", "7.5"}},
+ {kInterestFeedContentSuggestions.name});
+
+ ResetRefreshState(Time());
+ EXPECT_EQ(kRequestWithContent,
+ scheduler()->ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ test_clock()->Now() -
+ TimeDelta::FromHours(7),
+ /*has_outstanding_request*/ false));
+
+ ResetRefreshState(Time());
+ EXPECT_EQ(kRequestWithTimeout,
+ scheduler()->ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ test_clock()->Now() -
+ TimeDelta::FromHours(8),
+ /*has_outstanding_request*/ false));
+}
+
+TEST_F(FeedSchedulerHostTest, NtpShownActiveNtpUser) {
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"ntp_shown_hours_active_ntp_user", "2.5"}},
+ {kInterestFeedContentSuggestions.name});
+
+ EXPECT_EQ(kNoRequestWithContent,
+ ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ test_clock()->Now() -
+ TimeDelta::FromHours(2),
+ /*has_outstanding_request*/ false));
+
+ EXPECT_EQ(kRequestWithContent,
+ ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ test_clock()->Now() -
+ TimeDelta::FromHours(3),
+ /*has_outstanding_request*/ false));
+}
+
+TEST_F(FeedSchedulerHostTest, NtpShownRareNtpUser) {
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"ntp_shown_hours_rare_ntp_user", "1.5"}},
+ {kInterestFeedContentSuggestions.name});
+
+ ClassifyAsRareNtpUser();
+
+ EXPECT_EQ(kNoRequestWithContent,
+ ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ test_clock()->Now() -
+ TimeDelta::FromHours(1),
+ /*has_outstanding_request*/ false));
+
+ // ShouldSessionRequestData() has the side effect of adding NTP_SHOWN event to
+ // the classifier, so push the timer out to keep classification.
+ ClassifyAsRareNtpUser();
+
+ EXPECT_EQ(kRequestWithContent,
+ ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ test_clock()->Now() -
+ TimeDelta::FromHours(2),
+ /*has_outstanding_request*/ false));
+}
+
+TEST_F(FeedSchedulerHostTest, NtpShownActiveSuggestionsConsumer) {
+ ClassifyAsActiveSuggestionsConsumer();
+
+ EXPECT_EQ(kNoRequestWithContent,
+ ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ test_clock()->Now() -
+ TimeDelta::FromMinutes(59),
+ /*has_outstanding_request*/ false));
+
+ EXPECT_EQ(kRequestWithContent,
+ ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ test_clock()->Now() -
+ TimeDelta::FromMinutes(61),
+ /*has_outstanding_request*/ false));
+
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"ntp_shown_hours_active_suggestions_consumer", "7.5"}},
+ {kInterestFeedContentSuggestions.name});
+
+ EXPECT_EQ(kNoRequestWithContent,
+ ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ test_clock()->Now() -
+ TimeDelta::FromHours(7),
+ /*has_outstanding_request*/ false));
+
+ EXPECT_EQ(kRequestWithContent,
+ ShouldSessionRequestData(
+ /*has_content*/ true,
+ /*content_creation_date_time*/ test_clock()->Now() -
+ TimeDelta::FromHours(8),
+ /*has_outstanding_request*/ false));
+}
+
+TEST_F(FeedSchedulerHostTest, OnReceiveNewContentVerifyPref) {
+ EXPECT_EQ(Time(), profile_prefs()->GetTime(prefs::kLastFetchAttemptTime));
+ scheduler()->OnReceiveNewContent(Time());
+ EXPECT_EQ(Time(), profile_prefs()->GetTime(prefs::kLastFetchAttemptTime));
+ // Scheduler should prefer to use specified time over clock time.
+ EXPECT_NE(test_clock()->Now(),
+ profile_prefs()->GetTime(prefs::kLastFetchAttemptTime));
+}
+
+TEST_F(FeedSchedulerHostTest, OnRequestErrorVerifyPref) {
+ EXPECT_EQ(Time(), profile_prefs()->GetTime(prefs::kLastFetchAttemptTime));
+ scheduler()->OnRequestError(0);
+ EXPECT_EQ(test_clock()->Now(),
+ profile_prefs()->GetTime(prefs::kLastFetchAttemptTime));
+}
+
+TEST_F(FeedSchedulerHostTest, OnForegroundedActiveNtpUser) {
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, refresh_call_count());
+ scheduler()->OnReceiveNewContent(test_clock()->Now());
+
+ // Default is 24 hours.
+ test_clock()->Advance(TimeDelta::FromHours(23));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromHours(2));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(2, refresh_call_count());
+ scheduler()->OnReceiveNewContent(test_clock()->Now());
+
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"foregrounded_hours_active_ntp_user", "7.5"}},
+ {kInterestFeedContentSuggestions.name});
+
+ test_clock()->Advance(TimeDelta::FromHours(7));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(2, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromHours(1));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(3, refresh_call_count());
+}
+
+TEST_F(FeedSchedulerHostTest, OnForegroundedRareNtpUser) {
+ ClassifyAsRareNtpUser();
+
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, refresh_call_count());
+ scheduler()->OnReceiveNewContent(test_clock()->Now());
+
+ // Default is 24 hours.
+ test_clock()->Advance(TimeDelta::FromHours(23));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromHours(2));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(2, refresh_call_count());
+ scheduler()->OnReceiveNewContent(test_clock()->Now());
+
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"foregrounded_hours_rare_ntp_user", "7.5"}},
+ {kInterestFeedContentSuggestions.name});
+
+ test_clock()->Advance(TimeDelta::FromHours(7));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(2, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromHours(1));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(3, refresh_call_count());
+}
+
+TEST_F(FeedSchedulerHostTest, OnForegroundedActiveSuggestionsConsumer) {
+ ClassifyAsActiveSuggestionsConsumer();
+
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, refresh_call_count());
+ scheduler()->OnReceiveNewContent(test_clock()->Now());
+
+ // Default is 12 hours.
+ test_clock()->Advance(TimeDelta::FromHours(11));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromHours(2));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(2, refresh_call_count());
+ scheduler()->OnReceiveNewContent(test_clock()->Now());
+
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"foregrounded_hours_active_suggestions_consumer", "7.5"}},
+ {kInterestFeedContentSuggestions.name});
+
+ test_clock()->Advance(TimeDelta::FromHours(7));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(2, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromHours(1));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(3, refresh_call_count());
+}
+
+TEST_F(FeedSchedulerHostTest, OnFixedTimerNullCallback) {
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(1, refresh_call_count());
+}
+
+TEST_F(FeedSchedulerHostTest, OnFixedTimerCompletionRunOnSuccess) {
+ scheduler()->OnFixedTimer(base::BindOnce(
+ &FeedSchedulerHostTest::FixedTimerCompletion, base::Unretained(this)));
+ EXPECT_EQ(1, refresh_call_count());
+
+ scheduler()->OnReceiveNewContent(Time());
+ EXPECT_EQ(1, fixed_timer_completion_count());
+}
+
+TEST_F(FeedSchedulerHostTest, OnFixedTimerCompletionRunOnFailure) {
+ scheduler()->OnFixedTimer(base::BindOnce(
+ &FeedSchedulerHostTest::FixedTimerCompletion, base::Unretained(this)));
+ EXPECT_EQ(1, refresh_call_count());
+
+ scheduler()->OnRequestError(0);
+ EXPECT_EQ(1, fixed_timer_completion_count());
+}
+
+TEST_F(FeedSchedulerHostTest, OnFixedTimerActiveNtpUser) {
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(1, refresh_call_count());
+ scheduler()->OnReceiveNewContent(test_clock()->Now());
+
+ // Default is 48 hours.
+ test_clock()->Advance(TimeDelta::FromHours(47));
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(1, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromHours(2));
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(2, refresh_call_count());
+ scheduler()->OnReceiveNewContent(test_clock()->Now());
+
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"fixed_timer_hours_active_ntp_user", "7.5"}},
+ {kInterestFeedContentSuggestions.name});
+
+ test_clock()->Advance(TimeDelta::FromHours(7));
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(2, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromHours(1));
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(3, refresh_call_count());
+}
+
+TEST_F(FeedSchedulerHostTest, OnFixedTimerActiveRareNtpUser) {
+ ClassifyAsRareNtpUser();
+
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(1, refresh_call_count());
+ scheduler()->OnReceiveNewContent(test_clock()->Now());
+
+ // Default is 96 hours.
+ test_clock()->Advance(TimeDelta::FromHours(95));
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(1, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromHours(2));
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(2, refresh_call_count());
+ scheduler()->OnReceiveNewContent(test_clock()->Now());
+
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"fixed_timer_hours_rare_ntp_user", "7.5"}},
+ {kInterestFeedContentSuggestions.name});
+
+ test_clock()->Advance(TimeDelta::FromHours(7));
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(2, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromHours(1));
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(3, refresh_call_count());
+}
+
+TEST_F(FeedSchedulerHostTest, OnFixedTimerActiveSuggestionsConsumer) {
+ ClassifyAsActiveSuggestionsConsumer();
+
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(1, refresh_call_count());
+ scheduler()->OnReceiveNewContent(test_clock()->Now());
+
+ // Default is 24 hours.
+ test_clock()->Advance(TimeDelta::FromHours(23));
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(1, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromHours(2));
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(2, refresh_call_count());
+ scheduler()->OnReceiveNewContent(test_clock()->Now());
+
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"fixed_timer_hours_active_suggestions_consumer", "7.5"}},
+ {kInterestFeedContentSuggestions.name});
+
+ test_clock()->Advance(TimeDelta::FromHours(7));
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(2, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromHours(1));
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(3, refresh_call_count());
+}
+
+TEST_F(FeedSchedulerHostTest, ScheduleFixedTimerWakeUpOnSuccess) {
+ // First wake up scheduled during Initialize().
+ EXPECT_EQ(1U, schedule_wake_up_times().size());
+ scheduler()->OnReceiveNewContent(Time());
+ EXPECT_EQ(2U, schedule_wake_up_times().size());
+
+ // Make another scheduler to initialize, make sure it doesn't schedule a
+ // wake up.
+ FeedSchedulerHost second_scheduler(profile_prefs(), local_state(),
+ test_clock());
+ InitializeScheduler(&second_scheduler);
+ EXPECT_EQ(2U, schedule_wake_up_times().size());
+}
+
+TEST_F(FeedSchedulerHostTest, OnTaskReschedule) {
+ EXPECT_EQ(1U, schedule_wake_up_times().size());
+ scheduler()->OnTaskReschedule();
+ EXPECT_EQ(2U, schedule_wake_up_times().size());
+}
+
+TEST_F(FeedSchedulerHostTest, ShouldRefreshOffline) {
+ {
+ ForceDeviceOffline forceDeviceOffline;
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(0, refresh_call_count());
+ }
+
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, refresh_call_count());
+}
+
+TEST_F(FeedSchedulerHostTest, EulaNotAccepted) {
+ if (PlatformSupportsEula()) {
+ local_state()->SetBoolean(::prefs::kEulaAccepted, false);
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(0, refresh_call_count());
+
+ // The transition should kick off a refresh.
+ local_state()->SetBoolean(::prefs::kEulaAccepted, true);
+ EXPECT_EQ(1, refresh_call_count());
+
+ // And now it doesn't block normal triggers either.
+ ResetRefreshState(Time());
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(2, refresh_call_count());
+ }
+}
+
+TEST_F(FeedSchedulerHostTest, DisableOneTrigger) {
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"disable_trigger_types", "foregrounded"}},
+ {kInterestFeedContentSuggestions.name});
+ NewScheduler();
+
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(0, refresh_call_count());
+
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(1, refresh_call_count());
+
+ EXPECT_EQ(kRequestWithWait,
+ ShouldSessionRequestData(
+ /*has_content*/ false, /*content_creation_date_time*/ Time(),
+ /*has_outstanding_request*/ false));
+}
+
+TEST_F(FeedSchedulerHostTest, DisableAllTriggers) {
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"disable_trigger_types", "ntp_shown,foregrounded,fixed_timer"}},
+ {kInterestFeedContentSuggestions.name});
+ NewScheduler();
+
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(0, refresh_call_count());
+
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(0, refresh_call_count());
+
+ EXPECT_EQ(kNoRequestWithWait,
+ ShouldSessionRequestData(
+ /*has_content*/ false, /*content_creation_date_time*/ Time(),
+ /*has_outstanding_request*/ false));
+}
+
+TEST_F(FeedSchedulerHostTest, DisableBogusTriggers) {
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"disable_trigger_types", "foo,123,#$*,,"}},
+ {kInterestFeedContentSuggestions.name});
+
+ NewScheduler();
+
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, refresh_call_count());
+
+ ResetRefreshState(Time());
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(2, refresh_call_count());
+
+ EXPECT_EQ(kRequestWithWait,
+ ShouldSessionRequestData(
+ /*has_content*/ false, /*content_creation_date_time*/ Time(),
+ /*has_outstanding_request*/ false));
+}
+
+TEST_F(FeedSchedulerHostTest, OnHistoryCleared) {
+ // OnForegrounded() does nothing because content is fresher than threshold.
+ scheduler()->OnReceiveNewContent(test_clock()->Now());
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(0, refresh_call_count());
+
+ scheduler()->OnHistoryCleared();
+
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(0, refresh_call_count());
+
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(0, refresh_call_count());
+
+ EXPECT_EQ(kNoRequestWithWait,
+ ShouldSessionRequestData(
+ /*has_content*/ false, /*content_creation_date_time*/ Time(),
+ /*has_outstanding_request*/ false));
+
+ test_clock()->Advance(TimeDelta::FromMinutes(29));
+
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(0, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromMinutes(1));
+
+ // Normally this would still be within foreground threshold, but the
+ // OnHistoryCleared() cleared the last attempt time.
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, refresh_call_count());
+}
+
+TEST_F(FeedSchedulerHostTest, SuppressRefreshDuration) {
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"suppress_refresh_duration_minutes", "100"}},
+ {kInterestFeedContentSuggestions.name});
+ scheduler()->OnHistoryCleared();
+
+ test_clock()->Advance(TimeDelta::FromMinutes(99));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(0, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromMinutes(1));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, refresh_call_count());
+}
+
+TEST_F(FeedSchedulerHostTest, OustandingRequest) {
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, refresh_call_count());
+
+ scheduler()->OnForegrounded();
+ scheduler()->OnFixedTimer(base::OnceClosure());
+ EXPECT_EQ(1, refresh_call_count());
+ EXPECT_EQ(kNoRequestWithWait,
+ scheduler()->ShouldSessionRequestData(
+ /*has_content*/ false, /*content_creation_date_time*/ Time(),
+ /*has_outstanding_request*/ true));
+
+ test_clock()->Advance(TimeDelta::FromDays(7));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, refresh_call_count());
+
+ // Although this clears outstanding, it also updates last attempted time, so
+ // still expect no refresh.
+ scheduler()->OnRequestError(0);
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromDays(7));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(2, refresh_call_count());
+
+ // OnReceiveNewContent() should also clear tracked outstanding request, but
+ // similar to above, last attempted time is also set.
+ scheduler()->OnReceiveNewContent(test_clock()->Now());
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(2, refresh_call_count());
+
+ test_clock()->Advance(TimeDelta::FromDays(7));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(3, refresh_call_count());
+
+ // Although this shouldn't typically happen, OnReceiveNewContent() takes a
+ // time that could be wildly divergent from the scheduler's clock's now.
+ scheduler()->OnReceiveNewContent(test_clock()->Now() -
+ TimeDelta::FromDays(7));
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(4, refresh_call_count());
+}
+
+TEST_F(FeedSchedulerHostTest, IncorporatesExternalOustandingRequest) {
+ EXPECT_EQ(kNoRequestWithWait,
+ scheduler()->ShouldSessionRequestData(
+ /*has_content*/ false, /*content_creation_date_time*/ Time(),
+ /*has_outstanding_request*/ true));
+
+ // Normally this would trigger a refresh. In this case the scheduler will
+ // notice the ShouldSessionRequestData() call carries information that there
+ // is an outstanding request, which the scheduler should remember and then
+ // prevent the OnForegrounded() from requesting a refresh.
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(0, refresh_call_count());
+}
+
+TEST_F(FeedSchedulerHostTest, TimeUntilFirstMetrics) {
+ base::HistogramTester histogram_tester;
+ std::string ntpOpenedHistogram =
+ "NewTabPage.ContentSuggestions.TimeUntilFirstShownTrigger.ActiveNTPUser";
+ std::string forgroundedHistogram =
+ "NewTabPage.ContentSuggestions.TimeUntilFirstStartupTrigger"
+ ".ActiveNTPUser";
+ Time now = test_clock()->Now();
+ profile_prefs()->SetTime(prefs::kLastFetchAttemptTime, now);
+ EXPECT_EQ(0U, histogram_tester.GetAllSamples(ntpOpenedHistogram).size());
+ EXPECT_EQ(0U, histogram_tester.GetAllSamples(forgroundedHistogram).size());
+
+ scheduler()->ShouldSessionRequestData(
+ /*has_content*/ false, now, /*has_outstanding_request*/ false);
+ EXPECT_EQ(1, histogram_tester.GetBucketCount(ntpOpenedHistogram, 0));
+ EXPECT_EQ(0U, histogram_tester.GetAllSamples(forgroundedHistogram).size());
+
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, histogram_tester.GetBucketCount(ntpOpenedHistogram, 0));
+ EXPECT_EQ(1, histogram_tester.GetBucketCount(forgroundedHistogram, 0));
+
+ scheduler()->ShouldSessionRequestData(
+ /*has_content*/ false, now, /*has_outstanding_request*/ false);
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(1, histogram_tester.GetBucketCount(ntpOpenedHistogram, 0));
+ EXPECT_EQ(1, histogram_tester.GetBucketCount(forgroundedHistogram, 0));
+
+ // OnRequestError() should reset the flags, allowing these metrics to be
+ // reported again.
+ scheduler()->OnRequestError(0);
+
+ scheduler()->ShouldSessionRequestData(
+ /*has_content*/ false, now, /*has_outstanding_request*/ false);
+ scheduler()->OnForegrounded();
+ EXPECT_EQ(2, histogram_tester.GetBucketCount(ntpOpenedHistogram, 0));
+ EXPECT_EQ(2, histogram_tester.GetBucketCount(forgroundedHistogram, 0));
+}
+
+} // namespace feed
diff --git a/chromium/components/feed/core/feed_storage_database.cc b/chromium/components/feed/core/feed_storage_database.cc
new file mode 100644
index 00000000000..f440efde917
--- /dev/null
+++ b/chromium/components/feed/core/feed_storage_database.cc
@@ -0,0 +1,434 @@
+// 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/feed/core/feed_storage_database.h"
+
+#include <unordered_set>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/strings/string_util.h"
+#include "base/sys_info.h"
+#include "base/task_scheduler/post_task.h"
+#include "components/feed/core/proto/feed_storage.pb.h"
+#include "components/leveldb_proto/proto_database_impl.h"
+
+namespace feed {
+
+namespace {
+using StorageEntryVector =
+ leveldb_proto::ProtoDatabase<FeedStorageProto>::KeyEntryVector;
+
+// Statistics are logged to UMA with this string as part of histogram name. They
+// can all be found under LevelDB.*.FeedStorageDatabase. Changing this needs to
+// synchronize with histograms.xml, AND will also become incompatible with older
+// browsers still reporting the previous values.
+const char kStorageDatabaseUMAClientName[] = "FeedStorageDatabase";
+
+const char kStorageDatabaseFolder[] = "storage";
+
+const size_t kDatabaseWriteBufferSizeBytes = 512 * 1024;
+const size_t kDatabaseWriteBufferSizeBytesForLowEndDevice = 128 * 1024;
+
+// Key prefixes for content's storage key and journal's storage key. Because we
+// put both content data and journal data into one storage, we need to add
+// prefixes to their keys to distinguish between content keys and journal keys.
+const char kContentStoragePrefix[] = "cs-";
+const char kJournalStoragePrefix[] = "js-";
+
+// Formats content key to storage key by adding a prefix.
+std::string FormatContentKeyToStorageKey(const std::string& content_key) {
+ return kContentStoragePrefix + content_key;
+}
+
+// Formats journal key to storage key by adding a prefix.
+std::string FormatJournalKeyToStorageKey(const std::string& journal_key) {
+ return kJournalStoragePrefix + journal_key;
+}
+
+// Check if the |storage_key| is for content data.
+bool IsValidContentKey(const std::string& storage_key) {
+ return base::StartsWith(storage_key, kContentStoragePrefix,
+ base::CompareCase::SENSITIVE);
+}
+
+// Parse content key from storage key. Return an empty string if |storage_key|
+// is not recognized as content key. ex. journal's storage key.
+std::string ParseContentKey(const std::string& storage_key) {
+ if (!IsValidContentKey(storage_key)) {
+ return std::string();
+ }
+
+ return storage_key.substr(strlen(kContentStoragePrefix));
+}
+
+bool DatabaseKeyFilter(const std::unordered_set<std::string>& key_set,
+ const std::string& key) {
+ return key_set.find(key) != key_set.end();
+}
+
+bool DatabasePrefixFilter(const std::string& key_prefix,
+ const std::string& key) {
+ return base::StartsWith(key, key_prefix, base::CompareCase::SENSITIVE);
+}
+
+} // namespace
+
+FeedStorageDatabase::FeedStorageDatabase(const base::FilePath& database_folder)
+ : FeedStorageDatabase(
+ database_folder,
+ std::make_unique<leveldb_proto::ProtoDatabaseImpl<FeedStorageProto>>(
+ base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))) {}
+
+FeedStorageDatabase::FeedStorageDatabase(
+ const base::FilePath& database_folder,
+ std::unique_ptr<leveldb_proto::ProtoDatabase<FeedStorageProto>>
+ storage_database)
+ : database_status_(UNINITIALIZED),
+ storage_database_(std::move(storage_database)),
+ weak_ptr_factory_(this) {
+ leveldb_env::Options options = leveldb_proto::CreateSimpleOptions();
+ if (base::SysInfo::IsLowEndDevice()) {
+ options.write_buffer_size = kDatabaseWriteBufferSizeBytesForLowEndDevice;
+ } else {
+ options.write_buffer_size = kDatabaseWriteBufferSizeBytes;
+ }
+
+ base::FilePath storage_folder =
+ database_folder.AppendASCII(kStorageDatabaseFolder);
+ storage_database_->Init(
+ kStorageDatabaseUMAClientName, storage_folder, options,
+ base::BindOnce(&FeedStorageDatabase::OnDatabaseInitialized,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+FeedStorageDatabase::~FeedStorageDatabase() = default;
+
+bool FeedStorageDatabase::IsInitialized() const {
+ return INITIALIZED == database_status_;
+}
+
+void FeedStorageDatabase::LoadContent(const std::vector<std::string>& keys,
+ ContentLoadCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ std::unordered_set<std::string> key_set;
+ for (const auto& key : keys) {
+ key_set.insert(FormatContentKeyToStorageKey(key));
+ }
+
+ storage_database_->LoadEntriesWithFilter(
+ base::BindRepeating(&DatabaseKeyFilter, std::move(key_set)),
+ base::BindOnce(&FeedStorageDatabase::OnLoadEntriesForLoadContent,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FeedStorageDatabase::LoadContentByPrefix(const std::string& prefix,
+ ContentLoadCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ std::string key_prefix = FormatContentKeyToStorageKey(prefix);
+
+ storage_database_->LoadEntriesWithFilter(
+ base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)),
+ base::BindOnce(&FeedStorageDatabase::OnLoadEntriesForLoadContent,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FeedStorageDatabase::LoadAllContentKeys(ContentKeyCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ storage_database_->LoadKeys(
+ base::BindOnce(&FeedStorageDatabase::OnLoadKeysForLoadAllContentKeys,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FeedStorageDatabase::SaveContent(std::vector<KeyAndData> pairs,
+ ConfirmationCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ auto contents_to_save = std::make_unique<StorageEntryVector>();
+ for (auto entry : pairs) {
+ FeedStorageProto proto;
+ proto.set_key(std::move(entry.first));
+ proto.set_content_data(std::move(entry.second));
+ contents_to_save->emplace_back(FormatContentKeyToStorageKey(proto.key()),
+ std::move(proto));
+ }
+
+ storage_database_->UpdateEntries(
+ std::move(contents_to_save), std::make_unique<std::vector<std::string>>(),
+ base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FeedStorageDatabase::DeleteContent(
+ const std::vector<std::string>& keys_to_delete,
+ ConfirmationCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ auto content_to_delete = std::make_unique<std::vector<std::string>>();
+ for (const auto& key : keys_to_delete) {
+ content_to_delete->emplace_back(FormatContentKeyToStorageKey(key));
+ }
+ storage_database_->UpdateEntries(
+ std::make_unique<StorageEntryVector>(), std::move(content_to_delete),
+ base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FeedStorageDatabase::DeleteContentByPrefix(
+ const std::string& prefix_to_delete,
+ ConfirmationCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ std::string key_prefix = FormatContentKeyToStorageKey(prefix_to_delete);
+ storage_database_->UpdateEntriesWithRemoveFilter(
+ std::make_unique<StorageEntryVector>(),
+ base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)),
+ base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FeedStorageDatabase::DeleteAllContent(ConfirmationCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ std::string key_prefix = FormatContentKeyToStorageKey(std::string());
+ storage_database_->UpdateEntriesWithRemoveFilter(
+ std::make_unique<StorageEntryVector>(),
+ base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)),
+ base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FeedStorageDatabase::LoadJournal(const std::string& key,
+ JournalLoadCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ storage_database_->GetEntry(
+ FormatJournalKeyToStorageKey(key),
+ base::BindOnce(&FeedStorageDatabase::OnGetEntryForLoadJournal,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FeedStorageDatabase::LoadAllJournals(LoadAllJournalsCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ storage_database_->LoadEntriesWithFilter(
+ base::BindRepeating(&DatabasePrefixFilter, kJournalStoragePrefix),
+ base::BindOnce(&FeedStorageDatabase::OnLoadEntriesForLoadAllJournals,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FeedStorageDatabase::AppendToJournal(const std::string& key,
+ std::vector<std::string> entries,
+ ConfirmationCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ storage_database_->GetEntry(
+ FormatJournalKeyToStorageKey(key),
+ base::BindOnce(&FeedStorageDatabase::OnGetEntryAppendToJournal,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback), key,
+ std::move(entries)));
+}
+
+void FeedStorageDatabase::CopyJournal(const std::string& from_key,
+ const std::string& to_key,
+ ConfirmationCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ storage_database_->GetEntry(
+ FormatJournalKeyToStorageKey(from_key),
+ base::BindOnce(&FeedStorageDatabase::OnGetEntryForCopyJournal,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+ to_key));
+}
+
+void FeedStorageDatabase::DeleteJournal(const std::string& key,
+ ConfirmationCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ auto journals_to_delete = std::make_unique<std::vector<std::string>>();
+ journals_to_delete->push_back(FormatJournalKeyToStorageKey(key));
+
+ storage_database_->UpdateEntries(
+ std::make_unique<StorageEntryVector>(), std::move(journals_to_delete),
+ base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FeedStorageDatabase::DeleteAllJournals(ConfirmationCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ std::string key_prefix = FormatJournalKeyToStorageKey(std::string());
+ storage_database_->UpdateEntriesWithRemoveFilter(
+ std::make_unique<StorageEntryVector>(),
+ base::BindRepeating(&DatabasePrefixFilter, std::move(key_prefix)),
+ base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FeedStorageDatabase::OnDatabaseInitialized(bool success) {
+ DCHECK_EQ(database_status_, UNINITIALIZED);
+
+ if (success) {
+ database_status_ = INITIALIZED;
+ } else {
+ database_status_ = INIT_FAILURE;
+ DVLOG(1) << "FeedStorageDatabase init failed.";
+ }
+}
+
+void FeedStorageDatabase::OnLoadEntriesForLoadContent(
+ ContentLoadCallback callback,
+ bool success,
+ std::unique_ptr<std::vector<FeedStorageProto>> content) {
+ std::vector<KeyAndData> results;
+
+ if (!success || !content) {
+ DVLOG_IF(1, !success) << "FeedStorageDatabase load content failed.";
+ std::move(callback).Run(std::move(results));
+ return;
+ }
+
+ for (const auto& proto : *content) {
+ DCHECK(proto.has_key());
+ DCHECK(proto.has_content_data());
+
+ results.emplace_back(proto.key(), proto.content_data());
+ }
+
+ std::move(callback).Run(std::move(results));
+}
+
+void FeedStorageDatabase::OnLoadKeysForLoadAllContentKeys(
+ ContentKeyCallback callback,
+ bool success,
+ std::unique_ptr<std::vector<std::string>> keys) {
+ std::vector<std::string> results;
+
+ if (!success || !keys) {
+ DVLOG_IF(1, !success) << "FeedStorageDatabase load content keys failed.";
+ std::move(callback).Run(std::move(results));
+ return;
+ }
+
+ // Filter out journal keys, only keep content keys.
+ for (const std::string& key : *keys) {
+ if (IsValidContentKey(key))
+ results.emplace_back(ParseContentKey(key));
+ }
+
+ std::move(callback).Run(std::move(results));
+}
+
+void FeedStorageDatabase::OnGetEntryForLoadJournal(
+ JournalLoadCallback callback,
+ bool success,
+ std::unique_ptr<FeedStorageProto> journal) {
+ std::vector<std::string> results;
+
+ if (!success || !journal) {
+ DVLOG_IF(1, !success) << "FeedStorageDatabase load journal failed.";
+ std::move(callback).Run(std::move(results));
+ return;
+ }
+
+ for (int i = 0; i < journal->journal_data_size(); ++i) {
+ results.emplace_back(journal->journal_data(i));
+ }
+
+ std::move(callback).Run(std::move(results));
+}
+
+void FeedStorageDatabase::OnGetEntryAppendToJournal(
+ ConfirmationCallback callback,
+ const std::string& key,
+ std::vector<std::string> entries,
+ bool success,
+ std::unique_ptr<FeedStorageProto> journal) {
+ if (!success) {
+ DVLOG(1) << "FeedStorageDatabase load journal failed.";
+ std::move(callback).Run(success);
+ return;
+ }
+
+ if (journal == nullptr) {
+ // The journal does not exist, create a new one.
+ journal = std::make_unique<FeedStorageProto>();
+ journal->set_key(key);
+ }
+ DCHECK_EQ(journal->key(), key);
+
+ for (const std::string& entry : entries) {
+ journal->add_journal_data(entry);
+ }
+ auto journals_to_save = std::make_unique<StorageEntryVector>();
+ journals_to_save->emplace_back(FormatJournalKeyToStorageKey(key),
+ std::move(*journal));
+
+ storage_database_->UpdateEntries(
+ std::move(journals_to_save), std::make_unique<std::vector<std::string>>(),
+ base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FeedStorageDatabase::OnGetEntryForCopyJournal(
+ ConfirmationCallback callback,
+ const std::string& to_key,
+ bool success,
+ std::unique_ptr<FeedStorageProto> journal) {
+ if (!success || !journal) {
+ DVLOG_IF(1, !success) << "FeedStorageDatabase load journal failed.";
+ std::move(callback).Run(success);
+ return;
+ }
+
+ journal->set_key(to_key);
+ auto journal_to_save = std::make_unique<StorageEntryVector>();
+ journal_to_save->emplace_back(FormatJournalKeyToStorageKey(to_key),
+ std::move(*journal));
+
+ storage_database_->UpdateEntries(
+ std::move(journal_to_save), std::make_unique<std::vector<std::string>>(),
+ base::BindOnce(&FeedStorageDatabase::OnStorageCommitted,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void FeedStorageDatabase::OnLoadEntriesForLoadAllJournals(
+ LoadAllJournalsCallback callback,
+ bool success,
+ std::unique_ptr<std::vector<FeedStorageProto>> entries) {
+ std::vector<std::vector<std::string>> results;
+
+ if (!success || !entries) {
+ DVLOG_IF(1, !success) << "FeedStorageDatabase load journals failed.";
+ std::move(callback).Run(std::move(results));
+ return;
+ }
+
+ for (const auto& entry : *entries) {
+ DCHECK(entry.has_key());
+ DCHECK_NE(entry.journal_data_size(), 0);
+
+ std::vector<std::string> journal;
+ journal.reserve(entry.journal_data_size());
+ for (int i = 0; i < entry.journal_data_size(); ++i) {
+ journal.emplace_back(entry.journal_data(i));
+ }
+ results.push_back(journal);
+ }
+
+ std::move(callback).Run(std::move(results));
+}
+
+void FeedStorageDatabase::OnStorageCommitted(ConfirmationCallback callback,
+ bool success) {
+ DVLOG_IF(1, !success) << "FeedStorageDatabase committed failed.";
+ std::move(callback).Run(success);
+}
+
+} // namespace feed
diff --git a/chromium/components/feed/core/feed_storage_database.h b/chromium/components/feed/core/feed_storage_database.h
new file mode 100644
index 00000000000..dc657c6e89d
--- /dev/null
+++ b/chromium/components/feed/core/feed_storage_database.h
@@ -0,0 +1,171 @@
+// 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_FEED_CORE_FEED_STORAGE_DATABASE_H_
+#define COMPONENTS_FEED_CORE_FEED_STORAGE_DATABASE_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "components/leveldb_proto/proto_database.h"
+
+namespace feed {
+
+class FeedStorageProto;
+
+// FeedStorageDatabase is leveldb backed store for feed's content storage data
+// and jounal storage data.
+class FeedStorageDatabase {
+ public:
+ enum State {
+ UNINITIALIZED,
+ INITIALIZED,
+ INIT_FAILURE,
+ };
+
+ using KeyAndData = std::pair<std::string, std::string>;
+
+ // Returns the storage data as a vector of key-value pairs when calling
+ // loading data.
+ using ContentLoadCallback = base::OnceCallback<void(std::vector<KeyAndData>)>;
+
+ // Returns the content keys as a vector when calling loading all content keys.
+ using ContentKeyCallback = base::OnceCallback<void(std::vector<std::string>)>;
+
+ // Returns the journal data as a vector of strings when calling loading data.
+ using JournalLoadCallback =
+ base::OnceCallback<void(std::vector<std::string>)>;
+
+ // Returns a vector of journal data when calling loading all journals.
+ using LoadAllJournalsCallback =
+ base::OnceCallback<void(std::vector<std::vector<std::string>>)>;
+
+ // Returns whether the commit operation succeeded.
+ using ConfirmationCallback = base::OnceCallback<void(bool)>;
+
+ // Initializes the database with |database_folder|.
+ explicit FeedStorageDatabase(const base::FilePath& database_folder);
+
+ // Initializes the database with |database_folder|. Creates storage using the
+ // given |storage_database| for local storage. Useful for testing.
+ FeedStorageDatabase(
+ const base::FilePath& database_folder,
+ std::unique_ptr<leveldb_proto::ProtoDatabase<FeedStorageProto>>
+ storage_database);
+
+ ~FeedStorageDatabase();
+
+ // Returns true if initialization has finished successfully, else false.
+ // While this is false, initialization may already started, or initialization
+ // failed.
+ bool IsInitialized() const;
+
+ // Loads the content data for the |keys| and passes them to |callback|.
+ void LoadContent(const std::vector<std::string>& keys,
+ ContentLoadCallback callback);
+
+ // Loads the content data whose key matches |prefix|, and passes them to
+ // |callback|.
+ void LoadContentByPrefix(const std::string& prefix,
+ ContentLoadCallback callback);
+
+ // Loads all content keys in the storage, and passes them to |callback|.
+ void LoadAllContentKeys(ContentKeyCallback callback);
+
+ // Inserts or updates the content data |pairs|, |callback| will be called when
+ // the data are saved or if there is an error. The fields in |pairs| will be
+ // std::move.
+ void SaveContent(std::vector<KeyAndData> pairs,
+ ConfirmationCallback callback);
+
+ // Deletes the content data for |keys_to_delete|, |callback| will be called
+ // when the data are deleted or if there is an error.
+ void DeleteContent(const std::vector<std::string>& keys_to_delete,
+ ConfirmationCallback callback);
+
+ // Deletes the content data whose key matches |prefix_to_delete|, |callback|
+ // will be called when the content are deleted or if there is an error.
+ void DeleteContentByPrefix(const std::string& prefix_to_delete,
+ ConfirmationCallback callback);
+
+ // Delete all content, |callback| will be called when all content is deleted
+ // or if there is an error.
+ void DeleteAllContent(ConfirmationCallback callback);
+
+ // Loads the journal data for the |key| and passes it to |callback|.
+ void LoadJournal(const std::string& key, JournalLoadCallback callback);
+
+ // Loads all journals in the storage, and passes them to |callback|.
+ void LoadAllJournals(LoadAllJournalsCallback callback);
+
+ // Appends |entries| to a journal whose key is |key|, if there the journal do
+ // not exist, create one. |callback| will be called when the data are saved or
+ // if there is an error.
+ void AppendToJournal(const std::string& key,
+ std::vector<std::string> entries,
+ ConfirmationCallback callback);
+
+ // Creates a new journal with name |to_key|, and copys all data from the
+ // journal with |from_key| to it. |callback| will be called when the data are
+ // saved or if there is an error.
+ void CopyJournal(const std::string& from_key,
+ const std::string& to_key,
+ ConfirmationCallback callback);
+
+ // Deletes the journal with |key|, |callback| will be called when the journal
+ // is deleted or if there is an error.
+ void DeleteJournal(const std::string& key, ConfirmationCallback callback);
+
+ // Delete all journals, |callback| will be called when all journals are
+ // deleted or if there is an error.
+ void DeleteAllJournals(ConfirmationCallback callback);
+
+ private:
+ // Callback methods given to |storage_database_| for async responses.
+ void OnDatabaseInitialized(bool success);
+ void OnLoadEntriesForLoadContent(
+ ContentLoadCallback callback,
+ bool success,
+ std::unique_ptr<std::vector<FeedStorageProto>> content);
+ void OnLoadKeysForLoadAllContentKeys(
+ ContentKeyCallback callback,
+ bool success,
+ std::unique_ptr<std::vector<std::string>> keys);
+ void OnGetEntryForLoadJournal(JournalLoadCallback callback,
+ bool success,
+ std::unique_ptr<FeedStorageProto> journal);
+ void OnGetEntryAppendToJournal(ConfirmationCallback callback,
+ const std::string& key,
+ std::vector<std::string> entries,
+ bool success,
+ std::unique_ptr<FeedStorageProto> journal);
+ void OnGetEntryForCopyJournal(ConfirmationCallback callback,
+ const std::string& to_key,
+ bool success,
+ std::unique_ptr<FeedStorageProto> journal);
+ void OnLoadEntriesForLoadAllJournals(
+ LoadAllJournalsCallback callback,
+ bool success,
+ std::unique_ptr<std::vector<FeedStorageProto>> entries);
+ void OnStorageCommitted(ConfirmationCallback callback, bool success);
+
+ State database_status_;
+
+ std::unique_ptr<leveldb_proto::ProtoDatabase<FeedStorageProto>>
+ storage_database_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ base::WeakPtrFactory<FeedStorageDatabase> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FeedStorageDatabase);
+};
+
+} // namespace feed
+
+#endif // COMPONENTS_FEED_CORE_FEED_STORAGE_DATABASE_H_
diff --git a/chromium/components/feed/core/feed_storage_database_unittest.cc b/chromium/components/feed/core/feed_storage_database_unittest.cc
new file mode 100644
index 00000000000..71a6ee191d2
--- /dev/null
+++ b/chromium/components/feed/core/feed_storage_database_unittest.cc
@@ -0,0 +1,607 @@
+// 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/feed/core/feed_storage_database.h"
+
+#include <map>
+
+#include "base/test/scoped_task_environment.h"
+#include "components/feed/core/proto/feed_storage.pb.h"
+#include "components/feed/core/time_serialization.h"
+#include "components/leveldb_proto/testing/fake_db.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using leveldb_proto::test::FakeDB;
+using testing::Mock;
+using testing::NotNull;
+using testing::_;
+
+namespace feed {
+
+namespace {
+const std::string kContentKeyPrefix = "ContentKey";
+const std::string kContentKey1 = "ContentKey1";
+const std::string kContentKey2 = "ContentKey2";
+const std::string kContentKey3 = "ContentKey3";
+const std::string kContentData1 = "Content Data1";
+const std::string kContentData2 = "Content Data2";
+const std::string kContentData3 = "Content Data3";
+const std::string kJournalKey1 = "JournalKey1";
+const std::string kJournalKey2 = "JournalKey2";
+const std::string kJournalKey3 = "JournalKey3";
+const std::string kJournalData1 = "Journal Data1";
+const std::string kJournalData2 = "Journal Data2";
+const std::string kJournalData3 = "Journal Data3";
+const std::string kJournalData4 = "Journal Data4";
+const std::string kJournalData5 = "Journal Data5";
+const std::string kJournalData6 = "Journal Data6";
+} // namespace
+
+class FeedStorageDatabaseTest : public testing::Test {
+ public:
+ FeedStorageDatabaseTest() : storage_db_(nullptr) {}
+
+ void CreateDatabase(bool init_database) {
+ // The FakeDBs are owned by |feed_db_|, so clear our pointers before
+ // resetting |feed_db_| itself.
+ storage_db_ = nullptr;
+ // Explicitly destroy any existing database before creating a new one.
+ feed_db_.reset();
+
+ auto storage_db =
+ std::make_unique<FakeDB<FeedStorageProto>>(&storage_db_storage_);
+
+ storage_db_ = storage_db.get();
+ feed_db_ = std::make_unique<FeedStorageDatabase>(base::FilePath(),
+ std::move(storage_db));
+ if (init_database) {
+ storage_db_->InitCallback(true);
+ ASSERT_TRUE(db()->IsInitialized());
+ }
+ }
+
+ void InjectContentStorageProto(const std::string& key,
+ const std::string& data) {
+ FeedStorageProto storage_proto;
+ storage_proto.set_key(key);
+ storage_proto.set_content_data(data);
+ storage_db_storage_["cs-" + key] = storage_proto;
+ }
+
+ void InjectJournalStorageProto(const std::string& key,
+ const std::vector<std::string>& entries) {
+ FeedStorageProto storage_proto;
+ storage_proto.set_key(key);
+ for (const std::string& entry : entries) {
+ storage_proto.add_journal_data(entry);
+ }
+ storage_db_storage_["js-" + key] = storage_proto;
+ }
+
+ void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
+
+ FakeDB<FeedStorageProto>* storage_db() { return storage_db_; }
+
+ FeedStorageDatabase* db() { return feed_db_.get(); }
+
+ MOCK_METHOD1(OnContentEntriesReceived,
+ void(std::vector<std::pair<std::string, std::string>>));
+ MOCK_METHOD1(OnContentKeyReceived, void(std::vector<std::string>));
+ MOCK_METHOD1(OnJournalEntryReceived, void(std::vector<std::string>));
+ MOCK_METHOD1(OnJournalEntriesReceived,
+ void(std::vector<std::vector<std::string>>));
+ MOCK_METHOD1(OnStorageCommitted, void(bool));
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+ std::map<std::string, FeedStorageProto> storage_db_storage_;
+
+ // Owned by |feed_db_|.
+ FakeDB<FeedStorageProto>* storage_db_;
+
+ std::unique_ptr<FeedStorageDatabase> feed_db_;
+
+ DISALLOW_COPY_AND_ASSIGN(FeedStorageDatabaseTest);
+};
+
+TEST_F(FeedStorageDatabaseTest, Init) {
+ ASSERT_FALSE(db());
+
+ CreateDatabase(/*init_database=*/false);
+
+ storage_db()->InitCallback(true);
+ EXPECT_TRUE(db()->IsInitialized());
+}
+
+TEST_F(FeedStorageDatabaseTest, LoadContentAfterInitSuccess) {
+ CreateDatabase(/*init_database=*/true);
+
+ EXPECT_CALL(*this, OnContentEntriesReceived(_));
+ db()->LoadContent(
+ {kContentKey1},
+ base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
+ base::Unretained(this)));
+ storage_db()->LoadCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, LoadContentsEntries) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Store |kContentKey1| and |kContentKey2|.
+ InjectContentStorageProto(kContentKey1, kContentData1);
+ InjectContentStorageProto(kContentKey2, kContentData2);
+ InjectJournalStorageProto(kJournalKey1,
+ {kJournalData1, kJournalData2, kJournalData3});
+ InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
+ InjectJournalStorageProto(kJournalKey3, {kJournalData6});
+
+ // Try to Load |kContentKey2| and |kContentKey3|, only |kContentKey2| should
+ // return.
+ EXPECT_CALL(*this, OnContentEntriesReceived(_))
+ .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
+ ASSERT_EQ(results.size(), 1U);
+ EXPECT_EQ(results[0].first, kContentKey2);
+ EXPECT_EQ(results[0].second, kContentData2);
+ });
+ db()->LoadContent(
+ {kContentKey2, kContentKey3},
+ base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
+ base::Unretained(this)));
+ storage_db()->LoadCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, LoadContentsEntriesByPrefix) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Store |kContentKey1|, |kContentKey2|, |kJournalKey1|, |kJournalKey2|,
+ // |kJournalKey3|.
+ InjectContentStorageProto(kContentKey1, kContentData1);
+ InjectContentStorageProto(kContentKey2, kContentData2);
+ InjectJournalStorageProto(kJournalKey1,
+ {kJournalData1, kJournalData2, kJournalData3});
+ InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
+ InjectJournalStorageProto(kJournalKey3, {kJournalData6});
+
+ // Try to Load "ContentKey", both |kContentKey1| and |kContentKey2| should
+ // return.
+ EXPECT_CALL(*this, OnContentEntriesReceived(_))
+ .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
+ ASSERT_EQ(results.size(), 2U);
+ EXPECT_EQ(results[0].first, kContentKey1);
+ EXPECT_EQ(results[0].second, kContentData1);
+ EXPECT_EQ(results[1].first, kContentKey2);
+ EXPECT_EQ(results[1].second, kContentData2);
+ });
+ db()->LoadContentByPrefix(
+ kContentKeyPrefix,
+ base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
+ base::Unretained(this)));
+ storage_db()->LoadCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, LoadAllContentKeys) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Store |kContentKey1|, |kContentKey2|, |kJournalKey1|, |kJournalKey2|,
+ // |kJournalKey3|.
+ InjectContentStorageProto(kContentKey1, kContentData1);
+ InjectContentStorageProto(kContentKey2, kContentData2);
+ InjectJournalStorageProto(kJournalKey1,
+ {kJournalData1, kJournalData2, kJournalData3});
+ InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
+ InjectJournalStorageProto(kJournalKey3, {kJournalData6});
+
+ EXPECT_CALL(*this, OnContentKeyReceived(_))
+ .WillOnce([](std::vector<std::string> results) {
+ ASSERT_EQ(results.size(), 2U);
+ EXPECT_EQ(results[0], kContentKey1);
+ EXPECT_EQ(results[1], kContentKey2);
+ });
+ db()->LoadAllContentKeys(base::BindOnce(
+ &FeedStorageDatabaseTest::OnContentKeyReceived, base::Unretained(this)));
+ storage_db()->LoadKeysCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, SaveContent) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Store |kContentKey1|, |kContentKey2|, |kJournalKey1|, |kJournalKey2|,
+ // |kJournalKey3|.
+ std::vector<std::pair<std::string, std::string>> entries;
+ entries.push_back(std::make_pair(kContentKey1, kContentData1));
+ entries.push_back(std::make_pair(kContentKey2, kContentData2));
+ EXPECT_CALL(*this, OnStorageCommitted(true));
+ db()->SaveContent(std::move(entries),
+ base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
+ base::Unretained(this)));
+ storage_db()->UpdateCallback(true);
+
+ // Make sure they're there.
+ EXPECT_CALL(*this, OnContentEntriesReceived(_))
+ .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
+ ASSERT_EQ(results.size(), 2U);
+ EXPECT_EQ(results[0].first, kContentKey1);
+ EXPECT_EQ(results[0].second, kContentData1);
+ EXPECT_EQ(results[1].first, kContentKey2);
+ EXPECT_EQ(results[1].second, kContentData2);
+ });
+ db()->LoadContent(
+ {kContentKey1, kContentKey2},
+ base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
+ base::Unretained(this)));
+ storage_db()->LoadCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, DeleteContent) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Store |kContentKey1| and |kContentKey2|.
+ InjectContentStorageProto(kContentKey1, kContentData1);
+ InjectContentStorageProto(kContentKey2, kContentData2);
+
+ // Delete |kContentKey2| and |kContentKey3|
+ std::vector<std::string> keys;
+ keys.push_back(kContentKey2);
+ keys.push_back(kContentKey3);
+ EXPECT_CALL(*this, OnStorageCommitted(true));
+ db()->DeleteContent(
+ std::move(keys),
+ base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
+ base::Unretained(this)));
+ storage_db()->UpdateCallback(true);
+
+ // Make sure only |kContentKey2| got deleted.
+ EXPECT_CALL(*this, OnContentEntriesReceived(_))
+ .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
+ EXPECT_EQ(results.size(), 1U);
+ EXPECT_EQ(results[0].first, kContentKey1);
+ EXPECT_EQ(results[0].second, kContentData1);
+ });
+ db()->LoadContent(
+ {kContentKey1, kContentKey2},
+ base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
+ base::Unretained(this)));
+ storage_db()->LoadCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, DeleteContentByPrefix) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Store |kContentKey1| and |kContentKey2|.
+ InjectContentStorageProto(kContentKey1, kContentData1);
+ InjectContentStorageProto(kContentKey2, kContentData2);
+
+ // Delete |kContentKey1| and |kContentKey2|
+ EXPECT_CALL(*this, OnStorageCommitted(true));
+ db()->DeleteContentByPrefix(
+ kContentKeyPrefix,
+ base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
+ base::Unretained(this)));
+ storage_db()->UpdateCallback(true);
+
+ // Make sure |kContentKey1| and |kContentKey2| got deleted.
+ EXPECT_CALL(*this, OnContentEntriesReceived(_))
+ .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
+ EXPECT_EQ(results.size(), 0U);
+ });
+ db()->LoadContent(
+ {kContentKey1, kContentKey2},
+ base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
+ base::Unretained(this)));
+ storage_db()->LoadCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, DeleteAllContent) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Store |kContentKey1| and |kContentKey2|.
+ InjectContentStorageProto(kContentKey1, kContentData1);
+ InjectContentStorageProto(kContentKey2, kContentData2);
+
+ // Store |kJournalKey1|, |kJournalKey2|, |kJournalKey3|.
+ InjectJournalStorageProto(kJournalKey1,
+ {kJournalData1, kJournalData2, kJournalData3});
+ InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
+ InjectJournalStorageProto(kJournalKey3, {kJournalData6});
+
+ // Delete all content, meaning |kContentKey1| and |kContentKey2| are expected
+ // to be deleted.
+ EXPECT_CALL(*this, OnStorageCommitted(true));
+ db()->DeleteAllContent(base::BindOnce(
+ &FeedStorageDatabaseTest::OnStorageCommitted, base::Unretained(this)));
+ storage_db()->UpdateCallback(true);
+
+ // Make sure |kContentKey1| and |kContentKey2| got deleted.
+ EXPECT_CALL(*this, OnContentEntriesReceived(_))
+ .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
+ EXPECT_EQ(results.size(), 0U);
+ });
+ db()->LoadContent(
+ {kContentKey1, kContentKey2},
+ base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
+ base::Unretained(this)));
+ storage_db()->LoadCallback(true);
+
+ // Make sure all journals are there.
+ EXPECT_CALL(*this, OnJournalEntriesReceived(_))
+ .WillOnce([](std::vector<std::vector<std::string>> results) {
+ ASSERT_EQ(results.size(), 3U);
+ });
+ db()->LoadAllJournals(
+ base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntriesReceived,
+ base::Unretained(this)));
+ storage_db()->LoadCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, LoadJournalEntry) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Store |kJournalKey1|.
+ InjectJournalStorageProto(kJournalKey1,
+ {kJournalData1, kJournalData2, kJournalData3});
+
+ // Try to Load |kJournalKey1|.
+ EXPECT_CALL(*this, OnJournalEntryReceived(_))
+ .WillOnce([](std::vector<std::string> results) {
+ ASSERT_EQ(results.size(), 3U);
+ EXPECT_EQ(results[0], kJournalData1);
+ EXPECT_EQ(results[1], kJournalData2);
+ EXPECT_EQ(results[2], kJournalData3);
+ });
+ db()->LoadJournal(
+ kJournalKey1,
+ base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
+ base::Unretained(this)));
+ storage_db()->GetCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, LoadNonExistingJournalEntry) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Try to Load |kJournalKey1|.
+ EXPECT_CALL(*this, OnJournalEntryReceived(_))
+ .WillOnce([](std::vector<std::string> results) {
+ ASSERT_EQ(results.size(), 0U);
+ });
+ db()->LoadJournal(
+ kJournalKey1,
+ base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
+ base::Unretained(this)));
+ storage_db()->GetCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, LoadAllJournals) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Store |kContentKey1|, |kContentKey2|, |kJournalKey1|, |kJournalKey2|,
+ // |kJournalKey3|.
+ InjectContentStorageProto(kContentKey1, kContentData1);
+ InjectContentStorageProto(kContentKey2, kContentData2);
+ InjectJournalStorageProto(kJournalKey1,
+ {kJournalData1, kJournalData2, kJournalData3});
+ InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
+ InjectJournalStorageProto(kJournalKey3, {kJournalData6});
+
+ EXPECT_CALL(*this, OnJournalEntriesReceived(_))
+ .WillOnce([](std::vector<std::vector<std::string>> results) {
+ ASSERT_EQ(results.size(), 3U);
+ EXPECT_EQ(results[0][0], kJournalData1);
+ EXPECT_EQ(results[0][1], kJournalData2);
+ EXPECT_EQ(results[0][2], kJournalData3);
+ EXPECT_EQ(results[1][0], kJournalData4);
+ EXPECT_EQ(results[1][1], kJournalData5);
+ EXPECT_EQ(results[2][0], kJournalData6);
+ });
+ db()->LoadAllJournals(
+ base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntriesReceived,
+ base::Unretained(this)));
+ storage_db()->LoadCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, AppendToJournal_WhenJournalExists) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Save |kContentKey1|
+ EXPECT_CALL(*this, OnStorageCommitted(true));
+ db()->AppendToJournal(
+ kJournalKey1, {kJournalData1, kJournalData2},
+ base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
+ base::Unretained(this)));
+ storage_db()->GetCallback(true);
+ storage_db()->UpdateCallback(true);
+
+ // Make sure they're there.
+ EXPECT_CALL(*this, OnJournalEntryReceived(_))
+ .WillOnce([](std::vector<std::string> results) {
+ ASSERT_EQ(results.size(), 2U);
+ EXPECT_EQ(results[0], kJournalData1);
+ EXPECT_EQ(results[1], kJournalData2);
+ });
+ db()->LoadJournal(
+ kJournalKey1,
+ base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
+ base::Unretained(this)));
+ storage_db()->GetCallback(true);
+
+ Mock::VerifyAndClearExpectations(this);
+
+ // Append more for |kContentKey1|
+ EXPECT_CALL(*this, OnStorageCommitted(true));
+ db()->AppendToJournal(
+ kJournalKey1, {kJournalData3, kJournalData4, kJournalData5},
+ base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
+ base::Unretained(this)));
+ storage_db()->GetCallback(true);
+ storage_db()->UpdateCallback(true);
+
+ // Check new instances are there.
+ EXPECT_CALL(*this, OnJournalEntryReceived(_))
+ .WillOnce([](std::vector<std::string> results) {
+ ASSERT_EQ(results.size(), 5U);
+ EXPECT_EQ(results[0], kJournalData1);
+ EXPECT_EQ(results[1], kJournalData2);
+ EXPECT_EQ(results[2], kJournalData3);
+ EXPECT_EQ(results[3], kJournalData4);
+ EXPECT_EQ(results[4], kJournalData5);
+ });
+ db()->LoadJournal(
+ kJournalKey1,
+ base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
+ base::Unretained(this)));
+ storage_db()->GetCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, AppendToJournal_WhenJournalMissing) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Append data for |kContentKey1|
+ EXPECT_CALL(*this, OnStorageCommitted(true));
+ db()->AppendToJournal(
+ kJournalKey1, {kJournalData1, kJournalData2, kJournalData3},
+ base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
+ base::Unretained(this)));
+ storage_db()->GetCallback(true);
+ storage_db()->UpdateCallback(true);
+
+ // Check new data are there.
+ EXPECT_CALL(*this, OnJournalEntryReceived(_))
+ .WillOnce([](std::vector<std::string> results) {
+ ASSERT_EQ(results.size(), 3U);
+ EXPECT_EQ(results[0], kJournalData1);
+ EXPECT_EQ(results[1], kJournalData2);
+ EXPECT_EQ(results[2], kJournalData3);
+ });
+ db()->LoadJournal(
+ kJournalKey1,
+ base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
+ base::Unretained(this)));
+ storage_db()->GetCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, CopyJournal) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Save |kContentKey1|.
+ InjectJournalStorageProto(kJournalKey1,
+ {kJournalData1, kJournalData2, kJournalData3});
+
+ // Copy |kContentKey1| to |kContentKey2|.
+ EXPECT_CALL(*this, OnStorageCommitted(true));
+ db()->CopyJournal(kJournalKey1, kJournalKey2,
+ base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
+ base::Unretained(this)));
+ storage_db()->GetCallback(true);
+ storage_db()->UpdateCallback(true);
+
+ // Check new journal is there.
+ EXPECT_CALL(*this, OnJournalEntryReceived(_))
+ .WillOnce([](std::vector<std::string> results) {
+ ASSERT_EQ(results.size(), 3U);
+ EXPECT_EQ(results[0], kJournalData1);
+ EXPECT_EQ(results[1], kJournalData2);
+ EXPECT_EQ(results[2], kJournalData3);
+ });
+ db()->LoadJournal(
+ kJournalKey2,
+ base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
+ base::Unretained(this)));
+ storage_db()->GetCallback(true);
+
+ Mock::VerifyAndClearExpectations(this);
+
+ // Check first journal is still there.
+ EXPECT_CALL(*this, OnJournalEntryReceived(_))
+ .WillOnce([](std::vector<std::string> results) {
+ ASSERT_EQ(results.size(), 3U);
+ EXPECT_EQ(results[0], kJournalData1);
+ EXPECT_EQ(results[1], kJournalData2);
+ EXPECT_EQ(results[2], kJournalData3);
+ });
+ db()->LoadJournal(
+ kJournalKey1,
+ base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntryReceived,
+ base::Unretained(this)));
+ storage_db()->GetCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, DeleteJournal) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Store |kJournalKey1|, |kJournalKey2|, |kJournalKey3|.
+ InjectJournalStorageProto(kJournalKey1,
+ {kJournalData1, kJournalData2, kJournalData3});
+ InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
+ InjectJournalStorageProto(kJournalKey3, {kJournalData6});
+
+ // Delete |kJournalKey2|.
+ EXPECT_CALL(*this, OnStorageCommitted(true));
+ db()->DeleteJournal(
+ kJournalKey2, base::BindOnce(&FeedStorageDatabaseTest::OnStorageCommitted,
+ base::Unretained(this)));
+ storage_db()->UpdateCallback(true);
+
+ // Make sure |kJournalKey2| got deleted.
+ EXPECT_CALL(*this, OnJournalEntriesReceived(_))
+ .WillOnce([](std::vector<std::vector<std::string>> results) {
+ ASSERT_EQ(results.size(), 2U);
+ EXPECT_EQ(results[0][0], kJournalData1);
+ EXPECT_EQ(results[0][1], kJournalData2);
+ EXPECT_EQ(results[0][2], kJournalData3);
+ EXPECT_EQ(results[1][0], kJournalData6);
+ });
+ db()->LoadAllJournals(
+ base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntriesReceived,
+ base::Unretained(this)));
+ storage_db()->LoadCallback(true);
+}
+
+TEST_F(FeedStorageDatabaseTest, DeleteAllJournals) {
+ CreateDatabase(/*init_database=*/true);
+
+ // Store |kContentKey1| and |kContentKey2|.
+ InjectContentStorageProto(kContentKey1, kContentData1);
+ InjectContentStorageProto(kContentKey2, kContentData2);
+
+ // Store |kJournalKey1|, |kJournalKey2|, |kJournalKey3|.
+ InjectJournalStorageProto(kJournalKey1,
+ {kJournalData1, kJournalData2, kJournalData3});
+ InjectJournalStorageProto(kJournalKey2, {kJournalData4, kJournalData5});
+ InjectJournalStorageProto(kJournalKey3, {kJournalData6});
+
+ // Delete all journals, meaning |kJournalKey1|, |kJournalKey2| and
+ // |kJournalKey3| are expected to be deleted.
+ EXPECT_CALL(*this, OnStorageCommitted(true));
+ db()->DeleteAllJournals(base::BindOnce(
+ &FeedStorageDatabaseTest::OnStorageCommitted, base::Unretained(this)));
+ storage_db()->UpdateCallback(true);
+
+ // Make sure all journals got deleted.
+ EXPECT_CALL(*this, OnJournalEntriesReceived(_))
+ .WillOnce([](std::vector<std::vector<std::string>> results) {
+ ASSERT_EQ(results.size(), 0U);
+ });
+ db()->LoadAllJournals(
+ base::BindOnce(&FeedStorageDatabaseTest::OnJournalEntriesReceived,
+ base::Unretained(this)));
+ storage_db()->LoadCallback(true);
+
+ // Make sure all content are still there.
+ EXPECT_CALL(*this, OnContentEntriesReceived(_))
+ .WillOnce([](std::vector<std::pair<std::string, std::string>> results) {
+ ASSERT_EQ(results.size(), 2U);
+ EXPECT_EQ(results[0].first, kContentKey1);
+ EXPECT_EQ(results[0].second, kContentData1);
+ EXPECT_EQ(results[1].first, kContentKey2);
+ EXPECT_EQ(results[1].second, kContentData2);
+ });
+ db()->LoadContent(
+ {kContentKey1, kContentKey2},
+ base::BindOnce(&FeedStorageDatabaseTest::OnContentEntriesReceived,
+ base::Unretained(this)));
+ storage_db()->LoadCallback(true);
+}
+
+} // namespace feed
diff --git a/chromium/components/feed/core/pref_names.cc b/chromium/components/feed/core/pref_names.cc
new file mode 100644
index 00000000000..1976a47dfd9
--- /dev/null
+++ b/chromium/components/feed/core/pref_names.cc
@@ -0,0 +1,27 @@
+// 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/feed/core/pref_names.h"
+
+namespace feed {
+
+namespace prefs {
+
+const char kBackgroundRefreshPeriod[] = "feed.background_refresh_period";
+
+const char kLastFetchAttemptTime[] = "feed.last_fetch_attempt";
+
+const char kUserClassifierAverageNTPOpenedPerHour[] =
+ "feed.user_classifier.average_ntp_opened_per_hour";
+const char kUserClassifierAverageSuggestionsUsedPerHour[] =
+ "feed.user_classifier.average_suggestions_used_per_hour";
+
+const char kUserClassifierLastTimeToOpenNTP[] =
+ "feed.user_classifier.last_time_to_open_ntp";
+const char kUserClassifierLastTimeToUseSuggestions[] =
+ "feed.user_classifier.last_time_to_use_suggestions";
+
+} // namespace prefs
+
+} // namespace feed
diff --git a/chromium/components/feed/core/pref_names.h b/chromium/components/feed/core/pref_names.h
new file mode 100644
index 00000000000..cd613634e08
--- /dev/null
+++ b/chromium/components/feed/core/pref_names.h
@@ -0,0 +1,35 @@
+// 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_FEED_CORE_PREF_NAMES_H_
+#define COMPONENTS_FEED_CORE_PREF_NAMES_H_
+
+namespace feed {
+
+namespace prefs {
+
+// The pref name for the period of time between background refreshes.
+extern const char kBackgroundRefreshPeriod[];
+
+// The pref name for the last time when a background fetch was attempted.
+extern const char kLastFetchAttemptTime[];
+
+// The pref name for the discounted average number of browsing sessions per hour
+// that involve opening a new NTP.
+extern const char kUserClassifierAverageNTPOpenedPerHour[];
+// The pref name for the discounted average number of browsing sessions per hour
+// that involve using content suggestions (i.e. opening one or clicking on the
+// "More" button).
+extern const char kUserClassifierAverageSuggestionsUsedPerHour[];
+
+// The pref name for the last time a new NTP was opened.
+extern const char kUserClassifierLastTimeToOpenNTP[];
+// The pref name for the last time content suggestions were used by the user.
+extern const char kUserClassifierLastTimeToUseSuggestions[];
+
+} // namespace prefs
+
+} // namespace feed
+
+#endif // COMPONENTS_FEED_CORE_PREF_NAMES_H_
diff --git a/chromium/components/feed/core/proto/BUILD.gn b/chromium/components/feed/core/proto/BUILD.gn
index 34c6812f123..98626a99163 100644
--- a/chromium/components/feed/core/proto/BUILD.gn
+++ b/chromium/components/feed/core/proto/BUILD.gn
@@ -7,5 +7,6 @@ import("//third_party/protobuf/proto_library.gni")
proto_library("proto") {
sources = [
"cached_image.proto",
+ "feed_storage.proto",
]
}
diff --git a/chromium/components/feed/core/proto/feed_storage.proto b/chromium/components/feed/core/proto/feed_storage.proto
new file mode 100644
index 00000000000..6796b2b798c
--- /dev/null
+++ b/chromium/components/feed/core/proto/feed_storage.proto
@@ -0,0 +1,20 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package feed;
+
+message FeedStorageProto {
+ // original key for data.
+ optional string key = 1;
+
+ // Content data.
+ optional bytes content_data = 2;
+
+ // Journal data.
+ repeated bytes journal_data = 3;
+}
diff --git a/chromium/components/feed/core/user_classifier.cc b/chromium/components/feed/core/user_classifier.cc
new file mode 100644
index 00000000000..5e36c13a04a
--- /dev/null
+++ b/chromium/components/feed/core/user_classifier.cc
@@ -0,0 +1,364 @@
+// 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/feed/core/user_classifier.h"
+
+#include <algorithm>
+#include <cfloat>
+#include <string>
+
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/clock.h"
+#include "components/feed/core/pref_names.h"
+#include "components/feed/feed_feature_list.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/variations/variations_associated_data.h"
+
+namespace feed {
+
+namespace {
+
+// The discount rate for computing the discounted-average rates. Must be
+// strictly larger than 0 and strictly smaller than 1!
+const double kDiscountRatePerDay = 0.25;
+const char kDiscountRatePerDayParam[] = "user_classifier_discount_rate_per_day";
+
+// Never consider any larger interval than this (so that extreme situations such
+// as losing your phone or going for a long offline vacation do not skew the
+// average too much).
+// When overriding via variation parameters, it is better to use smaller values
+// than |kMaxHours| as this it the maximum value reported in the histograms.
+const double kMaxHours = 7 * 24;
+const char kMaxHoursParam[] = "user_classifier_max_hours";
+
+// Ignore events within |kMinHours| hours since the last event (|kMinHours| is
+// the length of the browsing session where subsequent events of the same type
+// do not count again).
+const double kMinHours = 0.5;
+const char kMinHoursParam[] = "user_classifier_min_hours";
+
+// Classification constants.
+const double kActiveConsumerClicksAtLeastOncePerHours = 96;
+const char kActiveConsumerClicksAtLeastOncePerHoursParam[] =
+ "user_classifier_active_consumer_clicks_at_least_once_per_hours";
+const double kRareUserOpensNTPAtMostOncePerHours = 96;
+const char kRareUserOpensNTPAtMostOncePerHoursParam[] =
+ "user_classifier_rare_user_opens_ntp_at_most_once_per_hours";
+
+// Histograms for logging the estimated average hours to next event.
+const char kHistogramAverageHoursToOpenNTP[] =
+ "NewTabPage.UserClassifier.AverageHoursToOpenNTP";
+const char kHistogramAverageHoursToUseSuggestions[] =
+ "NewTabPage.UserClassifier.AverageHoursToUseSuggestions";
+
+// List of all Events used for iteration.
+const UserClassifier::Event kEvents[] = {
+ UserClassifier::Event::kNtpOpened, UserClassifier::Event::kSuggestionsUsed};
+
+// Arrays of pref names, indexed by Event's int value.
+const char* kRateKeys[] = {prefs::kUserClassifierAverageNTPOpenedPerHour,
+ prefs::kUserClassifierAverageSuggestionsUsedPerHour};
+const char* kLastTimeKeys[] = {prefs::kUserClassifierLastTimeToOpenNTP,
+ prefs::kUserClassifierLastTimeToUseSuggestions};
+
+// Default lengths of the intervals for new users for the events.
+const double kInitialHoursBetweenEvents[] = {24, 120};
+const char* kInitialHoursBetweenEventsParams[] = {
+ "user_classifier_default_interval_ntp_opened",
+ "user_classifier_default_interval_suggestions_used"};
+
+// This verifies that each of the arrays has exactly the same number of values
+// as the number of enum values in UserClassifier::Event. These arrays are all
+// indexed by the integer value of UserClassifier::Event values.
+static_assert(base::size(kEvents) ==
+ static_cast<int>(UserClassifier::Event::kMaxValue) + 1 &&
+ base::size(kRateKeys) ==
+ static_cast<int>(UserClassifier::Event::kMaxValue) + 1 &&
+ base::size(kLastTimeKeys) ==
+ static_cast<int>(UserClassifier::Event::kMaxValue) + 1 &&
+ base::size(kInitialHoursBetweenEvents) ==
+ static_cast<int>(UserClassifier::Event::kMaxValue) + 1 &&
+ base::size(kInitialHoursBetweenEventsParams) ==
+ static_cast<int>(UserClassifier::Event::kMaxValue) + 1,
+ "Fill in info for all event types.");
+
+// Computes the discount rate.
+double GetDiscountRatePerHour() {
+ double discount_rate_per_day = variations::GetVariationParamByFeatureAsDouble(
+ kInterestFeedContentSuggestions, kDiscountRatePerDayParam,
+ kDiscountRatePerDay);
+ // Check for illegal values.
+ if (discount_rate_per_day <= 0 || discount_rate_per_day >= 1) {
+ DLOG(WARNING) << "Illegal value " << discount_rate_per_day
+ << " for the parameter " << kDiscountRatePerDayParam
+ << " (must be strictly between 0 and 1; the default "
+ << kDiscountRatePerDay << " is used, instead).";
+ discount_rate_per_day = kDiscountRatePerDay;
+ }
+ // Compute discount_rate_per_hour such that
+ // discount_rate_per_day = 1 - e^{-discount_rate_per_hour * 24}.
+ return std::log(1.0 / (1.0 - discount_rate_per_day)) / 24.0;
+}
+
+double GetInitialHoursBetweenEvents(UserClassifier::Event event) {
+ return variations::GetVariationParamByFeatureAsDouble(
+ kInterestFeedContentSuggestions,
+ kInitialHoursBetweenEventsParams[static_cast<int>(event)],
+ kInitialHoursBetweenEvents[static_cast<int>(event)]);
+}
+
+double GetMinHours() {
+ return variations::GetVariationParamByFeatureAsDouble(
+ kInterestFeedContentSuggestions, kMinHoursParam, kMinHours);
+}
+
+double GetMaxHours() {
+ return variations::GetVariationParamByFeatureAsDouble(
+ kInterestFeedContentSuggestions, kMaxHoursParam, kMaxHours);
+}
+
+// Returns the new value of the rate using its |old_value|, assuming
+// |hours_since_last_time| hours have passed since it was last discounted.
+double DiscountRate(double old_value,
+ double hours_since_last_time,
+ double discount_rate_per_hour) {
+ // Compute the new discounted average according to the formula
+ // avg_events := e^{-discount_rate_per_hour * hours_since} * avg_events
+ return std::exp(-discount_rate_per_hour * hours_since_last_time) * old_value;
+}
+
+// Compute the number of hours between two events for the given rate value
+// assuming the events were equally distributed.
+double GetEstimateHoursBetweenEvents(double rate,
+ double discount_rate_per_hour,
+ double min_hours,
+ double max_hours) {
+ // The computation below is well-defined only for |rate| > 1 (log of
+ // negative value or division by zero). When |rate| -> 1, the estimate
+ // below -> infinity, so max_hours is a natural result, here.
+ if (rate <= 1) {
+ return max_hours;
+ }
+
+ // This is the estimate with the assumption that last event happened right
+ // now and the system is in the steady-state. Solve estimate_hours in the
+ // steady-state equation:
+ // rate = 1 + e^{-discount_rate * estimate_hours} * rate,
+ // i.e.
+ // -discount_rate * estimate_hours = log((rate - 1) / rate),
+ // discount_rate * estimate_hours = log(rate / (rate - 1)),
+ // estimate_hours = log(rate / (rate - 1)) / discount_rate.
+ double estimate_hours = std::log(rate / (rate - 1)) / discount_rate_per_hour;
+ return std::max(min_hours, std::min(max_hours, estimate_hours));
+}
+
+// The inverse of GetEstimateHoursBetweenEvents().
+double GetRateForEstimateHoursBetweenEvents(double estimate_hours,
+ double discount_rate_per_hour,
+ double min_hours,
+ double max_hours) {
+ // Keep the input value within [min_hours, max_hours].
+ estimate_hours = std::max(min_hours, std::min(max_hours, estimate_hours));
+ // Return |rate| such that GetEstimateHoursBetweenEvents for |rate| returns
+ // |estimate_hours|. Thus, solve |rate| in
+ // rate = 1 + e^{-discount_rate * estimate_hours} * rate,
+ // i.e.
+ // rate * (1 - e^{-discount_rate * estimate_hours}) = 1,
+ // rate = 1 / (1 - e^{-discount_rate * estimate_hours}).
+ return 1.0 / (1.0 - std::exp(-discount_rate_per_hour * estimate_hours));
+}
+
+} // namespace
+
+UserClassifier::UserClassifier(PrefService* pref_service, base::Clock* clock)
+ : pref_service_(pref_service),
+ clock_(clock),
+ discount_rate_per_hour_(GetDiscountRatePerHour()),
+ min_hours_(GetMinHours()),
+ max_hours_(GetMaxHours()),
+ active_consumer_clicks_at_least_once_per_hours_(
+ variations::GetVariationParamByFeatureAsDouble(
+ kInterestFeedContentSuggestions,
+ kActiveConsumerClicksAtLeastOncePerHoursParam,
+ kActiveConsumerClicksAtLeastOncePerHours)),
+ rare_user_opens_ntp_at_most_once_per_hours_(
+ variations::GetVariationParamByFeatureAsDouble(
+ kInterestFeedContentSuggestions,
+ kRareUserOpensNTPAtMostOncePerHoursParam,
+ kRareUserOpensNTPAtMostOncePerHours)) {
+ // The pref_service_ can be null in tests.
+ if (!pref_service_) {
+ return;
+ }
+
+ // TODO(jkrcal): Store the current discount rate per hour into prefs. If it
+ // differs from the previous value, rescale the rate values so that the
+ // expectation does not change abruptly!
+
+ // Initialize the prefs storing the last time: the counter has just started!
+ for (const Event event : kEvents) {
+ if (!HasLastTime(event)) {
+ SetLastTimeToNow(event);
+ }
+ }
+}
+
+UserClassifier::~UserClassifier() = default;
+
+// static
+void UserClassifier::RegisterProfilePrefs(PrefRegistrySimple* registry) {
+ double discount_rate = GetDiscountRatePerHour();
+ double min_hours = GetMinHours();
+ double max_hours = GetMaxHours();
+
+ for (Event event : kEvents) {
+ double default_rate = GetRateForEstimateHoursBetweenEvents(
+ GetInitialHoursBetweenEvents(event), discount_rate, min_hours,
+ max_hours);
+ registry->RegisterDoublePref(kRateKeys[static_cast<int>(event)],
+ default_rate);
+ registry->RegisterTimePref(kLastTimeKeys[static_cast<int>(event)],
+ base::Time());
+ }
+}
+
+void UserClassifier::OnEvent(Event event) {
+ double metric_value = UpdateRateOnEvent(event);
+ double avg = GetEstimateHoursBetweenEvents(
+ metric_value, discount_rate_per_hour_, min_hours_, max_hours_);
+ // We use kMaxHours as the max value below as the maximum value for the
+ // histograms must be constant.
+ switch (event) {
+ case Event::kNtpOpened:
+ UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToOpenNTP, avg, 1,
+ kMaxHours, 50);
+ break;
+ case Event::kSuggestionsUsed:
+ UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToUseSuggestions, avg,
+ 1, kMaxHours, 50);
+ break;
+ }
+}
+
+double UserClassifier::GetEstimatedAvgTime(Event event) const {
+ double rate = GetUpToDateRate(event);
+ return GetEstimateHoursBetweenEvents(rate, discount_rate_per_hour_,
+ min_hours_, max_hours_);
+}
+
+UserClassifier::UserClass UserClassifier::GetUserClass() const {
+ // The pref_service_ can be null in tests.
+ if (!pref_service_) {
+ return UserClass::kActiveNtpUser;
+ }
+
+ if (GetEstimatedAvgTime(Event::kNtpOpened) >=
+ rare_user_opens_ntp_at_most_once_per_hours_) {
+ return UserClass::kRareNtpUser;
+ }
+
+ if (GetEstimatedAvgTime(Event::kSuggestionsUsed) <=
+ active_consumer_clicks_at_least_once_per_hours_) {
+ return UserClass::kActiveSuggestionsConsumer;
+ }
+
+ return UserClass::kActiveNtpUser;
+}
+
+std::string UserClassifier::GetUserClassDescriptionForDebugging() const {
+ switch (GetUserClass()) {
+ case UserClass::kRareNtpUser:
+ return "Rare user of the NTP";
+ case UserClass::kActiveNtpUser:
+ return "Active user of the NTP";
+ case UserClass::kActiveSuggestionsConsumer:
+ return "Active consumer of NTP articles";
+ }
+ NOTREACHED();
+ return std::string();
+}
+
+void UserClassifier::ClearClassificationForDebugging() {
+ // The pref_service_ can be null in tests.
+ if (!pref_service_) {
+ return;
+ }
+
+ for (const Event& event : kEvents) {
+ ClearRate(event);
+ SetLastTimeToNow(event);
+ }
+}
+
+double UserClassifier::UpdateRateOnEvent(Event event) {
+ // The pref_service_ can be null in tests.
+ if (!pref_service_) {
+ return 0;
+ }
+
+ double hours_since_last_time =
+ std::min(max_hours_, GetHoursSinceLastTime(event));
+ // Ignore events within the same "browsing session".
+ if (hours_since_last_time < min_hours_) {
+ return GetUpToDateRate(event);
+ }
+
+ SetLastTimeToNow(event);
+
+ double rate = GetRate(event);
+ // Add 1 to the discounted rate as the event has happened right now.
+ double new_rate =
+ 1 + DiscountRate(rate, hours_since_last_time, discount_rate_per_hour_);
+ SetRate(event, new_rate);
+ return new_rate;
+}
+
+double UserClassifier::GetUpToDateRate(Event event) const {
+ // The pref_service_ can be null in tests.
+ if (!pref_service_) {
+ return 0;
+ }
+
+ double hours_since_last_time =
+ std::min(max_hours_, GetHoursSinceLastTime(event));
+
+ double rate = GetRate(event);
+ return DiscountRate(rate, hours_since_last_time, discount_rate_per_hour_);
+}
+
+double UserClassifier::GetHoursSinceLastTime(Event event) const {
+ if (!HasLastTime(event)) {
+ return 0;
+ }
+
+ base::TimeDelta since_last_time =
+ clock_->Now() -
+ pref_service_->GetTime(kLastTimeKeys[static_cast<int>(event)]);
+ return since_last_time.InSecondsF() / 3600;
+}
+
+bool UserClassifier::HasLastTime(Event event) const {
+ return pref_service_->HasPrefPath(kLastTimeKeys[static_cast<int>(event)]);
+}
+
+void UserClassifier::SetLastTimeToNow(Event event) {
+ pref_service_->SetTime(kLastTimeKeys[static_cast<int>(event)], clock_->Now());
+}
+
+double UserClassifier::GetRate(Event event) const {
+ return pref_service_->GetDouble(kRateKeys[static_cast<int>(event)]);
+}
+
+void UserClassifier::SetRate(Event event, double rate) {
+ pref_service_->SetDouble(kRateKeys[static_cast<int>(event)], rate);
+}
+
+void UserClassifier::ClearRate(Event event) {
+ pref_service_->ClearPref(kRateKeys[static_cast<int>(event)]);
+}
+
+} // namespace feed
diff --git a/chromium/components/feed/core/user_classifier.h b/chromium/components/feed/core/user_classifier.h
new file mode 100644
index 00000000000..309f4741c66
--- /dev/null
+++ b/chromium/components/feed/core/user_classifier.h
@@ -0,0 +1,111 @@
+// 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_FEED_CORE_USER_CLASSIFIER_H_
+#define COMPONENTS_FEED_CORE_USER_CLASSIFIER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace base {
+class Clock;
+} // namespace base
+
+namespace feed {
+
+// Collects data about user usage patterns of content suggestions, computes
+// long-term user rates locally using pref, and reports the metrics to UMA.
+// Based on these long-term user rates, it classifies the user in a UserClass.
+class UserClassifier {
+ public:
+ // Different groupings of usage. A user will belong to exactly one of these at
+ // any given point in time. Can change at runtime.
+ enum class UserClass {
+ kRareNtpUser, // Almost never opens the NTP.
+ kActiveNtpUser, // Uses NTP but not articles.
+ kActiveSuggestionsConsumer, // Frequently opens news articles.
+ };
+
+ // For estimating the average length of the intervals between two successive
+ // events, we keep a simple frequency model, a single value that we call
+ // "rate" below.
+ // We track exponentially-discounted rate of the given event per hour where
+ // the continuous utility function between two successive events (e.g. opening
+ // a NTP) at times t1 < t2 is 1 / (t2-t1), i.e. intuitively the rate of this
+ // event in this time interval.
+ // See https://en.wikipedia.org/wiki/Exponential_discounting for more details.
+ // We keep track of the following events.
+ // NOTE: if you add any element, add it also in the static arrays in .cc and
+ // create another histogram.
+ enum class Event {
+ kNtpOpened = 0, // When the user opens a new NTP - this indicates potential
+ // use of content suggestions.
+ kSuggestionsUsed = 1, // When the user clicks on some suggestions or on
+ // some "More" button.
+ kMaxValue = kSuggestionsUsed
+ };
+
+ // The provided |pref_service| may be nullptr in unit-tests.
+ UserClassifier(PrefService* pref_service, base::Clock* clock);
+ ~UserClassifier();
+
+ // Registers profile prefs for all rates. Called from browser_prefs.cc.
+ static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+ // Informs the UserClassifier about a new event for |event|. The
+ // classification is based on these calls.
+ void OnEvent(Event event);
+
+ // Get the estimate average length of the interval between two successive
+ // events of the given type.
+ double GetEstimatedAvgTime(Event event) const;
+
+ // Return the classification of the current user.
+ UserClass GetUserClass() const;
+ std::string GetUserClassDescriptionForDebugging() const;
+
+ // Resets the classification (emulates a fresh upgrade / install).
+ void ClearClassificationForDebugging();
+
+ private:
+ // The event has happened, recompute the rate accordingly. Then store and
+ // return the new rate.
+ double UpdateRateOnEvent(Event event);
+ // No event has happened but we need to get up-to-date rate, recompute and
+ // return the new rate. This function does not store the recomputed rate.
+ double GetUpToDateRate(Event event) const;
+
+ // Returns the number of hours since the last event of the same type. If there
+ // is no last event of that type, assume it happened just now and return 0.
+ double GetHoursSinceLastTime(Event event) const;
+ bool HasLastTime(Event event) const;
+ void SetLastTimeToNow(Event event);
+
+ double GetRate(Event event) const;
+ void SetRate(Event event, double rate);
+ void ClearRate(Event event);
+
+ PrefService* pref_service_;
+ base::Clock* clock_;
+
+ // Params of the rate.
+ const double discount_rate_per_hour_;
+ const double min_hours_;
+ const double max_hours_;
+
+ // Params of the classification.
+ const double active_consumer_clicks_at_least_once_per_hours_;
+ const double rare_user_opens_ntp_at_most_once_per_hours_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserClassifier);
+};
+
+} // namespace feed
+
+#endif // COMPONENTS_FEED_CORE_USER_CLASSIFIER_H_
diff --git a/chromium/components/feed/core/user_classifier_unittest.cc b/chromium/components/feed/core/user_classifier_unittest.cc
new file mode 100644
index 00000000000..705e69fc75a
--- /dev/null
+++ b/chromium/components/feed/core/user_classifier_unittest.cc
@@ -0,0 +1,310 @@
+// 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/feed/core/user_classifier.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/simple_test_clock.h"
+#include "base/time/time.h"
+#include "components/feed/feed_feature_list.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/variations/variations_params_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::DoubleNear;
+using testing::Eq;
+using testing::Gt;
+using testing::Lt;
+using testing::SizeIs;
+
+namespace feed {
+
+namespace {
+
+char kNowString[] = "2017-03-01 10:45";
+
+class FeedUserClassifierTest : public testing::Test {
+ public:
+ FeedUserClassifierTest() {
+ UserClassifier::RegisterProfilePrefs(test_prefs_.registry());
+ }
+
+ UserClassifier* CreateUserClassifier() {
+ base::Time now;
+ CHECK(base::Time::FromUTCString(kNowString, &now));
+ test_clock_.SetNow(now);
+
+ user_classifier_ =
+ std::make_unique<UserClassifier>(&test_prefs_, &test_clock_);
+ return user_classifier_.get();
+ }
+
+ base::SimpleTestClock* test_clock() { return &test_clock_; }
+
+ private:
+ TestingPrefServiceSimple test_prefs_;
+ std::unique_ptr<UserClassifier> user_classifier_;
+ base::SimpleTestClock test_clock_;
+
+ DISALLOW_COPY_AND_ASSIGN(FeedUserClassifierTest);
+};
+
+TEST_F(FeedUserClassifierTest, ShouldBeActiveNtpUserInitially) {
+ UserClassifier* user_classifier = CreateUserClassifier();
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::kActiveNtpUser));
+}
+
+TEST_F(FeedUserClassifierTest,
+ ShouldBecomeActiveSuggestionsConsumerByClickingOften) {
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // After one click still only an active user.
+ user_classifier->OnEvent(UserClassifier::Event::kSuggestionsUsed);
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::kActiveNtpUser));
+
+ // After a few more clicks, become an active consumer.
+ for (int i = 0; i < 5; i++) {
+ test_clock()->Advance(base::TimeDelta::FromHours(1));
+ user_classifier->OnEvent(UserClassifier::Event::kSuggestionsUsed);
+ }
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::kActiveSuggestionsConsumer));
+}
+
+TEST_F(FeedUserClassifierTest,
+ ShouldBecomeActiveSuggestionsConsumerByClickingOftenWithDecreasedParam) {
+ // Increase the param to one half.
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"user_classifier_active_consumer_clicks_at_least_once_per_hours",
+ "36"}},
+ {kInterestFeedContentSuggestions.name});
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // After two clicks still only an active user.
+ user_classifier->OnEvent(UserClassifier::Event::kSuggestionsUsed);
+ test_clock()->Advance(base::TimeDelta::FromHours(1));
+ user_classifier->OnEvent(UserClassifier::Event::kSuggestionsUsed);
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::kActiveNtpUser));
+
+ // One more click to become an active consumer.
+ test_clock()->Advance(base::TimeDelta::FromHours(1));
+ user_classifier->OnEvent(UserClassifier::Event::kSuggestionsUsed);
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::kActiveSuggestionsConsumer));
+}
+
+TEST_F(FeedUserClassifierTest, ShouldBecomeRareNtpUserByNoActivity) {
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // After two days of waiting still an active user.
+ test_clock()->Advance(base::TimeDelta::FromDays(2));
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::kActiveNtpUser));
+
+ // Two more days to become a rare user.
+ test_clock()->Advance(base::TimeDelta::FromDays(2));
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::kRareNtpUser));
+}
+
+TEST_F(FeedUserClassifierTest,
+ ShouldBecomeRareNtpUserByNoActivityWithDecreasedParam) {
+ // Decrease the param to one half.
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"user_classifier_rare_user_opens_ntp_at_most_once_per_hours", "48"}},
+ {kInterestFeedContentSuggestions.name});
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // After one days of waiting still an active user.
+ test_clock()->Advance(base::TimeDelta::FromDays(1));
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::kActiveNtpUser));
+
+ // One more day to become a rare user.
+ test_clock()->Advance(base::TimeDelta::FromDays(1));
+ EXPECT_THAT(user_classifier->GetUserClass(),
+ Eq(UserClassifier::UserClass::kRareNtpUser));
+}
+
+class FeedUserClassifierEventTest
+ : public FeedUserClassifierTest,
+ public ::testing::WithParamInterface<
+ std::pair<UserClassifier::Event, std::string>> {
+ public:
+ FeedUserClassifierEventTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FeedUserClassifierEventTest);
+};
+
+TEST_P(FeedUserClassifierEventTest, ShouldDecreaseEstimateAfterEvent) {
+ UserClassifier::Event event = GetParam().first;
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // The initial event. does not decrease the estimate.
+ user_classifier->OnEvent(event);
+
+ for (int i = 0; i < 10; i++) {
+ test_clock()->Advance(base::TimeDelta::FromHours(1));
+ double old_rate = user_classifier->GetEstimatedAvgTime(event);
+ user_classifier->OnEvent(event);
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(event), Lt(old_rate));
+ }
+}
+
+TEST_P(FeedUserClassifierEventTest, ShouldReportToUmaOnEvent) {
+ UserClassifier::Event event = GetParam().first;
+ const std::string& histogram_name = GetParam().second;
+ base::HistogramTester histogram_tester;
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ user_classifier->OnEvent(event);
+ EXPECT_THAT(histogram_tester.GetAllSamples(histogram_name), SizeIs(1));
+}
+
+TEST_P(FeedUserClassifierEventTest, ShouldConvergeTowardsPattern) {
+ UserClassifier::Event event = GetParam().first;
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // Have the pattern of an event every five hours and start changing it towards
+ // an event every 10 hours.
+ for (int i = 0; i < 100; i++) {
+ test_clock()->Advance(base::TimeDelta::FromHours(5));
+ user_classifier->OnEvent(event);
+ }
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(event),
+ DoubleNear(5.0, 0.1));
+ for (int i = 0; i < 3; i++) {
+ test_clock()->Advance(base::TimeDelta::FromHours(10));
+ user_classifier->OnEvent(event);
+ }
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(event), Gt(5.5));
+ for (int i = 0; i < 100; i++) {
+ test_clock()->Advance(base::TimeDelta::FromHours(10));
+ user_classifier->OnEvent(event);
+ }
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(event),
+ DoubleNear(10.0, 0.1));
+}
+
+TEST_P(FeedUserClassifierEventTest, ShouldIgnoreSubsequentEventsForHalfAnHour) {
+ UserClassifier::Event event = GetParam().first;
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // The initial event.
+ user_classifier->OnEvent(event);
+ // Subsequent events get ignored for the next 30 minutes.
+ for (int i = 0; i < 5; i++) {
+ test_clock()->Advance(base::TimeDelta::FromMinutes(5));
+ double old_rate = user_classifier->GetEstimatedAvgTime(event);
+ user_classifier->OnEvent(event);
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(event), Eq(old_rate));
+ }
+ // An event 30 minutes after the initial event is finally not ignored.
+ test_clock()->Advance(base::TimeDelta::FromMinutes(5));
+ double old_rate = user_classifier->GetEstimatedAvgTime(event);
+ user_classifier->OnEvent(event);
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(event), Lt(old_rate));
+}
+
+TEST_P(FeedUserClassifierEventTest,
+ ShouldIgnoreSubsequentEventsWithIncreasedLimit) {
+ UserClassifier::Event event = GetParam().first;
+ // Increase the min_hours to 1.0, i.e. 60 minutes.
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"user_classifier_min_hours", "1.0"}},
+ {kInterestFeedContentSuggestions.name});
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // The initial event.
+ user_classifier->OnEvent(event);
+ // Subsequent events get ignored for the next 60 minutes.
+ for (int i = 0; i < 11; i++) {
+ test_clock()->Advance(base::TimeDelta::FromMinutes(5));
+ double old_rate = user_classifier->GetEstimatedAvgTime(event);
+ user_classifier->OnEvent(event);
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(event), Eq(old_rate));
+ }
+ // An event 60 minutes after the initial event is finally not ignored.
+ test_clock()->Advance(base::TimeDelta::FromMinutes(5));
+ double old_rate = user_classifier->GetEstimatedAvgTime(event);
+ user_classifier->OnEvent(event);
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(event), Lt(old_rate));
+}
+
+TEST_P(FeedUserClassifierEventTest, ShouldCapDelayBetweenEvents) {
+ UserClassifier::Event event = GetParam().first;
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // The initial event.
+ user_classifier->OnEvent(event);
+ // Wait for an insane amount of time
+ test_clock()->Advance(base::TimeDelta::FromDays(365));
+ user_classifier->OnEvent(event);
+ double rate_after_a_year = user_classifier->GetEstimatedAvgTime(event);
+
+ // Now repeat the same with s/one year/one week.
+ user_classifier->ClearClassificationForDebugging();
+ user_classifier->OnEvent(event);
+ test_clock()->Advance(base::TimeDelta::FromDays(7));
+ user_classifier->OnEvent(event);
+
+ // The results should be the same.
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(event),
+ Eq(rate_after_a_year));
+}
+
+TEST_P(FeedUserClassifierEventTest,
+ ShouldCapDelayBetweenEventsWithDecreasedLimit) {
+ UserClassifier::Event event = GetParam().first;
+ // Decrease the max_hours to 72, i.e. 3 days.
+ variations::testing::VariationParamsManager variation_params(
+ kInterestFeedContentSuggestions.name,
+ {{"user_classifier_max_hours", "72"}},
+ {kInterestFeedContentSuggestions.name});
+ UserClassifier* user_classifier = CreateUserClassifier();
+
+ // The initial event.
+ user_classifier->OnEvent(event);
+ // Wait for an insane amount of time
+ test_clock()->Advance(base::TimeDelta::FromDays(365));
+ user_classifier->OnEvent(event);
+ double rate_after_a_year = user_classifier->GetEstimatedAvgTime(event);
+
+ // Now repeat the same with s/one year/two days.
+ user_classifier->ClearClassificationForDebugging();
+ user_classifier->OnEvent(event);
+ test_clock()->Advance(base::TimeDelta::FromDays(3));
+ user_classifier->OnEvent(event);
+
+ // The results should be the same.
+ EXPECT_THAT(user_classifier->GetEstimatedAvgTime(event),
+ Eq(rate_after_a_year));
+}
+
+INSTANTIATE_TEST_CASE_P(
+ , // An empty prefix for the parametrized tests names (no need to
+ // distinguish the only instance we make here).
+ FeedUserClassifierEventTest,
+ testing::Values(
+ std::make_pair(UserClassifier::Event::kNtpOpened,
+ "NewTabPage.UserClassifier.AverageHoursToOpenNTP"),
+ std::make_pair(
+ UserClassifier::Event::kSuggestionsUsed,
+ "NewTabPage.UserClassifier.AverageHoursToUseSuggestions")));
+} // namespace
+
+} // namespace feed
diff --git a/chromium/components/feed/features.gni b/chromium/components/feed/features.gni
index 73f73c79a96..d61856bd8f3 100644
--- a/chromium/components/feed/features.gni
+++ b/chromium/components/feed/features.gni
@@ -3,5 +3,6 @@
# found in the LICENSE file.
declare_args() {
- enable_feed_in_chrome = is_android
+ # Temporarily compile out Feed while M69 branches to avoid bloating binary.
+ enable_feed_in_chrome = false
}
diff --git a/chromium/components/feedback/BUILD.gn b/chromium/components/feedback/BUILD.gn
index a3fa1d102b3..9278c2fb28e 100644
--- a/chromium/components/feedback/BUILD.gn
+++ b/chromium/components/feedback/BUILD.gn
@@ -16,8 +16,6 @@ static_library("feedback") {
"feedback_switches.h",
"feedback_uploader.cc",
"feedback_uploader.h",
- "feedback_uploader_delegate.cc",
- "feedback_uploader_delegate.h",
"feedback_uploader_factory.cc",
"feedback_uploader_factory.h",
"feedback_util.cc",
@@ -40,6 +38,7 @@ static_library("feedback") {
"//content/public/browser",
"//content/public/common",
"//net",
+ "//services/network/public/cpp",
"//third_party/re2",
"//third_party/zlib/google:zip",
]
@@ -65,6 +64,8 @@ source_set("unit_tests") {
"//components/variations/net",
"//content/test:test_support",
"//net:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//testing/gmock",
"//testing/gtest",
]
diff --git a/chromium/components/feedback/DEPS b/chromium/components/feedback/DEPS
index 2d72d2013c1..5d02e206042 100644
--- a/chromium/components/feedback/DEPS
+++ b/chromium/components/feedback/DEPS
@@ -11,6 +11,9 @@ include_rules = [
"+net/base",
"+net/traffic_annotation",
"+net/url_request",
+ "+net/http",
+ "+services/network/public/cpp",
+ "+services/network/test",
"+third_party/re2",
"+third_party/zlib/google",
]
diff --git a/chromium/components/feedback/feedback_data.cc b/chromium/components/feedback/feedback_data.cc
index c58dce1f077..54b8c8a82fa 100644
--- a/chromium/components/feedback/feedback_data.cc
+++ b/chromium/components/feedback/feedback_data.cc
@@ -72,8 +72,8 @@ void FeedbackData::SetAndCompressSystemInfo(
AddLogs(std::move(sys_info));
base::PostTaskWithTraitsAndReply(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
- base::Bind(&FeedbackData::CompressLogs, this),
- base::Bind(&FeedbackData::OnCompressComplete, this));
+ base::BindOnce(&FeedbackData::CompressLogs, this),
+ base::BindOnce(&FeedbackData::OnCompressComplete, this));
}
}
@@ -143,9 +143,9 @@ void FeedbackData::SendReport() {
report_sent_ = true;
userfeedback::ExtensionSubmit feedback_data;
PrepareReport(&feedback_data);
- std::string post_body;
- feedback_data.SerializeToString(&post_body);
- uploader_->QueueReport(post_body);
+ auto post_body = std::make_unique<std::string>();
+ feedback_data.SerializeToString(post_body.get());
+ uploader_->QueueReport(std::move(post_body));
}
}
diff --git a/chromium/components/feedback/feedback_data_unittest.cc b/chromium/components/feedback/feedback_data_unittest.cc
index 06fb1ffef33..d5e7bafae4e 100644
--- a/chromium/components/feedback/feedback_data_unittest.cc
+++ b/chromium/components/feedback/feedback_data_unittest.cc
@@ -14,6 +14,9 @@
#include "components/prefs/testing_pref_service.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.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_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace feedback {
@@ -26,18 +29,21 @@ constexpr char kFileData[] = "";
class MockUploader : public FeedbackUploader {
public:
- MockUploader(content::BrowserContext* context,
- const base::Closure& on_report_sent)
- : FeedbackUploader(context,
+ MockUploader(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ content::BrowserContext* context,
+ base::OnceClosure on_report_sent)
+ : FeedbackUploader(url_loader_factory,
+ context,
FeedbackUploaderFactory::CreateUploaderTaskRunner()),
- on_report_sent_(on_report_sent) {}
+ on_report_sent_(std::move(on_report_sent)) {}
~MockUploader() override {}
// feedback::FeedbackUploader:
- void StartDispatchingReport() override { on_report_sent_.Run(); }
+ void StartDispatchingReport() override { std::move(on_report_sent_).Run(); }
private:
- base::Closure on_report_sent_;
+ base::OnceClosure on_report_sent_;
DISALLOW_COPY_AND_ASSIGN(MockUploader);
};
@@ -51,9 +57,13 @@ std::unique_ptr<std::string> MakeScoped(const char* str) {
class FeedbackDataTest : public testing::Test {
protected:
FeedbackDataTest()
- : uploader_(&context_,
- base::Bind(&FeedbackDataTest::set_send_report_callback,
- base::Unretained(this))),
+ : test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)),
+ uploader_(test_shared_loader_factory_,
+ &context_,
+ base::BindOnce(&FeedbackDataTest::set_send_report_callback,
+ base::Unretained(this))),
data_(base::MakeRefCounted<FeedbackData>(&uploader_)) {}
~FeedbackDataTest() override = default;
@@ -79,6 +89,8 @@ class FeedbackDataTest : public testing::Test {
base::Closure quit_closure_;
std::unique_ptr<base::RunLoop> run_loop_;
content::TestBrowserThreadBundle test_browser_thread_bundle_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
content::TestBrowserContext context_;
MockUploader uploader_;
scoped_refptr<FeedbackData> data_;
diff --git a/chromium/components/feedback/feedback_report.cc b/chromium/components/feedback/feedback_report.cc
index faa5c7781e0..ced788fedf0 100644
--- a/chromium/components/feedback/feedback_report.cc
+++ b/chromium/components/feedback/feedback_report.cc
@@ -12,6 +12,8 @@
#include "base/sequenced_task_runner.h"
#include "base/strings/string_number_conversions.h"
+namespace feedback {
+
namespace {
constexpr base::FilePath::CharType kFeedbackReportFilenameWildcard[] =
@@ -21,44 +23,45 @@ constexpr char kFeedbackReportFilenamePrefix[] = "Feedback Report.";
void WriteReportOnBlockingPool(const base::FilePath reports_path,
const base::FilePath& file,
- const std::string& data) {
+ scoped_refptr<FeedbackReport> report) {
DCHECK(reports_path.IsParent(file));
if (!base::DirectoryExists(reports_path)) {
base::File::Error error;
if (!base::CreateDirectoryAndGetError(reports_path, &error))
return;
}
- base::ImportantFileWriter::WriteFileAtomically(file, data, "FeedbackReport");
+ base::ImportantFileWriter::WriteFileAtomically(file, report->data(),
+ "FeedbackReport");
}
} // namespace
-namespace feedback {
-
FeedbackReport::FeedbackReport(
const base::FilePath& path,
const base::Time& upload_at,
- const std::string& data,
+ std::unique_ptr<std::string> data,
scoped_refptr<base::SequencedTaskRunner> task_runner)
: reports_path_(path),
upload_at_(upload_at),
- data_(data),
+ data_(std::move(data)),
reports_task_runner_(task_runner) {
if (reports_path_.empty())
return;
file_ = reports_path_.AppendASCII(
kFeedbackReportFilenamePrefix + base::GenerateGUID());
- reports_task_runner_->PostTask(FROM_HERE, base::Bind(
- &WriteReportOnBlockingPool, reports_path_, file_, data_));
+ reports_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WriteReportOnBlockingPool, reports_path_, file_,
+ base::WrapRefCounted<FeedbackReport>(this)));
}
// static
const char FeedbackReport::kCrashReportIdsKey[] = "crash_report_ids";
// static
-void FeedbackReport::LoadReportsAndQueue(
- const base::FilePath& user_dir, QueueCallback callback) {
+void FeedbackReport::LoadReportsAndQueue(const base::FilePath& user_dir,
+ const QueueCallback& callback) {
if (user_dir.empty())
return;
@@ -69,9 +72,9 @@ void FeedbackReport::LoadReportsAndQueue(
for (base::FilePath name = enumerator.Next();
!name.empty();
name = enumerator.Next()) {
- std::string data;
- if (ReadFileToString(name, &data))
- callback.Run(data);
+ auto data = std::make_unique<std::string>();
+ if (ReadFileToString(name, data.get()))
+ callback.Run(std::move(data));
base::DeleteFile(name, false);
}
}
@@ -79,7 +82,7 @@ void FeedbackReport::LoadReportsAndQueue(
void FeedbackReport::DeleteReportOnDisk() {
reports_task_runner_->PostTask(
FROM_HERE,
- base::Bind(base::IgnoreResult(&base::DeleteFile), file_, false));
+ base::BindOnce(base::IgnoreResult(&base::DeleteFile), file_, false));
}
FeedbackReport::~FeedbackReport() {}
diff --git a/chromium/components/feedback/feedback_report.h b/chromium/components/feedback/feedback_report.h
index d3fdb8d3c82..1e330cab9e5 100644
--- a/chromium/components/feedback/feedback_report.h
+++ b/chromium/components/feedback/feedback_report.h
@@ -19,16 +19,19 @@ class SequencedTaskRunner;
namespace feedback {
-typedef base::Callback<void(const std::string&)> QueueCallback;
+// Repeating since for every feedback report file on disk, the callback to
+// queue it in the uploader needs to be invoked.
+using QueueCallback =
+ base::RepeatingCallback<void(std::unique_ptr<std::string>)>;
// This class holds a feedback report. Once a report is created, a disk backup
// for it is created automatically. This backup needs to explicitly be
// deleted by calling DeleteReportOnDisk.
-class FeedbackReport : public base::RefCounted<FeedbackReport> {
+class FeedbackReport : public base::RefCountedThreadSafe<FeedbackReport> {
public:
FeedbackReport(const base::FilePath& path,
const base::Time& upload_at,
- const std::string& data,
+ std::unique_ptr<std::string> data,
scoped_refptr<base::SequencedTaskRunner> task_runner);
// The ID of the product specific data for the crash report IDs as stored by
@@ -38,7 +41,7 @@ class FeedbackReport : public base::RefCounted<FeedbackReport> {
// Loads the reports still on disk and queues then using the given callback.
// This call blocks on the file reads.
static void LoadReportsAndQueue(const base::FilePath& user_dir,
- QueueCallback callback);
+ const QueueCallback& callback);
// Stops the disk write of the report and deletes the report file if already
// written.
@@ -46,10 +49,10 @@ class FeedbackReport : public base::RefCounted<FeedbackReport> {
const base::Time& upload_at() const { return upload_at_; }
void set_upload_at(const base::Time& time) { upload_at_ = time; }
- const std::string& data() const { return data_; }
+ const std::string& data() const { return *data_; }
private:
- friend class base::RefCounted<FeedbackReport>;
+ friend class base::RefCountedThreadSafe<FeedbackReport>;
virtual ~FeedbackReport();
// Name of the file corresponding to this report.
@@ -57,7 +60,7 @@ class FeedbackReport : public base::RefCounted<FeedbackReport> {
base::FilePath reports_path_;
base::Time upload_at_; // Upload this report at or after this time.
- std::string data_;
+ std::unique_ptr<std::string> data_;
scoped_refptr<base::SequencedTaskRunner> reports_task_runner_;
diff --git a/chromium/components/feedback/feedback_uploader.cc b/chromium/components/feedback/feedback_uploader.cc
index fe1670461ce..7b536c08441 100644
--- a/chromium/components/feedback/feedback_uploader.cc
+++ b/chromium/components/feedback/feedback_uploader.cc
@@ -9,12 +9,14 @@
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/feedback/feedback_report.h"
#include "components/feedback/feedback_switches.h"
-#include "components/feedback/feedback_uploader_delegate.h"
#include "components/variations/net/variations_http_headers.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.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/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
namespace feedback {
@@ -28,6 +30,11 @@ constexpr char kFeedbackPostUrl[] =
constexpr char kProtoBufMimeType[] = "application/x-protobuf";
+constexpr int kHttpPostSuccessNoContent = 204;
+constexpr int kHttpPostFailNoConnection = -1;
+constexpr int kHttpPostFailClientError = 400;
+constexpr int kHttpPostFailServerError = 500;
+
// The minimum time to wait before uploading reports are retried. Exponential
// backoff delay is applied on successive failures.
// This value can be overriden by tests by calling
@@ -53,9 +60,11 @@ GURL GetFeedbackPostGURL() {
} // namespace
FeedbackUploader::FeedbackUploader(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
content::BrowserContext* context,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
- : context_(context),
+ : url_loader_factory_(std::move(url_loader_factory)),
+ context_(context),
feedback_reports_path_(GetPathFromContext(context)),
task_runner_(task_runner),
feedback_post_url_(GetFeedbackPostGURL()),
@@ -72,8 +81,8 @@ void FeedbackUploader::SetMinimumRetryDelayForTesting(base::TimeDelta delay) {
g_minimum_retry_delay = delay;
}
-void FeedbackUploader::QueueReport(const std::string& data) {
- QueueReportWithDelay(data, base::TimeDelta());
+void FeedbackUploader::QueueReport(std::unique_ptr<std::string> data) {
+ QueueReportWithDelay(std::move(data), base::TimeDelta());
}
void FeedbackUploader::StartDispatchingReport() {
@@ -116,7 +125,7 @@ bool FeedbackUploader::ReportsUploadTimeComparator::operator()(
}
void FeedbackUploader::AppendExtraHeadersToUploadRequest(
- net::URLFetcher* fetcher) {}
+ network::ResourceRequest* resource_request) {}
void FeedbackUploader::DispatchReport() {
net::NetworkTrafficAnnotationTag traffic_annotation =
@@ -148,39 +157,78 @@ void FeedbackUploader::DispatchReport() {
"by direct user request."
policy_exception_justification: "Not implemented."
})");
- // Note: FeedbackUploaderDelegate deletes itself and the fetcher.
- net::URLFetcher* fetcher =
- net::URLFetcher::Create(
- feedback_post_url_, net::URLFetcher::POST,
- new FeedbackUploaderDelegate(
- base::Bind(&FeedbackUploader::OnReportUploadSuccess, AsWeakPtr()),
- base::Bind(&FeedbackUploader::OnReportUploadFailure,
- AsWeakPtr())),
- traffic_annotation)
- .release();
- data_use_measurement::DataUseUserData::AttachToFetcher(
- fetcher, data_use_measurement::DataUseUserData::FEEDBACK_UPLOADER);
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = feedback_post_url_;
+ resource_request->load_flags =
+ net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES;
+ resource_request->method = "POST";
+
// Tell feedback server about the variation state of this install.
- net::HttpRequestHeaders headers;
- // Note: It's OK to pass SignedIn::kNo if it's unknown, as it does not affect
- // transmission of experiments coming from the variations server.
- variations::AppendVariationHeaders(fetcher->GetOriginalURL(),
- context_->IsOffTheRecord()
- ? variations::InIncognito::kYes
- : variations::InIncognito::kNo,
- variations::SignedIn::kNo, &headers);
- fetcher->SetExtraRequestHeaders(headers.ToString());
-
- fetcher->SetUploadData(kProtoBufMimeType, report_being_dispatched_->data());
- fetcher->SetRequestContext(
- content::BrowserContext::GetDefaultStoragePartition(context_)
- ->GetURLRequestContext());
-
- AppendExtraHeadersToUploadRequest(fetcher);
-
- fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DO_NOT_SEND_COOKIES);
- fetcher->Start();
+ variations::AppendVariationHeadersUnknownSignedIn(
+ feedback_post_url_,
+ context_->IsOffTheRecord() ? variations::InIncognito::kYes
+ : variations::InIncognito::kNo,
+ &resource_request->headers);
+
+ AppendExtraHeadersToUploadRequest(resource_request.get());
+
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader =
+ network::SimpleURLLoader::Create(std::move(resource_request),
+ traffic_annotation);
+ network::SimpleURLLoader* simple_url_loader_ptr = simple_url_loader.get();
+ simple_url_loader->AttachStringForUpload(report_being_dispatched_->data(),
+ kProtoBufMimeType);
+ auto it = uploads_in_progress_.insert(uploads_in_progress_.begin(),
+ std::move(simple_url_loader));
+ // TODO(https://crbug.com/808498): Re-add data use measurement once
+ // SimpleURLLoader supports it.
+ // ID=data_use_measurement::DataUseUserData::FEEDBACK_UPLOADER
+ simple_url_loader_ptr->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&FeedbackUploader::OnDispatchComplete,
+ base::Unretained(this), std::move(it)));
+}
+
+void FeedbackUploader::OnDispatchComplete(
+ UrlLoaderList::iterator it,
+ std::unique_ptr<std::string> response_body) {
+ std::stringstream error_stream;
+ network::SimpleURLLoader* simple_url_loader = it->get();
+ int response_code = kHttpPostFailNoConnection;
+ if (simple_url_loader->ResponseInfo() &&
+ simple_url_loader->ResponseInfo()->headers) {
+ response_code = simple_url_loader->ResponseInfo()->headers->response_code();
+ }
+ if (response_code == kHttpPostSuccessNoContent) {
+ error_stream << "Success";
+ OnReportUploadSuccess();
+ } else {
+ bool should_retry = true;
+ // Process the error for debug output
+ if (response_code == kHttpPostFailNoConnection) {
+ error_stream << "No connection to server.";
+ } else if ((response_code >= kHttpPostFailClientError) &&
+ (response_code < kHttpPostFailServerError)) {
+ // Client errors mean that the server failed to parse the proto that was
+ // sent, or that some requirements weren't met by the server side
+ // validation, and hence we should NOT retry sending this corrupt report
+ // and give up.
+ should_retry = false;
+
+ error_stream << "Client error: HTTP response code " << response_code;
+ } else if (response_code >= kHttpPostFailServerError) {
+ error_stream << "Server error: HTTP response code " << response_code;
+ } else {
+ error_stream << "Unknown error: HTTP response code " << response_code;
+ }
+
+ OnReportUploadFailure(should_retry);
+ }
+
+ LOG(WARNING) << "FEEDBACK: Submission to feedback server ("
+ << simple_url_loader->GetFinalURL()
+ << ") status: " << error_stream.str();
+ uploads_in_progress_.erase(it);
}
void FeedbackUploader::UpdateUploadTimer() {
@@ -205,10 +253,11 @@ void FeedbackUploader::UpdateUploadTimer() {
}
}
-void FeedbackUploader::QueueReportWithDelay(const std::string& data,
+void FeedbackUploader::QueueReportWithDelay(std::unique_ptr<std::string> data,
base::TimeDelta delay) {
reports_queue_.emplace(base::MakeRefCounted<FeedbackReport>(
- feedback_reports_path_, base::Time::Now() + delay, data, task_runner_));
+ feedback_reports_path_, base::Time::Now() + delay, std::move(data),
+ task_runner_));
UpdateUploadTimer();
}
diff --git a/chromium/components/feedback/feedback_uploader.h b/chromium/components/feedback/feedback_uploader.h
index dc06ea05297..816fa256d9e 100644
--- a/chromium/components/feedback/feedback_uploader.h
+++ b/chromium/components/feedback/feedback_uploader.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_FEEDBACK_FEEDBACK_UPLOADER_H_
#define COMPONENTS_FEEDBACK_FEEDBACK_UPLOADER_H_
+#include <list>
#include <queue>
#include <vector>
@@ -22,9 +23,11 @@ namespace content {
class BrowserContext;
} // namespace content
-namespace net {
-class URLFetcher;
-} // namespace net
+namespace network {
+struct ResourceRequest;
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
+} // namespace network
namespace feedback {
@@ -36,14 +39,16 @@ class FeedbackReport;
class FeedbackUploader : public KeyedService,
public base::SupportsWeakPtr<FeedbackUploader> {
public:
- FeedbackUploader(content::BrowserContext* context,
- scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+ FeedbackUploader(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ content::BrowserContext* context,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner);
~FeedbackUploader() override;
static void SetMinimumRetryDelayForTesting(base::TimeDelta delay);
// Queues a report for uploading.
- void QueueReport(const std::string& data);
+ void QueueReport(std::unique_ptr<std::string> data);
bool QueueEmpty() const { return reports_queue_.empty(); }
@@ -84,6 +89,9 @@ class FeedbackUploader : public KeyedService,
private:
friend class FeedbackUploaderTest;
+ // This is a std::list so that iterators remain valid during modifications.
+ using UrlLoaderList = std::list<std::unique_ptr<network::SimpleURLLoader>>;
+
struct ReportsUploadTimeComparator {
bool operator()(const scoped_refptr<FeedbackReport>& a,
const scoped_refptr<FeedbackReport>& b) const;
@@ -91,17 +99,25 @@ class FeedbackUploader : public KeyedService,
// Called from DispatchReport() to give implementers a chance to add extra
// headers to the upload request before it's sent.
- virtual void AppendExtraHeadersToUploadRequest(net::URLFetcher* fetcher);
+ virtual void AppendExtraHeadersToUploadRequest(
+ network::ResourceRequest* resource_request);
// Uploads the |report_being_dispatched_| to be uploaded. It must
// call either OnReportUploadSuccess() or OnReportUploadFailure() so that
// dispatching reports can progress.
void DispatchReport();
+ void OnDispatchComplete(UrlLoaderList::iterator it,
+ std::unique_ptr<std::string> response_body);
+
// Update our timer for uploading the next report.
void UpdateUploadTimer();
- void QueueReportWithDelay(const std::string& data, base::TimeDelta delay);
+ void QueueReportWithDelay(std::unique_ptr<std::string> data,
+ base::TimeDelta delay);
+
+ // URLLoaderFactory used for network requests.
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// Browser context this uploader was created for.
content::BrowserContext* context_;
@@ -131,6 +147,8 @@ class FeedbackUploader : public KeyedService,
// at-a-time should be dispatched.
bool is_dispatching_;
+ UrlLoaderList uploads_in_progress_;
+
DISALLOW_COPY_AND_ASSIGN(FeedbackUploader);
};
diff --git a/chromium/components/feedback/feedback_uploader_delegate.cc b/chromium/components/feedback/feedback_uploader_delegate.cc
deleted file mode 100644
index fd7e475aa00..00000000000
--- a/chromium/components/feedback/feedback_uploader_delegate.cc
+++ /dev/null
@@ -1,71 +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/feedback/feedback_uploader_delegate.h"
-
-#include <memory>
-#include <sstream>
-
-#include "base/logging.h"
-#include "components/feedback/feedback_report.h"
-#include "net/url_request/url_fetcher.h"
-
-namespace feedback {
-
-namespace {
-
-constexpr int kHttpPostSuccessNoContent = 204;
-constexpr int kHttpPostFailNoConnection = -1;
-constexpr int kHttpPostFailClientError = 400;
-constexpr int kHttpPostFailServerError = 500;
-
-} // namespace
-
-FeedbackUploaderDelegate::FeedbackUploaderDelegate(
- const base::Closure& success_callback,
- const ReportFailureCallback& error_callback)
- : success_callback_(success_callback), error_callback_(error_callback) {}
-
-FeedbackUploaderDelegate::~FeedbackUploaderDelegate() {}
-
-void FeedbackUploaderDelegate::OnURLFetchComplete(
- const net::URLFetcher* source) {
- std::unique_ptr<const net::URLFetcher> source_scoper(source);
-
- std::stringstream error_stream;
- int response_code = source->GetResponseCode();
- if (response_code == kHttpPostSuccessNoContent) {
- error_stream << "Success";
- success_callback_.Run();
- } else {
- bool should_retry = true;
- // Process the error for debug output
- if (response_code == kHttpPostFailNoConnection) {
- error_stream << "No connection to server.";
- } else if ((response_code >= kHttpPostFailClientError) &&
- (response_code < kHttpPostFailServerError)) {
- // Client errors mean that the server failed to parse the proto that was
- // sent, or that some requirements weren't met by the server side
- // validation, and hence we should NOT retry sending this corrupt report
- // and give up.
- should_retry = false;
-
- error_stream << "Client error: HTTP response code " << response_code;
- } else if (response_code >= kHttpPostFailServerError) {
- error_stream << "Server error: HTTP response code " << response_code;
- } else {
- error_stream << "Unknown error: HTTP response code " << response_code;
- }
-
- error_callback_.Run(should_retry);
- }
-
- LOG(WARNING) << "FEEDBACK: Submission to feedback server ("
- << source->GetURL() << ") status: " << error_stream.str();
-
- // This instance won't be used for anything else, delete us.
- delete this;
-}
-
-} // namespace feedback
diff --git a/chromium/components/feedback/feedback_uploader_delegate.h b/chromium/components/feedback/feedback_uploader_delegate.h
deleted file mode 100644
index 817b53fc2f6..00000000000
--- a/chromium/components/feedback/feedback_uploader_delegate.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_FEEDBACK_FEEDBACK_UPLOADER_DELEGATE_H_
-#define COMPONENTS_FEEDBACK_FEEDBACK_UPLOADER_DELEGATE_H_
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "components/feedback/feedback_uploader.h"
-#include "net/url_request/url_fetcher_delegate.h"
-
-namespace feedback {
-
-// Type of the callback that gets invoked when uploading a feedback report
-// fails. |should_retry| is set to true when it's OK to retry sending the
-// report; e.g. when the failure is not a client error and retries is likely to
-// fail again.
-using ReportFailureCallback = base::Callback<void(bool should_retry)>;
-
-// FeedbackUploaderDelegate is a simple HTTP uploader for a feedback report.
-// When finished, it runs the appropriate callback passed in via the
-// constructor, and then deletes itself.
-class FeedbackUploaderDelegate : public net::URLFetcherDelegate {
- public:
- FeedbackUploaderDelegate(const base::Closure& success_callback,
- const ReportFailureCallback& error_callback);
- ~FeedbackUploaderDelegate() override;
-
- private:
- // Overridden from net::URLFetcherDelegate.
- void OnURLFetchComplete(const net::URLFetcher* source) override;
-
- base::Closure success_callback_;
- ReportFailureCallback error_callback_;
-
- DISALLOW_COPY_AND_ASSIGN(FeedbackUploaderDelegate);
-};
-
-} // namespace feedback
-
-#endif // COMPONENTS_FEEDBACK_FEEDBACK_UPLOADER_DELEGATE_H_
diff --git a/chromium/components/feedback/feedback_uploader_dispatch_unittest.cc b/chromium/components/feedback/feedback_uploader_dispatch_unittest.cc
index e7ca04431d8..41efba55b14 100644
--- a/chromium/components/feedback/feedback_uploader_dispatch_unittest.cc
+++ b/chromium/components/feedback/feedback_uploader_dispatch_unittest.cc
@@ -11,13 +11,16 @@
#include "base/run_loop.h"
#include "base/task_scheduler/post_task.h"
#include "base/task_scheduler/task_traits.h"
+#include "base/test/bind_test_util.h"
#include "components/feedback/feedback_uploader_factory.h"
#include "components/variations/variations_associated_data.h"
#include "components/variations/variations_http_header_provider.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_fetcher_delegate.h"
+#include "net/http/http_util.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_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace feedback {
@@ -27,16 +30,22 @@ namespace {
constexpr base::TimeDelta kTestRetryDelay =
base::TimeDelta::FromMilliseconds(1);
-constexpr int kHttpPostSuccessNoContent = 204;
-constexpr int kHttpPostFailClientError = 400;
-constexpr int kHttpPostFailServerError = 500;
+constexpr char kFeedbackPostUrl[] =
+ "https://www.google.com/tools/feedback/chrome/__submit";
+
+void QueueReport(FeedbackUploader* uploader, const std::string& report_data) {
+ uploader->QueueReport(std::make_unique<std::string>(report_data));
+}
} // namespace
class FeedbackUploaderDispatchTest : public ::testing::Test {
protected:
FeedbackUploaderDispatchTest()
- : browser_thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
+ : browser_thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
+ shared_url_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)) {}
~FeedbackUploaderDispatchTest() override {
// Clean up registered ids.
@@ -54,11 +63,21 @@ class FeedbackUploaderDispatchTest : public ::testing::Test {
base::FieldTrialList::CreateFieldTrial(trial_name, group_name)->group();
}
+ scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory() {
+ return shared_url_loader_factory_;
+ }
+
+ network::TestURLLoaderFactory* test_url_loader_factory() {
+ return &test_url_loader_factory_;
+ }
+
content::BrowserContext* context() { return &context_; }
private:
content::TestBrowserThreadBundle browser_thread_bundle_;
content::TestBrowserContext context_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
DISALLOW_COPY_AND_ASSIGN(FeedbackUploaderDispatchTest);
};
@@ -72,55 +91,90 @@ TEST_F(FeedbackUploaderDispatchTest, VariationHeaders) {
CreateFieldTrialWithId("Test", "Group1", 123);
FeedbackUploader uploader(
- context(), FeedbackUploaderFactory::CreateUploaderTaskRunner());
+ shared_url_loader_factory(), context(),
+ FeedbackUploaderFactory::CreateUploaderTaskRunner());
- net::TestURLFetcherFactory factory;
- uploader.QueueReport("test");
-
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
net::HttpRequestHeaders headers;
- fetcher->GetExtraRequestHeaders(&headers);
+ test_url_loader_factory()->SetInterceptor(
+ base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+ headers = request.headers;
+ }));
+
+ QueueReport(&uploader, "test");
+ base::RunLoop().RunUntilIdle();
+
std::string value;
EXPECT_TRUE(headers.GetHeader("X-Client-Data", &value));
EXPECT_FALSE(value.empty());
- // The fetcher's delegate is responsible for freeing the fetcher (and itself).
- fetcher->delegate()->OnURLFetchComplete(fetcher);
variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
}
-TEST_F(FeedbackUploaderDispatchTest, TestVariousServerResponses) {
+TEST_F(FeedbackUploaderDispatchTest, 204Response) {
FeedbackUploader::SetMinimumRetryDelayForTesting(kTestRetryDelay);
FeedbackUploader uploader(
- context(), FeedbackUploaderFactory::CreateUploaderTaskRunner());
+ shared_url_loader_factory(), context(),
+ FeedbackUploaderFactory::CreateUploaderTaskRunner());
EXPECT_EQ(kTestRetryDelay, uploader.retry_delay());
// Successful reports should not introduce any retries, and should not
// increase the backoff delay.
- net::TestURLFetcherFactory factory;
- factory.set_remove_fetcher_on_delete(true);
- uploader.QueueReport("Successful report");
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
- fetcher->set_response_code(kHttpPostSuccessNoContent);
- fetcher->delegate()->OnURLFetchComplete(fetcher);
+ network::ResourceResponseHead head;
+ std::string headers("HTTP/1.1 204 No Content\n\n");
+ head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
+ net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()));
+ network::URLLoaderCompletionStatus status;
+ test_url_loader_factory()->AddResponse(GURL(kFeedbackPostUrl), head, "",
+ status);
+ QueueReport(&uploader, "Successful report");
+ base::RunLoop().RunUntilIdle();
+
EXPECT_EQ(kTestRetryDelay, uploader.retry_delay());
EXPECT_TRUE(uploader.QueueEmpty());
+}
+TEST_F(FeedbackUploaderDispatchTest, 400Response) {
+ FeedbackUploader::SetMinimumRetryDelayForTesting(kTestRetryDelay);
+ FeedbackUploader uploader(
+ shared_url_loader_factory(), context(),
+ FeedbackUploaderFactory::CreateUploaderTaskRunner());
+
+ EXPECT_EQ(kTestRetryDelay, uploader.retry_delay());
// Failed reports due to client errors are not retried. No backoff delay
// should be doubled.
- uploader.QueueReport("Client error failed report");
- fetcher = factory.GetFetcherByID(0);
- fetcher->set_response_code(kHttpPostFailClientError);
- fetcher->delegate()->OnURLFetchComplete(fetcher);
+ network::ResourceResponseHead head;
+ std::string headers("HTTP/1.1 400 Bad Request\n\n");
+ head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
+ net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()));
+ network::URLLoaderCompletionStatus status;
+ test_url_loader_factory()->AddResponse(GURL(kFeedbackPostUrl), head, "",
+ status);
+ QueueReport(&uploader, "Client error failed report");
+ base::RunLoop().RunUntilIdle();
+
EXPECT_EQ(kTestRetryDelay, uploader.retry_delay());
EXPECT_TRUE(uploader.QueueEmpty());
+}
+TEST_F(FeedbackUploaderDispatchTest, 500Response) {
+ FeedbackUploader::SetMinimumRetryDelayForTesting(kTestRetryDelay);
+ FeedbackUploader uploader(
+ shared_url_loader_factory(), context(),
+ FeedbackUploaderFactory::CreateUploaderTaskRunner());
+
+ EXPECT_EQ(kTestRetryDelay, uploader.retry_delay());
// Failed reports due to server errors are retried.
- uploader.QueueReport("Server error failed report");
- fetcher = factory.GetFetcherByID(0);
- fetcher->set_response_code(kHttpPostFailServerError);
- fetcher->delegate()->OnURLFetchComplete(fetcher);
- EXPECT_EQ(kTestRetryDelay * 2, uploader.retry_delay());
+ network::ResourceResponseHead head;
+ std::string headers("HTTP/1.1 500 Server Error\n\n");
+ head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
+ net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()));
+ network::URLLoaderCompletionStatus status;
+ test_url_loader_factory()->AddResponse(GURL(kFeedbackPostUrl), head, "",
+ status);
+ QueueReport(&uploader, "Server error failed report");
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_LT(kTestRetryDelay, uploader.retry_delay());
EXPECT_FALSE(uploader.QueueEmpty());
}
diff --git a/chromium/components/feedback/feedback_uploader_factory.cc b/chromium/components/feedback/feedback_uploader_factory.cc
index 5445e1dd4e0..f446d033ae1 100644
--- a/chromium/components/feedback/feedback_uploader_factory.cc
+++ b/chromium/components/feedback/feedback_uploader_factory.cc
@@ -10,6 +10,8 @@
#include "base/task_scheduler/task_traits.h"
#include "components/feedback/feedback_uploader.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
namespace feedback {
@@ -51,7 +53,10 @@ FeedbackUploaderFactory::~FeedbackUploaderFactory() {}
KeyedService* FeedbackUploaderFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
- return new FeedbackUploader(context, task_runner_);
+ return new FeedbackUploader(
+ content::BrowserContext::GetDefaultStoragePartition(context)
+ ->GetURLLoaderFactoryForBrowserProcess(),
+ context, task_runner_);
}
content::BrowserContext* FeedbackUploaderFactory::GetBrowserContextToUse(
diff --git a/chromium/components/feedback/feedback_uploader_unittest.cc b/chromium/components/feedback/feedback_uploader_unittest.cc
index d9bf658d3c2..356229e28b5 100644
--- a/chromium/components/feedback/feedback_uploader_unittest.cc
+++ b/chromium/components/feedback/feedback_uploader_unittest.cc
@@ -18,6 +18,9 @@
#include "components/feedback/feedback_uploader_factory.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.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_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace feedback {
@@ -35,8 +38,11 @@ constexpr base::TimeDelta kRetryDelayForTest =
class MockFeedbackUploader : public FeedbackUploader {
public:
- MockFeedbackUploader(content::BrowserContext* context)
- : FeedbackUploader(context,
+ MockFeedbackUploader(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ content::BrowserContext* context)
+ : FeedbackUploader(url_loader_factory,
+ context,
FeedbackUploaderFactory::CreateUploaderTaskRunner()) {}
~MockFeedbackUploader() override {}
@@ -52,8 +58,8 @@ class MockFeedbackUploader : public FeedbackUploader {
FROM_HERE,
base::BindOnce(
&FeedbackReport::LoadReportsAndQueue, feedback_reports_path(),
- base::Bind(&MockFeedbackUploader::QueueSingleReport,
- base::SequencedTaskRunnerHandle::Get(), this)));
+ base::BindRepeating(&MockFeedbackUploader::QueueSingleReport,
+ base::SequencedTaskRunnerHandle::Get(), this)));
}
const std::map<std::string, unsigned int>& dispatched_reports() const {
@@ -66,10 +72,10 @@ class MockFeedbackUploader : public FeedbackUploader {
static void QueueSingleReport(
scoped_refptr<base::SequencedTaskRunner> main_task_runner,
MockFeedbackUploader* uploader,
- const std::string& data) {
+ std::unique_ptr<std::string> data) {
main_task_runner->PostTask(
FROM_HERE, base::BindOnce(&MockFeedbackUploader::QueueReport,
- uploader->AsWeakPtr(), data));
+ uploader->AsWeakPtr(), std::move(data)));
}
// FeedbackUploaderChrome:
@@ -112,22 +118,28 @@ class FeedbackUploaderTest : public testing::Test {
public:
FeedbackUploaderTest() {
FeedbackUploader::SetMinimumRetryDelayForTesting(kRetryDelayForTest);
+ test_shared_loader_factory_ =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_);
RecreateUploader();
}
~FeedbackUploaderTest() override = default;
void RecreateUploader() {
- uploader_ = std::make_unique<MockFeedbackUploader>(&context_);
+ uploader_ = std::make_unique<MockFeedbackUploader>(
+ test_shared_loader_factory_, &context_);
}
void QueueReport(const std::string& data) {
- uploader_->QueueReport(data);
+ uploader_->QueueReport(std::make_unique<std::string>(data));
}
MockFeedbackUploader* uploader() const { return uploader_.get(); }
private:
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
content::TestBrowserThreadBundle test_browser_thread_bundle_;
content::TestBrowserContext context_;
std::unique_ptr<MockFeedbackUploader> uploader_;
diff --git a/chromium/components/flags_ui/resources/flags.html b/chromium/components/flags_ui/resources/flags.html
index a91d6d623e4..6331741ba68 100644
--- a/chromium/components/flags_ui/resources/flags.html
+++ b/chromium/components/flags_ui/resources/flags.html
@@ -205,10 +205,10 @@
<div class="flex">
<if expr="not is_ios">
<button class="experiment-restart-button" type="button" tabindex="9">
-<if expr="not is_chromeos">
+<if expr="not chromeos">
Relaunch Now
</if>
-<if expr="is_chromeos">
+<if expr="chromeos">
Restart Now
</if>
</button>
diff --git a/chromium/components/gcm_driver/BUILD.gn b/chromium/components/gcm_driver/BUILD.gn
index ad729b9c8d4..ac5404b841a 100644
--- a/chromium/components/gcm_driver/BUILD.gn
+++ b/chromium/components/gcm_driver/BUILD.gn
@@ -63,6 +63,8 @@ static_library("gcm_driver") {
"//google_apis",
"//google_apis/gcm",
"//net",
+ "//services/identity/public/cpp",
+ "//services/network/public/cpp",
"//url:url",
]
@@ -135,9 +137,10 @@ static_library("test_support") {
deps = [
"//base",
"//components/gcm_driver/instance_id:test_support",
- "//content/public/browser",
"//google_apis/gcm:test_support",
"//net",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//testing/gtest",
]
@@ -151,8 +154,6 @@ static_library("test_support") {
"fake_gcm_profile_service.cc",
"fake_gcm_profile_service.h",
]
-
- deps -= [ "//content/public/browser" ]
}
if (!use_gcm_from_platform) {
@@ -206,5 +207,7 @@ source_set("unit_tests") {
"//components/signin/core/browser",
"//components/signin/core/browser:test_support",
"//components/sync_preferences:test_support",
+ "//services/identity/public/cpp",
+ "//services/identity/public/cpp:test_support",
]
}
diff --git a/chromium/components/google/core/browser/OWNERS b/chromium/components/google/core/browser/OWNERS
index 741e9e0d60a..991eb9d1e22 100644
--- a/chromium/components/google/core/browser/OWNERS
+++ b/chromium/components/google/core/browser/OWNERS
@@ -1,3 +1,2 @@
-per-file google_tld_list.h=mariakhomenko@chromium.org
per-file google_tld_list.h=msramek@chromium.org
diff --git a/chromium/components/guest_view/browser/guest_view_base.cc b/chromium/components/guest_view/browser/guest_view_base.cc
index 8f31866db03..7b1a30f6189 100644
--- a/chromium/components/guest_view/browser/guest_view_base.cc
+++ b/chromium/components/guest_view/browser/guest_view_base.cc
@@ -187,7 +187,7 @@ GuestViewBase::GuestViewBase(WebContents* owner_web_contents)
GuestViewBase::~GuestViewBase() {}
void GuestViewBase::Init(const base::DictionaryValue& create_params,
- const WebContentsCreatedCallback& callback) {
+ WebContentsCreatedCallback callback) {
if (initialized_)
return;
initialized_ = true;
@@ -196,16 +196,15 @@ void GuestViewBase::Init(const base::DictionaryValue& create_params,
// The derived class did not create a WebContents so this class serves no
// purpose. Let's self-destruct.
delete this;
- callback.Run(nullptr);
+ std::move(callback).Run(nullptr);
return;
}
std::unique_ptr<base::DictionaryValue> params(create_params.DeepCopy());
CreateWebContents(create_params,
- base::Bind(&GuestViewBase::CompleteInit,
- weak_ptr_factory_.GetWeakPtr(),
- base::Passed(&params),
- callback));
+ base::BindOnce(&GuestViewBase::CompleteInit,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Passed(&params), std::move(callback)));
}
void GuestViewBase::InitWithWebContents(
@@ -783,17 +782,17 @@ void GuestViewBase::SendQueuedEvents() {
void GuestViewBase::CompleteInit(
std::unique_ptr<base::DictionaryValue> create_params,
- const WebContentsCreatedCallback& callback,
+ WebContentsCreatedCallback callback,
WebContents* guest_web_contents) {
if (!guest_web_contents) {
// The derived class did not create a WebContents so this class serves no
// purpose. Let's self-destruct.
delete this;
- callback.Run(nullptr);
+ std::move(callback).Run(nullptr);
return;
}
InitWithWebContents(*create_params, guest_web_contents);
- callback.Run(guest_web_contents);
+ std::move(callback).Run(guest_web_contents);
}
double GuestViewBase::GetEmbedderZoomFactor() const {
diff --git a/chromium/components/guest_view/browser/guest_view_base.h b/chromium/components/guest_view/browser/guest_view_base.h
index 32341e9197a..fc5b48c0f9a 100644
--- a/chromium/components/guest_view/browser/guest_view_base.h
+++ b/chromium/components/guest_view/browser/guest_view_base.h
@@ -120,9 +120,9 @@ class GuestViewBase : public content::BrowserPluginGuestDelegate,
// This creates a WebContents and initializes |this| GuestViewBase to use the
// newly created WebContents.
using WebContentsCreatedCallback =
- base::Callback<void(content::WebContents*)>;
+ base::OnceCallback<void(content::WebContents*)>;
void Init(const base::DictionaryValue& create_params,
- const WebContentsCreatedCallback& callback);
+ WebContentsCreatedCallback callback);
void InitWithWebContents(const base::DictionaryValue& create_params,
content::WebContents* guest_web_contents);
@@ -233,9 +233,8 @@ class GuestViewBase : public content::BrowserPluginGuestDelegate,
// Given a set of initialization parameters, a concrete subclass of
// GuestViewBase can create a specialized WebContents that it returns back to
// GuestViewBase.
- virtual void CreateWebContents(
- const base::DictionaryValue& create_params,
- const WebContentsCreatedCallback& callback) = 0;
+ virtual void CreateWebContents(const base::DictionaryValue& create_params,
+ WebContentsCreatedCallback callback) = 0;
// This method is called after the guest has been attached to an embedder and
// suspended resource loads have been resumed.
@@ -386,7 +385,7 @@ class GuestViewBase : public content::BrowserPluginGuestDelegate,
void SendQueuedEvents();
void CompleteInit(std::unique_ptr<base::DictionaryValue> create_params,
- const WebContentsCreatedCallback& callback,
+ WebContentsCreatedCallback callback,
content::WebContents* guest_web_contents);
// Dispatches the onResize event to the embedder.
diff --git a/chromium/components/guest_view/browser/guest_view_manager.cc b/chromium/components/guest_view/browser/guest_view_manager.cc
index a18eacb3b74..0671c3cd7d7 100644
--- a/chromium/components/guest_view/browser/guest_view_manager.cc
+++ b/chromium/components/guest_view/browser/guest_view_manager.cc
@@ -174,13 +174,13 @@ int GuestViewManager::GetNextInstanceID() {
void GuestViewManager::CreateGuest(const std::string& view_type,
content::WebContents* owner_web_contents,
const base::DictionaryValue& create_params,
- const WebContentsCreatedCallback& callback) {
+ WebContentsCreatedCallback callback) {
GuestViewBase* guest = CreateGuestInternal(owner_web_contents, view_type);
if (!guest) {
- callback.Run(nullptr);
+ std::move(callback).Run(nullptr);
return;
}
- guest->Init(create_params, callback);
+ guest->Init(create_params, std::move(callback));
}
content::WebContents* GuestViewManager::CreateGuestWithWebContentsParams(
diff --git a/chromium/components/guest_view/browser/guest_view_manager.h b/chromium/components/guest_view/browser/guest_view_manager.h
index 9045a219f5b..84a8cda92a0 100644
--- a/chromium/components/guest_view/browser/guest_view_manager.h
+++ b/chromium/components/guest_view/browser/guest_view_manager.h
@@ -107,11 +107,11 @@ class GuestViewManager : public content::BrowserPluginGuestManager,
const base::Closure& callback);
using WebContentsCreatedCallback =
- base::Callback<void(content::WebContents*)>;
+ base::OnceCallback<void(content::WebContents*)>;
void CreateGuest(const std::string& view_type,
content::WebContents* owner_web_contents,
const base::DictionaryValue& create_params,
- const WebContentsCreatedCallback& callback);
+ WebContentsCreatedCallback callback);
content::WebContents* CreateGuestWithWebContentsParams(
const std::string& view_type,
diff --git a/chromium/components/heap_profiling/supervisor.cc b/chromium/components/heap_profiling/supervisor.cc
index ed21ae9063f..64b1a9495db 100644
--- a/chromium/components/heap_profiling/supervisor.cc
+++ b/chromium/components/heap_profiling/supervisor.cc
@@ -151,16 +151,25 @@ void Supervisor::RequestTraceWithHeapDump(TraceFinishedCallback callback,
},
std::move(callback));
- memory_instrumentation::MemoryInstrumentation::GetInstance()
- ->RequestGlobalDumpAndAppendToTrace(
- base::trace_event::MemoryDumpType::EXPLICITLY_TRIGGERED,
- base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND,
- base::AdaptCallbackForRepeating(std::move(finished_dump_callback)));
+ auto trigger_memory_dump_callback = base::BindOnce(
+ [](base::OnceCallback<void(bool success, uint64_t dump_guid)>
+ finished_dump_callback) {
+ memory_instrumentation::MemoryInstrumentation::GetInstance()
+ ->RequestGlobalDumpAndAppendToTrace(
+ base::trace_event::MemoryDumpType::EXPLICITLY_TRIGGERED,
+ base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND,
+ base::AdaptCallbackForRepeating(
+ std::move(finished_dump_callback)));
+ },
+ std::move(finished_dump_callback));
// The only reason this should return false is if tracing is already enabled,
// which we've already checked.
+ // Use AdaptCallbackForRepeating since the argument passed to StartTracing()
+ // is intended to be a OnceCallback, but the code has not yet been migrated.
bool result = content::TracingController::GetInstance()->StartTracing(
- GetBackgroundTracingConfig(anonymize), base::Closure());
+ GetBackgroundTracingConfig(anonymize),
+ base::AdaptCallbackForRepeating(std::move(trigger_memory_dump_callback)));
DCHECK(result);
}
diff --git a/chromium/components/heap_profiling/test_driver.cc b/chromium/components/heap_profiling/test_driver.cc
index 7c841bc5fbf..cf978596747 100644
--- a/chromium/components/heap_profiling/test_driver.cc
+++ b/chromium/components/heap_profiling/test_driver.cc
@@ -8,9 +8,11 @@
#include "base/bind.h"
#include "base/command_line.h"
+#include "base/files/file_path.h"
#include "base/json/json_reader.h"
#include "base/process/process_handle.h"
#include "base/run_loop.h"
+#include "base/stl_util.h"
#include "base/task_scheduler/post_task.h"
#include "base/threading/platform_thread.h"
#include "base/trace_event/heap_profiler_event_filter.h"
@@ -76,8 +78,7 @@ bool RenderersAreBeingProfiled(
base::kNullProcessHandle)
continue;
base::ProcessId pid = iter.GetCurrentValue()->GetProcess().Pid();
- if (std::find(profiled_pids.begin(), profiled_pids.end(), pid) !=
- profiled_pids.end()) {
+ if (base::ContainsValue(profiled_pids, pid)) {
return true;
}
}
@@ -518,17 +519,34 @@ bool ValidateProcessMmaps(base::Value* process_mmaps,
bool should_have_contents) {
base::Value* vm_regions = process_mmaps->FindKey("vm_regions");
size_t count = vm_regions->GetList().size();
- if (should_have_contents) {
- if (count == 0) {
- LOG(ERROR) << "vm_regions should have contents, but doesn't";
- return false;
- }
- } else {
+ if (!should_have_contents) {
if (count != 0) {
LOG(ERROR) << "vm_regions should be empty, but has contents";
return false;
}
+ return true;
+ }
+
+ if (count == 0) {
+ LOG(ERROR) << "vm_regions should have contents, but doesn't";
+ return false;
+ }
+
+ // File paths may contain PII. Make sure that "mf" entries only contain the
+ // basename, rather than a full path.
+ for (const base::Value& vm_region : vm_regions->GetList()) {
+ const base::Value* file_path_value = vm_region.FindKey("mf");
+ if (file_path_value) {
+ std::string file_path = file_path_value->GetString();
+
+ base::FilePath::StringType path(file_path.begin(), file_path.end());
+ if (base::FilePath(path).BaseName().AsUTF8Unsafe() != file_path) {
+ LOG(ERROR) << "vm_region should not contain file path: " << file_path;
+ return false;
+ }
+ }
}
+
return true;
}
@@ -830,7 +848,7 @@ void TestDriver::CollectResults(bool synchronous) {
Supervisor::GetInstance()->RequestTraceWithHeapDump(
base::Bind(&TestDriver::TraceFinished, base::Unretained(this),
std::move(finish_tracing_closure)),
- false /* strip_path_from_mapped_files */);
+ /* anonymize= */ true);
if (synchronous)
run_loop->Run();
@@ -1029,6 +1047,11 @@ void TestDriver::WaitForProfilingToStartForAllRenderersUIThreadCallback(
wait_for_ui_thread_.Signal();
return;
}
+
+ // Brief sleep to prevent spamming the task queue, since this code is called
+ // in a tight loop.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(100));
+
WaitForProfilingToStartForAllRenderersUIThreadAndSignal();
}
diff --git a/chromium/components/history/DEPS b/chromium/components/history/DEPS
index d73616b400e..2f786696509 100644
--- a/chromium/components/history/DEPS
+++ b/chromium/components/history/DEPS
@@ -6,6 +6,9 @@ include_rules = [
"+components/sync",
"+google_apis/gaia",
"+net",
+ "+services/network/public/cpp",
+ "+services/network/public/mojom",
+ "+services/network/test",
"+sql",
"+third_party/skia",
"+third_party/sqlite",
diff --git a/chromium/components/history/content/browser/content_history_backend_db_unittest.cc b/chromium/components/history/content/browser/content_history_backend_db_unittest.cc
index ecac62e4034..d189ba01f35 100644
--- a/chromium/components/history/content/browser/content_history_backend_db_unittest.cc
+++ b/chromium/components/history/content/browser/content_history_backend_db_unittest.cc
@@ -77,6 +77,7 @@ const InterruptReasonAssociation historical_reasons[] = {
{"SERVER_FORBIDDEN", 36},
{"SERVER_UNREACHABLE", 37},
{"SERVER_CONTENT_LENGTH_MISMATCH", 38},
+ {"SERVER_CROSS_ORIGIN_REDIRECT", 39},
{"USER_CANCELED", 40},
{"USER_SHUTDOWN", 41},
{"CRASH", 50},
diff --git a/chromium/components/history/core/browser/BUILD.gn b/chromium/components/history/core/browser/BUILD.gn
index 10fd62ac60e..c4619797a38 100644
--- a/chromium/components/history/core/browser/BUILD.gn
+++ b/chromium/components/history/core/browser/BUILD.gn
@@ -117,6 +117,7 @@ static_library("browser") {
"//google_apis",
"//net",
"//services/identity/public/cpp",
+ "//services/network/public/cpp",
"//sql",
"//third_party/sqlite",
"//ui/base",
@@ -175,6 +176,7 @@ bundle_data("unit_tests_bundle_data") {
"//components/test/data/history/history.32.sql",
"//components/test/data/history/history.38.sql",
"//components/test/data/history/history.39.sql",
+ "//components/test/data/history/history.40.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",
@@ -230,6 +232,8 @@ source_set("unit_tests") {
"//components/sync:test_support_driver",
"//components/sync:test_support_model",
"//net:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//sql",
"//sql:test_support",
"//testing/gtest",
diff --git a/chromium/components/history/core/browser/browsing_history_service.cc b/chromium/components/history/core/browser/browsing_history_service.cc
index 153dad43e1d..3221b84f1ab 100644
--- a/chromium/components/history/core/browser/browsing_history_service.cc
+++ b/chromium/components/history/core/browser/browsing_history_service.cc
@@ -161,7 +161,7 @@ BrowsingHistoryService::BrowsingHistoryService(
BrowsingHistoryDriver* driver,
HistoryService* local_history,
syncer::SyncService* sync_service,
- std::unique_ptr<base::Timer> web_history_timer)
+ std::unique_ptr<base::OneShotTimer> web_history_timer)
: web_history_timer_(std::move(web_history_timer)),
history_service_observer_(this),
web_history_service_observer_(this),
diff --git a/chromium/components/history/core/browser/browsing_history_service.h b/chromium/components/history/core/browser/browsing_history_service.h
index b768c8a4744..b35de252654 100644
--- a/chromium/components/history/core/browser/browsing_history_service.h
+++ b/chromium/components/history/core/browser/browsing_history_service.h
@@ -148,7 +148,7 @@ class BrowsingHistoryService : public HistoryServiceObserver,
BrowsingHistoryService(BrowsingHistoryDriver* driver,
HistoryService* local_history,
syncer::SyncService* sync_service,
- std::unique_ptr<base::Timer> web_history_timer);
+ std::unique_ptr<base::OneShotTimer> web_history_timer);
private:
FRIEND_TEST_ALL_PREFIXES(::BrowsingHistoryHandlerTest,
@@ -224,7 +224,7 @@ class BrowsingHistoryService : public HistoryServiceObserver,
std::set<GURL> urls_to_be_deleted_;
// Timer used to implement a timeout on a Web History response.
- std::unique_ptr<base::Timer> web_history_timer_;
+ std::unique_ptr<base::OneShotTimer> web_history_timer_;
// HistoryService (local history) observer.
ScopedObserver<HistoryService, HistoryServiceObserver>
diff --git a/chromium/components/history/core/browser/browsing_history_service_unittest.cc b/chromium/components/history/core/browser/browsing_history_service_unittest.cc
index ec292a15908..c7724c9b42e 100644
--- a/chromium/components/history/core/browser/browsing_history_service_unittest.cc
+++ b/chromium/components/history/core/browser/browsing_history_service_unittest.cc
@@ -120,8 +120,7 @@ class TestBrowsingHistoryDriver : public BrowsingHistoryDriver {
class TestWebHistoryService : public FakeWebHistoryService {
public:
- TestWebHistoryService()
- : FakeWebHistoryService(scoped_refptr<net::URLRequestContextGetter>()) {}
+ TestWebHistoryService() : FakeWebHistoryService() {}
void TriggerOnWebHistoryDeleted() {
TestRequest request;
@@ -175,7 +174,7 @@ class TestBrowsingHistoryService : public BrowsingHistoryService {
TestBrowsingHistoryService(BrowsingHistoryDriver* driver,
HistoryService* local_history,
syncer::SyncService* sync_service,
- std::unique_ptr<base::Timer> timer)
+ std::unique_ptr<base::OneShotTimer> timer)
: BrowsingHistoryService(driver,
local_history,
sync_service,
@@ -201,8 +200,8 @@ class BrowsingHistoryServiceTest : public ::testing::Test {
void ResetService(BrowsingHistoryDriver* driver,
HistoryService* local_history,
syncer::SyncService* sync_service) {
- std::unique_ptr<base::MockTimer> timer =
- std::make_unique<base::MockTimer>(false, false);
+ std::unique_ptr<base::MockOneShotTimer> timer =
+ std::make_unique<base::MockOneShotTimer>();
timer_ = timer.get();
browsing_history_service_ = std::make_unique<TestBrowsingHistoryService>(
driver, local_history, sync_service, std::move(timer));
@@ -286,7 +285,7 @@ class BrowsingHistoryServiceTest : public ::testing::Test {
TestWebHistoryService* web_history() { return &web_history_; }
TestSyncService* sync() { return &sync_service_; }
TestBrowsingHistoryDriver* driver() { return &driver_; }
- base::MockTimer* timer() { return timer_; }
+ base::MockOneShotTimer* timer() { return timer_; }
TestBrowsingHistoryService* service() {
return browsing_history_service_.get();
}
@@ -303,7 +302,7 @@ class BrowsingHistoryServiceTest : public ::testing::Test {
TestWebHistoryService web_history_;
TestSyncService sync_service_;
TestBrowsingHistoryDriver driver_;
- base::MockTimer* timer_;
+ base::MockOneShotTimer* timer_;
std::unique_ptr<TestBrowsingHistoryService> browsing_history_service_;
};
diff --git a/chromium/components/history/core/browser/domain_mixing_metrics_unittest.cc b/chromium/components/history/core/browser/domain_mixing_metrics_unittest.cc
index b03bdbf4aec..93088579cfb 100644
--- a/chromium/components/history/core/browser/domain_mixing_metrics_unittest.cc
+++ b/chromium/components/history/core/browser/domain_mixing_metrics_unittest.cc
@@ -8,7 +8,7 @@
#include <vector>
#include "base/bind.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace history {
@@ -86,4 +86,4 @@ TEST_F(DomainMixingMetricsTest, WithInactiveDays) {
tester_.ExpectBucketCount("DomainMixing.OneMonth", 50, 1); // Day 3.
}
-} // namespace history \ No newline at end of file
+} // namespace history
diff --git a/chromium/components/history/core/browser/history_backend.cc b/chromium/components/history/core/browser/history_backend.cc
index 478d3985a87..0bfa6b960c6 100644
--- a/chromium/components/history/core/browser/history_backend.cc
+++ b/chromium/components/history/core/browser/history_backend.cc
@@ -41,6 +41,8 @@
#include "components/history/core/browser/page_usage_data.h"
#include "components/history/core/browser/url_utils.h"
#include "components/sync/model_impl/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 "third_party/skia/include/core/SkBitmap.h"
@@ -119,6 +121,17 @@ bool AreIconTypesEquivalent(favicon_base::IconType type_a,
} // namespace
+base::string16 FormatUrlForRedirectComparison(const GURL& url) {
+ url::Replacements<char> remove_port;
+ remove_port.ClearPort();
+ return url_formatter::FormatUrl(
+ url.ReplaceComponents(remove_port),
+ url_formatter::kFormatUrlOmitHTTP | url_formatter::kFormatUrlOmitHTTPS |
+ url_formatter::kFormatUrlOmitUsernamePassword |
+ url_formatter::kFormatUrlOmitTrivialSubdomains,
+ net::UnescapeRule::NONE, nullptr, nullptr, nullptr);
+}
+
QueuedHistoryDBTask::QueuedHistoryDBTask(
std::unique_ptr<HistoryDBTask> task,
scoped_refptr<base::SingleThreadTaskRunner> origin_loop,
@@ -506,7 +519,8 @@ void HistoryBackend::AddPage(const HistoryAddPageArgs& request) {
// 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);
+ request.hidden, request.visit_source,
+ IsTypedIncrement(t), request.title);
// Update the segment for this visit. KEYWORD_GENERATED visits should not
// result in changing most visited, so we don't update segments (most
@@ -577,6 +591,21 @@ void HistoryBackend::AddPage(const HistoryAddPageArgs& request) {
}
}
+ bool transfer_typed_credit_from_first_to_second_url = false;
+ if (redirects.size() > 1) {
+ // Check if the first redirect is the same as the original URL but
+ // upgraded to HTTPS. This ignores the port numbers (in case of
+ // non-standard HTTP or HTTPS ports) and trivial subdomains (e.g., "www."
+ // or "m.").
+ if (IsTypedIncrement(request_transition) &&
+ redirects[0].SchemeIs(url::kHttpScheme) &&
+ redirects[1].SchemeIs(url::kHttpsScheme) &&
+ FormatUrlForRedirectComparison(redirects[0]) ==
+ FormatUrlForRedirectComparison(redirects[1])) {
+ transfer_typed_credit_from_first_to_second_url = true;
+ }
+ }
+
for (size_t redirect_index = 0; redirect_index < redirects.size();
redirect_index++) {
ui::PageTransition t = ui::PageTransitionFromInt(
@@ -587,12 +616,21 @@ void HistoryBackend::AddPage(const HistoryAddPageArgs& request) {
t = ui::PageTransitionFromInt(t | ui::PAGE_TRANSITION_CHAIN_END);
}
+ bool should_increment_typed_count = IsTypedIncrement(t);
+ if (transfer_typed_credit_from_first_to_second_url) {
+ if (redirect_index == 0)
+ should_increment_typed_count = false;
+ else if (redirect_index == 1)
+ should_increment_typed_count = true;
+ }
+
// Record all redirect visits with the same timestamp. We don't display
// them anyway, and if we ever decide to, we can reconstruct their order
// from the redirect chain.
last_ids =
AddPageVisit(redirects[redirect_index], request.time, last_ids.second,
- t, request.hidden, request.visit_source);
+ t, request.hidden, request.visit_source,
+ should_increment_typed_count, request.title);
if (t & ui::PAGE_TRANSITION_CHAIN_START) {
if (request.consider_for_ntp_most_visited) {
@@ -788,9 +826,9 @@ std::pair<URLID, VisitID> HistoryBackend::AddPageVisit(
VisitID referring_visit,
ui::PageTransition transition,
bool hidden,
- VisitSource visit_source) {
- const bool typed_increment = IsTypedIncrement(transition);
-
+ VisitSource visit_source,
+ bool should_increment_typed_count,
+ base::Optional<base::string16> title) {
if (!host_ranks_.empty() && visit_source == SOURCE_BROWSED &&
(transition & ui::PAGE_TRANSITION_CHAIN_END)) {
RecordTopHostsMetrics(url);
@@ -803,10 +841,12 @@ std::pair<URLID, VisitID> HistoryBackend::AddPageVisit(
// Update of an existing row.
if (!ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD))
url_info.set_visit_count(url_info.visit_count() + 1);
- if (typed_increment)
+ if (should_increment_typed_count)
url_info.set_typed_count(url_info.typed_count() + 1);
if (url_info.last_visit() < time)
url_info.set_last_visit(time);
+ if (title)
+ url_info.set_title(title.value());
// Only allow un-hiding of pages, never hiding.
if (!hidden)
@@ -816,8 +856,10 @@ std::pair<URLID, VisitID> HistoryBackend::AddPageVisit(
} else {
// Addition of a new row.
url_info.set_visit_count(1);
- url_info.set_typed_count(typed_increment ? 1 : 0);
+ url_info.set_typed_count(should_increment_typed_count ? 1 : 0);
url_info.set_last_visit(time);
+ if (title)
+ url_info.set_title(title.value());
url_info.set_hidden(hidden);
url_id = db_->AddURL(url_info);
@@ -830,7 +872,7 @@ std::pair<URLID, VisitID> HistoryBackend::AddPageVisit(
// Add the visit with the time to the database.
VisitRow visit_info(url_id, time, referring_visit, transition, 0,
- typed_increment);
+ should_increment_typed_count);
VisitID visit_id = db_->AddVisit(&visit_info, visit_source);
if (visit_info.visit_time < first_recorded_time_)
@@ -1038,7 +1080,7 @@ bool HistoryBackend::AddVisits(const GURL& url,
visit != visits.end(); ++visit) {
if (!AddPageVisit(url, visit->first, 0, visit->second,
!ui::PageTransitionIsMainFrame(visit->second),
- visit_source)
+ visit_source, IsTypedIncrement(visit->second))
.first) {
return false;
}
diff --git a/chromium/components/history/core/browser/history_backend.h b/chromium/components/history/core/browser/history_backend.h
index be693853337..80fcd569c30 100644
--- a/chromium/components/history/core/browser/history_backend.h
+++ b/chromium/components/history/core/browser/history_backend.h
@@ -23,6 +23,7 @@
#include "base/macros.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/observer_list.h"
+#include "base/optional.h"
#include "base/single_thread_task_runner.h"
#include "base/supports_user_data.h"
#include "base/task/cancelable_task_tracker.h"
@@ -61,6 +62,10 @@ class URLDatabase;
// the thumbnail database.
static const size_t kMaxFaviconBitmapsPerIconURL = 8;
+// Returns a formatted version of |url| with the HTTP/HTTPS scheme, port,
+// username/password, and any trivial subdomains (e.g., "www.", "m.") removed.
+base::string16 FormatUrlForRedirectComparison(const GURL& url);
+
// Keeps track of a queued HistoryDBTask. This class lives solely on the
// DB thread.
class QueuedHistoryDBTask {
@@ -625,12 +630,15 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>,
//
// This does not schedule database commits, it is intended to be used as a
// subroutine for AddPage only. It also assumes the database is valid.
- std::pair<URLID, VisitID> AddPageVisit(const GURL& url,
- base::Time time,
- VisitID referring_visit,
- ui::PageTransition transition,
- bool hidden,
- VisitSource visit_source);
+ std::pair<URLID, VisitID> AddPageVisit(
+ const GURL& url,
+ base::Time time,
+ VisitID referring_visit,
+ ui::PageTransition transition,
+ bool hidden,
+ VisitSource visit_source,
+ bool should_increment_typed_count,
+ base::Optional<base::string16> title = base::nullopt);
// Returns a redirect chain in |redirects| for the VisitID
// |cur_visit|. |cur_visit| is assumed to be valid. Assumes that
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 9f3ac0fad31..c09dabe205f 100644
--- a/chromium/components/history/core/browser/history_backend_db_unittest.cc
+++ b/chromium/components/history/core/browser/history_backend_db_unittest.cc
@@ -1698,6 +1698,118 @@ TEST_F(HistoryBackendDBTest,
EXPECT_FALSE(visit_row.incremented_omnibox_typed_score);
}
+// Test to verify the left-over typed_url sync metadata gets cleared correctly
+// during migration to version 41.
+TEST_F(HistoryBackendDBTest, MigrateTypedURLLeftoverMetadata) {
+ ASSERT_NO_FATAL_FAILURE(CreateDBVersion(40));
+
+ // Define common uninteresting data for visits.
+ const VisitID referring_visit = 0;
+ const ui::PageTransition transition = ui::PAGE_TRANSITION_TYPED;
+ const base::Time visit_time(base::Time::Now());
+ const base::TimeDelta visit_duration(base::TimeDelta::FromSeconds(30));
+
+ // The first visit has both a DB entry and a metadata entry.
+ const VisitID visit_id1 = 1;
+ const URLID url_id1 = 10;
+ const SegmentID segment_id1 = 20;
+ const std::string metadata_value1 = "BLOB1";
+
+ // The second one as well has both a DB entry and a metadata entry.
+ const VisitID visit_id2 = 2;
+ const URLID url_id2 = 11;
+ const SegmentID segment_id2 = 21;
+ const std::string metadata_value2 = "BLOB2";
+
+ // The second visit has only a left-over metadata entry.
+ const URLID url_id3 = 12;
+ const std::string metadata_value3 = "BLOB3";
+
+ {
+ // Open the db for manual manipulation.
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+
+ const char kInsertVisitStatement[] =
+ "INSERT INTO visits "
+ "(id, url, visit_time, from_visit, transition, segment_id, "
+ "visit_duration) VALUES (?, ?, ?, ?, ?, ?, ?)";
+ {
+ sql::Statement s(db.GetUniqueStatement(kInsertVisitStatement));
+ s.BindInt64(0, visit_id1);
+ s.BindInt64(1, url_id1);
+ s.BindInt64(2, visit_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+ s.BindInt64(3, referring_visit);
+ s.BindInt64(4, transition);
+ s.BindInt64(5, segment_id1);
+ s.BindInt64(6, visit_duration.InMicroseconds());
+ ASSERT_TRUE(s.Run());
+ }
+ {
+ sql::Statement s(db.GetUniqueStatement(kInsertVisitStatement));
+ s.BindInt64(0, visit_id2);
+ s.BindInt64(1, url_id2);
+ s.BindInt64(2, visit_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+ s.BindInt64(3, referring_visit);
+ s.BindInt64(4, transition);
+ s.BindInt64(5, segment_id2);
+ s.BindInt64(6, visit_duration.InMicroseconds());
+ ASSERT_TRUE(s.Run());
+ }
+
+ const char kInsertMetadataStatement[] =
+ "INSERT INTO typed_url_sync_metadata (storage_key, value) VALUES (?, "
+ "?)";
+ {
+ sql::Statement s(db.GetUniqueStatement(kInsertMetadataStatement));
+ s.BindInt64(0, url_id3);
+ s.BindString(1, metadata_value3);
+ ASSERT_TRUE(s.Run());
+ }
+ {
+ sql::Statement s(db.GetUniqueStatement(kInsertMetadataStatement));
+ s.BindInt64(0, url_id2);
+ s.BindString(1, metadata_value2);
+ ASSERT_TRUE(s.Run());
+ }
+ {
+ sql::Statement s(db.GetUniqueStatement(kInsertMetadataStatement));
+ s.BindInt64(0, url_id1);
+ s.BindString(1, metadata_value1);
+ ASSERT_TRUE(s.Run());
+ }
+ }
+
+ // Re-open the db, triggering migration.
+ CreateBackendAndDatabase();
+ DeleteBackend();
+ {
+ // Re-open the db for manual manipulation.
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+ {
+ // The version should have been updated.
+ sql::Statement s(db.GetUniqueStatement(
+ "SELECT value FROM meta WHERE key = 'version'"));
+ ASSERT_GE(HistoryDatabase::GetCurrentVersion(), 41);
+ EXPECT_TRUE(s.Step());
+ EXPECT_EQ(HistoryDatabase::GetCurrentVersion(), s.ColumnInt(0));
+ }
+ {
+ // Check that the left-over metadata entry is deleted.
+ sql::Statement s(db.GetUniqueStatement(
+ "SELECT storage_key FROM typed_url_sync_metadata"));
+ std::set<URLID> remaining_metadata;
+ while (s.Step()) {
+ remaining_metadata.insert(s.ColumnInt64(0));
+ }
+ EXPECT_EQ(remaining_metadata.count(url_id3), 0u);
+ EXPECT_EQ(remaining_metadata.count(url_id2), 1u);
+ EXPECT_EQ(remaining_metadata.count(url_id1), 1u);
+ }
+ }
+}
+
bool FilterURL(const GURL& url) {
return url.SchemeIsHTTPOrHTTPS();
}
diff --git a/chromium/components/history/core/browser/history_backend_unittest.cc b/chromium/components/history/core/browser/history_backend_unittest.cc
index 93859a28f9b..4d58f7c2273 100644
--- a/chromium/components/history/core/browser/history_backend_unittest.cc
+++ b/chromium/components/history/core/browser/history_backend_unittest.cc
@@ -29,7 +29,7 @@
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
@@ -1232,11 +1232,8 @@ TEST_F(HistoryBackendTest, StripUsernamePasswordTest) {
backend_->DeleteAllHistory();
// Visit the url with username, password.
- backend_->AddPageVisit(
- url, base::Time::Now(), 0,
- ui::PageTransitionFromInt(
- ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)),
- false, history::SOURCE_BROWSED);
+ backend_->AddPageVisit(url, base::Time::Now(), 0, ui::PAGE_TRANSITION_TYPED,
+ false, history::SOURCE_BROWSED, true);
// Fetch the row information about stripped url from history db.
VisitVector visits;
@@ -1257,7 +1254,7 @@ TEST_F(HistoryBackendTest, AddPageVisitBackForward) {
// Visit the url after typing it.
backend_->AddPageVisit(url, base::Time::Now(), 0, ui::PAGE_TRANSITION_TYPED,
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, true);
// Ensure both the typed count and visit count are 1.
VisitVector visits;
@@ -1272,7 +1269,7 @@ TEST_F(HistoryBackendTest, AddPageVisitBackForward) {
url, base::Time::Now(), 0,
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
ui::PAGE_TRANSITION_FORWARD_BACK),
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, false);
// Ensure the typed count is still 1 but the visit count is 2.
id = backend_->db()->GetRowForURL(url, &row);
@@ -1292,12 +1289,12 @@ TEST_F(HistoryBackendTest, AddPageVisitRedirectBackForward) {
// Visit a typed URL with a redirect.
backend_->AddPageVisit(url1, base::Time::Now(), 0, ui::PAGE_TRANSITION_TYPED,
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, true);
backend_->AddPageVisit(
url2, base::Time::Now(), 0,
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
ui::PAGE_TRANSITION_CLIENT_REDIRECT),
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, false);
// Ensure the redirected URL does not count as typed.
VisitVector visits;
@@ -1313,7 +1310,7 @@ TEST_F(HistoryBackendTest, AddPageVisitRedirectBackForward) {
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
ui::PAGE_TRANSITION_FORWARD_BACK |
ui::PAGE_TRANSITION_CLIENT_REDIRECT),
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, false);
// Ensure the typed count is still 1 but the visit count is 2.
id = backend_->db()->GetRowForURL(url2, &row);
@@ -1332,13 +1329,13 @@ TEST_F(HistoryBackendTest, AddPageVisitSource) {
// Assume visiting the url from an externsion.
backend_->AddPageVisit(url, base::Time::Now(), 0, ui::PAGE_TRANSITION_TYPED,
- false, history::SOURCE_EXTENSION);
+ false, history::SOURCE_EXTENSION, true);
// Assume the url is imported from Firefox.
backend_->AddPageVisit(url, base::Time::Now(), 0, ui::PAGE_TRANSITION_TYPED,
- false, history::SOURCE_FIREFOX_IMPORTED);
+ false, history::SOURCE_FIREFOX_IMPORTED, true);
// Assume this url is also synced.
backend_->AddPageVisit(url, base::Time::Now(), 0, ui::PAGE_TRANSITION_TYPED,
- false, history::SOURCE_SYNCED);
+ false, history::SOURCE_SYNCED, true);
// Fetch the row information about the url from history db.
VisitVector visits;
@@ -1383,19 +1380,13 @@ TEST_F(HistoryBackendTest, AddPageVisitNotLastVisit) {
base::Time older_time = recent_time - visit_age;
// Visit the url with recent time.
- backend_->AddPageVisit(
- url, recent_time, 0,
- ui::PageTransitionFromInt(
- ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)),
- false, history::SOURCE_BROWSED);
+ backend_->AddPageVisit(url, recent_time, 0, ui::PAGE_TRANSITION_TYPED, false,
+ history::SOURCE_BROWSED, true);
// Add to the url a visit with older time (could be syncing from another
// client, etc.).
- backend_->AddPageVisit(
- url, older_time, 0,
- ui::PageTransitionFromInt(
- ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)),
- false, history::SOURCE_SYNCED);
+ backend_->AddPageVisit(url, older_time, 0, ui::PAGE_TRANSITION_TYPED, false,
+ history::SOURCE_SYNCED, true);
// Fetch the row information about url from history db.
VisitVector visits;
@@ -1421,11 +1412,11 @@ TEST_F(HistoryBackendTest, AddPageVisitFiresNotificationWithCorrectDetails) {
// Visit two distinct URLs, the second one twice.
backend_->AddPageVisit(url1, base::Time::Now(), 0, ui::PAGE_TRANSITION_LINK,
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, false);
for (int i = 0; i < 2; ++i) {
backend_->AddPageVisit(url2, base::Time::Now(), 0,
ui::PAGE_TRANSITION_TYPED, false,
- history::SOURCE_BROWSED);
+ history::SOURCE_BROWSED, true);
}
URLRow stored_row1, stored_row2;
@@ -3266,7 +3257,7 @@ TEST_F(HistoryBackendTest, TopHosts) {
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
ui::PAGE_TRANSITION_CHAIN_START |
ui::PAGE_TRANSITION_CHAIN_END),
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, false);
}
EXPECT_THAT(backend_->TopHosts(3),
@@ -3285,7 +3276,7 @@ TEST_F(HistoryBackendTest, TopHosts_ElidePortAndScheme) {
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
ui::PAGE_TRANSITION_CHAIN_START |
ui::PAGE_TRANSITION_CHAIN_END),
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, false);
}
EXPECT_THAT(backend_->TopHosts(3), ElementsAre(std::make_pair("cnn.com", 3)));
@@ -3302,7 +3293,7 @@ TEST_F(HistoryBackendTest, TopHosts_ElideWWW) {
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
ui::PAGE_TRANSITION_CHAIN_START |
ui::PAGE_TRANSITION_CHAIN_END),
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, false);
}
EXPECT_THAT(backend_->TopHosts(3),
@@ -3321,7 +3312,7 @@ TEST_F(HistoryBackendTest, TopHosts_OnlyLast30Days) {
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
ui::PAGE_TRANSITION_CHAIN_START |
ui::PAGE_TRANSITION_CHAIN_END),
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, false);
}
backend_->AddPageVisit(
GURL("http://www.oracle.com/"),
@@ -3329,7 +3320,7 @@ TEST_F(HistoryBackendTest, TopHosts_OnlyLast30Days) {
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
ui::PAGE_TRANSITION_CHAIN_START |
ui::PAGE_TRANSITION_CHAIN_END),
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, false);
EXPECT_THAT(backend_->TopHosts(3),
ElementsAre(std::make_pair("cnn.com", 2),
@@ -3350,7 +3341,7 @@ TEST_F(HistoryBackendTest, TopHosts_MaxNumHosts) {
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
ui::PAGE_TRANSITION_CHAIN_START |
ui::PAGE_TRANSITION_CHAIN_END),
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, false);
}
EXPECT_THAT(backend_->TopHosts(2),
@@ -3375,7 +3366,7 @@ TEST_F(HistoryBackendTest, TopHosts_IgnoreUnusualURLs) {
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
ui::PAGE_TRANSITION_CHAIN_START |
ui::PAGE_TRANSITION_CHAIN_END),
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, false);
}
EXPECT_THAT(backend_->TopHosts(5), ElementsAre(std::make_pair("cnn.com", 3)));
@@ -3405,7 +3396,7 @@ TEST_F(HistoryBackendTest, HostRankIfAvailable) {
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
ui::PAGE_TRANSITION_CHAIN_START |
ui::PAGE_TRANSITION_CHAIN_END),
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, false);
}
EXPECT_EQ(kMaxTopHosts,
@@ -3433,7 +3424,7 @@ TEST_F(HistoryBackendTest, RecordTopHostsMetrics) {
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
ui::PAGE_TRANSITION_CHAIN_START |
ui::PAGE_TRANSITION_CHAIN_END),
- false, history::SOURCE_BROWSED);
+ false, history::SOURCE_BROWSED, false);
}
// Compute host_ranks_ for RecordTopHostsMetrics.
@@ -3448,7 +3439,7 @@ TEST_F(HistoryBackendTest, RecordTopHostsMetrics) {
for (const GURL& url : urls) {
backend_->AddPageVisit(url, base::Time::Now(), 0,
ui::PAGE_TRANSITION_CHAIN_END, false,
- history::SOURCE_BROWSED);
+ history::SOURCE_BROWSED, false);
}
EXPECT_THAT(histogram.GetAllSamples("History.TopHostsVisitsByRank"),
@@ -3463,22 +3454,22 @@ TEST_F(HistoryBackendTest, GetCountsAndLastVisitForOrigins) {
backend_->AddPageVisit(GURL("http://cnn.com/intl"), yesterday, 0,
ui::PAGE_TRANSITION_LINK, false,
- history::SOURCE_BROWSED);
+ history::SOURCE_BROWSED, false);
backend_->AddPageVisit(GURL("http://cnn.com/us"), last_week, 0,
ui::PAGE_TRANSITION_LINK, false,
- history::SOURCE_BROWSED);
+ history::SOURCE_BROWSED, false);
backend_->AddPageVisit(GURL("http://cnn.com/ny"), now, 0,
ui::PAGE_TRANSITION_LINK, false,
- history::SOURCE_BROWSED);
+ history::SOURCE_BROWSED, false);
backend_->AddPageVisit(GURL("https://cnn.com/intl"), yesterday, 0,
ui::PAGE_TRANSITION_LINK, false,
- history::SOURCE_BROWSED);
+ history::SOURCE_BROWSED, false);
backend_->AddPageVisit(GURL("http://cnn.com:8080/path"), yesterday, 0,
ui::PAGE_TRANSITION_LINK, false,
- history::SOURCE_BROWSED);
+ history::SOURCE_BROWSED, false);
backend_->AddPageVisit(GURL("http://dogtopia.com/pups?q=poods"), now, 0,
ui::PAGE_TRANSITION_LINK, false,
- history::SOURCE_BROWSED);
+ history::SOURCE_BROWSED, false);
std::set<GURL> origins;
origins.insert(GURL("http://cnn.com/"));
@@ -3492,7 +3483,7 @@ TEST_F(HistoryBackendTest, GetCountsAndLastVisitForOrigins) {
origins.insert(GURL("http://notpresent.com/"));
backend_->AddPageVisit(GURL("http://cnn.com/"), tomorrow, 0,
ui::PAGE_TRANSITION_LINK, false,
- history::SOURCE_BROWSED);
+ history::SOURCE_BROWSED, false);
EXPECT_THAT(
backend_->GetCountsAndLastVisitForOrigins(origins),
@@ -3880,6 +3871,128 @@ TEST_F(HistoryBackendTest, DatabaseErrorSynchronouslyKillAndNotifyBridge) {
/*end_time=*/base::Time::Max());
}
+// Tests that a typed navigation which results in a redirect from HTTP to HTTPS
+// will cause the HTTPS URL to accrue the typed count, and the HTTP URL to not.
+TEST_F(HistoryBackendTest, RedirectScoring) {
+ // Non-typed navigations should not increase the count for either.
+ const char* redirect1[] = {"http://foo1.com/page1.html",
+ "https://foo1.com/page1.html", nullptr};
+ AddRedirectChainWithTransitionAndTime(redirect1, 0, ui::PAGE_TRANSITION_LINK,
+ base::Time::Now());
+ URLRow url_row;
+ ASSERT_TRUE(backend_->GetURL(GURL("http://foo1.com/page1.html"), &url_row));
+ EXPECT_EQ(0, url_row.typed_count());
+ ASSERT_TRUE(backend_->GetURL(GURL("https://foo1.com/page1.html"), &url_row));
+ EXPECT_EQ(0, url_row.typed_count());
+
+ // Typed navigation with a redirect from HTTP to HTTPS should count for the
+ // HTTPS URL.
+ AddRedirectChainWithTransitionAndTime(redirect1, 1, ui::PAGE_TRANSITION_TYPED,
+ base::Time::Now());
+ ASSERT_TRUE(backend_->GetURL(GURL("http://foo1.com/page1.html"), &url_row));
+ EXPECT_EQ(0, url_row.typed_count());
+ ASSERT_TRUE(backend_->GetURL(GURL("https://foo1.com/page1.html"), &url_row));
+ EXPECT_EQ(1, url_row.typed_count());
+
+ // The HTTPS URL should accrue the typed count, even if it adds a trivial
+ // subdomain.
+ const char* redirect2[] = {"http://foo2.com", "https://www.foo2.com",
+ nullptr};
+ AddRedirectChainWithTransitionAndTime(redirect2, 2, ui::PAGE_TRANSITION_TYPED,
+ base::Time::Now());
+ ASSERT_TRUE(backend_->GetURL(GURL("http://foo2.com"), &url_row));
+ EXPECT_EQ(0, url_row.typed_count());
+ ASSERT_TRUE(backend_->GetURL(GURL("https://www.foo2.com"), &url_row));
+ EXPECT_EQ(1, url_row.typed_count());
+
+ // The HTTPS URL should accrue the typed count, even if it removes a trivial
+ // subdomain.
+ const char* redirect3[] = {"http://m.foo3.com", "https://foo3.com", nullptr};
+ AddRedirectChainWithTransitionAndTime(redirect3, 3, ui::PAGE_TRANSITION_TYPED,
+ base::Time::Now());
+ ASSERT_TRUE(backend_->GetURL(GURL("http://m.foo3.com"), &url_row));
+ EXPECT_EQ(0, url_row.typed_count());
+ ASSERT_TRUE(backend_->GetURL(GURL("https://foo3.com"), &url_row));
+ EXPECT_EQ(1, url_row.typed_count());
+
+ // A typed navigation redirecting to a different URL (not simply HTTP to HTTPS
+ // with trivial subdomain changes) should have the first URL accrue the typed
+ // count, not the second.
+ const char* redirect4[] = {"http://foo4.com", "https://foo4.com/page1.html",
+ nullptr};
+ AddRedirectChainWithTransitionAndTime(redirect4, 4, ui::PAGE_TRANSITION_TYPED,
+ base::Time::Now());
+ ASSERT_TRUE(backend_->GetURL(GURL("http://foo4.com"), &url_row));
+ EXPECT_EQ(1, url_row.typed_count());
+ ASSERT_TRUE(backend_->GetURL(GURL("https://foo4.com/page1.html"), &url_row));
+ EXPECT_EQ(0, url_row.typed_count());
+
+ const char* redirect5[] = {"http://bar.com", "https://baz.com", nullptr};
+ AddRedirectChainWithTransitionAndTime(redirect5, 5, ui::PAGE_TRANSITION_TYPED,
+ base::Time::Now());
+ ASSERT_TRUE(backend_->GetURL(GURL("http://bar.com"), &url_row));
+ EXPECT_EQ(1, url_row.typed_count());
+ ASSERT_TRUE(backend_->GetURL(GURL("https://baz.com"), &url_row));
+ EXPECT_EQ(0, url_row.typed_count());
+
+ // A typed navigation redirecting from HTTPS to HTTP should have the first URL
+ // accrue the typed count, not the second.
+ const char* redirect6[] = {"https://foo6.com", "http://foo6.com", nullptr};
+ AddRedirectChainWithTransitionAndTime(redirect6, 6, ui::PAGE_TRANSITION_TYPED,
+ base::Time::Now());
+ ASSERT_TRUE(backend_->GetURL(GURL("https://foo6.com"), &url_row));
+ EXPECT_EQ(1, url_row.typed_count());
+ ASSERT_TRUE(backend_->GetURL(GURL("http://foo6.com"), &url_row));
+ EXPECT_EQ(0, url_row.typed_count());
+
+ // A long redirect chain where the first redirect is HTTP to HTTPS should
+ // count for the second URL (not the first or later URLs).
+ const char* redirect7[] = {"http://foo7.com", "https://foo7.com",
+ "https://foo7.com/page1.html", nullptr};
+ AddRedirectChainWithTransitionAndTime(redirect7, 7, ui::PAGE_TRANSITION_TYPED,
+ base::Time::Now());
+ ASSERT_TRUE(backend_->GetURL(GURL("http://foo7.com"), &url_row));
+ EXPECT_EQ(0, url_row.typed_count());
+ ASSERT_TRUE(backend_->GetURL(GURL("https://foo7.com"), &url_row));
+ EXPECT_EQ(1, url_row.typed_count());
+ ASSERT_TRUE(backend_->GetURL(GURL("https://foo7.com/page1.html"), &url_row));
+ EXPECT_EQ(0, url_row.typed_count());
+
+ // A typed navigation redirecting from HTTP to HTTPS but using non-standard
+ // port numbers should have the HTTPS URL accrue the typed count.
+ const char* redirect8[] = {"http://foo8.com:1234", "https://foo8.com:9876",
+ nullptr};
+ AddRedirectChainWithTransitionAndTime(redirect8, 8, ui::PAGE_TRANSITION_TYPED,
+ base::Time::Now());
+ ASSERT_TRUE(backend_->GetURL(GURL("http://foo8.com:1234"), &url_row));
+ EXPECT_EQ(0, url_row.typed_count());
+ ASSERT_TRUE(backend_->GetURL(GURL("https://foo8.com:9876"), &url_row));
+ EXPECT_EQ(1, url_row.typed_count());
+}
+
+// Tests that a typed navigation will accrue the typed count even when a client
+// redirect from HTTP to HTTPS occurs.
+TEST_F(HistoryBackendTest, ClientRedirectScoring) {
+ const GURL typed_url("http://foo.com");
+ const GURL redirected_url("https://foo.com");
+
+ // Initial typed page visit, with no server redirects.
+ HistoryAddPageArgs request(typed_url, base::Time::Now(), nullptr, 0, GURL(),
+ {}, ui::PAGE_TRANSITION_TYPED, false,
+ history::SOURCE_BROWSED, false, true);
+ backend_->AddPage(request);
+
+ // Client redirect to HTTPS (non-user initiated).
+ AddClientRedirect(typed_url, redirected_url, /*did_replace=*/true,
+ base::Time::Now(), /*transition1=*/nullptr,
+ /*transition2=*/nullptr);
+ URLRow url_row;
+ ASSERT_TRUE(backend_->GetURL(typed_url, &url_row));
+ EXPECT_EQ(1, url_row.typed_count());
+ ASSERT_TRUE(backend_->GetURL(redirected_url, &url_row));
+ EXPECT_EQ(0, url_row.typed_count());
+}
+
// Common implementation for the two tests below, given that the only difference
// between them is the type of the notification sent out.
void InMemoryHistoryBackendTest::TestAddingAndChangingURLRows(
@@ -4116,4 +4229,22 @@ TEST_F(HistoryBackendTest, QueryMostVisitedURLs) {
MostVisitedURL(GURL("http://example5.com"), kSomeTitle)));
}
+TEST(FormatUrlForRedirectComparisonTest, TestUrlFormatting) {
+ // Tests that the formatter removes HTTPS scheme, port, username/password,
+ // and trivial "www." subdomain. Domain and path are left unchanged.
+ GURL url1("https://foo:bar@www.baz.com:4443/path1.html");
+ EXPECT_EQ(base::ASCIIToUTF16("baz.com/path1.html"),
+ FormatUrlForRedirectComparison(url1));
+
+ // Tests that the formatter removes the HTTP scheme.
+ GURL url2("http://www.baz.com");
+ EXPECT_EQ(base::ASCIIToUTF16("baz.com/"),
+ FormatUrlForRedirectComparison(url2));
+
+ // Tests that the formatter removes repeated trivial subdomains.
+ GURL url3("http://m.www.www.baz.com/");
+ EXPECT_EQ(base::ASCIIToUTF16("baz.com/"),
+ FormatUrlForRedirectComparison(url3));
+}
+
} // namespace history
diff --git a/chromium/components/history/core/browser/history_database.cc b/chromium/components/history/core/browser/history_database.cc
index d80bc2395a4..7b556591f4f 100644
--- a/chromium/components/history/core/browser/history_database.cc
+++ b/chromium/components/history/core/browser/history_database.cc
@@ -38,7 +38,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 = 40;
+const int kCurrentVersionNumber = 41;
const int kCompatibleVersionNumber = 16;
const char kEarlyExpirationThresholdKey[] = "early_expiration_threshold";
const int kMaxHostsInMemory = 10000;
@@ -607,6 +607,18 @@ sql::InitStatus HistoryDatabase::EnsureCurrentVersion() {
meta_table_.SetVersionNumber(cur_version);
}
+ if (cur_version == 40) {
+ std::vector<URLID> visited_url_rowids_sorted;
+ if (!GetAllVisitedURLRowidsForMigrationToVersion40(
+ &visited_url_rowids_sorted) ||
+ !CleanTypedURLOrphanedMetadataForMigrationToVersion40(
+ visited_url_rowids_sorted)) {
+ return LogMigrationFailure(40);
+ }
+ 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_querying_unittest.cc b/chromium/components/history/core/browser/history_querying_unittest.cc
index 04f821b90e4..ffba6cfb934 100644
--- a/chromium/components/history/core/browser/history_querying_unittest.cc
+++ b/chromium/components/history/core/browser/history_querying_unittest.cc
@@ -182,11 +182,11 @@ class HistoryQueryTest : public testing::Test {
void TearDown() override {
if (history_) {
- history_->SetOnBackendDestroyTask(
- base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
+ base::RunLoop run_loop;
+ history_->SetOnBackendDestroyTask(run_loop.QuitClosure());
history_->Cleanup();
history_.reset();
- base::RunLoop().Run(); // Wait for the other thread.
+ run_loop.Run(); // Wait for the other thread.
}
}
diff --git a/chromium/components/history/core/browser/history_service.h b/chromium/components/history/core/browser/history_service.h
index 6c4aeb6904e..41d8aed1399 100644
--- a/chromium/components/history/core/browser/history_service.h
+++ b/chromium/components/history/core/browser/history_service.h
@@ -189,6 +189,10 @@ class HistoryService : public syncer::SyncableService, public KeyedService {
// should be the unique ID of the current navigation entry in the given
// process.
//
+ // TODO(avi): This is no longer true. 'page id' was removed years ago, and
+ // their uses replaced by globally-unique nav_entry_ids. Is ContextID still
+ // needed? https://crbug.com/859902
+ //
// 'redirects' is an array of redirect URLs leading to this page, with the
// page itself as the last item (so when there is no redirect, it will have
// one entry). If there are no redirects, this array may also be empty for
diff --git a/chromium/components/history/core/browser/history_service_unittest.cc b/chromium/components/history/core/browser/history_service_unittest.cc
index eb65c9ab1c3..c68c6ab2da0 100644
--- a/chromium/components/history/core/browser/history_service_unittest.cc
+++ b/chromium/components/history/core/browser/history_service_unittest.cc
@@ -83,17 +83,15 @@ class HistoryServiceTest : public testing::Test {
// Make sure we don't have any event pending that could disrupt the next
// test.
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
- base::RunLoop().Run();
+ base::RunLoop().RunUntilIdle();
}
void CleanupHistoryService() {
DCHECK(history_service_);
+ base::RunLoop run_loop;
history_service_->ClearCachedDataForContextID(nullptr);
- history_service_->SetOnBackendDestroyTask(
- base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
+ history_service_->SetOnBackendDestroyTask(run_loop.QuitClosure());
history_service_->Cleanup();
history_service_.reset();
@@ -101,24 +99,26 @@ class HistoryServiceTest : public testing::Test {
// moving to the next test. Note: if this never terminates, somebody is
// probably leaking a reference to the history backend, so it never calls
// our destroy task.
- base::RunLoop().Run();
+ run_loop.Run();
}
// Fills the query_url_row_ and query_url_visits_ structures with the
// information about the given URL and returns true. If the URL was not
// found, this will return false and those structures will not be changed.
bool QueryURL(history::HistoryService* history, const GURL& url) {
+ base::RunLoop run_loop;
history_service_->QueryURL(
- url,
- true,
- base::Bind(&HistoryServiceTest::SaveURLAndQuit, base::Unretained(this)),
+ url, true,
+ base::BindOnce(&HistoryServiceTest::SaveURLAndQuit,
+ base::Unretained(this), run_loop.QuitClosure()),
&tracker_);
- base::RunLoop().Run(); // Will be exited in SaveURLAndQuit.
+ run_loop.Run(); // Will be exited in SaveURLAndQuit.
return query_url_success_;
}
// Callback for HistoryService::QueryURL.
- void SaveURLAndQuit(bool success,
+ void SaveURLAndQuit(base::OnceClosure done,
+ bool success,
const URLRow& url_row,
const VisitVector& visits) {
query_url_success_ = success;
@@ -129,28 +129,30 @@ class HistoryServiceTest : public testing::Test {
query_url_row_ = URLRow();
query_url_visits_.clear();
}
- base::RunLoop::QuitCurrentWhenIdleDeprecated();
+ std::move(done).Run();
}
// Fills in saved_redirects_ with the redirect information for the given URL,
// returning true on success. False means the URL was not found.
void QueryRedirectsFrom(history::HistoryService* history, const GURL& url) {
+ base::RunLoop run_loop;
history_service_->QueryRedirectsFrom(
url,
base::Bind(&HistoryServiceTest::OnRedirectQueryComplete,
- base::Unretained(this)),
+ base::Unretained(this), run_loop.QuitClosure()),
&tracker_);
- base::RunLoop().Run(); // Will be exited in *QueryComplete.
+ run_loop.Run(); // Will be exited in *QueryComplete.
}
// Callback for QueryRedirects.
- void OnRedirectQueryComplete(const history::RedirectList* redirects) {
+ void OnRedirectQueryComplete(base::OnceClosure done,
+ const history::RedirectList* redirects) {
saved_redirects_.clear();
if (!redirects->empty()) {
saved_redirects_.insert(
saved_redirects_.end(), redirects->begin(), redirects->end());
}
- base::RunLoop::QuitCurrentWhenIdleDeprecated();
+ std::move(done).Run();
}
base::ScopedTempDir temp_dir_;
diff --git a/chromium/components/history/core/browser/history_types.cc b/chromium/components/history/core/browser/history_types.cc
index 67263b5344c..aae0d221267 100644
--- a/chromium/components/history/core/browser/history_types.cc
+++ b/chromium/components/history/core/browser/history_types.cc
@@ -260,7 +260,8 @@ HistoryAddPageArgs::HistoryAddPageArgs()
false,
SOURCE_BROWSED,
false,
- true) {}
+ true,
+ base::nullopt) {}
HistoryAddPageArgs::HistoryAddPageArgs(const GURL& url,
base::Time time,
@@ -272,7 +273,8 @@ HistoryAddPageArgs::HistoryAddPageArgs(const GURL& url,
bool hidden,
VisitSource source,
bool did_replace_entry,
- bool consider_for_ntp_most_visited)
+ bool consider_for_ntp_most_visited,
+ base::Optional<base::string16> title)
: url(url),
time(time),
context_id(context_id),
@@ -283,7 +285,8 @@ HistoryAddPageArgs::HistoryAddPageArgs(const GURL& url,
hidden(hidden),
visit_source(source),
did_replace_entry(did_replace_entry),
- consider_for_ntp_most_visited(consider_for_ntp_most_visited) {}
+ consider_for_ntp_most_visited(consider_for_ntp_most_visited),
+ title(title) {}
HistoryAddPageArgs::HistoryAddPageArgs(const HistoryAddPageArgs& other) =
default;
diff --git a/chromium/components/history/core/browser/history_types.h b/chromium/components/history/core/browser/history_types.h
index 5e72a015705..e661601e547 100644
--- a/chromium/components/history/core/browser/history_types.h
+++ b/chromium/components/history/core/browser/history_types.h
@@ -366,7 +366,11 @@ struct HistoryAddPageArgs {
// HistoryAddPageArgs(
// GURL(), base::Time(), NULL, 0, GURL(),
// RedirectList(), ui::PAGE_TRANSITION_LINK,
- // false, SOURCE_BROWSED, false, true)
+ // false, SOURCE_BROWSED, false, true,
+ // base::nullopt)
+ //
+ // TODO(avi): Is ContextID needed, now that we have a globally-unique
+ // nav_entry_id? https://crbug.com/859902
HistoryAddPageArgs();
HistoryAddPageArgs(const GURL& url,
base::Time time,
@@ -378,7 +382,8 @@ struct HistoryAddPageArgs {
bool hidden,
VisitSource source,
bool did_replace_entry,
- bool consider_for_ntp_most_visited);
+ bool consider_for_ntp_most_visited,
+ base::Optional<base::string16> title = base::nullopt);
HistoryAddPageArgs(const HistoryAddPageArgs& other);
~HistoryAddPageArgs();
@@ -397,6 +402,7 @@ struct HistoryAddPageArgs {
// doesn't guarantee it's relevant for Most Visited, since other requirements
// exist (e.g. certain page transition types).
bool consider_for_ntp_most_visited;
+ base::Optional<base::string16> title;
};
// TopSites -------------------------------------------------------------------
diff --git a/chromium/components/history/core/browser/thumbnail_database.cc b/chromium/components/history/core/browser/thumbnail_database.cc
index 26307b9de72..03c18899c49 100644
--- a/chromium/components/history/core/browser/thumbnail_database.cc
+++ b/chromium/components/history/core/browser/thumbnail_database.cc
@@ -153,7 +153,7 @@ void GenerateDiagnostics(sql::Connection* db,
// NOTE(shess): Schema modifications must consider initial creation in
// |InitImpl()| and history pruning in |RetainDataForPageUrls()|.
bool InitTables(sql::Connection* db) {
- const char kIconMappingSql[] =
+ static const char kIconMappingSql[] =
"CREATE TABLE IF NOT EXISTS icon_mapping"
"("
"id INTEGER PRIMARY KEY,"
@@ -163,7 +163,7 @@ bool InitTables(sql::Connection* db) {
if (!db->Execute(kIconMappingSql))
return false;
- const char kFaviconsSql[] =
+ static const char kFaviconsSql[] =
"CREATE TABLE IF NOT EXISTS favicons"
"("
"id INTEGER PRIMARY KEY,"
@@ -174,7 +174,7 @@ bool InitTables(sql::Connection* db) {
if (!db->Execute(kFaviconsSql))
return false;
- const char kFaviconBitmapsSql[] =
+ static const char kFaviconBitmapsSql[] =
"CREATE TABLE IF NOT EXISTS favicon_bitmaps"
"("
"id INTEGER PRIMARY KEY,"
@@ -196,10 +196,10 @@ bool InitTables(sql::Connection* db) {
// NOTE(shess): Schema modifications must consider initial creation in
// |InitImpl()| and history pruning in |RetainDataForPageUrls()|.
bool InitIndices(sql::Connection* db) {
- const char kIconMappingUrlIndexSql[] =
+ static const char kIconMappingUrlIndexSql[] =
"CREATE INDEX IF NOT EXISTS icon_mapping_page_url_idx"
" ON icon_mapping(page_url)";
- const char kIconMappingIdIndexSql[] =
+ static const char kIconMappingIdIndexSql[] =
"CREATE INDEX IF NOT EXISTS icon_mapping_icon_id_idx"
" ON icon_mapping(icon_id)";
if (!db->Execute(kIconMappingUrlIndexSql) ||
@@ -207,12 +207,12 @@ bool InitIndices(sql::Connection* db) {
return false;
}
- const char kFaviconsIndexSql[] =
+ static const char kFaviconsIndexSql[] =
"CREATE INDEX IF NOT EXISTS favicons_url ON favicons(url)";
if (!db->Execute(kFaviconsIndexSql))
return false;
- const char kFaviconBitmapsIndexSql[] =
+ static const char kFaviconBitmapsIndexSql[] =
"CREATE INDEX IF NOT EXISTS favicon_bitmaps_icon_id ON "
"favicon_bitmaps(icon_id)";
if (!db->Execute(kFaviconBitmapsIndexSql))
@@ -847,7 +847,7 @@ base::Optional<GURL> ThumbnailDatabase::FindFirstPageURLForHost(
IconMappingID ThumbnailDatabase::AddIconMapping(
const GURL& page_url,
favicon_base::FaviconID icon_id) {
- const char kSql[] =
+ static const char kSql[] =
"INSERT INTO icon_mapping (page_url, icon_id) VALUES (?, ?)";
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url));
@@ -917,12 +917,12 @@ bool ThumbnailDatabase::RetainDataForPageUrls(
// Populate temp.retained_urls with |urls_to_keep|.
{
- const char kCreateRetainedUrls[] =
+ static const char kCreateRetainedUrls[] =
"CREATE TEMP TABLE retained_urls (url LONGVARCHAR PRIMARY KEY)";
if (!db_.Execute(kCreateRetainedUrls))
return false;
- const char kRetainedUrlSql[] =
+ static const char kRetainedUrlSql[] =
"INSERT OR IGNORE INTO temp.retained_urls (url) VALUES (?)";
sql::Statement statement(db_.GetUniqueStatement(kRetainedUrlSql));
for (const GURL& url : urls_to_keep) {
@@ -936,7 +936,7 @@ bool ThumbnailDatabase::RetainDataForPageUrls(
// temp.icon_id_mapping generates new icon ids as consecutive
// integers starting from 1, and maps them to the old icon ids.
{
- const char kIconMappingCreate[] =
+ static const char kIconMappingCreate[] =
"CREATE TEMP TABLE icon_id_mapping "
"("
"new_icon_id INTEGER PRIMARY KEY,"
@@ -946,7 +946,7 @@ bool ThumbnailDatabase::RetainDataForPageUrls(
return false;
// Insert the icon ids for retained urls, skipping duplicates.
- const char kIconMappingSql[] =
+ static const char kIconMappingSql[] =
"INSERT OR IGNORE INTO temp.icon_id_mapping (old_icon_id) "
"SELECT icon_id FROM icon_mapping "
"JOIN temp.retained_urls "
@@ -955,9 +955,9 @@ bool ThumbnailDatabase::RetainDataForPageUrls(
return false;
}
- const char kRenameIconMappingTable[] =
+ static const char kRenameIconMappingTable[] =
"ALTER TABLE icon_mapping RENAME TO old_icon_mapping";
- const char kCopyIconMapping[] =
+ static const char kCopyIconMapping[] =
"INSERT INTO icon_mapping (page_url, icon_id) "
"SELECT temp.retained_urls.url, mapping.new_icon_id "
"FROM temp.retained_urls "
@@ -965,17 +965,17 @@ bool ThumbnailDatabase::RetainDataForPageUrls(
"ON (temp.retained_urls.url = old.page_url) "
"JOIN temp.icon_id_mapping AS mapping "
"ON (old.icon_id = mapping.old_icon_id)";
- const char kDropOldIconMappingTable[] = "DROP TABLE old_icon_mapping";
+ static const char kDropOldIconMappingTable[] = "DROP TABLE old_icon_mapping";
- const char kRenameFaviconsTable[] =
+ static const char kRenameFaviconsTable[] =
"ALTER TABLE favicons RENAME TO old_favicons";
- const char kCopyFavicons[] =
+ static const char kCopyFavicons[] =
"INSERT INTO favicons (id, url, icon_type) "
"SELECT mapping.new_icon_id, old.url, old.icon_type "
"FROM old_favicons AS old "
"JOIN temp.icon_id_mapping AS mapping "
"ON (old.id = mapping.old_icon_id)";
- const char kDropOldFaviconsTable[] = "DROP TABLE old_favicons";
+ static const char kDropOldFaviconsTable[] = "DROP TABLE old_favicons";
// Set the retained favicon bitmaps to be expired (last_updated == 0).
// The user may be deleting their favicon bitmaps because the favicon bitmaps
@@ -983,9 +983,9 @@ bool ThumbnailDatabase::RetainDataForPageUrls(
// the user visits a page associated with the favicon bitmap. See
// crbug.com/474421 for an example of a bug which caused favicon bitmaps to
// become incorrect.
- const char kRenameFaviconBitmapsTable[] =
+ static const char kRenameFaviconBitmapsTable[] =
"ALTER TABLE favicon_bitmaps RENAME TO old_favicon_bitmaps";
- const char kCopyFaviconBitmaps[] =
+ static const char kCopyFaviconBitmaps[] =
"INSERT INTO favicon_bitmaps "
" (icon_id, last_updated, image_data, width, height, last_requested) "
"SELECT mapping.new_icon_id, 0, old.image_data, old.width, old.height,"
@@ -993,7 +993,7 @@ bool ThumbnailDatabase::RetainDataForPageUrls(
"FROM old_favicon_bitmaps AS old "
"JOIN temp.icon_id_mapping AS mapping "
"ON (old.icon_id = mapping.old_icon_id)";
- const char kDropOldFaviconBitmapsTable[] =
+ static const char kDropOldFaviconBitmapsTable[] =
"DROP TABLE old_favicon_bitmaps";
// Rename existing tables to new location.
@@ -1029,8 +1029,8 @@ bool ThumbnailDatabase::RetainDataForPageUrls(
if (!InitIndices(&db_))
return false;
- const char kIconMappingDrop[] = "DROP TABLE temp.icon_id_mapping";
- const char kRetainedUrlsDrop[] = "DROP TABLE temp.retained_urls";
+ static const char kIconMappingDrop[] = "DROP TABLE temp.icon_id_mapping";
+ static const char kRetainedUrlsDrop[] = "DROP TABLE temp.retained_urls";
if (!db_.Execute(kIconMappingDrop) || !db_.Execute(kRetainedUrlsDrop))
return false;
@@ -1221,7 +1221,7 @@ bool ThumbnailDatabase::UpgradeToVersion7() {
bool ThumbnailDatabase::UpgradeToVersion8() {
// Add the last_requested column to the favicon_bitmaps table.
- const char kFaviconBitmapsAddLastRequestedSql[] =
+ static const char kFaviconBitmapsAddLastRequestedSql[] =
"ALTER TABLE favicon_bitmaps ADD COLUMN last_requested INTEGER DEFAULT 0";
if (!db_.Execute(kFaviconBitmapsAddLastRequestedSql))
return false;
diff --git a/chromium/components/history/core/browser/thumbnail_database_unittest.cc b/chromium/components/history/core/browser/thumbnail_database_unittest.cc
index 8555de8676f..883b29c86aa 100644
--- a/chromium/components/history/core/browser/thumbnail_database_unittest.cc
+++ b/chromium/components/history/core/browser/thumbnail_database_unittest.cc
@@ -1035,8 +1035,8 @@ TEST_F(ThumbnailDatabaseTest, Recovery) {
EXPECT_TRUE(raw_db.Open(file_name_));
ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
}
- const char kIndexName[] = "icon_mapping_page_url_idx";
- const char kDeleteSql[] =
+ static const char kIndexName[] = "icon_mapping_page_url_idx";
+ static const char kDeleteSql[] =
"DELETE FROM icon_mapping WHERE page_url = 'http://yahoo.com/'";
EXPECT_TRUE(
sql::test::CorruptTableOrIndex(file_name_, kIndexName, kDeleteSql));
@@ -1129,8 +1129,8 @@ TEST_F(ThumbnailDatabaseTest, Recovery7) {
EXPECT_TRUE(raw_db.Open(file_name_));
ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
}
- const char kIndexName[] = "icon_mapping_page_url_idx";
- const char kDeleteSql[] =
+ static const char kIndexName[] = "icon_mapping_page_url_idx";
+ static const char kDeleteSql[] =
"DELETE FROM icon_mapping WHERE page_url = 'http://yahoo.com/'";
EXPECT_TRUE(
sql::test::CorruptTableOrIndex(file_name_, kIndexName, kDeleteSql));
diff --git a/chromium/components/history/core/browser/top_sites_database.cc b/chromium/components/history/core/browser/top_sites_database.cc
index 46acde473a9..a5e86ef0438 100644
--- a/chromium/components/history/core/browser/top_sites_database.cc
+++ b/chromium/components/history/core/browser/top_sites_database.cc
@@ -66,7 +66,7 @@ static const int kVersionNumber = 3;
static const int kDeprecatedVersionNumber = 2; // and earlier.
bool InitTables(sql::Connection* db) {
- const char kThumbnailsSql[] =
+ static const char kThumbnailsSql[] =
"CREATE TABLE IF NOT EXISTS thumbnails ("
"url LONGVARCHAR PRIMARY KEY,"
"url_rank INTEGER,"
@@ -153,7 +153,7 @@ void RecordRecoveryEvent(RecoveryEventType recovery_event) {
// together and yield a row with errors.
void FixThumbnailsTable(sql::Connection* db) {
// Enforce invariant separating forced and non-forced thumbnails.
- const char kFixRankSql[] =
+ static const char kFixRankSql[] =
"DELETE FROM thumbnails "
"WHERE (url_rank = -1 AND last_forced = 0) "
"OR (url_rank <> -1 AND last_forced <> 0)";
@@ -162,7 +162,7 @@ void FixThumbnailsTable(sql::Connection* db) {
RecordRecoveryEvent(RECOVERY_EVENT_INVARIANT_RANK);
// Enforce invariant that url is in its own redirects.
- const char kFixRedirectsSql[] =
+ static const char kFixRedirectsSql[] =
"DELETE FROM thumbnails "
"WHERE url <> substr(redirects, -length(url), length(url))";
ignore_result(db->Execute(kFixRedirectsSql));
@@ -174,12 +174,12 @@ void FixThumbnailsTable(sql::Connection* db) {
// It can be done with a temporary table and a subselect, but doing it
// manually is easier to follow. Another option would be to somehow integrate
// the renumbering into the table recovery code.
- const char kByRankSql[] =
+ static const char kByRankSql[] =
"SELECT url_rank, rowid FROM thumbnails WHERE url_rank <> -1 "
"ORDER BY url_rank";
sql::Statement select_statement(db->GetUniqueStatement(kByRankSql));
- const char kAdjustRankSql[] =
+ static const char kAdjustRankSql[] =
"UPDATE thumbnails SET url_rank = ? WHERE rowid = ?";
sql::Statement update_statement(db->GetUniqueStatement(kAdjustRankSql));
diff --git a/chromium/components/history/core/browser/top_sites_database_unittest.cc b/chromium/components/history/core/browser/top_sites_database_unittest.cc
index f05ecb81224..1c2ca1282f2 100644
--- a/chromium/components/history/core/browser/top_sites_database_unittest.cc
+++ b/chromium/components/history/core/browser/top_sites_database_unittest.cc
@@ -241,10 +241,10 @@ TEST_F(TopSitesDatabaseTest, Recovery3) {
// Corrupt the thumnails.url auto-index by deleting an element from the table
// but leaving it in the index.
- const char kIndexName[] = "sqlite_autoindex_thumbnails_1";
+ static const char kIndexName[] = "sqlite_autoindex_thumbnails_1";
// TODO(shess): Refactor CorruptTableOrIndex() to make parameterized
// statements easy.
- const char kDeleteSql[] =
+ static const char kDeleteSql[] =
"DELETE FROM thumbnails WHERE url = "
"'http://www.google.com/chrome/intl/en/welcome.html'";
EXPECT_TRUE(
diff --git a/chromium/components/history/core/browser/typed_url_model_type_controller.cc b/chromium/components/history/core/browser/typed_url_model_type_controller.cc
index 08fa338e7bc..f7d482a83f7 100644
--- a/chromium/components/history/core/browser/typed_url_model_type_controller.cc
+++ b/chromium/components/history/core/browser/typed_url_model_type_controller.cc
@@ -95,6 +95,7 @@ void TypedURLModelTypeController::OnSavingBrowserHistoryDisabledChanged() {
// Chrome (on restart, typed urls will not be a registered type).
if (state() != NOT_RUNNING && state() != STOPPING) {
ReportModelError(
+ syncer::SyncError::DATATYPE_POLICY_ERROR,
{FROM_HERE, "History saving is now disabled by policy."});
}
}
diff --git a/chromium/components/history/core/browser/typed_url_sync_bridge.cc b/chromium/components/history/core/browser/typed_url_sync_bridge.cc
index edbd2b4e795..4b6e891c60e 100644
--- a/chromium/components/history/core/browser/typed_url_sync_bridge.cc
+++ b/chromium/components/history/core/browser/typed_url_sync_bridge.cc
@@ -292,7 +292,7 @@ void TypedURLSyncBridge::GetData(StorageKeyList storage_keys,
std::move(callback).Run(std::move(batch));
}
-void TypedURLSyncBridge::GetAllData(DataCallback callback) {
+void TypedURLSyncBridge::GetAllDataForDebugging(DataCallback callback) {
DCHECK(sequence_checker_.CalledOnValidSequence());
URLRows typed_urls;
@@ -414,13 +414,16 @@ void TypedURLSyncBridge::OnURLsDeleted(HistoryBackend* history_backend,
// Delete metadata from the DB and ask the processor to untrack the entries.
for (const URLRow& row : deleted_rows) {
std::string storage_key = GetStorageKeyFromURLRow(row);
+ // The following functions need to tolerate if there exists no metadata
+ // for |storage_key|. The reason is we have no way to tell if there is any
+ // metadata for this row. We cannot distinguish a URL that has never been
+ // typed from a URL that has been typed before but its typed visit has
+ // expired earlier than the URL itself (because there were non-typed
+ // visits afterwards). On top of that, this bridge is not very robust in
+ // syncing up every typed URL.
sync_metadata_database_->ClearSyncMetadata(syncer::TYPED_URLS,
storage_key);
- // TODO(jkrcal): Untrack the entity from the processor, too (by
- // introducing UntrackEntityForStorageKey() function into the change
- // processor). Extend the integration tests to cover the crash in
- // https://crbug.com/827111. Also add unit-test coverage for expired
- // deletions (needs some refactoring in the tests).
+ change_processor()->UntrackEntityForStorageKey(storage_key);
}
return;
}
diff --git a/chromium/components/history/core/browser/typed_url_sync_bridge.h b/chromium/components/history/core/browser/typed_url_sync_bridge.h
index b136b9184f4..39b307563e5 100644
--- a/chromium/components/history/core/browser/typed_url_sync_bridge.h
+++ b/chromium/components/history/core/browser/typed_url_sync_bridge.h
@@ -36,7 +36,7 @@ class TypedURLSyncBridge : public syncer::ModelTypeSyncBridge,
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_changes) override;
void GetData(StorageKeyList storage_keys, DataCallback callback) override;
- void GetAllData(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;
bool SupportsGetStorageKey() const override;
diff --git a/chromium/components/history/core/browser/typed_url_sync_bridge_unittest.cc b/chromium/components/history/core/browser/typed_url_sync_bridge_unittest.cc
index 6eb285e17c1..8fbf1970da9 100644
--- a/chromium/components/history/core/browser/typed_url_sync_bridge_unittest.cc
+++ b/chromium/components/history/core/browser/typed_url_sync_bridge_unittest.cc
@@ -388,7 +388,8 @@ class TypedURLSyncBridgeTest : public testing::Test {
void VerifyAllLocalHistoryData(
const std::vector<TypedUrlSpecifics>& expected) {
- bridge()->GetAllData(base::Bind(&VerifyDataBatch, ExpectedMap(expected)));
+ bridge()->GetAllDataForDebugging(
+ base::Bind(&VerifyDataBatch, ExpectedMap(expected)));
}
void VerifyGetData(TypedURLSyncBridge::StorageKeyList storage_keys,
@@ -1034,10 +1035,11 @@ TEST_F(TypedURLSyncBridgeTest, ExpireLocalTypedUrl) {
EntityChange::ACTION_ADD);
}
- // Check all the metadata is here.
+ // Check all the metadata is here, no need to untrack anything so far.
MetadataBatch metadata_batch;
metadata_store()->GetAllSyncMetadata(&metadata_batch);
ASSERT_EQ(5u, metadata_batch.TakeAllMetadata().size());
+ ASSERT_EQ(0U, processor().untrack_for_storage_key_set().size());
// Simulate expiration - delete some urls from the backend and create deleted
// row vector.
@@ -1057,6 +1059,8 @@ TEST_F(TypedURLSyncBridgeTest, ExpireLocalTypedUrl) {
// This does not propagate to the processor.
EXPECT_EQ(0U, processor().put_multimap().size() - previous_put_size);
EXPECT_EQ(0U, processor().delete_set().size());
+ // The processor is still informed to clear its in-memory maps.
+ EXPECT_EQ(3U, processor().untrack_for_storage_key_set().size());
// The urls are removed from the metadata store.
MetadataBatch smaller_metadata_batch;
diff --git a/chromium/components/history/core/browser/typed_url_sync_metadata_database.cc b/chromium/components/history/core/browser/typed_url_sync_metadata_database.cc
index 8f6c1f1c04a..465317b577f 100644
--- a/chromium/components/history/core/browser/typed_url_sync_metadata_database.cc
+++ b/chromium/components/history/core/browser/typed_url_sync_metadata_database.cc
@@ -116,6 +116,49 @@ bool TypedURLSyncMetadataDatabase::InitSyncTable() {
return true;
}
+bool TypedURLSyncMetadataDatabase::
+ CleanTypedURLOrphanedMetadataForMigrationToVersion40(
+ const std::vector<URLID>& sorted_valid_rowids) {
+ DCHECK(
+ std::is_sorted(sorted_valid_rowids.begin(), sorted_valid_rowids.end()));
+ std::vector<URLID> invalid_metadata_rowids;
+ auto valid_rowids_iter = sorted_valid_rowids.begin();
+
+ sql::Statement sorted_metadata_rowids(GetDB().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);
+ // Both collections are sorted, we check whether |metadata_rowid| is valid
+ // by iterating both at the same time.
+
+ // First, skip all valid IDs that are omitted in |sorted_metadata_rowids|.
+ while (valid_rowids_iter != sorted_valid_rowids.end() &&
+ *valid_rowids_iter < metadata_rowid) {
+ valid_rowids_iter++;
+ }
+ // Now, is |metadata_rowid| invalid?
+ if (valid_rowids_iter == sorted_valid_rowids.end() ||
+ *valid_rowids_iter != metadata_rowid) {
+ invalid_metadata_rowids.push_back(metadata_rowid);
+ }
+ }
+
+ if (!sorted_metadata_rowids.Succeeded()) {
+ return false;
+ }
+
+ for (const URLID& rowid : invalid_metadata_rowids) {
+ sql::Statement del(GetDB().GetCachedStatement(
+ SQL_FROM_HERE,
+ "DELETE FROM typed_url_sync_metadata WHERE storage_key=?"));
+ del.BindInt64(0, rowid);
+ if (!del.Run())
+ return false;
+ }
+
+ return true;
+}
+
bool TypedURLSyncMetadataDatabase::GetAllSyncEntityMetadata(
syncer::MetadataBatch* metadata_batch) {
DCHECK(metadata_batch);
diff --git a/chromium/components/history/core/browser/typed_url_sync_metadata_database.h b/chromium/components/history/core/browser/typed_url_sync_metadata_database.h
index a48548cfd8f..5ca216348da 100644
--- a/chromium/components/history/core/browser/typed_url_sync_metadata_database.h
+++ b/chromium/components/history/core/browser/typed_url_sync_metadata_database.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_HISTORY_CORE_BROWSER_TYPED_URL_SYNC_METADATA_DATABASE_H_
#define COMPONENTS_HISTORY_CORE_BROWSER_TYPED_URL_SYNC_METADATA_DATABASE_H_
+#include <vector>
+
#include "base/macros.h"
#include "components/history/core/browser/url_row.h"
#include "components/sync/base/model_type.h"
@@ -59,6 +61,13 @@ class TypedURLSyncMetadataDatabase : public syncer::SyncMetadataStore {
// 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(
+ const std::vector<URLID>& sorted_valid_rowids);
+
private:
// Read all sync_pb::EntityMetadata for typed URL and fill
// |metadata_records| with it.
diff --git a/chromium/components/history/core/browser/url_database.cc b/chromium/components/history/core/browser/url_database.cc
index a60dc9ce1cf..9a34fcc3608 100644
--- a/chromium/components/history/core/browser/url_database.cc
+++ b/chromium/components/history/core/browser/url_database.cc
@@ -125,19 +125,19 @@ URLID URLDatabase::AddURLInternal(const URLRow& info, bool is_temporary) {
" (url, title, visit_count, typed_count, "\
"last_visit_time, hidden) "\
"VALUES (?,?,?,?,?,?)"
- const char* statement_name;
+ size_t statement_line;
const char* statement_sql;
if (is_temporary) {
- statement_name = "AddURLTemporary";
+ statement_line = __LINE__;
statement_sql = "INSERT INTO temp_urls" ADDURL_COMMON_SUFFIX;
} else {
- statement_name = "AddURL";
+ statement_line = __LINE__;
statement_sql = "INSERT INTO urls" ADDURL_COMMON_SUFFIX;
}
#undef ADDURL_COMMON_SUFFIX
sql::Statement statement(GetDB().GetCachedStatement(
- sql::StatementID(statement_name), statement_sql));
+ sql::StatementID(__FILE__, statement_line), statement_sql));
statement.BindString(0, GURLToDatabaseURL(info.url()));
statement.BindString16(1, info.title());
statement.BindInt(2, info.visit_count());
@@ -276,8 +276,9 @@ bool URLDatabase::AutocompleteForPrefix(const std::string& prefix,
// as bookmarks is no longer part of the db we no longer include the order
// by clause.
results->clear();
+
const char* sql;
- int line;
+ size_t line;
if (typed_only) {
sql = "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls "
"WHERE url >= ? AND url < ? AND hidden = 0 AND typed_count > 0 "
diff --git a/chromium/components/history/core/browser/visit_database.cc b/chromium/components/history/core/browser/visit_database.cc
index 8dca02c1f2b..785f1975610 100644
--- a/chromium/components/history/core/browser/visit_database.cc
+++ b/chromium/components/history/core/browser/visit_database.cc
@@ -720,4 +720,16 @@ bool VisitDatabase::MigrateVisitsWithoutIncrementedOmniboxTypedScore() {
return true;
}
+bool VisitDatabase::GetAllVisitedURLRowidsForMigrationToVersion40(
+ std::vector<URLID>* visited_url_rowids_sorted) {
+ DCHECK(visited_url_rowids_sorted);
+ sql::Statement statement(GetDB().GetUniqueStatement(
+ "SELECT DISTINCT url FROM visits ORDER BY url"));
+
+ while (statement.Step()) {
+ visited_url_rowids_sorted->push_back(statement.ColumnInt64(0));
+ }
+ return statement.Succeeded();
+}
+
} // namespace history
diff --git a/chromium/components/history/core/browser/visit_database.h b/chromium/components/history/core/browser/visit_database.h
index c4ec7ace7b1..323e86f1f90 100644
--- a/chromium/components/history/core/browser/visit_database.h
+++ b/chromium/components/history/core/browser/visit_database.h
@@ -227,6 +227,10 @@ class VisitDatabase {
// don't have incremented_omnibox_typed_score column yet.
bool MigrateVisitsWithoutIncrementedOmniboxTypedScore();
+ // A subprocedure in the process of migration to version 40.
+ bool GetAllVisitedURLRowidsForMigrationToVersion40(
+ std::vector<URLID>* visited_url_rowids_sorted);
+
private:
DISALLOW_COPY_AND_ASSIGN(VisitDatabase);
diff --git a/chromium/components/history/core/browser/web_history_service.cc b/chromium/components/history/core/browser/web_history_service.cc
index 325566e56eb..9df346bd454 100644
--- a/chromium/components/history/core/browser/web_history_service.cc
+++ b/chromium/components/history/core/browser/web_history_service.cc
@@ -28,10 +28,11 @@
#include "net/base/url_util.h"
#include "net/http/http_status_code.h"
#include "net/http/http_util.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_context_getter.h"
#include "services/identity/public/cpp/identity_manager.h"
+#include "services/identity/public/cpp/primary_account_access_token_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"
#include "ui/base/device_form_factor.h"
#include "url/gurl.h"
@@ -63,11 +64,10 @@ const char kPostDataMimeType[] = "text/plain";
const char kSyncProtoMimeType[] = "application/octet-stream";
-// The maximum number of retries for the URLFetcher requests.
+// The maximum number of retries for the SimpleURLLoader requests.
const size_t kMaxRetries = 1;
-class RequestImpl : public WebHistoryService::Request,
- private net::URLFetcherDelegate {
+class RequestImpl : public WebHistoryService::Request {
public:
~RequestImpl() override {}
@@ -85,12 +85,12 @@ class RequestImpl : public WebHistoryService::Request,
RequestImpl(
identity::IdentityManager* identity_manager,
- const scoped_refptr<net::URLRequestContextGetter>& request_context,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const GURL& url,
const WebHistoryService::CompletionCallback& callback,
const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation)
: identity_manager_(identity_manager),
- request_context_(request_context),
+ url_loader_factory_(std::move(url_loader_factory)),
url_(url),
post_data_mime_type_(kPostDataMimeType),
response_code_(0),
@@ -99,11 +99,11 @@ class RequestImpl : public WebHistoryService::Request,
is_pending_(false),
partial_traffic_annotation_(partial_traffic_annotation) {
DCHECK(identity_manager_);
- DCHECK(request_context_);
+ DCHECK(url_loader_factory_);
}
- void OnAccessTokenFetchComplete(const GoogleServiceAuthError& error,
- const std::string& access_token) {
+ void OnAccessTokenFetchComplete(GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info) {
access_token_fetcher_.reset();
if (error.state() != GoogleServiceAuthError::NONE) {
@@ -116,14 +116,55 @@ class RequestImpl : public WebHistoryService::Request,
return;
}
- DCHECK(!access_token.empty());
- access_token_ = access_token;
+ DCHECK(!access_token_info.token.empty());
+ access_token_ = access_token_info.token;
UMA_HISTOGRAM_BOOLEAN("WebHistory.OAuthTokenCompletion", true);
// Got an access token -- start the actual API request.
- url_fetcher_ = CreateUrlFetcher(access_token);
- url_fetcher_->Start();
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::CompleteNetworkTrafficAnnotation("web_history_service",
+ partial_traffic_annotation_,
+ R"(
+ semantics {
+ sender: "Web History"
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: NO
+ setting:
+ "To disable this feature, users can either sign out or disable "
+ "history sync via unchecking 'History' setting under 'Advanced "
+ "sync settings."
+ })");
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = url_;
+ resource_request->load_flags =
+ net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
+ resource_request->method = post_data_ ? "POST" : "GET";
+ resource_request->headers.SetHeader(net::HttpRequestHeaders::kAuthorization,
+ "Bearer " + access_token_info.token);
+ resource_request->headers.SetHeader(
+ "X-Developer-Key", GaiaUrls::GetInstance()->oauth2_chrome_client_id());
+ if (!user_agent_.empty()) {
+ resource_request->headers.SetHeader(net::HttpRequestHeaders::kUserAgent,
+ user_agent_);
+ }
+ // TODO(https://crbug.com/808498): Re-add data use measurement once
+ // SimpleURLLoader supports it.
+ // ID=data_use_measurement::DataUseUserData::WEB_HISTORY_SERVICE
+ simple_url_loader_ = network::SimpleURLLoader::Create(
+ std::move(resource_request), traffic_annotation);
+ if (post_data_) {
+ simple_url_loader_->AttachStringForUpload(post_data_.value(),
+ post_data_mime_type_);
+ }
+ simple_url_loader_->SetRetryOptions(kMaxRetries,
+ network::SimpleURLLoader::RETRY_ON_5XX);
+ simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&RequestImpl::OnSimpleLoaderComplete,
+ base::Unretained(this)));
}
// Tells the request to do its thang.
@@ -132,18 +173,22 @@ class RequestImpl : public WebHistoryService::Request,
oauth_scopes.insert(kHistoryOAuthScope);
access_token_fetcher_ =
- identity_manager_->CreateAccessTokenFetcherForPrimaryAccount(
- "web_history", oauth_scopes,
+ std::make_unique<identity::PrimaryAccountAccessTokenFetcher>(
+ "web_history", identity_manager_, oauth_scopes,
base::BindOnce(&RequestImpl::OnAccessTokenFetchComplete,
base::Unretained(this)),
identity::PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
is_pending_ = true;
}
- // content::URLFetcherDelegate interface.
- void OnURLFetchComplete(const net::URLFetcher* source) override {
- DCHECK_EQ(source, url_fetcher_.get());
- response_code_ = url_fetcher_->GetResponseCode();
+ void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body) {
+ response_code_ = -1;
+ if (simple_url_loader_->ResponseInfo() &&
+ simple_url_loader_->ResponseInfo()->headers) {
+ response_code_ =
+ simple_url_loader_->ResponseInfo()->headers->response_code();
+ }
+ simple_url_loader_.reset();
UMA_HISTOGRAM_CUSTOM_ENUMERATION("WebHistory.OAuthTokenResponseCode",
net::HttpUtil::MapStatusCodeForHistogram(response_code_),
@@ -155,65 +200,24 @@ class RequestImpl : public WebHistoryService::Request,
OAuth2TokenService::ScopeSet oauth_scopes;
oauth_scopes.insert(kHistoryOAuthScope);
identity_manager_->RemoveAccessTokenFromCache(
- identity_manager_->GetPrimaryAccountInfo(), oauth_scopes,
+ identity_manager_->GetPrimaryAccountInfo().account_id, oauth_scopes,
access_token_);
access_token_.clear();
Start();
return;
}
- url_fetcher_->GetResponseAsString(&response_body_);
- url_fetcher_.reset();
+ if (response_body) {
+ response_body_ = std::move(*response_body);
+ } else {
+ response_body_.clear();
+ }
is_pending_ = false;
callback_.Run(this, true);
// It is valid for the callback to delete |this|, so do not access any
// members below here.
}
- // Helper for creating a new URLFetcher for the API request.
- std::unique_ptr<net::URLFetcher> CreateUrlFetcher(
- const std::string& access_token) {
- net::URLFetcher::RequestType request_type = post_data_ ?
- net::URLFetcher::POST : net::URLFetcher::GET;
- net::NetworkTrafficAnnotationTag traffic_annotation =
- net::CompleteNetworkTrafficAnnotation("web_history_service",
- partial_traffic_annotation_,
- R"(
- semantics {
- sender: "Web History"
- destination: GOOGLE_OWNED_SERVICE
- }
- policy {
- cookies_allowed: NO
- setting:
- "To disable this feature, users can either sign out or disable "
- "history sync via unchecking 'History' setting under 'Advanced "
- "sync settings."
- })");
- std::unique_ptr<net::URLFetcher> fetcher =
- net::URLFetcher::Create(url_, request_type, this, traffic_annotation);
- data_use_measurement::DataUseUserData::AttachToFetcher(
- fetcher.get(),
- data_use_measurement::DataUseUserData::WEB_HISTORY_SERVICE);
- fetcher->SetRequestContext(request_context_.get());
- fetcher->SetMaxRetriesOn5xx(kMaxRetries);
- fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES);
- fetcher->AddExtraRequestHeader("Authorization: Bearer " + access_token);
- fetcher->AddExtraRequestHeader("X-Developer-Key: " +
- GaiaUrls::GetInstance()->oauth2_chrome_client_id());
-
- if (!user_agent_.empty()) {
- fetcher->AddExtraRequestHeader(
- std::string(net::HttpRequestHeaders::kUserAgent) +
- ": " + user_agent_);
- }
-
- if (post_data_)
- fetcher->SetUploadData(post_data_mime_type_, post_data_.value());
- return fetcher;
- }
-
void SetPostData(const std::string& post_data) override {
SetPostDataAndType(post_data, kPostDataMimeType);
}
@@ -229,7 +233,7 @@ class RequestImpl : public WebHistoryService::Request,
}
identity::IdentityManager* identity_manager_;
- scoped_refptr<net::URLRequestContextGetter> request_context_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// The URL of the API endpoint.
GURL url_;
@@ -250,7 +254,7 @@ class RequestImpl : public WebHistoryService::Request,
std::string access_token_;
// Handles the actual API requests after the OAuth token is acquired.
- std::unique_ptr<net::URLFetcher> url_fetcher_;
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
// Holds the response code received from the server.
int response_code_;
@@ -268,7 +272,7 @@ class RequestImpl : public WebHistoryService::Request,
// True if the request was started and has not yet completed, otherwise false.
bool is_pending_;
- // Partial Network traffic annotation used to create URLFetcher for this
+ // Partial Network traffic annotation used to create SimpleURLLoader for this
// request.
const net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation_;
};
@@ -348,9 +352,9 @@ WebHistoryService::Request::~Request() {
WebHistoryService::WebHistoryService(
identity::IdentityManager* identity_manager,
- const scoped_refptr<net::URLRequestContextGetter>& request_context)
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: identity_manager_(identity_manager),
- request_context_(request_context),
+ url_loader_factory_(std::move(url_loader_factory)),
weak_ptr_factory_(this) {}
WebHistoryService::~WebHistoryService() {
@@ -368,7 +372,7 @@ WebHistoryService::Request* WebHistoryService::CreateRequest(
const GURL& url,
const CompletionCallback& callback,
const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
- return new RequestImpl(identity_manager_, request_context_, url, callback,
+ return new RequestImpl(identity_manager_, url_loader_factory_, url, callback,
partial_traffic_annotation);
}
diff --git a/chromium/components/history/core/browser/web_history_service.h b/chromium/components/history/core/browser/web_history_service.h
index 9693f743e5d..4a971649a18 100644
--- a/chromium/components/history/core/browser/web_history_service.h
+++ b/chromium/components/history/core/browser/web_history_service.h
@@ -29,8 +29,8 @@ namespace identity {
class IdentityManager;
}
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
}
namespace version_info {
@@ -98,7 +98,7 @@ class WebHistoryService : public KeyedService {
WebHistoryService(
identity::IdentityManager* identity_manager,
- const scoped_refptr<net::URLRequestContextGetter>& request_context);
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
~WebHistoryService() override;
void AddObserver(WebHistoryServiceObserver* observer);
@@ -223,7 +223,7 @@ class WebHistoryService : public KeyedService {
identity::IdentityManager* identity_manager_;
// Request context getter to use.
- scoped_refptr<net::URLRequestContextGetter> request_context_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// Stores the version_info token received from the server in response to
// a mutation operation (e.g., deleting history). This is used to ensure that
diff --git a/chromium/components/history/core/browser/web_history_service_unittest.cc b/chromium/components/history/core/browser/web_history_service_unittest.cc
index 3b586117aee..e029ae37e40 100644
--- a/chromium/components/history/core/browser/web_history_service_unittest.cc
+++ b/chromium/components/history/core/browser/web_history_service_unittest.cc
@@ -15,7 +15,9 @@
#include "base/values.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/url_request_test_util.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_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -30,12 +32,12 @@ namespace {
class TestingWebHistoryService : public WebHistoryService {
public:
explicit TestingWebHistoryService(
- const scoped_refptr<net::URLRequestContextGetter>& request_context)
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
// NOTE: Simply pass null object for IdentityManager. WebHistoryService's
// only usage of this object is to fetch access tokens via RequestImpl,
// and TestWebHistoryService deliberately replaces this flow with
// TestRequest.
- : WebHistoryService(nullptr, request_context),
+ : WebHistoryService(nullptr, url_loader_factory),
expected_url_(GURL()),
expected_audio_history_value_(false),
current_expected_post_data_("") {}
@@ -215,9 +217,10 @@ std::string TestingWebHistoryService::GetExpectedAudioHistoryValue() {
class WebHistoryServiceTest : public testing::Test {
public:
WebHistoryServiceTest()
- : url_request_context_(new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get())),
- web_history_service_(url_request_context_) {}
+ : test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)),
+ web_history_service_(test_shared_loader_factory_) {}
~WebHistoryServiceTest() override {}
@@ -234,7 +237,8 @@ class WebHistoryServiceTest : public testing::Test {
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
- scoped_refptr<net::URLRequestContextGetter> url_request_context_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
TestingWebHistoryService web_history_service_;
DISALLOW_COPY_AND_ASSIGN(WebHistoryServiceTest);
diff --git a/chromium/components/history/core/test/BUILD.gn b/chromium/components/history/core/test/BUILD.gn
index 2fd38cf2284..73d0642df88 100644
--- a/chromium/components/history/core/test/BUILD.gn
+++ b/chromium/components/history/core/test/BUILD.gn
@@ -33,6 +33,7 @@ static_library("test") {
"//components/history/core/browser",
"//components/sync/protocol:protocol",
"//net",
+ "//services/network/public/cpp",
"//sql",
"//sql:test_support",
"//testing/gtest",
diff --git a/chromium/components/history_strings.grdp b/chromium/components/history_strings.grdp
index a46b1cd8631..463ebd95553 100644
--- a/chromium/components/history_strings.grdp
+++ b/chromium/components/history_strings.grdp
@@ -51,7 +51,7 @@
No search results found
</message>
<if expr="not use_titlecase">
- <message name="IDS_HISTORY_OPEN_CLEAR_BROWSING_DATA_DIALOG" desc="Title of the button that will open the clear browsing data dialog.">
+ <message name="IDS_HISTORY_OPEN_CLEAR_BROWSING_DATA_DIALOG" desc="Title of the button that will open the clear browsing data dialog [Length: 16em].">
Clear browsing data...
</message>
</if>
diff --git a/chromium/components/image_fetcher/DEPS b/chromium/components/image_fetcher/DEPS
index aec4d91f66d..aa4abf54081 100644
--- a/chromium/components/image_fetcher/DEPS
+++ b/chromium/components/image_fetcher/DEPS
@@ -1,6 +1,8 @@
include_rules = [
"+components/data_use_measurement/core",
"+net",
+ "+services/network/public/cpp",
+ "+services/network/test",
"+ui/gfx/geometry",
"+ui/gfx/image",
"+url",
diff --git a/chromium/components/image_fetcher/core/BUILD.gn b/chromium/components/image_fetcher/core/BUILD.gn
index 2dab433ef04..f9d557648c2 100644
--- a/chromium/components/image_fetcher/core/BUILD.gn
+++ b/chromium/components/image_fetcher/core/BUILD.gn
@@ -10,6 +10,7 @@ static_library("core") {
"image_fetcher.h",
"image_fetcher_impl.cc",
"image_fetcher_impl.h",
+ "image_fetcher_types.h",
"request_metadata.cc",
"request_metadata.h",
]
@@ -18,6 +19,7 @@ static_library("core") {
"//base",
"//components/data_use_measurement/core",
"//net",
+ "//services/network/public/cpp",
"//ui/gfx",
"//ui/gfx/geometry",
"//url",
@@ -35,6 +37,7 @@ static_library("test_support") {
public_deps = [
":core",
+ "//services/network:test_support",
"//testing/gmock",
]
}
diff --git a/chromium/components/image_fetcher/core/image_data_fetcher.cc b/chromium/components/image_fetcher/core/image_data_fetcher.cc
index 069e369a778..2ebb33f218a 100644
--- a/chromium/components/image_fetcher/core/image_data_fetcher.cc
+++ b/chromium/components/image_fetcher/core/image_data_fetcher.cc
@@ -9,9 +9,10 @@
#include "net/base/load_flags.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_status.h"
+#include "net/url_request/url_request.h" // for ReferrerPolicy
+#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"
using data_use_measurement::DataUseUserData;
@@ -24,14 +25,12 @@ const char kContentLocationHeader[] = "Content-Location";
namespace image_fetcher {
-const int ImageDataFetcher::kFirstUrlFetcherId = 163163;
-
// An active image URL fetcher request. The struct contains the related requests
// state.
struct ImageDataFetcher::ImageDataFetcherRequest {
- ImageDataFetcherRequest(const ImageDataFetcherCallback& callback,
- std::unique_ptr<net::URLFetcher> url_fetcher)
- : callback(callback), url_fetcher(std::move(url_fetcher)) {}
+ ImageDataFetcherRequest(ImageDataFetcherCallback callback,
+ std::unique_ptr<network::SimpleURLLoader> loader)
+ : callback(std::move(callback)), loader(std::move(loader)) {}
~ImageDataFetcherRequest() {}
@@ -39,111 +38,127 @@ struct ImageDataFetcher::ImageDataFetcherRequest {
// be run even if the image data could not be fetched successfully.
ImageDataFetcherCallback callback;
- std::unique_ptr<net::URLFetcher> url_fetcher;
+ std::unique_ptr<network::SimpleURLLoader> loader;
};
ImageDataFetcher::ImageDataFetcher(
- net::URLRequestContextGetter* url_request_context_getter)
- : url_request_context_getter_(url_request_context_getter),
- data_use_service_name_(DataUseUserData::IMAGE_FETCHER_UNTAGGED),
- next_url_fetcher_id_(kFirstUrlFetcherId) {}
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+ : url_loader_factory_(url_loader_factory),
+ data_use_service_name_(DataUseUserData::IMAGE_FETCHER_UNTAGGED) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
-ImageDataFetcher::~ImageDataFetcher() {}
+ImageDataFetcher::~ImageDataFetcher() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
void ImageDataFetcher::SetDataUseServiceName(
DataUseServiceName data_use_service_name) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
data_use_service_name_ = data_use_service_name;
}
void ImageDataFetcher::SetImageDownloadLimit(
base::Optional<int64_t> max_download_bytes) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
max_download_bytes_ = max_download_bytes;
}
void ImageDataFetcher::FetchImageData(
const GURL& image_url,
- const ImageDataFetcherCallback& callback,
+ ImageDataFetcherCallback callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation) {
FetchImageData(
- image_url, callback, /*referrer=*/std::string(),
+ image_url, std::move(callback), /*referrer=*/std::string(),
net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
traffic_annotation);
}
void ImageDataFetcher::FetchImageData(
const GURL& image_url,
- const ImageDataFetcherCallback& callback,
+ ImageDataFetcherCallback callback,
const std::string& referrer,
net::URLRequest::ReferrerPolicy referrer_policy,
const net::NetworkTrafficAnnotationTag& traffic_annotation) {
- std::unique_ptr<net::URLFetcher> url_fetcher =
- net::URLFetcher::Create(next_url_fetcher_id_++, image_url,
- net::URLFetcher::GET, this, traffic_annotation);
-
- DataUseUserData::AttachToFetcher(url_fetcher.get(), data_use_service_name_);
-
- std::unique_ptr<ImageDataFetcherRequest> request(
- new ImageDataFetcherRequest(callback, std::move(url_fetcher)));
- request->url_fetcher->SetRequestContext(url_request_context_getter_.get());
- request->url_fetcher->SetReferrer(referrer);
- request->url_fetcher->SetReferrerPolicy(referrer_policy);
- request->url_fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DO_NOT_SEND_AUTH_DATA);
- request->url_fetcher->Start();
-
- pending_requests_[request->url_fetcher.get()] = std::move(request);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ auto request = std::make_unique<network::ResourceRequest>();
+ request->url = image_url;
+ request->referrer_policy = referrer_policy;
+ request->referrer = GURL(referrer);
+ request->load_flags = net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DO_NOT_SEND_AUTH_DATA;
+
+ // TODO(https://crbug.com/808498) re-add data use measurement once
+ // SimpleURLLoader supports it. Parameter:
+ // data_use_service_name_
+
+ std::unique_ptr<network::SimpleURLLoader> loader =
+ network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
+
+ // For compatibility in error handling. This is a little wasteful since the
+ // body will get thrown out anyway, though.
+ loader->SetAllowHttpErrorResults(true);
+
+ if (max_download_bytes_.has_value()) {
+ loader->DownloadToString(
+ url_loader_factory_.get(),
+ base::BindOnce(&ImageDataFetcher::OnURLLoaderComplete,
+ base::Unretained(this), loader.get()),
+ max_download_bytes_.value());
+ } else {
+ loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&ImageDataFetcher::OnURLLoaderComplete,
+ base::Unretained(this), loader.get()));
+ }
+
+ std::unique_ptr<ImageDataFetcherRequest> request_track(
+ new ImageDataFetcherRequest(std::move(callback), std::move(loader)));
+
+ pending_requests_[request_track->loader.get()] = std::move(request_track);
}
-void ImageDataFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
+void ImageDataFetcher::OnURLLoaderComplete(
+ const network::SimpleURLLoader* source,
+ std::unique_ptr<std::string> response_body) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(pending_requests_.find(source) != pending_requests_.end());
- bool success = source->GetStatus().status() == net::URLRequestStatus::SUCCESS;
+ bool success = source->NetError() == net::OK;
RequestMetadata metadata;
- if (success && source->GetResponseHeaders()) {
- source->GetResponseHeaders()->GetMimeType(&metadata.mime_type);
- metadata.http_response_code = source->GetResponseHeaders()->response_code();
+ if (success && source->ResponseInfo() && source->ResponseInfo()->headers) {
+ net::HttpResponseHeaders* headers = source->ResponseInfo()->headers.get();
+ metadata.mime_type = source->ResponseInfo()->mime_type;
+ metadata.http_response_code = headers->response_code();
// Just read the first value-pair for this header (not caring about |iter|).
- source->GetResponseHeaders()->EnumerateHeader(
+ headers->EnumerateHeader(
/*iter=*/nullptr, kContentLocationHeader,
&metadata.content_location_header);
success &= (metadata.http_response_code == net::HTTP_OK);
}
std::string image_data;
- if (success) {
- source->GetResponseAsString(&image_data);
+ if (success && response_body) {
+ image_data = std::move(*response_body);
}
FinishRequest(source, metadata, image_data);
}
-void ImageDataFetcher::OnURLFetchDownloadProgress(
- const net::URLFetcher* source,
- int64_t current,
- int64_t total,
- int64_t current_network_bytes) {
- if (!max_download_bytes_.has_value()) {
- return;
- }
- if (total <= max_download_bytes_.value() &&
- current <= max_download_bytes_.value()) {
- return;
- }
- DCHECK(pending_requests_.find(source) != pending_requests_.end());
- DLOG(WARNING) << "Image data exceeded download size limit.";
- RequestMetadata metadata;
- metadata.http_response_code = net::URLFetcher::RESPONSE_CODE_INVALID;
-
- FinishRequest(source, metadata, /*image_data=*/std::string());
-}
-
-void ImageDataFetcher::FinishRequest(const net::URLFetcher* source,
+void ImageDataFetcher::FinishRequest(const network::SimpleURLLoader* source,
const RequestMetadata& metadata,
const std::string& image_data) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto request_iter = pending_requests_.find(source);
DCHECK(request_iter != pending_requests_.end());
- request_iter->second->callback.Run(image_data, metadata);
+ std::move(request_iter->second->callback).Run(image_data, metadata);
pending_requests_.erase(request_iter);
}
+void ImageDataFetcher::InjectResultForTesting(const RequestMetadata& metadata,
+ const std::string& image_data) {
+ DCHECK_EQ(pending_requests_.size(), 1u);
+ FinishRequest(pending_requests_.begin()->first, metadata, image_data);
+}
+
} // namespace image_fetcher
diff --git a/chromium/components/image_fetcher/core/image_data_fetcher.h b/chromium/components/image_fetcher/core/image_data_fetcher.h
index fb7558d5c2b..7dfe059ad24 100644
--- a/chromium/components/image_fetcher/core/image_data_fetcher.h
+++ b/chromium/components/image_fetcher/core/image_data_fetcher.h
@@ -13,46 +13,34 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/optional.h"
+#include "base/sequence_checker.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
+#include "components/image_fetcher/core/image_fetcher_types.h"
#include "components/image_fetcher/core/request_metadata.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request.h"
#include "url/gurl.h"
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
-} // namespace net
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+} // namespace network
namespace image_fetcher {
-class ImageDataFetcher : public net::URLFetcherDelegate {
+class ImageDataFetcher {
public:
- // Fetchers created by this class will be assigned an incremental id starting
- // from |kFirstUrlFetcherId|, so unit tests can differentiate the URLFetchers
- // used by this class from other fetchers.
- const static int kFirstUrlFetcherId;
-
- // Callback with the |image_data|. If an error prevented a http response,
- // |request_metadata.response_code| will be RESPONSE_CODE_INVALID.
- // TODO(treib): Pass |image_data| out by value, or use RefCountedBytes, to
- // avoid copying.
- using ImageDataFetcherCallback =
- base::Callback<void(const std::string& image_data,
- const RequestMetadata& request_metadata)>;
-
- using DataUseServiceName = data_use_measurement::DataUseUserData::ServiceName;
-
+ // Note that this must be used consistently on the thread that owns
+ // |url_loader_factory|. See SharedURLLoaderFactory::Clone() if changing
+ // thread is required.
explicit ImageDataFetcher(
- net::URLRequestContextGetter* url_request_context_getter);
- ~ImageDataFetcher() override;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+ ~ImageDataFetcher();
// Sets a service name against which to track data usage.
void SetDataUseServiceName(DataUseServiceName data_use_service_name);
// Sets an upper limit for image downloads.
- // Already running downloads are affected.
+ // Already running downloads are not affected.
void SetImageDownloadLimit(base::Optional<int64_t> max_download_bytes);
// Fetches the raw image bytes from the given |image_url| and calls the given
@@ -60,49 +48,47 @@ class ImageDataFetcher : public net::URLFetcherDelegate {
// of an error an empty string is passed to the callback.
void FetchImageData(
const GURL& image_url,
- const ImageDataFetcherCallback& callback,
+ ImageDataFetcherCallback callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation);
// Like above, but lets the caller set a referrer.
void FetchImageData(
const GURL& image_url,
- const ImageDataFetcherCallback& callback,
+ ImageDataFetcherCallback callback,
const std::string& referrer,
net::URLRequest::ReferrerPolicy referrer_policy,
const net::NetworkTrafficAnnotationTag& traffic_annotation);
+ // Test-only method to inject a fetch result directly, w/o regard for how the
+ // underlying loading is doing. This requires there to be a single pending
+ // fetch only.
+ void InjectResultForTesting(const RequestMetadata& metadata,
+ const std::string& image_data);
+
private:
struct ImageDataFetcherRequest;
- // Methods inherited from URLFetcherDelegate
- void OnURLFetchComplete(const net::URLFetcher* source) override;
- void OnURLFetchDownloadProgress(const net::URLFetcher* source,
- int64_t current,
- int64_t total,
- int64_t current_network_bytes) override;
+ void OnURLLoaderComplete(const network::SimpleURLLoader* source,
+ std::unique_ptr<std::string> response_body);
- void FinishRequest(const net::URLFetcher* source,
+ void FinishRequest(const network::SimpleURLLoader* source,
const RequestMetadata& metadata,
const std::string& image_data);
// All active image url requests.
- std::map<const net::URLFetcher*, std::unique_ptr<ImageDataFetcherRequest>>
+ std::map<const network::SimpleURLLoader*,
+ std::unique_ptr<ImageDataFetcherRequest>>
pending_requests_;
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
DataUseServiceName data_use_service_name_;
- // The next ID to use for a newly created URLFetcher. Each URLFetcher gets an
- // id when it is created. The |url_fetcher_id_| is incremented by one for each
- // newly created URLFetcher. The URLFetcher ID can be used during testing to
- // get individual URLFetchers and modify their state. Outside of tests this ID
- // is not used.
- int next_url_fetcher_id_;
-
// Upper limit for the number of bytes to download per image.
base::Optional<int64_t> max_download_bytes_;
+ SEQUENCE_CHECKER(sequence_checker_);
+
DISALLOW_COPY_AND_ASSIGN(ImageDataFetcher);
};
diff --git a/chromium/components/image_fetcher/core/image_data_fetcher_unittest.cc b/chromium/components/image_fetcher/core/image_data_fetcher_unittest.cc
index 738cc1efb3f..4c9c32294a9 100644
--- a/chromium/components/image_fetcher/core/image_data_fetcher_unittest.cc
+++ b/chromium/components/image_fetcher/core/image_data_fetcher_unittest.cc
@@ -13,10 +13,12 @@
#include "net/base/load_flags.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
+#include "net/http/http_util.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_status.h"
#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -32,9 +34,10 @@ namespace image_fetcher {
class ImageDataFetcherTest : public testing::Test {
public:
ImageDataFetcherTest()
- : test_request_context_getter_(
- new net::TestURLRequestContextGetter(message_loop_.task_runner())),
- image_data_fetcher_(test_request_context_getter_.get()) {}
+ : shared_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)),
+ image_data_fetcher_(shared_factory_) {}
~ImageDataFetcherTest() override {}
MOCK_METHOD2(OnImageDataFetched,
@@ -49,59 +52,58 @@ class ImageDataFetcherTest : public testing::Test {
protected:
base::MessageLoop message_loop_;
- scoped_refptr<net::URLRequestContextGetter> test_request_context_getter_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> shared_factory_;
ImageDataFetcher image_data_fetcher_;
- net::TestURLFetcherFactory fetcher_factory_;
-
private:
DISALLOW_COPY_AND_ASSIGN(ImageDataFetcherTest);
};
TEST_F(ImageDataFetcherTest, FetchImageData) {
+ std::string content = kURLResponseData;
+
image_data_fetcher_.FetchImageData(
GURL(kImageURL),
- base::Bind(&ImageDataFetcherTest::OnImageDataFetched,
- base::Unretained(this)),
+ base::BindOnce(&ImageDataFetcherTest::OnImageDataFetched,
+ base::Unretained(this)),
TRAFFIC_ANNOTATION_FOR_TESTS);
RequestMetadata expected_metadata;
expected_metadata.mime_type = std::string("image/png");
expected_metadata.http_response_code = net::HTTP_OK;
- EXPECT_CALL(*this, OnImageDataFetched(std::string(kURLResponseData),
- expected_metadata));
-
- // Get and configure the TestURLFetcher.
- net::TestURLFetcher* test_url_fetcher =
- fetcher_factory_.GetFetcherByID(ImageDataFetcher::kFirstUrlFetcherId);
- ASSERT_NE(nullptr, test_url_fetcher);
- EXPECT_TRUE(test_url_fetcher->GetLoadFlags() & net::LOAD_DO_NOT_SEND_COOKIES);
- EXPECT_TRUE(test_url_fetcher->GetLoadFlags() & net::LOAD_DO_NOT_SAVE_COOKIES);
- EXPECT_TRUE(test_url_fetcher->GetLoadFlags() &
- net::LOAD_DO_NOT_SEND_AUTH_DATA);
- test_url_fetcher->set_status(
- net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK));
- test_url_fetcher->SetResponseString(kURLResponseData);
- test_url_fetcher->set_response_code(net::HTTP_OK);
-
+ EXPECT_CALL(*this, OnImageDataFetched(content, expected_metadata));
+
+ // Check to make sure the request is pending with proper flags, and
+ // provide a response.
+ int pending_load_flags = 0;
+ EXPECT_TRUE(
+ test_url_loader_factory_.IsPending(kImageURL, &pending_load_flags));
+ EXPECT_TRUE(pending_load_flags & net::LOAD_DO_NOT_SEND_COOKIES);
+ EXPECT_TRUE(pending_load_flags & net::LOAD_DO_NOT_SAVE_COOKIES);
+ EXPECT_TRUE(pending_load_flags & net::LOAD_DO_NOT_SEND_AUTH_DATA);
+
+ network::ResourceResponseHead head;
std::string raw_header =
"HTTP/1.1 200 OK\n"
"Content-type: image/png\n\n";
- std::replace(raw_header.begin(), raw_header.end(), '\n', '\0');
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(raw_header));
- test_url_fetcher->set_response_headers(headers);
-
- // Call the URLFetcher delegate to continue the test.
- test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher);
+ head.headers = new net::HttpResponseHeaders(
+ net::HttpUtil::AssembleRawHeaders(raw_header.c_str(), raw_header.size()));
+ head.mime_type = "image/png";
+ network::URLLoaderCompletionStatus status;
+ status.decoded_body_length = content.size();
+ test_url_loader_factory_.AddResponse(GURL(kImageURL), head, content, status);
+ base::RunLoop().RunUntilIdle();
}
TEST_F(ImageDataFetcherTest, FetchImageData_NotFound) {
+ std::string content = kURLResponseData;
+
image_data_fetcher_.FetchImageData(
GURL(kImageURL),
- base::Bind(&ImageDataFetcherTest::OnImageDataFetched,
- base::Unretained(this)),
+ base::BindOnce(&ImageDataFetcherTest::OnImageDataFetched,
+ base::Unretained(this)),
TRAFFIC_ANNOTATION_FOR_TESTS);
RequestMetadata expected_metadata;
@@ -110,31 +112,29 @@ TEST_F(ImageDataFetcherTest, FetchImageData_NotFound) {
// For 404, expect an empty result even though correct image data is sent.
EXPECT_CALL(*this, OnImageDataFetched(std::string(), expected_metadata));
- // Get and configure the TestURLFetcher.
- net::TestURLFetcher* test_url_fetcher =
- fetcher_factory_.GetFetcherByID(ImageDataFetcher::kFirstUrlFetcherId);
- ASSERT_NE(nullptr, test_url_fetcher);
- test_url_fetcher->set_status(
- net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK));
- test_url_fetcher->SetResponseString(kURLResponseData);
+ // Check to make sure the request is pending, and provide a response.
+ EXPECT_TRUE(test_url_loader_factory_.IsPending(kImageURL));
+ network::ResourceResponseHead head;
std::string raw_header =
"HTTP/1.1 404 Not Found\n"
"Content-type: image/png\n\n";
- std::replace(raw_header.begin(), raw_header.end(), '\n', '\0');
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(raw_header));
- test_url_fetcher->set_response_headers(headers);
-
- // Call the URLFetcher delegate to continue the test.
- test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher);
+ head.headers = new net::HttpResponseHeaders(
+ net::HttpUtil::AssembleRawHeaders(raw_header.c_str(), raw_header.size()));
+ head.mime_type = "image/png";
+ network::URLLoaderCompletionStatus status;
+ status.decoded_body_length = content.size();
+ test_url_loader_factory_.AddResponse(GURL(kImageURL), head, content, status);
+ base::RunLoop().RunUntilIdle();
}
TEST_F(ImageDataFetcherTest, FetchImageData_WithContentLocation) {
+ std::string content = kURLResponseData;
+
image_data_fetcher_.FetchImageData(
GURL(kImageURL),
- base::Bind(&ImageDataFetcherTest::OnImageDataFetched,
- base::Unretained(this)),
+ base::BindOnce(&ImageDataFetcherTest::OnImageDataFetched,
+ base::Unretained(this)),
TRAFFIC_ANNOTATION_FOR_TESTS);
RequestMetadata expected_metadata;
@@ -144,32 +144,28 @@ TEST_F(ImageDataFetcherTest, FetchImageData_WithContentLocation) {
// For 404, expect an empty result even though correct image data is sent.
EXPECT_CALL(*this, OnImageDataFetched(std::string(), expected_metadata));
- // Get and configure the TestURLFetcher.
- net::TestURLFetcher* test_url_fetcher =
- fetcher_factory_.GetFetcherByID(ImageDataFetcher::kFirstUrlFetcherId);
- ASSERT_NE(nullptr, test_url_fetcher);
- test_url_fetcher->set_status(
- net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK));
- test_url_fetcher->SetResponseString(kURLResponseData);
+ // Check to make sure the request is pending, and provide a response.
+ EXPECT_TRUE(test_url_loader_factory_.IsPending(kImageURL));
+ network::ResourceResponseHead head;
std::string raw_header =
"HTTP/1.1 404 Not Found\n"
"Content-type: image/png\n"
"Content-location: http://test-location/image.png\n\n";
- std::replace(raw_header.begin(), raw_header.end(), '\n', '\0');
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(raw_header));
- test_url_fetcher->set_response_headers(headers);
-
- // Call the URLFetcher delegate to continue the test.
- test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher);
+ head.headers = new net::HttpResponseHeaders(
+ net::HttpUtil::AssembleRawHeaders(raw_header.c_str(), raw_header.size()));
+ head.mime_type = "image/png";
+ network::URLLoaderCompletionStatus status;
+ status.decoded_body_length = content.size();
+ test_url_loader_factory_.AddResponse(GURL(kImageURL), head, content, status);
+ base::RunLoop().RunUntilIdle();
}
TEST_F(ImageDataFetcherTest, FetchImageData_FailedRequest) {
image_data_fetcher_.FetchImageData(
GURL(kImageURL),
- base::Bind(&ImageDataFetcherTest::OnImageDataFetchedFailedRequest,
- base::Unretained(this)),
+ base::BindOnce(&ImageDataFetcherTest::OnImageDataFetchedFailedRequest,
+ base::Unretained(this)),
TRAFFIC_ANNOTATION_FOR_TESTS);
RequestMetadata expected_metadata;
@@ -177,53 +173,48 @@ TEST_F(ImageDataFetcherTest, FetchImageData_FailedRequest) {
EXPECT_CALL(
*this, OnImageDataFetchedFailedRequest(std::string(), expected_metadata));
- // Get and configure the TestURLFetcher.
- net::TestURLFetcher* test_url_fetcher =
- fetcher_factory_.GetFetcherByID(ImageDataFetcher::kFirstUrlFetcherId);
- ASSERT_NE(nullptr, test_url_fetcher);
- test_url_fetcher->set_status(net::URLRequestStatus(
- net::URLRequestStatus::FAILED, net::ERR_INVALID_URL));
+ // Check to make sure the request is pending, and provide a response.
+ EXPECT_TRUE(test_url_loader_factory_.IsPending(kImageURL));
- // Call the URLFetcher delegate to continue the test.
- test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher);
+ network::ResourceResponseHead head;
+ head.mime_type = "image/png";
+ network::URLLoaderCompletionStatus status;
+ status.error_code = net::ERR_INVALID_URL;
+ test_url_loader_factory_.AddResponse(GURL(kImageURL), head, "", status);
+ base::RunLoop().RunUntilIdle();
}
TEST_F(ImageDataFetcherTest, FetchImageData_MultipleRequests) {
- ImageDataFetcher::ImageDataFetcherCallback callback =
- base::Bind(&ImageDataFetcherTest::OnImageDataFetchedMultipleRequests,
- base::Unretained(this));
EXPECT_CALL(*this, OnImageDataFetchedMultipleRequests(testing::_, testing::_))
.Times(2);
- image_data_fetcher_.FetchImageData(GURL(kImageURL), callback,
- TRAFFIC_ANNOTATION_FOR_TESTS);
- image_data_fetcher_.FetchImageData(GURL(kImageURL), callback,
- TRAFFIC_ANNOTATION_FOR_TESTS);
+ image_data_fetcher_.FetchImageData(
+ GURL(kImageURL),
+ base::BindOnce(&ImageDataFetcherTest::OnImageDataFetchedMultipleRequests,
+ base::Unretained(this)),
+ TRAFFIC_ANNOTATION_FOR_TESTS);
+ image_data_fetcher_.FetchImageData(
+ GURL(kImageURL),
+ base::BindOnce(&ImageDataFetcherTest::OnImageDataFetchedMultipleRequests,
+ base::Unretained(this)),
+ TRAFFIC_ANNOTATION_FOR_TESTS);
// Multiple calls to FetchImageData for the same URL will result in
// multiple URLFetchers being created.
- net::TestURLFetcher* test_url_fetcher =
- fetcher_factory_.GetFetcherByID(ImageDataFetcher::kFirstUrlFetcherId);
- ASSERT_NE(nullptr, test_url_fetcher);
- test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher);
-
- test_url_fetcher =
- fetcher_factory_.GetFetcherByID(ImageDataFetcher::kFirstUrlFetcherId + 1);
- ASSERT_NE(nullptr, test_url_fetcher);
- test_url_fetcher->delegate()->OnURLFetchComplete(test_url_fetcher);
+ EXPECT_EQ(2, test_url_loader_factory_.NumPending());
+ test_url_loader_factory_.AddResponse(kImageURL, "");
+ base::RunLoop().RunUntilIdle();
}
TEST_F(ImageDataFetcherTest, FetchImageData_CancelFetchIfImageExceedsMaxSize) {
- // In order to know whether the fetcher was canceled, it must notify about its
- // deletion.
- fetcher_factory_.set_remove_fetcher_on_delete(true);
+ const int64_t kMaxDownloadBytes = 1024;
+ std::string oversize_download(kMaxDownloadBytes + 1, '#');
- const int64_t kMaxDownloadBytes = 1024 * 1024;
image_data_fetcher_.SetImageDownloadLimit(kMaxDownloadBytes);
image_data_fetcher_.FetchImageData(
GURL(kImageURL),
- base::Bind(&ImageDataFetcherTest::OnImageDataFetched,
- base::Unretained(this)),
+ base::BindOnce(&ImageDataFetcherTest::OnImageDataFetched,
+ base::Unretained(this)),
TRAFFIC_ANNOTATION_FOR_TESTS);
// Fetching an oversized image will behave like any other failed request.
@@ -233,55 +224,9 @@ TEST_F(ImageDataFetcherTest, FetchImageData_CancelFetchIfImageExceedsMaxSize) {
expected_metadata.http_response_code = net::URLFetcher::RESPONSE_CODE_INVALID;
EXPECT_CALL(*this, OnImageDataFetched(std::string(), expected_metadata));
- // Get and configure the TestURLFetcher.
- net::TestURLFetcher* test_url_fetcher =
- fetcher_factory_.GetFetcherByID(ImageDataFetcher::kFirstUrlFetcherId);
- ASSERT_NE(nullptr, test_url_fetcher);
-
- // Create a completely valid response that is never used. This is to make sure
- // that the answer isn't accidentally invalid but intentionally.
- test_url_fetcher->set_status(
- net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK));
- test_url_fetcher->SetResponseString(kURLResponseData);
- test_url_fetcher->set_response_code(net::HTTP_OK);
- std::string raw_header =
- "HTTP/1.1 200 OK\n"
- "Content-type: image/png\n\n";
- std::replace(raw_header.begin(), raw_header.end(), '\n', '\0');
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(raw_header));
- test_url_fetcher->set_response_headers(headers);
-
- test_url_fetcher->delegate()->OnURLFetchDownloadProgress(
- test_url_fetcher,
- /*current=*/0, // Bytes received up to the call.
- /*total=*/-1, // not determined
- /*current_network_bytes=*/0); // not relevant
- // The URL fetch should be running ...
- ASSERT_NE(nullptr, fetcher_factory_.GetFetcherByID(
- ImageDataFetcher::kFirstUrlFetcherId));
-
- test_url_fetcher->delegate()->OnURLFetchDownloadProgress(
- test_url_fetcher,
- 768 * 1024, // Current bytes are not exeeding the limit.
- /*total=*/-1, /*current_network_bytes=*/0);
- // ... and running ...
- ASSERT_NE(nullptr, fetcher_factory_.GetFetcherByID(
- ImageDataFetcher::kFirstUrlFetcherId));
-
- test_url_fetcher->delegate()->OnURLFetchDownloadProgress(
- test_url_fetcher, kMaxDownloadBytes, // Still not exeeding the limit.
- /*total=*/-1, /*current_network_bytes=*/0);
- // ... and running ...
- ASSERT_NE(nullptr, fetcher_factory_.GetFetcherByID(
- ImageDataFetcher::kFirstUrlFetcherId));
-
- test_url_fetcher->delegate()->OnURLFetchDownloadProgress(
- test_url_fetcher, kMaxDownloadBytes + 1, // Limits are exceeded.
- /*total=*/-1, /*current_network_bytes=*/0);
- // ... and be canceled.
- EXPECT_EQ(nullptr, fetcher_factory_.GetFetcherByID(
- ImageDataFetcher::kFirstUrlFetcherId));
+ EXPECT_TRUE(test_url_loader_factory_.IsPending(kImageURL));
+ test_url_loader_factory_.AddResponse(kImageURL, oversize_download);
+ base::RunLoop().RunUntilIdle();
}
} // namespace image_fetcher
diff --git a/chromium/components/image_fetcher/core/image_fetcher.h b/chromium/components/image_fetcher/core/image_fetcher.h
index 086e7f4b49f..33fa25dc430 100644
--- a/chromium/components/image_fetcher/core/image_fetcher.h
+++ b/chromium/components/image_fetcher/core/image_fetcher.h
@@ -10,12 +10,11 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/optional.h"
-#include "components/data_use_measurement/core/data_use_user_data.h"
+#include "components/image_fetcher/core/image_fetcher_types.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "url/gurl.h"
namespace gfx {
-class Image;
class Size;
} // namespace gfx
@@ -23,8 +22,6 @@ namespace image_fetcher {
class ImageDecoder;
-struct RequestMetadata;
-
// A class used to fetch server images. It can be called from any thread and the
// callback will be called on the thread which initiated the fetch.
class ImageFetcher {
@@ -32,27 +29,13 @@ class ImageFetcher {
ImageFetcher() {}
virtual ~ImageFetcher() {}
- using ImageFetcherCallback =
- base::OnceCallback<void(const std::string& id,
- const gfx::Image& image,
- const RequestMetadata& metadata)>;
-
- // Callback with the |image_data|. If an error prevented a http response,
- // |request_metadata.response_code| will be RESPONSE_CODE_INVALID.
- // TODO(treib): Use RefCountedBytes to avoid copying.
- using ImageDataFetcherCallback =
- base::OnceCallback<void(const std::string& image_data,
- const RequestMetadata& request_metadata)>;
-
- using DataUseServiceName = data_use_measurement::DataUseUserData::ServiceName;
-
// Sets a service name against which to track data usage.
virtual void SetDataUseServiceName(
DataUseServiceName data_use_service_name) = 0;
// Sets an upper limit for image downloads that is by default disabled.
// Setting |max_download_bytes| to a negative value will disable the limit.
- // Already running downloads are immediately affected.
+ // Already running downloads are not affected.
virtual void SetImageDownloadLimit(
base::Optional<int64_t> max_download_bytes) = 0;
diff --git a/chromium/components/image_fetcher/core/image_fetcher_impl.cc b/chromium/components/image_fetcher/core/image_fetcher_impl.cc
index 5b984243b72..3b1615f8259 100644
--- a/chromium/components/image_fetcher/core/image_fetcher_impl.cc
+++ b/chromium/components/image_fetcher/core/image_fetcher_impl.cc
@@ -9,17 +9,17 @@
#include "base/bind.h"
#include "base/threading/thread_task_runner_handle.h"
#include "net/base/load_flags.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "ui/gfx/image/image.h"
namespace image_fetcher {
ImageFetcherImpl::ImageFetcherImpl(
std::unique_ptr<ImageDecoder> image_decoder,
- net::URLRequestContextGetter* url_request_context)
- : url_request_context_(url_request_context),
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+ : url_loader_factory_(url_loader_factory),
image_decoder_(std::move(image_decoder)),
- image_data_fetcher_(new ImageDataFetcher(url_request_context_.get())) {}
+ image_data_fetcher_(new ImageDataFetcher(url_loader_factory)) {}
ImageFetcherImpl::~ImageFetcherImpl() {}
diff --git a/chromium/components/image_fetcher/core/image_fetcher_impl.h b/chromium/components/image_fetcher/core/image_fetcher_impl.h
index 05c0b4680dc..0b7a2eb2af6 100644
--- a/chromium/components/image_fetcher/core/image_fetcher_impl.h
+++ b/chromium/components/image_fetcher/core/image_fetcher_impl.h
@@ -24,8 +24,8 @@ namespace gfx {
class Image;
}
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
}
namespace image_fetcher {
@@ -33,8 +33,9 @@ namespace image_fetcher {
// The standard (non-test) implementation of ImageFetcher.
class ImageFetcherImpl : public ImageFetcher {
public:
- ImageFetcherImpl(std::unique_ptr<ImageDecoder> image_decoder,
- net::URLRequestContextGetter* url_request_context);
+ ImageFetcherImpl(
+ std::unique_ptr<ImageDecoder> image_decoder,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
~ImageFetcherImpl() override;
// Sets a service name against which to track data usage.
@@ -89,7 +90,7 @@ class ImageFetcherImpl : public ImageFetcher {
gfx::Size desired_image_frame_size_;
- scoped_refptr<net::URLRequestContextGetter> url_request_context_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
std::unique_ptr<ImageDecoder> image_decoder_;
diff --git a/chromium/components/image_fetcher/core/image_fetcher_impl_unittest.cc b/chromium/components/image_fetcher/core/image_fetcher_impl_unittest.cc
index e6280d29ec8..8b556eb9d8a 100644
--- a/chromium/components/image_fetcher/core/image_fetcher_impl_unittest.cc
+++ b/chromium/components/image_fetcher/core/image_fetcher_impl_unittest.cc
@@ -14,9 +14,10 @@
#include "base/test/scoped_task_environment.h"
#include "components/image_fetcher/core/image_decoder.h"
#include "components/image_fetcher/core/image_fetcher.h"
+#include "net/http/http_util.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/image/image.h"
@@ -67,30 +68,29 @@ class FakeImageDecoder : public image_fetcher::ImageDecoder {
class ImageFetcherImplTest : public testing::Test {
public:
- ImageFetcherImplTest() : fake_url_fetcher_factory_(nullptr) {
- request_context_getter_ = scoped_refptr<net::TestURLRequestContextGetter>(
- new net::TestURLRequestContextGetter(
- scoped_task_environment_.GetMainThreadTaskRunner()));
-
+ ImageFetcherImplTest()
+ : shared_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)) {
auto decoder = std::make_unique<FakeImageDecoder>();
fake_image_decoder_ = decoder.get();
image_fetcher_ = std::make_unique<image_fetcher::ImageFetcherImpl>(
- std::move(decoder), request_context_getter_.get());
+ std::move(decoder), shared_factory_);
}
void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
FakeImageDecoder* image_decoder() { return fake_image_decoder_; }
- net::FakeURLFetcherFactory* fake_url_fetcher_factory() {
- return &fake_url_fetcher_factory_;
+ network::TestURLLoaderFactory* test_url_loader_factory() {
+ return &test_url_loader_factory_;
}
ImageFetcherImpl* image_fetcher() { return image_fetcher_.get(); }
private:
std::unique_ptr<ImageFetcherImpl> image_fetcher_;
FakeImageDecoder* fake_image_decoder_;
- net::FakeURLFetcherFactory fake_url_fetcher_factory_;
- scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> shared_factory_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
DISALLOW_COPY_AND_ASSIGN(ImageFetcherImplTest);
@@ -103,11 +103,9 @@ MATCHER(EmptyImage, "") {
}
TEST_F(ImageFetcherImplTest, FetchImageAndDataSuccess) {
- fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
- net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback;
- base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback;
+ test_url_loader_factory()->AddResponse(kImageURL, kImageData);
+ base::MockCallback<ImageDataFetcherCallback> data_callback;
+ base::MockCallback<ImageFetcherCallback> image_callback;
EXPECT_CALL(data_callback, Run(kImageData, _));
EXPECT_CALL(image_callback, Run(kFetchID, ValidImage(), _));
@@ -119,11 +117,9 @@ TEST_F(ImageFetcherImplTest, FetchImageAndDataSuccess) {
TEST_F(ImageFetcherImplTest, FetchImageAndData3xSuccess) {
// Fetch an image three times.
- fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
- net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback1;
- base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback1;
+ test_url_loader_factory()->AddResponse(kImageURL, kImageData);
+ base::MockCallback<ImageDataFetcherCallback> data_callback1;
+ base::MockCallback<ImageFetcherCallback> image_callback1;
EXPECT_CALL(data_callback1, Run(kImageData, _));
EXPECT_CALL(image_callback1, Run(kFetchID, ValidImage(), _));
@@ -131,8 +127,8 @@ TEST_F(ImageFetcherImplTest, FetchImageAndData3xSuccess) {
kFetchID, GURL(kImageURL), data_callback1.Get(), image_callback1.Get(),
TRAFFIC_ANNOTATION_FOR_TESTS);
- base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback2;
- base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback2;
+ base::MockCallback<ImageDataFetcherCallback> data_callback2;
+ base::MockCallback<ImageFetcherCallback> image_callback2;
EXPECT_CALL(data_callback2, Run(kImageData, _));
EXPECT_CALL(image_callback2, Run(kFetchID, ValidImage(), _));
@@ -141,17 +137,15 @@ TEST_F(ImageFetcherImplTest, FetchImageAndData3xSuccess) {
kFetchID, GURL(kImageURL), data_callback2.Get(), image_callback2.Get(),
TRAFFIC_ANNOTATION_FOR_TESTS);
- base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback3;
- base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback3;
+ base::MockCallback<ImageDataFetcherCallback> data_callback3;
+ base::MockCallback<ImageFetcherCallback> image_callback3;
EXPECT_CALL(data_callback3, Run(kImageData, _));
EXPECT_CALL(image_callback3, Run(kFetchID, ValidImage(), _));
image_decoder()->SetBeforeImageDecoded(base::BindLambdaForTesting([&]() {
// This happens after the network request completes.
// Shouldn't need to fetch.
- fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), "",
- net::HTTP_NOT_FOUND,
- net::URLRequestStatus::FAILED);
+ test_url_loader_factory()->AddResponse(kImageURL, "", net::HTTP_NOT_FOUND);
image_fetcher()->FetchImageAndData(
kFetchID2, GURL(kImageURL), data_callback3.Get(), image_callback3.Get(),
TRAFFIC_ANNOTATION_FOR_TESTS);
@@ -163,10 +157,9 @@ TEST_F(ImageFetcherImplTest, FetchImageAndData3xSuccess) {
TEST_F(ImageFetcherImplTest, FetchImageAndData2xFail) {
// Fetch an image two times. The fetch fails.
image_decoder()->SetEnabled(false);
- fake_url_fetcher_factory()->SetFakeResponse(
- GURL(kImageURL), "", net::HTTP_NOT_FOUND, net::URLRequestStatus::FAILED);
- base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback1;
- base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback1;
+ test_url_loader_factory()->AddResponse(kImageURL, "", net::HTTP_NOT_FOUND);
+ base::MockCallback<ImageDataFetcherCallback> data_callback1;
+ base::MockCallback<ImageFetcherCallback> image_callback1;
EXPECT_CALL(data_callback1, Run("", _));
EXPECT_CALL(image_callback1, Run(kFetchID, EmptyImage(), _));
@@ -174,8 +167,8 @@ TEST_F(ImageFetcherImplTest, FetchImageAndData2xFail) {
kFetchID, GURL(kImageURL), data_callback1.Get(), image_callback1.Get(),
TRAFFIC_ANNOTATION_FOR_TESTS);
- base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback2;
- base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback2;
+ base::MockCallback<ImageDataFetcherCallback> data_callback2;
+ base::MockCallback<ImageFetcherCallback> image_callback2;
EXPECT_CALL(data_callback2, Run("", _));
EXPECT_CALL(image_callback2, Run(kFetchID, EmptyImage(), _));
@@ -188,64 +181,56 @@ TEST_F(ImageFetcherImplTest, FetchImageAndData2xFail) {
TEST_F(ImageFetcherImplTest, FetchOnlyData) {
image_decoder()->SetEnabled(false);
- fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
- net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback;
+ test_url_loader_factory()->AddResponse(kImageURL, kImageData);
+ base::MockCallback<ImageDataFetcherCallback> data_callback;
EXPECT_CALL(data_callback, Run(kImageData, _));
image_fetcher()->FetchImageAndData(
- kFetchID, GURL(kImageURL), data_callback.Get(),
- ImageFetcher::ImageFetcherCallback(), TRAFFIC_ANNOTATION_FOR_TESTS);
+ kFetchID, GURL(kImageURL), data_callback.Get(), ImageFetcherCallback(),
+ TRAFFIC_ANNOTATION_FOR_TESTS);
RunUntilIdle();
}
TEST_F(ImageFetcherImplTest, FetchDataThenImage) {
- fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
- net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback;
+ test_url_loader_factory()->AddResponse(kImageURL, kImageData);
+ base::MockCallback<ImageDataFetcherCallback> data_callback;
EXPECT_CALL(data_callback, Run(kImageData, _));
image_fetcher()->FetchImageAndData(
- kFetchID, GURL(kImageURL), data_callback.Get(),
- ImageFetcher::ImageFetcherCallback(), TRAFFIC_ANNOTATION_FOR_TESTS);
+ kFetchID, GURL(kImageURL), data_callback.Get(), ImageFetcherCallback(),
+ TRAFFIC_ANNOTATION_FOR_TESTS);
- base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback;
+ base::MockCallback<ImageFetcherCallback> image_callback;
EXPECT_CALL(image_callback, Run(kFetchID, ValidImage(), _));
image_fetcher()->FetchImageAndData(
- kFetchID2, GURL(kImageURL), ImageFetcher::ImageDataFetcherCallback(),
+ kFetchID2, GURL(kImageURL), ImageDataFetcherCallback(),
image_callback.Get(), TRAFFIC_ANNOTATION_FOR_TESTS);
RunUntilIdle();
}
TEST_F(ImageFetcherImplTest, FetchImageThenData) {
- fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
- net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ test_url_loader_factory()->AddResponse(kImageURL, kImageData);
- base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback;
+ base::MockCallback<ImageFetcherCallback> image_callback;
EXPECT_CALL(image_callback, Run(kFetchID, ValidImage(), _));
image_fetcher()->FetchImageAndData(
- kFetchID, GURL(kImageURL), ImageFetcher::ImageDataFetcherCallback(),
+ kFetchID, GURL(kImageURL), ImageDataFetcherCallback(),
image_callback.Get(), TRAFFIC_ANNOTATION_FOR_TESTS);
- base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback;
+ base::MockCallback<ImageDataFetcherCallback> data_callback;
EXPECT_CALL(data_callback, Run(kImageData, _));
image_decoder()->SetBeforeImageDecoded(base::BindLambdaForTesting([&]() {
// This happens after the network request completes.
// Shouldn't need to fetch.
- fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), "",
- net::HTTP_NOT_FOUND,
- net::URLRequestStatus::FAILED);
+ test_url_loader_factory()->AddResponse(kImageURL, "", net::HTTP_NOT_FOUND);
image_fetcher()->FetchImageAndData(
- kFetchID2, GURL(kImageURL), data_callback.Get(),
- ImageFetcher::ImageFetcherCallback(), TRAFFIC_ANNOTATION_FOR_TESTS);
+ kFetchID2, GURL(kImageURL), data_callback.Get(), ImageFetcherCallback(),
+ TRAFFIC_ANNOTATION_FOR_TESTS);
}));
RunUntilIdle();
diff --git a/chromium/components/image_fetcher/core/image_fetcher_types.h b/chromium/components/image_fetcher/core/image_fetcher_types.h
new file mode 100644
index 00000000000..84b4986af02
--- /dev/null
+++ b/chromium/components/image_fetcher/core/image_fetcher_types.h
@@ -0,0 +1,51 @@
+// 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_IMAGE_FETCHER_CORE_IMAGE_FETCHER_TYPES_H_
+#define COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_FETCHER_TYPES_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "components/data_use_measurement/core/data_use_user_data.h"
+
+namespace gfx {
+class Image;
+} // namespace gfx
+
+#if defined(__OBJC__)
+@class NSData;
+#endif // defined(__OBJC__)
+
+namespace image_fetcher {
+
+struct RequestMetadata;
+
+using DataUseServiceName = data_use_measurement::DataUseUserData::ServiceName;
+
+using ImageFetcherCallback =
+ base::OnceCallback<void(const std::string& id,
+ const gfx::Image& image,
+ const RequestMetadata& metadata)>;
+
+// Callback with the |image_data|. If an error prevented a http response,
+// |request_metadata.response_code| will be RESPONSE_CODE_INVALID.
+// TODO(treib): Use RefCountedBytes to avoid copying.
+using ImageDataFetcherCallback =
+ base::OnceCallback<void(const std::string& image_data,
+ const RequestMetadata& request_metadata)>;
+
+#if defined(__OBJC__)
+
+// Callback that informs of the download of an image encoded in |data| and the
+// associated metadata. If an error prevented a http response,
+// |metadata.http_response_code| will be RESPONSE_CODE_INVALID.
+using ImageDataFetcherBlock = void (^)(NSData* data,
+ const RequestMetadata& metadata);
+
+#endif // defined(__OBJC__)
+
+} // namespace image_fetcher
+
+#endif // COMPONENTS_IMAGE_FETCHER_CORE_IMAGE_FETCHER_TYPES_H_
diff --git a/chromium/components/image_fetcher/ios/BUILD.gn b/chromium/components/image_fetcher/ios/BUILD.gn
index 99ab37a4171..015a12fb226 100644
--- a/chromium/components/image_fetcher/ios/BUILD.gn
+++ b/chromium/components/image_fetcher/ios/BUILD.gn
@@ -34,8 +34,10 @@ source_set("unit_tests") {
":ios",
":webp_transcode_unit_tests_bundle_data",
"//base",
+ "//base/test:test_support",
"//net",
- "//net:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//testing/gmock",
"//testing/gtest",
"//ui/gfx",
diff --git a/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper.h b/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper.h
index 428be06bc39..9c13e03d847 100644
--- a/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper.h
+++ b/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper.h
@@ -10,34 +10,27 @@
#include "base/memory/ref_counted.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/image_fetcher/core/image_data_fetcher.h"
+#include "components/image_fetcher/core/image_fetcher_types.h"
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
}
class GURL;
namespace image_fetcher {
-// Callback that informs of the download of an image encoded in |data| and the
-// associated metadata. If an error prevented a http response,
-// |metadata.http_response_code| will be RESPONSE_CODE_INVALID.
-using IOSImageDataFetcherCallback = void (^)(NSData* data,
- const RequestMetadata& metadata);
-
class IOSImageDataFetcherWrapper {
public:
- using DataUseServiceName = data_use_measurement::DataUseUserData::ServiceName;
-
// The TaskRunner is used to decode the image if it is WebP-encoded.
explicit IOSImageDataFetcherWrapper(
- net::URLRequestContextGetter* url_request_context_getter);
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
virtual ~IOSImageDataFetcherWrapper();
// Helper to start downloading and possibly decoding the image without a
// referrer.
virtual void FetchImageDataWebpDecoded(const GURL& image_url,
- IOSImageDataFetcherCallback callback);
+ ImageDataFetcherBlock callback);
// Start downloading the image at the given |image_url|. The |callback| will
// be called with the downloaded image, or nil if any error happened. If the
@@ -47,16 +40,21 @@ class IOSImageDataFetcherWrapper {
// |callback| cannot be nil.
void FetchImageDataWebpDecoded(
const GURL& image_url,
- IOSImageDataFetcherCallback callback,
+ ImageDataFetcherBlock callback,
const std::string& referrer,
net::URLRequest::ReferrerPolicy referrer_policy);
// Sets a service name against which to track data usage.
void SetDataUseServiceName(DataUseServiceName data_use_service_name);
+ // Test-only accessor for underlying ImageDataFetcher.
+ ImageDataFetcher* AccessImageDataFetcherForTesting() {
+ return &image_data_fetcher_;
+ }
+
private:
- ImageDataFetcher::ImageDataFetcherCallback CallbackForImageDataFetcher(
- IOSImageDataFetcherCallback callback);
+ ImageDataFetcherCallback CallbackForImageDataFetcher(
+ ImageDataFetcherBlock callback);
ImageDataFetcher image_data_fetcher_;
diff --git a/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper.mm b/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper.mm
index 206058c4e1d..aaf955dc773 100644
--- a/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper.mm
+++ b/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper.mm
@@ -4,13 +4,13 @@
#import "components/image_fetcher/ios/ios_image_data_fetcher_wrapper.h"
-#import "base/mac/bind_objc_block.h"
+#include "base/bind.h"
#include "base/task_scheduler/post_task.h"
#import "components/image_fetcher/ios/webp_decoder.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "url/url_constants.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -22,14 +22,14 @@
namespace image_fetcher {
IOSImageDataFetcherWrapper::IOSImageDataFetcherWrapper(
- net::URLRequestContextGetter* url_request_context_getter)
- : image_data_fetcher_(url_request_context_getter) {}
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+ : image_data_fetcher_(url_loader_factory) {}
IOSImageDataFetcherWrapper::~IOSImageDataFetcherWrapper() {}
void IOSImageDataFetcherWrapper::FetchImageDataWebpDecoded(
const GURL& image_url,
- IOSImageDataFetcherCallback callback) {
+ ImageDataFetcherBlock callback) {
image_data_fetcher_.FetchImageData(image_url,
CallbackForImageDataFetcher(callback),
NO_TRAFFIC_ANNOTATION_YET);
@@ -37,7 +37,7 @@ void IOSImageDataFetcherWrapper::FetchImageDataWebpDecoded(
void IOSImageDataFetcherWrapper::FetchImageDataWebpDecoded(
const GURL& image_url,
- IOSImageDataFetcherCallback callback,
+ ImageDataFetcherBlock callback,
const std::string& referrer,
net::URLRequest::ReferrerPolicy referrer_policy) {
DCHECK(callback);
@@ -52,11 +52,11 @@ void IOSImageDataFetcherWrapper::SetDataUseServiceName(
image_data_fetcher_.SetDataUseServiceName(data_use_service_name);
}
-ImageDataFetcher::ImageDataFetcherCallback
+ImageDataFetcherCallback
IOSImageDataFetcherWrapper::CallbackForImageDataFetcher(
- IOSImageDataFetcherCallback callback) {
- return base::BindBlockArc(^(const std::string& image_data,
- const RequestMetadata& metadata) {
+ ImageDataFetcherBlock callback) {
+ return base::BindOnce(^(const std::string& image_data,
+ const RequestMetadata& metadata) {
// Create a NSData from the returned data and notify the callback.
NSData* data =
[NSData dataWithBytes:image_data.data() length:image_data.size()];
@@ -75,10 +75,10 @@ IOSImageDataFetcherWrapper::CallbackForImageDataFetcher(
base::TaskPriority::BACKGROUND,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
},
- base::BindBlockArc(^NSData*() {
+ base::BindOnce(^NSData*() {
return webp_transcode::WebpDecoder::DecodeWebpImage(data);
}),
- base::BindBlockArc(^(NSData* decoded_data) {
+ base::BindOnce(^(NSData* decoded_data) {
callback(decoded_data, webp_metadata);
}));
});
diff --git a/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper_unittest.mm b/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper_unittest.mm
index d1e60e3b778..1f8bcb2038b 100644
--- a/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper_unittest.mm
+++ b/chromium/components/image_fetcher/ios/ios_image_data_fetcher_wrapper_unittest.mm
@@ -9,17 +9,17 @@
#include <memory>
#include "base/mac/scoped_block.h"
-#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/stl_util.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#import "components/image_fetcher/ios/webp_decoder.h"
#include "net/http/http_response_headers.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_test_util.h"
+#include "net/http/http_status_code.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
@@ -111,27 +111,25 @@ class IOSImageDataFetcherWrapperTest : public PlatformTest {
result_ = [UIImage imageWithData:data];
called_ = true;
} copy]) {
- image_fetcher_ = std::make_unique<IOSImageDataFetcherWrapper>(
- new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get()));
+ shared_factory_ =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &factory_);
+ image_fetcher_ =
+ std::make_unique<IOSImageDataFetcherWrapper>(shared_factory_);
}
- net::TestURLFetcher* SetupFetcher() {
+ void SetupFetcher() {
image_fetcher_->FetchImageDataWebpDecoded(GURL(kTestUrl), callback_);
EXPECT_EQ(nil, result_);
EXPECT_EQ(false, called_);
- net::TestURLFetcher* fetcher =
- factory_.GetFetcherByID(ImageDataFetcher::kFirstUrlFetcherId);
- DCHECK(fetcher);
- DCHECK(fetcher->delegate());
- return fetcher;
}
// Message loop for the main test thread.
base::test::ScopedTaskEnvironment environment_;
- base::mac::ScopedBlock<IOSImageDataFetcherCallback> callback_;
- net::TestURLFetcherFactory factory_;
+ base::mac::ScopedBlock<ImageDataFetcherBlock> callback_;
+ network::TestURLLoaderFactory factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> shared_factory_;
std::unique_ptr<IOSImageDataFetcherWrapper> image_fetcher_;
NSData* result_data_ = nil;
UIImage* result_ = nil;
@@ -142,77 +140,88 @@ class IOSImageDataFetcherWrapperTest : public PlatformTest {
};
TEST_F(IOSImageDataFetcherWrapperTest, TestError) {
- net::TestURLFetcher* fetcher = SetupFetcher();
- fetcher->set_response_code(404);
- fetcher->delegate()->OnURLFetchComplete(fetcher);
+ SetupFetcher();
+ factory_.AddResponse(kTestUrl, "", net::HTTP_NOT_FOUND);
+ environment_.RunUntilIdle();
EXPECT_EQ(nil, result_);
EXPECT_TRUE(called_);
}
TEST_F(IOSImageDataFetcherWrapperTest, TestJpg) {
- net::TestURLFetcher* fetcher = SetupFetcher();
- fetcher->set_response_code(200);
- fetcher->SetResponseString(
+ SetupFetcher();
+ factory_.AddResponse(
+ kTestUrl,
std::string(reinterpret_cast<const char*>(kJPGImage), sizeof(kJPGImage)));
- fetcher->delegate()->OnURLFetchComplete(fetcher);
+ environment_.RunUntilIdle();
EXPECT_NE(nil, result_);
EXPECT_TRUE(called_);
}
TEST_F(IOSImageDataFetcherWrapperTest, TestPng) {
- net::TestURLFetcher* fetcher = SetupFetcher();
- fetcher->set_response_code(200);
- fetcher->SetResponseString(
+ SetupFetcher();
+ factory_.AddResponse(
+ kTestUrl,
std::string(reinterpret_cast<const char*>(kPNGImage), sizeof(kPNGImage)));
- fetcher->delegate()->OnURLFetchComplete(fetcher);
+ environment_.RunUntilIdle();
EXPECT_NE(nil, result_);
EXPECT_TRUE(called_);
}
TEST_F(IOSImageDataFetcherWrapperTest, TestGoodWebP) {
- net::TestURLFetcher* fetcher = SetupFetcher();
- fetcher->set_response_code(200);
- fetcher->SetResponseString(std::string(
- reinterpret_cast<const char*>(kWEBPImage), sizeof(kWEBPImage)));
- scoped_refptr<net::HttpResponseHeaders> headers(new net::HttpResponseHeaders(
- std::string(kWEBPHeaderResponse, arraysize(kWEBPHeaderResponse))));
- fetcher->set_response_headers(headers);
- fetcher->delegate()->OnURLFetchComplete(fetcher);
+ SetupFetcher();
+
+ std::string content(reinterpret_cast<const char*>(kWEBPImage),
+ sizeof(kWEBPImage));
+ network::ResourceResponseHead head;
+ head.headers = new net::HttpResponseHeaders(
+ std::string(kWEBPHeaderResponse, base::size(kWEBPHeaderResponse)));
+ head.mime_type = "image/webp";
+ network::URLLoaderCompletionStatus status;
+ status.decoded_body_length = content.size();
+ factory_.AddResponse(GURL(kTestUrl), head, content, status);
environment_.RunUntilIdle();
EXPECT_NE(nil, result_);
EXPECT_TRUE(called_);
}
TEST_F(IOSImageDataFetcherWrapperTest, TestGoodWebPNoHeader) {
- net::TestURLFetcher* fetcher = SetupFetcher();
- fetcher->set_response_code(200);
- fetcher->SetResponseString(std::string(
- reinterpret_cast<const char*>(kWEBPImage), sizeof(kWEBPImage)));
- fetcher->delegate()->OnURLFetchComplete(fetcher);
+ SetupFetcher();
+ factory_.AddResponse(kTestUrl,
+ std::string(reinterpret_cast<const char*>(kWEBPImage),
+ sizeof(kWEBPImage)));
environment_.RunUntilIdle();
EXPECT_TRUE([DecodedWebpImage() isEqualToData:result_data_]);
EXPECT_TRUE(called_);
}
TEST_F(IOSImageDataFetcherWrapperTest, TestBadWebP) {
- net::TestURLFetcher* fetcher = SetupFetcher();
- fetcher->set_response_code(200);
- fetcher->SetResponseString("This is not a valid WebP image");
- scoped_refptr<net::HttpResponseHeaders> headers(new net::HttpResponseHeaders(
- std::string(kWEBPHeaderResponse, arraysize(kWEBPHeaderResponse))));
- fetcher->set_response_headers(headers);
- fetcher->delegate()->OnURLFetchComplete(fetcher);
+ SetupFetcher();
+
+ std::string content = "This is not a valid WebP image";
+ network::ResourceResponseHead head;
+ head.headers = new net::HttpResponseHeaders(
+ std::string(kWEBPHeaderResponse, base::size(kWEBPHeaderResponse)));
+ head.mime_type = "image/webp";
+ network::URLLoaderCompletionStatus status;
+ status.decoded_body_length = content.size();
+ factory_.AddResponse(GURL(kTestUrl), head, content, status);
environment_.RunUntilIdle();
EXPECT_EQ(nil, result_);
EXPECT_TRUE(called_);
}
TEST_F(IOSImageDataFetcherWrapperTest, DeleteDuringWebPDecoding) {
- net::TestURLFetcher* fetcher = SetupFetcher();
- fetcher->set_response_code(200);
- fetcher->SetResponseString(std::string(
- reinterpret_cast<const char*>(kWEBPImage), sizeof(kWEBPImage)));
- fetcher->delegate()->OnURLFetchComplete(fetcher);
+ SetupFetcher();
+ // To control timing of events precisely below, this needs to inject the
+ // result synchronously, so it's done via some test-only methods rather than
+ // via TestURLLoaderFactory.
+ image_fetcher::RequestMetadata metadata;
+ metadata.mime_type = "image/webp";
+ metadata.http_response_code = 200;
+ image_fetcher_->AccessImageDataFetcherForTesting()->InjectResultForTesting(
+ metadata, std::string(reinterpret_cast<const char*>(kWEBPImage),
+ sizeof(kWEBPImage)));
+
// Delete the image fetcher, and check that the callback is called.
image_fetcher_.reset();
environment_.RunUntilIdle();
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 ee900df7a45..ff5ab2f54f3 100644
--- a/chromium/components/image_fetcher/ios/ios_image_decoder_impl.mm
+++ b/chromium/components/image_fetcher/ios/ios_image_decoder_impl.mm
@@ -8,8 +8,8 @@
#include <memory>
+#include "base/bind.h"
#include "base/callback.h"
-#import "base/mac/bind_objc_block.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/task_scheduler/post_task.h"
@@ -81,9 +81,9 @@ void IOSImageDecoderImpl::DecodeImage(const std::string& image_data,
}
base::PostTaskAndReplyWithResult(
- task_runner_.get(), FROM_HERE, base::BindBlockArc(decodeBlock),
- base::Bind(&IOSImageDecoderImpl::CreateUIImageAndRunCallback,
- weak_factory_.GetWeakPtr(), callback));
+ task_runner_.get(), FROM_HERE, base::BindOnce(decodeBlock),
+ base::BindOnce(&IOSImageDecoderImpl::CreateUIImageAndRunCallback,
+ weak_factory_.GetWeakPtr(), callback));
}
void IOSImageDecoderImpl::CreateUIImageAndRunCallback(
diff --git a/chromium/components/infobars/core/infobar_container.cc b/chromium/components/infobars/core/infobar_container.cc
index 20678aafe53..c9eeacc9ff9 100644
--- a/chromium/components/infobars/core/infobar_container.cc
+++ b/chromium/components/infobars/core/infobar_container.cc
@@ -11,6 +11,7 @@
#include "base/metrics/histogram_base.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/metrics_hashes.h"
+#include "base/stl_util.h"
#include "build/build_config.h"
#include "components/infobars/core/infobar.h"
#include "components/infobars/core/infobar_delegate.h"
@@ -123,8 +124,7 @@ void InfoBarContainer::OnManagerShuttingDown(InfoBarManager* manager) {
void InfoBarContainer::AddInfoBar(InfoBar* infobar,
size_t position,
bool animate) {
- DCHECK(std::find(infobars_.begin(), infobars_.end(), infobar) ==
- infobars_.end());
+ DCHECK(!base::ContainsValue(infobars_, infobar));
DCHECK_LE(position, infobars_.size());
infobars_.insert(infobars_.begin() + position, infobar);
PlatformSpecificAddInfoBar(infobar, position);
diff --git a/chromium/components/infobars/core/infobar_delegate.h b/chromium/components/infobars/core/infobar_delegate.h
index 7e7857c225c..98f49634640 100644
--- a/chromium/components/infobars/core/infobar_delegate.h
+++ b/chromium/components/infobars/core/infobar_delegate.h
@@ -151,6 +151,9 @@ class InfoBarDelegate {
INSTALLABLE_AMBIENT_BADGE_INFOBAR_DELEGATE = 80,
PAGE_LOAD_CAPPING_INFOBAR_DELEGATE = 81,
DOWNLOAD_PROGRESS_INFOBAR_ANDROID = 82,
+ AR_CORE_UPGRADE_ANDROID = 83,
+ BLOATED_RENDERER_INFOBAR_DELEGATE = 84,
+ SUPERVISED_USERS_DEPRECATED_INFOBAR_DELEGATE = 85,
};
// Describes navigation events, used to decide whether infobars should be
diff --git a/chromium/components/invalidation/impl/BUILD.gn b/chromium/components/invalidation/impl/BUILD.gn
index 274056bee63..092e48c1c2b 100644
--- a/chromium/components/invalidation/impl/BUILD.gn
+++ b/chromium/components/invalidation/impl/BUILD.gn
@@ -27,8 +27,12 @@ static_library("impl") {
"invalidator_storage.h",
"mock_ack_handler.cc",
"mock_ack_handler.h",
+ "per_user_topic_registration_manager.cc",
+ "per_user_topic_registration_manager.h",
"per_user_topic_registration_request.cc",
"per_user_topic_registration_request.h",
+ "profile_identity_provider.cc",
+ "profile_identity_provider.h",
"profile_invalidation_provider.cc",
"profile_invalidation_provider.h",
"status.cc",
@@ -48,10 +52,10 @@ static_library("impl") {
"//components/keyed_service/core",
"//components/pref_registry",
"//components/prefs",
- "//components/signin/core/browser",
"//google_apis",
"//jingle:notifier",
"//net:net",
+ "//services/identity/public/cpp",
"//services/network/public/cpp",
"//services/network/public/mojom",
@@ -61,6 +65,12 @@ static_library("impl") {
if (!is_android) {
sources += [
+ "fcm_network_handler.cc",
+ "fcm_network_handler.h",
+ "fcm_sync_invalidation_listener.cc",
+ "fcm_sync_invalidation_listener.h",
+ "fcm_sync_network_channel.cc",
+ "fcm_sync_network_channel.h",
"gcm_invalidation_bridge.cc",
"gcm_invalidation_bridge.h",
"gcm_network_channel.cc",
@@ -68,12 +78,17 @@ static_library("impl") {
"gcm_network_channel_delegate.h",
"invalidation_notifier.cc",
"invalidation_notifier.h",
+ "logger.cc",
+ "logger.h",
+ "network_channel.h",
"non_blocking_invalidator.cc",
"non_blocking_invalidator.h",
"notifier_reason_util.cc",
"notifier_reason_util.h",
"p2p_invalidator.cc",
"p2p_invalidator.h",
+ "per_user_topic_invalidation_client.cc",
+ "per_user_topic_invalidation_client.h",
"push_client_channel.cc",
"push_client_channel.h",
"registration_manager.cc",
@@ -141,6 +156,8 @@ source_set("unit_tests") {
# Non-Android tests.
sources += [
"fake_invalidator_unittest.cc",
+ "fcm_network_handler_unittests.cc",
+ "fcm_sync_invalidation_listener_unittest.cc",
"gcm_invalidation_bridge_unittest.cc",
"gcm_network_channel_unittest.cc",
"invalidation_notifier_unittest.cc",
@@ -149,6 +166,8 @@ source_set("unit_tests") {
"non_blocking_invalidator_unittest.cc",
"object_id_invalidation_map_unittest.cc",
"p2p_invalidator_unittest.cc",
+ "per_user_topic_invalidation_client_unittest.cc",
+ "per_user_topic_registration_manager_unittest.cc",
"push_client_channel_unittest.cc",
"registration_manager_unittest.cc",
"single_object_invalidation_set_unittest.cc",
@@ -160,10 +179,13 @@ source_set("unit_tests") {
]
deps += [
"//components/gcm_driver:test_support",
+ "//components/gcm_driver/instance_id:test_support",
"//components/signin/core/browser:test_support",
"//components/sync_preferences:test_support",
"//google_apis:test_support",
+ "//google_apis/gcm:gcm",
"//net",
+ "//services/identity/public/cpp:test_support",
]
}
}
@@ -179,6 +201,8 @@ static_library("test_support") {
"fake_invalidation_state_tracker.h",
"fake_invalidator.cc",
"fake_invalidator.h",
+ "fake_system_resources.cc",
+ "fake_system_resources.h",
"invalidation_service_test_template.cc",
"invalidation_service_test_template.h",
"invalidation_test_util.cc",
@@ -200,9 +224,6 @@ static_library("test_support") {
"//base",
"//components/gcm_driver:test_support",
"//components/keyed_service/core",
- "//components/signin/core/browser:test_support",
- "//google_apis",
- "//google_apis:test_support",
"//jingle:notifier",
"//net",
"//testing/gmock",
@@ -257,11 +278,12 @@ if (is_android) {
"//components/signin/core/browser/android:java",
"//components/sync/android:sync_java",
"//third_party/android_support_test_runner:runner_java",
+ "//third_party/android_tools:android_test_base_java",
+ "//third_party/android_tools:android_test_runner_java",
"//third_party/cacheinvalidation:cacheinvalidation_javalib",
"//third_party/cacheinvalidation:cacheinvalidation_proto_java",
"//third_party/junit",
]
- deps += android_extra_test_deps
java_files = [
"android/javatests/src/org/chromium/components/invalidation/InvalidationClientServiceTest.java",
"android/javatests/src/org/chromium/components/invalidation/TestableInvalidationClientService.java",
diff --git a/chromium/components/invalidation/public/BUILD.gn b/chromium/components/invalidation/public/BUILD.gn
index 8cee3149c32..c41f8c5831e 100644
--- a/chromium/components/invalidation/public/BUILD.gn
+++ b/chromium/components/invalidation/public/BUILD.gn
@@ -8,6 +8,10 @@ static_library("public") {
"ack_handle.h",
"ack_handler.cc",
"ack_handler.h",
+ "active_account_access_token_fetcher_impl.cc",
+ "active_account_access_token_fetcher_impl.h",
+ "identity_provider.cc",
+ "identity_provider.h",
"invalidation.cc",
"invalidation.h",
"invalidation_export.h",
@@ -24,6 +28,7 @@ static_library("public") {
"single_object_invalidation_set.h",
]
public_deps = [
+ "//google_apis",
"//third_party/cacheinvalidation",
]
deps = [
diff --git a/chromium/components/json_schema/OWNERS b/chromium/components/json_schema/OWNERS
index 33e2bce427f..6f528868ace 100644
--- a/chromium/components/json_schema/OWNERS
+++ b/chromium/components/json_schema/OWNERS
@@ -1,2 +1,2 @@
-asargent@chromium.org
-calamity@chromium.org
+emaxx@chromium.org
+ljusten@chromium.org
diff --git a/chromium/components/json_schema/json_schema_constants.cc b/chromium/components/json_schema/json_schema_constants.cc
index 0152cfc054d..79c5eaf4153 100644
--- a/chromium/components/json_schema/json_schema_constants.cc
+++ b/chromium/components/json_schema/json_schema_constants.cc
@@ -30,6 +30,7 @@ const char kPattern[] = "pattern";
const char kPatternProperties[] = "patternProperties";
const char kProperties[] = "properties";
const char kRef[] = "$ref";
+const char kRequired[] = "required";
const char kSchema[] = "$schema";
const char kString[] = "string";
const char kTitle[] = "title";
diff --git a/chromium/components/json_schema/json_schema_constants.h b/chromium/components/json_schema/json_schema_constants.h
index 5c64ebb5606..19394a915f0 100644
--- a/chromium/components/json_schema/json_schema_constants.h
+++ b/chromium/components/json_schema/json_schema_constants.h
@@ -32,6 +32,7 @@ extern const char kPattern[];
extern const char kPatternProperties[];
extern const char kProperties[];
extern const char kRef[];
+extern const char kRequired[];
extern const char kSchema[];
extern const char kString[];
extern const char kTitle[];
diff --git a/chromium/components/json_schema/json_schema_validator.cc b/chromium/components/json_schema/json_schema_validator.cc
index 8806e900756..7a6daba014a 100644
--- a/chromium/components/json_schema/json_schema_validator.cc
+++ b/chromium/components/json_schema/json_schema_validator.cc
@@ -97,6 +97,7 @@ bool IsValidSchema(const base::DictionaryValue* dict,
{ schema::kPattern, base::Value::Type::STRING },
{ schema::kPatternProperties, base::Value::Type::DICTIONARY },
{ schema::kProperties, base::Value::Type::DICTIONARY },
+ { schema::kRequired, base::Value::Type::LIST },
{ schema::kTitle, base::Value::Type::STRING },
};
@@ -236,6 +237,19 @@ bool IsValidSchema(const base::DictionaryValue* dict,
}
}
+ // Validate "required" attribute.
+ if (it.key() == schema::kRequired) {
+ it.value().GetAsList(&list_value);
+ for (const base::Value& value : *list_value) {
+ if (value.type() != base::Value::Type::STRING) {
+ *error = "Invalid value in 'required' attribute";
+ return false;
+ }
+ // TODO(crbug.com/856903): Check that |value| is a key in
+ // schema::kProperties
+ }
+ }
+
// Validate the values contained in an "enum" attribute.
if (it.key() == schema::kEnum) {
it.value().GetAsList(&list_value);
diff --git a/chromium/components/json_schema/json_schema_validator_unittest.cc b/chromium/components/json_schema/json_schema_validator_unittest.cc
index 19fc6f9b209..b1559536d57 100644
--- a/chromium/components/json_schema/json_schema_validator_unittest.cc
+++ b/chromium/components/json_schema/json_schema_validator_unittest.cc
@@ -156,4 +156,22 @@ TEST(JSONSchemaValidator, IsValidSchema) {
" \"type\": \"object\","
" \"unknown attribute\": \"that will cause a failure\""
"}", 0, &error)) << error;
+
+ EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(R"(
+ {
+ "type": "object",
+ "properties": {"foo": {"type": "number"}},
+ "required": 123
+ })",
+ 0, &error))
+ << error;
+
+ EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(R"(
+ {
+ "type": "object",
+ "properties": {"foo": {"type": "number"}},
+ "required": [ 123 ]
+ })",
+ 0, &error))
+ << error;
}
diff --git a/chromium/components/keep_alive_registry/OWNERS b/chromium/components/keep_alive_registry/OWNERS
new file mode 100644
index 00000000000..87d51bb33be
--- /dev/null
+++ b/chromium/components/keep_alive_registry/OWNERS
@@ -0,0 +1,2 @@
+benwells@chromium.org
+michaelpg@chromium.org
diff --git a/chromium/components/keyed_service/OWNERS b/chromium/components/keyed_service/OWNERS
index 4733a4f06bf..39557cd3030 100644
--- a/chromium/components/keyed_service/OWNERS
+++ b/chromium/components/keyed_service/OWNERS
@@ -1 +1 @@
-erg@chromium.org
+blundell@chromium.org
diff --git a/chromium/components/keyed_service/content/browser_context_dependency_manager.cc b/chromium/components/keyed_service/content/browser_context_dependency_manager.cc
index fe134b96490..e9cf9d391fe 100644
--- a/chromium/components/keyed_service/content/browser_context_dependency_manager.cc
+++ b/chromium/components/keyed_service/content/browser_context_dependency_manager.cc
@@ -4,7 +4,7 @@
#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
#include "base/trace_event/trace_event.h"
#include "content/public/browser/browser_context.h"
@@ -73,7 +73,8 @@ void BrowserContextDependencyManager::MarkBrowserContextLive(
// static
BrowserContextDependencyManager*
BrowserContextDependencyManager::GetInstance() {
- return base::Singleton<BrowserContextDependencyManager>::get();
+ static base::NoDestructor<BrowserContextDependencyManager> factory;
+ return factory.get();
}
BrowserContextDependencyManager::BrowserContextDependencyManager() {
diff --git a/chromium/components/keyed_service/content/browser_context_dependency_manager.h b/chromium/components/keyed_service/content/browser_context_dependency_manager.h
index 0a6eb603e1e..8320e2be5dd 100644
--- a/chromium/components/keyed_service/content/browser_context_dependency_manager.h
+++ b/chromium/components/keyed_service/content/browser_context_dependency_manager.h
@@ -16,7 +16,7 @@ class BrowserContextKeyedBaseFactory;
namespace base {
template <typename T>
-struct DefaultSingletonTraits;
+class NoDestructor;
} // namespace base
namespace content {
@@ -85,7 +85,7 @@ class KEYED_SERVICE_EXPORT BrowserContextDependencyManager
private:
friend class BrowserContextDependencyManagerUnittests;
- friend struct base::DefaultSingletonTraits<BrowserContextDependencyManager>;
+ friend class base::NoDestructor<BrowserContextDependencyManager>;
// Helper function used by CreateBrowserContextServices[ForTest].
void DoCreateBrowserContextServices(content::BrowserContext* context,
diff --git a/chromium/components/language/core/browser/BUILD.gn b/chromium/components/language/core/browser/BUILD.gn
index ae7553ba902..f0c15864561 100644
--- a/chromium/components/language/core/browser/BUILD.gn
+++ b/chromium/components/language/core/browser/BUILD.gn
@@ -10,6 +10,8 @@ static_library("browser") {
"heuristic_language_model.h",
"language_model.cc",
"language_model.h",
+ "language_model_manager.cc",
+ "language_model_manager.h",
"pref_names.cc",
"pref_names.h",
"url_language_histogram.cc",
diff --git a/chromium/components/language/core/browser/language_model.h b/chromium/components/language/core/browser/language_model.h
index 65346414a81..86f180336ea 100644
--- a/chromium/components/language/core/browser/language_model.h
+++ b/chromium/components/language/core/browser/language_model.h
@@ -14,7 +14,7 @@ namespace language {
// Defines a user language model represented by a ranked list of languages and
// associated scores.
-class LanguageModel : public KeyedService {
+class LanguageModel {
public:
// Information about one language that a user understands.
struct LanguageDetails {
@@ -29,6 +29,8 @@ class LanguageModel : public KeyedService {
float score;
};
+ virtual ~LanguageModel() {}
+
// The set of languages that the user understands. The languages are ranked
// from most important to least.
virtual std::vector<LanguageDetails> GetLanguages() = 0;
diff --git a/chromium/components/language/core/browser/language_model_manager.cc b/chromium/components/language/core/browser/language_model_manager.cc
new file mode 100644
index 00000000000..be20c3640e2
--- /dev/null
+++ b/chromium/components/language/core/browser/language_model_manager.cc
@@ -0,0 +1,25 @@
+// 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/language_model_manager.h"
+
+namespace language {
+
+LanguageModelManager::LanguageModelManager(PrefService* prefs,
+ const std::string& ui_lang) {
+ // TODO(crbug.com/855192): put code to add UI language to the blacklist here.
+}
+
+LanguageModelManager::~LanguageModelManager() {}
+
+void LanguageModelManager::SetDefaultModel(
+ std::unique_ptr<LanguageModel> model) {
+ default_model_ = std::move(model);
+}
+
+LanguageModel* LanguageModelManager::GetDefaultModel() {
+ return default_model_.get();
+}
+
+} // namespace language
diff --git a/chromium/components/language/core/browser/language_model_manager.h b/chromium/components/language/core/browser/language_model_manager.h
new file mode 100644
index 00000000000..d34ba25f899
--- /dev/null
+++ b/chromium/components/language/core/browser/language_model_manager.h
@@ -0,0 +1,35 @@
+// 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_LANGUAGE_CORE_BROWSER_LANGUAGE_MODEL_MANAGER_H_
+#define COMPONENTS_LANGUAGE_CORE_BROWSER_LANGUAGE_MODEL_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/language/core/browser/language_model.h"
+#include "components/prefs/pref_service.h"
+
+namespace language {
+
+// Manages a set of LanguageModel objects.
+class LanguageModelManager : public KeyedService {
+ public:
+ LanguageModelManager(PrefService* prefs, const std::string& ui_lang);
+
+ ~LanguageModelManager() override;
+
+ void SetDefaultModel(std::unique_ptr<LanguageModel> model);
+ LanguageModel* GetDefaultModel();
+
+ private:
+ std::unique_ptr<LanguageModel> default_model_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(LanguageModelManager);
+};
+
+} // namespace language
+
+#endif // COMPONENTS_LANGUAGE_CORE_BROWSER_LANGUAGE_MODEL_MANAGER_H_
diff --git a/chromium/components/language/core/browser/pref_names.cc b/chromium/components/language/core/browser/pref_names.cc
index 0a7fb76de87..875577a326d 100644
--- a/chromium/components/language/core/browser/pref_names.cc
+++ b/chromium/components/language/core/browser/pref_names.cc
@@ -12,5 +12,8 @@ namespace prefs {
// understands).
const char kUserLanguageProfile[] = "language_profile";
+// Important: Refer to header file for how to use this.
+const char kApplicationLocale[] = "intl.app_locale";
+
} // namespace prefs
} // namespace language
diff --git a/chromium/components/language/core/browser/pref_names.h b/chromium/components/language/core/browser/pref_names.h
index 629b4161f59..56923e276f8 100644
--- a/chromium/components/language/core/browser/pref_names.h
+++ b/chromium/components/language/core/browser/pref_names.h
@@ -11,6 +11,12 @@ namespace prefs {
// TODO(martis): Add accept language preference here.
extern const char kUserLanguageProfile[];
+// The application locale.
+// DO NOT USE this locale directly: use language::ConvertToActualUILocale()
+// after reading it to get the system locale. This pref stores the locale that
+// the user selected, if applicable.
+extern const char kApplicationLocale[];
+
} // namespace prefs
} // namespace language
diff --git a/chromium/components/leveldb_proto/leveldb_database.cc b/chromium/components/leveldb_proto/leveldb_database.cc
index c4fa33ac754..6a683350e9d 100644
--- a/chromium/components/leveldb_proto/leveldb_database.cc
+++ b/chromium/components/leveldb_proto/leveldb_database.cc
@@ -99,6 +99,40 @@ bool LevelDB::Save(const base::StringPairs& entries_to_save,
return false;
}
+bool LevelDB::UpdateWithRemoveFilter(const base::StringPairs& entries_to_save,
+ const KeyFilter& delete_key_filter) {
+ DFAKE_SCOPED_LOCK(thread_checker_);
+ if (!db_)
+ return false;
+
+ leveldb::WriteBatch updates;
+ for (const auto& pair : entries_to_save)
+ updates.Put(leveldb::Slice(pair.first), leveldb::Slice(pair.second));
+
+ if (!delete_key_filter.is_null()) {
+ leveldb::ReadOptions read_options;
+ std::unique_ptr<leveldb::Iterator> db_iterator(
+ db_->NewIterator(read_options));
+ for (db_iterator->SeekToFirst(); db_iterator->Valid();
+ db_iterator->Next()) {
+ leveldb::Slice key_slice = db_iterator->key();
+ std::string key(key_slice.data(), key_slice.size());
+ if (delete_key_filter.Run(key))
+ updates.Delete(leveldb::Slice(key));
+ }
+ }
+
+ leveldb::WriteOptions write_options;
+ write_options.sync = true;
+ leveldb::Status status = db_->Write(write_options, &updates);
+ if (status.ok())
+ return true;
+
+ DLOG(WARNING) << "Failed deleting leveldb_proto entries: "
+ << status.ToString();
+ return false;
+}
+
bool LevelDB::Load(std::vector<std::string>* entries) {
return LoadWithFilter(KeyFilter(), entries);
}
diff --git a/chromium/components/leveldb_proto/leveldb_database.h b/chromium/components/leveldb_proto/leveldb_database.h
index 020f21582ea..a0476b237f0 100644
--- a/chromium/components/leveldb_proto/leveldb_database.h
+++ b/chromium/components/leveldb_proto/leveldb_database.h
@@ -49,6 +49,8 @@ class LevelDB {
virtual bool Save(const base::StringPairs& pairs_to_save,
const std::vector<std::string>& keys_to_remove);
+ virtual bool UpdateWithRemoveFilter(const base::StringPairs& entries_to_save,
+ const KeyFilter& delete_key_filter);
virtual bool Load(std::vector<std::string>* entries);
virtual bool LoadWithFilter(const KeyFilter& filter,
std::vector<std::string>* entries);
diff --git a/chromium/components/leveldb_proto/proto_database.h b/chromium/components/leveldb_proto/proto_database.h
index 8dbdedf686d..d8adc5f6f2f 100644
--- a/chromium/components/leveldb_proto/proto_database.h
+++ b/chromium/components/leveldb_proto/proto_database.h
@@ -56,6 +56,15 @@ class ProtoDatabase {
std::unique_ptr<std::vector<std::string>> keys_to_remove,
UpdateCallback callback) = 0;
+ // Asynchronously saves |entries_to_save| and deletes entries that satisfies
+ // the |delete_key_filter| from the database. |callback| will be invoked on
+ // the calling thread when complete. The filter will be called on
+ // ProtoDatabase's taskrunner.
+ virtual void UpdateEntriesWithRemoveFilter(
+ std::unique_ptr<KeyEntryVector> entries_to_save,
+ const LevelDB::KeyFilter& delete_key_filter,
+ UpdateCallback callback) = 0;
+
// Asynchronously loads all entries from the database and invokes |callback|
// when complete.
virtual void LoadEntries(LoadCallback callback) = 0;
diff --git a/chromium/components/leveldb_proto/proto_database_impl.h b/chromium/components/leveldb_proto/proto_database_impl.h
index a3d236ab787..a96ec1fa0fc 100644
--- a/chromium/components/leveldb_proto/proto_database_impl.h
+++ b/chromium/components/leveldb_proto/proto_database_impl.h
@@ -50,6 +50,11 @@ class ProtoDatabaseImpl : public ProtoDatabase<T> {
entries_to_save,
std::unique_ptr<KeyVector> keys_to_remove,
typename ProtoDatabase<T>::UpdateCallback callback) override;
+ void UpdateEntriesWithRemoveFilter(
+ std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector>
+ entries_to_save,
+ const LevelDB::KeyFilter& delete_key_filter,
+ typename ProtoDatabase<T>::UpdateCallback callback) override;
void LoadEntries(typename ProtoDatabase<T>::LoadCallback callback) override;
void LoadEntriesWithFilter(
const LevelDB::KeyFilter& key_filter,
@@ -154,6 +159,24 @@ void UpdateEntriesFromTaskRunner(
}
template <typename T>
+void UpdateEntriesWithRemoveFilterFromTaskRunner(
+ LevelDB* database,
+ std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
+ const LevelDB::KeyFilter& delete_key_filter,
+ bool* success) {
+ DCHECK(success);
+
+ // Serialize the values from Proto to string before passing on to database.
+ KeyValueVector pairs_to_save;
+ for (const auto& pair : *entries_to_save) {
+ pairs_to_save.push_back(
+ std::make_pair(pair.first, pair.second.SerializeAsString()));
+ }
+
+ *success = database->UpdateWithRemoveFilter(pairs_to_save, delete_key_filter);
+}
+
+template <typename T>
void LoadEntriesFromTaskRunner(LevelDB* database,
const LevelDB::KeyFilter& filter,
std::vector<T>* entries,
@@ -288,6 +311,23 @@ void ProtoDatabaseImpl<T>::UpdateEntries(
}
template <typename T>
+void ProtoDatabaseImpl<T>::UpdateEntriesWithRemoveFilter(
+ std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
+ const LevelDB::KeyFilter& delete_key_filter,
+ typename ProtoDatabase<T>::UpdateCallback callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ bool* success = new bool(false);
+
+ task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ base::BindOnce(UpdateEntriesWithRemoveFilterFromTaskRunner<T>,
+ base::Unretained(db_.get()), std::move(entries_to_save),
+ delete_key_filter, success),
+ base::BindOnce(RunUpdateCallback<T>, std::move(callback),
+ base::Owned(success)));
+}
+
+template <typename T>
void ProtoDatabaseImpl<T>::LoadEntries(
typename ProtoDatabase<T>::LoadCallback callback) {
LoadEntriesWithFilter(LevelDB::KeyFilter(), std::move(callback));
diff --git a/chromium/components/leveldb_proto/proto_database_impl_unittest.cc b/chromium/components/leveldb_proto/proto_database_impl_unittest.cc
index f2f23286b21..4b03fb453a4 100644
--- a/chromium/components/leveldb_proto/proto_database_impl_unittest.cc
+++ b/chromium/components/leveldb_proto/proto_database_impl_unittest.cc
@@ -52,6 +52,8 @@ class MockDB : public LevelDB {
bool(const base::FilePath& database_dir,
const leveldb_env::Options& options));
MOCK_METHOD2(Save, bool(const KeyValueVector&, const KeyVector&));
+ MOCK_METHOD2(UpdateWithRemoveFilter,
+ bool(const base::StringPairs&, const KeyFilter&));
MOCK_METHOD1(Load, bool(std::vector<std::string>*));
MOCK_METHOD2(LoadWithFilter,
bool(const KeyFilter&, std::vector<std::string>*));
@@ -799,4 +801,40 @@ TEST_F(ProtoDatabaseImplLevelDBTest, TestCorruptDBReset) {
ASSERT_FALSE(found);
}
+TEST_F(ProtoDatabaseImplLevelDBTest, TestDBDeleteWithFilter) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ EntryMap model = GetSmallModel();
+
+ KeyValueVector save_entries;
+ std::vector<std::string> load_entries;
+ KeyVector remove_keys;
+
+ for (const auto& pair : model) {
+ save_entries.push_back(
+ std::make_pair(pair.second.id(), pair.second.SerializeAsString()));
+ }
+
+ std::unique_ptr<LevelDB> db(new LevelDB(kTestLevelDBClientName));
+ EXPECT_TRUE(db->Init(temp_dir.GetPath(), CreateSimpleOptions()));
+ EXPECT_TRUE(db->UpdateWithRemoveFilter(save_entries, LevelDB::KeyFilter()));
+
+ // Make sure the "0" entry is in database.
+ EXPECT_TRUE(
+ db->LoadWithFilter(base::BindRepeating(&ZeroFilter), &load_entries));
+ EXPECT_EQ(load_entries.size(), 1u);
+
+ // Delete "0" entry.
+ save_entries.clear();
+ EXPECT_TRUE(db->UpdateWithRemoveFilter(save_entries,
+ base::BindRepeating(&ZeroFilter)));
+
+ // Make sure the "0" entry is not there.
+ load_entries.clear();
+ EXPECT_TRUE(
+ db->LoadWithFilter(base::BindRepeating(&ZeroFilter), &load_entries));
+ EXPECT_EQ(load_entries.size(), 0u);
+}
+
} // namespace leveldb_proto
diff --git a/chromium/components/leveldb_proto/testing/fake_db.h b/chromium/components/leveldb_proto/testing/fake_db.h
index d349b316cc6..234809deecd 100644
--- a/chromium/components/leveldb_proto/testing/fake_db.h
+++ b/chromium/components/leveldb_proto/testing/fake_db.h
@@ -39,6 +39,11 @@ class FakeDB : public ProtoDatabase<T> {
entries_to_save,
std::unique_ptr<std::vector<std::string>> keys_to_remove,
typename ProtoDatabase<T>::UpdateCallback callback) override;
+ void UpdateEntriesWithRemoveFilter(
+ std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector>
+ entries_to_save,
+ const LevelDB::KeyFilter& filter,
+ typename ProtoDatabase<T>::UpdateCallback callback) override;
void LoadEntries(typename ProtoDatabase<T>::LoadCallback callback) override;
void LoadEntriesWithFilter(
const LevelDB::KeyFilter& key_filter,
@@ -120,6 +125,25 @@ void FakeDB<T>::UpdateEntries(
}
template <typename T>
+void FakeDB<T>::UpdateEntriesWithRemoveFilter(
+ std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
+ const LevelDB::KeyFilter& delete_key_filter,
+ typename ProtoDatabase<T>::UpdateCallback callback) {
+ for (const auto& pair : *entries_to_save)
+ (*db_)[pair.first] = pair.second;
+
+ auto it = db_->begin();
+ while (it != db_->end()) {
+ if (!delete_key_filter.is_null() && delete_key_filter.Run(it->first))
+ db_->erase(it++);
+ else
+ ++it;
+ }
+
+ update_callback_ = std::move(callback);
+}
+
+template <typename T>
void FakeDB<T>::LoadEntries(typename ProtoDatabase<T>::LoadCallback callback) {
LoadEntriesWithFilter(LevelDB::KeyFilter(), std::move(callback));
}
diff --git a/chromium/components/metrics/BUILD.gn b/chromium/components/metrics/BUILD.gn
index 75d36a28d49..15b3a14d569 100644
--- a/chromium/components/metrics/BUILD.gn
+++ b/chromium/components/metrics/BUILD.gn
@@ -105,7 +105,7 @@ static_library("metrics") {
]
public_deps = [
- ":call_stack_profile_params",
+ ":call_stack_profile",
"//third_party/metrics_proto",
]
@@ -256,8 +256,10 @@ static_library("single_sample_metrics") {
]
}
-source_set("call_stack_profile_params") {
+source_set("call_stack_profile") {
sources = [
+ "call_stack_profile_builder.cc",
+ "call_stack_profile_builder.h",
"call_stack_profile_params.h",
]
@@ -283,6 +285,7 @@ source_set("child_call_stacks") {
"child_call_stack_profile_collector.h",
]
deps = [
+ ":call_stack_profile",
"//components/metrics/public/interfaces:call_stack_mojo_bindings",
"//services/service_manager/public/cpp",
]
@@ -326,6 +329,7 @@ if (is_linux) {
source_set("unit_tests") {
testonly = true
sources = [
+ "call_stack_profile_builder_unittest.cc",
"call_stack_profile_metrics_provider_unittest.cc",
"child_call_stack_profile_collector_unittest.cc",
"cloned_install_detector_unittest.cc",
@@ -357,7 +361,7 @@ source_set("unit_tests") {
]
deps = [
- ":call_stack_profile_params",
+ ":call_stack_profile",
":child_call_stacks",
":component_metrics",
":metrics",
diff --git a/chromium/components/metrics/call_stack_profile_builder.cc b/chromium/components/metrics/call_stack_profile_builder.cc
new file mode 100644
index 00000000000..f308c93da51
--- /dev/null
+++ b/chromium/components/metrics/call_stack_profile_builder.cc
@@ -0,0 +1,106 @@
+// 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/metrics/call_stack_profile_builder.h"
+
+#include <utility>
+
+#include "base/atomicops.h"
+#include "base/logging.h"
+
+using StackSamplingProfiler = base::StackSamplingProfiler;
+
+namespace metrics {
+
+namespace {
+
+// This global variables holds the current system state and is recorded with
+// every captured sample, done on a separate thread which is why updates to
+// this must be atomic. A PostTask to move the the updates to that thread
+// would skew the timing and a lock could result in deadlock if the thread
+// making a change was also being profiled and got stopped.
+static base::subtle::Atomic32 g_process_milestones = 0;
+
+void ChangeAtomicFlags(base::subtle::Atomic32* flags,
+ base::subtle::Atomic32 set,
+ base::subtle::Atomic32 clear) {
+ DCHECK(set != 0 || clear != 0);
+ DCHECK_EQ(0, set & clear);
+
+ base::subtle::Atomic32 bits = base::subtle::NoBarrier_Load(flags);
+ while (true) {
+ base::subtle::Atomic32 existing = base::subtle::NoBarrier_CompareAndSwap(
+ flags, bits, (bits | set) & ~clear);
+ if (existing == bits)
+ break;
+ bits = existing;
+ }
+}
+
+} // namespace
+
+CallStackProfileBuilder::CallStackProfileBuilder(
+ const CompletedCallback& callback)
+ : callback_(callback) {}
+
+CallStackProfileBuilder::~CallStackProfileBuilder() = default;
+
+void CallStackProfileBuilder::RecordAnnotations() {
+ // The code inside this method must not do anything that could acquire a
+ // mutex, including allocating memory (which includes LOG messages) because
+ // that mutex could be held by a stopped thread, thus resulting in deadlock.
+ sample_.process_milestones =
+ base::subtle::NoBarrier_Load(&g_process_milestones);
+}
+
+void CallStackProfileBuilder::OnSampleCompleted(
+ std::vector<StackSamplingProfiler::InternalFrame> internal_frames) {
+ DCHECK(sample_.frames.empty());
+
+ // Dedup modules and convert InternalFrames to Frames.
+ for (const auto& internal_frame : internal_frames) {
+ const StackSamplingProfiler::InternalModule& module(
+ internal_frame.internal_module);
+ if (!module.is_valid) {
+ sample_.frames.emplace_back(internal_frame.instruction_pointer,
+ base::kUnknownModuleIndex);
+ continue;
+ }
+
+ auto loc = module_index_.find(module.base_address);
+ if (loc == module_index_.end()) {
+ profile_.modules.emplace_back(module.base_address, module.id,
+ module.filename);
+ size_t index = profile_.modules.size() - 1;
+ loc = module_index_.insert(std::make_pair(module.base_address, index))
+ .first;
+ }
+ sample_.frames.emplace_back(internal_frame.instruction_pointer,
+ loc->second);
+ }
+
+ profile_.samples.push_back(std::move(sample_));
+ sample_ = StackSamplingProfiler::Sample();
+}
+
+void CallStackProfileBuilder::OnProfileCompleted(
+ base::TimeDelta profile_duration,
+ base::TimeDelta sampling_period) {
+ profile_.profile_duration = profile_duration;
+ profile_.sampling_period = sampling_period;
+
+ // Run the associated callback, passing the collected profile.
+ callback_.Run(std::move(profile_));
+}
+
+// static
+void CallStackProfileBuilder::SetProcessMilestone(int milestone) {
+ DCHECK_LE(0, milestone);
+ DCHECK_GT(static_cast<int>(sizeof(g_process_milestones) * 8), milestone);
+ DCHECK_EQ(0, base::subtle::NoBarrier_Load(&g_process_milestones) &
+ (1 << milestone));
+ ChangeAtomicFlags(&g_process_milestones, 1 << milestone, 0);
+}
+
+} // namespace metrics
diff --git a/chromium/components/metrics/call_stack_profile_builder.h b/chromium/components/metrics/call_stack_profile_builder.h
new file mode 100644
index 00000000000..ec01592686b
--- /dev/null
+++ b/chromium/components/metrics/call_stack_profile_builder.h
@@ -0,0 +1,77 @@
+// 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_METRICS_CALL_STACK_PROFILE_BUILDER_H_
+#define COMPONENTS_METRICS_CALL_STACK_PROFILE_BUILDER_H_
+
+#include "base/profiler/stack_sampling_profiler.h"
+
+#include <map>
+
+#include "base/callback.h"
+
+namespace metrics {
+
+// CallStackProfileBuilder builds a CallStackProfile from the collected sampling
+// data.
+//
+// The results of the profile building -- a CallStackProfile, is passed to the
+// completed callback. A CallStackProfile contains a set of Samples and
+// Modules, and other sampling information. One Sample corresponds to a single
+// recorded stack, and the Modules record those modules associated with the
+// recorded stack frames.
+class CallStackProfileBuilder
+ : public base::StackSamplingProfiler::ProfileBuilder {
+ public:
+ // The callback type used to collect a completed profile. The passed
+ // CallStackProfile is move-only. Other threads, including the UI thread, may
+ // block on callback completion so this should run as quickly as possible.
+ //
+ // IMPORTANT NOTE: The callback is invoked on a thread the profiler
+ // constructs, rather than on the thread used to construct the profiler, and
+ // thus the callback must be callable on any thread. For threads with message
+ // loops that create CallStackProfileBuilders, posting a task to the message
+ // loop with the moved (i.e. std::move) profile is the thread-safe callback
+ // implementation.
+ using CompletedCallback =
+ base::Callback<void(base::StackSamplingProfiler::CallStackProfile)>;
+
+ CallStackProfileBuilder(const CompletedCallback& callback);
+
+ ~CallStackProfileBuilder() override;
+
+ // base::StackSamplingProfiler::ProfileBuilder:
+ void RecordAnnotations() override;
+ void OnSampleCompleted(std::vector<base::StackSamplingProfiler::InternalFrame>
+ internal_frames) override;
+ void OnProfileCompleted(base::TimeDelta profile_duration,
+ base::TimeDelta sampling_period) override;
+
+ // Sets the current system state that is recorded with each captured stack
+ // frame. This is thread-safe so can be called from anywhere. The parameter
+ // value should be from an enumeration of the appropriate type with values
+ // ranging from 0 to 31, inclusive. This sets bits within Sample field of
+ // |process_milestones|. The actual meanings of these bits are defined
+ // (globally) by the caller(s).
+ static void SetProcessMilestone(int milestone);
+
+ private:
+ // The collected stack samples.
+ base::StackSamplingProfiler::CallStackProfile profile_;
+
+ // The current sample being recorded.
+ base::StackSamplingProfiler::Sample sample_;
+
+ // The indexes of internal modules, indexed by module's base_address.
+ std::map<uintptr_t, size_t> module_index_;
+
+ // Callback made when sampling a profile completes.
+ const CompletedCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CallStackProfileBuilder);
+};
+
+} // namespace metrics
+
+#endif // COMPONENTS_METRICS_CALL_STACK_PROFILE_BUILDER_H_
diff --git a/chromium/components/metrics/call_stack_profile_builder_unittest.cc b/chromium/components/metrics/call_stack_profile_builder_unittest.cc
new file mode 100644
index 00000000000..93ff7cccca9
--- /dev/null
+++ b/chromium/components/metrics/call_stack_profile_builder_unittest.cc
@@ -0,0 +1,159 @@
+// 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/metrics/call_stack_profile_builder.h"
+
+#include "base/files/file_path.h"
+#include "base/test/bind_test_util.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using StackSamplingProfiler = base::StackSamplingProfiler;
+using InternalFrame = StackSamplingProfiler::InternalFrame;
+using InternalModule = StackSamplingProfiler::InternalModule;
+using CallStackProfile = StackSamplingProfiler::CallStackProfile;
+
+namespace metrics {
+
+namespace {
+
+// Called on the profiler thread when complete, to collect profile.
+void SaveProfile(CallStackProfile* profile, CallStackProfile pending_profile) {
+ *profile = std::move(pending_profile);
+}
+
+} // namespace
+
+TEST(CallStackProfileBuilderTest, SetProcessMilestone) {
+ CallStackProfile profile;
+
+ // Set up a callback to record the CallStackProfile to local variable
+ // |profile|.
+ auto profile_builder = std::make_unique<CallStackProfileBuilder>(
+ Bind(&SaveProfile, Unretained(&profile)));
+
+ profile_builder->RecordAnnotations();
+ profile_builder->OnSampleCompleted(std::vector<InternalFrame>());
+
+ CallStackProfileBuilder::SetProcessMilestone(1);
+ profile_builder->RecordAnnotations();
+ profile_builder->OnSampleCompleted(std::vector<InternalFrame>());
+
+ profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta());
+
+ ASSERT_EQ(2u, profile.samples.size());
+ EXPECT_EQ(0u, profile.samples[0].process_milestones);
+ EXPECT_EQ(1u << 1, profile.samples[1].process_milestones);
+}
+
+TEST(CallStackProfileBuilderTest, OnSampleCompleted) {
+ CallStackProfile profile;
+
+ // Set up a callback to record the CallStackProfile to local variable
+ // |profile|.
+ auto profile_builder = std::make_unique<CallStackProfileBuilder>(
+ Bind(&SaveProfile, Unretained(&profile)));
+
+ InternalModule module1 = {0xccccdddd, "1",
+ base::FilePath(FILE_PATH_LITERAL("file_path_1"))};
+ InternalModule module2 = {0xccddccdd, "2",
+ base::FilePath(FILE_PATH_LITERAL("file_path_2"))};
+ InternalFrame frame1 = {0xaaaabbbb, module1};
+ InternalFrame frame2 = {0xaabbaabb, module2};
+
+ std::vector<InternalFrame> frames = {frame1, frame2};
+
+ profile_builder->OnSampleCompleted(frames);
+ profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta());
+
+ ASSERT_EQ(1u, profile.samples.size());
+ ASSERT_EQ(2u, profile.samples[0].frames.size());
+ EXPECT_EQ(0u, profile.samples[0].frames[0].module_index);
+ EXPECT_EQ(1u, profile.samples[0].frames[1].module_index);
+}
+
+TEST(CallStackProfileBuilderTest, OnProfileCompleted) {
+ CallStackProfile profile;
+
+ // Set up a callback to record the CallStackProfile to local variable
+ // |profile|.
+ auto profile_builder = std::make_unique<CallStackProfileBuilder>(
+ Bind(&SaveProfile, Unretained(&profile)));
+
+ InternalModule module1 = {0xccccdddd, "1",
+ base::FilePath(FILE_PATH_LITERAL("file_path_1"))};
+ InternalModule module2 = {0xccddccdd, "2",
+ base::FilePath(FILE_PATH_LITERAL("file_path_2"))};
+ InternalFrame frame1 = {0xaaaabbbb, module1};
+ InternalFrame frame2 = {0xaabbaabb, module2};
+
+ std::vector<InternalFrame> frames = {frame1, frame2};
+
+ profile_builder->OnSampleCompleted(frames);
+ profile_builder->OnProfileCompleted(base::TimeDelta::FromMilliseconds(500),
+ base::TimeDelta::FromMilliseconds(100));
+
+ ASSERT_EQ(1u, profile.samples.size());
+ ASSERT_EQ(2u, profile.samples[0].frames.size());
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(500), profile.profile_duration);
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(100), profile.sampling_period);
+}
+
+TEST(CallStackProfileBuilderTest, InvalidModule) {
+ CallStackProfile profile;
+
+ // Set up a callback to record the CallStackProfile to local variable
+ // |profile|.
+ auto profile_builder = std::make_unique<CallStackProfileBuilder>(
+ Bind(&SaveProfile, Unretained(&profile)));
+
+ InternalModule module1;
+ InternalModule module2 = {0xccddccdd, "2",
+ base::FilePath(FILE_PATH_LITERAL("file_path_2"))};
+ InternalFrame frame1 = {0xaaaabbbb, module1};
+ InternalFrame frame2 = {0xaabbaabb, module2};
+
+ std::vector<InternalFrame> frames = {frame1, frame2};
+
+ profile_builder->OnSampleCompleted(frames);
+ profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta());
+
+ ASSERT_EQ(1u, profile.samples.size());
+ ASSERT_EQ(2u, profile.samples[0].frames.size());
+
+ // module1 has no information hence invalid. The module index of the frame is
+ // therefore base::kUnknownModuleIndex.
+ EXPECT_EQ(base::kUnknownModuleIndex,
+ profile.samples[0].frames[0].module_index);
+
+ EXPECT_EQ(0u, profile.samples[0].frames[1].module_index);
+}
+
+TEST(CallStackProfileBuilderTest, DedupModules) {
+ CallStackProfile profile;
+ auto profile_builder = std::make_unique<CallStackProfileBuilder>(
+ Bind(&SaveProfile, Unretained(&profile)));
+
+ InternalModule module1 = {0xccccdddd, "1",
+ base::FilePath(FILE_PATH_LITERAL("file_path_1"))};
+ InternalModule module2 = {0xccccdddd, "2",
+ base::FilePath(FILE_PATH_LITERAL("file_path_2"))};
+ InternalFrame frame1 = {0xaaaabbbb, module1};
+ InternalFrame frame2 = {0xaabbaabb, module2};
+
+ std::vector<InternalFrame> frames = {frame1, frame2};
+
+ profile_builder->OnSampleCompleted(frames);
+ profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta());
+
+ ASSERT_EQ(1u, profile.samples.size());
+ ASSERT_EQ(2u, profile.samples[0].frames.size());
+
+ // Since module1 and module2 have the same base address 0xccccdddd, they are
+ // considered the same module and therefore deduped.
+ EXPECT_EQ(0u, profile.samples[0].frames[0].module_index);
+ EXPECT_EQ(0u, profile.samples[0].frames[1].module_index);
+}
+
+} // namespace metrics
diff --git a/chromium/components/metrics/call_stack_profile_collector.cc b/chromium/components/metrics/call_stack_profile_collector.cc
index aa127fbf737..dc16a0fc909 100644
--- a/chromium/components/metrics/call_stack_profile_collector.cc
+++ b/chromium/components/metrics/call_stack_profile_collector.cc
@@ -5,7 +5,6 @@
#include "components/metrics/call_stack_profile_collector.h"
#include <utility>
-#include <vector>
#include <memory>
@@ -30,16 +29,15 @@ void CallStackProfileCollector::Create(
std::move(request));
}
-void CallStackProfileCollector::Collect(
- const CallStackProfileParams& params,
- base::TimeTicks start_timestamp,
- std::vector<CallStackProfile> profiles) {
+void CallStackProfileCollector::Collect(const CallStackProfileParams& params,
+ base::TimeTicks start_timestamp,
+ CallStackProfile profile) {
if (params.process != expected_process_)
return;
CallStackProfileParams params_copy = params;
- CallStackProfileMetricsProvider::ReceiveCompletedProfiles(
- params_copy, start_timestamp, std::move(profiles));
+ CallStackProfileMetricsProvider::ReceiveCompletedProfile(
+ params_copy, start_timestamp, std::move(profile));
}
} // namespace metrics
diff --git a/chromium/components/metrics/call_stack_profile_collector.h b/chromium/components/metrics/call_stack_profile_collector.h
index 6eeb6a87b44..97a4db4ca56 100644
--- a/chromium/components/metrics/call_stack_profile_collector.h
+++ b/chromium/components/metrics/call_stack_profile_collector.h
@@ -25,7 +25,7 @@ class CallStackProfileCollector : public mojom::CallStackProfileCollector {
// mojom::CallStackProfileCollector:
void Collect(const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
- std::vector<CallStackProfile> profiles) override;
+ CallStackProfile profile) override;
private:
// Profile params are validated to come from this process. Profiles with a
diff --git a/chromium/components/metrics/call_stack_profile_metrics_provider.cc b/chromium/components/metrics/call_stack_profile_metrics_provider.cc
index bce2bbbb871..9672bbaeb50 100644
--- a/chromium/components/metrics/call_stack_profile_metrics_provider.cc
+++ b/chromium/components/metrics/call_stack_profile_metrics_provider.cc
@@ -24,6 +24,7 @@
#include "base/process/process_info.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
@@ -36,6 +37,14 @@ namespace metrics {
namespace {
+// Cap the number of pending profiles to avoid excessive memory usage when
+// profile uploads are delayed (e.g. due to being offline). 1250 profiles
+// corresponds to 80MB of storage. Capping at this threshold loses approximately
+// 0.5% of profiles on canary and dev.
+// TODO(chengx): Remove this threshold after moving to a more memory-efficient
+// profile representation.
+const size_t kMaxPendingProfiles = 1250;
+
// Provide a mapping from the C++ "enum" definition of various process mile-
// stones to the equivalent protobuf "enum" definition. This table-lookup
// conversion allows for the implementation to evolve and still be compatible
@@ -52,16 +61,16 @@ const ProcessPhase
ProcessPhase::SHUTDOWN_START,
};
-// ProfilesState --------------------------------------------------------------
+// ProfileState --------------------------------------------------------------
// A set of profiles and the CallStackProfileMetricsProvider state associated
// with them.
-struct ProfilesState {
- ProfilesState(const CallStackProfileParams& params,
- base::TimeTicks start_timestamp,
- StackSamplingProfiler::CallStackProfiles profiles);
- ProfilesState(ProfilesState&&);
- ProfilesState& operator=(ProfilesState&&);
+struct ProfileState {
+ ProfileState(const CallStackProfileParams& params,
+ base::TimeTicks start_timestamp,
+ StackSamplingProfiler::CallStackProfile profile);
+ ProfileState(ProfileState&&);
+ ProfileState& operator=(ProfileState&&);
// The metrics-related parameters provided to
// CallStackProfileMetricsProvider::GetProfilerCallback().
@@ -70,24 +79,24 @@ struct ProfilesState {
// The time at which the profile collection was started.
base::TimeTicks start_timestamp;
- // The call stack profiles collected by the profiler.
- StackSamplingProfiler::CallStackProfiles profiles;
+ // The call stack profile collected by the profiler.
+ StackSamplingProfiler::CallStackProfile profile;
private:
- DISALLOW_COPY_AND_ASSIGN(ProfilesState);
+ DISALLOW_COPY_AND_ASSIGN(ProfileState);
};
-ProfilesState::ProfilesState(const CallStackProfileParams& params,
- base::TimeTicks start_timestamp,
- StackSamplingProfiler::CallStackProfiles profiles)
+ProfileState::ProfileState(const CallStackProfileParams& params,
+ base::TimeTicks start_timestamp,
+ StackSamplingProfiler::CallStackProfile profile)
: params(params),
start_timestamp(start_timestamp),
- profiles(std::move(profiles)) {}
+ profile(std::move(profile)) {}
-ProfilesState::ProfilesState(ProfilesState&&) = default;
+ProfileState::ProfileState(ProfileState&&) = default;
// Some versions of GCC need this for push_back to work with std::move.
-ProfilesState& ProfilesState::operator=(ProfilesState&&) = default;
+ProfileState& ProfileState::operator=(ProfileState&&) = default;
// PendingProfiles ------------------------------------------------------------
@@ -103,7 +112,7 @@ class PendingProfiles {
public:
static PendingProfiles* GetInstance();
- void Swap(std::vector<ProfilesState>* profiles);
+ void Swap(std::vector<ProfileState>* profiles);
// Enables the collection of profiles by CollectProfilesIfCollectionEnabled if
// |enabled| is true. Otherwise, clears current profiles and ignores profiles
@@ -113,9 +122,9 @@ class PendingProfiles {
// True if profiles are being collected.
bool IsCollectionEnabled() const;
- // Adds |profiles| to the list of profiles if collection is enabled; it is
+ // Adds |profile| to the list of profiles if collection is enabled; it is
// not const& because it must be passed with std::move.
- void CollectProfilesIfCollectionEnabled(ProfilesState profiles);
+ void CollectProfilesIfCollectionEnabled(ProfileState profile);
// Allows testing against the initial state multiple times.
void ResetToDefaultStateForTesting();
@@ -124,7 +133,7 @@ class PendingProfiles {
friend struct base::DefaultSingletonTraits<PendingProfiles>;
PendingProfiles();
- ~PendingProfiles();
+ ~PendingProfiles() = default;
mutable base::Lock lock_;
@@ -137,7 +146,7 @@ class PendingProfiles {
base::TimeTicks last_collection_disable_time_;
// The set of completed profiles that should be reported.
- std::vector<ProfilesState> profiles_;
+ std::vector<ProfileState> profiles_;
DISALLOW_COPY_AND_ASSIGN(PendingProfiles);
};
@@ -149,7 +158,7 @@ PendingProfiles* PendingProfiles::GetInstance() {
base::LeakySingletonTraits<PendingProfiles>>::get();
}
-void PendingProfiles::Swap(std::vector<ProfilesState>* profiles) {
+void PendingProfiles::Swap(std::vector<ProfileState>* profiles) {
base::AutoLock scoped_lock(lock_);
profiles_.swap(*profiles);
}
@@ -170,19 +179,19 @@ bool PendingProfiles::IsCollectionEnabled() const {
return collection_enabled_;
}
-void PendingProfiles::CollectProfilesIfCollectionEnabled(
- ProfilesState profiles) {
+void PendingProfiles::CollectProfilesIfCollectionEnabled(ProfileState profile) {
base::AutoLock scoped_lock(lock_);
// Only collect if collection is not disabled and hasn't been disabled
// since the start of collection for this profile.
if (!collection_enabled_ ||
(!last_collection_disable_time_.is_null() &&
- last_collection_disable_time_ >= profiles.start_timestamp)) {
+ last_collection_disable_time_ >= profile.start_timestamp)) {
return;
}
- profiles_.push_back(std::move(profiles));
+ if (profiles_.size() < kMaxPendingProfiles)
+ profiles_.push_back(std::move(profile));
}
void PendingProfiles::ResetToDefaultStateForTesting() {
@@ -200,23 +209,20 @@ void PendingProfiles::ResetToDefaultStateForTesting() {
// CallStackProfileMetricsProvider.
PendingProfiles::PendingProfiles() : collection_enabled_(true) {}
-PendingProfiles::~PendingProfiles() {}
-
-// Functions to process completed profiles ------------------------------------
+// Functions to process completed profile ------------------------------------
// Will be invoked on either the main thread or the profiler's thread. Provides
-// the profiles to PendingProfiles to append, if the collecting state allows.
-void ReceiveCompletedProfilesImpl(
+// the profile to PendingProfiles to append, if the collecting state allows.
+void ReceiveCompletedProfileImpl(
const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
- StackSamplingProfiler::CallStackProfiles profiles) {
+ StackSamplingProfiler::CallStackProfile profile) {
PendingProfiles::GetInstance()->CollectProfilesIfCollectionEnabled(
- ProfilesState(params, start_timestamp, std::move(profiles)));
+ ProfileState(params, start_timestamp, std::move(profile)));
}
-// Invoked on an arbitrary thread. Ignores the provided profiles.
-void IgnoreCompletedProfiles(
- StackSamplingProfiler::CallStackProfiles profiles) {}
+// Invoked on an arbitrary thread. Ignores the provided profile.
+void IgnoreCompletedProfile(StackSamplingProfiler::CallStackProfile profile) {}
// Functions to encode protobufs ----------------------------------------------
@@ -237,12 +243,12 @@ void CopySampleToProto(
const StackSamplingProfiler::Sample& sample,
const std::vector<StackSamplingProfiler::Module>& modules,
CallStackProfile::Sample* proto_sample) {
- for (const StackSamplingProfiler::Frame& frame : sample.frames) {
+ for (const auto& frame : sample.frames) {
CallStackProfile::Entry* entry = proto_sample->add_entry();
// A frame may not have a valid module. If so, we can't compute the
// instruction pointer offset, and we don't want to send bare pointers, so
// leave call_stack_entry empty.
- if (frame.module_index == StackSamplingProfiler::Frame::kUnknownModuleIndex)
+ if (frame.module_index == base::kUnknownModuleIndex)
continue;
int64_t module_offset =
reinterpret_cast<const char*>(frame.instruction_pointer) -
@@ -264,7 +270,7 @@ void CopyAnnotationsToProto(uint32_t new_milestones,
++bit) {
const uint32_t flag = 1U << bit;
if (new_milestones & flag) {
- if (bit >= arraysize(kProtoPhases)) {
+ if (bit >= base::size(kProtoPhases)) {
NOTREACHED();
continue;
}
@@ -283,7 +289,7 @@ void CopyProfileToProto(
return;
const bool preserve_order =
- (ordering_spec == CallStackProfileParams::PRESERVE_ORDER);
+ ordering_spec == CallStackProfileParams::PRESERVE_ORDER;
std::map<StackSamplingProfiler::Sample, int> sample_index;
uint32_t milestones = 0;
@@ -319,7 +325,7 @@ void CopyProfileToProto(
}
}
- for (const StackSamplingProfiler::Module& module : profile.modules) {
+ for (const auto& module : profile.modules) {
CallStackProfile::ModuleIdentifier* module_id =
proto_profile->add_module_id();
module_id->set_build_id(module.id);
@@ -366,7 +372,7 @@ Thread ToExecutionContextThread(CallStackProfileParams::Thread thread) {
case CallStackProfileParams::UNKNOWN_THREAD:
return UNKNOWN_THREAD;
case CallStackProfileParams::MAIN_THREAD:
- return UI_THREAD;
+ return MAIN_THREAD;
case CallStackProfileParams::IO_THREAD:
return IO_THREAD;
case CallStackProfileParams::COMPOSITOR_THREAD:
@@ -403,31 +409,29 @@ SampledProfile::TriggerEvent ToSampledProfileTriggerEvent(
const base::Feature CallStackProfileMetricsProvider::kEnableReporting = {
"SamplingProfilerReporting", base::FEATURE_DISABLED_BY_DEFAULT};
-CallStackProfileMetricsProvider::CallStackProfileMetricsProvider() {
-}
+CallStackProfileMetricsProvider::CallStackProfileMetricsProvider() {}
-CallStackProfileMetricsProvider::~CallStackProfileMetricsProvider() {
-}
+CallStackProfileMetricsProvider::~CallStackProfileMetricsProvider() {}
-StackSamplingProfiler::CompletedCallback
+CallStackProfileBuilder::CompletedCallback
CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcess(
const CallStackProfileParams& params) {
- // Ignore the profiles if the collection is disabled. If the collection state
+ // Ignore the profile if the collection is disabled. If the collection state
// changes while collecting, this will be detected by the callback and
- // profiles will be ignored at that point.
+ // profile will be ignored at that point.
if (!PendingProfiles::GetInstance()->IsCollectionEnabled())
- return base::Bind(&IgnoreCompletedProfiles);
+ return base::Bind(&IgnoreCompletedProfile);
- return base::Bind(&ReceiveCompletedProfilesImpl, params,
+ return base::Bind(&ReceiveCompletedProfileImpl, params,
base::TimeTicks::Now());
}
// static
-void CallStackProfileMetricsProvider::ReceiveCompletedProfiles(
+void CallStackProfileMetricsProvider::ReceiveCompletedProfile(
const CallStackProfileParams& params,
base::TimeTicks profile_start_time,
- base::StackSamplingProfiler::CallStackProfiles profiles) {
- ReceiveCompletedProfilesImpl(params, profile_start_time, std::move(profiles));
+ StackSamplingProfiler::CallStackProfile profile) {
+ ReceiveCompletedProfileImpl(params, profile_start_time, std::move(profile));
}
void CallStackProfileMetricsProvider::OnRecordingEnabled() {
@@ -441,25 +445,23 @@ void CallStackProfileMetricsProvider::OnRecordingDisabled() {
void CallStackProfileMetricsProvider::ProvideCurrentSessionData(
ChromeUserMetricsExtension* uma_proto) {
- std::vector<ProfilesState> pending_profiles;
+ std::vector<ProfileState> pending_profiles;
PendingProfiles::GetInstance()->Swap(&pending_profiles);
DCHECK(base::FeatureList::IsEnabled(kEnableReporting) ||
pending_profiles.empty());
- for (const ProfilesState& profiles_state : pending_profiles) {
- for (const StackSamplingProfiler::CallStackProfile& profile :
- profiles_state.profiles) {
- SampledProfile* sampled_profile = uma_proto->add_sampled_profile();
- sampled_profile->set_process(
- ToExecutionContextProcess(profiles_state.params.process));
- sampled_profile->set_thread(
- ToExecutionContextThread(profiles_state.params.thread));
- sampled_profile->set_trigger_event(
- ToSampledProfileTriggerEvent(profiles_state.params.trigger));
- CopyProfileToProto(profile, profiles_state.params.ordering_spec,
- sampled_profile->mutable_call_stack_profile());
- }
+ for (const auto& profile_state : pending_profiles) {
+ SampledProfile* sampled_profile = uma_proto->add_sampled_profile();
+ sampled_profile->set_process(
+ ToExecutionContextProcess(profile_state.params.process));
+ sampled_profile->set_thread(
+ ToExecutionContextThread(profile_state.params.thread));
+ sampled_profile->set_trigger_event(
+ ToSampledProfileTriggerEvent(profile_state.params.trigger));
+ CopyProfileToProto(profile_state.profile,
+ profile_state.params.ordering_spec,
+ sampled_profile->mutable_call_stack_profile());
}
}
diff --git a/chromium/components/metrics/call_stack_profile_metrics_provider.h b/chromium/components/metrics/call_stack_profile_metrics_provider.h
index f995a9a411b..4b05c4e896d 100644
--- a/chromium/components/metrics/call_stack_profile_metrics_provider.h
+++ b/chromium/components/metrics/call_stack_profile_metrics_provider.h
@@ -5,12 +5,11 @@
#ifndef COMPONENTS_METRICS_CALL_STACK_PROFILE_METRICS_PROVIDER_H_
#define COMPONENTS_METRICS_CALL_STACK_PROFILE_METRICS_PROVIDER_H_
-#include <vector>
-
#include "base/feature_list.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/profiler/stack_sampling_profiler.h"
+#include "components/metrics/call_stack_profile_builder.h"
#include "components/metrics/call_stack_profile_params.h"
#include "components/metrics/metrics_provider.h"
@@ -22,8 +21,9 @@ class ChromeUserMetricsExtension;
class CallStackProfileMetricsProvider : public MetricsProvider {
public:
// These milestones of a process lifetime can be passed as process "mile-
- // stones" to StackSmaplingProfile::SetProcessMilestone(). Be sure to update
- // the translation constants at the top of the .cc file when this is changed.
+ // stones" to CallStackProfileBuilder::SetProcessMilestone(). Be sure to
+ // update the translation constants at the top of the .cc file when this is
+ // changed.
enum Milestones : int {
MAIN_LOOP_START,
MAIN_NAVIGATION_START,
@@ -38,20 +38,21 @@ class CallStackProfileMetricsProvider : public MetricsProvider {
CallStackProfileMetricsProvider();
~CallStackProfileMetricsProvider() override;
- // Returns a callback for use with StackSamplingProfiler that sets up
+ // Returns a callback for use with CallStackProfileBuilder that sets up
// parameters for general browser process sampling. The callback should be
- // immediately passed to the StackSamplingProfiler, and should not be reused.
- static base::StackSamplingProfiler::CompletedCallback
+ // immediately passed to the CallStackProfileBuilder, and should not be
+ // reused.
+ static CallStackProfileBuilder::CompletedCallback
GetProfilerCallbackForBrowserProcess(const CallStackProfileParams& params);
- // Provides completed stack profiles to the metrics provider. Intended for use
+ // Provides completed stack profile to the metrics provider. Intended for use
// when receiving profiles over IPC. In-process StackSamplingProfiler users
- // should instead use a variant of GetProfilerCallback*(). |profiles| is not
+ // should instead use a variant of GetProfilerCallback*(). |profile| is not
// const& because it must be passed with std::move.
- static void ReceiveCompletedProfiles(
+ static void ReceiveCompletedProfile(
const CallStackProfileParams& params,
base::TimeTicks profile_start_time,
- base::StackSamplingProfiler::CallStackProfiles profiles);
+ base::StackSamplingProfiler::CallStackProfile profile);
// MetricsProvider:
void OnRecordingEnabled() override;
diff --git a/chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc b/chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc
index c4777110ef4..309235c3732 100644
--- a/chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc
+++ b/chromium/components/metrics/call_stack_profile_metrics_provider_unittest.cc
@@ -12,6 +12,7 @@
#include "base/macros.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/run_loop.h"
+#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
@@ -23,9 +24,8 @@
using base::StackSamplingProfiler;
using Frame = StackSamplingProfiler::Frame;
using Module = StackSamplingProfiler::Module;
-using Profile = StackSamplingProfiler::CallStackProfile;
-using Profiles = StackSamplingProfiler::CallStackProfiles;
using Sample = StackSamplingProfiler::Sample;
+using Profile = StackSamplingProfiler::CallStackProfile;
namespace {
@@ -56,61 +56,55 @@ struct ExpectedProtoProfile {
int sample_count;
};
-class ProfilesFactory {
+class ProfileFactory {
public:
- ProfilesFactory() {}
- ~ProfilesFactory() {}
+ ProfileFactory(int duration_ms, int interval_ms) {
+ profile_.profile_duration = base::TimeDelta::FromMilliseconds(duration_ms);
+ profile_.sampling_period = base::TimeDelta::FromMilliseconds(interval_ms);
+ }
+ ~ProfileFactory() {}
- ProfilesFactory& AddMilestone(int milestone);
- ProfilesFactory& NewProfile(int duration_ms, int interval_ms);
- ProfilesFactory& NewSample();
- ProfilesFactory& AddFrame(size_t module, uintptr_t offset);
- ProfilesFactory& DefineModule(const char* name,
- const base::FilePath& path,
- uintptr_t base);
+ ProfileFactory& AddMilestone(int milestone);
+ ProfileFactory& NewSample();
+ ProfileFactory& AddFrame(size_t module, uintptr_t offset);
+ ProfileFactory& DefineModule(const char* name,
+ const base::FilePath& path,
+ uintptr_t base);
- Profiles Build();
+ Profile Build();
private:
- Profiles profiles_;
+ Profile profile_;
uint32_t process_milestones_ = 0;
- DISALLOW_COPY_AND_ASSIGN(ProfilesFactory);
+ DISALLOW_COPY_AND_ASSIGN(ProfileFactory);
};
-ProfilesFactory& ProfilesFactory::AddMilestone(int milestone) {
+ProfileFactory& ProfileFactory::AddMilestone(int milestone) {
process_milestones_ |= 1 << milestone;
return *this;
}
-ProfilesFactory& ProfilesFactory::NewProfile(int duration_ms, int interval_ms) {
- profiles_.push_back(Profile());
- Profile* profile = &profiles_.back();
- profile->profile_duration = base::TimeDelta::FromMilliseconds(duration_ms);
- profile->sampling_period = base::TimeDelta::FromMilliseconds(interval_ms);
+ProfileFactory& ProfileFactory::NewSample() {
+ profile_.samples.push_back(Sample());
+ profile_.samples.back().process_milestones = process_milestones_;
return *this;
}
-ProfilesFactory& ProfilesFactory::NewSample() {
- profiles_.back().samples.push_back(Sample());
- profiles_.back().samples.back().process_milestones = process_milestones_;
+ProfileFactory& ProfileFactory::AddFrame(size_t module, uintptr_t offset) {
+ profile_.samples.back().frames.push_back(Frame(offset, module));
return *this;
}
-ProfilesFactory& ProfilesFactory::AddFrame(size_t module, uintptr_t offset) {
- profiles_.back().samples.back().frames.push_back(Frame(offset, module));
+ProfileFactory& ProfileFactory::DefineModule(const char* name,
+ const base::FilePath& path,
+ uintptr_t base) {
+ profile_.modules.push_back(Module(base, name, path));
return *this;
}
-ProfilesFactory& ProfilesFactory::DefineModule(const char* name,
- const base::FilePath& path,
- uintptr_t base) {
- profiles_.back().modules.push_back(Module(base, name, path));
- return *this;
-}
-
-Profiles ProfilesFactory::Build() {
- return std::move(profiles_);
+Profile ProfileFactory::Build() {
+ return std::move(profile_);
}
} // namespace
@@ -118,7 +112,7 @@ Profiles ProfilesFactory::Build() {
namespace metrics {
// This test fixture enables the feature that
-// CallStackProfileMetricsProvider depends on to report profiles.
+// CallStackProfileMetricsProvider depends on to report a profile.
class CallStackProfileMetricsProviderTest : public testing::Test {
public:
CallStackProfileMetricsProviderTest() {
@@ -128,11 +122,11 @@ class CallStackProfileMetricsProviderTest : public testing::Test {
~CallStackProfileMetricsProviderTest() override {}
- // Utility function to append profiles to the metrics provider.
- void AppendProfiles(const CallStackProfileParams& params, Profiles profiles) {
+ // Utility function to append a profile to the metrics provider.
+ void AppendProfile(const CallStackProfileParams& params, Profile profile) {
CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcess(
params)
- .Run(std::move(profiles));
+ .Run(std::move(profile));
}
void VerifyProfileProto(const ExpectedProtoProfile& expected,
@@ -203,160 +197,6 @@ void CallStackProfileMetricsProviderTest::VerifyProfileProto(
}
}
-// Checks that all properties from multiple profiles are filled as expected.
-TEST_F(CallStackProfileMetricsProviderTest, MultipleProfiles) {
- const uintptr_t moduleA_base_address = 0x1000;
- const uintptr_t moduleB_base_address = 0x2000;
- const uintptr_t moduleC_base_address = 0x3000;
- const char* moduleA_name = "ABCD";
- const char* moduleB_name = "EFGH";
- const char* moduleC_name = "MNOP";
-
- // Values for Windows generated with:
- // perl -MDigest::MD5=md5 -MEncode=encode
- // -e 'for(@ARGV){printf "%x\n", unpack "Q>", md5 encode "UTF-16LE", $_}'
- // chrome.exe third_party.dll third_party2.dll
- //
- // Values for Linux generated with:
- // perl -MDigest::MD5=md5
- // -e 'for(@ARGV){printf "%x\n", unpack "Q>", md5 $_}'
- // chrome third_party.so third_party2.so
-#if defined(OS_WIN)
- uint64_t moduleA_md5 = 0x46C3E4166659AC02ULL;
- uint64_t moduleB_md5 = 0x7E2B8BFDDEAE1ABAULL;
- uint64_t moduleC_md5 = 0x87B66F4573A4D5CAULL;
- base::FilePath moduleA_path(L"c:\\some\\path\\to\\chrome.exe");
- base::FilePath moduleB_path(L"c:\\some\\path\\to\\third_party.dll");
- base::FilePath moduleC_path(L"c:\\some\\path\\to\\third_party2.dll");
-#else
- uint64_t moduleA_md5 = 0x554838A8451AC36CULL;
- uint64_t moduleB_md5 = 0x843661148659C9F8ULL;
- uint64_t moduleC_md5 = 0xB4647E539FA6EC9EULL;
- base::FilePath moduleA_path("/some/path/to/chrome");
- base::FilePath moduleB_path("/some/path/to/third_party.so");
- base::FilePath moduleC_path("/some/path/to/third_party2.so");
-#endif
-
- // Represents two stack samples for each of two profiles, where each stack
- // contains three frames. Each frame contains an instruction pointer and a
- // module index corresponding to the module for the profile in
- // profile_modules.
- //
- // So, the first stack sample below has its top frame in module 0 at an offset
- // of 0x10 from the module's base address, the next-to-top frame in module 1
- // at an offset of 0x20 from the module's base address, and the bottom frame
- // in module 0 at an offset of 0x30 from the module's base address
- Profiles profiles = ProfilesFactory()
- .NewProfile(100, 10)
- .DefineModule(moduleA_name, moduleA_path, moduleA_base_address)
- .DefineModule(moduleB_name, moduleB_path, moduleB_base_address)
-
- .NewSample()
- .AddFrame(0, moduleA_base_address + 0x10)
- .AddFrame(1, moduleB_base_address + 0x20)
- .AddFrame(0, moduleA_base_address + 0x30)
- .NewSample()
- .AddFrame(1, moduleB_base_address + 0x10)
- .AddFrame(0, moduleA_base_address + 0x20)
- .AddFrame(1, moduleB_base_address + 0x30)
-
- .NewProfile(200, 20)
- .DefineModule(moduleC_name, moduleC_path, moduleC_base_address)
- .DefineModule(moduleA_name, moduleA_path, moduleA_base_address)
-
- .NewSample()
- .AddFrame(0, moduleC_base_address + 0x10)
- .AddFrame(1, moduleA_base_address + 0x20)
- .AddFrame(0, moduleC_base_address + 0x30)
- .NewSample()
- .AddFrame(1, moduleA_base_address + 0x10)
- .AddFrame(0, moduleC_base_address + 0x20)
- .AddFrame(1, moduleA_base_address + 0x30)
-
- .Build();
-
- const ExpectedProtoModule expected_proto_modules1[] = {
- { moduleA_name, moduleA_md5, moduleA_base_address },
- { moduleB_name, moduleB_md5, moduleB_base_address }
- };
-
- const ExpectedProtoEntry expected_proto_entries11[] = {
- { 0, 0x10 },
- { 1, 0x20 },
- { 0, 0x30 },
- };
- const ExpectedProtoEntry expected_proto_entries12[] = {
- { 1, 0x10 },
- { 0, 0x20 },
- { 1, 0x30 },
- };
- const ExpectedProtoSample expected_proto_samples1[] = {
- {
- 0, expected_proto_entries11, arraysize(expected_proto_entries11), 1,
- },
- {
- 0, expected_proto_entries12, arraysize(expected_proto_entries12), 1,
- },
- };
-
- const ExpectedProtoModule expected_proto_modules2[] = {
- { moduleC_name, moduleC_md5, moduleC_base_address },
- { moduleA_name, moduleA_md5, moduleA_base_address }
- };
-
- const ExpectedProtoEntry expected_proto_entries21[] = {
- { 0, 0x10 },
- { 1, 0x20 },
- { 0, 0x30 },
- };
- const ExpectedProtoEntry expected_proto_entries22[] = {
- { 1, 0x10 },
- { 0, 0x20 },
- { 1, 0x30 },
- };
- const ExpectedProtoSample expected_proto_samples2[] = {
- {
- 0, expected_proto_entries11, arraysize(expected_proto_entries21), 1,
- },
- {
- 0, expected_proto_entries12, arraysize(expected_proto_entries22), 1,
- },
- };
-
- const ExpectedProtoProfile expected_proto_profiles[] = {
- {
- 100, 10,
- expected_proto_modules1, arraysize(expected_proto_modules1),
- expected_proto_samples1, arraysize(expected_proto_samples1),
- },
- {
- 200, 20,
- expected_proto_modules2, arraysize(expected_proto_modules2),
- expected_proto_samples2, arraysize(expected_proto_samples2),
- },
- };
-
- ASSERT_EQ(arraysize(expected_proto_profiles), profiles.size());
-
- CallStackProfileMetricsProvider provider;
- provider.OnRecordingEnabled();
- CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
- CallStackProfileParams::MAIN_THREAD,
- CallStackProfileParams::PROCESS_STARTUP,
- CallStackProfileParams::MAY_SHUFFLE);
- AppendProfiles(params, std::move(profiles));
- ChromeUserMetricsExtension uma_proto;
- provider.ProvideCurrentSessionData(&uma_proto);
-
- ASSERT_EQ(static_cast<int>(arraysize(expected_proto_profiles)),
- uma_proto.sampled_profile().size());
- for (size_t p = 0; p < arraysize(expected_proto_profiles); ++p) {
- SCOPED_TRACE("profile " + base::NumberToString(p));
- VerifyProfileProto(expected_proto_profiles[p],
- uma_proto.sampled_profile().Get(p));
- }
-}
-
// Checks that all duplicate samples are collapsed with
// preserve_sample_ordering = false.
TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
@@ -371,31 +211,31 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
base::FilePath module_path("/some/path/to/chrome");
#endif
- Profiles profiles = ProfilesFactory()
- .NewProfile(100, 10)
- .DefineModule(module_name, module_path, module_base_address)
-
- .AddMilestone(0)
- .NewSample()
- .AddFrame(0, module_base_address + 0x10)
- .NewSample()
- .AddFrame(0, module_base_address + 0x20)
- .NewSample()
- .AddFrame(0, module_base_address + 0x10)
- .NewSample()
- .AddFrame(0, module_base_address + 0x10)
-
- .AddMilestone(1)
- .NewSample()
- .AddFrame(0, module_base_address + 0x10)
- .NewSample()
- .AddFrame(0, module_base_address + 0x20)
- .NewSample()
- .AddFrame(0, module_base_address + 0x10)
- .NewSample()
- .AddFrame(0, module_base_address + 0x10)
-
- .Build();
+ Profile profile =
+ ProfileFactory(100, 10)
+ .DefineModule(module_name, module_path, module_base_address)
+
+ .AddMilestone(0)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x10)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x20)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x10)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x10)
+
+ .AddMilestone(1)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x10)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x20)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x10)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x10)
+
+ .Build();
const ExpectedProtoModule expected_proto_modules[] = {
{ module_name, module_md5, module_base_address },
@@ -412,33 +252,28 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksUnordered) {
{ 0, &expected_proto_entries[1], 1, 1 },
};
- const ExpectedProtoProfile expected_proto_profiles[] = {
- {
- 100, 10,
- expected_proto_modules, arraysize(expected_proto_modules),
- expected_proto_samples, arraysize(expected_proto_samples),
- },
+ const ExpectedProtoProfile expected_proto_profile = {
+ 100,
+ 10,
+ expected_proto_modules,
+ base::size(expected_proto_modules),
+ expected_proto_samples,
+ base::size(expected_proto_samples),
};
- ASSERT_EQ(arraysize(expected_proto_profiles), profiles.size());
-
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
CallStackProfileParams::MAIN_THREAD,
CallStackProfileParams::PROCESS_STARTUP,
CallStackProfileParams::MAY_SHUFFLE);
- AppendProfiles(params, std::move(profiles));
+ AppendProfile(params, std::move(profile));
ChromeUserMetricsExtension uma_proto;
provider.ProvideCurrentSessionData(&uma_proto);
- ASSERT_EQ(static_cast<int>(arraysize(expected_proto_profiles)),
- uma_proto.sampled_profile().size());
- for (size_t p = 0; p < arraysize(expected_proto_profiles); ++p) {
- SCOPED_TRACE("profile " + base::NumberToString(p));
- VerifyProfileProto(expected_proto_profiles[p],
- uma_proto.sampled_profile().Get(p));
- }
+ ASSERT_EQ(1, uma_proto.sampled_profile().size());
+ VerifyProfileProto(expected_proto_profile,
+ uma_proto.sampled_profile().Get(0));
}
// Checks that only contiguous duplicate samples are collapsed with
@@ -455,31 +290,31 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) {
base::FilePath module_path("/some/path/to/chrome");
#endif
- Profiles profiles = ProfilesFactory()
- .NewProfile(100, 10)
- .DefineModule(module_name, module_path, module_base_address)
-
- .AddMilestone(0)
- .NewSample()
- .AddFrame(0, module_base_address + 0x10)
- .NewSample()
- .AddFrame(0, module_base_address + 0x20)
- .NewSample()
- .AddFrame(0, module_base_address + 0x10)
- .NewSample()
- .AddFrame(0, module_base_address + 0x10)
-
- .AddMilestone(1)
- .NewSample()
- .AddFrame(0, module_base_address + 0x10)
- .NewSample()
- .AddFrame(0, module_base_address + 0x20)
- .NewSample()
- .AddFrame(0, module_base_address + 0x10)
- .NewSample()
- .AddFrame(0, module_base_address + 0x10)
-
- .Build();
+ Profile profile =
+ ProfileFactory(100, 10)
+ .DefineModule(module_name, module_path, module_base_address)
+
+ .AddMilestone(0)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x10)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x20)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x10)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x10)
+
+ .AddMilestone(1)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x10)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x20)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x10)
+ .NewSample()
+ .AddFrame(0, module_base_address + 0x10)
+
+ .Build();
const ExpectedProtoModule expected_proto_modules[] = {
{ module_name, module_md5, module_base_address },
@@ -498,42 +333,36 @@ TEST_F(CallStackProfileMetricsProviderTest, RepeatedStacksOrdered) {
{ 0, &expected_proto_entries[0], 1, 2 },
};
- const ExpectedProtoProfile expected_proto_profiles[] = {
- {
- 100, 10,
- expected_proto_modules, arraysize(expected_proto_modules),
- expected_proto_samples, arraysize(expected_proto_samples),
- },
+ const ExpectedProtoProfile expected_proto_profile = {
+ 100,
+ 10,
+ expected_proto_modules,
+ base::size(expected_proto_modules),
+ expected_proto_samples,
+ base::size(expected_proto_samples),
};
- ASSERT_EQ(arraysize(expected_proto_profiles), profiles.size());
-
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
CallStackProfileParams::MAIN_THREAD,
CallStackProfileParams::PROCESS_STARTUP,
CallStackProfileParams::PRESERVE_ORDER);
- AppendProfiles(params, std::move(profiles));
+ AppendProfile(params, std::move(profile));
ChromeUserMetricsExtension uma_proto;
provider.ProvideCurrentSessionData(&uma_proto);
- ASSERT_EQ(static_cast<int>(arraysize(expected_proto_profiles)),
- uma_proto.sampled_profile().size());
- for (size_t p = 0; p < arraysize(expected_proto_profiles); ++p) {
- SCOPED_TRACE("profile " + base::NumberToString(p));
- VerifyProfileProto(expected_proto_profiles[p],
- uma_proto.sampled_profile().Get(p));
- }
+ ASSERT_EQ(1, uma_proto.sampled_profile().size());
+ VerifyProfileProto(expected_proto_profile,
+ uma_proto.sampled_profile().Get(0));
}
// Checks that unknown modules produce an empty Entry.
TEST_F(CallStackProfileMetricsProviderTest, UnknownModule) {
- Profiles profiles = ProfilesFactory()
- .NewProfile(100, 10)
- .NewSample()
- .AddFrame(Frame::kUnknownModuleIndex, 0x1234)
- .Build();
+ Profile profile = ProfileFactory(100, 10)
+ .NewSample()
+ .AddFrame(base::kUnknownModuleIndex, 0x1234)
+ .Build();
const ExpectedProtoEntry expected_proto_entries[] = {
{ -1, 0 },
@@ -542,48 +371,41 @@ TEST_F(CallStackProfileMetricsProviderTest, UnknownModule) {
{ 0, &expected_proto_entries[0], 1, 1 },
};
- const ExpectedProtoProfile expected_proto_profiles[] = {
- {
- 100, 10,
- nullptr, 0,
- expected_proto_samples, arraysize(expected_proto_samples),
- },
+ const ExpectedProtoProfile expected_proto_profile = {
+ 100,
+ 10,
+ nullptr,
+ 0,
+ expected_proto_samples,
+ base::size(expected_proto_samples),
};
- ASSERT_EQ(arraysize(expected_proto_profiles), profiles.size());
-
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
CallStackProfileParams::MAIN_THREAD,
CallStackProfileParams::PROCESS_STARTUP,
CallStackProfileParams::MAY_SHUFFLE);
- AppendProfiles(params, std::move(profiles));
+ AppendProfile(params, std::move(profile));
ChromeUserMetricsExtension uma_proto;
provider.ProvideCurrentSessionData(&uma_proto);
- ASSERT_EQ(static_cast<int>(arraysize(expected_proto_profiles)),
- uma_proto.sampled_profile().size());
- for (size_t p = 0; p < arraysize(expected_proto_profiles); ++p) {
- SCOPED_TRACE("profile " + base::NumberToString(p));
- VerifyProfileProto(expected_proto_profiles[p],
- uma_proto.sampled_profile().Get(p));
- }
+ ASSERT_EQ(1, uma_proto.sampled_profile().size());
+ VerifyProfileProto(expected_proto_profile,
+ uma_proto.sampled_profile().Get(0));
}
-// Checks that pending profiles are only passed back to
+// Checks that the pending profile is only passed back to
// ProvideCurrentSessionData once.
-TEST_F(CallStackProfileMetricsProviderTest, ProfilesProvidedOnlyOnce) {
+TEST_F(CallStackProfileMetricsProviderTest, ProfileProvidedOnlyOnce) {
CallStackProfileMetricsProvider provider;
for (int r = 0; r < 2; ++r) {
- Profiles profiles = ProfilesFactory()
+ Profile profile =
// Use the sampling period to distinguish the two profiles.
- .NewProfile(100, r)
- .NewSample()
- .AddFrame(Frame::kUnknownModuleIndex, 0x1234)
- .Build();
-
- ASSERT_EQ(1U, profiles.size());
+ ProfileFactory(100, r)
+ .NewSample()
+ .AddFrame(base::kUnknownModuleIndex, 0x1234)
+ .Build();
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
@@ -591,7 +413,7 @@ TEST_F(CallStackProfileMetricsProviderTest, ProfilesProvidedOnlyOnce) {
CallStackProfileParams::MAIN_THREAD,
CallStackProfileParams::PROCESS_STARTUP,
CallStackProfileParams::MAY_SHUFFLE);
- AppendProfiles(params, std::move(profiles));
+ AppendProfile(params, std::move(profile));
ChromeUserMetricsExtension uma_proto;
provider.ProvideCurrentSessionData(&uma_proto);
@@ -605,23 +427,20 @@ TEST_F(CallStackProfileMetricsProviderTest, ProfilesProvidedOnlyOnce) {
}
}
-// Checks that pending profiles are provided to ProvideCurrentSessionData
+// Checks that the pending profile is provided to ProvideCurrentSessionData
// when collected before CallStackProfileMetricsProvider is instantiated.
TEST_F(CallStackProfileMetricsProviderTest,
- ProfilesProvidedWhenCollectedBeforeInstantiation) {
- Profiles profiles = ProfilesFactory()
- .NewProfile(100, 10)
- .NewSample()
- .AddFrame(Frame::kUnknownModuleIndex, 0x1234)
- .Build();
-
- ASSERT_EQ(1U, profiles.size());
+ ProfileProvidedWhenCollectedBeforeInstantiation) {
+ Profile profile = ProfileFactory(100, 10)
+ .NewSample()
+ .AddFrame(base::kUnknownModuleIndex, 0x1234)
+ .Build();
CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
CallStackProfileParams::MAIN_THREAD,
CallStackProfileParams::PROCESS_STARTUP,
CallStackProfileParams::MAY_SHUFFLE);
- AppendProfiles(params, std::move(profiles));
+ AppendProfile(params, std::move(profile));
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
@@ -631,16 +450,13 @@ TEST_F(CallStackProfileMetricsProviderTest,
EXPECT_EQ(1, uma_proto.sampled_profile_size());
}
-// Checks that pending profiles are not provided to ProvideCurrentSessionData
+// Checks that the pending profile is not provided to ProvideCurrentSessionData
// while recording is disabled.
-TEST_F(CallStackProfileMetricsProviderTest, ProfilesNotProvidedWhileDisabled) {
- Profiles profiles = ProfilesFactory()
- .NewProfile(100, 10)
- .NewSample()
- .AddFrame(Frame::kUnknownModuleIndex, 0x1234)
- .Build();
-
- ASSERT_EQ(1U, profiles.size());
+TEST_F(CallStackProfileMetricsProviderTest, ProfileNotProvidedWhileDisabled) {
+ Profile profile = ProfileFactory(100, 10)
+ .NewSample()
+ .AddFrame(base::kUnknownModuleIndex, 0x1234)
+ .Build();
CallStackProfileMetricsProvider provider;
provider.OnRecordingDisabled();
@@ -648,89 +464,86 @@ TEST_F(CallStackProfileMetricsProviderTest, ProfilesNotProvidedWhileDisabled) {
CallStackProfileParams::MAIN_THREAD,
CallStackProfileParams::PROCESS_STARTUP,
CallStackProfileParams::MAY_SHUFFLE);
- AppendProfiles(params, std::move(profiles));
+ AppendProfile(params, std::move(profile));
ChromeUserMetricsExtension uma_proto;
provider.ProvideCurrentSessionData(&uma_proto);
EXPECT_EQ(0, uma_proto.sampled_profile_size());
}
-// Checks that pending profiles are not provided to ProvideCurrentSessionData
+// Checks that the pending profile is not provided to ProvideCurrentSessionData
// if recording is disabled while profiling.
TEST_F(CallStackProfileMetricsProviderTest,
- ProfilesNotProvidedAfterChangeToDisabled) {
+ ProfileNotProvidedAfterChangeToDisabled) {
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
CallStackProfileParams::MAIN_THREAD,
CallStackProfileParams::PROCESS_STARTUP,
CallStackProfileParams::MAY_SHUFFLE);
- base::StackSamplingProfiler::CompletedCallback callback =
+ CallStackProfileBuilder::CompletedCallback callback =
CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcess(
params);
provider.OnRecordingDisabled();
- Profiles profiles = ProfilesFactory()
- .NewProfile(100, 10)
- .NewSample()
- .AddFrame(Frame::kUnknownModuleIndex, 0x1234)
- .Build();
- callback.Run(std::move(profiles));
+ Profile profile = ProfileFactory(100, 10)
+ .NewSample()
+ .AddFrame(base::kUnknownModuleIndex, 0x1234)
+ .Build();
+ callback.Run(std::move(profile));
ChromeUserMetricsExtension uma_proto;
provider.ProvideCurrentSessionData(&uma_proto);
EXPECT_EQ(0, uma_proto.sampled_profile_size());
}
-// Checks that pending profiles are not provided to ProvideCurrentSessionData if
-// recording is enabled, but then disabled and reenabled while profiling.
+// Checks that the pending profile is not provided to ProvideCurrentSessionData
+// if recording is enabled, but then disabled and reenabled while profiling.
TEST_F(CallStackProfileMetricsProviderTest,
- ProfilesNotProvidedAfterChangeToDisabledThenEnabled) {
+ ProfileNotProvidedAfterChangeToDisabledThenEnabled) {
CallStackProfileMetricsProvider provider;
provider.OnRecordingEnabled();
CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
CallStackProfileParams::MAIN_THREAD,
CallStackProfileParams::PROCESS_STARTUP,
CallStackProfileParams::MAY_SHUFFLE);
- base::StackSamplingProfiler::CompletedCallback callback =
+ CallStackProfileBuilder::CompletedCallback callback =
CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcess(
params);
provider.OnRecordingDisabled();
provider.OnRecordingEnabled();
- Profiles profiles = ProfilesFactory()
- .NewProfile(100, 10)
- .NewSample()
- .AddFrame(Frame::kUnknownModuleIndex, 0x1234)
- .Build();
- callback.Run(std::move(profiles));
+ Profile profile = ProfileFactory(100, 10)
+ .NewSample()
+ .AddFrame(base::kUnknownModuleIndex, 0x1234)
+ .Build();
+ callback.Run(std::move(profile));
ChromeUserMetricsExtension uma_proto;
provider.ProvideCurrentSessionData(&uma_proto);
EXPECT_EQ(0, uma_proto.sampled_profile_size());
}
-// Checks that pending profiles are not provided to ProvideCurrentSessionData
+// Checks that the pending profile is not provided to ProvideCurrentSessionData
// if recording is disabled, but then enabled while profiling.
TEST_F(CallStackProfileMetricsProviderTest,
- ProfilesNotProvidedAfterChangeFromDisabled) {
+ ProfileNotProvidedAfterChangeFromDisabled) {
CallStackProfileMetricsProvider provider;
provider.OnRecordingDisabled();
CallStackProfileParams params(CallStackProfileParams::BROWSER_PROCESS,
CallStackProfileParams::MAIN_THREAD,
CallStackProfileParams::PROCESS_STARTUP,
CallStackProfileParams::MAY_SHUFFLE);
- base::StackSamplingProfiler::CompletedCallback callback =
+ CallStackProfileBuilder::CompletedCallback callback =
CallStackProfileMetricsProvider::GetProfilerCallbackForBrowserProcess(
params);
provider.OnRecordingEnabled();
- Profiles profiles = ProfilesFactory()
- .NewProfile(100, 10)
- .NewSample()
- .AddFrame(Frame::kUnknownModuleIndex, 0x1234)
- .Build();
- callback.Run(std::move(profiles));
+ Profile profile = ProfileFactory(100, 10)
+ .NewSample()
+ .AddFrame(base::kUnknownModuleIndex, 0x1234)
+ .Build();
+ callback.Run(std::move(profile));
ChromeUserMetricsExtension uma_proto;
provider.ProvideCurrentSessionData(&uma_proto);
diff --git a/chromium/components/metrics/child_call_stack_profile_collector.cc b/chromium/components/metrics/child_call_stack_profile_collector.cc
index 978ecd7382c..3cdfc55e7d1 100644
--- a/chromium/components/metrics/child_call_stack_profile_collector.cc
+++ b/chromium/components/metrics/child_call_stack_profile_collector.cc
@@ -15,30 +15,30 @@
namespace metrics {
-ChildCallStackProfileCollector::ProfilesState::ProfilesState() = default;
-ChildCallStackProfileCollector::ProfilesState::ProfilesState(ProfilesState&&) =
+ChildCallStackProfileCollector::ProfileState::ProfileState() = default;
+ChildCallStackProfileCollector::ProfileState::ProfileState(ProfileState&&) =
default;
-ChildCallStackProfileCollector::ProfilesState::ProfilesState(
+ChildCallStackProfileCollector::ProfileState::ProfileState(
const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
- base::StackSamplingProfiler::CallStackProfiles profiles)
+ base::StackSamplingProfiler::CallStackProfile profile)
: params(params),
start_timestamp(start_timestamp),
- profiles(std::move(profiles)) {}
+ profile(std::move(profile)) {}
-ChildCallStackProfileCollector::ProfilesState::~ProfilesState() = default;
+ChildCallStackProfileCollector::ProfileState::~ProfileState() = default;
// Some versions of GCC need this for push_back to work with std::move.
-ChildCallStackProfileCollector::ProfilesState&
-ChildCallStackProfileCollector::ProfilesState::operator=(ProfilesState&&) =
+ChildCallStackProfileCollector::ProfileState&
+ChildCallStackProfileCollector::ProfileState::operator=(ProfileState&&) =
default;
ChildCallStackProfileCollector::ChildCallStackProfileCollector() {}
ChildCallStackProfileCollector::~ChildCallStackProfileCollector() {}
-base::StackSamplingProfiler::CompletedCallback
+CallStackProfileBuilder::CompletedCallback
ChildCallStackProfileCollector::GetProfilerCallback(
const CallStackProfileParams& params,
base::TimeTicks profile_start_time) {
@@ -59,9 +59,9 @@ void ChildCallStackProfileCollector::SetParentProfileCollector(
DCHECK(!parent_collector_);
parent_collector_ = std::move(parent_collector);
if (parent_collector_) {
- for (ProfilesState& state : profiles_) {
+ for (ProfileState& state : profiles_) {
parent_collector_->Collect(state.params, state.start_timestamp,
- std::move(state.profiles));
+ std::move(state.profile));
}
}
profiles_.clear();
@@ -70,16 +70,16 @@ void ChildCallStackProfileCollector::SetParentProfileCollector(
void ChildCallStackProfileCollector::Collect(
const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
- std::vector<CallStackProfile> profiles) {
+ CallStackProfile profile) {
// Impl function is used as it needs to PostTask() to itself on a different
// thread - which only works with a void return value.
- CollectImpl(params, start_timestamp, std::move(profiles));
+ CollectImpl(params, start_timestamp, std::move(profile));
}
void ChildCallStackProfileCollector::CollectImpl(
const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
- std::vector<CallStackProfile> profiles) {
+ CallStackProfile profile) {
base::AutoLock alock(lock_);
if (task_runner_ &&
// The profiler thread does not have a task runner. Attempting to
@@ -91,15 +91,15 @@ void ChildCallStackProfileCollector::CollectImpl(
FROM_HERE, base::BindOnce(&ChildCallStackProfileCollector::CollectImpl,
// This class has lazy instance lifetime.
base::Unretained(this), params,
- start_timestamp, std::move(profiles)));
+ start_timestamp, std::move(profile)));
return;
}
if (parent_collector_) {
- parent_collector_->Collect(params, start_timestamp, std::move(profiles));
+ parent_collector_->Collect(params, start_timestamp, std::move(profile));
} else if (retain_profiles_) {
profiles_.push_back(
- ProfilesState(params, start_timestamp, std::move(profiles)));
+ ProfileState(params, start_timestamp, std::move(profile)));
}
}
diff --git a/chromium/components/metrics/child_call_stack_profile_collector.h b/chromium/components/metrics/child_call_stack_profile_collector.h
index a829f51911a..6e5f331326f 100644
--- a/chromium/components/metrics/child_call_stack_profile_collector.h
+++ b/chromium/components/metrics/child_call_stack_profile_collector.h
@@ -11,6 +11,7 @@
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
+#include "components/metrics/call_stack_profile_builder.h"
#include "components/metrics/public/interfaces/call_stack_profile_collector.mojom.h"
namespace service_manager {
@@ -41,7 +42,7 @@ namespace metrics {
// g_call_stack_profile_collector = LAZY_INSTANCE_INITIALIZER;
//
// Then, invoke CreateCompletedCallback() to generate the CompletedCallback to
-// pass when creating the StackSamplingProfiler.
+// pass when creating the CallStackProfileBuilder.
//
// When the mojo InterfaceProvider becomes available, provide it via
// SetParentProfileCollector().
@@ -50,11 +51,11 @@ class ChildCallStackProfileCollector {
ChildCallStackProfileCollector();
~ChildCallStackProfileCollector();
- // Get a callback for use with StackSamplingProfiler that provides completed
- // profiles to this object. The callback should be immediately passed to the
- // StackSamplingProfiler, and should not be reused between
- // StackSamplingProfilers. This function may be called on any thread.
- base::StackSamplingProfiler::CompletedCallback GetProfilerCallback(
+ // Get a callback for use with CallStackProfileBuilder that provides the
+ // completed profile to this object. The callback should be immediately passed
+ // to the CallStackProfileBuilder, and should not be reused between
+ // CallStackProfileBuilders. This function may be called on any thread.
+ CallStackProfileBuilder::CompletedCallback GetProfilerCallback(
const CallStackProfileParams& params,
base::TimeTicks profile_start_time);
@@ -67,39 +68,38 @@ class ChildCallStackProfileCollector {
private:
friend class ChildCallStackProfileCollectorTest;
- // Bundles together a set of collected profiles and the collection state for
- // storage, pending availability of the parent mojo interface. |profiles|
+ // Bundles together a collected profile and the collection state for
+ // storage, pending availability of the parent mojo interface. |profile|
// is not const& because it must be passed with std::move.
- struct ProfilesState {
- ProfilesState();
- ProfilesState(ProfilesState&&);
- ProfilesState(
- const CallStackProfileParams& params,
- base::TimeTicks start_timestamp,
- base::StackSamplingProfiler::CallStackProfiles profiles);
- ~ProfilesState();
+ struct ProfileState {
+ ProfileState();
+ ProfileState(ProfileState&&);
+ ProfileState(const CallStackProfileParams& params,
+ base::TimeTicks start_timestamp,
+ base::StackSamplingProfiler::CallStackProfile profile);
+ ~ProfileState();
- ProfilesState& operator=(ProfilesState&&);
+ ProfileState& operator=(ProfileState&&);
CallStackProfileParams params;
base::TimeTicks start_timestamp;
- // The sampled profiles.
- base::StackSamplingProfiler::CallStackProfiles profiles;
+ // The sampled profile.
+ base::StackSamplingProfiler::CallStackProfile profile;
private:
- DISALLOW_COPY_AND_ASSIGN(ProfilesState);
+ DISALLOW_COPY_AND_ASSIGN(ProfileState);
};
using CallStackProfile = base::StackSamplingProfiler::CallStackProfile;
void Collect(const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
- std::vector<CallStackProfile> profiles);
+ CallStackProfile profile);
void CollectImpl(const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
- std::vector<CallStackProfile> profiles);
+ CallStackProfile profile);
// This object may be accessed on any thread, including the profiler
// thread. The expected use case for the object is to be created and have
@@ -107,7 +107,7 @@ class ChildCallStackProfileCollector {
// of PostTask and the like for inter-thread communication.
base::Lock lock_;
- // Whether to retain profiles when the interface is not set. Remains true
+ // Whether to retain the profile when the interface is not set. Remains true
// until the invocation of SetParentProfileCollector(), at which point it is
// false for the rest of the object lifetime.
bool retain_profiles_ = true;
@@ -123,7 +123,7 @@ class ChildCallStackProfileCollector {
// Profiles being cached by this object, pending a parent interface to be
// supplied.
- std::vector<ProfilesState> profiles_;
+ std::vector<ProfileState> profiles_;
DISALLOW_COPY_AND_ASSIGN(ChildCallStackProfileCollector);
};
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 8bf1c5b7ca8..aa924cb0bc7 100644
--- a/chromium/components/metrics/child_call_stack_profile_collector_unittest.cc
+++ b/chromium/components/metrics/child_call_stack_profile_collector_unittest.cc
@@ -19,11 +19,6 @@
namespace metrics {
-namespace {
-
-
-} // namespace
-
class ChildCallStackProfileCollectorTest : public testing::Test {
protected:
class Receiver : public mojom::CallStackProfileCollector {
@@ -36,14 +31,12 @@ class ChildCallStackProfileCollectorTest : public testing::Test {
void Collect(const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
- std::vector<CallStackProfile> profiles) override {
- this->profiles.push_back(ChildCallStackProfileCollector::ProfilesState(
- params,
- start_timestamp,
- std::move(profiles)));
+ CallStackProfile profile) override {
+ this->profiles.push_back(ChildCallStackProfileCollector::ProfileState(
+ params, start_timestamp, std::move(profile)));
}
- std::vector<ChildCallStackProfileCollector::ProfilesState> profiles;
+ std::vector<ChildCallStackProfileCollector::ProfileState> profiles;
private:
mojo::Binding<mojom::CallStackProfileCollector> binding_;
@@ -54,18 +47,14 @@ class ChildCallStackProfileCollectorTest : public testing::Test {
ChildCallStackProfileCollectorTest()
: receiver_impl_(new Receiver(MakeRequest(&receiver_))) {}
- void CollectEmptyProfiles(
- const CallStackProfileParams& params,
- size_t profile_count) {
- base::StackSamplingProfiler::CallStackProfiles profiles;
- for (size_t i = 0; i < profile_count; ++i)
- profiles.push_back(base::StackSamplingProfiler::CallStackProfile());
+ void CollectEmptyProfile(const CallStackProfileParams& params) {
+ base::StackSamplingProfiler::CallStackProfile profile;
child_collector_.GetProfilerCallback(params, base::TimeTicks::Now())
- .Run(std::move(profiles));
+ .Run(std::move(profile));
}
- const std::vector<ChildCallStackProfileCollector::ProfilesState>&
- profiles() const {
+ const std::vector<ChildCallStackProfileCollector::ProfileState>& profiles()
+ const {
return child_collector_.profiles_;
}
@@ -82,13 +71,11 @@ class ChildCallStackProfileCollectorTest : public testing::Test {
TEST_F(ChildCallStackProfileCollectorTest, InterfaceProvided) {
EXPECT_EQ(0u, profiles().size());
- // Add profiles before providing the interface.
- CollectEmptyProfiles(
- CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS,
- CallStackProfileParams::MAIN_THREAD,
- CallStackProfileParams::JANKY_TASK,
- CallStackProfileParams::PRESERVE_ORDER),
- 2);
+ // Add a profile before providing the interface.
+ CollectEmptyProfile(CallStackProfileParams(
+ CallStackProfileParams::BROWSER_PROCESS,
+ CallStackProfileParams::MAIN_THREAD, CallStackProfileParams::JANKY_TASK,
+ CallStackProfileParams::PRESERVE_ORDER));
ASSERT_EQ(1u, profiles().size());
EXPECT_EQ(CallStackProfileParams::BROWSER_PROCESS,
profiles()[0].params.process);
@@ -99,7 +86,6 @@ TEST_F(ChildCallStackProfileCollectorTest, InterfaceProvided) {
base::TimeTicks start_timestamp = profiles()[0].start_timestamp;
EXPECT_GE(base::TimeDelta::FromMilliseconds(10),
base::TimeTicks::Now() - start_timestamp);
- EXPECT_EQ(2u, profiles()[0].profiles.size());
// Set the interface. The profiles should be passed to it.
child_collector_.SetParentProfileCollector(std::move(receiver_));
@@ -111,17 +97,13 @@ TEST_F(ChildCallStackProfileCollectorTest, InterfaceProvided) {
EXPECT_EQ(CallStackProfileParams::PRESERVE_ORDER,
receiver_impl_->profiles[0].params.ordering_spec);
EXPECT_EQ(start_timestamp, receiver_impl_->profiles[0].start_timestamp);
- EXPECT_EQ(2u, receiver_impl_->profiles[0].profiles.size());
- // Add profiles after providing the interface. They should also be passed to
- // it.
+ // Add a profile after providing the interface. It should also be passed.
receiver_impl_->profiles.clear();
- CollectEmptyProfiles(
- CallStackProfileParams(CallStackProfileParams::GPU_PROCESS,
- CallStackProfileParams::MAIN_THREAD,
- CallStackProfileParams::THREAD_HUNG,
- CallStackProfileParams::PRESERVE_ORDER),
- 1);
+ CollectEmptyProfile(CallStackProfileParams(
+ CallStackProfileParams::GPU_PROCESS, CallStackProfileParams::MAIN_THREAD,
+ CallStackProfileParams::THREAD_HUNG,
+ CallStackProfileParams::PRESERVE_ORDER));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, profiles().size());
ASSERT_EQ(1u, receiver_impl_->profiles.size());
@@ -136,19 +118,16 @@ TEST_F(ChildCallStackProfileCollectorTest, InterfaceProvided) {
EXPECT_GE(base::TimeDelta::FromMilliseconds(10),
(base::TimeTicks::Now() -
receiver_impl_->profiles[0].start_timestamp));
- EXPECT_EQ(1u, receiver_impl_->profiles[0].profiles.size());
}
TEST_F(ChildCallStackProfileCollectorTest, InterfaceNotProvided) {
EXPECT_EQ(0u, profiles().size());
- // Add profiles before providing a null interface.
- CollectEmptyProfiles(
- CallStackProfileParams(CallStackProfileParams::BROWSER_PROCESS,
- CallStackProfileParams::MAIN_THREAD,
- CallStackProfileParams::JANKY_TASK,
- CallStackProfileParams::PRESERVE_ORDER),
- 2);
+ // Add a profile before providing a null interface.
+ CollectEmptyProfile(CallStackProfileParams(
+ CallStackProfileParams::BROWSER_PROCESS,
+ CallStackProfileParams::MAIN_THREAD, CallStackProfileParams::JANKY_TASK,
+ CallStackProfileParams::PRESERVE_ORDER));
ASSERT_EQ(1u, profiles().size());
EXPECT_EQ(CallStackProfileParams::BROWSER_PROCESS,
profiles()[0].params.process);
@@ -158,21 +137,19 @@ TEST_F(ChildCallStackProfileCollectorTest, InterfaceNotProvided) {
profiles()[0].params.ordering_spec);
EXPECT_GE(base::TimeDelta::FromMilliseconds(10),
base::TimeTicks::Now() - profiles()[0].start_timestamp);
- EXPECT_EQ(2u, profiles()[0].profiles.size());
- // Set the null interface. The profiles should be flushed.
+ // Set the null interface. The profile should be flushed.
child_collector_.SetParentProfileCollector(
mojom::CallStackProfileCollectorPtr());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, profiles().size());
- // Add profiles after providing a null interface. They should also be flushed.
- CollectEmptyProfiles(
- CallStackProfileParams(CallStackProfileParams::GPU_PROCESS,
- CallStackProfileParams::MAIN_THREAD,
- CallStackProfileParams::THREAD_HUNG,
- CallStackProfileParams::PRESERVE_ORDER),
- 1);
+ // Add a profile after providing a null interface. They should also be
+ // flushed.
+ CollectEmptyProfile(CallStackProfileParams(
+ CallStackProfileParams::GPU_PROCESS, CallStackProfileParams::MAIN_THREAD,
+ CallStackProfileParams::THREAD_HUNG,
+ CallStackProfileParams::PRESERVE_ORDER));
EXPECT_EQ(0u, profiles().size());
}
diff --git a/chromium/components/metrics/generate_expired_histograms_array.gni b/chromium/components/metrics/generate_expired_histograms_array.gni
index b211c6676a8..b4522465325 100644
--- a/chromium/components/metrics/generate_expired_histograms_array.gni
+++ b/chromium/components/metrics/generate_expired_histograms_array.gni
@@ -46,7 +46,7 @@ template("generate_expired_histograms_array") {
"-o" + rebase_path(root_gen_dir, root_build_dir),
"-H" + rebase_path(header_filename, root_gen_dir),
"-d" + rebase_path(major_branch_date_filepath, root_build_dir),
- "-m" + rebase_path(milestone_filepath),
+ "-m" + rebase_path(milestone_filepath, root_build_dir),
] + rebase_path(inputs, root_build_dir)
}
}
diff --git a/chromium/components/metrics/gpu/gpu_metrics_provider.cc b/chromium/components/metrics/gpu/gpu_metrics_provider.cc
index 7eacbe710ee..53c15fb7c87 100644
--- a/chromium/components/metrics/gpu/gpu_metrics_provider.cc
+++ b/chromium/components/metrics/gpu/gpu_metrics_provider.cc
@@ -23,12 +23,13 @@ void GPUMetricsProvider::ProvideSystemProfileMetrics(
const gpu::GPUInfo& gpu_info =
content::GpuDataManager::GetInstance()->GetGPUInfo();
+ const gpu::GPUInfo::GPUDevice& active_gpu = gpu_info.active_gpu();
SystemProfileProto::Hardware::Graphics* gpu =
hardware->mutable_gpu();
- gpu->set_vendor_id(gpu_info.gpu.vendor_id);
- gpu->set_device_id(gpu_info.gpu.device_id);
- gpu->set_driver_version(gpu_info.driver_version);
- gpu->set_driver_date(gpu_info.driver_date);
+ gpu->set_vendor_id(active_gpu.vendor_id);
+ gpu->set_device_id(active_gpu.device_id);
+ gpu->set_driver_version(active_gpu.driver_version);
+ gpu->set_driver_date(active_gpu.driver_date);
gpu->set_gl_vendor(gpu_info.gl_vendor);
gpu->set_gl_renderer(gpu_info.gl_renderer);
}
diff --git a/chromium/components/metrics/metrics_log.cc b/chromium/components/metrics/metrics_log.cc
index a99d3c7489c..e7504d351c8 100644
--- a/chromium/components/metrics/metrics_log.cc
+++ b/chromium/components/metrics/metrics_log.cc
@@ -19,6 +19,7 @@
#include "base/metrics/histogram_snapshot_manager.h"
#include "base/metrics/metrics_hashes.h"
#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
#include "base/sys_info.h"
#include "base/time/time.h"
#include "build/build_config.h"
@@ -170,7 +171,9 @@ void MetricsLog::RecordCoreSystemProfile(MetricsServiceClient* client,
metrics::SystemProfileProto::OS* os = system_profile->mutable_os();
os->set_name(base::SysInfo::OperatingSystemName());
os->set_version(base::SysInfo::OperatingSystemVersion());
-#if defined(OS_ANDROID)
+#if defined(OS_CHROMEOS)
+ os->set_kernel_version(base::SysInfo::KernelVersion());
+#elif defined(OS_ANDROID)
os->set_build_fingerprint(
base::android::BuildInfo::GetInstance()->android_build_fp());
std::string package_name = client->GetAppPackageName();
diff --git a/chromium/components/metrics/metrics_log_unittest.cc b/chromium/components/metrics/metrics_log_unittest.cc
index c4250180a6d..8af49a8cbbc 100644
--- a/chromium/components/metrics/metrics_log_unittest.cc
+++ b/chromium/components/metrics/metrics_log_unittest.cc
@@ -15,6 +15,7 @@
#include "base/metrics/bucket_ranges.h"
#include "base/metrics/sample_vector.h"
#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
#include "base/sys_info.h"
#include "base/time/time.h"
#include "components/metrics/delegating_provider.h"
@@ -153,7 +154,10 @@ TEST_F(MetricsLogTest, BasicRecord) {
system_profile->mutable_os()->set_name(base::SysInfo::OperatingSystemName());
system_profile->mutable_os()->set_version(
base::SysInfo::OperatingSystemVersion());
-#if defined(OS_ANDROID)
+#if defined(OS_CHROMEOS)
+ system_profile->mutable_os()->set_kernel_version(
+ base::SysInfo::KernelVersion());
+#elif defined(OS_ANDROID)
system_profile->mutable_os()->set_build_fingerprint(
base::android::BuildInfo::GetInstance()->android_build_fp());
system_profile->set_app_package_name("test app");
diff --git a/chromium/components/metrics/metrics_state_manager_unittest.cc b/chromium/components/metrics/metrics_state_manager_unittest.cc
index 3e27e1a2696..0e1f1481c64 100644
--- a/chromium/components/metrics/metrics_state_manager_unittest.cc
+++ b/chromium/components/metrics/metrics_state_manager_unittest.cc
@@ -14,7 +14,7 @@
#include "base/command_line.h"
#include "base/macros.h"
#include "base/strings/string16.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/metrics/client_info.h"
#include "components/metrics/metrics_log.h"
#include "components/metrics/metrics_pref_names.h"
diff --git a/chromium/components/metrics/net/net_metrics_log_uploader.cc b/chromium/components/metrics/net/net_metrics_log_uploader.cc
index cbe1f0a0582..addad6b9f1a 100644
--- a/chromium/components/metrics/net/net_metrics_log_uploader.cc
+++ b/chromium/components/metrics/net/net_metrics_log_uploader.cc
@@ -12,6 +12,7 @@
#include "components/encrypted_messages/message_encrypter.h"
#include "components/metrics/metrics_log_uploader.h"
#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 "third_party/metrics_proto/reporting_info.pb.h"
@@ -195,8 +196,10 @@ void NetMetricsLogUploader::UploadLogToURL(
service);
current_fetch_->SetRequestContext(request_context_getter_);
std::string reporting_info_string = SerializeReportingInfo(reporting_info);
- // If we are not using HTTPS for this upload, encrypt it.
- if (!url.SchemeIs(url::kHttpsScheme)) {
+ // If we are not using HTTPS for this upload, encrypt it. We do not encrypt
+ // requests to localhost to allow testing with a local collector that doesn't
+ // have decryption enabled.
+ if (!url.SchemeIs(url::kHttpsScheme) && !net::IsLocalhost(url)) {
std::string encrypted_message;
if (!EncryptString(compressed_log_data, &encrypted_message)) {
current_fetch_.reset();
diff --git a/chromium/components/metrics/net/net_metrics_log_uploader_unittest.cc b/chromium/components/metrics/net/net_metrics_log_uploader_unittest.cc
index 7f7747e43e5..76652ec3968 100644
--- a/chromium/components/metrics/net/net_metrics_log_uploader_unittest.cc
+++ b/chromium/components/metrics/net/net_metrics_log_uploader_unittest.cc
@@ -32,11 +32,10 @@ class NetMetricsLogUploaderTest : public testing::Test {
reporting_info);
}
- void CreateUploaderAndUploadToSecureURL() {
+ void CreateUploaderAndUploadToSecureURL(const std::string& url) {
ReportingInfo dummy_reporting_info;
uploader_.reset(new NetMetricsLogUploader(
- nullptr, "https://dummy_secure_server", "dummy_mime",
- MetricsLogUploader::UMA,
+ nullptr, url, "dummy_mime", MetricsLogUploader::UMA,
base::Bind(&NetMetricsLogUploaderTest::DummyOnUploadComplete,
base::Unretained(this))));
uploader_->UploadLog("dummy_data", "dummy_hash", dummy_reporting_info);
@@ -128,7 +127,16 @@ TEST_F(NetMetricsLogUploaderTest, MessageOverHTTPIsEncrypted) {
// message.
TEST_F(NetMetricsLogUploaderTest, MessageOverHTTPSIsNotEncrypted) {
net::TestURLFetcherFactory factory;
- CreateUploaderAndUploadToSecureURL();
+ CreateUploaderAndUploadToSecureURL("https://dummy_secure_server");
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ EXPECT_EQ(fetcher->upload_data(), "dummy_data");
+}
+
+// Test that attempting to upload to localhost over http results in an
+// unencrypted message.
+TEST_F(NetMetricsLogUploaderTest, MessageOverHTTPLocalhostIsNotEncrypted) {
+ net::TestURLFetcherFactory factory;
+ CreateUploaderAndUploadToSecureURL("http://localhost");
net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
EXPECT_EQ(fetcher->upload_data(), "dummy_data");
}
diff --git a/chromium/components/metrics/net/network_metrics_provider.cc b/chromium/components/metrics/net/network_metrics_provider.cc
index 412f0afbb40..a0cdd391a03 100644
--- a/chromium/components/metrics/net/network_metrics_provider.cc
+++ b/chromium/components/metrics/net/network_metrics_provider.cc
@@ -208,6 +208,8 @@ void NetworkMetricsProvider::ProvideSystemProfileMetrics(
// window, since OnConnectionTypeChanged() ignores transitions to the "none"
// state.
connection_type_ = net::NetworkChangeNotifier::GetConnectionType();
+ if (connection_type_ != net::NetworkChangeNotifier::CONNECTION_UNKNOWN)
+ network_change_notifier_initialized_ = true;
// Reset the "ambiguous" flags, since a new metrics log session has started.
connection_type_is_ambiguous_ = false;
wifi_phy_layer_protocol_is_ambiguous_ = false;
@@ -415,7 +417,7 @@ void NetworkMetricsProvider::WriteWifiAccessPointProto(
void NetworkMetricsProvider::LogAggregatedMetrics() {
DCHECK(thread_checker_.CalledOnValidThread());
base::HistogramBase* error_codes = base::SparseHistogram::FactoryGet(
- "Net.ErrorCodesForMainFrame3",
+ "Net.ErrorCodesForMainFrame4",
base::HistogramBase::kUmaTargetedHistogramFlag);
std::unique_ptr<base::HistogramSamples> samples =
error_codes->SnapshotSamples();
diff --git a/chromium/components/metrics/net/network_metrics_provider.h b/chromium/components/metrics/net/network_metrics_provider.h
index c1a0312efb6..f0f6d0e0334 100644
--- a/chromium/components/metrics/net/network_metrics_provider.h
+++ b/chromium/components/metrics/net/network_metrics_provider.h
@@ -125,7 +125,7 @@ class NetworkMetricsProvider
// Helper object for retrieving connected wifi access point information.
std::unique_ptr<WifiAccessPointInfoProvider> wifi_access_point_info_provider_;
- // These metrics track histogram totals for the Net.ErrorCodesForMainFrame3
+ // These metrics track histogram totals for the Net.ErrorCodesForMainFrame4
// histogram. They are used to compute deltas at upload time.
base::HistogramBase::Count total_aborts_;
base::HistogramBase::Count total_codes_;
diff --git a/chromium/components/metrics/public/cpp/OWNERS b/chromium/components/metrics/public/cpp/OWNERS
index 154435234ea..2c44a463856 100644
--- a/chromium/components/metrics/public/cpp/OWNERS
+++ b/chromium/components/metrics/public/cpp/OWNERS
@@ -2,3 +2,5 @@ per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
per-file *_struct_traits*.*=set noparent
per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/metrics/public/cpp/call_stack_profile.typemap b/chromium/components/metrics/public/cpp/call_stack_profile.typemap
index 611148c8255..aa8456507d3 100644
--- a/chromium/components/metrics/public/cpp/call_stack_profile.typemap
+++ b/chromium/components/metrics/public/cpp/call_stack_profile.typemap
@@ -12,7 +12,7 @@ traits_headers =
[ "//components/metrics/public/cpp/call_stack_profile_struct_traits.h" ]
deps = [
"//base",
- "//components/metrics:call_stack_profile_params",
+ "//components/metrics:call_stack_profile",
]
type_mappings = [
"metrics.mojom.CallStackModule=base::StackSamplingProfiler::Module",
diff --git a/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits.h b/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits.h
index 98aae66fd70..b062a5a0497 100644
--- a/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits.h
+++ b/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits.h
@@ -56,17 +56,16 @@ struct StructTraits<metrics::mojom::CallStackFrameDataView,
}
static uint64_t module_index(
const base::StackSamplingProfiler::Frame& frame) {
- return frame.module_index ==
- base::StackSamplingProfiler::Frame::kUnknownModuleIndex ?
- static_cast<uint64_t>(-1) :
- frame.module_index;
+ return frame.module_index == base::kUnknownModuleIndex
+ ? static_cast<uint64_t>(-1)
+ : frame.module_index;
}
static bool Read(metrics::mojom::CallStackFrameDataView data,
base::StackSamplingProfiler::Frame* out) {
- size_t module_index = data.module_index() == static_cast<uint64_t>(-1) ?
- base::StackSamplingProfiler::Frame::kUnknownModuleIndex :
- data.module_index();
+ size_t module_index = data.module_index() == static_cast<uint64_t>(-1)
+ ? base::kUnknownModuleIndex
+ : data.module_index();
// We can't know whether the module_index field is valid at this point since
// we don't have access to the number of modules here. This will be checked
@@ -128,8 +127,7 @@ struct StructTraits<metrics::mojom::CallStackProfileDataView,
for (const base::StackSamplingProfiler::Sample& sample : samples) {
for (const base::StackSamplingProfiler::Frame& frame : sample.frames) {
if (frame.module_index >= module_count &&
- frame.module_index !=
- base::StackSamplingProfiler::Frame::kUnknownModuleIndex)
+ frame.module_index != base::kUnknownModuleIndex)
return false;
}
}
diff --git a/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc b/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc
index 7bdb4dbb085..b0d2bf0288b 100644
--- a/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc
+++ b/chromium/components/metrics/public/cpp/call_stack_profile_struct_traits_unittest.cc
@@ -151,20 +151,20 @@ TEST_F(CallStackProfileStructTraitsTest, Frame) {
using Frame = base::StackSamplingProfiler::Frame;
const Frame serialize_cases[] = {
- // Null instruction pointer.
- Frame(0x0, 10),
- // Non-null instruction pointer.
- Frame(0x10, 10),
- // Instruction pointer with a bit set beyond 32 bits, when built for x64.
- Frame(1ULL << (sizeof(uintptr_t) * 8) * 3 / 4, 10),
- // Zero module index.
- Frame(0xabcd, 0),
- // Non-zero module index.
- Frame(0xabcd, 1),
- // Non-zero module index.
- Frame(0xabcd, 10),
- // Unknown module index.
- Frame(0xabcd, Frame::kUnknownModuleIndex),
+ // Null instruction pointer.
+ Frame(0x0, 10),
+ // Non-null instruction pointer.
+ Frame(0x10, 10),
+ // Instruction pointer with a bit set beyond 32 bits, when built for x64.
+ Frame(1ULL << (sizeof(uintptr_t) * 8) * 3 / 4, 10),
+ // Zero module index.
+ Frame(0xabcd, 0),
+ // Non-zero module index.
+ Frame(0xabcd, 1),
+ // Non-zero module index.
+ Frame(0xabcd, 10),
+ // Unknown module index.
+ Frame(0xabcd, base::kUnknownModuleIndex),
};
for (const Frame& input : serialize_cases) {
@@ -179,10 +179,11 @@ TEST_F(CallStackProfileStructTraitsTest, Frame) {
// Checks serialization/deserialization of Profile fields, including validation
// of the Frame module_index field.
TEST_F(CallStackProfileStructTraitsTest, Profile) {
- using Module = base::StackSamplingProfiler::Module;
- using Frame = base::StackSamplingProfiler::Frame;
- using Sample = base::StackSamplingProfiler::Sample;
- using Profile = base::StackSamplingProfiler::CallStackProfile;
+ using base::StackSamplingProfiler;
+ using Module = StackSamplingProfiler::Module;
+ using Frame = StackSamplingProfiler::Frame;
+ using Sample = StackSamplingProfiler::Sample;
+ using Profile = StackSamplingProfiler::CallStackProfile;
struct SerializeCase {
Profile profile;
@@ -190,60 +191,49 @@ TEST_F(CallStackProfileStructTraitsTest, Profile) {
};
const SerializeCase serialize_cases[] = {
- // Empty modules and samples.
- {
- CreateProfile(std::vector<Module>(), std::vector<Sample>(),
- base::TimeDelta::FromSeconds(1),
- base::TimeDelta::FromSeconds(2)),
- true
- },
- // Non-empty modules and empty samples.
- {
- CreateProfile({ Module(0x4000, "a", base::FilePath()) },
- std::vector<Sample>(),
- base::TimeDelta::FromSeconds(1),
- base::TimeDelta::FromSeconds(2)),
- true
- },
- // Valid values for modules and samples.
- {
- CreateProfile({
- Module(0x4000, "a", base::FilePath()),
- Module(0x4100, "b", base::FilePath()),
- },
- {
- Sample({
- Frame(0x4010, 0),
- Frame(0x4110, 1),
- Frame(0x4110, Frame::kUnknownModuleIndex),
- }),
- },
- base::TimeDelta::FromSeconds(1),
- base::TimeDelta::FromSeconds(2)),
- true
- },
- // Valid values for modules, but an out of range module index in the second
- // sample.
- {
- CreateProfile({
- Module(0x4000, "a", base::FilePath()),
- Module(0x4100, "b", base::FilePath()),
- },
- {
- Sample({
- Frame(0x4010, 0),
- Frame(0x4110, 1),
- Frame(0x4110, Frame::kUnknownModuleIndex),
- }),
- Sample({
- Frame(0x4010, 0),
- Frame(0x4110, 2),
- }),
- },
- base::TimeDelta::FromSeconds(1),
- base::TimeDelta::FromSeconds(2)),
- false
- },
+ // Empty modules and samples.
+ {CreateProfile(std::vector<Module>(), std::vector<Sample>(),
+ base::TimeDelta::FromSeconds(1),
+ base::TimeDelta::FromSeconds(2)),
+ true},
+ // Non-empty modules and empty samples.
+ {CreateProfile({Module(0x4000, "a", base::FilePath())},
+ std::vector<Sample>(), base::TimeDelta::FromSeconds(1),
+ base::TimeDelta::FromSeconds(2)),
+ true},
+ // Valid values for modules and samples.
+ {CreateProfile(
+ {
+ Module(0x4000, "a", base::FilePath()),
+ Module(0x4100, "b", base::FilePath()),
+ },
+ {
+ Sample({
+ Frame(0x4010, 0), Frame(0x4110, 1),
+ Frame(0x4110, base::kUnknownModuleIndex),
+ }),
+ },
+ base::TimeDelta::FromSeconds(1), base::TimeDelta::FromSeconds(2)),
+ true},
+ // Valid values for modules, but an out of range module index in the
+ // second
+ // sample.
+ {CreateProfile(
+ {
+ Module(0x4000, "a", base::FilePath()),
+ Module(0x4100, "b", base::FilePath()),
+ },
+ {
+ Sample({
+ Frame(0x4010, 0), Frame(0x4110, 1),
+ Frame(0x4110, base::kUnknownModuleIndex),
+ }),
+ Sample({
+ Frame(0x4010, 0), Frame(0x4110, 2),
+ }),
+ },
+ base::TimeDelta::FromSeconds(1), base::TimeDelta::FromSeconds(2)),
+ false},
};
for (const SerializeCase& input : serialize_cases) {
diff --git a/chromium/components/metrics/public/interfaces/call_stack_profile_collector.mojom b/chromium/components/metrics/public/interfaces/call_stack_profile_collector.mojom
index d265d606e89..8f0031db654 100644
--- a/chromium/components/metrics/public/interfaces/call_stack_profile_collector.mojom
+++ b/chromium/components/metrics/public/interfaces/call_stack_profile_collector.mojom
@@ -74,5 +74,5 @@ struct CallStackProfileParams {
interface CallStackProfileCollector {
Collect(CallStackProfileParams params,
mojo_base.mojom.TimeTicks start_timestamp,
- array<CallStackProfile> profiles);
+ CallStackProfile profile);
};
diff --git a/chromium/components/metrics/single_sample_metrics_factory_impl_unittest.cc b/chromium/components/metrics/single_sample_metrics_factory_impl_unittest.cc
index f9fbbba6c44..cec7c869c12 100644
--- a/chromium/components/metrics/single_sample_metrics_factory_impl_unittest.cc
+++ b/chromium/components/metrics/single_sample_metrics_factory_impl_unittest.cc
@@ -8,7 +8,7 @@
#include "base/metrics/dummy_histogram.h"
#include "base/run_loop.h"
#include "base/test/gtest_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/threading/thread.h"
#include "components/metrics/single_sample_metrics.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/components/metrics/stability_metrics_helper.cc b/chromium/components/metrics/stability_metrics_helper.cc
index 375f75c4368..517fc71fc86 100644
--- a/chromium/components/metrics/stability_metrics_helper.cc
+++ b/chromium/components/metrics/stability_metrics_helper.cc
@@ -17,6 +17,7 @@
#include "components/metrics/metrics_pref_names.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
+#include "components/variations/hashing.h"
#include "extensions/buildflags/buildflags.h"
#include "third_party/metrics_proto/system_profile.pb.h"
@@ -188,6 +189,23 @@ void StabilityMetricsHelper::IncreaseRendererCrashCount() {
IncrementPrefValue(prefs::kStabilityRendererCrashCount);
}
+void StabilityMetricsHelper::BrowserUtilityProcessLaunched(
+ const std::string& metrics_name) {
+ uint32_t hash = variations::HashName(metrics_name);
+ base::UmaHistogramSparse("ChildProcess.Launched.UtilityProcessHash", hash);
+}
+
+void StabilityMetricsHelper::BrowserUtilityProcessCrashed(
+ const std::string& metrics_name,
+ int exit_code) {
+ // TODO(wfh): there doesn't appear to be a good way to log these exit_codes
+ // without adding something into the stability proto, so for now only log the
+ // crash and if the numbers are high enough, logging exit codes can be added
+ // later.
+ uint32_t hash = variations::HashName(metrics_name);
+ base::UmaHistogramSparse("ChildProcess.Crashed.UtilityProcessHash", hash);
+}
+
void StabilityMetricsHelper::BrowserChildProcessCrashed() {
IncrementPrefValue(prefs::kStabilityChildProcessCrashCount);
}
diff --git a/chromium/components/metrics/stability_metrics_helper.h b/chromium/components/metrics/stability_metrics_helper.h
index ddf4d50c597..5a22882af4b 100644
--- a/chromium/components/metrics/stability_metrics_helper.h
+++ b/chromium/components/metrics/stability_metrics_helper.h
@@ -30,6 +30,13 @@ class StabilityMetricsHelper {
// Clears the gathered stability metrics.
void ClearSavedStabilityMetrics();
+ // Records a utility process launch with name |metrics_name|.
+ void BrowserUtilityProcessLaunched(const std::string& metrics_name);
+
+ // Records a utility process crash with name |metrics_name|.
+ void BrowserUtilityProcessCrashed(const std::string& metrics_name,
+ int exit_code);
+
// Records a browser child process crash.
void BrowserChildProcessCrashed();
diff --git a/chromium/components/metrics/stability_metrics_helper_unittest.cc b/chromium/components/metrics/stability_metrics_helper_unittest.cc
index bcaa2c1e017..66fc041db09 100644
--- a/chromium/components/metrics/stability_metrics_helper_unittest.cc
+++ b/chromium/components/metrics/stability_metrics_helper_unittest.cc
@@ -7,7 +7,7 @@
#include <memory>
#include "base/macros.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "build/build_config.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
diff --git a/chromium/components/metrics/stability_metrics_provider_unittest.cc b/chromium/components/metrics/stability_metrics_provider_unittest.cc
index 3a188ec3f83..e0ba0ce9ba7 100644
--- a/chromium/components/metrics/stability_metrics_provider_unittest.cc
+++ b/chromium/components/metrics/stability_metrics_provider_unittest.cc
@@ -4,7 +4,7 @@
#include "components/metrics/stability_metrics_provider.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "build/build_config.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/components/minidump_uploader/OWNERS b/chromium/components/minidump_uploader/OWNERS
index 6b952ef4a27..1c0302aaf81 100644
--- a/chromium/components/minidump_uploader/OWNERS
+++ b/chromium/components/minidump_uploader/OWNERS
@@ -1,7 +1,3 @@
-# Java readability
-mariakhomenko@chromium.org
-
-# Crash uploading mechanism
gsennton@chromium.org
isherman@chromium.org
diff --git a/chromium/components/mirroring/service/BUILD.gn b/chromium/components/mirroring/service/BUILD.gn
index a5b3ef6d2f4..1f9d28bf573 100644
--- a/chromium/components/mirroring/service/BUILD.gn
+++ b/chromium/components/mirroring/service/BUILD.gn
@@ -18,6 +18,7 @@ source_set("interface") {
"//media/capture/mojom:video_capture",
"//media/cast:common",
"//media/mojo/interfaces",
+ "//media/mojo/interfaces:remoting",
"//net",
"//services/network/public/mojom",
]
@@ -25,12 +26,18 @@ source_set("interface") {
source_set("service") {
sources = [
+ "captured_audio_input.cc",
+ "captured_audio_input.h",
+ "media_remoter.cc",
+ "media_remoter.h",
"message_dispatcher.cc",
"message_dispatcher.h",
"mirror_settings.cc",
"mirror_settings.h",
"receiver_response.cc",
"receiver_response.h",
+ "remoting_sender.cc",
+ "remoting_sender.h",
"rtp_stream.cc",
"rtp_stream.h",
"session.cc",
@@ -62,6 +69,8 @@ source_set("service") {
"//media/cast:net",
"//media/cast:sender",
"//media/mojo/common:common",
+ "//media/mojo/interfaces",
+ "//media/mojo/interfaces:remoting",
"//mojo/public/cpp/bindings",
"//mojo/public/cpp/system",
"//net",
@@ -74,12 +83,15 @@ source_set("service") {
source_set("unittests") {
testonly = true
sources = [
+ "captured_audio_input_unittest.cc",
"fake_network_service.cc",
"fake_network_service.h",
"fake_video_capture_host.cc",
"fake_video_capture_host.h",
+ "media_remoter_unittest.cc",
"message_dispatcher_unittest.cc",
"receiver_response_unittest.cc",
+ "remoting_sender_unittest.cc",
"rtp_stream_unittest.cc",
"session_monitor_unittest.cc",
"session_unittest.cc",
@@ -100,6 +112,8 @@ source_set("unittests") {
"//media/cast:sender",
"//media/cast:test_support",
"//media/cast:test_support",
+ "//media/mojo/interfaces",
+ "//media/mojo/interfaces:remoting",
"//mojo/public/cpp/bindings",
"//net",
"//services/network:test_support",
diff --git a/chromium/components/mirroring/service/captured_audio_input.cc b/chromium/components/mirroring/service/captured_audio_input.cc
new file mode 100644
index 00000000000..1d9719c120f
--- /dev/null
+++ b/chromium/components/mirroring/service/captured_audio_input.cc
@@ -0,0 +1,96 @@
+// 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/mirroring/service/captured_audio_input.h"
+
+#include "base/logging.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+
+namespace mirroring {
+
+CapturedAudioInput::CapturedAudioInput(StreamCreatorCallback callback)
+ : stream_creator_callback_(std::move(callback)),
+ stream_client_binding_(this) {
+ DETACH_FROM_SEQUENCE(sequence_checker_);
+ DCHECK(!stream_creator_callback_.is_null());
+}
+
+CapturedAudioInput::~CapturedAudioInput() {}
+
+void CapturedAudioInput::CreateStream(media::AudioInputIPCDelegate* delegate,
+ const media::AudioParameters& params,
+ bool automatic_gain_control,
+ uint32_t total_segments) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!automatic_gain_control); // Invalid to be true for screen capture.
+ DCHECK(delegate);
+ DCHECK(!delegate_);
+ delegate_ = delegate;
+ stream_creator_callback_.Run(this, params, total_segments);
+}
+
+void CapturedAudioInput::RecordStream() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(stream_.is_bound());
+ stream_->Record();
+}
+
+void CapturedAudioInput::SetVolume(double volume) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(stream_.is_bound());
+ stream_->SetVolume(volume);
+}
+
+void CapturedAudioInput::CloseStream() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ delegate_ = nullptr;
+ if (stream_client_binding_.is_bound())
+ stream_client_binding_.Unbind();
+ stream_.reset();
+}
+
+void CapturedAudioInput::SetOutputDeviceForAec(
+ const std::string& output_device_id) {
+ NOTREACHED();
+}
+
+void CapturedAudioInput::StreamCreated(
+ media::mojom::AudioInputStreamPtr stream,
+ media::mojom::AudioInputStreamClientRequest client_request,
+ media::mojom::ReadOnlyAudioDataPipePtr data_pipe,
+ bool initially_muted) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(delegate_);
+ DCHECK(!stream_);
+ DCHECK(!stream_client_binding_.is_bound());
+
+ stream_ = std::move(stream);
+ stream_client_binding_.Bind(std::move(client_request));
+
+ base::PlatformFile socket_handle;
+ auto result =
+ mojo::UnwrapPlatformFile(std::move(data_pipe->socket), &socket_handle);
+ DCHECK_EQ(result, MOJO_RESULT_OK);
+
+ base::ReadOnlySharedMemoryRegion& shared_memory_region =
+ data_pipe->shared_memory;
+ DCHECK(shared_memory_region.IsValid());
+
+ delegate_->OnStreamCreated(std::move(shared_memory_region), socket_handle,
+ initially_muted);
+}
+
+void CapturedAudioInput::OnError() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(delegate_);
+ delegate_->OnError();
+}
+
+void CapturedAudioInput::OnMutedStateChanged(bool is_muted) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(delegate_);
+ delegate_->OnMuted(is_muted);
+}
+
+} // namespace mirroring
diff --git a/chromium/components/mirroring/service/captured_audio_input.h b/chromium/components/mirroring/service/captured_audio_input.h
new file mode 100644
index 00000000000..ca92e0acabe
--- /dev/null
+++ b/chromium/components/mirroring/service/captured_audio_input.h
@@ -0,0 +1,64 @@
+// 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_MIRRORING_SERVICE_CAPTURED_AUDIO_INPUT_H_
+#define COMPONENTS_MIRRORING_SERVICE_CAPTURED_AUDIO_INPUT_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+#include "components/mirroring/service/interface.h"
+#include "media/audio/audio_input_ipc.h"
+#include "media/mojo/interfaces/audio_input_stream.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace mirroring {
+
+// CapturedAudioInput handles the creation, initialization and control of an
+// audio input stream created by Audio Service.
+class CapturedAudioInput final : public media::AudioInputIPC,
+ public AudioStreamCreatorClient,
+ public media::mojom::AudioInputStreamClient {
+ public:
+ using StreamCreatorCallback =
+ base::RepeatingCallback<void(AudioStreamCreatorClient* client,
+ const media::AudioParameters& params,
+ uint32_t total_segments)>;
+ explicit CapturedAudioInput(StreamCreatorCallback callback);
+ ~CapturedAudioInput() override;
+
+ private:
+ // media::AudioInputIPC implementation.
+ void CreateStream(media::AudioInputIPCDelegate* delegate,
+ const media::AudioParameters& params,
+ bool automatic_gain_control,
+ uint32_t total_segments) override;
+ void RecordStream() override;
+ void SetVolume(double volume) override;
+ void CloseStream() override;
+ void SetOutputDeviceForAec(const std::string& output_device_id) override;
+
+ // AudioStreamCreatorClient implementation
+ void StreamCreated(media::mojom::AudioInputStreamPtr stream,
+ media::mojom::AudioInputStreamClientRequest client_request,
+ media::mojom::ReadOnlyAudioDataPipePtr data_pipe,
+ bool initially_muted) override;
+
+ // media::mojom::AudioInputStreamClient implementation.
+ void OnError() override;
+ void OnMutedStateChanged(bool is_muted) override;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ const StreamCreatorCallback stream_creator_callback_;
+ mojo::Binding<media::mojom::AudioInputStreamClient> stream_client_binding_;
+ media::AudioInputIPCDelegate* delegate_ = nullptr;
+ media::mojom::AudioInputStreamPtr stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(CapturedAudioInput);
+};
+
+} // namespace mirroring
+
+#endif // COMPONENTS_MIRRORING_SERVICE_CAPTURED_AUDIO_INPUT_H_
diff --git a/chromium/components/mirroring/service/captured_audio_input_unittest.cc b/chromium/components/mirroring/service/captured_audio_input_unittest.cc
new file mode 100644
index 00000000000..da8948b9513
--- /dev/null
+++ b/chromium/components/mirroring/service/captured_audio_input_unittest.cc
@@ -0,0 +1,177 @@
+// 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/mirroring/service/captured_audio_input.h"
+
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "media/base/audio_parameters.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/system/buffer.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::InvokeWithoutArgs;
+
+namespace mirroring {
+
+namespace {
+
+class MockStream final : public media::mojom::AudioInputStream {
+ public:
+ MOCK_METHOD0(Record, void());
+ MOCK_METHOD1(SetVolume, void(double));
+};
+
+class MockDelegate final : public media::AudioInputIPCDelegate {
+ public:
+ MockDelegate() {}
+ ~MockDelegate() override {}
+
+ MOCK_METHOD1(StreamCreated, void(bool initially_muted));
+ MOCK_METHOD0(OnError, void());
+ MOCK_METHOD1(OnMuted, void(bool muted));
+ MOCK_METHOD0(OnIPCClosed, void());
+
+ void OnStreamCreated(base::ReadOnlySharedMemoryRegion shared_memory_region,
+ base::SyncSocket::Handle socket_handle,
+ bool initially_muted) override {
+ StreamCreated(initially_muted);
+ }
+};
+
+} // namespace
+
+class CapturedAudioInputTest : public ::testing::Test {
+ public:
+ CapturedAudioInputTest() {}
+
+ ~CapturedAudioInputTest() override {
+ scoped_task_environment_.RunUntilIdle();
+ }
+
+ void CreateMockStream(bool initially_muted,
+ AudioStreamCreatorClient* client,
+ const media::AudioParameters& params,
+ uint32_t total_segments) {
+ EXPECT_EQ(base::SyncSocket::kInvalidHandle, socket_.handle());
+ EXPECT_FALSE(stream_);
+ media::mojom::AudioInputStreamPtr stream_ptr;
+ auto input_stream = std::make_unique<MockStream>();
+ stream_ = input_stream.get();
+ mojo::MakeStrongBinding(std::move(input_stream),
+ mojo::MakeRequest(&stream_ptr));
+ base::CancelableSyncSocket foreign_socket;
+ EXPECT_TRUE(
+ base::CancelableSyncSocket::CreatePair(&socket_, &foreign_socket));
+ client->StreamCreated(
+ std::move(stream_ptr), mojo::MakeRequest(&stream_client_),
+ {base::in_place, base::ReadOnlySharedMemoryRegion::Create(1024).region,
+ mojo::WrapPlatformFile(foreign_socket.Release())},
+ initially_muted);
+ }
+
+ protected:
+ void CreateStream(bool initially_muted) {
+ audio_input_ = std::make_unique<CapturedAudioInput>(
+ base::BindRepeating(&CapturedAudioInputTest::CreateMockStream,
+ base::Unretained(this), initially_muted));
+ base::RunLoop run_loop;
+ EXPECT_CALL(delegate_, StreamCreated(initially_muted))
+ .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+ audio_input_->CreateStream(&delegate_, media::AudioParameters(), false, 10);
+ run_loop.Run();
+ }
+
+ void CloseStream() {
+ EXPECT_TRUE(audio_input_);
+ audio_input_->CloseStream();
+ scoped_task_environment_.RunUntilIdle();
+ socket_.Close();
+ audio_input_.reset();
+ stream_ = nullptr;
+ }
+
+ void SignalStreamError() {
+ EXPECT_TRUE(stream_client_.is_bound());
+ base::RunLoop run_loop;
+ EXPECT_CALL(delegate_, OnError())
+ .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+ stream_client_->OnError();
+ run_loop.Run();
+ }
+
+ void SignalMutedStateChanged(bool is_muted) {
+ EXPECT_TRUE(stream_client_.is_bound());
+ base::RunLoop run_loop;
+ EXPECT_CALL(delegate_, OnMuted(true))
+ .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+ stream_client_->OnMutedStateChanged(is_muted);
+ run_loop.Run();
+ }
+
+ void SetVolume(double volume) {
+ EXPECT_TRUE(audio_input_);
+ base::RunLoop run_loop;
+ EXPECT_CALL(*stream_, SetVolume(volume))
+ .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+ audio_input_->SetVolume(volume);
+ run_loop.Run();
+ }
+
+ void Record() {
+ EXPECT_TRUE(audio_input_);
+ base::RunLoop run_loop;
+ EXPECT_CALL(*stream_, Record())
+ .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+ audio_input_->RecordStream();
+ run_loop.Run();
+ }
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ std::unique_ptr<media::AudioInputIPC> audio_input_;
+ MockDelegate delegate_;
+ MockStream* stream_ = nullptr;
+ media::mojom::AudioInputStreamClientPtr stream_client_;
+ base::CancelableSyncSocket socket_;
+
+ DISALLOW_COPY_AND_ASSIGN(CapturedAudioInputTest);
+};
+
+TEST_F(CapturedAudioInputTest, CreateStream) {
+ // Test that the initial muted state can be propagated to |delegate_|.
+ CreateStream(false);
+ CloseStream();
+ CreateStream(true);
+ CloseStream();
+}
+
+TEST_F(CapturedAudioInputTest, PropagatesStreamError) {
+ CreateStream(false);
+ SignalStreamError();
+ CloseStream();
+}
+
+TEST_F(CapturedAudioInputTest, PropagatesMutedStateChange) {
+ CreateStream(false);
+ SignalMutedStateChanged(true);
+ CloseStream();
+}
+
+TEST_F(CapturedAudioInputTest, SetVolume) {
+ CreateStream(false);
+ SetVolume(0.8);
+ CloseStream();
+}
+
+TEST_F(CapturedAudioInputTest, Record) {
+ CreateStream(false);
+ Record();
+ CloseStream();
+}
+
+} // namespace mirroring
diff --git a/chromium/components/mirroring/service/interface.h b/chromium/components/mirroring/service/interface.h
index 3feac5c3edb..222b342e241 100644
--- a/chromium/components/mirroring/service/interface.h
+++ b/chromium/components/mirroring/service/interface.h
@@ -7,7 +7,11 @@
#include <string>
+#include "media/base/audio_parameters.h"
#include "media/capture/mojom/video_capture.mojom.h"
+#include "media/mojo/interfaces/audio_data_pipe.mojom.h"
+#include "media/mojo/interfaces/audio_input_stream.mojom.h"
+#include "media/mojo/interfaces/remoting.mojom.h"
#include "net/base/ip_address.h"
#include "services/network/public/mojom/network_service.mojom.h"
@@ -78,15 +82,37 @@ class SessionObserver {
virtual void DidStop() = 0;
};
+class AudioStreamCreatorClient {
+ public:
+ virtual ~AudioStreamCreatorClient() {}
+
+ // Called by ResourceProvider when an audio input stream is created as
+ // requested.
+ virtual void StreamCreated(
+ media::mojom::AudioInputStreamPtr stream,
+ media::mojom::AudioInputStreamClientRequest client_request,
+ media::mojom::ReadOnlyAudioDataPipePtr data_pipe,
+ bool initially_muted) = 0;
+};
+
class ResourceProvider {
public:
virtual ~ResourceProvider() {}
virtual void GetVideoCaptureHost(
media::mojom::VideoCaptureHostRequest request) = 0;
+
virtual void GetNetworkContext(
network::mojom::NetworkContextRequest request) = 0;
- // TODO(xjz): Add interface to get AudioCaptureHost.
+
+ virtual void CreateAudioStream(AudioStreamCreatorClient* client,
+ const media::AudioParameters& params,
+ uint32_t total_segments) = 0;
+
+ virtual void ConnectToRemotingSource(
+ media::mojom::RemoterPtr remoter,
+ media::mojom::RemotingSourceRequest request) = 0;
+
// TODO(xjz): Add interface for HW encoder profiles query and VEA create
// support.
};
diff --git a/chromium/components/mirroring/service/media_remoter.cc b/chromium/components/mirroring/service/media_remoter.cc
new file mode 100644
index 00000000000..c1c39fd02cf
--- /dev/null
+++ b/chromium/components/mirroring/service/media_remoter.cc
@@ -0,0 +1,195 @@
+// 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/mirroring/service/media_remoter.h"
+
+#include "base/base64.h"
+#include "base/callback.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "base/values.h"
+#include "components/mirroring/service/interface.h"
+#include "components/mirroring/service/message_dispatcher.h"
+#include "components/mirroring/service/remoting_sender.h"
+#include "media/cast/net/cast_transport.h"
+
+using media::cast::Codec;
+using media::cast::FrameSenderConfig;
+
+namespace mirroring {
+
+MediaRemoter::MediaRemoter(
+ Client* client,
+ const media::mojom::RemotingSinkMetadata& sink_metadata,
+ MessageDispatcher* message_dispatcher)
+ : client_(client),
+ sink_metadata_(sink_metadata),
+ message_dispatcher_(message_dispatcher),
+ binding_(this),
+ cast_environment_(nullptr),
+ transport_(nullptr),
+ state_(MIRRORING),
+ weak_factory_(this) {
+ DCHECK(client_);
+ DCHECK(message_dispatcher_);
+
+ media::mojom::RemoterPtr remoter;
+ binding_.Bind(mojo::MakeRequest(&remoter));
+ client_->ConnectToRemotingSource(std::move(remoter),
+ mojo::MakeRequest(&remoting_source_));
+ remoting_source_->OnSinkAvailable(sink_metadata_.Clone());
+}
+
+MediaRemoter::~MediaRemoter() {
+ // Stop this remoting session if mirroring is stopped during a remoting
+ // session. For example, user stops mirroring through the cast dialog or
+ // closes the tab.
+ Stop(media::mojom::RemotingStopReason::ROUTE_TERMINATED);
+}
+
+void MediaRemoter::OnMessageFromSink(const ReceiverResponse& response) {
+ DCHECK_EQ(ResponseType::RPC, response.type);
+ remoting_source_->OnMessageFromSink(
+ std::vector<uint8_t>(response.rpc.begin(), response.rpc.end()));
+}
+
+void MediaRemoter::StartRpcMessaging(
+ scoped_refptr<media::cast::CastEnvironment> cast_environment,
+ media::cast::CastTransport* transport,
+ const FrameSenderConfig& audio_config,
+ const FrameSenderConfig& video_config) {
+ DCHECK(!cast_environment_);
+ DCHECK(!transport_);
+ DCHECK_EQ(Codec::CODEC_UNKNOWN, audio_config_.codec);
+ DCHECK_EQ(Codec::CODEC_UNKNOWN, video_config_.codec);
+ DCHECK(audio_config.codec == Codec::CODEC_AUDIO_REMOTE ||
+ video_config.codec == Codec::CODEC_VIDEO_REMOTE);
+
+ if (state_ != STARTING_REMOTING)
+ return; // Start operation was canceled.
+ // A remoting streaming session started. Start RPC message transport and
+ // notify the remoting source to start data streaming.
+ cast_environment_ = std::move(cast_environment);
+ transport_ = transport;
+ audio_config_ = audio_config;
+ video_config_ = video_config;
+ message_dispatcher_->Subscribe(
+ ResponseType::RPC, base::BindRepeating(&MediaRemoter::OnMessageFromSink,
+ weak_factory_.GetWeakPtr()));
+ state_ = REMOTING_STARTED;
+ remoting_source_->OnStarted();
+}
+
+void MediaRemoter::OnMirroringResumed() {
+ if (state_ == REMOTING_DISABLED)
+ return;
+ DCHECK_EQ(STOPPING_REMOTING, state_);
+ state_ = MIRRORING;
+ // Notify the remoting source to enable starting media remoting again.
+ remoting_source_->OnSinkAvailable(sink_metadata_.Clone());
+}
+
+void MediaRemoter::OnRemotingFailed() {
+ DCHECK(state_ == STARTING_REMOTING || state_ == REMOTING_STARTED);
+ if (state_ == STARTING_REMOTING) {
+ // TODO(xjz): Rename SERVICE_NOT_CONNECTED to INVALID_ANSWER_MESSAGE.
+ remoting_source_->OnStartFailed(
+ media::mojom::RemotingStartFailReason::SERVICE_NOT_CONNECTED);
+ }
+ state_ = REMOTING_DISABLED;
+ remoting_source_->OnSinkGone();
+ // Fallback to mirroring.
+ client_->RestartMirroringStreaming();
+}
+
+void MediaRemoter::Stop(media::mojom::RemotingStopReason reason) {
+ if (state_ != STARTING_REMOTING && state_ != REMOTING_STARTED)
+ return;
+ if (state_ == REMOTING_STARTED) {
+ message_dispatcher_->Unsubscribe(ResponseType::RPC);
+ audio_sender_.reset();
+ video_sender_.reset();
+ cast_environment_ = nullptr;
+ transport_ = nullptr;
+ audio_config_ = FrameSenderConfig();
+ video_config_ = FrameSenderConfig();
+ }
+ state_ = STOPPING_REMOTING;
+ remoting_source_->OnStopped(reason);
+ // Prevent the start of remoting until switching completes.
+ remoting_source_->OnSinkGone();
+ // Switch to mirroring.
+ client_->RestartMirroringStreaming();
+}
+
+void MediaRemoter::Start() {
+ if (state_ != MIRRORING) {
+ VLOG(2) << "Warning: Ignore start request. state=" << state_;
+ return;
+ }
+ state_ = STARTING_REMOTING;
+ client_->RequestRemotingStreaming();
+}
+
+void MediaRemoter::StartDataStreams(
+ mojo::ScopedDataPipeConsumerHandle audio_pipe,
+ mojo::ScopedDataPipeConsumerHandle video_pipe,
+ media::mojom::RemotingDataStreamSenderRequest audio_sender_request,
+ media::mojom::RemotingDataStreamSenderRequest video_sender_request) {
+ if (state_ != REMOTING_STARTED)
+ return; // Stop() was called before.
+ DCHECK(cast_environment_);
+ DCHECK(transport_);
+ if (audio_pipe.is_valid() &&
+ audio_config_.codec == Codec::CODEC_AUDIO_REMOTE) {
+ audio_sender_ = std::make_unique<RemotingSender>(
+ cast_environment_, transport_, audio_config_, std::move(audio_pipe),
+ std::move(audio_sender_request),
+ base::BindOnce(&MediaRemoter::OnRemotingDataStreamError,
+ base::Unretained(this)));
+ }
+ if (video_pipe.is_valid() &&
+ video_config_.codec == Codec::CODEC_VIDEO_REMOTE) {
+ video_sender_ = std::make_unique<RemotingSender>(
+ cast_environment_, transport_, video_config_, std::move(video_pipe),
+ std::move(video_sender_request),
+ base::BindOnce(&MediaRemoter::OnRemotingDataStreamError,
+ base::Unretained(this)));
+ }
+}
+
+void MediaRemoter::SendMessageToSink(const std::vector<uint8_t>& message) {
+ if (state_ != REMOTING_STARTED)
+ return;
+ std::string encoded_rpc;
+ base::Base64Encode(
+ base::StringPiece(reinterpret_cast<const char*>(message.data()),
+ message.size()),
+ &encoded_rpc);
+ base::Value rpc(base::Value::Type::DICTIONARY);
+ rpc.SetKey("type", base::Value("RPC"));
+ rpc.SetKey("rpc", base::Value(std::move(encoded_rpc)));
+ CastMessage rpc_message;
+ rpc_message.message_namespace = kRemotingNamespace;
+ const bool did_serialize_rpc =
+ base::JSONWriter::Write(rpc, &rpc_message.json_format_data);
+ DCHECK(did_serialize_rpc);
+ message_dispatcher_->SendOutboundMessage(rpc_message);
+}
+
+void MediaRemoter::EstimateTransmissionCapacity(
+ media::mojom::Remoter::EstimateTransmissionCapacityCallback callback) {
+ NOTIMPLEMENTED();
+ std::move(callback).Run(0);
+}
+
+void MediaRemoter::OnRemotingDataStreamError() {
+ if (state_ != REMOTING_STARTED)
+ return;
+ state_ = REMOTING_DISABLED;
+ Stop(media::mojom::RemotingStopReason::DATA_SEND_FAILED);
+}
+
+} // namespace mirroring
diff --git a/chromium/components/mirroring/service/media_remoter.h b/chromium/components/mirroring/service/media_remoter.h
new file mode 100644
index 00000000000..edaf8e3cff7
--- /dev/null
+++ b/chromium/components/mirroring/service/media_remoter.h
@@ -0,0 +1,147 @@
+// 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_MIRRORING_SERVICE_MEDIA_REMOTER_H_
+#define COMPONENTS_MIRRORING_SERVICE_MEDIA_REMOTER_H_
+
+#include "base/macros.h"
+#include "media/cast/cast_config.h"
+#include "media/mojo/interfaces/remoting.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace media {
+namespace cast {
+class CastEnvironment;
+class CastTransport;
+} // namespace cast
+} // namespace media
+
+namespace mirroring {
+
+class MessageDispatcher;
+struct ReceiverResponse;
+class RemotingSender;
+
+// MediaRemoter remotes media content directly to a Cast Receiver. When
+// MediaRemoter is started, it connects itself with a source tab in browser
+// through the Mirroring Service mojo interface and allows the browser to access
+// this MediaRemoter to start/stop individual remoting sessions, which are
+// caused by user actions (i.e., when they somehow indicate a desire to
+// enter/leave an immersive video-watching mode).
+//
+// When a remoting session is started, MediaRemoter will first request that tab
+// mirroring be switched into content remoting mode. If granted, it will notify
+// the browser that this has succeeded. At this point, two-way RPC binary
+// messaging begins, and the MediaRemoter simply forwards messages between the
+// browser and the Cast Receiver. The audio/video data streams are delivered
+// from the media renderer to the Mirroring Service through mojo datapipes, and
+// are then sent out to Cast Receiver through Cast Streaming.
+class MediaRemoter final : public media::mojom::Remoter {
+ public:
+ class Client {
+ public:
+ virtual ~Client() {}
+
+ // Connects the |remoter| with a source tab.
+ virtual void ConnectToRemotingSource(
+ media::mojom::RemoterPtr remoter,
+ media::mojom::RemotingSourceRequest source_request) = 0;
+
+ // Requests to start remoting. StartRpcMessaging() / OnRemotingStartFailed()
+ // will be called when starting succeeds / fails.
+ virtual void RequestRemotingStreaming() = 0;
+
+ // Requests to resume mirroring.
+ virtual void RestartMirroringStreaming() = 0;
+ };
+
+ MediaRemoter(Client* client,
+ const media::mojom::RemotingSinkMetadata& sink_metadata,
+ MessageDispatcher* message_dispatcher);
+
+ ~MediaRemoter() override;
+
+ // Callback from |message_dispatcher_| for received RPC messages.
+ void OnMessageFromSink(const ReceiverResponse& response);
+
+ // Called when OFFER/ANSWER exchange for a remoting session succeeds.
+ void StartRpcMessaging(
+ scoped_refptr<media::cast::CastEnvironment> cast_environment,
+ media::cast::CastTransport* transport,
+ const media::cast::FrameSenderConfig& audio_config,
+ const media::cast::FrameSenderConfig& video_config);
+
+ // Called when a mirroring session is successfully resumed.
+ void OnMirroringResumed();
+
+ // Error occurred either during the start of remoting or in the middle of
+ // remoting. In either case, this call fallbacks to mirroring, and prevents
+ // further starting of media remoting during this mirroring session.
+ void OnRemotingFailed();
+
+ // media::mojom::Remoter implememtation. Stops the current remoting session.
+ // This could be called either by the RemotingSource or the Session.
+ void Stop(media::mojom::RemotingStopReason reason) override;
+
+ private:
+ // media::mojom::Remoter implememtation.
+ void Start() override;
+ void StartDataStreams(
+ mojo::ScopedDataPipeConsumerHandle audio_pipe,
+ mojo::ScopedDataPipeConsumerHandle video_pipe,
+ media::mojom::RemotingDataStreamSenderRequest audio_sender_request,
+ media::mojom::RemotingDataStreamSenderRequest video_sender_request)
+ override;
+ void SendMessageToSink(const std::vector<uint8_t>& message) override;
+ void EstimateTransmissionCapacity(
+ media::mojom::Remoter::EstimateTransmissionCapacityCallback callback)
+ override;
+
+ // Called by RemotingSender when error occurred. Will stop this remoting
+ // session and fallback to mirroring.
+ void OnRemotingDataStreamError();
+
+ Client* const client_; // Outlives this class.
+ const media::mojom::RemotingSinkMetadata sink_metadata_;
+ MessageDispatcher* const message_dispatcher_; // Outlives this class.
+ mojo::Binding<media::mojom::Remoter> binding_;
+ media::mojom::RemotingSourcePtr remoting_source_;
+ scoped_refptr<media::cast::CastEnvironment> cast_environment_;
+ std::unique_ptr<RemotingSender> audio_sender_;
+ std::unique_ptr<RemotingSender> video_sender_;
+ media::cast::CastTransport* transport_; // Outlives this class;
+ media::cast::FrameSenderConfig audio_config_;
+ media::cast::FrameSenderConfig video_config_;
+
+ // State transition diagram:
+ //
+ // .-----------> MIRRORING
+ // | |
+ // | V
+ // | STARTING_REMOTING
+ // | |
+ // | V
+ // | .-----------------------------.
+ // | | | |
+ // | | V V
+ // | | REMOTING_STARTED ----> REMOTING_DISABLED
+ // | | |
+ // | V V
+ // .--STOPPING_REMOTING
+ enum {
+ MIRRORING, // In mirroring.
+ STARTING_REMOTING, // Starting a remoting session.
+ REMOTING_STARTED, // Remoting started successfully.
+ REMOTING_DISABLED, // Remoting was disabled (because of error).
+ STOPPING_REMOTING, // Stopping the remoting session.
+ } state_;
+
+ base::WeakPtrFactory<MediaRemoter> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaRemoter);
+};
+
+} // namespace mirroring
+
+#endif // COMPONENTS_MIRRORING_SERVICE_MEDIA_REMOTER_H_
diff --git a/chromium/components/mirroring/service/media_remoter_unittest.cc b/chromium/components/mirroring/service/media_remoter_unittest.cc
new file mode 100644
index 00000000000..18776e5ec77
--- /dev/null
+++ b/chromium/components/mirroring/service/media_remoter_unittest.cc
@@ -0,0 +1,220 @@
+// 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/mirroring/service/media_remoter.h"
+
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/time/default_tick_clock.h"
+#include "components/mirroring/service/message_dispatcher.h"
+#include "components/mirroring/service/mirror_settings.h"
+#include "media/cast/cast_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::InvokeWithoutArgs;
+using ::testing::_;
+using ::testing::Mock;
+using media::mojom::RemotingSinkMetadata;
+using media::mojom::RemotingStopReason;
+using media::cast::RtpPayloadType;
+using media::cast::Codec;
+
+namespace mirroring {
+
+namespace {
+
+class MockRemotingSource final : public media::mojom::RemotingSource {
+ public:
+ MockRemotingSource() : binding_(this) {}
+ ~MockRemotingSource() override {}
+
+ void Bind(media::mojom::RemotingSourceRequest request) {
+ binding_.Bind(std::move(request));
+ }
+
+ MOCK_METHOD0(OnSinkGone, void());
+ MOCK_METHOD0(OnStarted, void());
+ MOCK_METHOD1(OnStartFailed, void(media::mojom::RemotingStartFailReason));
+ MOCK_METHOD1(OnMessageFromSink, void(const std::vector<uint8_t>&));
+ MOCK_METHOD1(OnStopped, void(RemotingStopReason));
+ MOCK_METHOD1(OnSinkAvailable, void(const RemotingSinkMetadata&));
+ void OnSinkAvailable(
+ media::mojom::RemotingSinkMetadataPtr metadata) override {
+ OnSinkAvailable(*metadata);
+ }
+
+ private:
+ mojo::Binding<media::mojom::RemotingSource> binding_;
+};
+
+RemotingSinkMetadata DefaultSinkMetadata() {
+ RemotingSinkMetadata metadata;
+ metadata.features.push_back(media::mojom::RemotingSinkFeature::RENDERING);
+ metadata.video_capabilities.push_back(
+ media::mojom::RemotingSinkVideoCapability::CODEC_VP8);
+ metadata.audio_capabilities.push_back(
+ media::mojom::RemotingSinkAudioCapability::CODEC_BASELINE_SET);
+ metadata.friendly_name = "Test";
+ return metadata;
+}
+
+} // namespace
+
+class MediaRemoterTest : public CastMessageChannel,
+ public MediaRemoter::Client,
+ public ::testing::Test {
+ public:
+ MediaRemoterTest()
+ : message_dispatcher_(this, error_callback_.Get()),
+ sink_metadata_(DefaultSinkMetadata()) {}
+ ~MediaRemoterTest() override { scoped_task_environment_.RunUntilIdle(); }
+
+ protected:
+ MOCK_METHOD1(Send, void(const CastMessage&));
+ MOCK_METHOD0(OnConnectToRemotingSource, void());
+ MOCK_METHOD0(RequestRemotingStreaming, void());
+ MOCK_METHOD0(RestartMirroringStreaming, void());
+
+ // MediaRemoter::Client implementation.
+ void ConnectToRemotingSource(
+ media::mojom::RemoterPtr remoter,
+ media::mojom::RemotingSourceRequest source_request) override {
+ remoter_ = std::move(remoter);
+ remoting_source_.Bind(std::move(source_request));
+ OnConnectToRemotingSource();
+ }
+
+ void CreateRemoter() {
+ EXPECT_FALSE(media_remoter_);
+ EXPECT_CALL(*this, OnConnectToRemotingSource()).Times(1);
+ EXPECT_CALL(remoting_source_, OnSinkAvailable(_)).Times(1);
+ media_remoter_ = std::make_unique<MediaRemoter>(this, sink_metadata_,
+ &message_dispatcher_);
+ scoped_task_environment_.RunUntilIdle();
+ Mock::VerifyAndClear(this);
+ Mock::VerifyAndClear(&remoting_source_);
+ }
+
+ // Requests to start a remoting session.
+ void StartRemoting() {
+ ASSERT_TRUE(remoter_);
+ EXPECT_CALL(*this, RequestRemotingStreaming()).Times(1);
+ remoter_->Start();
+ scoped_task_environment_.RunUntilIdle();
+ Mock::VerifyAndClear(this);
+ }
+
+ // Stops the current remoting session.
+ void StopRemoting() {
+ ASSERT_TRUE(remoter_);
+ EXPECT_CALL(remoting_source_, OnStopped(RemotingStopReason::USER_DISABLED))
+ .Times(1);
+ EXPECT_CALL(remoting_source_, OnSinkGone()).Times(1);
+ EXPECT_CALL(*this, RestartMirroringStreaming()).Times(1);
+ remoter_->Stop(media::mojom::RemotingStopReason::USER_DISABLED);
+ scoped_task_environment_.RunUntilIdle();
+ Mock::VerifyAndClear(this);
+ Mock::VerifyAndClear(&remoting_source_);
+ }
+
+ // Signals that a remoting streaming session starts successfully.
+ void RemotingStreamingStarted() {
+ ASSERT_TRUE(media_remoter_);
+ scoped_refptr<media::cast::CastEnvironment> cast_environment =
+ new media::cast::CastEnvironment(
+ base::DefaultTickClock::GetInstance(),
+ scoped_task_environment_.GetMainThreadTaskRunner(),
+ scoped_task_environment_.GetMainThreadTaskRunner(),
+ scoped_task_environment_.GetMainThreadTaskRunner());
+ EXPECT_CALL(remoting_source_, OnStarted()).Times(1);
+ media_remoter_->StartRpcMessaging(
+ cast_environment, nullptr, media::cast::FrameSenderConfig(),
+ MirrorSettings::GetDefaultVideoConfig(RtpPayloadType::REMOTE_VIDEO,
+ Codec::CODEC_VIDEO_REMOTE));
+ scoped_task_environment_.RunUntilIdle();
+ Mock::VerifyAndClear(&remoting_source_);
+ }
+
+ // Signals that mirroring is resumed successfully.
+ void MirroringResumed() {
+ EXPECT_CALL(remoting_source_, OnSinkAvailable(_)).Times(1);
+ media_remoter_->OnMirroringResumed();
+ scoped_task_environment_.RunUntilIdle();
+ Mock::VerifyAndClear(&remoting_source_);
+ }
+
+ // Signals that remoting session failed to start.
+ void RemotingStartFailed() {
+ ASSERT_TRUE(media_remoter_);
+ EXPECT_CALL(remoting_source_, OnStartFailed(_)).Times(1);
+ EXPECT_CALL(remoting_source_, OnSinkGone()).Times(1);
+ EXPECT_CALL(*this, RestartMirroringStreaming()).Times(1);
+ media_remoter_->OnRemotingFailed();
+ scoped_task_environment_.RunUntilIdle();
+ Mock::VerifyAndClear(this);
+ Mock::VerifyAndClear(&remoting_source_);
+ }
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ base::MockCallback<MessageDispatcher::ErrorCallback> error_callback_;
+ MessageDispatcher message_dispatcher_;
+ const media::mojom::RemotingSinkMetadata sink_metadata_;
+ MockRemotingSource remoting_source_;
+ media::mojom::RemoterPtr remoter_;
+ std::unique_ptr<MediaRemoter> media_remoter_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaRemoterTest);
+};
+
+TEST_F(MediaRemoterTest, StartAndStopRemoting) {
+ CreateRemoter();
+ StartRemoting();
+ RemotingStreamingStarted();
+ StopRemoting();
+}
+
+TEST_F(MediaRemoterTest, StopRemotingWhileStarting) {
+ CreateRemoter();
+ // Starts a remoting session.
+ StartRemoting();
+ // Immediately stops the remoting session while not started yet.
+ StopRemoting();
+
+ // Signals that successfully switch to mirroring.
+ MirroringResumed();
+ // Now remoting can be started again.
+ StartRemoting();
+}
+
+TEST_F(MediaRemoterTest, RemotingStartFailed) {
+ CreateRemoter();
+ StartRemoting();
+ RemotingStartFailed();
+}
+
+TEST_F(MediaRemoterTest, SwitchBetweenMultipleSessions) {
+ CreateRemoter();
+
+ // Start a remoting session.
+ StartRemoting();
+ RemotingStreamingStarted();
+
+ // Stop the remoting session and switch to mirroring.
+ StopRemoting();
+ MirroringResumed();
+
+ // Switch to remoting again.
+ StartRemoting();
+ RemotingStreamingStarted();
+
+ // Switch to mirroring again.
+ StopRemoting();
+ MirroringResumed();
+}
+
+} // namespace mirroring
diff --git a/chromium/components/mirroring/service/message_dispatcher.cc b/chromium/components/mirroring/service/message_dispatcher.cc
index aa5ad4a4069..95cf380aa2e 100644
--- a/chromium/components/mirroring/service/message_dispatcher.cc
+++ b/chromium/components/mirroring/service/message_dispatcher.cc
@@ -17,10 +17,7 @@ class MessageDispatcher::RequestHolder {
public:
RequestHolder() {}
- ~RequestHolder() {
- if (!response_callback_.is_null())
- std::move(response_callback_).Run(ReceiverResponse());
- }
+ ~RequestHolder() {}
void Start(const base::TimeDelta& timeout,
int32_t sequence_number,
@@ -134,6 +131,8 @@ void MessageDispatcher::RequestReply(const CastMessage& message,
OnceResponseCallback callback) {
DCHECK(!callback.is_null());
DCHECK(timeout > base::TimeDelta());
+
+ Unsubscribe(response_type); // Cancel the old request if there is any.
RequestHolder* const request_holder = new RequestHolder();
request_holder->Start(
timeout, sequence_number,
diff --git a/chromium/components/mirroring/service/message_dispatcher.h b/chromium/components/mirroring/service/message_dispatcher.h
index 7b2ee9bfb62..7fbd3c5ceb9 100644
--- a/chromium/components/mirroring/service/message_dispatcher.h
+++ b/chromium/components/mirroring/service/message_dispatcher.h
@@ -38,6 +38,8 @@ class MessageDispatcher final : public CastMessageChannel {
// delivered to the supplied callback if the sequence number of the response
// matches |sequence_number|. If the timeout period elapses, the callback will
// be run once with an unknown type of |response|.
+ // Note: Calling RequestReply() before a previous reply was made will cancel
+ // the previous request and not run its response callback.
void RequestReply(const CastMessage& message,
ResponseType response_type,
int32_t sequence_number,
diff --git a/chromium/components/mirroring/service/message_dispatcher_unittest.cc b/chromium/components/mirroring/service/message_dispatcher_unittest.cc
index 32cb7328ecc..184227def27 100644
--- a/chromium/components/mirroring/service/message_dispatcher_unittest.cc
+++ b/chromium/components/mirroring/service/message_dispatcher_unittest.cc
@@ -286,14 +286,11 @@ TEST_F(MessageDispatcherTest, RequestReply) {
EXPECT_FALSE(last_answer_response_);
EXPECT_FALSE(last_status_response_);
- // Destroy the dispatcher. Expect to receive an unknown type response.
+ // Destroy the dispatcher.
message_dispatcher_.reset();
scoped_task_environment_.RunUntilIdle();
- ASSERT_TRUE(last_answer_response_);
+ ASSERT_FALSE(last_answer_response_);
EXPECT_FALSE(last_status_response_);
- EXPECT_TRUE(last_error_message_.empty());
- EXPECT_EQ(ResponseType::UNKNOWN, last_answer_response_->type);
- EXPECT_EQ(-1, last_answer_response_->sequence_number);
}
} // namespace mirroring
diff --git a/chromium/components/mirroring/service/mirror_settings.cc b/chromium/components/mirroring/service/mirror_settings.cc
index a14799757a4..3c51c1e6532 100644
--- a/chromium/components/mirroring/service/mirror_settings.cc
+++ b/chromium/components/mirroring/service/mirror_settings.cc
@@ -6,6 +6,8 @@
#include <algorithm>
+#include "media/base/audio_parameters.h"
+
using media::cast::FrameSenderConfig;
using media::cast::Codec;
using media::cast::RtpPayloadType;
@@ -118,6 +120,14 @@ media::VideoCaptureParams MirrorSettings::GetVideoCaptureParams() {
return params;
}
+media::AudioParameters MirrorSettings::GetAudioCaptureParams() {
+ media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ media::CHANNEL_LAYOUT_STEREO, kAudioTimebase,
+ kAudioTimebase / 100);
+ DCHECK(params.IsValid());
+ return params;
+}
+
base::Value MirrorSettings::ToDictionaryValue() {
base::Value settings(base::Value::Type::DICTIONARY);
settings.SetKey("maxWidth", base::Value(max_width_));
diff --git a/chromium/components/mirroring/service/mirror_settings.h b/chromium/components/mirroring/service/mirror_settings.h
index 937602d3e36..ac2ee3c528d 100644
--- a/chromium/components/mirroring/service/mirror_settings.h
+++ b/chromium/components/mirroring/service/mirror_settings.h
@@ -10,6 +10,10 @@
#include "media/capture/video_capture_types.h"
#include "media/cast/cast_config.h"
+namespace media {
+class AudioParameters;
+} // namespace media
+
namespace mirroring {
// Holds the default settings for a mirroring session. This class provides the
@@ -37,6 +41,9 @@ class MirrorSettings {
// Get video capture constraints with the current settings.
media::VideoCaptureParams GetVideoCaptureParams();
+ // Get Audio capture constraints with the current settings.
+ media::AudioParameters GetAudioCaptureParams();
+
int max_width() const { return max_width_; }
int max_height() const { return max_height_; }
diff --git a/chromium/components/mirroring/service/remoting_sender.cc b/chromium/components/mirroring/service/remoting_sender.cc
new file mode 100644
index 00000000000..11b21e4ec26
--- /dev/null
+++ b/chromium/components/mirroring/service/remoting_sender.cc
@@ -0,0 +1,216 @@
+// 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/mirroring/service/remoting_sender.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/time/default_tick_clock.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 {
+
+RemotingSender::RemotingSender(
+ scoped_refptr<media::cast::CastEnvironment> cast_environment,
+ media::cast::CastTransport* transport,
+ const media::cast::FrameSenderConfig& config,
+ mojo::ScopedDataPipeConsumerHandle pipe,
+ media::mojom::RemotingDataStreamSenderRequest request,
+ base::OnceClosure error_callback)
+ : FrameSender(cast_environment,
+ transport,
+ config,
+ media::cast::NewFixedCongestionControl(config.max_bitrate)),
+ clock_(cast_environment->Clock()),
+ error_callback_(std::move(error_callback)),
+ data_pipe_reader_(new media::MojoDataPipeReader(std::move(pipe))),
+ binding_(this, std::move(request)),
+ input_queue_discards_remaining_(0),
+ is_reading_(false),
+ flow_restart_pending_(true),
+ weak_factory_(this) {
+ binding_.set_connection_error_handler(base::BindOnce(
+ &RemotingSender::OnRemotingDataStreamError, base::Unretained(this)));
+}
+
+RemotingSender::~RemotingSender() {}
+
+void RemotingSender::SendFrame(uint32_t frame_size) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ const bool need_to_start_processing = input_queue_.empty();
+ input_queue_.push(base::BindRepeating(&RemotingSender::ReadFrame,
+ base::Unretained(this), frame_size));
+ input_queue_.push(base::BindRepeating(&RemotingSender::TrySendFrame,
+ base::Unretained(this)));
+ if (need_to_start_processing)
+ ProcessNextInputTask();
+}
+
+void RemotingSender::CancelInFlightData() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+// TODO(miu): The following code is something we want to do as an
+// optimization. However, as-is, it's not quite correct. We can only cancel
+// frames where no packets have actually hit the network yet. Said another
+// way, we can only cancel frames the receiver has definitely not seen any
+// part of (including kickstarting!). http://crbug.com/647423
+#if 0
+ if (latest_acked_frame_id_ < last_sent_frame_id_) {
+ std::vector<media::cast::FrameId> frames_to_cancel;
+ do {
+ ++latest_acked_frame_id_;
+ frames_to_cancel.push_back(latest_acked_frame_id_);
+ } while (latest_acked_frame_id_ < last_sent_frame_id_);
+ transport_->CancelSendingFrames(ssrc_, frames_to_cancel);
+ }
+#endif
+
+ // Flag that all pending input operations should discard data.
+ input_queue_discards_remaining_ = input_queue_.size();
+
+ flow_restart_pending_ = true;
+ VLOG(1) << "Now restarting because in-flight data was just canceled.";
+}
+
+int RemotingSender::GetNumberOfFramesInEncoder() const {
+ NOTREACHED();
+ return 0;
+}
+
+base::TimeDelta RemotingSender::GetInFlightMediaDuration() const {
+ NOTREACHED();
+ return base::TimeDelta();
+}
+
+void RemotingSender::OnCancelSendingFrames() {
+ // One or more frames were canceled. This may allow pending input operations
+ // to complete.
+ ProcessNextInputTask();
+}
+
+void RemotingSender::ProcessNextInputTask() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (input_queue_.empty() || is_reading_)
+ return;
+
+ input_queue_.front().Run();
+}
+
+void RemotingSender::ReadFrame(uint32_t size) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!is_reading_);
+ if (!data_pipe_reader_->IsPipeValid()) {
+ VLOG(1) << "Data pipe handle no longer valid.";
+ OnRemotingDataStreamError();
+ return;
+ }
+
+ is_reading_ = true;
+ if (input_queue_discards_remaining_ > 0) {
+ data_pipe_reader_->Read(
+ nullptr, size,
+ base::BindOnce(&RemotingSender::OnFrameRead, base::Unretained(this)));
+ } else {
+ next_frame_data_.resize(size);
+ data_pipe_reader_->Read(
+ reinterpret_cast<uint8_t*>(base::data(next_frame_data_)), size,
+ base::BindOnce(&RemotingSender::OnFrameRead, base::Unretained(this)));
+ }
+}
+
+void RemotingSender::TrySendFrame() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!is_reading_);
+ if (input_queue_discards_remaining_ > 0) {
+ OnInputTaskComplete();
+ return;
+ }
+
+ // If there would be too many frames in-flight, do not proceed.
+ if (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_;
+ auto remoting_frame = std::make_unique<media::cast::SenderEncodedFrame>();
+ remoting_frame->frame_id = 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);
+ remoting_frame->dependency = media::cast::EncodedFrame::DEPENDENT;
+ }
+ remoting_frame->referenced_frame_id =
+ remoting_frame->dependency == media::cast::EncodedFrame::KEY
+ ? frame_id
+ : frame_id - 1;
+ remoting_frame->reference_time = clock_->NowTicks();
+ remoting_frame->encode_completion_time = remoting_frame->reference_time;
+ media::cast::RtpTimeTicks last_frame_rtp_timestamp;
+ if (is_first_frame_to_be_sent) {
+ 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);
+ }
+ // Ensure each successive frame's RTP timestamp is unique, but otherwise just
+ // base it on the reference time.
+ remoting_frame->rtp_timestamp =
+ last_frame_rtp_timestamp +
+ std::max(media::cast::RtpTimeDelta::FromTicks(1),
+ media::cast::RtpTimeDelta::FromTimeDelta(
+ remoting_frame->reference_time - last_frame_reference_time,
+ media::cast::kRemotingRtpTimebase));
+ remoting_frame->data.swap(next_frame_data_);
+
+ SendEncodedFrame(0, std::move(remoting_frame));
+
+ OnInputTaskComplete();
+}
+
+void RemotingSender::OnFrameRead(bool success) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(is_reading_);
+ is_reading_ = false;
+ if (!success) {
+ OnRemotingDataStreamError();
+ return;
+ }
+ OnInputTaskComplete();
+}
+
+void RemotingSender::OnInputTaskComplete() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!input_queue_.empty());
+ input_queue_.pop();
+ if (input_queue_discards_remaining_ > 0)
+ --input_queue_discards_remaining_;
+
+ // Always force a post task to prevent the stack from growing too deep.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&RemotingSender::ProcessNextInputTask,
+ weak_factory_.GetWeakPtr()));
+}
+
+void RemotingSender::OnRemotingDataStreamError() {
+ data_pipe_reader_.reset();
+ binding_.Close();
+ if (!error_callback_.is_null())
+ std::move(error_callback_).Run();
+}
+
+} // namespace mirroring
diff --git a/chromium/components/mirroring/service/remoting_sender.h b/chromium/components/mirroring/service/remoting_sender.h
new file mode 100644
index 00000000000..d36d005ab56
--- /dev/null
+++ b/chromium/components/mirroring/service/remoting_sender.h
@@ -0,0 +1,120 @@
+// 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_MIRRORING_SERVICE_REMOTING_SENDER_H_
+#define COMPONENTS_MIRRORING_SERVICE_REMOTING_SENDER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/containers/queue.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "media/cast/sender/frame_sender.h"
+#include "media/mojo/interfaces/remoting.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace base {
+class TickClock;
+} // namespace base
+
+namespace media {
+class MojoDataPipeReader;
+} // namespace media
+
+namespace mirroring {
+
+// RTP sender for a single Cast Remoting RTP stream. The client calls Send() to
+// instruct the sender to read from a Mojo data pipe and transmit the data using
+// a CastTransport.
+class RemotingSender final : public media::mojom::RemotingDataStreamSender,
+ public media::cast::FrameSender {
+ public:
+ // |transport| is expected to outlive this class.
+ RemotingSender(scoped_refptr<media::cast::CastEnvironment> cast_environment,
+ media::cast::CastTransport* transport,
+ const media::cast::FrameSenderConfig& config,
+ mojo::ScopedDataPipeConsumerHandle pipe,
+ media::mojom::RemotingDataStreamSenderRequest request,
+ base::OnceClosure error_callback);
+ ~RemotingSender() override;
+
+ private:
+ // Friend class for unit tests.
+ friend class RemotingSenderTest;
+
+ // media::mojom::RemotingDataStreamSender implementation. SendFrame() will
+ // push callbacks onto the back of the input queue, and these may or may not
+ // be processed at a later time. It depends on whether the data pipe has data
+ // available or the CastTransport can accept more frames. CancelInFlightData()
+ // is processed immediately, and will cause all pending operations to discard
+ // data when they are processed later.
+ void SendFrame(uint32_t frame_size) override;
+ void CancelInFlightData() override;
+
+ // FrameSender override.
+ int GetNumberOfFramesInEncoder() const override;
+ base::TimeDelta GetInFlightMediaDuration() const override;
+ void OnCancelSendingFrames() override;
+
+ // Attempt to run next pending input task, popping the head of the input queue
+ // as each task succeeds.
+ void ProcessNextInputTask();
+
+ // These are called via callbacks run from the input queue.
+ // Consumes a frame of |size| from the associated Mojo data pipe.
+ void ReadFrame(uint32_t size);
+ // Sends out the frame to the receiver over network.
+ void TrySendFrame();
+
+ // Called when a frame is completely read/discarded from the data pipe.
+ void OnFrameRead(bool success);
+
+ // Called when an input task completes.
+ void OnInputTaskComplete();
+
+ void OnRemotingDataStreamError();
+
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ const base::TickClock* clock_;
+
+ // Callback that is run to notify when a fatal error occurs.
+ base::OnceClosure error_callback_;
+
+ std::unique_ptr<media::MojoDataPipeReader> data_pipe_reader_;
+
+ // Mojo binding for this instance. Implementation at the other end of the
+ // message pipe uses the RemotingDataStreamSender interface to control when
+ // this RemotingSender consumes from |pipe_|.
+ mojo::Binding<media::mojom::RemotingDataStreamSender> binding_;
+
+ // The next frame's payload data. Populated by call to OnFrameRead() when
+ // reading succeeded.
+ std::string next_frame_data_;
+
+ // Queue of pending input operations. |input_queue_discards_remaining_|
+ // indicates the number of operations where data should be discarded (due to
+ // CancelInFlightData()).
+ base::queue<base::RepeatingClosure> input_queue_;
+ size_t input_queue_discards_remaining_;
+
+ // Indicates whether the |data_pipe_reader_| is processing a reading request.
+ bool is_reading_;
+
+ // Set to true if the first frame has not yet been sent, or if a
+ // CancelInFlightData() operation just completed. This causes TrySendFrame()
+ // to mark the next frame as the start of a new sequence.
+ bool flow_restart_pending_;
+
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<RemotingSender> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemotingSender);
+};
+
+} // namespace mirroring
+
+#endif // COMPONENTS_MIRRORING_SERVICE_REMOTING_SENDER_H_
diff --git a/chromium/components/mirroring/service/remoting_sender_unittest.cc b/chromium/components/mirroring/service/remoting_sender_unittest.cc
new file mode 100644
index 00000000000..0c61f9d9a10
--- /dev/null
+++ b/chromium/components/mirroring/service/remoting_sender_unittest.cc
@@ -0,0 +1,615 @@
+// 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/mirroring/service/remoting_sender.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/time/default_tick_clock.h"
+#include "media/cast/constants.h"
+#include "media/cast/net/cast_transport.h"
+#include "media/cast/test/utility/default_config.h"
+#include "media/mojo/interfaces/remoting.mojom.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mirroring {
+
+namespace {
+
+// Data pipe capacity is 1KB.
+constexpr int kDataPipeCapacity = 1024;
+
+// Implements the CastTransport interface to capture output from the
+// RemotingSender.
+class FakeTransport : public media::cast::CastTransport {
+ public:
+ FakeTransport() {}
+ ~FakeTransport() final {}
+
+ void TakeSentFrames(std::vector<media::cast::EncodedFrame>* frames) {
+ frames->swap(sent_frames_);
+ sent_frames_.clear();
+ }
+
+ void TakeCanceledFrameIds(std::vector<media::cast::FrameId>* frame_ids) {
+ frame_ids->swap(canceled_frame_ids_);
+ canceled_frame_ids_.clear();
+ }
+
+ media::cast::FrameId WaitForKickstart() {
+ base::RunLoop run_loop;
+ kickstarted_callback_ = run_loop.QuitClosure();
+ run_loop.Run();
+ return kickstarted_frame_id_;
+ }
+
+ protected:
+ void InsertFrame(uint32_t ssrc,
+ const media::cast::EncodedFrame& frame) final {
+ sent_frames_.push_back(frame);
+ }
+
+ void CancelSendingFrames(
+ uint32_t ssrc,
+ const std::vector<media::cast::FrameId>& frame_ids) final {
+ for (media::cast::FrameId frame_id : frame_ids)
+ canceled_frame_ids_.push_back(frame_id);
+ }
+
+ void ResendFrameForKickstart(uint32_t ssrc,
+ media::cast::FrameId frame_id) final {
+ kickstarted_frame_id_ = frame_id;
+ if (!kickstarted_callback_.is_null())
+ base::ResetAndReturn(&kickstarted_callback_).Run();
+ }
+
+ // The rest of the interface is not used for these tests.
+ void SendSenderReport(
+ uint32_t ssrc,
+ base::TimeTicks current_time,
+ media::cast::RtpTimeTicks current_time_as_rtp_timestamp) final {}
+ void AddValidRtpReceiver(uint32_t rtp_sender_ssrc,
+ uint32_t rtp_receiver_ssrc) final {}
+ void InitializeRtpReceiverRtcpBuilder(
+ uint32_t rtp_receiver_ssrc,
+ const media::cast::RtcpTimeData& time_data) final {}
+ void AddCastFeedback(const media::cast::RtcpCastMessage& cast_message,
+ base::TimeDelta target_delay) final {}
+ void AddPli(const media::cast::RtcpPliMessage& pli_message) final {}
+ void AddRtcpEvents(
+ const media::cast::ReceiverRtcpEventSubscriber::RtcpEvents& e) final {}
+ void AddRtpReceiverReport(const media::cast::RtcpReportBlock& b) final {}
+ void SendRtcpFromRtpReceiver() final {}
+ void SetOptions(const base::DictionaryValue& options) final {}
+
+ private:
+ std::vector<media::cast::EncodedFrame> sent_frames_;
+ std::vector<media::cast::FrameId> canceled_frame_ids_;
+
+ base::RepeatingClosure kickstarted_callback_;
+ media::cast::FrameId kickstarted_frame_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeTransport);
+};
+
+} // namespace
+
+class RemotingSenderTest : public ::testing::Test {
+ protected:
+ RemotingSenderTest()
+ : cast_environment_(new media::cast::CastEnvironment(
+ base::DefaultTickClock::GetInstance(),
+ scoped_task_environment_.GetMainThreadTaskRunner(),
+ scoped_task_environment_.GetMainThreadTaskRunner(),
+ scoped_task_environment_.GetMainThreadTaskRunner())),
+ expecting_error_callback_run_(false),
+ receiver_ssrc_(-1) {
+ const MojoCreateDataPipeOptions data_pipe_options{
+ sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1,
+ kDataPipeCapacity};
+ mojo::ScopedDataPipeConsumerHandle consumer_end;
+ CHECK_EQ(MOJO_RESULT_OK,
+ mojo::CreateDataPipe(&data_pipe_options, &producer_end_,
+ &consumer_end));
+
+ media::cast::FrameSenderConfig video_config =
+ media::cast::GetDefaultVideoSenderConfig();
+ video_config.rtp_payload_type = media::cast::RtpPayloadType::REMOTE_VIDEO;
+ video_config.codec = media::cast::CODEC_VIDEO_REMOTE;
+ receiver_ssrc_ = video_config.receiver_ssrc;
+ remoting_sender_ = std::make_unique<RemotingSender>(
+ cast_environment_, &transport_, video_config, std::move(consumer_end),
+ mojo::MakeRequest(&sender_),
+ base::BindOnce(
+ [](bool expecting_error_callback_run) {
+ CHECK(expecting_error_callback_run);
+ },
+ expecting_error_callback_run_));
+
+ // Give CastRemotingSender a small RTT measurement to prevent kickstart
+ // testing from taking too long.
+ remoting_sender_->OnMeasuredRoundTripTime(
+ base::TimeDelta::FromMilliseconds(1));
+ RunPendingTasks();
+ }
+
+ ~RemotingSenderTest() override {}
+
+ void TearDown() final {
+ remoting_sender_.reset();
+ // Allow any pending tasks to run before destruction.
+ RunPendingTasks();
+ }
+
+ // Allow pending tasks, such as Mojo method calls, to execute.
+ void RunPendingTasks() { scoped_task_environment_.RunUntilIdle(); }
+
+ protected:
+ media::cast::FrameId latest_acked_frame_id() const {
+ return remoting_sender_->latest_acked_frame_id_;
+ }
+
+ int NumberOfFramesInFlight() {
+ return remoting_sender_->GetUnacknowledgedFrameCount();
+ }
+
+ size_t GetSizeOfNextFrameData() {
+ return remoting_sender_->next_frame_data_.size();
+ }
+
+ bool IsFlowRestartPending() const {
+ return remoting_sender_->flow_restart_pending_;
+ }
+
+ bool ProduceDataChunk(size_t offset, size_t size) WARN_UNUSED_RESULT {
+ std::vector<uint8_t> fake_chunk(size);
+ for (size_t i = 0; i < size; ++i)
+ fake_chunk[i] = static_cast<uint8_t>(offset + i);
+ uint32_t num_bytes = fake_chunk.size();
+ return producer_end_->WriteData(fake_chunk.data(), &num_bytes,
+ MOJO_WRITE_DATA_FLAG_ALL_OR_NONE) ==
+ MOJO_RESULT_OK;
+ }
+
+ void PostMojoCallTask_SendFrame(uint32_t size) { sender_->SendFrame(size); }
+
+ void PostMojoCallTask_CancelInFlightData() { sender_->CancelInFlightData(); }
+
+ void TakeSentFrames(std::vector<media::cast::EncodedFrame>* frames) {
+ transport_.TakeSentFrames(frames);
+ }
+
+ bool ExpectOneFrameWasSent(size_t expected_payload_size) {
+ std::vector<media::cast::EncodedFrame> frames;
+ transport_.TakeSentFrames(&frames);
+ EXPECT_EQ(1u, frames.size());
+ if (frames.empty())
+ return false;
+ return ExpectCorrectFrameData(expected_payload_size, frames.front());
+ }
+
+ 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);
+ }
+
+ void AckOldestInFlightFrames(int count) {
+ AckUpToAndIncluding(latest_acked_frame_id() + count);
+ }
+
+ // Blocks the caller indefinitely, until a kickstart frame is sent, and then
+ // returns the FrameId of the kickstarted-frame.
+ media::cast::FrameId WaitForKickstart() {
+ return transport_.WaitForKickstart();
+ }
+
+ bool ExpectNoFramesCanceled() {
+ std::vector<media::cast::FrameId> frame_ids;
+ transport_.TakeCanceledFrameIds(&frame_ids);
+ return frame_ids.empty();
+ }
+
+ bool ExpectFramesCanceled(media::cast::FrameId first_frame_id,
+ media::cast::FrameId last_frame_id) {
+ std::vector<media::cast::FrameId> frame_ids;
+ transport_.TakeCanceledFrameIds(&frame_ids);
+ auto begin = frame_ids.begin();
+ auto end = frame_ids.end();
+ for (auto fid = first_frame_id; fid <= last_frame_id; ++fid) {
+ auto new_end = std::remove(begin, end, fid);
+ if (new_end == end)
+ return false;
+ end = new_end;
+ }
+ return begin == end;
+ }
+
+ static bool ExpectCorrectFrameData(size_t expected_payload_size,
+ const media::cast::EncodedFrame& frame) {
+ if (expected_payload_size != frame.data.size()) {
+ ADD_FAILURE() << "Expected frame data size != frame.data.size(): "
+ << expected_payload_size << " vs " << frame.data.size();
+ return false;
+ }
+ for (size_t i = 0; i < expected_payload_size; ++i) {
+ if (static_cast<uint8_t>(frame.data[i]) != static_cast<uint8_t>(i)) {
+ ADD_FAILURE() << "Frame data byte mismatch at offset " << i;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ const scoped_refptr<media::cast::CastEnvironment> cast_environment_;
+ FakeTransport transport_;
+ std::unique_ptr<RemotingSender> remoting_sender_;
+ media::mojom::RemotingDataStreamSenderPtr sender_;
+ mojo::ScopedDataPipeProducerHandle producer_end_;
+ bool expecting_error_callback_run_;
+ uint32_t receiver_ssrc_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemotingSenderTest);
+};
+
+TEST_F(RemotingSenderTest, SendsFramesViaMojoInterface) {
+ // One 256-byte chunk pushed through the data pipe to make one frame.
+ ASSERT_TRUE(ProduceDataChunk(0, 256));
+ PostMojoCallTask_SendFrame(256);
+ RunPendingTasks();
+ EXPECT_TRUE(ExpectOneFrameWasSent(256));
+ AckOldestInFlightFrames(1);
+ EXPECT_EQ(media::cast::FrameId::first(), latest_acked_frame_id());
+
+ // Four 256-byte chunks pushed through the data pipe to make one frame.
+ PostMojoCallTask_SendFrame(1024);
+ for (int i = 0; i < 4; ++i) {
+ ASSERT_TRUE(ProduceDataChunk(i * 256, 256));
+ }
+ RunPendingTasks();
+ EXPECT_TRUE(ExpectOneFrameWasSent(1024));
+ AckOldestInFlightFrames(1);
+ EXPECT_EQ(media::cast::FrameId::first() + 1, latest_acked_frame_id());
+
+ // 10 differently-sized chunks pushed through the data pipe to make one frame
+ // that is larger than the data pipe's total capacity.
+ PostMojoCallTask_SendFrame(6665);
+ size_t offset = 0;
+ for (int i = 0; i < 10; ++i) {
+ const size_t chunk_size = 500 + i * 37;
+ ASSERT_TRUE(ProduceDataChunk(offset, chunk_size));
+ RunPendingTasks();
+ offset += chunk_size;
+ }
+ RunPendingTasks();
+ EXPECT_TRUE(ExpectOneFrameWasSent(6665));
+ AckOldestInFlightFrames(1);
+ EXPECT_EQ(media::cast::FrameId::first() + 2, latest_acked_frame_id());
+}
+
+TEST_F(RemotingSenderTest, SendsMultipleFramesWithDelayedAcks) {
+ // Send 4 frames.
+ for (int i = 0; i < 4; ++i) {
+ ASSERT_TRUE(ProduceDataChunk(0, 16));
+ PostMojoCallTask_SendFrame(16);
+ }
+ RunPendingTasks();
+ EXPECT_EQ(4, NumberOfFramesInFlight());
+ EXPECT_TRUE(ExpectNoFramesCanceled());
+
+ // Ack one frame.
+ AckOldestInFlightFrames(1);
+ EXPECT_EQ(3, NumberOfFramesInFlight());
+ EXPECT_TRUE(ExpectFramesCanceled(media::cast::FrameId::first(),
+ media::cast::FrameId::first()));
+
+ // Ack all.
+ AckOldestInFlightFrames(3);
+ EXPECT_EQ(0, NumberOfFramesInFlight());
+ EXPECT_TRUE(ExpectFramesCanceled(media::cast::FrameId::first() + 1,
+ media::cast::FrameId::first() + 3));
+}
+
+TEST_F(RemotingSenderTest, KickstartsIfAckNotTimely) {
+ // Send first frame and don't Ack it. Expect the first frame to be
+ // kickstarted.
+ ASSERT_TRUE(ProduceDataChunk(0, 16));
+ PostMojoCallTask_SendFrame(16);
+ EXPECT_EQ(media::cast::FrameId::first(), WaitForKickstart());
+ EXPECT_EQ(1, NumberOfFramesInFlight());
+
+ // Send 3 more frames and don't Ack them either. Expect the 4th frame to be
+ // kickstarted.
+ for (int i = 0; i < 3; ++i) {
+ ASSERT_TRUE(ProduceDataChunk(0, 16));
+ PostMojoCallTask_SendFrame(16);
+ }
+ EXPECT_EQ(media::cast::FrameId::first() + 3, WaitForKickstart());
+ EXPECT_EQ(4, NumberOfFramesInFlight());
+
+ // Ack the first two frames and wait for another kickstart (for the 4th frame
+ // again).
+ AckOldestInFlightFrames(2);
+ EXPECT_EQ(2, NumberOfFramesInFlight());
+ EXPECT_EQ(media::cast::FrameId::first() + 3, WaitForKickstart());
+}
+
+TEST_F(RemotingSenderTest, CancelsUnsentFrame) {
+ PostMojoCallTask_SendFrame(16);
+ PostMojoCallTask_SendFrame(32);
+ PostMojoCallTask_CancelInFlightData();
+ EXPECT_EQ(0u, GetSizeOfNextFrameData());
+ ASSERT_TRUE(ProduceDataChunk(0, 16));
+ RunPendingTasks();
+ // The first frame's data should have been read from the data pipe.
+ EXPECT_EQ(16u, GetSizeOfNextFrameData());
+ EXPECT_EQ(0, NumberOfFramesInFlight());
+ ASSERT_TRUE(ProduceDataChunk(0, 32));
+ RunPendingTasks();
+ // The |next_frame_data_| was not updated because the second frame data was
+ // discarded from the data pipe.
+ EXPECT_EQ(16u, GetSizeOfNextFrameData());
+ EXPECT_EQ(0, NumberOfFramesInFlight());
+
+ // Since no frames were sent, none should have been passed to the
+ // CastTransport, and none should have been canceled.
+ std::vector<media::cast::EncodedFrame> frames;
+ TakeSentFrames(&frames);
+ EXPECT_TRUE(frames.empty());
+ EXPECT_TRUE(ExpectNoFramesCanceled());
+}
+
+// http://crbug.com/647423
+#define MAYBE_CancelsFramesInFlight DISABLED_CancelsFramesInFlight
+TEST_F(RemotingSenderTest, MAYBE_CancelsFramesInFlight) {
+ EXPECT_TRUE(IsFlowRestartPending());
+
+ // Send 10 frames.
+ for (int i = 0; i < 10; ++i) {
+ ASSERT_TRUE(ProduceDataChunk(0, 16));
+ PostMojoCallTask_SendFrame(16);
+ }
+ RunPendingTasks();
+ EXPECT_FALSE(IsFlowRestartPending());
+ EXPECT_EQ(10, NumberOfFramesInFlight());
+
+ // Ack the first frame.
+ AckOldestInFlightFrames(1);
+ EXPECT_FALSE(IsFlowRestartPending());
+ EXPECT_EQ(9, NumberOfFramesInFlight());
+ EXPECT_TRUE(ExpectFramesCanceled(media::cast::FrameId::first(),
+ media::cast::FrameId::first()));
+
+ // Cancel all in-flight data. This should cause the remaining 9 frames to be
+ // canceled.
+ PostMojoCallTask_CancelInFlightData();
+ RunPendingTasks();
+ EXPECT_TRUE(IsFlowRestartPending());
+ EXPECT_EQ(0, NumberOfFramesInFlight());
+ EXPECT_TRUE(ExpectFramesCanceled(media::cast::FrameId::first() + 1,
+ media::cast::FrameId::first() + 9));
+
+ // Send one more frame and ack it.
+ ASSERT_TRUE(ProduceDataChunk(0, 16));
+ PostMojoCallTask_SendFrame(16);
+ RunPendingTasks();
+ EXPECT_FALSE(IsFlowRestartPending());
+ EXPECT_EQ(1, NumberOfFramesInFlight());
+ AckOldestInFlightFrames(1);
+ EXPECT_EQ(0, NumberOfFramesInFlight());
+
+ // Check that the dependency metadata was set correctly to indicate a frame
+ // that immediately follows a CancelInFlightData() operation.
+ std::vector<media::cast::EncodedFrame> frames;
+ TakeSentFrames(&frames);
+ ASSERT_EQ(11u, frames.size());
+ for (size_t i = 0; i < 11; ++i) {
+ const media::cast::EncodedFrame& frame = frames[i];
+ EXPECT_EQ(media::cast::FrameId::first() + i, frame.frame_id);
+ if (i == 0 || i == 10)
+ EXPECT_EQ(media::cast::EncodedFrame::KEY, frame.dependency);
+ else
+ EXPECT_EQ(media::cast::EncodedFrame::DEPENDENT, frame.dependency);
+ }
+}
+
+TEST_F(RemotingSenderTest, WaitsForDataBeforeConsumingFromDataPipe) {
+ // Queue up and issue Mojo calls to consume three frames. Since no data has
+ // been pushed into the pipe yet no frames should be sent.
+ for (int i = 0; i < 3; ++i) {
+ PostMojoCallTask_SendFrame(4);
+ }
+ RunPendingTasks();
+ EXPECT_TRUE(IsFlowRestartPending());
+ EXPECT_EQ(0, NumberOfFramesInFlight());
+
+ // Push the data for one frame into the data pipe. This should trigger input
+ // processing and allow one frame to be sent.
+ ASSERT_TRUE(ProduceDataChunk(0, 4));
+ RunPendingTasks(); // Allow Mojo Watcher to signal CastRemotingSender.
+ EXPECT_FALSE(IsFlowRestartPending());
+ EXPECT_EQ(1, NumberOfFramesInFlight());
+
+ // Now push the data for the other two frames into the data pipe and expect
+ // two more frames to be sent.
+ ASSERT_TRUE(ProduceDataChunk(0, 4));
+ ASSERT_TRUE(ProduceDataChunk(0, 4));
+ RunPendingTasks(); // Allow Mojo Watcher to signal CastRemotingSender.
+ EXPECT_FALSE(IsFlowRestartPending());
+ EXPECT_EQ(3, NumberOfFramesInFlight());
+}
+
+TEST_F(RemotingSenderTest, WaitsForDataThenDiscardsCanceledData) {
+ // Queue up and issue Mojo calls to consume data chunks and send three
+ // frames. Since no data has been pushed into the pipe yet no frames should be
+ // sent.
+ for (int i = 0; i < 3; ++i) {
+ PostMojoCallTask_SendFrame(4);
+ }
+ RunPendingTasks();
+ EXPECT_EQ(0, NumberOfFramesInFlight());
+
+ // Cancel all in-flight data.
+ PostMojoCallTask_CancelInFlightData();
+ RunPendingTasks();
+
+ // Now, push the data for one frame into the data pipe. Because of the
+ // cancellation, no frames should be sent.
+ ASSERT_TRUE(ProduceDataChunk(0, 4));
+ RunPendingTasks(); // Allow Mojo Watcher to signal CastRemotingSender.
+ EXPECT_EQ(0, NumberOfFramesInFlight());
+
+ // Now push the data for the other two frames into the data pipe and still no
+ // frames should be sent.
+ ASSERT_TRUE(ProduceDataChunk(0, 4));
+ ASSERT_TRUE(ProduceDataChunk(0, 4));
+ RunPendingTasks(); // Allow Mojo Watcher to signal CastRemotingSender.
+ EXPECT_EQ(0, NumberOfFramesInFlight());
+
+ // Now issue calls to send another frame and then push the data for it into
+ // the data pipe. Expect to see the frame gets sent since it was provided
+ // after the CancelInFlightData().
+ PostMojoCallTask_SendFrame(4);
+ RunPendingTasks();
+ EXPECT_EQ(0, NumberOfFramesInFlight());
+ ASSERT_TRUE(ProduceDataChunk(0, 4));
+ RunPendingTasks(); // Allow Mojo Watcher to signal CastRemotingSender.
+ EXPECT_EQ(1, NumberOfFramesInFlight());
+}
+
+TEST_F(RemotingSenderTest, StopsConsumingWhileTooManyFramesAreInFlight) {
+ EXPECT_TRUE(IsFlowRestartPending());
+
+ // Send out the maximum possible number of unacked frames, but don't ack any
+ // yet.
+ for (int i = 0; i < media::cast::kMaxUnackedFrames; ++i) {
+ ASSERT_TRUE(ProduceDataChunk(0, 4));
+ PostMojoCallTask_SendFrame(4);
+ }
+ RunPendingTasks();
+ EXPECT_FALSE(IsFlowRestartPending());
+ EXPECT_EQ(media::cast::kMaxUnackedFrames, NumberOfFramesInFlight());
+ // Note: All frames should have been sent to the Transport, and so
+ // CastRemotingSender's single-frame data buffer should be empty.
+ EXPECT_EQ(0u, GetSizeOfNextFrameData());
+
+ // When the client provides one more frame, CastRemotingSender will begin
+ // queuing input operations instead of sending the the frame to the
+ // CastTransport.
+ ASSERT_TRUE(ProduceDataChunk(0, 4));
+ PostMojoCallTask_SendFrame(4);
+ RunPendingTasks();
+ EXPECT_EQ(media::cast::kMaxUnackedFrames, NumberOfFramesInFlight());
+ // Note: The unsent frame resides in CastRemotingSender's single-frame data
+ // buffer.
+ EXPECT_EQ(4u, GetSizeOfNextFrameData());
+
+ // Ack the the first frame and expect sending to resume, with one more frame
+ // being sent to the CastTransport.
+ AckOldestInFlightFrames(1);
+ EXPECT_EQ(media::cast::kMaxUnackedFrames, NumberOfFramesInFlight());
+ // Note: Only one frame was backlogged, and so CastRemotingSender's
+ // single-frame data buffer should be empty.
+ EXPECT_EQ(0u, GetSizeOfNextFrameData());
+
+ // Attempting to send another frame will once again cause CastRemotingSender
+ // to queue input operations.
+ ASSERT_TRUE(ProduceDataChunk(0, 4));
+ PostMojoCallTask_SendFrame(4);
+ RunPendingTasks();
+ EXPECT_EQ(media::cast::kMaxUnackedFrames, NumberOfFramesInFlight());
+ // Note: Once again, CastRemotingSender's single-frame data buffer contains an
+ // unsent frame.
+ EXPECT_EQ(4u, GetSizeOfNextFrameData());
+
+ // Send more frames: Some number of frames will queue-up inside the Mojo data
+ // pipe (the exact number depends on the data pipe's capacity, and how Mojo
+ // manages memory internally). At some point, attempting to produce and push
+ // another frame will fail because the data pipe is full.
+ int num_frames_in_data_pipe = 0;
+ while (ProduceDataChunk(0, 768)) {
+ ++num_frames_in_data_pipe;
+ PostMojoCallTask_SendFrame(768);
+ RunPendingTasks();
+ EXPECT_EQ(media::cast::kMaxUnackedFrames, NumberOfFramesInFlight());
+ // Note: CastRemotingSender's single-frame data buffer should still contain
+ // the unsent 4-byte frame.
+ EXPECT_EQ(4u, GetSizeOfNextFrameData());
+ }
+ EXPECT_LT(0, num_frames_in_data_pipe);
+
+ // Ack one frame at a time until the backlog in the Mojo data pipe has
+ // cleared.
+ int remaining_frames_in_data_pipe = num_frames_in_data_pipe;
+ while (remaining_frames_in_data_pipe > 0) {
+ AckOldestInFlightFrames(1);
+ RunPendingTasks();
+ --remaining_frames_in_data_pipe;
+ EXPECT_EQ(media::cast::kMaxUnackedFrames, NumberOfFramesInFlight());
+ EXPECT_EQ(768u, GetSizeOfNextFrameData());
+ }
+
+ // Ack one more frame. There should no longer be a backlog on the input side
+ // of things.
+ AckOldestInFlightFrames(1);
+ RunPendingTasks(); // No additional Mojo method calls should be made here.
+ EXPECT_EQ(media::cast::kMaxUnackedFrames, NumberOfFramesInFlight());
+ // The single-frame data buffer should be empty to indicate no input backlog.
+ EXPECT_EQ(0u, GetSizeOfNextFrameData());
+
+ // Ack all but one frame.
+ AckOldestInFlightFrames(NumberOfFramesInFlight() - 1);
+ EXPECT_EQ(1, NumberOfFramesInFlight());
+ // ..and one more frame can be sent immediately.
+ ASSERT_TRUE(ProduceDataChunk(0, 4));
+ PostMojoCallTask_SendFrame(4);
+ RunPendingTasks();
+ EXPECT_EQ(2, NumberOfFramesInFlight());
+ // ...and ack these last two frames.
+ AckOldestInFlightFrames(2);
+ EXPECT_EQ(0, NumberOfFramesInFlight());
+
+ // Finally, examine all frames that were sent to the CastTransport, and
+ // confirm their metadata and data is valid.
+ std::vector<media::cast::EncodedFrame> frames;
+ TakeSentFrames(&frames);
+ const size_t total_frames_sent =
+ media::cast::kMaxUnackedFrames + 2 + num_frames_in_data_pipe + 1;
+ ASSERT_EQ(total_frames_sent, frames.size());
+ media::cast::RtpTimeTicks last_rtp_timestamp =
+ media::cast::RtpTimeTicks() - media::cast::RtpTimeDelta::FromTicks(1);
+ for (size_t i = 0; i < total_frames_sent; ++i) {
+ const media::cast::EncodedFrame& frame = frames[i];
+ EXPECT_EQ(media::cast::FrameId::first() + i, frame.frame_id);
+ if (i == 0) {
+ EXPECT_EQ(media::cast::EncodedFrame::KEY, frame.dependency);
+ EXPECT_EQ(media::cast::FrameId::first() + i, frame.referenced_frame_id);
+ } else {
+ EXPECT_EQ(media::cast::EncodedFrame::DEPENDENT, frame.dependency);
+ EXPECT_EQ(media::cast::FrameId::first() + i - 1,
+ frame.referenced_frame_id);
+ }
+
+ // RTP timestamp must be monotonically increasing.
+ EXPECT_GT(frame.rtp_timestamp, last_rtp_timestamp);
+ last_rtp_timestamp = frame.rtp_timestamp;
+
+ size_t expected_frame_size = 4;
+ if ((i >= media::cast::kMaxUnackedFrames + 2u) &&
+ (i < media::cast::kMaxUnackedFrames + 2u + num_frames_in_data_pipe)) {
+ expected_frame_size = 768;
+ }
+ EXPECT_TRUE(ExpectCorrectFrameData(expected_frame_size, frame));
+ }
+}
+
+} // namespace mirroring
diff --git a/chromium/components/mirroring/service/rtp_stream.cc b/chromium/components/mirroring/service/rtp_stream.cc
index d9a95d39b28..b3ca6b5d754 100644
--- a/chromium/components/mirroring/service/rtp_stream.cc
+++ b/chromium/components/mirroring/service/rtp_stream.cc
@@ -37,14 +37,13 @@ VideoRtpStream::VideoRtpStream(
: video_sender_(std::move(video_sender)),
client_(client),
consecutive_refresh_count_(0),
- expecting_a_refresh_frame_(false),
- weak_factory_(this) {
+ expecting_a_refresh_frame_(false) {
DCHECK(video_sender_);
DCHECK(client);
refresh_timer_.Start(FROM_HERE, kRefreshInterval,
base::BindRepeating(&VideoRtpStream::OnRefreshTimerFired,
- weak_factory_.GetWeakPtr()));
+ this->AsWeakPtr()));
}
VideoRtpStream::~VideoRtpStream() {}
@@ -112,7 +111,7 @@ AudioRtpStream::AudioRtpStream(
AudioRtpStream::~AudioRtpStream() {}
void AudioRtpStream::InsertAudio(std::unique_ptr<media::AudioBus> audio_bus,
- base::TimeTicks capture_time) {
+ const base::TimeTicks& capture_time) {
audio_sender_->InsertAudio(std::move(audio_bus), capture_time);
}
diff --git a/chromium/components/mirroring/service/rtp_stream.h b/chromium/components/mirroring/service/rtp_stream.h
index 9bffae1f60d..dbbf6673864 100644
--- a/chromium/components/mirroring/service/rtp_stream.h
+++ b/chromium/components/mirroring/service/rtp_stream.h
@@ -61,7 +61,7 @@ class RtpStreamClient {
// regular intervals for a short period of time. This provides the video
// encoder, downstream, several copies of the last frame so that it may clear up
// lossy encoding artifacts.
-class VideoRtpStream {
+class VideoRtpStream : public base::SupportsWeakPtr<VideoRtpStream> {
public:
VideoRtpStream(std::unique_ptr<media::cast::VideoSender> video_sender,
base::WeakPtr<RtpStreamClient> client);
@@ -71,10 +71,6 @@ class VideoRtpStream {
// |video_frame| is required to provide REFERENCE_TIME in the metadata.
void InsertVideoFrame(scoped_refptr<media::VideoFrame> video_frame);
- base::WeakPtr<VideoRtpStream> AsWeakPtr() {
- return weak_factory_.GetWeakPtr();
- }
-
void SetTargetPlayoutDelay(base::TimeDelta playout_delay);
private:
@@ -94,15 +90,11 @@ class VideoRtpStream {
// cleared once the next frame is received.
bool expecting_a_refresh_frame_;
- base::WeakPtrFactory<VideoRtpStream> weak_factory_;
-
DISALLOW_COPY_AND_ASSIGN(VideoRtpStream);
};
// Receives audio data and submits the data to media::cast::AudioSender.
-// TODO(xjz): Complete implementation after Audio Service mirroring refactoring
-// is landed.
-class AudioRtpStream {
+class AudioRtpStream : public base::SupportsWeakPtr<AudioRtpStream> {
public:
AudioRtpStream(std::unique_ptr<media::cast::AudioSender> audio_sender,
base::WeakPtr<RtpStreamClient> client);
@@ -110,7 +102,7 @@ class AudioRtpStream {
// Called by AudioCaptureClient when new audio data is available.
void InsertAudio(std::unique_ptr<media::AudioBus> audio_bus,
- base::TimeTicks estimated_capture_time);
+ const base::TimeTicks& estimated_capture_time);
void SetTargetPlayoutDelay(base::TimeDelta playout_delay);
diff --git a/chromium/components/mirroring/service/session.cc b/chromium/components/mirroring/service/session.cc
index 58047f8db81..f99169ae07b 100644
--- a/chromium/components/mirroring/service/session.cc
+++ b/chromium/components/mirroring/service/session.cc
@@ -4,6 +4,12 @@
#include "components/mirroring/service/session.h"
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/rand_util.h"
@@ -17,9 +23,13 @@
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
+#include "components/mirroring/service/captured_audio_input.h"
#include "components/mirroring/service/udp_socket_client.h"
#include "components/mirroring/service/video_capture_client.h"
#include "crypto/random.h"
+#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/net/cast_transport.h"
#include "media/cast/sender/audio_sender.h"
#include "media/cast/sender/video_sender.h"
@@ -36,6 +46,8 @@ using media::cast::FrameEvent;
using media::cast::PacketEvent;
using media::cast::OperationalStatus;
using media::cast::Packet;
+using media::mojom::RemotingSinkAudioCapability;
+using media::mojom::RemotingSinkVideoCapability;
namespace mirroring {
@@ -50,6 +62,11 @@ constexpr base::TimeDelta kSendEventsInterval = base::TimeDelta::FromSeconds(1);
constexpr base::TimeDelta kOfferAnswerExchangeTimeout =
base::TimeDelta::FromSeconds(15);
+// Amount of time to wait before assuming the Cast Receiver does not support
+// querying for capabilities via GET_CAPABILITIES.
+constexpr base::TimeDelta kGetCapabilitiesTimeout =
+ base::TimeDelta::FromSeconds(30);
+
// Used for OFFER/ANSWER message exchange. Some receivers will error out on
// payloadType values other than the ones hard-coded here.
constexpr int kAudioPayloadType = 127;
@@ -167,10 +184,10 @@ void AddStreamObject(int stream_index,
(config.rtp_payload_type <= media::cast::RtpPayloadType::AUDIO_LAST);
stream.SetKey("rtpPayloadType",
base::Value(is_audio ? kAudioPayloadType : kVideoPayloadType));
- stream.SetKey("ssrc", base::Value(int(config.sender_ssrc)));
- stream.SetKey(
- "targetDelay",
- base::Value(int(config.animated_playout_delay.InMilliseconds())));
+ stream.SetKey("ssrc", base::Value(static_cast<int>(config.sender_ssrc)));
+ stream.SetKey("targetDelay",
+ base::Value(static_cast<int>(
+ config.animated_playout_delay.InMilliseconds())));
stream.SetKey("aesKey", base::Value(base::HexEncode(config.aes_key.data(),
config.aes_key.size())));
stream.SetKey("aesIvMask",
@@ -215,8 +232,137 @@ void AddStreamObject(int stream_index,
stream_list->emplace_back(std::move(stream));
}
+// Checks whether receiver's build version is less than "1.|base_version|.xxxx".
+// Returns false if given version doesn't have the format of "1.xx.xxxx".
+bool NeedsWorkaroundForOlder1DotXVersions(
+ const std::string& receiver_build_version,
+ int base_version) {
+ if (!base::StartsWith(receiver_build_version, "1.",
+ base::CompareCase::SENSITIVE))
+ return false;
+ const size_t end_pos = receiver_build_version.find_first_of('.', 2);
+ if (end_pos == std::string::npos)
+ return false;
+ int version = 0;
+ return (base::StringToInt(receiver_build_version.substr(2, end_pos - 2),
+ &version) &&
+ version < base_version);
+}
+
+// Convert the sink capabilities to media::mojom::RemotingSinkMetadata.
+media::mojom::RemotingSinkMetadata ToRemotingSinkMetadata(
+ const std::vector<std::string>& capabilities,
+ const CastSinkInfo& sink_info,
+ const std::string& receiver_build_version) {
+ media::mojom::RemotingSinkMetadata sink_metadata;
+ sink_metadata.friendly_name = sink_info.friendly_name;
+
+ for (const auto& capability : capabilities) {
+ if (capability == "audio") {
+ sink_metadata.audio_capabilities.push_back(
+ RemotingSinkAudioCapability::CODEC_BASELINE_SET);
+ } else if (capability == "aac") {
+ sink_metadata.audio_capabilities.push_back(
+ RemotingSinkAudioCapability::CODEC_AAC);
+ } else if (capability == "opus") {
+ sink_metadata.audio_capabilities.push_back(
+ RemotingSinkAudioCapability::CODEC_OPUS);
+ } else if (capability == "video") {
+ sink_metadata.video_capabilities.push_back(
+ RemotingSinkVideoCapability::CODEC_BASELINE_SET);
+ } else if (capability == "4k") {
+ sink_metadata.video_capabilities.push_back(
+ RemotingSinkVideoCapability::SUPPORT_4K);
+ } else if (capability == "h264") {
+ sink_metadata.video_capabilities.push_back(
+ RemotingSinkVideoCapability::CODEC_H264);
+ } else if (capability == "vp8") {
+ sink_metadata.video_capabilities.push_back(
+ RemotingSinkVideoCapability::CODEC_VP8);
+ } else if (capability == "vp9") {
+ // Before 1.27 Earth receivers report "vp9" even though they don't support
+ // remoting the VP9 encoded video.
+ if (!NeedsWorkaroundForOlder1DotXVersions(receiver_build_version, 27) ||
+ base::StartsWith(sink_info.model_name, "Chromecast Ultra",
+ base::CompareCase::SENSITIVE)) {
+ sink_metadata.video_capabilities.push_back(
+ RemotingSinkVideoCapability::CODEC_VP9);
+ }
+ } else if (capability == "hevc") {
+ // Before 1.27 Earth receivers report "hevc" even though they don't
+ // support remoting the HEVC encoded video.
+ if (!NeedsWorkaroundForOlder1DotXVersions(receiver_build_version, 27) ||
+ base::StartsWith(sink_info.model_name, "Chromecast Ultra",
+ base::CompareCase::SENSITIVE)) {
+ sink_metadata.video_capabilities.push_back(
+ RemotingSinkVideoCapability::CODEC_HEVC);
+ }
+ } else {
+ DVLOG(1) << "Unknown mediaCap name: " << capability;
+ }
+ }
+
+ // Enable remoting 1080p 30fps or higher resolution/fps content for Chromecast
+ // Ultra receivers only.
+ // TODO(xjz): Receiver should report this capability.
+ if (sink_info.model_name == "Chromecast Ultra") {
+ sink_metadata.video_capabilities.push_back(
+ RemotingSinkVideoCapability::SUPPORT_4K);
+ }
+
+ return sink_metadata;
+}
+
} // namespace
+class Session::AudioCapturingCallback final
+ : public media::AudioCapturerSource::CaptureCallback {
+ public:
+ using AudioDataCallback =
+ base::RepeatingCallback<void(std::unique_ptr<media::AudioBus> audio_bus,
+ const base::TimeTicks& recorded_time)>;
+ AudioCapturingCallback(AudioDataCallback audio_data_callback,
+ base::OnceClosure error_callback)
+ : audio_data_callback_(std::move(audio_data_callback)),
+ error_callback_(std::move(error_callback)) {
+ DCHECK(!audio_data_callback_.is_null());
+ }
+
+ ~AudioCapturingCallback() override {}
+
+ private:
+ // media::AudioCapturerSource::CaptureCallback implementation.
+ void OnCaptureStarted() override {}
+
+ // Called on audio thread.
+ void Capture(const media::AudioBus* audio_bus,
+ int audio_delay_milliseconds,
+ double volume,
+ bool key_pressed) override {
+ // TODO(xjz): Don't copy the audio data. Instead, send |audio_bus| directly
+ // to the encoder.
+ std::unique_ptr<media::AudioBus> captured_audio =
+ media::AudioBus::Create(audio_bus->channels(), audio_bus->frames());
+ audio_bus->CopyTo(captured_audio.get());
+ const base::TimeTicks recorded_time =
+ base::TimeTicks::Now() -
+ base::TimeDelta::FromMilliseconds(audio_delay_milliseconds);
+ audio_data_callback_.Run(std::move(captured_audio), recorded_time);
+ }
+
+ void OnCaptureError(const std::string& message) override {
+ if (!error_callback_.is_null())
+ std::move(error_callback_).Run();
+ }
+
+ void OnCaptureMuted(bool is_muted) override {}
+
+ const AudioDataCallback audio_data_callback_;
+ base::OnceClosure error_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioCapturingCallback);
+};
+
Session::Session(int32_t session_id,
const CastSinkInfo& sink_info,
const gfx::Size& max_resolution,
@@ -225,6 +371,7 @@ Session::Session(int32_t session_id,
CastMessageChannel* outbound_channel)
: session_id_(session_id),
sink_info_(sink_info),
+ state_(MIRRORING),
observer_(observer),
resource_provider_(resource_provider),
message_dispatcher_(outbound_channel,
@@ -236,13 +383,13 @@ Session::Session(int32_t session_id,
max_resolution.height());
resource_provider_->GetNetworkContext(mojo::MakeRequest(&network_context_));
- auto wifi_status_monitor =
- std::make_unique<WifiStatusMonitor>(session_id_, &message_dispatcher_);
+ network::mojom::URLLoaderFactoryParamsPtr params =
+ network::mojom::URLLoaderFactoryParams::New();
+ params->process_id = network::mojom::kBrowserProcessId;
+ params->is_corb_enabled = false;
network::mojom::URLLoaderFactoryPtr url_loader_factory;
network_context_->CreateURLLoaderFactory(
- mojo::MakeRequest(&url_loader_factory),
- network::mojom::URLLoaderFactoryParams::New(
- network::mojom::kBrowserProcessId, false, std::string()));
+ mojo::MakeRequest(&url_loader_factory), std::move(params));
// Generate session level tags.
base::Value session_tags(base::Value::Type::DICTIONARY);
@@ -254,9 +401,9 @@ Session::Session(int32_t session_id,
session_tags.SetKey("receiverProductName",
base::Value(sink_info_.model_name));
- session_monitor_.emplace(
- kMaxCrashReportBytes, sink_info_.ip_address, std::move(session_tags),
- std::move(url_loader_factory), std::move(wifi_status_monitor));
+ session_monitor_.emplace(kMaxCrashReportBytes, sink_info_.ip_address,
+ std::move(session_tags),
+ std::move(url_loader_factory));
CreateAndSendOffer();
}
@@ -268,26 +415,48 @@ Session::~Session() {
void Session::ReportError(SessionError error) {
if (session_monitor_.has_value())
session_monitor_->OnStreamingError(error);
+ if (state_ == REMOTING) {
+ media_remoter_->OnRemotingFailed(); // Try to fallback on mirroring.
+ return;
+ }
+
+ // Report the error and stop this session.
if (observer_)
observer_->OnError(error);
StopSession();
}
+void Session::StopStreaming() {
+ DVLOG(2) << __func__ << " state=" << state_;
+ if (!cast_environment_)
+ return;
+
+ session_monitor_->StopStreamingSession();
+ if (audio_input_device_) {
+ audio_input_device_->Stop();
+ audio_input_device_ = nullptr;
+ }
+ audio_capturing_callback_.reset();
+ audio_stream_.reset();
+ video_stream_.reset();
+ cast_transport_.reset();
+ cast_environment_ = nullptr;
+}
+
void Session::StopSession() {
DVLOG(1) << __func__;
- if (!resource_provider_)
+ if (state_ == STOPPED)
return;
- session_monitor_->StopStreamingSession();
+ state_ = STOPPED;
+ StopStreaming();
+
session_monitor_.reset();
weak_factory_.InvalidateWeakPtrs();
audio_encode_thread_ = nullptr;
video_encode_thread_ = nullptr;
video_capture_client_.reset();
- audio_stream_.reset();
- video_stream_.reset();
- cast_transport_.reset();
- cast_environment_ = nullptr;
+ media_remoter_.reset();
resource_provider_ = nullptr;
if (observer_) {
observer_->DidStop();
@@ -383,10 +552,12 @@ void Session::OnLoggingEventsReceived(
std::move(packet_events));
}
-void Session::OnAnswer(const std::string& cast_mode,
- const std::vector<FrameSenderConfig>& audio_configs,
+void Session::OnAnswer(const std::vector<FrameSenderConfig>& audio_configs,
const std::vector<FrameSenderConfig>& video_configs,
const ReceiverResponse& response) {
+ if (state_ == STOPPED)
+ return;
+
if (!response.answer || response.type == ResponseType::UNKNOWN) {
ReportError(ANSWER_TIME_OUT);
return;
@@ -400,6 +571,8 @@ void Session::OnAnswer(const std::string& cast_mode,
}
const Answer& answer = *response.answer;
+ const std::string cast_mode =
+ (state_ == MIRRORING ? "mirroring" : "remoting");
if (answer.cast_mode != cast_mode) {
ReportError(ANSWER_MISMATCHED_CAST_MODE);
return;
@@ -450,23 +623,19 @@ void Session::OnAnswer(const std::string& cast_mode,
return;
}
- if ((has_audio &&
- audio_config.rtp_payload_type == RtpPayloadType::REMOTE_AUDIO) ||
- (has_video &&
- video_config.rtp_payload_type == RtpPayloadType::REMOTE_VIDEO)) {
- NOTIMPLEMENTED(); // TODO(xjz): Add support for media remoting.
- return;
- }
-
// Start streaming.
- audio_encode_thread_ = base::CreateSingleThreadTaskRunnerWithTraits(
- {base::TaskPriority::USER_BLOCKING,
- base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
- base::SingleThreadTaskRunnerThreadMode::DEDICATED);
- video_encode_thread_ = base::CreateSingleThreadTaskRunnerWithTraits(
- {base::TaskPriority::USER_BLOCKING,
- base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
- base::SingleThreadTaskRunnerThreadMode::DEDICATED);
+ const bool initially_starting_session =
+ !audio_encode_thread_ && !video_encode_thread_;
+ if (initially_starting_session) {
+ audio_encode_thread_ = base::CreateSingleThreadTaskRunnerWithTraits(
+ {base::TaskPriority::USER_BLOCKING,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+ base::SingleThreadTaskRunnerThreadMode::DEDICATED);
+ video_encode_thread_ = base::CreateSingleThreadTaskRunnerWithTraits(
+ {base::TaskPriority::USER_BLOCKING,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+ base::SingleThreadTaskRunnerThreadMode::DEDICATED);
+ }
cast_environment_ = new media::cast::CastEnvironment(
base::DefaultTickClock::GetInstance(),
base::ThreadTaskRunnerHandle::Get(), audio_encode_thread_,
@@ -481,51 +650,98 @@ void Session::OnAnswer(const std::string& cast_mode,
std::make_unique<TransportClient>(this), std::move(udp_client),
base::ThreadTaskRunnerHandle::Get());
- if (has_audio) {
- auto audio_sender = std::make_unique<media::cast::AudioSender>(
- cast_environment_, audio_config,
- base::BindRepeating(&Session::OnEncoderStatusChange,
- weak_factory_.GetWeakPtr()),
- cast_transport_.get());
- audio_stream_ = std::make_unique<AudioRtpStream>(
- std::move(audio_sender), weak_factory_.GetWeakPtr());
- // TODO(xjz): Start audio capturing.
- NOTIMPLEMENTED();
- }
+ if (state_ == REMOTING) {
+ DCHECK(media_remoter_);
+ DCHECK(audio_config.rtp_payload_type == RtpPayloadType::REMOTE_AUDIO ||
+ video_config.rtp_payload_type == RtpPayloadType::REMOTE_VIDEO);
+ media_remoter_->StartRpcMessaging(cast_environment_, cast_transport_.get(),
+ audio_config, video_config);
+ } else /* MIRRORING */ {
+ if (has_audio) {
+ auto audio_sender = std::make_unique<media::cast::AudioSender>(
+ cast_environment_, audio_config,
+ base::BindRepeating(&Session::OnEncoderStatusChange,
+ weak_factory_.GetWeakPtr()),
+ cast_transport_.get());
+ audio_stream_ = std::make_unique<AudioRtpStream>(
+ std::move(audio_sender), weak_factory_.GetWeakPtr());
+ DCHECK(!audio_capturing_callback_);
+ // TODO(xjz): Elliminate the thread hops. The audio data is thread-hopped
+ // from the audio thread, and later thread-hopped again to the encoding
+ // thread.
+ audio_capturing_callback_ = std::make_unique<AudioCapturingCallback>(
+ media::BindToCurrentLoop(base::BindRepeating(
+ &AudioRtpStream::InsertAudio, audio_stream_->AsWeakPtr())),
+ base::BindOnce(&Session::ReportError, weak_factory_.GetWeakPtr(),
+ SessionError::AUDIO_CAPTURE_ERROR));
+ audio_input_device_ = new media::AudioInputDevice(
+ std::make_unique<CapturedAudioInput>(base::BindRepeating(
+ &Session::CreateAudioStream, base::Unretained(this))),
+ base::ThreadPriority::NORMAL);
+ audio_input_device_->Initialize(mirror_settings_.GetAudioCaptureParams(),
+ audio_capturing_callback_.get());
+ audio_input_device_->Start();
+ }
- if (has_video) {
- auto video_sender = std::make_unique<media::cast::VideoSender>(
- cast_environment_, video_config,
- base::BindRepeating(&Session::OnEncoderStatusChange,
- weak_factory_.GetWeakPtr()),
- base::BindRepeating(&Session::CreateVideoEncodeAccelerator,
- weak_factory_.GetWeakPtr()),
- base::BindRepeating(&Session::CreateVideoEncodeMemory,
- weak_factory_.GetWeakPtr()),
- cast_transport_.get(),
- base::BindRepeating(&Session::SetTargetPlayoutDelay,
- weak_factory_.GetWeakPtr()));
- video_stream_ = std::make_unique<VideoRtpStream>(
- std::move(video_sender), weak_factory_.GetWeakPtr());
- media::mojom::VideoCaptureHostPtr video_host;
- resource_provider_->GetVideoCaptureHost(mojo::MakeRequest(&video_host));
- video_capture_client_ = std::make_unique<VideoCaptureClient>(
- mirror_settings_.GetVideoCaptureParams(), std::move(video_host));
- video_capture_client_->Start(
- base::BindRepeating(&VideoRtpStream::InsertVideoFrame,
- video_stream_->AsWeakPtr()),
- base::BindOnce(&Session::ReportError, weak_factory_.GetWeakPtr(),
- SessionError::VIDEO_CAPTURE_ERROR));
+ if (has_video) {
+ auto video_sender = std::make_unique<media::cast::VideoSender>(
+ cast_environment_, video_config,
+ base::BindRepeating(&Session::OnEncoderStatusChange,
+ weak_factory_.GetWeakPtr()),
+ base::BindRepeating(&Session::CreateVideoEncodeAccelerator,
+ weak_factory_.GetWeakPtr()),
+ base::BindRepeating(&Session::CreateVideoEncodeMemory,
+ weak_factory_.GetWeakPtr()),
+ cast_transport_.get(),
+ base::BindRepeating(&Session::SetTargetPlayoutDelay,
+ weak_factory_.GetWeakPtr()));
+ video_stream_ = std::make_unique<VideoRtpStream>(
+ std::move(video_sender), weak_factory_.GetWeakPtr());
+ if (!video_capture_client_) {
+ media::mojom::VideoCaptureHostPtr video_host;
+ resource_provider_->GetVideoCaptureHost(mojo::MakeRequest(&video_host));
+ video_capture_client_ = std::make_unique<VideoCaptureClient>(
+ mirror_settings_.GetVideoCaptureParams(), std::move(video_host));
+ video_capture_client_->Start(
+ base::BindRepeating(&VideoRtpStream::InsertVideoFrame,
+ video_stream_->AsWeakPtr()),
+ base::BindOnce(&Session::ReportError, weak_factory_.GetWeakPtr(),
+ SessionError::VIDEO_CAPTURE_ERROR));
+ } else {
+ video_capture_client_->Resume(base::BindRepeating(
+ &VideoRtpStream::InsertVideoFrame, video_stream_->AsWeakPtr()));
+ }
+ }
+ if (media_remoter_)
+ media_remoter_->OnMirroringResumed();
}
+ DCHECK(session_monitor_.has_value());
const SessionMonitor::SessionType session_type =
(has_audio && has_video)
? SessionMonitor::AUDIO_AND_VIDEO
: has_audio ? SessionMonitor::AUDIO_ONLY : SessionMonitor::VIDEO_ONLY;
- session_monitor_->StartStreamingSession(cast_environment_, session_type,
- false /* is_remoting */);
+ std::unique_ptr<WifiStatusMonitor> wifi_status_monitor;
+ if (answer.supports_get_status) {
+ wifi_status_monitor =
+ std::make_unique<WifiStatusMonitor>(session_id_, &message_dispatcher_);
+ // Before 1.28 Android TV Chromecast receivers respond to GET_CAPABILITIES
+ // even though they don't support remoting.
+ if (initially_starting_session &&
+ (!NeedsWorkaroundForOlder1DotXVersions(
+ session_monitor_->GetReceiverBuildVersion(), 28) ||
+ base::StartsWith(sink_info_.model_name, "Chromecast",
+ base::CompareCase::SENSITIVE) ||
+ base::StartsWith(sink_info_.model_name, "Eureka Dongle",
+ base::CompareCase::SENSITIVE))) {
+ QueryCapabilitiesForRemoting();
+ }
+ }
+ session_monitor_->StartStreamingSession(cast_environment_,
+ std::move(wifi_status_monitor),
+ session_type, state_ == REMOTING);
- if (observer_)
+ if (initially_starting_session && observer_)
observer_->DidStart();
}
@@ -533,6 +749,12 @@ void Session::OnResponseParsingError(const std::string& error_message) {
// TODO(xjz): Log the |error_message| in the mirroring logs.
}
+void Session::CreateAudioStream(AudioStreamCreatorClient* client,
+ const media::AudioParameters& params,
+ uint32_t shared_memory_count) {
+ resource_provider_->CreateAudioStream(client, params, shared_memory_count);
+}
+
void Session::SetTargetPlayoutDelay(base::TimeDelta playout_delay) {
if (audio_stream_)
audio_stream_->SetTargetPlayoutDelay(playout_delay);
@@ -541,6 +763,8 @@ void Session::SetTargetPlayoutDelay(base::TimeDelta playout_delay) {
}
void Session::CreateAndSendOffer() {
+ DCHECK(state_ != STOPPED);
+
// The random AES key and initialization vector pair used by all streams in
// this session.
const std::string aes_key = MakeRandomString(16); // AES-128.
@@ -552,46 +776,62 @@ void Session::CreateAndSendOffer() {
base::Value::ListStorage stream_list;
int stream_index = 0;
if (sink_info_.capability != DeviceCapability::VIDEO_ONLY) {
- FrameSenderConfig config = MirrorSettings::GetDefaultAudioConfig(
- RtpPayloadType::AUDIO_OPUS, Codec::CODEC_AUDIO_OPUS);
- AddSenderConfig(base::RandInt(kAudioSsrcMin, kAudioSsrcMax), config,
- aes_key, aes_iv, &audio_configs);
- AddStreamObject(stream_index++, "OPUS", audio_configs.back(),
- mirror_settings_, &stream_list);
- }
- if (sink_info_.capability != DeviceCapability::AUDIO_ONLY) {
- const int32_t video_ssrc = base::RandInt(kVideoSsrcMin, kVideoSsrcMax);
- if (IsHardwareVP8EncodingSupported(GetSupportedVeaProfiles())) {
- FrameSenderConfig config = MirrorSettings::GetDefaultVideoConfig(
- RtpPayloadType::VIDEO_VP8, Codec::CODEC_VIDEO_VP8);
- config.use_external_encoder = true;
- AddSenderConfig(video_ssrc, config, aes_key, aes_iv, &video_configs);
- AddStreamObject(stream_index++, "VP8", video_configs.back(),
+ const int32_t audio_ssrc = base::RandInt(kAudioSsrcMin, kAudioSsrcMax);
+ if (state_ == MIRRORING) {
+ FrameSenderConfig config = MirrorSettings::GetDefaultAudioConfig(
+ RtpPayloadType::AUDIO_OPUS, Codec::CODEC_AUDIO_OPUS);
+ AddSenderConfig(audio_ssrc, config, aes_key, aes_iv, &audio_configs);
+ AddStreamObject(stream_index++, "OPUS", audio_configs.back(),
mirror_settings_, &stream_list);
- }
- if (IsHardwareH264EncodingSupported(GetSupportedVeaProfiles())) {
- FrameSenderConfig config = MirrorSettings::GetDefaultVideoConfig(
- RtpPayloadType::VIDEO_H264, Codec::CODEC_VIDEO_H264);
- config.use_external_encoder = true;
- AddSenderConfig(video_ssrc, config, aes_key, aes_iv, &video_configs);
- AddStreamObject(stream_index++, "H264", video_configs.back(),
+ } else /* REMOTING */ {
+ FrameSenderConfig config = MirrorSettings::GetDefaultAudioConfig(
+ RtpPayloadType::REMOTE_AUDIO, Codec::CODEC_AUDIO_REMOTE);
+ AddSenderConfig(audio_ssrc, config, aes_key, aes_iv, &audio_configs);
+ AddStreamObject(stream_index++, "REMOTE_AUDIO", audio_configs.back(),
mirror_settings_, &stream_list);
}
- if (video_configs.empty()) {
+ }
+ if (sink_info_.capability != DeviceCapability::AUDIO_ONLY) {
+ const int32_t video_ssrc = base::RandInt(kVideoSsrcMin, kVideoSsrcMax);
+ if (state_ == MIRRORING) {
+ if (IsHardwareVP8EncodingSupported(GetSupportedVeaProfiles())) {
+ FrameSenderConfig config = MirrorSettings::GetDefaultVideoConfig(
+ RtpPayloadType::VIDEO_VP8, Codec::CODEC_VIDEO_VP8);
+ config.use_external_encoder = true;
+ AddSenderConfig(video_ssrc, config, aes_key, aes_iv, &video_configs);
+ AddStreamObject(stream_index++, "VP8", video_configs.back(),
+ mirror_settings_, &stream_list);
+ }
+ if (IsHardwareH264EncodingSupported(GetSupportedVeaProfiles())) {
+ FrameSenderConfig config = MirrorSettings::GetDefaultVideoConfig(
+ RtpPayloadType::VIDEO_H264, Codec::CODEC_VIDEO_H264);
+ config.use_external_encoder = true;
+ AddSenderConfig(video_ssrc, config, aes_key, aes_iv, &video_configs);
+ AddStreamObject(stream_index++, "H264", video_configs.back(),
+ mirror_settings_, &stream_list);
+ }
+ if (video_configs.empty()) {
+ FrameSenderConfig config = MirrorSettings::GetDefaultVideoConfig(
+ RtpPayloadType::VIDEO_VP8, Codec::CODEC_VIDEO_VP8);
+ AddSenderConfig(video_ssrc, config, aes_key, aes_iv, &video_configs);
+ AddStreamObject(stream_index++, "VP8", video_configs.back(),
+ mirror_settings_, &stream_list);
+ }
+ } else /* REMOTING */ {
FrameSenderConfig config = MirrorSettings::GetDefaultVideoConfig(
- RtpPayloadType::VIDEO_VP8, Codec::CODEC_VIDEO_VP8);
+ RtpPayloadType::REMOTE_VIDEO, Codec::CODEC_VIDEO_REMOTE);
AddSenderConfig(video_ssrc, config, aes_key, aes_iv, &video_configs);
- AddStreamObject(stream_index++, "VP8", video_configs.back(),
+ AddStreamObject(stream_index++, "REMOTE_VIDEO", video_configs.back(),
mirror_settings_, &stream_list);
}
}
DCHECK(!audio_configs.empty() || !video_configs.empty());
// Assemble the OFFER message.
- const std::string cast_mode = "mirroring";
base::Value offer(base::Value::Type::DICTIONARY);
- offer.SetKey("castMode", base::Value(cast_mode));
- offer.SetKey("receiverGetStatus", base::Value("true"));
+ offer.SetKey("castMode",
+ base::Value(state_ == MIRRORING ? "mirroring" : "remoting"));
+ offer.SetKey("receiverGetStatus", base::Value(true));
offer.SetKey("supportedStreams", base::Value(stream_list));
const int32_t sequence_number = message_dispatcher_.GetNextSeqNumber();
@@ -610,8 +850,75 @@ void Session::CreateAndSendOffer() {
message_dispatcher_.RequestReply(
message_to_receiver, ResponseType::ANSWER, sequence_number,
kOfferAnswerExchangeTimeout,
- base::BindOnce(&Session::OnAnswer, base::Unretained(this), cast_mode,
- audio_configs, video_configs));
+ base::BindOnce(&Session::OnAnswer, base::Unretained(this), audio_configs,
+ video_configs));
+}
+
+void Session::ConnectToRemotingSource(
+ media::mojom::RemoterPtr remoter,
+ media::mojom::RemotingSourceRequest request) {
+ resource_provider_->ConnectToRemotingSource(std::move(remoter),
+ std::move(request));
+}
+
+void Session::RequestRemotingStreaming() {
+ DCHECK(media_remoter_);
+ DCHECK_EQ(MIRRORING, state_);
+ if (video_capture_client_)
+ video_capture_client_->Pause();
+ StopStreaming();
+ state_ = REMOTING;
+ CreateAndSendOffer();
+}
+
+void Session::RestartMirroringStreaming() {
+ if (state_ != REMOTING)
+ return;
+ StopStreaming();
+ state_ = MIRRORING;
+ CreateAndSendOffer();
+}
+
+void Session::QueryCapabilitiesForRemoting() {
+ DCHECK(!media_remoter_);
+ const int32_t sequence_number = message_dispatcher_.GetNextSeqNumber();
+ base::Value query(base::Value::Type::DICTIONARY);
+ query.SetKey("type", base::Value("GET_CAPABILITIES"));
+ query.SetKey("sessionId", base::Value(session_id_));
+ query.SetKey("seqNum", base::Value(sequence_number));
+
+ CastMessage query_message;
+ query_message.message_namespace = kWebRtcNamespace;
+ const bool did_serialize_query =
+ base::JSONWriter::Write(query, &query_message.json_format_data);
+ DCHECK(did_serialize_query);
+ message_dispatcher_.RequestReply(
+ query_message, ResponseType::CAPABILITIES_RESPONSE, sequence_number,
+ kGetCapabilitiesTimeout,
+ base::BindOnce(&Session::OnCapabilitiesResponse, base::Unretained(this)));
+}
+
+void Session::OnCapabilitiesResponse(const ReceiverResponse& response) {
+ if (!response.capabilities || response.type == ResponseType::UNKNOWN) {
+ VLOG(1) << "Receiver doens't support GET_CAPABILITIES. Remoting disabled.";
+ return;
+ }
+ if (response.result != "ok") {
+ VLOG(1) << "Bad CAPABILITIES_RESPONSE. Remoting disabled.";
+ if (response.error) {
+ VLOG(1) << "error code=" << response.error->code
+ << " description=" << response.error->description
+ << " details=" << response.error->details;
+ }
+ return;
+ }
+ const std::vector<std::string>& caps = response.capabilities->media_caps;
+ const std::string receiver_build_version =
+ session_monitor_.has_value() ? session_monitor_->GetReceiverBuildVersion()
+ : "";
+ media_remoter_ = std::make_unique<MediaRemoter>(
+ this, ToRemotingSinkMetadata(caps, sink_info_, receiver_build_version),
+ &message_dispatcher_);
}
} // namespace mirroring
diff --git a/chromium/components/mirroring/service/session.h b/chromium/components/mirroring/service/session.h
index efc7d8b125b..41c21c37104 100644
--- a/chromium/components/mirroring/service/session.h
+++ b/chromium/components/mirroring/service/session.h
@@ -9,6 +9,7 @@
#include "base/optional.h"
#include "base/single_thread_task_runner.h"
#include "components/mirroring/service/interface.h"
+#include "components/mirroring/service/media_remoter.h"
#include "components/mirroring/service/message_dispatcher.h"
#include "components/mirroring/service/mirror_settings.h"
#include "components/mirroring/service/rtp_stream.h"
@@ -19,6 +20,8 @@
namespace media {
+class AudioInputDevice;
+
namespace cast {
class CastTransport;
} // namespace cast
@@ -31,13 +34,14 @@ struct ReceiverResponse;
class VideoCaptureClient;
class SessionMonitor;
-// Controls a mirroring session, including audio/video capturing and Cast
-// Streaming. When constructed, it does OFFER/ANSWER exchange with the mirroring
-// receiver. Mirroring starts when the exchange succeeds and stops when this
-// class is destructed or error occurs. |observer| will get notified when status
-// changes. |outbound_channel| is responsible for sending messages to the
-// mirroring receiver through Cast Channel.
-class Session final : public RtpStreamClient {
+// Controls a mirroring session, including audio/video capturing, Cast
+// Streaming, and the switching to/from media remoting. When constructed, it
+// does OFFER/ANSWER exchange with the mirroring receiver. Mirroring starts when
+// the exchange succeeds and stops when this class is destructed or error
+// occurs. |observer| will get notified when status changes. |outbound_channel|
+// is responsible for sending messages to the mirroring receiver through Cast
+// Channel.
+class Session final : public RtpStreamClient, public MediaRemoter::Client {
public:
Session(int32_t session_id,
const CastSinkInfo& sink_info,
@@ -70,7 +74,6 @@ class Session final : public RtpStreamClient {
// get notified with error, and session is stopped. Otherwise, capturing and
// streaming are started with the selected configs.
void OnAnswer(
- const std::string& cast_mode,
const std::vector<media::cast::FrameSenderConfig>& audio_configs,
const std::vector<media::cast::FrameSenderConfig>& video_configs,
const ReceiverResponse& response);
@@ -79,8 +82,36 @@ class Session final : public RtpStreamClient {
// responses.
void OnResponseParsingError(const std::string& error_message);
+ // Creates an audio input stream through Audio Service. |client| will be
+ // called after the stream is created.
+ void CreateAudioStream(AudioStreamCreatorClient* client,
+ const media::AudioParameters& params,
+ uint32_t shared_memory_count);
+
+ // Callback for CAPABILITIES_RESPONSE.
+ void OnCapabilitiesResponse(const ReceiverResponse& response);
+
private:
- void StopSession();
+ // To allow the test code access the |message_dispatcher_|.
+ // TODO(xjz): Remove this after adding the inbound message channel argument.
+ friend class SessionTest;
+
+ class AudioCapturingCallback;
+
+ // MediaRemoter::Client implementation.
+ void ConnectToRemotingSource(
+ media::mojom::RemoterPtr remoter,
+ media::mojom::RemotingSourceRequest source_request) override;
+ void RequestRemotingStreaming() override;
+ void RestartMirroringStreaming() override;
+
+ // Stops the current streaming session. If not called from StopSession(), a
+ // new streaming session will start later after exchanging OFFER/ANSWER
+ // messages with the receiver. This could happen any number of times before
+ // StopSession() shuts down everything permanently.
+ void StopStreaming();
+
+ void StopSession(); // Shuts down the entire mirroring session.
// Notify |observer_| that error occurred and close the session.
void ReportError(SessionError error);
@@ -96,10 +127,23 @@ class Session final : public RtpStreamClient {
// Create and send OFFER message.
void CreateAndSendOffer();
+ // Send GET_CAPABILITIES message.
+ void QueryCapabilitiesForRemoting();
+
// Provided by Cast Media Route Provider (MRP).
const int32_t session_id_;
const CastSinkInfo sink_info_;
+ // State transition:
+ // MIRRORING <-------> REMOTING
+ // | |
+ // .---> STOPPED <----.
+ enum {
+ MIRRORING, // A mirroring streaming session is starting or started.
+ REMOTING, // A remoting streaming session is starting or started.
+ STOPPED, // The session is stopped due to user's request or errors.
+ } state_;
+
SessionObserver* observer_ = nullptr;
ResourceProvider* resource_provider_ = nullptr;
MirrorSettings mirror_settings_;
@@ -118,6 +162,9 @@ class Session final : public RtpStreamClient {
std::unique_ptr<media::cast::CastTransport> cast_transport_;
scoped_refptr<base::SingleThreadTaskRunner> audio_encode_thread_ = nullptr;
scoped_refptr<base::SingleThreadTaskRunner> video_encode_thread_ = nullptr;
+ std::unique_ptr<AudioCapturingCallback> audio_capturing_callback_;
+ scoped_refptr<media::AudioInputDevice> audio_input_device_;
+ std::unique_ptr<MediaRemoter> media_remoter_;
base::WeakPtrFactory<Session> weak_factory_;
};
diff --git a/chromium/components/mirroring/service/session_monitor.cc b/chromium/components/mirroring/service/session_monitor.cc
index 94e6d77438a..82d05b524fb 100644
--- a/chromium/components/mirroring/service/session_monitor.cc
+++ b/chromium/components/mirroring/service/session_monitor.cc
@@ -105,25 +105,27 @@ SessionMonitor::SessionMonitor(
int max_retention_bytes,
const net::IPAddress& receiver_address,
base::Value session_tags,
- network::mojom::URLLoaderFactoryPtr loader_factory,
- std::unique_ptr<WifiStatusMonitor> wifi_status_monitor)
+ network::mojom::URLLoaderFactoryPtr loader_factory)
: max_retention_bytes_(max_retention_bytes),
receiver_address_(receiver_address),
session_tags_(std::move(session_tags)),
url_loader_factory_(std::move(loader_factory)),
- wifi_status_monitor_(std::move(wifi_status_monitor)),
stored_snapshots_bytes_(0),
- weak_factory_(this) {}
+ weak_factory_(this) {
+ QueryReceiverSetupInfo();
+}
SessionMonitor::~SessionMonitor() {}
void SessionMonitor::StartStreamingSession(
scoped_refptr<media::cast::CastEnvironment> cast_environment,
+ std::unique_ptr<WifiStatusMonitor> wifi_status_monitor,
SessionType session_type,
bool is_remoting) {
DCHECK(!event_subscribers_);
DCHECK(!snapshot_timer_.IsRunning());
+ wifi_status_monitor_ = std::move(wifi_status_monitor);
std::string session_activity =
session_type == AUDIO_AND_VIDEO
? "audio+video"
@@ -156,6 +158,7 @@ void SessionMonitor::StopStreamingSession() {
TakeSnapshot(); // Final snapshot of this streaming session.
}
event_subscribers_.reset();
+ wifi_status_monitor_.reset();
}
void SessionMonitor::OnStreamingError(SessionError error) {
@@ -305,6 +308,12 @@ void SessionMonitor::TakeSnapshot() {
stored_snapshots_bytes_ = snapshots_bytes;
}
+std::string SessionMonitor::GetReceiverBuildVersion() const {
+ std::string build_version;
+ GetString(session_tags_, "receiverVersion", &build_version);
+ return build_version;
+}
+
std::string SessionMonitor::GetEventLogsAndReset(
bool is_audio,
const std::string& extra_data) {
diff --git a/chromium/components/mirroring/service/session_monitor.h b/chromium/components/mirroring/service/session_monitor.h
index ba4892345c2..a6e27fb7249 100644
--- a/chromium/components/mirroring/service/session_monitor.h
+++ b/chromium/components/mirroring/service/session_monitor.h
@@ -56,8 +56,7 @@ class SessionMonitor {
SessionMonitor(int max_retention_bytes,
const net::IPAddress& receiver_address,
base::Value session_tags,
- network::mojom::URLLoaderFactoryPtr loader_factory,
- std::unique_ptr<WifiStatusMonitor> wifi_status_monitor);
+ network::mojom::URLLoaderFactoryPtr loader_factory);
~SessionMonitor();
@@ -71,6 +70,7 @@ class SessionMonitor {
// events/stats.
void StartStreamingSession(
scoped_refptr<media::cast::CastEnvironment> cast_environment,
+ std::unique_ptr<WifiStatusMonitor> wifi_status_monitor,
SessionType session_type,
bool is_remoting);
void StopStreamingSession();
@@ -91,6 +91,8 @@ class SessionMonitor {
// Takes a snapshot of recent Cast Streaming events and statistics.
void TakeSnapshot();
+ std::string GetReceiverBuildVersion() const;
+
private:
// Query the receiver for its current setup and uptime.
void QueryReceiverSetupInfo();
@@ -113,7 +115,7 @@ class SessionMonitor {
network::mojom::URLLoaderFactoryPtr url_loader_factory_;
// Monitors the WiFi status if not null.
- const std::unique_ptr<WifiStatusMonitor> wifi_status_monitor_;
+ std::unique_ptr<WifiStatusMonitor> wifi_status_monitor_;
std::unique_ptr<media::cast::RawEventSubscriberBundle> event_subscribers_;
diff --git a/chromium/components/mirroring/service/session_monitor_unittest.cc b/chromium/components/mirroring/service/session_monitor_unittest.cc
index c887eaa0a9f..93792e5184b 100644
--- a/chromium/components/mirroring/service/session_monitor_unittest.cc
+++ b/chromium/components/mirroring/service/session_monitor_unittest.cc
@@ -92,8 +92,6 @@ class SessionMonitorTest : public CastMessageChannel, public ::testing::Test {
void CreateSessionMonitor(int max_bytes, std::string* expected_settings) {
EXPECT_CALL(*this, Send(::testing::_)).Times(::testing::AtLeast(1));
- auto wifi_status_monitor =
- std::make_unique<WifiStatusMonitor>(123, &message_dispatcher_);
network::mojom::URLLoaderFactoryPtr url_loader_factory;
auto test_url_loader_factory =
std::make_unique<network::TestURLLoaderFactory>();
@@ -111,7 +109,7 @@ class SessionMonitorTest : public CastMessageChannel, public ::testing::Test {
session_tags.SetKey("shouldCaptureVideo", base::Value(true));
session_monitor_ = std::make_unique<SessionMonitor>(
max_bytes, receiver_address_, std::move(session_tags),
- std::move(url_loader_factory), std::move(wifi_status_monitor));
+ std::move(url_loader_factory));
}
// Generates and sends |num_of_responses| WiFi status.
@@ -148,9 +146,11 @@ class SessionMonitorTest : public CastMessageChannel, public ::testing::Test {
scoped_task_environment_.GetMainThreadTaskRunner(),
scoped_task_environment_.GetMainThreadTaskRunner());
EXPECT_TRUE(session_monitor_);
- session_monitor_->StartStreamingSession(cast_environment_,
- SessionMonitor::AUDIO_AND_VIDEO,
- false /* is_remoting */);
+ auto wifi_status_monitor =
+ std::make_unique<WifiStatusMonitor>(123, &message_dispatcher_);
+ session_monitor_->StartStreamingSession(
+ cast_environment_, std::move(wifi_status_monitor),
+ SessionMonitor::AUDIO_AND_VIDEO, false /* is_remoting */);
scoped_task_environment_.RunUntilIdle();
}
@@ -220,8 +220,8 @@ class SessionMonitorTest : public CastMessageChannel, public ::testing::Test {
TEST_F(SessionMonitorTest, ProvidesExpectedTags) {
std::string expected_settings;
CreateSessionMonitor(kRetentionBytes, &expected_settings);
- SendWifiStatus(34, 2000, 5);
StartStreamingSession();
+ SendWifiStatus(34, 2000, 5);
std::vector<int32_t> bundle_sizes({kRetentionBytes});
std::vector<SessionMonitor::EventsAndStats> bundles =
AssembleBundleAndVerify(bundle_sizes);
@@ -271,11 +271,11 @@ TEST_F(SessionMonitorTest, ConfigureMaxRetentionBytes) {
// 2500 is an estimate number of bytes for a snapshot that includes tags and
// five WiFi status records.
CreateSessionMonitor(2500, nullptr);
- SendWifiStatus(34, 2000, 5);
StartStreamingSession();
+ SendWifiStatus(34, 2000, 5);
StopStreamingSession();
- SendWifiStatus(54, 3000, 5);
StartStreamingSession();
+ SendWifiStatus(54, 3000, 5);
StopStreamingSession();
std::vector<int32_t> bundle_sizes({kRetentionBytes});
std::vector<SessionMonitor::EventsAndStats> bundles =
@@ -289,11 +289,11 @@ TEST_F(SessionMonitorTest, ConfigureMaxRetentionBytes) {
TEST_F(SessionMonitorTest, AssembleBundlesWithVaryingSizes) {
CreateSessionMonitor(kRetentionBytes, nullptr);
- SendWifiStatus(34, 2000, 5);
StartStreamingSession();
+ SendWifiStatus(34, 2000, 5);
StopStreamingSession();
- SendWifiStatus(54, 3000, 5);
StartStreamingSession();
+ SendWifiStatus(54, 3000, 5);
StopStreamingSession();
std::vector<int32_t> bundle_sizes({2500, kRetentionBytes});
std::vector<SessionMonitor::EventsAndStats> bundles =
diff --git a/chromium/components/mirroring/service/session_unittest.cc b/chromium/components/mirroring/service/session_unittest.cc
index 973c0daed8a..7443fb67949 100644
--- a/chromium/components/mirroring/service/session_unittest.cc
+++ b/chromium/components/mirroring/service/session_unittest.cc
@@ -10,7 +10,7 @@
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
-#include "base/test/simple_test_tick_clock.h"
+#include "base/time/time.h"
#include "base/values.h"
#include "components/mirroring/service/fake_network_service.h"
#include "components/mirroring/service/fake_video_capture_host.h"
@@ -27,35 +27,68 @@
using ::testing::InvokeWithoutArgs;
using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::Mock;
using media::cast::FrameSenderConfig;
using media::cast::Packet;
+using media::mojom::RemotingStopReason;
+using media::mojom::RemotingStartFailReason;
+using media::mojom::RemotingSinkMetadata;
+using media::mojom::RemotingSinkMetadataPtr;
namespace mirroring {
+namespace {
+
const int kSessionId = 5;
+class MockRemotingSource final : public media::mojom::RemotingSource {
+ public:
+ MockRemotingSource() : binding_(this) {}
+ ~MockRemotingSource() override {}
+
+ void Bind(media::mojom::RemotingSourceRequest request) {
+ binding_.Bind(std::move(request));
+ }
+
+ MOCK_METHOD0(OnSinkGone, void());
+ MOCK_METHOD0(OnStarted, void());
+ MOCK_METHOD1(OnStartFailed, void(RemotingStartFailReason));
+ MOCK_METHOD1(OnMessageFromSink, void(const std::vector<uint8_t>&));
+ MOCK_METHOD1(OnStopped, void(RemotingStopReason));
+ MOCK_METHOD1(OnSinkAvailable, void(const RemotingSinkMetadata&));
+ void OnSinkAvailable(RemotingSinkMetadataPtr metadata) override {
+ OnSinkAvailable(*metadata);
+ }
+
+ private:
+ mojo::Binding<media::mojom::RemotingSource> binding_;
+};
+
+} // namespace
+
class SessionTest : public ResourceProvider,
public SessionObserver,
public CastMessageChannel,
public ::testing::Test {
public:
- SessionTest() : receiver_endpoint_(media::cast::test::GetFreeLocalPort()) {
- testing_clock_.Advance(base::TimeTicks::Now() - base::TimeTicks());
- }
+ SessionTest() : receiver_endpoint_(media::cast::test::GetFreeLocalPort()) {}
~SessionTest() override { scoped_task_environment_.RunUntilIdle(); }
+ protected:
// SessionObserver implemenation.
MOCK_METHOD1(OnError, void(SessionError));
MOCK_METHOD0(DidStart, void());
MOCK_METHOD0(DidStop, void());
- // ResourceProvider implemenation.
MOCK_METHOD0(OnGetVideoCaptureHost, void());
MOCK_METHOD0(OnGetNetworkContext, void());
+ MOCK_METHOD0(OnCreateAudioStream, void());
+ MOCK_METHOD0(OnConnectToRemotingSource, void());
- // Called when sends OFFER message.
- MOCK_METHOD0(OnOffer, void());
+ // Called when sends an outbound message.
+ MOCK_METHOD1(OnOutboundMessage, void(const std::string& message_type));
// CastMessageHandler implementation. For outbound messages.
void Send(const CastMessage& message) {
@@ -68,10 +101,13 @@ class SessionTest : public ResourceProvider,
EXPECT_TRUE(GetString(*value, "type", &message_type));
if (message_type == "OFFER") {
EXPECT_TRUE(GetInt(*value, "seqNum", &offer_sequence_number_));
- OnOffer();
+ } else if (message_type == "GET_CAPABILITIES") {
+ EXPECT_TRUE(GetInt(*value, "seqNum", &capability_sequence_number_));
}
+ OnOutboundMessage(message_type);
}
+ // ResourceProvider implemenation.
void GetVideoCaptureHost(
media::mojom::VideoCaptureHostRequest request) override {
video_host_ = std::make_unique<FakeVideoCaptureHost>(std::move(request));
@@ -84,18 +120,50 @@ class SessionTest : public ResourceProvider,
OnGetNetworkContext();
}
+ void CreateAudioStream(AudioStreamCreatorClient* client,
+ const media::AudioParameters& params,
+ uint32_t total_segments) {
+ OnCreateAudioStream();
+ }
+
void SendAnswer() {
- FrameSenderConfig config = MirrorSettings::GetDefaultVideoConfig(
- media::cast::RtpPayloadType::VIDEO_VP8,
- media::cast::Codec::CODEC_VIDEO_VP8);
+ ASSERT_TRUE(session_);
+ std::vector<FrameSenderConfig> audio_configs;
std::vector<FrameSenderConfig> video_configs;
- video_configs.emplace_back(config);
+ if (sink_capability_ != DeviceCapability::VIDEO_ONLY) {
+ if (cast_mode_ == "remoting") {
+ audio_configs.emplace_back(MirrorSettings::GetDefaultAudioConfig(
+ media::cast::RtpPayloadType::REMOTE_AUDIO,
+ media::cast::Codec::CODEC_AUDIO_REMOTE));
+ } else {
+ EXPECT_EQ("mirroring", cast_mode_);
+ audio_configs.emplace_back(MirrorSettings::GetDefaultAudioConfig(
+ media::cast::RtpPayloadType::AUDIO_OPUS,
+ media::cast::Codec::CODEC_AUDIO_OPUS));
+ }
+ }
+ if (sink_capability_ != DeviceCapability::AUDIO_ONLY) {
+ if (cast_mode_ == "remoting") {
+ video_configs.emplace_back(MirrorSettings::GetDefaultVideoConfig(
+ media::cast::RtpPayloadType::REMOTE_VIDEO,
+ media::cast::Codec::CODEC_VIDEO_REMOTE));
+ } else {
+ EXPECT_EQ("mirroring", cast_mode_);
+ video_configs.emplace_back(MirrorSettings::GetDefaultVideoConfig(
+ media::cast::RtpPayloadType::VIDEO_VP8,
+ media::cast::Codec::CODEC_VIDEO_VP8));
+ }
+ }
auto answer = std::make_unique<Answer>();
answer->udp_port = receiver_endpoint_.port();
- answer->send_indexes.push_back(0);
- answer->ssrcs.push_back(32);
- answer->cast_mode = "mirroring";
+ answer->cast_mode = cast_mode_;
+ answer->supports_get_status = true;
+ const int number_of_configs = audio_configs.size() + video_configs.size();
+ for (int i = 0; i < number_of_configs; ++i) {
+ answer->send_indexes.push_back(i);
+ answer->ssrcs.push_back(31 + i); // Arbitrary receiver SSRCs.
+ }
ReceiverResponse response;
response.result = "ok";
@@ -103,94 +171,222 @@ class SessionTest : public ResourceProvider,
response.sequence_number = offer_sequence_number_;
response.answer = std::move(answer);
- session_->OnAnswer("mirroring", std::vector<FrameSenderConfig>(),
- video_configs, response);
+ session_->OnAnswer(audio_configs, video_configs, response);
+ scoped_task_environment_.RunUntilIdle();
}
- protected:
- void CreateSession() {
+ void ConnectToRemotingSource(
+ media::mojom::RemoterPtr remoter,
+ media::mojom::RemotingSourceRequest request) override {
+ remoter_ = std::move(remoter);
+ remoting_source_.Bind(std::move(request));
+ OnConnectToRemotingSource();
+ }
+
+ // Create a mirroring session. Expect to send OFFER message.
+ void CreateSession(DeviceCapability sink_capability) {
+ sink_capability_ = sink_capability;
CastSinkInfo sink_info;
sink_info.ip_address = receiver_endpoint_.address();
- sink_info.capability = DeviceCapability::AUDIO_AND_VIDEO;
- // Expect to receive OFFER message when session is created.
- base::RunLoop run_loop;
+ sink_info.capability = sink_capability_;
+ sink_info.model_name = "Chromecast";
+ cast_mode_ = "mirroring";
+ // Expect to send OFFER message when session is created.
EXPECT_CALL(*this, OnGetNetworkContext()).Times(1);
EXPECT_CALL(*this, OnError(_)).Times(0);
- EXPECT_CALL(*this, OnOffer())
- .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+ EXPECT_CALL(*this, OnOutboundMessage("OFFER")).Times(1);
session_ = std::make_unique<Session>(
kSessionId, sink_info, gfx::Size(1920, 1080), this, this, this);
- run_loop.Run();
+ scoped_task_environment_.RunUntilIdle();
+ Mock::VerifyAndClear(this);
}
- base::test::ScopedTaskEnvironment scoped_task_environment_;
- const net::IPEndPoint receiver_endpoint_;
- base::SimpleTestTickClock testing_clock_;
-
- std::unique_ptr<Session> session_;
- std::unique_ptr<FakeVideoCaptureHost> video_host_;
- std::unique_ptr<MockNetworkContext> network_context_;
-
- int32_t offer_sequence_number_ = -1;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(SessionTest);
-};
-
-TEST_F(SessionTest, Mirroring) {
- CreateSession();
- scoped_task_environment_.RunUntilIdle();
- {
+ // Starts the mirroring session.
+ void StartSession() {
+ ASSERT_TRUE(cast_mode_ == "mirroring");
// Except mirroing session starts after receiving ANSWER message.
- base::RunLoop run_loop;
- EXPECT_CALL(*this, OnGetVideoCaptureHost()).Times(1);
+ const int num_to_get_video_host =
+ sink_capability_ == DeviceCapability::AUDIO_ONLY ? 0 : 1;
+ const int num_to_create_audio_stream =
+ sink_capability_ == DeviceCapability::VIDEO_ONLY ? 0 : 1;
+ EXPECT_CALL(*this, OnGetVideoCaptureHost()).Times(num_to_get_video_host);
+ EXPECT_CALL(*this, OnCreateAudioStream()).Times(num_to_create_audio_stream);
EXPECT_CALL(*this, OnError(_)).Times(0);
- EXPECT_CALL(*this, DidStart())
- .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+ EXPECT_CALL(*this, OnOutboundMessage("GET_STATUS")).Times(AtLeast(1));
+ EXPECT_CALL(*this, OnOutboundMessage("GET_CAPABILITIES")).Times(1);
+ EXPECT_CALL(*this, DidStart()).Times(1);
SendAnswer();
- run_loop.Run();
+ scoped_task_environment_.RunUntilIdle();
+ Mock::VerifyAndClear(this);
}
- scoped_task_environment_.RunUntilIdle();
- {
- base::RunLoop run_loop;
+
+ void StopSession() {
+ if (video_host_)
+ EXPECT_CALL(*video_host_, OnStopped()).Times(1);
+ EXPECT_CALL(*this, DidStop()).Times(1);
+ session_.reset();
+ scoped_task_environment_.RunUntilIdle();
+ Mock::VerifyAndClear(this);
+ }
+
+ void CaptureOneVideoFrame() {
+ ASSERT_TRUE(cast_mode_ == "mirroring");
+ ASSERT_TRUE(video_host_);
// Expect to send out some UDP packets.
- EXPECT_CALL(*network_context_->udp_socket(), OnSend())
- .Times(testing::AtLeast(1));
- EXPECT_CALL(*video_host_, ReleaseBuffer(_, _, _))
- .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+ EXPECT_CALL(*network_context_->udp_socket(), OnSend()).Times(AtLeast(1));
+ EXPECT_CALL(*video_host_, ReleaseBuffer(_, _, _)).Times(1);
// Send one video frame to the consumer.
- video_host_->SendOneFrame(gfx::Size(64, 32), testing_clock_.NowTicks());
- run_loop.Run();
+ video_host_->SendOneFrame(gfx::Size(64, 32), base::TimeTicks::Now());
+ scoped_task_environment_.RunUntilIdle();
+ Mock::VerifyAndClear(network_context_.get());
+ Mock::VerifyAndClear(video_host_.get());
+ }
+
+ void SignalAnswerTimeout() {
+ if (cast_mode_ == "mirroring") {
+ EXPECT_CALL(*this, DidStop()).Times(1);
+ EXPECT_CALL(*this, OnError(ANSWER_TIME_OUT)).Times(1);
+ } else {
+ EXPECT_CALL(*this, DidStop()).Times(0);
+ EXPECT_CALL(*this, OnError(ANSWER_TIME_OUT)).Times(0);
+ // Expect to send OFFER message to fallback on mirroring.
+ EXPECT_CALL(*this, OnOutboundMessage("OFFER")).Times(1);
+ // The start of remoting is expected to fail.
+ EXPECT_CALL(remoting_source_,
+ OnStartFailed(RemotingStartFailReason::SERVICE_NOT_CONNECTED))
+ .Times(1);
+ EXPECT_CALL(remoting_source_, OnSinkGone()).Times(AtLeast(1));
+ }
+ session_->OnAnswer(std::vector<FrameSenderConfig>(),
+ std::vector<FrameSenderConfig>(), ReceiverResponse());
+ scoped_task_environment_.RunUntilIdle();
+ cast_mode_ = "mirroring";
+ Mock::VerifyAndClear(this);
+ Mock::VerifyAndClear(&remoting_source_);
+ }
+
+ void SendRemotingCapabilities() {
+ EXPECT_CALL(*this, OnConnectToRemotingSource()).Times(1);
+ EXPECT_CALL(remoting_source_, OnSinkAvailable(_)).Times(1);
+ ReceiverResponse response;
+ response.result = "ok";
+ response.type = ResponseType::CAPABILITIES_RESPONSE;
+ response.sequence_number = capability_sequence_number_;
+ response.capabilities = std::make_unique<ReceiverCapability>();
+ response.capabilities->media_caps =
+ std::vector<std::string>({"video", "audio", "vp8", "opus"});
+ session_->OnCapabilitiesResponse(response);
+ scoped_task_environment_.RunUntilIdle();
+ Mock::VerifyAndClear(this);
+ Mock::VerifyAndClear(&remoting_source_);
}
- scoped_task_environment_.RunUntilIdle();
- // Stop the session.
- {
+ void StartRemoting() {
base::RunLoop run_loop;
- EXPECT_CALL(*video_host_, OnStopped()).Times(1);
- EXPECT_CALL(*this, DidStop())
+ ASSERT_TRUE(remoter_.is_bound());
+ // GET_CAPABILITIES is only sent once at the start of mirroring.
+ EXPECT_CALL(*this, OnOutboundMessage("GET_CAPABILITIES")).Times(0);
+ EXPECT_CALL(*this, OnOutboundMessage("OFFER"))
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
- session_.reset();
+ remoter_->Start();
run_loop.Run();
+ scoped_task_environment_.RunUntilIdle();
+ cast_mode_ = "remoting";
+ Mock::VerifyAndClear(this);
}
- scoped_task_environment_.RunUntilIdle();
+
+ void RemotingStarted() {
+ ASSERT_TRUE(cast_mode_ == "remoting");
+ EXPECT_CALL(remoting_source_, OnStarted()).Times(1);
+ SendAnswer();
+ scoped_task_environment_.RunUntilIdle();
+ Mock::VerifyAndClear(this);
+ Mock::VerifyAndClear(&remoting_source_);
+ }
+
+ void StopRemoting() {
+ ASSERT_TRUE(cast_mode_ == "remoting");
+ const RemotingStopReason reason = RemotingStopReason::LOCAL_PLAYBACK;
+ // Expect to send OFFER message to fallback on mirroring.
+ EXPECT_CALL(*this, OnOutboundMessage("OFFER")).Times(1);
+ EXPECT_CALL(remoting_source_, OnStopped(reason)).Times(1);
+ remoter_->Stop(reason);
+ scoped_task_environment_.RunUntilIdle();
+ cast_mode_ = "mirroring";
+ Mock::VerifyAndClear(this);
+ Mock::VerifyAndClear(&remoting_source_);
+ }
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ const net::IPEndPoint receiver_endpoint_;
+ DeviceCapability sink_capability_ = DeviceCapability::AUDIO_AND_VIDEO;
+ media::mojom::RemoterPtr remoter_;
+ MockRemotingSource remoting_source_;
+ std::string cast_mode_;
+ int32_t offer_sequence_number_ = -1;
+ int32_t capability_sequence_number_ = -1;
+
+ std::unique_ptr<Session> session_;
+ std::unique_ptr<FakeVideoCaptureHost> video_host_;
+ std::unique_ptr<MockNetworkContext> network_context_;
+
+ DISALLOW_COPY_AND_ASSIGN(SessionTest);
+};
+
+TEST_F(SessionTest, AudioOnlyMirroring) {
+ CreateSession(DeviceCapability::AUDIO_ONLY);
+ StartSession();
+ StopSession();
+}
+
+TEST_F(SessionTest, VideoOnlyMirroring) {
+ CreateSession(DeviceCapability::VIDEO_ONLY);
+ StartSession();
+ CaptureOneVideoFrame();
+ StopSession();
+}
+
+TEST_F(SessionTest, AudioAndVideoMirroring) {
+ CreateSession(DeviceCapability::AUDIO_AND_VIDEO);
+ StartSession();
+ StopSession();
}
TEST_F(SessionTest, AnswerTimeout) {
- CreateSession();
- scoped_task_environment_.RunUntilIdle();
- {
- // Expect error.
- base::RunLoop run_loop;
- EXPECT_CALL(*this, OnGetVideoCaptureHost()).Times(0);
- EXPECT_CALL(*this, DidStop()).Times(1);
- EXPECT_CALL(*this, OnError(ANSWER_TIME_OUT))
- .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
- session_->OnAnswer("mirroring", std::vector<FrameSenderConfig>(),
- std::vector<FrameSenderConfig>(), ReceiverResponse());
- run_loop.Run();
- }
- scoped_task_environment_.RunUntilIdle();
+ CreateSession(DeviceCapability::AUDIO_AND_VIDEO);
+ SignalAnswerTimeout();
+}
+
+TEST_F(SessionTest, SwitchToAndFromRemoting) {
+ CreateSession(DeviceCapability::AUDIO_AND_VIDEO);
+ StartSession();
+ SendRemotingCapabilities();
+ StartRemoting();
+ RemotingStarted();
+ StopRemoting();
+ StopSession();
+}
+
+TEST_F(SessionTest, StopSessionWhileRemoting) {
+ CreateSession(DeviceCapability::AUDIO_AND_VIDEO);
+ StartSession();
+ SendRemotingCapabilities();
+ StartRemoting();
+ RemotingStarted();
+ StopSession();
+}
+
+TEST_F(SessionTest, StartRemotingFailed) {
+ CreateSession(DeviceCapability::AUDIO_AND_VIDEO);
+ StartSession();
+ SendRemotingCapabilities();
+ StartRemoting();
+ SignalAnswerTimeout();
+ // Resume mirroring.
+ SendAnswer();
+ CaptureOneVideoFrame();
+ StopSession();
}
} // namespace mirroring
diff --git a/chromium/components/nacl/broker/BUILD.gn b/chromium/components/nacl/broker/BUILD.gn
index 14641bdd76b..4c342b0748f 100644
--- a/chromium/components/nacl/broker/BUILD.gn
+++ b/chromium/components/nacl/broker/BUILD.gn
@@ -28,7 +28,8 @@ source_set("broker") {
"//components/nacl/common:switches",
"//content/public/common:static_switches",
"//ipc",
- "//mojo/edk",
+ "//mojo/public/cpp/platform",
+ "//mojo/public/cpp/system",
"//sandbox",
"//services/service_manager/public/cpp",
"//services/service_manager/zygote:zygote_buildflags",
diff --git a/chromium/components/nacl/browser/BUILD.gn b/chromium/components/nacl/browser/BUILD.gn
index 0f5426351f6..d6f2729da79 100644
--- a/chromium/components/nacl/browser/BUILD.gn
+++ b/chromium/components/nacl/browser/BUILD.gn
@@ -39,7 +39,6 @@ static_library("browser") {
"//components/url_formatter",
"//content/public/browser",
"//content/public/common",
- "//mojo/edk",
"//native_client/src/trusted/service_runtime:sel_main_chrome",
"//net",
"//ppapi/host",
diff --git a/chromium/components/nacl/common/BUILD.gn b/chromium/components/nacl/common/BUILD.gn
index 8f9ef992673..5e0c304c8db 100644
--- a/chromium/components/nacl/common/BUILD.gn
+++ b/chromium/components/nacl/common/BUILD.gn
@@ -37,7 +37,7 @@ if (enable_nacl) {
"//base",
"//base:base_static",
"//content/public/common:service_names",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//services/service_manager/public/cpp",
]
}
diff --git a/chromium/components/nacl/loader/BUILD.gn b/chromium/components/nacl/loader/BUILD.gn
index c468773ce22..cdf2ffae618 100644
--- a/chromium/components/nacl/loader/BUILD.gn
+++ b/chromium/components/nacl/loader/BUILD.gn
@@ -36,7 +36,7 @@ source_set("minimal") {
"//components/nacl/common:mojo_bindings",
"//crypto",
"//ipc",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//native_client/src/trusted/service_runtime:sel_main_chrome",
"//ppapi/c",
"//ppapi/proxy:ipc",
@@ -124,13 +124,12 @@ if (is_linux) {
deps = [
":loader",
"//base",
- "//build/config:exe_and_shlib_deps",
"//components/nacl/common:switches",
"//components/nacl/loader/sandbox_linux",
"//content/public/common",
"//crypto",
"//ipc",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//sandbox/linux:sandbox_services",
"//services/service_manager/sandbox",
"//services/service_manager/zygote",
@@ -159,6 +158,13 @@ if (is_linux) {
}
}
+ # The only symbols that nacl_helper needs to export are those specified by
+ # its direct dependencies, so -rdynamic would only serve to unnecessarily
+ # increase the binary size.
+ if (is_desktop_linux) {
+ configs -= [ "//build/config/linux:export_dynamic" ]
+ }
+
data_deps = [
"//native_client/src/trusted/service_runtime/linux:bootstrap",
]
@@ -210,7 +216,7 @@ if (is_win && target_cpu == "x86" && current_cpu == "x64") {
"//components/nacl/broker",
"//components/nacl/common:switches",
"//content/public/common:static_switches",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//sandbox",
"//services/service_manager/sandbox:sandbox",
]
@@ -240,7 +246,7 @@ if (is_nacl_nonsfi) {
"//components/tracing",
"//content",
"//ipc",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//native_client/src/nonsfi/irt:nacl_sys_private",
"//native_client/src/nonsfi/loader:elf_loader",
diff --git a/chromium/components/navigation_interception/BUILD.gn b/chromium/components/navigation_interception/BUILD.gn
index 4b2ba36c76f..16b17d6f9ea 100644
--- a/chromium/components/navigation_interception/BUILD.gn
+++ b/chromium/components/navigation_interception/BUILD.gn
@@ -51,6 +51,7 @@ source_set("unit_tests") {
deps = [
":navigation_interception",
"//base",
+ "//base/test:test_support",
"//content/public/browser",
"//content/test:test_support",
"//testing/gmock",
diff --git a/chromium/components/navigation_interception/intercept_navigation_throttle.cc b/chromium/components/navigation_interception/intercept_navigation_throttle.cc
index 81acec45bb1..020abc9340b 100644
--- a/chromium/components/navigation_interception/intercept_navigation_throttle.cc
+++ b/chromium/components/navigation_interception/intercept_navigation_throttle.cc
@@ -4,31 +4,38 @@
#include "components/navigation_interception/intercept_navigation_throttle.h"
+#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
-#include "components/navigation_interception/navigation_params.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
-
-using content::BrowserThread;
+#include "url/gurl.h"
namespace navigation_interception {
+const base::Feature InterceptNavigationThrottle::kAsyncCheck{
+ "AsyncNavigationIntercept", base::FEATURE_DISABLED_BY_DEFAULT};
+
InterceptNavigationThrottle::InterceptNavigationThrottle(
content::NavigationHandle* navigation_handle,
CheckCallback should_ignore_callback)
: content::NavigationThrottle(navigation_handle),
- should_ignore_callback_(should_ignore_callback) {}
+ should_ignore_callback_(should_ignore_callback),
+ ui_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ weak_factory_(this) {}
-InterceptNavigationThrottle::~InterceptNavigationThrottle() {}
+InterceptNavigationThrottle::~InterceptNavigationThrottle() {
+ UMA_HISTOGRAM_BOOLEAN("Navigation.Intercept.Ignored", should_ignore_);
+}
content::NavigationThrottle::ThrottleCheckResult
InterceptNavigationThrottle::WillStartRequest() {
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(!should_ignore_);
base::ElapsedTimer timer;
- auto result = CheckIfShouldIgnoreNavigation(false);
+ auto result = CheckIfShouldIgnoreNavigation(false /* is_redirect */);
UMA_HISTOGRAM_COUNTS_10M("Navigation.Intercept.WillStart",
timer.Elapsed().InMicroseconds());
return result;
@@ -36,8 +43,19 @@ InterceptNavigationThrottle::WillStartRequest() {
content::NavigationThrottle::ThrottleCheckResult
InterceptNavigationThrottle::WillRedirectRequest() {
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- return CheckIfShouldIgnoreNavigation(true);
+ if (should_ignore_)
+ return content::NavigationThrottle::CANCEL_AND_IGNORE;
+ return CheckIfShouldIgnoreNavigation(true /* is_redirect */);
+}
+
+content::NavigationThrottle::ThrottleCheckResult
+InterceptNavigationThrottle::WillFailRequest() {
+ return WillFinish();
+}
+
+content::NavigationThrottle::ThrottleCheckResult
+InterceptNavigationThrottle::WillProcessResponse() {
+ return WillFinish();
}
const char* InterceptNavigationThrottle::GetNameForLogging() {
@@ -45,19 +63,81 @@ const char* InterceptNavigationThrottle::GetNameForLogging() {
}
content::NavigationThrottle::ThrottleCheckResult
+InterceptNavigationThrottle::WillFinish() {
+ DCHECK(!deferring_);
+ if (should_ignore_)
+ return content::NavigationThrottle::CANCEL_AND_IGNORE;
+
+ if (pending_checks_ > 0) {
+ deferring_ = true;
+ return content::NavigationThrottle::DEFER;
+ }
+
+ return content::NavigationThrottle::PROCEED;
+}
+
+content::NavigationThrottle::ThrottleCheckResult
InterceptNavigationThrottle::CheckIfShouldIgnoreNavigation(bool is_redirect) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- NavigationParams navigation_params(
+ if (ShouldCheckAsynchronously()) {
+ pending_checks_++;
+ ui_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&InterceptNavigationThrottle::RunCheckAsync,
+ weak_factory_.GetWeakPtr(),
+ GetNavigationParams(is_redirect)));
+ return content::NavigationThrottle::PROCEED;
+ }
+ // No need to set |should_ignore_| since if it is true, we'll cancel the
+ // navigation immediately.
+ return should_ignore_callback_.Run(navigation_handle()->GetWebContents(),
+ GetNavigationParams(is_redirect))
+ ? content::NavigationThrottle::CANCEL_AND_IGNORE
+ : content::NavigationThrottle::PROCEED;
+ // Careful, |this| can be deleted at this point.
+}
+
+void InterceptNavigationThrottle::RunCheckAsync(
+ const NavigationParams& params) {
+ DCHECK(base::FeatureList::IsEnabled(kAsyncCheck));
+ DCHECK_GT(pending_checks_, 0);
+ pending_checks_--;
+ bool final_deferred_check = deferring_ && pending_checks_ == 0;
+ auto weak_this = weak_factory_.GetWeakPtr();
+ bool should_ignore = should_ignore_callback_.Run(
+ navigation_handle()->GetWebContents(), params);
+ if (!weak_this)
+ return;
+
+ should_ignore_ |= should_ignore;
+ if (!final_deferred_check)
+ return;
+
+ if (should_ignore) {
+ CancelDeferredNavigation(content::NavigationThrottle::CANCEL_AND_IGNORE);
+ } else {
+ Resume();
+ }
+}
+
+bool InterceptNavigationThrottle::ShouldCheckAsynchronously() const {
+ // Do not apply the async optimization for:
+ // - POST navigations, to ensure we aren't violating idempotency.
+ // - Subframe navigations, which aren't observed on Android, and should be
+ // fast on other platforms.
+ // - non-http/s URLs, which are more likely to be intercepted.
+ return navigation_handle()->IsInMainFrame() &&
+ !navigation_handle()->IsPost() &&
+ navigation_handle()->GetURL().SchemeIsHTTPOrHTTPS() &&
+ base::FeatureList::IsEnabled(kAsyncCheck);
+}
+
+NavigationParams InterceptNavigationThrottle::GetNavigationParams(
+ bool is_redirect) const {
+ return NavigationParams(
navigation_handle()->GetURL(), navigation_handle()->GetReferrer(),
navigation_handle()->HasUserGesture(), navigation_handle()->IsPost(),
navigation_handle()->GetPageTransition(), is_redirect,
navigation_handle()->IsExternalProtocol(), true,
navigation_handle()->GetBaseURLForDataURL());
- bool should_ignore_navigation = should_ignore_callback_.Run(
- navigation_handle()->GetWebContents(), navigation_params);
- return should_ignore_navigation
- ? content::NavigationThrottle::CANCEL_AND_IGNORE
- : content::NavigationThrottle::PROCEED;
}
} // namespace navigation_interception
diff --git a/chromium/components/navigation_interception/intercept_navigation_throttle.h b/chromium/components/navigation_interception/intercept_navigation_throttle.h
index 7607fd5f523..0b154bc01b6 100644
--- a/chromium/components/navigation_interception/intercept_navigation_throttle.h
+++ b/chromium/components/navigation_interception/intercept_navigation_throttle.h
@@ -5,11 +5,13 @@
#ifndef COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_THROTTLE_H_
#define COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_THROTTLE_H_
-#include <string>
-
#include "base/callback.h"
+#include "base/feature_list.h"
#include "base/macros.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "components/navigation_interception/navigation_params.h"
#include "content/public/browser/navigation_throttle.h"
namespace content {
@@ -25,10 +27,13 @@ class NavigationParams;
// level navigations. This is a UI thread class.
class InterceptNavigationThrottle : public content::NavigationThrottle {
public:
- typedef base::Callback<bool(content::WebContents* /* source */,
- const NavigationParams& /* navigation_params */)>
+ typedef base::RepeatingCallback<bool(
+ content::WebContents* /* source */,
+ const NavigationParams& /* navigation_params */)>
CheckCallback;
+ static const base::Feature kAsyncCheck;
+
InterceptNavigationThrottle(content::NavigationHandle* navigation_handle,
CheckCallback should_ignore_callback);
~InterceptNavigationThrottle() override;
@@ -36,13 +41,46 @@ class InterceptNavigationThrottle : public content::NavigationThrottle {
// content::NavigationThrottle implementation:
ThrottleCheckResult WillStartRequest() override;
ThrottleCheckResult WillRedirectRequest() override;
+ ThrottleCheckResult WillFailRequest() override;
+ ThrottleCheckResult WillProcessResponse() override;
const char* GetNameForLogging() override;
private:
+ // To be called on either WillFailRequest or WillProcessResponse.
+ ThrottleCheckResult WillFinish();
+
ThrottleCheckResult CheckIfShouldIgnoreNavigation(bool is_redirect);
+ void RunCheckAsync(const NavigationParams& params);
+
+ bool ShouldCheckAsynchronously() const;
+
+ // Constructs NavigationParams for this navigation.
+ NavigationParams GetNavigationParams(bool is_redirect) const;
+ // This callback should be called at the start of navigation and every
+ // redirect, until |should_ignore_| is true.
+ // Note: the callback can delete |this|.
CheckCallback should_ignore_callback_;
+ // Note that the CheckCallback currently has thread affinity on the Java side.
+ scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+
+ // The remaining members are only set for asynchronous checking.
+ //
+ // How many outbound pending checks are running. Normally this will be either
+ // 0 or 1, but making this a bool makes too many assumptions about the nature
+ // of Chrome's task queues (e.g. we could be scheduled after the task which
+ // redirects the navigation).
+ int pending_checks_ = 0;
+
+ // Whether the navigation should be ignored. Updated at every redirect.
+ bool should_ignore_ = false;
+
+ // Whether the navigation is currently deferred.
+ bool deferring_ = false;
+
+ base::WeakPtrFactory<InterceptNavigationThrottle> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(InterceptNavigationThrottle);
};
diff --git a/chromium/components/navigation_interception/intercept_navigation_throttle_unittest.cc b/chromium/components/navigation_interception/intercept_navigation_throttle_unittest.cc
index 7a456d62aa1..d9d493bba77 100644
--- a/chromium/components/navigation_interception/intercept_navigation_throttle_unittest.cc
+++ b/chromium/components/navigation_interception/intercept_navigation_throttle_unittest.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
+#include "base/test/scoped_feature_list.h"
#include "components/navigation_interception/navigation_params.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_throttle.h"
@@ -57,25 +58,35 @@ class MockInterceptCallbackReceiver {
// InterceptNavigationThrottleTest ------------------------------------
class InterceptNavigationThrottleTest
- : public content::RenderViewHostTestHarness {
+ : public content::RenderViewHostTestHarness,
+ public testing::WithParamInterface<bool> {
public:
InterceptNavigationThrottleTest()
- : mock_callback_receiver_(new MockInterceptCallbackReceiver()) {}
+ : mock_callback_receiver_(new MockInterceptCallbackReceiver()) {
+ if (GetParam()) {
+ scoped_feature_.InitAndEnableFeature(
+ InterceptNavigationThrottle::kAsyncCheck);
+ } else {
+ scoped_feature_.InitAndDisableFeature(
+ InterceptNavigationThrottle::kAsyncCheck);
+ }
+ }
- std::unique_ptr<content::NavigationThrottle> CreateThrottle(
+ static std::unique_ptr<content::NavigationThrottle> CreateThrottle(
+ InterceptNavigationThrottle::CheckCallback callback,
content::NavigationHandle* handle) {
- return std::make_unique<InterceptNavigationThrottle>(
- handle, base::BindRepeating(
- &MockInterceptCallbackReceiver::ShouldIgnoreNavigation,
- base::Unretained(mock_callback_receiver_.get())));
+ return std::make_unique<InterceptNavigationThrottle>(handle, callback);
}
std::unique_ptr<content::TestNavigationThrottleInserter>
CreateThrottleInserter() {
return std::make_unique<content::TestNavigationThrottleInserter>(
web_contents(),
- base::BindRepeating(&InterceptNavigationThrottleTest::CreateThrottle,
- base::Unretained(this)));
+ base::BindRepeating(
+ &InterceptNavigationThrottleTest::CreateThrottle,
+ base::BindRepeating(
+ &MockInterceptCallbackReceiver::ShouldIgnoreNavigation,
+ base::Unretained(mock_callback_receiver_.get()))));
}
NavigationThrottle::ThrottleCheckResult SimulateNavigation(
@@ -105,11 +116,12 @@ class InterceptNavigationThrottleTest
return simulator->GetLastThrottleCheckResult();
}
+ base::test::ScopedFeatureList scoped_feature_;
std::unique_ptr<MockInterceptCallbackReceiver> mock_callback_receiver_;
};
-TEST_F(InterceptNavigationThrottleTest,
- RequestDeferredAndResumedIfNavigationNotIgnored) {
+TEST_P(InterceptNavigationThrottleTest,
+ RequestCompletesIfNavigationNotIgnored) {
ON_CALL(*mock_callback_receiver_, ShouldIgnoreNavigation(_, _))
.WillByDefault(Return(false));
EXPECT_CALL(
@@ -121,8 +133,7 @@ TEST_F(InterceptNavigationThrottleTest,
EXPECT_EQ(NavigationThrottle::PROCEED, result);
}
-TEST_F(InterceptNavigationThrottleTest,
- RequestDeferredAndCancelledIfNavigationIgnored) {
+TEST_P(InterceptNavigationThrottleTest, RequestCancelledIfNavigationIgnored) {
ON_CALL(*mock_callback_receiver_, ShouldIgnoreNavigation(_, _))
.WillByDefault(Return(true));
EXPECT_CALL(
@@ -134,7 +145,7 @@ TEST_F(InterceptNavigationThrottleTest,
EXPECT_EQ(NavigationThrottle::CANCEL_AND_IGNORE, result);
}
-TEST_F(InterceptNavigationThrottleTest, CallbackIsPostFalseForGet) {
+TEST_P(InterceptNavigationThrottleTest, CallbackIsPostFalseForGet) {
EXPECT_CALL(*mock_callback_receiver_,
ShouldIgnoreNavigation(
_, AllOf(NavigationParamsUrlIsTest(),
@@ -147,7 +158,7 @@ TEST_F(InterceptNavigationThrottleTest, CallbackIsPostFalseForGet) {
EXPECT_EQ(NavigationThrottle::PROCEED, result);
}
-TEST_F(InterceptNavigationThrottleTest, CallbackIsPostTrueForPost) {
+TEST_P(InterceptNavigationThrottleTest, CallbackIsPostTrueForPost) {
EXPECT_CALL(*mock_callback_receiver_,
ShouldIgnoreNavigation(
_, AllOf(NavigationParamsUrlIsTest(),
@@ -159,7 +170,7 @@ TEST_F(InterceptNavigationThrottleTest, CallbackIsPostTrueForPost) {
EXPECT_EQ(NavigationThrottle::PROCEED, result);
}
-TEST_F(InterceptNavigationThrottleTest,
+TEST_P(InterceptNavigationThrottleTest,
CallbackIsPostFalseForPostConvertedToGetBy302) {
EXPECT_CALL(*mock_callback_receiver_,
ShouldIgnoreNavigation(
@@ -178,13 +189,9 @@ TEST_F(InterceptNavigationThrottleTest,
}
// Ensure POST navigations are cancelled before the start.
-TEST_F(InterceptNavigationThrottleTest, PostNavigationCancelledAtStart) {
- EXPECT_CALL(*mock_callback_receiver_,
- ShouldIgnoreNavigation(
- _, AllOf(NavigationParamsUrlIsTest(),
- Property(&NavigationParams::is_post, Eq(true)))))
- .WillOnce(Return(true));
-
+TEST_P(InterceptNavigationThrottleTest, PostNavigationCancelledAtStart) {
+ ON_CALL(*mock_callback_receiver_, ShouldIgnoreNavigation(_, _))
+ .WillByDefault(Return(true));
auto throttle_inserter = CreateThrottleInserter();
std::unique_ptr<content::NavigationSimulator> simulator =
content::NavigationSimulator::CreateRendererInitiated(GURL(kTestUrl),
@@ -195,4 +202,37 @@ TEST_F(InterceptNavigationThrottleTest, PostNavigationCancelledAtStart) {
EXPECT_EQ(NavigationThrottle::CANCEL_AND_IGNORE, result);
}
+// Regression test for https://crbug.com/856737. There is some java code that
+// runs in the CheckCallback that can synchronously tear down the navigation
+// while the throttle is running.
+// TODO(csharrison): We should probably make that code async to avoid these
+// sorts of situations. However, it might not be possible if we implement
+// WebViewClient#shouldOverrideUrlLoading with this class which can end up
+// calling loadUrl() within the callback. See https://crbug.com/794020 for more
+// details.
+TEST_P(InterceptNavigationThrottleTest, IgnoreCallbackDeletesNavigation) {
+ NavigateAndCommit(GURL("about:blank"));
+
+ auto ignore_callback = [](content::WebContents* contents,
+ const NavigationParams& params) {
+ contents->GetController().GoToIndex(0);
+ return true;
+ };
+ auto inserter = std::make_unique<content::TestNavigationThrottleInserter>(
+ web_contents(),
+ base::BindRepeating(&InterceptNavigationThrottleTest::CreateThrottle,
+ base::BindRepeating(ignore_callback)));
+
+ // Intercepting a navigation and forcing a synchronous re-navigation should
+ // not crash.
+ auto navigation = content::NavigationSimulator::CreateBrowserInitiated(
+ GURL("https://intercept.test/"), web_contents());
+ navigation->Start();
+ base::RunLoop().RunUntilIdle();
+}
+
+INSTANTIATE_TEST_CASE_P(,
+ InterceptNavigationThrottleTest,
+ testing::Values(true, false));
+
} // namespace navigation_interception
diff --git a/chromium/components/navigation_metrics/OWNERS b/chromium/components/navigation_metrics/OWNERS
index 06f6dae274c..db827c88019 100644
--- a/chromium/components/navigation_metrics/OWNERS
+++ b/chromium/components/navigation_metrics/OWNERS
@@ -1,6 +1,5 @@
-cbentzel@chromium.org
+cthomp@chromium.org
davidben@chromium.org
-elawrence@chromium.org
estark@chromium.org
# COMPONENT: Internals>Network
diff --git a/chromium/components/navigation_metrics/navigation_metrics_unittest.cc b/chromium/components/navigation_metrics/navigation_metrics_unittest.cc
index 0ff3b1803fa..090c82d9a7e 100644
--- a/chromium/components/navigation_metrics/navigation_metrics_unittest.cc
+++ b/chromium/components/navigation_metrics/navigation_metrics_unittest.cc
@@ -4,7 +4,7 @@
#include "components/navigation_metrics/navigation_metrics.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
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 dead8880616..454c4845200 100644
--- a/chromium/components/net_log/net_export_file_writer_unittest.cc
+++ b/chromium/components/net_log/net_export_file_writer_unittest.cc
@@ -697,9 +697,8 @@ TEST_F(NetExportFileWriterTest, StartWithNetworkContextActive) {
simple_loader->SetOnRedirectCallback(base::BindRepeating(
[](base::RepeatingClosure notify_log,
const net::RedirectInfo& redirect_info,
- const network::ResourceResponseHead& response_head) {
- notify_log.Run();
- },
+ const network::ResourceResponseHead& response_head,
+ std::vector<std::string>* to_be_removed_headers) { notify_log.Run(); },
run_loop.QuitClosure()));
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory.get(),
diff --git a/chromium/components/neterror/OWNERS b/chromium/components/neterror/OWNERS
index 40148ee4dea..9df4a0338f9 100644
--- a/chromium/components/neterror/OWNERS
+++ b/chromium/components/neterror/OWNERS
@@ -1,5 +1,4 @@
mmenke@chromium.org
-juliatuttle@chromium.org
edwardjung@chromium.org
# COMPONENT: Internals>Network
diff --git a/chromium/components/network_hints/OWNERS b/chromium/components/network_hints/OWNERS
index 510eb168acb..fb59c70225a 100644
--- a/chromium/components/network_hints/OWNERS
+++ b/chromium/components/network_hints/OWNERS
@@ -1 +1 @@
-juliatuttle@chromium.org
+file://net/OWNERS
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 d9f2a3d0d7d..39fd430aafb 100644
--- a/chromium/components/network_session_configurator/browser/network_session_configurator.cc
+++ b/chromium/components/network_session_configurator/browser/network_session_configurator.cc
@@ -39,9 +39,7 @@ const char kQuicFieldTrialName[] = "QUIC";
const char kQuicFieldTrialEnabledGroupName[] = "Enabled";
const char kQuicFieldTrialHttpsEnabledGroupName[] = "HttpsEnabled";
-// Field trial for HTTP/2.
const char kHttp2FieldTrialName[] = "HTTP2";
-const char kHttp2FieldTrialDisablePrefix[] = "Disable";
// Gets the value of the specified command line switch, as an integer. If unable
// to convert it to an int (It's not an int, or the switch is not present)
@@ -121,7 +119,7 @@ void ConfigureHttp2Params(const base::CommandLine& command_line,
base::StringPiece http2_trial_group,
const VariationParameters& http2_trial_params,
net::HttpNetworkSession::Params* params) {
- if (http2_trial_group.starts_with(kHttp2FieldTrialDisablePrefix)) {
+ if (GetVariationParam(http2_trial_params, "http2_enabled") == "false") {
params->enable_http2 = false;
return;
}
@@ -168,23 +166,23 @@ bool ShouldSupportIetfFormatQuicAltSvc(
"true");
}
-net::QuicTagVector GetQuicConnectionOptions(
+quic::QuicTagVector GetQuicConnectionOptions(
const VariationParameters& quic_trial_params) {
VariationParameters::const_iterator it =
quic_trial_params.find("connection_options");
if (it == quic_trial_params.end()) {
- return net::QuicTagVector();
+ return quic::QuicTagVector();
}
return net::ParseQuicConnectionOptions(it->second);
}
-net::QuicTagVector GetQuicClientConnectionOptions(
+quic::QuicTagVector GetQuicClientConnectionOptions(
const VariationParameters& quic_trial_params) {
VariationParameters::const_iterator it =
quic_trial_params.find("client_connection_options");
if (it == quic_trial_params.end()) {
- return net::QuicTagVector();
+ return quic::QuicTagVector();
}
return net::ParseQuicConnectionOptions(it->second);
@@ -197,6 +195,13 @@ bool ShouldQuicCloseSessionsOnIpChange(
"true");
}
+bool ShouldQuicGoAwaySessionsOnIpChange(
+ const VariationParameters& quic_trial_params) {
+ return base::LowerCaseEqualsASCII(
+ GetVariationParam(quic_trial_params, "goaway_sessions_on_ip_change"),
+ "true");
+}
+
int GetQuicIdleConnectionTimeoutSeconds(
const VariationParameters& quic_trial_params) {
int value;
@@ -327,7 +332,7 @@ size_t GetQuicMaxPacketLength(const VariationParameters& quic_trial_params) {
return 0;
}
-net::QuicTransportVersionVector GetQuicVersions(
+quic::QuicTransportVersionVector GetQuicVersions(
const VariationParameters& quic_trial_params) {
return network_session_configurator::ParseQuicVersions(
GetVariationParam(quic_trial_params, "quic_version"));
@@ -368,6 +373,8 @@ void ConfigureQuicParams(base::StringPiece quic_trial_group,
GetQuicClientConnectionOptions(quic_trial_params);
params->quic_close_sessions_on_ip_change =
ShouldQuicCloseSessionsOnIpChange(quic_trial_params);
+ params->quic_goaway_sessions_on_ip_change =
+ ShouldQuicGoAwaySessionsOnIpChange(quic_trial_params);
int idle_connection_timeout_seconds =
GetQuicIdleConnectionTimeoutSeconds(quic_trial_params);
if (idle_connection_timeout_seconds != 0) {
@@ -377,7 +384,7 @@ void ConfigureQuicParams(base::StringPiece quic_trial_group,
int reduced_ping_timeout_seconds =
GetQuicReducedPingTimeoutSeconds(quic_trial_params);
if (reduced_ping_timeout_seconds > 0 &&
- reduced_ping_timeout_seconds < net::kPingTimeoutSecs) {
+ reduced_ping_timeout_seconds < quic::kPingTimeoutSecs) {
params->quic_reduced_ping_timeout_seconds = reduced_ping_timeout_seconds;
}
int max_time_before_crypto_handshake_seconds =
@@ -427,7 +434,7 @@ void ConfigureQuicParams(base::StringPiece quic_trial_group,
params->quic_user_agent_id = quic_user_agent_id;
- net::QuicTransportVersionVector supported_versions =
+ quic::QuicTransportVersionVector supported_versions =
GetQuicVersions(quic_trial_params);
if (!supported_versions.empty())
params->quic_supported_versions = supported_versions;
@@ -437,17 +444,17 @@ void ConfigureQuicParams(base::StringPiece quic_trial_group,
namespace network_session_configurator {
-net::QuicTransportVersionVector ParseQuicVersions(
+quic::QuicTransportVersionVector ParseQuicVersions(
const std::string& quic_versions) {
- net::QuicTransportVersionVector supported_versions;
- net::QuicTransportVersionVector all_supported_versions =
- net::AllSupportedTransportVersions();
+ quic::QuicTransportVersionVector supported_versions;
+ quic::QuicTransportVersionVector all_supported_versions =
+ quic::AllSupportedTransportVersions();
for (const base::StringPiece& version : base::SplitStringPiece(
quic_versions, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
auto it = all_supported_versions.begin();
while (it != all_supported_versions.end()) {
- if (net::QuicVersionToString(*it) == version) {
+ if (quic::QuicVersionToString(*it) == version) {
supported_versions.push_back(*it);
// Remove the supported version to deduplicate versions extracted from
// |quic_versions|.
@@ -509,7 +516,7 @@ void ParseCommandLineAndFieldTrials(const base::CommandLine& command_line,
}
if (command_line.HasSwitch(switches::kQuicVersion)) {
- net::QuicTransportVersionVector supported_versions =
+ quic::QuicTransportVersionVector supported_versions =
network_session_configurator::ParseQuicVersions(
command_line.GetSwitchValueASCII(switches::kQuicVersion));
if (!supported_versions.empty())
diff --git a/chromium/components/network_session_configurator/browser/network_session_configurator.h b/chromium/components/network_session_configurator/browser/network_session_configurator.h
index a3e624c26b9..4b501fbee52 100644
--- a/chromium/components/network_session_configurator/browser/network_session_configurator.h
+++ b/chromium/components/network_session_configurator/browser/network_session_configurator.h
@@ -20,7 +20,7 @@ namespace network_session_configurator {
// trials and command line.
// Parse serialized QUIC versions string.
-net::QuicTransportVersionVector ParseQuicVersions(
+quic::QuicTransportVersionVector ParseQuicVersions(
const std::string& quic_versions);
// Configure |params| based on field trials and command line,
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 7e14acff63c..6235f7d927a 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
@@ -71,11 +71,23 @@ TEST_F(NetworkSessionConfiguratorTest, Defaults) {
EXPECT_EQ(0u, params_.origins_to_force_quic_on.size());
}
-TEST_F(NetworkSessionConfiguratorTest, Http2FieldTrialHttp2Disable) {
+TEST_F(NetworkSessionConfiguratorTest, Http2FieldTrialGroupNameDoesNotMatter) {
base::FieldTrialList::CreateFieldTrial("HTTP2", "Disable");
ParseFieldTrials();
+ EXPECT_TRUE(params_.enable_http2);
+}
+
+TEST_F(NetworkSessionConfiguratorTest, Http2FieldTrialDisable) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["http2_enabled"] = "false";
+ variations::AssociateVariationParams("HTTP2", "Experiment",
+ field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("HTTP2", "Experiment");
+
+ ParseFieldTrials();
+
EXPECT_FALSE(params_.enable_http2);
}
@@ -89,21 +101,20 @@ TEST_F(NetworkSessionConfiguratorTest, EnableQuicFromFieldTrialGroup) {
EXPECT_TRUE(params_.retry_without_alt_svc_on_quic_errors);
EXPECT_FALSE(params_.support_ietf_format_quic_altsvc);
EXPECT_EQ(1350u, params_.quic_max_packet_length);
- EXPECT_EQ(net::QuicTagVector(), params_.quic_connection_options);
- EXPECT_EQ(net::QuicTagVector(), params_.quic_client_connection_options);
+ EXPECT_EQ(quic::QuicTagVector(), params_.quic_connection_options);
+ EXPECT_EQ(quic::QuicTagVector(), params_.quic_client_connection_options);
EXPECT_FALSE(params_.enable_server_push_cancellation);
EXPECT_FALSE(params_.quic_close_sessions_on_ip_change);
+ EXPECT_FALSE(params_.quic_goaway_sessions_on_ip_change);
EXPECT_EQ(net::kIdleConnectionTimeoutSeconds,
params_.quic_idle_connection_timeout_seconds);
- EXPECT_EQ(net::kPingTimeoutSecs, params_.quic_reduced_ping_timeout_seconds);
- EXPECT_EQ(net::kMaxTimeForCryptoHandshakeSecs,
+ EXPECT_EQ(quic::kPingTimeoutSecs, params_.quic_reduced_ping_timeout_seconds);
+ EXPECT_EQ(quic::kMaxTimeForCryptoHandshakeSecs,
params_.quic_max_time_before_crypto_handshake_seconds);
- EXPECT_EQ(net::kInitialIdleTimeoutSecs,
+ EXPECT_EQ(quic::kInitialIdleTimeoutSecs,
params_.quic_max_idle_time_before_crypto_handshake_seconds);
EXPECT_FALSE(params_.quic_race_cert_verification);
EXPECT_FALSE(params_.quic_estimate_initial_rtt);
- EXPECT_FALSE(params_.quic_migrate_sessions_on_network_change);
- EXPECT_FALSE(params_.quic_migrate_sessions_early);
EXPECT_FALSE(params_.quic_migrate_sessions_on_network_change_v2);
EXPECT_FALSE(params_.quic_migrate_sessions_early_v2);
EXPECT_FALSE(params_.quic_allow_server_migration);
@@ -182,6 +193,18 @@ TEST_F(NetworkSessionConfiguratorTest,
}
TEST_F(NetworkSessionConfiguratorTest,
+ QuicGoAwaySessionsOnIpChangeFromFieldTrialParams) {
+ std::map<std::string, std::string> field_trial_params;
+ field_trial_params["goaway_sessions_on_ip_change"] = "true";
+ variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
+ base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
+
+ ParseFieldTrials();
+
+ EXPECT_TRUE(params_.quic_goaway_sessions_on_ip_change);
+}
+
+TEST_F(NetworkSessionConfiguratorTest,
QuicIdleConnectionTimeoutSecondsFieldTrialParams) {
std::map<std::string, std::string> field_trial_params;
field_trial_params["idle_connection_timeout_seconds"] = "300";
@@ -200,7 +223,7 @@ TEST_F(NetworkSessionConfiguratorTest,
variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
ParseFieldTrials();
- EXPECT_EQ(net::kPingTimeoutSecs, params_.quic_reduced_ping_timeout_seconds);
+ EXPECT_EQ(quic::kPingTimeoutSecs, params_.quic_reduced_ping_timeout_seconds);
}
TEST_F(NetworkSessionConfiguratorTest,
@@ -210,7 +233,7 @@ TEST_F(NetworkSessionConfiguratorTest,
variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
ParseFieldTrials();
- EXPECT_EQ(net::kPingTimeoutSecs, params_.quic_reduced_ping_timeout_seconds);
+ EXPECT_EQ(quic::kPingTimeoutSecs, params_.quic_reduced_ping_timeout_seconds);
}
TEST_F(NetworkSessionConfiguratorTest,
@@ -240,7 +263,7 @@ TEST_F(NetworkSessionConfiguratorTest,
variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
ParseFieldTrials();
- EXPECT_EQ(net::kMaxTimeForCryptoHandshakeSecs,
+ EXPECT_EQ(quic::kMaxTimeForCryptoHandshakeSecs,
params_.quic_max_time_before_crypto_handshake_seconds);
}
@@ -261,7 +284,7 @@ TEST_F(NetworkSessionConfiguratorTest,
variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
ParseFieldTrials();
- EXPECT_EQ(net::kInitialIdleTimeoutSecs,
+ EXPECT_EQ(quic::kInitialIdleTimeoutSecs,
params_.quic_max_idle_time_before_crypto_handshake_seconds);
}
@@ -375,14 +398,14 @@ TEST_F(NetworkSessionConfiguratorTest, PacketLengthFromFieldTrialParams) {
TEST_F(NetworkSessionConfiguratorTest, QuicVersionFromFieldTrialParams) {
std::map<std::string, std::string> field_trial_params;
field_trial_params["quic_version"] =
- net::QuicVersionToString(net::AllSupportedTransportVersions().back());
+ quic::QuicVersionToString(quic::AllSupportedTransportVersions().back());
variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
ParseFieldTrials();
- net::QuicTransportVersionVector supported_versions;
- supported_versions.push_back(net::AllSupportedTransportVersions().back());
+ quic::QuicTransportVersionVector supported_versions;
+ supported_versions.push_back(quic::AllSupportedTransportVersions().back());
EXPECT_EQ(supported_versions, params_.quic_supported_versions);
}
@@ -390,9 +413,9 @@ TEST_F(NetworkSessionConfiguratorTest,
MultipleQuicVersionFromFieldTrialParams) {
std::map<std::string, std::string> field_trial_params;
std::string quic_versions =
- net::QuicVersionToString(net::AllSupportedTransportVersions().front()) +
+ quic::QuicVersionToString(quic::AllSupportedTransportVersions().front()) +
"," +
- net::QuicVersionToString(net::AllSupportedTransportVersions().back());
+ quic::QuicVersionToString(quic::AllSupportedTransportVersions().back());
field_trial_params["quic_version"] = quic_versions;
variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
@@ -400,18 +423,18 @@ TEST_F(NetworkSessionConfiguratorTest,
ParseFieldTrials();
- net::QuicTransportVersionVector supported_versions;
- supported_versions.push_back(net::AllSupportedTransportVersions().front());
- supported_versions.push_back(net::AllSupportedTransportVersions().back());
+ quic::QuicTransportVersionVector supported_versions;
+ supported_versions.push_back(quic::AllSupportedTransportVersions().front());
+ supported_versions.push_back(quic::AllSupportedTransportVersions().back());
EXPECT_EQ(supported_versions, params_.quic_supported_versions);
}
TEST_F(NetworkSessionConfiguratorTest, SameQuicVersionsFromFieldTrialParams) {
std::map<std::string, std::string> field_trial_params;
std::string quic_versions =
- net::QuicVersionToString(net::AllSupportedTransportVersions().front()) +
+ quic::QuicVersionToString(quic::AllSupportedTransportVersions().front()) +
"," +
- net::QuicVersionToString(net::AllSupportedTransportVersions().front());
+ quic::QuicVersionToString(quic::AllSupportedTransportVersions().front());
field_trial_params["quic_version"] = quic_versions;
variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
@@ -419,8 +442,8 @@ TEST_F(NetworkSessionConfiguratorTest, SameQuicVersionsFromFieldTrialParams) {
ParseFieldTrials();
- net::QuicTransportVersionVector supported_versions;
- supported_versions.push_back(net::AllSupportedTransportVersions().front());
+ quic::QuicTransportVersionVector supported_versions;
+ supported_versions.push_back(quic::AllSupportedTransportVersions().front());
EXPECT_EQ(supported_versions, params_.quic_supported_versions);
}
@@ -433,10 +456,10 @@ TEST_F(NetworkSessionConfiguratorTest,
ParseFieldTrials();
- net::QuicTagVector options;
- options.push_back(net::kTIME);
- options.push_back(net::kTBBR);
- options.push_back(net::kREJ);
+ quic::QuicTagVector options;
+ options.push_back(quic::kTIME);
+ options.push_back(quic::kTBBR);
+ options.push_back(quic::kREJ);
EXPECT_EQ(options, params_.quic_connection_options);
}
@@ -449,9 +472,9 @@ TEST_F(NetworkSessionConfiguratorTest,
ParseFieldTrials();
- net::QuicTagVector options;
- options.push_back(net::kTBBR);
- options.push_back(net::k1RTT);
+ quic::QuicTagVector options;
+ options.push_back(quic::kTBBR);
+ options.push_back(quic::k1RTT);
EXPECT_EQ(options, params_.quic_client_connection_options);
}
@@ -558,10 +581,10 @@ TEST_F(NetworkSessionConfiguratorTest, QuicConnectionOptions) {
"TIMER,TBBR,REJ");
ParseCommandLineAndFieldTrials(command_line);
- net::QuicTagVector expected_options;
- expected_options.push_back(net::kTIME);
- expected_options.push_back(net::kTBBR);
- expected_options.push_back(net::kREJ);
+ quic::QuicTagVector expected_options;
+ expected_options.push_back(quic::kTIME);
+ expected_options.push_back(quic::kTBBR);
+ expected_options.push_back(quic::kREJ);
EXPECT_EQ(expected_options, params_.quic_connection_options);
}
@@ -574,13 +597,13 @@ TEST_F(NetworkSessionConfiguratorTest, QuicMaxPacketLength) {
}
TEST_F(NetworkSessionConfiguratorTest, QuicVersion) {
- net::QuicTransportVersionVector supported_versions =
- net::AllSupportedTransportVersions();
+ quic::QuicTransportVersionVector supported_versions =
+ quic::AllSupportedTransportVersions();
for (const auto& version : supported_versions) {
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
command_line.AppendSwitch(switches::kEnableQuic);
command_line.AppendSwitchASCII(switches::kQuicVersion,
- net::QuicVersionToString(version));
+ quic::QuicVersionToString(version));
ParseCommandLineAndFieldTrials(command_line);
ASSERT_EQ(1u, params_.quic_supported_versions.size());
EXPECT_EQ(version, params_.quic_supported_versions[0]);
diff --git a/chromium/components/network_time/BUILD.gn b/chromium/components/network_time/BUILD.gn
index 8305b1aae93..88780a07c17 100644
--- a/chromium/components/network_time/BUILD.gn
+++ b/chromium/components/network_time/BUILD.gn
@@ -18,6 +18,7 @@ static_library("network_time") {
"//components/prefs",
"//components/variations",
"//net",
+ "//services/network/public/cpp:cpp",
]
}
@@ -37,6 +38,7 @@ source_set("unit_tests") {
"//components/variations",
"//net",
"//net:test_support",
+ "//services/network:test_support",
"//testing/gtest",
]
}
diff --git a/chromium/components/network_time/DEPS b/chromium/components/network_time/DEPS
index 7f54a2c15dd..004d910d881 100644
--- a/chromium/components/network_time/DEPS
+++ b/chromium/components/network_time/DEPS
@@ -4,4 +4,6 @@ include_rules = [
"+components/variations",
"+components/prefs",
"+net",
+ "+services/network/public/cpp",
+ "+services/network/test",
]
diff --git a/chromium/components/network_time/network_time_tracker.cc b/chromium/components/network_time/network_time_tracker.cc
index b9581ce9d78..fe7cc5eb5ab 100644
--- a/chromium/components/network_time/network_time_tracker.cc
+++ b/chromium/components/network_time/network_time_tracker.cc
@@ -30,9 +30,8 @@
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_response_writer.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
namespace network_time {
@@ -136,12 +135,8 @@ const uint8_t kKeyPubBytes[] = {
0x4e, 0xf6, 0x8f, 0x54, 0xb5, 0x0c, 0x49, 0xe9, 0xa5, 0xf1, 0x40, 0xfd,
0xd9, 0x1a, 0x92, 0x90, 0x8a, 0x67, 0x15};
-std::string GetServerProof(const net::URLFetcher* source) {
- const net::HttpResponseHeaders* response_headers =
- source->GetResponseHeaders();
- if (!response_headers) {
- return std::string();
- }
+std::string GetServerProof(
+ scoped_refptr<net::HttpResponseHeaders> response_headers) {
std::string proof;
return response_headers->EnumerateHeader(nullptr, "x-cup-server-proof",
&proof)
@@ -149,24 +144,6 @@ std::string GetServerProof(const net::URLFetcher* source) {
: std::string();
}
-// Limits the amount of data that will be buffered from the server's response.
-class SizeLimitingStringWriter : public net::URLFetcherStringWriter {
- public:
- explicit SizeLimitingStringWriter(size_t limit) : limit_(limit) {}
-
- int Write(net::IOBuffer* buffer,
- int num_bytes,
- const net::CompletionCallback& callback) override {
- if (data().length() + num_bytes > limit_) {
- return net::ERR_FILE_TOO_BIG;
- }
- return net::URLFetcherStringWriter::Write(buffer, num_bytes, callback);
- }
-
- private:
- size_t limit_;
-};
-
base::TimeDelta CheckTimeInterval() {
int64_t seconds;
const std::string param = variations::GetVariationParamValueByFeature(
@@ -205,11 +182,11 @@ NetworkTimeTracker::NetworkTimeTracker(
std::unique_ptr<base::Clock> clock,
std::unique_ptr<const base::TickClock> tick_clock,
PrefService* pref_service,
- scoped_refptr<net::URLRequestContextGetter> getter)
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: server_url_(kTimeServiceURL),
max_response_size_(1024),
backoff_(base::TimeDelta::FromMinutes(kBackoffMinutes)),
- getter_(std::move(getter)),
+ url_loader_factory_(std::move(url_loader_factory)),
clock_(std::move(clock)),
tick_clock_(std::move(tick_clock)),
pref_service_(pref_service),
@@ -491,60 +468,54 @@ void NetworkTimeTracker::CheckTime() {
}
}
})");
- // This cancels any outstanding fetch.
- time_fetcher_ = net::URLFetcher::Create(url, net::URLFetcher::GET, this,
- traffic_annotation);
- if (!time_fetcher_) {
- DVLOG(1) << "tried to make fetch happen; failed";
- return;
- }
- data_use_measurement::DataUseUserData::AttachToFetcher(
- time_fetcher_.get(),
- data_use_measurement::DataUseUserData::NETWORK_TIME_TRACKER);
- time_fetcher_->SaveResponseWithWriter(
- std::unique_ptr<net::URLFetcherResponseWriter>(
- new SizeLimitingStringWriter(max_response_size_)));
- DCHECK(getter_);
- time_fetcher_->SetRequestContext(getter_.get());
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = url;
// Not expecting any cookies, but just in case.
- time_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
- net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SEND_AUTH_DATA);
+ resource_request->load_flags =
+ net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
+ net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SEND_AUTH_DATA;
+ // This cancels any outstanding fetch.
+ time_fetcher_ = network::SimpleURLLoader::Create(std::move(resource_request),
+ traffic_annotation);
+ time_fetcher_->SetAllowHttpErrorResults(true);
+ time_fetcher_->DownloadToString(
+ url_loader_factory_.get(),
+ base::BindOnce(&NetworkTimeTracker::OnURLLoaderComplete,
+ base::Unretained(this)),
+ max_response_size_);
- time_fetcher_->Start();
fetch_started_ = tick_clock_->NowTicks();
- timer_.Stop(); // Restarted in OnURLFetchComplete().
+ timer_.Stop(); // Restarted in OnURLLoaderComplete().
}
-bool NetworkTimeTracker::UpdateTimeFromResponse() {
- if (time_fetcher_->GetStatus().status() != net::URLRequestStatus::SUCCESS ||
- time_fetcher_->GetResponseCode() != 200) {
+bool NetworkTimeTracker::UpdateTimeFromResponse(
+ std::unique_ptr<std::string> response_body) {
+ int response_code = 0;
+ if (time_fetcher_->ResponseInfo() && time_fetcher_->ResponseInfo()->headers)
+ response_code = time_fetcher_->ResponseInfo()->headers->response_code();
+ if (response_code != 200 || !response_body) {
time_query_completed_ = true;
- DVLOG(1) << "fetch failed, status=" << time_fetcher_->GetStatus().status()
- << ",code=" << time_fetcher_->GetResponseCode();
+ DVLOG(1) << "fetch failed code=" << response_code;
// The error code is negated because net errors are negative, but
// the corresponding histogram enum is positive.
base::UmaHistogramSparse("NetworkTimeTracker.UpdateTimeFetchFailed",
- -time_fetcher_->GetStatus().error());
+ -time_fetcher_->NetError());
return false;
}
- std::string response_body;
- if (!time_fetcher_->GetResponseAsString(&response_body)) {
- DVLOG(1) << "failed to get response";
- return false;
- }
+ std::string data = *response_body.get();
+
DCHECK(query_signer_);
- if (!query_signer_->ValidateResponse(response_body,
- GetServerProof(time_fetcher_.get()))) {
+ if (!query_signer_->ValidateResponse(
+ data, GetServerProof(time_fetcher_->ResponseInfo()->headers))) {
DVLOG(1) << "invalid signature";
RecordFetchValidHistogram(false);
return false;
}
- response_body = response_body.substr(5); // Skips leading )]}'\n
- std::unique_ptr<base::Value> value = base::JSONReader::Read(response_body);
+ data = data.substr(5); // Skips leading )]}'\n
+ std::unique_ptr<base::Value> value = base::JSONReader::Read(data);
if (!value) {
DVLOG(1) << "bad JSON";
RecordFetchValidHistogram(false);
@@ -588,16 +559,17 @@ bool NetworkTimeTracker::UpdateTimeFromResponse() {
return true;
}
-void NetworkTimeTracker::OnURLFetchComplete(const net::URLFetcher* source) {
+void NetworkTimeTracker::OnURLLoaderComplete(
+ std::unique_ptr<std::string> response_body) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(time_fetcher_);
- DCHECK_EQ(source, time_fetcher_.get());
time_query_completed_ = true;
// After completion of a query, whether succeeded or failed, go to sleep for a
// long time.
- if (!UpdateTimeFromResponse()) { // On error, back off.
+ if (!UpdateTimeFromResponse(
+ std::move(response_body))) { // On error, back off.
if (backoff_ < base::TimeDelta::FromDays(2)) {
backoff_ *= 2;
}
diff --git a/chromium/components/network_time/network_time_tracker.h b/chromium/components/network_time/network_time_tracker.h
index edc2cd4a541..12e1ff8b609 100644
--- a/chromium/components/network_time/network_time_tracker.h
+++ b/chromium/components/network_time/network_time_tracker.h
@@ -16,7 +16,6 @@
#include "base/time/clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
-#include "net/url_request/url_fetcher_delegate.h"
#include "url/gurl.h"
class PrefRegistrySimple;
@@ -30,10 +29,10 @@ namespace client_update_protocol {
class Ecdsa;
} // namespace client_update_protocol
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
-} // namespace net
+namespace network {
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
+} // namespace network
namespace network_time {
@@ -49,7 +48,7 @@ extern const base::Feature kNetworkTimeServiceQuerying;
// A class that receives network time updates and can provide the network time
// for a corresponding local time. This class is not thread safe.
-class NetworkTimeTracker : public net::URLFetcherDelegate {
+class NetworkTimeTracker {
public:
// Describes the result of a GetNetworkTime() call, describing whether
// network time was available and if not, why not.
@@ -92,14 +91,15 @@ class NetworkTimeTracker : public net::URLFetcherDelegate {
static void RegisterPrefs(PrefRegistrySimple* registry);
- // Constructor. Arguments may be stubbed out for tests. |getter|, if not
- // null, will cause automatic queries to a time server. Otherwise, time is
- // available only if |UpdateNetworkTime| is called.
- NetworkTimeTracker(std::unique_ptr<base::Clock> clock,
- std::unique_ptr<const base::TickClock> tick_clock,
- PrefService* pref_service,
- scoped_refptr<net::URLRequestContextGetter> getter);
- ~NetworkTimeTracker() override;
+ // Constructor. Arguments may be stubbed out for tests. |url_loader_factory|
+ // must be non-null unless the kNetworkTimeServiceQuerying is disabled.
+ // Otherwise, time is available only if |UpdateNetworkTime| is called.
+ NetworkTimeTracker(
+ std::unique_ptr<base::Clock> clock,
+ std::unique_ptr<const base::TickClock> tick_clock,
+ PrefService* pref_service,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+ ~NetworkTimeTracker();
// Sets |network_time| to an estimate of the true time. Returns
// NETWORK_TIME_AVAILABLE if time is available. If |uncertainty| is
@@ -163,11 +163,10 @@ class NetworkTimeTracker : public net::URLFetcherDelegate {
// Updates network time from a time server response, returning true
// if successful.
- bool UpdateTimeFromResponse();
+ bool UpdateTimeFromResponse(std::unique_ptr<std::string> response_body);
- // net::URLFetcherDelegate:
// Called to process responses from the secure time service.
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ void OnURLLoaderComplete(std::unique_ptr<std::string> response_body);
// Sets the next time query to be run at the specified time.
void QueueCheckTime(base::TimeDelta delay);
@@ -185,8 +184,8 @@ class NetworkTimeTracker : public net::URLFetcherDelegate {
// changing the delay of this timer, with the result that CheckTime() may
// assume that if it runs, it is eligible to issue a time query.
base::RepeatingTimer timer_;
- scoped_refptr<net::URLRequestContextGetter> getter_;
- std::unique_ptr<net::URLFetcher> time_fetcher_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+ std::unique_ptr<network::SimpleURLLoader> time_fetcher_;
base::TimeTicks fetch_started_;
std::unique_ptr<client_update_protocol::Ecdsa> query_signer_;
diff --git a/chromium/components/network_time/network_time_tracker_unittest.cc b/chromium/components/network_time/network_time_tracker_unittest.cc
index 6221344ab6a..bc36dd09ff1 100644
--- a/chromium/components/network_time/network_time_tracker_unittest.cc
+++ b/chromium/components/network_time/network_time_tracker_unittest.cc
@@ -12,7 +12,8 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_clock.h"
#include "base/test/simple_test_tick_clock.h"
#include "components/client_update_protocol/ecdsa.h"
@@ -22,8 +23,7 @@
#include "net/http/http_response_headers.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_response.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_test_util.h"
+#include "services/network/test/test_shared_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network_time {
@@ -47,24 +47,25 @@ class NetworkTimeTrackerTest : public ::testing::Test {
~NetworkTimeTrackerTest() override {}
NetworkTimeTrackerTest()
- : io_thread_("IO thread"),
+ : task_environment_(
+ base::test::ScopedTaskEnvironment::MainThreadType::IO),
field_trial_test_(new FieldTrialTest()),
clock_(new base::SimpleTestClock),
tick_clock_(new base::SimpleTestTickClock),
test_server_(new net::EmbeddedTestServer) {
- base::Thread::Options thread_options;
- thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
- EXPECT_TRUE(io_thread_.StartWithOptions(thread_options));
NetworkTimeTracker::RegisterPrefs(pref_service_.registry());
field_trial_test_->SetNetworkQueriesWithVariationsService(
true, 0.0 /* query probability */,
NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
+ shared_url_loader_factory_ =
+ base::MakeRefCounted<network::TestSharedURLLoaderFactory>();
+
tracker_.reset(new NetworkTimeTracker(
std::unique_ptr<base::Clock>(clock_),
std::unique_ptr<const base::TickClock>(tick_clock_), &pref_service_,
- new net::TestURLRequestContextGetter(io_thread_.task_runner())));
+ shared_url_loader_factory_));
// Do this to be sure that |is_null| returns false.
clock_->Advance(base::TimeDelta::FromDays(111));
@@ -76,8 +77,6 @@ class NetworkTimeTrackerTest : public ::testing::Test {
adjustment_ = 7 * base::TimeDelta::FromMilliseconds(kTicksResolutionMs);
}
- void TearDown() override { io_thread_.Stop(); }
-
// Replaces |tracker_| with a new object, while preserving the
// testing clocks.
void Reset() {
@@ -90,7 +89,7 @@ class NetworkTimeTrackerTest : public ::testing::Test {
tracker_.reset(new NetworkTimeTracker(
std::unique_ptr<base::Clock>(clock_),
std::unique_ptr<const base::TickClock>(tick_clock_), &pref_service_,
- new net::TestURLRequestContextGetter(io_thread_.task_runner())));
+ shared_url_loader_factory_));
}
// Good signature over invalid data, though made with a non-production key.
@@ -156,9 +155,8 @@ class NetworkTimeTrackerTest : public ::testing::Test {
}
protected:
- base::Thread io_thread_;
+ base::test::ScopedTaskEnvironment task_environment_;
std::unique_ptr<FieldTrialTest> field_trial_test_;
- base::MessageLoop message_loop_;
base::TimeDelta resolution_;
base::TimeDelta latency_;
base::TimeDelta adjustment_;
@@ -167,6 +165,7 @@ class NetworkTimeTrackerTest : public ::testing::Test {
TestingPrefServiceSimple pref_service_;
std::unique_ptr<NetworkTimeTracker> tracker_;
std::unique_ptr<net::EmbeddedTestServer> test_server_;
+ scoped_refptr<network::TestSharedURLLoaderFactory> shared_url_loader_factory_;
};
TEST_F(NetworkTimeTrackerTest, Uninitialized) {
diff --git a/chromium/components/ntp_snippets/BUILD.gn b/chromium/components/ntp_snippets/BUILD.gn
index 407a0b0486e..65c2b85e552 100644
--- a/chromium/components/ntp_snippets/BUILD.gn
+++ b/chromium/components/ntp_snippets/BUILD.gn
@@ -52,6 +52,8 @@ static_library("ntp_snippets") {
"contextual/contextual_content_suggestions_service_proxy.h",
"contextual/contextual_suggestion.cc",
"contextual/contextual_suggestion.h",
+ "contextual/contextual_suggestions_cache.cc",
+ "contextual/contextual_suggestions_cache.h",
"contextual/contextual_suggestions_composite_reporter.cc",
"contextual/contextual_suggestions_composite_reporter.h",
"contextual/contextual_suggestions_debugging_reporter.cc",
diff --git a/chromium/components/ntp_snippets/breaking_news/breaking_news_gcm_app_handler_unittest.cc b/chromium/components/ntp_snippets/breaking_news/breaking_news_gcm_app_handler_unittest.cc
index 87daac8bfdb..37e0f2bdc0d 100644
--- a/chromium/components/ntp_snippets/breaking_news/breaking_news_gcm_app_handler_unittest.cc
+++ b/chromium/components/ntp_snippets/breaking_news/breaking_news_gcm_app_handler_unittest.cc
@@ -10,7 +10,7 @@
#include "base/bind_helpers.h"
#include "base/json/json_reader.h"
#include "base/message_loop/message_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/simple_test_clock.h"
#include "base/test/test_mock_time_task_runner.h"
diff --git a/chromium/components/ntp_snippets/breaking_news/subscription_json_request.cc b/chromium/components/ntp_snippets/breaking_news/subscription_json_request.cc
index 7e000ca70b2..641a66339f0 100644
--- a/chromium/components/ntp_snippets/breaking_news/subscription_json_request.cc
+++ b/chromium/components/ntp_snippets/breaking_news/subscription_json_request.cc
@@ -12,13 +12,11 @@
#include "components/variations/net/variations_http_headers.h"
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.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"
-using net::URLFetcher;
-using net::URLRequestContextGetter;
using net::HttpRequestHeaders;
-using net::URLRequestStatus;
namespace ntp_snippets {
@@ -30,25 +28,32 @@ SubscriptionJsonRequest::~SubscriptionJsonRequest() = default;
void SubscriptionJsonRequest::Start(CompletedCallback callback) {
DCHECK(request_completed_callback_.is_null()) << "Request already running!";
+ DCHECK(url_loader_factory_);
request_completed_callback_ = std::move(callback);
- url_fetcher_->Start();
+ simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&SubscriptionJsonRequest::OnSimpleLoaderComplete,
+ base::Unretained(this)));
}
-////////////////////////////////////////////////////////////////////////////////
-// URLFetcherDelegate overrides
-void SubscriptionJsonRequest::OnURLFetchComplete(const URLFetcher* source) {
- DCHECK_EQ(url_fetcher_.get(), source);
- const URLRequestStatus& status = url_fetcher_->GetStatus();
- int response = url_fetcher_->GetResponseCode();
+void SubscriptionJsonRequest::OnSimpleLoaderComplete(
+ std::unique_ptr<std::string> response_body) {
+ int net_error = simple_url_loader_->NetError();
- if (!status.is_success()) {
+ if (net_error != net::OK) {
std::move(request_completed_callback_)
.Run(Status(StatusCode::TEMPORARY_ERROR,
- base::StringPrintf("Network Error: %d", status.error())));
- } else if (response != net::HTTP_OK) {
+ base::StringPrintf("Network Error: %d", net_error)));
+ } else if (!response_body) {
+ int response_code = -1;
+ if (simple_url_loader_->ResponseInfo() &&
+ simple_url_loader_->ResponseInfo()->headers) {
+ response_code =
+ simple_url_loader_->ResponseInfo()->headers->response_code();
+ }
std::move(request_completed_callback_)
.Run(Status(StatusCode::PERMANENT_ERROR,
- base::StringPrintf("HTTP Error: %d", response)));
+ base::StringPrintf("HTTP Error: %d", response_code)));
} else {
std::move(request_completed_callback_)
.Run(Status(StatusCode::SUCCESS, std::string()));
@@ -63,17 +68,12 @@ SubscriptionJsonRequest::Builder::~Builder() = default;
std::unique_ptr<SubscriptionJsonRequest>
SubscriptionJsonRequest::Builder::Build() const {
DCHECK(!url_.is_empty());
- DCHECK(url_request_context_getter_);
+ DCHECK(url_loader_factory_);
auto request = base::WrapUnique(new SubscriptionJsonRequest());
std::string body = BuildBody();
- std::string headers = BuildHeaders();
- request->url_fetcher_ = BuildURLFetcher(request.get(), headers, body);
-
- // Log the request for debugging network issues.
- DVLOG(1) << "Building a subscription request to " << url_ << ":\n"
- << headers << "\n"
- << body;
+ request->simple_url_loader_ = BuildURLLoader(body);
+ request->url_loader_factory_ = std::move(url_loader_factory_);
return request;
}
@@ -91,9 +91,9 @@ SubscriptionJsonRequest::Builder& SubscriptionJsonRequest::Builder::SetUrl(
}
SubscriptionJsonRequest::Builder&
-SubscriptionJsonRequest::Builder::SetUrlRequestContextGetter(
- const scoped_refptr<URLRequestContextGetter>& context_getter) {
- url_request_context_getter_ = context_getter;
+SubscriptionJsonRequest::Builder::SetUrlLoaderFactory(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+ url_loader_factory_ = std::move(url_loader_factory);
return *this;
}
@@ -117,21 +117,6 @@ SubscriptionJsonRequest::Builder::SetCountryCode(
return *this;
}
-std::string SubscriptionJsonRequest::Builder::BuildHeaders() const {
- HttpRequestHeaders headers;
- headers.SetHeader(HttpRequestHeaders::kContentType,
- "application/json; charset=UTF-8");
- if (!auth_header_.empty()) {
- headers.SetHeader(HttpRequestHeaders::kAuthorization, auth_header_);
- }
- // Add X-Client-Data header with experiment IDs from field trials.
- // Note: It's OK to pass SignedIn::kNo if it's unknown, as it does not affect
- // transmission of experiments coming from the variations server.
- variations::AppendVariationHeaders(url_, variations::InIncognito::kNo,
- variations::SignedIn::kNo, &headers);
- return headers.ToString();
-}
-
std::string SubscriptionJsonRequest::Builder::BuildBody() const {
base::DictionaryValue request;
request.SetString("token", token_);
@@ -145,9 +130,8 @@ std::string SubscriptionJsonRequest::Builder::BuildBody() const {
return request_json;
}
-std::unique_ptr<URLFetcher> SubscriptionJsonRequest::Builder::BuildURLFetcher(
- URLFetcherDelegate* delegate,
- const std::string& headers,
+std::unique_ptr<network::SimpleURLLoader>
+SubscriptionJsonRequest::Builder::BuildURLLoader(
const std::string& body) const {
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("gcm_subscription", R"(
@@ -173,21 +157,36 @@ std::unique_ptr<URLFetcher> SubscriptionJsonRequest::Builder::BuildURLFetcher(
}
}
})");
- std::unique_ptr<URLFetcher> url_fetcher =
- URLFetcher::Create(url_, URLFetcher::POST, delegate, traffic_annotation);
- url_fetcher->SetRequestContext(url_request_context_getter_.get());
- url_fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES);
- data_use_measurement::DataUseUserData::AttachToFetcher(
- url_fetcher.get(),
- data_use_measurement::DataUseUserData::NTP_SNIPPETS_SUGGESTIONS);
-
- url_fetcher->SetExtraRequestHeaders(headers);
- url_fetcher->SetUploadData("application/json", body);
-
- // Fetchers are sometimes cancelled because a network change was detected.
- url_fetcher->SetAutomaticallyRetryOnNetworkChanges(1);
- return url_fetcher;
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = url_;
+ resource_request->load_flags =
+ net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
+ resource_request->method = "POST";
+ if (!auth_header_.empty()) {
+ resource_request->headers.SetHeader(HttpRequestHeaders::kAuthorization,
+ auth_header_);
+ }
+ // Add X-Client-Data header with experiment IDs from field trials.
+ // TODO: We should call AppendVariationHeaders with explicit
+ // variations::SignedIn::kNo If the auth_header_ is empty
+ variations::AppendVariationHeadersUnknownSignedIn(
+ url_, variations::InIncognito::kNo, &resource_request->headers);
+
+ // Log the request for debugging network issues.
+ DVLOG(1) << "Building a subscription request to " << url_ << ":\n"
+ << resource_request->headers.ToString() << "\n"
+ << body;
+
+ // TODO(https://crbug.com/808498): Re-add data use measurement once
+ // SimpleURLLoader supports it.
+ // ID=data_use_measurement::DataUseUserData::NTP_SNIPPETS_SUGGESTIONS
+ std::unique_ptr<network::SimpleURLLoader> loader =
+ network::SimpleURLLoader::Create(std::move(resource_request),
+ traffic_annotation);
+ loader->AttachStringForUpload(body, "application/json; charset=UTF-8");
+ loader->SetRetryOptions(1, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
+
+ return loader;
}
} // namespace internal
diff --git a/chromium/components/ntp_snippets/breaking_news/subscription_json_request.h b/chromium/components/ntp_snippets/breaking_news/subscription_json_request.h
index c82162b7880..217b512636f 100644
--- a/chromium/components/ntp_snippets/breaking_news/subscription_json_request.h
+++ b/chromium/components/ntp_snippets/breaking_news/subscription_json_request.h
@@ -15,17 +15,20 @@
#include "base/time/time.h"
#include "components/ntp_snippets/status.h"
#include "net/http/http_request_headers.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+} // namespace network
+
namespace ntp_snippets {
namespace internal {
// A single request to subscribe for breaking news via GCM. The Request has to
// stay alive in order to be successfully completed.
-class SubscriptionJsonRequest : public net::URLFetcherDelegate {
+class SubscriptionJsonRequest {
public:
// A client can expect a message in the status only, if there was any error
// during the subscription. In successful cases, it will be an empty string.
@@ -43,8 +46,8 @@ class SubscriptionJsonRequest : public net::URLFetcherDelegate {
Builder& SetToken(const std::string& token);
Builder& SetUrl(const GURL& url);
- Builder& SetUrlRequestContextGetter(
- const scoped_refptr<net::URLRequestContextGetter>& context_getter);
+ Builder& SetUrlLoaderFactory(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
Builder& SetAuthenticationHeader(const std::string& auth_header);
// The application language represented as an IETF language tag, defined in
@@ -58,11 +61,8 @@ class SubscriptionJsonRequest : public net::URLFetcherDelegate {
Builder& SetCountryCode(const std::string& country_code);
private:
- std::string BuildHeaders() const;
std::string BuildBody() const;
- std::unique_ptr<net::URLFetcher> BuildURLFetcher(
- net::URLFetcherDelegate* request,
- const std::string& headers,
+ std::unique_ptr<network::SimpleURLLoader> BuildURLLoader(
const std::string& body) const;
// GCM subscription token obtained from GCM driver (instanceID::getToken()).
@@ -72,13 +72,13 @@ class SubscriptionJsonRequest : public net::URLFetcherDelegate {
std::string country_code_;
GURL url_;
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
std::string auth_header_;
DISALLOW_COPY_AND_ASSIGN(Builder);
};
- ~SubscriptionJsonRequest() override;
+ ~SubscriptionJsonRequest();
// Starts an async request. The callback is invoked when the request succeeds
// or fails. The callback is not called if the request is destroyed.
@@ -87,14 +87,17 @@ class SubscriptionJsonRequest : public net::URLFetcherDelegate {
private:
friend class Builder;
SubscriptionJsonRequest();
- // URLFetcherDelegate implementation.
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
+
+ // The loader for subscribing.
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
- // The fetcher for subscribing.
- std::unique_ptr<net::URLFetcher> url_fetcher_;
+ // The loader factory for subscribing.
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
- // The callback to notify when URLFetcher finished and results are available.
- // When the request is finished/aborted/destroyed, it's called in the dtor!
+ // The callback to notify when SimpleURLLoader finished and results are
+ // available. When the request is finished/aborted/destroyed, it's called in
+ // the dtor!
CompletedCallback request_completed_callback_;
DISALLOW_COPY_AND_ASSIGN(SubscriptionJsonRequest);
diff --git a/chromium/components/ntp_snippets/breaking_news/subscription_json_request_unittest.cc b/chromium/components/ntp_snippets/breaking_news/subscription_json_request_unittest.cc
index 8ff89d1a5b6..e45af41e562 100644
--- a/chromium/components/ntp_snippets/breaking_news/subscription_json_request_unittest.cc
+++ b/chromium/components/ntp_snippets/breaking_news/subscription_json_request_unittest.cc
@@ -6,11 +6,15 @@
#include "base/json/json_reader.h"
#include "base/message_loop/message_loop.h"
+#include "base/test/bind_test_util.h"
#include "base/test/gtest_util.h"
#include "base/test/mock_callback.h"
#include "base/values.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.h"
+#include "net/http/http_util.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_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -49,42 +53,35 @@ MATCHER_P(EqualsJSON, json, "equals JSON") {
class SubscriptionJsonRequestTest : public testing::Test {
public:
SubscriptionJsonRequestTest()
- : request_context_getter_(
- new net::TestURLRequestContextGetter(message_loop_.task_runner())) {
- }
+ : test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)) {}
- scoped_refptr<net::URLRequestContextGetter> GetRequestContext() {
- return request_context_getter_.get();
+ protected:
+ scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory() {
+ return test_shared_loader_factory_;
}
- net::TestURLFetcher* GetRunningFetcher() {
- // All created TestURLFetchers have ID 0 by default.
- net::TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
- DCHECK(url_fetcher);
- return url_fetcher;
+ network::TestURLLoaderFactory* GetURLLoaderFactory() {
+ return &test_url_loader_factory_;
}
- void RespondWithData(const std::string& data) {
- net::TestURLFetcher* url_fetcher = GetRunningFetcher();
- url_fetcher->set_status(net::URLRequestStatus());
- url_fetcher->set_response_code(net::HTTP_OK);
- url_fetcher->SetResponseString(data);
- // Call the URLFetcher delegate to continue the test.
- url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+ void RespondWithData(const GURL& url, const std::string& data) {
+ GetURLLoaderFactory()->AddResponse(url.spec(), data);
+ base::RunLoop().RunUntilIdle();
}
- void RespondWithError(int error_code) {
- net::TestURLFetcher* url_fetcher = GetRunningFetcher();
- url_fetcher->set_status(net::URLRequestStatus::FromError(error_code));
- url_fetcher->SetResponseString(std::string());
- // Call the URLFetcher delegate to continue the test.
- url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+ void RespondWithError(const GURL& url, int error_code) {
+ network::URLLoaderCompletionStatus status(error_code);
+ test_url_loader_factory_.AddResponse(url, network::ResourceResponseHead(),
+ "", status);
+ base::RunLoop().RunUntilIdle();
}
private:
base::MessageLoop message_loop_;
- scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
- net::TestURLFetcherFactory url_fetcher_factory_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
DISALLOW_COPY_AND_ASSIGN(SubscriptionJsonRequestTest);
};
@@ -95,36 +92,34 @@ TEST_F(SubscriptionJsonRequestTest, BuildRequest) {
base::MockCallback<SubscriptionJsonRequest::CompletedCallback> callback;
+ std::string expected_body = R"(
+ {
+ "token": "1234567890",
+ "locale": "en-US",
+ "country_code": "us"
+ }
+ )";
+ std::string header;
+ std::string actual_body;
+ GetURLLoaderFactory()->SetInterceptor(
+ base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+ EXPECT_FALSE(request.headers.GetHeader("Authorization", &header));
+ EXPECT_TRUE(request.headers.GetHeader(
+ net::HttpRequestHeaders::kContentType, &header));
+ EXPECT_EQ(header, "application/json; charset=UTF-8");
+ actual_body = network::GetUploadData(request);
+ EXPECT_THAT(actual_body, EqualsJSON(expected_body));
+ }));
+
SubscriptionJsonRequest::Builder builder;
std::unique_ptr<SubscriptionJsonRequest> request =
builder.SetToken(token)
.SetUrl(url)
- .SetUrlRequestContextGetter(GetRequestContext())
+ .SetUrlLoaderFactory(GetSharedURLLoaderFactory())
.SetLocale("en-US")
.SetCountryCode("us")
.Build();
request->Start(callback.Get());
-
- net::TestURLFetcher* url_fetcher = GetRunningFetcher();
-
- EXPECT_EQ(url, url_fetcher->GetOriginalURL());
-
- net::HttpRequestHeaders headers;
- url_fetcher->GetExtraRequestHeaders(&headers);
-
- std::string header;
- EXPECT_FALSE(headers.GetHeader("Authorization", &header));
- EXPECT_TRUE(headers.GetHeader("Content-Type", &header));
- EXPECT_EQ(header, "application/json; charset=UTF-8");
-
- std::string expected_body = R"(
- {
- "token": "1234567890",
- "locale": "en-US",
- "country_code": "us"
- }
- )";
- EXPECT_THAT(url_fetcher->upload_data(), EqualsJSON(expected_body));
}
TEST_F(SubscriptionJsonRequestTest, ShouldNotInvokeCallbackWhenCancelled) {
@@ -138,8 +133,9 @@ TEST_F(SubscriptionJsonRequestTest, ShouldNotInvokeCallbackWhenCancelled) {
std::unique_ptr<SubscriptionJsonRequest> request =
builder.SetToken(token)
.SetUrl(url)
- .SetUrlRequestContextGetter(GetRequestContext())
+ .SetUrlLoaderFactory(GetSharedURLLoaderFactory())
.Build();
+ GetURLLoaderFactory()->AddResponse(url.spec(), "{}");
request->Start(callback.Get());
// Destroy the request before getting any response.
@@ -158,11 +154,11 @@ TEST_F(SubscriptionJsonRequestTest, SubscribeWithoutErrors) {
std::unique_ptr<SubscriptionJsonRequest> request =
builder.SetToken(token)
.SetUrl(url)
- .SetUrlRequestContextGetter(GetRequestContext())
+ .SetUrlLoaderFactory(GetSharedURLLoaderFactory())
.Build();
request->Start(callback.Get());
- RespondWithData("{}");
+ RespondWithData(url, "{}");
EXPECT_EQ(status.code, StatusCode::SUCCESS);
}
@@ -179,11 +175,11 @@ TEST_F(SubscriptionJsonRequestTest, SubscribeWithErrors) {
std::unique_ptr<SubscriptionJsonRequest> request =
builder.SetToken(token)
.SetUrl(url)
- .SetUrlRequestContextGetter(GetRequestContext())
+ .SetUrlLoaderFactory(GetSharedURLLoaderFactory())
.Build();
request->Start(callback.Get());
- RespondWithError(net::ERR_TIMED_OUT);
+ RespondWithError(url, net::ERR_TIMED_OUT);
EXPECT_EQ(status.code, StatusCode::TEMPORARY_ERROR);
}
diff --git a/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.cc b/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.cc
index 9770ca51ec0..1be2fbde66b 100644
--- a/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.cc
+++ b/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.cc
@@ -17,6 +17,7 @@
#include "components/variations/service/variations_service.h"
#include "net/base/url_util.h"
#include "services/identity/public/cpp/primary_account_access_token_fetcher.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace ntp_snippets {
@@ -30,7 +31,7 @@ const char kAuthorizationRequestHeaderFormat[] = "Bearer %s";
} // namespace
SubscriptionManagerImpl::SubscriptionManagerImpl(
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* pref_service,
variations::VariationsService* variations_service,
identity::IdentityManager* identity_manager,
@@ -38,7 +39,7 @@ SubscriptionManagerImpl::SubscriptionManagerImpl(
const std::string& api_key,
const GURL& subscribe_url,
const GURL& unsubscribe_url)
- : url_request_context_getter_(std::move(url_request_context_getter)),
+ : url_loader_factory_(std::move(url_loader_factory)),
pref_service_(pref_service),
variations_service_(variations_service),
identity_manager_(identity_manager),
@@ -69,8 +70,7 @@ void SubscriptionManagerImpl::SubscribeInternal(
const std::string& subscription_token,
const std::string& access_token) {
SubscriptionJsonRequest::Builder builder;
- builder.SetToken(subscription_token)
- .SetUrlRequestContextGetter(url_request_context_getter_);
+ builder.SetToken(subscription_token).SetUrlLoaderFactory(url_loader_factory_);
if (!access_token.empty()) {
builder.SetUrl(subscribe_url_);
@@ -101,30 +101,26 @@ void SubscriptionManagerImpl::StartAccessTokenRequest(
}
OAuth2TokenService::ScopeSet scopes = {kContentSuggestionsApiScope};
- access_token_fetcher_ =
- identity_manager_->CreateAccessTokenFetcherForPrimaryAccount(
- "ntp_snippets", scopes,
- base::BindOnce(&SubscriptionManagerImpl::AccessTokenFetchFinished,
- base::Unretained(this), subscription_token),
- identity::PrimaryAccountAccessTokenFetcher::Mode::
- kWaitUntilAvailable);
+ access_token_fetcher_ = std::make_unique<
+ identity::PrimaryAccountAccessTokenFetcher>(
+ "ntp_snippets", identity_manager_, scopes,
+ base::BindOnce(&SubscriptionManagerImpl::AccessTokenFetchFinished,
+ base::Unretained(this), subscription_token),
+ identity::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable);
}
void SubscriptionManagerImpl::AccessTokenFetchFinished(
const std::string& subscription_token,
- const GoogleServiceAuthError& error,
- const std::string& access_token) {
- // Delete the fetcher only after we leave this method (which is called from
- // the fetcher itself).
- std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher>
- access_token_fetcher_deleter(std::move(access_token_fetcher_));
+ GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info) {
+ access_token_fetcher_.reset();
if (error.state() != GoogleServiceAuthError::NONE) {
// In case of error, we will retry on next Chrome restart.
return;
}
- DCHECK(!access_token.empty());
- SubscribeInternal(subscription_token, access_token);
+ DCHECK(!access_token_info.token.empty());
+ SubscribeInternal(subscription_token, access_token_info.token);
}
void SubscriptionManagerImpl::DidSubscribe(
@@ -171,8 +167,7 @@ void SubscriptionManagerImpl::ResubscribeInternal(
}
SubscriptionJsonRequest::Builder builder;
- builder.SetToken(old_token).SetUrlRequestContextGetter(
- url_request_context_getter_);
+ builder.SetToken(old_token).SetUrlLoaderFactory(url_loader_factory_);
builder.SetUrl(
net::AppendQueryParameter(unsubscribe_url_, kApiKeyParamName, api_key_));
diff --git a/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.h b/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.h
index 17658d4035a..9bf77fd7d7f 100644
--- a/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.h
+++ b/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl.h
@@ -11,7 +11,7 @@
#include "base/memory/ref_counted.h"
#include "components/ntp_snippets/breaking_news/subscription_json_request.h"
#include "components/ntp_snippets/breaking_news/subscription_manager.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/identity/public/cpp/access_token_info.h"
#include "services/identity/public/cpp/identity_manager.h"
#include "url/gurl.h"
@@ -22,6 +22,10 @@ namespace identity {
class PrimaryAccountAccessTokenFetcher;
}
+namespace network {
+class SharedURLLoaderFactory;
+}
+
namespace variations {
class VariationsService;
}
@@ -37,7 +41,7 @@ class SubscriptionManagerImpl : public SubscriptionManager,
public identity::IdentityManager::Observer {
public:
SubscriptionManagerImpl(
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* pref_service,
variations::VariationsService* variations_service,
identity::IdentityManager* identity_manager,
@@ -86,10 +90,10 @@ class SubscriptionManagerImpl : public SubscriptionManager,
// access token.
void StartAccessTokenRequest(const std::string& subscription_token);
void AccessTokenFetchFinished(const std::string& subscription_token,
- const GoogleServiceAuthError& error,
- const std::string& access_token);
+ GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info);
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
std::unique_ptr<internal::SubscriptionJsonRequest> request_;
std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher>
diff --git a/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl_unittest.cc b/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl_unittest.cc
index 0851b482aa9..b38cffec4b3 100644
--- a/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl_unittest.cc
+++ b/chromium/components/ntp_snippets/breaking_news/subscription_manager_impl_unittest.cc
@@ -6,15 +6,16 @@
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "build/build_config.h"
#include "components/ntp_snippets/pref_names.h"
#include "components/ntp_snippets/remote/test_utils.h"
#include "components/prefs/testing_pref_service.h"
#include "net/base/net_errors.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.h"
#include "services/identity/public/cpp/identity_test_environment.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_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -40,17 +41,17 @@ const char kUnsubscriptionUrlSignedOut[] =
class SubscriptionManagerImplTest : public testing::Test {
public:
SubscriptionManagerImplTest()
- : request_context_getter_(
- new net::TestURLRequestContextGetter(message_loop_.task_runner())) {
- }
+ : test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)) {}
void SetUp() override {
SubscriptionManagerImpl::RegisterProfilePrefs(
utils_.pref_service()->registry());
}
- scoped_refptr<net::URLRequestContextGetter> GetRequestContext() {
- return request_context_getter_.get();
+ scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory() {
+ return test_shared_loader_factory_;
}
PrefService* GetPrefService() { return utils_.pref_service(); }
@@ -61,62 +62,43 @@ class SubscriptionManagerImplTest : public testing::Test {
std::unique_ptr<SubscriptionManagerImpl> BuildSubscriptionManager() {
return std::make_unique<SubscriptionManagerImpl>(
- GetRequestContext(), GetPrefService(),
+ GetSharedURLLoaderFactory(), GetPrefService(),
/*variations_service=*/nullptr,
GetIdentityTestEnv()->identity_manager(),
/*locale=*/"", kAPIKey, GURL(kSubscriptionUrl),
GURL(kUnsubscriptionUrl));
}
- net::TestURLFetcher* GetRunningFetcher() {
- // All created TestURLFetchers have ID 0 by default.
- net::TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
- DCHECK(url_fetcher);
- return url_fetcher;
- }
-
void RespondToSubscriptionRequestSuccessfully(bool is_signed_in) {
- net::TestURLFetcher* url_fetcher = GetRunningFetcher();
if (is_signed_in) {
- ASSERT_EQ(GURL(kSubscriptionUrlSignedIn), url_fetcher->GetOriginalURL());
+ RespondSuccessfully(GURL(kSubscriptionUrlSignedIn));
} else {
- ASSERT_EQ(GURL(kSubscriptionUrlSignedOut), url_fetcher->GetOriginalURL());
+ RespondSuccessfully(GURL(kSubscriptionUrlSignedOut));
}
- RespondSuccessfully();
}
void RespondToUnsubscriptionRequestSuccessfully(bool is_signed_in) {
- net::TestURLFetcher* url_fetcher = GetRunningFetcher();
if (is_signed_in) {
- ASSERT_EQ(GURL(kUnsubscriptionUrlSignedIn),
- url_fetcher->GetOriginalURL());
+ RespondSuccessfully(GURL(kUnsubscriptionUrlSignedIn));
} else {
- ASSERT_EQ(GURL(kUnsubscriptionUrlSignedOut),
- url_fetcher->GetOriginalURL());
+ RespondSuccessfully(GURL(kUnsubscriptionUrlSignedOut));
}
- RespondSuccessfully();
}
void RespondToSubscriptionWithError(bool is_signed_in, int error_code) {
- net::TestURLFetcher* url_fetcher = GetRunningFetcher();
if (is_signed_in) {
- ASSERT_EQ(GURL(kSubscriptionUrlSignedIn), url_fetcher->GetOriginalURL());
+ RespondWithError(GURL(kSubscriptionUrlSignedIn), error_code);
} else {
- ASSERT_EQ(GURL(kSubscriptionUrlSignedOut), url_fetcher->GetOriginalURL());
+ RespondWithError(GURL(kSubscriptionUrlSignedOut), error_code);
}
- RespondWithError(error_code);
}
void RespondToUnsubscriptionWithError(bool is_signed_in, int error_code) {
- net::TestURLFetcher* url_fetcher = GetRunningFetcher();
if (is_signed_in) {
- ASSERT_EQ(GURL(kUnsubscriptionUrlSignedIn),
- url_fetcher->GetOriginalURL());
+ RespondWithError(GURL(kUnsubscriptionUrlSignedIn), error_code);
} else {
- ASSERT_EQ(GURL(kUnsubscriptionUrlSignedOut),
- url_fetcher->GetOriginalURL());
+ RespondWithError(GURL(kUnsubscriptionUrlSignedOut), error_code);
}
- RespondWithError(error_code);
}
#if !defined(OS_CHROMEOS)
@@ -128,28 +110,23 @@ class SubscriptionManagerImplTest : public testing::Test {
#endif // !defined(OS_CHROMEOS)
private:
- void RespondSuccessfully() {
- net::TestURLFetcher* url_fetcher = GetRunningFetcher();
- url_fetcher->set_status(net::URLRequestStatus());
- url_fetcher->set_response_code(net::HTTP_OK);
- url_fetcher->SetResponseString(std::string());
- // Call the URLFetcher delegate to continue the test.
- url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+ void RespondSuccessfully(const GURL& url) {
+ test_url_loader_factory_.AddResponse(url.spec(), "");
+ base::RunLoop().RunUntilIdle();
}
- void RespondWithError(int error_code) {
- net::TestURLFetcher* url_fetcher = GetRunningFetcher();
- url_fetcher->set_status(net::URLRequestStatus::FromError(error_code));
- url_fetcher->SetResponseString(std::string());
- // Call the URLFetcher delegate to continue the test.
- url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+ void RespondWithError(const GURL& url, int error_code) {
+ network::URLLoaderCompletionStatus status(error_code);
+ test_url_loader_factory_.AddResponse(url, network::ResourceResponseHead(),
+ "", status);
+ base::RunLoop().RunUntilIdle();
}
base::MessageLoop message_loop_;
test::RemoteSuggestionsTestUtils utils_;
identity::IdentityTestEnvironment identity_test_env_;
- scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
- net::TestURLFetcherFactory url_fetcher_factory_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
};
TEST_F(SubscriptionManagerImplTest, SubscribeSuccessfully) {
diff --git a/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc b/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc
index bdc779d1e6d..b6ff4b819d8 100644
--- a/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc
+++ b/chromium/components/ntp_snippets/category_rankers/click_based_category_ranker_unittest.cc
@@ -7,7 +7,7 @@
#include <utility>
#include "base/strings/string_number_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/simple_test_clock.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
diff --git a/chromium/components/ntp_snippets/content_suggestions_metrics_unittest.cc b/chromium/components/ntp_snippets/content_suggestions_metrics_unittest.cc
index a027adcb969..b77666c8b49 100644
--- a/chromium/components/ntp_snippets/content_suggestions_metrics_unittest.cc
+++ b/chromium/components/ntp_snippets/content_suggestions_metrics_unittest.cc
@@ -4,7 +4,7 @@
#include "components/ntp_snippets/content_suggestions_metrics.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "components/ntp_snippets/category.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service.cc b/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service.cc
index c88ce6ad113..7c7ba9aeb0a 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service.cc
@@ -11,11 +11,11 @@
#include "base/bind.h"
#include "base/memory/ptr_util.h"
+#include "components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_result.h"
#include "components/ntp_snippets/remote/cached_image_fetcher.h"
#include "components/ntp_snippets/remote/remote_suggestions_database.h"
#include "components/ntp_snippets/remote/remote_suggestions_provider_impl.h"
-#include "contextual_content_suggestions_service_proxy.h"
#include "ui/gfx/image/image.h"
namespace contextual_suggestions {
@@ -43,6 +43,7 @@ ContextualContentSuggestionsService::ContextualContentSuggestionsService(
std::unique_ptr<ContextualSuggestionsReporterProvider> reporter_provider)
: contextual_suggestions_database_(
std::move(contextual_suggestions_database)),
+ fetch_cache_(kFetchCacheCapacity),
contextual_suggestions_fetcher_(
std::move(contextual_suggestions_fetcher)),
image_fetcher_(std::move(image_fetcher)),
@@ -56,15 +57,17 @@ void ContextualContentSuggestionsService::FetchContextualSuggestionClusters(
FetchClustersCallback callback,
ReportFetchMetricsCallback metrics_callback) {
// TODO(pnoland): Also check that the url is safe.
- if (IsEligibleURL(url)) {
+ ContextualSuggestionsResult result;
+ if (IsEligibleURL(url) && !fetch_cache_.GetSuggestionsResult(url, &result)) {
FetchClustersCallback internal_callback = base::BindOnce(
&ContextualContentSuggestionsService::FetchDone, base::Unretained(this),
- std::move(callback), metrics_callback);
+ url, std::move(callback), metrics_callback);
contextual_suggestions_fetcher_->FetchContextualSuggestionsClusters(
url, std::move(internal_callback), metrics_callback);
+ } else if (result.peek_conditions.confidence < kMinimumConfidence) {
+ BelowConfidenceThresholdFetchDone(std::move(callback), metrics_callback);
} else {
- std::move(callback).Run(
- ContextualSuggestionsResult("", {}, PeekConditions()));
+ std::move(callback).Run(result);
}
}
@@ -78,19 +81,38 @@ void ContextualContentSuggestionsService::FetchContextualSuggestionImage(
}
void ContextualContentSuggestionsService::FetchDone(
+ const GURL& url,
FetchClustersCallback callback,
ReportFetchMetricsCallback metrics_callback,
ContextualSuggestionsResult result) {
+ // We still want to cache low confidence results so that we avoid doing
+ // unnecessary fetches.
+ if (result.clusters.size() > 0) {
+ fetch_cache_.AddSuggestionsResult(url, result);
+ }
+
if (result.peek_conditions.confidence < kMinimumConfidence) {
- metrics_callback.Run(contextual_suggestions::FETCH_BELOW_THRESHOLD);
- std::move(callback).Run(
- ContextualSuggestionsResult("", {}, PeekConditions()));
+ BelowConfidenceThresholdFetchDone(std::move(callback), metrics_callback);
return;
}
std::move(callback).Run(result);
}
+ContextualSuggestionsDebuggingReporter*
+ContextualContentSuggestionsService::GetDebuggingReporter() {
+ return reporter_provider_->GetDebuggingReporter();
+}
+
+base::flat_map<GURL, ContextualSuggestionsResult>
+ContextualContentSuggestionsService::GetAllCachedResultsForDebugging() {
+ return fetch_cache_.GetAllCachedResultsForDebugging();
+}
+
+void ContextualContentSuggestionsService::ClearCachedResultsForDebugging() {
+ fetch_cache_.Clear();
+}
+
std::unique_ptr<
contextual_suggestions::ContextualContentSuggestionsServiceProxy>
ContextualContentSuggestionsService::CreateProxy() {
@@ -99,4 +121,12 @@ ContextualContentSuggestionsService::CreateProxy() {
this, reporter_provider_->CreateReporter());
}
+void ContextualContentSuggestionsService::BelowConfidenceThresholdFetchDone(
+ FetchClustersCallback callback,
+ ReportFetchMetricsCallback metrics_callback) {
+ metrics_callback.Run(contextual_suggestions::FETCH_BELOW_THRESHOLD);
+ std::move(callback).Run(ContextualSuggestionsResult("", {}, PeekConditions(),
+ ServerExperimentInfos()));
+}
+
} // namespace contextual_suggestions
diff --git a/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service.h b/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service.h
index 23214ede416..1100a89f4f9 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service.h
+++ b/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service.h
@@ -16,6 +16,8 @@
#include "components/keyed_service/core/keyed_service.h"
#include "components/ntp_snippets/callbacks.h"
#include "components/ntp_snippets/content_suggestion.h"
+#include "components/ntp_snippets/contextual/contextual_suggestions_cache.h"
+#include "components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_fetcher.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_reporter.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
@@ -27,6 +29,8 @@ class RemoteSuggestionsDatabase;
namespace contextual_suggestions {
+static constexpr int kFetchCacheCapacity = 10;
+
class ContextualContentSuggestionsServiceProxy;
// Retrieves contextual suggestions for a given URL and fetches images
@@ -57,17 +61,34 @@ class ContextualContentSuggestionsService : public KeyedService {
const GURL& url,
ntp_snippets::ImageFetchedCallback callback);
- void FetchDone(FetchClustersCallback callback,
+ void FetchDone(const GURL& url,
+ FetchClustersCallback callback,
ReportFetchMetricsCallback metrics_callback,
ContextualSuggestionsResult result);
+ // Used to surface metrics events via chrome://eoc-internals.
+ ContextualSuggestionsDebuggingReporter* GetDebuggingReporter();
+
+ // Expose cached results for debugging.
+ base::flat_map<GURL, ContextualSuggestionsResult>
+ GetAllCachedResultsForDebugging();
+
+ // Clear the cached results for debugging.
+ void ClearCachedResultsForDebugging();
+
std::unique_ptr<ContextualContentSuggestionsServiceProxy> CreateProxy();
private:
+ void BelowConfidenceThresholdFetchDone(
+ FetchClustersCallback callback,
+ ReportFetchMetricsCallback metrics_callback);
// Cache for images of contextual suggestions, needed by CachedImageFetcher.
std::unique_ptr<ntp_snippets::RemoteSuggestionsDatabase>
contextual_suggestions_database_;
+ // Cache of contextual suggestions fetch results, keyed by the context url.
+ ContextualSuggestionsCache fetch_cache_;
+
// Performs actual network request to fetch contextual suggestions.
std::unique_ptr<ContextualSuggestionsFetcher> contextual_suggestions_fetcher_;
diff --git a/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy_unittest.cc b/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy_unittest.cc
index e0de6e85134..5bbe7dcd40f 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy_unittest.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service_proxy_unittest.cc
@@ -6,6 +6,7 @@
#include <memory>
#include <string>
+#include <utility>
#include "base/bind.h"
#include "components/ntp_snippets/contextual/contextual_content_suggestions_service.h"
@@ -23,8 +24,8 @@ namespace contextual_suggestions {
namespace {
-static const std::string kTestPeekText("Test peek test");
-static const std::string kValidFromUrl = "http://some.url";
+static constexpr char kTestPeekText[] = "Test peek test";
+static constexpr char kValidFromUrl[] = "http://some.url";
class FakeContextualContentSuggestionsService
: public ContextualContentSuggestionsService {
@@ -47,14 +48,13 @@ class FakeContextualContentSuggestionsService
FetchClustersCallback clusters_callback_;
};
-} // namespace
-
FakeContextualContentSuggestionsService::
FakeContextualContentSuggestionsService()
: ContextualContentSuggestionsService(nullptr, nullptr, nullptr, nullptr) {}
FakeContextualContentSuggestionsService::
~FakeContextualContentSuggestionsService() {}
+} // namespace
class ContextualContentSuggestionsServiceProxyTest : public testing::Test {
public:
@@ -83,7 +83,10 @@ TEST_F(ContextualContentSuggestionsServiceProxyTest,
proxy()->FetchContextualSuggestions(GURL(kValidFromUrl),
mock_cluster_callback.ToOnceCallback());
- service()->RunClustersCallback(ContextualSuggestionsResult());
+ service()->RunClustersCallback(
+ ContextualSuggestionsResult(kTestPeekText, std::vector<Cluster>(),
+ PeekConditions(), ServerExperimentInfos()));
+
EXPECT_TRUE(mock_cluster_callback.has_run);
}
diff --git a/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service_unittest.cc b/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service_unittest.cc
index b44bebfa921..015b1569f74 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service_unittest.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_content_suggestions_service_unittest.cc
@@ -13,6 +13,7 @@
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
#include "base/test/mock_callback.h"
#include "components/image_fetcher/core/image_fetcher_impl.h"
#include "components/ntp_snippets/category_info.h"
@@ -82,10 +83,10 @@ class FakeContextualSuggestionsFetcher : public ContextualSuggestionsFetcher {
// Always fetches a fake image if the given URL is valid.
class FakeCachedImageFetcher : public CachedImageFetcher {
public:
- FakeCachedImageFetcher(PrefService* pref_service)
+ explicit FakeCachedImageFetcher(PrefService* pref_service)
: CachedImageFetcher(std::unique_ptr<image_fetcher::ImageFetcher>(),
pref_service,
- nullptr){};
+ nullptr) {}
void FetchSuggestionImage(const ContentSuggestion::ID&,
const GURL& image_url,
@@ -202,4 +203,116 @@ TEST_F(ContextualContentSuggestionsServiceTest,
EXPECT_EQ(mock_callback.response_peek_text, std::string());
}
+TEST_F(ContextualContentSuggestionsServiceTest, ShouldCacheResults) {
+ MockClustersCallback mock_callback;
+ MockClustersCallback mock_callback2;
+ std::vector<Cluster> clusters;
+ GURL context_url("http://www.from.url");
+
+ clusters.emplace_back(ClusterBuilder("Title")
+ .AddSuggestion(SuggestionBuilder(context_url)
+ .Title("Title1")
+ .PublisherName("from.url")
+ .Snippet("Summary")
+ .ImageId("abc")
+ .Build())
+ .Build());
+ fetcher()->SetFakeResponse(clusters);
+ source()->FetchContextualSuggestionClusters(
+ context_url, mock_callback.ToOnceCallback(), base::DoNothing());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(mock_callback.has_run);
+
+ // The correct result should be present even though we haven't set the fake
+ // response.
+ source()->FetchContextualSuggestionClusters(
+ context_url, mock_callback2.ToOnceCallback(), base::DoNothing());
+ EXPECT_TRUE(mock_callback2.has_run);
+ ExpectResponsesMatch(
+ mock_callback2,
+ ContextualSuggestionsResult("peek text", clusters, PeekConditions(),
+ ServerExperimentInfos()));
+}
+
+TEST_F(ContextualContentSuggestionsServiceTest, ShouldEvictOldCachedResults) {
+ std::vector<Cluster> clusters;
+ clusters.emplace_back(
+ ClusterBuilder("Title")
+ .AddSuggestion(SuggestionBuilder(GURL("http://foobar.com"))
+ .Title("Title1")
+ .PublisherName("from.url")
+ .Snippet("Summary")
+ .ImageId("abc")
+ .Build())
+ .Build());
+
+ for (int i = 0; i < kFetchCacheCapacity + 1; i++) {
+ MockClustersCallback mock_callback;
+ GURL context_url("http://www.from.url/" + base::NumberToString(i));
+
+ fetcher()->SetFakeResponse(clusters);
+ source()->FetchContextualSuggestionClusters(
+ context_url, mock_callback.ToOnceCallback(), base::DoNothing());
+ base::RunLoop().RunUntilIdle();
+
+ ExpectResponsesMatch(
+ mock_callback,
+ ContextualSuggestionsResult("peek text", clusters, PeekConditions(),
+ ServerExperimentInfos()));
+ }
+
+ // Urls numbered kFetchCacheCapacity through 1 should be cached still; 0
+ // should have been evicted.
+ for (int i = kFetchCacheCapacity; i > 0; i--) {
+ GURL context_url("http://www.from.url/" + base::NumberToString(i));
+ MockClustersCallback mock_callback;
+ source()->FetchContextualSuggestionClusters(
+ context_url, mock_callback.ToOnceCallback(), base::DoNothing());
+ ExpectResponsesMatch(
+ mock_callback,
+ ContextualSuggestionsResult("peek text", clusters, PeekConditions(),
+ ServerExperimentInfos()));
+ }
+
+ GURL context_url("http://www.from.url/0");
+ MockClustersCallback mock_callback;
+ source()->FetchContextualSuggestionClusters(
+ context_url, mock_callback.ToOnceCallback(), base::DoNothing());
+ EXPECT_EQ(mock_callback.response_clusters.size(), 0u);
+}
+
+TEST_F(ContextualContentSuggestionsServiceTest,
+ ShouldNotReturnCachedLowConfidenceResults) {
+ MockClustersCallback mock_callback;
+ MockClustersCallback mock_callback2;
+ std::vector<Cluster> clusters;
+ GURL context_url("http://www.from.url");
+
+ clusters.emplace_back(ClusterBuilder("Title")
+ .AddSuggestion(SuggestionBuilder(context_url)
+ .Title("Title1")
+ .PublisherName("from.url")
+ .Snippet("Summary")
+ .ImageId("abc")
+ .Build())
+ .Build());
+ PeekConditions peek_conditions;
+ peek_conditions.confidence = 0;
+ fetcher()->SetFakeResponse(clusters, peek_conditions);
+ source()->FetchContextualSuggestionClusters(
+ context_url, mock_callback.ToOnceCallback(), base::DoNothing());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(mock_callback.has_run);
+ ExpectResponsesMatch(mock_callback, ContextualSuggestionsResult());
+
+ // The cached result we get back should be empty, since its confidence is
+ // below the threshold.
+ source()->FetchContextualSuggestionClusters(
+ context_url, mock_callback2.ToOnceCallback(), base::DoNothing());
+ EXPECT_TRUE(mock_callback2.has_run);
+ ExpectResponsesMatch(mock_callback2, ContextualSuggestionsResult());
+}
+
} // namespace contextual_suggestions
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestion.cc b/chromium/components/ntp_snippets/contextual/contextual_suggestion.cc
index 1155b7fc6e1..d71dcb30825 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestion.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestion.cc
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <string>
+#include <utility>
+
#include "components/ntp_snippets/contextual/contextual_suggestion.h"
namespace contextual_suggestions {
@@ -26,6 +29,9 @@ ContextualSuggestion::ContextualSuggestion(
ContextualSuggestion::~ContextualSuggestion() = default;
+ContextualSuggestion& ContextualSuggestion::operator=(
+ const ContextualSuggestion&) = default;
+
SuggestionBuilder::SuggestionBuilder(const GURL& url) {
suggestion_.url = url;
suggestion_.id = url.spec();
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestion.h b/chromium/components/ntp_snippets/contextual/contextual_suggestion.h
index d14e64a00a9..28f4b513a06 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestion.h
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestion.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTION_H_
#define COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTION_H_
+#include <string>
+
#include "url/gurl.h"
namespace contextual_suggestions {
@@ -16,6 +18,8 @@ struct ContextualSuggestion {
ContextualSuggestion(ContextualSuggestion&&) noexcept;
~ContextualSuggestion();
+ ContextualSuggestion& operator=(const ContextualSuggestion&);
+
// The ID identifying the suggestion.
std::string id;
@@ -48,7 +52,7 @@ struct ContextualSuggestion {
// order has to be guessed at.
class SuggestionBuilder {
public:
- SuggestionBuilder(const GURL& url);
+ explicit SuggestionBuilder(const GURL& url);
SuggestionBuilder& Title(const std::string& title);
SuggestionBuilder& PublisherName(const std::string& publisher_name);
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_cache.cc b/chromium/components/ntp_snippets/contextual/contextual_suggestions_cache.cc
new file mode 100644
index 00000000000..07b282d48f7
--- /dev/null
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_cache.cc
@@ -0,0 +1,41 @@
+// 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/ntp_snippets/contextual/contextual_suggestions_cache.h"
+
+namespace contextual_suggestions {
+
+ContextualSuggestionsCache::ContextualSuggestionsCache(size_t capacity)
+ : cache_(capacity) {}
+ContextualSuggestionsCache::~ContextualSuggestionsCache() = default;
+
+base::flat_map<GURL, ContextualSuggestionsResult>
+ContextualSuggestionsCache::GetAllCachedResultsForDebugging() {
+ return base::flat_map<GURL, ContextualSuggestionsResult>(cache_.begin(),
+ cache_.end());
+}
+
+bool ContextualSuggestionsCache::GetSuggestionsResult(
+ const GURL& url,
+ ContextualSuggestionsResult* result) {
+ auto cache_iter = cache_.Get(url);
+ if (cache_iter == cache_.end()) {
+ return false;
+ }
+
+ *result = cache_iter->second;
+ return true;
+}
+
+void ContextualSuggestionsCache::AddSuggestionsResult(
+ const GURL& url,
+ ContextualSuggestionsResult result) {
+ cache_.Put(url, result);
+}
+
+void ContextualSuggestionsCache::Clear() {
+ cache_.Clear();
+}
+
+} // namespace contextual_suggestions
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_cache.h b/chromium/components/ntp_snippets/contextual/contextual_suggestions_cache.h
new file mode 100644
index 00000000000..b1639bd88b8
--- /dev/null
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_cache.h
@@ -0,0 +1,44 @@
+// 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_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_CACHE_H_
+#define COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_CACHE_H_
+
+#include "base/containers/flat_map.h"
+#include "base/containers/mru_cache.h"
+#include "components/ntp_snippets/contextual/contextual_suggestions_result.h"
+#include "url/gurl.h"
+
+namespace contextual_suggestions {
+
+// Wrapper for an LRU cache of ContextualSuggestionResult objects, keyed by
+// context URL.
+class ContextualSuggestionsCache {
+ public:
+ explicit ContextualSuggestionsCache(size_t capacity);
+ ~ContextualSuggestionsCache();
+
+ // Attempts to find a result for |url| in this cache, returning true and
+ // putting the result into |result| if successful.
+ bool GetSuggestionsResult(const GURL& url,
+ ContextualSuggestionsResult* result);
+ // Adds |result| to this cache for the key |url|, overwriting any previous
+ // value associated with |url| and potentially evicting the oldest item in the
+ // cache.
+ void AddSuggestionsResult(const GURL& url,
+ ContextualSuggestionsResult result);
+ // Removes all items from the cache.
+ void Clear();
+
+ // Returns all suggestion results for debugging purposes.
+ base::flat_map<GURL, ContextualSuggestionsResult>
+ GetAllCachedResultsForDebugging();
+
+ private:
+ base::MRUCache<GURL, ContextualSuggestionsResult> cache_;
+};
+
+} // namespace contextual_suggestions
+
+#endif // COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_CACHE_H_
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.cc b/chromium/components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.cc
index c3f9a48f2d5..f7a644f3411 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.cc
@@ -23,6 +23,10 @@ ContextualSuggestionsDebuggingReporter::GetEvents() const {
return events_;
}
+void ContextualSuggestionsDebuggingReporter::ClearEvents() {
+ events_.clear();
+}
+
void ContextualSuggestionsDebuggingReporter::SetupForPage(
const std::string& url,
ukm::SourceId source_id) {
@@ -44,6 +48,9 @@ void ContextualSuggestionsDebuggingReporter::RecordEvent(
case UI_PEEK_REVERSE_SCROLL:
current_event_.sheet_peeked = true;
return;
+ case UI_BUTTON_SHOWN:
+ current_event_.button_shown = true;
+ return;
case UI_OPENED:
current_event_.sheet_opened = true;
return;
@@ -70,10 +77,12 @@ void ContextualSuggestionsDebuggingReporter::Flush() {
// Check if we've already sent an event with this url to the cache. If so,
// remove it before adding another one.
const std::string current_url = current_event_.url;
- std::remove_if(events_.begin(), events_.end(),
- [current_url](ContextualSuggestionsDebuggingEvent event) {
- return current_url == event.url;
- });
+ auto itr =
+ std::remove_if(events_.begin(), events_.end(),
+ [current_url](ContextualSuggestionsDebuggingEvent event) {
+ return current_url == event.url;
+ });
+ events_.erase(itr, events_.end());
events_.push_back(current_event_);
// If the cache is too large, then remove the least recently used.
@@ -81,4 +90,4 @@ void ContextualSuggestionsDebuggingReporter::Flush() {
events_.erase(events_.begin());
}
-} // namespace contextual_suggestions \ No newline at end of file
+} // namespace contextual_suggestions
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.h b/chromium/components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.h
index 2baf1d91e41..585cf289458 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.h
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.h
@@ -35,6 +35,9 @@ struct ContextualSuggestionsDebuggingEvent {
// Whether the sheet ever peeked.
bool sheet_peeked = false;
+
+ // Whether the button has even been shown.
+ bool button_shown = false;
};
// Reporter specialized for caching information for debugging purposes.
@@ -47,6 +50,9 @@ class ContextualSuggestionsDebuggingReporter
// Get all events currently in the buffer.
const std::vector<ContextualSuggestionsDebuggingEvent>& GetEvents() const;
+ // Clear the debugging cache of events.
+ void ClearEvents();
+
// ContextualSuggestionsReporter
void SetupForPage(const std::string& url, ukm::SourceId source_id) override;
void RecordEvent(
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_features.cc b/chromium/components/ntp_snippets/contextual/contextual_suggestions_features.cc
index 376e0afae5b..9321557f35a 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_features.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_features.cc
@@ -9,11 +9,13 @@ namespace contextual_suggestions {
const base::Feature kContextualSuggestionsBottomSheet{
"ContextualSuggestionsBottomSheet", base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kContextualSuggestionsEnterprisePolicyBypass{
- "ContextualSuggestionsEnterprisePolicyBypass",
- base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kContextualSuggestionsButton{
+ "ContextualSuggestionsButton", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kContextualSuggestionsSlimPeekUI{
"ContextualSuggestionsSlimPeekUI", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kContextualSuggestionsOptOut{
+ "ContextualSuggestionsOptOut", base::FEATURE_ENABLED_BY_DEFAULT};
+
} // namespace contextual_suggestions
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_features.h b/chromium/components/ntp_snippets/contextual/contextual_suggestions_features.h
index 9ca7f7a0e9e..11d28fac2a1 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_features.h
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_features.h
@@ -10,8 +10,9 @@
namespace contextual_suggestions {
extern const base::Feature kContextualSuggestionsBottomSheet;
-extern const base::Feature kContextualSuggestionsEnterprisePolicyBypass;
+extern const base::Feature kContextualSuggestionsButton;
extern const base::Feature kContextualSuggestionsSlimPeekUI;
+extern const base::Feature kContextualSuggestionsOptOut;
} // namespace contextual_suggestions
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_fetch.cc b/chromium/components/ntp_snippets/contextual/contextual_suggestions_fetch.cc
index e4139f3bbb3..442509405c5 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_fetch.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_fetch.cc
@@ -79,7 +79,7 @@ Cluster PivotToCluster(const PivotCluster& pivot) {
return cluster_builder.Build();
}
-PeekConditions PeekConditionsFromResponse(
+PeekConditions GetPeekConditionsFromResponse(
const GetPivotsResponse& response_proto) {
AutoPeekConditions proto_conditions =
response_proto.pivots().auto_peek_conditions();
@@ -95,7 +95,7 @@ PeekConditions PeekConditionsFromResponse(
return peek_conditions;
}
-std::vector<Cluster> ClustersFromResponse(
+std::vector<Cluster> GetClustersFromResponse(
const GetPivotsResponse& response_proto) {
std::vector<Cluster> clusters;
Pivots pivots = response_proto.pivots();
@@ -122,7 +122,7 @@ std::vector<Cluster> ClustersFromResponse(
return clusters;
}
-std::string PeekTextFromResponse(const GetPivotsResponse& response_proto) {
+std::string GetPeekTextFromResponse(const GetPivotsResponse& response_proto) {
return response_proto.pivots().peek_text().text();
}
@@ -153,12 +153,26 @@ const std::string SerializedPivotsRequest(const std::string& url,
return pivot_request.SerializeAsString();
}
+ServerExperimentInfos GetServerExperimentInfosFromResponse(
+ const GetPivotsResponse& response_proto) {
+ ServerExperimentInfos field_trials;
+ for (auto experiment_info : response_proto.pivots().experiment_info()) {
+ std::string trial_name = experiment_info.experiment_group_name();
+ std::string group_name = experiment_info.experiment_arm_name();
+ if (!trial_name.empty() && !group_name.empty())
+ field_trials.emplace_back(std::move(trial_name), std::move(group_name));
+ }
+
+ return field_trials;
+}
+
ContextualSuggestionsResult ResultFromResponse(
const GetPivotsResponse& response_proto) {
return ContextualSuggestionsResult(
- PeekTextFromResponse(response_proto),
- ClustersFromResponse(response_proto),
- PeekConditionsFromResponse(response_proto));
+ GetPeekTextFromResponse(response_proto),
+ GetClustersFromResponse(response_proto),
+ GetPeekConditionsFromResponse(response_proto),
+ GetServerExperimentInfosFromResponse(response_proto));
}
} // namespace
@@ -177,6 +191,10 @@ const std::string ContextualSuggestionsFetch::GetFetchEndpoint() {
fetch_endpoint =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
kFetchEndpointUrlKey);
+ } else if (base::FeatureList::IsEnabled(
+ contextual_suggestions::kContextualSuggestionsButton)) {
+ fetch_endpoint = base::GetFieldTrialParamValueByFeature(
+ kContextualSuggestionsButton, kFetchEndpointUrlKey);
} else {
fetch_endpoint = base::GetFieldTrialParamValueByFeature(
kContextualSuggestionsBottomSheet, kFetchEndpointUrlKey);
@@ -206,8 +224,6 @@ void ContextualSuggestionsFetch::Start(
std::unique_ptr<network::SimpleURLLoader>
ContextualSuggestionsFetch::MakeURLLoader() const {
- // TODO(pnoland, https://crbug.com/831693): Update this once there's an
- // opt-out setting.
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("ntp_contextual_suggestions_fetch",
R"(
@@ -226,8 +242,8 @@ ContextualSuggestionsFetch::MakeURLLoader() const {
policy {
cookies_allowed: NO
setting:
- "This feature can be disabled by the flag "
- "enable-contextual-suggestions-bottom-sheet."
+ "This feature can be disabled by turning off the "
+ "'Suggest related pages' in Chrome for Android settings"
policy_exception_justification: "Not implemented. The feature is "
"currently Android-only and disabled for all enterprise users. "
"A policy will be added before enabling for enterprise users."
@@ -274,11 +290,9 @@ void ContextualSuggestionsFetch::OnURLLoaderComplete(
std::unique_ptr<std::string> result) {
ContextualSuggestionsResult suggestions_result;
- int32_t response_code = 0;
- int32_t error_code = url_loader_->NetError();
if (result) {
- response_code = url_loader_->ResponseInfo()->headers->response_code();
-
+ int32_t response_code =
+ url_loader_->ResponseInfo()->headers->response_code();
if (response_code == net::HTTP_OK) {
// The response comes in the format (length, bytes) where length is a
// varint32 encoded int. Rather than hand-rolling logic to skip the
@@ -294,22 +308,38 @@ void ContextualSuggestionsFetch::OnURLLoaderComplete(
}
}
}
-
- UMA_HISTOGRAM_COUNTS_1M("ContextualSuggestions.FetchResponseSizeKB",
- static_cast<int>(result->length() / 1024));
}
- ReportFetchMetrics(error_code, response_code,
- suggestions_result.clusters.size(),
+ ReportFetchMetrics(suggestions_result.clusters.size(),
std::move(metrics_callback));
std::move(request_completed_callback_).Run(std::move(suggestions_result));
}
void ContextualSuggestionsFetch::ReportFetchMetrics(
- int32_t error_code,
- int32_t response_code,
size_t clusters_size,
ReportFetchMetricsCallback metrics_callback) {
+ int32_t error_code = url_loader_->NetError();
+ int32_t response_code = 0;
+
+ base::UmaHistogramSparse("ContextualSuggestions.FetchErrorCode", error_code);
+ if (error_code == net::OK) {
+ const network::ResourceResponseHead* response_info =
+ url_loader_->ResponseInfo();
+ response_code = response_info->headers->response_code();
+ if (response_code > 0) {
+ base::UmaHistogramSparse("ContextualSuggestions.FetchResponseCode",
+ response_code);
+
+ UMA_HISTOGRAM_COUNTS_1M("ContextualSuggestions.FetchResponseNetworkBytes",
+ response_info->encoded_data_length);
+ }
+
+ base::TimeDelta latency_delta =
+ response_info->response_time - response_info->request_time;
+ UMA_HISTOGRAM_COUNTS_1M("ContextualSuggestions.FetchLatencyMilliseconds",
+ latency_delta.InMilliseconds());
+ }
+
ContextualSuggestionsEvent event;
if (error_code != net::OK) {
event = FETCH_ERROR;
@@ -323,12 +353,6 @@ void ContextualSuggestionsFetch::ReportFetchMetrics(
event = FETCH_COMPLETED;
}
- base::UmaHistogramSparse("ContextualSuggestions.FetchErrorCode", error_code);
- if (response_code > 0) {
- base::UmaHistogramSparse("ContextualSuggestions.FetchResponseCode",
- response_code);
- }
-
std::move(metrics_callback).Run(event);
}
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_fetch.h b/chromium/components/ntp_snippets/contextual/contextual_suggestions_fetch.h
index 4fb5acbff93..8e200040151 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_fetch.h
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_fetch.h
@@ -45,9 +45,7 @@ class ContextualSuggestionsFetch {
net::HttpRequestHeaders MakeHeaders() const;
void OnURLLoaderComplete(ReportFetchMetricsCallback metrics_callback,
std::unique_ptr<std::string> result);
- void ReportFetchMetrics(int32_t error_code,
- int32_t response_code,
- size_t clusters_size,
+ void ReportFetchMetrics(size_t clusters_size,
ReportFetchMetricsCallback metrics_callback);
// The url for which we're fetching suggestions.
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_fetcher_impl_unittest.cc b/chromium/components/ntp_snippets/contextual/contextual_suggestions_fetcher_impl_unittest.cc
index 89f10c6156f..02a9487b009 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_fetcher_impl_unittest.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_fetcher_impl_unittest.cc
@@ -12,7 +12,7 @@
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/bind_test_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_result.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_test_utils.h"
@@ -20,8 +20,8 @@
#include "components/ntp_snippets/contextual/proto/get_pivots_request.pb.h"
#include "components/ntp_snippets/contextual/proto/get_pivots_response.pb.h"
#include "net/http/http_status_code.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/url_loader_completion_status.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -51,41 +51,6 @@ using testing::ElementsAre;
namespace {
-// TODO(pnoland): de-dupe this and the identical class in
-// feed_networking_host_unittest.cc
-class TestSharedURLLoaderFactory : public SharedURLLoaderFactory {
- public:
- void CreateLoaderAndStart(network::mojom::URLLoaderRequest request,
- int32_t routing_id,
- int32_t request_id,
- uint32_t options,
- const network::ResourceRequest& url_request,
- network::mojom::URLLoaderClientPtr client,
- const net::MutableNetworkTrafficAnnotationTag&
- traffic_annotation) override {
- test_factory_.CreateLoaderAndStart(std::move(request), routing_id,
- request_id, options, url_request,
- std::move(client), traffic_annotation);
- }
-
- void Clone(network::mojom::URLLoaderFactoryRequest request) override {
- NOTREACHED();
- }
-
- std::unique_ptr<network::SharedURLLoaderFactoryInfo> Clone() override {
- NOTREACHED();
- return nullptr;
- }
-
- TestURLLoaderFactory* test_factory() { return &test_factory_; }
-
- protected:
- ~TestSharedURLLoaderFactory() override = default;
-
- private:
- TestURLLoaderFactory test_factory_;
-};
-
Cluster DefaultCluster() {
return ClusterBuilder("Articles")
.AddSuggestion(SuggestionBuilder(GURL("http://www.foobar.com"))
@@ -132,7 +97,8 @@ void PopulatePeekConditions(AutoPeekConditions* proto_conditions,
std::string SerializedResponseProto(
const std::string& peek_text,
std::vector<Cluster> clusters,
- PeekConditions peek_conditions = PeekConditions()) {
+ PeekConditions peek_conditions = PeekConditions(),
+ ServerExperimentInfos experiment_infos = ServerExperimentInfos()) {
GetPivotsResponse response_proto;
Pivots* pivots = response_proto.mutable_pivots();
PopulatePeekConditions(pivots->mutable_auto_peek_conditions(),
@@ -149,6 +115,12 @@ std::string SerializedResponseProto(
}
}
+ for (const auto& experiment_info : experiment_infos) {
+ ExperimentInfo* experiment = pivots->add_experiment_info();
+ experiment->set_experiment_group_name(experiment_info.name);
+ experiment->set_experiment_arm_name(experiment_info.group);
+ }
+
// The fetch parsing logic expects the response to come as (length, bytes)
// where length is varint32 encoded, but ignores the actual length read.
// " " is a valid varint32(32) so this works for now.
@@ -178,9 +150,11 @@ std::string SerializedResponseProto(const std::string& peek_text,
class ContextualSuggestionsFetcherTest : public testing::Test {
public:
ContextualSuggestionsFetcherTest() {
- loader_factory_ = base::MakeRefCounted<TestSharedURLLoaderFactory>();
+ shared_url_loader_factory_ =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_factory_);
fetcher_ = std::make_unique<ContextualSuggestionsFetcherImpl>(
- loader_factory_, "en");
+ shared_url_loader_factory_, "en");
}
~ContextualSuggestionsFetcherTest() override {}
@@ -197,8 +171,7 @@ class ContextualSuggestionsFetcherTest : public testing::Test {
status.decoded_body_length = response_data.length();
}
- loader_factory_->test_factory()->AddResponse(fetch_url, head, response_data,
- status);
+ test_factory_.AddResponse(fetch_url, head, response_data, status);
}
void SendAndAwaitResponse(
@@ -217,13 +190,12 @@ class ContextualSuggestionsFetcherTest : public testing::Test {
ContextualSuggestionsFetcher& fetcher() { return *fetcher_; }
- TestURLLoaderFactory* test_factory() {
- return loader_factory_->test_factory();
- }
+ TestURLLoaderFactory* test_factory() { return &test_factory_; }
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
- scoped_refptr<TestSharedURLLoaderFactory> loader_factory_;
+ network::TestURLLoaderFactory test_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
std::unique_ptr<ContextualSuggestionsFetcherImpl> fetcher_;
DISALLOW_COPY_AND_ASSIGN(ContextualSuggestionsFetcherTest);
@@ -238,9 +210,10 @@ TEST_F(ContextualSuggestionsFetcherTest, SingleSuggestionResponse) {
&metrics_callback);
EXPECT_TRUE(callback.has_run);
- ExpectResponsesMatch(std::move(callback),
- ContextualSuggestionsResult(
- "Peek Text", DefaultClusters(), PeekConditions()));
+ ExpectResponsesMatch(
+ std::move(callback),
+ ContextualSuggestionsResult("Peek Text", DefaultClusters(),
+ PeekConditions(), ServerExperimentInfos()));
EXPECT_EQ(metrics_callback.events,
std::vector<ContextualSuggestionsEvent>(
{contextual_suggestions::FETCH_COMPLETED}));
@@ -304,10 +277,12 @@ TEST_F(ContextualSuggestionsFetcherTest,
ExpectResponsesMatch(
std::move(callback),
ContextualSuggestionsResult("Peek Text", std::move(clusters_copy),
- PeekConditions()));
+ PeekConditions(), ServerExperimentInfos()));
- histogram_tester.ExpectTotalCount("ContextualSuggestions.FetchResponseSizeKB",
- 1);
+ histogram_tester.ExpectTotalCount(
+ "ContextualSuggestions.FetchResponseNetworkBytes", 1);
+ histogram_tester.ExpectTotalCount(
+ "ContextualSuggestions.FetchLatencyMilliseconds", 1);
}
TEST_F(ContextualSuggestionsFetcherTest, FlatResponse) {
@@ -323,7 +298,7 @@ TEST_F(ContextualSuggestionsFetcherTest, FlatResponse) {
ExpectResponsesMatch(
std::move(callback),
ContextualSuggestionsResult("Peek Text", std::move(expected_clusters),
- PeekConditions()));
+ PeekConditions(), ServerExperimentInfos()));
}
TEST_F(ContextualSuggestionsFetcherTest, PeekConditionsAreParsed) {
@@ -342,9 +317,32 @@ TEST_F(ContextualSuggestionsFetcherTest, PeekConditionsAreParsed) {
&metrics_callback);
EXPECT_TRUE(callback.has_run);
- ExpectResponsesMatch(std::move(callback),
- ContextualSuggestionsResult(
- "Peek Text", DefaultClusters(), peek_conditions));
+ ExpectResponsesMatch(
+ std::move(callback),
+ ContextualSuggestionsResult("Peek Text", DefaultClusters(),
+ peek_conditions, ServerExperimentInfos()));
+}
+
+TEST_F(ContextualSuggestionsFetcherTest, ServerExperimentInfosAreParsed) {
+ MockClustersCallback callback;
+ MockMetricsCallback metrics_callback;
+ ServerExperimentInfos experiment_infos;
+ experiment_infos.emplace_back("trial1", "group1");
+ experiment_infos.emplace_back("trial2", "group2");
+ SetFakeResponse(SerializedResponseProto("Peek Text", DefaultClusters(),
+ PeekConditions(), experiment_infos));
+
+ SendAndAwaitResponse(GURL("http://www.article.com"), &callback,
+ &metrics_callback);
+
+ EXPECT_TRUE(callback.has_run);
+ ExpectResponsesMatch(
+ std::move(callback),
+ ContextualSuggestionsResult("Peek Text", DefaultClusters(),
+ PeekConditions(), experiment_infos));
+ EXPECT_EQ(metrics_callback.events,
+ std::vector<ContextualSuggestionsEvent>(
+ {contextual_suggestions::FETCH_COMPLETED}));
}
TEST_F(ContextualSuggestionsFetcherTest, HtmlEntitiesAreUnescaped) {
@@ -373,7 +371,7 @@ TEST_F(ContextualSuggestionsFetcherTest, HtmlEntitiesAreUnescaped) {
ExpectResponsesMatch(
std::move(callback),
ContextualSuggestionsResult("Peek Text", std::move(expected_clusters),
- PeekConditions()));
+ PeekConditions(), ServerExperimentInfos()));
}
TEST_F(ContextualSuggestionsFetcherTest, RequestHeaderSetCorrectly) {
@@ -510,9 +508,10 @@ TEST_F(ContextualSuggestionsFetcherTest, ResponseWithUnsetFields) {
EXPECT_TRUE(callback.has_run);
EXPECT_EQ(callback.response_clusters.size(), 1u);
- ExpectResponsesMatch(std::move(callback),
- ContextualSuggestionsResult(
- "", std::move(expected_clusters), PeekConditions()));
+ ExpectResponsesMatch(
+ std::move(callback),
+ ContextualSuggestionsResult("", std::move(expected_clusters),
+ PeekConditions(), ServerExperimentInfos()));
}
TEST_F(ContextualSuggestionsFetcherTest, CorruptResponse) {
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.cc b/chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.cc
index e588db611a0..08b4fe7abc6 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.cc
@@ -14,6 +14,7 @@ namespace contextual_suggestions {
ContextualSuggestionsMetricsReporter::ContextualSuggestionsMetricsReporter()
: sheet_peeked_(false),
+ button_shown_(false),
sheet_opened_(false),
sheet_closed_(false),
any_suggestion_downloaded_(false),
@@ -64,6 +65,11 @@ void ContextualSuggestionsMetricsReporter::RecordUmaMetrics(
return;
sheet_peeked_ = true;
break;
+ case UI_BUTTON_SHOWN:
+ if (button_shown_)
+ return;
+ button_shown_ = true;
+ break;
case UI_OPENED:
if (sheet_opened_)
return;
@@ -97,6 +103,7 @@ void ContextualSuggestionsMetricsReporter::RecordUmaMetrics(
void ContextualSuggestionsMetricsReporter::ResetUma() {
sheet_peeked_ = false;
+ button_shown_ = false;
sheet_opened_ = false;
sheet_closed_ = false;
any_suggestion_downloaded_ = false;
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h b/chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h
index 0cd8bff2ed4..5a6ca75d6f0 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h
@@ -43,6 +43,8 @@ class ContextualSuggestionsMetricsReporter
// Internal UMA state data.
// Whether the sheet ever peeked.
bool sheet_peeked_;
+ // Whether the button was ever shown.
+ bool button_shown_;
// Whether the sheet was ever opened.
bool sheet_opened_;
// Whether the sheet was closed.
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter_unittest.cc b/chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter_unittest.cc
index ddf6e47fd9f..99512b43519 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter_unittest.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter_unittest.cc
@@ -3,8 +3,9 @@
// found in the LICENSE file.
#include "components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h"
+
#include "base/macros.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_ukm_entry.h"
#include "components/ukm/test_ukm_recorder.h"
@@ -35,6 +36,7 @@ const int kSuggestionDownloaded = 11;
const int kSuggestionClicked = 12;
const int kUiDismissedWithoutOpen = 13;
const int kUiDismissedAfterOpen = 14;
+const int kUiButtonShown = 15;
} // namespace
class ContextualSuggestionsMetricsReporterTest : public ::testing::Test {
@@ -196,4 +198,51 @@ TEST_F(ContextualSuggestionsMetricsReporterTest, EnumNotReorderedTest) {
GetReporter().Flush();
}
+TEST_F(ContextualSuggestionsMetricsReporterTest, ButtonTest) {
+ base::HistogramTester histogram_tester;
+ GetReporter().SetupForPage(kTestNavigationUrl, GetSourceId());
+ GetReporter().RecordEvent(FETCH_REQUESTED);
+ GetReporter().RecordEvent(FETCH_COMPLETED);
+ GetReporter().RecordEvent(UI_BUTTON_SHOWN);
+ GetReporter().RecordEvent(UI_OPENED);
+ GetReporter().RecordEvent(SUGGESTION_DOWNLOADED);
+ GetReporter().RecordEvent(SUGGESTION_CLICKED);
+ // Flush data to write to UKM.
+ GetReporter().Flush();
+ // Check that we wrote something to UKM. Details of UKM reporting are tested
+ // in a different test suite.
+ TestUkmRecorder* test_ukm_recorder = GetTestUkmRecorder();
+ std::vector<const ukm::mojom::UkmEntry*> entry_vector =
+ test_ukm_recorder->GetEntriesByName(ContextualSuggestions::kEntryName);
+ EXPECT_EQ(1U, entry_vector.size());
+ const ukm::mojom::UkmEntry* first_entry = entry_vector[0];
+ EXPECT_TRUE(test_ukm_recorder->EntryHasMetric(
+ first_entry, ContextualSuggestions::kFetchStateName));
+ EXPECT_EQ(static_cast<int64_t>(FetchState::COMPLETED),
+ *(test_ukm_recorder->GetEntryMetric(
+ first_entry, ContextualSuggestions::kFetchStateName)));
+ // Test that the expected histogram events were written.
+ histogram_tester.ExpectBucketCount(kEventsHistogramName, kUninitialized, 0);
+ histogram_tester.ExpectBucketCount(kEventsHistogramName, kFetchDelayed, 0);
+ histogram_tester.ExpectBucketCount(kEventsHistogramName, kFetchRequested, 1);
+ histogram_tester.ExpectBucketCount(kEventsHistogramName, kFetchError, 0);
+ histogram_tester.ExpectBucketCount(kEventsHistogramName, kFetchServerBusy, 0);
+ histogram_tester.ExpectBucketCount(kEventsHistogramName, kFetchBelowThreshold,
+ 0);
+ histogram_tester.ExpectBucketCount(kEventsHistogramName, kFetchEmpty, 0);
+ histogram_tester.ExpectBucketCount(kEventsHistogramName, kFetchCompleted, 1);
+ histogram_tester.ExpectBucketCount(kEventsHistogramName, kUiButtonShown, 1);
+ histogram_tester.ExpectBucketCount(kEventsHistogramName, kUiOpened, 1);
+ histogram_tester.ExpectBucketCount(kEventsHistogramName, kUiClosedObsolete,
+ 0);
+ histogram_tester.ExpectBucketCount(kEventsHistogramName,
+ kSuggestionDownloaded, 1);
+ histogram_tester.ExpectBucketCount(kEventsHistogramName, kSuggestionClicked,
+ 1);
+ histogram_tester.ExpectBucketCount(kEventsHistogramName,
+ kUiDismissedWithoutOpen, 0);
+ histogram_tester.ExpectBucketCount(kEventsHistogramName,
+ kUiDismissedAfterOpen, 0);
+}
+
} // namespace contextual_suggestions
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_reporter.cc b/chromium/components/ntp_snippets/contextual/contextual_suggestions_reporter.cc
index 12ca2fbbf88..ec68d6fe992 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_reporter.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_reporter.cc
@@ -28,6 +28,11 @@ ContextualSuggestionsReporterProvider::CreateReporter() {
return reporter;
}
+ContextualSuggestionsDebuggingReporter*
+ContextualSuggestionsReporterProvider::GetDebuggingReporter() {
+ return debugging_reporter_.get();
+}
+
ContextualSuggestionsReporter::ContextualSuggestionsReporter() = default;
ContextualSuggestionsReporter::~ContextualSuggestionsReporter() = default;
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_reporter.h b/chromium/components/ntp_snippets/contextual/contextual_suggestions_reporter.h
index b57e6e04466..2c01ea5a215 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_reporter.h
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_reporter.h
@@ -29,6 +29,8 @@ class ContextualSuggestionsReporterProvider {
virtual std::unique_ptr<ContextualSuggestionsReporter> CreateReporter();
+ virtual ContextualSuggestionsDebuggingReporter* GetDebuggingReporter();
+
private:
// The debugging reporter is shared between instances of top-level reporter.
// Since multiple objects need a reference to this, it's kept as a unique
@@ -84,9 +86,11 @@ enum ContextualSuggestionsEvent {
// The UI was dismissed after having been opened. This means the sheet was
// closed from any position after it was expanded at least once.
UI_DISMISSED_AFTER_OPEN = 14,
+ // The UI button was shown to the user.
+ UI_BUTTON_SHOWN = 15,
// Special name that marks the maximum value in an Enum used for UMA.
// https://cs.chromium.org/chromium/src/tools/metrics/histograms/README.md.
- kMaxValue = UI_DISMISSED_AFTER_OPEN,
+ kMaxValue = UI_BUTTON_SHOWN,
};
// Tracks various metrics based on reports of events that take place
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_result.cc b/chromium/components/ntp_snippets/contextual/contextual_suggestions_result.cc
index a562a865467..1033fb5c80f 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_result.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_result.cc
@@ -16,16 +16,18 @@ PeekConditions::PeekConditions()
Cluster::Cluster() = default;
+Cluster::Cluster(const Cluster& other) = default;
+
// MSVC doesn't support defaulted move constructors, so we have to define it
// ourselves.
Cluster::Cluster(Cluster&& other) noexcept
: title(std::move(other.title)),
suggestions(std::move(other.suggestions)) {}
-Cluster::Cluster(const Cluster&) = default;
-
Cluster::~Cluster() = default;
+Cluster& Cluster::operator=(const Cluster&) = default;
+
ClusterBuilder::ClusterBuilder(const std::string& title) {
cluster_.title = title;
}
@@ -53,15 +55,37 @@ Cluster ClusterBuilder::Build() {
return std::move(cluster_);
}
+ServerExperimentInfo::ServerExperimentInfo() = default;
+
+ServerExperimentInfo::ServerExperimentInfo(std::string name, std::string group)
+ : name(name), group(group) {}
+
+ServerExperimentInfo::ServerExperimentInfo(const ServerExperimentInfo& other) =
+ default;
+
+ServerExperimentInfo::ServerExperimentInfo(
+ ServerExperimentInfo&& other) noexcept
+ : name(std::move(other.name)), group(std::move(other.group)) {}
+
+ServerExperimentInfo::~ServerExperimentInfo() = default;
+
+ServerExperimentInfo& ServerExperimentInfo::operator=(
+ ServerExperimentInfo&& other) = default;
+
+ServerExperimentInfo& ServerExperimentInfo::operator=(
+ const ServerExperimentInfo& other) = default;
+
ContextualSuggestionsResult::ContextualSuggestionsResult() = default;
ContextualSuggestionsResult::ContextualSuggestionsResult(
std::string peek_text,
std::vector<Cluster> clusters,
- PeekConditions peek_conditions)
+ PeekConditions peek_conditions,
+ ServerExperimentInfos experiment_infos)
: clusters(clusters),
peek_text(peek_text),
- peek_conditions(peek_conditions) {}
+ peek_conditions(peek_conditions),
+ experiment_infos(std::move(experiment_infos)) {}
ContextualSuggestionsResult::ContextualSuggestionsResult(
const ContextualSuggestionsResult& other) = default;
@@ -72,11 +96,15 @@ ContextualSuggestionsResult::ContextualSuggestionsResult(
ContextualSuggestionsResult&& other) noexcept
: clusters(std::move(other.clusters)),
peek_text(std::move(other.peek_text)),
- peek_conditions(std::move(other.peek_conditions)) {}
+ peek_conditions(std::move(other.peek_conditions)),
+ experiment_infos(std::move(other.experiment_infos)) {}
ContextualSuggestionsResult::~ContextualSuggestionsResult() = default;
ContextualSuggestionsResult& ContextualSuggestionsResult::operator=(
ContextualSuggestionsResult&& other) = default;
+ContextualSuggestionsResult& ContextualSuggestionsResult::operator=(
+ const ContextualSuggestionsResult& other) = default;
+
} // namespace contextual_suggestions
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_result.h b/chromium/components/ntp_snippets/contextual/contextual_suggestions_result.h
index 6dadf379a21..b140c319f03 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_result.h
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_result.h
@@ -7,6 +7,12 @@
#include "components/ntp_snippets/contextual/contextual_suggestion.h"
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+
namespace contextual_suggestions {
// Encapsulates conditions under which to show or "peek" the contextual
@@ -36,6 +42,8 @@ struct Cluster {
Cluster(Cluster&&) noexcept;
~Cluster();
+ Cluster& operator=(const Cluster&);
+
std::string title;
std::vector<ContextualSuggestion> suggestions;
};
@@ -43,7 +51,7 @@ struct Cluster {
// Allows concise construction of a cluster.
class ClusterBuilder {
public:
- ClusterBuilder(const std::string& title);
+ explicit ClusterBuilder(const std::string& title);
// Allow copying for ease of validation when testing.
ClusterBuilder(const ClusterBuilder& other);
@@ -54,22 +62,42 @@ class ClusterBuilder {
Cluster cluster_;
};
+// Synthetic field trials driven by the server.
+struct ServerExperimentInfo {
+ ServerExperimentInfo();
+ ServerExperimentInfo(std::string name, std::string group);
+ ServerExperimentInfo(const ServerExperimentInfo&);
+ ServerExperimentInfo(ServerExperimentInfo&&) noexcept;
+ ~ServerExperimentInfo();
+
+ ServerExperimentInfo& operator=(ServerExperimentInfo&&);
+ ServerExperimentInfo& operator=(const ServerExperimentInfo&);
+
+ std::string name;
+ std::string group;
+};
+
+using ServerExperimentInfos = std::vector<ServerExperimentInfo>;
+
// Struct that holds the data from a ContextualSuggestions response that we care
// about for UI purposes.
struct ContextualSuggestionsResult {
ContextualSuggestionsResult();
ContextualSuggestionsResult(std::string peek_text,
std::vector<Cluster> clusters,
- PeekConditions peek_conditions);
+ PeekConditions peek_conditions,
+ ServerExperimentInfos experiment_infos);
ContextualSuggestionsResult(const ContextualSuggestionsResult&);
ContextualSuggestionsResult(ContextualSuggestionsResult&&) noexcept;
~ContextualSuggestionsResult();
ContextualSuggestionsResult& operator=(ContextualSuggestionsResult&&);
+ ContextualSuggestionsResult& operator=(const ContextualSuggestionsResult&);
std::vector<Cluster> clusters;
std::string peek_text;
PeekConditions peek_conditions;
+ ServerExperimentInfos experiment_infos;
};
using FetchClustersCallback =
@@ -77,4 +105,4 @@ using FetchClustersCallback =
} // namespace contextual_suggestions
-#endif // COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_RESULT_H_ \ No newline at end of file
+#endif // COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_RESULT_H_
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_test_utils.cc b/chromium/components/ntp_snippets/contextual/contextual_suggestions_test_utils.cc
index 69002ca4e87..bfccb0fffe8 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_test_utils.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_test_utils.cc
@@ -19,6 +19,7 @@ void MockClustersCallback::Done(ContextualSuggestionsResult result) {
response_peek_text = result.peek_text;
response_clusters = std::move(result.clusters);
peek_conditions = result.peek_conditions;
+ experiment_infos = result.experiment_infos;
}
FetchClustersCallback MockClustersCallback::ToOnceCallback() {
@@ -46,7 +47,7 @@ void ExpectSuggestionsMatch(const ContextualSuggestion& actual,
void ExpectClustersMatch(const Cluster& actual, const Cluster& expected) {
EXPECT_EQ(actual.title, expected.title);
ASSERT_EQ(actual.suggestions.size(), expected.suggestions.size());
- for (size_t i = 0; i < actual.suggestions.size(); i++) {
+ for (size_t i = 0; i < actual.suggestions.size(); ++i) {
ExpectSuggestionsMatch(std::move(actual.suggestions[i]),
std::move(expected.suggestions[i]));
}
@@ -60,16 +61,27 @@ void ExpectPeekConditionsMatch(const PeekConditions& actual,
EXPECT_EQ(actual.maximum_number_of_peeks, expected.maximum_number_of_peeks);
}
+void ExpectServerExperimentInfosMatch(const ServerExperimentInfos& actual,
+ const ServerExperimentInfos& expected) {
+ ASSERT_EQ(expected.size(), actual.size());
+ for (size_t i = 0; i < actual.size(); ++i) {
+ EXPECT_EQ(expected[i].name, actual[i].name);
+ EXPECT_EQ(expected[i].group, actual[i].group);
+ }
+}
+
void ExpectResponsesMatch(const MockClustersCallback& callback,
const ContextualSuggestionsResult& expected_result) {
EXPECT_EQ(callback.response_peek_text, expected_result.peek_text);
ExpectPeekConditionsMatch(callback.peek_conditions,
expected_result.peek_conditions);
ASSERT_EQ(callback.response_clusters.size(), expected_result.clusters.size());
- for (size_t i = 0; i < callback.response_clusters.size(); i++) {
+ for (size_t i = 0; i < callback.response_clusters.size(); ++i) {
ExpectClustersMatch(std::move(callback.response_clusters[i]),
std::move(expected_result.clusters[i]));
}
+ ExpectServerExperimentInfosMatch(callback.experiment_infos,
+ expected_result.experiment_infos);
}
} // namespace contextual_suggestions
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_test_utils.h b/chromium/components/ntp_snippets/contextual/contextual_suggestions_test_utils.h
index b0265d2310e..a94d2838db8 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_test_utils.h
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_test_utils.h
@@ -27,6 +27,7 @@ class MockClustersCallback {
std::string response_peek_text;
std::vector<Cluster> response_clusters;
PeekConditions peek_conditions;
+ ServerExperimentInfos experiment_infos;
};
class MockMetricsCallback {
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry.cc b/chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry.cc
index d06920c6157..1771fdcabeb 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry.cc
@@ -66,6 +66,10 @@ void ContextualSuggestionsUkmEntry::RecordEventMetrics(
trigger_event_ = TriggerEvent::REVERSE_SCROLL;
StartTimerIfNeeded();
break;
+ case UI_BUTTON_SHOWN:
+ trigger_event_ = TriggerEvent::TOOLBAR_BUTTON;
+ StartTimerIfNeeded();
+ break;
case UI_OPENED:
was_sheet_opened_ = true;
StartTimerIfNeeded();
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry.h b/chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry.h
index c340419a95b..f632dc2e2ac 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry.h
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry.h
@@ -35,6 +35,7 @@ enum class FetchState {
enum class TriggerEvent {
NEVER_SHOWN,
REVERSE_SCROLL,
+ TOOLBAR_BUTTON,
};
// Writes a single UKM entry that describes the latest state of the event stream
diff --git a/chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry_unittest.cc b/chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry_unittest.cc
index 8af8952196d..b711e58c9e4 100644
--- a/chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry_unittest.cc
+++ b/chromium/components/ntp_snippets/contextual/contextual_suggestions_ukm_entry_unittest.cc
@@ -104,4 +104,25 @@ TEST_F(ContextualSuggestionsUkmEntryTest, ExpectedOperationTest) {
EXPECT_EQ(1, GetEntryMetric(ContextualSuggestions::kTriggerEventName));
}
+TEST_F(ContextualSuggestionsUkmEntryTest, ExpectedOperationButtonTest) {
+ ukm_entry_->RecordEventMetrics(FETCH_DELAYED);
+ ukm_entry_->RecordEventMetrics(FETCH_REQUESTED);
+ ukm_entry_->RecordEventMetrics(FETCH_COMPLETED);
+ ukm_entry_->RecordEventMetrics(UI_BUTTON_SHOWN);
+ ukm_entry_->RecordEventMetrics(UI_OPENED);
+ ukm_entry_->RecordEventMetrics(SUGGESTION_DOWNLOADED);
+ ukm_entry_->RecordEventMetrics(SUGGESTION_DOWNLOADED);
+ ukm_entry_->RecordEventMetrics(SUGGESTION_CLICKED);
+ ukm_entry_->Flush();
+ EXPECT_EQ(1, GetEntryMetric(ContextualSuggestions::kAnyDownloadedName));
+ EXPECT_EQ(1, GetEntryMetric(ContextualSuggestions::kAnySuggestionTakenName));
+ EXPECT_EQ(0, GetEntryMetric(ContextualSuggestions::kClosedFromPeekName));
+ EXPECT_EQ(1, GetEntryMetric(ContextualSuggestions::kEverOpenedName));
+ EXPECT_EQ(static_cast<int64_t>(FetchState::COMPLETED),
+ GetEntryMetric(ContextualSuggestions::kFetchStateName));
+ EXPECT_LT(0,
+ GetEntryMetric(ContextualSuggestions::kShowDurationBucketMinName));
+ EXPECT_EQ(2, GetEntryMetric(ContextualSuggestions::kTriggerEventName));
+}
+
} // namespace contextual_suggestions
diff --git a/chromium/components/ntp_snippets/contextual/proto/get_pivots_response.proto b/chromium/components/ntp_snippets/contextual/proto/get_pivots_response.proto
index 72b54f6bc16..79c8548517a 100644
--- a/chromium/components/ntp_snippets/contextual/proto/get_pivots_response.proto
+++ b/chromium/components/ntp_snippets/contextual/proto/get_pivots_response.proto
@@ -23,6 +23,9 @@ message Pivots {
// The text to present to the user in the peek.
optional PeekText peek_text = 4;
+
+ // Information about experiments running on this request.
+ repeated ExperimentInfo experiment_info = 5;
}
// One related response.
@@ -139,3 +142,19 @@ message RasterImage {
// is determined by the "Content-Type" of the HTTP response for the URL.
optional Url url = 2;
}
+
+// Synthetic experiment IDs, which Chrome may log in UMA.
+message ExperimentInfo {
+ // Name of experiment. A single live-experiment will have one
+ // experiment_group_name, and two or more experiment_arm_name's.
+ //
+ // This corresponds to an experiment group in GWS, and a field trial in Chrome
+ // Variations.
+ optional string experiment_group_name = 1;
+
+ // Name of experiment arm.
+ //
+ // This corresponds to an experiment arm in GWS, and a field trial group in
+ // Chrome Variations.
+ optional string experiment_arm_name = 2;
+}
diff --git a/chromium/components/ntp_snippets/features.cc b/chromium/components/ntp_snippets/features.cc
index 8c49f5dc82d..b3db200cf94 100644
--- a/chromium/components/ntp_snippets/features.cc
+++ b/chromium/components/ntp_snippets/features.cc
@@ -35,7 +35,7 @@ const base::Feature* const kAllFeatures[] = {
&kRemoteSuggestionsBackendFeature};
const base::Feature kArticleSuggestionsExpandableHeader{
- "NTPArticleSuggestionsExpandableHeader", base::FEATURE_DISABLED_BY_DEFAULT};
+ "NTPArticleSuggestionsExpandableHeader", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kArticleSuggestionsFeature{
"NTPArticleSuggestions", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chromium/components/ntp_snippets/ntp_snippets_constants.cc b/chromium/components/ntp_snippets/ntp_snippets_constants.cc
index 9580ea472c9..5c16d03b22e 100644
--- a/chromium/components/ntp_snippets/ntp_snippets_constants.cc
+++ b/chromium/components/ntp_snippets/ntp_snippets_constants.cc
@@ -13,7 +13,8 @@ const char kContentSuggestionsApiScope[] =
"https://www.googleapis.com/auth/chrome-content-suggestions";
const char kContentSuggestionsServer[] =
- "https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/fetch";
+ "https://chromefeedcontentsuggestions-pa.googleapis.com/v2/suggestions/"
+ "fetch";
const char kContentSuggestionsStagingServer[] =
"https://staging-chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
"fetch";
diff --git a/chromium/components/ntp_snippets/remote/cached_image_fetcher.cc b/chromium/components/ntp_snippets/remote/cached_image_fetcher.cc
index eaeda716d4c..4c7cf07771f 100644
--- a/chromium/components/ntp_snippets/remote/cached_image_fetcher.cc
+++ b/chromium/components/ntp_snippets/remote/cached_image_fetcher.cc
@@ -142,7 +142,7 @@ void CachedImageFetcher::FetchImageFromNetwork(
return;
}
// Image decoding callback only set when requested.
- image_fetcher::ImageFetcher::ImageFetcherCallback decode_callback;
+ image_fetcher::ImageFetcherCallback decode_callback;
if (image_callback) {
decode_callback = base::BindOnce(&CachedImageFetcher::OnImageDecodingDone,
base::Unretained(this),
diff --git a/chromium/components/ntp_snippets/remote/cached_image_fetcher_unittest.cc b/chromium/components/ntp_snippets/remote/cached_image_fetcher_unittest.cc
index 64dc9e19951..7d330a87de1 100644
--- a/chromium/components/ntp_snippets/remote/cached_image_fetcher_unittest.cc
+++ b/chromium/components/ntp_snippets/remote/cached_image_fetcher_unittest.cc
@@ -19,8 +19,8 @@
#include "components/ntp_snippets/remote/remote_suggestions_database.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/image/image.h"
@@ -74,21 +74,22 @@ enum class TestType {
// both callbacks used, only image_callback used, only image_data_callback used.
class CachedImageFetcherTest : public testing::TestWithParam<TestType> {
public:
- CachedImageFetcherTest() : fake_url_fetcher_factory_(nullptr) {
+ CachedImageFetcherTest() {
EXPECT_TRUE(database_dir_.CreateUniqueTempDir());
RequestThrottler::RegisterProfilePrefs(pref_service_.registry());
database_ =
std::make_unique<RemoteSuggestionsDatabase>(database_dir_.GetPath());
- request_context_getter_ = scoped_refptr<net::TestURLRequestContextGetter>(
- new net::TestURLRequestContextGetter(
- scoped_task_environment_.GetMainThreadTaskRunner()));
+
+ shared_factory_ =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_);
auto decoder = std::make_unique<FakeImageDecoder>();
fake_image_decoder_ = decoder.get();
cached_image_fetcher_ = std::make_unique<ntp_snippets::CachedImageFetcher>(
- std::make_unique<image_fetcher::ImageFetcherImpl>(
- std::move(decoder), request_context_getter_.get()),
+ std::make_unique<image_fetcher::ImageFetcherImpl>(std::move(decoder),
+ shared_factory_),
&pref_service_, database_.get());
RunUntilIdle();
EXPECT_TRUE(database_->IsInitialized());
@@ -138,21 +139,22 @@ class CachedImageFetcherTest : public testing::TestWithParam<TestType> {
RemoteSuggestionsDatabase* database() { return database_.get(); }
FakeImageDecoder* fake_image_decoder() { return fake_image_decoder_; }
- net::FakeURLFetcherFactory* fake_url_fetcher_factory() {
- return &fake_url_fetcher_factory_;
+ network::TestURLLoaderFactory* test_url_loader_factory() {
+ return &test_url_loader_factory_;
}
CachedImageFetcher* cached_image_fetcher() {
return cached_image_fetcher_.get();
}
private:
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> shared_factory_;
std::unique_ptr<CachedImageFetcher> cached_image_fetcher_;
std::unique_ptr<RemoteSuggestionsDatabase> database_;
base::ScopedTempDir database_dir_;
FakeImageDecoder* fake_image_decoder_;
- net::FakeURLFetcherFactory fake_url_fetcher_factory_;
+
TestingPrefServiceSimple pref_service_;
- scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
DISALLOW_COPY_AND_ASSIGN(CachedImageFetcherTest);
@@ -171,23 +173,20 @@ TEST_P(CachedImageFetcherTest, FetchImageFromCache) {
TEST_P(CachedImageFetcherTest, FetchImagePopulatesCache) {
// Expect the image to be fetched by URL.
{
- fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
- net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ test_url_loader_factory()->AddResponse(kImageURL, kImageData);
Fetch(kImageData, true);
}
// Fetch again. The cache should be populated, no network request is needed.
{
- fake_url_fetcher_factory()->ClearFakeResponses();
+ test_url_loader_factory()->ClearResponses();
Fetch(kImageData, true);
}
}
TEST_P(CachedImageFetcherTest, FetchNonExistingImage) {
const std::string kErrorResponse = "error-response";
- fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kErrorResponse,
- net::HTTP_NOT_FOUND,
- net::URLRequestStatus::FAILED);
+ test_url_loader_factory()->AddResponse(kImageURL, kErrorResponse,
+ net::HTTP_NOT_FOUND);
// Expect an empty image is fetched if the URL cannot be requested.
Fetch("", false);
}
diff --git a/chromium/components/ntp_snippets/remote/json_request.cc b/chromium/components/ntp_snippets/remote/json_request.cc
index fea0ee0327a..18399676838 100644
--- a/chromium/components/ntp_snippets/remote/json_request.cc
+++ b/chromium/components/ntp_snippets/remote/json_request.cc
@@ -27,17 +27,14 @@
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.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 "third_party/icu/source/common/unicode/uloc.h"
#include "third_party/icu/source/common/unicode/utypes.h"
#include "ui/base/l10n/l10n_util.h"
using language::UrlLanguageHistogram;
-using net::URLFetcher;
-using net::URLRequestContextGetter;
-using net::HttpRequestHeaders;
-using net::URLRequestStatus;
namespace ntp_snippets {
@@ -54,15 +51,6 @@ const char kSendTopLanguagesName[] = "send_top_languages";
// Variation parameter for sending UserClassifier info to the server.
const char kSendUserClassName[] = "send_user_class";
-int Get5xxRetryCount(bool interactive_request) {
- if (interactive_request) {
- return 2;
- }
- return std::max(0, variations::GetVariationParamByFeatureAsInt(
- ntp_snippets::kArticleSuggestionsFeature,
- kBackground5xxRetriesName, 0));
-}
-
bool IsSendingTopLanguagesEnabled() {
return variations::GetVariationParamByFeatureAsBool(
ntp_snippets::kArticleSuggestionsFeature, kSendTopLanguagesName,
@@ -143,8 +131,25 @@ JsonRequest::~JsonRequest() {
}
void JsonRequest::Start(CompletedCallback callback) {
+ DCHECK(simple_url_loader_);
+ DCHECK(url_loader_factory_);
request_completed_callback_ = std::move(callback);
- url_fetcher_->Start();
+ last_response_string_.clear();
+ simple_url_loader_->SetAllowHttpErrorResults(true);
+ simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&JsonRequest::OnSimpleLoaderComplete,
+ base::Unretained(this)));
+}
+
+// static
+int JsonRequest::Get5xxRetryCount(bool interactive_request) {
+ if (interactive_request) {
+ return 2;
+ }
+ return std::max(0, variations::GetVariationParamByFeatureAsInt(
+ ntp_snippets::kArticleSuggestionsFeature,
+ kBackground5xxRetriesName, 0));
}
base::TimeDelta JsonRequest::GetFetchDuration() const {
@@ -152,50 +157,41 @@ base::TimeDelta JsonRequest::GetFetchDuration() const {
}
std::string JsonRequest::GetResponseString() const {
- std::string response;
- url_fetcher_->GetResponseAsString(&response);
- return response;
+ return last_response_string_;
}
-////////////////////////////////////////////////////////////////////////////////
-// URLFetcherDelegate overrides
-void JsonRequest::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK_EQ(url_fetcher_.get(), source);
- const URLRequestStatus& status = url_fetcher_->GetStatus();
- int response = url_fetcher_->GetResponseCode();
+void JsonRequest::OnSimpleLoaderComplete(
+ std::unique_ptr<std::string> response_body) {
+ int net_error = simple_url_loader_->NetError();
+ int response_code = -1;
+ if (simple_url_loader_->ResponseInfo() &&
+ simple_url_loader_->ResponseInfo()->headers) {
+ response_code =
+ simple_url_loader_->ResponseInfo()->headers->response_code();
+ }
+ simple_url_loader_.reset();
base::UmaHistogramSparse("NewTabPage.Snippets.FetchHttpResponseOrErrorCode",
- status.is_success() ? response : status.error());
-
- if (!status.is_success()) {
+ net_error == net::OK ? response_code : net_error);
+ if (net_error != net::OK) {
std::move(request_completed_callback_)
.Run(/*result=*/nullptr, FetchResult::URL_REQUEST_STATUS_ERROR,
- /*error_details=*/base::StringPrintf(" %d", status.error()));
- } else if (response != net::HTTP_OK) {
- // TODO(jkrcal): https://crbug.com/609084
- // We need to deal with the edge case again where the auth
- // token expires just before we send the request (in which case we need to
- // fetch a new auth token). We should extract that into a common class
- // instead of adding it to every single class that uses auth tokens.
+ /*error_details=*/base::StringPrintf(" %d", net_error));
+ } else if (response_code / 100 != 2) {
+ FetchResult result = response_code == net::HTTP_UNAUTHORIZED
+ ? FetchResult::HTTP_ERROR_UNAUTHORIZED
+ : FetchResult::HTTP_ERROR;
std::move(request_completed_callback_)
- .Run(/*result=*/nullptr, FetchResult::HTTP_ERROR,
- /*error_details=*/base::StringPrintf(" %d", response));
+ .Run(/*result=*/nullptr, result,
+ /*error_details=*/base::StringPrintf(" %d", response_code));
} else {
- ParseJsonResponse();
+ last_response_string_ = std::move(*response_body);
+ parse_json_callback_.Run(
+ last_response_string_,
+ base::Bind(&JsonRequest::OnJsonParsed, weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&JsonRequest::OnJsonError, weak_ptr_factory_.GetWeakPtr()));
}
}
-void JsonRequest::ParseJsonResponse() {
- std::string json_string;
- bool stores_result_to_string =
- url_fetcher_->GetResponseAsString(&json_string);
- DCHECK(stores_result_to_string);
-
- parse_json_callback_.Run(
- json_string,
- base::Bind(&JsonRequest::OnJsonParsed, weak_ptr_factory_.GetWeakPtr()),
- base::Bind(&JsonRequest::OnJsonError, weak_ptr_factory_.GetWeakPtr()));
-}
-
void JsonRequest::OnJsonParsed(std::unique_ptr<base::Value> result) {
std::move(request_completed_callback_)
.Run(std::move(result), FetchResult::SUCCESS,
@@ -203,9 +199,8 @@ void JsonRequest::OnJsonParsed(std::unique_ptr<base::Value> result) {
}
void JsonRequest::OnJsonError(const std::string& error) {
- std::string json_string;
- url_fetcher_->GetResponseAsString(&json_string);
- LOG(WARNING) << "Received invalid JSON (" << error << "): " << json_string;
+ LOG(WARNING) << "Received invalid JSON (" << error
+ << "): " << last_response_string_;
std::move(request_completed_callback_)
.Run(/*result=*/nullptr, FetchResult::JSON_PARSE_ERROR,
/*error_details=*/base::StringPrintf(" (error %s)", error.c_str()));
@@ -217,18 +212,13 @@ JsonRequest::Builder::~Builder() = default;
std::unique_ptr<JsonRequest> JsonRequest::Builder::Build() const {
DCHECK(!url_.is_empty());
- DCHECK(url_request_context_getter_);
+ DCHECK(url_loader_factory_);
DCHECK(clock_);
auto request = std::make_unique<JsonRequest>(params_.exclusive_category,
clock_, parse_json_callback_);
std::string body = BuildBody();
- std::string headers = BuildHeaders();
- request->url_fetcher_ = BuildURLFetcher(request.get(), headers, body);
-
- // Log the request for debugging network issues.
- VLOG(1) << "Sending a NTP snippets request to " << url_ << ":\n"
- << headers << "\n"
- << body;
+ request->simple_url_loader_ = BuildURLLoader(body);
+ request->url_loader_factory_ = std::move(url_loader_factory_);
return request;
}
@@ -269,9 +259,9 @@ JsonRequest::Builder& JsonRequest::Builder::SetUrl(const GURL& url) {
return *this;
}
-JsonRequest::Builder& JsonRequest::Builder::SetUrlRequestContextGetter(
- const scoped_refptr<net::URLRequestContextGetter>& context_getter) {
- url_request_context_getter_ = context_getter;
+JsonRequest::Builder& JsonRequest::Builder::SetUrlLoaderFactory(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+ url_loader_factory_ = std::move(url_loader_factory);
return *this;
}
@@ -283,18 +273,24 @@ JsonRequest::Builder& JsonRequest::Builder::SetUserClassifier(
return *this;
}
-std::string JsonRequest::Builder::BuildHeaders() const {
- net::HttpRequestHeaders headers;
- headers.SetHeader("Content-Type", "application/json; charset=UTF-8");
+std::unique_ptr<network::ResourceRequest>
+JsonRequest::Builder::BuildResourceRequest() const {
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = url_;
+ resource_request->load_flags =
+ net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
+ resource_request->method = "POST";
+ resource_request->headers.SetHeader("Content-Type",
+ "application/json; charset=UTF-8");
if (!auth_header_.empty()) {
- headers.SetHeader("Authorization", auth_header_);
+ resource_request->headers.SetHeader("Authorization", auth_header_);
}
// Add X-Client-Data header with experiment IDs from field trials.
- // Note: It's OK to pass SignedIn::kNo if it's unknown, as it does not affect
- // transmission of experiments coming from the variations server.
- variations::AppendVariationHeaders(url_, variations::InIncognito::kNo,
- variations::SignedIn::kNo, &headers);
- return headers.ToString();
+ // TODO: We should call AppendVariationHeaders with explicit
+ // variations::SignedIn::kNo If the auth_header_ is empty
+ variations::AppendVariationHeadersUnknownSignedIn(
+ url_, variations::InIncognito::kNo, &resource_request->headers);
+ return resource_request;
}
std::string JsonRequest::Builder::BuildBody() const {
@@ -353,9 +349,7 @@ std::string JsonRequest::Builder::BuildBody() const {
return request_json;
}
-std::unique_ptr<net::URLFetcher> JsonRequest::Builder::BuildURLFetcher(
- net::URLFetcherDelegate* delegate,
- const std::string& headers,
+std::unique_ptr<network::SimpleURLLoader> JsonRequest::Builder::BuildURLLoader(
const std::string& body) const {
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("ntp_snippets_fetch", R"(
@@ -386,23 +380,26 @@ std::unique_ptr<net::URLFetcher> JsonRequest::Builder::BuildURLFetcher(
}
}
})");
- std::unique_ptr<net::URLFetcher> url_fetcher = net::URLFetcher::Create(
- url_, net::URLFetcher::POST, delegate, traffic_annotation);
- url_fetcher->SetRequestContext(url_request_context_getter_.get());
- url_fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES);
- data_use_measurement::DataUseUserData::AttachToFetcher(
- url_fetcher.get(),
- data_use_measurement::DataUseUserData::NTP_SNIPPETS_SUGGESTIONS);
-
- url_fetcher->SetExtraRequestHeaders(headers);
- url_fetcher->SetUploadData("application/json", body);
-
- // Fetchers are sometimes cancelled because a network change was detected.
- url_fetcher->SetAutomaticallyRetryOnNetworkChanges(3);
- url_fetcher->SetMaxRetriesOn5xx(
- Get5xxRetryCount(params_.interactive_request));
- return url_fetcher;
+ auto resource_request = BuildResourceRequest();
+
+ // Log the request for debugging network issues.
+ VLOG(1) << "Sending a NTP snippets request to " << url_ << ":\n"
+ << resource_request->headers.ToString() << "\n"
+ << body;
+
+ // TODO(https://crbug.com/808498): Re-add data use measurement once
+ // SimpleURLLoader supports it.
+ // ID=data_use_measurement::DataUseUserData::NTP_SNIPPETS_SUGGESTIONS);
+ auto loader = network::SimpleURLLoader::Create(std::move(resource_request),
+ traffic_annotation);
+ loader->AttachStringForUpload(body, "application/json");
+ int max_retries = JsonRequest::Get5xxRetryCount(params_.interactive_request);
+ if (max_retries > 0) {
+ loader->SetRetryOptions(
+ max_retries, network::SimpleURLLoader::RETRY_ON_5XX |
+ network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
+ }
+ return loader;
}
void JsonRequest::Builder::PrepareLanguages(
diff --git a/chromium/components/ntp_snippets/remote/json_request.h b/chromium/components/ntp_snippets/remote/json_request.h
index d90fb293602..f5a69c3ae44 100644
--- a/chromium/components/ntp_snippets/remote/json_request.h
+++ b/chromium/components/ntp_snippets/remote/json_request.h
@@ -16,9 +16,7 @@
#include "components/language/core/browser/url_language_histogram.h"
#include "components/ntp_snippets/remote/request_params.h"
#include "components/ntp_snippets/status.h"
-#include "net/http/http_request_headers.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/resource_request.h"
#include "url/gurl.h"
namespace base {
@@ -26,6 +24,11 @@ class Value;
class Clock;
} // namespace base
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+} // namespace network
+
namespace ntp_snippets {
class UserClassifier;
@@ -45,12 +48,13 @@ enum class FetchResult {
// DEPRECATED_INTERACTIVE_QUOTA_ERROR = 7,
// DEPRECATED_NON_INTERACTIVE_QUOTA_ERROR = 8,
MISSING_API_KEY = 9,
- RESULT_MAX = 10
+ HTTP_ERROR_UNAUTHORIZED = 10,
+ RESULT_MAX = 11,
};
// A single request to query remote suggestions. On success, the suggestions are
// returned in parsed JSON form (base::Value).
-class JsonRequest : public net::URLFetcherDelegate {
+class JsonRequest {
public:
// A client can expect error_details only, if there was any error during the
// fetching or parsing. In successful cases, it will be an empty string.
@@ -83,8 +87,8 @@ class JsonRequest : public net::URLFetcherDelegate {
// It has to be alive until the request is destroyed.
Builder& SetClock(base::Clock* clock);
Builder& SetUrl(const GURL& url);
- Builder& SetUrlRequestContextGetter(
- const scoped_refptr<net::URLRequestContextGetter>& context_getter);
+ Builder& SetUrlLoaderFactory(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
Builder& SetUserClassifier(const UserClassifier& user_classifier);
// These preview methods allow to inspect the Request without exposing it
@@ -92,7 +96,9 @@ class JsonRequest : public net::URLFetcherDelegate {
// TODO(fhorschig): Remove these when moving the Builder to
// snippets::internal and trigger the request to intercept the request.
std::string PreviewRequestBodyForTesting() { return BuildBody(); }
- std::string PreviewRequestHeadersForTesting() { return BuildHeaders(); }
+ std::string PreviewRequestHeadersForTesting() {
+ return BuildResourceRequest()->headers.ToString();
+ }
Builder& SetUserClassForTesting(const std::string& user_class) {
user_class_ = user_class;
return *this;
@@ -101,11 +107,9 @@ class JsonRequest : public net::URLFetcherDelegate {
bool is_interactive_request() const { return params_.interactive_request; }
private:
- std::string BuildHeaders() const;
+ std::unique_ptr<network::ResourceRequest> BuildResourceRequest() const;
std::string BuildBody() const;
- std::unique_ptr<net::URLFetcher> BuildURLFetcher(
- net::URLFetcherDelegate* request,
- const std::string& headers,
+ std::unique_ptr<network::SimpleURLLoader> BuildURLLoader(
const std::string& body) const;
void PrepareLanguages(
@@ -118,7 +122,7 @@ class JsonRequest : public net::URLFetcherDelegate {
RequestParams params_;
ParseJSONCallback parse_json_callback_;
GURL url_;
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// Optional properties.
std::string obfuscated_gaia_id_;
@@ -132,10 +136,12 @@ class JsonRequest : public net::URLFetcherDelegate {
base::Clock* clock,
const ParseJSONCallback& callback);
JsonRequest(JsonRequest&&);
- ~JsonRequest() override;
+ ~JsonRequest();
void Start(CompletedCallback callback);
+ static int Get5xxRetryCount(bool interactive_request);
+
const base::Optional<Category>& exclusive_category() const {
return exclusive_category_;
}
@@ -144,16 +150,18 @@ class JsonRequest : public net::URLFetcherDelegate {
std::string GetResponseString() const;
private:
- // URLFetcherDelegate implementation.
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
void ParseJsonResponse();
void OnJsonParsed(std::unique_ptr<base::Value> result);
void OnJsonError(const std::string& error);
- // The fetcher for downloading the snippets. Only non-null if a fetch is
+ // The loader for downloading the snippets. Only non-null if a load is
// currently ongoing.
- std::unique_ptr<net::URLFetcher> url_fetcher_;
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
+
+ // The loader factory for downloading the snippets.
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// If set, only return results for this category.
base::Optional<Category> exclusive_category_;
@@ -171,6 +179,9 @@ class JsonRequest : public net::URLFetcherDelegate {
// The callback to notify when URLFetcher finished and results are available.
CompletedCallback request_completed_callback_;
+ // The last response string
+ std::string last_response_string_;
+
base::WeakPtrFactory<JsonRequest> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(JsonRequest);
diff --git a/chromium/components/ntp_snippets/remote/json_request_unittest.cc b/chromium/components/ntp_snippets/remote/json_request_unittest.cc
index 24da870e3e1..5207c2d65c3 100644
--- a/chromium/components/ntp_snippets/remote/json_request_unittest.cc
+++ b/chromium/components/ntp_snippets/remote/json_request_unittest.cc
@@ -18,8 +18,9 @@
#include "components/ntp_snippets/remote/request_params.h"
#include "components/prefs/testing_pref_service.h"
#include "components/variations/variations_params_manager.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.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_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -65,8 +66,9 @@ class JsonRequestTest : public testing::Test {
{ntp_snippets::kArticleSuggestionsFeature.name}),
pref_service_(std::make_unique<TestingPrefServiceSimple>()),
mock_task_runner_(new base::TestMockTimeTaskRunner()),
- request_context_getter_(
- new net::TestURLRequestContextGetter(mock_task_runner_.get())) {
+ test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)) {
language::UrlLanguageHistogram::RegisterProfilePrefs(
pref_service_->registry());
}
@@ -88,7 +90,7 @@ class JsonRequestTest : public testing::Test {
JsonRequest::Builder builder;
builder.SetUrl(GURL("http://valid-url.test"))
.SetClock(mock_task_runner_->GetMockClock())
- .SetUrlRequestContextGetter(request_context_getter_.get());
+ .SetUrlLoaderFactory(test_shared_loader_factory_);
return builder;
}
@@ -96,8 +98,8 @@ class JsonRequestTest : public testing::Test {
variations::testing::VariationParamsManager params_manager_;
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
- scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
- net::TestURLFetcherFactory fetcher_factory_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
DISALLOW_COPY_AND_ASSIGN(JsonRequestTest);
};
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.cc
index 62e01526870..883d80f2f42 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.cc
@@ -18,15 +18,11 @@
#include "components/ntp_snippets/user_classifier.h"
#include "components/variations/variations_associated_data.h"
#include "net/base/url_util.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_status.h"
#include "services/identity/public/cpp/identity_manager.h"
+#include "services/identity/public/cpp/primary_account_access_token_fetcher.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
using language::UrlLanguageHistogram;
-using net::HttpRequestHeaders;
-using net::URLFetcher;
-using net::URLRequestContextGetter;
-using net::URLRequestStatus;
namespace ntp_snippets {
@@ -83,6 +79,8 @@ std::string FetchResultToString(FetchResult result) {
return "Error in obtaining an OAuth2 access token.";
case FetchResult::MISSING_API_KEY:
return "No API key available.";
+ case FetchResult::HTTP_ERROR_UNAUTHORIZED:
+ return "Access token invalid";
case FetchResult::RESULT_MAX:
break;
}
@@ -103,6 +101,7 @@ Status FetchResultToStatus(FetchResult result) {
// correctly but the server failed to respond as expected.
// TODO(fhorschig): Revisit HTTP_ERROR once the rescheduling was reworked.
case FetchResult::HTTP_ERROR:
+ case FetchResult::HTTP_ERROR_UNAUTHORIZED:
case FetchResult::URL_REQUEST_STATUS_ERROR:
case FetchResult::INVALID_SNIPPET_CONTENT_ERROR:
case FetchResult::JSON_PARSE_ERROR:
@@ -152,9 +151,11 @@ void FilterCategories(FetchedCategoriesVector* categories,
} // namespace
+bool RemoteSuggestionsFetcherImpl::skip_api_key_check_for_testing_ = false;
+
RemoteSuggestionsFetcherImpl::RemoteSuggestionsFetcherImpl(
identity::IdentityManager* identity_manager,
- scoped_refptr<URLRequestContextGetter> url_request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* pref_service,
UrlLanguageHistogram* language_histogram,
const ParseJSONCallback& parse_json_callback,
@@ -162,7 +163,7 @@ RemoteSuggestionsFetcherImpl::RemoteSuggestionsFetcherImpl(
const std::string& api_key,
const UserClassifier* user_classifier)
: identity_manager_(identity_manager),
- url_request_context_getter_(std::move(url_request_context_getter)),
+ url_loader_factory_(std::move(url_loader_factory)),
language_histogram_(language_histogram),
parse_json_callback_(parse_json_callback),
fetch_url_(api_endpoint),
@@ -208,7 +209,7 @@ void RemoteSuggestionsFetcherImpl::FetchSnippets(
.SetParams(params)
.SetParseJsonCallback(parse_json_callback_)
.SetClock(clock_)
- .SetUrlRequestContextGetter(url_request_context_getter_)
+ .SetUrlLoaderFactory(url_loader_factory_)
.SetUserClassifier(*user_classifier_);
if (identity_manager_->HasPrimaryAccount()) {
@@ -224,11 +225,11 @@ void RemoteSuggestionsFetcherImpl::FetchSnippets(
void RemoteSuggestionsFetcherImpl::FetchSnippetsNonAuthenticated(
JsonRequest::Builder builder,
SnippetsAvailableCallback callback) {
- if (api_key_.empty()) {
+ if (api_key_.empty() && !skip_api_key_check_for_testing_) {
// If we don't have an API key, don't even try.
FetchFinished(OptionalFetchedCategories(), std::move(callback),
FetchResult::MISSING_API_KEY, std::string(),
- /*is_authenticated=*/false);
+ /*is_authenticated=*/false, std::string());
return;
}
// When not providing OAuth token, we need to pass the Google API key.
@@ -238,7 +239,7 @@ void RemoteSuggestionsFetcherImpl::FetchSnippetsNonAuthenticated(
builder.SetUrl(url);
StartRequest(std::move(builder), std::move(callback),
- /*is_authenticated=*/false);
+ /*is_authenticated=*/false, std::string());
}
void RemoteSuggestionsFetcherImpl::FetchSnippetsAuthenticated(
@@ -253,18 +254,19 @@ void RemoteSuggestionsFetcherImpl::FetchSnippetsAuthenticated(
base::StringPrintf(kAuthorizationRequestHeaderFormat,
oauth_access_token.c_str()));
StartRequest(std::move(builder), std::move(callback),
- /*is_authenticated=*/true);
+ /*is_authenticated=*/true, oauth_access_token);
}
void RemoteSuggestionsFetcherImpl::StartRequest(
JsonRequest::Builder builder,
SnippetsAvailableCallback callback,
- bool is_authenticated) {
+ bool is_authenticated,
+ std::string access_token) {
std::unique_ptr<JsonRequest> request = builder.Build();
JsonRequest* raw_request = request.get();
raw_request->Start(base::BindOnce(
&RemoteSuggestionsFetcherImpl::JsonRequestDone, base::Unretained(this),
- std::move(request), std::move(callback), is_authenticated));
+ std::move(request), std::move(callback), is_authenticated, access_token));
}
void RemoteSuggestionsFetcherImpl::StartTokenRequest() {
@@ -274,28 +276,25 @@ void RemoteSuggestionsFetcherImpl::StartTokenRequest() {
}
OAuth2TokenService::ScopeSet scopes{kContentSuggestionsApiScope};
- token_fetcher_ = identity_manager_->CreateAccessTokenFetcherForPrimaryAccount(
- "ntp_snippets", scopes,
+ token_fetcher_ = std::make_unique<identity::PrimaryAccountAccessTokenFetcher>(
+ "ntp_snippets", identity_manager_, scopes,
base::BindOnce(&RemoteSuggestionsFetcherImpl::AccessTokenFetchFinished,
base::Unretained(this)),
identity::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable);
}
void RemoteSuggestionsFetcherImpl::AccessTokenFetchFinished(
- const GoogleServiceAuthError& error,
- const std::string& access_token) {
- // Delete the fetcher only after we leave this method (which is called from
- // the fetcher itself).
+ GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info) {
DCHECK(token_fetcher_);
- std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher>
- token_fetcher_deleter(std::move(token_fetcher_));
+ token_fetcher_.reset();
if (error.state() != GoogleServiceAuthError::NONE) {
AccessTokenError(error);
return;
}
- DCHECK(!access_token.empty());
+ DCHECK(!access_token_info.token.empty());
while (!pending_requests_.empty()) {
std::pair<JsonRequest::Builder, SnippetsAvailableCallback>
@@ -303,7 +302,7 @@ void RemoteSuggestionsFetcherImpl::AccessTokenFetchFinished(
pending_requests_.pop();
FetchSnippetsAuthenticated(std::move(builder_and_callback.first),
std::move(builder_and_callback.second),
- access_token);
+ access_token_info.token);
}
}
@@ -322,7 +321,7 @@ void RemoteSuggestionsFetcherImpl::AccessTokenError(
FetchResult::OAUTH_TOKEN_ERROR,
/*error_details=*/
base::StringPrintf(" (%s)", error.ToString().c_str()),
- /*is_authenticated=*/true);
+ /*is_authenticated=*/true, std::string());
pending_requests_.pop();
}
}
@@ -331,6 +330,7 @@ void RemoteSuggestionsFetcherImpl::JsonRequestDone(
std::unique_ptr<JsonRequest> request,
SnippetsAvailableCallback callback,
bool is_authenticated,
+ std::string access_token,
std::unique_ptr<base::Value> result,
FetchResult status_code,
const std::string& error_details) {
@@ -345,7 +345,7 @@ void RemoteSuggestionsFetcherImpl::JsonRequestDone(
if (!result) {
FetchFinished(OptionalFetchedCategories(), std::move(callback), status_code,
- error_details, is_authenticated);
+ error_details, is_authenticated, access_token);
return;
}
@@ -354,7 +354,7 @@ void RemoteSuggestionsFetcherImpl::JsonRequestDone(
LOG(WARNING) << "Received invalid snippets: " << last_fetch_json_;
FetchFinished(OptionalFetchedCategories(), std::move(callback),
FetchResult::INVALID_SNIPPET_CONTENT_ERROR, std::string(),
- is_authenticated);
+ is_authenticated, access_token);
return;
}
// Filter out unwanted categories if necessary.
@@ -363,7 +363,8 @@ void RemoteSuggestionsFetcherImpl::JsonRequestDone(
FilterCategories(&categories, request->exclusive_category());
FetchFinished(std::move(categories), std::move(callback),
- FetchResult::SUCCESS, std::string(), is_authenticated);
+ FetchResult::SUCCESS, std::string(), is_authenticated,
+ access_token);
}
void RemoteSuggestionsFetcherImpl::FetchFinished(
@@ -371,9 +372,18 @@ void RemoteSuggestionsFetcherImpl::FetchFinished(
SnippetsAvailableCallback callback,
FetchResult fetch_result,
const std::string& error_details,
- bool is_authenticated) {
+ bool is_authenticated,
+ std::string access_token) {
DCHECK(fetch_result == FetchResult::SUCCESS || !categories.has_value());
+ if (fetch_result == FetchResult::HTTP_ERROR_UNAUTHORIZED) {
+ OAuth2TokenService::ScopeSet scopes{kContentSuggestionsApiScope};
+ std::string account_id =
+ identity_manager_->GetPrimaryAccountInfo().account_id;
+ identity_manager_->RemoveAccessTokenFromCache(account_id, scopes,
+ access_token);
+ }
+
last_status_ = FetchResultToString(fetch_result) + error_details;
last_fetch_authenticated_ = is_authenticated;
@@ -387,4 +397,9 @@ void RemoteSuggestionsFetcherImpl::FetchFinished(
std::move(categories));
}
+// static
+void RemoteSuggestionsFetcherImpl::set_skip_api_key_check_for_testing() {
+ skip_api_key_check_for_testing_ = true;
+}
+
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h
index ca495747e18..8c788b27136 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h
@@ -18,8 +18,8 @@
#include "components/ntp_snippets/remote/json_to_categories.h"
#include "components/ntp_snippets/remote/remote_suggestions_fetcher.h"
#include "components/ntp_snippets/remote/request_params.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "services/identity/public/cpp/primary_account_access_token_fetcher.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "services/identity/public/cpp/access_token_info.h"
class PrefService;
@@ -30,12 +30,16 @@ class Value;
namespace identity {
class IdentityManager;
class PrimaryAccountAccessTokenFetcher;
-}
+} // namespace identity
namespace language {
class UrlLanguageHistogram;
} // namespace language
+namespace network {
+class SharedURLLoaderFactory;
+} // namespace network
+
namespace ntp_snippets {
class UserClassifier;
@@ -44,7 +48,7 @@ class RemoteSuggestionsFetcherImpl : public RemoteSuggestionsFetcher {
public:
RemoteSuggestionsFetcherImpl(
identity::IdentityManager* identity_manager,
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* pref_service,
language::UrlLanguageHistogram* language_histogram,
const ParseJSONCallback& parse_json_callback,
@@ -64,6 +68,8 @@ class RemoteSuggestionsFetcherImpl : public RemoteSuggestionsFetcher {
// Overrides internal clock for testing purposes.
void SetClockForTesting(base::Clock* clock) { clock_ = clock; }
+ static void set_skip_api_key_check_for_testing();
+
private:
void FetchSnippetsNonAuthenticated(internal::JsonRequest::Builder builder,
SnippetsAvailableCallback callback);
@@ -72,17 +78,19 @@ class RemoteSuggestionsFetcherImpl : public RemoteSuggestionsFetcher {
const std::string& oauth_access_token);
void StartRequest(internal::JsonRequest::Builder builder,
SnippetsAvailableCallback callback,
- bool is_authenticated);
+ bool is_authenticated,
+ std::string access_token);
void StartTokenRequest();
- void AccessTokenFetchFinished(const GoogleServiceAuthError& error,
- const std::string& access_token);
+ void AccessTokenFetchFinished(GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info);
void AccessTokenError(const GoogleServiceAuthError& error);
void JsonRequestDone(std::unique_ptr<internal::JsonRequest> request,
SnippetsAvailableCallback callback,
bool is_authenticated,
+ std::string access_token,
std::unique_ptr<base::Value> result,
internal::FetchResult status_code,
const std::string& error_details);
@@ -90,15 +98,16 @@ class RemoteSuggestionsFetcherImpl : public RemoteSuggestionsFetcher {
SnippetsAvailableCallback callback,
internal::FetchResult status_code,
const std::string& error_details,
- bool is_authenticated);
+ bool is_authenticated,
+ std::string access_token);
// Authentication for signed-in users.
identity::IdentityManager* identity_manager_;
std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher> token_fetcher_;
- // Holds the URL request context.
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+ // Holds the URL loader factory
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// Stores requests that wait for an access token.
base::queue<
@@ -127,6 +136,8 @@ class RemoteSuggestionsFetcherImpl : public RemoteSuggestionsFetcher {
std::string last_fetch_json_;
bool last_fetch_authenticated_;
+ static bool skip_api_key_check_for_testing_;
+
DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsFetcherImpl);
};
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl_unittest.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl_unittest.cc
index 16363012725..a1bb5f40cc3 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl_unittest.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_fetcher_impl_unittest.cc
@@ -12,7 +12,8 @@
#include "base/json/json_reader.h"
#include "base/optional.h"
#include "base/strings/stringprintf.h"
-#include "base/test/histogram_tester.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_clock.h"
@@ -29,10 +30,12 @@
#include "components/prefs/testing_pref_service.h"
#include "components/variations/entropy_provider.h"
#include "components/variations/variations_params_manager.h"
+#include "net/http/http_util.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.h"
#include "services/identity/public/cpp/identity_test_environment.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_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -53,6 +56,9 @@ using testing::StartsWith;
const char kAPIKey[] = "fakeAPIkey";
const char kTestEmail[] = "foo@bar.com";
+const char kFetchSuggestionsEndpoint[] =
+ "https://chromefeedcontentsuggestions-pa.googleapis.com/v2/suggestions/"
+ "fetch";
// Artificial time delay for JSON parsing.
const int64_t kTestJsonParsingLatencyMs = 20;
@@ -134,100 +140,6 @@ class MockSnippetsAvailableCallback {
fetched_categories));
};
-// TODO(fhorschig): Transfer this class' functionality to call delegates
-// automatically as option to TestURLFetcherFactory where it was just deleted.
-// This can be represented as a single member there and would reduce the amount
-// of fake implementations from three to two.
-
-// DelegateCallingTestURLFetcherFactory can be used to temporarily inject
-// TestURLFetcher instances into a scope.
-// Client code can access the last created fetcher to verify expected
-// properties. When the factory gets destroyed, all available delegates of still
-// valid fetchers will be called.
-// This ensures once-bound callbacks (like SnippetsAvailableCallback) will be
-// called at some point and are not leaked.
-class DelegateCallingTestURLFetcherFactory
- : public net::TestURLFetcherFactory,
- public net::TestURLFetcherDelegateForTests {
- public:
- DelegateCallingTestURLFetcherFactory() {
- SetDelegateForTests(this);
- set_remove_fetcher_on_delete(true);
- }
-
- ~DelegateCallingTestURLFetcherFactory() override {
- while (!fetchers_.empty()) {
- DropAndCallDelegate(fetchers_.front());
- }
- }
-
- std::unique_ptr<net::URLFetcher> CreateURLFetcher(
- int id,
- const GURL& url,
- net::URLFetcher::RequestType request_type,
- net::URLFetcherDelegate* d,
- net::NetworkTrafficAnnotationTag traffic_annotation) override {
- if (GetFetcherByID(id)) {
- LOG(WARNING) << "The ID " << id << " was already assigned to a fetcher."
- << "Its delegate will thereforde be called right now.";
- DropAndCallDelegate(id);
- }
- fetchers_.push_back(id);
- return TestURLFetcherFactory::CreateURLFetcher(id, url, request_type, d,
- traffic_annotation);
- }
-
- // Returns the raw pointer of the last created URL fetcher.
- // If it was destroyed or no fetcher was created, it will return a nulltpr.
- net::TestURLFetcher* GetLastCreatedFetcher() {
- if (fetchers_.empty()) {
- return nullptr;
- }
- return GetFetcherByID(fetchers_.front());
- }
-
- private:
- // The fetcher can either be destroyed because the delegate was called during
- // execution or because we called it on destruction.
- void DropAndCallDelegate(int fetcher_id) {
- auto found_id_iter =
- std::find(fetchers_.begin(), fetchers_.end(), fetcher_id);
- if (found_id_iter == fetchers_.end()) {
- return;
- }
- fetchers_.erase(found_id_iter);
- net::TestURLFetcher* fetcher = GetFetcherByID(fetcher_id);
- if (!fetcher->delegate()) {
- return;
- }
- fetcher->delegate()->OnURLFetchComplete(fetcher);
- }
-
- // net::TestURLFetcherDelegateForTests overrides:
- void OnRequestStart(int fetcher_id) override {}
- void OnChunkUpload(int fetcher_id) override {}
- void OnRequestEnd(int fetcher_id) override {
- DropAndCallDelegate(fetcher_id);
- }
-
- base::circular_deque<int> fetchers_;
-};
-
-// Factory for FakeURLFetcher objects that always generate errors.
-class FailingFakeURLFetcherFactory : public net::URLFetcherFactory {
- public:
- std::unique_ptr<net::URLFetcher> CreateURLFetcher(
- int id,
- const GURL& url,
- net::URLFetcher::RequestType request_type,
- net::URLFetcherDelegate* delegate,
- net::NetworkTrafficAnnotationTag traffic_annotation) override {
- return std::make_unique<net::FakeURLFetcher>(
- url, delegate, /*response_data=*/std::string(), net::HTTP_NOT_FOUND,
- net::URLRequestStatus::FAILED);
- }
-};
-
void ParseJson(const std::string& json,
const SuccessCallback& success_callback,
const ErrorCallback& error_callback) {
@@ -277,12 +189,13 @@ class RemoteSuggestionsFetcherImplTest : public testing::Test {
void ResetFetcher() { ResetFetcherWithAPIKey(kAPIKey); }
void ResetFetcherWithAPIKey(const std::string& api_key) {
- scoped_refptr<net::TestURLRequestContextGetter> request_context_getter =
- new net::TestURLRequestContextGetter(mock_task_runner_.get());
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_);
fetcher_ = std::make_unique<RemoteSuggestionsFetcherImpl>(
identity_test_env_.identity_manager(),
- std::move(request_context_getter), utils_.pref_service(), nullptr,
+ std::move(test_shared_loader_factory), utils_.pref_service(), nullptr,
base::BindRepeating(&ParseJsonDelayed),
GetFetchEndpoint(version_info::Channel::STABLE), api_key,
user_classifier_.get());
@@ -312,16 +225,6 @@ class RemoteSuggestionsFetcherImplTest : public testing::Test {
return result;
}
- void InitFakeURLFetcherFactory() {
- if (fake_url_fetcher_factory_) {
- return;
- }
- // Instantiation of factory automatically sets itself as URLFetcher's
- // factory.
- fake_url_fetcher_factory_.reset(new net::FakeURLFetcherFactory(
- /*default_factory=*/&failing_url_fetcher_factory_));
- }
-
void SetVariationParam(std::string param_name, std::string value) {
std::map<std::string, std::string> params = default_variation_params_;
params[param_name] = value;
@@ -335,23 +238,29 @@ class RemoteSuggestionsFetcherImplTest : public testing::Test {
void SetFakeResponse(const GURL& request_url,
const std::string& response_data,
net::HttpStatusCode response_code,
- net::URLRequestStatus::Status status) {
- InitFakeURLFetcherFactory();
- fake_url_fetcher_factory_->SetFakeResponse(request_url, response_data,
- response_code, status);
+ net::Error error) {
+ network::ResourceResponseHead head;
+ 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 = new net::HttpResponseHeaders(
+ net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()));
+ head.mime_type = "application/json";
+ network::URLLoaderCompletionStatus status(error);
+ status.decoded_body_length = response_data.size();
+ test_url_loader_factory_.AddResponse(request_url, head, response_data,
+ status);
}
protected:
std::map<std::string, std::string> default_variation_params_;
identity::IdentityTestEnvironment identity_test_env_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
private:
test::RemoteSuggestionsTestUtils utils_;
variations::testing::VariationParamsManager params_manager_;
scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
- FailingFakeURLFetcherFactory failing_url_fetcher_factory_;
- // Initialized lazily in SetFakeResponse().
- std::unique_ptr<net::FakeURLFetcherFactory> fake_url_fetcher_factory_;
std::unique_ptr<RemoteSuggestionsFetcherImpl> fetcher_;
std::unique_ptr<UserClassifier> user_classifier_;
MockSnippetsAvailableCallback mock_callback_;
@@ -390,10 +299,9 @@ TEST_F(RemoteSuggestionsFetcherImplTest, ShouldFetchSuccessfully) {
" \"faviconUrl\" : \"http://localhost/favicon.ico\" "
" }]"
"}]}";
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/kJsonStr, net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/kJsonStr, net::HTTP_OK, net::OK);
EXPECT_CALL(mock_callback(),
Run(Property(&Status::IsSuccess, true),
/*fetched_categories=*/AllOf(
@@ -413,11 +321,10 @@ TEST_F(RemoteSuggestionsFetcherImplTest, ShouldFetchSuccessfully) {
}
TEST_F(RemoteSuggestionsFetcherImplTest, ShouldExposeRequestPriorityInUrl) {
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=background_prefetch"),
- /*response_data=*/"{\"categories\" : []}", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=background_prefetch"),
+ /*response_data=*/"{\"categories\" : []}", net::HTTP_OK,
+ net::OK);
EXPECT_CALL(mock_callback(), Run(Property(&Status::IsSuccess, true),
/*fetched_categories=*/_));
@@ -437,10 +344,8 @@ TEST_F(RemoteSuggestionsFetcherImplTest,
SetVariationParam("append_request_priority_as_query_parameter", "false");
SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey"),
- /*response_data=*/"{\"categories\" : []}", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ GURL(std::string(kFetchSuggestionsEndpoint) + "?key=fakeAPIkey"),
+ /*response_data=*/"{\"categories\" : []}", net::HTTP_OK, net::OK);
EXPECT_CALL(mock_callback(), Run(Property(&Status::IsSuccess, true),
/*fetched_categories=*/_));
@@ -476,9 +381,8 @@ TEST_F(RemoteSuggestionsFetcherImplTest, ShouldFetchSuccessfullyWhenSignedIn) {
" }]"
"}]}";
SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?priority=user_action"),
- /*response_data=*/kJsonStr, net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ GURL(std::string(kFetchSuggestionsEndpoint) + "?priority=user_action"),
+ /*response_data=*/kJsonStr, net::HTTP_OK, net::OK);
EXPECT_CALL(mock_callback(),
Run(Property(&Status::IsSuccess, true),
/*fetched_categories=*/AllOf(
@@ -508,11 +412,10 @@ TEST_F(RemoteSuggestionsFetcherImplTest,
ShouldExposeRequestPriorityInUrlWhenSignedIn) {
SignIn();
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?priority=background_prefetch"),
- /*response_data=*/"{\"categories\" : []}", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?priority=background_prefetch"),
+ /*response_data=*/"{\"categories\" : []}", net::HTTP_OK,
+ net::OK);
EXPECT_CALL(mock_callback(), Run(Property(&Status::IsSuccess, true),
/*fetched_categories=*/_));
@@ -536,11 +439,9 @@ TEST_F(RemoteSuggestionsFetcherImplTest,
SignIn();
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch"),
- /*response_data=*/"{\"categories\" : []}", net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(kFetchSuggestionsEndpoint),
+ /*response_data=*/"{\"categories\" : []}", net::HTTP_OK,
+ net::OK);
EXPECT_CALL(mock_callback(), Run(Property(&Status::IsSuccess, true),
/*fetched_categories=*/_));
@@ -580,9 +481,8 @@ TEST_F(RemoteSuggestionsFetcherImplTest,
" }]"
"}]}";
SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?priority=user_action"),
- /*response_data=*/kJsonStr, net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ GURL(std::string(kFetchSuggestionsEndpoint) + "?priority=user_action"),
+ /*response_data=*/kJsonStr, net::HTTP_OK, net::OK);
EXPECT_CALL(mock_callback(),
Run(Property(&Status::IsSuccess, true),
/*fetched_categories=*/AllOf(
@@ -620,10 +520,9 @@ TEST_F(RemoteSuggestionsFetcherImplTest, EmptyCategoryIsOK) {
" \"id\": 1,"
" \"localizedTitle\": \"Articles for You\""
"}]}";
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/kJsonStr, net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/kJsonStr, net::HTTP_OK, net::OK);
EXPECT_CALL(mock_callback(),
Run(Property(&Status::IsSuccess, true),
/*fetched_categories=*/IsEmptyArticleList()));
@@ -674,10 +573,9 @@ TEST_F(RemoteSuggestionsFetcherImplTest, ServerCategories) {
" \"faviconUrl\" : \"http://localhost/favicon.ico\" "
" }]"
"}]}";
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/kJsonStr, net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/kJsonStr, net::HTTP_OK, net::OK);
RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories;
EXPECT_CALL(mock_callback(),
Run(Property(&Status::IsSuccess, true), /*fetched_categories=*/_))
@@ -738,10 +636,9 @@ TEST_F(RemoteSuggestionsFetcherImplTest,
" \"faviconUrl\" : \"http://localhost/favicon.ico\" "
" }]"
"}]}";
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/kJsonStr, net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/kJsonStr, net::HTTP_OK, net::OK);
RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories;
EXPECT_CALL(mock_callback(),
Run(Property(&Status::IsSuccess, true), /*fetched_categories=*/_))
@@ -806,10 +703,9 @@ TEST_F(RemoteSuggestionsFetcherImplTest, ExclusiveCategoryOnly) {
" \"faviconUrl\" : \"http://localhost/favicon.ico\" "
" }]"
"}]}";
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/kJsonStr, net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/kJsonStr, net::HTTP_OK, net::OK);
RemoteSuggestionsFetcher::OptionalFetchedCategories fetched_categories;
EXPECT_CALL(mock_callback(),
Run(Property(&Status::IsSuccess, true), /*fetched_categories=*/_))
@@ -856,10 +752,9 @@ TEST_F(RemoteSuggestionsFetcherImplTest, ShouldNotFetchWithoutApiKey) {
TEST_F(RemoteSuggestionsFetcherImplTest, ShouldFetchSuccessfullyEmptyList) {
const std::string kJsonStr = "{\"categories\": []}";
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/kJsonStr, net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/kJsonStr, net::HTTP_OK, net::OK);
EXPECT_CALL(mock_callback(),
Run(Property(&Status::IsSuccess, true),
/*fetched_categories=*/IsEmptyCategoriesList()));
@@ -877,16 +772,12 @@ TEST_F(RemoteSuggestionsFetcherImplTest, ShouldFetchSuccessfullyEmptyList) {
}
TEST_F(RemoteSuggestionsFetcherImplTest, RetryOnInteractiveRequests) {
- DelegateCallingTestURLFetcherFactory fetcher_factory;
RequestParams params = test_params();
params.interactive_request = true;
- fetcher().FetchSnippets(params,
- ToSnippetsAvailableCallback(&mock_callback()));
-
- net::TestURLFetcher* fetcher = fetcher_factory.GetLastCreatedFetcher();
- ASSERT_THAT(fetcher, NotNull());
- EXPECT_THAT(fetcher->GetMaxRetriesOn5xx(), Eq(2));
+ EXPECT_THAT(
+ internal::JsonRequest::Get5xxRetryCount(params.interactive_request),
+ Eq(2));
}
TEST_F(RemoteSuggestionsFetcherImplTest,
@@ -906,25 +797,43 @@ TEST_F(RemoteSuggestionsFetcherImplTest,
params.interactive_request = false;
for (const auto& retry_config : retry_config_expectation) {
- DelegateCallingTestURLFetcherFactory fetcher_factory;
+ // DelegateCallingTestURLFetcherFactory fetcher_factory;
SetVariationParam("background_5xx_retries_count", retry_config.param_value);
- fetcher().FetchSnippets(params,
- ToSnippetsAvailableCallback(&mock_callback()));
-
- net::TestURLFetcher* fetcher = fetcher_factory.GetLastCreatedFetcher();
- ASSERT_THAT(fetcher, NotNull());
- EXPECT_THAT(fetcher->GetMaxRetriesOn5xx(), Eq(retry_config.expected_value))
+ EXPECT_THAT(internal::JsonRequest::Get5xxRetryCount(false),
+ Eq(retry_config.expected_value))
<< retry_config.description;
}
}
-TEST_F(RemoteSuggestionsFetcherImplTest, ShouldReportUrlStatusError) {
+TEST_F(RemoteSuggestionsFetcherImplTest,
+ HttpUnauthorizedIsTreatedAsDistinctTemporaryError) {
+ SignIn();
SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/std::string(), net::HTTP_NOT_FOUND,
- net::URLRequestStatus::FAILED);
+ GURL(std::string(kFetchSuggestionsEndpoint) + "?priority=user_action"),
+ /*response_data=*/std::string(), net::HTTP_UNAUTHORIZED, net::OK);
+ EXPECT_CALL(
+ mock_callback(),
+ Run(Field(&Status::code, StatusCode::TEMPORARY_ERROR),
+ /*fetched_categories=*/Property(
+ &base::Optional<std::vector<FetchedCategory>>::has_value, false)))
+ .Times(1);
+ fetcher().FetchSnippets(test_params(),
+ ToSnippetsAvailableCallback(&mock_callback()));
+
+ identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+ "access_token", base::Time::Max());
+ FastForwardUntilNoTasksRemain();
+
+ EXPECT_THAT(fetcher().GetLastStatusForDebugging(),
+ Eq("Access token invalid 401"));
+}
+
+TEST_F(RemoteSuggestionsFetcherImplTest, ShouldReportUrlStatusError) {
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/std::string(), net::HTTP_NOT_FOUND,
+ net::ERR_FAILED);
EXPECT_CALL(
mock_callback(),
Run(Field(&Status::code, StatusCode::TEMPORARY_ERROR),
@@ -948,11 +857,10 @@ TEST_F(RemoteSuggestionsFetcherImplTest, ShouldReportUrlStatusError) {
}
TEST_F(RemoteSuggestionsFetcherImplTest, ShouldReportHttpError) {
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/std::string(), net::HTTP_NOT_FOUND,
- net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/std::string(), net::HTTP_NOT_FOUND,
+ net::OK);
EXPECT_CALL(
mock_callback(),
Run(Field(&Status::code, StatusCode::TEMPORARY_ERROR),
@@ -975,11 +883,9 @@ TEST_F(RemoteSuggestionsFetcherImplTest, ShouldReportHttpError) {
TEST_F(RemoteSuggestionsFetcherImplTest, ShouldReportJsonError) {
const std::string kInvalidJsonStr = "{ \"recos\": []";
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/kInvalidJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/kInvalidJsonStr, net::HTTP_OK, net::OK);
EXPECT_CALL(
mock_callback(),
Run(Field(&Status::code, StatusCode::TEMPORARY_ERROR),
@@ -1005,11 +911,9 @@ TEST_F(RemoteSuggestionsFetcherImplTest, ShouldReportJsonError) {
TEST_F(RemoteSuggestionsFetcherImplTest,
ShouldReportJsonErrorForEmptyResponse) {
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/std::string(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/std::string(), net::HTTP_OK, net::OK);
EXPECT_CALL(
mock_callback(),
Run(Field(&Status::code, StatusCode::TEMPORARY_ERROR),
@@ -1031,10 +935,9 @@ TEST_F(RemoteSuggestionsFetcherImplTest,
TEST_F(RemoteSuggestionsFetcherImplTest, ShouldReportInvalidListError) {
const std::string kJsonStr =
"{\"recos\": [{ \"contentInfo\": { \"foo\" : \"bar\" }}]}";
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/kJsonStr, net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/kJsonStr, net::HTTP_OK, net::OK);
EXPECT_CALL(
mock_callback(),
Run(Field(&Status::code, StatusCode::TEMPORARY_ERROR),
@@ -1078,11 +981,9 @@ TEST_F(RemoteSuggestionsFetcherImplTest,
" \"faviconUrl\" : \"http://localhost/favicon.ico\" "
" }]"
"}]}";
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/kValidJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/kValidJsonStr, net::HTTP_OK, net::OK);
EXPECT_CALL(
mock_callback(),
Run(Field(&Status::code, StatusCode::TEMPORARY_ERROR),
@@ -1125,11 +1026,9 @@ TEST_F(RemoteSuggestionsFetcherImplTest,
" \"faviconUrl\" : \"http://localhost/favicon.ico\" "
" }]"
"}]}";
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/kValidJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/kValidJsonStr, net::HTTP_OK, net::OK);
EXPECT_CALL(
mock_callback(),
Run(Field(&Status::code, StatusCode::TEMPORARY_ERROR),
@@ -1164,11 +1063,9 @@ TEST_F(RemoteSuggestionsFetcherImplTest,
" \"faviconUrl\" : \"http://localhost/favicon.ico\" "
" }]"
"}]}";
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/kValidJsonStr, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/kValidJsonStr, net::HTTP_OK, net::OK);
EXPECT_CALL(
mock_callback(),
Run(Field(&Status::code, StatusCode::TEMPORARY_ERROR),
@@ -1184,11 +1081,10 @@ TEST_F(RemoteSuggestionsFetcherImplTest,
TEST_F(RemoteSuggestionsFetcherImplTest,
ShouldReportRequestFailureAsTemporaryError) {
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/std::string(), net::HTTP_NOT_FOUND,
- net::URLRequestStatus::FAILED);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/std::string(), net::HTTP_NOT_FOUND,
+ net::ERR_FAILED);
EXPECT_CALL(
mock_callback(),
Run(Field(&Status::code, StatusCode::TEMPORARY_ERROR),
@@ -1204,7 +1100,10 @@ TEST_F(RemoteSuggestionsFetcherImplTest,
// hard-to-reproduce test failures.
TEST_F(RemoteSuggestionsFetcherImplTest,
ShouldReportHttpErrorForMissingBakedResponse) {
- InitFakeURLFetcherFactory();
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/std::string(), net::HTTP_NOT_FOUND,
+ net::ERR_FAILED);
EXPECT_CALL(
mock_callback(),
Run(Field(&Status::code, StatusCode::TEMPORARY_ERROR),
@@ -1218,10 +1117,9 @@ TEST_F(RemoteSuggestionsFetcherImplTest,
TEST_F(RemoteSuggestionsFetcherImplTest, ShouldProcessConcurrentFetches) {
const std::string kJsonStr = "{ \"categories\": [] }";
- SetFakeResponse(
- GURL("https://chromecontentsuggestions-pa.googleapis.com/v1/suggestions/"
- "fetch?key=fakeAPIkey&priority=user_action"),
- /*response_data=*/kJsonStr, net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ SetFakeResponse(GURL(std::string(kFetchSuggestionsEndpoint) +
+ "?key=fakeAPIkey&priority=user_action"),
+ /*response_data=*/kJsonStr, net::HTTP_OK, net::OK);
EXPECT_CALL(mock_callback(),
Run(Property(&Status::IsSuccess, true),
/*fetched_categories=*/IsEmptyCategoriesList()))
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
index 8f579741544..df713edf355 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
@@ -21,7 +21,7 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_clock.h"
#include "base/test/test_mock_time_task_runner.h"
@@ -164,8 +164,8 @@ std::unique_ptr<RemoteSuggestion> CreateTestRemoteSuggestion(
void ServeOneByOneImage(
const std::string& id,
- image_fetcher::ImageFetcher::ImageDataFetcherCallback* image_data_callback,
- image_fetcher::ImageFetcher::ImageFetcherCallback* callback) {
+ image_fetcher::ImageDataFetcherCallback* image_data_callback,
+ image_fetcher::ImageFetcherCallback* callback) {
std::move(*image_data_callback)
.Run("1-by-1-image-data", image_fetcher::RequestMetadata());
base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
index 20310aa2400..4f0777af2ee 100644
--- a/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
+++ b/chromium/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
@@ -22,7 +22,6 @@
#include "components/ntp_snippets/remote/persistent_scheduler.h"
#include "components/ntp_snippets/remote/remote_suggestions_provider.h"
#include "components/ntp_snippets/status.h"
-#include "components/ntp_snippets/time_serialization.h"
#include "components/ntp_snippets/user_classifier.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
@@ -359,7 +358,7 @@ class EulaState final : public web_resource::EulaAcceptedNotifier::Observer {
eula_notifier_->Init(this);
}
- ~EulaState() = default;
+ ~EulaState() override = default;
bool IsEulaAccepted() {
if (!eula_notifier_) {
@@ -481,16 +480,21 @@ RemoteSuggestionsSchedulerImpl::~RemoteSuggestionsSchedulerImpl() = default;
// static
void RemoteSuggestionsSchedulerImpl::RegisterProfilePrefs(
PrefRegistrySimple* registry) {
- registry->RegisterInt64Pref(prefs::kSnippetPersistentFetchingIntervalWifi, 0);
- registry->RegisterInt64Pref(prefs::kSnippetPersistentFetchingIntervalFallback,
- 0);
- registry->RegisterInt64Pref(prefs::kSnippetStartupFetchingIntervalWifi, 0);
- registry->RegisterInt64Pref(prefs::kSnippetStartupFetchingIntervalFallback,
- 0);
- registry->RegisterInt64Pref(prefs::kSnippetShownFetchingIntervalWifi, 0);
- registry->RegisterInt64Pref(prefs::kSnippetShownFetchingIntervalFallback, 0);
- registry->RegisterInt64Pref(prefs::kSnippetLastFetchAttemptTime, 0);
- registry->RegisterInt64Pref(prefs::kSnippetLastSuccessfulFetchTime, 0);
+ registry->RegisterTimeDeltaPref(prefs::kSnippetPersistentFetchingIntervalWifi,
+ base::TimeDelta());
+ registry->RegisterTimeDeltaPref(
+ prefs::kSnippetPersistentFetchingIntervalFallback, base::TimeDelta());
+ registry->RegisterTimeDeltaPref(prefs::kSnippetStartupFetchingIntervalWifi,
+ base::TimeDelta());
+ registry->RegisterTimeDeltaPref(
+ prefs::kSnippetStartupFetchingIntervalFallback, base::TimeDelta());
+ registry->RegisterTimeDeltaPref(prefs::kSnippetShownFetchingIntervalWifi,
+ base::TimeDelta());
+ registry->RegisterTimeDeltaPref(prefs::kSnippetShownFetchingIntervalFallback,
+ base::TimeDelta());
+ registry->RegisterTimePref(prefs::kSnippetLastFetchAttemptTime, base::Time());
+ registry->RegisterTimePref(prefs::kSnippetLastSuccessfulFetchTime,
+ base::Time());
}
void RemoteSuggestionsSchedulerImpl::SetProvider(
@@ -644,38 +648,34 @@ RemoteSuggestionsSchedulerImpl::GetDesiredFetchingSchedule() const {
}
void RemoteSuggestionsSchedulerImpl::LoadLastFetchingSchedule() {
- schedule_.interval_persistent_wifi = DeserializeTimeDelta(
- profile_prefs_->GetInt64(prefs::kSnippetPersistentFetchingIntervalWifi));
- schedule_.interval_persistent_fallback =
- DeserializeTimeDelta(profile_prefs_->GetInt64(
- prefs::kSnippetPersistentFetchingIntervalFallback));
- schedule_.interval_startup_wifi = DeserializeTimeDelta(
- profile_prefs_->GetInt64(prefs::kSnippetStartupFetchingIntervalWifi));
- schedule_.interval_startup_fallback = DeserializeTimeDelta(
- profile_prefs_->GetInt64(prefs::kSnippetStartupFetchingIntervalFallback));
- schedule_.interval_shown_wifi = DeserializeTimeDelta(
- profile_prefs_->GetInt64(prefs::kSnippetShownFetchingIntervalWifi));
- schedule_.interval_shown_fallback = DeserializeTimeDelta(
- profile_prefs_->GetInt64(prefs::kSnippetShownFetchingIntervalFallback));
+ schedule_.interval_persistent_wifi = profile_prefs_->GetTimeDelta(
+ prefs::kSnippetPersistentFetchingIntervalWifi);
+ schedule_.interval_persistent_fallback = profile_prefs_->GetTimeDelta(
+ prefs::kSnippetPersistentFetchingIntervalFallback);
+ schedule_.interval_startup_wifi =
+ profile_prefs_->GetTimeDelta(prefs::kSnippetStartupFetchingIntervalWifi);
+ schedule_.interval_startup_fallback = profile_prefs_->GetTimeDelta(
+ prefs::kSnippetStartupFetchingIntervalFallback);
+ schedule_.interval_shown_wifi =
+ profile_prefs_->GetTimeDelta(prefs::kSnippetShownFetchingIntervalWifi);
+ schedule_.interval_shown_fallback = profile_prefs_->GetTimeDelta(
+ prefs::kSnippetShownFetchingIntervalFallback);
}
void RemoteSuggestionsSchedulerImpl::StoreFetchingSchedule() {
- profile_prefs_->SetInt64(
- prefs::kSnippetPersistentFetchingIntervalWifi,
- SerializeTimeDelta(schedule_.interval_persistent_wifi));
- profile_prefs_->SetInt64(
+ profile_prefs_->SetTimeDelta(prefs::kSnippetPersistentFetchingIntervalWifi,
+ schedule_.interval_persistent_wifi);
+ profile_prefs_->SetTimeDelta(
prefs::kSnippetPersistentFetchingIntervalFallback,
- SerializeTimeDelta(schedule_.interval_persistent_fallback));
- profile_prefs_->SetInt64(prefs::kSnippetStartupFetchingIntervalWifi,
- SerializeTimeDelta(schedule_.interval_startup_wifi));
- profile_prefs_->SetInt64(
- prefs::kSnippetStartupFetchingIntervalFallback,
- SerializeTimeDelta(schedule_.interval_startup_fallback));
- profile_prefs_->SetInt64(prefs::kSnippetShownFetchingIntervalWifi,
- SerializeTimeDelta(schedule_.interval_shown_wifi));
- profile_prefs_->SetInt64(
- prefs::kSnippetShownFetchingIntervalFallback,
- SerializeTimeDelta(schedule_.interval_shown_fallback));
+ schedule_.interval_persistent_fallback);
+ profile_prefs_->SetTimeDelta(prefs::kSnippetStartupFetchingIntervalWifi,
+ schedule_.interval_startup_wifi);
+ profile_prefs_->SetTimeDelta(prefs::kSnippetStartupFetchingIntervalFallback,
+ schedule_.interval_startup_fallback);
+ profile_prefs_->SetTimeDelta(prefs::kSnippetShownFetchingIntervalWifi,
+ schedule_.interval_shown_wifi);
+ profile_prefs_->SetTimeDelta(prefs::kSnippetShownFetchingIntervalFallback,
+ schedule_.interval_shown_fallback);
}
bool RemoteSuggestionsSchedulerImpl::IsLastSuccessfulFetchStale() const {
@@ -685,8 +685,8 @@ bool RemoteSuggestionsSchedulerImpl::IsLastSuccessfulFetchStale() const {
if (!profile_prefs_->HasPrefPath(prefs::kSnippetLastSuccessfulFetchTime)) {
return false;
}
- const base::Time last_successful_fetch_time = DeserializeTime(
- profile_prefs_->GetInt64(prefs::kSnippetLastSuccessfulFetchTime));
+ const base::Time last_successful_fetch_time =
+ profile_prefs_->GetTime(prefs::kSnippetLastSuccessfulFetchTime);
return clock_->Now() - last_successful_fetch_time >
schedule_.GetStalenessInterval();
@@ -719,8 +719,8 @@ void RemoteSuggestionsSchedulerImpl::RefetchIfAppropriate(TriggerType trigger) {
return;
}
- const base::Time last_fetch_attempt_time = base::Time::FromInternalValue(
- profile_prefs_->GetInt64(prefs::kSnippetLastFetchAttemptTime));
+ const base::Time last_fetch_attempt_time =
+ profile_prefs_->GetTime(prefs::kSnippetLastFetchAttemptTime);
if (trigger == TriggerType::SURFACE_OPENED &&
!time_until_first_shown_trigger_reported_) {
@@ -866,8 +866,7 @@ void RemoteSuggestionsSchedulerImpl::RefetchFinished(Status fetch_status) {
}
void RemoteSuggestionsSchedulerImpl::OnFetchCompleted(Status fetch_status) {
- profile_prefs_->SetInt64(prefs::kSnippetLastFetchAttemptTime,
- SerializeTime(clock_->Now()));
+ profile_prefs_->SetTime(prefs::kSnippetLastFetchAttemptTime, clock_->Now());
time_until_first_shown_trigger_reported_ = false;
time_until_first_startup_trigger_reported_ = false;
@@ -879,8 +878,8 @@ void RemoteSuggestionsSchedulerImpl::OnFetchCompleted(Status fetch_status) {
return;
}
- profile_prefs_->SetInt64(prefs::kSnippetLastSuccessfulFetchTime,
- SerializeTime(clock_->Now()));
+ profile_prefs_->SetTime(prefs::kSnippetLastSuccessfulFetchTime,
+ clock_->Now());
ApplyPersistentFetchingSchedule();
}
@@ -889,8 +888,7 @@ void RemoteSuggestionsSchedulerImpl::ClearLastFetchAttemptTime() {
profile_prefs_->ClearPref(prefs::kSnippetLastFetchAttemptTime);
// To mark the last fetch as stale, we need to keep the time in prefs, only
// making sure it is long ago.
- profile_prefs_->SetInt64(prefs::kSnippetLastSuccessfulFetchTime,
- SerializeTime(base::Time()));
+ profile_prefs_->SetTime(prefs::kSnippetLastSuccessfulFetchTime, base::Time());
}
std::set<RemoteSuggestionsSchedulerImpl::TriggerType>
diff --git a/chromium/components/ntp_snippets/remote/test_utils.cc b/chromium/components/ntp_snippets/remote/test_utils.cc
index ea48f1eea00..2e734b46a2c 100644
--- a/chromium/components/ntp_snippets/remote/test_utils.cc
+++ b/chromium/components/ntp_snippets/remote/test_utils.cc
@@ -14,24 +14,13 @@ namespace ntp_snippets {
namespace test {
FakeSyncService::FakeSyncService()
- : can_sync_start_(true),
- is_sync_active_(true),
- configuration_done_(true),
- is_encrypt_everything_enabled_(false),
+ : is_encrypt_everything_enabled_(false),
active_data_types_(syncer::HISTORY_DELETE_DIRECTIVES) {}
FakeSyncService::~FakeSyncService() = default;
-bool FakeSyncService::CanSyncStart() const {
- return can_sync_start_;
-}
-
-bool FakeSyncService::IsSyncActive() const {
- return is_sync_active_;
-}
-
-bool FakeSyncService::ConfigurationDone() const {
- return configuration_done_;
+int FakeSyncService::GetDisableReasons() const {
+ return DISABLE_REASON_NONE;
}
bool FakeSyncService::IsEncryptEverythingEnabled() const {
diff --git a/chromium/components/ntp_snippets/remote/test_utils.h b/chromium/components/ntp_snippets/remote/test_utils.h
index f72f52c021d..afa25329b53 100644
--- a/chromium/components/ntp_snippets/remote/test_utils.h
+++ b/chromium/components/ntp_snippets/remote/test_utils.h
@@ -24,15 +24,10 @@ class FakeSyncService : public syncer::FakeSyncService {
FakeSyncService();
~FakeSyncService() override;
- bool CanSyncStart() const override;
- bool IsSyncActive() const override;
- bool ConfigurationDone() const override;
+ int GetDisableReasons() const override;
bool IsEncryptEverythingEnabled() const override;
syncer::ModelTypeSet GetActiveDataTypes() const override;
- bool can_sync_start_;
- bool is_sync_active_;
- bool configuration_done_;
bool is_encrypt_everything_enabled_;
syncer::ModelTypeSet active_data_types_;
};
diff --git a/chromium/components/ntp_snippets/time_serialization.cc b/chromium/components/ntp_snippets/time_serialization.cc
index 8ba76099aa7..95343f6ed36 100644
--- a/chromium/components/ntp_snippets/time_serialization.cc
+++ b/chromium/components/ntp_snippets/time_serialization.cc
@@ -7,19 +7,11 @@
namespace ntp_snippets {
int64_t SerializeTime(const base::Time& time) {
- return SerializeTimeDelta(time - base::Time());
+ return (time - base::Time()).InMicroseconds();
}
base::Time DeserializeTime(int64_t serialized_time) {
- return base::Time() + DeserializeTimeDelta(serialized_time);
-}
-
-int64_t SerializeTimeDelta(const base::TimeDelta& time_delta) {
- return time_delta.InMicroseconds();
-}
-
-base::TimeDelta DeserializeTimeDelta(int64_t serialized_time_delta) {
- return base::TimeDelta::FromMicroseconds(serialized_time_delta);
+ return base::Time() + base::TimeDelta::FromMicroseconds(serialized_time);
}
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/time_serialization.h b/chromium/components/ntp_snippets/time_serialization.h
index eaab4d9ee53..46d927cff4d 100644
--- a/chromium/components/ntp_snippets/time_serialization.h
+++ b/chromium/components/ntp_snippets/time_serialization.h
@@ -17,10 +17,6 @@ namespace ntp_snippets {
int64_t SerializeTime(const base::Time& time);
base::Time DeserializeTime(int64_t serialized_time);
-// Same as above, but for base::TimeDelta.
-int64_t SerializeTimeDelta(const base::TimeDelta& time_delta);
-base::TimeDelta DeserializeTimeDelta(int64_t serialized_time_delta);
-
} // namespace ntp_snippets
#endif // COMPONENTS_NTP_SNIPPETS_TIME_SERIALIZATION_H_
diff --git a/chromium/components/ntp_snippets/time_serialization_unittest.cc b/chromium/components/ntp_snippets/time_serialization_unittest.cc
index b59d3723dff..26c3200defd 100644
--- a/chromium/components/ntp_snippets/time_serialization_unittest.cc
+++ b/chromium/components/ntp_snippets/time_serialization_unittest.cc
@@ -22,17 +22,4 @@ TEST(TimeSerializationTest, TimeSerialization) {
}
}
-TEST(TimeSerializationTest, TimeDeltaSerialization) {
- std::vector<base::TimeDelta> values_to_test = {
- base::TimeDelta::Min(), base::TimeDelta::FromHours(-1),
- base::TimeDelta::FromSeconds(0), base::TimeDelta::FromHours(1),
- base::TimeDelta::Max()};
- for (const base::TimeDelta& value : values_to_test) {
- EXPECT_EQ(SerializeTimeDelta(value), value.ToInternalValue());
- EXPECT_EQ(base::TimeDelta::FromInternalValue(SerializeTimeDelta(value)),
- value);
- EXPECT_EQ(DeserializeTimeDelta(SerializeTimeDelta(value)), value);
- }
-}
-
} // namespace ntp_snippets
diff --git a/chromium/components/ntp_snippets/user_classifier_unittest.cc b/chromium/components/ntp_snippets/user_classifier_unittest.cc
index 2dd874924d9..4c468831aa3 100644
--- a/chromium/components/ntp_snippets/user_classifier_unittest.cc
+++ b/chromium/components/ntp_snippets/user_classifier_unittest.cc
@@ -8,7 +8,7 @@
#include <string>
#include <utility>
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
#include "components/ntp_snippets/features.h"
diff --git a/chromium/components/ntp_snippets_strings.grdp b/chromium/components/ntp_snippets_strings.grdp
index ab1517ce2e2..8b1c4d7ea25 100644
--- a/chromium/components/ntp_snippets_strings.grdp
+++ b/chromium/components/ntp_snippets_strings.grdp
@@ -18,7 +18,7 @@
</if>
<if expr="not use_titlecase">
- <message name="IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_HEADER" desc="Header of the articles section, which is a list of news articles displayed as cards on the New Tab Page.">
+ <message name="IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_HEADER" desc="Header of the articles section, which is a list of news articles displayed as cards on the New Tab Page." formatter_data="android_java">
Articles for you
</message>
</if>
diff --git a/chromium/components/ntp_tiles/BUILD.gn b/chromium/components/ntp_tiles/BUILD.gn
index a01efec1bf3..5ca6c115478 100644
--- a/chromium/components/ntp_tiles/BUILD.gn
+++ b/chromium/components/ntp_tiles/BUILD.gn
@@ -12,6 +12,11 @@ static_library("ntp_tiles") {
"constants.h",
"country_code_ios.h",
"country_code_ios.mm",
+ "custom_links_manager.h",
+ "custom_links_manager_impl.cc",
+ "custom_links_manager_impl.h",
+ "custom_links_store.cc",
+ "custom_links_store.h",
"icon_cacher.h",
"icon_cacher_impl.cc",
"icon_cacher_impl.h",
@@ -59,6 +64,7 @@ static_library("ntp_tiles") {
"//components/url_formatter",
"//components/variations",
"//components/variations/service",
+ "//services/network/public/cpp",
"//ui/base",
]
}
@@ -82,6 +88,8 @@ source_set("json_unsafe_parser") {
source_set("unit_tests") {
testonly = true
sources = [
+ "custom_links_manager_impl_unittest.cc",
+ "custom_links_store_unittest.cc",
"icon_cacher_impl_unittest.cc",
"metrics_unittest.cc",
"most_visited_sites_unittest.cc",
@@ -100,6 +108,8 @@ source_set("unit_tests") {
"//components/rappor:test_support",
"//components/sync_preferences:test_support",
"//net:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//testing/gmock",
"//testing/gtest",
"//ui/base",
diff --git a/chromium/components/ntp_tiles/DEPS b/chromium/components/ntp_tiles/DEPS
index cf31b04ca16..5339a09f89b 100644
--- a/chromium/components/ntp_tiles/DEPS
+++ b/chromium/components/ntp_tiles/DEPS
@@ -16,6 +16,8 @@ include_rules = [
"+components/variations",
"+jni",
"+net",
+ "+services/network/public/cpp",
+ "+services/network/test",
"+ui/gfx",
"+ui/base",
]
diff --git a/chromium/components/ntp_tiles/constants.cc b/chromium/components/ntp_tiles/constants.cc
index 5cec78488ec..3def594b6c3 100644
--- a/chromium/components/ntp_tiles/constants.cc
+++ b/chromium/components/ntp_tiles/constants.cc
@@ -5,6 +5,8 @@
#include "components/ntp_tiles/constants.h"
#include "base/feature_list.h"
+#include "build/build_config.h"
+#include "ui/base/ui_base_features.h"
namespace ntp_tiles {
@@ -22,4 +24,29 @@ const base::Feature kSiteExplorationUiFeature{
const base::Feature kUsePopularSitesSuggestions{
"UsePopularSitesSuggestions", base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kNtpIcons{"NewTabPageIcons",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kNtpCustomLinks{"NewTabPageCustomLinks",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool IsMDIconsEnabled() {
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
+ return base::FeatureList::IsEnabled(kNtpIcons) ||
+ base::FeatureList::IsEnabled(kNtpCustomLinks) ||
+ base::FeatureList::IsEnabled(features::kExperimentalUi);
+#else
+ return false;
+#endif
+}
+
+bool IsCustomLinksEnabled() {
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
+ return base::FeatureList::IsEnabled(kNtpCustomLinks) ||
+ base::FeatureList::IsEnabled(features::kExperimentalUi);
+#else
+ return false;
+#endif
+}
+
} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/constants.h b/chromium/components/ntp_tiles/constants.h
index 7395995f975..508f456e834 100644
--- a/chromium/components/ntp_tiles/constants.h
+++ b/chromium/components/ntp_tiles/constants.h
@@ -29,6 +29,19 @@ extern const base::Feature kSiteExplorationUiFeature;
// If this feature is enabled, we enable popular sites in the suggestions UI.
extern const base::Feature kUsePopularSitesSuggestions;
+// Feature that enables the GM2 design for Most Visited. Desktop only.
+extern const base::Feature kNtpIcons;
+
+// Feature that enables custom links and replaces Most Visited. Implicitly
+// enables |kNtpIcons|. Desktop only.
+extern const base::Feature kNtpCustomLinks;
+
+// Returns whether the GM2 design for Most Visited is enabled.
+bool IsMDIconsEnabled();
+
+// Returns whether the custom links is enabled.
+bool IsCustomLinksEnabled();
+
} // namespace ntp_tiles
#endif // COMPONENTS_NTP_TILES_CONSTANTS_H_
diff --git a/chromium/components/ntp_tiles/custom_links_manager.h b/chromium/components/ntp_tiles/custom_links_manager.h
new file mode 100644
index 00000000000..d6c20ba7ec9
--- /dev/null
+++ b/chromium/components/ntp_tiles/custom_links_manager.h
@@ -0,0 +1,72 @@
+// 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_NTP_TILES_CUSTOM_LINKS_MANAGER_H_
+#define COMPONENTS_NTP_TILES_CUSTOM_LINKS_MANAGER_H_
+
+#include <vector>
+
+#include "base/strings/string16.h"
+#include "components/ntp_tiles/ntp_tile.h"
+#include "url/gurl.h"
+
+namespace ntp_tiles {
+
+// Interface to manage and store custom links for the NTP. Initialized from
+// MostVisitedSites.
+//
+// Custom links replaces the Most Visited tiles and allows users to manually
+// add, edit, and delete tiles (i.e. links) up to a certain maximum. Duplicate
+// URLs are not allowed, and the links are stored locally per profile.
+// TODO(crbug/861831): Add Chrome sync support.
+class CustomLinksManager {
+ public:
+ struct Link {
+ GURL url;
+ base::string16 title;
+
+ bool operator==(const Link& other) const {
+ return url == other.url && title == other.title;
+ }
+ };
+
+ virtual ~CustomLinksManager() = default;
+
+ // Fills the initial links with |tiles| and sets the initalized status to
+ // true. Returns false and does nothing if custom links has already been
+ // initialized.
+ virtual bool Initialize(const NTPTilesVector& tiles) = 0;
+ // Uninitializes custom links and clears the current links from storage.
+ virtual void Uninitialize() = 0;
+ // True if custom links is initialized and Most Visited tiles have been
+ // replaced by custom links.
+ virtual bool IsInitialized() const = 0;
+
+ // Returns the current links.
+ virtual const std::vector<Link>& GetLinks() const = 0;
+
+ // Adds a link to the end of the list. Returns false and does nothing if
+ // custom links is not initialized, |url| is invalid, we're at the maximum
+ // number of links, or |url| already exists in the list.
+ virtual bool AddLink(const GURL& url, const base::string16& title) = 0;
+ // Updates the URL and/or title of the link specified by |url|. Returns
+ // false and does nothing if custom links is not initialized, either URL is
+ // invalid, |url| does not exist in the list, |new_url| already exists in the
+ // list, or both parameters are empty.
+ virtual bool UpdateLink(const GURL& url,
+ const GURL& new_url,
+ const base::string16& new_title) = 0;
+ // Deletes the link with the specified |url|. Returns false and does nothing
+ // if custom links is not initialized, |url| is invalid, or |url| does not
+ // exist in the list.
+ virtual bool DeleteLink(const GURL& url) = 0;
+ // Restores the previous state of the list of links. Used to undo the previous
+ // action (add, edit, delete, etc.). Returns false and does nothing if custom
+ // links is not initialized or there is no previous state to restore.
+ virtual bool UndoAction() = 0;
+};
+
+} // namespace ntp_tiles
+
+#endif // COMPONENTS_NTP_TILES_CUSTOM_LINKS_MANAGER_H_
diff --git a/chromium/components/ntp_tiles/custom_links_manager_impl.cc b/chromium/components/ntp_tiles/custom_links_manager_impl.cc
new file mode 100644
index 00000000000..f744eca278b
--- /dev/null
+++ b/chromium/components/ntp_tiles/custom_links_manager_impl.cc
@@ -0,0 +1,149 @@
+// 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/ntp_tiles/custom_links_manager_impl.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "components/ntp_tiles/pref_names.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+
+namespace ntp_tiles {
+
+namespace {
+
+const int kMaxNumLinks = 10;
+
+} // namespace
+
+CustomLinksManagerImpl::CustomLinksManagerImpl(PrefService* prefs)
+ : prefs_(prefs), store_(prefs), weak_ptr_factory_(this) {
+ DCHECK(prefs);
+ if (IsInitialized())
+ current_links_ = store_.RetrieveLinks();
+}
+
+CustomLinksManagerImpl::~CustomLinksManagerImpl() = default;
+
+bool CustomLinksManagerImpl::Initialize(const NTPTilesVector& tiles) {
+ if (IsInitialized())
+ return false;
+
+ for (const NTPTile& tile : tiles)
+ current_links_.emplace_back(Link{tile.url, tile.title});
+
+ store_.StoreLinks(current_links_);
+ prefs_->SetBoolean(prefs::kCustomLinksInitialized, true);
+ return true;
+}
+
+void CustomLinksManagerImpl::Uninitialize() {
+ ClearLinks();
+ prefs_->SetBoolean(prefs::kCustomLinksInitialized, false);
+}
+
+bool CustomLinksManagerImpl::IsInitialized() const {
+ return prefs_->GetBoolean(prefs::kCustomLinksInitialized);
+}
+
+const std::vector<CustomLinksManager::Link>& CustomLinksManagerImpl::GetLinks()
+ const {
+ return current_links_;
+}
+
+bool CustomLinksManagerImpl::AddLink(const GURL& url,
+ const base::string16& title) {
+ if (!IsInitialized() || !url.is_valid() ||
+ current_links_.size() == kMaxNumLinks) {
+ return false;
+ }
+
+ if (FindLinkWithUrl(url) != current_links_.end())
+ return false;
+
+ previous_links_ = current_links_;
+ current_links_.emplace_back(Link{url, title});
+ store_.StoreLinks(current_links_);
+ return true;
+}
+
+bool CustomLinksManagerImpl::UpdateLink(const GURL& url,
+ const GURL& new_url,
+ const base::string16& new_title) {
+ if (!IsInitialized() || !url.is_valid() ||
+ (new_url.is_empty() && new_title.empty())) {
+ return false;
+ }
+
+ // Do not update if |new_url| is invalid or already exists in the list.
+ if (!new_url.is_empty() &&
+ (!new_url.is_valid() ||
+ FindLinkWithUrl(new_url) != current_links_.end())) {
+ return false;
+ }
+
+ auto it = FindLinkWithUrl(url);
+ if (it == current_links_.end())
+ return false;
+
+ // At this point, we will be modifying at least one of the values.
+ previous_links_ = current_links_;
+
+ if (!new_url.is_empty())
+ it->url = new_url;
+ if (!new_title.empty())
+ it->title = new_title;
+
+ store_.StoreLinks(current_links_);
+ return true;
+}
+
+bool CustomLinksManagerImpl::DeleteLink(const GURL& url) {
+ if (!IsInitialized() || !url.is_valid())
+ return false;
+
+ auto it = FindLinkWithUrl(url);
+ if (it == current_links_.end())
+ return false;
+
+ previous_links_ = current_links_;
+ current_links_.erase(it);
+ store_.StoreLinks(current_links_);
+ return true;
+}
+
+bool CustomLinksManagerImpl::UndoAction() {
+ if (!IsInitialized() || !previous_links_.has_value())
+ return false;
+
+ // Replace the current links with the previous state.
+ current_links_ = *previous_links_;
+ previous_links_ = base::nullopt;
+ store_.StoreLinks(current_links_);
+ return true;
+}
+
+void CustomLinksManagerImpl::ClearLinks() {
+ store_.ClearLinks();
+ current_links_.clear();
+ previous_links_ = base::nullopt;
+}
+
+std::vector<CustomLinksManager::Link>::iterator
+CustomLinksManagerImpl::FindLinkWithUrl(const GURL& url) {
+ return std::find_if(current_links_.begin(), current_links_.end(),
+ [&url](const Link& link) { return link.url == url; });
+}
+
+// static
+void CustomLinksManagerImpl::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* user_prefs) {
+ user_prefs->RegisterBooleanPref(prefs::kCustomLinksInitialized, false);
+ CustomLinksStore::RegisterProfilePrefs(user_prefs);
+}
+
+} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/custom_links_manager_impl.h b/chromium/components/ntp_tiles/custom_links_manager_impl.h
new file mode 100644
index 00000000000..74d23d83ad4
--- /dev/null
+++ b/chromium/components/ntp_tiles/custom_links_manager_impl.h
@@ -0,0 +1,71 @@
+// 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_NTP_TILES_CUSTOM_LINKS_MANAGER_IMPL_H_
+#define COMPONENTS_NTP_TILES_CUSTOM_LINKS_MANAGER_IMPL_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "components/ntp_tiles/custom_links_manager.h"
+#include "components/ntp_tiles/custom_links_store.h"
+#include "components/ntp_tiles/ntp_tile.h"
+
+class PrefService;
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+} // namespace user_prefs
+
+namespace ntp_tiles {
+
+// Non-test implementation of the CustomLinksManager interface.
+class CustomLinksManagerImpl : public CustomLinksManager {
+ public:
+ // Restores the previous state of |current_links_| from prefs.
+ explicit CustomLinksManagerImpl(PrefService* prefs);
+
+ ~CustomLinksManagerImpl() override;
+
+ // CustomLinksManager implementation.
+ bool Initialize(const NTPTilesVector& tiles) override;
+ void Uninitialize() override;
+ bool IsInitialized() const override;
+
+ const std::vector<Link>& GetLinks() const override;
+
+ bool AddLink(const GURL& url, const base::string16& title) override;
+ bool UpdateLink(const GURL& url,
+ const GURL& new_url,
+ const base::string16& new_title) override;
+ bool DeleteLink(const GURL& url) override;
+ bool UndoAction() override;
+
+ // Register preferences used by this class.
+ static void RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* user_prefs);
+
+ private:
+ void ClearLinks();
+ // Returns an iterator into |custom_links_|.
+ std::vector<Link>::iterator FindLinkWithUrl(const GURL& url);
+
+ PrefService* const prefs_;
+ CustomLinksStore store_;
+ std::vector<Link> current_links_;
+ // The state of the current list of links before the last action was
+ // performed.
+ base::Optional<std::vector<Link>> previous_links_;
+
+ base::WeakPtrFactory<CustomLinksManagerImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CustomLinksManagerImpl);
+};
+
+} // namespace ntp_tiles
+
+#endif // COMPONENTS_NTP_TILES_CUSTOM_LINKS_MANAGER_IMPL_H_
diff --git a/chromium/components/ntp_tiles/custom_links_manager_impl_unittest.cc b/chromium/components/ntp_tiles/custom_links_manager_impl_unittest.cc
new file mode 100644
index 00000000000..7556244671a
--- /dev/null
+++ b/chromium/components/ntp_tiles/custom_links_manager_impl_unittest.cc
@@ -0,0 +1,400 @@
+// 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/ntp_tiles/custom_links_manager_impl.h"
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sync_preferences::TestingPrefServiceSyncable;
+
+namespace ntp_tiles {
+
+namespace {
+
+struct TestCaseItem {
+ const char* url;
+ const char* title;
+};
+
+const TestCaseItem kTestCase1[] = {{"http://foo1.com/", "Foo1"}};
+const TestCaseItem kTestCase2[] = {
+ {"http://foo1.com/", "Foo1"},
+ {"http://foo2.com/", "Foo2"},
+};
+const TestCaseItem kTestCaseMax[] = {
+ {"http://foo1.com/", "Foo1"}, {"http://foo2.com/", "Foo2"},
+ {"http://foo3.com/", "Foo3"}, {"http://foo4.com/", "Foo4"},
+ {"http://foo5.com/", "Foo5"}, {"http://foo6.com/", "Foo6"},
+ {"http://foo7.com/", "Foo7"}, {"http://foo8.com/", "Foo8"},
+ {"http://foo9.com/", "Foo9"}, {"http://foo10.com/", "Foo10"},
+};
+
+const char kTestTitle[] = "Test";
+const char kTestUrl[] = "http://test.com/";
+
+void AddTile(NTPTilesVector* tiles, const char* url, const char* title) {
+ NTPTile tile;
+ tile.url = GURL(url);
+ tile.title = base::UTF8ToUTF16(title);
+ tiles->push_back(std::move(tile));
+}
+
+NTPTilesVector FillTestTiles(base::span<const TestCaseItem> test_cases) {
+ NTPTilesVector tiles;
+ for (const auto& test_case : test_cases) {
+ AddTile(&tiles, test_case.url, test_case.title);
+ }
+ return tiles;
+}
+
+std::vector<CustomLinksManager::Link> FillTestLinks(
+ base::span<const TestCaseItem> test_cases) {
+ std::vector<CustomLinksManager::Link> links;
+ for (const auto& test_case : test_cases) {
+ links.emplace_back(CustomLinksManager::Link{
+ GURL(test_case.url), base::UTF8ToUTF16(test_case.title)});
+ }
+ return links;
+}
+
+} // namespace
+
+class CustomLinksManagerImplTest : public testing::Test {
+ public:
+ CustomLinksManagerImplTest() {
+ CustomLinksManagerImpl::RegisterProfilePrefs(prefs_.registry());
+ custom_links_ = std::make_unique<CustomLinksManagerImpl>(&prefs_);
+ }
+
+ protected:
+ sync_preferences::TestingPrefServiceSyncable prefs_;
+ std::unique_ptr<CustomLinksManagerImpl> custom_links_;
+
+ DISALLOW_COPY_AND_ASSIGN(CustomLinksManagerImplTest);
+};
+
+TEST_F(CustomLinksManagerImplTest, InitializeOnlyOnce) {
+ NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
+ NTPTilesVector new_tiles = FillTestTiles(kTestCase2);
+ std::vector<CustomLinksManager::Link> initial_links =
+ FillTestLinks(kTestCase1);
+ std::vector<CustomLinksManager::Link> empty_links;
+
+ ASSERT_FALSE(custom_links_->IsInitialized());
+ ASSERT_TRUE(custom_links_->GetLinks().empty());
+
+ // Initialize.
+ EXPECT_TRUE(custom_links_->Initialize(initial_tiles));
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Try to initialize again. This should fail and leave the links intact.
+ EXPECT_FALSE(custom_links_->Initialize(new_tiles));
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+}
+
+TEST_F(CustomLinksManagerImplTest, UninitializeDeletesOldLinks) {
+ NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
+ std::vector<CustomLinksManager::Link> initial_links =
+ FillTestLinks(kTestCase1);
+
+ // Initialize.
+ ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+ ASSERT_EQ(initial_links, custom_links_->GetLinks());
+
+ custom_links_->Uninitialize();
+ EXPECT_TRUE(custom_links_->GetLinks().empty());
+
+ // Initialize with no links.
+ EXPECT_TRUE(custom_links_->Initialize(NTPTilesVector()));
+ EXPECT_TRUE(custom_links_->GetLinks().empty());
+}
+
+TEST_F(CustomLinksManagerImplTest, ReInitializeWithNewLinks) {
+ NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
+ NTPTilesVector new_tiles = FillTestTiles(kTestCase2);
+ std::vector<CustomLinksManager::Link> initial_links =
+ FillTestLinks(kTestCase1);
+ std::vector<CustomLinksManager::Link> new_links = FillTestLinks(kTestCase2);
+
+ // Initialize.
+ ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+ ASSERT_EQ(initial_links, custom_links_->GetLinks());
+
+ custom_links_->Uninitialize();
+ ASSERT_TRUE(custom_links_->GetLinks().empty());
+
+ // Initialize with new links.
+ EXPECT_TRUE(custom_links_->Initialize(new_tiles));
+ EXPECT_EQ(new_links, custom_links_->GetLinks());
+}
+
+TEST_F(CustomLinksManagerImplTest, AddLink) {
+ NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
+ std::vector<CustomLinksManager::Link> initial_links =
+ FillTestLinks(kTestCase1);
+ std::vector<CustomLinksManager::Link> expected_links = initial_links;
+ expected_links.emplace_back(
+ CustomLinksManager::Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)});
+
+ // Initialize.
+ ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+ ASSERT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Add link.
+ EXPECT_TRUE(
+ custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
+ EXPECT_EQ(expected_links, custom_links_->GetLinks());
+}
+
+TEST_F(CustomLinksManagerImplTest, AddLinkWhenAtMaxLinks) {
+ NTPTilesVector initial_tiles = FillTestTiles(kTestCaseMax);
+ std::vector<CustomLinksManager::Link> initial_links =
+ FillTestLinks(kTestCaseMax);
+
+ // Initialize.
+ ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+ ASSERT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Try to add link. This should fail and not modify the list.
+ EXPECT_FALSE(
+ custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+}
+
+TEST_F(CustomLinksManagerImplTest, AddDuplicateLink) {
+ NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
+ std::vector<CustomLinksManager::Link> initial_links =
+ FillTestLinks(kTestCase1);
+
+ // Initialize.
+ ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+ ASSERT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Try to add duplicate link. This should fail and not modify the list.
+ EXPECT_FALSE(custom_links_->AddLink(GURL(kTestCase1[0].url),
+ base::UTF8ToUTF16(kTestCase1[0].title)));
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+}
+
+TEST_F(CustomLinksManagerImplTest, UpdateLink) {
+ NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
+ std::vector<CustomLinksManager::Link> initial_links =
+ FillTestLinks(kTestCase1);
+ std::vector<CustomLinksManager::Link> links_after_update_url(initial_links);
+ links_after_update_url[0].url = GURL(kTestUrl);
+ std::vector<CustomLinksManager::Link> links_after_update_title(
+ links_after_update_url);
+ links_after_update_title[0].title = base::UTF8ToUTF16(kTestTitle);
+
+ // Initialize.
+ ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+ ASSERT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Update the link's URL.
+ EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(kTestUrl),
+ base::string16()));
+ EXPECT_EQ(links_after_update_url, custom_links_->GetLinks());
+
+ // Update the link's title.
+ EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestUrl), GURL(),
+ base::UTF8ToUTF16(kTestTitle)));
+ EXPECT_EQ(links_after_update_title, custom_links_->GetLinks());
+
+ // Update the link's URL and title.
+ EXPECT_TRUE(
+ custom_links_->UpdateLink(GURL(kTestUrl), GURL(kTestCase1[0].url),
+ base::UTF8ToUTF16(kTestCase1[0].title)));
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+}
+
+TEST_F(CustomLinksManagerImplTest, UpdateLinkWithInvalidParams) {
+ const GURL kInvalidUrl = GURL("test");
+ NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
+ std::vector<CustomLinksManager::Link> initial_links =
+ FillTestLinks(kTestCase1);
+
+ // Initialize.
+ ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+ ASSERT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Try to update a link that does not exist. This should fail and not modify
+ // the list.
+ EXPECT_FALSE(custom_links_->UpdateLink(GURL(kTestUrl), GURL(),
+ base::UTF8ToUTF16(kTestTitle)));
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Try to pass empty params. This should fail and not modify the list.
+ EXPECT_FALSE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(),
+ base::string16()));
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Try to pass an invalid URL. This should fail and not modify the list.
+ EXPECT_FALSE(custom_links_->UpdateLink(kInvalidUrl, GURL(),
+ base::UTF8ToUTF16(kTestTitle)));
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+ EXPECT_FALSE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), kInvalidUrl,
+ base::string16()));
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+}
+
+TEST_F(CustomLinksManagerImplTest, UpdateLinkWhenUrlAlreadyExists) {
+ NTPTilesVector initial_tiles = FillTestTiles(kTestCase2);
+ std::vector<CustomLinksManager::Link> initial_links =
+ FillTestLinks(kTestCase2);
+
+ // Initialize.
+ ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+ ASSERT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Try to update a link with a URL that exists in the list. This should fail
+ // and not modify the list.
+ EXPECT_FALSE(custom_links_->UpdateLink(
+ GURL(kTestCase2[0].url), GURL(kTestCase2[1].url), base::string16()));
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+}
+
+TEST_F(CustomLinksManagerImplTest, DeleteLink) {
+ NTPTilesVector initial_tiles;
+ AddTile(&initial_tiles, kTestUrl, kTestTitle);
+ std::vector<CustomLinksManager::Link> initial_links({CustomLinksManager::Link{
+ GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)}});
+
+ // Initialize.
+ ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+ ASSERT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Delete link.
+ EXPECT_TRUE(custom_links_->DeleteLink(GURL(kTestUrl)));
+ EXPECT_TRUE(custom_links_->GetLinks().empty());
+}
+
+TEST_F(CustomLinksManagerImplTest, DeleteLinkWhenUrlDoesNotExist) {
+ NTPTilesVector initial_tiles;
+
+ // Initialize.
+ ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+ ASSERT_TRUE(custom_links_->GetLinks().empty());
+
+ // Try to delete link. This should fail and not modify the list.
+ EXPECT_FALSE(custom_links_->DeleteLink(GURL(kTestUrl)));
+ EXPECT_TRUE(custom_links_->GetLinks().empty());
+}
+
+TEST_F(CustomLinksManagerImplTest, UndoAddLink) {
+ NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
+ std::vector<CustomLinksManager::Link> initial_links =
+ FillTestLinks(kTestCase1);
+ std::vector<CustomLinksManager::Link> expected_links = initial_links;
+ expected_links.emplace_back(
+ CustomLinksManager::Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)});
+
+ // Initialize.
+ ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+ ASSERT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Try to undo before add is called. This should fail and not modify the list.
+ EXPECT_FALSE(custom_links_->UndoAction());
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Add link.
+ EXPECT_TRUE(
+ custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
+ EXPECT_EQ(expected_links, custom_links_->GetLinks());
+
+ // Undo add link.
+ EXPECT_TRUE(custom_links_->UndoAction());
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Try to undo again. This should fail and not modify the list.
+ EXPECT_FALSE(custom_links_->UndoAction());
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+}
+
+TEST_F(CustomLinksManagerImplTest, UndoUpdateLink) {
+ NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
+ std::vector<CustomLinksManager::Link> initial_links =
+ FillTestLinks(kTestCase1);
+ std::vector<CustomLinksManager::Link> links_after_update_url(initial_links);
+ links_after_update_url[0].url = GURL(kTestUrl);
+ std::vector<CustomLinksManager::Link> links_after_update_title(initial_links);
+ links_after_update_title[0].title = base::UTF8ToUTF16(kTestTitle);
+
+ // Initialize.
+ ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+ ASSERT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Update the link's URL.
+ EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(kTestUrl),
+ base::string16()));
+ EXPECT_EQ(links_after_update_url, custom_links_->GetLinks());
+
+ // Undo update link.
+ EXPECT_TRUE(custom_links_->UndoAction());
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Update the link's title.
+ EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(),
+ base::UTF8ToUTF16(kTestTitle)));
+ EXPECT_EQ(links_after_update_title, custom_links_->GetLinks());
+
+ // Undo update link.
+ EXPECT_TRUE(custom_links_->UndoAction());
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+
+ // Try to undo again. This should fail and not modify the list.
+ EXPECT_FALSE(custom_links_->UndoAction());
+ EXPECT_EQ(initial_links, custom_links_->GetLinks());
+}
+
+TEST_F(CustomLinksManagerImplTest, UndoDeleteLink) {
+ NTPTilesVector initial_tiles;
+ AddTile(&initial_tiles, kTestUrl, kTestTitle);
+ std::vector<CustomLinksManager::Link> expected_links(
+ {CustomLinksManager::Link{GURL(kTestUrl),
+ base::UTF8ToUTF16(kTestTitle)}});
+
+ // Initialize.
+ ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+ ASSERT_EQ(expected_links, custom_links_->GetLinks());
+
+ // Delete link.
+ ASSERT_TRUE(custom_links_->DeleteLink(GURL(kTestUrl)));
+ ASSERT_TRUE(custom_links_->GetLinks().empty());
+
+ // Undo delete link.
+ EXPECT_TRUE(custom_links_->UndoAction());
+ EXPECT_EQ(expected_links, custom_links_->GetLinks());
+}
+
+TEST_F(CustomLinksManagerImplTest, UndoDeleteLinkAfterAdd) {
+ NTPTilesVector initial_tiles;
+ std::vector<CustomLinksManager::Link> expected_links(
+ {CustomLinksManager::Link{GURL(kTestUrl),
+ base::UTF8ToUTF16(kTestTitle)}});
+
+ // Initialize.
+ ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+ ASSERT_TRUE(custom_links_->GetLinks().empty());
+
+ // Add link.
+ ASSERT_TRUE(
+ custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
+ ASSERT_EQ(expected_links, custom_links_->GetLinks());
+
+ // Delete link.
+ ASSERT_TRUE(custom_links_->DeleteLink(GURL(kTestUrl)));
+ ASSERT_TRUE(custom_links_->GetLinks().empty());
+
+ // Undo delete link.
+ EXPECT_TRUE(custom_links_->UndoAction());
+ EXPECT_EQ(expected_links, custom_links_->GetLinks());
+}
+
+} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/custom_links_store.cc b/chromium/components/ntp_tiles/custom_links_store.cc
new file mode 100644
index 00000000000..84211f418b7
--- /dev/null
+++ b/chromium/components/ntp_tiles/custom_links_store.cc
@@ -0,0 +1,77 @@
+// 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/ntp_tiles/custom_links_store.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/ntp_tiles/pref_names.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+#include "components/suggestions/suggestions_pref_names.h"
+
+namespace ntp_tiles {
+
+namespace {
+
+const char* kDictionaryKeyUrl = "url";
+const char* kDictionaryKeyTitle = "title";
+
+} // namespace
+
+CustomLinksStore::CustomLinksStore(PrefService* prefs) : prefs_(prefs) {
+ DCHECK(prefs);
+}
+
+CustomLinksStore::~CustomLinksStore() = default;
+
+std::vector<CustomLinksManager::Link> CustomLinksStore::RetrieveLinks() {
+ std::vector<CustomLinksManager::Link> links;
+
+ const base::ListValue* stored_links =
+ prefs_->GetList(prefs::kCustomLinksList);
+
+ for (const base::Value& link : stored_links->GetList()) {
+ const base::Value* url_value = link.FindKey(kDictionaryKeyUrl);
+ const base::Value* title_value = link.FindKey(kDictionaryKeyTitle);
+ GURL url = GURL(url_value->GetString());
+ if (!url_value || !title_value || !url.is_valid()) {
+ ClearLinks();
+ links.clear();
+ return links;
+ }
+ links.emplace_back(CustomLinksManager::Link{
+ std::move(url), base::UTF8ToUTF16(title_value->GetString())});
+ }
+ return links;
+}
+
+void CustomLinksStore::StoreLinks(
+ const std::vector<CustomLinksManager::Link>& links) {
+ base::Value::ListStorage new_link_list;
+ for (const CustomLinksManager::Link& link : links) {
+ base::DictionaryValue new_link;
+ new_link.SetKey(kDictionaryKeyUrl, base::Value(link.url.spec()));
+ new_link.SetKey(kDictionaryKeyTitle, base::Value(link.title));
+ new_link_list.push_back(std::move(new_link));
+ }
+ prefs_->Set(prefs::kCustomLinksList, base::Value(std::move(new_link_list)));
+}
+
+void CustomLinksStore::ClearLinks() {
+ prefs_->ClearPref(prefs::kCustomLinksList);
+}
+
+// static
+void CustomLinksStore::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* user_prefs) {
+ user_prefs->RegisterListPref(prefs::kCustomLinksList);
+}
+
+} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/custom_links_store.h b/chromium/components/ntp_tiles/custom_links_store.h
new file mode 100644
index 00000000000..eabb3af6092
--- /dev/null
+++ b/chromium/components/ntp_tiles/custom_links_store.h
@@ -0,0 +1,55 @@
+// 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_NTP_TILES_CUSTOM_LINKS_STORE_H_
+#define COMPONENTS_NTP_TILES_CUSTOM_LINKS_STORE_H_
+
+#include <vector>
+
+#include "components/ntp_tiles/custom_links_manager.h"
+
+class PrefService;
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+} // namespace user_prefs
+
+namespace ntp_tiles {
+
+// A helper class for reading and writing custom links to the profile's
+// preference file. All virtual functions are for testing.
+class CustomLinksStore {
+ public:
+ explicit CustomLinksStore(PrefService* prefs);
+ // Virtual for testing.
+ virtual ~CustomLinksStore();
+
+ // Retrieves the custom link data from the profile's preferences and returns
+ // them as a list of |Link|s. If there is a problem with retrieval, the pref
+ // value is cleared and an empty list is returned.
+ // Virtual for testing.
+ virtual std::vector<CustomLinksManager::Link> RetrieveLinks();
+
+ // Stores the provided |links| to the profile's preferences.
+ // Virtual for testing.
+ virtual void StoreLinks(const std::vector<CustomLinksManager::Link>& links);
+
+ // Clears any custom link data from the profile's preferences.
+ // Virtual for testing.
+ virtual void ClearLinks();
+
+ // Register CustomLinksStore related prefs in the Profile prefs.
+ static void RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* user_prefs);
+
+ private:
+ // The pref service used to persist the custom link data.
+ PrefService* prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(CustomLinksStore);
+};
+
+} // namespace ntp_tiles
+
+#endif // COMPONENTS_NTP_TILES_CUSTOM_LINKS_STORE_H_
diff --git a/chromium/components/ntp_tiles/custom_links_store_unittest.cc b/chromium/components/ntp_tiles/custom_links_store_unittest.cc
new file mode 100644
index 00000000000..df4f394312d
--- /dev/null
+++ b/chromium/components/ntp_tiles/custom_links_store_unittest.cc
@@ -0,0 +1,83 @@
+// 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/ntp_tiles/custom_links_store.h"
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sync_preferences::TestingPrefServiceSyncable;
+
+namespace ntp_tiles {
+
+namespace {
+
+const char kTestTitle1[] = "Foo1";
+const char kTestTitle2[] = "Foo2";
+const char kTestUrl1[] = "http://foo1.com/";
+const char kTestUrl2[] = "http://foo2.com/";
+
+} // namespace
+
+class CustomLinksStoreTest : public testing::Test {
+ public:
+ CustomLinksStoreTest() : custom_links_store_(&prefs_) {
+ CustomLinksStore::RegisterProfilePrefs(prefs_.registry());
+ }
+
+ protected:
+ sync_preferences::TestingPrefServiceSyncable prefs_;
+ CustomLinksStore custom_links_store_;
+
+ DISALLOW_COPY_AND_ASSIGN(CustomLinksStoreTest);
+};
+
+TEST_F(CustomLinksStoreTest, StoreAndRetrieveLinks) {
+ std::vector<CustomLinksManager::Link> initial_links({CustomLinksManager::Link{
+ GURL(kTestUrl1), base::UTF8ToUTF16(kTestTitle1)}});
+
+ custom_links_store_.StoreLinks(initial_links);
+ std::vector<CustomLinksManager::Link> retrieved_links =
+ custom_links_store_.RetrieveLinks();
+ EXPECT_EQ(initial_links, retrieved_links);
+}
+
+TEST_F(CustomLinksStoreTest, StoreEmptyList) {
+ std::vector<CustomLinksManager::Link> populated_links(
+ {CustomLinksManager::Link{GURL(kTestUrl1),
+ base::UTF8ToUTF16(kTestTitle1)},
+ CustomLinksManager::Link{GURL(kTestUrl2),
+ base::UTF8ToUTF16(kTestTitle2)}});
+
+ custom_links_store_.StoreLinks(populated_links);
+ std::vector<CustomLinksManager::Link> retrieved_links =
+ custom_links_store_.RetrieveLinks();
+ ASSERT_EQ(populated_links, retrieved_links);
+
+ custom_links_store_.StoreLinks(std::vector<CustomLinksManager::Link>());
+ retrieved_links = custom_links_store_.RetrieveLinks();
+ EXPECT_TRUE(retrieved_links.empty());
+}
+
+TEST_F(CustomLinksStoreTest, ClearLinks) {
+ std::vector<CustomLinksManager::Link> initial_links({CustomLinksManager::Link{
+ GURL(kTestUrl1), base::UTF8ToUTF16(kTestTitle1)}});
+
+ custom_links_store_.StoreLinks(initial_links);
+ std::vector<CustomLinksManager::Link> retrieved_links =
+ custom_links_store_.RetrieveLinks();
+ ASSERT_EQ(initial_links, retrieved_links);
+
+ custom_links_store_.ClearLinks();
+ retrieved_links = custom_links_store_.RetrieveLinks();
+ EXPECT_TRUE(retrieved_links.empty());
+}
+
+} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc b/chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc
index 3722b859a91..d67de38c731 100644
--- a/chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc
+++ b/chromium/components/ntp_tiles/icon_cacher_impl_unittest.cc
@@ -11,7 +11,7 @@
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/test_simple_task_runner.h"
diff --git a/chromium/components/ntp_tiles/metrics.cc b/chromium/components/ntp_tiles/metrics.cc
index bce9376fc84..f3df4427ada 100644
--- a/chromium/components/ntp_tiles/metrics.cc
+++ b/chromium/components/ntp_tiles/metrics.cc
@@ -28,6 +28,7 @@ const char kHistogramPopularName[] = "popular_fetched";
const char kHistogramBakedInName[] = "popular_baked_in";
const char kHistogramWhitelistName[] = "whitelist";
const char kHistogramHomepageName[] = "homepage";
+const char kHistogramCustomLinksName[] = "custom_links";
// Suffixes for the various icon types.
const char kTileTypeSuffixIconColor[] = "IconsColor";
@@ -56,6 +57,8 @@ std::string GetSourceHistogramName(TileSource source) {
return kHistogramServerName;
case TileSource::HOMEPAGE:
return kHistogramHomepageName;
+ case TileSource::CUSTOM_LINKS:
+ return kHistogramCustomLinksName;
}
NOTREACHED();
return std::string();
diff --git a/chromium/components/ntp_tiles/metrics_unittest.cc b/chromium/components/ntp_tiles/metrics_unittest.cc
index 910938c9688..10fcf1475b0 100644
--- a/chromium/components/ntp_tiles/metrics_unittest.cc
+++ b/chromium/components/ntp_tiles/metrics_unittest.cc
@@ -10,7 +10,7 @@
#include <vector>
#include "base/macros.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/rappor/test_rappor_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/components/ntp_tiles/most_visited_sites.cc b/chromium/components/ntp_tiles/most_visited_sites.cc
index ba46b4140ab..3fca3150fa9 100644
--- a/chromium/components/ntp_tiles/most_visited_sites.cc
+++ b/chromium/components/ntp_tiles/most_visited_sites.cc
@@ -31,6 +31,10 @@ namespace ntp_tiles {
namespace {
+// The maximum number of custom links that can be shown. This is independent of
+// the maximum number of Most Visited sites that can be shown.
+const size_t kMaxNumCustomLinks = 10;
+
const base::Feature kDisplaySuggestionsServiceTiles{
"DisplaySuggestionsServiceTiles", base::FEATURE_ENABLED_BY_DEFAULT};
@@ -64,9 +68,8 @@ bool AreURLsEquivalent(const GURL& url1, const GURL& url2) {
bool HasHomeTile(const NTPTilesVector& tiles) {
for (const auto& tile : tiles) {
- if (tile.source == TileSource::HOMEPAGE) {
+ if (tile.source == TileSource::HOMEPAGE)
return true;
- }
}
return false;
}
@@ -92,16 +95,18 @@ MostVisitedSites::MostVisitedSites(
scoped_refptr<history::TopSites> top_sites,
SuggestionsService* suggestions,
std::unique_ptr<PopularSites> popular_sites,
+ std::unique_ptr<CustomLinksManager> custom_links,
std::unique_ptr<IconCacher> icon_cacher,
std::unique_ptr<MostVisitedSitesSupervisor> supervisor)
: prefs_(prefs),
top_sites_(top_sites),
suggestions_service_(suggestions),
popular_sites_(std::move(popular_sites)),
+ custom_links_(std::move(custom_links)),
icon_cacher_(std::move(icon_cacher)),
supervisor_(std::move(supervisor)),
observer_(nullptr),
- num_sites_(0u),
+ max_num_sites_(0u),
top_sites_observer_(this),
mv_source_(TileSource::TOP_SITES),
top_sites_weak_ptr_factory_(this) {
@@ -141,30 +146,32 @@ bool MostVisitedSites::DoesSourceExist(TileSource source) const {
case TileSource::POPULAR_BAKED_IN:
case TileSource::POPULAR:
return popular_sites_ != nullptr;
+ case TileSource::HOMEPAGE:
+ return homepage_client_ != nullptr;
case TileSource::WHITELIST:
return supervisor_ != nullptr;
- case TileSource::HOMEPAGE:
- return home_page_client_ != nullptr;
+ case TileSource::CUSTOM_LINKS:
+ return custom_links_ != nullptr;
}
NOTREACHED();
return false;
}
-void MostVisitedSites::SetHomePageClient(
- std::unique_ptr<HomePageClient> client) {
+void MostVisitedSites::SetHomepageClient(
+ std::unique_ptr<HomepageClient> client) {
DCHECK(client);
- home_page_client_ = std::move(client);
+ homepage_client_ = std::move(client);
}
void MostVisitedSites::SetMostVisitedURLsObserver(Observer* observer,
size_t num_sites) {
DCHECK(observer);
observer_ = observer;
- num_sites_ = num_sites;
+ max_num_sites_ = num_sites;
// The order for this condition is important, ShouldShowPopularSites() should
// always be called last to keep metrics as relevant as possible.
- if (popular_sites_ && NeedPopularSites(prefs_, num_sites_) &&
+ if (popular_sites_ && NeedPopularSites(prefs_, max_num_sites_) &&
ShouldShowPopularSites()) {
popular_sites_->MaybeStartFetch(
false, base::Bind(&MostVisitedSites::OnPopularSitesDownloaded,
@@ -199,8 +206,73 @@ void MostVisitedSites::Refresh() {
suggestions_service_->FetchSuggestionsData();
}
-void MostVisitedSites::OnHomePageStateChanged() {
+void MostVisitedSites::RefreshHomepageTile() {
+ BuildCurrentTiles();
+}
+
+void MostVisitedSites::InitializeCustomLinks() {
+ if (!custom_links_ || !current_tiles_.has_value())
+ return;
+
+ if (custom_links_->Initialize(current_tiles_.value()))
+ BuildCurrentTiles();
+}
+
+void MostVisitedSites::UninitializeCustomLinks() {
+ if (!custom_links_)
+ return;
+
+ custom_links_->Uninitialize();
BuildCurrentTiles();
+ Refresh();
+}
+
+bool MostVisitedSites::IsCustomLinksInitialized() {
+ if (!custom_links_)
+ return false;
+
+ return custom_links_->IsInitialized();
+}
+
+bool MostVisitedSites::AddCustomLink(const GURL& url,
+ const base::string16& title) {
+ if (!custom_links_)
+ return false;
+
+ bool success = custom_links_->AddLink(url, title);
+ if (success)
+ BuildCurrentTiles();
+ return success;
+}
+
+bool MostVisitedSites::UpdateCustomLink(const GURL& url,
+ const GURL& new_url,
+ const base::string16& new_title) {
+ if (!custom_links_)
+ return false;
+
+ bool success = custom_links_->UpdateLink(url, new_url, new_title);
+ if (success)
+ BuildCurrentTiles();
+ return success;
+}
+
+bool MostVisitedSites::DeleteCustomLink(const GURL& url) {
+ if (!custom_links_)
+ return false;
+
+ bool success = custom_links_->DeleteLink(url);
+ if (success)
+ BuildCurrentTiles();
+ return success;
+}
+
+void MostVisitedSites::UndoCustomLinkAction() {
+ if (!custom_links_)
+ return;
+
+ if (custom_links_->UndoAction())
+ BuildCurrentTiles();
}
void MostVisitedSites::AddOrRemoveBlacklistedUrl(const GURL& url,
@@ -274,14 +346,15 @@ base::FilePath MostVisitedSites::GetWhitelistLargeIconPath(const GURL& url) {
void MostVisitedSites::OnMostVisitedURLsAvailable(
const history::MostVisitedURLList& visited_list) {
- // Ignore the event if tiles provided by the Suggestions Service, which take
- // precedence.
- if (mv_source_ == TileSource::SUGGESTIONS_SERVICE) {
+ // Ignore the event if tiles are provided by the Suggestions Service or custom
+ // links, which take precedence.
+ if ((custom_links_ && custom_links_->IsInitialized()) ||
+ mv_source_ == TileSource::SUGGESTIONS_SERVICE) {
return;
}
NTPTilesVector tiles;
- size_t num_tiles = std::min(visited_list.size(), num_sites_);
+ size_t num_tiles = std::min(visited_list.size(), max_num_sites_);
for (size_t i = 0; i < num_tiles; ++i) {
const history::MostVisitedURL& visited = visited_list[i];
if (visited.url.is_empty())
@@ -308,8 +381,11 @@ void MostVisitedSites::OnMostVisitedURLsAvailable(
void MostVisitedSites::OnSuggestionsProfileChanged(
const SuggestionsProfile& suggestions_profile) {
- if (suggestions_profile.suggestions_size() == 0 &&
- mv_source_ != TileSource::SUGGESTIONS_SERVICE) {
+ // Ignore the event if tiles are provided by custom links, which take
+ // precedence.
+ if ((custom_links_ && custom_links_->IsInitialized()) ||
+ (suggestions_profile.suggestions_size() == 0 &&
+ mv_source_ != TileSource::SUGGESTIONS_SERVICE)) {
return;
}
@@ -317,6 +393,11 @@ void MostVisitedSites::OnSuggestionsProfileChanged(
}
void MostVisitedSites::BuildCurrentTiles() {
+ if (custom_links_ && custom_links_->IsInitialized()) {
+ BuildCustomLinks(custom_links_->GetLinks());
+ return;
+ }
+
BuildCurrentTilesGivenSuggestionsProfile(
suggestions_service_->GetSuggestionsDataFromCache().value_or(
SuggestionsProfile()));
@@ -332,8 +413,8 @@ void MostVisitedSites::BuildCurrentTilesGivenSuggestionsProfile(
InitiateTopSitesQuery();
return;
}
- if (num_sites_ < num_tiles)
- num_tiles = num_sites_;
+ if (max_num_sites_ < num_tiles)
+ num_tiles = max_num_sites_;
const base::Time profile_timestamp =
base::Time::UnixEpoch() +
@@ -358,8 +439,8 @@ void MostVisitedSites::BuildCurrentTilesGivenSuggestionsProfile(
tile.data_generation_time = profile_timestamp;
icon_cacher_->StartFetchMostLikely(
- url, base::Bind(&MostVisitedSites::OnIconMadeAvailable,
- base::Unretained(this), url));
+ url, base::BindRepeating(&MostVisitedSites::OnIconMadeAvailable,
+ base::Unretained(this), url));
tiles.push_back(std::move(tile));
}
@@ -377,7 +458,7 @@ NTPTilesVector MostVisitedSites::CreateWhitelistEntryPointTiles(
NTPTilesVector whitelist_tiles;
for (const auto& whitelist : supervisor_->GetWhitelists()) {
- if (whitelist_tiles.size() + num_actual_tiles >= num_sites_)
+ if (whitelist_tiles.size() + num_actual_tiles >= max_num_sites_)
break;
// Skip blacklisted sites.
@@ -425,15 +506,16 @@ MostVisitedSites::CreatePopularSitesSections(
SectionType type = section_type_and_sites.first;
const PopularSites::SitesVector& sites = section_type_and_sites.second;
if (type == SectionType::PERSONALIZED) {
- size_t num_required_tiles = num_sites_ - num_actual_tiles;
+ size_t num_required_tiles = max_num_sites_ - num_actual_tiles;
sections[type] =
CreatePopularSitesTiles(/*popular_sites=*/sites,
/*hosts_to_skip=*/used_hosts,
/*num_max_tiles=*/num_required_tiles);
} else {
- sections[type] = CreatePopularSitesTiles(/*popular_sites=*/sites,
- /*hosts_to_skip=*/no_hosts,
- /*num_max_tiles=*/num_sites_);
+ sections[type] =
+ CreatePopularSitesTiles(/*popular_sites=*/sites,
+ /*hosts_to_skip=*/no_hosts,
+ /*num_max_tiles=*/max_num_sites_);
}
}
return sections;
@@ -476,71 +558,103 @@ NTPTilesVector MostVisitedSites::CreatePopularSitesTiles(
return popular_sites_tiles;
}
-void MostVisitedSites::OnHomePageTitleDetermined(
+void MostVisitedSites::OnHomepageTitleDetermined(
NTPTilesVector tiles,
const base::Optional<base::string16>& title) {
- if (!title.has_value()) {
+ if (!title.has_value())
return; // If there is no title, the most recent tile was already sent out.
- }
- SaveTilesAndNotify(InsertHomeTile(std::move(tiles), title.value()));
+
+ MergeMostVisitedTiles(InsertHomeTile(std::move(tiles), title.value()));
}
NTPTilesVector MostVisitedSites::InsertHomeTile(
NTPTilesVector tiles,
const base::string16& title) const {
- DCHECK(home_page_client_);
- DCHECK_GT(num_sites_, 0u);
+ DCHECK(homepage_client_);
+ DCHECK_GT(max_num_sites_, 0u);
- const GURL& home_page_url = home_page_client_->GetHomePageUrl();
+ const GURL& homepage_url = homepage_client_->GetHomepageUrl();
NTPTilesVector new_tiles;
- bool home_tile_added = false;
+ bool homepage_tile_added = false;
for (auto& tile : tiles) {
- if (new_tiles.size() >= num_sites_) {
+ if (new_tiles.size() >= max_num_sites_) {
break;
}
- // TODO(fhorschig): Introduce a more sophisticated deduplication.
- if (tile.url.host() == home_page_url.host()) {
+ // If there's a tile has the same host name with homepage, insert the tile
+ // to the first position of the list. This is also a deduplication.
+ if (tile.url.host() == homepage_url.host() && !homepage_tile_added) {
tile.source = TileSource::HOMEPAGE;
- home_tile_added = true;
+ homepage_tile_added = true;
+ new_tiles.insert(new_tiles.begin(), std::move(tile));
+ continue;
}
new_tiles.push_back(std::move(tile));
}
- // Add the home page tile if there are less than 4 tiles
- // and none of them is the home page (and there is space left).
- if (!home_tile_added) {
- // Make room for the home page tile.
- if (new_tiles.size() >= num_sites_) {
+ if (!homepage_tile_added) {
+ // Make room for the homepage tile.
+ if (new_tiles.size() >= max_num_sites_) {
new_tiles.pop_back();
}
+ NTPTile homepage_tile;
+ homepage_tile.url = homepage_url;
+ homepage_tile.title = title;
+ homepage_tile.source = TileSource::HOMEPAGE;
+ homepage_tile.title_source = TileTitleSource::TITLE_TAG;
- NTPTile home_tile;
- home_tile.url = home_page_url;
- home_tile.title = title;
- home_tile.source = TileSource::HOMEPAGE;
- home_tile.title_source = TileTitleSource::TITLE_TAG; // From history.
-
- new_tiles.push_back(std::move(home_tile));
+ // Always insert |homepage_tile| to the front of |new_tiles| to ensure it's
+ // the first tile.
+ new_tiles.insert(new_tiles.begin(), std::move(homepage_tile));
}
return new_tiles;
}
+void MostVisitedSites::BuildCustomLinks(
+ const std::vector<CustomLinksManager::Link>& links) {
+ DCHECK(IsCustomLinksEnabled());
+
+ NTPTilesVector tiles;
+ size_t num_tiles = std::min(links.size(), kMaxNumCustomLinks);
+ for (size_t i = 0; i < num_tiles; ++i) {
+ const CustomLinksManager::Link& link = links.at(i);
+ if (supervisor_ && supervisor_->IsBlocked(link.url))
+ continue;
+
+ NTPTile tile;
+ tile.title = link.title;
+ tile.url = link.url;
+ tile.source = TileSource::CUSTOM_LINKS;
+ // TODO(crbug.com/773278): Populate |data_generation_time| here in order to
+ // log UMA metrics of age.
+ tiles.push_back(std::move(tile));
+ }
+
+ mv_source_ = TileSource::CUSTOM_LINKS;
+ SaveTilesAndNotify(std::move(tiles), std::map<SectionType, NTPTilesVector>());
+}
+
void MostVisitedSites::InitiateNotificationForNewTiles(
NTPTilesVector new_tiles) {
if (ShouldAddHomeTile() && !HasHomeTile(new_tiles)) {
- home_page_client_->QueryHomePageTitle(
- base::BindOnce(&MostVisitedSites::OnHomePageTitleDetermined,
+ homepage_client_->QueryHomepageTitle(
+ base::BindOnce(&MostVisitedSites::OnHomepageTitleDetermined,
base::Unretained(this), new_tiles));
+ GURL homepage_url = homepage_client_->GetHomepageUrl();
+ icon_cacher_->StartFetchMostLikely(
+ homepage_url,
+ base::BindRepeating(&MostVisitedSites::OnIconMadeAvailable,
+ base::Unretained(this), homepage_url));
+
// Don't wait for the homepage title from history but immediately serve a
// copy of new tiles.
new_tiles = InsertHomeTile(std::move(new_tiles), base::string16());
}
- SaveTilesAndNotify(std::move(new_tiles));
+ MergeMostVisitedTiles(std::move(new_tiles));
}
-void MostVisitedSites::SaveTilesAndNotify(NTPTilesVector personal_tiles) {
+void MostVisitedSites::MergeMostVisitedTiles(NTPTilesVector personal_tiles) {
std::set<std::string> used_hosts;
size_t num_actual_tiles = 0u;
AddToHostsAndTotalCount(personal_tiles, &used_hosts, &num_actual_tiles);
@@ -557,12 +671,16 @@ void MostVisitedSites::SaveTilesAndNotify(NTPTilesVector personal_tiles) {
NTPTilesVector new_tiles =
MergeTiles(std::move(personal_tiles), std::move(whitelist_tiles),
std::move(sections[SectionType::PERSONALIZED]));
- if (current_tiles_.has_value() && (*current_tiles_ == new_tiles)) {
- return;
- }
+ SaveTilesAndNotify(std::move(new_tiles), std::move(sections));
+}
+
+void MostVisitedSites::SaveTilesAndNotify(
+ NTPTilesVector new_tiles,
+ std::map<SectionType, NTPTilesVector> sections) {
+ if (current_tiles_.has_value() && (*current_tiles_ == new_tiles))
+ return;
current_tiles_.emplace(std::move(new_tiles));
- DCHECK_EQ(num_actual_tiles, current_tiles_->size());
int num_personal_tiles = 0;
for (const auto& tile : *current_tiles_) {
@@ -622,13 +740,12 @@ void MostVisitedSites::TopSitesChanged(TopSites* top_sites,
}
bool MostVisitedSites::ShouldAddHomeTile() const {
- return num_sites_ > 0u &&
- home_page_client_ && // No platform-specific implementation - no tile.
- home_page_client_->IsHomePageEnabled() &&
- !home_page_client_->IsNewTabPageUsedAsHomePage() &&
- !home_page_client_->GetHomePageUrl().is_empty() &&
+ return max_num_sites_ > 0u &&
+ homepage_client_ && // No platform-specific implementation - no tile.
+ homepage_client_->IsHomepageTileEnabled() &&
+ !homepage_client_->GetHomepageUrl().is_empty() &&
!(top_sites_ &&
- top_sites_->IsBlacklisted(home_page_client_->GetHomePageUrl()));
+ top_sites_->IsBlacklisted(homepage_client_->GetHomepageUrl()));
}
void MostVisitedSites::AddToHostsAndTotalCount(const NTPTilesVector& new_tiles,
@@ -638,7 +755,7 @@ void MostVisitedSites::AddToHostsAndTotalCount(const NTPTilesVector& new_tiles,
hosts->insert(tile.url.host());
}
*total_tile_count += new_tiles.size();
- DCHECK_LE(*total_tile_count, num_sites_);
+ DCHECK_LE(*total_tile_count, max_num_sites_);
}
} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/most_visited_sites.h b/chromium/components/ntp_tiles/most_visited_sites.h
index 2331c58f3ca..6924002c464 100644
--- a/chromium/components/ntp_tiles/most_visited_sites.h
+++ b/chromium/components/ntp_tiles/most_visited_sites.h
@@ -24,6 +24,7 @@
#include "base/strings/string16.h"
#include "components/history/core/browser/history_types.h"
#include "components/history/core/browser/top_sites_observer.h"
+#include "components/ntp_tiles/custom_links_manager.h"
#include "components/ntp_tiles/ntp_tile.h"
#include "components/ntp_tiles/popular_sites.h"
#include "components/ntp_tiles/section_type.h"
@@ -97,29 +98,30 @@ class MostVisitedSites : public history::TopSitesObserver,
virtual ~Observer() {}
};
- // This interface delegates the retrieval of the home page to the
+ // This interface delegates the retrieval of the homepage to the
// platform-specific implementation.
- class HomePageClient {
+ class HomepageClient {
public:
using TitleCallback =
base::OnceCallback<void(const base::Optional<base::string16>& title)>;
- virtual ~HomePageClient() = default;
- virtual bool IsHomePageEnabled() const = 0;
- virtual bool IsNewTabPageUsedAsHomePage() const = 0;
- virtual GURL GetHomePageUrl() const = 0;
- virtual void QueryHomePageTitle(TitleCallback title_callback) = 0;
+ virtual ~HomepageClient() = default;
+ virtual bool IsHomepageTileEnabled() const = 0;
+ virtual GURL GetHomepageUrl() const = 0;
+ // TODO(https://crbug.com/862753): Extract this to another interface.
+ virtual void QueryHomepageTitle(TitleCallback title_callback) = 0;
};
// Construct a MostVisitedSites instance.
//
// |prefs| and |suggestions| are required and may not be null. |top_sites|,
- // |popular_sites|, |supervisor| and |home_page_client| are optional and if
- // null, the associated features will be disabled.
+ // |popular_sites|, |custom_links|, |supervisor| and |homepage_client| are
+ // optional and if null, the associated features will be disabled.
MostVisitedSites(PrefService* prefs,
scoped_refptr<history::TopSites> top_sites,
suggestions::SuggestionsService* suggestions,
std::unique_ptr<PopularSites> popular_sites,
+ std::unique_ptr<CustomLinksManager> custom_links,
std::unique_ptr<IconCacher> icon_cacher,
std::unique_ptr<MostVisitedSitesSupervisor> supervisor);
@@ -143,17 +145,46 @@ class MostVisitedSites : public history::TopSitesObserver,
// must not be null.
void SetMostVisitedURLsObserver(Observer* observer, size_t num_sites);
- // Sets the client that provides platform-specific home page preferences.
- // When used to replace an existing client, the new client will first be used
- // during the construction of a new tile set.
- void SetHomePageClient(std::unique_ptr<HomePageClient> client);
+ // Sets the client that provides platform-specific homepage preferences.
+ // When used to replace an existing client, the new client will first be
+ // used during the construction of a new tile set.
+ // |client| must not be null and outlive this object.
+ void SetHomepageClient(std::unique_ptr<HomepageClient> client);
// Requests an asynchronous refresh of the suggestions. Notifies the observer
// if the request resulted in the set of tiles changing.
void Refresh();
- // Forces a rebuild of the current tiles to update the pinned home page.
- void OnHomePageStateChanged();
+ // Forces a rebuild of the current tiles to update the pinned homepage.
+ void RefreshHomepageTile();
+
+ // Initializes custom links, which "freezes" the current MV tiles and converts
+ // them to custom links. Once custom links is initialized, MostVisitedSites
+ // will return only custom links. If the Most Visited tiles have not been
+ // loaded yet, does nothing.
+ void InitializeCustomLinks();
+ // Uninitializes custom links and reverts back to regular MV tiles. The
+ // current custom links will be deleted.
+ void UninitializeCustomLinks();
+ // Returns true if custom links has been initialized, false otherwise.
+ bool IsCustomLinksInitialized();
+ // Adds a custom link. If the number of current links is maxed, returns false
+ // and does nothing. Custom links must be enabled.
+ bool AddCustomLink(const GURL& url, const base::string16& title);
+ // Updates the URL and/or title of the custom link specified by |url|. If
+ // |url| does not exist or |new_url| already exists in the custom link list,
+ // returns false and does nothing. Custom links must be enabled.
+ bool UpdateCustomLink(const GURL& url,
+ const GURL& new_url,
+ const base::string16& new_title);
+ // Deletes the custom link with the specified |url|. If |url| does not exist
+ // in the custom link list, returns false and does nothing. Custom links must
+ // be enabled.
+ bool DeleteCustomLink(const GURL& url);
+ // Restores the previous state of custom links before the last action that
+ // modified them. If there was no action, does nothing. Custom links must be
+ // enabled.
+ void UndoCustomLinkAction();
void AddOrRemoveBlacklistedUrl(const GURL& url, bool add_url);
void ClearBlacklistedUrls();
@@ -227,14 +258,22 @@ class MostVisitedSites : public history::TopSitesObserver,
const std::set<std::string>& hosts_to_skip,
size_t num_max_tiles);
- // Initiates a query for the home page tile if needed and calls
+ // Creates tiles for |links| up to |max_num_sites_|. |links| will never exceed
+ // a certain maximum.
+ void BuildCustomLinks(const std::vector<CustomLinksManager::Link>& links);
+
+ // Initiates a query for the homepage tile if needed and calls
// |SaveTilesAndNotify| in the end.
void InitiateNotificationForNewTiles(NTPTilesVector new_tiles);
// Takes the personal tiles, creates and merges in whitelist and popular tiles
- // if appropriate, and saves the new tiles. Notifies the observer if the tiles
- // were actually changed.
- void SaveTilesAndNotify(NTPTilesVector personal_tiles);
+ // if appropriate. Calls |SaveTilesAndNotify| at the end.
+ void MergeMostVisitedTiles(NTPTilesVector personal_tiles);
+
+ // Saves the new tiles and notifies the observer if the tiles were actually
+ // changed.
+ void SaveTilesAndNotify(NTPTilesVector new_tiles,
+ std::map<SectionType, NTPTilesVector> sections);
void OnPopularSitesDownloaded(bool success);
@@ -246,16 +285,16 @@ class MostVisitedSites : public history::TopSitesObserver,
std::set<std::string>* hosts,
size_t* total_tile_count) const;
- // Adds the home page as first tile to |tiles| and returns them as new vector.
+ // Adds the homepage as first tile to |tiles| and returns them as new vector.
// Drops existing tiles with the same host as the home page and tiles that
// would exceed the maximum.
NTPTilesVector InsertHomeTile(NTPTilesVector tiles,
const base::string16& title) const;
- void OnHomePageTitleDetermined(NTPTilesVector tiles,
+ void OnHomepageTitleDetermined(NTPTilesVector tiles,
const base::Optional<base::string16>& title);
- // Returns true if there is a valid home page that can be pinned as tile.
+ // Returns true if there is a valid homepage that can be pinned as tile.
bool ShouldAddHomeTile() const;
// history::TopSitesObserver implementation.
@@ -267,14 +306,15 @@ class MostVisitedSites : public history::TopSitesObserver,
scoped_refptr<history::TopSites> top_sites_;
suggestions::SuggestionsService* suggestions_service_;
std::unique_ptr<PopularSites> const popular_sites_;
+ std::unique_ptr<CustomLinksManager> const custom_links_;
std::unique_ptr<IconCacher> const icon_cacher_;
std::unique_ptr<MostVisitedSitesSupervisor> supervisor_;
- std::unique_ptr<HomePageClient> home_page_client_;
+ std::unique_ptr<HomepageClient> homepage_client_;
Observer* observer_;
// The maximum number of most visited sites to return.
- size_t num_sites_;
+ size_t max_num_sites_;
std::unique_ptr<
suggestions::SuggestionsService::ResponseCallbackList::Subscription>
diff --git a/chromium/components/ntp_tiles/most_visited_sites_unittest.cc b/chromium/components/ntp_tiles/most_visited_sites_unittest.cc
index dcc584a62af..6b4dc80145a 100644
--- a/chromium/components/ntp_tiles/most_visited_sites_unittest.cc
+++ b/chromium/components/ntp_tiles/most_visited_sites_unittest.cc
@@ -23,9 +23,11 @@
#include "base/task/cancelable_task_tracker.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
#include "components/history/core/browser/top_sites.h"
#include "components/history/core/browser/top_sites_observer.h"
#include "components/ntp_tiles/constants.h"
+#include "components/ntp_tiles/custom_links_manager.h"
#include "components/ntp_tiles/icon_cacher.h"
#include "components/ntp_tiles/json_unsafe_parser.h"
#include "components/ntp_tiles/popular_sites_impl.h"
@@ -33,8 +35,9 @@
#include "components/ntp_tiles/section_type.h"
#include "components/ntp_tiles/switches.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.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_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -76,8 +79,8 @@ using testing::SizeIs;
using testing::StrictMock;
using testing::_;
-const char kHomePageUrl[] = "http://ho.me/";
-const char kHomePageTitle[] = "Home";
+const char kHomepageUrl[] = "http://ho.me/";
+const char kHomepageTitle[] = "Home";
std::string PrintTile(const std::string& title,
const std::string& url,
@@ -222,43 +225,34 @@ class MockMostVisitedSitesObserver : public MostVisitedSites::Observer {
MOCK_METHOD1(OnIconMadeAvailable, void(const GURL& site_url));
};
-class FakeHomePageClient : public MostVisitedSites::HomePageClient {
+class FakeHomepageClient : public MostVisitedSites::HomepageClient {
public:
- FakeHomePageClient()
- : home_page_enabled_(false),
- ntp_is_homepage_(false),
- home_page_url_(kHomePageUrl) {}
- ~FakeHomePageClient() override {}
+ FakeHomepageClient()
+ : homepage_tile_enabled_(false), homepage_url_(kHomepageUrl) {}
+ ~FakeHomepageClient() override {}
- bool IsHomePageEnabled() const override { return home_page_enabled_; }
+ bool IsHomepageTileEnabled() const override { return homepage_tile_enabled_; }
- bool IsNewTabPageUsedAsHomePage() const override { return ntp_is_homepage_; }
+ GURL GetHomepageUrl() const override { return homepage_url_; }
- GURL GetHomePageUrl() const override { return home_page_url_; }
-
- void QueryHomePageTitle(TitleCallback title_callback) override {
- std::move(title_callback).Run(home_page_title_);
- }
-
- void SetHomePageEnabled(bool home_page_enabled) {
- home_page_enabled_ = home_page_enabled;
+ void QueryHomepageTitle(TitleCallback title_callback) override {
+ std::move(title_callback).Run(homepage_title_);
}
- void SetNtpIsHomePage(bool ntp_is_homepage) {
- ntp_is_homepage_ = ntp_is_homepage;
+ void SetHomepageTileEnabled(bool homepage_tile_enabled) {
+ homepage_tile_enabled_ = homepage_tile_enabled;
}
- void SetHomePageUrl(GURL home_page_url) { home_page_url_ = home_page_url; }
+ void SetHomepageUrl(GURL homepage_url) { homepage_url_ = homepage_url; }
- void SetHomePageTitle(const base::Optional<base::string16>& home_page_title) {
- home_page_title_ = home_page_title;
+ void SetHomepageTitle(const base::Optional<base::string16>& homepage_title) {
+ homepage_title_ = homepage_title;
}
private:
- bool home_page_enabled_;
- bool ntp_is_homepage_;
- GURL home_page_url_;
- base::Optional<base::string16> home_page_title_;
+ bool homepage_tile_enabled_;
+ GURL homepage_url_;
+ base::Optional<base::string16> homepage_title_;
};
class MockIconCacher : public IconCacher {
@@ -271,14 +265,29 @@ class MockIconCacher : public IconCacher {
void(const GURL& page_url, const base::Closure& icon_available));
};
+class MockCustomLinksManager : public CustomLinksManager {
+ public:
+ MOCK_METHOD1(Initialize, bool(const NTPTilesVector& tiles));
+ MOCK_METHOD0(Uninitialize, void());
+ MOCK_CONST_METHOD0(IsInitialized, bool());
+ MOCK_CONST_METHOD0(GetLinks, const std::vector<CustomLinksManager::Link>&());
+ MOCK_METHOD2(AddLink, bool(const GURL& url, const base::string16& title));
+ MOCK_METHOD3(UpdateLink,
+ bool(const GURL& url,
+ const GURL& new_url,
+ const base::string16& new_title));
+ MOCK_METHOD1(DeleteLink, bool(const GURL& url));
+ MOCK_METHOD0(UndoAction, bool());
+};
+
class PopularSitesFactoryForTest {
public:
PopularSitesFactoryForTest(
sync_preferences::TestingPrefServiceSyncable* pref_service)
- : prefs_(pref_service),
- url_fetcher_factory_(/*default_factory=*/nullptr),
- url_request_context_(new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get())) {
+ : prefs_(pref_service) {
+ test_shared_loader_factory_ =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_);
PopularSitesImpl::RegisterProfilePrefs(pref_service->registry());
}
@@ -286,9 +295,9 @@ class PopularSitesFactoryForTest {
prefs_->SetString(prefs::kPopularSitesOverrideCountry, "IN");
prefs_->SetString(prefs::kPopularSitesOverrideVersion, "5");
- url_fetcher_factory_.ClearFakeResponses();
- url_fetcher_factory_.SetFakeResponse(
- GURL("https://www.gstatic.com/chrome/ntp/suggested_sites_IN_5.json"),
+ test_url_loader_factory_.ClearResponses();
+ test_url_loader_factory_.AddResponse(
+ "https://www.gstatic.com/chrome/ntp/suggested_sites_IN_5.json",
R"([{
"title": "PopularSite1",
"url": "http://popularsite1/",
@@ -299,11 +308,10 @@ class PopularSitesFactoryForTest {
"url": "http://popularsite2/",
"favicon_url": "http://popularsite2/favicon.ico"
},
- ])",
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ ])");
- url_fetcher_factory_.SetFakeResponse(
- GURL("https://www.gstatic.com/chrome/ntp/suggested_sites_US_5.json"),
+ test_url_loader_factory_.AddResponse(
+ "https://www.gstatic.com/chrome/ntp/suggested_sites_US_5.json",
R"([{
"title": "ESPN",
"url": "http://www.espn.com",
@@ -317,11 +325,10 @@ class PopularSitesFactoryForTest {
"url": "http://news.google.com",
"favicon_url": "http://news.google.com/favicon.ico"
},
- ])",
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ ])");
- url_fetcher_factory_.SetFakeResponse(
- GURL("https://www.gstatic.com/chrome/ntp/suggested_sites_IN_6.json"),
+ test_url_loader_factory_.AddResponse(
+ "https://www.gstatic.com/chrome/ntp/suggested_sites_IN_6.json",
R"([{
"section": 1, // PERSONALIZED
"sites": [{
@@ -363,22 +370,21 @@ class PopularSitesFactoryForTest {
// Intentionally empty site list.
]
}
- ])",
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ ])");
}
std::unique_ptr<PopularSites> New() {
return std::make_unique<PopularSitesImpl>(
prefs_,
/*template_url_service=*/nullptr,
- /*variations_service=*/nullptr, url_request_context_.get(),
+ /*variations_service=*/nullptr, test_shared_loader_factory_,
base::Bind(JsonUnsafeParser::Parse));
}
private:
PrefService* prefs_;
- net::FakeURLFetcherFactory url_fetcher_factory_;
- scoped_refptr<net::TestURLRequestContextGetter> url_request_context_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
};
// CallbackList-like container without Subscription, mimicking the
@@ -411,7 +417,8 @@ class TopSitesCallbackList {
class MostVisitedSitesTest : public ::testing::TestWithParam<bool> {
protected:
MostVisitedSitesTest()
- : popular_sites_factory_(&pref_service_),
+ : is_custom_links_enabled_(false),
+ popular_sites_factory_(&pref_service_),
mock_top_sites_(new StrictMock<MockTopSites>()) {
MostVisitedSites::RegisterProfilePrefs(pref_service_.registry());
@@ -437,6 +444,15 @@ class MostVisitedSitesTest : public ::testing::TestWithParam<bool> {
auto icon_cacher = std::make_unique<StrictMock<MockIconCacher>>();
icon_cacher_ = icon_cacher.get();
+ // Custom links needs to be nullptr when MostVisitedSites is created, unless
+ // the custom links feature (kNtpCustomLinks) is enabled.
+ std::unique_ptr<StrictMock<MockCustomLinksManager>> mock_custom_links;
+ if (is_custom_links_enabled_) {
+ mock_custom_links =
+ std::make_unique<StrictMock<MockCustomLinksManager>>();
+ mock_custom_links_ = mock_custom_links.get();
+ }
+
if (IsPopularSitesFeatureEnabled()) {
// Populate Popular Sites' internal cache by mimicking a past usage of
// PopularSitesImpl.
@@ -467,7 +483,8 @@ class MostVisitedSitesTest : public ::testing::TestWithParam<bool> {
most_visited_sites_ = std::make_unique<MostVisitedSites>(
&pref_service_, mock_top_sites_, &mock_suggestions_service_,
- popular_sites_factory_.New(), std::move(icon_cacher),
+ popular_sites_factory_.New(), std::move(mock_custom_links),
+ std::move(icon_cacher),
/*supervisor=*/nullptr);
}
@@ -487,10 +504,10 @@ class MostVisitedSitesTest : public ::testing::TestWithParam<bool> {
return success;
}
- FakeHomePageClient* RegisterNewHomePageClient() {
- auto home_page_client = std::make_unique<FakeHomePageClient>();
- FakeHomePageClient* raw_client_ptr = home_page_client.get();
- most_visited_sites_->SetHomePageClient(std::move(home_page_client));
+ FakeHomepageClient* RegisterNewHomepageClient() {
+ auto homepage_client = std::make_unique<FakeHomepageClient>();
+ FakeHomepageClient* raw_client_ptr = homepage_client.get();
+ most_visited_sites_->SetHomepageClient(std::move(homepage_client));
return raw_client_ptr;
}
@@ -507,6 +524,9 @@ class MostVisitedSitesTest : public ::testing::TestWithParam<bool> {
.WillRepeatedly(Return(true));
}
+ void EnableCustomLinks() { is_custom_links_enabled_ = true; }
+
+ bool is_custom_links_enabled_;
base::CallbackList<SuggestionsService::ResponseCallback::RunType>
suggestions_service_callbacks_;
TopSitesCallbackList top_sites_callbacks_;
@@ -519,6 +539,7 @@ class MostVisitedSitesTest : public ::testing::TestWithParam<bool> {
StrictMock<MockMostVisitedSitesObserver> mock_observer_;
std::unique_ptr<MostVisitedSites> most_visited_sites_;
base::test::ScopedFeatureList feature_list_;
+ MockCustomLinksManager* mock_custom_links_;
MockIconCacher* icon_cacher_;
};
@@ -534,24 +555,24 @@ TEST_P(MostVisitedSitesTest, ShouldRefreshBothBackends) {
most_visited_sites_->Refresh();
}
-TEST_P(MostVisitedSitesTest, ShouldIncludeTileForHomePage) {
- FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
- home_page_client->SetHomePageEnabled(true);
+TEST_P(MostVisitedSitesTest, ShouldIncludeTileForHomepage) {
+ FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
+ homepage_client->SetHomepageTileEnabled(true);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{}));
EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomepageUrl))))
.Times(AnyNumber())
.WillRepeatedly(Return(false));
EXPECT_CALL(mock_observer_, OnURLsAvailable(FirstPersonalizedTileIs(
- "", kHomePageUrl, TileSource::HOMEPAGE)));
+ "", kHomepageUrl, TileSource::HOMEPAGE)));
most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
/*num_sites=*/3);
base::RunLoop().RunUntilIdle();
}
-TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageWithoutClient) {
+TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomepageWithoutClient) {
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{}));
@@ -559,7 +580,7 @@ TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageWithoutClient) {
EXPECT_CALL(mock_observer_,
OnURLsAvailable(Contains(
Pair(SectionType::PERSONALIZED,
- Not(Contains(MatchesTile("", kHomePageUrl,
+ Not(Contains(MatchesTile("", kHomepageUrl,
TileSource::HOMEPAGE)))))));
most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
/*num_sites=*/3);
@@ -570,14 +591,14 @@ TEST_P(MostVisitedSitesTest, ShouldIncludeHomeTileWithUrlBeforeQueryingName) {
// Because the query time for the real name might take a while, provide the
// home tile with URL as title immediately and update the tiles as soon as the
// real title was found.
- FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
- home_page_client->SetHomePageEnabled(true);
- home_page_client->SetHomePageTitle(base::UTF8ToUTF16(kHomePageTitle));
+ FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
+ homepage_client->SetHomepageTileEnabled(true);
+ homepage_client->SetHomepageTitle(base::UTF8ToUTF16(kHomepageTitle));
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{}));
EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomepageUrl))))
.Times(AnyNumber())
.WillRepeatedly(Return(false));
{
@@ -585,12 +606,12 @@ TEST_P(MostVisitedSitesTest, ShouldIncludeHomeTileWithUrlBeforeQueryingName) {
EXPECT_CALL(mock_observer_,
OnURLsAvailable(Contains(
Pair(SectionType::PERSONALIZED,
- Not(Contains(MatchesTile("", kHomePageUrl,
+ Not(Contains(MatchesTile("", kHomepageUrl,
TileSource::HOMEPAGE)))))));
EXPECT_CALL(mock_observer_,
OnURLsAvailable(Contains(
Pair(SectionType::PERSONALIZED,
- Not(Contains(MatchesTile(kHomePageTitle, kHomePageUrl,
+ Not(Contains(MatchesTile(kHomepageTitle, kHomepageUrl,
TileSource::HOMEPAGE)))))));
}
most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
@@ -598,45 +619,45 @@ TEST_P(MostVisitedSitesTest, ShouldIncludeHomeTileWithUrlBeforeQueryingName) {
base::RunLoop().RunUntilIdle();
}
-TEST_P(MostVisitedSitesTest, ShouldUpdateHomePageTileOnHomePageStateChanged) {
- FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
- home_page_client->SetHomePageEnabled(true);
+TEST_P(MostVisitedSitesTest, ShouldUpdateHomepageTileWhenRefreshHomepageTile) {
+ FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
+ homepage_client->SetHomepageTileEnabled(true);
DisableRemoteSuggestions();
// Ensure that home tile is available as usual.
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{}));
EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomepageUrl))))
.Times(AnyNumber())
.WillRepeatedly(Return(false));
EXPECT_CALL(mock_observer_, OnURLsAvailable(FirstPersonalizedTileIs(
- "", kHomePageUrl, TileSource::HOMEPAGE)));
+ "", kHomepageUrl, TileSource::HOMEPAGE)));
most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
/*num_sites=*/3);
base::RunLoop().RunUntilIdle();
VerifyAndClearExpectations();
// Disable home page and rebuild _without_ Resync. The tile should be gone.
- home_page_client->SetHomePageEnabled(false);
+ homepage_client->SetHomepageTileEnabled(false);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{}));
EXPECT_CALL(*mock_top_sites_, SyncWithHistory()).Times(0);
EXPECT_CALL(mock_observer_, OnURLsAvailable(Not(FirstPersonalizedTileIs(
- "", kHomePageUrl, TileSource::HOMEPAGE))));
- most_visited_sites_->OnHomePageStateChanged();
+ "", kHomepageUrl, TileSource::HOMEPAGE))));
+ most_visited_sites_->RefreshHomepageTile();
base::RunLoop().RunUntilIdle();
}
-TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfNoTileRequested) {
- FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
- home_page_client->SetHomePageEnabled(true);
+TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomepageIfNoTileRequested) {
+ FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
+ homepage_client->SetHomepageTileEnabled(true);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{}));
EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomepageUrl))))
.Times(AnyNumber())
.WillRepeatedly(Return(false));
EXPECT_CALL(
@@ -647,30 +668,30 @@ TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfNoTileRequested) {
base::RunLoop().RunUntilIdle();
}
-TEST_P(MostVisitedSitesTest, ShouldReturnHomePageIfOneTileRequested) {
- FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
- home_page_client->SetHomePageEnabled(true);
+TEST_P(MostVisitedSitesTest, ShouldReturnHomepageIfOneTileRequested) {
+ FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
+ homepage_client->SetHomepageTileEnabled(true);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(
(MostVisitedURLList{MakeMostVisitedURL("Site 1", "http://site1/")})));
EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomepageUrl))))
.Times(AnyNumber())
.WillRepeatedly(Return(false));
EXPECT_CALL(
mock_observer_,
OnURLsAvailable(Contains(Pair(
SectionType::PERSONALIZED,
- ElementsAre(MatchesTile("", kHomePageUrl, TileSource::HOMEPAGE))))));
+ ElementsAre(MatchesTile("", kHomepageUrl, TileSource::HOMEPAGE))))));
most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
/*num_sites=*/1);
base::RunLoop().RunUntilIdle();
}
-TEST_P(MostVisitedSitesTest, ShouldReplaceLastTileWithHomePageWhenFull) {
- FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
- home_page_client->SetHomePageEnabled(true);
+TEST_P(MostVisitedSitesTest, ShouldHaveHomepageFirstInListWhenFull) {
+ FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
+ homepage_client->SetHomepageTileEnabled(true);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>((MostVisitedURLList{
@@ -681,7 +702,7 @@ TEST_P(MostVisitedSitesTest, ShouldReplaceLastTileWithHomePageWhenFull) {
MakeMostVisitedURL("Site 5", "http://site5/"),
})));
EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomepageUrl))))
.Times(AnyNumber())
.WillRepeatedly(Return(false));
std::map<SectionType, NTPTilesVector> sections;
@@ -694,12 +715,12 @@ TEST_P(MostVisitedSitesTest, ShouldReplaceLastTileWithHomePageWhenFull) {
NTPTilesVector tiles = sections.at(SectionType::PERSONALIZED);
ASSERT_THAT(tiles.size(), Ge(4ul));
// Assert that the home page is appended as the final tile.
- EXPECT_THAT(tiles[3], MatchesTile("", kHomePageUrl, TileSource::HOMEPAGE));
+ EXPECT_THAT(tiles[0], MatchesTile("", kHomepageUrl, TileSource::HOMEPAGE));
}
-TEST_P(MostVisitedSitesTest, ShouldAppendHomePageWhenNotFull) {
- FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
- home_page_client->SetHomePageEnabled(true);
+TEST_P(MostVisitedSitesTest, ShouldHaveHomepageFirstInListWhenNotFull) {
+ FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
+ homepage_client->SetHomepageTileEnabled(true);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>((MostVisitedURLList{
@@ -710,7 +731,7 @@ TEST_P(MostVisitedSitesTest, ShouldAppendHomePageWhenNotFull) {
MakeMostVisitedURL("Site 5", "http://site5/"),
})));
EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomepageUrl))))
.Times(AnyNumber())
.WillRepeatedly(Return(false));
std::map<SectionType, NTPTilesVector> sections;
@@ -722,114 +743,93 @@ TEST_P(MostVisitedSitesTest, ShouldAppendHomePageWhenNotFull) {
ASSERT_THAT(sections, Contains(Key(SectionType::PERSONALIZED)));
NTPTilesVector tiles = sections.at(SectionType::PERSONALIZED);
ASSERT_THAT(tiles.size(), Ge(6ul));
- // Assert that the home page is appended as the final tile.
- EXPECT_THAT(tiles[5], MatchesTile("", kHomePageUrl, TileSource::HOMEPAGE));
+ // Assert that the home page is the first tile.
+ EXPECT_THAT(tiles[0], MatchesTile("", kHomepageUrl, TileSource::HOMEPAGE));
}
-TEST_P(MostVisitedSitesTest, ShouldDeduplicateHomePageWithTopSites) {
- FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
- home_page_client->SetHomePageEnabled(true);
+TEST_P(MostVisitedSitesTest, ShouldDeduplicateHomepageWithTopSites) {
+ FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
+ homepage_client->SetHomepageTileEnabled(true);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(
(MostVisitedURLList{MakeMostVisitedURL("Site 1", "http://site1/"),
- MakeMostVisitedURL("", kHomePageUrl)})));
+ MakeMostVisitedURL("", kHomepageUrl)})));
EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomepageUrl))))
.Times(AnyNumber())
.WillRepeatedly(Return(false));
EXPECT_CALL(
mock_observer_,
OnURLsAvailable(Contains(Pair(
SectionType::PERSONALIZED,
- AllOf(Contains(MatchesTile("", kHomePageUrl, TileSource::HOMEPAGE)),
+ AllOf(Contains(MatchesTile("", kHomepageUrl, TileSource::HOMEPAGE)),
Not(Contains(
- MatchesTile("", kHomePageUrl, TileSource::TOP_SITES))))))));
- most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
- /*num_sites=*/3);
- base::RunLoop().RunUntilIdle();
-}
-
-TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfItIsNewTabPage) {
- FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
- home_page_client->SetHomePageEnabled(true);
- home_page_client->SetNtpIsHomePage(true);
- DisableRemoteSuggestions();
- EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
- .WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{}));
- EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
- .Times(AnyNumber())
- .WillRepeatedly(Return(false));
- EXPECT_CALL(mock_observer_,
- OnURLsAvailable(Contains(
- Pair(SectionType::PERSONALIZED,
- Not(Contains(MatchesTile("", kHomePageUrl,
- TileSource::HOMEPAGE)))))));
+ MatchesTile("", kHomepageUrl, TileSource::TOP_SITES))))))));
most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
/*num_sites=*/3);
base::RunLoop().RunUntilIdle();
}
-TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfThereIsNone) {
- FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
- home_page_client->SetHomePageEnabled(false);
+TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomepageIfThereIsNone) {
+ FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
+ homepage_client->SetHomepageTileEnabled(false);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{}));
EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomepageUrl))))
.Times(AnyNumber())
.WillRepeatedly(Return(false));
EXPECT_CALL(mock_observer_,
OnURLsAvailable(Contains(
Pair(SectionType::PERSONALIZED,
- Not(Contains(MatchesTile("", kHomePageUrl,
+ Not(Contains(MatchesTile("", kHomepageUrl,
TileSource::HOMEPAGE)))))));
most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
/*num_sites=*/3);
base::RunLoop().RunUntilIdle();
}
-TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfEmptyUrl) {
- const std::string kEmptyHomePageUrl;
- FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
- home_page_client->SetHomePageEnabled(true);
- home_page_client->SetHomePageUrl(GURL(kEmptyHomePageUrl));
+TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomepageIfEmptyUrl) {
+ const std::string kEmptyHomepageUrl;
+ FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
+ homepage_client->SetHomepageTileEnabled(true);
+ homepage_client->SetHomepageUrl(GURL(kEmptyHomepageUrl));
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(MostVisitedURLList{}));
EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(kEmptyHomePageUrl)))
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(kEmptyHomepageUrl)))
.Times(AnyNumber())
.WillRepeatedly(Return(false));
EXPECT_CALL(mock_observer_,
OnURLsAvailable(Not(FirstPersonalizedTileIs(
- "", kEmptyHomePageUrl, TileSource::HOMEPAGE))));
+ "", kEmptyHomepageUrl, TileSource::HOMEPAGE))));
most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
/*num_sites=*/3);
base::RunLoop().RunUntilIdle();
}
-TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfBlacklisted) {
- FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
- home_page_client->SetHomePageEnabled(true);
+TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomepageIfBlacklisted) {
+ FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
+ homepage_client->SetHomepageTileEnabled(true);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillRepeatedly(InvokeCallbackArgument<0>(
- (MostVisitedURLList{MakeMostVisitedURL("", kHomePageUrl)})));
+ (MostVisitedURLList{MakeMostVisitedURL("", kHomepageUrl)})));
EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomepageUrl))))
.Times(AnyNumber())
.WillRepeatedly(Return(false));
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomepageUrl))))
.Times(AtLeast(1))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_observer_,
OnURLsAvailable(Contains(
Pair(SectionType::PERSONALIZED,
- Not(Contains(MatchesTile("", kHomePageUrl,
+ Not(Contains(MatchesTile("", kHomepageUrl,
TileSource::HOMEPAGE)))))));
most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
@@ -837,22 +837,22 @@ TEST_P(MostVisitedSitesTest, ShouldNotIncludeHomePageIfBlacklisted) {
base::RunLoop().RunUntilIdle();
}
-TEST_P(MostVisitedSitesTest, ShouldPinHomePageAgainIfBlacklistingUndone) {
- FakeHomePageClient* home_page_client = RegisterNewHomePageClient();
- home_page_client->SetHomePageEnabled(true);
+TEST_P(MostVisitedSitesTest, ShouldPinHomepageAgainIfBlacklistingUndone) {
+ FakeHomepageClient* homepage_client = RegisterNewHomepageClient();
+ homepage_client->SetHomepageTileEnabled(true);
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillOnce(InvokeCallbackArgument<0>(
- (MostVisitedURLList{MakeMostVisitedURL("", kHomePageUrl)})));
+ (MostVisitedURLList{MakeMostVisitedURL("", kHomepageUrl)})));
EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomepageUrl))))
.Times(AtLeast(1))
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_observer_,
OnURLsAvailable(Contains(
Pair(SectionType::PERSONALIZED,
- Not(Contains(MatchesTile("", kHomePageUrl,
+ Not(Contains(MatchesTile("", kHomepageUrl,
TileSource::HOMEPAGE)))))));
most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
@@ -863,32 +863,32 @@ TEST_P(MostVisitedSitesTest, ShouldPinHomePageAgainIfBlacklistingUndone) {
DisableRemoteSuggestions();
EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
.WillOnce(InvokeCallbackArgument<0>(MostVisitedURLList{}));
- EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, IsBlacklisted(Eq(GURL(kHomepageUrl))))
.Times(AtLeast(1))
.WillRepeatedly(Return(false));
EXPECT_CALL(
mock_observer_,
OnURLsAvailable(Contains(Pair(
SectionType::PERSONALIZED,
- Contains(MatchesTile("", kHomePageUrl, TileSource::HOMEPAGE))))));
+ Contains(MatchesTile("", kHomepageUrl, TileSource::HOMEPAGE))))));
most_visited_sites_->OnBlockedSitesChanged();
base::RunLoop().RunUntilIdle();
}
TEST_P(MostVisitedSitesTest, ShouldInformSuggestionSourcesWhenBlacklisting) {
- EXPECT_CALL(*mock_top_sites_, AddBlacklistedURL(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, AddBlacklistedURL(Eq(GURL(kHomepageUrl))))
.Times(1);
- EXPECT_CALL(mock_suggestions_service_, BlacklistURL(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(mock_suggestions_service_, BlacklistURL(Eq(GURL(kHomepageUrl))))
.Times(AnyNumber());
- most_visited_sites_->AddOrRemoveBlacklistedUrl(GURL(kHomePageUrl),
+ most_visited_sites_->AddOrRemoveBlacklistedUrl(GURL(kHomepageUrl),
/*add_url=*/true);
- EXPECT_CALL(*mock_top_sites_, RemoveBlacklistedURL(Eq(GURL(kHomePageUrl))))
+ EXPECT_CALL(*mock_top_sites_, RemoveBlacklistedURL(Eq(GURL(kHomepageUrl))))
.Times(1);
EXPECT_CALL(mock_suggestions_service_,
- UndoBlacklistURL(Eq(GURL(kHomePageUrl))))
+ UndoBlacklistURL(Eq(GURL(kHomepageUrl))))
.Times(AnyNumber());
- most_visited_sites_->AddOrRemoveBlacklistedUrl(GURL(kHomePageUrl),
+ most_visited_sites_->AddOrRemoveBlacklistedUrl(GURL(kHomepageUrl),
/*add_url=*/false);
}
@@ -1069,6 +1069,187 @@ TEST(MostVisitedSitesTest, ShouldDeduplicateDomainByReplacingMobilePrefixes) {
"www.cnn.com"));
}
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
+TEST_P(MostVisitedSitesTest, ShouldOnlyBuildCustomLinksWhenInitialized) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(kNtpCustomLinks);
+ const char kTestUrl[] = "http://site1/";
+ const char kTestTitle[] = "Site 1";
+ std::vector<CustomLinksManager::Link> expected_links(
+ {CustomLinksManager::Link{GURL(kTestUrl),
+ base::UTF8ToUTF16(kTestTitle)}});
+
+ std::map<SectionType, NTPTilesVector> sections;
+ EnableCustomLinks();
+ RecreateMostVisitedSites();
+ DisableRemoteSuggestions();
+
+ // Build tiles when custom links is not initialized. Tiles should be Top
+ // Sites.
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
+ .WillRepeatedly(InvokeCallbackArgument<0>(
+ MostVisitedURLList{MakeMostVisitedURL(kTestTitle, kTestUrl)}));
+ EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
+ EXPECT_CALL(*mock_custom_links_, IsInitialized())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(mock_observer_, OnURLsAvailable(_))
+ .WillOnce(SaveArg<0>(&sections));
+
+ most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
+ /*num_sites=*/1);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_THAT(
+ sections.at(SectionType::PERSONALIZED),
+ ElementsAre(MatchesTile(kTestTitle, kTestUrl, TileSource::TOP_SITES)));
+
+ // Initialize custom links and rebuild tiles. Tiles should be custom links.
+ EXPECT_CALL(*mock_custom_links_, Initialize(_)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_custom_links_, IsInitialized()).WillOnce(Return(true));
+ EXPECT_CALL(*mock_custom_links_, GetLinks())
+ .WillOnce(ReturnRef(expected_links));
+ EXPECT_CALL(mock_observer_, OnURLsAvailable(_))
+ .WillOnce(SaveArg<0>(&sections));
+
+ most_visited_sites_->InitializeCustomLinks();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_THAT(
+ sections.at(SectionType::PERSONALIZED),
+ ElementsAre(MatchesTile(kTestTitle, kTestUrl, TileSource::CUSTOM_LINKS)));
+
+ // Uninitialize custom links and rebuild tiles. Tiles should be Top Sites.
+ EXPECT_CALL(*mock_custom_links_, Uninitialize());
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
+ .WillRepeatedly(InvokeCallbackArgument<0>(
+ MostVisitedURLList{MakeMostVisitedURL(kTestTitle, kTestUrl)}));
+ EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
+ EXPECT_CALL(*mock_custom_links_, IsInitialized())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(mock_observer_, OnURLsAvailable(_))
+ .WillOnce(SaveArg<0>(&sections));
+
+ most_visited_sites_->UninitializeCustomLinks();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_THAT(
+ sections.at(SectionType::PERSONALIZED),
+ ElementsAre(MatchesTile(kTestTitle, kTestUrl, TileSource::TOP_SITES)));
+}
+
+TEST_P(MostVisitedSitesTest, ShouldFavorCustomLinksOverTopSites) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(kNtpCustomLinks);
+ const char kTestUrl[] = "http://site1/";
+ const char kTestTitle[] = "Site 1";
+ std::vector<CustomLinksManager::Link> expected_links(
+ {CustomLinksManager::Link{GURL(kTestUrl),
+ base::UTF8ToUTF16(kTestTitle)}});
+
+ std::map<SectionType, NTPTilesVector> sections;
+ EnableCustomLinks();
+ RecreateMostVisitedSites();
+ DisableRemoteSuggestions();
+
+ // Build tiles when custom links is not initialized. Tiles should be Top
+ // Sites.
+ EXPECT_CALL(*mock_top_sites_, GetMostVisitedURLs(_, false))
+ .WillRepeatedly(InvokeCallbackArgument<0>(
+ MostVisitedURLList{MakeMostVisitedURL(kTestTitle, kTestUrl)}));
+ EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
+ EXPECT_CALL(*mock_custom_links_, IsInitialized())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(mock_observer_, OnURLsAvailable(_))
+ .WillOnce(SaveArg<0>(&sections));
+ most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
+ /*num_sites=*/1);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_THAT(
+ sections.at(SectionType::PERSONALIZED),
+ ElementsAre(MatchesTile(kTestTitle, kTestUrl, TileSource::TOP_SITES)));
+
+ // Initialize custom links and rebuild tiles. Tiles should be custom links.
+ EXPECT_CALL(*mock_custom_links_, Initialize(_)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_custom_links_, IsInitialized()).WillOnce(Return(true));
+ EXPECT_CALL(*mock_custom_links_, GetLinks())
+ .WillOnce(ReturnRef(expected_links));
+ EXPECT_CALL(mock_observer_, OnURLsAvailable(_))
+ .WillOnce(SaveArg<0>(&sections));
+ most_visited_sites_->InitializeCustomLinks();
+ base::RunLoop().RunUntilIdle();
+ ASSERT_THAT(
+ sections.at(SectionType::PERSONALIZED),
+ ElementsAre(MatchesTile(kTestTitle, kTestUrl, TileSource::CUSTOM_LINKS)));
+
+ // Initiate notification for new Top Sites. This should be ignored.
+ EXPECT_CALL(*mock_custom_links_, IsInitialized()).WillOnce(Return(true));
+ // Notify with an empty SuggestionsProfile first to trigger a query for
+ // TopSites.
+ suggestions_service_callbacks_.Notify(SuggestionsProfile());
+ VerifyAndClearExpectations();
+ EXPECT_CALL(mock_observer_, OnURLsAvailable(_)).Times(0);
+ top_sites_callbacks_.ClearAndNotify(
+ {MakeMostVisitedURL("Site 2", "http://site2/")});
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_P(MostVisitedSitesTest, ShouldFavorCustomLinksOverSuggestions) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(kNtpCustomLinks);
+ const char kTestUrl[] = "http://site1/";
+ const char kTestTitle[] = "Site 1";
+ std::vector<CustomLinksManager::Link> expected_links(
+ {CustomLinksManager::Link{GURL(kTestUrl),
+ base::UTF8ToUTF16(kTestTitle)}});
+
+ std::map<SectionType, NTPTilesVector> sections;
+ EnableCustomLinks();
+ RecreateMostVisitedSites();
+
+ // Build tiles when custom links is not initialized. Tiles should be Top
+ // Sites.
+ EXPECT_CALL(mock_suggestions_service_, AddCallback(_))
+ .WillOnce(Invoke(&suggestions_service_callbacks_,
+ &SuggestionsService::ResponseCallbackList::Add));
+ EXPECT_CALL(mock_suggestions_service_, GetSuggestionsDataFromCache())
+ .WillOnce(Return(MakeProfile({MakeSuggestion(kTestTitle, kTestUrl)})));
+ EXPECT_CALL(*mock_top_sites_, SyncWithHistory());
+ EXPECT_CALL(mock_suggestions_service_, FetchSuggestionsData())
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_custom_links_, IsInitialized())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(mock_observer_, OnURLsAvailable(_))
+ .WillOnce(SaveArg<0>(&sections));
+ most_visited_sites_->SetMostVisitedURLsObserver(&mock_observer_,
+ /*num_sites=*/1);
+ base::RunLoop().RunUntilIdle();
+ ASSERT_THAT(sections.at(SectionType::PERSONALIZED),
+ ElementsAre(MatchesTile(kTestTitle, kTestUrl,
+ TileSource::SUGGESTIONS_SERVICE)));
+
+ // Initialize custom links and rebuild tiles. Tiles should be custom links.
+ EXPECT_CALL(*mock_custom_links_, Initialize(_)).WillOnce(Return(true));
+ EXPECT_CALL(*mock_custom_links_, IsInitialized()).WillOnce(Return(true));
+ EXPECT_CALL(*mock_custom_links_, GetLinks())
+ .WillOnce(ReturnRef(expected_links));
+ EXPECT_CALL(mock_observer_, OnURLsAvailable(_))
+ .WillOnce(SaveArg<0>(&sections));
+ most_visited_sites_->InitializeCustomLinks();
+ base::RunLoop().RunUntilIdle();
+ ASSERT_THAT(
+ sections.at(SectionType::PERSONALIZED),
+ ElementsAre(MatchesTile(kTestTitle, kTestUrl, TileSource::CUSTOM_LINKS)));
+
+ // Initiate notification for new suggestions. This should be ignored.
+ EXPECT_CALL(*mock_custom_links_, IsInitialized())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mock_observer_, OnURLsAvailable(_)).Times(0);
+ suggestions_service_callbacks_.Notify(
+ MakeProfile({MakeSuggestion("Site 2", "http://site2/")}));
+ base::RunLoop().RunUntilIdle();
+}
+#endif
+
class MostVisitedSitesWithCacheHitTest : public MostVisitedSitesTest {
public:
// Constructor sets the common expectations for the case where suggestions
diff --git a/chromium/components/ntp_tiles/popular_sites_impl.cc b/chromium/components/ntp_tiles/popular_sites_impl.cc
index 4282e128d9b..6f4f99fb6c1 100644
--- a/chromium/components/ntp_tiles/popular_sites_impl.cc
+++ b/chromium/components/ntp_tiles/popular_sites_impl.cc
@@ -33,8 +33,9 @@
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_status.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"
#if defined(OS_ANDROID) || defined(OS_IOS)
#include "base/json/json_reader.h"
@@ -46,7 +47,6 @@
#include "components/ntp_tiles/country_code_ios.h"
#endif
-using net::URLFetcher;
using variations::VariationsService;
namespace ntp_tiles {
@@ -271,12 +271,12 @@ PopularSitesImpl::PopularSitesImpl(
PrefService* prefs,
const TemplateURLService* template_url_service,
VariationsService* variations_service,
- net::URLRequestContextGetter* download_context,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
ParseJSONCallback parse_json)
: prefs_(prefs),
template_url_service_(template_url_service),
variations_(variations_service),
- download_context_(download_context),
+ url_loader_factory_(std::move(url_loader_factory)),
parse_json_(std::move(parse_json)),
is_fallback_(false),
sections_(
@@ -452,30 +452,33 @@ void PopularSitesImpl::FetchPopularSites() {
policy_exception_justification:
"Not implemented, considered not useful."
})");
- fetcher_ = URLFetcher::Create(pending_url_, URLFetcher::GET, this,
- traffic_annotation);
- data_use_measurement::DataUseUserData::AttachToFetcher(
- fetcher_.get(), data_use_measurement::DataUseUserData::NTP_TILES);
- fetcher_->SetRequestContext(download_context_);
- fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES);
- fetcher_->SetAutomaticallyRetryOnNetworkChanges(1);
- fetcher_->Start();
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = pending_url_;
+ resource_request->load_flags =
+ net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
+ // TODO(https://crbug.com/808498): Re-add data use measurement once
+ // SimpleURLLoader supports it.
+ // ID=data_use_measurement::DataUseUserData::NTP_TILES
+ simple_url_loader_ = network::SimpleURLLoader::Create(
+ std::move(resource_request), traffic_annotation);
+ simple_url_loader_->SetRetryOptions(
+ 1, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
+ simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&PopularSitesImpl::OnSimpleLoaderComplete,
+ base::Unretained(this)));
}
-void PopularSitesImpl::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK_EQ(fetcher_.get(), source);
- std::unique_ptr<net::URLFetcher> free_fetcher = std::move(fetcher_);
+void PopularSitesImpl::OnSimpleLoaderComplete(
+ std::unique_ptr<std::string> response_body) {
+ simple_url_loader_.reset();
- std::string json_string;
- if (!(source->GetStatus().is_success() &&
- source->GetResponseCode() == net::HTTP_OK &&
- source->GetResponseAsString(&json_string))) {
+ if (!response_body) {
OnDownloadFailed();
return;
}
- parse_json_.Run(json_string,
+ parse_json_.Run(*response_body,
base::Bind(&PopularSitesImpl::OnJsonParsed,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&PopularSitesImpl::OnJsonParseFailed,
diff --git a/chromium/components/ntp_tiles/popular_sites_impl.h b/chromium/components/ntp_tiles/popular_sites_impl.h
index c58dbf3538d..43588d01250 100644
--- a/chromium/components/ntp_tiles/popular_sites_impl.h
+++ b/chromium/components/ntp_tiles/popular_sites_impl.h
@@ -15,15 +15,15 @@
#include "base/memory/weak_ptr.h"
#include "base/strings/string16.h"
#include "components/ntp_tiles/popular_sites.h"
-#include "net/url_request/url_fetcher_delegate.h"
#include "url/gurl.h"
namespace base {
class Value;
}
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
}
namespace user_prefs {
@@ -46,13 +46,13 @@ using ParseJSONCallback = base::Callback<void(
// Actual (non-test) implementation of the PopularSites interface. Caches the
// downloaded file on disk to avoid re-downloading on every startup.
-class PopularSitesImpl : public PopularSites, public net::URLFetcherDelegate {
+class PopularSitesImpl : public PopularSites {
public:
PopularSitesImpl(
PrefService* prefs,
const TemplateURLService* template_url_service,
variations::VariationsService* variations_service,
- net::URLRequestContextGetter* download_context,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
ParseJSONCallback parse_json);
~PopularSitesImpl() override;
@@ -77,8 +77,8 @@ class PopularSitesImpl : public PopularSites, public net::URLFetcherDelegate {
// that already exists.
void FetchPopularSites();
- // net::URLFetcherDelegate implementation.
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ // Called once SimpleURLLoader completes the network request.
+ void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
void OnJsonParsed(std::unique_ptr<base::Value> json);
void OnJsonParseFailed(const std::string& error_message);
@@ -88,13 +88,13 @@ class PopularSitesImpl : public PopularSites, public net::URLFetcherDelegate {
PrefService* const prefs_;
const TemplateURLService* const template_url_service_;
variations::VariationsService* const variations_;
- net::URLRequestContextGetter* const download_context_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
ParseJSONCallback parse_json_;
// Set by MaybeStartFetch() and called after fetch completes.
FinishedCallback callback_;
- std::unique_ptr<net::URLFetcher> fetcher_;
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
bool is_fallback_;
std::map<SectionType, SitesVector> sections_;
GURL pending_url_;
diff --git a/chromium/components/ntp_tiles/popular_sites_impl_unittest.cc b/chromium/components/ntp_tiles/popular_sites_impl_unittest.cc
index 20fb333fb36..bc89950cae5 100644
--- a/chromium/components/ntp_tiles/popular_sites_impl_unittest.cc
+++ b/chromium/components/ntp_tiles/popular_sites_impl_unittest.cc
@@ -17,6 +17,7 @@
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
@@ -28,9 +29,9 @@
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "net/http/http_status_code.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_status.h"
-#include "net/url_request/url_request_test_util.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_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -98,7 +99,9 @@ class PopularSitesTest : public ::testing::Test {
// No "title_source" (like in v5 or earlier). Defaults to TITLE_TAG.
},
prefs_(new sync_preferences::TestingPrefServiceSyncable()),
- url_fetcher_factory_(nullptr) {
+ test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)) {
PopularSitesImpl::RegisterProfilePrefs(prefs_->registry());
}
@@ -132,8 +135,7 @@ class PopularSitesTest : public ::testing::Test {
const TestPopularSiteVector& sites) {
std::string sites_string;
base::JSONWriter::Write(*CreateListFromTestSites(sites), &sites_string);
- url_fetcher_factory_.SetFakeResponse(GURL(url), sites_string, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ test_url_loader_factory_.AddResponse(url, sites_string);
}
void RespondWithV6JSON(const std::string& url,
@@ -147,18 +149,15 @@ class PopularSitesTest : public ::testing::Test {
}
std::string sites_string;
base::JSONWriter::Write(sections_value, &sites_string);
- url_fetcher_factory_.SetFakeResponse(GURL(url), sites_string, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ test_url_loader_factory_.AddResponse(url, sites_string);
}
void RespondWithData(const std::string& url, const std::string& data) {
- url_fetcher_factory_.SetFakeResponse(GURL(url), data, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ test_url_loader_factory_.AddResponse(url, data);
}
void RespondWith404(const std::string& url) {
- url_fetcher_factory_.SetFakeResponse(GURL(url), "404", net::HTTP_NOT_FOUND,
- net::URLRequestStatus::SUCCESS);
+ test_url_loader_factory_.AddResponse(url, "", net::HTTP_NOT_FOUND);
}
void ReregisterProfilePrefs() {
@@ -182,11 +181,7 @@ class PopularSitesTest : public ::testing::Test {
base::Optional<bool> FetchAllSections(
bool force_download,
std::map<SectionType, PopularSites::SitesVector>* sections) {
- scoped_refptr<net::TestURLRequestContextGetter> url_request_context(
- new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get()));
- std::unique_ptr<PopularSites> popular_sites =
- CreatePopularSites(url_request_context.get());
+ std::unique_ptr<PopularSites> popular_sites = CreatePopularSites();
base::RunLoop loop;
base::Optional<bool> save_success;
@@ -204,12 +199,11 @@ class PopularSitesTest : public ::testing::Test {
return save_success;
}
- std::unique_ptr<PopularSites> CreatePopularSites(
- net::URLRequestContextGetter* context) {
+ std::unique_ptr<PopularSites> CreatePopularSites() {
return std::make_unique<PopularSitesImpl>(
prefs_.get(),
/*template_url_service=*/nullptr,
- /*variations_service=*/nullptr, context,
+ /*variations_service=*/nullptr, test_shared_loader_factory_,
base::Bind(JsonUnsafeParser::Parse));
}
@@ -219,15 +213,12 @@ class PopularSitesTest : public ::testing::Test {
base::MessageLoopForUI ui_loop_;
std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> prefs_;
- net::FakeURLFetcherFactory url_fetcher_factory_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
};
TEST_F(PopularSitesTest, ContainsDefaultTilesRightAfterConstruction) {
- scoped_refptr<net::TestURLRequestContextGetter> url_request_context(
- new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get()));
-
- auto popular_sites = CreatePopularSites(url_request_context.get());
+ auto popular_sites = CreatePopularSites();
EXPECT_THAT(
popular_sites->sections(),
ElementsAre(Pair(SectionType::PERSONALIZED,
@@ -239,10 +230,7 @@ TEST_F(PopularSitesTest, IsEmptyOnConstructionIfDisabledByTrial) {
override_features.InitAndDisableFeature(kPopularSitesBakedInContentFeature);
ReregisterProfilePrefs();
- scoped_refptr<net::TestURLRequestContextGetter> url_request_context(
- new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get()));
- auto popular_sites = CreatePopularSites(url_request_context.get());
+ auto popular_sites = CreatePopularSites();
EXPECT_THAT(popular_sites->sections(),
ElementsAre(Pair(SectionType::PERSONALIZED, IsEmpty())));
@@ -311,11 +299,7 @@ TEST_F(PopularSitesTest, PopulatesWithDefaultResoucesOnFailure) {
#if defined(OS_ANDROID) || defined(OS_IOS)
TEST_F(PopularSitesTest, AddsIconResourcesToDefaultPages) {
- scoped_refptr<net::TestURLRequestContextGetter> url_request_context(
- new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get()));
- std::unique_ptr<PopularSites> popular_sites =
- CreatePopularSites(url_request_context.get());
+ std::unique_ptr<PopularSites> popular_sites = CreatePopularSites();
const PopularSites::SitesVector& sites =
popular_sites->sections().at(SectionType::PERSONALIZED);
@@ -334,11 +318,7 @@ TEST_F(PopularSitesTest, ProvidesDefaultSitesUntilCallbackReturns) {
RespondWithV5JSON(
"https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_5.json",
{kWikipedia});
- scoped_refptr<net::TestURLRequestContextGetter> url_request_context(
- new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get()));
- std::unique_ptr<PopularSites> popular_sites =
- CreatePopularSites(url_request_context.get());
+ std::unique_ptr<PopularSites> popular_sites = CreatePopularSites();
base::RunLoop loop;
base::Optional<bool> save_success = false;
diff --git a/chromium/components/ntp_tiles/pref_names.cc b/chromium/components/ntp_tiles/pref_names.cc
index 409de49b546..80444932676 100644
--- a/chromium/components/ntp_tiles/pref_names.cc
+++ b/chromium/components/ntp_tiles/pref_names.cc
@@ -31,5 +31,9 @@ const char kPopularSitesURLPref[] = "popular_sites_url";
const char kPopularSitesJsonPref[] = "suggested_sites_json";
const char kPopularSitesVersionPref[] = "suggested_sites_version";
+// Prefs used to cache custom links.
+const char kCustomLinksList[] = "custom_links.list";
+const char kCustomLinksInitialized[] = "custom_links.initialized";
+
} // namespace prefs
} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/pref_names.h b/chromium/components/ntp_tiles/pref_names.h
index 687f5577b29..1b0959bd030 100644
--- a/chromium/components/ntp_tiles/pref_names.h
+++ b/chromium/components/ntp_tiles/pref_names.h
@@ -20,6 +20,9 @@ extern const char kPopularSitesURLPref[];
extern const char kPopularSitesJsonPref[];
extern const char kPopularSitesVersionPref[];
+extern const char kCustomLinksList[];
+extern const char kCustomLinksInitialized[];
+
} // namespace prefs
} // namespace ntp_tiles
diff --git a/chromium/components/ntp_tiles/tile_source.h b/chromium/components/ntp_tiles/tile_source.h
index 551e1f92557..165aa71809f 100644
--- a/chromium/components/ntp_tiles/tile_source.h
+++ b/chromium/components/ntp_tiles/tile_source.h
@@ -19,6 +19,8 @@ enum class TileSource {
POPULAR,
// Tile is a popular site baked into the binary.
POPULAR_BAKED_IN,
+ // Tile is a custom link.
+ CUSTOM_LINKS,
// Tile is on a custodian-managed whitelist.
WHITELIST,
// Tile containing the user-set home page is replacing the home page button.
diff --git a/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc b/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc
index 43ef9303ad0..b0258cecd31 100644
--- a/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc
+++ b/chromium/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc
@@ -18,6 +18,7 @@
#include "base/task_runner_util.h"
#include "base/values.h"
#include "components/favicon/core/favicon_service.h"
+#include "components/ntp_tiles/constants.h"
#include "components/ntp_tiles/most_visited_sites.h"
#include "components/ntp_tiles/pref_names.h"
#include "components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.h"
@@ -58,7 +59,10 @@ NTPTilesInternalsMessageHandler::NTPTilesInternalsMessageHandler(
favicon::FaviconService* favicon_service)
: favicon_service_(favicon_service),
client_(nullptr),
- site_count_(8),
+ // 9 tiles are required for the custom links feature in order to balance
+ // the Most Visited rows (this is due to an additional "Add" button).
+ // Otherwise, Most Visited should return the regular 8 tiles.
+ site_count_(IsCustomLinksEnabled() ? 9 : 8),
weak_ptr_factory_(this) {}
NTPTilesInternalsMessageHandler::~NTPTilesInternalsMessageHandler() = default;
diff --git a/chromium/components/nux/OWNERS b/chromium/components/nux/OWNERS
new file mode 100644
index 00000000000..08ee1a9630e
--- /dev/null
+++ b/chromium/components/nux/OWNERS
@@ -0,0 +1 @@
+hcarmona@chromium.org
diff --git a/chromium/components/nux/show_promo_delegate.h b/chromium/components/nux/show_promo_delegate.h
new file mode 100644
index 00000000000..9c1e4fa36f1
--- /dev/null
+++ b/chromium/components/nux/show_promo_delegate.h
@@ -0,0 +1,26 @@
+// 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_NUX_SHOW_PROMO_DELEGATE_H_
+#define COMPONENTS_NUX_SHOW_PROMO_DELEGATE_H_
+
+#include <memory>
+
+namespace bookmarks {
+class BookmarkNode;
+} // namespace bookmarks
+
+class ShowPromoDelegate {
+ public:
+ virtual ~ShowPromoDelegate() = default;
+
+ // Shows a promotional popup for the specified bookmark node.
+ virtual void ShowForNode(const bookmarks::BookmarkNode* node) = 0;
+
+ // Return an instance of the promo delegate.
+ static std::unique_ptr<ShowPromoDelegate> CreatePromoDelegate(
+ int string_specifier);
+};
+
+#endif // COMPONENTS_NUX_SHOW_PROMO_DELEGATE_H_
diff --git a/chromium/components/nux_google_apps/BUILD.gn b/chromium/components/nux_google_apps/BUILD.gn
new file mode 100644
index 00000000000..833a110d179
--- /dev/null
+++ b/chromium/components/nux_google_apps/BUILD.gn
@@ -0,0 +1,35 @@
+# 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.
+
+import("//build/config/chrome_build.gni")
+
+if (is_win && is_chrome_branded) {
+ static_library("nux_google_apps_feature") {
+ sources = [
+ "constants.cc",
+ "constants.h",
+ "google_apps_handler.cc",
+ "google_apps_handler.h",
+ ]
+
+ public_deps = [
+ "//base",
+ "//net",
+ "//url",
+ ]
+
+ deps = [
+ "//components/bookmarks/browser",
+ "//components/bookmarks/common",
+ "//components/favicon/core",
+ "//components/pref_registry",
+ "//components/prefs",
+ "//components/resources",
+ "//components/strings",
+ "//components/variations",
+ "//content/public/browser",
+ "//ui/base",
+ ]
+ }
+}
diff --git a/chromium/components/nux_google_apps/DEPS b/chromium/components/nux_google_apps/DEPS
new file mode 100644
index 00000000000..168b9698923
--- /dev/null
+++ b/chromium/components/nux_google_apps/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ "+components/bookmarks",
+ "+components/grit",
+ "+components/prefs",
+ "+components/nux",
+ "+components/favicon",
+ "+components/strings/grit/components_strings.h",
+ "+content/public/browser",
+ "+ui/base",
+]
diff --git a/chromium/components/nux_google_apps/OWNERS b/chromium/components/nux_google_apps/OWNERS
new file mode 100644
index 00000000000..08ee1a9630e
--- /dev/null
+++ b/chromium/components/nux_google_apps/OWNERS
@@ -0,0 +1 @@
+hcarmona@chromium.org
diff --git a/chromium/components/nux_google_apps/README b/chromium/components/nux_google_apps/README
new file mode 100644
index 00000000000..4218a87d7c6
--- /dev/null
+++ b/chromium/components/nux_google_apps/README
@@ -0,0 +1,5 @@
+New User Experience Experiment (NUX). Code here will enable new users to
+bookmark popular Google Apps as part of the first run steps.
+
+Feature is initially Windows Only. After collecting stats, we will determine
+how useful it is for users, this data will help decide next steps for component.
diff --git a/chromium/components/nux_google_apps/constants.cc b/chromium/components/nux_google_apps/constants.cc
new file mode 100644
index 00000000000..7b63d0cf94d
--- /dev/null
+++ b/chromium/components/nux_google_apps/constants.cc
@@ -0,0 +1,16 @@
+// 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/nux_google_apps/constants.h"
+
+#include "base/feature_list.h"
+
+namespace nux_google_apps {
+
+extern const base::Feature kNuxGoogleAppsFeature{
+ "NuxGoogleApps", base::FEATURE_DISABLED_BY_DEFAULT};
+
+extern const char kNuxGoogleAppsUrl[] = "chrome://welcome/apps";
+
+} // namespace nux_google_apps \ No newline at end of file
diff --git a/chromium/components/nux_google_apps/constants.h b/chromium/components/nux_google_apps/constants.h
new file mode 100644
index 00000000000..938277d8e16
--- /dev/null
+++ b/chromium/components/nux_google_apps/constants.h
@@ -0,0 +1,19 @@
+// 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_NUX_GOOGLE_APPS_CONSTANTS_H_
+#define COMPONENTS_NUX_GOOGLE_APPS_CONSTANTS_H_
+
+namespace base {
+struct Feature;
+} // namespace base
+
+namespace nux_google_apps {
+
+extern const base::Feature kNuxGoogleAppsFeature;
+extern const char kNuxGoogleAppsUrl[];
+
+} // namespace nux_google_apps
+
+#endif // COMPONENTS_NUX_GOOGLE_APPS_CONSTANTS_H_
diff --git a/chromium/components/nux_google_apps/google_apps_handler.cc b/chromium/components/nux_google_apps/google_apps_handler.cc
new file mode 100644
index 00000000000..282ccfa622b
--- /dev/null
+++ b/chromium/components/nux_google_apps/google_apps_handler.cc
@@ -0,0 +1,185 @@
+// 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/nux_google_apps/google_apps_handler.h"
+
+#include "base/bind.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/common/bookmark_pref_names.h"
+#include "components/favicon/core/favicon_service.h"
+#include "components/grit/components_resources.h"
+#include "components/grit/components_scaled_resources.h"
+#include "components/nux/show_promo_delegate.h"
+#include "components/nux_google_apps/constants.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace nux_google_apps {
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class GoogleApps {
+ kGmail = 0,
+ kYouTube = 1,
+ kMaps = 2,
+ kTranslate = 3,
+ kNews = 4,
+ kChromeWebStore = 5,
+ kCount,
+};
+
+const char* kGoogleAppsInteractionHistogram =
+ "FirstRun.NewUserExperience.GoogleAppsInteraction";
+
+// Strings in costants not translated because this is an experiment.
+// Translate before wide release.
+
+constexpr const char* kGoogleAppNames[] = {
+ "Gmail", "YouTube", "Maps", "Translate", "News", "Chrome Web Store",
+};
+
+constexpr const char* kGoogleAppUrls[] = {
+ "https://gmail.com", "https://youtube.com",
+ "https://maps.google.com", "https://translate.google.com",
+ "https://news.google.com", "https://chrome.google.com/webstore",
+};
+
+constexpr const int kGoogleAppIconSize = 48; // Pixels.
+constexpr const int kGoogleAppIcons[] = {
+ IDR_NUX_GOOGLE_APPS_GMAIL_1X, IDR_NUX_GOOGLE_APPS_YOUTUBE_1X,
+ IDR_NUX_GOOGLE_APPS_MAPS_1X, IDR_NUX_GOOGLE_APPS_TRANSLATE_1X,
+ IDR_NUX_GOOGLE_APPS_NEWS_1X, IDR_NUX_GOOGLE_APPS_CHROME_STORE_1X,
+};
+
+static_assert(base::size(kGoogleAppNames) == base::size(kGoogleAppUrls),
+ "names and urls must match");
+static_assert(base::size(kGoogleAppNames) == (size_t)GoogleApps::kCount,
+ "names and histograms must match");
+static_assert(base::size(kGoogleAppNames) == base::size(kGoogleAppIcons),
+ "names and icons must match");
+
+GoogleAppsHandler::GoogleAppsHandler(PrefService* prefs,
+ favicon::FaviconService* favicon_service,
+ bookmarks::BookmarkModel* bookmark_model)
+ : prefs_(prefs),
+ favicon_service_(favicon_service),
+ bookmark_model_(bookmark_model) {}
+
+GoogleAppsHandler::~GoogleAppsHandler() {}
+
+void GoogleAppsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "rejectGoogleApps",
+ base::BindRepeating(&GoogleAppsHandler::HandleRejectGoogleApps,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "addGoogleApps",
+ base::BindRepeating(&GoogleAppsHandler::HandleAddGoogleApps,
+ base::Unretained(this)));
+}
+
+void GoogleAppsHandler::HandleRejectGoogleApps(const base::ListValue* args) {
+ UMA_HISTOGRAM_ENUMERATION(kGoogleAppsInteractionHistogram,
+ GoogleAppsInteraction::kNoThanks,
+ GoogleAppsInteraction::kCount);
+}
+
+void GoogleAppsHandler::HandleAddGoogleApps(const base::ListValue* args) {
+ // Add bookmarks for all selected apps.
+ int bookmarkIndex = 0;
+ for (size_t i = 0; i < (size_t)GoogleApps::kCount; ++i) {
+ bool selected = false;
+ CHECK(args->GetBoolean(i, &selected));
+ if (selected) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "FirstRun.NewUserExperience.GoogleAppsSelection", (GoogleApps)i,
+ GoogleApps::kCount);
+ GURL app_url = GURL(kGoogleAppUrls[i]);
+ bookmark_model_->AddURL(bookmark_model_->bookmark_bar_node(),
+ bookmarkIndex++,
+ base::ASCIIToUTF16(kGoogleAppNames[i]), app_url);
+
+ // Preload the favicon cache with Chrome-bundled images. Otherwise, the
+ // pre-populated bookmarks don't have favicons and look bad. Favicons are
+ // updated automatically when a user visits a site.
+ favicon_service_->MergeFavicon(
+ app_url, app_url, favicon_base::IconType::kFavicon,
+ ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
+ kGoogleAppIcons[i]),
+ gfx::Size(kGoogleAppIconSize, kGoogleAppIconSize));
+ }
+ }
+
+ // Enable bookmark bar.
+ prefs_->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
+
+ // Wait to show bookmark bar.
+ // TODO(hcarmona): Any advice here would be helpful.
+
+ // Show bookmark bubble.
+ ShowPromoDelegate::CreatePromoDelegate(
+ IDS_NUX_GOOGLE_APPS_DESCRIPTION_PROMO_BUBBLE)
+ ->ShowForNode(bookmark_model_->bookmark_bar_node()->GetChild(0));
+
+ UMA_HISTOGRAM_ENUMERATION(kGoogleAppsInteractionHistogram,
+ GoogleAppsInteraction::kGetStarted,
+ GoogleAppsInteraction::kCount);
+}
+
+void GoogleAppsHandler::AddSources(content::WebUIDataSource* html_source) {
+ // Localized strings.
+ html_source->AddLocalizedString("noThanks", IDS_NO_THANKS);
+ html_source->AddLocalizedString("getStarted",
+ IDS_NUX_GOOGLE_APPS_GET_STARTED);
+ html_source->AddLocalizedString("nuxDescription",
+ IDS_NUX_GOOGLE_APPS_DESCRIPTION);
+
+ // Add required resources.
+ html_source->AddResourcePath("apps", IDR_NUX_GOOGLE_APPS_HTML);
+ html_source->AddResourcePath("apps/nux_google_apps.js",
+ IDR_NUX_GOOGLE_APPS_JS);
+
+ html_source->AddResourcePath("apps/nux_google_apps_proxy.html",
+ IDR_NUX_GOOGLE_APPS_PROXY_HTML);
+ html_source->AddResourcePath("apps/nux_google_apps_proxy.js",
+ IDR_NUX_GOOGLE_APPS_PROXY_JS);
+
+ html_source->AddResourcePath("apps/apps_chooser.html",
+ IDR_NUX_GOOGLE_APPS_CHOOSER_HTML);
+ html_source->AddResourcePath("apps/apps_chooser.js",
+ IDR_NUX_GOOGLE_APPS_CHOOSER_JS);
+
+ // Add icons
+ html_source->AddResourcePath("apps/chrome_store_1x.png",
+ IDR_NUX_GOOGLE_APPS_CHROME_STORE_1X);
+ html_source->AddResourcePath("apps/chrome_store_2x.png",
+ IDR_NUX_GOOGLE_APPS_CHROME_STORE_2X);
+ html_source->AddResourcePath("apps/gmail_1x.png",
+ IDR_NUX_GOOGLE_APPS_GMAIL_1X);
+ html_source->AddResourcePath("apps/gmail_2x.png",
+ IDR_NUX_GOOGLE_APPS_GMAIL_2X);
+ html_source->AddResourcePath("apps/maps_1x.png", IDR_NUX_GOOGLE_APPS_MAPS_1X);
+ html_source->AddResourcePath("apps/maps_2x.png", IDR_NUX_GOOGLE_APPS_MAPS_2X);
+ html_source->AddResourcePath("apps/news_1x.png", IDR_NUX_GOOGLE_APPS_NEWS_1X);
+ html_source->AddResourcePath("apps/news_2x.png", IDR_NUX_GOOGLE_APPS_NEWS_2X);
+ html_source->AddResourcePath("apps/translate_1x.png",
+ IDR_NUX_GOOGLE_APPS_TRANSLATE_1X);
+ html_source->AddResourcePath("apps/translate_2x.png",
+ IDR_NUX_GOOGLE_APPS_TRANSLATE_2X);
+ html_source->AddResourcePath("apps/youtube_1x.png",
+ IDR_NUX_GOOGLE_APPS_YOUTUBE_1X);
+ html_source->AddResourcePath("apps/youtube_2x.png",
+ IDR_NUX_GOOGLE_APPS_YOUTUBE_2X);
+}
+
+} // namespace nux_google_apps \ No newline at end of file
diff --git a/chromium/components/nux_google_apps/google_apps_handler.h b/chromium/components/nux_google_apps/google_apps_handler.h
new file mode 100644
index 00000000000..4901740da4c
--- /dev/null
+++ b/chromium/components/nux_google_apps/google_apps_handler.h
@@ -0,0 +1,71 @@
+// 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_NUX_GOOGLE_APPS_GOOGLE_APPS_HANDLER_H_
+#define COMPONENTS_NUX_GOOGLE_APPS_GOOGLE_APPS_HANDLER_H_
+
+#include "base/macros.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class PrefService;
+
+namespace bookmarks {
+class BookmarkModel;
+} // namespace bookmarks
+
+namespace content {
+class WebUIDataSource;
+} // namespace content
+
+namespace favicon {
+class FaviconService;
+} // namespace favicon
+
+namespace nux_google_apps {
+
+extern const char* kGoogleAppsInteractionHistogram;
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class GoogleAppsInteraction {
+ kPromptShown = 0,
+ kNoThanks = 1,
+ kGetStarted = 2,
+ kCount,
+};
+
+class GoogleAppsHandler : public content::WebUIMessageHandler {
+ public:
+ GoogleAppsHandler(PrefService* prefs,
+ favicon::FaviconService* favicon_service,
+ bookmarks::BookmarkModel* bookmark_model);
+ ~GoogleAppsHandler() override;
+
+ // WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // Callbacks for JS APIs.
+ void HandleRejectGoogleApps(const base::ListValue* args);
+ void HandleAddGoogleApps(const base::ListValue* args);
+
+ // Adds webui sources.
+ static void AddSources(content::WebUIDataSource* html_source);
+
+ private:
+ // Weak reference.
+ PrefService* prefs_;
+
+ // Weak reference.
+ favicon::FaviconService* favicon_service_;
+
+ // Weak reference.
+ bookmarks::BookmarkModel* bookmark_model_;
+
+ DISALLOW_COPY_AND_ASSIGN(GoogleAppsHandler);
+};
+
+} // namespace nux_google_apps
+
+#endif // COMPONENTS_NUX_GOOGLE_APPS_GOOGLE_APPS_HANDLER_H_
diff --git a/chromium/components/nux_google_apps/resources/BUILD.gn b/chromium/components/nux_google_apps/resources/BUILD.gn
new file mode 100644
index 00000000000..c2ce56522bf
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/BUILD.gn
@@ -0,0 +1,27 @@
+# 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.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+js_type_check("closure_compile") {
+ deps = [
+ ":apps_chooser",
+ ":nux_google_apps",
+ ]
+}
+
+js_library("apps_chooser") {
+ deps = []
+}
+
+js_library("nux_google_apps") {
+ deps = [
+ ":apps_chooser",
+ ":nux_google_apps_proxy",
+ ]
+}
+
+js_library("nux_google_apps_proxy") {
+ deps = []
+}
diff --git a/chromium/components/nux_google_apps/resources/apps_chooser.html b/chromium/components/nux_google_apps/resources/apps_chooser.html
new file mode 100644
index 00000000000..437954a6e50
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/apps_chooser.html
@@ -0,0 +1,103 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+
+<dom-module id="apps-chooser">
+ <template>
+ <style>
+ :host {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .app-icon {
+ -webkit-margin-end: 16px;
+ -webkit-margin-start: 4px;
+ }
+
+ iron-icon {
+ -webkit-margin-end: 48px;
+ }
+
+ [active] iron-icon[icon="cr:check"] {
+ --iron-icon-fill-color: var(--google-blue-600);
+ opacity: unset;
+ }
+
+ iron-icon[icon="cr:check"] {
+ opacity: 0;
+ }
+
+ .gmail {
+ content: -webkit-image-set(
+ url(chrome://welcome/apps/gmail_1x.png) 1x,
+ url(chrome://welcome/apps/gmail_2x.png) 2x);
+ }
+
+ .youtube {
+ content: -webkit-image-set(
+ url(chrome://welcome/apps/youtube_1x.png) 1x,
+ url(chrome://welcome/apps/youtube_2x.png) 2x);
+ }
+
+ .maps {
+ content: -webkit-image-set(
+ url(chrome://welcome/apps/maps_1x.png) 1x,
+ url(chrome://welcome/apps/maps_2x.png) 2x);
+ }
+
+ .translate {
+ content: -webkit-image-set(
+ url(chrome://welcome/apps/translate_1x.png) 1x,
+ url(chrome://welcome/apps/translate_2x.png) 2x);
+ }
+
+ .news {
+ content: -webkit-image-set(
+ url(chrome://welcome/apps/news_1x.png) 1x,
+ url(chrome://welcome/apps/news_2x.png) 2x);
+ }
+
+ .chrome_store {
+ content: -webkit-image-set(
+ url(chrome://welcome/apps/chrome_store_1x.png) 1x,
+ url(chrome://welcome/apps/chrome_store_2x.png) 2x);
+ }
+
+ paper-button:first-of-type {
+ border-top: unset;
+ }
+
+ paper-button {
+ border-radius: 0;
+ border-top: var(--cr-section_-_border-top);
+ display: flex;
+ margin: 0;
+ padding: 12px 0;
+ text-transform: unset;
+ }
+
+ paper-button:not([raised]).keyboard-focus {
+ outline-width: unset;
+ font-weight: unset;
+ }
+
+ .app-name {
+ flex: 1;
+ }
+ </style>
+
+ <template is="dom-repeat" items="[[appList]]">
+ <paper-button toggles noink
+ active$="[[item.selected]]" on-click="onAppClick_">
+ <div class$="[[item.icon]] app-icon"></div>
+ <div class="app-name">[[item.name]]</div>
+ <iron-icon icon="cr:check"></iron-icon>
+ </paper-button>
+ </template>
+ </template>
+ <script src="apps_chooser.js"></script>
+</dom-module>
diff --git a/chromium/components/nux_google_apps/resources/apps_chooser.js b/chromium/components/nux_google_apps/resources/apps_chooser.js
new file mode 100644
index 00000000000..87c50fe9584
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/apps_chooser.js
@@ -0,0 +1,93 @@
+// 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.
+
+/**
+ * @const
+ */
+var nux_google_apps = nux_google_apps || {};
+
+/**
+ * @typedef {{
+ * item: !{
+ * name: string,
+ * icon: string,
+ * selected: boolean,
+ * },
+ * set: function(string, boolean):void
+ * }}
+ */
+nux_google_apps.AppsArrayModel;
+
+Polymer({
+ is: 'apps-chooser',
+ properties: {
+ // TODO(hcarmona): Get this list dynamically.
+ appList: {
+ type: Array,
+ value: function() {
+ return [
+ {
+ name: 'Gmail',
+ icon: 'gmail',
+ selected: true,
+ },
+ {
+ name: 'YouTube',
+ icon: 'youtube',
+ selected: true,
+ },
+ {
+ name: 'Maps',
+ icon: 'maps',
+ selected: true,
+ },
+ {
+ name: 'Translate',
+ icon: 'translate',
+ selected: true,
+ },
+ {
+ name: 'News',
+ icon: 'news',
+ selected: true,
+ },
+ {
+ name: 'Chrome Web Store',
+ icon: 'chrome_store',
+ selected: true,
+ },
+ ];
+ },
+ },
+
+ hasAppsSelected: {
+ type: Boolean,
+ notify: true,
+ value: true,
+ }
+ },
+
+ /**
+ * Returns an array of booleans for each selected app.
+ * @return {Array<boolean>}
+ */
+ getSelectedAppList() {
+ return this.appList.map(a => a.selected)
+ },
+
+ /**
+ * Handle toggling the apps selected.
+ * @param {!{model: !nux_google_apps.AppsArrayModel}} e
+ * @private
+ */
+ onAppClick_: function(e) {
+ e.model.set('item.selected', !e.model.item.selected);
+ this.hasAppsSelected = this.computeHasAppsSelected_();
+ },
+
+ /** @private {boolean} */
+ computeHasAppsSelected_: function() {
+ return this.appList.some(a => a.selected);
+ },
+});
diff --git a/chromium/components/nux_google_apps/resources/chrome_store_24dp_1x.png b/chromium/components/nux_google_apps/resources/chrome_store_24dp_1x.png
new file mode 100644
index 00000000000..9ffad9e46a2
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/chrome_store_24dp_1x.png
Binary files differ
diff --git a/chromium/components/nux_google_apps/resources/chrome_store_24dp_2x.png b/chromium/components/nux_google_apps/resources/chrome_store_24dp_2x.png
new file mode 100644
index 00000000000..4307c077c65
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/chrome_store_24dp_2x.png
Binary files differ
diff --git a/chromium/components/nux_google_apps/resources/gmail_24dp_1x.png b/chromium/components/nux_google_apps/resources/gmail_24dp_1x.png
new file mode 100644
index 00000000000..220e974470a
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/gmail_24dp_1x.png
Binary files differ
diff --git a/chromium/components/nux_google_apps/resources/gmail_24dp_2x.png b/chromium/components/nux_google_apps/resources/gmail_24dp_2x.png
new file mode 100644
index 00000000000..2ab3dbdc1d1
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/gmail_24dp_2x.png
Binary files differ
diff --git a/chromium/components/nux_google_apps/resources/maps_24dp_1x.png b/chromium/components/nux_google_apps/resources/maps_24dp_1x.png
new file mode 100644
index 00000000000..110bff3e285
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/maps_24dp_1x.png
Binary files differ
diff --git a/chromium/components/nux_google_apps/resources/maps_24dp_2x.png b/chromium/components/nux_google_apps/resources/maps_24dp_2x.png
new file mode 100644
index 00000000000..041a617a9d5
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/maps_24dp_2x.png
Binary files differ
diff --git a/chromium/components/nux_google_apps/resources/news_24dp_1x.png b/chromium/components/nux_google_apps/resources/news_24dp_1x.png
new file mode 100644
index 00000000000..6433d5bc54f
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/news_24dp_1x.png
Binary files differ
diff --git a/chromium/components/nux_google_apps/resources/news_24dp_2x.png b/chromium/components/nux_google_apps/resources/news_24dp_2x.png
new file mode 100644
index 00000000000..cb3b8f886af
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/news_24dp_2x.png
Binary files differ
diff --git a/chromium/components/nux_google_apps/resources/nux_google_apps.html b/chromium/components/nux_google_apps/resources/nux_google_apps.html
new file mode 100644
index 00000000000..56f427a838a
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/nux_google_apps.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
+<meta charset="utf-8">
+<title>$i18n{headerText}</title>
+
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="chrome://welcome/apps/apps_chooser.html">
+<link rel="import" href="chrome://welcome/apps/nux_google_apps_proxy.html">
+
+<dom-module id="nux-google-apps">
+ <template>
+ <style include="paper-button-style">
+ body {
+ margin: 0;
+ }
+
+ .apps-ask {
+ margin-left: auto;
+ margin-right: auto;
+ margin-top: 116px;
+ max-width: 568px;
+ }
+
+ .chrome-logo {
+ content: -webkit-image-set(
+ url('chrome://welcome/logo.png') 1x,
+ url('chrome://welcome/logo2x.png') 2x);
+ height: 40px;
+ margin-bottom: 36px;
+ width: 40px;
+ }
+
+ h1 {
+ color: #202124;
+ font-size: 28px;
+ opacity: .8;
+ margin-bottom: 16px;
+ }
+
+ .description {
+ color: #5f6368;
+ font-size: 16px;
+ margin-bottom: 24px;
+ }
+
+ apps-chooser {
+ color: #202124;
+ font-size: 14px;
+ margin-bottom: 48px;
+ }
+
+ paper-button {
+ font-size: 14px;
+ }
+
+ .button-bar {
+ display: flex;
+ justify-content: space-between;
+ }
+ </style>
+
+ <div class="apps-ask">
+ <div class="chrome-logo"></div>
+ <h1>$i18n{headerText}</h1>
+ <div class="description">$i18n{nuxDescription}</div>
+
+ <apps-chooser id="appChooser" has-apps-selected="{{hasAppsSelected_}}">
+ </apps-chooser>
+
+ <div class="button-bar">
+ <paper-button on-click="onNoThanksClicked_">
+ $i18n{noThanks}
+ </paper-button>
+ <paper-button class="action-button" disabled$="[[!hasAppsSelected_]]"
+ on-click="onGetStartedClicked_">$i18n{getStarted}</paper-button>
+ </div>
+ </div>
+ </template>
+ <script src="apps/nux_google_apps.js"></script>
+</dom-module>
+
+<nux-google-apps></nux-google-apps>
+<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
+
+</html> \ No newline at end of file
diff --git a/chromium/components/nux_google_apps/resources/nux_google_apps.js b/chromium/components/nux_google_apps/resources/nux_google_apps.js
new file mode 100644
index 00000000000..911ec744765
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/nux_google_apps.js
@@ -0,0 +1,25 @@
+// 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.
+
+Polymer({
+ is: 'nux-google-apps',
+
+ properties: {
+ /** @private */
+ hasAppsSelected_: Boolean,
+ },
+
+ /** @private */
+ onNoThanksClicked_: function() {
+ chrome.send('rejectGoogleApps');
+ window.location.replace('chrome://newtab');
+ },
+
+ /** @private */
+ onGetStartedClicked_: function() {
+ let selectedApps = this.$.appChooser.getSelectedAppList();
+ nux.NuxGoogleAppsProxyImpl.getInstance().addGoogleApps(selectedApps);
+ window.location.replace('chrome://newtab');
+ },
+});
diff --git a/chromium/components/nux_google_apps/resources/nux_google_apps_proxy.html b/chromium/components/nux_google_apps/resources/nux_google_apps_proxy.html
new file mode 100644
index 00000000000..604b825e5e1
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/nux_google_apps_proxy.html
@@ -0,0 +1,2 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<script src="nux_google_apps_proxy.js"></script> \ No newline at end of file
diff --git a/chromium/components/nux_google_apps/resources/nux_google_apps_proxy.js b/chromium/components/nux_google_apps/resources/nux_google_apps_proxy.js
new file mode 100644
index 00000000000..48d63ceb82d
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/nux_google_apps_proxy.js
@@ -0,0 +1,29 @@
+// 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.
+
+cr.define('nux', function() {
+ /** @interface */
+ class NuxGoogleAppsProxy {
+ /**
+ * Adds the selected apps to the bookmark bar.
+ * @param {!Array<boolean>} selectedApps
+ */
+ addGoogleApps(selectedApps) {}
+ }
+
+ /** @implements {NuxGoogleAppsProxy} */
+ class NuxGoogleAppsProxyImpl {
+ /** @override */
+ addGoogleApps(selectedApps) {
+ chrome.send('addGoogleApps', selectedApps);
+ }
+ }
+
+ cr.addSingletonGetter(NuxGoogleAppsProxyImpl);
+
+ return {
+ NuxGoogleAppsProxy: NuxGoogleAppsProxy,
+ NuxGoogleAppsProxyImpl: NuxGoogleAppsProxyImpl,
+ };
+}); \ No newline at end of file
diff --git a/chromium/components/nux_google_apps/resources/translate_24dp_1x.png b/chromium/components/nux_google_apps/resources/translate_24dp_1x.png
new file mode 100644
index 00000000000..023417f6f46
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/translate_24dp_1x.png
Binary files differ
diff --git a/chromium/components/nux_google_apps/resources/translate_24dp_2x.png b/chromium/components/nux_google_apps/resources/translate_24dp_2x.png
new file mode 100644
index 00000000000..a03417ca03d
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/translate_24dp_2x.png
Binary files differ
diff --git a/chromium/components/nux_google_apps/resources/youtube_24dp_1x.png b/chromium/components/nux_google_apps/resources/youtube_24dp_1x.png
new file mode 100644
index 00000000000..5a346af934d
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/youtube_24dp_1x.png
Binary files differ
diff --git a/chromium/components/nux_google_apps/resources/youtube_24dp_2x.png b/chromium/components/nux_google_apps/resources/youtube_24dp_2x.png
new file mode 100644
index 00000000000..4d40711765b
--- /dev/null
+++ b/chromium/components/nux_google_apps/resources/youtube_24dp_2x.png
Binary files differ
diff --git a/chromium/components/nux_google_apps_strings.grdp b/chromium/components/nux_google_apps_strings.grdp
new file mode 100644
index 00000000000..1dd1bf245a0
--- /dev/null
+++ b/chromium/components/nux_google_apps_strings.grdp
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+ <message name="IDS_NUX_GOOGLE_APPS_GET_STARTED" desc="Message for a confirmation button that will add the selected apps from a list.">
+ Get started
+ </message>
+ <message name="IDS_NUX_GOOGLE_APPS_DESCRIPTION" desc="Description of what selecting apps and pressing 'Get started' will do.">
+ Get quick access to your favorite Google Apps
+ </message>
+ <message name="IDS_NUX_GOOGLE_APPS_DESCRIPTION_PROMO_BUBBLE" desc="Text shown when Google Apps have been added to highlight where bookmarks have been placed.">
+ Open apps easily with bookmarks
+ </message>
+</grit-part>
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 edb50247a1f..407811c62ec 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
@@ -5,6 +5,8 @@
#include "components/offline_items_collection/core/android/offline_content_aggregator_bridge.h"
#include <memory>
+#include <utility>
+#include <vector>
#include "base/android/callback_android.h"
#include "base/android/jni_string.h"
@@ -52,14 +54,14 @@ void GetVisualsForItemHelperCallback(
void RunGetAllItemsCallback(const base::android::JavaRef<jobject>& j_callback,
const std::vector<OfflineItem>& items) {
JNIEnv* env = AttachCurrentThread();
- RunCallbackAndroid(j_callback,
- OfflineItemBridge::CreateOfflineItemList(env, items));
+ RunObjectCallbackAndroid(
+ j_callback, OfflineItemBridge::CreateOfflineItemList(env, items));
}
void RunGetItemByIdCallback(const base::android::JavaRef<jobject>& j_callback,
const base::Optional<OfflineItem>& item) {
JNIEnv* env = AttachCurrentThread();
- RunCallbackAndroid(
+ RunObjectCallbackAndroid(
j_callback, item.has_value()
? OfflineItemBridge::CreateOfflineItem(env, item.value())
: nullptr);
diff --git a/chromium/components/offline_items_collection/core/fail_state.h b/chromium/components/offline_items_collection/core/fail_state.h
index 2ca2b7d1061..5d615842971 100644
--- a/chromium/components/offline_items_collection/core/fail_state.h
+++ b/chromium/components/offline_items_collection/core/fail_state.h
@@ -17,6 +17,9 @@ enum class FailState {
// connection.
};
+// Implemented for testing only. See test_support/offline_item_test_support.cc.
+std::ostream& operator<<(std::ostream& os, FailState state);
+
} // namespace offline_items_collection
#endif // COMPONENTS_OFFLINE_ITEMS_COLLECTION_CORE_FAIL_STATE_H_
diff --git a/chromium/components/offline_items_collection/core/offline_content_aggregator_unittest.cc b/chromium/components/offline_items_collection/core/offline_content_aggregator_unittest.cc
index d9e49ba6361..58648897446 100644
--- a/chromium/components/offline_items_collection/core/offline_content_aggregator_unittest.cc
+++ b/chromium/components/offline_items_collection/core/offline_content_aggregator_unittest.cc
@@ -6,6 +6,7 @@
#include <map>
+#include "base/test/bind_test_util.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/offline_items_collection/core/offline_item.h"
@@ -117,7 +118,6 @@ TEST_F(OfflineContentAggregatorTest, QueryingItemsWith2Providers) {
ScopedMockOfflineContentProvider provider1("1", &aggregator_);
ScopedMockOfflineContentProvider provider2("2", &aggregator_);
- OfflineContentProvider::OfflineItemList empty;
OfflineContentProvider::OfflineItemList items1;
items1.push_back(OfflineItem(ContentId("1", "A")));
items1.push_back(OfflineItem(ContentId("1", "B")));
diff --git a/chromium/components/offline_items_collection/core/offline_item.h b/chromium/components/offline_items_collection/core/offline_item.h
index 80aec976f26..0426be02f99 100644
--- a/chromium/components/offline_items_collection/core/offline_item.h
+++ b/chromium/components/offline_items_collection/core/offline_item.h
@@ -86,6 +86,9 @@ struct OfflineItem {
bool operator==(const OfflineItem& offline_item) const;
+ // Note: please update test_support/offline_item_test_support.cc
+ // when adding members here.
+
// The id of this OfflineItem. Used to identify this item across all relevant
// systems.
ContentId id;
@@ -186,6 +189,9 @@ struct OfflineItem {
bool is_dangerous;
};
+// Implemented for test-only. See test_support/offline_item_test_support.cc.
+std::ostream& operator<<(std::ostream& os, const OfflineItem& item);
+
// This struct holds any potentially expensive visuals for an OfflineItem. If
// the front end requires the visuals it will ask for them through the
// OfflineContentProvider interface asynchronously to give the backend time to
diff --git a/chromium/components/offline_items_collection/core/offline_item_filter.h b/chromium/components/offline_items_collection/core/offline_item_filter.h
index 3bdb64b242a..26c91b5d31c 100644
--- a/chromium/components/offline_items_collection/core/offline_item_filter.h
+++ b/chromium/components/offline_items_collection/core/offline_item_filter.h
@@ -5,13 +5,14 @@
#ifndef COMPONENTS_OFFLINE_ITEMS_COLLECTION_CORE_OFFLINE_ITEM_FILTER_H_
#define COMPONENTS_OFFLINE_ITEMS_COLLECTION_CORE_OFFLINE_ITEM_FILTER_H_
+#include <iosfwd>
+
namespace offline_items_collection {
// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.offline_items_collection
enum OfflineItemFilter {
- FILTER_ALL = 0,
- FILTER_PAGE,
+ FILTER_PAGE = 0,
FILTER_VIDEO,
FILTER_AUDIO,
FILTER_IMAGE,
@@ -22,6 +23,9 @@ enum OfflineItemFilter {
FILTER_BOUNDARY,
};
+// Implemented for test-only. See test_support/offline_item_test_support.cc.
+std::ostream& operator<<(std::ostream& os, OfflineItemFilter state);
+
} // namespace offline_items_collection
#endif // COMPONENTS_OFFLINE_ITEMS_COLLECTION_CORE_OFFLINE_ITEM_FILTER_H_
diff --git a/chromium/components/offline_items_collection/core/offline_item_state.h b/chromium/components/offline_items_collection/core/offline_item_state.h
index db23a527fb1..780da574d56 100644
--- a/chromium/components/offline_items_collection/core/offline_item_state.h
+++ b/chromium/components/offline_items_collection/core/offline_item_state.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_OFFLINE_ITEMS_COLLECTION_CORE_OFFLINE_ITEM_STATE_H_
#define COMPONENTS_OFFLINE_ITEMS_COLLECTION_CORE_OFFLINE_ITEM_STATE_H_
+#include <iosfwd>
+
namespace offline_items_collection {
// A Java counterpart will be generated for this enum.
@@ -21,6 +23,9 @@ enum OfflineItemState {
MAX_DOWNLOAD_STATE,
};
+// Implemented for testing only. See test_support/offline_item_test_support.cc.
+std::ostream& operator<<(std::ostream& os, const OfflineItemState& state);
+
} // namespace offline_items_collection
#endif // COMPONENTS_OFFLINE_ITEMS_COLLECTION_CORE_OFFLINE_ITEM_STATE_H_
diff --git a/chromium/components/offline_items_collection/core/pending_state.h b/chromium/components/offline_items_collection/core/pending_state.h
index 5e83a229a3e..51ab7d8cc90 100644
--- a/chromium/components/offline_items_collection/core/pending_state.h
+++ b/chromium/components/offline_items_collection/core/pending_state.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_OFFLINE_ITEMS_COLLECTION_CORE_PENDING_STATE_H_
#define COMPONENTS_OFFLINE_ITEMS_COLLECTION_CORE_PENDING_STATE_H_
+#include <iosfwd>
+
namespace offline_items_collection {
// A Java counterpart will be generated for this enum.
@@ -17,6 +19,9 @@ enum class PendingState {
// is currently being downloaded.
};
+// Implemented for testing only. See test_support/offline_item_test_support.cc.
+std::ostream& operator<<(std::ostream& os, PendingState state);
+
} // namespace offline_items_collection
#endif // COMPONENTS_OFFLINE_ITEMS_COLLECTION_CORE_PENDING_STATE_H_
diff --git a/chromium/components/offline_items_collection/core/test_support/BUILD.gn b/chromium/components/offline_items_collection/core/test_support/BUILD.gn
index e2314388d3e..0334a7a62fe 100644
--- a/chromium/components/offline_items_collection/core/test_support/BUILD.gn
+++ b/chromium/components/offline_items_collection/core/test_support/BUILD.gn
@@ -2,14 +2,13 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-source_set("test_support") {
- visibility = [ "//components/offline_items_collection/core:unit_tests" ]
-
+static_library("test_support") {
testonly = true
sources = [
"mock_offline_content_provider.cc",
"mock_offline_content_provider.h",
+ "offline_item_test_support.cc",
"scoped_mock_offline_content_provider.cc",
"scoped_mock_offline_content_provider.h",
]
diff --git a/chromium/components/offline_items_collection/core/test_support/offline_item_test_support.cc b/chromium/components/offline_items_collection/core/test_support/offline_item_test_support.cc
new file mode 100644
index 00000000000..6a221a9d8c7
--- /dev/null
+++ b/chromium/components/offline_items_collection/core/test_support/offline_item_test_support.cc
@@ -0,0 +1,120 @@
+// 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/offline_items_collection/core/offline_item.h"
+
+#include <iostream>
+
+namespace offline_items_collection {
+
+// All of these methods are not provided in core so that they can't be
+// accidentally called and linked into Chrome. The declarations are provided
+// in the core headers to avoid ODR violation that can occur if, for instance,
+// one test includes these operators and one test does not.
+
+std::ostream& operator<<(std::ostream& os, const OfflineItem& item) {
+ os << "OfflineItem(";
+ os << "id: " << item.id.name_space << "." << item.id.id;
+ os << ", title: " << item.title;
+ os << ", description: " << item.description;
+ os << ", filter: " << item.filter;
+ os << ", is_transient: " << item.is_transient;
+ os << ", is_suggested: " << item.is_suggested;
+ os << ", is_accelerated: " << item.is_accelerated;
+ os << ", total_size_bytes: " << item.total_size_bytes;
+ os << ", externally_removed: " << item.externally_removed;
+ os << ", creation_time: " << item.creation_time;
+ os << ", last_accessed_time: " << item.last_accessed_time;
+ os << ", is_openable: " << item.is_openable;
+ os << ", file_path: " << item.file_path;
+ os << ", mime_type: " << item.mime_type;
+ os << ", page_url: " << item.page_url;
+ os << ", original_url: " << item.original_url;
+ os << ", is_off_the_record: " << item.is_off_the_record;
+ os << ", state: " << item.state;
+ os << ", fail_state: " << item.fail_state;
+ os << ", pending_state: " << item.pending_state;
+ os << ", is_resumable: " << item.is_resumable;
+ os << ", allow_metered: " << item.allow_metered;
+ os << ", received_bytes: " << item.received_bytes;
+ os << ", progress: " << item.progress.value;
+ if (item.progress.max)
+ os << "/" << item.progress.max.value();
+ os << ", time_remaining_ms: " << item.time_remaining_ms;
+ os << ", is_dangerous: " << item.is_dangerous;
+ os << ")";
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const OfflineItemState& state) {
+ switch (state) {
+ case IN_PROGRESS:
+ return os << "IN_PROGRESS";
+ case PENDING:
+ return os << "PENDING";
+ case COMPLETE:
+ return os << "COMPLETE";
+ case CANCELLED:
+ return os << "CANCELLED";
+ case INTERRUPTED:
+ return os << "INTERRUPTED";
+ case FAILED:
+ return os << "FAILED";
+ case PAUSED:
+ return os << "PAUSED";
+ case MAX_DOWNLOAD_STATE:
+ return os << "MAX_DOWNLOAD_STATE";
+ }
+ CHECK(false) << "state=" << static_cast<int>(state);
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, FailState state) {
+ switch (state) {
+ case FailState::NO_FAILURE:
+ return os << "NO_FAILURE";
+ case FailState::CANNOT_DOWNLOAD:
+ return os << "CANNOT_DOWNLOAD";
+ case FailState::NETWORK_INSTABILITY:
+ return os << "NETWORK_INSTABILITY";
+ }
+ CHECK(false) << "state=" << static_cast<int>(state);
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, PendingState state) {
+ switch (state) {
+ case PendingState::NOT_PENDING:
+ return os << "NOT_PENDING";
+ case PendingState::PENDING_NETWORK:
+ return os << "PENDING_NETWORK";
+ case PendingState::PENDING_ANOTHER_DOWNLOAD:
+ return os << "PENDING_ANOTHER_DOWNLOAD";
+ }
+ CHECK(false) << "state=" << static_cast<int>(state);
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, OfflineItemFilter state) {
+ switch (state) {
+ case FILTER_PAGE:
+ return os << "FILTER_PAGE";
+ case FILTER_VIDEO:
+ return os << "FILTER_VIDEO";
+ case FILTER_AUDIO:
+ return os << "FILTER_AUDIO";
+ case FILTER_IMAGE:
+ return os << "FILTER_IMAGE";
+ case FILTER_DOCUMENT:
+ return os << "FILTER_DOCUMENT";
+ case FILTER_OTHER:
+ return os << "FILTER_OTHER";
+ case FILTER_BOUNDARY:
+ return os << "FILTER_BOUNDARY";
+ }
+ CHECK(false) << "state=" << static_cast<int>(state);
+ return os;
+}
+
+} // namespace offline_items_collection
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 d59cec9c4f4..ed7a6be5651 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
@@ -11,6 +11,10 @@ namespace background_loader {
BackgroundLoaderContents::BackgroundLoaderContents(
content::BrowserContext* browser_context)
: browser_context_(browser_context) {
+ // It is very important that we create the web contents with
+ // CreateParams::initially_hidden == false, and that we never change the
+ // visibility after that. If we did change it, then background throttling
+ // could kill the background offliner while it was running.
web_contents_ = content::WebContents::Create(
content::WebContents::CreateParams(browser_context_));
web_contents_->SetAudioMuted(true);
@@ -109,9 +113,9 @@ bool BackgroundLoaderContents::ShouldBlockMediaRequest(const GURL& url) {
void BackgroundLoaderContents::RequestMediaAccessPermission(
content::WebContents* contents,
const content::MediaStreamRequest& request,
- const content::MediaResponseCallback& callback) {
+ content::MediaResponseCallback callback) {
// No permissions granted, act as if dismissed.
- callback.Run(
+ std::move(callback).Run(
content::MediaStreamDevices(),
content::MediaStreamRequestResult::MEDIA_DEVICE_PERMISSION_DISMISSED,
std::unique_ptr<content::MediaStreamUI>());
diff --git a/chromium/components/offline_pages/content/background_loader/background_loader_contents.h b/chromium/components/offline_pages/content/background_loader/background_loader_contents.h
index 0cfdf259e46..4b55d90239d 100644
--- a/chromium/components/offline_pages/content/background_loader/background_loader_contents.h
+++ b/chromium/components/offline_pages/content/background_loader/background_loader_contents.h
@@ -84,7 +84,7 @@ class BackgroundLoaderContents : public content::WebContentsDelegate {
void RequestMediaAccessPermission(
content::WebContents* contents,
const content::MediaStreamRequest& request,
- const content::MediaResponseCallback& callback) override;
+ content::MediaResponseCallback callback) override;
bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,
const GURL& security_origin,
content::MediaStreamType type) override;
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 6910403a96c..26dabb5e990 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
@@ -108,8 +108,8 @@ TEST_F(BackgroundLoaderContentsTest, DoesNotFocusAfterCrash) {
TEST_F(BackgroundLoaderContentsTest, CannotDownloadNoDelegate) {
contents()->CanDownload(
GURL::EmptyGURL(), std::string(),
- base::Bind(&BackgroundLoaderContentsTest::DownloadCallback,
- base::Unretained(this)));
+ base::BindRepeating(&BackgroundLoaderContentsTest::DownloadCallback,
+ base::Unretained(this)));
WaitForSignal();
ASSERT_FALSE(download());
ASSERT_FALSE(can_download_delegate_called());
@@ -119,8 +119,8 @@ TEST_F(BackgroundLoaderContentsTest, CanDownload_DelegateCalledWhenSet) {
SetDelegate();
contents()->CanDownload(
GURL::EmptyGURL(), std::string(),
- base::Bind(&BackgroundLoaderContentsTest::DownloadCallback,
- base::Unretained(this)));
+ base::BindRepeating(&BackgroundLoaderContentsTest::DownloadCallback,
+ base::Unretained(this)));
WaitForSignal();
ASSERT_TRUE(download());
ASSERT_TRUE(can_download_delegate_called());
@@ -161,8 +161,8 @@ TEST_F(BackgroundLoaderContentsTest, DoesNotGiveMediaAccessPermission) {
false /* disable_local_echo */);
contents()->RequestMediaAccessPermission(
nullptr /* contents */, request /* request */,
- base::Bind(&BackgroundLoaderContentsTest::MediaAccessCallback,
- base::Unretained(this)));
+ base::BindRepeating(&BackgroundLoaderContentsTest::MediaAccessCallback,
+ base::Unretained(this)));
WaitForSignal();
// No devices allowed.
ASSERT_TRUE(devices().empty());
diff --git a/chromium/components/offline_pages/content/renovations/render_frame_script_injector.cc b/chromium/components/offline_pages/content/renovations/render_frame_script_injector.cc
index 78111112b11..e9d1ab841d2 100644
--- a/chromium/components/offline_pages/content/renovations/render_frame_script_injector.cc
+++ b/chromium/components/offline_pages/content/renovations/render_frame_script_injector.cc
@@ -29,20 +29,20 @@ void RenderFrameScriptInjector::Inject(base::string16 script,
// Must create proxy callback since ExecuteJavaScriptInIsolatedWorld
// takes a |const base::Value*| argument instead of a |const
// base::Value&|.
- base::RepeatingCallback<void(const base::Value*)> proxy_callback =
- base::BindRepeating(
- [](ResultCallback user_callback, const base::Value* result) {
- base::Value new_result = result ? result->Clone() : base::Value();
- if (user_callback)
- user_callback.Run(new_result);
- },
- callback);
+ base::OnceCallback<void(const base::Value*)> proxy_callback = base::BindOnce(
+ [](ResultCallback user_callback, const base::Value* result) {
+ base::Value new_result = result ? result->Clone() : base::Value();
+ if (user_callback)
+ std::move(user_callback).Run(new_result);
+ },
+ std::move(callback));
// |render_frame_host_| should still be alive if the
// caller is using this class correctly.
DCHECK(render_frame_host_);
render_frame_host_->ExecuteJavaScriptInIsolatedWorld(
- script, std::move(proxy_callback), isolated_world_id_);
+ script, base::AdaptCallbackForRepeating(std::move(proxy_callback)),
+ isolated_world_id_);
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/BUILD.gn b/chromium/components/offline_pages/core/BUILD.gn
index 8138618d19e..b3c96cc7bc2 100644
--- a/chromium/components/offline_pages/core/BUILD.gn
+++ b/chromium/components/offline_pages/core/BUILD.gn
@@ -63,8 +63,8 @@ static_library("core") {
"offline_page_client_policy.h",
"offline_page_item.cc",
"offline_page_item.h",
- "offline_page_metadata_store_sql.cc",
- "offline_page_metadata_store_sql.h",
+ "offline_page_metadata_store.cc",
+ "offline_page_metadata_store.h",
"offline_page_model.cc",
"offline_page_model.h",
"offline_page_model_event_logger.cc",
diff --git a/chromium/components/offline_pages/core/archive_manager.cc b/chromium/components/offline_pages/core/archive_manager.cc
index ced20b2279e..f24e1690be4 100644
--- a/chromium/components/offline_pages/core/archive_manager.cc
+++ b/chromium/components/offline_pages/core/archive_manager.cc
@@ -110,13 +110,13 @@ void ArchiveManager::EnsureArchivesDirCreated(
// The callback will only be invoked once both directories are created.
if (!temporary_archives_dir_.empty()) {
task_runner_->PostTask(
- FROM_HERE, base::Bind(EnsureArchivesDirCreatedImpl,
- temporary_archives_dir_, true /* is_temp */));
+ FROM_HERE, base::BindOnce(EnsureArchivesDirCreatedImpl,
+ temporary_archives_dir_, true /* is_temp */));
}
task_runner_->PostTaskAndReply(
FROM_HERE,
- base::Bind(EnsureArchivesDirCreatedImpl, private_archives_dir_,
- false /* is_temp */),
+ base::BindOnce(EnsureArchivesDirCreatedImpl, private_archives_dir_,
+ false /* is_temp */),
std::move(callback));
}
diff --git a/chromium/components/offline_pages/core/archive_manager_unittest.cc b/chromium/components/offline_pages/core/archive_manager_unittest.cc
index b05d3eca683..5428468ee8b 100644
--- a/chromium/components/offline_pages/core/archive_manager_unittest.cc
+++ b/chromium/components/offline_pages/core/archive_manager_unittest.cc
@@ -15,7 +15,7 @@
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/sys_info.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/components/offline_pages/core/background/BUILD.gn b/chromium/components/offline_pages/core/background/BUILD.gn
index 757ea0ce4d0..18a8833fc5e 100644
--- a/chromium/components/offline_pages/core/background/BUILD.gn
+++ b/chromium/components/offline_pages/core/background/BUILD.gn
@@ -90,6 +90,7 @@ static_library("test_support") {
deps = [
":background_offliner",
"//base",
+ "//components/offline_items_collection/core/test_support",
"//components/offline_pages/core",
"//net",
]
diff --git a/chromium/components/offline_pages/core/background/request_coordinator.cc b/chromium/components/offline_pages/core/background/request_coordinator.cc
index c310b7baa80..f940ed48843 100644
--- a/chromium/components/offline_pages/core/background/request_coordinator.cc
+++ b/chromium/components/offline_pages/core/background/request_coordinator.cc
@@ -12,6 +12,7 @@
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/rand_util.h"
+#include "base/stl_util.h"
#include "base/sys_info.h"
#include "base/time/time.h"
#include "components/offline_pages/core/background/offliner.h"
@@ -342,18 +343,22 @@ void RequestCoordinator::GetQueuedRequestsCallback(
void RequestCoordinator::StopOfflining(CancelCallback final_callback,
Offliner::RequestStatus stop_status) {
+ // Wrapping the |final_callback| since it might be moved twice if offliner
+ // returns false when Cancel().
+ // TODO(https://crbug.com/874313): refactor so we can use |final_callback| as
+ // an OnceCallback.
+ auto callback = base::AdaptCallbackForRepeating(std::move(final_callback));
if (offliner_ && state_ == RequestCoordinatorState::OFFLINING) {
DCHECK_NE(active_request_id_, 0);
if (offliner_->Cancel(base::BindOnce(
&RequestCoordinator::HandleCancelUpdateStatusCallback,
- weak_ptr_factory_.GetWeakPtr(), std::move(final_callback),
- stop_status))) {
+ weak_ptr_factory_.GetWeakPtr(), callback, stop_status))) {
return;
}
}
UpdateStatusForCancel(stop_status);
- std::move(final_callback).Run(active_request_id_);
+ callback.Run(active_request_id_);
}
void RequestCoordinator::GetRequestsForSchedulingCallback(
@@ -381,8 +386,7 @@ bool RequestCoordinator::CancelActiveRequestIfItMatches(
// If we have a request in progress and need to cancel it, call the
// offliner to cancel.
if (active_request_id_ != 0) {
- if (request_ids.end() !=
- std::find(request_ids.begin(), request_ids.end(), active_request_id_)) {
+ if (base::ContainsValue(request_ids, active_request_id_)) {
StopOfflining(
base::BindOnce(&RequestCoordinator::ResetActiveRequestCallback,
weak_ptr_factory_.GetWeakPtr()),
diff --git a/chromium/components/offline_pages/core/background/request_coordinator_unittest.cc b/chromium/components/offline_pages/core/background/request_coordinator_unittest.cc
index a135f81ce26..cf16cb620d1 100644
--- a/chromium/components/offline_pages/core/background/request_coordinator_unittest.cc
+++ b/chromium/components/offline_pages/core/background/request_coordinator_unittest.cc
@@ -15,7 +15,7 @@
#include "base/logging.h"
#include "base/synchronization/waitable_event.h"
#include "base/sys_info.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
diff --git a/chromium/components/offline_pages/core/background/request_queue_store_sql.cc b/chromium/components/offline_pages/core/background/request_queue_store_sql.cc
index b601100955b..1f8342fc15f 100644
--- a/chromium/components/offline_pages/core/background/request_queue_store_sql.cc
+++ b/chromium/components/offline_pages/core/background/request_queue_store_sql.cc
@@ -35,21 +35,22 @@ using SuccessCallback = base::OnceCallback<void(bool)>;
const bool kUserRequested = true;
bool CreateRequestQueueTable(sql::Connection* db) {
- const char kSql[] = "CREATE TABLE IF NOT EXISTS " REQUEST_QUEUE_TABLE_NAME
- " (request_id INTEGER PRIMARY KEY NOT NULL,"
- " creation_time INTEGER NOT NULL,"
- " activation_time INTEGER NOT NULL DEFAULT 0,"
- " last_attempt_time INTEGER NOT NULL DEFAULT 0,"
- " started_attempt_count INTEGER NOT NULL,"
- " completed_attempt_count INTEGER NOT NULL,"
- " state INTEGER NOT NULL DEFAULT 0,"
- " url VARCHAR NOT NULL,"
- " client_namespace VARCHAR NOT NULL,"
- " client_id VARCHAR NOT NULL,"
- " original_url VARCHAR NOT NULL DEFAULT '',"
- " request_origin VARCHAR NOT NULL DEFAULT '',"
- " fail_state INTEGER NOT NULL DEFAULT 0"
- ")";
+ static const char kSql[] =
+ "CREATE TABLE IF NOT EXISTS " REQUEST_QUEUE_TABLE_NAME
+ " (request_id INTEGER PRIMARY KEY NOT NULL,"
+ " creation_time INTEGER NOT NULL,"
+ " activation_time INTEGER NOT NULL DEFAULT 0,"
+ " last_attempt_time INTEGER NOT NULL DEFAULT 0,"
+ " started_attempt_count INTEGER NOT NULL,"
+ " completed_attempt_count INTEGER NOT NULL,"
+ " state INTEGER NOT NULL DEFAULT 0,"
+ " url VARCHAR NOT NULL,"
+ " client_namespace VARCHAR NOT NULL,"
+ " client_id VARCHAR NOT NULL,"
+ " original_url VARCHAR NOT NULL DEFAULT '',"
+ " request_origin VARCHAR NOT NULL DEFAULT '',"
+ " fail_state INTEGER NOT NULL DEFAULT 0"
+ ")";
return db->Execute(kSql);
}
@@ -66,7 +67,7 @@ bool UpgradeWithQuery(sql::Connection* db, const char* upgrade_sql) {
}
bool UpgradeFrom57(sql::Connection* db) {
- const char kSql[] =
+ static const char kSql[] =
"INSERT INTO " REQUEST_QUEUE_TABLE_NAME
" (request_id, creation_time, activation_time, last_attempt_time, "
"started_attempt_count, completed_attempt_count, state, url, "
@@ -80,7 +81,7 @@ bool UpgradeFrom57(sql::Connection* db) {
}
bool UpgradeFrom58(sql::Connection* db) {
- const char kSql[] =
+ static const char kSql[] =
"INSERT INTO " REQUEST_QUEUE_TABLE_NAME
" (request_id, creation_time, activation_time, last_attempt_time, "
"started_attempt_count, completed_attempt_count, state, url, "
@@ -94,7 +95,7 @@ bool UpgradeFrom58(sql::Connection* db) {
}
bool UpgradeFrom61(sql::Connection* db) {
- const char kSql[] =
+ static const char kSql[] =
"INSERT INTO " REQUEST_QUEUE_TABLE_NAME
" (request_id, creation_time, activation_time, last_attempt_time, "
"started_attempt_count, completed_attempt_count, state, url, "
@@ -183,7 +184,7 @@ std::unique_ptr<SavePageRequest> MakeSavePageRequest(
// Get a request for a specific id.
std::unique_ptr<SavePageRequest> GetOneRequest(sql::Connection* db,
const int64_t request_id) {
- const char kSql[] =
+ static const char kSql[] =
"SELECT request_id, creation_time, activation_time,"
" last_attempt_time, started_attempt_count, completed_attempt_count,"
" state, url, client_namespace, client_id, original_url, request_origin,"
@@ -199,7 +200,7 @@ std::unique_ptr<SavePageRequest> GetOneRequest(sql::Connection* db,
}
ItemActionStatus DeleteRequestById(sql::Connection* db, int64_t request_id) {
- const char kSql[] =
+ static const char kSql[] =
"DELETE FROM " REQUEST_QUEUE_TABLE_NAME " WHERE request_id=?";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt64(0, request_id);
@@ -211,7 +212,7 @@ ItemActionStatus DeleteRequestById(sql::Connection* db, int64_t request_id) {
}
ItemActionStatus Insert(sql::Connection* db, const SavePageRequest& request) {
- const char kSql[] =
+ static const char kSql[] =
"INSERT OR IGNORE INTO " REQUEST_QUEUE_TABLE_NAME
" (request_id, creation_time, activation_time,"
" last_attempt_time, started_attempt_count, completed_attempt_count,"
@@ -244,7 +245,7 @@ ItemActionStatus Insert(sql::Connection* db, const SavePageRequest& request) {
}
ItemActionStatus Update(sql::Connection* db, const SavePageRequest& request) {
- const char kSql[] =
+ static const char kSql[] =
"UPDATE OR IGNORE " REQUEST_QUEUE_TABLE_NAME
" SET creation_time = ?, activation_time = ?, last_attempt_time = ?,"
" started_attempt_count = ?, completed_attempt_count = ?, state = ?,"
@@ -328,7 +329,7 @@ bool InitDatabase(sql::Connection* db, const base::FilePath& path) {
void GetRequestsSync(sql::Connection* db,
scoped_refptr<base::SingleThreadTaskRunner> runner,
RequestQueueStore::GetRequestsCallback callback) {
- const char kSql[] =
+ static const char kSql[] =
"SELECT request_id, creation_time, activation_time,"
" last_attempt_time, started_attempt_count, completed_attempt_count,"
" state, url, client_namespace, client_id, original_url, request_origin,"
diff --git a/chromium/components/offline_pages/core/background/request_queue_store_sql.h b/chromium/components/offline_pages/core/background/request_queue_store_sql.h
index 93e36b4c1cb..22fc2299cca 100644
--- a/chromium/components/offline_pages/core/background/request_queue_store_sql.h
+++ b/chromium/components/offline_pages/core/background/request_queue_store_sql.h
@@ -36,7 +36,7 @@ namespace offline_pages {
// schema.
//
// Looking for procedure to update the schema, please refer to
-// offline_page_metadata_store_sql.h
+// offline_page_metadata_store.h
class RequestQueueStoreSQL : public RequestQueueStore {
public:
RequestQueueStoreSQL(
diff --git a/chromium/components/offline_pages/core/background/scheduler.h b/chromium/components/offline_pages/core/background/scheduler.h
index 818c216df06..b5122b2955f 100644
--- a/chromium/components/offline_pages/core/background/scheduler.h
+++ b/chromium/components/offline_pages/core/background/scheduler.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_H_
#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_H_
+#include <stdint.h>
+
#include "components/offline_pages/core/background/device_conditions.h"
namespace offline_pages {
@@ -38,7 +40,7 @@ class Scheduler {
// so we can continue processing background download requests. This will
// not overwrite existing tasks.
virtual void BackupSchedule(const TriggerConditions& trigger_conditions,
- long delay_in_seconds) = 0;
+ int64_t delay_in_seconds) = 0;
// Unschedules the currently scheduled task, if any.
virtual void Unschedule() = 0;
diff --git a/chromium/components/offline_pages/core/background/scheduler_stub.cc b/chromium/components/offline_pages/core/background/scheduler_stub.cc
index f80c60c3eee..1d855b16e67 100644
--- a/chromium/components/offline_pages/core/background/scheduler_stub.cc
+++ b/chromium/components/offline_pages/core/background/scheduler_stub.cc
@@ -30,7 +30,7 @@ void SchedulerStub::Schedule(const TriggerConditions& trigger_conditions) {
}
void SchedulerStub::BackupSchedule(const TriggerConditions& trigger_conditions,
- long delay_in_seconds) {
+ int64_t delay_in_seconds) {
backup_schedule_called_ = true;
schedule_delay_ = delay_in_seconds;
trigger_conditions_ = trigger_conditions;
diff --git a/chromium/components/offline_pages/core/background/scheduler_stub.h b/chromium/components/offline_pages/core/background/scheduler_stub.h
index fa57d1feb9c..56d2d8ae701 100644
--- a/chromium/components/offline_pages/core/background/scheduler_stub.h
+++ b/chromium/components/offline_pages/core/background/scheduler_stub.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_STUB_H_
#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_SCHEDULER_STUB_H_
+#include <stdint.h>
+
#include "components/offline_pages/core/background/scheduler.h"
namespace offline_pages {
@@ -19,7 +21,7 @@ class SchedulerStub : public Scheduler {
void Schedule(const TriggerConditions& trigger_conditions) override;
void BackupSchedule(const TriggerConditions& trigger_conditions,
- long delay_in_seconds) override;
+ int64_t delay_in_seconds) override;
// Unschedules the currently scheduled task, if any.
void Unschedule() override;
@@ -42,7 +44,7 @@ class SchedulerStub : public Scheduler {
bool backup_schedule_called_;
bool unschedule_called_;
bool get_current_device_conditions_called_;
- long schedule_delay_;
+ int64_t schedule_delay_;
DeviceConditions device_conditions_;
TriggerConditions trigger_conditions_;
};
diff --git a/chromium/components/offline_pages/core/downloads/BUILD.gn b/chromium/components/offline_pages/core/downloads/BUILD.gn
index a3522e414b3..b3ef6b22208 100644
--- a/chromium/components/offline_pages/core/downloads/BUILD.gn
+++ b/chromium/components/offline_pages/core/downloads/BUILD.gn
@@ -8,13 +8,10 @@ if (is_android) {
static_library("offline_pages_ui_adapter") {
sources = [
- "download_notifying_observer.cc",
- "download_notifying_observer.h",
"download_ui_adapter.cc",
"download_ui_adapter.h",
"offline_item_conversions.cc",
"offline_item_conversions.h",
- "offline_page_download_notifier.h",
]
deps = [
@@ -31,7 +28,6 @@ static_library("offline_pages_ui_adapter") {
source_set("unit_tests") {
testonly = true
sources = [
- "download_notifying_observer_unittest.cc",
"download_ui_adapter_unittest.cc",
"offline_item_conversions_unittest.cc",
]
@@ -41,6 +37,7 @@ source_set("unit_tests") {
"//base",
"//base/test:test_support",
"//components/offline_items_collection/core",
+ "//components/offline_items_collection/core/test_support",
"//components/offline_pages/core",
"//components/offline_pages/core:switches",
"//components/offline_pages/core:test_support",
diff --git a/chromium/components/offline_pages/core/downloads/download_notifying_observer.cc b/chromium/components/offline_pages/core/downloads/download_notifying_observer.cc
deleted file mode 100644
index 63b6c7b252e..00000000000
--- a/chromium/components/offline_pages/core/downloads/download_notifying_observer.cc
+++ /dev/null
@@ -1,123 +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/offline_pages/core/downloads/download_notifying_observer.h"
-
-#include "base/memory/ptr_util.h"
-#include "components/offline_pages/core/background/request_coordinator.h"
-#include "components/offline_pages/core/background/save_page_request.h"
-#include "components/offline_pages/core/client_policy_controller.h"
-#include "components/offline_pages/core/downloads/download_ui_adapter.h"
-#include "components/offline_pages/core/downloads/offline_item_conversions.h"
-#include "components/offline_pages/core/downloads/offline_page_download_notifier.h"
-
-namespace offline_pages {
-namespace {
-int kUserDataKey; // Only address is used.
-} // namespace
-
-DownloadNotifyingObserver::DownloadNotifyingObserver(
- std::unique_ptr<OfflinePageDownloadNotifier> notifier,
- ClientPolicyController* policy_controller)
- : notifier_(std::move(notifier)), policy_controller_(policy_controller) {}
-
-DownloadNotifyingObserver::~DownloadNotifyingObserver() {}
-
-// static
-DownloadNotifyingObserver* DownloadNotifyingObserver::GetFromRequestCoordinator(
- RequestCoordinator* request_coordinator) {
- DCHECK(request_coordinator);
- return static_cast<DownloadNotifyingObserver*>(
- request_coordinator->GetUserData(&kUserDataKey));
-}
-
-// static
-void DownloadNotifyingObserver::CreateAndStartObserving(
- RequestCoordinator* request_coordinator,
- std::unique_ptr<OfflinePageDownloadNotifier> notifier) {
- DCHECK(request_coordinator);
- DCHECK(notifier);
- std::unique_ptr<DownloadNotifyingObserver> observer =
- base::WrapUnique(new DownloadNotifyingObserver(
- std::move(notifier), request_coordinator->GetPolicyController()));
- request_coordinator->AddObserver(observer.get());
- request_coordinator->SetUserData(&kUserDataKey, std::move(observer));
-}
-
-void DownloadNotifyingObserver::OnAdded(const SavePageRequest& request) {
- DCHECK(notifier_);
- if (!IsVisibleInUI(request.client_id()))
- return;
-
- // Calling Progress ensures notification is created in lieu of specific
- // Add/Create call.
- notifier_->NotifyDownloadProgress(
- OfflineItemConversions::CreateOfflineItem(request));
-
- // Now we need to update the notification if it is not active/offlining.
- if (request.request_state() != SavePageRequest::RequestState::OFFLINING)
- NotifyRequestStateChange(request);
-}
-
-void DownloadNotifyingObserver::OnChanged(const SavePageRequest& request) {
- DCHECK(notifier_);
- if (!IsVisibleInUI(request.client_id()))
- return;
- NotifyRequestStateChange(request);
-}
-
-void DownloadNotifyingObserver::OnNetworkProgress(
- const SavePageRequest& request,
- int64_t received_bytes) {
- // TODO(dimich): Enable this back in M59. See bug 704049 for more info and
- // what was temporarily (for M58) reverted.
-}
-
-void DownloadNotifyingObserver::OnCompleted(
- const SavePageRequest& request,
- RequestCoordinator::BackgroundSavePageResult status) {
- DCHECK(notifier_);
- if (!IsVisibleInUI(request.client_id()))
- return;
- if (status == RequestCoordinator::BackgroundSavePageResult::SUCCESS) {
- // Suppress notifications for certin downloads resulting from CCT.
- OfflineItem item = OfflineItemConversions::CreateOfflineItem(request);
- if (!notifier_->MaybeSuppressNotification(request.request_origin(), item)) {
- notifier_->NotifyDownloadSuccessful(item);
- }
- } else if (status ==
- RequestCoordinator::BackgroundSavePageResult::USER_CANCELED ||
- status == RequestCoordinator::BackgroundSavePageResult::
- DOWNLOAD_THROTTLED) {
- notifier_->NotifyDownloadCanceled(
- OfflineItemConversions::CreateOfflineItem(request));
- } else {
- notifier_->NotifyDownloadFailed(
- OfflineItemConversions::CreateOfflineItem(request));
- }
-}
-
-bool DownloadNotifyingObserver::IsVisibleInUI(const ClientId& page) {
- return policy_controller_->IsSupportedByDownload(page.name_space) &&
- base::IsValidGUID(page.id);
-}
-
-// Calls the appropriate notifier method depending upon the state of the
-// request. For example, an AVAILABLE request is not active (aka, pending)
-// which the notifier understands as an Interrupted operation vs. one that
-// has Progress or is Paused.
-void DownloadNotifyingObserver::NotifyRequestStateChange(
- const SavePageRequest& request) {
- if (request.request_state() == SavePageRequest::RequestState::PAUSED)
- notifier_->NotifyDownloadPaused(
- OfflineItemConversions::CreateOfflineItem(request));
- else if (request.request_state() == SavePageRequest::RequestState::AVAILABLE)
- notifier_->NotifyDownloadInterrupted(
- OfflineItemConversions::CreateOfflineItem(request));
- else
- notifier_->NotifyDownloadProgress(
- OfflineItemConversions::CreateOfflineItem(request));
-}
-
-} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/downloads/download_notifying_observer.h b/chromium/components/offline_pages/core/downloads/download_notifying_observer.h
deleted file mode 100644
index 583c3d773c0..00000000000
--- a/chromium/components/offline_pages/core/downloads/download_notifying_observer.h
+++ /dev/null
@@ -1,65 +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_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
-#define COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
-
-#include <memory>
-
-#include "base/guid.h"
-#include "base/macros.h"
-#include "components/offline_pages/core/background/request_coordinator.h"
-#include "components/offline_pages/core/client_policy_controller.h"
-
-namespace offline_pages {
-
-struct ClientId;
-struct OfflinePageDownloadNotifier;
-class ClientPolicyController;
-class SavePageRequest;
-
-// Class observing the save page requests and issuing corresponding user
-// notifications as requests are added or updated.
-class DownloadNotifyingObserver : public RequestCoordinator::Observer,
- public base::SupportsUserData::Data {
- public:
- ~DownloadNotifyingObserver() override;
-
- static DownloadNotifyingObserver* GetFromRequestCoordinator(
- RequestCoordinator* request_coordinator);
- static void CreateAndStartObserving(
- RequestCoordinator* request_coordinator,
- std::unique_ptr<OfflinePageDownloadNotifier> notifier);
-
- // RequestCoordinator::Observer implementation:
- void OnAdded(const SavePageRequest& request) override;
- void OnChanged(const SavePageRequest& request) override;
- void OnCompleted(
- const SavePageRequest& request,
- RequestCoordinator::BackgroundSavePageResult status) override;
- void OnNetworkProgress(const SavePageRequest& request,
- int64_t received_bytes) override;
-
- private:
- friend class DownloadNotifyingObserverTest;
-
- DownloadNotifyingObserver(
- std::unique_ptr<OfflinePageDownloadNotifier> notifier,
- ClientPolicyController* policy_controller);
-
- bool IsVisibleInUI(const ClientId& id);
-
- void NotifyRequestStateChange(const SavePageRequest& request);
-
- // Used to issue notifications related to save page requests.
- std::unique_ptr<OfflinePageDownloadNotifier> notifier_;
- // Used to determine policy-related permissions. Not owned.
- ClientPolicyController* policy_controller_;
-
- DISALLOW_COPY_AND_ASSIGN(DownloadNotifyingObserver);
-};
-
-} // namespace offline_pages
-
-#endif // COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_DOWNLOAD_NOTIFYING_OBSERVER_H_
diff --git a/chromium/components/offline_pages/core/downloads/download_notifying_observer_unittest.cc b/chromium/components/offline_pages/core/downloads/download_notifying_observer_unittest.cc
deleted file mode 100644
index f4195f132f6..00000000000
--- a/chromium/components/offline_pages/core/downloads/download_notifying_observer_unittest.cc
+++ /dev/null
@@ -1,312 +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/offline_pages/core/downloads/download_notifying_observer.h"
-
-#include "components/offline_pages/core/background/save_page_request.h"
-#include "components/offline_pages/core/client_namespace_constants.h"
-#include "components/offline_pages/core/client_policy_controller.h"
-#include "components/offline_pages/core/downloads/offline_page_download_notifier.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace offline_pages {
-
-namespace {
-static const int64_t kTestOfflineId = 42L;
-static const char kTestUrl[] = "http://foo.com/bar";
-static const char kTestGuid[] = "cccccccc-cccc-4ccc-0ccc-ccccccccccc1";
-static const ClientId kTestClientId(kDownloadNamespace, kTestGuid);
-static const base::Time kTestCreationTime = base::Time::Now();
-static const bool kTestUserRequested = true;
-
-enum class LastNotificationType {
- NONE,
- DOWNLOAD_SUCCESSFUL,
- DOWNLOAD_FAILED,
- DOWNLOAD_PROGRESS,
- DOWNLOAD_PAUSED,
- DOWNLOAD_INTERRUPTED,
- DOWNLOAD_CANCELED,
- DOWNLOAD_SUPPRESSED,
-};
-
-class TestNotifier : public OfflinePageDownloadNotifier {
- public:
- TestNotifier();
- ~TestNotifier() override;
-
- // OfflinePageDownloadNotifier implementation:
- void NotifyDownloadSuccessful(const OfflineItem& item) override;
- void NotifyDownloadFailed(const OfflineItem& item) override;
- void NotifyDownloadProgress(const OfflineItem& item) override;
- void NotifyDownloadPaused(const OfflineItem& item) override;
- void NotifyDownloadInterrupted(const OfflineItem& item) override;
- void NotifyDownloadCanceled(const OfflineItem& item) override;
- bool MaybeSuppressNotification(const std::string& origin,
- const OfflineItem& item) override;
-
- void Reset();
-
- LastNotificationType last_notification_type() const {
- return last_notification_type_;
- }
-
- OfflineItem* offline_item() const { return offline_item_.get(); }
-
- void SetShouldSuppressNotification(bool should_suppress) {
- should_suppress_notification_ = should_suppress;
- }
-
- private:
- LastNotificationType last_notification_type_;
- std::unique_ptr<OfflineItem> offline_item_;
- bool should_suppress_notification_ = false;
-};
-
-TestNotifier::TestNotifier()
- : last_notification_type_(LastNotificationType::NONE) {}
-
-TestNotifier::~TestNotifier() {}
-
-void TestNotifier::NotifyDownloadSuccessful(const OfflineItem& item) {
- last_notification_type_ = LastNotificationType::DOWNLOAD_SUCCESSFUL;
- offline_item_.reset(new OfflineItem(item));
-}
-
-void TestNotifier::NotifyDownloadFailed(const OfflineItem& item) {
- last_notification_type_ = LastNotificationType::DOWNLOAD_FAILED;
- offline_item_.reset(new OfflineItem(item));
-}
-
-void TestNotifier::NotifyDownloadProgress(const OfflineItem& item) {
- last_notification_type_ = LastNotificationType::DOWNLOAD_PROGRESS;
- offline_item_.reset(new OfflineItem(item));
-}
-
-void TestNotifier::NotifyDownloadPaused(const OfflineItem& item) {
- last_notification_type_ = LastNotificationType::DOWNLOAD_PAUSED;
- offline_item_.reset(new OfflineItem(item));
-}
-
-void TestNotifier::NotifyDownloadInterrupted(const OfflineItem& item) {
- last_notification_type_ = LastNotificationType::DOWNLOAD_INTERRUPTED;
- offline_item_.reset(new OfflineItem(item));
-}
-
-void TestNotifier::NotifyDownloadCanceled(const OfflineItem& item) {
- last_notification_type_ = LastNotificationType::DOWNLOAD_CANCELED;
- offline_item_.reset(new OfflineItem(item));
-}
-
-bool TestNotifier::MaybeSuppressNotification(const std::string& origin,
- const OfflineItem& item) {
- last_notification_type_ = LastNotificationType::DOWNLOAD_SUPPRESSED;
- offline_item_.reset(new OfflineItem(item));
- return should_suppress_notification_;
-}
-
-void TestNotifier::Reset() {
- last_notification_type_ = LastNotificationType::NONE;
- offline_item_.reset(nullptr);
- should_suppress_notification_ = false;
-}
-
-} // namespace
-
-class DownloadNotifyingObserverTest : public testing::Test {
- public:
- DownloadNotifyingObserverTest();
- ~DownloadNotifyingObserverTest() override;
-
- // testing::Test implementation:
- void SetUp() override;
-
- TestNotifier* notifier() const { return notifier_; }
- DownloadNotifyingObserver* observer() { return observer_.get(); }
-
- private:
- TestNotifier* notifier_;
- std::unique_ptr<DownloadNotifyingObserver> observer_;
- std::unique_ptr<ClientPolicyController> policy_controller_;
-};
-
-DownloadNotifyingObserverTest::DownloadNotifyingObserverTest() {}
-
-DownloadNotifyingObserverTest::~DownloadNotifyingObserverTest() {}
-
-void DownloadNotifyingObserverTest::SetUp() {
- std::unique_ptr<TestNotifier> notifier(new TestNotifier);
- policy_controller_.reset(new ClientPolicyController());
- notifier_ = notifier.get();
- observer_.reset(new DownloadNotifyingObserver(std::move(notifier),
- policy_controller_.get()));
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnAddedAsAvailable) {
- SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
- kTestCreationTime, kTestUserRequested);
- request.set_request_state(SavePageRequest::RequestState::AVAILABLE);
- observer()->OnAdded(request);
- EXPECT_EQ(LastNotificationType::DOWNLOAD_INTERRUPTED,
- notifier()->last_notification_type());
- EXPECT_EQ(kTestGuid, notifier()->offline_item()->id.id);
- EXPECT_EQ(GURL(kTestUrl), notifier()->offline_item()->page_url);
- EXPECT_EQ(kTestCreationTime, notifier()->offline_item()->creation_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnAddedAsOfflining) {
- SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
- kTestCreationTime, kTestUserRequested);
- request.set_request_state(SavePageRequest::RequestState::OFFLINING);
- observer()->OnAdded(request);
- EXPECT_EQ(LastNotificationType::DOWNLOAD_PROGRESS,
- notifier()->last_notification_type());
- EXPECT_EQ(kTestGuid, notifier()->offline_item()->id.id);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnAddedAsPaused) {
- SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
- kTestCreationTime, kTestUserRequested);
- request.set_request_state(SavePageRequest::RequestState::PAUSED);
- observer()->OnAdded(request);
- EXPECT_EQ(LastNotificationType::DOWNLOAD_PAUSED,
- notifier()->last_notification_type());
- EXPECT_EQ(kTestGuid, notifier()->offline_item()->id.id);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnChangedToPaused) {
- SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
- kTestCreationTime, kTestUserRequested);
- request.set_request_state(SavePageRequest::RequestState::PAUSED);
- observer()->OnChanged(request);
- EXPECT_EQ(LastNotificationType::DOWNLOAD_PAUSED,
- notifier()->last_notification_type());
- EXPECT_EQ(kTestGuid, notifier()->offline_item()->id.id);
- EXPECT_EQ(GURL(kTestUrl), notifier()->offline_item()->page_url);
- EXPECT_EQ(kTestCreationTime, notifier()->offline_item()->creation_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnChangedToAvailable) {
- SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
- kTestCreationTime, kTestUserRequested);
- request.set_request_state(SavePageRequest::RequestState::AVAILABLE);
- observer()->OnChanged(request);
- EXPECT_EQ(LastNotificationType::DOWNLOAD_INTERRUPTED,
- notifier()->last_notification_type());
- EXPECT_EQ(kTestGuid, notifier()->offline_item()->id.id);
- EXPECT_EQ(GURL(kTestUrl), notifier()->offline_item()->page_url);
- EXPECT_EQ(kTestCreationTime, notifier()->offline_item()->creation_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnChangedToOfflining) {
- SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
- kTestCreationTime, kTestUserRequested);
- request.set_request_state(SavePageRequest::RequestState::OFFLINING);
- observer()->OnChanged(request);
- EXPECT_EQ(LastNotificationType::DOWNLOAD_PROGRESS,
- notifier()->last_notification_type());
- EXPECT_EQ(kTestGuid, notifier()->offline_item()->id.id);
- EXPECT_EQ(GURL(kTestUrl), notifier()->offline_item()->page_url);
- EXPECT_EQ(kTestCreationTime, notifier()->offline_item()->creation_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnCompletedSuccess) {
- SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
- kTestCreationTime, kTestUserRequested);
- observer()->OnCompleted(request,
- RequestNotifier::BackgroundSavePageResult::SUCCESS);
- EXPECT_EQ(LastNotificationType::DOWNLOAD_SUCCESSFUL,
- notifier()->last_notification_type());
- EXPECT_EQ(kTestGuid, notifier()->offline_item()->id.id);
- EXPECT_EQ(GURL(kTestUrl), notifier()->offline_item()->page_url);
- EXPECT_EQ(kTestCreationTime, notifier()->offline_item()->creation_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnCompletedSuccess_SuppressNotification) {
- SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
- kTestCreationTime, kTestUserRequested);
- notifier()->SetShouldSuppressNotification(true);
- observer()->OnCompleted(request,
- RequestNotifier::BackgroundSavePageResult::SUCCESS);
- EXPECT_EQ(LastNotificationType::DOWNLOAD_SUPPRESSED,
- notifier()->last_notification_type());
- EXPECT_EQ(kTestGuid, notifier()->offline_item()->id.id);
- EXPECT_EQ(GURL(kTestUrl), notifier()->offline_item()->page_url);
- EXPECT_EQ(kTestCreationTime, notifier()->offline_item()->creation_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnCompletedCanceled) {
- SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
- kTestCreationTime, kTestUserRequested);
- observer()->OnCompleted(
- request, RequestNotifier::BackgroundSavePageResult::USER_CANCELED);
- EXPECT_EQ(LastNotificationType::DOWNLOAD_CANCELED,
- notifier()->last_notification_type());
- EXPECT_EQ(kTestGuid, notifier()->offline_item()->id.id);
- EXPECT_EQ(GURL(kTestUrl), notifier()->offline_item()->page_url);
- EXPECT_EQ(kTestCreationTime, notifier()->offline_item()->creation_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, OnCompletedFailure) {
- SavePageRequest request(kTestOfflineId, GURL(kTestUrl), kTestClientId,
- kTestCreationTime, kTestUserRequested);
- observer()->OnCompleted(
- request, RequestNotifier::BackgroundSavePageResult::LOADING_FAILURE);
- EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
- notifier()->last_notification_type());
- EXPECT_EQ(kTestGuid, notifier()->offline_item()->id.id);
- EXPECT_EQ(GURL(kTestUrl), notifier()->offline_item()->page_url);
- EXPECT_EQ(kTestCreationTime, notifier()->offline_item()->creation_time);
-
- notifier()->Reset();
- observer()->OnCompleted(
- request, RequestNotifier::BackgroundSavePageResult::FOREGROUND_CANCELED);
- EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
- notifier()->last_notification_type());
- EXPECT_EQ(kTestGuid, notifier()->offline_item()->id.id);
- EXPECT_EQ(GURL(kTestUrl), notifier()->offline_item()->page_url);
- EXPECT_EQ(kTestCreationTime, notifier()->offline_item()->creation_time);
-
- notifier()->Reset();
- observer()->OnCompleted(
- request, RequestNotifier::BackgroundSavePageResult::SAVE_FAILED);
- EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
- notifier()->last_notification_type());
- EXPECT_EQ(kTestGuid, notifier()->offline_item()->id.id);
- EXPECT_EQ(GURL(kTestUrl), notifier()->offline_item()->page_url);
- EXPECT_EQ(kTestCreationTime, notifier()->offline_item()->creation_time);
-
- notifier()->Reset();
- observer()->OnCompleted(request,
- RequestNotifier::BackgroundSavePageResult::EXPIRED);
- EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
- notifier()->last_notification_type());
- EXPECT_EQ(kTestGuid, notifier()->offline_item()->id.id);
- EXPECT_EQ(GURL(kTestUrl), notifier()->offline_item()->page_url);
- EXPECT_EQ(kTestCreationTime, notifier()->offline_item()->creation_time);
-
- notifier()->Reset();
- observer()->OnCompleted(
- request, RequestNotifier::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED);
- EXPECT_EQ(LastNotificationType::DOWNLOAD_FAILED,
- notifier()->last_notification_type());
- EXPECT_EQ(kTestGuid, notifier()->offline_item()->id.id);
- EXPECT_EQ(GURL(kTestUrl), notifier()->offline_item()->page_url);
- EXPECT_EQ(kTestCreationTime, notifier()->offline_item()->creation_time);
-}
-
-TEST_F(DownloadNotifyingObserverTest, NamespacesNotVisibleInUI) {
- std::vector<std::string> name_spaces = {kBookmarkNamespace, kLastNNamespace,
- kCCTNamespace, kDefaultNamespace};
-
- for (auto name_space : name_spaces) {
- ClientId invisible_client_id(name_space, kTestGuid);
- SavePageRequest request(kTestOfflineId, GURL(kTestUrl), invisible_client_id,
- kTestCreationTime, kTestUserRequested);
- observer()->OnAdded(request);
- EXPECT_EQ(LastNotificationType::NONE, notifier()->last_notification_type());
- }
-}
-
-} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/downloads/download_ui_adapter.cc b/chromium/components/offline_pages/core/downloads/download_ui_adapter.cc
index dca1fdf4069..a8ce6418e20 100644
--- a/chromium/components/offline_pages/core/downloads/download_ui_adapter.cc
+++ b/chromium/components/offline_pages/core/downloads/download_ui_adapter.cc
@@ -12,6 +12,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/request_notifier.h"
#include "components/offline_pages/core/background/save_page_request.h"
#include "components/offline_pages/core/client_namespace_constants.h"
#include "components/offline_pages/core/client_policy_controller.h"
@@ -63,27 +64,6 @@ void DownloadUIAdapter::AttachToOfflinePageModel(
model->SetUserData(kDownloadUIAdapterKey, std::move(adapter));
}
-DownloadUIAdapter::ItemInfo::ItemInfo(const OfflinePageItem& page,
- bool temporarily_hidden,
- bool is_suggested)
- : ui_item(std::make_unique<OfflineItem>(
- OfflineItemConversions::CreateOfflineItem(page, is_suggested))),
- is_request(false),
- offline_id(page.offline_id),
- client_id(page.client_id),
- temporarily_hidden(temporarily_hidden) {}
-
-DownloadUIAdapter::ItemInfo::ItemInfo(const SavePageRequest& request,
- bool temporarily_hidden)
- : ui_item(std::make_unique<OfflineItem>(
- OfflineItemConversions::CreateOfflineItem(request))),
- is_request(true),
- offline_id(request.request_id()),
- client_id(request.client_id()),
- temporarily_hidden() {}
-
-DownloadUIAdapter::ItemInfo::~ItemInfo() {}
-
DownloadUIAdapter::DownloadUIAdapter(
OfflineContentAggregator* aggregator,
OfflinePageModel* model,
@@ -95,11 +75,14 @@ DownloadUIAdapter::DownloadUIAdapter(
request_coordinator_(request_coordinator),
thumbnail_decoder_(std::move(thumbnail_decoder)),
delegate_(std::move(delegate)),
- state_(State::NOT_LOADED),
weak_ptr_factory_(this) {
delegate_->SetUIAdapter(this);
if (aggregator_)
aggregator_->RegisterProvider(kOfflinePageNamespace, this);
+ if (model_)
+ model_->AddObserver(this);
+ if (request_coordinator_)
+ request_coordinator_->AddObserver(this);
}
DownloadUIAdapter::~DownloadUIAdapter() {
@@ -127,25 +110,53 @@ void DownloadUIAdapter::OfflinePageModelLoaded(OfflinePageModel* model) {
// This signal is not used here.
}
+// OfflinePageModel::Observer
void DownloadUIAdapter::OfflinePageAdded(OfflinePageModel* model,
const OfflinePageItem& added_page) {
DCHECK(model == model_);
if (!delegate_->IsVisibleInUI(added_page.client_id))
return;
- bool temporarily_hidden =
- delegate_->IsTemporarilyHiddenInUI(added_page.client_id);
bool is_suggested = model->GetPolicyController()->IsSuggested(
added_page.client_id.name_space);
- AddItemHelper(
- std::make_unique<ItemInfo>(added_page, temporarily_hidden, is_suggested));
+
+ OfflineItem offline_item(
+ OfflineItemConversions::CreateOfflineItem(added_page, is_suggested));
+
+ // We assume the pages which are non-suggested and shown in Download Home UI
+ // should be coming from requests, so their corresponding offline items should
+ // have been added to the UI when their corresponding requests were created.
+ // So OnItemUpdated is used for non-suggested pages.
+ // Otherwise, for pages of suggested articles, they'll be added to the UI
+ // since they're added to Offline Page database directly, so OnItemsAdded is
+ // used.
+ for (auto& observer : observers_) {
+ if (!is_suggested)
+ observer.OnItemUpdated(offline_item);
+ else
+ observer.OnItemsAdded({offline_item});
+ }
}
+// OfflinePageModel::Observer
void DownloadUIAdapter::OfflinePageDeleted(
const OfflinePageModel::DeletedPageInfo& page_info) {
if (!delegate_->IsVisibleInUI(page_info.client_id))
return;
- DeleteItemHelper(page_info.client_id.id);
+
+ for (auto& observer : observers_) {
+ observer.OnItemRemoved(
+ ContentId(kOfflinePageNamespace, page_info.client_id.id));
+ }
+}
+
+// OfflinePageModel::Observer
+void DownloadUIAdapter::ThumbnailAdded(OfflinePageModel* model,
+ const OfflinePageThumbnail& thumbnail) {
+ model_->GetPageByOfflineId(
+ thumbnail.offline_id,
+ base::BindOnce(&DownloadUIAdapter::OnPageGetForThumbnailAdded,
+ weak_ptr_factory_.GetWeakPtr()));
}
// RequestCoordinator::Observer
@@ -153,9 +164,11 @@ void DownloadUIAdapter::OnAdded(const SavePageRequest& added_request) {
if (!delegate_->IsVisibleInUI(added_request.client_id()))
return;
- bool temporarily_hidden =
- delegate_->IsTemporarilyHiddenInUI(added_request.client_id());
- AddItemHelper(std::make_unique<ItemInfo>(added_request, temporarily_hidden));
+ OfflineItem offline_item(
+ OfflineItemConversions::CreateOfflineItem(added_request));
+
+ for (auto& observer : observers_)
+ observer.OnItemsAdded({offline_item});
}
// RequestCoordinator::Observer
@@ -165,11 +178,26 @@ void DownloadUIAdapter::OnCompleted(
if (!delegate_->IsVisibleInUI(request.client_id()))
return;
- // If request completed successfully, report ItemUpdated when a page is added
- // to the model. If the request failed, tell UI that the item is gone.
- if (status == RequestNotifier::BackgroundSavePageResult::SUCCESS)
+ if (delegate_->MaybeSuppressNotification(request.request_origin(),
+ request.client_id())) {
return;
- DeleteItemHelper(request.client_id().id);
+ }
+
+ OfflineItem item = OfflineItemConversions::CreateOfflineItem(request);
+ if (status == RequestNotifier::BackgroundSavePageResult::SUCCESS) {
+ // If the request is completed successfully, it means there should already
+ // be a OfflinePageAdded fired. So doing nothing in this case.
+ } else if (status ==
+ RequestNotifier::BackgroundSavePageResult::USER_CANCELED ||
+ status == RequestNotifier::BackgroundSavePageResult::
+ DOWNLOAD_THROTTLED) {
+ for (auto& observer : observers_)
+ observer.OnItemRemoved(item.id);
+ } else {
+ item.state = offline_items_collection::OfflineItemState::FAILED;
+ for (auto& observer : observers_)
+ observer.OnItemUpdated(item);
+ }
}
// RequestCoordinator::Observer
@@ -177,95 +205,53 @@ void DownloadUIAdapter::OnChanged(const SavePageRequest& request) {
if (!delegate_->IsVisibleInUI(request.client_id()))
return;
- std::string guid = request.client_id().id;
-
- // There is a chance that when OnChanged comes from RequestCoordinator,
- // the item has already been downloaded and this update would cause an
- // incorrect "in progress" state to be shown in UI.
- bool page_already_added =
- items_.find(guid) != items_.end() && !items_[guid]->is_request;
- if (page_already_added)
- return;
-
- bool temporarily_hidden =
- delegate_->IsTemporarilyHiddenInUI(request.client_id());
- items_[guid] = std::make_unique<ItemInfo>(request, temporarily_hidden);
-
- if (state_ != State::LOADED)
- return;
-
- const OfflineItem& offline_item = *(items_[guid]->ui_item);
+ OfflineItem offline_item(OfflineItemConversions::CreateOfflineItem(request));
for (OfflineContentProvider::Observer& observer : observers_)
observer.OnItemUpdated(offline_item);
}
+// RequestCoordinator::Observer
void DownloadUIAdapter::OnNetworkProgress(const SavePageRequest& request,
int64_t received_bytes) {
- if (state_ != State::LOADED)
- return;
-
- for (auto& item : items_) {
- if (item.second->is_request &&
- item.second->offline_id == request.request_id()) {
- if (received_bytes == item.second->ui_item->received_bytes)
- return;
-
- item.second->ui_item->received_bytes = received_bytes;
- for (auto& observer : observers_)
- observer.OnItemUpdated(*(item.second->ui_item));
- return;
- }
- }
-}
-
-void DownloadUIAdapter::TemporaryHiddenStatusChanged(
- const ClientId& client_id) {
- if (state_ != State::LOADED)
+ if (!delegate_->IsVisibleInUI(request.client_id()))
return;
- bool hidden = delegate_->IsTemporarilyHiddenInUI(client_id);
-
- for (const auto& item : items_) {
- if (item.second->client_id == client_id) {
- if (item.second->temporarily_hidden == hidden)
- continue;
- item.second->temporarily_hidden = hidden;
- if (hidden) {
- for (auto& observer : observers_)
- observer.OnItemRemoved(item.second->ui_item->id);
- } else {
- for (auto& observer : observers_) {
- observer.OnItemsAdded({*item.second->ui_item});
- }
- }
- }
- }
+ OfflineItem offline_item(OfflineItemConversions::CreateOfflineItem(request));
+ offline_item.received_bytes = received_bytes;
+ for (auto& observer : observers_)
+ observer.OnItemUpdated(offline_item);
}
void DownloadUIAdapter::GetAllItems(
OfflineContentProvider::MultipleItemCallback callback) {
- if (state_ == State::LOADED) {
- ReplyWithAllItems(std::move(callback));
- return;
- }
-
- postponed_callbacks_.emplace_back(std::move(callback));
- LoadCache();
+ std::unique_ptr<OfflineContentProvider::OfflineItemList> offline_items =
+ std::make_unique<OfflineContentProvider::OfflineItemList>();
+ model_->GetAllPages(base::BindOnce(
+ &DownloadUIAdapter::OnOfflinePagesLoaded, weak_ptr_factory_.GetWeakPtr(),
+ std::move(callback), std::move(offline_items)));
}
void DownloadUIAdapter::GetVisualsForItem(
const ContentId& id,
const VisualsCallback& visuals_callback) {
- auto it = items_.find(id.id);
- if (it == items_.end() || !thumbnail_decoder_) {
+ model_->GetPageByGuid(
+ id.id,
+ base::BindOnce(&DownloadUIAdapter::OnPageGetForVisuals,
+ weak_ptr_factory_.GetWeakPtr(), id, visuals_callback));
+}
+
+void DownloadUIAdapter::OnPageGetForVisuals(
+ const ContentId& id,
+ const VisualsCallback& visuals_callback,
+ const OfflinePageItem* page) {
+ if (!page) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(visuals_callback, id, nullptr));
return;
}
- const ItemInfo* item = it->second.get();
VisualResultCallback callback = base::BindOnce(visuals_callback, id);
- if (item->client_id.name_space == kSuggestedArticlesNamespace) {
+ if (page->client_id.name_space == kSuggestedArticlesNamespace) {
// Report PrefetchedItemHasThumbnail along with result callback.
auto report_and_callback =
[](VisualResultCallback result_callback,
@@ -280,7 +266,7 @@ void DownloadUIAdapter::GetVisualsForItem(
}
model_->GetThumbnailByOfflineId(
- item->offline_id,
+ page->offline_id,
base::BindOnce(&DownloadUIAdapter::OnThumbnailLoaded,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
@@ -312,20 +298,16 @@ void DownloadUIAdapter::OnThumbnailLoaded(
base::BindOnce(forward_visuals_lambda, std::move(callback)));
}
-void DownloadUIAdapter::ThumbnailAdded(OfflinePageModel* model,
- const OfflinePageThumbnail& thumbnail) {
- // Note, this is an O(N) lookup. Not ideal, but this method is called at most
- // 10 times a day (once per prefetch download), so it's probably not worth
- // optimizing.
- auto it =
- std::find_if(items_.cbegin(), items_.cend(),
- [&](const OfflineItems::value_type& entry) {
- return entry.second->offline_id == thumbnail.offline_id;
- });
- if (it == items_.end() || !it->second->ui_item)
+void DownloadUIAdapter::OnPageGetForThumbnailAdded(
+ const OfflinePageItem* page) {
+ if (!page)
return;
+
+ bool is_suggested =
+ model_->GetPolicyController()->IsSuggested(page->client_id.name_space);
for (auto& observer : observers_)
- observer.OnItemUpdated(*it->second->ui_item);
+ observer.OnItemUpdated(
+ OfflineItemConversions::CreateOfflineItem(*page, is_suggested));
}
// TODO(dimich): Remove this method since it is not used currently. If needed,
@@ -334,53 +316,71 @@ void DownloadUIAdapter::ThumbnailAdded(OfflinePageModel* model,
void DownloadUIAdapter::GetItemById(
const ContentId& id,
OfflineContentProvider::SingleItemCallback callback) {
+ model_->GetPageByGuid(
+ id.id,
+ base::BindOnce(&DownloadUIAdapter::OnPageGetForGetItem,
+ weak_ptr_factory_.GetWeakPtr(), id, std::move(callback)));
+}
+
+void DownloadUIAdapter::OnPageGetForGetItem(
+ const ContentId& id,
+ OfflineContentProvider::SingleItemCallback callback,
+ const OfflinePageItem* page) {
+ if (page) {
+ bool is_suggested =
+ model_->GetPolicyController()->IsSuggested(page->client_id.name_space);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback),
+ OfflineItemConversions::CreateOfflineItem(
+ *page, is_suggested)));
+ return;
+ }
+ request_coordinator_->GetAllRequests(
+ base::BindOnce(&DownloadUIAdapter::OnAllRequestsGetForGetItem,
+ weak_ptr_factory_.GetWeakPtr(), id, std::move(callback)));
+}
+
+void DownloadUIAdapter::OnAllRequestsGetForGetItem(
+ const ContentId& id,
+ OfflineContentProvider::SingleItemCallback callback,
+ std::vector<std::unique_ptr<SavePageRequest>> requests) {
base::Optional<OfflineItem> offline_item;
- if (state_ == State::LOADED) {
- OfflineItems::const_iterator it = items_.find(id.id);
- if (it != items_.end() && it->second->ui_item &&
- !delegate_->IsTemporarilyHiddenInUI(it->second->client_id)) {
- offline_item = *it->second->ui_item;
- }
+ for (const auto& request : requests) {
+ if (request->client_id().id == id.id)
+ offline_item = OfflineItemConversions::CreateOfflineItem(*request);
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), offline_item));
}
void DownloadUIAdapter::OpenItem(const ContentId& id) {
- if (state_ == State::LOADED) {
- OpenItemByGuid(id.id);
- return;
- }
-
- postponed_operations_.push_back(
- base::BindOnce(&DownloadUIAdapter::OpenItemByGuid,
- weak_ptr_factory_.GetWeakPtr(), id.id));
- LoadCache();
+ model_->GetPageByGuid(id.id,
+ base::BindOnce(&DownloadUIAdapter::OnPageGetForOpenItem,
+ weak_ptr_factory_.GetWeakPtr()));
}
-void DownloadUIAdapter::RemoveItem(const ContentId& id) {
- if (state_ == State::LOADED) {
- RemoveItemByGuid(id.id);
+void DownloadUIAdapter::OnPageGetForOpenItem(const OfflinePageItem* page) {
+ if (!page)
return;
- }
- postponed_operations_.push_back(
- base::BindOnce(&DownloadUIAdapter::RemoveItemByGuid,
- weak_ptr_factory_.GetWeakPtr(), id.id));
- LoadCache();
+ bool is_suggested =
+ model_->GetPolicyController()->IsSuggested(page->client_id.name_space);
+ OfflineItem item =
+ OfflineItemConversions::CreateOfflineItem(*page, is_suggested);
+ delegate_->OpenItem(item, page->offline_id);
}
-int64_t DownloadUIAdapter::GetOfflineIdByGuid(const std::string& guid) const {
- if (state_ != State::LOADED)
- return 0;
-
- if (deleting_item_ && deleting_item_->ui_item->id.id == guid)
- return deleting_item_->offline_id;
+void DownloadUIAdapter::RemoveItem(const ContentId& id) {
+ std::vector<ClientId> client_ids;
+ auto* policy_controller = model_->GetPolicyController();
+ for (const auto& name_space :
+ policy_controller->GetNamespacesSupportedByDownload()) {
+ client_ids.push_back(ClientId(name_space, id.id));
+ }
- OfflineItems::const_iterator it = items_.find(guid);
- if (it != items_.end())
- return it->second->offline_id;
- return 0;
+ model_->DeletePagesByClientIds(
+ client_ids, base::BindRepeating(&DownloadUIAdapter::OnDeletePagesDone,
+ weak_ptr_factory_.GetWeakPtr()));
}
void DownloadUIAdapter::CancelDownload(const ContentId& id) {
@@ -403,8 +403,8 @@ void DownloadUIAdapter::PauseDownload(const ContentId& id) {
// TODO(fgorski): Clean this up in a way where 2 round trips + GetAllRequests
// is not necessary.
request_coordinator_->GetAllRequests(
- base::Bind(&DownloadUIAdapter::PauseDownloadContinuation,
- weak_ptr_factory_.GetWeakPtr(), id.id));
+ base::BindOnce(&DownloadUIAdapter::PauseDownloadContinuation,
+ weak_ptr_factory_.GetWeakPtr(), id.id));
}
void DownloadUIAdapter::PauseDownloadContinuation(
@@ -420,8 +420,8 @@ void DownloadUIAdapter::ResumeDownload(const ContentId& id,
// is not necessary.
if (has_user_gesture) {
request_coordinator_->GetAllRequests(
- base::Bind(&DownloadUIAdapter::ResumeDownloadContinuation,
- weak_ptr_factory_.GetWeakPtr(), id.id));
+ base::BindOnce(&DownloadUIAdapter::ResumeDownloadContinuation,
+ weak_ptr_factory_.GetWeakPtr(), id.id));
} else {
request_coordinator_->StartImmediateProcessing(base::DoNothing());
}
@@ -434,197 +434,43 @@ void DownloadUIAdapter::ResumeDownloadContinuation(
std::move(requests), guid, request_coordinator_->GetPolicyController()));
}
-// Note that several LoadCache calls may be issued before the async GetAllPages
-// comes back.
-void DownloadUIAdapter::LoadCache() {
- if (state_ != State::NOT_LOADED)
- return;
- state_ = State::LOADING_PAGES;
- model_->GetAllPages(base::BindOnce(&DownloadUIAdapter::OnOfflinePagesLoaded,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-// TODO(dimich): Start clearing this cache on UI close. Also, after OpenItem can
-// done without loading all items from database.
-void DownloadUIAdapter::ClearCache() {
- // Once loaded, this class starts to observe the model. Only remove observer
- // if it was added.
- if (state_ == State::LOADED) {
- model_->RemoveObserver(this);
- request_coordinator_->RemoveObserver(this);
- }
- items_.clear();
- state_ = State::NOT_LOADED;
- TRACE_EVENT_ASYNC_END0("offline_pages", "DownloadUIAdapter: items cached",
- this);
-}
-
void DownloadUIAdapter::OnOfflinePagesLoaded(
+ OfflineContentProvider::MultipleItemCallback callback,
+ std::unique_ptr<OfflineContentProvider::OfflineItemList> offline_items,
const MultipleOfflinePageItemResult& pages) {
- // If multiple observers register quickly, the cache might be already loaded
- // by the previous LoadCache call. At the same time, if all observers already
- // left, there is no reason to populate the cache.
- if (state_ != State::LOADING_PAGES)
- return;
for (const auto& page : pages) {
if (delegate_->IsVisibleInUI(page.client_id)) {
std::string guid = page.client_id.id;
- DCHECK(items_.find(guid) == items_.end());
- bool temporarily_hidden =
- delegate_->IsTemporarilyHiddenInUI(page.client_id);
bool is_suggested =
model_->GetPolicyController()->IsSuggested(page.client_id.name_space);
- std::unique_ptr<ItemInfo> item =
- std::make_unique<ItemInfo>(page, temporarily_hidden, is_suggested);
- items_[guid] = std::move(item);
+ offline_items->push_back(
+ OfflineItemConversions::CreateOfflineItem(page, is_suggested));
}
}
- model_->AddObserver(this);
-
- state_ = State::LOADING_REQUESTS;
- request_coordinator_->GetAllRequests(base::Bind(
- &DownloadUIAdapter::OnRequestsLoaded, weak_ptr_factory_.GetWeakPtr()));
+ request_coordinator_->GetAllRequests(base::BindOnce(
+ &DownloadUIAdapter::OnRequestsLoaded, weak_ptr_factory_.GetWeakPtr(),
+ std::move(callback), std::move(offline_items)));
}
void DownloadUIAdapter::OnRequestsLoaded(
+ OfflineContentProvider::MultipleItemCallback callback,
+ std::unique_ptr<OfflineContentProvider::OfflineItemList> offline_items,
std::vector<std::unique_ptr<SavePageRequest>> requests) {
- // If multiple observers register quickly, the cache might be already loaded
- // by the previous LoadCache call. At the same time, if all observers already
- // left, there is no reason to populate the cache.
- if (state_ != State::LOADING_REQUESTS)
- return;
-
for (const auto& request : requests) {
if (delegate_->IsVisibleInUI(request->client_id())) {
std::string guid = request->client_id().id;
- DCHECK(items_.find(guid) == items_.end());
- bool temporarily_hidden =
- delegate_->IsTemporarilyHiddenInUI(request->client_id());
- std::unique_ptr<ItemInfo> item =
- std::make_unique<ItemInfo>(*request, temporarily_hidden);
- items_[guid] = std::move(item);
+ offline_items->push_back(
+ OfflineItemConversions::CreateOfflineItem(*request.get()));
}
}
- request_coordinator_->AddObserver(this);
-
- state_ = State::LOADED;
- TRACE_EVENT_ASYNC_BEGIN1("offline_pages", "DownloadUIAdapter: items cached",
- this, "initial count", items_.size());
-
- // If there are callers waiting for GetAllItems callback, call them.
- for (auto& callback : postponed_callbacks_) {
- ReplyWithAllItems(std::move(callback));
- }
- postponed_callbacks_.clear();
- // If there were requests to perform operations on items before cache was
- // loaded, perform them now.
- for (auto& operation : postponed_operations_) {
- std::move(operation).Run();
- }
- postponed_operations_.clear();
+ OfflineContentProvider::OfflineItemList list = *offline_items;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), list));
}
void DownloadUIAdapter::OnDeletePagesDone(DeletePageResult result) {
// TODO(dimich): Consider adding UMA to record user actions.
}
-void DownloadUIAdapter::AddItemHelper(std::unique_ptr<ItemInfo> item_info) {
- const std::string& guid = item_info->ui_item->id.id;
-
- OfflineItems::const_iterator it = items_.find(guid);
- // In case when request is completed and morphed into a page, this comes as
- // new page added and request completed. We ignore request completion
- // notification and when page is added, fire 'updated' instead of 'added'.
- bool request_to_page_transition =
- (it != items_.end() && it->second->is_request && !item_info->is_request);
-
- items_[guid] = std::move(item_info);
-
- if (state_ != State::LOADED)
- return;
-
- OfflineItem* offline_item = items_[guid]->ui_item.get();
-
- if (request_to_page_transition) {
- offline_item->state = offline_items_collection::OfflineItemState::COMPLETE;
- offline_item->progress.value = 100;
- offline_item->progress.max = 100L;
- offline_item->progress.unit =
- offline_items_collection::OfflineItemProgressUnit::PERCENTAGE;
- if (!items_[guid]->temporarily_hidden) {
- for (auto& observer : observers_)
- observer.OnItemUpdated(*offline_item);
- }
- } else {
- if (!items_[guid]->temporarily_hidden) {
- std::vector<OfflineItem> items(1, *offline_item);
- for (auto& observer : observers_)
- observer.OnItemsAdded(items);
- }
- }
-}
-
-void DownloadUIAdapter::DeleteItemHelper(const std::string& guid) {
- OfflineItems::iterator it = items_.find(guid);
- if (it == items_.end())
- return;
- DCHECK(deleting_item_ == nullptr);
- deleting_item_ = std::move(it->second);
- items_.erase(it);
-
- if (!deleting_item_->temporarily_hidden && state_ == State::LOADED) {
- for (auto& observer : observers_)
- observer.OnItemRemoved(ContentId(kOfflinePageNamespace, guid));
- }
-
- deleting_item_.reset();
-}
-
-void DownloadUIAdapter::ReplyWithAllItems(
- OfflineContentProvider::MultipleItemCallback callback) {
- std::vector<OfflineItem> items;
- for (const auto& item : items_) {
- if (delegate_->IsTemporarilyHiddenInUI(item.second->client_id))
- continue;
- items.push_back(*(item.second->ui_item));
- }
-
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback), items));
-}
-
-void DownloadUIAdapter::OpenItemByGuid(const std::string& guid) {
- if (state_ != State::LOADED) {
- return;
- }
-
- OfflineItems::const_iterator it = items_.find(guid);
- if (it == items_.end())
- return;
-
- const OfflineItem* item = it->second->ui_item.get();
- if (!item)
- return;
-
- delegate_->OpenItem(*item, GetOfflineIdByGuid(guid));
-}
-
-void DownloadUIAdapter::RemoveItemByGuid(const std::string& guid) {
- if (state_ != State::LOADED) {
- return;
- }
-
- OfflineItems::const_iterator it = items_.find(guid);
- if (it == items_.end())
- return;
-
- std::vector<int64_t> page_ids;
- page_ids.push_back(it->second->offline_id);
-
- model_->DeletePagesByOfflineId(
- page_ids, base::BindRepeating(&DownloadUIAdapter::OnDeletePagesDone,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/downloads/download_ui_adapter.h b/chromium/components/offline_pages/core/downloads/download_ui_adapter.h
index 2457a495cd8..ab1e738b104 100644
--- a/chromium/components/offline_pages/core/downloads/download_ui_adapter.h
+++ b/chromium/components/offline_pages/core/downloads/download_ui_adapter.h
@@ -51,12 +51,6 @@ class DownloadUIAdapter : public OfflineContentProvider,
// be visible in the collection of items exposed by this Adapter. This also
// indicates if Observers will be notified about changes for the given page.
virtual bool IsVisibleInUI(const ClientId& client_id) = 0;
- // Sometimes the item should be in the collection but not visible in the UI,
- // temporarily. This is a relatively special case, for example for Last_N
- // snapshots that are only valid while their tab is alive. When the status
- // of temporary visibility changes, the Delegate is supposed to call
- // DownloadUIAdapter::TemporarilyHiddenStatusChanged().
- virtual bool IsTemporarilyHiddenInUI(const ClientId& client_id) = 0;
// Delegates need a reference to the UI adapter in order to notify it about
// visibility changes.
@@ -64,6 +58,11 @@ class DownloadUIAdapter : public OfflineContentProvider,
// Opens an offline item.
virtual void OpenItem(const OfflineItem& item, int64_t offline_id) = 0;
+
+ // Suppresses the download complete notification
+ // depending on flags and origin.
+ virtual bool MaybeSuppressNotification(const std::string& origin,
+ const ClientId& id) = 0;
};
// Create the adapter. thumbnail_decoder may be null, in which case,
@@ -80,8 +79,6 @@ class DownloadUIAdapter : public OfflineContentProvider,
std::unique_ptr<DownloadUIAdapter> adapter,
OfflinePageModel* model);
- int64_t GetOfflineIdByGuid(const std::string& guid) const;
-
// OfflineContentProvider implementation.
void OpenItem(const ContentId& id) override;
void RemoveItem(const ContentId& id) override;
@@ -115,56 +112,12 @@ class DownloadUIAdapter : public OfflineContentProvider,
void OnNetworkProgress(const SavePageRequest& request,
int64_t received_bytes) override;
- // For the DownloadUIAdapter::Delegate, to report the temporary hidden status
- // change.
- void TemporaryHiddenStatusChanged(const ClientId& client_id);
-
Delegate* delegate() { return delegate_.get(); }
- // Test method, to verify the internal cache is not loaded too early.
- bool IsCacheLoadedForTest() { return (state_ != State::NOT_LOADED); }
-
private:
- enum class State { NOT_LOADED, LOADING_PAGES, LOADING_REQUESTS, LOADED };
-
- struct ItemInfo {
- ItemInfo(const OfflinePageItem& page,
- bool temporarily_hidden,
- bool is_suggested);
- ItemInfo(const SavePageRequest& request, bool temporarily_hidden);
- ~ItemInfo();
-
- std::unique_ptr<OfflineItem> ui_item;
-
- // Additional cached data, not exposed to UI through OfflineItem.
- // Indicates if this item wraps the completed page or in-progress request.
- bool is_request;
-
- // These are shared between pages and requests.
- int64_t offline_id;
-
- // ClientId is here to support the Delegate that can toggle temporary
- // visibility of the items in the collection.
- ClientId client_id;
-
- // This item is present in the collection but temporarily hidden from UI.
- // This is useful when unrelated reasons cause the UI item to be excluded
- // (filtered out) from UI. When item becomes temporarily hidden the adapter
- // issues ItemDeleted notification to observers, and ItemAdded when it
- // becomes visible again.
- bool temporarily_hidden;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ItemInfo);
- };
-
- typedef std::map<std::string, std::unique_ptr<ItemInfo>> OfflineItems;
using VisualResultCallback = base::OnceCallback<void(
std::unique_ptr<offline_items_collection::OfflineItemVisuals>)>;
- void LoadCache();
- void ClearCache();
-
// Task callbacks.
void CancelDownloadContinuation(
const std::string& guid,
@@ -175,23 +128,33 @@ class DownloadUIAdapter : public OfflineContentProvider,
void ResumeDownloadContinuation(
const std::string& guid,
std::vector<std::unique_ptr<SavePageRequest>> requests);
- void OnOfflinePagesLoaded(const MultipleOfflinePageItemResult& pages);
+ void OnOfflinePagesLoaded(
+ OfflineContentProvider::MultipleItemCallback callback,
+ std::unique_ptr<OfflineContentProvider::OfflineItemList> offline_items,
+ const MultipleOfflinePageItemResult& pages);
void OnThumbnailLoaded(VisualResultCallback callback,
std::unique_ptr<OfflinePageThumbnail> thumbnail);
- void OnRequestsLoaded(std::vector<std::unique_ptr<SavePageRequest>> requests);
-
- void OnDeletePagesDone(DeletePageResult result);
+ void OnRequestsLoaded(
+ OfflineContentProvider::MultipleItemCallback callback,
+ std::unique_ptr<OfflineContentProvider::OfflineItemList> offline_items,
+ std::vector<std::unique_ptr<SavePageRequest>> requests);
+ void OnPageGetForVisuals(const ContentId& id,
+ const VisualsCallback& visuals_callback,
+ const OfflinePageItem* page);
+ void OnPageGetForGetItem(const ContentId& id,
+ OfflineContentProvider::SingleItemCallback callback,
+ const OfflinePageItem* page);
+ void OnAllRequestsGetForGetItem(
+ const ContentId& id,
+ OfflineContentProvider::SingleItemCallback callback,
+ std::vector<std::unique_ptr<SavePageRequest>> requests);
- void AddItemHelper(std::unique_ptr<ItemInfo> item_info);
- // This function is not re-entrant. It temporarily sets |deleting_item_|
- // while it runs, so that functions such as |GetOfflineIdByGuid| will work
- // during the |ItemDeleted| callback.
- void DeleteItemHelper(const std::string& guid);
+ void OnPageGetForOpenItem(const OfflinePageItem* page);
+ void OnPageGetForThumbnailAdded(const OfflinePageItem* page);
- void ReplyWithAllItems(OfflineContentProvider::MultipleItemCallback callback);
+ void OnDeletePagesDone(DeletePageResult result);
void OpenItemByGuid(const std::string& guid);
- void RemoveItemByGuid(const std::string& guid);
// A valid offline content aggregator, supplied at construction.
OfflineContentAggregator* aggregator_;
@@ -208,21 +171,6 @@ class DownloadUIAdapter : public OfflineContentProvider,
// A delegate, supplied at construction.
std::unique_ptr<Delegate> delegate_;
- State state_;
-
- // The cache of UI items. The key is OfflineItem.guid.
- OfflineItems items_;
-
- // The callbacks for GetAllItems waiting for cache initialization.
- std::vector<OfflineContentProvider::MultipleItemCallback>
- postponed_callbacks_;
-
- // The requests for operations with items waiting for cache initialization.
- // std::vector<base::OnceCallback<const std::string&>> postponed_operations_;
- std::vector<base::OnceClosure> postponed_operations_;
-
- std::unique_ptr<ItemInfo> deleting_item_;
-
// The observers.
base::ObserverList<OfflineContentProvider::Observer> observers_;
diff --git a/chromium/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc b/chromium/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc
index 43a3de166dc..da517712ba9 100644
--- a/chromium/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc
+++ b/chromium/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc
@@ -19,7 +19,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -54,9 +54,10 @@ static const char kTestBadGuid[] = "ccccccc-cccc-0ccc-0ccc-ccccccccccc0";
static const ClientId kTestClientIdOtherNamespace(kLastNNamespace, kTestGuid1);
static const ClientId kTestClientIdOtherGuid(kLastNNamespace, kTestBadGuid);
static const ClientId kTestClientId1(kAsyncNamespace, kTestGuid1);
-static const ClientId kTestClientId2(kAsyncNamespace, kTestGuid2);
static const ClientId kTestClientIdPrefetch(kSuggestedArticlesNamespace,
kTestGuid1);
+static const ClientId kTestClientIdPrefetch2(kSuggestedArticlesNamespace,
+ kTestGuid2);
static const ContentId kTestContentId1(kOfflinePageNamespace, kTestGuid1);
static const base::FilePath kTestFilePath =
base::FilePath(FILE_PATH_LITERAL("foo/bar.mhtml"));
@@ -74,11 +75,6 @@ void GetItemAndVerify(const base::Optional<OfflineItem>& expected,
EXPECT_EQ(expected.value().state, actual.value().state);
}
-void GetAllItemsAndVerify(size_t expected_size,
- const std::vector<OfflineItem>& actual) {
- EXPECT_EQ(expected_size, actual.size());
-}
-
// Mock DownloadUIAdapter::Delegate
class DownloadUIAdapterDelegate : public DownloadUIAdapter::Delegate {
public:
@@ -87,14 +83,15 @@ class DownloadUIAdapterDelegate : public DownloadUIAdapter::Delegate {
// DownloadUIAdapter::Delegate
bool IsVisibleInUI(const ClientId& client_id) override { return is_visible; }
- bool IsTemporarilyHiddenInUI(const ClientId& client_id) override {
- return is_temporarily_hidden;
- }
void SetUIAdapter(DownloadUIAdapter* ui_adapter) override {}
void OpenItem(const OfflineItem& item, int64_t offline_id) override {}
+ bool MaybeSuppressNotification(const std::string& origin,
+ const ClientId& item) override {
+ return maybe_suppress_notification_;
+ }
bool is_visible = true;
- bool is_temporarily_hidden = false;
+ bool maybe_suppress_notification_ = false;
};
class MockThumbnailDecoder : public ThumbnailDecoder {
@@ -137,7 +134,7 @@ class MockOfflinePageModel : public StubOfflinePageModel {
observer_ = nullptr;
}
- // PostTask instead of just running callback to simpulate the real class.
+ // PostTask instead of just running callback to simulate the real class.
void GetAllPages(MultipleOfflinePageItemCallback callback) override {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MockOfflinePageModel::GetAllPagesImpl,
@@ -179,6 +176,28 @@ class MockOfflinePageModel : public StubOfflinePageModel {
FROM_HERE, base::BindOnce(std::move(callback), std::move(copy)));
}
+ void GetPageByOfflineId(int64_t offline_id,
+ SingleOfflinePageItemCallback callback) override {
+ for (const auto& page : pages) {
+ if (page.second.offline_id == offline_id) {
+ std::move(callback).Run(&(page.second));
+ return;
+ }
+ }
+ std::move(callback).Run(nullptr);
+ }
+
+ void GetPageByGuid(const std::string& guid,
+ SingleOfflinePageItemCallback callback) override {
+ for (const auto& page : pages) {
+ if (page.second.client_id.id == guid) {
+ std::move(callback).Run(&(page.second));
+ return;
+ }
+ }
+ std::move(callback).Run(nullptr);
+ }
+
void AddPageAndNotifyAdapter(const OfflinePageItem& page) {
EXPECT_EQ(pages.end(), pages.find(page.offline_id));
pages[page.offline_id] = page;
@@ -203,7 +222,7 @@ class MockOfflinePageModel : public StubOfflinePageModel {
// Creates mock versions for OfflinePageModel, RequestCoordinator and their
// dependencies, then passes them to DownloadUIAdapter for testing.
-// Note that initially the OfflienPageModel is not "loaded". PumpLoop() will
+// Note that initially the OfflinePageModel is not "loaded". PumpLoop() will
// load it, firing ItemsLoaded callback to the Adapter. Hence some tests
// start from PumpLoop() right away if they don't need to test this.
class DownloadUIAdapterTest : public testing::Test,
@@ -233,7 +252,6 @@ class DownloadUIAdapterTest : public testing::Test,
RequestCoordinator* request_coordinator() {
return request_coordinator_taco_->request_coordinator();
}
- bool items_loaded() { return adapter->IsCacheLoadedForTest(); }
void AddInitialPage(const ClientId client_id);
int64_t AddInitialRequest(const GURL& url, const ClientId& client_id);
@@ -308,37 +326,22 @@ int64_t DownloadUIAdapterTest::AddRequest(const GURL& url,
params.url = url;
params.client_id = client_id;
return request_coordinator()->SavePageLater(
- params, base::Bind(&SavePageLaterCallback));
+ params, base::BindOnce(&SavePageLaterCallback));
}
void DownloadUIAdapterTest::AddInitialPage(
const ClientId client_id = kTestClientId1) {
model->AddInitialPage(client_id);
- // Trigger cache load in the adapter.
- adapter->GetAllItems(base::DoNothing());
PumpLoop();
}
int64_t DownloadUIAdapterTest::AddInitialRequest(const GURL& url,
const ClientId& client_id) {
int64_t id = AddRequest(url, client_id);
- // Trigger cache load in the adapter.
- adapter->GetAllItems(base::DoNothing());
PumpLoop();
return id;
}
-TEST_F(DownloadUIAdapterTest, InitialLoad) {
- EXPECT_NE(nullptr, adapter.get());
- EXPECT_FALSE(items_loaded());
- AddInitialPage();
- EXPECT_TRUE(items_loaded());
- OfflineItem item(kTestContentId1);
- adapter->GetItemById(kTestContentId1,
- base::BindOnce(&GetItemAndVerify, item));
- PumpLoop();
-}
-
TEST_F(DownloadUIAdapterTest, InitialItemConversion) {
AddInitialPage();
EXPECT_EQ(1UL, model->pages.size());
@@ -362,7 +365,7 @@ TEST_F(DownloadUIAdapterTest, InitialItemConversion) {
TEST_F(DownloadUIAdapterTest, ItemDeletedAdded) {
AddInitialPage();
// Add page, notify adapter.
- OfflinePageItem page(GURL(kTestUrl), kTestOfflineId2, kTestClientId2,
+ OfflinePageItem page(GURL(kTestUrl), kTestOfflineId2, kTestClientIdPrefetch2,
base::FilePath(kTestFilePath), kFileSize,
kTestCreationTime);
model->AddPageAndNotifyAdapter(page);
@@ -390,58 +393,30 @@ TEST_F(DownloadUIAdapterTest, NotVisibleItem) {
EXPECT_EQ(0UL, added_guids.size());
}
-TEST_F(DownloadUIAdapterTest, TemporarilyNotVisibleItem) {
- adapter_delegate->is_temporarily_hidden = true;
- AddInitialPage();
- // Initial Item should be invisible in the collection now.
- adapter->GetItemById(kTestContentId1,
- base::BindOnce(&GetItemAndVerify, base::nullopt));
- adapter->GetAllItems(base::BindOnce(&GetAllItemsAndVerify, 0UL));
+TEST_F(DownloadUIAdapterTest, PageInvisibleOnUIAdded) {
+ // Add a new page which should not be shown in UI.
+ adapter_delegate->is_visible = false;
+ OfflinePageItem page(
+ GURL(kTestUrl), kTestOfflineId1, kTestClientIdOtherNamespace,
+ base::FilePath(kTestFilePath), kFileSize, kTestCreationTime);
+ model->AddPageAndNotifyAdapter(page);
PumpLoop();
-
EXPECT_EQ(0UL, added_guids.size());
- EXPECT_EQ(0UL, deleted_guids.size());
-
- adapter_delegate->is_temporarily_hidden = false;
- // Notify adapter about visibility change for the clientId of initial page.
- adapter->TemporaryHiddenStatusChanged(kTestClientId1);
- PumpLoop();
-
- // There should be OnAdded simulated.
- EXPECT_EQ(1UL, added_guids.size());
- EXPECT_EQ(0UL, deleted_guids.size());
- // Also the item should be visible in the collection of items now.
- OfflineItem item(kTestContentId1);
- adapter->GetItemById(kTestContentId1,
- base::BindOnce(&GetItemAndVerify, item));
- adapter->GetAllItems(base::BindOnce(&GetAllItemsAndVerify, 1UL));
- PumpLoop();
-
- // Switch visibility back to hidden
- adapter_delegate->is_temporarily_hidden = true;
- adapter->TemporaryHiddenStatusChanged(kTestClientId1);
- // There should be OnDeleted fired.
- EXPECT_EQ(1UL, added_guids.size());
- EXPECT_EQ(1UL, deleted_guids.size());
- // Also the item should be visible in the collection of items now.
- adapter->GetItemById(kTestContentId1,
- base::BindOnce(&GetItemAndVerify, base::nullopt));
- adapter->GetAllItems(base::BindOnce(&GetAllItemsAndVerify, 0UL));
- PumpLoop();
+ // TODO(dimich): we currently don't report updated items since OPM doesn't
+ // have support for that. Add as needed, this will have to be updated when
+ // support is added.
+ EXPECT_EQ(0UL, updated_guids.size());
}
-TEST_F(DownloadUIAdapterTest, ItemAdded) {
- AddInitialPage();
- // Clear the initial page and replace it with updated one.
- model->pages.clear();
- // Add a new page which did not exist before.
- OfflinePageItem page2(GURL(kTestUrl), kTestOfflineId2, kTestClientId2,
- base::FilePath(kTestFilePath), kFileSize,
- kTestCreationTime);
- model->AddPageAndNotifyAdapter(page2);
+TEST_F(DownloadUIAdapterTest, PageVisibleOnUIAdded) {
+ // Add a new page which should be shown in UI.
+ OfflinePageItem page(GURL(kTestUrl), kTestOfflineId1, kTestClientIdPrefetch,
+ base::FilePath(kTestFilePath), kFileSize,
+ kTestCreationTime);
+ model->AddPageAndNotifyAdapter(page);
PumpLoop();
EXPECT_EQ(1UL, added_guids.size());
- EXPECT_EQ(kTestGuid2, added_guids[0]);
+ EXPECT_EQ(kTestGuid1, added_guids[0]);
// TODO(dimich): we currently don't report updated items since OPM doesn't
// have support for that. Add as needed, this will have to be updated when
// support is added.
@@ -450,7 +425,6 @@ TEST_F(DownloadUIAdapterTest, ItemAdded) {
TEST_F(DownloadUIAdapterTest, LoadExistingRequest) {
AddInitialRequest(GURL(kTestUrl), kTestClientId1);
- EXPECT_TRUE(items_loaded());
OfflineItem item(kTestContentId1);
item.state = OfflineItemState::IN_PROGRESS;
adapter->GetItemById(kTestContentId1,
@@ -459,10 +433,8 @@ TEST_F(DownloadUIAdapterTest, LoadExistingRequest) {
}
TEST_F(DownloadUIAdapterTest, AddRequest) {
- AddInitialPage();
- EXPECT_TRUE(items_loaded());
- EXPECT_EQ(0UL, added_guids.size());
AddRequest(GURL(kTestUrl), kTestClientId1);
+ EXPECT_EQ(0UL, added_guids.size());
PumpLoop();
EXPECT_EQ(1UL, added_guids.size());
EXPECT_EQ(kTestClientId1.id, added_guids[0]);
@@ -475,8 +447,7 @@ TEST_F(DownloadUIAdapterTest, AddRequest) {
TEST_F(DownloadUIAdapterTest, RemoveRequest) {
int64_t id = AddInitialRequest(GURL(kTestUrl), kTestClientId1);
- // No added requests, the initial one is loaded.
- EXPECT_EQ(0UL, added_guids.size());
+ EXPECT_EQ(1UL, added_guids.size());
OfflineItem item(kTestContentId1);
item.state = OfflineItemState::IN_PROGRESS;
adapter->GetItemById(kTestContentId1,
@@ -486,7 +457,7 @@ TEST_F(DownloadUIAdapterTest, RemoveRequest) {
std::vector<int64_t> requests_to_remove = {id};
request_coordinator()->RemoveRequests(
requests_to_remove,
- base::Bind(
+ base::BindOnce(
[](int64_t id, const MultipleItemStatuses& statuses) {
EXPECT_EQ(1UL, statuses.size());
EXPECT_EQ(id, statuses[0].first);
@@ -495,7 +466,7 @@ TEST_F(DownloadUIAdapterTest, RemoveRequest) {
id));
PumpLoop();
- EXPECT_EQ(0UL, added_guids.size());
+ EXPECT_EQ(1UL, added_guids.size());
EXPECT_EQ(1UL, deleted_guids.size());
EXPECT_EQ(kTestClientId1.id, deleted_guids[0]);
adapter->GetItemById(kTestContentId1,
@@ -504,9 +475,6 @@ TEST_F(DownloadUIAdapterTest, RemoveRequest) {
}
TEST_F(DownloadUIAdapterTest, PauseAndResume) {
- AddInitialPage();
- EXPECT_TRUE(items_loaded());
-
AddRequest(GURL(kTestUrl), kTestClientId1);
PumpLoop();
@@ -575,35 +543,23 @@ TEST_F(DownloadUIAdapterTest, RequestBecomesPage) {
offliner_stub->enable_callback(true);
AddInitialRequest(GURL(kTestUrl), kTestClientId1);
- OfflineItem item(kTestContentId1);
-
- // The item is still IN_PROGRESS, since we did not delete it when
- // request is competed successfully, waiting for the page with the
- // same client_id to come in.
- item.state = OfflineItemState::IN_PROGRESS;
- {
- SCOPED_TRACE("IN_PROGRESS");
- adapter->GetItemById(kTestContentId1,
- base::BindOnce(&GetItemAndVerify, item));
- PumpLoop();
- }
+ EXPECT_EQ(1UL, added_guids.size());
// Add a new saved page with the same client id.
- // This simulates what happens when the request is completed.
- // It should not fire and OnAdded or OnDeleted, just OnUpdated.
+ // This simulates what happens when the page is added after the request is
+ // completed.
OfflinePageItem page(GURL(kTestUrl), kTestOfflineId1, kTestClientId1,
base::FilePath(kTestFilePath), kFileSize,
kTestCreationTime);
model->AddPageAndNotifyAdapter(page);
PumpLoop();
- // No added or deleted items, the one existing item should be 'updated'.
- EXPECT_EQ(0UL, added_guids.size());
- EXPECT_EQ(0UL, deleted_guids.size());
+ EXPECT_EQ(1UL, added_guids.size());
+ // 3 updates: OnChanged for starting request, OnNetworkProgress and
+ // OnComplete.
+ EXPECT_EQ(3UL, updated_guids.size());
- EXPECT_GE(updated_guids.size(), 1UL);
- std::string last_updated_guid = updated_guids[updated_guids.size() - 1];
- item.id = ContentId(kOfflinePageNamespace, last_updated_guid);
+ OfflineItem item(kTestContentId1);
item.state = OfflineItemState::COMPLETE;
{
SCOPED_TRACE("COMPLETE");
@@ -613,23 +569,6 @@ TEST_F(DownloadUIAdapterTest, RequestBecomesPage) {
}
}
-TEST_F(DownloadUIAdapterTest, UpdateProgress) {
- offliner_stub->enable_callback(true);
- AddInitialRequest(GURL(kTestUrl), kTestClientId1);
-
- auto callback = [](const base::Optional<OfflineItem>& item) {
- ASSERT_TRUE(item.has_value());
- EXPECT_GT(item.value().received_bytes, 0LL);
- };
- adapter->GetItemById(kTestContentId1, base::BindOnce(callback));
-
- // Updated 2 times - with progress and to 'completed'.
- EXPECT_EQ(2UL, updated_guids.size());
- EXPECT_EQ(kTestGuid1, updated_guids[0]);
- EXPECT_EQ(kTestGuid1, updated_guids[1]);
- PumpLoop();
-}
-
TEST_F(DownloadUIAdapterTest, GetVisualsForItem) {
AddInitialPage(kTestClientIdPrefetch);
model->thumbnail_by_offline_id_result =
@@ -650,9 +589,8 @@ TEST_F(DownloadUIAdapterTest, GetVisualsForItem) {
EXPECT_EQ(kImageWidth, visuals->icon.Width());
called = true;
});
- adapter->GetAllItems(base::DoNothing());
- base::HistogramTester histogram_tester;
+ base::HistogramTester histogram_tester;
adapter->GetVisualsForItem(kTestContentId1, callback);
PumpLoop();
@@ -675,7 +613,6 @@ TEST_F(DownloadUIAdapterTest, GetVisualsForItemInvalidItem) {
EXPECT_FALSE(visuals);
called = true;
});
- adapter->GetAllItems(base::DoNothing());
base::HistogramTester histogram_tester;
adapter->GetVisualsForItem(kContentID, callback);
@@ -728,7 +665,6 @@ TEST_F(DownloadUIAdapterTest, GetVisualsForItemBadDecode) {
EXPECT_FALSE(visuals);
called = true;
});
- adapter->GetAllItems(base::DoNothing());
base::HistogramTester histogram_tester;
adapter->GetVisualsForItem(kTestContentId1, callback);
@@ -743,13 +679,12 @@ TEST_F(DownloadUIAdapterTest, ThumbnailAddedUpdatesItem) {
// Add an item without a thumbnail. Then notify the adapter about the added
// thumbnail. It should notify the delegate about the updated item.
AddInitialPage();
- adapter->GetAllItems(base::DoNothing());
PumpLoop();
updated_guids.clear();
OfflinePageThumbnail thumb;
thumb.offline_id = kTestOfflineId1;
- adapter->ThumbnailAdded(nullptr, thumb);
+ adapter->ThumbnailAdded(model.get(), thumb);
EXPECT_EQ(std::vector<std::string>{kTestGuid1}, updated_guids);
}
@@ -763,9 +698,10 @@ TEST_F(DownloadUIAdapterTest, ThumbnailAddedItemNotFound) {
OfflinePageThumbnail thumb;
thumb.offline_id = 958120;
- adapter->ThumbnailAdded(nullptr, thumb);
+ adapter->ThumbnailAdded(model.get(), thumb);
EXPECT_EQ(std::vector<std::string>{}, updated_guids);
}
+
} // namespace
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/downloads/offline_item_conversions.cc b/chromium/components/offline_pages/core/downloads/offline_item_conversions.cc
index 1e638213878..fc6e922981e 100644
--- a/chromium/components/offline_pages/core/downloads/offline_item_conversions.cc
+++ b/chromium/components/offline_pages/core/downloads/offline_item_conversions.cc
@@ -60,6 +60,8 @@ OfflineItem OfflineItemConversions::CreateOfflineItem(
item.progress.max = 100;
item.progress.unit = OfflineItemProgressUnit::PERCENTAGE;
item.is_suggested = is_suggested;
+ item.is_openable = true;
+ item.externally_removed = page.file_missing_time != base::Time();
return item;
}
diff --git a/chromium/components/offline_pages/core/downloads/offline_item_conversions_unittest.cc b/chromium/components/offline_pages/core/downloads/offline_item_conversions_unittest.cc
index 910cbc6f26e..218d7443e58 100644
--- a/chromium/components/offline_pages/core/downloads/offline_item_conversions_unittest.cc
+++ b/chromium/components/offline_pages/core/downloads/offline_item_conversions_unittest.cc
@@ -47,7 +47,7 @@ TEST(OfflineItemConversionsTest, OfflinePageItemConversion) {
EXPECT_EQ(creation_time, offline_item.creation_time);
EXPECT_EQ(last_access_time, offline_item.last_accessed_time);
EXPECT_EQ(file_size, offline_item.total_size_bytes);
- EXPECT_EQ("text/html", offline_item.mime_type);
+ EXPECT_EQ("multipart/related", offline_item.mime_type);
EXPECT_EQ(OfflineItemFilter::FILTER_PAGE, offline_item.filter);
EXPECT_EQ(OfflineItemState::COMPLETE, offline_item.state);
EXPECT_EQ(100, offline_item.progress.value);
@@ -55,19 +55,21 @@ TEST(OfflineItemConversionsTest, OfflinePageItemConversion) {
EXPECT_EQ(100, offline_item.progress.max.value());
EXPECT_EQ(OfflineItemProgressUnit::PERCENTAGE, offline_item.progress.unit);
EXPECT_TRUE(offline_item.is_suggested);
+ EXPECT_TRUE(offline_item.is_openable);
+ EXPECT_FALSE(offline_item.externally_removed);
// Enabled P2P sharing and flag the item as suggested when creating the
// OfflineItem. Then check that only the mime type is and is_suggested
// information changed.
base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(kOfflinePagesSharingFeature);
+ scoped_feature_list.InitAndDisableFeature(kOfflinePagesSharingFeature);
OfflineItem offline_item_p2p =
OfflineItemConversions::CreateOfflineItem(offline_page_item, false);
- EXPECT_EQ("multipart/related", offline_item_p2p.mime_type);
+ EXPECT_EQ("text/html", offline_item_p2p.mime_type);
EXPECT_FALSE(offline_item_p2p.is_suggested);
// Change offline_item_p2p to match offline_item and check that it does.
- offline_item_p2p.mime_type = "text/html";
+ offline_item_p2p.mime_type = "multipart/related";
offline_item_p2p.is_suggested = true;
EXPECT_EQ(offline_item, offline_item_p2p);
}
@@ -93,7 +95,7 @@ TEST(OfflineItemConversionsTest, SavePageRequestConversion) {
EXPECT_EQ(base::FilePath(), offline_item.file_path);
EXPECT_EQ(creation_time, offline_item.creation_time);
EXPECT_EQ(base::Time(), offline_item.last_accessed_time);
- EXPECT_EQ("text/html", offline_item.mime_type);
+ EXPECT_EQ("multipart/related", offline_item.mime_type);
EXPECT_EQ(OfflineItemFilter::FILTER_PAGE, offline_item.filter);
EXPECT_EQ(OfflineItemState::IN_PROGRESS, offline_item.state);
EXPECT_EQ(0, offline_item.progress.value);
@@ -101,16 +103,16 @@ TEST(OfflineItemConversionsTest, SavePageRequestConversion) {
EXPECT_EQ(OfflineItemProgressUnit::PERCENTAGE, offline_item.progress.unit);
EXPECT_FALSE(offline_item.is_suggested);
- // Enabled P2P sharing of offline pages and check that only the mime type is
+ // Disable P2P sharing of offline pages and check that only the mime type is
// different.
base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(kOfflinePagesSharingFeature);
+ scoped_feature_list.InitAndDisableFeature(kOfflinePagesSharingFeature);
OfflineItem offline_item_p2p =
OfflineItemConversions::CreateOfflineItem(save_page_request);
- EXPECT_EQ("multipart/related", offline_item_p2p.mime_type);
+ EXPECT_EQ("text/html", offline_item_p2p.mime_type);
// Change offline_item_p2p to match offline_item and check that it does.
- offline_item_p2p.mime_type = "text/html";
+ offline_item_p2p.mime_type = "multipart/related";
EXPECT_EQ(offline_item, offline_item_p2p);
}
diff --git a/chromium/components/offline_pages/core/downloads/offline_page_download_notifier.h b/chromium/components/offline_pages/core/downloads/offline_page_download_notifier.h
deleted file mode 100644
index a2aba78d60f..00000000000
--- a/chromium/components/offline_pages/core/downloads/offline_page_download_notifier.h
+++ /dev/null
@@ -1,45 +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_OFFLINE_PAGES_CORE_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
-#define COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
-
-#include "components/offline_items_collection/core/offline_item.h"
-
-using OfflineItem = offline_items_collection::OfflineItem;
-
-namespace offline_pages {
-
-struct OfflinePageDownloadNotifier {
- public:
- virtual ~OfflinePageDownloadNotifier() = default;
-
- // Reports that |item| has completed successfully.
- virtual void NotifyDownloadSuccessful(const OfflineItem& item) = 0;
-
- // Reports that |item| has completed unsuccessfully.
- virtual void NotifyDownloadFailed(const OfflineItem& item) = 0;
-
- // Reports that |item| is active and possibly making progress.
- virtual void NotifyDownloadProgress(const OfflineItem& item) = 0;
-
- // Reports that |item| has been paused (and so it is not active).
- virtual void NotifyDownloadPaused(const OfflineItem& item) = 0;
-
- // Reports that any progress on |item| has been interrupted. It is pending
- // or available for another attempt when conditions allows.
- virtual void NotifyDownloadInterrupted(const OfflineItem& item) = 0;
-
- // Reports that |item| has been canceled.
- virtual void NotifyDownloadCanceled(const OfflineItem& item) = 0;
-
- // Suppresses the download complete notification
- // depending on flags and origin.
- virtual bool MaybeSuppressNotification(const std::string& origin,
- const OfflineItem& item) = 0;
-};
-
-} // namespace offline_pages
-
-#endif // COMPONENTS_OFFLINE_PAGES_CORE_DOWNLOADS_OFFLINE_PAGE_DOWNLOAD_NOTIFIER_H_
diff --git a/chromium/components/offline_pages/core/model/add_page_task.cc b/chromium/components/offline_pages/core/model/add_page_task.cc
index 7e81f2618b9..8760e8b76b1 100644
--- a/chromium/components/offline_pages/core/model/add_page_task.cc
+++ b/chromium/components/offline_pages/core/model/add_page_task.cc
@@ -7,7 +7,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "components/offline_pages/core/offline_page_item.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_page_types.h"
#include "components/offline_pages/core/offline_store_types.h"
#include "components/offline_pages/core/offline_store_utils.h"
@@ -76,7 +76,7 @@ ItemActionStatus AddOfflinePageSync(const OfflinePageItem& item,
} // namespace
-AddPageTask::AddPageTask(OfflinePageMetadataStoreSQL* store,
+AddPageTask::AddPageTask(OfflinePageMetadataStore* store,
const OfflinePageItem& offline_page,
AddPageTaskCallback callback)
: store_(store),
diff --git a/chromium/components/offline_pages/core/model/add_page_task.h b/chromium/components/offline_pages/core/model/add_page_task.h
index 0086b406f5a..fc056172c0d 100644
--- a/chromium/components/offline_pages/core/model/add_page_task.h
+++ b/chromium/components/offline_pages/core/model/add_page_task.h
@@ -14,7 +14,7 @@ namespace offline_pages {
enum class AddPageResult;
enum class ItemActionStatus;
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
// Task that adds a new page to the metadata store.
// The caller needs to provide a callback which consumes an AddPageResult and
@@ -25,7 +25,7 @@ class AddPageTask : public Task {
public:
typedef base::OnceCallback<void(AddPageResult)> AddPageTaskCallback;
- AddPageTask(OfflinePageMetadataStoreSQL* store,
+ AddPageTask(OfflinePageMetadataStore* store,
const OfflinePageItem& offline_page,
AddPageTaskCallback callback);
~AddPageTask() override;
@@ -38,7 +38,7 @@ class AddPageTask : public Task {
void InformAddPageDone(AddPageResult result);
// The metadata store to insert the page. Not owned.
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
OfflinePageItem offline_page_;
AddPageTaskCallback callback_;
diff --git a/chromium/components/offline_pages/core/model/add_page_to_download_manager_task.cc b/chromium/components/offline_pages/core/model/add_page_to_download_manager_task.cc
index 462763aa6d9..7e8fea72b45 100644
--- a/chromium/components/offline_pages/core/model/add_page_to_download_manager_task.cc
+++ b/chromium/components/offline_pages/core/model/add_page_to_download_manager_task.cc
@@ -5,7 +5,7 @@
#include "components/offline_pages/core/model/add_page_to_download_manager_task.h"
#include "base/bind.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/system_download_manager.h"
#include "sql/connection.h"
#include "sql/statement.h"
@@ -22,9 +22,9 @@ bool SetDownloadIdSync(int64_t offline_id,
if (!db)
return false;
- const char kSql[] = "UPDATE OR IGNORE " OFFLINE_PAGES_TABLE_NAME
- " SET system_download_id = ?"
- " WHERE offline_id = ?";
+ static const char kSql[] = "UPDATE OR IGNORE " OFFLINE_PAGES_TABLE_NAME
+ " SET system_download_id = ?"
+ " WHERE offline_id = ?";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt64(0, download_id);
statement.BindInt64(1, offline_id);
@@ -34,7 +34,7 @@ bool SetDownloadIdSync(int64_t offline_id,
} // namespace
AddPageToDownloadManagerTask::AddPageToDownloadManagerTask(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
SystemDownloadManager* download_manager,
int64_t offline_id,
const std::string& title,
diff --git a/chromium/components/offline_pages/core/model/add_page_to_download_manager_task.h b/chromium/components/offline_pages/core/model/add_page_to_download_manager_task.h
index 32959791a12..d9d74cb92a5 100644
--- a/chromium/components/offline_pages/core/model/add_page_to_download_manager_task.h
+++ b/chromium/components/offline_pages/core/model/add_page_to_download_manager_task.h
@@ -16,12 +16,12 @@
namespace offline_pages {
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
class SystemDownloadManager;
class AddPageToDownloadManagerTask : public Task {
public:
- AddPageToDownloadManagerTask(OfflinePageMetadataStoreSQL* store,
+ AddPageToDownloadManagerTask(OfflinePageMetadataStore* store,
SystemDownloadManager* download_manager,
int64_t offline_id,
const std::string& title,
@@ -40,7 +40,7 @@ class AddPageToDownloadManagerTask : public Task {
void OnAddIdDone(bool result);
// Unowned pointer to the Metadata SQL store.
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
const std::string title_;
const std::string description_;
const std::string path_;
diff --git a/chromium/components/offline_pages/core/model/cleanup_thumbnails_task.cc b/chromium/components/offline_pages/core/model/cleanup_thumbnails_task.cc
index dcb00a9d63c..afc673c725b 100644
--- a/chromium/components/offline_pages/core/model/cleanup_thumbnails_task.cc
+++ b/chromium/components/offline_pages/core/model/cleanup_thumbnails_task.cc
@@ -5,7 +5,7 @@
#include "components/offline_pages/core/model/cleanup_thumbnails_task.h"
#include "base/metrics/histogram_macros.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_store_utils.h"
#include "sql/connection.h"
#include "sql/statement.h"
@@ -40,7 +40,7 @@ CleanupThumbnailsTask::Result CleanupThumbnailsSync(base::Time now,
} // namespace
CleanupThumbnailsTask::CleanupThumbnailsTask(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
base::Time now,
CleanupThumbnailsCallback complete_callback)
: store_(store),
diff --git a/chromium/components/offline_pages/core/model/cleanup_thumbnails_task.h b/chromium/components/offline_pages/core/model/cleanup_thumbnails_task.h
index 3636dd8bc10..753d6c4e457 100644
--- a/chromium/components/offline_pages/core/model/cleanup_thumbnails_task.h
+++ b/chromium/components/offline_pages/core/model/cleanup_thumbnails_task.h
@@ -14,7 +14,7 @@
#include "components/offline_pages/core/task.h"
namespace offline_pages {
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
// CleanupThumbnailsTask deletes thumbnails from page_thumbnails if they
// are no longer needed.
@@ -25,7 +25,7 @@ class CleanupThumbnailsTask : public Task {
int removed_thumbnails = 0;
};
- CleanupThumbnailsTask(OfflinePageMetadataStoreSQL* store,
+ CleanupThumbnailsTask(OfflinePageMetadataStore* store,
base::Time now,
CleanupThumbnailsCallback complete_callback);
~CleanupThumbnailsTask() override;
@@ -35,7 +35,7 @@ class CleanupThumbnailsTask : public Task {
private:
void Complete(Result result);
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
base::Time now_;
CleanupThumbnailsCallback complete_callback_;
diff --git a/chromium/components/offline_pages/core/model/cleanup_thumbnails_task_unittest.cc b/chromium/components/offline_pages/core/model/cleanup_thumbnails_task_unittest.cc
index f7d09afc33a..0dd76f8743e 100644
--- a/chromium/components/offline_pages/core/model/cleanup_thumbnails_task_unittest.cc
+++ b/chromium/components/offline_pages/core/model/cleanup_thumbnails_task_unittest.cc
@@ -7,7 +7,7 @@
#include <memory>
#include "base/test/bind_test_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "components/offline_pages/core/model/get_thumbnail_task.h"
#include "components/offline_pages/core/model/model_task_test_base.h"
diff --git a/chromium/components/offline_pages/core/model/clear_digest_task.cc b/chromium/components/offline_pages/core/model/clear_digest_task.cc
index 03487934193..e2177542f01 100644
--- a/chromium/components/offline_pages/core/model/clear_digest_task.cc
+++ b/chromium/components/offline_pages/core/model/clear_digest_task.cc
@@ -5,7 +5,7 @@
#include "components/offline_pages/core/model/clear_digest_task.h"
#include "base/bind.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "sql/connection.h"
#include "sql/statement.h"
@@ -17,7 +17,7 @@ bool ClearDigestSync(int64_t offline_id, sql::Connection* db) {
if (!db)
return false;
- const char kSql[] =
+ static const char kSql[] =
"UPDATE OR IGNORE offlinepages_v1"
" SET digest = '' "
" WHERE offline_id = ?";
@@ -28,7 +28,7 @@ bool ClearDigestSync(int64_t offline_id, sql::Connection* db) {
} // namespace
-ClearDigestTask::ClearDigestTask(OfflinePageMetadataStoreSQL* store,
+ClearDigestTask::ClearDigestTask(OfflinePageMetadataStore* store,
int64_t offline_id)
: store_(store), offline_id_(offline_id), weak_ptr_factory_(this) {
DCHECK(store_);
diff --git a/chromium/components/offline_pages/core/model/clear_digest_task.h b/chromium/components/offline_pages/core/model/clear_digest_task.h
index 6f733968e61..35097a3053d 100644
--- a/chromium/components/offline_pages/core/model/clear_digest_task.h
+++ b/chromium/components/offline_pages/core/model/clear_digest_task.h
@@ -13,14 +13,14 @@
namespace offline_pages {
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
// Task that clears the digest field of a page in the metadata store. It takes
// the offline ID of the page that needs to have digest cleared.
// There is no callback needed for this task.
class ClearDigestTask : public Task {
public:
- ClearDigestTask(OfflinePageMetadataStoreSQL* store, int64_t offline_id);
+ ClearDigestTask(OfflinePageMetadataStore* store, int64_t offline_id);
~ClearDigestTask() override;
// Task implementation.
@@ -30,7 +30,7 @@ class ClearDigestTask : public Task {
void OnClearDigestDone(bool result);
// The metadata store used to clear the digest. Not owned.
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
int64_t offline_id_;
diff --git a/chromium/components/offline_pages/core/model/clear_storage_task.cc b/chromium/components/offline_pages/core/model/clear_storage_task.cc
index 657c4f3c93e..4110dd1080b 100644
--- a/chromium/components/offline_pages/core/model/clear_storage_task.cc
+++ b/chromium/components/offline_pages/core/model/clear_storage_task.cc
@@ -18,7 +18,7 @@
#include "base/trace_event/trace_event.h"
#include "components/offline_pages/core/client_policy_controller.h"
#include "components/offline_pages/core/offline_page_client_policy.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_store_utils.h"
#include "sql/connection.h"
#include "sql/statement.h"
@@ -77,9 +77,9 @@ std::unique_ptr<std::vector<PageInfo>> GetAllTemporaryPageInfos(
sql::Connection* db) {
auto result = std::make_unique<std::vector<PageInfo>>();
- const char kSql[] = "SELECT " PAGE_INFO_PROJECTION
- " FROM offlinepages_v1"
- " WHERE client_namespace = ?";
+ static const char kSql[] = "SELECT " PAGE_INFO_PROJECTION
+ " FROM offlinepages_v1"
+ " WHERE client_namespace = ?";
for (const auto& temp_namespace_policy : temp_namespace_policy_map) {
std::string name_space = temp_namespace_policy.first;
@@ -226,7 +226,7 @@ std::map<std::string, LifetimePolicy> GetTempNamespacePolicyMap(
} // namespace
-ClearStorageTask::ClearStorageTask(OfflinePageMetadataStoreSQL* store,
+ClearStorageTask::ClearStorageTask(OfflinePageMetadataStore* store,
ArchiveManager* archive_manager,
ClientPolicyController* policy_controller,
const base::Time& clearup_time,
@@ -248,8 +248,8 @@ ClearStorageTask::~ClearStorageTask() {}
void ClearStorageTask::Run() {
TRACE_EVENT_ASYNC_BEGIN0("offline_pages", "ClearStorageTask running", this);
archive_manager_->GetStorageStats(
- base::Bind(&ClearStorageTask::OnGetStorageStatsDone,
- weak_ptr_factory_.GetWeakPtr()));
+ base::BindOnce(&ClearStorageTask::OnGetStorageStatsDone,
+ weak_ptr_factory_.GetWeakPtr()));
}
void ClearStorageTask::OnGetStorageStatsDone(
diff --git a/chromium/components/offline_pages/core/model/clear_storage_task.h b/chromium/components/offline_pages/core/model/clear_storage_task.h
index 518b4a5acd9..e60ad3ed1f5 100644
--- a/chromium/components/offline_pages/core/model/clear_storage_task.h
+++ b/chromium/components/offline_pages/core/model/clear_storage_task.h
@@ -20,7 +20,7 @@ class Time;
namespace offline_pages {
class ClientPolicyController;
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
// This task is responsible for clearing expired temporary pages from metadata
// store and disk.
@@ -48,7 +48,7 @@ class ClearStorageTask : public Task {
typedef base::OnceCallback<void(size_t, ClearStorageResult)>
ClearStorageCallback;
- ClearStorageTask(OfflinePageMetadataStoreSQL* store,
+ ClearStorageTask(OfflinePageMetadataStore* store,
ArchiveManager* archive_manager,
ClientPolicyController* policy_controller,
const base::Time& clearup_time,
@@ -64,7 +64,7 @@ class ClearStorageTask : public Task {
void InformClearStorageDone(size_t pages_cleared, ClearStorageResult result);
// The store containing the pages to be cleared. Not owned.
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
// The archive manager owning the archive directories to delete pages from.
// Not owned.
ArchiveManager* archive_manager_;
diff --git a/chromium/components/offline_pages/core/model/clear_storage_task_unittest.cc b/chromium/components/offline_pages/core/model/clear_storage_task_unittest.cc
index f7fac6027e2..93f7736e1dc 100644
--- a/chromium/components/offline_pages/core/model/clear_storage_task_unittest.cc
+++ b/chromium/components/offline_pages/core/model/clear_storage_task_unittest.cc
@@ -10,7 +10,7 @@
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
#include "components/offline_pages/core/client_namespace_constants.h"
diff --git a/chromium/components/offline_pages/core/model/complete_offline_page_upgrade_task.cc b/chromium/components/offline_pages/core/model/complete_offline_page_upgrade_task.cc
index 9085b5cd036..8fd6db9ff59 100644
--- a/chromium/components/offline_pages/core/model/complete_offline_page_upgrade_task.cc
+++ b/chromium/components/offline_pages/core/model/complete_offline_page_upgrade_task.cc
@@ -7,7 +7,7 @@
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/sys_info.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_store_utils.h"
#include "sql/connection.h"
#include "sql/statement.h"
@@ -33,7 +33,7 @@ CompleteUpgradeStatus CompleteOfflinePageUpgradeSync(
// We need to remember the old file path, so that we can remove that file
// later on.
- const char kSql[] =
+ static const char kSql[] =
"SELECT file_path FROM offlinepages_v1 WHERE offline_id = ?";
sql::Statement select_statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
select_statement.BindInt64(0, offline_id);
@@ -62,7 +62,7 @@ CompleteUpgradeStatus CompleteOfflinePageUpgradeSync(
// Conditions for upgrade are met here.
// Update remaining attempts in DB and complete task.
- const char kUpdateSql[] =
+ static const char kUpdateSql[] =
"UPDATE offlinepages_v1"
" SET upgrade_attempt = 0, file_path = ?, file_size = ?, digest = ?"
" WHERE offline_id = ?";
@@ -89,7 +89,7 @@ CompleteUpgradeStatus CompleteOfflinePageUpgradeSync(
} // namespace
CompleteOfflinePageUpgradeTask::CompleteOfflinePageUpgradeTask(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
int64_t offline_id,
const base::FilePath& temporary_file_path,
const base::FilePath& target_file_path,
diff --git a/chromium/components/offline_pages/core/model/complete_offline_page_upgrade_task.h b/chromium/components/offline_pages/core/model/complete_offline_page_upgrade_task.h
index 049755bb56c..fba8f2d278c 100644
--- a/chromium/components/offline_pages/core/model/complete_offline_page_upgrade_task.h
+++ b/chromium/components/offline_pages/core/model/complete_offline_page_upgrade_task.h
@@ -15,13 +15,13 @@
namespace offline_pages {
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
// This task is responsible for completing the upgrade process for an offline
// page.
class CompleteOfflinePageUpgradeTask : public Task {
public:
- CompleteOfflinePageUpgradeTask(OfflinePageMetadataStoreSQL* store,
+ CompleteOfflinePageUpgradeTask(OfflinePageMetadataStore* store,
int64_t offline_id,
const base::FilePath& temporary_file_path,
const base::FilePath& target_file_path,
@@ -37,7 +37,7 @@ class CompleteOfflinePageUpgradeTask : public Task {
void InformUpgradeAttemptDone(CompleteUpgradeStatus result);
// The store containing the pages to be cleared. Not owned.
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
// ID of the item that needs to be updated.
int64_t offline_id_;
diff --git a/chromium/components/offline_pages/core/model/delete_page_task.cc b/chromium/components/offline_pages/core/model/delete_page_task.cc
index 5ae3385d641..28b421f5659 100644
--- a/chromium/components/offline_pages/core/model/delete_page_task.cc
+++ b/chromium/components/offline_pages/core/model/delete_page_task.cc
@@ -13,7 +13,7 @@
#include "components/offline_pages/core/client_policy_controller.h"
#include "components/offline_pages/core/model/offline_page_model_utils.h"
#include "components/offline_pages/core/offline_page_client_policy.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.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_page_types.h"
#include "components/offline_pages/core/offline_store_utils.h"
@@ -425,7 +425,7 @@ DeletePageTaskResult::~DeletePageTaskResult() = default;
// static
std::unique_ptr<DeletePageTask> DeletePageTask::CreateTaskMatchingOfflineIds(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
DeletePageTask::DeletePageTaskCallback callback,
const std::vector<int64_t>& offline_ids) {
return std::unique_ptr<DeletePageTask>(new DeletePageTask(
@@ -435,7 +435,7 @@ std::unique_ptr<DeletePageTask> DeletePageTask::CreateTaskMatchingOfflineIds(
// static
std::unique_ptr<DeletePageTask> DeletePageTask::CreateTaskMatchingClientIds(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
DeletePageTask::DeletePageTaskCallback callback,
const std::vector<ClientId>& client_ids) {
return std::unique_ptr<DeletePageTask>(new DeletePageTask(
@@ -446,7 +446,7 @@ std::unique_ptr<DeletePageTask> DeletePageTask::CreateTaskMatchingClientIds(
// static
std::unique_ptr<DeletePageTask>
DeletePageTask::CreateTaskMatchingClientIdsAndOrigin(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
DeletePageTask::DeletePageTaskCallback callback,
const std::vector<ClientId>& client_ids,
const std::string& origin) {
@@ -459,7 +459,7 @@ DeletePageTask::CreateTaskMatchingClientIdsAndOrigin(
// static
std::unique_ptr<DeletePageTask>
DeletePageTask::CreateTaskMatchingUrlPredicateForCachedPages(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
DeletePageTask::DeletePageTaskCallback callback,
ClientPolicyController* policy_controller,
const UrlPredicate& predicate) {
@@ -474,7 +474,7 @@ DeletePageTask::CreateTaskMatchingUrlPredicateForCachedPages(
// static
std::unique_ptr<DeletePageTask> DeletePageTask::CreateTaskDeletingForPageLimit(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
DeletePageTask::DeletePageTaskCallback callback,
ClientPolicyController* policy_controller,
const OfflinePageItem& page) {
@@ -486,7 +486,7 @@ std::unique_ptr<DeletePageTask> DeletePageTask::CreateTaskDeletingForPageLimit(
std::move(callback)));
}
-DeletePageTask::DeletePageTask(OfflinePageMetadataStoreSQL* store,
+DeletePageTask::DeletePageTask(OfflinePageMetadataStore* store,
DeleteFunction func,
DeletePageTaskCallback callback)
: store_(store),
diff --git a/chromium/components/offline_pages/core/model/delete_page_task.h b/chromium/components/offline_pages/core/model/delete_page_task.h
index b95606688e5..5c1e19a3e8a 100644
--- a/chromium/components/offline_pages/core/model/delete_page_task.h
+++ b/chromium/components/offline_pages/core/model/delete_page_task.h
@@ -10,7 +10,7 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.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_page_types.h"
#include "components/offline_pages/core/task.h"
@@ -22,7 +22,7 @@ class Connection;
namespace offline_pages {
struct ClientId;
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
// Task that deletes pages from the metadata store. It takes the store and
// archive manager for deleting entries from database and file system. Also the
@@ -51,20 +51,20 @@ class DeletePageTask : public Task {
// Creates a task to delete pages with offline ids in |offline_ids|.
static std::unique_ptr<DeletePageTask> CreateTaskMatchingOfflineIds(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
DeletePageTask::DeletePageTaskCallback callback,
const std::vector<int64_t>& offline_ids);
// Creates a task to delete pages with client ids in |client_ids|.
static std::unique_ptr<DeletePageTask> CreateTaskMatchingClientIds(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
DeletePageTask::DeletePageTaskCallback callback,
const std::vector<ClientId>& client_ids);
// Creates a task to delete pages with the client ids in |client_ids|
// provided they also have origin |origin|.
static std::unique_ptr<DeletePageTask> CreateTaskMatchingClientIdsAndOrigin(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
DeletePageTask::DeletePageTaskCallback callback,
const std::vector<ClientId>& client_ids,
const std::string& origin);
@@ -72,7 +72,7 @@ class DeletePageTask : public Task {
// Creates a task to delete pages which satisfy |predicate|.
static std::unique_ptr<DeletePageTask>
CreateTaskMatchingUrlPredicateForCachedPages(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
DeletePageTask::DeletePageTaskCallback callback,
ClientPolicyController* policy_controller,
const UrlPredicate& predicate);
@@ -82,7 +82,7 @@ class DeletePageTask : public Task {
// defined with the namespace that this |page| belongs to.
// Returns nullptr if there's no page limit per url of the page's namespace.
static std::unique_ptr<DeletePageTask> CreateTaskDeletingForPageLimit(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
DeletePageTask::DeletePageTaskCallback callback,
ClientPolicyController* policy_controller,
const OfflinePageItem& page);
@@ -98,7 +98,7 @@ class DeletePageTask : public Task {
// Making the constructor private, in order to use static methods to create
// tasks.
- DeletePageTask(OfflinePageMetadataStoreSQL* store,
+ DeletePageTask(OfflinePageMetadataStore* store,
DeleteFunction func,
DeletePageTaskCallback callback);
@@ -109,7 +109,7 @@ class DeletePageTask : public Task {
void InformDeletePageDone(DeletePageResult result);
// The store to delete pages from. Not owned.
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
// The function which will delete pages.
DeleteFunction func_;
DeletePageTaskCallback callback_;
diff --git a/chromium/components/offline_pages/core/model/delete_page_task_unittest.cc b/chromium/components/offline_pages/core/model/delete_page_task_unittest.cc
index e18804e77ce..4965a90e6e0 100644
--- a/chromium/components/offline_pages/core/model/delete_page_task_unittest.cc
+++ b/chromium/components/offline_pages/core/model/delete_page_task_unittest.cc
@@ -11,7 +11,7 @@
#include "base/files/file_util.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/offline_pages/core/client_namespace_constants.h"
#include "components/offline_pages/core/model/model_task_test_base.h"
#include "components/offline_pages/core/model/offline_page_model_utils.h"
@@ -344,7 +344,7 @@ TEST_F(DeletePageTaskTest, DeletePageByUrlPredicate) {
EXPECT_TRUE(base::PathExists(page3.file_path));
// Delete all pages with url contains example.com, which are with kTestUrl1.
- UrlPredicate predicate = base::Bind([](const GURL& url) -> bool {
+ UrlPredicate predicate = base::BindRepeating([](const GURL& url) -> bool {
return url.spec().find("example.com") != std::string::npos;
});
@@ -395,7 +395,7 @@ TEST_F(DeletePageTaskTest, DeletePageByUrlPredicateNotFound) {
// Return false for all pages so that no pages will be deleted.
UrlPredicate predicate =
- base::Bind([](const GURL& url) -> bool { return false; });
+ base::BindRepeating([](const GURL& url) -> bool { return false; });
auto task = DeletePageTask::CreateTaskMatchingUrlPredicateForCachedPages(
store(), delete_page_callback(), policy_controller(), predicate);
diff --git a/chromium/components/offline_pages/core/model/get_pages_task.cc b/chromium/components/offline_pages/core/model/get_pages_task.cc
index 0b470fd3018..04013ab082b 100644
--- a/chromium/components/offline_pages/core/model/get_pages_task.cc
+++ b/chromium/components/offline_pages/core/model/get_pages_task.cc
@@ -315,7 +315,7 @@ GetPagesTask::ReadResult::~ReadResult() {}
// static
std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingAllPages(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback) {
return base::WrapUnique(new GetPagesTask(
store, base::BindOnce(&ReadAllPagesSync), std::move(callback)));
@@ -323,7 +323,7 @@ std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingAllPages(
// static
std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingClientIds(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback,
const std::vector<ClientId>& client_ids) {
// Creates an instance of GetPagesTask, which wraps the client_ids argument in
@@ -335,7 +335,7 @@ std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingClientIds(
// static
std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingNamespace(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback,
const std::string& name_space) {
std::vector<std::string> namespaces = {name_space};
@@ -347,7 +347,7 @@ std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingNamespace(
// static
std::unique_ptr<GetPagesTask>
GetPagesTask::CreateTaskMatchingPagesRemovedOnCacheReset(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback,
ClientPolicyController* policy_controller) {
return base::WrapUnique(new GetPagesTask(
@@ -360,7 +360,7 @@ GetPagesTask::CreateTaskMatchingPagesRemovedOnCacheReset(
// static
std::unique_ptr<GetPagesTask>
GetPagesTask::CreateTaskMatchingPagesSupportedByDownloads(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback,
ClientPolicyController* policy_controller) {
return base::WrapUnique(new GetPagesTask(
@@ -372,7 +372,7 @@ GetPagesTask::CreateTaskMatchingPagesSupportedByDownloads(
// static
std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingRequestOrigin(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback,
const std::string& request_origin) {
return base::WrapUnique(new GetPagesTask(
@@ -382,7 +382,7 @@ std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingRequestOrigin(
// static
std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingUrl(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback,
const GURL& url) {
return base::WrapUnique(new GetPagesTask(
@@ -391,7 +391,7 @@ std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingUrl(
// static
std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingOfflineId(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
SingleOfflinePageItemCallback callback,
int64_t offline_id) {
return base::WrapUnique(new GetPagesTask(
@@ -401,7 +401,7 @@ std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingOfflineId(
// static
std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingGuid(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
SingleOfflinePageItemCallback callback,
const std::string& guid) {
return base::WrapUnique(new GetPagesTask(
@@ -411,7 +411,7 @@ std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingGuid(
// static
std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingSizeAndDigest(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
SingleOfflinePageItemCallback callback,
int64_t file_size,
const std::string& digest) {
@@ -423,13 +423,13 @@ std::unique_ptr<GetPagesTask> GetPagesTask::CreateTaskMatchingSizeAndDigest(
// static
std::unique_ptr<GetPagesTask>
GetPagesTask::CreateTaskSelectingItemsMarkedForUpgrade(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback) {
return base::WrapUnique(new GetPagesTask(
store, base::BindOnce(&SelectItemsForUpgrade), std::move(callback)));
}
-GetPagesTask::GetPagesTask(OfflinePageMetadataStoreSQL* store,
+GetPagesTask::GetPagesTask(OfflinePageMetadataStore* store,
DbWorkCallback db_work_callback,
MultipleOfflinePageItemCallback callback)
: store_(store),
diff --git a/chromium/components/offline_pages/core/model/get_pages_task.h b/chromium/components/offline_pages/core/model/get_pages_task.h
index c8008b73022..825199b05e1 100644
--- a/chromium/components/offline_pages/core/model/get_pages_task.h
+++ b/chromium/components/offline_pages/core/model/get_pages_task.h
@@ -11,7 +11,7 @@
#include "base/memory/weak_ptr.h"
#include "components/offline_pages/core/client_id.h"
#include "components/offline_pages/core/offline_page_item.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_page_types.h"
#include "components/offline_pages/core/task.h"
@@ -33,31 +33,31 @@ class GetPagesTask : public Task {
std::vector<OfflinePageItem> pages;
};
- using DbWorkCallback = OfflinePageMetadataStoreSQL::RunCallback<ReadResult>;
+ using DbWorkCallback = OfflinePageMetadataStore::RunCallback<ReadResult>;
// Creates |GetPagesTask| reading all pages from DB.
static std::unique_ptr<GetPagesTask> CreateTaskMatchingAllPages(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback);
// Creates |GetPagesTask| reading pages matching provided |client_ids| from
// DB.
static std::unique_ptr<GetPagesTask> CreateTaskMatchingClientIds(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback,
const std::vector<ClientId>& client_ids);
// Creates |GetPagesTask| reading pages belonging to provided |name_space|
// from DB.
static std::unique_ptr<GetPagesTask> CreateTaskMatchingNamespace(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback,
const std::string& name_space);
// Creates |GetPagesTask| reading pages removed on cache reset from DB.
static std::unique_ptr<GetPagesTask>
CreateTaskMatchingPagesRemovedOnCacheReset(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback,
ClientPolicyController* policy_controller);
@@ -65,14 +65,14 @@ class GetPagesTask : public Task {
// from DB.
static std::unique_ptr<GetPagesTask>
CreateTaskMatchingPagesSupportedByDownloads(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback,
ClientPolicyController* policy_controller);
// Creates |GetPagesTask| reading pages matching provided |request_origin|
// from DB.
static std::unique_ptr<GetPagesTask> CreateTaskMatchingRequestOrigin(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback,
const std::string& request_origin);
@@ -81,28 +81,28 @@ class GetPagesTask : public Task {
// be removed from all URLs prior to matching. Only a match on a single field
// is necessary.
static std::unique_ptr<GetPagesTask> CreateTaskMatchingUrl(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback,
const GURL& url);
// Creates |GetPagesTask| reading a single page matching provided |offline_id|
// from DB.
static std::unique_ptr<GetPagesTask> CreateTaskMatchingOfflineId(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
SingleOfflinePageItemCallback callback,
int64_t offline_id);
// Creates |GetPagesTask| reading a single page matching provided |guid| from
// DB.
static std::unique_ptr<GetPagesTask> CreateTaskMatchingGuid(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
SingleOfflinePageItemCallback callback,
const std::string& guid);
// Creates |GetPagesTask| reading a single page matching provided |file_size|
// and |digest| from DB.
static std::unique_ptr<GetPagesTask> CreateTaskMatchingSizeAndDigest(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
SingleOfflinePageItemCallback callback,
int64_t file_size,
const std::string& digest);
@@ -112,7 +112,7 @@ class GetPagesTask : public Task {
// Order of items is determined by number of remaining attempts (descending)
// and creation time (descending).
static std::unique_ptr<GetPagesTask> CreateTaskSelectingItemsMarkedForUpgrade(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
MultipleOfflinePageItemCallback callback);
~GetPagesTask() override;
@@ -121,14 +121,14 @@ class GetPagesTask : public Task {
void Run() override;
private:
- GetPagesTask(OfflinePageMetadataStoreSQL* store,
+ GetPagesTask(OfflinePageMetadataStore* store,
DbWorkCallback db_work_callback,
MultipleOfflinePageItemCallback callback);
void ReadRequests();
void CompleteWithResult(ReadResult result);
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
DbWorkCallback db_work_callback_;
MultipleOfflinePageItemCallback callback_;
diff --git a/chromium/components/offline_pages/core/model/get_thumbnail_task.cc b/chromium/components/offline_pages/core/model/get_thumbnail_task.cc
index 9a17d93fc6a..abdfab15833 100644
--- a/chromium/components/offline_pages/core/model/get_thumbnail_task.cc
+++ b/chromium/components/offline_pages/core/model/get_thumbnail_task.cc
@@ -4,7 +4,7 @@
#include "components/offline_pages/core/model/get_thumbnail_task.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_store_utils.h"
#include "sql/connection.h"
#include "sql/statement.h"
@@ -41,7 +41,7 @@ std::unique_ptr<OfflinePageThumbnail> GetThumbnailSync(int64_t offline_id,
} // namespace
-GetThumbnailTask::GetThumbnailTask(OfflinePageMetadataStoreSQL* store,
+GetThumbnailTask::GetThumbnailTask(OfflinePageMetadataStore* store,
int64_t offline_id,
CompleteCallback complete_callback)
: store_(store),
diff --git a/chromium/components/offline_pages/core/model/get_thumbnail_task.h b/chromium/components/offline_pages/core/model/get_thumbnail_task.h
index fc2131fc6e5..2ee87587ecc 100644
--- a/chromium/components/offline_pages/core/model/get_thumbnail_task.h
+++ b/chromium/components/offline_pages/core/model/get_thumbnail_task.h
@@ -13,7 +13,7 @@
#include "components/offline_pages/core/task.h"
namespace offline_pages {
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
// GetThumbnailTask reads a thumbnail from the page_thumbnails table.
class GetThumbnailTask : public Task {
@@ -21,7 +21,7 @@ class GetThumbnailTask : public Task {
typedef base::OnceCallback<void(std::unique_ptr<OfflinePageThumbnail>)>
CompleteCallback;
- GetThumbnailTask(OfflinePageMetadataStoreSQL* store,
+ GetThumbnailTask(OfflinePageMetadataStore* store,
int64_t offline_id,
CompleteCallback complete_callback);
~GetThumbnailTask() override;
@@ -34,7 +34,7 @@ class GetThumbnailTask : public Task {
void Complete(Result result);
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
int64_t offline_id_;
base::OnceCallback<void(std::unique_ptr<OfflinePageThumbnail>)>
complete_callback_;
diff --git a/chromium/components/offline_pages/core/model/has_thumbnail_task.cc b/chromium/components/offline_pages/core/model/has_thumbnail_task.cc
index 757e1fa1afd..b76f48c4553 100644
--- a/chromium/components/offline_pages/core/model/has_thumbnail_task.cc
+++ b/chromium/components/offline_pages/core/model/has_thumbnail_task.cc
@@ -4,7 +4,7 @@
#include "components/offline_pages/core/model/has_thumbnail_task.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "sql/connection.h"
#include "sql/statement.h"
#include "sql/transaction.h"
@@ -25,7 +25,7 @@ bool ThumbnailExistsSync(int64_t offline_id, sql::Connection* db) {
} // namespace
-HasThumbnailTask::HasThumbnailTask(OfflinePageMetadataStoreSQL* store,
+HasThumbnailTask::HasThumbnailTask(OfflinePageMetadataStore* store,
int64_t offline_id,
ThumbnailExistsCallback exists_callback)
: store_(store),
diff --git a/chromium/components/offline_pages/core/model/has_thumbnail_task.h b/chromium/components/offline_pages/core/model/has_thumbnail_task.h
index 80b53d8cc2c..2ecfda10d50 100644
--- a/chromium/components/offline_pages/core/model/has_thumbnail_task.h
+++ b/chromium/components/offline_pages/core/model/has_thumbnail_task.h
@@ -11,14 +11,14 @@
#include "components/offline_pages/core/task.h"
namespace offline_pages {
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
// Checks if a thumbnail exists for the specified offline id.
class HasThumbnailTask : public Task {
public:
using ThumbnailExistsCallback = base::OnceCallback<void(bool)>;
- HasThumbnailTask(OfflinePageMetadataStoreSQL* store,
+ HasThumbnailTask(OfflinePageMetadataStore* store,
int64_t offline_id,
ThumbnailExistsCallback exists_callback);
~HasThumbnailTask() override;
@@ -29,7 +29,7 @@ class HasThumbnailTask : public Task {
private:
void OnThumbnailExists(bool exists);
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
int64_t offline_id_;
ThumbnailExistsCallback exists_callback_;
base::WeakPtrFactory<HasThumbnailTask> weak_ptr_factory_;
diff --git a/chromium/components/offline_pages/core/model/mark_page_accessed_task.cc b/chromium/components/offline_pages/core/model/mark_page_accessed_task.cc
index a82afd6cd51..457b403689a 100644
--- a/chromium/components/offline_pages/core/model/mark_page_accessed_task.cc
+++ b/chromium/components/offline_pages/core/model/mark_page_accessed_task.cc
@@ -9,7 +9,7 @@
#include "base/metrics/histogram_macros.h"
#include "components/offline_pages/core/client_namespace_constants.h"
#include "components/offline_pages/core/model/offline_page_model_utils.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_store_utils.h"
#include "sql/connection.h"
#include "sql/statement.h"
@@ -29,7 +29,7 @@ void ReportAccessHistogram(int64_t offline_id,
// page will be longer than one year in extreme cases so it's good enough.
const int kMinutesPerYear = base::TimeDelta::FromDays(365).InMinutes();
- const char kSql[] =
+ static const char kSql[] =
"SELECT client_namespace, last_access_time FROM " OFFLINE_PAGES_TABLE_NAME
" WHERE offline_id = ?";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
@@ -61,7 +61,7 @@ bool MarkPageAccessedSync(const base::Time& access_time,
ReportAccessHistogram(offline_id, access_time, db);
- const char kSql[] =
+ static const char kSql[] =
"UPDATE OR IGNORE " OFFLINE_PAGES_TABLE_NAME
" SET last_access_time = ?, access_count = access_count + 1"
" WHERE offline_id = ?";
@@ -76,7 +76,7 @@ bool MarkPageAccessedSync(const base::Time& access_time,
} // namespace
-MarkPageAccessedTask::MarkPageAccessedTask(OfflinePageMetadataStoreSQL* store,
+MarkPageAccessedTask::MarkPageAccessedTask(OfflinePageMetadataStore* store,
int64_t offline_id,
const base::Time& access_time)
: store_(store),
diff --git a/chromium/components/offline_pages/core/model/mark_page_accessed_task.h b/chromium/components/offline_pages/core/model/mark_page_accessed_task.h
index fe33d1c30d9..5770898e8d3 100644
--- a/chromium/components/offline_pages/core/model/mark_page_accessed_task.h
+++ b/chromium/components/offline_pages/core/model/mark_page_accessed_task.h
@@ -15,14 +15,14 @@ class Time;
namespace offline_pages {
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
// Task that marks a page accessed in the metadata store. It takes the offline
// ID of the page accessed, and the time when it was accessed.
// There is no callback needed for this task.
class MarkPageAccessedTask : public Task {
public:
- MarkPageAccessedTask(OfflinePageMetadataStoreSQL* store,
+ MarkPageAccessedTask(OfflinePageMetadataStore* store,
int64_t offline_id,
const base::Time& access_time);
~MarkPageAccessedTask() override;
@@ -34,7 +34,7 @@ class MarkPageAccessedTask : public Task {
void OnMarkPageAccessedDone(bool result);
// The metadata store used to update the page. Not owned.
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
int64_t offline_id_;
base::Time access_time_;
diff --git a/chromium/components/offline_pages/core/model/mark_page_accessed_task_unittest.cc b/chromium/components/offline_pages/core/model/mark_page_accessed_task_unittest.cc
index a432190751e..5b73e60c27b 100644
--- a/chromium/components/offline_pages/core/model/mark_page_accessed_task_unittest.cc
+++ b/chromium/components/offline_pages/core/model/mark_page_accessed_task_unittest.cc
@@ -6,7 +6,7 @@
#include <memory>
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "components/offline_pages/core/model/model_task_test_base.h"
#include "components/offline_pages/core/model/offline_page_model_utils.h"
diff --git a/chromium/components/offline_pages/core/model/model_task_test_base.h b/chromium/components/offline_pages/core/model/model_task_test_base.h
index 830b0b4ed17..8be2176f283 100644
--- a/chromium/components/offline_pages/core/model/model_task_test_base.h
+++ b/chromium/components/offline_pages/core/model/model_task_test_base.h
@@ -48,7 +48,7 @@ class ModelTaskTestBase : public TaskTestBase,
OfflinePageMetadataStoreTestUtil* store_test_util() {
return &store_test_util_;
}
- OfflinePageMetadataStoreSQL* store() { return store_test_util_.store(); }
+ OfflinePageMetadataStore* store() { return store_test_util_.store(); }
OfflinePageItemGenerator* generator() { return &generator_; }
ArchiveManager* archive_manager() { return archive_manager_.get(); }
ClientPolicyController* policy_controller() { return &policy_controller_; }
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 4345e25a82b..6b3ffe5ed47 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
@@ -4,8 +4,7 @@
#include "components/offline_pages/core/model/offline_page_model_taskified.h"
-#include <memory>
-#include <vector>
+#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -34,7 +33,7 @@
#include "components/offline_pages/core/model/store_thumbnail_task.h"
#include "components/offline_pages/core/model/update_file_path_task.h"
#include "components/offline_pages/core/offline_page_feature.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.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"
#include "components/offline_pages/core/system_download_manager.h"
@@ -47,12 +46,12 @@ using ClearStorageResult = ClearStorageTask::ClearStorageResult;
namespace {
-void WrapInMultipleItemsCallback(const MultipleOfflineIdCallback& callback,
+void WrapInMultipleItemsCallback(MultipleOfflineIdCallback callback,
const MultipleOfflinePageItemResult& pages) {
std::vector<int64_t> results;
for (const auto& page : pages)
results.push_back(page.offline_id);
- callback.Run(results);
+ std::move(callback).Run(results);
}
SavePageResult ArchiverResultToSavePageResult(ArchiverResult archiver_result) {
@@ -186,7 +185,7 @@ constexpr base::TimeDelta OfflinePageModelTaskified::kMaintenanceTasksDelay;
constexpr base::TimeDelta OfflinePageModelTaskified::kClearStorageInterval;
OfflinePageModelTaskified::OfflinePageModelTaskified(
- std::unique_ptr<OfflinePageMetadataStoreSQL> store,
+ std::unique_ptr<OfflinePageMetadataStore> store,
std::unique_ptr<ArchiveManager> archive_manager,
std::unique_ptr<SystemDownloadManager> download_manager,
const scoped_refptr<base::SequencedTaskRunner>& task_runner,
@@ -201,7 +200,7 @@ OfflinePageModelTaskified::OfflinePageModelTaskified(
skip_maintenance_tasks_for_testing_(false),
task_runner_(task_runner),
weak_ptr_factory_(this) {
- DCHECK_LT(kMaintenanceTasksDelay, OfflinePageMetadataStoreSQL::kClosingDelay);
+ DCHECK_LT(kMaintenanceTasksDelay, OfflinePageMetadataStore::kClosingDelay);
CreateArchivesDirectoryIfNeeded();
// TODO(fgorski): Call from here, when upgrade task is available:
// PostSelectItemsMarkedForUpgrade();
@@ -223,18 +222,18 @@ void OfflinePageModelTaskified::SavePage(
const SavePageParams& save_page_params,
std::unique_ptr<OfflinePageArchiver> archiver,
content::WebContents* web_contents,
- const SavePageCallback& callback) {
+ SavePageCallback callback) {
// Skip saving the page that is not intended to be saved, like local file
// page.
if (!OfflinePageModel::CanSaveURL(save_page_params.url)) {
- InformSavePageDone(callback, SavePageResult::SKIPPED,
+ InformSavePageDone(std::move(callback), SavePageResult::SKIPPED,
save_page_params.client_id, kInvalidOfflineId);
return;
}
// The web contents is not available if archiver is not created and passed.
if (!archiver) {
- InformSavePageDone(callback, SavePageResult::CONTENT_UNAVAILABLE,
+ InformSavePageDone(std::move(callback), SavePageResult::CONTENT_UNAVAILABLE,
save_page_params.client_id, kInvalidOfflineId);
return;
}
@@ -250,21 +249,26 @@ void OfflinePageModelTaskified::SavePage(
create_archive_params.remove_popup_overlay = save_page_params.is_background;
create_archive_params.use_page_problem_detectors =
save_page_params.use_page_problem_detectors;
- archiver->CreateArchive(
+
+ // Note: the archiver instance must be kept alive until the final callback
+ // coming from it takes place.
+ OfflinePageArchiver* raw_archiver = archiver.get();
+ raw_archiver->CreateArchive(
GetInternalArchiveDirectory(save_page_params.client_id.name_space),
create_archive_params, web_contents,
- base::Bind(&OfflinePageModelTaskified::OnCreateArchiveDone,
- weak_ptr_factory_.GetWeakPtr(), save_page_params, offline_id,
- GetCurrentTime(), callback));
- pending_archivers_.push_back(std::move(archiver));
+ base::BindOnce(&OfflinePageModelTaskified::OnCreateArchiveDone,
+ weak_ptr_factory_.GetWeakPtr(), save_page_params,
+ offline_id, GetCurrentTime(), std::move(archiver),
+ std::move(callback)));
}
void OfflinePageModelTaskified::AddPage(const OfflinePageItem& page,
- const AddPageCallback& callback) {
+ AddPageCallback callback) {
auto task = std::make_unique<AddPageTask>(
store_.get(), page,
base::BindOnce(&OfflinePageModelTaskified::OnAddPageDone,
- weak_ptr_factory_.GetWeakPtr(), page, callback));
+ weak_ptr_factory_.GetWeakPtr(), page,
+ std::move(callback)));
task_queue_.AddTask(std::move(task));
}
@@ -276,22 +280,22 @@ void OfflinePageModelTaskified::MarkPageAccessed(int64_t offline_id) {
void OfflinePageModelTaskified::DeletePagesByOfflineId(
const std::vector<int64_t>& offline_ids,
- const DeletePageCallback& callback) {
+ DeletePageCallback callback) {
auto task = DeletePageTask::CreateTaskMatchingOfflineIds(
store_.get(),
base::BindOnce(&OfflinePageModelTaskified::OnDeleteDone,
- weak_ptr_factory_.GetWeakPtr(), callback),
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
offline_ids);
task_queue_.AddTask(std::move(task));
}
void OfflinePageModelTaskified::DeletePagesByClientIds(
const std::vector<ClientId>& client_ids,
- const DeletePageCallback& callback) {
+ DeletePageCallback callback) {
auto task = DeletePageTask::CreateTaskMatchingClientIds(
store_.get(),
base::BindOnce(&OfflinePageModelTaskified::OnDeleteDone,
- weak_ptr_factory_.GetWeakPtr(), callback),
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
client_ids);
task_queue_.AddTask(std::move(task));
}
@@ -299,22 +303,22 @@ void OfflinePageModelTaskified::DeletePagesByClientIds(
void OfflinePageModelTaskified::DeletePagesByClientIdsAndOrigin(
const std::vector<ClientId>& client_ids,
const std::string& origin,
- const DeletePageCallback& callback) {
+ DeletePageCallback callback) {
auto task = DeletePageTask::CreateTaskMatchingClientIdsAndOrigin(
store_.get(),
base::BindOnce(&OfflinePageModelTaskified::OnDeleteDone,
- weak_ptr_factory_.GetWeakPtr(), callback),
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
client_ids, origin);
task_queue_.AddTask(std::move(task));
}
void OfflinePageModelTaskified::DeleteCachedPagesByURLPredicate(
const UrlPredicate& predicate,
- const DeletePageCallback& callback) {
+ DeletePageCallback callback) {
auto task = DeletePageTask::CreateTaskMatchingUrlPredicateForCachedPages(
store_.get(),
base::BindOnce(&OfflinePageModelTaskified::OnDeleteDone,
- weak_ptr_factory_.GetWeakPtr(), callback),
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
policy_controller_.get(), predicate);
task_queue_.AddTask(std::move(task));
}
@@ -404,12 +408,13 @@ void OfflinePageModelTaskified::GetPageBySizeAndDigest(
void OfflinePageModelTaskified::GetOfflineIdsForClientId(
const ClientId& client_id,
- const MultipleOfflineIdCallback& callback) {
+ MultipleOfflineIdCallback callback) {
// We're currently getting offline IDs by querying offline items based on
// client ids, and then extract the offline IDs from the items. This is fine
// since we're not expecting many pages with the same client ID.
auto task = GetPagesTask::CreateTaskMatchingClientIds(
- store_.get(), base::Bind(&WrapInMultipleItemsCallback, callback),
+ store_.get(),
+ base::BindOnce(&WrapInMultipleItemsCallback, std::move(callback)),
{client_id});
task_queue_.AddTask(std::move(task));
}
@@ -461,11 +466,10 @@ OfflineEventLogger* OfflinePageModelTaskified::GetLogger() {
return &offline_event_logger_;
}
-void OfflinePageModelTaskified::InformSavePageDone(
- const SavePageCallback& callback,
- SavePageResult result,
- const ClientId& client_id,
- int64_t offline_id) {
+void OfflinePageModelTaskified::InformSavePageDone(SavePageCallback callback,
+ SavePageResult result,
+ const ClientId& client_id,
+ int64_t offline_id) {
UMA_HISTOGRAM_ENUMERATION("OfflinePages.SavePageCount",
model_utils::ToNamespaceEnum(client_id.name_space),
OfflinePagesNamespaceEnumeration::RESULT_COUNT);
@@ -481,33 +485,32 @@ void OfflinePageModelTaskified::InformSavePageDone(
if (result == SavePageResult::ARCHIVE_CREATION_FAILED)
CreateArchivesDirectoryIfNeeded();
if (!callback.is_null())
- callback.Run(result, offline_id);
+ std::move(callback).Run(result, offline_id);
}
void OfflinePageModelTaskified::OnCreateArchiveDone(
const SavePageParams& save_page_params,
int64_t offline_id,
const base::Time& start_time,
- const SavePageCallback& callback,
- OfflinePageArchiver* archiver,
+ std::unique_ptr<OfflinePageArchiver> archiver,
+ SavePageCallback callback,
ArchiverResult archiver_result,
const GURL& saved_url,
const base::FilePath& file_path,
const base::string16& title,
int64_t file_size,
- const std::string& file_hash) {
+ const std::string& digest) {
if (archiver_result != ArchiverResult::SUCCESSFULLY_CREATED) {
SavePageResult result = ArchiverResultToSavePageResult(archiver_result);
- InformSavePageDone(callback, result, save_page_params.client_id,
+ InformSavePageDone(std::move(callback), result, save_page_params.client_id,
offline_id);
- ErasePendingArchiver(archiver);
return;
}
if (save_page_params.url != saved_url) {
DVLOG(1) << "Saved URL does not match requested URL.";
- InformSavePageDone(callback, SavePageResult::ARCHIVE_CREATION_FAILED,
+ InformSavePageDone(std::move(callback),
+ SavePageResult::ARCHIVE_CREATION_FAILED,
save_page_params.client_id, offline_id);
- ErasePendingArchiver(archiver);
return;
}
@@ -515,7 +518,7 @@ void OfflinePageModelTaskified::OnCreateArchiveDone(
save_page_params.client_id, file_path, file_size,
start_time);
offline_page.title = title;
- offline_page.digest = file_hash;
+ offline_page.digest = digest;
offline_page.request_origin = save_page_params.request_origin;
// Don't record the original URL if it is identical to the final URL. This is
// because some websites might route the redirect finally back to itself upon
@@ -529,73 +532,75 @@ void OfflinePageModelTaskified::OnCreateArchiveDone(
policy_controller_->IsUserRequestedDownload(
offline_page.client_id.name_space)) {
// If the user intentionally downloaded the page, move it to a public place.
- PublishArchive(offline_page, callback, archiver);
- } else {
- // For pages that we download on the user's behalf, we keep them in an
- // internal chrome directory, and add them here to the OfflinePageModel
- // database.
- AddPage(offline_page,
- base::Bind(&OfflinePageModelTaskified::OnAddPageForSavePageDone,
- weak_ptr_factory_.GetWeakPtr(), callback, offline_page));
+ // Note: Moving the archiver instance into the callback so it won't be
+ // deleted.
+ OfflinePageArchiver* raw_archiver = archiver.get();
+ raw_archiver->PublishArchive(
+ offline_page, task_runner_, archive_manager_->GetPublicArchivesDir(),
+ download_manager_.get(),
+ base::BindOnce(&OfflinePageModelTaskified::PublishArchiveDone,
+ weak_ptr_factory_.GetWeakPtr(), std::move(archiver),
+ std::move(callback)));
+ return;
}
- ErasePendingArchiver(archiver);
-}
-
-void OfflinePageModelTaskified::ErasePendingArchiver(
- OfflinePageArchiver* archiver) {
- pending_archivers_.erase(
- std::find_if(pending_archivers_.begin(), pending_archivers_.end(),
- [archiver](const std::unique_ptr<OfflinePageArchiver>& a) {
- return a.get() == archiver;
- }));
-}
-void OfflinePageModelTaskified::PublishArchive(
- const OfflinePageItem& offline_page,
- const SavePageCallback& save_page_callback,
- OfflinePageArchiver* archiver) {
- archiver->PublishArchive(
- offline_page, task_runner_, archive_manager_->GetPublicArchivesDir(),
- download_manager_.get(),
- base::BindOnce(&OfflinePageModelTaskified::PublishArchiveDone,
- weak_ptr_factory_.GetWeakPtr(), save_page_callback));
+ // For pages that we download on the user's behalf, we keep them in an
+ // internal chrome directory, and add them here to the OfflinePageModel
+ // database.
+ AddPage(offline_page,
+ base::BindOnce(&OfflinePageModelTaskified::OnAddPageForSavePageDone,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+ offline_page));
+ // Note: If the archiver instance ownership was not transferred, it will be
+ // deleted here.
}
void OfflinePageModelTaskified::PublishArchiveDone(
- const SavePageCallback& save_page_callback,
+ std::unique_ptr<OfflinePageArchiver> archiver,
+ SavePageCallback save_page_callback,
const OfflinePageItem& offline_page,
- PublishArchiveResult* move_results) {
- if (move_results->move_result != SavePageResult::SUCCESS) {
- save_page_callback.Run(move_results->move_result, 0LL);
+ PublishArchiveResult publish_results) {
+ if (publish_results.move_result != SavePageResult::SUCCESS) {
+ // Add UMA for the failure reason.
+ UMA_HISTOGRAM_ENUMERATION("OfflinePages.PublishPageResult",
+ publish_results.move_result,
+ SavePageResult::RESULT_COUNT);
+
+ std::move(save_page_callback).Run(publish_results.move_result, 0LL);
return;
}
OfflinePageItem page = offline_page;
- page.file_path = move_results->new_file_path;
- page.system_download_id = move_results->download_id;
+ page.file_path = publish_results.new_file_path;
+ page.system_download_id = publish_results.download_id;
AddPage(page,
- base::Bind(&OfflinePageModelTaskified::OnAddPageForSavePageDone,
- weak_ptr_factory_.GetWeakPtr(), save_page_callback, page));
+ base::BindOnce(&OfflinePageModelTaskified::OnAddPageForSavePageDone,
+ weak_ptr_factory_.GetWeakPtr(),
+ std::move(save_page_callback), page));
}
void OfflinePageModelTaskified::PublishInternalArchive(
const OfflinePageItem& offline_page,
std::unique_ptr<OfflinePageArchiver> archiver,
PublishPageCallback publish_done_callback) {
- archiver->PublishArchive(
+ // Note: the archiver instance must be kept alive until the final callback
+ // coming from it takes place.
+ OfflinePageArchiver* raw_archiver = archiver.get();
+ raw_archiver->PublishArchive(
offline_page, task_runner_, archive_manager_->GetPublicArchivesDir(),
download_manager_.get(),
base::BindOnce(&OfflinePageModelTaskified::PublishInternalArchiveDone,
- weak_ptr_factory_.GetWeakPtr(),
+ weak_ptr_factory_.GetWeakPtr(), std::move(archiver),
std::move(publish_done_callback)));
}
void OfflinePageModelTaskified::PublishInternalArchiveDone(
+ std::unique_ptr<OfflinePageArchiver> archiver,
PublishPageCallback publish_done_callback,
const OfflinePageItem& offline_page,
- PublishArchiveResult* publish_results) {
- base::FilePath file_path = publish_results->new_file_path;
- SavePageResult result = publish_results->move_result;
+ PublishArchiveResult publish_results) {
+ base::FilePath file_path = publish_results.new_file_path;
+ SavePageResult result = publish_results.move_result;
// Call the callback with success == false if we failed to move the page.
if (result != SavePageResult::SUCCESS) {
std::move(publish_done_callback).Run(file_path, result);
@@ -613,14 +618,14 @@ void OfflinePageModelTaskified::PublishInternalArchiveDone(
}
void OfflinePageModelTaskified::OnAddPageForSavePageDone(
- const SavePageCallback& callback,
+ SavePageCallback callback,
const OfflinePageItem& page_attempted,
AddPageResult add_page_result,
int64_t offline_id) {
SavePageResult save_page_result =
AddPageResultToSavePageResult(add_page_result);
- InformSavePageDone(callback, save_page_result, page_attempted.client_id,
- offline_id);
+ InformSavePageDone(std::move(callback), save_page_result,
+ page_attempted.client_id, offline_id);
if (save_page_result == SavePageResult::SUCCESS) {
ReportPageHistogramAfterSuccessfulSaving(page_attempted, GetCurrentTime());
// TODO(romax): Just keep the same with logic in OPMImpl (which was wrong).
@@ -637,9 +642,9 @@ void OfflinePageModelTaskified::OnAddPageForSavePageDone(
}
void OfflinePageModelTaskified::OnAddPageDone(const OfflinePageItem& page,
- const AddPageCallback& callback,
+ AddPageCallback callback,
AddPageResult result) {
- callback.Run(result, page.offline_id);
+ std::move(callback).Run(result, page.offline_id);
if (result == AddPageResult::SUCCESS) {
for (Observer& observer : observers_)
observer.OfflinePageAdded(this, page);
@@ -647,7 +652,7 @@ void OfflinePageModelTaskified::OnAddPageDone(const OfflinePageItem& page,
}
void OfflinePageModelTaskified::OnDeleteDone(
- const DeletePageCallback& callback,
+ DeletePageCallback callback,
DeletePageResult result,
const std::vector<OfflinePageModel::DeletedPageInfo>& infos) {
UMA_HISTOGRAM_ENUMERATION("OfflinePages.DeletePageResult", result,
@@ -675,7 +680,7 @@ void OfflinePageModelTaskified::OnDeleteDone(
download_manager_.get(), system_download_ids));
if (!callback.is_null())
- callback.Run(result);
+ std::move(callback).Run(result);
}
void OfflinePageModelTaskified::OnStoreThumbnailDone(
@@ -795,7 +800,8 @@ void OfflinePageModelTaskified::RemovePagesMatchingUrlAndNamespace(
auto task = DeletePageTask::CreateTaskDeletingForPageLimit(
store_.get(),
base::BindOnce(&OfflinePageModelTaskified::OnDeleteDone,
- weak_ptr_factory_.GetWeakPtr(), base::DoNothing()),
+ weak_ptr_factory_.GetWeakPtr(),
+ base::DoNothing::Once<DeletePageResult>()),
policy_controller_.get(), page);
task_queue_.AddTask(std::move(task));
}
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 4e65fcc90f0..c21d381eff6 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
@@ -5,9 +5,10 @@
#ifndef COMPONENTS_OFFLINE_PAGES_CORE_MODEL_OFFLINE_PAGE_MODEL_TASKIFIED_H_
#define COMPONENTS_OFFLINE_PAGES_CORE_MODEL_OFFLINE_PAGE_MODEL_TASKIFIED_H_
-#include <stdint.h>
-
-#include <utility>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
#include "base/callback.h"
#include "base/macros.h"
@@ -38,7 +39,7 @@ struct OfflinePageItem;
class ArchiveManager;
class ClientPolicyController;
class OfflinePageArchiver;
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
class SystemDownloadManager;
// Implementaion of OfflinePageModel, which is a service for saving pages
@@ -56,7 +57,7 @@ class OfflinePageModelTaskified : public OfflinePageModel,
// Delay between the scheduling and actual running of maintenance tasks. To
// not cause the re-opening of the metadata store this delay should be kept
- // smaller than OfflinePageMetadataStoreSQL::kClosingDelay.
+ // smaller than OfflinePageMetadataStore::kClosingDelay.
static constexpr base::TimeDelta kMaintenanceTasksDelay =
base::TimeDelta::FromSeconds(10);
@@ -65,7 +66,7 @@ class OfflinePageModelTaskified : public OfflinePageModel,
base::TimeDelta::FromMinutes(30);
OfflinePageModelTaskified(
- std::unique_ptr<OfflinePageMetadataStoreSQL> store,
+ std::unique_ptr<OfflinePageMetadataStore> store,
std::unique_ptr<ArchiveManager> archive_manager,
std::unique_ptr<SystemDownloadManager> download_manager,
const scoped_refptr<base::SequencedTaskRunner>& task_runner,
@@ -82,22 +83,19 @@ class OfflinePageModelTaskified : public OfflinePageModel,
void SavePage(const SavePageParams& save_page_params,
std::unique_ptr<OfflinePageArchiver> archiver,
content::WebContents* web_contents,
- const SavePageCallback& callback) override;
- void AddPage(const OfflinePageItem& page,
- const AddPageCallback& callback) override;
+ SavePageCallback callback) override;
+ void AddPage(const OfflinePageItem& page, AddPageCallback callback) override;
void MarkPageAccessed(int64_t offline_id) override;
void DeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
- const DeletePageCallback& callback) override;
+ DeletePageCallback callback) override;
void DeletePagesByClientIds(const std::vector<ClientId>& client_ids,
- const DeletePageCallback& callback) override;
- void DeletePagesByClientIdsAndOrigin(
- const std::vector<ClientId>& client_ids,
- const std::string& origin,
- const DeletePageCallback& callback) override;
- void DeleteCachedPagesByURLPredicate(
- const UrlPredicate& predicate,
- const DeletePageCallback& callback) override;
+ DeletePageCallback callback) override;
+ void DeletePagesByClientIdsAndOrigin(const std::vector<ClientId>& client_ids,
+ const std::string& origin,
+ DeletePageCallback callback) override;
+ void DeleteCachedPagesByURLPredicate(const UrlPredicate& predicate,
+ DeletePageCallback callback) override;
void GetAllPages(MultipleOfflinePageItemCallback callback) override;
void GetPageByOfflineId(int64_t offline_id,
@@ -123,9 +121,8 @@ class OfflinePageModelTaskified : public OfflinePageModel,
void GetPageBySizeAndDigest(int64_t file_size,
const std::string& digest,
SingleOfflinePageItemCallback callback) override;
- void GetOfflineIdsForClientId(
- const ClientId& client_id,
- const MultipleOfflineIdCallback& callback) override;
+ void GetOfflineIdsForClientId(const ClientId& client_id,
+ MultipleOfflineIdCallback callback) override;
void StoreThumbnail(const OfflinePageThumbnail& thumb) override;
void GetThumbnailByOfflineId(
int64_t offline_id,
@@ -150,7 +147,7 @@ class OfflinePageModelTaskified : public OfflinePageModel,
PublishPageCallback publish_done_callback) override;
// Methods for testing only:
- OfflinePageMetadataStoreSQL* GetStoreForTesting() { return store_.get(); }
+ OfflinePageMetadataStore* GetStoreForTesting() { return store_.get(); }
void SetClockForTesting(base::Clock* clock) { clock_ = clock; }
void SetSkipClearingOriginalUrlForTesting() {
skip_clearing_original_url_for_testing_ = true;
@@ -164,34 +161,34 @@ class OfflinePageModelTaskified : public OfflinePageModel,
friend class OfflinePageModelTaskifiedTest;
// Callbacks for saving pages.
- void InformSavePageDone(const SavePageCallback& calback,
+ void InformSavePageDone(SavePageCallback calback,
SavePageResult result,
const ClientId& client_id,
int64_t offline_id);
- void OnAddPageForSavePageDone(const SavePageCallback& callback,
+ void OnAddPageForSavePageDone(SavePageCallback callback,
const OfflinePageItem& page_attempted,
AddPageResult add_page_result,
int64_t offline_id);
void OnCreateArchiveDone(const SavePageParams& save_page_params,
int64_t offline_id,
const base::Time& start_time,
- const SavePageCallback& callback,
- OfflinePageArchiver* archiver,
+ std::unique_ptr<OfflinePageArchiver> archiver,
+ SavePageCallback callback,
OfflinePageArchiver::ArchiverResult archiver_result,
const GURL& saved_url,
const base::FilePath& file_path,
const base::string16& title,
int64_t file_size,
- const std::string& digest);
+ const std::string& file_hash);
// Callback for adding pages.
void OnAddPageDone(const OfflinePageItem& page,
- const AddPageCallback& callback,
+ AddPageCallback callback,
AddPageResult result);
// Callbacks for deleting pages.
void OnDeleteDone(
- const DeletePageCallback& callback,
+ DeletePageCallback callback,
DeletePageResult result,
const std::vector<OfflinePageModel::DeletedPageInfo>& infos);
@@ -214,20 +211,17 @@ class OfflinePageModelTaskified : public OfflinePageModel,
void OnSelectItemsMarkedForUpgradeDone(
const MultipleOfflinePageItemResult& pages_for_upgrade);
- // Methods for publishing the page to the public directory.
- void PublishArchive(const OfflinePageItem& offline_page,
- const SavePageCallback& callback,
- OfflinePageArchiver* archiver);
-
// Callback for when PublishArchive has completd.
- void PublishArchiveDone(const SavePageCallback& save_page_callback,
+ void PublishArchiveDone(std::unique_ptr<OfflinePageArchiver> archiver,
+ SavePageCallback save_page_callback,
const OfflinePageItem& offline_page,
- PublishArchiveResult* archive_result);
+ PublishArchiveResult publish_results);
// Callback for when publishing an internal archive has completed.
- void PublishInternalArchiveDone(PublishPageCallback publish_done_callback,
+ void PublishInternalArchiveDone(std::unique_ptr<OfflinePageArchiver> archiver,
+ PublishPageCallback publish_done_callback,
const OfflinePageItem& offline_page,
- PublishArchiveResult* publish_results);
+ PublishArchiveResult publish_results);
// Method for unpublishing the page from the system download manager.
static void RemoveFromDownloadManager(
@@ -236,12 +230,11 @@ class OfflinePageModelTaskified : public OfflinePageModel,
// Other utility methods.
void RemovePagesMatchingUrlAndNamespace(const OfflinePageItem& page);
- void ErasePendingArchiver(OfflinePageArchiver* archiver);
void CreateArchivesDirectoryIfNeeded();
base::Time GetCurrentTime();
// Persistent store for offline page metadata.
- std::unique_ptr<OfflinePageMetadataStoreSQL> store_;
+ std::unique_ptr<OfflinePageMetadataStore> store_;
// Manager for the offline archive files and directory.
std::unique_ptr<ArchiveManager> archive_manager_;
@@ -255,12 +248,6 @@ class OfflinePageModelTaskified : public OfflinePageModel,
// The observers.
base::ObserverList<Observer> observers_;
- // Pending archivers owned by this model.
- // This is above the definition of |task_queue_|. Since the queue may hold raw
- // pointers to the pending archivers, and the pending archivers are better
- // destructed after the |task_queue_|.
- std::vector<std::unique_ptr<OfflinePageArchiver>> pending_archivers_;
-
// Clock for testing only.
base::Clock* clock_ = nullptr;
diff --git a/chromium/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc b/chromium/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
index 977717956c9..239a09c9d30 100644
--- a/chromium/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
+++ b/chromium/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
@@ -13,7 +13,7 @@
#include "base/files/scoped_temp_dir.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_mock_time_task_runner.h"
@@ -26,7 +26,7 @@
#include "components/offline_pages/core/model/persistent_page_consistency_check_task.h"
#include "components/offline_pages/core/offline_page_feature.h"
#include "components/offline_pages/core/offline_page_item.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_page_metadata_store_test_util.h"
#include "components/offline_pages/core/offline_page_model.h"
#include "components/offline_pages/core/offline_page_test_archiver.h"
@@ -111,7 +111,7 @@ class OfflinePageModelTaskifiedTest : public testing::Test,
const GURL& original_url,
const std::string& request_origin,
std::unique_ptr<OfflinePageArchiver> archiver,
- const SavePageCallback& callback);
+ SavePageCallback callback);
int64_t SavePageWithExpectedResult(
const GURL& url,
const ClientId& client_id,
@@ -119,6 +119,13 @@ class OfflinePageModelTaskifiedTest : public testing::Test,
const std::string& request_origin,
std::unique_ptr<OfflinePageArchiver> archiver,
SavePageResult expected_result);
+ // Start a save page simulating a file move failure.
+ int64_t SavePageWithFileMoveFailure(
+ const GURL& url,
+ const ClientId& client_id,
+ const GURL& original_url,
+ const std::string& request_origin,
+ std::unique_ptr<OfflinePageArchiver> archiver);
// Insert an offline page in to store, it does not rely on the model
// implementation.
void InsertPageIntoStore(const OfflinePageItem& offline_page);
@@ -129,7 +136,7 @@ class OfflinePageModelTaskifiedTest : public testing::Test,
// Getters for private fields.
base::TestMockTimeTaskRunner* task_runner() { return task_runner_.get(); }
OfflinePageModelTaskified* model() { return model_.get(); }
- OfflinePageMetadataStoreSQL* store() { return store_test_util_.store(); }
+ OfflinePageMetadataStore* store() { return store_test_util_.store(); }
OfflinePageMetadataStoreTestUtil* store_test_util() {
return &store_test_util_;
}
@@ -216,7 +223,6 @@ void OfflinePageModelTaskifiedTest::TearDown() {
if (!public_archive_dir_.Delete())
DLOG(ERROR) << "public_archive_dir not created";
}
- EXPECT_EQ(0UL, model_->pending_archivers_.size());
model_->RemoveObserver(this);
model_.reset();
PumpLoop();
@@ -242,7 +248,6 @@ void OfflinePageModelTaskifiedTest::BuildModel() {
model_->AddObserver(this);
histogram_tester_ = std::make_unique<base::HistogramTester>();
ResetResults();
- EXPECT_EQ(0UL, model_->pending_archivers_.size());
}
void OfflinePageModelTaskifiedTest::ResetModel() {
@@ -283,14 +288,15 @@ void OfflinePageModelTaskifiedTest::SavePageWithCallback(
const GURL& original_url,
const std::string& request_origin,
std::unique_ptr<OfflinePageArchiver> archiver,
- const SavePageCallback& callback) {
+ SavePageCallback callback) {
OfflinePageModel::SavePageParams save_page_params;
save_page_params.url = url;
save_page_params.client_id = client_id;
save_page_params.original_url = original_url;
save_page_params.request_origin = request_origin;
save_page_params.is_background = false;
- model()->SavePage(save_page_params, std::move(archiver), nullptr, callback);
+ model()->SavePage(save_page_params, std::move(archiver), nullptr,
+ std::move(callback));
PumpLoop();
}
@@ -313,6 +319,20 @@ int64_t OfflinePageModelTaskifiedTest::SavePageWithExpectedResult(
return offline_id;
}
+int64_t OfflinePageModelTaskifiedTest::SavePageWithFileMoveFailure(
+ const GURL& url,
+ const ClientId& client_id,
+ const GURL& original_url,
+ const std::string& request_origin,
+ std::unique_ptr<OfflinePageArchiver> archiver) {
+ int64_t offline_id = OfflinePageModel::kInvalidOfflineId;
+ base::MockCallback<SavePageCallback> callback;
+
+ SavePageWithCallback(url, client_id, original_url, request_origin,
+ std::move(archiver), callback.Get());
+ return offline_id;
+}
+
void OfflinePageModelTaskifiedTest::InsertPageIntoStore(
const OfflinePageItem& offline_page) {
store_test_util()->InsertItem(offline_page);
@@ -863,10 +883,11 @@ TEST_F(OfflinePageModelTaskifiedTest, DeletePagesByUrlPredicate) {
EXPECT_CALL(callback, Run(testing::A<DeletePageResult>()));
CheckTaskQueueIdle();
- UrlPredicate predicate =
- base::Bind([](const GURL& expected_url,
- const GURL& url) -> bool { return url == expected_url; },
- kTestUrl);
+ UrlPredicate predicate = base::BindRepeating(
+ [](const GURL& expected_url, const GURL& url) -> bool {
+ return url == expected_url;
+ },
+ kTestUrl);
model()->DeleteCachedPagesByURLPredicate(predicate, callback.Get());
EXPECT_TRUE(task_queue()->HasRunningTask());
@@ -1261,6 +1282,27 @@ TEST_F(OfflinePageModelTaskifiedTest,
// This test is affected by https://crbug.com/725685, which only affects windows
// platform.
#if defined(OS_WIN)
+#define MAYBE_PublishPageFailure DISABLED_PublishPageFailure
+#else
+#define MAYBE_PublishPageFailure PublishPageFailure
+#endif
+TEST_F(OfflinePageModelTaskifiedTest, MAYBE_PublishPageFailure) {
+ // Save a persistent page that will report failure to be copied to a public
+ // dir.
+ auto archiver = BuildArchiver(kTestUrl, ArchiverResult::SUCCESSFULLY_CREATED);
+ archiver->set_archive_attempt_failure(true);
+ SavePageWithFileMoveFailure(kTestUrl, kTestUserRequestedClientId, GURL(),
+ kEmptyRequestOrigin, std::move(archiver));
+
+ // Ensure that a histogram is emitted for the failure
+ histogram_tester()->ExpectUniqueSample(
+ "OfflinePages.PublishPageResult",
+ static_cast<int>(SavePageResult::FILE_MOVE_FAILED), 1);
+}
+
+// This test is affected by https://crbug.com/725685, which only affects windows
+// platform.
+#if defined(OS_WIN)
#define MAYBE_CheckPublishInternalArchive DISABLED_CheckPublishInternalArchive
#else
#define MAYBE_CheckPublishInternalArchive CheckPublishInternalArchive
diff --git a/chromium/components/offline_pages/core/model/persistent_page_consistency_check_task.cc b/chromium/components/offline_pages/core/model/persistent_page_consistency_check_task.cc
index 1133faaeb55..9dffb84ac4d 100644
--- a/chromium/components/offline_pages/core/model/persistent_page_consistency_check_task.cc
+++ b/chromium/components/offline_pages/core/model/persistent_page_consistency_check_task.cc
@@ -14,7 +14,7 @@
#include "components/offline_pages/core/archive_manager.h"
#include "components/offline_pages/core/client_policy_controller.h"
#include "components/offline_pages/core/offline_page_client_policy.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_store_utils.h"
#include "sql/connection.h"
#include "sql/statement.h"
@@ -113,7 +113,7 @@ bool MarkPagesAsReappeared(const std::vector<int64_t>& ids_of_reappeared_pages,
PersistentPageConsistencyCheckTask::CheckResult
PersistentPageConsistencyCheckSync(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
const base::FilePath& private_dir,
const base::FilePath& public_dir,
const std::vector<std::string>& persistent_namespaces,
@@ -200,7 +200,7 @@ PersistentPageConsistencyCheckTask::CheckResult::operator=(
PersistentPageConsistencyCheckTask::CheckResult::~CheckResult() {}
PersistentPageConsistencyCheckTask::PersistentPageConsistencyCheckTask(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
ArchiveManager* archive_manager,
ClientPolicyController* policy_controller,
base::Time check_time,
diff --git a/chromium/components/offline_pages/core/model/persistent_page_consistency_check_task.h b/chromium/components/offline_pages/core/model/persistent_page_consistency_check_task.h
index c98d9fa75a6..1f725a4d5fa 100644
--- a/chromium/components/offline_pages/core/model/persistent_page_consistency_check_task.h
+++ b/chromium/components/offline_pages/core/model/persistent_page_consistency_check_task.h
@@ -16,7 +16,7 @@ namespace offline_pages {
class ArchiveManager;
class ClientPolicyController;
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
// This task is responsible for checking consistency of persistent pages, mark
// the expired ones with the file missing time and recover the previously
@@ -40,7 +40,7 @@ class PersistentPageConsistencyCheckTask : public Task {
};
PersistentPageConsistencyCheckTask(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
ArchiveManager* archive_manager,
ClientPolicyController* policy_controller,
base::Time check_time,
@@ -54,7 +54,7 @@ class PersistentPageConsistencyCheckTask : public Task {
void OnPersistentPageConsistencyCheckDone(CheckResult result);
// The store containing the offline pages. Not owned.
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
// The archive manager storing archive directories. Not owned.
ArchiveManager* archive_manager_;
// The policy controller which is used to acquire names of namespaces. Not
diff --git a/chromium/components/offline_pages/core/model/persistent_page_consistency_check_task_unittest.cc b/chromium/components/offline_pages/core/model/persistent_page_consistency_check_task_unittest.cc
index df1b7dfda76..e8da209c3e6 100644
--- a/chromium/components/offline_pages/core/model/persistent_page_consistency_check_task_unittest.cc
+++ b/chromium/components/offline_pages/core/model/persistent_page_consistency_check_task_unittest.cc
@@ -5,7 +5,7 @@
#include "components/offline_pages/core/model/persistent_page_consistency_check_task.h"
#include "base/bind.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/time/time.h"
#include "build/build_config.h"
diff --git a/chromium/components/offline_pages/core/model/start_offline_page_upgrade_task.cc b/chromium/components/offline_pages/core/model/start_offline_page_upgrade_task.cc
index b97698f9571..17379505750 100644
--- a/chromium/components/offline_pages/core/model/start_offline_page_upgrade_task.cc
+++ b/chromium/components/offline_pages/core/model/start_offline_page_upgrade_task.cc
@@ -7,7 +7,7 @@
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/sys_info.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_store_utils.h"
#include "sql/connection.h"
#include "sql/statement.h"
@@ -28,7 +28,7 @@ StartUpgradeResult StartOfflinePageUpgradeSync(
if (!transaction.Begin())
return StartUpgradeResult(StartUpgradeStatus::DB_ERROR);
- const char kSql[] =
+ static const char kSql[] =
"SELECT file_path, file_size, digest"
" FROM offlinepages_v1 WHERE offline_id = ?";
sql::Statement select_statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
@@ -56,7 +56,7 @@ StartUpgradeResult StartOfflinePageUpgradeSync(
// Conditions for upgrade are met here.
// Update remaining attempts in DB and complete task.
- const char kUpdateSql[] =
+ static const char kUpdateSql[] =
"UPDATE offlinepages_v1 SET upgrade_attempt = upgrade_attempt - 1 "
" WHERE offline_id = ?";
sql::Statement update_statement(
@@ -73,7 +73,7 @@ StartUpgradeResult StartOfflinePageUpgradeSync(
} // namespace
StartOfflinePageUpgradeTask::StartOfflinePageUpgradeTask(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
int64_t offline_id,
const base::FilePath& target_directory,
StartUpgradeCallback callback)
diff --git a/chromium/components/offline_pages/core/model/start_offline_page_upgrade_task.h b/chromium/components/offline_pages/core/model/start_offline_page_upgrade_task.h
index 14c8b9be2ad..f01f1e7c455 100644
--- a/chromium/components/offline_pages/core/model/start_offline_page_upgrade_task.h
+++ b/chromium/components/offline_pages/core/model/start_offline_page_upgrade_task.h
@@ -15,13 +15,13 @@
namespace offline_pages {
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
// This task is responsible for starting the upgrade process for an offline
// page.
class StartOfflinePageUpgradeTask : public Task {
public:
- StartOfflinePageUpgradeTask(OfflinePageMetadataStoreSQL* store,
+ StartOfflinePageUpgradeTask(OfflinePageMetadataStore* store,
int64_t offline_id,
const base::FilePath& target_directory,
StartUpgradeCallback callback);
@@ -34,7 +34,7 @@ class StartOfflinePageUpgradeTask : public Task {
void InformUpgradeAttemptDone(StartUpgradeResult result);
// The store containing the pages to be cleared. Not owned.
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
// ID of the item that needs to be updated.
int64_t offline_id_;
diff --git a/chromium/components/offline_pages/core/model/startup_maintenance_task.cc b/chromium/components/offline_pages/core/model/startup_maintenance_task.cc
index 20f3ffb614c..4e303fd5f21 100644
--- a/chromium/components/offline_pages/core/model/startup_maintenance_task.cc
+++ b/chromium/components/offline_pages/core/model/startup_maintenance_task.cc
@@ -17,7 +17,7 @@
#include "components/offline_pages/core/archive_manager.h"
#include "components/offline_pages/core/client_policy_controller.h"
#include "components/offline_pages/core/offline_page_client_policy.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_store_utils.h"
#include "sql/connection.h"
#include "sql/statement.h"
@@ -274,7 +274,7 @@ bool StartupMaintenanceSync(
} // namespace
StartupMaintenanceTask::StartupMaintenanceTask(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
ArchiveManager* archive_manager,
ClientPolicyController* policy_controller)
: store_(store),
diff --git a/chromium/components/offline_pages/core/model/startup_maintenance_task.h b/chromium/components/offline_pages/core/model/startup_maintenance_task.h
index 34c3b1324c1..6f1bb30fd20 100644
--- a/chromium/components/offline_pages/core/model/startup_maintenance_task.h
+++ b/chromium/components/offline_pages/core/model/startup_maintenance_task.h
@@ -13,14 +13,14 @@ namespace offline_pages {
class ArchiveManager;
class ClientPolicyController;
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
// This task is responsible for executing maintenance sub-tasks during Chrome
// startup, including: temporary page consistency check, legacy directory
// cleaning and report storage usage UMA.
class StartupMaintenanceTask : public Task {
public:
- StartupMaintenanceTask(OfflinePageMetadataStoreSQL* store,
+ StartupMaintenanceTask(OfflinePageMetadataStore* store,
ArchiveManager* archive_manager,
ClientPolicyController* policy_controller);
~StartupMaintenanceTask() override;
@@ -32,7 +32,7 @@ class StartupMaintenanceTask : public Task {
void OnStartupMaintenanceDone(bool result);
// The store containing the offline pages. Not owned.
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
// The archive manager storing archive directories. Not owned.
ArchiveManager* archive_manager_;
// The policy controller which is used to acquire names of namespaces. Not
diff --git a/chromium/components/offline_pages/core/model/startup_maintenance_task_unittest.cc b/chromium/components/offline_pages/core/model/startup_maintenance_task_unittest.cc
index bbc1e53f917..7fcbabcc873 100644
--- a/chromium/components/offline_pages/core/model/startup_maintenance_task_unittest.cc
+++ b/chromium/components/offline_pages/core/model/startup_maintenance_task_unittest.cc
@@ -9,7 +9,7 @@
#include "base/bind.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "build/build_config.h"
#include "components/offline_pages/core/client_namespace_constants.h"
#include "components/offline_pages/core/model/model_task_test_base.h"
diff --git a/chromium/components/offline_pages/core/model/store_thumbnail_task.cc b/chromium/components/offline_pages/core/model/store_thumbnail_task.cc
index 8a153a5ed2a..48c1c440569 100644
--- a/chromium/components/offline_pages/core/model/store_thumbnail_task.cc
+++ b/chromium/components/offline_pages/core/model/store_thumbnail_task.cc
@@ -4,7 +4,7 @@
#include "components/offline_pages/core/model/store_thumbnail_task.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_store_utils.h"
#include "sql/connection.h"
#include "sql/statement.h"
@@ -31,7 +31,7 @@ bool StoreThumbnailSync(const OfflinePageThumbnail& thumbnail,
} // namespace
StoreThumbnailTask::StoreThumbnailTask(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
OfflinePageThumbnail thumbnail,
base::OnceCallback<void(bool)> complete_callback)
: store_(store),
diff --git a/chromium/components/offline_pages/core/model/store_thumbnail_task.h b/chromium/components/offline_pages/core/model/store_thumbnail_task.h
index a0ce7cbc62f..d48b44fa05f 100644
--- a/chromium/components/offline_pages/core/model/store_thumbnail_task.h
+++ b/chromium/components/offline_pages/core/model/store_thumbnail_task.h
@@ -13,14 +13,14 @@
#include "components/offline_pages/core/task.h"
namespace offline_pages {
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
// StoreThumbnailTask stores a thumbnail in the page_thumbnails table.
class StoreThumbnailTask : public Task {
public:
typedef base::OnceCallback<void(bool)> CompleteCallback;
- StoreThumbnailTask(OfflinePageMetadataStoreSQL* store,
+ StoreThumbnailTask(OfflinePageMetadataStore* store,
OfflinePageThumbnail thumbnail,
CompleteCallback complete_callback);
~StoreThumbnailTask() override;
@@ -31,7 +31,7 @@ class StoreThumbnailTask : public Task {
private:
void Complete(bool success);
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
OfflinePageThumbnail thumbnail_;
CompleteCallback complete_callback_;
base::WeakPtrFactory<StoreThumbnailTask> weak_ptr_factory_;
diff --git a/chromium/components/offline_pages/core/model/store_thumbnail_task_unittest.cc b/chromium/components/offline_pages/core/model/store_thumbnail_task_unittest.cc
index f5b9b6981ae..7dc95df8452 100644
--- a/chromium/components/offline_pages/core/model/store_thumbnail_task_unittest.cc
+++ b/chromium/components/offline_pages/core/model/store_thumbnail_task_unittest.cc
@@ -10,7 +10,7 @@
#include "base/test/mock_callback.h"
#include "components/offline_pages/core/model/get_thumbnail_task.h"
#include "components/offline_pages/core/model/model_task_test_base.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_store_utils.h"
namespace offline_pages {
diff --git a/chromium/components/offline_pages/core/model/update_file_path_task.cc b/chromium/components/offline_pages/core/model/update_file_path_task.cc
index 6ff0dfd337f..a4cc54417ae 100644
--- a/chromium/components/offline_pages/core/model/update_file_path_task.cc
+++ b/chromium/components/offline_pages/core/model/update_file_path_task.cc
@@ -7,7 +7,7 @@
#include "base/bind.h"
#include "components/offline_pages/core/client_namespace_constants.h"
#include "components/offline_pages/core/model/offline_page_model_utils.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_store_utils.h"
#include "sql/connection.h"
#include "sql/statement.h"
@@ -28,7 +28,7 @@ bool UpdateFilePathSync(const base::FilePath& new_file_path,
return false;
// Update the file_path to point to the new path.
- const char kSqlUpdate[] =
+ static const char kSqlUpdate[] =
"UPDATE OR IGNORE offlinepages_v1"
" SET file_path = ?"
" WHERE offline_id = ?";
@@ -49,7 +49,7 @@ bool UpdateFilePathSync(const base::FilePath& new_file_path,
} // namespace
-UpdateFilePathTask::UpdateFilePathTask(OfflinePageMetadataStoreSQL* store,
+UpdateFilePathTask::UpdateFilePathTask(OfflinePageMetadataStore* store,
int64_t offline_id,
const base::FilePath& file_path,
UpdateFilePathDoneCallback callback)
diff --git a/chromium/components/offline_pages/core/model/update_file_path_task.h b/chromium/components/offline_pages/core/model/update_file_path_task.h
index da61bf6d114..308e425930d 100644
--- a/chromium/components/offline_pages/core/model/update_file_path_task.h
+++ b/chromium/components/offline_pages/core/model/update_file_path_task.h
@@ -16,13 +16,13 @@ namespace offline_pages {
using ReadResult = GetPagesTask::ReadResult;
-class OfflinePageMetadataStoreSQL;
+class OfflinePageMetadataStore;
// Task that updates the file path in the metadata store. It takes the offline
// ID of the page accessed, the new file path, and the completion callback.
class UpdateFilePathTask : public Task {
public:
- UpdateFilePathTask(OfflinePageMetadataStoreSQL* store,
+ UpdateFilePathTask(OfflinePageMetadataStore* store,
int64_t offline_id,
const base::FilePath& file_path,
UpdateFilePathDoneCallback callback);
@@ -35,7 +35,7 @@ class UpdateFilePathTask : public Task {
void OnUpdateFilePathDone(bool result);
// The metadata store used to update the page. Not owned.
- OfflinePageMetadataStoreSQL* store_;
+ OfflinePageMetadataStore* store_;
int64_t offline_id_;
base::FilePath file_path_;
diff --git a/chromium/components/offline_pages/core/offline_page_archiver.cc b/chromium/components/offline_pages/core/offline_page_archiver.cc
index e77af89c01f..231fe1fd90c 100644
--- a/chromium/components/offline_pages/core/offline_page_archiver.cc
+++ b/chromium/components/offline_pages/core/offline_page_archiver.cc
@@ -4,43 +4,79 @@
#include "components/offline_pages/core/offline_page_archiver.h"
-#include "base/strings/utf_string_conversions.h"
+#include <errno.h>
+#include <utility>
-#include "base/bind_helpers.h"
-#include "base/files/file_path.h"
+#include "base/bind.h"
#include "base/files/file_util.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task_runner_util.h"
#include "components/offline_pages/core/model/offline_page_model_taskified.h"
#include "components/offline_pages/core/model/offline_page_model_utils.h"
#include "components/offline_pages/core/offline_store_utils.h"
#include "components/offline_pages/core/system_download_manager.h"
+namespace offline_pages {
+
namespace {
+const char* kMoveFileFailureReason =
+ "OfflinePages.PublishArchive.MoveFileFailureReason";
+const int kSourceMissing = 0;
+const int kDestinationMissing = 1;
+
+using offline_pages::SavePageResult;
+
// Helper function to do the move and register synchronously. Make sure this is
// called from a background thread.
-void MoveAndRegisterArchive(
+PublishArchiveResult MoveAndRegisterArchive(
const offline_pages::OfflinePageItem& offline_page,
const base::FilePath& publish_directory,
- offline_pages::SystemDownloadManager* download_manager,
- offline_pages::PublishArchiveResult* archive_result) {
+ offline_pages::SystemDownloadManager* download_manager) {
+ PublishArchiveResult archive_result;
// Calculate the new file name.
base::FilePath new_file_path =
offline_pages::model_utils::GenerateUniqueFilenameForOfflinePage(
offline_page.title, offline_page.url, publish_directory);
+ // Create the destination directory if it does not already exist.
+ if (!publish_directory.empty() && !base::DirectoryExists(publish_directory)) {
+ base::File::Error file_error;
+ if (!base::CreateDirectoryAndGetError(publish_directory, &file_error)) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "OfflinePages.PublishArchive.CreateDirectoryError", -file_error,
+ -base::File::Error::FILE_ERROR_MAX);
+ }
+ }
+
// Move the file.
bool moved = base::Move(offline_page.file_path, new_file_path);
if (!moved) {
- archive_result->move_result =
- offline_pages::SavePageResult::FILE_MOVE_FAILED;
- return;
+ archive_result.move_result = SavePageResult::FILE_MOVE_FAILED;
+ DVLOG(0) << "OfflinePage publishing file move failure errno is " << errno
+ << " " << __func__;
+ base::UmaHistogramSparse("OfflinePages.PublishArchive.MoveFileError",
+ errno);
+
+ if (!base::PathExists(offline_page.file_path)) {
+ DVLOG(0) << "Can't copy from non-existent path, from "
+ << offline_page.file_path << " " << __func__;
+ base::UmaHistogramBoolean(kMoveFileFailureReason, kSourceMissing);
+ }
+ if (!base::PathExists(publish_directory)) {
+ DVLOG(0) << "Target directory does not exist, " << publish_directory
+ << " " << __func__;
+ base::UmaHistogramBoolean(kMoveFileFailureReason, kDestinationMissing);
+ }
+ return archive_result;
}
// Tell the download manager about our file, get back an id.
if (!download_manager->IsDownloadManagerInstalled()) {
- archive_result->move_result =
- offline_pages::SavePageResult::ADD_TO_DOWNLOAD_MANAGER_FAILED;
- return;
+ archive_result.move_result = SavePageResult::ADD_TO_DOWNLOAD_MANAGER_FAILED;
+ return archive_result;
}
// TODO(petewil): Handle empty page title.
@@ -52,36 +88,34 @@ void MoveAndRegisterArchive(
offline_pages::store_utils::ToDatabaseFilePath(new_file_path),
offline_page.file_size, offline_page.url.spec(), std::string());
if (download_id == 0LL) {
- archive_result->move_result =
- offline_pages::SavePageResult::ADD_TO_DOWNLOAD_MANAGER_FAILED;
- return;
+ archive_result.move_result = SavePageResult::ADD_TO_DOWNLOAD_MANAGER_FAILED;
+ return archive_result;
}
// Put results into the result object.
- archive_result->move_result = offline_pages::SavePageResult::SUCCESS;
- archive_result->new_file_path = new_file_path;
- archive_result->download_id = download_id;
+ archive_result.move_result = SavePageResult::SUCCESS;
+ archive_result.new_file_path = new_file_path;
+ archive_result.download_id = download_id;
- return;
+ return archive_result;
}
} // namespace
-namespace offline_pages {
-
void OfflinePageArchiver::PublishArchive(
const OfflinePageItem& offline_page,
const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
const base::FilePath& publish_directory,
SystemDownloadManager* download_manager,
- PublishArchiveDoneCallback archive_done_callback) {
- PublishArchiveResult* archive_results = new PublishArchiveResult();
- background_task_runner->PostTaskAndReply(
- FROM_HERE,
+ PublishArchiveDoneCallback publish_done_callback) {
+ // Note: once the |publish_done_callback| is invoked it is very likely that
+ // this instance will be destroyed. So all parameters sent to it must not be
+ // bound to the lifetime on this.
+ base::PostTaskAndReplyWithResult(
+ background_task_runner.get(), FROM_HERE,
base::BindOnce(&MoveAndRegisterArchive, offline_page, publish_directory,
- download_manager, base::Unretained(archive_results)),
- base::BindOnce(std::move(archive_done_callback), offline_page,
- base::Owned(archive_results)));
+ download_manager),
+ base::BindOnce(std::move(publish_done_callback), offline_page));
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/offline_page_archiver.h b/chromium/components/offline_pages/core/offline_page_archiver.h
index 81fb7a59ea0..8bffda9bc28 100644
--- a/chromium/components/offline_pages/core/offline_page_archiver.h
+++ b/chromium/components/offline_pages/core/offline_page_archiver.h
@@ -5,13 +5,12 @@
#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_ARCHIVER_H_
#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_ARCHIVER_H_
-#include <stdint.h>
+#include <cstdint>
+#include <string>
#include "base/callback.h"
#include "base/files/file_path.h"
-#include "base/memory/weak_ptr.h"
#include "base/strings/string16.h"
-#include "base/task_scheduler/post_task.h"
#include "components/offline_pages/core/offline_page_item.h"
#include "components/offline_pages/core/offline_page_types.h"
#include "url/gurl.h"
@@ -36,9 +35,6 @@ struct PublishArchiveResult {
int64_t download_id;
};
-using PublishArchiveDoneCallback =
- base::OnceCallback<void(const OfflinePageItem&, PublishArchiveResult*)>;
-
// Interface of a class responsible for creation of the archive for offline use.
//
// Archiver will be implemented by embedder and may have additional methods that
@@ -94,14 +90,17 @@ class OfflinePageArchiver {
bool use_page_problem_detectors;
};
- typedef base::Callback<void(OfflinePageArchiver* /* archiver */,
- ArchiverResult /* result */,
+ using CreateArchiveCallback =
+ base::OnceCallback<void(ArchiverResult /* result */,
const GURL& /* url */,
const base::FilePath& /* file_path */,
const base::string16& /* title */,
int64_t /* file_size */,
- const std::string& /* digest */)>
- CreateArchiveCallback;
+ const std::string& /* digest */)>;
+
+ using PublishArchiveDoneCallback =
+ base::OnceCallback<void(const OfflinePageItem& /* offline_page */,
+ PublishArchiveResult /* archive_result */)>;
virtual ~OfflinePageArchiver() {}
@@ -111,10 +110,13 @@ class OfflinePageArchiver {
virtual void CreateArchive(const base::FilePath& archives_dir,
const CreateArchiveParams& create_archive_params,
content::WebContents* web_contents,
- const CreateArchiveCallback& callback) = 0;
+ CreateArchiveCallback callback) = 0;
// Publishes the page on a background thread, then returns to the
// OfflinePageModelTaskified's done callback.
+ //
+ // TODO(https://crbug.com/849424): move the publish logic out of the archiver
+ // as it doesn't depend on the embedder code as creation logic does.
virtual void PublishArchive(
const OfflinePageItem& offline_page,
const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
diff --git a/chromium/components/offline_pages/core/offline_page_archiver_unittest.cc b/chromium/components/offline_pages/core/offline_page_archiver_unittest.cc
index a56f60b925e..4740589695b 100644
--- a/chromium/components/offline_pages/core/offline_page_archiver_unittest.cc
+++ b/chromium/components/offline_pages/core/offline_page_archiver_unittest.cc
@@ -26,7 +26,7 @@ class TestOfflinePageArchiver : public OfflinePageArchiver {
void CreateArchive(const base::FilePath& archives_dir,
const CreateArchiveParams& create_archive_params,
content::WebContents* web_contents,
- const CreateArchiveCallback& callback) override {}
+ CreateArchiveCallback callback) override {}
};
class OfflinePageArchiverTest
@@ -65,9 +65,9 @@ class OfflinePageArchiverTest
return weak_ptr_factory_.GetWeakPtr();
}
- void PublishArchiveDone(const SavePageCallback& save_page_callback,
+ void PublishArchiveDone(SavePageCallback save_page_callback,
const OfflinePageItem& offline_page,
- PublishArchiveResult* archive_result);
+ PublishArchiveResult archive_result);
private:
base::ScopedTempDir temporary_dir_;
@@ -87,10 +87,10 @@ void OfflinePageArchiverTest::SetUp() {
}
void OfflinePageArchiverTest::PublishArchiveDone(
- const SavePageCallback& save_page_callback,
+ SavePageCallback save_page_callback,
const OfflinePageItem& offline_page,
- PublishArchiveResult* archive_result) {
- publish_archive_result_ = *archive_result;
+ PublishArchiveResult archive_result) {
+ publish_archive_result_ = archive_result;
}
void OfflinePageArchiverTest::PumpLoop() {
@@ -112,7 +112,7 @@ TEST_F(OfflinePageArchiverTest, PublishArchive) {
offline_page, base::ThreadTaskRunnerHandle::Get(),
public_archive_dir_path(), download_manager.get(),
base::BindOnce(&OfflinePageArchiverTest::PublishArchiveDone,
- get_weak_ptr(), save_page_callback));
+ get_weak_ptr(), std::move(save_page_callback)));
PumpLoop();
EXPECT_EQ(SavePageResult::SUCCESS, publish_archive_result().move_result);
diff --git a/chromium/components/offline_pages/core/offline_page_feature.cc b/chromium/components/offline_pages/core/offline_page_feature.cc
index f25efba9519..d6c31ddc7c7 100644
--- a/chromium/components/offline_pages/core/offline_page_feature.cc
+++ b/chromium/components/offline_pages/core/offline_page_feature.cc
@@ -17,6 +17,7 @@ namespace {
const char kOfflinePagesUseTestingSnapshotDelay[] =
"short-offline-page-snapshot-delay-for-test";
+bool limitless_prefetching_enabled = false;
} // namespace
namespace offline_pages {
@@ -31,7 +32,7 @@ const base::Feature kOfflinePagesCTFeature{"OfflinePagesCT",
base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kOfflinePagesSharingFeature{
- "OfflinePagesSharing", base::FEATURE_DISABLED_BY_DEFAULT};
+ "OfflinePagesSharing", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kOfflinePagesSvelteConcurrentLoadingFeature{
"OfflinePagesSvelteConcurrentLoading", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -67,7 +68,7 @@ const base::Feature kOfflinePagesDescriptivePendingStatusFeature{
"OfflinePagesDescriptivePendingStatus", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kOfflinePagesInDownloadHomeOpenInCctFeature{
- "OfflinePagesInDownloadHomeOpenInCct", base::FEATURE_DISABLED_BY_DEFAULT};
+ "OfflinePagesInDownloadHomeOpenInCct", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kOfflinePagesCTSuppressNotificationsFeature{
"OfflinePagesCTSuppressNotifications", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -75,6 +76,9 @@ const base::Feature kOfflinePagesCTSuppressNotificationsFeature{
const base::Feature kOfflinePagesShowAlternateDinoPageFeature{
"OfflinePagesShowAlternateDinoPage", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kOfflineIndicatorFeature{"OfflineIndicator",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
const char kPrefetchingOfflinePagesExperimentsOption[] = "exp";
bool IsOfflineBookmarksEnabled() {
@@ -113,7 +117,11 @@ bool IsOfflinePagesPrefetchingUIEnabled() {
bool IsLimitlessPrefetchingEnabled() {
// TODO(https://crbug.com/803584): fix limitless mode or fully remove it.
- return false;
+ return limitless_prefetching_enabled;
+}
+
+void SetLimitlessPrefetchingEnabledForTesting(bool enabled) {
+ limitless_prefetching_enabled = enabled;
}
bool IsOfflinePagesLoadSignalCollectingEnabled() {
@@ -169,4 +177,8 @@ std::string GetPrefetchingOfflinePagesExperimentTag() {
kPrefetchingOfflinePagesExperimentsOption);
}
+bool IsOfflineIndicatorFeatureEnabled() {
+ return base::FeatureList::IsEnabled(kOfflineIndicatorFeature);
+}
+
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/offline_page_feature.h b/chromium/components/offline_pages/core/offline_page_feature.h
index d1bda409128..1ef0d1664b6 100644
--- a/chromium/components/offline_pages/core/offline_page_feature.h
+++ b/chromium/components/offline_pages/core/offline_page_feature.h
@@ -28,6 +28,7 @@ extern const base::Feature kOfflinePagesInDownloadHomeOpenInCctFeature;
extern const base::Feature kOfflinePagesDescriptiveFailStatusFeature;
extern const base::Feature kOfflinePagesCTSuppressNotificationsFeature;
extern const base::Feature kOfflinePagesShowAlternateDinoPageFeature;
+extern const base::Feature kOfflineIndicatorFeature;
// The parameter name used to find the experiment tag for prefetching offline
// pages.
@@ -62,6 +63,9 @@ bool IsOfflinePagesPrefetchingUIEnabled();
// usage limits.
bool IsLimitlessPrefetchingEnabled();
+// Enables or disabled limitless prefetching. Provided for testing only.
+void SetLimitlessPrefetchingEnabledForTesting(bool enabled);
+
// Returns true if we enable load timing signals to be collected.
bool IsOfflinePagesLoadSignalCollectingEnabled();
@@ -106,6 +110,9 @@ bool ShouldShowAlternateDinoPage();
// the request.
std::string GetPrefetchingOfflinePagesExperimentTag();
+// Returns true if offline indicator UI is shown when the user is offline.
+bool IsOfflineIndicatorFeatureEnabled();
+
} // namespace offline_pages
#endif // COMPONENTS_OFFLINE_PAGES_OFFLINE_PAGE_FEATURE_H_
diff --git a/chromium/components/offline_pages/core/offline_page_feature_unittest.cc b/chromium/components/offline_pages/core/offline_page_feature_unittest.cc
index e68635d84c9..41913b07b01 100644
--- a/chromium/components/offline_pages/core/offline_page_feature_unittest.cc
+++ b/chromium/components/offline_pages/core/offline_page_feature_unittest.cc
@@ -31,8 +31,8 @@ TEST(OfflinePageFeatureTest, OffliningRecentPages) {
}
TEST(OfflinePageFeatureTest, OfflinePagesSharing) {
- // Disabled by default.
- EXPECT_FALSE(offline_pages::IsOfflinePagesSharingEnabled());
+ // Enabled by default.
+ EXPECT_TRUE(offline_pages::IsOfflinePagesSharingEnabled());
// Check if helper method works correctly when the features is disabled.
base::test::ScopedFeatureList scoped_feature_list;
@@ -114,14 +114,14 @@ TEST(OfflinePageFeatureTest, OfflinePagesLimitlessPrefetching) {
}
TEST(OfflinePageFeatureTest, OfflinePagesInDownloadHomeOpenInCct) {
- // Disabled by default.
- EXPECT_FALSE(offline_pages::ShouldOfflinePagesInDownloadHomeOpenInCct());
+ // Enabled by default.
+ EXPECT_TRUE(offline_pages::ShouldOfflinePagesInDownloadHomeOpenInCct());
- // Check if helper method works correctly when the features is enabled.
+ // Check if helper method works correctly when the features is disabled.
base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndEnableFeature(
+ scoped_feature_list.InitAndDisableFeature(
kOfflinePagesInDownloadHomeOpenInCctFeature);
- EXPECT_TRUE(offline_pages::ShouldOfflinePagesInDownloadHomeOpenInCct());
+ EXPECT_FALSE(offline_pages::ShouldOfflinePagesInDownloadHomeOpenInCct());
}
TEST(OfflinePageFeatureTest, OfflinePagesDescriptiveFailStatus) {
diff --git a/chromium/components/offline_pages/core/offline_page_metadata_store_sql.cc b/chromium/components/offline_pages/core/offline_page_metadata_store.cc
index bbf8979786b..086e51d5a32 100644
--- a/chromium/components/offline_pages/core/offline_page_metadata_store_sql.cc
+++ b/chromium/components/offline_pages/core/offline_page_metadata_store.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/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "base/bind.h"
#include "base/files/file_path.h"
@@ -24,9 +24,9 @@
namespace offline_pages {
-const int OfflinePageMetadataStoreSQL::kFirstPostLegacyVersion;
-const int OfflinePageMetadataStoreSQL::kCurrentVersion;
-const int OfflinePageMetadataStoreSQL::kCompatibleVersion;
+const int OfflinePageMetadataStore::kFirstPostLegacyVersion;
+const int OfflinePageMetadataStore::kCurrentVersion;
+const int OfflinePageMetadataStore::kCompatibleVersion;
namespace {
@@ -40,24 +40,25 @@ void ReportStoreEvent(OfflinePagesStoreEvent event) {
}
bool CreateOfflinePagesTable(sql::Connection* db) {
- const char kSql[] = "CREATE TABLE IF NOT EXISTS " OFFLINE_PAGES_TABLE_NAME
- "(offline_id INTEGER PRIMARY KEY NOT NULL,"
- " creation_time INTEGER NOT NULL,"
- " file_size INTEGER NOT NULL,"
- " last_access_time INTEGER NOT NULL,"
- " access_count INTEGER NOT NULL,"
- " system_download_id INTEGER NOT NULL DEFAULT 0,"
- " file_missing_time INTEGER NOT NULL DEFAULT 0,"
- " upgrade_attempt INTEGER NOT NULL DEFAULT 0,"
- " client_namespace VARCHAR NOT NULL,"
- " client_id VARCHAR NOT NULL,"
- " online_url VARCHAR NOT NULL,"
- " file_path VARCHAR NOT NULL,"
- " title VARCHAR NOT NULL DEFAULT '',"
- " original_url VARCHAR NOT NULL DEFAULT '',"
- " request_origin VARCHAR NOT NULL DEFAULT '',"
- " digest VARCHAR NOT NULL DEFAULT ''"
- ")";
+ static const char kSql[] =
+ "CREATE TABLE IF NOT EXISTS " OFFLINE_PAGES_TABLE_NAME
+ "(offline_id INTEGER PRIMARY KEY NOT NULL,"
+ " creation_time INTEGER NOT NULL,"
+ " file_size INTEGER NOT NULL,"
+ " last_access_time INTEGER NOT NULL,"
+ " access_count INTEGER NOT NULL,"
+ " system_download_id INTEGER NOT NULL DEFAULT 0,"
+ " file_missing_time INTEGER NOT NULL DEFAULT 0,"
+ " upgrade_attempt INTEGER NOT NULL DEFAULT 0,"
+ " client_namespace VARCHAR NOT NULL,"
+ " client_id VARCHAR NOT NULL,"
+ " online_url VARCHAR NOT NULL,"
+ " file_path VARCHAR NOT NULL,"
+ " title VARCHAR NOT NULL DEFAULT '',"
+ " original_url VARCHAR NOT NULL DEFAULT '',"
+ " request_origin VARCHAR NOT NULL DEFAULT '',"
+ " digest VARCHAR NOT NULL DEFAULT ''"
+ ")";
return db->Execute(kSql);
}
@@ -76,7 +77,7 @@ bool UpgradeWithQuery(sql::Connection* db, const char* upgrade_sql) {
}
bool UpgradeFrom52(sql::Connection* db) {
- const char kSql[] =
+ static const char kSql[] =
"INSERT INTO " OFFLINE_PAGES_TABLE_NAME
" (offline_id, creation_time, file_size, last_access_time, "
"access_count, client_namespace, client_id, "
@@ -90,7 +91,7 @@ bool UpgradeFrom52(sql::Connection* db) {
}
bool UpgradeFrom53(sql::Connection* db) {
- const char kSql[] =
+ static const char kSql[] =
"INSERT INTO " OFFLINE_PAGES_TABLE_NAME
" (offline_id, creation_time, file_size, last_access_time, "
"access_count, client_namespace, client_id, online_url, "
@@ -104,7 +105,7 @@ bool UpgradeFrom53(sql::Connection* db) {
}
bool UpgradeFrom54(sql::Connection* db) {
- const char kSql[] =
+ static const char kSql[] =
"INSERT INTO " OFFLINE_PAGES_TABLE_NAME
" (offline_id, creation_time, file_size, last_access_time, "
"access_count, client_namespace, client_id, online_url, "
@@ -118,7 +119,7 @@ bool UpgradeFrom54(sql::Connection* db) {
}
bool UpgradeFrom55(sql::Connection* db) {
- const char kSql[] =
+ static const char kSql[] =
"INSERT INTO " OFFLINE_PAGES_TABLE_NAME
" (offline_id, creation_time, file_size, last_access_time, "
"access_count, client_namespace, client_id, online_url, "
@@ -132,7 +133,7 @@ bool UpgradeFrom55(sql::Connection* db) {
}
bool UpgradeFrom56(sql::Connection* db) {
- const char kSql[] =
+ static const char kSql[] =
"INSERT INTO " OFFLINE_PAGES_TABLE_NAME
" (offline_id, creation_time, file_size, last_access_time, "
"access_count, client_namespace, client_id, online_url, "
@@ -146,7 +147,7 @@ bool UpgradeFrom56(sql::Connection* db) {
}
bool UpgradeFrom57(sql::Connection* db) {
- const char kSql[] =
+ static const char kSql[] =
"INSERT INTO " OFFLINE_PAGES_TABLE_NAME
" (offline_id, creation_time, file_size, last_access_time, "
"access_count, client_namespace, client_id, online_url, "
@@ -160,7 +161,7 @@ bool UpgradeFrom57(sql::Connection* db) {
}
bool UpgradeFrom61(sql::Connection* db) {
- const char kSql[] =
+ static const char kSql[] =
"INSERT INTO " OFFLINE_PAGES_TABLE_NAME
" (offline_id, creation_time, file_size, last_access_time, "
"access_count, client_namespace, client_id, online_url, "
@@ -174,7 +175,7 @@ bool UpgradeFrom61(sql::Connection* db) {
}
bool CreatePageThumbnailsTable(sql::Connection* db) {
- const char kSql[] =
+ static const char kSql[] =
"CREATE TABLE IF NOT EXISTS page_thumbnails"
" (offline_id INTEGER PRIMARY KEY NOT NULL,"
" expiration INTEGER NOT NULL,"
@@ -195,8 +196,8 @@ bool CreateLatestSchema(sql::Connection* db) {
return false;
sql::MetaTable meta_table;
- if (!meta_table.Init(db, OfflinePageMetadataStoreSQL::kCurrentVersion,
- OfflinePageMetadataStoreSQL::kCompatibleVersion))
+ if (!meta_table.Init(db, OfflinePageMetadataStore::kCurrentVersion,
+ OfflinePageMetadataStore::kCompatibleVersion))
return false;
return transaction.Commit();
@@ -235,8 +236,8 @@ bool UpgradeFromLegacyVersion(sql::Connection* db) {
}
sql::MetaTable meta_table;
- if (!meta_table.Init(db, OfflinePageMetadataStoreSQL::kFirstPostLegacyVersion,
- OfflinePageMetadataStoreSQL::kCompatibleVersion))
+ if (!meta_table.Init(db, OfflinePageMetadataStore::kFirstPostLegacyVersion,
+ OfflinePageMetadataStore::kCompatibleVersion))
return false;
return transaction.Commit();
@@ -288,8 +289,8 @@ bool CreateSchema(sql::Connection* db) {
}
sql::MetaTable meta_table;
- if (!meta_table.Init(db, OfflinePageMetadataStoreSQL::kCurrentVersion,
- OfflinePageMetadataStoreSQL::kCompatibleVersion))
+ if (!meta_table.Init(db, OfflinePageMetadataStore::kCurrentVersion,
+ OfflinePageMetadataStore::kCompatibleVersion))
return false;
for (;;) {
@@ -302,7 +303,7 @@ bool CreateSchema(sql::Connection* db) {
if (!UpgradeFromVersion2ToVersion3(db, &meta_table))
return false;
break;
- case OfflinePageMetadataStoreSQL::kCurrentVersion:
+ case OfflinePageMetadataStore::kCurrentVersion:
return true;
default:
return false;
@@ -364,9 +365,9 @@ void CloseDatabaseSync(
} // anonymous namespace
// static
-constexpr base::TimeDelta OfflinePageMetadataStoreSQL::kClosingDelay;
+constexpr base::TimeDelta OfflinePageMetadataStore::kClosingDelay;
-OfflinePageMetadataStoreSQL::OfflinePageMetadataStoreSQL(
+OfflinePageMetadataStore::OfflinePageMetadataStore(
scoped_refptr<base::SequencedTaskRunner> background_task_runner)
: background_task_runner_(std::move(background_task_runner)),
in_memory_(true),
@@ -374,7 +375,7 @@ OfflinePageMetadataStoreSQL::OfflinePageMetadataStoreSQL(
weak_ptr_factory_(this),
closing_weak_ptr_factory_(this) {}
-OfflinePageMetadataStoreSQL::OfflinePageMetadataStoreSQL(
+OfflinePageMetadataStore::OfflinePageMetadataStore(
scoped_refptr<base::SequencedTaskRunner> background_task_runner,
const base::FilePath& path)
: background_task_runner_(std::move(background_task_runner)),
@@ -384,25 +385,25 @@ OfflinePageMetadataStoreSQL::OfflinePageMetadataStoreSQL(
weak_ptr_factory_(this),
closing_weak_ptr_factory_(this) {}
-OfflinePageMetadataStoreSQL::~OfflinePageMetadataStoreSQL() {
+OfflinePageMetadataStore::~OfflinePageMetadataStore() {
if (db_.get() &&
!background_task_runner_->DeleteSoon(FROM_HERE, db_.release())) {
DLOG(WARNING) << "SQL database will not be deleted.";
}
}
-StoreState OfflinePageMetadataStoreSQL::GetStateForTesting() const {
+StoreState OfflinePageMetadataStore::GetStateForTesting() const {
return state_;
}
-void OfflinePageMetadataStoreSQL::SetStateForTesting(StoreState state,
- bool reset_db) {
+void OfflinePageMetadataStore::SetStateForTesting(StoreState state,
+ bool reset_db) {
state_ = state;
if (reset_db)
db_.reset(nullptr);
}
-void OfflinePageMetadataStoreSQL::InitializeInternal(
+void OfflinePageMetadataStore::InitializeInternal(
base::OnceClosure pending_command) {
TRACE_EVENT_ASYNC_BEGIN1("offline_pages", "Metadata Store", this, "is reopen",
!last_closing_time_.is_null());
@@ -424,12 +425,12 @@ void OfflinePageMetadataStoreSQL::InitializeInternal(
base::PostTaskAndReplyWithResult(
background_task_runner_.get(), FROM_HERE,
base::BindOnce(&InitDatabase, db_.get(), db_file_path_, in_memory_),
- base::BindOnce(&OfflinePageMetadataStoreSQL::OnInitializeInternalDone,
+ base::BindOnce(&OfflinePageMetadataStore::OnInitializeInternalDone,
weak_ptr_factory_.GetWeakPtr(),
std::move(pending_command)));
}
-void OfflinePageMetadataStoreSQL::OnInitializeInternalDone(
+void OfflinePageMetadataStore::OnInitializeInternalDone(
base::OnceClosure pending_command,
bool success) {
TRACE_EVENT_ASYNC_STEP_PAST1("offline_pages", "Metadata Store", this,
@@ -459,7 +460,7 @@ void OfflinePageMetadataStoreSQL::OnInitializeInternalDone(
state_ = StoreState::NOT_LOADED;
}
-void OfflinePageMetadataStoreSQL::CloseInternal() {
+void OfflinePageMetadataStore::CloseInternal() {
if (state_ != StoreState::LOADED) {
ReportStoreEvent(OfflinePagesStoreEvent::STORE_CLOSE_SKIPPED);
return;
@@ -474,11 +475,11 @@ void OfflinePageMetadataStoreSQL::CloseInternal() {
FROM_HERE,
base::BindOnce(
&CloseDatabaseSync, db_.get(), base::ThreadTaskRunnerHandle::Get(),
- base::BindOnce(&OfflinePageMetadataStoreSQL::CloseInternalDone,
+ base::BindOnce(&OfflinePageMetadataStore::CloseInternalDone,
weak_ptr_factory_.GetWeakPtr(), std::move(db_))));
}
-void OfflinePageMetadataStoreSQL::CloseInternalDone(
+void OfflinePageMetadataStore::CloseInternalDone(
std::unique_ptr<sql::Connection> db) {
db.reset();
TRACE_EVENT_ASYNC_STEP_PAST0("offline_pages", "Metadata Store", this,
diff --git a/chromium/components/offline_pages/core/offline_page_metadata_store_sql.h b/chromium/components/offline_pages/core/offline_page_metadata_store.h
index 74d727af180..b62865b125b 100644
--- a/chromium/components/offline_pages/core/offline_page_metadata_store_sql.h
+++ b/chromium/components/offline_pages/core/offline_page_metadata_store.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_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_SQL_H_
-#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_SQL_H_
+#ifndef COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_H_
+#define COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_H_
#include <stdint.h>
@@ -31,7 +31,7 @@ class Connection;
namespace offline_pages {
typedef StoreUpdateResult<OfflinePageItem> OfflinePagesUpdateResult;
-// OfflinePageMetadataStoreSQL keeps metadata for the offline pages in an SQLite
+// OfflinePageMetadataStore keeps metadata for the offline pages in an SQLite
// database.
//
// This store has a history of schema updates in pretty much every release.
@@ -64,7 +64,7 @@ typedef StoreUpdateResult<OfflinePageItem> OfflinePagesUpdateResult;
// |UpgradeFrom54|.
// * Upgrade should use |UpgradeWithQuery| and simply specify SQL command to
// move data from old table (prefixed by temp_) to the new one.
-class OfflinePageMetadataStoreSQL {
+class OfflinePageMetadataStore {
public:
// This enum is used in an UMA histogram. Hence the entries here shouldn't
// be deleted or re-ordered and new ones should be added to the end.
@@ -105,15 +105,15 @@ class OfflinePageMetadataStoreSQL {
// TODO(fgorski): Move to private and expose ForTest factory.
// Applies in PrefetchStore as well.
// Creates the store in memory. Should only be used for testing.
- explicit OfflinePageMetadataStoreSQL(
+ explicit OfflinePageMetadataStore(
scoped_refptr<base::SequencedTaskRunner> background_task_runner);
// Creates the store with database pointing to provided directory.
- OfflinePageMetadataStoreSQL(
+ OfflinePageMetadataStore(
scoped_refptr<base::SequencedTaskRunner> background_task_runner,
const base::FilePath& database_dir);
- ~OfflinePageMetadataStoreSQL();
+ ~OfflinePageMetadataStore();
// Executes a |run_callback| on SQL store on the blocking thread, and posts
// its result back to calling thread through |result_callback|.
@@ -128,10 +128,9 @@ class OfflinePageMetadataStoreSQL {
// and CHECK that state.
if (state_ == StoreState::NOT_LOADED) {
- InitializeInternal(
- base::BindOnce(&OfflinePageMetadataStoreSQL::Execute<T>,
- weak_ptr_factory_.GetWeakPtr(),
- std::move(run_callback), std::move(result_callback)));
+ InitializeInternal(base::BindOnce(
+ &OfflinePageMetadataStore::Execute<T>, weak_ptr_factory_.GetWeakPtr(),
+ std::move(run_callback), std::move(result_callback)));
return;
}
@@ -141,10 +140,9 @@ class OfflinePageMetadataStoreSQL {
// This if allows to run commands later, after store was given a chance to
// initialize. They would be failing immediately otherwise.
if (state_ == StoreState::INITIALIZING) {
- pending_commands_.push_back(
- base::BindOnce(&OfflinePageMetadataStoreSQL::Execute<T>,
- weak_ptr_factory_.GetWeakPtr(),
- std::move(run_callback), std::move(result_callback)));
+ pending_commands_.push_back(base::BindOnce(
+ &OfflinePageMetadataStore::Execute<T>, weak_ptr_factory_.GetWeakPtr(),
+ std::move(run_callback), std::move(result_callback)));
TRACE_EVENT_ASYNC_END1("offline_pages", "Metadata Store: task execution",
this, "postponed", true);
return;
@@ -158,7 +156,7 @@ class OfflinePageMetadataStoreSQL {
base::PostTaskAndReplyWithResult(
background_task_runner_.get(), FROM_HERE,
base::BindOnce(std::move(run_callback), db),
- base::BindOnce(&OfflinePageMetadataStoreSQL::RescheduleClosing<T>,
+ base::BindOnce(&OfflinePageMetadataStore::RescheduleClosing<T>,
weak_ptr_factory_.GetWeakPtr(),
std::move(result_callback)));
}
@@ -181,7 +179,7 @@ class OfflinePageMetadataStoreSQL {
void RescheduleClosing(ResultCallback<T> result_callback, T result) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
- base::BindOnce(&OfflinePageMetadataStoreSQL::CloseInternal,
+ base::BindOnce(&OfflinePageMetadataStore::CloseInternal,
closing_weak_ptr_factory_.GetWeakPtr()),
kClosingDelay);
@@ -223,12 +221,12 @@ class OfflinePageMetadataStoreSQL {
// Time of the last time the store was closed. Kept for metrics reporting.
base::Time last_closing_time_;
- base::WeakPtrFactory<OfflinePageMetadataStoreSQL> weak_ptr_factory_;
- base::WeakPtrFactory<OfflinePageMetadataStoreSQL> closing_weak_ptr_factory_;
+ base::WeakPtrFactory<OfflinePageMetadataStore> weak_ptr_factory_;
+ base::WeakPtrFactory<OfflinePageMetadataStore> closing_weak_ptr_factory_;
- DISALLOW_COPY_AND_ASSIGN(OfflinePageMetadataStoreSQL);
+ DISALLOW_COPY_AND_ASSIGN(OfflinePageMetadataStore);
};
} // namespace offline_pages
-#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_SQL_H_
+#endif // COMPONENTS_OFFLINE_PAGES_CORE_OFFLINE_PAGE_METADATA_STORE_H_
diff --git a/chromium/components/offline_pages/core/offline_page_metadata_store_test_util.cc b/chromium/components/offline_pages/core/offline_page_metadata_store_test_util.cc
index 6a7e7e5d437..67d9518c6c9 100644
--- a/chromium/components/offline_pages/core/offline_page_metadata_store_test_util.cc
+++ b/chromium/components/offline_pages/core/offline_page_metadata_store_test_util.cc
@@ -19,7 +19,7 @@ namespace offline_pages {
namespace {
int64_t GetPageCountSync(sql::Connection* db) {
- const char kSql[] = "SELECT count(*) FROM offlinepages_v1";
+ static const char kSql[] = "SELECT count(*) FROM offlinepages_v1";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
if (statement.Step()) {
return statement.ColumnInt64(0);
@@ -42,12 +42,12 @@ void OfflinePageMetadataStoreTestUtil::BuildStore() {
}
store_.reset(
- new OfflinePageMetadataStoreSQL(task_runner_, temp_directory_.GetPath()));
+ new OfflinePageMetadataStore(task_runner_, temp_directory_.GetPath()));
store_ptr_ = store_.get();
}
void OfflinePageMetadataStoreTestUtil::BuildStoreInMemory() {
- store_.reset(new OfflinePageMetadataStoreSQL(task_runner_));
+ store_.reset(new OfflinePageMetadataStore(task_runner_));
store_ptr_ = store_.get();
}
@@ -57,7 +57,7 @@ void OfflinePageMetadataStoreTestUtil::DeleteStore() {
task_runner_->FastForwardUntilNoTasksRemain();
}
-std::unique_ptr<OfflinePageMetadataStoreSQL>
+std::unique_ptr<OfflinePageMetadataStore>
OfflinePageMetadataStoreTestUtil::ReleaseStore() {
return std::move(store_);
}
@@ -66,9 +66,9 @@ void OfflinePageMetadataStoreTestUtil::InsertItem(const OfflinePageItem& page) {
AddPageResult result;
auto task = std::make_unique<AddPageTask>(
store(), page,
- base::Bind([](AddPageResult* out_result,
- AddPageResult cb_result) { *out_result = cb_result; },
- &result));
+ base::BindOnce([](AddPageResult* out_result,
+ AddPageResult cb_result) { *out_result = cb_result; },
+ &result));
task->Run();
task_runner_->RunUntilIdle();
EXPECT_EQ(AddPageResult::SUCCESS, result);
@@ -90,7 +90,7 @@ OfflinePageMetadataStoreTestUtil::GetPageByOfflineId(int64_t offline_id) {
OfflinePageItem* page = nullptr;
auto task = GetPagesTask::CreateTaskMatchingOfflineId(
store(),
- base::Bind(
+ base::BindOnce(
[](OfflinePageItem** out_page, const OfflinePageItem* cb_page) {
if (cb_page)
*out_page = new OfflinePageItem(*cb_page);
diff --git a/chromium/components/offline_pages/core/offline_page_metadata_store_test_util.h b/chromium/components/offline_pages/core/offline_page_metadata_store_test_util.h
index 700d5dd9ab1..4bffad5a861 100644
--- a/chromium/components/offline_pages/core/offline_page_metadata_store_test_util.h
+++ b/chromium/components/offline_pages/core/offline_page_metadata_store_test_util.h
@@ -12,7 +12,7 @@
#include "base/macros.h"
#include "base/test/simple_test_clock.h"
#include "base/test/test_mock_time_task_runner.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
namespace base {
class ScopedTempDir;
@@ -21,7 +21,7 @@ class SimpleTestClock;
namespace offline_pages {
-// Encapsulates the OfflinePageMetadataStoreSQL and provides synchronous
+// Encapsulates the OfflinePageMetadataStore and provides synchronous
// operations on the store, for test writing convenience.
class OfflinePageMetadataStoreTestUtil {
public:
@@ -36,7 +36,7 @@ class OfflinePageMetadataStoreTestUtil {
// Releases the ownership of currently controlled store. But still keeps a raw
// pointer to the previously owned store in |store_ptr|, until the next time
// BuildStore*() is called.
- std::unique_ptr<OfflinePageMetadataStoreSQL> ReleaseStore();
+ std::unique_ptr<OfflinePageMetadataStore> ReleaseStore();
// Deletes the currently held store that was previously built.
void DeleteStore();
@@ -49,7 +49,7 @@ class OfflinePageMetadataStoreTestUtil {
// Gets offline page by offline_id.
std::unique_ptr<OfflinePageItem> GetPageByOfflineId(int64_t offline_id);
- OfflinePageMetadataStoreSQL* store() { return store_ptr_; }
+ OfflinePageMetadataStore* store() { return store_ptr_; }
base::SimpleTestClock* clock() { return &clock_; }
@@ -61,8 +61,8 @@ class OfflinePageMetadataStoreTestUtil {
// TODO(romax): Refactor the test util along with the similar one used in
// Prefetching, to remove the ownership to the store. And clean up related
// usage of |store_ptr_|.
- std::unique_ptr<OfflinePageMetadataStoreSQL> store_;
- OfflinePageMetadataStoreSQL* store_ptr_;
+ std::unique_ptr<OfflinePageMetadataStore> store_;
+ OfflinePageMetadataStore* store_ptr_;
base::SimpleTestClock clock_;
DISALLOW_COPY_AND_ASSIGN(OfflinePageMetadataStoreTestUtil);
diff --git a/chromium/components/offline_pages/core/offline_page_metadata_store_unittest.cc b/chromium/components/offline_pages/core/offline_page_metadata_store_unittest.cc
index db3e759ce4e..71286a9b7ac 100644
--- a/chromium/components/offline_pages/core/offline_page_metadata_store_unittest.cc
+++ b/chromium/components/offline_pages/core/offline_page_metadata_store_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/offline_pages/core/offline_page_metadata_store_sql.h"
+#include "components/offline_pages/core/offline_page_metadata_store.h"
#include <stdint.h>
@@ -20,7 +20,7 @@
#include "components/offline_pages/core/client_namespace_constants.h"
#include "components/offline_pages/core/model/offline_page_item_generator.h"
#include "components/offline_pages/core/offline_page_item.h"
-#include "components/offline_pages/core/offline_page_metadata_store_sql.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_page_thumbnail.h"
#include "components/offline_pages/core/offline_store_utils.h"
@@ -469,9 +469,9 @@ void BuildTestStoreWithSchemaVersion2(const base::FilePath& file) {
sql::Connection db;
ASSERT_TRUE(db.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db"))));
sql::MetaTable meta_table;
- ASSERT_TRUE(meta_table.Init(&db, OfflinePageMetadataStoreSQL::kCurrentVersion,
- OfflinePageMetadataStoreSQL::kCompatibleVersion));
- const char kSql[] =
+ ASSERT_TRUE(meta_table.Init(&db, OfflinePageMetadataStore::kCurrentVersion,
+ OfflinePageMetadataStore::kCompatibleVersion));
+ static const char kSql[] =
"CREATE TABLE page_thumbnails"
" (offline_id INTEGER PRIMARY KEY NOT NULL,"
" expiration INTEGER NOT NULL,"
@@ -522,7 +522,7 @@ std::vector<OfflinePageItem> GetOfflinePagesSync(sql::Connection* db) {
if (!db)
return result;
- const char kSql[] = "SELECT * FROM " OFFLINE_PAGES_TABLE_V1;
+ static const char kSql[] = "SELECT * FROM " OFFLINE_PAGES_TABLE_V1;
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
while (statement.Step())
@@ -549,8 +549,8 @@ class OfflinePageMetadataStoreTest : public testing::Test {
PumpLoop();
}
- std::unique_ptr<OfflinePageMetadataStoreSQL> BuildStore() {
- auto store = std::make_unique<OfflinePageMetadataStoreSQL>(
+ std::unique_ptr<OfflinePageMetadataStore> BuildStore() {
+ auto store = std::make_unique<OfflinePageMetadataStore>(
base::ThreadTaskRunnerHandle::Get(), TempPath());
PumpLoop();
return store;
@@ -566,14 +566,14 @@ class OfflinePageMetadataStoreTest : public testing::Test {
}
base::FilePath TempPath() const { return temp_directory_.GetPath(); }
- OfflinePageItem CheckThatStoreHasOneItem(OfflinePageMetadataStoreSQL* store) {
+ OfflinePageItem CheckThatStoreHasOneItem(OfflinePageMetadataStore* store) {
std::vector<OfflinePageItem> pages = GetOfflinePages(store);
EXPECT_EQ(1U, pages.size());
return pages[0];
}
void CheckThatOfflinePageCanBeSaved(
- std::unique_ptr<OfflinePageMetadataStoreSQL> store) {
+ std::unique_ptr<OfflinePageMetadataStore> store) {
size_t store_size = GetOfflinePages(store.get()).size();
OfflinePageItem offline_page(GURL(kTestURL), 1234LL, kTestClientId1,
base::FilePath(kFilePath), kFileSize);
@@ -596,7 +596,7 @@ class OfflinePageMetadataStoreTest : public testing::Test {
EXPECT_EQ(offline_page, pages[0]);
}
- void CheckThatPageThumbnailCanBeSaved(OfflinePageMetadataStoreSQL* store) {
+ void CheckThatPageThumbnailCanBeSaved(OfflinePageMetadataStore* store) {
OfflinePageThumbnail thumbnail;
thumbnail.offline_id = kOfflineId;
thumbnail.expiration = kThumbnailExpiration;
@@ -617,17 +617,17 @@ class OfflinePageMetadataStoreTest : public testing::Test {
sql::MetaTable meta_table;
EXPECT_TRUE(meta_table.Init(&connection, 1, 1));
- EXPECT_EQ(OfflinePageMetadataStoreSQL::kCurrentVersion,
+ EXPECT_EQ(OfflinePageMetadataStore::kCurrentVersion,
meta_table.GetVersionNumber());
- EXPECT_EQ(OfflinePageMetadataStoreSQL::kCompatibleVersion,
+ EXPECT_EQ(OfflinePageMetadataStore::kCompatibleVersion,
meta_table.GetCompatibleVersionNumber());
}
void LoadAndCheckStore() {
- auto store = std::make_unique<OfflinePageMetadataStoreSQL>(
+ auto store = std::make_unique<OfflinePageMetadataStore>(
base::ThreadTaskRunnerHandle::Get(), TempPath());
OfflinePageItem item = CheckThatStoreHasOneItem(store.get());
- CheckThatPageThumbnailCanBeSaved((OfflinePageMetadataStoreSQL*)store.get());
+ CheckThatPageThumbnailCanBeSaved((OfflinePageMetadataStore*)store.get());
CheckThatOfflinePageCanBeSaved(std::move(store));
VerifyMetaVersions();
}
@@ -635,7 +635,7 @@ class OfflinePageMetadataStoreTest : public testing::Test {
void LoadAndCheckStoreFromMetaVersion1AndUp() {
// At meta version 1, more items were added to the database for testing,
// which necessitates different checks.
- auto store = std::make_unique<OfflinePageMetadataStoreSQL>(
+ auto store = std::make_unique<OfflinePageMetadataStore>(
base::ThreadTaskRunnerHandle::Get(), TempPath());
std::vector<OfflinePageItem> pages = GetOfflinePages(store.get());
EXPECT_EQ(5U, pages.size());
@@ -652,13 +652,13 @@ class OfflinePageMetadataStoreTest : public testing::Test {
else
EXPECT_EQ(0, page.upgrade_attempt);
}
- CheckThatPageThumbnailCanBeSaved((OfflinePageMetadataStoreSQL*)store.get());
+ CheckThatPageThumbnailCanBeSaved((OfflinePageMetadataStore*)store.get());
CheckThatOfflinePageCanBeSaved(std::move(store));
VerifyMetaVersions();
}
template <typename T>
- T ExecuteSync(OfflinePageMetadataStoreSQL* store,
+ T ExecuteSync(OfflinePageMetadataStore* store,
base::OnceCallback<T(sql::Connection*)> run_callback) {
bool called = false;
T result;
@@ -673,7 +673,7 @@ class OfflinePageMetadataStoreTest : public testing::Test {
}
void GetOfflinePagesAsync(
- OfflinePageMetadataStoreSQL* store,
+ OfflinePageMetadataStore* store,
base::OnceCallback<void(std::vector<OfflinePageItem>)> callback) {
auto run_callback = base::BindOnce(&GetOfflinePagesSync);
store->Execute<std::vector<OfflinePageItem>>(std::move(run_callback),
@@ -681,12 +681,12 @@ class OfflinePageMetadataStoreTest : public testing::Test {
}
std::vector<OfflinePageItem> GetOfflinePages(
- OfflinePageMetadataStoreSQL* store) {
+ OfflinePageMetadataStore* store) {
return ExecuteSync<std::vector<OfflinePageItem>>(
store, base::BindOnce(&GetOfflinePagesSync));
}
- ItemActionStatus AddOfflinePage(OfflinePageMetadataStoreSQL* store,
+ ItemActionStatus AddOfflinePage(OfflinePageMetadataStore* store,
const OfflinePageItem& item) {
auto result_callback = base::BindLambdaForTesting([&](sql::Connection* db) {
if (!db)
@@ -694,7 +694,7 @@ class OfflinePageMetadataStoreTest : public testing::Test {
// Using 'INSERT OR FAIL' or 'INSERT OR ABORT' in the query below
// causes debug builds to DLOG.
- const char kSql[] =
+ static const char kSql[] =
"INSERT OR IGNORE INTO " OFFLINE_PAGES_TABLE_V1
" (offline_id, online_url, client_namespace, client_id, "
"file_path, "
@@ -734,10 +734,10 @@ class OfflinePageMetadataStoreTest : public testing::Test {
}
std::vector<OfflinePageThumbnail> GetThumbnails(
- OfflinePageMetadataStoreSQL* store) {
+ OfflinePageMetadataStore* store) {
std::vector<OfflinePageThumbnail> thumbnails;
auto run_callback = base::BindLambdaForTesting([&](sql::Connection* db) {
- const char kSql[] = "SELECT * FROM page_thumbnails";
+ static const char kSql[] = "SELECT * FROM page_thumbnails";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
while (statement.Step()) {
@@ -755,11 +755,11 @@ class OfflinePageMetadataStoreTest : public testing::Test {
return ExecuteSync<std::vector<OfflinePageThumbnail>>(store, run_callback);
}
- void AddThumbnail(OfflinePageMetadataStoreSQL* store,
+ void AddThumbnail(OfflinePageMetadataStore* store,
const OfflinePageThumbnail& thumbnail) {
std::vector<OfflinePageThumbnail> thumbnails;
auto run_callback = base::BindLambdaForTesting([&](sql::Connection* db) {
- const char kSql[] =
+ static const char kSql[] =
"INSERT INTO page_thumbnails"
" (offline_id, expiration, thumbnail) VALUES (?, ?, ?)";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
@@ -782,12 +782,12 @@ class OfflinePageMetadataStoreTest : public testing::Test {
// Loads empty store and makes sure that there are no offline pages stored in
// it.
TEST_F(OfflinePageMetadataStoreTest, LoadEmptyStore) {
- std::unique_ptr<OfflinePageMetadataStoreSQL> store(BuildStore());
+ std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
EXPECT_EQ(0U, GetOfflinePages(store.get()).size());
}
TEST_F(OfflinePageMetadataStoreTest, GetOfflinePagesFromInvalidStore) {
- std::unique_ptr<OfflinePageMetadataStoreSQL> store(BuildStore());
+ std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
// Because execute method is self-healing this part of the test expects a
// positive results now.
@@ -869,7 +869,7 @@ TEST_F(OfflinePageMetadataStoreTest, AddOfflinePage) {
}
TEST_F(OfflinePageMetadataStoreTest, AddSameOfflinePageTwice) {
- std::unique_ptr<OfflinePageMetadataStoreSQL> store(BuildStore());
+ std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
OfflinePageItem offline_page(GURL(kTestURL), 1234LL, kTestClientId1,
base::FilePath(kFilePath), kFileSize);
@@ -884,7 +884,7 @@ TEST_F(OfflinePageMetadataStoreTest, AddSameOfflinePageTwice) {
// Adds metadata of multiple offline pages into a store and removes some.
TEST_F(OfflinePageMetadataStoreTest, AddRemoveMultipleOfflinePages) {
- std::unique_ptr<OfflinePageMetadataStoreSQL> store(BuildStore());
+ std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
// Add an offline page.
OfflinePageItem offline_page_1(GURL(kTestURL), 12345LL, kTestClientId1,
@@ -918,13 +918,13 @@ TEST_F(OfflinePageMetadataStoreTest, AddRemoveMultipleOfflinePages) {
}
TEST_F(OfflinePageMetadataStoreTest, StoreCloses) {
- std::unique_ptr<OfflinePageMetadataStoreSQL> store(BuildStore());
+ std::unique_ptr<OfflinePageMetadataStore> store(BuildStore());
GetOfflinePages(store.get());
EXPECT_TRUE(task_runner()->HasPendingTask());
EXPECT_LT(base::TimeDelta(), task_runner()->NextPendingTaskDelay());
- FastForwardBy(OfflinePageMetadataStoreSQL::kClosingDelay);
+ FastForwardBy(OfflinePageMetadataStore::kClosingDelay);
PumpLoop();
EXPECT_EQ(StoreState::NOT_LOADED, store->GetStateForTesting());
@@ -934,7 +934,7 @@ TEST_F(OfflinePageMetadataStoreTest, StoreCloses) {
}
TEST_F(OfflinePageMetadataStoreTest, MultiplePendingCalls) {
- auto store = std::make_unique<OfflinePageMetadataStoreSQL>(
+ auto store = std::make_unique<OfflinePageMetadataStore>(
base::ThreadTaskRunnerHandle::Get(), TempPath());
EXPECT_FALSE(task_runner()->HasPendingTask());
EXPECT_EQ(StoreState::NOT_LOADED, store->GetStateForTesting());
diff --git a/chromium/components/offline_pages/core/offline_page_model.h b/chromium/components/offline_pages/core/offline_page_model.h
index d953922f628..2dcbfe601e0 100644
--- a/chromium/components/offline_pages/core/offline_page_model.h
+++ b/chromium/components/offline_pages/core/offline_page_model.h
@@ -29,18 +29,6 @@ struct ClientId;
// Service for saving pages offline, storing the offline copy and metadata, and
// retrieving them upon request.
//
-// Example usage:
-// class ArchiverImpl : public OfflinePageArchiver {
-// // This is a class that knows how to create archiver
-// void CreateArchiver(...) override;
-// ...
-// }
-//
-// // In code using the OfflinePagesModel to save a page:
-// std::unique_ptr<ArchiverImpl> archiver(new ArchiverImpl());
-// // Callback is of type SavePageCallback.
-// model->SavePage(url, std::move(archiver), callback);
-//
// TODO(fgorski): Things to describe:
// * how to cancel requests and what to expect
class OfflinePageModel : public base::SupportsUserData, public KeyedService {
@@ -137,14 +125,29 @@ class OfflinePageModel : public base::SupportsUserData, public KeyedService {
// Attempts to save a page offline per |save_page_params|. Requires that the
// model is loaded. Generates a new offline id or uses the proposed offline
// id in |save_page_params| and returns it.
+ //
+ // Example usage:
+ // class ArchiverImpl : public OfflinePageArchiver {
+ // // This is a class that knows how to create archiver
+ // void CreateArchiver(...) override;
+ // ...
+ // }
+ //
+ // // In code using the OfflinePagesModel to save a page:
+ // std::unique_ptr<ArchiverImpl> archiver(new ArchiverImpl());
+ // // Callback is of type SavePageCallback.
+ // model->SavePage(url, std::move(archiver), std::move(callback));
+ //
+ // TODO(https://crbug.com/849424): This method's implementation shouldn't
+ // take ownership of OfflinePageArchiver.
virtual void SavePage(const SavePageParams& save_page_params,
std::unique_ptr<OfflinePageArchiver> archiver,
content::WebContents* web_contents,
- const SavePageCallback& callback) = 0;
+ SavePageCallback callback) = 0;
// Adds a page entry to the metadata store.
virtual void AddPage(const OfflinePageItem& page,
- const AddPageCallback& callback) = 0;
+ AddPageCallback callback) = 0;
// Marks that the offline page related to the passed |offline_id| has been
// accessed. Its access info, including last access time and access count,
@@ -153,23 +156,22 @@ class OfflinePageModel : public base::SupportsUserData, public KeyedService {
// Deletes pages based on |offline_ids|.
virtual void DeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
- const DeletePageCallback& callback) = 0;
+ DeletePageCallback callback) = 0;
// Deletes all pages associated with any of |client_ids|.
virtual void DeletePagesByClientIds(const std::vector<ClientId>& client_ids,
- const DeletePageCallback& callback) = 0;
+ DeletePageCallback callback) = 0;
// Deletes all pages associated with any of the |client_ids| provided the page
// also was created by origin.
virtual void DeletePagesByClientIdsAndOrigin(
const std::vector<ClientId>& client_ids,
const std::string& origin,
- const DeletePageCallback& callback) = 0;
+ DeletePageCallback callback) = 0;
// Deletes cached offline pages matching the URL predicate.
- virtual void DeleteCachedPagesByURLPredicate(
- const UrlPredicate& predicate,
- const DeletePageCallback& callback) = 0;
+ virtual void DeleteCachedPagesByURLPredicate(const UrlPredicate& predicate,
+ DeletePageCallback callback) = 0;
// Gets all offline pages.
virtual void GetAllPages(MultipleOfflinePageItemCallback callback) = 0;
@@ -220,9 +222,8 @@ class OfflinePageModel : public base::SupportsUserData, public KeyedService {
SingleOfflinePageItemCallback callback) = 0;
// Gets all offline ids where the offline page has the matching client id.
- virtual void GetOfflineIdsForClientId(
- const ClientId& client_id,
- const MultipleOfflineIdCallback& callback) = 0;
+ virtual void GetOfflineIdsForClientId(const ClientId& client_id,
+ MultipleOfflineIdCallback callback) = 0;
// Stores a new page thumbnail in the page_thumbnails table.
virtual void StoreThumbnail(const OfflinePageThumbnail& thumb) = 0;
@@ -241,6 +242,9 @@ class OfflinePageModel : public base::SupportsUserData, public KeyedService {
// Publishes an offline page from the internal offline page directory. This
// includes putting it in a public directory, updating the system download
// manager, if any, and updating the offline page model database.
+ //
+ // TODO(https://crbug.com/849424): This method's implementation shouldn't
+ // take ownership of OfflinePageArchiver.
virtual void PublishInternalArchive(
const OfflinePageItem& offline_page,
std::unique_ptr<OfflinePageArchiver> archiver,
diff --git a/chromium/components/offline_pages/core/offline_page_test_archiver.cc b/chromium/components/offline_pages/core/offline_page_test_archiver.cc
index 656f452d7f1..b87e12f5efb 100644
--- a/chromium/components/offline_pages/core/offline_page_test_archiver.cc
+++ b/chromium/components/offline_pages/core/offline_page_test_archiver.cc
@@ -26,22 +26,24 @@ OfflinePageTestArchiver::OfflinePageTestArchiver(
size_to_report_(size_to_report),
create_archive_called_(false),
publish_archive_called_(false),
+ archive_attempt_failure_(false),
delayed_(false),
result_title_(result_title),
digest_to_report_(digest_to_report),
task_runner_(task_runner) {}
OfflinePageTestArchiver::~OfflinePageTestArchiver() {
- EXPECT_TRUE(create_archive_called_ || publish_archive_called_);
+ EXPECT_TRUE(create_archive_called_ || publish_archive_called_ ||
+ archive_attempt_failure_);
}
void OfflinePageTestArchiver::CreateArchive(
const base::FilePath& archives_dir,
const CreateArchiveParams& create_archive_params,
content::WebContents* web_contents,
- const CreateArchiveCallback& callback) {
+ CreateArchiveCallback callback) {
create_archive_called_ = true;
- callback_ = callback;
+ callback_ = std::move(callback);
archives_dir_ = archives_dir;
create_archive_params_ = create_archive_params;
if (!delayed_)
@@ -55,10 +57,21 @@ void OfflinePageTestArchiver::PublishArchive(
SystemDownloadManager* download_manager,
PublishArchiveDoneCallback publish_done_callback) {
publish_archive_called_ = true;
- publish_archive_result_.move_result = SavePageResult::SUCCESS;
- publish_archive_result_.new_file_path = offline_page.file_path;
- publish_archive_result_.download_id = 0;
- std::move(publish_done_callback).Run(offline_page, &publish_archive_result_);
+ PublishArchiveResult publish_archive_result;
+ publish_archive_result.move_result = SavePageResult::SUCCESS;
+ publish_archive_result.new_file_path = offline_page.file_path;
+ publish_archive_result.download_id = 0;
+
+ if (archive_attempt_failure_) {
+ publish_archive_result.move_result = SavePageResult::FILE_MOVE_FAILED;
+ }
+
+ // Note: once the |publish_done_callback| is invoked it is very likely that
+ // this instance will be destroyed. So all parameters sent to it must not be
+ // bound to the lifetime on this.
+ background_task_runner->PostTask(
+ FROM_HERE, base::BindOnce(std::move(publish_done_callback), offline_page,
+ std::move(publish_archive_result)));
}
void OfflinePageTestArchiver::CompleteCreateArchive() {
@@ -71,10 +84,12 @@ void OfflinePageTestArchiver::CompleteCreateArchive() {
// This step ensures the file is created and closed immediately.
base::File file(archive_path, base::File::FLAG_OPEN_ALWAYS);
}
- observer_->SetLastPathCreatedByArchiver(archive_path);
+ if (observer_)
+ observer_->SetLastPathCreatedByArchiver(archive_path);
task_runner_->PostTask(
- FROM_HERE, base::Bind(callback_, this, result_, url_, archive_path,
- result_title_, size_to_report_, digest_to_report_));
+ FROM_HERE,
+ base::BindOnce(std::move(callback_), result_, url_, archive_path,
+ result_title_, size_to_report_, digest_to_report_));
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/offline_page_test_archiver.h b/chromium/components/offline_pages/core/offline_page_test_archiver.h
index 03f88921d0c..6cae827ff5e 100644
--- a/chromium/components/offline_pages/core/offline_page_test_archiver.h
+++ b/chromium/components/offline_pages/core/offline_page_test_archiver.h
@@ -7,6 +7,7 @@
#include <stddef.h>
#include <stdint.h>
+#include <string>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
@@ -47,7 +48,7 @@ class OfflinePageTestArchiver : public OfflinePageArchiver {
void CreateArchive(const base::FilePath& archives_dir,
const CreateArchiveParams& create_archive_params,
content::WebContents* web_contents,
- const CreateArchiveCallback& callback) override;
+ CreateArchiveCallback callback) override;
void PublishArchive(
const OfflinePageItem& offline_page,
@@ -75,6 +76,10 @@ class OfflinePageTestArchiver : public OfflinePageArchiver {
bool create_archive_called() const { return create_archive_called_; }
+ void set_archive_attempt_failure(bool fail) {
+ archive_attempt_failure_ = fail;
+ }
+
private:
// Not owned. Outlives OfflinePageTestArchiver.
Observer* observer_;
@@ -86,10 +91,10 @@ class OfflinePageTestArchiver : public OfflinePageArchiver {
int64_t size_to_report_;
bool create_archive_called_;
bool publish_archive_called_;
+ bool archive_attempt_failure_;
bool delayed_;
base::string16 result_title_;
std::string digest_to_report_;
- PublishArchiveResult publish_archive_result_;
CreateArchiveCallback callback_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/chromium/components/offline_pages/core/offline_page_types.h b/chromium/components/offline_pages/core/offline_page_types.h
index 591052ca574..e0c126e2373 100644
--- a/chromium/components/offline_pages/core/offline_page_types.h
+++ b/chromium/components/offline_pages/core/offline_page_types.h
@@ -94,18 +94,17 @@ typedef std::vector<int64_t> MultipleOfflineIdResult;
typedef std::vector<OfflinePageItem> MultipleOfflinePageItemResult;
// TODO(carlosk): All or most of these should use base::OnceCallback.
-typedef base::Callback<void(SavePageResult, int64_t)> SavePageCallback;
-typedef base::Callback<void(AddPageResult, int64_t)> AddPageCallback;
-typedef base::Callback<void(DeletePageResult)> DeletePageCallback;
-typedef base::Callback<void(bool)> HasPagesCallback;
-typedef base::Callback<void(const MultipleOfflineIdResult&)>
+typedef base::OnceCallback<void(SavePageResult, int64_t)> SavePageCallback;
+typedef base::OnceCallback<void(AddPageResult, int64_t)> AddPageCallback;
+typedef base::OnceCallback<void(DeletePageResult)> DeletePageCallback;
+typedef base::OnceCallback<void(const MultipleOfflineIdResult&)>
MultipleOfflineIdCallback;
typedef base::OnceCallback<void(const OfflinePageItem*)>
SingleOfflinePageItemCallback;
typedef base::OnceCallback<void(const MultipleOfflinePageItemResult&)>
MultipleOfflinePageItemCallback;
-typedef base::Callback<bool(const GURL&)> UrlPredicate;
-typedef base::Callback<void(int64_t)> SizeInBytesCallback;
+typedef base::RepeatingCallback<bool(const GURL&)> UrlPredicate;
+typedef base::OnceCallback<void(int64_t)> SizeInBytesCallback;
typedef base::OnceCallback<void(std::unique_ptr<OfflinePageThumbnail>)>
GetThumbnailCallback;
typedef base::OnceCallback<void(bool)> CleanupThumbnailsCallback;
diff --git a/chromium/components/offline_pages/core/prefetch/BUILD.gn b/chromium/components/offline_pages/core/prefetch/BUILD.gn
index 22c3e9e4cad..b0a9af5124c 100644
--- a/chromium/components/offline_pages/core/prefetch/BUILD.gn
+++ b/chromium/components/offline_pages/core/prefetch/BUILD.gn
@@ -176,6 +176,18 @@ proto_library("proto") {
]
}
+if (is_android) {
+ proto_java_library("offline_prefetch_proto_java") {
+ proto_path = "proto"
+ sources = [
+ "proto/any.proto",
+ "proto/offline_pages.proto",
+ "proto/operation.proto",
+ "proto/status.proto",
+ ]
+ }
+}
+
source_set("unit_tests") {
testonly = true
sources = [
diff --git a/chromium/components/offline_pages/core/prefetch/add_unique_urls_task.cc b/chromium/components/offline_pages/core/prefetch/add_unique_urls_task.cc
index ec4d24651a8..a0f0862a0fa 100644
--- a/chromium/components/offline_pages/core/prefetch/add_unique_urls_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/add_unique_urls_task.cc
@@ -82,9 +82,6 @@ Result AddUrlsAndCleanupZombiesSync(
const std::string& name_space,
const std::vector<PrefetchURL>& candidate_prefetch_urls,
sql::Connection* db) {
- if (!db)
- return Result::STORE_ERROR;
-
sql::Transaction transaction(db);
if (!transaction.Begin())
return Result::STORE_ERROR;
@@ -133,7 +130,7 @@ Result AddUrlsAndCleanupZombiesSync(
added_row_count);
return added_row_count > 0 ? Result::URLS_ADDED : Result::NOTHING_ADDED;
}
-}
+} // namespace
AddUniqueUrlsTask::AddUniqueUrlsTask(
PrefetchDispatcher* prefetch_dispatcher,
@@ -155,7 +152,8 @@ void AddUniqueUrlsTask::Run() {
prefetch_store_->Execute(base::BindOnce(&AddUrlsAndCleanupZombiesSync,
name_space_, prefetch_urls_),
base::BindOnce(&AddUniqueUrlsTask::OnUrlsAdded,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr()),
+ Result::STORE_ERROR);
}
void AddUniqueUrlsTask::OnUrlsAdded(Result result) {
diff --git a/chromium/components/offline_pages/core/prefetch/download_archives_task.cc b/chromium/components/offline_pages/core/prefetch/download_archives_task.cc
index 8fafb2b6ad2..dfaa83b11b8 100644
--- a/chromium/components/offline_pages/core/prefetch/download_archives_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/download_archives_task.cc
@@ -28,7 +28,8 @@ using ItemsToDownload = DownloadArchivesTask::ItemsToDownload;
ItemsToDownload FindItemsReadyForDownload(sql::Connection* db) {
static const char kSql[] =
- "SELECT offline_id, archive_body_name, archive_body_length"
+ "SELECT offline_id, archive_body_name, operation_name,"
+ " archive_body_length"
" FROM prefetch_items"
" WHERE state = ?"
" ORDER BY creation_time DESC";
@@ -37,9 +38,12 @@ ItemsToDownload FindItemsReadyForDownload(sql::Connection* db) {
ItemsToDownload items_to_download;
while (statement.Step()) {
- items_to_download.push_back({statement.ColumnInt64(0),
- statement.ColumnString(1),
- statement.ColumnInt64(2), std::string()});
+ DownloadItem item;
+ item.offline_id = statement.ColumnInt64(0);
+ item.archive_body_name = statement.ColumnString(1);
+ item.operation_name = statement.ColumnString(2);
+ item.archive_body_length = statement.ColumnInt64(3);
+ items_to_download.push_back(std::move(item));
}
return items_to_download;
@@ -79,9 +83,6 @@ bool MarkItemAsDownloading(sql::Connection* db,
std::unique_ptr<ItemsToDownload> SelectAndMarkItemsForDownloadSync(
sql::Connection* db) {
- if (!db)
- return nullptr;
-
sql::Transaction transaction(db);
if (!transaction.Begin())
return nullptr;
@@ -160,6 +161,10 @@ const int DownloadArchivesTask::kMaxConcurrentDownloads = 2;
// static
const int DownloadArchivesTask::kMaxConcurrentDownloadsForLimitless = 4;
+DownloadArchivesTask::DownloadItem::DownloadItem() = default;
+DownloadArchivesTask::DownloadItem::DownloadItem(const DownloadItem& other) =
+ default;
+
DownloadArchivesTask::DownloadArchivesTask(
PrefetchStore* prefetch_store,
PrefetchDownloader* prefetch_downloader)
@@ -182,7 +187,8 @@ void DownloadArchivesTask::Run() {
prefetch_store_->Execute(
base::BindOnce(SelectAndMarkItemsForDownloadSync),
base::BindOnce(&DownloadArchivesTask::SendItemsToPrefetchDownloader,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr()),
+ std::unique_ptr<ItemsToDownload>());
}
void DownloadArchivesTask::SendItemsToPrefetchDownloader(
@@ -190,7 +196,8 @@ void DownloadArchivesTask::SendItemsToPrefetchDownloader(
if (items_to_download) {
for (const auto& download_item : *items_to_download) {
prefetch_downloader_->StartDownload(download_item.guid,
- download_item.archive_body_name);
+ download_item.archive_body_name,
+ download_item.operation_name);
// Reports expected archive size in KiB (accepting values up to 100 MiB).
UMA_HISTOGRAM_COUNTS_100000(
"OfflinePages.Prefetching.DownloadExpectedFileSize",
diff --git a/chromium/components/offline_pages/core/prefetch/download_archives_task.h b/chromium/components/offline_pages/core/prefetch/download_archives_task.h
index 58e116b6122..7343025b3bd 100644
--- a/chromium/components/offline_pages/core/prefetch/download_archives_task.h
+++ b/chromium/components/offline_pages/core/prefetch/download_archives_task.h
@@ -29,8 +29,12 @@ class DownloadArchivesTask : public Task {
// Represents item to be downloaded as a result of running the task.
struct DownloadItem {
+ DownloadItem();
+ DownloadItem(const DownloadItem& other);
+
int64_t offline_id;
std::string archive_body_name;
+ std::string operation_name;
int64_t archive_body_length;
std::string guid;
};
diff --git a/chromium/components/offline_pages/core/prefetch/download_archives_task_unittest.cc b/chromium/components/offline_pages/core/prefetch/download_archives_task_unittest.cc
index 3e324a7923c..613344a06b5 100644
--- a/chromium/components/offline_pages/core/prefetch/download_archives_task_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/download_archives_task_unittest.cc
@@ -9,7 +9,7 @@
#include "base/guid.h"
#include "base/numerics/safe_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "components/offline_pages/core/offline_page_feature.h"
#include "components/offline_pages/core/prefetch/prefetch_task_test_base.h"
@@ -136,11 +136,11 @@ TEST_F(DownloadArchivesTaskTest, SingleArchiveToDownload) {
EXPECT_LT(download_item_before->freshness_time,
download_item->freshness_time);
- std::map<std::string, std::string> requested_downloads =
+ const TestPrefetchDownloader::RequestMap& requested_downloads =
prefetch_downloader()->requested_downloads();
auto it = requested_downloads.find(download_item->guid);
ASSERT_TRUE(it != requested_downloads.end());
- EXPECT_EQ(it->second, download_item->archive_body_name);
+ EXPECT_EQ(it->second.download_location, download_item->archive_body_name);
histogram_tester.ExpectUniqueSample(
"OfflinePages.Prefetching.DownloadExpectedFileSize",
@@ -180,17 +180,17 @@ TEST_F(DownloadArchivesTaskTest, MultipleArchivesToDownload) {
ASSERT_TRUE(download_item_2);
EXPECT_EQ(PrefetchItemState::DOWNLOADING, download_item_2->state);
- std::map<std::string, std::string> requested_downloads =
+ const TestPrefetchDownloader::RequestMap& requested_downloads =
prefetch_downloader()->requested_downloads();
EXPECT_EQ(2U, requested_downloads.size());
auto it = requested_downloads.find(download_item_1->guid);
ASSERT_TRUE(it != requested_downloads.end());
- EXPECT_EQ(it->second, download_item_1->archive_body_name);
+ EXPECT_EQ(it->second.download_location, download_item_1->archive_body_name);
it = requested_downloads.find(download_item_2->guid);
ASSERT_TRUE(it != requested_downloads.end());
- EXPECT_EQ(it->second, download_item_2->archive_body_name);
+ EXPECT_EQ(it->second.download_location, download_item_2->archive_body_name);
histogram_tester.ExpectUniqueSample(
"OfflinePages.Prefetching.DownloadExpectedFileSize",
@@ -231,13 +231,13 @@ TEST_F(DownloadArchivesTaskTest, MultipleLargeArchivesToDownload) {
ASSERT_TRUE(download_item_2);
EXPECT_EQ(PrefetchItemState::RECEIVED_BUNDLE, download_item_2->state);
- std::map<std::string, std::string> requested_downloads =
+ const TestPrefetchDownloader::RequestMap& requested_downloads =
prefetch_downloader()->requested_downloads();
EXPECT_EQ(1U, requested_downloads.size());
auto it = requested_downloads.find(download_item_1->guid);
ASSERT_TRUE(it != requested_downloads.end());
- EXPECT_EQ(it->second, download_item_1->archive_body_name);
+ EXPECT_EQ(it->second.download_location, download_item_1->archive_body_name);
histogram_tester.ExpectUniqueSample(
"OfflinePages.Prefetching.DownloadExpectedFileSize",
@@ -265,7 +265,7 @@ TEST_F(DownloadArchivesTaskTest, TooManyArchivesToDownload) {
EXPECT_EQ(static_cast<size_t>(total_items),
store_util()->GetAllItems(&items_after_run));
- std::map<std::string, std::string> requested_downloads =
+ const TestPrefetchDownloader::RequestMap& requested_downloads =
prefetch_downloader()->requested_downloads();
EXPECT_EQ(static_cast<size_t>(DownloadArchivesTask::kMaxConcurrentDownloads),
requested_downloads.size());
@@ -279,7 +279,7 @@ TEST_F(DownloadArchivesTaskTest, TooManyArchivesToDownload) {
auto it = requested_downloads.find(download_item->guid);
ASSERT_TRUE(it != requested_downloads.end());
- EXPECT_EQ(it->second, download_item->archive_body_name);
+ EXPECT_EQ(it->second.download_location, download_item->archive_body_name);
}
// Remaining items shouldn't have been started.
@@ -333,7 +333,7 @@ TEST_F(DownloadArchivesTaskTest,
std::set<PrefetchItem> items_after_run;
EXPECT_EQ(total_items, store_util()->GetAllItems(&items_after_run));
- std::map<std::string, std::string> requested_downloads =
+ const TestPrefetchDownloader::RequestMap& requested_downloads =
prefetch_downloader()->requested_downloads();
EXPECT_EQ(max_concurrent_downloads, requested_downloads.size());
@@ -347,7 +347,7 @@ TEST_F(DownloadArchivesTaskTest,
auto it = requested_downloads.find(download_item->guid);
ASSERT_TRUE(it != requested_downloads.end());
- EXPECT_EQ(it->second, download_item->archive_body_name);
+ EXPECT_EQ(it->second.download_location, download_item->archive_body_name);
}
// Remaining items shouldn't have been started.
@@ -391,16 +391,17 @@ TEST_F(DownloadArchivesTaskTest, SingleArchiveSecondAttempt) {
EXPECT_EQ(PrefetchItemState::DOWNLOADING, download_item->state);
EXPECT_EQ(2, download_item->download_initiation_attempts);
EXPECT_EQ(item.archive_body_name, download_item->archive_body_name);
+ EXPECT_EQ(item.operation_name, download_item->operation_name);
// GUID expected to change between download attempts.
EXPECT_NE(item.guid, download_item->guid);
// Freshness time not expected to change after first attempt.
EXPECT_EQ(item.freshness_time, download_item->freshness_time);
- std::map<std::string, std::string> requested_downloads =
+ const TestPrefetchDownloader::RequestMap& requested_downloads =
prefetch_downloader()->requested_downloads();
auto it = requested_downloads.find(download_item->guid);
ASSERT_TRUE(it != requested_downloads.end());
- EXPECT_EQ(it->second, download_item->archive_body_name);
+ EXPECT_EQ(it->second.download_location, download_item->archive_body_name);
histogram_tester.ExpectUniqueSample(
"OfflinePages.Prefetching.DownloadExpectedFileSize",
diff --git a/chromium/components/offline_pages/core/prefetch/download_cleanup_task.cc b/chromium/components/offline_pages/core/prefetch/download_cleanup_task.cc
index 68ebb698aa5..e6fc35030eb 100644
--- a/chromium/components/offline_pages/core/prefetch/download_cleanup_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/download_cleanup_task.cc
@@ -91,9 +91,6 @@ bool CleanupDownloadsSync(
const std::map<std::string, std::pair<base::FilePath, int64_t>>&
success_downloads,
sql::Connection* db) {
- if (!db)
- return false;
-
sql::Transaction transaction(db);
if (!transaction.Begin())
return false;
@@ -164,7 +161,8 @@ void DownloadCleanupTask::Run() {
base::BindOnce(&CleanupDownloadsSync, kMaxDownloadAttempts,
outstanding_download_ids_, success_downloads_),
base::BindOnce(&DownloadCleanupTask::OnFinished,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr()),
+ false);
}
void DownloadCleanupTask::OnFinished(bool success) {
diff --git a/chromium/components/offline_pages/core/prefetch/download_completed_task.cc b/chromium/components/offline_pages/core/prefetch/download_completed_task.cc
index 147cd15db71..8a3413588a5 100644
--- a/chromium/components/offline_pages/core/prefetch/download_completed_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/download_completed_task.cc
@@ -50,8 +50,6 @@ UpdateInfo UpdatePrefetchItemOnDownloadSuccessSync(
const base::FilePath& file_path,
int64_t file_size,
sql::Connection* db) {
- if (!db)
- return UpdateInfo();
sql::Transaction transaction(db);
if (!transaction.Begin())
return UpdateInfo();
@@ -94,9 +92,6 @@ UpdateInfo UpdatePrefetchItemOnDownloadSuccessSync(
// true if the respective row was successfully updated (as normally expected).
UpdateInfo UpdatePrefetchItemOnDownloadErrorSync(const std::string& guid,
sql::Connection* db) {
- if (!db)
- return UpdateInfo();
-
static const char kSql[] =
"UPDATE prefetch_items"
" SET state = ?, error_code = ?"
@@ -138,13 +133,15 @@ void DownloadCompletedTask::Run() {
download_result_.download_id, download_result_.file_path,
download_result_.file_size),
base::BindOnce(&DownloadCompletedTask::OnPrefetchItemUpdated,
- weak_ptr_factory_.GetWeakPtr(), true));
+ weak_ptr_factory_.GetWeakPtr(), true),
+ UpdateInfo());
} else {
prefetch_store_->Execute(
base::BindOnce(&UpdatePrefetchItemOnDownloadErrorSync,
download_result_.download_id),
base::BindOnce(&DownloadCompletedTask::OnPrefetchItemUpdated,
- weak_ptr_factory_.GetWeakPtr(), false));
+ weak_ptr_factory_.GetWeakPtr(), false),
+ UpdateInfo());
}
}
diff --git a/chromium/components/offline_pages/core/prefetch/download_completed_task_unittest.cc b/chromium/components/offline_pages/core/prefetch/download_completed_task_unittest.cc
index 772c3008e9b..829fc741990 100644
--- a/chromium/components/offline_pages/core/prefetch/download_completed_task_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/download_completed_task_unittest.cc
@@ -7,7 +7,7 @@
#include <string>
#include <vector>
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
diff --git a/chromium/components/offline_pages/core/prefetch/finalize_dismissed_url_suggestion_task.cc b/chromium/components/offline_pages/core/prefetch/finalize_dismissed_url_suggestion_task.cc
index 77a07866857..999539f014d 100644
--- a/chromium/components/offline_pages/core/prefetch/finalize_dismissed_url_suggestion_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/finalize_dismissed_url_suggestion_task.cc
@@ -18,9 +18,6 @@ namespace {
bool DeletePageByClientIdIfNotDownloadedSync(const ClientId& client_id,
sql::Connection* db) {
- if (!db)
- return false;
-
static const std::array<PrefetchItemState, 6>& finalizable_states =
FinalizeDismissedUrlSuggestionTask::kFinalizableStates;
@@ -57,10 +54,11 @@ FinalizeDismissedUrlSuggestionTask::FinalizeDismissedUrlSuggestionTask(
FinalizeDismissedUrlSuggestionTask::~FinalizeDismissedUrlSuggestionTask() {}
void FinalizeDismissedUrlSuggestionTask::Run() {
- prefetch_store_->Execute<bool>(
+ prefetch_store_->Execute(
base::BindOnce(&DeletePageByClientIdIfNotDownloadedSync, client_id_),
base::BindOnce(&FinalizeDismissedUrlSuggestionTask::OnComplete,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr()),
+ false);
}
void FinalizeDismissedUrlSuggestionTask::OnComplete(bool result) {
diff --git a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_reconcile_task.cc b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_reconcile_task.cc
index 3e959ccfba8..8a1efe2c507 100644
--- a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_reconcile_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_reconcile_task.cc
@@ -78,9 +78,6 @@ bool ReconcileGenerateBundleRequests(
std::unique_ptr<std::set<std::string>> requested_urls,
int max_attempts,
sql::Connection* db) {
- if (!db)
- return false;
-
sql::Transaction transaction(db);
if (!transaction.Begin())
return false;
@@ -127,7 +124,8 @@ void GeneratePageBundleReconcileTask::Run() {
base::BindOnce(&ReconcileGenerateBundleRequests,
std::move(requested_urls), kMaxGenerateBundleAttempts),
base::BindOnce(&GeneratePageBundleReconcileTask::FinishedUpdate,
- weak_factory_.GetWeakPtr()));
+ weak_factory_.GetWeakPtr()),
+ false);
}
void GeneratePageBundleReconcileTask::FinishedUpdate(bool success) {
@@ -135,4 +133,4 @@ void GeneratePageBundleReconcileTask::FinishedUpdate(bool success) {
TaskComplete();
}
-} // namespace offline_pages \ No newline at end of file
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_reconcile_task_unittest.cc b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_reconcile_task_unittest.cc
index c0b038caba7..2d1227c237e 100644
--- a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_reconcile_task_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_reconcile_task_unittest.cc
@@ -32,13 +32,13 @@ class FakePrefetchNetworkRequestFactory : public PrefetchNetworkRequestFactory {
void MakeGeneratePageBundleRequest(
const std::vector<std::string>& prefetch_urls,
const std::string& gcm_registration_id,
- const PrefetchRequestFinishedCallback& callback) override {}
+ PrefetchRequestFinishedCallback callback) override {}
std::unique_ptr<std::set<std::string>> GetAllUrlsRequested() const override {
return std::make_unique<std::set<std::string>>(*requested_urls_);
}
void MakeGetOperationRequest(
const std::string& operation_name,
- const PrefetchRequestFinishedCallback& callback) override {}
+ PrefetchRequestFinishedCallback callback) override {}
GetOperationRequest* FindGetOperationRequestByName(
const std::string& operation_name) const override {
return nullptr;
diff --git a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_request.cc b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_request.cc
index 75e7b0460bf..1a067e251ec 100644
--- a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_request.cc
+++ b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_request.cc
@@ -23,8 +23,8 @@ GeneratePageBundleRequest::GeneratePageBundleRequest(
const std::vector<std::string>& page_urls,
version_info::Channel channel,
net::URLRequestContextGetter* request_context_getter,
- const PrefetchRequestFinishedCallback& callback)
- : callback_(callback), requested_urls_(page_urls) {
+ PrefetchRequestFinishedCallback callback)
+ : callback_(std::move(callback)), requested_urls_(page_urls) {
proto::GeneratePageBundleRequest request;
request.set_user_agent(user_agent);
request.set_max_bundle_size_bytes(max_bundle_size_bytes);
@@ -43,9 +43,9 @@ GeneratePageBundleRequest::GeneratePageBundleRequest(
fetcher_ = PrefetchRequestFetcher::CreateForPost(
GeneratePageBundleRequestURL(channel), upload_data,
request_context_getter,
- base::Bind(&GeneratePageBundleRequest::OnCompleted,
- // Fetcher is owned by this instance.
- base::Unretained(this)));
+ base::BindOnce(&GeneratePageBundleRequest::OnCompleted,
+ // Fetcher is owned by this instance.
+ base::Unretained(this)));
}
GeneratePageBundleRequest::~GeneratePageBundleRequest() {}
@@ -53,19 +53,21 @@ GeneratePageBundleRequest::~GeneratePageBundleRequest() {}
void GeneratePageBundleRequest::OnCompleted(PrefetchRequestStatus status,
const std::string& data) {
if (status != PrefetchRequestStatus::SUCCESS) {
- callback_.Run(status, std::string(), std::vector<RenderPageInfo>());
+ std::move(callback_).Run(status, std::string(),
+ std::vector<RenderPageInfo>());
return;
}
std::vector<RenderPageInfo> pages;
std::string operation_name = ParseOperationResponse(data, &pages);
if (operation_name.empty()) {
- callback_.Run(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
- std::string(), std::vector<RenderPageInfo>());
+ std::move(callback_).Run(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
+ std::string(), std::vector<RenderPageInfo>());
return;
}
- callback_.Run(PrefetchRequestStatus::SUCCESS, operation_name, pages);
+ std::move(callback_).Run(PrefetchRequestStatus::SUCCESS, operation_name,
+ pages);
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_request.h b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_request.h
index 2bc1a98f5d7..ffc5cb7a08c 100644
--- a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_request.h
+++ b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_request.h
@@ -30,7 +30,7 @@ class GeneratePageBundleRequest {
const std::vector<std::string>& page_urls,
version_info::Channel channel,
net::URLRequestContextGetter* request_context_getter,
- const PrefetchRequestFinishedCallback& callback);
+ PrefetchRequestFinishedCallback callback);
~GeneratePageBundleRequest();
const std::vector<std::string>& requested_urls() { return requested_urls_; }
diff --git a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_request_unittest.cc b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_request_unittest.cc
index ea8f8369eee..f266242bbcb 100644
--- a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_request_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_request_unittest.cc
@@ -38,12 +38,12 @@ const int kTestMaxBundleSize = 100000;
class GeneratePageBundleRequestTest : public PrefetchRequestTestBase {
public:
std::unique_ptr<GeneratePageBundleRequest> CreateRequest(
- const PrefetchRequestFinishedCallback& callback) {
+ PrefetchRequestFinishedCallback callback) {
std::vector<std::string> page_urls = {kTestURL, kTestURL2};
return std::unique_ptr<GeneratePageBundleRequest>(
new GeneratePageBundleRequest(
kTestUserAgent, kTestGCMID, kTestMaxBundleSize, page_urls,
- kTestChannel, request_context(), callback));
+ kTestChannel, request_context(), std::move(callback)));
}
};
diff --git a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_task.cc b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_task.cc
index adc45fb0079..624b9638c1d 100644
--- a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_task.cc
@@ -111,9 +111,6 @@ bool MarkUrlFinishedWithError(sql::Connection* db, const FetchedUrl& url) {
std::unique_ptr<UrlAndIds> SelectUrlsToPrefetchSync(base::Clock* clock,
sql::Connection* db) {
- if (!db)
- return nullptr;
-
sql::Transaction transaction(db);
if (!transaction.Begin())
return nullptr;
@@ -152,13 +149,13 @@ GeneratePageBundleTask::GeneratePageBundleTask(
PrefetchStore* prefetch_store,
PrefetchGCMHandler* gcm_handler,
PrefetchNetworkRequestFactory* request_factory,
- const PrefetchRequestFinishedCallback& callback)
+ PrefetchRequestFinishedCallback callback)
: clock_(base::DefaultClock::GetInstance()),
prefetch_dispatcher_(prefetch_dispatcher),
prefetch_store_(prefetch_store),
gcm_handler_(gcm_handler),
request_factory_(request_factory),
- callback_(callback),
+ callback_(std::move(callback)),
weak_factory_(this) {}
GeneratePageBundleTask::~GeneratePageBundleTask() {}
@@ -167,7 +164,8 @@ void GeneratePageBundleTask::Run() {
prefetch_store_->Execute(
base::BindOnce(&SelectUrlsToPrefetchSync, clock_),
base::BindOnce(&GeneratePageBundleTask::StartGeneratePageBundle,
- weak_factory_.GetWeakPtr()));
+ weak_factory_.GetWeakPtr()),
+ std::unique_ptr<UrlAndIds>());
}
void GeneratePageBundleTask::SetClockForTesting(base::Clock* clock) {
@@ -195,7 +193,7 @@ void GeneratePageBundleTask::GotRegistrationId(
DCHECK(url_and_ids);
// TODO(dimich): Add UMA reporting on instance_id::InstanceID::Result.
request_factory_->MakeGeneratePageBundleRequest(url_and_ids->urls, id,
- callback_);
+ std::move(callback_));
prefetch_dispatcher_->GeneratePageBundleRequested(
std::make_unique<PrefetchDispatcher::IdsVector>(
std::move(url_and_ids->ids)));
diff --git a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_task.h b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_task.h
index 04301649fdc..9e1bee2422b 100644
--- a/chromium/components/offline_pages/core/prefetch/generate_page_bundle_task.h
+++ b/chromium/components/offline_pages/core/prefetch/generate_page_bundle_task.h
@@ -31,7 +31,7 @@ class GeneratePageBundleTask : public Task {
PrefetchStore* prefetch_store,
PrefetchGCMHandler* gcm_handler,
PrefetchNetworkRequestFactory* request_factory,
- const PrefetchRequestFinishedCallback& callback);
+ PrefetchRequestFinishedCallback callback);
~GeneratePageBundleTask() override;
// Task implementation.
diff --git a/chromium/components/offline_pages/core/prefetch/get_operation_request.cc b/chromium/components/offline_pages/core/prefetch/get_operation_request.cc
index c6bc5a53fcd..f5b8d6c764f 100644
--- a/chromium/components/offline_pages/core/prefetch/get_operation_request.cc
+++ b/chromium/components/offline_pages/core/prefetch/get_operation_request.cc
@@ -19,13 +19,13 @@ GetOperationRequest::GetOperationRequest(
const std::string& name,
version_info::Channel channel,
net::URLRequestContextGetter* request_context_getter,
- const PrefetchRequestFinishedCallback& callback)
- : callback_(callback) {
+ PrefetchRequestFinishedCallback callback)
+ : callback_(std::move(callback)) {
fetcher_ = PrefetchRequestFetcher::CreateForGet(
GetOperationRequestURL(name, channel), request_context_getter,
- base::Bind(&GetOperationRequest::OnCompleted,
- // Fetcher is owned by this instance.
- base::Unretained(this), name));
+ base::BindOnce(&GetOperationRequest::OnCompleted,
+ // Fetcher is owned by this instance.
+ base::Unretained(this), name));
}
GetOperationRequest::~GetOperationRequest() {}
@@ -35,20 +35,22 @@ void GetOperationRequest::OnCompleted(
PrefetchRequestStatus status,
const std::string& data) {
if (status != PrefetchRequestStatus::SUCCESS) {
- callback_.Run(status, assigned_operation_name,
- std::vector<RenderPageInfo>());
+ std::move(callback_).Run(status, assigned_operation_name,
+ std::vector<RenderPageInfo>());
return;
}
std::vector<RenderPageInfo> pages;
std::string found_operation_name = ParseOperationResponse(data, &pages);
if (found_operation_name.empty()) {
- callback_.Run(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
- assigned_operation_name, std::vector<RenderPageInfo>());
+ std::move(callback_).Run(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
+ assigned_operation_name,
+ std::vector<RenderPageInfo>());
return;
}
- callback_.Run(PrefetchRequestStatus::SUCCESS, assigned_operation_name, pages);
+ std::move(callback_).Run(PrefetchRequestStatus::SUCCESS,
+ assigned_operation_name, pages);
}
-} // offline_pages
+} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/get_operation_request.h b/chromium/components/offline_pages/core/prefetch/get_operation_request.h
index 3ff6a51b40b..af482f84228 100644
--- a/chromium/components/offline_pages/core/prefetch/get_operation_request.h
+++ b/chromium/components/offline_pages/core/prefetch/get_operation_request.h
@@ -30,7 +30,7 @@ class GetOperationRequest {
GetOperationRequest(const std::string& name,
version_info::Channel channel,
net::URLRequestContextGetter* request_context_getter,
- const PrefetchRequestFinishedCallback& callback);
+ PrefetchRequestFinishedCallback callback);
~GetOperationRequest();
private:
diff --git a/chromium/components/offline_pages/core/prefetch/get_operation_request_unittest.cc b/chromium/components/offline_pages/core/prefetch/get_operation_request_unittest.cc
index d943c9fed50..3fd39b70fa7 100644
--- a/chromium/components/offline_pages/core/prefetch/get_operation_request_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/get_operation_request_unittest.cc
@@ -38,9 +38,10 @@ const char kServerPathForTestOperation[] = "/v1/operations/test-operation-1234";
class GetOperationRequestTest : public PrefetchRequestTestBase {
public:
std::unique_ptr<GetOperationRequest> CreateRequest(
- const PrefetchRequestFinishedCallback& callback) {
- return std::unique_ptr<GetOperationRequest>(new GetOperationRequest(
- kTestOperationName, kTestChannel, request_context(), callback));
+ PrefetchRequestFinishedCallback callback) {
+ return std::unique_ptr<GetOperationRequest>(
+ new GetOperationRequest(kTestOperationName, kTestChannel,
+ request_context(), std::move(callback)));
}
};
diff --git a/chromium/components/offline_pages/core/prefetch/get_operation_task.cc b/chromium/components/offline_pages/core/prefetch/get_operation_task.cc
index 6d73ff5db93..d468aaf9044 100644
--- a/chromium/components/offline_pages/core/prefetch/get_operation_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/get_operation_task.cc
@@ -57,9 +57,6 @@ std::unique_ptr<std::vector<std::string>> AvailableOperationsSync(
GetOperationTask::OperationResultList SelectOperationsToFetch(
sql::Connection* db) {
- if (!db)
- return MakeError();
-
sql::Transaction transaction(db);
if (!transaction.Begin())
return MakeError();
@@ -84,10 +81,10 @@ GetOperationTask::OperationResultList SelectOperationsToFetch(
GetOperationTask::GetOperationTask(
PrefetchStore* store,
PrefetchNetworkRequestFactory* request_factory,
- const PrefetchRequestFinishedCallback& callback)
+ PrefetchRequestFinishedCallback callback)
: prefetch_store_(store),
request_factory_(request_factory),
- callback_(callback),
+ callback_(std::move(callback)),
weak_factory_(this) {}
GetOperationTask::~GetOperationTask() {}
@@ -96,14 +93,16 @@ void GetOperationTask::Run() {
prefetch_store_->Execute(
base::BindOnce(&SelectOperationsToFetch),
base::BindOnce(&GetOperationTask::StartGetOperationRequests,
- weak_factory_.GetWeakPtr()));
+ weak_factory_.GetWeakPtr()),
+ MakeError());
}
void GetOperationTask::StartGetOperationRequests(
OperationResultList operation_names) {
if (operation_names) {
for (std::string& operation : *operation_names) {
- request_factory_->MakeGetOperationRequest(operation, callback_);
+ request_factory_->MakeGetOperationRequest(operation,
+ std::move(callback_));
}
}
diff --git a/chromium/components/offline_pages/core/prefetch/get_operation_task.h b/chromium/components/offline_pages/core/prefetch/get_operation_task.h
index 8b67e61b70a..d205deba668 100644
--- a/chromium/components/offline_pages/core/prefetch/get_operation_task.h
+++ b/chromium/components/offline_pages/core/prefetch/get_operation_task.h
@@ -26,7 +26,7 @@ class GetOperationTask : public Task {
GetOperationTask(PrefetchStore* store,
PrefetchNetworkRequestFactory* request_factory,
- const PrefetchRequestFinishedCallback& callback);
+ PrefetchRequestFinishedCallback callback);
~GetOperationTask() override;
// Task implementation.
diff --git a/chromium/components/offline_pages/core/prefetch/import_archives_task.cc b/chromium/components/offline_pages/core/prefetch/import_archives_task.cc
index 7ebacfc3f5b..59431e4cef6 100644
--- a/chromium/components/offline_pages/core/prefetch/import_archives_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/import_archives_task.cc
@@ -68,9 +68,6 @@ bool UpdateToImportingStateSync(int64_t offline_id, sql::Connection* db) {
std::unique_ptr<std::vector<PrefetchArchiveInfo>>
GetArchivesAndUpdateToImportingStateSync(sql::Connection* db) {
- if (!db)
- return nullptr;
-
sql::Transaction transaction(db);
if (!transaction.Begin())
return nullptr;
@@ -105,7 +102,8 @@ void ImportArchivesTask::Run() {
prefetch_store_->Execute(
base::BindOnce(&GetArchivesAndUpdateToImportingStateSync),
base::BindOnce(&ImportArchivesTask::OnArchivesRetrieved,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr()),
+ std::unique_ptr<std::vector<PrefetchArchiveInfo>>());
}
void ImportArchivesTask::OnArchivesRetrieved(
diff --git a/chromium/components/offline_pages/core/prefetch/import_cleanup_task.cc b/chromium/components/offline_pages/core/prefetch/import_cleanup_task.cc
index 859ef5e745d..0d1d103d23a 100644
--- a/chromium/components/offline_pages/core/prefetch/import_cleanup_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/import_cleanup_task.cc
@@ -51,9 +51,6 @@ bool ExpireImportSync(int64_t offline_id, sql::Connection* db) {
bool CleanupImportsSync(const std::set<int64_t>& outstanding_import_offline_ids,
sql::Connection* db) {
- if (!db)
- return false;
-
sql::Transaction transaction(db);
if (!transaction.Begin())
return false;
@@ -93,7 +90,8 @@ void ImportCleanupTask::Run() {
prefetch_store_->Execute(
base::BindOnce(&CleanupImportsSync, outstanding_import_offline_ids),
base::BindOnce(&ImportCleanupTask::OnPrefetchItemUpdated,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr()),
+ false);
}
void ImportCleanupTask::OnPrefetchItemUpdated(bool success) {
diff --git a/chromium/components/offline_pages/core/prefetch/import_completed_task.cc b/chromium/components/offline_pages/core/prefetch/import_completed_task.cc
index fe2f1352237..6044ba44ba1 100644
--- a/chromium/components/offline_pages/core/prefetch/import_completed_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/import_completed_task.cc
@@ -21,9 +21,6 @@ namespace {
bool UpdateToFinishedStateSync(int64_t offline_id,
bool success,
sql::Connection* db) {
- if (!db)
- return false;
-
static const char kSql[] =
"UPDATE prefetch_items"
" SET state = ?, error_code = ?"
@@ -61,7 +58,8 @@ void ImportCompletedTask::Run() {
prefetch_store_->Execute(
base::BindOnce(&UpdateToFinishedStateSync, offline_id_, success_),
base::BindOnce(&ImportCompletedTask::OnStateUpdatedToFinished,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr()),
+ false);
}
void ImportCompletedTask::OnStateUpdatedToFinished(bool row_was_updated) {
diff --git a/chromium/components/offline_pages/core/prefetch/mark_operation_done_task.cc b/chromium/components/offline_pages/core/prefetch/mark_operation_done_task.cc
index 9d68ab9b8ab..c7f0d962eb5 100644
--- a/chromium/components/offline_pages/core/prefetch/mark_operation_done_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/mark_operation_done_task.cc
@@ -44,9 +44,6 @@ MarkOperationDoneTask::TaskResult MakeStoreError() {
MarkOperationDoneTask::TaskResult MarkOperationCompletedOnServerSync(
const std::string& operation_name,
sql::Connection* db) {
- if (!db)
- return MakeStoreError();
-
sql::Transaction transaction(db);
if (transaction.Begin() && UpdatePrefetchItemsSync(db, operation_name) &&
transaction.Commit()) {
@@ -72,7 +69,8 @@ MarkOperationDoneTask::~MarkOperationDoneTask() {}
void MarkOperationDoneTask::Run() {
prefetch_store_->Execute(
base::BindOnce(&MarkOperationCompletedOnServerSync, operation_name_),
- base::BindOnce(&MarkOperationDoneTask::Done, weak_factory_.GetWeakPtr()));
+ base::BindOnce(&MarkOperationDoneTask::Done, weak_factory_.GetWeakPtr()),
+ MakeStoreError());
}
void MarkOperationDoneTask::Done(TaskResult result) {
diff --git a/chromium/components/offline_pages/core/prefetch/metrics_finalization_task.cc b/chromium/components/offline_pages/core/prefetch/metrics_finalization_task.cc
index 42b7e26fa87..b15b3fdcb40 100644
--- a/chromium/components/offline_pages/core/prefetch/metrics_finalization_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/metrics_finalization_task.cc
@@ -202,9 +202,6 @@ void ReportMetricsFor(const PrefetchItemStats& url, const base::Time now) {
}
bool ReportMetricsAndFinalizeSync(sql::Connection* db) {
- if (!db)
- return false;
-
sql::Transaction transaction(db);
if (!transaction.Begin())
return false;
@@ -249,7 +246,8 @@ void MetricsFinalizationTask::Run() {
prefetch_store_->Execute(
base::BindOnce(&ReportMetricsAndFinalizeSync),
base::BindOnce(&MetricsFinalizationTask::MetricsFinalized,
- weak_factory_.GetWeakPtr()));
+ weak_factory_.GetWeakPtr()),
+ false);
}
void MetricsFinalizationTask::MetricsFinalized(bool result) {
diff --git a/chromium/components/offline_pages/core/prefetch/metrics_finalization_task_unittest.cc b/chromium/components/offline_pages/core/prefetch/metrics_finalization_task_unittest.cc
index f62e433ab2e..d0c5fa802d9 100644
--- a/chromium/components/offline_pages/core/prefetch/metrics_finalization_task_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/metrics_finalization_task_unittest.cc
@@ -7,7 +7,7 @@
#include <memory>
#include <set>
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/offline_pages/core/prefetch/mock_prefetch_item_generator.h"
#include "components/offline_pages/core/prefetch/prefetch_item.h"
#include "components/offline_pages/core/prefetch/prefetch_task_test_base.h"
diff --git a/chromium/components/offline_pages/core/prefetch/mock_thumbnail_fetcher.h b/chromium/components/offline_pages/core/prefetch/mock_thumbnail_fetcher.h
index 87cefbdfa67..bb64245d56b 100644
--- a/chromium/components/offline_pages/core/prefetch/mock_thumbnail_fetcher.h
+++ b/chromium/components/offline_pages/core/prefetch/mock_thumbnail_fetcher.h
@@ -14,15 +14,10 @@ class MockThumbnailFetcher : public ThumbnailFetcher {
public:
MockThumbnailFetcher();
~MockThumbnailFetcher() override;
- MOCK_METHOD3(FetchSuggestionImageData_,
+ MOCK_METHOD3(FetchSuggestionImageData,
void(const ClientId& client_id,
bool is_first_attempt,
- ImageDataFetchedCallback* callback));
- void FetchSuggestionImageData(const ClientId& client_id,
- bool is_first_attempt,
- ImageDataFetchedCallback callback) override {
- FetchSuggestionImageData_(client_id, is_first_attempt, &callback);
- }
+ ImageDataFetchedCallback callback));
};
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/page_bundle_update_task.cc b/chromium/components/offline_pages/core/prefetch/page_bundle_update_task.cc
index 02e913a0ac8..f4b737fb7e2 100644
--- a/chromium/components/offline_pages/core/prefetch/page_bundle_update_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/page_bundle_update_task.cc
@@ -29,19 +29,18 @@ bool MarkUrlRenderedSync(sql::Connection* db,
const std::string& operation_name) {
DCHECK_EQ(page.status, RenderStatus::RENDERED);
- // If the operation name is empty, then we assume this is from a
- // GeneratePageBundle request, so we don't need to match because the incoming
- // operation name is the new operation name for unfinished entries in the
- // newest batch.
+ // This method may be called upon receiving the results of GeneratePageBundle
+ // or GetOperation. For GeneratePageBundle, the operation name is not yet set
+ // in the database. For GetOperation, the operation name is already set.
+ // This statement ensures that the item's operation_name is assigned, and that
+ // an item can't be reassigned an operation name.
static const char kSql[] = R"(UPDATE prefetch_items
SET state = ?,
final_archived_url = ?,
archive_body_name = ?,
- archive_body_length = ?
- WHERE requested_url = ? AND (
- (state = ? AND operation_name = "") OR
- (state = ? AND operation_name = ?)
- )
+ archive_body_length = ?,
+ operation_name = ?
+ WHERE requested_url = ? AND state IN (?, ?) AND operation_name IN ("", ?)
)";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
@@ -56,13 +55,14 @@ bool MarkUrlRenderedSync(sql::Connection* db,
statement.BindString(1, final_url);
statement.BindString(2, page.body_name);
statement.BindInt64(3, page.body_length);
+ statement.BindString(4, operation_name);
// WHERE
- statement.BindString(4, page.url);
+ statement.BindString(5, page.url);
statement.BindInt(
- 5, static_cast<int>(PrefetchItemState::SENT_GENERATE_PAGE_BUNDLE));
- statement.BindInt(6, static_cast<int>(PrefetchItemState::SENT_GET_OPERATION));
- statement.BindString(7, operation_name);
+ 6, static_cast<int>(PrefetchItemState::SENT_GENERATE_PAGE_BUNDLE));
+ statement.BindInt(7, static_cast<int>(PrefetchItemState::SENT_GET_OPERATION));
+ statement.BindString(8, operation_name);
if (!statement.Run())
return false;
@@ -79,11 +79,9 @@ void MarkUrlFailedSync(sql::Connection* db,
static const char kSql[] = R"(UPDATE prefetch_items
SET state = ?,
- error_code = ?
- WHERE requested_url = ? AND (
- (state = ? AND operation_name = "") OR
- (state = ? AND operation_name = ?)
- )
+ error_code = ?,
+ operation_name = ?
+ WHERE requested_url = ? AND state IN (?, ?) AND operation_name IN ("", ?)
)";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
@@ -96,13 +94,14 @@ void MarkUrlFailedSync(sql::Connection* db,
// SET
statement.BindInt(0, static_cast<int>(PrefetchItemState::FINISHED));
statement.BindInt(1, static_cast<int>(final_status));
+ statement.BindString(2, operation_name);
// WHERE
- statement.BindString(2, page.url);
+ statement.BindString(3, page.url);
statement.BindInt(
- 3, static_cast<int>(PrefetchItemState::SENT_GENERATE_PAGE_BUNDLE));
- statement.BindInt(4, static_cast<int>(PrefetchItemState::SENT_GET_OPERATION));
- statement.BindString(5, operation_name);
+ 4, static_cast<int>(PrefetchItemState::SENT_GENERATE_PAGE_BUNDLE));
+ statement.BindInt(5, static_cast<int>(PrefetchItemState::SENT_GET_OPERATION));
+ statement.BindString(6, operation_name);
statement.Run();
}
@@ -137,9 +136,6 @@ PageBundleUpdateResult UpdateWithOperationResultsSync(
const std::string operation_name,
const std::vector<RenderPageInfo>& pages,
sql::Connection* db) {
- if (!db)
- return false;
-
sql::Transaction transaction(db);
if (!transaction.Begin())
return false;
@@ -195,7 +191,8 @@ void PageBundleUpdateTask::Run() {
store_->Execute(
base::BindOnce(&UpdateWithOperationResultsSync, operation_name_, pages_),
base::BindOnce(&PageBundleUpdateTask::FinishedWork,
- weak_factory_.GetWeakPtr()));
+ weak_factory_.GetWeakPtr()),
+ false);
}
void PageBundleUpdateTask::FinishedWork(
diff --git a/chromium/components/offline_pages/core/prefetch/page_bundle_update_task_unittest.cc b/chromium/components/offline_pages/core/prefetch/page_bundle_update_task_unittest.cc
index 2c7a8da255e..146da6cbed5 100644
--- a/chromium/components/offline_pages/core/prefetch/page_bundle_update_task_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/page_bundle_update_task_unittest.cc
@@ -96,12 +96,12 @@ TEST_F(PageBundleUpdateTaskTest, UpdatesItemsFromSentGeneratePageBundle) {
RunTask(&task);
// The matching entry has been updated.
- EXPECT_EQ(PrefetchItemState::RECEIVED_BUNDLE,
- store_util()->GetPrefetchItem(item1.offline_id)->state);
- EXPECT_NE("",
- store_util()->GetPrefetchItem(item1.offline_id)->archive_body_name);
- EXPECT_LT(
- 0, store_util()->GetPrefetchItem(item1.offline_id)->archive_body_length);
+ const PrefetchItem stored_item =
+ *store_util()->GetPrefetchItem(item1.offline_id).get();
+ EXPECT_EQ(PrefetchItemState::RECEIVED_BUNDLE, stored_item.state);
+ EXPECT_NE("", stored_item.archive_body_name);
+ EXPECT_LT(0, stored_item.archive_body_length);
+ EXPECT_EQ("operation", stored_item.operation_name);
// The non-matching entry has not changed.
EXPECT_EQ(item2, *(store_util()->GetPrefetchItem(item2.offline_id)));
// The dispatcher knows to reschedule the action tasks.
@@ -123,12 +123,12 @@ TEST_F(PageBundleUpdateTaskTest, SentGetOperationToReceivedBundle) {
RunTask(&task);
// The matching entry has been updated.
- EXPECT_EQ(store_util()->GetPrefetchItem(item1.offline_id)->state,
- PrefetchItemState::RECEIVED_BUNDLE);
- EXPECT_NE("",
- store_util()->GetPrefetchItem(item1.offline_id)->archive_body_name);
- EXPECT_LT(
- 0, store_util()->GetPrefetchItem(item1.offline_id)->archive_body_length);
+ const PrefetchItem stored_item =
+ *store_util()->GetPrefetchItem(item1.offline_id).get();
+ EXPECT_EQ(stored_item.state, PrefetchItemState::RECEIVED_BUNDLE);
+ EXPECT_NE("", stored_item.archive_body_name);
+ EXPECT_LT(0, stored_item.archive_body_length);
+ EXPECT_EQ(item1.operation_name, stored_item.operation_name);
EXPECT_LE(1, dispatcher.processing_schedule_count);
}
@@ -180,12 +180,14 @@ TEST_F(PageBundleUpdateTaskTest, FailedRenderToFinished) {
// exceeded result.
PrefetchItem item1 = item_generator()->CreateItem(
PrefetchItemState::SENT_GENERATE_PAGE_BUNDLE);
+ item1.operation_name = "operation";
ASSERT_EQ(PrefetchItemErrorCode::SUCCESS, item1.error_code);
ASSERT_TRUE(store_util()->InsertPrefetchItem(item1));
RenderPageInfo item1_render_info = FailedPageInfoForPrefetchItem(item1);
PrefetchItem item2 = item_generator()->CreateItem(
PrefetchItemState::SENT_GENERATE_PAGE_BUNDLE);
+ item2.operation_name = "operation";
ASSERT_EQ(PrefetchItemErrorCode::SUCCESS, item2.error_code);
ASSERT_TRUE(store_util()->InsertPrefetchItem(item2));
RenderPageInfo item2_render_info = FailedPageInfoForPrefetchItem(item2);
@@ -198,11 +200,15 @@ TEST_F(PageBundleUpdateTaskTest, FailedRenderToFinished) {
store_util()->GetPrefetchItem(item1.offline_id)->state);
EXPECT_EQ(PrefetchItemErrorCode::ARCHIVING_FAILED,
store_util()->GetPrefetchItem(item1.offline_id)->error_code);
+ EXPECT_EQ("operation",
+ store_util()->GetPrefetchItem(item1.offline_id)->operation_name);
EXPECT_EQ(PrefetchItemState::FINISHED,
store_util()->GetPrefetchItem(item2.offline_id)->state);
EXPECT_EQ(PrefetchItemErrorCode::ARCHIVING_LIMIT_EXCEEDED,
store_util()->GetPrefetchItem(item2.offline_id)->error_code);
+ EXPECT_EQ("operation",
+ store_util()->GetPrefetchItem(item2.offline_id)->operation_name);
EXPECT_EQ(0, dispatcher.processing_schedule_count);
}
@@ -211,16 +217,19 @@ TEST_F(PageBundleUpdateTaskTest, MixOfResults) {
// independently and properly.
PrefetchItem item1 = item_generator()->CreateItem(
PrefetchItemState::SENT_GENERATE_PAGE_BUNDLE);
+ item1.operation_name = "operation";
ASSERT_TRUE(store_util()->InsertPrefetchItem(item1));
RenderPageInfo item1_render_info = PendingPageInfoForPrefetchItem(item1);
PrefetchItem item2 = item_generator()->CreateItem(
PrefetchItemState::SENT_GENERATE_PAGE_BUNDLE);
+ item2.operation_name = "operation";
ASSERT_TRUE(store_util()->InsertPrefetchItem(item2));
RenderPageInfo item2_render_info = FailedPageInfoForPrefetchItem(item2);
PrefetchItem item3 = item_generator()->CreateItem(
PrefetchItemState::SENT_GENERATE_PAGE_BUNDLE);
+ item3.operation_name = "operation";
ASSERT_TRUE(store_util()->InsertPrefetchItem(item3));
RenderPageInfo item3_render_info = RenderedPageInfoForPrefetchItem(item3);
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc b/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
index 591627820bd..86becd16455 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
@@ -209,7 +209,7 @@ void PrefetchDispatcherImpl::QueueActionTasks() {
std::unique_ptr<Task> get_operation_task = std::make_unique<GetOperationTask>(
service_->GetPrefetchStore(),
service_->GetPrefetchNetworkRequestFactory(),
- base::Bind(
+ base::BindOnce(
&PrefetchDispatcherImpl::DidGenerateBundleOrGetOperationRequest,
weak_factory_.GetWeakPtr(), "GetOperationRequest"));
task_queue_.AddTask(std::move(get_operation_task));
@@ -218,7 +218,7 @@ void PrefetchDispatcherImpl::QueueActionTasks() {
std::make_unique<GeneratePageBundleTask>(
this, service_->GetPrefetchStore(), service_->GetPrefetchGCMHandler(),
service_->GetPrefetchNetworkRequestFactory(),
- base::Bind(
+ base::BindOnce(
&PrefetchDispatcherImpl::DidGenerateBundleOrGetOperationRequest,
weak_factory_.GetWeakPtr(), "GeneratePageBundleRequest"));
task_queue_.AddTask(std::move(generate_page_bundle_task));
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc b/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
index 20ec61e57e3..8b74e711f90 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_dispatcher_impl_unittest.cc
@@ -76,17 +76,11 @@ class MockOfflinePageModel : public StubOfflinePageModel {
}
~MockOfflinePageModel() override = default;
MOCK_METHOD1(StoreThumbnail, void(const OfflinePageThumbnail& thumb));
- MOCK_METHOD2(HasThumbnailForOfflineId_,
+ MOCK_METHOD2(HasThumbnailForOfflineId,
void(int64_t offline_id,
- base::OnceCallback<void(bool)>* callback));
+ base::OnceCallback<void(bool)> callback));
MOCK_METHOD2(AddPage,
- void(const OfflinePageItem& page,
- const AddPageCallback& callback));
- void HasThumbnailForOfflineId(
- int64_t offline_id,
- base::OnceCallback<void(bool)> callback) override {
- HasThumbnailForOfflineId_(offline_id, &callback);
- }
+ void(const OfflinePageItem& page, AddPageCallback callback));
};
class TestPrefetchBackgroundTask : public PrefetchBackgroundTask {
@@ -124,18 +118,26 @@ class FakePrefetchNetworkRequestFactory
void MakeGeneratePageBundleRequest(
const std::vector<std::string>& prefetch_urls,
const std::string& gcm_registration_id,
- const PrefetchRequestFinishedCallback& callback) override {
- TestPrefetchNetworkRequestFactory::MakeGeneratePageBundleRequest(
- prefetch_urls, gcm_registration_id, callback);
- if (!respond_to_generate_page_bundle_)
+ PrefetchRequestFinishedCallback callback) override {
+ // TODO(https://crbug.com/850648): Explicitly passing in a base::DoNothing()
+ // callback when |respond_to_generate_page_bundle_| is set to true, to avoid
+ // the |callback| being called for more than once.
+ if (!respond_to_generate_page_bundle_) {
+ TestPrefetchNetworkRequestFactory::MakeGeneratePageBundleRequest(
+ prefetch_urls, gcm_registration_id, std::move(callback));
return;
+ } else {
+ TestPrefetchNetworkRequestFactory::MakeGeneratePageBundleRequest(
+ prefetch_urls, gcm_registration_id, base::DoNothing());
+ }
std::vector<RenderPageInfo> pages;
for (const std::string& url : prefetch_urls) {
pages.push_back(RenderInfo(url));
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindRepeating(callback, PrefetchRequestStatus::SUCCESS,
- kOperationName, pages));
+ FROM_HERE,
+ base::BindOnce(std::move(callback), PrefetchRequestStatus::SUCCESS,
+ kOperationName, pages));
}
void set_respond_to_generate_page_bundle(bool value) {
@@ -193,34 +195,24 @@ class PrefetchDispatcherTest : public PrefetchRequestTestBase {
const char* client_id) {
EXPECT_CALL(
*thumbnail_fetcher_,
- FetchSuggestionImageData_(
+ FetchSuggestionImageData(
ClientId(kSuggestedArticlesNamespace, client_id), first_attempt, _))
- .WillOnce(
- testing::Invoke(testing::CallbackToFunctor(base::BindRepeating(
- [](const std::string& thumbnail_data,
- scoped_refptr<base::TestMockTimeTaskRunner> task_runner,
- const ClientId& id, bool is_first_attemp,
- ThumbnailFetcher::ImageDataFetchedCallback* callback) {
- task_runner->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(*callback), thumbnail_data));
- },
- thumbnail_data, task_runner()))));
+ .WillOnce([&, thumbnail_data](
+ const ClientId& client_id, bool first_attempt,
+ ThumbnailFetcher::ImageDataFetchedCallback callback) {
+ task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), thumbnail_data));
+ });
}
void ExpectHasThumbnailForOfflineId(int64_t offline_id, bool to_return) {
- EXPECT_CALL(*offline_model_, HasThumbnailForOfflineId_(offline_id, _))
+ EXPECT_CALL(*offline_model_, HasThumbnailForOfflineId(offline_id, _))
.WillOnce(
- testing::Invoke(testing::CallbackToFunctor(base::BindRepeating(
- [](bool to_return,
- scoped_refptr<base::TestMockTimeTaskRunner> task_runner,
- int64_t offline_id_,
- base::OnceCallback<void(bool)>* callback) {
- task_runner->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(*callback), to_return));
- },
- to_return, task_runner()))));
+ [&, offline_id, to_return](
+ int64_t offline_id, base::OnceCallback<void(bool)> callback) {
+ task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), to_return));
+ });
}
PrefetchDispatcherImpl* dispatcher() { return dispatcher_; }
@@ -574,7 +566,7 @@ TEST_F(PrefetchDispatcherTest, ThumbnailFetchSuccess_ItemDownloaded) {
TEST_F(PrefetchDispatcherTest, ThumbnailAlreadyExists_ItemDownloaded) {
ExpectHasThumbnailForOfflineId(kTestOfflineID, true);
- EXPECT_CALL(*thumbnail_fetcher_, FetchSuggestionImageData_(_, _, _)).Times(0);
+ EXPECT_CALL(*thumbnail_fetcher_, FetchSuggestionImageData(_, _, _)).Times(0);
EXPECT_CALL(*offline_model_, StoreThumbnail(_)).Times(0);
prefetch_dispatcher()->ItemDownloaded(
kTestOfflineID, ClientId(kSuggestedArticlesNamespace, kClientID));
@@ -626,13 +618,12 @@ TEST_F(PrefetchDispatcherTest, PrefetchItemFlow) {
// callback, and store the page to added_page.
OfflinePageItem added_page;
EXPECT_CALL(*offline_model_, AddPage(_, _))
- .WillOnce(testing::Invoke([&](const OfflinePageItem& page,
- const AddPageCallback& callback) {
+ .WillOnce([&](const OfflinePageItem& page, AddPageCallback callback) {
added_page = page;
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(callback, AddPageResult::SUCCESS, page.offline_id));
- }));
+ FROM_HERE, base::BindOnce(std::move(callback),
+ AddPageResult::SUCCESS, page.offline_id));
+ });
network_request_factory()->set_respond_to_generate_page_bundle(true);
download_service()->SetTestFileData(kBodyContent);
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_downloader.h b/chromium/components/offline_pages/core/prefetch/prefetch_downloader.h
index 01a6521b108..349509fe405 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_downloader.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_downloader.h
@@ -36,7 +36,8 @@ class PrefetchDownloader {
// Starts to download an archive from |download_location|.
virtual void StartDownload(const std::string& download_id,
- const std::string& download_location) = 0;
+ const std::string& download_location,
+ const std::string& operation_name) = 0;
// Called when the download service is initialized.
// |success_downloads| is a map with download_id as key and pair of file path
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl.cc b/chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl.cc
index c857544970d..cd62c3af2fb 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl.cc
@@ -5,6 +5,7 @@
#include "components/offline_pages/core/prefetch/prefetch_downloader_impl.h"
#include "base/bind.h"
+#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/time/default_clock.h"
@@ -17,6 +18,7 @@
#include "components/offline_pages/core/prefetch/prefetch_dispatcher.h"
#include "components/offline_pages/core/prefetch/prefetch_server_urls.h"
#include "components/offline_pages/core/prefetch/prefetch_service.h"
+#include "net/http/http_util.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "url/gurl.h"
@@ -69,9 +71,9 @@ void PrefetchDownloaderImpl::CleanupDownloadsWhenReady() {
cleanup_downloads_when_service_starts_ = true;
}
-void PrefetchDownloaderImpl::StartDownload(
- const std::string& download_id,
- const std::string& download_location) {
+void PrefetchDownloaderImpl::StartDownload(const std::string& download_id,
+ const std::string& download_location,
+ const std::string& operation_name) {
prefetch_service_->GetLogger()->RecordActivity(
"Downloader: Start download of '" + download_location +
"', download_id=" + download_id);
@@ -108,8 +110,9 @@ void PrefetchDownloaderImpl::StartDownload(
net::MutableNetworkTrafficAnnotationTag(traffic_annotation);
params.client = download::DownloadClient::OFFLINE_PAGE_PREFETCH;
params.guid = download_id;
- params.callback = base::Bind(&PrefetchDownloaderImpl::OnStartDownload,
- weak_ptr_factory_.GetWeakPtr());
+ params.callback = base::AdaptCallbackForRepeating(
+ base::BindOnce(&PrefetchDownloaderImpl::OnStartDownload,
+ weak_ptr_factory_.GetWeakPtr()));
params.scheduling_params.network_requirements =
download::SchedulingParams::NetworkRequirements::UNMETERED;
params.scheduling_params.battery_requirements =
@@ -124,6 +127,16 @@ void PrefetchDownloaderImpl::StartDownload(
experiment_header);
}
+ if (!operation_name.empty() &&
+ net::HttpUtil::IsValidHeaderValue(operation_name)) {
+ params.request_params.request_headers.SetHeader(
+ kPrefetchOperationHeaderName, operation_name);
+ } else {
+ // Offline internals uses operation_name="".
+ LOG(WARNING) << "Not setting " << kPrefetchOperationHeaderName
+ << ", invalid operation name '" << operation_name << "'";
+ }
+
// Lessen download restrictions if limitless prefetching is enabled.
if (IsLimitlessPrefetchingEnabled()) {
params.scheduling_params.network_requirements =
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl.h b/chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl.h
index a61d58a1548..3a7f0b0c29c 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl.h
@@ -39,7 +39,8 @@ class PrefetchDownloaderImpl : public PrefetchDownloader {
bool IsDownloadServiceUnavailable() const override;
void CleanupDownloadsWhenReady() override;
void StartDownload(const std::string& download_id,
- const std::string& download_location) override;
+ const std::string& download_location,
+ const std::string& operation_name) override;
void OnDownloadServiceReady(
const std::set<std::string>& outstanding_download_ids,
const std::map<std::string, std::pair<base::FilePath, int64_t>>&
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl_unittest.cc b/chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl_unittest.cc
index d934b9b5fae..f77debc5802 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_downloader_impl_unittest.cc
@@ -6,6 +6,7 @@
#include <vector>
+#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
@@ -27,6 +28,7 @@ const char kDownloadId2[] = "Abcd";
const char kFailedDownloadId[] = "f1f1FF";
const char kDownloadLocation[] = "page/1";
const char kDownloadLocation2[] = "page/zz";
+const char kOperationName[] = "operation-123";
const char kServerPathForDownload[] = "/v1/media/page/1";
} // namespace
@@ -65,8 +67,10 @@ class PrefetchDownloaderImplTest : public PrefetchRequestTestBase {
}
void StartDownload(const std::string& download_id,
- const std::string& download_location) {
- prefetch_downloader()->StartDownload(download_id, download_location);
+ const std::string& download_location,
+ const std::string& operation_name) {
+ prefetch_downloader()->StartDownload(download_id, download_location,
+ operation_name);
}
base::Optional<download::DownloadParams> GetDownload(
@@ -101,7 +105,7 @@ TEST_F(PrefetchDownloaderImplTest, DownloadParams) {
base::Time epoch = base::Time();
clock()->SetNow(epoch);
- StartDownload(kDownloadId, kDownloadLocation);
+ StartDownload(kDownloadId, kDownloadLocation, kOperationName);
base::Optional<download::DownloadParams> params = GetDownload(kDownloadId);
ASSERT_TRUE(params.has_value());
EXPECT_EQ(kDownloadId, params->guid);
@@ -115,7 +119,9 @@ TEST_F(PrefetchDownloaderImplTest, DownloadParams) {
std::string alt_value;
EXPECT_TRUE(net::GetValueForKeyInQuery(download_url, "alt", &alt_value));
EXPECT_EQ("media", alt_value);
- EXPECT_TRUE(params->request_params.request_headers.IsEmpty());
+ EXPECT_EQ(base::StrCat({kPrefetchOperationHeaderName, ": ", kOperationName,
+ "\r\n\r\n"}),
+ params->request_params.request_headers.ToString());
EXPECT_EQ(base::TimeDelta::FromDays(2),
params->scheduling_params.cancel_time - epoch);
@@ -126,7 +132,7 @@ TEST_F(PrefetchDownloaderImplTest, ExperimentHeaderInDownloadParams) {
OnDownloadServiceReady();
SetUpExperimentOption();
- StartDownload(kDownloadId, kDownloadLocation);
+ StartDownload(kDownloadId, kDownloadLocation, kOperationName);
base::Optional<download::DownloadParams> params = GetDownload(kDownloadId);
ASSERT_TRUE(params.has_value());
std::string header_value;
@@ -138,8 +144,8 @@ TEST_F(PrefetchDownloaderImplTest, ExperimentHeaderInDownloadParams) {
TEST_F(PrefetchDownloaderImplTest, DownloadSucceeded) {
OnDownloadServiceReady();
- StartDownload(kDownloadId, kDownloadLocation);
- StartDownload(kDownloadId2, kDownloadLocation2);
+ StartDownload(kDownloadId, kDownloadLocation, kOperationName);
+ StartDownload(kDownloadId2, kDownloadLocation2, kOperationName);
RunUntilIdle();
ASSERT_EQ(2u, completed_downloads().size());
EXPECT_EQ(kDownloadId, completed_downloads()[0].download_id);
@@ -150,7 +156,7 @@ TEST_F(PrefetchDownloaderImplTest, DownloadSucceeded) {
TEST_F(PrefetchDownloaderImplTest, DownloadFailed) {
OnDownloadServiceReady();
- StartDownload(kFailedDownloadId, kDownloadLocation);
+ StartDownload(kFailedDownloadId, kDownloadLocation, kOperationName);
RunUntilIdle();
ASSERT_EQ(1u, completed_downloads().size());
EXPECT_EQ(kFailedDownloadId, completed_downloads()[0].download_id);
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc b/chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc
index b124dc1e9ac..7b88369f686 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_gcm_app_handler_unittest.cc
@@ -103,10 +103,10 @@ TEST_F(PrefetchGCMAppHandlerTest, TestInvalidMessage) {
TEST_F(PrefetchGCMAppHandlerTest, TestGetToken) {
std::string result_token;
- handler()->GetGCMToken(base::Bind(
+ handler()->GetGCMToken(base::AdaptCallbackForRepeating(base::BindOnce(
[](std::string* result_token, const std::string& token,
instance_id::InstanceID::Result result) { *result_token = token; },
- &result_token));
+ &result_token)));
EXPECT_EQ(token_factory()->token, result_token);
}
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_importer_impl_unittest.cc b/chromium/components/offline_pages/core/prefetch/prefetch_importer_impl_unittest.cc
index 69e80d37731..ab0997e2ec3 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_importer_impl_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_importer_impl_unittest.cc
@@ -32,12 +32,11 @@ class TestOfflinePageModel : public StubOfflinePageModel {
TestOfflinePageModel() { ignore_result(archive_dir_.CreateUniqueTempDir()); }
~TestOfflinePageModel() override = default;
- void AddPage(const OfflinePageItem& page,
- const AddPageCallback& callback) override {
+ void AddPage(const OfflinePageItem& page, AddPageCallback callback) override {
page_added_ = page.offline_id != kTestOfflineIDFailedToAdd;
if (page_added_)
last_added_page_ = page;
- callback.Run(
+ std::move(callback).Run(
page_added_ ? AddPageResult::SUCCESS : AddPageResult::STORE_FAILURE,
page.offline_id);
}
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory.h b/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory.h
index 835ce2156fd..e9f1bb07b81 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory.h
@@ -30,7 +30,7 @@ class PrefetchNetworkRequestFactory {
virtual void MakeGeneratePageBundleRequest(
const std::vector<std::string>& prefetch_urls,
const std::string& gcm_registration_id,
- const PrefetchRequestFinishedCallback& callback) = 0;
+ PrefetchRequestFinishedCallback callback) = 0;
// Returns a list of URLs included into all currently ongoing
// GeneratePageBundle requests.
@@ -42,7 +42,7 @@ class PrefetchNetworkRequestFactory {
// will cancel the existing request and start a new one.
virtual void MakeGetOperationRequest(
const std::string& operation_name,
- const PrefetchRequestFinishedCallback& callback) = 0;
+ PrefetchRequestFinishedCallback callback) = 0;
// Returns the current GetOperationRequest with the given name, if any.
virtual GetOperationRequest* FindGetOperationRequestByName(
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.cc b/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.cc
index 7d245a74bd9..15f86fe0f89 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.cc
@@ -61,7 +61,7 @@ bool PrefetchNetworkRequestFactoryImpl::HasOutstandingRequests() const {
void PrefetchNetworkRequestFactoryImpl::MakeGeneratePageBundleRequest(
const std::vector<std::string>& url_strings,
const std::string& gcm_registration_id,
- const PrefetchRequestFinishedCallback& callback) {
+ PrefetchRequestFinishedCallback callback) {
if (!AddConcurrentRequest())
return;
int max_bundle_size = IsLimitlessPrefetchingEnabled()
@@ -72,9 +72,9 @@ void PrefetchNetworkRequestFactoryImpl::MakeGeneratePageBundleRequest(
std::make_unique<GeneratePageBundleRequest>(
user_agent_, gcm_registration_id, max_bundle_size, url_strings,
channel_, request_context_.get(),
- base::Bind(
+ base::BindOnce(
&PrefetchNetworkRequestFactoryImpl::GeneratePageBundleRequestDone,
- weak_factory_.GetWeakPtr(), callback, request_id));
+ weak_factory_.GetWeakPtr(), std::move(callback), request_id));
}
std::unique_ptr<std::set<std::string>>
@@ -89,35 +89,35 @@ PrefetchNetworkRequestFactoryImpl::GetAllUrlsRequested() const {
void PrefetchNetworkRequestFactoryImpl::MakeGetOperationRequest(
const std::string& operation_name,
- const PrefetchRequestFinishedCallback& callback) {
+ PrefetchRequestFinishedCallback callback) {
if (!AddConcurrentRequest())
return;
get_operation_requests_[operation_name] =
std::make_unique<GetOperationRequest>(
operation_name, channel_, request_context_.get(),
- base::Bind(
+ base::BindOnce(
&PrefetchNetworkRequestFactoryImpl::GetOperationRequestDone,
- weak_factory_.GetWeakPtr(), callback));
+ weak_factory_.GetWeakPtr(), std::move(callback)));
}
void PrefetchNetworkRequestFactoryImpl::GeneratePageBundleRequestDone(
- const PrefetchRequestFinishedCallback& callback,
+ PrefetchRequestFinishedCallback callback,
uint64_t request_id,
PrefetchRequestStatus status,
const std::string& operation_name,
const std::vector<RenderPageInfo>& pages) {
- callback.Run(status, operation_name, pages);
+ std::move(callback).Run(status, operation_name, pages);
generate_page_bundle_requests_.erase(request_id);
ReleaseConcurrentRequest();
RecordGeneratePageBundleStatusUma(status);
}
void PrefetchNetworkRequestFactoryImpl::GetOperationRequestDone(
- const PrefetchRequestFinishedCallback& callback,
+ PrefetchRequestFinishedCallback callback,
PrefetchRequestStatus status,
const std::string& operation_name,
const std::vector<RenderPageInfo>& pages) {
- callback.Run(status, operation_name, pages);
+ std::move(callback).Run(status, operation_name, pages);
get_operation_requests_.erase(operation_name);
ReleaseConcurrentRequest();
RecordGetOperationStatusUma(status);
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.h b/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.h
index 3c481084a0a..8b04646538e 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.h
@@ -34,13 +34,13 @@ class PrefetchNetworkRequestFactoryImpl : public PrefetchNetworkRequestFactory {
void MakeGeneratePageBundleRequest(
const std::vector<std::string>& prefetch_urls,
const std::string& gcm_registration_id,
- const PrefetchRequestFinishedCallback& callback) override;
+ PrefetchRequestFinishedCallback callback) override;
std::unique_ptr<std::set<std::string>> GetAllUrlsRequested() const override;
void MakeGetOperationRequest(
const std::string& operation_name,
- const PrefetchRequestFinishedCallback& callback) override;
+ PrefetchRequestFinishedCallback callback) override;
GetOperationRequest* FindGetOperationRequestByName(
const std::string& operation_name) const override;
@@ -49,14 +49,13 @@ class PrefetchNetworkRequestFactoryImpl : public PrefetchNetworkRequestFactory {
const override;
private:
- void GeneratePageBundleRequestDone(
- const PrefetchRequestFinishedCallback& callback,
- uint64_t request_id,
- PrefetchRequestStatus status,
- const std::string& operation_name,
- const std::vector<RenderPageInfo>& pages);
+ void GeneratePageBundleRequestDone(PrefetchRequestFinishedCallback callback,
+ uint64_t request_id,
+ PrefetchRequestStatus status,
+ const std::string& operation_name,
+ const std::vector<RenderPageInfo>& pages);
- void GetOperationRequestDone(const PrefetchRequestFinishedCallback& callback,
+ void GetOperationRequestDone(PrefetchRequestFinishedCallback callback,
PrefetchRequestStatus status,
const std::string& operation_name,
const std::vector<RenderPageInfo>& pages);
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl_unittest.cc b/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl_unittest.cc
index 68b019090fd..37886f93fe9 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_network_request_factory_impl_unittest.cc
@@ -4,7 +4,7 @@
#include "components/offline_pages/core/prefetch/prefetch_network_request_factory_impl.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc b/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc
index 52a26756b9f..f455c5e38e5 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.cc
@@ -32,9 +32,9 @@ const char kRequestContentType[] = "application/x-protobuf";
std::unique_ptr<PrefetchRequestFetcher> PrefetchRequestFetcher::CreateForGet(
const GURL& url,
net::URLRequestContextGetter* request_context_getter,
- const FinishedCallback& callback) {
+ FinishedCallback callback) {
return base::WrapUnique(new PrefetchRequestFetcher(
- url, std::string(), request_context_getter, callback));
+ url, std::string(), request_context_getter, std::move(callback)));
}
// static
@@ -42,17 +42,18 @@ std::unique_ptr<PrefetchRequestFetcher> PrefetchRequestFetcher::CreateForPost(
const GURL& url,
const std::string& message,
net::URLRequestContextGetter* request_context_getter,
- const FinishedCallback& callback) {
+ FinishedCallback callback) {
return base::WrapUnique(new PrefetchRequestFetcher(
- url, message, request_context_getter, callback));
+ url, message, request_context_getter, std::move(callback)));
}
PrefetchRequestFetcher::PrefetchRequestFetcher(
const GURL& url,
const std::string& message,
net::URLRequestContextGetter* request_context_getter,
- const FinishedCallback& callback)
- : request_context_getter_(request_context_getter), callback_(callback) {
+ FinishedCallback callback)
+ : request_context_getter_(request_context_getter),
+ callback_(std::move(callback)) {
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("offline_prefetch", R"(
semantics {
@@ -104,7 +105,7 @@ void PrefetchRequestFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
// TODO(jianli): Report UMA.
- callback_.Run(status, data);
+ std::move(callback_).Run(status, data);
}
PrefetchRequestStatus PrefetchRequestFetcher::ParseResponse(
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.h b/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.h
index 4ac86405b6e..543a701802b 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher.h
@@ -21,21 +21,21 @@ namespace offline_pages {
// Asynchronously fetches the offline prefetch related data from the server.
class PrefetchRequestFetcher : public net::URLFetcherDelegate {
public:
- using FinishedCallback = base::Callback<void(PrefetchRequestStatus status,
- const std::string& data)>;
+ using FinishedCallback = base::OnceCallback<void(PrefetchRequestStatus status,
+ const std::string& data)>;
// Creates a fetcher that will sends a GET request to the server.
static std::unique_ptr<PrefetchRequestFetcher> CreateForGet(
const GURL& url,
net::URLRequestContextGetter* request_context_getter,
- const FinishedCallback& callback);
+ FinishedCallback callback);
// Creates a fetcher that will sends a POST request to the server.
static std::unique_ptr<PrefetchRequestFetcher> CreateForPost(
const GURL& url,
const std::string& message,
net::URLRequestContextGetter* request_context_getter,
- const FinishedCallback& callback);
+ FinishedCallback callback);
~PrefetchRequestFetcher() override;
@@ -48,7 +48,7 @@ class PrefetchRequestFetcher : public net::URLFetcherDelegate {
PrefetchRequestFetcher(const GURL& url,
const std::string& message,
net::URLRequestContextGetter* request_context_getter,
- const FinishedCallback& callback);
+ FinishedCallback callback);
PrefetchRequestStatus ParseResponse(const net::URLFetcher* source,
std::string* data);
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher_unittest.cc b/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher_unittest.cc
index 39b5d2183d3..c718b3376b8 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_request_fetcher_unittest.cc
@@ -33,7 +33,7 @@ class PrefetchRequestFetcherTest : public PrefetchRequestTestBase {
private:
PrefetchRequestStatus RunFetcher(
- const base::Callback<void(void)>& respond_callback,
+ base::OnceCallback<void(void)> respond_callback,
std::string* data_received);
};
@@ -41,8 +41,8 @@ PrefetchRequestStatus PrefetchRequestFetcherTest::RunFetcherWithNetError(
net::Error net_error) {
std::string data_received;
PrefetchRequestStatus status =
- RunFetcher(base::Bind(&PrefetchRequestTestBase::RespondWithNetError,
- base::Unretained(this), net_error),
+ RunFetcher(base::BindOnce(&PrefetchRequestTestBase::RespondWithNetError,
+ base::Unretained(this), net_error),
&data_received);
EXPECT_TRUE(data_received.empty());
return status;
@@ -52,8 +52,8 @@ PrefetchRequestStatus PrefetchRequestFetcherTest::RunFetcherWithHttpError(
int http_error) {
std::string data_received;
PrefetchRequestStatus status =
- RunFetcher(base::Bind(&PrefetchRequestTestBase::RespondWithHttpError,
- base::Unretained(this), http_error),
+ RunFetcher(base::BindOnce(&PrefetchRequestTestBase::RespondWithHttpError,
+ base::Unretained(this), http_error),
&data_received);
EXPECT_TRUE(data_received.empty());
return status;
@@ -62,13 +62,13 @@ PrefetchRequestStatus PrefetchRequestFetcherTest::RunFetcherWithHttpError(
PrefetchRequestStatus PrefetchRequestFetcherTest::RunFetcherWithData(
const std::string& response_data,
std::string* data_received) {
- return RunFetcher(base::Bind(&PrefetchRequestTestBase::RespondWithData,
- base::Unretained(this), response_data),
+ return RunFetcher(base::BindOnce(&PrefetchRequestTestBase::RespondWithData,
+ base::Unretained(this), response_data),
data_received);
}
PrefetchRequestStatus PrefetchRequestFetcherTest::RunFetcher(
- const base::Callback<void(void)>& respond_callback,
+ base::OnceCallback<void(void)> respond_callback,
std::string* data_received) {
base::MockCallback<PrefetchRequestFetcher::FinishedCallback> callback;
std::unique_ptr<PrefetchRequestFetcher> fetcher =
@@ -79,7 +79,7 @@ PrefetchRequestStatus PrefetchRequestFetcherTest::RunFetcher(
std::string data;
EXPECT_CALL(callback, Run(_, _))
.WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&data)));
- respond_callback.Run();
+ std::move(respond_callback).Run();
*data_received = data;
return status;
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc b/chromium/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc
index 7f93f98e947..19986533fa3 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc
@@ -45,17 +45,17 @@ class RequestBuilder {
virtual void CreateRequest(
net::URLRequestContextGetter* request_context_getter,
- const PrefetchRequestFinishedCallback& callback) = 0;
+ PrefetchRequestFinishedCallback callback) = 0;
};
class GeneratePageBundleRequestBuilder : public RequestBuilder {
public:
void CreateRequest(net::URLRequestContextGetter* request_context_getter,
- const PrefetchRequestFinishedCallback& callback) override {
+ PrefetchRequestFinishedCallback callback) override {
std::vector<std::string> pages = {kTestURL, kTestURL2};
fetcher_.reset(new GeneratePageBundleRequest(
kTestUserAgent, kTestGCMID, kTestMaxBundleSize, pages, kTestChannel,
- request_context_getter, callback));
+ request_context_getter, std::move(callback)));
}
private:
@@ -65,9 +65,10 @@ class GeneratePageBundleRequestBuilder : public RequestBuilder {
class GetOperationRequestBuilder : public RequestBuilder {
public:
void CreateRequest(net::URLRequestContextGetter* request_context_getter,
- const PrefetchRequestFinishedCallback& callback) override {
+ PrefetchRequestFinishedCallback callback) override {
fetcher_.reset(new GetOperationRequest(kTestOperationName, kTestChannel,
- request_context_getter, callback));
+ request_context_getter,
+ std::move(callback)));
}
private:
@@ -159,8 +160,9 @@ class PrefetchRequestOperationResponseTestBuilder {
virtual ~PrefetchRequestOperationResponseTestBuilder() {}
void CreateRequest(net::URLRequestContextGetter* request_context_getter,
- const PrefetchRequestFinishedCallback& callback) {
- request_builder_->CreateRequest(request_context_getter, callback);
+ PrefetchRequestFinishedCallback callback) {
+ request_builder_->CreateRequest(request_context_getter,
+ std::move(callback));
}
std::string BuildFromAny(const std::string& any_type_url,
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_server_urls.cc b/chromium/components/offline_pages/core/prefetch/prefetch_server_urls.cc
index 6990b8968ae..818ab80c6af 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_server_urls.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_server_urls.cc
@@ -13,6 +13,7 @@ namespace offline_pages {
const char kPrefetchServer[] = "https://offlinepages-pa.googleapis.com/";
const char kPrefetchExperimentHeaderName[] = "X-Offline-Prefetch-Experiment";
+const char kPrefetchOperationHeaderName[] = "X-Offline-Prefetch-Operation";
namespace {
@@ -32,9 +33,7 @@ GURL GetServerURL() {
offline_pages::kPrefetchingOfflinePagesFeature, kOfflinePagesBackend));
// |is_valid| returns false for bad URLs and also for empty URLs.
- return endpoint.is_valid() && endpoint.SchemeIsCryptographic()
- ? endpoint
- : GURL(kPrefetchServer);
+ return endpoint.is_valid() ? endpoint : GURL(kPrefetchServer);
}
GURL GetServerURLForPath(const std::string& url_path) {
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_server_urls.h b/chromium/components/offline_pages/core/prefetch/prefetch_server_urls.h
index 081c37bf378..c42eab8ac59 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_server_urls.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_server_urls.h
@@ -13,6 +13,7 @@ namespace offline_pages {
extern const char kPrefetchServer[];
extern const char kPrefetchExperimentHeaderName[];
+extern const char kPrefetchOperationHeaderName[];
// Returns the URL to send a request to generate page bundle.
GURL GeneratePageBundleRequestURL(version_info::Channel channel);
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_server_urls_unittest.cc b/chromium/components/offline_pages/core/prefetch/prefetch_server_urls_unittest.cc
index 02fc66b9bfe..4ebbb85f7b2 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_server_urls_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_server_urls_unittest.cc
@@ -14,8 +14,6 @@ namespace {
const char kTestOfflinePagesSuggestionsServerEndpoint[] =
"https://test-offlinepages-pa.sandbox.googleapis.com/";
const char kInvalidServerEndpoint[] = "^__^";
-const char kInvalidSchemeServerEndpoint[] =
- "http://test-offlinepages-pa.sandbox.googleapis.com/";
} // namespace
class PrefetchServerURLsTest : public testing::Test {
@@ -56,12 +54,6 @@ TEST_F(PrefetchServerURLsTest, TestVariationsConfig) {
request_url = GeneratePageBundleRequestURL(version_info::Channel::UNKNOWN);
EXPECT_EQ(default_server.host(), request_url.host());
EXPECT_TRUE(request_url.SchemeIsCryptographic());
-
- // Then a valid URL with a non-cryptographic scheme.
- SetTestingServerEndpoint(kInvalidSchemeServerEndpoint);
- request_url = GeneratePageBundleRequestURL(version_info::Channel::UNKNOWN);
- EXPECT_EQ(default_server.host(), request_url.host());
- EXPECT_TRUE(request_url.SchemeIsCryptographic());
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_task_test_base.cc b/chromium/components/offline_pages/core/prefetch/prefetch_task_test_base.cc
index e7911621aa9..233fc568fde 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_task_test_base.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_task_test_base.cc
@@ -30,6 +30,7 @@ void PrefetchTaskTestBase::TearDown() {
TaskTestBase::TearDown();
}
+// static
std::vector<PrefetchItemState> PrefetchTaskTestBase::GetAllStatesExcept(
std::set<PrefetchItemState> states_to_exclude) {
std::vector<PrefetchItemState> selected_states;
@@ -55,9 +56,10 @@ int64_t PrefetchTaskTestBase::InsertPrefetchItemInStateWithOperation(
return item.offline_id;
}
+// static
std::set<PrefetchItem> PrefetchTaskTestBase::FilterByState(
const std::set<PrefetchItem>& items,
- PrefetchItemState state) const {
+ PrefetchItemState state) {
std::set<PrefetchItem> result;
for (const PrefetchItem& item : items) {
if (item.state == state)
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_task_test_base.h b/chromium/components/offline_pages/core/prefetch/prefetch_task_test_base.h
index 860187d4136..e4afb3d7225 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_task_test_base.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_task_test_base.h
@@ -40,17 +40,18 @@ class PrefetchTaskTestBase : public TaskTestBase {
void SetUp() override;
void TearDown() override;
- // Returns all PrefetchItemState values in a vector, filtering our the ones
+ // Returns all PrefetchItemState values in a vector, filtering out the ones
// listed in |states_to_exclude|. The returned list is based off
// |kOrderedPrefetchItemStates| and its order of states is maintained.
- std::vector<PrefetchItemState> GetAllStatesExcept(
+ static std::vector<PrefetchItemState> GetAllStatesExcept(
std::set<PrefetchItemState> states_to_exclude);
int64_t InsertPrefetchItemInStateWithOperation(std::string operation_name,
PrefetchItemState state);
- std::set<PrefetchItem> FilterByState(const std::set<PrefetchItem>& items,
- PrefetchItemState state) const;
+ static std::set<PrefetchItem> FilterByState(
+ const std::set<PrefetchItem>& items,
+ PrefetchItemState state);
TestPrefetchNetworkRequestFactory* prefetch_request_factory() {
return &prefetch_request_factory_;
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_task_test_base_unittest.cc b/chromium/components/offline_pages/core/prefetch/prefetch_task_test_base_unittest.cc
index a63be41a49a..caa28a136e2 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_task_test_base_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_task_test_base_unittest.cc
@@ -7,6 +7,7 @@
#include <algorithm>
#include <set>
+#include "base/stl_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace offline_pages {
@@ -36,10 +37,8 @@ TEST(PrefetchTaskTestBaseTest, StateEnumIsFullyRepresentedInOrderedArray) {
case PrefetchItemState::IMPORTING:
case PrefetchItemState::FINISHED:
case PrefetchItemState::ZOMBIE:
- EXPECT_NE(
- std::find(kOrderedPrefetchItemStates.begin(),
- kOrderedPrefetchItemStates.end(), maybe_valid_state),
- kOrderedPrefetchItemStates.end())
+ EXPECT_TRUE(
+ base::ContainsValue(kOrderedPrefetchItemStates, maybe_valid_state))
<< "Valid state was not found in the array: " << i;
++element_count;
break;
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_types.cc b/chromium/components/offline_pages/core/prefetch/prefetch_types.cc
index 91f7f25f44f..23b8e24ef91 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_types.cc
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_types.cc
@@ -20,7 +20,7 @@ std::string PrefetchEnumToString(PrefetchBackgroundTaskRescheduleType value) {
case PrefetchBackgroundTaskRescheduleType::SUSPEND:
return "SUSPEND";
}
- CHECK(false) << static_cast<int>(value) << " not valid enum value";
+ DCHECK(false) << static_cast<int>(value) << " not valid enum value";
}
std::string PrefetchEnumToString(PrefetchRequestStatus value) {
@@ -36,7 +36,7 @@ std::string PrefetchEnumToString(PrefetchRequestStatus value) {
case PrefetchRequestStatus::COUNT:
return "COUNT";
}
- CHECK(false) << static_cast<int>(value) << " not valid enum value";
+ DCHECK(false) << static_cast<int>(value) << " not valid enum value";
}
std::string PrefetchEnumToString(RenderStatus value) {
@@ -50,7 +50,7 @@ std::string PrefetchEnumToString(RenderStatus value) {
case RenderStatus::EXCEEDED_LIMIT:
return "EXCEEDED_LIMIT";
}
- CHECK(false) << static_cast<int>(value) << " not valid enum value";
+ DCHECK(false) << static_cast<int>(value) << " not valid enum value";
}
std::string PrefetchEnumToString(PrefetchItemState value) {
@@ -78,7 +78,7 @@ std::string PrefetchEnumToString(PrefetchItemState value) {
case PrefetchItemState::ZOMBIE:
return "ZOMBIE";
}
- CHECK(false) << static_cast<int>(value) << " not valid enum value";
+ DCHECK(false) << static_cast<int>(value) << " not valid enum value";
}
std::string PrefetchEnumToString(PrefetchItemErrorCode value) {
@@ -109,6 +109,8 @@ std::string PrefetchEnumToString(PrefetchItemErrorCode value) {
return "STALE_AT_IMPORTING";
case PrefetchItemErrorCode::STALE_AT_UNKNOWN:
return "STALE_AT_UNKNOWN";
+ case PrefetchItemErrorCode::STUCK:
+ return "STUCK";
case PrefetchItemErrorCode::GET_OPERATION_MAX_ATTEMPTS_REACHED:
return "GET_OPERATION_MAX_ATTEMPTS_REACHED";
case PrefetchItemErrorCode::
@@ -123,7 +125,7 @@ std::string PrefetchEnumToString(PrefetchItemErrorCode value) {
case PrefetchItemErrorCode::SUGGESTION_INVALIDATED:
return "SUGGESTION_INVALIDATED";
}
- CHECK(false) << static_cast<int>(value) << " not valid enum value";
+ DCHECK(false) << static_cast<int>(value) << " not valid enum value";
}
} // namespace
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_types.h b/chromium/components/offline_pages/core/prefetch/prefetch_types.h
index 633191289aa..eedb26f0cf5 100644
--- a/chromium/components/offline_pages/core/prefetch/prefetch_types.h
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_types.h
@@ -176,6 +176,9 @@ enum class PrefetchItemErrorCode {
STALE_AT_DOWNLOADING = 1000,
STALE_AT_IMPORTING = 1050,
STALE_AT_UNKNOWN = 1100,
+ // The item was terminated due to not being concluded after being more than 7
+ // days in the pipeline.
+ STUCK = 1150,
// Exceeded maximum retries for get operation request.
GET_OPERATION_MAX_ATTEMPTS_REACHED = 1200,
// Exceeded maximum retries limit for generate page bundle request.
@@ -196,9 +199,9 @@ enum class PrefetchItemErrorCode {
// Callback invoked upon completion of a prefetch request.
using PrefetchRequestFinishedCallback =
- base::Callback<void(PrefetchRequestStatus status,
- const std::string& operation_name,
- const std::vector<RenderPageInfo>& pages)>;
+ base::OnceCallback<void(PrefetchRequestStatus status,
+ const std::string& operation_name,
+ const std::vector<RenderPageInfo>& pages)>;
// Holds information about a suggested URL to be prefetched.
struct PrefetchURL {
@@ -235,10 +238,6 @@ struct PrefetchDownloadResult {
int64_t file_size = 0;
};
-// Callback invoked upon completion of a download.
-using PrefetchDownloadCompletedCallback =
- base::Callback<void(const PrefetchDownloadResult& result)>;
-
// Describes all the info needed to import an archive.
struct PrefetchArchiveInfo {
PrefetchArchiveInfo();
diff --git a/chromium/components/offline_pages/core/prefetch/proto/any.proto b/chromium/components/offline_pages/core/prefetch/proto/any.proto
index c575c70b813..00176f74051 100644
--- a/chromium/components/offline_pages/core/prefetch/proto/any.proto
+++ b/chromium/components/offline_pages/core/prefetch/proto/any.proto
@@ -6,6 +6,7 @@ syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package offline_pages.proto;
+option java_package = "org.chromium.components.offline_pages.core.prefetch.proto";
message Any {
optional string type_url = 1;
diff --git a/chromium/components/offline_pages/core/prefetch/proto/offline_pages.proto b/chromium/components/offline_pages/core/prefetch/proto/offline_pages.proto
index 37580909334..f54cf020b51 100644
--- a/chromium/components/offline_pages/core/prefetch/proto/offline_pages.proto
+++ b/chromium/components/offline_pages/core/prefetch/proto/offline_pages.proto
@@ -6,6 +6,7 @@ syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package offline_pages.proto;
+option java_package = "org.chromium.components.offline_pages.core.prefetch.proto";
import "status.proto";
diff --git a/chromium/components/offline_pages/core/prefetch/proto/operation.proto b/chromium/components/offline_pages/core/prefetch/proto/operation.proto
index db0b1eeedf4..4684aa6c3ce 100644
--- a/chromium/components/offline_pages/core/prefetch/proto/operation.proto
+++ b/chromium/components/offline_pages/core/prefetch/proto/operation.proto
@@ -6,6 +6,7 @@ syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package offline_pages.proto;
+option java_package = "org.chromium.components.offline_pages.core.prefetch.proto";
import "any.proto";
import "status.proto";
diff --git a/chromium/components/offline_pages/core/prefetch/proto/status.proto b/chromium/components/offline_pages/core/prefetch/proto/status.proto
index affec0b3a09..d9146de6515 100644
--- a/chromium/components/offline_pages/core/prefetch/proto/status.proto
+++ b/chromium/components/offline_pages/core/prefetch/proto/status.proto
@@ -6,6 +6,7 @@ syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package offline_pages.proto;
+option java_package = "org.chromium.components.offline_pages.core.prefetch.proto";
import "any.proto";
diff --git a/chromium/components/offline_pages/core/prefetch/sent_get_operation_cleanup_task.cc b/chromium/components/offline_pages/core/prefetch/sent_get_operation_cleanup_task.cc
index 1ad8d5f54c0..56fb4b51768 100644
--- a/chromium/components/offline_pages/core/prefetch/sent_get_operation_cleanup_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/sent_get_operation_cleanup_task.cc
@@ -73,9 +73,6 @@ bool CleanupOperationsSync(
std::unique_ptr<std::set<std::string>> ongoing_operation_names,
int max_attempts,
sql::Connection* db) {
- if (!db)
- return false;
-
sql::Transaction transaction(db);
if (!transaction.Begin())
return false;
@@ -121,7 +118,8 @@ void SentGetOperationCleanupTask::Run() {
base::BindOnce(&CleanupOperationsSync, std::move(ongoing_operation_names),
kMaxGetOperationAttempts),
base::BindOnce(&SentGetOperationCleanupTask::OnFinished,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr()),
+ false);
}
void SentGetOperationCleanupTask::OnFinished(bool success) {
diff --git a/chromium/components/offline_pages/core/prefetch/sent_get_operation_cleanup_task_unittest.cc b/chromium/components/offline_pages/core/prefetch/sent_get_operation_cleanup_task_unittest.cc
index e886d647992..adfbea62f15 100644
--- a/chromium/components/offline_pages/core/prefetch/sent_get_operation_cleanup_task_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/sent_get_operation_cleanup_task_unittest.cc
@@ -34,13 +34,13 @@ class TestingPrefetchNetworkRequestFactory
void MakeGeneratePageBundleRequest(
const std::vector<std::string>& prefetch_urls,
const std::string& gcm_registration_id,
- const PrefetchRequestFinishedCallback& callback) override {}
+ PrefetchRequestFinishedCallback callback) override {}
std::unique_ptr<std::set<std::string>> GetAllUrlsRequested() const override {
return std::unique_ptr<std::set<std::string>>();
}
void MakeGetOperationRequest(
const std::string& operation_name,
- const PrefetchRequestFinishedCallback& callback) override {}
+ PrefetchRequestFinishedCallback callback) override {}
GetOperationRequest* FindGetOperationRequestByName(
const std::string& operation_name) const override {
return nullptr;
diff --git a/chromium/components/offline_pages/core/prefetch/stale_entry_finalizer_task.cc b/chromium/components/offline_pages/core/prefetch/stale_entry_finalizer_task.cc
index ae9ce091899..5c3cabe2cd5 100644
--- a/chromium/components/offline_pages/core/prefetch/stale_entry_finalizer_task.cc
+++ b/chromium/components/offline_pages/core/prefetch/stale_entry_finalizer_task.cc
@@ -135,27 +135,45 @@ bool FinalizeFutureItems(PrefetchItemState state,
}
// If there is a bug in our code, an item might be stuck in the queue waiting
-// on an event that didn't happen. If so, report that item.
-void ReportStuckItems(base::Time now, sql::Connection* db) {
- static constexpr char kSql[] =
- "SELECT state FROM prefetch_items"
- " WHERE creation_time < ?";
+// on an event that didn't happen. If so, finalize that item and report it.
+void ReportAndFinalizeStuckItems(base::Time now, sql::Connection* db) {
const int64_t earliest_valid_creation_time = store_utils::ToDatabaseTime(
now - base::TimeDelta::FromDays(kStuckTimeLimitInDays));
- sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
- statement.BindInt64(0, earliest_valid_creation_time);
-
- while (statement.Step()) {
- base::UmaHistogramSparse("OfflinePages.Prefetching.StuckItemState",
- statement.ColumnInt(0));
+ // Report.
+ {
+ static constexpr char kSql[] =
+ "SELECT state FROM prefetch_items"
+ " WHERE creation_time < ?"
+ " AND state NOT IN (?, ?)"; // (ZOMBIE, FINISHED);
+ sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, earliest_valid_creation_time);
+ statement.BindInt64(1, static_cast<int>(PrefetchItemState::FINISHED));
+ statement.BindInt64(2, static_cast<int>(PrefetchItemState::ZOMBIE));
+
+ while (statement.Step()) {
+ base::UmaHistogramSparse("OfflinePages.Prefetching.StuckItemState",
+ statement.ColumnInt(0));
+ }
+ }
+ // Finalize.
+ {
+ static constexpr char kSql[] =
+ "UPDATE prefetch_items SET state = ?, error_code = ?"
+ " WHERE creation_time < ?"
+ " AND state NOT IN (?, ?)"; // (ZOMBIE, FINISHED)
+ sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+ statement.BindInt64(0, static_cast<int>(PrefetchItemState::FINISHED));
+ statement.BindInt64(1, static_cast<int>(PrefetchItemErrorCode::STUCK));
+ statement.BindInt64(2, earliest_valid_creation_time);
+ statement.BindInt64(3, static_cast<int>(PrefetchItemState::FINISHED));
+ statement.BindInt64(4, static_cast<int>(PrefetchItemState::ZOMBIE));
+
+ statement.Run();
}
}
Result FinalizeStaleEntriesSync(StaleEntryFinalizerTask::NowGetter now_getter,
sql::Connection* db) {
- if (!db)
- return Result::NO_MORE_WORK;
-
sql::Transaction transaction(db);
if (!transaction.Begin())
return Result::NO_MORE_WORK;
@@ -182,7 +200,7 @@ Result FinalizeStaleEntriesSync(StaleEntryFinalizerTask::NowGetter now_getter,
// Items could also be stuck in a non-expirable state due to a bug, report
// them.
- ReportStuckItems(now, db);
+ ReportAndFinalizeStuckItems(now, db);
Result result = Result::MORE_WORK_NEEDED;
if (!MoreWorkInQueue(db))
@@ -211,7 +229,8 @@ void StaleEntryFinalizerTask::Run() {
prefetch_store_->Execute(
base::BindOnce(&FinalizeStaleEntriesSync, now_getter_),
base::BindOnce(&StaleEntryFinalizerTask::OnFinished,
- weak_ptr_factory_.GetWeakPtr()));
+ weak_ptr_factory_.GetWeakPtr()),
+ Result::NO_MORE_WORK);
}
void StaleEntryFinalizerTask::SetNowGetterForTesting(NowGetter now_getter) {
diff --git a/chromium/components/offline_pages/core/prefetch/stale_entry_finalizer_task_unittest.cc b/chromium/components/offline_pages/core/prefetch/stale_entry_finalizer_task_unittest.cc
index 098642546e6..0f6bea4e5e2 100644
--- a/chromium/components/offline_pages/core/prefetch/stale_entry_finalizer_task_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/stale_entry_finalizer_task_unittest.cc
@@ -9,7 +9,7 @@
#include "base/bind.h"
#include "base/strings/string_number_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/offline_pages/core/prefetch/mock_prefetch_item_generator.h"
@@ -88,13 +88,12 @@ TEST_F(StaleEntryFinalizerTaskTest, StoreFailure) {
// Tests that the task works correctly with an empty database.
TEST_F(StaleEntryFinalizerTaskTest, EmptyRun) {
- std::set<PrefetchItem> no_items;
- EXPECT_EQ(0U, store_util()->GetAllItems(&no_items));
+ EXPECT_EQ(std::set<PrefetchItem>(), store_util()->GetAllItems());
// Execute the expiration task.
RunTask(stale_finalizer_task_.get());
EXPECT_EQ(Result::NO_MORE_WORK, stale_finalizer_task_->final_status());
- EXPECT_EQ(0U, store_util()->GetAllItems(&no_items));
+ EXPECT_EQ(std::set<PrefetchItem>(), store_util()->GetAllItems());
}
// Verifies that expired and non-expired items from all expirable states are
@@ -133,9 +132,7 @@ TEST_F(StaleEntryFinalizerTaskTest, HandlesFreshnessTimesCorrectly) {
b1_item1_fresh, b1_item2_stale, b2_item1_fresh, b2_item2_stale,
b2_item3_fresh, b2_item4_stale, b2_item5_fresh, b2_item6_stale,
b3_item1_fresh, b3_item2_stale, b3_item3_fresh, b3_item4_stale};
- std::set<PrefetchItem> all_inserted_items;
- EXPECT_EQ(12U, store_util()->GetAllItems(&all_inserted_items));
- EXPECT_EQ(initial_items, all_inserted_items);
+ EXPECT_EQ(initial_items, store_util()->GetAllItems());
// Execute the expiration task.
RunTask(stale_finalizer_task_.get());
@@ -167,10 +164,7 @@ TEST_F(StaleEntryFinalizerTaskTest, HandlesFreshnessTimesCorrectly) {
b1_item1_fresh, b1_item2_finished, b2_item1_fresh, b2_item2_finished,
b2_item3_fresh, b2_item4_finished, b2_item5_fresh, b2_item6_finished,
b3_item1_fresh, b3_item2_finished, b3_item3_fresh, b3_item4_finished};
- EXPECT_EQ(12U, expected_final_items.size());
- std::set<PrefetchItem> all_items_post_expiration;
- EXPECT_EQ(12U, store_util()->GetAllItems(&all_items_post_expiration));
- EXPECT_EQ(expected_final_items, all_items_post_expiration);
+ EXPECT_EQ(expected_final_items, store_util()->GetAllItems());
}
// Checks that items from all states are handled properly by the task when all
@@ -189,8 +183,8 @@ TEST_F(StaleEntryFinalizerTaskTest, HandlesStalesInAllStatesCorrectly) {
EXPECT_EQ(Result::MORE_WORK_NEEDED, stale_finalizer_task_->final_status());
// Checks item counts for states expected to still exist.
- std::set<PrefetchItem> post_items;
- EXPECT_EQ(11U, store_util()->GetAllItems(&post_items));
+ std::set<PrefetchItem> post_items = store_util()->GetAllItems();
+ EXPECT_EQ(11U, post_items.size());
EXPECT_EQ(
1U,
Filter(post_items, PrefetchItemState::SENT_GENERATE_PAGE_BUNDLE).size());
@@ -274,9 +268,7 @@ TEST_F(StaleEntryFinalizerTaskTest, HandlesClockSetBackwardsCorrectly) {
b2_item3_recent, b2_item4_future, b2_item5_recent, b2_item6_future,
b3_item1_recent, b3_item2_future, b3_item3_recent, b3_item4_future,
b4_item1_future};
- std::set<PrefetchItem> all_inserted_items;
- EXPECT_EQ(13U, store_util()->GetAllItems(&all_inserted_items));
- EXPECT_EQ(initial_items, all_inserted_items);
+ EXPECT_EQ(initial_items, store_util()->GetAllItems());
// Execute the expiration task.
RunTask(stale_finalizer_task_.get());
@@ -318,10 +310,7 @@ TEST_F(StaleEntryFinalizerTaskTest, HandlesClockSetBackwardsCorrectly) {
b2_item3_recent, b2_item4_finished, b2_item5_recent, b2_item6_finished,
b3_item1_recent, b3_item2_finished, b3_item3_recent, b3_item4_finished,
b4_item1_future};
- EXPECT_EQ(13U, expected_final_items.size());
- std::set<PrefetchItem> all_items_post_expiration;
- EXPECT_EQ(13U, store_util()->GetAllItems(&all_items_post_expiration));
- EXPECT_EQ(expected_final_items, all_items_post_expiration);
+ EXPECT_EQ(expected_final_items, store_util()->GetAllItems());
}
// Checks that items from all states are handled properly by the task when all
@@ -339,8 +328,8 @@ TEST_F(StaleEntryFinalizerTaskTest,
EXPECT_EQ(Result::MORE_WORK_NEEDED, stale_finalizer_task_->final_status());
// Checks item counts for states expected to still exist.
- std::set<PrefetchItem> post_items;
- EXPECT_EQ(11U, store_util()->GetAllItems(&post_items));
+ std::set<PrefetchItem> post_items = store_util()->GetAllItems();
+ EXPECT_EQ(11U, post_items.size());
EXPECT_EQ(
1U,
Filter(post_items, PrefetchItemState::SENT_GENERATE_PAGE_BUNDLE).size());
@@ -351,8 +340,7 @@ TEST_F(StaleEntryFinalizerTaskTest,
EXPECT_EQ(1U, Filter(post_items, PrefetchItemState::ZOMBIE).size());
}
-// Verifies that expired and non-expired items from all expirable states are
-// properly handled.
+// Verifies that only stale, live items are transitioned to 'FINISHED'.
TEST_F(StaleEntryFinalizerTaskTest, HandlesStuckItemsCorrectly) {
base::HistogramTester histogram_tester;
// Insert fresh and stale items for all expirable states from all buckets.
@@ -360,17 +348,27 @@ TEST_F(StaleEntryFinalizerTaskTest, HandlesStuckItemsCorrectly) {
CreateAndInsertItem(PrefetchItemState::SENT_GENERATE_PAGE_BUNDLE, 1);
PrefetchItem item2_stuck =
CreateAndInsertItem(PrefetchItemState::SENT_GENERATE_PAGE_BUNDLE, -170);
+ PrefetchItem item3_finished =
+ CreateAndInsertItem(PrefetchItemState::FINISHED, -170);
+ PrefetchItem item4_zombie =
+ CreateAndInsertItem(PrefetchItemState::ZOMBIE, -170);
// Check inserted initial items.
- std::set<PrefetchItem> initial_items = {item1_recent, item2_stuck};
- std::set<PrefetchItem> all_inserted_items;
- EXPECT_EQ(2U, store_util()->GetAllItems(&all_inserted_items));
- EXPECT_EQ(initial_items, all_inserted_items);
+ std::set<PrefetchItem> initial_items = {item1_recent, item2_stuck,
+ item3_finished, item4_zombie};
+ EXPECT_EQ(initial_items, store_util()->GetAllItems());
// Execute the expiration task.
RunTask(stale_finalizer_task_.get());
EXPECT_EQ(Result::MORE_WORK_NEEDED, stale_finalizer_task_->final_status());
+ // Only the stuck item is changed.
+ PrefetchItem want_stuck_item = item2_stuck;
+ want_stuck_item.state = PrefetchItemState::FINISHED;
+ want_stuck_item.error_code = PrefetchItemErrorCode::STUCK;
+ std::set<PrefetchItem> final_items{item1_recent, want_stuck_item,
+ item3_finished, item4_zombie};
+ EXPECT_EQ(final_items, store_util()->GetAllItems());
// Check that the proper UMA was reported for the stale item, but not the
// fresh item, so there should be exactly one sample.
histogram_tester.ExpectUniqueSample(
diff --git a/chromium/components/offline_pages/core/prefetch/store/prefetch_store.cc b/chromium/components/offline_pages/core/prefetch/store/prefetch_store.cc
index 82507579d91..d72a7e057c2 100644
--- a/chromium/components/offline_pages/core/prefetch/store/prefetch_store.cc
+++ b/chromium/components/offline_pages/core/prefetch/store/prefetch_store.cc
@@ -23,10 +23,6 @@ namespace {
const char kPrefetchStoreFileName[] = "PrefetchStore.db";
-using InitializeCallback =
- base::Callback<void(InitializationStatus,
- std::unique_ptr<sql::Connection>)>;
-
bool PrepareDirectory(const base::FilePath& path) {
base::File::Error error = base::File::FILE_OK;
if (!base::DirectoryExists(path.DirName())) {
diff --git a/chromium/components/offline_pages/core/prefetch/store/prefetch_store.h b/chromium/components/offline_pages/core/prefetch/store/prefetch_store.h
index 355b1e3e6c2..e1e4e462272 100644
--- a/chromium/components/offline_pages/core/prefetch/store/prefetch_store.h
+++ b/chromium/components/offline_pages/core/prefetch/store/prefetch_store.h
@@ -71,17 +71,19 @@ class PrefetchStore {
// its result back to calling thread through |result_callback|.
// Calling |Execute| when store is NOT_INITIALIZED will cause the store
// initialization to start.
- // Store initialization status needs to be SUCCESS for test task to run, or
- // FAILURE, in which case the |db| pointer passed to |run_callback| will be
- // null and such case should be gracefully handled.
+ // Store initialization status needs to be SUCCESS for run_callback to run.
+ // If initialization fails, |result_callback| is invoked with |default_value|.
template <typename T>
- void Execute(RunCallback<T> run_callback, ResultCallback<T> result_callback) {
+ void Execute(RunCallback<T> run_callback,
+ ResultCallback<T> result_callback,
+ T default_value) {
CHECK_NE(initialization_status_, InitializationStatus::INITIALIZING);
if (initialization_status_ == InitializationStatus::NOT_INITIALIZED) {
Initialize(base::BindOnce(
&PrefetchStore::Execute<T>, weak_ptr_factory_.GetWeakPtr(),
- std::move(run_callback), std::move(result_callback)));
+ std::move(run_callback), std::move(result_callback),
+ std::move(default_value)));
return;
}
@@ -95,12 +97,18 @@ class PrefetchStore {
sql::Connection* db =
initialization_status_ == InitializationStatus::SUCCESS ? db_.get()
: nullptr;
- base::PostTaskAndReplyWithResult(
- blocking_task_runner_.get(), FROM_HERE,
- base::BindOnce(std::move(run_callback), db),
- base::BindOnce(&PrefetchStore::RescheduleClosing<T>,
- weak_ptr_factory_.GetWeakPtr(),
- std::move(result_callback)));
+ if (!db) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(result_callback), std::move(default_value)));
+ } else {
+ base::PostTaskAndReplyWithResult(
+ blocking_task_runner_.get(), FROM_HERE,
+ base::BindOnce(std::move(run_callback), db),
+ base::BindOnce(&PrefetchStore::RescheduleClosing<T>,
+ weak_ptr_factory_.GetWeakPtr(),
+ std::move(result_callback)));
+ }
}
// Gets the initialization status of the store.
diff --git a/chromium/components/offline_pages/core/prefetch/store/prefetch_store_schema_unittest.cc b/chromium/components/offline_pages/core/prefetch/store/prefetch_store_schema_unittest.cc
index 3cbe717bb03..3307cec6f91 100644
--- a/chromium/components/offline_pages/core/prefetch/store/prefetch_store_schema_unittest.cc
+++ b/chromium/components/offline_pages/core/prefetch/store/prefetch_store_schema_unittest.cc
@@ -152,7 +152,7 @@ TEST_F(PrefetchStoreSchemaTest, TestMissingTablesAreRecreated) {
void CreateVersion1TablesWithSampleRows(sql::Connection* db) {
// Create version 1 tables.
- const char kV0ItemsTableCreationSql[] =
+ static const char kV0ItemsTableCreationSql[] =
"CREATE TABLE prefetch_items"
"(offline_id INTEGER PRIMARY KEY NOT NULL,"
" state INTEGER NOT NULL DEFAULT 0,"
@@ -175,7 +175,7 @@ void CreateVersion1TablesWithSampleRows(sql::Connection* db) {
" file_path VARCHAR NOT NULL DEFAULT ''"
")";
EXPECT_TRUE(db->Execute(kV0ItemsTableCreationSql));
- const char kV0QuotaTableCreationSql[] =
+ static const char kV0QuotaTableCreationSql[] =
"CREATE TABLE prefetch_downloader_quota"
"(quota_id INTEGER PRIMARY KEY NOT NULL DEFAULT 1,"
" update_time INTEGER NOT NULL,"
@@ -183,7 +183,7 @@ void CreateVersion1TablesWithSampleRows(sql::Connection* db) {
EXPECT_TRUE(db->Execute(kV0QuotaTableCreationSql));
// Insert one row with artificial values into the items table.
- const char kV0ItemInsertSql[] =
+ static const char kV0ItemInsertSql[] =
"INSERT INTO prefetch_items"
" (offline_id, state, generate_bundle_attempts, get_operation_attempts,"
" download_initiation_attempts, archive_body_length, creation_time,"
@@ -201,7 +201,7 @@ void CreateVersion1TablesWithSampleRows(sql::Connection* db) {
EXPECT_TRUE(insertStatement1.Run());
// Insert one row with artificial values into the quota table.
- const char kV0QuotaInsertSql[] =
+ static const char kV0QuotaInsertSql[] =
"INSERT INTO prefetch_downloader_quota"
" (quota_id, update_time, available_quota)"
" VALUES (?, ?, ?)";
@@ -215,7 +215,7 @@ void CreateVersion1TablesWithSampleRows(sql::Connection* db) {
void CheckSampleRowsAtCurrentVersion(sql::Connection* db) {
// Checks the previously inserted item row was migrated correctly.
- const char kV0ItemSelectSql[] =
+ static const char kV0ItemSelectSql[] =
"SELECT "
" offline_id, state, generate_bundle_attempts, get_operation_attempts,"
" download_initiation_attempts, archive_body_length, creation_time,"
@@ -237,7 +237,7 @@ void CheckSampleRowsAtCurrentVersion(sql::Connection* db) {
EXPECT_FALSE(selectStatement1.Step());
// Checks the previously inserted quota row was migrated correctly.
- const char kV0QuotaSelectSql[] =
+ static const char kV0QuotaSelectSql[] =
"SELECT quota_id, update_time, available_quota"
" FROM prefetch_downloader_quota";
sql::Statement selectStatement2(db->GetUniqueStatement(kV0QuotaSelectSql));
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 18d679c0eba..14073accc18 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
@@ -133,18 +133,18 @@ std::unique_ptr<PrefetchItem> GetPrefetchItemSync(int64_t offline_id,
return item;
}
-std::size_t GetAllItemsSync(std::set<PrefetchItem>* items,
- sql::Connection* db) {
+std::set<PrefetchItem> GetAllItemsSync(sql::Connection* db) {
// Not starting transaction as this is a single read.
+ std::set<PrefetchItem> items;
static const std::string kSql =
base::StringPrintf("SELECT %s FROM prefetch_items", kSqlAllColumnNames);
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql.c_str()));
while (statement.Step()) {
PrefetchItem loaded_item;
PopulatePrefetchItem(statement, &loaded_item);
- items->insert(loaded_item);
+ items.insert(loaded_item);
}
- return items->size();
+ return items;
}
int UpdateItemsStateSync(const std::string& name_space,
@@ -217,7 +217,7 @@ bool PrefetchStoreTestUtil::InsertPrefetchItem(const PrefetchItem& item) {
bool success = false;
store_->Execute(
base::BindOnce(&InsertPrefetchItemSync, item),
- base::BindOnce([](bool* alias, bool s) { *alias = s; }, &success));
+ base::BindOnce([](bool* alias, bool s) { *alias = s; }, &success), false);
RunUntilIdle();
return success;
}
@@ -226,7 +226,8 @@ int PrefetchStoreTestUtil::CountPrefetchItems() {
int count = 0;
store_->Execute(
base::BindOnce(&CountPrefetchItemsSync),
- base::BindOnce([](int* alias, int result) { *alias = result; }, &count));
+ base::BindOnce([](int* alias, int result) { *alias = result; }, &count),
+ kPrefetchStoreCommandFailed);
RunUntilIdle();
return count;
}
@@ -240,20 +241,31 @@ std::unique_ptr<PrefetchItem> PrefetchStoreTestUtil::GetPrefetchItem(
std::unique_ptr<PrefetchItem> result) {
*alias = std::move(result);
},
- &item));
+ &item),
+ std::unique_ptr<PrefetchItem>());
RunUntilIdle();
return item;
}
std::size_t PrefetchStoreTestUtil::GetAllItems(
std::set<PrefetchItem>* all_items) {
- std::size_t items_count;
- store_->Execute(base::BindOnce(&GetAllItemsSync, all_items),
- base::BindOnce([](std::size_t* alias,
- std::size_t result) { *alias = result; },
- &items_count));
+ DCHECK(all_items->empty());
+ *all_items = GetAllItems();
+ return all_items->size();
+}
+
+std::set<PrefetchItem> PrefetchStoreTestUtil::GetAllItems() {
+ std::set<PrefetchItem> items;
+ store_->Execute(
+ base::BindOnce(&GetAllItemsSync),
+ base::BindOnce(
+ [](std::set<PrefetchItem>* alias, std::set<PrefetchItem> result) {
+ *alias = std::move(result);
+ },
+ &items),
+ std::set<PrefetchItem>());
RunUntilIdle();
- return items_count;
+ return items;
}
std::string PrefetchStoreTestUtil::ToString() {
@@ -274,7 +286,8 @@ int PrefetchStoreTestUtil::ZombifyPrefetchItems(const std::string& name_space,
store_->Execute(
base::BindOnce(&UpdateItemsStateSync, name_space, url.spec(),
PrefetchItemState::ZOMBIE),
- base::BindOnce([](int* alias, int result) { *alias = result; }, &count));
+ base::BindOnce([](int* alias, int result) { *alias = result; }, &count),
+ kPrefetchStoreCommandFailed);
RunUntilIdle();
return count;
}
@@ -289,7 +302,8 @@ int PrefetchStoreTestUtil::LastCommandChangeCount() {
base::BindOnce([](sql::Connection* connection) {
return connection->GetLastChangeCount();
}),
- base::BindOnce([](int* result, int count) { *result = count; }, &count));
+ base::BindOnce([](int* result, int count) { *result = count; }, &count),
+ 0);
RunUntilIdle();
return count;
}
@@ -299,7 +313,8 @@ int64_t PrefetchStoreTestUtil::GetPrefetchQuota() {
store_->Execute(
base::BindOnce(&GetPrefetchQuotaSync, clock()),
base::BindOnce([](int64_t* result, int64_t quota) { *result = quota; },
- &result));
+ &result),
+ int64_t());
RunUntilIdle();
return result;
}
@@ -309,7 +324,8 @@ bool PrefetchStoreTestUtil::SetPrefetchQuota(int64_t available_quota) {
store_->Execute(
base::BindOnce(&SetPrefetchQuotaSync, available_quota, clock()),
base::BindOnce([](bool* result, bool success) { *result = success; },
- &result));
+ &result),
+ false);
RunUntilIdle();
return result;
}
diff --git a/chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.h b/chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.h
index 1536662daf6..2fb2ef26161 100644
--- a/chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.h
+++ b/chromium/components/offline_pages/core/prefetch/store/prefetch_store_test_util.h
@@ -60,6 +60,7 @@ class PrefetchStoreTestUtil {
// Gets all existing items from the store, inserting them into |all_items|.
// Returns the number of items found.
std::size_t GetAllItems(std::set<PrefetchItem>* all_items);
+ std::set<PrefetchItem> GetAllItems();
// Prints a representation of the prefetch store contents.
std::string ToString();
diff --git a/chromium/components/offline_pages/core/prefetch/test_download_service.cc b/chromium/components/offline_pages/core/prefetch/test_download_service.cc
index 9b6d5fc02de..df0ab3cfb4b 100644
--- a/chromium/components/offline_pages/core/prefetch/test_download_service.cc
+++ b/chromium/components/offline_pages/core/prefetch/test_download_service.cc
@@ -78,7 +78,7 @@ void TestDownloadService::SetTestFileData(const std::string& data) {
void TestDownloadService::OnStartScheduledTask(
download::DownloadTaskType task_type,
- const download::TaskFinishedCallback& callback) {
+ download::TaskFinishedCallback callback) {
NOTIMPLEMENTED();
}
bool TestDownloadService::OnStopScheduledTask(
diff --git a/chromium/components/offline_pages/core/prefetch/test_download_service.h b/chromium/components/offline_pages/core/prefetch/test_download_service.h
index 445d269b658..d5ef24dcfad 100644
--- a/chromium/components/offline_pages/core/prefetch/test_download_service.h
+++ b/chromium/components/offline_pages/core/prefetch/test_download_service.h
@@ -6,6 +6,7 @@
#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_TEST_DOWNLOAD_SERVICE_H_
#include <list>
+#include <string>
#include "base/files/scoped_temp_dir.h"
#include "base/optional.h"
@@ -19,14 +20,13 @@ namespace offline_pages {
// Implementation of DownloadService used for testing.
class TestDownloadService : public download::DownloadService {
public:
- explicit TestDownloadService();
+ TestDownloadService();
~TestDownloadService() override;
// DownloadService implementation.
const download::ServiceConfig& GetConfig() override;
- void OnStartScheduledTask(
- download::DownloadTaskType task_type,
- const download::TaskFinishedCallback& callback) override;
+ void OnStartScheduledTask(download::DownloadTaskType task_type,
+ download::TaskFinishedCallback callback) override;
bool OnStopScheduledTask(download::DownloadTaskType task_type) override;
DownloadService::ServiceStatus GetStatus() override;
void StartDownload(const download::DownloadParams& download_params) override;
diff --git a/chromium/components/offline_pages/core/prefetch/test_prefetch_downloader.cc b/chromium/components/offline_pages/core/prefetch/test_prefetch_downloader.cc
index 8e1025cede5..2216c3049fd 100644
--- a/chromium/components/offline_pages/core/prefetch/test_prefetch_downloader.cc
+++ b/chromium/components/offline_pages/core/prefetch/test_prefetch_downloader.cc
@@ -18,10 +18,13 @@ bool TestPrefetchDownloader::IsDownloadServiceUnavailable() const {
void TestPrefetchDownloader::CleanupDownloadsWhenReady() {}
-void TestPrefetchDownloader::StartDownload(
- const std::string& download_id,
- const std::string& download_location) {
- requested_downloads_[download_id] = download_location;
+void TestPrefetchDownloader::StartDownload(const std::string& download_id,
+ const std::string& download_location,
+ const std::string& operation_name) {
+ Request request;
+ request.download_location = download_location;
+ request.operation_name = operation_name;
+ requested_downloads_[download_id] = request;
}
void TestPrefetchDownloader::OnDownloadServiceReady(
diff --git a/chromium/components/offline_pages/core/prefetch/test_prefetch_downloader.h b/chromium/components/offline_pages/core/prefetch/test_prefetch_downloader.h
index 85ba5981d7c..48d863b9005 100644
--- a/chromium/components/offline_pages/core/prefetch/test_prefetch_downloader.h
+++ b/chromium/components/offline_pages/core/prefetch/test_prefetch_downloader.h
@@ -16,6 +16,11 @@ namespace offline_pages {
// Mock implementation of prefetch downloader that is suitable for testing.
class TestPrefetchDownloader : public PrefetchDownloader {
public:
+ struct Request {
+ std::string download_location;
+ std::string operation_name;
+ };
+ using RequestMap = std::map<std::string, Request>;
TestPrefetchDownloader();
~TestPrefetchDownloader() override;
@@ -23,7 +28,8 @@ class TestPrefetchDownloader : public PrefetchDownloader {
bool IsDownloadServiceUnavailable() const override;
void CleanupDownloadsWhenReady() override;
void StartDownload(const std::string& download_id,
- const std::string& download_location) override;
+ const std::string& download_location,
+ const std::string& operation_name) override;
void OnDownloadServiceReady(
const std::set<std::string>& outstanding_download_ids,
const std::map<std::string, std::pair<base::FilePath, int64_t>>&
@@ -36,12 +42,10 @@ class TestPrefetchDownloader : public PrefetchDownloader {
void Reset();
- const std::map<std::string, std::string>& requested_downloads() const {
- return requested_downloads_;
- }
+ const RequestMap& requested_downloads() const { return requested_downloads_; }
private:
- std::map<std::string, std::string> requested_downloads_;
+ RequestMap requested_downloads_;
};
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/renovations/page_renovator.cc b/chromium/components/offline_pages/core/renovations/page_renovator.cc
index 3c8e5df1934..3b81edb2219 100644
--- a/chromium/components/offline_pages/core/renovations/page_renovator.cc
+++ b/chromium/components/offline_pages/core/renovations/page_renovator.cc
@@ -24,16 +24,16 @@ PageRenovator::PageRenovator(PageRenovationLoader* renovation_loader,
PageRenovator::~PageRenovator() {}
-void PageRenovator::RunRenovations(base::Closure callback) {
+void PageRenovator::RunRenovations(CompletionCallback callback) {
// Prepare callback and inject combined script.
- base::RepeatingCallback<void(const base::Value&)> cb = base::Bind(
- [](base::Closure callback, const base::Value&) {
+ base::OnceCallback<void(const base::Value&)> cb = base::BindOnce(
+ [](base::OnceClosure callback, const base::Value&) {
if (callback)
- callback.Run();
+ std::move(callback).Run();
},
std::move(callback));
- script_injector_->Inject(script_, cb);
+ script_injector_->Inject(script_, std::move(cb));
}
void PageRenovator::PrepareScript(const GURL& url) {
diff --git a/chromium/components/offline_pages/core/renovations/page_renovator.h b/chromium/components/offline_pages/core/renovations/page_renovator.h
index 62e02c50291..f26137a3f46 100644
--- a/chromium/components/offline_pages/core/renovations/page_renovator.h
+++ b/chromium/components/offline_pages/core/renovations/page_renovator.h
@@ -24,7 +24,7 @@ namespace offline_pages {
// be called.
class PageRenovator {
public:
- using CompletionCallback = base::RepeatingClosure;
+ using CompletionCallback = base::OnceClosure;
// |renovation_loader| should be owned by the user and is expected to
// outlive this PageRenovator instance.
diff --git a/chromium/components/offline_pages/core/renovations/page_renovator_unittest.cc b/chromium/components/offline_pages/core/renovations/page_renovator_unittest.cc
index 000adcc16e8..31a6f68f870 100644
--- a/chromium/components/offline_pages/core/renovations/page_renovator_unittest.cc
+++ b/chromium/components/offline_pages/core/renovations/page_renovator_unittest.cc
@@ -55,7 +55,7 @@ PageRenovatorTest::FakeScriptInjector::FakeScriptInjector(
void PageRenovatorTest::FakeScriptInjector::Inject(base::string16 script,
ResultCallback callback) {
if (callback)
- callback.Run(base::Value());
+ std::move(callback).Run(base::Value());
fixture_->script_injector_inject_called_ = true;
}
diff --git a/chromium/components/offline_pages/core/renovations/script_injector.h b/chromium/components/offline_pages/core/renovations/script_injector.h
index aa80cf063d6..a90224c5379 100644
--- a/chromium/components/offline_pages/core/renovations/script_injector.h
+++ b/chromium/components/offline_pages/core/renovations/script_injector.h
@@ -19,7 +19,7 @@ namespace offline_pages {
// context, then calls the ResultCallback with the expression value.
class ScriptInjector {
public:
- using ResultCallback = base::Callback<void(const base::Value&)>;
+ using ResultCallback = base::OnceCallback<void(const base::Value&)>;
virtual ~ScriptInjector() = default;
diff --git a/chromium/components/offline_pages/core/snapshot_controller.cc b/chromium/components/offline_pages/core/snapshot_controller.cc
index 27bb798c8e1..c1cb13d29a2 100644
--- a/chromium/components/offline_pages/core/snapshot_controller.cc
+++ b/chromium/components/offline_pages/core/snapshot_controller.cc
@@ -112,8 +112,8 @@ void SnapshotController::RenovationsCompleted() {
if (renovations_enabled_) {
task_runner_->PostDelayedTask(
FROM_HERE,
- base::Bind(&SnapshotController::MaybeStartSnapshotThenStop,
- weak_ptr_factory_.GetWeakPtr()),
+ base::BindOnce(&SnapshotController::MaybeStartSnapshotThenStop,
+ weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(
delay_after_renovations_completed_ms_));
}
@@ -125,9 +125,9 @@ void SnapshotController::DocumentAvailableInMainFrame() {
// Post a delayed task to snapshot.
task_runner_->PostDelayedTask(
FROM_HERE,
- base::Bind(&SnapshotController::MaybeStartSnapshot,
- weak_ptr_factory_.GetWeakPtr(),
- PageQuality::FAIR_AND_IMPROVING),
+ base::BindOnce(&SnapshotController::MaybeStartSnapshot,
+ weak_ptr_factory_.GetWeakPtr(),
+ PageQuality::FAIR_AND_IMPROVING),
base::TimeDelta::FromMilliseconds(delay_after_document_available_ms_));
}
}
@@ -141,8 +141,8 @@ void SnapshotController::DocumentOnLoadCompletedInMainFrame() {
// Post a delayed task to snapshot and then stop this controller.
task_runner_->PostDelayedTask(
FROM_HERE,
- base::Bind(&SnapshotController::MaybeStartSnapshotThenStop,
- weak_ptr_factory_.GetWeakPtr()),
+ base::BindOnce(&SnapshotController::MaybeStartSnapshotThenStop,
+ weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(
delay_after_document_on_load_completed_ms_));
}
diff --git a/chromium/components/offline_pages/core/stub_offline_page_model.cc b/chromium/components/offline_pages/core/stub_offline_page_model.cc
index d217c07d376..429be629694 100644
--- a/chromium/components/offline_pages/core/stub_offline_page_model.cc
+++ b/chromium/components/offline_pages/core/stub_offline_page_model.cc
@@ -22,31 +22,31 @@ void StubOfflinePageModel::SavePage(
const SavePageParams& save_page_params,
std::unique_ptr<OfflinePageArchiver> archiver,
content::WebContents* web_contents,
- const SavePageCallback& callback) {}
+ SavePageCallback callback) {}
void StubOfflinePageModel::AddPage(const OfflinePageItem& page,
- const AddPageCallback& callback) {}
+ AddPageCallback callback) {}
void StubOfflinePageModel::MarkPageAccessed(int64_t offline_id) {}
void StubOfflinePageModel::DeletePagesByOfflineId(
const std::vector<int64_t>& offline_ids,
- const DeletePageCallback& callback) {}
+ DeletePageCallback callback) {}
void StubOfflinePageModel::DeletePagesByClientIds(
const std::vector<ClientId>& client_ids,
- const DeletePageCallback& callback) {}
+ DeletePageCallback callback) {}
void StubOfflinePageModel::DeletePagesByClientIdsAndOrigin(
const std::vector<ClientId>& client_ids,
const std::string& origin,
- const DeletePageCallback& callback) {}
+ DeletePageCallback callback) {}
void StubOfflinePageModel::GetPagesByClientIds(
const std::vector<ClientId>& client_ids,
MultipleOfflinePageItemCallback callback) {}
void StubOfflinePageModel::DeleteCachedPagesByURLPredicate(
const UrlPredicate& predicate,
- const DeletePageCallback& callback) {}
+ DeletePageCallback callback) {}
void StubOfflinePageModel::GetAllPages(
MultipleOfflinePageItemCallback callback) {}
void StubOfflinePageModel::GetOfflineIdsForClientId(
const ClientId& client_id,
- const MultipleOfflineIdCallback& callback) {}
+ MultipleOfflineIdCallback callback) {}
void StubOfflinePageModel::GetPageByOfflineId(
int64_t offline_id,
SingleOfflinePageItemCallback callback) {}
diff --git a/chromium/components/offline_pages/core/stub_offline_page_model.h b/chromium/components/offline_pages/core/stub_offline_page_model.h
index c99f6ae12ff..33a0950fce6 100644
--- a/chromium/components/offline_pages/core/stub_offline_page_model.h
+++ b/chromium/components/offline_pages/core/stub_offline_page_model.h
@@ -29,27 +29,23 @@ class StubOfflinePageModel : public OfflinePageModel {
void SavePage(const SavePageParams& save_page_params,
std::unique_ptr<OfflinePageArchiver> archiver,
content::WebContents* web_contents,
- const SavePageCallback& callback) override;
- void AddPage(const OfflinePageItem& page,
- const AddPageCallback& callback) override;
+ SavePageCallback callback) override;
+ void AddPage(const OfflinePageItem& page, AddPageCallback callback) override;
void MarkPageAccessed(int64_t offline_id) override;
void DeletePagesByOfflineId(const std::vector<int64_t>& offline_ids,
- const DeletePageCallback& callback) override;
+ DeletePageCallback callback) override;
void DeletePagesByClientIds(const std::vector<ClientId>& client_ids,
- const DeletePageCallback& callback) override;
- void DeletePagesByClientIdsAndOrigin(
- const std::vector<ClientId>& client_ids,
- const std::string& origin,
- const DeletePageCallback& callback) override;
+ DeletePageCallback callback) override;
+ void DeletePagesByClientIdsAndOrigin(const std::vector<ClientId>& client_ids,
+ const std::string& origin,
+ DeletePageCallback callback) override;
void GetPagesByClientIds(const std::vector<ClientId>& client_ids,
MultipleOfflinePageItemCallback callback) override;
- void DeleteCachedPagesByURLPredicate(
- const UrlPredicate& predicate,
- const DeletePageCallback& callback) override;
+ void DeleteCachedPagesByURLPredicate(const UrlPredicate& predicate,
+ DeletePageCallback callback) override;
void GetAllPages(MultipleOfflinePageItemCallback callback) override;
- void GetOfflineIdsForClientId(
- const ClientId& client_id,
- const MultipleOfflineIdCallback& callback) override;
+ void GetOfflineIdsForClientId(const ClientId& client_id,
+ MultipleOfflineIdCallback callback) override;
void GetPageByOfflineId(int64_t offline_id,
SingleOfflinePageItemCallback callback) override;
void GetPageByGuid(const std::string& guid,
diff --git a/chromium/components/offline_pages/core/task.cc b/chromium/components/offline_pages/core/task.cc
index 1bf7a7d5503..32c8089f2ed 100644
--- a/chromium/components/offline_pages/core/task.cc
+++ b/chromium/components/offline_pages/core/task.cc
@@ -15,13 +15,14 @@ Task::~Task() {}
void Task::SetTaskCompletionCallbackForTesting(
scoped_refptr<base::SingleThreadTaskRunner> task_completion_runner,
- const TaskCompletionCallback& task_completion_callback) {
- SetTaskCompletionCallback(task_completion_runner, task_completion_callback);
+ TaskCompletionCallback task_completion_callback) {
+ SetTaskCompletionCallback(task_completion_runner,
+ std::move(task_completion_callback));
}
void Task::SetTaskCompletionCallback(
scoped_refptr<base::SingleThreadTaskRunner> task_completion_runner,
- const TaskCompletionCallback& task_completion_callback) {
+ TaskCompletionCallback task_completion_callback) {
// Attempts to enforce that SetTaskCompletionCallback is at most called once
// and enforces that reasonable values are set once that happens.
DCHECK(!task_completion_runner_);
@@ -29,7 +30,7 @@ void Task::SetTaskCompletionCallback(
DCHECK(task_completion_callback_.is_null());
DCHECK(!task_completion_callback.is_null());
task_completion_runner_ = task_completion_runner;
- task_completion_callback_ = task_completion_callback;
+ task_completion_callback_ = std::move(task_completion_callback);
}
void Task::TaskComplete() {
@@ -37,7 +38,7 @@ void Task::TaskComplete() {
return;
task_completion_runner_->PostTask(
- FROM_HERE, base::Bind(task_completion_callback_, this));
+ FROM_HERE, base::BindOnce(std::move(task_completion_callback_), this));
}
} // namespace offline_pages
diff --git a/chromium/components/offline_pages/core/task.h b/chromium/components/offline_pages/core/task.h
index 8c259c33649..dc333812a0c 100644
--- a/chromium/components/offline_pages/core/task.h
+++ b/chromium/components/offline_pages/core/task.h
@@ -30,7 +30,7 @@ namespace offline_pages {
class Task {
public:
// Signature of the method to be called by a task, when its work is done.
- typedef base::Callback<void(Task*)> TaskCompletionCallback;
+ typedef base::OnceCallback<void(Task*)> TaskCompletionCallback;
Task();
virtual ~Task();
@@ -45,7 +45,7 @@ class Task {
// |SetTaskCompletionCallback| for details.
void SetTaskCompletionCallbackForTesting(
scoped_refptr<base::SingleThreadTaskRunner> task_completion_runner,
- const TaskCompletionCallback& task_completion_callback);
+ TaskCompletionCallback task_completion_callback);
protected:
// Call |TaskComplete| as the last call, before the task is terminated. This
@@ -66,7 +66,7 @@ class Task {
// not set, it will also work.
void SetTaskCompletionCallback(
scoped_refptr<base::SingleThreadTaskRunner> task_completion_runner,
- const TaskCompletionCallback& task_completion_callback);
+ TaskCompletionCallback task_completion_callback);
// Completion callback for this task set by |SetTaskCompletionCallback|.
TaskCompletionCallback task_completion_callback_;
diff --git a/chromium/components/offline_pages/core/task_queue.cc b/chromium/components/offline_pages/core/task_queue.cc
index e5b10a54b47..93bc953ca22 100644
--- a/chromium/components/offline_pages/core/task_queue.cc
+++ b/chromium/components/offline_pages/core/task_queue.cc
@@ -19,7 +19,8 @@ TaskQueue::~TaskQueue() {}
void TaskQueue::AddTask(std::unique_ptr<Task> task) {
task->SetTaskCompletionCallback(
base::ThreadTaskRunnerHandle::Get(),
- base::Bind(&TaskQueue::TaskCompleted, weak_ptr_factory_.GetWeakPtr()));
+ base::BindOnce(&TaskQueue::TaskCompleted,
+ weak_ptr_factory_.GetWeakPtr()));
tasks_.push(std::move(task));
StartTaskIfAvailable();
}
@@ -40,8 +41,8 @@ void TaskQueue::StartTaskIfAvailable() {
if (!HasPendingTasks()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(&TaskQueue::InformTaskQueueIsIdle,
- weak_ptr_factory_.GetWeakPtr()));
+ FROM_HERE, base::BindOnce(&TaskQueue::InformTaskQueueIsIdle,
+ weak_ptr_factory_.GetWeakPtr()));
return;
}
diff --git a/chromium/components/offline_pages/core/task_unittest.cc b/chromium/components/offline_pages/core/task_unittest.cc
index 0574a7933cb..6bdea9919ea 100644
--- a/chromium/components/offline_pages/core/task_unittest.cc
+++ b/chromium/components/offline_pages/core/task_unittest.cc
@@ -49,7 +49,7 @@ TEST_F(OfflineTaskTest, RunTaskStepByStep) {
TestTask task(&resource);
task.SetTaskCompletionCallbackForTesting(
base::ThreadTaskRunnerHandle::Get(),
- base::Bind(&OfflineTaskTest::TaskCompleted, base::Unretained(this)));
+ base::BindOnce(&OfflineTaskTest::TaskCompleted, base::Unretained(this)));
EXPECT_EQ(TaskState::NOT_STARTED, task.state());
task.Run();
diff --git a/chromium/components/offline_pages/core/test_task.cc b/chromium/components/offline_pages/core/test_task.cc
index cd0ade91ffb..9de80baaa79 100644
--- a/chromium/components/offline_pages/core/test_task.cc
+++ b/chromium/components/offline_pages/core/test_task.cc
@@ -12,14 +12,12 @@ ConsumedResource::ConsumedResource() {}
ConsumedResource::~ConsumedResource() {}
-void ConsumedResource::Step(const base::Closure& step_callback) {
- next_step_ = step_callback;
+void ConsumedResource::Step(base::OnceClosure step_callback) {
+ next_step_ = std::move(step_callback);
}
void ConsumedResource::CompleteStep() {
- base::Closure temp_ = next_step_;
- next_step_.Reset();
- temp_.Run();
+ std::move(next_step_).Run();
}
TestTask::TestTask(ConsumedResource* resource)
@@ -37,7 +35,7 @@ TestTask::~TestTask() {}
// Run is Step 1 in our case.
void TestTask::Run() {
state_ = TaskState::STEP_1;
- resource_->Step(base::Bind(&TestTask::Step2, base::Unretained(this)));
+ resource_->Step(base::BindOnce(&TestTask::Step2, base::Unretained(this)));
}
void TestTask::Step2() {
@@ -46,7 +44,7 @@ void TestTask::Step2() {
return;
}
state_ = TaskState::STEP_2;
- resource_->Step(base::Bind(&TestTask::LastStep, base::Unretained(this)));
+ resource_->Step(base::BindOnce(&TestTask::LastStep, base::Unretained(this)));
}
// This is step 3, but we conclude here.
diff --git a/chromium/components/offline_pages/core/test_task.h b/chromium/components/offline_pages/core/test_task.h
index 01e517f5592..6b2a09042be 100644
--- a/chromium/components/offline_pages/core/test_task.h
+++ b/chromium/components/offline_pages/core/test_task.h
@@ -16,12 +16,12 @@ class ConsumedResource {
ConsumedResource();
~ConsumedResource();
- void Step(const base::Closure& step_callback);
+ void Step(base::OnceClosure step_callback);
void CompleteStep();
bool HasNextStep() const { return !next_step_.is_null(); }
private:
- base::Closure next_step_;
+ base::OnceClosure next_step_;
};
// Sample test task. This should not be used as a example of task implementation
diff --git a/chromium/components/offline_pages/core/test_task_runner.cc b/chromium/components/offline_pages/core/test_task_runner.cc
index f0d50dd00ea..effac46cc4f 100644
--- a/chromium/components/offline_pages/core/test_task_runner.cc
+++ b/chromium/components/offline_pages/core/test_task_runner.cc
@@ -25,9 +25,9 @@ void TestTaskRunner::RunTask(Task* task) {
Task* completed_task = nullptr;
task->SetTaskCompletionCallbackForTesting(
task_runner_.get(),
- base::Bind([](Task** completed_task_ptr,
- Task* task) { *completed_task_ptr = task; },
- &completed_task));
+ base::BindOnce([](Task** completed_task_ptr,
+ Task* task) { *completed_task_ptr = task; },
+ &completed_task));
task->Run();
task_runner_->RunUntilIdle();
EXPECT_EQ(task, completed_task);
diff --git a/chromium/components/offline_pages/resources/PRESUBMIT.py b/chromium/components/offline_pages/resources/PRESUBMIT.py
index 957ea7a6a75..af7a56f6eec 100644
--- a/chromium/components/offline_pages/resources/PRESUBMIT.py
+++ b/chromium/components/offline_pages/resources/PRESUBMIT.py
@@ -6,6 +6,6 @@ def PostUploadHook(cl, change, output_api):
return output_api.EnsureCQIncludeTrybotsAreAdded(
cl,
[
- 'master.tryserver.chromium.linux:closure_compilation',
+ 'luci.chromium.try:closure_compilation',
],
'Automatically added optional Closure bots to run on CQ.')
diff --git a/chromium/components/omnibox/browser/BUILD.gn b/chromium/components/omnibox/browser/BUILD.gn
index 765dd1b5e2a..c06139016ea 100644
--- a/chromium/components/omnibox/browser/BUILD.gn
+++ b/chromium/components/omnibox/browser/BUILD.gn
@@ -20,11 +20,18 @@ aggregate_vector_icons("omnibox_vector_icons") {
icon_directory = "vector_icons"
icons = [
+ "answer_currency.icon",
+ "answer_default.icon",
+ "answer_dictionary.icon",
+ "answer_finance.icon",
+ "answer_sunrise.icon",
+ "answer_when_is.icon",
"blank.icon",
"calculator.icon",
"extension_app.icon",
"http.icon",
"keyword_search.icon",
+ "md_page.icon",
"star.icon",
"switch.icon",
"tab.icon",
@@ -65,6 +72,10 @@ static_library("browser") {
"clipboard_url_provider.h",
"contextual_suggestions_service.cc",
"contextual_suggestions_service.h",
+ "document_provider.cc",
+ "document_provider.h",
+ "document_suggestions_service.cc",
+ "document_suggestions_service.h",
"favicon_cache.cc",
"favicon_cache.h",
"history_match.cc",
@@ -235,6 +246,8 @@ static_library("test_support") {
"test_omnibox_client.h",
"test_omnibox_edit_controller.cc",
"test_omnibox_edit_controller.h",
+ "test_omnibox_edit_model.cc",
+ "test_omnibox_edit_model.h",
"test_omnibox_view.cc",
"test_omnibox_view.h",
"test_scheme_classifier.cc",
@@ -253,6 +266,7 @@ static_library("test_support") {
"//components/sessions",
"//components/toolbar:test_support",
"//net",
+ "//services/network:test_support",
"//testing/gmock",
"//testing/gtest",
"//third_party/metrics_proto",
@@ -288,6 +302,7 @@ source_set("unit_tests") {
"bookmark_provider_unittest.cc",
"builtin_provider_unittest.cc",
"clipboard_url_provider_unittest.cc",
+ "document_provider_unittest.cc",
"favicon_cache_unittest.cc",
"history_provider_unittest.cc",
"history_quick_provider_unittest.cc",
@@ -317,6 +332,7 @@ source_set("unit_tests") {
":test_support",
":unit_tests_bundle_data",
"//base",
+ "//base/test:test_support",
"//components/bookmarks/browser",
"//components/bookmarks/test",
"//components/favicon/core/test:test_support",
@@ -330,12 +346,12 @@ source_set("unit_tests") {
"//components/toolbar:test_support",
"//components/url_formatter",
"//components/variations",
- "//net:test_support",
+ "//services/network:test_support",
"//sql",
"//sql:test_support",
"//testing/gmock",
"//testing/gtest",
- "//ui/base:base",
+ "//ui/base:test_support",
"//url",
]
}
diff --git a/chromium/components/omnibox_strings.grdp b/chromium/components/omnibox_strings.grdp
index 905cf841831..477924207ac 100644
--- a/chromium/components/omnibox_strings.grdp
+++ b/chromium/components/omnibox_strings.grdp
@@ -71,7 +71,10 @@
<message name="IDS_ACC_AUTOCOMPLETE_SUGGESTED_SEARCH" desc="Text for screenreaders describing a suggested search.">
<ph name="TEXT">$1<ex>dogs</ex> search suggestion</ph>
</message>
- <message name="IDS_ACC_AUTOCOMPLETE_QUICK_ANSWER" desc="Readable text represening a query typed by the user in the omnibox, followed by an indication that an answer to that query will follow, followed by the answer. The commas are significant as they will introduce a pause in the spoken text.">
+ <message name="IDS_ACC_AUTOCOMPLETE_SUGGESTED_SEARCH_ENTITY" desc="Text for screenreaders describing a suggested search for an entity with a description. The commas are significant as they will introduce a pause in the spoken text.">
+ <ph name="ENTITY">$1<ex>muhammad ali</ex></ph>, <ph name="DESCRIPTION">$2<ex>american professional boxer</ex></ph>, search suggestion
+ </message>
+ <message name="IDS_ACC_AUTOCOMPLETE_QUICK_ANSWER" desc="Text for screenreaders describing a query, followed by an indication that an answer to that query will follow, followed by the answer. The commas are significant as they will introduce a pause in the spoken text.">
<ph name="QUERY">$1<ex>weather in los angeles</ex></ph>, answer, <ph name="ANSWER">$2<ex>sunny and 84 degrees</ex></ph>
</message>
<message name="IDS_ACC_AUTOCOMPLETE_BOOKMARK" desc="Text for screenreaders describing a URL from a bookmark.">
@@ -80,8 +83,15 @@
<message name="IDS_ACC_AUTOCOMPLETE_CLIPBOARD" desc="Text for screenreaders describing a URL from a clipboard.">
<ph name="LOCATION_TITLE">$2<ex>The Chromium Projects</ex></ph> <ph name="SHORT_URL">$1<ex>www.chromium.org</ex> location from clipboard</ph>
</message>
+ <message name="IDS_ACC_SEARCH_ICON" desc="Text for screenreaders describing a search icon image.">
+ Search icon
+ </message>
<!-- Supplement accessibility label with item position, e.g. "1 of 3" -->
<message name="IDS_ACC_AUTOCOMPLETE_N_OF_M" desc="Text for screenreaders describing the current matche's position in the list of suggestions.">
<ph name="FRIENDLY_MATCH_TEXT">$1<ex>The Chromium Projects http://www.chromium.org bookmark</ex></ph>, <ph name="MATCH_POSITION">$2<ex>2</ex></ph> of <ph name="NUM_MATCHES">$3<ex>3</ex></ph>
</message>
+ <!-- Accessibility suffix for suggestions with a tab switch match. Comma is important. -->
+ <message name="IDS_ACC_TAB_SWITCH_SUFFIX" desc="Suffix for tab switch suggestions to explain keystroke used to switch.">
+ <ph name="TAB_SWITCH_SUFFIX">$1, currently open, press tab then enter to switch to the open tab</ph>
+ </message>
</grit-part>
diff --git a/chromium/components/omnibox_strings_grdp/OWNERS b/chromium/components/omnibox_strings_grdp/OWNERS
index 5535cd29ae9..90b1b756a2d 100644
--- a/chromium/components/omnibox_strings_grdp/OWNERS
+++ b/chromium/components/omnibox_strings_grdp/OWNERS
@@ -1 +1,3 @@
file://components/omnibox/OWNERS
+
+# COMPONENT: UI>Browser>Omnibox
diff --git a/chromium/components/onc/docs/onc_spec.md b/chromium/components/onc/docs/onc_spec.md
index 8fec180ce0b..d35d35d7c08 100644
--- a/chromium/components/onc/docs/onc_spec.md
+++ b/chromium/components/onc/docs/onc_spec.md
@@ -1480,6 +1480,10 @@ ONC configuration of of **Cellular** networks is not yet supported.
* (optional) - **string**
* Password for making connections if required.
+* **Authentication**
+ * (optional) - **string**
+ * Type of authentication protocol for sending username and password.
+
* **Language**
* (optional, rquired if **LocalizedName** is provided) - **string**
Two letter language code for Localizedname if provided.
diff --git a/chromium/components/onc/onc_constants.cc b/chromium/components/onc/onc_constants.cc
index 681a255a497..3ff5a96862e 100644
--- a/chromium/components/onc/onc_constants.cc
+++ b/chromium/components/onc/onc_constants.cc
@@ -158,8 +158,9 @@ const char kAccessPointName[] = "AccessPointName";
const char kName[] = "Name";
const char kUsername[] = "Username";
const char kPassword[] = "Password";
+const char kAuthentication[] = "Authentication";
const char kLocalizedName[] = "LocalizedName";
-const char kLanguage[] = "LocalizedName";
+const char kLanguage[] = "Language";
} // namespace cellular_apn
namespace cellular_found_network {
@@ -461,6 +462,7 @@ const char kAllowOnlyPolicyNetworksToAutoconnect[] =
"AllowOnlyPolicyNetworksToAutoconnect";
const char kAllowOnlyPolicyNetworksToConnect[] =
"AllowOnlyPolicyNetworksToConnect";
+const char kBlacklistedHexSSIDs[] = "BlacklistedHexSSIDs";
const char kDisableNetworkTypes[] = "DisableNetworkTypes";
} // global_network_config
diff --git a/chromium/components/onc/onc_constants.h b/chromium/components/onc/onc_constants.h
index a662bc4f6ac..1006424c56f 100644
--- a/chromium/components/onc/onc_constants.h
+++ b/chromium/components/onc/onc_constants.h
@@ -171,6 +171,7 @@ ONC_EXPORT extern const char kAccessPointName[];
ONC_EXPORT extern const char kName[];
ONC_EXPORT extern const char kUsername[];
ONC_EXPORT extern const char kPassword[];
+ONC_EXPORT extern const char kAuthentication[];
ONC_EXPORT extern const char kLocalizedName[];
ONC_EXPORT extern const char kLanguage[];
} // namespace cellular_apn
@@ -466,6 +467,7 @@ ONC_EXPORT extern const char kWPAD[];
namespace global_network_config {
ONC_EXPORT extern const char kAllowOnlyPolicyNetworksToAutoconnect[];
ONC_EXPORT extern const char kAllowOnlyPolicyNetworksToConnect[];
+ONC_EXPORT extern const char kBlacklistedHexSSIDs[];
ONC_EXPORT extern const char kDisableNetworkTypes[];
} // global_network_config
diff --git a/chromium/components/optimization_guide/optimization_guide_service_observer.h b/chromium/components/optimization_guide/optimization_guide_service_observer.h
index 6dcadf6c138..4f1a4d96c75 100644
--- a/chromium/components/optimization_guide/optimization_guide_service_observer.h
+++ b/chromium/components/optimization_guide/optimization_guide_service_observer.h
@@ -5,6 +5,7 @@
#ifndef COMPONENTS_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SERVICE_OBSERVER_H_
#define COMPONENTS_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SERVICE_OBSERVER_H_
+#include "base/version.h"
#include "components/optimization_guide/proto/hints.pb.h"
namespace optimization_guide {
diff --git a/chromium/components/optimization_guide/optimization_guide_service_unittest.cc b/chromium/components/optimization_guide/optimization_guide_service_unittest.cc
index 751a3601656..1a37f5fcd00 100644
--- a/chromium/components/optimization_guide/optimization_guide_service_unittest.cc
+++ b/chromium/components/optimization_guide/optimization_guide_service_unittest.cc
@@ -11,7 +11,7 @@
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/version.h"
#include "components/optimization_guide/optimization_guide_service_observer.h"
diff --git a/chromium/components/optimization_guide/proto/hints.proto b/chromium/components/optimization_guide/proto/hints.proto
index da2efb7f895..50526774ae8 100644
--- a/chromium/components/optimization_guide/proto/hints.proto
+++ b/chromium/components/optimization_guide/proto/hints.proto
@@ -9,19 +9,40 @@ package optimization_guide.proto;
enum OptimizationType {
TYPE_UNSPECIFIED = 0;
- // This optimization blocks Javascript on the page.
+ // This optimization blocks JavaScript on the page.
NOSCRIPT = 1;
+ // This optimization applies a set of ResourceLoadingHint(s) to load the
+ // page.
+ RESOURCE_LOADING = 2;
}
// Presents semantics for how page load URLs should be matched.
enum KeyRepresentation {
REPRESENTATION_UNSPECIFIED = 0;
// The suffix to match in the hostname of a page load URL.
- // Example: A host suffix of cnn.com would match pages with host
- // sports.cnn.com, but not foocnn.com.
+ //
+ // Example: A host suffix of example.com would match pages with host
+ // sports.example.com, but not fooexample.com.
HOST_SUFFIX = 1;
}
+enum LoadingOptimizationType {
+ LOADING_UNSPECIFIED = 0;
+ // The resource should not be loaded.
+ LOADING_BLOCK_RESOURCE = 1;
+}
+
+message ResourceLoadingHint {
+ // The pattern to match against the resource URL.
+ //
+ // The pattern may be a single substring to match against the URL or it may be
+ // an ordered set of substrings to match where the substrings are separated by
+ // the ‘*’ wildcard character (with an implicit ‘*’ at the beginning and end).
+ optional string resource_pattern = 1;
+ // The type of loading optimization to apply to the resource.
+ optional LoadingOptimizationType loading_optimization_type = 2;
+}
+
message Optimization {
// The type of optimization the hint applies to.
optional OptimizationType optimization_type = 1;
@@ -34,6 +55,72 @@ message Optimization {
// inflated bytes calculated by the client will be 30 in order to have a total
// consumed bytes value of 130.
optional int64 inflation_percent = 2;
+ // An ordered set of resource loading hints for OptimizationType
+ // RESOURCE_LOADING.
+ //
+ // Only the first ResourceLoadingHint record that matches a target resource
+ // URL will be applied to that resource.
+ repeated ResourceLoadingHint resource_loading_hints = 3;
+ // The experiment name that activates the optimization.
+ //
+ // If a non-empty name is provided, the optimization will be disabled unless
+ // a previews experiment of that same name is running. An empty name is not
+ // experimental.
+ //
+ // Previews experiments are enabled and configured by passing an
+ // experiment_name parameter to the OptimizationHintsExperiments feature.
+ // For example, if experiment_name is my_exp, it could be enabled with the
+ // following command-line flags:
+ // --enable-features=OptimizationHintsExperiments<MyFieldTrial
+ // --force-fieldtrial-params="MyFieldTrial.Enabled:experiment_name/my_exp"
+ // --force-fieldtrials=MyFieldTrial/Enabled/
+ optional string experiment_name = 4;
+}
+
+// The possible effective connection type values.
+//
+// The values should match those of //net/nqe/effective_connection_type.h in the
+// Chromium repository.
+enum EffectiveConnectionType {
+ // Effective connection type reported when the network quality is unknown.
+ EFFECTIVE_CONNECTION_TYPE_UNKNOWN = 0;
+
+ // Effective connection type reported when the Internet is unreachable,
+ // either because the device does not have a connection or because the
+ // connection is too slow to be usable.
+ EFFECTIVE_CONNECTION_TYPE_OFFLINE = 1;
+
+ // Effective connection type reported when the network has the quality of a
+ // poor 2G connection.
+ EFFECTIVE_CONNECTION_TYPE_SLOW_2G = 2;
+
+ // Effective connection type reported when the network has the quality of a
+ // faster 2G connection.
+ EFFECTIVE_CONNECTION_TYPE_2G = 3;
+
+ // Effective connection type reported when the network has the quality of a
+ // 3G connection.
+ EFFECTIVE_CONNECTION_TYPE_3G = 4;
+
+ // Effective connection type reported when the network has the quality of a
+ // 4G connection.
+ EFFECTIVE_CONNECTION_TYPE_4G = 5;
+}
+
+message PageHint {
+ // The pattern to match against the committed page URL.
+ //
+ // The pattern may be a single substring to match against the full URL or it
+ // may be an ordered set of substrings to match where the substrings are
+ // separated by the ‘*’ wildcard character (with an implicit ‘*’ at the
+ // beginning and end).
+ optional string page_pattern = 1;
+ // The maximum effective connection type threshold for triggering the
+ // optimization associated with this hint.
+ optional EffectiveConnectionType max_ect_trigger = 2;
+ // An unordered set of optimizations that should be whitelisted for this
+ // page pattern.
+ repeated Optimization whitelisted_optimizations = 3;
}
message Hint {
@@ -42,21 +129,33 @@ message Hint {
// The key that applies to this hint. The key_representation field describes
// the form in which this key takes. Guaranteed to be non-empty.
optional string key = 2;
- // An unordered, non-empty set of optimizations that should be whitelisted for
- // this hint.
+ // An unordered set of optimizations that should be whitelisted for this
+ // hint.
+ // If empty, then no optimizations should be whitelisted (this allows for an
+ // earlier hint entry to act as an exception to a subsequently matching hint
+ // whitelist entry). If multiple optimizations or page_hints are provided, the
+ // client will decide which one to act on.
repeated Optimization whitelisted_optimizations = 3;
+ // An ordered set of per-page hints. Only the first PageHint record
+ // that matches a committed page URL should be used for that page load.
+ repeated PageHint page_hints = 4;
}
message Configuration {
- // An unordered list containing hints for key/optimization combinations.
+ // An ordered list containing hints for key/optimization combinations.
+ //
// It is guaranteed that there will only be a single hint per key and key
// representation combination. These hints are intended to apply to a full
// page.
//
// Note, this list may contain multiple hints that apply to a page. For
- // example, if there are hints for (HOST_SUFFIX,cnn.com) and
- // (HOST_SUFFIX,sports.cnn.com), these may both apply to sports.cnn.com/foo.
+ // example, if there are hints for (HOST_SUFFIX,example.com) and
+ // (HOST_SUFFIX,sports.example.com), these may both apply to
+ // sports.example.com/foo.
//
- // It is expected for the client to use the first match in this list.
+ // The client will use the first Hint record with a key that matches the
+ // main frame URL. Therefore, the server should provide all the hint
+ // details for that key that it wants to provide as the client will only
+ // look at that one record to decide which optimization to apply.
repeated Hint hints = 1;
}
diff --git a/chromium/components/optimization_guide/test_component_creator.cc b/chromium/components/optimization_guide/test_component_creator.cc
index 99c0a617f64..cec41ff5416 100644
--- a/chromium/components/optimization_guide/test_component_creator.cc
+++ b/chromium/components/optimization_guide/test_component_creator.cc
@@ -24,11 +24,12 @@ TestComponentCreator::~TestComponentCreator() {
}
optimization_guide::ComponentInfo
-TestComponentCreator::CreateComponentInfoWithNoScriptWhitelist(
- std::vector<std::string> whitelisted_host_suffixes) {
+TestComponentCreator::CreateComponentInfoWithWhitelist(
+ optimization_guide::proto::OptimizationType optimization_type,
+ const std::vector<std::string>& whitelisted_host_suffixes) {
std::string version_string = base::IntToString(next_component_version_++);
base::FilePath hints_path = GetFilePath(version_string);
- WriteConfigToFile(hints_path, whitelisted_host_suffixes);
+ WriteConfigToFile(optimization_type, hints_path, whitelisted_host_suffixes);
return optimization_guide::ComponentInfo(base::Version(version_string),
hints_path);
}
@@ -41,18 +42,19 @@ base::FilePath TestComponentCreator::GetFilePath(std::string file_path_suffix) {
}
void TestComponentCreator::WriteConfigToFile(
+ optimization_guide::proto::OptimizationType optimization_type,
base::FilePath file_path,
- std::vector<std::string> whitelisted_noscript_host_suffixes) {
+ std::vector<std::string> whitelisted_host_suffixes) {
base::ScopedAllowBlockingForTesting allow_blocking;
optimization_guide::proto::Configuration config;
- for (auto whitelisted_noscript_site : whitelisted_noscript_host_suffixes) {
+ for (auto whitelisted_noscript_site : whitelisted_host_suffixes) {
optimization_guide::proto::Hint* hint = config.add_hints();
hint->set_key(whitelisted_noscript_site);
hint->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::Optimization* optimization =
hint->add_whitelisted_optimizations();
- optimization->set_optimization_type(optimization_guide::proto::NOSCRIPT);
+ optimization->set_optimization_type(optimization_type);
}
std::string serialized_config;
diff --git a/chromium/components/optimization_guide/test_component_creator.h b/chromium/components/optimization_guide/test_component_creator.h
index 8daa5571a9c..ccf056ffab6 100644
--- a/chromium/components/optimization_guide/test_component_creator.h
+++ b/chromium/components/optimization_guide/test_component_creator.h
@@ -22,10 +22,12 @@ class TestComponentCreator {
TestComponentCreator();
~TestComponentCreator();
- // Creates component data based on |whitelisted_host_suffixes| to a temp file
+ // Creates component data based on |whitelisted_host_suffixes| for
+ // optimization with type |optimization_type| to a temp file
// and returns the ComponentInfo for it.
- optimization_guide::ComponentInfo CreateComponentInfoWithNoScriptWhitelist(
- std::vector<std::string> whitelisted_host_suffixes);
+ optimization_guide::ComponentInfo CreateComponentInfoWithWhitelist(
+ optimization_guide::proto::OptimizationType optimization_type,
+ const std::vector<std::string>& whitelisted_host_suffixes);
private:
// Returns the scoped temp directory path with the |file_path_suffix| that is
@@ -33,11 +35,12 @@ class TestComponentCreator {
// The file itself will not be automatically created.
base::FilePath GetFilePath(std::string file_path_suffix);
- // Writes a configuration of hints with the
- // |whitelisted_noscript_host_suffixes| to the file path.
+ // Writes a configuration of hints for |optimization_type| with the
+ // |whitelisted_host_suffixes| to the file path.
void WriteConfigToFile(
+ optimization_guide::proto::OptimizationType optimization_type,
base::FilePath file_path,
- std::vector<std::string> whitelisted_noscript_host_suffixes);
+ std::vector<std::string> whitelisted_host_suffixes);
std::unique_ptr<base::ScopedTempDir> scoped_temp_dir_;
int next_component_version_;
diff --git a/chromium/components/os_crypt/BUILD.gn b/chromium/components/os_crypt/BUILD.gn
index 363f0207b5e..4f1d855df74 100644
--- a/chromium/components/os_crypt/BUILD.gn
+++ b/chromium/components/os_crypt/BUILD.gn
@@ -127,7 +127,7 @@ static_library("test_support") {
"//base",
"//testing/gtest",
]
- if (is_desktop_linux) {
+ if (is_desktop_linux && !is_chromecast) {
sources += [
"os_crypt_mocker_linux.cc",
"os_crypt_mocker_linux.h",
@@ -162,7 +162,7 @@ source_set("unit_tests") {
"//testing/gtest",
]
- if (is_desktop_linux) {
+ if (is_desktop_linux && !is_chromecast) {
sources += [
"key_storage_linux_unittest.cc",
"key_storage_util_linux_unittest.cc",
diff --git a/chromium/components/os_crypt/keychain_password_mac.mm b/chromium/components/os_crypt/keychain_password_mac.mm
index 2b38db266f9..7c110c7b14e 100644
--- a/chromium/components/os_crypt/keychain_password_mac.mm
+++ b/chromium/components/os_crypt/keychain_password_mac.mm
@@ -28,14 +28,9 @@ std::string AddRandomPasswordToKeychain(const AppleKeychain& keychain,
void* password_data =
const_cast<void*>(static_cast<const void*>(password.data()));
- OSStatus error = keychain.AddGenericPassword(NULL,
- service_name.size(),
- service_name.data(),
- account_name.size(),
- account_name.data(),
- password.size(),
- password_data,
- NULL);
+ OSStatus error = keychain.AddGenericPassword(
+ service_name.size(), service_name.data(), account_name.size(),
+ account_name.data(), password.size(), password_data, NULL);
if (error != noErr) {
OSSTATUS_DLOG(ERROR, error) << "Keychain add failed";
@@ -62,13 +57,13 @@ std::string KeychainPassword::GetPassword() const {
UInt32 password_length = 0;
void* password_data = NULL;
OSStatus error = keychain_.FindGenericPassword(
- nullptr, strlen(service_name), service_name, strlen(account_name),
- account_name, &password_length, &password_data, NULL);
+ strlen(service_name), service_name, strlen(account_name), account_name,
+ &password_length, &password_data, NULL);
if (error == noErr) {
std::string password =
std::string(static_cast<char*>(password_data), password_length);
- keychain_.ItemFreeContent(NULL, password_data);
+ keychain_.ItemFreeContent(password_data);
return password;
} else if (error == errSecItemNotFound) {
return AddRandomPasswordToKeychain(keychain_, service_name, account_name);
diff --git a/chromium/components/os_crypt/os_crypt_win.cc b/chromium/components/os_crypt/os_crypt_win.cc
index b62323be9ff..beff2c7014b 100644
--- a/chromium/components/os_crypt/os_crypt_win.cc
+++ b/chromium/components/os_crypt/os_crypt_win.cc
@@ -34,8 +34,10 @@ bool OSCrypt::EncryptString(const std::string& plaintext,
DATA_BLOB output;
BOOL result =
CryptProtectData(&input, L"", nullptr, nullptr, nullptr, 0, &output);
- if (!result)
+ if (!result) {
+ PLOG(ERROR) << "Failed to encrypt";
return false;
+ }
// this does a copy
ciphertext->assign(reinterpret_cast<std::string::value_type*>(output.pbData),
@@ -55,8 +57,10 @@ bool OSCrypt::DecryptString(const std::string& ciphertext,
DATA_BLOB output;
BOOL result = CryptUnprotectData(&input, nullptr, nullptr, nullptr, nullptr,
0, &output);
- if (!result)
+ if (!result) {
+ PLOG(ERROR) << "Failed to decrypt";
return false;
+ }
plaintext->assign(reinterpret_cast<char*>(output.pbData), output.cbData);
LocalFree(output.pbData);
diff --git a/chromium/components/password_manager/OWNERS b/chromium/components/password_manager/OWNERS
index b43b8caaeb7..dd4cf37530f 100644
--- a/chromium/components/password_manager/OWNERS
+++ b/chromium/components/password_manager/OWNERS
@@ -1,4 +1,6 @@
dvadym@chromium.org
+ioanap@chromium.org
+jdoerrie@chromium.org
kolos@chromium.org
vabr@chromium.org
vasilii@chromium.org
diff --git a/chromium/components/password_manager/content/browser/BUILD.gn b/chromium/components/password_manager/content/browser/BUILD.gn
index 8697f171ab7..1ca8e569ef1 100644
--- a/chromium/components/password_manager/content/browser/BUILD.gn
+++ b/chromium/components/password_manager/content/browser/BUILD.gn
@@ -12,8 +12,12 @@ static_library("browser") {
"content_password_manager_driver.h",
"content_password_manager_driver_factory.cc",
"content_password_manager_driver_factory.h",
+ "form_submission_tracker_util.cc",
+ "form_submission_tracker_util.h",
"password_manager_internals_service_factory.cc",
"password_manager_internals_service_factory.h",
+ "password_requirements_service_factory.cc",
+ "password_requirements_service_factory.h",
]
public_deps = [
@@ -36,6 +40,7 @@ source_set("unit_tests") {
testonly = true
sources = [
"content_password_manager_driver_unittest.cc",
+ "form_submission_tracker_util_unittest.cc",
]
deps = [
":browser",
diff --git a/chromium/components/password_manager/content/browser/DEPS b/chromium/components/password_manager/content/browser/DEPS
index 5897af94fe4..8f2c2b79aa5 100644
--- a/chromium/components/password_manager/content/browser/DEPS
+++ b/chromium/components/password_manager/content/browser/DEPS
@@ -3,6 +3,7 @@ include_rules = [
"+components/autofill/content/browser",
"+components/autofill/content/common",
"+components/keyed_service/content",
+ "+components/keyed_service/core",
"+content/public/browser",
"+net",
"+services/service_manager/public/cpp",
diff --git a/chromium/components/password_manager/content/browser/bad_message.cc b/chromium/components/password_manager/content/browser/bad_message.cc
index c46175fa839..8f7ad688d2c 100644
--- a/chromium/components/password_manager/content/browser/bad_message.cc
+++ b/chromium/components/password_manager/content/browser/bad_message.cc
@@ -6,11 +6,19 @@
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
+#include "base/syslog_logging.h"
+#include "components/autofill/core/common/password_form.h"
+#include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
namespace password_manager {
namespace bad_message {
+namespace {
+// Called when the browser receives a bad IPC message from a renderer process on
+// the UI thread. Logs the event, records a histogram metric for the |reason|,
+// and terminates the process for |host|.
void ReceivedBadMessage(content::RenderProcessHost* host,
BadMessageReason reason) {
LOG(ERROR)
@@ -22,5 +30,53 @@ void ReceivedBadMessage(content::RenderProcessHost* host,
content::RenderProcessHost::CrashReportMode::GENERATE_CRASH_DUMP);
}
+bool CheckChildProcessSecurityPolicyForURL(content::RenderFrameHost* frame,
+ const GURL& url,
+ BadMessageReason reason) {
+ // Renderer-side logic should prevent any password manager usage for
+ // about:blank frames as well as data URLs. If that's not the case, kill the
+ // renderer, as it might be exploited.
+ if (url.SchemeIs(url::kAboutScheme) || url.SchemeIs(url::kDataScheme)) {
+ SYSLOG(WARNING) << "Killing renderer: illegal password access from about: "
+ << "or data: URL. Reason: " << static_cast<int>(reason);
+ bad_message::ReceivedBadMessage(frame->GetProcess(), reason);
+ return false;
+ }
+
+ content::ChildProcessSecurityPolicy* policy =
+ content::ChildProcessSecurityPolicy::GetInstance();
+ if (!policy->CanAccessDataForOrigin(frame->GetProcess()->GetID(), url)) {
+ SYSLOG(WARNING) << "Killing renderer: illegal password access. Reason: "
+ << static_cast<int>(reason);
+ bad_message::ReceivedBadMessage(frame->GetProcess(), reason);
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+bool CheckChildProcessSecurityPolicy(
+ content::RenderFrameHost* frame,
+ const autofill::PasswordForm& password_form,
+ BadMessageReason reason) {
+ return CheckChildProcessSecurityPolicyForURL(frame, password_form.origin,
+ reason) &&
+ CheckChildProcessSecurityPolicyForURL(
+ frame, GURL(password_form.signon_realm), reason);
+}
+
+bool CheckChildProcessSecurityPolicy(
+ content::RenderFrameHost* frame,
+ const std::vector<autofill::PasswordForm>& forms,
+ BadMessageReason reason) {
+ for (const auto& form : forms) {
+ if (!bad_message::CheckChildProcessSecurityPolicy(frame, form, reason))
+ return false;
+ }
+ return true;
+}
+
} // namespace bad_message
} // namespace password_manager
diff --git a/chromium/components/password_manager/content/browser/bad_message.h b/chromium/components/password_manager/content/browser/bad_message.h
index c0feba10860..8c2c0c524d1 100644
--- a/chromium/components/password_manager/content/browser/bad_message.h
+++ b/chromium/components/password_manager/content/browser/bad_message.h
@@ -5,8 +5,14 @@
#ifndef COMPONENTS_PASSWORD_MANAGER_CONTENT_BROWSER_BAD_MESSAGE_H_
#define COMPONENTS_PASSWORD_MANAGER_CONTENT_BROWSER_BAD_MESSAGE_H_
+#include <vector>
+
+namespace autofill {
+struct PasswordForm;
+}
+
namespace content {
-class RenderProcessHost;
+class RenderFrameHost;
}
namespace password_manager {
@@ -28,6 +34,10 @@ enum class BadMessageReason {
CPMD_BAD_ORIGIN_PRESAVE_GENERATED_PASSWORD = 7,
CPMD_BAD_ORIGIN_SAVE_GENERATION_FIELD_DETECTED_BY_CLASSIFIER = 8,
CPMD_BAD_ORIGIN_SHOW_FALLBACK_FOR_SAVING = 9,
+ CPMD_BAD_ORIGIN_AUTOMATIC_GENERATION_STATUS_CHANGED = 10,
+ CPMD_BAD_ORIGIN_SHOW_MANUAL_PASSWORD_GENERATION_POPUP = 11,
+ CPMD_BAD_ORIGIN_SHOW_PASSWORD_EDITING_POPUP = 12,
+ CPMD_BAD_ORIGIN_GENERATION_AVAILABLE_FOR_FORM = 13,
// Please add new elements here. The naming convention is abbreviated class
// name (e.g. ContentPasswordManagerDriver becomes CPMD) plus a unique
@@ -38,11 +48,19 @@ enum class BadMessageReason {
};
namespace bad_message {
-// Called when the browser receives a bad IPC message from a renderer process on
-// the UI thread. Logs the event, records a histogram metric for the |reason|,
-// and terminates the process for |host|.
-void ReceivedBadMessage(content::RenderProcessHost* host,
- BadMessageReason reason);
+// Returns true if the renderer for |frame| is allowed to perform an operation
+// on |password_form|. If the origin mismatches, the process for |frame| is
+// terminated and the function returns false.
+bool CheckChildProcessSecurityPolicy(
+ content::RenderFrameHost* frame,
+ const autofill::PasswordForm& password_form,
+ BadMessageReason reason);
+
+// Same as above but checks every form in |forms|.
+bool CheckChildProcessSecurityPolicy(
+ content::RenderFrameHost* frame,
+ const std::vector<autofill::PasswordForm>& forms,
+ BadMessageReason reason);
} // namespace bad_message
} // namespace password_manager
diff --git a/chromium/components/password_manager/content/browser/content_credential_manager.cc b/chromium/components/password_manager/content/browser/content_credential_manager.cc
index 2bf69bd4ddd..99aa19e8999 100644
--- a/chromium/components/password_manager/content/browser/content_credential_manager.cc
+++ b/chromium/components/password_manager/content/browser/content_credential_manager.cc
@@ -19,7 +19,7 @@ ContentCredentialManager::ContentCredentialManager(
ContentCredentialManager::~ContentCredentialManager() {}
void ContentCredentialManager::BindRequest(
- mojom::CredentialManagerRequest request) {
+ blink::mojom::CredentialManagerRequest request) {
DCHECK(!binding_.is_bound());
binding_.Bind(std::move(request));
diff --git a/chromium/components/password_manager/content/browser/content_credential_manager.h b/chromium/components/password_manager/content/browser/content_credential_manager.h
index 960c5473780..1c048683c9c 100644
--- a/chromium/components/password_manager/content/browser/content_credential_manager.h
+++ b/chromium/components/password_manager/content/browser/content_credential_manager.h
@@ -16,19 +16,20 @@ namespace password_manager {
class PasswordManagerClient;
struct CredentialInfo;
-// Implements mojom::CredentialManager using core class CredentialManagerImpl.
-// Methods Store, PreventSilentAccess and Get are invoked from the renderer
-// with callbacks as arguments. PasswordManagerClient is used to invoke UI.
-class ContentCredentialManager : public mojom::CredentialManager {
+// Implements blink::mojom::CredentialManager using core class
+// CredentialManagerImpl. Methods Store, PreventSilentAccess and Get are invoked
+// from the renderer with callbacks as arguments. PasswordManagerClient is used
+// to invoke UI.
+class ContentCredentialManager : public blink::mojom::CredentialManager {
public:
explicit ContentCredentialManager(PasswordManagerClient* client);
~ContentCredentialManager() override;
- void BindRequest(mojom::CredentialManagerRequest request);
+ void BindRequest(blink::mojom::CredentialManagerRequest request);
bool HasBinding() const;
void DisconnectBinding();
- // mojom::CredentialManager methods:
+ // blink::mojom::CredentialManager methods:
void Store(const CredentialInfo& credential, StoreCallback callback) override;
void PreventSilentAccess(PreventSilentAccessCallback callback) override;
void Get(CredentialMediationRequirement mediation,
@@ -39,7 +40,7 @@ class ContentCredentialManager : public mojom::CredentialManager {
private:
CredentialManagerImpl impl_;
- mojo::Binding<mojom::CredentialManager> binding_;
+ mojo::Binding<blink::mojom::CredentialManager> binding_;
DISALLOW_COPY_AND_ASSIGN(ContentCredentialManager);
};
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 ed4741525d5..232702f87f9 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
@@ -6,7 +6,7 @@
#include <utility>
-#include "base/syslog_logging.h"
+#include "base/callback.h"
#include "components/autofill/content/browser/content_autofill_driver.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/password_form.h"
@@ -17,14 +17,8 @@
#include "components/password_manager/core/browser/password_manager_client.h"
#include "components/password_manager/core/browser/password_manager_metrics_recorder.h"
#include "content/public/browser/browser_context.h"
-#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/browser/render_widget_host_view.h"
-#include "content/public/browser/site_instance.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/web_contents.h"
#include "net/cert/cert_status_flags.h"
@@ -41,7 +35,6 @@ ContentPasswordManagerDriver::ContentPasswordManagerDriver(
password_generation_manager_(client, this),
password_autofill_manager_(this, autofill_client, client),
is_main_frame_(render_frame_host->GetParent() == nullptr),
- password_manager_binding_(this),
weak_factory_(this) {
// For some frames |this| may be instantiated before log manager creation, so
// here we can not send logging state to renderer process for them. For such
@@ -70,11 +63,6 @@ ContentPasswordManagerDriver::GetForRenderFrameHost(
return factory ? factory->GetDriverForFrame(render_frame_host) : nullptr;
}
-void ContentPasswordManagerDriver::BindRequest(
- autofill::mojom::PasswordManagerDriverRequest request) {
- password_manager_binding_.Bind(std::move(request));
-}
-
void ContentPasswordManagerDriver::FillPasswordForm(
const autofill::PasswordFormFillData& form_data) {
const int key = GetNextKey();
@@ -85,8 +73,10 @@ void ContentPasswordManagerDriver::FillPasswordForm(
void ContentPasswordManagerDriver::AllowPasswordGenerationForForm(
const autofill::PasswordForm& form) {
- if (!GetPasswordGenerationManager()->IsGenerationEnabled())
+ if (!GetPasswordGenerationManager()->IsGenerationEnabled(
+ /*log_debug_data=*/true)) {
return;
+ }
GetPasswordGenerationAgent()->FormNotBlacklisted(form);
}
@@ -107,16 +97,20 @@ void ContentPasswordManagerDriver::GeneratedPasswordAccepted(
GetPasswordGenerationAgent()->GeneratedPasswordAccepted(password);
}
-void ContentPasswordManagerDriver::UserSelectedManualGenerationOption() {
- GetPasswordGenerationAgent()->UserSelectedManualGenerationOption();
-}
-
void ContentPasswordManagerDriver::FillSuggestion(
const base::string16& username,
const base::string16& password) {
GetAutofillAgent()->FillPasswordSuggestion(username, password);
}
+void ContentPasswordManagerDriver::FillIntoFocusedField(
+ bool is_password,
+ const base::string16& credential,
+ base::OnceCallback<void(autofill::FillingStatus)> compeleted_callback) {
+ GetPasswordAutofillAgent()->FillIntoFocusedField(
+ is_password, credential, std::move(compeleted_callback));
+}
+
void ContentPasswordManagerDriver::PreviewSuggestion(
const base::string16& username,
const base::string16& password) {
@@ -140,45 +134,10 @@ void ContentPasswordManagerDriver::ForceSavePassword() {
weak_factory_.GetWeakPtr()));
}
-void ContentPasswordManagerDriver::ShowManualFallbackForSaving(
- const autofill::PasswordForm& password_form) {
- if (!CheckChildProcessSecurityPolicy(
- password_form.origin,
- BadMessageReason::CPMD_BAD_ORIGIN_SHOW_FALLBACK_FOR_SAVING))
- return;
- GetPasswordManager()->ShowManualFallbackForSaving(this, password_form);
-}
-
-void ContentPasswordManagerDriver::HideManualFallbackForSaving() {
- GetPasswordManager()->HideManualFallbackForSaving();
-}
-
void ContentPasswordManagerDriver::GeneratePassword() {
GetPasswordGenerationAgent()->UserTriggeredGeneratePassword();
}
-void ContentPasswordManagerDriver::SendLoggingAvailability() {
- GetPasswordAutofillAgent()->SetLoggingState(
- client_->GetLogManager()->IsLoggingActive());
-}
-
-void ContentPasswordManagerDriver::AllowToRunFormClassifier() {
- GetPasswordGenerationAgent()->AllowToRunFormClassifier();
-}
-
-autofill::AutofillDriver* ContentPasswordManagerDriver::GetAutofillDriver() {
- return autofill::ContentAutofillDriver::GetForRenderFrameHost(
- render_frame_host_);
-}
-
-bool ContentPasswordManagerDriver::IsMainFrame() const {
- return is_main_frame_;
-}
-
-void ContentPasswordManagerDriver::MatchingBlacklistedFormFound() {
- GetPasswordAutofillAgent()->BlacklistedFormFound();
-}
-
PasswordGenerationManager*
ContentPasswordManagerDriver::GetPasswordGenerationManager() {
return &password_generation_manager_;
@@ -193,49 +152,22 @@ ContentPasswordManagerDriver::GetPasswordAutofillManager() {
return &password_autofill_manager_;
}
-void ContentPasswordManagerDriver::PasswordFormsParsed(
- const std::vector<autofill::PasswordForm>& forms) {
- for (const auto& form : forms)
- if (!CheckChildProcessSecurityPolicy(
- form.origin, BadMessageReason::CPMD_BAD_ORIGIN_FORMS_PARSED))
- return;
-
- OnPasswordFormsParsedNoRenderCheck(forms);
-}
-
-void ContentPasswordManagerDriver::OnPasswordFormsParsedNoRenderCheck(
- const std::vector<autofill::PasswordForm>& forms) {
- GetPasswordManager()->OnPasswordFormsParsed(this, forms);
- GetPasswordGenerationManager()->CheckIfFormClassifierShouldRun();
+void ContentPasswordManagerDriver::SendLoggingAvailability() {
+ GetPasswordAutofillAgent()->SetLoggingState(
+ client_->GetLogManager()->IsLoggingActive());
}
-void ContentPasswordManagerDriver::PasswordFormsRendered(
- const std::vector<autofill::PasswordForm>& visible_forms,
- bool did_stop_loading) {
- for (const auto& form : visible_forms)
- if (!CheckChildProcessSecurityPolicy(
- form.origin, BadMessageReason::CPMD_BAD_ORIGIN_FORMS_RENDERED))
- return;
- GetPasswordManager()->OnPasswordFormsRendered(this, visible_forms,
- did_stop_loading);
+void ContentPasswordManagerDriver::AllowToRunFormClassifier() {
+ GetPasswordGenerationAgent()->AllowToRunFormClassifier();
}
-void ContentPasswordManagerDriver::PasswordFormSubmitted(
- const autofill::PasswordForm& password_form) {
- if (!CheckChildProcessSecurityPolicy(
- password_form.origin,
- BadMessageReason::CPMD_BAD_ORIGIN_FORM_SUBMITTED))
- return;
- GetPasswordManager()->OnPasswordFormSubmitted(this, password_form);
+autofill::AutofillDriver* ContentPasswordManagerDriver::GetAutofillDriver() {
+ return autofill::ContentAutofillDriver::GetForRenderFrameHost(
+ render_frame_host_);
}
-void ContentPasswordManagerDriver::OnFocusedPasswordFormFound(
- const autofill::PasswordForm& password_form) {
- if (!CheckChildProcessSecurityPolicy(
- password_form.origin,
- BadMessageReason::CPMD_BAD_ORIGIN_FOCUSED_PASSWORD_FORM_FOUND))
- return;
- GetPasswordManager()->OnPasswordFormForceSaveRequested(this, password_form);
+bool ContentPasswordManagerDriver::IsMainFrame() const {
+ return is_main_frame_;
}
void ContentPasswordManagerDriver::DidNavigateFrame(
@@ -248,104 +180,13 @@ void ContentPasswordManagerDriver::DidNavigateFrame(
}
}
-void ContentPasswordManagerDriver::SameDocumentNavigation(
- const autofill::PasswordForm& password_form) {
- if (!CheckChildProcessSecurityPolicy(
- password_form.origin,
- BadMessageReason::CPMD_BAD_ORIGIN_IN_PAGE_NAVIGATION))
- return;
- GetPasswordManager()->OnSameDocumentNavigation(this, password_form);
-}
-
-void ContentPasswordManagerDriver::PresaveGeneratedPassword(
- const autofill::PasswordForm& password_form) {
- if (!CheckChildProcessSecurityPolicy(
- password_form.origin,
- BadMessageReason::CPMD_BAD_ORIGIN_PRESAVE_GENERATED_PASSWORD))
- return;
- GetPasswordManager()->OnPresaveGeneratedPassword(password_form);
-}
-
-void ContentPasswordManagerDriver::PasswordNoLongerGenerated(
+void ContentPasswordManagerDriver::OnFocusedPasswordFormFound(
const autofill::PasswordForm& password_form) {
- if (!CheckChildProcessSecurityPolicy(
- password_form.origin,
- BadMessageReason::CPMD_BAD_ORIGIN_PASSWORD_NO_LONGER_GENERATED))
- return;
- GetPasswordManager()->OnPasswordNoLongerGenerated(password_form);
-}
-
-void ContentPasswordManagerDriver::SaveGenerationFieldDetectedByClassifier(
- const autofill::PasswordForm& password_form,
- const base::string16& generation_field) {
- if (!CheckChildProcessSecurityPolicy(
- password_form.origin,
- BadMessageReason::
- CPMD_BAD_ORIGIN_SAVE_GENERATION_FIELD_DETECTED_BY_CLASSIFIER))
+ if (!bad_message::CheckChildProcessSecurityPolicy(
+ render_frame_host_, password_form,
+ BadMessageReason::CPMD_BAD_ORIGIN_FOCUSED_PASSWORD_FORM_FOUND))
return;
- GetPasswordManager()->SaveGenerationFieldDetectedByClassifier(
- password_form, generation_field);
-}
-
-void ContentPasswordManagerDriver::CheckSafeBrowsingReputation(
- const GURL& form_action,
- const GURL& frame_url) {
-#if defined(SAFE_BROWSING_DB_LOCAL)
- client_->CheckSafeBrowsingReputation(form_action, frame_url);
-#endif
-}
-
-void ContentPasswordManagerDriver::ShowPasswordSuggestions(
- int key,
- base::i18n::TextDirection text_direction,
- const base::string16& typed_username,
- int options,
- const gfx::RectF& bounds) {
- password_autofill_manager_.OnShowPasswordSuggestions(
- key, text_direction, typed_username, options,
- TransformToRootCoordinates(bounds));
-}
-
-void ContentPasswordManagerDriver::ShowManualFallbackSuggestion(
- base::i18n::TextDirection text_direction,
- const gfx::RectF& bounds) {
- password_autofill_manager_.OnShowManualFallbackSuggestion(
- text_direction, TransformToRootCoordinates(bounds));
-}
-
-void ContentPasswordManagerDriver::RecordSavePasswordProgress(
- const std::string& log) {
- client_->GetLogManager()->LogSavePasswordProgress(log);
-}
-
-void ContentPasswordManagerDriver::UserModifiedPasswordField() {
- client_->GetMetricsRecorder().RecordUserModifiedPasswordField();
-}
-
-bool ContentPasswordManagerDriver::CheckChildProcessSecurityPolicy(
- const GURL& url,
- BadMessageReason reason) {
- // Renderer-side logic should prevent any password manager usage for
- // about:blank frames as well as data URLs. If that's not the case, kill the
- // renderer, as it might be exploited.
- if (url.SchemeIs(url::kAboutScheme) || url.SchemeIs(url::kDataScheme)) {
- SYSLOG(WARNING) << "Killing renderer: illegal password access from about: "
- << " or data: URL. Reason: " << static_cast<int>(reason);
- bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(), reason);
- return false;
- }
-
- content::ChildProcessSecurityPolicy* policy =
- content::ChildProcessSecurityPolicy::GetInstance();
- if (!policy->CanAccessDataForOrigin(render_frame_host_->GetProcess()->GetID(),
- url)) {
- SYSLOG(WARNING) << "Killing renderer: illegal password access. Reason: "
- << static_cast<int>(reason);
- bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(), reason);
- return false;
- }
-
- return true;
+ GetPasswordManager()->OnPasswordFormForceSaveRequested(this, password_form);
}
const autofill::mojom::AutofillAgentPtr&
@@ -381,16 +222,6 @@ ContentPasswordManagerDriver::GetPasswordGenerationAgent() {
return password_gen_agent_;
}
-gfx::RectF ContentPasswordManagerDriver::TransformToRootCoordinates(
- const gfx::RectF& bounds_in_frame_coordinates) {
- content::RenderWidgetHostView* rwhv = render_frame_host_->GetView();
- if (!rwhv)
- return bounds_in_frame_coordinates;
- return gfx::RectF(rwhv->TransformPointToRootCoordSpaceF(
- bounds_in_frame_coordinates.origin()),
- bounds_in_frame_coordinates.size());
-}
-
int ContentPasswordManagerDriver::GetNextKey() {
// Limit the range of the key to avoid excessive allocations. See
// https://crbug.com/846404.
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 7fe883b92c5..d2507515804 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
@@ -12,15 +12,13 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "components/autofill/content/common/autofill_agent.mojom.h"
-#include "components/autofill/content/common/autofill_driver.mojom.h"
#include "components/autofill/core/common/password_form_field_prediction_map.h"
#include "components/autofill/core/common/password_form_generation_data.h"
#include "components/password_manager/core/browser/password_autofill_manager.h"
#include "components/password_manager/core/browser/password_generation_manager.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/browser/password_manager_driver.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
namespace autofill {
struct PasswordForm;
@@ -36,9 +34,7 @@ enum class BadMessageReason;
// There is one ContentPasswordManagerDriver per RenderFrameHost.
// The lifetime is managed by the ContentPasswordManagerDriverFactory.
-class ContentPasswordManagerDriver
- : public PasswordManagerDriver,
- public autofill::mojom::PasswordManagerDriver {
+class ContentPasswordManagerDriver : public PasswordManagerDriver {
public:
ContentPasswordManagerDriver(content::RenderFrameHost* render_frame_host,
PasswordManagerClient* client,
@@ -49,8 +45,6 @@ class ContentPasswordManagerDriver
static ContentPasswordManagerDriver* GetForRenderFrameHost(
content::RenderFrameHost* render_frame_host);
- void BindRequest(autofill::mojom::PasswordManagerDriverRequest request);
-
// PasswordManagerDriver implementation.
void FillPasswordForm(
const autofill::PasswordFormFillData& form_data) override;
@@ -63,66 +57,31 @@ class ContentPasswordManagerDriver
autofill::PasswordFormFieldPredictionMap>& predictions)
override;
void GeneratedPasswordAccepted(const base::string16& password) override;
- void UserSelectedManualGenerationOption() override;
void FillSuggestion(const base::string16& username,
const base::string16& password) override;
+ void FillIntoFocusedField(bool is_password,
+ const base::string16& credential,
+ base::OnceCallback<void(autofill::FillingStatus)>
+ compeleted_callback) override;
void PreviewSuggestion(const base::string16& username,
const base::string16& password) override;
void ShowInitialPasswordAccountSuggestions(
const autofill::PasswordFormFillData& form_data) override;
void ClearPreviewedForm() override;
void ForceSavePassword() override;
- void ShowManualFallbackForSaving(const autofill::PasswordForm& form) override;
- void HideManualFallbackForSaving() override;
void GeneratePassword() override;
+ PasswordGenerationManager* GetPasswordGenerationManager() override;
+ PasswordManager* GetPasswordManager() override;
+ PasswordAutofillManager* GetPasswordAutofillManager() override;
void SendLoggingAvailability() override;
void AllowToRunFormClassifier() override;
autofill::AutofillDriver* GetAutofillDriver() override;
bool IsMainFrame() const override;
- void MatchingBlacklistedFormFound() override;
-
- PasswordGenerationManager* GetPasswordGenerationManager() override;
- PasswordManager* GetPasswordManager() override;
- PasswordAutofillManager* GetPasswordAutofillManager() override;
void DidNavigateFrame(content::NavigationHandle* navigation_handle);
- // autofill::mojom::PasswordManagerDriver:
- void PasswordFormsParsed(
- const std::vector<autofill::PasswordForm>& forms) override;
- void PasswordFormsRendered(
- const std::vector<autofill::PasswordForm>& visible_forms,
- bool did_stop_loading) override;
- void PasswordFormSubmitted(
- const autofill::PasswordForm& password_form) override;
- void SameDocumentNavigation(
- const autofill::PasswordForm& password_form) override;
- void PresaveGeneratedPassword(
- const autofill::PasswordForm& password_form) override;
- void PasswordNoLongerGenerated(
- const autofill::PasswordForm& password_form) override;
- void ShowPasswordSuggestions(int key,
- base::i18n::TextDirection text_direction,
- const base::string16& typed_username,
- int options,
- const gfx::RectF& bounds) override;
- void ShowManualFallbackSuggestion(base::i18n::TextDirection text_direction,
- const gfx::RectF& bounds) override;
- void RecordSavePasswordProgress(const std::string& log) override;
- void UserModifiedPasswordField() override;
- void SaveGenerationFieldDetectedByClassifier(
- const autofill::PasswordForm& password_form,
- const base::string16& generation_field) override;
- void CheckSafeBrowsingReputation(const GURL& form_action,
- const GURL& frame_url) override;
-
- void OnPasswordFormsParsedNoRenderCheck(
- const std::vector<autofill::PasswordForm>& forms);
- void OnFocusedPasswordFormFound(const autofill::PasswordForm& password_form);
-
private:
- bool CheckChildProcessSecurityPolicy(const GURL& url,
- BadMessageReason reason);
+ void OnFocusedPasswordFormFound(const autofill::PasswordForm& password_form);
const autofill::mojom::AutofillAgentPtr& GetAutofillAgent();
@@ -131,9 +90,6 @@ class ContentPasswordManagerDriver
const autofill::mojom::PasswordGenerationAgentPtr&
GetPasswordGenerationAgent();
- gfx::RectF TransformToRootCoordinates(
- const gfx::RectF& bounds_in_frame_coordinates);
-
// Returns the next key to be used for PasswordFormFillData sent to
// PasswordAutofillManager and PasswordAutofillAgent.
int GetNextKey();
@@ -160,9 +116,6 @@ class ContentPasswordManagerDriver
autofill::mojom::PasswordGenerationAgentPtr password_gen_agent_;
- mojo::Binding<autofill::mojom::PasswordManagerDriver>
- password_manager_binding_;
-
base::WeakPtrFactory<ContentPasswordManagerDriver> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ContentPasswordManagerDriver);
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 bd2d2c68706..654b456f37b 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
@@ -75,30 +75,6 @@ ContentPasswordManagerDriverFactory::FromWebContents(
kContentPasswordManagerDriverFactoryWebContentsUserDataKey));
}
-// static
-void ContentPasswordManagerDriverFactory::BindPasswordManagerDriver(
- autofill::mojom::PasswordManagerDriverRequest request,
- content::RenderFrameHost* render_frame_host) {
- content::WebContents* web_contents =
- content::WebContents::FromRenderFrameHost(render_frame_host);
- if (!web_contents)
- return;
-
- ContentPasswordManagerDriverFactory* factory =
- ContentPasswordManagerDriverFactory::FromWebContents(web_contents);
- // We try to bind to the driver, but if driver is not ready for now or totally
- // not available for this render frame host, the request will be just dropped.
- // This would cause the message pipe to be closed, which will raise a
- // connection error on the peer side.
- if (!factory)
- return;
-
- ContentPasswordManagerDriver* driver =
- factory->GetDriverForFrame(render_frame_host);
- if (driver)
- driver->BindRequest(std::move(request));
-}
-
ContentPasswordManagerDriver*
ContentPasswordManagerDriverFactory::GetDriverForFrame(
content::RenderFrameHost* render_frame_host) {
diff --git a/chromium/components/password_manager/content/browser/content_password_manager_driver_factory.h b/chromium/components/password_manager/content/browser/content_password_manager_driver_factory.h
index 652529cee2a..42bdb01e0ea 100644
--- a/chromium/components/password_manager/content/browser/content_password_manager_driver_factory.h
+++ b/chromium/components/password_manager/content/browser/content_password_manager_driver_factory.h
@@ -41,10 +41,6 @@ class ContentPasswordManagerDriverFactory
static ContentPasswordManagerDriverFactory* FromWebContents(
content::WebContents* web_contents);
- static void BindPasswordManagerDriver(
- autofill::mojom::PasswordManagerDriverRequest request,
- content::RenderFrameHost* render_frame_host);
-
ContentPasswordManagerDriver* GetDriverForFrame(
content::RenderFrameHost* render_frame_host);
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 7a8ba9fedff..428462db12d 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
@@ -80,6 +80,8 @@ class FakePasswordAutofillAgent
// autofill::mojom::PasswordAutofillAgent:
MOCK_METHOD2(FillPasswordForm,
void(int, const autofill::PasswordFormFillData&));
+ MOCK_METHOD3(FillIntoFocusedField,
+ void(bool, const base::string16&, FillIntoFocusedFieldCallback));
MOCK_METHOD0(BlacklistedFormFound, void());
@@ -243,16 +245,6 @@ TEST_F(ContentPasswordManagerDriverTest, NotInformAboutBlacklistedForm) {
driver->FillPasswordForm(fill_data);
}
-#if defined(SAFE_BROWSING_DB_LOCAL)
-TEST_F(ContentPasswordManagerDriverTest, CheckSafeBrowsingReputationCalled) {
- std::unique_ptr<ContentPasswordManagerDriver> driver(
- new ContentPasswordManagerDriver(main_rfh(), &password_manager_client_,
- &autofill_client_));
- EXPECT_CALL(password_manager_client_, CheckSafeBrowsingReputation(_, _));
- driver->CheckSafeBrowsingReputation(GURL(), GURL());
-}
-#endif
-
INSTANTIATE_TEST_CASE_P(,
ContentPasswordManagerDriverTest,
testing::Values(true, false));
diff --git a/chromium/components/password_manager/content/browser/form_submission_tracker_util.cc b/chromium/components/password_manager/content/browser/form_submission_tracker_util.cc
new file mode 100644
index 00000000000..58be99e4f98
--- /dev/null
+++ b/chromium/components/password_manager/content/browser/form_submission_tracker_util.cc
@@ -0,0 +1,31 @@
+// 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/password_manager/content/browser/form_submission_tracker_util.h"
+
+#include "components/password_manager/core/browser/form_submission_observer.h"
+#include "content/public/browser/navigation_handle.h"
+
+namespace password_manager {
+
+void NotifyOnStartNavigation(content::NavigationHandle* navigation_handle,
+ PasswordManagerDriver* driver,
+ FormSubmissionObserver* observer) {
+ DCHECK(navigation_handle);
+ DCHECK(observer);
+
+ // Password manager isn't interested in
+ // - subframe navigations,
+ // - user initiated navigations (e.g. click on the bookmark),
+ // - hyperlink navigations.
+ if (!navigation_handle->IsInMainFrame() ||
+ !navigation_handle->IsRendererInitiated() ||
+ ui::PageTransitionCoreTypeIs(navigation_handle->GetPageTransition(),
+ ui::PAGE_TRANSITION_LINK))
+ return;
+
+ observer->OnStartNavigation(driver);
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/content/browser/form_submission_tracker_util.h b/chromium/components/password_manager/content/browser/form_submission_tracker_util.h
new file mode 100644
index 00000000000..2a8ff2e117b
--- /dev/null
+++ b/chromium/components/password_manager/content/browser/form_submission_tracker_util.h
@@ -0,0 +1,28 @@
+// 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_PASSWORD_MANAGER_CONTENT_BROWSER_FORM_SUBMISSION_TRACKER_UTIL_H_
+#define COMPONENTS_PASSWORD_MANAGER_CONTENT_BROWSER_FORM_SUBMISSION_TRACKER_UTIL_H_
+
+#include "base/macros.h"
+
+namespace content {
+class NavigationHandle;
+} // namespace content
+
+namespace password_manager {
+
+class FormSubmissionObserver;
+class PasswordManagerDriver;
+
+// Calls |observer_| if the navigation is interesting for the password
+// manager. |driver| is just passed to the observer. None content
+// initiated, none top level, hyperlinks navigations are ignored.
+void NotifyOnStartNavigation(content::NavigationHandle* navigation_handle,
+ PasswordManagerDriver* driver,
+ FormSubmissionObserver* observer);
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CONTENT_BROWSER_FORM_SUBMISSION_TRACKER_UTIL_H_
diff --git a/chromium/components/password_manager/content/browser/form_submission_tracker_util_unittest.cc b/chromium/components/password_manager/content/browser/form_submission_tracker_util_unittest.cc
new file mode 100644
index 00000000000..362ccd4ba38
--- /dev/null
+++ b/chromium/components/password_manager/content/browser/form_submission_tracker_util_unittest.cc
@@ -0,0 +1,47 @@
+// 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/password_manager/content/browser/form_submission_tracker_util.h"
+
+#include "components/password_manager/core/browser/form_submission_observer.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/test/test_renderer_host.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+namespace {
+
+constexpr char kExampleURL[] = "https://example.com";
+
+class FormSubmissionObserverMock : public FormSubmissionObserver {
+ public:
+ MOCK_METHOD1(OnStartNavigation, void(PasswordManagerDriver*));
+};
+
+class FormSubmissionTrackerUtilTest
+ : public content::RenderViewHostTestHarness {
+ public:
+ FormSubmissionTrackerUtilTest() = default;
+ ~FormSubmissionTrackerUtilTest() override = default;
+
+ FormSubmissionObserverMock& observer() { return observer_; }
+
+ private:
+ FormSubmissionObserverMock observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(FormSubmissionTrackerUtilTest);
+};
+
+TEST_F(FormSubmissionTrackerUtilTest, DidStartNavigation) {
+ std::unique_ptr<content::NavigationHandle> navigation_handle =
+ content::NavigationHandle::CreateNavigationHandleForTesting(
+ GURL(kExampleURL), main_rfh(), false, net::OK, false, false,
+ ui::PAGE_TRANSITION_FORM_SUBMIT);
+ EXPECT_CALL(observer(), OnStartNavigation(nullptr));
+ NotifyOnStartNavigation(navigation_handle.get(), nullptr, &observer());
+}
+
+} // namespace
+} // namespace password_manager
diff --git a/chromium/components/password_manager/content/browser/password_requirements_service_factory.cc b/chromium/components/password_manager/content/browser/password_requirements_service_factory.cc
new file mode 100644
index 00000000000..cfa39766d47
--- /dev/null
+++ b/chromium/components/password_manager/content/browser/password_requirements_service_factory.cc
@@ -0,0 +1,120 @@
+// 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/password_manager/content/browser/password_requirements_service_factory.h"
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/memory/singleton.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/autofill/core/browser/password_requirements_spec_fetcher.h"
+#include "components/autofill/core/browser/password_requirements_spec_fetcher_impl.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/password_manager/core/browser/password_requirements_service.h"
+#include "components/password_manager/core/common/password_manager_features.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
+#include "ui/base/ui_base_features.h"
+
+namespace password_manager {
+
+// static
+PasswordRequirementsServiceFactory*
+PasswordRequirementsServiceFactory::GetInstance() {
+ return base::Singleton<PasswordRequirementsServiceFactory>::get();
+}
+
+// static
+PasswordRequirementsService*
+PasswordRequirementsServiceFactory::GetForBrowserContext(
+ content::BrowserContext* context) {
+ return static_cast<PasswordRequirementsService*>(
+ GetInstance()->GetServiceForBrowserContext(context, true /* create */));
+}
+
+PasswordRequirementsServiceFactory::PasswordRequirementsServiceFactory()
+ : BrowserContextKeyedServiceFactory(
+ "PasswordRequirementsServiceFactory",
+ BrowserContextDependencyManager::GetInstance()) {}
+
+PasswordRequirementsServiceFactory::~PasswordRequirementsServiceFactory() {}
+
+KeyedService* PasswordRequirementsServiceFactory::BuildServiceInstanceFor(
+ content::BrowserContext* context) const {
+ if (context->IsOffTheRecord())
+ return nullptr;
+
+ VLOG(1) << "PasswordGenerationRequirements experiment enabled? "
+ << base::FeatureList::IsEnabled(
+ features::kPasswordGenerationRequirements);
+
+ if (!base::FeatureList::IsEnabled(
+ features::kPasswordGenerationRequirements) &&
+ !base::FeatureList::IsEnabled(::features::kExperimentalUi)) {
+ return nullptr;
+ }
+
+ bool enable_domain_overrides =
+ base::FeatureList::IsEnabled(
+ features::kPasswordGenerationRequirementsDomainOverrides) ||
+ base::FeatureList::IsEnabled(::features::kExperimentalUi);
+
+ VLOG(1)
+ << "PasswordGenerationRequirementsDomainOverrides experiment enabled? "
+ << enable_domain_overrides;
+
+ if (!enable_domain_overrides)
+ return new PasswordRequirementsService(nullptr);
+
+ // Default parameters.
+ int version = 0;
+ int prefix_length = 0;
+ int timeout_in_ms = 5000;
+
+ // Override defaults with parameters from field trial if defined.
+ std::map<std::string, std::string> field_trial_params;
+ base::GetFieldTrialParams(features::kGenerationRequirementsFieldTrial,
+ &field_trial_params);
+ // base::StringToInt modifies the target even if it fails to parse the input.
+ // |tmp| is used to protect the default values above.
+ int tmp = 0;
+ if (base::StringToInt(
+ field_trial_params[features::kGenerationRequirementsVersion], &tmp)) {
+ version = tmp;
+ }
+ if (base::StringToInt(
+ field_trial_params[features::kGenerationRequirementsPrefixLength],
+ &tmp)) {
+ prefix_length = tmp;
+ }
+ if (base::StringToInt(
+ field_trial_params[features::kGenerationRequirementsTimeout], &tmp)) {
+ timeout_in_ms = tmp;
+ }
+
+ // If no experiment configuration is set to a user with experimental-ui flag,
+ // set a default that exercises the full new code.
+ if (version == 0 &&
+ base::FeatureList::IsEnabled(::features::kExperimentalUi)) {
+ version = 1;
+ prefix_length = 0;
+ timeout_in_ms = 5000;
+ }
+
+ VLOG(1) << "PasswordGenerationRequirements parameters: " << version << ", "
+ << prefix_length << ", " << timeout_in_ms << " ms";
+
+ std::unique_ptr<autofill::PasswordRequirementsSpecFetcher> fetcher =
+ std::make_unique<autofill::PasswordRequirementsSpecFetcherImpl>(
+ content::BrowserContext::GetDefaultStoragePartition(context)
+ ->GetURLLoaderFactoryForBrowserProcess(),
+ version, prefix_length, timeout_in_ms);
+ return new PasswordRequirementsService(std::move(fetcher));
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/content/browser/password_requirements_service_factory.h b/chromium/components/password_manager/content/browser/password_requirements_service_factory.h
new file mode 100644
index 00000000000..b47063cb2fd
--- /dev/null
+++ b/chromium/components/password_manager/content/browser/password_requirements_service_factory.h
@@ -0,0 +1,50 @@
+// 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_PASSWORD_MANAGER_CONTENT_BROWSER_PASSWORD_REQUIREMENTS_SERVICE_FACTORY_H_
+#define COMPONENTS_PASSWORD_MANAGER_CONTENT_BROWSER_PASSWORD_REQUIREMENTS_SERVICE_FACTORY_H_
+
+#include "base/macros.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}
+
+namespace content {
+class BrowserContext;
+}
+
+namespace password_manager {
+
+class PasswordRequirementsService;
+
+class PasswordRequirementsServiceFactory
+ : public BrowserContextKeyedServiceFactory {
+ public:
+ static PasswordRequirementsServiceFactory* GetInstance();
+
+ // Returns the PasswordRequirementsService associated with |context|.
+ // This may be nullptr for an incognito |context|.
+ static PasswordRequirementsService* GetForBrowserContext(
+ content::BrowserContext* context);
+
+ private:
+ friend struct base::DefaultSingletonTraits<
+ PasswordRequirementsServiceFactory>;
+
+ PasswordRequirementsServiceFactory();
+ ~PasswordRequirementsServiceFactory() override;
+
+ // BrowserContextKeyedServiceFactory overrides:
+ KeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* context) const override;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordRequirementsServiceFactory);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CONTENT_BROWSER_PASSWORD_REQUIREMENTS_SERVICE_FACTORY_H_
diff --git a/chromium/components/password_manager/content/common/credential_manager.typemap b/chromium/components/password_manager/content/common/credential_manager.typemap
index 9380a7cdf03..8c871093777 100644
--- a/chromium/components/password_manager/content/common/credential_manager.typemap
+++ b/chromium/components/password_manager/content/common/credential_manager.typemap
@@ -15,8 +15,8 @@ deps = [
]
type_mappings = [
- "password_manager.mojom.CredentialType=password_manager::CredentialType",
- "password_manager.mojom.CredentialMediationRequirement=password_manager::CredentialMediationRequirement",
- "password_manager.mojom.CredentialInfo=password_manager::CredentialInfo",
- "password_manager.mojom.CredentialManagerError=password_manager::CredentialManagerError",
+ "blink.mojom.CredentialType=password_manager::CredentialType",
+ "blink.mojom.CredentialMediationRequirement=password_manager::CredentialMediationRequirement",
+ "blink.mojom.CredentialInfo=password_manager::CredentialInfo",
+ "blink.mojom.CredentialManagerError=password_manager::CredentialManagerError",
]
diff --git a/chromium/components/password_manager/content/common/credential_manager_mojom_traits.cc b/chromium/components/password_manager/content/common/credential_manager_mojom_traits.cc
index e5793d1b465..21cbdedd471 100644
--- a/chromium/components/password_manager/content/common/credential_manager_mojom_traits.cc
+++ b/chromium/components/password_manager/content/common/credential_manager_mojom_traits.cc
@@ -11,36 +11,35 @@
namespace mojo {
// static
-password_manager::mojom::CredentialType EnumTraits<
- password_manager::mojom::CredentialType,
- password_manager::CredentialType>::ToMojom(password_manager::CredentialType
- input) {
+blink::mojom::CredentialType
+EnumTraits<blink::mojom::CredentialType, password_manager::CredentialType>::
+ ToMojom(password_manager::CredentialType input) {
switch (input) {
case password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY:
- return password_manager::mojom::CredentialType::EMPTY;
+ return blink::mojom::CredentialType::EMPTY;
case password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD:
- return password_manager::mojom::CredentialType::PASSWORD;
+ return blink::mojom::CredentialType::PASSWORD;
case password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED:
- return password_manager::mojom::CredentialType::FEDERATED;
+ return blink::mojom::CredentialType::FEDERATED;
}
NOTREACHED();
- return password_manager::mojom::CredentialType::EMPTY;
+ return blink::mojom::CredentialType::EMPTY;
}
// static
-bool EnumTraits<password_manager::mojom::CredentialType,
+bool EnumTraits<blink::mojom::CredentialType,
password_manager::CredentialType>::
- FromMojom(password_manager::mojom::CredentialType input,
+ FromMojom(blink::mojom::CredentialType input,
password_manager::CredentialType* output) {
switch (input) {
- case password_manager::mojom::CredentialType::EMPTY:
+ case blink::mojom::CredentialType::EMPTY:
*output = password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY;
return true;
- case password_manager::mojom::CredentialType::PASSWORD:
+ case blink::mojom::CredentialType::PASSWORD:
*output = password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD;
return true;
- case password_manager::mojom::CredentialType::FEDERATED:
+ case blink::mojom::CredentialType::FEDERATED:
*output = password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED;
return true;
}
@@ -50,58 +49,53 @@ bool EnumTraits<password_manager::mojom::CredentialType,
}
// static
-password_manager::mojom::CredentialManagerError
-EnumTraits<password_manager::mojom::CredentialManagerError,
+blink::mojom::CredentialManagerError
+EnumTraits<blink::mojom::CredentialManagerError,
password_manager::CredentialManagerError>::
ToMojom(password_manager::CredentialManagerError input) {
switch (input) {
case password_manager::CredentialManagerError::SUCCESS:
- return password_manager::mojom::CredentialManagerError::SUCCESS;
+ return blink::mojom::CredentialManagerError::SUCCESS;
case password_manager::CredentialManagerError::PENDING_REQUEST:
- return password_manager::mojom::CredentialManagerError::PENDING_REQUEST;
+ return blink::mojom::CredentialManagerError::PENDING_REQUEST;
case password_manager::CredentialManagerError::PASSWORDSTOREUNAVAILABLE:
- return password_manager::mojom::CredentialManagerError::
- PASSWORD_STORE_UNAVAILABLE;
+ return blink::mojom::CredentialManagerError::PASSWORD_STORE_UNAVAILABLE;
case password_manager::CredentialManagerError::UNKNOWN:
- return password_manager::mojom::CredentialManagerError::UNKNOWN;
+ return blink::mojom::CredentialManagerError::UNKNOWN;
}
NOTREACHED();
- return password_manager::mojom::CredentialManagerError::UNKNOWN;
+ return blink::mojom::CredentialManagerError::UNKNOWN;
}
// static
-bool EnumTraits<password_manager::mojom::CredentialManagerError,
+bool EnumTraits<blink::mojom::CredentialManagerError,
password_manager::CredentialManagerError>::
- FromMojom(password_manager::mojom::CredentialManagerError input,
+ FromMojom(blink::mojom::CredentialManagerError input,
password_manager::CredentialManagerError* output) {
switch (input) {
- case password_manager::mojom::CredentialManagerError::SUCCESS:
+ case blink::mojom::CredentialManagerError::SUCCESS:
*output = password_manager::CredentialManagerError::SUCCESS;
return true;
- case password_manager::mojom::CredentialManagerError::PENDING_REQUEST:
+ case blink::mojom::CredentialManagerError::PENDING_REQUEST:
*output = password_manager::CredentialManagerError::PENDING_REQUEST;
return true;
- case password_manager::mojom::CredentialManagerError::
- PASSWORD_STORE_UNAVAILABLE:
+ case blink::mojom::CredentialManagerError::PASSWORD_STORE_UNAVAILABLE:
*output =
password_manager::CredentialManagerError::PASSWORDSTOREUNAVAILABLE;
return true;
- case password_manager::mojom::CredentialManagerError::NOT_ALLOWED:
- case password_manager::mojom::CredentialManagerError::
- AUTHENTICATOR_CRITERIA_UNSUPPORTED:
- case password_manager::mojom::CredentialManagerError::ALGORITHM_UNSUPPORTED:
- case password_manager::mojom::CredentialManagerError::
- EMPTY_ALLOW_CREDENTIALS:
- case password_manager::mojom::CredentialManagerError::
- USER_VERIFICATION_UNSUPPORTED:
- case password_manager::mojom::CredentialManagerError::INVALID_DOMAIN:
- case password_manager::mojom::CredentialManagerError::CREDENTIAL_EXCLUDED:
- case password_manager::mojom::CredentialManagerError::
- CREDENTIAL_NOT_RECOGNIZED:
- case password_manager::mojom::CredentialManagerError::NOT_IMPLEMENTED:
- case password_manager::mojom::CredentialManagerError::NOT_FOCUSED:
- case password_manager::mojom::CredentialManagerError::UNKNOWN:
+ case blink::mojom::CredentialManagerError::NOT_ALLOWED:
+ case blink::mojom::CredentialManagerError::ANDROID_NOT_SUPPORTED_ERROR:
+ case blink::mojom::CredentialManagerError::ANDROID_ALGORITHM_UNSUPPORTED:
+ case blink::mojom::CredentialManagerError::ANDROID_EMPTY_ALLOW_CREDENTIALS:
+ case blink::mojom::CredentialManagerError::
+ ANDROID_USER_VERIFICATION_UNSUPPORTED:
+ case blink::mojom::CredentialManagerError::INVALID_DOMAIN:
+ case blink::mojom::CredentialManagerError::CREDENTIAL_EXCLUDED:
+ case blink::mojom::CredentialManagerError::CREDENTIAL_NOT_RECOGNIZED:
+ case blink::mojom::CredentialManagerError::NOT_IMPLEMENTED:
+ case blink::mojom::CredentialManagerError::NOT_FOCUSED:
+ case blink::mojom::CredentialManagerError::UNKNOWN:
*output = password_manager::CredentialManagerError::UNKNOWN;
return true;
}
@@ -111,36 +105,36 @@ bool EnumTraits<password_manager::mojom::CredentialManagerError,
}
// static
-password_manager::mojom::CredentialMediationRequirement
-EnumTraits<password_manager::mojom::CredentialMediationRequirement,
+blink::mojom::CredentialMediationRequirement
+EnumTraits<blink::mojom::CredentialMediationRequirement,
password_manager::CredentialMediationRequirement>::
ToMojom(password_manager::CredentialMediationRequirement input) {
switch (input) {
case password_manager::CredentialMediationRequirement::kSilent:
- return password_manager::mojom::CredentialMediationRequirement::kSilent;
+ return blink::mojom::CredentialMediationRequirement::kSilent;
case password_manager::CredentialMediationRequirement::kOptional:
- return password_manager::mojom::CredentialMediationRequirement::kOptional;
+ return blink::mojom::CredentialMediationRequirement::kOptional;
case password_manager::CredentialMediationRequirement::kRequired:
- return password_manager::mojom::CredentialMediationRequirement::kRequired;
+ return blink::mojom::CredentialMediationRequirement::kRequired;
}
NOTREACHED();
- return password_manager::mojom::CredentialMediationRequirement::kOptional;
+ return blink::mojom::CredentialMediationRequirement::kOptional;
}
// static
-bool EnumTraits<password_manager::mojom::CredentialMediationRequirement,
+bool EnumTraits<blink::mojom::CredentialMediationRequirement,
password_manager::CredentialMediationRequirement>::
- FromMojom(password_manager::mojom::CredentialMediationRequirement input,
+ FromMojom(blink::mojom::CredentialMediationRequirement input,
password_manager::CredentialMediationRequirement* output) {
switch (input) {
- case password_manager::mojom::CredentialMediationRequirement::kSilent:
+ case blink::mojom::CredentialMediationRequirement::kSilent:
*output = password_manager::CredentialMediationRequirement::kSilent;
return true;
- case password_manager::mojom::CredentialMediationRequirement::kOptional:
+ case blink::mojom::CredentialMediationRequirement::kOptional:
*output = password_manager::CredentialMediationRequirement::kOptional;
return true;
- case password_manager::mojom::CredentialMediationRequirement::kRequired:
+ case blink::mojom::CredentialMediationRequirement::kRequired:
*output = password_manager::CredentialMediationRequirement::kRequired;
return true;
}
@@ -150,9 +144,9 @@ bool EnumTraits<password_manager::mojom::CredentialMediationRequirement,
}
// static
-bool StructTraits<password_manager::mojom::CredentialInfoDataView,
+bool StructTraits<blink::mojom::CredentialInfoDataView,
password_manager::CredentialInfo>::
- Read(password_manager::mojom::CredentialInfoDataView data,
+ Read(blink::mojom::CredentialInfoDataView data,
password_manager::CredentialInfo* out) {
if (data.ReadType(&out->type) && data.ReadId(&out->id) &&
data.ReadName(&out->name) && data.ReadIcon(&out->icon) &&
diff --git a/chromium/components/password_manager/content/common/credential_manager_mojom_traits.h b/chromium/components/password_manager/content/common/credential_manager_mojom_traits.h
index 20c6b2635e4..d7b82804075 100644
--- a/chromium/components/password_manager/content/common/credential_manager_mojom_traits.h
+++ b/chromium/components/password_manager/content/common/credential_manager_mojom_traits.h
@@ -14,35 +14,35 @@
namespace mojo {
template <>
-struct EnumTraits<password_manager::mojom::CredentialType,
+struct EnumTraits<blink::mojom::CredentialType,
password_manager::CredentialType> {
- static password_manager::mojom::CredentialType ToMojom(
+ static blink::mojom::CredentialType ToMojom(
password_manager::CredentialType input);
- static bool FromMojom(password_manager::mojom::CredentialType input,
+ static bool FromMojom(blink::mojom::CredentialType input,
password_manager::CredentialType* output);
};
template <>
-struct EnumTraits<password_manager::mojom::CredentialManagerError,
+struct EnumTraits<blink::mojom::CredentialManagerError,
password_manager::CredentialManagerError> {
- static password_manager::mojom::CredentialManagerError ToMojom(
+ static blink::mojom::CredentialManagerError ToMojom(
password_manager::CredentialManagerError input);
- static bool FromMojom(password_manager::mojom::CredentialManagerError input,
+ static bool FromMojom(blink::mojom::CredentialManagerError input,
password_manager::CredentialManagerError* output);
};
template <>
-struct EnumTraits<password_manager::mojom::CredentialMediationRequirement,
+struct EnumTraits<blink::mojom::CredentialMediationRequirement,
password_manager::CredentialMediationRequirement> {
- static password_manager::mojom::CredentialMediationRequirement ToMojom(
+ static blink::mojom::CredentialMediationRequirement ToMojom(
password_manager::CredentialMediationRequirement input);
static bool FromMojom(
- password_manager::mojom::CredentialMediationRequirement input,
+ blink::mojom::CredentialMediationRequirement input,
password_manager::CredentialMediationRequirement* output);
};
template <>
-struct StructTraits<password_manager::mojom::CredentialInfoDataView,
+struct StructTraits<blink::mojom::CredentialInfoDataView,
password_manager::CredentialInfo> {
static password_manager::CredentialType type(
const password_manager::CredentialInfo& r) {
@@ -73,7 +73,7 @@ struct StructTraits<password_manager::mojom::CredentialInfoDataView,
return r.federation;
}
- static bool Read(password_manager::mojom::CredentialInfoDataView data,
+ static bool Read(blink::mojom::CredentialInfoDataView data,
password_manager::CredentialInfo* out);
};
diff --git a/chromium/components/password_manager/core/browser/BUILD.gn b/chromium/components/password_manager/core/browser/BUILD.gn
index e26d296486d..d8e57da2dfd 100644
--- a/chromium/components/password_manager/core/browser/BUILD.gn
+++ b/chromium/components/password_manager/core/browser/BUILD.gn
@@ -59,6 +59,7 @@ static_library("browser") {
"form_saver.h",
"form_saver_impl.cc",
"form_saver_impl.h",
+ "form_submission_observer.h",
"http_password_store_migrator.cc",
"http_password_store_migrator.h",
"import/csv_reader.cc",
@@ -115,6 +116,8 @@ static_library("browser") {
"password_manager_metrics_util.h",
"password_manager_util.cc",
"password_manager_util.h",
+ "password_requirements_service.cc",
+ "password_requirements_service.h",
"password_reuse_defines.h",
"password_store.cc",
"password_store.h",
@@ -136,8 +139,6 @@ static_library("browser") {
"psl_matching_helper.h",
"site_affiliation/asset_link_data.cc",
"site_affiliation/asset_link_data.h",
- "site_affiliation/asset_link_retriever.cc",
- "site_affiliation/asset_link_retriever.h",
"sql_table_builder.cc",
"sql_table_builder.h",
"statistics_table.cc",
@@ -148,6 +149,8 @@ static_library("browser") {
"ui/export_flow.h",
"ui/export_progress_status.h",
"ui/import_flow.h",
+ "votes_uploader.cc",
+ "votes_uploader.h",
"webdata/logins_table.cc",
"webdata/logins_table.h",
"webdata/logins_table_win.cc",
@@ -178,11 +181,13 @@ static_library("browser") {
]
deps = [
":hash_password_manager",
+ ":password_hash_data",
":proto",
"//base:i18n",
"//components/autofill/core/browser",
"//components/autofill/core/browser/proto",
"//components/autofill/core/common",
+ "//components/favicon/core",
"//components/keyed_service/core",
"//components/os_crypt",
"//components/password_manager/core/browser/form_parsing",
@@ -199,6 +204,7 @@ static_library("browser") {
"//net",
"//services/metrics/public/cpp:metrics_cpp",
"//services/metrics/public/cpp:ukm_builders",
+ "//services/network/public/cpp",
"//sql",
"//third_party/protobuf:protobuf_lite",
"//third_party/re2",
@@ -207,18 +213,12 @@ static_library("browser") {
"//url",
]
- if (password_reuse_detection_support) {
- deps += [
- "//components/safe_browsing:features",
- "//components/safe_browsing/common:safe_browsing_prefs",
- ]
- }
-
if (!is_ios) {
sources += [
"hsts_query.cc",
"hsts_query.h",
]
+ deps += [ "//components/safe_browsing/common:safe_browsing_prefs" ]
}
if (is_posix && !is_mac && !is_ios) {
@@ -235,17 +235,29 @@ proto_library("proto") {
]
}
+static_library("password_hash_data") {
+ sources = [
+ "password_hash_data.cc",
+ "password_hash_data.h",
+ ]
+ deps = [
+ "//base",
+ "//crypto",
+ "//google_apis",
+ ]
+}
+
static_library("hash_password_manager") {
sources = [
"hash_password_manager.cc",
"hash_password_manager.h",
]
deps = [
+ ":password_hash_data",
"//base:base",
"//components/os_crypt",
"//components/password_manager/core/common",
"//components/prefs",
- "//crypto",
]
}
@@ -284,6 +296,7 @@ static_library("test_support") {
":browser",
":hash_password_manager",
"//components/ukm",
+ "//services/network/public/cpp",
"//testing/gmock",
"//url:url",
]
@@ -366,10 +379,12 @@ source_set("unit_tests") {
"password_form_manager_unittest.cc",
"password_form_metrics_recorder_unittest.cc",
"password_generation_manager_unittest.cc",
+ "password_hash_data_unittest.cc",
"password_list_sorter_unittest.cc",
"password_manager_metrics_recorder_unittest.cc",
"password_manager_unittest.cc",
"password_manager_util_unittest.cc",
+ "password_requirements_service_unittest.cc",
"password_store_default_unittest.cc",
"password_store_origin_unittest.h",
"password_store_unittest.cc",
@@ -377,10 +392,11 @@ source_set("unit_tests") {
"password_ui_utils_unittest.cc",
"psl_matching_helper_unittest.cc",
"site_affiliation/asset_link_data_unittest.cc",
- "site_affiliation/asset_link_retriever_unittest.cc",
"sql_table_builder_unittest.cc",
"statistics_table_unittest.cc",
"suppressed_form_fetcher_unittest.cc",
+ "vote_uploads_test_matchers.h",
+ "votes_uploader_unittest.cc",
]
if (is_mac) {
sources -= [ "password_store_default_unittest.cc" ]
@@ -403,12 +419,14 @@ source_set("unit_tests") {
deps = [
":hash_password_manager",
+ ":password_hash_data",
":test_support",
":unit_tests_bundle_data",
"//base/test:test_support",
"//components/autofill/core/browser:test_support",
"//components/autofill/core/browser/proto",
"//components/autofill/core/common",
+ "//components/favicon/core/test:test_support",
"//components/os_crypt",
"//components/os_crypt:test_support",
"//components/password_manager/core/browser:proto",
@@ -422,12 +440,16 @@ source_set("unit_tests") {
"//components/sync:test_support_model",
"//components/ukm:test_support",
"//components/variations",
+ "//google_apis:google_apis",
"//net:test_support",
"//services/metrics/public/cpp:ukm_builders",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//sql:test_support",
"//testing/gmock",
"//testing/gtest",
"//ui/base",
+ "//ui/gfx:test_support",
"//url",
]
diff --git a/chromium/components/password_manager/core/browser/DEPS b/chromium/components/password_manager/core/browser/DEPS
index b62763e00cd..dc8e2ace0b4 100644
--- a/chromium/components/password_manager/core/browser/DEPS
+++ b/chromium/components/password_manager/core/browser/DEPS
@@ -1,5 +1,6 @@
include_rules = [
"+components/autofill/core/browser",
+ "+components/favicon/core",
"+components/keyed_service/core",
"+components/pref_registry",
"+components/security_state",
@@ -15,6 +16,8 @@ include_rules = [
"+grit",
"+net",
"+services/metrics/public/cpp",
+ "+services/network/public/cpp",
+ "+services/network/test",
]
specific_include_rules = {
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend.cc
index 8e86a50dea3..4d6014e0155 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend.cc
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend.cc
@@ -21,16 +21,15 @@
#include "components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h"
#include "components/password_manager/core/browser/android_affiliation/facet_manager.h"
#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace password_manager {
AffiliationBackend::AffiliationBackend(
- const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
const scoped_refptr<base::SequencedTaskRunner>& task_runner,
base::Clock* time_source,
const base::TickClock* time_tick_source)
- : request_context_getter_(request_context_getter),
- task_runner_(task_runner),
+ : task_runner_(task_runner),
clock_(time_source),
tick_clock_(time_tick_source),
construction_time_(clock_->Now()),
@@ -42,7 +41,10 @@ AffiliationBackend::AffiliationBackend(
AffiliationBackend::~AffiliationBackend() {
}
-void AffiliationBackend::Initialize(const base::FilePath& db_path) {
+void AffiliationBackend::Initialize(
+ std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+ url_loader_factory_info,
+ const base::FilePath& db_path) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!throttler_);
throttler_.reset(
@@ -54,6 +56,10 @@ void AffiliationBackend::Initialize(const base::FilePath& db_path) {
// return value here. See: https://crbug.com/478831.
cache_.reset(new AffiliationDatabase());
cache_->Init(db_path);
+ DCHECK(url_loader_factory_info);
+ DCHECK(!url_loader_factory_);
+ url_loader_factory_ = network::SharedURLLoaderFactory::Create(
+ std::move(url_loader_factory_info));
}
void AffiliationBackend::GetAffiliationsAndBranding(
@@ -252,7 +258,7 @@ bool AffiliationBackend::OnCanSendNetworkRequest() {
if (requested_facet_uris.empty())
return false;
- fetcher_.reset(AffiliationFetcher::Create(request_context_getter_.get(),
+ fetcher_.reset(AffiliationFetcher::Create(url_loader_factory_,
requested_facet_uris, this));
fetcher_->StartRequest();
ReportStatistics(requested_facet_uris.size());
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend.h b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend.h
index 79c17d65b8e..3c38dcaec80 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend.h
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend.h
@@ -32,9 +32,9 @@ class TickClock;
class Time;
} // namespace base
-namespace net {
-class URLRequestContextGetter;
-} // namespace net
+namespace network {
+class SharedURLLoaderFactoryInfo;
+} // namespace network
namespace password_manager {
@@ -57,12 +57,11 @@ class AffiliationBackend : public FacetManagerHost,
public:
using StrategyOnCacheMiss = AffiliationService::StrategyOnCacheMiss;
- // Constructs an instance that will use |request_context_getter| for all
+ // Constructs an instance that will use |url_loader_factory| for all
// network requests, use |task_runner| for asynchronous tasks, and will rely
// on |time_source| and |time_tick_source| to tell the current time/ticks.
// Construction is very cheap, expensive steps are deferred to Initialize().
AffiliationBackend(
- const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
const scoped_refptr<base::SequencedTaskRunner>& task_runner,
base::Clock* time_source,
const base::TickClock* time_tick_source);
@@ -70,7 +69,9 @@ class AffiliationBackend : public FacetManagerHost,
// Performs the I/O-heavy part of initialization. The database used to cache
// affiliation information locally will be opened/created at |db_path|.
- void Initialize(const base::FilePath& db_path);
+ void Initialize(std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+ url_loader_factory_info,
+ const base::FilePath& db_path);
// Implementations for methods of the same name in AffiliationService. They
// are not documented here again. See affiliation_service.h for details:
@@ -140,7 +141,7 @@ class AffiliationBackend : public FacetManagerHost,
// sequence.
SEQUENCE_CHECKER(sequence_checker_);
- scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
base::Clock* clock_;
const base::TickClock* tick_clock_;
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend_unittest.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend_unittest.cc
index 127845381ff..91586206bfb 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend_unittest.cc
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_backend_unittest.cc
@@ -12,6 +12,7 @@
#include "base/files/file_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/test/scoped_task_environment.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/test/test_simple_task_runner.h"
#include "base/time/clock.h"
@@ -23,6 +24,9 @@
#include "components/password_manager/core/browser/android_affiliation/fake_affiliation_api.h"
#include "components/password_manager/core/browser/android_affiliation/mock_affiliation_consumer.h"
#include "net/url_request/url_request_context_getter.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_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -333,9 +337,12 @@ class AffiliationBackendTest : public testing::Test {
void SetUp() override {
ASSERT_TRUE(CreateTemporaryFile(&db_path_));
backend_.reset(new AffiliationBackend(
- nullptr, backend_task_runner_, backend_task_runner_->GetMockClock(),
+ backend_task_runner_, backend_task_runner_->GetMockClock(),
backend_task_runner_->GetMockTickClock()));
- backend_->Initialize(db_path());
+ auto test_shared_loader_factory =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_);
+ backend_->Initialize(test_shared_loader_factory->Clone(), db_path());
auto mock_fetch_throttler =
std::make_unique<MockAffiliationFetchThrottler>(backend_.get());
mock_fetch_throttler_ = mock_fetch_throttler.get();
@@ -349,6 +356,7 @@ class AffiliationBackendTest : public testing::Test {
GetTestEquivalenceClassGamma());
}
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
scoped_refptr<base::TestMockTimeTaskRunner> backend_task_runner_;
scoped_refptr<base::TestSimpleTaskRunner> consumer_task_runner_;
@@ -356,6 +364,7 @@ class AffiliationBackendTest : public testing::Test {
ScopedFakeAffiliationAPI fake_affiliation_api_;
MockAffiliationConsumer mock_consumer_;
std::unique_ptr<AffiliationBackend> backend_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
MockAffiliationFetchThrottler* mock_fetch_throttler_; // Owned by |backend_|.
DISALLOW_COPY_AND_ASSIGN(AffiliationBackendTest);
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.cc
index edeae34993f..0f797e417b1 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.cc
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.cc
@@ -64,12 +64,12 @@ AffiliationFetchThrottler::AffiliationFetchThrottler(
DCHECK(delegate);
// Start observing before querying the current connectivity state, so that if
// the state changes concurrently in-between, it will not go unnoticed.
- net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
+ net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
has_network_connectivity_ = !net::NetworkChangeNotifier::IsOffline();
}
AffiliationFetchThrottler::~AffiliationFetchThrottler() {
- net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
+ net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
}
void AffiliationFetchThrottler::SignalNetworkRequestNeeded() {
@@ -108,7 +108,7 @@ void AffiliationFetchThrottler::OnBackoffDelayExpiredCallback() {
is_fetch_scheduled_ = false;
// Do nothing if network connectivity was lost while this callback was in the
- // task queue. The callback will be posted in the OnConnectionTypeChanged
+ // task queue. The callback will be posted in the OnNetworkChanged
// handler once again.
if (!has_network_connectivity_)
return;
@@ -121,7 +121,7 @@ void AffiliationFetchThrottler::OnBackoffDelayExpiredCallback() {
state_ = delegate_->OnCanSendNetworkRequest() ? FETCH_IN_FLIGHT : IDLE;
}
-void AffiliationFetchThrottler::OnConnectionTypeChanged(
+void AffiliationFetchThrottler::OnNetworkChanged(
net::NetworkChangeNotifier::ConnectionType type) {
bool old_has_network_connectivity = has_network_connectivity_;
has_network_connectivity_ =
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.h b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.h
index 3cf52739426..7776c1bd2b3 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.h
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.h
@@ -50,7 +50,7 @@ class AffiliationFetchThrottlerDelegate;
// periods, so that requests will not be held back for too long after
// connectivity is restored.
class AffiliationFetchThrottler
- : public net::NetworkChangeNotifier::ConnectionTypeObserver {
+ : public net::NetworkChangeNotifier::NetworkChangeObserver {
public:
// Creates an instance that will use |tick_clock| as its tick source, and will
// post to |task_runner| to call the |delegate|'s OnSendNetworkRequest(). The
@@ -114,8 +114,8 @@ class AffiliationFetchThrottler
// Called back when the |exponential_backoff_| delay expires.
void OnBackoffDelayExpiredCallback();
- // net::NetworkChangeNotifier::ConnectionTypeObserver:
- void OnConnectionTypeChanged(
+ // net::NetworkChangeNotifier::NetworkChangeObserver:
+ void OnNetworkChanged(
net::NetworkChangeNotifier::ConnectionType type) override;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc
index 83e9f38d65b..e0daa1a8bb4 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.cc
@@ -21,14 +21,13 @@
#include "net/base/url_util.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.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"
namespace password_manager {
-namespace {
-
// Enumeration listing the possible outcomes of fetching affiliation information
// from the Affiliation API. This is used in UMA histograms, so do not change
// existing values, only add new values at the end.
@@ -39,32 +38,13 @@ enum AffiliationFetchResult {
AFFILIATION_FETCH_RESULT_MAX
};
-// Records the given fetch |result| into the respective UMA histogram, as well
-// as the response and error codes of |fetcher| if it is non-null.
-void ReportStatistics(AffiliationFetchResult result,
- const net::URLFetcher* fetcher) {
- UMA_HISTOGRAM_ENUMERATION("PasswordManager.AffiliationFetcher.FetchResult",
- result, AFFILIATION_FETCH_RESULT_MAX);
- if (fetcher) {
- base::UmaHistogramSparse(
- "PasswordManager.AffiliationFetcher.FetchHttpResponseCode",
- fetcher->GetResponseCode());
- // Network error codes are negative. See: src/net/base/net_error_list.h.
- base::UmaHistogramSparse(
- "PasswordManager.AffiliationFetcher.FetchErrorCode",
- -fetcher->GetStatus().error());
- }
-}
-
-} // namespace
-
static TestAffiliationFetcherFactory* g_testing_factory = nullptr;
AffiliationFetcher::AffiliationFetcher(
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const std::vector<FacetURI>& facet_uris,
AffiliationFetcherDelegate* delegate)
- : request_context_getter_(request_context_getter),
+ : url_loader_factory_(std::move(url_loader_factory)),
requested_facet_uris_(facet_uris),
delegate_(delegate) {
for (const FacetURI& uri : requested_facet_uris_) {
@@ -72,19 +52,19 @@ AffiliationFetcher::AffiliationFetcher(
}
}
-AffiliationFetcher::~AffiliationFetcher() {
-}
+AffiliationFetcher::~AffiliationFetcher() = default;
// static
AffiliationFetcher* AffiliationFetcher::Create(
- net::URLRequestContextGetter* context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const std::vector<FacetURI>& facet_uris,
AffiliationFetcherDelegate* delegate) {
if (g_testing_factory) {
- return g_testing_factory->CreateInstance(context_getter, facet_uris,
- delegate);
+ return g_testing_factory->CreateInstance(std::move(url_loader_factory),
+ facet_uris, delegate);
}
- return new AffiliationFetcher(context_getter, facet_uris, delegate);
+ return new AffiliationFetcher(std::move(url_loader_factory), facet_uris,
+ delegate);
}
// static
@@ -94,7 +74,7 @@ void AffiliationFetcher::SetFactoryForTesting(
}
void AffiliationFetcher::StartRequest() {
- DCHECK(!fetcher_);
+ DCHECK(!simple_url_loader_);
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("affiliation_lookup", R"(
@@ -127,20 +107,25 @@ void AffiliationFetcher::StartRequest() {
}
}
})");
- fetcher_ = net::URLFetcher::Create(BuildQueryURL(), net::URLFetcher::POST,
- this, traffic_annotation);
- fetcher_->SetRequestContext(request_context_getter_.get());
- fetcher_->SetUploadData("application/x-protobuf", PreparePayload());
- fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SEND_AUTH_DATA |
- net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE);
- fetcher_->SetAutomaticallyRetryOn5xx(false);
- fetcher_->SetAutomaticallyRetryOnNetworkChanges(0);
- fetcher_->Start();
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = BuildQueryURL();
+ resource_request->load_flags =
+ net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SEND_AUTH_DATA | net::LOAD_BYPASS_CACHE |
+ net::LOAD_DISABLE_CACHE;
+ resource_request->method = "POST";
+ simple_url_loader_ = network::SimpleURLLoader::Create(
+ std::move(resource_request), traffic_annotation);
+ simple_url_loader_->AttachStringForUpload(PreparePayload(),
+ "application/x-protobuf");
+ simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&AffiliationFetcher::OnSimpleLoaderComplete,
+ base::Unretained(this)));
}
-GURL AffiliationFetcher::BuildQueryURL() const {
+// static
+GURL AffiliationFetcher::BuildQueryURL() {
return net::AppendQueryParameter(
GURL("https://www.googleapis.com/affiliation/v1/affiliation:lookup"),
"key", google_apis::GetAPIKey());
@@ -163,6 +148,7 @@ std::string AffiliationFetcher::PreparePayload() const {
}
bool AffiliationFetcher::ParseResponse(
+ const std::string& serialized_response,
AffiliationFetcherDelegate::Result* result) const {
// This function parses the response protocol buffer message for a list of
// equivalence classes, and stores them into |results| after performing some
@@ -178,11 +164,6 @@ bool AffiliationFetcher::ParseResponse(
// case so the caller can be notified and it can act accordingly.
// * The |result| will be free of duplicate or empty equivalence classes.
- std::string serialized_response;
- if (!fetcher_->GetResponseAsString(&serialized_response)) {
- NOTREACHED();
- }
-
affiliation_pb::LookupAffiliationResponse response;
if (!response.ParseFromString(serialized_response))
return false;
@@ -238,24 +219,41 @@ bool AffiliationFetcher::ParseResponse(
return true;
}
-void AffiliationFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK_EQ(source, fetcher_.get());
-
+void AffiliationFetcher::OnSimpleLoaderComplete(
+ std::unique_ptr<std::string> response_body) {
// Note that invoking the |delegate_| may destroy |this| synchronously, so the
// invocation must happen last.
std::unique_ptr<AffiliationFetcherDelegate::Result> result_data(
new AffiliationFetcherDelegate::Result);
- if (fetcher_->GetStatus().status() == net::URLRequestStatus::SUCCESS &&
- fetcher_->GetResponseCode() == net::HTTP_OK) {
- if (ParseResponse(result_data.get())) {
- ReportStatistics(AFFILIATION_FETCH_RESULT_SUCCESS, nullptr);
+ if (response_body) {
+ if (ParseResponse(*response_body, result_data.get())) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordManager.AffiliationFetcher.FetchResult",
+ AFFILIATION_FETCH_RESULT_SUCCESS, AFFILIATION_FETCH_RESULT_MAX);
delegate_->OnFetchSucceeded(std::move(result_data));
} else {
- ReportStatistics(AFFILIATION_FETCH_RESULT_MALFORMED, nullptr);
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordManager.AffiliationFetcher.FetchResult",
+ AFFILIATION_FETCH_RESULT_MALFORMED, AFFILIATION_FETCH_RESULT_MAX);
delegate_->OnMalformedResponse();
}
} else {
- ReportStatistics(AFFILIATION_FETCH_RESULT_FAILURE, fetcher_.get());
+ UMA_HISTOGRAM_ENUMERATION("PasswordManager.AffiliationFetcher.FetchResult",
+ AFFILIATION_FETCH_RESULT_FAILURE,
+ AFFILIATION_FETCH_RESULT_MAX);
+ int response_code = -1;
+ if (simple_url_loader_->ResponseInfo() &&
+ simple_url_loader_->ResponseInfo()->headers) {
+ response_code =
+ simple_url_loader_->ResponseInfo()->headers->response_code();
+ }
+ base::UmaHistogramSparse(
+ "PasswordManager.AffiliationFetcher.FetchHttpResponseCode",
+ response_code);
+ // Network error codes are negative. See: src/net/base/net_error_list.h.
+ base::UmaHistogramSparse(
+ "PasswordManager.AffiliationFetcher.FetchErrorCode",
+ -simple_url_loader_->NetError());
delegate_->OnFetchFailed();
}
}
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h
index 2199744f6da..2c220e09186 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher.h
@@ -13,13 +13,13 @@
#include "base/memory/ref_counted.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_fetcher_delegate.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
-#include "net/url_request/url_fetcher_delegate.h"
class GURL;
-namespace net {
-class URLRequestContextGetter;
-} // namespace net
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+} // namespace network
namespace password_manager {
@@ -31,18 +31,21 @@ class TestAffiliationFetcherFactory;
//
// An instance is good for exactly one fetch, and may be used from any thread
// that runs a message loop (i.e. not a worker pool thread).
-class AffiliationFetcher : net::URLFetcherDelegate {
+class AffiliationFetcher {
public:
- ~AffiliationFetcher() override;
+ ~AffiliationFetcher();
// Constructs a fetcher to retrieve affiliations for each facet in |facet_ids|
- // using the specified |request_context_getter|, and will provide the results
+ // using the specified |url_loader_factory|, and will provide the results
// to the |delegate| on the same thread that creates the instance.
static AffiliationFetcher* Create(
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const std::vector<FacetURI>& facet_uris,
AffiliationFetcherDelegate* delegate);
+ // Builds the URL for the Affiliation API's lookup method.
+ static GURL BuildQueryURL();
+
// Sets the |factory| to be used by Create() to construct AffiliationFetcher
// instances. To be used only for testing.
//
@@ -57,7 +60,7 @@ class AffiliationFetcher : net::URLFetcherDelegate {
// * No cookies are sent/saved with the request.
// * In case of network/server errors, the request will not be retried.
// * Results are guaranteed to be always fresh and will never be cached.
- virtual void StartRequest();
+ void StartRequest();
const std::vector<FacetURI>& requested_facet_uris() const {
return requested_facet_uris_;
@@ -66,14 +69,12 @@ class AffiliationFetcher : net::URLFetcherDelegate {
AffiliationFetcherDelegate* delegate() const { return delegate_; }
protected:
- AffiliationFetcher(net::URLRequestContextGetter* request_context_getter,
- const std::vector<FacetURI>& facet_uris,
- AffiliationFetcherDelegate* delegate);
+ AffiliationFetcher(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ const std::vector<FacetURI>& facet_uris,
+ AffiliationFetcherDelegate* delegate);
private:
- // Builds the URL for the Affiliation API's lookup method.
- GURL BuildQueryURL() const;
-
// Prepares and returns the serialized protocol buffer message that will be
// the payload of the POST request.
std::string PreparePayload() const;
@@ -84,16 +85,16 @@ class AffiliationFetcher : net::URLFetcherDelegate {
// member of exactly one returned equivalence class.
// Returns false if the response was gravely ill-formed or self-inconsistent.
// Unknown kinds of facet URIs and new protocol buffer fields will be ignored.
- bool ParseResponse(AffiliationFetcherDelegate::Result* result) const;
+ bool ParseResponse(const std::string& serialized_response,
+ AffiliationFetcherDelegate::Result* result) const;
- // net::URLFetcherDelegate:
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
- const scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
const std::vector<FacetURI> requested_facet_uris_;
AffiliationFetcherDelegate* const delegate_;
- std::unique_ptr<net::URLFetcher> fetcher_;
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
DISALLOW_COPY_AND_ASSIGN(AffiliationFetcher);
};
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher_unittest.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher_unittest.cc
index 79351debdc7..1caccd27ced 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher_unittest.cc
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_fetcher_unittest.cc
@@ -9,10 +9,15 @@
#include <utility>
#include "base/macros.h"
+#include "base/test/bind_test_util.h"
#include "base/test/null_task_runner.h"
+#include "base/test/scoped_task_environment.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_api.pb.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.h"
+#include "net/base/url_util.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_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -55,25 +60,31 @@ class MockAffiliationFetcherDelegate
class AffiliationFetcherTest : public testing::Test {
public:
AffiliationFetcherTest()
- : request_context_getter_(new net::TestURLRequestContextGetter(
- base::MakeRefCounted<base::NullTaskRunner>())) {}
+ : test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)) {
+ test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting(
+ [&](const network::ResourceRequest& request) {
+ intercepted_body_ = network::GetUploadData(request);
+ intercepted_headers_ = request.headers;
+ }));
+ }
~AffiliationFetcherTest() override {}
protected:
void VerifyRequestPayload(const std::vector<FacetURI>& expected_facet_uris) {
- net::TestURLFetcher* url_fetcher =
- test_url_fetcher_factory_.GetFetcherByID(0);
- ASSERT_NE(nullptr, url_fetcher);
-
affiliation_pb::LookupAffiliationRequest request;
- ASSERT_TRUE(request.ParseFromString(url_fetcher->upload_data()));
+ ASSERT_TRUE(request.ParseFromString(intercepted_body_));
std::vector<FacetURI> actual_uris;
for (int i = 0; i < request.facet_size(); ++i)
actual_uris.push_back(FacetURI::FromCanonicalSpec(request.facet(i)));
- EXPECT_EQ("application/x-protobuf", url_fetcher->upload_content_type());
+ std::string content_type;
+ intercepted_headers_.GetHeader(net::HttpRequestHeaders::kContentType,
+ &content_type);
+ EXPECT_EQ("application/x-protobuf", content_type);
EXPECT_THAT(actual_uris,
testing::UnorderedElementsAreArray(expected_facet_uris));
@@ -82,41 +93,37 @@ class AffiliationFetcherTest : public testing::Test {
EXPECT_TRUE(request.mask().branding_info());
}
- void ServiceURLRequest(const std::string& response) {
- net::TestURLFetcher* url_fetcher =
- test_url_fetcher_factory_.GetFetcherByID(0);
- ASSERT_NE(nullptr, url_fetcher);
+ GURL interception_url() { return AffiliationFetcher::BuildQueryURL(); }
- url_fetcher->set_response_code(200);
- url_fetcher->SetResponseString(response);
- url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+ void SetupSuccessfulResponse(const std::string& response) {
+ test_url_loader_factory_.AddResponse(interception_url().spec(), response);
}
- void SimulateServerError() {
- net::TestURLFetcher* url_fetcher =
- test_url_fetcher_factory_.GetFetcherByID(0);
- ASSERT_NE(nullptr, url_fetcher);
-
- url_fetcher->set_response_code(500);
- url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+ void SetupServerErrorResponse() {
+ test_url_loader_factory_.AddResponse(interception_url().spec(), "",
+ net::HTTP_INTERNAL_SERVER_ERROR);
}
- void SimulateNetworkError() {
- net::TestURLFetcher* url_fetcher =
- test_url_fetcher_factory_.GetFetcherByID(0);
- ASSERT_NE(nullptr, url_fetcher);
- url_fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED,
- net::ERR_NETWORK_CHANGED));
- url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+ void SetupNetworkErrorResponse() {
+ network::ResourceResponseHead head =
+ network::CreateResourceResponseHead(net::HTTP_INTERNAL_SERVER_ERROR);
+ head.mime_type = "application/protobuf";
+ network::URLLoaderCompletionStatus status(net::ERR_NETWORK_CHANGED);
+ test_url_loader_factory_.AddResponse(interception_url(), head, "", status);
}
- net::TestURLRequestContextGetter* request_context_getter() {
- return request_context_getter_.get();
+ void WaitForResponse() { scoped_task_environment_.RunUntilIdle(); }
+
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory() {
+ return test_shared_loader_factory_;
}
private:
- scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
- net::TestURLFetcherFactory test_url_fetcher_factory_;
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+ std::string intercepted_body_;
+ net::HttpRequestHeaders intercepted_headers_;
DISALLOW_COPY_AND_ASSIGN(AffiliationFetcherTest);
};
@@ -140,14 +147,15 @@ TEST_F(AffiliationFetcherTest, BasicReqestAndResponse) {
requested_uris.push_back(
FacetURI::FromCanonicalSpec(kNotExampleAndroidFacetURI));
+ SetupSuccessfulResponse(test_response.SerializeAsString());
MockAffiliationFetcherDelegate mock_delegate;
+ EXPECT_CALL(mock_delegate, OnFetchSucceededProxy());
std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), requested_uris, &mock_delegate));
+ test_shared_loader_factory(), requested_uris, &mock_delegate));
fetcher->StartRequest();
+ WaitForResponse();
- EXPECT_CALL(mock_delegate, OnFetchSucceededProxy());
ASSERT_NO_FATAL_FAILURE(VerifyRequestPayload(requested_uris));
- ASSERT_NO_FATAL_FAILURE(ServiceURLRequest(test_response.SerializeAsString()));
ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_delegate));
ASSERT_EQ(2u, mock_delegate.result().size());
@@ -178,14 +186,15 @@ TEST_F(AffiliationFetcherTest, AndroidBrandingInfoIsReturnedIfPresent) {
std::vector<FacetURI> requested_uris;
requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+ SetupSuccessfulResponse(test_response.SerializeAsString());
MockAffiliationFetcherDelegate mock_delegate;
+ EXPECT_CALL(mock_delegate, OnFetchSucceededProxy());
std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), requested_uris, &mock_delegate));
+ test_shared_loader_factory(), requested_uris, &mock_delegate));
fetcher->StartRequest();
+ WaitForResponse();
- EXPECT_CALL(mock_delegate, OnFetchSucceededProxy());
ASSERT_NO_FATAL_FAILURE(VerifyRequestPayload(requested_uris));
- ASSERT_NO_FATAL_FAILURE(ServiceURLRequest(test_response.SerializeAsString()));
ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_delegate));
ASSERT_EQ(1u, mock_delegate.result().size());
@@ -208,15 +217,15 @@ TEST_F(AffiliationFetcherTest, MissingEquivalenceClassesAreCreated) {
std::vector<FacetURI> requested_uris;
requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+ SetupSuccessfulResponse(empty_test_response.SerializeAsString());
MockAffiliationFetcherDelegate mock_delegate;
+ EXPECT_CALL(mock_delegate, OnFetchSucceededProxy());
std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), requested_uris, &mock_delegate));
+ test_shared_loader_factory(), requested_uris, &mock_delegate));
fetcher->StartRequest();
+ WaitForResponse();
- EXPECT_CALL(mock_delegate, OnFetchSucceededProxy());
ASSERT_NO_FATAL_FAILURE(VerifyRequestPayload(requested_uris));
- ASSERT_NO_FATAL_FAILURE(
- ServiceURLRequest(empty_test_response.SerializeAsString()));
ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_delegate));
ASSERT_EQ(1u, mock_delegate.result().size());
@@ -239,13 +248,14 @@ TEST_F(AffiliationFetcherTest, DuplicateEquivalenceClassesAreIgnored) {
std::vector<FacetURI> requested_uris;
requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+ SetupSuccessfulResponse(test_response.SerializeAsString());
MockAffiliationFetcherDelegate mock_delegate;
+ EXPECT_CALL(mock_delegate, OnFetchSucceededProxy());
std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), requested_uris, &mock_delegate));
+ test_shared_loader_factory(), requested_uris, &mock_delegate));
fetcher->StartRequest();
+ WaitForResponse();
- EXPECT_CALL(mock_delegate, OnFetchSucceededProxy());
- ASSERT_NO_FATAL_FAILURE(ServiceURLRequest(test_response.SerializeAsString()));
ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_delegate));
ASSERT_EQ(1u, mock_delegate.result().size());
@@ -266,13 +276,14 @@ TEST_F(AffiliationFetcherTest, EmptyEquivalenceClassesAreIgnored) {
std::vector<FacetURI> requested_uris;
requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+ SetupSuccessfulResponse(test_response.SerializeAsString());
MockAffiliationFetcherDelegate mock_delegate;
+ EXPECT_CALL(mock_delegate, OnFetchSucceededProxy());
std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), requested_uris, &mock_delegate));
+ test_shared_loader_factory(), requested_uris, &mock_delegate));
fetcher->StartRequest();
+ WaitForResponse();
- EXPECT_CALL(mock_delegate, OnFetchSucceededProxy());
- ASSERT_NO_FATAL_FAILURE(ServiceURLRequest(test_response.SerializeAsString()));
ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_delegate));
ASSERT_EQ(1u, mock_delegate.result().size());
@@ -297,13 +308,14 @@ TEST_F(AffiliationFetcherTest, UnrecognizedFacetURIsAreIgnored) {
std::vector<FacetURI> requested_uris;
requested_uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+ SetupSuccessfulResponse(test_response.SerializeAsString());
MockAffiliationFetcherDelegate mock_delegate;
+ EXPECT_CALL(mock_delegate, OnFetchSucceededProxy());
std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), requested_uris, &mock_delegate));
+ test_shared_loader_factory(), requested_uris, &mock_delegate));
fetcher->StartRequest();
+ WaitForResponse();
- EXPECT_CALL(mock_delegate, OnFetchSucceededProxy());
- ASSERT_NO_FATAL_FAILURE(ServiceURLRequest(test_response.SerializeAsString()));
ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_delegate));
ASSERT_EQ(1u, mock_delegate.result().size());
@@ -320,13 +332,13 @@ TEST_F(AffiliationFetcherTest, FailureBecauseResponseIsNotAProtobuf) {
std::vector<FacetURI> uris;
uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+ SetupSuccessfulResponse(kMalformedResponse);
MockAffiliationFetcherDelegate mock_delegate;
+ EXPECT_CALL(mock_delegate, OnMalformedResponse());
std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), uris, &mock_delegate));
+ test_shared_loader_factory(), uris, &mock_delegate));
fetcher->StartRequest();
-
- EXPECT_CALL(mock_delegate, OnMalformedResponse());
- ASSERT_NO_FATAL_FAILURE(ServiceURLRequest(kMalformedResponse));
+ WaitForResponse();
}
// Partially overlapping equivalence classes violate the invariant that
@@ -344,39 +356,39 @@ TEST_F(AffiliationFetcherTest,
std::vector<FacetURI> uris;
uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+ SetupSuccessfulResponse(test_response.SerializeAsString());
MockAffiliationFetcherDelegate mock_delegate;
+ EXPECT_CALL(mock_delegate, OnMalformedResponse());
std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), uris, &mock_delegate));
+ test_shared_loader_factory(), uris, &mock_delegate));
fetcher->StartRequest();
-
- EXPECT_CALL(mock_delegate, OnMalformedResponse());
- ASSERT_NO_FATAL_FAILURE(ServiceURLRequest(test_response.SerializeAsString()));
+ WaitForResponse();
}
TEST_F(AffiliationFetcherTest, FailOnServerError) {
std::vector<FacetURI> uris;
uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+ SetupServerErrorResponse();
MockAffiliationFetcherDelegate mock_delegate;
+ EXPECT_CALL(mock_delegate, OnFetchFailed());
std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), uris, &mock_delegate));
+ test_shared_loader_factory(), uris, &mock_delegate));
fetcher->StartRequest();
-
- EXPECT_CALL(mock_delegate, OnFetchFailed());
- ASSERT_NO_FATAL_FAILURE(SimulateServerError());
+ WaitForResponse();
}
TEST_F(AffiliationFetcherTest, FailOnNetworkError) {
std::vector<FacetURI> uris;
uris.push_back(FacetURI::FromCanonicalSpec(kExampleWebFacet1URI));
+ SetupNetworkErrorResponse();
MockAffiliationFetcherDelegate mock_delegate;
+ EXPECT_CALL(mock_delegate, OnFetchFailed());
std::unique_ptr<AffiliationFetcher> fetcher(AffiliationFetcher::Create(
- request_context_getter(), uris, &mock_delegate));
+ test_shared_loader_factory(), uris, &mock_delegate));
fetcher->StartRequest();
-
- EXPECT_CALL(mock_delegate, OnFetchFailed());
- ASSERT_NO_FATAL_FAILURE(SimulateNetworkError());
+ WaitForResponse();
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service.cc
index e20d57c0b32..0b469dff50f 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service.cc
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service.cc
@@ -15,7 +15,7 @@
#include "base/time/default_clock.h"
#include "base/time/default_tick_clock.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_backend.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace password_manager {
@@ -34,17 +34,18 @@ AffiliationService::~AffiliationService() {
}
void AffiliationService::Initialize(
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const base::FilePath& db_path) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!backend_);
- backend_ = new AffiliationBackend(
- request_context_getter, backend_task_runner_,
- base::DefaultClock::GetInstance(), base::DefaultTickClock::GetInstance());
+ backend_ = new AffiliationBackend(backend_task_runner_,
+ base::DefaultClock::GetInstance(),
+ base::DefaultTickClock::GetInstance());
backend_task_runner_->PostTask(
- FROM_HERE, base::Bind(&AffiliationBackend::Initialize,
- base::Unretained(backend_), db_path));
+ FROM_HERE, base::BindOnce(&AffiliationBackend::Initialize,
+ base::Unretained(backend_),
+ url_loader_factory->Clone(), db_path));
}
void AffiliationService::GetAffiliationsAndBranding(
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service.h b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service.h
index 8c9d8e18032..37c65fa800f 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service.h
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service.h
@@ -20,9 +20,9 @@ class FilePath;
class SequencedTaskRunner;
} // namespace base
-namespace net {
-class URLRequestContextGetter;
-} // namespace net
+namespace network {
+class SharedURLLoaderFactory;
+} // namespace network
namespace password_manager {
@@ -105,8 +105,9 @@ class AffiliationService : public KeyedService {
// Initializes the service by creating its backend and transferring it to the
// thread corresponding to |backend_task_runner_|.
- void Initialize(net::URLRequestContextGetter* request_context_getter,
- const base::FilePath& db_path);
+ void Initialize(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ const base::FilePath& db_path);
// Looks up facets affiliated with the facet identified by |facet_uri| and
// branding information, and invokes |result_callback| with the results. It is
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service_unittest.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service_unittest.cc
index 4b98c8f597e..33277795225 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service_unittest.cc
+++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliation_service_unittest.cc
@@ -18,6 +18,9 @@
#include "base/threading/thread_task_runner_handle.h"
#include "components/password_manager/core/browser/android_affiliation/fake_affiliation_api.h"
#include "components/password_manager/core/browser/android_affiliation/mock_affiliation_consumer.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_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -47,7 +50,10 @@ class AffiliationServiceTest : public testing::Test {
AffiliationServiceTest()
: main_task_runner_(new base::TestSimpleTaskRunner),
main_task_runner_handle_(main_task_runner_),
- background_task_runner_(new base::TestMockTimeTaskRunner) {}
+ background_task_runner_(new base::TestMockTimeTaskRunner),
+ test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)) {}
~AffiliationServiceTest() override {}
protected:
@@ -74,7 +80,7 @@ class AffiliationServiceTest : public testing::Test {
base::FilePath database_path;
ASSERT_TRUE(CreateTemporaryFile(&database_path));
service_.reset(new AffiliationService(background_task_runner()));
- service_->Initialize(nullptr, database_path);
+ service_->Initialize(test_shared_loader_factory_, database_path);
// Note: the background task runner is purposely not pumped here, so that
// the tests also verify that the service can be used synchronously right
// away after having been constructed.
@@ -92,6 +98,8 @@ class AffiliationServiceTest : public testing::Test {
scoped_refptr<base::TestSimpleTaskRunner> main_task_runner_;
base::ThreadTaskRunnerHandle main_task_runner_handle_;
scoped_refptr<base::TestMockTimeTaskRunner> background_task_runner_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
ScopedFakeAffiliationAPI fake_affiliation_api_;
MockAffiliationConsumer mock_consumer_;
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.cc b/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.cc
index e24ebe4cebc..8fa599f63f7 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.cc
+++ b/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.cc
@@ -3,17 +3,17 @@
// found in the LICENSE file.
#include "components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#include <utility>
namespace password_manager {
password_manager::FakeAffiliationFetcher::FakeAffiliationFetcher(
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const std::vector<FacetURI>& facet_ids,
AffiliationFetcherDelegate* delegate)
- : AffiliationFetcher(request_context_getter, facet_ids, delegate) {
-}
+ : AffiliationFetcher(std::move(url_loader_factory), facet_ids, delegate) {}
password_manager::FakeAffiliationFetcher::~FakeAffiliationFetcher() {
}
@@ -27,10 +27,6 @@ void password_manager::FakeAffiliationFetcher::SimulateFailure() {
delegate()->OnFetchFailed();
}
-void password_manager::FakeAffiliationFetcher::StartRequest() {
- // Fake. Does nothing.
-}
-
password_manager::ScopedFakeAffiliationFetcherFactory::
ScopedFakeAffiliationFetcherFactory() {
AffiliationFetcher::SetFactoryForTesting(this);
@@ -54,11 +50,11 @@ FakeAffiliationFetcher* ScopedFakeAffiliationFetcherFactory::PeekNextFetcher() {
}
AffiliationFetcher* ScopedFakeAffiliationFetcherFactory::CreateInstance(
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const std::vector<FacetURI>& facet_ids,
AffiliationFetcherDelegate* delegate) {
- FakeAffiliationFetcher* fetcher =
- new FakeAffiliationFetcher(request_context_getter, facet_ids, delegate);
+ FakeAffiliationFetcher* fetcher = new FakeAffiliationFetcher(
+ std::move(url_loader_factory), facet_ids, delegate);
pending_fetchers_.push(fetcher);
return fetcher;
}
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.h b/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.h
index c6acf44d3f9..100de780989 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.h
+++ b/chromium/components/password_manager/core/browser/android_affiliation/fake_affiliation_fetcher.h
@@ -19,10 +19,11 @@ namespace password_manager {
// responses to users of AffiliationFetcher.
class FakeAffiliationFetcher : public AffiliationFetcher {
public:
- FakeAffiliationFetcher(net::URLRequestContextGetter* request_context_getter,
- const std::vector<FacetURI>& facet_ids,
- AffiliationFetcherDelegate* delegate);
- ~FakeAffiliationFetcher() override;
+ FakeAffiliationFetcher(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ const std::vector<FacetURI>& facet_ids,
+ AffiliationFetcherDelegate* delegate);
+ ~FakeAffiliationFetcher();
// Simulates successful completion of the request with |fake_result|. Note
// that the consumer may choose to destroy |this| from within this call.
@@ -34,8 +35,6 @@ class FakeAffiliationFetcher : public AffiliationFetcher {
void SimulateFailure();
private:
- void StartRequest() override;
-
DISALLOW_COPY_AND_ASSIGN(FakeAffiliationFetcher);
};
@@ -66,7 +65,7 @@ class ScopedFakeAffiliationFetcherFactory
// AffiliationFetcherFactory:
AffiliationFetcher* CreateInstance(
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const std::vector<FacetURI>& facet_ids,
AffiliationFetcherDelegate* delegate) override;
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/test_affiliation_fetcher_factory.h b/chromium/components/password_manager/core/browser/android_affiliation/test_affiliation_fetcher_factory.h
index 0f7381285f3..8b43a20da53 100644
--- a/chromium/components/password_manager/core/browser/android_affiliation/test_affiliation_fetcher_factory.h
+++ b/chromium/components/password_manager/core/browser/android_affiliation/test_affiliation_fetcher_factory.h
@@ -7,9 +7,9 @@
#include <vector>
-namespace net {
-class URLRequestContextGetter;
-} // namespace net
+namespace network {
+class SharedURLLoaderFactory;
+} // namespace network
namespace password_manager {
@@ -23,10 +23,10 @@ class AffiliationFetcherDelegate;
class TestAffiliationFetcherFactory {
public:
// Constructs a fetcher to retrieve affiliations for each facet in |facet_ids|
- // using the specified |request_context_getter|, and will provide the results
+ // using the specified |url_loader_factory|, and will provide the results
// to the |delegate| on the same thread that creates the instance.
virtual AffiliationFetcher* CreateInstance(
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const std::vector<FacetURI>& facet_ids,
AffiliationFetcherDelegate* delegate) = 0;
diff --git a/chromium/components/password_manager/core/browser/browser_save_password_progress_logger.cc b/chromium/components/password_manager/core/browser/browser_save_password_progress_logger.cc
index 25afbbbc4cf..9ac310d4cda 100644
--- a/chromium/components/password_manager/core/browser/browser_save_password_progress_logger.cc
+++ b/chromium/components/password_manager/core/browser/browser_save_password_progress_logger.cc
@@ -4,6 +4,7 @@
#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
@@ -16,6 +17,7 @@
#include "components/password_manager/core/browser/password_manager.h"
using autofill::AutofillUploadContents;
+using base::UintToString;
namespace password_manager {
@@ -67,6 +69,26 @@ std::string ClassifierOutcomeToString(
return std::string();
}
+std::string VoteTypeToString(
+ AutofillUploadContents::Field::VoteType vote_type) {
+ switch (vote_type) {
+ case AutofillUploadContents::Field::NO_INFORMATION:
+ return "No information";
+ case AutofillUploadContents::Field::CREDENTIALS_REUSED:
+ return "Credentials reused";
+ case AutofillUploadContents::Field::USERNAME_OVERWRITTEN:
+ return "Username overwritten";
+ case AutofillUploadContents::Field::USERNAME_EDITED:
+ return "Username edited";
+ case AutofillUploadContents::Field::BASE_HEURISTIC:
+ return "Base heuristic";
+ case AutofillUploadContents::Field::HTML_CLASSIFIER:
+ return "HTML classifier";
+ case AutofillUploadContents::Field::FIRST_USE:
+ return "First use";
+ }
+}
+
} // namespace
BrowserSavePasswordProgressLogger::BrowserSavePasswordProgressLogger(
@@ -135,27 +157,39 @@ std::string BrowserSavePasswordProgressLogger::FormStructureToFieldsLogString(
ScrubNonDigit(field->FieldSignatureAsStr()) +
", type=" + ScrubElementID(field->form_control_type);
+ field_info += ", renderer_id = " + UintToString(field->unique_renderer_id);
+
if (!field->autocomplete_attribute.empty())
field_info +=
", autocomplete=" + ScrubElementID(field->autocomplete_attribute);
- if (!field->Type().IsUnknown())
- field_info += ", SERVER_PREDICTION: " + field->Type().ToString();
+ if (field->server_type() != autofill::NO_SERVER_DATA) {
+ field_info +=
+ ", SERVER_PREDICTION: " +
+ autofill::AutofillType::ServerFieldTypeToString(field->server_type());
+ }
for (autofill::ServerFieldType type : field->possible_types())
field_info +=
", VOTE: " + autofill::AutofillType::ServerFieldTypeToString(type);
+ if (field->vote_type())
+ field_info += ", vote_type=" + VoteTypeToString(field->vote_type());
+
if (field->properties_mask) {
- field_info += ", properties = ";
+ field_info += ", properties=";
field_info +=
(field->properties_mask & autofill::FieldPropertiesFlags::USER_TYPED)
? "T"
: "_";
- field_info +=
- (field->properties_mask & autofill::FieldPropertiesFlags::AUTOFILLED)
- ? "A"
- : "_";
+ field_info += (field->properties_mask &
+ autofill::FieldPropertiesFlags::AUTOFILLED_ON_PAGELOAD)
+ ? "Ap"
+ : "__";
+ field_info += (field->properties_mask &
+ autofill::FieldPropertiesFlags::AUTOFILLED_ON_USER_TRIGGER)
+ ? "Au"
+ : "__";
field_info +=
(field->properties_mask & autofill::FieldPropertiesFlags::HAD_FOCUS)
? "F"
@@ -213,6 +247,11 @@ void BrowserSavePasswordProgressLogger::LogFormData(
message += GetStringFromID(STRING_IS_FORM_TAG) + ": " +
(form.is_form_tag ? "true" : "false") + "\n";
+ if (form.is_form_tag) {
+ message +=
+ "Form renderer id: " + UintToString(form.unique_renderer_id) + "\n";
+ }
+
// Log fields.
message += GetStringFromID(STRING_FIELDS) + ": " + "\n";
for (const auto& field : form.fields) {
@@ -223,9 +262,11 @@ void BrowserSavePasswordProgressLogger::LogFormData(
? std::string()
: (", autocomplete=" +
ScrubElementID(field.autocomplete_attribute));
- std::string field_info = ScrubElementID(field.name) + ": type=" +
- ScrubElementID(field.form_control_type) + ", " +
- is_visible + ", " + is_empty + autocomplete + "\n";
+ std::string field_info =
+ ScrubElementID(field.name) +
+ ": type=" + ScrubElementID(field.form_control_type) +
+ ", renderer_id = " + UintToString(field.unique_renderer_id) + ", " +
+ is_visible + ", " + is_empty + autocomplete + "\n";
message += field_info;
}
message += "}";
diff --git a/chromium/components/password_manager/core/browser/browser_save_password_progress_logger_unittest.cc b/chromium/components/password_manager/core/browser/browser_save_password_progress_logger_unittest.cc
index ee3695be0d3..07c6a9c7f8c 100644
--- a/chromium/components/password_manager/core/browser/browser_save_password_progress_logger_unittest.cc
+++ b/chromium/components/password_manager/core/browser/browser_save_password_progress_logger_unittest.cc
@@ -55,12 +55,14 @@ class BrowserSavePasswordProgressLoggerTest : public testing::Test {
field.form_control_type = "password";
field.is_focusable = true;
field.autocomplete_attribute = "new-password";
+ field.unique_renderer_id = 10;
form_.fields.push_back(field);
// Add a text field.
field.name = base::UTF8ToUTF16("email");
field.form_control_type = "text";
field.is_focusable = false;
+ field.unique_renderer_id = 42;
field.value = base::UTF8ToUTF16("a@example.com");
field.autocomplete_attribute.clear();
form_.fields.push_back(field);
@@ -101,12 +103,14 @@ TEST_F(BrowserSavePasswordProgressLoggerTest, LogFormSignatures) {
EXPECT_TRUE(logger.LogsContainSubstring("Origin: http://myform.com"));
EXPECT_TRUE(logger.LogsContainSubstring("Form fields:"));
EXPECT_TRUE(logger.LogsContainSubstring(
- "password: 2051817934, type=password, autocomplete=new-password, VOTE: "
+ "password: 2051817934, type=password, renderer_id = 10, "
+ "autocomplete=new-password, VOTE: "
"NEW_PASSWORD, GENERATION_EVENT: "
"Manual generation on sign-up, CLIENT_SIDE_CLASSIFIER: Generation "
"element"));
- EXPECT_TRUE(logger.LogsContainSubstring(
- "email: 420638584, type=text, SERVER_PREDICTION: EMAIL_ADDRESS"));
+ EXPECT_TRUE(
+ logger.LogsContainSubstring("email: 420638584, type=text, renderer_id = "
+ "42, SERVER_PREDICTION: EMAIL_ADDRESS"));
}
TEST_F(BrowserSavePasswordProgressLoggerTest, LogFormData) {
@@ -121,10 +125,11 @@ TEST_F(BrowserSavePasswordProgressLoggerTest, LogFormData) {
EXPECT_TRUE(logger.LogsContainSubstring("Form name: form_name"));
EXPECT_TRUE(logger.LogsContainSubstring("Form with form tag: true"));
EXPECT_TRUE(logger.LogsContainSubstring("Form fields:"));
- EXPECT_TRUE(logger.LogsContainSubstring(
- "password: type=password, visible, empty, autocomplete=new-password"));
EXPECT_TRUE(
- logger.LogsContainSubstring("email: type=text, invisible, non-empty"));
+ logger.LogsContainSubstring("password: type=password, renderer_id = 10, "
+ "visible, empty, autocomplete=new-password"));
+ EXPECT_TRUE(logger.LogsContainSubstring(
+ "email: type=text, renderer_id = 42, invisible, non-empty"));
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/credentials_filter.h b/chromium/components/password_manager/core/browser/credentials_filter.h
index cf5038cabe1..cbc7eed4b16 100644
--- a/chromium/components/password_manager/core/browser/credentials_filter.h
+++ b/chromium/components/password_manager/core/browser/credentials_filter.h
@@ -27,9 +27,14 @@ class CredentialsFilter {
// Should |form| be offered to be saved?
virtual bool ShouldSave(const autofill::PasswordForm& form) const = 0;
- // Returns true if the hash of |form.password_value| should be saved for
+ // Returns true if the hash of the password in |form| should be saved for Gaia
// password reuse checking.
- virtual bool ShouldSavePasswordHash(
+ virtual bool ShouldSaveGaiaPasswordHash(
+ const autofill::PasswordForm& form) const = 0;
+
+ // Returns true if the hash of the password in |form| should be saved for
+ // enterprise password reuse checking.
+ virtual bool ShouldSaveEnterprisePasswordHash(
const autofill::PasswordForm& form) const = 0;
// Call this if the form associated with |form_manager| was filled, and the
@@ -37,6 +42,9 @@ class CredentialsFilter {
virtual void ReportFormLoginSuccess(
const PasswordFormManager& form_manager) const {}
+ // If |username| matches Chrome sync account email.
+ virtual bool IsSyncAccountEmail(const std::string& username) const = 0;
+
private:
DISALLOW_COPY_AND_ASSIGN(CredentialsFilter);
};
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 9800fbd3164..220e941d8e2 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
@@ -10,7 +10,7 @@
#include <vector>
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "build/build_config.h"
diff --git a/chromium/components/password_manager/core/browser/form_parsing/BUILD.gn b/chromium/components/password_manager/core/browser/form_parsing/BUILD.gn
index 7c5e844c480..9fdd67c26dc 100644
--- a/chromium/components/password_manager/core/browser/form_parsing/BUILD.gn
+++ b/chromium/components/password_manager/core/browser/form_parsing/BUILD.gn
@@ -4,28 +4,55 @@
import("//build/config/sanitizers/sanitizers.gni")
+# Determine whetner fuzzer_test targets are built.
+does_fuzzer_test_compile =
+ !disable_libfuzzer && (use_fuzzing_engine || use_drfuzz || is_linux)
+
static_library("form_parsing") {
sources = [
- # TODO(https://crbug.com/831123): Make compilation of ios_form_parser only for iOS, when it is not needed for experimentation anymore.
- "ios_form_parser.cc",
- "ios_form_parser.h",
+ "form_parser.cc",
+ "form_parser.h",
+ "password_field_prediction.cc",
+ "password_field_prediction.h",
]
+ # TODO(crbug.com/845426): Provide a single parser for all platforms, then
+ # remove the ios_* variant. This will be possible once the form_parser.* is
+ # used by default and causing no known issues. Note that (the
+ # non-iOS-specific) 'form_parser.*' is used by NewPasswordFormManager on
+ # every platform, including iOS.
+ if (does_fuzzer_test_compile || is_ios) {
+ sources += [
+ "ios_form_parser.cc",
+ "ios_form_parser.h",
+ ]
+ }
+
deps = [
"//base",
+ "//components/autofill/core/browser",
+ "//components/autofill/core/browser/proto",
"//components/autofill/core/common",
+ "//components/password_manager/core/common",
]
}
source_set("unit_tests") {
testonly = true
sources = [
- "ios_form_parser_unittest.cc",
+ "form_parser_unittest.cc",
+ "password_field_prediction_unittest.cc",
]
+ if (does_fuzzer_test_compile || is_ios) {
+ sources += [ "ios_form_parser_unittest.cc" ]
+ }
+
deps = [
":form_parsing",
"//base",
+ "//components/autofill/core/browser",
+ "//components/autofill/core/browser/proto",
"//components/autofill/core/common",
"//testing/gmock",
"//testing/gtest",
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
new file mode 100644
index 00000000000..e900a5a407e
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/form_parsing/form_parser.cc
@@ -0,0 +1,730 @@
+// 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/password_manager/core/browser/form_parsing/form_parser.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <iterator>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/no_destructor.h"
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "components/autofill/core/common/autofill_regex_constants.h"
+#include "components/autofill/core/common/autofill_regexes.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/common/password_manager_features.h"
+
+using autofill::FieldPropertiesFlags;
+using autofill::FormFieldData;
+using autofill::PasswordForm;
+using base::string16;
+
+namespace password_manager {
+
+namespace {
+
+constexpr char kAutocompleteUsername[] = "username";
+constexpr char kAutocompleteCurrentPassword[] = "current-password";
+constexpr char kAutocompleteNewPassword[] = "new-password";
+constexpr char kAutocompleteCreditCardPrefix[] = "cc-";
+
+// The susbset of autocomplete flags related to passwords.
+enum class AutocompleteFlag {
+ kNone,
+ kUsername,
+ kCurrentPassword,
+ kNewPassword,
+ // Represents the whole family of cc-* flags.
+ kCreditCard
+};
+
+// The autocomplete attribute has one of the following structures:
+// [section-*] [shipping|billing] [type_hint] field_type
+// on | off | false
+// (see
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls%3A-the-autocomplete-attribute).
+// For password forms, only the field_type is relevant. So parsing the attribute
+// amounts to just taking the last token. If that token is one of "username",
+// "current-password" or "new-password", this returns an appropriate enum value.
+// If the token starts with a "cc-" prefix, this returns kCreditCard.
+// Otherwise, returns kNone.
+AutocompleteFlag ExtractAutocompleteFlag(const std::string& attribute) {
+ std::vector<base::StringPiece> tokens =
+ base::SplitStringPiece(attribute, base::kWhitespaceASCII,
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ if (tokens.empty())
+ return AutocompleteFlag::kNone;
+
+ const base::StringPiece& field_type = tokens.back();
+ if (base::LowerCaseEqualsASCII(field_type, kAutocompleteUsername))
+ return AutocompleteFlag::kUsername;
+ if (base::LowerCaseEqualsASCII(field_type, kAutocompleteCurrentPassword))
+ return AutocompleteFlag::kCurrentPassword;
+ if (base::LowerCaseEqualsASCII(field_type, kAutocompleteNewPassword))
+ return AutocompleteFlag::kNewPassword;
+
+ if (base::StartsWith(field_type, kAutocompleteCreditCardPrefix,
+ base::CompareCase::SENSITIVE))
+ return AutocompleteFlag::kCreditCard;
+
+ return AutocompleteFlag::kNone;
+}
+
+// How likely is user interaction for a given field?
+// Note: higher numeric values should match higher likeliness to allow using the
+// standard operator< for comparison of likeliness.
+enum class Interactability {
+ // When the field is invisible.
+ kUnlikely = 0,
+ // When the field is visible/focusable.
+ kPossible = 1,
+ // When the user actually typed into the field before.
+ kCertain = 2,
+};
+
+// A wrapper around FormFieldData, carrying some additional data used during
+// parsing.
+struct ProcessedField {
+ // This points to the wrapped FormFieldData.
+ const FormFieldData* field;
+
+ // The flag derived from field->autocomplete_attribute.
+ AutocompleteFlag autocomplete_flag = AutocompleteFlag::kNone;
+
+ // True iff field->form_control_type == "password".
+ bool is_password = false;
+
+ Interactability interactability = Interactability::kUnlikely;
+};
+
+// Returns true if the |str| contains words related to CVC fields.
+bool StringMatchesCVC(const base::string16& str) {
+ static const base::NoDestructor<base::string16> kCardCvcReCached(
+ base::UTF8ToUTF16(autofill::kCardCvcRe));
+
+ return autofill::MatchesPattern(str, *kCardCvcReCached);
+}
+
+// TODO(crbug.com/860700): Remove once server-side provides hints for CVC
+// fields.
+// Returns true if the |field|'s name or id hint at the field being a CVC field.
+bool IsFieldCVC(const FormFieldData& field) {
+ return StringMatchesCVC(field.name) || StringMatchesCVC(field.id);
+}
+
+// Returns true iff |processed_field| matches the |interactability_bar|. That is
+// when either:
+// (1) |processed_field.interactability| is not less than |interactability_bar|,
+// or
+// (2) |interactability_bar| is |kCertain|, and |processed_field| was
+// autofilled. The second clause helps to handle the case when both Chrome and
+// the user contribute to filling a form:
+//
+// <form>
+// <input type="password" autocomplete="current-password" id="Chrome">
+// <input type="password" autocomplete="new-password" id="user">
+// </form>
+//
+// In the example above, imagine that Chrome filled the field with id=Chrome,
+// and the user typed the new password in field with id=user. Then the parser
+// should identify that id=Chrome is the current password and id=user is the new
+// password. Without clause (2), Chrome would ignore id=Chrome.
+bool MatchesInteractability(const ProcessedField& processed_field,
+ Interactability interactability_bar) {
+ return (processed_field.interactability >= interactability_bar) ||
+ (interactability_bar == Interactability::kCertain &&
+ (processed_field.field->properties_mask &
+ FieldPropertiesFlags::AUTOFILLED));
+}
+
+// A helper struct that is used to capture significant fields to be used for
+// the construction of a PasswordForm.
+struct SignificantFields {
+ const FormFieldData* username = nullptr;
+ const FormFieldData* password = nullptr;
+ const FormFieldData* new_password = nullptr;
+ const FormFieldData* confirmation_password = nullptr;
+
+ // Returns true if some password field is present. This is the minimal
+ // requirement for a successful creation of a PasswordForm is present.
+ bool HasPasswords() const {
+ DCHECK(!confirmation_password || new_password)
+ << "There is no password to confirm if there is no new password field.";
+ return password || new_password;
+ }
+};
+
+// Returns the first element of |fields| which has the specified
+// |unique_renderer_id|, or null if there is no such element.
+const FormFieldData* FindFieldWithUniqueRendererId(
+ const std::vector<ProcessedField>& processed_fields,
+ uint32_t unique_renderer_id) {
+ for (const ProcessedField& processed_field : processed_fields) {
+ if (processed_field.field->unique_renderer_id == unique_renderer_id)
+ return processed_field.field;
+ }
+ return nullptr;
+}
+
+// Tries to parse |processed_fields| based on server |predictions|.
+std::unique_ptr<SignificantFields> ParseUsingPredictions(
+ const std::vector<ProcessedField>& processed_fields,
+ const FormPredictions& predictions) {
+ auto result = std::make_unique<SignificantFields>();
+ // Note: The code does not check whether there is at most 1 username, 1
+ // current password and at most 2 new passwords. It is assumed that server
+ // side predictions are sane.
+ for (const auto& prediction : predictions) {
+ switch (DeriveFromServerFieldType(prediction.second.type)) {
+ case CredentialFieldType::kUsername:
+ result->username =
+ FindFieldWithUniqueRendererId(processed_fields, prediction.first);
+ break;
+ case CredentialFieldType::kCurrentPassword:
+ result->password =
+ FindFieldWithUniqueRendererId(processed_fields, prediction.first);
+ break;
+ case CredentialFieldType::kNewPassword:
+ result->new_password =
+ FindFieldWithUniqueRendererId(processed_fields, prediction.first);
+ break;
+ case CredentialFieldType::kConfirmationPassword:
+ result->confirmation_password =
+ FindFieldWithUniqueRendererId(processed_fields, prediction.first);
+ break;
+ case CredentialFieldType::kNone:
+ break;
+ }
+ }
+ // If the server suggests there is a confirmation field but no new password,
+ // something went wrong. Sanitize the result.
+ if (result->confirmation_password && !result->new_password)
+ result->confirmation_password = nullptr;
+
+ return result->HasPasswords() ? std::move(result) : nullptr;
+}
+
+// Tries to parse |processed_fields| based on autocomplete attributes.
+// Assumption on the usage autocomplete attributes:
+// 1. Not more than 1 field with autocomplete=username.
+// 2. Not more than 1 field with autocomplete=current-password.
+// 3. Not more than 2 fields with autocomplete=new-password.
+// 4. Only password fields have "*-password" attribute and only non-password
+// fields have the "username" attribute.
+// Are these assumptions violated, or is there no password with an autocomplete
+// attribute, parsing is unsuccessful. Returns nullptr if parsing is
+// unsuccessful.
+std::unique_ptr<SignificantFields> ParseUsingAutocomplete(
+ const std::vector<ProcessedField>& processed_fields) {
+ auto result = std::make_unique<SignificantFields>();
+ for (const ProcessedField& processed_field : processed_fields) {
+ switch (processed_field.autocomplete_flag) {
+ case AutocompleteFlag::kUsername:
+ if (processed_field.is_password || result->username)
+ return nullptr;
+ result->username = processed_field.field;
+ break;
+ case AutocompleteFlag::kCurrentPassword:
+ if (!processed_field.is_password || result->password)
+ return nullptr;
+ result->password = processed_field.field;
+ break;
+ case AutocompleteFlag::kNewPassword:
+ if (!processed_field.is_password)
+ return nullptr;
+ // The first field with autocomplete=new-password is considered to be
+ // new_password and the second is confirmation_password.
+ if (!result->new_password)
+ result->new_password = processed_field.field;
+ else if (!result->confirmation_password)
+ result->confirmation_password = processed_field.field;
+ else
+ return nullptr;
+ break;
+ case AutocompleteFlag::kCreditCard:
+ NOTREACHED();
+ break;
+ case AutocompleteFlag::kNone:
+ break;
+ }
+ }
+
+ return result->HasPasswords() ? std::move(result) : nullptr;
+}
+
+// Returns only relevant password fields from |processed_fields|. Namely, if
+// |mode| == SAVING return only non-empty fields (for saving empty fields are
+// useless). This ignores all passwords with Interactability below
+// |best_interactability| and also fields with names which sound like CVC
+// fields. Stores the iterator to the first relevant password in
+// |first_relevant_password|.
+std::vector<const FormFieldData*> GetRelevantPasswords(
+ const std::vector<ProcessedField>& processed_fields,
+ FormParsingMode mode,
+ Interactability best_interactability,
+ std::vector<ProcessedField>::const_iterator* first_relevant_password) {
+ DCHECK(first_relevant_password);
+ *first_relevant_password = processed_fields.end();
+ std::vector<const FormFieldData*> result;
+ result.reserve(processed_fields.size());
+
+ const bool consider_only_non_empty = mode == FormParsingMode::SAVING;
+
+ for (auto it = processed_fields.begin(); it != processed_fields.end(); ++it) {
+ const ProcessedField& processed_field = *it;
+ if (!processed_field.is_password)
+ continue;
+ if (!MatchesInteractability(processed_field, best_interactability))
+ continue;
+ if (consider_only_non_empty && processed_field.field->value.empty())
+ continue;
+ // Readonly fields can be an indication that filling is useless (e.g., the
+ // page might use a virtual keyboard). However, if the field was readonly
+ // only temporarily, that makes it still interesting for saving. The fact
+ // that a user typed or Chrome filled into that field in tha past is an
+ // indicator that the radonly was only temporary.
+ if (processed_field.field->is_readonly &&
+ !(processed_field.field->properties_mask &
+ (FieldPropertiesFlags::USER_TYPED |
+ FieldPropertiesFlags::AUTOFILLED))) {
+ continue;
+ }
+ if (IsFieldCVC(*processed_field.field))
+ continue;
+ if (*first_relevant_password == processed_fields.end())
+ *first_relevant_password = it;
+ result.push_back(processed_field.field);
+ }
+
+ return result;
+}
+
+// Detects different password fields from |passwords|.
+void LocateSpecificPasswords(const std::vector<const FormFieldData*>& passwords,
+ const FormFieldData** current_password,
+ const FormFieldData** new_password,
+ const FormFieldData** confirmation_password) {
+ DCHECK(current_password);
+ DCHECK(!*current_password);
+ DCHECK(new_password);
+ DCHECK(!*new_password);
+ DCHECK(confirmation_password);
+ DCHECK(!*confirmation_password);
+
+ switch (passwords.size()) {
+ case 1:
+ *current_password = passwords[0];
+ break;
+ case 2:
+ if (!passwords[0]->value.empty() &&
+ passwords[0]->value == passwords[1]->value) {
+ // Two identical non-empty passwords: assume we are seeing a new
+ // password with a confirmation. This can be either a sign-up form or a
+ // password change form that does not ask for the old password.
+ *new_password = passwords[0];
+ *confirmation_password = passwords[1];
+ } else {
+ // Assume first is old password, second is new (no choice but to guess).
+ // If the passwords are both empty, it is impossible to tell if they
+ // are the old and the new one, or the new one and its confirmation. In
+ // that case Chrome errs on the side of filling and classifies them as
+ // old & new to allow filling of change password forms.
+ *current_password = passwords[0];
+ *new_password = passwords[1];
+ }
+ break;
+ default:
+ // If there are more than 3 passwords it is not very clear what this form
+ // it is. Consider only the first 3 passwords in such case as a
+ // best-effort solution.
+ if (!passwords[0]->value.empty() &&
+ passwords[0]->value == passwords[1]->value &&
+ passwords[0]->value == passwords[2]->value) {
+ // All passwords are the same. Assume that the first field is the
+ // current password.
+ *current_password = passwords[0];
+ } else if (passwords[1]->value == passwords[2]->value) {
+ // New password is the duplicated one, and comes second; or empty form
+ // with at least 3 password fields.
+ *current_password = passwords[0];
+ *new_password = passwords[1];
+ *confirmation_password = passwords[2];
+ } else if (passwords[0]->value == passwords[1]->value) {
+ // It is strange that the new password comes first, but trust more which
+ // fields are duplicated than the ordering of fields. Assume that
+ // any password fields after the new password contain sensitive
+ // information that isn't actually a password (security hint, SSN, etc.)
+ *new_password = passwords[0];
+ *confirmation_password = passwords[1];
+ } else {
+ // Three different passwords, or first and last match with middle
+ // different. No idea which is which. Let's save the first password.
+ // Password selection in a prompt will allow to correct the choice.
+ *current_password = passwords[0];
+ }
+ }
+}
+
+// Tries to find username field among text fields from |processed_fields|
+// occurring before |first_relevant_password|. Returns nullptr if the username
+// is not found. If |mode| is SAVING, ignores all fields with empty values.
+// Ignores all fields with interactability less than |best_interactability|.
+const FormFieldData* FindUsernameFieldBaseHeuristics(
+ const std::vector<ProcessedField>& processed_fields,
+ const std::vector<ProcessedField>::const_iterator& first_relevant_password,
+ FormParsingMode mode,
+ Interactability best_interactability) {
+ DCHECK(first_relevant_password != processed_fields.end());
+
+ // For saving filter out empty fields.
+ const bool consider_only_non_empty = mode == FormParsingMode::SAVING;
+
+ // Search through the text input fields preceding |first_relevant_password|
+ // and find the closest one focusable and the closest one in general.
+
+ const FormFieldData* focusable_username = nullptr;
+ const FormFieldData* username = nullptr;
+ // Do reverse search to find the closest candidates preceding the password.
+ for (auto it = std::make_reverse_iterator(first_relevant_password);
+ it != processed_fields.rend(); ++it) {
+ if (it->is_password)
+ continue;
+ if (!MatchesInteractability(*it, best_interactability))
+ continue;
+ if (consider_only_non_empty && it->field->value.empty())
+ continue;
+ if (IsFieldCVC(*it->field))
+ continue;
+ if (!username)
+ username = it->field;
+ if (it->field->is_focusable) {
+ focusable_username = it->field;
+ break;
+ }
+ }
+
+ return focusable_username ? focusable_username : username;
+}
+
+// A helper to return a |field|'s unique_renderer_id or
+// kNotSetFormControlRendererId if |field| is null.
+uint32_t ExtractUniqueId(const FormFieldData* field) {
+ return field ? field->unique_renderer_id : FormFieldData::kNotSetFormControlRendererId;
+}
+
+// Tries to find the username and password fields in |processed_fields| based
+// on the structure (how the fields are ordered). If |mode| is SAVING, only
+// considers non-empty fields. The |found_fields| is both an input and output
+// argument: if some password field and the username are already present, the
+// the function exits early. If something is missing, the function tries to
+// complete it. The result is stored back in |found_fields|. The best
+// interactability for usernames, which depends on position of the found
+// passwords as well, is returned through |username_max| to be used in other
+// kinds of analysis.
+void ParseUsingBaseHeuristics(
+ const std::vector<ProcessedField>& processed_fields,
+ FormParsingMode mode,
+ SignificantFields* found_fields,
+ Interactability* username_max) {
+ // If there is both the username and the minimal set of fields to build a
+ // PasswordForm, return early -- no more work to do.
+ if (found_fields->HasPasswords() && found_fields->username)
+ return;
+
+ // Will point to the password included in |found_field| which is first in the
+ // order of fields in |processed_fields|.
+ std::vector<ProcessedField>::const_iterator first_relevant_password =
+ processed_fields.end();
+
+ if (!found_fields->HasPasswords()) {
+ // What is the best interactability among passwords?
+ Interactability password_max = Interactability::kUnlikely;
+ for (const ProcessedField& processed_field : processed_fields) {
+ if (processed_field.is_password)
+ password_max = std::max(password_max, processed_field.interactability);
+ }
+
+ // Try to find password elements (current, new, confirmation) among those
+ // with best interactability.
+ first_relevant_password = processed_fields.end();
+ std::vector<const FormFieldData*> passwords = GetRelevantPasswords(
+ processed_fields, mode, password_max, &first_relevant_password);
+ if (passwords.empty())
+ return;
+ LocateSpecificPasswords(passwords, &found_fields->password,
+ &found_fields->new_password,
+ &found_fields->confirmation_password);
+ if (!found_fields->HasPasswords())
+ return;
+ } else {
+ const uint32_t password_ids[] = {
+ ExtractUniqueId(found_fields->password),
+ ExtractUniqueId(found_fields->new_password),
+ ExtractUniqueId(found_fields->confirmation_password)};
+ for (auto it = processed_fields.begin(); it != processed_fields.end();
+ ++it) {
+ if (it->is_password &&
+ base::ContainsValue(password_ids, it->field->unique_renderer_id)) {
+ first_relevant_password = it;
+ break;
+ }
+ }
+ }
+ DCHECK(first_relevant_password != processed_fields.end());
+
+ if (found_fields->username)
+ return;
+
+ // What is the best interactability among text fields preceding the passwords?
+ *username_max = Interactability::kUnlikely;
+ for (auto it = processed_fields.begin(); it != first_relevant_password;
+ ++it) {
+ if (!it->is_password)
+ *username_max = std::max(*username_max, it->interactability);
+ }
+
+ found_fields->username = FindUsernameFieldBaseHeuristics(
+ processed_fields, first_relevant_password, mode, *username_max);
+ return;
+}
+
+string16 GetPlatformSpecificIdentifier(const FormFieldData& field) {
+#if defined(OS_IOS)
+ return field.id;
+#else
+ return field.name;
+#endif
+}
+
+// Set username and password fields in |password_form| based on
+// |significant_fields| .
+void SetFields(const SignificantFields& significant_fields,
+ PasswordForm* password_form) {
+ password_form->has_renderer_ids = true;
+ if (significant_fields.username) {
+ password_form->username_element =
+ GetPlatformSpecificIdentifier(*significant_fields.username);
+ password_form->username_value = significant_fields.username->value;
+ password_form->username_element_renderer_id =
+ significant_fields.username->unique_renderer_id;
+ }
+
+ if (significant_fields.password) {
+ password_form->password_element =
+ GetPlatformSpecificIdentifier(*significant_fields.password);
+ password_form->password_value = significant_fields.password->value;
+ password_form->password_element_renderer_id =
+ significant_fields.password->unique_renderer_id;
+ }
+
+ if (significant_fields.new_password) {
+ password_form->new_password_element =
+ GetPlatformSpecificIdentifier(*significant_fields.new_password);
+ password_form->new_password_value = significant_fields.new_password->value;
+ }
+
+ if (significant_fields.confirmation_password) {
+ password_form->confirmation_password_element =
+ GetPlatformSpecificIdentifier(
+ *significant_fields.confirmation_password);
+ }
+}
+
+// For each relevant field of |fields| computes additional data useful for
+// parsing and wraps that in a ProcessedField. Returns the vector of all those
+// ProcessedField instances, or an empty vector if there was not a single
+// password field. Also, computes the vector of all password values and
+// associated element names in |all_possible_passwords|.
+std::vector<ProcessedField> ProcessFields(
+ const std::vector<FormFieldData>& fields,
+ autofill::ValueElementVector* all_possible_passwords) {
+ DCHECK(all_possible_passwords);
+ DCHECK(all_possible_passwords->empty());
+
+ std::vector<ProcessedField> result;
+ bool password_field_found = false;
+
+ result.reserve(fields.size());
+
+ // |all_possible_passwords| should only contain each value once. |seen_values|
+ // ensures that duplicates are ignored.
+ std::set<base::StringPiece16> seen_values;
+ // Pretend that an empty value has been already seen, so that empty-valued
+ // password elements won't get added to |all_possible_passwords|.
+ seen_values.insert(base::StringPiece16());
+
+ for (const FormFieldData& field : fields) {
+ if (!field.IsTextInputElement())
+ continue;
+
+ const bool is_password = field.form_control_type == "password";
+ if (is_password) {
+ // Only the field name of the first occurrence is added to
+ // |all_possible_passwords|.
+ auto insertion = seen_values.insert(base::StringPiece16(field.value));
+ if (insertion.second) // There was no such element in |seen_values|.
+ all_possible_passwords->push_back({field.value, field.name});
+ }
+
+ const AutocompleteFlag flag =
+ ExtractAutocompleteFlag(field.autocomplete_attribute);
+ if (flag == AutocompleteFlag::kCreditCard)
+ continue;
+
+ ProcessedField processed_field = {
+ .field = &field, .autocomplete_flag = flag, .is_password = is_password};
+
+ password_field_found |= is_password;
+
+ if (field.properties_mask & FieldPropertiesFlags::USER_TYPED)
+ processed_field.interactability = Interactability::kCertain;
+ else if (field.is_focusable)
+ processed_field.interactability = Interactability::kPossible;
+
+ result.push_back(processed_field);
+ }
+
+ if (!password_field_found)
+ result.clear();
+
+ return result;
+}
+
+// Find the first element in |username_predictions| (i.e. the most reliable
+// prediction) that occurs in |processed_fields| and has interactability level
+// at least |username_max|.
+const FormFieldData* FindUsernameInPredictions(
+ const std::vector<uint32_t>& username_predictions,
+ const std::vector<ProcessedField>& processed_fields,
+ Interactability username_max) {
+ for (uint32_t predicted_id : username_predictions) {
+ auto iter = std::find_if(
+ processed_fields.begin(), processed_fields.end(),
+ [predicted_id, username_max](const ProcessedField& processed_field) {
+ return processed_field.field->unique_renderer_id == predicted_id &&
+ MatchesInteractability(processed_field, username_max);
+ });
+ if (iter != processed_fields.end()) {
+ return iter->field;
+ }
+ }
+ return nullptr;
+}
+
+// Return true if |significant_fields| has an username field and
+// |form_predictions| has |may_use_prefilled_placeholder| == true for the
+// username field.
+bool GetMayUsePrefilledPlaceholder(
+ const FormPredictions* form_predictions,
+ const SignificantFields& significant_fields) {
+ if (!form_predictions || !significant_fields.username)
+ return false;
+
+ uint32_t username_id = significant_fields.username->unique_renderer_id;
+ auto it = form_predictions->find(username_id);
+ if (it == form_predictions->end())
+ return false;
+ return it->second.may_use_prefilled_placeholder;
+}
+
+// Puts together a PasswordForm, the result of the parsing, based on the
+// |form_data| description of the form metadata (e.g., action), the already
+// parsed information about what are the |significant_fields|, and the list
+// |all_possible_passwords| of all non-empty password values and associated
+// element names which occurred in the form. |form_predictions| is used to find
+// fields that may have preffilled placeholders.
+std::unique_ptr<PasswordForm> AssemblePasswordForm(
+ const autofill::FormData& form_data,
+ const SignificantFields* significant_fields,
+ autofill::ValueElementVector all_possible_passwords,
+ const FormPredictions* form_predictions) {
+ if (!significant_fields || !significant_fields->HasPasswords())
+ return nullptr;
+
+ // Create the PasswordForm and set data not related to specific fields.
+ auto result = std::make_unique<PasswordForm>();
+ result->origin = form_data.origin;
+ result->signon_realm = form_data.origin.GetOrigin().spec();
+ result->action = form_data.action;
+ result->form_data = form_data;
+ result->all_possible_passwords = std::move(all_possible_passwords);
+ result->scheme = PasswordForm::SCHEME_HTML;
+ result->preferred = false;
+ result->blacklisted_by_user = false;
+ result->type = PasswordForm::TYPE_MANUAL;
+ result->username_may_use_prefilled_placeholder =
+ GetMayUsePrefilledPlaceholder(form_predictions, *significant_fields);
+
+ // Set data related to specific fields.
+ SetFields(*significant_fields, result.get());
+ return result;
+}
+
+} // namespace
+
+std::unique_ptr<PasswordForm> ParseFormData(
+ const autofill::FormData& form_data,
+ const FormPredictions* form_predictions,
+ FormParsingMode mode) {
+ autofill::ValueElementVector all_possible_passwords;
+ std::vector<ProcessedField> processed_fields =
+ ProcessFields(form_data.fields, &all_possible_passwords);
+
+ if (processed_fields.empty())
+ return nullptr;
+
+ std::unique_ptr<SignificantFields> significant_fields;
+
+ // (1) First, try to parse with server predictions.
+ if (form_predictions)
+ significant_fields =
+ ParseUsingPredictions(processed_fields, *form_predictions);
+
+ // (2) If that failed, try to parse with autocomplete attributes.
+ if (!significant_fields)
+ significant_fields = ParseUsingAutocomplete(processed_fields);
+
+ // (3) Now try to fill the gaps.
+ if (!significant_fields)
+ significant_fields = std::make_unique<SignificantFields>();
+
+ const bool username_found_before_heuristic = significant_fields->username;
+
+ // Try to parse with base heuristic.
+ Interactability username_max = Interactability::kUnlikely;
+ ParseUsingBaseHeuristics(processed_fields, mode, significant_fields.get(),
+ &username_max);
+
+ // Additionally, and based on the best interactability computed by base
+ // heuristics, try to improve the username based on the context of the
+ // fields, unless the username already came from more reliable types of
+ // analysis.
+ if (!username_found_before_heuristic &&
+ base::FeatureList::IsEnabled(
+ password_manager::features::kHtmlBasedUsernameDetector)) {
+ const FormFieldData* username_field_by_context = FindUsernameInPredictions(
+ form_data.username_predictions, processed_fields, username_max);
+ if (username_field_by_context &&
+ !(mode == FormParsingMode::SAVING &&
+ username_field_by_context->value.empty())) {
+ significant_fields->username = username_field_by_context;
+ }
+ }
+
+ return AssemblePasswordForm(form_data, significant_fields.get(),
+ std::move(all_possible_passwords),
+ form_predictions);
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/form_parsing/form_parser.h b/chromium/components/password_manager/core/browser/form_parsing/form_parser.h
new file mode 100644
index 00000000000..d77df8b2447
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/form_parsing/form_parser.h
@@ -0,0 +1,35 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_FORM_PARSING_FORM_PARSER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FORM_PARSING_FORM_PARSER_H_
+
+#include <memory>
+#include <vector>
+
+#include "components/password_manager/core/browser/form_parsing/password_field_prediction.h"
+
+namespace autofill {
+struct FormData;
+struct PasswordForm;
+} // namespace autofill
+
+namespace password_manager {
+
+enum class FormParsingMode { FILLING, SAVING };
+
+// TODO(crbug.com/845426): Add the UsernameDetectionMethod enum and log data
+// into the "PasswordManager.UsernameDetectionMethod" histogram.
+
+// Parse DOM information |form_data| into Password Manager's form representation
+// PasswordForm. |form_predictions| are an optional source of server-side
+// predictions about field types. Return nullptr when parsing is unsuccessful.
+std::unique_ptr<autofill::PasswordForm> ParseFormData(
+ const autofill::FormData& form_data,
+ const FormPredictions* form_predictions,
+ FormParsingMode mode);
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FORM_PARSING_IOS_FORM_PARSER_H_
diff --git a/chromium/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc b/chromium/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
new file mode 100644
index 00000000000..e4d65462625
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
@@ -0,0 +1,1305 @@
+// 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/password_manager/core/browser/form_parsing/form_parser.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <set>
+
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/password_form.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using autofill::FieldPropertiesFlags;
+using autofill::FormData;
+using autofill::FormFieldData;
+using autofill::PasswordForm;
+using base::ASCIIToUTF16;
+
+namespace password_manager {
+
+namespace {
+
+// Use this value in FieldDataDescription.value to get an arbitrary unique value
+// generated in GetFormDataAndExpectation().
+constexpr char kNonimportantValue[] = "non-important unique";
+
+// Use this in FieldDataDescription below to mark the expected username and
+// password fields. The *_FILLING variants apply to FormParsingMode::FILLING
+// only, the *_SAVING variants to FormParsingMode::SAVING only, the suffix-less
+// variants to both.
+enum class ElementRole {
+ NONE,
+ USERNAME_FILLING,
+ USERNAME_SAVING,
+ USERNAME,
+ CURRENT_PASSWORD_FILLING,
+ CURRENT_PASSWORD_SAVING,
+ CURRENT_PASSWORD,
+ NEW_PASSWORD_FILLING,
+ NEW_PASSWORD_SAVING,
+ NEW_PASSWORD,
+ CONFIRMATION_PASSWORD_FILLING,
+ CONFIRMATION_PASSWORD_SAVING,
+ CONFIRMATION_PASSWORD
+};
+
+// Expected FormFieldData are constructed based on these descriptions.
+struct FieldDataDescription {
+ ElementRole role = ElementRole::NONE;
+ bool is_focusable = true;
+ bool is_enabled = true;
+ bool is_readonly = false;
+ autofill::FieldPropertiesMask properties_mask =
+ FieldPropertiesFlags::NO_FLAGS;
+ const char* autocomplete_attribute = nullptr;
+ const char* value = kNonimportantValue;
+ const char* name = kNonimportantValue;
+ const char* form_control_type = "text";
+ PasswordFieldPrediction prediction = {.type = autofill::MAX_VALID_FIELD_TYPE};
+ // If not -1, indicates on which rank among predicted usernames this should
+ // be. Unused ranks will be padded with unique IDs (not found in any fields).
+ int predicted_username = -1;
+};
+
+// Describes a test case for the parser.
+struct FormParsingTestCase {
+ const char* description_for_logging;
+ std::vector<FieldDataDescription> fields;
+ // -1 just mean no checking.
+ int number_of_all_possible_passwords = -1;
+ // null means no checking
+ const autofill::ValueElementVector* all_possible_passwords = nullptr;
+ bool username_may_use_prefilled_placeholder = false;
+};
+
+// Returns numbers which are distinct from each other within the scope of one
+// test.
+uint32_t GetUniqueId() {
+ static uint32_t counter = 10;
+ return counter++;
+}
+
+// Use to add a number suffix which is unique in the scope of the test.
+base::string16 StampUniqueSuffix(const char* base_str) {
+ return ASCIIToUTF16(base_str) + ASCIIToUTF16("_") +
+ base::UintToString16(GetUniqueId());
+}
+
+// Describes which renderer IDs are expected for username/password fields
+// identified in a PasswordForm.
+struct ParseResultIds {
+ uint32_t username_id = FormFieldData::kNotSetFormControlRendererId;
+ uint32_t password_id = FormFieldData::kNotSetFormControlRendererId;
+ uint32_t new_password_id = FormFieldData::kNotSetFormControlRendererId;
+ uint32_t confirmation_password_id =
+ FormFieldData::kNotSetFormControlRendererId;
+
+ bool IsEmpty() const {
+ return username_id == FormFieldData::kNotSetFormControlRendererId &&
+ password_id == FormFieldData::kNotSetFormControlRendererId &&
+ new_password_id == FormFieldData::kNotSetFormControlRendererId &&
+ confirmation_password_id ==
+ FormFieldData::kNotSetFormControlRendererId;
+ }
+};
+
+// Creates a FormData to be fed to the parser. Includes FormFieldData as
+// described in |fields_description|. Generates |fill_result| and |save_result|
+// expectations about the result in FILLING and SAVING mode, respectively. Also
+// fills |predictions| with the predictions contained in FieldDataDescriptions.
+FormData GetFormDataAndExpectation(
+ const std::vector<FieldDataDescription>& fields_description,
+ FormPredictions* predictions,
+ ParseResultIds* fill_result,
+ ParseResultIds* save_result) {
+ FormData form_data;
+ form_data.action = GURL("http://example1.com");
+ form_data.origin = GURL("http://example2.com");
+ for (const FieldDataDescription& field_description : fields_description) {
+ FormFieldData field;
+ const uint32_t unique_id = GetUniqueId();
+ field.unique_renderer_id = unique_id;
+ field.id = StampUniqueSuffix("html_id");
+ if (field_description.name == kNonimportantValue) {
+ field.name = StampUniqueSuffix("html_name");
+ } else {
+ field.name = ASCIIToUTF16(field_description.name);
+ }
+ field.form_control_type = field_description.form_control_type;
+ field.is_focusable = field_description.is_focusable;
+ field.is_enabled = field_description.is_enabled;
+ field.is_readonly = field_description.is_readonly;
+ field.properties_mask = field_description.properties_mask;
+ if (field_description.value == kNonimportantValue) {
+ field.value = StampUniqueSuffix("value");
+ } else {
+ field.value = ASCIIToUTF16(field_description.value);
+ }
+ if (field_description.autocomplete_attribute)
+ field.autocomplete_attribute = field_description.autocomplete_attribute;
+ form_data.fields.push_back(field);
+ switch (field_description.role) {
+ case ElementRole::NONE:
+ break;
+ case ElementRole::USERNAME_FILLING:
+ fill_result->username_id = unique_id;
+ break;
+ case ElementRole::USERNAME_SAVING:
+ save_result->username_id = unique_id;
+ break;
+ case ElementRole::USERNAME:
+ fill_result->username_id = unique_id;
+ save_result->username_id = unique_id;
+ break;
+ case ElementRole::CURRENT_PASSWORD_FILLING:
+ fill_result->password_id = unique_id;
+ break;
+ case ElementRole::CURRENT_PASSWORD_SAVING:
+ save_result->password_id = unique_id;
+ break;
+ case ElementRole::CURRENT_PASSWORD:
+ fill_result->password_id = unique_id;
+ save_result->password_id = unique_id;
+ break;
+ case ElementRole::NEW_PASSWORD_FILLING:
+ fill_result->new_password_id = unique_id;
+ break;
+ case ElementRole::NEW_PASSWORD_SAVING:
+ save_result->new_password_id = unique_id;
+ break;
+ case ElementRole::NEW_PASSWORD:
+ fill_result->new_password_id = unique_id;
+ save_result->new_password_id = unique_id;
+ break;
+ case ElementRole::CONFIRMATION_PASSWORD_FILLING:
+ fill_result->confirmation_password_id = unique_id;
+ break;
+ case ElementRole::CONFIRMATION_PASSWORD_SAVING:
+ save_result->confirmation_password_id = unique_id;
+ break;
+ case ElementRole::CONFIRMATION_PASSWORD:
+ fill_result->confirmation_password_id = unique_id;
+ save_result->confirmation_password_id = unique_id;
+ break;
+ }
+ if (field_description.prediction.type != autofill::MAX_VALID_FIELD_TYPE) {
+ (*predictions)[unique_id] = field_description.prediction;
+ }
+ if (field_description.predicted_username >= 0) {
+ size_t index = static_cast<size_t>(field_description.predicted_username);
+ if (form_data.username_predictions.size() <= index)
+ form_data.username_predictions.resize(index + 1);
+ form_data.username_predictions[index] = field.unique_renderer_id;
+ }
+ }
+ // Fill unused ranks in predictions with fresh IDs to check that those are
+ // correctly ignored. In real situation, this might correspond, e.g., to
+ // fields which were not fillable and hence dropped from the selection.
+ for (uint32_t& id : form_data.username_predictions) {
+ if (id == 0)
+ id = GetUniqueId();
+ }
+ return form_data;
+}
+
+// Check that |fields| has a field with unique renderer ID |renderer_id| which
+// has the name |element_name| and value |*element_value|. If |renderer_id| is
+// FormFieldData::kNotSetFormControlRendererId, then instead check that
+// |element_name| and |*element_value| are empty. Set |element_kind| to identify
+// the type of the field in logging: 'username', 'password', etc. The argument
+// |element_value| can be null, in which case all checks involving it are
+// skipped (useful for the confirmation password value, which is not represented
+// in PasswordForm).
+void CheckField(const std::vector<FormFieldData>& fields,
+ uint32_t renderer_id,
+ const base::string16& element_name,
+ const base::string16* element_value,
+ const char* element_kind) {
+ SCOPED_TRACE(testing::Message("Looking for element of kind ")
+ << element_kind);
+
+ if (renderer_id == FormFieldData::kNotSetFormControlRendererId) {
+ EXPECT_EQ(base::string16(), element_name);
+ if (element_value)
+ EXPECT_EQ(base::string16(), *element_value);
+ return;
+ }
+
+ auto field_it = std::find_if(fields.begin(), fields.end(),
+ [renderer_id](const FormFieldData& field) {
+ return field.unique_renderer_id == renderer_id;
+ });
+ ASSERT_TRUE(field_it != fields.end())
+ << "Could not find a field with renderer ID " << renderer_id;
+
+// On iOS |id| is used for identifying DOM elements, so the parser should return
+// it.
+#if defined(OS_IOS)
+ EXPECT_EQ(element_name, field_it->id);
+#else
+ EXPECT_EQ(element_name, field_it->name);
+#endif
+
+ if (element_value)
+ EXPECT_EQ(*element_value, field_it->value);
+}
+
+// Check that the information distilled from |form_data| into |password_form| is
+// matching |expectations|.
+void CheckPasswordFormFields(const PasswordForm& password_form,
+ const FormData& form_data,
+ const ParseResultIds& expectations) {
+ CheckField(form_data.fields, expectations.username_id,
+ password_form.username_element, &password_form.username_value,
+ "username");
+ EXPECT_EQ(expectations.username_id,
+ password_form.username_element_renderer_id);
+
+ CheckField(form_data.fields, expectations.password_id,
+ password_form.password_element, &password_form.password_value,
+ "password");
+ EXPECT_EQ(expectations.password_id,
+ password_form.password_element_renderer_id);
+
+ CheckField(form_data.fields, expectations.new_password_id,
+ password_form.new_password_element,
+ &password_form.new_password_value, "new_password");
+
+ CheckField(form_data.fields, expectations.confirmation_password_id,
+ password_form.confirmation_password_element, nullptr,
+ "confirmation_password");
+}
+
+// Checks that in a vector of pairs of string16s, all the first parts of the
+// pairs (which represent element values) are unique.
+void CheckAllValuesUnique(const autofill::ValueElementVector& v) {
+ std::set<base::string16> all_values;
+ for (const auto pair : v) {
+ auto insertion = all_values.insert(pair.first);
+ EXPECT_TRUE(insertion.second) << pair.first << " is duplicated";
+ }
+}
+
+// Iterates over |test_cases|, creates a FormData for each, runs the parser and
+// checks the results.
+void CheckTestData(const std::vector<FormParsingTestCase>& test_cases) {
+ for (const FormParsingTestCase& test_case : test_cases) {
+ FormPredictions predictions;
+ ParseResultIds fill_result;
+ ParseResultIds save_result;
+ const FormData form_data = GetFormDataAndExpectation(
+ test_case.fields, &predictions, &fill_result, &save_result);
+ for (auto mode : {FormParsingMode::FILLING, FormParsingMode::SAVING}) {
+ SCOPED_TRACE(
+ testing::Message("Test description: ")
+ << test_case.description_for_logging << ", parsing mode = "
+ << (mode == FormParsingMode::FILLING ? "Filling" : "Saving"));
+
+ std::unique_ptr<PasswordForm> parsed_form =
+ ParseFormData(form_data, &predictions, mode);
+
+ const ParseResultIds& expected_ids =
+ mode == FormParsingMode::FILLING ? fill_result : save_result;
+
+ if (expected_ids.IsEmpty()) {
+ EXPECT_FALSE(parsed_form) << "Expected no parsed results";
+ } else {
+ ASSERT_TRUE(parsed_form) << "Expected successful parsing";
+ EXPECT_EQ(PasswordForm::SCHEME_HTML, parsed_form->scheme);
+ EXPECT_FALSE(parsed_form->preferred);
+ EXPECT_FALSE(parsed_form->blacklisted_by_user);
+ EXPECT_EQ(PasswordForm::TYPE_MANUAL, parsed_form->type);
+ EXPECT_TRUE(parsed_form->has_renderer_ids);
+ EXPECT_EQ(test_case.username_may_use_prefilled_placeholder,
+ parsed_form->username_may_use_prefilled_placeholder);
+ CheckPasswordFormFields(*parsed_form, form_data, expected_ids);
+ CheckAllValuesUnique(parsed_form->all_possible_passwords);
+ if (test_case.number_of_all_possible_passwords >= 0) {
+ EXPECT_EQ(
+ static_cast<size_t>(test_case.number_of_all_possible_passwords),
+ parsed_form->all_possible_passwords.size());
+ }
+ if (test_case.all_possible_passwords) {
+ EXPECT_EQ(*test_case.all_possible_passwords,
+ parsed_form->all_possible_passwords);
+ }
+ }
+ }
+ }
+}
+
+TEST(FormParserTest, NotPasswordForm) {
+ CheckTestData({
+ {
+ "No fields", {},
+ },
+ {
+ .description_for_logging = "No password fields",
+ .fields =
+ {
+ {.form_control_type = "text"}, {.form_control_type = "text"},
+ },
+ .number_of_all_possible_passwords = 0,
+ },
+ });
+}
+
+TEST(FormParserTest, SkipNotTextFields) {
+ CheckTestData({
+ {
+ "Select between username and password fields",
+ {
+ {.role = ElementRole::USERNAME},
+ {.form_control_type = "select"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ });
+}
+
+TEST(FormParserTest, OnlyPasswordFields) {
+ CheckTestData({
+ {
+ .description_for_logging = "1 password field",
+ .fields =
+ {
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ .number_of_all_possible_passwords = 1,
+ },
+ {
+ "2 password fields, new and confirmation password",
+ {
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw"},
+ {.role = ElementRole::CONFIRMATION_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw"},
+ },
+ },
+ {
+ "2 password fields, current and new password",
+ {
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw1"},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw2"},
+ },
+ },
+ {
+ "3 password fields, current, new, confirm password",
+ {
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw1"},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw2"},
+ {.role = ElementRole::CONFIRMATION_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw2"},
+ },
+ },
+ {
+ .description_for_logging = "3 password fields with different values",
+ .fields =
+ {
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw1"},
+ {.form_control_type = "password", .value = "pw2"},
+ {.form_control_type = "password", .value = "pw3"},
+ },
+ .number_of_all_possible_passwords = 3,
+ },
+ {
+ "4 password fields, only the first 3 are considered",
+ {
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw1"},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw2"},
+ {.role = ElementRole::CONFIRMATION_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw2"},
+ {.form_control_type = "password", .value = "pw3"},
+ },
+ },
+ {
+ "4 password fields, 4th same value as 3rd and 2nd, only the first 3 "
+ "are considered",
+ {
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw1"},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw2"},
+ {.role = ElementRole::CONFIRMATION_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw2"},
+ {.form_control_type = "password", .value = "pw2"},
+ },
+ },
+ {
+ "4 password fields, all same value",
+ {
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw"},
+ {.form_control_type = "password", .value = "pw"},
+ {.form_control_type = "password", .value = "pw"},
+ {.form_control_type = "password", .value = "pw"},
+ },
+ },
+ });
+}
+
+TEST(FormParserTest, TestFocusability) {
+ CheckTestData({
+ {
+ "non-focusable fields are considered when there are no focusable "
+ "fields",
+ {
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .is_focusable = false},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password",
+ .is_focusable = false},
+ },
+ },
+ {
+ "non-focusable should be skipped when there are focusable fields",
+ {
+ {.form_control_type = "password", .is_focusable = false},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .is_focusable = true},
+ },
+ },
+ {
+ "non-focusable text fields before password",
+ {
+ {.form_control_type = "text", .is_focusable = false},
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .is_focusable = false},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .is_focusable = true},
+ },
+ },
+ {
+ "focusable and non-focusable text fields before password",
+ {
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .is_focusable = true},
+ {.form_control_type = "text", .is_focusable = false},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .is_focusable = true},
+ },
+ },
+ {
+ .description_for_logging = "many passwords, some of them focusable",
+ .fields =
+ {
+ {.form_control_type = "password", .is_focusable = false},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .is_focusable = true},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password",
+ .is_focusable = true,
+ .value = "pw"},
+ {.form_control_type = "password", .is_focusable = false},
+ {.form_control_type = "password", .is_focusable = false},
+ {.form_control_type = "password", .is_focusable = false},
+ {.form_control_type = "password", .is_focusable = false},
+ {.role = ElementRole::CONFIRMATION_PASSWORD,
+ .form_control_type = "password",
+ .is_focusable = true,
+ .value = "pw"},
+ {.form_control_type = "password", .is_focusable = false},
+ {.form_control_type = "password", .is_focusable = false},
+ },
+ // 9 distinct values in 10 password fields:
+ .number_of_all_possible_passwords = 9,
+ },
+ });
+}
+
+TEST(FormParserTest, TextAndPasswordFields) {
+ CheckTestData({
+ {
+ "Simple empty sign-in form",
+ // Forms with empty fields cannot be saved, so the parsing result for
+ // saving is empty.
+ {
+ {.role = ElementRole::USERNAME_FILLING,
+ .form_control_type = "text",
+ .value = ""},
+ {.role = ElementRole::CURRENT_PASSWORD_FILLING,
+ .form_control_type = "password",
+ .value = ""},
+ },
+ },
+ {
+ .description_for_logging = "Simple sign-in form with filled data",
+ .fields =
+ {
+ {.role = ElementRole::USERNAME, .form_control_type = "text"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ .number_of_all_possible_passwords = 1,
+ },
+ {
+ "Empty sign-in form with an extra text field",
+ {
+ {.form_control_type = "text", .value = ""},
+ {.role = ElementRole::USERNAME_FILLING,
+ .form_control_type = "text",
+ .value = ""},
+ {.role = ElementRole::CURRENT_PASSWORD_FILLING,
+ .form_control_type = "password",
+ .value = ""},
+ },
+ },
+ {
+ "Non-empty sign-in form with an extra text field",
+ {
+ {.role = ElementRole::USERNAME_SAVING,
+ .form_control_type = "text"},
+ {.role = ElementRole::USERNAME_FILLING,
+ .form_control_type = "text",
+ .value = ""},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ {
+ "Empty sign-in form with an extra invisible text field",
+ {
+ {.role = ElementRole::USERNAME_FILLING,
+ .form_control_type = "text",
+ .value = ""},
+ {.form_control_type = "text", .is_focusable = false, .value = ""},
+ {.role = ElementRole::CURRENT_PASSWORD_FILLING,
+ .form_control_type = "password",
+ .value = ""},
+ },
+ },
+ {
+ "Non-empty sign-in form with an extra invisible text field",
+ {
+ {.role = ElementRole::USERNAME, .form_control_type = "text"},
+ {.form_control_type = "text", .is_focusable = false},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ {
+ "Simple empty sign-in form with empty username",
+ // Filled forms with a username field which is left empty are
+ // suspicious. The parser will just omit the username altogether.
+ {
+ {.role = ElementRole::USERNAME_FILLING,
+ .form_control_type = "text",
+ .value = ""},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ {
+ "Simple empty sign-in form with empty password",
+ // Empty password, nothing to save.
+ {
+ {.role = ElementRole::USERNAME_FILLING,
+ .form_control_type = "text"},
+ {.role = ElementRole::CURRENT_PASSWORD_FILLING,
+ .form_control_type = "password",
+ .value = ""},
+ },
+ },
+ });
+}
+
+TEST(FormParserTest, TestAutocomplete) {
+ CheckTestData({
+ {
+ .description_for_logging =
+ "All possible password autocomplete attributes and some fields "
+ "without autocomplete",
+ .fields =
+ {
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .autocomplete_attribute = "username"},
+ {.form_control_type = "text"},
+ {.form_control_type = "password"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .autocomplete_attribute = "current-password"},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password",
+ .autocomplete_attribute = "new-password",
+ .value = "np"},
+ {.form_control_type = "password"},
+ {.role = ElementRole::CONFIRMATION_PASSWORD,
+ .form_control_type = "password",
+ .autocomplete_attribute = "new-password",
+ .value = "np"},
+ },
+ // 4 distinct password values in 5 password fields
+ .number_of_all_possible_passwords = 4,
+ },
+ {
+ .description_for_logging =
+ "Non-password autocomplete attributes are skipped",
+ .fields =
+ {
+ {.form_control_type = "text",
+ .autocomplete_attribute = "email"},
+ {.role = ElementRole::USERNAME, .form_control_type = "text"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw"},
+ {.role = ElementRole::CONFIRMATION_PASSWORD,
+ .form_control_type = "password",
+ .value = "pw"},
+ // NB: 'password' is not a valid autocomplete type hint.
+ {.form_control_type = "password",
+ .autocomplete_attribute = "password"},
+ },
+ .number_of_all_possible_passwords = 3,
+ },
+ {
+ "Basic heuristics kick in if autocomplete analysis fails",
+ {
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .autocomplete_attribute = "email"},
+ // NB: 'password' is not a valid autocomplete type hint.
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .autocomplete_attribute = "password"},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ {
+ "Partial autocomplete analysis fails if no passwords are found",
+ // The attribute 'username' is ignored, because there was no password
+ // marked up.
+ {
+ {.form_control_type = "text",
+ .autocomplete_attribute = "username"},
+ {.role = ElementRole::USERNAME, .form_control_type = "text"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ {
+ "Multiple username autocomplete attributes, fallback to base "
+ "heuristics",
+ {
+ {.form_control_type = "text",
+ .autocomplete_attribute = "username"},
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .autocomplete_attribute = "username"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password",
+ .autocomplete_attribute = "current-password"},
+ },
+ },
+ {
+ "Parsing complex autocomplete attributes",
+ {
+ // Valid information about form sections, in addition to the
+ // username hint.
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .autocomplete_attribute = "section-test billing username"},
+ {.form_control_type = "text"},
+ // Invalid composition, but the parser is simplistic and just
+ // grabs the last token.
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .autocomplete_attribute = "new-password current-password"},
+ {.form_control_type = "password"},
+ },
+ },
+ {
+ "Ignored autocomplete attributes",
+ {
+ // 'off' is ignored.
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .autocomplete_attribute = "off"},
+ // Invalid composition, the parser ignores all but the last token.
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .autocomplete_attribute = "new-password abc"},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ {
+ "Swapped username/password autocomplete attributes",
+ // Swap means ignoring autocomplete analysis and falling back to basic
+ // heuristics.
+ {
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .autocomplete_attribute = "current-password"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password",
+ .autocomplete_attribute = "username"},
+ },
+ },
+ {
+ "Autocomplete mark-up overrides visibility",
+ {
+ {.role = ElementRole::USERNAME,
+ .is_focusable = false,
+ .form_control_type = "text",
+ .autocomplete_attribute = "username"},
+ {.is_focusable = true, .form_control_type = "text"},
+ {.is_focusable = true, .form_control_type = "password"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .is_focusable = false,
+ .autocomplete_attribute = "current-password"},
+ },
+ },
+ });
+}
+
+TEST(FormParserTest, DisabledFields) {
+ CheckTestData({
+ {
+ .description_for_logging = "The disabled attribute is ignored",
+ .fields =
+ {
+ {.is_enabled = true, .form_control_type = "text"},
+ {.role = ElementRole::USERNAME,
+ .is_enabled = false,
+ .form_control_type = "text"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .is_enabled = false},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password",
+ .is_enabled = true},
+ },
+ .number_of_all_possible_passwords = 2,
+ },
+ });
+}
+
+TEST(FormParserTest, SkippingFieldsWithCreditCardFields) {
+ CheckTestData({
+ {
+ "Simple form, all fields are credit-card-related",
+ {
+ {.form_control_type = "text",
+ .autocomplete_attribute = "cc-name"},
+ {.form_control_type = "password",
+ .autocomplete_attribute = "cc-any-string"},
+ },
+ },
+ {
+ .description_for_logging = "Non-CC fields are considered",
+ .fields =
+ {
+ {.role = ElementRole::USERNAME, .form_control_type = "text"},
+ {.form_control_type = "text",
+ .autocomplete_attribute = "cc-name"},
+ {.form_control_type = "password",
+ .autocomplete_attribute = "cc-any-string"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ .number_of_all_possible_passwords = 2,
+ },
+ });
+}
+
+TEST(FormParserTest, ReadonlyFields) {
+ CheckTestData({
+ {
+ "For usernames, readonly does not matter",
+ {
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .is_readonly = true},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ {
+ "For passwords, readonly means: 'give up', perhaps there is a "
+ "virtual keyboard, filling might be ignored",
+ {
+ {.form_control_type = "text"},
+ {.form_control_type = "password", .is_readonly = true},
+ },
+ },
+ {
+ "But correctly marked passwords are accepted even if readonly",
+ {
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .autocomplete_attribute = "username"},
+ {.role = ElementRole::NEW_PASSWORD,
+ .autocomplete_attribute = "new-password",
+ .form_control_type = "password",
+ .is_readonly = true},
+ {.role = ElementRole::CONFIRMATION_PASSWORD,
+ .autocomplete_attribute = "new-password",
+ .form_control_type = "password",
+ .is_readonly = true},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .autocomplete_attribute = "current-password",
+ .form_control_type = "password",
+ .is_readonly = true},
+ },
+ },
+ {
+ .description_for_logging = "And passwords already filled by user or "
+ "Chrome on pageload are accepted even if "
+ "readonly",
+ .fields =
+ {
+ {.role = ElementRole::USERNAME, .form_control_type = "text"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .properties_mask =
+ FieldPropertiesFlags::AUTOFILLED_ON_PAGELOAD,
+ .form_control_type = "password",
+ .is_readonly = true},
+ {.role = ElementRole::NEW_PASSWORD,
+ .properties_mask = FieldPropertiesFlags::USER_TYPED,
+ .form_control_type = "password",
+ .is_readonly = true},
+ {.form_control_type = "password", .is_readonly = true},
+ },
+ .number_of_all_possible_passwords = 3,
+ },
+ {
+ .description_for_logging = "And passwords already filled by user or "
+ "Chrome with FOAS are accepted even if "
+ "readonly",
+ .fields =
+ {
+ {.role = ElementRole::USERNAME, .form_control_type = "text"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .properties_mask =
+ FieldPropertiesFlags::AUTOFILLED_ON_USER_TRIGGER,
+ .form_control_type = "password",
+ .is_readonly = true},
+ {.role = ElementRole::NEW_PASSWORD,
+ .properties_mask = FieldPropertiesFlags::USER_TYPED,
+ .form_control_type = "password",
+ .is_readonly = true},
+ {.form_control_type = "password", .is_readonly = true},
+ },
+ .number_of_all_possible_passwords = 3,
+ },
+ });
+}
+
+TEST(FormParserTest, ServerHints) {
+ CheckTestData({
+ {
+ "Empty predictions don't cause panic",
+ {
+ {.form_control_type = "text"},
+ {.role = ElementRole::USERNAME, .form_control_type = "text"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ {
+ "Username-only predictions are ignored",
+ {
+ {.form_control_type = "text",
+ .prediction = {.type = autofill::USERNAME,
+ .may_use_prefilled_placeholder = true}},
+ {.role = ElementRole::USERNAME, .form_control_type = "text"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ {
+ "Simple predictions work",
+ {
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS,
+ .may_use_prefilled_placeholder = true}},
+ {.form_control_type = "text"},
+ {.form_control_type = "password"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .prediction = {.type = autofill::PASSWORD,
+ .may_use_prefilled_placeholder = true},
+ .form_control_type = "password"},
+ },
+ .username_may_use_prefilled_placeholder = true,
+ },
+ {
+ .description_for_logging = "Longer predictions work",
+ .fields =
+ {
+ {.role = ElementRole::USERNAME,
+ .prediction = {.type = autofill::USERNAME},
+ .form_control_type = "text"},
+ {.form_control_type = "text"},
+ {.form_control_type = "password"},
+ {.role = ElementRole::NEW_PASSWORD,
+ .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD},
+ .form_control_type = "password"},
+ {.role = ElementRole::CONFIRMATION_PASSWORD,
+ .prediction = {.type = autofill::CONFIRMATION_PASSWORD},
+ .form_control_type = "password"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .prediction = {.type = autofill::PASSWORD},
+ .form_control_type = "password"},
+ },
+ .number_of_all_possible_passwords = 4,
+ },
+ });
+}
+
+TEST(FormParserTest, Interactability) {
+ CheckTestData({
+ {
+ "If all fields are hidden, all are considered",
+ {
+ {.form_control_type = "text", .is_focusable = false},
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .is_focusable = false},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .is_focusable = false},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password",
+ .is_focusable = false},
+ },
+ },
+ {
+ .description_for_logging =
+ "If some fields are hidden, only visible are considered",
+ .fields =
+ {
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .is_focusable = true},
+ {.form_control_type = "text", .is_focusable = false},
+ {.form_control_type = "password", .is_focusable = false},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .is_focusable = true},
+ },
+ .number_of_all_possible_passwords = 2,
+ },
+ {
+ .description_for_logging =
+ "If user typed somewhere, only typed-into fields are considered, "
+ "even if not currently visible",
+ .fields =
+ {
+ {.role = ElementRole::USERNAME,
+ .properties_mask = FieldPropertiesFlags::USER_TYPED,
+ .form_control_type = "text",
+ .is_focusable = false},
+ {.form_control_type = "text", .is_focusable = true},
+ {.form_control_type = "password", .is_focusable = false},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .properties_mask = FieldPropertiesFlags::AUTOFILLED,
+ .is_focusable = true},
+ {.role = ElementRole::NEW_PASSWORD,
+ .form_control_type = "password",
+ .properties_mask = FieldPropertiesFlags::USER_TYPED,
+ .is_focusable = true},
+ },
+ .number_of_all_possible_passwords = 3,
+ },
+ {
+ "Interactability for usernames is only considered before the first "
+ "relevant password. That way, if, e.g., the username gets filled and "
+ "hidden (to let the user enter password), and there is another text "
+ "field visible below, the maximum Interactability won't end up being "
+ "kPossible, which would exclude the hidden username.",
+ {
+ {.role = ElementRole::USERNAME,
+ .properties_mask = FieldPropertiesFlags::AUTOFILLED,
+ .form_control_type = "text",
+ .is_focusable = false},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .properties_mask = FieldPropertiesFlags::AUTOFILLED,
+ .is_focusable = true},
+ {.form_control_type = "text", .is_focusable = true, .value = ""},
+ },
+ },
+ {
+ "Interactability also matters for HTML classifier.",
+ {
+ {.form_control_type = "text",
+ .is_focusable = false,
+ .predicted_username = 0},
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .is_focusable = true},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .is_focusable = true},
+ },
+ },
+ });
+}
+
+TEST(FormParserTest, AllPossiblePasswords) {
+ const autofill::ValueElementVector kPasswords = {
+ {ASCIIToUTF16("a"), ASCIIToUTF16("p1")},
+ {ASCIIToUTF16("b"), ASCIIToUTF16("p3")},
+ };
+ CheckTestData({
+ {
+ .description_for_logging = "It is always the first field name which "
+ "is associated with a duplicated password "
+ "value",
+ .fields =
+ {
+ {.form_control_type = "password", .name = "p1", .value = "a"},
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .autocomplete_attribute = "username"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .autocomplete_attribute = "current-password",
+ .value = "a"},
+ {.form_control_type = "text"},
+ {.form_control_type = "text"},
+ {.form_control_type = "password", .name = "p3", .value = "b"},
+ {.form_control_type = "password", .value = "b"},
+ },
+ .number_of_all_possible_passwords = 2,
+ .all_possible_passwords = &kPasswords,
+ },
+ {
+ .description_for_logging =
+ "Empty values don't get added to all_possible_passwords",
+ .fields =
+ {
+ {.form_control_type = "password", .value = ""},
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .autocomplete_attribute = "username"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .autocomplete_attribute = "current-password",
+ .value = ""},
+ {.form_control_type = "text"},
+ {.form_control_type = "text"},
+ {.form_control_type = "password", .value = ""},
+ {.form_control_type = "password", .value = ""},
+ },
+ .number_of_all_possible_passwords = 0,
+ },
+ {
+ .description_for_logging =
+ "A particular type of a squashed form (sign-in + sign-up)",
+ .fields =
+ {
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .autocomplete_attribute = "username"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .autocomplete_attribute = "current-password"},
+ {.form_control_type = "text"},
+ {.form_control_type = "text"},
+ {.form_control_type = "password"},
+ {.form_control_type = "password"},
+ },
+ .number_of_all_possible_passwords = 3,
+ },
+ {
+ .description_for_logging = "A strange but not squashed form",
+ .fields =
+ {
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ {.form_control_type = "text"},
+ {.form_control_type = "text"},
+ {.form_control_type = "password"},
+ {.form_control_type = "password"},
+ {.form_control_type = "password"},
+ },
+ .number_of_all_possible_passwords = 4,
+ },
+ });
+}
+
+TEST(FormParserTest, UsernamePredictions) {
+ CheckTestData({
+ {
+ "Username prediction overrides structure",
+ {
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .predicted_username = 0},
+ {.form_control_type = "text"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ {
+ "Username prediction does not override structure if empty and mode "
+ "is SAVING",
+ {
+ {.role = ElementRole::USERNAME_FILLING,
+ .form_control_type = "text",
+ .predicted_username = 2,
+ .value = ""},
+ {.role = ElementRole::USERNAME_SAVING,
+ .form_control_type = "text"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ {
+ "Username prediction does not override autocomplete analysis",
+ {
+ {.form_control_type = "text", .predicted_username = 0},
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .autocomplete_attribute = "username"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .autocomplete_attribute = "current-password"},
+ },
+ },
+ {
+ "Username prediction does not override server hints",
+ {
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS}},
+ {.form_control_type = "text", .predicted_username = 0},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .prediction = {.type = autofill::PASSWORD},
+ .form_control_type = "password"},
+ },
+ },
+ {
+ "Username prediction order matters",
+ {
+ {.role = ElementRole::USERNAME,
+ .form_control_type = "text",
+ .predicted_username = 1},
+ {.form_control_type = "text", .predicted_username = 4},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ });
+}
+
+// In some situations, server hints or autocomplete mark-up do not provide the
+// username might be omitted. Sometimes this is a truthful signal (there might
+// be no username despite the presence of plain text fields), but often this is
+// just incomplete data. In the long term, the server hints should be complete
+// and also cover cases when the autocomplete mark-up is lacking; at that point,
+// the parser should just trust that the signal is truthful. Until then,
+// however, the parser is trying to complement the signal with its structural
+// heuristics.
+TEST(FormParserTest, ComplementingResults) {
+ CheckTestData({
+ {
+ "Current password from autocomplete analysis, username from basic "
+ "heuristics",
+ {
+ {.role = ElementRole::USERNAME, .form_control_type = "text"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password",
+ .autocomplete_attribute = "current-password"},
+ },
+ },
+ {
+ "New and confirmation passwords from server, username from basic "
+ "heuristics",
+ {
+ {.role = ElementRole::USERNAME, .form_control_type = "text"},
+ {.role = ElementRole::CONFIRMATION_PASSWORD,
+ .prediction = {.type = autofill::CONFIRMATION_PASSWORD},
+ .form_control_type = "password"},
+ {.form_control_type = "text"},
+ {.role = ElementRole::NEW_PASSWORD,
+ .prediction = {.type = autofill::NEW_PASSWORD},
+ .form_control_type = "password"},
+ },
+ },
+ {
+ "No password from server still means that serve hints are ignored.",
+ {
+ {.prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS},
+ .form_control_type = "text"},
+ {.role = ElementRole::USERNAME, .form_control_type = "text"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ });
+}
+
+// Until autofill server learns to provide CVC-related hints, the parser should
+// try to get the hint from the field names.
+TEST(FormParserTest, CVC) {
+ CheckTestData({
+ {
+ "Name of 'verification_type' matches the CVC pattern.",
+ {
+ {.role = ElementRole::USERNAME, .form_control_type = "text"},
+ {.form_control_type = "text", .name = "verification_type"},
+ {.role = ElementRole::CURRENT_PASSWORD,
+ .form_control_type = "password"},
+ },
+ },
+ });
+}
+
+} // namespace
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/form_parsing/fuzzer/BUILD.gn b/chromium/components/password_manager/core/browser/form_parsing/fuzzer/BUILD.gn
index cba3911fbee..3b5c79756e1 100644
--- a/chromium/components/password_manager/core/browser/form_parsing/fuzzer/BUILD.gn
+++ b/chromium/components/password_manager/core/browser/form_parsing/fuzzer/BUILD.gn
@@ -9,10 +9,35 @@ static_library("fuzzer_support") {
sources = [
"data_accessor.cc",
"data_accessor.h",
+ "form_data_producer.cc",
+ "form_data_producer.h",
]
deps = [
"//base",
+ "//components/autofill/core/common",
+ "//components/password_manager/core/browser/form_parsing",
+ "//url",
+ ]
+}
+
+# The part of fuzzer support which depends on protobufs is separate from the
+# rest, which is also linked in unittests. This is because protobuf support for
+# the fuzzer gets compiled with 'lite' runtime for unittests but without it for
+# fuzzers. As a result, if the proto_library target is shared both by the fuzzer
+# and the unittest target, linker errors are the result.
+static_library("fuzzer_support_proto") {
+ sources = [
+ "form_data_proto_producer.cc",
+ "form_data_proto_producer.h",
+ ]
+
+ deps = [
+ ":form_data_essentials_proto",
+ "//base",
+ "//components/autofill/core/common",
+ "//components/password_manager/core/browser/form_parsing",
+ "//url",
]
}
@@ -29,10 +54,20 @@ source_set("unit_tests") {
]
}
+# TODO(crbug.com/845426): There are currently four fuzzers, all combinations of
+# two binary parameters. The first parameter is whether the input data for the
+# fuzzed code is prepared directly from the raw fuzzing string, or from a
+# protobuf description. The second parameter is whether the fuzzed code is the
+# iOS-specific FormData parser, or the generic one. Ultimately, there will be
+# only the generic one, but currently it is under development, so both parsers
+# are used (and need to be fuzzed). The iOS parser is older and its fuzzer's
+# name did not hint at the "iOS" restriction. It is better to keep that name
+# the same, because the security team watches statistics of the fuzzers
+# (https://crbug.com/828705#c11) and renaming causes confusion. So instead, the
+# new fuzzer has the "_generic" suffix on its target name.
+
fuzzer_test("password_manager_form_parser_fuzzer") {
sources = [
- "form_data_producer.cc",
- "form_data_producer.h",
"form_parser_fuzzer.cc",
]
@@ -42,7 +77,6 @@ fuzzer_test("password_manager_form_parser_fuzzer") {
"//base:i18n",
"//components/autofill/core/common",
"//components/password_manager/core/browser/form_parsing",
- "//url",
]
dict = "form_parser_fuzzer.dict"
@@ -50,19 +84,53 @@ fuzzer_test("password_manager_form_parser_fuzzer") {
fuzzer_test("password_manager_form_parser_proto_fuzzer") {
sources = [
- "form_data_proto_producer.cc",
- "form_data_proto_producer.h",
"form_parser_proto_fuzzer.cc",
]
deps = [
":form_data_essentials_proto",
+ ":fuzzer_support",
+ ":fuzzer_support_proto",
+ "//base",
+ "//base:i18n",
+ "//components/autofill/core/common",
+ "//components/password_manager/core/browser/form_parsing",
+ "//third_party/libprotobuf-mutator",
+ ]
+
+ dict = "form_parser_fuzzer.dict"
+}
+
+fuzzer_test("password_manager_form_parser_generic_fuzzer") {
+ sources = [
+ "form_parser_generic_fuzzer.cc",
+ ]
+
+ deps = [
+ ":fuzzer_support",
+ "//base",
+ "//base:i18n",
+ "//components/autofill/core/common",
+ "//components/password_manager/core/browser/form_parsing",
+ ]
+
+ dict = "form_parser_fuzzer.dict"
+}
+
+fuzzer_test("password_manager_form_parser_proto_generic_fuzzer") {
+ sources = [
+ "form_parser_proto_generic_fuzzer.cc",
+ ]
+
+ deps = [
+ ":form_data_essentials_proto",
+ ":fuzzer_support",
+ ":fuzzer_support_proto",
"//base",
"//base:i18n",
"//components/autofill/core/common",
"//components/password_manager/core/browser/form_parsing",
"//third_party/libprotobuf-mutator",
- "//url",
]
dict = "form_parser_fuzzer.dict"
diff --git a/chromium/components/password_manager/core/browser/form_parsing/fuzzer/form_parser_generic_fuzzer.cc b/chromium/components/password_manager/core/browser/form_parsing/fuzzer/form_parser_generic_fuzzer.cc
new file mode 100644
index 00000000000..eb9763323fb
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/form_parsing/fuzzer/form_parser_generic_fuzzer.cc
@@ -0,0 +1,44 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/at_exit.h"
+#include "base/i18n/icu_util.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/form_parsing/form_parser.h"
+#include "components/password_manager/core/browser/form_parsing/fuzzer/data_accessor.h"
+#include "components/password_manager/core/browser/form_parsing/fuzzer/form_data_producer.h"
+
+namespace password_manager {
+
+// ICU is used inside GURL parser, which is used by GenerateWithDataAccessor.
+struct IcuEnvironment {
+ IcuEnvironment() { CHECK(base::i18n::InitializeICU()); }
+ // used by ICU integration.
+ base::AtExitManager at_exit_manager;
+};
+
+IcuEnvironment* env = new IcuEnvironment();
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ DataAccessor accessor(data, size);
+ FormParsingMode mode = accessor.ConsumeBit() ? FormParsingMode::FILLING
+ : FormParsingMode::SAVING;
+ autofill::FormData form_data = GenerateWithDataAccessor(&accessor);
+
+ std::unique_ptr<autofill::PasswordForm> result =
+ ParseFormData(form_data, nullptr, mode);
+ if (result) {
+ // Create a copy of the result -- running the copy-constructor might
+ // discover some invalid data in |result|.
+ autofill::PasswordForm copy(*result);
+ }
+ return 0;
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/form_parsing/fuzzer/form_parser_proto_generic_fuzzer.cc b/chromium/components/password_manager/core/browser/form_parsing/fuzzer/form_parser_proto_generic_fuzzer.cc
new file mode 100644
index 00000000000..b02e04b16ea
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/form_parsing/fuzzer/form_parser_proto_generic_fuzzer.cc
@@ -0,0 +1,43 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/at_exit.h"
+#include "base/i18n/icu_util.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/form_parsing/form_parser.h"
+#include "components/password_manager/core/browser/form_parsing/fuzzer/form_data_essentials.pb.h"
+#include "components/password_manager/core/browser/form_parsing/fuzzer/form_data_proto_producer.h"
+#include "testing/libfuzzer/proto/lpm_interface.h"
+
+namespace password_manager {
+
+// ICU is used inside GURL parser, which is used by GenerateWithDataAccessor.
+struct IcuEnvironment {
+ IcuEnvironment() { CHECK(base::i18n::InitializeICU()); }
+ // used by ICU integration.
+ base::AtExitManager at_exit_manager;
+};
+
+IcuEnvironment* env = new IcuEnvironment();
+
+DEFINE_BINARY_PROTO_FUZZER(const ::form_data_fuzzer::Form& form_proto) {
+ FormParsingMode mode = form_proto.is_mode_filling() ? FormParsingMode::FILLING
+ : FormParsingMode::SAVING;
+ autofill::FormData form_data = GenerateWithProto(form_proto);
+
+ std::unique_ptr<autofill::PasswordForm> result =
+ ParseFormData(form_data, nullptr, mode);
+ if (result) {
+ // Create a copy of the result -- running the copy-constructor might
+ // discover some invalid data in |result|.
+ autofill::PasswordForm copy(*result);
+ }
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/form_parsing/password_field_prediction.cc b/chromium/components/password_manager/core/browser/form_parsing/password_field_prediction.cc
new file mode 100644
index 00000000000..4f49355f1fd
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/form_parsing/password_field_prediction.cc
@@ -0,0 +1,63 @@
+// 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/password_manager/core/browser/form_parsing/password_field_prediction.h"
+
+#include "base/logging.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/common/form_data.h"
+
+using autofill::FormData;
+using autofill::FormStructure;
+using autofill::ServerFieldType;
+
+namespace password_manager {
+
+namespace {
+
+// Returns true if the field is password or username prediction.
+bool IsCredentialRelatedPrediction(ServerFieldType type) {
+ return DeriveFromServerFieldType(type) != CredentialFieldType::kNone;
+}
+
+} // namespace
+
+CredentialFieldType DeriveFromServerFieldType(ServerFieldType type) {
+ switch (type) {
+ case autofill::USERNAME:
+ case autofill::USERNAME_AND_EMAIL_ADDRESS:
+ return CredentialFieldType::kUsername;
+ case autofill::PASSWORD:
+ return CredentialFieldType::kCurrentPassword;
+ case autofill::ACCOUNT_CREATION_PASSWORD:
+ case autofill::NEW_PASSWORD:
+ return CredentialFieldType::kNewPassword;
+ case autofill::CONFIRMATION_PASSWORD:
+ return CredentialFieldType::kConfirmationPassword;
+ default:
+ return CredentialFieldType::kNone;
+ }
+}
+
+FormPredictions ConvertToFormPredictions(const FormStructure& form_structure) {
+ FormPredictions result;
+ for (const auto& field : form_structure) {
+ ServerFieldType server_type = field->server_type();
+ if (IsCredentialRelatedPrediction(server_type)) {
+ bool may_use_prefilled_placeholder = false;
+ for (const auto& predictions : field->server_predictions()) {
+ may_use_prefilled_placeholder |=
+ predictions.may_use_prefilled_placeholder();
+ }
+
+ result[field->unique_renderer_id] = PasswordFieldPrediction{
+ .type = server_type,
+ .may_use_prefilled_placeholder = may_use_prefilled_placeholder};
+ }
+ }
+
+ return result;
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/form_parsing/password_field_prediction.h b/chromium/components/password_manager/core/browser/form_parsing/password_field_prediction.h
new file mode 100644
index 00000000000..c4bb30a063c
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/form_parsing/password_field_prediction.h
@@ -0,0 +1,47 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_FORM_PARSING_PASSWORD_FIELD_PREDICTION_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FORM_PARSING_PASSWORD_FIELD_PREDICTION_H_
+
+#include <stdint.h>
+#include <map>
+
+#include "components/autofill/core/browser/field_types.h"
+
+namespace autofill {
+class FormStructure;
+} // namespace autofill
+
+namespace password_manager {
+
+enum class CredentialFieldType {
+ kNone,
+ kUsername,
+ kCurrentPassword,
+ kNewPassword,
+ kConfirmationPassword
+};
+
+// Transforms the general field type to the information useful for password
+// forms.
+CredentialFieldType DeriveFromServerFieldType(autofill::ServerFieldType type);
+
+// Contains server predictions for a field.
+struct PasswordFieldPrediction {
+ autofill::ServerFieldType type;
+ bool may_use_prefilled_placeholder = false;
+};
+
+// Contains server predictions for a form. Keys are unique renderer ids of
+// fields.
+using FormPredictions = std::map<uint32_t, PasswordFieldPrediction>;
+
+// Extracts all password related server predictions from |form_structure|.
+FormPredictions ConvertToFormPredictions(
+ const autofill::FormStructure& form_structure);
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FORM_PARSING_PASSWORD_FIELD_PREDICTION_H_
diff --git a/chromium/components/password_manager/core/browser/form_parsing/password_field_prediction_unittest.cc b/chromium/components/password_manager/core/browser/form_parsing/password_field_prediction_unittest.cc
new file mode 100644
index 00000000000..2904367bb40
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/form_parsing/password_field_prediction_unittest.cc
@@ -0,0 +1,127 @@
+// 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/password_manager/core/browser/form_parsing/password_field_prediction.h"
+
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/common/form_data.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using autofill::AutofillField;
+using autofill::ACCOUNT_CREATION_PASSWORD;
+using autofill::CONFIRMATION_PASSWORD;
+using autofill::EMAIL_ADDRESS;
+using autofill::FormData;
+using autofill::FormFieldData;
+using autofill::FormStructure;
+using autofill::NEW_PASSWORD;
+using autofill::NO_SERVER_DATA;
+using autofill::PASSWORD;
+using autofill::ServerFieldType;
+using autofill::UNKNOWN_TYPE;
+using autofill::USERNAME;
+using autofill::USERNAME_AND_EMAIL_ADDRESS;
+using base::ASCIIToUTF16;
+
+using FieldPrediction =
+ autofill::AutofillQueryResponseContents::Field::FieldPrediction;
+
+namespace password_manager {
+
+namespace {
+
+TEST(FormPredictionsTest, ConvertToFormPredictions) {
+ struct TestField {
+ std::string name;
+ std::string form_control_type;
+ ServerFieldType input_type;
+ ServerFieldType expected_type;
+ bool may_use_prefilled_placeholder;
+ } test_fields[] = {
+ {"full_name", "text", UNKNOWN_TYPE, UNKNOWN_TYPE, false},
+ // Password Manager is interested only in credential related types.
+ {"Email", "email", EMAIL_ADDRESS, UNKNOWN_TYPE, false},
+ {"username", "text", USERNAME, USERNAME, true},
+ {"Password", "password", PASSWORD, PASSWORD, false},
+ {"confirm_password", "password", CONFIRMATION_PASSWORD,
+ CONFIRMATION_PASSWORD, true}};
+
+ FormData form_data;
+ for (size_t i = 0; i < base::size(test_fields); ++i) {
+ FormFieldData field;
+ field.unique_renderer_id = i + 1000;
+ field.name = ASCIIToUTF16(test_fields[i].name);
+ field.form_control_type = test_fields[i].form_control_type;
+ form_data.fields.push_back(field);
+ }
+
+ FormStructure form_structure(form_data);
+ size_t expected_predictions = 0;
+ // Set server predictions and create expected votes.
+ for (size_t i = 0; i < base::size(test_fields); ++i) {
+ AutofillField* field = form_structure.field(i);
+ field->set_server_type(test_fields[i].input_type);
+ ServerFieldType expected_type = test_fields[i].expected_type;
+
+ FieldPrediction prediction;
+ prediction.set_may_use_prefilled_placeholder(
+ test_fields[i].may_use_prefilled_placeholder);
+ field->set_server_predictions({prediction});
+
+ if (expected_type != UNKNOWN_TYPE)
+ ++expected_predictions;
+ }
+
+ FormPredictions actual_predictions = ConvertToFormPredictions(form_structure);
+
+ // Check whether actual predictions are equal to expected ones.
+ EXPECT_EQ(expected_predictions, actual_predictions.size());
+
+ for (size_t i = 0; i < base::size(test_fields); ++i) {
+ uint32_t unique_renderer_id = form_data.fields[i].unique_renderer_id;
+ auto it = actual_predictions.find(unique_renderer_id);
+ if (test_fields[i].expected_type == UNKNOWN_TYPE) {
+ EXPECT_EQ(actual_predictions.end(), it);
+ } else {
+ ASSERT_NE(actual_predictions.end(), it);
+ EXPECT_EQ(test_fields[i].expected_type, it->second.type);
+ EXPECT_EQ(test_fields[i].may_use_prefilled_placeholder,
+ it->second.may_use_prefilled_placeholder);
+ }
+ }
+}
+
+TEST(FormPredictionsTest, DeriveFromServerFieldType) {
+ struct TestCase {
+ const char* name;
+ // Input.
+ ServerFieldType server_type;
+ CredentialFieldType expected_result;
+ } test_cases[] = {
+ {"No prediction", NO_SERVER_DATA, CredentialFieldType::kNone},
+ {"Irrelevant type", EMAIL_ADDRESS, CredentialFieldType::kNone},
+ {"Username", USERNAME, CredentialFieldType::kUsername},
+ {"Username/Email", USERNAME_AND_EMAIL_ADDRESS,
+ CredentialFieldType::kUsername},
+ {"Password", PASSWORD, CredentialFieldType::kCurrentPassword},
+ {"New password", NEW_PASSWORD, CredentialFieldType::kNewPassword},
+ {"Account creation password", ACCOUNT_CREATION_PASSWORD,
+ CredentialFieldType::kNewPassword},
+ {"Confirmation password", CONFIRMATION_PASSWORD,
+ CredentialFieldType::kConfirmationPassword},
+ };
+
+ for (const TestCase& test_case : test_cases) {
+ SCOPED_TRACE(test_case.name);
+ EXPECT_EQ(test_case.expected_result,
+ DeriveFromServerFieldType(test_case.server_type));
+ }
+}
+
+} // namespace
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/form_submission_observer.h b/chromium/components/password_manager/core/browser/form_submission_observer.h
new file mode 100644
index 00000000000..e886e01870f
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/form_submission_observer.h
@@ -0,0 +1,25 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_FORM_SUBMISSION_OBSERVER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FORM_SUBMISSION_OBSERVER_H_
+
+namespace password_manager {
+
+class PasswordManagerDriver;
+
+// Observer interface for the password manager about the relevant events from
+// the embedder.
+class FormSubmissionObserver {
+ public:
+ // Notifies the listener that a form may be submitted due to a navigation.
+ virtual void OnStartNavigation(PasswordManagerDriver* driver) = 0;
+
+ protected:
+ virtual ~FormSubmissionObserver() = default;
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FORM_SUBMISSION_OBSERVER_H_
diff --git a/chromium/components/password_manager/core/browser/hash_password_manager.cc b/chromium/components/password_manager/core/browser/hash_password_manager.cc
index 74f5e76922a..ef6f420206e 100644
--- a/chromium/components/password_manager/core/browser/hash_password_manager.cc
+++ b/chromium/components/password_manager/core/browser/hash_password_manager.cc
@@ -12,12 +12,9 @@
#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 "crypto/openssl_util.h"
-#include "crypto/random.h"
-#include "third_party/boringssl/src/include/openssl/evp.h"
namespace {
-constexpr size_t kSyncPasswordSaltLength = 16;
+
constexpr char kSeparator = '.';
constexpr char kHashFieldKey[] = "hash";
constexpr char kLastSignInTimeFieldKey[] = "last_signin";
@@ -27,6 +24,7 @@ constexpr char kIsGaiaFieldKey[] = "is_gaia";
// The maximum number of password hash data we store in prefs.
constexpr size_t kMaxPasswordHashDataDictSize = 5;
+
} // namespace
namespace password_manager {
@@ -88,6 +86,10 @@ std::string GetAndDecryptField(const base::Value& dict,
: std::string();
}
+bool IsGaiaPassword(const base::Value& dict) {
+ return GetAndDecryptField(dict, kIsGaiaFieldKey) == "true";
+}
+
// Packs |salt| and |password_length| to a string.
std::string LengthAndSaltToString(const std::string& salt,
size_t password_length) {
@@ -140,47 +142,6 @@ base::Optional<PasswordHashData> ConvertToPasswordHashData(
} // namespace
-SyncPasswordData::SyncPasswordData(const base::string16& password,
- bool force_update)
- : length(password.size()),
- salt(HashPasswordManager::CreateRandomSalt()),
- hash(HashPasswordManager::CalculatePasswordHash(password, salt)),
- force_update(force_update) {}
-
-bool SyncPasswordData::MatchesPassword(const base::string16& password) {
- if (password.size() != this->length)
- return false;
- return HashPasswordManager::CalculatePasswordHash(password, this->salt) ==
- this->hash;
-}
-
-PasswordHashData::PasswordHashData(const std::string& username,
- const base::string16& password,
- bool force_update,
- bool is_gaia_password)
- : username(username),
- length(password.size()),
- salt(HashPasswordManager::CreateRandomSalt()),
- hash(HashPasswordManager::CalculatePasswordHash(password, salt)),
- force_update(force_update),
- is_gaia_password(is_gaia_password) {}
-
-PasswordHashData::PasswordHashData() = default;
-
-PasswordHashData::PasswordHashData(const PasswordHashData& other) = default;
-
-bool PasswordHashData::MatchesPassword(const std::string& username,
- const base::string16& password,
- bool is_gaia_password) {
- if (password.size() != this->length || username != this->username ||
- is_gaia_password != this->is_gaia_password) {
- return false;
- }
-
- return HashPasswordManager::CalculatePasswordHash(password, this->salt) ==
- this->hash;
-}
-
HashPasswordManager::HashPasswordManager(PrefService* prefs) : prefs_(prefs) {}
bool HashPasswordManager::SavePasswordHash(const base::string16& password) {
@@ -209,7 +170,9 @@ bool HashPasswordManager::SavePasswordHash(const std::string username,
// sign in timestamp.
ListPrefUpdate update(prefs_, prefs::kPasswordHashDataList);
for (base::Value& password_hash_data : update.Get()->GetList()) {
- if (GetAndDecryptField(password_hash_data, kUsernameFieldKey) == username) {
+ if (AreUsernamesSame(
+ GetAndDecryptField(password_hash_data, kUsernameFieldKey),
+ IsGaiaPassword(password_hash_data), username, is_gaia_password)) {
base::Optional<PasswordHashData> existing_password_hash =
ConvertToPasswordHashData(password_hash_data);
if (existing_password_hash && existing_password_hash->MatchesPassword(
@@ -256,18 +219,32 @@ void HashPasswordManager::ClearSavedPasswordHash(const std::string& username,
bool is_gaia_password) {
if (prefs_) {
ListPrefUpdate update(prefs_, prefs::kPasswordHashDataList);
- for (auto it = update->GetList().begin(); it != update->GetList().end();
- it++) {
- if (GetAndDecryptField(*it, kUsernameFieldKey) == username &&
- GetAndDecryptField(*it, kIsGaiaFieldKey) ==
- BooleanToString(is_gaia_password)) {
- update->GetList().erase(it);
- return;
+ for (auto it = update->GetList().begin(); it != update->GetList().end();) {
+ if (AreUsernamesSame(GetAndDecryptField(*it, kUsernameFieldKey),
+ IsGaiaPassword(*it), username, is_gaia_password)) {
+ it = update->GetList().erase(it);
+ } else {
+ it++;
}
}
}
}
+void HashPasswordManager::ClearAllPasswordHash(bool is_gaia_password) {
+ if (!prefs_)
+ return;
+
+ ListPrefUpdate update(prefs_, prefs::kPasswordHashDataList);
+ for (auto it = update->GetList().begin(); it != update->GetList().end();) {
+ if (GetAndDecryptField(*it, kIsGaiaFieldKey) ==
+ BooleanToString(is_gaia_password)) {
+ it = update->GetList().erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
base::Optional<SyncPasswordData> HashPasswordManager::RetrievePasswordHash() {
if (!prefs_ || !prefs_->HasPrefPath(prefs::kSyncPasswordHash))
return base::nullopt;
@@ -311,9 +288,8 @@ base::Optional<PasswordHashData> HashPasswordManager::RetrievePasswordHash(
for (const base::Value& entry :
prefs_->GetList(prefs::kPasswordHashDataList)->GetList()) {
- if (GetAndDecryptField(entry, kUsernameFieldKey) == username &&
- GetAndDecryptField(entry, kIsGaiaFieldKey) ==
- BooleanToString(is_gaia_password)) {
+ if (AreUsernamesSame(GetAndDecryptField(entry, kUsernameFieldKey),
+ IsGaiaPassword(entry), username, is_gaia_password)) {
return ConvertToPasswordHashData(entry);
}
}
@@ -334,9 +310,8 @@ bool HashPasswordManager::HasPasswordHash(const std::string& username,
for (const base::Value& entry :
prefs_->GetList(prefs::kPasswordHashDataList)->GetList()) {
- if (username == GetAndDecryptField(entry, kUsernameFieldKey) &&
- BooleanToString(is_gaia_password) ==
- GetAndDecryptField(entry, kIsGaiaFieldKey)) {
+ if (AreUsernamesSame(GetAndDecryptField(entry, kUsernameFieldKey),
+ IsGaiaPassword(entry), username, is_gaia_password)) {
return true;
}
}
@@ -346,12 +321,45 @@ bool HashPasswordManager::HasPasswordHash(const std::string& username,
void HashPasswordManager::MaybeMigrateExistingSyncPasswordHash(
const std::string& sync_username) {
- if (!prefs_ || sync_username.empty() ||
- !prefs_->HasPrefPath(prefs::kSyncPasswordHash) ||
+ if (!prefs_ || sync_username.empty())
+ return;
+
+ // For a very small portion of Canary and Dev users, there maybe a captured
+ // password hash with no |is_gaia_password| field.
+ // Note that, there's at most one such hash.
+ bool has_sync_password =
+ HasPasswordHash(sync_username, /*is_gaia_password=*/true);
+ if (prefs_->HasPrefPath(prefs::kPasswordHashDataList)) {
+ ListPrefUpdate update(prefs_, prefs::kPasswordHashDataList);
+ auto entry_to_remove = update->GetList().end();
+ for (auto it = update->GetList().begin(); it != update->GetList().end();
+ it++) {
+ if (it->FindKey(kIsGaiaFieldKey))
+ continue;
+ // If there's another hash matches |sync_username|, remove the entry
+ // without |is_gaia_password| field. Otherwise, set the missing
+ // |is_gaia_password| field to true.
+ if (has_sync_password) {
+ entry_to_remove = it;
+ } else {
+ std::string encrypted_is_gaia_value =
+ EncryptString(BooleanToString(true));
+ it->SetKey(kIsGaiaFieldKey, base::Value(encrypted_is_gaia_value));
+ }
+ break;
+ }
+ if (entry_to_remove != update->GetList().end())
+ update->GetList().erase(entry_to_remove);
+ }
+
+ if (!prefs_->HasPrefPath(prefs::kSyncPasswordHash) ||
!prefs_->HasPrefPath(prefs::kSyncPasswordLengthAndHashSalt)) {
return;
}
+ // For users who are still use |kSyncPasswordHash| and
+ // |kSyncPasswordLengthAndHashSalt| to store password hashes, migrate them
+ // to |prefs::kPasswordHashDataList|.
base::Optional<SyncPasswordData> captured_sync_password_hash =
RetrievePasswordHash();
@@ -371,52 +379,6 @@ void HashPasswordManager::MaybeMigrateExistingSyncPasswordHash(
prefs_->ClearPref(prefs::kSyncPasswordLengthAndHashSalt);
}
-// static
-std::string HashPasswordManager::CreateRandomSalt() {
- char buffer[kSyncPasswordSaltLength];
- crypto::RandBytes(buffer, kSyncPasswordSaltLength);
- // Explicit std::string constructor with a string length must be used in order
- // to avoid treating '\0' symbols as a string ends.
- std::string result(buffer, kSyncPasswordSaltLength);
- return result;
-}
-
-// static
-uint64_t HashPasswordManager::CalculatePasswordHash(
- const base::StringPiece16& text,
- const std::string& salt) {
- crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
- constexpr size_t kBytesFromHash = 8;
- constexpr uint64_t kScryptCost = 32; // It must be power of 2.
- constexpr uint64_t kScryptBlockSize = 8;
- constexpr uint64_t kScryptParallelization = 1;
- constexpr size_t kScryptMaxMemory = 1024 * 1024;
-
- uint8_t hash[kBytesFromHash];
- base::StringPiece text_8bits(reinterpret_cast<const char*>(text.data()),
- text.size() * 2);
- const uint8_t* salt_ptr = reinterpret_cast<const uint8_t*>(salt.c_str());
-
- int scrypt_ok = EVP_PBE_scrypt(text_8bits.data(), text_8bits.size(), salt_ptr,
- salt.size(), kScryptCost, kScryptBlockSize,
- kScryptParallelization, kScryptMaxMemory, hash,
- kBytesFromHash);
-
- // EVP_PBE_scrypt can only fail due to memory allocation error (which aborts
- // Chromium) or invalid parameters. In case of a failure a hash could leak
- // information from the stack, so using CHECK is better than DCHECK.
- CHECK(scrypt_ok);
-
- // Take 37 bits of |hash|.
- uint64_t hash37 = ((static_cast<uint64_t>(hash[0]))) |
- ((static_cast<uint64_t>(hash[1])) << 8) |
- ((static_cast<uint64_t>(hash[2])) << 16) |
- ((static_cast<uint64_t>(hash[3])) << 24) |
- (((static_cast<uint64_t>(hash[4])) & 0x1F) << 32);
-
- return hash37;
-}
-
bool HashPasswordManager::EncryptAndSaveToPrefs(const std::string& pref_name,
const std::string& s) {
std::string encrypted_base64_text = EncryptString(s);
@@ -433,7 +395,8 @@ bool HashPasswordManager::EncryptAndSave(
return false;
}
- std::string encrypted_username = EncryptString(password_hash_data.username);
+ std::string encrypted_username = EncryptString(CanonicalizeUsername(
+ password_hash_data.username, password_hash_data.is_gaia_password));
if (encrypted_username.empty())
return false;
@@ -464,17 +427,22 @@ bool HashPasswordManager::EncryptAndSave(
encrypted_password_hash_entry.SetKey(
kLastSignInTimeFieldKey, base::Value(base::Time::Now().ToDoubleT()));
ListPrefUpdate update(prefs_, prefs::kPasswordHashDataList);
- for (auto it = update->GetList().begin(); it != update->GetList().end();
- it++) {
- if (GetAndDecryptField(*it, kUsernameFieldKey) ==
- password_hash_data.username &&
- GetAndDecryptField(*it, kIsGaiaFieldKey) ==
- BooleanToString(password_hash_data.is_gaia_password)) {
- update->GetList().erase(it);
- update->GetList().push_back(std::move(encrypted_password_hash_entry));
- return true;
+ bool replace_old_entry = false;
+ for (auto it = update->GetList().begin(); it != update->GetList().end();) {
+ if (AreUsernamesSame(GetAndDecryptField(*it, kUsernameFieldKey),
+ IsGaiaPassword(*it), password_hash_data.username,
+ password_hash_data.is_gaia_password)) {
+ it = update->GetList().erase(it);
+ replace_old_entry = true;
+ } else {
+ it++;
}
}
+ if (replace_old_entry) {
+ update->GetList().push_back(std::move(encrypted_password_hash_entry));
+ return true;
+ }
+
if (update->GetList().size() >= kMaxPasswordHashDataDictSize)
RemoveOldestSignInPasswordHashData(&update->GetList());
diff --git a/chromium/components/password_manager/core/browser/hash_password_manager.h b/chromium/components/password_manager/core/browser/hash_password_manager.h
index b2a32dc9190..735a56153bd 100644
--- a/chromium/components/password_manager/core/browser/hash_password_manager.h
+++ b/chromium/components/password_manager/core/browser/hash_password_manager.h
@@ -8,45 +8,12 @@
#include "base/macros.h"
#include "base/optional.h"
#include "base/strings/string16.h"
+#include "components/password_manager/core/browser/password_hash_data.h"
class PrefService;
namespace password_manager {
-// SyncPasswordData is being deprecated. Please use PasswordHashData instead.
-struct SyncPasswordData {
- SyncPasswordData() = default;
- SyncPasswordData(const SyncPasswordData& other) = default;
- SyncPasswordData(const base::string16& password, bool force_update);
- bool MatchesPassword(const base::string16& password);
-
- size_t length;
- std::string salt;
- uint64_t hash;
- // Signal that we need to update password hash, salt, and length in profile
- // prefs.
- bool force_update;
-};
-
-struct PasswordHashData {
- PasswordHashData();
- PasswordHashData(const PasswordHashData& other);
- PasswordHashData(const std::string& username,
- const base::string16& password,
- bool force_update,
- bool is_gaia_password = true);
- bool MatchesPassword(const std::string& username,
- const base::string16& password,
- bool is_gaia_password);
-
- std::string username;
- size_t length;
- std::string salt;
- uint64_t hash;
- bool force_update;
- bool is_gaia_password = true;
-};
-
// Responsible for saving, clearing, retrieving and encryption of a password
// hash data in preferences.
// All methods should be called on UI thread.
@@ -65,6 +32,9 @@ class HashPasswordManager {
void ClearSavedPasswordHash();
void ClearSavedPasswordHash(const std::string& username,
bool is_gaia_password);
+ // If |is_gaia_password| is true, clears all Gaia password hashes, otherwise
+ // clears all enterprise password hashes.
+ void ClearAllPasswordHash(bool is_gaia_password);
// Returns empty if no hash is available.
base::Optional<SyncPasswordData> RetrievePasswordHash();
@@ -90,13 +60,6 @@ class HashPasswordManager {
// that has already been captured.
void MaybeMigrateExistingSyncPasswordHash(const std::string& sync_username);
- static std::string CreateRandomSalt();
-
- // Calculates 37 bits hash for a password. The calculation is based on a slow
- // hash function. The running time is ~10^{-4} seconds on Desktop.
- static uint64_t CalculatePasswordHash(const base::StringPiece16& text,
- const std::string& salt);
-
private:
// Saves encrypted string |s| in a preference |pref_name|. Returns true on
// success.
diff --git a/chromium/components/password_manager/core/browser/hash_password_manager_unittest.cc b/chromium/components/password_manager/core/browser/hash_password_manager_unittest.cc
index 02ed3e064ff..ed5a748ca4a 100644
--- a/chromium/components/password_manager/core/browser/hash_password_manager_unittest.cc
+++ b/chromium/components/password_manager/core/browser/hash_password_manager_unittest.cc
@@ -6,8 +6,10 @@
#include "base/strings/utf_string_conversions.h"
#include "components/os_crypt/os_crypt_mocker.h"
+#include "components/password_manager/core/browser/password_hash_data.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/scoped_user_pref_update.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -73,7 +75,7 @@ TEST_F(HashPasswordManagerTest, SavingPasswordHashData) {
// Verify |SavePasswordHash(const std::string,const base::string16&)|
// behavior.
hash_password_manager.SavePasswordHash(username, password,
- /*force_update=*/true);
+ /*is_gaia_password=*/true);
EXPECT_TRUE(prefs_.HasPrefPath(prefs::kPasswordHashDataList));
// Saves the same password again won't change password hash, length or salt.
@@ -81,7 +83,7 @@ TEST_F(HashPasswordManagerTest, SavingPasswordHashData) {
hash_password_manager.RetrievePasswordHash(username,
/*is_gaia_password=*/true);
hash_password_manager.SavePasswordHash(username, password,
- /*force_update=*/true);
+ /*is_gaia_password=*/true);
base::Optional<PasswordHashData> existing_password_data =
hash_password_manager.RetrievePasswordHash(username,
/*is_gaia_password=*/true);
@@ -93,7 +95,7 @@ TEST_F(HashPasswordManagerTest, SavingPasswordHashData) {
// Verify |SavePasswordHash(const PasswordHashData&)| behavior.
base::string16 new_password(base::UTF8ToUTF16("new_password"));
PasswordHashData new_password_data(username, new_password,
- /*force_update=*/true);
+ /*is_gaia_password=*/true);
EXPECT_TRUE(hash_password_manager.SavePasswordHash(new_password_data));
EXPECT_NE(current_password_hash_data->hash,
hash_password_manager
@@ -101,6 +103,67 @@ TEST_F(HashPasswordManagerTest, SavingPasswordHashData) {
->hash);
}
+TEST_F(HashPasswordManagerTest, SavingPasswordHashDataNotCanonicalized) {
+ ASSERT_FALSE(prefs_.HasPrefPath(prefs::kPasswordHashDataList));
+ HashPasswordManager hash_password_manager;
+ hash_password_manager.set_prefs(&prefs_);
+ base::string16 password(base::UTF8ToUTF16("password"));
+ std::string canonical_username("user@gmail.com");
+ std::string username("US.ER@gmail.com");
+ std::string gmail_prefix("user");
+
+ // Verify |SavePasswordHash(const std::string,const base::string16&)|
+ // behavior.
+ hash_password_manager.SavePasswordHash(canonical_username, password,
+ /*is_gaia_password=*/true);
+ ASSERT_TRUE(prefs_.HasPrefPath(prefs::kPasswordHashDataList));
+ EXPECT_EQ(1u, prefs_.GetList(prefs::kPasswordHashDataList)->GetList().size());
+ EXPECT_EQ(
+ canonical_username,
+ hash_password_manager
+ .RetrievePasswordHash(canonical_username, /*is_gaia_password=*/true)
+ ->username);
+
+ // Saves the same password with not canonicalized username should not change
+ // password hash.
+ base::Optional<PasswordHashData> current_password_hash_data =
+ hash_password_manager.RetrievePasswordHash(username,
+ /*is_gaia_password=*/true);
+ hash_password_manager.SavePasswordHash(username, password,
+ /*is_gaia_password=*/true);
+ base::Optional<PasswordHashData> existing_password_data =
+ hash_password_manager.RetrievePasswordHash(username,
+ /*is_gaia_password=*/true);
+ EXPECT_EQ(current_password_hash_data->hash, existing_password_data->hash);
+ EXPECT_EQ(1u, prefs_.GetList(prefs::kPasswordHashDataList)->GetList().size());
+ EXPECT_EQ(canonical_username,
+ hash_password_manager
+ .RetrievePasswordHash(username, /*is_gaia_password=*/true)
+ ->username);
+ hash_password_manager.SavePasswordHash(gmail_prefix, password,
+ /*is_gaia_password=*/true);
+ EXPECT_EQ(current_password_hash_data->hash,
+ hash_password_manager
+ .RetrievePasswordHash(gmail_prefix,
+ /*is_gaia_password=*/true)
+ ->hash);
+ EXPECT_EQ(1u, prefs_.GetList(prefs::kPasswordHashDataList)->GetList().size());
+ EXPECT_EQ(canonical_username,
+ hash_password_manager
+ .RetrievePasswordHash(gmail_prefix, /*is_gaia_password=*/true)
+ ->username);
+
+ // Saves the password with gmail prefix only should be canonicalized into
+ // full gmail user name.
+ hash_password_manager.SavePasswordHash("user.name", password,
+ /*is_gaia_password=*/true);
+ EXPECT_EQ(2u, prefs_.GetList(prefs::kPasswordHashDataList)->GetList().size());
+ EXPECT_EQ("username@gmail.com",
+ hash_password_manager
+ .RetrievePasswordHash("user.name", /*is_gaia_password=*/true)
+ ->username);
+}
+
TEST_F(HashPasswordManagerTest, SavingGaiaPasswordAndNonGaiaPassword) {
ASSERT_FALSE(prefs_.HasPrefPath(prefs::kPasswordHashDataList));
HashPasswordManager hash_password_manager;
@@ -185,23 +248,34 @@ TEST_F(HashPasswordManagerTest, ClearingPasswordHashData) {
ASSERT_FALSE(prefs_.HasPrefPath(prefs::kPasswordHashDataList));
HashPasswordManager hash_password_manager;
hash_password_manager.set_prefs(&prefs_);
- hash_password_manager.SavePasswordHash("username",
+ hash_password_manager.SavePasswordHash("username1",
+ base::UTF8ToUTF16("sync_password"),
+ /*is_gaia_password=*/true);
+ hash_password_manager.SavePasswordHash("username2",
base::UTF8ToUTF16("sync_password"),
/*is_gaia_password=*/true);
+ hash_password_manager.SavePasswordHash(
+ "username3", base::UTF8ToUTF16("enterprise_password"),
+ /*is_gaia_password=*/false);
+ hash_password_manager.SavePasswordHash(
+ "username4", base::UTF8ToUTF16("enterprise_password"),
+ /*is_gaia_password=*/false);
hash_password_manager.ClearSavedPasswordHash("other_username",
/*is_gaia_password=*/true);
- EXPECT_TRUE(hash_password_manager.HasPasswordHash("username",
- /*is_gaia_password=*/true));
- hash_password_manager.ClearSavedPasswordHash("username",
+ EXPECT_EQ(4u, hash_password_manager.RetrieveAllPasswordHashes().size());
+ hash_password_manager.ClearSavedPasswordHash("username2",
/*is_gaia_password=*/false);
- EXPECT_TRUE(hash_password_manager.HasPasswordHash("username",
- /*is_gaia_password=*/true));
+ EXPECT_EQ(4u, hash_password_manager.RetrieveAllPasswordHashes().size());
- hash_password_manager.ClearSavedPasswordHash("username",
- /*is_gaia_password=*/true);
+ hash_password_manager.ClearSavedPasswordHash("username3",
+ /*is_gaia_password=*/false);
EXPECT_FALSE(hash_password_manager.HasPasswordHash(
- "username", /*is_gaia_password=*/true));
+ "username3", /*is_gaia_password=*/false));
+ EXPECT_EQ(3u, hash_password_manager.RetrieveAllPasswordHashes().size());
+ hash_password_manager.ClearAllPasswordHash(/*is_gaia_password=*/true);
+ EXPECT_EQ(1u, hash_password_manager.RetrieveAllPasswordHashes().size());
+ hash_password_manager.ClearAllPasswordHash(/*is_gaia_password=*/false);
EXPECT_EQ(0u, hash_password_manager.RetrieveAllPasswordHashes().size());
}
@@ -217,7 +291,7 @@ TEST_F(HashPasswordManagerTest, RetrievingSyncPasswordData) {
ASSERT_TRUE(sync_password_data);
EXPECT_EQ(13u, sync_password_data->length);
EXPECT_EQ(16u, sync_password_data->salt.size());
- uint64_t expected_hash = HashPasswordManager::CalculatePasswordHash(
+ uint64_t expected_hash = CalculatePasswordHash(
base::UTF8ToUTF16("sync_password"), sync_password_data->salt);
EXPECT_EQ(expected_hash, sync_password_data->hash);
}
@@ -226,52 +300,39 @@ TEST_F(HashPasswordManagerTest, RetrievingPasswordHashData) {
ASSERT_FALSE(prefs_.HasPrefPath(prefs::kPasswordHashDataList));
HashPasswordManager hash_password_manager;
hash_password_manager.set_prefs(&prefs_);
- hash_password_manager.SavePasswordHash("username",
+ hash_password_manager.SavePasswordHash("username@gmail.com",
base::UTF8ToUTF16("password"),
/*is_gaia_password=*/true);
EXPECT_EQ(1u, hash_password_manager.RetrieveAllPasswordHashes().size());
base::Optional<PasswordHashData> password_hash_data =
- hash_password_manager.RetrievePasswordHash("username",
+ hash_password_manager.RetrievePasswordHash("username@gmail.com",
/*is_gaia_password=*/false);
ASSERT_FALSE(password_hash_data);
password_hash_data = hash_password_manager.RetrievePasswordHash(
- "username", /*is_gaia_password=*/true);
+ "username@gmail.com", /*is_gaia_password=*/true);
ASSERT_TRUE(password_hash_data);
EXPECT_EQ(8u, password_hash_data->length);
EXPECT_EQ(16u, password_hash_data->salt.size());
- uint64_t expected_hash = HashPasswordManager::CalculatePasswordHash(
- base::UTF8ToUTF16("password"), password_hash_data->salt);
+ uint64_t expected_hash = CalculatePasswordHash(base::UTF8ToUTF16("password"),
+ password_hash_data->salt);
EXPECT_EQ(expected_hash, password_hash_data->hash);
+ // Retrieve not canonicalized version of "username@gmail.com" should return
+ // the same result.
+ EXPECT_TRUE(
+ hash_password_manager.RetrievePasswordHash("user.name@gmail.com",
+ /*is_gaia_password=*/true));
+ EXPECT_TRUE(
+ hash_password_manager.RetrievePasswordHash("USER.NAME@gmail.com",
+ /*is_gaia_password=*/true));
+
base::Optional<PasswordHashData> non_existing_data =
hash_password_manager.RetrievePasswordHash("non_existing_user", true);
ASSERT_FALSE(non_existing_data);
}
-TEST_F(HashPasswordManagerTest, CalculatePasswordHash) {
- const char* kPlainText[] = {"", "password", "password", "secret"};
- const char* kSalt[] = {"", "salt", "123", "456"};
-
- constexpr uint64_t kExpectedHash[] = {
- UINT64_C(0x1c610a7950), UINT64_C(0x1927dc525e), UINT64_C(0xf72f81aa6),
- UINT64_C(0x3645af77f),
- };
-
- static_assert(arraysize(kPlainText) == arraysize(kSalt),
- "Arrays must have the same size");
- static_assert(arraysize(kPlainText) == arraysize(kExpectedHash),
- "Arrays must have the same size");
-
- for (size_t i = 0; i < arraysize(kPlainText); ++i) {
- SCOPED_TRACE(i);
- base::string16 text = base::UTF8ToUTF16(kPlainText[i]);
- EXPECT_EQ(kExpectedHash[i],
- HashPasswordManager::CalculatePasswordHash(text, kSalt[i]));
- }
-}
-
-TEST_F(HashPasswordManagerTest, MigrateCapturedPasswordHash) {
+TEST_F(HashPasswordManagerTest, MigrateCapturedPasswordHashFromOldPref) {
ASSERT_FALSE(prefs_.HasPrefPath(prefs::kSyncPasswordHash));
HashPasswordManager hash_password_manager;
hash_password_manager.set_prefs(&prefs_);
@@ -288,5 +349,57 @@ TEST_F(HashPasswordManagerTest, MigrateCapturedPasswordHash) {
EXPECT_FALSE(prefs_.HasPrefPath(prefs::kSyncPasswordLengthAndHashSalt));
}
+TEST_F(HashPasswordManagerTest,
+ MigrateCapturedPasswordHashWithoutIsGaiaPasswordField) {
+ HashPasswordManager hash_password_manager;
+ hash_password_manager.set_prefs(&prefs_);
+ hash_password_manager.SavePasswordHash("sync_username",
+ base::UTF8ToUTF16("sync_password"),
+ /*is_gaia_password=*/true);
+ EXPECT_TRUE(hash_password_manager.HasPasswordHash("sync_username",
+ /*is_gaia_password=*/true));
+ EXPECT_FALSE(
+ hash_password_manager.HasPasswordHash("sync_username",
+ /*is_gaia_password=*/false));
+
+ // Remove the |is_gaia_password| value from the pref value.
+ ListPrefUpdate update(&prefs_, prefs::kPasswordHashDataList);
+ ASSERT_EQ(1u, update.Get()->GetList().size());
+ update.Get()->GetList()[0].RemoveKey("is_gaia");
+ EXPECT_FALSE(
+ hash_password_manager.HasPasswordHash("sync_username",
+ /*is_gaia_password=*/true));
+ EXPECT_FALSE(
+ hash_password_manager.HasPasswordHash("sync_username",
+ /*is_gaia_password=*/false));
+
+ // Trigger the migration code should set the |is_gaia_password| value to true.
+ hash_password_manager.MaybeMigrateExistingSyncPasswordHash("sync_username");
+
+ EXPECT_TRUE(hash_password_manager.HasPasswordHash("sync_username",
+ /*is_gaia_password=*/true));
+ EXPECT_FALSE(hash_password_manager.HasPasswordHash(
+ "sync_username", /*is_gaia_password=*/false));
+ EXPECT_EQ(1u, update->GetList().size());
+
+ // Now remove the |is_gaia_password| again, and save a new captured Gaia
+ // password hash with same username.
+ update.Get()->GetList()[0].RemoveKey("is_gaia");
+ hash_password_manager.SavePasswordHash("sync_username",
+ base::UTF8ToUTF16("sync_password"),
+ /*is_gaia_password=*/true);
+ // There should be 2 entries in the pref.
+ EXPECT_EQ(2u, update->GetList().size());
+
+ // Trigger the migration code should remove the duplicated entry without
+ // |is_gaia_password| field.
+ hash_password_manager.MaybeMigrateExistingSyncPasswordHash("sync_username");
+ EXPECT_EQ(1u, update->GetList().size());
+ EXPECT_TRUE(hash_password_manager.HasPasswordHash("sync_username",
+ /*is_gaia_password=*/true));
+ EXPECT_FALSE(hash_password_manager.HasPasswordHash(
+ "sync_username", /*is_gaia_password=*/false));
+}
+
} // namespace
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/login_database.cc b/chromium/components/password_manager/core/browser/login_database.cc
index 19b17d48c21..57d03b3575c 100644
--- a/chromium/components/password_manager/core/browser/login_database.cc
+++ b/chromium/components/password_manager/core/browser/login_database.cc
@@ -10,6 +10,7 @@
#include <limits>
#include <map>
#include <memory>
+#include <set>
#include <utility>
#include "base/bind.h"
@@ -816,6 +817,19 @@ void LoginDatabase::ReportMetrics(const std::string& sync_username,
for (const auto& password_to_realms : passwords_to_realms)
LogPasswordReuseMetrics(password_to_realms.second);
+
+ sql::Statement blacklist_statement(
+ db_.GetUniqueStatement("SELECT signon_realm "
+ "FROM logins WHERE blacklisted_by_user = 1"));
+ std::set<std::string> signon_realms;
+ size_t blacklisted_items = 0;
+ while (blacklist_statement.Step()) {
+ signon_realms.insert(blacklist_statement.ColumnString(0));
+ ++blacklisted_items;
+ }
+ size_t blacklisted_duplicates = blacklisted_items - signon_realms.size();
+ UMA_HISTOGRAM_COUNTS_1000("PasswordManager.BlacklistedDuplicates",
+ blacklisted_duplicates);
}
PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form) {
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 8c76d1a516d..e7e8c6568a1 100644
--- a/chromium/components/password_manager/core/browser/login_database_unittest.cc
+++ b/chromium/components/password_manager/core/browser/login_database_unittest.cc
@@ -16,7 +16,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/time.h"
#include "build/build_config.h"
@@ -1484,6 +1484,26 @@ TEST_F(LoginDatabaseTest, ReportMetricsTest) {
password_form.username_value = ASCIIToUTF16("JaneDoe");
EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
+ password_form.origin = GURL("http://rsolomakhin.github.io/autofill/");
+ password_form.signon_realm = "http://rsolomakhin.github.io/";
+ password_form.blacklisted_by_user = true;
+ EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
+
+ password_form.origin = GURL("https://rsolomakhin.github.io/autofill/");
+ password_form.signon_realm = "https://rsolomakhin.github.io/";
+ password_form.blacklisted_by_user = true;
+ EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
+
+ password_form.origin = GURL("http://rsolomakhin.github.io/autofill/123");
+ password_form.signon_realm = "http://rsolomakhin.github.io/";
+ password_form.blacklisted_by_user = true;
+ EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
+
+ password_form.origin = GURL("https://rsolomakhin.github.io/autofill/1234");
+ password_form.signon_realm = "https://rsolomakhin.github.io/";
+ password_form.blacklisted_by_user = true;
+ EXPECT_EQ(AddChangeForForm(password_form), db().AddLogin(password_form));
+
base::HistogramTester histogram_tester;
db().ReportMetrics("", false);
@@ -1544,6 +1564,8 @@ TEST_F(LoginDatabaseTest, ReportMetricsTest) {
1);
histogram_tester.ExpectUniqueSample("PasswordManager.InaccessiblePasswords",
0, 1);
+ histogram_tester.ExpectUniqueSample("PasswordManager.BlacklistedDuplicates",
+ 2, 1);
}
TEST_F(LoginDatabaseTest, PasswordReuseMetrics) {
diff --git a/chromium/components/password_manager/core/browser/mock_password_store.h b/chromium/components/password_manager/core/browser/mock_password_store.h
index 60e67beb3a9..941efc4a183 100644
--- a/chromium/components/password_manager/core/browser/mock_password_store.h
+++ b/chromium/components/password_manager/core/browser/mock_password_store.h
@@ -76,10 +76,12 @@ class MockPasswordStore : public PasswordStore {
void(const base::string16&,
const std::string&,
PasswordReuseDetectorConsumer*));
- MOCK_METHOD3(SaveSyncPasswordHash,
+ MOCK_METHOD3(SaveGaiaPasswordHash,
void(const std::string&,
const base::string16&,
metrics_util::SyncPasswordHashChange));
+ MOCK_METHOD2(SaveEnterprisePasswordHash,
+ void(const std::string&, const base::string16&));
MOCK_METHOD1(ClearPasswordHash, void(const std::string&));
#endif
diff --git a/chromium/components/password_manager/core/browser/new_password_form_manager.cc b/chromium/components/password_manager/core/browser/new_password_form_manager.cc
index 150b7ab410b..36703f51cf9 100644
--- a/chromium/components/password_manager/core/browser/new_password_form_manager.cc
+++ b/chromium/components/password_manager/core/browser/new_password_form_manager.cc
@@ -4,9 +4,11 @@
#include "components/password_manager/core/browser/new_password_form_manager.h"
+#include "base/metrics/histogram_macros.h"
+#include "components/autofill/core/browser/form_structure.h"
#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
#include "components/password_manager/core/browser/form_fetcher_impl.h"
-#include "components/password_manager/core/browser/form_parsing/ios_form_parser.h"
+#include "components/password_manager/core/browser/form_parsing/form_parser.h"
#include "components/password_manager/core/browser/password_form_filling.h"
#include "components/password_manager/core/browser/password_form_metrics_recorder.h"
#include "components/password_manager/core/browser/password_manager_client.h"
@@ -14,6 +16,12 @@
#include "components/password_manager/core/browser/password_manager_util.h"
using autofill::FormData;
+using autofill::FormFieldData;
+using autofill::FormSignature;
+using autofill::FormStructure;
+using autofill::PasswordForm;
+using base::TimeDelta;
+using base::TimeTicks;
using Logger = autofill::SavePasswordProgressLogger;
@@ -21,28 +29,18 @@ namespace password_manager {
namespace {
-// On iOS, id are used for field identification, whereas on other platforms
-// name field is used. Set id equal to name.
-// TODO(https://crbug.com/831123): Remove this method when the browser side form
-// parsing is ready.
-FormData PreprocessFormData(const FormData& form) {
- FormData result_form = form;
- for (auto& field : result_form.fields)
- field.id = field.name;
- return result_form;
-}
+constexpr TimeDelta kMaxFillingDelayForServerPerdictions =
+ TimeDelta::FromMilliseconds(500);
// Helper function for calling form parsing and logging results if logging is
// active.
-std::unique_ptr<autofill::PasswordForm> ParseFormAndMakeLogging(
+std::unique_ptr<PasswordForm> ParseFormAndMakeLogging(
PasswordManagerClient* client,
- const FormData& form) {
- // iOS form parsing is a prototype for parsing on all platforms. Call it for
- // developing and experimentation purposes.
- // TODO(https://crbug.com/831123): Call general form parsing instead of iOS
- // one when it is ready.
- std::unique_ptr<autofill::PasswordForm> password_form =
- ParseFormData(PreprocessFormData(form), FormParsingMode::FILLING);
+ const FormData& form,
+ const base::Optional<FormPredictions>& predictions) {
+ std::unique_ptr<PasswordForm> password_form =
+ ParseFormData(form, predictions ? &predictions.value() : nullptr,
+ FormParsingMode::FILLING);
if (password_manager_util::IsLoggingActive(client)) {
BrowserSavePasswordProgressLogger logger(client->GetLogManager());
@@ -71,7 +69,8 @@ NewPasswordFormManager::NewPasswordFormManager(
client_,
true /* should_migrate_http_passwords */,
true /* should_query_suppressed_https_forms */)),
- form_fetcher_(form_fetcher ? form_fetcher : owned_form_fetcher_.get()) {
+ form_fetcher_(form_fetcher ? form_fetcher : owned_form_fetcher_.get()),
+ weak_ptr_factory_(this) {
metrics_recorder_ = base::MakeRefCounted<PasswordFormMetricsRecorder>(
client_->IsMainFrameSecure(), client_->GetUkmSourceId());
metrics_recorder_->RecordFormSignature(CalculateFormSignature(observed_form));
@@ -84,7 +83,7 @@ NewPasswordFormManager::NewPasswordFormManager(
// TODO(https://crbug.com/831123): remove it when NewPasswordFormManager will
// be production ready.
if (password_manager_util::IsLoggingActive(client_))
- ParseFormAndMakeLogging(client_, observed_form_);
+ ParseFormAndMakeLogging(client_, observed_form_, predictions_);
}
NewPasswordFormManager::~NewPasswordFormManager() = default;
@@ -109,55 +108,51 @@ const GURL& NewPasswordFormManager::GetOrigin() const {
return observed_form_.origin;
}
-const std::map<base::string16, const autofill::PasswordForm*>&
+const std::map<base::string16, const PasswordForm*>&
NewPasswordFormManager::GetBestMatches() const {
- // TODO(https://crbug.com/831123): Implement.
- DCHECK(false);
- static std::map<base::string16, const autofill::PasswordForm*> dummy_map;
- return dummy_map;
+ return best_matches_;
}
-const autofill::PasswordForm& NewPasswordFormManager::GetPendingCredentials()
- const {
+
+const PasswordForm& NewPasswordFormManager::GetPendingCredentials() const {
// TODO(https://crbug.com/831123): Implement.
DCHECK(false);
- static autofill::PasswordForm dummy_form;
+ static PasswordForm dummy_form;
return dummy_form;
}
+
metrics_util::CredentialSourceType
NewPasswordFormManager::GetCredentialSource() {
// TODO(https://crbug.com/831123): Implement.
return metrics_util::CredentialSourceType::kPasswordManager;
}
+
PasswordFormMetricsRecorder* NewPasswordFormManager::GetMetricsRecorder() {
return metrics_recorder_.get();
}
-const std::vector<const autofill::PasswordForm*>&
+
+const std::vector<const PasswordForm*>&
NewPasswordFormManager::GetBlacklistedMatches() const {
- // TODO(https://crbug.com/831123): Implement.
- DCHECK(false);
- static std::vector<const autofill::PasswordForm*> dummy_vector;
- return dummy_vector;
+ return blacklisted_matches_;
}
+
bool NewPasswordFormManager::IsBlacklisted() const {
- // TODO(https://crbug.com/831123): Implement.
- return false;
+ return !blacklisted_matches_.empty();
}
+
bool NewPasswordFormManager::IsPasswordOverridden() const {
// TODO(https://crbug.com/831123): Implement.
return false;
}
-const autofill::PasswordForm* NewPasswordFormManager::GetPreferredMatch()
- const {
- // TODO(https://crbug.com/831123): Implement or remove from
- // PasswordFormManagerForUI.
- return nullptr;
+
+const PasswordForm* NewPasswordFormManager::GetPreferredMatch() const {
+ return preferred_match_;
}
// TODO(https://crbug.com/831123): Implement all methods from
// PasswordFormManagerForUI.
void NewPasswordFormManager::Save() {}
-void NewPasswordFormManager::Update(
- const autofill::PasswordForm& credentials_to_update) {}
+void NewPasswordFormManager::Update(const PasswordForm& credentials_to_update) {
+}
void NewPasswordFormManager::UpdateUsername(
const base::string16& new_username) {}
void NewPasswordFormManager::UpdatePasswordValue(
@@ -169,46 +164,138 @@ void NewPasswordFormManager::PermanentlyBlacklist() {}
void NewPasswordFormManager::OnPasswordsRevealed() {}
void NewPasswordFormManager::ProcessMatches(
- const std::vector<const autofill::PasswordForm*>& non_federated,
+ const std::vector<const PasswordForm*>& non_federated,
size_t filtered_count) {
- if (!driver_)
+ received_stored_credentials_time_ = TimeTicks::Now();
+ std::vector<const PasswordForm*> matches;
+ std::copy_if(non_federated.begin(), non_federated.end(),
+ std::back_inserter(matches), [](const PasswordForm* form) {
+ return !form->blacklisted_by_user &&
+ form->scheme == PasswordForm::SCHEME_HTML;
+ });
+
+ std::vector<const PasswordForm*> not_best_matches;
+ password_manager_util::FindBestMatches(matches, &best_matches_,
+ &not_best_matches, &preferred_match_);
+
+ // Copy out blacklisted matches.
+ blacklisted_matches_.clear();
+ std::copy_if(
+ non_federated.begin(), non_federated.end(),
+ std::back_inserter(blacklisted_matches_), [](const PasswordForm* form) {
+ return form->blacklisted_by_user && !form->is_public_suffix_match;
+ });
+
+ autofills_left_ = kMaxTimesAutofill;
+
+ if (predictions_) {
+ ReportTimeBetweenStoreAndServerUMA();
+ Fill();
+ } else {
+ base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&NewPasswordFormManager::Fill,
+ weak_ptr_factory_.GetWeakPtr()),
+ kMaxFillingDelayForServerPerdictions);
+ }
+}
+
+bool NewPasswordFormManager::SetSubmittedFormIfIsManaged(
+ const autofill::FormData& submitted_form,
+ const PasswordManagerDriver* driver) {
+ if (!DoesManage(submitted_form, driver))
+ return false;
+ submitted_form_ = submitted_form;
+ is_submitted_ = true;
+ return true;
+}
+
+void NewPasswordFormManager::ProcessServerPredictions(
+ const std::vector<FormStructure*>& predictions) {
+ FormSignature observed_form_signature =
+ CalculateFormSignature(observed_form_);
+ for (const FormStructure* form_predictions : predictions) {
+ if (form_predictions->form_signature() != observed_form_signature)
+ continue;
+ ReportTimeBetweenStoreAndServerUMA();
+ predictions_ = ConvertToFormPredictions(*form_predictions);
+ Fill();
+ break;
+ }
+}
+
+void NewPasswordFormManager::Fill() {
+ if (autofills_left_ <= 0)
return;
+ autofills_left_--;
// There are additional signals (server-side data) and parse results in
// filling and saving mode might be different so it is better not to cache
// parse result, but to parse each time again.
- std::unique_ptr<autofill::PasswordForm> observed_password_form =
- ParseFormAndMakeLogging(client_, observed_form_);
+ std::unique_ptr<PasswordForm> observed_password_form =
+ ParseFormAndMakeLogging(client_, observed_form_, predictions_);
if (!observed_password_form)
return;
- // TODO(https://crbug.com/831123). Implement correct treating of blacklisted
- // matches.
- std::map<base::string16, const autofill::PasswordForm*> best_matches;
- std::vector<const autofill::PasswordForm*> not_best_matches;
- const autofill::PasswordForm* preferred_match = nullptr;
- password_manager_util::FindBestMatches(non_federated, &best_matches,
- &not_best_matches, &preferred_match);
- if (best_matches.empty())
+ RecordMetricOnCompareParsingResult(*observed_password_form);
+
+ // TODO(https://crbug.com/831123). Move this lines to the beginning of the
+ // function when the old parsing is removed.
+ if (!driver_)
return;
// TODO(https://crbug.com/831123). Implement correct treating of federated
// matches.
- std::vector<const autofill::PasswordForm*> federated_matches;
- SendFillInformationToRenderer(
- *client_, driver_.get(), false /* is_blaclisted */,
- *observed_password_form.get(), best_matches, federated_matches,
- preferred_match, metrics_recorder_.get());
+ std::vector<const PasswordForm*> federated_matches;
+ SendFillInformationToRenderer(*client_, driver_.get(), IsBlacklisted(),
+ *observed_password_form.get(), best_matches_,
+ federated_matches, preferred_match_,
+ metrics_recorder_.get());
}
-bool NewPasswordFormManager::SetSubmittedFormIfIsManaged(
- const autofill::FormData& submitted_form,
- const PasswordManagerDriver* driver) {
- if (!DoesManage(submitted_form, driver))
- return false;
- submitted_form_ = submitted_form;
- is_submitted_ = true;
- return true;
+void NewPasswordFormManager::RecordMetricOnCompareParsingResult(
+ const PasswordForm& parsed_form) {
+ bool same =
+ parsed_form.username_element == old_parsing_result_.username_element &&
+ parsed_form.password_element == old_parsing_result_.password_element &&
+ parsed_form.new_password_element ==
+ old_parsing_result_.new_password_element &&
+ parsed_form.confirmation_password_element ==
+ old_parsing_result_.confirmation_password_element;
+ if (same) {
+ metrics_recorder_->RecordParsingsComparisonResult(
+ PasswordFormMetricsRecorder::ParsingComparisonResult::kSame);
+ return;
+ }
+
+ // In the old parsing for fields with empty name, placeholders are used. The
+ // reason for this is that an empty "..._element" attribute in a PasswordForm
+ // means that no corresponding input element exists. The new form parsing sets
+ // empty string in that case because renderer ids are used instead of element
+ // names for fields identification. Hence in case of anonymous fields, the
+ // results will be different for sure. Compare to placeholders and record this
+ // case.
+ if (old_parsing_result_.username_element ==
+ base::ASCIIToUTF16("anonymous_username") ||
+ old_parsing_result_.password_element ==
+ base::ASCIIToUTF16("anonymous_password") ||
+ old_parsing_result_.new_password_element ==
+ base::ASCIIToUTF16("anonymous_new_password") ||
+ old_parsing_result_.confirmation_password_element ==
+ base::ASCIIToUTF16("anonymous_confirmation_password")) {
+ metrics_recorder_->RecordParsingsComparisonResult(
+ PasswordFormMetricsRecorder::ParsingComparisonResult::kAnonymousFields);
+ } else {
+ metrics_recorder_->RecordParsingsComparisonResult(
+ PasswordFormMetricsRecorder::ParsingComparisonResult::kDifferent);
+ }
+}
+
+void NewPasswordFormManager::ReportTimeBetweenStoreAndServerUMA() {
+ if (!received_stored_credentials_time_.is_null()) {
+ UMA_HISTOGRAM_TIMES("PasswordManager.TimeBetweenStoreAndServer",
+ TimeTicks::Now() - received_stored_credentials_time_);
+ }
}
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/new_password_form_manager.h b/chromium/components/password_manager/core/browser/new_password_form_manager.h
index 1b935cc28ad..0459cc5e119 100644
--- a/chromium/components/password_manager/core/browser/new_password_form_manager.h
+++ b/chromium/components/password_manager/core/browser/new_password_form_manager.h
@@ -5,13 +5,24 @@
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_NEW_PASSWORD_FORM_MANAGER_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_NEW_PASSWORD_FORM_MANAGER_H_
+#include <map>
+#include <vector>
+
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
#include "components/autofill/core/common/form_data.h"
#include "components/password_manager/core/browser/form_fetcher.h"
+#include "components/password_manager/core/browser/form_parsing/password_field_prediction.h"
#include "components/password_manager/core/browser/password_form_manager_for_ui.h"
+namespace autofill {
+class FormStructure;
+}
+
namespace password_manager {
class PasswordFormMetricsRecorder;
@@ -36,6 +47,10 @@ class NewPasswordFormManager : public PasswordFormManagerForUI,
~NewPasswordFormManager() override;
+ // The upper limit on how many times Chrome will try to autofill the same
+ // form.
+ static constexpr int kMaxTimesAutofill = 5;
+
// Compares |observed_form_| with |form| and returns true if they are the
// same and if |driver| is the same as |driver_|.
bool DoesManage(const autofill::FormData& form,
@@ -50,6 +65,19 @@ class NewPasswordFormManager : public PasswordFormManagerForUI,
bool is_submitted() { return is_submitted_; }
void set_not_submitted() { is_submitted_ = false; }
+ void set_old_parsing_result(const autofill::PasswordForm& form) {
+ old_parsing_result_ = form;
+ }
+
+ // Selects from |predictions| predictions that corresponds to
+ // |observed_form_|, initiates filling and stores predictions in
+ // |predictions_|.
+ void ProcessServerPredictions(
+ const std::vector<autofill::FormStructure*>& predictions);
+
+ // Sends fill data to the renderer.
+ void Fill();
+
// PasswordFormManagerForUI:
FormFetcher* GetFormFetcher() override;
const GURL& GetOrigin() const override;
@@ -82,6 +110,16 @@ class NewPasswordFormManager : public PasswordFormManagerForUI,
size_t filtered_count) override;
private:
+ // Compares |parsed_form| with |old_parsing_result_| and records UKM metric.
+ // TODO(https://crbug.com/831123): Remove it when the old form parsing is
+ // removed.
+ void RecordMetricOnCompareParsingResult(
+ const autofill::PasswordForm& parsed_form);
+
+ // Report the time between receiving credentials from the password store and
+ // the autofill server responding to the lookup request.
+ void ReportTimeBetweenStoreAndServerUMA();
+
// The client which implements embedder-specific PasswordManager operations.
PasswordManagerClient* client_;
@@ -89,6 +127,21 @@ class NewPasswordFormManager : public PasswordFormManagerForUI,
const autofill::FormData observed_form_;
+ // Set of nonblacklisted PasswordForms from the DB that best match the form
+ // being managed by |this|, indexed by username. The PasswordForms are owned
+ // by |form_fetcher_|.
+ std::map<base::string16, const autofill::PasswordForm*> best_matches_;
+
+ // Set of blacklisted forms from the PasswordStore that best match the current
+ // form. They are owned by |form_fetcher_|.
+ std::vector<const autofill::PasswordForm*> blacklisted_matches_;
+
+ // Convenience pointer to entry in best_matches_ that is marked
+ // as preferred. This is only allowed to be null if there are no best matches
+ // at all, since there will always be one preferred login when there are
+ // multiple matches (when first saved, a login is marked preferred).
+ const autofill::PasswordForm* preferred_match_;
+
// Takes care of recording metrics and events for |*this|.
scoped_refptr<PasswordFormMetricsRecorder> metrics_recorder_;
@@ -103,6 +156,24 @@ class NewPasswordFormManager : public PasswordFormManagerForUI,
bool is_submitted_ = false;
autofill::FormData submitted_form_;
+ base::Optional<FormPredictions> predictions_;
+
+ // If Chrome has already autofilled a few times, it is probable that autofill
+ // is triggered by programmatic changes in the page. We set a maximum number
+ // of times that Chrome will autofill to avoid being stuck in an infinite
+ // loop.
+ int autofills_left_ = kMaxTimesAutofill;
+
+ // Used for comparison metrics.
+ // TODO(https://crbug.com/831123): Remove it when the old form parsing is
+ // removed.
+ autofill::PasswordForm old_parsing_result_;
+
+ // Time when stored credentials are received from the store. Used for metrics.
+ base::TimeTicks received_stored_credentials_time_;
+
+ base::WeakPtrFactory<NewPasswordFormManager> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(NewPasswordFormManager);
};
diff --git a/chromium/components/password_manager/core/browser/new_password_form_manager_unittest.cc b/chromium/components/password_manager/core/browser/new_password_form_manager_unittest.cc
index c87d06be9cf..65b6bced8e6 100644
--- a/chromium/components/password_manager/core/browser/new_password_form_manager_unittest.cc
+++ b/chromium/components/password_manager/core/browser/new_password_form_manager_unittest.cc
@@ -5,7 +5,10 @@
#include "components/password_manager/core/browser/new_password_form_manager.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/password_form.h"
#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/password_manager/core/browser/fake_form_fetcher.h"
@@ -15,11 +18,14 @@
#include "testing/gtest/include/gtest/gtest.h"
using autofill::FormData;
+using autofill::FormStructure;
using autofill::FormFieldData;
using autofill::PasswordForm;
using autofill::PasswordFormFillData;
using base::ASCIIToUTF16;
+using base::TestMockTimeTaskRunner;
using testing::_;
+using testing::Mock;
using testing::SaveArg;
namespace password_manager {
@@ -39,7 +45,7 @@ class MockPasswordManagerDriver : public StubPasswordManagerDriver {
class NewPasswordFormManagerTest : public testing::Test {
public:
- NewPasswordFormManagerTest() {
+ NewPasswordFormManagerTest() : task_runner_(new TestMockTimeTaskRunner) {
GURL origin = GURL("http://accounts.google.com/a/ServiceLoginAuth");
GURL action = GURL("http://accounts.google.com/a/ServiceLogin");
@@ -52,14 +58,19 @@ class NewPasswordFormManagerTest : public testing::Test {
FormFieldData field;
field.name = ASCIIToUTF16("firstname");
field.form_control_type = "text";
+ field.unique_renderer_id = 1;
observed_form_.fields.push_back(field);
field.name = ASCIIToUTF16("username");
+ field.id = field.name;
field.form_control_type = "text";
+ field.unique_renderer_id = 2;
observed_form_.fields.push_back(field);
field.name = ASCIIToUTF16("password");
+ field.id = field.name;
field.form_control_type = "password";
+ field.unique_renderer_id = 3;
observed_form_.fields.push_back(field);
saved_match_.origin = origin;
@@ -67,13 +78,20 @@ class NewPasswordFormManagerTest : public testing::Test {
saved_match_.preferred = true;
saved_match_.username_value = ASCIIToUTF16("test@gmail.com");
saved_match_.password_value = ASCIIToUTF16("test1");
+ saved_match_.is_public_suffix_match = false;
+ saved_match_.scheme = PasswordForm::SCHEME_HTML;
+
+ blacklisted_match_ = saved_match_;
+ blacklisted_match_.blacklisted_by_user = true;
}
protected:
FormData observed_form_;
PasswordForm saved_match_;
+ PasswordForm blacklisted_match_;
StubPasswordManagerClient client_;
MockPasswordManagerDriver driver_;
+ scoped_refptr<TestMockTimeTaskRunner> task_runner_;
};
TEST_F(NewPasswordFormManagerTest, DoesManage) {
@@ -108,6 +126,7 @@ TEST_F(NewPasswordFormManagerTest, DoesManageNoFormTag) {
}
TEST_F(NewPasswordFormManagerTest, Autofill) {
+ TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
FakeFormFetcher fetcher;
fetcher.Fetch();
EXPECT_CALL(driver_, AllowPasswordGenerationForForm(_));
@@ -117,6 +136,8 @@ TEST_F(NewPasswordFormManagerTest, Autofill) {
observed_form_, &fetcher);
fetcher.SetNonFederated({&saved_match_}, 0u);
+ task_runner_->FastForwardUntilNoTasksRemain();
+
EXPECT_EQ(observed_form_.origin, fill_data.origin);
EXPECT_FALSE(fill_data.wait_for_username);
EXPECT_EQ(observed_form_.fields[1].name, fill_data.username_field.name);
@@ -125,6 +146,72 @@ TEST_F(NewPasswordFormManagerTest, Autofill) {
EXPECT_EQ(saved_match_.password_value, fill_data.password_field.value);
}
+TEST_F(NewPasswordFormManagerTest, AutofillNotMoreThan5Times) {
+ TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
+ PasswordFormFillData fill_data;
+ EXPECT_CALL(driver_, FillPasswordForm(_));
+ NewPasswordFormManager form_manager(&client_, driver_.AsWeakPtr(),
+ observed_form_, &fetcher);
+ fetcher.SetNonFederated({&saved_match_}, 0u);
+
+ task_runner_->FastForwardUntilNoTasksRemain();
+ Mock::VerifyAndClearExpectations(&driver_);
+
+ for (size_t i = 0; i < NewPasswordFormManager::kMaxTimesAutofill - 1; ++i) {
+ EXPECT_CALL(driver_, FillPasswordForm(_));
+ form_manager.Fill();
+ Mock::VerifyAndClearExpectations(&driver_);
+ }
+
+ EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0);
+ form_manager.Fill();
+}
+
+// NewPasswordFormManager should always send fill data to renderer, even for
+// sign-up forms (no "current-password" field, i.e., no password field to fill
+// into). However, for sign-up forms, no particular password field should be
+// identified for filling. That way, Chrome won't disturb the user by filling
+// the sign-up form, but will be able to offer a manual fallback for filling if
+// the form was misclassified.
+TEST_F(NewPasswordFormManagerTest, AutofillSignUpForm) {
+ TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
+ // Make |observed_form_| to be sign-up form.
+ observed_form_.fields.back().autocomplete_attribute = "new-password";
+
+ PasswordFormFillData fill_data;
+ EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data));
+ NewPasswordFormManager form_manager(&client_, driver_.AsWeakPtr(),
+ observed_form_, &fetcher);
+ fetcher.SetNonFederated({&saved_match_}, 0u);
+
+ task_runner_->FastForwardUntilNoTasksRemain();
+ constexpr uint32_t kNoID = FormFieldData::kNotSetFormControlRendererId;
+ EXPECT_EQ(kNoID, fill_data.password_field.unique_renderer_id);
+ EXPECT_EQ(saved_match_.password_value, fill_data.password_field.value);
+}
+
+TEST_F(NewPasswordFormManagerTest, AutofillWithBlacklistedMatch) {
+ TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
+ PasswordFormFillData fill_data;
+ EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data));
+ NewPasswordFormManager form_manager(&client_, driver_.AsWeakPtr(),
+ observed_form_, &fetcher);
+
+ fetcher.SetNonFederated({&saved_match_, &blacklisted_match_}, 0u);
+
+ task_runner_->FastForwardUntilNoTasksRemain();
+
+ EXPECT_EQ(observed_form_.origin, fill_data.origin);
+ EXPECT_EQ(saved_match_.username_value, fill_data.username_field.value);
+ EXPECT_EQ(saved_match_.password_value, fill_data.password_field.value);
+}
+
TEST_F(NewPasswordFormManagerTest, SetSubmitted) {
FakeFormFetcher fetcher;
fetcher.Fetch();
@@ -157,4 +244,75 @@ TEST_F(NewPasswordFormManagerTest, SetSubmitted) {
EXPECT_FALSE(form_manager.is_submitted());
}
+// Tests that when NewPasswordFormManager receives saved matches it waits for
+// server predictions and fills on receving them.
+TEST_F(NewPasswordFormManagerTest, ServerPredictionsWithinDelay) {
+ TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
+
+ // Expects no filling on save matches receiving.
+ EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0);
+ NewPasswordFormManager form_manager(&client_, driver_.AsWeakPtr(),
+ observed_form_, &fetcher);
+ fetcher.SetNonFederated({&saved_match_}, 0u);
+ Mock::VerifyAndClearExpectations(&driver_);
+
+ FormStructure form_structure(observed_form_);
+ form_structure.field(2)->set_server_type(autofill::PASSWORD);
+ std::vector<FormStructure*> predictions{&form_structure};
+
+ // Expect filling without delay on receiving server predictions.
+ EXPECT_CALL(driver_, FillPasswordForm(_)).Times(1);
+ form_manager.ProcessServerPredictions(predictions);
+}
+
+// Tests that NewPasswordFormManager fills after some delay even without
+// server predictions.
+TEST_F(NewPasswordFormManagerTest, ServerPredictionsAfterDelay) {
+ TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
+
+ NewPasswordFormManager form_manager(&client_, driver_.AsWeakPtr(),
+ observed_form_, &fetcher);
+ fetcher.SetNonFederated({&saved_match_}, 0u);
+ // Expect filling after passing filling delay.
+ EXPECT_CALL(driver_, FillPasswordForm(_)).Times(1);
+ // Simulate passing filling delay.
+ task_runner_->FastForwardUntilNoTasksRemain();
+ Mock::VerifyAndClearExpectations(&driver_);
+
+ FormStructure form_structure(observed_form_);
+ form_structure.field(2)->set_server_type(autofill::PASSWORD);
+ std::vector<FormStructure*> predictions{&form_structure};
+
+ // Expect filling on receiving server predictions because it was less than
+ // kMaxTimesAutofill attempts to fill.
+ EXPECT_CALL(driver_, FillPasswordForm(_)).Times(1);
+ form_manager.ProcessServerPredictions(predictions);
+ task_runner_->FastForwardUntilNoTasksRemain();
+}
+
+// Tests that filling happens immediately if server predictions are received
+// before saved matches.
+TEST_F(NewPasswordFormManagerTest, ServerPredictionsBeforeFetcher) {
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
+ // Expect no filling after receiving saved matches from |fetcher|, since
+ // |form_manager| is waiting for server-side predictions.
+ EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0);
+ NewPasswordFormManager form_manager(&client_, driver_.AsWeakPtr(),
+ observed_form_, &fetcher);
+ FormStructure form_structure(observed_form_);
+ form_structure.field(2)->set_server_type(autofill::PASSWORD);
+ std::vector<FormStructure*> predictions{&form_structure};
+ form_manager.ProcessServerPredictions(predictions);
+ Mock::VerifyAndClearExpectations(&driver_);
+
+ // Expect filling without delay on receiving server predictions.
+ EXPECT_CALL(driver_, FillPasswordForm(_)).Times(1);
+ fetcher.SetNonFederated({&saved_match_}, 0u);
+}
+
} // namespace password_manager
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 3911c7018ce..534137b6d3d 100644
--- a/chromium/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_autofill_manager.cc
@@ -26,6 +26,7 @@
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_data_validation.h"
#include "components/autofill/core/common/autofill_util.h"
+#include "components/favicon/core/favicon_util.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
#include "components/password_manager/core/browser/password_manager_client.h"
#include "components/password_manager/core/browser/password_manager_driver.h"
@@ -45,10 +46,14 @@ namespace password_manager {
namespace {
+constexpr base::char16 kPasswordReplacementChar = 0x2022;
+
// Returns |username| unless it is empty. For an empty |username| returns a
// localised string saying this username is empty. Use this for displaying the
-// usernames to the user.
-base::string16 ReplaceEmptyUsername(const base::string16& username) {
+// usernames to the user. |replaced| is set to true iff |username| is empty.
+base::string16 ReplaceEmptyUsername(const base::string16& username,
+ bool* replaced) {
+ *replaced = username.empty();
if (username.empty())
return l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_EMPTY_LOGIN);
return username;
@@ -79,50 +84,63 @@ base::string16 GetUsernameFromSuggestion(const base::string16& suggestion) {
void AppendSuggestionIfMatching(
const base::string16& field_suggestion,
const base::string16& field_contents,
+ const gfx::Image& custom_icon,
const std::string& signon_realm,
bool show_all,
bool is_password_field,
+ size_t password_length,
std::vector<autofill::Suggestion>* suggestions) {
base::string16 lower_suggestion = base::i18n::ToLower(field_suggestion);
base::string16 lower_contents = base::i18n::ToLower(field_contents);
- bool prefix_matched_suggestion =
- show_all || base::StartsWith(lower_suggestion, lower_contents,
- base::CompareCase::SENSITIVE);
- if (prefix_matched_suggestion ||
- autofill::FieldIsSuggestionSubstringStartingOnTokenBoundary(
- lower_suggestion, lower_contents, true)) {
- autofill::Suggestion suggestion(ReplaceEmptyUsername(field_suggestion));
- suggestion.label = GetHumanReadableRealm(signon_realm);
+ if (show_all || autofill::FieldIsSuggestionSubstringStartingOnTokenBoundary(
+ lower_suggestion, lower_contents, true)) {
+ bool replaced_username;
+ autofill::Suggestion suggestion(
+ ReplaceEmptyUsername(field_suggestion, &replaced_username));
+ suggestion.is_value_secondary = replaced_username;
+ suggestion.label =
+ signon_realm.empty()
+ ? base::string16(password_length, kPasswordReplacementChar)
+ : GetHumanReadableRealm(signon_realm);
suggestion.frontend_id = is_password_field
? autofill::POPUP_ITEM_ID_PASSWORD_ENTRY
: autofill::POPUP_ITEM_ID_USERNAME_ENTRY;
- suggestion.match = prefix_matched_suggestion
- ? autofill::Suggestion::PREFIX_MATCH
- : autofill::Suggestion::SUBSTRING_MATCH;
+ suggestion.match =
+ show_all || base::StartsWith(lower_suggestion, lower_contents,
+ base::CompareCase::SENSITIVE)
+ ? autofill::Suggestion::PREFIX_MATCH
+ : autofill::Suggestion::SUBSTRING_MATCH;
+ suggestion.custom_icon = custom_icon;
+ // The UI code will pick up an icon from the resources based on the string.
+ suggestion.icon = base::ASCIIToUTF16("globeIcon");
suggestions->push_back(suggestion);
}
}
-// This function attempts to fill |suggestions| and |realms| form |fill_data|
-// based on |current_username|. Unless |show_all| is true, it only picks
-// suggestions where the username has |current_username| as a prefix.
+// This function attempts to fill |suggestions| from |fill_data| based on
+// |current_username| that is the current value of the field. Unless |show_all|
+// is true, it only picks suggestions allowed by
+// FieldIsSuggestionSubstringStartingOnTokenBoundary. It can pick either a
+// substring or a prefix based on the flag.
void GetSuggestions(const autofill::PasswordFormFillData& fill_data,
const base::string16& current_username,
- std::vector<autofill::Suggestion>* suggestions,
+ const gfx::Image& custom_icon,
bool show_all,
- bool is_password_field) {
- AppendSuggestionIfMatching(fill_data.username_field.value, current_username,
- fill_data.preferred_realm, show_all,
- is_password_field, suggestions);
+ bool is_password_field,
+ std::vector<autofill::Suggestion>* suggestions) {
+ AppendSuggestionIfMatching(
+ fill_data.username_field.value, current_username, custom_icon,
+ fill_data.preferred_realm, show_all, is_password_field,
+ fill_data.password_field.value.size(), suggestions);
for (const auto& login : fill_data.additional_logins) {
- AppendSuggestionIfMatching(login.first, current_username,
+ AppendSuggestionIfMatching(login.first, current_username, custom_icon,
login.second.realm, show_all, is_password_field,
- suggestions);
+ login.second.password.size(), suggestions);
}
// Prefix matches should precede other token matches.
- if (autofill::IsFeatureSubstringMatchEnabled()) {
+ if (!show_all && autofill::IsFeatureSubstringMatchEnabled()) {
std::sort(suggestions->begin(), suggestions->end(),
[](const autofill::Suggestion& a, const autofill::Suggestion& b) {
return a.match < b.match;
@@ -141,20 +159,6 @@ bool ShouldShowManualFallbackForPreLollipop(syncer::SyncService* sync_service) {
#endif
}
-void AddSimpleSuggestionWithSeparatorOnTop(
- int value,
- int frontend_id,
- std::vector<autofill::Suggestion>* suggestions) {
-#if !defined(OS_ANDROID)
- suggestions->push_back(autofill::Suggestion());
- suggestions->back().frontend_id = autofill::POPUP_ITEM_ID_SEPARATOR;
-#endif
-
- autofill::Suggestion suggestion(l10n_util::GetStringUTF8(value),
- std::string(), std::string(), frontend_id);
- suggestions->push_back(suggestion);
-}
-
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -214,6 +218,7 @@ void PasswordAutofillManager::OnAddPasswordFormMapping(
return;
login_to_password_info_[key] = fill_data;
+ RequestFavicon(fill_data.origin);
}
void PasswordAutofillManager::OnShowPasswordSuggestions(
@@ -230,9 +235,9 @@ void PasswordAutofillManager::OnShowPasswordSuggestions(
NOTREACHED();
return;
}
- GetSuggestions(fill_data_it->second, typed_username, &suggestions,
+ GetSuggestions(fill_data_it->second, typed_username, page_favicon_,
(options & autofill::SHOW_ALL) != 0,
- (options & autofill::IS_PASSWORD_FIELD) != 0);
+ (options & autofill::IS_PASSWORD_FIELD) != 0, &suggestions);
form_data_key_ = key;
@@ -241,86 +246,79 @@ void PasswordAutofillManager::OnShowPasswordSuggestions(
return;
}
- if (options & autofill::IS_PASSWORD_FIELD) {
- autofill::Suggestion password_field_suggestions(l10n_util::GetStringUTF16(
- IDS_AUTOFILL_PASSWORD_FIELD_SUGGESTIONS_TITLE));
- password_field_suggestions.frontend_id = autofill::POPUP_ITEM_ID_TITLE;
- suggestions.insert(suggestions.begin(), password_field_suggestions);
- }
-
- GURL origin = (fill_data_it->second).origin;
+ GURL origin = fill_data_it->second.origin;
if (ShouldShowManualFallbackForPreLollipop(
autofill_client_->GetSyncService())) {
- if (base::FeatureList::IsEnabled(
- password_manager::features::kManualFallbacksFilling) &&
- (options & autofill::IS_PASSWORD_FIELD) && password_client_ &&
+ if (password_client_ &&
password_client_->IsFillingFallbackEnabledForCurrentPage()) {
- AddSimpleSuggestionWithSeparatorOnTop(
- IDS_AUTOFILL_SHOW_ALL_SAVED_FALLBACK,
- autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY, &suggestions);
+ autofill::Suggestion suggestion(
+ l10n_util::GetStringUTF8(IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS),
+ std::string(), std::string(),
+ autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY);
+ suggestions.push_back(suggestion);
- show_all_saved_passwords_shown_context_ =
- metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD;
metrics_util::LogContextOfShowAllSavedPasswordsShown(
- show_all_saved_passwords_shown_context_);
- }
- if (base::FeatureList::IsEnabled(
- password_manager::features::kEnableManualFallbacksGeneration) &&
- password_manager_util::GetPasswordSyncState(
- autofill_client_->GetSyncService()) == SYNCING_NORMAL_ENCRYPTION) {
- AddSimpleSuggestionWithSeparatorOnTop(
- IDS_AUTOFILL_GENERATE_PASSWORD_FALLBACK,
- autofill::POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY, &suggestions);
+ metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD);
}
}
- autofill_client_->ShowAutofillPopup(bounds,
- text_direction,
- suggestions,
- weak_ptr_factory_.GetWeakPtr());
+ autofill_client_->ShowAutofillPopup(bounds, text_direction, suggestions,
+ false, weak_ptr_factory_.GetWeakPtr());
+}
+
+bool PasswordAutofillManager::MaybeShowPasswordSuggestions(
+ const gfx::RectF& bounds,
+ base::i18n::TextDirection text_direction) {
+ if (login_to_password_info_.empty())
+ return false;
+ OnShowPasswordSuggestions(
+ login_to_password_info_.begin()->first, text_direction, base::string16(),
+ autofill::SHOW_ALL | autofill::IS_PASSWORD_FIELD, bounds);
+ return true;
}
-void PasswordAutofillManager::OnShowManualFallbackSuggestion(
- base::i18n::TextDirection text_direction,
- const gfx::RectF& bounds) {
- // https://crbug.com/699197
- // CroS SimpleWebviewDialog used for the captive portal dialog is a special
- // case because it doesn't instantiate many helper classes. |autofill_client_|
- // is NULL too.
- if (!autofill_client_ || !ShouldShowManualFallbackForPreLollipop(
- autofill_client_->GetSyncService()))
- return;
- if (!password_client_ ||
- !password_client_->IsFillingFallbackEnabledForCurrentPage())
- return;
+bool PasswordAutofillManager::MaybeShowPasswordSuggestionsWithGeneration(
+ const gfx::RectF& bounds,
+ base::i18n::TextDirection text_direction) {
+ if (login_to_password_info_.empty())
+ return false;
std::vector<autofill::Suggestion> suggestions;
- autofill::Suggestion all_saved_passwords(
- l10n_util::GetStringUTF8(IDS_AUTOFILL_SHOW_ALL_SAVED_FALLBACK),
- std::string(), std::string(),
- autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY);
- suggestions.push_back(all_saved_passwords);
-
- show_all_saved_passwords_shown_context_ =
- metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_MANUAL_FALLBACK;
- metrics_util::LogContextOfShowAllSavedPasswordsShown(
- show_all_saved_passwords_shown_context_);
-
- if (base::FeatureList::IsEnabled(
- password_manager::features::kEnableManualFallbacksGeneration) &&
- password_manager_util::GetPasswordSyncState(
- autofill_client_->GetSyncService()) == SYNCING_NORMAL_ENCRYPTION) {
- AddSimpleSuggestionWithSeparatorOnTop(
- IDS_AUTOFILL_GENERATE_PASSWORD_FALLBACK,
- autofill::POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY, &suggestions);
+ GetSuggestions(login_to_password_info_.begin()->second, base::string16(),
+ page_favicon_, true /* show_all */,
+ true /* is_password_field */, &suggestions);
+ form_data_key_ = login_to_password_info_.begin()->first;
+
+ // Add 'Generation' option.
+ // The UI code will pick up an icon from the resources based on the string.
+ autofill::Suggestion suggestion(
+ l10n_util::GetStringUTF8(IDS_PASSWORD_MANAGER_GENERATE_PASSWORD),
+ std::string(), std::string("keyIcon"),
+ autofill::POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY);
+ suggestions.push_back(suggestion);
+
+ // Add "Manage passwords".
+ if (ShouldShowManualFallbackForPreLollipop(
+ autofill_client_->GetSyncService())) {
+ autofill::Suggestion suggestion(
+ l10n_util::GetStringUTF8(IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS),
+ std::string(), std::string(),
+ autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY);
+ suggestions.push_back(suggestion);
+
+ metrics_util::LogContextOfShowAllSavedPasswordsShown(
+ metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD);
}
+
autofill_client_->ShowAutofillPopup(bounds, text_direction, suggestions,
- weak_ptr_factory_.GetWeakPtr());
+ false, weak_ptr_factory_.GetWeakPtr());
+ return true;
}
void PasswordAutofillManager::DidNavigateMainFrame() {
login_to_password_info_.clear();
- did_show_form_not_secure_warning_ = false;
+ favicon_tracker_.TryCancelAll();
+ page_favicon_ = gfx::Image();
}
bool PasswordAutofillManager::FillSuggestionForTest(
@@ -357,38 +355,22 @@ void PasswordAutofillManager::DidAcceptSuggestion(const base::string16& value,
int position) {
autofill_client_->ExecuteCommand(identifier);
if (identifier == autofill::POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY) {
- password_manager_driver_->UserSelectedManualGenerationOption();
- } else if (identifier != autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY) {
- bool success =
- FillSuggestion(form_data_key_, GetUsernameFromSuggestion(value));
- DCHECK(success);
- }
-
- if (identifier == autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY) {
- DCHECK_NE(show_all_saved_passwords_shown_context_,
- metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_NONE);
-
+ password_client_->GeneratePassword();
+ } else if (identifier == autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY) {
metrics_util::LogContextOfShowAllSavedPasswordsAccepted(
- show_all_saved_passwords_shown_context_);
+ metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD);
if (password_client_) {
using UserAction =
password_manager::PasswordManagerMetricsRecorder::PageLevelUserAction;
- switch (show_all_saved_passwords_shown_context_) {
- case metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD:
- password_client_->GetMetricsRecorder().RecordPageLevelUserAction(
- UserAction::kShowAllPasswordsWhileSomeAreSuggested);
- break;
- case metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_MANUAL_FALLBACK:
- password_client_->GetMetricsRecorder().RecordPageLevelUserAction(
- UserAction::kShowAllPasswordsWhileNoneAreSuggested);
- break;
- case metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_CONTEXT_MENU:
- case metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_NONE:
- case metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_COUNT:
- NOTREACHED();
+
+ password_client_->GetMetricsRecorder().RecordPageLevelUserAction(
+ UserAction::kShowAllPasswordsWhileSomeAreSuggested);
}
- }
+ } else {
+ bool success =
+ FillSuggestion(form_data_key_, GetUsernameFromSuggestion(value));
+ DCHECK(success);
}
autofill_client_->HideAutofillPopup();
@@ -468,4 +450,21 @@ bool PasswordAutofillManager::FindLoginInfo(
return true;
}
+void PasswordAutofillManager::RequestFavicon(const GURL& url) {
+ if (!password_client_)
+ return;
+ favicon::GetFaviconImageForPageURL(
+ password_client_->GetFaviconService(), url,
+ favicon_base::IconType::kFavicon,
+ base::BindRepeating(&PasswordAutofillManager::OnFaviconReady,
+ weak_ptr_factory_.GetWeakPtr()),
+ &favicon_tracker_);
+}
+
+void PasswordAutofillManager::OnFaviconReady(
+ const favicon_base::FaviconImageResult& result) {
+ if (!result.image.IsEmpty())
+ page_favicon_ = result.image;
+}
+
} // namespace password_manager
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 e830ca726c6..b0d5c99ab72 100644
--- a/chromium/components/password_manager/core/browser/password_autofill_manager.h
+++ b/chromium/components/password_manager/core/browser/password_autofill_manager.h
@@ -10,10 +10,16 @@
#include "base/callback.h"
#include "base/i18n/rtl.h"
#include "base/macros.h"
+#include "base/task/cancelable_task_tracker.h"
#include "components/autofill/core/browser/autofill_client.h"
#include "components/autofill/core/browser/autofill_popup_delegate.h"
#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "ui/gfx/image/image.h"
+
+namespace favicon_base {
+struct FaviconImageResult;
+}
namespace gfx {
class RectF;
@@ -55,19 +61,29 @@ class PasswordAutofillManager : public autofill::AutofillPopupDelegate {
int key,
const autofill::PasswordFormFillData& fill_data);
- // Handles a request from the renderer to show a popup with the given
- // |suggestions| from the password manager. |options| should be a bitwise mask
- // of autofill::ShowPasswordSuggestionsOptions values.
+ // Handles a request from the renderer to show a popup with the suggestions
+ // from the password manager. |options| should be a bitwise mask of
+ // autofill::ShowPasswordSuggestionsOptions values.
void OnShowPasswordSuggestions(int key,
base::i18n::TextDirection text_direction,
const base::string16& typed_username,
int options,
const gfx::RectF& bounds);
- // Handles a request from the renderer to show a popup with an option to check
- // user's saved passwords, used when a password field is not autofilled.
- void OnShowManualFallbackSuggestion(base::i18n::TextDirection text_direction,
- const gfx::RectF& bounds);
+ // If there are relevant credentials for the current frame show them and
+ // return true. Otherwise, return false.
+ // This is currently used for cases in which the automatic generation
+ // option is offered through a different UI surface than the popup
+ // (e.g. via the keyboard accessory on Android).
+ bool MaybeShowPasswordSuggestions(const gfx::RectF& bounds,
+ base::i18n::TextDirection text_direction);
+
+ // If there are relevant credentials for the current frame, shows them with
+ // an additional 'generation' option and returns true. Otherwise, does nothing
+ // and returns false.
+ bool MaybeShowPasswordSuggestionsWithGeneration(
+ const gfx::RectF& bounds,
+ base::i18n::TextDirection text_direction);
// Called when main frame navigates. Not called for in-page navigations.
void DidNavigateMainFrame();
@@ -108,13 +124,20 @@ class PasswordAutofillManager : public autofill::AutofillPopupDelegate {
// Finds login information for a |node| that was previously filled.
bool FindLoginInfo(int key, autofill::PasswordFormFillData* found_password);
- // Creates suggestion and records the metrics for the "Form not secure
- // warning".
- autofill::Suggestion CreateFormNotSecureWarning();
+ // Makes a request to the favicon service for the icon of |url|.
+ void RequestFavicon(const GURL& url);
+
+ // Called when the favicon was retrieved. When the icon is not ready or
+ // unavailable a fallback globe icon is used. The request to the favicon
+ // store is canceled on navigation.
+ void OnFaviconReady(const favicon_base::FaviconImageResult& result);
// The logins we have filled so far with their associated info.
LoginToPasswordInfoMap login_to_password_info_;
+ // Contains the favicon for the credentials offered on the current page.
+ gfx::Image page_favicon_;
+
// When the autofill popup should be shown, |form_data_key_| identifies the
// right password info in |login_to_password_info_|.
int form_data_key_;
@@ -122,15 +145,6 @@ class PasswordAutofillManager : public autofill::AutofillPopupDelegate {
// The driver that owns |this|.
PasswordManagerDriver* password_manager_driver_;
- // True if the Form-Not-Secure warning has been shown on the current
- // navigation. Used for metrics.
- bool did_show_form_not_secure_warning_ = false;
-
- // Context in which the "Show all saved passwords" fallback was shown.
- metrics_util::ShowAllSavedPasswordsContext
- show_all_saved_passwords_shown_context_ =
- metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_NONE;
-
autofill::AutofillClient* autofill_client_; // weak
PasswordManagerClient* password_client_;
@@ -138,6 +152,9 @@ class PasswordAutofillManager : public autofill::AutofillPopupDelegate {
// If not null then it will be called in destructor.
base::OnceClosure deletion_callback_;
+ // Used to track a requested favicon.
+ base::CancelableTaskTracker favicon_tracker_;
+
base::WeakPtrFactory<PasswordAutofillManager> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(PasswordAutofillManager);
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 90bdfefc7fc..f5a3ba67027 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
@@ -11,9 +11,8 @@
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/user_action_tester.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/metrics/user_action_tester.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/popup_item_ids.h"
#include "components/autofill/core/browser/suggestion_test_helpers.h"
@@ -23,6 +22,7 @@
#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/password_form_fill_data.h"
+#include "components/favicon/core/test/mock_favicon_service.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
@@ -37,6 +37,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/image/image_unittest_util.h"
#if defined(OS_ANDROID)
#include "base/android/build_info.h"
@@ -51,10 +52,13 @@ const char kAliceUsername[] = "alice";
const char kAlicePassword[] = "password";
using autofill::Suggestion;
+using autofill::SuggestionVectorIconsAre;
using autofill::SuggestionVectorIdsAre;
using autofill::SuggestionVectorValuesAre;
using autofill::SuggestionVectorLabelsAre;
using testing::_;
+using testing::ElementsAreArray;
+using testing::Return;
using UkmEntry = ukm::builders::PageWithPassword;
@@ -85,6 +89,9 @@ class TestPasswordManagerClient : public StubPasswordManagerClient {
MockPasswordManagerDriver* mock_driver() { return &driver_; }
const GURL& GetMainFrameURL() const override { return main_frame_url_; }
+ MOCK_METHOD0(GeneratePassword, void());
+ MOCK_METHOD0(GetFaviconService, favicon::FaviconService*());
+
private:
MockPasswordManagerDriver driver_;
GURL main_frame_url_;
@@ -107,10 +114,11 @@ class MockAutofillClient : public autofill::TestAutofillClient {
: sync_service_(sync_service) {
LOG(ERROR) << "init mpck client";
}
- MOCK_METHOD4(ShowAutofillPopup,
+ MOCK_METHOD5(ShowAutofillPopup,
void(const gfx::RectF& element_bounds,
base::i18n::TextDirection text_direction,
const std::vector<Suggestion>& suggestions,
+ bool autoselect_first_suggestion,
base::WeakPtr<autofill::AutofillPopupDelegate> delegate));
MOCK_METHOD0(HideAutofillPopup, void());
MOCK_METHOD1(ExecuteCommand, void(int));
@@ -130,6 +138,24 @@ bool IsPreLollipopAndroid() {
#endif
}
+std::vector<base::string16> GetSuggestionList(
+ std::vector<base::string16> credentials) {
+ if (!IsPreLollipopAndroid()) {
+ credentials.push_back(
+ l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS));
+ }
+ return credentials;
+}
+
+std::vector<base::string16> GetIconsList(std::vector<std::string> icons) {
+ std::vector<base::string16> ret(icons.size());
+ std::transform(icons.begin(), icons.end(), ret.begin(), &base::ASCIIToUTF16);
+ // On older Android versions the item "Manage passwords" is absent.
+ if (!IsPreLollipopAndroid())
+ ret.push_back(base::string16());
+ return ret;
+}
+
} // namespace
class PasswordAutofillManagerTest : public testing::Test {
@@ -157,113 +183,22 @@ class PasswordAutofillManagerTest : public testing::Test {
autofill::AutofillClient* autofill_client) {
password_autofill_manager_.reset(new PasswordAutofillManager(
client->mock_driver(), autofill_client, client));
+ favicon::MockFaviconService favicon_service;
+ EXPECT_CALL(*client, GetFaviconService())
+ .WillOnce(Return(&favicon_service));
+ EXPECT_CALL(favicon_service,
+ GetFaviconImageForPageURL(fill_data_.origin, _, _));
password_autofill_manager_->OnAddPasswordFormMapping(fill_data_id_,
fill_data_);
+ testing::Mock::VerifyAndClearExpectations(client);
+ // Suppress the warnings in the tests.
+ EXPECT_CALL(*client, GetFaviconService()).WillRepeatedly(Return(nullptr));
}
protected:
int fill_data_id() { return fill_data_id_; }
autofill::PasswordFormFillData& fill_data() { return fill_data_; }
- void SetManualFallbacksForFilling(bool enabled) {
- if (enabled) {
- scoped_feature_list_.InitAndEnableFeature(
- password_manager::features::kManualFallbacksFilling);
- } else {
- scoped_feature_list_.InitAndDisableFeature(
- password_manager::features::kManualFallbacksFilling);
- }
- }
-
- void SetManualFallbacksForFillingStandalone(bool enabled) {
- if (enabled) {
- scoped_feature_list_.InitAndEnableFeature(
- password_manager::features::kEnableManualFallbacksFillingStandalone);
- } else {
- scoped_feature_list_.InitAndDisableFeature(
- password_manager::features::kEnableManualFallbacksFillingStandalone);
- }
- }
-
- static bool IsManualFallbackForFillingEnabled() {
- return base::FeatureList::IsEnabled(
- password_manager::features::kManualFallbacksFilling) &&
- !IsPreLollipopAndroid();
- }
-
- void SetManualFallbacks(bool enabled) {
- std::vector<std::string> features = {
- password_manager::features::kManualFallbacksFilling.name,
- password_manager::features::kEnableManualFallbacksFillingStandalone
- .name,
- password_manager::features::kEnableManualFallbacksGeneration.name};
- if (enabled) {
- scoped_feature_list_.InitFromCommandLine(base::JoinString(features, ","),
- std::string());
- } else {
- scoped_feature_list_.InitFromCommandLine(std::string(),
- base::JoinString(features, ","));
- }
- }
-
- void TestGenerationFallback(bool custom_passphrase_enabled) {
- MockSyncService mock_sync_service;
- EXPECT_CALL(mock_sync_service, IsFirstSetupComplete())
- .WillRepeatedly(::testing::Return(true));
- EXPECT_CALL(mock_sync_service, IsSyncActive())
- .WillRepeatedly(::testing::Return(true));
- EXPECT_CALL(mock_sync_service, GetActiveDataTypes())
- .Times(::testing::AnyNumber())
- .WillRepeatedly(
- ::testing::Return(syncer::ModelTypeSet(syncer::PASSWORDS)));
- EXPECT_CALL(mock_sync_service, IsUsingSecondaryPassphrase())
- .WillRepeatedly(::testing::Return(custom_passphrase_enabled));
- std::unique_ptr<TestPasswordManagerClient> client(
- new TestPasswordManagerClient);
- std::unique_ptr<MockAutofillClient> autofill_client(
- new MockAutofillClient(&mock_sync_service));
- InitializePasswordAutofillManager(client.get(), autofill_client.get());
-
- gfx::RectF element_bounds;
- autofill::PasswordFormFillData data;
- data.username_field.value = test_username_;
- data.password_field.value = test_password_;
- data.origin = GURL("https://foo.test");
-
- int dummy_key = 0;
- password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
- SetManualFallbacks(true);
-
- std::vector<base::string16> elements = {
- l10n_util::GetStringUTF16(
- IDS_AUTOFILL_PASSWORD_FIELD_SUGGESTIONS_TITLE),
- test_username_};
- if (!IsPreLollipopAndroid() || !custom_passphrase_enabled) {
-#if !defined(OS_ANDROID)
- elements.push_back(base::string16());
-#endif
- elements.push_back(
- l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ALL_SAVED_FALLBACK));
- if (!custom_passphrase_enabled) {
-#if !defined(OS_ANDROID)
- elements.push_back(base::string16());
-#endif
- elements.push_back(
- l10n_util::GetStringUTF16(IDS_AUTOFILL_GENERATE_PASSWORD_FALLBACK));
- }
- }
-
- EXPECT_CALL(
- *autofill_client,
- ShowAutofillPopup(
- element_bounds, _,
- SuggestionVectorValuesAre(testing::ElementsAreArray(elements)), _));
-
- password_autofill_manager_->OnShowPasswordSuggestions(
- dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_,
- autofill::IS_PASSWORD_FIELD, element_bounds);
- }
-
std::unique_ptr<PasswordAutofillManager> password_autofill_manager_;
base::string16 test_username_;
@@ -272,7 +207,6 @@ class PasswordAutofillManagerTest : public testing::Test {
private:
autofill::PasswordFormFillData fill_data_;
const int fill_data_id_;
- base::test::ScopedFeatureList scoped_feature_list_;
// The TestAutofillDriver uses a SequencedWorkerPool which expects the
// existence of a MessageLoop.
@@ -329,10 +263,12 @@ TEST_F(PasswordAutofillManagerTest, PreviewSuggestion) {
fill_data_id(), test_username_));
}
-// Test that the popup is marked as visible after recieving password
+// Test that the popup is marked as visible after receiving password
// suggestions.
TEST_F(PasswordAutofillManagerTest, ExternalDelegatePasswordSuggestions) {
for (bool is_suggestion_on_password_field : {false, true}) {
+ SCOPED_TRACE(testing::Message() << "is_suggestion_on_password_field = "
+ << is_suggestion_on_password_field);
std::unique_ptr<TestPasswordManagerClient> client(
new TestPasswordManagerClient);
std::unique_ptr<MockAutofillClient> autofill_client(new MockAutofillClient);
@@ -344,34 +280,45 @@ TEST_F(PasswordAutofillManagerTest, ExternalDelegatePasswordSuggestions) {
data.password_field.value = test_password_;
data.preferred_realm = "http://foo.com/";
int dummy_key = 0;
+ favicon::MockFaviconService favicon_service;
+ EXPECT_CALL(*client, GetFaviconService())
+ .WillOnce(Return(&favicon_service));
+ favicon_base::FaviconImageCallback callback;
+ EXPECT_CALL(favicon_service, GetFaviconImageForPageURL(data.origin, _, _))
+ .WillOnce(DoAll(testing::SaveArg<1>(&callback), Return(1)));
password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
- EXPECT_CALL(*client->mock_driver(),
- FillSuggestion(test_username_, test_password_));
+ // Resolve the favicon.
+ favicon_base::FaviconImageResult image_result;
+ image_result.image = gfx::test::CreateImage(16, 16);
+ callback.Run(image_result);
std::vector<autofill::PopupItemId> ids = {
- autofill::POPUP_ITEM_ID_USERNAME_ENTRY};
- if (is_suggestion_on_password_field) {
- ids = {autofill::POPUP_ITEM_ID_TITLE,
- autofill::POPUP_ITEM_ID_PASSWORD_ENTRY};
- if (IsManualFallbackForFillingEnabled()) {
-#if !defined(OS_ANDROID)
- ids.push_back(autofill::POPUP_ITEM_ID_SEPARATOR);
-#endif
- ids.push_back(autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY);
- }
+ is_suggestion_on_password_field
+ ? autofill::POPUP_ITEM_ID_PASSWORD_ENTRY
+ : autofill::POPUP_ITEM_ID_USERNAME_ENTRY};
+ if (!IsPreLollipopAndroid()) {
+ ids.push_back(autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY);
}
+ std::vector<Suggestion> suggestions;
EXPECT_CALL(
*autofill_client,
ShowAutofillPopup(
- _, _, SuggestionVectorIdsAre(testing::ElementsAreArray(ids)), _));
+ _, _, SuggestionVectorIdsAre(testing::ElementsAreArray(ids)), false,
+ _))
+ .WillOnce(testing::SaveArg<2>(&suggestions));
int show_suggestion_options =
is_suggestion_on_password_field ? autofill::IS_PASSWORD_FIELD : 0;
password_autofill_manager_->OnShowPasswordSuggestions(
dummy_key, base::i18n::RIGHT_TO_LEFT, base::string16(),
show_suggestion_options, element_bounds);
+ ASSERT_GE(suggestions.size(), 1u);
+ EXPECT_TRUE(gfx::test::AreImagesEqual(suggestions[0].custom_icon,
+ image_result.image));
+ EXPECT_CALL(*client->mock_driver(),
+ FillSuggestion(test_username_, test_password_));
// Accepting a suggestion should trigger a call to hide the popup.
EXPECT_CALL(*autofill_client, HideAutofillPopup());
password_autofill_manager_->DidAcceptSuggestion(
@@ -407,41 +354,43 @@ TEST_F(PasswordAutofillManagerTest, ExtractSuggestions) {
// First, simulate displaying suggestions matching an empty prefix. Also
// verify that both the values and labels are filled correctly. The 'value'
// should be the user name; the 'label' should be the realm.
- EXPECT_CALL(*autofill_client,
- ShowAutofillPopup(
- element_bounds, _,
- testing::AllOf(
- SuggestionVectorValuesAre(testing::UnorderedElementsAre(
- test_username_, additional_username)),
- SuggestionVectorLabelsAre(testing::UnorderedElementsAre(
- base::UTF8ToUTF16(data.preferred_realm),
- base::UTF8ToUTF16(additional.realm)))),
- _));
+ EXPECT_CALL(
+ *autofill_client,
+ ShowAutofillPopup(
+ element_bounds, _,
+ testing::AllOf(
+ SuggestionVectorValuesAre(testing::UnorderedElementsAreArray(
+ GetSuggestionList({test_username_, additional_username}))),
+ SuggestionVectorLabelsAre(testing::AllOf(
+ testing::Contains(base::UTF8ToUTF16(data.preferred_realm)),
+ testing::Contains(base::UTF8ToUTF16(additional.realm))))),
+ false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
- dummy_key, base::i18n::RIGHT_TO_LEFT, base::string16(), false,
+ dummy_key, base::i18n::RIGHT_TO_LEFT, base::string16(), 0,
element_bounds);
// Now simulate displaying suggestions matching "John".
EXPECT_CALL(
*autofill_client,
ShowAutofillPopup(element_bounds, _,
- SuggestionVectorValuesAre(
- testing::UnorderedElementsAre(additional_username)),
- _));
+ SuggestionVectorValuesAre(testing::ElementsAreArray(
+ GetSuggestionList({additional_username}))),
+ false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
- dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("John"), false,
+ dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("John"), 0,
element_bounds);
// Finally, simulate displaying all suggestions, without any prefix matching.
EXPECT_CALL(
*autofill_client,
- ShowAutofillPopup(element_bounds, _,
- SuggestionVectorValuesAre(testing::UnorderedElementsAre(
- test_username_, additional_username)),
- _));
+ ShowAutofillPopup(
+ element_bounds, _,
+ SuggestionVectorValuesAre(testing::ElementsAreArray(
+ GetSuggestionList({test_username_, additional_username}))),
+ false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
- dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("xyz"), true,
- element_bounds);
+ dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("xyz"),
+ autofill::SHOW_ALL, element_bounds);
}
// Verify that, for Android application credentials, the prettified realms of
@@ -466,15 +415,15 @@ TEST_F(PasswordAutofillManagerTest, PrettifiedAndroidRealmsAreShownAsLabels) {
password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
EXPECT_CALL(*autofill_client,
- ShowAutofillPopup(
- _, _,
- SuggestionVectorLabelsAre(testing::UnorderedElementsAre(
- base::ASCIIToUTF16("android://com.example1.android/"),
- base::ASCIIToUTF16("android://com.example2.android/"))),
- _));
+ ShowAutofillPopup(_, _,
+ SuggestionVectorLabelsAre(testing::AllOf(
+ testing::Contains(base::ASCIIToUTF16(
+ "android://com.example1.android/")),
+ testing::Contains(base::ASCIIToUTF16(
+ "android://com.example2.android/")))),
+ false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
- dummy_key, base::i18n::RIGHT_TO_LEFT, base::string16(), false,
- gfx::RectF());
+ dummy_key, base::i18n::RIGHT_TO_LEFT, base::string16(), 0, gfx::RectF());
}
TEST_F(PasswordAutofillManagerTest, FillSuggestionPasswordField) {
@@ -500,27 +449,12 @@ TEST_F(PasswordAutofillManagerTest, FillSuggestionPasswordField) {
int dummy_key = 0;
password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
- // Simulate displaying suggestions matching a username and specifying that the
- // field is a password field.
- base::string16 title = l10n_util::GetStringUTF16(
- IDS_AUTOFILL_PASSWORD_FIELD_SUGGESTIONS_TITLE);
- std::vector<base::string16> elements = {title, test_username_};
- if (IsManualFallbackForFillingEnabled()) {
- elements = {
- title,
- test_username_,
-#if !defined(OS_ANDROID)
- base::string16(),
-#endif
- l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ALL_SAVED_FALLBACK)
- };
- }
-
EXPECT_CALL(
*autofill_client,
- ShowAutofillPopup(
- element_bounds, _,
- SuggestionVectorValuesAre(testing::ElementsAreArray(elements)), _));
+ ShowAutofillPopup(element_bounds, _,
+ SuggestionVectorValuesAre(testing::ElementsAreArray(
+ GetSuggestionList({test_username_}))),
+ false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_,
autofill::IS_PASSWORD_FIELD, element_bounds);
@@ -556,14 +490,14 @@ TEST_F(PasswordAutofillManagerTest, DisplaySuggestionsWithMatchingTokens) {
int dummy_key = 0;
password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
- EXPECT_CALL(
- *autofill_client,
- ShowAutofillPopup(element_bounds, _,
- SuggestionVectorValuesAre(testing::UnorderedElementsAre(
- username, additional_username)),
- _));
+ EXPECT_CALL(*autofill_client,
+ ShowAutofillPopup(
+ element_bounds, _,
+ SuggestionVectorValuesAre(testing::UnorderedElementsAreArray(
+ GetSuggestionList({username, additional_username}))),
+ false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
- dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("foo"), false,
+ dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("foo"), 0,
element_bounds);
}
@@ -597,10 +531,10 @@ TEST_F(PasswordAutofillManagerTest, NoSuggestionForNonPrefixTokenMatch) {
int dummy_key = 0;
password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
- EXPECT_CALL(*autofill_client, ShowAutofillPopup(_, _, _, _)).Times(0);
+ EXPECT_CALL(*autofill_client, ShowAutofillPopup(_, _, _, _, _)).Times(0);
password_autofill_manager_->OnShowPasswordSuggestions(
- dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("oo"), false,
+ dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("oo"), 0,
element_bounds);
}
@@ -639,12 +573,12 @@ TEST_F(PasswordAutofillManagerTest,
EXPECT_CALL(
*autofill_client,
ShowAutofillPopup(element_bounds, _,
- SuggestionVectorValuesAre(
- testing::UnorderedElementsAre(additional_username)),
- _));
+ SuggestionVectorValuesAre(testing::ElementsAreArray(
+ GetSuggestionList({additional_username}))),
+ false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
- dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("foo@exam"),
- false, element_bounds);
+ dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("foo@exam"), 0,
+ element_bounds);
}
// Verify that typing "example" into the username field will match and order
@@ -679,12 +613,12 @@ TEST_F(PasswordAutofillManagerTest,
int dummy_key = 0;
password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
- EXPECT_CALL(
- *autofill_client,
- ShowAutofillPopup(element_bounds, _,
- SuggestionVectorValuesAre(testing::UnorderedElementsAre(
- username, additional_username)),
- _));
+ EXPECT_CALL(*autofill_client,
+ ShowAutofillPopup(
+ element_bounds, _,
+ SuggestionVectorValuesAre(testing::ElementsAreArray(
+ GetSuggestionList({username, additional_username}))),
+ false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
dummy_key, base::i18n::RIGHT_TO_LEFT, base::ASCIIToUTF16("foo"), false,
element_bounds);
@@ -702,7 +636,7 @@ TEST_F(PasswordAutofillManagerTest, PreviewAndFillEmptyUsernameSuggestion) {
l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_EMPTY_LOGIN);
// Simulate that the user clicks on a username field.
- EXPECT_CALL(*autofill_client, ShowAutofillPopup(_, _, _, _));
+ EXPECT_CALL(*autofill_client, ShowAutofillPopup(_, _, _, _, _));
gfx::RectF element_bounds;
password_autofill_manager_->OnShowPasswordSuggestions(
fill_data_id(), base::i18n::RIGHT_TO_LEFT, base::string16(), false,
@@ -724,60 +658,8 @@ TEST_F(PasswordAutofillManagerTest, PreviewAndFillEmptyUsernameSuggestion) {
testing::Mock::VerifyAndClearExpectations(client->mock_driver());
}
-// Tests that the "Show all passwords" suggestion isn't shown along with
-// "Use password for" in the popup when the feature which controls its
-// appearance is disabled.
-TEST_F(PasswordAutofillManagerTest,
- NotShowAllPasswordsOptionOnPasswordFieldWhenFeatureDisabled) {
- auto client = std::make_unique<TestPasswordManagerClient>();
- auto autofill_client = std::make_unique<MockAutofillClient>();
- InitializePasswordAutofillManager(client.get(), autofill_client.get());
-
- gfx::RectF element_bounds;
- autofill::PasswordFormFillData data;
- data.username_field.value = test_username_;
- data.password_field.value = test_password_;
- data.origin = GURL("https://foo.test");
-
- int dummy_key = 0;
- password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
-
- // String "Use password for:" shown when displaying suggestions matching a
- // username and specifying that the field is a password field.
- base::string16 title =
- l10n_util::GetStringUTF16(IDS_AUTOFILL_PASSWORD_FIELD_SUGGESTIONS_TITLE);
-
- SetManualFallbacksForFilling(false);
-
- // No "Show all passwords row" when feature is disabled.
- EXPECT_CALL(*autofill_client,
- ShowAutofillPopup(element_bounds, _,
- SuggestionVectorValuesAre(testing::ElementsAre(
- title, test_username_)),
- _));
- password_autofill_manager_->OnShowPasswordSuggestions(
- dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_,
- autofill::IS_PASSWORD_FIELD, element_bounds);
-}
-
-// Tests that the "Generate Password Suggestion" suggestion isn't shown in the
-// Suggestion popup when the user is sync user with custom passphrase and manual
-// fallbacks experiment is enabled.
-TEST_F(PasswordAutofillManagerTest,
- NotShowGeneratePasswordOptionOnPasswordFieldWhenCustomPassphraseUser) {
- TestGenerationFallback(true /* custom_passphrase_enabled */);
-}
-
-// Tests that the "Generate Password Suggestion" suggestion is shown along
-// with "Use password for" and "Show all passwords" in the popup for the user
-// with custom passphrase.
-TEST_F(PasswordAutofillManagerTest, ShowGeneratePasswordOptionOnPasswordField) {
- TestGenerationFallback(false /* custom_passphrase_enabled */);
-}
-
-// Tests that the "Show all passwords" suggestion is shown along with
-// "Use password for" in the popup when the feature which controls its
-// appearance is enabled.
+// Tests that the "Manage passwords" suggestion is shown along with the password
+// popup.
TEST_F(PasswordAutofillManagerTest, ShowAllPasswordsOptionOnPasswordField) {
const char kShownContextHistogram[] =
"PasswordManager.ShowAllSavedPasswordsShownContext";
@@ -804,30 +686,12 @@ TEST_F(PasswordAutofillManagerTest, ShowAllPasswordsOptionOnPasswordField) {
int dummy_key = 0;
password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
- // String "Use password for:" shown when displaying suggestions matching a
- // username and specifying that the field is a password field.
- base::string16 title =
- l10n_util::GetStringUTF16(IDS_AUTOFILL_PASSWORD_FIELD_SUGGESTIONS_TITLE);
-
- SetManualFallbacksForFilling(true);
-
- std::vector<base::string16> elements = {title, test_username_};
- if (!IsPreLollipopAndroid()) {
- elements = {
- title,
- test_username_,
-#if !defined(OS_ANDROID)
- base::string16(),
-#endif
- l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ALL_SAVED_FALLBACK)
- };
- }
-
EXPECT_CALL(
*autofill_client,
- ShowAutofillPopup(
- element_bounds, _,
- SuggestionVectorValuesAre(testing::ElementsAreArray(elements)), _));
+ ShowAutofillPopup(element_bounds, _,
+ SuggestionVectorValuesAre(testing::ElementsAreArray(
+ GetSuggestionList({test_username_}))),
+ false, _));
password_autofill_manager_->OnShowPasswordSuggestions(
dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_,
@@ -878,100 +742,48 @@ TEST_F(PasswordAutofillManagerTest, ShowAllPasswordsOptionOnPasswordField) {
}
}
-TEST_F(PasswordAutofillManagerTest, ShowStandaloneShowAllPasswords) {
- const char kShownContextHistogram[] =
- "PasswordManager.ShowAllSavedPasswordsShownContext";
- const char kAcceptedContextHistogram[] =
- "PasswordManager.ShowAllSavedPasswordsAcceptedContext";
- base::HistogramTester histograms;
- ukm::TestAutoSetUkmRecorder test_ukm_recorder;
-
+// Tests that the "Manage passwords" fallback shows up in non-password
+// fields of login forms.
+TEST_F(PasswordAutofillManagerTest, ShowAllPasswordsOptionOnNonPasswordField) {
auto client = std::make_unique<TestPasswordManagerClient>();
auto autofill_client = std::make_unique<MockAutofillClient>();
- auto manager =
- std::make_unique<password_manager::PasswordManager>(client.get());
InitializePasswordAutofillManager(client.get(), autofill_client.get());
- ON_CALL(*(client->mock_driver()), GetPasswordManager())
- .WillByDefault(testing::Return(manager.get()));
-
gfx::RectF element_bounds;
autofill::PasswordFormFillData data;
data.username_field.value = test_username_;
data.password_field.value = test_password_;
- data.origin = GURL("http://foo.test");
-
- // String for the "Show all passwords" fallback.
- base::string16 show_all_saved_row_text =
- l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ALL_SAVED_FALLBACK);
+ data.origin = GURL("https://foo.test");
- SetManualFallbacksForFillingStandalone(true);
+ int dummy_key = 0;
+ password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
EXPECT_CALL(
*autofill_client,
ShowAutofillPopup(element_bounds, _,
SuggestionVectorValuesAre(testing::ElementsAreArray(
- {show_all_saved_row_text})),
- _))
- .Times(IsPreLollipopAndroid() ? 0 : 1);
- password_autofill_manager_->OnShowManualFallbackSuggestion(
- base::i18n::RIGHT_TO_LEFT, element_bounds);
-
- if (IsPreLollipopAndroid()) {
- EXPECT_THAT(histograms.GetAllSamples(kShownContextHistogram),
- testing::IsEmpty());
- } else {
- // Expect a sample only in the shown histogram.
- histograms.ExpectUniqueSample(
- kShownContextHistogram,
- metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_MANUAL_FALLBACK, 1);
- }
+ GetSuggestionList({test_username_}))),
+ false, _));
+ password_autofill_manager_->OnShowPasswordSuggestions(
+ dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_, 0, element_bounds);
+}
- if (!IsPreLollipopAndroid()) {
- // Clicking at the "Show all passwords row" should trigger a call to open
- // the Password Manager settings page and hide the popup.
- EXPECT_CALL(
- *autofill_client,
- ExecuteCommand(autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY));
- EXPECT_CALL(*autofill_client, HideAutofillPopup());
- password_autofill_manager_->DidAcceptSuggestion(
- base::string16(), autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY, 0);
- // Expect a sample in both the shown and accepted histogram.
- histograms.ExpectUniqueSample(
- kShownContextHistogram,
- metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_MANUAL_FALLBACK, 1);
- histograms.ExpectUniqueSample(
- kAcceptedContextHistogram,
- metrics_util::SHOW_ALL_SAVED_PASSWORDS_CONTEXT_MANUAL_FALLBACK, 1);
- // Trigger UKM reporting, which happens at destruction time.
- ukm::SourceId expected_source_id = client->GetUkmSourceId();
- manager.reset();
- autofill_client.reset();
- client.reset();
+TEST_F(PasswordAutofillManagerTest,
+ MaybeShowPasswordSuggestionsWithGenerationNoCredentials) {
+ auto client = std::make_unique<TestPasswordManagerClient>();
+ auto autofill_client = std::make_unique<MockAutofillClient>();
+ password_autofill_manager_.reset(new PasswordAutofillManager(
+ client->mock_driver(), autofill_client.get(), client.get()));
- const auto& entries =
- test_ukm_recorder.GetEntriesByName("PageWithPassword");
- EXPECT_EQ(1u, entries.size());
- for (const auto* entry : entries) {
- EXPECT_EQ(expected_source_id, entry->source_id);
- test_ukm_recorder.ExpectEntryMetric(
- entry, UkmEntry::kPageLevelUserActionName,
- static_cast<int64_t>(
- password_manager::PasswordManagerMetricsRecorder::
- PageLevelUserAction::kShowAllPasswordsWhileNoneAreSuggested));
- }
- } else {
- EXPECT_THAT(histograms.GetAllSamples(kShownContextHistogram),
- testing::IsEmpty());
- EXPECT_THAT(histograms.GetAllSamples(kAcceptedContextHistogram),
- testing::IsEmpty());
- }
+ EXPECT_CALL(*autofill_client, ShowAutofillPopup(_, _, _, _, _)).Times(0);
+ gfx::RectF element_bounds;
+ EXPECT_FALSE(
+ password_autofill_manager_->MaybeShowPasswordSuggestionsWithGeneration(
+ element_bounds, base::i18n::RIGHT_TO_LEFT));
}
-// Tests that the "Show all passwords" fallback doesn't shows up in non-password
-// fields of login forms.
TEST_F(PasswordAutofillManagerTest,
- NotShowAllPasswordsOptionOnNonPasswordField) {
+ MaybeShowPasswordSuggestionsWithGenerationSomeCredentials) {
auto client = std::make_unique<TestPasswordManagerClient>();
auto autofill_client = std::make_unique<MockAutofillClient>();
InitializePasswordAutofillManager(client.get(), autofill_client.get());
@@ -983,29 +795,32 @@ TEST_F(PasswordAutofillManagerTest,
data.origin = GURL("https://foo.test");
int dummy_key = 0;
+ favicon::MockFaviconService favicon_service;
+ EXPECT_CALL(*client, GetFaviconService()).WillOnce(Return(&favicon_service));
+ EXPECT_CALL(favicon_service, GetFaviconImageForPageURL(data.origin, _, _));
password_autofill_manager_->OnAddPasswordFormMapping(dummy_key, data);
- SetManualFallbacksForFilling(true);
-
+ // Bring up the drop-down with the generaion option.
+ base::string16 generation_string =
+ l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_GENERATE_PASSWORD);
EXPECT_CALL(
*autofill_client,
ShowAutofillPopup(
- element_bounds, _,
- SuggestionVectorValuesAre(testing::ElementsAre(test_username_)), _));
- password_autofill_manager_->OnShowPasswordSuggestions(
- dummy_key, base::i18n::RIGHT_TO_LEFT, test_username_, 0, element_bounds);
-}
-
-// SimpleWebviewDialog doesn't have an autofill client. Nothing should crash if
-// the filling fallback is invoked.
-TEST_F(PasswordAutofillManagerTest, ShowAllPasswordsWithoutAutofillClient) {
- auto client = std::make_unique<TestPasswordManagerClient>();
- InitializePasswordAutofillManager(client.get(), nullptr);
-
- SetManualFallbacksForFillingStandalone(true);
-
- password_autofill_manager_->OnShowManualFallbackSuggestion(
- base::i18n::RIGHT_TO_LEFT, gfx::RectF());
+ element_bounds, base::i18n::RIGHT_TO_LEFT,
+ AllOf(SuggestionVectorValuesAre(ElementsAreArray(
+ GetSuggestionList({test_username_, generation_string}))),
+ SuggestionVectorIconsAre(
+ ElementsAreArray(GetIconsList({"globeIcon", "keyIcon"})))),
+ false, _));
+ EXPECT_TRUE(
+ password_autofill_manager_->MaybeShowPasswordSuggestionsWithGeneration(
+ element_bounds, base::i18n::RIGHT_TO_LEFT));
+
+ // Click "Generate password".
+ EXPECT_CALL(*client, GeneratePassword());
+ EXPECT_CALL(*autofill_client, HideAutofillPopup());
+ password_autofill_manager_->DidAcceptSuggestion(
+ base::string16(), autofill::POPUP_ITEM_ID_GENERATE_PASSWORD_ENTRY, 1);
}
} // namespace password_manager
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 3c446d45560..96c7f25caa1 100644
--- a/chromium/components/password_manager/core/browser/password_bubble_experiment.cc
+++ b/chromium/components/password_manager/core/browser/password_bubble_experiment.cc
@@ -38,8 +38,8 @@ int GetSmartBubbleDismissalThreshold() {
}
bool IsSmartLockUser(const syncer::SyncService* sync_service) {
- return password_manager_util::GetPasswordSyncState(sync_service) ==
- password_manager::SYNCING_NORMAL_ENCRYPTION;
+ return password_manager_util::GetPasswordSyncState(sync_service) !=
+ password_manager::NOT_SYNCING;
}
bool ShouldShowAutoSignInPromptFirstRunExperience(PrefService* prefs) {
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 19e982d037d..9d83ccb42f6 100644
--- a/chromium/components/password_manager/core/browser/password_bubble_experiment.h
+++ b/chromium/components/password_manager/core/browser/password_bubble_experiment.h
@@ -26,7 +26,8 @@ void RegisterPrefs(PrefRegistrySimple* registry);
// user before it's not shown automatically.
int GetSmartBubbleDismissalThreshold();
-// A Smart Lock user is a sync user without a custom passphrase.
+// Returns true if the user syncs passwords to Google Account.
+// TODO(crbug.com/862269): rename the function.
bool IsSmartLockUser(const syncer::SyncService* sync_service);
// Returns true if first run experience for auto sign-in prompt should be shown.
diff --git a/chromium/components/password_manager/core/browser/password_bubble_experiment_unittest.cc b/chromium/components/password_manager/core/browser/password_bubble_experiment_unittest.cc
index 80dba82ab7a..d9c17deade0 100644
--- a/chromium/components/password_manager/core/browser/password_bubble_experiment_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_bubble_experiment_unittest.cc
@@ -25,14 +25,15 @@ enum class CustomPassphraseState { NONE, SET };
class TestSyncService : public syncer::FakeSyncService {
public:
// FakeSyncService overrides.
- bool IsSyncAllowed() const override { return is_sync_allowed_; }
+ int GetDisableReasons() const override { return disable_reasons_; }
- bool IsFirstSetupComplete() const override {
- return is_first_setup_complete_;
+ State GetState() const override {
+ return IsFirstSetupComplete() ? State::ACTIVE
+ : State::PENDING_DESIRED_CONFIGURATION;
}
- bool IsSyncActive() const override {
- return is_sync_allowed_ && is_first_setup_complete_;
+ bool IsFirstSetupComplete() const override {
+ return is_first_setup_complete_;
}
syncer::ModelTypeSet GetActiveDataTypes() const override { return type_set_; }
@@ -53,7 +54,9 @@ class TestSyncService : public syncer::FakeSyncService {
type_set_ = type_set;
}
- void set_sync_allowed(bool sync_allowed) { is_sync_allowed_ = sync_allowed; }
+ void set_disable_reasons(int disable_reasons) {
+ disable_reasons_ = disable_reasons;
+ }
void set_first_setup_complete(bool setup_complete) {
is_first_setup_complete_ = setup_complete;
@@ -61,12 +64,10 @@ class TestSyncService : public syncer::FakeSyncService {
void ClearActiveDataTypes() { type_set_.Clear(); }
- bool CanSyncStart() const override { return true; }
-
private:
+ int disable_reasons_ = DISABLE_REASON_NONE;
syncer::ModelTypeSet type_set_;
bool is_using_secondary_passphrase_ = false;
- bool is_sync_allowed_ = true;
bool is_first_setup_complete_ = true;
};
@@ -120,7 +121,10 @@ TEST_F(PasswordManagerPasswordBubbleExperimentTest,
prefs()->SetInteger(
password_manager::prefs::kNumberSignInPasswordPromoShown,
test_case.current_shown_count);
- sync_service()->set_sync_allowed(test_case.is_sync_allowed);
+ sync_service()->set_disable_reasons(
+ test_case.is_sync_allowed
+ ? syncer::SyncService::DISABLE_REASON_NONE
+ : syncer::SyncService::DISABLE_REASON_PLATFORM_OVERRIDE);
sync_service()->set_first_setup_complete(test_case.is_first_setup_complete);
EXPECT_EQ(test_case.result,
@@ -132,19 +136,18 @@ TEST_F(PasswordManagerPasswordBubbleExperimentTest, IsSmartLockUser) {
constexpr struct {
syncer::ModelType type;
CustomPassphraseState passphrase_state;
- bool expected_smart_lock_user;
+ bool expected_sync_user;
} kTestData[] = {
{syncer::ModelType::BOOKMARKS, CustomPassphraseState::NONE, false},
{syncer::ModelType::BOOKMARKS, CustomPassphraseState::SET, false},
{syncer::ModelType::PASSWORDS, CustomPassphraseState::NONE, true},
- {syncer::ModelType::PASSWORDS, CustomPassphraseState::SET, false},
+ {syncer::ModelType::PASSWORDS, CustomPassphraseState::SET, true},
};
for (const auto& test_case : kTestData) {
SCOPED_TRACE(testing::Message("#test_case = ") << (&test_case - kTestData));
SetupFakeSyncServiceForTestCase(test_case.type, test_case.passphrase_state);
- EXPECT_EQ(test_case.expected_smart_lock_user,
- IsSmartLockUser(sync_service()));
+ EXPECT_EQ(test_case.expected_sync_user, IsSmartLockUser(sync_service()));
}
}
diff --git a/chromium/components/password_manager/core/browser/password_form_filling.cc b/chromium/components/password_manager/core/browser/password_form_filling.cc
index 7abbd663d20..6471717405e 100644
--- a/chromium/components/password_manager/core/browser/password_form_filling.cc
+++ b/chromium/components/password_manager/core/browser/password_form_filling.cc
@@ -115,10 +115,8 @@ void SendFillInformationToRenderer(
DCHECK(driver);
DCHECK_EQ(PasswordForm::SCHEME_HTML, observed_form.scheme);
- if (is_blacklisted)
- driver->MatchingBlacklistedFormFound();
-
- driver->AllowPasswordGenerationForForm(observed_form);
+ if (!is_blacklisted)
+ driver->AllowPasswordGenerationForForm(observed_form);
if (best_matches.empty()) {
driver->InformNoSavedCredentials();
diff --git a/chromium/components/password_manager/core/browser/password_form_filling_unittest.cc b/chromium/components/password_manager/core/browser/password_form_filling_unittest.cc
index 7017c4cb8d3..0fb935e750b 100644
--- a/chromium/components/password_manager/core/browser/password_form_filling_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_form_filling_unittest.cc
@@ -38,7 +38,6 @@ class MockPasswordManagerDriver : public StubPasswordManagerDriver {
MOCK_METHOD1(ShowInitialPasswordAccountSuggestions,
void(const PasswordFormFillData&));
MOCK_METHOD1(AllowPasswordGenerationForForm, void(const PasswordForm&));
- MOCK_METHOD0(MatchingBlacklistedFormFound, void());
};
class MockPasswordManagerClient : public StubPasswordManagerClient {
@@ -114,14 +113,13 @@ TEST_F(PasswordFormFillingTest, Autofill) {
another_saved_match.password_value += ASCIIToUTF16("1");
best_matches[another_saved_match.username_value] = &another_saved_match;
- EXPECT_CALL(driver_, AllowPasswordGenerationForForm(observed_form_));
+ EXPECT_CALL(driver_, AllowPasswordGenerationForForm(observed_form_))
+ .Times(is_blacklisted ? 0 : 1);
EXPECT_CALL(driver_, InformNoSavedCredentials()).Times(0);
PasswordFormFillData fill_data;
EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data));
EXPECT_CALL(driver_, ShowInitialPasswordAccountSuggestions(_)).Times(0);
EXPECT_CALL(client_, PasswordWasAutofilled(_, _, _));
- EXPECT_CALL(driver_, MatchingBlacklistedFormFound)
- .Times(is_blacklisted ? 1 : 0);
SendFillInformationToRenderer(
client_, &driver_, is_blacklisted, observed_form_, best_matches,
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 663cba455fc..8181a463565 100644
--- a/chromium/components/password_manager/core/browser/password_form_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_form_manager.cc
@@ -4,7 +4,6 @@
#include "components/password_manager/core/browser/password_form_manager.h"
-#include <ctype.h>
#include <stddef.h>
#include <algorithm>
@@ -16,14 +15,11 @@
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
-#include "base/rand_util.h"
#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
-#include "components/autofill/core/browser/autofill_manager.h"
-#include "components/autofill/core/browser/proto/server.pb.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
@@ -124,86 +120,6 @@ void CopyFieldPropertiesMasks(const PasswordForm& from, PasswordForm* to) {
}
}
-// Sets autofill types of password and new password fields in |field_types|.
-// |password_type| (the autofill type of new password field) should be equal to
-// NEW_PASSWORD, PROBABLY_NEW_PASSWORD or NOT_NEW_PASSWORD. These values
-// correspond to cases when the user confirmed password update, did nothing or
-// declined to update password respectively.
-void SetFieldLabelsOnUpdate(const autofill::ServerFieldType password_type,
- const autofill::PasswordForm& submitted_form,
- FieldTypeMap* field_types) {
- DCHECK(password_type == autofill::NEW_PASSWORD ||
- password_type == autofill::PROBABLY_NEW_PASSWORD ||
- password_type == autofill::NOT_NEW_PASSWORD)
- << password_type;
- DCHECK(!submitted_form.new_password_element.empty());
-
- (*field_types)[submitted_form.password_element] = autofill::PASSWORD;
- (*field_types)[submitted_form.new_password_element] = password_type;
-}
-
-// Sets the autofill type of the password field stored in |submitted_form| to
-// |password_type| in |field_types| map.
-void SetFieldLabelsOnSave(const autofill::ServerFieldType password_type,
- const autofill::PasswordForm& form,
- FieldTypeMap* field_types) {
- DCHECK(password_type == autofill::PASSWORD ||
- password_type == autofill::ACCOUNT_CREATION_PASSWORD ||
- password_type == autofill::NOT_ACCOUNT_CREATION_PASSWORD)
- << password_type;
-
- if (!form.new_password_element.empty()) {
- (*field_types)[form.new_password_element] = password_type;
- } else {
- DCHECK(!form.password_element.empty());
- (*field_types)[form.password_element] = password_type;
- }
-}
-
-// Label username and password fields with autofill types in |form_structure|
-// based on |field_types|, and vote types based on |vote_types|. The function
-// also adds the types to |available_field_types|. For fields of |USERNAME|
-// type, a vote type must exist.
-void LabelFields(const FieldTypeMap& field_types,
- const VoteTypeMap& vote_types,
- FormStructure* form_structure,
- autofill::ServerFieldTypeSet* available_field_types) {
- for (size_t i = 0; i < form_structure->field_count(); ++i) {
- autofill::AutofillField* field = form_structure->field(i);
-
- autofill::ServerFieldType type = autofill::UNKNOWN_TYPE;
- if (!field->name.empty()) {
- auto iter = field_types.find(field->name);
- if (iter != field_types.end()) {
- type = iter->second;
- available_field_types->insert(type);
- }
-
- auto vote_type_iter = vote_types.find(field->name);
- if (vote_type_iter != vote_types.end())
- field->set_vote_type(vote_type_iter->second);
- DCHECK(type != autofill::USERNAME ||
- field->vote_type() !=
- autofill::AutofillUploadContents::Field::NO_INFORMATION);
- }
-
- autofill::ServerFieldTypeSet types;
- types.insert(type);
- field->set_possible_types(types);
- }
-}
-
-// Returns true iff |credentials| has the same password as an entry in |matches|
-// which doesn't have a username.
-bool IsAddingUsernameToExistingMatch(
- const PasswordForm& credentials,
- const std::map<base::string16, const autofill::PasswordForm*>& matches) {
- const auto match = matches.find(base::string16());
- return !credentials.username_value.empty() && match != matches.end() &&
- !match->second->is_public_suffix_match &&
- match->second->password_value == credentials.password_value;
-}
-
} // namespace
PasswordFormManager::PasswordFormManager(
@@ -217,10 +133,6 @@ PasswordFormManager::PasswordFormManager(
observed_form_signature_(CalculateFormSignature(observed_form.form_data)),
is_new_login_(true),
has_generated_password_(false),
- generated_password_changed_(false),
- is_manual_generation_(false),
- generation_popup_was_shown_(false),
- form_classifier_outcome_(kNoOutcome),
password_overridden_(false),
retry_password_form_password_update_(false),
password_manager_(password_manager),
@@ -238,6 +150,7 @@ PasswordFormManager::PasswordFormManager(
true /* should_migrate_http_passwords */,
true /* should_query_suppressed_https_forms */)),
form_fetcher_(form_fetcher ? form_fetcher : owned_form_fetcher_.get()),
+ votes_uploader_(client, observed_form.IsPossibleChangePasswordForm()),
is_main_frame_secure_(client->IsMainFrameSecure()) {
// Non-HTML forms should not need any interaction with the renderer, and hence
// no driver. Note that cloned PasswordFormManager instances can have HTML
@@ -260,8 +173,10 @@ void PasswordFormManager::Init(
metrics_recorder_->RecordFormSignature(observed_form_signature_);
- if (owned_form_fetcher_)
+ if (owned_form_fetcher_ &&
+ !observed_form_.is_gaia_with_skip_save_password_form) {
owned_form_fetcher_->Fetch();
+ }
form_fetcher_->AddConsumer(this);
}
@@ -397,11 +312,19 @@ void PasswordFormManager::Save() {
DidPreferenceChange(best_matches_, pending_credentials_.username_value)) {
SetUserAction(UserAction::kChoose);
}
+ if (user_action_ == UserAction::kOverridePassword &&
+ pending_credentials_.type == PasswordForm::TYPE_GENERATED &&
+ !has_generated_password_) {
+ metrics_util::LogPasswordGenerationSubmissionEvent(
+ metrics_util::PASSWORD_OVERRIDDEN);
+ pending_credentials_.type = PasswordForm::TYPE_MANUAL;
+ }
if (is_new_login_) {
SanitizePossibleUsernames(&pending_credentials_);
pending_credentials_.date_created = base::Time::Now();
- SendVotesOnSave();
+ votes_uploader_.SendVotesOnSave(observed_form_.form_data, *submitted_form_,
+ best_matches_, &pending_credentials_);
form_saver_->Save(pending_credentials_, best_matches_);
} else {
ProcessUpdate();
@@ -414,7 +337,7 @@ void PasswordFormManager::Save() {
}
// This is not in ProcessUpdate() to catch PSL matched credentials.
- if (pending_credentials_.times_used != 0 &&
+ if (pending_credentials_.times_used == 1 &&
pending_credentials_.type == PasswordForm::TYPE_GENERATED) {
metrics_util::LogPasswordGenerationSubmissionEvent(
metrics_util::PASSWORD_USED);
@@ -431,8 +354,9 @@ void PasswordFormManager::Update(
submitted_form_->submission_event);
if (observed_form_.IsPossibleChangePasswordForm()) {
FormStructure form_structure(credentials_to_update.form_data);
- UploadPasswordVote(observed_form_, autofill::NEW_PASSWORD,
- form_structure.FormSignatureAsStr());
+ votes_uploader_.UploadPasswordVote(observed_form_, *submitted_form_,
+ autofill::NEW_PASSWORD,
+ form_structure.FormSignatureAsStr());
}
base::string16 password_to_save = pending_credentials_.password_value;
bool skip_zero_click = pending_credentials_.skip_zero_click;
@@ -462,7 +386,7 @@ void PasswordFormManager::UpdateUsername(const base::string16& new_username) {
// |has_username_edited_vote_| is true iff |new_username| was typed in another
// field. Otherwise, |has_username_edited_vote_| is false and no vote will be
// uploaded.
- has_username_edited_vote_ = false;
+ votes_uploader_.set_has_username_edited_vote(false);
if (!new_username.empty()) {
for (size_t i = 0; i < credential.other_possible_usernames.size(); ++i) {
if (credential.other_possible_usernames[i].first == new_username) {
@@ -473,7 +397,7 @@ void PasswordFormManager::UpdateUsername(const base::string16& new_username) {
credential.other_possible_usernames.begin() + i);
// Set |corrected_username_element_| to upload a username vote.
- has_username_edited_vote_ = true;
+ votes_uploader_.set_has_username_edited_vote(true);
break;
}
}
@@ -523,21 +447,26 @@ void PasswordFormManager::UpdatePasswordValue(
void PasswordFormManager::PresaveGeneratedPassword(
const autofill::PasswordForm& form) {
- form_saver()->PresaveGeneratedPassword(form);
- metrics_recorder_->SetHasGeneratedPassword(true);
- if (has_generated_password_) {
- generated_password_changed_ = true;
+ if ((best_matches_.find(form.username_value) == best_matches_.end()) ||
+ form.username_value.empty()) {
+ form_saver()->PresaveGeneratedPassword(form);
} else {
- SetHasGeneratedPassword(true);
- generated_password_changed_ = false;
+ autofill::PasswordForm form_without_username(form);
+ form_without_username.username_value.clear();
+ form_saver()->PresaveGeneratedPassword(form_without_username);
}
+ // If a password had been generated already, a call to
+ // PresaveGeneratedPassword() implies that this password was modified.
+ SetGeneratedPasswordChanged(has_generated_password_);
+ if (!has_generated_password_)
+ SetHasGeneratedPassword(true);
}
void PasswordFormManager::PasswordNoLongerGenerated() {
DCHECK(has_generated_password_);
form_saver()->RemovePresavedPassword();
SetHasGeneratedPassword(false);
- generated_password_changed_ = false;
+ SetGeneratedPasswordChanged(false);
}
void PasswordFormManager::SaveSubmittedFormTypeForMetrics(
@@ -681,323 +610,14 @@ void PasswordFormManager::ProcessUpdate() {
// Check to see if this form is a candidate for password generation.
// Do not send votes on change password forms, since they were already sent in
// Update() method.
- if (!observed_form_.IsPossibleChangePasswordForm())
- SendVoteOnCredentialsReuse(observed_form_, &pending_credentials_);
-
- // TODO(crbug.com/840384): If there is no username, we should vote again when
- // the credential is updated with a username.
- if (pending_credentials_.times_used == 1)
- UploadFirstLoginVotes(*submitted_form_);
-}
-
-bool PasswordFormManager::FindUsernameInOtherPossibleUsernames(
- const autofill::PasswordForm& match,
- const base::string16& username) {
- DCHECK(!username_correction_vote_);
-
- for (const ValueElementPair& pair : match.other_possible_usernames) {
- if (pair.first == username) {
- username_correction_vote_.reset(new autofill::PasswordForm(match));
- username_correction_vote_->username_element = pair.second;
- return true;
- }
- }
- return false;
-}
-
-bool PasswordFormManager::FindCorrectedUsernameElement(
- const base::string16& username,
- const base::string16& password) {
- if (username.empty())
- return false;
- for (const auto& key_value : best_matches_) {
- const PasswordForm* match = key_value.second;
- if ((match->password_value == password) &&
- FindUsernameInOtherPossibleUsernames(*match, username))
- return true;
+ if (!observed_form_.IsPossibleChangePasswordForm()) {
+ votes_uploader_.SendVoteOnCredentialsReuse(
+ observed_form_.form_data, *submitted_form_, &pending_credentials_);
}
- for (const autofill::PasswordForm* match : not_best_matches_) {
- if ((match->password_value == password) &&
- FindUsernameInOtherPossibleUsernames(*match, username))
- return true;
- }
- return false;
-}
-void PasswordFormManager::SendVoteOnCredentialsReuse(
- const PasswordForm& observed,
- PasswordForm* pending) {
- // Ignore |pending_structure| if its FormData has no fields. This is to
- // weed out those credentials that were saved before FormData was added
- // to PasswordForm. Even without this check, these FormStructure's won't
- // be uploaded, but it makes it hard to see if we are encountering
- // unexpected errors.
- if (pending->form_data.fields.empty())
- return;
-
- FormStructure pending_structure(pending->form_data);
- FormStructure observed_structure(observed.form_data);
-
- if (pending_structure.form_signature() !=
- observed_structure.form_signature()) {
- // Only upload if this is the first time the password has been used.
- // Otherwise the credentials have been used on the same field before so
- // they aren't from an account creation form.
- // Also bypass uploading if the username was edited. Offering generation
- // in cases where we currently save the wrong username isn't great.
- // TODO(gcasto): Determine if generation should be offered in this case.
- if (pending->times_used == 1) {
- if (UploadPasswordVote(*pending, autofill::ACCOUNT_CREATION_PASSWORD,
- observed_structure.FormSignatureAsStr())) {
- pending->generation_upload_status =
- autofill::PasswordForm::POSITIVE_SIGNAL_SENT;
- }
- }
- } else if (pending->generation_upload_status ==
- autofill::PasswordForm::POSITIVE_SIGNAL_SENT) {
- // A signal was sent that this was an account creation form, but the
- // credential is now being used on the same form again. This cancels out
- // the previous vote.
- if (UploadPasswordVote(*pending, autofill::NOT_ACCOUNT_CREATION_PASSWORD,
- std::string())) {
- pending->generation_upload_status =
- autofill::PasswordForm::NEGATIVE_SIGNAL_SENT;
- }
- } else if (generation_popup_was_shown_) {
- // Even if there is no autofill vote to be sent, send the vote about the
- // usage of the generation popup.
- UploadPasswordVote(*pending, autofill::UNKNOWN_TYPE, std::string());
- }
-}
-
-bool PasswordFormManager::UploadPasswordVote(
- const autofill::PasswordForm& form_to_upload,
- const autofill::ServerFieldType& autofill_type,
- const std::string& login_form_signature) {
- // Check if there is any vote to be sent.
- bool has_autofill_vote = autofill_type != autofill::UNKNOWN_TYPE;
- bool has_password_generation_vote = generation_popup_was_shown_;
- if (!has_autofill_vote && !has_password_generation_vote)
- return false;
-
- autofill::AutofillManager* autofill_manager =
- client_->GetAutofillManagerForMainFrame();
- if (!autofill_manager || !autofill_manager->download_manager())
- return false;
-
- // If this is an update, a vote about the observed form is sent. If the user
- // re-uses credentials, a vote about the saved form is sent. If the user saves
- // credentials, the observed and pending forms are the same.
- FormStructure form_structure(form_to_upload.form_data);
- if (!autofill_manager->ShouldUploadForm(form_structure)) {
- UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", false);
- return false;
- }
-
- autofill::ServerFieldTypeSet available_field_types;
- // A map from field names to field types.
- FieldTypeMap field_types;
- auto username_vote_type =
- autofill::AutofillUploadContents::Field::NO_INFORMATION;
- if (autofill_type != autofill::USERNAME) {
- if (has_autofill_vote) {
- bool is_update = autofill_type == autofill::NEW_PASSWORD ||
- autofill_type == autofill::PROBABLY_NEW_PASSWORD ||
- autofill_type == autofill::NOT_NEW_PASSWORD;
-
- if (is_update) {
- if (form_to_upload.new_password_element.empty())
- return false;
- SetFieldLabelsOnUpdate(autofill_type, form_to_upload, &field_types);
- } else { // Saving.
- SetFieldLabelsOnSave(autofill_type, form_to_upload, &field_types);
- }
- if (autofill_type != autofill::ACCOUNT_CREATION_PASSWORD) {
- // If |autofill_type| == autofill::ACCOUNT_CREATION_PASSWORD, Chrome
- // will upload a vote for another form: the one that the credential was
- // saved on.
- field_types[submitted_form_->confirmation_password_element] =
- autofill::CONFIRMATION_PASSWORD;
- form_structure.set_passwords_were_revealed(
- has_passwords_revealed_vote_);
- }
- }
- if (autofill_type != autofill::ACCOUNT_CREATION_PASSWORD) {
- if (generation_popup_was_shown_)
- AddGeneratedVote(&form_structure);
- if (form_classifier_outcome_ != kNoOutcome)
- AddFormClassifierVote(&form_structure);
- if (has_username_edited_vote_) {
- field_types[form_to_upload.username_element] = autofill::USERNAME;
- username_vote_type =
- autofill::AutofillUploadContents::Field::USERNAME_EDITED;
- }
- } else { // User reuses credentials.
- // If the saved username value was used, then send a confirmation vote for
- // username.
- if (!submitted_form_->username_value.empty()) {
- DCHECK(submitted_form_->username_value ==
- form_to_upload.username_value);
- field_types[form_to_upload.username_element] = autofill::USERNAME;
- username_vote_type =
- autofill::AutofillUploadContents::Field::CREDENTIALS_REUSED;
- }
- }
- if (autofill_type == autofill::PASSWORD) {
- // The password attributes should be uploaded only on the first save.
- DCHECK(pending_credentials_.times_used == 0);
- GeneratePasswordAttributesVote(pending_credentials_.password_value,
- &form_structure);
- }
- } else { // User overwrites username.
- field_types[form_to_upload.username_element] = autofill::USERNAME;
- field_types[form_to_upload.password_element] =
- autofill::ACCOUNT_CREATION_PASSWORD;
- username_vote_type =
- autofill::AutofillUploadContents::Field::USERNAME_OVERWRITTEN;
- }
- LabelFields(field_types,
- {{form_to_upload.username_element, username_vote_type}},
- &form_structure, &available_field_types);
-
- // Force uploading as these events are relatively rare and we want to make
- // sure to receive them.
- form_structure.set_upload_required(UPLOAD_REQUIRED);
-
- if (password_manager_util::IsLoggingActive(client_)) {
- BrowserSavePasswordProgressLogger logger(client_->GetLogManager());
- logger.LogFormStructure(Logger::STRING_FORM_VOTES, form_structure);
- }
-
- bool success = autofill_manager->download_manager()->StartUploadRequest(
- form_structure, false /* was_autofilled */, available_field_types,
- login_form_signature, true /* observed_submission */);
-
- UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", success);
- return success;
-}
-
-void PasswordFormManager::AddGeneratedVote(
- autofill::FormStructure* form_structure) {
- DCHECK(form_structure);
- DCHECK(generation_popup_was_shown_);
-
- if (generation_element_.empty())
- return;
-
- autofill::AutofillUploadContents::Field::PasswordGenerationType type =
- autofill::AutofillUploadContents::Field::NO_GENERATION;
- if (has_generated_password_) {
- if (is_manual_generation_) {
- type = observed_form_.IsPossibleChangePasswordForm()
- ? autofill::AutofillUploadContents::Field::
- MANUALLY_TRIGGERED_GENERATION_ON_CHANGE_PASSWORD_FORM
- : autofill::AutofillUploadContents::Field::
- MANUALLY_TRIGGERED_GENERATION_ON_SIGN_UP_FORM;
- } else {
- type =
- observed_form_.IsPossibleChangePasswordForm()
- ? autofill::AutofillUploadContents::Field::
- AUTOMATICALLY_TRIGGERED_GENERATION_ON_CHANGE_PASSWORD_FORM
- : autofill::AutofillUploadContents::Field::
- AUTOMATICALLY_TRIGGERED_GENERATION_ON_SIGN_UP_FORM;
- }
- } else
- type = autofill::AutofillUploadContents::Field::IGNORED_GENERATION_POPUP;
-
- for (size_t i = 0; i < form_structure->field_count(); ++i) {
- autofill::AutofillField* field = form_structure->field(i);
- if (field->name == generation_element_) {
- field->set_generation_type(type);
- if (has_generated_password_) {
- field->set_generated_password_changed(generated_password_changed_);
- UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.GeneratedPasswordWasEdited",
- generated_password_changed_);
- }
- break;
- }
- }
-}
-
-void PasswordFormManager::AddFormClassifierVote(
- autofill::FormStructure* form_structure) {
- DCHECK(form_structure);
- DCHECK(form_classifier_outcome_ != kNoOutcome);
-
- for (size_t i = 0; i < form_structure->field_count(); ++i) {
- autofill::AutofillField* field = form_structure->field(i);
- if (form_classifier_outcome_ == kFoundGenerationElement &&
- field->name == generation_element_detected_by_classifier_) {
- field->set_form_classifier_outcome(
- autofill::AutofillUploadContents::Field::GENERATION_ELEMENT);
- } else {
- field->set_form_classifier_outcome(
- autofill::AutofillUploadContents::Field::NON_GENERATION_ELEMENT);
- }
- }
-}
-
-// TODO(crbug.com/840384): Share common code with UploadPasswordVote.
-void PasswordFormManager::UploadFirstLoginVotes(
- const PasswordForm& form_to_upload) {
- autofill::AutofillManager* autofill_manager =
- client_->GetAutofillManagerForMainFrame();
- if (!autofill_manager || !autofill_manager->download_manager())
- return;
-
- FormStructure form_structure(form_to_upload.form_data);
- if (!autofill_manager->ShouldUploadForm(form_structure))
- return;
-
- FieldTypeMap field_types = {
- {form_to_upload.username_element, autofill::USERNAME}};
- VoteTypeMap vote_types = {
- {form_to_upload.username_element,
- autofill::AutofillUploadContents::Field::FIRST_USE}};
- if (!password_overridden_) {
- field_types[form_to_upload.password_element] = autofill::PASSWORD;
- vote_types[form_to_upload.password_element] =
- autofill::AutofillUploadContents::Field::FIRST_USE;
- }
-
- autofill::ServerFieldTypeSet available_field_types;
- LabelFields(field_types, vote_types, &form_structure, &available_field_types);
- SetKnownValueFlag(&form_structure);
-
- // Force uploading as these events are relatively rare and we want to make
- // sure to receive them.
- form_structure.set_upload_required(UPLOAD_REQUIRED);
-
- if (password_manager_util::IsLoggingActive(client_)) {
- BrowserSavePasswordProgressLogger logger(client_->GetLogManager());
- logger.LogFormStructure(Logger::STRING_FORM_VOTES, form_structure);
- }
-
- autofill_manager->download_manager()->StartUploadRequest(
- form_structure, false /* was_autofilled */, available_field_types,
- std::string(), true /* observed_submission */);
-}
-
-void PasswordFormManager::SetKnownValueFlag(autofill::FormStructure* form) {
- DCHECK(!password_overridden_ ||
- best_matches_.find(pending_credentials_.username_value) !=
- best_matches_.end())
- << "The credential is being overriden, but it does not exist in "
- "the best matches.";
-
- const base::string16& known_username = pending_credentials_.username_value;
- // If we are updating a password, the known value is the old password, not
- // the new one.
- const base::string16& known_password =
- password_overridden_ ? best_matches_[known_username]->password_value
- : pending_credentials_.password_value;
-
- for (auto& field : *form) {
- if (field->value.empty())
- continue;
- if (known_username == field->value || known_password == field->value) {
- field->properties_mask |= autofill::FieldPropertiesFlags::KNOWN_VALUE;
- }
+ if (pending_credentials_.times_used == 1) {
+ votes_uploader_.UploadFirstLoginVotes(best_matches_, pending_credentials_,
+ *submitted_form_);
}
}
@@ -1014,8 +634,9 @@ void PasswordFormManager::CreatePendingCredentials() {
if (saved_form != nullptr) {
// The user signed in with a login we autofilled.
pending_credentials_ = *saved_form;
- password_overridden_ =
- pending_credentials_.password_value != password_to_save.first;
+ SetPasswordOverridden(pending_credentials_.password_value !=
+ password_to_save.first);
+
if (IsPendingCredentialsPublicSuffixMatch()) {
// If the autofilled credentials were a PSL match or credentials stored
// from Android apps, store a copy with the current origin and signon
@@ -1062,7 +683,7 @@ void PasswordFormManager::CreatePendingCredentials() {
// here again.
if (password_overridden_) {
pending_credentials_.is_public_suffix_match = false;
- password_overridden_ = false;
+ SetPasswordOverridden(false);
}
} else { // Not a PSL match but a match of an already stored credential.
is_new_login_ = false;
@@ -1114,8 +735,10 @@ void PasswordFormManager::CreatePendingCredentials() {
// save new credentials.
CreatePendingCredentialsForNewCredentials(password_to_save.second);
// Generate username correction votes.
- bool username_correction_found = FindCorrectedUsernameElement(
- submitted_form_->username_value, submitted_form_->password_value);
+ bool username_correction_found =
+ votes_uploader_.FindCorrectedUsernameElement(
+ best_matches_, not_best_matches_, submitted_form_->username_value,
+ submitted_form_->password_value);
UMA_HISTOGRAM_BOOLEAN("PasswordManager.UsernameCorrectionFound",
username_correction_found);
if (username_correction_found) {
@@ -1154,14 +777,6 @@ void PasswordFormManager::CreatePendingCredentials() {
pending_credentials_.signon_realm = submitted_form_->signon_realm;
}
- if (user_action_ == UserAction::kOverridePassword &&
- pending_credentials_.type == PasswordForm::TYPE_GENERATED &&
- !has_generated_password_) {
- metrics_util::LogPasswordGenerationSubmissionEvent(
- metrics_util::PASSWORD_OVERRIDDEN);
- pending_credentials_.type = PasswordForm::TYPE_MANUAL;
- }
-
if (has_generated_password_)
pending_credentials_.type = PasswordForm::TYPE_GENERATED;
}
@@ -1247,34 +862,52 @@ void PasswordFormManager::CreatePendingCredentialsForNewCredentials(
}
void PasswordFormManager::OnNopeUpdateClicked() {
- UploadPasswordVote(observed_form_, autofill::NOT_NEW_PASSWORD, std::string());
+ votes_uploader_.UploadPasswordVote(observed_form_, *submitted_form_,
+ autofill::NOT_NEW_PASSWORD, std::string());
}
void PasswordFormManager::OnNeverClicked() {
- UploadPasswordVote(pending_credentials_, autofill::UNKNOWN_TYPE,
- std::string());
+ votes_uploader_.UploadPasswordVote(pending_credentials_, *submitted_form_,
+ autofill::UNKNOWN_TYPE, std::string());
PermanentlyBlacklist();
}
void PasswordFormManager::OnNoInteraction(bool is_update) {
if (is_update) {
- UploadPasswordVote(observed_form_, autofill::PROBABLY_NEW_PASSWORD,
- std::string());
+ votes_uploader_.UploadPasswordVote(observed_form_, *submitted_form_,
+ autofill::PROBABLY_NEW_PASSWORD,
+ std::string());
} else {
- UploadPasswordVote(pending_credentials_, autofill::UNKNOWN_TYPE,
- std::string());
+ votes_uploader_.UploadPasswordVote(pending_credentials_, *submitted_form_,
+ autofill::UNKNOWN_TYPE, std::string());
}
}
void PasswordFormManager::OnPasswordsRevealed() {
- has_passwords_revealed_vote_ = true;
+ votes_uploader_.set_has_passwords_revealed_vote(true);
}
void PasswordFormManager::SetHasGeneratedPassword(bool generated_password) {
has_generated_password_ = generated_password;
+ votes_uploader_.set_has_generated_password(generated_password);
metrics_recorder_->SetHasGeneratedPassword(generated_password);
}
+void PasswordFormManager::SetGeneratedPasswordChanged(
+ bool generated_password_changed) {
+ votes_uploader_.set_generated_password_changed(generated_password_changed);
+ metrics_recorder_->SetHasGeneratedPasswordChanged(generated_password_changed);
+}
+
+void PasswordFormManager::SetGenerationPopupWasShown(
+ bool generation_popup_was_shown,
+ bool is_manual_generation) {
+ votes_uploader_.set_generation_popup_was_shown(generation_popup_was_shown);
+ votes_uploader_.set_is_manual_generation(is_manual_generation);
+ metrics_recorder_->SetPasswordGenerationPopupShown(generation_popup_was_shown,
+ is_manual_generation);
+}
+
void PasswordFormManager::LogSubmitPassed() {
metrics_recorder_->LogSubmitPassed();
}
@@ -1342,9 +975,7 @@ void PasswordFormManager::WipeStoreCopyIfOutdated() {
void PasswordFormManager::SaveGenerationFieldDetectedByClassifier(
const base::string16& generation_field) {
- form_classifier_outcome_ =
- generation_field.empty() ? kNoGenerationElement : kFoundGenerationElement;
- generation_element_detected_by_classifier_ = generation_field;
+ votes_uploader_.SaveGenerationFieldDetectedByClassifier(generation_field);
}
void PasswordFormManager::ResetStoredMatches() {
@@ -1396,116 +1027,21 @@ std::unique_ptr<PasswordFormManager> PasswordFormManager::Clone() {
// by the cloned FormFetcher.
if (submitted_form_)
result->submitted_form_ = std::make_unique<PasswordForm>(*submitted_form_);
- if (username_correction_vote_) {
- result->username_correction_vote_ =
- std::make_unique<PasswordForm>(*username_correction_vote_);
- }
+
result->pending_credentials_ = pending_credentials_;
result->is_new_login_ = is_new_login_;
result->has_generated_password_ = has_generated_password_;
- result->generated_password_changed_ = generated_password_changed_;
- result->is_manual_generation_ = is_manual_generation_;
- result->generation_element_ = generation_element_;
- result->generation_popup_was_shown_ = generation_popup_was_shown_;
- result->form_classifier_outcome_ = form_classifier_outcome_;
- result->generation_element_detected_by_classifier_ =
- generation_element_detected_by_classifier_;
result->password_overridden_ = password_overridden_;
result->retry_password_form_password_update_ =
retry_password_form_password_update_;
result->is_possible_change_password_form_without_username_ =
is_possible_change_password_form_without_username_;
result->user_action_ = user_action_;
+ result->votes_uploader_ = votes_uploader_;
return result;
}
-void PasswordFormManager::SendVotesOnSave() {
- if (observed_form_.IsPossibleChangePasswordFormWithoutUsername())
- return;
-
- // Send votes for sign-in form.
- autofill::FormData& form_data = pending_credentials_.form_data;
- if (form_data.fields.size() == 2 &&
- form_data.fields[0].form_control_type == "text" &&
- form_data.fields[1].form_control_type == "password") {
- // |form_data| is received from the renderer and does not contain field
- // values. Fill username field value with username to allow AutofillManager
- // to detect username autofill type.
- form_data.fields[0].value = pending_credentials_.username_value;
- SendSignInVote(form_data);
- }
-
- if (pending_credentials_.times_used == 1 ||
- IsAddingUsernameToExistingMatch(pending_credentials_, best_matches_)) {
- UploadFirstLoginVotes(*submitted_form_);
- }
-
- // Upload credentials the first time they are saved. This data is used
- // by password generation to help determine account creation sites.
- // Credentials that have been previously used (e.g., PSL matches) are checked
- // to see if they are valid account creation forms.
- if (pending_credentials_.times_used == 0) {
- UploadPasswordVote(pending_credentials_, autofill::PASSWORD, std::string());
- if (username_correction_vote_) {
- UploadPasswordVote(
- *username_correction_vote_, autofill::USERNAME,
- FormStructure(observed_form_.form_data).FormSignatureAsStr());
- }
- } else {
- SendVoteOnCredentialsReuse(observed_form_, &pending_credentials_);
- }
-}
-
-void PasswordFormManager::SendSignInVote(const FormData& form_data) {
- autofill::AutofillManager* autofill_manager =
- client_->GetAutofillManagerForMainFrame();
- if (!autofill_manager)
- return;
- std::unique_ptr<FormStructure> form_structure(new FormStructure(form_data));
- form_structure->set_is_signin_upload(true);
- DCHECK(form_structure->ShouldBeUploaded());
- DCHECK_EQ(2u, form_structure->field_count());
- form_structure->field(1)->set_possible_types({autofill::PASSWORD});
- autofill_manager->MaybeStartVoteUploadProcess(std::move(form_structure),
- base::TimeTicks::Now(),
- /*observed_submission=*/true);
-}
-
-void PasswordFormManager::GeneratePasswordAttributesVote(
- const base::string16& password_value,
- FormStructure* form_structure) {
- // Select a password attribute to upload. Do upload symbols more often as
- // 2/3rd of issues are because of missing special symbols.
- int bucket = base::RandGenerator(9);
- int (*predicate)(int c) = nullptr;
- autofill::PasswordAttribute attribute =
- autofill::PasswordAttribute::kHasSpecialSymbol;
- if (bucket == 0) {
- predicate = &islower;
- attribute = autofill::PasswordAttribute::kHasLowercaseLetter;
- } else if (bucket == 1) {
- predicate = &isupper;
- attribute = autofill::PasswordAttribute::kHasUppercaseLetter;
- } else if (bucket == 2) {
- predicate = &isdigit;
- attribute = autofill::PasswordAttribute::kHasNumeric;
- } else { // 3 <= bucket < 9
- predicate = &ispunct;
- attribute = autofill::PasswordAttribute::kHasSpecialSymbol;
- }
- bool actual_value =
- std::any_of(password_value.begin(), password_value.end(), predicate);
-
- // Apply the randomized response technique to noisify the actual value
- // (https://en.wikipedia.org/wiki/Randomized_response).
- bool randomized_value =
- base::RandGenerator(2) ? actual_value : base::RandGenerator(2);
-
- form_structure->set_password_attributes_vote(
- std::make_pair(attribute, randomized_value));
-}
-
void PasswordFormManager::SetUserAction(UserAction user_action) {
user_action_ = user_action;
metrics_recorder_->SetUserAction(user_action);
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 e5076b9c1a3..547c46271b1 100644
--- a/chromium/components/password_manager/core/browser/password_form_manager.h
+++ b/chromium/components/password_manager/core/browser/password_form_manager.h
@@ -28,6 +28,7 @@
#include "components/password_manager/core/browser/password_manager_driver.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_store.h"
+#include "components/password_manager/core/browser/votes_uploader.h"
using autofill::FormData;
using autofill::FormStructure;
@@ -38,12 +39,6 @@ class FormSaver;
class PasswordManager;
class PasswordManagerClient;
-// A map from field names to field types.
-using FieldTypeMap = std::map<base::string16, autofill::ServerFieldType>;
-// A map from field names to field vote types.
-using VoteTypeMap =
- std::map<base::string16, autofill::AutofillUploadContents::Field::VoteType>;
-
// This class helps with filling the observed form (both HTML and from HTTP
// auth) and with saving/updating the stored information about it.
class PasswordFormManager : public PasswordFormManagerForUI,
@@ -156,28 +151,25 @@ class PasswordFormManager : public PasswordFormManagerForUI,
// These functions are used to determine if this form has generated password
// changed by user.
bool generated_password_changed() const {
- return generated_password_changed_;
- }
- void set_generated_password_changed(bool generated_password_changed) {
- generated_password_changed_ = generated_password_changed;
+ return votes_uploader_.generated_password_changed();
}
+ void SetGeneratedPasswordChanged(bool generated_password_changed);
- bool is_manual_generation() { return is_manual_generation_; }
- void set_is_manual_generation(bool is_manual_generation) {
- is_manual_generation_ = is_manual_generation;
+ bool is_manual_generation() const {
+ return votes_uploader_.is_manual_generation();
+ }
+ const base::string16& generation_element() const {
+ return votes_uploader_.get_generation_element();
}
-
- const base::string16& generation_element() { return generation_element_; }
void set_generation_element(const base::string16& generation_element) {
- generation_element_ = generation_element;
+ votes_uploader_.set_generation_element(generation_element);
}
bool get_generation_popup_was_shown() const {
- return generation_popup_was_shown_;
- }
- void set_generation_popup_was_shown(bool generation_popup_was_shown) {
- generation_popup_was_shown_ = generation_popup_was_shown;
+ return votes_uploader_.get_generation_popup_was_shown();
}
+ void SetGenerationPopupWasShown(bool generation_popup_was_shown,
+ bool is_manual_generation);
bool retry_password_form_password_update() const {
return retry_password_form_password_update_;
@@ -211,11 +203,6 @@ class PasswordFormManager : public PasswordFormManagerForUI,
void SaveGenerationFieldDetectedByClassifier(
const base::string16& generation_field);
- // Generates a password attributes vote based on |password_value| and saves it
- // to |form_structure|. Declared as public for testing.
- void GeneratePasswordAttributesVote(const base::string16& password_value,
- FormStructure* form_structure);
-
FormSaver* form_saver() { return form_saver_.get(); }
// Clears references to matches derived from the associated FormFetcher data.
@@ -268,13 +255,6 @@ class PasswordFormManager : public PasswordFormManagerForUI,
size_t filtered_count) override;
private:
- // The outcome of the form classifier.
- enum FormClassifierOutcome {
- kNoOutcome,
- kNoGenerationElement,
- kFoundGenerationElement
- };
-
// Through |driver|, supply the associated frame with appropriate information
// (fill data, whether to allow password generation, etc.).
void ProcessFrameInternal(const base::WeakPtr<PasswordManagerDriver>& driver);
@@ -296,14 +276,6 @@ class PasswordFormManager : public PasswordFormManagerForUI,
// triggers some UMA reporting.
void ProcessUpdate();
- // Check to see if |pending| corresponds to an account creation form. If we
- // think that it does, we label it as such and upload this state to the
- // Autofill server to vote for the correct username field, and also so that
- // we will trigger password generation in the future. This function will
- // update generation_upload_status of |pending| if an upload is performed.
- void SendVoteOnCredentialsReuse(const autofill::PasswordForm& observed,
- autofill::PasswordForm* pending);
-
// Update all login matches to reflect new preferred state - preferred flag
// will be reset on all matched logins that different than the current
// |pending_credentials_|.
@@ -315,46 +287,12 @@ class PasswordFormManager : public PasswordFormManagerForUI,
bool UpdatePendingCredentialsIfOtherPossibleUsername(
const base::string16& username);
- // Searches for |username| in |other_possible_usernames| of |match|. If the
- // username value is found, the match is saved to |username_correction_vote_|
- // and the function returns true.
- bool FindUsernameInOtherPossibleUsernames(const autofill::PasswordForm& match,
- const base::string16& username);
-
- // Searches for |username| in |other_possible_usernames| of |best_matches_|
- // and |not_best_matches_|. If the username value is found in
- // |other_possible_usernames| and the password value of the match is equal to
- // |password|, the match is saved to |username_correction_vote_| and the
- // method returns true.
- bool FindCorrectedUsernameElement(const base::string16& username,
- const base::string16& password);
-
// Returns true if |form| is a username update of a credential already in
// |best_matches_|. Sets |pending_credentials_| to the appropriate
// PasswordForm if it returns true.
bool UpdatePendingCredentialsIfUsernameChanged(
const autofill::PasswordForm& form);
- // Tries to set all votes (e.g. autofill field types, generation vote) to
- // a |FormStructure| and upload it to the server. Returns true on success.
- bool UploadPasswordVote(const autofill::PasswordForm& form_to_upload,
- const autofill::ServerFieldType& password_type,
- const std::string& login_form_signature);
-
- // Adds a vote on password generation usage to |form_structure|.
- void AddGeneratedVote(autofill::FormStructure* form_structure);
-
- // Adds a vote from HTML parsing based form classifier to |form_structure|.
- void AddFormClassifierVote(autofill::FormStructure* form_structure);
-
- // Sets the known-value flag for each field, indicating that the field
- // contained a previously stored credential on submission.
- void SetKnownValueFlag(autofill::FormStructure* form_to_upload);
-
- // Sends USERNAME and PASSWORD votes, when a credential is used to login for
- // the first time. |form_to_upload| is the submitted login form.
- void UploadFirstLoginVotes(const autofill::PasswordForm& form_to_upload);
-
// Create pending credentials from provisionally saved form and forms received
// from password store.
void CreatePendingCredentials();
@@ -383,12 +321,6 @@ class PasswordFormManager : public PasswordFormManagerForUI,
const autofill::PasswordForm* FindBestSavedMatch(
const autofill::PasswordForm* form) const;
- // Send appropriate votes based on what is currently being saved.
- void SendVotesOnSave();
-
- // Send a vote for sign-in forms with autofill types for a username field.
- void SendSignInVote(const FormData& form_data);
-
// Sets |user_action_| and records some metrics.
void SetUserAction(UserAction user_action);
@@ -402,6 +334,11 @@ class PasswordFormManager : public PasswordFormManagerForUI,
base::Optional<autofill::PasswordForm> UpdatePendingAndGetOldKey(
std::vector<autofill::PasswordForm>* credentials_to_update);
+ void SetPasswordOverridden(bool password_overridden) {
+ password_overridden_ = password_overridden;
+ votes_uploader_.set_password_overridden(password_overridden);
+ }
+
// Set of nonblacklisted PasswordForms from the DB that best match the form
// being managed by |this|, indexed by username. This means the best
// PasswordForm for each username is stored in this map. The PasswordForms are
@@ -436,14 +373,6 @@ class PasswordFormManager : public PasswordFormManagerForUI,
// Stores a submitted form.
std::unique_ptr<const autofill::PasswordForm> submitted_form_;
- // If the user typed username that doesn't match any saved credentials, but
- // matches an entry from |other_possible_usernames| of a saved credential,
- // then |username_correction_vote_| stores the credential with matched
- // username. The matched credential is copied to |username_correction_vote_|,
- // but |username_correction_vote_.username_element| is set to the name of the
- // field where matched username was found.
- std::unique_ptr<autofill::PasswordForm> username_correction_vote_;
-
// Stores updated credentials when the form was submitted but success is still
// unknown. This variable contains credentials that are ready to be written
// (saved or updated) to a password store. It is calculated based on
@@ -457,25 +386,6 @@ class PasswordFormManager : public PasswordFormManagerForUI,
// Whether this form has an auto generated password.
bool has_generated_password_;
- // Whether this form has a generated password changed by user.
- bool generated_password_changed_;
-
- // Whether password generation was manually triggered.
- bool is_manual_generation_;
-
- // A password field name that is used for generation.
- base::string16 generation_element_;
-
- // Whether generation popup was shown at least once.
- bool generation_popup_was_shown_;
-
- // The outcome of HTML parsing based form classifier.
- FormClassifierOutcome form_classifier_outcome_;
-
- // If |form_classifier_outcome_| == kFoundGenerationElement, the field
- // contains the name of the detected generation element.
- base::string16 generation_element_detected_by_classifier_;
-
// Whether the saved password was overridden.
bool password_overridden_;
@@ -525,6 +435,8 @@ class PasswordFormManager : public PasswordFormManagerForUI,
// FormFetcher instance which owns the login data from PasswordStore.
FormFetcher* form_fetcher_;
+ VotesUploader votes_uploader_;
+
// True if the main frame's visible URL, at the time this PasswordFormManager
// was created, is secure.
bool is_main_frame_secure_ = false;
@@ -533,19 +445,12 @@ class PasswordFormManager : public PasswordFormManagerForUI,
// Make sure to call Init before using |*this|, to ensure it is not null.
scoped_refptr<PasswordFormMetricsRecorder> metrics_recorder_;
- // True iff a user edited the username value in a prompt and new username is
- // the value of another field of the observed form.
- bool has_username_edited_vote_ = false;
-
// If Chrome has already autofilled a few times, it is probable that autofill
// is triggered by programmatic changes in the page. We set a maximum number
// of times that Chrome will autofill to avoid being stuck in an infinite
// loop.
int autofills_left_ = kMaxTimesAutofill;
- // Whether the password values have been shown to the user on the save prompt.
- bool has_passwords_revealed_vote_ = false;
-
DISALLOW_COPY_AND_ASSIGN(PasswordFormManager);
};
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 d618926770b..4f1dde27b6d 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
@@ -13,13 +13,11 @@
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/metrics_hashes.h"
-#include "base/optional.h"
-#include "base/rand_util.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
-#include "base/test/user_action_tester.h"
#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/proto/server.pb.h"
@@ -39,6 +37,7 @@
#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"
+#include "components/password_manager/core/browser/vote_uploads_test_matchers.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_simple.h"
@@ -80,9 +79,6 @@ namespace password_manager {
namespace {
-constexpr int kNumberOfPasswordAttributes =
- static_cast<int>(autofill::PasswordAttribute::kPasswordAttributesCount);
-
// Enum that describes what button the user pressed on the save prompt.
enum SavePromptInteraction { SAVE, NEVER, NO_INTERACTION };
@@ -164,158 +160,6 @@ MATCHER_P(UsernamePtrIs, username_value, "") {
return true;
}
-// Matches a FormStructure if its signature is the same as that of the
-// PasswordForm |form|.
-MATCHER_P(SignatureIsSameAs,
- form,
- std::string(negation ? "signature isn't " : "signature is ") +
- autofill::FormStructure(form.form_data).FormSignatureAsStr()) {
- if (autofill::FormStructure(form.form_data).FormSignatureAsStr() ==
- arg.FormSignatureAsStr())
- return true;
-
- *result_listener << "signature is " << arg.FormSignatureAsStr() << " instead";
- return false;
-}
-
-MATCHER_P(UploadedAutofillTypesAre, expected_types, "") {
- size_t fields_matched_type_count = 0;
- bool conflict_found = false;
- for (const auto& field : arg) {
- fields_matched_type_count +=
- expected_types.find(field->name) == expected_types.end() ? 0 : 1;
- if (field->possible_types().size() > 1) {
- *result_listener << (conflict_found ? ", " : "") << "Field "
- << field->name << ": has several possible types";
- conflict_found = true;
- }
-
- autofill::ServerFieldType expected_vote =
- expected_types.find(field->name) == expected_types.end()
- ? autofill::NO_SERVER_DATA
- : expected_types.find(field->name)->second;
- autofill::ServerFieldType actual_vote =
- field->possible_types().empty() ? autofill::NO_SERVER_DATA
- : *field->possible_types().begin();
- if (expected_vote != actual_vote) {
- *result_listener << (conflict_found ? ", " : "") << "Field "
- << field->name << ": expected vote " << expected_vote
- << " but found " << actual_vote;
- conflict_found = true;
- }
- }
- if (expected_types.size() != fields_matched_type_count) {
- *result_listener << (conflict_found ? ", " : "")
- << "Some types were expected but not found in the vote";
- return false;
- }
-
- return !conflict_found;
-}
-
-MATCHER_P(HasGenerationVote, expect_generation_vote, "") {
- bool found_generation_vote = false;
- for (const auto& field : arg) {
- if (field->generation_type() !=
- autofill::AutofillUploadContents::Field::NO_GENERATION) {
- found_generation_vote = true;
- break;
- }
- }
- return found_generation_vote == expect_generation_vote;
-}
-
-// Matches if all fields with a vote type are described in |expected_vote_types|
-// and all votes from |expected_vote_types| are found in a field.
-MATCHER_P(VoteTypesAre, expected_vote_types, "") {
- size_t matched_count = 0;
- bool conflict_found = false;
- for (const auto& field : arg) {
- auto expectation = expected_vote_types.find(field->name);
- if (expectation == expected_vote_types.end()) {
- if (field->vote_type() !=
- autofill::AutofillUploadContents::Field::NO_INFORMATION) {
- *result_listener << (conflict_found ? ", " : "") << "field "
- << field->name << ": unexpected vote type "
- << field->vote_type();
- conflict_found = true;
- }
- continue;
- }
-
- matched_count++;
- if (expectation->second != field->vote_type()) {
- *result_listener << (conflict_found ? ", " : "") << "field "
- << field->name << ": expected vote type "
- << expectation->second << " but has "
- << field->vote_type();
- conflict_found = true;
- }
- }
- if (expected_vote_types.size() != matched_count) {
- *result_listener
- << (conflict_found ? ", " : "")
- << "some vote types were expected but not found in the vote";
- conflict_found = true;
- }
-
- return !conflict_found;
-}
-
-MATCHER_P2(UploadedGenerationTypesAre,
- expected_generation_types,
- generated_password_changed,
- "") {
- for (const auto& field : arg) {
- if (expected_generation_types.find(field->name) ==
- expected_generation_types.end()) {
- if (field->generation_type() !=
- autofill::AutofillUploadContents::Field::NO_GENERATION) {
- // Unexpected generation type.
- *result_listener << "Expected no generation type for the field "
- << field->name << ", but found "
- << field->generation_type();
- return false;
- }
- } else {
- if (expected_generation_types.find(field->name)->second !=
- field->generation_type()) {
- // Wrong generation type.
- *result_listener << "Expected generation type for the field "
- << field->name << " is "
- << expected_generation_types.find(field->name)->second
- << ", but found " << field->generation_type();
- return false;
- }
-
- if (field->generation_type() !=
- autofill::AutofillUploadContents::Field::IGNORED_GENERATION_POPUP) {
- if (generated_password_changed != field->generated_password_changed())
- return false;
- }
- }
- }
- return true;
-}
-
-MATCHER_P2(UploadedFormClassifierVoteIs,
- found_generation_element,
- generation_element,
- "") {
- for (const auto& field : arg) {
- if (found_generation_element && field->name == generation_element) {
- if (field->form_classifier_outcome() !=
- autofill::AutofillUploadContents::Field::GENERATION_ELEMENT)
- return false;
- } else {
- if (field->form_classifier_outcome() !=
- autofill::AutofillUploadContents::Field::NON_GENERATION_ELEMENT)
- return false;
- }
- }
- return true;
-}
-
MATCHER_P(PasswordsWereRevealed, revealed, "") {
return arg.passwords_were_revealed() == revealed;
}
@@ -324,6 +168,11 @@ MATCHER_P(HasPasswordAttributesVote, is_vote_expected, "") {
base::Optional<std::pair<autofill::PasswordAttribute, bool>> vote =
arg.get_password_attributes_vote_for_testing();
EXPECT_EQ(is_vote_expected, vote.has_value());
+ if (vote.has_value()) {
+ size_t reported_length = arg.get_password_length_vote_for_testing();
+ EXPECT_LT(0u, reported_length);
+ EXPECT_LE(reported_length, 5u /* actual password length */);
+ }
return true;
}
@@ -429,7 +278,6 @@ class MockPasswordManagerDriver : public StubPasswordManagerDriver {
void(const autofill::PasswordFormFillData&));
MOCK_METHOD1(AllowPasswordGenerationForForm,
void(const autofill::PasswordForm&));
- MOCK_METHOD0(MatchingBlacklistedFormFound, void());
MockAutofillManager* mock_autofill_manager() {
return &mock_autofill_manager_;
@@ -598,7 +446,8 @@ class PasswordFormManagerTest : public testing::Test {
// Show the password generation popup to check that the generation vote
// would be ignored.
form_manager.set_generation_element(saved_match()->password_element);
- form_manager.set_generation_popup_was_shown(true);
+ form_manager.SetGenerationPopupWasShown(/*shown=*/true,
+ /*manually_triggered*/ true);
expect_generation_vote =
*field_type != autofill::ACCOUNT_CREATION_PASSWORD;
@@ -831,15 +680,14 @@ class PasswordFormManagerTest : public testing::Test {
if (interaction == SAVE)
expected_available_field_types.insert(autofill::PASSWORD);
- form_manager.set_is_manual_generation(is_manual_generation);
base::string16 generation_element = is_change_password_form
? form.new_password_element
: form.password_element;
form_manager.set_generation_element(generation_element);
- form_manager.set_generation_popup_was_shown(true);
+ form_manager.SetGenerationPopupWasShown(true, is_manual_generation);
form_manager.SetHasGeneratedPassword(has_generated_password);
if (has_generated_password)
- form_manager.set_generated_password_changed(generated_password_changed);
+ form_manager.SetGeneratedPasswordChanged(generated_password_changed);
// Figure out expected generation event type.
autofill::AutofillUploadContents::Field::PasswordGenerationType
@@ -876,6 +724,137 @@ class PasswordFormManagerTest : public testing::Test {
histogram_tester.ExpectUniqueSample(
"PasswordGeneration.GeneratedPasswordWasEdited",
generated_password_changed /* sample */, 1);
+ histogram_tester.ExpectUniqueSample(
+ "PasswordGeneration.IsTriggeredManually",
+ is_manual_generation /* sample */, 1);
+ }
+ Mock::VerifyAndClearExpectations(
+ client()->mock_driver()->mock_autofill_download_manager());
+ }
+
+ void GeneratedPasswordUkmTest(bool is_manual_generation,
+ bool is_change_password_form,
+ bool has_generated_password,
+ bool generated_password_changed,
+ SavePromptInteraction interaction) {
+ SCOPED_TRACE(testing::Message()
+ << "is_manual_generation=" << is_manual_generation
+ << " is_change_password_form=" << is_change_password_form
+ << " has_generated_password=" << has_generated_password
+ << " generated_password_changed=" << generated_password_changed
+ << " interaction=" << interaction);
+
+ ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+ test_ukm_recorder.UpdateSourceURL(client()->GetUkmSourceId(),
+ client()->GetMainFrameURL());
+
+ PasswordForm form(*observed_form());
+ form.form_data = saved_match()->form_data;
+
+ if (is_change_password_form) {
+ // Turn |form| to a change password form.
+ form.new_password_element = ASCIIToUTF16("NewPasswd");
+
+ autofill::FormFieldData field;
+ field.label = ASCIIToUTF16("password");
+ field.name = ASCIIToUTF16("NewPasswd");
+ field.form_control_type = "password";
+ form.form_data.fields.push_back(field);
+ }
+
+ // Create submitted form.
+ PasswordForm submitted_form(form);
+ submitted_form.preferred = true;
+ submitted_form.username_value = saved_match()->username_value;
+ submitted_form.password_value = saved_match()->password_value;
+
+ if (is_change_password_form) {
+ submitted_form.new_password_value =
+ saved_match()->password_value + ASCIIToUTF16("1");
+ }
+
+ FakeFormFetcher fetcher;
+ fetcher.Fetch();
+ auto metrics_recorder = base::MakeRefCounted<PasswordFormMetricsRecorder>(
+ form.origin.SchemeIsCryptographic(), client()->GetUkmSourceId());
+ auto form_manager = std::make_unique<PasswordFormManager>(
+ password_manager(), client(), client()->driver(), form,
+ std::make_unique<NiceMock<MockFormSaver>>(), &fetcher);
+ // *Move* the metrics recorder to not hold on to a reference.
+ form_manager->Init(std::move(metrics_recorder));
+ fetcher.SetNonFederated(std::vector<const PasswordForm*>(), 0u);
+
+ autofill::ServerFieldTypeSet expected_available_field_types;
+ // Don't send autofill votes if the user didn't press "Save" button.
+ if (interaction == SAVE)
+ expected_available_field_types.insert(autofill::PASSWORD);
+
+ base::string16 generation_element = is_change_password_form
+ ? form.new_password_element
+ : form.password_element;
+ form_manager->set_generation_element(generation_element);
+ form_manager->SetGenerationPopupWasShown(true, is_manual_generation);
+ form_manager->SetHasGeneratedPassword(has_generated_password);
+ if (has_generated_password)
+ form_manager->SetGeneratedPasswordChanged(generated_password_changed);
+
+ // Figure out expected generation event type.
+ autofill::AutofillUploadContents::Field::PasswordGenerationType
+ expected_generation_type = GetExpectedPasswordGenerationType(
+ is_manual_generation, is_change_password_form,
+ has_generated_password);
+ std::map<base::string16,
+ autofill::AutofillUploadContents::Field::PasswordGenerationType>
+ expected_generation_types;
+ expected_generation_types[generation_element] = expected_generation_type;
+
+ EXPECT_CALL(
+ *client()->mock_driver()->mock_autofill_download_manager(),
+ StartUploadRequest(
+ AllOf(SignatureIsSameAs(submitted_form),
+ UploadedGenerationTypesAre(expected_generation_types,
+ generated_password_changed)),
+ false, expected_available_field_types, std::string(), true));
+ form_manager->ProvisionallySave(submitted_form);
+ switch (interaction) {
+ case SAVE:
+ form_manager->Save();
+ break;
+ case NEVER:
+ form_manager->OnNeverClicked();
+ break;
+ case NO_INTERACTION:
+ form_manager->OnNoInteraction(false /* not an update prompt*/);
+ break;
+ }
+ // Reset form manager to flush UKM metrics.
+ form_manager.reset();
+
+ auto entries = test_ukm_recorder.GetEntriesByName(
+ ukm::builders::PasswordForm::kEntryName);
+ ASSERT_EQ(1u, entries.size());
+
+ ukm::TestUkmRecorder::ExpectEntryMetric(
+ entries[0], ukm::builders::PasswordForm::kGeneration_PopupShownName,
+ is_manual_generation
+ ? static_cast<int64_t>(
+ password_manager::PasswordFormMetricsRecorder::
+ PasswordGenerationPopupShown::kShownManually)
+ : static_cast<int64_t>(
+ password_manager::PasswordFormMetricsRecorder::
+ PasswordGenerationPopupShown::kShownAutomatically));
+
+ ukm::TestUkmRecorder::ExpectEntryMetric(
+ entries[0],
+ ukm::builders::PasswordForm::kGeneration_GeneratedPasswordName,
+ has_generated_password ? 1 : 0);
+
+ if (has_generated_password) {
+ ukm::TestUkmRecorder::ExpectEntryMetric(
+ entries[0],
+ ukm::builders::PasswordForm::
+ kGeneration_GeneratedPasswordModifiedName,
+ generated_password_changed ? 1 : 0);
}
Mock::VerifyAndClearExpectations(
client()->mock_driver()->mock_autofill_download_manager());
@@ -1493,15 +1472,6 @@ TEST_F(PasswordFormManagerTest,
fetcher.SetNonFederated({&simulated_result}, 0u);
}
-TEST_F(PasswordFormManagerTest,
- TestSendMachedBlacklistedFoundMessage_BlacklistedCredentials) {
- // Signing up on a previously visited site. Credentials are found in the
- // password store, but they are blacklisted.
- EXPECT_CALL(*client()->mock_driver(), MatchingBlacklistedFormFound());
- PasswordForm simulated_result = CreateSavedMatch(true);
- fake_form_fetcher()->SetNonFederated({&simulated_result}, 0u);
-}
-
// Test that exactly one match for each username is chosen as a best match, even
// though it is a PSL match.
TEST_F(PasswordFormManagerTest, TestBestCredentialsForEachUsernameAreIncluded) {
@@ -2816,6 +2786,52 @@ TEST_F(PasswordFormManagerTest, GenerationStatusNotUpdatedIfPasswordUnchanged) {
EXPECT_EQ(PasswordForm::TYPE_GENERATED, new_credentials.type);
histogram_tester.ExpectBucketCount("PasswordGeneration.SubmissionEvent",
metrics_util::PASSWORD_USED, 1);
+
+ // On the second reuse, the metric is not reported.
+ generated_form.times_used = 1;
+ fake_form_fetcher()->SetNonFederated({&generated_form}, 0u);
+ form_manager()->ProvisionallySave(submitted_form);
+ EXPECT_CALL(MockFormSaver::Get(form_manager()), Update(_, _, _, _));
+ form_manager()->Save();
+ histogram_tester.ExpectBucketCount("PasswordGeneration.SubmissionEvent",
+ metrics_util::PASSWORD_USED, 1);
+}
+
+TEST_F(PasswordFormManagerTest, GeneratedPasswordIsOverridden) {
+ base::HistogramTester histogram_tester;
+
+ PasswordForm generated_form = *observed_form();
+ generated_form.type = PasswordForm::TYPE_GENERATED;
+ generated_form.username_value = ASCIIToUTF16("username");
+ generated_form.password_value = ASCIIToUTF16("password2");
+ generated_form.preferred = true;
+
+ PasswordForm submitted_form(generated_form);
+ submitted_form.password_value = ASCIIToUTF16("another_password");
+
+ fake_form_fetcher()->SetNonFederated({&generated_form}, 0u);
+
+ form_manager()->ProvisionallySave(submitted_form);
+
+ PasswordForm new_credentials;
+ EXPECT_CALL(MockFormSaver::Get(form_manager()), Update(_, _, _, nullptr))
+ .WillOnce(testing::SaveArg<0>(&new_credentials));
+ form_manager()->Save();
+
+ EXPECT_EQ(PasswordForm::TYPE_MANUAL, new_credentials.type);
+ EXPECT_EQ(ASCIIToUTF16("another_password"), new_credentials.password_value);
+ histogram_tester.ExpectBucketCount("PasswordGeneration.SubmissionEvent",
+ metrics_util::PASSWORD_OVERRIDDEN, 1);
+
+ // On the reuse of the overriden password, the metric is not reported.
+ fake_form_fetcher()->SetNonFederated({&new_credentials}, 0u);
+ form_manager()->ProvisionallySave(new_credentials);
+ EXPECT_CALL(MockFormSaver::Get(form_manager()), Update(_, _, _, _));
+ form_manager()->Save();
+ histogram_tester.ExpectBucketCount("PasswordGeneration.SubmissionEvent",
+ metrics_util::PASSWORD_OVERRIDDEN, 1);
+ histogram_tester.ExpectBucketCount("PasswordGeneration.SubmissionEvent",
+ metrics_util::PASSWORD_USED, 0);
}
// Test that ProcessFrame is called on receiving matches from the fetcher,
@@ -3268,6 +3284,26 @@ TEST_F(PasswordFormManagerTest, GeneratedVoteUpload) {
}
}
+TEST_F(PasswordFormManagerTest, GeneratedPasswordUkm) {
+ bool kFalseTrue[] = {false, true};
+ SavePromptInteraction kSavePromptInterations[] = {SAVE, NEVER,
+ NO_INTERACTION};
+ for (bool is_manual_generation : kFalseTrue) {
+ for (bool is_change_password_form : kFalseTrue) {
+ for (bool has_generated_password : kFalseTrue) {
+ for (bool generated_password_changed : kFalseTrue) {
+ for (SavePromptInteraction interaction : kSavePromptInterations) {
+ GeneratedPasswordUkmTest(is_manual_generation,
+ is_change_password_form,
+ has_generated_password,
+ generated_password_changed, interaction);
+ }
+ }
+ }
+ }
+ }
+}
+
TEST_F(PasswordFormManagerTest, PresaveGeneratedPasswordAndRemoveIt) {
PasswordForm credentials = *observed_form();
@@ -4363,27 +4399,17 @@ TEST_F(PasswordFormManagerTest, TestUkmContextMetrics) {
}
TEST_F(PasswordFormManagerTest,
- TestSendNotBlacklistedMessage_BlacklistedCredentials) {
+ TestNotSendNotBlacklistedMessage_BlacklistedCredentials) {
// Signing up on a previously visited site. Credentials are found in the
// password store, but they are blacklisted. AllowPasswordGenerationForForm
- // is still called.
- EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_));
- PasswordForm simulated_result = CreateSavedMatch(true);
- fake_form_fetcher()->SetNonFederated({&simulated_result}, 0u);
-}
-
-TEST_F(PasswordFormManagerTest,
- TestSendMachedBlacklistedFoundMessage_Credentials) {
- // Signing up on a previously visited site. Credentials are found in the
- // password store, and are not blacklisted.
- EXPECT_CALL(*client()->mock_driver(), MatchingBlacklistedFormFound())
+ // is not called.
+ EXPECT_CALL(*(client()->mock_driver()), AllowPasswordGenerationForForm(_))
.Times(0);
- PasswordForm simulated_result = CreateSavedMatch(false);
+ PasswordForm simulated_result = CreateSavedMatch(true);
fake_form_fetcher()->SetNonFederated({&simulated_result}, 0u);
}
TEST_F(PasswordFormManagerTest, FirstLoginVote) {
-
PasswordForm old_without_username = *saved_match();
old_without_username.username_value.clear();
old_without_username.username_element.clear();
@@ -4862,57 +4888,6 @@ TEST_F(PasswordFormManagerTest, FirstLoginVote_KnownValue) {
form_manager()->Save();
}
-TEST_F(PasswordFormManagerTest, GeneratePasswordAttributesVote) {
- // Checks that randomization distorts information about present and missed
- // character classess, but a true value is still restorable with aggregation
- // of many distorted reports.
- const char* kPasswordSnippets[] = {"abc", "XYZ", "123", "*-_"};
- for (int test_case = 0; test_case < 10; ++test_case) {
- bool has_password_attribute[kNumberOfPasswordAttributes];
- base::string16 password_value;
- for (int i = 0; i < kNumberOfPasswordAttributes; ++i) {
- has_password_attribute[i] = base::RandGenerator(2);
- if (has_password_attribute[i])
- password_value += ASCIIToUTF16(kPasswordSnippets[i]);
- }
- if (password_value.empty())
- continue;
-
- autofill::FormData form;
- autofill::FormStructure form_structure(form);
- int reported_false[kNumberOfPasswordAttributes] = {0, 0, 0, 0};
- int reported_true[kNumberOfPasswordAttributes] = {0, 0, 0, 0};
-
- for (int i = 0; i < 1000; ++i) {
- form_manager()->GeneratePasswordAttributesVote(password_value,
- &form_structure);
- base::Optional<std::pair<autofill::PasswordAttribute, bool>> vote =
- form_structure.get_password_attributes_vote_for_testing();
- int attribute_index = static_cast<int>(vote->first);
- if (vote->second)
- reported_true[attribute_index]++;
- else
- reported_false[attribute_index]++;
- }
- for (int i = 0; i < kNumberOfPasswordAttributes; i++) {
- EXPECT_LT(0, reported_false[i]);
- EXPECT_LT(0, reported_true[i]);
-
- // If the actual value is |true|, then it should report more |true|s than
- // |false|s.
- if (has_password_attribute[i]) {
- EXPECT_LT(reported_false[i], reported_true[i])
- << "Wrong distribution for attribute " << i
- << ". password_value = " << password_value;
- } else {
- EXPECT_GT(reported_false[i], reported_true[i])
- << "Wrong distribution for attribute " << i
- << ". password_value = " << password_value;
- }
- }
- }
-}
-
TEST_F(PasswordFormManagerTest, UploadPasswordAttributesVote) {
PasswordForm credentials = *observed_form();
// Set FormData to enable crowdsourcing.
@@ -4935,4 +4910,51 @@ TEST_F(PasswordFormManagerTest, UploadPasswordAttributesVote) {
form_manager.Save();
}
+TEST_F(PasswordFormManagerTest, PresaveGeneratedPassword_UnknownUsername) {
+ // Checks whether the generated password is presaved with the captured
+ // username. The username is new, so there will be no credentail override.
+ fake_form_fetcher()->SetNonFederated({saved_match()}, 0u);
+ PasswordForm credentials = *observed_form();
+ credentials.username_value = ASCIIToUTF16("new_user");
+ credentials.password_value = ASCIIToUTF16("generatated_password");
+
+ EXPECT_CALL(MockFormSaver::Get(form_manager()),
+ PresaveGeneratedPassword(credentials));
+ form_manager()->PresaveGeneratedPassword(credentials);
+}
+
+TEST_F(PasswordFormManagerTest, PresaveGeneratedPassword_KnownUsername) {
+ // Checks whether the generated password is presaved with empty username if
+ // there is already an entry with the captured username in the store.
+ PasswordForm saved_form_without_username(*saved_match());
+ saved_form_without_username.username_value.clear();
+ fake_form_fetcher()->SetNonFederated(
+ {saved_match(), &saved_form_without_username}, 0u);
+ PasswordForm credentials = *observed_form();
+ credentials.username_value = saved_match()->username_value;
+ credentials.password_value = ASCIIToUTF16("generatated_password");
+
+ PasswordForm credentials_without_username(credentials);
+ credentials_without_username.username_value.clear();
+ EXPECT_CALL(MockFormSaver::Get(form_manager()),
+ PresaveGeneratedPassword(credentials_without_username));
+ form_manager()->PresaveGeneratedPassword(credentials);
+}
+
+TEST_F(PasswordFormManagerTest, PresaveGeneratedPassword_EmptyUsername) {
+ // Checks whether the generated password is presaved even if the captured
+ // username is empty.
+ PasswordForm saved_form_without_username(*saved_match());
+ saved_form_without_username.username_value.clear();
+ fake_form_fetcher()->SetNonFederated(
+ {saved_match(), &saved_form_without_username}, 0u);
+ PasswordForm credentials = *observed_form();
+ credentials.username_value.clear();
+ credentials.password_value = ASCIIToUTF16("generatated_password");
+
+ EXPECT_CALL(MockFormSaver::Get(form_manager()),
+ PresaveGeneratedPassword(credentials));
+ form_manager()->PresaveGeneratedPassword(credentials);
+}
+
} // namespace password_manager
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 576f4c3dbc9..226cc8e5e24 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
@@ -128,10 +128,27 @@ PasswordFormMetricsRecorder::~PasswordFormMetricsRecorder() {
}
}
+ ukm_entry_builder_.SetGeneration_GeneratedPassword(
+ has_generated_password_ ? 1 : 0);
+ if (has_generated_password_) {
+ ukm_entry_builder_.SetGeneration_GeneratedPasswordModified(
+ has_generated_password_changed_ ? 1 : 0);
+ }
+ if (password_generation_popup_shown_ !=
+ PasswordGenerationPopupShown::kNotShown) {
+ ukm_entry_builder_.SetGeneration_PopupShown(
+ static_cast<int64_t>(password_generation_popup_shown_));
+ }
+ if (spec_priority_of_generated_password_) {
+ ukm_entry_builder_.SetGeneration_SpecPriority(
+ spec_priority_of_generated_password_.value());
+ }
+
if (showed_manual_fallback_for_saving_) {
ukm_entry_builder_.SetSaving_ShowedManualFallbackForSaving(
showed_manual_fallback_for_saving_.value());
}
+
ukm_entry_builder_.Record(ukm::UkmRecorder::Get());
}
@@ -144,6 +161,16 @@ void PasswordFormMetricsRecorder::SetHasGeneratedPassword(
has_generated_password_ = has_generated_password;
}
+void PasswordFormMetricsRecorder::SetHasGeneratedPasswordChanged(
+ bool has_generated_password_changed) {
+ has_generated_password_changed_ = has_generated_password_changed;
+}
+
+void PasswordFormMetricsRecorder::ReportSpecPriorityForGeneratedPassword(
+ uint32_t spec_priority) {
+ spec_priority_of_generated_password_ = spec_priority;
+}
+
void PasswordFormMetricsRecorder::SetManagerAction(
ManagerAction manager_action) {
manager_action_ = manager_action;
@@ -198,6 +225,17 @@ void PasswordFormMetricsRecorder::LogSubmitFailed() {
submit_result_ = kSubmitResultFailed;
}
+void PasswordFormMetricsRecorder::SetPasswordGenerationPopupShown(
+ bool generation_popup_was_shown,
+ bool is_manual_generation) {
+ password_generation_popup_shown_ =
+ generation_popup_was_shown
+ ? (is_manual_generation
+ ? PasswordGenerationPopupShown::kShownManually
+ : PasswordGenerationPopupShown::kShownAutomatically)
+ : PasswordGenerationPopupShown::kNotShown;
+}
+
void PasswordFormMetricsRecorder::SetSubmittedFormType(
SubmittedFormType form_type) {
submitted_form_type_ = form_type;
@@ -239,6 +277,12 @@ void PasswordFormMetricsRecorder::RecordFormSignature(
HashFormSignature(form_signature));
}
+void PasswordFormMetricsRecorder::RecordParsingsComparisonResult(
+ ParsingComparisonResult comparison_result) {
+ ukm_entry_builder_.SetParsingComparison(
+ static_cast<uint64_t>(comparison_result));
+}
+
void PasswordFormMetricsRecorder::RecordShowManualFallbackForSaving(
bool has_generated_password,
bool is_update) {
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 4f82cc73e1d..f0d1916a989 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
@@ -13,6 +13,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/optional.h"
#include "components/autofill/core/common/password_form.h"
#include "components/autofill/core/common/signatures_util.h"
#include "components/password_manager/core/browser/password_form_user_action.h"
@@ -157,6 +158,23 @@ class PasswordFormMetricsRecorder
kCorrectedUsernameInForm = 200,
};
+ // Old and new form parsings comparison result.
+ enum class ParsingComparisonResult {
+ kSame,
+ kDifferent,
+ // Old and new parsers use different identification mechanism for unnamed
+ // fields, so the difference in parsing of anonymous fields is expected.
+ kAnonymousFields,
+ kMax
+ };
+
+ // Indicator whether the user has seen a password generation popup and why.
+ enum class PasswordGenerationPopupShown {
+ kNotShown = 0,
+ kShownAutomatically = 1,
+ kShownManually = 2,
+ };
+
// The maximum number of combinations of the ManagerAction, UserAction and
// SubmitResult enums.
// This is used when recording the actions taken by the form in UMA.
@@ -182,6 +200,16 @@ class PasswordFormMetricsRecorder
// Stores whether the form has had its password auto generated by the browser.
void SetHasGeneratedPassword(bool has_generated_password);
+ // Stores whether the a generated password has been modified by the user.
+ void SetHasGeneratedPasswordChanged(bool has_changed_generated_password);
+
+ // Reports the priority of a PasswordGenerationRequirementsSpec for a
+ // generated password. This can be used for debugging as a 0 means that
+ // no spec was used, a 10 means that the spec came from autofill and was crowd
+ // sourced, a 20 means that it was overrideen per domain and a 30 means that
+ // is was overridden for the form.
+ void ReportSpecPriorityForGeneratedPassword(uint32_t spec_priority);
+
// Stores the password manager and user actions and logs them.
void SetManagerAction(ManagerAction manager_action);
void SetUserAction(UserAction user_action);
@@ -191,6 +219,10 @@ class PasswordFormMetricsRecorder
void LogSubmitPassed();
void LogSubmitFailed();
+ // This can be called multiple times in which case the last value is reported.
+ void SetPasswordGenerationPopupShown(bool generation_popup_was_shown,
+ bool is_manual_generation);
+
// Call this once the submitted form type has been determined.
void SetSubmittedFormType(SubmittedFormType form_type);
@@ -236,6 +268,10 @@ class PasswordFormMetricsRecorder
// distinguish two forms on the same site.
void RecordFormSignature(autofill::FormSignature form_signature);
+ // Records old and new form parsings comparison result.
+ void RecordParsingsComparisonResult(
+ ParsingComparisonResult comparison_result);
+
// Records that Chrome noticed that it should show a manual fallback for
// saving.
void RecordShowManualFallbackForSaving(bool has_generated_password,
@@ -296,6 +332,12 @@ class PasswordFormMetricsRecorder
// Whether this form has an auto generated password.
bool has_generated_password_ = false;
+ // Whether this form has an auto generated password that was modified by the
+ // user.
+ bool has_generated_password_changed_ = false;
+
+ base::Optional<uint32_t> spec_priority_of_generated_password_;
+
// Tracks which bubble is currently being displayed to the user.
CurrentBubbleOfInterest current_bubble_ = CurrentBubbleOfInterest::kNone;
@@ -305,6 +347,11 @@ class PasswordFormMetricsRecorder
// Whether the user was shown a prompt to save a new credential.
bool save_prompt_shown_ = false;
+ // Whether the user was shown a password generation popup and why.
+ // Only reportet when a popup was shown.
+ PasswordGenerationPopupShown password_generation_popup_shown_ =
+ PasswordGenerationPopupShown::kNotShown;
+
// These three fields record the "ActionsTaken" by the browser and
// the user with this form, and the result. They are combined and
// recorded in UMA when the PasswordFormMetricsRecorder is destroyed.
diff --git a/chromium/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc b/chromium/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc
index 58ccf1d0a87..05a9e8b6ec9 100644
--- a/chromium/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc
@@ -6,9 +6,9 @@
#include "base/logging.h"
#include "base/metrics/metrics_hashes.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_task_environment.h"
-#include "base/test/user_action_tester.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/ukm/test_ukm_recorder.h"
#include "components/ukm/ukm_source.h"
diff --git a/chromium/components/password_manager/core/browser/password_generation_manager.cc b/chromium/components/password_manager/core/browser/password_generation_manager.cc
index f089ae1dd64..bfd796033d6 100644
--- a/chromium/components/password_manager/core/browser/password_generation_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_generation_manager.cc
@@ -8,12 +8,15 @@
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/password_generator.h"
+#include "components/autofill/core/browser/proto/password_requirements.pb.h"
#include "components/autofill/core/common/password_form_generation_data.h"
#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/browser/password_manager_client.h"
#include "components/password_manager/core/browser/password_manager_driver.h"
#include "components/password_manager/core/browser/password_manager_util.h"
+#include "components/password_manager/core/browser/password_requirements_service.h"
using autofill::AutofillField;
using autofill::FieldSignature;
@@ -35,9 +38,43 @@ PasswordGenerationManager::PasswordGenerationManager(
PasswordGenerationManager::~PasswordGenerationManager() {
}
+void PasswordGenerationManager::ProcessPasswordRequirements(
+ const std::vector<autofill::FormStructure*>& forms) {
+ // IsGenerationEnabled is called multiple times and it is sufficient to
+ // log debug data once.
+ if (!IsGenerationEnabled(/*log_debug_data=*/false))
+ return;
+
+ // It is legit to have no PasswordRequirementsService on some platforms where
+ // it has not been implemented.
+ PasswordRequirementsService* password_requirements_service =
+ client_->GetPasswordRequirementsService();
+ if (!password_requirements_service)
+ return;
+
+ // Fetch password requirements for the domain.
+ if (IsRequirementsFetchingEnabled()) {
+ password_requirements_service->PrefetchSpec(
+ client_->GetLastCommittedEntryURL().GetOrigin());
+ }
+
+ // Store password requirements from the autofill server.
+ for (const autofill::FormStructure* form : forms) {
+ for (const auto& field : *form) {
+ if (field->password_requirements()) {
+ password_requirements_service->AddSpec(
+ form->form_signature(), field->GetFieldSignature(),
+ field->password_requirements().value());
+ }
+ }
+ }
+}
+
void PasswordGenerationManager::DetectFormsEligibleForGeneration(
const std::vector<autofill::FormStructure*>& forms) {
- if (!IsGenerationEnabled())
+ // IsGenerationEnabled is called multiple times and it is sufficient to
+ // log debug data once. This is it!
+ if (!IsGenerationEnabled(/*log_debug_data=*/true))
return;
std::vector<autofill::PasswordFormGenerationData>
@@ -70,9 +107,9 @@ void PasswordGenerationManager::DetectFormsEligibleForGeneration(
// In order for password generation to be enabled, we need to make sure:
// (1) Password sync is enabled, and
// (2) Password saving is enabled.
-bool PasswordGenerationManager::IsGenerationEnabled() const {
+bool PasswordGenerationManager::IsGenerationEnabled(bool log_debug_data) const {
std::unique_ptr<Logger> logger;
- if (password_manager_util::IsLoggingActive(client_)) {
+ if (log_debug_data && password_manager_util::IsLoggingActive(client_)) {
logger.reset(
new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
}
@@ -83,7 +120,7 @@ bool PasswordGenerationManager::IsGenerationEnabled() const {
return false;
}
- if (client_->GetPasswordSyncState() != NOT_SYNCING_PASSWORDS)
+ if (client_->GetPasswordSyncState() != NOT_SYNCING)
return true;
if (logger)
logger->LogMessage(Logger::STRING_GENERATION_DISABLED_NO_SYNC);
@@ -91,9 +128,43 @@ bool PasswordGenerationManager::IsGenerationEnabled() const {
return false;
}
+bool PasswordGenerationManager::IsRequirementsFetchingEnabled() const {
+ return client_->GetHistorySyncState() == SYNCING_NORMAL_ENCRYPTION;
+}
+
void PasswordGenerationManager::CheckIfFormClassifierShouldRun() {
if (autofill::FormStructure::IsAutofillFieldMetadataEnabled())
driver_->AllowToRunFormClassifier();
}
+base::string16 PasswordGenerationManager::GeneratePassword(
+ const GURL& last_committed_url,
+ autofill::FormSignature form_signature,
+ autofill::FieldSignature field_signature,
+ uint32_t max_length,
+ uint32_t* spec_priority) {
+ autofill::PasswordRequirementsSpec spec;
+
+ // Lookup password requirements.
+ PasswordRequirementsService* password_requirements_service =
+ client_->GetPasswordRequirementsService();
+ if (password_requirements_service) {
+ spec = password_requirements_service->GetSpec(
+ last_committed_url.GetOrigin(), form_signature, field_signature);
+ }
+
+ if (spec_priority)
+ *spec_priority = spec.priority();
+
+ // Choose the password length as the minimum of default length, what website
+ // allows, and what the autofill server suggests.
+ uint32_t target_length = autofill::kDefaultPasswordLength;
+ if (max_length && max_length < target_length)
+ target_length = max_length;
+ if (spec.has_max_length() && spec.max_length() < target_length)
+ target_length = spec.max_length();
+ spec.set_max_length(target_length);
+ return autofill::GeneratePassword(spec);
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_generation_manager.h b/chromium/components/password_manager/core/browser/password_generation_manager.h
index c683b1865ba..b1039448d06 100644
--- a/chromium/components/password_manager/core/browser/password_generation_manager.h
+++ b/chromium/components/password_manager/core/browser/password_generation_manager.h
@@ -8,6 +8,9 @@
#include <vector>
#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "components/autofill/core/common/signatures_util.h"
+#include "url/gurl.h"
namespace autofill {
class FormStructure;
@@ -37,6 +40,11 @@ class PasswordGenerationManager {
PasswordManagerDriver* driver);
virtual ~PasswordGenerationManager();
+ // Stores password requirements received from the autofill server for the
+ // |forms| and fetches domain-wide requirements.
+ void ProcessPasswordRequirements(
+ const std::vector<autofill::FormStructure*>& forms);
+
// Detect account creation forms from forms with autofill type annotated.
// Will send a message to the renderer if we find a correctly annotated form
// and the feature is enabled.
@@ -44,7 +52,13 @@ class PasswordGenerationManager {
const std::vector<autofill::FormStructure*>& forms);
// Determines current state of password generation
- bool IsGenerationEnabled() const;
+ // |log_debug_data| determines whether log entries are sent to the
+ // autofill::SavePasswordProgressLogger.
+ bool IsGenerationEnabled(bool log_debug_data) const;
+
+ // Determines whether the PasswordGeneraitonManager has the permission to
+ // fetch domain wide password requirements from gstatic.com.
+ bool IsRequirementsFetchingEnabled() const;
// Determine if the form classifier should run. If yes, sends a message to the
// renderer.
@@ -52,6 +66,25 @@ class PasswordGenerationManager {
// classifier is ready.
void CheckIfFormClassifierShouldRun();
+ // Returns a randomly generated password that should (but is not guaranteed
+ // to) match the requirements of the site.
+ // |last_committed_url| refers to the main frame URL and may impact the
+ // password generation rules that are imposed by the site.
+ // |form_signature| and |field_signature| identify the field for which a
+ // password shall be generated.
+ // |max_length| refers to the maximum allowed length according to the site and
+ // may be 0 if unset.
+ //
+ // Virtual for testing
+ //
+ // TODO(crbug.com/855595): Add a stub for this class to facilitate testing.
+ virtual base::string16 GeneratePassword(
+ const GURL& last_committed_url,
+ autofill::FormSignature form_signature,
+ autofill::FieldSignature field_signature,
+ uint32_t max_length,
+ uint32_t* spec_priority);
+
private:
friend class PasswordGenerationManagerTest;
diff --git a/chromium/components/password_manager/core/browser/password_generation_manager_unittest.cc b/chromium/components/password_manager/core/browser/password_generation_manager_unittest.cc
index 437f09755bf..61044851d60 100644
--- a/chromium/components/password_manager/core/browser/password_generation_manager_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_generation_manager_unittest.cc
@@ -10,16 +10,20 @@
#include "base/message_loop/message_loop.h"
#include "base/metrics/field_trial.h"
+#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/autofill_metrics.h"
#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/password_requirements_spec_fetcher.h"
+#include "components/autofill/core/browser/proto/password_requirements.pb.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/password_form_generation_data.h"
#include "components/autofill/core/common/signatures_util.h"
#include "components/password_manager/core/browser/password_autofill_manager.h"
#include "components/password_manager/core/browser/password_manager.h"
+#include "components/password_manager/core/browser/password_requirements_service.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
#include "components/password_manager/core/browser/stub_password_manager_driver.h"
#include "components/password_manager/core/browser/test_password_store.h"
@@ -40,6 +44,13 @@ namespace password_manager {
namespace {
+// Magic constants.
+// Host (suffix) of a server for which no domain wide requirements are given.
+constexpr char kNoServerResponse[] = "www.no_server_response.com";
+// Host (suffix) of a server for which the autofill requirements are overriden
+// via a domain wide spec.
+constexpr char kHasServerResponse[] = "www.has_server_response.com";
+
class TestPasswordManagerDriver : public StubPasswordManagerDriver {
public:
explicit TestPasswordManagerDriver(PasswordManagerClient* client)
@@ -69,7 +80,6 @@ class TestPasswordManagerDriver : public StubPasswordManagerDriver {
}
MOCK_METHOD0(AllowToRunFormClassifier, void());
- MOCK_METHOD0(MatchingBlacklistedFormFound, void());
private:
PasswordManager password_manager_;
@@ -79,21 +89,67 @@ class TestPasswordManagerDriver : public StubPasswordManagerDriver {
found_forms_eligible_for_generation_;
};
+autofill::PasswordRequirementsSpec GetDomainWideRequirements() {
+ autofill::PasswordRequirementsSpec spec;
+ spec.set_max_length(7);
+ spec.set_priority(20);
+ return spec;
+}
+
+autofill::PasswordRequirementsSpec GetFieldRequirements() {
+ autofill::PasswordRequirementsSpec spec;
+ spec.set_max_length(8);
+ spec.set_priority(10);
+ return spec;
+}
+
+class FakePasswordRequirementsSpecFetcher
+ : public autofill::PasswordRequirementsSpecFetcher {
+ public:
+ using FetchCallback =
+ autofill::PasswordRequirementsSpecFetcher::FetchCallback;
+
+ FakePasswordRequirementsSpecFetcher() = default;
+ ~FakePasswordRequirementsSpecFetcher() override = default;
+
+ void Fetch(GURL origin, FetchCallback callback) override {
+ if (origin.GetOrigin().host_piece().find(kNoServerResponse) !=
+ std::string::npos) {
+ std::move(callback).Run(autofill::PasswordRequirementsSpec());
+ } else if (origin.GetOrigin().host_piece().find(kHasServerResponse) !=
+ std::string::npos) {
+ std::move(callback).Run(GetDomainWideRequirements());
+ } else {
+ NOTREACHED();
+ }
+ }
+};
+
class MockPasswordManagerClient : public StubPasswordManagerClient {
public:
- MOCK_CONST_METHOD0(GetPasswordSyncState, PasswordSyncState());
+ MOCK_CONST_METHOD0(GetPasswordSyncState, SyncState());
+ MOCK_CONST_METHOD0(GetHistorySyncState, SyncState());
MOCK_CONST_METHOD0(IsSavingAndFillingEnabledForCurrentPage, bool());
MOCK_CONST_METHOD0(IsIncognito, bool());
explicit MockPasswordManagerClient(std::unique_ptr<PrefService> prefs)
: prefs_(std::move(prefs)),
store_(new TestPasswordStore),
- driver_(this) {}
+ driver_(this),
+ password_requirements_service_(
+ std::make_unique<FakePasswordRequirementsSpecFetcher>()) {}
~MockPasswordManagerClient() override { store_->ShutdownOnUIThread(); }
PasswordStore* GetPasswordStore() const override { return store_.get(); }
PrefService* GetPrefs() const override { return prefs_.get(); }
+ PasswordRequirementsService* GetPasswordRequirementsService() override {
+ return &password_requirements_service_;
+ }
+ void SetLastCommittedEntryUrl(const GURL& url) { last_committed_url_ = url; }
+ const GURL& GetLastCommittedEntryURL() const override {
+ return last_committed_url_;
+ }
TestPasswordManagerDriver* test_driver() { return &driver_; }
@@ -101,6 +157,8 @@ class MockPasswordManagerClient : public StubPasswordManagerClient {
std::unique_ptr<PrefService> prefs_;
scoped_refptr<TestPasswordStore> store_;
TestPasswordManagerDriver driver_;
+ PasswordRequirementsService password_requirements_service_;
+ GURL last_committed_url_;
};
} // anonymous namespace
@@ -127,7 +185,7 @@ class PasswordGenerationManagerTest : public testing::Test {
TestPasswordManagerDriver* GetTestDriver() { return client_->test_driver(); }
bool IsGenerationEnabled() {
- return GetGenerationManager()->IsGenerationEnabled();
+ return GetGenerationManager()->IsGenerationEnabled(true);
}
void DetectFormsEligibleForGeneration(
@@ -154,7 +212,7 @@ TEST_F(PasswordGenerationManagerTest, IsGenerationEnabled) {
// Disabling password syncing should cause generation to be disabled.
EXPECT_CALL(*client_, GetPasswordSyncState())
- .WillRepeatedly(testing::Return(NOT_SYNCING_PASSWORDS));
+ .WillRepeatedly(testing::Return(NOT_SYNCING));
EXPECT_FALSE(IsGenerationEnabled());
// Disabling the PasswordManager should cause generation to be disabled even
@@ -170,6 +228,129 @@ TEST_F(PasswordGenerationManagerTest, IsGenerationEnabled) {
EXPECT_FALSE(IsGenerationEnabled());
}
+// Verify that password requirements received from the autofill server are
+// stored and that domain-wide password requirements are fetched as well.
+TEST_F(PasswordGenerationManagerTest, ProcessPasswordRequirements) {
+ // Setup so that IsGenerationEnabled() returns true.
+ EXPECT_CALL(*client_, IsSavingAndFillingEnabledForCurrentPage())
+ .WillRepeatedly(testing::Return(true));
+ EXPECT_CALL(*client_, GetPasswordSyncState())
+ .WillRepeatedly(testing::Return(SYNCING_NORMAL_ENCRYPTION));
+ struct {
+ const char* name;
+ bool has_domain_wide_requirements = false;
+ bool has_field_requirements = false;
+ bool allowed_to_fetch_specs = true;
+ autofill::PasswordRequirementsSpec expected_spec;
+ } kTests[] = {
+ {
+ .name = "No known requirements",
+ .expected_spec = autofill::PasswordRequirementsSpec(),
+ },
+ {
+ .name = "Only domain wide requirements",
+ .has_domain_wide_requirements = true,
+ .expected_spec = GetDomainWideRequirements(),
+ },
+ {
+ .name = "Only field requirements",
+ .has_field_requirements = true,
+ .expected_spec = GetFieldRequirements(),
+ },
+ {
+ .name = "Domain wide requirements take precedence",
+ .has_domain_wide_requirements = true,
+ .has_field_requirements = true,
+ .expected_spec = GetDomainWideRequirements(),
+ },
+ {
+ .name = "Don't fetch spec if not allowed",
+ .has_domain_wide_requirements = true,
+ .allowed_to_fetch_specs = false,
+ // Default value is expected even though .has_domain_wide_requirements
+ // is true.
+ .expected_spec = autofill::PasswordRequirementsSpec(),
+ },
+ };
+
+ // The purpose of this counter is to generate unique URLs and field signatures
+ // so that no caching can happen between test runs. If this causes issues in
+ // the future, the test should be converted into a parameterized test that
+ // creates unqieue PasswordRequirementsService instances for each run.
+ int test_counter = 0;
+ for (const auto& test : kTests) {
+ SCOPED_TRACE(test.name);
+ ++test_counter;
+
+ autofill::FormFieldData username;
+ username.label = ASCIIToUTF16("username");
+ username.name = ASCIIToUTF16("login");
+ username.form_control_type = "text";
+
+ autofill::FormFieldData password;
+ password.label = ASCIIToUTF16("password");
+ password.name =
+ ASCIIToUTF16(base::StringPrintf("password%d", test_counter));
+ password.form_control_type = "password";
+
+ autofill::FormData account_creation_form;
+ account_creation_form.origin = GURL("http://accounts.yahoo.com/");
+ account_creation_form.action = GURL("http://accounts.yahoo.com/signup");
+ account_creation_form.name = ASCIIToUTF16("account_creation_form");
+ account_creation_form.fields.push_back(username);
+ account_creation_form.fields.push_back(password);
+
+ autofill::FormStructure form(account_creation_form);
+
+ std::vector<autofill::FormStructure*> forms = {&form};
+
+ // EMAIL_ADDRESS = 9
+ // ACCOUNT_CREATION_PASSWORD = 76
+ autofill::AutofillQueryResponseContents response;
+ response.add_field()->set_overall_type_prediction(9);
+ response.add_field()->set_overall_type_prediction(76);
+
+ if (test.has_field_requirements) {
+ *response.mutable_field(1)->mutable_password_requirements() =
+ GetFieldRequirements();
+ }
+ // Configure the last committed entry URL with some magic constants for
+ // which the FakePasswordRequirementsFetcher is configured to respond
+ // with a filled or empty response.
+ GURL origin(base::StringPrintf("https://%d-%s/", test_counter,
+ test.has_domain_wide_requirements
+ ? kHasServerResponse
+ : kNoServerResponse));
+ client_->SetLastCommittedEntryUrl(origin);
+
+ std::string response_string;
+ ASSERT_TRUE(response.SerializeToString(&response_string));
+ autofill::FormStructure::ParseQueryResponse(response_string, forms,
+ nullptr);
+
+ EXPECT_CALL(*client_, GetHistorySyncState())
+ .WillRepeatedly(testing::Return(test.allowed_to_fetch_specs
+ ? SYNCING_NORMAL_ENCRYPTION
+ : NOT_SYNCING));
+
+ // Processs the password requirements with expected side effects of
+ // either storing the requirements from the AutofillQueryResponseContents)
+ // in the PasswordRequirementsService or triggering a lookup for the
+ // domain that is stored in the PasswordRequirementsService.
+ GetGenerationManager()->ProcessPasswordRequirements(forms);
+
+ // Validate the result.
+ autofill::FormSignature form_signature =
+ autofill::CalculateFormSignature(account_creation_form);
+ autofill::FieldSignature field_signature =
+ autofill::CalculateFieldSignatureForField(password);
+ autofill::PasswordRequirementsSpec spec =
+ client_->GetPasswordRequirementsService()->GetSpec(
+ origin, form_signature, field_signature);
+ EXPECT_EQ(test.expected_spec.max_length(), spec.max_length());
+ }
+}
+
TEST_F(PasswordGenerationManagerTest, DetectFormsEligibleForGeneration) {
// Setup so that IsGenerationEnabled() returns true.
EXPECT_CALL(*client_, IsSavingAndFillingEnabledForCurrentPage())
@@ -245,7 +426,7 @@ TEST_F(PasswordGenerationManagerTest, DetectFormsEligibleForGeneration) {
std::string response_string;
ASSERT_TRUE(response.SerializeToString(&response_string));
- autofill::FormStructure::ParseQueryResponse(response_string, forms);
+ autofill::FormStructure::ParseQueryResponse(response_string, forms, nullptr);
DetectFormsEligibleForGeneration(forms);
EXPECT_EQ(2u, GetTestDriver()->GetFoundEligibleForGenerationForms().size());
@@ -276,8 +457,9 @@ TEST_F(PasswordGenerationManagerTest, DetectFormsEligibleForGeneration) {
TEST_F(PasswordGenerationManagerTest, UpdatePasswordSyncStateIncognito) {
// Disable password manager by going incognito. Even though password
- // syncing is enabled, generation should still
- // be disabled.
+ // syncing is enabled, generation should still be disabled.
+ EXPECT_CALL(*client_, IsSavingAndFillingEnabledForCurrentPage())
+ .WillRepeatedly(testing::Return(false));
EXPECT_CALL(*client_, IsIncognito()).WillRepeatedly(testing::Return(true));
PrefService* prefs = client_->GetPrefs();
prefs->SetBoolean(prefs::kCredentialsEnableService, true);
diff --git a/chromium/components/password_manager/core/browser/password_hash_data.cc b/chromium/components/password_manager/core/browser/password_hash_data.cc
new file mode 100644
index 00000000000..ef7b2f9069e
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_hash_data.cc
@@ -0,0 +1,128 @@
+// 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/password_manager/core/browser/password_hash_data.h"
+
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "crypto/openssl_util.h"
+#include "crypto/random.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
+
+namespace password_manager {
+
+namespace {
+
+std::string CreateRandomSalt() {
+ constexpr size_t kSyncPasswordSaltLength = 16;
+
+ char buffer[kSyncPasswordSaltLength];
+ crypto::RandBytes(buffer, kSyncPasswordSaltLength);
+ // Explicit std::string constructor with a string length must be used in order
+ // to avoid treating '\0' symbols as a string ends.
+ std::string result(buffer, kSyncPasswordSaltLength);
+ return result;
+}
+
+} // namespace
+
+PasswordHashData::PasswordHashData() = default;
+
+PasswordHashData::PasswordHashData(const PasswordHashData& other) = default;
+
+PasswordHashData::PasswordHashData(const std::string& username,
+ const base::string16& password,
+ bool force_update,
+ bool is_gaia_password)
+ : username(username),
+ length(password.size()),
+ salt(CreateRandomSalt()),
+ hash(CalculatePasswordHash(password, salt)),
+ force_update(force_update),
+ is_gaia_password(is_gaia_password) {}
+
+bool PasswordHashData::MatchesPassword(const std::string& username,
+ const base::string16& password,
+ bool is_gaia_password) const {
+ if (password.size() != this->length ||
+ !AreUsernamesSame(username, is_gaia_password, this->username,
+ this->is_gaia_password)) {
+ return false;
+ }
+
+ return CalculatePasswordHash(password, this->salt) == this->hash;
+}
+
+SyncPasswordData::SyncPasswordData(const base::string16& password,
+ bool force_update)
+ : length(password.size()),
+ salt(CreateRandomSalt()),
+ hash(CalculatePasswordHash(password, salt)),
+ force_update(force_update) {}
+
+bool SyncPasswordData::MatchesPassword(const base::string16& password) const {
+ if (password.size() != this->length)
+ return false;
+ return CalculatePasswordHash(password, this->salt) == this->hash;
+}
+
+uint64_t CalculatePasswordHash(const base::StringPiece16& text,
+ const std::string& salt) {
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+ constexpr size_t kBytesFromHash = 8;
+ constexpr uint64_t kScryptCost = 32; // It must be a power of 2.
+ constexpr uint64_t kScryptBlockSize = 8;
+ constexpr uint64_t kScryptParallelization = 1;
+ constexpr size_t kScryptMaxMemory = 1024 * 1024;
+
+ uint8_t hash[kBytesFromHash];
+ base::StringPiece text_8bits(reinterpret_cast<const char*>(text.data()),
+ text.size() * 2);
+ const uint8_t* salt_ptr = reinterpret_cast<const uint8_t*>(salt.c_str());
+
+ int scrypt_ok = EVP_PBE_scrypt(text_8bits.data(), text_8bits.size(), salt_ptr,
+ salt.size(), kScryptCost, kScryptBlockSize,
+ kScryptParallelization, kScryptMaxMemory, hash,
+ kBytesFromHash);
+
+ // EVP_PBE_scrypt can only fail due to memory allocation error (which aborts
+ // Chromium) or invalid parameters. In case of a failure a hash could leak
+ // information from the stack, so using CHECK is better than DCHECK.
+ CHECK(scrypt_ok);
+
+ // Take 37 bits of |hash|.
+ uint64_t hash37 = ((static_cast<uint64_t>(hash[0]))) |
+ ((static_cast<uint64_t>(hash[1])) << 8) |
+ ((static_cast<uint64_t>(hash[2])) << 16) |
+ ((static_cast<uint64_t>(hash[3])) << 24) |
+ (((static_cast<uint64_t>(hash[4])) & 0x1F) << 32);
+
+ return hash37;
+}
+
+std::string CanonicalizeUsername(const std::string& username,
+ bool is_gaia_account) {
+ std::vector<std::string> parts = base::SplitString(
+ username, "@", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ if (parts.size() != 2U) {
+ if (is_gaia_account && parts.size() == 1U)
+ return gaia::CanonicalizeEmail(username + "@gmail.com");
+ return username;
+ }
+ return gaia::CanonicalizeEmail(username);
+}
+
+bool AreUsernamesSame(const std::string& username1,
+ bool is_username1_gaia_account,
+ const std::string& username2,
+ bool is_username2_gaia_account) {
+ if (is_username1_gaia_account != is_username2_gaia_account)
+ return false;
+ return CanonicalizeUsername(username1, is_username1_gaia_account) ==
+ CanonicalizeUsername(username2, is_username2_gaia_account);
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_hash_data.h b/chromium/components/password_manager/core/browser/password_hash_data.h
new file mode 100644
index 00000000000..b074dbeb3e9
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_hash_data.h
@@ -0,0 +1,72 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_HASH_DATA_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_HASH_DATA_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/strings/string16.h"
+#include "base/strings/string_piece_forward.h"
+
+namespace password_manager {
+
+struct PasswordHashData {
+ PasswordHashData();
+ PasswordHashData(const PasswordHashData& other);
+ PasswordHashData(const std::string& username,
+ const base::string16& password,
+ bool force_update,
+ bool is_gaia_password = true);
+ // Returns true iff |*this| represents the credential (|username|,
+ // |password|), also with respect to whether it |is_gaia_password|.
+ bool MatchesPassword(const std::string& username,
+ const base::string16& password,
+ bool is_gaia_password) const;
+
+ std::string username;
+ size_t length = 0;
+ std::string salt;
+ uint64_t hash = 0;
+ bool force_update = false;
+ bool is_gaia_password = true;
+};
+
+// SyncPasswordData is being deprecated. Please use PasswordHashData instead.
+struct SyncPasswordData {
+ SyncPasswordData() = default;
+ SyncPasswordData(const SyncPasswordData& other) = default;
+ SyncPasswordData(const base::string16& password, bool force_update);
+ // Returns true iff |*this| represents |password|.
+ bool MatchesPassword(const base::string16& password) const;
+
+ size_t length = 0;
+ std::string salt;
+ uint64_t hash = 0;
+ // Signal that we need to update password hash, salt, and length in profile
+ // prefs.
+ bool force_update = false;
+};
+
+// Calculates 37 bits hash for a password. The calculation is based on a slow
+// hash function. The running time is ~10^{-4} seconds on Desktop.
+uint64_t CalculatePasswordHash(const base::StringPiece16& text,
+ const std::string& salt);
+
+// If username is an email address, canonicalizes this email. Otherwise,
+// append "@gmail.com" if it is gaia or returns |username| for non-Gaia account.
+std::string CanonicalizeUsername(const std::string& username,
+ bool is_gaia_account);
+
+// Returns true if the two usernames the same after canonicalization.
+bool AreUsernamesSame(const std::string& username1,
+ bool is_username1_gaia_account,
+ const std::string& username2,
+ bool is_username2_gaia_account);
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_HASH_DATA_H_
diff --git a/chromium/components/password_manager/core/browser/password_hash_data_unittest.cc b/chromium/components/password_manager/core/browser/password_hash_data_unittest.cc
new file mode 100644
index 00000000000..66b94733383
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_hash_data_unittest.cc
@@ -0,0 +1,36 @@
+// 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/password_manager/core/browser/password_hash_data.h"
+
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+namespace {
+
+TEST(PasswordHashDataTest, CalculatePasswordHash) {
+ const char* kPlainText[] = {"", "password", "password", "secret"};
+ const char* kSalt[] = {"", "salt", "123", "456"};
+
+ constexpr uint64_t kExpectedHash[] = {
+ UINT64_C(0x1c610a7950), UINT64_C(0x1927dc525e), UINT64_C(0xf72f81aa6),
+ UINT64_C(0x3645af77f),
+ };
+
+ static_assert(base::size(kPlainText) == base::size(kSalt),
+ "Arrays must have the same size");
+ static_assert(base::size(kPlainText) == base::size(kExpectedHash),
+ "Arrays must have the same size");
+
+ for (size_t i = 0; i < base::size(kPlainText); ++i) {
+ SCOPED_TRACE(i);
+ base::string16 text = base::UTF8ToUTF16(kPlainText[i]);
+ EXPECT_EQ(kExpectedHash[i], CalculatePasswordHash(text, kSalt[i]));
+ }
+}
+
+} // namespace
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_manager.cc b/chromium/components/password_manager/core/browser/password_manager.cc
index b5383eb386e..af5db58012d 100644
--- a/chromium/components/password_manager/core/browser/password_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_manager.cc
@@ -46,6 +46,9 @@
using autofill::FormData;
using autofill::PasswordForm;
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+using password_manager::metrics_util::SyncPasswordHashChange;
+#endif // SYNC_PASSWORD_REUSE_DETECTION_ENABLED
namespace password_manager {
@@ -57,6 +60,14 @@ const char kSpdyProxyRealm[] = "/SpdyProxy";
// already.
typedef autofill::SavePasswordProgressLogger Logger;
+enum class ShouldShowPromptResults {
+ kOldNoNewNo = 0,
+ kOldNoNewYes,
+ kOldYesNewNo,
+ kOldYesNewYes,
+ kMaxValue = kOldYesNewYes,
+};
+
bool URLsEqualUpToScheme(const GURL& a, const GURL& b) {
return (a.GetContent() == b.GetContent());
}
@@ -168,6 +179,8 @@ bool AreAllFieldsEmpty(const PasswordForm& form) {
// Helper function that determines whether update or save prompt should be
// shown for credentials in |provisional_save_manager|.
+// TODO(https://crbug.com/831123): Move to NewPasswordFormManager when the old
+// PasswordFormManager is gone.
bool IsPasswordUpdate(const PasswordFormManager& provisional_save_manager) {
return (!provisional_save_manager.GetBestMatches().empty() &&
provisional_save_manager
@@ -222,6 +235,43 @@ PasswordFormManager* FindMatchedManager(
: matched_manager_it->get();
}
+void LogShouldShouldPromptComparison(bool should_show_prompt_old,
+ bool should_show_prompt) {
+ ShouldShowPromptResults outcome = ShouldShowPromptResults::kMaxValue;
+ if (!should_show_prompt_old && !should_show_prompt)
+ outcome = ShouldShowPromptResults::kOldNoNewNo;
+ if (!should_show_prompt_old && should_show_prompt)
+ outcome = ShouldShowPromptResults::kOldNoNewYes;
+ if (should_show_prompt_old && !should_show_prompt)
+ outcome = ShouldShowPromptResults::kOldYesNewNo;
+ if (should_show_prompt_old && should_show_prompt)
+ outcome = ShouldShowPromptResults::kOldYesNewYes;
+
+ UMA_HISTOGRAM_ENUMERATION("PasswordManager.ShouldShowPromptComparison",
+ outcome);
+}
+
+// Returns true if the user needs to be prompted before a password can be
+// saved (instead of automatically saving the password), based on inspecting
+// the state of |manager|.
+bool ShouldPromptUserToSavePassword(const PasswordFormManager& manager) {
+ if (IsPasswordUpdate(manager)) {
+ // Updating a credential might erase a useful stored value by accident.
+ // Always ask the user to confirm.
+ return true;
+ }
+
+ // User successfully signed-in with PSL match credentials. These credentials
+ // should be automatically saved in order to be autofilled on next login.
+ if (manager.IsPendingCredentialsPublicSuffixMatch())
+ return false;
+
+ if (manager.has_generated_password())
+ return false;
+
+ return manager.IsNewLogin();
+}
+
} // namespace
// static
@@ -313,8 +363,7 @@ void PasswordManager::SetGenerationElementAndReasonForForm(
PasswordFormManager* form_manager = GetMatchingPendingManager(form);
if (form_manager) {
form_manager->set_generation_element(generation_element);
- form_manager->set_is_manual_generation(is_manually_triggered);
- form_manager->set_generation_popup_was_shown(true);
+ form_manager->SetGenerationPopupWasShown(true, is_manually_triggered);
return;
}
@@ -522,15 +571,17 @@ void PasswordManager::ShowManualFallbackForSaving(
FormFetcher::State::WAITING) {
return;
}
- ProvisionallySaveManager(password_form, matched_manager, nullptr);
+
+ std::unique_ptr<PasswordFormManager> manager = matched_manager->Clone();
+ PasswordForm form(password_form);
+ form.preferred = true;
+ manager->ProvisionallySave(form);
// Show the fallback if a prompt or a confirmation bubble should be available.
- bool has_generated_password =
- provisional_save_manager_->has_generated_password();
- if (ShouldPromptUserToSavePassword() || has_generated_password) {
- DCHECK(provisional_save_manager_);
- bool is_update = IsPasswordUpdate(*provisional_save_manager_);
- client_->ShowManualFallbackForSaving(std::move(provisional_save_manager_),
+ bool has_generated_password = manager->has_generated_password();
+ if (ShouldPromptUserToSavePassword(*manager) || has_generated_password) {
+ bool is_update = IsPasswordUpdate(*manager);
+ client_->ShowManualFallbackForSaving(std::move(manager),
has_generated_password, is_update);
matched_manager->GetMetricsRecorder()->RecordShowManualFallbackForSaving(
has_generated_password, is_update);
@@ -620,6 +671,7 @@ void PasswordManager::CreatePendingLoginManagers(
if (base::EndsWith(iter->signon_realm, kSpdyProxyRealm,
base::CompareCase::SENSITIVE))
continue;
+
bool old_manager_found = false;
for (const auto& old_manager : pending_login_managers_) {
if (old_manager->DoesManage(*iter, driver) !=
@@ -667,23 +719,37 @@ void PasswordManager::CreateFormManagers(
password_manager::PasswordManagerDriver* driver,
const std::vector<autofill::PasswordForm>& forms) {
// Find new forms.
- std::vector<const FormData*> new_forms;
- for (const autofill::PasswordForm& form : forms) {
- bool form_manager_exists =
- std::any_of(form_managers_.begin(), form_managers_.end(),
- [&form, driver](const auto& form_manager) {
- return form_manager->DoesManage(form.form_data, driver);
- });
- if (!form_manager_exists)
- new_forms.push_back(&form.form_data);
+ std::vector<const PasswordForm*> new_forms;
+ for (const PasswordForm& form : forms) {
+ // TODO(https://crbug.com/831123): Implement inside NewPasswordFormManger
+ // not-filling Gaia forms that should be ignored instead of non-creating
+ // NewPasswordFormManger instance.
+ if (form.is_gaia_with_skip_save_password_form)
+ continue;
+ auto form_it =
+ std::find_if(form_managers_.begin(), form_managers_.end(),
+ [&form, driver](const auto& form_manager) {
+ return form_manager->DoesManage(form.form_data, driver);
+ });
+ if (form_it == form_managers_.end()) {
+ new_forms.push_back(&form);
+ } else {
+ // This extra filling is just duplicating redundancy that was in
+ // PasswordFormManager, that helps to fix cases when the site overrides
+ // filled values.
+ // TODO(https://crbug.com/831123): Implement more robust filling and
+ // remove the next line.
+ (*form_it)->Fill();
+ }
}
// Create form manager for new forms.
- for (const FormData* new_form : new_forms) {
+ for (const PasswordForm* new_form : new_forms) {
form_managers_.push_back(std::make_unique<NewPasswordFormManager>(
client_,
driver ? driver->AsWeakPtr() : base::WeakPtr<PasswordManagerDriver>(),
- *new_form, nullptr));
+ new_form->form_data, nullptr));
+ form_managers_.back()->set_old_parsing_result(*new_form);
}
}
@@ -710,6 +776,21 @@ void PasswordManager::ProcessSubmittedForm(
}
}
+void PasswordManager::ReportSpecPriorityForGeneratedPassword(
+ const autofill::PasswordForm& password_form,
+ uint32_t spec_priority) {
+ PasswordFormManager* form_manager = GetMatchingPendingManager(password_form);
+ if (form_manager && form_manager->GetMetricsRecorder()) {
+ form_manager->GetMetricsRecorder()->ReportSpecPriorityForGeneratedPassword(
+ spec_priority);
+ }
+}
+
+void PasswordManager::OnStartNavigation(PasswordManagerDriver* driver) {
+ // TODO(crbug/842643): use this signal instead of DidStartProvisionalLoad in
+ // the renderer.
+}
+
void PasswordManager::ProvisionallySaveManager(
const PasswordForm& form,
PasswordFormManager* matched_manager,
@@ -764,7 +845,7 @@ bool PasswordManager::ShouldBlockPasswordForSameOriginButDifferentScheme(
!new_origin.SchemeIsCryptographic();
}
-bool PasswordManager::ShouldPromptUserToSavePassword() const {
+bool PasswordManager::ShouldPromptUserToSavePasswordOld() const {
return (provisional_save_manager_->IsNewLogin() ||
provisional_save_manager_
->is_possible_change_password_form_without_username() ||
@@ -854,12 +935,6 @@ void PasswordManager::OnPasswordFormsRendered(
}
}
-void PasswordManager::OnSameDocumentNavigation(
- password_manager::PasswordManagerDriver* driver,
- const PasswordForm& password_form) {
- OnPasswordFormSubmittedNoChecks(driver, password_form);
-}
-
void PasswordManager::OnLoginSuccessful() {
std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
if (password_manager_util::IsLoggingActive(client_)) {
@@ -900,7 +975,11 @@ void PasswordManager::OnLoginSuccessful() {
// If the form is eligible only for saving fallback, it shouldn't go here.
DCHECK(!provisional_save_manager_->GetPendingCredentials()
.only_for_fallback_saving);
- if (ShouldPromptUserToSavePassword()) {
+
+ LogShouldShouldPromptComparison(
+ ShouldPromptUserToSavePasswordOld(),
+ ShouldPromptUserToSavePassword(*provisional_save_manager_));
+ if (ShouldPromptUserToSavePassword(*provisional_save_manager_)) {
bool empty_password = provisional_save_manager_->GetPendingCredentials()
.username_value.empty();
UMA_HISTOGRAM_BOOLEAN("PasswordManager.EmptyUsernames.OfferedToSave",
@@ -933,37 +1012,53 @@ void PasswordManager::OnLoginSuccessful() {
void PasswordManager::MaybeSavePasswordHash() {
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
- if (client_->GetStoreResultFilter()->ShouldSavePasswordHash(
- *provisional_save_manager_->submitted_form())) {
- // When |username_value| is empty, it's not clear whether the submitted
- // credentials are really sync credentials. Don't save sync password hash
- // in that case.
- std::string username = base::UTF16ToUTF8(
- provisional_save_manager_->submitted_form()->username_value);
- if (username.empty())
- return;
-
- password_manager::PasswordStore* store = client_->GetPasswordStore();
- // May be null in tests.
- if (!store)
- return;
- bool is_sync_password_change = !provisional_save_manager_->submitted_form()
- ->new_password_element.empty();
- // Canonicalizes username if it is an email.
- if (username.find('@') != std::string::npos)
- username = gaia::CanonicalizeEmail(username);
-
- if (is_sync_password_change) {
- store->SaveSyncPasswordHash(
- username,
- provisional_save_manager_->submitted_form()->new_password_value,
- metrics_util::SyncPasswordHashChange::CHANGED_IN_CONTENT_AREA);
- } else {
- store->SaveSyncPasswordHash(
- username, provisional_save_manager_->submitted_form()->password_value,
- metrics_util::SyncPasswordHashChange::SAVED_IN_CONTENT_AREA);
- }
+ // When |username_value| is empty, it's not clear whether the submitted
+ // credentials are really Gaia or enterprise credentials. Don't save
+ // password hash in that case.
+ std::string username = base::UTF16ToUTF8(
+ provisional_save_manager_->submitted_form()->username_value);
+ if (username.empty())
+ return;
+
+ password_manager::PasswordStore* store = client_->GetPasswordStore();
+ // May be null in tests.
+ if (!store)
+ return;
+
+ const autofill::PasswordForm* password_form =
+ provisional_save_manager_->submitted_form();
+
+ bool should_save_enterprise_pw =
+ client_->GetStoreResultFilter()->ShouldSaveEnterprisePasswordHash(
+ *password_form);
+ bool should_save_gaia_pw =
+ client_->GetStoreResultFilter()->ShouldSaveGaiaPasswordHash(
+ *password_form);
+
+ if (!should_save_enterprise_pw && !should_save_gaia_pw)
+ return;
+
+ // Canonicalizes username if it is an email.
+ if (username.find('@') != std::string::npos)
+ username = gaia::CanonicalizeEmail(username);
+ bool is_password_change = !password_form->new_password_element.empty();
+ const base::string16 password = is_password_change
+ ? password_form->new_password_value
+ : password_form->password_value;
+
+ if (should_save_enterprise_pw) {
+ store->SaveEnterprisePasswordHash(username, password);
+ return;
}
+
+ DCHECK(should_save_gaia_pw);
+ SyncPasswordHashChange event =
+ client_->GetStoreResultFilter()->IsSyncAccountEmail(username)
+ ? (is_password_change
+ ? SyncPasswordHashChange::CHANGED_IN_CONTENT_AREA
+ : SyncPasswordHashChange::SAVED_IN_CONTENT_AREA)
+ : SyncPasswordHashChange::NOT_SYNC_PASSWORD_CHANGE;
+ store->SaveGaiaPasswordHash(username, password, event);
#endif
}
@@ -996,6 +1091,12 @@ void PasswordManager::ProcessAutofillPredictions(
logger.reset(
new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
+ if (base::FeatureList::IsEnabled(
+ password_manager::features::kNewPasswordFormParsing)) {
+ for (auto& manager : form_managers_)
+ manager->ProcessServerPredictions(forms);
+ }
+
// Leave only forms that contain fields that are useful for password manager.
std::map<FormData, autofill::PasswordFormFieldPredictionMap> predictions;
for (const autofill::FormStructure* form : forms) {
diff --git a/chromium/components/password_manager/core/browser/password_manager.h b/chromium/components/password_manager/core/browser/password_manager.h
index 5e02b70745c..0998e36877d 100644
--- a/chromium/components/password_manager/core/browser/password_manager.h
+++ b/chromium/components/password_manager/core/browser/password_manager.h
@@ -18,6 +18,7 @@
#include "build/build_config.h"
#include "components/autofill/core/common/password_form.h"
#include "components/autofill/core/common/password_form_fill_data.h"
+#include "components/password_manager/core/browser/form_submission_observer.h"
#include "components/password_manager/core/browser/login_model.h"
#include "components/password_manager/core/browser/password_form_manager.h"
@@ -44,7 +45,7 @@ class NewPasswordFormManager;
// receiving password form data from the renderer and managing the password
// database through the PasswordStore. The PasswordManager is a LoginModel
// for purposes of supporting HTTP authentication dialogs.
-class PasswordManager : public LoginModel {
+class PasswordManager : public LoginModel, public FormSubmissionObserver {
public:
// Expresses which navigation entry to use to check whether password manager
// is enabled.
@@ -117,12 +118,14 @@ class PasswordManager : public LoginModel {
bool did_stop_loading);
// Handles a password form being submitted.
- virtual void OnPasswordFormSubmitted(
- password_manager::PasswordManagerDriver* driver,
- const autofill::PasswordForm& password_form);
+ void OnPasswordFormSubmitted(password_manager::PasswordManagerDriver* driver,
+ const autofill::PasswordForm& password_form);
// Handles a password form being submitted, assumes that submission is
// successful and does not do any checks on success of submission.
+ // For example, this is called if |password_form| was filled
+ // upon in-page navigation. This often means history.pushState being
+ // called from JavaScript.
void OnPasswordFormSubmittedNoChecks(
password_manager::PasswordManagerDriver* driver,
const autofill::PasswordForm& password_form);
@@ -141,13 +144,6 @@ class PasswordManager : public LoginModel {
// Handles a request to hide manual fallback for password saving.
void HideManualFallbackForSaving();
- // Called if |password_form| was filled upon in-page navigation. This often
- // means history.pushState being called from JavaScript. If this causes false
- // positive in password saving, update http://crbug.com/357696.
- // TODO(https://crbug.com/795462): find better name for this function.
- void OnSameDocumentNavigation(password_manager::PasswordManagerDriver* driver,
- const autofill::PasswordForm& password_form);
-
void ProcessAutofillPredictions(
password_manager::PasswordManagerDriver* driver,
const std::vector<autofill::FormStructure*>& forms);
@@ -176,15 +172,29 @@ class PasswordManager : public LoginModel {
const std::vector<std::unique_ptr<NewPasswordFormManager>>& form_managers() {
return form_managers_;
}
+
+ const PasswordFormManager* provisional_save_manager() {
+ return provisional_save_manager_.get();
+ }
#endif
NavigationEntryToCheck entry_to_check() const { return entry_to_check_; }
+ // Reports the priority of a PasswordGenerationRequirementsSpec for a
+ // generated password. See
+ // PasswordFormMetricsRecorder::ReportSpecPriorityForGeneratedPassword.
+ void ReportSpecPriorityForGeneratedPassword(
+ const autofill::PasswordForm& password_form,
+ uint32_t spec_priority);
+
private:
FRIEND_TEST_ALL_PREFIXES(
PasswordManagerTest,
ShouldBlockPasswordForSameOriginButDifferentSchemeTest);
+ // FormSubmissionObserver:
+ void OnStartNavigation(PasswordManagerDriver* driver) override;
+
// Clones |matched_manager| and keeps it as |provisional_save_manager_|.
// |form| is saved provisionally to |provisional_save_manager_|.
void ProvisionallySaveManager(const autofill::PasswordForm& form,
@@ -202,11 +212,10 @@ class PasswordManager : public LoginModel {
bool ShouldBlockPasswordForSameOriginButDifferentScheme(
const autofill::PasswordForm& form) const;
- // Returns true if the user needs to be prompted before a password can be
- // saved (instead of automatically saving
- // the password), based on inspecting the state of
- // |provisional_save_manager_|.
- bool ShouldPromptUserToSavePassword() const;
+ // The old version of ShouldPromptUserToSavePassword, it is left for
+ // comparison and metric sending.
+ // TODO(crbug.com/856543): Remove it after M-70.
+ bool ShouldPromptUserToSavePasswordOld() const;
// Called when the login was deemed successful. It handles the special case
// when the provisionally saved password is a sync credential, and otherwise
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 ca18e83f821..533a825c85e 100644
--- a/chromium/components/password_manager/core/browser/password_manager_client.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_client.cc
@@ -29,9 +29,6 @@ bool PasswordManagerClient::OnCredentialManagerUsed() {
return true;
}
-void PasswordManagerClient::ForceSavePassword() {
-}
-
void PasswordManagerClient::GeneratePassword() {}
void PasswordManagerClient::PasswordWasAutofilled(
@@ -40,8 +37,12 @@ void PasswordManagerClient::PasswordWasAutofilled(
const std::vector<const autofill::PasswordForm*>* federated_matches) const {
}
-PasswordSyncState PasswordManagerClient::GetPasswordSyncState() const {
- return NOT_SYNCING_PASSWORDS;
+SyncState PasswordManagerClient::GetPasswordSyncState() const {
+ return NOT_SYNCING;
+}
+
+SyncState PasswordManagerClient::GetHistorySyncState() const {
+ return NOT_SYNCING;
}
bool PasswordManagerClient::WasLastNavigationHTTPError() const {
@@ -84,4 +85,15 @@ const LogManager* PasswordManagerClient::GetLogManager() const {
void PasswordManagerClient::AnnotateNavigationEntry(bool has_password_field) {}
+PasswordRequirementsService*
+PasswordManagerClient::GetPasswordRequirementsService() {
+ // Not impemented but that is a valid state as per interface definition.
+ // Therefore, don't call NOTIMPLEMENTED() here.
+ return nullptr;
+}
+
+favicon::FaviconService* PasswordManagerClient::GetFaviconService() {
+ return nullptr;
+}
+
} // 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 6f39847ed68..a16235459cd 100644
--- a/chromium/components/password_manager/core/browser/password_manager_client.h
+++ b/chromium/components/password_manager/core/browser/password_manager_client.h
@@ -21,6 +21,10 @@ namespace autofill {
class AutofillManager;
}
+namespace favicon {
+class FaviconService;
+}
+
class GURL;
#if defined(SAFE_BROWSING_DB_LOCAL)
@@ -35,10 +39,11 @@ class LogManager;
class PasswordFormManagerForUI;
class PasswordManager;
class PasswordManagerMetricsRecorder;
+class PasswordRequirementsService;
class PasswordStore;
-enum PasswordSyncState {
- NOT_SYNCING_PASSWORDS,
+enum SyncState {
+ NOT_SYNCING,
SYNCING_NORMAL_ENCRYPTION,
SYNCING_WITH_CUSTOM_PASSPHRASE
};
@@ -120,10 +125,6 @@ class PasswordManagerClient {
const GURL& origin,
const CredentialsCallback& callback) = 0;
- // Informs the embedder that the user has manually requested to save the
- // password in the focused password field.
- virtual void ForceSavePassword();
-
// Informs the embedder that the user has manually requested to generate a
// password in the focused password field.
virtual void GeneratePassword();
@@ -176,9 +177,14 @@ class PasswordManagerClient {
virtual PasswordStore* GetPasswordStore() const = 0;
// Reports whether and how passwords are synced in the embedder. The default
- // implementation always returns NOT_SYNCING_PASSWORDS.
- // TODO(vabr): Factor this out of the client to the sync layer.
- virtual PasswordSyncState GetPasswordSyncState() const;
+ // implementation always returns NOT_SYNCING.
+ // TODO(crbug.com/515108): Factor this out of the client to the sync layer.
+ virtual SyncState GetPasswordSyncState() const;
+
+ // Reports whether and how browsing history is synced in the embedder. The
+ // default implementation always returns NOT_SYNCING.
+ // TODO(crbug.com/515108): Factor this out of the client to the sync layer.
+ virtual SyncState GetHistorySyncState() const;
// Returns true if last navigation page had HTTP error i.e 5XX or 4XX
virtual bool WasLastNavigationHTTPError() const;
@@ -226,11 +232,11 @@ class PasswordManagerClient {
const GURL& frame_url) = 0;
// Checks the safe browsing reputation of the webpage where password reuse
- // happens. This is called by the PasswordReuseDetectionManager when either
- // the sync password or a saved password is typed on the wrong domain.
- // This may trigger a warning dialog if it looks like the page is phishy.
+ // happens. This is called by the PasswordReuseDetectionManager when a
+ // protected password is typed on the wrong domain. This may trigger a
+ // warning dialog if it looks like the page is phishy.
virtual void CheckProtectedPasswordEntry(
- bool matches_sync_password,
+ metrics_util::PasswordType reused_password_type,
const std::vector<std::string>& matching_domains,
bool password_field_exists) = 0;
@@ -248,6 +254,19 @@ class PasswordManagerClient {
// hold on to the pointer.
virtual PasswordManagerMetricsRecorder& GetMetricsRecorder() = 0;
+ // Gets the PasswordRequirementsService associated with the client. It is
+ // valid that this method returns a nullptr if the PasswordRequirementsService
+ // has not been implemented for a specific platform or the context is an
+ // incognito context. Callers should guard against this.
+ virtual PasswordRequirementsService* GetPasswordRequirementsService();
+
+ // Returns the favicon service used to retrieve icons for an origin.
+ virtual favicon::FaviconService* GetFaviconService();
+
+ // Causes all live PasswordFormManager objects to query the password store
+ // again. Results in updating the fill information on the page.
+ virtual void UpdateFormManagers() {}
+
private:
DISALLOW_COPY_AND_ASSIGN(PasswordManagerClient);
};
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 4a54e8075f9..9fc4211d524 100644
--- a/chromium/components/password_manager/core/browser/password_manager_constants.h
+++ b/chromium/components/password_manager/core/browser/password_manager_constants.h
@@ -13,9 +13,11 @@ extern const base::FilePath::CharType kAffiliationDatabaseFileName[];
extern const base::FilePath::CharType kLoginDataFileName[];
// URL to the password manager account dashboard.
+// TODO(crbug.com/862269): remove when "Smart Lock" is completely gone.
extern const char kPasswordManagerAccountDashboardURL[];
// URL to the help center article about Smart Lock;
+// TODO(crbug.com/862269): remove when "Smart Lock" is completely gone.
extern const char kPasswordManagerHelpCenterSmartLock[];
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_manager_driver.h b/chromium/components/password_manager/core/browser/password_manager_driver.h
index 0469659be97..557a038c22c 100644
--- a/chromium/components/password_manager/core/browser/password_manager_driver.h
+++ b/chromium/components/password_manager/core/browser/password_manager_driver.h
@@ -8,9 +8,11 @@
#include <map>
#include <vector>
+#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string16.h"
+#include "components/autofill/core/common/filling_status.h"
#include "components/autofill/core/common/password_form_field_prediction_map.h"
namespace autofill {
@@ -62,13 +64,17 @@ class PasswordManagerDriver
// Notifies the driver that the user has accepted a generated password.
virtual void GeneratedPasswordAccepted(const base::string16& password) = 0;
- // User have selected a password generation option.
- virtual void UserSelectedManualGenerationOption() = 0;
-
// Tells the driver to fill the form with the |username| and |password|.
virtual void FillSuggestion(const base::string16& username,
const base::string16& password) = 0;
+ // Tells the renderer to fill the given credential into the focused element.
+ // Always calls |completed_callback| with a status indicating success/error.
+ virtual void FillIntoFocusedField(
+ bool is_password,
+ const base::string16& user_provided_credential,
+ base::OnceCallback<void(autofill::FillingStatus)> compeleted_callback) {}
+
// Tells the driver to preview filling form with the |username| and
// |password|.
virtual void PreviewSuggestion(const base::string16& username,
@@ -86,14 +92,6 @@ class PasswordManagerDriver
// the corresponding password form, so that it can be saved.
virtual void ForceSavePassword() {}
- // Tells the driver to show the manual fallback for password saving, i.e. to
- // show the omnibox icon with anchored hidden save prompt.
- virtual void ShowManualFallbackForSaving(const autofill::PasswordForm& form) {
- }
-
- // Tells the driver to hide the manual fallback for saving.
- virtual void HideManualFallbackForSaving() {}
-
// Tells the driver to find the focused password field and to show generation
// popup at it.
virtual void GeneratePassword() {}
@@ -120,9 +118,6 @@ class PasswordManagerDriver
// Return true iff the driver corresponds to the main frame.
virtual bool IsMainFrame() const = 0;
- // Tells the driver that the matching blacklisted form was found.
- virtual void MatchingBlacklistedFormFound() = 0;
-
private:
DISALLOW_COPY_AND_ASSIGN(PasswordManagerDriver);
};
diff --git a/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.h b/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.h
index 1f348f5a0b6..cadb37ef3b8 100644
--- a/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.h
+++ b/chromium/components/password_manager/core/browser/password_manager_metrics_recorder.h
@@ -63,8 +63,8 @@ class PasswordManagerMetricsRecorder {
kUnknown = 0,
// User chose to open the password viewer as part of a manual fallback.
- kShowAllPasswordsWhileSomeAreSuggested,
- kShowAllPasswordsWhileNoneAreSuggested,
+ kShowAllPasswordsWhileSomeAreSuggested = 1,
+ kObsoleteShowAllPasswordsWhileNoneAreSuggested = 2,
};
// Records UKM metrics and reports them on destruction.
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 a6e6827c071..5c70bb179ae 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
@@ -149,7 +149,8 @@ void LogCredentialManagerGetResult(CredentialManagerGetResult result,
void LogPasswordReuse(int password_length,
int saved_passwords,
int number_matches,
- bool password_field_detected) {
+ bool password_field_detected,
+ PasswordType reused_password_type) {
UMA_HISTOGRAM_COUNTS_100("PasswordManager.PasswordReuse.PasswordLength",
password_length);
UMA_HISTOGRAM_COUNTS_1000("PasswordManager.PasswordReuse.TotalPasswords",
@@ -160,6 +161,9 @@ void LogPasswordReuse(int password_length,
"PasswordManager.PasswordReuse.PasswordFieldDetected",
password_field_detected ? HAS_PASSWORD_FIELD : NO_PASSWORD_FIELD,
PASSWORD_REUSE_PASSWORD_FIELD_DETECTED_COUNT);
+ UMA_HISTOGRAM_ENUMERATION("PasswordManager.ReusedPasswordType",
+ reused_password_type,
+ PasswordType::PASSWORD_TYPE_COUNT);
}
void LogContextOfShowAllSavedPasswordsShown(
@@ -208,6 +212,16 @@ void LogIsSyncPasswordHashSaved(IsSyncPasswordHashSaved state) {
"PasswordManager.IsSyncPasswordHashSaved", state,
IsSyncPasswordHashSaved::IS_SYNC_PASSWORD_HASH_SAVED_COUNT);
}
+
+void LogProtectedPasswordHashCounts(size_t gaia_hash_count,
+ size_t enterprise_hash_count) {
+ UMA_HISTOGRAM_COUNTS_100("PasswordManager.SavedGaiaPasswordHashCount",
+ static_cast<int>(gaia_hash_count));
+ UMA_HISTOGRAM_COUNTS_100("PasswordManager.SavedEnterprisePasswordHashCount",
+ static_cast<int>(enterprise_hash_count));
+}
+
+void LogProtectedPasswordReuse(PasswordType reused_password_type) {}
#endif
} // namespace 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 66b8f724c93..25f3da551a4 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
@@ -203,6 +203,7 @@ enum class SyncPasswordHashChange {
SAVED_IN_CONTENT_AREA,
CLEARED_ON_CHROME_SIGNOUT,
CHANGED_IN_CONTENT_AREA,
+ NOT_SYNC_PASSWORD_CHANGE,
SAVED_SYNC_PASSWORD_CHANGE_COUNT
};
@@ -224,10 +225,8 @@ enum ShowAllSavedPasswordsContext {
// The "Show all saved passwords..." fallback is shown below a list of
// available passwords.
SHOW_ALL_SAVED_PASSWORDS_CONTEXT_PASSWORD,
- // The "Show all saved passwords..." fallback is shown when no available
- // passwords can be suggested to the user, e.g. because none are saved or
- // because of technical issues.
- SHOW_ALL_SAVED_PASSWORDS_CONTEXT_MANUAL_FALLBACK,
+ // Obsolete.
+ SHOW_ALL_SAVED_PASSWORDS_CONTEXT_MANUAL_FALLBACK_DEPRECATED,
// The "Show all saved passwords..." fallback is shown in context menu.
SHOW_ALL_SAVED_PASSWORDS_CONTEXT_CONTEXT_MENU,
SHOW_ALL_SAVED_PASSWORDS_CONTEXT_COUNT
@@ -253,6 +252,20 @@ enum class ExportPasswordsResult {
COUNT,
};
+// Used in UMA histograms, please do NOT reorder.
+// Metric: "PasswordManager.ReusedPasswordType".
+enum class PasswordType {
+ // Passwords saved by password manager.
+ SAVED_PASSWORD = 0,
+ // Passwords used for Chrome sign-in.
+ SYNC_PASSWORD = 1,
+ // Other Gaia passwords used in Chrome other than the sync password.
+ OTHER_GAIA_PASSWORD = 2,
+ // Passwords captured from enterprise login page.
+ ENTERPRISE_PASSWORD = 3,
+ PASSWORD_TYPE_COUNT
+};
+
// A version of the UMA_HISTOGRAM_BOOLEAN macro that allows the |name|
// to vary over the program's runtime.
void LogUMAHistogramBoolean(const std::string& name, bool sample);
@@ -319,7 +332,8 @@ void LogCredentialManagerGetResult(CredentialManagerGetResult result,
void LogPasswordReuse(int password_length,
int saved_passwords,
int number_matches,
- bool password_field_detected);
+ bool password_field_detected,
+ PasswordType reused_password_type);
// Log the context in which the "Show all saved passwords" fallback was shown.
void LogContextOfShowAllSavedPasswordsShown(
@@ -348,6 +362,11 @@ void LogSyncPasswordHashChange(SyncPasswordHashChange event);
// Log whether a sync password hash saved.
void LogIsSyncPasswordHashSaved(IsSyncPasswordHashSaved state);
+
+// Log the number of Gaia password hashes saved, and the number of enterprise
+// password hashes saved.
+void LogProtectedPasswordHashCounts(size_t gaia_hash_count,
+ size_t enterprise_hash_count);
#endif
} // namespace metrics_util
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 1d72f8fdeec..85ddfe211e7 100644
--- a/chromium/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_unittest.cc
@@ -14,9 +14,10 @@
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
-#include "base/test/user_action_tester.h"
+#include "base/test/test_mock_time_task_runner.h"
#include "build/build_config.h"
#include "components/password_manager/core/browser/form_fetcher_impl.h"
#include "components/password_manager/core/browser/mock_password_store.h"
@@ -40,8 +41,11 @@
using autofill::PasswordForm;
using base::ASCIIToUTF16;
+using base::TestMockTimeTaskRunner;
using testing::_;
using testing::AnyNumber;
+using testing::IsNull;
+using testing::NotNull;
using testing::Return;
using testing::ReturnRef;
using testing::SaveArg;
@@ -54,10 +58,13 @@ namespace {
class MockStoreResultFilter : public StubCredentialsFilter {
public:
MOCK_CONST_METHOD1(ShouldSave, bool(const autofill::PasswordForm& form));
- MOCK_CONST_METHOD1(ShouldSavePasswordHash,
- bool(const autofill::PasswordForm& form));
MOCK_CONST_METHOD1(ReportFormLoginSuccess,
void(const PasswordFormManager& form_manager));
+ MOCK_CONST_METHOD1(IsSyncAccountEmail, bool(const std::string&));
+ MOCK_CONST_METHOD1(ShouldSaveGaiaPasswordHash,
+ bool(const autofill::PasswordForm&));
+ MOCK_CONST_METHOD1(ShouldSaveEnterprisePasswordHash,
+ bool(const autofill::PasswordForm&));
};
class MockPasswordManagerClient : public StubPasswordManagerClient {
@@ -67,7 +74,11 @@ class MockPasswordManagerClient : public StubPasswordManagerClient {
.Times(AnyNumber())
.WillRepeatedly(Return(&filter_));
ON_CALL(filter_, ShouldSave(_)).WillByDefault(Return(true));
- ON_CALL(filter_, ShouldSavePasswordHash(_)).WillByDefault(Return(false));
+ ON_CALL(filter_, ShouldSaveGaiaPasswordHash(_))
+ .WillByDefault(Return(false));
+ ON_CALL(filter_, ShouldSaveEnterprisePasswordHash(_))
+ .WillByDefault(Return(false));
+ ON_CALL(filter_, IsSyncAccountEmail(_)).WillByDefault(Return(false));
}
MOCK_CONST_METHOD0(IsSavingAndFillingEnabledForCurrentPage, bool());
@@ -143,7 +154,9 @@ ACTION_P(SaveToScopedPtr, scoped) { scoped->reset(arg0); }
class PasswordManagerTest : public testing::Test {
public:
- PasswordManagerTest() : test_url_("https://www.example.com") {}
+ PasswordManagerTest()
+ : test_url_("https://www.example.com"),
+ task_runner_(new TestMockTimeTaskRunner) {}
~PasswordManagerTest() override = default;
protected:
@@ -279,6 +292,7 @@ class PasswordManagerTest : public testing::Test {
std::unique_ptr<PasswordAutofillManager> password_autofill_manager_;
std::unique_ptr<PasswordManager> manager_;
PasswordForm submitted_form_;
+ scoped_refptr<TestMockTimeTaskRunner> task_runner_;
};
MATCHER_P(FormMatches, form, "") {
@@ -820,10 +834,14 @@ TEST_F(PasswordManagerTest, SyncCredentialsNotSaved) {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_)).Times(0);
EXPECT_CALL(*store_, AddLogin(_)).Times(0);
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
- ON_CALL(*client_.GetStoreResultFilter(), ShouldSavePasswordHash(_))
+ ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
+ .WillByDefault(Return(true));
+ ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
.WillByDefault(Return(true));
EXPECT_CALL(*store_,
- SaveSyncPasswordHash("googleuser", form.password_value, _));
+ SaveGaiaPasswordHash(
+ "googleuser", form.password_value,
+ metrics_util::SyncPasswordHashChange::SAVED_IN_CONTENT_AREA));
#endif
// Prefs are needed for failure logging about sync credentials.
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
@@ -838,6 +856,37 @@ TEST_F(PasswordManagerTest, SyncCredentialsNotSaved) {
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+TEST_F(PasswordManagerTest, HashSavedOnGaiaFormWithSkipSavePassword) {
+ EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
+ .WillRepeatedly(Return(true));
+
+ std::vector<PasswordForm> observed;
+ PasswordForm form(MakeSimpleGAIAForm());
+ // Simulate that this is Gaia form that should be ignored for saving/filling.
+ form.is_gaia_with_skip_save_password_form = true;
+ observed.push_back(form);
+ manager()->OnPasswordFormsParsed(&driver_, observed);
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+
+ ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
+ .WillByDefault(Return(true));
+ ON_CALL(*client_.GetStoreResultFilter(), ShouldSave(_))
+ .WillByDefault(Return(false));
+ ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
+ .WillByDefault(Return(true));
+
+ EXPECT_CALL(*store_,
+ SaveGaiaPasswordHash(
+ "googleuser", form.password_value,
+ metrics_util::SyncPasswordHashChange::SAVED_IN_CONTENT_AREA));
+
+ OnPasswordFormSubmitted(form);
+ observed.clear();
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+}
+#endif
+
// On a successful login with an updated password,
// CredentialsFilter::ReportFormLoginSuccess and CredentialsFilter::ShouldSave
// should be called. The argument of ShouldSave shold be the submitted form.
@@ -901,10 +950,14 @@ TEST_F(PasswordManagerTest, SyncCredentialsNotDroppedIfUpToDate) {
.WillRepeatedly(Return(true));
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
- ON_CALL(*client_.GetStoreResultFilter(), ShouldSavePasswordHash(_))
+ ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
+ .WillByDefault(Return(true));
+ ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
.WillByDefault(Return(true));
EXPECT_CALL(*store_,
- SaveSyncPasswordHash("googleuser", form.password_value, _));
+ SaveGaiaPasswordHash(
+ "googleuser", form.password_value,
+ metrics_util::SyncPasswordHashChange::SAVED_IN_CONTENT_AREA));
#endif
manager()->ProvisionallySavePassword(form, nullptr);
@@ -940,10 +993,14 @@ TEST_F(PasswordManagerTest, SyncCredentialsDroppedWhenObsolete) {
.WillRepeatedly(Return(true));
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
- ON_CALL(*client_.GetStoreResultFilter(), ShouldSavePasswordHash(_))
+ ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
+ .WillByDefault(Return(true));
+ ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
.WillByDefault(Return(true));
- EXPECT_CALL(*store_, SaveSyncPasswordHash("googleuser",
- ASCIIToUTF16("n3w passw0rd"), _));
+ EXPECT_CALL(*store_,
+ SaveGaiaPasswordHash(
+ "googleuser", ASCIIToUTF16("n3w passw0rd"),
+ metrics_util::SyncPasswordHashChange::SAVED_IN_CONTENT_AREA));
#endif
manager()->ProvisionallySavePassword(updated_form, nullptr);
@@ -1197,6 +1254,10 @@ TEST_F(PasswordManagerTest, FormSubmitWithOnlyPasswordField) {
// ID. The test checks that nevertheless each of them gets assigned its own
// NewPasswordFormManager and filled as expected.
TEST_F(PasswordManagerTest, FillPasswordOnManyFrames_SameId) {
+ // Setting task runner is required since NewPasswordFormManager uses
+ // PostDelayTask for making filling.
+ TestMockTimeTaskRunner::ScopedContext scoped_context_(task_runner_.get());
+
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kNewPasswordFormParsing);
@@ -1207,9 +1268,11 @@ TEST_F(PasswordManagerTest, FillPasswordOnManyFrames_SameId) {
form_data.fields.resize(2);
form_data.fields[0].name = ASCIIToUTF16("Email");
form_data.fields[0].value = ASCIIToUTF16("googleuser");
+ form_data.fields[0].unique_renderer_id = 1;
form_data.fields[0].form_control_type = "text";
form_data.fields[1].name = ASCIIToUTF16("Passwd");
form_data.fields[1].value = ASCIIToUTF16("p4ssword");
+ form_data.fields[1].unique_renderer_id = 2;
form_data.fields[1].form_control_type = "password";
PasswordForm first_form;
first_form.form_data = form_data;
@@ -1218,8 +1281,10 @@ TEST_F(PasswordManagerTest, FillPasswordOnManyFrames_SameId) {
form_data.action = GURL("http://www.example.com/");
form_data.fields[0].name = ASCIIToUTF16("User");
form_data.fields[0].value = ASCIIToUTF16("exampleuser");
+ form_data.fields[0].unique_renderer_id = 3;
form_data.fields[1].name = ASCIIToUTF16("Pwd");
form_data.fields[1].value = ASCIIToUTF16("1234");
+ form_data.fields[1].unique_renderer_id = 4;
PasswordForm second_form;
second_form.form_data = form_data;
@@ -1246,6 +1311,7 @@ TEST_F(PasswordManagerTest, FillPasswordOnManyFrames_SameId) {
.WillOnce(WithArg<1>(InvokeConsumer(second_form)));
EXPECT_CALL(driver_b, FillPasswordForm(_));
manager()->OnPasswordFormsParsed(&driver_b, {second_form});
+ task_runner_->FastForwardUntilNoTasksRemain();
}
// If kNewPasswordFormParsing is disabled, "similar" is governed by
@@ -1291,7 +1357,7 @@ TEST_F(PasswordManagerTest, SameDocumentNavigation) {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
- manager()->OnSameDocumentNavigation(&driver_, form);
+ manager()->OnPasswordFormSubmittedNoChecks(&driver_, form);
ASSERT_TRUE(form_manager_to_save);
// Simulate saving the form, as if the info bar was accepted.
@@ -1326,7 +1392,7 @@ TEST_F(PasswordManagerTest, SameDocumentBlacklistedSite) {
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
.WillOnce(WithArg<0>(SaveToScopedPtr(&form_manager_to_save)));
- manager()->OnSameDocumentNavigation(&driver_, form);
+ manager()->OnPasswordFormSubmittedNoChecks(&driver_, form);
EXPECT_TRUE(form_manager_to_save->IsBlacklisted());
}
@@ -1803,12 +1869,15 @@ TEST_F(PasswordManagerTest, PasswordGenerationPresavePasswordAndLogin) {
manager()->OnPasswordFormsRendered(&driver_, observed, true);
// The user accepts generated password and makes successful login.
- EXPECT_CALL(*store_, AddLogin(form)).WillOnce(Return());
+ PasswordForm presaved_form(form);
+ if (found_matched_logins_in_store)
+ presaved_form.username_value.clear();
+ EXPECT_CALL(*store_, AddLogin(presaved_form)).WillOnce(Return());
manager()->OnPresaveGeneratedPassword(form);
::testing::Mock::VerifyAndClearExpectations(store_.get());
if (!found_matched_logins_in_store)
- EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, form));
+ EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, presaved_form));
OnPasswordFormSubmitted(form);
observed.clear();
manager()->DidNavigateMainFrame();
@@ -1819,8 +1888,9 @@ TEST_F(PasswordManagerTest, PasswordGenerationPresavePasswordAndLogin) {
if (found_matched_logins_in_store) {
// Credentials should be updated only when the user explicitly chooses.
ASSERT_TRUE(form_manager);
- EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, form));
+ EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(_, presaved_form));
form_manager->Update(form_manager->GetPendingCredentials());
+ ::testing::Mock::VerifyAndClearExpectations(store_.get());
}
}
}
@@ -2086,8 +2156,8 @@ TEST_F(PasswordManagerTest, NotSavingSyncPasswordHash_NoUsername) {
// Simulate that this credentials which is similar to be sync credentials.
client_.FilterAllResultsForSaving();
- // Check that no sync credential password hash is saved.
- EXPECT_CALL(*store_, SaveSyncPasswordHash(_, _, _)).Times(0);
+ // Check that no Gaia credential password hash is saved.
+ EXPECT_CALL(*store_, SaveGaiaPasswordHash(_, _, _)).Times(0);
OnPasswordFormSubmitted(form);
observed.clear();
manager()->OnPasswordFormsRendered(&driver_, observed, true);
@@ -2107,9 +2177,9 @@ TEST_F(PasswordManagerTest, NotSavingSyncPasswordHash_NotSyncCredentials) {
EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
.WillRepeatedly(Return(true));
- // Check that no sync credential password hash is saved since these
+ // Check that no Gaia credential password hash is saved since these
// credentials are eligible for saving.
- EXPECT_CALL(*store_, SaveSyncPasswordHash(_, _, _)).Times(0);
+ EXPECT_CALL(*store_, SaveGaiaPasswordHash(_, _, _)).Times(0);
std::unique_ptr<PasswordFormManager> form_manager_to_save;
EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr(_))
@@ -2291,7 +2361,7 @@ TEST_F(PasswordManagerTest, ProcessAutofillPredictions) {
std::string response_string;
ASSERT_TRUE(response.SerializeToString(&response_string));
- FormStructure::ParseQueryResponse(response_string, forms);
+ FormStructure::ParseQueryResponse(response_string, forms, nullptr);
// Check that Autofill predictions are converted to password related
// predictions.
@@ -2356,11 +2426,13 @@ TEST_F(PasswordManagerTest, SaveSyncPasswordHashOnChangePasswordPage) {
.WillRepeatedly(Return(true));
EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
- ON_CALL(*client_.GetStoreResultFilter(), ShouldSavePasswordHash(_))
+ ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
+ .WillByDefault(Return(true));
+ ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
.WillByDefault(Return(true));
EXPECT_CALL(
*store_,
- SaveSyncPasswordHash(
+ SaveGaiaPasswordHash(
"googleuser", form.new_password_value,
metrics_util::SyncPasswordHashChange::CHANGED_IN_CONTENT_AREA));
#endif
@@ -2372,6 +2444,68 @@ TEST_F(PasswordManagerTest, SaveSyncPasswordHashOnChangePasswordPage) {
manager()->OnPasswordFormsRendered(&driver_, observed, true);
}
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+// Non-Sync Gaia password hash should be saved upon submission of Gaia login
+// page.
+TEST_F(PasswordManagerTest, SaveOtherGaiaPasswordHash) {
+ PasswordForm form(MakeSimpleGAIAForm());
+ EXPECT_CALL(*store_, GetLogins(_, _))
+ .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
+
+ std::vector<PasswordForm> observed;
+ observed.push_back(form);
+ manager()->OnPasswordFormsParsed(&driver_, observed);
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+ // Submit form and finish navigation.
+ EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
+
+ ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
+ .WillByDefault(Return(true));
+ EXPECT_CALL(
+ *store_,
+ SaveGaiaPasswordHash(
+ "googleuser", form.password_value,
+ metrics_util::SyncPasswordHashChange::NOT_SYNC_PASSWORD_CHANGE));
+
+ client_.FilterAllResultsForSaving();
+ OnPasswordFormSubmitted(form);
+
+ observed.clear();
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+}
+
+// Enterprise password hash should be saved upon submission of enterprise login
+// page.
+TEST_F(PasswordManagerTest, SaveEnterprisePasswordHash) {
+ PasswordForm form(MakeSimpleForm());
+ EXPECT_CALL(*store_, GetLogins(_, _))
+ .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
+
+ std::vector<PasswordForm> observed;
+ observed.push_back(form);
+ manager()->OnPasswordFormsParsed(&driver_, observed);
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+
+ // Submit form and finish navigation.
+ EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(client_, GetPrefs()).WillRepeatedly(Return(nullptr));
+ ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveEnterprisePasswordHash(_))
+ .WillByDefault(Return(true));
+ ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
+ .WillByDefault(Return(false));
+ EXPECT_CALL(*store_,
+ SaveEnterprisePasswordHash("googleuser", form.password_value));
+ client_.FilterAllResultsForSaving();
+ OnPasswordFormSubmitted(form);
+
+ observed.clear();
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+}
+#endif
+
// If there are no forms to parse, certificate errors should not be reported.
TEST_F(PasswordManagerTest, CertErrorReported_NoForms) {
const std::vector<PasswordForm> observed;
@@ -2451,4 +2585,39 @@ TEST_F(PasswordManagerTest, CreatingFormManagers) {
EXPECT_EQ(1u, manager()->form_managers().size());
}
+TEST_F(PasswordManagerTest,
+ ShowManualFallbacksDontChangeProvisionalSaveManager) {
+ EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
+ .WillRepeatedly(Return(true));
+
+ std::vector<PasswordForm> observed;
+ PasswordForm form(MakeSimpleForm());
+ observed.push_back(form);
+ EXPECT_CALL(*store_, GetLogins(_, _))
+ .WillRepeatedly(WithArg<1>(InvokeConsumer(form)));
+ EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
+ manager()->OnPasswordFormsParsed(&driver_, observed);
+ manager()->OnPasswordFormsRendered(&driver_, observed, true);
+
+ EXPECT_THAT(manager()->provisional_save_manager(), IsNull());
+ manager()->ShowManualFallbackForSaving(&driver_, form);
+ EXPECT_THAT(manager()->provisional_save_manager(), IsNull());
+
+ // The user submits the form and a provisional save manager is set.
+ OnPasswordFormSubmitted(form);
+
+ EXPECT_THAT(manager()->provisional_save_manager(), NotNull());
+ const PasswordFormManager* last_provisional_save_manager =
+ manager()->provisional_save_manager();
+
+ EXPECT_CALL(client_, HideManualFallbackForSaving());
+ // The call to manual fallback with |form| equal to already saved should close
+ // the fallback.
+ manager()->ShowManualFallbackForSaving(&driver_, form);
+
+ EXPECT_THAT(manager()->provisional_save_manager(), NotNull());
+ EXPECT_EQ(last_provisional_save_manager,
+ manager()->provisional_save_manager());
+}
+
} // 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 8471ae39eb4..af3feb482c1 100644
--- a/chromium/components/password_manager/core/browser/password_manager_util.cc
+++ b/chromium/components/password_manager/core/browser/password_manager_util.cc
@@ -85,7 +85,7 @@ bool IsBetterMatch(const PasswordForm* lhs, const PasswordForm* rhs) {
} // namespace
-password_manager::PasswordSyncState GetPasswordSyncState(
+password_manager::SyncState GetPasswordSyncState(
const syncer::SyncService* sync_service) {
if (sync_service && sync_service->IsFirstSetupComplete() &&
sync_service->IsSyncActive() &&
@@ -94,7 +94,21 @@ password_manager::PasswordSyncState GetPasswordSyncState(
? password_manager::SYNCING_WITH_CUSTOM_PASSPHRASE
: password_manager::SYNCING_NORMAL_ENCRYPTION;
}
- return password_manager::NOT_SYNCING_PASSWORDS;
+ return password_manager::NOT_SYNCING;
+}
+
+password_manager::SyncState GetHistorySyncState(
+ const syncer::SyncService* sync_service) {
+ if (sync_service && sync_service->IsFirstSetupComplete() &&
+ sync_service->IsSyncActive() &&
+ (sync_service->GetActiveDataTypes().Has(
+ syncer::HISTORY_DELETE_DIRECTIVES) ||
+ sync_service->GetActiveDataTypes().Has(syncer::PROXY_TABS))) {
+ return sync_service->IsUsingSecondaryPassphrase()
+ ? password_manager::SYNCING_WITH_CUSTOM_PASSPHRASE
+ : password_manager::SYNCING_NORMAL_ENCRYPTION;
+ }
+ return password_manager::NOT_SYNCING;
}
void FindDuplicates(
@@ -155,10 +169,8 @@ bool IsLoggingActive(const password_manager::PasswordManagerClient* client) {
}
bool ManualPasswordGenerationEnabled(syncer::SyncService* sync_service) {
- if (!(base::FeatureList::IsEnabled(
- password_manager::features::kEnableManualPasswordGeneration) &&
- (password_manager_util::GetPasswordSyncState(sync_service) ==
- password_manager::SYNCING_NORMAL_ENCRYPTION))) {
+ if (password_manager_util::GetPasswordSyncState(sync_service) !=
+ password_manager::SYNCING_NORMAL_ENCRYPTION) {
return false;
}
LogPasswordGenerationEvent(
@@ -168,8 +180,7 @@ bool ManualPasswordGenerationEnabled(syncer::SyncService* sync_service) {
bool ShowAllSavedPasswordsContextMenuEnabled() {
if (!base::FeatureList::IsEnabled(
- password_manager::features::
- kEnableShowAllSavedPasswordsContextMenu)) {
+ password_manager::features::kShowAllSavedPasswordsContextMenu)) {
return false;
}
LogContextOfShowAllSavedPasswordsShown(
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 c989c9239fb..f95e92812e1 100644
--- a/chromium/components/password_manager/core/browser/password_manager_util.h
+++ b/chromium/components/password_manager/core/browser/password_manager_util.h
@@ -33,8 +33,13 @@ class PrefService;
namespace password_manager_util {
// Reports whether and how passwords are currently synced. In particular, for a
-// null |sync_service| returns NOT_SYNCING_PASSWORDS.
-password_manager::PasswordSyncState GetPasswordSyncState(
+// null |sync_service| returns NOT_SYNCING.
+password_manager::SyncState GetPasswordSyncState(
+ const syncer::SyncService* sync_service);
+
+// Reports whether and how browsing history is currently synced. In particular,
+// for a null |sync_service| returns NOT_SYNCING.
+password_manager::SyncState GetHistorySyncState(
const syncer::SyncService* sync_service);
// Finds the forms with a duplicate sync tags in |forms|. The first one of
diff --git a/chromium/components/password_manager/core/browser/password_requirements_service.cc b/chromium/components/password_manager/core/browser/password_requirements_service.cc
new file mode 100644
index 00000000000..f7019ab1646
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_requirements_service.cc
@@ -0,0 +1,99 @@
+// 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/password_manager/core/browser/password_requirements_service.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+
+#include "components/autofill/core/browser/password_requirements_spec_printer.h"
+
+namespace {
+constexpr size_t kCacheSizeForDomainKeyedSpecs = 200;
+constexpr size_t kCacheSizeForSignatureKeyedSpecs = 500;
+} // namespace
+
+namespace password_manager {
+
+PasswordRequirementsService::PasswordRequirementsService(
+ std::unique_ptr<autofill::PasswordRequirementsSpecFetcher> fetcher)
+ : specs_for_domains_(kCacheSizeForDomainKeyedSpecs),
+ specs_for_signatures_(kCacheSizeForSignatureKeyedSpecs),
+ fetcher_(std::move(fetcher)) {}
+
+PasswordRequirementsService::~PasswordRequirementsService() = default;
+
+autofill::PasswordRequirementsSpec PasswordRequirementsService::GetSpec(
+ const GURL& main_frame_domain,
+ autofill::FormSignature form_signature,
+ autofill::FieldSignature field_signature) {
+ autofill::PasswordRequirementsSpec result;
+
+ auto iter_by_signature = specs_for_signatures_.Get(
+ std::make_pair(form_signature, field_signature));
+ bool found_item_by_signature =
+ iter_by_signature != specs_for_signatures_.end();
+ if (found_item_by_signature) {
+ result = iter_by_signature->second;
+ }
+
+ auto iter_by_domain = specs_for_domains_.Get(main_frame_domain);
+ if (iter_by_domain != specs_for_domains_.end()) {
+ const autofill::PasswordRequirementsSpec& spec = iter_by_domain->second;
+ if (!found_item_by_signature) {
+ // If nothing was found by signature, |spec| can be adopted.
+ result = spec;
+ } else if (spec.has_priority() && (!result.has_priority() ||
+ spec.priority() > result.priority())) {
+ // If something was found by signature, override with |spec| only in case
+ // the priority of |spec| exceeds the priority of the data found by
+ // signature.
+ result = spec;
+ }
+ }
+
+ VLOG(1) << "PasswordRequirementsService::GetSpec(" << main_frame_domain
+ << ", " << form_signature << ", " << field_signature
+ << ") = " << result;
+
+ return result;
+}
+
+void PasswordRequirementsService::PrefetchSpec(const GURL& main_frame_domain) {
+ VLOG(1) << "PasswordRequirementsService::PrefetchSpec(" << main_frame_domain
+ << ")";
+
+ if (!fetcher_) {
+ VLOG(1) << "PasswordRequirementsService::PrefetchSpec has no fetcher";
+ return;
+ }
+
+ // Using base::Unretained(this) is safe here because the
+ // PasswordRequirementsService owns fetcher_. If |this| is deleted, so is
+ // the |fetcher_|, and no callback can happen.
+ fetcher_->Fetch(
+ main_frame_domain,
+ base::BindOnce(&PasswordRequirementsService::OnFetchedRequirements,
+ base::Unretained(this), main_frame_domain));
+}
+
+void PasswordRequirementsService::OnFetchedRequirements(
+ const GURL& main_frame_domain,
+ const autofill::PasswordRequirementsSpec& spec) {
+ VLOG(1) << "PasswordRequirementsService::OnFetchedRequirements("
+ << main_frame_domain << ", " << spec << ")";
+ specs_for_domains_.Put(main_frame_domain, spec);
+}
+
+void PasswordRequirementsService::AddSpec(
+ autofill::FormSignature form_signature,
+ autofill::FieldSignature field_signature,
+ const autofill::PasswordRequirementsSpec& spec) {
+ VLOG(1) << "PasswordRequirementsService::AddSpec(" << form_signature << ", "
+ << field_signature << ", " << spec << ")";
+ specs_for_signatures_.Put(std::make_pair(form_signature, field_signature),
+ spec);
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_requirements_service.h b/chromium/components/password_manager/core/browser/password_requirements_service.h
new file mode 100644
index 00000000000..66d62b65ac4
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_requirements_service.h
@@ -0,0 +1,72 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REQUIREMENTS_SERVICE_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REQUIREMENTS_SERVICE_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/containers/mru_cache.h"
+#include "components/autofill/core/browser/password_requirements_spec_fetcher.h"
+#include "components/autofill/core/browser/proto/password_requirements.pb.h"
+#include "components/autofill/core/common/signatures_util.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "url/gurl.h"
+
+namespace autofill {
+class PasswordRequirementsSpec;
+}
+
+namespace password_manager {
+
+// A service that fetches, stores and returns requirements for generating a
+// random password on a specific form and site.
+class PasswordRequirementsService : public KeyedService {
+ public:
+ // If |fetcher| is a nullptr, no network requests happen.
+ explicit PasswordRequirementsService(
+ std::unique_ptr<autofill::PasswordRequirementsSpecFetcher> fetcher);
+ ~PasswordRequirementsService() override;
+
+ // Returns the password requirements for a field that appears on a site
+ // with domain |main_frame_domain| and has the specified |form_signature|
+ // and |field_signature|.
+ //
+ // This function returns synchronously and only returns results if these
+ // have been retrieved via the Add/Prefetch methods below and the data is
+ // still in the cache.
+ autofill::PasswordRequirementsSpec GetSpec(
+ const GURL& main_frame_domain,
+ autofill::FormSignature form_signature,
+ autofill::FieldSignature field_signature);
+
+ // Triggers a fetch for password requirements for the domain passed in
+ // |main_frame_domain| and stores it into the MRU cache.
+ void PrefetchSpec(const GURL& main_frame_domain);
+
+ // Stores the password requirements for the field identified via
+ // |form_signature| and |field_signature| in the MRU cache.
+ void AddSpec(autofill::FormSignature form_signature,
+ autofill::FieldSignature field_signature,
+ const autofill::PasswordRequirementsSpec& spec);
+
+ private:
+ void OnFetchedRequirements(const GURL& main_frame_domain,
+ const autofill::PasswordRequirementsSpec& spec);
+
+ using FullSignature =
+ std::pair<autofill::FormSignature, autofill::FieldSignature>;
+ base::MRUCache<GURL, autofill::PasswordRequirementsSpec> specs_for_domains_;
+ base::MRUCache<FullSignature, autofill::PasswordRequirementsSpec>
+ specs_for_signatures_;
+ // May be a nullptr.
+ std::unique_ptr<autofill::PasswordRequirementsSpecFetcher> fetcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordRequirementsService);
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_REQUIREMENTS_SERVICE_H_
diff --git a/chromium/components/password_manager/core/browser/password_requirements_service_unittest.cc b/chromium/components/password_manager/core/browser/password_requirements_service_unittest.cc
new file mode 100644
index 00000000000..db518425e18
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/password_requirements_service_unittest.cc
@@ -0,0 +1,159 @@
+// 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/password_manager/core/browser/password_requirements_service.h"
+
+#include <map>
+
+#include "base/logging.h"
+#include "base/test/bind_test_util.h"
+#include "components/autofill/core/browser/password_requirements_spec_fetcher.h"
+#include "components/autofill/core/browser/proto/password_requirements.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+
+namespace {
+
+class MockPasswordRequirementsSpecFetcher
+ : public autofill::PasswordRequirementsSpecFetcher {
+ public:
+ MockPasswordRequirementsSpecFetcher() = default;
+ ~MockPasswordRequirementsSpecFetcher() override = default;
+
+ void Fetch(GURL origin, FetchCallback callback) override {
+ auto iter = data_to_return_.find(origin);
+ std::move(callback).Run(iter != data_to_return_.end()
+ ? iter->second
+ : autofill::PasswordRequirementsSpec());
+ }
+
+ void SetDataToReturn(const GURL& origin,
+ const autofill::PasswordRequirementsSpec& spec) {
+ data_to_return_[origin] = spec;
+ }
+
+ private:
+ std::map<GURL, autofill::PasswordRequirementsSpec> data_to_return_;
+};
+
+class PasswordRequirementsServiceTest : public testing::Test {
+ public:
+ PasswordRequirementsServiceTest()
+ : test_origin_("http://www.example.com"),
+ // Ownership is passed to the |service_| below.
+ fetcher_ptr_(new MockPasswordRequirementsSpecFetcher()),
+ service_(std::unique_ptr<MockPasswordRequirementsSpecFetcher>(
+ fetcher_ptr_)) {}
+ ~PasswordRequirementsServiceTest() override = default;
+
+ protected:
+ // Prepopulated test data.
+ GURL test_origin_;
+ autofill::FormSignature test_form_signature_ = 123;
+ autofill::FieldSignature test_field_signature_ = 22;
+
+ // Weak pointer.
+ MockPasswordRequirementsSpecFetcher* fetcher_ptr_;
+ PasswordRequirementsService service_;
+};
+
+TEST_F(PasswordRequirementsServiceTest, ExerciseEverything) {
+ // The following specs are names according to the following scheme:
+ // spec_l${max_length value}_p${priority value}
+ // values of 0 imply that no value is specified.
+ // It would be possible to test the behavior with fewer instances than below
+ // but these are chosen to be representative of what we expect the server
+ // to send with regards to priorities.
+ autofill::PasswordRequirementsSpec spec_l0_p0; // empty spec.
+ autofill::PasswordRequirementsSpec spec_l7_p0;
+ spec_l7_p0.set_max_length(7u);
+ autofill::PasswordRequirementsSpec spec_l8_p10;
+ spec_l8_p10.set_max_length(8u);
+ spec_l8_p10.set_priority(10);
+ autofill::PasswordRequirementsSpec spec_l9_p20;
+ spec_l9_p20.set_max_length(9u);
+ spec_l9_p20.set_priority(20);
+ autofill::PasswordRequirementsSpec spec_l10_p30;
+ spec_l10_p30.set_max_length(10u);
+ spec_l10_p30.set_priority(30);
+
+ struct {
+ const char* test_name;
+ autofill::PasswordRequirementsSpec* spec_for_signature = nullptr;
+ autofill::PasswordRequirementsSpec* spec_for_domain = nullptr;
+ autofill::PasswordRequirementsSpec* expected;
+ } tests[] = {
+ {
+ .test_name = "No data prefechted", .expected = &spec_l0_p0,
+ },
+ {
+ .test_name = "Only domain wide spec",
+ .spec_for_domain = &spec_l7_p0,
+ .expected = &spec_l7_p0,
+ },
+ {
+ .test_name = "Only signature based spec",
+ .spec_for_signature = &spec_l7_p0,
+ .expected = &spec_l7_p0,
+ },
+ {
+ .test_name = "Domain spec can override spec based on signature",
+ .spec_for_signature = &spec_l8_p10,
+ .spec_for_domain = &spec_l9_p20,
+ .expected = &spec_l9_p20, // priority 20 trumps priority 10.
+ },
+ {
+ .test_name = "Signature spec can override spec based on domain",
+ .spec_for_signature = &spec_l10_p30,
+ .spec_for_domain = &spec_l9_p20,
+ .expected = &spec_l10_p30, // priority 30 trumps priority 20.
+ },
+ {
+ .test_name = "Dealing with unset priority in domain",
+ .spec_for_signature = &spec_l8_p10,
+ .spec_for_domain = &spec_l7_p0, // No prioritiy specified.
+ .expected = &spec_l8_p10,
+ },
+ {
+ .test_name = "Dealing with unset priority in signature",
+ .spec_for_signature = &spec_l7_p0, // No prioritiy specified.
+ .spec_for_domain = &spec_l8_p10,
+ .expected = &spec_l8_p10,
+ },
+ };
+
+ for (const auto& test : tests) {
+ SCOPED_TRACE(test.test_name);
+
+ // Populate the service with data.
+ if (test.spec_for_domain) {
+ fetcher_ptr_->SetDataToReturn(test_origin_, *(test.spec_for_domain));
+ service_.PrefetchSpec(test_origin_);
+ }
+ if (test.spec_for_signature) {
+ service_.AddSpec(test_form_signature_, test_field_signature_,
+ *(test.spec_for_signature));
+ }
+
+ // Perform lookup.
+ auto result = service_.GetSpec(test_origin_, test_form_signature_,
+ test_field_signature_);
+
+ // Validate answer.
+ EXPECT_EQ(test.expected->has_priority(), result.has_priority());
+ if (test.expected->has_priority()) {
+ EXPECT_EQ(test.expected->priority(), result.priority());
+ }
+
+ EXPECT_EQ(test.expected->has_max_length(), result.has_max_length());
+ if (test.expected->has_max_length()) {
+ EXPECT_EQ(test.expected->max_length(), result.max_length());
+ }
+ }
+}
+
+} // namespace
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_reuse_detection_manager.cc b/chromium/components/password_manager/core/browser/password_reuse_detection_manager.cc
index ac53aa59813..6eceda3b4ef 100644
--- a/chromium/components/password_manager/core/browser/password_reuse_detection_manager.cc
+++ b/chromium/components/password_manager/core/browser/password_reuse_detection_manager.cc
@@ -8,7 +8,6 @@
#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/browser/password_manager_client.h"
-#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
@@ -79,14 +78,22 @@ void PasswordReuseDetectionManager::OnReuseFound(
int saved_passwords) {
reuse_on_this_page_was_found_ = true;
std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
- bool matches_sync_password = reused_protected_password_hash &&
- reused_protected_password_hash->is_gaia_password;
+ metrics_util::PasswordType reused_password_type = GetReusedPasswordType(
+ reused_protected_password_hash, matching_domains.size());
+
if (password_manager_util::IsLoggingActive(client_)) {
logger.reset(
new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
std::vector<std::string> domains_to_log(matching_domains);
- if (matches_sync_password)
+ if (reused_password_type == metrics_util::PasswordType::SYNC_PASSWORD) {
domains_to_log.push_back("CHROME SYNC PASSWORD");
+ } else if (reused_password_type ==
+ metrics_util::PasswordType::OTHER_GAIA_PASSWORD) {
+ domains_to_log.push_back("OTHER GAIA PASSWORD");
+ } else if (reused_password_type ==
+ metrics_util::PasswordType::ENTERPRISE_PASSWORD) {
+ domains_to_log.push_back("ENTERPRISE PASSWORD");
+ }
// TODO(nparker): Implement LogList() to log all domains in one call.
for (const auto& domain : domains_to_log) {
logger->LogString(BrowserSavePasswordProgressLogger::STRING_REUSE_FOUND,
@@ -102,15 +109,14 @@ void PasswordReuseDetectionManager::OnReuseFound(
metrics_util::LogPasswordReuse(password_length, saved_passwords,
matching_domains.size(),
- password_field_detected);
+ password_field_detected, reused_password_type);
#if defined(SAFE_BROWSING_DB_LOCAL)
// TODO(jialiul): After CSD whitelist being added to Android, we should gate
// this by either SAFE_BROWSING_DB_LOCAL or SAFE_BROWSING_DB_REMOTE.
- if (matches_sync_password) {
+ if (reused_password_type == metrics_util::PasswordType::SYNC_PASSWORD)
client_->LogPasswordReuseDetectedEvent();
- }
- client_->CheckProtectedPasswordEntry(matches_sync_password, matching_domains,
+ client_->CheckProtectedPasswordEntry(reused_password_type, matching_domains,
password_field_detected);
#endif
}
@@ -119,4 +125,22 @@ void PasswordReuseDetectionManager::SetClockForTesting(base::Clock* clock) {
clock_ = clock;
}
+metrics_util::PasswordType PasswordReuseDetectionManager::GetReusedPasswordType(
+ base::Optional<PasswordHashData> reused_protected_password_hash,
+ size_t matching_domain_count) {
+ if (!reused_protected_password_hash.has_value()) {
+ DCHECK_GT(matching_domain_count, 0u);
+ return metrics_util::PasswordType::SAVED_PASSWORD;
+ }
+
+ if (!reused_protected_password_hash->is_gaia_password) {
+ return metrics_util::PasswordType::ENTERPRISE_PASSWORD;
+ } else if (client_->GetStoreResultFilter()->IsSyncAccountEmail(
+ reused_protected_password_hash->username)) {
+ return metrics_util::PasswordType::SYNC_PASSWORD;
+ } else {
+ return metrics_util::PasswordType::OTHER_GAIA_PASSWORD;
+ }
+}
+
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/password_reuse_detection_manager.h b/chromium/components/password_manager/core/browser/password_reuse_detection_manager.h
index 990c59e77c9..0d4bd50f8fb 100644
--- a/chromium/components/password_manager/core/browser/password_reuse_detection_manager.h
+++ b/chromium/components/password_manager/core/browser/password_reuse_detection_manager.h
@@ -8,6 +8,7 @@
#include "base/macros.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_reuse_detector_consumer.h"
#include "url/gurl.h"
@@ -39,6 +40,10 @@ class PasswordReuseDetectionManager : public PasswordReuseDetectorConsumer {
void SetClockForTesting(base::Clock* clock);
private:
+ // Determines the type of password being reused.
+ metrics_util::PasswordType GetReusedPasswordType(
+ base::Optional<PasswordHashData> reused_protected_password_hash,
+ size_t match_domain_count);
PasswordManagerClient* client_;
base::string16 input_characters_;
GURL main_frame_url_;
diff --git a/chromium/components/password_manager/core/browser/password_reuse_detection_manager_unittest.cc b/chromium/components/password_manager/core/browser/password_reuse_detection_manager_unittest.cc
index 7e7efda3667..b0c6dfb5adc 100644
--- a/chromium/components/password_manager/core/browser/password_reuse_detection_manager_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_reuse_detection_manager_unittest.cc
@@ -59,7 +59,7 @@ class PasswordReuseDetectionManagerTest : public ::testing::Test {
};
// Verify that CheckReuse is called on each key pressed event with an argument
-// equal to the last 30 keystrokes typed after the last main frame navigaion.
+// equal to the last 30 keystrokes typed after the last main frame navigation.
TEST_F(PasswordReuseDetectionManagerTest, CheckReuseCalled) {
const GURL gurls[] = {GURL("https://www.example.com"),
GURL("https://www.otherexample.com")};
@@ -137,7 +137,7 @@ TEST_F(PasswordReuseDetectionManagerTest, NoReuseCheckingAfterReuseFound) {
PasswordReuseDetectionManager manager(&client_);
// Simulate that reuse found.
- manager.OnReuseFound(0ul, base::nullopt, {}, 0);
+ manager.OnReuseFound(0ul, base::nullopt, {"https://example.com"}, 0);
// Expect no checking of reuse.
EXPECT_CALL(*store_, CheckReuse(_, _, _)).Times(0);
@@ -149,7 +149,7 @@ TEST_F(PasswordReuseDetectionManagerTest, NoReuseCheckingAfterReuseFound) {
manager.OnKeyPressed(base::ASCIIToUTF16("1"));
}
-// Verify that keystoke buffer is cleared only on cross host navigation.
+// Verify that keystroke buffer is cleared only on cross host navigation.
TEST_F(PasswordReuseDetectionManagerTest, DidNavigateMainFrame) {
EXPECT_CALL(client_, GetPasswordStore())
.WillRepeatedly(testing::Return(store_.get()));
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 5fda9755626..14f08a9735a 100644
--- a/chromium/components/password_manager/core/browser/password_reuse_detector.cc
+++ b/chromium/components/password_manager/core/browser/password_reuse_detector.cc
@@ -9,6 +9,7 @@
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/hash_password_manager.h"
+#include "components/password_manager/core/browser/password_hash_data.h"
#include "components/password_manager/core/browser/password_reuse_detector_consumer.h"
#include "components/password_manager/core/browser/psl_matching_helper.h"
#include "components/safe_browsing/common/safe_browsing_prefs.h"
@@ -35,21 +36,28 @@ bool IsSuffix(const base::string16& str,
str.rbegin());
}
-// Helper function to returns matching PasswordHashData from a list.
+// Helper function to returns matching PasswordHashData from a list that has
+// the longest password length.
base::Optional<PasswordHashData> FindPasswordReuse(
const base::string16& input,
const std::vector<PasswordHashData>& password_hash_list) {
+ base::Optional<PasswordHashData> longest_match = base::nullopt;
+ size_t longest_match_size = 0;
for (const PasswordHashData& hash_data : password_hash_list) {
if (input.size() < hash_data.length)
continue;
size_t offset = input.size() - hash_data.length;
base::string16 reuse_candidate = input.substr(offset);
- if (HashPasswordManager::CalculatePasswordHash(
- reuse_candidate, hash_data.salt) == hash_data.hash) {
- return hash_data;
+ // It is possible that input matches multiple passwords in the list,
+ // we only return the first match due to simplicity.
+ if (CalculatePasswordHash(reuse_candidate, hash_data.salt) ==
+ hash_data.hash &&
+ hash_data.length > longest_match_size) {
+ longest_match_size = hash_data.length;
+ longest_match = hash_data;
}
}
- return base::nullopt;
+ return longest_match;
}
} // namespace
@@ -60,7 +68,7 @@ bool ReverseStringLess::operator()(const base::string16& lhs,
rhs.rend());
}
-PasswordReuseDetector::PasswordReuseDetector() : prefs_(nullptr) {}
+PasswordReuseDetector::PasswordReuseDetector() {}
PasswordReuseDetector::~PasswordReuseDetector() {}
@@ -111,19 +119,16 @@ void PasswordReuseDetector::CheckReuse(
if (max_reused_password_length == 0)
return;
- if (gaia_reused_password_length == 0 &&
- enterprise_reused_password_length == 0) {
- consumer->OnReuseFound(max_reused_password_length, base::nullopt,
- matching_domains, saved_passwords_);
- } else if (gaia_reused_password_length > enterprise_reused_password_length) {
- consumer->OnReuseFound(max_reused_password_length,
- reused_gaia_password_hash, matching_domains,
- saved_passwords_);
- } else {
- consumer->OnReuseFound(max_reused_password_length,
- reused_enterprise_password_hash, matching_domains,
- saved_passwords_);
+ base::Optional<PasswordHashData> reused_protected_password_hash =
+ base::nullopt;
+ if (gaia_reused_password_length > enterprise_reused_password_length) {
+ reused_protected_password_hash = std::move(reused_gaia_password_hash);
+ } else if (enterprise_reused_password_length != 0) {
+ reused_protected_password_hash = std::move(reused_enterprise_password_hash);
}
+ consumer->OnReuseFound(max_reused_password_length,
+ reused_protected_password_hash, matching_domains,
+ saved_passwords_);
}
base::Optional<PasswordHashData> PasswordReuseDetector::CheckGaiaPasswordReuse(
@@ -155,10 +160,9 @@ PasswordReuseDetector::CheckNonGaiaEnterprisePasswordReuse(
// Skips password reuse check if |domain| matches enterprise login URL or
// enterprise change password URL.
GURL page_url(domain);
- if (!prefs_ ||
- safe_browsing::MatchesPasswordProtectionLoginURL(page_url, *prefs_) ||
- safe_browsing::MatchesPasswordProtectionChangePasswordURL(page_url,
- *prefs_)) {
+ if (enterprise_password_urls_.has_value() &&
+ safe_browsing::MatchesURLList(page_url,
+ enterprise_password_urls_.value())) {
return base::nullopt;
}
@@ -223,6 +227,20 @@ void PasswordReuseDetector::UseNonGaiaEnterprisePasswordHash(
enterprise_password_hash_data_list_ = std::move(password_hash_data_list);
}
+void PasswordReuseDetector::UseEnterprisePasswordURLs(
+ base::Optional<std::vector<GURL>> enterprise_login_urls,
+ base::Optional<GURL> enterprise_change_password_url) {
+ enterprise_password_urls_ = std::move(enterprise_login_urls);
+ if (!enterprise_change_password_url.has_value() ||
+ !enterprise_change_password_url->is_valid()) {
+ return;
+ }
+
+ if (!enterprise_password_urls_)
+ enterprise_password_urls_ = base::make_optional<std::vector<GURL>>();
+ enterprise_password_urls_->push_back(enterprise_change_password_url.value());
+}
+
void PasswordReuseDetector::ClearGaiaPasswordHash(const std::string& username) {
if (!gaia_password_hash_data_list_)
return;
diff --git a/chromium/components/password_manager/core/browser/password_reuse_detector.h b/chromium/components/password_manager/core/browser/password_reuse_detector.h
index 1397120c9b0..e244bdde509 100644
--- a/chromium/components/password_manager/core/browser/password_reuse_detector.h
+++ b/chromium/components/password_manager/core/browser/password_reuse_detector.h
@@ -65,14 +65,18 @@ class PasswordReuseDetector : public PasswordStoreConsumer {
void UseNonGaiaEnterprisePasswordHash(
base::Optional<std::vector<PasswordHashData>> password_hash_data_list);
+ // Stores enterprise login URLs and change password URL.
+ // These URLs should be skipped in enterprise password reuse checking.
+ void UseEnterprisePasswordURLs(
+ base::Optional<std::vector<GURL>> enterprise_login_urls,
+ base::Optional<GURL> enterprise_change_password_url);
+
void ClearGaiaPasswordHash(const std::string& username);
void ClearAllGaiaPasswordHash();
void ClearAllEnterprisePasswordHash();
- void SetPrefs(PrefService* prefs) { prefs_ = prefs; }
-
private:
using passwords_iterator = std::map<base::string16,
std::set<std::string>,
@@ -127,7 +131,7 @@ class PasswordReuseDetector : public PasswordStoreConsumer {
base::Optional<std::vector<PasswordHashData>>
enterprise_password_hash_data_list_;
- PrefService* prefs_;
+ base::Optional<std::vector<GURL>> enterprise_password_urls_;
DISALLOW_COPY_AND_ASSIGN(PasswordReuseDetector);
};
diff --git a/chromium/components/password_manager/core/browser/password_reuse_detector_unittest.cc b/chromium/components/password_manager/core/browser/password_reuse_detector_unittest.cc
index 7fff3e93650..f58a6bc37d1 100644
--- a/chromium/components/password_manager/core/browser/password_reuse_detector_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_reuse_detector_unittest.cc
@@ -14,10 +14,6 @@
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/hash_password_manager.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -104,15 +100,13 @@ std::vector<PasswordHashData> PrepareEnterprisePasswordData(
return result;
}
-void ConfigureEnterprisePasswordProtection(TestingPrefServiceSimple* prefs) {
- prefs->registry()->RegisterStringPref(
- prefs::kPasswordProtectionChangePasswordURL, "");
- prefs->registry()->RegisterListPref(prefs::kPasswordProtectionLoginURLs);
- prefs->SetString(prefs::kPasswordProtectionChangePasswordURL,
- "https://changepassword.example.com/");
- base::ListValue login_urls;
- login_urls.AppendString("https://login.example.com");
- prefs->Set(prefs::kPasswordProtectionLoginURLs, login_urls);
+void ConfigureEnterprisePasswordProtection(
+ PasswordReuseDetector* reuse_detector) {
+ base::Optional<std::vector<GURL>> login_urls =
+ base::make_optional<std::vector<GURL>>();
+ login_urls->push_back(GURL("https://login.example.com"));
+ reuse_detector->UseEnterprisePasswordURLs(
+ login_urls, GURL("https://changepassword.example.com/"));
}
TEST(PasswordReuseDetectorTest, TypingPasswordOnDifferentSite) {
@@ -296,14 +290,8 @@ TEST(PasswordReuseDetectorTest, GaiaPasswordReuseFound) {
}
TEST(PasswordReuseDetectorTest, EnterprisePasswordNoReuse) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndEnableFeature(
- safe_browsing::kEnterprisePasswordProtectionV1);
- TestingPrefServiceSimple prefs;
- ConfigureEnterprisePasswordProtection(&prefs);
-
PasswordReuseDetector reuse_detector;
- reuse_detector.SetPrefs(&prefs);
+ ConfigureEnterprisePasswordProtection(&reuse_detector);
reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
MockPasswordReuseDetectorConsumer mockConsumer;
@@ -330,14 +318,8 @@ TEST(PasswordReuseDetectorTest, EnterprisePasswordNoReuse) {
}
TEST(PasswordReuseDetectorTest, EnterprisePasswordReuseFound) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndEnableFeature(
- safe_browsing::kEnterprisePasswordProtectionV1);
- TestingPrefServiceSimple prefs;
- ConfigureEnterprisePasswordProtection(&prefs);
-
PasswordReuseDetector reuse_detector;
- reuse_detector.SetPrefs(&prefs);
+ ConfigureEnterprisePasswordProtection(&reuse_detector);
reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
MockPasswordReuseDetectorConsumer mockConsumer;
@@ -406,17 +388,11 @@ TEST(PasswordReuseDetectorTest, MatchSavedPasswordButNotGaiaPassword) {
}
TEST(PasswordReuseDetectorTest, MatchEnterpriseAndMultipleSavedPasswords) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndEnableFeature(
- safe_browsing::kEnterprisePasswordProtectionV1);
- TestingPrefServiceSimple prefs;
- ConfigureEnterprisePasswordProtection(&prefs);
-
const std::vector<std::pair<std::string, std::string>> domain_passwords = {
{"https://a.com", "34567890"}, {"https://b.com", "01234567890"},
};
PasswordReuseDetector reuse_detector;
- reuse_detector.SetPrefs(&prefs);
+ ConfigureEnterprisePasswordProtection(&reuse_detector);
reuse_detector.OnGetPasswordStoreResults(GetForms(domain_passwords));
std::string enterprise_password = "1234567890";
@@ -449,14 +425,8 @@ TEST(PasswordReuseDetectorTest, MatchEnterpriseAndMultipleSavedPasswords) {
}
TEST(PasswordReuseDetectorTest, MatchSavedPasswordButNotEnterprisePassword) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndEnableFeature(
- safe_browsing::kEnterprisePasswordProtectionV1);
- TestingPrefServiceSimple prefs;
- ConfigureEnterprisePasswordProtection(&prefs);
-
PasswordReuseDetector reuse_detector;
- reuse_detector.SetPrefs(&prefs);
+ ConfigureEnterprisePasswordProtection(&reuse_detector);
reuse_detector.OnGetPasswordStoreResults(GetForms(GetTestDomainsPasswords()));
MockPasswordReuseDetectorConsumer mockConsumer;
@@ -472,17 +442,11 @@ TEST(PasswordReuseDetectorTest, MatchSavedPasswordButNotEnterprisePassword) {
}
TEST(PasswordReuseDetectorTest, MatchGaiaEnterpriseAndSavedPassword) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndEnableFeature(
- safe_browsing::kEnterprisePasswordProtectionV1);
- TestingPrefServiceSimple prefs;
- ConfigureEnterprisePasswordProtection(&prefs);
-
const std::vector<std::pair<std::string, std::string>> domain_passwords = {
{"https://a.com", "34567890"}, {"https://b.com", "01234567890"},
};
PasswordReuseDetector reuse_detector;
- reuse_detector.SetPrefs(&prefs);
+ ConfigureEnterprisePasswordProtection(&reuse_detector);
reuse_detector.OnGetPasswordStoreResults(GetForms(domain_passwords));
std::string gaia_password = "123456789";
diff --git a/chromium/components/password_manager/core/browser/password_store.cc b/chromium/components/password_manager/core/browser/password_store.cc
index ec111d1d2be..9c03765a440 100644
--- a/chromium/components/password_manager/core/browser/password_store.cc
+++ b/chromium/components/password_manager/core/browser/password_store.cc
@@ -29,6 +29,7 @@
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
#include "components/password_manager/core/browser/password_store_signin_notifier.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
#endif
using autofill::PasswordForm;
@@ -129,11 +130,12 @@ bool PasswordStore::Init(const syncer::SyncableService::StartSyncFlare& flare,
DCHECK(main_task_runner_);
background_task_runner_ = CreateBackgroundTaskRunner();
DCHECK(background_task_runner_);
- ScheduleTask(
- base::Bind(&PasswordStore::InitOnBackgroundSequence, this, flare));
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+ prefs_ = prefs;
hash_password_manager_.set_prefs(prefs);
#endif
+ ScheduleTask(base::BindRepeating(&PasswordStore::InitOnBackgroundSequence,
+ this, flare));
return true;
}
@@ -359,29 +361,43 @@ void PasswordStore::CheckReuse(const base::string16& input,
#endif
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
-void PasswordStore::PrepareSyncPasswordHashData(
- const std::string& sync_username) {
+void PasswordStore::PreparePasswordHashData(const std::string& sync_username) {
// TODO(crbug.com/841438): Delete migration code when most users complete
// the migration.
- hash_password_manager_.MaybeMigrateExistingSyncPasswordHash(sync_username);
- ScheduleTask(base::BindRepeating(
- &PasswordStore::SaveSyncPasswordHashImpl, this,
- base::Passed(hash_password_manager_.RetrievePasswordHash(
- sync_username, /*is_gaia_password=*/true))));
+ if (!sync_username.empty())
+ hash_password_manager_.MaybeMigrateExistingSyncPasswordHash(sync_username);
+ SchedulePasswordHashUpdate(/*should_log_metrics=*/true);
+ ScheduleEnterprisePasswordURLUpdate();
}
-void PasswordStore::SaveSyncPasswordHash(
+void PasswordStore::SaveGaiaPasswordHash(
const std::string& username,
const base::string16& password,
metrics_util::SyncPasswordHashChange event) {
+ SaveProtectedPasswordHash(username, password, /*is_gaia_password=*/true,
+ event);
+}
+
+void PasswordStore::SaveEnterprisePasswordHash(const std::string& username,
+ const base::string16& password) {
+ SaveProtectedPasswordHash(
+ username, password, /*is_gaia_password=*/false,
+ metrics_util::SyncPasswordHashChange::NOT_SYNC_PASSWORD_CHANGE);
+}
+
+void PasswordStore::SaveProtectedPasswordHash(
+ const std::string& username,
+ const base::string16& password,
+ bool is_gaia_password,
+ metrics_util::SyncPasswordHashChange event) {
if (hash_password_manager_.SavePasswordHash(username, password,
- /*is_gaia_password=*/true)) {
- base::Optional<PasswordHashData> sync_password_data =
- hash_password_manager_.RetrievePasswordHash(username,
- /*is_gaia_password=*/true);
- metrics_util::LogSyncPasswordHashChange(event);
- ScheduleTask(base::BindRepeating(&PasswordStore::SaveSyncPasswordHashImpl,
- this, std::move(sync_password_data)));
+ is_gaia_password)) {
+ if (is_gaia_password &&
+ event !=
+ metrics_util::SyncPasswordHashChange::NOT_SYNC_PASSWORD_CHANGE) {
+ metrics_util::LogSyncPasswordHashChange(event);
+ }
+ SchedulePasswordHashUpdate(/*should_log_metrics=*/false);
}
}
@@ -390,15 +406,18 @@ void PasswordStore::SaveSyncPasswordHash(
metrics_util::SyncPasswordHashChange event) {
if (hash_password_manager_.SavePasswordHash(sync_password_data)) {
metrics_util::LogSyncPasswordHashChange(event);
- ScheduleTask(base::BindRepeating(&PasswordStore::SaveSyncPasswordHashImpl,
- this, sync_password_data));
+ SchedulePasswordHashUpdate(/*should_log_metrics=*/false);
}
}
void PasswordStore::ClearPasswordHash(const std::string& username) {
hash_password_manager_.ClearSavedPasswordHash(username,
/*is_gaia_password=*/true);
- ScheduleTask(base::Bind(&PasswordStore::ClearSyncPasswordHashImpl, this));
+ // TODO(crbug.com/844134): Find a way to clear corresponding Gaia password
+ // hash when user logs out individual Gaia account in content area.
+ hash_password_manager_.ClearAllPasswordHash(/*is_gaia_password=*/true);
+ ScheduleTask(base::BindRepeating(
+ &PasswordStore::ClearProtectedPasswordHashImpl, this));
}
void PasswordStore::SetPasswordStoreSigninNotifier(
@@ -408,6 +427,26 @@ void PasswordStore::SetPasswordStoreSigninNotifier(
notifier_ = std::move(notifier);
notifier_->SubscribeToSigninEvents(this);
}
+
+void PasswordStore::SchedulePasswordHashUpdate(bool should_log_metrics) {
+ ScheduleTask(base::BindRepeating(
+ &PasswordStore::SaveProtectedPasswordHashImpl, this,
+ base::Passed(hash_password_manager_.RetrieveAllPasswordHashes()),
+ should_log_metrics));
+}
+
+void PasswordStore::ScheduleEnterprisePasswordURLUpdate() {
+ std::vector<GURL> enterprise_login_urls;
+ safe_browsing::GetPasswordProtectionLoginURLsPref(*prefs_,
+ &enterprise_login_urls);
+ GURL enterprise_change_password_url =
+ safe_browsing::GetPasswordProtectionChangePasswordURLPref(*prefs_);
+
+ ScheduleTask(base::BindRepeating(&PasswordStore::SaveEnterprisePasswordURLs,
+ this, base::Passed(&enterprise_login_urls),
+ enterprise_change_password_url));
+}
+
#endif
PasswordStore::~PasswordStore() {
@@ -502,17 +541,45 @@ void PasswordStore::NotifyLoginsChanged(
void PasswordStore::CheckReuseImpl(std::unique_ptr<CheckReuseRequest> request,
const base::string16& input,
const std::string& domain) {
- if (reuse_detector_)
+ if (reuse_detector_) {
reuse_detector_->CheckReuse(input, domain, request.get());
+ }
}
-void PasswordStore::SaveSyncPasswordHashImpl(
- base::Optional<PasswordHashData> sync_password_data) {
- if (reuse_detector_)
- reuse_detector_->UseSyncPasswordHash(std::move(sync_password_data));
+void PasswordStore::SaveProtectedPasswordHashImpl(
+ PasswordHashDataList protected_password_data_list,
+ bool should_log_metrics) {
+ if (!reuse_detector_ || !protected_password_data_list.has_value())
+ return;
+
+ std::vector<PasswordHashData> gaia_password_hash_list;
+ std::vector<PasswordHashData> enterprise_password_hash_list;
+ for (PasswordHashData& password_hash : *protected_password_data_list) {
+ if (password_hash.is_gaia_password)
+ gaia_password_hash_list.push_back(std::move(password_hash));
+ else
+ enterprise_password_hash_list.push_back(std::move(password_hash));
+ }
+
+ if (should_log_metrics) {
+ metrics_util::LogProtectedPasswordHashCounts(
+ gaia_password_hash_list.size(), enterprise_password_hash_list.size());
+ }
+ reuse_detector_->UseGaiaPasswordHash(std::move(gaia_password_hash_list));
+ reuse_detector_->UseNonGaiaEnterprisePasswordHash(
+ std::move(enterprise_password_hash_list));
+}
+
+void PasswordStore::SaveEnterprisePasswordURLs(
+ const std::vector<GURL>& enterprise_login_urls,
+ const GURL& enterprise_change_password_url) {
+ if (!reuse_detector_)
+ return;
+ reuse_detector_->UseEnterprisePasswordURLs(std::move(enterprise_login_urls),
+ enterprise_change_password_url);
}
-void PasswordStore::ClearSyncPasswordHashImpl() {
+void PasswordStore::ClearProtectedPasswordHashImpl() {
// TODO(crbug.com/844134): Find a way to clear corresponding Gaia password
// hash when user logs out individual Gaia account in content area.
if (reuse_detector_)
diff --git a/chromium/components/password_manager/core/browser/password_store.h b/chromium/components/password_manager/core/browser/password_store.h
index f6fa997e927..cd5233b377a 100644
--- a/chromium/components/password_manager/core/browser/password_store.h
+++ b/chromium/components/password_manager/core/browser/password_store.h
@@ -50,6 +50,10 @@ class PasswordStoreSigninNotifier;
class PasswordSyncableService;
struct InteractionsStats;
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+using PasswordHashDataList = base::Optional<std::vector<PasswordHashData>>;
+#endif
+
// Interface for storing form passwords in a platform-specific secure way.
// The login request/manipulation API is not threadsafe and must be used
// from the UI thread.
@@ -252,9 +256,9 @@ class PasswordStore : protected PasswordStoreSync,
// TODO(crbug.com/706392): Fix password reuse detection for Android.
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
- // Immediately called after |Init()| to retrieve sync password hash data for
+ // Immediately called after |Init()| to retrieve password hash data for
// reuse detection.
- void PrepareSyncPasswordHashData(const std::string& sync_username);
+ void PreparePasswordHashData(const std::string& sync_username);
// Checks that some suffix of |input| equals to a password saved on another
// registry controlled domain than |domain|.
@@ -265,13 +269,19 @@ class PasswordStore : protected PasswordStoreSync,
const std::string& domain,
PasswordReuseDetectorConsumer* consumer);
- // Saves |sync_username| and a hash of |password| for password reuse checking.
- // |event| is used for metric logging.
- virtual void SaveSyncPasswordHash(const std::string& sync_username,
+ // Saves |username| and a hash of |password| for Gaia password reuse checking.
+ // |event| is used for metric logging and for distinguishing sync password
+ // hash change event and other non-sync Gaia password change event.
+ virtual void SaveGaiaPasswordHash(const std::string& username,
const base::string16& password,
metrics_util::SyncPasswordHashChange event);
- // Saves |sync_password_data| for password reuse checking.
+ // Saves |username| and a hash of |password| for enterprise password reuse
+ // checking.
+ virtual void SaveEnterprisePasswordHash(const std::string& username,
+ const base::string16& password);
+
+ // Saves |sync_password_data| for sync password reuse checking.
// |event| is used for metric logging.
virtual void SaveSyncPasswordHash(const PasswordHashData& sync_password_data,
metrics_util::SyncPasswordHashChange event);
@@ -283,6 +293,13 @@ class PasswordStore : protected PasswordStoreSync,
void SetPasswordStoreSigninNotifier(
std::unique_ptr<PasswordStoreSigninNotifier> notifier);
+ // Schedules the update of password hashes used by reuse detector.
+ void SchedulePasswordHashUpdate(bool should_log_metrics);
+
+ // Schedules the update of enterprise login and change password URLs.
+ // These URLs are used in enterprise password reuse detection.
+ void ScheduleEnterprisePasswordURLUpdate();
+
#endif
protected:
@@ -450,17 +467,34 @@ class PasswordStore : protected PasswordStoreSync,
// TODO(crbug.com/706392): Fix password reuse detection for Android.
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+ // Saves |username| and a hash of |password| for password reuse checking.
+ // |is_gaia_password| indicates if it is a Gaia account. |event| is used for
+ // metric logging.
+ void SaveProtectedPasswordHash(const std::string& username,
+ const base::string16& password,
+ bool is_gaia_password,
+ metrics_util::SyncPasswordHashChange event);
+
// Synchronous implementation of CheckReuse().
void CheckReuseImpl(std::unique_ptr<CheckReuseRequest> request,
const base::string16& input,
const std::string& domain);
- // Synchronous implementation of SaveSyncPasswordHash().
- void SaveSyncPasswordHashImpl(
- base::Optional<PasswordHashData> sync_password_data);
-
- // Synchronous implementation of ClearSyncPasswordHash().
- void ClearSyncPasswordHashImpl();
+ // Synchronous implementation of SaveProtectedPasswordHash().
+ // |should_log_metrics| indicates whether to log the counts of captured
+ // password hashes.
+ void SaveProtectedPasswordHashImpl(
+ PasswordHashDataList protected_password_data_list,
+ bool should_log_metrics);
+
+ // Propagates enterprise login urls and change password url to
+ // |reuse_detector_|.
+ void SaveEnterprisePasswordURLs(
+ const std::vector<GURL>& enterprise_login_urls,
+ const GURL& enterprise_change_password_url);
+
+ // Synchronous implementation of ClearProtectedPasswordHash().
+ void ClearProtectedPasswordHashImpl();
#endif
scoped_refptr<base::SequencedTaskRunner> main_task_runner() const {
@@ -617,6 +651,7 @@ class PasswordStore : protected PasswordStoreSync,
std::unique_ptr<AffiliatedMatchHelper> affiliated_match_helper_;
// TODO(crbug.com/706392): Fix password reuse detection for Android.
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+ PrefService* prefs_ = nullptr;
// PasswordReuseDetector can be only destroyed on the background sequence. It
// can't be owned by PasswordStore because PasswordStore can be destroyed on
// the UI thread and DestroyOnBackgroundThread isn't guaranteed to be called.
diff --git a/chromium/components/password_manager/core/browser/password_store_factory_util.cc b/chromium/components/password_manager/core/browser/password_store_factory_util.cc
index 4e8cdb8db09..4b2bb81095f 100644
--- a/chromium/components/password_manager/core/browser/password_store_factory_util.cc
+++ b/chromium/components/password_manager/core/browser/password_store_factory_util.cc
@@ -13,6 +13,7 @@
#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
#include "components/password_manager/core/browser/password_manager_constants.h"
#include "components/password_manager/core/common/password_manager_features.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace password_manager {
@@ -28,7 +29,7 @@ bool ShouldAffiliationBasedMatchingBeActive(syncer::SyncService* sync_service) {
void ActivateAffiliationBasedMatching(
PasswordStore* password_store,
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const base::FilePath& db_path) {
// Subsequent instances of the AffiliationService must use the same sequenced
// task runner for their backends. This guarantees that the backend of the
@@ -45,7 +46,7 @@ void ActivateAffiliationBasedMatching(
// is owned by the PasswordStore.
std::unique_ptr<AffiliationService> affiliation_service(
new AffiliationService(backend_task_runner));
- affiliation_service->Initialize(request_context_getter, db_path);
+ affiliation_service->Initialize(std::move(url_loader_factory), db_path);
std::unique_ptr<AffiliatedMatchHelper> affiliated_match_helper(
new AffiliatedMatchHelper(password_store,
std::move(affiliation_service)));
@@ -65,7 +66,7 @@ base::FilePath GetAffiliationDatabasePath(const base::FilePath& profile_path) {
void ToggleAffiliationBasedMatchingBasedOnPasswordSyncedState(
PasswordStore* password_store,
syncer::SyncService* sync_service,
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const base::FilePath& profile_path) {
DCHECK(password_store);
@@ -75,7 +76,8 @@ void ToggleAffiliationBasedMatchingBasedOnPasswordSyncedState(
password_store->affiliated_match_helper() != nullptr;
if (matching_should_be_active && !matching_is_active) {
- ActivateAffiliationBasedMatching(password_store, request_context_getter,
+ ActivateAffiliationBasedMatching(password_store,
+ std::move(url_loader_factory),
GetAffiliationDatabasePath(profile_path));
} else if (!matching_should_be_active && matching_is_active) {
password_store->SetAffiliatedMatchHelper(nullptr);
diff --git a/chromium/components/password_manager/core/browser/password_store_factory_util.h b/chromium/components/password_manager/core/browser/password_store_factory_util.h
index b3f7a1c4dea..a1fdbc9a126 100644
--- a/chromium/components/password_manager/core/browser/password_store_factory_util.h
+++ b/chromium/components/password_manager/core/browser/password_store_factory_util.h
@@ -15,19 +15,19 @@
#include "components/password_manager/core/browser/password_store.h"
#include "components/sync/driver/sync_service.h"
#include "components/sync/model/syncable_service.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace password_manager {
// Activates or deactivates affiliation-based matching for |password_store|,
// depending on whether or not the |sync_service| is syncing passwords stored
-// therein. The AffiliationService will use |request_context_getter| to fetch
+// therein. The AffiliationService will use |url_loader_factory| to fetch
// affiliation information. This function should be called whenever there is a
// possibility that syncing passwords has just started or ended.
void ToggleAffiliationBasedMatchingBasedOnPasswordSyncedState(
PasswordStore* password_store,
syncer::SyncService* sync_service,
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const base::FilePath& profile_path);
// Creates a LoginDatabase. Looks in |profile_path| for the database file.
diff --git a/chromium/components/password_manager/core/browser/password_store_signin_notifier.cc b/chromium/components/password_manager/core/browser/password_store_signin_notifier.cc
index 405267a1a12..d0369111f95 100644
--- a/chromium/components/password_manager/core/browser/password_store_signin_notifier.cc
+++ b/chromium/components/password_manager/core/browser/password_store_signin_notifier.cc
@@ -15,8 +15,10 @@ PasswordStoreSigninNotifier::~PasswordStoreSigninNotifier() {}
void PasswordStoreSigninNotifier::NotifySignin(const std::string& username,
const std::string& password) {
- if (store_) {
- store_->SaveSyncPasswordHash(
+ // After the full roll out of DICE, |password| may be empty
+ // if user clicks "Sync as ..." button in the sign-in promotion bubble.
+ if (store_ && !password.empty()) {
+ store_->SaveGaiaPasswordHash(
username, base::UTF8ToUTF16(password),
metrics_util::SyncPasswordHashChange::SAVED_ON_CHROME_SIGNIN);
}
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 f732337d28b..e845e244276 100644
--- a/chromium/components/password_manager/core/browser/password_store_unittest.cc
+++ b/chromium/components/password_manager/core/browser/password_store_unittest.cc
@@ -139,14 +139,14 @@ class PasswordStoreTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(PasswordStoreTest);
};
-base::Optional<PasswordHashData> GetSyncPasswordFromPref(
+base::Optional<PasswordHashData> GetPasswordFromPref(
const std::string& username,
+ bool is_gaia_password,
PrefService* prefs) {
HashPasswordManager hash_password_manager;
hash_password_manager.set_prefs(prefs);
- return hash_password_manager.RetrievePasswordHash(username,
- /*is_gaia_password=*/true);
+ return hash_password_manager.RetrievePasswordHash(username, is_gaia_password);
}
TEST_F(PasswordStoreTest, IgnoreOldWwwGoogleLogins) {
@@ -921,7 +921,7 @@ TEST_F(PasswordStoreTest, CheckPasswordReuse) {
#endif
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
-TEST_F(PasswordStoreTest, SavingClearingSyncPassword) {
+TEST_F(PasswordStoreTest, SavingClearingProtectedPassword) {
scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
std::make_unique<LoginDatabase>(test_login_db_file_path())));
@@ -933,13 +933,14 @@ TEST_F(PasswordStoreTest, SavingClearingSyncPassword) {
const base::string16 sync_password = base::ASCIIToUTF16("password");
const base::string16 input = base::ASCIIToUTF16("123password");
- store->SaveSyncPasswordHash(
+ store->SaveGaiaPasswordHash(
"sync_username", sync_password,
metrics_util::SyncPasswordHashChange::SAVED_ON_CHROME_SIGNIN);
WaitForPasswordStore();
EXPECT_TRUE(prefs.HasPrefPath(prefs::kPasswordHashDataList));
base::Optional<PasswordHashData> sync_password_hash =
- GetSyncPasswordFromPref("sync_username", &prefs);
+ GetPasswordFromPref("sync_username", /*is_gaia_password=*/true, &prefs);
+ ASSERT_TRUE(sync_password_hash.has_value());
// Check that sync password reuse is found.
MockPasswordReuseDetectorConsumer mock_consumer;
@@ -950,13 +951,47 @@ TEST_F(PasswordStoreTest, SavingClearingSyncPassword) {
WaitForPasswordStore();
testing::Mock::VerifyAndClearExpectations(&mock_consumer);
- // Check that no sync password reuse is found after clearing the saved sync
- // password hash.
+ // Save a non-sync Gaia password this time.
+ const base::string16 gaia_password = base::ASCIIToUTF16("3password");
+ store->SaveGaiaPasswordHash(
+ "other_gaia_username", gaia_password,
+ metrics_util::SyncPasswordHashChange::NOT_SYNC_PASSWORD_CHANGE);
+ base::Optional<PasswordHashData> gaia_password_hash = GetPasswordFromPref(
+ "other_gaia_username", /*is_gaia_password=*/true, &prefs);
+ ASSERT_TRUE(gaia_password_hash.has_value());
+
+ // Check that Gaia password reuse is found.
+ EXPECT_CALL(mock_consumer,
+ OnReuseFound(gaia_password.size(), Matches(gaia_password_hash),
+ std::vector<std::string>(), 0));
+ store->CheckReuse(input, "https://example.com", &mock_consumer);
+ WaitForPasswordStore();
+ testing::Mock::VerifyAndClearExpectations(&mock_consumer);
+
+ // Check that no Gaia password reuse is found after clearing the password
+ // hash.
store->ClearPasswordHash("sync_username");
EXPECT_EQ(0u, prefs.GetList(prefs::kPasswordHashDataList)->GetList().size());
EXPECT_CALL(mock_consumer, OnReuseFound(_, _, _, _)).Times(0);
store->CheckReuse(input, "https://facebook.com", &mock_consumer);
WaitForPasswordStore();
+ testing::Mock::VerifyAndClearExpectations(&mock_consumer);
+
+ // Save a enterprise password this time.
+ const base::string16 enterprise_password = base::ASCIIToUTF16("23password");
+ store->SaveEnterprisePasswordHash("enterprise_username", enterprise_password);
+ base::Optional<PasswordHashData> enterprise_password_hash =
+ GetPasswordFromPref("enterprise_username", /*is_gaia_password=*/false,
+ &prefs);
+ ASSERT_TRUE(enterprise_password_hash.has_value());
+
+ // Check that enterprise password reuse is found.
+ EXPECT_CALL(mock_consumer, OnReuseFound(enterprise_password.size(),
+ Matches(enterprise_password_hash),
+ std::vector<std::string>(), 0));
+ store->CheckReuse(input, "https://example.com", &mock_consumer);
+ WaitForPasswordStore();
+ testing::Mock::VerifyAndClearExpectations(&mock_consumer);
store->ShutdownOnUIThread();
}
diff --git a/chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever.cc b/chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever.cc
deleted file mode 100644
index 87edae58d7e..00000000000
--- a/chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever.cc
+++ /dev/null
@@ -1,99 +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/password_manager/core/browser/site_affiliation/asset_link_retriever.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/task_runner_util.h"
-#include "base/task_scheduler/post_task.h"
-#include "net/base/load_flags.h"
-#include "net/http/http_status_code.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-
-namespace password_manager {
-
-AssetLinkRetriever::AssetLinkRetriever(GURL file_url)
- : url_(std::move(file_url)), state_(State::INACTIVE), error_(false) {
- DCHECK(url_.is_valid());
- DCHECK(url_.SchemeIs(url::kHttpsScheme));
-}
-
-void AssetLinkRetriever::Start(net::URLRequestContextGetter* context_getter) {
- if (state_ != State::INACTIVE)
- return;
- net::NetworkTrafficAnnotationTag traffic_annotation =
- net::DefineNetworkTrafficAnnotation("asset_links", R"(
- semantics {
- sender: "Asset Links Fetcher"
- description:
- "The asset links is a JSON file hosted on "
- "https://<domain>/.well-known/assetlinks.json. It contains "
- "different permissions the site gives to apps/other sites. Chrome "
- "looks for a permission to delegate the credentials from the site "
- "to another domain. It's used for handling the stored credentials "
- "in the password manager."
- trigger:
- "Load a site where it's possible to sign-in. The site can have a "
- "password form or use the Credential Management API."
- data: "None."
- destination: WEBSITE
- }
- policy {
- cookies_allowed: NO
- setting: "No setting"
- policy_exception_justification:
- "The file is considered to be a resource of the page loaded."
- })");
- fetcher_ = net::URLFetcher::Create(url_, net::URLFetcher::GET, this,
- traffic_annotation);
- fetcher_->SetRequestContext(context_getter);
- fetcher_->SetLoadFlags(
- net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DO_NOT_SEND_AUTH_DATA | net::LOAD_MAYBE_USER_GESTURE);
- fetcher_->SetStopOnRedirect(true);
- fetcher_->Start();
- state_ = State::NETWORK_REQUEST;
-}
-
-AssetLinkRetriever::~AssetLinkRetriever() = default;
-
-void AssetLinkRetriever::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK(source == fetcher_.get());
-
- error_ = !source->GetStatus().is_success() ||
- source->GetResponseCode() != net::HTTP_OK;
- if (error_) {
- state_ = State::FINISHED;
- } else {
- state_ = State::PARSING;
- std::string response_string;
- source->GetResponseAsString(&response_string);
- scoped_refptr<base::TaskRunner> task_runner =
- base::CreateTaskRunnerWithTraits({base::TaskPriority::USER_BLOCKING});
- auto data = std::make_unique<AssetLinkData>();
- AssetLinkData* data_raw = data.get();
- base::PostTaskAndReplyWithResult(
- task_runner.get(), FROM_HERE,
- base::BindOnce(&AssetLinkData::Parse, base::Unretained(data_raw),
- std::move(response_string)),
- base::BindOnce(&AssetLinkRetriever::OnResponseParsed, this,
- std::move(data)));
- }
- fetcher_.reset();
-}
-
-void AssetLinkRetriever::OnResponseParsed(std::unique_ptr<AssetLinkData> data,
- bool result) {
- error_ = !result;
- if (result)
- data_ = std::move(*data);
- state_ = State::FINISHED;
-}
-
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever.h b/chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever.h
deleted file mode 100644
index 2beecdf7377..00000000000
--- a/chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever.h
+++ /dev/null
@@ -1,77 +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_PASSWORD_MANAGER_CORE_BROWSER_SITE_AFFILIATION_ASSET_LINK_RETRIEVER_H_
-#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SITE_AFFILIATION_ASSET_LINK_RETRIEVER_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "components/password_manager/core/browser/site_affiliation/asset_link_data.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "url/gurl.h"
-
-namespace net {
-class URLRequestContextGetter;
-}
-
-namespace password_manager {
-
-// The class is responsible for fetching and parsing the digit asset links file.
-// The file is a JSON containing different directives for the domain. The class
-// is only interested in those related to credentials delegations.
-// The spec is
-// https://github.com/google/digitalassetlinks/blob/master/well-known/details.md
-class AssetLinkRetriever : public base::RefCounted<AssetLinkRetriever>,
- public net::URLFetcherDelegate {
- public:
- enum class State {
- INACTIVE,
- NETWORK_REQUEST,
- PARSING,
- FINISHED,
- };
-
- explicit AssetLinkRetriever(GURL file_url);
-
- // Starts a network request if the current state is INACTIVE. All the calls
- // afterwards are ignored.
- void Start(net::URLRequestContextGetter* context_getter);
-
- State state() const { return state_; }
-
- bool error() const { return error_; }
-
- const std::vector<GURL>& includes() const { return data_.includes(); }
- const std::vector<GURL>& targets() const { return data_.targets(); }
-
- private:
- friend class base::RefCounted<AssetLinkRetriever>;
- ~AssetLinkRetriever() override;
-
- // net::URLFetcherDelegate:
- void OnURLFetchComplete(const net::URLFetcher* source) override;
-
- void OnResponseParsed(std::unique_ptr<AssetLinkData> data, bool result);
-
- // URL of the file retrieved.
- const GURL url_;
-
- // Current state of the retrieval.
- State state_;
-
- // Whether the reading finished with error.
- bool error_;
-
- // Actual data from the asset link.
- AssetLinkData data_;
-
- std::unique_ptr<net::URLFetcher> fetcher_;
-
- DISALLOW_COPY_AND_ASSIGN(AssetLinkRetriever);
-};
-
-} // namespace password_manager
-#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_SITE_AFFILIATION_ASSET_LINK_RETRIEVER_H_
diff --git a/chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever_unittest.cc b/chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever_unittest.cc
deleted file mode 100644
index 3d90f9398f7..00000000000
--- a/chromium/components/password_manager/core/browser/site_affiliation/asset_link_retriever_unittest.cc
+++ /dev/null
@@ -1,165 +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/password_manager/core/browser/site_affiliation/asset_link_retriever.h"
-
-#include <memory>
-
-#include "base/test/scoped_task_environment.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace password_manager {
-namespace {
-
-using ::testing::IsEmpty;
-using ::testing::ElementsAre;
-
-constexpr char kAssetLinkFile[] =
- "https://example.com/.well-known/assetlinks.json";
-
-// A test URL fecther which is very cautious about not following the redirects.
-class AssetLinksTestFetcher : public net::FakeURLFetcher {
- public:
- using FakeURLFetcher::FakeURLFetcher;
-
- ~AssetLinksTestFetcher() override { EXPECT_TRUE(stop_on_redirect_); }
-
- // FakeURLFetcher:
- void SetStopOnRedirect(bool stop_on_redirect) override {
- FakeURLFetcher::SetStopOnRedirect(stop_on_redirect);
- stop_on_redirect_ = stop_on_redirect;
- }
-
- private:
- bool stop_on_redirect_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(AssetLinksTestFetcher);
-};
-
-std::unique_ptr<net::FakeURLFetcher> AssetLinksFetcherCreator(
- const GURL& url,
- net::URLFetcherDelegate* delegate,
- const std::string& response_data,
- net::HttpStatusCode response_code,
- net::URLRequestStatus::Status status) {
- return std::make_unique<AssetLinksTestFetcher>(url, delegate, response_data,
- response_code, status);
-}
-
-class AssetLinkRetrieverTest : public testing::Test {
- public:
- AssetLinkRetrieverTest();
-
- net::TestURLRequestContextGetter* request_context() const {
- return request_context_.get();
- }
-
- net::FakeURLFetcherFactory& factory() { return factory_; }
-
- void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
-
- private:
- base::test::ScopedTaskEnvironment scoped_task_environment_;
- scoped_refptr<net::TestURLRequestContextGetter> request_context_;
- net::FakeURLFetcherFactory factory_;
-
- DISALLOW_COPY_AND_ASSIGN(AssetLinkRetrieverTest);
-};
-
-AssetLinkRetrieverTest::AssetLinkRetrieverTest()
- : request_context_(new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get())),
- factory_(nullptr, base::Bind(&AssetLinksFetcherCreator)) {}
-
-// Load the asset links resource that isn't available.
-TEST_F(AssetLinkRetrieverTest, LoadNonExistent) {
- scoped_refptr<AssetLinkRetriever> asset_link_retriever =
- base::MakeRefCounted<AssetLinkRetriever>(GURL(kAssetLinkFile));
- EXPECT_EQ(AssetLinkRetriever::State::INACTIVE, asset_link_retriever->state());
-
- factory().SetFakeResponse(GURL(kAssetLinkFile), std::string(),
- net::HTTP_NOT_FOUND, net::URLRequestStatus::FAILED);
- asset_link_retriever->Start(request_context());
- EXPECT_EQ(AssetLinkRetriever::State::NETWORK_REQUEST,
- asset_link_retriever->state());
-
- RunUntilIdle();
- EXPECT_EQ(AssetLinkRetriever::State::FINISHED, asset_link_retriever->state());
- EXPECT_TRUE(asset_link_retriever->error());
-}
-
-// Load the asset links resource that replies with redirect. It should be
-// treated as an error.
-TEST_F(AssetLinkRetrieverTest, LoadRedirect) {
- scoped_refptr<AssetLinkRetriever> asset_link_retriever =
- base::MakeRefCounted<AssetLinkRetriever>(GURL(kAssetLinkFile));
- EXPECT_EQ(AssetLinkRetriever::State::INACTIVE, asset_link_retriever->state());
-
- factory().SetFakeResponse(GURL(kAssetLinkFile), std::string(),
- net::HTTP_FOUND, net::URLRequestStatus::CANCELED);
- asset_link_retriever->Start(request_context());
- EXPECT_EQ(AssetLinkRetriever::State::NETWORK_REQUEST,
- asset_link_retriever->state());
-
- RunUntilIdle();
- EXPECT_EQ(AssetLinkRetriever::State::FINISHED, asset_link_retriever->state());
- EXPECT_TRUE(asset_link_retriever->error());
-}
-
-// Load a valid asset links resource.
-TEST_F(AssetLinkRetrieverTest, LoadValidFile) {
- scoped_refptr<AssetLinkRetriever> asset_link_retriever =
- base::MakeRefCounted<AssetLinkRetriever>(GURL(kAssetLinkFile));
- EXPECT_EQ(AssetLinkRetriever::State::INACTIVE, asset_link_retriever->state());
-
- constexpr char json[] =
- u8R"([{
- "relation": ["delegate_permission/common.get_login_creds"],
- "target": {
- "namespace": "web",
- "site": "https://www.google.com"
- }
- },{
- "include": "https://go/assetlinks.json"
- }])";
- factory().SetFakeResponse(GURL(kAssetLinkFile), json, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- asset_link_retriever->Start(request_context());
- EXPECT_EQ(AssetLinkRetriever::State::NETWORK_REQUEST,
- asset_link_retriever->state());
-
- RunUntilIdle();
- EXPECT_EQ(AssetLinkRetriever::State::FINISHED, asset_link_retriever->state());
- EXPECT_FALSE(asset_link_retriever->error());
- EXPECT_THAT(asset_link_retriever->includes(),
- ElementsAre(GURL("https://go/assetlinks.json")));
- EXPECT_THAT(asset_link_retriever->targets(),
- ElementsAre(GURL("https://www.google.com")));
-}
-
-// Load a broken resource.
-TEST_F(AssetLinkRetrieverTest, LoadInvalidFile) {
- scoped_refptr<AssetLinkRetriever> asset_link_retriever =
- base::MakeRefCounted<AssetLinkRetriever>(GURL(kAssetLinkFile));
- EXPECT_EQ(AssetLinkRetriever::State::INACTIVE, asset_link_retriever->state());
-
- constexpr char json[] = u8R"([{111}])";
- factory().SetFakeResponse(GURL(kAssetLinkFile), json, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- asset_link_retriever->Start(request_context());
- EXPECT_EQ(AssetLinkRetriever::State::NETWORK_REQUEST,
- asset_link_retriever->state());
-
- RunUntilIdle();
- EXPECT_EQ(AssetLinkRetriever::State::FINISHED, asset_link_retriever->state());
- EXPECT_TRUE(asset_link_retriever->error());
- EXPECT_THAT(asset_link_retriever->includes(), IsEmpty());
- EXPECT_THAT(asset_link_retriever->targets(), IsEmpty());
-}
-
-} // namespace
-} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/stub_credentials_filter.cc b/chromium/components/password_manager/core/browser/stub_credentials_filter.cc
index 32549cd49d9..2def94359c7 100644
--- a/chromium/components/password_manager/core/browser/stub_credentials_filter.cc
+++ b/chromium/components/password_manager/core/browser/stub_credentials_filter.cc
@@ -22,7 +22,12 @@ bool StubCredentialsFilter::ShouldSave(
return true;
}
-bool StubCredentialsFilter::ShouldSavePasswordHash(
+bool StubCredentialsFilter::ShouldSaveGaiaPasswordHash(
+ const autofill::PasswordForm& form) const {
+ return false;
+}
+
+bool StubCredentialsFilter::ShouldSaveEnterprisePasswordHash(
const autofill::PasswordForm& form) const {
return false;
}
@@ -30,6 +35,11 @@ bool StubCredentialsFilter::ShouldSavePasswordHash(
void StubCredentialsFilter::ReportFormLoginSuccess(
const PasswordFormManager& form_manager) const {}
+bool StubCredentialsFilter::IsSyncAccountEmail(
+ const std::string& username) const {
+ return false;
+}
+
void StubCredentialsFilter::FilterResultsPtr(
std::vector<std::unique_ptr<autofill::PasswordForm>>* results) const {}
diff --git a/chromium/components/password_manager/core/browser/stub_credentials_filter.h b/chromium/components/password_manager/core/browser/stub_credentials_filter.h
index 7424c55f056..977c949acbd 100644
--- a/chromium/components/password_manager/core/browser/stub_credentials_filter.h
+++ b/chromium/components/password_manager/core/browser/stub_credentials_filter.h
@@ -23,10 +23,13 @@ class StubCredentialsFilter : public CredentialsFilter {
std::vector<std::unique_ptr<autofill::PasswordForm>> results)
const override;
bool ShouldSave(const autofill::PasswordForm& form) const override;
- bool ShouldSavePasswordHash(
+ bool ShouldSaveGaiaPasswordHash(
+ const autofill::PasswordForm& form) const override;
+ bool ShouldSaveEnterprisePasswordHash(
const autofill::PasswordForm& form) const override;
void ReportFormLoginSuccess(
const PasswordFormManager& form_manager) const override;
+ bool IsSyncAccountEmail(const std::string& username) const override;
// A version of FilterResult without moveable arguments, which cannot be
// mocked in GMock. StubCredentialsFilter::FilterResults(arg) calls
diff --git a/chromium/components/password_manager/core/browser/stub_password_manager_client.cc b/chromium/components/password_manager/core/browser/stub_password_manager_client.cc
index 9fb9378f691..c003ad46d7c 100644
--- a/chromium/components/password_manager/core/browser/stub_password_manager_client.cc
+++ b/chromium/components/password_manager/core/browser/stub_password_manager_client.cc
@@ -83,7 +83,7 @@ void StubPasswordManagerClient::CheckSafeBrowsingReputation(
const GURL& frame_url) {}
void StubPasswordManagerClient::CheckProtectedPasswordEntry(
- bool matches_sync_password,
+ metrics_util::PasswordType reused_password_type,
const std::vector<std::string>& matching_domains,
bool password_field_exists) {}
diff --git a/chromium/components/password_manager/core/browser/stub_password_manager_client.h b/chromium/components/password_manager/core/browser/stub_password_manager_client.h
index a4e9cb66124..58f2610b344 100644
--- a/chromium/components/password_manager/core/browser/stub_password_manager_client.h
+++ b/chromium/components/password_manager/core/browser/stub_password_manager_client.h
@@ -9,6 +9,7 @@
#include "base/optional.h"
#include "components/password_manager/core/browser/password_manager_client.h"
#include "components/password_manager/core/browser/password_manager_metrics_recorder.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/stub_credentials_filter.h"
#include "components/password_manager/core/browser/stub_log_manager.h"
@@ -56,7 +57,7 @@ class StubPasswordManagerClient : public PasswordManagerClient {
void CheckSafeBrowsingReputation(const GURL& form_action,
const GURL& frame_url) override;
void CheckProtectedPasswordEntry(
- bool matches_sync_password,
+ metrics_util::PasswordType reused_password_type,
const std::vector<std::string>& matching_domains,
bool password_field_exists) override;
void LogPasswordReuseDetectedEvent() override;
diff --git a/chromium/components/password_manager/core/browser/stub_password_manager_driver.cc b/chromium/components/password_manager/core/browser/stub_password_manager_driver.cc
index 3c3791838aa..4a9145faa38 100644
--- a/chromium/components/password_manager/core/browser/stub_password_manager_driver.cc
+++ b/chromium/components/password_manager/core/browser/stub_password_manager_driver.cc
@@ -27,8 +27,6 @@ void StubPasswordManagerDriver::GeneratedPasswordAccepted(
const base::string16& password) {
}
-void StubPasswordManagerDriver::UserSelectedManualGenerationOption() {}
-
void StubPasswordManagerDriver::FillSuggestion(const base::string16& username,
const base::string16& password) {
}
@@ -66,6 +64,4 @@ bool StubPasswordManagerDriver::IsMainFrame() const {
return true;
}
-void StubPasswordManagerDriver::MatchingBlacklistedFormFound() {}
-
} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/stub_password_manager_driver.h b/chromium/components/password_manager/core/browser/stub_password_manager_driver.h
index bd540d4b8f3..406190aef35 100644
--- a/chromium/components/password_manager/core/browser/stub_password_manager_driver.h
+++ b/chromium/components/password_manager/core/browser/stub_password_manager_driver.h
@@ -26,7 +26,6 @@ class StubPasswordManagerDriver : public PasswordManagerDriver {
void FormsEligibleForGenerationFound(
const std::vector<autofill::PasswordFormGenerationData>& forms) override;
void GeneratedPasswordAccepted(const base::string16& password) override;
- void UserSelectedManualGenerationOption() override;
void FillSuggestion(const base::string16& username,
const base::string16& password) override;
void PreviewSuggestion(const base::string16& username,
@@ -39,7 +38,6 @@ class StubPasswordManagerDriver : public PasswordManagerDriver {
PasswordAutofillManager* GetPasswordAutofillManager() override;
autofill::AutofillDriver* GetAutofillDriver() override;
bool IsMainFrame() const override;
- void MatchingBlacklistedFormFound() override;
private:
DISALLOW_COPY_AND_ASSIGN(StubPasswordManagerDriver);
diff --git a/chromium/components/password_manager/core/browser/test_password_store.cc b/chromium/components/password_manager/core/browser/test_password_store.cc
index 2166d73d67a..2ff6e76f723 100644
--- a/chromium/components/password_manager/core/browser/test_password_store.cc
+++ b/chromium/components/password_manager/core/browser/test_password_store.cc
@@ -88,6 +88,7 @@ PasswordStoreChangeList TestPasswordStore::RemoveLoginImpl(
std::vector<std::unique_ptr<autofill::PasswordForm>>
TestPasswordStore::FillMatchingLogins(const FormDigest& form) {
+ ++fill_matching_logins_calls_;
std::vector<std::unique_ptr<autofill::PasswordForm>> matched_forms;
for (const auto& elements : stored_passwords_) {
// The code below doesn't support PSL federated credential. It's doable but
diff --git a/chromium/components/password_manager/core/browser/test_password_store.h b/chromium/components/password_manager/core/browser/test_password_store.h
index c61793e5c48..cd3acca2721 100644
--- a/chromium/components/password_manager/core/browser/test_password_store.h
+++ b/chromium/components/password_manager/core/browser/test_password_store.h
@@ -35,6 +35,8 @@ class TestPasswordStore : public PasswordStore {
// have entries of size 0.
bool IsEmpty() const;
+ int fill_matching_logins_calls() const { return fill_matching_logins_calls_; }
+
protected:
~TestPasswordStore() override;
@@ -85,6 +87,9 @@ class TestPasswordStore : public PasswordStore {
private:
PasswordMap stored_passwords_;
+ // Number of calls of FillMatchingLogins() method.
+ int fill_matching_logins_calls_ = 0;
+
DISALLOW_COPY_AND_ASSIGN(TestPasswordStore);
};
diff --git a/chromium/components/password_manager/core/browser/vote_uploads_test_matchers.h b/chromium/components/password_manager/core/browser/vote_uploads_test_matchers.h
new file mode 100644
index 00000000000..9f0076b2255
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/vote_uploads_test_matchers.h
@@ -0,0 +1,166 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_VOTE_UPLOADS_TEST_MATCHERS_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_VOTE_UPLOADS_TEST_MATCHERS_H_
+
+#include <string>
+
+#include "components/autofill/core/browser/form_structure.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Matches a FormStructure if its signature is the same as that of the
+// PasswordForm |form|.
+MATCHER_P(SignatureIsSameAs,
+ form,
+ std::string(negation ? "signature isn't " : "signature is ") +
+ autofill::FormStructure(form.form_data).FormSignatureAsStr()) {
+ if (autofill::FormStructure(form.form_data).FormSignatureAsStr() ==
+ arg.FormSignatureAsStr())
+ return true;
+
+ *result_listener << "signature is " << arg.FormSignatureAsStr() << " instead";
+ return false;
+}
+
+MATCHER_P(UploadedAutofillTypesAre, expected_types, "") {
+ size_t fields_matched_type_count = 0;
+ bool conflict_found = false;
+ for (const auto& field : arg) {
+ fields_matched_type_count +=
+ expected_types.find(field->name) == expected_types.end() ? 0 : 1;
+ if (field->possible_types().size() > 1) {
+ *result_listener << (conflict_found ? ", " : "") << "Field "
+ << field->name << ": has several possible types";
+ conflict_found = true;
+ }
+
+ autofill::ServerFieldType expected_vote =
+ expected_types.find(field->name) == expected_types.end()
+ ? autofill::UNKNOWN_TYPE
+ : expected_types.find(field->name)->second;
+ autofill::ServerFieldType actual_vote =
+ field->possible_types().empty() ? autofill::UNKNOWN_TYPE
+ : *field->possible_types().begin();
+ if (expected_vote != actual_vote) {
+ *result_listener << (conflict_found ? ", " : "") << "Field "
+ << field->name << ": expected vote " << expected_vote
+ << " but found " << actual_vote;
+ conflict_found = true;
+ }
+ }
+ if (expected_types.size() != fields_matched_type_count) {
+ *result_listener << (conflict_found ? ", " : "")
+ << "Some types were expected but not found in the vote";
+ return false;
+ }
+
+ return !conflict_found;
+}
+
+MATCHER_P(HasGenerationVote, expect_generation_vote, "") {
+ bool found_generation_vote = false;
+ for (const auto& field : arg) {
+ if (field->generation_type() !=
+ autofill::AutofillUploadContents::Field::NO_GENERATION) {
+ found_generation_vote = true;
+ break;
+ }
+ }
+ return found_generation_vote == expect_generation_vote;
+}
+
+// Matches if all fields with a vote type are described in |expected_vote_types|
+// and all votes from |expected_vote_types| are found in a field.
+MATCHER_P(VoteTypesAre, expected_vote_types, "") {
+ size_t matched_count = 0;
+ bool conflict_found = false;
+ for (const auto& field : arg) {
+ auto expectation = expected_vote_types.find(field->name);
+ if (expectation == expected_vote_types.end()) {
+ if (field->vote_type() !=
+ autofill::AutofillUploadContents::Field::NO_INFORMATION) {
+ *result_listener << (conflict_found ? ", " : "") << "field "
+ << field->name << ": unexpected vote type "
+ << field->vote_type();
+ conflict_found = true;
+ }
+ continue;
+ }
+
+ matched_count++;
+ if (expectation->second != field->vote_type()) {
+ *result_listener << (conflict_found ? ", " : "") << "field "
+ << field->name << ": expected vote type "
+ << expectation->second << " but has "
+ << field->vote_type();
+ conflict_found = true;
+ }
+ }
+ if (expected_vote_types.size() != matched_count) {
+ *result_listener
+ << (conflict_found ? ", " : "")
+ << "some vote types were expected but not found in the vote";
+ conflict_found = true;
+ }
+
+ return !conflict_found;
+}
+
+MATCHER_P2(UploadedGenerationTypesAre,
+ expected_generation_types,
+ generated_password_changed,
+ "") {
+ for (const auto& field : arg) {
+ if (expected_generation_types.find(field->name) ==
+ expected_generation_types.end()) {
+ if (field->generation_type() !=
+ autofill::AutofillUploadContents::Field::NO_GENERATION) {
+ // Unexpected generation type.
+ *result_listener << "Expected no generation type for the field "
+ << field->name << ", but found "
+ << field->generation_type();
+ return false;
+ }
+ } else {
+ if (expected_generation_types.find(field->name)->second !=
+ field->generation_type()) {
+ // Wrong generation type.
+ *result_listener << "Expected generation type for the field "
+ << field->name << " is "
+ << expected_generation_types.find(field->name)->second
+ << ", but found " << field->generation_type();
+ return false;
+ }
+
+ if (field->generation_type() !=
+ autofill::AutofillUploadContents::Field::IGNORED_GENERATION_POPUP) {
+ if (generated_password_changed != field->generated_password_changed())
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+MATCHER_P2(UploadedFormClassifierVoteIs,
+ found_generation_element,
+ generation_element,
+ "") {
+ for (const auto& field : arg) {
+ if (found_generation_element && field->name == generation_element) {
+ if (field->form_classifier_outcome() !=
+ autofill::AutofillUploadContents::Field::GENERATION_ELEMENT)
+ return false;
+ } else {
+ if (field->form_classifier_outcome() !=
+ autofill::AutofillUploadContents::Field::NON_GENERATION_ELEMENT)
+ return false;
+ }
+ }
+ return true;
+}
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_VOTE_UPLOADS_TEST_MATCHERS_H_
diff --git a/chromium/components/password_manager/core/browser/votes_uploader.cc b/chromium/components/password_manager/core/browser/votes_uploader.cc
new file mode 100644
index 00000000000..b36493e5a36
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/votes_uploader.cc
@@ -0,0 +1,541 @@
+// 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/password_manager/core/browser/votes_uploader.h"
+
+#include <ctype.h>
+#include <map>
+
+#include "base/metrics/histogram_macros.h"
+#include "base/rand_util.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
+#include "components/password_manager/core/browser/password_manager_client.h"
+#include "components/password_manager/core/browser/password_manager_util.h"
+
+using autofill::AutofillField;
+using autofill::AutofillManager;
+using autofill::AutofillUploadContents;
+using autofill::FormData;
+using autofill::FormStructure;
+using autofill::PasswordForm;
+using autofill::ServerFieldType;
+using autofill::ServerFieldTypeSet;
+using autofill::ValueElementPair;
+
+using Logger = autofill::SavePasswordProgressLogger;
+
+namespace password_manager {
+namespace {
+
+// Sets autofill types of password and new password fields in |field_types|.
+// |password_type| (the autofill type of new password field) should be equal to
+// NEW_PASSWORD, PROBABLY_NEW_PASSWORD or NOT_NEW_PASSWORD. These values
+// correspond to cases when the user confirmed password update, did nothing or
+// declined to update password respectively.
+void SetFieldLabelsOnUpdate(const ServerFieldType password_type,
+ const PasswordForm& submitted_form,
+ FieldTypeMap* field_types) {
+ DCHECK(password_type == autofill::NEW_PASSWORD ||
+ password_type == autofill::PROBABLY_NEW_PASSWORD ||
+ password_type == autofill::NOT_NEW_PASSWORD)
+ << password_type;
+ if (submitted_form.new_password_element.empty())
+ return;
+
+ (*field_types)[submitted_form.password_element] = autofill::PASSWORD;
+ (*field_types)[submitted_form.new_password_element] = password_type;
+}
+
+// Sets the autofill type of the password field stored in |submitted_form| to
+// |password_type| in |field_types| map.
+void SetFieldLabelsOnSave(const ServerFieldType password_type,
+ const PasswordForm& form,
+ FieldTypeMap* field_types) {
+ DCHECK(password_type == autofill::PASSWORD ||
+ password_type == autofill::ACCOUNT_CREATION_PASSWORD ||
+ password_type == autofill::NOT_ACCOUNT_CREATION_PASSWORD)
+ << password_type;
+
+ if (!form.new_password_element.empty())
+ (*field_types)[form.new_password_element] = password_type;
+ else if (!form.password_element.empty())
+ (*field_types)[form.password_element] = password_type;
+}
+
+// Label username and password fields with autofill types in |form_structure|
+// based on |field_types|, and vote types based on |vote_types|. The function
+// also adds the types to |available_field_types|. For fields of |USERNAME|
+// type, a vote type must exist.
+void LabelFields(const FieldTypeMap& field_types,
+ const VoteTypeMap& vote_types,
+ FormStructure* form_structure,
+ ServerFieldTypeSet* available_field_types) {
+ for (size_t i = 0; i < form_structure->field_count(); ++i) {
+ AutofillField* field = form_structure->field(i);
+
+ ServerFieldType type = autofill::UNKNOWN_TYPE;
+ if (!field->name.empty()) {
+ auto iter = field_types.find(field->name);
+ if (iter != field_types.end()) {
+ type = iter->second;
+ available_field_types->insert(type);
+ }
+
+ auto vote_type_iter = vote_types.find(field->name);
+ if (vote_type_iter != vote_types.end())
+ field->set_vote_type(vote_type_iter->second);
+ DCHECK(type != autofill::USERNAME ||
+ field->vote_type() !=
+ AutofillUploadContents::Field::NO_INFORMATION);
+ }
+
+ ServerFieldTypeSet types;
+ types.insert(type);
+ field->set_possible_types(types);
+ }
+}
+
+// Returns true iff |credentials| has the same password as an entry in |matches|
+// which doesn't have a username.
+bool IsAddingUsernameToExistingMatch(
+ const PasswordForm& credentials,
+ const std::map<base::string16, const PasswordForm*>& matches) {
+ const auto match = matches.find(base::string16());
+ return !credentials.username_value.empty() && match != matches.end() &&
+ !match->second->is_public_suffix_match &&
+ match->second->password_value == credentials.password_value;
+}
+
+} // namespace
+
+VotesUploader::VotesUploader(PasswordManagerClient* client,
+ bool is_possible_change_password_form)
+ : client_(client),
+ is_possible_change_password_form_(is_possible_change_password_form) {}
+
+VotesUploader::VotesUploader(const VotesUploader& other) = default;
+VotesUploader::~VotesUploader() = default;
+
+void VotesUploader::SendVotesOnSave(
+ const FormData& observed,
+ const PasswordForm& submitted_form,
+ const std::map<base::string16, const PasswordForm*>& best_matches,
+ PasswordForm* pending_credentials) {
+ // if (observed_form_.IsPossibleChangePasswordFormWithoutUsername())
+ // return; // todo: is it needed
+
+ // Send votes for sign-in form.
+ FormData& form_data = pending_credentials->form_data;
+ if (form_data.fields.size() == 2 &&
+ form_data.fields[0].form_control_type == "text" &&
+ form_data.fields[1].form_control_type == "password") {
+ // |form_data| is received from the renderer and does not contain field
+ // values. Fill username field value with username to allow AutofillManager
+ // to detect username autofill type.
+ form_data.fields[0].value = pending_credentials->username_value;
+ SendSignInVote(form_data);
+ }
+
+ if (pending_credentials->times_used == 1 ||
+ IsAddingUsernameToExistingMatch(*pending_credentials, best_matches))
+ UploadFirstLoginVotes(best_matches, *pending_credentials, submitted_form);
+
+ // Upload credentials the first time they are saved. This data is used
+ // by password generation to help determine account creation sites.
+ // Credentials that have been previously used (e.g., PSL matches) are checked
+ // to see if they are valid account creation forms.
+ if (pending_credentials->times_used == 0) {
+ UploadPasswordVote(*pending_credentials, submitted_form, autofill::PASSWORD,
+ std::string());
+ if (has_username_correction_vote_) {
+ UploadPasswordVote(username_correction_vote_, submitted_form,
+ autofill::USERNAME,
+ FormStructure(observed).FormSignatureAsStr());
+ }
+ } else {
+ SendVoteOnCredentialsReuse(observed, submitted_form, pending_credentials);
+ }
+}
+
+void VotesUploader::SendVoteOnCredentialsReuse(
+ const FormData& observed,
+ const PasswordForm& submitted_form,
+ PasswordForm* pending) {
+ // Ignore |pending_structure| if its FormData has no fields. This is to
+ // weed out those credentials that were saved before FormData was added
+ // to PasswordForm. Even without this check, these FormStructure's won't
+ // be uploaded, but it makes it hard to see if we are encountering
+ // unexpected errors.
+ if (pending->form_data.fields.empty())
+ return;
+
+ FormStructure pending_structure(pending->form_data);
+ FormStructure observed_structure(observed);
+
+ if (pending_structure.form_signature() !=
+ observed_structure.form_signature()) {
+ // Only upload if this is the first time the password has been used.
+ // Otherwise the credentials have been used on the same field before so
+ // they aren't from an account creation form.
+ // Also bypass uploading if the username was edited. Offering generation
+ // in cases where we currently save the wrong username isn't great.
+ if (pending->times_used == 1) {
+ if (UploadPasswordVote(*pending, submitted_form,
+ autofill::ACCOUNT_CREATION_PASSWORD,
+ observed_structure.FormSignatureAsStr())) {
+ pending->generation_upload_status = PasswordForm::POSITIVE_SIGNAL_SENT;
+ }
+ }
+ } else if (pending->generation_upload_status ==
+ PasswordForm::POSITIVE_SIGNAL_SENT) {
+ // A signal was sent that this was an account creation form, but the
+ // credential is now being used on the same form again. This cancels out
+ // the previous vote.
+ if (UploadPasswordVote(*pending, submitted_form,
+ autofill::NOT_ACCOUNT_CREATION_PASSWORD,
+ std::string())) {
+ pending->generation_upload_status = PasswordForm::NEGATIVE_SIGNAL_SENT;
+ }
+ } else if (generation_popup_was_shown_) {
+ // Even if there is no autofill vote to be sent, send the vote about the
+ // usage of the generation popup.
+ UploadPasswordVote(*pending, submitted_form, autofill::UNKNOWN_TYPE,
+ std::string());
+ }
+}
+
+bool VotesUploader::UploadPasswordVote(
+ const PasswordForm& form_to_upload,
+ const PasswordForm& submitted_form,
+ const ServerFieldType autofill_type,
+ const std::string& login_form_signature) {
+ // Check if there is any vote to be sent.
+ bool has_autofill_vote = autofill_type != autofill::UNKNOWN_TYPE;
+ bool has_password_generation_vote = generation_popup_was_shown_;
+ if (!has_autofill_vote && !has_password_generation_vote)
+ return false;
+
+ AutofillManager* autofill_manager = client_->GetAutofillManagerForMainFrame();
+ if (!autofill_manager || !autofill_manager->download_manager())
+ return false;
+
+ // If this is an update, a vote about the observed form is sent. If the user
+ // re-uses credentials, a vote about the saved form is sent. If the user saves
+ // credentials, the observed and pending forms are the same.
+ FormStructure form_structure(form_to_upload.form_data);
+ if (!autofill_manager->ShouldUploadForm(form_structure)) {
+ UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", false);
+ return false;
+ }
+
+ ServerFieldTypeSet available_field_types;
+ // A map from field names to field types.
+ FieldTypeMap field_types;
+ auto username_vote_type = AutofillUploadContents::Field::NO_INFORMATION;
+ if (autofill_type != autofill::USERNAME) {
+ if (has_autofill_vote) {
+ bool is_update = autofill_type == autofill::NEW_PASSWORD ||
+ autofill_type == autofill::PROBABLY_NEW_PASSWORD ||
+ autofill_type == autofill::NOT_NEW_PASSWORD;
+
+ if (is_update) {
+ if (form_to_upload.new_password_element.empty())
+ return false;
+ SetFieldLabelsOnUpdate(autofill_type, form_to_upload, &field_types);
+ } else { // Saving.
+ SetFieldLabelsOnSave(autofill_type, form_to_upload, &field_types);
+ }
+ if (autofill_type != autofill::ACCOUNT_CREATION_PASSWORD) {
+ // If |autofill_type| == autofill::ACCOUNT_CREATION_PASSWORD, Chrome
+ // will upload a vote for another form: the one that the credential was
+ // saved on.
+ field_types[submitted_form.confirmation_password_element] =
+ autofill::CONFIRMATION_PASSWORD;
+ form_structure.set_passwords_were_revealed(
+ has_passwords_revealed_vote_);
+ }
+ }
+ if (autofill_type != autofill::ACCOUNT_CREATION_PASSWORD) {
+ if (generation_popup_was_shown_)
+ AddGeneratedVote(&form_structure);
+ if (form_classifier_outcome_ != kNoOutcome)
+ AddFormClassifierVote(&form_structure);
+ if (has_username_edited_vote_) {
+ field_types[form_to_upload.username_element] = autofill::USERNAME;
+ username_vote_type = AutofillUploadContents::Field::USERNAME_EDITED;
+ }
+ } else { // User reuses credentials.
+ // If the saved username value was used, then send a confirmation vote for
+ // username.
+ if (!submitted_form.username_value.empty()) {
+ DCHECK(submitted_form.username_value == form_to_upload.username_value);
+ field_types[form_to_upload.username_element] = autofill::USERNAME;
+ username_vote_type = AutofillUploadContents::Field::CREDENTIALS_REUSED;
+ }
+ }
+ if (autofill_type == autofill::PASSWORD) {
+ // The password attributes should be uploaded only on the first save.
+ DCHECK(form_to_upload.times_used == 0);
+ GeneratePasswordAttributesVote(form_to_upload.password_value,
+ &form_structure);
+ }
+ } else { // User overwrites username.
+ field_types[form_to_upload.username_element] = autofill::USERNAME;
+ field_types[form_to_upload.password_element] =
+ autofill::ACCOUNT_CREATION_PASSWORD;
+ username_vote_type = AutofillUploadContents::Field::USERNAME_OVERWRITTEN;
+ }
+ LabelFields(field_types,
+ {{form_to_upload.username_element, username_vote_type}},
+ &form_structure, &available_field_types);
+
+ // Force uploading as these events are relatively rare and we want to make
+ // sure to receive them.
+ form_structure.set_upload_required(UPLOAD_REQUIRED);
+
+ if (password_manager_util::IsLoggingActive(client_)) {
+ BrowserSavePasswordProgressLogger logger(client_->GetLogManager());
+ logger.LogFormStructure(Logger::STRING_FORM_VOTES, form_structure);
+ }
+
+ bool success = autofill_manager->download_manager()->StartUploadRequest(
+ form_structure, false /* was_autofilled */, available_field_types,
+ login_form_signature, true /* observed_submission */);
+
+ UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", success);
+ return success;
+}
+
+// TODO(crbug.com/840384): Share common code with UploadPasswordVote.
+void VotesUploader::UploadFirstLoginVotes(
+ const std::map<base::string16, const PasswordForm*>& best_matches,
+ const PasswordForm& pending_credentials,
+ const PasswordForm& form_to_upload) {
+ AutofillManager* autofill_manager = client_->GetAutofillManagerForMainFrame();
+ if (!autofill_manager || !autofill_manager->download_manager())
+ return;
+
+ FormStructure form_structure(form_to_upload.form_data);
+ if (!autofill_manager->ShouldUploadForm(form_structure))
+ return;
+
+ FieldTypeMap field_types = {
+ {form_to_upload.username_element, autofill::USERNAME}};
+ VoteTypeMap vote_types = {{form_to_upload.username_element,
+ AutofillUploadContents::Field::FIRST_USE}};
+ if (!password_overridden_) {
+ field_types[form_to_upload.password_element] = autofill::PASSWORD;
+ vote_types[form_to_upload.password_element] =
+ AutofillUploadContents::Field::FIRST_USE;
+ }
+
+ ServerFieldTypeSet available_field_types;
+ LabelFields(field_types, vote_types, &form_structure, &available_field_types);
+ SetKnownValueFlag(pending_credentials, best_matches, &form_structure);
+
+ // Force uploading as these events are relatively rare and we want to make
+ // sure to receive them.
+ form_structure.set_upload_required(UPLOAD_REQUIRED);
+
+ if (password_manager_util::IsLoggingActive(client_)) {
+ BrowserSavePasswordProgressLogger logger(client_->GetLogManager());
+ logger.LogFormStructure(Logger::STRING_FORM_VOTES, form_structure);
+ }
+
+ autofill_manager->download_manager()->StartUploadRequest(
+ form_structure, false /* was_autofilled */, available_field_types,
+ std::string(), true /* observed_submission */);
+}
+
+void VotesUploader::SendSignInVote(const FormData& form_data) {
+ AutofillManager* autofill_manager = client_->GetAutofillManagerForMainFrame();
+ if (!autofill_manager)
+ return;
+ std::unique_ptr<FormStructure> form_structure(new FormStructure(form_data));
+ form_structure->set_is_signin_upload(true);
+ DCHECK(form_structure->ShouldBeUploaded());
+ DCHECK_EQ(2u, form_structure->field_count());
+ form_structure->field(1)->set_possible_types({autofill::PASSWORD});
+ autofill_manager->MaybeStartVoteUploadProcess(std::move(form_structure),
+ base::TimeTicks::Now(),
+ /*observed_submission=*/true);
+}
+
+void VotesUploader::AddGeneratedVote(FormStructure* form_structure) {
+ DCHECK(form_structure);
+ DCHECK(generation_popup_was_shown_);
+
+ if (generation_element_.empty())
+ return;
+
+ AutofillUploadContents::Field::PasswordGenerationType type =
+ AutofillUploadContents::Field::NO_GENERATION;
+ if (has_generated_password_) {
+ UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.IsTriggeredManually",
+ is_manual_generation_);
+ if (is_manual_generation_) {
+ type = is_possible_change_password_form_
+ ? AutofillUploadContents::Field::
+ MANUALLY_TRIGGERED_GENERATION_ON_CHANGE_PASSWORD_FORM
+ : AutofillUploadContents::Field::
+ MANUALLY_TRIGGERED_GENERATION_ON_SIGN_UP_FORM;
+ } else {
+ type =
+ is_possible_change_password_form_
+ ? AutofillUploadContents::Field::
+ AUTOMATICALLY_TRIGGERED_GENERATION_ON_CHANGE_PASSWORD_FORM
+ : AutofillUploadContents::Field::
+ AUTOMATICALLY_TRIGGERED_GENERATION_ON_SIGN_UP_FORM;
+ }
+ } else
+ type = AutofillUploadContents::Field::IGNORED_GENERATION_POPUP;
+
+ for (size_t i = 0; i < form_structure->field_count(); ++i) {
+ AutofillField* field = form_structure->field(i);
+ if (field->name == generation_element_) {
+ field->set_generation_type(type);
+ if (has_generated_password_) {
+ field->set_generated_password_changed(generated_password_changed_);
+ UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.GeneratedPasswordWasEdited",
+ generated_password_changed_);
+ }
+ break;
+ }
+ }
+}
+
+void VotesUploader::AddFormClassifierVote(FormStructure* form_structure) {
+ DCHECK(form_structure);
+ DCHECK(form_classifier_outcome_ != kNoOutcome);
+
+ for (size_t i = 0; i < form_structure->field_count(); ++i) {
+ AutofillField* field = form_structure->field(i);
+ if (form_classifier_outcome_ == kFoundGenerationElement &&
+ field->name == generation_element_detected_by_classifier_) {
+ field->set_form_classifier_outcome(
+ AutofillUploadContents::Field::GENERATION_ELEMENT);
+ } else {
+ field->set_form_classifier_outcome(
+ AutofillUploadContents::Field::NON_GENERATION_ELEMENT);
+ }
+ }
+}
+
+void VotesUploader::SetKnownValueFlag(
+ const PasswordForm& pending_credentials,
+ const std::map<base::string16, const PasswordForm*>& best_matches,
+ FormStructure* form) {
+ DCHECK(!password_overridden_ ||
+ best_matches.find(pending_credentials.username_value) !=
+ best_matches.end())
+ << "The credential is being overriden, but it does not exist in "
+ "the best matches.";
+
+ const base::string16& known_username = pending_credentials.username_value;
+ // If we are updating a password, the known value is the old password, not
+ // the new one.
+ const base::string16& known_password =
+ password_overridden_ ? best_matches.at(known_username)->password_value
+ : pending_credentials.password_value;
+
+ for (auto& field : *form) {
+ if (field->value.empty())
+ continue;
+ if (known_username == field->value || known_password == field->value) {
+ field->properties_mask |= autofill::FieldPropertiesFlags::KNOWN_VALUE;
+ }
+ }
+}
+
+void VotesUploader::SaveGenerationFieldDetectedByClassifier(
+ const base::string16& generation_field) {
+ form_classifier_outcome_ =
+ generation_field.empty() ? kNoGenerationElement : kFoundGenerationElement;
+ generation_element_detected_by_classifier_ = generation_field;
+}
+
+bool VotesUploader::FindUsernameInOtherPossibleUsernames(
+ const PasswordForm& match,
+ const base::string16& username) {
+ DCHECK(!has_username_correction_vote_);
+
+ for (const ValueElementPair& pair : match.other_possible_usernames) {
+ if (pair.first == username) {
+ username_correction_vote_ = match;
+ username_correction_vote_.username_element = pair.second;
+ has_username_correction_vote_ = true;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool VotesUploader::FindCorrectedUsernameElement(
+ const std::map<base::string16, const PasswordForm*>& best_matches,
+ const std::vector<const PasswordForm*>& not_best_matches,
+ const base::string16& username,
+ const base::string16& password) {
+ if (username.empty())
+ return false;
+ for (const auto& key_value : best_matches) {
+ const PasswordForm* match = key_value.second;
+ if ((match->password_value == password) &&
+ FindUsernameInOtherPossibleUsernames(*match, username))
+ return true;
+ }
+ for (const PasswordForm* match : not_best_matches) {
+ if ((match->password_value == password) &&
+ FindUsernameInOtherPossibleUsernames(*match, username))
+ return true;
+ }
+ return false;
+}
+
+void VotesUploader::GeneratePasswordAttributesVote(
+ const base::string16& password_value,
+ FormStructure* form_structure) {
+ // Select a character class attribute to upload.
+ int bucket = base::RandGenerator(9);
+ int (*predicate)(int c) = nullptr;
+ autofill::PasswordAttribute character_class_attribute =
+ autofill::PasswordAttribute::kHasSpecialSymbol;
+ if (bucket == 0) {
+ predicate = &islower;
+ character_class_attribute =
+ autofill::PasswordAttribute::kHasLowercaseLetter;
+ } else if (bucket == 1) {
+ predicate = &isupper;
+ character_class_attribute =
+ autofill::PasswordAttribute::kHasUppercaseLetter;
+ } else if (bucket == 2) {
+ predicate = &isdigit;
+ character_class_attribute = autofill::PasswordAttribute::kHasNumeric;
+ } else { // 3 <= bucket < 9
+ // Upload symbols more often as 2/3rd of issues are because of missing
+ // special symbols.
+ predicate = &ispunct;
+ character_class_attribute = autofill::PasswordAttribute::kHasSpecialSymbol;
+ }
+ bool actual_value_for_character_class =
+ std::any_of(password_value.begin(), password_value.end(), predicate);
+
+ // Apply the randomized response technique to noisify the actual value
+ // (https://en.wikipedia.org/wiki/Randomized_response).
+ bool randomized_value_for_character_class =
+ base::RandGenerator(2) ? actual_value_for_character_class
+ : base::RandGenerator(2);
+ form_structure->set_password_attributes_vote(std::make_pair(
+ character_class_attribute, randomized_value_for_character_class));
+
+ size_t actual_length = password_value.size();
+ size_t randomized_length = actual_length <= 1 || base::RandGenerator(5) == 0
+ ? actual_length
+ : base::RandGenerator(actual_length - 1) + 1;
+ form_structure->set_password_length_vote(randomized_length);
+}
+
+} // namespace password_manager
diff --git a/chromium/components/password_manager/core/browser/votes_uploader.h b/chromium/components/password_manager/core/browser/votes_uploader.h
new file mode 100644
index 00000000000..0756586c54b
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/votes_uploader.h
@@ -0,0 +1,218 @@
+// 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_PASSWORD_MANAGER_CORE_BROWSER_VOTES_UPLOADER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_VOTES_UPLOADER_H_
+
+#include <string>
+
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/proto/server.pb.h"
+#include "components/autofill/core/common/password_form.h"
+
+namespace autofill {
+struct FormData;
+class FormStructure;
+} // namespace autofill
+
+namespace password_manager {
+
+class PasswordManagerClient;
+
+// A map from field names to field types.
+using FieldTypeMap = std::map<base::string16, autofill::ServerFieldType>;
+// A map from field names to field vote types.
+using VoteTypeMap =
+ std::map<base::string16, autofill::AutofillUploadContents::Field::VoteType>;
+
+// This class manages vote uploads for password forms.
+class VotesUploader {
+ public:
+ VotesUploader(PasswordManagerClient* client,
+ bool is_possible_change_password_form);
+ VotesUploader(const VotesUploader& other);
+ ~VotesUploader();
+
+ // Send appropriate votes based on what is currently being saved.
+ void SendVotesOnSave(
+ const autofill::FormData& observed,
+ const autofill::PasswordForm& submitted_form,
+ const std::map<base::string16, const autofill::PasswordForm*>&
+ best_matches,
+ autofill::PasswordForm* pending_credentials);
+
+ // Check to see if |pending| corresponds to an account creation form. If we
+ // think that it does, we label it as such and upload this state to the
+ // Autofill server to vote for the correct username field, and also so that
+ // we will trigger password generation in the future. This function will
+ // update generation_upload_status of |pending| if an upload is performed.
+ void SendVoteOnCredentialsReuse(const autofill::FormData& observed,
+ const autofill::PasswordForm& submitted_form,
+ autofill::PasswordForm* pending);
+
+ // Tries to set all votes (e.g. autofill field types, generation vote) to
+ // a |FormStructure| and upload it to the server. Returns true on success.
+ bool UploadPasswordVote(const autofill::PasswordForm& form_to_upload,
+ const autofill::PasswordForm& submitted_form,
+ const autofill::ServerFieldType password_type,
+ const std::string& login_form_signature);
+
+ // Sends USERNAME and PASSWORD votes, when a credential is used to login for
+ // the first time. |form_to_upload| is the submitted login form.
+ void UploadFirstLoginVotes(
+ const std::map<base::string16, const autofill::PasswordForm*>&
+ best_matches,
+ const autofill::PasswordForm& pending_credentials,
+ const autofill::PasswordForm& form_to_upload);
+
+ // Saves the outcome of HTML parsing based form classifier to upload proto.
+ void SaveGenerationFieldDetectedByClassifier(
+ const base::string16& generation_field);
+
+ // Searches for |username| in |other_possible_usernames| of |best_matches|
+ // and |not_best_matches|. If the username value is found in
+ // |other_possible_usernames| and the password value of the match is equal to
+ // |password|, the match is saved to |username_correction_vote_| and the
+ // method returns true.
+ bool FindCorrectedUsernameElement(
+ const std::map<base::string16, const autofill::PasswordForm*>&
+ best_matches,
+ const std::vector<const autofill::PasswordForm*>& not_best_matches,
+ const base::string16& username,
+ const base::string16& password);
+
+ // Generates a password attributes vote based on |password_value| and saves it
+ // to |form_structure|. Declared as public for testing.
+ void GeneratePasswordAttributesVote(const base::string16& password_value,
+ autofill::FormStructure* form_structure);
+
+ bool get_generation_popup_was_shown() const {
+ return generation_popup_was_shown_;
+ }
+ void set_generation_popup_was_shown(bool generation_popup_was_shown) {
+ generation_popup_was_shown_ = generation_popup_was_shown;
+ }
+
+ bool is_manual_generation() const { return is_manual_generation_; }
+ void set_is_manual_generation(bool is_manual_generation) {
+ is_manual_generation_ = is_manual_generation;
+ }
+
+ const base::string16& get_generation_element() const {
+ return generation_element_;
+ }
+ void set_generation_element(const base::string16& generation_element) {
+ generation_element_ = generation_element;
+ }
+
+ void set_has_username_edited_vote(bool has_username_edited_vote) {
+ has_username_edited_vote_ = has_username_edited_vote;
+ }
+
+ void set_has_passwords_revealed_vote(bool has_passwords_revealed_vote) {
+ has_passwords_revealed_vote_ = has_passwords_revealed_vote;
+ }
+
+ void set_password_overridden(bool password_overridden) {
+ password_overridden_ = password_overridden;
+ }
+
+ void set_has_generated_password(bool has_generated_password) {
+ has_generated_password_ = has_generated_password;
+ }
+
+ bool generated_password_changed() const {
+ return generated_password_changed_;
+ }
+
+ void set_generated_password_changed(bool generated_password_changed) {
+ generated_password_changed_ = generated_password_changed;
+ }
+
+ private:
+ // The outcome of the form classifier.
+ enum FormClassifierOutcome {
+ kNoOutcome,
+ kNoGenerationElement,
+ kFoundGenerationElement
+ };
+
+ // Send a vote for sign-in forms with autofill types for a username field.
+ // TODO(https://crbug.com/831123): Remove this method.
+ void SendSignInVote(const autofill::FormData& form_data);
+
+ // Adds a vote on password generation usage to |form_structure|.
+ void AddGeneratedVote(autofill::FormStructure* form_structure);
+
+ // Adds a vote from HTML parsing based form classifier to |form_structure|.
+ void AddFormClassifierVote(autofill::FormStructure* form_structure);
+
+ // Sets the known-value flag for each field, indicating that the field
+ // contained a previously stored credential on submission.
+ void SetKnownValueFlag(
+ const autofill::PasswordForm& pending_credentials,
+ const std::map<base::string16, const autofill::PasswordForm*>&
+ best_matches,
+ autofill::FormStructure* form_to_upload);
+
+ // Searches for |username| in |other_possible_usernames| of |match|. If the
+ // username value is found, the match is saved to |username_correction_vote_|
+ // and the function returns true.
+ bool FindUsernameInOtherPossibleUsernames(const autofill::PasswordForm& match,
+ const base::string16& username);
+
+ // The client which implements embedder-specific PasswordManager operations.
+ PasswordManagerClient* client_;
+
+ // The outcome of HTML parsing based form classifier.
+ FormClassifierOutcome form_classifier_outcome_ = kNoOutcome;
+
+ // If |form_classifier_outcome_| == kFoundGenerationElement, the field
+ // contains the name of the detected generation element.
+ base::string16 generation_element_detected_by_classifier_;
+
+ // Whether generation popup was shown at least once.
+ bool generation_popup_was_shown_ = false;
+
+ // Whether password generation was manually triggered.
+ bool is_manual_generation_ = false;
+
+ // A password field name that is used for generation.
+ base::string16 generation_element_;
+
+ // True iff a user edited the username value in a prompt and new username is
+ // the value of another field of the observed form.
+ bool has_username_edited_vote_ = false;
+
+ // If the user typed username that doesn't match any saved credentials, but
+ // matches an entry from |other_possible_usernames| of a saved credential,
+ // then |has_username_correction_vote_| is set to true and
+ // |username_correction_vote_| stores the credential with matched username.
+ // The matched credential is copied to |username_correction_vote_|, but
+ // |username_correction_vote_.username_element| is set to the name of the
+ // field where matched username was found.
+ bool has_username_correction_vote_ = false;
+ autofill::PasswordForm username_correction_vote_;
+
+ // Whether the password values have been shown to the user on the save prompt.
+ bool has_passwords_revealed_vote_ = false;
+
+ // Whether the saved password was overridden.
+ bool password_overridden_ = false;
+
+ // True if the observed form of owning PasswordFormManager is considered to be
+ // change password form.
+ bool is_possible_change_password_form_ = false;
+
+ // Whether this form has an auto generated password.
+ bool has_generated_password_ = false;
+
+ // Whether this form has a generated password changed by user.
+ bool generated_password_changed_ = false;
+};
+
+} // namespace password_manager
+
+#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_VOTES_UPLOADER_H_
diff --git a/chromium/components/password_manager/core/browser/votes_uploader_unittest.cc b/chromium/components/password_manager/core/browser/votes_uploader_unittest.cc
new file mode 100644
index 00000000000..eed3ad9ad0a
--- /dev/null
+++ b/chromium/components/password_manager/core/browser/votes_uploader_unittest.cc
@@ -0,0 +1,263 @@
+// 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/password_manager/core/browser/votes_uploader.h"
+
+#include <string>
+
+#include "base/optional.h"
+#include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_download_manager.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/browser/form_structure.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_personal_data_manager.h"
+#include "components/password_manager/core/browser/stub_password_manager_client.h"
+#include "components/password_manager/core/browser/vote_uploads_test_matchers.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"
+
+using autofill::CONFIRMATION_PASSWORD;
+using autofill::FormFieldData;
+using autofill::FormStructure;
+using autofill::NEW_PASSWORD;
+using autofill::PASSWORD;
+using autofill::PasswordForm;
+using autofill::ServerFieldTypeSet;
+using base::ASCIIToUTF16;
+using testing::_;
+using testing::AllOf;
+using testing::AnyNumber;
+using testing::Return;
+using testing::SaveArg;
+
+namespace password_manager {
+namespace {
+
+constexpr int kNumberOfPasswordAttributes =
+ static_cast<int>(autofill::PasswordAttribute::kPasswordAttributesCount);
+
+class MockAutofillDownloadManager : public autofill::AutofillDownloadManager {
+ public:
+ MockAutofillDownloadManager(
+ autofill::AutofillDriver* driver,
+ autofill::AutofillDownloadManager::Observer* observer)
+ : AutofillDownloadManager(driver, observer) {}
+
+ MOCK_METHOD5(StartUploadRequest,
+ bool(const FormStructure&,
+ bool,
+ const ServerFieldTypeSet&,
+ const std::string&,
+ bool));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillDownloadManager);
+};
+
+class MockAutofillManager : public autofill::AutofillManager {
+ public:
+ MockAutofillManager(autofill::AutofillDriver* driver,
+ autofill::AutofillClient* client,
+ autofill::PersonalDataManager* data_manager)
+ : AutofillManager(driver, client, data_manager) {}
+
+ void SetDownloadManager(autofill::AutofillDownloadManager* manager) {
+ set_download_manager(manager);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillManager);
+};
+
+class MockPasswordManagerClient : public StubPasswordManagerClient {
+ public:
+ MOCK_METHOD0(GetAutofillManagerForMainFrame, autofill::AutofillManager*());
+};
+
+} // namespace
+
+class VotesUploaderTest : public testing::Test {
+ public:
+ VotesUploaderTest()
+ : mock_autofill_manager_(&test_autofill_driver_,
+ &test_autofill_client_,
+ &test_personal_data_manager_) {
+ std::unique_ptr<TestingPrefServiceSimple> prefs(
+ new TestingPrefServiceSimple());
+ prefs->registry()->RegisterBooleanPref(autofill::prefs::kAutofillEnabled,
+ true);
+ test_autofill_client_.SetPrefs(std::move(prefs));
+ mock_autofill_download_manager_ = new MockAutofillDownloadManager(
+ &test_autofill_driver_, &mock_autofill_manager_);
+ // AutofillManager takes ownership of |mock_autofill_download_manager_|.
+ mock_autofill_manager_.SetDownloadManager(mock_autofill_download_manager_);
+
+ EXPECT_CALL(client_, GetAutofillManagerForMainFrame())
+ .WillRepeatedly(Return(&mock_autofill_manager_));
+
+ ON_CALL(*mock_autofill_download_manager_, StartUploadRequest(_, _, _, _, _))
+ .WillByDefault(Return(true));
+
+ // Create |fields| in |form_to_upload_| and |submitted_form_|. Only |name|
+ // field in FormFieldData is important. Set them to the unique values based
+ // on index.
+ const size_t kNumberOfFields = 20;
+ for (size_t i = 0; i < kNumberOfFields; ++i) {
+ FormFieldData field;
+ field.name = GetFieldNameByIndex(i);
+ form_to_upload_.form_data.fields.push_back(field);
+ submitted_form_.form_data.fields.push_back(field);
+ }
+ }
+
+ protected:
+ base::string16 GetFieldNameByIndex(size_t index) {
+ return ASCIIToUTF16("field") + base::UintToString16(index);
+ }
+
+ autofill::TestAutofillDriver test_autofill_driver_;
+ autofill::TestAutofillClient test_autofill_client_;
+ autofill::TestPersonalDataManager test_personal_data_manager_;
+ MockAutofillDownloadManager* mock_autofill_download_manager_;
+
+ MockAutofillManager mock_autofill_manager_;
+ MockPasswordManagerClient client_;
+
+ PasswordForm form_to_upload_;
+ PasswordForm submitted_form_;
+
+ std::string login_form_signature_ = "123";
+};
+
+TEST_F(VotesUploaderTest, UploadPasswordVoteUpdate) {
+ VotesUploader votes_uploader(&client_, true);
+ base::string16 new_password_element = GetFieldNameByIndex(3);
+ base::string16 confirmation_element = GetFieldNameByIndex(11);
+ form_to_upload_.new_password_element = new_password_element;
+ submitted_form_.new_password_element = new_password_element;
+ form_to_upload_.confirmation_password_element = confirmation_element;
+ submitted_form_.confirmation_password_element = confirmation_element;
+ ServerFieldTypeSet expexted_field_types = {NEW_PASSWORD,
+ CONFIRMATION_PASSWORD};
+ FieldTypeMap expected_types = {{new_password_element, NEW_PASSWORD},
+ {confirmation_element, CONFIRMATION_PASSWORD}};
+
+ EXPECT_CALL(*mock_autofill_download_manager_,
+ StartUploadRequest(
+ AllOf(SignatureIsSameAs(form_to_upload_),
+ UploadedAutofillTypesAre(expected_types)),
+ false, expexted_field_types, login_form_signature_, true));
+
+ EXPECT_TRUE(votes_uploader.UploadPasswordVote(
+ form_to_upload_, submitted_form_, NEW_PASSWORD, login_form_signature_));
+}
+
+TEST_F(VotesUploaderTest, UploadPasswordVoteSave) {
+ VotesUploader votes_uploader(&client_, false);
+ base::string16 password_element = GetFieldNameByIndex(5);
+ base::string16 confirmation_element = GetFieldNameByIndex(12);
+ form_to_upload_.password_element = password_element;
+ submitted_form_.password_element = password_element;
+ form_to_upload_.confirmation_password_element = confirmation_element;
+ submitted_form_.confirmation_password_element = confirmation_element;
+ ServerFieldTypeSet expexted_field_types = {PASSWORD, CONFIRMATION_PASSWORD};
+
+ EXPECT_CALL(*mock_autofill_download_manager_,
+ StartUploadRequest(_, false, expexted_field_types,
+ login_form_signature_, true));
+
+ EXPECT_TRUE(votes_uploader.UploadPasswordVote(
+ form_to_upload_, submitted_form_, PASSWORD, login_form_signature_));
+}
+
+TEST_F(VotesUploaderTest, GeneratePasswordAttributesVote) {
+ VotesUploader votes_uploader(&client_, true);
+ // Checks that randomization distorts information about present and missed
+ // character classess, but a true value is still restorable with aggregation
+ // of many distorted reports.
+ const char* kPasswordSnippets[] = {"abc", "XYZ", "123", "*-_"};
+ for (int test_case = 0; test_case < 10; ++test_case) {
+ bool has_password_attribute[kNumberOfPasswordAttributes];
+ base::string16 password_value;
+ for (int i = 0; i < kNumberOfPasswordAttributes; ++i) {
+ has_password_attribute[i] = base::RandGenerator(2);
+ if (has_password_attribute[i])
+ password_value += ASCIIToUTF16(kPasswordSnippets[i]);
+ }
+ if (password_value.empty())
+ continue;
+
+ autofill::FormData form;
+ autofill::FormStructure form_structure(form);
+ int reported_false[kNumberOfPasswordAttributes] = {0, 0, 0, 0};
+ int reported_true[kNumberOfPasswordAttributes] = {0, 0, 0, 0};
+
+ int reported_actual_length = 0;
+ int reported_wrong_length = 0;
+
+ for (int i = 0; i < 1000; ++i) {
+ votes_uploader.GeneratePasswordAttributesVote(password_value,
+ &form_structure);
+ base::Optional<std::pair<autofill::PasswordAttribute, bool>> vote =
+ form_structure.get_password_attributes_vote_for_testing();
+ int attribute_index = static_cast<int>(vote->first);
+ if (vote->second)
+ reported_true[attribute_index]++;
+ else
+ reported_false[attribute_index]++;
+ size_t reported_length =
+ form_structure.get_password_length_vote_for_testing();
+ if (reported_length == password_value.size()) {
+ reported_actual_length++;
+ } else {
+ reported_wrong_length++;
+ EXPECT_LT(0u, reported_length);
+ EXPECT_LT(reported_length, password_value.size());
+ }
+ }
+ for (int i = 0; i < kNumberOfPasswordAttributes; i++) {
+ EXPECT_LT(0, reported_false[i]);
+ EXPECT_LT(0, reported_true[i]);
+
+ // If the actual value is |true|, then it should report more |true|s than
+ // |false|s.
+ if (has_password_attribute[i]) {
+ EXPECT_LT(reported_false[i], reported_true[i])
+ << "Wrong distribution for attribute " << i
+ << ". password_value = " << password_value;
+ } else {
+ EXPECT_GT(reported_false[i], reported_true[i])
+ << "Wrong distribution for attribute " << i
+ << ". password_value = " << password_value;
+ }
+ }
+ EXPECT_LT(0, reported_actual_length);
+ EXPECT_LT(0, reported_wrong_length);
+ EXPECT_LT(reported_actual_length, reported_wrong_length);
+ }
+}
+
+TEST_F(VotesUploaderTest, GeneratePasswordAttributesVote_OneCharacterPassword) {
+ // |VotesUploader::GeneratePasswordAttributesVote| shouldn't crash if a
+ // password has only one character.
+ autofill::FormData form;
+ autofill::FormStructure form_structure(form);
+ VotesUploader votes_uploader(&client_, true);
+ votes_uploader.GeneratePasswordAttributesVote(ASCIIToUTF16("1"),
+ &form_structure);
+ base::Optional<std::pair<autofill::PasswordAttribute, bool>> vote =
+ form_structure.get_password_attributes_vote_for_testing();
+ EXPECT_TRUE(vote.has_value());
+ size_t reported_length =
+ form_structure.get_password_length_vote_for_testing();
+ EXPECT_EQ(1u, reported_length);
+}
+
+} // namespace password_manager
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 3491aec200a..437840a575b 100644
--- a/chromium/components/password_manager/core/common/password_manager_features.cc
+++ b/chromium/components/password_manager/core/common/password_manager_features.cc
@@ -14,40 +14,34 @@ namespace features {
const base::Feature kAffiliationBasedMatching = {
"AffiliationBasedMatching", base::FEATURE_ENABLED_BY_DEFAULT};
+// Enables links to the setting pages from the Chrome profile menu for Passwords
+// and Autofill.
+const base::Feature kAutofillHome = {"AutofillHome",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
// Use HTML based username detector.
const base::Feature kHtmlBasedUsernameDetector = {
"HtmlBaseUsernameDetector", base::FEATURE_ENABLED_BY_DEFAULT};
-// Enable additional elements in the form popup UI, which will allow the user to
-// view all saved passwords.
-const base::Feature kEnableManualFallbacksGeneration = {
- "EnableManualFallbacksGeneration", base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Enable additional elements in the form popup UI, which will allow the user to
-// trigger generation or view all saved passwords.
-const base::Feature kManualFallbacksFilling = {
- "ManualFallbacksFilling", base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Enable a standalone popup UI, which will allow the user to view all saved
-// passwords.
-const base::Feature kEnableManualFallbacksFillingStandalone = {
- "EnableManualFallbacksFillingStandalone",
- base::FEATURE_DISABLED_BY_DEFAULT};
-
// Enable a context menu item in the password field that allows the user
// to manually enforce saving of their password.
const base::Feature kPasswordForceSaving = {
"PasswordForceSaving", base::FEATURE_DISABLED_BY_DEFAULT};
-// Enable the user to trigger password generation manually.
-const base::Feature kEnableManualPasswordGeneration = {
- "enable-manual-password-generation", base::FEATURE_ENABLED_BY_DEFAULT};
+// Controls the ability to generate passwords that fit sites' requirements.
+const base::Feature kPasswordGenerationRequirements = {
+ "PasswordGenerationRequirements", base::FEATURE_ENABLED_BY_DEFAULT};
-// Enables the "Show all saved passwords" option in Context Menu.
-const base::Feature kEnableShowAllSavedPasswordsContextMenu{
- "kEnableShowAllSavedPasswordsContextMenu",
+// Controls whether password requirements can be overridden for domains
+// (as opposed to only relying on the autofill server).
+const base::Feature kPasswordGenerationRequirementsDomainOverrides = {
+ "PasswordGenerationRequirementsDomainOverrides",
base::FEATURE_ENABLED_BY_DEFAULT};
+// Enables the "Show all saved passwords" option in Context Menu.
+const base::Feature kShowAllSavedPasswordsContextMenu{
+ "ShowAllSavedPasswordsContextMenu", base::FEATURE_ENABLED_BY_DEFAULT};
+
// Disallow autofilling of the sync credential.
const base::Feature kProtectSyncCredential = {
"protect-sync-credential", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -82,6 +76,26 @@ const base::Feature kFillOnAccountSelect = {"fill-on-account-select",
const base::Feature kNewPasswordFormParsing = {
"new-password-form-parsing", base::FEATURE_DISABLED_BY_DEFAULT};
+// Field trial identifier for password generation requirements.
+const char* kGenerationRequirementsFieldTrial =
+ "PasswordGenerationRequirements";
+
+// The file version number of password requirements files. If the prefix length
+// changes, this version number needs to be updated.
+// Default to 0 in order to get an empty requirements file.
+const char* kGenerationRequirementsVersion = "version";
+
+// Length of a hash prefix of domain names. This is used to shard domains
+// across multiple files.
+// Default to 0 in order to put all domain names into the same shard.
+const char* kGenerationRequirementsPrefixLength = "prefix_length";
+
+// Timeout (in milliseconds) for password requirements lookups. As this is a
+// network request in the background that does not block the UI, the impact of
+// high values is not strong.
+// Default to 5000 ms.
+const char* kGenerationRequirementsTimeout = "timeout";
+
} // namespace features
} // namespace password_manager
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 87e3e469a96..cc597179f33 100644
--- a/chromium/components/password_manager/core/common/password_manager_features.h
+++ b/chromium/components/password_manager/core/common/password_manager_features.h
@@ -18,13 +18,12 @@ namespace features {
// alongside the definition of their values in the .cc file.
extern const base::Feature kAffiliationBasedMatching;
+extern const base::Feature kAutofillHome;
extern const base::Feature kHtmlBasedUsernameDetector;
-extern const base::Feature kManualFallbacksFilling;
-extern const base::Feature kEnableManualFallbacksFillingStandalone;
-extern const base::Feature kEnableManualFallbacksGeneration;
-extern const base::Feature kEnableManualPasswordGeneration;
+extern const base::Feature kPasswordGenerationRequirements;
+extern const base::Feature kPasswordGenerationRequirementsDomainOverrides;
extern const base::Feature kPasswordForceSaving;
-extern const base::Feature kEnableShowAllSavedPasswordsContextMenu;
+extern const base::Feature kShowAllSavedPasswordsContextMenu;
extern const base::Feature kFillOnAccountSelect;
extern const base::Feature kNewPasswordFormParsing;
extern const base::Feature kPasswordExport;
@@ -34,6 +33,18 @@ extern const base::Feature kPasswordsKeyboardAccessory;
extern const base::Feature kProtectSyncCredential;
extern const base::Feature kProtectSyncCredentialOnReauth;
+// Field trial and corresponding parameters.
+// To manually override this, start Chrome with the following parameters:
+// --enable-features=PasswordGenerationRequirements,\
+// PasswordGenerationRequirementsDomainOverrides
+// --force-fieldtrials=PasswordGenerationRequirements/Enabled
+// --force-fieldtrial-params=PasswordGenerationRequirements.Enabled:\
+// version/0/prefix_length/0/timeout/5000
+extern const char* kGenerationRequirementsFieldTrial;
+extern const char* kGenerationRequirementsVersion;
+extern const char* kGenerationRequirementsPrefixLength;
+extern const char* kGenerationRequirementsTimeout;
+
} // namespace features
} // namespace password_manager
diff --git a/chromium/components/password_manager/sync/browser/password_sync_util.cc b/chromium/components/password_manager/sync/browser/password_sync_util.cc
index 26cf165a3fa..b41a87f7311 100644
--- a/chromium/components/password_manager/sync/browser/password_sync_util.cc
+++ b/chromium/components/password_manager/sync/browser/password_sync_util.cc
@@ -47,12 +47,8 @@ std::string GetSyncUsernameIfSyncingPasswords(
bool IsSyncAccountCredential(const autofill::PasswordForm& form,
const syncer::SyncService* sync_service,
const SigninManagerBase* signin_manager) {
- const Origin gaia_origin =
- Origin::Create(GaiaUrls::GetInstance()->gaia_url().GetOrigin());
- if (!Origin::Create(GURL(form.signon_realm)).IsSameOriginWith(gaia_origin) &&
- form.signon_realm != kGoogleChangePasswordSignonRealm) {
+ if (!IsGaiaCredentialPage(form.signon_realm))
return false;
- }
// The empty username can mean that Chrome did not detect it correctly. For
// reasons described in http://crbug.com/636292#c1, the username is suspected
@@ -97,5 +93,38 @@ bool ShouldSavePasswordHash(const autofill::PasswordForm& form,
#endif // SYNC_PASSWORD_REUSE_DETECTION_ENABLED
}
+bool IsSyncAccountEmail(const std::string& username,
+ const SigninManagerBase* signin_manager) {
+ // |signin_manager| can be null if user is not signed in.
+ if (!signin_manager)
+ return false;
+
+ std::string sync_email = signin_manager->GetAuthenticatedAccountInfo().email;
+
+ if (sync_email.empty() || username.empty())
+ return false;
+
+ if (username.find('@') == std::string::npos)
+ return false;
+
+ return gaia::AreEmailsSame(username, sync_email);
+}
+
+bool IsGaiaCredentialPage(const std::string& signon_realm) {
+ return gaia::IsGaiaSignonRealm(GURL(signon_realm)) ||
+ signon_realm == kGoogleChangePasswordSignonRealm;
+}
+
+bool ShouldSaveEnterprisePasswordHash(const autofill::PasswordForm& form,
+ const PrefService& prefs) {
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+ return safe_browsing::MatchesPasswordProtectionLoginURL(form.origin, prefs) ||
+ safe_browsing::MatchesPasswordProtectionChangePasswordURL(form.origin,
+ prefs);
+#else
+ return false;
+#endif // SYNC_PASSWORD_REUSE_DETECTION_ENABLED
+}
+
} // namespace sync_util
} // namespace password_manager
diff --git a/chromium/components/password_manager/sync/browser/password_sync_util.h b/chromium/components/password_manager/sync/browser/password_sync_util.h
index 38f0c94cb11..c595c0933cf 100644
--- a/chromium/components/password_manager/sync/browser/password_sync_util.h
+++ b/chromium/components/password_manager/sync/browser/password_sync_util.h
@@ -32,12 +32,23 @@ bool IsSyncAccountCredential(const autofill::PasswordForm& form,
const SigninManagerBase* signin_manager);
// If |form| doesn't match GAIA sign-on realm or enterprise-specified password
-// protection URL, returns false. Otherwise, checks if the username
-// in |form| matches sign-in account (no syncing passwords are required).
+// protection URL, returns false. Otherwise, return true.
bool ShouldSavePasswordHash(const autofill::PasswordForm& form,
const SigninManagerBase* signin_manager,
PrefService* prefs);
+// If |username| matches sync account.
+bool IsSyncAccountEmail(const std::string& username,
+ const SigninManagerBase* signin_manager);
+
+// If |signon_realm| matches Gaia signon realm.
+bool IsGaiaCredentialPage(const std::string& signon_realm);
+
+// If |form|'s origin matches enterprise login URL or enterprise change password
+// URL.
+bool ShouldSaveEnterprisePasswordHash(const autofill::PasswordForm& form,
+ const PrefService& prefs);
+
} // namespace sync_util
} // namespace password_manager
diff --git a/chromium/components/password_manager/sync/browser/password_sync_util_unittest.cc b/chromium/components/password_manager/sync/browser/password_sync_util_unittest.cc
index c90545f8a3a..c65e51b8a3b 100644
--- a/chromium/components/password_manager/sync/browser/password_sync_util_unittest.cc
+++ b/chromium/components/password_manager/sync/browser/password_sync_util_unittest.cc
@@ -97,6 +97,34 @@ TEST_F(PasswordSyncUtilTest, IsSyncAccountCredential) {
}
}
+TEST_F(PasswordSyncUtilTest, IsSyncAccountEmail) {
+ const struct {
+ std::string fake_sync_email;
+ std::string input_username;
+ bool expected_result;
+ } kTestCases[] = {
+ {"", "", false},
+ {"", "user@example.org", false},
+ {"sync_user@example.org", "", false},
+ {"sync_user@example.org", "sync_user@example.org", true},
+ {"sync_user@example.org", "sync_user", false},
+ {"sync_user@example.org", "non_sync_user@example.org", false},
+ };
+
+ for (size_t i = 0; i < base::size(kTestCases); ++i) {
+ SCOPED_TRACE(testing::Message() << "i=" << i);
+ if (kTestCases[i].fake_sync_email.empty()) {
+ EXPECT_EQ(kTestCases[i].expected_result,
+ IsSyncAccountEmail(kTestCases[i].input_username, nullptr));
+ continue;
+ }
+ FakeSigninAs(kTestCases[i].fake_sync_email);
+ EXPECT_EQ(
+ kTestCases[i].expected_result,
+ IsSyncAccountEmail(kTestCases[i].input_username, signin_manager()));
+ }
+}
+
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
class PasswordSyncUtilEnterpriseTest : public SyncUsernameTestBase {
public:
diff --git a/chromium/components/password_manager/sync/browser/sync_credentials_filter.cc b/chromium/components/password_manager/sync/browser/sync_credentials_filter.cc
index 04eb1cd3acc..9238f543d3d 100644
--- a/chromium/components/password_manager/sync/browser/sync_credentials_filter.cc
+++ b/chromium/components/password_manager/sync/browser/sync_credentials_filter.cc
@@ -83,10 +83,25 @@ bool SyncCredentialsFilter::ShouldSave(
signin_manager_factory_function_.Run());
}
-bool SyncCredentialsFilter::ShouldSavePasswordHash(
+bool SyncCredentialsFilter::ShouldSaveGaiaPasswordHash(
const autofill::PasswordForm& form) const {
- return sync_util::ShouldSavePasswordHash(
- form, signin_manager_factory_function_.Run(), client_->GetPrefs());
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+ return sync_util::IsGaiaCredentialPage(form.signon_realm);
+#else
+ return false;
+#endif // SYNC_PASSWORD_REUSE_DETECTION_ENABLED
+}
+
+bool SyncCredentialsFilter::ShouldSaveEnterprisePasswordHash(
+ const autofill::PasswordForm& form) const {
+ return sync_util::ShouldSaveEnterprisePasswordHash(form,
+ *client_->GetPrefs());
+}
+
+bool SyncCredentialsFilter::IsSyncAccountEmail(
+ const std::string& username) const {
+ return sync_util::IsSyncAccountEmail(username,
+ signin_manager_factory_function_.Run());
}
void SyncCredentialsFilter::ReportFormLoginSuccess(
diff --git a/chromium/components/password_manager/sync/browser/sync_credentials_filter.h b/chromium/components/password_manager/sync/browser/sync_credentials_filter.h
index 2c0571f848a..d2543072232 100644
--- a/chromium/components/password_manager/sync/browser/sync_credentials_filter.h
+++ b/chromium/components/password_manager/sync/browser/sync_credentials_filter.h
@@ -44,10 +44,13 @@ class SyncCredentialsFilter : public CredentialsFilter {
std::vector<std::unique_ptr<autofill::PasswordForm>> results)
const override;
bool ShouldSave(const autofill::PasswordForm& form) const override;
- bool ShouldSavePasswordHash(
+ bool ShouldSaveGaiaPasswordHash(
+ const autofill::PasswordForm& form) const override;
+ bool ShouldSaveEnterprisePasswordHash(
const autofill::PasswordForm& form) const override;
void ReportFormLoginSuccess(
const PasswordFormManager& form_manager) const override;
+ bool IsSyncAccountEmail(const std::string& username) const override;
private:
enum AutofillForSyncCredentialsState {
diff --git a/chromium/components/password_manager/sync/browser/sync_credentials_filter_unittest.cc b/chromium/components/password_manager/sync/browser/sync_credentials_filter_unittest.cc
index cfbc0b9a337..b28a3fd6841 100644
--- a/chromium/components/password_manager/sync/browser/sync_credentials_filter_unittest.cc
+++ b/chromium/components/password_manager/sync/browser/sync_credentials_filter_unittest.cc
@@ -15,9 +15,9 @@
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
-#include "base/test/user_action_tester.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/fake_form_fetcher.h"
#include "components/password_manager/core/browser/mock_password_store.h"
@@ -28,6 +28,10 @@
#include "components/password_manager/core/browser/stub_password_manager_driver.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/sync/browser/sync_username_test_base.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -40,10 +44,24 @@ namespace {
const char kFilledAndLoginActionName[] =
"PasswordManager_SyncCredentialFilledAndLoginSuccessfull";
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+const char kEnterpriseURL[] = "https://enterprise.test/";
+#endif // SYNC_PASSWORD_REUSE_DETECTION_ENABLED
+
class FakePasswordManagerClient : public StubPasswordManagerClient {
public:
FakePasswordManagerClient()
- : password_store_(new testing::NiceMock<MockPasswordStore>) {}
+ : password_store_(new testing::NiceMock<MockPasswordStore>) {
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+ // Initializes and configures prefs.
+ prefs_ = std::make_unique<TestingPrefServiceSimple>();
+ prefs_->registry()->RegisterStringPref(
+ prefs::kPasswordProtectionChangePasswordURL, "");
+ prefs_->registry()->RegisterListPref(prefs::kPasswordProtectionLoginURLs);
+ prefs_->SetString(prefs::kPasswordProtectionChangePasswordURL,
+ kEnterpriseURL);
+#endif // SYNC_PASSWORD_REUSE_DETECTION_ENABLED
+ }
~FakePasswordManagerClient() override {
password_store_->ShutdownOnUIThread();
@@ -61,10 +79,17 @@ class FakePasswordManagerClient : public StubPasswordManagerClient {
last_committed_entry_url_ = GURL(url_spec);
}
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+ PrefService* GetPrefs() const override { return prefs_.get(); }
+#endif
+
private:
base::MessageLoop message_loop_; // For |password_store_|.
GURL last_committed_entry_url_;
scoped_refptr<testing::NiceMock<MockPasswordStore>> password_store_;
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+ std::unique_ptr<TestingPrefServiceSimple> prefs_;
+#endif // SYNC_PASSWORD_REUSE_DETECTION_ENABLED
DISALLOW_COPY_AND_ASSIGN(FakePasswordManagerClient);
};
@@ -388,4 +413,39 @@ TEST_F(CredentialsFilterTest, ShouldFilterOneForm) {
EXPECT_EQ(SimpleGaiaForm("test2@gmail.com"), *results[0]);
}
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+TEST_F(CredentialsFilterTest, ShouldSaveGaiaPasswordHash) {
+ PasswordForm gaia_form = SimpleGaiaForm("user@gmail.org");
+ EXPECT_TRUE(filter_.ShouldSaveGaiaPasswordHash(gaia_form));
+
+ PasswordForm other_form = SimpleNonGaiaForm("user@example.org");
+ EXPECT_FALSE(filter_.ShouldSaveGaiaPasswordHash(other_form));
+}
+
+TEST_F(CredentialsFilterTest, ShouldSaveEnterprisePasswordHash) {
+ PasswordForm gaia_form = SimpleGaiaForm("user@gmail.org");
+ EXPECT_FALSE(filter_.ShouldSaveEnterprisePasswordHash(gaia_form));
+
+ PasswordForm other_form = SimpleNonGaiaForm("user@example.org");
+ EXPECT_FALSE(filter_.ShouldSaveEnterprisePasswordHash(other_form));
+
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ safe_browsing::kEnterprisePasswordProtectionV1);
+ PasswordForm enterprise_form =
+ SimpleNonGaiaForm("user@enterprise.test", kEnterpriseURL);
+ EXPECT_TRUE(filter_.ShouldSaveEnterprisePasswordHash(enterprise_form));
+}
+
+TEST_F(CredentialsFilterTest, IsSyncAccountEmail) {
+ FakeSigninAs("user@gmail.com");
+ EXPECT_FALSE(filter_.IsSyncAccountEmail("user"));
+ EXPECT_FALSE(filter_.IsSyncAccountEmail("user2@gmail.com"));
+ EXPECT_FALSE(filter_.IsSyncAccountEmail("user2@example.com"));
+ EXPECT_TRUE(filter_.IsSyncAccountEmail("user@gmail.com"));
+ EXPECT_TRUE(filter_.IsSyncAccountEmail("us.er@gmail.com"));
+ EXPECT_TRUE(filter_.IsSyncAccountEmail("user@googlemail.com"));
+}
+#endif // SYNC_PASSWORD_REUSE_DETECTION_ENABLED
+
} // namespace password_manager
diff --git a/chromium/components/password_manager_strings.grdp b/chromium/components/password_manager_strings.grdp
index cfc7a008091..c7985c14ce1 100644
--- a/chromium/components/password_manager_strings.grdp
+++ b/chromium/components/password_manager_strings.grdp
@@ -5,8 +5,24 @@
Signing in as <ph name="username">$1<ex>chef@google.com</ex></ph>
</message>
<message name="IDS_PASSWORD_MANAGER_EMPTY_LOGIN" desc="A placeholder for the 'Manage Passwords' bubble's empty username label">
- (No username)
+ No username
</message>
+ <if expr="not use_titlecase">
+ <message name="IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS" desc="The menu item in the password field drop down that opens the list of saved passwords.">
+ Manage passwords…
+ </message>
+ <message name="IDS_PASSWORD_MANAGER_GENERATE_PASSWORD" desc="The menu item in the password field drop down that starts the password generation flow.">
+ Suggest strong password…
+ </message>
+ </if>
+ <if expr="use_titlecase">
+ <message name="IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS" desc="The menu item in the password field drop down that opens the list of saved passwords.">
+ Manage Passwords…
+ </message>
+ <message name="IDS_PASSWORD_MANAGER_GENERATE_PASSWORD" desc="The menu item in the password field drop down that starts the password generation flow.">
+ Suggest Strong Password…
+ </message>
+ </if>
<message name="IDS_PASSWORD_MANAGER_EXCEPTIONS_TAB_TITLE" desc="Title for 'Never saved' tab">
Never saved
</message>
diff --git a/chromium/components/payments/content/BUILD.gn b/chromium/components/payments/content/BUILD.gn
index fa7441914c7..74faebc571a 100644
--- a/chromium/components/payments/content/BUILD.gn
+++ b/chromium/components/payments/content/BUILD.gn
@@ -36,6 +36,7 @@ static_library("content") {
"//components/payments/mojom",
"//components/prefs",
"//components/strings:components_strings_grit",
+ "//components/ukm/content",
"//components/url_formatter",
"//content/public/browser",
"//third_party/blink/public:blink_headers",
@@ -119,6 +120,7 @@ source_set("unit_tests") {
"//components/strings:components_strings_grit",
"//components/webdata/common",
"//content/test:test_support",
+ "//services/metrics/public/cpp:metrics_cpp",
"//sql",
"//testing/gtest",
"//third_party/blink/public:blink_headers",
diff --git a/chromium/components/payments/content/DEPS b/chromium/components/payments/content/DEPS
index a1853f1bc10..b57f1c08bcb 100644
--- a/chromium/components/payments/content/DEPS
+++ b/chromium/components/payments/content/DEPS
@@ -5,12 +5,14 @@ include_rules = [
"+components/keyed_service/core",
"+components/prefs",
"+components/strings",
+ "+components/ukm/content",
"+components/url_formatter",
"+components/webdata/common",
"+content/public",
"+mojo/public/cpp",
"+net",
"+services/data_decoder/public/cpp",
+ "+services/metrics/public/cpp",
"+sql",
"+third_party/blink/public/common",
"+third_party/blink/public/platform/modules/payments",
diff --git a/chromium/components/payments/content/android/DEPS b/chromium/components/payments/content/android/DEPS
index c80012b5621..64dd245b35d 100644
--- a/chromium/components/payments/content/android/DEPS
+++ b/chromium/components/payments/content/android/DEPS
@@ -1,3 +1,4 @@
include_rules = [
"+jni",
+ "+services/network/public/cpp",
]
diff --git a/chromium/components/payments/content/android/payment_manifest_downloader_android.cc b/chromium/components/payments/content/android/payment_manifest_downloader_android.cc
index cf27adb2383..ac19e0e211f 100644
--- a/chromium/components/payments/content/android/payment_manifest_downloader_android.cc
+++ b/chromium/components/payments/content/android/payment_manifest_downloader_android.cc
@@ -12,7 +12,7 @@
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "jni/PaymentManifestDownloader_jni.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "url/gurl.h"
namespace payments {
@@ -59,8 +59,8 @@ class DownloadCallback {
} // namespace
PaymentManifestDownloaderAndroid::PaymentManifestDownloaderAndroid(
- const scoped_refptr<net::URLRequestContextGetter>& context)
- : downloader_(context) {}
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+ : downloader_(std::move(url_loader_factory)) {}
PaymentManifestDownloaderAndroid::~PaymentManifestDownloaderAndroid() {}
@@ -108,7 +108,7 @@ static jlong JNI_PaymentManifestDownloader_Init(
return reinterpret_cast<jlong>(new PaymentManifestDownloaderAndroid(
content::BrowserContext::GetDefaultStoragePartition(
web_contents->GetBrowserContext())
- ->GetURLRequestContext()));
+ ->GetURLLoaderFactoryForBrowserProcess()));
}
} // namespace payments
diff --git a/chromium/components/payments/content/android/payment_manifest_downloader_android.h b/chromium/components/payments/content/android/payment_manifest_downloader_android.h
index 78422863f5b..b57222fd23b 100644
--- a/chromium/components/payments/content/android/payment_manifest_downloader_android.h
+++ b/chromium/components/payments/content/android/payment_manifest_downloader_android.h
@@ -12,8 +12,8 @@
#include "base/memory/ref_counted.h"
#include "components/payments/core/payment_manifest_downloader.h"
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
}
namespace payments {
@@ -22,7 +22,7 @@ namespace payments {
class PaymentManifestDownloaderAndroid {
public:
explicit PaymentManifestDownloaderAndroid(
- const scoped_refptr<net::URLRequestContextGetter>& context);
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
~PaymentManifestDownloaderAndroid();
void DownloadPaymentMethodManifest(
diff --git a/chromium/components/payments/content/installable_payment_app_crawler.cc b/chromium/components/payments/content/installable_payment_app_crawler.cc
index 324fb68bc0c..c03c7a7dee1 100644
--- a/chromium/components/payments/content/installable_payment_app_crawler.cc
+++ b/chromium/components/payments/content/installable_payment_app_crawler.cc
@@ -10,13 +10,14 @@
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/manifest_icon_downloader.h"
-#include "content/public/browser/manifest_icon_selector.h"
#include "content/public/browser/payment_app_provider.h"
-#include "content/public/browser/permission_manager.h"
+#include "content/public/browser/permission_controller.h"
#include "content/public/browser/permission_type.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/console_message_level.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "third_party/blink/public/common/manifest/manifest.h"
+#include "third_party/blink/public/common/manifest/manifest_icon_selector.h"
#include "ui/gfx/geometry/size.h"
#include "url/gurl.h"
#include "url/origin.h"
@@ -51,13 +52,11 @@ void InstallablePaymentAppCrawler::Start(
std::set<GURL> manifests_to_download;
for (const auto& method_data : requested_method_data) {
- for (const auto& method_name : method_data->supported_methods) {
- if (!base::IsStringUTF8(method_name))
- continue;
- GURL url = GURL(method_name);
- if (url.is_valid()) {
- manifests_to_download.insert(url);
- }
+ if (!base::IsStringUTF8(method_data->supported_method))
+ continue;
+ GURL url = GURL(method_data->supported_method);
+ if (url.is_valid()) {
+ manifests_to_download.insert(url);
}
}
@@ -106,10 +105,10 @@ void InstallablePaymentAppCrawler::OnPaymentMethodManifestParsed(
if (web_contents() == nullptr)
return;
- content::PermissionManager* permission_manager =
- web_contents()->GetBrowserContext()->GetPermissionManager();
- if (permission_manager == nullptr)
- return;
+ content::PermissionController* permission_controller =
+ content::BrowserContext::GetPermissionController(
+ web_contents()->GetBrowserContext());
+ DCHECK(permission_controller);
for (const auto& url : default_applications) {
if (downloaded_web_app_manifests_.find(url) !=
@@ -119,7 +118,16 @@ void InstallablePaymentAppCrawler::OnPaymentMethodManifestParsed(
continue;
}
- if (permission_manager->GetPermissionStatus(
+ if (!net::registry_controlled_domains::SameDomainOrHost(
+ method_manifest_url, url,
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
+ WarnIfPossible("Installable payment app from " + url.spec() +
+ " is not allowed for the method " +
+ method_manifest_url.spec());
+ continue;
+ }
+
+ if (permission_controller->GetPermissionStatus(
content::PermissionType::PAYMENT_HANDLER, url.GetOrigin(),
url.GetOrigin()) != blink::mojom::PermissionStatus::GRANTED) {
// Do not download the web app manifest if it is blocked.
@@ -196,6 +204,14 @@ bool InstallablePaymentAppCrawler::CompleteAndStorePaymentWebAppInfoIfValid(
app_info->sw_js_url + ").");
return false;
}
+ if (!net::registry_controlled_domains::SameDomainOrHost(
+ method_manifest_url, absolute_url,
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
+ WarnIfPossible("Installable payment app's js url " + absolute_url.spec() +
+ " is not allowed for the method " +
+ method_manifest_url.spec());
+ return false;
+ }
app_info->sw_js_url = absolute_url.spec();
}
@@ -209,6 +225,14 @@ bool InstallablePaymentAppCrawler::CompleteAndStorePaymentWebAppInfoIfValid(
app_info->sw_scope + ").");
return false;
}
+ if (!net::registry_controlled_domains::SameDomainOrHost(
+ method_manifest_url, absolute_scope,
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
+ WarnIfPossible("Installable payment app's registration scope " +
+ absolute_scope.spec() + " is not allowed for the method " +
+ method_manifest_url.spec());
+ return false;
+ }
app_info->sw_scope = absolute_scope.spec();
}
@@ -237,7 +261,7 @@ void InstallablePaymentAppCrawler::DownloadAndDecodeWebAppIcon(
if (icons == nullptr || icons->empty())
return;
- std::vector<blink::Manifest::Icon> manifest_icons;
+ std::vector<blink::Manifest::ImageResource> manifest_icons;
for (const auto& icon : *icons) {
if (icon.src.empty() || !base::IsStringUTF8(icon.src)) {
WarnIfPossible(
@@ -257,10 +281,11 @@ void InstallablePaymentAppCrawler::DownloadAndDecodeWebAppIcon(
}
}
- blink::Manifest::Icon manifest_icon;
+ blink::Manifest::ImageResource manifest_icon;
manifest_icon.src = icon_src;
manifest_icon.type = base::UTF8ToUTF16(icon.type);
- manifest_icon.purpose.emplace_back(blink::Manifest::Icon::ANY);
+ manifest_icon.purpose.emplace_back(
+ blink::Manifest::ImageResource::Purpose::ANY);
// TODO(crbug.com/782270): Parse icon sizes.
manifest_icon.sizes.emplace_back(gfx::Size());
manifest_icons.emplace_back(manifest_icon);
@@ -275,9 +300,9 @@ void InstallablePaymentAppCrawler::DownloadAndDecodeWebAppIcon(
// but not scale up.
const int kPaymentAppIdealIconSize = 0xFFFF;
const int kPaymentAppMinimumIconSize = 0;
- GURL best_icon_url = content::ManifestIconSelector::FindBestMatchingIcon(
+ GURL best_icon_url = blink::ManifestIconSelector::FindBestMatchingIcon(
manifest_icons, kPaymentAppIdealIconSize, kPaymentAppMinimumIconSize,
- blink::Manifest::Icon::ANY);
+ blink::Manifest::ImageResource::Purpose::ANY);
if (!best_icon_url.is_valid()) {
WarnIfPossible(
"No suitable icon found in the installabble payment app's manifest (" +
diff --git a/chromium/components/payments/content/manifest_verifier.cc b/chromium/components/payments/content/manifest_verifier.cc
index c4025b7813f..26047dd03fb 100644
--- a/chromium/components/payments/content/manifest_verifier.cc
+++ b/chromium/components/payments/content/manifest_verifier.cc
@@ -10,6 +10,7 @@
#include "base/bind.h"
#include "base/logging.h"
+#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "components/payments/content/payment_manifest_web_data_service.h"
#include "components/payments/content/utility/payment_manifest_parser.h"
@@ -40,10 +41,8 @@ void EnableMethodManifestUrlForSupportedApps(
for (auto& app : *apps) {
if (app_origin.IsSameOriginWith(
url::Origin::Create(app.second->scope.GetOrigin()))) {
- app.second->has_explicitly_verified_methods =
- std::find(supported_origin_strings.begin(),
- supported_origin_strings.end(),
- app_origin.Serialize()) != supported_origin_strings.end();
+ app.second->has_explicitly_verified_methods = base::ContainsValue(
+ supported_origin_strings, app_origin.Serialize());
if (all_origins_supported ||
app.second->has_explicitly_verified_methods) {
app.second->enabled_methods.emplace_back(method_manifest_url.spec());
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 8935e6bcc84..3e3bcd5c3cd 100644
--- a/chromium/components/payments/content/payment_method_manifest_table_unittest.cc
+++ b/chromium/components/payments/content/payment_method_manifest_table_unittest.cc
@@ -8,6 +8,7 @@
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
+#include "base/stl_util.h"
#include "components/webdata/common/web_database.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -70,12 +71,8 @@ TEST_F(PaymentMethodManifestTableTest, AddAndGetSingleManifest) {
retrieved_web_app_ids =
payment_method_manifest_table->GetManifest("https://bobpay.com");
ASSERT_EQ(web_app_ids.size(), retrieved_web_app_ids.size());
- ASSERT_TRUE(std::find(retrieved_web_app_ids.begin(),
- retrieved_web_app_ids.end(),
- web_app_ids[0]) != retrieved_web_app_ids.end());
- ASSERT_TRUE(std::find(retrieved_web_app_ids.begin(),
- retrieved_web_app_ids.end(),
- web_app_ids[1]) != retrieved_web_app_ids.end());
+ ASSERT_TRUE(base::ContainsValue(retrieved_web_app_ids, web_app_ids[0]));
+ ASSERT_TRUE(base::ContainsValue(retrieved_web_app_ids, web_app_ids[1]));
}
TEST_F(PaymentMethodManifestTableTest, AddAndGetMultipleManifest) {
@@ -109,20 +106,14 @@ TEST_F(PaymentMethodManifestTableTest, AddAndGetMultipleManifest) {
bobpay_web_app_ids =
payment_method_manifest_table->GetManifest(method_name_1);
ASSERT_EQ(web_app_ids.size(), bobpay_web_app_ids.size());
- ASSERT_TRUE(std::find(bobpay_web_app_ids.begin(), bobpay_web_app_ids.end(),
- web_app_ids[0]) != bobpay_web_app_ids.end());
- ASSERT_TRUE(std::find(bobpay_web_app_ids.begin(), bobpay_web_app_ids.end(),
- web_app_ids[1]) != bobpay_web_app_ids.end());
+ ASSERT_TRUE(base::ContainsValue(bobpay_web_app_ids, web_app_ids[0]));
+ ASSERT_TRUE(base::ContainsValue(bobpay_web_app_ids, web_app_ids[1]));
alicepay_web_app_ids =
payment_method_manifest_table->GetManifest(method_name_1);
ASSERT_EQ(web_app_ids.size(), alicepay_web_app_ids.size());
- ASSERT_TRUE(std::find(alicepay_web_app_ids.begin(),
- alicepay_web_app_ids.end(),
- web_app_ids[0]) != alicepay_web_app_ids.end());
- ASSERT_TRUE(std::find(alicepay_web_app_ids.begin(),
- alicepay_web_app_ids.end(),
- web_app_ids[1]) != alicepay_web_app_ids.end());
+ ASSERT_TRUE(base::ContainsValue(alicepay_web_app_ids, web_app_ids[0]));
+ ASSERT_TRUE(base::ContainsValue(alicepay_web_app_ids, web_app_ids[1]));
}
} // namespace
diff --git a/chromium/components/payments/content/payment_request.cc b/chromium/components/payments/content/payment_request.cc
index a791237f311..32464225223 100644
--- a/chromium/components/payments/content/payment_request.cc
+++ b/chromium/components/payments/content/payment_request.cc
@@ -21,6 +21,7 @@
#include "components/payments/core/payment_instrument.h"
#include "components/payments/core/payment_prefs.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/render_frame_host.h"
@@ -49,8 +50,7 @@ PaymentRequest::PaymentRequest(
render_frame_host->GetLastCommittedURL())),
observer_for_testing_(observer_for_testing),
journey_logger_(delegate_->IsIncognito(),
- web_contents_->GetLastCommittedURL(),
- delegate_->GetUkmRecorder()),
+ ukm::GetSourceIdForWebContentsDocument(web_contents)),
weak_ptr_factory_(this) {
// OnConnectionTerminated will be called when the Mojo pipe is closed. This
// will happen as a result of many renderer-side events (both successful and
diff --git a/chromium/components/payments/content/payment_request_converter.cc b/chromium/components/payments/content/payment_request_converter.cc
index b815ca50a80..a577c440640 100644
--- a/chromium/components/payments/content/payment_request_converter.cc
+++ b/chromium/components/payments/content/payment_request_converter.cc
@@ -97,7 +97,7 @@ std::string GetBasicCardNetworkName(const mojom::BasicCardNetwork& network) {
PaymentMethodData ConvertPaymentMethodData(
const mojom::PaymentMethodDataPtr& method_data_entry) {
PaymentMethodData method_data;
- method_data.supported_methods = method_data_entry->supported_methods;
+ method_data.supported_method = method_data_entry->supported_method;
// Transfer the supported basic card networks (visa, amex) and types
// (credit, debit).
diff --git a/chromium/components/payments/content/payment_request_spec.cc b/chromium/components/payments/content/payment_request_spec.cc
index ef18c44e0a0..0319e3f71c1 100644
--- a/chromium/components/payments/content/payment_request_spec.cc
+++ b/chromium/components/payments/content/payment_request_spec.cc
@@ -55,10 +55,8 @@ void PopulateValidatedMethodData(
method_data_vector.reserve(method_data_mojom.size());
for (const mojom::PaymentMethodDataPtr& method_data_entry :
method_data_mojom) {
- for (const std::string& method : method_data_entry->supported_methods) {
- (*stringified_method_data)[method].insert(
- method_data_entry->stringified_data);
- }
+ (*stringified_method_data)[method_data_entry->supported_method].insert(
+ method_data_entry->stringified_data);
method_data_vector.push_back(ConvertPaymentMethodData(method_data_entry));
}
@@ -237,7 +235,7 @@ PaymentRequestSpec::GetApplicableModifier(
&payment_method_identifiers_set, &stringified_method_data);
if (selected_instrument->IsValidForModifier(
- modifier->method_data->supported_methods,
+ modifier->method_data->supported_method,
!modifier->method_data->supported_networks.empty(),
supported_card_networks_set,
!modifier->method_data->supported_types.empty(), supported_types)) {
diff --git a/chromium/components/payments/content/payment_request_spec_unittest.cc b/chromium/components/payments/content/payment_request_spec_unittest.cc
index b613d302997..2ea47d4ad01 100644
--- a/chromium/components/payments/content/payment_request_spec_unittest.cc
+++ b/chromium/components/payments/content/payment_request_spec_unittest.cc
@@ -54,21 +54,26 @@ TEST_F(PaymentRequestSpecTest, EmptyMethodData) {
}
TEST_F(PaymentRequestSpecTest, IsMethodSupportedThroughBasicCard) {
- mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("visa");
- entry->supported_methods.push_back("mastercard");
- entry->supported_methods.push_back("invalid");
- entry->supported_methods.push_back("");
- entry->supported_methods.push_back("visa");
+ mojom::PaymentMethodDataPtr entry1 = mojom::PaymentMethodData::New();
+ entry1->supported_method = "visa";
mojom::PaymentMethodDataPtr entry2 = mojom::PaymentMethodData::New();
- entry2->supported_methods.push_back("basic-card");
- entry2->supported_networks.push_back(mojom::BasicCardNetwork::UNIONPAY);
- entry2->supported_networks.push_back(mojom::BasicCardNetwork::JCB);
- entry2->supported_networks.push_back(mojom::BasicCardNetwork::VISA);
+ entry2->supported_method = "mastercard";
+ mojom::PaymentMethodDataPtr entry3 = mojom::PaymentMethodData::New();
+ entry3->supported_method = "invalid";
+ mojom::PaymentMethodDataPtr entry4 = mojom::PaymentMethodData::New();
+ entry4->supported_method = "visa";
+ mojom::PaymentMethodDataPtr entry5 = mojom::PaymentMethodData::New();
+ entry5->supported_method = "basic-card";
+ entry5->supported_networks.push_back(mojom::BasicCardNetwork::UNIONPAY);
+ entry5->supported_networks.push_back(mojom::BasicCardNetwork::JCB);
+ entry5->supported_networks.push_back(mojom::BasicCardNetwork::VISA);
std::vector<mojom::PaymentMethodDataPtr> method_data;
- method_data.push_back(std::move(entry));
+ method_data.push_back(std::move(entry1));
method_data.push_back(std::move(entry2));
+ method_data.push_back(std::move(entry3));
+ method_data.push_back(std::move(entry4));
+ method_data.push_back(std::move(entry5));
RecreateSpecWithMethodData(std::move(method_data));
@@ -86,18 +91,22 @@ TEST_F(PaymentRequestSpecTest, IsMethodSupportedThroughBasicCard) {
// Order matters when parsing the supportedMethods and basic card networks.
TEST_F(PaymentRequestSpecTest,
IsMethodSupportedThroughBasicCard_DifferentOrder) {
- mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("basic-card");
- entry->supported_networks.push_back(mojom::BasicCardNetwork::UNIONPAY);
- entry->supported_networks.push_back(mojom::BasicCardNetwork::VISA);
+ mojom::PaymentMethodDataPtr entry1 = mojom::PaymentMethodData::New();
+ entry1->supported_method = "basic-card";
+ entry1->supported_networks.push_back(mojom::BasicCardNetwork::UNIONPAY);
+ entry1->supported_networks.push_back(mojom::BasicCardNetwork::VISA);
mojom::PaymentMethodDataPtr entry2 = mojom::PaymentMethodData::New();
- entry2->supported_methods.push_back("visa");
- entry2->supported_methods.push_back("unionpay");
- entry2->supported_methods.push_back("jcb");
+ entry2->supported_method = "visa";
+ mojom::PaymentMethodDataPtr entry3 = mojom::PaymentMethodData::New();
+ entry3->supported_method = "unionpay";
+ mojom::PaymentMethodDataPtr entry4 = mojom::PaymentMethodData::New();
+ entry4->supported_method = "jcb";
std::vector<mojom::PaymentMethodDataPtr> method_data;
- method_data.push_back(std::move(entry));
+ method_data.push_back(std::move(entry1));
method_data.push_back(std::move(entry2));
+ method_data.push_back(std::move(entry3));
+ method_data.push_back(std::move(entry4));
RecreateSpecWithMethodData(std::move(method_data));
@@ -113,14 +122,22 @@ TEST_F(PaymentRequestSpecTest,
// Test that parsing supported methods (with invalid values and duplicates)
// works as expected.
TEST_F(PaymentRequestSpecTest, SupportedMethods) {
- mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("visa");
- entry->supported_methods.push_back("mastercard");
- entry->supported_methods.push_back("invalid");
- entry->supported_methods.push_back("");
- entry->supported_methods.push_back("visa");
+ mojom::PaymentMethodDataPtr entry1 = mojom::PaymentMethodData::New();
+ entry1->supported_method = "visa";
+ mojom::PaymentMethodDataPtr entry2 = mojom::PaymentMethodData::New();
+ entry2->supported_method = "mastercard";
+ mojom::PaymentMethodDataPtr entry3 = mojom::PaymentMethodData::New();
+ entry3->supported_method = "invalid";
+ mojom::PaymentMethodDataPtr entry4 = mojom::PaymentMethodData::New();
+ entry4->supported_method = "";
+ mojom::PaymentMethodDataPtr entry5 = mojom::PaymentMethodData::New();
+ entry5->supported_method = "visa";
std::vector<mojom::PaymentMethodDataPtr> method_data;
- method_data.push_back(std::move(entry));
+ method_data.push_back(std::move(entry1));
+ method_data.push_back(std::move(entry2));
+ method_data.push_back(std::move(entry3));
+ method_data.push_back(std::move(entry4));
+ method_data.push_back(std::move(entry5));
RecreateSpecWithMethodData(std::move(method_data));
@@ -134,11 +151,11 @@ TEST_F(PaymentRequestSpecTest, SupportedMethods) {
// invalid values and duplicates) works as expected.
TEST_F(PaymentRequestSpecTest, SupportedMethods_MultipleEntries) {
mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("visa");
+ entry->supported_method = "visa";
mojom::PaymentMethodDataPtr entry2 = mojom::PaymentMethodData::New();
- entry2->supported_methods.push_back("mastercard");
+ entry2->supported_method = "mastercard";
mojom::PaymentMethodDataPtr entry3 = mojom::PaymentMethodData::New();
- entry3->supported_methods.push_back("invalid");
+ entry3->supported_method = "invalid";
std::vector<mojom::PaymentMethodDataPtr> method_data;
method_data.push_back(std::move(entry));
@@ -158,12 +175,12 @@ TEST_F(PaymentRequestSpecTest, SupportedMethods_MultipleEntries) {
TEST_F(PaymentRequestSpecTest, SupportedMethods_MultipleEntries_OneEmpty) {
// First entry is valid.
mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("visa");
+ entry->supported_method = "visa";
// Empty method data entry.
mojom::PaymentMethodDataPtr entry2 = mojom::PaymentMethodData::New();
// Valid one follows the empty.
mojom::PaymentMethodDataPtr entry3 = mojom::PaymentMethodData::New();
- entry3->supported_methods.push_back("mastercard");
+ entry3->supported_method = "mastercard";
std::vector<mojom::PaymentMethodDataPtr> method_data;
method_data.push_back(std::move(entry));
@@ -180,7 +197,7 @@ TEST_F(PaymentRequestSpecTest, SupportedMethods_MultipleEntries_OneEmpty) {
// Test that only specifying basic-card means that all are supported.
TEST_F(PaymentRequestSpecTest, SupportedMethods_OnlyBasicCard) {
mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("basic-card");
+ entry->supported_method = "basic-card";
std::vector<mojom::PaymentMethodDataPtr> method_data;
method_data.push_back(std::move(entry));
@@ -201,11 +218,13 @@ TEST_F(PaymentRequestSpecTest, SupportedMethods_OnlyBasicCard) {
// Test that specifying a method AND basic-card means that all are supported,
// but with the method as first.
TEST_F(PaymentRequestSpecTest, SupportedMethods_BasicCard_WithSpecificMethod) {
- mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("jcb");
- entry->supported_methods.push_back("basic-card");
+ mojom::PaymentMethodDataPtr entry1 = mojom::PaymentMethodData::New();
+ entry1->supported_method = "jcb";
+ mojom::PaymentMethodDataPtr entry2 = mojom::PaymentMethodData::New();
+ entry2->supported_method = "basic-card";
std::vector<mojom::PaymentMethodDataPtr> method_data;
- method_data.push_back(std::move(entry));
+ method_data.push_back(std::move(entry1));
+ method_data.push_back(std::move(entry2));
RecreateSpecWithMethodData(std::move(method_data));
@@ -225,18 +244,20 @@ TEST_F(PaymentRequestSpecTest, SupportedMethods_BasicCard_WithSpecificMethod) {
// Test that specifying basic-card with a supported network (with previous
// supported methods) will work as expected
TEST_F(PaymentRequestSpecTest, SupportedMethods_BasicCard_Overlap) {
- mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("mastercard");
- entry->supported_methods.push_back("visa");
- // Visa and mastercard are repeated, but in reverse order.
+ mojom::PaymentMethodDataPtr entry1 = mojom::PaymentMethodData::New();
+ entry1->supported_method = "mastercard";
mojom::PaymentMethodDataPtr entry2 = mojom::PaymentMethodData::New();
- entry2->supported_methods.push_back("basic-card");
- entry2->supported_networks.push_back(mojom::BasicCardNetwork::VISA);
- entry2->supported_networks.push_back(mojom::BasicCardNetwork::MASTERCARD);
- entry2->supported_networks.push_back(mojom::BasicCardNetwork::UNIONPAY);
+ entry2->supported_method = "visa";
+ // Visa and mastercard are repeated, but in reverse order.
+ mojom::PaymentMethodDataPtr entry3 = mojom::PaymentMethodData::New();
+ entry3->supported_method = "basic-card";
+ entry3->supported_networks.push_back(mojom::BasicCardNetwork::VISA);
+ entry3->supported_networks.push_back(mojom::BasicCardNetwork::MASTERCARD);
+ entry3->supported_networks.push_back(mojom::BasicCardNetwork::UNIONPAY);
std::vector<mojom::PaymentMethodDataPtr> method_data;
- method_data.push_back(std::move(entry));
+ method_data.push_back(std::move(entry1));
method_data.push_back(std::move(entry2));
+ method_data.push_back(std::move(entry3));
RecreateSpecWithMethodData(std::move(method_data));
@@ -251,7 +272,7 @@ TEST_F(PaymentRequestSpecTest, SupportedMethods_BasicCard_Overlap) {
TEST_F(PaymentRequestSpecTest,
SupportedMethods_BasicCard_WithSupportedNetworks) {
mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("basic-card");
+ entry->supported_method = "basic-card";
entry->supported_networks.push_back(mojom::BasicCardNetwork::VISA);
entry->supported_networks.push_back(mojom::BasicCardNetwork::UNIONPAY);
std::vector<mojom::PaymentMethodDataPtr> method_data;
diff --git a/chromium/components/payments/content/payment_request_state.cc b/chromium/components/payments/content/payment_request_state.cc
index 6377d67b769..cb0941cf942 100644
--- a/chromium/components/payments/content/payment_request_state.cc
+++ b/chromium/components/payments/content/payment_request_state.cc
@@ -20,6 +20,7 @@
#include "components/payments/content/payment_response_helper.h"
#include "components/payments/content/service_worker_payment_instrument.h"
#include "components/payments/core/autofill_payment_instrument.h"
+#include "components/payments/core/features.h"
#include "components/payments/core/journey_logger.h"
#include "components/payments/core/payment_instrument.h"
#include "components/payments/core/payment_request_data_util.h"
@@ -53,7 +54,7 @@ PaymentRequestState::PaymentRequestState(
payment_request_delegate_(payment_request_delegate),
profile_comparator_(app_locale, *spec),
weak_ptr_factory_(this) {
- if (base::FeatureList::IsEnabled(features::kServiceWorkerPaymentApps)) {
+ if (base::FeatureList::IsEnabled(::features::kServiceWorkerPaymentApps)) {
get_all_instruments_finished_ = false;
ServiceWorkerPaymentAppFactory::GetInstance()->GetAllPaymentApps(
web_contents,
@@ -415,7 +416,8 @@ void PaymentRequestState::PopulateProfileCache() {
// by their respective AutofillPaymentInstrument.
const std::vector<autofill::CreditCard*>& cards =
personal_data_manager_->GetCreditCardsToSuggest(
- /*include_server_cards=*/true);
+ /*include_server_cards=*/base::FeatureList::IsEnabled(
+ payments::features::kReturnGooglePayInBasicCard));
for (autofill::CreditCard* card : cards)
AddAutofillPaymentInstrument(/*selected=*/false, *card);
}
diff --git a/chromium/components/payments/content/payment_request_state_unittest.cc b/chromium/components/payments/content/payment_request_state_unittest.cc
index 3b4c618d2c1..a2763edbb36 100644
--- a/chromium/components/payments/content/payment_request_state_unittest.cc
+++ b/chromium/components/payments/content/payment_request_state_unittest.cc
@@ -16,6 +16,7 @@
#include "components/payments/content/payment_request_spec.h"
#include "components/payments/content/test_content_payment_request_delegate.h"
#include "components/payments/core/journey_logger.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/modules/payments/payment_request.mojom.h"
@@ -29,11 +30,11 @@ class PaymentRequestStateTest : public testing::Test,
: num_on_selected_information_changed_called_(0),
test_payment_request_delegate_(&test_personal_data_manager_),
journey_logger_(test_payment_request_delegate_.IsIncognito(),
- GURL("http://www.test.com"),
- test_payment_request_delegate_.GetUkmRecorder()),
+ ukm::UkmRecorder::GetNewSourceID()),
address_(autofill::test::GetFullProfile()),
credit_card_visa_(autofill::test::GetCreditCard()) {
test_personal_data_manager_.SetAutofillCreditCardEnabled(true);
+ test_personal_data_manager_.SetAutofillProfileEnabled(true);
test_personal_data_manager_.SetAutofillWalletImportEnabled(true);
test_personal_data_manager_.AddProfile(address_);
credit_card_visa_.set_billing_address_id(address_.guid());
@@ -97,7 +98,7 @@ class PaymentRequestStateTest : public testing::Test,
std::vector<mojom::PaymentMethodDataPtr> GetMethodDataForVisa() {
std::vector<mojom::PaymentMethodDataPtr> method_data;
mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("visa");
+ entry->supported_method = "visa";
method_data.push_back(std::move(entry));
return method_data;
}
@@ -146,7 +147,7 @@ TEST_F(PaymentRequestStateTest, CanMakePayment_CannotMakePayment) {
// The method data requires MasterCard.
std::vector<mojom::PaymentMethodDataPtr> method_data;
mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("mastercard");
+ entry->supported_method = "mastercard";
method_data.push_back(std::move(entry));
RecreateStateWithOptionsAndDetails(mojom::PaymentOptions::New(),
mojom::PaymentDetails::New(),
@@ -161,7 +162,7 @@ TEST_F(PaymentRequestStateTest, CanMakePayment_CannotMakePayment) {
TEST_F(PaymentRequestStateTest, CanMakePayment_OnlyBasicCard) {
// The method data supports everything in basic-card.
mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("basic-card");
+ entry->supported_method = "basic-card";
std::vector<mojom::PaymentMethodDataPtr> method_data;
method_data.push_back(std::move(entry));
RecreateStateWithOptionsAndDetails(mojom::PaymentOptions::New(),
@@ -177,7 +178,7 @@ TEST_F(PaymentRequestStateTest, CanMakePayment_OnlyBasicCard) {
TEST_F(PaymentRequestStateTest, CanMakePayment_BasicCard_SpecificAvailable) {
// The method data supports visa through basic-card.
mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("basic-card");
+ entry->supported_method = "basic-card";
entry->supported_networks.push_back(mojom::BasicCardNetwork::VISA);
std::vector<mojom::PaymentMethodDataPtr> method_data;
method_data.push_back(std::move(entry));
@@ -195,7 +196,7 @@ TEST_F(PaymentRequestStateTest,
CanMakePayment_BasicCard_SpecificAvailableButInvalid) {
// The method data supports jcb through basic-card.
mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("basic-card");
+ entry->supported_method = "basic-card";
entry->supported_networks.push_back(mojom::BasicCardNetwork::JCB);
std::vector<mojom::PaymentMethodDataPtr> method_data;
method_data.push_back(std::move(entry));
@@ -212,7 +213,7 @@ TEST_F(PaymentRequestStateTest,
TEST_F(PaymentRequestStateTest, CanMakePayment_BasicCard_SpecificUnavailable) {
// The method data supports mastercard through basic-card.
mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("basic-card");
+ entry->supported_method = "basic-card";
entry->supported_networks.push_back(mojom::BasicCardNetwork::MASTERCARD);
std::vector<mojom::PaymentMethodDataPtr> method_data;
method_data.push_back(std::move(entry));
diff --git a/chromium/components/payments/content/payment_response_helper_unittest.cc b/chromium/components/payments/content/payment_response_helper_unittest.cc
index ca4410169bd..05092484cf6 100644
--- a/chromium/components/payments/content/payment_response_helper_unittest.cc
+++ b/chromium/components/payments/content/payment_response_helper_unittest.cc
@@ -79,7 +79,7 @@ class PaymentResponseHelperTest : public testing::Test,
std::vector<mojom::PaymentMethodDataPtr> GetMethodDataForVisa() {
std::vector<mojom::PaymentMethodDataPtr> method_data;
mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("visa");
+ entry->supported_method = "visa";
method_data.push_back(std::move(entry));
return method_data;
}
@@ -141,7 +141,7 @@ TEST_F(PaymentResponseHelperTest, GeneratePaymentResponse_SupportedMethod) {
TEST_F(PaymentResponseHelperTest, GeneratePaymentResponse_BasicCard) {
// The method data supports visa through basic-card.
mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New();
- entry->supported_methods.push_back("basic-card");
+ entry->supported_method = "basic-card";
entry->supported_networks.push_back(mojom::BasicCardNetwork::VISA);
std::vector<mojom::PaymentMethodDataPtr> method_data;
method_data.push_back(std::move(entry));
diff --git a/chromium/components/payments/content/service_worker_payment_app_factory.cc b/chromium/components/payments/content/service_worker_payment_app_factory.cc
index 75458892b5d..956860531c7 100644
--- a/chromium/components/payments/content/service_worker_payment_app_factory.cc
+++ b/chromium/components/payments/content/service_worker_payment_app_factory.cc
@@ -11,6 +11,7 @@
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/singleton.h"
+#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/payments/content/installable_payment_app_crawler.h"
#include "components/payments/content/manifest_verifier.h"
@@ -66,9 +67,7 @@ bool AppSupportsAtLeastOneRequestedMethodData(
const std::vector<mojom::PaymentMethodDataPtr>& requests) {
for (const auto& enabled_method : app.enabled_methods) {
for (const auto& request : requests) {
- auto it = std::find(request->supported_methods.begin(),
- request->supported_methods.end(), enabled_method);
- if (it != request->supported_methods.end()) {
+ if (enabled_method == request->supported_method) {
if (enabled_method != "basic-card" ||
BasicCardCapabilitiesMatch(app.capabilities, request)) {
return true;
@@ -267,7 +266,7 @@ void ServiceWorkerPaymentAppFactory::GetAllPaymentApps(
? std::make_unique<payments::PaymentManifestDownloader>(
content::BrowserContext::GetDefaultStoragePartition(
web_contents->GetBrowserContext())
- ->GetURLRequestContext())
+ ->GetURLLoaderFactoryForBrowserProcess())
: std::move(test_downloader_),
cache, requested_method_data, may_crawl_for_installable_payment_apps,
std::move(callback),
diff --git a/chromium/components/payments/content/service_worker_payment_app_factory_unittest.cc b/chromium/components/payments/content/service_worker_payment_app_factory_unittest.cc
index 3b1138405c4..db59cea73c1 100644
--- a/chromium/components/payments/content/service_worker_payment_app_factory_unittest.cc
+++ b/chromium/components/payments/content/service_worker_payment_app_factory_unittest.cc
@@ -23,7 +23,7 @@ TEST_F(ServiceWorkerPaymentAppFactoryTest,
RemoveAppsWithoutMatchingMethodData_NoApps) {
std::vector<mojom::PaymentMethodDataPtr> requested_methods;
requested_methods.emplace_back(mojom::PaymentMethodData::New());
- requested_methods.back()->supported_methods = {"method1", "method2"};
+ requested_methods.back()->supported_method = "method";
content::PaymentAppProvider::PaymentApps no_apps;
RemoveAppsWithoutMatchingMethodData(requested_methods, &no_apps);
@@ -47,11 +47,11 @@ TEST_F(ServiceWorkerPaymentAppFactoryTest,
RemoveAppsWithoutMatchingMethodData_IntersectionOfMethods) {
std::vector<mojom::PaymentMethodDataPtr> requested_methods;
requested_methods.emplace_back(mojom::PaymentMethodData::New());
- requested_methods.back()->supported_methods = {"method1"};
+ requested_methods.back()->supported_method = "method1";
requested_methods.emplace_back(mojom::PaymentMethodData::New());
- requested_methods.back()->supported_methods = {"method2"};
+ requested_methods.back()->supported_method = "method2";
requested_methods.emplace_back(mojom::PaymentMethodData::New());
- requested_methods.back()->supported_methods = {"method3"};
+ requested_methods.back()->supported_method = "method3";
content::PaymentAppProvider::PaymentApps apps;
apps[0] = std::make_unique<content::StoredPaymentApp>();
apps[0]->enabled_methods = {"method2"};
@@ -75,7 +75,7 @@ TEST_F(ServiceWorkerPaymentAppFactoryTest,
RemoveAppsWithoutMatchingMethodData_NoCapabilitiesNetworksOrTypes) {
std::vector<mojom::PaymentMethodDataPtr> requested_methods;
requested_methods.emplace_back(mojom::PaymentMethodData::New());
- requested_methods.back()->supported_methods = {"basic-card"};
+ requested_methods.back()->supported_method = "basic-card";
content::PaymentAppProvider::PaymentApps apps;
apps[0] = std::make_unique<content::StoredPaymentApp>();
apps[0]->enabled_methods = {"basic-card"};
@@ -92,7 +92,7 @@ TEST_F(ServiceWorkerPaymentAppFactoryTest,
RemoveAppsWithoutMatchingMethodData_NoNetworkCapabilities) {
std::vector<mojom::PaymentMethodDataPtr> requested_methods;
requested_methods.emplace_back(mojom::PaymentMethodData::New());
- requested_methods.back()->supported_methods = {"basic-card"};
+ requested_methods.back()->supported_method = "basic-card";
requested_methods.back()->supported_networks = {
mojom::BasicCardNetwork::AMEX};
content::PaymentAppProvider::PaymentApps apps;
@@ -108,7 +108,7 @@ TEST_F(ServiceWorkerPaymentAppFactoryTest,
RemoveAppsWithoutMatchingMethodData_NoTypeCapabilities) {
std::vector<mojom::PaymentMethodDataPtr> requested_methods;
requested_methods.emplace_back(mojom::PaymentMethodData::New());
- requested_methods.back()->supported_methods = {"basic-card"};
+ requested_methods.back()->supported_method = "basic-card";
requested_methods.back()->supported_types = {mojom::BasicCardType::DEBIT};
content::PaymentAppProvider::PaymentApps apps;
apps[0] = std::make_unique<content::StoredPaymentApp>();
@@ -123,7 +123,7 @@ TEST_F(ServiceWorkerPaymentAppFactoryTest,
RemoveAppsWithoutMatchingMethodData_NoMatchingNetworkCapabilities) {
std::vector<mojom::PaymentMethodDataPtr> requested_methods;
requested_methods.emplace_back(mojom::PaymentMethodData::New());
- requested_methods.back()->supported_methods = {"basic-card"};
+ requested_methods.back()->supported_method = "basic-card";
requested_methods.back()->supported_networks = {
mojom::BasicCardNetwork::AMEX};
content::PaymentAppProvider::PaymentApps apps;
@@ -142,7 +142,7 @@ TEST_F(ServiceWorkerPaymentAppFactoryTest,
RemoveAppsWithoutMatchingMethodData_NoMatchingTypeCapabilities) {
std::vector<mojom::PaymentMethodDataPtr> requested_methods;
requested_methods.emplace_back(mojom::PaymentMethodData::New());
- requested_methods.back()->supported_methods = {"basic-card"};
+ requested_methods.back()->supported_method = "basic-card";
requested_methods.back()->supported_types = {mojom::BasicCardType::DEBIT};
content::PaymentAppProvider::PaymentApps apps;
apps[0] = std::make_unique<content::StoredPaymentApp>();
@@ -160,7 +160,7 @@ TEST_F(ServiceWorkerPaymentAppFactoryTest,
RemoveAppsWithoutMatchingMethodData_NoRequestedNetworkOrType) {
std::vector<mojom::PaymentMethodDataPtr> requested_methods;
requested_methods.emplace_back(mojom::PaymentMethodData::New());
- requested_methods.back()->supported_methods = {"basic-card"};
+ requested_methods.back()->supported_method = "basic-card";
content::PaymentAppProvider::PaymentApps apps;
apps[0] = std::make_unique<content::StoredPaymentApp>();
apps[0]->enabled_methods = {"basic-card"};
@@ -190,7 +190,7 @@ TEST_F(ServiceWorkerPaymentAppFactoryTest,
RemoveAppsWithoutMatchingMethodData_IntersectionOfNetworksAndTypes) {
std::vector<mojom::PaymentMethodDataPtr> requested_methods;
requested_methods.emplace_back(mojom::PaymentMethodData::New());
- requested_methods.back()->supported_methods = {"basic-card"};
+ requested_methods.back()->supported_method = "basic-card";
requested_methods.back()->supported_types = {mojom::BasicCardType::DEBIT,
mojom::BasicCardType::CREDIT};
requested_methods.back()->supported_networks = {
@@ -228,7 +228,7 @@ TEST_F(ServiceWorkerPaymentAppFactoryTest,
RemoveAppsWithoutMatchingMethodData_NonBasicCardIgnoresCapabilities) {
std::vector<mojom::PaymentMethodDataPtr> requested_methods;
requested_methods.emplace_back(mojom::PaymentMethodData::New());
- requested_methods.back()->supported_methods = {"unknown-method"};
+ requested_methods.back()->supported_method = "unknown-method";
requested_methods.back()->supported_types = {mojom::BasicCardType::DEBIT};
content::PaymentAppProvider::PaymentApps apps;
apps[0] = std::make_unique<content::StoredPaymentApp>();
diff --git a/chromium/components/payments/content/service_worker_payment_instrument.cc b/chromium/components/payments/content/service_worker_payment_instrument.cc
index 3e84fcdfdb5..bff5a60d7ac 100644
--- a/chromium/components/payments/content/service_worker_payment_instrument.cc
+++ b/chromium/components/payments/content/service_worker_payment_instrument.cc
@@ -168,29 +168,16 @@ ServiceWorkerPaymentInstrument::CreateCanMakePaymentEventData() {
event_data->payment_request_origin = frame_origin_;
for (const auto& modifier : spec_->details().modifiers) {
- std::vector<std::string>::const_iterator it =
- modifier->method_data->supported_methods.begin();
- for (; it != modifier->method_data->supported_methods.end(); it++) {
- if (supported_url_methods.find(*it) != supported_url_methods.end())
- break;
+ if (base::ContainsKey(supported_url_methods,
+ modifier->method_data->supported_method)) {
+ event_data->modifiers.emplace_back(modifier.Clone());
}
- if (it == modifier->method_data->supported_methods.end())
- continue;
-
- event_data->modifiers.emplace_back(modifier.Clone());
}
for (const auto& data : spec_->method_data()) {
- std::vector<std::string>::const_iterator it =
- data->supported_methods.begin();
- for (; it != data->supported_methods.end(); it++) {
- if (supported_url_methods.find(*it) != supported_url_methods.end())
- break;
+ if (base::ContainsKey(supported_url_methods, data->supported_method)) {
+ event_data->method_data.push_back(data.Clone());
}
- if (it == data->supported_methods.end())
- continue;
-
- event_data->method_data.push_back(data.Clone());
}
return event_data;
@@ -252,29 +239,16 @@ ServiceWorkerPaymentInstrument::CreatePaymentRequestEventData() {
stored_payment_app_info_->enabled_methods.end());
}
for (const auto& modifier : spec_->details().modifiers) {
- std::vector<std::string>::const_iterator it =
- modifier->method_data->supported_methods.begin();
- for (; it != modifier->method_data->supported_methods.end(); it++) {
- if (supported_methods.find(*it) != supported_methods.end())
- break;
+ if (base::ContainsKey(supported_methods,
+ modifier->method_data->supported_method)) {
+ event_data->modifiers.emplace_back(modifier.Clone());
}
- if (it == modifier->method_data->supported_methods.end())
- continue;
-
- event_data->modifiers.emplace_back(modifier.Clone());
}
for (const auto& data : spec_->method_data()) {
- std::vector<std::string>::const_iterator it =
- data->supported_methods.begin();
- for (; it != data->supported_methods.end(); it++) {
- if (supported_methods.find(*it) != supported_methods.end())
- break;
+ if (base::ContainsKey(supported_methods, data->supported_method)) {
+ event_data->method_data.push_back(data.Clone());
}
- if (it == data->supported_methods.end())
- continue;
-
- event_data->method_data.push_back(data.Clone());
}
return event_data;
@@ -338,32 +312,22 @@ base::string16 ServiceWorkerPaymentInstrument::GetSublabel() const {
}
bool ServiceWorkerPaymentInstrument::IsValidForModifier(
- const std::vector<std::string>& methods,
+ const std::string& method,
bool supported_networks_specified,
const std::set<std::string>& supported_networks,
bool supported_types_specified,
const std::set<autofill::CreditCard::CardType>& supported_types) const {
// Payment app that needs installation only supports url based payment
// methods.
- if (needs_installation_) {
- return std::find(methods.begin(), methods.end(),
- installable_enabled_method_) != methods.end();
- }
-
- std::vector<std::string> matched_methods;
- for (const auto& modifier_supported_method : methods) {
- if (base::ContainsValue(stored_payment_app_info_->enabled_methods,
- modifier_supported_method)) {
- matched_methods.emplace_back(modifier_supported_method);
- }
- }
+ if (needs_installation_)
+ return installable_enabled_method_ == method;
- if (matched_methods.empty())
+ if (!base::ContainsValue(stored_payment_app_info_->enabled_methods, method))
return false;
// Return true if 'basic-card' is not the only matched payment method. This
// assumes that there is no duplicated payment methods.
- if (matched_methods.size() > 1U || matched_methods[0] != "basic-card")
+ if (method != "basic-card")
return true;
// Checking the capabilities of this instrument against the modifier.
diff --git a/chromium/components/payments/content/service_worker_payment_instrument.h b/chromium/components/payments/content/service_worker_payment_instrument.h
index 0f5dc8c127b..32a4660b177 100644
--- a/chromium/components/payments/content/service_worker_payment_instrument.h
+++ b/chromium/components/payments/content/service_worker_payment_instrument.h
@@ -69,7 +69,7 @@ class ServiceWorkerPaymentInstrument : public PaymentInstrument {
void RecordUse() override;
base::string16 GetLabel() const override;
base::string16 GetSublabel() const override;
- bool IsValidForModifier(const std::vector<std::string>& methods,
+ bool IsValidForModifier(const std::string& method,
bool supported_networks_specified,
const std::set<std::string>& supported_networks,
bool supported_types_specified,
diff --git a/chromium/components/payments/content/service_worker_payment_instrument_unittest.cc b/chromium/components/payments/content/service_worker_payment_instrument_unittest.cc
index 76d7e782777..07cfdb05afe 100644
--- a/chromium/components/payments/content/service_worker_payment_instrument_unittest.cc
+++ b/chromium/components/payments/content/service_worker_payment_instrument_unittest.cc
@@ -77,7 +77,7 @@ class ServiceWorkerPaymentInstrumentTest : public testing::Test,
modifier_1->total->amount->currency = "USD";
modifier_1->total->amount->value = "4.00";
modifier_1->method_data = mojom::PaymentMethodData::New();
- modifier_1->method_data->supported_methods = {"basic-card"};
+ modifier_1->method_data->supported_method = "basic-card";
details->modifiers.push_back(std::move(modifier_1));
mojom::PaymentDetailsModifierPtr modifier_2 =
@@ -87,7 +87,7 @@ class ServiceWorkerPaymentInstrumentTest : public testing::Test,
modifier_2->total->amount->currency = "USD";
modifier_2->total->amount->value = "3.00";
modifier_2->method_data = mojom::PaymentMethodData::New();
- modifier_2->method_data->supported_methods = {"https://bobpay.com"};
+ modifier_2->method_data->supported_method = "https://bobpay.com";
details->modifiers.push_back(std::move(modifier_2));
mojom::PaymentDetailsModifierPtr modifier_3 =
@@ -97,12 +97,12 @@ class ServiceWorkerPaymentInstrumentTest : public testing::Test,
modifier_3->total->amount->currency = "USD";
modifier_3->total->amount->value = "2.00";
modifier_3->method_data = mojom::PaymentMethodData::New();
- modifier_3->method_data->supported_methods = {"https://alicepay.com"};
+ modifier_3->method_data->supported_method = "https://alicepay.com";
details->modifiers.push_back(std::move(modifier_3));
std::vector<mojom::PaymentMethodDataPtr> method_data;
mojom::PaymentMethodDataPtr entry_1 = mojom::PaymentMethodData::New();
- entry_1->supported_methods.push_back("basic-card");
+ entry_1->supported_method = "basic-card";
entry_1->supported_networks.push_back(mojom::BasicCardNetwork::UNIONPAY);
entry_1->supported_networks.push_back(mojom::BasicCardNetwork::JCB);
entry_1->supported_networks.push_back(mojom::BasicCardNetwork::VISA);
@@ -110,7 +110,7 @@ class ServiceWorkerPaymentInstrumentTest : public testing::Test,
method_data.push_back(std::move(entry_1));
mojom::PaymentMethodDataPtr entry_2 = mojom::PaymentMethodData::New();
- entry_2->supported_methods.push_back("https://bobpay.com");
+ entry_2->supported_method = "https://bobpay.com";
method_data.push_back(std::move(entry_2));
spec_ = std::make_unique<PaymentRequestSpec>(
@@ -192,13 +192,10 @@ TEST_F(ServiceWorkerPaymentInstrumentTest, CreatePaymentRequestEventData) {
"https://testmerchant.com/bobpay");
EXPECT_EQ(event_data->method_data.size(), 2U);
- EXPECT_EQ(event_data->method_data[0]->supported_methods.size(), 1U);
- EXPECT_EQ(event_data->method_data[0]->supported_methods[0], "basic-card");
+ EXPECT_EQ(event_data->method_data[0]->supported_method, "basic-card");
EXPECT_EQ(event_data->method_data[0]->supported_networks.size(), 3U);
EXPECT_EQ(event_data->method_data[0]->supported_types.size(), 1U);
- EXPECT_EQ(event_data->method_data[1]->supported_methods.size(), 1U);
- EXPECT_EQ(event_data->method_data[1]->supported_methods[0],
- "https://bobpay.com");
+ EXPECT_EQ(event_data->method_data[1]->supported_method, "https://bobpay.com");
EXPECT_EQ(event_data->total->currency, "USD");
EXPECT_EQ(event_data->total->value, "5.00");
@@ -207,11 +204,11 @@ TEST_F(ServiceWorkerPaymentInstrumentTest, CreatePaymentRequestEventData) {
EXPECT_EQ(event_data->modifiers.size(), 2U);
EXPECT_EQ(event_data->modifiers[0]->total->amount->value, "4.00");
EXPECT_EQ(event_data->modifiers[0]->total->amount->currency, "USD");
- EXPECT_EQ(event_data->modifiers[0]->method_data->supported_methods[0],
+ EXPECT_EQ(event_data->modifiers[0]->method_data->supported_method,
"basic-card");
EXPECT_EQ(event_data->modifiers[1]->total->amount->value, "3.00");
EXPECT_EQ(event_data->modifiers[1]->total->amount->currency, "USD");
- EXPECT_EQ(event_data->modifiers[1]->method_data->supported_methods[0],
+ EXPECT_EQ(event_data->modifiers[1]->method_data->supported_method,
"https://bobpay.com");
}
@@ -232,14 +229,12 @@ TEST_F(ServiceWorkerPaymentInstrumentTest, CreateCanMakePaymentEvent) {
"https://testmerchant.com/bobpay");
EXPECT_EQ(event_data->method_data.size(), 1U);
- EXPECT_EQ(event_data->method_data[0]->supported_methods.size(), 1U);
- EXPECT_EQ(event_data->method_data[0]->supported_methods[0],
- "https://bobpay.com");
+ EXPECT_EQ(event_data->method_data[0]->supported_method, "https://bobpay.com");
EXPECT_EQ(event_data->modifiers.size(), 1U);
EXPECT_EQ(event_data->modifiers[0]->total->amount->value, "3.00");
EXPECT_EQ(event_data->modifiers[0]->total->amount->currency, "USD");
- EXPECT_EQ(event_data->modifiers[0]->method_data->supported_methods[0],
+ EXPECT_EQ(event_data->modifiers[0]->method_data->supported_method,
"https://bobpay.com");
}
@@ -247,31 +242,25 @@ TEST_F(ServiceWorkerPaymentInstrumentTest, CreateCanMakePaymentEvent) {
TEST_F(ServiceWorkerPaymentInstrumentTest, IsValidForModifier) {
CreateServiceWorkerPaymentInstrument(true);
- EXPECT_TRUE(GetInstrument()->IsValidForModifier({"basic-card"}, false, {},
- false, {}));
+ EXPECT_TRUE(
+ GetInstrument()->IsValidForModifier("basic-card", false, {}, false, {}));
- EXPECT_TRUE(GetInstrument()->IsValidForModifier({"https://bobpay.com"}, true,
+ EXPECT_TRUE(GetInstrument()->IsValidForModifier("https://bobpay.com", true,
{}, true, {}));
- EXPECT_TRUE(GetInstrument()->IsValidForModifier(
- {"basic-card", "https://bobpay.com"}, false, {}, false, {}));
-
- EXPECT_TRUE(GetInstrument()->IsValidForModifier(
- {"basic-card", "https://bobpay.com"}, true, {"mastercard"}, false, {}));
-
- EXPECT_FALSE(GetInstrument()->IsValidForModifier({"basic-card"}, true,
+ EXPECT_FALSE(GetInstrument()->IsValidForModifier("basic-card", true,
{"mastercard"}, false, {}));
- EXPECT_TRUE(GetInstrument()->IsValidForModifier({"basic-card"}, true,
+ EXPECT_TRUE(GetInstrument()->IsValidForModifier("basic-card", true,
{"unionpay"}, false, {}));
EXPECT_TRUE(GetInstrument()->IsValidForModifier(
- {"basic-card"}, true, {"unionpay"}, true,
+ "basic-card", true, {"unionpay"}, true,
{autofill::CreditCard::CardType::CARD_TYPE_DEBIT,
autofill::CreditCard::CardType::CARD_TYPE_CREDIT}));
EXPECT_FALSE(GetInstrument()->IsValidForModifier(
- {"basic-card"}, true, {"unionpay"}, true,
+ "basic-card", true, {"unionpay"}, true,
{autofill::CreditCard::CardType::CARD_TYPE_CREDIT}));
}
diff --git a/chromium/components/payments/core/BUILD.gn b/chromium/components/payments/core/BUILD.gn
index 8717791ec03..c58fb97cd2c 100644
--- a/chromium/components/payments/core/BUILD.gn
+++ b/chromium/components/payments/core/BUILD.gn
@@ -69,12 +69,14 @@ static_library("core") {
"//net",
"//services/metrics/public/cpp:metrics_cpp",
"//services/metrics/public/cpp:ukm_builders",
+ "//services/network/public/cpp",
"//third_party/re2",
"//ui/base",
"//url",
]
public_deps = [
+ "//services/network/public/cpp",
"//third_party/icu",
"//third_party/libaddressinput",
]
@@ -99,6 +101,8 @@ static_library("test_support") {
"//components/pref_registry",
"//components/prefs",
"//net:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
]
}
@@ -140,6 +144,8 @@ source_set("unit_tests") {
"//components/ukm:test_support",
"//net:test_support",
"//services/metrics/public/cpp:ukm_builders",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//testing/gmock",
"//testing/gtest",
"//third_party/libaddressinput:test_support",
diff --git a/chromium/components/payments/core/DEPS b/chromium/components/payments/core/DEPS
index a7cb020a237..ea76fc56a00 100644
--- a/chromium/components/payments/core/DEPS
+++ b/chromium/components/payments/core/DEPS
@@ -11,6 +11,8 @@ include_rules = [
"+components/strings",
"+net",
"+services/metrics/public",
+ "+services/network/public/cpp",
+ "+services/network/test",
"+third_party/libaddressinput",
"+third_party/re2",
"+ui/base",
diff --git a/chromium/components/payments/core/autofill_payment_instrument.cc b/chromium/components/payments/core/autofill_payment_instrument.cc
index bdfee812405..09ca141e1e7 100644
--- a/chromium/components/payments/core/autofill_payment_instrument.cc
+++ b/chromium/components/payments/core/autofill_payment_instrument.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/json/json_writer.h"
+#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "components/autofill/core/browser/autofill_country.h"
@@ -120,13 +121,13 @@ base::string16 AutofillPaymentInstrument::GetSublabel() const {
}
bool AutofillPaymentInstrument::IsValidForModifier(
- const std::vector<std::string>& methods,
+ const std::string& method,
bool supported_networks_specified,
const std::set<std::string>& supported_networks,
bool supported_types_specified,
const std::set<autofill::CreditCard::CardType>& supported_types) const {
// This instrument only matches basic-card.
- if (std::find(methods.begin(), methods.end(), "basic-card") == methods.end())
+ if (method != "basic-card")
return false;
// If supported_types is not specified and this instrument matches the method,
diff --git a/chromium/components/payments/core/autofill_payment_instrument.h b/chromium/components/payments/core/autofill_payment_instrument.h
index 9647a71117f..b1859e63f05 100644
--- a/chromium/components/payments/core/autofill_payment_instrument.h
+++ b/chromium/components/payments/core/autofill_payment_instrument.h
@@ -48,7 +48,7 @@ class AutofillPaymentInstrument
void RecordUse() override;
base::string16 GetLabel() const override;
base::string16 GetSublabel() const override;
- bool IsValidForModifier(const std::vector<std::string>& methods,
+ bool IsValidForModifier(const std::string& method,
bool supported_networks_specified,
const std::set<std::string>& supported_networks,
bool supported_types_specified,
diff --git a/chromium/components/payments/core/autofill_payment_instrument_unittest.cc b/chromium/components/payments/core/autofill_payment_instrument_unittest.cc
index f70e5521592..57b9f6a22e6 100644
--- a/chromium/components/payments/core/autofill_payment_instrument_unittest.cc
+++ b/chromium/components/payments/core/autofill_payment_instrument_unittest.cc
@@ -22,6 +22,9 @@
#include "components/payments/core/test_payment_request_delegate.h"
#include "components/strings/grit/components_strings.h"
#include "net/url_request/url_request_test_util.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_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
@@ -64,9 +67,10 @@ class FakePaymentRequestDelegate
: locale_("en-US"),
last_committed_url_("https://shop.com"),
personal_data_("en-US"),
- request_context_(new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get())),
- payments_client_(request_context_.get(),
+ test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)),
+ payments_client_(test_shared_loader_factory_,
nullptr,
nullptr,
this,
@@ -120,7 +124,8 @@ class FakePaymentRequestDelegate
const GURL last_committed_url_;
autofill::TestAddressNormalizer address_normalizer_;
autofill::PersonalDataManager personal_data_;
- scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
autofill::TestAutofillClient autofill_client_;
autofill::payments::PaymentsClient payments_client_;
autofill::payments::FullCardRequest full_card_request_;
diff --git a/chromium/components/payments/core/currency_formatter.cc b/chromium/components/payments/core/currency_formatter.cc
index 2766fa6f709..df23c63dbca 100644
--- a/chromium/components/payments/core/currency_formatter.cc
+++ b/chromium/components/payments/core/currency_formatter.cc
@@ -115,6 +115,16 @@ base::string16 CurrencyFormatter::Format(const std::string& amount) {
output.findAndReplace(tmp_currency_code, "");
tmp_currency_code.truncate(1);
output.findAndReplace(tmp_currency_code, "");
+
+ // In some locales, "-" sign comes before 3-letter currency code followed by
+ // a space and the amount, removing currency code leaves a space between '-'
+ // and the amount. e.g. In en-AU, -4.56 (USD) is formatted as '-USD 4.56'.
+ // This change is a temporary work-around for updating ICU to 62.1/CLDR 33.1.
+ // A rather peculiar requirement/behavior of CurrencyFormatter needs to be
+ // reviewed. See https://crbug.com/856113 .
+ output.findAndReplace("- ", "-");
+ output.findAndReplace(icu::UnicodeString::fromUTF8(u8"-\u00a0"), "-");
+
// Trims any unicode whitespace (including non-breaking space).
if (u_isUWhiteSpace(output[0])) {
output.remove(0, 1);
diff --git a/chromium/components/payments/core/currency_formatter_unittest.cc b/chromium/components/payments/core/currency_formatter_unittest.cc
index 2d0de367629..1beae605b15 100644
--- a/chromium/components/payments/core/currency_formatter_unittest.cc
+++ b/chromium/components/payments/core/currency_formatter_unittest.cc
@@ -41,7 +41,7 @@ TEST_P(PaymentsCurrencyFormatterTest, IsValidCurrencyFormat) {
// Convenience so the test cases can use regular spaces.
const base::string16 kSpace(base::ASCIIToUTF16(" "));
- const base::string16 kNonBreakingSpace(base::UTF8ToUTF16("\xC2\xA0"));
+ const base::string16 kNonBreakingSpace(base::UTF8ToUTF16(u8"\u00a0"));
base::string16 converted;
base::ReplaceChars(base::UTF8ToUTF16(GetParam().expected_amount), kSpace,
kNonBreakingSpace, &converted);
@@ -69,7 +69,7 @@ INSTANTIATE_TEST_CASE_P(
TestCase("55.00", "USD", "en_AU", "55.00", "USD"),
TestCase("55.00", "CAD", "en_AU", "55.00", "CAD"),
TestCase("55.00", "JPY", "en_AU", "55", "JPY"),
- TestCase("55.00", "RUB", "en_AU", "55.00", "RUB"),
+ TestCase("-55.00", "USD", "en_AU", "-55.00", "USD"),
TestCase("55.5", "USD", "en_US", "$55.50", "USD"),
TestCase("55", "USD", "en_US", "$55.00", "USD"),
@@ -120,8 +120,8 @@ INSTANTIATE_TEST_CASE_P(
// Edge cases.
TestCase("", "", "", "", ""),
- TestCase("-1", "", "", "- 1.00", ""),
- TestCase("-1.1255", "", "", "- 1.1255", ""),
+ TestCase("-1", "", "", "-1.00", ""),
+ TestCase("-1.1255", "", "", "-1.1255", ""),
// Handles big numbers.
TestCase(
diff --git a/chromium/components/payments/core/features.cc b/chromium/components/payments/core/features.cc
index bda32a2f0d6..5e9a313b3de 100644
--- a/chromium/components/payments/core/features.cc
+++ b/chromium/components/payments/core/features.cc
@@ -7,6 +7,9 @@
namespace payments {
namespace features {
+const base::Feature kReturnGooglePayInBasicCard{
+ "ReturnGooglePayInBasicCard", base::FEATURE_ENABLED_BY_DEFAULT};
+
#if defined(OS_IOS)
const base::Feature kWebPayments{"WebPayments",
base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chromium/components/payments/core/features.h b/chromium/components/payments/core/features.h
index 279475cfd9d..50e411eb6e2 100644
--- a/chromium/components/payments/core/features.h
+++ b/chromium/components/payments/core/features.h
@@ -11,6 +11,9 @@
namespace payments {
namespace features {
+// Used to control whether Google Pay cards are returned for basic-card.
+extern const base::Feature kReturnGooglePayInBasicCard;
+
#if defined(OS_IOS)
// Used to control the state of the Payment Request API feature.
extern const base::Feature kWebPayments;
diff --git a/chromium/components/payments/core/journey_logger.cc b/chromium/components/payments/core/journey_logger.cc
index b306f492381..307337f6742 100644
--- a/chromium/components/payments/core/journey_logger.cc
+++ b/chromium/components/payments/core/journey_logger.cc
@@ -55,13 +55,10 @@ std::string GetHistogramNameSuffix(
} // namespace
-JourneyLogger::JourneyLogger(bool is_incognito,
- const GURL& url,
- ukm::UkmRecorder* ukm_recorder)
+JourneyLogger::JourneyLogger(bool is_incognito, ukm::SourceId source_id)
: is_incognito_(is_incognito),
events_(EVENT_INITIATED),
- url_(url),
- ukm_recorder_(ukm_recorder) {}
+ source_id_(source_id) {}
JourneyLogger::~JourneyLogger() {
if (WasPaymentRequestTriggered())
@@ -241,16 +238,14 @@ void JourneyLogger::RecordEventsMetric(CompletionStatus completion_status) {
// Record the events in UMA.
base::UmaHistogramSparse("PaymentRequest.Events", events_);
- if (!ukm_recorder_ || !url_.is_valid())
+ if (source_id_ == ukm::kInvalidSourceId)
return;
// Record the events in UKM.
- ukm::SourceId source_id = ukm_recorder_->GetNewSourceID();
- ukm_recorder_->UpdateSourceURL(source_id, url_);
- ukm::builders::PaymentRequest_CheckoutEvents(source_id)
+ ukm::builders::PaymentRequest_CheckoutEvents(source_id_)
.SetCompletionStatus(completion_status)
.SetEvents(events_)
- .Record(ukm_recorder_);
+ .Record(ukm::UkmRecorder::Get());
}
bool JourneyLogger::WasPaymentRequestTriggered() {
diff --git a/chromium/components/payments/core/journey_logger.h b/chromium/components/payments/core/journey_logger.h
index 85709fc4085..7db5ce3a8af 100644
--- a/chromium/components/payments/core/journey_logger.h
+++ b/chromium/components/payments/core/journey_logger.h
@@ -8,11 +8,7 @@
#include <string>
#include "base/macros.h"
-#include "url/gurl.h"
-
-namespace ukm {
-class UkmRecorder;
-}
+#include "services/metrics/public/cpp/ukm_source_id.h"
namespace payments {
@@ -127,9 +123,7 @@ class JourneyLogger {
NOT_SHOWN_REASON_MAX = 4,
};
- JourneyLogger(bool is_incognito,
- const GURL& url,
- ukm::UkmRecorder* ukm_recorder);
+ JourneyLogger(bool is_incognito, ukm::SourceId source_id);
~JourneyLogger();
// Increments the number of selection adds for the specified section.
@@ -232,10 +226,7 @@ class JourneyLogger {
// Accumulates the many events that have happened during the Payment Request.
int events_;
- const GURL url_;
-
- // Not owned, will outlive this object.
- ukm::UkmRecorder* ukm_recorder_;
+ ukm::SourceId source_id_;
DISALLOW_COPY_AND_ASSIGN(JourneyLogger);
};
diff --git a/chromium/components/payments/core/journey_logger_unittest.cc b/chromium/components/payments/core/journey_logger_unittest.cc
index b9b3f43bedf..8a30a1a95c5 100644
--- a/chromium/components/payments/core/journey_logger_unittest.cc
+++ b/chromium/components/payments/core/journey_logger_unittest.cc
@@ -5,7 +5,7 @@
#include "components/payments/core/journey_logger.h"
#include "base/metrics/metrics_hashes.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "components/ukm/test_ukm_recorder.h"
@@ -23,8 +23,7 @@ namespace payments {
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentNotCalled_NoShow) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
logger.SetCompleted();
@@ -42,8 +41,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndUserAbort) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The merchant does not query CanMakePayment, show the PaymentRequest and the
// user aborts it.
@@ -66,8 +64,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndOtherAbort) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The merchant does not query CanMakePayment, show the PaymentRequest and
// there is an abort not initiated by the user.
@@ -90,8 +87,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndComplete) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The merchant does not query CanMakePayment, show the PaymentRequest and the
// user completes it.
@@ -114,8 +110,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseAndNoShow) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetCanMakePaymentValue(false);
@@ -136,8 +131,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueAndNoShow) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The user can make payment and the PaymentRequest is not shown.
logger.SetCanMakePaymentValue(true);
@@ -158,8 +152,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndUserAbort) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The user cannot make payment, the Payment Request is shown but is aborted
// by the user.
@@ -183,8 +176,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndOtherAbort) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The user cannot make payment, the Payment Request is shown but is aborted.
logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
@@ -207,8 +199,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndComplete) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The user cannot make payment, the payment request is shown and is
// completed.
@@ -232,8 +223,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndUserAbort) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The user can make payment, the Payment Request is shown and aborted by the
// user.
@@ -257,8 +247,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndOtherAbort) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The user can make a payment, the request is shown but the transaction is
// aborted.
@@ -282,8 +271,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndComplete) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The user can make a payment, the request is shown and the user completes
// the checkout.
@@ -307,8 +295,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePayment_IncognitoTab) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/true, ukm::kInvalidSourceId);
// The user can make a payment, the request is shown and the user completes
// the checkout.
@@ -332,8 +319,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_SuggestionsForEverything_Completed) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The merchant only requests payment information.
logger.SetRequestedInformation(
@@ -378,8 +364,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_SuggestionsForEverything_UserAborted) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The merchant only requests payment information.
logger.SetRequestedInformation(
@@ -424,8 +409,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_SuggestionsForEverything_OtherAborted) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The merchant only requests payment information.
logger.SetRequestedInformation(
@@ -471,8 +455,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_SuggestionsForEverything_Incognito) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/true, ukm::kInvalidSourceId);
// The merchant only requests payment information.
logger.SetRequestedInformation(
@@ -517,8 +500,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_NoSuggestionsForEverything_Completed) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The merchant only requests payment information.
logger.SetRequestedInformation(
@@ -563,8 +545,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_NoSuggestionsForEverything_UserAborted) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The merchant only requests payment information.
logger.SetRequestedInformation(
@@ -609,8 +590,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_NoSuggestionsForEverything_OtherAborted) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The merchant only requests payment information.
logger.SetRequestedInformation(
@@ -656,8 +636,7 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_NoSuggestionsForEverything_Incognito) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/true, ukm::kInvalidSourceId);
// The merchant only requests payment information.
logger.SetRequestedInformation(
@@ -703,8 +682,7 @@ TEST(
JourneyLoggerTest,
RecordJourneyStatsHistograms_NoCompleteSuggestionsForEverything_OtherAborted) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The merchant only requests payment information.
logger.SetRequestedInformation(
@@ -751,8 +729,7 @@ TEST(
JourneyLoggerTest,
RecordJourneyStatsHistograms_NoCompleteSuggestionsForEverything_SomeComplete_OtherAborted) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The merchant only requests payment information.
logger.SetRequestedInformation(
@@ -801,8 +778,7 @@ TEST(
JourneyLoggerTest,
RecordJourneyStatsHistograms_CompleteSuggestionsForEverything_OtherAborted) {
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
// The merchant only requests payment information.
logger.SetRequestedInformation(
@@ -849,10 +825,8 @@ TEST(
// Requests.
TEST(JourneyLoggerTest, RecordJourneyStatsHistograms_TwoPaymentRequests) {
base::HistogramTester histogram_tester;
- JourneyLogger logger1(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
- JourneyLogger logger2(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_recorder=*/nullptr);
+ JourneyLogger logger1(/*is_incognito=*/false, ukm::kInvalidSourceId);
+ JourneyLogger logger2(/*is_incognito=*/false, ukm::kInvalidSourceId);
// Make the two loggers have different data.
logger1.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
@@ -933,8 +907,9 @@ TEST(JourneyLoggerTest,
char test_url[] = "http://www.google.com/";
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(test_url),
- /*ukm_recorder=*/&ukm_recorder);
+ ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID();
+ ukm_recorder.UpdateSourceURL(source_id, GURL(test_url));
+ JourneyLogger logger(/*is_incognito=*/true, source_id);
logger.SetRequestedInformation(
/*requested_shipping=*/true, /*requested_email=*/true,
/*requested_phone=*/false, /*requested_name=*/false);
@@ -983,8 +958,9 @@ TEST(JourneyLoggerTest,
char test_url[] = "http://www.google.com/";
base::HistogramTester histogram_tester;
- JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(test_url),
- /*ukm_recorder=*/&ukm_recorder);
+ ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID();
+ ukm_recorder.UpdateSourceURL(source_id, GURL(test_url));
+ JourneyLogger logger(/*is_incognito=*/true, source_id);
logger.SetRequestedInformation(
/*requested_shipping=*/true, /*requested_email=*/true,
/*requested_phone=*/false, /*requested_name=*/false);
diff --git a/chromium/components/payments/core/payment_details_modifier.cc b/chromium/components/payments/core/payment_details_modifier.cc
index 48c11e72ba5..ded6efe15da 100644
--- a/chromium/components/payments/core/payment_details_modifier.cc
+++ b/chromium/components/payments/core/payment_details_modifier.cc
@@ -57,13 +57,8 @@ bool PaymentDetailsModifier::operator!=(
std::unique_ptr<base::DictionaryValue>
PaymentDetailsModifier::ToDictionaryValue() const {
auto result = std::make_unique<base::DictionaryValue>();
- std::unique_ptr<base::ListValue> methods =
- std::make_unique<base::ListValue>();
- size_t numMethods = method_data.supported_methods.size();
- for (size_t i = 0; i < numMethods; i++) {
- methods->GetList().emplace_back(method_data.supported_methods[i]);
- }
- result->SetList(kPaymentDetailsModifierSupportedMethods, std::move(methods));
+ result->SetString(kPaymentDetailsModifierSupportedMethods,
+ method_data.supported_method);
result->SetString(kPaymentDetailsModifierData, method_data.data);
if (total) {
result->SetDictionary(kPaymentDetailsModifierTotal,
diff --git a/chromium/components/payments/core/payment_details_modifier_unittest.cc b/chromium/components/payments/core/payment_details_modifier_unittest.cc
index fd9679c7da0..203cf541cfd 100644
--- a/chromium/components/payments/core/payment_details_modifier_unittest.cc
+++ b/chromium/components/payments/core/payment_details_modifier_unittest.cc
@@ -15,9 +15,7 @@ namespace payments {
TEST(PaymentRequestTest, EmptyPaymentDetailsModifierDictionary) {
base::DictionaryValue expected_value;
- std::unique_ptr<base::ListValue> supported_methods_list =
- std::make_unique<base::ListValue>();
- expected_value.SetList("supportedMethods", std::move(supported_methods_list));
+ expected_value.SetString("supportedMethods", "");
expected_value.SetString("data", "");
PaymentDetailsModifier payment_details_modifier;
@@ -30,11 +28,7 @@ TEST(PaymentRequestTest, EmptyPaymentDetailsModifierDictionary) {
TEST(PaymentRequestTest, PopulatedDetailsModifierDictionary) {
base::DictionaryValue expected_value;
- std::unique_ptr<base::ListValue> supported_methods_list =
- std::make_unique<base::ListValue>();
- supported_methods_list->GetList().emplace_back("basic-card");
- supported_methods_list->GetList().emplace_back("amex");
- expected_value.SetList("supportedMethods", std::move(supported_methods_list));
+ expected_value.SetString("supportedMethods", "basic-card");
expected_value.SetString("data",
"{\"supportedNetworks\":[\"visa\",\"mastercard\"]}");
std::unique_ptr<base::DictionaryValue> item_dict =
@@ -49,9 +43,7 @@ TEST(PaymentRequestTest, PopulatedDetailsModifierDictionary) {
expected_value.SetDictionary("total", std::move(item_dict));
PaymentDetailsModifier payment_details_modifier;
- payment_details_modifier.method_data.supported_methods.push_back(
- "basic-card");
- payment_details_modifier.method_data.supported_methods.push_back("amex");
+ payment_details_modifier.method_data.supported_method = "basic-card";
payment_details_modifier.method_data.data =
"{\"supportedNetworks\":[\"visa\",\"mastercard\"]}";
payment_details_modifier.total = std::make_unique<PaymentItem>();
@@ -72,16 +64,13 @@ TEST(PaymentRequestTest, PaymentDetailsModifierEquality) {
PaymentDetailsModifier details_modifier2;
EXPECT_EQ(details_modifier1, details_modifier2);
- std::vector<std::string> supported_methods1;
- supported_methods1.push_back("China UnionPay");
- supported_methods1.push_back("BobPay");
- details_modifier1.method_data.supported_methods = supported_methods1;
+ details_modifier1.method_data.supported_method = "BobPay";
EXPECT_NE(details_modifier1, details_modifier2);
- std::vector<std::string> supported_methods2;
- supported_methods2.push_back("BobPay");
- details_modifier2.method_data.supported_methods = supported_methods2;
+
+ details_modifier2.method_data.supported_method = "China UnionPay";
EXPECT_NE(details_modifier1, details_modifier2);
- details_modifier2.method_data.supported_methods = supported_methods1;
+
+ details_modifier2.method_data.supported_method = "BobPay";
EXPECT_EQ(details_modifier1, details_modifier2);
details_modifier1.method_data.data =
diff --git a/chromium/components/payments/core/payment_details_validation.cc b/chromium/components/payments/core/payment_details_validation.cc
index 1d9085da32f..b209b0f60cd 100644
--- a/chromium/components/payments/core/payment_details_validation.cc
+++ b/chromium/components/payments/core/payment_details_validation.cc
@@ -88,8 +88,8 @@ bool ValidatePaymentDetailsModifiers(
}
for (const auto& modifier : modifiers) {
- if (modifier.method_data.supported_methods.empty()) {
- *error_message = "Must specify at least one payment method identifier";
+ if (modifier.method_data.supported_method.empty()) {
+ *error_message = "Must specify payment method identifier";
return false;
}
diff --git a/chromium/components/payments/core/payment_details_validation_unittest.cc b/chromium/components/payments/core/payment_details_validation_unittest.cc
index dddfe16d756..97ba1800595 100644
--- a/chromium/components/payments/core/payment_details_validation_unittest.cc
+++ b/chromium/components/payments/core/payment_details_validation_unittest.cc
@@ -265,7 +265,7 @@ INSTANTIATE_TEST_CASE_P(TestCases,
"amount": {"currency": "USD", "value": "-0.01"}
}],
"modifiers": [{
- "supportedMethods": ["basic-card"],
+ "supportedMethods": "basic-card",
"data": {
"supportedTypes": ["debit"]
},
@@ -278,7 +278,7 @@ INSTANTIATE_TEST_CASE_P(TestCases,
"amount": {"currency": "USD", "value": "-0.20"}
}]
}, {
- "supportedMethods": ["basic-card"],
+ "supportedMethods": "basic-card",
"data": {
"supportedTypes": ["mastercard"]
},
@@ -304,7 +304,7 @@ INSTANTIATE_TEST_CASE_P(TestCases,
"amount": {"currency": "USD", "value": "-0.01"}
}],
"modifiers": [{
- "supportedMethods": ["basic-card"],
+ "supportedMethods": "basic-card",
"data": {
"supportedTypes": ["debit"]
},
@@ -330,7 +330,7 @@ INSTANTIATE_TEST_CASE_P(TestCases,
"amount": {"currency": "USD", "value": "-0.01"}
}],
"modifiers": [{
- "supportedMethods": ["basic-card"],
+ "supportedMethods": "basic-card",
"data": {
"supportedTypes": ["debit"]
},
@@ -356,7 +356,7 @@ INSTANTIATE_TEST_CASE_P(TestCases,
"amount": {"currency": "USD", "value": "-0.01"}
}],
"modifiers": [{
- "supportedMethods": ["basic-card"],
+ "supportedMethods": "basic-card",
"data": {
"supportedTypes": ["debit"]
},
@@ -382,7 +382,7 @@ INSTANTIATE_TEST_CASE_P(TestCases,
"amount": {"currency": "USD", "value": "-0.01"}
}],
"modifiers": [{
- "supportedMethods": ["basic-card"],
+ "supportedMethods": "basic-card",
"data": {
"supportedTypes": ["debit"]
},
@@ -408,7 +408,7 @@ INSTANTIATE_TEST_CASE_P(TestCases,
"amount": {"currency": "USD", "value": "-0.01"}
}],
"modifiers": [{
- "supportedMethods": ["basic-card"],
+ "supportedMethods": "basic-card",
"data": {
"supportedTypes": ["debit"]
},
diff --git a/chromium/components/payments/core/payment_instrument.h b/chromium/components/payments/core/payment_instrument.h
index fe643e46fa3..c444def14d0 100644
--- a/chromium/components/payments/core/payment_instrument.h
+++ b/chromium/components/payments/core/payment_instrument.h
@@ -61,9 +61,9 @@ class PaymentInstrument {
virtual const gfx::ImageSkia* icon_image_skia() const;
// Returns true if this payment instrument can be used to fulfill a request
- // specifying |methods| as supported methods of payment, false otherwise.
+ // specifying |method| as supported method of payment, false otherwise.
virtual bool IsValidForModifier(
- const std::vector<std::string>& methods,
+ const std::string& method,
bool supported_networks_specified,
const std::set<std::string>& supported_networks,
bool supported_types_specified,
diff --git a/chromium/components/payments/core/payment_manifest_downloader.cc b/chromium/components/payments/core/payment_manifest_downloader.cc
index 6a19aee7d19..3ba5bec0052 100644
--- a/chromium/components/payments/core/payment_manifest_downloader.cc
+++ b/chromium/components/payments/core/payment_manifest_downloader.cc
@@ -14,29 +14,31 @@
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/link_header_util/link_header_util.h"
#include "net/base/load_flags.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/base/url_util.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/http/http_util.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "url/gurl.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
#include "url/url_constants.h"
namespace payments {
namespace {
-GURL ParseResponseHeader(const net::URLFetcher* source) {
- if (source->GetResponseCode() != net::HTTP_OK &&
- source->GetResponseCode() != net::HTTP_NO_CONTENT) {
- LOG(ERROR) << "Unable to make a HEAD request to " << source->GetURL()
+GURL ParseResponseHeader(const GURL& original_url,
+ const GURL& final_url,
+ scoped_refptr<net::HttpResponseHeaders> headers) {
+ if (!headers) {
+ LOG(ERROR) << "No HTTP headers found on " << final_url
<< " for payment method manifest.";
return GURL();
}
- net::HttpResponseHeaders* headers = source->GetResponseHeaders();
- if (!headers) {
- LOG(ERROR) << "No HTTP headers found on " << source->GetURL()
+ int response_code = headers->response_code();
+ if (response_code != net::HTTP_OK && response_code != net::HTTP_NO_CONTENT) {
+ LOG(ERROR) << "Unable to make a HEAD request to " << final_url
<< " for payment method manifest.";
return GURL();
}
@@ -44,7 +46,7 @@ GURL ParseResponseHeader(const net::URLFetcher* source) {
std::string link_header;
headers->GetNormalizedHeader("link", &link_header);
if (link_header.empty()) {
- LOG(ERROR) << "No HTTP Link headers found on " << source->GetURL()
+ LOG(ERROR) << "No HTTP Link headers found on " << final_url
<< " for payment method manifest.";
return GURL();
}
@@ -65,11 +67,11 @@ GURL ParseResponseHeader(const net::URLFetcher* source) {
base::SplitString(rel->second.value_or(""), HTTP_LWS,
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (base::ContainsValue(rel_parts, "payment-method-manifest"))
- return source->GetOriginalURL().Resolve(payment_method_manifest_url);
+ return original_url.Resolve(payment_method_manifest_url);
}
LOG(ERROR) << "No rel=\"payment-method-manifest\" HTTP Link headers found on "
- << source->GetURL() << " for payment method manifest.";
+ << final_url << " for payment method manifest.";
return GURL();
}
@@ -79,42 +81,41 @@ bool IsValidManifestUrl(const GURL& url) {
(url.SchemeIs(url::kHttpScheme) && net::IsLocalhost(url)));
}
-GURL ParseRedirectUrlFromResponseHeader(const net::URLFetcher* source) {
+GURL ParseRedirectUrl(const net::RedirectInfo& redirect_info) {
// Do not follow net::HTTP_MULTIPLE_CHOICES, net::HTTP_NOT_MODIFIED and
// net::HTTP_USE_PROXY redirects.
- if (source->GetResponseCode() != net::HTTP_MOVED_PERMANENTLY &&
- source->GetResponseCode() != net::HTTP_FOUND &&
- source->GetResponseCode() != net::HTTP_SEE_OTHER &&
- source->GetResponseCode() != net::HTTP_TEMPORARY_REDIRECT &&
- source->GetResponseCode() != net::HTTP_PERMANENT_REDIRECT) {
+ if (redirect_info.status_code != net::HTTP_MOVED_PERMANENTLY &&
+ redirect_info.status_code != net::HTTP_FOUND &&
+ redirect_info.status_code != net::HTTP_SEE_OTHER &&
+ redirect_info.status_code != net::HTTP_TEMPORARY_REDIRECT &&
+ redirect_info.status_code != net::HTTP_PERMANENT_REDIRECT) {
return GURL();
}
- if (!IsValidManifestUrl(source->GetURL()))
+ if (!IsValidManifestUrl(redirect_info.new_url))
return GURL();
- return source->GetURL();
+ return redirect_info.new_url;
}
-std::string ParseResponseContent(const net::URLFetcher* source) {
- std::string content;
- if (source->GetResponseCode() != net::HTTP_OK) {
- LOG(ERROR) << "Unable to download " << source->GetURL()
+std::string ParseResponseContent(
+ const GURL& final_url,
+ const std::string& response_body,
+ scoped_refptr<net::HttpResponseHeaders> headers) {
+ if (!headers || headers->response_code() != net::HTTP_OK) {
+ LOG(ERROR) << "Unable to download " << final_url
<< " for payment manifests.";
- return content;
+ return std::string();
}
- bool success = source->GetResponseAsString(&content);
- DCHECK(success); // Whether the fetcher was set to store result as string.
-
- return content;
+ return response_body;
}
} // namespace
PaymentManifestDownloader::PaymentManifestDownloader(
- const scoped_refptr<net::URLRequestContextGetter>& context)
- : context_(context) {}
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+ : url_loader_factory_(std::move(url_loader_factory)) {}
PaymentManifestDownloader::~PaymentManifestDownloader() {}
@@ -123,7 +124,7 @@ void PaymentManifestDownloader::DownloadPaymentMethodManifest(
PaymentManifestDownloadCallback callback) {
DCHECK(IsValidManifestUrl(url));
// Restrict number of redirects for efficiency and breaking circle.
- InitiateDownload(url, net::URLFetcher::HEAD,
+ InitiateDownload(url, "HEAD",
/*allowed_number_of_redirects=*/3, std::move(callback));
}
@@ -131,7 +132,7 @@ void PaymentManifestDownloader::DownloadWebAppManifest(
const GURL& url,
PaymentManifestDownloadCallback callback) {
DCHECK(IsValidManifestUrl(url));
- InitiateDownload(url, net::URLFetcher::GET, /*allowed_number_of_redirects=*/0,
+ InitiateDownload(url, "GET", /*allowed_number_of_redirects=*/0,
std::move(callback));
}
@@ -139,29 +140,68 @@ PaymentManifestDownloader::Download::Download() {}
PaymentManifestDownloader::Download::~Download() {}
-void PaymentManifestDownloader::OnURLFetchComplete(
- const net::URLFetcher* source) {
- auto download_it = downloads_.find(source);
+void PaymentManifestDownloader::OnURLLoaderRedirect(
+ network::SimpleURLLoader* url_loader,
+ const net::RedirectInfo& redirect_info,
+ const network::ResourceResponseHead& response_head,
+ std::vector<std::string>* to_be_removed_headers) {
+ auto download_it = downloads_.find(url_loader);
DCHECK(download_it != downloads_.end());
std::unique_ptr<Download> download = std::move(download_it->second);
downloads_.erase(download_it);
- if (download->request_type == net::URLFetcher::HEAD) {
- // Manually follow some type of redirects.
- if (download->allowed_number_of_redirects > 0) {
- GURL redirect_url = ParseRedirectUrlFromResponseHeader(source);
- if (!redirect_url.is_empty()) {
- InitiateDownload(redirect_url, net::URLFetcher::HEAD,
- --download->allowed_number_of_redirects,
- std::move(download->callback));
- return;
- }
+ // Manually follow some type of redirects.
+ if (download->allowed_number_of_redirects > 0) {
+ DCHECK(download->method == "HEAD");
+ GURL redirect_url = ParseRedirectUrl(redirect_info);
+ if (!redirect_url.is_empty() &&
+ // Do not allow cross site redirects.
+ net::registry_controlled_domains::SameDomainOrHost(
+ download->original_url, redirect_url,
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
+ InitiateDownload(redirect_url, "HEAD",
+ --download->allowed_number_of_redirects,
+ std::move(download->callback));
+ return;
}
+ }
+
+ std::move(download->callback).Run(std::string());
+}
+
+void PaymentManifestDownloader::OnURLLoaderComplete(
+ network::SimpleURLLoader* url_loader,
+ std::unique_ptr<std::string> response_body) {
+ scoped_refptr<net::HttpResponseHeaders> headers;
+ if (url_loader->ResponseInfo())
+ headers = url_loader->ResponseInfo()->headers;
+
+ std::string response_body_str;
+ if (response_body.get())
+ response_body_str = std::move(*response_body);
+
+ OnURLLoaderCompleteInternal(url_loader, url_loader->GetFinalURL(),
+ response_body_str, headers,
+ url_loader->NetError());
+}
- GURL url = ParseResponseHeader(source);
+void PaymentManifestDownloader::OnURLLoaderCompleteInternal(
+ network::SimpleURLLoader* url_loader,
+ const GURL& final_url,
+ const std::string& response_body,
+ scoped_refptr<net::HttpResponseHeaders> headers,
+ int net_error) {
+ auto download_it = downloads_.find(url_loader);
+ DCHECK(download_it != downloads_.end());
+
+ std::unique_ptr<Download> download = std::move(download_it->second);
+ downloads_.erase(download_it);
+
+ if (download->method == "HEAD") {
+ GURL url = ParseResponseHeader(download->original_url, final_url, headers);
if (IsValidManifestUrl(url)) {
- InitiateDownload(url, net::URLFetcher::GET,
+ InitiateDownload(url, "GET",
/*allowed_number_of_redirects=*/0,
std::move(download->callback));
} else {
@@ -171,14 +211,27 @@ void PaymentManifestDownloader::OnURLFetchComplete(
LOG(ERROR) << url << " is not a valid payment method manifest URL.";
std::move(download->callback).Run(std::string());
}
- } else {
- std::move(download->callback).Run(ParseResponseContent(source));
+
+ return;
}
+
+ std::move(download->callback)
+ .Run(ParseResponseContent(final_url, response_body, headers));
+}
+
+network::SimpleURLLoader* PaymentManifestDownloader::GetLoaderForTesting() {
+ CHECK_EQ(downloads_.size(), 1u);
+ return downloads_.begin()->second->loader.get();
+}
+
+GURL PaymentManifestDownloader::GetLoaderOriginalURLForTesting() {
+ CHECK_EQ(downloads_.size(), 1u);
+ return downloads_.begin()->second->original_url;
}
void PaymentManifestDownloader::InitiateDownload(
const GURL& url,
- net::URLFetcher::RequestType request_type,
+ const std::string& method,
int allowed_number_of_redirects,
PaymentManifestDownloadCallback callback) {
DCHECK(IsValidManifestUrl(url));
@@ -203,23 +256,30 @@ void PaymentManifestDownloader::InitiateDownload(
"disable all payment apps to stop this feature."
policy_exception_justification: "Not implemented."
})");
- std::unique_ptr<net::URLFetcher> fetcher = net::URLFetcher::Create(
- 0 /* id */, url, request_type, this, traffic_annotation);
- data_use_measurement::DataUseUserData::AttachToFetcher(
- fetcher.get(), data_use_measurement::DataUseUserData::PAYMENTS);
- fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES);
- fetcher->SetStopOnRedirect(true);
- fetcher->SetRequestContext(context_.get());
- fetcher->Start();
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = url;
+ resource_request->method = method;
+ resource_request->load_flags =
+ net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
+ std::unique_ptr<network::SimpleURLLoader> loader =
+ network::SimpleURLLoader::Create(std::move(resource_request),
+ traffic_annotation);
+ loader->SetOnRedirectCallback(
+ base::BindRepeating(&PaymentManifestDownloader::OnURLLoaderRedirect,
+ base::Unretained(this), loader.get()));
+ loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&PaymentManifestDownloader::OnURLLoaderComplete,
+ base::Unretained(this), loader.get()));
auto download = std::make_unique<Download>();
- download->request_type = request_type;
- download->fetcher = std::move(fetcher);
+ download->method = method;
+ download->original_url = url;
+ download->loader = std::move(loader);
download->callback = std::move(callback);
download->allowed_number_of_redirects = allowed_number_of_redirects;
- const net::URLFetcher* identifier = download->fetcher.get();
+ const network::SimpleURLLoader* identifier = download->loader.get();
auto insert_result =
downloads_.insert(std::make_pair(identifier, std::move(download)));
DCHECK(insert_result.second); // Whether the insert has succeeded.
diff --git a/chromium/components/payments/core/payment_manifest_downloader.h b/chromium/components/payments/core/payment_manifest_downloader.h
index ef41decc9a4..d4583a3e0c2 100644
--- a/chromium/components/payments/core/payment_manifest_downloader.h
+++ b/chromium/components/payments/core/payment_manifest_downloader.h
@@ -8,17 +8,22 @@
#include <map>
#include <memory>
#include <string>
+#include <vector>
-#include "base/callback_forward.h"
+#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
-
-class GURL;
+#include "url/gurl.h"
namespace net {
-class URLRequestContextGetter;
+class HttpResponseHeaders;
+struct RedirectInfo;
+} // namespace net
+
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+struct ResourceResponseHead;
}
namespace payments {
@@ -43,13 +48,12 @@ using PaymentManifestDownloadCallback =
//
// The downloader does not follow redirects. A download succeeds only if all
// HTTP response codes are 200 or 204.
-class PaymentManifestDownloader : public net::URLFetcherDelegate {
+class PaymentManifestDownloader {
public:
- // |delegate| should not be null and must outlive this object.
explicit PaymentManifestDownloader(
- const scoped_refptr<net::URLRequestContextGetter>& context);
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
- ~PaymentManifestDownloader() override;
+ virtual ~PaymentManifestDownloader();
// Download a payment method manifest via two consecutive HTTP requests:
//
@@ -83,32 +87,58 @@ class PaymentManifestDownloader : public net::URLFetcherDelegate {
PaymentManifestDownloadCallback callback);
private:
+ friend class PaymentMethodManifestDownloaderTest;
+ friend class WebAppManifestDownloaderTest;
+
// Information about an ongoing download request.
struct Download {
Download();
~Download();
int allowed_number_of_redirects = 0;
- net::URLFetcher::RequestType request_type;
- std::unique_ptr<net::URLFetcher> fetcher;
+ std::string method;
+ GURL original_url;
+ std::unique_ptr<network::SimpleURLLoader> loader;
PaymentManifestDownloadCallback callback;
};
- // net::URLFetcherDelegate
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ // Called by SimpleURLLoader on a redirect.
+ void OnURLLoaderRedirect(network::SimpleURLLoader* url_loader,
+ const net::RedirectInfo& redirect_info,
+ const network::ResourceResponseHead& response_head,
+ std::vector<std::string>* to_be_removed_headers);
+
+ // Called by SimpleURLLoader on completion.
+ void OnURLLoaderComplete(network::SimpleURLLoader* url_loader,
+ std::unique_ptr<std::string> response_body);
+
+ // Internally called by OnURLLoaderComplete, exposed to ease unit tests.
+ void OnURLLoaderCompleteInternal(
+ network::SimpleURLLoader* url_loader,
+ const GURL& final_url,
+ const std::string& response_body,
+ scoped_refptr<net::HttpResponseHeaders> headers,
+ int net_error);
+
+ // Called by unittests to get the one in-progress loader.
+ network::SimpleURLLoader* GetLoaderForTesting();
+
+ // Called by unittests to get the original URL of the in-progress loader.
+ GURL GetLoaderOriginalURLForTesting();
void InitiateDownload(const GURL& url,
- net::URLFetcher::RequestType request_type,
+ const std::string& method,
int allowed_number_of_redirects,
PaymentManifestDownloadCallback callback);
- scoped_refptr<net::URLRequestContextGetter> context_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
- // Downloads are identified by net::URLFetcher pointers, because that's the
- // only unique piece of information that OnURLFetchComplete() receives. Can't
- // rely on the URL of the download, because of possible collision between HEAD
- // and GET requests.
- std::map<const net::URLFetcher*, std::unique_ptr<Download>> downloads_;
+ // Downloads are identified by network::SimpleURLLoader pointers, because
+ // that's the only unique piece of information that OnURLLoaderComplete()
+ // receives. Can't rely on the URL of the download, because of possible
+ // collision between HEAD and GET requests.
+ std::map<const network::SimpleURLLoader*, std::unique_ptr<Download>>
+ downloads_;
DISALLOW_COPY_AND_ASSIGN(PaymentManifestDownloader);
};
diff --git a/chromium/components/payments/core/payment_manifest_downloader_unittest.cc b/chromium/components/payments/core/payment_manifest_downloader_unittest.cc
index 99c3d6bccc4..695c34f34ba 100644
--- a/chromium/components/payments/core/payment_manifest_downloader_unittest.cc
+++ b/chromium/components/payments/core/payment_manifest_downloader_unittest.cc
@@ -4,25 +4,28 @@
#include "components/payments/core/payment_manifest_downloader.h"
+#include "base/strings/stringprintf.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "net/http/http_response_headers.h"
-#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace payments {
-namespace {
class PaymentMethodManifestDownloaderTest : public testing::Test {
public:
PaymentMethodManifestDownloaderTest()
- : context_(new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get())),
- downloader_(context_) {
+ : test_url_("https://bobpay.com"),
+ shared_url_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_factory_)),
+ downloader_(shared_url_loader_factory_) {
downloader_.DownloadPaymentMethodManifest(
- GURL("https://bobpay.com"),
+ test_url_,
base::BindOnce(&PaymentMethodManifestDownloaderTest::OnManifestDownload,
base::Unretained(this)));
}
@@ -31,340 +34,253 @@ class PaymentMethodManifestDownloaderTest : public testing::Test {
MOCK_METHOD1(OnManifestDownload, void(const std::string& content));
- net::TestURLFetcher* fetcher() { return factory_.GetFetcherByID(0); }
+ void CallComplete(int response_code = 200,
+ const std::string& link_header = std::string(),
+ const std::string& response_body = std::string(),
+ bool send_headers = true) {
+ scoped_refptr<net::HttpResponseHeaders> headers;
+ if (send_headers) {
+ headers = base::MakeRefCounted<net::HttpResponseHeaders>(std::string());
+ headers->ReplaceStatusLine(base::StringPrintf("%d", response_code));
+ }
+
+ if (!link_header.empty())
+ headers->AddHeader(link_header);
+ downloader_.OnURLLoaderCompleteInternal(downloader_.GetLoaderForTesting(),
+ test_url_, response_body, headers,
+ net::OK);
+ }
+
+ void CallRedirect(int redirect_code, const GURL& new_url) {
+ net::RedirectInfo redirect_info;
+ redirect_info.status_code = redirect_code;
+ redirect_info.new_url = new_url;
+ std::vector<std::string> to_be_removed_headers;
+
+ downloader_.OnURLLoaderRedirect(
+ downloader_.GetLoaderForTesting(), redirect_info,
+ network::ResourceResponseHead(), &to_be_removed_headers);
+ }
+
+ GURL GetOriginalURL() { return downloader_.GetLoaderOriginalURLForTesting(); }
private:
+ GURL test_url_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
- net::TestURLFetcherFactory factory_;
- scoped_refptr<net::TestURLRequestContextGetter> context_;
+ network::TestURLLoaderFactory test_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
PaymentManifestDownloader downloader_;
DISALLOW_COPY_AND_ASSIGN(PaymentMethodManifestDownloaderTest);
};
TEST_F(PaymentMethodManifestDownloaderTest, HttpHeadResponse404IsFailure) {
- fetcher()->set_response_code(404);
-
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(404);
}
TEST_F(PaymentMethodManifestDownloaderTest, NoHttpHeadersIsFailure) {
- fetcher()->set_response_code(200);
-
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200, std::string(), std::string(), false);
}
TEST_F(PaymentMethodManifestDownloaderTest, EmptyHttpHeaderIsFailure) {
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(std::string()));
- fetcher()->set_response_headers(headers);
- fetcher()->set_response_code(200);
-
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200);
}
TEST_F(PaymentMethodManifestDownloaderTest, EmptyHttpLinkHeaderIsFailure) {
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(std::string()));
- headers->AddHeader("Link:");
- fetcher()->set_response_headers(headers);
- fetcher()->set_response_code(200);
-
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200, "Link:");
}
TEST_F(PaymentMethodManifestDownloaderTest, NoRelInHttpLinkHeaderIsFailure) {
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(std::string()));
- headers->AddHeader("Link: <manifest.json>");
- fetcher()->set_response_headers(headers);
- fetcher()->set_response_code(200);
-
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200, "Link: <manifest.json>");
}
TEST_F(PaymentMethodManifestDownloaderTest, NoUrlInHttpLinkHeaderIsFailure) {
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(std::string()));
- headers->AddHeader("Link: rel=payment-method-manifest");
- fetcher()->set_response_headers(headers);
- fetcher()->set_response_code(200);
-
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200, "Link: rel=payment-method-manifest");
}
TEST_F(PaymentMethodManifestDownloaderTest,
NoManifestRellInHttpLinkHeaderIsFailure) {
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(std::string()));
- headers->AddHeader("Link: <manifest.json>; rel=web-app-manifest");
- fetcher()->set_response_headers(headers);
- fetcher()->set_response_code(200);
-
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200, "Link: <manifest.json>; rel=web-app-manifest");
}
TEST_F(PaymentMethodManifestDownloaderTest, HttpGetResponse404IsFailure) {
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(std::string()));
- headers->AddHeader("Link: <manifest.json>; rel=payment-method-manifest");
- fetcher()->set_response_headers(headers);
- fetcher()->set_response_code(200);
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
- fetcher()->set_response_code(404);
+ CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(404);
}
TEST_F(PaymentMethodManifestDownloaderTest, EmptyHttpGetResponseIsFailure) {
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(std::string()));
- headers->AddHeader("Link: <manifest.json>; rel=payment-method-manifest");
- fetcher()->set_response_headers(headers);
- fetcher()->set_response_code(200);
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
- fetcher()->set_response_code(200);
+ CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200, std::string(), std::string(), false);
}
TEST_F(PaymentMethodManifestDownloaderTest, NonEmptyHttpGetResponseIsSuccess) {
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(std::string()));
- headers->AddHeader("Link: <manifest.json>; rel=payment-method-manifest");
- fetcher()->set_response_headers(headers);
- fetcher()->set_response_code(200);
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
- fetcher()->SetResponseString("manifest content");
- fetcher()->set_response_code(200);
+ CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
EXPECT_CALL(*this, OnManifestDownload("manifest content"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200, std::string(), "manifest content");
}
TEST_F(PaymentMethodManifestDownloaderTest, HeaderResponseCode204IsSuccess) {
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(std::string()));
- headers->AddHeader("Link: <manifest.json>; rel=payment-method-manifest");
- fetcher()->set_response_headers(headers);
- // HTTP code 204 means "no content", which is not a problem for an HTTP HEAD
- // request.
- fetcher()->set_response_code(204);
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
- fetcher()->SetResponseString("manifest content");
- fetcher()->set_response_code(200);
+ CallComplete(204, "Link: <manifest.json>; rel=payment-method-manifest");
EXPECT_CALL(*this, OnManifestDownload("manifest content"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200, std::string(), "manifest content");
}
TEST_F(PaymentMethodManifestDownloaderTest, RelativeHttpHeaderLinkUrl) {
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(std::string()));
- headers->AddHeader("Link: <manifest.json>; rel=payment-method-manifest");
- fetcher()->set_response_headers(headers);
- fetcher()->set_response_code(200);
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
- EXPECT_EQ("https://bobpay.com/manifest.json",
- fetcher()->GetOriginalURL().spec());
+ EXPECT_EQ("https://bobpay.com/manifest.json", GetOriginalURL());
}
TEST_F(PaymentMethodManifestDownloaderTest, AbsoluteHttpsHeaderLinkUrl) {
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(std::string()));
- headers->AddHeader(
- "Link: <https://alicepay.com/manifest.json>; "
- "rel=payment-method-manifest");
- fetcher()->set_response_headers(headers);
- fetcher()->set_response_code(200);
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
-
- EXPECT_EQ("https://alicepay.com/manifest.json",
- fetcher()->GetOriginalURL().spec());
+ CallComplete(200,
+ "Link: <https://alicepay.com/manifest.json>; "
+ "rel=payment-method-manifest");
+
+ EXPECT_EQ("https://alicepay.com/manifest.json", GetOriginalURL());
}
TEST_F(PaymentMethodManifestDownloaderTest, AbsoluteHttpHeaderLinkUrl) {
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(std::string()));
- headers->AddHeader(
- "Link: <http://alicepay.com/manifest.json>; "
- "rel=payment-method-manifest");
- fetcher()->set_response_headers(headers);
- fetcher()->set_response_code(200);
-
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200,
+ "Link: <http://alicepay.com/manifest.json>; "
+ "rel=payment-method-manifest");
}
TEST_F(PaymentMethodManifestDownloaderTest, 300IsUnsupportedRedirect) {
- fetcher()->set_response_code(300);
- fetcher()->set_url(GURL("https://alicepay.com"));
-
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallRedirect(300, GURL("https://pay.bobpay.com"));
}
TEST_F(PaymentMethodManifestDownloaderTest, 301And302AreSupportedRedirects) {
- fetcher()->set_response_code(301);
- fetcher()->set_url(GURL("https://alicepay.com"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallRedirect(301, GURL("https://pay.bobpay.com"));
- EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://alicepay.com"));
+ EXPECT_EQ(GetOriginalURL(), GURL("https://pay.bobpay.com"));
- fetcher()->set_response_code(302);
- fetcher()->set_url(GURL("https://charliepay.com"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallRedirect(302, GURL("https://newpay.bobpay.com"));
- EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://charliepay.com"));
+ EXPECT_EQ(GetOriginalURL(), GURL("https://newpay.bobpay.com"));
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(std::string()));
- headers->AddHeader("Link: <manifest.json>; rel=payment-method-manifest");
- fetcher()->set_response_headers(headers);
- fetcher()->set_response_code(200);
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
- fetcher()->SetResponseString("manifest content");
- fetcher()->set_response_code(200);
+ CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
EXPECT_CALL(*this, OnManifestDownload("manifest content"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200, std::string(), "manifest content");
}
TEST_F(PaymentMethodManifestDownloaderTest, 302And303AreSupportedRedirects) {
- fetcher()->set_response_code(302);
- fetcher()->set_url(GURL("https://alicepay.com"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallRedirect(302, GURL("https://pay.bobpay.com"));
- EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://alicepay.com"));
+ EXPECT_EQ(GetOriginalURL(), GURL("https://pay.bobpay.com"));
- fetcher()->set_response_code(303);
- fetcher()->set_url(GURL("https://charliepay.com"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallRedirect(303, GURL("https://newpay.bobpay.com"));
- EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://charliepay.com"));
+ EXPECT_EQ(GetOriginalURL(), GURL("https://newpay.bobpay.com"));
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(std::string()));
- headers->AddHeader("Link: <manifest.json>; rel=payment-method-manifest");
- fetcher()->set_response_headers(headers);
- fetcher()->set_response_code(200);
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
- fetcher()->SetResponseString("manifest content");
- fetcher()->set_response_code(200);
+ CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
EXPECT_CALL(*this, OnManifestDownload("manifest content"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200, std::string(), "manifest content");
}
TEST_F(PaymentMethodManifestDownloaderTest, 304IsUnsupportedRedirect) {
- fetcher()->set_response_code(304);
- fetcher()->set_url(GURL("https://alicepay.com"));
-
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallRedirect(304, GURL("https://pay.bobpay.com"));
}
TEST_F(PaymentMethodManifestDownloaderTest, 305IsUnsupportedRedirect) {
- fetcher()->set_response_code(305);
- fetcher()->set_url(GURL("https://alicepay.com"));
-
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallRedirect(305, GURL("https://pay.bobpay.com"));
}
TEST_F(PaymentMethodManifestDownloaderTest, 307And308AreSupportedRedirects) {
- fetcher()->set_response_code(307);
- fetcher()->set_url(GURL("https://alicepay.com"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallRedirect(307, GURL("https://pay.bobpay.com"));
- EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://alicepay.com"));
+ EXPECT_EQ(GetOriginalURL(), GURL("https://pay.bobpay.com"));
- fetcher()->set_response_code(308);
- fetcher()->set_url(GURL("https://charliepay.com"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallRedirect(308, GURL("https://newpay.bobpay.com"));
- EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://charliepay.com"));
+ EXPECT_EQ(GetOriginalURL(), GURL("https://newpay.bobpay.com"));
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders(std::string()));
- headers->AddHeader("Link: <manifest.json>; rel=payment-method-manifest");
- fetcher()->set_response_headers(headers);
- fetcher()->set_response_code(200);
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
- fetcher()->SetResponseString("manifest content");
- fetcher()->set_response_code(200);
+ CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
EXPECT_CALL(*this, OnManifestDownload("manifest content"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200, std::string(), "manifest content");
}
-TEST_F(PaymentMethodManifestDownloaderTest, NoMoreThanThreeRediects) {
- fetcher()->set_response_code(301);
- fetcher()->set_url(GURL("https://alicepay.com"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+TEST_F(PaymentMethodManifestDownloaderTest, NoMoreThanThreeRedirects) {
+ CallRedirect(301, GURL("https://pay.bobpay.com"));
+
+ EXPECT_EQ(GetOriginalURL(), GURL("https://pay.bobpay.com"));
- EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://alicepay.com"));
+ CallRedirect(302, GURL("https://oldpay.bobpay.com"));
- fetcher()->set_response_code(302);
- fetcher()->set_url(GURL("https://charliepay.com"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ EXPECT_EQ(GetOriginalURL(), GURL("https://oldpay.bobpay.com"));
- EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://charliepay.com"));
+ CallRedirect(308, GURL("https://newpay.bobpay.com"));
- fetcher()->set_response_code(308);
- fetcher()->set_url(GURL("https://davepay.com"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ EXPECT_EQ(GetOriginalURL(), GURL("https://newpay.bobpay.com"));
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallRedirect(308, GURL("https://newpay.bobpay.com"));
}
TEST_F(PaymentMethodManifestDownloaderTest, InvalidRedirectUrlIsFailure) {
- fetcher()->set_response_code(308);
- fetcher()->set_url(GURL("alicepay.com"));
+ EXPECT_CALL(*this, OnManifestDownload(std::string()));
+ CallRedirect(308, GURL("pay.bobpay.com"));
+}
+
+TEST_F(PaymentMethodManifestDownloaderTest, NotAllowCrossSiteRedirects) {
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallRedirect(301, GURL("https://alicepay.com"));
}
class WebAppManifestDownloaderTest : public testing::Test {
public:
WebAppManifestDownloaderTest()
- : context_(new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get())),
- downloader_(context_) {
+ : test_url_("https://bobpay.com"),
+ shared_url_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_factory_)),
+ downloader_(shared_url_loader_factory_) {
downloader_.DownloadWebAppManifest(
- GURL("https://bobpay.com"),
+ test_url_,
base::BindOnce(&WebAppManifestDownloaderTest::OnManifestDownload,
base::Unretained(this)));
}
@@ -373,41 +289,42 @@ class WebAppManifestDownloaderTest : public testing::Test {
MOCK_METHOD1(OnManifestDownload, void(const std::string& content));
- net::TestURLFetcher* fetcher() { return factory_.GetFetcherByID(0); }
+ void CallComplete(int response_code,
+ const std::string& response_body = std::string()) {
+ scoped_refptr<net::HttpResponseHeaders> headers =
+ base::MakeRefCounted<net::HttpResponseHeaders>(std::string());
+ headers->ReplaceStatusLine(base::StringPrintf("%d", response_code));
+ downloader_.OnURLLoaderCompleteInternal(downloader_.GetLoaderForTesting(),
+ test_url_, response_body, headers,
+ net::OK);
+ }
private:
+ GURL test_url_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
- net::TestURLFetcherFactory factory_;
- scoped_refptr<net::TestURLRequestContextGetter> context_;
+ network::TestURLLoaderFactory test_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
PaymentManifestDownloader downloader_;
DISALLOW_COPY_AND_ASSIGN(WebAppManifestDownloaderTest);
};
TEST_F(WebAppManifestDownloaderTest, HttpGetResponse404IsFailure) {
- fetcher()->set_response_code(404);
-
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(404);
}
TEST_F(WebAppManifestDownloaderTest, EmptyHttpGetResponseIsFailure) {
- fetcher()->set_response_code(200);
-
EXPECT_CALL(*this, OnManifestDownload(std::string()));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200);
}
TEST_F(WebAppManifestDownloaderTest, NonEmptyHttpGetResponseIsSuccess) {
- fetcher()->SetResponseString("manifest content");
- fetcher()->set_response_code(200);
-
EXPECT_CALL(*this, OnManifestDownload("manifest content"));
- fetcher()->delegate()->OnURLFetchComplete(fetcher());
+ CallComplete(200, "manifest content");
}
-} // namespace
} // namespace payments
diff --git a/chromium/components/payments/core/payment_method_data.cc b/chromium/components/payments/core/payment_method_data.cc
index f0df76a2a01..515cc27fb18 100644
--- a/chromium/components/payments/core/payment_method_data.cc
+++ b/chromium/components/payments/core/payment_method_data.cc
@@ -47,7 +47,7 @@ PaymentMethodData::PaymentMethodData(const PaymentMethodData& other) = default;
PaymentMethodData::~PaymentMethodData() = default;
bool PaymentMethodData::operator==(const PaymentMethodData& other) const {
- return supported_methods == other.supported_methods && data == other.data &&
+ return supported_method == other.supported_method && data == other.data &&
supported_networks == other.supported_networks &&
supported_types == other.supported_types;
}
@@ -58,34 +58,14 @@ bool PaymentMethodData::operator!=(const PaymentMethodData& other) const {
bool PaymentMethodData::FromDictionaryValue(
const base::DictionaryValue& value) {
- supported_methods.clear();
supported_networks.clear();
supported_types.clear();
- // The value of supportedMethods can be an array or a string.
- const base::ListValue* supported_methods_list = nullptr;
- if (value.GetList(kSupportedMethods, &supported_methods_list)) {
- for (size_t i = 0; i < supported_methods_list->GetSize(); ++i) {
- std::string supported_method;
- if (!supported_methods_list->GetString(i, &supported_method) ||
- !base::IsStringASCII(supported_method)) {
- return false;
- }
- if (!supported_method.empty())
- supported_methods.push_back(supported_method);
- }
- } else {
- std::string supported_method;
- if (!value.GetString(kSupportedMethods, &supported_method) ||
- !base::IsStringASCII(supported_method) || supported_method.empty()) {
- return false;
- }
- supported_methods.push_back(supported_method);
- }
-
- // At least one supported method is required.
- if (supported_methods.empty())
+ // The value of supportedMethods should be a string.
+ if (!value.GetString(kSupportedMethods, &supported_method) ||
+ !base::IsStringASCII(supported_method) || supported_method.empty()) {
return false;
+ }
// Data is optional, but if a dictionary is present, save a stringified
// version and attempt to parse supportedNetworks/supportedTypes.
diff --git a/chromium/components/payments/core/payment_method_data.h b/chromium/components/payments/core/payment_method_data.h
index f0ce9237db5..1e32ec28fb2 100644
--- a/chromium/components/payments/core/payment_method_data.h
+++ b/chromium/components/payments/core/payment_method_data.h
@@ -33,9 +33,9 @@ class PaymentMethodData {
// true if the required values are present.
bool FromDictionaryValue(const base::DictionaryValue& value);
- // Payment method identifiers for payment methods that the merchant web site
+ // Payment method identifier for payment method that the merchant web site
// accepts.
- std::vector<std::string> supported_methods;
+ std::string supported_method;
// A JSON-serialized object that provides optional information that might be
// needed by the supported payment methods.
diff --git a/chromium/components/payments/core/payment_method_data_unittest.cc b/chromium/components/payments/core/payment_method_data_unittest.cc
index c2ee2b23f28..be891a43256 100644
--- a/chromium/components/payments/core/payment_method_data_unittest.cc
+++ b/chromium/components/payments/core/payment_method_data_unittest.cc
@@ -10,44 +10,10 @@
namespace payments {
// Tests the success case when populating a PaymentMethodData from a dictionary
-// when the supportedMethods is an array of strings.
-TEST(PaymentMethodData, FromDictionaryValueSuccess_SupportedMethodsArray) {
- PaymentMethodData expected;
- expected.supported_methods.push_back("visa");
- expected.supported_methods.push_back("basic-card");
- expected.data =
- "{\"supportedNetworks\":[\"mastercard\"],"
- "\"supportedTypes\":[\"debit\",\"credit\"]}";
- expected.supported_networks.push_back("mastercard");
- expected.supported_types.insert(autofill::CreditCard::CARD_TYPE_DEBIT);
- expected.supported_types.insert(autofill::CreditCard::CARD_TYPE_CREDIT);
-
- base::DictionaryValue method_data_dict;
- auto supported_methods_list = std::make_unique<base::ListValue>();
- supported_methods_list->AppendString("visa");
- supported_methods_list->AppendString("basic-card");
- method_data_dict.Set("supportedMethods", std::move(supported_methods_list));
- auto data_dict = std::make_unique<base::DictionaryValue>();
- auto supported_networks_list = std::make_unique<base::ListValue>();
- supported_networks_list->AppendString("mastercard");
- data_dict->Set("supportedNetworks", std::move(supported_networks_list));
- auto supported_types_list = std::make_unique<base::ListValue>();
- supported_types_list->AppendString("debit");
- supported_types_list->AppendString("credit");
- data_dict->Set("supportedTypes", std::move(supported_types_list));
- method_data_dict.Set("data", std::move(data_dict));
-
- PaymentMethodData actual;
- EXPECT_TRUE(actual.FromDictionaryValue(method_data_dict));
-
- EXPECT_EQ(expected, actual);
-}
-
-// Tests the success case when populating a PaymentMethodData from a dictionary
// when the supportedMethods is a string.
TEST(PaymentMethodData, FromDictionaryValueSuccess_SupportedMethodsString) {
PaymentMethodData expected;
- expected.supported_methods.push_back("basic-card");
+ expected.supported_method = "basic-card";
expected.data =
"{\"supportedNetworks\":[\"mastercard\"],"
"\"supportedTypes\":[\"debit\",\"credit\"]}";
@@ -109,16 +75,14 @@ TEST(PaymentMethodData, Equality) {
PaymentMethodData method_data2;
EXPECT_EQ(method_data1, method_data2);
- std::vector<std::string> supported_methods1;
- supported_methods1.push_back("basic-card");
- supported_methods1.push_back("http://bobpay.com");
- method_data1.supported_methods = supported_methods1;
+ method_data1.supported_method = "basic-card";
EXPECT_NE(method_data1, method_data2);
- std::vector<std::string> supported_methods2;
- supported_methods2.push_back("http://bobpay.com");
- method_data2.supported_methods = supported_methods2;
+
+ method_data2.supported_method = "http://bobpay.com";
EXPECT_NE(method_data1, method_data2);
- method_data2.supported_methods = supported_methods1;
+
+ method_data2.supported_method = "basic-card";
+ ;
EXPECT_EQ(method_data1, method_data2);
method_data1.data = "{merchantId: '123456'}";
diff --git a/chromium/components/payments/core/payment_request_data_util.cc b/chromium/components/payments/core/payment_request_data_util.cc
index 19092912716..12a624b6e1b 100644
--- a/chromium/components/payments/core/payment_request_data_util.cc
+++ b/chromium/components/payments/core/payment_request_data_util.cc
@@ -109,64 +109,60 @@ void ParseSupportedMethods(
std::set<GURL> url_payment_method_identifiers;
for (const PaymentMethodData& method_data_entry : method_data) {
- if (method_data_entry.supported_methods.empty())
+ if (method_data_entry.supported_method.empty())
return;
- out_payment_method_identifiers->insert(
- method_data_entry.supported_methods.begin(),
- method_data_entry.supported_methods.end());
-
- for (const std::string& method : method_data_entry.supported_methods) {
- if (method.empty())
- continue;
-
- const char kBasicCardMethodName[] = "basic-card";
- // If a card network is specified right in "supportedMethods", add it.
- auto card_it = remaining_card_networks.find(method);
- if (card_it != remaining_card_networks.end()) {
- out_supported_networks->push_back(method);
- // |method| removed from |remaining_card_networks| so that it is not
- // doubly added to |out_supported_networks|.
- remaining_card_networks.erase(card_it);
- } else if (method == kBasicCardMethodName) {
- // For the "basic-card" method, check "supportedNetworks".
- if (method_data_entry.supported_networks.empty()) {
- // Empty |supported_networks| means all networks are supported.
- out_supported_networks->insert(out_supported_networks->end(),
- remaining_card_networks.begin(),
- remaining_card_networks.end());
- out_basic_card_specified_networks->insert(kBasicCardNetworks.begin(),
- kBasicCardNetworks.end());
- // Clear the set so that no further networks are added to
- // |out_supported_networks|.
- remaining_card_networks.clear();
- } else {
- // The merchant has specified a few basic card supported networks. Use
- // the mapping to transform to known basic-card types.
- for (const std::string& supported_network :
- method_data_entry.supported_networks) {
- // Make sure that the network was not already added to
- // |out_supported_networks|. If it's still in
- // |remaining_card_networks| it's fair game.
- auto it = remaining_card_networks.find(supported_network);
- if (it != remaining_card_networks.end()) {
- out_supported_networks->push_back(supported_network);
- remaining_card_networks.erase(it);
- }
- if (kBasicCardNetworks.find(supported_network) !=
- kBasicCardNetworks.end()) {
- out_basic_card_specified_networks->insert(supported_network);
- }
+ out_payment_method_identifiers->insert(method_data_entry.supported_method);
+
+ const char kBasicCardMethodName[] = "basic-card";
+ // If a card network is specified right in "supportedMethods", add it.
+ auto card_it =
+ remaining_card_networks.find(method_data_entry.supported_method);
+ if (card_it != remaining_card_networks.end()) {
+ out_supported_networks->push_back(method_data_entry.supported_method);
+ // |method| removed from |remaining_card_networks| so that it is not
+ // doubly added to |out_supported_networks|.
+ remaining_card_networks.erase(card_it);
+ } else if (method_data_entry.supported_method == kBasicCardMethodName) {
+ // For the "basic-card" method, check "supportedNetworks".
+ if (method_data_entry.supported_networks.empty()) {
+ // Empty |supported_networks| means all networks are supported.
+ out_supported_networks->insert(out_supported_networks->end(),
+ remaining_card_networks.begin(),
+ remaining_card_networks.end());
+ out_basic_card_specified_networks->insert(kBasicCardNetworks.begin(),
+ kBasicCardNetworks.end());
+ // Clear the set so that no further networks are added to
+ // |out_supported_networks|.
+ remaining_card_networks.clear();
+ } else {
+ // The merchant has specified a few basic card supported networks. Use
+ // the mapping to transform to known basic-card types.
+ for (const std::string& supported_network :
+ method_data_entry.supported_networks) {
+ // Make sure that the network was not already added to
+ // |out_supported_networks|. If it's still in
+ // |remaining_card_networks| it's fair game.
+ auto it = remaining_card_networks.find(supported_network);
+ if (it != remaining_card_networks.end()) {
+ out_supported_networks->push_back(supported_network);
+ remaining_card_networks.erase(it);
+ }
+ if (kBasicCardNetworks.find(supported_network) !=
+ kBasicCardNetworks.end()) {
+ out_basic_card_specified_networks->insert(supported_network);
}
}
+ }
} else {
- // Here |method| could be a repeated deprecated supported network (e.g.,
- // "visa"), some invalid string or a URL Payment Method Identifier.
- // Capture this last category if the URL is valid. A valid URL must have
- // an https scheme and its username and password must be empty:
+ // Here |method_data_entry.supported_method| could be a deprecated
+ // supported network (e.g., "visa"), some invalid string or a URL
+ // Payment Method Identifier. Capture this last category if the URL
+ // is valid. A valid URL must have an https scheme and its username and
+ // password must be empty:
// https://www.w3.org/TR/payment-method-id/#dfn-validate-a-url-based-payment-method-identifier
// Avoid duplicate URLs.
- GURL url(method);
+ GURL url(method_data_entry.supported_method);
if (url.is_valid() && url.SchemeIs(url::kHttpsScheme) &&
!url.has_username() && !url.has_password()) {
const auto result = url_payment_method_identifiers.insert(url);
@@ -174,7 +170,6 @@ void ParseSupportedMethods(
out_url_payment_method_identifiers->push_back(url);
}
}
- }
}
}
@@ -184,9 +179,8 @@ void ParseSupportedCardTypes(
DCHECK(out_supported_card_types_set->empty());
for (const PaymentMethodData& method_data_entry : method_data) {
- // Ignore |supported_types| if |supported_methods| does not contain
- // "basic_card".
- if (!base::ContainsValue(method_data_entry.supported_methods, "basic-card"))
+ // Ignore |supported_types| if |supported_method| is not "basic-card".
+ if (method_data_entry.supported_method != "basic-card")
continue;
for (const autofill::CreditCard::CardType& card_type :
diff --git a/chromium/components/payments/core/test_payment_manifest_downloader.cc b/chromium/components/payments/core/test_payment_manifest_downloader.cc
index 422ba40939a..902b7b6597c 100644
--- a/chromium/components/payments/core/test_payment_manifest_downloader.cc
+++ b/chromium/components/payments/core/test_payment_manifest_downloader.cc
@@ -8,13 +8,14 @@
#include "base/memory/ref_counted.h"
#include "base/strings/string_util.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "url/gurl.h"
namespace payments {
TestDownloader::TestDownloader(
- const scoped_refptr<net::URLRequestContextGetter>& context)
- : PaymentManifestDownloader(context) {}
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+ : PaymentManifestDownloader(url_loader_factory) {}
TestDownloader::~TestDownloader() {}
diff --git a/chromium/components/payments/core/test_payment_manifest_downloader.h b/chromium/components/payments/core/test_payment_manifest_downloader.h
index d4bae37ff81..473402eef5d 100644
--- a/chromium/components/payments/core/test_payment_manifest_downloader.h
+++ b/chromium/components/payments/core/test_payment_manifest_downloader.h
@@ -17,9 +17,9 @@ class GURL;
template <class T>
class scoped_refptr;
-namespace net {
-class URLRequestContextGetter;
-} // namespace net
+namespace network {
+class SharedURLLoaderFactory;
+}
namespace payments {
@@ -49,7 +49,7 @@ namespace payments {
class TestDownloader : public PaymentManifestDownloader {
public:
explicit TestDownloader(
- const scoped_refptr<net::URLRequestContextGetter>& context);
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
~TestDownloader() override;
// PaymentManifestDownloader implementation.
diff --git a/chromium/components/payments/core/test_payment_request_delegate.cc b/chromium/components/payments/core/test_payment_request_delegate.cc
index 1d1654d79ab..de207ed82f3 100644
--- a/chromium/components/payments/core/test_payment_request_delegate.cc
+++ b/chromium/components/payments/core/test_payment_request_delegate.cc
@@ -13,8 +13,10 @@ TestPaymentRequestDelegate::TestPaymentRequestDelegate(
: personal_data_manager_(personal_data_manager),
locale_("en-US"),
last_committed_url_("https://shop.com"),
- request_context_(new TestURLRequestContextGetter(loop_.task_runner())),
- payments_client_(request_context_.get(),
+ test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)),
+ payments_client_(test_shared_loader_factory_,
nullptr,
nullptr,
/*unmask_delegate=*/&payments_client_delegate_,
@@ -108,19 +110,4 @@ void TestPaymentsClientDelegate::OnDidGetRealPan(
autofill::AutofillClient::PaymentsRpcResult result,
const std::string& real_pan) {}
-TestURLRequestContextGetter::TestURLRequestContextGetter(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner)
- : task_runner_(task_runner) {}
-
-TestURLRequestContextGetter::~TestURLRequestContextGetter() {}
-
-net::URLRequestContext* TestURLRequestContextGetter::GetURLRequestContext() {
- return nullptr;
-}
-
-scoped_refptr<base::SingleThreadTaskRunner>
-TestURLRequestContextGetter::GetNetworkTaskRunner() const {
- return task_runner_;
-}
-
} // namespace payments
diff --git a/chromium/components/payments/core/test_payment_request_delegate.h b/chromium/components/payments/core/test_payment_request_delegate.h
index e7bb00217e8..6c07f63d974 100644
--- a/chromium/components/payments/core/test_payment_request_delegate.h
+++ b/chromium/components/payments/core/test_payment_request_delegate.h
@@ -15,6 +15,9 @@
#include "components/autofill/core/browser/test_autofill_client.h"
#include "components/payments/core/payment_request_delegate.h"
#include "net/url_request/url_request_test_util.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_url_loader_factory.h"
namespace payments {
@@ -30,20 +33,6 @@ class TestPaymentsClientDelegate
const std::string& real_pan) override;
};
-class TestURLRequestContextGetter : public net::URLRequestContextGetter {
- public:
- explicit TestURLRequestContextGetter(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner);
- net::URLRequestContext* GetURLRequestContext() override;
- scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
- const override;
-
- private:
- ~TestURLRequestContextGetter() override;
-
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-};
-
class TestPaymentRequestDelegate : public PaymentRequestDelegate {
public:
explicit TestPaymentRequestDelegate(
@@ -82,7 +71,8 @@ class TestPaymentRequestDelegate : public PaymentRequestDelegate {
std::string locale_;
const GURL last_committed_url_;
autofill::TestAddressNormalizer address_normalizer_;
- scoped_refptr<TestURLRequestContextGetter> request_context_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
autofill::TestAutofillClient autofill_client_;
autofill::payments::PaymentsClient payments_client_;
autofill::payments::FullCardRequest full_card_request_;
diff --git a/chromium/components/payments/core/web_payment_request_unittest.cc b/chromium/components/payments/core/web_payment_request_unittest.cc
index 1267234088f..554e8950cac 100644
--- a/chromium/components/payments/core/web_payment_request_unittest.cc
+++ b/chromium/components/payments/core/web_payment_request_unittest.cc
@@ -66,9 +66,7 @@ TEST(PaymentRequestTest, ParsingFullyPopulatedRequestDictionarySucceeds) {
expected_request.details.error = "Error in details";
PaymentMethodData method_data;
- std::vector<std::string> supported_methods;
- supported_methods.push_back("Visa");
- method_data.supported_methods = supported_methods;
+ method_data.supported_method = "Visa";
expected_request.method_data.push_back(method_data);
// Add the same values to the dictionary to be parsed.
@@ -86,9 +84,7 @@ TEST(PaymentRequestTest, ParsingFullyPopulatedRequestDictionarySucceeds) {
auto method_data_list = std::make_unique<base::ListValue>();
auto method_data_dict = std::make_unique<base::DictionaryValue>();
- auto supported_methods_list = std::make_unique<base::ListValue>();
- supported_methods_list->AppendString("Visa");
- method_data_dict->Set("supportedMethods", std::move(supported_methods_list));
+ method_data_dict->SetString("supportedMethods", "Visa");
method_data_list->Append(std::move(method_data_dict));
request_dict.Set("methodData", std::move(method_data_list));
request_dict.SetString("id", "123456789");
diff --git a/chromium/components/payments_strings.grdp b/chromium/components/payments_strings.grdp
index 0af9b3b4579..12de33ee15d 100644
--- a/chromium/components/payments_strings.grdp
+++ b/chromium/components/payments_strings.grdp
@@ -634,4 +634,7 @@
<message name="IDS_PAYMENTS_ACCESSIBLE_LABEL_WITH_ERROR" desc="The format to append an error message to a screen reader string describing a profile or payment method">
<ph name="LABEL">$1<ex>Homer Simpson 123 Fake Street</ex></ph> <ph name="ERROR">$2<ex>Phone number required</ex></ph>
</message>
+ <message name="IDS_PAYMENTS_ORDER_SUMMARY_ACCESSIBLE_LABEL" desc="The string used to format what the screenreader reads out for the order summary section of the Payment Sheet.">
+ Order Summary, <ph name="TOTAL_LABEL">$1<ex>Total, USD $10.00</ex></ph>, More Details
+ </message>
</grit-part>
diff --git a/chromium/components/payments_strings_grdp/IDS_SETTINGS_CAN_MAKE_PAYMENT_TOGGLE_LABEL.png.sha1 b/chromium/components/payments_strings_grdp/IDS_SETTINGS_CAN_MAKE_PAYMENT_TOGGLE_LABEL.png.sha1
index 2253092ed26..f386c2dd5d7 100644
--- a/chromium/components/payments_strings_grdp/IDS_SETTINGS_CAN_MAKE_PAYMENT_TOGGLE_LABEL.png.sha1
+++ b/chromium/components/payments_strings_grdp/IDS_SETTINGS_CAN_MAKE_PAYMENT_TOGGLE_LABEL.png.sha1
@@ -1 +1 @@
-8cffb2c454760d101b26ff199ca21cf0032b4c16 \ No newline at end of file
+cdeb883cfd93bd1b9053e53feff125fe06bd0786 \ No newline at end of file
diff --git a/chromium/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc b/chromium/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc
index a727b4cf793..f160c16aae1 100644
--- a/chromium/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc
+++ b/chromium/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc
@@ -58,6 +58,14 @@ class FakeRendererPpapiHost : public content::RendererPpapiHost {
const base::SharedMemoryHandle& handle) override {
return base::SharedMemoryHandle();
}
+ base::UnsafeSharedMemoryRegion ShareUnsafeSharedMemoryRegionWithRemote(
+ const base::UnsafeSharedMemoryRegion& region) override {
+ return base::UnsafeSharedMemoryRegion();
+ }
+ base::ReadOnlySharedMemoryRegion ShareReadOnlySharedMemoryRegionWithRemote(
+ const base::ReadOnlySharedMemoryRegion& region) override {
+ return base::ReadOnlySharedMemoryRegion();
+ }
bool IsRunningInProcess() const override { return false; }
std::string GetPluginName() const override { return std::string(); }
void SetToExternalPluginHost() override {}
diff --git a/chromium/components/plugins/renderer/webview_plugin.cc b/chromium/components/plugins/renderer/webview_plugin.cc
index 804fdd933c9..94b96f56ae2 100644
--- a/chromium/components/plugins/renderer/webview_plugin.cc
+++ b/chromium/components/plugins/renderer/webview_plugin.cc
@@ -28,12 +28,10 @@
#include "third_party/blink/public/web/web_plugin_container.h"
#include "third_party/blink/public/web/web_view.h"
-using blink::WebCanvas;
using blink::WebCursorInfo;
using blink::WebDragData;
using blink::WebDragOperationsMask;
using blink::WebFrameWidget;
-using blink::WebImage;
using blink::WebLocalFrame;
using blink::WebMouseEvent;
using blink::WebPlugin;
@@ -154,7 +152,7 @@ bool WebViewPlugin::IsErrorPlaceholder() {
return delegate_->IsErrorPlaceholder();
}
-void WebViewPlugin::Paint(WebCanvas* canvas, const WebRect& rect) {
+void WebViewPlugin::Paint(cc::PaintCanvas* canvas, const WebRect& rect) {
gfx::Rect paint_rect = gfx::IntersectRects(rect_, rect);
if (paint_rect.IsEmpty())
return;
@@ -173,7 +171,7 @@ void WebViewPlugin::Paint(WebCanvas* canvas, const WebRect& rect) {
SkFloatToScalar(1.0 / container_->DeviceScaleFactor());
canvas->scale(inverse_scale, inverse_scale);
- web_view()->Paint(canvas, paint_rect);
+ web_view()->PaintContent(canvas, paint_rect);
canvas->restore();
}
@@ -256,9 +254,10 @@ void WebViewPlugin::DidFailLoading(const WebURLError& error) {
WebViewPlugin::WebViewHelper::WebViewHelper(WebViewPlugin* plugin,
const WebPreferences& preferences)
: plugin_(plugin) {
- web_view_ = WebView::Create(/* client = */ this,
+ web_view_ = WebView::Create(/*client=*/this,
+ /*widget_client=*/this,
blink::mojom::PageVisibilityState::kVisible,
- /* opener = */ nullptr);
+ /*opener=*/nullptr);
// ApplyWebPreferences before making a WebLocalFrame so that the frame sees a
// consistent view of our preferences.
content::RenderView::ApplyWebPreferences(preferences, web_view_);
@@ -290,6 +289,10 @@ bool WebViewPlugin::WebViewHelper::CanUpdateLayout() {
return true;
}
+blink::WebWidgetClient* WebViewPlugin::WebViewHelper::WidgetClient() {
+ return this;
+}
+
void WebViewPlugin::WebViewHelper::SetToolTipText(
const WebString& text,
blink::WebTextDirection hint) {
@@ -300,7 +303,7 @@ void WebViewPlugin::WebViewHelper::SetToolTipText(
void WebViewPlugin::WebViewHelper::StartDragging(blink::WebReferrerPolicy,
const WebDragData&,
WebDragOperationsMask,
- const WebImage&,
+ const SkBitmap&,
const WebPoint&) {
// Immediately stop dragging.
main_frame()->FrameWidget()->DragSourceSystemDragEnded();
diff --git a/chromium/components/plugins/renderer/webview_plugin.h b/chromium/components/plugins/renderer/webview_plugin.h
index d9a033b66ad..ada465ce3e7 100644
--- a/chromium/components/plugins/renderer/webview_plugin.h
+++ b/chromium/components/plugins/renderer/webview_plugin.h
@@ -15,9 +15,10 @@
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_url_response.h"
#include "third_party/blink/public/web/blink.h"
-#include "third_party/blink/public/web/web_frame_client.h"
+#include "third_party/blink/public/web/web_local_frame_client.h"
#include "third_party/blink/public/web/web_plugin.h"
#include "third_party/blink/public/web/web_view_client.h"
+#include "third_party/blink/public/web/web_widget_client.h"
namespace blink {
class WebLocalFrame;
@@ -90,7 +91,7 @@ class WebViewPlugin : public blink::WebPlugin,
bool IsErrorPlaceholder() override;
void UpdateAllLifecyclePhases() override;
- void Paint(blink::WebCanvas* canvas, const blink::WebRect& rect) override;
+ void Paint(cc::PaintCanvas* canvas, const blink::WebRect& rect) override;
// Coordinates are relative to the containing window.
void UpdateGeometry(const blink::WebRect& window_rect,
@@ -147,7 +148,8 @@ class WebViewPlugin : public blink::WebPlugin,
// A helper that handles interaction from WebViewPlugin's internal WebView.
class WebViewHelper : public blink::WebViewClient,
- public blink::WebFrameClient {
+ public blink::WebWidgetClient,
+ public blink::WebLocalFrameClient {
public:
WebViewHelper(WebViewPlugin* plugin,
const content::WebPreferences& preferences);
@@ -160,6 +162,7 @@ class WebViewPlugin : public blink::WebPlugin,
bool AcceptsLoadDrops() override;
bool CanHandleGestureEvent() override;
bool CanUpdateLayout() override;
+ blink::WebWidgetClient* WidgetClient() override;
// WebWidgetClient methods:
void SetToolTipText(const blink::WebString&,
@@ -167,7 +170,7 @@ class WebViewPlugin : public blink::WebPlugin,
void StartDragging(blink::WebReferrerPolicy,
const blink::WebDragData&,
blink::WebDragOperationsMask,
- const blink::WebImage&,
+ const SkBitmap&,
const blink::WebPoint&) override;
// TODO(ojan): Remove this override and have this class use a non-null
// layerTreeView.
@@ -178,7 +181,7 @@ class WebViewPlugin : public blink::WebPlugin,
std::unique_ptr<blink::WebURLLoaderFactory> CreateURLLoaderFactory()
override;
- // WebFrameClient methods:
+ // WebLocalFrameClient methods:
void DidClearWindowObject() override;
void FrameDetached(DetachType) override;
diff --git a/chromium/components/policy/BUILD.gn b/chromium/components/policy/BUILD.gn
index 87b81de6a59..6d776e3c22f 100644
--- a/chromium/components/policy/BUILD.gn
+++ b/chromium/components/policy/BUILD.gn
@@ -78,7 +78,6 @@ chrome_settings_full_runtime_proto_path =
constants_header_path = "$target_gen_dir/policy_constants.h"
constants_source_path = "$target_gen_dir/policy_constants.cc"
-protobuf_decoder_path = "$target_gen_dir/cloud_policy_generated.cc"
app_restrictions_path = "$target_gen_dir/app_restrictions.xml"
risk_tag_header_path = "$target_gen_dir/risk_tag.h"
@@ -100,7 +99,6 @@ action("cloud_policy_code_generate") {
outputs = [
constants_header_path,
constants_source_path,
- protobuf_decoder_path,
chrome_settings_proto_path,
cloud_policy_proto_path,
app_restrictions_path,
@@ -120,8 +118,6 @@ action("cloud_policy_code_generate") {
rebase_path(chrome_settings_proto_path, root_build_dir),
"--cloud-policy-protobuf=" +
rebase_path(cloud_policy_proto_path, root_build_dir),
- "--cloud-policy-decoder=" +
- rebase_path(protobuf_decoder_path, root_build_dir),
"--app-restrictions-definition=" +
rebase_path(app_restrictions_path, root_build_dir),
"--risk-tag-header=" + rebase_path(risk_tag_header_path, root_build_dir),
@@ -334,7 +330,6 @@ static_library("generated") {
sources = [
constants_header_path,
constants_source_path,
- protobuf_decoder_path,
risk_tag_header_path,
]
diff --git a/chromium/components/policy/core/common/BUILD.gn b/chromium/components/policy/core/common/BUILD.gn
index 922f57ef261..014ca960b78 100644
--- a/chromium/components/policy/core/common/BUILD.gn
+++ b/chromium/components/policy/core/common/BUILD.gn
@@ -69,8 +69,6 @@ source_set("internal") {
"cloud/machine_level_user_cloud_policy_metrics.h",
"cloud/machine_level_user_cloud_policy_store.cc",
"cloud/machine_level_user_cloud_policy_store.h",
- "cloud/policy_header_io_helper.cc",
- "cloud/policy_header_io_helper.h",
"cloud/policy_header_service.cc",
"cloud/policy_header_service.h",
"cloud/resource_cache.cc",
@@ -110,6 +108,8 @@ source_set("internal") {
"policy_namespace.h",
"policy_pref_names.cc",
"policy_pref_names.h",
+ "policy_proto_decoders.cc",
+ "policy_proto_decoders.h",
"policy_service.cc",
"policy_service.h",
"policy_service_impl.cc",
@@ -158,6 +158,7 @@ source_set("internal") {
"//extensions/buildflags",
"//google_apis",
"//net",
+ "//services/network/public/cpp",
"//third_party/libxml",
"//third_party/re2",
"//url",
@@ -289,6 +290,7 @@ static_library("test_support") {
"//components/policy/proto",
"//crypto",
"//net",
+ "//services/network/public/cpp",
"//testing/gmock",
"//testing/gtest",
]
@@ -305,7 +307,6 @@ source_set("unit_tests") {
"cloud/cloud_policy_service_unittest.cc",
"cloud/cloud_policy_validator_unittest.cc",
"cloud/device_management_service_unittest.cc",
- "cloud/policy_header_io_helper_unittest.cc",
"cloud/policy_header_service_unittest.cc",
"cloud/user_info_fetcher_unittest.cc",
"generate_policy_source_unittest.cc",
@@ -377,6 +378,7 @@ source_set("unit_tests") {
"//extensions/buildflags",
"//google_apis",
"//net:test_support",
+ "//services/network:test_support",
"//testing/gmock",
"//testing/gtest",
"//third_party/libxml",
diff --git a/chromium/components/policy_strings.grdp b/chromium/components/policy_strings.grdp
index 3a3d70f7d9b..7c5bf1164e6 100644
--- a/chromium/components/policy_strings.grdp
+++ b/chromium/components/policy_strings.grdp
@@ -158,6 +158,9 @@
<message name="IDS_POLICY_SCHEMA_VALIDATION_ERROR" desc="The text displayed in the status column when the policy value doesn't conform to the policy schema.">
Schema validation error at "<ph name="ERROR_PATH">$1<ex>AC.Delays.ScreenOff</ex></ph>": <ph name="ERROR">$2<ex>Value is out of range.</ex></ph>
</message>
+ <message name="IDS_POLICY_INVALID_JSON_ERROR" desc="The text displayed in the status column when the policy value should be a valid JSON string, but it cannot be parsed as JSON.">
+ Error while parsing JSON value: <ph name="ERROR">$1<ex>Line 2, Column 3: Map keys must be quoted.</ex></ph>
+ </message>
<message name="IDS_POLICY_INVALID_SEARCH_URL_ERROR" desc="The text displayed in the status column when a the URL given for DefaultSearchProviderSearchURL is invalid.">
Invalid search URL.
</message>
diff --git a/chromium/components/pref_registry/pref_registry_syncable.cc b/chromium/components/pref_registry/pref_registry_syncable.cc
index f00f0c6d3f1..f458cf7f074 100644
--- a/chromium/components/pref_registry/pref_registry_syncable.cc
+++ b/chromium/components/pref_registry/pref_registry_syncable.cc
@@ -48,4 +48,14 @@ scoped_refptr<PrefRegistrySyncable> PrefRegistrySyncable::ForkForIncognito() {
return registry;
}
+void PrefRegistrySyncable::WhitelistLateRegistrationPrefForSync(
+ const std::string& pref_name) {
+ sync_unknown_prefs_whitelist_.insert(pref_name);
+}
+
+bool PrefRegistrySyncable::IsWhitelistedLateRegistrationPref(
+ const std::string& path) const {
+ return sync_unknown_prefs_whitelist_.count(path) != 0;
+}
+
} // namespace user_prefs
diff --git a/chromium/components/pref_registry/pref_registry_syncable.h b/chromium/components/pref_registry/pref_registry_syncable.h
index 7eef955c9d5..d8e7c457b33 100644
--- a/chromium/components/pref_registry/pref_registry_syncable.h
+++ b/chromium/components/pref_registry/pref_registry_syncable.h
@@ -7,6 +7,7 @@
#include <stdint.h>
+#include <set>
#include <string>
#include "base/callback.h"
@@ -73,6 +74,16 @@ class PrefRegistrySyncable : public PrefRegistrySimple {
// store.
scoped_refptr<PrefRegistrySyncable> ForkForIncognito();
+ // Adds a the preference with name |pref_name| to the whitelist of prefs which
+ // will be synced even before they got registered. Note that it's still
+ // illegal to read or write a whitelisted preference via the PrefService
+ // before its registration.
+ void WhitelistLateRegistrationPrefForSync(const std::string& pref_name);
+
+ // Checks weather the preference with name |path| is on the whitelist of
+ // sync-supported prefs before registration.
+ bool IsWhitelistedLateRegistrationPref(const std::string& path) const;
+
private:
~PrefRegistrySyncable() override;
@@ -82,6 +93,7 @@ class PrefRegistrySyncable : public PrefRegistrySimple {
uint32_t flags) override;
SyncableRegistrationCallback callback_;
+ std::set<std::string> sync_unknown_prefs_whitelist_;
DISALLOW_COPY_AND_ASSIGN(PrefRegistrySyncable);
};
diff --git a/chromium/components/prefs/json_pref_store_unittest.cc b/chromium/components/prefs/json_pref_store_unittest.cc
index 2d63df8bec7..b084ebda7c3 100644
--- a/chromium/components/prefs/json_pref_store_unittest.cc
+++ b/chromium/components/prefs/json_pref_store_unittest.cc
@@ -22,7 +22,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_clock.h"
#include "base/threading/sequenced_task_runner_handle.h"
diff --git a/chromium/components/prefs/overlay_user_pref_store.cc b/chromium/components/prefs/overlay_user_pref_store.cc
index 618033adaec..2b45d8c6449 100644
--- a/chromium/components/prefs/overlay_user_pref_store.cc
+++ b/chromium/components/prefs/overlay_user_pref_store.cc
@@ -16,41 +16,42 @@
// binding additional arguments).
class OverlayUserPrefStore::ObserverAdapter : public PrefStore::Observer {
public:
- ObserverAdapter(bool overlay, OverlayUserPrefStore* parent)
- : overlay_(overlay), parent_(parent) {}
+ ObserverAdapter(bool ephemeral, OverlayUserPrefStore* parent)
+ : ephemeral_user_pref_store_(ephemeral), parent_(parent) {}
// Methods of PrefStore::Observer.
void OnPrefValueChanged(const std::string& key) override {
- parent_->OnPrefValueChanged(overlay_, key);
+ parent_->OnPrefValueChanged(ephemeral_user_pref_store_, key);
}
void OnInitializationCompleted(bool succeeded) override {
- parent_->OnInitializationCompleted(overlay_, succeeded);
+ parent_->OnInitializationCompleted(ephemeral_user_pref_store_, succeeded);
}
private:
- // Is the update for the overlay?
- const bool overlay_;
+ // Is the update for the ephemeral?
+ const bool ephemeral_user_pref_store_;
OverlayUserPrefStore* const parent_;
};
-OverlayUserPrefStore::OverlayUserPrefStore(PersistentPrefStore* underlay)
- : OverlayUserPrefStore(new InMemoryPrefStore(), underlay) {}
+OverlayUserPrefStore::OverlayUserPrefStore(PersistentPrefStore* persistent)
+ : OverlayUserPrefStore(new InMemoryPrefStore(), persistent) {}
-OverlayUserPrefStore::OverlayUserPrefStore(PersistentPrefStore* overlay,
- PersistentPrefStore* underlay)
- : overlay_observer_(
+OverlayUserPrefStore::OverlayUserPrefStore(PersistentPrefStore* ephemeral,
+ PersistentPrefStore* persistent)
+ : ephemeral_pref_store_observer_(
std::make_unique<OverlayUserPrefStore::ObserverAdapter>(true, this)),
- underlay_observer_(
+ persistent_pref_store_observer_(
std::make_unique<OverlayUserPrefStore::ObserverAdapter>(false, this)),
- overlay_(overlay),
- underlay_(underlay) {
- DCHECK(overlay->IsInitializationComplete());
- overlay_->AddObserver(overlay_observer_.get());
- underlay_->AddObserver(underlay_observer_.get());
+ ephemeral_user_pref_store_(ephemeral),
+ persistent_user_pref_store_(persistent) {
+ DCHECK(ephemeral->IsInitializationComplete());
+ ephemeral_user_pref_store_->AddObserver(ephemeral_pref_store_observer_.get());
+ persistent_user_pref_store_->AddObserver(
+ persistent_pref_store_observer_.get());
}
bool OverlayUserPrefStore::IsSetInOverlay(const std::string& key) const {
- return overlay_->GetValue(key, nullptr);
+ return ephemeral_user_pref_store_->GetValue(key, nullptr);
}
void OverlayUserPrefStore::AddObserver(PrefStore::Observer* observer) {
@@ -66,27 +67,33 @@ bool OverlayUserPrefStore::HasObservers() const {
}
bool OverlayUserPrefStore::IsInitializationComplete() const {
- return underlay_->IsInitializationComplete() &&
- overlay_->IsInitializationComplete();
+ return persistent_user_pref_store_->IsInitializationComplete() &&
+ ephemeral_user_pref_store_->IsInitializationComplete();
}
bool OverlayUserPrefStore::GetValue(const std::string& key,
const base::Value** result) const {
- // If the |key| shall NOT be stored in the overlay store, there must not
+ // If the |key| shall NOT be stored in the ephemeral store, there must not
// be an entry.
- DCHECK(ShallBeStoredInOverlay(key) || !overlay_->GetValue(key, nullptr));
+ DCHECK(!ShallBeStoredInPersistent(key) ||
+ !ephemeral_user_pref_store_->GetValue(key, nullptr));
- if (overlay_->GetValue(key, result))
+ if (ephemeral_user_pref_store_->GetValue(key, result))
return true;
- return underlay_->GetValue(key, result);
+ return persistent_user_pref_store_->GetValue(key, result);
}
std::unique_ptr<base::DictionaryValue> OverlayUserPrefStore::GetValues() const {
- auto values = underlay_->GetValues();
- auto overlay_values = overlay_->GetValues();
- for (const auto& key : overlay_names_set_) {
+ auto values = ephemeral_user_pref_store_->GetValues();
+ auto persistent_values = persistent_user_pref_store_->GetValues();
+
+ // Output |values| are read from |ephemeral_user_pref_store_| (in-memory
+ // store). Then the values of preferences in |persistent_names_set_| are
+ // overwritten by the content of |persistent_user_pref_store_| (the persistent
+ // store).
+ for (const auto& key : persistent_names_set_) {
std::unique_ptr<base::Value> out_value;
- overlay_values->Remove(key, &out_value);
+ persistent_values->Remove(key, &out_value);
if (out_value) {
values->Set(key, std::move(out_value));
}
@@ -96,56 +103,60 @@ std::unique_ptr<base::DictionaryValue> OverlayUserPrefStore::GetValues() const {
bool OverlayUserPrefStore::GetMutableValue(const std::string& key,
base::Value** result) {
- if (!ShallBeStoredInOverlay(key))
- return underlay_->GetMutableValue(key, result);
+ if (ShallBeStoredInPersistent(key))
+ return persistent_user_pref_store_->GetMutableValue(key, result);
- written_overlay_names_.insert(key);
- if (overlay_->GetMutableValue(key, result))
+ written_ephemeral_names_.insert(key);
+ if (ephemeral_user_pref_store_->GetMutableValue(key, result))
return true;
- // Try to create copy of underlay if the overlay does not contain a value.
- base::Value* underlay_value = nullptr;
- if (!underlay_->GetMutableValue(key, &underlay_value))
+ // Try to create copy of persistent if the ephemeral does not contain a value.
+ base::Value* persistent_value = nullptr;
+ if (!persistent_user_pref_store_->GetMutableValue(key, &persistent_value))
return false;
- *result = underlay_value->DeepCopy();
- overlay_->SetValue(key, base::WrapUnique(*result),
- WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ *result = persistent_value->DeepCopy();
+ ephemeral_user_pref_store_->SetValue(
+ key, base::WrapUnique(*result),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
return true;
}
void OverlayUserPrefStore::SetValue(const std::string& key,
std::unique_ptr<base::Value> value,
uint32_t flags) {
- if (!ShallBeStoredInOverlay(key)) {
- underlay_->SetValue(key, std::move(value), flags);
+ if (ShallBeStoredInPersistent(key)) {
+ persistent_user_pref_store_->SetValue(key, std::move(value), flags);
return;
}
- written_overlay_names_.insert(key);
- overlay_->SetValue(key, std::move(value), flags);
+ // TODO(https://crbug.com/861722): If we always store in in-memory storage
+ // and conditionally also stored in persistent one, we wouldn't have to do a
+ // complex merge in GetValues().
+ written_ephemeral_names_.insert(key);
+ ephemeral_user_pref_store_->SetValue(key, std::move(value), flags);
}
void OverlayUserPrefStore::SetValueSilently(const std::string& key,
std::unique_ptr<base::Value> value,
uint32_t flags) {
- if (!ShallBeStoredInOverlay(key)) {
- underlay_->SetValueSilently(key, std::move(value), flags);
+ if (ShallBeStoredInPersistent(key)) {
+ persistent_user_pref_store_->SetValueSilently(key, std::move(value), flags);
return;
}
- written_overlay_names_.insert(key);
- overlay_->SetValueSilently(key, std::move(value), flags);
+ written_ephemeral_names_.insert(key);
+ ephemeral_user_pref_store_->SetValueSilently(key, std::move(value), flags);
}
void OverlayUserPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
- if (!ShallBeStoredInOverlay(key)) {
- underlay_->RemoveValue(key, flags);
+ if (ShallBeStoredInPersistent(key)) {
+ persistent_user_pref_store_->RemoveValue(key, flags);
return;
}
- written_overlay_names_.insert(key);
- overlay_->RemoveValue(key, flags);
+ written_ephemeral_names_.insert(key);
+ ephemeral_user_pref_store_->RemoveValue(key, flags);
}
bool OverlayUserPrefStore::ReadOnly() const {
@@ -158,7 +169,7 @@ PersistentPrefStore::PrefReadError OverlayUserPrefStore::GetReadError() const {
PersistentPrefStore::PrefReadError OverlayUserPrefStore::ReadPrefs() {
// We do not read intentionally.
- OnInitializationCompleted(/* overlay */ false, true);
+ OnInitializationCompleted(/* ephemeral */ false, true);
return PersistentPrefStore::PREF_READ_ERROR_NONE;
}
@@ -166,16 +177,16 @@ void OverlayUserPrefStore::ReadPrefsAsync(
ReadErrorDelegate* error_delegate_raw) {
std::unique_ptr<ReadErrorDelegate> error_delegate(error_delegate_raw);
// We do not read intentionally.
- OnInitializationCompleted(/* overlay */ false, true);
+ OnInitializationCompleted(/* ephemeral */ false, true);
}
void OverlayUserPrefStore::CommitPendingWrite(base::OnceClosure done_callback) {
- underlay_->CommitPendingWrite(std::move(done_callback));
+ persistent_user_pref_store_->CommitPendingWrite(std::move(done_callback));
// We do not write our content intentionally.
}
void OverlayUserPrefStore::SchedulePendingLossyWrites() {
- underlay_->SchedulePendingLossyWrites();
+ persistent_user_pref_store_->SchedulePendingLossyWrites();
}
void OverlayUserPrefStore::ReportValueChanged(const std::string& key,
@@ -184,39 +195,42 @@ void OverlayUserPrefStore::ReportValueChanged(const std::string& key,
observer.OnPrefValueChanged(key);
}
-void OverlayUserPrefStore::RegisterOverlayPref(const std::string& key) {
+void OverlayUserPrefStore::RegisterPersistentPref(const std::string& key) {
DCHECK(!key.empty()) << "Key is empty";
- DCHECK(overlay_names_set_.find(key) == overlay_names_set_.end())
- << "Key already registered";
- overlay_names_set_.insert(key);
+ DCHECK(persistent_names_set_.find(key) == persistent_names_set_.end())
+ << "Key already registered: " << key;
+ persistent_names_set_.insert(key);
}
void OverlayUserPrefStore::ClearMutableValues() {
- for (const auto& key : written_overlay_names_) {
- overlay_->RemoveValue(key, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ for (const auto& key : written_ephemeral_names_) {
+ ephemeral_user_pref_store_->RemoveValue(
+ key, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
}
}
void OverlayUserPrefStore::OnStoreDeletionFromDisk() {
- underlay_->OnStoreDeletionFromDisk();
+ persistent_user_pref_store_->OnStoreDeletionFromDisk();
}
OverlayUserPrefStore::~OverlayUserPrefStore() {
- overlay_->RemoveObserver(overlay_observer_.get());
- underlay_->RemoveObserver(underlay_observer_.get());
+ ephemeral_user_pref_store_->RemoveObserver(
+ ephemeral_pref_store_observer_.get());
+ persistent_user_pref_store_->RemoveObserver(
+ persistent_pref_store_observer_.get());
}
-void OverlayUserPrefStore::OnPrefValueChanged(bool overlay,
+void OverlayUserPrefStore::OnPrefValueChanged(bool ephemeral,
const std::string& key) {
- if (overlay) {
+ if (ephemeral) {
ReportValueChanged(key, DEFAULT_PREF_WRITE_FLAGS);
} else {
- if (!overlay_->GetValue(key, nullptr))
+ if (!ephemeral_user_pref_store_->GetValue(key, nullptr))
ReportValueChanged(key, DEFAULT_PREF_WRITE_FLAGS);
}
}
-void OverlayUserPrefStore::OnInitializationCompleted(bool overlay,
+void OverlayUserPrefStore::OnInitializationCompleted(bool ephemeral,
bool succeeded) {
if (!IsInitializationComplete())
return;
@@ -224,7 +238,7 @@ void OverlayUserPrefStore::OnInitializationCompleted(bool overlay,
observer.OnInitializationCompleted(succeeded);
}
-bool OverlayUserPrefStore::ShallBeStoredInOverlay(
+bool OverlayUserPrefStore::ShallBeStoredInPersistent(
const std::string& key) const {
- return overlay_names_set_.find(key) != overlay_names_set_.end();
+ return persistent_names_set_.find(key) != persistent_names_set_.end();
}
diff --git a/chromium/components/prefs/overlay_user_pref_store.h b/chromium/components/prefs/overlay_user_pref_store.h
index 77fcd9b186b..0817aedded2 100644
--- a/chromium/components/prefs/overlay_user_pref_store.h
+++ b/chromium/components/prefs/overlay_user_pref_store.h
@@ -20,18 +20,19 @@
// PersistentPrefStore that directs all write operations into an in-memory
// PrefValueMap. Read operations are first answered by the PrefValueMap.
// If the PrefValueMap does not contain a value for the requested key,
-// the look-up is passed on to an underlying PersistentPrefStore |underlay_|.
+// the look-up is passed on to an underlying PersistentPrefStore
+// |persistent_user_pref_store_|.
class COMPONENTS_PREFS_EXPORT OverlayUserPrefStore
: public PersistentPrefStore {
public:
- explicit OverlayUserPrefStore(PersistentPrefStore* underlay);
- // The |overlay| must already be initialized.
- OverlayUserPrefStore(PersistentPrefStore* overlay,
- PersistentPrefStore* underlay);
+ explicit OverlayUserPrefStore(PersistentPrefStore* persistent);
+ // The |ephemeral| store must already be initialized.
+ OverlayUserPrefStore(PersistentPrefStore* ephemeral,
+ PersistentPrefStore* persistent);
// Returns true if a value has been set for the |key| in this
// OverlayUserPrefStore, i.e. if it potentially overrides a value
- // from the |underlay_|.
+ // from the |persistent_user_pref_store_|.
virtual bool IsSetInOverlay(const std::string& key) const;
// Methods of PrefStore.
@@ -60,7 +61,9 @@ class COMPONENTS_PREFS_EXPORT OverlayUserPrefStore
void SchedulePendingLossyWrites() override;
void ReportValueChanged(const std::string& key, uint32_t flags) override;
- void RegisterOverlayPref(const std::string& key);
+ // Registers preferences that should be stored in the persistent preferences
+ // (|persistent_user_pref_store_|).
+ void RegisterPersistentPref(const std::string& key);
void ClearMutableValues() override;
void OnStoreDeletionFromDisk() override;
@@ -72,20 +75,20 @@ class COMPONENTS_PREFS_EXPORT OverlayUserPrefStore
typedef std::set<std::string> NamesSet;
class ObserverAdapter;
- void OnPrefValueChanged(bool overlay, const std::string& key);
- void OnInitializationCompleted(bool overlay, bool succeeded);
+ void OnPrefValueChanged(bool ephemeral, const std::string& key);
+ void OnInitializationCompleted(bool ephemeral, bool succeeded);
// Returns true if |key| corresponds to a preference that shall be stored in
- // an in-memory PrefStore that is not persisted to disk.
- bool ShallBeStoredInOverlay(const std::string& key) const;
+ // persistent PrefStore.
+ bool ShallBeStoredInPersistent(const std::string& key) const;
base::ObserverList<PrefStore::Observer, true> observers_;
- std::unique_ptr<ObserverAdapter> overlay_observer_;
- std::unique_ptr<ObserverAdapter> underlay_observer_;
- scoped_refptr<PersistentPrefStore> overlay_;
- scoped_refptr<PersistentPrefStore> underlay_;
- NamesSet overlay_names_set_;
- NamesSet written_overlay_names_;
+ std::unique_ptr<ObserverAdapter> ephemeral_pref_store_observer_;
+ std::unique_ptr<ObserverAdapter> persistent_pref_store_observer_;
+ scoped_refptr<PersistentPrefStore> ephemeral_user_pref_store_;
+ scoped_refptr<PersistentPrefStore> persistent_user_pref_store_;
+ NamesSet persistent_names_set_;
+ NamesSet written_ephemeral_names_;
DISALLOW_COPY_AND_ASSIGN(OverlayUserPrefStore);
};
diff --git a/chromium/components/prefs/overlay_user_pref_store_unittest.cc b/chromium/components/prefs/overlay_user_pref_store_unittest.cc
index 528b9ab27c0..5f0d7258198 100644
--- a/chromium/components/prefs/overlay_user_pref_store_unittest.cc
+++ b/chromium/components/prefs/overlay_user_pref_store_unittest.cc
@@ -23,8 +23,8 @@ const char kBrowserWindowPlacement[] = "browser.window_placement";
const char kShowBookmarkBar[] = "bookmark_bar.show_on_all_tabs";
const char kSharedKey[] = "sync_promo.show_on_first_run_allowed";
-const char* const overlay_key = kBrowserWindowPlacement;
-const char* const regular_key = kShowBookmarkBar;
+const char* const regular_key = kBrowserWindowPlacement;
+const char* const persistent_key = kShowBookmarkBar;
const char* const shared_key = kSharedKey;
} // namespace
@@ -34,8 +34,8 @@ class OverlayUserPrefStoreTest : public testing::Test {
OverlayUserPrefStoreTest()
: underlay_(new TestingPrefStore()),
overlay_(new OverlayUserPrefStore(underlay_.get())) {
- overlay_->RegisterOverlayPref(overlay_key);
- overlay_->RegisterOverlayPref(shared_key);
+ overlay_->RegisterPersistentPref(persistent_key);
+ overlay_->RegisterPersistentPref(shared_key);
}
~OverlayUserPrefStoreTest() override {}
@@ -50,104 +50,104 @@ TEST_F(OverlayUserPrefStoreTest, Observer) {
overlay_->AddObserver(&obs);
// Check that underlay first value is reported.
- underlay_->SetValue(overlay_key, std::make_unique<Value>(42),
+ underlay_->SetValue(regular_key, std::make_unique<Value>(42),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- obs.VerifyAndResetChangedKey(overlay_key);
+ obs.VerifyAndResetChangedKey(regular_key);
// Check that underlay overwriting is reported.
- underlay_->SetValue(overlay_key, std::make_unique<Value>(43),
+ underlay_->SetValue(regular_key, std::make_unique<Value>(43),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- obs.VerifyAndResetChangedKey(overlay_key);
+ obs.VerifyAndResetChangedKey(regular_key);
// Check that overwriting change in overlay is reported.
- overlay_->SetValue(overlay_key, std::make_unique<Value>(44),
+ overlay_->SetValue(regular_key, std::make_unique<Value>(44),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- obs.VerifyAndResetChangedKey(overlay_key);
+ obs.VerifyAndResetChangedKey(regular_key);
// Check that hidden underlay change is not reported.
- underlay_->SetValue(overlay_key, std::make_unique<Value>(45),
+ underlay_->SetValue(regular_key, std::make_unique<Value>(45),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
// Check that overlay remove is reported.
- overlay_->RemoveValue(overlay_key,
+ overlay_->RemoveValue(regular_key,
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- obs.VerifyAndResetChangedKey(overlay_key);
+ obs.VerifyAndResetChangedKey(regular_key);
// Check that underlay remove is reported.
- underlay_->RemoveValue(overlay_key,
+ underlay_->RemoveValue(regular_key,
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- obs.VerifyAndResetChangedKey(overlay_key);
+ obs.VerifyAndResetChangedKey(regular_key);
// Check respecting of silence.
- overlay_->SetValueSilently(overlay_key, std::make_unique<Value>(46),
+ overlay_->SetValueSilently(regular_key, std::make_unique<Value>(46),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
overlay_->RemoveObserver(&obs);
// Check successful unsubscription.
- underlay_->SetValue(overlay_key, std::make_unique<Value>(47),
+ underlay_->SetValue(regular_key, std::make_unique<Value>(47),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- overlay_->SetValue(overlay_key, std::make_unique<Value>(48),
+ overlay_->SetValue(regular_key, std::make_unique<Value>(48),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
}
TEST_F(OverlayUserPrefStoreTest, GetAndSet) {
const Value* value = nullptr;
- EXPECT_FALSE(overlay_->GetValue(overlay_key, &value));
- EXPECT_FALSE(underlay_->GetValue(overlay_key, &value));
+ EXPECT_FALSE(overlay_->GetValue(regular_key, &value));
+ EXPECT_FALSE(underlay_->GetValue(regular_key, &value));
- underlay_->SetValue(overlay_key, std::make_unique<Value>(42),
+ underlay_->SetValue(regular_key, std::make_unique<Value>(42),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
// Value shines through:
- EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
EXPECT_TRUE(base::Value(42).Equals(value));
- EXPECT_TRUE(underlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(underlay_->GetValue(regular_key, &value));
EXPECT_TRUE(base::Value(42).Equals(value));
- overlay_->SetValue(overlay_key, std::make_unique<Value>(43),
+ overlay_->SetValue(regular_key, std::make_unique<Value>(43),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
EXPECT_TRUE(base::Value(43).Equals(value));
- EXPECT_TRUE(underlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(underlay_->GetValue(regular_key, &value));
EXPECT_TRUE(base::Value(42).Equals(value));
- overlay_->RemoveValue(overlay_key,
+ overlay_->RemoveValue(regular_key,
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
// Value shines through:
- EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
EXPECT_TRUE(base::Value(42).Equals(value));
- EXPECT_TRUE(underlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(underlay_->GetValue(regular_key, &value));
EXPECT_TRUE(base::Value(42).Equals(value));
}
// Check that GetMutableValue does not return the dictionary of the underlay.
TEST_F(OverlayUserPrefStoreTest, ModifyDictionaries) {
- underlay_->SetValue(overlay_key, std::make_unique<DictionaryValue>(),
+ underlay_->SetValue(regular_key, std::make_unique<DictionaryValue>(),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
Value* modify = nullptr;
- EXPECT_TRUE(overlay_->GetMutableValue(overlay_key, &modify));
+ EXPECT_TRUE(overlay_->GetMutableValue(regular_key, &modify));
ASSERT_TRUE(modify);
ASSERT_TRUE(modify->is_dict());
- static_cast<DictionaryValue*>(modify)->SetInteger(overlay_key, 42);
+ static_cast<DictionaryValue*>(modify)->SetInteger(regular_key, 42);
Value* original_in_underlay = nullptr;
- EXPECT_TRUE(underlay_->GetMutableValue(overlay_key, &original_in_underlay));
+ EXPECT_TRUE(underlay_->GetMutableValue(regular_key, &original_in_underlay));
ASSERT_TRUE(original_in_underlay);
ASSERT_TRUE(original_in_underlay->is_dict());
EXPECT_TRUE(static_cast<DictionaryValue*>(original_in_underlay)->empty());
Value* modified = nullptr;
- EXPECT_TRUE(overlay_->GetMutableValue(overlay_key, &modified));
+ EXPECT_TRUE(overlay_->GetMutableValue(regular_key, &modified));
ASSERT_TRUE(modified);
ASSERT_TRUE(modified->is_dict());
EXPECT_EQ(*modify, *modified);
@@ -161,50 +161,50 @@ TEST_F(OverlayUserPrefStoreTest, GlobalPref) {
const Value* value = nullptr;
// Check that underlay first value is reported.
- underlay_->SetValue(regular_key, std::make_unique<Value>(42),
+ underlay_->SetValue(persistent_key, std::make_unique<Value>(42),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- obs.VerifyAndResetChangedKey(regular_key);
+ obs.VerifyAndResetChangedKey(persistent_key);
// Check that underlay overwriting is reported.
- underlay_->SetValue(regular_key, std::make_unique<Value>(43),
+ underlay_->SetValue(persistent_key, std::make_unique<Value>(43),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- obs.VerifyAndResetChangedKey(regular_key);
+ obs.VerifyAndResetChangedKey(persistent_key);
// Check that we get this value from the overlay
- EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
+ EXPECT_TRUE(overlay_->GetValue(persistent_key, &value));
EXPECT_TRUE(base::Value(43).Equals(value));
// Check that overwriting change in overlay is reported.
- overlay_->SetValue(regular_key, std::make_unique<Value>(44),
+ overlay_->SetValue(persistent_key, std::make_unique<Value>(44),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- obs.VerifyAndResetChangedKey(regular_key);
+ obs.VerifyAndResetChangedKey(persistent_key);
// Check that we get this value from the overlay and the underlay.
- EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
+ EXPECT_TRUE(overlay_->GetValue(persistent_key, &value));
EXPECT_TRUE(base::Value(44).Equals(value));
- EXPECT_TRUE(underlay_->GetValue(regular_key, &value));
+ EXPECT_TRUE(underlay_->GetValue(persistent_key, &value));
EXPECT_TRUE(base::Value(44).Equals(value));
// Check that overlay remove is reported.
- overlay_->RemoveValue(regular_key,
+ overlay_->RemoveValue(persistent_key,
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- obs.VerifyAndResetChangedKey(regular_key);
+ obs.VerifyAndResetChangedKey(persistent_key);
// Check that value was removed from overlay and underlay
- EXPECT_FALSE(overlay_->GetValue(regular_key, &value));
- EXPECT_FALSE(underlay_->GetValue(regular_key, &value));
+ EXPECT_FALSE(overlay_->GetValue(persistent_key, &value));
+ EXPECT_FALSE(underlay_->GetValue(persistent_key, &value));
// Check respecting of silence.
- overlay_->SetValueSilently(regular_key, std::make_unique<Value>(46),
+ overlay_->SetValueSilently(persistent_key, std::make_unique<Value>(46),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
overlay_->RemoveObserver(&obs);
// Check successful unsubscription.
- underlay_->SetValue(regular_key, std::make_unique<Value>(47),
+ underlay_->SetValue(persistent_key, std::make_unique<Value>(47),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- overlay_->SetValue(regular_key, std::make_unique<Value>(48),
+ overlay_->SetValue(persistent_key, std::make_unique<Value>(48),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
}
@@ -212,47 +212,47 @@ TEST_F(OverlayUserPrefStoreTest, GlobalPref) {
// Check that mutable values are removed correctly.
TEST_F(OverlayUserPrefStoreTest, ClearMutableValues) {
// Set in overlay and underlay the same preference.
- underlay_->SetValue(overlay_key, std::make_unique<Value>(42),
+ underlay_->SetValue(regular_key, std::make_unique<Value>(42),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- overlay_->SetValue(overlay_key, std::make_unique<Value>(43),
+ overlay_->SetValue(regular_key, std::make_unique<Value>(43),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
const Value* value = nullptr;
// Check that an overlay preference is returned.
- EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
EXPECT_TRUE(base::Value(43).Equals(value));
overlay_->ClearMutableValues();
// Check that an underlay preference is returned.
- EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
EXPECT_TRUE(base::Value(42).Equals(value));
}
// Check that mutable values are removed correctly when using a silent set.
TEST_F(OverlayUserPrefStoreTest, ClearMutableValues_Silently) {
// Set in overlay and underlay the same preference.
- underlay_->SetValueSilently(overlay_key, std::make_unique<Value>(42),
+ underlay_->SetValueSilently(regular_key, std::make_unique<Value>(42),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- overlay_->SetValueSilently(overlay_key, std::make_unique<Value>(43),
+ overlay_->SetValueSilently(regular_key, std::make_unique<Value>(43),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
const Value* value = nullptr;
// Check that an overlay preference is returned.
- EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
EXPECT_TRUE(base::Value(43).Equals(value));
overlay_->ClearMutableValues();
// Check that an underlay preference is returned.
- EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
EXPECT_TRUE(base::Value(42).Equals(value));
}
TEST_F(OverlayUserPrefStoreTest, GetValues) {
// To check merge behavior, create underlay and overlay so each has a key the
// other doesn't have and they have one key in common.
- underlay_->SetValue(regular_key, std::make_unique<Value>(42),
+ underlay_->SetValue(persistent_key, std::make_unique<Value>(42),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- overlay_->SetValue(overlay_key, std::make_unique<Value>(43),
+ overlay_->SetValue(regular_key, std::make_unique<Value>(43),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
underlay_->SetValue(shared_key, std::make_unique<Value>(42),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
@@ -262,12 +262,12 @@ TEST_F(OverlayUserPrefStoreTest, GetValues) {
auto values = overlay_->GetValues();
const Value* value = nullptr;
// Check that an overlay preference is returned.
- ASSERT_TRUE(values->Get(overlay_key, &value));
- EXPECT_TRUE(base::Value(43).Equals(value));
+ ASSERT_TRUE(values->Get(persistent_key, &value));
+ EXPECT_TRUE(base::Value(42).Equals(value));
// Check that an underlay preference is returned.
ASSERT_TRUE(values->Get(regular_key, &value));
- EXPECT_TRUE(base::Value(42).Equals(value));
+ EXPECT_TRUE(base::Value(43).Equals(value));
// Check that the overlay is preferred.
ASSERT_TRUE(values->Get(shared_key, &value));
diff --git a/chromium/components/prefs/pref_registry_simple.cc b/chromium/components/prefs/pref_registry_simple.cc
index 670a4b44df7..fdf1feeb1a9 100644
--- a/chromium/components/prefs/pref_registry_simple.cc
+++ b/chromium/components/prefs/pref_registry_simple.cc
@@ -91,3 +91,9 @@ void PrefRegistrySimple::RegisterTimePref(const std::string& path,
RegisterInt64Pref(
path, default_value.ToDeltaSinceWindowsEpoch().InMicroseconds(), flags);
}
+
+void PrefRegistrySimple::RegisterTimeDeltaPref(const std::string& path,
+ base::TimeDelta default_value,
+ uint32_t flags) {
+ RegisterInt64Pref(path, default_value.InMicroseconds(), flags);
+}
diff --git a/chromium/components/prefs/pref_registry_simple.h b/chromium/components/prefs/pref_registry_simple.h
index 5e01cc4ecc1..e674b16bdd3 100644
--- a/chromium/components/prefs/pref_registry_simple.h
+++ b/chromium/components/prefs/pref_registry_simple.h
@@ -75,6 +75,10 @@ class COMPONENTS_PREFS_EXPORT PrefRegistrySimple : public PrefRegistry {
base::Time default_value,
uint32_t flags = NO_REGISTRATION_FLAGS);
+ void RegisterTimeDeltaPref(const std::string& path,
+ base::TimeDelta default_value,
+ uint32_t flags = NO_REGISTRATION_FLAGS);
+
protected:
~PrefRegistrySimple() override;
diff --git a/chromium/components/prefs/pref_service.cc b/chromium/components/prefs/pref_service.cc
index 7254d0b582e..6e7ae1e1861 100644
--- a/chromium/components/prefs/pref_service.cc
+++ b/chromium/components/prefs/pref_service.cc
@@ -68,9 +68,9 @@ PrefService::PrefService(
bool async)
: pref_notifier_(std::move(pref_notifier)),
pref_value_store_(std::move(pref_value_store)),
- pref_registry_(std::move(pref_registry)),
user_pref_store_(std::move(user_prefs)),
- read_error_callback_(std::move(read_error_callback)) {
+ read_error_callback_(std::move(read_error_callback)),
+ pref_registry_(std::move(pref_registry)) {
pref_notifier_->SetPrefService(this);
DCHECK(pref_registry_);
@@ -480,6 +480,14 @@ base::Time PrefService::GetTime(const std::string& path) const {
base::TimeDelta::FromMicroseconds(GetInt64(path)));
}
+void PrefService::SetTimeDelta(const std::string& path, base::TimeDelta value) {
+ SetInt64(path, value.InMicroseconds());
+}
+
+base::TimeDelta PrefService::GetTimeDelta(const std::string& path) const {
+ return base::TimeDelta::FromMicroseconds(GetInt64(path));
+}
+
base::Value* PrefService::GetMutableUserPref(const std::string& path,
base::Value::Type type) {
CHECK(type == base::Value::Type::DICTIONARY ||
diff --git a/chromium/components/prefs/pref_service.h b/chromium/components/prefs/pref_service.h
index 39525525f81..8ced3c9ecb8 100644
--- a/chromium/components/prefs/pref_service.h
+++ b/chromium/components/prefs/pref_service.h
@@ -16,6 +16,7 @@
#include <memory>
#include <set>
#include <string>
+#include <vector>
#include "base/callback.h"
#include "base/compiler_specific.h"
@@ -106,8 +107,9 @@ class COMPONENTS_PREFS_EXPORT PrefService {
// policy, this is the controlling pref if set.
bool IsManagedByCustodian() const;
- // Returns true if the Preference is recommended, i.e. set by an admin
- // policy but the user is allowed to change it.
+ // Returns true if the Preference's current value is one recommended by
+ // admin policy. Note that this will be false if any other higher-priority
+ // source overrides the value (e.g., the user has set a value).
bool IsRecommended() const;
// Returns true if the Preference has a value set by an extension, even if
@@ -246,11 +248,14 @@ class COMPONENTS_PREFS_EXPORT PrefService {
uint64_t GetUint64(const std::string& path) const;
// Time helper methods that actually store the given value as a string, which
- // represents the number of microseconds elapsed since the Windows epoch. Note
- // that if obtaining the named value via GetDictionary or GetList, the Value
- // type will be Type::STRING.
+ // represents the number of microseconds elapsed (absolute for TimeDelta and
+ // relative to Windows epoch for Time variants). Note that if obtaining the
+ // named value via GetDictionary or GetList, the Value type will be
+ // Type::STRING.
void SetTime(const std::string& path, base::Time value);
base::Time GetTime(const std::string& path) const;
+ void SetTimeDelta(const std::string& path, base::TimeDelta value);
+ base::TimeDelta GetTimeDelta(const std::string& path) const;
// Returns the value of the given preference, from the user pref store. If
// the preference is not set in the user pref store, returns NULL.
@@ -354,8 +359,6 @@ class COMPONENTS_PREFS_EXPORT PrefService {
// this PrefService. Subclasses may access it for unit testing.
const std::unique_ptr<PrefValueStore> pref_value_store_;
- const scoped_refptr<PrefRegistry> pref_registry_;
-
// Pref Stores and profile that we passed to the PrefValueStore.
const scoped_refptr<PersistentPrefStore> user_pref_store_;
@@ -431,6 +434,8 @@ class COMPONENTS_PREFS_EXPORT PrefService {
// actually get the value.).
const base::Value* GetPreferenceValue(const std::string& path) const;
+ const scoped_refptr<PrefRegistry> pref_registry_;
+
// Local cache of registered Preference objects. The pref_registry_
// is authoritative with respect to what the types and default values
// of registered preferences are.
diff --git a/chromium/components/prefs/pref_service_unittest.cc b/chromium/components/prefs/pref_service_unittest.cc
index c1ee360d6a6..317b51dd28b 100644
--- a/chromium/components/prefs/pref_service_unittest.cc
+++ b/chromium/components/prefs/pref_service_unittest.cc
@@ -259,6 +259,35 @@ TEST(PrefServiceTest, SetTimeValue_NullTime) {
EXPECT_TRUE(prefs.GetTime(kPrefName).is_null());
}
+TEST(PrefServiceTest, SetTimeDeltaValue_RegularTimeDelta) {
+ TestingPrefServiceSimple prefs;
+
+ // Register a zero time delta as the default.
+ prefs.registry()->RegisterTimeDeltaPref(kPrefName, base::TimeDelta());
+ EXPECT_TRUE(prefs.GetTimeDelta(kPrefName).is_zero());
+
+ // Set a time delta and make sure that we can read it without any loss of
+ // precision.
+ const base::TimeDelta delta = base::Time::Now() - base::Time();
+ prefs.SetTimeDelta(kPrefName, delta);
+ EXPECT_EQ(delta, prefs.GetTimeDelta(kPrefName));
+}
+
+TEST(PrefServiceTest, SetTimeDeltaValue_ZeroTimeDelta) {
+ TestingPrefServiceSimple prefs;
+
+ // Register a non-zero time delta as the default.
+ const base::TimeDelta default_delta =
+ base::TimeDelta::FromMicroseconds(12345);
+ prefs.registry()->RegisterTimeDeltaPref(kPrefName, default_delta);
+ EXPECT_FALSE(prefs.GetTimeDelta(kPrefName).is_zero());
+
+ // Set a zero time delta and make sure that it remains zero upon
+ // deserialization.
+ prefs.SetTimeDelta(kPrefName, base::TimeDelta());
+ EXPECT_TRUE(prefs.GetTimeDelta(kPrefName).is_zero());
+}
+
// A PrefStore which just stores the last write flags that were used to write
// values to it.
class WriteFlagChecker : public TestingPrefStore {
diff --git a/chromium/components/previews/content/BUILD.gn b/chromium/components/previews/content/BUILD.gn
index d4d6ca45dce..4eeea643872 100644
--- a/chromium/components/previews/content/BUILD.gn
+++ b/chromium/components/previews/content/BUILD.gn
@@ -4,10 +4,14 @@
static_library("content") {
sources = [
+ "activation_list.cc",
+ "activation_list.h",
"previews_content_util.cc",
"previews_content_util.h",
- "previews_io_data.cc",
- "previews_io_data.h",
+ "previews_decider_impl.cc",
+ "previews_decider_impl.h",
+ "previews_hints.cc",
+ "previews_hints.h",
"previews_optimization_guide.cc",
"previews_optimization_guide.h",
"previews_ui_service.cc",
@@ -16,6 +20,7 @@ static_library("content") {
deps = [
"//base",
+ "//components/blacklist/opt_out_blacklist",
"//components/optimization_guide",
"//components/optimization_guide/proto:optimization_guide_proto",
"//components/previews/core",
@@ -29,8 +34,9 @@ static_library("content") {
source_set("unit_tests") {
testonly = true
sources = [
+ "activation_list_unittest.cc",
"previews_content_util_unittest.cc",
- "previews_io_data_unittest.cc",
+ "previews_decider_impl_unittest.cc",
"previews_optimization_guide_unittest.cc",
"previews_ui_service_unittest.cc",
]
@@ -38,6 +44,7 @@ source_set("unit_tests") {
deps = [
":content",
"//base",
+ "//components/blacklist/opt_out_blacklist",
"//components/optimization_guide",
"//components/optimization_guide/proto:optimization_guide_proto",
"//components/previews/core",
diff --git a/chromium/components/previews/content/DEPS b/chromium/components/previews/content/DEPS
index 9371e4d3b17..3ecca97f938 100644
--- a/chromium/components/previews/content/DEPS
+++ b/chromium/components/previews/content/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+components/blacklist/opt_out_blacklist",
"+components/optimization_guide",
"+components/url_matcher",
"+components/variations",
diff --git a/chromium/components/previews/content/activation_list.cc b/chromium/components/previews/content/activation_list.cc
new file mode 100644
index 00000000000..be70f1d47fb
--- /dev/null
+++ b/chromium/components/previews/content/activation_list.cc
@@ -0,0 +1,59 @@
+// 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/previews/content/activation_list.h"
+
+#include <set>
+
+#include "base/metrics/histogram_macros.h"
+#include "url/gurl.h"
+
+namespace previews {
+
+ActivationList::ActivationList(const std::vector<std::string>& url_suffixes) {
+ url_matcher_ = std::make_unique<url_matcher::URLMatcher>();
+
+ url_matcher::URLMatcherConditionSet::Vector all_conditions;
+ url_matcher::URLMatcherConditionFactory* condition_factory =
+ url_matcher_->condition_factory();
+
+ int id = 0;
+
+ for (const auto& url_suffix : url_suffixes) {
+ DCHECK(!url_suffix.empty());
+ DCHECK_EQ(std::string::npos, url_suffix.find("/"));
+
+ url_matcher::URLMatcherCondition condition =
+ condition_factory->CreateHostSuffixCondition(url_suffix);
+
+ all_conditions.push_back(new url_matcher::URLMatcherConditionSet(
+ id, std::set<url_matcher::URLMatcherCondition>{condition}));
+ ++id;
+ }
+ url_matcher_->AddConditionSets(all_conditions);
+
+ DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+ActivationList::~ActivationList() {}
+
+bool ActivationList::IsHostWhitelistedAtNavigation(const GURL& url,
+ PreviewsType type) const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_EQ(PreviewsType::RESOURCE_LOADING_HINTS, type);
+ DCHECK(url.is_valid());
+ DCHECK(url.SchemeIsHTTPOrHTTPS());
+
+ if (!url_matcher_)
+ return false;
+
+ const std::set<url_matcher::URLMatcherConditionSet::ID>& matches =
+ url_matcher_->MatchURL(url);
+
+ // Only consider the first match in iteration order as it takes precedence
+ // if there are multiple matches.
+ return (matches.begin() != matches.end());
+}
+
+} // namespace previews \ No newline at end of file
diff --git a/chromium/components/previews/content/activation_list.h b/chromium/components/previews/content/activation_list.h
new file mode 100644
index 00000000000..532dd15629e
--- /dev/null
+++ b/chromium/components/previews/content/activation_list.h
@@ -0,0 +1,50 @@
+// 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_PREVIEWS_CONTENT_ACTIVATION_LIST_H_
+#define COMPONENTS_PREVIEWS_CONTENT_ACTIVATION_LIST_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+#include "components/optimization_guide/proto/hints.pb.h"
+#include "components/previews/core/previews_experiments.h"
+#include "components/previews/core/previews_user_data.h"
+#include "components/url_matcher/url_matcher.h"
+
+class GURL;
+
+namespace previews {
+
+// Holds the activation list of hosts extracted from the server hints
+// configuration.
+class ActivationList {
+ public:
+ // Adds |url_suffixes| to |url_matcher_| for resource loading hints
+ // optimization.
+ explicit ActivationList(const std::vector<std::string>& url_suffixes);
+ ~ActivationList();
+
+ // Whether |url| is whitelisted for previews type |type|. The method may
+ // make the decision based only on a partial comparison (e.g., only the
+ // hostname of |url|). As such, the method may return true even if |type|
+ // can't be used for the previews.
+ bool IsHostWhitelistedAtNavigation(const GURL& url, PreviewsType type) const;
+
+ private:
+ // The URLMatcher used to match whether a URL has any previews whitelisted
+ // with it.
+ std::unique_ptr<url_matcher::URLMatcher> url_matcher_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(ActivationList);
+};
+
+} // namespace previews
+
+#endif // COMPONENTS_PREVIEWS_CONTENT_ACTIVATION_LIST_H_
diff --git a/chromium/components/previews/content/activation_list_unittest.cc b/chromium/components/previews/content/activation_list_unittest.cc
new file mode 100644
index 00000000000..eb2491edc51
--- /dev/null
+++ b/chromium/components/previews/content/activation_list_unittest.cc
@@ -0,0 +1,77 @@
+// 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/previews/content/activation_list.h"
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/previews/core/previews_experiments.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace previews {
+
+namespace {
+
+class ActivationListTest : public testing::Test {
+ public:
+ ActivationListTest() {}
+
+ ~ActivationListTest() override {}
+
+ protected:
+ std::unique_ptr<ActivationList> page_host_activation_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(ActivationListTest);
+};
+
+TEST_F(ActivationListTest, TestInitialization) {
+ std::vector<std::string> keys;
+ keys.push_back("example.com");
+ keys.push_back("m.foo.com");
+ page_host_activation_list_ = std::make_unique<ActivationList>(keys);
+
+ struct {
+ GURL url;
+ bool expect_whitelisted;
+ } test_cases[] = {
+ {GURL("https://www.example.com"), true},
+ {GURL("https://www.example.co.in"), false},
+ {GURL("https://www.example.com/index.html"), true},
+ {GURL("https://www.example.com/a.html"), true},
+ {GURL("https://www.example.com/foo/a.html"), true},
+ {GURL("https://m.example.com"), true},
+ {GURL("https://m.example.com/foo.html"), true},
+ {GURL("https://m.example.com/foo/bar/baz.html"), true},
+ {GURL("https://example.com"), true},
+ {GURL("https://example2.com"), false},
+ {GURL("https://m.example2.com"), false},
+ {GURL("https://example2.com/foo.html"), false},
+
+ {GURL("https://m.foo.com"), true},
+ {GURL("https://en.m.foo.com"), true},
+ {GURL("https://m.en.foo.com"), false},
+ {GURL("https://m.foo.com/foo.html"), true},
+ {GURL("https://m.foo.com/foo/bar/baz.html"), true},
+ {GURL("https://foo.com"), false},
+ {GURL("https://en.foo.com"), false},
+ {GURL("https://m.foo2.com"), false},
+ {GURL("https://en.foo2.com"), false},
+ };
+
+ for (const auto& test : test_cases) {
+ EXPECT_TRUE(test.url.is_valid());
+ EXPECT_EQ(test.expect_whitelisted,
+ page_host_activation_list_->IsHostWhitelistedAtNavigation(
+ test.url, PreviewsType::RESOURCE_LOADING_HINTS))
+ << " url=" << test.url.spec();
+ }
+}
+
+} // namespace
+
+} // namespace previews \ No newline at end of file
diff --git a/chromium/components/previews/content/previews_content_util.cc b/chromium/components/previews/content/previews_content_util.cc
index 2d312006d51..6872231b854 100644
--- a/chromium/components/previews/content/previews_content_util.cc
+++ b/chromium/components/previews/content/previews_content_util.cc
@@ -29,22 +29,31 @@ content::PreviewsState DetermineEnabledClientPreviewsState(
return previews_state;
}
+ if (previews_decider->ShouldAllowPreview(
+ url_request, previews::PreviewsType::RESOURCE_LOADING_HINTS)) {
+ previews_state |= content::RESOURCE_LOADING_HINTS_ON;
+ }
+
+ if (previews_decider->ShouldAllowPreview(url_request,
+ previews::PreviewsType::OFFLINE)) {
+ previews_state |= content::OFFLINE_PAGE_ON;
+ }
+
// Check for client-side previews in precendence order.
- // Note: this for for the beginning of navigation so we should not
+ // Note: this is for the beginning of navigation so we should not
// check for https here (since an http request may redirect to https).
if (previews_decider->ShouldAllowPreview(url_request,
previews::PreviewsType::NOSCRIPT)) {
previews_state |= content::NOSCRIPT_ON;
- return previews_state;
}
if (previews::params::IsClientLoFiEnabled() &&
previews_decider->ShouldAllowPreviewAtECT(
url_request, previews::PreviewsType::LOFI,
previews::params::EffectiveConnectionTypeThresholdForClientLoFi(),
- previews::params::GetBlackListedHostsForClientLoFiFieldTrial())) {
+ previews::params::GetBlackListedHostsForClientLoFiFieldTrial(),
+ false)) {
previews_state |= content::CLIENT_LOFI_ON;
- return previews_state;
}
return previews_state;
@@ -56,6 +65,15 @@ content::PreviewsState DetermineCommittedClientPreviewsState(
const previews::PreviewsDecider* previews_decider) {
bool is_https = url_request.url().SchemeIs(url::kHttpsScheme);
+ previews::PreviewsUserData* previews_user_data =
+ previews::PreviewsUserData::GetData(url_request);
+ // Check if an offline preview was actually served.
+ if (previews_user_data && previews_user_data->offline_preview_used()) {
+ DCHECK(previews_state & content::OFFLINE_PAGE_ON);
+ return content::OFFLINE_PAGE_ON;
+ }
+ previews_state &= ~content::OFFLINE_PAGE_ON;
+
// If a server preview is set, retain only the bits determined for the server.
// |previews_state| must already have been updated for server previews from
// the main frame response headers (so if they are set here, then they are
@@ -67,8 +85,6 @@ content::PreviewsState DetermineCommittedClientPreviewsState(
content::SERVER_LOFI_ON | content::CLIENT_LOFI_ON);
}
- previews::PreviewsUserData* previews_user_data =
- previews::PreviewsUserData::GetData(url_request);
if (previews_user_data &&
previews_user_data->cache_control_no_transform_directive()) {
if (HasEnabledPreviews(previews_state)) {
@@ -82,6 +98,19 @@ content::PreviewsState DetermineCommittedClientPreviewsState(
// Make priority decision among allowed client preview types that can be
// decided at Commit time.
+ if (previews_state & content::RESOURCE_LOADING_HINTS_ON) {
+ // Resource loading hints was chosen for the original URL but only continue
+ // with it if the committed URL has HTTPS scheme and is allowed by decider.
+ if (is_https && previews_decider &&
+ previews_decider->IsURLAllowedForPreview(
+ url_request, previews::PreviewsType::RESOURCE_LOADING_HINTS)) {
+ return content::RESOURCE_LOADING_HINTS_ON;
+ }
+ // Remove RESOURCE_LOADING_HINTS_ON from |previews_state| since we decided
+ // not to commit to it.
+ previews_state = previews_state & ~content::RESOURCE_LOADING_HINTS_ON;
+ }
+
if (previews_state & content::NOSCRIPT_ON) {
// NoScript was chosen for the original URL but only continue with it
// if the committed URL has HTTPS scheme and is allowed by decider.
@@ -100,21 +129,30 @@ content::PreviewsState DetermineCommittedClientPreviewsState(
return content::PREVIEWS_OFF;
}
- NOTREACHED() << previews_state;
- return previews_state;
+ DCHECK(previews_state == content::PREVIEWS_OFF ||
+ previews_state == content::PREVIEWS_UNSPECIFIED);
+ return content::PREVIEWS_OFF;
}
previews::PreviewsType GetMainFramePreviewsType(
content::PreviewsState previews_state) {
- if (previews_state & content::SERVER_LITE_PAGE_ON) {
+ // The order is important here.
+ if (previews_state & content::OFFLINE_PAGE_ON)
+ return previews::PreviewsType::OFFLINE;
+ if (previews_state & content::SERVER_LITE_PAGE_ON)
return previews::PreviewsType::LITE_PAGE;
- } else if (previews_state & content::SERVER_LOFI_ON) {
+ if (previews_state & content::SERVER_LOFI_ON)
return previews::PreviewsType::LOFI;
- } else if (previews_state & content::NOSCRIPT_ON) {
+ if (previews_state & content::RESOURCE_LOADING_HINTS_ON)
+ return previews::PreviewsType::RESOURCE_LOADING_HINTS;
+ if (previews_state & content::NOSCRIPT_ON)
return previews::PreviewsType::NOSCRIPT;
- } else if (previews_state & content::CLIENT_LOFI_ON) {
+ if (previews_state & content::CLIENT_LOFI_ON)
return previews::PreviewsType::LOFI;
- }
+
+ DCHECK_EQ(content::PREVIEWS_UNSPECIFIED,
+ previews_state & ~content::CLIENT_LOFI_AUTO_RELOAD &
+ ~content::PREVIEWS_NO_TRANSFORM & ~content::PREVIEWS_OFF);
return previews::PreviewsType::NONE;
}
diff --git a/chromium/components/previews/content/previews_content_util_unittest.cc b/chromium/components/previews/content/previews_content_util_unittest.cc
index 0f3aa8db25c..95a4d920736 100644
--- a/chromium/components/previews/content/previews_content_util_unittest.cc
+++ b/chromium/components/previews/content/previews_content_util_unittest.cc
@@ -31,8 +31,8 @@ class PreviewEnabledPreviewsDecider : public PreviewsDecider {
const net::URLRequest& request,
PreviewsType type,
net::EffectiveConnectionType effective_connection_type_threshold,
- const std::vector<std::string>& host_blacklist_from_server)
- const override {
+ const std::vector<std::string>& host_blacklist_from_server,
+ bool ignore_long_term_black_list_rules) const override {
return IsEnabled(type);
}
@@ -40,11 +40,13 @@ class PreviewEnabledPreviewsDecider : public PreviewsDecider {
PreviewsType type) const override {
return ShouldAllowPreviewAtECT(request, type,
params::GetECTThresholdForPreview(type),
- std::vector<std::string>());
+ std::vector<std::string>(), false);
}
bool IsURLAllowedForPreview(const net::URLRequest& request,
PreviewsType type) const override {
+ EXPECT_TRUE(type == PreviewsType::NOSCRIPT ||
+ type == PreviewsType::RESOURCE_LOADING_HINTS);
return IsEnabled(type);
}
@@ -52,17 +54,19 @@ class PreviewEnabledPreviewsDecider : public PreviewsDecider {
bool IsEnabled(PreviewsType type) const {
switch (type) {
case previews::PreviewsType::OFFLINE:
- return previews::params::IsOfflinePreviewsEnabled();
+ return params::IsOfflinePreviewsEnabled();
case previews::PreviewsType::LOFI:
- return previews::params::IsClientLoFiEnabled();
- case previews::PreviewsType::AMP_REDIRECTION:
- return previews::params::IsAMPRedirectionPreviewEnabled();
+ return params::IsClientLoFiEnabled();
+ case previews::PreviewsType::DEPRECATED_AMP_REDIRECTION:
+ return false;
case previews::PreviewsType::NOSCRIPT:
- return previews::params::IsNoScriptPreviewsEnabled();
- case previews::PreviewsType::LITE_PAGE:
- case previews::PreviewsType::NONE:
- case previews::PreviewsType::UNSPECIFIED:
- case previews::PreviewsType::LAST:
+ return params::IsNoScriptPreviewsEnabled();
+ case previews::PreviewsType::RESOURCE_LOADING_HINTS:
+ return params::IsResourceLoadingHintsEnabled();
+ case PreviewsType::LITE_PAGE:
+ case PreviewsType::NONE:
+ case PreviewsType::UNSPECIFIED:
+ case PreviewsType::LAST:
break;
}
NOTREACHED();
@@ -104,8 +108,9 @@ class PreviewsContentUtilTest : public testing::Test {
TEST_F(PreviewsContentUtilTest,
DetermineEnabledClientPreviewsStatePreviewsDisabled) {
base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitFromCommandLine("ClientLoFi" /* enable_features */,
- "Previews" /* disable_features */);
+ scoped_feature_list.InitFromCommandLine(
+ "ClientLoFi,ResourceLoadingHints,NoScriptPreviews" /* enable_features */,
+ "Previews" /* disable_features */);
EXPECT_EQ(content::PREVIEWS_UNSPECIFIED,
previews::DetermineEnabledClientPreviewsState(
*CreateHttpsRequest(), enabled_previews_decider()));
@@ -117,12 +122,25 @@ TEST_F(PreviewsContentUtilTest,
TEST_F(PreviewsContentUtilTest, DetermineEnabledClientPreviewsStateClientLoFi) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitFromCommandLine("Previews,ClientLoFi", std::string());
- EXPECT_EQ(content::CLIENT_LOFI_ON,
- previews::DetermineEnabledClientPreviewsState(
- *CreateHttpsRequest(), enabled_previews_decider()));
- EXPECT_EQ(content::CLIENT_LOFI_ON,
- previews::DetermineEnabledClientPreviewsState(
- *CreateRequest(), enabled_previews_decider()));
+ EXPECT_TRUE(content::CLIENT_LOFI_ON &
+ previews::DetermineEnabledClientPreviewsState(
+ *CreateHttpsRequest(), enabled_previews_decider()));
+ EXPECT_TRUE(content::CLIENT_LOFI_ON &
+ previews::DetermineEnabledClientPreviewsState(
+ *CreateRequest(), enabled_previews_decider()));
+}
+
+TEST_F(PreviewsContentUtilTest,
+ DetermineEnabledClientPreviewsStateResourceLoadingHints) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitFromCommandLine("Previews,ResourceLoadingHints",
+ std::string());
+ EXPECT_LT(0, content::RESOURCE_LOADING_HINTS_ON &
+ previews::DetermineEnabledClientPreviewsState(
+ *CreateHttpsRequest(), enabled_previews_decider()));
+ EXPECT_LT(0, content::RESOURCE_LOADING_HINTS_ON &
+ previews::DetermineEnabledClientPreviewsState(
+ *CreateRequest(), enabled_previews_decider()));
}
TEST_F(PreviewsContentUtilTest,
@@ -132,13 +150,13 @@ TEST_F(PreviewsContentUtilTest,
scoped_feature_list.InitFromCommandLine(
"Previews,ClientLoFi,NoScriptPreviews", std::string());
- // Verify NoScript takes precendence over LoFi (for https).
- EXPECT_EQ(content::NOSCRIPT_ON,
- previews::DetermineEnabledClientPreviewsState(
- *CreateHttpsRequest(), enabled_previews_decider()));
- EXPECT_EQ(content::NOSCRIPT_ON,
- previews::DetermineEnabledClientPreviewsState(
- *CreateRequest(), enabled_previews_decider()));
+ // Verify both are enabled.
+ EXPECT_TRUE((content::NOSCRIPT_ON | content::CLIENT_LOFI_ON) &
+ previews::DetermineEnabledClientPreviewsState(
+ *CreateHttpsRequest(), enabled_previews_decider()));
+ EXPECT_TRUE((content::NOSCRIPT_ON | content::CLIENT_LOFI_ON) &
+ previews::DetermineEnabledClientPreviewsState(
+ *CreateRequest(), enabled_previews_decider()));
// Verify non-HTTP[S] URL has no previews enabled.
std::unique_ptr<net::URLRequest> data_url_request(
@@ -151,8 +169,9 @@ TEST_F(PreviewsContentUtilTest,
TEST_F(PreviewsContentUtilTest, DetermineCommittedClientPreviewsState) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitFromCommandLine(
- "Previews,ClientLoFi,NoScriptPreviews", std::string());
- // Server bits take precendence over NoScript:
+ "Previews,ClientLoFi,NoScriptPreviews,ResourceLoadingHints",
+ std::string());
+ // Server bits take precedence over NoScript:
EXPECT_EQ(content::SERVER_LITE_PAGE_ON | content::SERVER_LOFI_ON |
content::CLIENT_LOFI_ON,
previews::DetermineCommittedClientPreviewsState(
@@ -168,18 +187,33 @@ TEST_F(PreviewsContentUtilTest, DetermineCommittedClientPreviewsState) {
*CreateHttpsRequest(), content::CLIENT_LOFI_ON | content::NOSCRIPT_ON,
enabled_previews_decider()));
+ // RESOURCE_LOADING_HINTS has precedence over Client LoFi and NoScript.
+ EXPECT_EQ(content::RESOURCE_LOADING_HINTS_ON,
+ previews::DetermineCommittedClientPreviewsState(
+ *CreateHttpsRequest(),
+ content::CLIENT_LOFI_ON | content::NOSCRIPT_ON |
+ content::RESOURCE_LOADING_HINTS_ON,
+ enabled_previews_decider()));
+
// NoScript has precedence over Client LoFi - dropped for committed HTTP:
- EXPECT_EQ(
- content::PREVIEWS_OFF,
- previews::DetermineCommittedClientPreviewsState(
- *CreateRequest(), content::CLIENT_LOFI_ON | content::NOSCRIPT_ON,
- enabled_previews_decider()));
+ EXPECT_EQ(content::PREVIEWS_OFF,
+ previews::DetermineCommittedClientPreviewsState(
+ *CreateRequest(),
+ content::CLIENT_LOFI_ON | content::NOSCRIPT_ON |
+ content::RESOURCE_LOADING_HINTS_ON,
+ enabled_previews_decider()));
- // Client LoFi:
+ // Only Client LoFi:
EXPECT_EQ(content::CLIENT_LOFI_ON,
previews::DetermineCommittedClientPreviewsState(
*CreateHttpsRequest(), content::CLIENT_LOFI_ON,
enabled_previews_decider()));
+
+ // Only NoScript:
+ EXPECT_EQ(content::NOSCRIPT_ON,
+ previews::DetermineCommittedClientPreviewsState(
+ *CreateHttpsRequest(), content::NOSCRIPT_ON,
+ enabled_previews_decider()));
}
TEST_F(PreviewsContentUtilTest,
@@ -187,11 +221,12 @@ TEST_F(PreviewsContentUtilTest,
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitFromCommandLine("Previews,ClientLoFi", std::string());
// NoScript not allowed at commit time so Client LoFi chosen:
- EXPECT_EQ(
- content::PREVIEWS_OFF,
- previews::DetermineCommittedClientPreviewsState(
- *CreateHttpsRequest(), content::CLIENT_LOFI_ON | content::NOSCRIPT_ON,
- enabled_previews_decider()));
+ EXPECT_EQ(content::PREVIEWS_OFF,
+ previews::DetermineCommittedClientPreviewsState(
+ *CreateHttpsRequest(),
+ content::CLIENT_LOFI_ON | content::NOSCRIPT_ON |
+ content::RESOURCE_LOADING_HINTS_ON,
+ enabled_previews_decider()));
}
TEST_F(PreviewsContentUtilTest, GetMainFramePreviewsType) {
@@ -202,6 +237,9 @@ TEST_F(PreviewsContentUtilTest, GetMainFramePreviewsType) {
previews::GetMainFramePreviewsType(content::SERVER_LOFI_ON));
EXPECT_EQ(previews::PreviewsType::NOSCRIPT,
previews::GetMainFramePreviewsType(content::NOSCRIPT_ON));
+ EXPECT_EQ(
+ previews::PreviewsType::RESOURCE_LOADING_HINTS,
+ previews::GetMainFramePreviewsType(content::RESOURCE_LOADING_HINTS_ON));
EXPECT_EQ(previews::PreviewsType::LOFI,
previews::GetMainFramePreviewsType(content::CLIENT_LOFI_ON));
@@ -211,18 +249,25 @@ TEST_F(PreviewsContentUtilTest, GetMainFramePreviewsType) {
EXPECT_EQ(previews::PreviewsType::NONE,
previews::GetMainFramePreviewsType(content::PREVIEWS_NO_TRANSFORM));
- // Precedence cases:
+ // Precedence cases when server preview is available:
EXPECT_EQ(previews::PreviewsType::LITE_PAGE,
previews::GetMainFramePreviewsType(
content::SERVER_LITE_PAGE_ON | content::SERVER_LOFI_ON |
- content::NOSCRIPT_ON | content::CLIENT_LOFI_ON));
+ content::NOSCRIPT_ON | content::CLIENT_LOFI_ON |
+ content::RESOURCE_LOADING_HINTS_ON));
EXPECT_EQ(previews::PreviewsType::LOFI,
- previews::GetMainFramePreviewsType(content::SERVER_LOFI_ON |
- content::NOSCRIPT_ON |
- content::CLIENT_LOFI_ON));
+ previews::GetMainFramePreviewsType(
+ content::SERVER_LOFI_ON | content::NOSCRIPT_ON |
+ content::CLIENT_LOFI_ON | content::RESOURCE_LOADING_HINTS_ON));
+
+ // Precedence cases when server preview is not available:
EXPECT_EQ(previews::PreviewsType::NOSCRIPT,
previews::GetMainFramePreviewsType(content::NOSCRIPT_ON |
content::CLIENT_LOFI_ON));
+ EXPECT_EQ(previews::PreviewsType::RESOURCE_LOADING_HINTS,
+ previews::GetMainFramePreviewsType(
+ content::NOSCRIPT_ON | content::CLIENT_LOFI_ON |
+ content::RESOURCE_LOADING_HINTS_ON));
}
} // namespace
diff --git a/chromium/components/previews/content/previews_io_data.cc b/chromium/components/previews/content/previews_decider_impl.cc
index faa410dd5d0..0135465dbc9 100644
--- a/chromium/components/previews/content/previews_io_data.cc
+++ b/chromium/components/previews/content/previews_decider_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/previews/content/previews_io_data.h"
+#include "components/previews/content/previews_decider_impl.h"
#include <algorithm>
#include <utility>
@@ -14,11 +14,12 @@
#include "base/location.h"
#include "base/metrics/histogram.h"
#include "base/sequenced_task_runner.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
-#include "base/time/default_clock.h"
+#include "base/time/clock.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_store.h"
#include "components/previews/content/previews_ui_service.h"
#include "components/previews/core/previews_experiments.h"
-#include "components/previews/core/previews_opt_out_store.h"
#include "components/previews/core/previews_switches.h"
#include "components/previews/core/previews_user_data.h"
#include "net/base/load_flags.h"
@@ -47,14 +48,15 @@ bool AllowedOnReload(PreviewsType type) {
// These types return new content on refresh.
case PreviewsType::LITE_PAGE:
case PreviewsType::LOFI:
- case PreviewsType::AMP_REDIRECTION:
case PreviewsType::NOSCRIPT:
+ case PreviewsType::RESOURCE_LOADING_HINTS:
return true;
// Loading these types will always be stale when refreshed.
case PreviewsType::OFFLINE:
return false;
case PreviewsType::NONE:
case PreviewsType::UNSPECIFIED:
+ case PreviewsType::DEPRECATED_AMP_REDIRECTION:
case PreviewsType::LAST:
break;
}
@@ -66,14 +68,15 @@ bool IsServerWhitelistedType(PreviewsType type) {
switch (type) {
// These types check server whitelist, if available.
case PreviewsType::NOSCRIPT:
+ case PreviewsType::RESOURCE_LOADING_HINTS:
return true;
case PreviewsType::OFFLINE:
case PreviewsType::LITE_PAGE:
case PreviewsType::LOFI:
- case PreviewsType::AMP_REDIRECTION:
return false;
case PreviewsType::NONE:
case PreviewsType::UNSPECIFIED:
+ case PreviewsType::DEPRECATED_AMP_REDIRECTION:
case PreviewsType::LAST:
break;
}
@@ -88,22 +91,25 @@ bool IsPreviewsBlacklistIgnoredViaFlag() {
} // namespace
-PreviewsIOData::PreviewsIOData(
+PreviewsDeciderImpl::PreviewsDeciderImpl(
const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
- const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
+ base::Clock* clock)
: blacklist_ignored_(IsPreviewsBlacklistIgnoredViaFlag()),
+ clock_(clock),
ui_task_runner_(ui_task_runner),
io_task_runner_(io_task_runner),
page_id_(1u),
weak_factory_(this) {}
-PreviewsIOData::~PreviewsIOData() {}
+PreviewsDeciderImpl::~PreviewsDeciderImpl() {}
-void PreviewsIOData::Initialize(
+void PreviewsDeciderImpl::Initialize(
base::WeakPtr<PreviewsUIService> previews_ui_service,
- std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store,
+ std::unique_ptr<blacklist::OptOutStore> previews_opt_out_store,
std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide,
- const PreviewsIsEnabledCallback& is_enabled_callback) {
+ const PreviewsIsEnabledCallback& is_enabled_callback,
+ blacklist::BlacklistData::AllowedTypesAndVersions allowed_previews) {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
is_enabled_callback_ = is_enabled_callback;
previews_ui_service_ = previews_ui_service;
@@ -111,61 +117,65 @@ void PreviewsIOData::Initialize(
// Set up the IO thread portion of |this|.
io_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&PreviewsIOData::InitializeOnIOThread,
- base::Unretained(this),
- std::move(previews_opt_out_store)));
+ FROM_HERE,
+ base::BindOnce(&PreviewsDeciderImpl::InitializeOnIOThread,
+ base::Unretained(this), std::move(previews_opt_out_store),
+ std::move(allowed_previews)));
}
-void PreviewsIOData::OnNewBlacklistedHost(const std::string& host,
- base::Time time) {
+void PreviewsDeciderImpl::OnNewBlacklistedHost(const std::string& host,
+ base::Time time) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
- ui_task_runner_->PostTask(FROM_HERE,
- base::Bind(&PreviewsUIService::OnNewBlacklistedHost,
- previews_ui_service_, host, time));
+ ui_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&PreviewsUIService::OnNewBlacklistedHost,
+ previews_ui_service_, host, time));
}
-void PreviewsIOData::OnUserBlacklistedStatusChange(bool blacklisted) {
+void PreviewsDeciderImpl::OnUserBlacklistedStatusChange(bool blacklisted) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
ui_task_runner_->PostTask(
- FROM_HERE, base::Bind(&PreviewsUIService::OnUserBlacklistedStatusChange,
- previews_ui_service_, blacklisted));
+ FROM_HERE,
+ base::BindOnce(&PreviewsUIService::OnUserBlacklistedStatusChange,
+ previews_ui_service_, blacklisted));
}
-void PreviewsIOData::OnBlacklistCleared(base::Time time) {
+void PreviewsDeciderImpl::OnBlacklistCleared(base::Time time) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
- ui_task_runner_->PostTask(FROM_HERE,
- base::Bind(&PreviewsUIService::OnBlacklistCleared,
- previews_ui_service_, time));
+ ui_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&PreviewsUIService::OnBlacklistCleared,
+ previews_ui_service_, time));
}
-void PreviewsIOData::InitializeOnIOThread(
- std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store) {
+void PreviewsDeciderImpl::InitializeOnIOThread(
+ std::unique_ptr<blacklist::OptOutStore> previews_opt_out_store,
+ blacklist::BlacklistData::AllowedTypesAndVersions allowed_previews) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
previews_black_list_.reset(
- new PreviewsBlackList(std::move(previews_opt_out_store),
- base::DefaultClock::GetInstance(), this));
+ new PreviewsBlackList(std::move(previews_opt_out_store), clock_, this,
+ std::move(allowed_previews)));
ui_task_runner_->PostTask(
- FROM_HERE, base::Bind(&PreviewsUIService::SetIOData, previews_ui_service_,
- weak_factory_.GetWeakPtr()));
+ FROM_HERE,
+ base::BindOnce(&PreviewsUIService::SetIOData, previews_ui_service_,
+ weak_factory_.GetWeakPtr()));
}
-void PreviewsIOData::SetPreviewsBlacklistForTesting(
+void PreviewsDeciderImpl::SetPreviewsBlacklistForTesting(
std::unique_ptr<PreviewsBlackList> previews_back_list) {
previews_black_list_ = std::move(previews_back_list);
}
-void PreviewsIOData::LogPreviewNavigation(const GURL& url,
- bool opt_out,
- PreviewsType type,
- base::Time time,
- uint64_t page_id) const {
+void PreviewsDeciderImpl::LogPreviewNavigation(const GURL& url,
+ bool opt_out,
+ PreviewsType type,
+ base::Time time,
+ uint64_t page_id) const {
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&PreviewsUIService::LogPreviewNavigation,
previews_ui_service_, url, type, opt_out, time, page_id));
}
-void PreviewsIOData::LogPreviewDecisionMade(
+void PreviewsDeciderImpl::LogPreviewDecisionMade(
PreviewsEligibilityReason reason,
const GURL& url,
base::Time time,
@@ -179,43 +189,51 @@ void PreviewsIOData::LogPreviewDecisionMade(
std::move(passed_reasons), page_id));
}
-void PreviewsIOData::AddPreviewNavigation(const GURL& url,
- bool opt_out,
- PreviewsType type,
- uint64_t page_id) {
+void PreviewsDeciderImpl::AddPreviewNavigation(const GURL& url,
+ bool opt_out,
+ PreviewsType type,
+ uint64_t page_id) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
base::Time time =
previews_black_list_->AddPreviewNavigation(url, opt_out, type);
+ if (opt_out) {
+ last_opt_out_time_ = time;
+ }
LogPreviewNavigation(url, opt_out, type, time, page_id);
}
-void PreviewsIOData::ClearBlackList(base::Time begin_time,
- base::Time end_time) {
+void PreviewsDeciderImpl::ClearBlackList(base::Time begin_time,
+ base::Time end_time) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
previews_black_list_->ClearBlackList(begin_time, end_time);
}
-void PreviewsIOData::SetIgnorePreviewsBlacklistDecision(bool ignored) {
+void PreviewsDeciderImpl::SetIgnorePreviewsBlacklistDecision(bool ignored) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
blacklist_ignored_ = ignored;
ui_task_runner_->PostTask(
FROM_HERE,
- base::Bind(&PreviewsUIService::OnIgnoreBlacklistDecisionStatusChanged,
- previews_ui_service_, blacklist_ignored_));
+ base::BindOnce(&PreviewsUIService::OnIgnoreBlacklistDecisionStatusChanged,
+ previews_ui_service_, blacklist_ignored_));
}
-bool PreviewsIOData::ShouldAllowPreview(const net::URLRequest& request,
- PreviewsType type) const {
+bool PreviewsDeciderImpl::ShouldAllowPreview(const net::URLRequest& request,
+ PreviewsType type) const {
+ DCHECK(type == PreviewsType::OFFLINE || type == PreviewsType::NOSCRIPT ||
+ type == PreviewsType::RESOURCE_LOADING_HINTS);
+ // Consumers that need to specify a blacklist or ignore flag should use
+ // ShouldAllowPreviewAtECT directly.
return ShouldAllowPreviewAtECT(request, type,
params::GetECTThresholdForPreview(type),
- std::vector<std::string>());
+ std::vector<std::string>(), false);
}
-bool PreviewsIOData::ShouldAllowPreviewAtECT(
+bool PreviewsDeciderImpl::ShouldAllowPreviewAtECT(
const net::URLRequest& request,
PreviewsType type,
net::EffectiveConnectionType effective_connection_type_threshold,
- const std::vector<std::string>& host_blacklist_from_server) const {
+ const std::vector<std::string>& host_blacklist_from_server,
+ bool ignore_long_term_black_list_rules) const {
if (!previews::params::ArePreviewsAllowed()) {
return false;
}
@@ -230,7 +248,7 @@ bool PreviewsIOData::ShouldAllowPreviewAtECT(
uint64_t page_id = PreviewsUserData::GetData(request)->page_id();
if (is_enabled_callback_.is_null() || !previews_black_list_) {
LogPreviewDecisionMade(PreviewsEligibilityReason::BLACKLIST_UNAVAILABLE,
- request.url(), base::Time::Now(), type,
+ request.url(), clock_->Now(), type,
std::move(passed_reasons), page_id);
return false;
}
@@ -239,13 +257,32 @@ bool PreviewsIOData::ShouldAllowPreviewAtECT(
if (!is_enabled_callback_.Run(type))
return false;
- if (!blacklist_ignored_) {
+ // In the case that the user has chosen to ignore the normal blacklist rules
+ // (flags or interventions-internals), a preview should still not be served
+ // for 5 seconds after the last opt out. This allows "show original" to
+ // function correctly as the start of that navigation will be within 5 seconds
+ // (we don't yet re-evaluate on redirects, so this is sufficient).
+ if (blacklist_ignored_) {
+ if (clock_->Now() < last_opt_out_time_ + base::TimeDelta::FromSeconds(5)) {
+ LogPreviewDecisionMade(PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT,
+ request.url(), clock_->Now(), type,
+ std::move(passed_reasons), page_id);
+
+ return false;
+ }
+ } else {
// The blacklist will disallow certain hosts for periods of time based on
// user's opting out of the preview.
PreviewsEligibilityReason status = previews_black_list_->IsLoadedAndAllowed(
- request.url(), type, &passed_reasons);
+ request.url(), type, ignore_long_term_black_list_rules,
+ &passed_reasons);
+
if (status != PreviewsEligibilityReason::ALLOWED) {
- LogPreviewDecisionMade(status, request.url(), base::Time::Now(), type,
+ if (type == PreviewsType::LITE_PAGE) {
+ PreviewsUserData::GetData(request)->set_black_listed_for_lite_page(
+ true);
+ }
+ LogPreviewDecisionMade(status, request.url(), clock_->Now(), type,
std::move(passed_reasons), page_id);
return false;
}
@@ -268,7 +305,7 @@ bool PreviewsIOData::ShouldAllowPreviewAtECT(
net::EFFECTIVE_CONNECTION_TYPE_OFFLINE) {
LogPreviewDecisionMade(
PreviewsEligibilityReason::NETWORK_QUALITY_UNAVAILABLE, request.url(),
- base::Time::Now(), type, std::move(passed_reasons), page_id);
+ clock_->Now(), type, std::move(passed_reasons), page_id);
return false;
}
passed_reasons.push_back(
@@ -277,7 +314,7 @@ bool PreviewsIOData::ShouldAllowPreviewAtECT(
if (observed_effective_connection_type >
effective_connection_type_threshold) {
LogPreviewDecisionMade(PreviewsEligibilityReason::NETWORK_NOT_SLOW,
- request.url(), base::Time::Now(), type,
+ request.url(), clock_->Now(), type,
std::move(passed_reasons), page_id);
return false;
}
@@ -290,7 +327,7 @@ bool PreviewsIOData::ShouldAllowPreviewAtECT(
request.load_flags() &
(net::LOAD_VALIDATE_CACHE | net::LOAD_BYPASS_CACHE)) {
LogPreviewDecisionMade(PreviewsEligibilityReason::RELOAD_DISALLOWED,
- request.url(), base::Time::Now(), type,
+ request.url(), clock_->Now(), type,
std::move(passed_reasons), page_id);
return false;
}
@@ -298,12 +335,11 @@ bool PreviewsIOData::ShouldAllowPreviewAtECT(
// Check provided blacklist, if any. This type of blacklist was added for
// Finch provided blacklist for Client LoFi.
- if (std::find(host_blacklist_from_server.begin(),
- host_blacklist_from_server.end(), request.url().host_piece()) !=
- host_blacklist_from_server.end()) {
+ if (base::ContainsValue(host_blacklist_from_server,
+ request.url().host_piece())) {
LogPreviewDecisionMade(
PreviewsEligibilityReason::HOST_BLACKLISTED_BY_SERVER, request.url(),
- base::Time::Now(), type, std::move(passed_reasons), page_id);
+ clock_->Now(), type, std::move(passed_reasons), page_id);
return false;
}
passed_reasons.push_back(
@@ -316,37 +352,52 @@ bool PreviewsIOData::ShouldAllowPreviewAtECT(
PreviewsEligibilityReason status =
IsPreviewAllowedByOptmizationHints(request, type, &passed_reasons);
if (status != PreviewsEligibilityReason::ALLOWED) {
- LogPreviewDecisionMade(status, request.url(), base::Time::Now(), type,
+ LogPreviewDecisionMade(status, request.url(), clock_->Now(), type,
std::move(passed_reasons), page_id);
return false;
}
+ } else if (type == PreviewsType::RESOURCE_LOADING_HINTS) {
+ // RESOURCE_LOADING_HINTS optimization can be applied only when a server
+ // provided whitelist is available.
+ LogPreviewDecisionMade(
+ PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER,
+ request.url(), clock_->Now(), type, std::move(passed_reasons),
+ page_id);
+ return false;
} else {
+ DCHECK_EQ(PreviewsType::NOSCRIPT, type);
// Since server optimization guidance not configured, allow the preview
// but with qualified eligibility reason.
LogPreviewDecisionMade(
PreviewsEligibilityReason::ALLOWED_WITHOUT_OPTIMIZATION_HINTS,
- request.url(), base::Time::Now(), type, std::move(passed_reasons),
+ request.url(), clock_->Now(), type, std::move(passed_reasons),
page_id);
return true;
}
}
LogPreviewDecisionMade(PreviewsEligibilityReason::ALLOWED, request.url(),
- base::Time::Now(), type, std::move(passed_reasons),
+ clock_->Now(), type, std::move(passed_reasons),
page_id);
return true;
}
-bool PreviewsIOData::IsURLAllowedForPreview(const net::URLRequest& request,
- PreviewsType type) const {
+bool PreviewsDeciderImpl::IsURLAllowedForPreview(const net::URLRequest& request,
+ PreviewsType type) const {
+ DCHECK(PreviewsType::NOSCRIPT == type ||
+ PreviewsType::RESOURCE_LOADING_HINTS == type);
if (previews_black_list_ && !blacklist_ignored_) {
std::vector<PreviewsEligibilityReason> passed_reasons;
// The blacklist will disallow certain hosts for periods of time based on
// user's opting out of the preview.
PreviewsEligibilityReason status = previews_black_list_->IsLoadedAndAllowed(
- request.url(), type, &passed_reasons);
+ request.url(), type, false, &passed_reasons);
if (status != PreviewsEligibilityReason::ALLOWED) {
- LogPreviewDecisionMade(status, request.url(), base::Time::Now(), type,
+ if (type == PreviewsType::LITE_PAGE) {
+ PreviewsUserData::GetData(request)->set_black_listed_for_lite_page(
+ true);
+ }
+ LogPreviewDecisionMade(status, request.url(), clock_->Now(), type,
std::move(passed_reasons),
PreviewsUserData::GetData(request)->page_id());
return false;
@@ -360,7 +411,7 @@ bool PreviewsIOData::IsURLAllowedForPreview(const net::URLRequest& request,
PreviewsEligibilityReason status =
IsPreviewAllowedByOptmizationHints(request, type, &passed_reasons);
if (status != PreviewsEligibilityReason::ALLOWED) {
- LogPreviewDecisionMade(status, request.url(), base::Time::Now(), type,
+ LogPreviewDecisionMade(status, request.url(), clock_->Now(), type,
std::move(passed_reasons),
PreviewsUserData::GetData(request)->page_id());
return false;
@@ -370,14 +421,20 @@ bool PreviewsIOData::IsURLAllowedForPreview(const net::URLRequest& request,
return true;
}
-PreviewsEligibilityReason PreviewsIOData::IsPreviewAllowedByOptmizationHints(
+PreviewsEligibilityReason
+PreviewsDeciderImpl::IsPreviewAllowedByOptmizationHints(
const net::URLRequest& request,
PreviewsType type,
std::vector<PreviewsEligibilityReason>* passed_reasons) const {
- if (!previews_opt_guide_)
+ DCHECK(type == PreviewsType::NOSCRIPT ||
+ type == PreviewsType::RESOURCE_LOADING_HINTS);
+
+ // For NoScript, if optimization guide is not present, assume that all URLs
+ // are ALLOWED.
+ if (!previews_opt_guide_ && type == PreviewsType::NOSCRIPT)
return PreviewsEligibilityReason::ALLOWED;
- // Check optmization guide whitelist.
+ // Check optimization guide whitelist.
if (!previews_opt_guide_->IsWhitelisted(request, type)) {
return PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER;
}
@@ -387,7 +444,7 @@ PreviewsEligibilityReason PreviewsIOData::IsPreviewAllowedByOptmizationHints(
return PreviewsEligibilityReason::ALLOWED;
}
-uint64_t PreviewsIOData::GeneratePageId() {
+uint64_t PreviewsDeciderImpl::GeneratePageId() {
return ++page_id_;
}
diff --git a/chromium/components/previews/content/previews_io_data.h b/chromium/components/previews/content/previews_decider_impl.h
index 9304cb947a7..13757d46be9 100644
--- a/chromium/components/previews/content/previews_io_data.h
+++ b/chromium/components/previews/content/previews_decider_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_PREVIEWS_CONTENT_PREVIEWS_IO_DATA_H_
-#define COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_IO_DATA_H_
+#ifndef COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_DECIDER_IMPL_H_
+#define COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_DECIDER_IMPL_H_
#include <stdint.h>
@@ -17,9 +17,10 @@
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/time/time.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_delegate.h"
#include "components/previews/content/previews_optimization_guide.h"
#include "components/previews/core/previews_black_list.h"
-#include "components/previews/core/previews_black_list_delegate.h"
#include "components/previews/core/previews_decider.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_logger.h"
@@ -27,28 +28,36 @@
class GURL;
+namespace base {
+class Clock;
+}
+
+namespace blacklist {
+class OptOutStore;
+}
+
namespace net {
class URLRequest;
}
namespace previews {
-class PreviewsOptOutStore;
class PreviewsUIService;
-typedef base::Callback<bool(PreviewsType)> PreviewsIsEnabledCallback;
+typedef base::RepeatingCallback<bool(PreviewsType)> PreviewsIsEnabledCallback;
// A class to manage the IO portion of inter-thread communication between
// previews/ objects. Created on the UI thread, but used only on the IO thread
// after initialization.
-class PreviewsIOData : public PreviewsDecider,
- public PreviewsBlacklistDelegate {
+class PreviewsDeciderImpl : public PreviewsDecider,
+ public blacklist::OptOutBlacklistDelegate {
public:
- PreviewsIOData(
+ PreviewsDeciderImpl(
const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
- const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner);
- ~PreviewsIOData() override;
+ const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
+ base::Clock* clock);
+ ~PreviewsDeciderImpl() override;
- // PreviewsBlacklistDelegate:
+ // blacklist::OptOutBlacklistDelegate:
void OnNewBlacklistedHost(const std::string& host, base::Time time) override;
void OnUserBlacklistedStatusChange(bool blacklisted) override;
void OnBlacklistCleared(base::Time time) override;
@@ -57,9 +66,10 @@ class PreviewsIOData : public PreviewsDecider,
// InitializeOnIOThread on the IO thread.
virtual void Initialize(
base::WeakPtr<PreviewsUIService> previews_ui_service,
- std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store,
+ std::unique_ptr<blacklist::OptOutStore> previews_opt_out_store,
std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide,
- const PreviewsIsEnabledCallback& is_enabled_callback);
+ const PreviewsIsEnabledCallback& is_enabled_callback,
+ blacklist::BlacklistData::AllowedTypesAndVersions allowed_previews);
// Adds log message of the navigation asynchronously.
void LogPreviewNavigation(const GURL& url,
@@ -73,7 +83,7 @@ class PreviewsIOData : public PreviewsDecider,
// eligibility checks that were satisfied prior to determining |reason| and
// so the opposite of these |passed_reasons| codes was true. The method
// takes ownership of |passed_reasons|. |page_id| is generated by
- // PreviewsIOData, and used to group decisions into groups on the page,
+ // PreviewsDeciderImpl, and used to group decisions into groups on the page,
// messages that don't need to be grouped can pass in 0 as page_id.
void LogPreviewDecisionMade(
PreviewsEligibilityReason reason,
@@ -107,8 +117,8 @@ class PreviewsIOData : public PreviewsDecider,
const net::URLRequest& request,
PreviewsType type,
net::EffectiveConnectionType effective_connection_type_threshold,
- const std::vector<std::string>& host_blacklist_from_server)
- const override;
+ const std::vector<std::string>& host_blacklist_from_server,
+ bool ignore_long_term_black_list_rules) const override;
bool IsURLAllowedForPreview(const net::URLRequest& request,
PreviewsType type) const override;
@@ -120,7 +130,8 @@ class PreviewsIOData : public PreviewsDecider,
// Posts a task to SetIOData for |previews_ui_service_| on the UI thread with
// a weak pointer to |this|. Virtualized for testing.
virtual void InitializeOnIOThread(
- std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store);
+ std::unique_ptr<blacklist::OptOutStore> previews_opt_out_store,
+ blacklist::BlacklistData::AllowedTypesAndVersions allowed_previews);
// Sets a blacklist for testing.
void SetPreviewsBlacklistForTesting(
@@ -140,6 +151,10 @@ class PreviewsIOData : public PreviewsDecider,
std::unique_ptr<PreviewsBlackList> previews_black_list_;
+ // Only used when the blacklist has been disabled to allow "Show Original" to
+ // function as expected. The time of the most recent opt out event.
+ base::Time last_opt_out_time_;
+
// Holds optimization guidance from the server.
std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide_;
@@ -148,6 +163,8 @@ class PreviewsIOData : public PreviewsDecider,
// behavior of Previews decisions.
bool blacklist_ignored_;
+ base::Clock* clock_;
+
// The UI and IO thread task runners. |ui_task_runner_| is used to post
// tasks to |previews_ui_service_|, and |io_task_runner_| is used to post from
// Initialize to InitializeOnIOThread as well as verify that execution is
@@ -160,11 +177,11 @@ class PreviewsIOData : public PreviewsDecider,
uint64_t page_id_;
- base::WeakPtrFactory<PreviewsIOData> weak_factory_;
+ base::WeakPtrFactory<PreviewsDeciderImpl> weak_factory_;
- DISALLOW_COPY_AND_ASSIGN(PreviewsIOData);
+ DISALLOW_COPY_AND_ASSIGN(PreviewsDeciderImpl);
};
} // namespace previews
-#endif // COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_IO_DATA_H_
+#endif // COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_DECIDER_IMPL_H_
diff --git a/chromium/components/previews/content/previews_io_data_unittest.cc b/chromium/components/previews/content/previews_decider_impl_unittest.cc
index 69ff818ff91..a3283aca5e4 100644
--- a/chromium/components/previews/content/previews_io_data_unittest.cc
+++ b/chromium/components/previews/content/previews_decider_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/previews/content/previews_io_data.h"
+#include "components/previews/content/previews_decider_impl.h"
#include <initializer_list>
#include <map>
@@ -19,22 +19,24 @@
#include "base/metrics/field_trial_params.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_command_line.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
+#include "base/test/simple_test_clock.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_delegate.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_item.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_store.h"
#include "components/optimization_guide/optimization_guide_service.h"
#include "components/previews/content/previews_ui_service.h"
#include "components/previews/core/previews_black_list.h"
-#include "components/previews/core/previews_black_list_delegate.h"
-#include "components/previews/core/previews_black_list_item.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_features.h"
#include "components/previews/core/previews_logger.h"
-#include "components/previews/core/previews_opt_out_store.h"
#include "components/previews/core/previews_switches.h"
#include "components/previews/core/previews_user_data.h"
#include "components/variations/variations_associated_data.h"
@@ -63,12 +65,15 @@ bool IsPreviewFieldTrialEnabled(PreviewsType type) {
switch (type) {
case PreviewsType::OFFLINE:
case PreviewsType::LITE_PAGE:
- case PreviewsType::AMP_REDIRECTION:
return params::IsOfflinePreviewsEnabled();
+ case PreviewsType::DEPRECATED_AMP_REDIRECTION:
+ return false;
case PreviewsType::LOFI:
return params::IsClientLoFiEnabled();
case PreviewsType::NOSCRIPT:
return params::IsNoScriptPreviewsEnabled();
+ case PreviewsType::RESOURCE_LOADING_HINTS:
+ return params::IsResourceLoadingHintsEnabled();
case PreviewsType::NONE:
case PreviewsType::UNSPECIFIED:
case PreviewsType::LAST:
@@ -79,14 +84,15 @@ bool IsPreviewFieldTrialEnabled(PreviewsType type) {
}
// Stub class of PreviewsBlackList to control IsLoadedAndAllowed outcome when
-// testing PreviewsIOData.
+// testing PreviewsDeciderImpl.
class TestPreviewsBlackList : public PreviewsBlackList {
public:
TestPreviewsBlackList(PreviewsEligibilityReason status,
- PreviewsBlacklistDelegate* blacklist_delegate)
+ blacklist::OptOutBlacklistDelegate* blacklist_delegate)
: PreviewsBlackList(nullptr,
base::DefaultClock::GetInstance(),
- blacklist_delegate),
+ blacklist_delegate,
+ {}),
status_(status) {}
~TestPreviewsBlackList() override {}
@@ -94,6 +100,7 @@ class TestPreviewsBlackList : public PreviewsBlackList {
PreviewsEligibilityReason IsLoadedAndAllowed(
const GURL& url,
PreviewsType type,
+ bool ignore_long_term_black_list_rules,
std::vector<PreviewsEligibilityReason>* passed_reasons) const override {
PreviewsEligibilityReason ordered_reasons[] = {
PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
@@ -118,7 +125,7 @@ class TestPreviewsBlackList : public PreviewsBlackList {
};
// Stub class of PreviewsOptimizationGuide to control IsWhitelisted outcome
-// when testing PreviewsIOData.
+// when testing PreviewsDeciderImpl.
class TestPreviewsOptimizationGuide : public PreviewsOptimizationGuide {
public:
TestPreviewsOptimizationGuide(
@@ -130,27 +137,39 @@ class TestPreviewsOptimizationGuide : public PreviewsOptimizationGuide {
// PreviewsOptimizationGuide:
bool IsWhitelisted(const net::URLRequest& request,
PreviewsType type) const override {
- return request.url().host().compare("whitelisted.example.com") == 0;
+ EXPECT_TRUE(type == PreviewsType::NOSCRIPT ||
+ type == PreviewsType::RESOURCE_LOADING_HINTS);
+ if (type == PreviewsType::NOSCRIPT) {
+ return request.url().host().compare("whitelisted.example.com") == 0 ||
+ request.url().host().compare(
+ "noscript_only_whitelisted.example.com") == 0;
+ }
+ if (type == PreviewsType::RESOURCE_LOADING_HINTS) {
+ return request.url().host().compare("whitelisted.example.com") == 0;
+ }
+ return false;
}
};
// Stub class of PreviewsUIService to test logging functionalities in
-// PreviewsIOData.
+// PreviewsDeciderImpl.
class TestPreviewsUIService : public PreviewsUIService {
public:
TestPreviewsUIService(
- PreviewsIOData* previews_io_data,
+ PreviewsDeciderImpl* previews_decider_impl,
const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
- std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store,
+ std::unique_ptr<blacklist::OptOutStore> previews_opt_out_store,
std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide,
const PreviewsIsEnabledCallback& is_enabled_callback,
- std::unique_ptr<PreviewsLogger> logger)
- : PreviewsUIService(previews_io_data,
+ std::unique_ptr<PreviewsLogger> logger,
+ blacklist::BlacklistData::AllowedTypesAndVersions allowed_types)
+ : PreviewsUIService(previews_decider_impl,
io_task_runner,
std::move(previews_opt_out_store),
std::move(previews_opt_guide),
is_enabled_callback,
- std::move(logger)),
+ std::move(logger),
+ std::move(allowed_types)),
user_blacklisted_(false),
blacklist_ignored_(false) {}
@@ -264,18 +283,20 @@ class TestPreviewsUIService : public PreviewsUIService {
bool blacklist_ignored_;
};
-class TestPreviewsIOData : public PreviewsIOData {
+class TestPreviewsDeciderImpl : public PreviewsDeciderImpl {
public:
- TestPreviewsIOData(
+ TestPreviewsDeciderImpl(
const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
- const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner)
- : PreviewsIOData(io_task_runner, ui_task_runner), initialized_(false) {}
- ~TestPreviewsIOData() override {}
+ const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
+ base::Clock* clock)
+ : PreviewsDeciderImpl(io_task_runner, ui_task_runner, clock),
+ initialized_(false) {}
+ ~TestPreviewsDeciderImpl() override {}
// Whether Initialize was called.
bool initialized() { return initialized_; }
- // Expose the injecting blacklist method from PreviewsIOData, and inject
+ // Expose the injecting blacklist method from PreviewsDeciderImpl, and inject
// |blacklist| into |this|.
void InjectTestBlacklist(std::unique_ptr<PreviewsBlackList> blacklist) {
SetPreviewsBlacklistForTesting(std::move(blacklist));
@@ -284,87 +305,92 @@ class TestPreviewsIOData : public PreviewsIOData {
private:
// Set |initialized_| to true and use base class functionality.
void InitializeOnIOThread(
- std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store) override {
+ std::unique_ptr<blacklist::OptOutStore> previews_opt_out_store,
+ blacklist::BlacklistData::AllowedTypesAndVersions allowed_previews)
+ override {
initialized_ = true;
- PreviewsIOData::InitializeOnIOThread(std::move(previews_opt_out_store));
+ PreviewsDeciderImpl::InitializeOnIOThread(std::move(previews_opt_out_store),
+ std::move(allowed_previews));
}
// Whether Initialize was called.
bool initialized_;
};
-void RunLoadCallback(
- LoadBlackListCallback callback,
- std::unique_ptr<BlackListItemMap> black_list_item_map,
- std::unique_ptr<PreviewsBlackListItem> host_indifferent_black_list_item) {
- callback.Run(std::move(black_list_item_map),
- std::move(host_indifferent_black_list_item));
+void RunLoadCallback(blacklist::LoadBlackListCallback callback,
+ std::unique_ptr<blacklist::BlacklistData> data) {
+ std::move(callback).Run(std::move(data));
}
-class TestPreviewsOptOutStore : public PreviewsOptOutStore {
+class TestOptOutStore : public blacklist::OptOutStore {
public:
- TestPreviewsOptOutStore() {}
- ~TestPreviewsOptOutStore() override {}
+ TestOptOutStore() {}
+ ~TestOptOutStore() override {}
private:
- // PreviewsOptOutStore implementation:
- void AddPreviewNavigation(bool opt_out,
- const std::string& host_name,
- PreviewsType type,
- base::Time now) override {}
-
- void LoadBlackList(LoadBlackListCallback callback) override {
- std::unique_ptr<BlackListItemMap> black_list_item_map(
- new BlackListItemMap());
- std::unique_ptr<PreviewsBlackListItem> host_indifferent_black_list_item =
- PreviewsBlackList::CreateHostIndifferentBlackListItem();
+ // blacklist::OptOutStore implementation:
+ void AddEntry(bool opt_out,
+ const std::string& host_name,
+ int type,
+ base::Time now) override {}
+
+ void LoadBlackList(std::unique_ptr<blacklist::BlacklistData> data,
+ blacklist::LoadBlackListCallback callback) override {
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&RunLoadCallback, callback,
- std::move(black_list_item_map),
- std::move(host_indifferent_black_list_item)));
+ FROM_HERE,
+ base::BindOnce(&RunLoadCallback, std::move(callback), std::move(data)));
}
void ClearBlackList(base::Time begin_time, base::Time end_time) override {}
};
-class PreviewsIODataTest : public testing::Test {
+class PreviewsDeciderImplTest : public testing::Test {
public:
- PreviewsIODataTest()
+ PreviewsDeciderImplTest()
: field_trial_list_(nullptr),
- io_data_(std::make_unique<TestPreviewsIOData>(
+ previews_decider_impl_(std::make_unique<TestPreviewsDeciderImpl>(
+ scoped_task_environment_.GetMainThreadTaskRunner(),
scoped_task_environment_.GetMainThreadTaskRunner(),
- scoped_task_environment_.GetMainThreadTaskRunner())),
+ &clock_)),
optimization_guide_service_(
scoped_task_environment_.GetMainThreadTaskRunner()),
context_(true) {
context_.set_network_quality_estimator(&network_quality_estimator_);
context_.Init();
+ clock_.SetNow(base::Time::Now());
network_quality_estimator_.set_effective_connection_type(
net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
}
- ~PreviewsIODataTest() override {
+ ~PreviewsDeciderImplTest() override {
// TODO(dougarnett) bug 781975: Consider switching to Feature API and
// ScopedFeatureList (and dropping components/variations dep).
variations::testing::ClearAllVariationParams();
}
void InitializeIOData() {
- io_data_ = std::make_unique<TestPreviewsIOData>(
+ previews_decider_impl_ = std::make_unique<TestPreviewsDeciderImpl>(
scoped_task_environment_.GetMainThreadTaskRunner(),
- scoped_task_environment_.GetMainThreadTaskRunner());
+ scoped_task_environment_.GetMainThreadTaskRunner(), &clock_);
}
void InitializeUIServiceWithoutWaitingForBlackList() {
+ blacklist::BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types[static_cast<int>(PreviewsType::OFFLINE)] = 0;
+ allowed_types[static_cast<int>(PreviewsType::LOFI)] = 0;
+ allowed_types[static_cast<int>(PreviewsType::LITE_PAGE)] = 0;
+ allowed_types[static_cast<int>(PreviewsType::NOSCRIPT)] = 0;
+ allowed_types[static_cast<int>(PreviewsType::RESOURCE_LOADING_HINTS)] = 0;
ui_service_.reset(new TestPreviewsUIService(
- io_data_.get(), scoped_task_environment_.GetMainThreadTaskRunner(),
- std::make_unique<TestPreviewsOptOutStore>(),
+ previews_decider_impl_.get(),
+ scoped_task_environment_.GetMainThreadTaskRunner(),
+ std::make_unique<TestOptOutStore>(),
std::make_unique<TestPreviewsOptimizationGuide>(
&optimization_guide_service_,
scoped_task_environment_.GetMainThreadTaskRunner()),
- base::Bind(&IsPreviewFieldTrialEnabled),
- std::make_unique<PreviewsLogger>()));
+ base::BindRepeating(&IsPreviewFieldTrialEnabled),
+ std::make_unique<PreviewsLogger>(), std::move(allowed_types)));
}
void InitializeUIService() {
@@ -392,31 +418,36 @@ class PreviewsIODataTest : public testing::Test {
return request;
}
- TestPreviewsIOData* io_data() { return io_data_.get(); }
+ TestPreviewsDeciderImpl* previews_decider_impl() {
+ return previews_decider_impl_.get();
+ }
TestPreviewsUIService* ui_service() { return ui_service_.get(); }
net::TestURLRequestContext* context() { return &context_; }
net::TestNetworkQualityEstimator* network_quality_estimator() {
return &network_quality_estimator_;
}
+ protected:
+ base::SimpleTestClock clock_;
+
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
base::FieldTrialList field_trial_list_;
- std::unique_ptr<TestPreviewsIOData> io_data_;
+ std::unique_ptr<TestPreviewsDeciderImpl> previews_decider_impl_;
optimization_guide::OptimizationGuideService optimization_guide_service_;
std::unique_ptr<TestPreviewsUIService> ui_service_;
net::TestNetworkQualityEstimator network_quality_estimator_;
net::TestURLRequestContext context_;
};
-TEST_F(PreviewsIODataTest, TestInitialization) {
+TEST_F(PreviewsDeciderImplTest, TestInitialization) {
InitializeUIService();
- // After the outstanding posted tasks have run, |io_data_| should be fully
- // initialized.
- EXPECT_TRUE(io_data()->initialized());
+ // After the outstanding posted tasks have run, |previews_decider_impl_|
+ // should be fully initialized.
+ EXPECT_TRUE(previews_decider_impl()->initialized());
}
-TEST_F(PreviewsIODataTest, AllPreviewsDisabledByFeature) {
+TEST_F(PreviewsDeciderImplTest, AllPreviewsDisabledByFeature) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kClientLoFi, features::kNoScriptPreviews},
@@ -426,29 +457,30 @@ TEST_F(PreviewsIODataTest, AllPreviewsDisabledByFeature) {
network_quality_estimator()->set_effective_connection_type(
net::EFFECTIVE_CONNECTION_TYPE_2G);
- EXPECT_FALSE(io_data()->ShouldAllowPreviewAtECT(
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateHttpsRequest(), PreviewsType::LOFI,
previews::params::GetECTThresholdForPreview(
previews::PreviewsType::NOSCRIPT),
- std::vector<std::string>()));
- EXPECT_FALSE(io_data()->ShouldAllowPreviewAtECT(
+ std::vector<std::string>(), false));
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateHttpsRequest(), PreviewsType::NOSCRIPT,
previews::params::GetECTThresholdForPreview(
previews::PreviewsType::NOSCRIPT),
- std::vector<std::string>()));
+ std::vector<std::string>(), false));
}
// Tests most of the reasons that a preview could be disallowed because of the
// state of the blacklist. Excluded values are USER_RECENTLY_OPTED_OUT,
// USER_BLACKLISTED, HOST_BLACKLISTED. These are internal to the blacklist.
-TEST_F(PreviewsIODataTest, TestDisallowPreviewBecauseOfBlackListState) {
+TEST_F(PreviewsDeciderImplTest, TestDisallowPreviewBecauseOfBlackListState) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kPreviews);
std::unique_ptr<net::URLRequest> request = CreateRequest();
base::HistogramTester histogram_tester;
// The blacklist is not created yet.
- EXPECT_FALSE(io_data()->ShouldAllowPreview(*request, PreviewsType::OFFLINE));
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreview(
+ *request, PreviewsType::OFFLINE));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.Offline",
static_cast<int>(PreviewsEligibilityReason::BLACKLIST_UNAVAILABLE), 1);
@@ -456,7 +488,8 @@ TEST_F(PreviewsIODataTest, TestDisallowPreviewBecauseOfBlackListState) {
InitializeUIServiceWithoutWaitingForBlackList();
// The blacklist is not created yet.
- EXPECT_FALSE(io_data()->ShouldAllowPreview(*request, PreviewsType::OFFLINE));
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreview(
+ *request, PreviewsType::OFFLINE));
histogram_tester.ExpectBucketCount(
"Previews.EligibilityReason.Offline",
static_cast<int>(PreviewsEligibilityReason::BLACKLIST_UNAVAILABLE), 2);
@@ -468,9 +501,10 @@ TEST_F(PreviewsIODataTest, TestDisallowPreviewBecauseOfBlackListState) {
// Return one of the failing statuses from the blacklist; cause the blacklist
// to not be loaded by clearing the blacklist.
base::Time now = base::Time::Now();
- io_data()->ClearBlackList(now, now);
+ previews_decider_impl()->ClearBlackList(now, now);
- EXPECT_FALSE(io_data()->ShouldAllowPreview(*request, PreviewsType::OFFLINE));
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreview(
+ *request, PreviewsType::OFFLINE));
histogram_tester.ExpectBucketCount(
"Previews.EligibilityReason.Offline",
static_cast<int>(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED),
@@ -480,7 +514,27 @@ TEST_F(PreviewsIODataTest, TestDisallowPreviewBecauseOfBlackListState) {
variations::testing::ClearAllVariationParams();
}
-TEST_F(PreviewsIODataTest, TestDisallowOfflineWhenNetworkQualityUnavailable) {
+TEST_F(PreviewsDeciderImplTest, TestSetBlacklistBoolDueToBlackListState) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(features::kPreviews);
+ std::unique_ptr<net::URLRequest> request = CreateRequest();
+ base::HistogramTester histogram_tester;
+ InitializeUIServiceWithoutWaitingForBlackList();
+ base::RunLoop().RunUntilIdle();
+ previews_decider_impl()->AddPreviewNavigation(GURL(request->url()), true,
+ PreviewsType::LITE_PAGE, 1);
+
+ auto* data =
+ PreviewsUserData::Create(request.get(), 54321 /* page_id, not used */);
+ EXPECT_FALSE(data->black_listed_for_lite_page());
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT(
+ *request, PreviewsType::LITE_PAGE, net::EFFECTIVE_CONNECTION_TYPE_2G, {},
+ false));
+ EXPECT_TRUE(data->black_listed_for_lite_page());
+}
+
+TEST_F(PreviewsDeciderImplTest,
+ TestDisallowOfflineWhenNetworkQualityUnavailable) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kPreviews);
InitializeUIService();
@@ -489,15 +543,15 @@ TEST_F(PreviewsIODataTest, TestDisallowOfflineWhenNetworkQualityUnavailable) {
net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
base::HistogramTester histogram_tester;
- EXPECT_FALSE(
- io_data()->ShouldAllowPreview(*CreateRequest(), PreviewsType::OFFLINE));
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreview(
+ *CreateRequest(), PreviewsType::OFFLINE));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.Offline",
static_cast<int>(PreviewsEligibilityReason::NETWORK_QUALITY_UNAVAILABLE),
1);
}
-TEST_F(PreviewsIODataTest, TestAllowLitePageWhenNetworkQualityFast) {
+TEST_F(PreviewsDeciderImplTest, TestAllowLitePageWhenNetworkQualityFast) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kPreviews);
InitializeUIService();
@@ -507,15 +561,15 @@ TEST_F(PreviewsIODataTest, TestAllowLitePageWhenNetworkQualityFast) {
net::EFFECTIVE_CONNECTION_TYPE_3G);
base::HistogramTester histogram_tester;
- EXPECT_TRUE(io_data()->ShouldAllowPreviewAtECT(
+ EXPECT_TRUE(previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateRequest(), PreviewsType::LITE_PAGE,
- net::EFFECTIVE_CONNECTION_TYPE_4G, std::vector<std::string>()));
+ net::EFFECTIVE_CONNECTION_TYPE_4G, std::vector<std::string>(), false));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.LitePage",
static_cast<int>(PreviewsEligibilityReason::ALLOWED), 1);
}
-TEST_F(PreviewsIODataTest, TestDisallowOfflineWhenNetworkQualityFast) {
+TEST_F(PreviewsDeciderImplTest, TestDisallowOfflineWhenNetworkQualityFast) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kPreviews);
InitializeUIService();
@@ -523,14 +577,14 @@ TEST_F(PreviewsIODataTest, TestDisallowOfflineWhenNetworkQualityFast) {
network_quality_estimator()->set_effective_connection_type(
net::EFFECTIVE_CONNECTION_TYPE_3G);
base::HistogramTester histogram_tester;
- EXPECT_FALSE(
- io_data()->ShouldAllowPreview(*CreateRequest(), PreviewsType::OFFLINE));
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreview(
+ *CreateRequest(), PreviewsType::OFFLINE));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.Offline",
static_cast<int>(PreviewsEligibilityReason::NETWORK_NOT_SLOW), 1);
}
-TEST_F(PreviewsIODataTest, TestDisallowOfflineOnReload) {
+TEST_F(PreviewsDeciderImplTest, TestDisallowOfflineOnReload) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kPreviews);
InitializeUIService();
@@ -542,13 +596,14 @@ TEST_F(PreviewsIODataTest, TestDisallowOfflineOnReload) {
request->SetLoadFlags(net::LOAD_BYPASS_CACHE);
base::HistogramTester histogram_tester;
- EXPECT_FALSE(io_data()->ShouldAllowPreview(*request, PreviewsType::OFFLINE));
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreview(
+ *request, PreviewsType::OFFLINE));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.Offline",
static_cast<int>(PreviewsEligibilityReason::RELOAD_DISALLOWED), 1);
}
-TEST_F(PreviewsIODataTest, TestAllowOffline) {
+TEST_F(PreviewsDeciderImplTest, TestAllowOffline) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kPreviews);
InitializeUIService();
@@ -568,9 +623,9 @@ TEST_F(PreviewsIODataTest, TestAllowOffline) {
test.effective_connection_type);
base::HistogramTester histogram_tester;
- EXPECT_EQ(
- test.expected_offline_allowed,
- io_data()->ShouldAllowPreview(*CreateRequest(), PreviewsType::OFFLINE))
+ EXPECT_EQ(test.expected_offline_allowed,
+ previews_decider_impl()->ShouldAllowPreview(
+ *CreateRequest(), PreviewsType::OFFLINE))
<< " effective_connection_type=" << test.effective_connection_type;
if (test.expected_offline_allowed) {
histogram_tester.ExpectUniqueSample(
@@ -584,7 +639,7 @@ TEST_F(PreviewsIODataTest, TestAllowOffline) {
}
}
-TEST_F(PreviewsIODataTest, ClientLoFiDisallowedWhenFeatureDisabled) {
+TEST_F(PreviewsDeciderImplTest, ClientLoFiDisallowedWhenFeatureDisabled) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures({features::kPreviews},
{features::kClientLoFi});
@@ -596,14 +651,15 @@ TEST_F(PreviewsIODataTest, ClientLoFiDisallowedWhenFeatureDisabled) {
net::EFFECTIVE_CONNECTION_TYPE_2G);
base::HistogramTester histogram_tester;
- EXPECT_FALSE(io_data()->ShouldAllowPreviewAtECT(
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateRequest(), PreviewsType::LOFI,
params::EffectiveConnectionTypeThresholdForClientLoFi(),
- params::GetBlackListedHostsForClientLoFiFieldTrial()));
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false));
histogram_tester.ExpectTotalCount("Previews.EligibilityReason.LoFi", 0);
}
-TEST_F(PreviewsIODataTest, ClientLoFiDisallowedWhenNetworkQualityUnavailable) {
+TEST_F(PreviewsDeciderImplTest,
+ ClientLoFiDisallowedWhenNetworkQualityUnavailable) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kClientLoFi}, {});
@@ -613,17 +669,17 @@ TEST_F(PreviewsIODataTest, ClientLoFiDisallowedWhenNetworkQualityUnavailable) {
net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
base::HistogramTester histogram_tester;
- EXPECT_FALSE(io_data()->ShouldAllowPreviewAtECT(
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateRequest(), PreviewsType::LOFI,
params::EffectiveConnectionTypeThresholdForClientLoFi(),
- params::GetBlackListedHostsForClientLoFiFieldTrial()));
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.LoFi",
static_cast<int>(PreviewsEligibilityReason::NETWORK_QUALITY_UNAVAILABLE),
1);
}
-TEST_F(PreviewsIODataTest, ClientLoFiDisallowedWhenNetworkFast) {
+TEST_F(PreviewsDeciderImplTest, ClientLoFiDisallowedWhenNetworkFast) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kClientLoFi}, {});
@@ -635,16 +691,16 @@ TEST_F(PreviewsIODataTest, ClientLoFiDisallowedWhenNetworkFast) {
net::EFFECTIVE_CONNECTION_TYPE_3G);
base::HistogramTester histogram_tester;
- EXPECT_FALSE(io_data()->ShouldAllowPreviewAtECT(
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateRequest(), PreviewsType::LOFI,
params::EffectiveConnectionTypeThresholdForClientLoFi(),
- params::GetBlackListedHostsForClientLoFiFieldTrial()));
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.LoFi",
static_cast<int>(PreviewsEligibilityReason::NETWORK_NOT_SLOW), 1);
}
-TEST_F(PreviewsIODataTest, ClientLoFiAllowed) {
+TEST_F(PreviewsDeciderImplTest, ClientLoFiAllowed) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kClientLoFi}, {});
@@ -670,10 +726,10 @@ TEST_F(PreviewsIODataTest, ClientLoFiAllowed) {
base::HistogramTester histogram_tester;
EXPECT_EQ(test.expected_client_lofi_allowed,
- io_data()->ShouldAllowPreviewAtECT(
+ previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateRequest(), PreviewsType::LOFI,
params::EffectiveConnectionTypeThresholdForClientLoFi(),
- params::GetBlackListedHostsForClientLoFiFieldTrial()))
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false))
<< " effective_connection_type=" << test.effective_connection_type;
if (test.expected_client_lofi_allowed) {
histogram_tester.ExpectUniqueSample(
@@ -687,7 +743,7 @@ TEST_F(PreviewsIODataTest, ClientLoFiAllowed) {
}
}
-TEST_F(PreviewsIODataTest, MissingHostDisallowed) {
+TEST_F(PreviewsDeciderImplTest, MissingHostDisallowed) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kClientLoFi}, {});
@@ -698,13 +754,13 @@ TEST_F(PreviewsIODataTest, MissingHostDisallowed) {
network_quality_estimator()->set_effective_connection_type(
net::EFFECTIVE_CONNECTION_TYPE_2G);
- EXPECT_FALSE(io_data()->ShouldAllowPreviewAtECT(
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateRequestWithURL(GURL("file:///sdcard")), PreviewsType::LOFI,
params::EffectiveConnectionTypeThresholdForClientLoFi(),
- params::GetBlackListedHostsForClientLoFiFieldTrial()));
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false));
}
-TEST_F(PreviewsIODataTest, ClientLoFiAllowedOnReload) {
+TEST_F(PreviewsDeciderImplTest, ClientLoFiAllowedOnReload) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kClientLoFi}, {});
@@ -719,16 +775,16 @@ TEST_F(PreviewsIODataTest, ClientLoFiAllowedOnReload) {
request->SetLoadFlags(net::LOAD_BYPASS_CACHE);
base::HistogramTester histogram_tester;
- EXPECT_TRUE(io_data()->ShouldAllowPreviewAtECT(
+ EXPECT_TRUE(previews_decider_impl()->ShouldAllowPreviewAtECT(
*request, PreviewsType::LOFI,
params::EffectiveConnectionTypeThresholdForClientLoFi(),
- params::GetBlackListedHostsForClientLoFiFieldTrial()));
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.LoFi",
static_cast<int>(PreviewsEligibilityReason::ALLOWED), 1);
}
-TEST_F(PreviewsIODataTest, ClientLoFiObeysHostBlackListFromServer) {
+TEST_F(PreviewsDeciderImplTest, ClientLoFiObeysHostBlackListFromServer) {
base::test::ScopedFeatureList scoped_previews_feature_list;
scoped_previews_feature_list.InitAndEnableFeature(features::kPreviews);
@@ -763,10 +819,10 @@ TEST_F(PreviewsIODataTest, ClientLoFiObeysHostBlackListFromServer) {
PreviewsUserData::Create(request.get(), 54321 /* page_id, not used */);
EXPECT_EQ(test.expected_client_lofi_allowed,
- io_data()->ShouldAllowPreviewAtECT(
+ previews_decider_impl()->ShouldAllowPreviewAtECT(
*request, PreviewsType::LOFI,
params::EffectiveConnectionTypeThresholdForClientLoFi(),
- params::GetBlackListedHostsForClientLoFiFieldTrial()));
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.LoFi",
@@ -778,7 +834,7 @@ TEST_F(PreviewsIODataTest, ClientLoFiObeysHostBlackListFromServer) {
}
}
-TEST_F(PreviewsIODataTest, NoScriptDisallowedByDefault) {
+TEST_F(PreviewsDeciderImplTest, NoScriptDisallowedByDefault) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kPreviews);
InitializeUIService();
@@ -787,15 +843,15 @@ TEST_F(PreviewsIODataTest, NoScriptDisallowedByDefault) {
net::EFFECTIVE_CONNECTION_TYPE_2G);
base::HistogramTester histogram_tester;
- EXPECT_FALSE(io_data()->ShouldAllowPreviewAtECT(
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateRequest(), PreviewsType::NOSCRIPT,
previews::params::GetECTThresholdForPreview(
previews::PreviewsType::NOSCRIPT),
- std::vector<std::string>()));
+ std::vector<std::string>(), false));
histogram_tester.ExpectTotalCount("Previews.EligibilityReason.NoScript", 0);
}
-TEST_F(PreviewsIODataTest, NoScriptAllowedByFeature) {
+TEST_F(PreviewsDeciderImplTest, NoScriptAllowedByFeature) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kNoScriptPreviews}, {});
@@ -818,11 +874,11 @@ TEST_F(PreviewsIODataTest, NoScriptAllowedByFeature) {
base::HistogramTester histogram_tester;
EXPECT_EQ(test.expected_noscript_allowed,
- io_data()->ShouldAllowPreviewAtECT(
+ previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateHttpsRequest(), PreviewsType::NOSCRIPT,
previews::params::GetECTThresholdForPreview(
previews::PreviewsType::NOSCRIPT),
- std::vector<std::string>()))
+ std::vector<std::string>(), false))
<< " effective_connection_type=" << test.effective_connection_type;
if (test.expected_noscript_allowed) {
histogram_tester.ExpectUniqueSample(
@@ -840,7 +896,7 @@ TEST_F(PreviewsIODataTest, NoScriptAllowedByFeature) {
}
}
-TEST_F(PreviewsIODataTest, NoScriptAllowedByFeatureWithWhitelist) {
+TEST_F(PreviewsDeciderImplTest, NoScriptAllowedByFeatureWithWhitelist) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kNoScriptPreviews,
@@ -854,11 +910,11 @@ TEST_F(PreviewsIODataTest, NoScriptAllowedByFeatureWithWhitelist) {
base::HistogramTester histogram_tester;
// First verify no preview for non-whitelisted url.
- EXPECT_FALSE(io_data()->ShouldAllowPreviewAtECT(
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateHttpsRequest(), PreviewsType::NOSCRIPT,
previews::params::GetECTThresholdForPreview(
previews::PreviewsType::NOSCRIPT),
- std::vector<std::string>()));
+ std::vector<std::string>(), false));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.NoScript",
@@ -867,19 +923,19 @@ TEST_F(PreviewsIODataTest, NoScriptAllowedByFeatureWithWhitelist) {
1);
// Now verify preview for whitelisted url.
- EXPECT_TRUE(io_data()->ShouldAllowPreviewAtECT(
+ EXPECT_TRUE(previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateRequestWithURL(GURL("https://whitelisted.example.com")),
PreviewsType::NOSCRIPT,
previews::params::GetECTThresholdForPreview(
previews::PreviewsType::NOSCRIPT),
- std::vector<std::string>()));
+ std::vector<std::string>(), false));
histogram_tester.ExpectBucketCount(
"Previews.EligibilityReason.NoScript",
static_cast<int>(PreviewsEligibilityReason::ALLOWED), 1);
}
-TEST_F(PreviewsIODataTest, NoScriptCommitTimeWhitelistCheck) {
+TEST_F(PreviewsDeciderImplTest, NoScriptCommitTimeWhitelistCheck) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kNoScriptPreviews,
@@ -893,8 +949,8 @@ TEST_F(PreviewsIODataTest, NoScriptCommitTimeWhitelistCheck) {
// First verify not allowed for non-whitelisted url.
{
base::HistogramTester histogram_tester;
- EXPECT_FALSE(io_data()->IsURLAllowedForPreview(*CreateHttpsRequest(),
- PreviewsType::NOSCRIPT));
+ EXPECT_FALSE(previews_decider_impl()->IsURLAllowedForPreview(
+ *CreateHttpsRequest(), PreviewsType::NOSCRIPT));
histogram_tester.ExpectUniqueSample(
"Previews.EligibilityReason.NoScript",
@@ -906,7 +962,7 @@ TEST_F(PreviewsIODataTest, NoScriptCommitTimeWhitelistCheck) {
// Now verify preview for whitelisted url.
{
base::HistogramTester histogram_tester;
- EXPECT_TRUE(io_data()->IsURLAllowedForPreview(
+ EXPECT_TRUE(previews_decider_impl()->IsURLAllowedForPreview(
*CreateRequestWithURL(GURL("https://whitelisted.example.com")),
PreviewsType::NOSCRIPT));
@@ -915,7 +971,226 @@ TEST_F(PreviewsIODataTest, NoScriptCommitTimeWhitelistCheck) {
}
}
-TEST_F(PreviewsIODataTest, LogPreviewNavigationPassInCorrectParams) {
+TEST_F(PreviewsDeciderImplTest, ResourceLoadingHintsDisallowedByDefault) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {features::kPreviews, features::kResourceLoadingHints}, {});
+ InitializeUIService();
+
+ network_quality_estimator()->set_effective_connection_type(
+ net::EFFECTIVE_CONNECTION_TYPE_2G);
+
+ base::HistogramTester histogram_tester;
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT(
+ *CreateRequest(), PreviewsType::RESOURCE_LOADING_HINTS,
+ previews::params::GetECTThresholdForPreview(
+ previews::PreviewsType::RESOURCE_LOADING_HINTS),
+ std::vector<std::string>(), false));
+ histogram_tester.ExpectUniqueSample(
+ "Previews.EligibilityReason.ResourceLoadingHints",
+ static_cast<int>(
+ PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER),
+ 1);
+}
+
+TEST_F(PreviewsDeciderImplTest,
+ ResourceLoadingHintsDisallowedWithoutOptimizationHints) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {features::kPreviews, features::kResourceLoadingHints}, {});
+ InitializeUIService();
+
+ network_quality_estimator()->set_effective_connection_type(
+ net::EFFECTIVE_CONNECTION_TYPE_2G);
+
+ base::HistogramTester histogram_tester;
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT(
+ *CreateRequestWithURL(GURL("https://whitelisted.example.com")),
+ PreviewsType::RESOURCE_LOADING_HINTS,
+ previews::params::GetECTThresholdForPreview(
+ previews::PreviewsType::RESOURCE_LOADING_HINTS),
+ std::vector<std::string>(), false));
+ histogram_tester.ExpectUniqueSample(
+ "Previews.EligibilityReason.ResourceLoadingHints",
+ static_cast<int>(
+ PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER),
+ 1);
+}
+
+TEST_F(PreviewsDeciderImplTest, ResourceLoadingHintsAllowedByFeature) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {features::kPreviews, features::kResourceLoadingHints,
+ features::kOptimizationHints},
+ {});
+ InitializeUIService();
+
+ const struct {
+ net::EffectiveConnectionType effective_connection_type;
+ bool expected_resource_loading_hints_allowed;
+ } tests[] = {
+ {net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN, false},
+ {net::EFFECTIVE_CONNECTION_TYPE_OFFLINE, false},
+ {net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G, true},
+ {net::EFFECTIVE_CONNECTION_TYPE_2G, true},
+ {net::EFFECTIVE_CONNECTION_TYPE_3G, false},
+ };
+
+ for (const auto& test : tests) {
+ network_quality_estimator()->set_effective_connection_type(
+ test.effective_connection_type);
+
+ base::HistogramTester histogram_tester;
+
+ // Check whitelisted URL.
+ EXPECT_EQ(
+ test.expected_resource_loading_hints_allowed,
+ previews_decider_impl()->ShouldAllowPreviewAtECT(
+ *CreateRequestWithURL(GURL("https://whitelisted.example.com")),
+ PreviewsType::RESOURCE_LOADING_HINTS,
+ previews::params::GetECTThresholdForPreview(
+ previews::PreviewsType::RESOURCE_LOADING_HINTS),
+ std::vector<std::string>(), false))
+ << " effective_connection_type=" << test.effective_connection_type;
+ if (test.expected_resource_loading_hints_allowed) {
+ histogram_tester.ExpectUniqueSample(
+ "Previews.EligibilityReason.ResourceLoadingHints",
+ static_cast<int>(PreviewsEligibilityReason::ALLOWED), 1);
+ } else if (test.effective_connection_type ==
+ net::EFFECTIVE_CONNECTION_TYPE_3G) {
+ histogram_tester.ExpectBucketCount(
+ "Previews.EligibilityReason.ResourceLoadingHints",
+ static_cast<int>(PreviewsEligibilityReason::NETWORK_NOT_SLOW), 1);
+ } else {
+ histogram_tester.ExpectBucketCount(
+ "Previews.EligibilityReason.ResourceLoadingHints",
+ static_cast<int>(
+ PreviewsEligibilityReason::NETWORK_QUALITY_UNAVAILABLE),
+ 1);
+ }
+ }
+}
+
+TEST_F(PreviewsDeciderImplTest,
+ ResourceLoadingHintsAllowedByFeatureWithWhitelist) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {features::kPreviews, features::kResourceLoadingHints,
+ features::kOptimizationHints},
+ {});
+ InitializeUIService();
+
+ network_quality_estimator()->set_effective_connection_type(
+ net::EFFECTIVE_CONNECTION_TYPE_2G);
+
+ base::HistogramTester histogram_tester;
+
+ // First verify no preview for non-whitelisted url.
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT(
+ *CreateHttpsRequest(), PreviewsType::RESOURCE_LOADING_HINTS,
+ previews::params::GetECTThresholdForPreview(
+ previews::PreviewsType::RESOURCE_LOADING_HINTS),
+ std::vector<std::string>(), false));
+
+ histogram_tester.ExpectUniqueSample(
+ "Previews.EligibilityReason.ResourceLoadingHints",
+ static_cast<int>(
+ PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER),
+ 1);
+
+ // Now verify preview for whitelisted url.
+ EXPECT_TRUE(previews_decider_impl()->ShouldAllowPreviewAtECT(
+ *CreateRequestWithURL(GURL("https://whitelisted.example.com")),
+ PreviewsType::RESOURCE_LOADING_HINTS,
+ previews::params::GetECTThresholdForPreview(
+ previews::PreviewsType::RESOURCE_LOADING_HINTS),
+ std::vector<std::string>(), false));
+
+ histogram_tester.ExpectBucketCount(
+ "Previews.EligibilityReason.ResourceLoadingHints",
+ static_cast<int>(PreviewsEligibilityReason::ALLOWED), 1);
+}
+
+TEST_F(PreviewsDeciderImplTest, ResourceLoadingHintsCommitTimeWhitelistCheck) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {features::kPreviews, features::kResourceLoadingHints,
+ features::kOptimizationHints},
+ {});
+ InitializeUIService();
+
+ network_quality_estimator()->set_effective_connection_type(
+ net::EFFECTIVE_CONNECTION_TYPE_2G);
+
+ // First verify not allowed for non-whitelisted url.
+ {
+ base::HistogramTester histogram_tester;
+ EXPECT_FALSE(previews_decider_impl()->IsURLAllowedForPreview(
+ *CreateHttpsRequest(), PreviewsType::RESOURCE_LOADING_HINTS));
+
+ histogram_tester.ExpectUniqueSample(
+ "Previews.EligibilityReason.ResourceLoadingHints",
+ static_cast<int>(
+ PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER),
+ 1);
+ }
+
+ // Now verify preview for whitelisted url.
+ {
+ base::HistogramTester histogram_tester;
+ EXPECT_TRUE(previews_decider_impl()->IsURLAllowedForPreview(
+ *CreateRequestWithURL(GURL("https://whitelisted.example.com")),
+ PreviewsType::RESOURCE_LOADING_HINTS));
+
+ // Expect no eligibility logging.
+ histogram_tester.ExpectTotalCount(
+ "Previews.EligibilityReason.ResourceLoadingHints", 0);
+ }
+}
+
+TEST_F(PreviewsDeciderImplTest,
+ ResourceLoadingHintsAndNoScriptAllowedByFeatureWithWhitelist) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {features::kPreviews, features::kResourceLoadingHints,
+ features::kOptimizationHints, features::kNoScriptPreviews},
+ {});
+ InitializeUIService();
+
+ network_quality_estimator()->set_effective_connection_type(
+ net::EFFECTIVE_CONNECTION_TYPE_2G);
+
+ base::HistogramTester histogram_tester;
+
+ // Now verify preview for url that's whitelisted only for NoScript.
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT(
+ *CreateRequestWithURL(
+ GURL("https://noscript_only_whitelisted.example.com")),
+ PreviewsType::RESOURCE_LOADING_HINTS,
+ previews::params::GetECTThresholdForPreview(
+ previews::PreviewsType::RESOURCE_LOADING_HINTS),
+ std::vector<std::string>(), false));
+
+ histogram_tester.ExpectBucketCount(
+ "Previews.EligibilityReason.ResourceLoadingHints",
+ static_cast<int>(
+ PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER),
+ 1);
+
+ EXPECT_TRUE(previews_decider_impl()->ShouldAllowPreviewAtECT(
+ *CreateRequestWithURL(
+ GURL("https://noscript_only_whitelisted.example.com")),
+ PreviewsType::NOSCRIPT,
+ previews::params::GetECTThresholdForPreview(
+ previews::PreviewsType::NOSCRIPT),
+ std::vector<std::string>(), false));
+
+ histogram_tester.ExpectBucketCount(
+ "Previews.EligibilityReason.NoScript",
+ static_cast<int>(PreviewsEligibilityReason::ALLOWED), 1);
+}
+
+TEST_F(PreviewsDeciderImplTest, LogPreviewNavigationPassInCorrectParams) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kPreviews);
InitializeUIService();
@@ -925,7 +1200,8 @@ TEST_F(PreviewsIODataTest, LogPreviewNavigationPassInCorrectParams) {
const base::Time time = base::Time::Now();
const uint64_t page_id = 1234;
- io_data()->LogPreviewNavigation(url, opt_out, type, time, page_id);
+ previews_decider_impl()->LogPreviewNavigation(url, opt_out, type, time,
+ page_id);
base::RunLoop().RunUntilIdle();
EXPECT_THAT(ui_service()->navigation_urls(), ::testing::ElementsAre(url));
@@ -937,7 +1213,7 @@ TEST_F(PreviewsIODataTest, LogPreviewNavigationPassInCorrectParams) {
::testing::ElementsAre(page_id));
}
-TEST_F(PreviewsIODataTest, LogPreviewDecisionMadePassInCorrectParams) {
+TEST_F(PreviewsDeciderImplTest, LogPreviewDecisionMadePassInCorrectParams) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kPreviews);
InitializeUIService();
@@ -955,8 +1231,8 @@ TEST_F(PreviewsIODataTest, LogPreviewDecisionMadePassInCorrectParams) {
passed_reasons);
const uint64_t page_id = 1234;
- io_data()->LogPreviewDecisionMade(reason, url, time, type,
- std::move(passed_reasons), page_id);
+ previews_decider_impl()->LogPreviewDecisionMade(
+ reason, url, time, type, std::move(passed_reasons), page_id);
base::RunLoop().RunUntilIdle();
EXPECT_THAT(ui_service()->decision_reasons(), ::testing::ElementsAre(reason));
@@ -973,7 +1249,7 @@ TEST_F(PreviewsIODataTest, LogPreviewDecisionMadePassInCorrectParams) {
}
} // namespace
-TEST_F(PreviewsIODataTest, LogDecisionMadeBlacklistNotAvailable) {
+TEST_F(PreviewsDeciderImplTest, LogDecisionMadeBlacklistNotAvailable) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kClientLoFi}, {});
@@ -982,10 +1258,10 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeBlacklistNotAvailable) {
auto expected_reason = PreviewsEligibilityReason::BLACKLIST_UNAVAILABLE;
auto expected_type = PreviewsType::LOFI;
- io_data()->InjectTestBlacklist(nullptr /* blacklist */);
- io_data()->ShouldAllowPreviewAtECT(*CreateRequest(), expected_type,
- net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
- {});
+ previews_decider_impl()->InjectTestBlacklist(nullptr /* blacklist */);
+ previews_decider_impl()->ShouldAllowPreviewAtECT(
+ *CreateRequest(), expected_type, net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
+ {}, false);
base::RunLoop().RunUntilIdle();
// Testing correct log method is called.
EXPECT_THAT(ui_service()->decision_reasons(),
@@ -994,7 +1270,7 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeBlacklistNotAvailable) {
::testing::Contains(expected_type));
}
-TEST_F(PreviewsIODataTest, LogDecisionMadeBlacklistStatusesDefault) {
+TEST_F(PreviewsDeciderImplTest, LogDecisionMadeBlacklistStatusesDefault) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kClientLoFi}, {});
@@ -1014,12 +1290,13 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeBlacklistStatusesDefault) {
auto expected_reason = expected_reasons[i];
std::unique_ptr<TestPreviewsBlackList> blacklist =
- std::make_unique<TestPreviewsBlackList>(expected_reason, io_data());
- io_data()->InjectTestBlacklist(std::move(blacklist));
+ std::make_unique<TestPreviewsBlackList>(expected_reason,
+ previews_decider_impl());
+ previews_decider_impl()->InjectTestBlacklist(std::move(blacklist));
- io_data()->ShouldAllowPreviewAtECT(*CreateRequest(), expected_type,
- net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
- {});
+ previews_decider_impl()->ShouldAllowPreviewAtECT(
+ *CreateRequest(), expected_type, net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
+ {}, false);
base::RunLoop().RunUntilIdle();
// Testing correct log method is called.
// Check for all decision upto current decision is logged.
@@ -1032,7 +1309,7 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeBlacklistStatusesDefault) {
}
}
-TEST_F(PreviewsIODataTest, IsURLAllowedForPreviewBlacklistStatuses) {
+TEST_F(PreviewsDeciderImplTest, IsURLAllowedForPreviewBlacklistStatuses) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kNoScriptPreviews}, {});
@@ -1040,8 +1317,8 @@ TEST_F(PreviewsIODataTest, IsURLAllowedForPreviewBlacklistStatuses) {
auto expected_type = PreviewsType::NOSCRIPT;
// First verify URL is allowed for no blacklist status.
- EXPECT_TRUE(
- io_data()->IsURLAllowedForPreview(*CreateRequest(), expected_type));
+ EXPECT_TRUE(previews_decider_impl()->IsURLAllowedForPreview(*CreateRequest(),
+ expected_type));
PreviewsEligibilityReason expected_reasons[] = {
PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
@@ -1056,11 +1333,12 @@ TEST_F(PreviewsIODataTest, IsURLAllowedForPreviewBlacklistStatuses) {
auto expected_reason = expected_reasons[i];
std::unique_ptr<TestPreviewsBlackList> blacklist =
- std::make_unique<TestPreviewsBlackList>(expected_reason, io_data());
- io_data()->InjectTestBlacklist(std::move(blacklist));
+ std::make_unique<TestPreviewsBlackList>(expected_reason,
+ previews_decider_impl());
+ previews_decider_impl()->InjectTestBlacklist(std::move(blacklist));
- EXPECT_FALSE(
- io_data()->IsURLAllowedForPreview(*CreateRequest(), expected_type));
+ EXPECT_FALSE(previews_decider_impl()->IsURLAllowedForPreview(
+ *CreateRequest(), expected_type));
base::RunLoop().RunUntilIdle();
// Testing correct log method is called.
// Check for all decision upto current decision is logged.
@@ -1073,7 +1351,7 @@ TEST_F(PreviewsIODataTest, IsURLAllowedForPreviewBlacklistStatuses) {
}
}
-TEST_F(PreviewsIODataTest, LogDecisionMadeBlacklistStatusesIgnore) {
+TEST_F(PreviewsDeciderImplTest, LogDecisionMadeBlacklistStatusesIgnore) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kClientLoFi}, {});
@@ -1090,17 +1368,19 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeBlacklistStatusesIgnore) {
PreviewsEligibilityReason::HOST_BLACKLISTED,
};
- io_data()->SetIgnorePreviewsBlacklistDecision(true /* ignored */);
+ previews_decider_impl()->SetIgnorePreviewsBlacklistDecision(
+ true /* ignored */);
for (auto blacklist_decision : blacklist_decisions) {
std::unique_ptr<TestPreviewsBlackList> blacklist =
- std::make_unique<TestPreviewsBlackList>(blacklist_decision, io_data());
- io_data()->InjectTestBlacklist(std::move(blacklist));
+ std::make_unique<TestPreviewsBlackList>(blacklist_decision,
+ previews_decider_impl());
+ previews_decider_impl()->InjectTestBlacklist(std::move(blacklist));
- io_data()->ShouldAllowPreviewAtECT(
+ previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateRequest(), expected_type,
params::EffectiveConnectionTypeThresholdForClientLoFi(),
- params::GetBlackListedHostsForClientLoFiFieldTrial());
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false);
base::RunLoop().RunUntilIdle();
// Testing correct log method is called.
@@ -1111,15 +1391,47 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeBlacklistStatusesIgnore) {
}
}
-TEST_F(PreviewsIODataTest, LogDecisionMadeNetworkQualityNotAvailable) {
+TEST_F(PreviewsDeciderImplTest, IgnoreFlagStillHasFiveSecondRule) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures(
+ {features::kPreviews, features::kClientLoFi}, {});
+ InitializeUIService();
+ network_quality_estimator()->set_effective_connection_type(
+ net::EFFECTIVE_CONNECTION_TYPE_2G);
+
+ previews_decider_impl()->SetIgnorePreviewsBlacklistDecision(
+ true /* ignored */);
+
+ EXPECT_TRUE(previews_decider_impl()->ShouldAllowPreviewAtECT(
+ *CreateRequest(), PreviewsType::LOFI,
+ params::EffectiveConnectionTypeThresholdForClientLoFi(),
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false));
+
+ previews_decider_impl()->AddPreviewNavigation(
+ GURL("http://wwww.somedomain.com"), true, PreviewsType::LOFI, 1);
+
+ EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT(
+ *CreateRequest(), PreviewsType::LOFI,
+ params::EffectiveConnectionTypeThresholdForClientLoFi(),
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false));
+
+ clock_.Advance(base::TimeDelta::FromSeconds(6));
+
+ EXPECT_TRUE(previews_decider_impl()->ShouldAllowPreviewAtECT(
+ *CreateRequest(), PreviewsType::LOFI,
+ params::EffectiveConnectionTypeThresholdForClientLoFi(),
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false));
+}
+
+TEST_F(PreviewsDeciderImplTest, LogDecisionMadeNetworkQualityNotAvailable) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kClientLoFi}, {});
InitializeUIService();
std::unique_ptr<TestPreviewsBlackList> blacklist =
std::make_unique<TestPreviewsBlackList>(
- PreviewsEligibilityReason::ALLOWED, io_data());
- io_data()->InjectTestBlacklist(std::move(blacklist));
+ PreviewsEligibilityReason::ALLOWED, previews_decider_impl());
+ previews_decider_impl()->InjectTestBlacklist(std::move(blacklist));
auto expected_reason = PreviewsEligibilityReason::NETWORK_QUALITY_UNAVAILABLE;
auto expected_type = PreviewsType::LOFI;
@@ -1135,10 +1447,10 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeNetworkQualityNotAvailable) {
network_quality_estimator()->set_effective_connection_type(
net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
- io_data()->ShouldAllowPreviewAtECT(
+ previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateRequest(), expected_type,
params::EffectiveConnectionTypeThresholdForClientLoFi(),
- params::GetBlackListedHostsForClientLoFiFieldTrial());
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false);
base::RunLoop().RunUntilIdle();
// Testing correct log method is called.
@@ -1156,15 +1468,15 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeNetworkQualityNotAvailable) {
}
}
-TEST_F(PreviewsIODataTest, LogDecisionMadeNetworkNotSlow) {
+TEST_F(PreviewsDeciderImplTest, LogDecisionMadeNetworkNotSlow) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kClientLoFi}, {});
InitializeUIService();
std::unique_ptr<TestPreviewsBlackList> blacklist =
std::make_unique<TestPreviewsBlackList>(
- PreviewsEligibilityReason::ALLOWED, io_data());
- io_data()->InjectTestBlacklist(std::move(blacklist));
+ PreviewsEligibilityReason::ALLOWED, previews_decider_impl());
+ previews_decider_impl()->InjectTestBlacklist(std::move(blacklist));
network_quality_estimator()->set_effective_connection_type(
net::EFFECTIVE_CONNECTION_TYPE_4G);
@@ -1181,9 +1493,9 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeNetworkNotSlow) {
PreviewsEligibilityReason::NETWORK_QUALITY_UNAVAILABLE,
};
- io_data()->ShouldAllowPreviewAtECT(
+ previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateRequest(), expected_type,
- net::EFFECTIVE_CONNECTION_TYPE_2G /* threshold */, {});
+ net::EFFECTIVE_CONNECTION_TYPE_2G /* threshold */, {}, false);
base::RunLoop().RunUntilIdle();
// Testing correct log method is called.
EXPECT_THAT(ui_service()->decision_reasons(),
@@ -1200,7 +1512,7 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeNetworkNotSlow) {
}
}
-TEST_F(PreviewsIODataTest, LogDecisionMadeHostBlacklisted) {
+TEST_F(PreviewsDeciderImplTest, LogDecisionMadeHostBlacklisted) {
base::test::ScopedFeatureList scoped_previews_feature_list;
scoped_previews_feature_list.InitAndEnableFeature(features::kPreviews);
@@ -1212,8 +1524,8 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeHostBlacklisted) {
InitializeUIService();
std::unique_ptr<TestPreviewsBlackList> blacklist =
std::make_unique<TestPreviewsBlackList>(
- PreviewsEligibilityReason::ALLOWED, io_data());
- io_data()->InjectTestBlacklist(std::move(blacklist));
+ PreviewsEligibilityReason::ALLOWED, previews_decider_impl());
+ previews_decider_impl()->InjectTestBlacklist(std::move(blacklist));
network_quality_estimator()->set_effective_connection_type(
net::EFFECTIVE_CONNECTION_TYPE_2G);
@@ -1232,10 +1544,10 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeHostBlacklisted) {
PreviewsEligibilityReason::RELOAD_DISALLOWED,
};
- io_data()->ShouldAllowPreviewAtECT(
+ previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateRequest(), expected_type,
params::EffectiveConnectionTypeThresholdForClientLoFi(),
- params::GetBlackListedHostsForClientLoFiFieldTrial());
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false);
base::RunLoop().RunUntilIdle();
// Testing correct log method is called.
@@ -1253,14 +1565,14 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeHostBlacklisted) {
}
}
-TEST_F(PreviewsIODataTest, LogDecisionMadeReloadDisallowed) {
+TEST_F(PreviewsDeciderImplTest, LogDecisionMadeReloadDisallowed) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(features::kPreviews);
InitializeUIService();
std::unique_ptr<TestPreviewsBlackList> blacklist =
std::make_unique<TestPreviewsBlackList>(
- PreviewsEligibilityReason::ALLOWED, io_data());
- io_data()->InjectTestBlacklist(std::move(blacklist));
+ PreviewsEligibilityReason::ALLOWED, previews_decider_impl());
+ previews_decider_impl()->InjectTestBlacklist(std::move(blacklist));
network_quality_estimator()->set_effective_connection_type(
net::EFFECTIVE_CONNECTION_TYPE_2G);
@@ -1280,10 +1592,10 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeReloadDisallowed) {
PreviewsEligibilityReason::NETWORK_NOT_SLOW,
};
- io_data()->ShouldAllowPreviewAtECT(
+ previews_decider_impl()->ShouldAllowPreviewAtECT(
*request, expected_type,
params::EffectiveConnectionTypeThresholdForClientLoFi(),
- params::GetBlackListedHostsForClientLoFiFieldTrial());
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false);
base::RunLoop().RunUntilIdle();
// Testing correct log method is called.
@@ -1301,7 +1613,7 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeReloadDisallowed) {
}
}
-TEST_F(PreviewsIODataTest, IgnoreBlacklistEnabledViaFlag) {
+TEST_F(PreviewsDeciderImplTest, IgnoreBlacklistEnabledViaFlag) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kClientLoFi}, {});
@@ -1316,23 +1628,23 @@ TEST_F(PreviewsIODataTest, IgnoreBlacklistEnabledViaFlag) {
std::unique_ptr<TestPreviewsBlackList> blacklist =
std::make_unique<TestPreviewsBlackList>(
- PreviewsEligibilityReason::HOST_BLACKLISTED, io_data());
- io_data()->InjectTestBlacklist(std::move(blacklist));
+ PreviewsEligibilityReason::HOST_BLACKLISTED, previews_decider_impl());
+ previews_decider_impl()->InjectTestBlacklist(std::move(blacklist));
network_quality_estimator()->set_effective_connection_type(
net::EFFECTIVE_CONNECTION_TYPE_2G);
auto expected_reason = PreviewsEligibilityReason::ALLOWED;
- EXPECT_TRUE(io_data()->ShouldAllowPreviewAtECT(
+ EXPECT_TRUE(previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateRequest(), PreviewsType::LOFI,
params::EffectiveConnectionTypeThresholdForClientLoFi(),
- params::GetBlackListedHostsForClientLoFiFieldTrial()));
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false));
base::RunLoop().RunUntilIdle();
EXPECT_THAT(ui_service()->decision_reasons(),
::testing::Contains(expected_reason));
}
-TEST_F(PreviewsIODataTest, LogDecisionMadeAllowPreviewsOnECT) {
+TEST_F(PreviewsDeciderImplTest, LogDecisionMadeAllowPreviewsOnECT) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kPreviews, features::kClientLoFi}, {});
@@ -1340,9 +1652,9 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeAllowPreviewsOnECT) {
std::unique_ptr<TestPreviewsBlackList> blacklist =
std::make_unique<TestPreviewsBlackList>(
- PreviewsEligibilityReason::ALLOWED, io_data());
+ PreviewsEligibilityReason::ALLOWED, previews_decider_impl());
- io_data()->InjectTestBlacklist(std::move(blacklist));
+ previews_decider_impl()->InjectTestBlacklist(std::move(blacklist));
network_quality_estimator()->set_effective_connection_type(
net::EFFECTIVE_CONNECTION_TYPE_2G);
@@ -1362,10 +1674,10 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeAllowPreviewsOnECT) {
PreviewsEligibilityReason::HOST_BLACKLISTED_BY_SERVER,
};
- io_data()->ShouldAllowPreviewAtECT(
+ previews_decider_impl()->ShouldAllowPreviewAtECT(
*CreateRequest(), expected_type,
params::EffectiveConnectionTypeThresholdForClientLoFi(),
- params::GetBlackListedHostsForClientLoFiFieldTrial());
+ params::GetBlackListedHostsForClientLoFiFieldTrial(), false);
base::RunLoop().RunUntilIdle();
// Testing correct log method is called.
@@ -1383,57 +1695,61 @@ TEST_F(PreviewsIODataTest, LogDecisionMadeAllowPreviewsOnECT) {
}
}
-TEST_F(PreviewsIODataTest, OnNewBlacklistedHostCallsUIMethodCorrectly) {
+TEST_F(PreviewsDeciderImplTest, OnNewBlacklistedHostCallsUIMethodCorrectly) {
InitializeUIService();
std::string expected_host = "example.com";
base::Time expected_time = base::Time::Now();
- io_data()->OnNewBlacklistedHost(expected_host, expected_time);
+ previews_decider_impl()->OnNewBlacklistedHost(expected_host, expected_time);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(expected_host, ui_service()->host_blacklisted());
EXPECT_EQ(expected_time, ui_service()->host_blacklisted_time());
}
-TEST_F(PreviewsIODataTest, OnUserBlacklistedCallsUIMethodCorrectly) {
+TEST_F(PreviewsDeciderImplTest, OnUserBlacklistedCallsUIMethodCorrectly) {
InitializeUIService();
- io_data()->OnUserBlacklistedStatusChange(true /* blacklisted */);
+ previews_decider_impl()->OnUserBlacklistedStatusChange(
+ true /* blacklisted */);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(ui_service()->user_blacklisted());
- io_data()->OnUserBlacklistedStatusChange(false /* blacklisted */);
+ previews_decider_impl()->OnUserBlacklistedStatusChange(
+ false /* blacklisted */);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(ui_service()->user_blacklisted());
}
-TEST_F(PreviewsIODataTest, OnBlacklistClearedCallsUIMethodCorrectly) {
+TEST_F(PreviewsDeciderImplTest, OnBlacklistClearedCallsUIMethodCorrectly) {
InitializeUIService();
base::Time expected_time = base::Time::Now();
- io_data()->OnBlacklistCleared(expected_time);
+ previews_decider_impl()->OnBlacklistCleared(expected_time);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(expected_time, ui_service()->blacklist_cleared_time());
}
-TEST_F(PreviewsIODataTest,
+TEST_F(PreviewsDeciderImplTest,
OnIgnoreBlacklistDecisionStatusChangedCalledCorrect) {
InitializeUIService();
- io_data()->SetIgnorePreviewsBlacklistDecision(true /* ignored */);
+ previews_decider_impl()->SetIgnorePreviewsBlacklistDecision(
+ true /* ignored */);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(ui_service()->blacklist_ignored());
- io_data()->SetIgnorePreviewsBlacklistDecision(false /* ignored */);
+ previews_decider_impl()->SetIgnorePreviewsBlacklistDecision(
+ false /* ignored */);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(ui_service()->blacklist_ignored());
}
-TEST_F(PreviewsIODataTest, GeneratePageIdMakesUniqueNonZero) {
+TEST_F(PreviewsDeciderImplTest, GeneratePageIdMakesUniqueNonZero) {
InitializeUIService();
std::unordered_set<uint64_t> page_id_set;
size_t number_of_generated_ids = 10;
for (size_t i = 0; i < number_of_generated_ids; i++) {
- page_id_set.insert(io_data()->GeneratePageId());
+ page_id_set.insert(previews_decider_impl()->GeneratePageId());
}
EXPECT_EQ(number_of_generated_ids, page_id_set.size());
EXPECT_EQ(page_id_set.end(), page_id_set.find(0u));
diff --git a/chromium/components/previews/content/previews_hints.cc b/chromium/components/previews/content/previews_hints.cc
new file mode 100644
index 00000000000..2a1773dcda1
--- /dev/null
+++ b/chromium/components/previews/content/previews_hints.cc
@@ -0,0 +1,264 @@
+// 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/previews/content/previews_hints.h"
+
+#include <memory>
+#include <string>
+
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/optional.h"
+#include "components/optimization_guide/optimization_guide_service_observer.h"
+#include "components/previews/core/previews_features.h"
+
+namespace previews {
+
+namespace {
+
+// Name of sentinel file to guard potential crash loops while processing
+// the config into hints. It holds the version of the config that is/was
+// being processed into hints.
+const base::FilePath::CharType kSentinelFileName[] =
+ FILE_PATH_LITERAL("previews_config_sentinel.txt");
+
+// Creates the sentinel file (at |sentinel_path|) to persistently mark the
+// beginning of processing the configuration data for Previews hints. It
+// records the configuration version in the file. Returns true when the
+// sentinel file is successfully created and processing should continue.
+// Returns false if the processing should not continue because the
+// file exists with the same version (indicating that processing that version
+// failed previously (possibly crash or shutdown). Should be run in the
+// background (e.g., same task as Hints.CreateFromConfig).
+bool CreateSentinelFile(const base::FilePath& sentinel_path,
+ const base::Version& version) {
+ DCHECK(version.IsValid());
+
+ if (base::PathExists(sentinel_path)) {
+ // Processing apparently did not complete previously, check its version.
+ std::string content;
+ if (!base::ReadFileToString(sentinel_path, &content)) {
+ DLOG(WARNING) << "Error reading previews config sentinel file";
+ // Attempt to delete sentinel for fresh start next time.
+ base::DeleteFile(sentinel_path, false /* recursive */);
+ return false;
+ }
+ base::Version previous_attempted_version(content);
+ if (!previous_attempted_version.IsValid()) {
+ DLOG(ERROR) << "Bad contents in previews config sentinel file";
+ // Attempt to delete sentinel for fresh start next time.
+ base::DeleteFile(sentinel_path, false /* recursive */);
+ return false;
+ }
+ if (previous_attempted_version.CompareTo(version) == 0) {
+ // Previously attempted same version without completion.
+ return false;
+ }
+ }
+
+ // Write config version in the sentinel file.
+ std::string new_sentinel_value = version.GetString();
+ if (base::WriteFile(sentinel_path, new_sentinel_value.data(),
+ new_sentinel_value.length()) <= 0) {
+ DLOG(ERROR) << "Failed to create sentinel file " << sentinel_path;
+ return false;
+ }
+ return true;
+}
+
+// Enumerates the possible outcomes of processing previews hints. Used in UMA
+// histograms, so the order of enumerators should not be changed.
+//
+// Keep in sync with PreviewsProcessHintsResult in
+// tools/metrics/histograms/enums.xml.
+enum class PreviewsProcessHintsResult {
+ PROCESSED_NO_PREVIEWS_HINTS = 0,
+ PROCESSED_PREVIEWS_HINTS = 1,
+ FAILED_FINISH_PROCESSING = 2,
+
+ // Insert new values before this line.
+ MAX,
+};
+
+// Returns base::nullopt if |optimization_type| can't be converted.
+base::Optional<PreviewsType>
+ConvertProtoOptimizationTypeToPreviewsOptimizationType(
+ optimization_guide::proto::OptimizationType optimization_type) {
+ switch (optimization_type) {
+ case optimization_guide::proto::TYPE_UNSPECIFIED:
+ return base::nullopt;
+ case optimization_guide::proto::NOSCRIPT:
+ return PreviewsType::NOSCRIPT;
+ case optimization_guide::proto::RESOURCE_LOADING:
+ return PreviewsType::RESOURCE_LOADING_HINTS;
+ }
+}
+
+void RecordProcessHintsResult(PreviewsProcessHintsResult result) {
+ UMA_HISTOGRAM_ENUMERATION("Previews.ProcessHintsResult",
+ static_cast<int>(result),
+ static_cast<int>(PreviewsProcessHintsResult::MAX));
+}
+
+// Deletes the sentinel file. This should be done once processing the
+// configuration is complete and should be done in the background (e.g.,
+// same task as Hints.CreateFromConfig).
+void DeleteSentinelFile(const base::FilePath& sentinel_path) {
+ if (!base::DeleteFile(sentinel_path, false /* recursive */))
+ DLOG(ERROR) << "Error deleting sentinel file";
+}
+
+} // namespace
+
+PreviewsHints::PreviewsHints() {
+ DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+PreviewsHints::~PreviewsHints() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+// static
+std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromConfig(
+ const optimization_guide::proto::Configuration& config,
+ const optimization_guide::ComponentInfo& info) {
+ base::FilePath sentinel_path(
+ info.hints_path.DirName().Append(kSentinelFileName));
+ if (!CreateSentinelFile(sentinel_path, info.hints_version)) {
+ std::unique_ptr<PreviewsHints> no_hints;
+ RecordProcessHintsResult(
+ PreviewsProcessHintsResult::FAILED_FINISH_PROCESSING);
+ return no_hints;
+ }
+
+ std::unique_ptr<PreviewsHints> hints(new PreviewsHints());
+
+ // The condition set ID is a simple increasing counter that matches the
+ // order of hints in the config (where earlier hints in the config take
+ // precendence over later hints in the config if there are multiple matches).
+ url_matcher::URLMatcherConditionSet::ID id = 0;
+ url_matcher::URLMatcherConditionFactory* condition_factory =
+ hints->url_matcher_.condition_factory();
+ url_matcher::URLMatcherConditionSet::Vector all_conditions;
+ std::set<std::string> seen_host_suffixes;
+
+ std::vector<std::string> activation_list;
+
+ // Process hint configuration.
+ for (const auto hint : config.hints()) {
+ // We only support host suffixes at the moment. Skip anything else.
+ if (hint.key_representation() != optimization_guide::proto::HOST_SUFFIX)
+ continue;
+
+ // Validate configuration keys.
+ DCHECK(!hint.key().empty());
+ if (hint.key().empty())
+ continue;
+
+ auto seen_host_suffixes_iter = seen_host_suffixes.find(hint.key());
+ DCHECK(seen_host_suffixes_iter == seen_host_suffixes.end());
+ if (seen_host_suffixes_iter != seen_host_suffixes.end()) {
+ DLOG(WARNING) << "Received config with duplicate key";
+ continue;
+ }
+ seen_host_suffixes.insert(hint.key());
+
+ // Create whitelist condition set out of the optimizations that are
+ // whitelisted for the host suffix.
+ std::set<std::pair<PreviewsType, int>> whitelisted_optimizations;
+ for (const auto optimization : hint.whitelisted_optimizations()) {
+ // If this optimization has been marked with an experiment name, skip it
+ // unless an experiment with that name is running. Experiment names are
+ // configured with the experiment_name parameter to the
+ // kOptimizationHintsExperiments feature.
+ //
+ // If kOptimizationHintsExperiments is disabled, getting the param value
+ // returns an empty string. Since experiment names are not allowed to be
+ // empty strings, all experiments will be disabled if the feature is
+ // disabled.
+ if (optimization.has_experiment_name() &&
+ !optimization.experiment_name().empty() &&
+ optimization.experiment_name() !=
+ base::GetFieldTrialParamValueByFeature(
+ features::kOptimizationHintsExperiments,
+ features::kOptimizationHintsExperimentNameParam)) {
+ continue;
+ }
+ base::Optional<PreviewsType> previews_type =
+ ConvertProtoOptimizationTypeToPreviewsOptimizationType(
+ optimization.optimization_type());
+ if (previews_type.has_value()) {
+ whitelisted_optimizations.insert(std::make_pair(
+ previews_type.value(), optimization.inflation_percent()));
+
+ if (previews_type == previews::PreviewsType::RESOURCE_LOADING_HINTS) {
+ activation_list.push_back(hint.key());
+ }
+ }
+ }
+ url_matcher::URLMatcherCondition condition =
+ condition_factory->CreateHostSuffixCondition(hint.key());
+ all_conditions.push_back(new url_matcher::URLMatcherConditionSet(
+ id, std::set<url_matcher::URLMatcherCondition>{condition}));
+ hints->whitelist_[id] = whitelisted_optimizations;
+ id++;
+ }
+ hints->url_matcher_.AddConditionSets(all_conditions);
+ hints->activation_list_ = std::make_unique<ActivationList>(activation_list);
+
+ // Completed processing hints data without crashing so clear sentinel.
+ DeleteSentinelFile(sentinel_path);
+ RecordProcessHintsResult(
+ all_conditions.empty()
+ ? PreviewsProcessHintsResult::PROCESSED_NO_PREVIEWS_HINTS
+ : PreviewsProcessHintsResult::PROCESSED_PREVIEWS_HINTS);
+ return hints;
+}
+
+bool PreviewsHints::IsWhitelisted(const GURL& url,
+ PreviewsType type,
+ int* out_inflation_percent) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ std::set<url_matcher::URLMatcherConditionSet::ID> matches =
+ url_matcher_.MatchURL(url);
+
+ // Only consider the first match in iteration order as it takes precedence
+ // if there are multiple matches.
+ const auto& first_match = matches.begin();
+ if (first_match == matches.end()) {
+ return false;
+ }
+
+ const auto whitelist_iter = whitelist_.find(*first_match);
+ if (whitelist_iter == whitelist_.end()) {
+ return false;
+ }
+
+ const auto& whitelisted_optimizations = whitelist_iter->second;
+ for (auto optimization_iter = whitelisted_optimizations.begin();
+ optimization_iter != whitelisted_optimizations.end();
+ ++optimization_iter) {
+ if (optimization_iter->first == type) {
+ *out_inflation_percent = optimization_iter->second;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PreviewsHints::IsHostWhitelistedAtNavigation(
+ const GURL& url,
+ previews::PreviewsType type) const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (!activation_list_)
+ return false;
+
+ return activation_list_->IsHostWhitelistedAtNavigation(url, type);
+}
+
+} // namespace previews
diff --git a/chromium/components/previews/content/previews_hints.h b/chromium/components/previews/content/previews_hints.h
new file mode 100644
index 00000000000..60a70473e72
--- /dev/null
+++ b/chromium/components/previews/content/previews_hints.h
@@ -0,0 +1,75 @@
+// 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_PREVIEWS_CONTENT_PREVIEWS_HINTS_H_
+#define COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_HINTS_H_
+
+#include <map>
+#include <memory>
+#include <set>
+
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+#include "components/optimization_guide/proto/hints.pb.h"
+#include "components/previews/content/activation_list.h"
+#include "components/previews/content/previews_hints.h"
+#include "components/previews/core/previews_user_data.h"
+#include "components/url_matcher/url_matcher.h"
+
+class GURL;
+
+namespace optimization_guide {
+struct ComponentInfo;
+}
+
+namespace previews {
+
+// Holds previews hints extracted from the configuration.
+class PreviewsHints {
+ public:
+ ~PreviewsHints();
+
+ // Creates a Hints instance from the provided configuration.
+ static std::unique_ptr<PreviewsHints> CreateFromConfig(
+ const optimization_guide::proto::Configuration& config,
+ const optimization_guide::ComponentInfo& info);
+
+ // Whether the URL is whitelisted for the given previews type. If so,
+ // |out_inflation_percent| will be populated if meta data available for it.
+ bool IsWhitelisted(const GURL& url,
+ PreviewsType type,
+ int* out_inflation_percent);
+
+ // Whether |url| is whitelisted for previews type |type|. The method may
+ // make the decision based only on a partial comparison (e.g., only the
+ // hostname of |url|). As such, the method may return true even if |type|
+ // can't be used for the previews.
+ bool IsHostWhitelistedAtNavigation(const GURL& url,
+ previews::PreviewsType type) const;
+
+ private:
+ PreviewsHints();
+
+ // The URLMatcher used to match whether a URL has any hints associated with
+ // it.
+ url_matcher::URLMatcher url_matcher_;
+
+ // Holds the activation list of hosts extracted from the server hints
+ // configuration.
+ std::unique_ptr<ActivationList> activation_list_;
+
+ // A map from the condition set ID to associated whitelist Optimization
+ // details.
+ std::map<url_matcher::URLMatcherConditionSet::ID,
+ std::set<std::pair<PreviewsType, int>>>
+ whitelist_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(PreviewsHints);
+};
+
+} // namespace previews
+
+#endif // COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_HINTS_H_ \ No newline at end of file
diff --git a/chromium/components/previews/content/previews_optimization_guide.cc b/chromium/components/previews/content/previews_optimization_guide.cc
index 8923216fe9c..30fef53bcd5 100644
--- a/chromium/components/previews/content/previews_optimization_guide.cc
+++ b/chromium/components/previews/content/previews_optimization_guide.cc
@@ -5,237 +5,17 @@
#include "components/previews/content/previews_optimization_guide.h"
#include "base/bind.h"
-#include "base/files/file.h"
-#include "base/files/file_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/task_runner_util.h"
#include "base/task_scheduler/post_task.h"
#include "components/optimization_guide/proto/hints.pb.h"
+#include "components/previews/content/previews_hints.h"
#include "components/previews/core/previews_user_data.h"
#include "net/url_request/url_request.h"
#include "url/gurl.h"
namespace previews {
-namespace {
-
-// Enumerates the possible outcomes of processing previews hints. Used in UMA
-// histograms, so the order of enumerators should not be changed.
-//
-// Keep in sync with PreviewsProcessHintsResult in
-// tools/metrics/histograms/enums.xml.
-enum class PreviewsProcessHintsResult {
- PROCESSED_NO_PREVIEWS_HINTS = 0,
- PROCESSED_PREVIEWS_HINTS = 1,
- FAILED_FINISH_PROCESSING = 2,
-
- // Insert new values before this line.
- MAX,
-};
-
-void RecordProcessHintsResult(PreviewsProcessHintsResult result) {
- UMA_HISTOGRAM_ENUMERATION("Previews.ProcessHintsResult",
- static_cast<int>(result),
- static_cast<int>(PreviewsProcessHintsResult::MAX));
-}
-
-// Name of sentinel file to guard potential crash loops while processing
-// the config into hints. It holds the version of the config that is/was
-// being processed into hints.
-const base::FilePath::CharType kSentinelFileName[] =
- FILE_PATH_LITERAL("previews_config_sentinel.txt");
-
-// Creates the sentinel file (at |sentinel_path|) to persistently mark the
-// beginning of processing the configuration data for Previews hints. It
-// records the configuration version in the file. Returns true when the
-// sentinel file is successfully created and processing should continue.
-// Returns false if the processing should not continue because the
-// file exists with the same version (indicating that processing that version
-// failed previously (possibly crash or shutdown). Should be run in the
-// background (e.g., same task as Hints.CreateFromConfig).
-bool CreateSentinelFile(const base::FilePath& sentinel_path,
- const base::Version& version) {
- DCHECK(version.IsValid());
-
- if (base::PathExists(sentinel_path)) {
- // Processing apparently did not complete previously, check its version.
- std::string content;
- if (!base::ReadFileToString(sentinel_path, &content)) {
- DLOG(WARNING) << "Error reading previews config sentinel file";
- // Attempt to delete sentinel for fresh start next time.
- base::DeleteFile(sentinel_path, false /* recursive */);
- return false;
- }
- base::Version previous_attempted_version(content);
- if (!previous_attempted_version.IsValid()) {
- DLOG(ERROR) << "Bad contents in previews config sentinel file";
- // Attempt to delete sentinel for fresh start next time.
- base::DeleteFile(sentinel_path, false /* recursive */);
- return false;
- }
- if (previous_attempted_version.CompareTo(version) == 0) {
- // Previously attempted same version without completion.
- return false;
- }
- }
-
- // Write config version in the sentinel file.
- std::string new_sentinel_value = version.GetString();
- if (base::WriteFile(sentinel_path, new_sentinel_value.data(),
- new_sentinel_value.length()) <= 0) {
- DLOG(ERROR) << "Failed to create sentinel file " << sentinel_path;
- return false;
- }
- return true;
-}
-
-// Deletes the sentinel file. This should be done once processing the
-// configuration is complete and should be done in the background (e.g.,
-// same task as Hints.CreateFromConfig).
-void DeleteSentinelFile(const base::FilePath& sentinel_path) {
- if (!base::DeleteFile(sentinel_path, false /* recursive */))
- DLOG(ERROR) << "Error deleting sentinel file";
-}
-
-} // namespace
-
-// Holds previews hints extracted from the configuration sent by the
-// Optimization Guide Service.
-class PreviewsOptimizationGuide::Hints {
- public:
- ~Hints();
-
- // Creates a Hints instance from the provided configuration.
- static std::unique_ptr<Hints> CreateFromConfig(
- const optimization_guide::proto::Configuration& config,
- const optimization_guide::ComponentInfo& info);
-
- // Whether the URL is whitelisted for the given previews type. If so,
- // |out_inflation_percent| will be populated if meta data available for it.
- bool IsWhitelisted(const GURL& url,
- PreviewsType type,
- int* out_inflation_percent);
-
- private:
- Hints();
-
- // The URLMatcher used to match whether a URL has any hints associated with
- // it.
- url_matcher::URLMatcher url_matcher_;
-
- // A map from the condition set ID to associated whitelist Optimization
- // details.
- std::map<url_matcher::URLMatcherConditionSet::ID,
- std::set<std::pair<PreviewsType, int>>>
- whitelist_;
-};
-
-PreviewsOptimizationGuide::Hints::Hints() {}
-
-PreviewsOptimizationGuide::Hints::~Hints() {}
-
-// static
-std::unique_ptr<PreviewsOptimizationGuide::Hints>
-PreviewsOptimizationGuide::Hints::CreateFromConfig(
- const optimization_guide::proto::Configuration& config,
- const optimization_guide::ComponentInfo& info) {
- base::FilePath sentinel_path(
- info.hints_path.DirName().Append(kSentinelFileName));
- if (!CreateSentinelFile(sentinel_path, info.hints_version)) {
- std::unique_ptr<Hints> no_hints;
- RecordProcessHintsResult(
- PreviewsProcessHintsResult::FAILED_FINISH_PROCESSING);
- return no_hints;
- }
-
- std::unique_ptr<Hints> hints(new Hints());
-
- // The condition set ID is a simple increasing counter that matches the
- // order of hints in the config (where earlier hints in the config take
- // precendence over later hints in the config if there are multiple matches).
- url_matcher::URLMatcherConditionSet::ID id = 0;
- url_matcher::URLMatcherConditionFactory* condition_factory =
- hints->url_matcher_.condition_factory();
- url_matcher::URLMatcherConditionSet::Vector all_conditions;
- std::set<std::string> seen_host_suffixes;
-
- // Process hint configuration.
- for (const auto hint : config.hints()) {
- // We only support host suffixes at the moment. Skip anything else.
- if (hint.key_representation() != optimization_guide::proto::HOST_SUFFIX)
- continue;
-
- // Validate configuration keys.
- DCHECK(!hint.key().empty());
- if (hint.key().empty())
- continue;
-
- auto seen_host_suffixes_iter = seen_host_suffixes.find(hint.key());
- DCHECK(seen_host_suffixes_iter == seen_host_suffixes.end());
- if (seen_host_suffixes_iter != seen_host_suffixes.end()) {
- DLOG(WARNING) << "Received config with duplicate key";
- continue;
- }
- seen_host_suffixes.insert(hint.key());
-
- // Create whitelist condition set out of the optimizations that are
- // whitelisted for the host suffix.
- std::set<std::pair<PreviewsType, int>> whitelisted_optimizations;
- for (const auto optimization : hint.whitelisted_optimizations()) {
- if (optimization.optimization_type() ==
- optimization_guide::proto::NOSCRIPT) {
- whitelisted_optimizations.insert(std::make_pair(
- PreviewsType::NOSCRIPT, optimization.inflation_percent()));
- }
- }
- url_matcher::URLMatcherCondition condition =
- condition_factory->CreateHostSuffixCondition(hint.key());
- all_conditions.push_back(new url_matcher::URLMatcherConditionSet(
- id, std::set<url_matcher::URLMatcherCondition>{condition}));
- hints->whitelist_[id] = whitelisted_optimizations;
- id++;
- }
- hints->url_matcher_.AddConditionSets(all_conditions);
- // Completed processing hints data without crashing so clear sentinel.
- DeleteSentinelFile(sentinel_path);
- RecordProcessHintsResult(
- all_conditions.empty()
- ? PreviewsProcessHintsResult::PROCESSED_NO_PREVIEWS_HINTS
- : PreviewsProcessHintsResult::PROCESSED_PREVIEWS_HINTS);
- return hints;
-}
-
-bool PreviewsOptimizationGuide::Hints::IsWhitelisted(
- const GURL& url,
- PreviewsType type,
- int* out_inflation_percent) {
- std::set<url_matcher::URLMatcherConditionSet::ID> matches =
- url_matcher_.MatchURL(url);
-
- // Only consider the first match in iteration order as it takes precendence
- // if there are multiple matches.
- const auto& first_match = matches.begin();
- if (first_match == matches.end()) {
- return false;
- }
-
- const auto whitelist_iter = whitelist_.find(*first_match);
- if (whitelist_iter == whitelist_.end()) {
- return false;
- }
-
- const auto& whitelisted_optimizations = whitelist_iter->second;
- for (auto optimization_iter = whitelisted_optimizations.begin();
- optimization_iter != whitelisted_optimizations.end();
- ++optimization_iter) {
- if (optimization_iter->first == type) {
- *out_inflation_percent = optimization_iter->second;
- return true;
- }
- }
- return false;
-}
-
PreviewsOptimizationGuide::PreviewsOptimizationGuide(
optimization_guide::OptimizationGuideService* optimization_guide_service,
const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
@@ -254,6 +34,7 @@ PreviewsOptimizationGuide::~PreviewsOptimizationGuide() {
bool PreviewsOptimizationGuide::IsWhitelisted(const net::URLRequest& request,
PreviewsType type) const {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
if (!hints_)
return false;
@@ -269,6 +50,16 @@ bool PreviewsOptimizationGuide::IsWhitelisted(const net::URLRequest& request,
return true;
}
+bool PreviewsOptimizationGuide::IsHostWhitelistedAtNavigation(
+ const GURL& url,
+ previews::PreviewsType type) const {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+ if (!hints_)
+ return false;
+ return hints_->IsHostWhitelistedAtNavigation(url, type);
+}
+
void PreviewsOptimizationGuide::OnHintsProcessed(
const optimization_guide::proto::Configuration& config,
const optimization_guide::ComponentInfo& info) {
@@ -276,13 +67,13 @@ void PreviewsOptimizationGuide::OnHintsProcessed(
base::PostTaskAndReplyWithResult(
background_task_runner_.get(), FROM_HERE,
- base::BindOnce(&PreviewsOptimizationGuide::Hints::CreateFromConfig,
- config, info),
+ base::BindOnce(&PreviewsHints::CreateFromConfig, config, info),
base::BindOnce(&PreviewsOptimizationGuide::UpdateHints,
io_weak_ptr_factory_.GetWeakPtr()));
}
-void PreviewsOptimizationGuide::UpdateHints(std::unique_ptr<Hints> hints) {
+void PreviewsOptimizationGuide::UpdateHints(
+ std::unique_ptr<PreviewsHints> hints) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
hints_ = std::move(hints);
}
diff --git a/chromium/components/previews/content/previews_optimization_guide.h b/chromium/components/previews/content/previews_optimization_guide.h
index 2128c9151f3..603fe903390 100644
--- a/chromium/components/previews/content/previews_optimization_guide.h
+++ b/chromium/components/previews/content/previews_optimization_guide.h
@@ -5,20 +5,17 @@
#ifndef COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_OPTIMIZATION_GUIDE_H_
#define COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_OPTIMIZATION_GUIDE_H_
-#include <map>
-#include <set>
-
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
-#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "components/optimization_guide/optimization_guide_service.h"
#include "components/optimization_guide/optimization_guide_service_observer.h"
#include "components/previews/content/previews_optimization_guide.h"
#include "components/previews/core/previews_experiments.h"
-#include "components/url_matcher/url_matcher.h"
+
+class GURL;
namespace net {
class URLRequest;
@@ -32,6 +29,8 @@ class Configuration;
namespace previews {
+class PreviewsHints;
+
// A Previews optimization guide that makes decisions guided by hints received
// from the OptimizationGuideService.
class PreviewsOptimizationGuide
@@ -49,16 +48,21 @@ class PreviewsOptimizationGuide
virtual bool IsWhitelisted(const net::URLRequest& request,
PreviewsType type) const;
+ // Whether |url| is whitelisted for previews type |type|. The method may
+ // make the decision based only on a partial comparison (e.g., only the
+ // hostname of |url|). As such, the method may return true even if |type|
+ // can't be used for the previews.
+ bool IsHostWhitelistedAtNavigation(const GURL& url,
+ previews::PreviewsType type) const;
+
// optimization_guide::OptimizationGuideServiceObserver implementation:
void OnHintsProcessed(
const optimization_guide::proto::Configuration& config,
const optimization_guide::ComponentInfo& component_info) override;
private:
- class Hints;
-
// Updates the hints to the latest hints sent by the Component Updater.
- void UpdateHints(std::unique_ptr<Hints> hints);
+ void UpdateHints(std::unique_ptr<PreviewsHints> hints);
// The OptimizationGuideService that this guide is listening to. Not owned.
optimization_guide::OptimizationGuideService* optimization_guide_service_;
@@ -70,7 +74,7 @@ class PreviewsOptimizationGuide
scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
// The current hints used for this optimization guide.
- std::unique_ptr<Hints> hints_;
+ std::unique_ptr<PreviewsHints> hints_;
// Used to get |weak_ptr_| to self on the IO thread.
base::WeakPtrFactory<PreviewsOptimizationGuide> io_weak_ptr_factory_;
diff --git a/chromium/components/previews/content/previews_optimization_guide_unittest.cc b/chromium/components/previews/content/previews_optimization_guide_unittest.cc
index 35bed74c7a4..7ad2a7b57b4 100644
--- a/chromium/components/previews/content/previews_optimization_guide_unittest.cc
+++ b/chromium/components/previews/content/previews_optimization_guide_unittest.cc
@@ -10,13 +10,16 @@
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
+#include "base/optional.h"
#include "base/run_loop.h"
#include "base/test/gtest_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "components/optimization_guide/optimization_guide_service.h"
#include "components/optimization_guide/optimization_guide_service_observer.h"
#include "components/optimization_guide/proto/hints.pb.h"
+#include "components/previews/core/previews_features.h"
#include "components/previews/core/previews_user_data.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request.h"
@@ -96,6 +99,9 @@ class PreviewsOptimizationGuideTest : public testing::Test {
base::RunLoop().RunUntilIdle();
}
+ void DoExperimentFlagTest(base::Optional<std::string> experiment_name,
+ bool expect_enabled);
+
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
base::ScopedTempDir temp_dir_;
@@ -150,6 +156,242 @@ TEST_F(PreviewsOptimizationGuideTest,
EXPECT_FALSE(
guide()->IsWhitelisted(*CreateRequestWithURL(GURL("https://google.com")),
PreviewsType::NOSCRIPT));
+
+ EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://m.facebook.com"), PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://m.twitter.com/example"),
+ PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://google.com"), PreviewsType::RESOURCE_LOADING_HINTS));
+}
+
+// Test when resource loading hints are enabled.
+TEST_F(PreviewsOptimizationGuideTest,
+ ProcessHintsWhitelistForResourceLoadingHintsPopulatedCorrectly) {
+ optimization_guide::proto::Configuration config;
+ optimization_guide::proto::Hint* hint1 = config.add_hints();
+ hint1->set_key("facebook.com");
+ hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+ optimization_guide::proto::Optimization* optimization1 =
+ hint1->add_whitelisted_optimizations();
+ optimization1->set_optimization_type(
+ optimization_guide::proto::RESOURCE_LOADING);
+ // Add a second optimization to ensure that the applicable optimizations are
+ // still whitelisted.
+ optimization_guide::proto::Optimization* optimization2 =
+ hint1->add_whitelisted_optimizations();
+ optimization2->set_optimization_type(
+ optimization_guide::proto::TYPE_UNSPECIFIED);
+ // Add a second hint.
+ optimization_guide::proto::Hint* hint2 = config.add_hints();
+ hint2->set_key("twitter.com");
+ hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+ optimization_guide::proto::Optimization* optimization3 =
+ hint2->add_whitelisted_optimizations();
+ optimization3->set_optimization_type(
+ optimization_guide::proto::RESOURCE_LOADING);
+ ProcessHints(config, "2.0.0");
+
+ RunUntilIdle();
+
+ // Twitter and Facebook should be whitelisted but not Google.
+ EXPECT_TRUE(guide()->IsWhitelisted(
+ *CreateRequestWithURL(GURL("https://m.facebook.com")),
+ PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_TRUE(guide()->IsWhitelisted(
+ *CreateRequestWithURL(GURL("https://m.twitter.com/example")),
+ PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_FALSE(
+ guide()->IsWhitelisted(*CreateRequestWithURL(GURL("https://google.com")),
+ PreviewsType::RESOURCE_LOADING_HINTS));
+
+ EXPECT_TRUE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://m.facebook.com"), PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_TRUE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://m.facebook.com/example.html"),
+ PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_TRUE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://m.twitter.com/example"),
+ PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://google.com"), PreviewsType::RESOURCE_LOADING_HINTS));
+}
+
+// Test when both NoScript and resource loading hints are enabled.
+TEST_F(
+ PreviewsOptimizationGuideTest,
+ ProcessHintsWhitelistForNoScriptAndResourceLoadingHintsPopulatedCorrectly) {
+ optimization_guide::proto::Configuration config;
+ optimization_guide::proto::Hint* hint1 = config.add_hints();
+ hint1->set_key("facebook.com");
+ hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+ optimization_guide::proto::Optimization* optimization1 =
+ hint1->add_whitelisted_optimizations();
+ optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
+ // Add a second optimization to ensure that the applicable optimizations are
+ // still whitelisted.
+ optimization_guide::proto::Optimization* optimization2 =
+ hint1->add_whitelisted_optimizations();
+ optimization2->set_optimization_type(
+ optimization_guide::proto::TYPE_UNSPECIFIED);
+ // Add a second hint.
+ optimization_guide::proto::Hint* hint2 = config.add_hints();
+ hint2->set_key("twitter.com");
+ hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+ optimization_guide::proto::Optimization* optimization3 =
+ hint2->add_whitelisted_optimizations();
+ optimization3->set_optimization_type(
+ optimization_guide::proto::RESOURCE_LOADING);
+ ProcessHints(config, "2.0.0");
+
+ RunUntilIdle();
+
+ // Twitter and Facebook should be whitelisted but not Google.
+ EXPECT_TRUE(guide()->IsWhitelisted(
+ *CreateRequestWithURL(GURL("https://m.facebook.com")),
+ PreviewsType::NOSCRIPT));
+ EXPECT_TRUE(guide()->IsWhitelisted(
+ *CreateRequestWithURL(GURL("https://m.facebook.com/example.html")),
+ PreviewsType::NOSCRIPT));
+ EXPECT_TRUE(guide()->IsWhitelisted(
+ *CreateRequestWithURL(GURL("https://m.twitter.com/example")),
+ PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_FALSE(
+ guide()->IsWhitelisted(*CreateRequestWithURL(GURL("https://google.com")),
+ PreviewsType::RESOURCE_LOADING_HINTS));
+
+ EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://m.facebook.com"), PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_TRUE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://m.twitter.com/example"),
+ PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_TRUE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://m.twitter.com"), PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://google.com"), PreviewsType::RESOURCE_LOADING_HINTS));
+}
+
+// This is a helper function for testing the experiment flags on the config for
+// the optimization guide. It creates a test config with a hint containing
+// multiple optimizations. The optimization under test will be marked with an
+// experiment name if one is provided in |experiment_name|. It will then be
+// tested to see if it's enabled, the expectation found in |expect_enabled|.
+void PreviewsOptimizationGuideTest::DoExperimentFlagTest(
+ base::Optional<std::string> experiment_name,
+ bool expect_enabled) {
+ optimization_guide::proto::Configuration config;
+
+ // Create a hint with two optimizations. One may be marked experimental
+ // depending on test configuration. The other is never marked experimental.
+ optimization_guide::proto::Hint* hint1 = config.add_hints();
+ hint1->set_key("facebook.com");
+ hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+ optimization_guide::proto::Optimization* optimization1 =
+ hint1->add_whitelisted_optimizations();
+ // NOSCRIPT is the optimization under test and may be marked experimental.
+ if (experiment_name.has_value()) {
+ optimization1->set_experiment_name(experiment_name.value());
+ }
+ optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
+ // RESOURCE_LOADING is never marked experimental.
+ optimization_guide::proto::Optimization* optimization2 =
+ hint1->add_whitelisted_optimizations();
+ optimization2->set_optimization_type(
+ optimization_guide::proto::RESOURCE_LOADING);
+
+ // Add a second, non-experimental hint.
+ optimization_guide::proto::Hint* hint2 = config.add_hints();
+ hint2->set_key("twitter.com");
+ hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+ optimization_guide::proto::Optimization* optimization3 =
+ hint2->add_whitelisted_optimizations();
+ optimization3->set_optimization_type(optimization_guide::proto::NOSCRIPT);
+ ProcessHints(config, "2.0.0");
+
+ RunUntilIdle();
+
+ // Check to ensure the optimization under test (facebook noscript) is either
+ // enabled or disabled, depending on what the caller told us to expect.
+ EXPECT_EQ(expect_enabled,
+ guide()->IsWhitelisted(
+ *CreateRequestWithURL(GURL("https://m.facebook.com")),
+ PreviewsType::NOSCRIPT));
+
+ // RESOURCE_LOADING_HINTS for facebook should always be enabled.
+ EXPECT_TRUE(guide()->IsWhitelisted(
+ *CreateRequestWithURL(GURL("https://m.facebook.com")),
+ PreviewsType::RESOURCE_LOADING_HINTS));
+ // Twitter's NOSCRIPT should always be enabled; RESOURCE_LOADING_HINTS is not
+ // configured and should be disabled.
+ EXPECT_TRUE(guide()->IsWhitelisted(
+ *CreateRequestWithURL(GURL("https://m.twitter.com/example")),
+ PreviewsType::NOSCRIPT));
+ EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://m.twitter.com/example"),
+ PreviewsType::RESOURCE_LOADING_HINTS));
+ // Google (which is not configured at all) should always have both NOSCRIPT
+ // and RESOURCE_LOADING_HINTS disabled.
+ EXPECT_FALSE(
+ guide()->IsWhitelisted(*CreateRequestWithURL(GURL("https://google.com")),
+ PreviewsType::NOSCRIPT));
+ EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://google.com"), PreviewsType::RESOURCE_LOADING_HINTS));
+}
+
+TEST_F(PreviewsOptimizationGuideTest,
+ HandlesExperimentalFlagWithNoExperimentFlaggedOrEnabled) {
+ // With the optimization NOT flagged as experimental and no experiment
+ // enabled, the optimization should be enabled.
+ base::test::ScopedFeatureList scoped_list;
+ scoped_list.InitAndDisableFeature(features::kOptimizationHintsExperiments);
+ DoExperimentFlagTest(base::nullopt, true);
+}
+
+TEST_F(PreviewsOptimizationGuideTest,
+ HandlesExperimentalFlagWithEmptyExperimentName) {
+ // Empty experiment names should be equivalent to no experiment flag set.
+ base::test::ScopedFeatureList scoped_list;
+ scoped_list.InitAndDisableFeature(features::kOptimizationHintsExperiments);
+ DoExperimentFlagTest("", true);
+}
+
+TEST_F(PreviewsOptimizationGuideTest,
+ HandlesExperimentalFlagWithExperimentConfiguredAndNotRunning) {
+ // With the optimization flagged as experimental and no experiment
+ // enabled, the optimization should be disabled.
+ base::test::ScopedFeatureList scoped_list;
+ scoped_list.InitAndDisableFeature(features::kOptimizationHintsExperiments);
+ DoExperimentFlagTest("foo_experiment", false);
+}
+
+TEST_F(PreviewsOptimizationGuideTest,
+ HandlesExperimentalFlagWithExperimentConfiguredAndSameOneRunning) {
+ // With the optimization flagged as experimental and an experiment with that
+ // name running, the optimization should be enabled.
+ base::test::ScopedFeatureList scoped_list;
+ scoped_list.InitAndEnableFeatureWithParameters(
+ features::kOptimizationHintsExperiments,
+ {{"experiment_name", "foo_experiment"}});
+ DoExperimentFlagTest("foo_experiment", true);
+}
+
+TEST_F(PreviewsOptimizationGuideTest,
+ HandlesExperimentalFlagWithExperimentConfiguredAndDifferentOneRunning) {
+ // With the optimization flagged as experimental and a *different* experiment
+ // enabled, the optimization should be disabled.
+ base::test::ScopedFeatureList scoped_list;
+ scoped_list.InitAndEnableFeatureWithParameters(
+ features::kOptimizationHintsExperiments,
+ {{"experiment_name", "bar_experiment"}});
+ DoExperimentFlagTest("foo_experiment", false);
+}
+
+TEST_F(PreviewsOptimizationGuideTest, EnsureExperimentsDisabledByDefault) {
+ // Mark an optimization as experiment, and ensure it's disabled even though we
+ // don't explicitly enable or disable the feature as part of the test. This
+ // ensures the experiments feature is disabled by default.
+ DoExperimentFlagTest("foo_experiment", false);
}
TEST_F(PreviewsOptimizationGuideTest, ProcessHintsUnsupportedKeyRepIsIgnored) {
@@ -368,6 +610,22 @@ TEST_F(PreviewsOptimizationGuideTest, IsWhitelistedWithMultipleHintMatches) {
CreateRequestWithURL(GURL("https://outdoor.sports.yahoo.com"));
// Uses "sports.yahoo.com" match before "yahoo.com" match.
EXPECT_FALSE(guide()->IsWhitelisted(*request5, PreviewsType::NOSCRIPT));
+
+ EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://yahoo.com"), PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://m.facebook.com"), PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://m.twitter.com/example"),
+ PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://google.com"), PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://outdoor.sports.yahoo.com"),
+ PreviewsType::RESOURCE_LOADING_HINTS));
+ EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+ GURL("https://outdoor.sports.yahoo.com/index.html"),
+ PreviewsType::RESOURCE_LOADING_HINTS));
}
TEST_F(PreviewsOptimizationGuideTest, RemoveObserverCalledAtDestruction) {
diff --git a/chromium/components/previews/content/previews_ui_service.cc b/chromium/components/previews/content/previews_ui_service.cc
index d31dd5e6a33..0ff68ee2319 100644
--- a/chromium/components/previews/content/previews_ui_service.cc
+++ b/chromium/components/previews/content/previews_ui_service.cc
@@ -11,28 +11,31 @@
namespace previews {
PreviewsUIService::PreviewsUIService(
- PreviewsIOData* previews_io_data,
+ PreviewsDeciderImpl* previews_decider_impl,
const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
- std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store,
+ std::unique_ptr<blacklist::OptOutStore> previews_opt_out_store,
std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide,
const PreviewsIsEnabledCallback& is_enabled_callback,
- std::unique_ptr<PreviewsLogger> logger)
+ std::unique_ptr<PreviewsLogger> logger,
+ blacklist::BlacklistData::AllowedTypesAndVersions allowed_previews)
: io_task_runner_(io_task_runner),
logger_(std::move(logger)),
weak_factory_(this) {
DCHECK(logger_);
- previews_io_data->Initialize(
+ previews_decider_impl->Initialize(
weak_factory_.GetWeakPtr(), std::move(previews_opt_out_store),
- std::move(previews_opt_guide), is_enabled_callback);
+ std::move(previews_opt_guide), is_enabled_callback,
+ std::move(allowed_previews));
}
PreviewsUIService::~PreviewsUIService() {
DCHECK(thread_checker_.CalledOnValidThread());
}
-void PreviewsUIService::SetIOData(base::WeakPtr<PreviewsIOData> io_data) {
+void PreviewsUIService::SetIOData(
+ base::WeakPtr<PreviewsDeciderImpl> previews_decider_impl) {
DCHECK(thread_checker_.CalledOnValidThread());
- io_data_ = io_data;
+ previews_decider_impl_ = previews_decider_impl;
}
void PreviewsUIService::AddPreviewNavigation(const GURL& url,
@@ -41,8 +44,9 @@ void PreviewsUIService::AddPreviewNavigation(const GURL& url,
uint64_t page_id) {
DCHECK(thread_checker_.CalledOnValidThread());
io_task_runner_->PostTask(
- FROM_HERE, base::Bind(&PreviewsIOData::AddPreviewNavigation, io_data_,
- url, opt_out, type, page_id));
+ FROM_HERE,
+ base::BindOnce(&PreviewsDeciderImpl::AddPreviewNavigation,
+ previews_decider_impl_, url, opt_out, type, page_id));
}
void PreviewsUIService::LogPreviewNavigation(const GURL& url,
@@ -85,8 +89,9 @@ void PreviewsUIService::OnBlacklistCleared(base::Time time) {
void PreviewsUIService::SetIgnorePreviewsBlacklistDecision(bool ignored) {
DCHECK(thread_checker_.CalledOnValidThread());
io_task_runner_->PostTask(
- FROM_HERE, base::Bind(&PreviewsIOData::SetIgnorePreviewsBlacklistDecision,
- io_data_, ignored));
+ FROM_HERE,
+ base::BindOnce(&PreviewsDeciderImpl::SetIgnorePreviewsBlacklistDecision,
+ previews_decider_impl_, ignored));
}
void PreviewsUIService::OnIgnoreBlacklistDecisionStatusChanged(bool ignored) {
@@ -94,6 +99,26 @@ void PreviewsUIService::OnIgnoreBlacklistDecisionStatusChanged(bool ignored) {
logger_->OnIgnoreBlacklistDecisionStatusChanged(ignored);
}
+void PreviewsUIService::SetResourceLoadingHintsResourcePatternsToBlock(
+ const GURL& document_gurl,
+ const std::vector<std::string>& patterns) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ resource_loading_hints_document_gurl_ = document_gurl;
+ resource_loading_hints_patterns_to_block_ = patterns;
+}
+
+std::vector<std::string>
+PreviewsUIService::GetResourceLoadingHintsResourcePatternsToBlock(
+ const GURL& document_gurl) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // TODO(tbansal): https://crbug.com/856243. Read patterns from the proto
+ // optimizations file from the disk, and populate the return value.
+ if (document_gurl != resource_loading_hints_document_gurl_)
+ return std::vector<std::string>();
+ return resource_loading_hints_patterns_to_block_;
+}
+
PreviewsLogger* PreviewsUIService::previews_logger() const {
DCHECK(thread_checker_.CalledOnValidThread());
return logger_.get();
@@ -103,8 +128,8 @@ void PreviewsUIService::ClearBlackList(base::Time begin_time,
base::Time end_time) {
DCHECK(thread_checker_.CalledOnValidThread());
io_task_runner_->PostTask(
- FROM_HERE, base::Bind(&PreviewsIOData::ClearBlackList, io_data_,
- begin_time, end_time));
+ FROM_HERE, base::BindOnce(&PreviewsDeciderImpl::ClearBlackList,
+ previews_decider_impl_, begin_time, end_time));
}
} // namespace previews
diff --git a/chromium/components/previews/content/previews_ui_service.h b/chromium/components/previews/content/previews_ui_service.h
index 7b96f2d52f0..0c4ffdb26be 100644
--- a/chromium/components/previews/content/previews_ui_service.h
+++ b/chromium/components/previews/content/previews_ui_service.h
@@ -6,18 +6,20 @@
#define COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_UI_SERVICE_H_
#include <string>
+#include <vector>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
-#include "components/previews/content/previews_io_data.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_store.h"
+#include "components/previews/content/previews_decider_impl.h"
#include "components/previews/content/previews_optimization_guide.h"
#include "components/previews/core/previews_black_list.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_logger.h"
-#include "components/previews/core/previews_opt_out_store.h"
class GURL;
@@ -26,24 +28,26 @@ class SingleThreadTaskRunner;
}
namespace previews {
-class PreviewsIOData;
+class PreviewsDeciderImpl;
// A class to manage the UI portion of inter-thread communication between
// previews/ objects. Created and used on the UI thread.
class PreviewsUIService {
public:
PreviewsUIService(
- PreviewsIOData* previews_io_data,
+ PreviewsDeciderImpl* previews_decider_impl,
const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
- std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store,
+ std::unique_ptr<blacklist::OptOutStore> previews_opt_out_store,
std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide,
const PreviewsIsEnabledCallback& is_enabled_callback,
- std::unique_ptr<PreviewsLogger> logger);
+ std::unique_ptr<PreviewsLogger> logger,
+ blacklist::BlacklistData::AllowedTypesAndVersions allowed_previews);
virtual ~PreviewsUIService();
- // Sets |io_data_| to |io_data| to allow calls from the UI thread to the IO
- // thread. Virtualized in testing.
- virtual void SetIOData(base::WeakPtr<PreviewsIOData> io_data);
+ // Sets |previews_decider_impl_| to |previews_decider_impl| to allow calls
+ // from the UI thread to the IO thread. Virtualized in testing.
+ virtual void SetIOData(
+ base::WeakPtr<PreviewsDeciderImpl> previews_decider_impl);
// Adds a navigation to |url| to the black list with result |opt_out|.
void AddPreviewNavigation(const GURL& url,
@@ -67,13 +71,14 @@ class PreviewsUIService {
virtual void OnBlacklistCleared(base::Time time);
// Change the status of whether to ignored or consider PreviewsBlackList
- // decisions in |io_data_|. This method is called when users interact with the
- // UI (i.e. click on the "Ignore Blacklist" button). Virtualized in testing.
+ // decisions in |previews_decider_impl_|. This method is called when users
+ // interact with the UI (i.e. click on the "Ignore Blacklist" button).
+ // Virtualized in testing.
virtual void SetIgnorePreviewsBlacklistDecision(bool ignored);
// Notifies |logger_| whether PreviewsBlackList decisions are ignored or not.
- // This method is listening for notification from PreviewsIOData for when the
- // blacklist ignore status is changed so that |logger_| can update all
+ // This method is listening for notification from PreviewsDeciderImpl for when
+ // the blacklist ignore status is changed so that |logger_| can update all
// PreviewsLoggerObservers so that multiple instances of the page have the
// same status. Virtualized in testing.
virtual void OnIgnoreBlacklistDecisionStatusChanged(bool ignored);
@@ -90,8 +95,8 @@ class PreviewsUIService {
// correspond to eligibility checks that were satisfied prior to determining
// |reason| and so the opposite of these |passed_reasons| codes was true.
// The method takes ownership of |passed_reasons|. |page_id| is generated
- // by PreviewsIOData, and used to group decisions into groups on the page,
- // messages that don't need to be grouped can pass in 0 as page_id.
+ // by PreviewsDeciderImpl, and used to group decisions into groups on the
+ // page, messages that don't need to be grouped can pass in 0 as page_id.
// Virtualized in testing.
virtual void LogPreviewDecisionMade(
PreviewsEligibilityReason reason,
@@ -101,6 +106,23 @@ class PreviewsUIService {
std::vector<PreviewsEligibilityReason>&& passed_reasons,
uint64_t page_id);
+ // Returns the vector of subresource patterns whose loading should be blocked
+ // when loading |document_gurl|. The pattern may be a single substring to
+ // match against the URL or it may be an ordered set of substrings to match
+ // where the substrings are separated by the ‘*’ wildcard character (with an
+ // implicit ‘*’ at the beginning and end).
+ std::vector<std::string> GetResourceLoadingHintsResourcePatternsToBlock(
+ const GURL& document_gurl) const;
+
+ // Sets the vector of subresource patterns whose loading should be blocked
+ // when loading |document_gurl| to |patterns|. The pattern may be a single
+ // substring to match against the URL or it may be an ordered set of
+ // substrings to match where the substrings are separated by the ‘*’ wildcard
+ // character (with an implicit ‘*’ at the beginning and end).
+ void SetResourceLoadingHintsResourcePatternsToBlock(
+ const GURL& document_gurl,
+ const std::vector<std::string>& patterns);
+
// Expose the pointer to PreviewsLogger to extract logging messages. This
// pointer's life time is the same as of |this|, and it is guaranteed to not
// return null.
@@ -108,13 +130,21 @@ class PreviewsUIService {
private:
// The IO thread portion of the inter-thread communication for previews/.
- base::WeakPtr<previews::PreviewsIOData> io_data_;
+ base::WeakPtr<previews::PreviewsDeciderImpl> previews_decider_impl_;
base::ThreadChecker thread_checker_;
- // The IO thread task runner. Used to post tasks to |io_data_|.
+ // The IO thread task runner. Used to post tasks to |previews_decider_impl_|.
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+ // |resource_loading_hints_patterns_| are the set of subresource patterns
+ // whose loading should be blocked. The hints apply to subresources when
+ // fetching |resource_loading_hints_document_gurl_|.
+ // TODO(tbansal): https://crbug.com/856243. Consider storing this
+ // data in a map or an LRU cache.
+ GURL resource_loading_hints_document_gurl_;
+ std::vector<std::string> resource_loading_hints_patterns_to_block_;
+
// A log object to keep track of events such as previews navigations,
// blacklist actions, etc.
std::unique_ptr<PreviewsLogger> logger_;
diff --git a/chromium/components/previews/content/previews_ui_service_unittest.cc b/chromium/components/previews/content/previews_ui_service_unittest.cc
index 722f51be83a..ae90aabdbb2 100644
--- a/chromium/components/previews/content/previews_ui_service_unittest.cc
+++ b/chromium/components/previews/content/previews_ui_service_unittest.cc
@@ -10,7 +10,9 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
-#include "components/previews/content/previews_io_data.h"
+#include "base/time/default_clock.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h"
+#include "components/previews/content/previews_decider_impl.h"
#include "components/previews/core/previews_black_list.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_logger.h"
@@ -23,32 +25,34 @@ namespace {
class TestPreviewsUIService : public PreviewsUIService {
public:
TestPreviewsUIService(
- PreviewsIOData* previews_io_data,
+ PreviewsDeciderImpl* previews_decider_impl,
const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
- std::unique_ptr<PreviewsOptOutStore> previews_opt_out_store,
+ std::unique_ptr<blacklist::OptOutStore> previews_opt_out_store,
std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide,
std::unique_ptr<PreviewsLogger> logger)
- : PreviewsUIService(previews_io_data,
+ : PreviewsUIService(previews_decider_impl,
io_task_runner,
std::move(previews_opt_out_store),
std::move(previews_opt_guide),
PreviewsIsEnabledCallback(),
- std::move(logger)),
- io_data_set_(false) {}
+ std::move(logger),
+ blacklist::BlacklistData::AllowedTypesAndVersions()),
+ previews_decider_impl_set_(false) {}
~TestPreviewsUIService() override {}
- // Set |io_data_set_| to true and use base class functionality.
- void SetIOData(base::WeakPtr<PreviewsIOData> previews_io_data) override {
- io_data_set_ = true;
- PreviewsUIService::SetIOData(previews_io_data);
+ // Set |previews_decider_impl_set_| to true and use base class functionality.
+ void SetIOData(
+ base::WeakPtr<PreviewsDeciderImpl> previews_decider_impl) override {
+ previews_decider_impl_set_ = true;
+ PreviewsUIService::SetIOData(previews_decider_impl);
}
// Whether SetIOData was called.
- bool io_data_set() { return io_data_set_; }
+ bool previews_decider_impl_set() { return previews_decider_impl_set_; }
private:
// Whether SetIOData was called.
- bool io_data_set_;
+ bool previews_decider_impl_set_;
};
// Mock class of PreviewsLogger for checking passed in parameters.
@@ -158,15 +162,17 @@ class TestPreviewsLogger : public PreviewsLogger {
bool blacklist_ignored_;
};
-class TestPreviewsIOData : public PreviewsIOData {
+class TestPreviewsDeciderImpl : public PreviewsDeciderImpl {
public:
- TestPreviewsIOData(
+ TestPreviewsDeciderImpl(
const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
- : PreviewsIOData(ui_task_runner, io_task_runner),
+ : PreviewsDeciderImpl(ui_task_runner,
+ io_task_runner,
+ base::DefaultClock::GetInstance()),
blacklist_ignored_(false) {}
- // PreviewsIOData:
+ // PreviewsDeciderImpl:
void SetIgnorePreviewsBlacklistDecision(bool ignored) override {
blacklist_ignored_ = ignored;
}
@@ -193,15 +199,18 @@ class PreviewsUIServiceTest : public testing::Test {
// Use to testing logger data.
logger_ptr_ = logger.get();
- io_data_ = std::make_unique<TestPreviewsIOData>(loop_.task_runner(),
- loop_.task_runner());
+ previews_decider_impl_ = std::make_unique<TestPreviewsDeciderImpl>(
+ loop_.task_runner(), loop_.task_runner());
ui_service_ = std::make_unique<TestPreviewsUIService>(
- io_data(), loop_.task_runner(), nullptr /* previews_opt_out_store */,
- nullptr /* previews_opt_guide */, std::move(logger));
+ previews_decider_impl(), loop_.task_runner(),
+ nullptr /* previews_opt_out_store */, nullptr /* previews_opt_guide */,
+ std::move(logger));
base::RunLoop().RunUntilIdle();
}
- TestPreviewsIOData* io_data() { return io_data_.get(); }
+ TestPreviewsDeciderImpl* previews_decider_impl() {
+ return previews_decider_impl_.get();
+ }
TestPreviewsUIService* ui_service() { return ui_service_.get(); }
protected:
@@ -210,7 +219,7 @@ class PreviewsUIServiceTest : public testing::Test {
TestPreviewsLogger* logger_ptr_;
private:
- std::unique_ptr<TestPreviewsIOData> io_data_;
+ std::unique_ptr<TestPreviewsDeciderImpl> previews_decider_impl_;
std::unique_ptr<TestPreviewsUIService> ui_service_;
};
@@ -219,7 +228,7 @@ class PreviewsUIServiceTest : public testing::Test {
TEST_F(PreviewsUIServiceTest, TestInitialization) {
// After the outstanding posted tasks have run, SetIOData should have been
// called for |ui_service_|.
- EXPECT_TRUE(ui_service()->io_data_set());
+ EXPECT_TRUE(ui_service()->previews_decider_impl_set());
}
TEST_F(PreviewsUIServiceTest, TestLogPreviewNavigationPassInCorrectParams) {
@@ -342,11 +351,11 @@ TEST_F(PreviewsUIServiceTest,
TestSetIgnorePreviewsBlacklistDecisionPassesCorrectParams) {
ui_service()->SetIgnorePreviewsBlacklistDecision(true /* ignored */);
base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(io_data()->blacklist_ignored());
+ EXPECT_TRUE(previews_decider_impl()->blacklist_ignored());
ui_service()->SetIgnorePreviewsBlacklistDecision(false /* ignored */);
base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(io_data()->blacklist_ignored());
+ EXPECT_FALSE(previews_decider_impl()->blacklist_ignored());
}
TEST_F(PreviewsUIServiceTest, TestOnIgnoreBlacklistDecisionStatusChanged) {
diff --git a/chromium/components/previews/core/BUILD.gn b/chromium/components/previews/core/BUILD.gn
index 6ab04816343..611a4892a17 100644
--- a/chromium/components/previews/core/BUILD.gn
+++ b/chromium/components/previews/core/BUILD.gn
@@ -4,12 +4,8 @@
static_library("core") {
sources = [
- "previews_amp_converter.cc",
- "previews_amp_converter.h",
"previews_black_list.cc",
"previews_black_list.h",
- "previews_black_list_item.cc",
- "previews_black_list_item.h",
"previews_decider.h",
"previews_experiments.cc",
"previews_experiments.h",
@@ -18,9 +14,6 @@ static_library("core") {
"previews_logger.cc",
"previews_logger.h",
"previews_logger_observer.h",
- "previews_opt_out_store.h",
- "previews_opt_out_store_sql.cc",
- "previews_opt_out_store_sql.h",
"previews_switches.cc",
"previews_switches.h",
"previews_user_data.cc",
@@ -31,9 +24,9 @@ static_library("core") {
deps = [
"//base",
+ "//components/blacklist/opt_out_blacklist",
"//components/variations",
"//net:net",
- "//sql",
"//third_party/re2",
"//url:url",
]
@@ -42,12 +35,9 @@ static_library("core") {
source_set("unit_tests") {
testonly = true
sources = [
- "previews_amp_converter_unittest.cc",
- "previews_black_list_item_unittest.cc",
"previews_black_list_unittest.cc",
"previews_experiments_unittest.cc",
"previews_logger_unittest.cc",
- "previews_opt_out_store_sql_unittest.cc",
"previews_user_data_unittest.cc",
]
@@ -55,11 +45,10 @@ source_set("unit_tests") {
":core",
"//base",
"//base/test:test_support",
+ "//components/blacklist/opt_out_blacklist",
"//components/variations",
"//net:net",
"//net:test_support",
- "//sql",
- "//sql:test_support",
"//testing/gtest",
"//url:url",
]
diff --git a/chromium/components/previews/core/DEPS b/chromium/components/previews/core/DEPS
index 8eb45247179..11d53fd6515 100644
--- a/chromium/components/previews/core/DEPS
+++ b/chromium/components/previews/core/DEPS
@@ -1,6 +1,6 @@
include_rules = [
+ "+components/blacklist/opt_out_blacklist",
"+components/variations",
"+net",
- "+sql",
"+third_party/re2"
]
diff --git a/chromium/components/previews/core/previews_amp_converter.cc b/chromium/components/previews/core/previews_amp_converter.cc
deleted file mode 100644
index c1e64c96f6e..00000000000
--- a/chromium/components/previews/core/previews_amp_converter.cc
+++ /dev/null
@@ -1,183 +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/previews/core/previews_amp_converter.h"
-
-#include <utility>
-
-#include "base/feature_list.h"
-#include "base/json/json_reader.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/strings/strcat.h"
-#include "base/strings/string_util.h"
-#include "base/values.h"
-#include "components/previews/core/previews_features.h"
-#include "components/variations/variations_associated_data.h"
-#include "third_party/re2/src/re2/re2.h"
-#include "url/url_constants.h"
-
-namespace previews {
-
-namespace {
-
-const char kAMPRedirectionConfig[] = "config";
-
-// Allowed URL schemes to match.
-enum MatchingScheme { HTTP, HTTPS, BOTH };
-
-} // namespace
-
-struct AMPConverterEntry {
- AMPConverterEntry(MatchingScheme matching_scheme,
- std::unique_ptr<re2::RE2> matching_path_pattern,
- const std::string& hostname_amp,
- const std::string& scheme_amp,
- const std::string& prefix,
- const std::string& suffix,
- const std::string& suffix_html)
- : matching_scheme(matching_scheme),
- matching_path_pattern(std::move(matching_path_pattern)),
- hostname_amp(hostname_amp),
- scheme_amp(scheme_amp),
- prefix(prefix),
- suffix(suffix),
- suffix_html(suffix_html) {}
-
- ~AMPConverterEntry() {}
-
- // URL scheme to match.
- const MatchingScheme matching_scheme;
-
- // RE2 pattern the URL path should match.
- const std::unique_ptr<re2::RE2> matching_path_pattern;
-
- // New AMP hostname for the URL, if any.
- const std::string hostname_amp;
-
- // New scheme for the AMP URL, if any.
- const std::string scheme_amp;
-
- // String to be prefixed to the URL path, if any.
- const std::string prefix;
-
- // String to be suffixed to the URL path, before query string and named
- // anchor, if any.
- const std::string suffix;
-
- // String to be suffixed to the URL path, before the .html extension, if
- // any.
- const std::string suffix_html;
-};
-
-PreviewsAMPConverter::PreviewsAMPConverter() {
- std::string config_text = GetFieldTrialParamValueByFeature(
- features::kAMPRedirection, kAMPRedirectionConfig);
- if (config_text.empty())
- return;
-
- std::unique_ptr<base::Value> value = base::JSONReader::Read(config_text);
- const base::ListValue* entries = nullptr;
- if (!value || !value->GetAsList(&entries))
- return;
-
- re2::RE2::Options options(re2::RE2::DefaultOptions);
- options.set_case_sensitive(true);
-
- for (const auto& entry : *entries) {
- const base::DictionaryValue* dict = nullptr;
- if (!entry.GetAsDictionary(&dict))
- continue;
- std::string host, matching_scheme_str, matching_path_pattern_str, host_amp,
- scheme_amp, prefix, suffix, suffix_html;
- dict->GetString("host", &host);
- dict->GetString("scheme", &matching_scheme_str);
- dict->GetString("pattern", &matching_path_pattern_str);
- dict->GetString("hostamp", &host_amp);
- dict->GetString("schemeamp", &scheme_amp);
- dict->GetString("prefix", &prefix);
- dict->GetString("suffix", &suffix);
- dict->GetString("suffixhtml", &suffix_html);
- MatchingScheme matching_scheme =
- matching_scheme_str == url::kHttpScheme
- ? MatchingScheme::HTTP
- : matching_scheme_str == url::kHttpsScheme ? MatchingScheme::HTTPS
- : MatchingScheme::BOTH;
-
- // If matching scheme is HTTPS or BOTH, then AMP scheme should not be http,
- // which will redirect https to http.
- DCHECK(matching_scheme == MatchingScheme::HTTP ||
- scheme_amp != url::kHttpScheme);
-
- std::unique_ptr<re2::RE2> matching_path_pattern_re2(
- std::make_unique<re2::RE2>(matching_path_pattern_str, options));
- if (host.empty() || !matching_path_pattern_re2->ok() ||
- (scheme_amp != "" && scheme_amp != url::kHttpScheme &&
- scheme_amp != url::kHttpsScheme)) {
- continue;
- }
- amp_converter_.insert(std::make_pair(
- host, std::make_unique<AMPConverterEntry>(
- matching_scheme, std::move(matching_path_pattern_re2),
- host_amp, scheme_amp, prefix, suffix, suffix_html)));
- }
-}
-
-PreviewsAMPConverter::~PreviewsAMPConverter() {}
-
-bool PreviewsAMPConverter::GetAMPURL(const GURL& url, GURL* new_amp_url) const {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
- if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS()) {
- return false;
- }
-
- const auto amp_converter_iter = amp_converter_.find(url.host());
- if (amp_converter_iter == amp_converter_.end() ||
- !re2::RE2::FullMatch(
- url.path(), *amp_converter_iter->second->matching_path_pattern)) {
- return false;
- }
- const auto& entry = *amp_converter_iter->second;
-
- // Check for allowed URL schemes.
- if (entry.matching_scheme != MatchingScheme::BOTH &&
- (entry.matching_scheme == MatchingScheme::HTTP) !=
- url.SchemeIs(url::kHttpScheme)) {
- return false;
- }
-
- GURL amp_url(url);
- GURL::Replacements url_replacements;
- if (!entry.hostname_amp.empty())
- url_replacements.SetHostStr(entry.hostname_amp);
- if (!entry.scheme_amp.empty()) {
- // Avoid https to http redirection.
- if (url.scheme() == url::kHttpsScheme &&
- entry.scheme_amp == url::kHttpScheme) {
- return false;
- }
- url_replacements.SetSchemeStr(entry.scheme_amp);
- }
-
- std::string path;
- if (!entry.prefix.empty() || !entry.suffix.empty() ||
- !entry.suffix_html.empty()) {
- DCHECK(entry.prefix.empty() || entry.prefix[0] == '/');
- path = base::StrCat({entry.prefix, url.path(), entry.suffix});
- if (!entry.suffix_html.empty() &&
- base::EndsWith(path, ".html", base::CompareCase::SENSITIVE)) {
- // Insert suffix_html before the .html extension.
- path.insert(path.length() - 5, entry.suffix_html);
- }
- url_replacements.SetPathStr(path);
- }
- amp_url = amp_url.ReplaceComponents(url_replacements);
- if (!amp_url.is_valid())
- return false;
- new_amp_url->Swap(&amp_url);
- return true;
-}
-
-} // namespace previews
diff --git a/chromium/components/previews/core/previews_amp_converter.h b/chromium/components/previews/core/previews_amp_converter.h
deleted file mode 100644
index e5e4a837fc8..00000000000
--- a/chromium/components/previews/core/previews_amp_converter.h
+++ /dev/null
@@ -1,44 +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_PREVIEWS_CORE_PREVIEWS_AMP_REDIRECTION_H_
-#define COMPONENTS_PREVIEWS_CORE_PREVIEWS_AMP_REDIRECTION_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "base/threading/thread_checker.h"
-#include "url/gurl.h"
-
-namespace previews {
-
-// Contains the parameters for converting URLs for one domain.
-struct AMPConverterEntry;
-
-// Checks whether an URL is whitelisted for AMP redirection previews and
-// implements the scheme used to convert URL to its AMP version.
-class PreviewsAMPConverter {
- public:
- PreviewsAMPConverter();
-
- virtual ~PreviewsAMPConverter();
-
- // Returns true if |url| can be converted to its AMP version. |new_amp_url|
- // will contain the AMP URL.
- bool GetAMPURL(const GURL& url, GURL* new_amp_url) const;
-
- private:
- // Maps the hostname to its AMP conversion scheme.
- std::map<std::string, std::unique_ptr<AMPConverterEntry>> amp_converter_;
-
- THREAD_CHECKER(thread_checker_);
-
- DISALLOW_COPY_AND_ASSIGN(PreviewsAMPConverter);
-};
-
-} // namespace previews
-
-#endif // COMPONENTS_PREVIEWS_CORE_PREVIEWS_AMP_REDIRECTION_H_
diff --git a/chromium/components/previews/core/previews_amp_converter_unittest.cc b/chromium/components/previews/core/previews_amp_converter_unittest.cc
deleted file mode 100644
index 2583dc6384d..00000000000
--- a/chromium/components/previews/core/previews_amp_converter_unittest.cc
+++ /dev/null
@@ -1,278 +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/previews/core/previews_amp_converter.h"
-
-#include <memory>
-#include <string>
-
-#include "base/test/scoped_feature_list.h"
-#include "components/previews/core/previews_features.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace previews {
-
-namespace {
-
-class PreviewsAMPConverterTest : public testing::Test {
- public:
- PreviewsAMPConverterTest() {}
-
- void Initialize() { amp_converter_.reset(new PreviewsAMPConverter()); }
-
- PreviewsAMPConverter* amp_converter() { return amp_converter_.get(); }
-
- void CreateAMPRedirectionFieldTrialWithParams(
- std::initializer_list<
- typename std::map<std::string, std::string>::value_type> params) {
- scoped_feature_list_.InitAndEnableFeatureWithParameters(
- features::kAMPRedirection, params);
- }
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
- std::unique_ptr<PreviewsAMPConverter> amp_converter_;
-};
-
-TEST_F(PreviewsAMPConverterTest, TestDisallowedByDefault) {
- Initialize();
-
- const char* urls[] = {"", "http://test.com", "https://test.com",
- "http://www.test.com", "http://test.com/index.html"};
- GURL amp_url;
- for (const char* url : urls) {
- EXPECT_FALSE(amp_converter()->GetAMPURL(GURL(url), &amp_url)) << url;
- }
-}
-
-TEST_F(PreviewsAMPConverterTest, TestEmptyConfig) {
- CreateAMPRedirectionFieldTrialWithParams({{"config", ""}});
- Initialize();
-
- const char* urls[] = {"", "http://test.com", "https://test.com",
- "http://www.test.com", "http://test.com/index.html"};
- GURL amp_url;
- for (const char* url : urls) {
- EXPECT_FALSE(amp_converter()->GetAMPURL(GURL(url), &amp_url)) << url;
- }
-}
-
-TEST_F(PreviewsAMPConverterTest, TestMalformedConfig) {
- CreateAMPRedirectionFieldTrialWithParams({{"config", "config = foo bar"}});
- Initialize();
-
- const char* urls[] = {"", "http://test.com", "https://test.com",
- "http://www.test.com", "http://test.com/index.html"};
- GURL amp_url;
- for (const char* url : urls) {
- EXPECT_FALSE(amp_converter()->GetAMPURL(GURL(url), &amp_url)) << url;
- }
-}
-
-TEST_F(PreviewsAMPConverterTest, TestFieldTrialEnabled) {
- struct {
- const char* url;
- bool expected_result;
- const char* expected_amp_url;
- } tests[] = {
- {"", false},
- {"http://www.test.com", false},
- {"http://test.com", true, "http://test.com/"},
- {"http://test2.com", false},
- {"https://test.com", true, "https://test.com/"},
- {"http://test.com/index.html", true, "http://test.com/index.html"},
-
- // Case sensitive tests.
- {"http://Test.com", true, "http://test.com/"},
- {"http://TEST.com/Index.html", true, "http://test.com/Index.html"},
- };
-
- CreateAMPRedirectionFieldTrialWithParams(
- {{"config", "[{\"host\":\"test.com\", \"pattern\":\".*\"}]"}});
- Initialize();
-
- for (const auto& test : tests) {
- GURL amp_url;
- EXPECT_EQ(test.expected_result,
- amp_converter()->GetAMPURL(GURL(test.url), &amp_url))
- << test.url;
- if (test.expected_amp_url)
- EXPECT_EQ(test.expected_amp_url, amp_url.spec());
- }
-}
-
-TEST_F(PreviewsAMPConverterTest, TestMultipleConversions) {
- CreateAMPRedirectionFieldTrialWithParams(
- {{"config",
- "[{\"host\":\"test.com\", \"pattern\":\".*\", "
- "\"hostamp\":\"amp.test.com\"},"
- "{\"host\":\"testprefix.com\", \"pattern\":\".*\", "
- "\"prefix\":\"/amp\"},"
- "{\"host\":\"testsuffix.com\", \"pattern\":\".*\", "
- "\"suffix\":\".amp\"},"
- "{\"host\":\"testsuffixhtml.com\", \"pattern\":\".*\", "
- "\"suffixhtml\":\".amp\"},"
- "{\"host\":\"prefixandsuffix.com\", \"pattern\":\".*\", "
- "\"prefix\":\"/pre\", \"suffix\":\".suf\"}]"}});
- Initialize();
-
- struct {
- const char* url;
- bool expected_result;
- const char* expected_amp_url;
- } tests[] = {
- {"", false},
- {"http://www.test.com", false},
- {"http://test.com", true, "http://amp.test.com/"},
- {"https://test.com", true, "https://amp.test.com/"},
- {"http://test.com/index.html?id=foo#anchor", true,
- "http://amp.test.com/index.html?id=foo#anchor"},
- {"http://testprefix.com/index.html?id=foo#anchor", true,
- "http://testprefix.com/amp/index.html?id=foo#anchor"},
- {"http://testprefix.com", true, "http://testprefix.com/amp/"},
- {"http://testsuffix.com/index.html?id=foo#anchor", true,
- "http://testsuffix.com/index.html.amp?id=foo#anchor"},
- {"http://testsuffixhtml.com/index.html?id=foo#anchor", true,
- "http://testsuffixhtml.com/index.amp.html?id=foo#anchor"},
- {"http://prefixandsuffix.com/index.html?id=foo#anchor", true,
- "http://prefixandsuffix.com/pre/index.html."
- "suf?id=foo#anchor"},
- };
-
- for (const auto& test : tests) {
- GURL amp_url;
- EXPECT_EQ(test.expected_result,
- amp_converter()->GetAMPURL(GURL(test.url), &amp_url))
- << test.url;
- if (test.expected_amp_url)
- EXPECT_EQ(test.expected_amp_url, amp_url.spec());
- }
-}
-
-TEST_F(PreviewsAMPConverterTest, TestPathPatterns) {
- CreateAMPRedirectionFieldTrialWithParams(
- {{"config",
- "[{\"host\":\"test1.com\", \"pattern\":\"/foo/bar/.+\"},"
- "{\"host\":\"test2.com\", \"pattern\":\"/(foo|bar)/.*.html$\"},"
- "{\"host\":\"test3.com\", "
- "\"pattern\":\"/[0-9]{4}/[0-9]{2}/[0-9]{2}/.*.html$\"}]"}});
- Initialize();
-
- struct {
- const char* url;
- bool expected_result;
- const char* expected_amp_url;
- } tests[] = {
- {"http://test1.com", false},
- {"http://test1.com/foo/bar", false},
- {"http://test1.com/foo/bar/", false},
- {"http://test1.com/foo/bar/baz", true, "http://test1.com/foo/bar/baz"},
- {"http://test2.com/foo/index.html", true,
- "http://test2.com/foo/index.html"},
- {"http://test2.com/bar/b.html", true, "http://test2.com/bar/b.html"},
- {"http://test2.com/baz/b.html", false},
- {"http://test3.com/2017/01/02/index.html", true,
- "http://test3.com/2017/01/02/index.html"}};
-
- for (const auto& test : tests) {
- GURL amp_url;
- EXPECT_EQ(test.expected_result,
- amp_converter()->GetAMPURL(GURL(test.url), &amp_url))
- << test.url;
- if (test.expected_amp_url)
- EXPECT_EQ(test.expected_amp_url, amp_url.spec());
- }
-}
-
-TEST_F(PreviewsAMPConverterTest, TestURLScheme) {
- CreateAMPRedirectionFieldTrialWithParams(
- {{"config",
- "[{\"host\":\"test_http.com\", \"scheme\":\"http\", "
- "\"pattern\":\".*\"},"
- "{\"host\":\"test_https.com\", \"scheme\":\"https\", "
- "\"pattern\":\".*\"},"
- "{\"host\":\"test_both.com\", \"pattern\":\".*\"},"
- "{\"host\":\"test_amp_http.com\", \"scheme\":\"http\", "
- "\"pattern\":\".*\", \"schemeamp\":\"http\"},"
- "{\"host\":\"test_amp_https.com\", \"pattern\":\".*\", "
- "\"schemeamp\":\"https\"},"
- "{\"host\":\"test_amp_invalid.com\", "
- "\"pattern\":\".*\", \"schemeamp\":\"invalid\"}]"}});
- Initialize();
-
- struct {
- const char* url;
- bool expected_result;
- const char* expected_amp_url;
- } tests[] = {
- {"http://test_http.com", true, "http://test_http.com/"},
- {"https://test_http.com", false},
- {"http://test_https.com", false},
- {"https://test_https.com", true, "https://test_https.com/"},
- {"http://test_both.com", true, "http://test_both.com/"},
- {"https://test_both.com", true, "https://test_both.com/"},
- {"http://test_amp_http.com", true, "http://test_amp_http.com/"},
- {"https://test_amp_http.com", false, /* Should not redirect to http */},
- {"http://test_amp_https.com", true, "https://test_amp_https.com/"},
- {"https://test_amp_https.com", true, "https://test_amp_https.com/"},
- {"https://test_amp_invalid.com", false},
- {"http://test_amp_invalid.com", false},
- };
-
- for (const auto& test : tests) {
- GURL amp_url;
- EXPECT_EQ(test.expected_result,
- amp_converter()->GetAMPURL(GURL(test.url), &amp_url))
- << test.url;
- if (test.expected_amp_url)
- EXPECT_EQ(test.expected_amp_url, amp_url.spec());
- }
-}
-
-TEST_F(PreviewsAMPConverterTest, TestFullRegex) {
- CreateAMPRedirectionFieldTrialWithParams(
- {{"config",
- "[{\"host\":\"www.test1.com\", \"scheme\":\"http\", "
- "\"pattern\":\"^/201[67]/[0-9]{2}/[0-9]{2}/.*/index.html$\", "
- "\"schemeamp\":\"https\", \"hostamp\":\"www.amp.test1.com\", "
- "\"prefix\":\"/foo\"},"
- "{\"host\":\"www.test2.com\", \"scheme\":\"http\", "
- "\"pattern\":\"^/.*/201[67]/[0-9]{2}/[0-9]{2}/.*[.]html$\", "
- "\"suffixhtml\":\".amp\"},"
- "{\"host\":\"m.test3.com\", \"pattern\":\"^/foo/entry/bar_.*/$\", "
- "\"suffix\":\"post\"}]"}});
- Initialize();
-
- struct {
- const char* url;
- bool expected_result;
- const char* expected_amp_url;
- } tests[] = {
- {"http://www.test1.com", false},
- {"http://www.test1.com/2017/09/05/foo-bar/index.html", true,
- "https://www.amp.test1.com/foo/2017/09/05/foo-bar/index.html"},
- {"http://www.test1.com/2016/01/12/baz-foo/index.html", true,
- "https://www.amp.test1.com/foo/2016/01/12/baz-foo/index.html"},
- {"http://www.test2.com", false},
- {"http://www.test2.com/foo/2017/09/05/foo-bar/index.html", true,
- "http://www.test2.com/foo/2017/09/05/foo-bar/index.amp.html"},
- {"http://www.test2.com/bar/2016/04/23/foo-bar/index.html", true,
- "http://www.test2.com/bar/2016/04/23/foo-bar/index.amp.html"},
- {"http://m.test3.com/foo/entry/bar_baz/", true,
- "http://m.test3.com/foo/entry/bar_baz/post"},
- };
-
- for (const auto& test : tests) {
- GURL amp_url;
- EXPECT_EQ(test.expected_result,
- amp_converter()->GetAMPURL(GURL(test.url), &amp_url))
- << test.url;
- if (test.expected_amp_url)
- EXPECT_EQ(test.expected_amp_url, amp_url.spec());
- }
-}
-
-} // namespace
-
-} // namespace previews
diff --git a/chromium/components/previews/core/previews_black_list.cc b/chromium/components/previews/core/previews_black_list.cc
index 9f76000a671..d718e8412ed 100644
--- a/chromium/components/previews/core/previews_black_list.cc
+++ b/chromium/components/previews/core/previews_black_list.cc
@@ -10,7 +10,6 @@
#include "base/optional.h"
#include "base/strings/stringprintf.h"
#include "base/time/clock.h"
-#include "components/previews/core/previews_black_list_item.h"
#include "components/previews/core/previews_experiments.h"
#include "url/gurl.h"
@@ -18,56 +17,76 @@ namespace previews {
namespace {
-void EvictOldestOptOut(BlackListItemMap* black_list_item_map) {
- // TODO(ryansturm): Add UMA. crbug.com/647717
- base::Optional<base::Time> oldest_opt_out;
- BlackListItemMap::iterator item_to_delete = black_list_item_map->end();
- for (BlackListItemMap::iterator iter = black_list_item_map->begin();
- iter != black_list_item_map->end(); ++iter) {
- if (!iter->second->most_recent_opt_out_time()) {
- // If there is no opt out time, this is a good choice to evict.
- item_to_delete = iter;
- break;
- }
- if (!oldest_opt_out || iter->second->most_recent_opt_out_time().value() <
- oldest_opt_out.value()) {
- oldest_opt_out = iter->second->most_recent_opt_out_time().value();
- item_to_delete = iter;
- }
+PreviewsEligibilityReason BlacklistReasonToPreviewsReason(
+ blacklist::BlacklistReason reason) {
+ switch (reason) {
+ case blacklist::BlacklistReason::kBlacklistNotLoaded:
+ return PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED;
+ case blacklist::BlacklistReason::kUserOptedOutInSession:
+ return PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT;
+ case blacklist::BlacklistReason::kUserOptedOutInGeneral:
+ return PreviewsEligibilityReason::USER_BLACKLISTED;
+ case blacklist::BlacklistReason::kUserOptedOutOfHost:
+ return PreviewsEligibilityReason::HOST_BLACKLISTED;
+ case blacklist::BlacklistReason::kUserOptedOutOfType:
+ NOTREACHED() << "Previews does not support type-base blacklisting";
+ return PreviewsEligibilityReason::ALLOWED;
+ case blacklist::BlacklistReason::kAllowed:
+ return PreviewsEligibilityReason::ALLOWED;
}
- black_list_item_map->erase(item_to_delete);
}
-// Returns the PreviewsBlackListItem representing |host_name| in
-// |black_list_item_map|. If there is no item for |host_name|, returns null.
-PreviewsBlackListItem* GetBlackListItemFromMap(
- const BlackListItemMap& black_list_item_map,
- const std::string& host_name) {
- BlackListItemMap::const_iterator iter = black_list_item_map.find(host_name);
- if (iter != black_list_item_map.end())
- return iter->second.get();
- return nullptr;
}
-} // namespace
-
PreviewsBlackList::PreviewsBlackList(
- std::unique_ptr<PreviewsOptOutStore> opt_out_store,
+ std::unique_ptr<blacklist::OptOutStore> opt_out_store,
base::Clock* clock,
- PreviewsBlacklistDelegate* blacklist_delegate)
- : loaded_(false),
- opt_out_store_(std::move(opt_out_store)),
- clock_(clock),
- blacklist_delegate_(blacklist_delegate),
- weak_factory_(this) {
- DCHECK(blacklist_delegate_);
- if (opt_out_store_) {
- opt_out_store_->LoadBlackList(base::Bind(
- &PreviewsBlackList::LoadBlackListDone, weak_factory_.GetWeakPtr()));
- } else {
- LoadBlackListDone(std::make_unique<BlackListItemMap>(),
- CreateHostIndifferentBlackListItem());
- }
+ blacklist::OptOutBlacklistDelegate* blacklist_delegate,
+ blacklist::BlacklistData::AllowedTypesAndVersions allowed_types)
+ : blacklist::OptOutBlacklist(std::move(opt_out_store),
+ clock,
+ blacklist_delegate),
+ allowed_types_(std::move(allowed_types)) {
+ DCHECK(blacklist_delegate);
+ Init();
+}
+
+bool PreviewsBlackList::ShouldUseSessionPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const {
+ *duration = params::SingleOptOutDuration();
+ *history = 1;
+ *threshold = 1;
+ return true;
+}
+
+bool PreviewsBlackList::ShouldUsePersistentPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const {
+ *history = params::MaxStoredHistoryLengthForHostIndifferentBlackList();
+ *threshold = params::HostIndifferentBlackListOptOutThreshold();
+ *duration = params::HostIndifferentBlackListPerHostDuration();
+ return true;
+}
+bool PreviewsBlackList::ShouldUseHostPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold,
+ size_t* max_hosts) const {
+ *max_hosts = params::MaxInMemoryHostsInBlackList();
+ *history = params::MaxStoredHistoryLengthForPerHostBlackList();
+ *threshold = params::PerHostBlackListOptOutThreshold();
+ *duration = params::PerHostBlackListDuration();
+ return true;
+}
+bool PreviewsBlackList::ShouldUseTypePolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const {
+ return false;
+}
+
+blacklist::BlacklistData::AllowedTypesAndVersions
+PreviewsBlackList::GetAllowedTypes() const {
+ return allowed_types_;
}
PreviewsBlackList::~PreviewsBlackList() {}
@@ -75,7 +94,6 @@ PreviewsBlackList::~PreviewsBlackList() {}
base::Time PreviewsBlackList::AddPreviewNavigation(const GURL& url,
bool opt_out,
PreviewsType type) {
- DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(url.has_host());
base::BooleanHistogram::FactoryGet(
@@ -84,205 +102,27 @@ base::Time PreviewsBlackList::AddPreviewNavigation(const GURL& url,
base::HistogramBase::kUmaTargetedHistogramFlag)
->Add(opt_out);
- base::Time now = clock_->Now();
- if (opt_out) {
- last_opt_out_time_ = now;
- }
- // If the |black_list_item_map_| has been loaded from |opt_out_store_|,
- // synchronous operations will be accurate. Otherwise, queue the task to run
- // asynchronously.
- if (loaded_) {
- AddPreviewNavigationSync(url, opt_out, type, now);
- } else {
- QueuePendingTask(base::Bind(&PreviewsBlackList::AddPreviewNavigationSync,
- base::Unretained(this), url, opt_out, type,
- now));
- }
-
- return now;
-}
-
-void PreviewsBlackList::AddPreviewNavigationSync(const GURL& url,
- bool opt_out,
- PreviewsType type,
- base::Time time) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(url.has_host());
- DCHECK(loaded_);
- DCHECK(host_indifferent_black_list_item_);
- DCHECK(black_list_item_map_);
- std::string host_name = url.host();
- PreviewsBlackListItem* item =
- GetOrCreateBlackListItemForMap(black_list_item_map_.get(), host_name);
-
- // Check if the host has already been blacklisted.
- bool host_was_blacklisted = item->IsBlackListed(time);
- item->AddPreviewNavigation(opt_out, time);
-
- if (!host_was_blacklisted && item->IsBlackListed(time)) {
- // Notify |blacklist_delegate_| about a new blacklisted host.
- blacklist_delegate_->OnNewBlacklistedHost(url.host(), time);
- }
-
- DCHECK_LE(black_list_item_map_->size(),
- params::MaxInMemoryHostsInBlackList());
-
- // Check if the user has already been blacklisted.
- bool user_was_blacklisted =
- host_indifferent_black_list_item_->IsBlackListed(time);
- host_indifferent_black_list_item_->AddPreviewNavigation(opt_out, time);
-
- if (user_was_blacklisted !=
- host_indifferent_black_list_item_->IsBlackListed(time)) {
- // Notify |blacklist_delegate_| on user blacklisted status change.
- blacklist_delegate_->OnUserBlacklistedStatusChange(
- host_indifferent_black_list_item_->IsBlackListed(time));
- }
-
- if (!opt_out_store_)
- return;
- opt_out_store_->AddPreviewNavigation(opt_out, host_name, type, time);
+ return blacklist::OptOutBlacklist::AddEntry(url.host(), opt_out,
+ static_cast<int>(type));
}
PreviewsEligibilityReason PreviewsBlackList::IsLoadedAndAllowed(
const GURL& url,
PreviewsType type,
+ bool ignore_long_term_black_list_rules,
std::vector<PreviewsEligibilityReason>* passed_reasons) const {
- DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(url.has_host());
- if (!loaded_)
- return PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED;
- passed_reasons->push_back(
- PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED);
- DCHECK(black_list_item_map_);
- if (last_opt_out_time_ &&
- clock_->Now() <
- last_opt_out_time_.value() + params::SingleOptOutDuration()) {
- return PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT;
- }
- passed_reasons->push_back(PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT);
- if (host_indifferent_black_list_item_->IsBlackListed(clock_->Now()))
- return PreviewsEligibilityReason::USER_BLACKLISTED;
- passed_reasons->push_back(PreviewsEligibilityReason::USER_BLACKLISTED);
- PreviewsBlackListItem* black_list_item =
- GetBlackListItemFromMap(*black_list_item_map_, url.host());
- if (black_list_item && black_list_item->IsBlackListed(clock_->Now()))
- return PreviewsEligibilityReason::HOST_BLACKLISTED;
- passed_reasons->push_back(PreviewsEligibilityReason::HOST_BLACKLISTED);
- return PreviewsEligibilityReason::ALLOWED;
-}
-
-void PreviewsBlackList::ClearBlackList(base::Time begin_time,
- base::Time end_time) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_LE(begin_time, end_time);
- // If the |black_list_item_map_| has been loaded from |opt_out_store_|,
- // synchronous operations will be accurate. Otherwise, queue the task to run
- // asynchronously.
- if (loaded_) {
- ClearBlackListSync(begin_time, end_time);
- } else {
- QueuePendingTask(base::Bind(&PreviewsBlackList::ClearBlackListSync,
- base::Unretained(this), begin_time, end_time));
- }
-}
-
-void PreviewsBlackList::ClearBlackListSync(base::Time begin_time,
- base::Time end_time) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(loaded_);
- DCHECK_LE(begin_time, end_time);
-
- // Clear last_opt_out_time_ if the period being cleared is larger than the
- // short black list timeout and the last time the user opted out was before
- // |end_time|.
- if (end_time - begin_time > params::SingleOptOutDuration() &&
- last_opt_out_time_ && last_opt_out_time_.value() < end_time) {
- last_opt_out_time_.reset();
- }
- black_list_item_map_.reset();
- host_indifferent_black_list_item_.reset();
- loaded_ = false;
-
- // Notify |blacklist_delegate_| that the blacklist is cleared.
- blacklist_delegate_->OnBlacklistCleared(clock_->Now());
-
- // Delete relevant entries and reload the blacklist into memory.
- if (opt_out_store_) {
- opt_out_store_->ClearBlackList(begin_time, end_time);
- opt_out_store_->LoadBlackList(base::Bind(
- &PreviewsBlackList::LoadBlackListDone, weak_factory_.GetWeakPtr()));
- } else {
- LoadBlackListDone(std::make_unique<BlackListItemMap>(),
- CreateHostIndifferentBlackListItem());
- }
-}
-
-void PreviewsBlackList::QueuePendingTask(base::Closure callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(!loaded_);
- DCHECK(!callback.is_null());
- pending_callbacks_.emplace(callback);
-}
-
-void PreviewsBlackList::LoadBlackListDone(
- std::unique_ptr<BlackListItemMap> black_list_item_map,
- std::unique_ptr<PreviewsBlackListItem> host_indifferent_black_list_item) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(black_list_item_map);
- DCHECK(host_indifferent_black_list_item);
- DCHECK_LE(black_list_item_map->size(), params::MaxInMemoryHostsInBlackList());
- loaded_ = true;
- black_list_item_map_ = std::move(black_list_item_map);
- host_indifferent_black_list_item_ =
- std::move(host_indifferent_black_list_item);
-
- // Notify |blacklist_delegate_| on current user blacklisted status.
- blacklist_delegate_->OnUserBlacklistedStatusChange(
- host_indifferent_black_list_item_->IsBlackListed(clock_->Now()));
- // Notify the |blacklist_delegate_| on historical blacklisted hosts.
- for (const auto& entry : *black_list_item_map_) {
- if (entry.second->IsBlackListed(clock_->Now())) {
- blacklist_delegate_->OnNewBlacklistedHost(
- entry.first, *entry.second->most_recent_opt_out_time());
- }
+ std::vector<blacklist::BlacklistReason> passed_blacklist_reasons;
+ blacklist::BlacklistReason reason =
+ blacklist::OptOutBlacklist::IsLoadedAndAllowed(
+ url.host(), static_cast<int>(type), ignore_long_term_black_list_rules,
+ &passed_blacklist_reasons);
+ for (auto passed_reason : passed_blacklist_reasons) {
+ passed_reasons->push_back(BlacklistReasonToPreviewsReason(passed_reason));
}
- // Run all pending tasks. |loaded_| may change if ClearBlackList is queued.
- while (pending_callbacks_.size() > 0 && loaded_) {
- pending_callbacks_.front().Run();
- pending_callbacks_.pop();
- }
-}
-
-// static
-PreviewsBlackListItem* PreviewsBlackList::GetOrCreateBlackListItemForMap(
- BlackListItemMap* black_list_item_map,
- const std::string& host_name) {
- PreviewsBlackListItem* black_list_item =
- GetBlackListItemFromMap(*black_list_item_map, host_name);
- if (black_list_item)
- return black_list_item;
- if (black_list_item_map->size() >= params::MaxInMemoryHostsInBlackList())
- EvictOldestOptOut(black_list_item_map);
- DCHECK_LT(black_list_item_map->size(), params::MaxInMemoryHostsInBlackList());
- black_list_item = new PreviewsBlackListItem(
- params::MaxStoredHistoryLengthForPerHostBlackList(),
- params::PerHostBlackListOptOutThreshold(),
- params::PerHostBlackListDuration());
- black_list_item_map->operator[](host_name) =
- base::WrapUnique(black_list_item);
- return black_list_item;
-}
-
-// static
-std::unique_ptr<PreviewsBlackListItem>
-PreviewsBlackList::CreateHostIndifferentBlackListItem() {
- return std::make_unique<PreviewsBlackListItem>(
- params::MaxStoredHistoryLengthForHostIndifferentBlackList(),
- params::HostIndifferentBlackListOptOutThreshold(),
- params::HostIndifferentBlackListPerHostDuration());
+ return BlacklistReasonToPreviewsReason(reason);
}
} // namespace previews
diff --git a/chromium/components/previews/core/previews_black_list.h b/chromium/components/previews/core/previews_black_list.h
index ef56ed75292..c58f72267bd 100644
--- a/chromium/components/previews/core/previews_black_list.h
+++ b/chromium/components/previews/core/previews_black_list.h
@@ -7,20 +7,19 @@
#include <stdint.h>
+#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/callback.h"
-#include "base/containers/queue.h"
#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
#include "base/optional.h"
-#include "base/threading/thread_checker.h"
#include "base/time/time.h"
-#include "components/previews/core/previews_black_list_delegate.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_delegate.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_store.h"
#include "components/previews/core/previews_experiments.h"
-#include "components/previews/core/previews_opt_out_store.h"
class GURL;
@@ -29,7 +28,6 @@ class Clock;
}
namespace previews {
-class PreviewsBlackListItem;
enum class PreviewsEligibilityReason {
// The preview navigation was allowed.
@@ -77,18 +75,14 @@ enum class PreviewsEligibilityReason {
// browsing history), domains are reported as black listed. The list stores no
// more than previews::params::MaxInMemoryHostsInBlackList hosts in-memory,
// which defaults to 100.
-class PreviewsBlackList {
+class PreviewsBlackList : public blacklist::OptOutBlacklist {
public:
- // |opt_out_store| is the backing store to retrieve and store black list
- // information, and can be null. When |opt_out_store| is null, the in-memory
- // map will be immediately loaded to empty. If |opt_out_store| is non-null,
- // it will be used to load the in-memory map asynchronously.
- // |blacklist_delegate| is a single object listening for blacklist events, and
- // it is guaranteed to overlive the life time of |this|.
- PreviewsBlackList(std::unique_ptr<PreviewsOptOutStore> opt_out_store,
- base::Clock* clock,
- PreviewsBlacklistDelegate* blacklist_delegate);
- virtual ~PreviewsBlackList();
+ PreviewsBlackList(
+ std::unique_ptr<blacklist::OptOutStore> opt_out_store,
+ base::Clock* clock,
+ blacklist::OptOutBlacklistDelegate* blacklist_delegate,
+ blacklist::BlacklistData::AllowedTypesAndVersions allowed_types);
+ ~PreviewsBlackList() override;
// Asynchronously adds a new navigation to to the in-memory black list and
// backing store. |opt_out| is whether the user opted out of the preview or
@@ -108,78 +102,29 @@ class PreviewsBlackList {
virtual PreviewsEligibilityReason IsLoadedAndAllowed(
const GURL& url,
PreviewsType type,
+ bool ignore_long_term_black_list_rules,
std::vector<PreviewsEligibilityReason>* passed_reasons) const;
- // Asynchronously deletes all entries in the in-memory black list. Informs
- // the backing store to delete entries between |begin_time| and |end_time|,
- // and reloads entries into memory from the backing store. If the embedder
- // passed in a null store, resets all history in the in-memory black list.
- void ClearBlackList(base::Time begin_time, base::Time end_time);
-
- // Returns a new PreviewsBlackListItem representing |host_name|. Adds the new
- // item to |black_list_item_map|.
- static PreviewsBlackListItem* GetOrCreateBlackListItemForMap(
- BlackListItemMap* black_list_item_map,
- const std::string& host_name);
-
- // Returns a new PreviewsBlackListItem for the host indifferent black list
- // that does not consider host name when determining eligibility.
- static std::unique_ptr<PreviewsBlackListItem>
- CreateHostIndifferentBlackListItem();
+ protected:
+ // blacklist::OptOutBlacklist (virtual for testing):
+ bool ShouldUseSessionPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const override;
+ bool ShouldUsePersistentPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const override;
+ bool ShouldUseHostPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold,
+ size_t* max_hosts) const override;
+ bool ShouldUseTypePolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const override;
+ blacklist::BlacklistData::AllowedTypesAndVersions GetAllowedTypes()
+ const override;
private:
- // Synchronous version of AddPreviewNavigation method. |time| is the time
- // stamp of when the navigation was determined to be an opt-out or non-opt
- // out.
- void AddPreviewNavigationSync(const GURL& host_name,
- bool opt_out,
- PreviewsType type,
- base::Time time);
-
- // Synchronous version of ClearBlackList method.
- void ClearBlackListSync(base::Time begin_time, base::Time end_time);
-
- // Callback passed to the backing store when loading black list information.
- // Moves the |black_list_item_map| and |host_indifferent_black_list_item| into
- // the in-memory black list and runs any outstanding tasks.
- void LoadBlackListDone(
- std::unique_ptr<BlackListItemMap> black_list_item_map,
- std::unique_ptr<PreviewsBlackListItem> host_indifferent_black_list_item);
-
- // Called while waiting for the black list to be loaded from the backing
- // store.
- // Enqueues a task to run when when loading black list information has
- // completed. Maintains the order that tasks were called in.
- void QueuePendingTask(base::Closure callback);
-
- // Map maintaining the in-memory black list.
- std::unique_ptr<BlackListItemMap> black_list_item_map_;
-
- // Host indifferent opt out history.
- std::unique_ptr<PreviewsBlackListItem> host_indifferent_black_list_item_;
-
- // Whether the black list is done being loaded from the backing store.
- bool loaded_;
-
- // The time of the last opt out for this session.
- base::Optional<base::Time> last_opt_out_time_;
-
- // The backing store of the black list information.
- std::unique_ptr<PreviewsOptOutStore> opt_out_store_;
-
- // Callbacks to be run after loading information from the backing store has
- // completed.
- base::queue<base::Closure> pending_callbacks_;
-
- base::Clock* clock_;
-
- // The delegate listening to this blacklist. |blacklist_delegate_| lifetime is
- // guaranteed to overlive |this|.
- PreviewsBlacklistDelegate* blacklist_delegate_;
-
- base::ThreadChecker thread_checker_;
-
- base::WeakPtrFactory<PreviewsBlackList> weak_factory_;
+ const blacklist::BlacklistData::AllowedTypesAndVersions allowed_types_;
DISALLOW_COPY_AND_ASSIGN(PreviewsBlackList);
};
diff --git a/chromium/components/previews/core/previews_black_list_unittest.cc b/chromium/components/previews/core/previews_black_list_unittest.cc
index fb851da95fe..654d5784bf7 100644
--- a/chromium/components/previews/core/previews_black_list_unittest.cc
+++ b/chromium/components/previews/core/previews_black_list_unittest.cc
@@ -18,14 +18,14 @@
#include "base/metrics/field_trial.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/simple_test_clock.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
-#include "components/previews/core/previews_black_list_delegate.h"
-#include "components/previews/core/previews_black_list_item.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_delegate.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_blacklist_item.h"
+#include "components/blacklist/opt_out_blacklist/opt_out_store.h"
#include "components/previews/core/previews_experiments.h"
-#include "components/previews/core/previews_opt_out_store.h"
#include "components/variations/variations_associated_data.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -35,112 +35,58 @@ namespace previews {
namespace {
-void RunLoadCallback(
- LoadBlackListCallback callback,
- std::unique_ptr<BlackListItemMap> black_list_item_map,
- std::unique_ptr<PreviewsBlackListItem> host_indifferent_black_list_item) {
- callback.Run(std::move(black_list_item_map),
- std::move(host_indifferent_black_list_item));
-}
-
// Mock class to test that PreviewsBlackList notifies the delegate with correct
// events (e.g. New host blacklisted, user blacklisted, and blacklist cleared).
-class TestPreviewsBlacklistDelegate : public PreviewsBlacklistDelegate {
+class TestOptOutBlacklistDelegate : public blacklist::OptOutBlacklistDelegate {
public:
- TestPreviewsBlacklistDelegate()
- : user_blacklisted_(false),
- blacklist_cleared_(false),
- blacklist_cleared_time_(base::Time::Now()) {}
+ TestOptOutBlacklistDelegate() {}
- // PreviewsBlacklistDelegate:
+ // blacklist::OptOutBlacklistDelegate:
void OnNewBlacklistedHost(const std::string& host, base::Time time) override {
- blacklisted_hosts_[host] = time;
}
void OnUserBlacklistedStatusChange(bool blacklisted) override {
- user_blacklisted_ = blacklisted;
}
void OnBlacklistCleared(base::Time time) override {
- blacklist_cleared_ = true;
- blacklist_cleared_time_ = time;
- }
-
- // Gets the set of blacklisted hosts recorded.
- const std::unordered_map<std::string, base::Time>& blacklisted_hosts() const {
- return blacklisted_hosts_;
}
-
- // Gets the state of user blacklisted status.
- bool user_blacklisted() const { return user_blacklisted_; }
-
- // Gets the state of blacklisted cleared status of |this| for testing.
- bool blacklist_cleared() const { return blacklist_cleared_; }
-
- // Gets the event time of blacklist is as cleared.
- base::Time blacklist_cleared_time() const { return blacklist_cleared_time_; }
-
- private:
- // The user blacklisted status of |this| blacklist_delegate.
- bool user_blacklisted_;
-
- // Check if the blacklist is notified as cleared on |this| blacklist_delegate.
- bool blacklist_cleared_;
-
- // The time when blacklist is cleared.
- base::Time blacklist_cleared_time_;
-
- // |this| blacklist_delegate's collection of blacklisted hosts.
- std::unordered_map<std::string, base::Time> blacklisted_hosts_;
};
-class TestPreviewsOptOutStore : public PreviewsOptOutStore {
+class TestPreviewsBlackList : public PreviewsBlackList {
public:
- TestPreviewsOptOutStore() : clear_blacklist_count_(0) {}
- ~TestPreviewsOptOutStore() override {}
-
- int clear_blacklist_count() { return clear_blacklist_count_; }
-
- // Set |host_indifferent_black_list_item_| to test behavior of
- // PreviewsBlackList on certain PreviewsOptOutStore states.
- void SetHostIndifferentBlacklistItem(
- std::unique_ptr<PreviewsBlackListItem> item) {
- host_indifferent_black_list_item_ = std::move(item);
+ TestPreviewsBlackList(
+ std::unique_ptr<blacklist::OptOutStore> opt_out_store,
+ base::Clock* clock,
+ blacklist::OptOutBlacklistDelegate* blacklist_delegate,
+ blacklist::BlacklistData::AllowedTypesAndVersions allowed_types)
+ : PreviewsBlackList(std::move(opt_out_store),
+ clock,
+ blacklist_delegate,
+ allowed_types) {}
+ ~TestPreviewsBlackList() override {}
+
+ bool ShouldUseSessionPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const override {
+ return PreviewsBlackList::ShouldUseSessionPolicy(duration, history,
+ threshold);
}
-
- // Set |black_list_item_map_| to test behavior of
- // PreviewsBlackList on certain PreviewsOptOutStore states.
- void SetBlacklistItemMap(std::unique_ptr<BlackListItemMap> item_map) {
- black_list_item_map_ = std::move(item_map);
+ bool ShouldUsePersistentPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const override {
+ return PreviewsBlackList::ShouldUsePersistentPolicy(duration, history,
+ threshold);
}
-
- private:
- // PreviewsOptOutStore implementation:
- void AddPreviewNavigation(bool opt_out,
- const std::string& host_name,
- PreviewsType type,
- base::Time now) override {}
-
- void LoadBlackList(LoadBlackListCallback callback) override {
- if (!black_list_item_map_) {
- black_list_item_map_ = std::make_unique<BlackListItemMap>();
- }
- if (!host_indifferent_black_list_item_) {
- host_indifferent_black_list_item_ =
- PreviewsBlackList::CreateHostIndifferentBlackListItem();
- }
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&RunLoadCallback, callback,
- std::move(black_list_item_map_),
- std::move(host_indifferent_black_list_item_)));
+ bool ShouldUseHostPolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold,
+ size_t* max_hosts) const override {
+ return PreviewsBlackList::ShouldUseHostPolicy(duration, history, threshold,
+ max_hosts);
}
-
- void ClearBlackList(base::Time begin_time, base::Time end_time) override {
- ++clear_blacklist_count_;
+ bool ShouldUseTypePolicy(base::TimeDelta* duration,
+ size_t* history,
+ int* threshold) const override {
+ return PreviewsBlackList::ShouldUseTypePolicy(duration, history, threshold);
}
-
- int clear_blacklist_count_;
- std::unique_ptr<BlackListItemMap> black_list_item_map_;
- std::unique_ptr<PreviewsBlackListItem> host_indifferent_black_list_item_;
};
class PreviewsBlackListTest : public testing::Test {
@@ -150,7 +96,7 @@ class PreviewsBlackListTest : public testing::Test {
void TearDown() override { variations::testing::ClearAllVariationParams(); }
- void StartTest(bool null_opt_out) {
+ void StartTest() {
if (params_.size() > 0) {
ASSERT_TRUE(variations::AssociateVariationParams("ClientSidePreviews",
"Enabled", params_));
@@ -158,12 +104,11 @@ class PreviewsBlackListTest : public testing::Test {
"Enabled"));
params_.clear();
}
- std::unique_ptr<TestPreviewsOptOutStore> opt_out_store =
- null_opt_out ? nullptr : std::make_unique<TestPreviewsOptOutStore>();
- opt_out_store_ = opt_out_store.get();
- black_list_ = std::make_unique<PreviewsBlackList>(
- std::move(opt_out_store), &test_clock_, &blacklist_delegate_);
- start_ = test_clock_.Now();
+
+ blacklist::BlacklistData::AllowedTypesAndVersions allowed_types;
+ allowed_types[static_cast<int>(PreviewsType::OFFLINE)] = 0;
+ black_list_ = std::make_unique<TestPreviewsBlackList>(
+ nullptr, &test_clock_, &blacklist_delegate_, std::move(allowed_types));
passed_reasons_ = {};
}
@@ -193,6 +138,11 @@ class PreviewsBlackListTest : public testing::Test {
base::IntToString(duration_in_days);
}
+ void SetHostIndifferentDurationParam(int duration_in_days) {
+ params_["host_indifferent_black_list_duration_in_days"] =
+ base::IntToString(duration_in_days);
+ }
+
void SetSingleOptOutDurationParam(int single_opt_out_duration) {
params_["single_opt_out_duration_in_seconds"] =
base::IntToString(single_opt_out_duration);
@@ -203,499 +153,28 @@ class PreviewsBlackListTest : public testing::Test {
base::IntToString(max_hosts_in_blacklist);
}
- // Adds an opt out and either clears the black list for a time either longer
- // or shorter than the single opt out duration parameter depending on
- // |short_time|.
- void RunClearingBlackListTest(const GURL& url, bool short_time) {
- const size_t host_indifferent_history = 1;
- const int single_opt_out_duration = 5;
- SetHostDurationParam(365);
- SetHostIndifferentHistoryParam(host_indifferent_history);
- SetHostIndifferentThresholdParam(host_indifferent_history + 1);
- SetSingleOptOutDurationParam(single_opt_out_duration);
-
- StartTest(false /* null_opt_out */);
- if (!short_time)
- test_clock_.Advance(
- base::TimeDelta::FromSeconds(single_opt_out_duration));
-
- black_list_->AddPreviewNavigation(url, true /* opt_out */,
- PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->ClearBlackList(start_, test_clock_.Now());
- base::RunLoop().RunUntilIdle();
- }
-
protected:
base::MessageLoop loop_;
// Observer to |black_list_|.
- TestPreviewsBlacklistDelegate blacklist_delegate_;
+ TestOptOutBlacklistDelegate blacklist_delegate_;
base::SimpleTestClock test_clock_;
- TestPreviewsOptOutStore* opt_out_store_;
- base::Time start_;
std::map<std::string, std::string> params_;
base::FieldTrialList field_trial_list_;
- std::unique_ptr<PreviewsBlackList> black_list_;
+ std::unique_ptr<TestPreviewsBlackList> black_list_;
std::vector<PreviewsEligibilityReason> passed_reasons_;
private:
DISALLOW_COPY_AND_ASSIGN(PreviewsBlackListTest);
};
-TEST_F(PreviewsBlackListTest, PerHostBlackListNoStore) {
- // Tests the black list behavior when a null OptOutStore is passed in.
- const GURL url_a("http://www.url_a.com");
- const GURL url_b("http://www.url_b.com");
-
- // Host indifferent blacklisting should have no effect with the following
- // params.
- const size_t host_indifferent_history = 1;
- SetHostHistoryParam(4);
- SetHostIndifferentHistoryParam(host_indifferent_history);
- SetHostThresholdParam(2);
- SetHostIndifferentThresholdParam(host_indifferent_history + 1);
- SetHostDurationParam(365);
- // Disable single opt out by setting duration to 0.
- SetSingleOptOutDurationParam(0);
-
- StartTest(true /* null_opt_out */);
-
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_a, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- black_list_->AddPreviewNavigation(url_a, true, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_a, true, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url_a, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- black_list_->AddPreviewNavigation(url_b, true, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_b, true, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url_a, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- black_list_->AddPreviewNavigation(url_b, false, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_b, false, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_b, false, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url_a, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- black_list_->ClearBlackList(start_, test_clock_.Now());
-
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_a, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
-}
-
-TEST_F(PreviewsBlackListTest, PerHostBlackListWithStore) {
- // Tests the black list behavior when a non-null OptOutStore is passed in.
- const GURL url_a1("http://www.url_a.com/a1");
- const GURL url_a2("http://www.url_a.com/a2");
- const GURL url_b("http://www.url_b.com");
-
- // Host indifferent blacklisting should have no effect with the following
- // params.
- const size_t host_indifferent_history = 1;
- SetHostHistoryParam(4);
- SetHostIndifferentHistoryParam(host_indifferent_history);
- SetHostThresholdParam(2);
- SetHostIndifferentThresholdParam(host_indifferent_history + 1);
- SetHostDurationParam(365);
- // Disable single opt out by setting duration to 0.
- SetSingleOptOutDurationParam(0);
-
- StartTest(false /* null_opt_out */);
-
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
- EXPECT_EQ(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
- black_list_->IsLoadedAndAllowed(url_a1, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
- black_list_->IsLoadedAndAllowed(url_a2, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- base::RunLoop().RunUntilIdle();
-
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_a1, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_a2, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- black_list_->AddPreviewNavigation(url_a1, true, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_a1, true, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url_a1, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url_a2, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- black_list_->AddPreviewNavigation(url_b, true, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_b, true, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url_a1, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url_a2, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- black_list_->AddPreviewNavigation(url_b, false, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_b, false, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_b, false, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url_a1, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url_a2, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- EXPECT_EQ(0, opt_out_store_->clear_blacklist_count());
- black_list_->ClearBlackList(start_, base::Time::Now());
- EXPECT_EQ(1, opt_out_store_->clear_blacklist_count());
-
- EXPECT_EQ(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
- black_list_->IsLoadedAndAllowed(url_a1, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
- black_list_->IsLoadedAndAllowed(url_a2, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- base::RunLoop().RunUntilIdle();
- EXPECT_EQ(1, opt_out_store_->clear_blacklist_count());
-
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_a1, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_a1, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
-}
-
-TEST_F(PreviewsBlackListTest, HostIndifferentBlackList) {
- // Tests the black list behavior when a null OptOutStore is passed in.
- const GURL urls[] = {
- GURL("http://www.url_0.com"), GURL("http://www.url_1.com"),
- GURL("http://www.url_2.com"), GURL("http://www.url_3.com"),
- };
-
- // Per host blacklisting should have no effect with the following params.
- const size_t per_host_history = 1;
- const size_t host_indifferent_history = 4;
- const size_t host_indifferent_threshold = host_indifferent_history;
- SetHostHistoryParam(per_host_history);
- SetHostIndifferentHistoryParam(host_indifferent_history);
- SetHostThresholdParam(per_host_history + 1);
- SetHostIndifferentThresholdParam(host_indifferent_threshold);
- SetHostDurationParam(365);
- // Disable single opt out by setting duration to 0.
- SetSingleOptOutDurationParam(0);
-
- StartTest(true /* null_opt_out */);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(urls[0], PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(urls[1], PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(urls[2], PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(urls[3], PreviewsType::OFFLINE,
- &passed_reasons_));
-
- for (size_t i = 0; i < host_indifferent_threshold; i++) {
- black_list_->AddPreviewNavigation(urls[i], true, PreviewsType::OFFLINE);
- EXPECT_EQ(i != 3 ? PreviewsEligibilityReason::ALLOWED
- : PreviewsEligibilityReason::USER_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(urls[0], PreviewsType::OFFLINE,
- &passed_reasons_));
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- }
-
- EXPECT_EQ(PreviewsEligibilityReason::USER_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(urls[0], PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::USER_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(urls[1], PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::USER_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(urls[2], PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::USER_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(urls[3], PreviewsType::OFFLINE,
- &passed_reasons_));
-
- black_list_->AddPreviewNavigation(urls[3], false, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
- // New non-opt-out entry will cause these to be allowed now.
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(urls[0], PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(urls[1], PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(urls[2], PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(urls[3], PreviewsType::OFFLINE,
- &passed_reasons_));
-}
-
-TEST_F(PreviewsBlackListTest, QueueBehavior) {
- // Tests the black list asynchronous queue behavior. Methods called while
- // loading the opt-out store are queued and should run in the order they were
- // queued.
- const GURL url("http://www.url.com");
- const GURL url2("http://www.url2.com");
-
- // Host indifferent blacklisting should have no effect with the following
- // params.
- const size_t host_indifferent_history = 1;
- SetHostIndifferentHistoryParam(host_indifferent_history);
- SetHostIndifferentThresholdParam(host_indifferent_history + 1);
- SetHostDurationParam(365);
- // Disable single opt out by setting duration to 0.
- SetSingleOptOutDurationParam(0);
-
- std::vector<bool> test_opt_out{true, false};
-
- for (auto opt_out : test_opt_out) {
- StartTest(false /* null_opt_out */);
-
- EXPECT_EQ(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
- black_list_->IsLoadedAndAllowed(url, PreviewsType::OFFLINE,
- &passed_reasons_));
- black_list_->AddPreviewNavigation(url, opt_out, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url, opt_out, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- EXPECT_EQ(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
- black_list_->IsLoadedAndAllowed(url, PreviewsType::OFFLINE,
- &passed_reasons_));
- base::RunLoop().RunUntilIdle();
- EXPECT_EQ(opt_out ? PreviewsEligibilityReason::HOST_BLACKLISTED
- : PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url, PreviewsType::OFFLINE,
- &passed_reasons_));
- black_list_->AddPreviewNavigation(url, opt_out, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url, opt_out, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- EXPECT_EQ(0, opt_out_store_->clear_blacklist_count());
- black_list_->ClearBlackList(
- start_, test_clock_.Now() + base::TimeDelta::FromSeconds(1));
- EXPECT_EQ(1, opt_out_store_->clear_blacklist_count());
- black_list_->AddPreviewNavigation(url2, opt_out, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url2, opt_out, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- base::RunLoop().RunUntilIdle();
- EXPECT_EQ(1, opt_out_store_->clear_blacklist_count());
-
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(opt_out ? PreviewsEligibilityReason::HOST_BLACKLISTED
- : PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url2, PreviewsType::OFFLINE,
- &passed_reasons_));
- }
-}
-
-TEST_F(PreviewsBlackListTest, MaxHosts) {
- // Test that the black list only stores n hosts, and it stores the correct n
- // hosts.
- const GURL url_a("http://www.url_a.com");
- const GURL url_b("http://www.url_b.com");
- const GURL url_c("http://www.url_c.com");
- const GURL url_d("http://www.url_d.com");
- const GURL url_e("http://www.url_e.com");
-
- // Host indifferent blacklisting should have no effect with the following
- // params.
- const size_t host_indifferent_history = 1;
- const size_t stored_history_length = 1;
- SetHostHistoryParam(stored_history_length);
- SetHostIndifferentHistoryParam(host_indifferent_history);
- SetHostIndifferentThresholdParam(host_indifferent_history + 1);
- SetMaxHostInBlackListParam(2);
- SetHostThresholdParam(stored_history_length);
- SetHostDurationParam(365);
- // Disable single opt out by setting duration to 0.
- SetSingleOptOutDurationParam(0);
-
- StartTest(true /* null_opt_out */);
-
- black_list_->AddPreviewNavigation(url_a, true, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_b, false, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_c, false, PreviewsType::OFFLINE);
- // url_a should stay in the map, since it has an opt out time.
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url_a, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_c, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_d, true, PreviewsType::OFFLINE);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_e, true, PreviewsType::OFFLINE);
- // url_d and url_e should remain in the map, but url_a should be evicted.
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_a, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url_d, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url_e, PreviewsType::OFFLINE,
- &passed_reasons_));
-}
-
-TEST_F(PreviewsBlackListTest, SingleOptOut) {
- // Test that when a user opts out of a preview, previews won't be shown until
- // |single_opt_out_duration| has elapsed.
- const GURL url_a("http://www.url_a.com");
- const GURL url_b("http://www.url_b.com");
- const GURL url_c("http://www.url_c.com");
-
- // Host indifferent blacklisting should have no effect with the following
- // params.
- const size_t host_indifferent_history = 1;
- const int single_opt_out_duration = 5;
- SetHostHistoryParam(1);
- SetHostIndifferentHistoryParam(2);
- SetHostDurationParam(365);
- SetMaxHostInBlackListParam(10);
- SetHostIndifferentHistoryParam(host_indifferent_history);
- SetHostIndifferentThresholdParam(host_indifferent_history + 1);
- SetSingleOptOutDurationParam(single_opt_out_duration);
-
- StartTest(true /* null_opt_out */);
-
- black_list_->AddPreviewNavigation(url_a, false, PreviewsType::OFFLINE);
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_a, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_c, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- test_clock_.Advance(
- base::TimeDelta::FromSeconds(single_opt_out_duration + 1));
-
- black_list_->AddPreviewNavigation(url_b, true, PreviewsType::OFFLINE);
- EXPECT_EQ(PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT,
- black_list_->IsLoadedAndAllowed(url_c, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- test_clock_.Advance(
- base::TimeDelta::FromSeconds(single_opt_out_duration - 1));
-
- EXPECT_EQ(PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT,
- black_list_->IsLoadedAndAllowed(url_c, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- test_clock_.Advance(
- base::TimeDelta::FromSeconds(single_opt_out_duration + 1));
-
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_b, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_c, PreviewsType::OFFLINE,
- &passed_reasons_));
-}
-
TEST_F(PreviewsBlackListTest, AddPreviewUMA) {
base::HistogramTester histogram_tester;
const GURL url("http://www.url.com");
- StartTest(false /* null_opt_out */);
+ StartTest();
black_list_->AddPreviewNavigation(url, false, PreviewsType::OFFLINE);
histogram_tester.ExpectUniqueSample("Previews.OptOut.UserOptedOut.Offline", 0,
@@ -705,356 +184,72 @@ TEST_F(PreviewsBlackListTest, AddPreviewUMA) {
1);
}
-TEST_F(PreviewsBlackListTest, ClearShortTime) {
- // Tests that clearing the black list for a short amount of time (relative to
- // "SetSingleOptOutDurationParam") does not reset the blacklist's recent
- // opt out rule.
-
- const GURL url("http://www.url.com");
- RunClearingBlackListTest(url, true /* short_time */);
- EXPECT_EQ(PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT,
- black_list_->IsLoadedAndAllowed(url, PreviewsType::OFFLINE,
- &passed_reasons_));
-}
-
-TEST_F(PreviewsBlackListTest, ClearingBlackListClearsRecentNavigation) {
- // Tests that clearing the black list for a long amount of time (relative to
- // "single_opt_out_duration_in_seconds") resets the blacklist's recent opt out
- // rule.
-
- const GURL url("http://www.url.com");
- RunClearingBlackListTest(url, false /* short_time */);
-
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url, PreviewsType::OFFLINE,
- &passed_reasons_));
-}
-
-TEST_F(PreviewsBlackListTest, ObserverIsNotifiedOnHostBlacklisted) {
- // Tests the black list behavior when a null OptOutStore is passed in.
- const GURL url_("http://www.url_.com");
-
- // Host indifferent blacklisting should have no effect with the following
- // params.
- const size_t host_indifferent_history = 1;
- SetHostHistoryParam(4);
- SetHostThresholdParam(2);
- SetHostIndifferentHistoryParam(host_indifferent_history);
- SetHostIndifferentThresholdParam(host_indifferent_history + 1);
- SetHostDurationParam(365);
- // Disable single opt out by setting duration to 0.
- SetSingleOptOutDurationParam(0);
-
- StartTest(true /* null_opt_out */);
-
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url_, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- // Observer is not notified as blacklisted when the threshold does not met.
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_, true, PreviewsType::OFFLINE);
- base::RunLoop().RunUntilIdle();
- EXPECT_THAT(blacklist_delegate_.blacklisted_hosts(), ::testing::SizeIs(0));
-
- // Observer is notified as blacklisted when the threshold is met.
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_, true, PreviewsType::OFFLINE);
- base::RunLoop().RunUntilIdle();
- const base::Time blacklisted_time = test_clock_.Now();
- EXPECT_THAT(blacklist_delegate_.blacklisted_hosts(), ::testing::SizeIs(1));
- EXPECT_EQ(blacklisted_time,
- blacklist_delegate_.blacklisted_hosts().find(url_.host())->second);
-
- // Observer is not notified when the host is already blacklisted.
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(url_, true, PreviewsType::OFFLINE);
- base::RunLoop().RunUntilIdle();
- EXPECT_THAT(blacklist_delegate_.blacklisted_hosts(), ::testing::SizeIs(1));
- EXPECT_EQ(blacklisted_time,
- blacklist_delegate_.blacklisted_hosts().find(url_.host())->second);
-
- // Observer is notified when blacklist is cleared.
- EXPECT_FALSE(blacklist_delegate_.blacklist_cleared());
-
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->ClearBlackList(start_, test_clock_.Now());
- base::RunLoop().RunUntilIdle();
-
- EXPECT_TRUE(blacklist_delegate_.blacklist_cleared());
- EXPECT_EQ(test_clock_.Now(), blacklist_delegate_.blacklist_cleared_time());
-}
-
-TEST_F(PreviewsBlackListTest, ObserverIsNotifiedOnUserBlacklisted) {
- // Tests the black list behavior when a null OptOutStore is passed in.
- const GURL urls[] = {
- GURL("http://www.url_0.com"), GURL("http://www.url_1.com"),
- GURL("http://www.url_2.com"), GURL("http://www.url_3.com"),
- };
-
- // Per host blacklisting should have no effect with the following params.
- const size_t per_host_history = 1;
- const size_t host_indifferent_history = 4;
- const size_t host_indifferent_threshold = host_indifferent_history;
- SetHostHistoryParam(per_host_history);
- SetHostIndifferentHistoryParam(host_indifferent_history);
- SetHostThresholdParam(per_host_history + 1);
- SetHostIndifferentThresholdParam(host_indifferent_threshold);
- SetHostDurationParam(365);
- // Disable single opt out by setting duration to 0.
- SetSingleOptOutDurationParam(0);
-
- StartTest(true /* null_opt_out */);
-
- // Initially no host is blacklisted, and user is not blacklisted.
- EXPECT_THAT(blacklist_delegate_.blacklisted_hosts(), ::testing::SizeIs(0));
- EXPECT_FALSE(blacklist_delegate_.user_blacklisted());
-
- for (size_t i = 0; i < host_indifferent_threshold; ++i) {
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(urls[i], true, PreviewsType::OFFLINE);
- base::RunLoop().RunUntilIdle();
-
- EXPECT_THAT(blacklist_delegate_.blacklisted_hosts(), ::testing::SizeIs(0));
- // Observer is notified when number of recently opt out meets
- // |host_indifferent_threshold|.
- EXPECT_EQ(i >= host_indifferent_threshold - 1,
- blacklist_delegate_.user_blacklisted());
- }
-
- // Observer is notified when the user is no longer blacklisted.
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
- black_list_->AddPreviewNavigation(urls[3], false, PreviewsType::OFFLINE);
- base::RunLoop().RunUntilIdle();
-
- EXPECT_FALSE(blacklist_delegate_.user_blacklisted());
-}
-
-TEST_F(PreviewsBlackListTest, ObserverIsNotifiedWhenLoadBlacklistDone) {
- const GURL url_a1("http://www.url_a.com/a1");
- const GURL url_a2("http://www.url_a.com/a2");
-
- // Per host blacklisting should have no effect with the following params.
- const size_t per_host_history = 1;
- const size_t host_indifferent_history = 4;
- const size_t host_indifferent_threshold = host_indifferent_history;
- SetHostHistoryParam(per_host_history);
- SetHostIndifferentHistoryParam(host_indifferent_history);
- SetHostThresholdParam(per_host_history + 1);
- SetHostIndifferentThresholdParam(host_indifferent_threshold);
- SetHostDurationParam(365);
- // Disable single opt out by setting duration to 0.
- SetSingleOptOutDurationParam(0);
-
- StartTest(false /* null_opt_out */);
-
- std::unique_ptr<PreviewsBlackListItem> host_indifferent_item =
- PreviewsBlackList::CreateHostIndifferentBlackListItem();
- base::SimpleTestClock test_clock;
-
- for (size_t i = 0; i < host_indifferent_threshold; ++i) {
- test_clock.Advance(base::TimeDelta::FromSeconds(1));
- host_indifferent_item->AddPreviewNavigation(true, test_clock.Now());
- }
-
- std::unique_ptr<TestPreviewsOptOutStore> opt_out_store =
- std::make_unique<TestPreviewsOptOutStore>();
- opt_out_store->SetHostIndifferentBlacklistItem(
- std::move(host_indifferent_item));
-
- EXPECT_FALSE(blacklist_delegate_.user_blacklisted());
- auto black_list = std::make_unique<PreviewsBlackList>(
- std::move(opt_out_store), &test_clock, &blacklist_delegate_);
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(blacklist_delegate_.user_blacklisted());
-}
-
-TEST_F(PreviewsBlackListTest, ObserverIsNotifiedOfHistoricalBlacklistedHosts) {
- // Tests the black list behavior when a non-null OptOutStore is passed in.
- const GURL url_a("http://www.url_a.com");
- const GURL url_b("http://www.url_b.com");
-
- // Host indifferent blacklisting should have no effect with the following
- // params.
- const size_t host_indifferent_history = 1;
- SetHostThresholdParam(2);
- SetHostHistoryParam(4);
- SetHostIndifferentHistoryParam(host_indifferent_history);
- SetHostIndifferentThresholdParam(host_indifferent_history + 1);
- SetHostDurationParam(365);
- // Disable single opt out by setting duration to 0.
- SetSingleOptOutDurationParam(0);
-
- StartTest(false /* null_opt_out */);
-
- base::SimpleTestClock test_clock;
+TEST_F(PreviewsBlackListTest, SessionParams) {
+ int duration_seconds = 5;
+ SetSingleOptOutDurationParam(duration_seconds);
- PreviewsBlackListItem* item_a = new PreviewsBlackListItem(
- params::MaxStoredHistoryLengthForPerHostBlackList(),
- params::PerHostBlackListOptOutThreshold(),
- params::PerHostBlackListDuration());
- PreviewsBlackListItem* item_b = new PreviewsBlackListItem(
- params::MaxStoredHistoryLengthForPerHostBlackList(),
- params::PerHostBlackListOptOutThreshold(),
- params::PerHostBlackListDuration());
+ StartTest();
- // Host |url_a| is blacklisted.
- test_clock.Advance(base::TimeDelta::FromSeconds(1));
- item_a->AddPreviewNavigation(true, test_clock.Now());
- test_clock.Advance(base::TimeDelta::FromSeconds(1));
- item_a->AddPreviewNavigation(true, test_clock.Now());
- base::Time blacklisted_time = test_clock.Now();
+ base::TimeDelta duration;
+ size_t history = 0;
+ int threshold = 0;
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(item_a->IsBlackListed(test_clock.Now()));
-
- // Host |url_b| is not blacklisted.
- test_clock.Advance(base::TimeDelta::FromSeconds(1));
- item_b->AddPreviewNavigation(true, test_clock.Now());
-
- std::unique_ptr<BlackListItemMap> item_map =
- std::make_unique<BlackListItemMap>();
- item_map->emplace(url_a.host(), base::WrapUnique(item_a));
- item_map->emplace(url_b.host(), base::WrapUnique(item_b));
-
- std::unique_ptr<TestPreviewsOptOutStore> opt_out_store =
- std::make_unique<TestPreviewsOptOutStore>();
- opt_out_store->SetBlacklistItemMap(std::move(item_map));
-
- auto black_list = std::make_unique<PreviewsBlackList>(
- std::move(opt_out_store), &test_clock, &blacklist_delegate_);
- base::RunLoop().RunUntilIdle();
-
- ASSERT_THAT(blacklist_delegate_.blacklisted_hosts(), ::testing::SizeIs(1));
- EXPECT_EQ(blacklisted_time,
- blacklist_delegate_.blacklisted_hosts().find(url_a.host())->second);
+ EXPECT_TRUE(
+ black_list_->ShouldUseSessionPolicy(&duration, &history, &threshold));
+ EXPECT_EQ(base::TimeDelta::FromSeconds(duration_seconds), duration);
+ EXPECT_EQ(1u, history);
+ EXPECT_EQ(1, threshold);
}
-TEST_F(PreviewsBlackListTest, PassedReasonsWhenBlacklistDataNotLoaded) {
- // Test that IsLoadedAndAllow, push checked PreviewsEligibilityReasons to the
- // |passed_reasons| vector.
- const GURL url("http://www.url_.com/");
- StartTest(false /* null_opt_out */);
-
- EXPECT_EQ(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
- black_list_->IsLoadedAndAllowed(url, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- EXPECT_EQ(0UL, passed_reasons_.size());
+TEST_F(PreviewsBlackListTest, PersistentParams) {
+ int duration_days = 5;
+ size_t expected_history = 6;
+ int expected_threshold = 4;
+ SetHostIndifferentThresholdParam(expected_threshold);
+ SetHostIndifferentHistoryParam(expected_history);
+ SetHostIndifferentDurationParam(duration_days);
+
+ StartTest();
+
+ base::TimeDelta duration;
+ size_t history = 0;
+ int threshold = 0;
+
+ EXPECT_TRUE(
+ black_list_->ShouldUsePersistentPolicy(&duration, &history, &threshold));
+ EXPECT_EQ(base::TimeDelta::FromDays(duration_days), duration);
+ EXPECT_EQ(expected_history, history);
+ EXPECT_EQ(expected_threshold, threshold);
}
-TEST_F(PreviewsBlackListTest, PassedReasonsWhenUserRecentlyOptedOut) {
- // Test that IsLoadedAndAllow, push checked PreviewsEligibilityReasons to the
- // |passed_reasons| vector.
- const GURL url("http://www.url_.com/");
- StartTest(true /* null_opt_out */);
-
- black_list_->AddPreviewNavigation(url, true, PreviewsType::OFFLINE);
- EXPECT_EQ(PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT,
- black_list_->IsLoadedAndAllowed(url, PreviewsType::OFFLINE,
- &passed_reasons_));
- EXPECT_EQ(1UL, passed_reasons_.size());
- EXPECT_EQ(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
- passed_reasons_[0]);
+TEST_F(PreviewsBlackListTest, HostParams) {
+ int duration_days = 5;
+ size_t expected_history = 6;
+ int expected_threshold = 4;
+ size_t expected_max_hosts = 11;
+ SetHostThresholdParam(expected_threshold);
+ SetHostHistoryParam(expected_history);
+ SetHostDurationParam(duration_days);
+ SetMaxHostInBlackListParam(expected_max_hosts);
+
+ StartTest();
+
+ base::TimeDelta duration;
+ size_t history = 0;
+ int threshold = 0;
+ size_t max_hosts = 0;
+
+ EXPECT_TRUE(black_list_->ShouldUseHostPolicy(&duration, &history, &threshold,
+ &max_hosts));
+ EXPECT_EQ(base::TimeDelta::FromDays(duration_days), duration);
+ EXPECT_EQ(expected_history, history);
+ EXPECT_EQ(expected_threshold, threshold);
+ EXPECT_EQ(expected_max_hosts, max_hosts);
}
-TEST_F(PreviewsBlackListTest, PassedReasonsWhenUserBlacklisted) {
- // Test that IsLoadedAndAllow, push checked PreviewsEligibilityReasons to the
- // |passed_reasons| vector.
- const GURL urls[] = {
- GURL("http://www.url_0.com"), GURL("http://www.url_1.com"),
- GURL("http://www.url_2.com"), GURL("http://www.url_3.com"),
- };
-
- // Per host blacklisting should have no effect with the following params.
- const size_t per_host_history = 1;
- const size_t host_indifferent_history = 4;
- const size_t host_indifferent_threshold = host_indifferent_history;
- SetHostHistoryParam(per_host_history);
- SetHostIndifferentHistoryParam(host_indifferent_history);
- SetHostThresholdParam(per_host_history + 1);
- SetHostIndifferentThresholdParam(host_indifferent_threshold);
- SetHostDurationParam(365);
- // Disable single opt out by setting duration to 0.
- SetSingleOptOutDurationParam(0);
-
- StartTest(true /* null_opt_out */);
- test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
- for (auto url : urls) {
- black_list_->AddPreviewNavigation(url, true, PreviewsType::OFFLINE);
- }
-
- EXPECT_EQ(PreviewsEligibilityReason::USER_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(urls[0], PreviewsType::OFFLINE,
- &passed_reasons_));
-
- PreviewsEligibilityReason expected_reasons[] = {
- PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
- PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT,
- };
- EXPECT_EQ(2UL, passed_reasons_.size());
- for (size_t i = 0; i < passed_reasons_.size(); i++) {
- EXPECT_EQ(expected_reasons[i], passed_reasons_[i]);
- }
-}
-
-TEST_F(PreviewsBlackListTest, PassedReasonsWhenHostBlacklisted) {
- // Test that IsLoadedAndAllow, push checked PreviewsEligibilityReasons to the
- // |passed_reasons| vector.
- const GURL url("http://www.url_a.com");
-
- // Host indifferent blacklisting should have no effect with the following
- // params.
- const size_t host_indifferent_history = 1;
- SetHostHistoryParam(4);
- SetHostIndifferentHistoryParam(host_indifferent_history);
- SetHostThresholdParam(2);
- SetHostIndifferentThresholdParam(host_indifferent_history + 1);
- SetHostDurationParam(365);
- // Disable single opt out by setting duration to 0.
- SetSingleOptOutDurationParam(0);
-
- StartTest(true /* null_opt_out */);
-
- black_list_->AddPreviewNavigation(url, true, PreviewsType::OFFLINE);
- black_list_->AddPreviewNavigation(url, true, PreviewsType::OFFLINE);
-
- EXPECT_EQ(PreviewsEligibilityReason::HOST_BLACKLISTED,
- black_list_->IsLoadedAndAllowed(url, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- PreviewsEligibilityReason expected_reasons[] = {
- PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
- PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT,
- PreviewsEligibilityReason::USER_BLACKLISTED,
- };
- EXPECT_EQ(3UL, passed_reasons_.size());
- for (size_t i = 0; i < passed_reasons_.size(); i++) {
- EXPECT_EQ(expected_reasons[i], passed_reasons_[i]);
- }
-}
-
-TEST_F(PreviewsBlackListTest, PassedReasonsWhenAllowed) {
- // Test that IsLoadedAndAllow, push checked PreviewsEligibilityReasons to the
- // |passed_reasons| vector.
- const GURL url("http://www.url.com");
- StartTest(true /* null_opt_out */);
-
- EXPECT_EQ(PreviewsEligibilityReason::ALLOWED,
- black_list_->IsLoadedAndAllowed(url, PreviewsType::OFFLINE,
- &passed_reasons_));
-
- PreviewsEligibilityReason expected_reasons[] = {
- PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED,
- PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT,
- PreviewsEligibilityReason::USER_BLACKLISTED,
- PreviewsEligibilityReason::HOST_BLACKLISTED,
- };
- EXPECT_EQ(4UL, passed_reasons_.size());
- for (size_t i = 0; i < passed_reasons_.size(); i++) {
- EXPECT_EQ(expected_reasons[i], passed_reasons_[i]);
- }
+TEST_F(PreviewsBlackListTest, TypeParams) {
+ StartTest();
+ EXPECT_FALSE(black_list_->ShouldUseTypePolicy(nullptr, nullptr, nullptr));
}
} // namespace
diff --git a/chromium/components/previews/core/previews_decider.h b/chromium/components/previews/core/previews_decider.h
index 96e706a0e44..6f8c0f7987c 100644
--- a/chromium/components/previews/core/previews_decider.h
+++ b/chromium/components/previews/core/previews_decider.h
@@ -30,7 +30,8 @@ class PreviewsDecider {
const net::URLRequest& request,
PreviewsType type,
net::EffectiveConnectionType effective_connection_type_threshold,
- const std::vector<std::string>& host_blacklist_from_server) const = 0;
+ const std::vector<std::string>& host_blacklist_from_server,
+ bool ignore_long_term_black_list_rules) const = 0;
// Same as ShouldAllowPreviewAtECT, but uses the previews default
// EffectiveConnectionType and no blacklisted hosts from the server.
diff --git a/chromium/components/previews/core/previews_experiments.cc b/chromium/components/previews/core/previews_experiments.cc
index 93f092afb52..07f2dc4b8e1 100644
--- a/chromium/components/previews/core/previews_experiments.cc
+++ b/chromium/components/previews/core/previews_experiments.cc
@@ -144,11 +144,13 @@ net::EffectiveConnectionType GetECTThresholdForPreview(
case PreviewsType::LITE_PAGE:
NOTREACHED();
break;
- case PreviewsType::AMP_REDIRECTION:
- return net::EFFECTIVE_CONNECTION_TYPE_LAST; // Trigger irrespective of
- // ECT.
case PreviewsType::NONE:
case PreviewsType::UNSPECIFIED:
+ case PreviewsType::RESOURCE_LOADING_HINTS:
+ return GetParamValueAsECTByFeature(features::kResourceLoadingHints,
+ kEffectiveConnectionTypeThreshold,
+ net::EFFECTIVE_CONNECTION_TYPE_2G);
+ case PreviewsType::DEPRECATED_AMP_REDIRECTION:
case PreviewsType::LAST:
break;
}
@@ -168,14 +170,14 @@ bool IsClientLoFiEnabled() {
return base::FeatureList::IsEnabled(features::kClientLoFi);
}
-bool IsAMPRedirectionPreviewEnabled() {
- return base::FeatureList::IsEnabled(features::kAMPRedirection);
-}
-
bool IsNoScriptPreviewsEnabled() {
return base::FeatureList::IsEnabled(features::kNoScriptPreviews);
}
+bool IsResourceLoadingHintsEnabled() {
+ return base::FeatureList::IsEnabled(features::kResourceLoadingHints);
+}
+
int OfflinePreviewsVersion() {
return GetParamValueAsInt(kClientSidePreviewsFieldTrial, kVersion, 0);
}
@@ -185,16 +187,16 @@ int ClientLoFiVersion() {
0);
}
-int AMPRedirectionPreviewsVersion() {
- return GetFieldTrialParamByFeatureAsInt(features::kAMPRedirection, kVersion,
- 0);
-}
-
int NoScriptPreviewsVersion() {
return GetFieldTrialParamByFeatureAsInt(features::kNoScriptPreviews, kVersion,
0);
}
+int ResourceLoadingHintsVersion() {
+ return GetFieldTrialParamByFeatureAsInt(features::kResourceLoadingHints,
+ kVersion, 0);
+}
+
bool IsOptimizationHintsEnabled() {
return base::FeatureList::IsEnabled(features::kOptimizationHints);
}
@@ -239,12 +241,13 @@ std::string GetStringNameForType(PreviewsType type) {
return "LoFi";
case PreviewsType::LITE_PAGE:
return "LitePage";
- case PreviewsType::AMP_REDIRECTION:
- return "AMPRedirection";
case PreviewsType::NOSCRIPT:
return "NoScript";
case PreviewsType::UNSPECIFIED:
return "Unspecified";
+ case PreviewsType::RESOURCE_LOADING_HINTS:
+ return "ResourceLoadingHints";
+ case PreviewsType::DEPRECATED_AMP_REDIRECTION:
case PreviewsType::LAST:
break;
}
diff --git a/chromium/components/previews/core/previews_experiments.h b/chromium/components/previews/core/previews_experiments.h
index 27726d52209..b63c6c62509 100644
--- a/chromium/components/previews/core/previews_experiments.h
+++ b/chromium/components/previews/core/previews_experiments.h
@@ -27,8 +27,9 @@ enum class PreviewsType {
// The user is shown a server lite page.
LITE_PAGE = 3,
- // AMP version of the page is shown as a preview.
- AMP_REDIRECTION = 4,
+ // AMP version of the page is shown as a preview. Deprecated, and should not
+ // be used.
+ DEPRECATED_AMP_REDIRECTION = 4,
// Preview that disables JavaScript for the navigation.
NOSCRIPT = 5,
@@ -37,9 +38,12 @@ enum class PreviewsType {
// might be used for checks or logging that applies to any type.
UNSPECIFIED = 6,
+ // Request that resource loading hints be used during pageload.
+ RESOURCE_LOADING_HINTS = 7,
+
// Insert new enum values here. Keep values sequential to allow looping from
// NONE+1 to LAST-1. Also add the enum to Previews.Types histogram suffix.
- LAST = 7,
+ LAST = 8,
};
typedef std::vector<std::pair<PreviewsType, int>> PreviewsTypeList;
@@ -92,14 +96,14 @@ bool ArePreviewsAllowed();
// Whether the preview type is enabled.
bool IsOfflinePreviewsEnabled();
bool IsClientLoFiEnabled();
-bool IsAMPRedirectionPreviewEnabled();
bool IsNoScriptPreviewsEnabled();
+bool IsResourceLoadingHintsEnabled();
// The blacklist version for each preview type.
int OfflinePreviewsVersion();
int ClientLoFiVersion();
-int AMPRedirectionPreviewsVersion();
int NoScriptPreviewsVersion();
+int ResourceLoadingHintsVersion();
// Whether server optimization hints are enabled.
bool IsOptimizationHintsEnabled();
diff --git a/chromium/components/previews/core/previews_features.cc b/chromium/components/previews/core/previews_features.cc
index a248816c221..937ee624c5e 100644
--- a/chromium/components/previews/core/previews_features.cc
+++ b/chromium/components/previews/core/previews_features.cc
@@ -44,13 +44,26 @@ const base::Feature kNoScriptPreviews{"NoScriptPreviews",
const base::Feature kStalePreviewsTimestamp{"StalePreviewsTimestamp",
base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature kAMPRedirection{"AMPRedirectionPreviews",
- base::FEATURE_DISABLED_BY_DEFAULT};
-
// Enables the syncing of the Optimization Hints component, which provides
// hints for what Previews can be applied on a page load.
const base::Feature kOptimizationHints{"OptimizationHints",
base::FEATURE_DISABLED_BY_DEFAULT};
+// Enables Optimization Hints that are marked as experimental. Optimizations are
+// marked experimental by setting an experiment name in the "experiment_name"
+// field of the Optimization proto. This allows experiments at the granularity
+// of a single PreviewType for a single host (or host suffix). The intent is
+// that optimizations that may not work properly for certain sites can be tried
+// at a small scale via Finch experiments. Experimental optimizations can be
+// activated by enabling this feature and passing an experiment name as a
+// parameter called "experiment_name" that matches the experiment name in the
+// Optimization proto.
+const base::Feature kOptimizationHintsExperiments{
+ "OptimizationHintsExperiments", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Enables the application of the resource loading hints when loading resources.
+const base::Feature kResourceLoadingHints{"ResourceLoadingHints",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
} // namespace features
} // namespace previews
diff --git a/chromium/components/previews/core/previews_features.h b/chromium/components/previews/core/previews_features.h
index 61f61f1fb03..fd0a1a8d430 100644
--- a/chromium/components/previews/core/previews_features.h
+++ b/chromium/components/previews/core/previews_features.h
@@ -15,8 +15,10 @@ extern const base::Feature kOfflinePreviews;
extern const base::Feature kClientLoFi;
extern const base::Feature kNoScriptPreviews;
extern const base::Feature kStalePreviewsTimestamp;
-extern const base::Feature kAMPRedirection;
extern const base::Feature kOptimizationHints;
+extern const base::Feature kOptimizationHintsExperiments;
+constexpr char kOptimizationHintsExperimentNameParam[] = "experiment_name";
+extern const base::Feature kResourceLoadingHints;
} // namespace features
} // namespace previews
diff --git a/chromium/components/previews/core/previews_logger.h b/chromium/components/previews/core/previews_logger.h
index 002232d038f..e30fdb5a79d 100644
--- a/chromium/components/previews/core/previews_logger.h
+++ b/chromium/components/previews/core/previews_logger.h
@@ -71,7 +71,7 @@ class PreviewsLogger {
// Add MessageLog using the given information. Pop out the oldest log if the
// size of |log_messages_| grows larger than a threshold. Virtualized in
- // testing. |page_id| generated by PreviewsIOData, used for grouping log
+ // testing. |page_id| generated by PreviewsDeciderImpl, used for grouping log
// messages together, messages that don't need to be grouped can pass in 0 as
// page_id.
virtual void LogMessage(const std::string& event_type,
@@ -91,9 +91,9 @@ class PreviewsLogger {
// Add a MessageLog for the a decision that was made about the state of
// previews and blacklist. |passed_reasons| is an ordered list of
// PreviewsEligibilityReasons that got pass the decision. The method takes
- // ownership of |passed_reasons|. |page_id| generated by PreviewsIOData, used
- // for grouping log messages together, messages that don't need to be grouped
- // can pass in 0 as page_id. Virtualized in testing.
+ // ownership of |passed_reasons|. |page_id| generated by PreviewsDeciderImpl,
+ // used for grouping log messages together, messages that don't need to be
+ // grouped can pass in 0 as page_id. Virtualized in testing.
virtual void LogPreviewDecisionMade(
PreviewsEligibilityReason reason,
const GURL& url,
diff --git a/chromium/components/previews/core/previews_opt_out_store.h b/chromium/components/previews/core/previews_opt_out_store.h
deleted file mode 100644
index 7b352d59aca..00000000000
--- a/chromium/components/previews/core/previews_opt_out_store.h
+++ /dev/null
@@ -1,55 +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_PREVIEWS_CORE_PREVIEWS_OPT_OUT_STORE_H_
-#define COMPONENTS_PREVIEWS_CORE_PREVIEWS_OPT_OUT_STORE_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/time/time.h"
-#include "components/previews/core/previews_black_list_item.h"
-#include "components/previews/core/previews_experiments.h"
-
-namespace previews {
-
-typedef std::unordered_map<std::string, std::unique_ptr<PreviewsBlackListItem>>
- BlackListItemMap;
-
-typedef base::Callback<void(std::unique_ptr<BlackListItemMap>,
- std::unique_ptr<PreviewsBlackListItem>)>
- LoadBlackListCallback;
-
-// PreviewsOptOutStore keeps opt out information for the previews.
-// Ability to create multiple instances of the store as well as behavior of
-// asynchronous operations when the object is being destroyed, before such
-// operation finishes will depend on implementation. It is possible to issue
-// multiple asynchronous operations in parallel and maintain ordering.
-class PreviewsOptOutStore {
- public:
- virtual ~PreviewsOptOutStore() {}
-
- // Adds a new navigation to the store. |opt_out| is whether the user opted out
- // of the preview.
- virtual void AddPreviewNavigation(bool opt_out,
- const std::string& host_name,
- PreviewsType type,
- base::Time now) = 0;
-
- // Asynchronously loads a map of host names to PreviewsBlackListItem for that
- // host from the store. And runs |callback| once loading is finished.
- virtual void LoadBlackList(LoadBlackListCallback callback) = 0;
-
- // Deletes all history in the store between |begin_time| and |end_time|.
- virtual void ClearBlackList(base::Time begin_time, base::Time end_time) = 0;
-};
-
-} // namespace previews
-
-#endif // COMPONENTS_PREVIEWS_CORE_PREVIEWS_OPT_OUT_STORE_H_
diff --git a/chromium/components/previews/core/previews_opt_out_store_sql.cc b/chromium/components/previews/core/previews_opt_out_store_sql.cc
deleted file mode 100644
index 5cddd0b823f..00000000000
--- a/chromium/components/previews/core/previews_opt_out_store_sql.cc
+++ /dev/null
@@ -1,461 +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/previews/core/previews_opt_out_store_sql.h"
-
-#include <map>
-#include <string>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/command_line.h"
-#include "base/files/file_util.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/sequenced_task_runner.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/previews/core/previews_black_list.h"
-#include "components/previews/core/previews_black_list_item.h"
-#include "components/previews/core/previews_experiments.h"
-#include "sql/connection.h"
-#include "sql/recovery.h"
-#include "sql/statement.h"
-#include "sql/transaction.h"
-
-namespace previews {
-
-namespace {
-
-// Command line switch to change the previews per row DB size.
-const char kMaxRowsPerHost[] = "previews-max-opt-out-rows-per-host";
-
-// Command line switch to change the previews DB size.
-const char kMaxRows[] = "previews-max-opt-out-rows";
-
-// Returns the maximum number of table rows allowed per host for the previews
-// opt out store. This is enforced during insertion of new navigation entries.
-int MaxRowsPerHostInOptOutDB() {
- std::string max_rows =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- kMaxRowsPerHost);
- int value;
- return base::StringToInt(max_rows, &value) ? value : 32;
-}
-
-// Returns the maximum number of table rows allowed for the previews opt out
-// store. This is enforced during load time; thus the database can grow
-// larger than this temporarily.
-int MaxRowsInOptOutDB() {
- std::string max_rows =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kMaxRows);
- int value;
- return base::StringToInt(max_rows, &value) ? value : 3200;
-}
-
-// Table names use a macro instead of a const, so they can be used inline in
-// other SQL statements below.
-
-// The Previews OptOut table holds entries for hosts that should not use a
-// specified PreviewsType treatment. Also known as the previews blacklist.
-#define PREVIEWS_OPT_OUT_TABLE_NAME "previews_v1"
-
-// The Enabled Previews table hold the list of enabled PreviewsType
-// treatments with a version for that enabled treatment. If the version
-// changes or the type becomes disabled, then any entries in the OptOut
-// table for that treatment type should be cleared.
-#define ENABLED_PREVIEWS_TABLE_NAME "enabled_previews_v1"
-
-void CreateSchema(sql::Connection* db) {
- const char kSqlCreatePreviewsTable[] =
- "CREATE TABLE IF NOT EXISTS " PREVIEWS_OPT_OUT_TABLE_NAME
- " (host_name VARCHAR NOT NULL,"
- " time INTEGER NOT NULL,"
- " opt_out INTEGER NOT NULL,"
- " type INTEGER NOT NULL,"
- " PRIMARY KEY(host_name, time DESC, opt_out, type))";
- if (!db->Execute(kSqlCreatePreviewsTable))
- return;
-
- const char kSqlCreateEnabledTypeVersionTable[] =
- "CREATE TABLE IF NOT EXISTS " ENABLED_PREVIEWS_TABLE_NAME
- " (type INTEGER NOT NULL,"
- " version INTEGER NOT NULL,"
- " PRIMARY KEY(type))";
- if (!db->Execute(kSqlCreateEnabledTypeVersionTable))
- return;
-}
-
-void DatabaseErrorCallback(sql::Connection* db,
- const base::FilePath& db_path,
- int extended_error,
- sql::Statement* stmt) {
- if (sql::Recovery::ShouldRecover(extended_error)) {
- // Prevent reentrant calls.
- 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.
- sql::Recovery::RecoverDatabase(db, db_path);
-
- // The DLOG(WARNING) below is intended to draw immediate attention to errors
- // in newly-written code. Database corruption is generally a result of OS
- // or hardware issues, not coding errors at the client level, so displaying
- // the error would probably lead to confusion. The ignored call signals the
- // test-expectation framework that the error was handled.
- ignore_result(sql::Connection::IsExpectedSqliteError(extended_error));
- return;
- }
-
- // The default handling is to assert on debug and to ignore on release.
- if (!sql::Connection::IsExpectedSqliteError(extended_error)) {
- DLOG(WARNING) << db->GetErrorMessage();
- base::UmaHistogramSparse("Previews.OptOut.SQLiteLoadError", extended_error);
- }
-}
-
-void InitDatabase(sql::Connection* db, base::FilePath path) {
- // The entry size should be between 11 and 10 + x bytes, where x is the the
- // length of the host name string in bytes.
- // The total number of entries per host is bounded at 32, and the total number
- // of hosts is currently unbounded (but typically expected to be under 100).
- // Assuming average of 100 bytes per entry, and 100 hosts, the total size will
- // be 4096 * 78. 250 allows room for extreme cases such as many host names
- // or very long host names.
- // The average case should be much smaller as users rarely visit hosts that
- // are not in their top 20 hosts. It should be closer to 32 * 100 * 20 for
- // most users, which is about 4096 * 15.
- // The total size of the database will be capped at 3200 entries.
- db->set_page_size(4096);
- db->set_cache_size(250);
- db->set_histogram_tag("PreviewsOptOut");
- db->set_exclusive_locking();
-
- db->set_error_callback(base::Bind(&DatabaseErrorCallback, db, path));
-
- base::File::Error err;
- if (!base::CreateDirectoryAndGetError(path.DirName(), &err)) {
- return;
- }
- if (!db->Open(path)) {
- return;
- }
-
- CreateSchema(db);
-}
-
-// Adds a new OptOut entry to the data base.
-void AddPreviewNavigationToDataBase(sql::Connection* db,
- bool opt_out,
- const std::string& host_name,
- PreviewsType type,
- base::Time now) {
- // Adds the new entry.
- const char kSqlInsert[] = "INSERT INTO " PREVIEWS_OPT_OUT_TABLE_NAME
- " (host_name, time, opt_out, type)"
- " VALUES "
- " (?, ?, ?, ?)";
-
- sql::Statement statement_insert(
- db->GetCachedStatement(SQL_FROM_HERE, kSqlInsert));
- statement_insert.BindString(0, host_name);
- statement_insert.BindInt64(1, now.ToInternalValue());
- statement_insert.BindBool(2, opt_out);
- statement_insert.BindInt(3, static_cast<int>(type));
- statement_insert.Run();
-}
-
-// Removes OptOut entries for |host_name| if the per-host row limit is exceeded.
-// Removes OptOut entries if per data base row limit is exceeded.
-void MaybeEvictHostEntryFromDataBase(sql::Connection* db,
- const std::string& host_name) {
- // Delete the oldest entries if there are more than |MaxRowsPerHostInOptOutDB|
- // for |host_name|.
- // DELETE ... LIMIT -1 OFFSET x means delete all but the first x entries.
- const char kSqlDeleteByHost[] =
- "DELETE FROM " PREVIEWS_OPT_OUT_TABLE_NAME
- " WHERE ROWID IN"
- " (SELECT ROWID from " PREVIEWS_OPT_OUT_TABLE_NAME
- " WHERE host_name == ?"
- " ORDER BY time DESC"
- " LIMIT -1 OFFSET ?)";
-
- sql::Statement statement_delete_by_host(
- db->GetCachedStatement(SQL_FROM_HERE, kSqlDeleteByHost));
- statement_delete_by_host.BindString(0, host_name);
- statement_delete_by_host.BindInt(1, MaxRowsPerHostInOptOutDB());
- statement_delete_by_host.Run();
-}
-
-// Deletes every preview navigation/OptOut entry for |type|.
-void ClearBlacklistForTypeInDataBase(sql::Connection* db, PreviewsType type) {
- const char kSql[] =
- "DELETE FROM " PREVIEWS_OPT_OUT_TABLE_NAME " WHERE type == ?";
- sql::Statement statement(db->GetUniqueStatement(kSql));
- statement.BindInt(0, static_cast<int>(type));
- statement.Run();
-}
-
-// Retrieves the list of previously enabled previews types with their version
-// from the Enabled Previews table.
-std::unique_ptr<std::map<PreviewsType, int>> GetStoredPreviews(
- sql::Connection* db) {
- const char kSqlLoadEnabledPreviewsVersions[] =
- "SELECT type, version FROM " ENABLED_PREVIEWS_TABLE_NAME;
-
- sql::Statement statement(
- db->GetUniqueStatement(kSqlLoadEnabledPreviewsVersions));
-
- std::unique_ptr<std::map<PreviewsType, int>> stored_previews(
- new std::map<PreviewsType, int>());
- while (statement.Step()) {
- PreviewsType type = static_cast<PreviewsType>(statement.ColumnInt(0));
- int version = statement.ColumnInt(1);
- stored_previews->insert({type, version});
- }
- return stored_previews;
-}
-
-// Adds a newly enabled |type| with its |version| to the Enabled Previews table.
-void InsertEnabledPreviewInDataBase(sql::Connection* db,
- PreviewsType type,
- int version) {
- const char kSqlInsert[] = "INSERT INTO " ENABLED_PREVIEWS_TABLE_NAME
- " (type, version)"
- " VALUES "
- " (?, ?)";
-
- sql::Statement statement_insert(db->GetUniqueStatement(kSqlInsert));
- statement_insert.BindInt(0, static_cast<int>(type));
- statement_insert.BindInt(1, version);
- statement_insert.Run();
-}
-
-// Updates the |version| of an enabled previews |type| in the Enabled Previews
-// table.
-void UpdateEnabledPreviewInDataBase(sql::Connection* db,
- PreviewsType type,
- int version) {
- const char kSqlUpdate[] = "UPDATE " ENABLED_PREVIEWS_TABLE_NAME
- " SET version = ?"
- " WHERE type = ?";
-
- sql::Statement statement_update(
- db->GetCachedStatement(SQL_FROM_HERE, kSqlUpdate));
- statement_update.BindInt(0, version);
- statement_update.BindInt(1, static_cast<int>(type));
- statement_update.Run();
-}
-
-// Deletes a previously enabled previews |type| from the Enabled Previews table.
-void DeleteEnabledPreviewInDataBase(sql::Connection* db, PreviewsType type) {
- const char kSqlDelete[] =
- "DELETE FROM " ENABLED_PREVIEWS_TABLE_NAME " WHERE type == ?";
-
- sql::Statement statement_delete(db->GetUniqueStatement(kSqlDelete));
- statement_delete.BindInt(0, static_cast<int>(type));
- statement_delete.Run();
-}
-
-// Checks the current set of enabled previews (with their current version)
-// and where a preview is now disabled or has a different version, cleans up
-// any associated blacklist entries.
-void CheckAndReconcileEnabledPreviewsWithDataBase(
- sql::Connection* db,
- PreviewsTypeList* enabled_previews) {
- std::unique_ptr<std::map<PreviewsType, int>> stored_previews(
- GetStoredPreviews(db));
-
- for (auto enabled_it = enabled_previews->begin();
- enabled_it != enabled_previews->end(); ++enabled_it) {
- PreviewsType type = enabled_it->first;
- int current_version = enabled_it->second;
- auto stored_it = stored_previews->find(type);
- if (stored_it == stored_previews->end()) {
- InsertEnabledPreviewInDataBase(db, type, current_version);
- } else {
- if (stored_it->second != current_version) {
- DCHECK_GE(current_version, stored_it->second);
- ClearBlacklistForTypeInDataBase(db, type);
- UpdateEnabledPreviewInDataBase(db, type, current_version);
- }
- // Erase entry from the local map to detect any newly disabled types.
- stored_previews->erase(stored_it);
- }
- }
-
- // Now check for any types that are no longer enabled.
- for (auto stored_it = stored_previews->begin();
- stored_it != stored_previews->end(); ++stored_it) {
- PreviewsType type = stored_it->first;
- ClearBlacklistForTypeInDataBase(db, type);
- DeleteEnabledPreviewInDataBase(db, type);
- }
-}
-
-void LoadBlackListFromDataBase(
- sql::Connection* db,
- PreviewsTypeList* enabled_previews,
- scoped_refptr<base::SingleThreadTaskRunner> runner,
- LoadBlackListCallback callback) {
- // First handle any update needed wrt enabled previews and their versions.
- CheckAndReconcileEnabledPreviewsWithDataBase(db, enabled_previews);
-
- // Gets the table sorted by host and time. Limits the number of hosts using
- // most recent opt_out time as the limiting function. Sorting is free due to
- // the table structure, and it improves performance in the loop below.
- const char kSql[] =
- "SELECT host_name, time, opt_out"
- " FROM " PREVIEWS_OPT_OUT_TABLE_NAME " ORDER BY host_name, time DESC";
-
- sql::Statement statement(db->GetUniqueStatement(kSql));
-
- std::unique_ptr<BlackListItemMap> black_list_item_map(new BlackListItemMap());
- std::unique_ptr<PreviewsBlackListItem> host_indifferent_black_list_item =
- PreviewsBlackList::CreateHostIndifferentBlackListItem();
- int count = 0;
- // Add the host name, the visit time, and opt out history to
- // |black_list_item_map|.
- while (statement.Step()) {
- ++count;
- std::string host_name = statement.ColumnString(0);
- PreviewsBlackListItem* black_list_item =
- PreviewsBlackList::GetOrCreateBlackListItemForMap(
- black_list_item_map.get(), host_name);
- DCHECK_LE(black_list_item_map->size(),
- params::MaxInMemoryHostsInBlackList());
- // Allows the internal logic of PreviewsBlackListItem to determine how to
- // evict entries when there are more than
- // |StoredHistoryLengthForBlackList()| for the host.
- black_list_item->AddPreviewNavigation(
- statement.ColumnBool(2),
- base::Time::FromInternalValue(statement.ColumnInt64(1)));
- // Allows the internal logic of PreviewsBlackListItem to determine what
- // items to evict.
- host_indifferent_black_list_item->AddPreviewNavigation(
- statement.ColumnBool(2),
- base::Time::FromInternalValue(statement.ColumnInt64(1)));
- }
-
- UMA_HISTOGRAM_COUNTS_10000("Previews.OptOut.DBRowCount", count);
-
- if (count > MaxRowsInOptOutDB()) {
- // Delete the oldest entries if there are more than |kMaxEntriesInDB|.
- // DELETE ... LIMIT -1 OFFSET x means delete all but the first x entries.
- const char kSqlDeleteByDBSize[] =
- "DELETE FROM " PREVIEWS_OPT_OUT_TABLE_NAME
- " WHERE ROWID IN"
- " (SELECT ROWID from " PREVIEWS_OPT_OUT_TABLE_NAME
- " ORDER BY time DESC"
- " LIMIT -1 OFFSET ?)";
-
- sql::Statement statement_delete(
- db->GetCachedStatement(SQL_FROM_HERE, kSqlDeleteByDBSize));
- statement_delete.BindInt(0, MaxRowsInOptOutDB());
- statement_delete.Run();
- }
-
- runner->PostTask(FROM_HERE,
- base::BindOnce(callback, std::move(black_list_item_map),
- std::move(host_indifferent_black_list_item)));
-}
-
-// Synchronous implementations, these are run on the background thread
-// and actually do the work to access the SQL data base.
-void LoadBlackListSync(sql::Connection* db,
- const base::FilePath& path,
- std::unique_ptr<PreviewsTypeList> enabled_previews,
- scoped_refptr<base::SingleThreadTaskRunner> runner,
- LoadBlackListCallback callback) {
- if (!db->is_open())
- InitDatabase(db, path);
-
- LoadBlackListFromDataBase(db, enabled_previews.get(), runner, callback);
-}
-
-// Deletes every row in the table that has entry time between |begin_time| and
-// |end_time|.
-void ClearBlackListSync(sql::Connection* db,
- base::Time begin_time,
- base::Time end_time) {
- const char kSql[] = "DELETE FROM " PREVIEWS_OPT_OUT_TABLE_NAME
- " WHERE time >= ? and time <= ?";
-
- sql::Statement statement(db->GetUniqueStatement(kSql));
- statement.BindInt64(0, begin_time.ToInternalValue());
- statement.BindInt64(1, end_time.ToInternalValue());
- statement.Run();
-}
-
-void AddPreviewNavigationSync(bool opt_out,
- const std::string& host_name,
- PreviewsType type,
- base::Time now,
- sql::Connection* db) {
- sql::Transaction transaction(db);
- if (!transaction.Begin())
- return;
- AddPreviewNavigationToDataBase(db, opt_out, host_name, type, now);
- MaybeEvictHostEntryFromDataBase(db, host_name);
- transaction.Commit();
-}
-
-} // namespace
-
-PreviewsOptOutStoreSQL::PreviewsOptOutStoreSQL(
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
- scoped_refptr<base::SequencedTaskRunner> background_task_runner,
- const base::FilePath& path,
- std::unique_ptr<PreviewsTypeList> enabled_previews)
- : io_task_runner_(io_task_runner),
- background_task_runner_(background_task_runner),
- db_file_path_(path),
- enabled_previews_(std::move(enabled_previews)) {
- DCHECK(enabled_previews_);
-}
-
-PreviewsOptOutStoreSQL::~PreviewsOptOutStoreSQL() {
- DCHECK(io_task_runner_->BelongsToCurrentThread());
- if (db_) {
- background_task_runner_->DeleteSoon(FROM_HERE, db_.release());
- }
-}
-
-void PreviewsOptOutStoreSQL::AddPreviewNavigation(bool opt_out,
- const std::string& host_name,
- PreviewsType type,
- base::Time now) {
- DCHECK(io_task_runner_->BelongsToCurrentThread());
- DCHECK(db_);
- background_task_runner_->PostTask(
- FROM_HERE, base::Bind(&AddPreviewNavigationSync, opt_out, host_name, type,
- now, db_.get()));
-}
-
-void PreviewsOptOutStoreSQL::ClearBlackList(base::Time begin_time,
- base::Time end_time) {
- DCHECK(io_task_runner_->BelongsToCurrentThread());
- DCHECK(db_);
- background_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&ClearBlackListSync, db_.get(), begin_time, end_time));
-}
-
-void PreviewsOptOutStoreSQL::LoadBlackList(LoadBlackListCallback callback) {
- DCHECK(io_task_runner_->BelongsToCurrentThread());
- if (!db_)
- db_ = std::make_unique<sql::Connection>();
- std::unique_ptr<PreviewsTypeList> enabled_previews =
- std::make_unique<PreviewsTypeList>(*enabled_previews_);
- background_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&LoadBlackListSync, db_.get(), db_file_path_,
- std::move(enabled_previews),
- base::ThreadTaskRunnerHandle::Get(), callback));
-}
-
-} // namespace previews
diff --git a/chromium/components/previews/core/previews_opt_out_store_sql_unittest.cc b/chromium/components/previews/core/previews_opt_out_store_sql_unittest.cc
deleted file mode 100644
index e32e6e7e55b..00000000000
--- a/chromium/components/previews/core/previews_opt_out_store_sql_unittest.cc
+++ /dev/null
@@ -1,356 +0,0 @@
-// Copyright (c) 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/previews/core/previews_opt_out_store_sql.h"
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/message_loop/message_loop.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/field_trial_param_associator.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/run_loop.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/test/histogram_tester.h"
-#include "base/test/simple_test_clock.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "components/previews/core/previews_black_list_item.h"
-#include "components/previews/core/previews_opt_out_store.h"
-#include "sql/test/test_helpers.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace previews {
-
-namespace {
-
-const base::FilePath::CharType kOptOutFilename[] = FILE_PATH_LITERAL("OptOut");
-
-} // namespace
-
-class PreviewsOptOutStoreSQLTest : public testing::Test {
- public:
- PreviewsOptOutStoreSQLTest()
- : field_trials_(new base::FieldTrialList(nullptr)) {}
- ~PreviewsOptOutStoreSQLTest() override {}
-
- // Called when |store_| is done loading.
- void OnLoaded(std::unique_ptr<BlackListItemMap> black_list_map,
- std::unique_ptr<PreviewsBlackListItem> host_indifferent_item) {
- black_list_map_ = std::move(black_list_map);
- host_indifferent_item_ = std::move(host_indifferent_item);
- }
-
- // Initializes the store and get the data from it.
- void Load() {
- store_->LoadBlackList(base::Bind(&PreviewsOptOutStoreSQLTest::OnLoaded,
- base::Unretained(this)));
- base::RunLoop().RunUntilIdle();
- }
-
- // Destroys the database connection and |store_|.
- void DestroyStore() {
- store_.reset();
- base::RunLoop().RunUntilIdle();
- }
-
- // Creates a store that operates on one thread.
- void Create(std::unique_ptr<PreviewsTypeList> enabled_previews) {
- store_ = std::make_unique<PreviewsOptOutStoreSQL>(
- base::ThreadTaskRunnerHandle::Get(),
- base::ThreadTaskRunnerHandle::Get(),
- temp_dir_.GetPath().Append(kOptOutFilename),
- std::move(enabled_previews));
- }
-
- // Sets up initialization of |store_|.
- void CreateAndLoad(std::unique_ptr<PreviewsTypeList> enabled_previews) {
- Create(std::move(enabled_previews));
- Load();
- }
-
- // Creates a directory for the test.
- void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
-
- // Delete |store_| if it hasn't been deleted.
- void TearDown() override { DestroyStore(); }
-
- protected:
- void ResetFieldTrials() {
- // Destroy existing FieldTrialList before creating new one to avoid DCHECK.
- field_trials_.reset();
- field_trials_.reset(new base::FieldTrialList(nullptr));
- base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
- }
-
- base::HistogramTester histogram_tester_;
-
- base::MessageLoop message_loop_;
-
- // The backing SQL store.
- std::unique_ptr<PreviewsOptOutStoreSQL> store_;
-
- // The map returned from |store_|.
- std::unique_ptr<BlackListItemMap> black_list_map_;
-
- // The host indifferent item from |store_|
- std::unique_ptr<PreviewsBlackListItem> host_indifferent_item_;
-
- // The directory for the database.
- base::ScopedTempDir temp_dir_;
-
- private:
- std::unique_ptr<base::FieldTrialList> field_trials_;
-};
-
-TEST_F(PreviewsOptOutStoreSQLTest, TestErrorRecovery) {
- // Creates the database and corrupt to test the recovery method.
- std::string test_host = "host.com";
- std::unique_ptr<PreviewsTypeList> enabled_previews(new PreviewsTypeList);
- enabled_previews->push_back({PreviewsType::OFFLINE, 0});
- CreateAndLoad(std::move(enabled_previews));
- store_->AddPreviewNavigation(true, test_host, PreviewsType::OFFLINE,
- base::Time::Now());
- base::RunLoop().RunUntilIdle();
- DestroyStore();
-
- // Corrupts the database by adjusting the header size.
- EXPECT_TRUE(sql::test::CorruptSizeInHeader(
- temp_dir_.GetPath().Append(kOptOutFilename)));
- base::RunLoop().RunUntilIdle();
-
- enabled_previews.reset(new PreviewsTypeList);
- enabled_previews->push_back({PreviewsType::OFFLINE, 0});
- CreateAndLoad(std::move(enabled_previews));
- // The data should be recovered.
- EXPECT_EQ(1U, black_list_map_->size());
- auto iter = black_list_map_->find(test_host);
-
- EXPECT_NE(black_list_map_->end(), iter);
- EXPECT_EQ(1U, iter->second->OptOutRecordsSizeForTesting());
-}
-
-TEST_F(PreviewsOptOutStoreSQLTest, TestPersistance) {
- // Tests if data is stored as expected in the SQLite database.
- std::string test_host = "host.com";
- std::unique_ptr<PreviewsTypeList> enabled_previews(new PreviewsTypeList);
- enabled_previews->push_back({PreviewsType::OFFLINE, 0});
- CreateAndLoad(std::move(enabled_previews));
- histogram_tester_.ExpectUniqueSample("Previews.OptOut.DBRowCount", 0, 1);
- base::Time now = base::Time::Now();
- store_->AddPreviewNavigation(true, test_host, PreviewsType::OFFLINE, now);
- base::RunLoop().RunUntilIdle();
-
- // Replace the store effectively destroying the current one and forcing it
- // to write its data to disk.
- DestroyStore();
-
- // Reload and test for persistence
- enabled_previews.reset(new PreviewsTypeList);
- enabled_previews->push_back({PreviewsType::OFFLINE, 0});
- CreateAndLoad(std::move(enabled_previews));
- EXPECT_EQ(1U, black_list_map_->size());
- auto iter = black_list_map_->find(test_host);
-
- EXPECT_NE(black_list_map_->end(), iter);
- EXPECT_EQ(1U, iter->second->OptOutRecordsSizeForTesting());
- EXPECT_EQ(now, iter->second->most_recent_opt_out_time().value());
- EXPECT_EQ(1U, host_indifferent_item_->OptOutRecordsSizeForTesting());
- EXPECT_EQ(now, host_indifferent_item_->most_recent_opt_out_time().value());
- histogram_tester_.ExpectBucketCount("Previews.OptOut.DBRowCount", 1, 1);
- histogram_tester_.ExpectTotalCount("Previews.OptOut.DBRowCount", 2);
-}
-
-TEST_F(PreviewsOptOutStoreSQLTest, TestMaxRows) {
- // Tests that the number of rows are culled down to the row limit at each
- // load.
- std::string test_host_a = "host_a.com";
- std::string test_host_b = "host_b.com";
- std::string test_host_c = "host_c.com";
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- size_t row_limit = 2;
- std::string row_limit_string = base::NumberToString(row_limit);
- command_line->AppendSwitchASCII("previews-max-opt-out-rows",
- row_limit_string);
- std::unique_ptr<PreviewsTypeList> enabled_previews(new PreviewsTypeList);
- enabled_previews->push_back({PreviewsType::OFFLINE, 0});
- CreateAndLoad(std::move(enabled_previews));
- histogram_tester_.ExpectUniqueSample("Previews.OptOut.DBRowCount", 0, 1);
- base::SimpleTestClock clock;
-
- // Create three different entries with different hosts.
- store_->AddPreviewNavigation(true, test_host_a, PreviewsType::OFFLINE,
- clock.Now());
- clock.Advance(base::TimeDelta::FromSeconds(1));
-
- store_->AddPreviewNavigation(true, test_host_b, PreviewsType::OFFLINE,
- clock.Now());
- base::Time host_b_time = clock.Now();
- clock.Advance(base::TimeDelta::FromSeconds(1));
-
- store_->AddPreviewNavigation(false, test_host_c, PreviewsType::OFFLINE,
- clock.Now());
- base::RunLoop().RunUntilIdle();
- // Replace the store effectively destroying the current one and forcing it
- // to write its data to disk.
- DestroyStore();
-
- // Reload and test for persistence
- enabled_previews.reset(new PreviewsTypeList);
- enabled_previews->push_back({PreviewsType::OFFLINE, 0});
- CreateAndLoad(std::move(enabled_previews));
- histogram_tester_.ExpectBucketCount("Previews.OptOut.DBRowCount",
- static_cast<int>(row_limit) + 1, 1);
- // The delete happens after the load, so it is possible to load more than
- // |row_limit| into the in memory map.
- EXPECT_EQ(row_limit + 1, black_list_map_->size());
- EXPECT_EQ(row_limit + 1,
- host_indifferent_item_->OptOutRecordsSizeForTesting());
-
- DestroyStore();
- enabled_previews.reset(new PreviewsTypeList);
- enabled_previews->push_back({PreviewsType::OFFLINE, 0});
- CreateAndLoad(std::move(enabled_previews));
- histogram_tester_.ExpectBucketCount("Previews.OptOut.DBRowCount",
- static_cast<int>(row_limit), 1);
-
- EXPECT_EQ(row_limit, black_list_map_->size());
- auto iter_host_b = black_list_map_->find(test_host_b);
- auto iter_host_c = black_list_map_->find(test_host_c);
-
- EXPECT_EQ(black_list_map_->end(), black_list_map_->find(test_host_a));
- EXPECT_NE(black_list_map_->end(), iter_host_b);
- EXPECT_NE(black_list_map_->end(), iter_host_c);
- EXPECT_EQ(host_b_time,
- iter_host_b->second->most_recent_opt_out_time().value());
- EXPECT_EQ(1U, iter_host_b->second->OptOutRecordsSizeForTesting());
- EXPECT_EQ(host_b_time,
- host_indifferent_item_->most_recent_opt_out_time().value());
- EXPECT_EQ(row_limit, host_indifferent_item_->OptOutRecordsSizeForTesting());
- histogram_tester_.ExpectTotalCount("Previews.OptOut.DBRowCount", 3);
-}
-
-TEST_F(PreviewsOptOutStoreSQLTest, TestMaxRowsPerHost) {
- // Tests that each host is limited to |row_limit| rows.
- std::string test_host = "host.com";
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- size_t row_limit = 2;
- std::string row_limit_string = base::NumberToString(row_limit);
- command_line->AppendSwitchASCII("previews-max-opt-out-rows-per-host",
- row_limit_string);
- std::unique_ptr<PreviewsTypeList> enabled_previews(new PreviewsTypeList);
- enabled_previews->push_back({PreviewsType::OFFLINE, 0});
- CreateAndLoad(std::move(enabled_previews));
- histogram_tester_.ExpectUniqueSample("Previews.OptOut.DBRowCount", 0, 1);
- base::SimpleTestClock clock;
-
- base::Time last_opt_out_time;
- for (size_t i = 0; i < row_limit; i++) {
- store_->AddPreviewNavigation(true, test_host, PreviewsType::OFFLINE,
- clock.Now());
- last_opt_out_time = clock.Now();
- clock.Advance(base::TimeDelta::FromSeconds(1));
- }
-
- clock.Advance(base::TimeDelta::FromSeconds(1));
- store_->AddPreviewNavigation(false, test_host, PreviewsType::OFFLINE,
- clock.Now());
-
- base::RunLoop().RunUntilIdle();
- // Replace the store effectively destroying the current one and forcing it
- // to write its data to disk.
- DestroyStore();
-
- // Reload and test for persistence.
- enabled_previews.reset(new PreviewsTypeList);
- enabled_previews->push_back({PreviewsType::OFFLINE, 0});
- CreateAndLoad(std::move(enabled_previews));
- histogram_tester_.ExpectBucketCount("Previews.OptOut.DBRowCount",
- static_cast<int>(row_limit), 1);
-
- EXPECT_EQ(1U, black_list_map_->size());
- auto iter = black_list_map_->find(test_host);
-
- EXPECT_NE(black_list_map_->end(), iter);
- EXPECT_EQ(last_opt_out_time,
- iter->second->most_recent_opt_out_time().value());
- EXPECT_EQ(row_limit, iter->second->OptOutRecordsSizeForTesting());
- EXPECT_EQ(row_limit, host_indifferent_item_->OptOutRecordsSizeForTesting());
- clock.Advance(base::TimeDelta::FromSeconds(1));
- // If both entries' opt out states are stored correctly, then this should not
- // be black listed.
- EXPECT_FALSE(iter->second->IsBlackListed(clock.Now()));
- histogram_tester_.ExpectTotalCount("Previews.OptOut.DBRowCount", 2);
-}
-
-TEST_F(PreviewsOptOutStoreSQLTest, TestPreviewsDisabledClearsBlacklistEntry) {
- // Tests if data is cleared for previews type when it is disabled.
- // Enable offline previews and add black list entry for it.
- std::map<std::string, std::string> params;
- std::string test_host = "host.com";
- std::unique_ptr<PreviewsTypeList> enabled_previews(new PreviewsTypeList);
- enabled_previews->push_back({PreviewsType::OFFLINE, 0});
- CreateAndLoad(std::move(enabled_previews));
- histogram_tester_.ExpectUniqueSample("Previews.OptOut.DBRowCount", 0, 1);
- base::Time now = base::Time::Now();
- store_->AddPreviewNavigation(true, test_host, PreviewsType::OFFLINE, now);
- base::RunLoop().RunUntilIdle();
-
- // Force data write to database then reload it and verify black list entry
- // is present.
- DestroyStore();
- enabled_previews.reset(new PreviewsTypeList);
- enabled_previews->push_back({PreviewsType::OFFLINE, 0});
- CreateAndLoad(std::move(enabled_previews));
- auto iter = black_list_map_->find(test_host);
- EXPECT_NE(black_list_map_->end(), iter);
- EXPECT_EQ(1U, iter->second->OptOutRecordsSizeForTesting());
-
- DestroyStore();
- enabled_previews.reset(new PreviewsTypeList);
- CreateAndLoad(std::move(enabled_previews));
- iter = black_list_map_->find(test_host);
- EXPECT_EQ(black_list_map_->end(), iter);
-
-}
-
-TEST_F(PreviewsOptOutStoreSQLTest,
- TestPreviewsVersionUpdateClearsBlacklistEntry) {
- // Tests if data is cleared for new version of previews type.
- // Enable offline previews and add black list entry for it.
- std::string test_host = "host.com";
- std::unique_ptr<PreviewsTypeList> enabled_previews(new PreviewsTypeList);
- enabled_previews->push_back({PreviewsType::OFFLINE, 1});
- CreateAndLoad(std::move(enabled_previews));
- histogram_tester_.ExpectUniqueSample("Previews.OptOut.DBRowCount", 0, 1);
- base::Time now = base::Time::Now();
- store_->AddPreviewNavigation(true, test_host, PreviewsType::OFFLINE, now);
- base::RunLoop().RunUntilIdle();
-
- // Force data write to database then reload it and verify black list entry
- // is present.
- DestroyStore();
- enabled_previews.reset(new PreviewsTypeList);
- enabled_previews->push_back({PreviewsType::OFFLINE, 1});
- CreateAndLoad(std::move(enabled_previews));
- auto iter = black_list_map_->find(test_host);
- EXPECT_NE(black_list_map_->end(), iter);
- EXPECT_EQ(1U, iter->second->OptOutRecordsSizeForTesting());
-
- DestroyStore();
- enabled_previews.reset(new PreviewsTypeList);
- enabled_previews->push_back({PreviewsType::OFFLINE, 2});
- CreateAndLoad(std::move(enabled_previews));
- iter = black_list_map_->find(test_host);
- EXPECT_EQ(black_list_map_->end(), iter);
-}
-
-} // namespace net
diff --git a/chromium/components/previews/core/previews_user_data.h b/chromium/components/previews/core/previews_user_data.h
index 881f1b98678..df90bc1cacd 100644
--- a/chromium/components/previews/core/previews_user_data.h
+++ b/chromium/components/previews/core/previews_user_data.h
@@ -50,6 +50,15 @@ class PreviewsUserData : public base::SupportsUserData::Data {
return data_savings_inflation_percent_;
}
+ // Whether a lite page preview was prevented from being shown due to the
+ // blacklist.
+ bool black_listed_for_lite_page() const {
+ return black_listed_for_lite_page_;
+ }
+ void set_black_listed_for_lite_page(bool black_listed_for_lite_page) {
+ black_listed_for_lite_page_ = black_listed_for_lite_page;
+ }
+
// Sets that the page load received the Cache-Control:no-transform
// directive. Expected to be set upon receiving a committed response.
void SetCacheControlNoTransformDirective() {
@@ -76,6 +85,12 @@ class PreviewsUserData : public base::SupportsUserData::Data {
return committed_previews_type_ != previews::PreviewsType::NONE;
}
+ // Whether an offline preview is being served.
+ void set_offline_preview_used(bool offline_preview_used) {
+ offline_preview_used_ = offline_preview_used;
+ }
+ bool offline_preview_used() { return offline_preview_used_; }
+
private:
// A session unique ID related to this navigation.
const uint64_t page_id_;
@@ -84,6 +99,13 @@ class PreviewsUserData : public base::SupportsUserData::Data {
// Whether the origin provided a no-transform directive.
bool cache_control_no_transform_directive_ = false;
+ // Whether an offline preview is being served.
+ bool offline_preview_used_ = false;
+
+ // Whether a lite page preview was prevented from being shown due to the
+ // blacklist.
+ bool black_listed_for_lite_page_ = false;
+
// The committed previews type, if any.
previews::PreviewsType committed_previews_type_ = PreviewsType::NONE;
diff --git a/chromium/components/previews/core/test_previews_decider.cc b/chromium/components/previews/core/test_previews_decider.cc
index f6a9f3abcde..801dbc65c96 100644
--- a/chromium/components/previews/core/test_previews_decider.cc
+++ b/chromium/components/previews/core/test_previews_decider.cc
@@ -15,7 +15,8 @@ bool TestPreviewsDecider::ShouldAllowPreviewAtECT(
const net::URLRequest& request,
previews::PreviewsType type,
net::EffectiveConnectionType effective_connection_type_threshold,
- const std::vector<std::string>& host_blacklist_from_server) const {
+ const std::vector<std::string>& host_blacklist_from_server,
+ bool ignore_long_term_black_list_rules) const {
return allow_previews_;
}
diff --git a/chromium/components/previews/core/test_previews_decider.h b/chromium/components/previews/core/test_previews_decider.h
index 015993bd5ee..824138134bd 100644
--- a/chromium/components/previews/core/test_previews_decider.h
+++ b/chromium/components/previews/core/test_previews_decider.h
@@ -20,8 +20,8 @@ class TestPreviewsDecider : public previews::PreviewsDecider {
const net::URLRequest& request,
previews::PreviewsType type,
net::EffectiveConnectionType effective_connection_type_threshold,
- const std::vector<std::string>& host_blacklist_from_server)
- const override;
+ const std::vector<std::string>& host_blacklist_from_server,
+ bool ignore_long_term_black_list_rules) const override;
bool ShouldAllowPreview(const net::URLRequest& request,
previews::PreviewsType type) const override;
bool IsURLAllowedForPreview(const net::URLRequest& request,
diff --git a/chromium/components/printing/browser/features.cc b/chromium/components/printing/browser/features.cc
index cca95f2de52..e24200dbf4f 100644
--- a/chromium/components/printing/browser/features.cc
+++ b/chromium/components/printing/browser/features.cc
@@ -8,7 +8,7 @@ namespace printing {
namespace features {
const base::Feature kUsePdfCompositorServiceForPrint{
- "UsePdfCompositorServiceForPrint", base::FEATURE_DISABLED_BY_DEFAULT};
+ "UsePdfCompositorServiceForPrint", base::FEATURE_ENABLED_BY_DEFAULT};
} // namespace features
} // namespace printing
diff --git a/chromium/components/printing/browser/print_manager_utils.cc b/chromium/components/printing/browser/print_manager_utils.cc
index a38d8ab661b..0bd4f44c608 100644
--- a/chromium/components/printing/browser/print_manager_utils.cc
+++ b/chromium/components/printing/browser/print_manager_utils.cc
@@ -68,7 +68,7 @@ void RenderParamsFromPrintSettings(const PrintSettings& settings,
params->url = settings.url();
params->printed_doc_type =
IsOopifEnabled() ? SkiaDocumentType::MSKP : SkiaDocumentType::PDF;
- params->num_pages_per_sheet = settings.num_pages_per_sheet();
+ params->pages_per_sheet = settings.pages_per_sheet();
}
} // namespace printing
diff --git a/chromium/components/printing/common/print_messages.cc b/chromium/components/printing/common/print_messages.cc
index 8b6f52b3f80..18df7614a98 100644
--- a/chromium/components/printing/common/print_messages.cc
+++ b/chromium/components/printing/common/print_messages.cc
@@ -89,7 +89,7 @@ PrintMsg_Print_Params::PrintMsg_Print_Params()
should_print_backgrounds(false),
printed_doc_type(printing::SkiaDocumentType::PDF),
prefer_css_page_size(false),
- num_pages_per_sheet(1) {}
+ pages_per_sheet(1) {}
PrintMsg_Print_Params::PrintMsg_Print_Params(
const PrintMsg_Print_Params& other) = default;
@@ -121,7 +121,7 @@ void PrintMsg_Print_Params::Reset() {
should_print_backgrounds = false;
printed_doc_type = printing::SkiaDocumentType::PDF;
prefer_css_page_size = false;
- num_pages_per_sheet = 1;
+ pages_per_sheet = 1;
}
PrintMsg_PrintPages_Params::PrintMsg_PrintPages_Params() {}
@@ -152,6 +152,14 @@ PrintHostMsg_RequestPrintPreview_Params::
PrintHostMsg_RequestPrintPreview_Params::
~PrintHostMsg_RequestPrintPreview_Params() {}
+PrintHostMsg_PreviewIds::PrintHostMsg_PreviewIds()
+ : request_id(-1), ui_id(-1) {}
+
+PrintHostMsg_PreviewIds::PrintHostMsg_PreviewIds(int request_id, int ui_id)
+ : request_id(request_id), ui_id(ui_id) {}
+
+PrintHostMsg_PreviewIds::~PrintHostMsg_PreviewIds() {}
+
PrintHostMsg_SetOptionsFromDocument_Params::
PrintHostMsg_SetOptionsFromDocument_Params()
: is_scaling_disabled(false),
diff --git a/chromium/components/printing/common/print_messages.h b/chromium/components/printing/common/print_messages.h
index cd3fe2986af..d29bb6aedec 100644
--- a/chromium/components/printing/common/print_messages.h
+++ b/chromium/components/printing/common/print_messages.h
@@ -63,7 +63,7 @@ struct PrintMsg_Print_Params {
bool should_print_backgrounds;
printing::SkiaDocumentType printed_doc_type;
bool prefer_css_page_size;
- int num_pages_per_sheet;
+ int pages_per_sheet;
};
struct PrintMsg_PrintPages_Params {
@@ -96,6 +96,14 @@ struct PrintHostMsg_RequestPrintPreview_Params {
bool selection_only;
};
+struct PrintHostMsg_PreviewIds {
+ PrintHostMsg_PreviewIds();
+ PrintHostMsg_PreviewIds(int request_id, int ui_id);
+ ~PrintHostMsg_PreviewIds();
+ int request_id;
+ int ui_id;
+};
+
struct PrintHostMsg_SetOptionsFromDocument_Params {
PrintHostMsg_SetOptionsFromDocument_Params();
~PrintHostMsg_SetOptionsFromDocument_Params();
@@ -196,7 +204,7 @@ IPC_STRUCT_TRAITS_BEGIN(PrintMsg_Print_Params)
// Number of pages per sheet. This parameter is for N-up mode.
// Defaults to 1 if the feature is disabled, and some number greater
// than 1 otherwise. See printing::NupParameters for supported values.
- IPC_STRUCT_TRAITS_MEMBER(num_pages_per_sheet)
+ IPC_STRUCT_TRAITS_MEMBER(pages_per_sheet)
IPC_STRUCT_TRAITS_END()
IPC_STRUCT_TRAITS_BEGIN(printing::PageRange)
@@ -212,6 +220,11 @@ IPC_STRUCT_TRAITS_BEGIN(PrintHostMsg_RequestPrintPreview_Params)
IPC_STRUCT_TRAITS_MEMBER(selection_only)
IPC_STRUCT_TRAITS_END()
+IPC_STRUCT_TRAITS_BEGIN(PrintHostMsg_PreviewIds)
+ IPC_STRUCT_TRAITS_MEMBER(request_id)
+ IPC_STRUCT_TRAITS_MEMBER(ui_id)
+IPC_STRUCT_TRAITS_END()
+
IPC_STRUCT_TRAITS_BEGIN(PrintHostMsg_SetOptionsFromDocument_Params)
// Specifies whether print scaling is enabled or not.
IPC_STRUCT_TRAITS_MEMBER(is_scaling_disabled)
@@ -284,9 +297,6 @@ IPC_STRUCT_BEGIN(PrintHostMsg_DidPreviewDocument_Params)
// Whether the preview can be modified.
IPC_STRUCT_MEMBER(bool, modifiable)
-
- // The id of the preview request.
- IPC_STRUCT_MEMBER(int, preview_request_id)
IPC_STRUCT_END()
// Parameters to describe a rendered preview page.
@@ -297,9 +307,6 @@ IPC_STRUCT_BEGIN(PrintHostMsg_DidPreviewPage_Params)
// |page_number| is zero-based and should not be negative.
IPC_STRUCT_MEMBER(int, page_number)
- // The id of the preview request.
- IPC_STRUCT_MEMBER(int, preview_request_id)
-
// Cookie for the document to ensure correctness.
IPC_STRUCT_MEMBER(int, document_cookie)
IPC_STRUCT_END()
@@ -311,9 +318,6 @@ IPC_STRUCT_BEGIN(PrintHostMsg_DidGetPreviewPageCount_Params)
// Scaling % to fit to page
IPC_STRUCT_MEMBER(int, fit_to_page_scaling)
-
- // The id of the preview request.
- IPC_STRUCT_MEMBER(int, preview_request_id)
IPC_STRUCT_END()
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
@@ -457,34 +461,37 @@ IPC_MESSAGE_ROUTED1(PrintHostMsg_RequestPrintPreview,
PrintHostMsg_RequestPrintPreview_Params /* params */)
// Notify the browser the number of pages in the print preview document.
-IPC_MESSAGE_ROUTED1(PrintHostMsg_DidGetPreviewPageCount,
- PrintHostMsg_DidGetPreviewPageCount_Params /* params */)
+IPC_MESSAGE_ROUTED2(PrintHostMsg_DidGetPreviewPageCount,
+ PrintHostMsg_DidGetPreviewPageCount_Params /* params */,
+ PrintHostMsg_PreviewIds /* ids */)
// Notify the browser of the default page layout according to the currently
// selected printer and page size.
// |printable_area_in_points| Specifies the printable area in points.
// |has_custom_page_size_style| is true when the printing frame has a custom
// page size css otherwise false.
-IPC_MESSAGE_ROUTED3(PrintHostMsg_DidGetDefaultPageLayout,
+IPC_MESSAGE_ROUTED4(PrintHostMsg_DidGetDefaultPageLayout,
printing::PageSizeMargins /* page layout in points */,
gfx::Rect /* printable area in points */,
- bool /* has custom page size style */)
+ bool /* has custom page size style */,
+ PrintHostMsg_PreviewIds /* ids */)
// Notify the browser a print preview page has been rendered.
-IPC_MESSAGE_ROUTED1(PrintHostMsg_DidPreviewPage,
- PrintHostMsg_DidPreviewPage_Params /* params */)
+IPC_MESSAGE_ROUTED2(PrintHostMsg_DidPreviewPage,
+ PrintHostMsg_DidPreviewPage_Params /* params */,
+ PrintHostMsg_PreviewIds /* ids */)
// Asks the browser whether the print preview has been cancelled.
-IPC_SYNC_MESSAGE_ROUTED2_1(PrintHostMsg_CheckForCancel,
- int32_t /* PrintPreviewUI ID */,
- int /* request id */,
+IPC_SYNC_MESSAGE_ROUTED1_1(PrintHostMsg_CheckForCancel,
+ PrintHostMsg_PreviewIds /* ids */,
bool /* print preview cancelled */)
// Sends back to the browser the complete rendered document (non-draft mode,
// used for printing) that was requested by a PrintMsg_PrintPreview message.
// The memory handle in this message is already valid in the browser process.
-IPC_MESSAGE_ROUTED1(PrintHostMsg_MetafileReadyForPrinting,
- PrintHostMsg_DidPreviewDocument_Params /* params */)
+IPC_MESSAGE_ROUTED2(PrintHostMsg_MetafileReadyForPrinting,
+ PrintHostMsg_DidPreviewDocument_Params /* params */,
+ PrintHostMsg_PreviewIds /* ids */)
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
// This is sent when there are invalid printer settings.
@@ -496,18 +503,21 @@ IPC_MESSAGE_ROUTED1(PrintHostMsg_PrintingFailed,
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
// Tell the browser print preview failed.
-IPC_MESSAGE_ROUTED1(PrintHostMsg_PrintPreviewFailed,
- int /* document cookie */)
+IPC_MESSAGE_ROUTED2(PrintHostMsg_PrintPreviewFailed,
+ int /* document cookie */,
+ PrintHostMsg_PreviewIds /* ids */)
// Tell the browser print preview was cancelled.
-IPC_MESSAGE_ROUTED1(PrintHostMsg_PrintPreviewCancelled,
- int /* document cookie */)
+IPC_MESSAGE_ROUTED2(PrintHostMsg_PrintPreviewCancelled,
+ int /* document cookie */,
+ PrintHostMsg_PreviewIds /* ids */)
// Tell the browser print preview found the selected printer has invalid
// settings (which typically caused by disconnected network printer or printer
// driver is bogus).
-IPC_MESSAGE_ROUTED1(PrintHostMsg_PrintPreviewInvalidPrinterSettings,
- int /* document cookie */)
+IPC_MESSAGE_ROUTED2(PrintHostMsg_PrintPreviewInvalidPrinterSettings,
+ int /* document cookie */,
+ PrintHostMsg_PreviewIds /* ids */)
// Run a nested run loop in the renderer until print preview for
// window.print() finishes.
@@ -519,8 +529,9 @@ IPC_MESSAGE_ROUTED1(PrintHostMsg_ShowScriptedPrintPreview,
bool /* is_modifiable */)
// Notify the browser to set print presets based on source PDF document.
-IPC_MESSAGE_ROUTED1(PrintHostMsg_SetOptionsFromDocument,
- PrintHostMsg_SetOptionsFromDocument_Params /* params */)
+IPC_MESSAGE_ROUTED2(PrintHostMsg_SetOptionsFromDocument,
+ PrintHostMsg_SetOptionsFromDocument_Params /* params */,
+ PrintHostMsg_PreviewIds /* ids */)
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
#endif // COMPONENTS_PRINTING_COMMON_PRINT_MESSAGES_H_
diff --git a/chromium/components/printing/renderer/DEPS b/chromium/components/printing/renderer/DEPS
index e33c1df924d..d195afd698a 100644
--- a/chromium/components/printing/renderer/DEPS
+++ b/chromium/components/printing/renderer/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+cc/paint",
"+components/grit/components_resources.h",
"+content/public/renderer",
"+mojo/public",
diff --git a/chromium/components/printing/renderer/print_render_frame_helper.cc b/chromium/components/printing/renderer/print_render_frame_helper.cc
index af18233bc50..c7ea5cf76aa 100644
--- a/chromium/components/printing/renderer/print_render_frame_helper.cc
+++ b/chromium/components/printing/renderer/print_render_frame_helper.cc
@@ -41,16 +41,18 @@
#include "third_party/blink/public/common/frame/sandbox_flags.h"
#include "third_party/blink/public/mojom/page/page_visibility_state.mojom.h"
#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_data.h"
#include "third_party/blink/public/platform/web_double_size.h"
#include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/public/web/web_console_message.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_frame_client.h"
#include "third_party/blink/public/web/web_frame_owner_properties.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.h"
#include "third_party/blink/public/web/web_plugin_document.h"
#include "third_party/blink/public/web/web_print_params.h"
@@ -59,6 +61,7 @@
#include "third_party/blink/public/web/web_settings.h"
#include "third_party/blink/public/web/web_view.h"
#include "third_party/blink/public/web/web_view_client.h"
+#include "third_party/blink/public/web/web_widget_client.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/base/resource/resource_bundle.h"
@@ -321,7 +324,7 @@ void ComputeWebKitPrintParamsInDesiredDpi(
ConvertUnit(print_params.page_size.height(), dpi, kPointsPerInch);
// The following settings is for N-up mode.
- webkit_print_params->num_pages_per_sheet = print_params.num_pages_per_sheet;
+ webkit_print_params->pages_per_sheet = print_params.pages_per_sheet;
}
blink::WebPlugin* GetPlugin(const blink::WebLocalFrame* frame) {
@@ -554,7 +557,7 @@ void FrameReference::Reset(blink::WebLocalFrame* frame) {
if (frame) {
view_ = frame->View();
// Make sure this isn't called too early in the |frame| lifecycle... i.e.
- // calling this in WebFrameClient::BindToFrame() doesn't work.
+ // calling this in WebLocalFrameClient::BindToFrame() doesn't work.
// TODO(dcheng): It's a bit awkward that lifetime details like this leak out
// of Blink. Fixing https://crbug.com/727166 should allow this to be
// addressed.
@@ -591,7 +594,7 @@ double PrintRenderFrameHelper::GetScaleFactor(double input_scale_factor,
// static - Not anonymous so that platform implementations can use it.
void PrintRenderFrameHelper::PrintHeaderAndFooter(
- blink::WebCanvas* canvas,
+ cc::PaintCanvas* canvas,
int page_number,
int total_pages,
const blink::WebLocalFrame& source_frame,
@@ -607,13 +610,14 @@ void PrintRenderFrameHelper::PrintHeaderAndFooter(
page_layout.content_height);
blink::WebView* web_view = blink::WebView::Create(
- /* client = */ nullptr, blink::mojom::PageVisibilityState::kVisible,
- /* opener = */ nullptr);
+ /*client=*/nullptr, /*widget_client=*/nullptr,
+ blink::mojom::PageVisibilityState::kVisible,
+ /*opener=*/nullptr);
web_view->GetSettings()->SetJavaScriptEnabled(true);
- class HeaderAndFooterClient final : public blink::WebFrameClient {
+ class HeaderAndFooterClient final : public blink::WebLocalFrameClient {
public:
- // WebFrameClient:
+ // WebLocalFrameClient:
void BindToFrame(blink::WebLocalFrame* frame) override { frame_ = frame; }
void FrameDetached(DetachType detach_type) override {
frame_->FrameWidget()->Close();
@@ -668,7 +672,7 @@ float PrintRenderFrameHelper::RenderPageContent(blink::WebLocalFrame* frame,
const gfx::Rect& canvas_area,
const gfx::Rect& content_area,
double scale_factor,
- blink::WebCanvas* canvas) {
+ cc::PaintCanvas* canvas) {
cc::PaintCanvasAutoRestore auto_restore(canvas, true);
canvas->translate((content_area.x() - canvas_area.x()) / scale_factor,
(content_area.y() - canvas_area.y()) / scale_factor);
@@ -678,7 +682,8 @@ float PrintRenderFrameHelper::RenderPageContent(blink::WebLocalFrame* frame,
// Class that calls the Begin and End print functions on the frame and changes
// the size of the view temporarily to support full page printing..
class PrepareFrameAndViewForPrint : public blink::WebViewClient,
- public blink::WebFrameClient {
+ public blink::WebWidgetClient,
+ public blink::WebLocalFrameClient {
public:
PrepareFrameAndViewForPrint(const PrintMsg_Print_Params& params,
blink::WebLocalFrame* frame,
@@ -713,8 +718,9 @@ class PrepareFrameAndViewForPrint : public blink::WebViewClient,
// TODO(ojan): Remove this override and have this class use a non-null
// layerTreeView.
bool AllowsBrokenNullLayerTreeView() const override;
+ WebWidgetClient* WidgetClient() override { return this; }
- // blink::WebFrameClient:
+ // blink::WebLocalFrameClient:
blink::WebLocalFrame* CreateChildFrame(
blink::WebLocalFrame* parent,
blink::WebTreeScopeType scope,
@@ -842,9 +848,7 @@ void PrepareFrameAndViewForPrint::CopySelectionIfNeeded(
void PrepareFrameAndViewForPrint::CopySelection(
const WebPreferences& preferences) {
ResizeForPrinting();
- std::string url_str = "data:text/html;charset=utf-8,";
- url_str.append(
- net::EscapeQueryParamValue(frame()->SelectionAsMarkup().Utf8(), false));
+ std::string html = frame()->SelectionAsMarkup().Utf8();
RestoreSize();
// Create a new WebView with the same settings as the current display one.
// Except that we disable javascript (don't want any active content running
@@ -853,8 +857,9 @@ void PrepareFrameAndViewForPrint::CopySelection(
prefs.javascript_enabled = false;
blink::WebView* web_view = blink::WebView::Create(
- /* client = */ this, blink::mojom::PageVisibilityState::kVisible,
- /* opener = */ nullptr);
+ /*client=*/this, /*widget_client=*/this,
+ blink::mojom::PageVisibilityState::kVisible,
+ /*opener=*/nullptr);
owns_web_view_ = true;
content::RenderView::ApplyWebPreferences(prefs, web_view);
blink::WebLocalFrame* main_frame =
@@ -865,8 +870,8 @@ void PrepareFrameAndViewForPrint::CopySelection(
// When loading is done this will call didStopLoading() and that will do the
// actual printing.
- blink::WebURLRequest request = blink::WebURLRequest(GURL(url_str));
- frame()->LoadRequest(request);
+ frame()->LoadHTMLString(blink::WebData(html),
+ blink::WebURL(GURL(url::kAboutBlankURL)));
}
bool PrepareFrameAndViewForPrint::AllowsBrokenNullLayerTreeView() const {
@@ -1178,8 +1183,12 @@ void PrintRenderFrameHelper::OnPrintPreview(
if (print_pages_params_->params.is_first_request &&
!print_preview_context_.IsModifiable()) {
PrintHostMsg_SetOptionsFromDocument_Params options;
- if (SetOptionsFromPdfDocument(&options))
- Send(new PrintHostMsg_SetOptionsFromDocument(routing_id(), options));
+ if (SetOptionsFromPdfDocument(&options)) {
+ PrintHostMsg_PreviewIds ids(
+ print_pages_params_->params.preview_request_id,
+ print_pages_params_->params.preview_ui_id);
+ Send(new PrintHostMsg_SetOptionsFromDocument(routing_id(), options, ids));
+ }
}
is_print_ready_metafile_sent_ = false;
@@ -1257,48 +1266,19 @@ bool PrintRenderFrameHelper::CreatePreviewDocument() {
ConvertUnit(print_params.printable_area.width(), dpi, kPointsPerInch),
ConvertUnit(print_params.printable_area.height(), dpi, kPointsPerInch));
- double fit_to_page_scale_factor = 1.0f;
- if (!print_preview_context_.IsModifiable()) {
- blink::WebLocalFrame* source_frame = print_preview_context_.source_frame();
- const blink::WebNode& source_node = print_preview_context_.source_node();
- blink::WebPrintPresetOptions preset_options;
- if (source_frame->GetPrintPresetOptionsForPlugin(source_node,
- &preset_options)) {
- if (preset_options.is_page_size_uniform) {
- // Figure out if the sizes have the same orientation
- bool is_printable_area_landscape = printable_area_in_points.width() >
- printable_area_in_points.height();
- bool is_preset_landscape = preset_options.uniform_page_size.width >
- preset_options.uniform_page_size.height;
- bool rotate = is_printable_area_landscape != is_preset_landscape;
- // Match orientation for computing scaling
- double printable_width = rotate ? printable_area_in_points.height()
- : printable_area_in_points.width();
- double printable_height = rotate ? printable_area_in_points.width()
- : printable_area_in_points.height();
- double scale_width =
- printable_width /
- static_cast<double>(preset_options.uniform_page_size.width);
- double scale_height =
- printable_height /
- static_cast<double>(preset_options.uniform_page_size.height);
- fit_to_page_scale_factor = std::min(scale_width, scale_height);
- } else {
- fit_to_page_scale_factor = 0.0f;
- }
- }
- }
- int fit_to_page_scaling = static_cast<int>(100.0f * fit_to_page_scale_factor);
+ PrintHostMsg_PreviewIds ids(print_params.preview_request_id,
+ print_params.preview_ui_id);
+
// Margins: Send default page layout to browser process.
Send(new PrintHostMsg_DidGetDefaultPageLayout(
routing_id(), default_page_layout, printable_area_in_points,
- has_page_size_style));
+ has_page_size_style, ids));
PrintHostMsg_DidGetPreviewPageCount_Params params;
params.page_count = print_preview_context_.total_page_count();
- params.fit_to_page_scaling = fit_to_page_scaling;
- params.preview_request_id = print_params.preview_request_id;
- Send(new PrintHostMsg_DidGetPreviewPageCount(routing_id(), params));
+ params.fit_to_page_scaling =
+ GetFitToPageScaleFactor(printable_area_in_points);
+ Send(new PrintHostMsg_DidGetPreviewPageCount(routing_id(), params, ids));
if (CheckForCancel())
return false;
@@ -1380,14 +1360,53 @@ bool PrintRenderFrameHelper::FinalizePrintReadyDocument() {
preview_params.expected_pages_count =
print_preview_context_.total_page_count();
preview_params.modifiable = print_preview_context_.IsModifiable();
- preview_params.preview_request_id =
- print_pages_params_->params.preview_request_id;
+
+ PrintHostMsg_PreviewIds ids(print_pages_params_->params.preview_request_id,
+ print_pages_params_->params.preview_ui_id);
is_print_ready_metafile_sent_ = true;
- Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(), preview_params));
+ Send(new PrintHostMsg_MetafileReadyForPrinting(routing_id(), preview_params,
+ ids));
return true;
}
+
+int PrintRenderFrameHelper::GetFitToPageScaleFactor(
+ const gfx::Rect& printable_area_in_points) {
+ if (print_preview_context_.IsModifiable())
+ return 100;
+
+ blink::WebLocalFrame* frame = print_preview_context_.source_frame();
+ const blink::WebNode& node = print_preview_context_.source_node();
+ blink::WebPrintPresetOptions preset_options;
+ if (!frame->GetPrintPresetOptionsForPlugin(node, &preset_options))
+ return 100;
+
+ if (!preset_options.is_page_size_uniform)
+ return 0;
+
+ // Ensure we do not divide by 0 later.
+ const auto& uniform_page_size = preset_options.uniform_page_size;
+ if (uniform_page_size.width == 0 || uniform_page_size.height == 0)
+ return 0;
+
+ // Figure out if the sizes have the same orientation
+ bool is_printable_area_landscape =
+ printable_area_in_points.width() > printable_area_in_points.height();
+ bool is_preset_landscape = uniform_page_size.width > uniform_page_size.height;
+ bool rotate = is_printable_area_landscape != is_preset_landscape;
+ // Match orientation for computing scaling
+ double printable_width = rotate ? printable_area_in_points.height()
+ : printable_area_in_points.width();
+ double printable_height = rotate ? printable_area_in_points.width()
+ : printable_area_in_points.height();
+
+ double scale_width =
+ printable_width / static_cast<double>(uniform_page_size.width);
+ double scale_height =
+ printable_height / static_cast<double>(uniform_page_size.height);
+ return static_cast<int>(100.0f * std::min(scale_width, scale_height));
+}
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
void PrintRenderFrameHelper::OnPrintingDone(bool success) {
@@ -1590,6 +1609,13 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame,
void PrintRenderFrameHelper::DidFinishPrinting(PrintingResult result) {
int cookie =
print_pages_params_ ? print_pages_params_->params.document_cookie : 0;
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+ PrintHostMsg_PreviewIds ids;
+ if (print_pages_params_) {
+ ids.ui_id = print_pages_params_->params.preview_ui_id;
+ ids.request_id = print_pages_params_->params.preview_request_id;
+ }
+#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
switch (result) {
case OK:
break;
@@ -1609,16 +1635,17 @@ void PrintRenderFrameHelper::DidFinishPrinting(PrintingResult result) {
if (!is_print_ready_metafile_sent_) {
if (notify_browser_of_print_failure_) {
LOG(ERROR) << "CreatePreviewDocument failed";
- Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), cookie));
+ Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), cookie, ids));
} else {
- Send(new PrintHostMsg_PrintPreviewCancelled(routing_id(), cookie));
+ Send(new PrintHostMsg_PrintPreviewCancelled(routing_id(), cookie,
+ ids));
}
}
print_preview_context_.Failed(notify_browser_of_print_failure_);
break;
case INVALID_SETTINGS:
Send(new PrintHostMsg_PrintPreviewInvalidPrinterSettings(routing_id(),
- cookie));
+ cookie, ids));
print_preview_context_.Failed(false);
break;
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
@@ -2135,9 +2162,11 @@ void PrintRenderFrameHelper::RequestPrintPreview(PrintPreviewRequestType type) {
bool PrintRenderFrameHelper::CheckForCancel() {
const PrintMsg_Print_Params& print_params = print_pages_params_->params;
bool cancel = false;
- Send(new PrintHostMsg_CheckForCancel(routing_id(), print_params.preview_ui_id,
- print_params.preview_request_id,
- &cancel));
+ Send(new PrintHostMsg_CheckForCancel(
+ routing_id(),
+ PrintHostMsg_PreviewIds(print_params.preview_request_id,
+ print_params.preview_ui_id),
+ &cancel));
if (cancel)
notify_browser_of_print_failure_ = false;
return cancel;
@@ -2159,12 +2188,13 @@ bool PrintRenderFrameHelper::PreviewPageRendered(
}
preview_page_params.page_number = page_number;
- preview_page_params.preview_request_id =
- print_pages_params_->params.preview_request_id;
preview_page_params.document_cookie =
print_pages_params_->params.document_cookie;
- Send(new PrintHostMsg_DidPreviewPage(routing_id(), preview_page_params));
+ PrintHostMsg_PreviewIds ids(print_pages_params_->params.preview_request_id,
+ print_pages_params_->params.preview_ui_id);
+
+ Send(new PrintHostMsg_DidPreviewPage(routing_id(), preview_page_params, ids));
return true;
}
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
diff --git a/chromium/components/printing/renderer/print_render_frame_helper.h b/chromium/components/printing/renderer/print_render_frame_helper.h
index 46da03048df..6e2d7e1467b 100644
--- a/chromium/components/printing/renderer/print_render_frame_helper.h
+++ b/chromium/components/printing/renderer/print_render_frame_helper.h
@@ -15,11 +15,11 @@
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "build/build_config.h"
+#include "cc/paint/paint_canvas.h"
#include "content/public/renderer/render_frame_observer.h"
#include "content/public/renderer/render_frame_observer_tracker.h"
#include "printing/buildflags/buildflags.h"
#include "printing/pdf_metafile_skia.h"
-#include "third_party/blink/public/platform/web_canvas.h"
#include "third_party/blink/public/web/web_node.h"
#include "third_party/blink/public/web/web_print_params.h"
#include "ui/gfx/geometry/size.h"
@@ -223,6 +223,9 @@ class PrintRenderFrameHelper
// Finalize the print ready preview document.
bool FinalizePrintReadyDocument();
+
+ // Helper method to calculate the scale factor for fit-to-page.
+ int GetFitToPageScaleFactor(const gfx::Rect& printable_area_in_points);
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
// Enable/Disable printing.
@@ -303,7 +306,7 @@ class PrintRenderFrameHelper
const gfx::Rect& canvas_area,
const gfx::Rect& content_area,
double scale_factor,
- blink::WebCanvas* canvas);
+ cc::PaintCanvas* canvas);
// Helper methods -----------------------------------------------------------
@@ -331,7 +334,7 @@ class PrintRenderFrameHelper
// 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(blink::WebCanvas* canvas,
+ static void PrintHeaderAndFooter(cc::PaintCanvas* canvas,
int page_number,
int total_pages,
const blink::WebLocalFrame& source_frame,
diff --git a/chromium/components/printing/renderer/print_render_frame_helper_mac.mm b/chromium/components/printing/renderer/print_render_frame_helper_mac.mm
index 9e6eb4768aa..b4b23ad574a 100644
--- a/chromium/components/printing/renderer/print_render_frame_helper_mac.mm
+++ b/chromium/components/printing/renderer/print_render_frame_helper_mac.mm
@@ -11,11 +11,11 @@
#include "base/logging.h"
#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/metrics/histogram.h"
+#include "cc/paint/paint_canvas.h"
#include "components/printing/common/print_messages.h"
#include "printing/buildflags/buildflags.h"
#include "printing/metafile_skia_wrapper.h"
#include "printing/page_size_margins.h"
-#include "third_party/blink/public/platform/web_canvas.h"
#include "third_party/blink/public/web/web_local_frame.h"
namespace printing {
diff --git a/chromium/components/profile_metrics/OWNERS b/chromium/components/profile_metrics/OWNERS
index eccc04a101f..edf3fbf8484 100644
--- a/chromium/components/profile_metrics/OWNERS
+++ b/chromium/components/profile_metrics/OWNERS
@@ -1,4 +1,3 @@
anthonyvd@chromium.org
-erg@chromium.org
# COMPONENT: UI>Browser>Profiles
diff --git a/chromium/components/quirks/BUILD.gn b/chromium/components/quirks/BUILD.gn
index 0a9a24f2628..36587f8da90 100644
--- a/chromium/components/quirks/BUILD.gn
+++ b/chromium/components/quirks/BUILD.gn
@@ -19,7 +19,7 @@ source_set("quirks") {
"//base",
"//components/prefs",
"//components/version_info",
- "//net",
+ "//services/network/public/cpp",
"//url",
]
}
diff --git a/chromium/components/quirks/DEPS b/chromium/components/quirks/DEPS
index d4865093781..7f14ac6c621 100644
--- a/chromium/components/quirks/DEPS
+++ b/chromium/components/quirks/DEPS
@@ -1,5 +1,8 @@
include_rules = [
"+components/prefs",
"+components/version_info",
- "+net",
+ "+net/base",
+ "+net/http",
+ "+net/traffic_annotation",
+ "+services/network/public/cpp",
]
diff --git a/chromium/components/quirks/quirks_client.cc b/chromium/components/quirks/quirks_client.cc
index 13eea620eab..ce0aa65ef8e 100644
--- a/chromium/components/quirks/quirks_client.cc
+++ b/chromium/components/quirks/quirks_client.cc
@@ -15,8 +15,7 @@
#include "net/base/escape.h"
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/simple_url_loader.h"
namespace quirks {
@@ -86,31 +85,56 @@ void QuirksClient::StartDownload() {
url += "key=" + manager_->delegate()->GetApiKey();
- url_fetcher_ = manager_->CreateURLFetcher(GURL(url), this);
- url_fetcher_->SetRequestContext(manager_->url_context_getter());
- url_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
- net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SEND_AUTH_DATA);
- url_fetcher_->Start();
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = GURL(url);
+ resource_request->load_flags =
+ net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
+ net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SEND_AUTH_DATA;
+
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("quirks_display_fetcher", R"(
+ semantics {
+ sender: "Quirks"
+ description: "Download custom display calibration file."
+ trigger:
+ "Chrome OS attempts to download monitor calibration files on"
+ "first device login, and then once every 30 days."
+ data: "ICC files to calibrate and improve the quality of a display."
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: NO
+ chrome_policy {
+ DeviceQuirksDownloadEnabled {
+ DeviceQuirksDownloadEnabled: false
+ }
+ }
+ }
+ )");
+
+ url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
+ traffic_annotation);
+ url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ manager_->url_loader_factory(),
+ base::BindOnce(&QuirksClient::OnDownloadComplete,
+ base::Unretained(this)));
}
-void QuirksClient::OnURLFetchComplete(const net::URLFetcher* source) {
+void QuirksClient::OnDownloadComplete(
+ std::unique_ptr<std::string> response_body) {
DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK_EQ(url_fetcher_.get(), source);
- const int HTTP_INTERNAL_SERVER_ERROR_LAST =
- net::HTTP_INTERNAL_SERVER_ERROR + 99;
- const net::URLRequestStatus status = source->GetStatus();
- const int response_code = source->GetResponseCode();
- const bool server_error = !status.is_success() ||
- (response_code >= net::HTTP_INTERNAL_SERVER_ERROR &&
- response_code <= HTTP_INTERNAL_SERVER_ERROR_LAST);
+ // Take ownership of the loader in this scope.
+ std::unique_ptr<network::SimpleURLLoader> url_loader = std::move(url_loader_);
+
+ int response_code = 0;
+ if (url_loader->ResponseInfo() && url_loader->ResponseInfo()->headers)
+ response_code = url_loader->ResponseInfo()->headers->response_code();
VLOG(2) << "QuirksClient::OnURLFetchComplete():"
- << " status=" << status.status()
- << ", response_code=" << response_code
- << ", server_error=" << server_error;
+ << " net_error=" << url_loader->NetError()
+ << ", response_code=" << response_code;
if (response_code == net::HTTP_NOT_FOUND) {
VLOG(1) << IdToFileName(product_id_) << " not found on Quirks server.";
@@ -118,25 +142,23 @@ void QuirksClient::OnURLFetchComplete(const net::URLFetcher* source) {
return;
}
- if (server_error) {
+ if (url_loader->NetError() != net::OK) {
if (backoff_entry_.failure_count() >= kMaxServerFailures) {
// After 10 retires (5+ hours), give up, and try again in a month.
VLOG(1) << "Too many retries; Quirks Client shutting down.";
Shutdown(false);
return;
}
- url_fetcher_.reset();
Retry();
return;
}
- std::string response;
- url_fetcher_->GetResponseAsString(&response);
- VLOG(2) << "Quirks server response:\n" << response;
+ DCHECK(response_body); // Guaranteed to be valid if NetError() is net::OK.
+ VLOG(2) << "Quirks server response:\n" << *response_body;
// Parse response data and write to file on file thread.
std::string data;
- if (!ParseResult(response, &data)) {
+ if (!ParseResult(*response_body, &data)) {
Shutdown(false);
return;
}
diff --git a/chromium/components/quirks/quirks_client.h b/chromium/components/quirks/quirks_client.h
index e993e7b17c6..bc36efb2b9e 100644
--- a/chromium/components/quirks/quirks_client.h
+++ b/chromium/components/quirks/quirks_client.h
@@ -12,7 +12,10 @@
#include "base/threading/thread_checker.h"
#include "base/timer/timer.h"
#include "net/base/backoff_entry.h"
-#include "net/url_request/url_fetcher_delegate.h"
+
+namespace network {
+class SimpleURLLoader;
+}
namespace quirks {
@@ -23,21 +26,20 @@ using RequestFinishedCallback =
base::Callback<void(const base::FilePath&, bool)>;
// Handles downloading icc and other display data files from Quirks Server.
-class QuirksClient : public net::URLFetcherDelegate {
+class QuirksClient {
public:
QuirksClient(int64_t product_id,
const std::string& display_name,
const RequestFinishedCallback& on_request_finished,
QuirksManager* manager);
- ~QuirksClient() override;
+ ~QuirksClient();
void StartDownload();
int64_t product_id() const { return product_id_; }
private:
- // net::URLFetcherDelegate:
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ void OnDownloadComplete(std::unique_ptr<std::string> response_body);
// Send callback and tell manager to delete |this|.
void Shutdown(bool success);
@@ -66,8 +68,8 @@ class QuirksClient : public net::URLFetcherDelegate {
// The class is expected to run on UI thread.
base::ThreadChecker thread_checker_;
- // This fetcher is used to download icc file.
- std::unique_ptr<net::URLFetcher> url_fetcher_;
+ // This loader is used to download icc file.
+ std::unique_ptr<network::SimpleURLLoader> url_loader_;
// Pending retry.
base::OneShotTimer request_scheduled_;
diff --git a/chromium/components/quirks/quirks_manager.cc b/chromium/components/quirks/quirks_manager.cc
index 7b927d5f67d..496487b204c 100644
--- a/chromium/components/quirks/quirks_manager.cc
+++ b/chromium/components/quirks/quirks_manager.cc
@@ -20,8 +20,6 @@
#include "components/quirks/pref_names.h"
#include "components/quirks/quirks_client.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.h"
#include "url/gurl.h"
namespace quirks {
@@ -60,12 +58,12 @@ std::string IdToFileName(int64_t product_id) {
QuirksManager::QuirksManager(
std::unique_ptr<Delegate> delegate,
PrefService* local_state,
- scoped_refptr<net::URLRequestContextGetter> url_context_getter)
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: waiting_for_login_(true),
delegate_(std::move(delegate)),
task_runner_(base::CreateTaskRunnerWithTraits({base::MayBlock()})),
local_state_(local_state),
- url_context_getter_(url_context_getter),
+ url_loader_factory_(std::move(url_loader_factory)),
weak_ptr_factory_(this) {}
QuirksManager::~QuirksManager() {
@@ -77,9 +75,9 @@ QuirksManager::~QuirksManager() {
void QuirksManager::Initialize(
std::unique_ptr<Delegate> delegate,
PrefService* local_state,
- scoped_refptr<net::URLRequestContextGetter> url_context_getter) {
- manager_ =
- new QuirksManager(std::move(delegate), local_state, url_context_getter);
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+ manager_ = new QuirksManager(std::move(delegate), local_state,
+ std::move(url_loader_factory));
}
// static
@@ -152,37 +150,6 @@ void QuirksManager::ClientFinished(QuirksClient* client) {
clients_.erase(it);
}
-std::unique_ptr<net::URLFetcher> QuirksManager::CreateURLFetcher(
- const GURL& url,
- net::URLFetcherDelegate* delegate) {
- if (!fake_quirks_fetcher_creator_.is_null())
- return fake_quirks_fetcher_creator_.Run(url, delegate);
-
- net::NetworkTrafficAnnotationTag traffic_annotation =
- net::DefineNetworkTrafficAnnotation("quirks_display_fetcher", R"(
- semantics {
- sender: "Quirks"
- description: "Download custom display calibration file."
- trigger:
- "Chrome OS attempts to download monitor calibration files on"
- "first device login, and then once every 30 days."
- data: "ICC files to calibrate and improve the quality of a display."
- destination: GOOGLE_OWNED_SERVICE
- }
- policy {
- cookies_allowed: NO
- chrome_policy {
- DeviceQuirksDownloadEnabled {
- DeviceQuirksDownloadEnabled: false
- }
- }
- }
- )");
-
- return net::URLFetcher::Create(url, net::URLFetcher::GET, delegate,
- traffic_annotation);
-}
-
void QuirksManager::OnIccFilePathRequestCompleted(
int64_t product_id,
const std::string& display_name,
diff --git a/chromium/components/quirks/quirks_manager.h b/chromium/components/quirks/quirks_manager.h
index f1ea85de4fc..aafd3f455d1 100644
--- a/chromium/components/quirks/quirks_manager.h
+++ b/chromium/components/quirks/quirks_manager.h
@@ -15,8 +15,8 @@
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "components/quirks/quirks_export.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
-class GURL;
class PrefRegistrySimple;
class PrefService;
@@ -24,12 +24,6 @@ namespace base {
class TaskRunner;
}
-namespace net {
-class URLFetcher;
-class URLFetcherDelegate;
-class URLRequestContextGetter;
-}
-
namespace quirks {
class QuirksClient;
@@ -52,11 +46,6 @@ QUIRKS_EXPORT std::string IdToFileName(int64_t product_id);
// blocking pool, etc), and owns clients and manages their life cycles.
class QUIRKS_EXPORT QuirksManager {
public:
- // Passed function to create a URLFetcher for tests.
- // Same parameters as URLFetcher::Create().
- using FakeQuirksFetcherCreator = base::Callback<
- std::unique_ptr<net::URLFetcher>(const GURL&, net::URLFetcherDelegate*)>;
-
// Delegate class, so implementation can access browser functionality.
class Delegate {
public:
@@ -79,7 +68,7 @@ class QUIRKS_EXPORT QuirksManager {
static void Initialize(
std::unique_ptr<Delegate> delegate,
PrefService* local_state,
- scoped_refptr<net::URLRequestContextGetter> url_context_getter);
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
static void Shutdown();
static QuirksManager* Get();
@@ -96,29 +85,25 @@ class QUIRKS_EXPORT QuirksManager {
void ClientFinished(QuirksClient* client);
- // Creates a real URLFetcher for OS, and a fake one for tests.
- std::unique_ptr<net::URLFetcher> CreateURLFetcher(
- const GURL& url,
- net::URLFetcherDelegate* delegate);
-
Delegate* delegate() { return delegate_.get(); }
base::TaskRunner* task_runner() { return task_runner_.get(); }
- net::URLRequestContextGetter* url_context_getter() {
- return url_context_getter_.get();
+ network::mojom::URLLoaderFactory* url_loader_factory() {
+ return url_loader_factory_.get();
}
protected:
friend class QuirksBrowserTest;
- void SetFakeQuirksFetcherCreatorForTests(
- const FakeQuirksFetcherCreator& creator) {
- fake_quirks_fetcher_creator_ = creator;
+ void SetURLLoaderFactoryForTests(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+ url_loader_factory_ = std::move(url_loader_factory);
}
private:
- QuirksManager(std::unique_ptr<Delegate> delegate,
- PrefService* local_state,
- scoped_refptr<net::URLRequestContextGetter> url_context_getter);
+ QuirksManager(
+ std::unique_ptr<Delegate> delegate,
+ PrefService* local_state,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
~QuirksManager();
// Callback after checking for existing icc file; proceed if not found.
@@ -147,9 +132,7 @@ class QUIRKS_EXPORT QuirksManager {
std::unique_ptr<Delegate> delegate_; // Impl runs from chrome/browser.
scoped_refptr<base::TaskRunner> task_runner_;
PrefService* local_state_; // For local prefs.
- scoped_refptr<net::URLRequestContextGetter> url_context_getter_;
-
- FakeQuirksFetcherCreator fake_quirks_fetcher_creator_; // For tests.
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// Factory for callbacks.
base::WeakPtrFactory<QuirksManager> weak_ptr_factory_;
diff --git a/chromium/components/rappor/rappor_prefs_unittest.cc b/chromium/components/rappor/rappor_prefs_unittest.cc
index 29e5d614d33..af9fe3ec1ba 100644
--- a/chromium/components/rappor/rappor_prefs_unittest.cc
+++ b/chromium/components/rappor/rappor_prefs_unittest.cc
@@ -8,7 +8,7 @@
#include "base/base64.h"
#include "base/macros.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/prefs/testing_pref_service.h"
#include "components/rappor/byte_vector_utils.h"
#include "components/rappor/proto/rappor_metric.pb.h"
diff --git a/chromium/components/reading_list/core/reading_list_model_unittest.cc b/chromium/components/reading_list/core/reading_list_model_unittest.cc
index 8f619c77de9..01c516e4b6a 100644
--- a/chromium/components/reading_list/core/reading_list_model_unittest.cc
+++ b/chromium/components/reading_list/core/reading_list_model_unittest.cc
@@ -133,7 +133,7 @@ class TestReadingListStorage : public ReadingListModelStorage {
return;
}
- void GetAllData(DataCallback callback) override {
+ void GetAllDataForDebugging(DataCallback callback) override {
NOTREACHED();
return;
}
diff --git a/chromium/components/reading_list/core/reading_list_store.cc b/chromium/components/reading_list/core/reading_list_store.cc
index e8fad377613..0ce991f32cf 100644
--- a/chromium/components/reading_list/core/reading_list_store.cc
+++ b/chromium/components/reading_list/core/reading_list_store.cc
@@ -9,7 +9,6 @@
#include "base/bind.h"
#include "base/logging.h"
-#include "base/memory/ptr_util.h"
#include "base/time/clock.h"
#include "components/reading_list/core/proto/reading_list.pb.h"
#include "components/reading_list/core/reading_list_model_impl.h"
@@ -25,7 +24,8 @@ ReadingListStore::ReadingListStore(
std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor)
: ReadingListModelStorage(std::move(change_processor)),
create_store_callback_(std::move(create_store_callback)),
- pending_transaction_count_(0) {}
+ pending_transaction_count_(0),
+ weak_ptr_factory_(this) {}
ReadingListStore::~ReadingListStore() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -42,13 +42,12 @@ void ReadingListStore::SetReadingListModel(ReadingListModel* model,
std::move(create_store_callback_)
.Run(syncer::READING_LIST,
base::BindOnce(&ReadingListStore::OnStoreCreated,
- base::AsWeakPtr(this)));
+ weak_ptr_factory_.GetWeakPtr()));
}
std::unique_ptr<ReadingListModelStorage::ScopedBatchUpdate>
ReadingListStore::EnsureBatchCreated() {
- return base::WrapUnique<ReadingListModelStorage::ScopedBatchUpdate>(
- new ScopedBatchUpdate(this));
+ return std::make_unique<ScopedBatchUpdate>(this);
}
ReadingListStore::ScopedBatchUpdate::ScopedBatchUpdate(ReadingListStore* store)
@@ -72,9 +71,9 @@ void ReadingListStore::CommitTransaction() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pending_transaction_count_--;
if (pending_transaction_count_ == 0) {
- store_->CommitWriteBatch(
- std::move(batch_),
- base::Bind(&ReadingListStore::OnDatabaseSave, base::AsWeakPtr(this)));
+ store_->CommitWriteBatch(std::move(batch_),
+ base::Bind(&ReadingListStore::OnDatabaseSave,
+ weak_ptr_factory_.GetWeakPtr()));
batch_.reset();
}
}
@@ -145,8 +144,8 @@ void ReadingListStore::OnDatabaseLoad(
delegate_->StoreLoaded(std::move(loaded_entries));
- store_->ReadAllMetadata(
- base::Bind(&ReadingListStore::OnReadAllMetadata, base::AsWeakPtr(this)));
+ store_->ReadAllMetadata(base::Bind(&ReadingListStore::OnReadAllMetadata,
+ weak_ptr_factory_.GetWeakPtr()));
}
void ReadingListStore::OnReadAllMetadata(
@@ -174,8 +173,8 @@ void ReadingListStore::OnStoreCreated(
return;
}
store_ = std::move(store);
- store_->ReadAllData(
- base::Bind(&ReadingListStore::OnDatabaseLoad, base::AsWeakPtr(this)));
+ store_->ReadAllData(base::Bind(&ReadingListStore::OnDatabaseLoad,
+ weak_ptr_factory_.GetWeakPtr()));
return;
}
@@ -362,7 +361,7 @@ void ReadingListStore::GetData(StorageKeyList storage_keys,
std::move(callback).Run(std::move(batch));
}
-void ReadingListStore::GetAllData(DataCallback callback) {
+void ReadingListStore::GetAllDataForDebugging(DataCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto batch = std::make_unique<syncer::MutableDataBatch>();
diff --git a/chromium/components/reading_list/core/reading_list_store.h b/chromium/components/reading_list/core/reading_list_store.h
index aae23830c7e..9e08330d075 100644
--- a/chromium/components/reading_list/core/reading_list_store.h
+++ b/chromium/components/reading_list/core/reading_list_store.h
@@ -8,6 +8,7 @@
#include <memory>
#include <string>
+#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "components/reading_list/core/reading_list_model_storage.h"
#include "components/reading_list/core/reading_list_store_delegate.h"
@@ -106,7 +107,7 @@ class ReadingListStore : public ReadingListModelStorage {
void GetData(StorageKeyList storage_keys, DataCallback callback) override;
// Asynchronously retrieve all of the local sync data.
- void GetAllData(DataCallback callback) override;
+ void GetAllDataForDebugging(DataCallback callback) override;
// Get or generate a client tag for |entity_data|. This must be the same tag
// that was/would have been generated in the SyncableService/Directory world
@@ -167,6 +168,8 @@ class ReadingListStore : public ReadingListModelStorage {
SEQUENCE_CHECKER(sequence_checker_);
+ base::WeakPtrFactory<ReadingListStore> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(ReadingListStore);
};
diff --git a/chromium/components/reading_list/core/reading_list_store_unittest.cc b/chromium/components/reading_list/core/reading_list_store_unittest.cc
index af55638eadb..fd5a3470cb4 100644
--- a/chromium/components/reading_list/core/reading_list_store_unittest.cc
+++ b/chromium/components/reading_list/core/reading_list_store_unittest.cc
@@ -13,12 +13,37 @@
#include "base/run_loop.h"
#include "base/test/simple_test_clock.h"
#include "components/reading_list/core/reading_list_model_impl.h"
-#include "components/sync/model/fake_model_type_change_processor.h"
+#include "components/sync/model/mock_model_type_change_processor.h"
#include "components/sync/model/model_type_store_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
+using testing::_;
+
+MATCHER_P3(MatchesSpecifics,
+ expected_title,
+ expected_url,
+ expected_status,
+ "") {
+ const sync_pb::ReadingListSpecifics& specifics =
+ arg->specifics.reading_list();
+ if (specifics.title() != expected_title) {
+ *result_listener << "which has title \"" << specifics.title();
+ return false;
+ }
+ if (specifics.url() != expected_url) {
+ *result_listener << "which has URL " << specifics.url();
+ return false;
+ }
+ if (specifics.status() != expected_status) {
+ *result_listener << "which has unexpected status";
+ return false;
+ }
+ return true;
+}
+
// Tests that the transition from |entryA| to |entryB| is possible (|possible|
// is true) or not.
void ExpectAB(const sync_pb::ReadingListSpecifics& entryA,
@@ -62,82 +87,33 @@ class FakeModelTypeChangeProcessorObserver {
syncer::MetadataChangeList* metadata_change_list) = 0;
};
-class TestModelTypeChangeProcessor
- : public syncer::FakeModelTypeChangeProcessor {
- public:
- void SetObserver(FakeModelTypeChangeProcessorObserver* observer) {
- observer_ = observer;
- }
-
- void Put(const std::string& client_tag,
- std::unique_ptr<syncer::EntityData> entity_data,
- syncer::MetadataChangeList* metadata_change_list) override {
- observer_->Put(client_tag, std::move(entity_data), metadata_change_list);
- }
-
- void Delete(const std::string& client_tag,
- syncer::MetadataChangeList* metadata_change_list) override {
- observer_->Delete(client_tag, metadata_change_list);
- }
-
- private:
- FakeModelTypeChangeProcessorObserver* observer_;
-};
-
class ReadingListStoreTest : public testing::Test,
- public FakeModelTypeChangeProcessorObserver,
public ReadingListStoreDelegate {
protected:
ReadingListStoreTest()
: store_(syncer::ModelTypeStoreTestUtil::CreateInMemoryStoreForTest()) {
+ ON_CALL(processor_, IsTrackingMetadata())
+ .WillByDefault(testing::Return(true));
ClearState();
reading_list_store_ = std::make_unique<ReadingListStore>(
base::BindOnce(&syncer::ModelTypeStoreTestUtil::MoveStoreToCallback,
std::move(store_)),
- CreateModelTypeChangeProcessor());
+ processor_.CreateForwardingProcessor());
model_ = std::make_unique<ReadingListModelImpl>(nullptr, nullptr, &clock_);
reading_list_store_->SetReadingListModel(model_.get(), this, &clock_);
base::RunLoop().RunUntilIdle();
}
- std::unique_ptr<syncer::ModelTypeChangeProcessor>
- CreateModelTypeChangeProcessor() {
- auto processor = std::make_unique<TestModelTypeChangeProcessor>();
- processor->SetObserver(this);
- return processor;
- }
-
- void Put(const std::string& storage_key,
- std::unique_ptr<syncer::EntityData> entity_data,
- syncer::MetadataChangeList* metadata_changes) override {
- put_multimap_.insert(std::make_pair(storage_key, std::move(entity_data)));
- put_called_++;
- }
-
- void Delete(const std::string& storage_key,
- syncer::MetadataChangeList* metadata_changes) override {
- delete_set_.insert(storage_key);
- delete_called_++;
- }
-
- void AssertCounts(int put_called,
- int delete_called,
- int sync_add_called,
+ void AssertCounts(int sync_add_called,
int sync_remove_called,
int sync_merge_called) {
- EXPECT_EQ(put_called, put_called_);
- EXPECT_EQ(delete_called, delete_called_);
EXPECT_EQ(sync_add_called, sync_add_called_);
EXPECT_EQ(sync_remove_called, sync_remove_called_);
EXPECT_EQ(sync_merge_called, sync_merge_called_);
}
void ClearState() {
- delete_called_ = 0;
- put_called_ = 0;
- delete_set_.clear();
- put_multimap_.clear();
sync_add_called_ = 0;
sync_remove_called_ = 0;
sync_merge_called_ = 0;
@@ -170,17 +146,15 @@ class ReadingListStoreTest : public testing::Test,
// In memory model type store needs a MessageLoop.
base::MessageLoop message_loop_;
+ testing::NiceMock<syncer::MockModelTypeChangeProcessor> processor_;
std::unique_ptr<syncer::ModelTypeStore> store_;
std::unique_ptr<ReadingListModelImpl> model_;
base::SimpleTestClock clock_;
std::unique_ptr<ReadingListStore> reading_list_store_;
- int put_called_;
- int delete_called_;
+
int sync_add_called_;
int sync_remove_called_;
int sync_merge_called_;
- std::map<std::string, std::unique_ptr<syncer::EntityData>> put_multimap_;
- std::set<std::string> delete_set_;
std::map<std::string, bool> sync_added_;
std::set<std::string> sync_removed_;
std::map<std::string, bool> sync_merged_;
@@ -195,30 +169,30 @@ TEST_F(ReadingListStoreTest, SaveOneRead) {
AdvanceAndGetTime(&clock_));
entry.SetRead(true, AdvanceAndGetTime(&clock_));
AdvanceAndGetTime(&clock_);
+ EXPECT_CALL(processor_,
+ Put("http://read.example.com/",
+ MatchesSpecifics("read title", "http://read.example.com/",
+ sync_pb::ReadingListSpecifics::READ),
+ _));
reading_list_store_->SaveEntry(entry);
- AssertCounts(1, 0, 0, 0, 0);
- syncer::EntityData* data = put_multimap_["http://read.example.com/"].get();
- const sync_pb::ReadingListSpecifics& specifics =
- data->specifics.reading_list();
- EXPECT_EQ(specifics.title(), "read title");
- EXPECT_EQ(specifics.url(), "http://read.example.com/");
- EXPECT_EQ(specifics.status(), sync_pb::ReadingListSpecifics::READ);
+ AssertCounts(0, 0, 0);
}
TEST_F(ReadingListStoreTest, SaveOneUnread) {
ReadingListEntry entry(GURL("http://unread.example.com/"), "unread title",
AdvanceAndGetTime(&clock_));
+ EXPECT_CALL(processor_,
+ Put("http://unread.example.com/",
+ MatchesSpecifics("unread title", "http://unread.example.com/",
+ sync_pb::ReadingListSpecifics::UNSEEN),
+ _));
reading_list_store_->SaveEntry(entry);
- AssertCounts(1, 0, 0, 0, 0);
- syncer::EntityData* data = put_multimap_["http://unread.example.com/"].get();
- const sync_pb::ReadingListSpecifics& specifics =
- data->specifics.reading_list();
- EXPECT_EQ(specifics.title(), "unread title");
- EXPECT_EQ(specifics.url(), "http://unread.example.com/");
- EXPECT_EQ(specifics.status(), sync_pb::ReadingListSpecifics::UNSEEN);
+ AssertCounts(0, 0, 0);
}
TEST_F(ReadingListStoreTest, SyncMergeOneEntry) {
+ EXPECT_CALL(processor_, Put(_, _, _)).Times(0);
+
syncer::EntityChangeList remote_input;
ReadingListEntry entry(GURL("http://read.example.com/"), "read title",
AdvanceAndGetTime(&clock_));
@@ -237,13 +211,15 @@ TEST_F(ReadingListStoreTest, SyncMergeOneEntry) {
reading_list_store_->CreateMetadataChangeList());
auto error = reading_list_store_->MergeSyncData(std::move(metadata_changes),
remote_input);
- AssertCounts(0, 0, 1, 0, 0);
+ AssertCounts(1, 0, 0);
EXPECT_EQ(sync_added_.size(), 1u);
EXPECT_EQ(sync_added_.count("http://read.example.com/"), 1u);
EXPECT_EQ(sync_added_["http://read.example.com/"], true);
}
TEST_F(ReadingListStoreTest, ApplySyncChangesOneAdd) {
+ EXPECT_CALL(processor_, Put(_, _, _)).Times(0);
+
ReadingListEntry entry(GURL("http://read.example.com/"), "read title",
AdvanceAndGetTime(&clock_));
entry.SetRead(true, AdvanceAndGetTime(&clock_));
@@ -259,7 +235,7 @@ TEST_F(ReadingListStoreTest, ApplySyncChangesOneAdd) {
"http://read.example.com/", data.PassToPtr()));
auto error = reading_list_store_->ApplySyncChanges(
reading_list_store_->CreateMetadataChangeList(), add_changes);
- AssertCounts(0, 0, 1, 0, 0);
+ AssertCounts(1, 0, 0);
EXPECT_EQ(sync_added_.size(), 1u);
EXPECT_EQ(sync_added_.count("http://read.example.com/"), 1u);
EXPECT_EQ(sync_added_["http://read.example.com/"], true);
@@ -279,12 +255,14 @@ TEST_F(ReadingListStoreTest, ApplySyncChangesOneMerge) {
data.client_tag_hash = "http://unread.example.com/";
*data.specifics.mutable_reading_list() = *specifics;
+ EXPECT_CALL(processor_, Put("http://unread.example.com/", _, _));
+
syncer::EntityChangeList add_changes;
add_changes.push_back(syncer::EntityChange::CreateAdd(
"http://unread.example.com/", data.PassToPtr()));
auto error = reading_list_store_->ApplySyncChanges(
reading_list_store_->CreateMetadataChangeList(), add_changes);
- AssertCounts(1, 0, 0, 0, 1);
+ AssertCounts(0, 0, 1);
EXPECT_EQ(sync_merged_.size(), 1u);
EXPECT_EQ(sync_merged_.count("http://unread.example.com/"), 1u);
EXPECT_EQ(sync_merged_["http://unread.example.com/"], true);
@@ -299,7 +277,7 @@ TEST_F(ReadingListStoreTest, ApplySyncChangesOneIgnored) {
AdvanceAndGetTime(&clock_);
model_->AddEntry(GURL("http://unread.example.com/"), "new unread title",
reading_list::ADDED_VIA_CURRENT_APP);
- AssertCounts(0, 0, 0, 0, 0);
+ AssertCounts(0, 0, 0);
std::unique_ptr<sync_pb::ReadingListSpecifics> specifics =
old_entry.AsReadingListSpecifics();
@@ -307,12 +285,14 @@ TEST_F(ReadingListStoreTest, ApplySyncChangesOneIgnored) {
data.client_tag_hash = "http://unread.example.com/";
*data.specifics.mutable_reading_list() = *specifics;
+ EXPECT_CALL(processor_, Put("http://unread.example.com/", _, _));
+
syncer::EntityChangeList add_changes;
add_changes.push_back(syncer::EntityChange::CreateAdd(
"http://unread.example.com/", data.PassToPtr()));
auto error = reading_list_store_->ApplySyncChanges(
reading_list_store_->CreateMetadataChangeList(), add_changes);
- AssertCounts(1, 0, 0, 0, 1);
+ AssertCounts(0, 0, 1);
EXPECT_EQ(sync_merged_.size(), 1u);
}
@@ -322,7 +302,7 @@ TEST_F(ReadingListStoreTest, ApplySyncChangesOneRemove) {
syncer::EntityChange::CreateDelete("http://read.example.com/"));
auto error = reading_list_store_->ApplySyncChanges(
reading_list_store_->CreateMetadataChangeList(), delete_changes);
- AssertCounts(0, 0, 0, 1, 0);
+ AssertCounts(0, 1, 0);
EXPECT_EQ(sync_removed_.size(), 1u);
EXPECT_EQ(sync_removed_.count("http://read.example.com/"), 1u);
}
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 d01e00ca9dd..83ff099a2ae 100644
--- a/chromium/components/renderer_context_menu/context_menu_content_type.cc
+++ b/chromium/components/renderer_context_menu/context_menu_content_type.cc
@@ -63,10 +63,9 @@ bool ContextMenuContentType::SupportsGroup(int group) {
if (IsDevToolsURL(params_.page_url)) {
// DevTools mostly provides custom context menu and uses
// only the following default options.
- if (group != ITEM_GROUP_CUSTOM &&
- group != ITEM_GROUP_EDITABLE &&
- group != ITEM_GROUP_COPY &&
- group != ITEM_GROUP_DEVELOPER) {
+ if (group != ITEM_GROUP_CUSTOM && group != ITEM_GROUP_EDITABLE &&
+ group != ITEM_GROUP_COPY && group != ITEM_GROUP_DEVELOPER &&
+ group != ITEM_GROUP_SEARCH_PROVIDER) {
return false;
}
}
@@ -105,6 +104,9 @@ bool ContextMenuContentType::SupportsGroupInternal(int group) {
case ITEM_GROUP_LINK:
return has_link;
+ case ITEM_GROUP_SMART_SELECTION:
+ return has_selection && !has_link;
+
case ITEM_GROUP_MEDIA_IMAGE:
return params_.media_type == WebContextMenuData::kMediaTypeImage;
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 92d86a5be54..0cce18f08a7 100644
--- a/chromium/components/renderer_context_menu/context_menu_content_type.h
+++ b/chromium/components/renderer_context_menu/context_menu_content_type.h
@@ -29,6 +29,7 @@ class ContextMenuContentType {
ITEM_GROUP_PAGE,
ITEM_GROUP_FRAME,
ITEM_GROUP_LINK,
+ ITEM_GROUP_SMART_SELECTION,
ITEM_GROUP_MEDIA_IMAGE,
ITEM_GROUP_SEARCHWEBFORIMAGE,
ITEM_GROUP_MEDIA_VIDEO,
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 da8fd4f2240..2262c376ffb 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
@@ -239,6 +239,14 @@ void RenderViewContextMenuBase::UpdateMenuIcon(int command_id,
#endif
}
+void RenderViewContextMenuBase::AddSeparatorBelowMenuItem(int command_id) {
+#if defined(OS_CHROMEOS)
+ if (toolkit_delegate_)
+ toolkit_delegate_->AddSeparatorAt(
+ menu_model_.GetIndexOfCommandId(command_id) + 1);
+#endif
+}
+
RenderViewHost* RenderViewContextMenuBase::GetRenderViewHost() const {
return source_web_contents_->GetRenderViewHost();
}
diff --git a/chromium/components/renderer_context_menu/render_view_context_menu_base.h b/chromium/components/renderer_context_menu/render_view_context_menu_base.h
index 83ef6e6797a..d959c4ea893 100644
--- a/chromium/components/renderer_context_menu/render_view_context_menu_base.h
+++ b/chromium/components/renderer_context_menu/render_view_context_menu_base.h
@@ -50,6 +50,8 @@ class RenderViewContextMenuBase : public ui::SimpleMenuModel::Delegate,
const base::string16& title) = 0;
#if defined(OS_CHROMEOS)
virtual void UpdateMenuIcon(int command_id, const gfx::Image& image) = 0;
+
+ virtual void AddSeparatorAt(int index) = 0;
#endif
};
@@ -105,6 +107,7 @@ class RenderViewContextMenuBase : public ui::SimpleMenuModel::Delegate,
bool hidden,
const base::string16& title) override;
void UpdateMenuIcon(int command_id, const gfx::Image& image) override;
+ void AddSeparatorBelowMenuItem(int command_id) override;
content::RenderViewHost* GetRenderViewHost() const override;
content::WebContents* GetWebContents() const override;
content::BrowserContext* GetBrowserContext() const override;
diff --git a/chromium/components/renderer_context_menu/render_view_context_menu_proxy.h b/chromium/components/renderer_context_menu/render_view_context_menu_proxy.h
index 0ec293f6e38..69c58e7ad5a 100644
--- a/chromium/components/renderer_context_menu/render_view_context_menu_proxy.h
+++ b/chromium/components/renderer_context_menu/render_view_context_menu_proxy.h
@@ -97,6 +97,9 @@ class RenderViewContextMenuProxy {
// Update the icon of the specified context-menu item.
virtual void UpdateMenuIcon(int command_id, const gfx::Image& image) = 0;
+ // Add a separator below the specified context-menu item.
+ virtual void AddSeparatorBelowMenuItem(int command_id) = 0;
+
// Add spell check service item to the context menu.
virtual void AddSpellCheckServiceItem(bool is_checked) = 0;
diff --git a/chromium/components/renderer_context_menu/views/toolkit_delegate_views.cc b/chromium/components/renderer_context_menu/views/toolkit_delegate_views.cc
index b5312c48c3a..2abe1fa6298 100644
--- a/chromium/components/renderer_context_menu/views/toolkit_delegate_views.cc
+++ b/chromium/components/renderer_context_menu/views/toolkit_delegate_views.cc
@@ -73,4 +73,9 @@ void ToolkitDelegateViews::UpdateMenuIcon(int command_id,
parent->ChildrenChanged();
}
+
+void ToolkitDelegateViews::AddSeparatorAt(int index) {
+ menu_view_->AddSeparatorAt(index);
+ menu_view_->ChildrenChanged();
+}
#endif
diff --git a/chromium/components/renderer_context_menu/views/toolkit_delegate_views.h b/chromium/components/renderer_context_menu/views/toolkit_delegate_views.h
index eb3ef326b4c..a2c067c3f4f 100644
--- a/chromium/components/renderer_context_menu/views/toolkit_delegate_views.h
+++ b/chromium/components/renderer_context_menu/views/toolkit_delegate_views.h
@@ -48,6 +48,7 @@ class ToolkitDelegateViews : public RenderViewContextMenuBase::ToolkitDelegate {
const base::string16& title) override;
#if defined(OS_CHROMEOS)
void UpdateMenuIcon(int command_id, const gfx::Image& image) override;
+ void AddSeparatorAt(int index) override;
#endif
std::unique_ptr<views::MenuModelAdapter> menu_adapter_;
diff --git a/chromium/components/reset_password_strings.grdp b/chromium/components/reset_password_strings.grdp
index 00117ea39b7..04ff72a1a56 100644
--- a/chromium/components/reset_password_strings.grdp
+++ b/chromium/components/reset_password_strings.grdp
@@ -13,7 +13,7 @@
You entered your password on a site that’s not managed by your organization. To protect your account, don’t reuse your password on other apps and sites.
</message>
<message name="IDS_RESET_PASSWORD_WARNING_EXPLANATION_PARAGRAPH_WITH_ORG_NAME" desc="The primary explanatory paragraph for the reset password page.">
- You entered your password on a site that’s not managed by <ph name="ORG_NAME">$1<ex>Google</ex></ph>. To protect your account, don’t reuse your password on other apps and sites.
+ You entered your password on a site that’s not managed by <ph name="BEGIN_BOLD">&lt;strong&gt;</ph><ph name="ORG_NAME">$1<ex>Google</ex></ph><ph name="END_BOLD">&lt;/strong&gt;</ph>. To protect your account, don’t reuse your password on other apps and sites.
</message>
<message name="IDS_RESET_PASSWORD_BUTTON" desc="The text for the button that takes the user to reset their password.">
Reset password
@@ -23,7 +23,7 @@
Chrome recommends resetting your password if you reused it on other sites.
</message>
<message name="IDS_RESET_PASSWORD_EXPLANATION_PARAGRAPH_WITH_ORG_NAME" desc="The primary explanatory paragraph for the reset password page when the user manually navigates to this page.">
- Chrome recommends resetting your <ph name="ORG_NAME">$1<ex>Google</ex></ph> password if you reused it on other sites.
+ Chrome recommends resetting your <ph name="BEGIN_BOLD">&lt;strong&gt;</ph><ph name="ORG_NAME">$1<ex>Google</ex></ph><ph name="END_BOLD">&lt;/strong&gt;</ph> password if you reused it on other sites.
</message>
</if>
<if expr="not _google_chrome">
@@ -31,7 +31,7 @@
Chromium recommends resetting your password if you reused it on other sites.
</message>
<message name="IDS_RESET_PASSWORD_EXPLANATION_PARAGRAPH_WITH_ORG_NAME" desc="The primary explanatory paragraph for the reset password page when the user manually navigates to this page.">
- Chromium recommends resetting your <ph name="ORG_NAME">$1<ex>Google</ex></ph> password if you reused it on other sites.
+ Chromium recommends resetting your <ph name="BEGIN_BOLD">&lt;strong&gt;</ph><ph name="ORG_NAME">$1<ex>Google</ex></ph><ph name="END_BOLD">&lt;/strong&gt;</ph> password if you reused it on other sites.
</message>
</if>
</grit-part>
diff --git a/chromium/components/resources/OWNERS b/chromium/components/resources/OWNERS
index 1bde68de71e..456bcda3adc 100644
--- a/chromium/components/resources/OWNERS
+++ b/chromium/components/resources/OWNERS
@@ -1,4 +1,4 @@
-per-file autofill_scaled_resources.grdp=mathp@chromium.org
+per-file autofill*=file://components/autofill/OWNERS
per-file content_suggestions*=file://components/ntp_snippets/OWNERS
per-file crash_*=cpu@chromium.org
per-file crash_*=jochen@chromium.org
@@ -15,8 +15,8 @@ per-file gcm_driver_resources.grdp=fgorski@chromium.org
per-file gcm_driver_resources.grdp=jianli@chromium.org
per-file gcm_driver_resources.grdp=peter@chromium.org
per-file gcm_driver_resources.grdp=zea@chromium.org
-per-file neterror*=juliatuttle@chromium.org
per-file neterror*=mmenke@chromium.org
+per-file neterror*=file://net/OWNERS
per-file ntp_tiles_resources.grdp=file://components/ntp_tiles/OWNERS
per-file offline_pages_resources.grdp=file://components/offline_pages/OWNERS
per-file proximity_auth*=tengs@chromium.org
diff --git a/chromium/components/resources/autofill_scaled_resources.grdp b/chromium/components/resources/autofill_scaled_resources.grdp
index 97db3335d35..ef1166083bd 100644
--- a/chromium/components/resources/autofill_scaled_resources.grdp
+++ b/chromium/components/resources/autofill_scaled_resources.grdp
@@ -32,7 +32,5 @@
</if>
<if expr="is_ios">
<structure type="chrome_scaled_image" name="IDR_AUTOFILL_GOOGLE_PAY_WITH_DIVIDER" file="autofill/infobar_autofill_googlepay_with_divider.png" />
- <structure type="chrome_scaled_image" name="IDR_AUTOFILL_TOOLTIP_ICON" file="autofill/autofill_tooltip_icon.png" />
- <structure type="chrome_scaled_image" name="IDR_AUTOFILL_TOOLTIP_ICON_H" file="autofill/autofill_tooltip_icon_hover.png" />
</if>
</grit-part>
diff --git a/chromium/components/resources/components_resources.grd b/chromium/components/resources/components_resources.grd
index 4623e36e0d2..acbb344c857 100644
--- a/chromium/components/resources/components_resources.grd
+++ b/chromium/components/resources/components_resources.grd
@@ -16,6 +16,9 @@
<part file="net_log_resources.grdp" />
<part file="neterror_resources.grdp" />
<part file="ntp_tiles_resources.grdp" />
+ <if expr="is_win">
+ <part file="nux_google_apps.grdp" />
+ </if>
<part file="offline_pages_resources.grdp" />
<part file="password_manager_internals_resources.grdp" />
<part file="printing_resources.grdp" />
diff --git a/chromium/components/resources/default_100_percent/autofill/autofill_tooltip_icon.png b/chromium/components/resources/default_100_percent/autofill/autofill_tooltip_icon.png
deleted file mode 100644
index 4434da71fd8..00000000000
--- a/chromium/components/resources/default_100_percent/autofill/autofill_tooltip_icon.png
+++ /dev/null
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/autofill_tooltip_icon_hover.png b/chromium/components/resources/default_100_percent/autofill/autofill_tooltip_icon_hover.png
deleted file mode 100644
index 893b63d7e4c..00000000000
--- a/chromium/components/resources/default_100_percent/autofill/autofill_tooltip_icon_hover.png
+++ /dev/null
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/credit_card_cvc_hint.png b/chromium/components/resources/default_100_percent/autofill/credit_card_cvc_hint.png
index 951ade9a8b9..60ec7623868 100644
--- a/chromium/components/resources/default_100_percent/autofill/credit_card_cvc_hint.png
+++ b/chromium/components/resources/default_100_percent/autofill/credit_card_cvc_hint.png
Binary files differ
diff --git a/chromium/components/resources/default_100_percent/autofill/credit_card_cvc_hint_amex.png b/chromium/components/resources/default_100_percent/autofill/credit_card_cvc_hint_amex.png
index 22e11d31722..a6df7c5590b 100644
--- a/chromium/components/resources/default_100_percent/autofill/credit_card_cvc_hint_amex.png
+++ b/chromium/components/resources/default_100_percent/autofill/credit_card_cvc_hint_amex.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/autofill_tooltip_icon.png b/chromium/components/resources/default_200_percent/autofill/autofill_tooltip_icon.png
deleted file mode 100644
index 47322b966c5..00000000000
--- a/chromium/components/resources/default_200_percent/autofill/autofill_tooltip_icon.png
+++ /dev/null
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/autofill_tooltip_icon_hover.png b/chromium/components/resources/default_200_percent/autofill/autofill_tooltip_icon_hover.png
deleted file mode 100644
index 5d112369861..00000000000
--- a/chromium/components/resources/default_200_percent/autofill/autofill_tooltip_icon_hover.png
+++ /dev/null
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/credit_card_cvc_hint.png b/chromium/components/resources/default_200_percent/autofill/credit_card_cvc_hint.png
index be64e428985..004e9c4b715 100644
--- a/chromium/components/resources/default_200_percent/autofill/credit_card_cvc_hint.png
+++ b/chromium/components/resources/default_200_percent/autofill/credit_card_cvc_hint.png
Binary files differ
diff --git a/chromium/components/resources/default_200_percent/autofill/credit_card_cvc_hint_amex.png b/chromium/components/resources/default_200_percent/autofill/credit_card_cvc_hint_amex.png
index 39b3bdf4fc1..d65dc108010 100644
--- a/chromium/components/resources/default_200_percent/autofill/credit_card_cvc_hint_amex.png
+++ b/chromium/components/resources/default_200_percent/autofill/credit_card_cvc_hint_amex.png
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/autofill_tooltip_icon.png b/chromium/components/resources/default_300_percent/autofill/autofill_tooltip_icon.png
deleted file mode 100644
index 0c7dd7e4cec..00000000000
--- a/chromium/components/resources/default_300_percent/autofill/autofill_tooltip_icon.png
+++ /dev/null
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/autofill_tooltip_icon_hover.png b/chromium/components/resources/default_300_percent/autofill/autofill_tooltip_icon_hover.png
deleted file mode 100644
index 33bd12ff9e5..00000000000
--- a/chromium/components/resources/default_300_percent/autofill/autofill_tooltip_icon_hover.png
+++ /dev/null
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/credit_card_cvc_hint.png b/chromium/components/resources/default_300_percent/autofill/credit_card_cvc_hint.png
new file mode 100644
index 00000000000..091974d6e20
--- /dev/null
+++ b/chromium/components/resources/default_300_percent/autofill/credit_card_cvc_hint.png
Binary files differ
diff --git a/chromium/components/resources/default_300_percent/autofill/credit_card_cvc_hint_amex.png b/chromium/components/resources/default_300_percent/autofill/credit_card_cvc_hint_amex.png
new file mode 100644
index 00000000000..931d8b17779
--- /dev/null
+++ b/chromium/components/resources/default_300_percent/autofill/credit_card_cvc_hint_amex.png
Binary files differ
diff --git a/chromium/components/resources/nux_google_apps.grdp b/chromium/components/resources/nux_google_apps.grdp
new file mode 100644
index 00000000000..58f9f8f1b8e
--- /dev/null
+++ b/chromium/components/resources/nux_google_apps.grdp
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+ <include name="IDR_NUX_GOOGLE_APPS_HTML" file="../nux_google_apps/resources/nux_google_apps.html" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_JS" file="../nux_google_apps/resources/nux_google_apps.js" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_PROXY_HTML" file="../nux_google_apps/resources/nux_google_apps_proxy.html" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_PROXY_JS" file="../nux_google_apps/resources/nux_google_apps_proxy.js" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_CHOOSER_HTML" file="../nux_google_apps/resources/apps_chooser.html" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_CHOOSER_JS" file="../nux_google_apps/resources/apps_chooser.js" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_CHROME_STORE_1X" file="../nux_google_apps/resources/chrome_store_24dp_1x.png" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_CHROME_STORE_2X" file="../nux_google_apps/resources/chrome_store_24dp_2x.png" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_GMAIL_1X" file="../nux_google_apps/resources/gmail_24dp_1x.png" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_GMAIL_2X" file="../nux_google_apps/resources/gmail_24dp_2x.png" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_MAPS_1X" file="../nux_google_apps/resources/maps_24dp_1x.png" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_MAPS_2X" file="../nux_google_apps/resources/maps_24dp_2x.png" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_NEWS_1X" file="../nux_google_apps/resources/news_24dp_1x.png" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_NEWS_2X" file="../nux_google_apps/resources/news_24dp_2x.png" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_TRANSLATE_1X" file="../nux_google_apps/resources/translate_24dp_1x.png" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_TRANSLATE_2X" file="../nux_google_apps/resources/translate_24dp_2x.png" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_YOUTUBE_1X" file="../nux_google_apps/resources/youtube_24dp_1x.png" type="BINDATA" />
+ <include name="IDR_NUX_GOOGLE_APPS_YOUTUBE_2X" file="../nux_google_apps/resources/youtube_24dp_2x.png" type="BINDATA" />
+</grit-part>
diff --git a/chromium/components/safe_browsing/BUILD.gn b/chromium/components/safe_browsing/BUILD.gn
index 5fba6778129..6da62af66db 100644
--- a/chromium/components/safe_browsing/BUILD.gn
+++ b/chromium/components/safe_browsing/BUILD.gn
@@ -27,8 +27,8 @@ source_set("safe_browsing") {
"//components/security_interstitials/content:security_interstitial_page",
]
deps = [
- ":base_ping_manager",
":features",
+ ":ping_manager",
"//base:base",
"//base:i18n",
"//components/safe_browsing/common:common",
@@ -42,10 +42,10 @@ source_set("safe_browsing") {
]
}
-static_library("base_ping_manager") {
+static_library("ping_manager") {
sources = [
- "base_ping_manager.cc",
- "base_ping_manager.h",
+ "ping_manager.cc",
+ "ping_manager.h",
]
public_deps = [
@@ -62,14 +62,14 @@ static_library("base_ping_manager") {
]
}
-source_set("base_ping_manager_unittest") {
+source_set("ping_manager_unittest") {
testonly = true
sources = [
- "base_ping_manager_unittest.cc",
+ "ping_manager_unittest.cc",
]
deps = [
- ":base_ping_manager",
+ ":ping_manager",
"//base:base",
"//net:net",
"//net:test_support",
diff --git a/chromium/components/safe_browsing/DEPS b/chromium/components/safe_browsing/DEPS
index 8b4e3a64490..c67416364e2 100644
--- a/chromium/components/safe_browsing/DEPS
+++ b/chromium/components/safe_browsing/DEPS
@@ -2,6 +2,7 @@ include_rules = [
"+components/data_use_measurement/core",
"+components/security_interstitials/content",
"+components/security_interstitials/core",
+ "+components/sync/protocol",
"+content/public/browser",
"+content/public/common",
"+google_apis",
diff --git a/chromium/components/safe_browsing/android/remote_database_manager.cc b/chromium/components/safe_browsing/android/remote_database_manager.cc
index 2e891d43c13..c3da90103e8 100644
--- a/chromium/components/safe_browsing/android/remote_database_manager.cc
+++ b/chromium/components/safe_browsing/android/remote_database_manager.cc
@@ -169,10 +169,6 @@ bool RemoteSafeBrowsingDatabaseManager::CanCheckResourceType(
return resource_types_to_check_.count(resource_type) > 0;
}
-bool RemoteSafeBrowsingDatabaseManager::CanCheckSubresourceFilter() const {
- return true;
-}
-
bool RemoteSafeBrowsingDatabaseManager::CanCheckUrl(const GURL& url) const {
return url.SchemeIsHTTPOrHTTPS() || url.SchemeIs(url::kFtpScheme) ||
url.SchemeIsWSOrWSS();
@@ -241,7 +237,6 @@ bool RemoteSafeBrowsingDatabaseManager::CheckUrlForSubresourceFilter(
const GURL& url,
Client* client) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
- DCHECK(CanCheckSubresourceFilter());
if (!enabled_ || !CanCheckUrl(url))
return true;
diff --git a/chromium/components/safe_browsing/android/remote_database_manager.h b/chromium/components/safe_browsing/android/remote_database_manager.h
index a7e3e409814..3a81e5b3b8b 100644
--- a/chromium/components/safe_browsing/android/remote_database_manager.h
+++ b/chromium/components/safe_browsing/android/remote_database_manager.h
@@ -37,7 +37,6 @@ class RemoteSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager {
void CancelCheck(Client* client) override;
bool CanCheckResourceType(content::ResourceType resource_type) const override;
- bool CanCheckSubresourceFilter() const override;
bool CanCheckUrl(const GURL& url) const override;
bool ChecksAreAlwaysAsync() const override;
bool CheckBrowseUrl(const GURL& url,
diff --git a/chromium/components/safe_browsing/browser/base_parallel_resource_throttle.cc b/chromium/components/safe_browsing/browser/base_parallel_resource_throttle.cc
index cb064204274..86868a57918 100644
--- a/chromium/components/safe_browsing/browser/base_parallel_resource_throttle.cc
+++ b/chromium/components/safe_browsing/browser/base_parallel_resource_throttle.cc
@@ -143,8 +143,9 @@ void BaseParallelResourceThrottle::WillRedirectRequest(
// The safe browsing URLLoaderThrottle doesn't use ResourceResponse, so pass
// in an empty struct to avoid changing ResourceThrottle signature.
network::ResourceResponseHead resource_response;
+ std::vector<std::string> to_be_removed_headers;
url_loader_throttle_holder_->throttle()->WillRedirectRequest(
- redirect_info, resource_response, defer);
+ redirect_info, resource_response, defer, &to_be_removed_headers);
DCHECK(!*defer);
throttle_in_band_ = false;
}
diff --git a/chromium/components/safe_browsing/browser/browser_url_loader_throttle.cc b/chromium/components/safe_browsing/browser/browser_url_loader_throttle.cc
index 1447b38c849..8b10f84b17d 100644
--- a/chromium/components/safe_browsing/browser/browser_url_loader_throttle.cc
+++ b/chromium/components/safe_browsing/browser/browser_url_loader_throttle.cc
@@ -69,7 +69,8 @@ void BrowserURLLoaderThrottle::WillStartRequest(
void BrowserURLLoaderThrottle::WillRedirectRequest(
const net::RedirectInfo& redirect_info,
const network::ResourceResponseHead& response_head,
- bool* defer) {
+ bool* defer,
+ std::vector<std::string>* to_be_removed_headers) {
if (blocked_) {
// OnCheckUrlResult() has set |blocked_| to true and called
// |delegate_->CancelWithError|, but this method is called before the
diff --git a/chromium/components/safe_browsing/browser/browser_url_loader_throttle.h b/chromium/components/safe_browsing/browser/browser_url_loader_throttle.h
index 4b4cc0fbb5f..5fabb8dec28 100644
--- a/chromium/components/safe_browsing/browser/browser_url_loader_throttle.h
+++ b/chromium/components/safe_browsing/browser/browser_url_loader_throttle.h
@@ -43,9 +43,11 @@ class BrowserURLLoaderThrottle : public content::URLLoaderThrottle {
// content::URLLoaderThrottle implementation.
void WillStartRequest(network::ResourceRequest* request,
bool* defer) override;
- void WillRedirectRequest(const net::RedirectInfo& redirect_info,
- const network::ResourceResponseHead& response_head,
- bool* defer) override;
+ void WillRedirectRequest(
+ const net::RedirectInfo& redirect_info,
+ const network::ResourceResponseHead& response_head,
+ bool* defer,
+ std::vector<std::string>* to_be_removed_headers) override;
void WillProcessResponse(const GURL& response_url,
const network::ResourceResponseHead& response_head,
bool* defer) override;
diff --git a/chromium/components/safe_browsing/browser/safe_browsing_network_context.cc b/chromium/components/safe_browsing/browser/safe_browsing_network_context.cc
index 1dcc1cd5ea0..799d6ccf709 100644
--- a/chromium/components/safe_browsing/browser/safe_browsing_network_context.cc
+++ b/chromium/components/safe_browsing/browser/safe_browsing_network_context.cc
@@ -166,6 +166,7 @@ class SafeBrowsingNetworkContext::SharedURLLoaderFactory
base::FilePath cookie_path = user_data_dir_.Append(
base::FilePath::StringType(kSafeBrowsingBaseFilename) + kCookiesFile);
network_context_params->cookie_path = cookie_path;
+ network_context_params->enable_encrypted_cookies = false;
base::FilePath channel_id_path = user_data_dir_.Append(
base::FilePath::StringType(kSafeBrowsingBaseFilename) + kChannelIDFile);
diff --git a/chromium/components/safe_browsing/browser/safe_browsing_url_request_context_getter.cc b/chromium/components/safe_browsing/browser/safe_browsing_url_request_context_getter.cc
index de2a7407287..a1bcbb38222 100644
--- a/chromium/components/safe_browsing/browser/safe_browsing_url_request_context_getter.cc
+++ b/chromium/components/safe_browsing/browser/safe_browsing_url_request_context_getter.cc
@@ -30,6 +30,7 @@ SafeBrowsingURLRequestContextGetter::SafeBrowsingURLRequestContextGetter(
network_task_runner_(
BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)) {
DCHECK(!user_data_dir.empty());
+ DCHECK(system_context_getter_);
}
net::URLRequestContext*
@@ -42,11 +43,8 @@ SafeBrowsingURLRequestContextGetter::GetURLRequestContext() {
if (!safe_browsing_request_context_) {
safe_browsing_request_context_.reset(new net::URLRequestContext());
- // May be NULL in unit tests.
- if (system_context_getter_) {
- safe_browsing_request_context_->CopyFrom(
- system_context_getter_->GetURLRequestContext());
- }
+ safe_browsing_request_context_->CopyFrom(
+ system_context_getter_->GetURLRequestContext());
scoped_refptr<base::SequencedTaskRunner> background_task_runner =
base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), base::TaskPriority::BACKGROUND,
diff --git a/chromium/components/safe_browsing/browser/threat_details.cc b/chromium/components/safe_browsing/browser/threat_details.cc
index 7842af3eee8..36f1bf888d0 100644
--- a/chromium/components/safe_browsing/browser/threat_details.cc
+++ b/chromium/components/safe_browsing/browser/threat_details.cc
@@ -15,6 +15,7 @@
#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "components/history/core/browser/history_service.h"
#include "components/safe_browsing/base_ui_manager.h"
@@ -91,7 +92,8 @@ ClientSafeBrowsingReportRequest::ReportType GetReportTypeFromSBThreatType(
return ClientSafeBrowsingReportRequest::URL_CLIENT_SIDE_MALWARE;
case SB_THREAT_TYPE_AD_SAMPLE:
return ClientSafeBrowsingReportRequest::AD_SAMPLE;
- case SB_THREAT_TYPE_PASSWORD_REUSE:
+ case SB_THREAT_TYPE_SIGN_IN_PASSWORD_REUSE:
+ case SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE:
return ClientSafeBrowsingReportRequest::URL_PASSWORD_PROTECTION_PHISHING;
case SB_THREAT_TYPE_SUSPICIOUS_SITE:
return ClientSafeBrowsingReportRequest::URL_SUSPICIOUS;
@@ -251,8 +253,7 @@ void TrimElements(const std::set<int> target_ids,
const HTMLElement& element = *element_iter->second;
// Delete any elements that we do not want to keep.
- if (std::find(ids_to_keep.begin(), ids_to_keep.end(), element.id()) ==
- ids_to_keep.end()) {
+ if (!base::ContainsValue(ids_to_keep, element.id())) {
if (element.has_resource_id()) {
const std::string& resource_url =
resource_id_to_url[element.resource_id()];
@@ -354,7 +355,6 @@ ThreatDetails::ThreatDetails()
num_visits_(0),
ambiguous_dom_(false),
trim_to_ad_tags_(false),
- done_callback_(nullptr),
all_done_expected_(false),
is_all_done_(false) {}
@@ -775,17 +775,9 @@ void ThreatDetails::OnCacheCollectionReady() {
return;
}
- // For measuring performance impact of ad sampling reports, we may want to
- // do all the heavy lifting of creating the report but not actually send it.
- if (report_->type() == ClientSafeBrowsingReportRequest::AD_SAMPLE &&
- base::FeatureList::IsEnabled(kAdSamplerCollectButDontSendFeature)) {
- AllDone();
- return;
- }
-
BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
- base::BindOnce(&WebUIInfoSingleton::AddToReportsSent,
+ base::BindOnce(&WebUIInfoSingleton::AddToCSBRRsSent,
base::Unretained(WebUIInfoSingleton::GetInstance()),
std::move(report_)));
diff --git a/chromium/components/safe_browsing/common/safe_browsing_prefs.cc b/chromium/components/safe_browsing/common/safe_browsing_prefs.cc
index 8f66cdbffc0..eb70226ff83 100644
--- a/chromium/components/safe_browsing/common/safe_browsing_prefs.cc
+++ b/chromium/components/safe_browsing/common/safe_browsing_prefs.cc
@@ -159,8 +159,6 @@ GURL GetSimplifiedURL(const GURL& url) {
namespace prefs {
const char kSafeBrowsingEnabled[] = "safebrowsing.enabled";
-const char kSafeBrowsingExtendedReportingEnabled[] =
- "safebrowsing.extended_reporting_enabled";
const char kSafeBrowsingExtendedReportingOptInAllowed[] =
"safebrowsing.extended_reporting_opt_in_allowed";
const char kSafeBrowsingIncidentsSent[] = "safebrowsing.incidents_sent";
@@ -219,85 +217,7 @@ ExtendedReportingLevel GetExtendedReportingLevel(const PrefService& prefs) {
}
const char* GetExtendedReportingPrefName(const PrefService& prefs) {
- // The Scout pref is active if the experiment features is on, and
- // ScoutGroupSelected is on as well.
- if (base::FeatureList::IsEnabled(kCanShowScoutOptIn) &&
- prefs.GetBoolean(prefs::kSafeBrowsingScoutGroupSelected)) {
return prefs::kSafeBrowsingScoutReportingEnabled;
- }
-
- // ..otherwise, either no experiment is on (ie: the Control group) or
- // ScoutGroupSelected is off. So we use the SBER pref instead.
- return prefs::kSafeBrowsingExtendedReportingEnabled;
-}
-
-void InitializeSafeBrowsingPrefs(PrefService* prefs) {
- // Handle the two possible experiment states.
- if (base::FeatureList::IsEnabled(kCanShowScoutOptIn)) {
- // CanShowScoutOptIn will only turn on ScoutGroupSelected pref if the legacy
- // SBER pref is false. Otherwise the legacy SBER pref will stay on and
- // continue to be used until the next security incident, at which point
- // the Scout pref will become the active one.
- if (!prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) {
- prefs->SetBoolean(prefs::kSafeBrowsingScoutGroupSelected, true);
- UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
- CAN_SHOW_SCOUT_OPT_IN_SCOUT_GROUP_ON,
- MAX_REASONS);
- } else {
- UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
- CAN_SHOW_SCOUT_OPT_IN_WAIT_FOR_INTERSTITIAL,
- MAX_REASONS);
- }
- } else {
- // Experiment feature is off, so this is the Control group. We must
- // handle the possibility that the user was previously in an experiment
- // group (above) that was reverted. We want to restore the user to a
- // reasonable state based on the ScoutGroup and ScoutReporting preferences.
- UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName, CONTROL, MAX_REASONS);
- bool transitioned = false;
- if (prefs->GetBoolean(prefs::kSafeBrowsingScoutReportingEnabled)) {
- // User opted-in to Scout which is broader than legacy Extended Reporting.
- // Opt them in to Extended Reporting.
- prefs->SetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled, true);
- UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
- ROLLBACK_SBER2_IMPLIES_SBER1, MAX_REASONS);
- transitioned = true;
- } else if (prefs->GetBoolean(prefs::kSafeBrowsingScoutGroupSelected)) {
- // User was in the Scout Group (ie: seeing the Scout opt-in text) but did
- // NOT opt-in to Scout. Assume this was a conscious choice and remove
- // their legacy Extended Reporting opt-in as well. The user will have a
- // chance to evaluate their choice next time they see the opt-in text.
-
- // We make the Extended Reporting pref mimic the state of the Scout
- // Reporting pref. So we either Clear it or set it to False.
- if (prefs->HasPrefPath(prefs::kSafeBrowsingScoutReportingEnabled)) {
- // Scout Reporting pref was explicitly set to false, so set the SBER
- // pref to false.
- prefs->SetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled, false);
- UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
- ROLLBACK_NO_SBER2_SET_SBER1_FALSE,
- MAX_REASONS);
- } else {
- // Scout Reporting pref is unset, so clear the SBER pref.
- prefs->ClearPref(prefs::kSafeBrowsingExtendedReportingEnabled);
- UMA_HISTOGRAM_ENUMERATION(kScoutTransitionMetricName,
- ROLLBACK_NO_SBER2_CLEAR_SBER1, MAX_REASONS);
- }
- transitioned = true;
- }
-
- // Also clear both the Scout settings to start over from a clean state and
- // avoid the above logic from triggering on next restart.
- prefs->ClearPref(prefs::kSafeBrowsingScoutGroupSelected);
- prefs->ClearPref(prefs::kSafeBrowsingScoutReportingEnabled);
-
- // Also forget that the user has seen any interstitials if they're
- // reverting back to a clean state.
- if (transitioned) {
- prefs->ClearPref(prefs::kSafeBrowsingSawInterstitialExtendedReporting);
- prefs->ClearPref(prefs::kSafeBrowsingSawInterstitialScoutReporting);
- }
- }
}
bool IsExtendedReportingOptInAllowed(const PrefService& prefs) {
@@ -334,21 +254,11 @@ void RecordExtendedReportingMetrics(const PrefService& prefs) {
// These metrics track the Scout transition.
if (prefs.GetBoolean(prefs::kSafeBrowsingScoutGroupSelected)) {
- // Users in the Scout group: currently seeing the Scout opt-in.
- UMA_HISTOGRAM_ENUMERATION(
- "SafeBrowsing.Pref.Scout.ScoutGroup.SBER1Pref",
- GetPrefValueOrNull(prefs, prefs::kSafeBrowsingExtendedReportingEnabled),
- MAX_NULLABLE_BOOLEAN);
UMA_HISTOGRAM_ENUMERATION(
"SafeBrowsing.Pref.Scout.ScoutGroup.SBER2Pref",
GetPrefValueOrNull(prefs, prefs::kSafeBrowsingScoutReportingEnabled),
MAX_NULLABLE_BOOLEAN);
} else {
- // Users not in the Scout group: currently seeing the SBER opt-in.
- UMA_HISTOGRAM_ENUMERATION(
- "SafeBrowsing.Pref.Scout.NoScoutGroup.SBER1Pref",
- GetPrefValueOrNull(prefs, prefs::kSafeBrowsingExtendedReportingEnabled),
- MAX_NULLABLE_BOOLEAN);
// The following metric is a corner case. User was previously in the
// Scout group and was able to opt-in to the Scout pref, but was since
// removed from the Scout group (eg: by rolling back a Scout experiment).
@@ -360,8 +270,6 @@ void RecordExtendedReportingMetrics(const PrefService& prefs) {
}
void RegisterProfilePrefs(PrefRegistrySimple* registry) {
- registry->RegisterBooleanPref(prefs::kSafeBrowsingExtendedReportingEnabled,
- false);
registry->RegisterBooleanPref(prefs::kSafeBrowsingScoutReportingEnabled,
false);
registry->RegisterBooleanPref(prefs::kSafeBrowsingScoutGroupSelected, false);
@@ -489,7 +397,6 @@ base::ListValue GetSafeBrowsingPreferencesList(PrefService* prefs) {
const char* safe_browsing_preferences[] = {
prefs::kSafeBrowsingEnabled,
prefs::kSafeBrowsingExtendedReportingOptInAllowed,
- prefs::kSafeBrowsingExtendedReportingEnabled,
prefs::kSafeBrowsingScoutReportingEnabled};
// Add the status of the preferences if they are Enabled or Disabled for the
@@ -576,12 +483,15 @@ bool MatchesPasswordProtectionLoginURL(const GURL& url,
std::vector<GURL> login_urls;
GetPasswordProtectionLoginURLsPref(prefs, &login_urls);
- if (login_urls.empty())
- return false;
+ return MatchesURLList(url, login_urls);
+}
- GURL simple_url = GetSimplifiedURL(url);
- for (const GURL& login_url : login_urls) {
- if (GetSimplifiedURL(login_url) == simple_url) {
+bool MatchesURLList(const GURL& target_url, const std::vector<GURL> url_list) {
+ if (url_list.empty() || !target_url.is_valid())
+ return false;
+ GURL simple_target_url = GetSimplifiedURL(target_url);
+ for (const GURL& url : url_list) {
+ if (GetSimplifiedURL(url) == simple_target_url) {
return true;
}
}
diff --git a/chromium/components/safe_browsing/common/safe_browsing_prefs.h b/chromium/components/safe_browsing/common/safe_browsing_prefs.h
index d6cd6be18d4..3bc20af246c 100644
--- a/chromium/components/safe_browsing/common/safe_browsing_prefs.h
+++ b/chromium/components/safe_browsing/common/safe_browsing_prefs.h
@@ -19,9 +19,6 @@ namespace prefs {
// Boolean that is true when SafeBrowsing is enabled.
extern const char kSafeBrowsingEnabled[];
-// Boolean that tell us whether Safe Browsing extended reporting is enabled.
-extern const char kSafeBrowsingExtendedReportingEnabled[];
-
// Boolean that tells us whether users are given the option to opt in to Safe
// Browsing extended reporting. This is exposed as a preference that can be
// overridden by enterprise policy.
@@ -159,11 +156,6 @@ ExtendedReportingLevel GetExtendedReportingLevel(const PrefService& prefs);
// currently in effect. The specific pref in-use may change through experiments.
const char* GetExtendedReportingPrefName(const PrefService& prefs);
-// Initializes Safe Browsing preferences based on data such as experiment state,
-// command line flags, etc.
-// TODO: this is temporary (crbug.com/662944)
-void InitializeSafeBrowsingPrefs(PrefService* prefs);
-
// Returns whether the user is able to modify the Safe Browsing Extended
// Reporting opt-in.
bool IsExtendedReportingOptInAllowed(const PrefService& prefs);
@@ -255,6 +247,9 @@ GURL GetPasswordProtectionChangePasswordURLPref(const PrefService& prefs);
bool MatchesPasswordProtectionChangePasswordURL(const GURL& url,
const PrefService& prefs);
+// Helper function to match a |target_url| against |url_list|.
+bool MatchesURLList(const GURL& target_url, const std::vector<GURL> url_list);
+
} // namespace safe_browsing
#endif // COMPONENTS_SAFE_BROWSING_COMMON_SAFE_BROWSING_PREFS_H_
diff --git a/chromium/components/safe_browsing/common/safe_browsing_prefs_unittest.cc b/chromium/components/safe_browsing/common/safe_browsing_prefs_unittest.cc
index e3c1ac9d5a8..cf0367e3815 100644
--- a/chromium/components/safe_browsing/common/safe_browsing_prefs_unittest.cc
+++ b/chromium/components/safe_browsing/common/safe_browsing_prefs_unittest.cc
@@ -23,8 +23,6 @@ class SafeBrowsingPrefsTest : public ::testing::Test {
protected:
void SetUp() override {
prefs_.registry()->RegisterBooleanPref(
- prefs::kSafeBrowsingExtendedReportingEnabled, false);
- prefs_.registry()->RegisterBooleanPref(
prefs::kSafeBrowsingScoutReportingEnabled, false);
prefs_.registry()->RegisterBooleanPref(
prefs::kSafeBrowsingScoutGroupSelected, false);
@@ -42,9 +40,7 @@ class SafeBrowsingPrefsTest : public ::testing::Test {
ResetExperiments(/*can_show_scout=*/false);
}
- void ResetPrefs(bool sber_reporting, bool scout_reporting, bool scout_group) {
- prefs_.SetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled,
- sber_reporting);
+ void ResetPrefs(bool scout_reporting, bool scout_group) {
prefs_.SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled,
scout_reporting);
prefs_.SetBoolean(prefs::kSafeBrowsingScoutGroupSelected, scout_group);
@@ -73,45 +69,33 @@ class SafeBrowsingPrefsTest : public ::testing::Test {
// Convenience method for explicitly setting up all combinations of prefs and
// experiments.
- void TestGetPrefName(bool sber_reporting,
- bool scout_reporting,
+ void TestGetPrefName(bool scout_reporting,
bool scout_group,
bool can_show_scout,
const std::string& expected_pref) {
- ResetPrefs(sber_reporting, scout_reporting, scout_group);
+ ResetPrefs(scout_reporting, scout_group);
ResetExperiments(can_show_scout);
EXPECT_EQ(expected_pref, GetActivePref())
- << "sber=" << sber_reporting << " scout=" << scout_reporting
- << " scout_group=" << scout_group
+ << " scout=" << scout_reporting << " scout_group=" << scout_group
<< " can_show_scout=" << can_show_scout;
}
- void InitPrefs() { InitializeSafeBrowsingPrefs(&prefs_); }
-
bool IsScoutGroupSelected() {
return prefs_.GetBoolean(prefs::kSafeBrowsingScoutGroupSelected);
}
- void ExpectPrefs(bool sber_reporting,
- bool scout_reporting,
- bool scout_group) {
- LOG(INFO) << "Pref values: sber=" << sber_reporting
- << " scout=" << scout_reporting << " scout_group=" << scout_group;
- EXPECT_EQ(sber_reporting,
- prefs_.GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled));
+ void ExpectPrefs(bool scout_reporting, bool scout_group) {
+ LOG(INFO) << "Pref values: scout=" << scout_reporting
+ << " scout_group=" << scout_group;
EXPECT_EQ(scout_reporting,
prefs_.GetBoolean(prefs::kSafeBrowsingScoutReportingEnabled));
EXPECT_EQ(scout_group,
prefs_.GetBoolean(prefs::kSafeBrowsingScoutGroupSelected));
}
- void ExpectPrefsExist(bool sber_reporting,
- bool scout_reporting,
- bool scout_group) {
- LOG(INFO) << "Prefs exist: sber=" << sber_reporting
- << " scout=" << scout_reporting << " scout_group=" << scout_group;
- EXPECT_EQ(sber_reporting,
- prefs_.HasPrefPath(prefs::kSafeBrowsingExtendedReportingEnabled));
+ void ExpectPrefsExist(bool scout_reporting, bool scout_group) {
+ LOG(INFO) << "Prefs exist: scout=" << scout_reporting
+ << " scout_group=" << scout_group;
EXPECT_EQ(scout_reporting,
prefs_.HasPrefPath(prefs::kSafeBrowsingScoutReportingEnabled));
EXPECT_EQ(scout_group,
@@ -124,244 +108,81 @@ class SafeBrowsingPrefsTest : public ::testing::Test {
content::TestBrowserThreadBundle thread_bundle_;
};
-// This test ensures that we correctly select between SBER and Scout as the
+// This test ensures that we correctly select Scout as the
// active preference in a number of common scenarios.
TEST_F(SafeBrowsingPrefsTest, GetExtendedReportingPrefName_Common) {
- const std::string& sber = prefs::kSafeBrowsingExtendedReportingEnabled;
const std::string& scout = prefs::kSafeBrowsingScoutReportingEnabled;
- // By default (all prefs and experiment features disabled), SBER pref is used.
- TestGetPrefName(false, false, false, false, sber);
+ // By default (all prefs and experiment features disabled), Scout pref is
+ // used.
+ TestGetPrefName(false, false, false, scout);
- // Changing any prefs (including ScoutGroupSelected) keeps SBER as the active
+ // Changing any prefs (including ScoutGroupSelected) keeps Scout as the active
// pref because the experiment remains in the Control group.
- TestGetPrefName(/*sber=*/true, false, false, false, sber);
- TestGetPrefName(false, /*scout=*/true, false, false, sber);
- TestGetPrefName(false, false, /*scout_group=*/true, false, sber);
+ TestGetPrefName(/*scout=*/true, false, false, scout);
+ TestGetPrefName(false, /*scout_group=*/true, false, scout);
// Being in the experiment group with ScoutGroup selected makes Scout the
// active pref.
- TestGetPrefName(false, false, /*scout_group=*/true, /*can_show_scout=*/true,
- scout);
+ TestGetPrefName(false, /*scout_group=*/true, /*can_show_scout=*/true, scout);
- // When ScoutGroup is not selected then SBER remains the active pref,
+ // When ScoutGroup is not selected then Scout still remains the active pref,
// regardless if the experiment is enabled.
- TestGetPrefName(false, false, false, /*can_show_scout=*/true, sber);
+ TestGetPrefName(false, false, /*can_show_scout=*/true, scout);
}
// Here we exhaustively check all combinations of pref and experiment states.
// This should help catch regressions.
TEST_F(SafeBrowsingPrefsTest, GetExtendedReportingPrefName_Exhaustive) {
- const std::string& sber = prefs::kSafeBrowsingExtendedReportingEnabled;
const std::string& scout = prefs::kSafeBrowsingScoutReportingEnabled;
- TestGetPrefName(false, false, false, false, sber);
- TestGetPrefName(false, false, false, true, sber);
- TestGetPrefName(false, false, true, false, sber);
- TestGetPrefName(false, false, true, true, scout);
- TestGetPrefName(false, true, false, false, sber);
- TestGetPrefName(false, true, false, true, sber);
- TestGetPrefName(false, true, true, false, sber);
- TestGetPrefName(false, true, true, true, scout);
- TestGetPrefName(true, false, false, false, sber);
- TestGetPrefName(true, false, false, true, sber);
- TestGetPrefName(true, false, true, false, sber);
- TestGetPrefName(true, false, true, true, scout);
- TestGetPrefName(true, true, false, false, sber);
- TestGetPrefName(true, true, false, true, sber);
- TestGetPrefName(true, true, true, false, sber);
- TestGetPrefName(true, true, true, true, scout);
-}
-
-// Test all combinations of prefs during initialization when no experiment
-// is on (ie: control group). In all cases the Scout prefs should be cleared,
-// and the SBER pref may get switched.
-TEST_F(SafeBrowsingPrefsTest, InitPrefs_Control) {
- // Turn the experiment off.
- ResetExperiments(/*can_show_scout=*/false);
-
- // Default case (everything off) - no change on init.
- ResetPrefs(false, false, false);
- InitPrefs();
- ExpectPrefs(false, false, false);
- // SBER pref exists because it was set to false above.
- ExpectPrefsExist(true, false, false);
-
- // ScoutGroup on - SBER cleared since Scout opt-in was shown but Scout pref
- // was not chosen. Scout prefs cleared.
- ResetPrefs(false, false, true);
- InitPrefs();
- ExpectPrefs(false, false, false);
- ExpectPrefsExist(true, false, false);
-
- // ScoutReporting on without ScoutGroup - SBER turns on since user opted-in to
- // broader Scout reporting, we can continue collecting the SBER subset. Scout
- // prefs cleared.
- ResetPrefs(false, true, false);
- InitPrefs();
- ExpectPrefs(true, false, false);
- ExpectPrefsExist(true, false, false);
-
- // ScoutReporting and ScoutGroup on - SBER turns on since user opted-in to
- // broader Scout reporting, we can continue collecting the SBER subset. Scout
- // prefs cleared.
- ResetPrefs(false, true, true);
- InitPrefs();
- ExpectPrefs(true, false, false);
- ExpectPrefsExist(true, false, false);
-
- // SBER on - no change on init since ScoutGroup is off implying that user
- // never saw Scout opt-in text. Scout prefs remain cleared.
- ResetPrefs(true, false, false);
- InitPrefs();
- ExpectPrefs(true, false, false);
- ExpectPrefsExist(true, false, false);
-
- // SBER and ScoutGroup on - SBER cleared. User previously opted-in to SBER
- // and they saw Scout opt-in text (ie. ScoutGroup on), but chose not to opt-in
- // to Scout reporting. We want them to re-evaluate their choice of SBER since
- // the lack of Scout opt-in was a conscious choice. Scout cleared.
- ResetPrefs(true, false, true);
- InitPrefs();
- ExpectPrefs(false, false, false);
- ExpectPrefsExist(true, false, false);
-
- // SBER and ScoutReporting on. User has opted-in to broader level of reporting
- // so SBER stays on. Scout prefs cleared.
- ResetPrefs(true, true, false);
- InitPrefs();
- ExpectPrefs(true, false, false);
- ExpectPrefsExist(true, false, false);
-
- // Everything on. User has opted-in to broader level of reporting so SBER
- // stays on. Scout prefs cleared.
- ResetPrefs(true, true, true);
- InitPrefs();
- ExpectPrefs(true, false, false);
- ExpectPrefs(true, false, false);
-}
-
-// Tests a unique case where the Extended Reporting pref will be Cleared instead
-// of set to False in order to mimic the state of the Scout reporting pref.
-// This happens when a user is in the Scout experiment but never encounters a
-// security issue so never sees the Scout opt-in. This user then returns to the
-// Control group having never seen the Scout opt-in, so their Scout Reporting
-// pref is un-set. We want to return their SBER pref to the unset state as well.
-TEST_F(SafeBrowsingPrefsTest, InitPrefs_Control_SberPrefCleared) {
- // Turn the experiment off.
- ResetExperiments(/*can_show_scout=*/false);
-
- // Set the user's old SBER pref to on to be explicit.
- prefs_.SetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled, true);
- // User is in the OnlyShowScoutOptIn experiment so they go directly to the
- // Scout Group.
- prefs_.SetBoolean(prefs::kSafeBrowsingScoutGroupSelected, true);
- // But they never see a security popup or change the setting manually so the
- // Scout pref remains unset.
- prefs_.ClearPref(prefs::kSafeBrowsingScoutReportingEnabled);
-
- InitPrefs();
-
- // All pref values should be false and unset.
- ExpectPrefs(false, false, false);
- ExpectPrefsExist(false, false, false);
-}
-
-// Test all combinations of prefs during initialization when the CanShowScout
-// experiment is on.
-TEST_F(SafeBrowsingPrefsTest, InitPrefs_CanShowScout) {
- // Turn the CanShowScout experiment on.
- ResetExperiments(/*can_show_scout=*/true);
-
- // Default case (everything off) - ScoutGroup turns on because SBER is off.
- ResetPrefs(false, false, false);
- InitPrefs();
- ExpectPrefs(false, false, true);
-
- // ScoutGroup on - no change on init since ScoutGroup is already on.
- ResetPrefs(false, false, true);
- InitPrefs();
- ExpectPrefs(false, false, true);
-
- // ScoutReporting on without ScoutGroup - ScoutGroup turns on because SBER is
- // off.
- ResetPrefs(false, true, false);
- InitPrefs();
- ExpectPrefs(false, true, true);
-
- // ScoutReporting and ScoutGroup on - no change on init since ScoutGroup is
- // already on.
- ResetPrefs(false, true, true);
- InitPrefs();
- ExpectPrefs(false, true, true);
-
- // SBER on - no change on init. Will wait for first security incident before
- // turning on ScoutGroup and displaying the Scout opt-in.
- ResetPrefs(true, false, false);
- InitPrefs();
- ExpectPrefs(true, false, false);
-
- // SBER and ScoutGroup on - no change on init since ScoutGroup is already on.
- ResetPrefs(true, false, true);
- InitPrefs();
- ExpectPrefs(true, false, true);
-
- // SBER and ScoutReporting on - no change on init because SBER is on.
- // ScoutGroup will turn on on next security incident.
- ResetPrefs(true, true, false);
- InitPrefs();
- ExpectPrefs(true, true, false);
-
- // Everything on - no change on init since ScoutGroup is already on.
- ResetPrefs(true, true, true);
- InitPrefs();
- ExpectPrefs(true, true, true);
+ TestGetPrefName(false, false, false, scout);
+ TestGetPrefName(false, false, true, scout);
+ TestGetPrefName(false, true, false, scout);
+ TestGetPrefName(false, true, true, scout);
+ TestGetPrefName(true, false, false, scout);
+ TestGetPrefName(true, false, true, scout);
+ TestGetPrefName(true, true, false, scout);
+ TestGetPrefName(true, true, true, scout);
}
TEST_F(SafeBrowsingPrefsTest, ChooseOptInText) {
+ // Ensure that Scout resources are always chosen.
const int kSberResource = 100;
const int kScoutResource = 500;
- // By default, SBER opt-in is used
- EXPECT_EQ(kSberResource,
+
+ // By default, Scout opt-in is used
+ EXPECT_EQ(kScoutResource,
ChooseOptInTextResource(prefs_, kSberResource, kScoutResource));
- // Enabling Scout switches to the Scout opt-in text.
+ // Enabling Scout still uses the Scout opt-in text.
ResetExperiments(/*can_show_scout=*/true);
- ResetPrefs(/*sber=*/false, /*scout=*/false, /*scout_group=*/true);
+ ResetPrefs(/*scout=*/false, /*scout_group=*/true);
EXPECT_EQ(kScoutResource,
ChooseOptInTextResource(prefs_, kSberResource, kScoutResource));
}
TEST_F(SafeBrowsingPrefsTest, GetSafeBrowsingExtendedReportingLevel) {
- // By Default, SBER is off
+ // By Default, extneded reporting is off.
EXPECT_EQ(SBER_LEVEL_OFF, GetExtendedReportingLevel(prefs_));
- // Opt-in to Legacy SBER gives Legacy reporting leve.
- ResetPrefs(/*sber=*/true, /*scout_reporting=*/false, /*scout_group=*/false);
- EXPECT_EQ(SBER_LEVEL_LEGACY, GetExtendedReportingLevel(prefs_));
-
- // The value of the Scout pref doesn't change the reporting level if the user
- // is outside of the Scout Group and/or no experiment is running.
+ // The value of the Scout pref affects the reporting level directly,
+ // regardless of the experiment configuration since Scout is the only level
+ // we are using.
// No scout group.
- ResetPrefs(/*sber=*/true, /*scout_reporting=*/true, /*scout_group=*/false);
- EXPECT_EQ(SBER_LEVEL_LEGACY, GetExtendedReportingLevel(prefs_));
+ ResetPrefs(/*scout_reporting=*/true, /*scout_group=*/false);
+ EXPECT_EQ(SBER_LEVEL_SCOUT, GetExtendedReportingLevel(prefs_));
// Scout group but no experiment.
- ResetPrefs(/*sber=*/true, /*scout_reporting=*/true, /*scout_group=*/true);
- EXPECT_EQ(SBER_LEVEL_LEGACY, GetExtendedReportingLevel(prefs_));
-
- // Remaining in the Scout Group and adding an experiment will switch to the
- // Scout pref to determine reporting level.
+ ResetPrefs(/*scout_reporting=*/true, /*scout_group=*/true);
+ EXPECT_EQ(SBER_LEVEL_SCOUT, GetExtendedReportingLevel(prefs_));
ResetExperiments(/*can_show_scout=*/true);
- // Both reporting prefs off, so reporting is off.
- ResetPrefs(/*sber=*/false, /*scout_reporting=*/false, /*scout_group=*/true);
+ // Scout pref off, so reporting is off.
+ ResetPrefs(/*scout_reporting=*/false, /*scout_group=*/true);
EXPECT_EQ(SBER_LEVEL_OFF, GetExtendedReportingLevel(prefs_));
- // Legacy pref on when we're using Scout - reporting remains off.
- ResetPrefs(/*sber=*/true, /*scout_reporting=*/false, /*scout_group=*/true);
+ // Scout pref off with the experiment group off, so reporting remains off.
+ ResetPrefs(/*scout_reporting=*/false, /*scout_group=*/true);
EXPECT_EQ(SBER_LEVEL_OFF, GetExtendedReportingLevel(prefs_));
// Turning on Scout gives us Scout level reporting
- ResetPrefs(/*sber=*/false, /*scout_reporting=*/true, /*scout_group=*/true);
- EXPECT_EQ(SBER_LEVEL_SCOUT, GetExtendedReportingLevel(prefs_));
- // .. and the legacy pref doesn't affect this.
- ResetPrefs(/*sber=*/true, /*scout_reporting=*/true, /*scout_group=*/true);
+ ResetPrefs(/*scout_reporting=*/true, /*scout_group=*/true);
EXPECT_EQ(SBER_LEVEL_SCOUT, GetExtendedReportingLevel(prefs_));
}
diff --git a/chromium/components/safe_browsing/db/database_manager.h b/chromium/components/safe_browsing/db/database_manager.h
index d213ff90299..2f5514614d7 100644
--- a/chromium/components/safe_browsing/db/database_manager.h
+++ b/chromium/components/safe_browsing/db/database_manager.h
@@ -103,8 +103,6 @@ class SafeBrowsingDatabaseManager
virtual bool CanCheckResourceType(
content::ResourceType resource_type) const = 0;
- virtual bool CanCheckSubresourceFilter() const = 0;
-
// Returns true if the url's scheme can be checked.
virtual bool CanCheckUrl(const GURL& url) const = 0;
diff --git a/chromium/components/safe_browsing/db/test_database_manager.cc b/chromium/components/safe_browsing/db/test_database_manager.cc
index 7a84058b2b3..f9ec6bacc3b 100644
--- a/chromium/components/safe_browsing/db/test_database_manager.cc
+++ b/chromium/components/safe_browsing/db/test_database_manager.cc
@@ -23,10 +23,6 @@ bool TestSafeBrowsingDatabaseManager::CanCheckResourceType(
return false;
}
-bool TestSafeBrowsingDatabaseManager::CanCheckSubresourceFilter() const {
- return false;
-}
-
bool TestSafeBrowsingDatabaseManager::CanCheckUrl(const GURL& url) const {
NOTIMPLEMENTED();
return false;
diff --git a/chromium/components/safe_browsing/db/test_database_manager.h b/chromium/components/safe_browsing/db/test_database_manager.h
index 167431bb824..5f0ae5bbad6 100644
--- a/chromium/components/safe_browsing/db/test_database_manager.h
+++ b/chromium/components/safe_browsing/db/test_database_manager.h
@@ -24,7 +24,6 @@ class TestSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager {
void CancelCheck(Client* client) override;
bool CanCheckResourceType(content::ResourceType resource_type) const override;
bool CanCheckUrl(const GURL& url) const override;
- bool CanCheckSubresourceFilter() const override;
bool ChecksAreAlwaysAsync() const override;
bool CheckBrowseUrl(const GURL& url,
const SBThreatTypeSet& threat_types,
diff --git a/chromium/components/safe_browsing/db/v4_get_hash_protocol_manager.cc b/chromium/components/safe_browsing/db/v4_get_hash_protocol_manager.cc
index e3589ea35ec..da067cbd07f 100644
--- a/chromium/components/safe_browsing/db/v4_get_hash_protocol_manager.cc
+++ b/chromium/components/safe_browsing/db/v4_get_hash_protocol_manager.cc
@@ -531,8 +531,7 @@ void V4GetHashProtocolManager::OnFullHashForApi(
ThreatMetadata md;
for (const FullHashInfo& full_hash_info : full_hash_infos) {
DCHECK_EQ(GetChromeUrlApiId(), full_hash_info.list_id);
- DCHECK(std::find(full_hashes.begin(), full_hashes.end(),
- full_hash_info.full_hash) != full_hashes.end());
+ DCHECK(base::ContainsValue(full_hashes, full_hash_info.full_hash));
md.api_permissions.insert(full_hash_info.metadata.api_permissions.begin(),
full_hash_info.metadata.api_permissions.end());
}
@@ -703,6 +702,7 @@ void V4GetHashProtocolManager::ParseMetadata(const ThreatMatch& match,
void V4GetHashProtocolManager::ResetGetHashErrors() {
gethash_error_count_ = 0;
gethash_back_off_mult_ = 1;
+ next_gethash_time_ = base::Time();
}
void V4GetHashProtocolManager::SetClockForTests(base::Clock* clock) {
diff --git a/chromium/components/safe_browsing/db/v4_get_hash_protocol_manager.h b/chromium/components/safe_browsing/db/v4_get_hash_protocol_manager.h
index a8bf7dfde2f..ba19ed16a18 100644
--- a/chromium/components/safe_browsing/db/v4_get_hash_protocol_manager.h
+++ b/chromium/components/safe_browsing/db/v4_get_hash_protocol_manager.h
@@ -221,6 +221,8 @@ class V4GetHashProtocolManager {
TestGetHashErrorHandlingNetwork);
FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
TestGetHashErrorHandlingResponseCode);
+ FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
+ TestGetHashErrorHandlingParallelRequests);
FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, GetCachedResults);
FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, TestUpdatesAreMerged);
friend class V4GetHashProtocolManagerTest;
diff --git a/chromium/components/safe_browsing/db/v4_get_hash_protocol_manager_unittest.cc b/chromium/components/safe_browsing/db/v4_get_hash_protocol_manager_unittest.cc
index 92a253fab24..46f0067c739 100644
--- a/chromium/components/safe_browsing/db/v4_get_hash_protocol_manager_unittest.cc
+++ b/chromium/components/safe_browsing/db/v4_get_hash_protocol_manager_unittest.cc
@@ -11,6 +11,7 @@
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
#include "components/safe_browsing/db/safebrowsing.pb.h"
@@ -93,6 +94,24 @@ class V4GetHashProtocolManagerTest : public PlatformTest {
response_code, data);
}
+ static void SetupFullHashFetcherToReturnResponse(V4GetHashProtocolManager* pm,
+ const FullHash& full_hash,
+ int net_error,
+ int response_code,
+ const std::string& data) {
+ network::SimpleURLLoader* url_loader = nullptr;
+ for (const auto& pending_request : pm->pending_hash_requests_) {
+ if (pending_request.second->full_hash_to_store_and_hash_prefixes.count(
+ full_hash)) {
+ EXPECT_EQ(nullptr, url_loader);
+ url_loader = pending_request.second->loader.get();
+ }
+ }
+ ASSERT_TRUE(url_loader);
+
+ pm->OnURLLoaderCompleteInternal(url_loader, net_error, response_code, data);
+ }
+
static void SetupFetcherToReturnOKResponse(
V4GetHashProtocolManager* pm,
const std::vector<ResponseInfo>& infos) {
@@ -133,7 +152,8 @@ class V4GetHashProtocolManagerTest : public PlatformTest {
callback_called_ = true;
}
- bool callback_called() { return callback_called_; }
+ bool callback_called() const { return callback_called_; }
+ void reset_callback_called() { callback_called_ = false; }
private:
static std::string GetV4HashResponse(
@@ -214,6 +234,71 @@ TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingResponseCode) {
EXPECT_TRUE(callback_called());
}
+TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingParallelRequests) {
+ std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+ std::vector<FullHashInfo> empty_results;
+ base::HistogramTester histogram_tester;
+
+ FullHashToStoreAndHashPrefixesMap matched_locally1;
+ matched_locally1[FullHash("AHash1Full")].emplace_back(GetUrlSocEngId(),
+ HashPrefix("AHash1"));
+ pm->GetFullHashes(matched_locally1, {},
+ base::BindRepeating(
+ &V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
+ base::Unretained(this), empty_results));
+
+ FullHashToStoreAndHashPrefixesMap matched_locally2;
+ matched_locally2[FullHash("AHash2Full")].emplace_back(GetUrlSocEngId(),
+ HashPrefix("AHash2"));
+ pm->GetFullHashes(matched_locally2, {},
+ base::BindRepeating(
+ &V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
+ base::Unretained(this), empty_results));
+
+ // Fail the first request.
+ SetupFullHashFetcherToReturnResponse(pm.get(), FullHash("AHash1Full"),
+ net::ERR_CONNECTION_RESET, 200,
+ GetStockV4HashResponse());
+
+ // Should have recorded one error, but back off multiplier is unchanged.
+ EXPECT_EQ(1ul, pm->gethash_error_count_);
+ EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
+ EXPECT_TRUE(callback_called());
+ histogram_tester.ExpectUniqueSample("SafeBrowsing.V4GetHash.Result",
+ V4OperationResult::NETWORK_ERROR, 1);
+
+ reset_callback_called();
+
+ // Comple the second request successfully.
+ SetupFullHashFetcherToReturnResponse(pm.get(), FullHash("AHash2Full"),
+ net::OK, 200, GetStockV4HashResponse());
+
+ // Error counters are reset.
+ EXPECT_EQ(0ul, pm->gethash_error_count_);
+ EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
+ EXPECT_TRUE(callback_called());
+ histogram_tester.ExpectBucketCount("SafeBrowsing.V4GetHash.Result",
+ V4OperationResult::STATUS_200, 1);
+
+ reset_callback_called();
+
+ // Start the third request.
+ FullHashToStoreAndHashPrefixesMap matched_locally3;
+ matched_locally3[FullHash("AHash3Full")].emplace_back(GetUrlSocEngId(),
+ HashPrefix("AHash3"));
+ pm->GetFullHashes(matched_locally3, {},
+ base::BindRepeating(
+ &V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
+ base::Unretained(this), empty_results));
+
+ // The request is not failed right away.
+ EXPECT_FALSE(callback_called());
+ // The request is not reported as MIN_WAIT_DURATION_ERROR.
+ histogram_tester.ExpectBucketCount("SafeBrowsing.V4GetHash.Result",
+ V4OperationResult::MIN_WAIT_DURATION_ERROR,
+ 0);
+}
+
TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingOK) {
std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
diff --git a/chromium/components/safe_browsing/db/v4_local_database_manager.cc b/chromium/components/safe_browsing/db/v4_local_database_manager.cc
index 786b7a324b5..f3c5b5867bb 100644
--- a/chromium/components/safe_browsing/db/v4_local_database_manager.cc
+++ b/chromium/components/safe_browsing/db/v4_local_database_manager.cc
@@ -294,10 +294,6 @@ bool V4LocalDatabaseManager::CanCheckResourceType(
return true;
}
-bool V4LocalDatabaseManager::CanCheckSubresourceFilter() const {
- return true;
-}
-
bool V4LocalDatabaseManager::CanCheckUrl(const GURL& url) const {
return url.SchemeIsHTTPOrHTTPS() || url.SchemeIs(url::kFtpScheme) ||
url.SchemeIsWSOrWSS();
@@ -382,7 +378,6 @@ bool V4LocalDatabaseManager::CheckResourceUrl(const GURL& url, Client* client) {
bool V4LocalDatabaseManager::CheckUrlForSubresourceFilter(const GURL& url,
Client* client) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
- DCHECK(CanCheckSubresourceFilter());
StoresToCheck stores_to_check(
{GetUrlSocEngId(), GetUrlSubresourceFilterId()});
diff --git a/chromium/components/safe_browsing/db/v4_local_database_manager.h b/chromium/components/safe_browsing/db/v4_local_database_manager.h
index d7399b97902..b3eb420b24e 100644
--- a/chromium/components/safe_browsing/db/v4_local_database_manager.h
+++ b/chromium/components/safe_browsing/db/v4_local_database_manager.h
@@ -52,7 +52,6 @@ class V4LocalDatabaseManager : public SafeBrowsingDatabaseManager {
void CancelCheck(Client* client) override;
bool CanCheckResourceType(content::ResourceType resource_type) const override;
- bool CanCheckSubresourceFilter() const override;
bool CanCheckUrl(const GURL& url) const override;
bool ChecksAreAlwaysAsync() const override;
bool CheckBrowseUrl(const GURL& url,
diff --git a/chromium/components/safe_browsing/db/v4_protocol_manager_util.h b/chromium/components/safe_browsing/db/v4_protocol_manager_util.h
index 53033ae3955..e89a204ccf8 100644
--- a/chromium/components/safe_browsing/db/v4_protocol_manager_util.h
+++ b/chromium/components/safe_browsing/db/v4_protocol_manager_util.h
@@ -129,14 +129,17 @@ enum SBThreatType {
// DEPRECATED. Url detected by password protection service.
DEPRECATED_SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING,
- // Password reuse detected on low reputation page,
- SB_THREAT_TYPE_PASSWORD_REUSE,
+ // Chrome sign in password reuse detected on low reputation page,
+ SB_THREAT_TYPE_SIGN_IN_PASSWORD_REUSE,
// A sample of an ad was collected
SB_THREAT_TYPE_AD_SAMPLE,
// The page loaded a resource from the Suspicious Site list.
SB_THREAT_TYPE_SUSPICIOUS_SITE,
+
+ // Enterprise password reuse detected on low reputation page,
+ SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE,
};
using SBThreatTypeSet = base::flat_set<SBThreatType>;
diff --git a/chromium/components/safe_browsing/db/v4_protocol_manager_util_unittest.cc b/chromium/components/safe_browsing/db/v4_protocol_manager_util_unittest.cc
index b346da2b83d..f925b8ec37e 100644
--- a/chromium/components/safe_browsing/db/v4_protocol_manager_util_unittest.cc
+++ b/chromium/components/safe_browsing/db/v4_protocol_manager_util_unittest.cc
@@ -7,6 +7,7 @@
#include <vector>
#include "base/base64.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "components/safe_browsing/db/v4_test_util.h"
@@ -17,15 +18,6 @@
using base::Time;
using base::TimeDelta;
-namespace {
-
-bool VectorContains(const std::vector<std::string>& data,
- const std::string& str) {
- return std::find(data.begin(), data.end(), str) != data.end();
-}
-
-} // namespace
-
namespace safe_browsing {
class V4ProtocolManagerUtilTest : public testing::Test {};
@@ -133,10 +125,10 @@ TEST_F(V4ProtocolManagerUtilTest, UrlParsing) {
EXPECT_EQ(hosts[0], "b.c");
EXPECT_EQ(hosts[1], "a.b.c");
- EXPECT_TRUE(VectorContains(paths, "/1/2.html?param=1"));
- EXPECT_TRUE(VectorContains(paths, "/1/2.html"));
- EXPECT_TRUE(VectorContains(paths, "/1/"));
- EXPECT_TRUE(VectorContains(paths, "/"));
+ EXPECT_TRUE(base::ContainsValue(paths, "/1/2.html?param=1"));
+ EXPECT_TRUE(base::ContainsValue(paths, "/1/2.html"));
+ EXPECT_TRUE(base::ContainsValue(paths, "/1/"));
+ EXPECT_TRUE(base::ContainsValue(paths, "/"));
url = GURL("http://a.b.c.d.e.f.g/1.html");
V4ProtocolManagerUtil::GenerateHostsToCheck(url, &hosts);
@@ -148,15 +140,15 @@ TEST_F(V4ProtocolManagerUtilTest, UrlParsing) {
EXPECT_EQ(hosts[2], "d.e.f.g");
EXPECT_EQ(hosts[3], "c.d.e.f.g");
EXPECT_EQ(hosts[4], "a.b.c.d.e.f.g");
- EXPECT_TRUE(VectorContains(paths, "/1.html"));
- EXPECT_TRUE(VectorContains(paths, "/"));
+ EXPECT_TRUE(base::ContainsValue(paths, "/1.html"));
+ EXPECT_TRUE(base::ContainsValue(paths, "/"));
url = GURL("http://a.b/saw-cgi/eBayISAPI.dll/");
V4ProtocolManagerUtil::GeneratePathsToCheck(url, &paths);
EXPECT_EQ(paths.size(), static_cast<size_t>(3));
- EXPECT_TRUE(VectorContains(paths, "/saw-cgi/eBayISAPI.dll/"));
- EXPECT_TRUE(VectorContains(paths, "/saw-cgi/"));
- EXPECT_TRUE(VectorContains(paths, "/"));
+ EXPECT_TRUE(base::ContainsValue(paths, "/saw-cgi/eBayISAPI.dll/"));
+ EXPECT_TRUE(base::ContainsValue(paths, "/saw-cgi/"));
+ EXPECT_TRUE(base::ContainsValue(paths, "/"));
}
// Tests the url canonicalization according to the Safe Browsing spec.
diff --git a/chromium/components/safe_browsing/features.cc b/chromium/components/safe_browsing/features.cc
index 605f52b3926..06899184cfb 100644
--- a/chromium/components/safe_browsing/features.cc
+++ b/chromium/components/safe_browsing/features.cc
@@ -17,12 +17,6 @@ namespace safe_browsing {
// them to the ExperimentalFeaturesList below to start displaying their status
// on the chrome://safe-browsing page.
-// Allows an ad sample report to be created but not sent. Used to measure
-// performance impact of report generation.
-const base::Feature kAdSamplerCollectButDontSendFeature{
- "SafeBrowsingAdSamplerCollectButDontSend",
- base::FEATURE_DISABLED_BY_DEFAULT};
-
// Controls various parameters related to occasionally collecting ad samples,
// for example to control how often collection should occur.
const base::Feature kAdSamplerTriggerFeature{"SafeBrowsingAdSamplerTrigger",
@@ -42,8 +36,7 @@ const base::Feature kThreatDomDetailsTagAndAttributeFeature{
"ThreatDomDetailsTagAttributes", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kSuspiciousSiteTriggerQuotaFeature{
- "SafeBrowsingSuspiciousSiteTriggerQuota",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ "SafeBrowsingSuspiciousSiteTriggerQuota", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kTriggerThrottlerDailyQuotaFeature{
"SafeBrowsingTriggerThrottlerDailyQuota",
@@ -53,7 +46,7 @@ const base::Feature kInspectDownloadedRarFiles{
"InspectDownloadedRarFiles", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kEnterprisePasswordProtectionV1{
- "EnterprisePasswordProtectionV1", base::FEATURE_DISABLED_BY_DEFAULT};
+ "EnterprisePasswordProtectionV1", base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kForceEnableResetPasswordWebUI{
"ForceEnableResetPasswordWebUI", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -67,12 +60,11 @@ constexpr struct {
// True if the feature is running at a probability other than 1 or 0.
bool probabilistically_enabled;
} kExperimentalFeatures[]{
- {&kAdSamplerCollectButDontSendFeature, false},
{&kAdSamplerTriggerFeature, false},
{&kCheckByURLLoaderThrottle, true},
- {&kEnterprisePasswordProtectionV1, true},
- {&kForceEnableResetPasswordWebUI, false},
+ {&kForceEnableResetPasswordWebUI, true},
{&kInspectDownloadedRarFiles, true},
+ {&kSuspiciousSiteTriggerQuotaFeature, true},
{&kThreatDomDetailsTagAndAttributeFeature, false},
{&kTriggerThrottlerDailyQuotaFeature, false},
};
diff --git a/chromium/components/safe_browsing/features.h b/chromium/components/safe_browsing/features.h
index dd913aa6920..c1a12133232 100644
--- a/chromium/components/safe_browsing/features.h
+++ b/chromium/components/safe_browsing/features.h
@@ -19,7 +19,6 @@ class ListValue;
namespace safe_browsing {
// Features list
-extern const base::Feature kAdSamplerCollectButDontSendFeature;
extern const base::Feature kAdSamplerTriggerFeature;
extern const base::Feature kCheckByURLLoaderThrottle;
diff --git a/chromium/components/safe_browsing/password_protection/BUILD.gn b/chromium/components/safe_browsing/password_protection/BUILD.gn
index d219c28f91b..f2f640f3d42 100644
--- a/chromium/components/safe_browsing/password_protection/BUILD.gn
+++ b/chromium/components/safe_browsing/password_protection/BUILD.gn
@@ -30,6 +30,7 @@ source_set("password_protection") {
"//components/safe_browsing/db:database_manager",
"//components/safe_browsing/db:v4_protocol_manager_util",
"//components/safe_browsing/db:whitelist_checker_client",
+ "//components/safe_browsing/web_ui:web_ui",
"//components/sessions",
"//content/public/browser:browser",
"//net:net",
diff --git a/chromium/components/safe_browsing/password_protection/DEPS b/chromium/components/safe_browsing/password_protection/DEPS
index 41ce05c016e..0aea96dcd01 100644
--- a/chromium/components/safe_browsing/password_protection/DEPS
+++ b/chromium/components/safe_browsing/password_protection/DEPS
@@ -1,6 +1,7 @@
include_rules = [
"+components/content_settings/core/browser",
"+components/history/core/browser",
+ "+components/password_manager/core/browser/password_manager_metrics_util.h",
"+components/password_manager/core/browser/password_reuse_detector.h",
"+components/sessions",
"+components/sync_preferences/testing_pref_service_syncable.h",
diff --git a/chromium/components/safe_browsing/password_protection/mock_password_protection_service.cc b/chromium/components/safe_browsing/password_protection/mock_password_protection_service.cc
index 3c372d0e596..6d17c2d07ec 100644
--- a/chromium/components/safe_browsing/password_protection/mock_password_protection_service.cc
+++ b/chromium/components/safe_browsing/password_protection/mock_password_protection_service.cc
@@ -16,10 +16,11 @@ MockPasswordProtectionService::MockPasswordProtectionService()
MockPasswordProtectionService::MockPasswordProtectionService(
const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ history::HistoryService* history_service,
scoped_refptr<HostContentSettingsMap> content_setting_map)
: PasswordProtectionService(database_manager,
url_loader_factory,
- nullptr,
+ history_service,
content_setting_map.get()) {}
MockPasswordProtectionService::~MockPasswordProtectionService() {}
diff --git a/chromium/components/safe_browsing/password_protection/mock_password_protection_service.h b/chromium/components/safe_browsing/password_protection/mock_password_protection_service.h
index 7accf1edb73..aafdf42dc89 100644
--- a/chromium/components/safe_browsing/password_protection/mock_password_protection_service.h
+++ b/chromium/components/safe_browsing/password_protection/mock_password_protection_service.h
@@ -17,6 +17,7 @@ class MockPasswordProtectionService : public PasswordProtectionService {
MockPasswordProtectionService(
const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ history::HistoryService* history_service,
scoped_refptr<HostContentSettingsMap> content_setting_map);
~MockPasswordProtectionService() override;
@@ -27,7 +28,7 @@ class MockPasswordProtectionService : public PasswordProtectionService {
MOCK_CONST_METHOD0(GetBrowserPolicyConnector,
const policy::BrowserPolicyConnector*());
MOCK_CONST_METHOD0(GetPasswordProtectionWarningTriggerPref,
- safe_browsing::PasswordProtectionTrigger());
+ PasswordProtectionTrigger());
MOCK_CONST_METHOD2(IsURLWhitelistedForPasswordEntry,
bool(const GURL&, RequestOutcome*));
@@ -37,35 +38,39 @@ class MockPasswordProtectionService : public PasswordProtectionService {
MOCK_METHOD0(OnPolicySpecifiedPasswordChanged, void());
MOCK_METHOD1(MaybeLogPasswordReuseDetectedEvent, void(content::WebContents*));
MOCK_METHOD1(UserClickedThroughSBInterstitial, bool(content::WebContents*));
- MOCK_METHOD1(ShowInterstitial, void(content::WebContents*));
+ MOCK_METHOD2(ShowInterstitial,
+ void(content::WebContents*, ReusedPasswordType));
MOCK_METHOD2(IsPingingEnabled,
- bool(safe_browsing::LoginReputationClientRequest::TriggerType,
+ bool(LoginReputationClientRequest::TriggerType,
RequestOutcome*));
- MOCK_METHOD2(ShowModalWarning,
- void(content::WebContents*, const std::string&));
- MOCK_METHOD2(UpdateSecurityState,
- void(safe_browsing::SBThreatType, content::WebContents*));
+ MOCK_METHOD3(ShowModalWarning,
+ void(content::WebContents*,
+ const std::string&,
+ ReusedPasswordType));
+ MOCK_METHOD3(UpdateSecurityState,
+ void(safe_browsing::SBThreatType,
+ ReusedPasswordType,
+ content::WebContents*));
MOCK_METHOD2(RemoveUnhandledSyncPasswordReuseOnURLsDeleted,
void(bool, const history::URLRows&));
MOCK_METHOD2(OnPolicySpecifiedPasswordReuseDetected, void(const GURL&, bool));
MOCK_METHOD3(FillReferrerChain,
void(const GURL&,
SessionID,
- safe_browsing::LoginReputationClientRequest::Frame*));
+ LoginReputationClientRequest::Frame*));
MOCK_METHOD3(MaybeLogPasswordReuseLookupEvent,
void(content::WebContents*,
PasswordProtectionService::RequestOutcome,
const safe_browsing::LoginReputationClientResponse*));
- MOCK_METHOD3(OnUserAction,
- void(content::WebContents*, WarningUIType, WarningAction));
-
+ MOCK_METHOD3(CanShowInterstitial,
+ bool(RequestOutcome, ReusedPasswordType, const GURL&));
MOCK_METHOD4(
MaybeStartPasswordFieldOnFocusRequest,
void(content::WebContents*, const GURL&, const GURL&, const GURL&));
MOCK_METHOD5(MaybeStartProtectedPasswordEntryRequest,
void(content::WebContents*,
const GURL&,
- bool,
+ ReusedPasswordType,
const std::vector<std::string>&,
bool));
diff --git a/chromium/components/safe_browsing/password_protection/password_protection_request.cc b/chromium/components/safe_browsing/password_protection/password_protection_request.cc
index 06345bf5500..cc572c83da0 100644
--- a/chromium/components/safe_browsing/password_protection/password_protection_request.cc
+++ b/chromium/components/safe_browsing/password_protection/password_protection_request.cc
@@ -13,6 +13,7 @@
#include "components/password_manager/core/browser/password_reuse_detector.h"
#include "components/safe_browsing/db/whitelist_checker_client.h"
#include "components/safe_browsing/password_protection/password_protection_navigation_throttle.h"
+#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
#include "content/public/browser/web_contents.h"
#include "net/base/escape.h"
#include "net/base/load_flags.h"
@@ -43,13 +44,23 @@ const char kSyncPasswordEntryVerdictHistogram[] =
"PasswordProtection.Verdict.SyncPasswordEntry";
const char kProtectedPasswordEntryVerdictHistogram[] =
"PasswordProtection.Verdict.ProtectedPasswordEntry";
+const char kEnterprisePasswordEntryVerdictHistogram[] =
+ "PasswordProtection.Verdict.NonGaiaEnterprisePasswordEntry";
+const char kGSuiteSyncPasswordEntryVerdictHistogram[] =
+ "PasswordProtection.Verdict.GSuiteSyncPasswordEntry";
+const char kReferrerChainSizeOfSafeVerdictHistogram[] =
+ "PasswordProtection.ReferrerChainSize.Safe";
+const char kReferrerChainSizeOfPhishingVerdictHistogram[] =
+ "PasswordProtection.ReferrerChainSize.Phishing";
+const char kReferrerChainSizeOfLowRepVerdictHistogram[] =
+ "PasswordProtection.ReferrerChainSize.LowReputation";
PasswordProtectionRequest::PasswordProtectionRequest(
WebContents* web_contents,
const GURL& main_frame_url,
const GURL& password_form_action,
const GURL& password_form_frame_url,
- bool matches_sync_password,
+ ReusedPasswordType reused_password_type,
const std::vector<std::string>& matching_domains,
LoginReputationClientRequest::TriggerType type,
bool password_field_exists,
@@ -59,7 +70,7 @@ PasswordProtectionRequest::PasswordProtectionRequest(
main_frame_url_(main_frame_url),
password_form_action_(password_form_action),
password_form_frame_url_(password_form_frame_url),
- matches_sync_password_(matches_sync_password),
+ reused_password_type_(reused_password_type),
matching_domains_(matching_domains),
trigger_type_(type),
password_field_exists_(password_field_exists),
@@ -73,7 +84,9 @@ PasswordProtectionRequest::PasswordProtectionRequest(
DCHECK(trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
trigger_type_ == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
DCHECK(trigger_type_ != LoginReputationClientRequest::PASSWORD_REUSE_EVENT ||
- matches_sync_password_ || matching_domains_.size() > 0);
+ reused_password_type_ !=
+ LoginReputationClientRequest::PasswordReuseEvent::SAVED_PASSWORD ||
+ matching_domains_.size() > 0);
request_proto_->set_trigger_type(trigger_type_);
}
@@ -130,7 +143,8 @@ void PasswordProtectionRequest::CheckCachedVerdicts() {
std::unique_ptr<LoginReputationClientResponse> cached_response =
std::make_unique<LoginReputationClientResponse>();
auto verdict = password_protection_service_->GetCachedVerdict(
- main_frame_url_, trigger_type_, cached_response.get());
+ main_frame_url_, trigger_type_, reused_password_type_,
+ cached_response.get());
if (verdict != LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED)
Finish(PasswordProtectionService::RESPONSE_ALREADY_CACHED,
std::move(cached_response));
@@ -156,6 +170,8 @@ void PasswordProtectionRequest::FillRequestProto() {
request_proto_->set_clicked_through_interstitial(
clicked_through_interstitial);
+ request_proto_->set_content_type(web_contents_->GetContentsMimeType());
+
switch (trigger_type_) {
case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE: {
UMA_HISTOGRAM_BOOLEAN(
@@ -187,14 +203,18 @@ void PasswordProtectionRequest::FillRequestProto() {
main_frame->set_has_password_field(password_field_exists_);
LoginReputationClientRequest::PasswordReuseEvent* reuse_event =
request_proto_->mutable_password_reuse_event();
- reuse_event->set_is_chrome_signin_password(matches_sync_password_);
- if (matches_sync_password_) {
+ bool matches_sync_password =
+ reused_password_type_ ==
+ LoginReputationClientRequest::PasswordReuseEvent::SIGN_IN_PASSWORD;
+ reuse_event->set_is_chrome_signin_password(matches_sync_password);
+ if (matches_sync_password) {
UMA_HISTOGRAM_BOOLEAN(
"PasswordProtection.UserClickedThroughSBInterstitial."
"SyncPasswordEntry",
clicked_through_interstitial);
reuse_event->set_sync_account_type(
password_protection_service_->GetSyncAccountType());
+ reuse_event->set_reused_password_type(reused_password_type_);
UMA_HISTOGRAM_ENUMERATION(
"PasswordProtection.PasswordReuseSyncAccountType",
reuse_event->sync_account_type(),
@@ -227,6 +247,9 @@ void PasswordProtectionRequest::SendRequest() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
FillRequestProto();
+ web_ui_token_ =
+ WebUIInfoSingleton::GetInstance()->AddToPGPings(*request_proto_);
+
std::string serialized_request;
if (!request_proto_->SerializeToString(&serialized_request)) {
Finish(PasswordProtectionService::REQUEST_MALFORMED, nullptr);
@@ -320,10 +343,13 @@ void PasswordProtectionRequest::OnURLLoaderComplete(
url_loader_.reset(); // We don't need it anymore.
UMA_HISTOGRAM_TIMES("PasswordProtection.RequestNetworkDuration",
base::TimeTicks::Now() - request_start_time_);
- if (response_body && response->ParseFromString(*response_body))
+ if (response_body && response->ParseFromString(*response_body)) {
+ WebUIInfoSingleton::GetInstance()->AddToPGResponses(web_ui_token_,
+ *response);
Finish(PasswordProtectionService::SUCCEEDED, std::move(response));
- else
+ } else {
Finish(PasswordProtectionService::RESPONSE_MALFORMED, nullptr);
+ }
}
void PasswordProtectionRequest::Finish(
@@ -335,9 +361,10 @@ void PasswordProtectionRequest::Finish(
UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogram, outcome,
PasswordProtectionService::MAX_OUTCOME);
} else {
- PasswordProtectionService::LogPasswordEntryRequestOutcome(
- outcome, matches_sync_password_);
- if (matches_sync_password_) {
+ password_protection_service_->LogPasswordEntryRequestOutcome(
+ outcome, reused_password_type_);
+ if (reused_password_type_ ==
+ LoginReputationClientRequest::PasswordReuseEvent::SIGN_IN_PASSWORD) {
password_protection_service_->MaybeLogPasswordReuseLookupEvent(
web_contents_, outcome, response.get());
}
@@ -354,10 +381,25 @@ void PasswordProtectionRequest::Finish(
UMA_HISTOGRAM_ENUMERATION(
kAnyPasswordEntryVerdictHistogram, response->verdict_type(),
LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1);
- if (matches_sync_password_) {
+ if (reused_password_type_ == LoginReputationClientRequest::
+ PasswordReuseEvent::SIGN_IN_PASSWORD) {
+ if (password_protection_service_->GetSyncAccountType() ==
+ LoginReputationClientRequest::PasswordReuseEvent::GSUITE) {
+ UMA_HISTOGRAM_ENUMERATION(
+ kGSuiteSyncPasswordEntryVerdictHistogram,
+ response->verdict_type(),
+ LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1);
+ }
UMA_HISTOGRAM_ENUMERATION(
kSyncPasswordEntryVerdictHistogram, response->verdict_type(),
LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1);
+ } else if (reused_password_type_ ==
+ LoginReputationClientRequest::PasswordReuseEvent::
+ ENTERPRISE_PASSWORD) {
+ UMA_HISTOGRAM_ENUMERATION(
+ kEnterprisePasswordEntryVerdictHistogram,
+ response->verdict_type(),
+ LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1);
} else {
UMA_HISTOGRAM_ENUMERATION(
kProtectedPasswordEntryVerdictHistogram, response->verdict_type(),
@@ -367,6 +409,11 @@ void PasswordProtectionRequest::Finish(
default:
NOTREACHED();
}
+ int referrer_chain_size =
+ request_proto_->frames_size() > 0
+ ? request_proto_->frames(0).referrer_chain_size()
+ : 0;
+ LogReferrerChainSize(response->verdict_type(), referrer_chain_size);
}
password_protection_service_->RequestFinished(
@@ -397,4 +444,26 @@ void PasswordProtectionRequest::HandleDeferredNavigations() {
throttles_.clear();
}
+void PasswordProtectionRequest::LogReferrerChainSize(
+ LoginReputationClientResponse::VerdictType verdict_type,
+ int referrer_chain_size) {
+ switch (verdict_type) {
+ case LoginReputationClientResponse::SAFE:
+ UMA_HISTOGRAM_COUNTS_100(kReferrerChainSizeOfSafeVerdictHistogram,
+ referrer_chain_size);
+ return;
+ case LoginReputationClientResponse::LOW_REPUTATION:
+ UMA_HISTOGRAM_COUNTS_100(kReferrerChainSizeOfLowRepVerdictHistogram,
+ referrer_chain_size);
+ return;
+ case LoginReputationClientResponse::PHISHING:
+ UMA_HISTOGRAM_COUNTS_100(kReferrerChainSizeOfPhishingVerdictHistogram,
+ referrer_chain_size);
+ return;
+ case LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED:
+ break;
+ }
+ NOTREACHED();
+}
+
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/password_protection/password_protection_request.h b/chromium/components/safe_browsing/password_protection/password_protection_request.h
index 7405508b270..80cee479f79 100644
--- a/chromium/components/safe_browsing/password_protection/password_protection_request.h
+++ b/chromium/components/safe_browsing/password_protection/password_protection_request.h
@@ -28,6 +28,11 @@ extern const char kPasswordOnFocusVerdictHistogram[];
extern const char kAnyPasswordEntryVerdictHistogram[];
extern const char kSyncPasswordEntryVerdictHistogram[];
extern const char kProtectedPasswordEntryVerdictHistogram[];
+extern const char kEnterprisePasswordEntryVerdictHistogram[];
+extern const char kGSuiteSyncPasswordEntryVerdictHistogram[];
+extern const char kReferrerChainSizeOfSafeVerdictHistogram[];
+extern const char kReferrerChainSizeOfPhishingVerdictHistogram[];
+extern const char kReferrerChainSizeOfLowRepVerdictHistogram[];
// A request for checking if an unfamiliar login form or a password reuse event
// is safe. PasswordProtectionRequest objects are owned by
@@ -55,7 +60,7 @@ class PasswordProtectionRequest
const GURL& main_frame_url,
const GURL& password_form_action,
const GURL& password_form_frame_url,
- bool matches_sync_password,
+ ReusedPasswordType reused_password_type,
const std::vector<std::string>& matching_origins,
LoginReputationClientRequest::TriggerType type,
bool password_field_exists,
@@ -89,7 +94,9 @@ class PasswordProtectionRequest
return trigger_type_;
}
- bool matches_sync_password() { return matches_sync_password_; }
+ ReusedPasswordType reused_password_type() const {
+ return reused_password_type_;
+ }
bool is_modal_warning_showing() const { return is_modal_warning_showing_; }
@@ -147,6 +154,12 @@ class PasswordProtectionRequest
void Finish(PasswordProtectionService::RequestOutcome outcome,
std::unique_ptr<LoginReputationClientResponse> response);
+ // TODO(crbug.com/854314): Move this function to a separate util file.
+ // Logs the size of referrer chain by verdict type.
+ void LogReferrerChainSize(
+ LoginReputationClientResponse::VerdictType verdict_type,
+ int referrer_chain_size);
+
// WebContents of the password protection event.
content::WebContents* web_contents_;
@@ -159,12 +172,12 @@ class PasswordProtectionRequest
// Frame url of the detected password form.
const GURL password_form_frame_url_;
- // True if the password is the sync/Google password.
- const bool matches_sync_password_;
+ // Type of the reused password.
+ const ReusedPasswordType reused_password_type_;
// Domains from the Password Manager that match this password.
- // Should be non-empty if |matches_sync_password_| == false. Otherwise,
- // may or may not be empty.
+ // Should be non-empty if |reused_password_type_| == SAVED_PASSWORD.
+ // Otherwise, may or may not be empty.
const std::vector<std::string> matching_domains_;
// If this request is for unfamiliar login page or for a password reuse event.
@@ -200,6 +213,9 @@ class PasswordProtectionRequest
// Whether there is a modal warning triggered by this request.
bool is_modal_warning_showing_;
+ // If a request is sent, this is the token returned by the WebUI.
+ int web_ui_token_;
+
base::WeakPtrFactory<PasswordProtectionRequest> weakptr_factory_;
DISALLOW_COPY_AND_ASSIGN(PasswordProtectionRequest);
};
diff --git a/chromium/components/safe_browsing/password_protection/password_protection_service.cc b/chromium/components/safe_browsing/password_protection/password_protection_service.cc
index d393546c3cf..93df501aa64 100644
--- a/chromium/components/safe_browsing/password_protection/password_protection_service.cc
+++ b/chromium/components/safe_browsing/password_protection/password_protection_service.cc
@@ -15,6 +15,8 @@
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
@@ -37,9 +39,12 @@
using content::BrowserThread;
using content::WebContents;
using history::HistoryService;
+using password_manager::metrics_util::PasswordType;
namespace safe_browsing {
+using PasswordReuseEvent = LoginReputationClientRequest::PasswordReuseEvent;
+
namespace {
// Keys for storing password protection verdict into a DictionaryValue.
@@ -79,6 +84,7 @@ GURL GetHostNameWithHTTPScheme(const GURL& url) {
} // namespace
+// TODO(jialiul): Move all UMA consts and functions to a separate file.
const char kPasswordOnFocusRequestOutcomeHistogram[] =
"PasswordProtection.RequestOutcome.PasswordFieldOnFocus";
// Matches sync and/or saved password
@@ -98,6 +104,25 @@ const char kSyncPasswordChromeSettingsHistogram[] =
"PasswordProtection.ChromeSettingsAction.SyncPasswordEntry";
const char kSyncPasswordInterstitialHistogram[] =
"PasswordProtection.InterstitialAction.SyncPasswordEntry";
+const char kEnterprisePasswordWarningDialogHistogram[] =
+ "PasswordProtection.ModalWarningDialogAction."
+ "NonGaiaEnterprisePasswordEntry";
+const char kEnterprisePasswordPageInfoHistogram[] =
+ "PasswordProtection.PageInfoAction.NonGaiaEnterprisePasswordEntry";
+const char kEnterprisePasswordInterstitialHistogram[] =
+ "PasswordProtection.InterstitialAction.NonGaiaEnterprisePasswordEntry";
+const char kEnterprisePasswordEntryRequestOutcomeHistogram[] =
+ "PasswordProtection.RequestOutcome.NonGaiaEnterprisePasswordEntry";
+const char kGSuiteSyncPasswordWarningDialogHistogram[] =
+ "PasswordProtection.ModalWarningDialogAction.GSuiteSyncPasswordEntry";
+const char kGSuiteSyncPasswordPageInfoHistogram[] =
+ "PasswordProtection.PageInfoAction.GSuiteSyncPasswordEntry";
+const char kGSuiteSyncPasswordInterstitialHistogram[] =
+ "PasswordProtection.InterstitialAction.GSuiteSyncPasswordEntry";
+const char kGSuiteSyncPasswordEntryRequestOutcomeHistogram[] =
+ "PasswordProtection.RequestOutcome.GSuiteSyncPasswordEntry";
+const char kInterstitialActionByUserNavigationHistogram[] =
+ "PasswordProtection.InterstitialActionByUserNavigation";
PasswordProtectionService::PasswordProtectionService(
const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
@@ -114,6 +139,9 @@ PasswordProtectionService::PasswordProtectionService(
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (history_service)
history_service_observer_.Add(history_service);
+
+ // TODO(jialiul): Remove this code when migration is done.
+ MigrateCachedVerdicts();
}
PasswordProtectionService::~PasswordProtectionService() {
@@ -132,24 +160,66 @@ bool PasswordProtectionService::CanGetReputationOfURL(const GURL& url) {
hostname.find('.') != std::string::npos;
}
-void PasswordProtectionService::RecordWarningAction(WarningUIType ui_type,
- WarningAction action) {
+void PasswordProtectionService::RecordWarningAction(
+ WarningUIType ui_type,
+ WarningAction action,
+ ReusedPasswordType password_type) {
+ // |password_type| can be unknown if user directly navigates to
+ // chrome://reset-password page. In this case, do not record user action.
+ if (password_type == PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN &&
+ ui_type == INTERSTITIAL) {
+ base::UmaHistogramEnumeration(
+ "PasswordProtection.InterstitialActionByUserNavigation", action,
+ MAX_ACTION);
+ return;
+ }
+ bool is_sign_in_password =
+ password_type == PasswordReuseEvent::SIGN_IN_PASSWORD;
+ bool is_gsuite_user = GetSyncAccountType() == PasswordReuseEvent::GSUITE;
switch (ui_type) {
case PAGE_INFO:
- base::UmaHistogramEnumeration(kSyncPasswordPageInfoHistogram, action,
- MAX_ACTION);
+ if (is_sign_in_password) {
+ base::UmaHistogramEnumeration(kSyncPasswordPageInfoHistogram, action,
+ MAX_ACTION);
+ if (is_gsuite_user) {
+ base::UmaHistogramEnumeration(kGSuiteSyncPasswordPageInfoHistogram,
+ action, MAX_ACTION);
+ }
+ } else {
+ base::UmaHistogramEnumeration(kEnterprisePasswordPageInfoHistogram,
+ action, MAX_ACTION);
+ }
break;
case MODAL_DIALOG:
- base::UmaHistogramEnumeration(kSyncPasswordWarningDialogHistogram, action,
- MAX_ACTION);
+ if (is_sign_in_password) {
+ base::UmaHistogramEnumeration(kSyncPasswordWarningDialogHistogram,
+ action, MAX_ACTION);
+ if (is_gsuite_user) {
+ base::UmaHistogramEnumeration(
+ kGSuiteSyncPasswordWarningDialogHistogram, action, MAX_ACTION);
+ }
+ } else {
+ base::UmaHistogramEnumeration(kEnterprisePasswordWarningDialogHistogram,
+ action, MAX_ACTION);
+ }
break;
case CHROME_SETTINGS:
+ DCHECK(is_sign_in_password);
base::UmaHistogramEnumeration(kSyncPasswordChromeSettingsHistogram,
action, MAX_ACTION);
break;
case INTERSTITIAL:
- base::UmaHistogramEnumeration(kSyncPasswordInterstitialHistogram, action,
- MAX_ACTION);
+ if (is_sign_in_password) {
+ base::UmaHistogramEnumeration(kSyncPasswordInterstitialHistogram,
+ action, MAX_ACTION);
+ if (is_gsuite_user) {
+ base::UmaHistogramEnumeration(
+ kGSuiteSyncPasswordInterstitialHistogram, action, MAX_ACTION);
+ }
+ } else {
+ base::UmaHistogramEnumeration(kEnterprisePasswordInterstitialHistogram,
+ action, MAX_ACTION);
+ }
break;
case NOT_USED:
NOTREACHED();
@@ -159,12 +229,17 @@ void PasswordProtectionService::RecordWarningAction(WarningUIType ui_type,
bool PasswordProtectionService::ShouldShowModalWarning(
LoginReputationClientRequest::TriggerType trigger_type,
- bool matches_sync_password,
+ PasswordReuseEvent::ReusedPasswordType password_type,
LoginReputationClientResponse::VerdictType verdict_type) {
if (trigger_type != LoginReputationClientRequest::PASSWORD_REUSE_EVENT ||
- !matches_sync_password ||
- GetSyncAccountType() ==
- LoginReputationClientRequest::PasswordReuseEvent::NOT_SIGNED_IN) {
+ !IsSupportedPasswordTypeForModalWarning(password_type)) {
+ return false;
+ }
+
+ // Shows modal warning for sync password reuse only if user's currently logged
+ // in.
+ if (password_type == PasswordReuseEvent::SIGN_IN_PASSWORD &&
+ GetSyncAccountType() == PasswordReuseEvent::NOT_SIGNED_IN) {
return false;
}
@@ -178,15 +253,17 @@ bool PasswordProtectionService::ShouldShowModalWarning(
// verdicts are only enabled on extended reporting users, we cache them one
// layer lower in the content setting DictionaryValue than PASSWORD_REUSE_EVENT
// verdicts.
-// In other words, to cache a UNFAMILIAR_LOGIN_PAGE verdict we needs two levels
-// of keys: (1) origin, (2) cache expression returned in verdict.
-// To cache a PASSWORD_REUSE_EVENT, three levels of keys are used:
+// In other words, to cache a PASSWORD_REUSE_EVENT verdict we needs three levels
+// of keys: (1) origin, (2) password type, (3) cache expression
+// returned in verdict.
+// To cache a UNFAMILIAR_LOGIN_PAGE, three levels of keys are used:
// (1) origin, (2) 2nd level key is always |kPasswordOnFocusCacheKey|,
// (3) cache expression.
LoginReputationClientResponse::VerdictType
PasswordProtectionService::GetCachedVerdict(
const GURL& url,
LoginReputationClientRequest::TriggerType trigger_type,
+ ReusedPasswordType password_type,
LoginReputationClientResponse* out_response) {
DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
@@ -203,16 +280,18 @@ PasswordProtectionService::GetCachedVerdict(
if (!cache_dictionary || cache_dictionary->empty())
return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
- base::DictionaryValue* verdict_dictionary = nullptr;
+ base::Value* verdict_dictionary = nullptr;
if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
// All UNFAMILIAR_LOGIN_PAGE verdicts (a.k.a password on focus ping)
// are cached under |kPasswordOnFocusCacheKey|.
- if (!cache_dictionary->GetDictionaryWithoutPathExpansion(
- base::StringPiece(kPasswordOnFocusCacheKey), &verdict_dictionary)) {
+ verdict_dictionary = cache_dictionary->FindKey(kPasswordOnFocusCacheKey);
+ if (!verdict_dictionary)
return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
- }
} else {
- verdict_dictionary = cache_dictionary.get();
+ verdict_dictionary =
+ cache_dictionary->FindKey(base::NumberToString(password_type));
+ if (!verdict_dictionary)
+ return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
}
std::vector<std::string> paths;
@@ -223,20 +302,12 @@ PasswordProtectionService::GetCachedVerdict(
// For all the verdicts of the same origin, we key them by |cache_expression|.
// Its corresponding value is a DictionaryValue contains its creation time and
// the serialized verdict proto.
- for (base::DictionaryValue::Iterator it(*verdict_dictionary); !it.IsAtEnd();
- it.Advance()) {
- if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
- it.key() == base::StringPiece(kPasswordOnFocusCacheKey)) {
- continue;
- }
- base::DictionaryValue* verdict_entry = nullptr;
- verdict_dictionary->GetDictionaryWithoutPathExpansion(
- it.key() /* cache_expression */, &verdict_entry);
+ for (const auto& item : verdict_dictionary->DictItems()) {
int verdict_received_time;
LoginReputationClientResponse verdict;
// Ignore any entry that we cannot parse. These invalid entries will be
// cleaned up during shutdown.
- if (!ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict))
+ if (!ParseVerdictEntry(&item.second, &verdict_received_time, &verdict))
continue;
// Since password protection content settings are keyed by origin, we only
// need to compare the path part of the cache_expression and the given url.
@@ -263,6 +334,7 @@ PasswordProtectionService::GetCachedVerdict(
void PasswordProtectionService::CacheVerdict(
const GURL& url,
LoginReputationClientRequest::TriggerType trigger_type,
+ ReusedPasswordType password_type,
LoginReputationClientResponse* verdict,
const base::Time& receive_time) {
DCHECK(verdict);
@@ -297,7 +369,13 @@ void PasswordProtectionService::CacheVerdict(
kPasswordOnFocusCacheKey, base::Value(base::Value::Type::DICTIONARY));
}
} else {
- verdict_dictionary = cache_dictionary.get();
+ std::string password_type_key = base::NumberToString(password_type);
+ verdict_dictionary = cache_dictionary->FindKeyOfType(
+ password_type_key, base::Value::Type::DICTIONARY);
+ if (!verdict_dictionary) {
+ verdict_dictionary = cache_dictionary->SetKey(
+ password_type_key, base::Value(base::Value::Type::DICTIONARY));
+ }
}
// Increases stored verdict count if we haven't seen this cache expression
@@ -348,11 +426,11 @@ void PasswordProtectionService::CleanUpExpiredVerdicts() {
content_settings_->ClearSettingsForOneTypeWithPredicate(
CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
base::Time::Max(),
- base::Bind(&OriginMatchPrimaryPattern, primary_pattern_url));
+ base::BindRepeating(&OriginMatchPrimaryPattern, primary_pattern_url));
} else if (has_expired_password_on_focus_entry ||
has_expired_password_reuse_entry) {
// Set the website setting of this origin with the updated
- // |verdict_diectionary|.
+ // |cache_dictionary|.
content_settings_->SetWebsiteSettingDefaultScope(
primary_pattern_url, GURL(),
CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
@@ -366,7 +444,7 @@ void PasswordProtectionService::StartRequest(
const GURL& main_frame_url,
const GURL& password_form_action,
const GURL& password_form_frame_url,
- bool matches_sync_password,
+ ReusedPasswordType reused_password_type,
const std::vector<std::string>& matching_domains,
LoginReputationClientRequest::TriggerType trigger_type,
bool password_field_exists) {
@@ -374,7 +452,7 @@ void PasswordProtectionService::StartRequest(
scoped_refptr<PasswordProtectionRequest> request(
new PasswordProtectionRequest(
web_contents, main_frame_url, password_form_action,
- password_form_frame_url, matches_sync_password, matching_domains,
+ password_form_frame_url, reused_password_type, matching_domains,
trigger_type, password_field_exists, this, GetRequestTimeoutInMS()));
request->Start();
@@ -389,11 +467,12 @@ void PasswordProtectionService::MaybeStartPasswordFieldOnFocusRequest(
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RequestOutcome reason;
if (CanSendPing(LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
- main_frame_url, false, &reason)) {
+ main_frame_url,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN, &reason)) {
StartRequest(web_contents, main_frame_url, password_form_action,
password_form_frame_url,
- false, /* matches_sync_password: not used for this type */
- {}, /* matching_domains: not used for this type */
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
+ {}, /* matching_domains: not used for this type */
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
}
}
@@ -401,29 +480,31 @@ void PasswordProtectionService::MaybeStartPasswordFieldOnFocusRequest(
void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest(
WebContents* web_contents,
const GURL& main_frame_url,
- bool matches_sync_password,
+ ReusedPasswordType reused_password_type,
const std::vector<std::string>& matching_domains,
bool password_field_exists) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (!IsSupportedPasswordTypeForPinging(reused_password_type))
+ return;
+
RequestOutcome reason;
if (CanSendPing(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
- main_frame_url, matches_sync_password, &reason)) {
+ main_frame_url, reused_password_type, &reason)) {
StartRequest(web_contents, main_frame_url, GURL(), GURL(),
- matches_sync_password, matching_domains,
+ reused_password_type, matching_domains,
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
password_field_exists);
} else {
MaybeLogPasswordReuseLookupEvent(web_contents, reason, nullptr);
- if (reason == PASSWORD_ALERT_MODE && matches_sync_password) {
- ShowInterstitial(web_contents);
- }
+ if (CanShowInterstitial(reason, reused_password_type, main_frame_url))
+ ShowInterstitial(web_contents, reused_password_type);
}
}
bool PasswordProtectionService::CanSendPing(
LoginReputationClientRequest::TriggerType trigger_type,
const GURL& main_frame_url,
- bool matches_sync_password,
+ ReusedPasswordType password_type,
RequestOutcome* reason) {
*reason = URL_NOT_VALID_FOR_REPUTATION_COMPUTING;
if (IsPingingEnabled(trigger_type, reason) &&
@@ -431,7 +512,7 @@ bool PasswordProtectionService::CanSendPing(
CanGetReputationOfURL(main_frame_url)) {
return true;
}
- RecordNoPingingReason(trigger_type, *reason, matches_sync_password);
+ RecordNoPingingReason(trigger_type, *reason, password_type);
return false;
}
@@ -445,12 +526,14 @@ void PasswordProtectionService::RequestFinished(
if (response) {
if (!already_cached) {
CacheVerdict(request->main_frame_url(), request->trigger_type(),
- response.get(), base::Time::Now());
+ request->reused_password_type(), response.get(),
+ base::Time::Now());
}
if (ShouldShowModalWarning(request->trigger_type(),
- request->matches_sync_password(),
+ request->reused_password_type(),
response->verdict_type())) {
- ShowModalWarning(request->web_contents(), response->verdict_token());
+ ShowModalWarning(request->web_contents(), response->verdict_token(),
+ request->reused_password_type());
request->set_is_modal_warning_showing(true);
}
}
@@ -524,17 +607,12 @@ int PasswordProtectionService::GetStoredVerdictCount(
GURL(source.primary_pattern.ToString()), GURL(),
CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr));
if (cache_dictionary.get() && !cache_dictionary->empty()) {
- stored_verdict_count_password_entry_ +=
- static_cast<int>(cache_dictionary->size());
- base::DictionaryValue* password_on_focus_dict = nullptr;
- if (cache_dictionary->GetDictionaryWithoutPathExpansion(
- base::StringPiece(kPasswordOnFocusCacheKey),
- &password_on_focus_dict)) {
- // Substracts 1 from password_entry count if |kPasswordOnFocusCacheKey|
- // presents.
- stored_verdict_count_password_entry_ -= 1;
- stored_verdict_count_password_on_focus_ +=
- static_cast<int>(password_on_focus_dict->size());
+ for (const auto& item : cache_dictionary->DictItems()) {
+ if (item.first == base::StringPiece(kPasswordOnFocusCacheKey)) {
+ stored_verdict_count_password_on_focus_ += item.second.DictSize();
+ } else {
+ stored_verdict_count_password_entry_ += item.second.DictSize();
+ }
}
}
}
@@ -562,15 +640,16 @@ void PasswordProtectionService::OnURLsDeleted(
const history::DeletionInfo& deletion_info) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
- base::Bind(&PasswordProtectionService::RemoveContentSettingsOnURLsDeleted,
- GetWeakPtr(), deletion_info.IsAllHistory(),
- deletion_info.deleted_rows()));
+ base::BindRepeating(
+ &PasswordProtectionService::RemoveContentSettingsOnURLsDeleted,
+ GetWeakPtr(), deletion_info.IsAllHistory(),
+ deletion_info.deleted_rows()));
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
- base::Bind(&PasswordProtectionService::
- RemoveUnhandledSyncPasswordReuseOnURLsDeleted,
- GetWeakPtr(), deletion_info.IsAllHistory(),
- deletion_info.deleted_rows()));
+ base::BindRepeating(&PasswordProtectionService::
+ RemoveUnhandledSyncPasswordReuseOnURLsDeleted,
+ GetWeakPtr(), deletion_info.IsAllHistory(),
+ deletion_info.deleted_rows()));
}
void PasswordProtectionService::HistoryServiceBeingDeleted(
@@ -613,7 +692,8 @@ void PasswordProtectionService::RemoveContentSettingsOnURLsDeleted(
url_key, LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
content_settings_->ClearSettingsForOneTypeWithPredicate(
CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
- base::Time::Max(), base::Bind(&OriginMatchPrimaryPattern, url_key));
+ base::Time::Max(),
+ base::BindRepeating(&OriginMatchPrimaryPattern, url_key));
}
}
@@ -629,18 +709,21 @@ int PasswordProtectionService::GetVerdictCountForURL(
if (!cache_dictionary || cache_dictionary->empty())
return 0;
+ int verdict_cnt = 0;
if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
- base::DictionaryValue* password_on_focus_dict = nullptr;
- return cache_dictionary->GetDictionaryWithoutPathExpansion(
- base::StringPiece(kPasswordOnFocusCacheKey),
- &password_on_focus_dict)
- ? password_on_focus_dict->size()
- : 0;
+ base::Value* password_on_focus_dict = nullptr;
+ password_on_focus_dict =
+ cache_dictionary->FindKey(kPasswordOnFocusCacheKey);
+ verdict_cnt +=
+ password_on_focus_dict ? password_on_focus_dict->DictSize() : 0;
} else {
- return cache_dictionary->HasKey(base::StringPiece(kPasswordOnFocusCacheKey))
- ? cache_dictionary->size() - 1
- : cache_dictionary->size();
+ for (const auto& item : cache_dictionary->DictItems()) {
+ if (item.first == kPasswordOnFocusCacheKey)
+ continue;
+ verdict_cnt += item.second.DictSize();
+ }
}
+ return verdict_cnt;
}
bool PasswordProtectionService::RemoveExpiredVerdicts(
@@ -648,69 +731,73 @@ bool PasswordProtectionService::RemoveExpiredVerdicts(
base::DictionaryValue* cache_dictionary) {
DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
- base::DictionaryValue* verdict_dictionary = nullptr;
- if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT) {
- verdict_dictionary = cache_dictionary;
- } else {
- if (!cache_dictionary->GetDictionaryWithoutPathExpansion(
- base::StringPiece(kPasswordOnFocusCacheKey), &verdict_dictionary)) {
- return false;
+ if (!cache_dictionary || cache_dictionary->empty())
+ return false;
+
+ size_t verdicts_removed = 0;
+ std::vector<std::string> empty_keys;
+ for (auto item : cache_dictionary->DictItems()) {
+ if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
+ item.first == std::string(kPasswordOnFocusCacheKey)) {
+ size_t removed_cnt = RemoveExpiredEntries(&item.second);
+ verdicts_removed += removed_cnt;
+ stored_verdict_count_password_on_focus_ -= removed_cnt;
+ } else {
+ size_t removed_cnt = RemoveExpiredEntries(&item.second);
+ verdicts_removed += removed_cnt;
+ stored_verdict_count_password_entry_ -= removed_cnt;
}
+
+ if (item.second.DictSize() == 0U)
+ empty_keys.push_back(item.first);
}
+ for (const auto& key : empty_keys)
+ cache_dictionary->RemoveKey(key);
- if (!verdict_dictionary || verdict_dictionary->empty())
- return false;
+ return verdicts_removed > 0U;
+}
+size_t PasswordProtectionService::RemoveExpiredEntries(
+ base::Value* verdict_dictionary) {
std::vector<std::string> expired_keys;
- for (base::DictionaryValue::Iterator it(*verdict_dictionary); !it.IsAtEnd();
- it.Advance()) {
- if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
- it.key() == std::string(kPasswordOnFocusCacheKey))
- continue;
-
- base::DictionaryValue* verdict_entry = nullptr;
- verdict_dictionary->GetDictionaryWithoutPathExpansion(it.key(),
- &verdict_entry);
+ for (const auto& item : verdict_dictionary->DictItems()) {
int verdict_received_time;
LoginReputationClientResponse verdict;
-
- if (!ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict) ||
- IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())) {
- // Since DictionaryValue::Iterator cannot be used to modify the
- // dictionary, we record the keys of expired verdicts in |expired_keys|
- // and remove them in the next for-loop.
- expired_keys.push_back(it.key());
+ if (!PasswordProtectionService::ParseVerdictEntry(
+ &item.second, &verdict_received_time, &verdict) ||
+ PasswordProtectionService::IsCacheExpired(
+ verdict_received_time, verdict.cache_duration_sec())) {
+ expired_keys.push_back(item.first);
}
}
- for (const std::string& key : expired_keys) {
- verdict_dictionary->RemoveWithoutPathExpansion(key, nullptr);
- if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT)
- stored_verdict_count_password_entry_--;
- else
- stored_verdict_count_password_on_focus_--;
- }
-
- if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
- verdict_dictionary->size() == 0U) {
- cache_dictionary->RemoveWithoutPathExpansion(
- base::StringPiece(kPasswordOnFocusCacheKey), nullptr);
- }
+ for (const std::string& key : expired_keys)
+ verdict_dictionary->RemoveKey(key);
- return expired_keys.size() > 0U;
+ return expired_keys.size();
}
// static
bool PasswordProtectionService::ParseVerdictEntry(
- base::DictionaryValue* verdict_entry,
+ base::Value* verdict_entry,
int* out_verdict_received_time,
LoginReputationClientResponse* out_verdict) {
std::string serialized_verdict_proto;
- return verdict_entry && out_verdict &&
- verdict_entry->GetInteger(kCacheCreationTime,
- out_verdict_received_time) &&
- verdict_entry->GetString(kVerdictProto, &serialized_verdict_proto) &&
- base::Base64Decode(serialized_verdict_proto,
+ if (!verdict_entry || !out_verdict)
+ return false;
+ base::Value* cache_creation_time_value =
+ verdict_entry->FindKey(kCacheCreationTime);
+
+ if (!cache_creation_time_value || !cache_creation_time_value->is_int())
+ return false;
+ *out_verdict_received_time = cache_creation_time_value->GetInt();
+
+ base::Value* verdict_proto_value = verdict_entry->FindKey(kVerdictProto);
+ if (!verdict_proto_value || !verdict_proto_value->is_string())
+ return false;
+ serialized_verdict_proto = verdict_proto_value->GetString();
+
+ return base::Base64Decode(serialized_verdict_proto,
&serialized_verdict_proto) &&
out_verdict->ParseFromString(serialized_verdict_proto);
}
@@ -718,8 +805,7 @@ bool PasswordProtectionService::ParseVerdictEntry(
bool PasswordProtectionService::PathVariantsMatchCacheExpression(
const std::vector<std::string>& generated_paths,
const std::string& cache_expression_path) {
- return std::find(generated_paths.begin(), generated_paths.end(),
- cache_expression_path) != generated_paths.end();
+ return base::ContainsValue(generated_paths, cache_expression_path);
}
bool PasswordProtectionService::IsCacheExpired(int cache_creation_time,
@@ -774,7 +860,7 @@ PasswordProtectionService::CreateDictionaryFromVerdict(
void PasswordProtectionService::RecordNoPingingReason(
LoginReputationClientRequest::TriggerType trigger_type,
RequestOutcome reason,
- bool matches_sync_password) {
+ ReusedPasswordType password_type) {
DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
@@ -784,18 +870,24 @@ void PasswordProtectionService::RecordNoPingingReason(
return;
}
- LogPasswordEntryRequestOutcome(reason, matches_sync_password);
+ LogPasswordEntryRequestOutcome(reason, password_type);
}
-// static
void PasswordProtectionService::LogPasswordEntryRequestOutcome(
RequestOutcome reason,
- bool matches_sync_password) {
+ ReusedPasswordType password_type) {
base::UmaHistogramEnumeration(kAnyPasswordEntryRequestOutcomeHistogram,
reason, MAX_OUTCOME);
- if (matches_sync_password) {
+ if (password_type == PasswordReuseEvent::SIGN_IN_PASSWORD) {
+ if (GetSyncAccountType() == PasswordReuseEvent::GSUITE) {
+ base::UmaHistogramEnumeration(
+ kGSuiteSyncPasswordEntryRequestOutcomeHistogram, reason, MAX_OUTCOME);
+ }
base::UmaHistogramEnumeration(kSyncPasswordEntryRequestOutcomeHistogram,
reason, MAX_OUTCOME);
+ } else if (password_type == PasswordReuseEvent::ENTERPRISE_PASSWORD) {
+ base::UmaHistogramEnumeration(
+ kEnterprisePasswordEntryRequestOutcomeHistogram, reason, MAX_OUTCOME);
} else {
base::UmaHistogramEnumeration(
kProtectedPasswordEntryRequestOutcomeHistogram, reason, MAX_OUTCOME);
@@ -813,7 +905,8 @@ PasswordProtectionService::MaybeCreateNavigationThrottle(
if (request->web_contents() == web_contents &&
request->trigger_type() ==
safe_browsing::LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
- request->matches_sync_password()) {
+ IsSupportedPasswordTypeForModalWarning(
+ request->reused_password_type())) {
return std::make_unique<PasswordProtectionNavigationThrottle>(
navigation_handle, request, /*is_warning_showing=*/false);
}
@@ -852,8 +945,125 @@ bool PasswordProtectionService::IsWarningEnabled() {
}
bool PasswordProtectionService::IsEventLoggingEnabled() {
- return GetSyncAccountType() !=
- LoginReputationClientRequest::PasswordReuseEvent::NOT_SIGNED_IN;
+ return GetSyncAccountType() != PasswordReuseEvent::NOT_SIGNED_IN;
+}
+
+// static
+ReusedPasswordType
+PasswordProtectionService::GetPasswordProtectionReusedPasswordType(
+ password_manager::metrics_util::PasswordType password_type) {
+ switch (password_type) {
+ case PasswordType::SAVED_PASSWORD:
+ return PasswordReuseEvent::SAVED_PASSWORD;
+ case PasswordType::SYNC_PASSWORD:
+ return PasswordReuseEvent::SIGN_IN_PASSWORD;
+ case PasswordType::OTHER_GAIA_PASSWORD:
+ return PasswordReuseEvent::OTHER_GAIA_PASSWORD;
+ case PasswordType::ENTERPRISE_PASSWORD:
+ return PasswordReuseEvent::ENTERPRISE_PASSWORD;
+ case PasswordType::PASSWORD_TYPE_COUNT:
+ break;
+ }
+ NOTREACHED();
+ return PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN;
+}
+
+bool PasswordProtectionService::IsSupportedPasswordTypeForPinging(
+ ReusedPasswordType reused_password_type) const {
+ switch (reused_password_type) {
+ case PasswordReuseEvent::SAVED_PASSWORD:
+ return true;
+ case PasswordReuseEvent::SIGN_IN_PASSWORD:
+ return GetSyncAccountType() != PasswordReuseEvent::NOT_SIGNED_IN;
+ case PasswordReuseEvent::OTHER_GAIA_PASSWORD:
+ return false;
+ case PasswordReuseEvent::ENTERPRISE_PASSWORD:
+ return base::FeatureList::IsEnabled(kEnterprisePasswordProtectionV1);
+ case PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN:
+ break;
+ }
+ NOTREACHED();
+ return false;
+}
+
+bool PasswordProtectionService::IsSupportedPasswordTypeForModalWarning(
+ ReusedPasswordType reused_password_type) const {
+ return reused_password_type == PasswordReuseEvent::SIGN_IN_PASSWORD ||
+ reused_password_type == PasswordReuseEvent::ENTERPRISE_PASSWORD;
+}
+
+void PasswordProtectionService::MigrateCachedVerdicts() {
+ // |content_settings_| can be null in tests.
+ if (!content_settings_)
+ return;
+
+ ContentSettingsForOneType password_protection_settings;
+ content_settings_->GetSettingsForOneType(
+ CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
+ &password_protection_settings);
+
+ size_t verdicts_migrated = 0;
+ for (const ContentSettingPatternSource& source :
+ password_protection_settings) {
+ GURL primary_pattern_url = GURL(source.primary_pattern.ToString());
+ // Find all verdicts associated with this origin.
+ std::unique_ptr<base::DictionaryValue> cache_dictionary =
+ base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
+ primary_pattern_url, GURL(),
+ CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr));
+
+ std::vector<std::string> removed_keys;
+ for (const auto& item : cache_dictionary->DictItems()) {
+ int password_type_int = -1;
+ if (item.first == kPasswordOnFocusCacheKey ||
+ (base::StringToInt(item.first, &password_type_int) &&
+ password_type_int >= PasswordReuseEvent::ReusedPasswordType_MIN &&
+ password_type_int <= PasswordReuseEvent::ReusedPasswordType_MAX)) {
+ continue;
+ }
+ // Removes value if its key is not kPasswordOnFocusCacheKey or a valid
+ // reused password type.
+ removed_keys.push_back(item.first);
+ }
+
+ verdicts_migrated += removed_keys.size();
+ for (const std::string& key : removed_keys)
+ cache_dictionary->RemoveKey(key);
+
+ if (cache_dictionary->size() == 0u) {
+ content_settings_->ClearSettingsForOneTypeWithPredicate(
+ CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
+ base::Time::Max(),
+ base::BindRepeating(&OriginMatchPrimaryPattern, primary_pattern_url));
+ } else {
+ // Set the website setting of this origin with the updated
+ // |cache_dictionary|.
+ content_settings_->SetWebsiteSettingDefaultScope(
+ primary_pattern_url, GURL(),
+ CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
+ std::move(cache_dictionary));
+ }
+ }
+ UMA_HISTOGRAM_COUNTS_100(
+ "PasswordProtection.NumberOfVerdictsMigratedDuringInitialization",
+ verdicts_migrated);
+}
+
+void PasswordProtectionService::LogPasswordAlertModeOutcome(
+ RequestOutcome reason,
+ ReusedPasswordType password_type) {
+ DCHECK(password_type == PasswordReuseEvent::SIGN_IN_PASSWORD ||
+ password_type == PasswordReuseEvent::ENTERPRISE_PASSWORD);
+ if (password_type == PasswordReuseEvent::SIGN_IN_PASSWORD) {
+ base::UmaHistogramEnumeration(
+ "PasswordProtection.PasswordAlertModeOutcome.GSuiteSyncPasswordEntry",
+ reason, MAX_OUTCOME);
+ } else {
+ base::UmaHistogramEnumeration(
+ "PasswordProtection.PasswordAlertModeOutcome."
+ "NonGaiaEnterprisePasswordEntry",
+ reason, MAX_OUTCOME);
+ }
}
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/password_protection/password_protection_service.h b/chromium/components/safe_browsing/password_protection/password_protection_service.h
index c1f4f43a84a..b48bf574e95 100644
--- a/chromium/components/safe_browsing/password_protection/password_protection_service.h
+++ b/chromium/components/safe_browsing/password_protection/password_protection_service.h
@@ -18,6 +18,7 @@
#include "base/task/cancelable_task_tracker.h"
#include "base/values.h"
#include "components/history/core/browser/history_service_observer.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/safe_browsing/common/safe_browsing_prefs.h"
#include "components/safe_browsing/db/v4_protocol_manager_util.h"
#include "components/safe_browsing/proto/csd.pb.h"
@@ -55,6 +56,20 @@ extern const char kProtectedPasswordEntryRequestOutcomeHistogram[];
extern const char kSyncPasswordWarningDialogHistogram[];
extern const char kSyncPasswordPageInfoHistogram[];
extern const char kSyncPasswordChromeSettingsHistogram[];
+extern const char kSyncPasswordInterstitialHistogram[];
+extern const char kEnterprisePasswordEntryRequestOutcomeHistogram[];
+extern const char kEnterprisePasswordWarningDialogHistogram[];
+extern const char kEnterprisePasswordPageInfoHistogram[];
+extern const char kEnterprisePasswordInterstitialHistogram[];
+extern const char kGSuiteSyncPasswordEntryRequestOutcomeHistogram[];
+extern const char kGSuiteSyncPasswordWarningDialogHistogram[];
+extern const char kGSuiteSyncPasswordPageInfoHistogram[];
+extern const char kGSuiteSyncPasswordInterstitialHistogram[];
+extern const char kInterstitialActionByUserNavigationHistogram[];
+;
+
+using ReusedPasswordType =
+ LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordType;
// Manage password protection pings and verdicts. There is one instance of this
// class per profile. Therefore, every PasswordProtectionService instance is
@@ -88,6 +103,8 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
PASSWORD_ALERT_MODE = 18,
// No request is event sent if the admin turns off password protection.
TURNED_OFF_BY_ADMIN = 19,
+ // Safe Browsing is disabled.
+ SAFE_BROWSING_DISABLED = 20,
MAX_OUTCOME
};
@@ -140,16 +157,22 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
LoginReputationClientResponse::VerdictType GetCachedVerdict(
const GURL& url,
LoginReputationClientRequest::TriggerType trigger_type,
+ ReusedPasswordType password_type,
LoginReputationClientResponse* out_response);
// Stores |verdict| in |settings| based on its |trigger_type|, |url|,
- // |verdict| and |receive_time|.
+ // reused |password_type|, |verdict| and |receive_time|.
virtual void CacheVerdict(
const GURL& url,
LoginReputationClientRequest::TriggerType trigger_type,
+ ReusedPasswordType password_type,
LoginReputationClientResponse* verdict,
const base::Time& receive_time);
+ // Migrates cached password reuse verdicts such that verdicts of different
+ // reused password type are cached separately.
+ void MigrateCachedVerdicts();
+
// Removes all the expired verdicts from cache.
void CleanUpExpiredVerdicts();
@@ -160,7 +183,7 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
const GURL& main_frame_url,
const GURL& password_form_action,
const GURL& password_form_frame_url,
- bool matches_sync_password,
+ ReusedPasswordType reused_password_type,
const std::vector<std::string>& matching_domains,
LoginReputationClientRequest::TriggerType trigger_type,
bool password_field_exists);
@@ -174,7 +197,7 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
virtual void MaybeStartProtectedPasswordEntryRequest(
content::WebContents* web_contents,
const GURL& main_frame_url,
- bool matches_sync_password,
+ ReusedPasswordType reused_password_type,
const std::vector<std::string>& matching_domains,
bool password_field_exists);
@@ -193,35 +216,35 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
// (6) Its hostname is a dotless domain.
static bool CanGetReputationOfURL(const GURL& url);
- // Records user action to corresponding UMA histograms.
- void RecordWarningAction(WarningUIType ui_type, WarningAction action);
+ // Records user action on warnings to corresponding UMA histograms.
+ void RecordWarningAction(WarningUIType ui_type,
+ WarningAction action,
+ ReusedPasswordType password_type);
// If we want to show password reuse modal warning.
bool ShouldShowModalWarning(
LoginReputationClientRequest::TriggerType trigger_type,
- bool matches_sync_password,
+ ReusedPasswordType reused_password_type,
LoginReputationClientResponse::VerdictType verdict_type);
// Shows modal warning dialog on the current |web_contents| and pass the
// |verdict_token| to callback of this dialog.
virtual void ShowModalWarning(content::WebContents* web_contents,
- const std::string& verdict_token) = 0;
+ const std::string& verdict_token,
+ ReusedPasswordType reused_password_type) = 0;
// Shows chrome://reset-password interstitial.
- virtual void ShowInterstitial(content::WebContents* web_contens) = 0;
-
- // Called when user interacts with warning UIs.
- virtual void OnUserAction(content::WebContents* web_contents,
- WarningUIType ui_type,
- WarningAction action) = 0;
+ virtual void ShowInterstitial(content::WebContents* web_contens,
+ ReusedPasswordType password_type) = 0;
virtual void UpdateSecurityState(safe_browsing::SBThreatType threat_type,
+ ReusedPasswordType password_type,
content::WebContents* web_contents) = 0;
// Log the |reason| to several UMA metrics, depending on the value
- // of |matches_sync_password|.
- static void LogPasswordEntryRequestOutcome(RequestOutcome reason,
- bool matches_sync_password);
+ // of |password_type|.
+ void LogPasswordEntryRequestOutcome(RequestOutcome reason,
+ ReusedPasswordType password_type);
// If user has clicked through any Safe Browsing interstitial on this given
// |web_contents|.
@@ -260,17 +283,30 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
// UI thread.
virtual void OnPolicySpecifiedPasswordChanged() = 0;
+ // Converts from password::metrics_util::PasswordType to
+ // LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordType.
+ static ReusedPasswordType GetPasswordProtectionReusedPasswordType(
+ password_manager::metrics_util::PasswordType password_type);
+
+ // If we can send ping for this type of reused password.
+ bool IsSupportedPasswordTypeForPinging(
+ ReusedPasswordType reused_password_type) const;
+
+ // If we can show modal warning for this type of reused password.
+ bool IsSupportedPasswordTypeForModalWarning(
+ ReusedPasswordType reused_password_type) const;
+
protected:
friend class PasswordProtectionRequest;
- // Chrome can send password protection ping if it is allowed by Finch config
- // and if Safe Browsing can compute reputation of |main_frame_url| (e.g.
- // Safe Browsing is not able to compute reputation of a private IP or
- // a local host). Update |reason| if sending ping is not allowed.
- // |matches_sync_password| is used for UMA metric recording.
+ // Chrome can send password protection ping if it is allowed by for the
+ // |trigger_type| and if Safe Browsing can compute reputation of
+ // |main_frame_url| (e.g. Safe Browsing is not able to compute reputation of a
+ // private IP or a local host). Update |reason| if sending ping is not
+ // allowed. |password_type| is used for UMA metric recording.
bool CanSendPing(LoginReputationClientRequest::TriggerType trigger_type,
const GURL& main_frame_url,
- bool matches_sync_password,
+ ReusedPasswordType password_type,
RequestOutcome* reason);
// Called by a PasswordProtectionRequest instance when it finishes to remove
@@ -345,10 +381,18 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
bool IsModalWarningShowingInWebContents(content::WebContents* web_contents);
+ virtual bool CanShowInterstitial(RequestOutcome reason,
+ ReusedPasswordType password_type,
+ const GURL& main_frame_url) = 0;
+
+ void LogPasswordAlertModeOutcome(RequestOutcome reason,
+ ReusedPasswordType password_type);
+
private:
friend class PasswordProtectionServiceTest;
friend class TestPasswordProtectionService;
friend class ChromePasswordProtectionServiceTest;
+ friend class ChromePasswordProtectionServiceBrowserTest;
FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
TestParseInvalidVerdictEntry);
FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
@@ -388,7 +432,11 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
bool RemoveExpiredVerdicts(LoginReputationClientRequest::TriggerType type,
base::DictionaryValue* cache_dictionary);
- static bool ParseVerdictEntry(base::DictionaryValue* verdict_entry,
+ // Helper function called by RemoveExpiredVerdicts(..). Returns the number of
+ // expired entries removed.
+ size_t RemoveExpiredEntries(base::Value* verdict_dictionary);
+
+ static bool ParseVerdictEntry(base::Value* verdict_entry,
int* out_verdict_received_time,
LoginReputationClientResponse* out_verdict);
@@ -408,10 +456,10 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
const LoginReputationClientResponse* verdict,
const base::Time& receive_time);
- static void RecordNoPingingReason(
+ void RecordNoPingingReason(
LoginReputationClientRequest::TriggerType trigger_type,
RequestOutcome reason,
- bool matches_sync_password);
+ ReusedPasswordType password_type);
// Number of verdict stored for this profile for password on focus pings.
int stored_verdict_count_password_on_focus_;
diff --git a/chromium/components/safe_browsing/password_protection/password_protection_service_unittest.cc b/chromium/components/safe_browsing/password_protection/password_protection_service_unittest.cc
index 9c2f2f6d3ab..120b0641380 100644
--- a/chromium/components/safe_browsing/password_protection/password_protection_service_unittest.cc
+++ b/chromium/components/safe_browsing/password_protection/password_protection_service_unittest.cc
@@ -8,7 +8,7 @@
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/null_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
@@ -18,7 +18,10 @@
#include "components/safe_browsing/password_protection/mock_password_protection_service.h"
#include "components/safe_browsing/password_protection/password_protection_request.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/web_contents_tester.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -27,6 +30,8 @@
using testing::_;
using testing::ElementsAre;
using testing::Return;
+using PasswordReuseEvent =
+ safe_browsing::LoginReputationClientRequest::PasswordReuseEvent;
namespace {
@@ -65,6 +70,7 @@ class TestPasswordProtectionService : public MockPasswordProtectionService {
scoped_refptr<HostContentSettingsMap> content_setting_map)
: MockPasswordProtectionService(database_manager,
url_loader_factory,
+ nullptr,
content_setting_map.get()) {}
void RequestFinished(
@@ -132,8 +138,7 @@ class PasswordProtectionServiceTest
EXPECT_CALL(*password_protection_service_, IsIncognito())
.WillRepeatedly(Return(GetParam()[1]));
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
- .WillRepeatedly(Return(
- LoginReputationClientRequest::PasswordReuseEvent::NOT_SIGNED_IN));
+ .WillRepeatedly(Return(PasswordReuseEvent::NOT_SIGNED_IN));
EXPECT_CALL(*password_protection_service_,
IsURLWhitelistedForPasswordEntry(_, _))
.WillRepeatedly(Return(false));
@@ -146,35 +151,38 @@ class PasswordProtectionServiceTest
void TearDown() override { content_setting_map_->ShutdownOnUIThread(); }
// Sets up |database_manager_| and |pending_requests_| as needed.
- void InitializeAndStartPasswordOnFocusRequest(bool match_whitelist,
- int timeout_in_ms) {
+ void InitializeAndStartPasswordOnFocusRequest(
+ bool match_whitelist,
+ int timeout_in_ms,
+ content::WebContents* web_contents) {
GURL target_url(kTargetUrl);
EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url, _))
.WillRepeatedly(
Return(match_whitelist ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH));
request_ = new PasswordProtectionRequest(
- nullptr, target_url, GURL(kFormActionUrl), GURL(kPasswordFrameUrl),
- false /* matches_sync_password */, {},
+ web_contents, target_url, GURL(kFormActionUrl), GURL(kPasswordFrameUrl),
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN, {},
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true,
password_protection_service_.get(), timeout_in_ms);
request_->Start();
}
void InitializeAndStartPasswordEntryRequest(
- bool matches_sync_password,
+ PasswordReuseEvent::ReusedPasswordType type,
const std::vector<std::string>& matching_domains,
bool match_whitelist,
- int timeout_in_ms) {
+ int timeout_in_ms,
+ content::WebContents* web_contents) {
GURL target_url(kTargetUrl);
EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url, _))
.WillRepeatedly(
Return(match_whitelist ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH));
request_ = new PasswordProtectionRequest(
- nullptr, target_url, GURL(), GURL(), matches_sync_password,
- matching_domains, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
- true, password_protection_service_.get(), timeout_in_ms);
+ web_contents, target_url, GURL(), GURL(), type, matching_domains,
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT, true,
+ password_protection_service_.get(), timeout_in_ms);
request_->Start();
}
@@ -189,6 +197,7 @@ class PasswordProtectionServiceTest
void CacheVerdict(const GURL& url,
LoginReputationClientRequest::TriggerType trigger,
+ ReusedPasswordType password_type,
LoginReputationClientResponse::VerdictType verdict,
int cache_duration_sec,
const std::string& cache_expression,
@@ -196,11 +205,11 @@ class PasswordProtectionServiceTest
ASSERT_FALSE(cache_expression.empty());
LoginReputationClientResponse response(
CreateVerdictProto(verdict, cache_duration_sec, cache_expression));
- password_protection_service_->CacheVerdict(url, trigger, &response,
- verdict_received_time);
+ password_protection_service_->CacheVerdict(
+ url, trigger, password_type, &response, verdict_received_time);
}
- void CacheInvalidVerdict() {
+ void CacheInvalidVerdict(ReusedPasswordType password_type) {
GURL invalid_hostname("http://invalid.com");
std::unique_ptr<base::DictionaryValue> verdict_dictionary =
base::DictionaryValue::From(content_setting_map_->GetWebsiteSetting(
@@ -214,8 +223,13 @@ class PasswordProtectionServiceTest
std::make_unique<base::DictionaryValue>();
invalid_verdict_entry->SetString("invalid", "invalid_string");
- verdict_dictionary->SetWithoutPathExpansion(
+ std::unique_ptr<base::DictionaryValue> invalid_cache_expression_entry =
+ std::make_unique<base::DictionaryValue>();
+ invalid_cache_expression_entry->SetWithoutPathExpansion(
"invalid_cache_expression", std::move(invalid_verdict_entry));
+ verdict_dictionary->SetWithoutPathExpansion(
+ base::NumberToString(password_type),
+ std::move(invalid_cache_expression_entry));
content_setting_map_->SetWebsiteSettingDefaultScope(
invalid_hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
std::string(), std::move(verdict_dictionary));
@@ -225,6 +239,11 @@ class PasswordProtectionServiceTest
return password_protection_service_->GetStoredVerdictCount(type);
}
+ content::WebContents* GetWebContents() {
+ return content::WebContentsTester::CreateTestWebContents(
+ content::WebContents::CreateParams(&browser_context_));
+ }
+
protected:
// |thread_bundle_| is needed here because this test involves both UI and IO
// threads.
@@ -237,6 +256,7 @@ class PasswordProtectionServiceTest
std::unique_ptr<TestPasswordProtectionService> password_protection_service_;
scoped_refptr<PasswordProtectionRequest> request_;
base::HistogramTester histograms_;
+ content::TestBrowserContext browser_context_;
};
TEST_P(PasswordProtectionServiceTest, TestParseInvalidVerdictEntry) {
@@ -321,6 +341,7 @@ TEST_P(PasswordProtectionServiceTest, TestCachePasswordReuseVerdicts) {
// Cache a verdict for http://www.test.com/foo/index.html
CacheVerdict(GURL("http://www.test.com/foo/index.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/foo/", base::Time::Now());
@@ -331,34 +352,52 @@ TEST_P(PasswordProtectionServiceTest, TestCachePasswordReuseVerdicts) {
// override the cache.
CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::PHISHING, 10 * kMinute,
"test.com/foo/", base::Time::Now());
EXPECT_EQ(1U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
LoginReputationClientResponse out_verdict;
- EXPECT_EQ(
- LoginReputationClientResponse::PHISHING,
- password_protection_service_->GetCachedVerdict(
- GURL("http://www.test.com/foo/index2.html"),
- LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &out_verdict));
+ EXPECT_EQ(LoginReputationClientResponse::PHISHING,
+ password_protection_service_->GetCachedVerdict(
+ GURL("http://www.test.com/foo/index2.html"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD, &out_verdict));
+
+ // Cache a password reuse verdict with a different password type but same
+ // origin and cache expression should add a new entry.
+ CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::ENTERPRISE_PASSWORD,
+ LoginReputationClientResponse::PHISHING, 10 * kMinute,
+ "test.com/foo/", base::Time::Now());
+ EXPECT_EQ(2U, GetStoredVerdictCount(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+ EXPECT_EQ(LoginReputationClientResponse::PHISHING,
+ password_protection_service_->GetCachedVerdict(
+ GURL("http://www.test.com/foo/index2.html"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::ENTERPRISE_PASSWORD, &out_verdict));
// Cache another verdict with the same origin but different cache_expression
// will not increase setting count, but will increase the number of verdicts
// in the given origin.
CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/bar/", base::Time::Now());
- EXPECT_EQ(2U, GetStoredVerdictCount(
+ EXPECT_EQ(3U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
// Now cache a UNFAMILIAR_LOGIN_PAGE verdict, stored verdict count for
// PASSWORD_REUSE_EVENT should be the same.
CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/foobar/", base::Time::Now());
- EXPECT_EQ(2U, GetStoredVerdictCount(
+ EXPECT_EQ(3U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
EXPECT_EQ(1U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
@@ -372,6 +411,7 @@ TEST_P(PasswordProtectionServiceTest, TestCacheUnfamiliarLoginVerdicts) {
// Cache a verdict for http://www.test.com/foo/index.html
CacheVerdict(GURL("http://www.test.com/foo/index.html"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/foo/", base::Time::Now());
@@ -383,6 +423,7 @@ TEST_P(PasswordProtectionServiceTest, TestCacheUnfamiliarLoginVerdicts) {
// in the given origin.
CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/bar/", base::Time::Now());
EXPECT_EQ(2U, GetStoredVerdictCount(
@@ -392,6 +433,7 @@ TEST_P(PasswordProtectionServiceTest, TestCacheUnfamiliarLoginVerdicts) {
// UNFAMILIAR_LOGIN_PAGE should be the same.
CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/foobar/", base::Time::Now());
EXPECT_EQ(2U, GetStoredVerdictCount(
@@ -405,70 +447,92 @@ TEST_P(PasswordProtectionServiceTest, TestGetCachedVerdicts) {
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
- // Prepare 3 verdicts of the same origin with different cache expressions,
- // one is expired, one is not, the other is of a different type.
+ // Prepare 4 verdicts of the same origin with different cache expressions,
+ // or password type, one is expired, one is not, one is of a different
+ // trigger type, and the other is with a different password type.
base::Time now = base::Time::Now();
CacheVerdict(GURL("http://test.com/login.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::SAFE, 10 * kMinute, "test.com/",
now);
CacheVerdict(
GURL("http://test.com/def/index.jsp"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::PHISHING, 10 * kMinute, "test.com/def/",
base::Time::FromDoubleT(now.ToDoubleT() - kDay)); // Yesterday, expired.
CacheVerdict(GURL("http://test.com/bar/login.html"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
LoginReputationClientResponse::PHISHING, 10 * kMinute,
"test.com/bar/", now);
+ CacheVerdict(GURL("http://test.com/login.html"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::ENTERPRISE_PASSWORD,
+ LoginReputationClientResponse::SAFE, 10 * kMinute, "test.com/",
+ now);
- ASSERT_EQ(2U, GetStoredVerdictCount(
+ ASSERT_EQ(3U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
ASSERT_EQ(1U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
// Return VERDICT_TYPE_UNSPECIFIED if look up for a URL with unknown origin.
LoginReputationClientResponse actual_verdict;
- EXPECT_EQ(
- LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
- password_protection_service_->GetCachedVerdict(
- GURL("http://www.unknown.com/"),
- LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
+ EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+ password_protection_service_->GetCachedVerdict(
+ GURL("http://www.unknown.com/"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD, &actual_verdict));
+ EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+ password_protection_service_->GetCachedVerdict(
+ GURL("http://www.unknown.com/"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::ENTERPRISE_PASSWORD, &actual_verdict));
// Return SAFE if look up for a URL that matches "test.com" cache expression.
- EXPECT_EQ(
- LoginReputationClientResponse::SAFE,
- password_protection_service_->GetCachedVerdict(
- GURL("http://test.com/xyz/foo.jsp"),
- LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
+ EXPECT_EQ(LoginReputationClientResponse::SAFE,
+ password_protection_service_->GetCachedVerdict(
+ GURL("http://test.com/xyz/foo.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD, &actual_verdict));
+ EXPECT_EQ(LoginReputationClientResponse::SAFE,
+ password_protection_service_->GetCachedVerdict(
+ GURL("http://test.com/xyz/foo.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::ENTERPRISE_PASSWORD, &actual_verdict));
// Return VERDICT_TYPE_UNSPECIFIED if look up for a URL whose variants match
// test.com/def, but the corresponding verdict is expired.
- EXPECT_EQ(
- LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
- password_protection_service_->GetCachedVerdict(
- GURL("http://test.com/def/ghi/index.html"),
- LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
+ EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+ password_protection_service_->GetCachedVerdict(
+ GURL("http://test.com/def/ghi/index.html"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD, &actual_verdict));
// Return PHISHING. Matches "test.com/bar/" cache expression.
- EXPECT_EQ(LoginReputationClientResponse::PHISHING,
- password_protection_service_->GetCachedVerdict(
- GURL("http://test.com/bar/foo.jsp"),
- LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
- &actual_verdict));
+ EXPECT_EQ(
+ LoginReputationClientResponse::PHISHING,
+ password_protection_service_->GetCachedVerdict(
+ GURL("http://test.com/bar/foo.jsp"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN, &actual_verdict));
// Now cache SAFE verdict for the full path.
CacheVerdict(GURL("http://test.com/bar/foo.jsp"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/bar/foo.jsp", now);
// Return SAFE now. Matches the full cache expression.
- EXPECT_EQ(LoginReputationClientResponse::SAFE,
- password_protection_service_->GetCachedVerdict(
- GURL("http://test.com/bar/foo.jsp"),
- LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
- &actual_verdict));
+ EXPECT_EQ(
+ LoginReputationClientResponse::SAFE,
+ password_protection_service_->GetCachedVerdict(
+ GURL("http://test.com/bar/foo.jsp"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN, &actual_verdict));
}
TEST_P(PasswordProtectionServiceTest, TestRemoveCachedVerdictOnURLsDeleted) {
@@ -476,26 +540,35 @@ TEST_P(PasswordProtectionServiceTest, TestRemoveCachedVerdictOnURLsDeleted) {
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
- // Prepare 2 verdicts. One is for origin "http://foo.com", and the other is
- // for "http://bar.com".
+ // Prepare 5 verdicts. Three are for origin "http://foo.com", and the others
+ // are for "http://bar.com".
base::Time now = base::Time::Now();
CacheVerdict(GURL("http://foo.com/abc/index.jsp"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
+ LoginReputationClientResponse::LOW_REPUTATION, 10 * kMinute,
+ "foo.com/abc/", now);
+ CacheVerdict(GURL("http://foo.com/abc/index.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::ENTERPRISE_PASSWORD,
LoginReputationClientResponse::LOW_REPUTATION, 10 * kMinute,
"foo.com/abc/", now);
CacheVerdict(GURL("http://bar.com/index.jsp"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::PHISHING, 10 * kMinute, "bar.com",
now);
- ASSERT_EQ(2U, GetStoredVerdictCount(
+ ASSERT_EQ(3U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
CacheVerdict(GURL("http://foo.com/abc/index.jsp"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
LoginReputationClientResponse::LOW_REPUTATION, 10 * kMinute,
"foo.com/abc/", now);
CacheVerdict(GURL("http://bar.com/index.jsp"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
LoginReputationClientResponse::PHISHING, 10 * kMinute, "bar.com",
now);
ASSERT_EQ(2U, GetStoredVerdictCount(
@@ -512,22 +585,23 @@ TEST_P(PasswordProtectionServiceTest, TestRemoveCachedVerdictOnURLsDeleted) {
password_protection_service_->RemoveContentSettingsOnURLsDeleted(
false /* all_history */, deleted_urls);
- EXPECT_EQ(1U, GetStoredVerdictCount(
+ EXPECT_EQ(2U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
EXPECT_EQ(1U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
LoginReputationClientResponse actual_verdict;
+ EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+ password_protection_service_->GetCachedVerdict(
+ GURL("http://bar.com"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD, &actual_verdict));
EXPECT_EQ(
LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
password_protection_service_->GetCachedVerdict(
GURL("http://bar.com"),
- LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
- EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
- password_protection_service_->GetCachedVerdict(
- GURL("http://bar.com"),
- LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
- &actual_verdict));
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN, &actual_verdict));
// If delete all history. All password protection content settings should be
// gone.
@@ -579,8 +653,8 @@ TEST_P(PasswordProtectionServiceTest, VerifyCanGetReputationOfURL) {
TEST_P(PasswordProtectionServiceTest, TestNoRequestSentForWhitelistedURL) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
- InitializeAndStartPasswordOnFocusRequest(true /* match whitelist */,
- 10000 /* timeout in ms*/);
+ InitializeAndStartPasswordOnFocusRequest(
+ true /* match whitelist */, 10000 /* timeout in ms */, GetWebContents());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(nullptr, password_protection_service_->latest_response());
EXPECT_THAT(
@@ -592,10 +666,11 @@ TEST_P(PasswordProtectionServiceTest, TestNoRequestSentIfVerdictAlreadyCached) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
CacheVerdict(GURL(kTargetUrl),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
LoginReputationClientResponse::LOW_REPUTATION, 10 * kMinute,
GURL(kTargetUrl).host().append("/"), base::Time::Now());
- InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
- 10000 /* timeout in ms*/);
+ InitializeAndStartPasswordOnFocusRequest(
+ false /* match whitelist */, 10000 /* timeout in ms*/, GetWebContents());
base::RunLoop().RunUntilIdle();
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
@@ -611,8 +686,8 @@ TEST_P(PasswordProtectionServiceTest, TestResponseFetchFailed) {
network::URLLoaderCompletionStatus status(net::ERR_FAILED);
test_url_loader_factory_.AddResponse(url_, head, std::string(), status);
- InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
- 10000 /* timeout in ms*/);
+ InitializeAndStartPasswordOnFocusRequest(
+ false /* match whitelist */, 10000 /* timeout in ms */, GetWebContents());
password_protection_service_->WaitForResponse();
EXPECT_EQ(nullptr, password_protection_service_->latest_response());
EXPECT_THAT(
@@ -625,8 +700,8 @@ TEST_P(PasswordProtectionServiceTest, TestMalformedResponse) {
// Set up malformed response.
test_url_loader_factory_.AddResponse(url_.spec(), "invalid response");
- InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
- 10000 /* timeout in ms*/);
+ InitializeAndStartPasswordOnFocusRequest(
+ false /* match whitelist */, 10000 /* timeout in ms */, GetWebContents());
password_protection_service_->WaitForResponse();
EXPECT_EQ(nullptr, password_protection_service_->latest_response());
EXPECT_THAT(
@@ -637,7 +712,8 @@ TEST_P(PasswordProtectionServiceTest, TestMalformedResponse) {
TEST_P(PasswordProtectionServiceTest, TestRequestTimedout) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
- 0 /* timeout immediately */);
+ 0 /* timeout immediately */,
+ GetWebContents());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(nullptr, password_protection_service_->latest_response());
EXPECT_THAT(
@@ -655,14 +731,15 @@ TEST_P(PasswordProtectionServiceTest,
test_url_loader_factory_.AddResponse(url_.spec(),
expected_response.SerializeAsString());
- InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
- 10000 /* timeout in ms*/);
+ InitializeAndStartPasswordOnFocusRequest(
+ false /* match whitelist */, 10000 /* timeout in ms */, GetWebContents());
password_protection_service_->WaitForResponse();
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
EXPECT_THAT(histograms_.GetAllSamples(kPasswordOnFocusVerdictHistogram),
ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
+ histograms_.ExpectTotalCount(kReferrerChainSizeOfPhishingVerdictHistogram, 1);
LoginReputationClientResponse* actual_response =
password_protection_service_->latest_response();
EXPECT_EQ(expected_response.verdict_type(), actual_response->verdict_type());
@@ -687,8 +764,8 @@ TEST_P(PasswordProtectionServiceTest,
// Initiate a saved password entry request (w/ no sync password).
InitializeAndStartPasswordEntryRequest(
- false /* matches_sync_password */, {"example.com"},
- false /* match whitelist */, 10000 /* timeout in ms*/);
+ PasswordReuseEvent::SAVED_PASSWORD, {"example.com"},
+ false /* match whitelist */, 10000 /* timeout in ms*/, GetWebContents());
password_protection_service_->WaitForResponse();
// UMA: request outcomes
@@ -710,6 +787,7 @@ TEST_P(PasswordProtectionServiceTest,
EXPECT_THAT(
histograms_.GetAllSamples(kProtectedPasswordEntryVerdictHistogram),
ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
+ histograms_.ExpectTotalCount(kReferrerChainSizeOfPhishingVerdictHistogram, 1);
}
TEST_P(PasswordProtectionServiceTest,
@@ -726,9 +804,9 @@ TEST_P(PasswordProtectionServiceTest,
expected_response.SerializeAsString());
// Initiate a sync password entry request (w/ no saved password).
- InitializeAndStartPasswordEntryRequest(true /* matches_sync_password */, {},
- false /* match whitelist */,
- 10000 /* timeout in ms*/);
+ InitializeAndStartPasswordEntryRequest(
+ PasswordReuseEvent::SIGN_IN_PASSWORD, {}, false /* match whitelist */,
+ 10000 /* timeout in ms*/, GetWebContents());
password_protection_service_->WaitForResponse();
// UMA: request outcomes
@@ -747,6 +825,7 @@ TEST_P(PasswordProtectionServiceTest,
EXPECT_THAT(histograms_.GetAllSamples(kSyncPasswordEntryVerdictHistogram),
ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
histograms_.ExpectTotalCount(kProtectedPasswordEntryVerdictHistogram, 0);
+ histograms_.ExpectTotalCount(kReferrerChainSizeOfPhishingVerdictHistogram, 1);
}
TEST_P(PasswordProtectionServiceTest, TestTearDownWithPendingRequests) {
@@ -756,7 +835,7 @@ TEST_P(PasswordProtectionServiceTest, TestTearDownWithPendingRequests) {
.WillRepeatedly(Return(AsyncMatch::NO_MATCH));
password_protection_service_->StartRequest(
nullptr, target_url, GURL("http://foo.com/submit"),
- GURL("http://foo.com/frame"), false, {},
+ GURL("http://foo.com/frame"), PasswordReuseEvent::SAVED_PASSWORD, {},
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
// Destroy password_protection_service_ while there is one request pending.
@@ -769,7 +848,7 @@ TEST_P(PasswordProtectionServiceTest, TestTearDownWithPendingRequests) {
}
TEST_P(PasswordProtectionServiceTest, TestCleanUpExpiredVerdict) {
- // Prepare 4 verdicts for PASSWORD_REUSE_EVENT:
+ // Prepare 4 verdicts for PASSWORD_REUSE_EVENT with SIGN_IN_PASSWORD type:
// (1) "foo.com/abc/" valid
// (2) "foo.com/def/" expired
// (3) "bar.com/abc/" expired
@@ -777,17 +856,21 @@ TEST_P(PasswordProtectionServiceTest, TestCleanUpExpiredVerdict) {
base::Time now = base::Time::Now();
CacheVerdict(GURL("https://foo.com/abc/index.jsp"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::LOW_REPUTATION, 10 * kMinute,
"foo.com/abc/", now);
CacheVerdict(GURL("https://foo.com/def/index.jsp"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::LOW_REPUTATION, 0, "foo.com/def/",
now);
CacheVerdict(GURL("https://bar.com/abc/index.jsp"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::PHISHING, 0, "bar.com/abc/", now);
CacheVerdict(GURL("https://bar.com/def/index.jsp"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::PHISHING, 0, "bar.com/def/", now);
ASSERT_EQ(4U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
@@ -797,10 +880,12 @@ TEST_P(PasswordProtectionServiceTest, TestCleanUpExpiredVerdict) {
// (2) "bar.com/xyz/" expired
CacheVerdict(GURL("https://bar.com/def/index.jsp"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"bar.com/def/", now);
CacheVerdict(GURL("https://bar.com/xyz/index.jsp"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
LoginReputationClientResponse::PHISHING, 0, "bar.com/xyz/", now);
ASSERT_EQ(2U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
@@ -813,48 +898,50 @@ TEST_P(PasswordProtectionServiceTest, TestCleanUpExpiredVerdict) {
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
LoginReputationClientResponse actual_verdict;
// Has cached PASSWORD_REUSE_EVENT verdict for foo.com/abc/.
- EXPECT_EQ(
- LoginReputationClientResponse::LOW_REPUTATION,
- password_protection_service_->GetCachedVerdict(
- GURL("https://foo.com/abc/test.jsp"),
- LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
+ EXPECT_EQ(LoginReputationClientResponse::LOW_REPUTATION,
+ password_protection_service_->GetCachedVerdict(
+ GURL("https://foo.com/abc/test.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD, &actual_verdict));
// No cached PASSWORD_REUSE_EVENT verdict for foo.com/def.
- EXPECT_EQ(
- LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
- password_protection_service_->GetCachedVerdict(
- GURL("https://foo.com/def/index.jsp"),
- LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
+ EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+ password_protection_service_->GetCachedVerdict(
+ GURL("https://foo.com/def/index.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD, &actual_verdict));
// No cached PASSWORD_REUSE_EVENT verdict for bar.com/abc.
- EXPECT_EQ(
- LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
- password_protection_service_->GetCachedVerdict(
- GURL("https://bar.com/abc/index.jsp"),
- LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
+ EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+ password_protection_service_->GetCachedVerdict(
+ GURL("https://bar.com/abc/index.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD, &actual_verdict));
// No cached PASSWORD_REUSE_EVENT verdict for bar.com/def.
+ EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+ password_protection_service_->GetCachedVerdict(
+ GURL("https://bar.com/def/index.jsp"),
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD, &actual_verdict));
+
+ // Has cached UNFAMILIAR_LOGIN_PAGE verdict for bar.com/def.
EXPECT_EQ(
- LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+ LoginReputationClientResponse::SAFE,
password_protection_service_->GetCachedVerdict(
GURL("https://bar.com/def/index.jsp"),
- LoginReputationClientRequest::PASSWORD_REUSE_EVENT, &actual_verdict));
-
- // Has cached UNFAMILIAR_LOGIN_PAGE verdict for bar.com/def.
- EXPECT_EQ(LoginReputationClientResponse::SAFE,
- password_protection_service_->GetCachedVerdict(
- GURL("https://bar.com/def/index.jsp"),
- LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
- &actual_verdict));
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN, &actual_verdict));
// No cached UNFAMILIAR_LOGIN_PAGE verdict for bar.com/xyz.
- EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
- password_protection_service_->GetCachedVerdict(
- GURL("https://bar.com/xyz/index.jsp"),
- LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
- &actual_verdict));
+ EXPECT_EQ(
+ LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+ password_protection_service_->GetCachedVerdict(
+ GURL("https://bar.com/xyz/index.jsp"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN, &actual_verdict));
}
TEST_P(PasswordProtectionServiceTest,
TestCleanUpExpiredVerdictWithInvalidEntry) {
- CacheInvalidVerdict();
+ CacheInvalidVerdict(PasswordReuseEvent::SIGN_IN_PASSWORD);
ContentSettingsForOneType password_protection_settings;
content_setting_map_->GetSettingsForOneType(
CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
@@ -878,7 +965,8 @@ TEST_P(PasswordProtectionServiceTest, VerifyPasswordOnFocusRequestProto) {
expected_response.SerializeAsString());
InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
- 100000 /* timeout in ms*/);
+ 100000 /* timeout in ms */,
+ GetWebContents());
password_protection_service_->WaitForResponse();
const LoginReputationClientRequest* actual_request =
@@ -904,9 +992,9 @@ TEST_P(PasswordProtectionServiceTest,
expected_response.SerializeAsString());
// Initialize request triggered by chrome sync password reuse.
- InitializeAndStartPasswordEntryRequest(true /* matches_sync_password */, {},
- false /* match whitelist */,
- 100000 /* timeout in ms*/);
+ InitializeAndStartPasswordEntryRequest(
+ PasswordReuseEvent::SIGN_IN_PASSWORD, {}, false /* match whitelist */,
+ 100000 /* timeout in ms*/, GetWebContents());
password_protection_service_->WaitForResponse();
const LoginReputationClientRequest* actual_request =
@@ -934,8 +1022,8 @@ TEST_P(PasswordProtectionServiceTest,
// Initialize request triggered by saved password reuse.
InitializeAndStartPasswordEntryRequest(
- false /* matches_sync_password */, {kSavedDomain, kSavedDomain2},
- false /* match whitelist */, 100000 /* timeout in ms*/);
+ PasswordReuseEvent::SAVED_PASSWORD, {kSavedDomain, kSavedDomain2},
+ false /* match whitelist */, 100000 /* timeout in ms*/, GetWebContents());
password_protection_service_->WaitForResponse();
const LoginReputationClientRequest* actual_request =
@@ -956,8 +1044,7 @@ TEST_P(PasswordProtectionServiceTest,
TEST_P(PasswordProtectionServiceTest, VerifyShouldShowModalWarning) {
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
- .WillRepeatedly(
- Return(LoginReputationClientRequest::PasswordReuseEvent::GMAIL));
+ .WillRepeatedly(Return(PasswordReuseEvent::GMAIL));
EXPECT_CALL(*password_protection_service_,
GetPasswordProtectionWarningTriggerPref())
.WillRepeatedly(Return(PHISHING_REUSE));
@@ -965,23 +1052,49 @@ TEST_P(PasswordProtectionServiceTest, VerifyShouldShowModalWarning) {
// Don't show modal warning if it is not a password reuse ping.
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
- /*matches_sync_password=*/true, LoginReputationClientResponse::PHISHING));
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
+ LoginReputationClientResponse::PHISHING));
- // Don't show modal warning if it is not a sync password reuse.
+ // Don't show modal warning if it is a saved password reuse.
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
- /*matches_sync_password=*/false,
+ PasswordReuseEvent::SAVED_PASSWORD,
LoginReputationClientResponse::PHISHING));
- // Show modal warning otherwise
+ // Don't show modal warning if it is a non-sync gaia password reuse.
+ EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::OTHER_GAIA_PASSWORD,
+ LoginReputationClientResponse::PHISHING));
+
+ // Don't show modal warning if reused password type unknown.
+ EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
+ LoginReputationClientResponse::PHISHING));
+
+ // Don't show modal warning if it is a sync password reuse but user is not
+ // signed in.
+ EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
+ .WillRepeatedly(Return(PasswordReuseEvent::NOT_SIGNED_IN));
+ EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
+ LoginReputationClientResponse::PHISHING));
+
+ // Show warning if it is a sync password reuse and user is signed in and
+ // is not manged by enterprise.
+ EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
+ .WillRepeatedly(Return(PasswordReuseEvent::GMAIL));
EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
- /*matches_sync_password=*/true, LoginReputationClientResponse::PHISHING));
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
+ LoginReputationClientResponse::PHISHING));
- // For a GSUITE account, don't show warning if password protection is off.
+ // For a GSUITE account, don't show warning if password protection is set to
+ // off by enterprise policy.
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
- .WillRepeatedly(
- Return(LoginReputationClientRequest::PasswordReuseEvent::GSUITE));
+ .WillRepeatedly(Return(PasswordReuseEvent::GSUITE));
EXPECT_CALL(*password_protection_service_,
GetPasswordProtectionWarningTriggerPref())
.WillRepeatedly(Return(PASSWORD_PROTECTION_OFF));
@@ -990,7 +1103,8 @@ TEST_P(PasswordProtectionServiceTest, VerifyShouldShowModalWarning) {
password_protection_service_->GetPasswordProtectionWarningTriggerPref());
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
- /*matches_sync_password=*/true, LoginReputationClientResponse::PHISHING));
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
+ LoginReputationClientResponse::PHISHING));
// For a GSUITE account, show warning if password protection is set to
// PHISHING_REUSE.
@@ -1002,40 +1116,183 @@ TEST_P(PasswordProtectionServiceTest, VerifyShouldShowModalWarning) {
password_protection_service_->GetPasswordProtectionWarningTriggerPref());
EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
- /*matches_sync_password=*/true, LoginReputationClientResponse::PHISHING));
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
+ LoginReputationClientResponse::PHISHING));
// Modal dialog warning is also shown on LOW_REPUTATION verdict.
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
- .WillRepeatedly(
- Return(LoginReputationClientRequest::PasswordReuseEvent::GMAIL));
+ .WillRepeatedly(Return(PasswordReuseEvent::GMAIL));
EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
- /*matches_sync_password=*/true,
+ PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::LOW_REPUTATION));
+
+ // Modal dialog warning should not be shown for enterprise password reuse
+ // if it is turned off by policy.
+ EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
+ .WillRepeatedly(Return(PasswordReuseEvent::NOT_SIGNED_IN));
+ EXPECT_CALL(*password_protection_service_,
+ GetPasswordProtectionWarningTriggerPref())
+ .WillRepeatedly(Return(PASSWORD_PROTECTION_OFF));
+ EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::ENTERPRISE_PASSWORD,
+ LoginReputationClientResponse::PHISHING));
+
+ // Show modal warning for enterprise password reuse if the trigger is
+ // configured to PHISHING_REUSE.
+ EXPECT_CALL(*password_protection_service_,
+ GetPasswordProtectionWarningTriggerPref())
+ .WillRepeatedly(Return(PHISHING_REUSE));
+ EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+ PasswordReuseEvent::ENTERPRISE_PASSWORD,
+ LoginReputationClientResponse::PHISHING));
}
TEST_P(PasswordProtectionServiceTest, VerifyIsEventLoggingEnabled) {
// For user who is not signed-in, event logging should be disabled.
- EXPECT_EQ(LoginReputationClientRequest::PasswordReuseEvent::NOT_SIGNED_IN,
+ EXPECT_EQ(PasswordReuseEvent::NOT_SIGNED_IN,
password_protection_service_->GetSyncAccountType());
EXPECT_FALSE(password_protection_service_->IsEventLoggingEnabled());
// Event logging should be enable for all signed-in users..
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
- .WillRepeatedly(
- Return(LoginReputationClientRequest::PasswordReuseEvent::GMAIL));
- EXPECT_EQ(LoginReputationClientRequest::PasswordReuseEvent::GMAIL,
+ .WillRepeatedly(Return(PasswordReuseEvent::GMAIL));
+ EXPECT_EQ(PasswordReuseEvent::GMAIL,
password_protection_service_->GetSyncAccountType());
EXPECT_TRUE(password_protection_service_->IsEventLoggingEnabled());
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
- .WillRepeatedly(
- Return(LoginReputationClientRequest::PasswordReuseEvent::GSUITE));
- EXPECT_EQ(LoginReputationClientRequest::PasswordReuseEvent::GSUITE,
+ .WillRepeatedly(Return(PasswordReuseEvent::GSUITE));
+ EXPECT_EQ(PasswordReuseEvent::GSUITE,
password_protection_service_->GetSyncAccountType());
EXPECT_TRUE(password_protection_service_->IsEventLoggingEnabled());
}
+TEST_P(PasswordProtectionServiceTest, VerifyContentTypeIsPopulated) {
+ content::WebContents* web_contents = GetWebContents();
+
+ content::WebContentsTester::For(web_contents)
+ ->SetMainFrameMimeType("application/pdf");
+
+ InitializeAndStartPasswordOnFocusRequest(
+ false /* match whitelist */, 10000 /* timeout in ms */, web_contents);
+
+ password_protection_service_->WaitForResponse();
+
+ EXPECT_EQ(
+ "application/pdf",
+ password_protection_service_->GetLatestRequestProto()->content_type());
+}
+
+TEST_P(PasswordProtectionServiceTest, VerifyIsSupportedPasswordTypeForPinging) {
+ {
+ base::test::ScopedFeatureList scoped_features;
+ scoped_features.InitAndDisableFeature(kEnterprisePasswordProtectionV1);
+ EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
+ .WillRepeatedly(Return(PasswordReuseEvent::NOT_SIGNED_IN));
+ EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::SAVED_PASSWORD));
+ EXPECT_FALSE(
+ password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::SIGN_IN_PASSWORD));
+ EXPECT_FALSE(
+ password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::OTHER_GAIA_PASSWORD));
+ EXPECT_FALSE(
+ password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::ENTERPRISE_PASSWORD));
+
+ EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
+ .WillRepeatedly(Return(PasswordReuseEvent::GMAIL));
+ EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::SAVED_PASSWORD));
+ EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::SIGN_IN_PASSWORD));
+ EXPECT_FALSE(
+ password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::OTHER_GAIA_PASSWORD));
+ EXPECT_FALSE(
+ password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::ENTERPRISE_PASSWORD));
+ }
+
+ {
+ base::test::ScopedFeatureList scoped_features;
+ scoped_features.InitAndEnableFeature(kEnterprisePasswordProtectionV1);
+ EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
+ .WillRepeatedly(Return(PasswordReuseEvent::NOT_SIGNED_IN));
+ EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::SAVED_PASSWORD));
+ EXPECT_FALSE(
+ password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::SIGN_IN_PASSWORD));
+ EXPECT_FALSE(
+ password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::OTHER_GAIA_PASSWORD));
+ EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::ENTERPRISE_PASSWORD));
+
+ EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
+ .WillRepeatedly(Return(PasswordReuseEvent::GMAIL));
+ EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::SAVED_PASSWORD));
+ EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::SIGN_IN_PASSWORD));
+ EXPECT_FALSE(
+ password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::OTHER_GAIA_PASSWORD));
+ EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+ PasswordReuseEvent::ENTERPRISE_PASSWORD));
+ }
+}
+
+TEST_P(PasswordProtectionServiceTest, TestMigrateCachedVerdict) {
+ ASSERT_EQ(0U, GetStoredVerdictCount(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+ ASSERT_EQ(0U, GetStoredVerdictCount(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+ base::Time now = base::Time::Now();
+ CacheVerdict(GURL("http://foo.com/abc/index.jsp"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
+ LoginReputationClientResponse::LOW_REPUTATION, 10 * kMinute,
+ "foo.com/abc/", now);
+ CacheVerdict(GURL("http://bar.com/index.jsp"),
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+ PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
+ LoginReputationClientResponse::PHISHING, 10 * kMinute, "bar.com",
+ now);
+
+ // Now add some entries that need to be migrated to |content_setting_map_|.
+ GURL hostname("http://foo.com");
+ std::unique_ptr<base::DictionaryValue> cache_dictionary =
+ base::DictionaryValue::From(content_setting_map_->GetWebsiteSetting(
+ hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
+ std::string(), nullptr));
+ DCHECK(cache_dictionary);
+ cache_dictionary->SetKey("foo.com/abc", base::Value("some value"));
+ cache_dictionary->SetKey("bar.com", base::Value("some value"));
+ content_setting_map_->SetWebsiteSettingDefaultScope(
+ hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
+ std::string(), std::move(cache_dictionary));
+
+ password_protection_service_->MigrateCachedVerdicts();
+ cache_dictionary =
+ base::DictionaryValue::From(content_setting_map_->GetWebsiteSetting(
+ hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
+ std::string(), nullptr));
+ EXPECT_FALSE(cache_dictionary->FindKey("foo.com/abc"));
+ EXPECT_FALSE(cache_dictionary->FindKey("bar.com"));
+ histograms_.ExpectBucketCount(
+ "PasswordProtection.NumberOfVerdictsMigratedDuringInitialization", 2, 1);
+ EXPECT_EQ(0U, GetStoredVerdictCount(
+ LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+ EXPECT_EQ(2U, GetStoredVerdictCount(
+ LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+}
+
INSTANTIATE_TEST_CASE_P(
InstSBERIncog,
PasswordProtectionServiceTest,
diff --git a/chromium/components/safe_browsing/base_ping_manager.cc b/chromium/components/safe_browsing/ping_manager.cc
index c950fb56cb0..e3a35fe20e9 100644
--- a/chromium/components/safe_browsing/base_ping_manager.cc
+++ b/chromium/components/safe_browsing/ping_manager.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/safe_browsing/base_ping_manager.h"
+#include "components/safe_browsing/ping_manager.h"
#include <utility>
@@ -71,14 +71,13 @@ namespace safe_browsing {
// SafeBrowsingPingManager implementation ----------------------------------
// static
-std::unique_ptr<BasePingManager> BasePingManager::Create(
+std::unique_ptr<PingManager> PingManager::Create(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const SafeBrowsingProtocolConfig& config) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
- return base::WrapUnique(new BasePingManager(url_loader_factory, config));
+ return base::WrapUnique(new PingManager(url_loader_factory, config));
}
-BasePingManager::BasePingManager(
+PingManager::PingManager(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const SafeBrowsingProtocolConfig& config)
: client_name_(config.client_name),
@@ -89,12 +88,12 @@ BasePingManager::BasePingManager(
version_ = ProtocolManagerHelper::Version();
}
-BasePingManager::~BasePingManager() {}
+PingManager::~PingManager() {}
// net::URLFetcherDelegate implementation ----------------------------------
// All SafeBrowsing request responses are handled here.
-void BasePingManager::OnURLLoaderComplete(
+void PingManager::OnURLLoaderComplete(
network::SimpleURLLoader* source,
std::unique_ptr<std::string> response_body) {
auto it = std::find_if(
@@ -107,7 +106,7 @@ void BasePingManager::OnURLLoaderComplete(
}
// Sends a SafeBrowsing "hit" report.
-void BasePingManager::ReportSafeBrowsingHit(
+void PingManager::ReportSafeBrowsingHit(
const safe_browsing::HitReport& hit_report) {
auto resource_request = std::make_unique<network::ResourceRequest>();
GURL report_url = SafeBrowsingHitUrl(hit_report);
@@ -124,13 +123,13 @@ void BasePingManager::ReportSafeBrowsingHit(
report_ptr->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory_.get(),
- base::BindOnce(&BasePingManager::OnURLLoaderComplete,
- base::Unretained(this), report_ptr.get()));
+ base::BindOnce(&PingManager::OnURLLoaderComplete, base::Unretained(this),
+ report_ptr.get()));
safebrowsing_reports_.insert(std::move(report_ptr));
}
// Sends threat details for users who opt-in.
-void BasePingManager::ReportThreatDetails(const std::string& report) {
+void PingManager::ReportThreatDetails(const std::string& report) {
GURL report_url = ThreatDetailsUrl();
auto resource_request = std::make_unique<network::ResourceRequest>();
@@ -145,12 +144,12 @@ void BasePingManager::ReportThreatDetails(const std::string& report) {
loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory_.get(),
- base::BindOnce(&BasePingManager::OnURLLoaderComplete,
- base::Unretained(this), loader.get()));
+ base::BindOnce(&PingManager::OnURLLoaderComplete, base::Unretained(this),
+ loader.get()));
safebrowsing_reports_.insert(std::move(loader));
}
-GURL BasePingManager::SafeBrowsingHitUrl(
+GURL PingManager::SafeBrowsingHitUrl(
const safe_browsing::HitReport& hit_report) const {
DCHECK(hit_report.threat_type == SB_THREAT_TYPE_URL_MALWARE ||
hit_report.threat_type == SB_THREAT_TYPE_URL_PHISHING ||
@@ -235,7 +234,7 @@ GURL BasePingManager::SafeBrowsingHitUrl(
hit_report.is_metrics_reporting_active, user_population_comp.c_str()));
}
-GURL BasePingManager::ThreatDetailsUrl() const {
+GURL PingManager::ThreatDetailsUrl() const {
std::string url = base::StringPrintf(
"%s/clientreport/malware?client=%s&appver=%s&pver=1.0",
url_prefix_.c_str(), client_name_.c_str(), version_.c_str());
diff --git a/chromium/components/safe_browsing/base_ping_manager.h b/chromium/components/safe_browsing/ping_manager.h
index 54b5d9d1ca3..4f48c18e575 100644
--- a/chromium/components/safe_browsing/base_ping_manager.h
+++ b/chromium/components/safe_browsing/ping_manager.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_SAFE_BROWSING_BASE_PING_MANAGER_H_
-#define COMPONENTS_SAFE_BROWSING_BASE_PING_MANAGER_H_
+#ifndef COMPONENTS_SAFE_BROWSING_PING_MANAGER_H_
+#define COMPONENTS_SAFE_BROWSING_PING_MANAGER_H_
// A class that reports basic safebrowsing statistics to Google's SafeBrowsing
// servers.
@@ -27,12 +27,12 @@ class SimpleURLLoader;
namespace safe_browsing {
-class BasePingManager {
+class PingManager {
public:
- virtual ~BasePingManager();
+ virtual ~PingManager();
// Create an instance of the safe browsing ping manager.
- static std::unique_ptr<BasePingManager> Create(
+ static std::unique_ptr<PingManager> Create(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const SafeBrowsingProtocolConfig& config);
@@ -49,18 +49,17 @@ class BasePingManager {
void ReportThreatDetails(const std::string& report);
protected:
- friend class BasePingManagerTest;
- // Constructs a BasePingManager that issues network requests
+ friend class PingManagerTest;
+ // Constructs a PingManager that issues network requests
// using |url_loader_factory|.
- BasePingManager(
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
- const SafeBrowsingProtocolConfig& config);
+ PingManager(scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ const SafeBrowsingProtocolConfig& config);
private:
- FRIEND_TEST_ALL_PREFIXES(BasePingManagerTest, TestSafeBrowsingHitUrl);
- FRIEND_TEST_ALL_PREFIXES(BasePingManagerTest, TestThreatDetailsUrl);
- FRIEND_TEST_ALL_PREFIXES(BasePingManagerTest, TestReportThreatDetails);
- FRIEND_TEST_ALL_PREFIXES(BasePingManagerTest, TestReportSafeBrowsingHit);
+ FRIEND_TEST_ALL_PREFIXES(PingManagerTest, TestSafeBrowsingHitUrl);
+ FRIEND_TEST_ALL_PREFIXES(PingManagerTest, TestThreatDetailsUrl);
+ FRIEND_TEST_ALL_PREFIXES(PingManagerTest, TestReportThreatDetails);
+ FRIEND_TEST_ALL_PREFIXES(PingManagerTest, TestReportSafeBrowsingHit);
typedef std::set<std::unique_ptr<network::SimpleURLLoader>> Reports;
@@ -87,9 +86,9 @@ class BasePingManager {
// We add both "hit" and "detail" fetchers in this set.
Reports safebrowsing_reports_;
- DISALLOW_COPY_AND_ASSIGN(BasePingManager);
+ DISALLOW_COPY_AND_ASSIGN(PingManager);
};
} // namespace safe_browsing
-#endif // COMPONENTS_SAFE_BROWSING_BASE_PING_MANAGER_H_
+#endif // COMPONENTS_SAFE_BROWSING_PING_MANAGER_H_
diff --git a/chromium/components/safe_browsing/base_ping_manager_unittest.cc b/chromium/components/safe_browsing/ping_manager_unittest.cc
index d34c87f6c19..bbc311a2a43 100644
--- a/chromium/components/safe_browsing/base_ping_manager_unittest.cc
+++ b/chromium/components/safe_browsing/ping_manager_unittest.cc
@@ -3,7 +3,7 @@
// found in the LICENSE file.
//
-#include "components/safe_browsing/base_ping_manager.h"
+#include "components/safe_browsing/ping_manager.h"
#include "base/base64.h"
#include "base/logging.h"
#include "base/run_loop.h"
@@ -25,9 +25,9 @@ static const char kAppVer[] = "1.0";
namespace safe_browsing {
-class BasePingManagerTest : public testing::Test {
+class PingManagerTest : public testing::Test {
public:
- BasePingManagerTest() {}
+ PingManagerTest() {}
protected:
void SetUp() override {
@@ -40,17 +40,17 @@ class BasePingManagerTest : public testing::Test {
SafeBrowsingProtocolConfig config;
config.client_name = kClient;
config.url_prefix = kUrlPrefix;
- ping_manager_.reset(new BasePingManager(nullptr, config));
+ ping_manager_.reset(new PingManager(nullptr, config));
ping_manager_->version_ = kAppVer;
}
- BasePingManager* ping_manager() { return ping_manager_.get(); }
+ PingManager* ping_manager() { return ping_manager_.get(); }
std::string key_param_;
- std::unique_ptr<BasePingManager> ping_manager_;
+ std::unique_ptr<PingManager> ping_manager_;
};
-TEST_F(BasePingManagerTest, TestSafeBrowsingHitUrl) {
+TEST_F(PingManagerTest, TestSafeBrowsingHitUrl) {
HitReport base_hp;
base_hp.malicious_url = GURL("http://malicious.url.com");
base_hp.page_url = GURL("http://page.url.com");
@@ -185,7 +185,7 @@ TEST_F(BasePingManagerTest, TestSafeBrowsingHitUrl) {
}
}
-TEST_F(BasePingManagerTest, TestThreatDetailsUrl) {
+TEST_F(PingManagerTest, TestThreatDetailsUrl) {
EXPECT_EQ(
"https://prefix.com/foo/clientreport/malware?"
"client=unittest&appver=1.0&pver=1.0" +
diff --git a/chromium/components/safe_browsing/proto/PRESUBMIT.py b/chromium/components/safe_browsing/proto/PRESUBMIT.py
new file mode 100644
index 00000000000..0e7381237a4
--- /dev/null
+++ b/chromium/components/safe_browsing/proto/PRESUBMIT.py
@@ -0,0 +1,20 @@
+# 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.
+
+def CheckChangeOnUpload(input_api, output_api):
+ # Warn if the proto file is not modified without also modifying
+ # the WebUI.
+ proto_path = 'components/safe_browsing/proto/csd.proto'
+ web_ui_path = 'components/safe_browsing/web_ui/safe_browsing_ui.cc'
+
+ if proto_path in input_api.change.LocalPaths() and \
+ not web_ui_path in input_api.change.LocalPaths():
+ return [
+ output_api.PresubmitPromptWarning(
+ 'You modified the one or more of the CSD protos in: \n'
+ ' ' + proto_path + '\n'
+ 'without changing the WebUI in: \n'
+ ' ' + web_ui_path + '\n')
+ ]
+ return []
diff --git a/chromium/components/safe_browsing/proto/csd.proto b/chromium/components/safe_browsing/proto/csd.proto
index b314be984fc..1079f03d1c0 100644
--- a/chromium/components/safe_browsing/proto/csd.proto
+++ b/chromium/components/safe_browsing/proto/csd.proto
@@ -260,6 +260,26 @@ message LoginReputationClientRequest {
GSUITE = 2;
}
optional SyncAccountType sync_account_type = 4;
+
+ // Type of password being reused.
+ enum ReusedPasswordType {
+ REUSED_PASSWORD_TYPE_UNKNOWN = 0;
+
+ // Password saved in Chrome's password manager.
+ SAVED_PASSWORD = 1;
+
+ // Password used in Chrome sign-in.
+ SIGN_IN_PASSWORD = 2;
+
+ // Other Gaia password used in Chrome's content area other than Chrome's
+ // sign-in password.
+ OTHER_GAIA_PASSWORD = 3;
+
+ // Non-Gaia enterprise password captured from enterprise login page.
+ ENTERPRISE_PASSWORD = 4;
+ }
+ optional ReusedPasswordType reused_password_type = 5
+ [default = REUSED_PASSWORD_TYPE_UNKNOWN];
}
optional PasswordReuseEvent password_reuse_event = 4;
@@ -272,6 +292,9 @@ message LoginReputationClientRequest {
// If user clicked through safe browsing interstitial on this page.
optional bool clicked_through_interstitial = 7;
+
+ // The MIME type of the page, e.g. "application/pdf".
+ optional string content_type = 8;
}
// The message is used for client response for login reputation requests.
@@ -456,6 +479,13 @@ message ClientDownloadRequest {
// A file we don't support, but we've decided to sample and send
// a light-ping.
SAMPLED_UNSUPPORTED_FILE = 10;
+ // .rar file containing one of the other executable types.
+ RAR_COMPRESSED_EXECUTABLE = 11;
+ // .rar file containing another archive.
+ RAR_COMPRESSED_ARCHIVE = 12;
+ // .rar file that Chrome failed to unpack to the point of finding
+ // executables or archives.
+ INVALID_RAR = 13;
}
optional DownloadType download_type = 10 [default = WIN_EXECUTABLE];
@@ -675,7 +705,12 @@ message ReferrerChainEntry {
// How this navigation is initiated.
optional NavigationInitiation navigation_initiation = 10;
- // next available tag number: 11.
+ // Whether we think this entry may have been launched by an external
+ // application. This will have no false negatives, but some false positives
+ // are possible.
+ optional bool maybe_launched_by_external_application = 11;
+
+ // next available tag number: 12.
} // End of ReferrerChainEntry
message ClientDownloadResponse {
diff --git a/chromium/components/safe_browsing/renderer/renderer_url_loader_throttle.cc b/chromium/components/safe_browsing/renderer/renderer_url_loader_throttle.cc
index 83d932bbb0c..c3ce971d773 100644
--- a/chromium/components/safe_browsing/renderer/renderer_url_loader_throttle.cc
+++ b/chromium/components/safe_browsing/renderer/renderer_url_loader_throttle.cc
@@ -72,7 +72,8 @@ void RendererURLLoaderThrottle::WillStartRequest(
void RendererURLLoaderThrottle::WillRedirectRequest(
const net::RedirectInfo& redirect_info,
const network::ResourceResponseHead& response_head,
- bool* defer) {
+ bool* defer,
+ std::vector<std::string>* to_be_removed_headers) {
// If |blocked_| is true, the resource load has been canceled and there
// shouldn't be such a notification.
DCHECK(!blocked_);
diff --git a/chromium/components/safe_browsing/renderer/renderer_url_loader_throttle.h b/chromium/components/safe_browsing/renderer/renderer_url_loader_throttle.h
index f6040302e74..3d90186463a 100644
--- a/chromium/components/safe_browsing/renderer/renderer_url_loader_throttle.h
+++ b/chromium/components/safe_browsing/renderer/renderer_url_loader_throttle.h
@@ -34,9 +34,11 @@ class RendererURLLoaderThrottle : public content::URLLoaderThrottle,
void DetachFromCurrentSequence() override;
void WillStartRequest(network::ResourceRequest* request,
bool* defer) override;
- void WillRedirectRequest(const net::RedirectInfo& redirect_info,
- const network::ResourceResponseHead& response_head,
- bool* defer) override;
+ void WillRedirectRequest(
+ const net::RedirectInfo& redirect_info,
+ const network::ResourceResponseHead& response_head,
+ bool* defer,
+ std::vector<std::string>* to_be_removed_headers) override;
void WillProcessResponse(const GURL& response_url,
const network::ResourceResponseHead& response_head,
bool* defer) override;
diff --git a/chromium/components/safe_browsing/triggers/ad_sampler_trigger_unittest.cc b/chromium/components/safe_browsing/triggers/ad_sampler_trigger_unittest.cc
index 5419fcfee3a..c50c304b8ed 100644
--- a/chromium/components/safe_browsing/triggers/ad_sampler_trigger_unittest.cc
+++ b/chromium/components/safe_browsing/triggers/ad_sampler_trigger_unittest.cc
@@ -5,7 +5,7 @@
#include "components/safe_browsing/triggers/ad_sampler_trigger.h"
#include "base/metrics/field_trial_params.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_simple_task_runner.h"
#include "components/prefs/testing_pref_service.h"
diff --git a/chromium/components/safe_browsing/triggers/suspicious_site_trigger_unittest.cc b/chromium/components/safe_browsing/triggers/suspicious_site_trigger_unittest.cc
index 4473c9fb1a8..51fd9a3afab 100644
--- a/chromium/components/safe_browsing/triggers/suspicious_site_trigger_unittest.cc
+++ b/chromium/components/safe_browsing/triggers/suspicious_site_trigger_unittest.cc
@@ -4,7 +4,7 @@
#include "components/safe_browsing/triggers/suspicious_site_trigger.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_simple_task_runner.h"
#include "components/prefs/testing_pref_service.h"
#include "components/safe_browsing/common/safe_browsing_prefs.h"
diff --git a/chromium/components/safe_browsing/triggers/trigger_manager.cc b/chromium/components/safe_browsing/triggers/trigger_manager.cc
index 46fc4d0798f..30b6ade3729 100644
--- a/chromium/components/safe_browsing/triggers/trigger_manager.cc
+++ b/chromium/components/safe_browsing/triggers/trigger_manager.cc
@@ -67,15 +67,6 @@ bool TriggerNeedsOptInForCollection(const TriggerType trigger_type) {
bool CanSendReport(const SBErrorOptions& error_display_options,
const TriggerType trigger_type) {
- // If the |kAdSamplerCollectButDontSendFeature| feature is enabled then we
- // will overlook other checks to force the report to be created (which is safe
- // because we ensure it will be discarded downstream).
- // TODO(crbug.com/776893): Remove the feature and this logic.
- if (trigger_type == TriggerType::AD_SAMPLE &&
- base::FeatureList::IsEnabled(kAdSamplerCollectButDontSendFeature)) {
- return true;
- }
-
// Some triggers require that users are eligible for elevated Scout data
// collection in order to run.
bool scout_check_ok = !TriggerNeedsScout(trigger_type) ||
@@ -136,14 +127,6 @@ bool TriggerManager::CanStartDataCollectionWithReason(
const TriggerType trigger_type,
TriggerManagerReason* out_reason) {
*out_reason = TriggerManagerReason::NO_REASON;
- // If the |kAdSamplerCollectButDontSendFeature| feature is enabled then we
- // will overlook other checks to force the report to be created (which is safe
- // because we ensure it will be discarded downstream).
- // TODO(crbug.com/776893): Remote the feature and this logic.
- if (trigger_type == TriggerType::AD_SAMPLE &&
- base::FeatureList::IsEnabled(kAdSamplerCollectButDontSendFeature)) {
- return true;
- }
// Some triggers require that the user be opted-in to extended reporting in
// order to run, while others can run without opt-in (eg: because users are
diff --git a/chromium/components/safe_browsing/triggers/trigger_manager_unittest.cc b/chromium/components/safe_browsing/triggers/trigger_manager_unittest.cc
index 77230aef016..f3f5a725024 100644
--- a/chromium/components/safe_browsing/triggers/trigger_manager_unittest.cc
+++ b/chromium/components/safe_browsing/triggers/trigger_manager_unittest.cc
@@ -146,14 +146,6 @@ class TriggerManagerTest : public ::testing::Test {
return trigger_manager_.data_collectors_map_;
}
- void SetCollectDontSendFeature(bool enabled) {
- feature_list_.reset(new base::test::ScopedFeatureList);
- if (enabled)
- feature_list_->InitAndEnableFeature(kAdSamplerCollectButDontSendFeature);
- else
- feature_list_->InitAndDisableFeature(kAdSamplerCollectButDontSendFeature);
- }
-
private:
TriggerManager trigger_manager_;
MockThreatDetailsFactory mock_threat_details_factory_;
@@ -379,13 +371,7 @@ TEST_F(TriggerManagerTest, AdSamplerTrigger) {
SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
EXPECT_FALSE(
StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
- // It can be forced on via a finch feature.
- SetCollectDontSendFeature(true);
- EXPECT_TRUE(
- StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
- EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
- web_contents, true));
- SetCollectDontSendFeature(false);
+
// Confirm it can fire when we re-enable SBEROptInAllowed
SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
EXPECT_TRUE(
@@ -393,21 +379,13 @@ TEST_F(TriggerManagerTest, AdSamplerTrigger) {
EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
web_contents, true));
- // Disabling Scout disables this trigger even if the legacy SBER is enabled.
+ // Disabling Scout disables this trigger.
SetPref(prefs::kSafeBrowsingScoutReportingEnabled, false);
- SetPref(prefs::kSafeBrowsingExtendedReportingEnabled, true);
EXPECT_FALSE(
StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
- // It can be forced on via a finch feature.
- SetCollectDontSendFeature(true);
- EXPECT_TRUE(
- StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
- EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
- web_contents, true));
- SetCollectDontSendFeature(false);
+
// Confirm it can fire when we re-enable Scout and disable legacy SBER.
SetPref(prefs::kSafeBrowsingScoutReportingEnabled, true);
- SetPref(prefs::kSafeBrowsingExtendedReportingEnabled, false);
EXPECT_TRUE(
StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
@@ -417,13 +395,7 @@ TEST_F(TriggerManagerTest, AdSamplerTrigger) {
SetTriggerHasQuota(TriggerType::AD_SAMPLE, false);
EXPECT_FALSE(
StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
- // It can be forced on via a finch feature.
- SetCollectDontSendFeature(true);
- EXPECT_TRUE(
- StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
- EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
- web_contents, true));
- SetCollectDontSendFeature(false);
+
// Confirm it can fire again when quota is available.
SetTriggerHasQuota(TriggerType::AD_SAMPLE, true);
EXPECT_TRUE(
@@ -442,13 +414,5 @@ TEST_F(TriggerManagerTest, AdSamplerTrigger_Incognito) {
// all triggers have quota), but the incognito window prevents it from firing.
EXPECT_FALSE(
StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
-
- // The Finch feature makes the trigger fire even in incognito (which is safe
- // because data is discarded and not sent to Google downstream).
- SetCollectDontSendFeature(true);
- EXPECT_TRUE(
- StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
- EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
- web_contents, true));
}
} // namespace safe_browsing
diff --git a/chromium/components/safe_browsing/triggers/trigger_throttler.cc b/chromium/components/safe_browsing/triggers/trigger_throttler.cc
index 0afce2d270b..b4165eb498d 100644
--- a/chromium/components/safe_browsing/triggers/trigger_throttler.cc
+++ b/chromium/components/safe_browsing/triggers/trigger_throttler.cc
@@ -16,6 +16,7 @@
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";
@@ -42,7 +43,8 @@ void ParseTriggerTypeAndQuotaParam(
// First, handle the trigger-specific features.
int suspicious_site_quota = base::GetFieldTrialParamByFeatureAsInt(
- kSuspiciousSiteTriggerQuotaFeature, kSuspiciousSiteTriggerQuotaParam, 0);
+ kSuspiciousSiteTriggerQuotaFeature, kSuspiciousSiteTriggerQuotaParam,
+ kSuspiciousSiteTriggerDefaultQuota);
if (suspicious_site_quota > 0) {
trigger_type_and_quota_list->push_back(
std::make_pair(TriggerType::SUSPICIOUS_SITE, suspicious_site_quota));
diff --git a/chromium/components/safe_browsing/triggers/trigger_throttler.h b/chromium/components/safe_browsing/triggers/trigger_throttler.h
index 0ce7c36b96d..dc0c816d168 100644
--- a/chromium/components/safe_browsing/triggers/trigger_throttler.h
+++ b/chromium/components/safe_browsing/triggers/trigger_throttler.h
@@ -19,6 +19,9 @@ namespace safe_browsing {
// Default quota for ad sampler trigger.
extern const size_t kAdSamplerTriggerDefaultQuota;
+// Default quota for suspicious site trigger.
+extern const size_t kSuspiciousSiteTriggerDefaultQuota;
+
// Param name of the finch param containing the quota for the suspicious site
// trigger.
extern const char kSuspiciousSiteTriggerQuotaParam[];
diff --git a/chromium/components/safe_browsing/triggers/trigger_throttler_unittest.cc b/chromium/components/safe_browsing/triggers/trigger_throttler_unittest.cc
index ca3d5d46df5..1ef5be965f8 100644
--- a/chromium/components/safe_browsing/triggers/trigger_throttler_unittest.cc
+++ b/chromium/components/safe_browsing/triggers/trigger_throttler_unittest.cc
@@ -303,19 +303,20 @@ TEST_F(TriggerThrottlerTestFinch, AdSamplerDefaultQuota) {
}
TEST_F(TriggerThrottlerTestFinch, SuspiciousSiteTriggerDefaultQuota) {
- // Ensure that suspicious site trigger is disabled by default.
+ // Ensure that suspicious site trigger is enabled with default quota.
TriggerThrottler throttler_default(nullptr);
- EXPECT_EQ(0u, GetDailyQuotaForTrigger(throttler_default,
- TriggerType::SUSPICIOUS_SITE));
- EXPECT_FALSE(throttler_default.TriggerCanFire(TriggerType::SUSPICIOUS_SITE));
+ EXPECT_EQ(
+ kSuspiciousSiteTriggerDefaultQuota,
+ GetDailyQuotaForTrigger(throttler_default, TriggerType::SUSPICIOUS_SITE));
+ EXPECT_TRUE(throttler_default.TriggerCanFire(TriggerType::SUSPICIOUS_SITE));
base::FieldTrialList field_trial_list(nullptr);
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatureList(
SetupQuotaInFinch(TriggerType::SUSPICIOUS_SITE,
- "Group_SuspiciousSiteTriggerDefaultQuota", 5));
+ "Group_SuspiciousSiteTriggerDefaultQuota", 7));
TriggerThrottler throttler_finch(nullptr);
- EXPECT_EQ(5u, GetDailyQuotaForTrigger(throttler_finch,
+ EXPECT_EQ(7u, GetDailyQuotaForTrigger(throttler_finch,
TriggerType::SUSPICIOUS_SITE));
}
diff --git a/chromium/components/safe_browsing/web_ui/BUILD.gn b/chromium/components/safe_browsing/web_ui/BUILD.gn
index 3c17276efb5..a764df17553 100644
--- a/chromium/components/safe_browsing/web_ui/BUILD.gn
+++ b/chromium/components/safe_browsing/web_ui/BUILD.gn
@@ -13,6 +13,7 @@ static_library("web_ui") {
deps = [
":constants",
"//base",
+ "//components/password_manager/core/browser:hash_password_manager",
"//components/resources:components_resources_grit",
"//components/resources:components_scaled_resources_grit",
"//components/safe_browsing:csd_proto",
@@ -21,6 +22,7 @@ static_library("web_ui") {
"//components/safe_browsing/common:safe_browsing_prefs",
"//components/safe_browsing/db:v4_local_database_manager",
"//components/strings:components_strings_grit",
+ "//components/sync/protocol:protocol",
"//components/user_prefs:user_prefs",
"//content/public/browser",
"//net",
diff --git a/chromium/components/safe_browsing/web_ui/DEPS b/chromium/components/safe_browsing/web_ui/DEPS
index a1cf89e9c68..1936f2f2b05 100644
--- a/chromium/components/safe_browsing/web_ui/DEPS
+++ b/chromium/components/safe_browsing/web_ui/DEPS
@@ -1,7 +1,8 @@
include_rules = [
"+components/grit/components_resources.h",
+ "+components/password_manager/core/browser/hash_password_manager.h",
"+components/user_prefs",
- "+components/safe_browsing/proto/csd.pb.h",
+ "+components/safe_browsing/proto/csd.pb.h",
"+components/strings/grit/components_strings.h",
"+components/grit/components_scaled_resources.h",
"+components/safe_browsing_db",
diff --git a/chromium/components/safe_browsing/web_ui/resources/safe_browsing.css b/chromium/components/safe_browsing/web_ui/resources/safe_browsing.css
index 5fe64a5f6bb..0b9eaf08492 100644
--- a/chromium/components/safe_browsing/web_ui/resources/safe_browsing.css
+++ b/chromium/components/safe_browsing/web_ui/resources/safe_browsing.css
@@ -24,3 +24,12 @@ h1, h2, h3, p {
font-weight: normal;
line-height: 1.5;
}
+table.request-response {
+ table-layout:fixed;
+ width: 100%;
+ word-break:break-all;
+ white-space:pre-wrap;
+}
+table.request-response td {
+ width: 50%;
+}
diff --git a/chromium/components/safe_browsing/web_ui/resources/safe_browsing.html b/chromium/components/safe_browsing/web_ui/resources/safe_browsing.html
index 75f2f3aefe3..6bf9333a715 100644
--- a/chromium/components/safe_browsing/web_ui/resources/safe_browsing.html
+++ b/chromium/components/safe_browsing/web_ui/resources/safe_browsing.html
@@ -5,37 +5,82 @@
<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">
<script src="chrome://resources/js/promise_resolver.js"></script>
<script src="chrome://resources/js/cr.js"></script>
<script src="chrome://resources/js/load_time_data.js"></script>
<script src="chrome://resources/js/util.js"></script>
- <script src="safe_browsing.js"></script>
+ <script src="chrome://resources/js/cr/ui.js"></script>
+ <script src="chrome://resources/js/cr/ui/focus_outline_manager.js"></script>
+ <script src="chrome://resources/js/cr/ui/tabs.js"></script>
</head>
<body>
<div id="header">
<h1 id="sb-title">Safe Browsing</h1>
</div>
- <p>$i18n{sbUnderConstruction}</p>
- <h2>Experiments</h2>
- <div class="content">
- <p id="experiments-list"></p>
- </div>
- <h2>Preferences</h2>
- <div class="content">
- <p id="preferences-list"></p>
- </div>
- <h2>Database Manager</h2>
- <div class="content">
- <p id="database-info-list"></p>
- </div>
- <h2>Full Hash Cache</h2>
- <div class="content">
- <p id="full-hash-cache-info"></p>
- </div>
- <h2>Threat Details</h2>
- <div class="content">
- <p id="threat-details-list"></p>
- </div>
+ <tabbox>
+ <tabs>
+ <tab>Preferences</tab>
+ <tab>Database Manager</tab>
+ <tab>Hash Cache</tab>
+ <tab>ClientSafeBrowsingReportRequests</tab>
+ <tab>Download Protection</tab>
+ <tab>Password Protection</tab>
+ </tabs>
+ <tabpanels>
+ <tabpanel>
+ <h2>Experiments</h2>
+ <div class="content">
+ <p id="experiments-list"></p>
+ </div>
+ <h2>Preferences</h2>
+ <div class="content">
+ <p id="preferences-list"></p>
+ </div>
+ </tabpanel>
+ <tabpanel>
+ <h2>Database Manager</h2>
+ <div class="content">
+ <p id="database-info-list"></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 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>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>Password Protection Pings</h2>
+ <table id="pg-ping-list" class="request-response"></table>
+ </tabpanel>
+ </tabpanels>
+ </tabbox>
<script src="chrome://resources/js/i18n_template.js"></script>
+ <script src="safe_browsing.js"></script>
</body>
</html>
diff --git a/chromium/components/safe_browsing/web_ui/resources/safe_browsing.js b/chromium/components/safe_browsing/web_ui/resources/safe_browsing.js
index 091e0847d1d..a7e7bade59c 100644
--- a/chromium/components/safe_browsing/web_ui/resources/safe_browsing.js
+++ b/chromium/components/safe_browsing/web_ui/resources/safe_browsing.js
@@ -1,6 +1,8 @@
/* Copyright 2017 The Chromium Authors. All rights reserved.
-* Use of this source code is governed by a BSD-style license that can be
-* found in the LICENSE file. */
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+cr.ui.decorate('tabbox', cr.ui.TabBox);
cr.define('safe_browsing', function() {
'use strict';
@@ -11,70 +13,197 @@ cr.define('safe_browsing', function() {
* addPreferences() (below).
*/
function initialize() {
- cr.sendWithPromise('getExperiments', []).then((experiments) =>
- addExperiments(experiments));
+ cr.sendWithPromise('getExperiments', [])
+ .then((experiments) => addExperiments(experiments));
cr.sendWithPromise('getPrefs', []).then((prefs) => addPrefs(prefs));
+ cr.sendWithPromise('getSavedPasswords', []).then((passwords) =>
+ addSavedPasswords(passwords));
cr.sendWithPromise('getDatabaseManagerInfo', []).then(
function(databaseState) {
var fullHashCacheState = databaseState.splice(-1,1);
addDatabaseManagerInfo(databaseState);
addFullHashCacheInfo(fullHashCacheState);
});
- cr.sendWithPromise('getSentThreatDetails', []).then((threatDetails) =>
- addThreatDetailsInfo(threatDetails));
- cr.addWebUIListener('threat-details-update', function(result) {
- addThreatDetailsInfo(result);
+
+ cr.sendWithPromise('getSentClientDownloadRequests', [])
+ .then(
+ (sentClientDownloadRequests) => {
+ sentClientDownloadRequests.forEach(function(cdr) {
+ addSentClientDownloadRequestsInfo(cdr);
+ })});
+ cr.addWebUIListener(
+ 'sent-client-download-requests-update', function(result) {
+ addSentClientDownloadRequestsInfo(result);
+ });
+
+ cr.sendWithPromise('getReceivedClientDownloadResponses', [])
+ .then(
+ (receivedClientDownloadResponses) => {
+ receivedClientDownloadResponses.forEach(function(cdr) {
+ addReceivedClientDownloadResponseInfo(cdr);
+ })});
+ cr.addWebUIListener(
+ 'received-client-download-responses-update', function(result) {
+ addReceivedClientDownloadResponseInfo(result);
+ });
+
+ cr.sendWithPromise('getSentCSBRRs', [])
+ .then((sentCSBRRs) => {sentCSBRRs.forEach(function(csbrr) {
+ addSentCSBRRsInfo(csbrr);
+ })});
+ cr.addWebUIListener('sent-csbrr-update', function(result) {
+ addSentCSBRRsInfo(result);
+ });
+
+ cr.sendWithPromise('getPGEvents', [])
+ .then((pgEvents) => {pgEvents.forEach(function(pgEvent) {
+ addPGEvent(pgEvent);
+ })});
+ cr.addWebUIListener('sent-pg-event', function(result) {
+ addPGEvent(result);
+ });
+
+ cr.sendWithPromise('getPGPings', [])
+ .then((pgPings) => { pgPings.forEach(function (pgPing) {
+ addPGPing(pgPing);
+ })});
+ cr.addWebUIListener('pg-pings-update', function(result) {
+ addPGPing(result);
+ });
+
+ cr.sendWithPromise('getPGResponses', [])
+ .then((pgResponses) => { pgResponses.forEach(function (pgResponse) {
+ addPGResponse(pgResponse);
+ })});
+ cr.addWebUIListener('pg-responses-update', function(result) {
+ addPGResponse(result);
});
}
function addExperiments(result) {
var resLength = result.length;
- var experimentsListFormatted = "";
+ var experimentsListFormatted = '';
for (var i = 0; i < resLength; i += 2) {
experimentsListFormatted += "<div><b>" + result[i + 1] +
"</b>: " + result[i] + "</div>";
- }
- $('experiments-list').innerHTML = experimentsListFormatted;
+ }
+ $('experiments-list').innerHTML = experimentsListFormatted;
}
function addPrefs(result) {
- var resLength = result.length;
- var preferencesListFormatted = "";
+ var resLength = result.length;
+ var preferencesListFormatted = "";
+
+ for (var i = 0; i < resLength; i += 2) {
+ preferencesListFormatted += "<div><b>" + result[i + 1] + "</b>: " +
+ result[i] + "</div>";
+ }
+ $('preferences-list').innerHTML = preferencesListFormatted;
+ }
- for (var i = 0; i < resLength; i += 2) {
- preferencesListFormatted += "<div><b>" + result[i + 1] + "</b>: " +
- result[i] + "</div>";
+ function addSavedPasswords(result) {
+ var resLength = result.length;
+ var savedPasswordFormatted = "";
+
+ for (var i = 0; i < resLength; i += 2) {
+ savedPasswordFormatted += "<div>" + result[i];
+ if (result[i+1]) {
+ savedPasswordFormatted += " (GAIA password)";
+ } else {
+ savedPasswordFormatted += " (Enterprise password)";
}
- $('preferences-list').innerHTML = preferencesListFormatted;
+ savedPasswordFormatted += "</div>";
+ }
+
+ $('saved-passwords').innerHTML = savedPasswordFormatted;
}
function addDatabaseManagerInfo(result) {
- var resLength = result.length;
- var preferencesListFormatted = "";
+ var resLength = result.length;
+ var preferencesListFormatted = "";
- for (var i = 0; i < resLength; i += 2) {
- preferencesListFormatted += "<div><b>" + result[i] + "</b>: " +
- result[i+1] + "</div>";
- }
- $('database-info-list').innerHTML = preferencesListFormatted;
+ for (var i = 0; i < resLength; i += 2) {
+ preferencesListFormatted += "<div><b>" + result[i] + "</b>: " +
+ result[i + 1] + "</div>";
+ }
+ $('database-info-list').innerHTML = preferencesListFormatted;
}
function addFullHashCacheInfo(result) {
- $('full-hash-cache-info').innerHTML = result;
+ $('full-hash-cache-info').innerHTML = result;
+ }
+
+ function addSentClientDownloadRequestsInfo(result) {
+ var logDiv = $('sent-client-download-requests-list');
+ appendChildWithInnerText(logDiv, result);
+ }
+
+ function addReceivedClientDownloadResponseInfo(result) {
+ var logDiv = $('received-client-download-response-list');
+ appendChildWithInnerText(logDiv, result);
+ }
+
+ function addSentCSBRRsInfo(result) {
+ var logDiv = $('sent-csbrrs-list');
+ appendChildWithInnerText(logDiv, result);
+ }
+
+ function addPGEvent(result) {
+ var logDiv = $('pg-event-log');
+ var eventFormatted = "[" + (new Date(result["time"])).toLocaleString() +
+ "] " + result['message'];
+ appendChildWithInnerText(logDiv, eventFormatted);
+ }
+
+ function addPGPingRow(token) {
+ var row = $('pg-ping-list').insertRow();
+ row.className = 'content';
+ row.id = 'pg-ping-list-' + token;
+ row.insertCell();
+ row.insertCell();
+ }
+
+ function addPGPing(result) {
+ var token = result[0];
+ var request = result[1];
+
+ if ($('pg-ping-list-'+token) == undefined) {
+ addPGPingRow(token);
+ }
+
+ var cell = $('pg-ping-list-'+token).cells[0];
+ appendChildWithInnerText(cell, request);
+ }
+
+ function addPGResponse(result) {
+ var token = result[0];
+ var response = result[1];
+
+ if ($('pg-ping-list-'+token) == undefined) {
+ addPGPingRow(token);
+ }
+
+ var cell = $('pg-ping-list-'+token).cells[1];
+ appendChildWithInnerText(cell, response);
}
- function addThreatDetailsInfo(result) {
- var logDiv = $('threat-details-list');
- if (!logDiv)
- return;
- var textDiv = document.createElement('div');
- textDiv.innerText = result;
- logDiv.appendChild(textDiv);
+ function appendChildWithInnerText(logDiv, text) {
+ if (!logDiv)
+ return;
+ var textDiv = document.createElement('div');
+ textDiv.innerText = text;
+ logDiv.appendChild(textDiv);
}
return {
- addThreatDetailsInfo: addThreatDetailsInfo,
+ addSentCSBRRsInfo: addSentCSBRRsInfo,
+ addSentClientDownloadRequestsInfo: addSentClientDownloadRequestsInfo,
+ addReceivedClientDownloadResponseInfo:
+ addReceivedClientDownloadResponseInfo,
+ addPGEvent: addPGEvent,
+ addPGPing: addPGPing,
+ addPGResponse: addPGResponse,
initialize: initialize,
};
});
diff --git a/chromium/components/safe_browsing/web_ui/safe_browsing_ui.cc b/chromium/components/safe_browsing/web_ui/safe_browsing_ui.cc
index b6bbd413c0e..e95569398af 100644
--- a/chromium/components/safe_browsing/web_ui/safe_browsing_ui.cc
+++ b/chromium/components/safe_browsing/web_ui/safe_browsing_ui.cc
@@ -22,6 +22,7 @@
#include "base/values.h"
#include "components/grit/components_resources.h"
#include "components/grit/components_scaled_resources.h"
+#include "components/password_manager/core/browser/hash_password_manager.h"
#include "components/safe_browsing/common/safe_browsing_prefs.h"
#include "components/safe_browsing/features.h"
#include "components/safe_browsing/web_ui/constants.h"
@@ -34,6 +35,12 @@
#endif
using base::Time;
+using PasswordReuseLookup =
+ sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseLookup;
+using PasswordReuseDetected =
+ sync_pb::UserEventSpecifics::GaiaPasswordReuse::PasswordReuseDetected;
+using PasswordReuseDialogInteraction = sync_pb::UserEventSpecifics::
+ GaiaPasswordReuse::PasswordReuseDialogInteraction;
namespace safe_browsing {
WebUIInfoSingleton::WebUIInfoSingleton() = default;
@@ -45,16 +52,102 @@ WebUIInfoSingleton* WebUIInfoSingleton::GetInstance() {
return base::Singleton<WebUIInfoSingleton>::get();
}
-void WebUIInfoSingleton::AddToReportsSent(
- std::unique_ptr<ClientSafeBrowsingReportRequest> report_request) {
+// static
+bool WebUIInfoSingleton::HasListener() {
+ return !GetInstance()->webui_instances_.empty();
+}
+
+void WebUIInfoSingleton::AddToClientDownloadRequestsSent(
+ std::unique_ptr<ClientDownloadRequest> client_download_request) {
+ if (webui_instances_.empty())
+ return;
+
+ for (auto* webui_listener : webui_instances_)
+ webui_listener->NotifyClientDownloadRequestJsListener(
+ client_download_request.get());
+ client_download_requests_sent_.push_back(std::move(client_download_request));
+}
+
+void WebUIInfoSingleton::ClearClientDownloadRequestsSent() {
+ std::vector<std::unique_ptr<ClientDownloadRequest>>().swap(
+ client_download_requests_sent_);
+}
+
+void WebUIInfoSingleton::AddToClientDownloadResponsesReceived(
+ std::unique_ptr<ClientDownloadResponse> client_download_response) {
+ if (webui_instances_.empty())
+ return;
+
+ for (auto* webui_listener : webui_instances_)
+ webui_listener->NotifyClientDownloadResponseJsListener(
+ client_download_response.get());
+ client_download_responses_received_.push_back(
+ std::move(client_download_response));
+}
+
+void WebUIInfoSingleton::ClearClientDownloadResponsesReceived() {
+ std::vector<std::unique_ptr<ClientDownloadResponse>>().swap(
+ client_download_responses_received_);
+}
+
+void WebUIInfoSingleton::AddToCSBRRsSent(
+ std::unique_ptr<ClientSafeBrowsingReportRequest> csbrr) {
+ if (webui_instances_.empty())
+ return;
+
for (auto* webui_listener : webui_instances_)
- webui_listener->NotifyThreatDetailsJsListener(report_request.get());
- reports_sent_.push_back(std::move(report_request));
+ webui_listener->NotifyCSBRRJsListener(csbrr.get());
+ csbrrs_sent_.push_back(std::move(csbrr));
}
-void WebUIInfoSingleton::ClearReportsSent() {
+void WebUIInfoSingleton::ClearCSBRRsSent() {
std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>>().swap(
- reports_sent_);
+ csbrrs_sent_);
+}
+
+void WebUIInfoSingleton::AddToPGEvents(
+ const sync_pb::UserEventSpecifics& event) {
+ if (webui_instances_.empty())
+ return;
+
+ for (auto* webui_listener : webui_instances_)
+ webui_listener->NotifyPGEventJsListener(event);
+
+ pg_event_log_.push_back(event);
+}
+
+void WebUIInfoSingleton::ClearPGEvents() {
+ std::vector<sync_pb::UserEventSpecifics>().swap(pg_event_log_);
+}
+
+int WebUIInfoSingleton::AddToPGPings(
+ const LoginReputationClientRequest& request) {
+ if (webui_instances_.empty())
+ return -1;
+
+ for (auto* webui_listener : webui_instances_)
+ webui_listener->NotifyPGPingJsListener(pg_pings_.size(), request);
+
+ pg_pings_.push_back(request);
+
+ return pg_pings_.size() - 1;
+}
+
+void WebUIInfoSingleton::AddToPGResponses(
+ int token,
+ const LoginReputationClientResponse& response) {
+ if (webui_instances_.empty())
+ return;
+
+ for (auto* webui_listener : webui_instances_)
+ webui_listener->NotifyPGResponseJsListener(token, response);
+
+ pg_responses_[token] = response;
+}
+
+void WebUIInfoSingleton::ClearPGPings() {
+ std::vector<LoginReputationClientRequest>().swap(pg_pings_);
+ std::map<int, LoginReputationClientResponse>().swap(pg_responses_);
}
void WebUIInfoSingleton::RegisterWebUIInstance(SafeBrowsingUIHandler* webui) {
@@ -65,8 +158,11 @@ void WebUIInfoSingleton::UnregisterWebUIInstance(SafeBrowsingUIHandler* webui) {
webui_instances_.erase(
std::remove(webui_instances_.begin(), webui_instances_.end(), webui),
webui_instances_.end());
- if (webui_instances_.empty())
- ClearReportsSent();
+ if (webui_instances_.empty()) {
+ ClearCSBRRsSent();
+ ClearClientDownloadRequestsSent();
+ ClearPGEvents();
+ }
}
namespace {
@@ -234,9 +330,89 @@ std::string AddFullHashCacheInfo(
#endif
-std::string ParseThreatDetailsInfo(
- const ClientSafeBrowsingReportRequest& report) {
- std::string report_request_parsed;
+std::string SerializeClientDownloadRequest(const ClientDownloadRequest& cdr) {
+ base::DictionaryValue dict;
+ if (cdr.has_url())
+ dict.SetString("url", cdr.url());
+ if (cdr.has_download_type())
+ dict.SetInteger("download_type", cdr.download_type());
+ if (cdr.has_length())
+ dict.SetInteger("length", cdr.length());
+ if (cdr.has_file_basename())
+ dict.SetString("file_basename", cdr.file_basename());
+ if (cdr.has_archive_valid())
+ dict.SetBoolean("archive_valid", cdr.archive_valid());
+
+ auto archived_binaries = std::make_unique<base::ListValue>();
+ for (const auto& archived_binary : cdr.archived_binary()) {
+ auto dict_archived_binary = std::make_unique<base::DictionaryValue>();
+ if (archived_binary.has_file_basename())
+ dict_archived_binary->SetString("file_basename",
+ archived_binary.file_basename());
+ if (archived_binary.has_download_type())
+ dict_archived_binary->SetInteger("download_type",
+ archived_binary.download_type());
+ if (archived_binary.has_length())
+ dict_archived_binary->SetInteger("length", archived_binary.length());
+ archived_binaries->Append(std::move(dict_archived_binary));
+ }
+ dict.SetList("archived_binary", std::move(archived_binaries));
+
+ base::Value* request_tree = &dict;
+ std::string request_serialized;
+ JSONStringValueSerializer serializer(&request_serialized);
+ serializer.set_pretty_print(true);
+ serializer.Serialize(*request_tree);
+ return request_serialized;
+}
+
+std::string SerializeClientDownloadResponse(const ClientDownloadResponse& cdr) {
+ base::DictionaryValue dict;
+
+ switch (cdr.verdict()) {
+ case ClientDownloadResponse::SAFE:
+ dict.SetPath({"verdict"}, base::Value("SAFE"));
+ break;
+ case ClientDownloadResponse::DANGEROUS:
+ dict.SetPath({"verdict"}, base::Value("DANGEROUS"));
+ break;
+ case ClientDownloadResponse::UNCOMMON:
+ dict.SetPath({"verdict"}, base::Value("UNCOMMON"));
+ break;
+ case ClientDownloadResponse::POTENTIALLY_UNWANTED:
+ dict.SetPath({"verdict"}, base::Value("POTENTIALLY_UNWANTED"));
+ break;
+ case ClientDownloadResponse::DANGEROUS_HOST:
+ dict.SetPath({"verdict"}, base::Value("DANGEROUS_HOST"));
+ break;
+ case ClientDownloadResponse::UNKNOWN:
+ dict.SetPath({"verdict"}, base::Value("UNKNOWN"));
+ break;
+ }
+
+ if (cdr.has_more_info()) {
+ dict.SetPath({"more_info", "description"},
+ base::Value(cdr.more_info().description()));
+ dict.SetPath({"more_info", "url"}, base::Value(cdr.more_info().url()));
+ }
+
+ if (cdr.has_token()) {
+ dict.SetPath({"token"}, base::Value(cdr.token()));
+ }
+
+ if (cdr.has_upload()) {
+ dict.SetPath({"upload"}, base::Value(cdr.upload()));
+ }
+
+ base::Value* request_tree = &dict;
+ std::string request_serialized;
+ JSONStringValueSerializer serializer(&request_serialized);
+ serializer.set_pretty_print(true);
+ serializer.Serialize(*request_tree);
+ return request_serialized;
+}
+
+std::string SerializeCSBRR(const ClientSafeBrowsingReportRequest& report) {
base::DictionaryValue report_request;
if (report.has_type()) {
report_request.SetInteger("type", static_cast<int>(report.type()));
@@ -260,10 +436,413 @@ std::string ParseThreatDetailsInfo(
}
base::Value* report_request_tree = &report_request;
- JSONStringValueSerializer serializer(&report_request_parsed);
+ std::string report_request_serialized;
+ JSONStringValueSerializer serializer(&report_request_serialized);
serializer.set_pretty_print(true);
serializer.Serialize(*report_request_tree);
- return report_request_parsed;
+ return report_request_serialized;
+}
+
+base::DictionaryValue SerializePGEvent(
+ const sync_pb::UserEventSpecifics& event) {
+ base::DictionaryValue result;
+
+ base::Time timestamp = base::Time::FromDeltaSinceWindowsEpoch(
+ base::TimeDelta::FromMicroseconds(event.event_time_usec()));
+ result.SetDouble("time", timestamp.ToJsTime());
+
+ base::DictionaryValue event_dict;
+
+ sync_pb::UserEventSpecifics::GaiaPasswordReuse reuse =
+ event.gaia_password_reuse_event();
+ if (reuse.has_reuse_detected()) {
+ event_dict.SetPath({"reuse_detected", "status", "enabled"},
+ base::Value(reuse.reuse_detected().status().enabled()));
+
+ std::string reporting_population;
+ switch (
+ reuse.reuse_detected().status().safe_browsing_reporting_population()) {
+ case PasswordReuseDetected::SafeBrowsingStatus::
+ REPORTING_POPULATION_UNSPECIFIED:
+ reporting_population = "REPORTING_POPULATION_UNSPECIFIED";
+ break;
+ case PasswordReuseDetected::SafeBrowsingStatus::NONE:
+ reporting_population = "NONE";
+ break;
+ case PasswordReuseDetected::SafeBrowsingStatus::EXTENDED_REPORTING:
+ reporting_population = "EXTENDED_REPORTING";
+ break;
+ case PasswordReuseDetected::SafeBrowsingStatus::SCOUT:
+ reporting_population = "SCOUT";
+ break;
+ }
+ event_dict.SetPath({"reuse_detected", "status", "reporting_population"},
+ base::Value(reporting_population));
+ }
+
+ if (reuse.has_reuse_lookup()) {
+ std::string lookup_result;
+ switch (reuse.reuse_lookup().lookup_result()) {
+ case PasswordReuseLookup::UNSPECIFIED:
+ lookup_result = "UNSPECIFIED";
+ break;
+ case PasswordReuseLookup::WHITELIST_HIT:
+ lookup_result = "WHITELIST_HIT";
+ break;
+ case PasswordReuseLookup::CACHE_HIT:
+ lookup_result = "CACHE_HIT";
+ break;
+ case PasswordReuseLookup::REQUEST_SUCCESS:
+ lookup_result = "REQUEST_SUCCESS";
+ break;
+ case PasswordReuseLookup::REQUEST_FAILURE:
+ lookup_result = "REQUEST_FAILURE";
+ break;
+ case PasswordReuseLookup::URL_UNSUPPORTED:
+ lookup_result = "URL_UNSUPPORTED";
+ break;
+ case PasswordReuseLookup::ENTERPRISE_WHITELIST_HIT:
+ lookup_result = "ENTERPRISE_WHITELIST_HIT";
+ break;
+ case PasswordReuseLookup::TURNED_OFF_BY_POLICY:
+ lookup_result = "TURNED_OFF_BY_POLICY";
+ break;
+ }
+ event_dict.SetPath({"reuse_lookup", "lookup_result"},
+ base::Value(lookup_result));
+
+ std::string verdict;
+ switch (reuse.reuse_lookup().verdict()) {
+ case PasswordReuseLookup::VERDICT_UNSPECIFIED:
+ verdict = "VERDICT_UNSPECIFIED";
+ break;
+ case PasswordReuseLookup::SAFE:
+ verdict = "SAFE";
+ break;
+ case PasswordReuseLookup::LOW_REPUTATION:
+ verdict = "LOW_REPUTATION";
+ break;
+ case PasswordReuseLookup::PHISHING:
+ verdict = "PHISHING";
+ break;
+ }
+ event_dict.SetPath({"reuse_lookup", "verdict"}, base::Value(verdict));
+ event_dict.SetPath({"reuse_lookup", "verdict_token"},
+ base::Value(reuse.reuse_lookup().verdict_token()));
+ }
+
+ if (reuse.has_dialog_interaction()) {
+ std::string interaction_result;
+ switch (reuse.dialog_interaction().interaction_result()) {
+ case PasswordReuseDialogInteraction::UNSPECIFIED:
+ interaction_result = "UNSPECIFIED";
+ break;
+ case PasswordReuseDialogInteraction::WARNING_ACTION_TAKEN:
+ interaction_result = "WARNING_ACTION_TAKEN";
+ break;
+ case PasswordReuseDialogInteraction::WARNING_ACTION_IGNORED:
+ interaction_result = "WARNING_ACTION_IGNORED";
+ break;
+ case PasswordReuseDialogInteraction::WARNING_UI_IGNORED:
+ interaction_result = "WARNING_UI_IGNORED";
+ break;
+ }
+ event_dict.SetPath({"dialog_interaction", "interaction_result"},
+ base::Value(interaction_result));
+ }
+
+ std::string event_serialized;
+ JSONStringValueSerializer serializer(&event_serialized);
+ serializer.set_pretty_print(true);
+ serializer.Serialize(event_dict);
+ result.SetString("message", event_serialized);
+ return result;
+}
+
+base::Value SerializeReferrer(const ReferrerChainEntry& referrer) {
+ base::DictionaryValue referrer_dict;
+ referrer_dict.SetKey("url", base::Value(referrer.url()));
+ referrer_dict.SetKey("main_frame_url",
+ base::Value(referrer.main_frame_url()));
+
+ std::string url_type;
+ switch (referrer.type()) {
+ case ReferrerChainEntry::EVENT_URL:
+ url_type = "EVENT_URL";
+ break;
+ case ReferrerChainEntry::LANDING_PAGE:
+ url_type = "LANDING_PAGE";
+ break;
+ case ReferrerChainEntry::LANDING_REFERRER:
+ url_type = "LANDING_REFERRER";
+ break;
+ case ReferrerChainEntry::CLIENT_REDIRECT:
+ url_type = "CLIENT_REDIRECT";
+ break;
+ case ReferrerChainEntry::DEPRECATED_SERVER_REDIRECT:
+ url_type = "DEPRECATED_SERVER_REDIRECT";
+ break;
+ case ReferrerChainEntry::RECENT_NAVIGATION:
+ url_type = "RECENT_NAVIGATION";
+ break;
+ }
+ referrer_dict.SetKey("type", base::Value(url_type));
+
+ base::ListValue ip_addresses;
+ for (const std::string& ip_address : referrer.ip_addresses()) {
+ ip_addresses.GetList().push_back(base::Value(ip_address));
+ }
+ referrer_dict.SetKey("ip_addresses", std::move(ip_addresses));
+
+ referrer_dict.SetKey("referrer_url", base::Value(referrer.referrer_url()));
+
+ referrer_dict.SetKey("referrer_main_frame_url",
+ base::Value(referrer.referrer_main_frame_url()));
+
+ referrer_dict.SetKey("is_retargeting",
+ base::Value(referrer.is_retargeting()));
+
+ referrer_dict.SetKey("navigation_time_msec",
+ base::Value(referrer.navigation_time_msec()));
+
+ base::ListValue server_redirects;
+ for (const ReferrerChainEntry::ServerRedirect& server_redirect :
+ referrer.server_redirect_chain()) {
+ server_redirects.GetList().push_back(base::Value(server_redirect.url()));
+ }
+ referrer_dict.SetKey("server_redirect_chain", std::move(server_redirects));
+
+ std::string navigation_initiation;
+ switch (referrer.navigation_initiation()) {
+ case ReferrerChainEntry::UNDEFINED:
+ navigation_initiation = "UNDEFINED";
+ break;
+ case ReferrerChainEntry::BROWSER_INITIATED:
+ navigation_initiation = "BROWSER_INITIATED";
+ break;
+ case ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE:
+ navigation_initiation = "RENDERER_INITIATED_WITHOUT_USER_GESTURE";
+ break;
+ case ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE:
+ navigation_initiation = "RENDERER_INITIATED_WITH_USER_GESTURE";
+ break;
+ }
+ referrer_dict.SetKey("navigation_initiation",
+ base::Value(navigation_initiation));
+
+ referrer_dict.SetKey(
+ "maybe_launched_by_external_application",
+ base::Value(referrer.maybe_launched_by_external_application()));
+
+ return std::move(referrer_dict);
+}
+
+base::Value SerializeFrame(const LoginReputationClientRequest::Frame& frame) {
+ base::DictionaryValue frame_dict;
+ frame_dict.SetKey("frame_index", base::Value(frame.frame_index()));
+ frame_dict.SetKey("parent_frame_index",
+ base::Value(frame.parent_frame_index()));
+ frame_dict.SetKey("url", base::Value(frame.url()));
+ frame_dict.SetKey("has_password_field",
+ base::Value(frame.has_password_field()));
+
+ base::ListValue referrer_list;
+ for (const ReferrerChainEntry& referrer : frame.referrer_chain()) {
+ referrer_list.GetList().push_back(SerializeReferrer(referrer));
+ }
+ frame_dict.SetKey("referrer_chain", std::move(referrer_list));
+
+ frame_dict.SetPath(
+ {"referrer_chain_options", "recent_navigations_to_collect"},
+ base::Value(
+ frame.referrer_chain_options().recent_navigations_to_collect()));
+
+ base::ListValue form_list;
+ for (const LoginReputationClientRequest::Frame::Form& form : frame.forms()) {
+ base::DictionaryValue form_dict;
+ form_dict.SetKey("action_url", base::Value(form.action_url()));
+ form_dict.SetKey("has_password_field",
+ base::Value(form.has_password_field()));
+ form_list.GetList().push_back(std::move(form_dict));
+ }
+ frame_dict.SetKey("forms", std::move(form_list));
+
+ return std::move(frame_dict);
+}
+
+base::Value SerializePasswordReuseEvent(
+ const LoginReputationClientRequest::PasswordReuseEvent& event) {
+ base::DictionaryValue event_dict;
+
+ base::ListValue domains_list;
+ for (const std::string& domain : event.domains_matching_password()) {
+ domains_list.GetList().push_back(base::Value(domain));
+ }
+ event_dict.SetKey("domains_matching_password", std::move(domains_list));
+
+ event_dict.SetKey("frame_id", base::Value(event.frame_id()));
+ event_dict.SetKey("is_chrome_signin_password",
+ base::Value(event.is_chrome_signin_password()));
+
+ std::string sync_account_type;
+ switch (event.sync_account_type()) {
+ case LoginReputationClientRequest::PasswordReuseEvent::NOT_SIGNED_IN:
+ sync_account_type = "NOT_SIGNED_IN";
+ break;
+ case LoginReputationClientRequest::PasswordReuseEvent::GMAIL:
+ sync_account_type = "GMAIL";
+ break;
+ case LoginReputationClientRequest::PasswordReuseEvent::GSUITE:
+ sync_account_type = "GSUITE";
+ break;
+ }
+ event_dict.SetKey("sync_account_type", base::Value(sync_account_type));
+
+ std::string reused_password_type;
+ switch (event.reused_password_type()) {
+ case LoginReputationClientRequest::PasswordReuseEvent::
+ REUSED_PASSWORD_TYPE_UNKNOWN:
+ reused_password_type = "REUSED_PASSWORD_TYPE_UNKNOWN";
+ break;
+ case LoginReputationClientRequest::PasswordReuseEvent::SAVED_PASSWORD:
+ reused_password_type = "SAVED_PASSWORD";
+ break;
+ case LoginReputationClientRequest::PasswordReuseEvent::SIGN_IN_PASSWORD:
+ reused_password_type = "SIGN_IN_PASSWORD";
+ break;
+ case LoginReputationClientRequest::PasswordReuseEvent::OTHER_GAIA_PASSWORD:
+ reused_password_type = "OTHER_GAIA_PASSWORD";
+ break;
+ case LoginReputationClientRequest::PasswordReuseEvent::ENTERPRISE_PASSWORD:
+ reused_password_type = "ENTERPRISE_PASSWORD";
+ break;
+ }
+ event_dict.SetKey("reused_password_type", base::Value(reused_password_type));
+
+ return std::move(event_dict);
+}
+
+base::Value SerializeChromeUserPopulation(
+ const ChromeUserPopulation& population) {
+ base::DictionaryValue population_dict;
+
+ std::string user_population;
+ switch (population.user_population()) {
+ case ChromeUserPopulation::UNKNOWN_USER_POPULATION:
+ user_population = "REUSED_PASSWORD_TYPE_UNKNOWN";
+ break;
+ case ChromeUserPopulation::SAFE_BROWSING:
+ user_population = "SAFE_BROWSING";
+ break;
+ case ChromeUserPopulation::EXTENDED_REPORTING:
+ user_population = "EXTENDED_REPORTING";
+ break;
+ }
+ population_dict.SetKey("user_population", base::Value(user_population));
+
+ population_dict.SetKey("is_history_sync_enabled",
+ base::Value(population.is_history_sync_enabled()));
+
+ base::ListValue finch_list;
+ for (const std::string& finch_group : population.finch_active_groups()) {
+ finch_list.GetList().push_back(base::Value(finch_group));
+ }
+ population_dict.SetKey("finch_active_groups", std::move(finch_list));
+
+ std::string management_status;
+ switch (population.profile_management_status()) {
+ case ChromeUserPopulation::UNKNOWN:
+ management_status = "UNKNOWN";
+ break;
+ case ChromeUserPopulation::UNAVAILABLE:
+ management_status = "UNAVAILABLE";
+ break;
+ case ChromeUserPopulation::NOT_MANAGED:
+ management_status = "NOT_MANAGED";
+ break;
+ case ChromeUserPopulation::ENTERPRISE_MANAGED:
+ management_status = "ENTERPRISE_MANAGED";
+ break;
+ }
+ population_dict.SetKey("profile_management_status",
+ base::Value(management_status));
+
+ return std::move(population_dict);
+}
+
+std::string SerializePGPing(const LoginReputationClientRequest& request) {
+ base::DictionaryValue request_dict;
+
+ request_dict.SetKey("page_url", base::Value(request.page_url()));
+
+ std::string trigger_type;
+ switch (request.trigger_type()) {
+ case LoginReputationClientRequest::TRIGGER_TYPE_UNSPECIFIED:
+ trigger_type = "TRIGGER_TYPE_UNSPECIFIED";
+ break;
+ case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE:
+ trigger_type = "UNFAMILIAR_LOGIN_PAGE";
+ break;
+ case LoginReputationClientRequest::PASSWORD_REUSE_EVENT:
+ trigger_type = "PASSWORD_REUSE_EVENT";
+ break;
+ }
+ request_dict.SetKey("trigger_type", base::Value(trigger_type));
+
+ base::ListValue frames_list;
+ for (const LoginReputationClientRequest::Frame& frame : request.frames()) {
+ frames_list.GetList().push_back(SerializeFrame(frame));
+ }
+ request_dict.SetKey("frames", std::move(frames_list));
+
+ request_dict.SetKey(
+ "password_reuse_event",
+ SerializePasswordReuseEvent(request.password_reuse_event()));
+ request_dict.SetKey("stored_verdict_cnt",
+ base::Value(request.stored_verdict_cnt()));
+ request_dict.SetKey("population",
+ SerializeChromeUserPopulation(request.population()));
+ request_dict.SetKey("clicked_through_interstitial",
+ base::Value(request.clicked_through_interstitial()));
+ request_dict.SetKey("content_type", base::Value(request.content_type()));
+
+ std::string request_serialized;
+ JSONStringValueSerializer serializer(&request_serialized);
+ serializer.set_pretty_print(true);
+ serializer.Serialize(request_dict);
+ return request_serialized;
+}
+
+std::string SerializePGResponse(const LoginReputationClientResponse& response) {
+ base::DictionaryValue response_dict;
+
+ std::string verdict;
+ switch (response.verdict_type()) {
+ case LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED:
+ verdict = "VERDICT_TYPE_UNSPECIFIED";
+ break;
+ case LoginReputationClientResponse::SAFE:
+ verdict = "SAFE";
+ break;
+ case LoginReputationClientResponse::LOW_REPUTATION:
+ verdict = "LOW_REPUTATION";
+ break;
+ case LoginReputationClientResponse::PHISHING:
+ verdict = "PHISHING";
+ break;
+ }
+ response_dict.SetKey("verdict_type", base::Value(verdict));
+ response_dict.SetKey("cache_duration_sec",
+ base::Value(int(response.cache_duration_sec())));
+ response_dict.SetKey("cache_expression",
+ base::Value(response.cache_expression()));
+ response_dict.SetKey("verdict_token", base::Value(response.verdict_token()));
+
+ std::string response_serialized;
+ JSONStringValueSerializer serializer(&response_serialized);
+ serializer.set_pretty_print(true);
+ serializer.Serialize(response_dict);
+ return response_serialized;
}
} // namespace
@@ -283,10 +862,6 @@ SafeBrowsingUI::SafeBrowsingUI(content::WebUI* web_ui)
web_ui->AddMessageHandler(
std::make_unique<SafeBrowsingUIHandler>(browser_context));
- // Add localized string resources.
- html_source->AddLocalizedString("sbUnderConstruction",
- IDS_SB_UNDER_CONSTRUCTION);
-
// Add required resources.
html_source->AddResourcePath("safe_browsing.css", IDR_SAFE_BROWSING_CSS);
html_source->AddResourcePath("safe_browsing.js", IDR_SAFE_BROWSING_JS);
@@ -323,6 +898,23 @@ void SafeBrowsingUIHandler::GetPrefs(const base::ListValue* args) {
user_prefs::UserPrefs::Get(browser_context_)));
}
+void SafeBrowsingUIHandler::GetSavedPasswords(const base::ListValue* args) {
+ password_manager::HashPasswordManager hash_manager(
+ user_prefs::UserPrefs::Get(browser_context_));
+
+ base::ListValue saved_passwords;
+ for (const password_manager::PasswordHashData& hash_data :
+ hash_manager.RetrieveAllPasswordHashes()) {
+ saved_passwords.AppendString(hash_data.username);
+ saved_passwords.AppendBoolean(hash_data.is_gaia_password);
+ }
+
+ AllowJavascript();
+ std::string callback_id;
+ args->GetString(0, &callback_id);
+ ResolveJavascriptCallback(base::Value(callback_id), saved_passwords);
+}
+
void SafeBrowsingUIHandler::GetDatabaseManagerInfo(
const base::ListValue* args) {
base::ListValue database_manager_info;
@@ -358,28 +950,160 @@ void SafeBrowsingUIHandler::GetDatabaseManagerInfo(
ResolveJavascriptCallback(base::Value(callback_id), database_manager_info);
}
-void SafeBrowsingUIHandler::GetSentThreatDetails(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetSentClientDownloadRequests(
+ const base::ListValue* args) {
+ const std::vector<std::unique_ptr<ClientDownloadRequest>>& cdrs =
+ WebUIInfoSingleton::GetInstance()->client_download_requests_sent();
+
+ base::ListValue cdrs_sent;
+
+ for (const auto& cdr : cdrs) {
+ cdrs_sent.GetList().push_back(
+ base::Value(SerializeClientDownloadRequest(*cdr)));
+ }
+
+ AllowJavascript();
+ std::string callback_id;
+ args->GetString(0, &callback_id);
+ ResolveJavascriptCallback(base::Value(callback_id), cdrs_sent);
+}
+
+void SafeBrowsingUIHandler::GetReceivedClientDownloadResponses(
+ const base::ListValue* args) {
+ const std::vector<std::unique_ptr<ClientDownloadResponse>>& cdrs =
+ WebUIInfoSingleton::GetInstance()->client_download_responses_received();
+
+ base::ListValue cdrs_received;
+
+ for (const auto& cdr : cdrs) {
+ cdrs_received.GetList().push_back(
+ base::Value(SerializeClientDownloadResponse(*cdr)));
+ }
+
+ AllowJavascript();
+ std::string callback_id;
+ args->GetString(0, &callback_id);
+ ResolveJavascriptCallback(base::Value(callback_id), cdrs_received);
+}
+
+void SafeBrowsingUIHandler::GetSentCSBRRs(const base::ListValue* args) {
const std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>>& reports =
- WebUIInfoSingleton::GetInstance()->reports_sent();
+ WebUIInfoSingleton::GetInstance()->csbrrs_sent();
base::ListValue sent_reports;
for (const auto& report : reports) {
- sent_reports.GetList().push_back(
- base::Value(ParseThreatDetailsInfo(*report)));
+ sent_reports.GetList().push_back(base::Value(SerializeCSBRR(*report)));
+ }
+
+ AllowJavascript();
+ std::string callback_id;
+ args->GetString(0, &callback_id);
+ ResolveJavascriptCallback(base::Value(callback_id), sent_reports);
+}
+
+void SafeBrowsingUIHandler::GetPGEvents(const base::ListValue* args) {
+ const std::vector<sync_pb::UserEventSpecifics>& events =
+ WebUIInfoSingleton::GetInstance()->pg_event_log();
+
+ base::ListValue events_sent;
+
+ for (const sync_pb::UserEventSpecifics& event : events)
+ events_sent.GetList().push_back(SerializePGEvent(event));
+
+ AllowJavascript();
+ std::string callback_id;
+ args->GetString(0, &callback_id);
+ ResolveJavascriptCallback(base::Value(callback_id), events_sent);
+}
- AllowJavascript();
- std::string callback_id;
- args->GetString(0, &callback_id);
- ResolveJavascriptCallback(base::Value(callback_id), sent_reports);
+void SafeBrowsingUIHandler::GetPGPings(const base::ListValue* args) {
+ const std::vector<LoginReputationClientRequest> requests =
+ WebUIInfoSingleton::GetInstance()->pg_pings();
+
+ base::ListValue pings_sent;
+ for (size_t request_index = 0; request_index < requests.size();
+ request_index++) {
+ base::ListValue ping_entry;
+ ping_entry.GetList().push_back(base::Value(int(request_index)));
+ ping_entry.GetList().push_back(
+ base::Value(SerializePGPing(requests[request_index])));
+ pings_sent.GetList().push_back(std::move(ping_entry));
}
+
+ AllowJavascript();
+ std::string callback_id;
+ args->GetString(0, &callback_id);
+ ResolveJavascriptCallback(base::Value(callback_id), pings_sent);
}
-void SafeBrowsingUIHandler::NotifyThreatDetailsJsListener(
- ClientSafeBrowsingReportRequest* threat_detail) {
+void SafeBrowsingUIHandler::GetPGResponses(const base::ListValue* args) {
+ const std::map<int, LoginReputationClientResponse> responses =
+ WebUIInfoSingleton::GetInstance()->pg_responses();
+
+ base::ListValue responses_sent;
+ for (const auto& token_and_response : responses) {
+ base::ListValue response_entry;
+ response_entry.GetList().push_back(base::Value(token_and_response.first));
+ response_entry.GetList().push_back(
+ base::Value(SerializePGResponse(token_and_response.second)));
+ responses_sent.GetList().push_back(std::move(response_entry));
+ }
+
AllowJavascript();
- FireWebUIListener("threat-details-update",
- base::Value(ParseThreatDetailsInfo(*threat_detail)));
+ std::string callback_id;
+ args->GetString(0, &callback_id);
+ ResolveJavascriptCallback(base::Value(callback_id), responses_sent);
+}
+
+void SafeBrowsingUIHandler::NotifyClientDownloadRequestJsListener(
+ ClientDownloadRequest* client_download_request) {
+ AllowJavascript();
+ FireWebUIListener(
+ "sent-client-download-requests-update",
+ base::Value(SerializeClientDownloadRequest(*client_download_request)));
+}
+
+void SafeBrowsingUIHandler::NotifyClientDownloadResponseJsListener(
+ ClientDownloadResponse* client_download_response) {
+ AllowJavascript();
+ FireWebUIListener(
+ "received-client-download-responses-update",
+ base::Value(SerializeClientDownloadResponse(*client_download_response)));
+}
+
+void SafeBrowsingUIHandler::NotifyCSBRRJsListener(
+ ClientSafeBrowsingReportRequest* csbrr) {
+ AllowJavascript();
+ FireWebUIListener("sent-csbrr-update", base::Value(SerializeCSBRR(*csbrr)));
+}
+
+void SafeBrowsingUIHandler::NotifyPGEventJsListener(
+ const sync_pb::UserEventSpecifics& event) {
+ AllowJavascript();
+ FireWebUIListener("sent-pg-event", SerializePGEvent(event));
+}
+
+void SafeBrowsingUIHandler::NotifyPGPingJsListener(
+ int token,
+ const LoginReputationClientRequest& request) {
+ base::ListValue request_list;
+ request_list.GetList().push_back(base::Value(token));
+ request_list.GetList().push_back(base::Value(SerializePGPing(request)));
+
+ AllowJavascript();
+ FireWebUIListener("pg-pings-update", request_list);
+}
+
+void SafeBrowsingUIHandler::NotifyPGResponseJsListener(
+ int token,
+ const LoginReputationClientResponse& response) {
+ base::ListValue response_list;
+ response_list.GetList().push_back(base::Value(token));
+ response_list.GetList().push_back(base::Value(SerializePGResponse(response)));
+
+ AllowJavascript();
+ FireWebUIListener("pg-responses-update", response_list);
}
void SafeBrowsingUIHandler::RegisterMessages() {
@@ -391,12 +1115,35 @@ void SafeBrowsingUIHandler::RegisterMessages() {
"getPrefs", base::BindRepeating(&SafeBrowsingUIHandler::GetPrefs,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
+ "getSavedPasswords",
+ base::BindRepeating(&SafeBrowsingUIHandler::GetSavedPasswords,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
"getDatabaseManagerInfo",
base::BindRepeating(&SafeBrowsingUIHandler::GetDatabaseManagerInfo,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
- "getSentThreatDetails",
- base::BindRepeating(&SafeBrowsingUIHandler::GetSentThreatDetails,
+ "getSentClientDownloadRequests",
+ base::BindRepeating(&SafeBrowsingUIHandler::GetSentClientDownloadRequests,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getReceivedClientDownloadResponses",
+ base::BindRepeating(
+ &SafeBrowsingUIHandler::GetReceivedClientDownloadResponses,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getSentCSBRRs",
+ base::BindRepeating(&SafeBrowsingUIHandler::GetSentCSBRRs,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getPGEvents", base::BindRepeating(&SafeBrowsingUIHandler::GetPGEvents,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getPGPings", base::BindRepeating(&SafeBrowsingUIHandler::GetPGPings,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getPGResponses",
+ base::BindRepeating(&SafeBrowsingUIHandler::GetPGResponses,
base::Unretained(this)));
}
diff --git a/chromium/components/safe_browsing/web_ui/safe_browsing_ui.h b/chromium/components/safe_browsing/web_ui/safe_browsing_ui.h
index 6fb01244c67..8b473dc73ed 100644
--- a/chromium/components/safe_browsing/web_ui/safe_browsing_ui.h
+++ b/chromium/components/safe_browsing/web_ui/safe_browsing_ui.h
@@ -8,6 +8,7 @@
#include "base/macros.h"
#include "components/safe_browsing/proto/csd.pb.h"
#include "components/safe_browsing/proto/webui.pb.h"
+#include "components/sync/protocol/user_event_specifics.pb.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_data_source.h"
@@ -17,12 +18,14 @@ namespace base {
class ListValue;
template <typename T>
struct DefaultSingletonTraits;
-}
+} // namespace base
namespace safe_browsing {
+class WebUIInfoSingleton;
+
class SafeBrowsingUIHandler : public content::WebUIMessageHandler {
public:
- SafeBrowsingUIHandler(content::BrowserContext*);
+ SafeBrowsingUIHandler(content::BrowserContext* context);
~SafeBrowsingUIHandler() override;
// Get the experiments that are currently enabled per Chrome instance.
@@ -31,24 +34,74 @@ class SafeBrowsingUIHandler : public content::WebUIMessageHandler {
// Get the Safe Browsing related preferences for the current user.
void GetPrefs(const base::ListValue* args);
+ // Get the current captured passwords.
+ void GetSavedPasswords(const base::ListValue* args);
+
// Get the information related to the Safe Browsing database and full hash
// cache.
void GetDatabaseManagerInfo(const base::ListValue* args);
+ // Get the ClientDownloadRequests that have been collected since the oldest
+ // currently open chrome://safe-browsing tab was opened.
+ void GetSentClientDownloadRequests(const base::ListValue* args);
+
+ // Get the ClientDownloadReponses that have been collected since the oldest
+ // currently open chrome://safe-browsing tab was opened.
+ void GetReceivedClientDownloadResponses(const base::ListValue* args);
+
// Get the ThreatDetails that have been collected since the oldest currently
// open chrome://safe-browsing tab was opened.
- void GetSentThreatDetails(const base::ListValue* args);
+ void GetSentCSBRRs(const base::ListValue* args);
- // Get the new ThreatDetails messages sent from ThreatDetails when a ping is
- // sent, while one or more WebUI tabs are opened.
- void NotifyThreatDetailsJsListener(
- ClientSafeBrowsingReportRequest* threat_detail);
+ // Get the PhishGuard events that have been collected since the oldest
+ // currently open chrome://safe-browsing tab was opened.
+ void GetPGEvents(const base::ListValue* args);
+
+ // Get the PhishGuard pings that have been sent since the oldest currently
+ // open chrome://safe-browsing tab was opened.
+ void GetPGPings(const base::ListValue* args);
+
+ // Get the PhishGuard responses that have been received since the oldest
+ // currently open chrome://safe-browsing tab was opened.
+ void GetPGResponses(const base::ListValue* args);
// Register callbacks for WebUI messages.
void RegisterMessages() override;
private:
+ friend class WebUIInfoSingleton;
+
+ // Called when any new ClientDownloadRequest messages are sent while one or
+ // more WebUI tabs are open.
+ void NotifyClientDownloadRequestJsListener(
+ ClientDownloadRequest* client_download_request);
+
+ // Called when any new ClientDownloadResponse messages are received while one
+ // or more WebUI tabs are open.
+ void NotifyClientDownloadResponseJsListener(
+ ClientDownloadResponse* client_download_response);
+
+ // Get the new ThreatDetails messages sent from ThreatDetails when a ping is
+ // sent, while one or more WebUI tabs are opened.
+ void NotifyCSBRRJsListener(ClientSafeBrowsingReportRequest* csbrr);
+
+ // Called when any new PhishGuard events are sent while one or more WebUI tabs
+ // are open.
+ void NotifyPGEventJsListener(const sync_pb::UserEventSpecifics& event);
+
+ // Called when any new PhishGuard pings are sent while one or more WebUI tabs
+ // are open.
+ void NotifyPGPingJsListener(int token,
+ const LoginReputationClientRequest& request);
+
+ // Called when any new PhishGuard responses are received while one or more
+ // WebUI tabs are open.
+ void NotifyPGResponseJsListener(
+ int token,
+ const LoginReputationClientResponse& response);
+
content::BrowserContext* browser_context_;
+
// List that keeps all the WebUI listener objects.
static std::vector<SafeBrowsingUIHandler*> webui_list_;
DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUIHandler);
@@ -68,13 +121,51 @@ class WebUIInfoSingleton {
public:
static WebUIInfoSingleton* GetInstance();
- // Delete the list of the sent ClientSafeBrowsingReportRequest messages.
- void ClearReportsSent();
+ // Returns true when there is a listening chrome://safe-browsing tab.
+ static bool HasListener();
+
+ // Add the new message in |client_download_requests_sent_| and send it to all
+ // the open chrome://safe-browsing tabs.
+ void AddToClientDownloadRequestsSent(
+ std::unique_ptr<ClientDownloadRequest> report_request);
+
+ // Clear the list of the sent ClientDownloadRequest messages.
+ void ClearClientDownloadRequestsSent();
+
+ // Add the new message in |client_download_responses_received_| and send it to
+ // all the open chrome://safe-browsing tabs.
+ void AddToClientDownloadResponsesReceived(
+ std::unique_ptr<ClientDownloadResponse> response);
- // Add the new message in reports_sent_ and send it to all the
- // chrome://safe-browsing opened tabs.
- void AddToReportsSent(
- std::unique_ptr<ClientSafeBrowsingReportRequest> report_request);
+ // Clear the list of the received ClientDownloadResponse messages.
+ void ClearClientDownloadResponsesReceived();
+
+ // 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);
+
+ // Clear the list of the sent ClientSafeBrowsingReportRequest messages.
+ void ClearCSBRRsSent();
+
+ // 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);
+
+ // Clear the list of sent PhishGuard events.
+ void ClearPGEvents();
+
+ // Add the new ping to |pg_pings_| and send it to all the open
+ // chrome://safe-browsing tabs. Returns a token that can be used in
+ // |AddToPGReponses| to correlate a ping and response.
+ int AddToPGPings(const LoginReputationClientRequest& request);
+
+ // Add the new response to |pg_responses_| and send it to all the open
+ // chrome://safe-browsing tabs.
+ void AddToPGResponses(int token,
+ const LoginReputationClientResponse& response);
+
+ // Clear the list of sent PhishGuard pings and responses.
+ void ClearPGPings();
// Register the new WebUI listener object.
void RegisterWebUIInstance(SafeBrowsingUIHandler* webui);
@@ -83,29 +174,87 @@ class WebUIInfoSingleton {
// this is last listener.
void UnregisterWebUIInstance(SafeBrowsingUIHandler* webui);
- // Get the list of the sent reports that have been collected since the oldest
- // currently open chrome://safe-browsing tab was opened.
+ // Get the list of the sent ClientDownloadRequests that have been collected
+ // since the oldest currently open chrome://safe-browsing tab was opened.
+ const std::vector<std::unique_ptr<ClientDownloadRequest>>&
+ client_download_requests_sent() const {
+ return client_download_requests_sent_;
+ }
+
+ // Get the list of the sent ClientDownloadResponses that have been collected
+ // since the oldest currently open chrome://safe-browsing tab was opened.
+ const std::vector<std::unique_ptr<ClientDownloadResponse>>&
+ client_download_responses_received() const {
+ return client_download_responses_received_;
+ }
+
+ // Get the list of the sent CSBRR reports that have been collected since the
+ // oldest currently open chrome://safe-browsing tab was opened.
const std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>>&
- reports_sent() const {
- return reports_sent_;
- ;
+ csbrrs_sent() const {
+ return csbrrs_sent_;
}
+
// Get the list of WebUI listener objects.
const std::vector<SafeBrowsingUIHandler*>& webui_instances() const {
return webui_instances_;
}
+ // Get the list of PhishGuard events since the oldest currently open
+ // chrome://safe-browsing tab was opened.
+ const std::vector<sync_pb::UserEventSpecifics>& pg_event_log() const {
+ return pg_event_log_;
+ }
+
+ // Get the list of PhishGuard pings since the oldest currently open
+ // chrome://safe-browsing tab was opened.
+ const std::vector<LoginReputationClientRequest>& pg_pings() const {
+ return pg_pings_;
+ }
+
+ // Get the list of PhishGuard pings since the oldest currently open
+ // chrome://safe-browsing tab was opened.
+ const std::map<int, LoginReputationClientResponse>& pg_responses() const {
+ return pg_responses_;
+ }
+
private:
WebUIInfoSingleton();
~WebUIInfoSingleton();
friend struct base::DefaultSingletonTraits<WebUIInfoSingleton>;
- // List of reports sent since since the oldest currently
- // open chrome://safe-browsing tab was opened.
+ // List of ClientDownloadRequests sent since since the oldest currently open
+ // chrome://safe-browsing tab was opened.
+ // "ClientDownloadRequests" cannot be const, due to being used by functions
+ // that call AllowJavascript(), which is not marked const.
+ std::vector<std::unique_ptr<ClientDownloadRequest>>
+ client_download_requests_sent_;
+
+ // List of ClientDownloadResponses received since since the oldest currently
+ // open chrome://safe-browsing tab was opened. "ClientDownloadReponse" cannot
+ // be const, due to being used by functions that call AllowJavascript(), which
+ // is not marked const.
+ std::vector<std::unique_ptr<ClientDownloadResponse>>
+ client_download_responses_received_;
+
+ // List of CSBRRs sent since since the oldest currently open
+ // chrome://safe-browsing tab was opened.
// "ClientSafeBrowsingReportRequest" cannot be const, due to being used by
// functions that call AllowJavascript(), which is not marked const.
- std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>> reports_sent_;
+ std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>> csbrrs_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_;
+
+ // List of PhishGuard pings sent since the oldest currently open
+ // chrome://safe-browsing tab was opened.
+ std::vector<LoginReputationClientRequest> pg_pings_;
+
+ // List of PhishGuard responses received since the oldest currently open
+ // chrome://safe-browsing tab was opened.
+ std::map<int, LoginReputationClientResponse> pg_responses_;
// List of WebUI listener objects. "SafeBrowsingUIHandler*" cannot be const,
// due to being used by functions that call AllowJavascript(), which is not
diff --git a/chromium/components/safe_search_api/BUILD.gn b/chromium/components/safe_search_api/BUILD.gn
new file mode 100644
index 00000000000..086b2ea5b2c
--- /dev/null
+++ b/chromium/components/safe_search_api/BUILD.gn
@@ -0,0 +1,38 @@
+# 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.
+
+source_set("safe_search_api") {
+ sources = [
+ "url_checker.cc",
+ "url_checker.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/google/core/browser",
+ "//google_apis",
+ "//net",
+ "//services/network/public/cpp",
+ "//url",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "url_checker_unittest.cc",
+ ]
+ deps = [
+ ":safe_search_api",
+ "//base",
+ "//base/test:test_support",
+ "//net",
+ "//net/traffic_annotation:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//url",
+ ]
+}
diff --git a/chromium/components/safe_search_api/DEPS b/chromium/components/safe_search_api/DEPS
new file mode 100644
index 00000000000..83e61e186c8
--- /dev/null
+++ b/chromium/components/safe_search_api/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+components/google/core/browser",
+ "+google_apis",
+ "+net",
+ "+services/network",
+]
diff --git a/chromium/components/safe_search_api/OWNERS b/chromium/components/safe_search_api/OWNERS
new file mode 100644
index 00000000000..f0b684fb2d5
--- /dev/null
+++ b/chromium/components/safe_search_api/OWNERS
@@ -0,0 +1,2 @@
+bauerb@chromium.org
+treib@chromium.org
diff --git a/chromium/components/safe_search_api/url_checker.cc b/chromium/components/safe_search_api/url_checker.cc
new file mode 100644
index 00000000000..6a68d13921a
--- /dev/null
+++ b/chromium/components/safe_search_api/url_checker.cc
@@ -0,0 +1,239 @@
+// 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/safe_search_api/url_checker.h"
+
+#include <string>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/feature_list.h"
+#include "base/json/json_reader.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.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/browser/google_util.h"
+#include "google_apis/google_api_keys.h"
+#include "net/base/escape.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_request_status.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/url_constants.h"
+
+namespace safe_search_api {
+
+namespace {
+
+const char kSafeSearchApiUrl[] =
+ "https://safesearch.googleapis.com/v1:classify";
+const char kDataContentType[] = "application/x-www-form-urlencoded";
+const char kDataFormat[] = "key=%s&urls=%s&region_code=%s";
+
+const size_t kDefaultCacheSize = 1000;
+const size_t kDefaultCacheTimeoutSeconds = 3600;
+
+// Builds the POST data for SafeSearch API requests.
+std::string BuildRequestData(const std::string& api_key,
+ const GURL& url,
+ const std::string& region_code) {
+ std::string query = net::EscapeQueryParamValue(url.spec(), true);
+ return base::StringPrintf(kDataFormat, api_key.c_str(), query.c_str(),
+ region_code.c_str());
+}
+
+// Parses a SafeSearch API |response| and stores the result in |is_porn|.
+// On errors, returns false and doesn't set |is_porn|.
+bool ParseResponse(const std::string& response, bool* is_porn) {
+ std::unique_ptr<base::Value> value = base::JSONReader::Read(response);
+ const base::DictionaryValue* dict = nullptr;
+ if (!value || !value->GetAsDictionary(&dict)) {
+ DLOG(WARNING) << "ParseResponse failed to parse global dictionary";
+ return false;
+ }
+ const base::ListValue* classifications_list = nullptr;
+ if (!dict->GetList("classifications", &classifications_list)) {
+ DLOG(WARNING) << "ParseResponse failed to parse classifications list";
+ return false;
+ }
+ if (classifications_list->GetSize() != 1) {
+ DLOG(WARNING) << "ParseResponse expected exactly one result";
+ return false;
+ }
+ const base::DictionaryValue* classification_dict = nullptr;
+ if (!classifications_list->GetDictionary(0, &classification_dict)) {
+ DLOG(WARNING) << "ParseResponse failed to parse classification dict";
+ return false;
+ }
+ classification_dict->GetBoolean("pornography", is_porn);
+ return true;
+}
+
+} // namespace
+
+// Consider all URLs within a google domain to be safe.
+const base::Feature kAllowAllGoogleUrls{"SafeSearchAllowAllGoogleURLs",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+struct URLChecker::Check {
+ Check(const GURL& url,
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader,
+ CheckCallback callback);
+ ~Check();
+
+ GURL url;
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader;
+ std::vector<CheckCallback> callbacks;
+ base::TimeTicks start_time;
+};
+
+URLChecker::Check::Check(
+ const GURL& url,
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader,
+ CheckCallback callback)
+ : url(url),
+ simple_url_loader(std::move(simple_url_loader)),
+ start_time(base::TimeTicks::Now()) {
+ callbacks.push_back(std::move(callback));
+}
+
+URLChecker::Check::~Check() {
+ for (const CheckCallback& callback : callbacks) {
+ DCHECK(!callback);
+ }
+}
+
+URLChecker::CheckResult::CheckResult(Classification classification,
+ bool uncertain)
+ : classification(classification),
+ uncertain(uncertain),
+ timestamp(base::TimeTicks::Now()) {}
+
+URLChecker::URLChecker(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ const net::NetworkTrafficAnnotationTag& traffic_annotation,
+ const std::string& country)
+ : URLChecker(std::move(url_loader_factory),
+ traffic_annotation,
+ country,
+ kDefaultCacheSize) {}
+
+URLChecker::URLChecker(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ const net::NetworkTrafficAnnotationTag& traffic_annotation,
+ const std::string& country,
+ size_t cache_size)
+ : url_loader_factory_(std::move(url_loader_factory)),
+ traffic_annotation_(traffic_annotation),
+ country_(country),
+ cache_(cache_size),
+ cache_timeout_(
+ base::TimeDelta::FromSeconds(kDefaultCacheTimeoutSeconds)) {}
+
+URLChecker::~URLChecker() = default;
+
+bool URLChecker::CheckURL(const GURL& url, CheckCallback callback) {
+ if (base::FeatureList::IsEnabled(kAllowAllGoogleUrls)) {
+ // TODO(treib): Hack: For now, allow all Google URLs to save QPS.
+ if (google_util::IsGoogleDomainUrl(url, google_util::ALLOW_SUBDOMAIN,
+ google_util::ALLOW_NON_STANDARD_PORTS)) {
+ std::move(callback).Run(url, Classification::SAFE, false);
+ return true;
+ }
+ // TODO(treib): Hack: For now, allow all YouTube URLs since YouTube has its
+ // own Safety Mode anyway.
+ if (google_util::IsYoutubeDomainUrl(
+ url, google_util::ALLOW_SUBDOMAIN,
+ google_util::ALLOW_NON_STANDARD_PORTS)) {
+ std::move(callback).Run(url, Classification::SAFE, false);
+ return true;
+ }
+ }
+
+ auto cache_it = cache_.Get(url);
+ if (cache_it != cache_.end()) {
+ const CheckResult& result = cache_it->second;
+ base::TimeDelta age = base::TimeTicks::Now() - result.timestamp;
+ if (age < cache_timeout_) {
+ DVLOG(1) << "Cache hit! " << url.spec() << " is "
+ << (result.classification == Classification::UNSAFE ? "NOT" : "")
+ << " safe; certain: " << !result.uncertain;
+ std::move(callback).Run(url, result.classification, result.uncertain);
+ return true;
+ }
+ DVLOG(1) << "Outdated cache entry for " << url.spec() << ", purging";
+ cache_.Erase(cache_it);
+ }
+
+ // See if we already have a check in progress for this URL.
+ for (const auto& check : checks_in_progress_) {
+ if (check->url == url) {
+ DVLOG(1) << "Adding to pending check for " << url.spec();
+ check->callbacks.push_back(std::move(callback));
+ return false;
+ }
+ }
+
+ DVLOG(1) << "Checking URL " << url;
+ std::string api_key = google_apis::GetAPIKey();
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = GURL(kSafeSearchApiUrl);
+ resource_request->method = "POST";
+ resource_request->load_flags =
+ net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader =
+ network::SimpleURLLoader::Create(std::move(resource_request),
+ traffic_annotation_);
+ simple_url_loader->AttachStringForUpload(
+ BuildRequestData(api_key, url, country_), kDataContentType);
+ auto it = checks_in_progress_.insert(
+ checks_in_progress_.begin(),
+ std::make_unique<Check>(url, std::move(simple_url_loader),
+ std::move(callback)));
+ network::SimpleURLLoader* loader = it->get()->simple_url_loader.get();
+ loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&URLChecker::OnSimpleLoaderComplete,
+ base::Unretained(this), std::move(it)));
+ return false;
+}
+
+void URLChecker::OnSimpleLoaderComplete(
+ CheckList::iterator it,
+ std::unique_ptr<std::string> response_body) {
+ Check* check = it->get();
+
+ GURL url = check->url;
+ std::vector<CheckCallback> callbacks = std::move(check->callbacks);
+ base::TimeTicks start_time = check->start_time;
+ checks_in_progress_.erase(it);
+
+ if (!response_body) {
+ DLOG(WARNING) << "URL request failed! Letting through...";
+ for (size_t i = 0; i < callbacks.size(); i++)
+ std::move(callbacks[i]).Run(url, Classification::SAFE, true);
+ return;
+ }
+
+ bool is_porn = false;
+ bool uncertain = !ParseResponse(*response_body, &is_porn);
+ Classification classification =
+ is_porn ? Classification::UNSAFE : Classification::SAFE;
+
+ // TODO(msramek): Consider moving this to SupervisedUserResourceThrottle.
+ UMA_HISTOGRAM_TIMES("ManagedUsers.SafeSitesDelay",
+ base::TimeTicks::Now() - start_time);
+
+ cache_.Put(url, CheckResult(classification, uncertain));
+
+ for (size_t i = 0; i < callbacks.size(); i++)
+ std::move(callbacks[i]).Run(url, classification, uncertain);
+}
+
+} // namespace safe_search_api
diff --git a/chromium/components/safe_search_api/url_checker.h b/chromium/components/safe_search_api/url_checker.h
new file mode 100644
index 00000000000..6a950dda9c4
--- /dev/null
+++ b/chromium/components/safe_search_api/url_checker.h
@@ -0,0 +1,91 @@
+// 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_SAFE_SEARCH_API_URL_CHECKER_H_
+#define COMPONENTS_SAFE_SEARCH_API_URL_CHECKER_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/containers/mru_cache.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/time/time.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "url/gurl.h"
+
+namespace network {
+class SharedURLLoaderFactory;
+} // namespace network
+
+namespace base {
+struct Feature;
+}
+
+namespace safe_search_api {
+
+// The SafeSearch API classification of a URL.
+enum class Classification { SAFE, UNSAFE };
+
+// Visible for testing.
+extern const base::Feature kAllowAllGoogleUrls;
+
+// This class uses the SafeSearch API to check the SafeSearch classification
+// of the content on a given URL and returns the result asynchronously
+// via a callback.
+class URLChecker {
+ public:
+ // Returns whether |url| should be blocked. Called from CheckURL.
+ using CheckCallback = base::OnceCallback<
+ void(const GURL&, Classification classification, bool /* uncertain */)>;
+
+ // |country| should be a two-letter country code (ISO 3166-1 alpha-2), e.g.,
+ // "us".
+ URLChecker(scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ const net::NetworkTrafficAnnotationTag& traffic_annotation,
+ const std::string& country);
+ URLChecker(scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ const net::NetworkTrafficAnnotationTag& traffic_annotation,
+ const std::string& country,
+ size_t cache_size);
+ ~URLChecker();
+
+ // Returns whether |callback| was run synchronously.
+ bool CheckURL(const GURL& url, CheckCallback callback);
+
+ void SetCacheTimeoutForTesting(const base::TimeDelta& timeout) {
+ cache_timeout_ = timeout;
+ }
+
+ private:
+ struct Check;
+ struct CheckResult {
+ CheckResult(Classification classification, bool uncertain);
+ Classification classification;
+ bool uncertain;
+ base::TimeTicks timestamp;
+ };
+ using CheckList = std::list<std::unique_ptr<Check>>;
+
+ void OnSimpleLoaderComplete(CheckList::iterator it,
+ std::unique_ptr<std::string> response_body);
+
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+ const net::NetworkTrafficAnnotationTag traffic_annotation_;
+ const std::string country_;
+
+ CheckList checks_in_progress_;
+
+ base::MRUCache<GURL, CheckResult> cache_;
+ base::TimeDelta cache_timeout_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLChecker);
+};
+
+} // namespace safe_search_api
+
+#endif // COMPONENTS_SAFE_SEARCH_API_URL_CHECKER_H_
diff --git a/chromium/components/safe_search_api/url_checker_unittest.cc b/chromium/components/safe_search_api/url_checker_unittest.cc
new file mode 100644
index 00000000000..66ceb44e2e2
--- /dev/null
+++ b/chromium/components/safe_search_api/url_checker_unittest.cc
@@ -0,0 +1,244 @@
+// 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/safe_search_api/url_checker.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/json/json_writer.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/values.h"
+#include "net/base/net_errors.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.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_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using testing::_;
+
+namespace safe_search_api {
+
+namespace {
+
+const size_t kCacheSize = 2;
+
+const char* kURLs[] = {
+ "http://www.randomsite1.com", "http://www.randomsite2.com",
+ "http://www.randomsite3.com", "http://www.randomsite4.com",
+ "http://www.randomsite5.com", "http://www.randomsite6.com",
+ "http://www.randomsite7.com", "http://www.randomsite8.com",
+ "http://www.randomsite9.com",
+};
+
+const char kSafeSearchApiUrl[] =
+ "https://safesearch.googleapis.com/v1:classify";
+
+std::string BuildResponse(bool is_porn) {
+ base::DictionaryValue dict;
+ auto classification_dict = std::make_unique<base::DictionaryValue>();
+ if (is_porn)
+ classification_dict->SetBoolean("pornography", is_porn);
+ auto classifications_list = std::make_unique<base::ListValue>();
+ classifications_list->Append(std::move(classification_dict));
+ dict.SetWithoutPathExpansion("classifications",
+ std::move(classifications_list));
+ std::string result;
+ base::JSONWriter::Write(dict, &result);
+ return result;
+}
+
+std::string BuildPornResponse() {
+ return BuildResponse(true);
+}
+
+} // namespace
+
+class SafeSearchURLCheckerTest : public testing::Test {
+ public:
+ SafeSearchURLCheckerTest()
+ : next_url_(0),
+ test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)),
+ checker_(test_shared_loader_factory_,
+ TRAFFIC_ANNOTATION_FOR_TESTS,
+ "us",
+ kCacheSize) {}
+
+ MOCK_METHOD3(OnCheckDone,
+ void(const GURL& url,
+ Classification classification,
+ bool uncertain));
+
+ protected:
+ GURL GetNewURL() {
+ CHECK(next_url_ < base::size(kURLs));
+ return GURL(kURLs[next_url_++]);
+ }
+
+ void SetupResponse(const GURL& url,
+ net::Error error,
+ const std::string& response) {
+ network::URLLoaderCompletionStatus status(error);
+ status.decoded_body_length = response.size();
+ test_url_loader_factory_.AddResponse(GURL(kSafeSearchApiUrl),
+ network::ResourceResponseHead(),
+ response, status);
+ }
+
+ // Returns true if the result was returned synchronously (cache hit).
+ bool CheckURL(const GURL& url) {
+ bool cached = checker_.CheckURL(
+ url, base::BindOnce(&SafeSearchURLCheckerTest::OnCheckDone,
+ base::Unretained(this)));
+ return cached;
+ }
+
+ void WaitForResponse() { base::RunLoop().RunUntilIdle(); }
+
+ bool SendValidResponse(const GURL& url, bool is_porn) {
+ SetupResponse(url, net::OK, BuildResponse(is_porn));
+ bool result = CheckURL(url);
+ WaitForResponse();
+ return result;
+ }
+
+ bool SendFailedResponse(const GURL& url) {
+ SetupResponse(url, net::ERR_ABORTED, std::string());
+ bool result = CheckURL(url);
+ WaitForResponse();
+ return result;
+ }
+
+ size_t next_url_;
+ base::MessageLoop message_loop_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+ URLChecker checker_;
+};
+
+TEST_F(SafeSearchURLCheckerTest, Simple) {
+ {
+ GURL url(GetNewURL());
+ EXPECT_CALL(*this, OnCheckDone(url, Classification::SAFE, false));
+ ASSERT_FALSE(SendValidResponse(url, false));
+ }
+ {
+ GURL url(GetNewURL());
+ EXPECT_CALL(*this, OnCheckDone(url, Classification::UNSAFE, false));
+ ASSERT_FALSE(SendValidResponse(url, true));
+ }
+ {
+ GURL url(GetNewURL());
+ EXPECT_CALL(*this, OnCheckDone(url, Classification::SAFE, true));
+ ASSERT_FALSE(SendFailedResponse(url));
+ }
+}
+
+TEST_F(SafeSearchURLCheckerTest, Cache) {
+ // One more URL than fit in the cache.
+ ASSERT_EQ(2u, kCacheSize);
+ GURL url1(GetNewURL());
+ GURL url2(GetNewURL());
+ GURL url3(GetNewURL());
+
+ // Populate the cache.
+ EXPECT_CALL(*this, OnCheckDone(url1, Classification::SAFE, false));
+ ASSERT_FALSE(SendValidResponse(url1, false));
+ EXPECT_CALL(*this, OnCheckDone(url2, Classification::SAFE, false));
+ ASSERT_FALSE(SendValidResponse(url2, false));
+
+ // Now we should get results synchronously, without a network request.
+ test_url_loader_factory_.ClearResponses();
+ EXPECT_CALL(*this, OnCheckDone(url2, Classification::SAFE, false));
+ ASSERT_TRUE(CheckURL(url2));
+ EXPECT_CALL(*this, OnCheckDone(url1, Classification::SAFE, false));
+ ASSERT_TRUE(CheckURL(url1));
+
+ // Now |url2| is the LRU and should be evicted on the next check.
+ EXPECT_CALL(*this, OnCheckDone(url3, Classification::SAFE, false));
+ ASSERT_FALSE(SendValidResponse(url3, false));
+
+ EXPECT_CALL(*this, OnCheckDone(url2, Classification::SAFE, false));
+ ASSERT_FALSE(SendValidResponse(url2, false));
+}
+
+TEST_F(SafeSearchURLCheckerTest, CoalesceRequestsToSameURL) {
+ GURL url(GetNewURL());
+ // Start two checks for the same URL.
+ SetupResponse(url, net::OK, BuildResponse(false));
+ ASSERT_FALSE(CheckURL(url));
+ ASSERT_FALSE(CheckURL(url));
+ // A single response should answer both of those checks
+ EXPECT_CALL(*this, OnCheckDone(url, Classification::SAFE, false)).Times(2);
+ WaitForResponse();
+}
+
+TEST_F(SafeSearchURLCheckerTest, CacheTimeout) {
+ GURL url(GetNewURL());
+
+ checker_.SetCacheTimeoutForTesting(base::TimeDelta::FromSeconds(0));
+
+ EXPECT_CALL(*this, OnCheckDone(url, Classification::SAFE, false));
+ ASSERT_FALSE(SendValidResponse(url, false));
+
+ // Since the cache timeout is zero, the cache entry should be invalidated
+ // immediately.
+ EXPECT_CALL(*this, OnCheckDone(url, Classification::UNSAFE, false));
+ ASSERT_FALSE(SendValidResponse(url, true));
+}
+
+TEST_F(SafeSearchURLCheckerTest, AllowAllGoogleURLs) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(kAllowAllGoogleUrls);
+ {
+ GURL url("https://sites.google.com/porn");
+ EXPECT_CALL(*this, OnCheckDone(url, Classification::SAFE, _));
+ // No server interaction.
+ bool cache_hit = CheckURL(url);
+ ASSERT_TRUE(cache_hit);
+ }
+ {
+ GURL url("https://youtube.com/porn");
+ EXPECT_CALL(*this, OnCheckDone(url, Classification::SAFE, _));
+ // No server interaction
+ bool cache_hit = CheckURL(url);
+ ASSERT_TRUE(cache_hit);
+ }
+}
+
+TEST_F(SafeSearchURLCheckerTest, NoAllowAllGoogleURLs) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndDisableFeature(kAllowAllGoogleUrls);
+ {
+ GURL url("https://sites.google.com/porn");
+ EXPECT_CALL(*this, OnCheckDone(url, Classification::UNSAFE, _));
+ SetupResponse(url, net::OK, BuildPornResponse());
+ bool cache_hit = CheckURL(url);
+ ASSERT_FALSE(cache_hit);
+ WaitForResponse();
+ }
+ {
+ GURL url("https://youtube.com/porn");
+ EXPECT_CALL(*this, OnCheckDone(url, Classification::UNSAFE, _));
+ SetupResponse(url, net::OK, BuildPornResponse());
+ bool cache_hit = CheckURL(url);
+ ASSERT_FALSE(cache_hit);
+ WaitForResponse();
+ }
+}
+
+} // namespace safe_search_api
diff --git a/chromium/components/search/OWNERS b/chromium/components/search/OWNERS
index bad91dbce35..b15621a59a3 100644
--- a/chromium/components/search/OWNERS
+++ b/chromium/components/search/OWNERS
@@ -1,3 +1,4 @@
+mathp@chromium.org
treib@chromium.org
sfiera@chromium.org
diff --git a/chromium/components/search_engines/BUILD.gn b/chromium/components/search_engines/BUILD.gn
index 90e00b4b68a..b5c89d4f919 100644
--- a/chromium/components/search_engines/BUILD.gn
+++ b/chromium/components/search_engines/BUILD.gn
@@ -77,6 +77,7 @@ static_library("search_engines") {
"//components/url_formatter",
"//google_apis",
"//net",
+ "//services/network/public/cpp",
"//sql",
"//third_party/libxml",
"//ui/base",
@@ -140,6 +141,8 @@ source_set("unit_tests") {
"//components/sync_preferences:test_support",
"//components/webdata/common",
"//net:net",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//sql",
"//testing/gmock",
"//testing/gtest",
diff --git a/chromium/components/search_engines/DEPS b/chromium/components/search_engines/DEPS
index a5e5859cae6..5f550b12827 100644
--- a/chromium/components/search_engines/DEPS
+++ b/chromium/components/search_engines/DEPS
@@ -17,6 +17,8 @@ include_rules = [
"+google_apis",
"+libxml",
"+net",
+ "+services/network/public/cpp",
+ "+services/network/test",
"+sql",
"+ui/base",
"+ui/gfx",
diff --git a/chromium/components/search_engines/prepopulated_engines.json b/chromium/components/search_engines/prepopulated_engines.json
index 4571a4ca4f3..535692d44a7 100644
--- a/chromium/components/search_engines/prepopulated_engines.json
+++ b/chromium/components/search_engines/prepopulated_engines.json
@@ -32,7 +32,7 @@
// Increment this if you change the data in ways that mean users with
// existing data should get a new version.
- "kCurrentDataVersion": 102
+ "kCurrentDataVersion": 103
},
// The following engines are included in country lists and are added to the
@@ -86,6 +86,11 @@
"keyword": "baidu.com",
"favicon_url": "https://www.baidu.com/favicon.ico",
"search_url": "https://www.baidu.com/#ie={inputEncoding}&wd={searchTerms}",
+ "alternate_urls": [
+ "https://www.baidu.com/s?ie={inputEncoding}&wd={searchTerms}",
+ "https://www.baidu.com/s?ie={inputEncoding}&word={searchTerms}",
+ "https://www.baidu.com/{google:pathWildcard}/s?ie={inputEncoding}&word={searchTerms}"
+ ],
"suggest_url": "http://suggestion.baidu.com/su?wd={searchTerms}&action=opensearch&ie={inputEncoding}",
"type": "SEARCH_ENGINE_BAIDU",
"id": 21
@@ -149,6 +154,9 @@
"keyword": "mail.ru",
"favicon_url": "https://go.imgsmail.ru/favicon.ico",
"search_url": "https://go.mail.ru/search?q={searchTerms}&{mailru:referralID}",
+ "alternate_urls": [
+ "https://go.mail.ru/msearch?q={searchTerms}&{mailru:referralID}"
+ ],
"encoding": "windows-1251",
"suggest_url": "https://suggests.go.mail.ru/chrome?q={searchTerms}",
"type": "SEARCH_ENGINE_MAILRU",
@@ -198,6 +206,10 @@
"keyword": "so.com",
"favicon_url": "https://www.so.com/favicon.ico",
"search_url": "https://www.so.com/s?ie={inputEncoding}&q={searchTerms}",
+ "alternate_urls": [
+ "https://m.so.com/s?ie={inputEncoding}&q={searchTerms}",
+ "https://m.so.com/index.php?ie={inputEncoding}&q={searchTerms}"
+ ],
"suggest_url": "https://sug.so.360.cn/suggest?encodein={inputEncoding}&encodeout={outputEncoding}&format=opensearch&word={searchTerms}",
"type": "SEARCH_ENGINE_360",
"id": 88
@@ -208,6 +220,9 @@
"keyword": "sogou.com",
"favicon_url": "https://www.sogou.com/images/logo/old/favicon.ico",
"search_url": "https://www.sogou.com/web?ie={inputEncoding}&query={searchTerms}",
+ "alternate_urls": [
+ "https://m.sogou.com/web/{google:pathWildcard}?ie={inputEncoding}&keyword={searchTerms}"
+ ],
"suggest_url": "http://api.sugg.sogou.com/su?type=addrbar&key={searchTerms}&ie={inputEncoding}",
"type": "SEARCH_ENGINE_SOGOU",
"id": 56
@@ -236,7 +251,7 @@
"name": "Yahoo!",
"keyword": "yahoo.com",
"favicon_url": "https://search.yahoo.com/favicon.ico",
- "search_url": "https://search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -246,7 +261,7 @@
"name": "Yahoo! Argentina",
"keyword": "ar.yahoo.com",
"favicon_url": "https://ar.search.yahoo.com/favicon.ico",
- "search_url": "https://ar.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://ar.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://ar.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -256,7 +271,7 @@
"name": "Yahoo! \u00d6sterreich",
"keyword": "at.yahoo.com",
"favicon_url": "https://at.search.yahoo.com/favicon.ico",
- "search_url": "https://at.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://at.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://at.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -266,7 +281,7 @@
"name": "Yahoo!7",
"keyword": "au.yahoo.com",
"favicon_url": "https://au.search.yahoo.com/favicon.ico",
- "search_url": "https://au.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://au.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://au.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -276,7 +291,7 @@
"name": "Yahoo! Brasil",
"keyword": "br.yahoo.com",
"favicon_url": "https://br.search.yahoo.com/favicon.ico",
- "search_url": "https://br.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://br.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://br.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -286,7 +301,7 @@
"name": "Yahoo! Canada",
"keyword": "ca.yahoo.com",
"favicon_url": "https://ca.search.yahoo.com/favicon.ico",
- "search_url": "https://ca.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://ca.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://ca.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -296,7 +311,7 @@
"name": "Yahoo! Schweiz",
"keyword": "ch.yahoo.com",
"favicon_url": "https://ch.search.yahoo.com/favicon.ico",
- "search_url": "https://ch.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://ch.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://ch.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -306,7 +321,7 @@
"name": "Yahoo! Chile",
"keyword": "cl.yahoo.com",
"favicon_url": "https://cl.search.yahoo.com/favicon.ico",
- "search_url": "https://cl.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://cl.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://cl.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -316,7 +331,7 @@
"name": "Yahoo! Colombia",
"keyword": "co.yahoo.com",
"favicon_url": "https://co.search.yahoo.com/favicon.ico",
- "search_url": "https://co.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://co.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://co.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -326,7 +341,7 @@
"name": "Yahoo! Deutschland",
"keyword": "de.yahoo.com",
"favicon_url": "https://de.search.yahoo.com/favicon.ico",
- "search_url": "https://de.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://de.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://de.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -339,7 +354,7 @@
"name": "Yahoo! Danmark",
"keyword": "dk.yahoo.com",
"favicon_url": "https://dk.search.yahoo.com/favicon.ico",
- "search_url": "https://dk.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://dk.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
},
@@ -348,7 +363,7 @@
"name": "Yahoo! Espa\u00f1a",
"keyword": "es.yahoo.com",
"favicon_url": "https://es.search.yahoo.com/favicon.ico",
- "search_url": "https://es.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://es.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://es.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -358,7 +373,7 @@
"name": "Yahoo! Suomi",
"keyword": "fi.yahoo.com",
"favicon_url": "https://fi.search.yahoo.com/favicon.ico",
- "search_url": "https://fi.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://fi.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
},
@@ -367,7 +382,7 @@
"name": "Yahoo! France",
"keyword": "fr.yahoo.com",
"favicon_url": "https://fr.search.yahoo.com/favicon.ico",
- "search_url": "https://fr.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://fr.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://fr.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 5 // Can't be 2 as this has to appear in the Belgium list alongside
@@ -378,7 +393,7 @@
"name": "Yahoo! \u0395\u03bb\u03bb\u03ac\u03b4\u03b1\u03c2",
"keyword": "gr.yahoo.com",
"favicon_url": "https://gr.search.yahoo.com/favicon.ico",
- "search_url": "https://gr.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://gr.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
},
@@ -387,7 +402,7 @@
"name": "Yahoo! Hong Kong",
"keyword": "hk.yahoo.com",
"favicon_url": "https://hk.search.yahoo.com/favicon.ico",
- "search_url": "https://hk.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://hk.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://hk.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -397,7 +412,7 @@
"name": "Yahoo! Indonesia",
"keyword": "id.yahoo.com",
"favicon_url": "https://id.search.yahoo.com/favicon.ico",
- "search_url": "https://id.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://id.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://id.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -407,7 +422,7 @@
"name": "Yahoo! India",
"keyword": "in.yahoo.com",
"favicon_url": "https://in.search.yahoo.com/favicon.ico",
- "search_url": "https://in.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://in.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://in.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -417,7 +432,7 @@
"name": "Yahoo! JAPAN",
"keyword": "yahoo.co.jp",
"favicon_url": "https://search.yahoo.co.jp/favicon.ico",
- "search_url": "https://search.yahoo.co.jp/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://search.yahoo.co.jp/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://search.yahooapis.jp/AssistSearchService/V2/webassistSearch?p={searchTerms}&appid=oQsoxcyxg66enp0TYoirkKoryq6rF8bK76mW0KYxZ0v0WPLtn.Lix6wy8F_LwGWHUII-&output=fxjson&fr=crmas",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -427,7 +442,7 @@
"name": "Yahoo!\u200e \u0645\u0643\u062a\u0648\u0628",
"keyword": "maktoob.yahoo.com",
"favicon_url": "https://maktoob.search.yahoo.com/favicon.ico",
- "search_url": "https://maktoob.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://maktoob.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
},
@@ -436,7 +451,7 @@
"name": "Yahoo! M\u00e9xico",
"keyword": "mx.yahoo.com",
"favicon_url": "https://mx.search.yahoo.com/favicon.ico",
- "search_url": "https://mx.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://mx.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://mx.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -446,7 +461,7 @@
"name": "Yahoo! Malaysia",
"keyword": "malaysia.yahoo.com",
"favicon_url": "https://malaysia.search.yahoo.com/favicon.ico",
- "search_url": "https://malaysia.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://malaysia.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://malaysia.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -456,7 +471,7 @@
"name": "Yahoo! Nederland",
"keyword": "nl.yahoo.com",
"favicon_url": "https://nl.search.yahoo.com/favicon.ico",
- "search_url": "https://nl.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://nl.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://nl.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -466,7 +481,7 @@
"name": "Yahoo! New Zealand",
"keyword": "nz.yahoo.com",
"favicon_url": "https://nz.search.yahoo.com/favicon.ico",
- "search_url": "https://nz.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://nz.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://nz.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -476,7 +491,7 @@
"name": "Yahoo! Per\u00fa",
"keyword": "pe.yahoo.com",
"favicon_url": "https://pe.search.yahoo.com/favicon.ico",
- "search_url": "https://pe.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://pe.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://pe.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -486,7 +501,7 @@
"name": "Yahoo! Philippines",
"keyword": "ph.yahoo.com",
"favicon_url": "https://ph.search.yahoo.com/favicon.ico",
- "search_url": "https://ph.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://ph.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://ph.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -496,7 +511,7 @@
"name": "Yahoo! Qu\u00e9bec",
"keyword": "qc.yahoo.com",
"favicon_url": "https://qc.search.yahoo.com/favicon.ico",
- "search_url": "https://qc.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://qc.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://qc.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 5 // Can't be 2 as this has to appear in the Canada list alongside
@@ -507,7 +522,7 @@
"name": "Yahoo! Rom\u00e2nia",
"keyword": "ro.yahoo.com",
"favicon_url": "https://ro.search.yahoo.com/favicon.ico",
- "search_url": "https://ro.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://ro.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
},
@@ -516,7 +531,7 @@
"name": "Yahoo! Sverige",
"keyword": "se.yahoo.com",
"favicon_url": "https://se.search.yahoo.com/favicon.ico",
- "search_url": "https://se.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://se.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
},
@@ -525,7 +540,7 @@
"name": "Yahoo! Singapore",
"keyword": "sg.yahoo.com",
"favicon_url": "https://sg.search.yahoo.com/favicon.ico",
- "search_url": "https://sg.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://sg.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://sg.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -535,7 +550,7 @@
"name": "Yahoo! \u0e1b\u0e23\u0e30\u0e40\u0e17\u0e28\u0e44\u0e17\u0e22",
"keyword": "th.yahoo.com",
"favicon_url": "https://th.search.yahoo.com/favicon.ico",
- "search_url": "https://th.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://th.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://th.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -545,7 +560,7 @@
"name": "Yahoo! T\u00fcrkiye",
"keyword": "tr.yahoo.com",
"favicon_url": "https://tr.search.yahoo.com/favicon.ico",
- "search_url": "https://tr.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://tr.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
},
@@ -554,7 +569,7 @@
"name": "Yahoo!\u5947\u6469",
"keyword": "tw.yahoo.com",
"favicon_url": "https://tw.search.yahoo.com/favicon.ico",
- "search_url": "https://tw.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://tw.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://tw.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -564,7 +579,7 @@
"name": "Yahoo! UK & Ireland",
"keyword": "uk.yahoo.com",
"favicon_url": "https://uk.search.yahoo.com/favicon.ico",
- "search_url": "https://uk.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://uk.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://uk.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -574,7 +589,7 @@
"name": "Yahoo! Venezuela",
"keyword": "ve.yahoo.com",
"favicon_url": "https://ve.search.yahoo.com/favicon.ico",
- "search_url": "https://ve.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://ve.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://ve.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
@@ -584,7 +599,7 @@
"name": "Yahoo! Vi\u1ec7t Nam",
"keyword": "vn.yahoo.com",
"favicon_url": "https://vn.search.yahoo.com/favicon.ico",
- "search_url": "https://vn.search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}",
+ "search_url": "https://vn.search.yahoo.com/search{google:pathWildcard}?ei={inputEncoding}&fr=crmas&p={searchTerms}",
"suggest_url": "https://vn.search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}",
"type": "SEARCH_ENGINE_YAHOO",
"id": 2
diff --git a/chromium/components/search_engines/search_engine_data_type_controller_unittest.cc b/chromium/components/search_engines/search_engine_data_type_controller_unittest.cc
index e71aa9259a5..c61dd5df173 100644
--- a/chromium/components/search_engines/search_engine_data_type_controller_unittest.cc
+++ b/chromium/components/search_engines/search_engine_data_type_controller_unittest.cc
@@ -134,9 +134,9 @@ TEST_F(SyncSearchEngineDataTypeControllerTest, StartAssociationFailed) {
syncer::SEARCH_ENGINES));
Start();
- EXPECT_EQ(syncer::DataTypeController::DISABLED, search_engine_dtc_.state());
+ EXPECT_EQ(syncer::DataTypeController::FAILED, search_engine_dtc_.state());
EXPECT_FALSE(syncable_service_.syncing());
- search_engine_dtc_.Stop();
+ search_engine_dtc_.Stop(syncer::KEEP_METADATA);
EXPECT_EQ(syncer::DataTypeController::NOT_RUNNING,
search_engine_dtc_.state());
EXPECT_FALSE(syncable_service_.syncing());
@@ -153,7 +153,7 @@ TEST_F(SyncSearchEngineDataTypeControllerTest, Stop) {
Start();
EXPECT_EQ(syncer::DataTypeController::RUNNING, search_engine_dtc_.state());
EXPECT_TRUE(syncable_service_.syncing());
- search_engine_dtc_.Stop();
+ search_engine_dtc_.Stop(syncer::KEEP_METADATA);
EXPECT_EQ(syncer::DataTypeController::NOT_RUNNING,
search_engine_dtc_.state());
// AsyncDirectoryTypeController::Stop posts call to StopLocalService to model
@@ -171,7 +171,7 @@ TEST_F(SyncSearchEngineDataTypeControllerTest, StopBeforeLoaded) {
EXPECT_EQ(syncer::DataTypeController::MODEL_STARTING,
search_engine_dtc_.state());
EXPECT_FALSE(syncable_service_.syncing());
- search_engine_dtc_.Stop();
+ search_engine_dtc_.Stop(syncer::KEEP_METADATA);
EXPECT_EQ(nullptr, search_engine_dtc_.GetSubscriptionForTesting());
EXPECT_EQ(syncer::DataTypeController::NOT_RUNNING,
search_engine_dtc_.state());
diff --git a/chromium/components/search_engines/template_url.cc b/chromium/components/search_engines/template_url.cc
index bcb27362724..1c945fecb78 100644
--- a/chromium/components/search_engines/template_url.cc
+++ b/chromium/components/search_engines/template_url.cc
@@ -15,6 +15,7 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/metrics/field_trial.h"
+#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
@@ -1441,7 +1442,7 @@ void TemplateURL::EncodeSearchTerms(
base::string16* encoded_original_query) const {
std::vector<std::string> encodings(input_encodings());
- if (std::find(encodings.begin(), encodings.end(), "UTF-8") == encodings.end())
+ if (!base::ContainsValue(encodings, "UTF-8"))
encodings.push_back("UTF-8");
for (auto i = encodings.begin(); i != encodings.end(); ++i) {
if (TryEncoding(search_terms_args.search_terms,
diff --git a/chromium/components/search_engines/template_url_fetcher.cc b/chromium/components/search_engines/template_url_fetcher.cc
index 47a8a7904db..a930d09b6ad 100644
--- a/chromium/components/search_engines/template_url_fetcher.cc
+++ b/chromium/components/search_engines/template_url_fetcher.cc
@@ -13,10 +13,8 @@
#include "components/search_engines/template_url_service.h"
#include "net/base/load_flags.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_status.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
namespace {
@@ -46,19 +44,20 @@ constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
} // namespace
// RequestDelegate ------------------------------------------------------------
-class TemplateURLFetcher::RequestDelegate : public net::URLFetcherDelegate {
+class TemplateURLFetcher::RequestDelegate {
public:
- RequestDelegate(
- TemplateURLFetcher* fetcher,
- const base::string16& keyword,
- const GURL& osdd_url,
- const GURL& favicon_url,
- const URLFetcherCustomizeCallback& url_fetcher_customize_callback);
-
- // net::URLFetcherDelegate:
+ RequestDelegate(TemplateURLFetcher* fetcher,
+ const base::string16& keyword,
+ const GURL& osdd_url,
+ const GURL& favicon_url,
+ const url::Origin& initiator,
+ network::mojom::URLLoaderFactory* url_loader_factory,
+ int render_frame_id,
+ int resource_type);
+
// If data contains a valid OSDD, a TemplateURL is created and added to
// the TemplateURLService.
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
// URL of the OSDD.
GURL url() const { return osdd_url_; }
@@ -70,7 +69,7 @@ class TemplateURLFetcher::RequestDelegate : public net::URLFetcherDelegate {
void OnLoaded();
void AddSearchProvider();
- std::unique_ptr<net::URLFetcher> url_fetcher_;
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
TemplateURLFetcher* fetcher_;
std::unique_ptr<TemplateURL> template_url_;
base::string16 keyword_;
@@ -87,12 +86,11 @@ TemplateURLFetcher::RequestDelegate::RequestDelegate(
const base::string16& keyword,
const GURL& osdd_url,
const GURL& favicon_url,
- const URLFetcherCustomizeCallback& url_fetcher_customize_callback)
- : url_fetcher_(net::URLFetcher::Create(osdd_url,
- net::URLFetcher::GET,
- this,
- kTrafficAnnotation)),
- fetcher_(fetcher),
+ const url::Origin& initiator,
+ network::mojom::URLLoaderFactory* url_loader_factory,
+ int render_frame_id,
+ int resource_type)
+ : fetcher_(fetcher),
keyword_(keyword),
osdd_url_(osdd_url),
favicon_url_(favicon_url) {
@@ -107,14 +105,22 @@ TemplateURLFetcher::RequestDelegate::RequestDelegate(
model->Load();
}
- if (!url_fetcher_customize_callback.is_null())
- url_fetcher_customize_callback.Run(url_fetcher_.get());
-
- url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DO_NOT_SEND_AUTH_DATA);
- url_fetcher_->SetRequestContext(fetcher->request_context_.get());
- url_fetcher_->Start();
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = osdd_url;
+ resource_request->request_initiator = initiator;
+ resource_request->render_frame_id = render_frame_id;
+ resource_request->resource_type = resource_type;
+ resource_request->load_flags = net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DO_NOT_SEND_AUTH_DATA;
+ simple_url_loader_ = network::SimpleURLLoader::Create(
+ std::move(resource_request), kTrafficAnnotation);
+ simple_url_loader_->DownloadToString(
+ url_loader_factory,
+ base::BindOnce(
+ &TemplateURLFetcher::RequestDelegate::OnSimpleLoaderComplete,
+ base::Unretained(this)),
+ 50000 /* max_body_size */);
}
void TemplateURLFetcher::RequestDelegate::OnLoaded() {
@@ -125,27 +131,19 @@ void TemplateURLFetcher::RequestDelegate::OnLoaded() {
// WARNING: AddSearchProvider deletes us.
}
-void TemplateURLFetcher::RequestDelegate::OnURLFetchComplete(
- const net::URLFetcher* source) {
+void TemplateURLFetcher::RequestDelegate::OnSimpleLoaderComplete(
+ std::unique_ptr<std::string> response_body) {
// Validation checks.
// Make sure we can still replace the keyword, i.e. the fetch was successful.
- // If the OSDD file was loaded HTTP, we also have to check the response_code.
- // For other schemes, e.g. when the OSDD file is bundled with an extension,
- // the response_code is not applicable and should be -1. Also, ensure that
- // the returned information results in a valid search URL.
- std::string data;
- if (!source->GetStatus().is_success() ||
- ((source->GetResponseCode() != -1) &&
- (source->GetResponseCode() != 200)) ||
- !source->GetResponseAsString(&data)) {
+ if (!response_body) {
fetcher_->RequestCompleted(this);
// WARNING: RequestCompleted deletes us.
return;
}
template_url_ = TemplateURLParser::Parse(
- fetcher_->template_url_service_->search_terms_data(), data.data(),
- data.length(), nullptr);
+ fetcher_->template_url_service_->search_terms_data(),
+ response_body->data(), response_body->length(), nullptr);
if (!template_url_ ||
!template_url_->url_ref().SupportsReplacement(
fetcher_->template_url_service_->search_terms_data())) {
@@ -198,12 +196,8 @@ void TemplateURLFetcher::RequestDelegate::AddSearchProvider() {
// TemplateURLFetcher ---------------------------------------------------------
-TemplateURLFetcher::TemplateURLFetcher(
- TemplateURLService* template_url_service,
- net::URLRequestContextGetter* request_context)
- : template_url_service_(template_url_service),
- request_context_(request_context) {
-}
+TemplateURLFetcher::TemplateURLFetcher(TemplateURLService* template_url_service)
+ : template_url_service_(template_url_service) {}
TemplateURLFetcher::~TemplateURLFetcher() {
}
@@ -212,7 +206,10 @@ void TemplateURLFetcher::ScheduleDownload(
const base::string16& keyword,
const GURL& osdd_url,
const GURL& favicon_url,
- const URLFetcherCustomizeCallback& url_fetcher_customize_callback) {
+ const url::Origin& initiator,
+ network::mojom::URLLoaderFactory* url_loader_factory,
+ int render_frame_id,
+ int resource_type) {
DCHECK(osdd_url.is_valid());
DCHECK(!keyword.empty());
@@ -236,7 +233,8 @@ void TemplateURLFetcher::ScheduleDownload(
}
requests_.push_back(std::make_unique<RequestDelegate>(
- this, keyword, osdd_url, favicon_url, url_fetcher_customize_callback));
+ this, keyword, osdd_url, favicon_url, initiator, url_loader_factory,
+ render_frame_id, resource_type));
}
void TemplateURLFetcher::RequestCompleted(RequestDelegate* request) {
diff --git a/chromium/components/search_engines/template_url_fetcher.h b/chromium/components/search_engines/template_url_fetcher.h
index 896c755f027..9944c70d872 100644
--- a/chromium/components/search_engines/template_url_fetcher.h
+++ b/chromium/components/search_engines/template_url_fetcher.h
@@ -18,9 +18,14 @@ class GURL;
class TemplateURL;
class TemplateURLService;
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
+namespace network {
+namespace mojom {
+class URLLoaderFactory;
+}
+} // namespace network
+
+namespace url {
+class Origin;
}
// TemplateURLFetcher is responsible for downloading OpenSearch description
@@ -29,12 +34,8 @@ class URLRequestContextGetter;
//
class TemplateURLFetcher : public KeyedService {
public:
- typedef base::Callback<void(
- net::URLFetcher* url_fetcher)> URLFetcherCustomizeCallback;
-
// Creates a TemplateURLFetcher.
- TemplateURLFetcher(TemplateURLService* template_url_service,
- net::URLRequestContextGetter* request_context);
+ explicit TemplateURLFetcher(TemplateURLService* template_url_service);
~TemplateURLFetcher() override;
// If TemplateURLFetcher is not already downloading the OSDD for osdd_url,
@@ -45,14 +46,13 @@ class TemplateURLFetcher : public KeyedService {
// TemplateURL in the model for |keyword|, or we're already downloading an
// OSDD for this keyword, no download is started.
//
- // If |url_fetcher_customize_callback| is not null, it's run after a
- // URLFetcher is created. This callback can be used to set additional
- // parameters on the URLFetcher.
- void ScheduleDownload(
- const base::string16& keyword,
- const GURL& osdd_url,
- const GURL& favicon_url,
- const URLFetcherCustomizeCallback& url_fetcher_customize_callback);
+ void ScheduleDownload(const base::string16& keyword,
+ const GURL& osdd_url,
+ const GURL& favicon_url,
+ const url::Origin& initiator,
+ network::mojom::URLLoaderFactory* url_loader_factory,
+ int render_frame_id,
+ int resource_type);
// The current number of outstanding requests.
int requests_count() const { return requests_.size(); }
@@ -69,7 +69,6 @@ class TemplateURLFetcher : public KeyedService {
friend class RequestDelegate;
TemplateURLService* template_url_service_;
- scoped_refptr<net::URLRequestContextGetter> request_context_;
// In progress requests.
std::vector<std::unique_ptr<RequestDelegate>> requests_;
diff --git a/chromium/components/search_engines/template_url_service.cc b/chromium/components/search_engines/template_url_service.cc
index 6ad8d799d05..646d44aa3da 100644
--- a/chromium/components/search_engines/template_url_service.cc
+++ b/chromium/components/search_engines/template_url_service.cc
@@ -2232,11 +2232,22 @@ void TemplateURLService::MergeInSyncTemplateURL(
false)) {
std::string guid = conflicting_prepopulated_turl->sync_guid();
if (conflicting_prepopulated_turl == default_search_provider_) {
- // ApplyDefaultSearchChange() may change something that requires a
- // notification, but if so, it will send out that notification, and we
- // are not involved, thus we do not update |should_notify| here.
- ApplyDefaultSearchChange(&sync_turl->data(),
- DefaultSearchManager::FROM_USER);
+ bool pref_matched =
+ prefs_->GetString(prefs::kSyncedDefaultSearchProviderGUID) ==
+ default_search_provider_->sync_guid();
+ // Update the existing engine in-place.
+ Update(default_search_provider_, TemplateURL(sync_turl->data()));
+ // If prefs::kSyncedDefaultSearchProviderGUID matched
+ // |default_search_provider_|'s GUID before, then update it to match its
+ // new GUID. If the pref didn't match before, then it probably refers to
+ // a new search engine from Sync which just hasn't been added locally
+ // yet, so leave it alone in that case.
+ if (pref_matched) {
+ prefs_->SetString(prefs::kSyncedDefaultSearchProviderGUID,
+ default_search_provider_->sync_guid());
+ }
+
+ should_add_sync_turl = false;
merge_result->set_num_items_modified(
merge_result->num_items_modified() + 1);
} else {
diff --git a/chromium/components/search_provider_logos/BUILD.gn b/chromium/components/search_provider_logos/BUILD.gn
index 07e2f6cd5aa..1861979218b 100644
--- a/chromium/components/search_provider_logos/BUILD.gn
+++ b/chromium/components/search_provider_logos/BUILD.gn
@@ -36,6 +36,8 @@ static_library("search_provider_logos") {
"//components/search_engines",
"//components/signin/core/browser",
"//net",
+ "//services/network/public/cpp",
+ "//services/network/public/mojom",
"//ui/gfx",
"//url",
]
@@ -99,6 +101,7 @@ source_set("unit_tests") {
"//components/signin/core/browser:test_support",
"//components/sync_preferences:test_support",
"//net:test_support",
+ "//services/network:test_support",
"//testing/gmock",
"//testing/gtest",
"//ui/gfx",
diff --git a/chromium/components/search_provider_logos/DEPS b/chromium/components/search_provider_logos/DEPS
index 0d5e959a2a7..10a30abb6b2 100644
--- a/chromium/components/search_provider_logos/DEPS
+++ b/chromium/components/search_provider_logos/DEPS
@@ -6,6 +6,9 @@ include_rules = [
"+components/signin/core/browser",
"+components/sync_preferences/testing_pref_service_syncable.h",
"+net",
+ "+services/network/public/cpp",
+ "+services/network/public/mojom",
+ "+services/network/test",
"+third_party/skia/include/core/SkBitmap.h",
"+ui/gfx/image",
]
diff --git a/chromium/components/search_provider_logos/logo_service_impl.cc b/chromium/components/search_provider_logos/logo_service_impl.cc
index 3c7ec3f5de7..6dc002751be 100644
--- a/chromium/components/search_provider_logos/logo_service_impl.cc
+++ b/chromium/components/search_provider_logos/logo_service_impl.cc
@@ -22,7 +22,7 @@
#include "components/search_provider_logos/logo_tracker.h"
#include "components/search_provider_logos/switches.h"
#include "components/signin/core/browser/gaia_cookie_manager_service.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "ui/gfx/image/image.h"
using search_provider_logos::LogoDelegate;
@@ -178,11 +178,11 @@ LogoServiceImpl::LogoServiceImpl(
GaiaCookieManagerService* cookie_service,
TemplateURLService* template_url_service,
std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
- scoped_refptr<net::URLRequestContextGetter> request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
base::RepeatingCallback<bool()> want_gray_logo_getter)
: cache_directory_(cache_directory),
template_url_service_(template_url_service),
- request_context_getter_(request_context_getter),
+ url_loader_factory_(url_loader_factory),
want_gray_logo_getter_(std::move(want_gray_logo_getter)),
image_decoder_(std::move(image_decoder)),
signin_observer_(std::make_unique<SigninObserver>(
@@ -304,7 +304,7 @@ void LogoServiceImpl::InitializeLogoTrackerIfNecessary() {
}
logo_tracker_ = std::make_unique<LogoTracker>(
- request_context_getter_,
+ url_loader_factory_,
std::make_unique<LogoDelegateImpl>(std::move(image_decoder_)),
std::move(logo_cache), clock);
}
diff --git a/chromium/components/search_provider_logos/logo_service_impl.h b/chromium/components/search_provider_logos/logo_service_impl.h
index 6dbf07912bb..16bf1186692 100644
--- a/chromium/components/search_provider_logos/logo_service_impl.h
+++ b/chromium/components/search_provider_logos/logo_service_impl.h
@@ -23,9 +23,9 @@ namespace image_fetcher {
class ImageDecoder;
} // namespace image_fetcher
-namespace net {
-class URLRequestContextGetter;
-} // namespace net
+namespace network {
+class SharedURLLoaderFactory;
+} // namespace network
namespace search_provider_logos {
@@ -40,7 +40,7 @@ class LogoServiceImpl : public LogoService {
GaiaCookieManagerService* cookie_service,
TemplateURLService* template_url_service,
std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
- scoped_refptr<net::URLRequestContextGetter> request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
base::RepeatingCallback<bool()> want_gray_logo_getter);
~LogoServiceImpl() override;
@@ -68,7 +68,7 @@ class LogoServiceImpl : public LogoService {
// Constructor arguments.
const base::FilePath cache_directory_;
TemplateURLService* const template_url_service_;
- const scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+ const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// Callback to get the type of logo to fetch. Returns whether we want a logo
// optimized for gray backgrounds or not.
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 83ac25aff14..9ea86f4bd7e 100644
--- a/chromium/components/search_provider_logos/logo_service_impl_unittest.cc
+++ b/chromium/components/search_provider_logos/logo_service_impl_unittest.cc
@@ -45,8 +45,9 @@
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_status.h"
#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/image/image.h"
@@ -280,16 +281,13 @@ class FakeImageDecoder : public image_fetcher::ImageDecoder {
// signing in/out.
class SigninHelper {
public:
- SigninHelper(base::test::ScopedTaskEnvironment* task_environment,
- net::FakeURLFetcherFactory* fetcher_factory)
+ explicit SigninHelper(base::test::ScopedTaskEnvironment* task_environment)
: task_environment_(task_environment),
signin_client_(&pref_service_),
cookie_service_(&token_service_, "test_source", &signin_client_) {
// GaiaCookieManagerService calls static methods of AccountTrackerService
// which access prefs.
AccountTrackerService::RegisterPrefs(pref_service_.registry());
-
- cookie_service_.Init(fetcher_factory);
}
GaiaCookieManagerService* cookie_service() { return &cookie_service_; }
@@ -321,12 +319,14 @@ class LogoServiceImplTest : public ::testing::Test {
LogoServiceImplTest()
: template_url_service_(nullptr, 0),
logo_cache_(new NiceMock<MockLogoCache>()),
- fake_url_fetcher_factory_(
- nullptr,
- base::Bind(&LogoServiceImplTest::CapturingFakeURLFetcherCreator,
- base::Unretained(this))),
- signin_helper_(&task_environment_, &fake_url_fetcher_factory_),
+ shared_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)),
+ signin_helper_(&task_environment_),
use_gray_background_(false) {
+ test_url_loader_factory_.SetInterceptor(base::BindRepeating(
+ &LogoServiceImplTest::CapturingInterceptor, base::Unretained(this)));
+
// Default search engine with logo. All 3P doodle_urls use ddljson API.
AddSearchEngine("ex", "Logo Example",
"https://example.com/?q={searchTerms}",
@@ -337,8 +337,7 @@ class LogoServiceImplTest : public ::testing::Test {
logo_service_ = std::make_unique<LogoServiceImpl>(
base::FilePath(), signin_helper_.cookie_service(),
&template_url_service_, std::make_unique<FakeImageDecoder>(),
- new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get()),
+ shared_factory_,
base::BindRepeating(&LogoServiceImplTest::use_gray_background,
base::Unretained(this)));
logo_service_->SetClockForTests(&test_clock_);
@@ -359,8 +358,7 @@ class LogoServiceImplTest : public ::testing::Test {
// Sets the response to be returned when the LogoTracker fetches the logo.
void SetServerResponse(const std::string& response,
- net::URLRequestStatus::Status request_status =
- net::URLRequestStatus::SUCCESS,
+ int error_code = net::OK,
net::HttpStatusCode response_code = net::HTTP_OK);
// Sets the response to be returned when the LogoTracker fetches the logo and
@@ -368,8 +366,7 @@ class LogoServiceImplTest : public ::testing::Test {
void SetServerResponseWhenFingerprint(
const std::string& fingerprint,
const std::string& response_when_fingerprint,
- net::URLRequestStatus::Status request_status =
- net::URLRequestStatus::SUCCESS,
+ int error_code = net::OK,
net::HttpStatusCode response_code = net::HTTP_OK);
const GURL& DoodleURL() const;
@@ -385,12 +382,7 @@ class LogoServiceImplTest : public ::testing::Test {
GURL doodle_url,
bool make_default);
- std::unique_ptr<net::FakeURLFetcher> CapturingFakeURLFetcherCreator(
- const GURL& url,
- net::URLFetcherDelegate* delegate,
- const std::string& response_data,
- net::HttpStatusCode response_code,
- net::URLRequestStatus::Status status);
+ void CapturingInterceptor(const network::ResourceRequest& request);
bool use_gray_background() const { return use_gray_background_; }
@@ -398,24 +390,21 @@ class LogoServiceImplTest : public ::testing::Test {
TemplateURLService template_url_service_;
base::SimpleTestClock test_clock_;
NiceMock<MockLogoCache>* logo_cache_;
- net::FakeURLFetcherFactory fake_url_fetcher_factory_;
+
+ // Used for mocking |logo_service_| URLs.
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> shared_factory_;
std::unique_ptr<LogoServiceImpl> logo_service_;
+
SigninHelper signin_helper_;
GURL latest_url_;
bool use_gray_background_;
};
-std::unique_ptr<net::FakeURLFetcher>
-LogoServiceImplTest::CapturingFakeURLFetcherCreator(
- const GURL& url,
- net::URLFetcherDelegate* delegate,
- const std::string& response_data,
- net::HttpStatusCode response_code,
- net::URLRequestStatus::Status status) {
- latest_url_ = url;
- return std::make_unique<net::FakeURLFetcher>(url, delegate, response_data,
- response_code, status);
+void LogoServiceImplTest::CapturingInterceptor(
+ const network::ResourceRequest& request) {
+ latest_url_ = request.url;
}
std::string LogoServiceImplTest::ServerResponse(const Logo& logo) {
@@ -425,23 +414,34 @@ std::string LogoServiceImplTest::ServerResponse(const Logo& logo) {
return MakeServerResponse(logo, time_to_live);
}
-void LogoServiceImplTest::SetServerResponse(
- const std::string& response,
- net::URLRequestStatus::Status request_status,
- net::HttpStatusCode response_code) {
- SetServerResponseWhenFingerprint(std::string(), response, request_status,
+void LogoServiceImplTest::SetServerResponse(const std::string& response,
+ int error_code,
+ net::HttpStatusCode response_code) {
+ SetServerResponseWhenFingerprint(std::string(), response, error_code,
response_code);
}
void LogoServiceImplTest::SetServerResponseWhenFingerprint(
const std::string& fingerprint,
const std::string& response_when_fingerprint,
- net::URLRequestStatus::Status request_status,
+ int error_code,
net::HttpStatusCode response_code) {
GURL url_with_fp = AppendFingerprintParamToDoodleURL(
AppendPreliminaryParamsToDoodleURL(false, DoodleURL()), fingerprint);
- fake_url_fetcher_factory_.SetFakeResponse(
- url_with_fp, response_when_fingerprint, response_code, request_status);
+
+ network::ResourceResponseHead head;
+ std::string headers(base::StringPrintf(
+ "HTTP/1.1 %d %s\nContent-type: text/html\n\n",
+ static_cast<int>(response_code), GetHttpReasonPhrase(response_code)));
+ head.headers = new net::HttpResponseHeaders(
+ net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()));
+ head.mime_type = "text/html";
+ network::URLLoaderCompletionStatus status;
+ status.error_code = error_code;
+ status.decoded_body_length = response_when_fingerprint.size();
+
+ test_url_loader_factory_.AddResponse(url_with_fp, head,
+ response_when_fingerprint, status);
}
const GURL& LogoServiceImplTest::DoodleURL() const {
@@ -498,10 +498,9 @@ TEST_F(LogoServiceImplTest, CTARequestedBackgroundCanUpdate) {
AppendPreliminaryParamsToDoodleURL(false, DoodleURL()), std::string());
use_gray_background_ = false;
- fake_url_fetcher_factory_.ClearFakeResponses();
- fake_url_fetcher_factory_.SetFakeResponse(query_without_gray_background,
- response, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ test_url_loader_factory_.ClearResponses();
+ test_url_loader_factory_.AddResponse(query_without_gray_background.spec(),
+ response, net::HTTP_OK);
{
StrictMock<MockLogoCallback> fresh;
EXPECT_CALL(fresh, Run(_, _));
@@ -513,10 +512,9 @@ TEST_F(LogoServiceImplTest, CTARequestedBackgroundCanUpdate) {
EXPECT_EQ(latest_url_.query().find("graybg:1"), std::string::npos);
use_gray_background_ = true;
- fake_url_fetcher_factory_.ClearFakeResponses();
- fake_url_fetcher_factory_.SetFakeResponse(query_with_gray_background,
- response, net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
+ test_url_loader_factory_.ClearResponses();
+ test_url_loader_factory_.AddResponse(query_with_gray_background.spec(),
+ response, net::HTTP_OK);
{
StrictMock<MockLogoCallback> fresh;
EXPECT_CALL(fresh, Run(_, _));
@@ -589,7 +587,7 @@ TEST_F(LogoServiceImplTest, EmptyCacheAndFailedDownload) {
{
StrictMock<MockLogoCallback> cached;
StrictMock<MockLogoCallback> fresh;
- SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
+ SetServerResponse("", net::ERR_FAILED, net::HTTP_OK);
EXPECT_CALL(cached, Run(LogoCallbackReason::DETERMINED, Eq(base::nullopt)));
EXPECT_CALL(fresh, Run(LogoCallbackReason::FAILED, Eq(base::nullopt)));
GetDecodedLogo(cached.Get(), fresh.Get());
@@ -598,8 +596,7 @@ TEST_F(LogoServiceImplTest, EmptyCacheAndFailedDownload) {
{
StrictMock<MockLogoCallback> cached;
StrictMock<MockLogoCallback> fresh;
- SetServerResponse("", net::URLRequestStatus::SUCCESS,
- net::HTTP_BAD_GATEWAY);
+ SetServerResponse("", net::OK, net::HTTP_BAD_GATEWAY);
EXPECT_CALL(cached, Run(LogoCallbackReason::DETERMINED, Eq(base::nullopt)));
EXPECT_CALL(fresh, Run(LogoCallbackReason::FAILED, Eq(base::nullopt)));
GetDecodedLogo(cached.Get(), fresh.Get());
@@ -632,7 +629,7 @@ TEST_F(LogoServiceImplTest, ReturnCachedLogo) {
Logo cached_logo = GetSampleLogo(DoodleURL(), test_clock_.Now());
logo_cache_->EncodeAndSetCachedLogo(cached_logo);
SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint, "",
- net::URLRequestStatus::FAILED, net::HTTP_OK);
+ net::ERR_FAILED, net::HTTP_OK);
EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
@@ -769,7 +766,7 @@ TEST_F(LogoServiceImplTest, InvalidateCachedLogo) {
}
TEST_F(LogoServiceImplTest, DeleteCachedLogoFromOldUrl) {
- SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
+ SetServerResponse("", net::ERR_FAILED, net::HTTP_OK);
Logo cached_logo =
GetSampleLogo(GURL("https://oldsearchprovider.com"), test_clock_.Now());
logo_cache_->EncodeAndSetCachedLogo(cached_logo);
@@ -820,7 +817,7 @@ TEST_F(LogoServiceImplTest, LogoWithoutTTLCanBeShownAfterExpiration) {
}
TEST_F(LogoServiceImplTest, UseSoftExpiredCachedLogo) {
- SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
+ SetServerResponse("", net::ERR_FAILED, net::HTTP_OK);
Logo cached_logo = GetSampleLogo(DoodleURL(), test_clock_.Now());
cached_logo.metadata.expiration_time =
test_clock_.Now() - base::TimeDelta::FromSeconds(1);
@@ -862,7 +859,7 @@ TEST_F(LogoServiceImplTest, RerequestSoftExpiredCachedLogo) {
}
TEST_F(LogoServiceImplTest, DeleteAncientCachedLogo) {
- SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
+ SetServerResponse("", net::ERR_FAILED, net::HTTP_OK);
Logo cached_logo = GetSampleLogo(DoodleURL(), test_clock_.Now());
cached_logo.metadata.expiration_time =
test_clock_.Now() - base::TimeDelta::FromDays(200);
@@ -883,7 +880,7 @@ TEST_F(LogoServiceImplTest, DeleteAncientCachedLogo) {
}
TEST_F(LogoServiceImplTest, DeleteExpiredCachedLogo) {
- SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
+ SetServerResponse("", net::ERR_FAILED, net::HTTP_OK);
Logo cached_logo = GetSampleLogo(DoodleURL(), test_clock_.Now());
cached_logo.metadata.expiration_time =
test_clock_.Now() - base::TimeDelta::FromSeconds(1);
diff --git a/chromium/components/search_provider_logos/logo_tracker.cc b/chromium/components/search_provider_logos/logo_tracker.cc
index 4c48e4f3fe3..f8e50cd9cb6 100644
--- a/chromium/components/search_provider_logos/logo_tracker.cc
+++ b/chromium/components/search_provider_logos/logo_tracker.cc
@@ -17,9 +17,9 @@
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_status.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 search_provider_logos {
@@ -81,7 +81,7 @@ void NotifyAndClear(std::vector<EncodedLogoCallback>* encoded_callbacks,
} // namespace
LogoTracker::LogoTracker(
- scoped_refptr<net::URLRequestContextGetter> request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
std::unique_ptr<LogoDelegate> delegate,
std::unique_ptr<LogoCache> logo_cache,
base::Clock* clock)
@@ -94,7 +94,7 @@ LogoTracker::LogoTracker(
logo_cache_(logo_cache.release(),
base::OnTaskRunnerDeleter(cache_task_runner_)),
clock_(clock),
- request_context_getter_(request_context_getter),
+ url_loader_factory_(std::move(url_loader_factory)),
weak_ptr_factory_(this) {}
LogoTracker::~LogoTracker() {
@@ -169,7 +169,7 @@ void LogoTracker::ReturnToIdle(int outcome) {
DOWNLOAD_OUTCOME_COUNT);
}
// Cancel the current asynchronous operation, if any.
- fetcher_.reset();
+ loader_.reset();
weak_ptr_factory_.InvalidateWeakPtrs();
// Reset state.
@@ -235,7 +235,7 @@ void LogoTracker::SetCachedMetadata(const LogoMetadata& metadata) {
}
void LogoTracker::FetchLogo() {
- DCHECK(!fetcher_);
+ DCHECK(!loader_);
DCHECK(!is_idle_);
std::string fingerprint;
@@ -268,13 +268,18 @@ void LogoTracker::FetchLogo() {
"Not implemented, considered not useful as it does not upload any"
"data and just downloads a logo image."
})");
- fetcher_ = net::URLFetcher::Create(url, net::URLFetcher::GET, this,
- traffic_annotation);
- fetcher_->SetRequestContext(request_context_getter_.get());
- data_use_measurement::DataUseUserData::AttachToFetcher(
- fetcher_.get(),
- data_use_measurement::DataUseUserData::SEARCH_PROVIDER_LOGOS);
- fetcher_->Start();
+ auto request = std::make_unique<network::ResourceRequest>();
+ request->url = url;
+ // TODO(https://crbug.com/808498) re-add data use measurement once
+ // SimpleURLLoader supports it:
+ // data_use_measurement::DataUseUserData::SEARCH_PROVIDER_LOGOS
+ loader_ =
+ network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
+ loader_->DownloadToString(
+ url_loader_factory_.get(),
+ base::BindOnce(&LogoTracker::OnURLLoadComplete, base::Unretained(this),
+ loader_.get()),
+ kMaxDownloadBytes);
logo_download_start_time_ = base::TimeTicks::Now();
}
@@ -414,21 +419,19 @@ void LogoTracker::OnFreshLogoAvailable(
ReturnToIdle(download_outcome);
}
-void LogoTracker::OnURLFetchComplete(const net::URLFetcher* source) {
+void LogoTracker::OnURLLoadComplete(const network::SimpleURLLoader* source,
+ std::unique_ptr<std::string> body) {
DCHECK(!is_idle_);
- std::unique_ptr<net::URLFetcher> cleanup_fetcher(fetcher_.release());
+ std::unique_ptr<network::SimpleURLLoader> cleanup_loader(loader_.release());
- if (!source->GetStatus().is_success()) {
+ if (source->NetError() != net::OK) {
OnFreshLogoAvailable({}, /*download_failed=*/true, false, false,
SkBitmap());
return;
}
- int response_code = source->GetResponseCode();
- if (response_code != net::HTTP_OK &&
- response_code != net::URLFetcher::RESPONSE_CODE_INVALID) {
- // RESPONSE_CODE_INVALID is returned when fetching from a file: URL
- // (for testing). In all other cases we would have had a non-success status.
+ if (!source->ResponseInfo() || !source->ResponseInfo()->headers ||
+ source->ResponseInfo()->headers->response_code() != net::HTTP_OK) {
OnFreshLogoAvailable({}, /*download_failed=*/true, false, false,
SkBitmap());
return;
@@ -437,11 +440,11 @@ void LogoTracker::OnURLFetchComplete(const net::URLFetcher* source) {
UMA_HISTOGRAM_TIMES("NewTabPage.LogoDownloadTime",
base::TimeTicks::Now() - logo_download_start_time_);
- std::unique_ptr<std::string> response(new std::string());
- source->GetResponseAsString(response.get());
+ std::unique_ptr<std::string> response =
+ body ? std::move(body) : std::make_unique<std::string>();
base::Time response_time = clock_->Now();
- bool from_http_cache = source->WasCached();
+ bool from_http_cache = !source->ResponseInfo()->network_accessed;
bool* parsing_failed = new bool(false);
base::PostTaskWithTraitsAndReplyWithResult(
@@ -455,15 +458,4 @@ void LogoTracker::OnURLFetchComplete(const net::URLFetcher* source) {
base::Owned(parsing_failed), from_http_cache));
}
-void LogoTracker::OnURLFetchDownloadProgress(const net::URLFetcher* source,
- int64_t current,
- int64_t total,
- int64_t current_network_bytes) {
- if (total > kMaxDownloadBytes || current > kMaxDownloadBytes) {
- LOG(WARNING) << "Search provider logo exceeded download size limit";
- OnFreshLogoAvailable({}, /*download_failed=*/true, false, false,
- SkBitmap());
- }
-}
-
} // namespace search_provider_logos
diff --git a/chromium/components/search_provider_logos/logo_tracker.h b/chromium/components/search_provider_logos/logo_tracker.h
index 8c3191beb7b..7951bcfd1a9 100644
--- a/chromium/components/search_provider_logos/logo_tracker.h
+++ b/chromium/components/search_provider_logos/logo_tracker.h
@@ -21,12 +21,11 @@
#include "base/time/time.h"
#include "components/search_provider_logos/logo_cache.h"
#include "components/search_provider_logos/logo_common.h"
-#include "net/url_request/url_fetcher_delegate.h"
#include "url/gurl.h"
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
+namespace network {
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
}
namespace search_provider_logos {
@@ -73,7 +72,7 @@ class LogoDelegate {
// Call SetServerAPI() at least once to specify how to get the logo from the
// server. Then call GetLogo() to trigger retrieval of the logo and receive
// updates once the cached and/or fresh logos are available.
-class LogoTracker : public net::URLFetcherDelegate {
+class LogoTracker {
public:
// Constructs a LogoTracker with the given LogoDelegate. Takes ownership of
// |delegate|, which will be deleted at the same time as the LogoTracker.
@@ -84,15 +83,15 @@ class LogoTracker : public net::URLFetcherDelegate {
// |background_task_runner| is the TaskRunner that should be used to for
// CPU-intensive background operations.
//
- // |request_context_getter| is the URLRequestContextGetter used to download
+ // |url_loader_factory| is the SharedURLLoaderFactory used to download
// the logo.
explicit LogoTracker(
- scoped_refptr<net::URLRequestContextGetter> request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
std::unique_ptr<LogoDelegate> delegate,
std::unique_ptr<LogoCache> logo_cache,
base::Clock* clock);
- ~LogoTracker() override;
+ ~LogoTracker();
// Defines the server API for downloading and parsing the logo. This must be
// called at least once before calling GetLogo().
@@ -173,12 +172,9 @@ class LogoTracker : public net::URLFetcherDelegate {
bool from_http_cache,
const SkBitmap& image);
- // net::URLFetcherDelegate:
- void OnURLFetchComplete(const net::URLFetcher* source) override;
- void OnURLFetchDownloadProgress(const net::URLFetcher* source,
- int64_t current,
- int64_t total,
- int64_t current_network_bytes) override;
+ // Invoked by |loader|.
+ void OnURLLoadComplete(const network::SimpleURLLoader* source,
+ std::unique_ptr<std::string> body);
// The URL from which the logo is fetched.
GURL logo_url_;
@@ -207,8 +203,8 @@ class LogoTracker : public net::URLFetcherDelegate {
// The timestamp for the last time a logo is stated to be downloaded.
base::TimeTicks logo_download_start_time_;
- // The URLFetcher currently fetching the logo. NULL when not fetching.
- std::unique_ptr<net::URLFetcher> fetcher_;
+ // The SimpleURLLoader currently fetching the logo. NULL when not loading.
+ std::unique_ptr<network::SimpleURLLoader> loader_;
// Lists of callbacks to be invoked when logos are available. All should be
// empty when the state is IDLE.
@@ -229,8 +225,8 @@ class LogoTracker : public net::URLFetcherDelegate {
// Clock used to determine current time. Can be overridden in tests.
base::Clock* clock_;
- // The URLRequestContextGetter used for network requests.
- scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+ // Used for network requests.
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
base::WeakPtrFactory<LogoTracker> weak_ptr_factory_;
diff --git a/chromium/components/security_interstitials/content/unsafe_resource.cc b/chromium/components/security_interstitials/content/unsafe_resource.cc
index 6a29eb121e7..f9693b52796 100644
--- a/chromium/components/security_interstitials/content/unsafe_resource.cc
+++ b/chromium/components/security_interstitials/content/unsafe_resource.cc
@@ -47,8 +47,12 @@ bool UnsafeResource::IsMainPageLoadBlocked() const {
case safe_browsing::SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE:
// Ad sampling happens in the background.
case safe_browsing::SB_THREAT_TYPE_AD_SAMPLE:
- // Password reuse warning happens after the page is finished loading.
- case safe_browsing::SB_THREAT_TYPE_PASSWORD_REUSE:
+ // Sign-in password reuse warning happens after the page is finished
+ // loading.
+ case safe_browsing::SB_THREAT_TYPE_SIGN_IN_PASSWORD_REUSE:
+ // Enterprise password reuse warning happens after the page is finished
+ // loading.
+ case safe_browsing::SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE:
// Suspicious site collection happens in the background
case safe_browsing::SB_THREAT_TYPE_SUSPICIOUS_SITE:
return false;
diff --git a/chromium/components/security_interstitials/content/unsafe_resource.h b/chromium/components/security_interstitials/content/unsafe_resource.h
index 5f7a488e8f5..dfa70cb6857 100644
--- a/chromium/components/security_interstitials/content/unsafe_resource.h
+++ b/chromium/components/security_interstitials/content/unsafe_resource.h
@@ -68,7 +68,7 @@ struct UnsafeResource {
base::Callback<content::WebContents*(void)> web_contents_getter;
safe_browsing::ThreatSource threat_source;
// |token| field is only set if |threat_type| is
- // SB_THREAT_TYPE_PASSWORD_REUSE.
+ // SB_THREAT_TYPE_*_PASSWORD_REUSE.
std::string token;
};
diff --git a/chromium/components/security_interstitials_strings.grdp b/chromium/components/security_interstitials_strings.grdp
index 7cd2c265d6d..c37249385f8 100644
--- a/chromium/components/security_interstitials_strings.grdp
+++ b/chromium/components/security_interstitials_strings.grdp
@@ -239,7 +239,7 @@
<ph name="BEGIN_WHITEPAPER_LINK">&lt;a href="#" id="whitepaper-link"&gt;</ph>Automatically report<ph name="END_WHITEPAPER_LINK">&lt;/a&gt;</ph> details of possible security incidents to Google. <ph name="PRIVACY_PAGE_LINK">$1</ph>
</message>
<message name="IDS_SAFE_BROWSING_SCOUT_REPORTING_AGREE" desc="SafeBrowsing Scout label next to checkbox">
- Automatically send some <ph name="BEGIN_WHITEPAPER_LINK">&lt;a href="#" id="whitepaper-link"&gt;</ph>system information and page content<ph name="END_WHITEPAPER_LINK">&lt;/a&gt;</ph> to Google to help detect dangerous apps and sites. <ph name="PRIVACY_PAGE_LINK">$1</ph>
+ Help improve Safe Browsing by sending some <ph name="BEGIN_WHITEPAPER_LINK">&lt;a href="#" id="whitepaper-link"&gt;</ph>system information and page content<ph name="END_WHITEPAPER_LINK">&lt;/a&gt;</ph> to Google. <ph name="PRIVACY_PAGE_LINK">$1</ph>
</message>
<!-- Harmful download interstitial -->
diff --git a/chromium/components/security_state/OWNERS b/chromium/components/security_state/OWNERS
index 96886d353e1..03ba2881b38 100644
--- a/chromium/components/security_state/OWNERS
+++ b/chromium/components/security_state/OWNERS
@@ -1,4 +1,3 @@
-elawrence@chromium.org
estark@chromium.org
felt@chromium.org
diff --git a/chromium/components/security_state/content/content_utils_unittest.cc b/chromium/components/security_state/content/content_utils_unittest.cc
index f5140a8396f..18d8a8597a0 100644
--- a/chromium/components/security_state/content/content_utils_unittest.cc
+++ b/chromium/components/security_state/content/content_utils_unittest.cc
@@ -10,7 +10,7 @@
#include "base/atomic_sequence_num.h"
#include "base/command_line.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/security_state/core/security_state.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/security_style_explanation.h"
diff --git a/chromium/components/security_state/core/security_state.h b/chromium/components/security_state/core/security_state.h
index 4e6002adf8f..d50c445a58b 100644
--- a/chromium/components/security_state/core/security_state.h
+++ b/chromium/components/security_state/core/security_state.h
@@ -90,7 +90,8 @@ enum MaliciousContentStatus {
MALICIOUS_CONTENT_STATUS_MALWARE,
MALICIOUS_CONTENT_STATUS_UNWANTED_SOFTWARE,
MALICIOUS_CONTENT_STATUS_SOCIAL_ENGINEERING,
- MALICIOUS_CONTENT_STATUS_PASSWORD_REUSE,
+ MALICIOUS_CONTENT_STATUS_SIGN_IN_PASSWORD_REUSE,
+ MALICIOUS_CONTENT_STATUS_ENTERPRISE_PASSWORD_REUSE,
};
// Describes the security status of a page or request. This is the
diff --git a/chromium/components/security_state/core/security_state_unittest.cc b/chromium/components/security_state/core/security_state_unittest.cc
index b3276ba1033..22edd55438e 100644
--- a/chromium/components/security_state/core/security_state_unittest.cc
+++ b/chromium/components/security_state/core/security_state_unittest.cc
@@ -10,7 +10,7 @@
#include "base/bind.h"
#include "base/command_line.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "components/security_state/core/features.h"
#include "components/security_state/core/insecure_input_event_data.h"
diff --git a/chromium/components/services/filesystem/OWNERS b/chromium/components/services/filesystem/OWNERS
index 771a70f0147..21beb1aaa89 100644
--- a/chromium/components/services/filesystem/OWNERS
+++ b/chromium/components/services/filesystem/OWNERS
@@ -1,5 +1,3 @@
-erg@chromium.org
-
per-file manifest.json=set noparent
per-file manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/services/font/BUILD.gn b/chromium/components/services/font/BUILD.gn
index 5ca31f1b490..a6ce90a915e 100644
--- a/chromium/components/services/font/BUILD.gn
+++ b/chromium/components/services/font/BUILD.gn
@@ -2,8 +2,14 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//build/config/features.gni")
+import("//build/config/features.gni")
+import("//ppapi/buildflags/buildflags.gni")
+import("//ppapi/buildflags/buildflags.gni")
import("//services/service_manager/public/cpp/service.gni")
import("//services/service_manager/public/service_manifest.gni")
+import("//services/service_manager/public/tools/test/service_test.gni")
+import("//testing/test.gni")
source_set("lib") {
sources = [
@@ -16,12 +22,38 @@ source_set("lib") {
"//components/services/font/public/interfaces",
"//mojo/public/cpp/bindings",
"//mojo/public/cpp/system",
+ "//ppapi/buildflags:buildflags",
"//services/service_manager/public/cpp",
+ "//ui/gfx",
]
public_deps = [
"//skia",
]
+
+ if (is_linux && enable_plugins) {
+ deps += [ ":ppapi_fontconfig_matching" ]
+ }
+
+ if (is_linux) {
+ deps += [ "//base/test:fontconfig_util_linux" ]
+ }
+}
+
+if (is_linux && enable_plugins) {
+ source_set("ppapi_fontconfig_matching") {
+ sources = [
+ "ppapi_fontconfig_matching.cc",
+ "ppapi_fontconfig_matching.h",
+ ]
+
+ deps = [
+ "//base:base",
+ "//ppapi/buildflags:buildflags",
+ "//ppapi/c",
+ "//third_party/fontconfig",
+ ]
+ }
}
service("font_service") {
@@ -41,3 +73,45 @@ service_manifest("manifest") {
name = "font_service"
source = "manifest.json"
}
+
+service_test("font_service_unittests") {
+ sources = [
+ "font_loader_test.cc",
+ "font_loader_test.h",
+ ]
+
+ catalog = ":font_service_unittests_catalog"
+
+ deps = [
+ "//base",
+ "//components/services/font/public/cpp",
+ "//components/services/font/public/interfaces",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/system",
+ "//ppapi/buildflags:buildflags",
+ "//services/service_manager/public/cpp",
+ "//services/service_manager/public/cpp:service_test_support",
+ "//skia",
+ ]
+
+ if (enable_plugins) {
+ deps += [
+ "//ppapi/c",
+ "//third_party:freetype_harfbuzz",
+ ]
+ }
+
+ data_deps = [
+ ":font_service",
+ ]
+}
+
+service_manifest("test_manifest") {
+ name = "font_service_unittests"
+ source = "test_manifest.json"
+}
+
+catalog("font_service_unittests_catalog") {
+ embedded_services = [ ":test_manifest" ]
+ standalone_services = [ ":manifest" ]
+}
diff --git a/chromium/components/services/font/DEPS b/chromium/components/services/font/DEPS
index dccac320689..b81f8787fc9 100644
--- a/chromium/components/services/font/DEPS
+++ b/chromium/components/services/font/DEPS
@@ -1,6 +1,9 @@
include_rules = [
"+services/service_manager",
"+mojo/public",
+ "+ppapi/buildflags",
+ "+ppapi/c",
"+skia",
"+third_party/skia/include",
+ "+ui/gfx"
]
diff --git a/chromium/components/services/font/OWNERS b/chromium/components/services/font/OWNERS
index 0447911a6c3..3d43cf0bd5f 100644
--- a/chromium/components/services/font/OWNERS
+++ b/chromium/components/services/font/OWNERS
@@ -1,4 +1,6 @@
-erg@chromium.org
+drott@chromium.org
per-file manifest.json=set noparent
per-file manifest.json=file://ipc/SECURITY_OWNERS
+per-file test_manifest.json=set noparent
+per-file test_manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/services/font/font_loader_test.cc b/chromium/components/services/font/font_loader_test.cc
new file mode 100644
index 00000000000..4425736a967
--- /dev/null
+++ b/chromium/components/services/font/font_loader_test.cc
@@ -0,0 +1,235 @@
+// 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/services/font/font_loader_test.h"
+
+#include <utility>
+
+#include "base/run_loop.h"
+#include "components/services/font/public/interfaces/constants.mojom.h"
+#include "components/services/font/public/interfaces/font_service.mojom.h"
+#include "ppapi/buildflags/buildflags.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "third_party/skia/include/core/SkFontStyle.h"
+
+#if BUILDFLAG(ENABLE_PLUGINS)
+#include <ft2build.h>
+#include <freetype/freetype.h>
+#include "ppapi/c/private/pp_private_font_charset.h" // nogncheck
+#endif
+
+namespace {
+bool IsInTestFontDirectory(const char* path) {
+ const char kTestFontsDir[] = "test_fonts";
+ return std::string(path).find(kTestFontsDir) != std::string::npos;
+}
+
+#if BUILDFLAG(ENABLE_PLUGINS)
+std::string GetPostscriptNameFromFile(base::File& font_file) {
+ int64_t file_size = font_file.GetLength();
+ if (!file_size)
+ return "";
+
+ std::vector<char> file_contents;
+ file_contents.reserve(file_size);
+ CHECK_EQ(file_size, font_file.Read(0, file_contents.data(), file_size));
+ std::string font_family_name;
+ FT_Library library;
+ FT_Init_FreeType(&library);
+ FT_Face font_face;
+ FT_Open_Args open_args = {FT_OPEN_MEMORY,
+ reinterpret_cast<FT_Byte*>(file_contents.data()),
+ file_size};
+ CHECK_EQ(FT_Err_Ok, FT_Open_Face(library, &open_args, 0, &font_face));
+ font_family_name = FT_Get_Postscript_Name(font_face);
+ FT_Done_Face(font_face);
+ FT_Done_FreeType(library);
+ return font_family_name;
+}
+#endif
+
+} // namespace
+
+namespace font_service {
+
+FontLoaderTest::FontLoaderTest() : ServiceTest("font_service_unittests") {}
+
+FontLoaderTest::~FontLoaderTest() {}
+
+void FontLoaderTest::SetUp() {
+ ServiceTest::SetUp();
+ font_loader_ = std::make_unique<FontLoader>(connector());
+}
+
+TEST_F(FontLoaderTest, BasicMatchingTest) {
+ SkFontStyle styles[] = {
+ SkFontStyle(SkFontStyle::kNormal_Weight, SkFontStyle::kNormal_Width,
+ SkFontStyle::kUpright_Slant),
+ SkFontStyle(SkFontStyle::kBold_Weight, SkFontStyle::kNormal_Width,
+ SkFontStyle::kUpright_Slant),
+ SkFontStyle(SkFontStyle::kBold_Weight, SkFontStyle::kNormal_Width,
+ SkFontStyle::kItalic_Slant)};
+ // See kFontsConfTemplate[] in fontconfig_util_linux.cc for details of which
+ // fonts can be picked. Arial, Times New Roman and Courier New are aliased to
+ // Arimos, Tinos and Cousine ChromeOS open source alternatives when FontConfig
+ // is set up for testing.
+ std::vector<std::vector<std::string>> family_names_expected_names = {
+ {"Arial", "Arimo"},
+ {"Times New Roman", "Tinos"},
+ {"Courier New", "Cousine"}};
+ for (std::vector<std::string> request_family_name :
+ family_names_expected_names) {
+ for (auto& request_style : styles) {
+ SkFontConfigInterface::FontIdentity font_identity;
+ SkFontStyle result_style;
+ SkString result_family_name;
+ font_loader()->matchFamilyName(request_family_name[0].c_str(),
+ request_style, &font_identity,
+ &result_family_name, &result_style);
+ EXPECT_EQ(request_family_name[1],
+ std::string(result_family_name.c_str()));
+ EXPECT_TRUE(IsInTestFontDirectory(font_identity.fString.c_str()));
+ EXPECT_EQ(result_style, request_style);
+ }
+ }
+}
+
+TEST_F(FontLoaderTest, NotFoundTest) {
+ std::string request_family_name = {"IMPROBABLE_FONT_NAME"};
+ SkFontConfigInterface::FontIdentity font_identity;
+ SkFontStyle result_style;
+ SkString result_family_name;
+ font_loader()->matchFamilyName(request_family_name.c_str(), SkFontStyle(),
+ &font_identity, &result_family_name,
+ &result_style);
+ EXPECT_EQ("", std::string(result_family_name.c_str()));
+ EXPECT_EQ(0u, font_identity.fID);
+ EXPECT_EQ("", std::string(font_identity.fString.c_str()));
+}
+
+TEST_F(FontLoaderTest, EmptyFontName) {
+ std::string request_family_name = {""};
+ std::string kDefaultFontName = "DejaVu Sans";
+ SkFontConfigInterface::FontIdentity font_identity;
+ SkFontStyle result_style;
+ SkString result_family_name;
+ font_loader()->matchFamilyName(request_family_name.c_str(), SkFontStyle(),
+ &font_identity, &result_family_name,
+ &result_style);
+ EXPECT_EQ(kDefaultFontName, std::string(result_family_name.c_str()));
+ EXPECT_TRUE(IsInTestFontDirectory(font_identity.fString.c_str()));
+}
+
+TEST_F(FontLoaderTest, CharacterFallback) {
+ std::pair<uint32_t, std::string> fallback_characters_families[] = {
+ // A selection of character hitting fonts from the third_party/test_fonts
+ // portfolio.
+ {0x662F /* CJK UNIFIED IDEOGRAPH-662F */, "Noto Sans CJK JP"},
+ {0x1780 /* KHMER LETTER KA */, "Noto Sans Khmer"},
+ {0xA05 /* GURMUKHI LETTER A */, "Lohit Gurmukhi"},
+ {0xB85 /* TAMIL LETTER A */, "Lohit Tamil"},
+ {0x904 /* DEVANAGARI LETTER SHORT A */, "Lohit Devanagari"},
+ {0x985 /* BENGALI LETTER A */, "Mukti Narrow"},
+ // Tests for not finding fallback:
+ {0x13170 /* EGYPTIAN HIEROGLYPH G042 */, ""},
+ {0x1817 /* MONGOLIAN DIGIT SEVEN */, ""}};
+ for (auto& character_family : fallback_characters_families) {
+ mojom::FontIdentityPtr font_identity;
+ std::string result_family_name;
+ bool is_bold;
+ bool is_italic;
+ font_loader()->FallbackFontForCharacter(
+ std::move(character_family.first), "en_US", &font_identity,
+ &result_family_name, &is_bold, &is_italic);
+ EXPECT_EQ(result_family_name, character_family.second);
+ EXPECT_FALSE(is_bold);
+ EXPECT_FALSE(is_italic);
+ if (character_family.second.size()) {
+ EXPECT_TRUE(
+ IsInTestFontDirectory(font_identity->str_representation.c_str()));
+ } else {
+ EXPECT_EQ(font_identity->str_representation.size(), 0u);
+ EXPECT_EQ(result_family_name, "");
+ }
+ }
+}
+
+TEST_F(FontLoaderTest, RenderStyleForStrike) {
+ // Use FontConfig configured test font aliases from kFontsConfTemplate in
+ // fontconfig_util_linux.cc.
+ std::pair<std::string, mojom::FontRenderStylePtr> families_styles[] = {
+ {"NonAntiAliasedSans",
+ mojom::FontRenderStyle::New(
+ mojom::RenderStyleSwitch::ON, mojom::RenderStyleSwitch::OFF,
+ mojom::RenderStyleSwitch::ON, 3, mojom::RenderStyleSwitch::OFF,
+ mojom::RenderStyleSwitch::OFF, mojom::RenderStyleSwitch::OFF)},
+ {"SlightHintedGeorgia",
+ mojom::FontRenderStyle::New(
+ mojom::RenderStyleSwitch::ON, mojom::RenderStyleSwitch::OFF,
+ mojom::RenderStyleSwitch::ON, 1, mojom::RenderStyleSwitch::ON,
+ mojom::RenderStyleSwitch::OFF, mojom::RenderStyleSwitch::OFF)},
+ {"NonHintedSans",
+ mojom::FontRenderStyle::New(
+ mojom::RenderStyleSwitch::ON, mojom::RenderStyleSwitch::OFF,
+ mojom::RenderStyleSwitch::OFF, 0, mojom::RenderStyleSwitch::ON,
+ mojom::RenderStyleSwitch::OFF, mojom::RenderStyleSwitch::OFF)},
+ {"AutohintedSerif",
+ mojom::FontRenderStyle::New(
+ mojom::RenderStyleSwitch::ON, mojom::RenderStyleSwitch::ON,
+ mojom::RenderStyleSwitch::ON, 2, mojom::RenderStyleSwitch::ON,
+ mojom::RenderStyleSwitch::OFF, mojom::RenderStyleSwitch::OFF)},
+ {"HintedSerif",
+ mojom::FontRenderStyle::New(
+ mojom::RenderStyleSwitch::ON, mojom::RenderStyleSwitch::OFF,
+ mojom::RenderStyleSwitch::ON, 2, mojom::RenderStyleSwitch::ON,
+ mojom::RenderStyleSwitch::OFF, mojom::RenderStyleSwitch::OFF)},
+ {"FullAndAutoHintedSerif",
+ mojom::FontRenderStyle::New(
+ mojom::RenderStyleSwitch::ON, mojom::RenderStyleSwitch::ON,
+ mojom::RenderStyleSwitch::ON, 3, mojom::RenderStyleSwitch::ON,
+ mojom::RenderStyleSwitch::OFF, mojom::RenderStyleSwitch::OFF)},
+ {"SubpixelEnabledArial",
+ mojom::FontRenderStyle::New(
+ mojom::RenderStyleSwitch::ON, mojom::RenderStyleSwitch::OFF,
+ mojom::RenderStyleSwitch::ON, 3, mojom::RenderStyleSwitch::ON,
+ mojom::RenderStyleSwitch::ON, mojom::RenderStyleSwitch::OFF)},
+ {"SubpixelDisabledArial",
+ mojom::FontRenderStyle::New(
+ mojom::RenderStyleSwitch::ON, mojom::RenderStyleSwitch::OFF,
+ mojom::RenderStyleSwitch::ON, 3, mojom::RenderStyleSwitch::ON,
+ mojom::RenderStyleSwitch::OFF, mojom::RenderStyleSwitch::OFF)}};
+
+ for (auto& family_style : families_styles) {
+ mojom::FontRenderStylePtr result_style;
+ font_loader()->FontRenderStyleForStrike(std::move(family_style.first), 16,
+ false, false, 1.0, &result_style);
+ EXPECT_TRUE(result_style.Equals(family_style.second));
+ }
+}
+
+TEST_F(FontLoaderTest, PPAPIFallback) {
+#if BUILDFLAG(ENABLE_PLUGINS)
+ std::tuple<std::string, uint32_t, std::string> family_charset_expectations[] =
+ {
+ {"", PP_PRIVATEFONTCHARSET_SHIFTJIS, "DejaVuSans"},
+ {"", PP_PRIVATEFONTCHARSET_THAI, "Garuda"},
+ {"", PP_PRIVATEFONTCHARSET_GB2312, "DejaVuSans"},
+ {"", PP_PRIVATEFONTCHARSET_GREEK, "DejaVuSans"},
+ {"Arial", PP_PRIVATEFONTCHARSET_DEFAULT, "Arimo-Regular"},
+ {"Times New Roman", PP_PRIVATEFONTCHARSET_DEFAULT, "Tinos-Regular"},
+ {"Courier New", PP_PRIVATEFONTCHARSET_DEFAULT, "Cousine-Regular"},
+ };
+ for (auto& family_charset_expectation : family_charset_expectations) {
+ base::File result_file;
+ font_loader()->MatchFontWithFallback(
+ std::move(std::get<0>(family_charset_expectation)), false, false,
+ std::move(std::get<1>(family_charset_expectation)), 0, &result_file);
+ EXPECT_TRUE(result_file.IsValid());
+ EXPECT_EQ(GetPostscriptNameFromFile(result_file),
+ std::get<2>(family_charset_expectation));
+ }
+#endif
+}
+
+} // namespace font_service
diff --git a/chromium/components/services/font/font_loader_test.h b/chromium/components/services/font/font_loader_test.h
new file mode 100644
index 00000000000..a0a4499164b
--- /dev/null
+++ b/chromium/components/services/font/font_loader_test.h
@@ -0,0 +1,38 @@
+// 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_SERVICES_FONT_FONT_LOADER_TEST_H_
+#define COMPONENTS_SERVICES_FONT_FONT_LOADER_TEST_H_
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "components/services/font/public/cpp/font_loader.h"
+#include "components/services/font/public/interfaces/font_service.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/service_manager/public/cpp/service_test.h"
+
+namespace font_service {
+
+class FontLoaderTest : public service_manager::test::ServiceTest {
+ public:
+ FontLoaderTest();
+ ~FontLoaderTest() override;
+
+ // Overridden from service_manager::test::ServiceTest:
+ void SetUp() override;
+
+ protected:
+ FontLoader* font_loader() { return font_loader_.get(); }
+
+ private:
+ std::unique_ptr<FontLoader> font_loader_;
+
+ DISALLOW_COPY_AND_ASSIGN(FontLoaderTest);
+};
+
+} // namespace font_service
+
+#endif // COMPONENTS_SERVICES_FONT_FONT_LOADER_TEST_H_
diff --git a/chromium/components/services/font/font_service_app.cc b/chromium/components/services/font/font_service_app.cc
index 63ecd060702..aae40ebce0b 100644
--- a/chromium/components/services/font/font_service_app.cc
+++ b/chromium/components/services/font/font_service_app.cc
@@ -6,10 +6,23 @@
#include <utility>
+#include "base/command_line.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
+#include "build/build_config.h"
#include "mojo/public/cpp/system/platform_handle.h"
+#include "ppapi/buildflags/buildflags.h"
#include "services/service_manager/public/cpp/service_context.h"
+#include "ui/gfx/font_fallback_linux.h"
+#include "ui/gfx/font_render_params.h"
+
+#if BUILDFLAG(ENABLE_PLUGINS)
+#include "components/services/font/ppapi_fontconfig_matching.h" // nogncheck
+#endif
+
+#if defined(OS_LINUX)
+#include "base/test/fontconfig_util_linux.h"
+#endif
static_assert(
static_cast<uint32_t>(SkFontStyle::kUpright_Slant) ==
@@ -35,13 +48,47 @@ base::File GetFileForPath(const base::FilePath& path) {
return file;
}
+int ConvertHinting(gfx::FontRenderParams::Hinting hinting) {
+ switch (hinting) {
+ case gfx::FontRenderParams::HINTING_NONE:
+ return 0;
+ case gfx::FontRenderParams::HINTING_SLIGHT:
+ return 1;
+ case gfx::FontRenderParams::HINTING_MEDIUM:
+ return 2;
+ case gfx::FontRenderParams::HINTING_FULL:
+ return 3;
+ }
+ NOTREACHED() << "Unexpected hinting value " << hinting;
+ return 0;
+}
+
+font_service::mojom::RenderStyleSwitch ConvertSubpixelRendering(
+ gfx::FontRenderParams::SubpixelRendering rendering) {
+ switch (rendering) {
+ case gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE:
+ return font_service::mojom::RenderStyleSwitch::OFF;
+ case gfx::FontRenderParams::SUBPIXEL_RENDERING_RGB:
+ case gfx::FontRenderParams::SUBPIXEL_RENDERING_BGR:
+ case gfx::FontRenderParams::SUBPIXEL_RENDERING_VRGB:
+ case gfx::FontRenderParams::SUBPIXEL_RENDERING_VBGR:
+ return font_service::mojom::RenderStyleSwitch::ON;
+ }
+ NOTREACHED() << "Unexpected subpixel rendering value " << rendering;
+ return font_service::mojom::RenderStyleSwitch::NO_PREFERENCE;
+}
+
} // namespace
namespace font_service {
+std::unique_ptr<service_manager::Service> FontServiceApp::CreateService() {
+ return std::make_unique<FontServiceApp>();
+}
+
FontServiceApp::FontServiceApp() {
registry_.AddInterface(
- base::Bind(&FontServiceApp::Create, base::Unretained(this)));
+ base::BindRepeating(&FontServiceApp::CreateSelf, base::Unretained(this)));
}
FontServiceApp::~FontServiceApp() {}
@@ -98,6 +145,7 @@ void FontServiceApp::MatchFamilyName(const std::string& family_name,
void FontServiceApp::OpenStream(uint32_t id_number,
OpenStreamCallback callback) {
+ DCHECK_LT(id_number, static_cast<uint32_t>(paths_.size()));
base::File file;
if (id_number < static_cast<uint32_t>(paths_.size())) {
file = GetFileForPath(base::FilePath(paths_[id_number].c_str()));
@@ -106,7 +154,76 @@ void FontServiceApp::OpenStream(uint32_t id_number,
std::move(callback).Run(std::move(file));
}
-void FontServiceApp::Create(mojom::FontServiceRequest request) {
+void FontServiceApp::FallbackFontForCharacter(
+ uint32_t character,
+ const std::string& locale,
+ FallbackFontForCharacterCallback callback) {
+ auto fallback_font = gfx::GetFallbackFontForChar(character, locale);
+ int index = FindOrAddPath(SkString(fallback_font.filename.data()));
+
+ mojom::FontIdentityPtr identity(mojom::FontIdentity::New());
+ identity->id = static_cast<uint32_t>(index);
+ identity->ttc_index = fallback_font.ttc_index;
+ identity->str_representation = fallback_font.filename;
+
+ std::move(callback).Run(std::move(identity), fallback_font.name,
+ fallback_font.is_bold, fallback_font.is_italic);
+}
+
+void FontServiceApp::FontRenderStyleForStrike(
+ const std::string& family,
+ uint32_t size,
+ bool is_bold,
+ bool is_italic,
+ float device_scale_factor,
+ FontRenderStyleForStrikeCallback callback) {
+ gfx::FontRenderParamsQuery query;
+
+ query.device_scale_factor = device_scale_factor;
+ query.families.push_back(family);
+ query.pixel_size = size;
+ query.style = is_italic ? gfx::Font::ITALIC : 0;
+ query.weight = is_bold ? gfx::Font::Weight::BOLD : gfx::Font::Weight::NORMAL;
+ const gfx::FontRenderParams params = gfx::GetFontRenderParams(query, nullptr);
+
+ mojom::FontRenderStylePtr font_render_style(mojom::FontRenderStyle::New(
+ params.use_bitmaps ? mojom::RenderStyleSwitch::ON
+ : mojom::RenderStyleSwitch::OFF,
+ params.autohinter ? mojom::RenderStyleSwitch::ON
+ : mojom::RenderStyleSwitch::OFF,
+ params.hinting != gfx::FontRenderParams::HINTING_NONE
+ ? mojom::RenderStyleSwitch::ON
+ : mojom::RenderStyleSwitch::OFF,
+ ConvertHinting(params.hinting),
+ params.antialiasing ? mojom::RenderStyleSwitch::ON
+ : mojom::RenderStyleSwitch::OFF,
+ ConvertSubpixelRendering(params.subpixel_rendering),
+ params.subpixel_positioning ? mojom::RenderStyleSwitch::ON
+ : mojom::RenderStyleSwitch::OFF));
+ std::move(callback).Run(std::move(font_render_style));
+}
+
+void FontServiceApp::MatchFontWithFallback(
+ const std::string& family,
+ bool is_bold,
+ bool is_italic,
+ uint32_t charset,
+ uint32_t fallbackFamilyType,
+ MatchFontWithFallbackCallback callback) {
+#if BUILDFLAG(ENABLE_PLUGINS)
+ base::File matched_font_file;
+ int font_file_descriptor = MatchFontFaceWithFallback(
+ family, is_bold, is_italic, charset, fallbackFamilyType);
+ matched_font_file = base::File(font_file_descriptor);
+ if (!matched_font_file.IsValid())
+ matched_font_file = base::File();
+ std::move(callback).Run(std::move(matched_font_file));
+#else
+ NOTREACHED();
+#endif
+}
+
+void FontServiceApp::CreateSelf(mojom::FontServiceRequest request) {
bindings_.AddBinding(this, std::move(request));
}
diff --git a/chromium/components/services/font/font_service_app.h b/chromium/components/services/font/font_service_app.h
index ce0fa7c3b49..9256a46cc51 100644
--- a/chromium/components/services/font/font_service_app.h
+++ b/chromium/components/services/font/font_service_app.h
@@ -23,6 +23,10 @@ class FontServiceApp : public service_manager::Service,
FontServiceApp();
~FontServiceApp() override;
+ static std::unique_ptr<service_manager::Service> CreateService();
+
+ void CreateSelf(mojom::FontServiceRequest request);
+
private:
// service_manager::Service:
void OnStart() override;
@@ -35,9 +39,23 @@ class FontServiceApp : public service_manager::Service,
mojom::TypefaceStylePtr requested_style,
MatchFamilyNameCallback callback) override;
void OpenStream(uint32_t id_number, OpenStreamCallback callback) override;
-
- void Create(mojom::FontServiceRequest request);
-
+ void FallbackFontForCharacter(
+ uint32_t character,
+ const std::string& locale,
+ FallbackFontForCharacterCallback callback) override;
+ void FontRenderStyleForStrike(
+ const std::string& family,
+ uint32_t size,
+ bool italic,
+ bool bold,
+ float device_scale_factor,
+ FontRenderStyleForStrikeCallback callback) override;
+ void MatchFontWithFallback(const std::string& family,
+ bool is_bold,
+ bool is_italic,
+ uint32_t charset,
+ uint32_t fallbackFamilyType,
+ MatchFontWithFallbackCallback callback) override;
int FindOrAddPath(const SkString& path);
service_manager::BinderRegistry registry_;
diff --git a/chromium/components/services/font/manifest.json b/chromium/components/services/font/manifest.json
index 434a7ff30d7..77f1006edb7 100644
--- a/chromium/components/services/font/manifest.json
+++ b/chromium/components/services/font/manifest.json
@@ -5,10 +5,10 @@
"interface_provider_specs": {
"service_manager:connector": {
"provides": {
- "app": [ "font_service.mojom.FontService" ]
+ "font_service": [ "font_service.mojom.FontService" ]
},
"requires": {
- "*": [ "app" ]
+ "service_manager": [ "service_manager:all_users" ]
}
}
}
diff --git a/chromium/components/services/font/ppapi_fontconfig_matching.cc b/chromium/components/services/font/ppapi_fontconfig_matching.cc
new file mode 100644
index 00000000000..42f40e7469c
--- /dev/null
+++ b/chromium/components/services/font/ppapi_fontconfig_matching.cc
@@ -0,0 +1,262 @@
+// 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/services/font/ppapi_fontconfig_matching.h"
+
+#include <fcntl.h>
+#include <fontconfig/fontconfig.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_util.h"
+
+#include "ppapi/c/private/pp_private_font_charset.h"
+#include "ppapi/c/trusted/ppb_browser_font_trusted.h"
+
+namespace {
+
+// MSCharSetToFontconfig translates a Microsoft charset identifier to a
+// fontconfig language set by appending to |langset|.
+// Returns true if |langset| is Latin/Greek/Cyrillic.
+bool MSCharSetToFontconfig(FcLangSet* langset, unsigned fdwCharSet) {
+ // We have need to translate raw fdwCharSet values into terms that
+ // fontconfig can understand. (See the description of fdwCharSet in the MSDN
+ // documentation for CreateFont:
+ // http://msdn.microsoft.com/en-us/library/dd183499(VS.85).aspx )
+ //
+ // Although the argument is /called/ 'charset', the actual values conflate
+ // character sets (which are sets of Unicode code points) and character
+ // encodings (which are algorithms for turning a series of bits into a
+ // series of code points.) Sometimes the values will name a language,
+ // sometimes they'll name an encoding. In the latter case I'm assuming that
+ // they mean the set of code points in the domain of that encoding.
+ //
+ // fontconfig deals with ISO 639-1 language codes:
+ // http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
+ //
+ // So, for each of the documented fdwCharSet values I've had to take a
+ // guess at the set of ISO 639-1 languages intended.
+
+ bool is_lgc = false;
+ switch (fdwCharSet) {
+ case PP_PRIVATEFONTCHARSET_ANSI:
+ // These values I don't really know what to do with, so I'm going to map
+ // them to English also.
+ case PP_PRIVATEFONTCHARSET_DEFAULT:
+ case PP_PRIVATEFONTCHARSET_MAC:
+ case PP_PRIVATEFONTCHARSET_OEM:
+ case PP_PRIVATEFONTCHARSET_SYMBOL:
+ is_lgc = true;
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("en"));
+ break;
+ case PP_PRIVATEFONTCHARSET_BALTIC:
+ // The three baltic languages.
+ is_lgc = true;
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("et"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lv"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lt"));
+ break;
+ case PP_PRIVATEFONTCHARSET_CHINESEBIG5:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh-tw"));
+ break;
+ case PP_PRIVATEFONTCHARSET_GB2312:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh-cn"));
+ break;
+ case PP_PRIVATEFONTCHARSET_EASTEUROPE:
+ // A scattering of eastern European languages.
+ is_lgc = true;
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("pl"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("cs"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("sk"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hu"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hr"));
+ break;
+ case PP_PRIVATEFONTCHARSET_GREEK:
+ is_lgc = true;
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("el"));
+ break;
+ case PP_PRIVATEFONTCHARSET_HANGUL:
+ case PP_PRIVATEFONTCHARSET_JOHAB:
+ // Korean
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ko"));
+ break;
+ case PP_PRIVATEFONTCHARSET_RUSSIAN:
+ is_lgc = true;
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ru"));
+ break;
+ case PP_PRIVATEFONTCHARSET_SHIFTJIS:
+ // Japanese
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ja"));
+ break;
+ case PP_PRIVATEFONTCHARSET_TURKISH:
+ is_lgc = true;
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("tr"));
+ break;
+ case PP_PRIVATEFONTCHARSET_VIETNAMESE:
+ is_lgc = true;
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("vi"));
+ break;
+ case PP_PRIVATEFONTCHARSET_ARABIC:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ar"));
+ break;
+ case PP_PRIVATEFONTCHARSET_HEBREW:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("he"));
+ break;
+ case PP_PRIVATEFONTCHARSET_THAI:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("th"));
+ break;
+ // default:
+ // Don't add any languages in that case that we don't recognise the
+ // constant.
+ }
+ return is_lgc;
+}
+
+} // namespace
+
+namespace font_service {
+
+int MatchFontFaceWithFallback(const std::string& face,
+ bool is_bold,
+ bool is_italic,
+ uint32_t charset,
+ uint32_t fallback_family) {
+ FcLangSet* langset = FcLangSetCreate();
+ bool is_lgc = MSCharSetToFontconfig(langset, charset);
+ FcPattern* pattern = FcPatternCreate();
+ FcPatternAddString(pattern, FC_FAMILY,
+ reinterpret_cast<const FcChar8*>(face.c_str()));
+
+ // TODO(thestig) Check if we can access Chrome's per-script font preference
+ // here and select better default fonts for non-LGC case.
+ std::string generic_font_name;
+ if (is_lgc) {
+ switch (fallback_family) {
+ case PP_BROWSERFONT_TRUSTED_FAMILY_SERIF:
+ generic_font_name = "Times New Roman";
+ break;
+ case PP_BROWSERFONT_TRUSTED_FAMILY_SANSSERIF:
+ generic_font_name = "Arial";
+ break;
+ case PP_BROWSERFONT_TRUSTED_FAMILY_MONOSPACE:
+ generic_font_name = "Courier New";
+ break;
+ }
+ }
+ if (!generic_font_name.empty()) {
+ const FcChar8* fc_generic_font_name =
+ reinterpret_cast<const FcChar8*>(generic_font_name.c_str());
+ FcPatternAddString(pattern, FC_FAMILY, fc_generic_font_name);
+ }
+
+ if (is_bold)
+ FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
+ if (is_italic)
+ FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
+ FcPatternAddLangSet(pattern, FC_LANG, langset);
+ FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
+ FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
+ FcDefaultSubstitute(pattern);
+
+ FcResult result;
+ FcFontSet* font_set = FcFontSort(nullptr, pattern, 0, nullptr, &result);
+ int font_fd = -1;
+ int good_enough_index = -1;
+ bool good_enough_index_set = false;
+
+ if (font_set) {
+ for (int i = 0; i < font_set->nfont; ++i) {
+ FcPattern* current = font_set->fonts[i];
+
+ // Older versions of fontconfig have a bug where they cannot select
+ // only scalable fonts so we have to manually filter the results.
+ FcBool is_scalable;
+ if (FcPatternGetBool(current, FC_SCALABLE, 0, &is_scalable) !=
+ FcResultMatch ||
+ !is_scalable) {
+ continue;
+ }
+
+ FcChar8* c_filename;
+ if (FcPatternGetString(current, FC_FILE, 0, &c_filename) !=
+ FcResultMatch) {
+ continue;
+ }
+
+ // We only want to return sfnt (TrueType) based fonts. We don't have a
+ // very good way of detecting this so we'll filter based on the
+ // filename.
+ bool is_sfnt = false;
+ static const char kSFNTExtensions[][5] = {".ttf", ".otc", ".TTF", ".ttc",
+ ""};
+ for (size_t j = 0;; j++) {
+ if (kSFNTExtensions[j][0] == 0) {
+ // None of the extensions matched.
+ break;
+ }
+ if (base::EndsWith(std::string(reinterpret_cast<char*>(c_filename)),
+ kSFNTExtensions[j], base::CompareCase::SENSITIVE)) {
+ is_sfnt = true;
+ break;
+ }
+ }
+
+ if (!is_sfnt)
+ continue;
+
+ // This font is good enough to pass muster, but we might be able to do
+ // better with subsequent ones.
+ if (!good_enough_index_set) {
+ good_enough_index = i;
+ good_enough_index_set = true;
+ }
+
+ FcValue matrix;
+ bool have_matrix = FcPatternGet(current, FC_MATRIX, 0, &matrix) == 0;
+
+ if (is_italic && have_matrix) {
+ // we asked for an italic font, but fontconfig is giving us a
+ // non-italic font with a transformation matrix.
+ continue;
+ }
+
+ FcValue embolden;
+ const bool have_embolden =
+ FcPatternGet(current, FC_EMBOLDEN, 0, &embolden) == 0;
+
+ if (is_bold && have_embolden) {
+ // we asked for a bold font, but fontconfig gave us a non-bold font
+ // and asked us to apply fake bolding.
+ continue;
+ }
+
+ font_fd =
+ HANDLE_EINTR(open(reinterpret_cast<char*>(c_filename), O_RDONLY));
+ if (font_fd >= 0)
+ break;
+ }
+ }
+
+ if (font_fd == -1 && good_enough_index_set) {
+ // We didn't find a font that we liked, so we fallback to something
+ // acceptable.
+ FcPattern* current = font_set->fonts[good_enough_index];
+ FcChar8* c_filename;
+ FcPatternGetString(current, FC_FILE, 0, &c_filename);
+ font_fd = HANDLE_EINTR(open(reinterpret_cast<char*>(c_filename), O_RDONLY));
+ }
+
+ if (font_set)
+ FcFontSetDestroy(font_set);
+ FcPatternDestroy(pattern);
+
+ return font_fd;
+}
+
+} // namespace font_service
diff --git a/chromium/components/services/font/ppapi_fontconfig_matching.h b/chromium/components/services/font/ppapi_fontconfig_matching.h
new file mode 100644
index 00000000000..0eedf196ffe
--- /dev/null
+++ b/chromium/components/services/font/ppapi_fontconfig_matching.h
@@ -0,0 +1,29 @@
+// 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_SERVICES_FONT_PPAPI_FONTCONFIG_MATCHING_H_
+#define COMPONENTS_SERVICES_FONT_PPAPI_FONTCONFIG_MATCHING_H_
+
+#include <stdint.h>
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "ppapi/buildflags/buildflags.h"
+
+#include <string>
+
+#if !BUILDFLAG(ENABLE_PLUGINS)
+#error "Plugins should be enabled when including " ## __FILE__
+#endif
+
+namespace font_service {
+
+int MatchFontFaceWithFallback(const std::string& face,
+ bool is_bold,
+ bool is_italic,
+ uint32_t charset,
+ uint32_t fallback_family);
+
+} // namespace font_service
+
+#endif // COMPONENTS_SERVICES_FONT_PPAPI_FONTCONFIG_MATCHING_H_
diff --git a/chromium/components/services/font/public/cpp/font_loader.cc b/chromium/components/services/font/public/cpp/font_loader.cc
index 618c22f73a8..4974393ebb4 100644
--- a/chromium/components/services/font/public/cpp/font_loader.cc
+++ b/chromium/components/services/font/public/cpp/font_loader.cc
@@ -9,13 +9,14 @@
#include "base/bind.h"
#include "base/trace_event/trace_event.h"
#include "components/services/font/public/cpp/font_service_thread.h"
+#include "components/services/font/public/interfaces/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
namespace font_service {
FontLoader::FontLoader(service_manager::Connector* connector) {
mojom::FontServicePtr font_service;
- connector->BindInterface("font_service", &font_service);
+ connector->BindInterface(font_service::mojom::kServiceName, &font_service);
thread_ = new internal::FontServiceThread(std::move(font_service));
}
@@ -66,6 +67,41 @@ SkStreamAsset* FontLoader::openStream(const FontIdentity& identity) {
}
}
+// Additional cross-thread accessible methods.
+bool FontLoader::FallbackFontForCharacter(
+ uint32_t character,
+ std::string locale,
+ mojom::FontIdentityPtr* out_font_identity,
+ std::string* out_family_name,
+ bool* out_is_bold,
+ bool* out_is_italic) {
+ return thread_->FallbackFontForCharacter(character, std::move(locale),
+ out_font_identity, out_family_name,
+ out_is_bold, out_is_italic);
+}
+
+bool FontLoader::FontRenderStyleForStrike(
+ std::string family,
+ uint32_t size,
+ bool is_italic,
+ bool is_bold,
+ float device_scale_factor,
+ mojom::FontRenderStylePtr* out_font_render_style) {
+ return thread_->FontRenderStyleForStrike(std::move(family), size, is_italic,
+ is_bold, device_scale_factor,
+ out_font_render_style);
+}
+
+void FontLoader::MatchFontWithFallback(std::string family,
+ bool is_bold,
+ bool is_italic,
+ uint32_t charset,
+ uint32_t fallback_family_type,
+ base::File* out_font_file_handle) {
+ thread_->MatchFontWithFallback(std::move(family), is_bold, is_italic, charset,
+ fallback_family_type, out_font_file_handle);
+}
+
void FontLoader::OnMappedFontFileDestroyed(internal::MappedFontFile* f) {
TRACE_EVENT1("font_loader", "FontLoader::OnMappedFontFileDestroyed",
"identity", f->font_id());
diff --git a/chromium/components/services/font/public/cpp/font_loader.h b/chromium/components/services/font/public/cpp/font_loader.h
index 7f9d7d22e00..4eebdeac1f9 100644
--- a/chromium/components/services/font/public/cpp/font_loader.h
+++ b/chromium/components/services/font/public/cpp/font_loader.h
@@ -49,6 +49,35 @@ class FontLoader : public SkFontConfigInterface,
SkFontStyle* out_style) override;
SkStreamAsset* openStream(const FontIdentity& identity) override;
+ // Additional cross-thread accessible methods below.
+
+ // Out parameters are only guaranteed to be initialized when method returns
+ // true.
+ bool FallbackFontForCharacter(uint32_t character,
+ std::string locale,
+ mojom::FontIdentityPtr* out_identity,
+ std::string* out_family_name,
+ bool* out_is_bold,
+ bool* out_is_italic);
+ // Out parameters are only guaranteed to be initialized when method returns
+ // true.
+ bool FontRenderStyleForStrike(
+ std::string family,
+ uint32_t size,
+ bool is_italic,
+ bool is_bold,
+ float device_scale_factor,
+ mojom::FontRenderStylePtr* out_font_render_style);
+ // Out parameter out_font_file_handle should always be an opened file handle
+ // to a matched or default font file. out_font_file_handle is a default
+ // initialized base::File on error.
+ void MatchFontWithFallback(std::string family,
+ bool is_bold,
+ bool is_italic,
+ uint32_t charset,
+ uint32_t fallbackFamilyType,
+ base::File* out_font_file_handle);
+
private:
// internal::MappedFontFile::Observer:
void OnMappedFontFileDestroyed(internal::MappedFontFile* f) override;
diff --git a/chromium/components/services/font/public/cpp/font_service_thread.cc b/chromium/components/services/font/public/cpp/font_service_thread.cc
index d1ecf77b032..b4f3aa45c34 100644
--- a/chromium/components/services/font/public/cpp/font_service_thread.cc
+++ b/chromium/components/services/font/public/cpp/font_service_thread.cc
@@ -22,6 +22,7 @@ FontServiceThread::FontServiceThread(mojom::FontServicePtr font_service)
: base::Thread(kFontThreadName),
font_service_info_(font_service.PassInterface()),
weak_factory_(this) {
+ DETACH_FROM_THREAD(thread_checker_);
Start();
}
@@ -36,32 +37,85 @@ bool FontServiceThread::MatchFamilyName(
bool out_valid = false;
// This proxies to the other thread, which proxies to mojo. Only on the reply
// from mojo do we return from this.
- base::WaitableEvent done_event(
- base::WaitableEvent::ResetPolicy::AUTOMATIC,
- base::WaitableEvent::InitialState::NOT_SIGNALED);
+ base::WaitableEvent done_event;
task_runner()->PostTask(
FROM_HERE,
- base::Bind(&FontServiceThread::MatchFamilyNameImpl, this, &done_event,
- family_name, requested_style, &out_valid, out_font_identity,
- out_family_name, out_style));
+ base::BindOnce(&FontServiceThread::MatchFamilyNameImpl, this, &done_event,
+ family_name, requested_style, &out_valid,
+ out_font_identity, out_family_name, out_style));
done_event.Wait();
return out_valid;
}
+bool FontServiceThread::FallbackFontForCharacter(
+ uint32_t character,
+ std::string locale,
+ font_service::mojom::FontIdentityPtr* out_font_identity,
+ std::string* out_family_name,
+ bool* out_is_bold,
+ bool* out_is_italic) {
+ DCHECK_NE(GetThreadId(), base::PlatformThread::CurrentId());
+ bool out_valid = false;
+ base::WaitableEvent done_event;
+ task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&FontServiceThread::FallbackFontForCharacterImpl, this,
+ &done_event, character, std::move(locale), &out_valid,
+ out_font_identity, out_family_name, out_is_bold,
+ out_is_italic));
+ done_event.Wait();
+
+ return out_valid;
+}
+
+bool FontServiceThread::FontRenderStyleForStrike(
+ std::string family,
+ uint32_t size,
+ bool is_italic,
+ bool is_bold,
+ float device_scale_factor,
+ font_service::mojom::FontRenderStylePtr* out_font_render_style) {
+ DCHECK_NE(GetThreadId(), base::PlatformThread::CurrentId());
+ bool out_valid = false;
+ base::WaitableEvent done_event;
+ task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&FontServiceThread::FontRenderStyleForStrikeImpl, this,
+ &done_event, family, size, is_italic, is_bold,
+ device_scale_factor, &out_valid, out_font_render_style));
+ done_event.Wait();
+ return out_valid;
+}
+
+void FontServiceThread::MatchFontWithFallback(
+ std::string family,
+ bool is_bold,
+ bool is_italic,
+ uint32_t charset,
+ uint32_t fallback_family_type,
+ base::File* out_font_file_handle) {
+ DCHECK_NE(GetThreadId(), base::PlatformThread::CurrentId());
+ base::WaitableEvent done_event;
+ task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&FontServiceThread::MatchFontWithFallbackImpl, this,
+ &done_event, std::move(family), is_bold, is_italic,
+ charset, fallback_family_type, out_font_file_handle));
+ done_event.Wait();
+}
+
scoped_refptr<MappedFontFile> FontServiceThread::OpenStream(
const SkFontConfigInterface::FontIdentity& identity) {
DCHECK_NE(GetThreadId(), base::PlatformThread::CurrentId());
base::File stream_file;
- // This proxies to the other thread, which proxies to mojo. Only on the reply
- // from mojo do we return from this.
- base::WaitableEvent done_event(
- base::WaitableEvent::ResetPolicy::AUTOMATIC,
- base::WaitableEvent::InitialState::NOT_SIGNALED);
- task_runner()->PostTask(FROM_HERE,
- base::Bind(&FontServiceThread::OpenStreamImpl, this,
- &done_event, &stream_file, identity.fID));
+ // This proxies to the other thread, which proxies to mojo. Only on the
+ // reply from mojo do we return from this.
+ base::WaitableEvent done_event;
+ task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&FontServiceThread::OpenStreamImpl, this,
+ &done_event, &stream_file, identity.fID));
done_event.Wait();
if (!stream_file.IsValid()) {
@@ -79,6 +133,7 @@ scoped_refptr<MappedFontFile> FontServiceThread::OpenStream(
}
FontServiceThread::~FontServiceThread() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
Stop();
}
@@ -90,7 +145,7 @@ void FontServiceThread::MatchFamilyNameImpl(
SkFontConfigInterface::FontIdentity* out_font_identity,
SkString* out_family_name,
SkFontStyle* out_style) {
- DCHECK_EQ(GetThreadId(), base::PlatformThread::CurrentId());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (font_service_.encountered_error()) {
*out_valid = false;
@@ -103,12 +158,14 @@ void FontServiceThread::MatchFamilyNameImpl(
style->width = requested_style.width();
style->slant = static_cast<mojom::TypefaceSlant>(requested_style.slant());
+ const char* arg_family = family_name ? family_name : "";
+
pending_waitable_events_.insert(done_event);
font_service_->MatchFamilyName(
- family_name, std::move(style),
- base::Bind(&FontServiceThread::OnMatchFamilyNameComplete, this,
- done_event, out_valid, out_font_identity, out_family_name,
- out_style));
+ arg_family, std::move(style),
+ base::BindOnce(&FontServiceThread::OnMatchFamilyNameComplete, this,
+ done_event, out_valid, out_font_identity, out_family_name,
+ out_style));
}
void FontServiceThread::OnMatchFamilyNameComplete(
@@ -120,7 +177,7 @@ void FontServiceThread::OnMatchFamilyNameComplete(
mojom::FontIdentityPtr font_identity,
const std::string& family_name,
mojom::TypefaceStylePtr style) {
- DCHECK_EQ(GetThreadId(), base::PlatformThread::CurrentId());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
pending_waitable_events_.erase(done_event);
*out_valid = !font_identity.is_null();
@@ -142,7 +199,7 @@ void FontServiceThread::OnMatchFamilyNameComplete(
void FontServiceThread::OpenStreamImpl(base::WaitableEvent* done_event,
base::File* output_file,
const uint32_t id_number) {
- DCHECK_EQ(GetThreadId(), base::PlatformThread::CurrentId());
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (font_service_.encountered_error()) {
done_event->Signal();
return;
@@ -150,18 +207,139 @@ void FontServiceThread::OpenStreamImpl(base::WaitableEvent* done_event,
pending_waitable_events_.insert(done_event);
font_service_->OpenStream(
- id_number, base::Bind(&FontServiceThread::OnOpenStreamComplete, this,
- done_event, output_file));
+ id_number, base::BindOnce(&FontServiceThread::OnOpenStreamComplete, this,
+ done_event, output_file));
}
void FontServiceThread::OnOpenStreamComplete(base::WaitableEvent* done_event,
base::File* output_file,
base::File file) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
pending_waitable_events_.erase(done_event);
*output_file = std::move(file);
done_event->Signal();
}
+void FontServiceThread::FallbackFontForCharacterImpl(
+ base::WaitableEvent* done_event,
+ uint32_t character,
+ std::string locale,
+ bool* out_valid,
+ font_service::mojom::FontIdentityPtr* out_font_identity,
+ std::string* out_family_name,
+ bool* out_is_bold,
+ bool* out_is_italic) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (font_service_.encountered_error()) {
+ *out_valid = false;
+ done_event->Signal();
+ return;
+ }
+
+ pending_waitable_events_.insert(done_event);
+ font_service_->FallbackFontForCharacter(
+ character, std::move(locale),
+ base::BindOnce(&FontServiceThread::OnFallbackFontForCharacterComplete,
+ this, done_event, out_valid, out_font_identity,
+ out_family_name, out_is_bold, out_is_italic));
+}
+
+void FontServiceThread::OnFallbackFontForCharacterComplete(
+ base::WaitableEvent* done_event,
+ bool* out_valid,
+ font_service::mojom::FontIdentityPtr* out_font_identity,
+ std::string* out_family_name,
+ bool* out_is_bold,
+ bool* out_is_italic,
+ mojom::FontIdentityPtr font_identity,
+ const std::string& family_name,
+ bool is_bold,
+ bool is_italic) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ pending_waitable_events_.erase(done_event);
+
+ *out_valid = !font_identity.is_null();
+ if (font_identity) {
+ *out_font_identity = std::move(font_identity);
+ *out_family_name = family_name.data();
+ *out_is_bold = is_bold;
+ *out_is_italic = is_italic;
+ }
+ done_event->Signal();
+}
+
+void FontServiceThread::FontRenderStyleForStrikeImpl(
+ base::WaitableEvent* done_event,
+ std::string family,
+ uint32_t size,
+ bool is_italic,
+ bool is_bold,
+ float device_scale_factor,
+ bool* out_valid,
+ mojom::FontRenderStylePtr* out_font_render_style) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (font_service_.encountered_error()) {
+ *out_valid = false;
+ done_event->Signal();
+ return;
+ }
+
+ pending_waitable_events_.insert(done_event);
+ font_service_->FontRenderStyleForStrike(
+ std::move(family), size, is_italic, is_bold, device_scale_factor,
+ base::BindOnce(&FontServiceThread::OnFontRenderStyleForStrikeComplete,
+ this, done_event, out_valid, out_font_render_style));
+}
+
+void FontServiceThread::OnFontRenderStyleForStrikeComplete(
+ base::WaitableEvent* done_event,
+ bool* out_valid,
+ mojom::FontRenderStylePtr* out_font_render_style,
+ mojom::FontRenderStylePtr font_render_style) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ pending_waitable_events_.erase(done_event);
+
+ *out_valid = !font_render_style.is_null();
+ if (font_render_style) {
+ *out_font_render_style = std::move(font_render_style);
+ }
+ done_event->Signal();
+}
+
+void FontServiceThread::MatchFontWithFallbackImpl(
+ base::WaitableEvent* done_event,
+ std::string family,
+ bool is_bold,
+ bool is_italic,
+ uint32_t charset,
+ uint32_t fallback_family_type,
+ base::File* out_font_file_handle) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ *out_font_file_handle = base::File();
+ if (font_service_.encountered_error()) {
+ done_event->Signal();
+ return;
+ }
+ pending_waitable_events_.insert(done_event);
+ font_service_->MatchFontWithFallback(
+ family, is_bold, is_italic, charset, fallback_family_type,
+ base::BindOnce(&FontServiceThread::OnMatchFontWithFallbackComplete, this,
+ done_event, out_font_file_handle));
+}
+
+void FontServiceThread::OnMatchFontWithFallbackComplete(
+ base::WaitableEvent* done_event,
+ base::File* out_font_file_handle,
+ base::File file) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ pending_waitable_events_.erase(done_event);
+
+ *out_font_file_handle = std::move(file);
+ done_event->Signal();
+}
+
void FontServiceThread::OnFontServiceConnectionError() {
std::set<base::WaitableEvent*> events;
events.swap(pending_waitable_events_);
@@ -172,8 +350,8 @@ void FontServiceThread::OnFontServiceConnectionError() {
void FontServiceThread::Init() {
font_service_.Bind(std::move(font_service_info_));
font_service_.set_connection_error_handler(
- base::Bind(&FontServiceThread::OnFontServiceConnectionError,
- weak_factory_.GetWeakPtr()));
+ base::BindOnce(&FontServiceThread::OnFontServiceConnectionError,
+ weak_factory_.GetWeakPtr()));
}
void FontServiceThread::CleanUp() {
diff --git a/chromium/components/services/font/public/cpp/font_service_thread.h b/chromium/components/services/font/public/cpp/font_service_thread.h
index 2069a290492..865c587b398 100644
--- a/chromium/components/services/font/public/cpp/font_service_thread.h
+++ b/chromium/components/services/font/public/cpp/font_service_thread.h
@@ -13,6 +13,7 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread.h"
+#include "base/threading/thread_checker.h"
#include "components/services/font/public/interfaces/font_service.mojom.h"
#include "third_party/skia/include/core/SkStream.h"
#include "third_party/skia/include/core/SkTypeface.h"
@@ -45,6 +46,27 @@ class FontServiceThread : public base::Thread,
scoped_refptr<MappedFontFile> OpenStream(
const SkFontConfigInterface::FontIdentity& identity);
+ bool FallbackFontForCharacter(
+ uint32_t character,
+ std::string locale,
+ font_service::mojom::FontIdentityPtr* out_font_identity,
+ std::string* out_family_name,
+ bool* out_is_bold,
+ bool* out_is_italic);
+ bool FontRenderStyleForStrike(
+ std::string family,
+ uint32_t size,
+ bool is_italic,
+ bool is_bold,
+ float device_scale_factor,
+ font_service::mojom::FontRenderStylePtr* out_font_render_style);
+ void MatchFontWithFallback(std::string family,
+ bool is_bold,
+ bool is_italic,
+ uint32_t charset,
+ uint32_t fallbackFamilyType,
+ base::File* out_font_file_handle);
+
private:
friend class base::RefCountedThreadSafe<FontServiceThread>;
~FontServiceThread() override;
@@ -82,6 +104,53 @@ class FontServiceThread : public base::Thread,
base::File* output_file,
base::File file);
+ void FallbackFontForCharacterImpl(
+ base::WaitableEvent* done_event,
+ uint32_t character,
+ std::string locale,
+ bool* out_is_valid,
+ font_service::mojom::FontIdentityPtr* out_font_identity,
+ std::string* out_family_name,
+ bool* out_is_bold,
+ bool* out_is_italic);
+ void OnFallbackFontForCharacterComplete(
+ base::WaitableEvent* done_event,
+ bool* out_valid,
+ font_service::mojom::FontIdentityPtr* out_font_identity,
+ std::string* out_family_name,
+ bool* out_is_bold,
+ bool* out_is_italic,
+ mojom::FontIdentityPtr font_identity,
+ const std::string& family_name,
+ bool is_bold,
+ bool is_italic);
+
+ void FontRenderStyleForStrikeImpl(
+ base::WaitableEvent* done_event,
+ std::string family,
+ uint32_t size,
+ bool is_italic,
+ bool is_bold,
+ float device_scale_factor,
+ bool* out_valid,
+ mojom::FontRenderStylePtr* out_font_render_style);
+ void OnFontRenderStyleForStrikeComplete(
+ base::WaitableEvent* done_event,
+ bool* out_valid,
+ mojom::FontRenderStylePtr* out_font_render_style,
+ mojom::FontRenderStylePtr font_render_style);
+
+ void MatchFontWithFallbackImpl(base::WaitableEvent* done_event,
+ std::string family,
+ bool is_bold,
+ bool is_italic,
+ uint32_t charset,
+ uint32_t fallbackFamilyType,
+ base::File* out_font_file_handle);
+ void OnMatchFontWithFallbackComplete(base::WaitableEvent* done_event,
+ base::File* out_font_file_handle,
+ base::File file);
+
// Connection to |font_service_| has gone away. Called on the background
// thread.
void OnFontServiceConnectionError();
@@ -98,14 +167,17 @@ class FontServiceThread : public base::Thread,
// non-thread bound, and binds it to the newly created thread.
mojo::InterfacePtr<mojom::FontService> font_service_;
- // All WaitableEvents supplied to OpenStreamImpl() are added here while
- // waiting on the response from the |font_service_| (FontService::OpenStream()
- // was called, but the callback has not been processed yet). If
- // |font_service_| gets an error during this time all events in
- // |pending_waitable_events_| are signaled. This is necessary as when the
- // pipe is closed the callbacks are never received.
+ // All WaitableEvents supplied to OpenStreamImpl() and the other *Impl()
+ // functions are added here while waiting on the response from the
+ // |font_service_| (FontService::OpenStream() or other such functions were
+ // called, but the callbacks have not been processed yet). If |font_service_|
+ // gets an error during this time all events in |pending_waitable_events_| are
+ // signaled. This is necessary as when the pipe is closed the callbacks are
+ // never received.
std::set<base::WaitableEvent*> pending_waitable_events_;
+ THREAD_CHECKER(thread_checker_);
+
base::WeakPtrFactory<FontServiceThread> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FontServiceThread);
diff --git a/chromium/components/services/font/public/interfaces/font_service.mojom b/chromium/components/services/font/public/interfaces/font_service.mojom
index 6359889efd3..346499fe414 100644
--- a/chromium/components/services/font/public/interfaces/font_service.mojom
+++ b/chromium/components/services/font/public/interfaces/font_service.mojom
@@ -30,7 +30,26 @@ struct FontIdentity {
string str_representation;
};
-// Loads and resolves fonts.
+enum RenderStyleSwitch {
+ OFF = 0,
+ ON = 1,
+ NO_PREFERENCE = 2,
+};
+
+struct FontRenderStyle {
+ RenderStyleSwitch use_bitmaps; // use embedded bitmap strike if possible
+ RenderStyleSwitch use_autohint; // use 'auto' hinting (FreeType specific)
+ RenderStyleSwitch use_hinting; // hint glyphs to the pixel grid
+ uint8 hint_style; // level of hinting, 0..3
+ RenderStyleSwitch use_antialias; // antialias glyph shapes
+ // use subpixel rendering (partially-filled pixels)
+ RenderStyleSwitch use_subpixel_rendering;
+ // use subpixel positioning (fractional X positions for glyphs)
+ RenderStyleSwitch use_subpixel_positioning;
+};
+
+// Loads and resolves fonts & returns additional information accessible from
+// FontConfig only outside the sandbox.
//
// We still need to load fonts from within a sandboxed process. We set
// up a service to match fonts and load them. This service needs full
@@ -45,4 +64,27 @@ interface FontService {
// Returns a handle to the raw font specified by |id_number|.
OpenStream(uint32 id_number) => (mojo_base.mojom.File? font_handle);
+
+ // Returns a fallback FontIdentity and Typeface style for the given character
+ // and locale. If no fallback font can be found, returns a null identity.
+ FallbackFontForCharacter(uint32 character, string locale) =>
+ (FontIdentity? identity,
+ string family_name,
+ bool is_bold,
+ bool is_italic);
+
+ // Fill out the given WebFontRenderStyle with the user's preferences for
+ // rendering the given font at the given size (in pixels), given weight
+ // (is_bold or not), and given slant (is_italic or not).
+ FontRenderStyleForStrike(string family,
+ uint32 size,
+ bool is_italic,
+ bool is_bold,
+ float device_scale_factor) =>
+ (FontRenderStyle? font_render_style);
+
+ // PPAPI Specific font call to match a font family and charset.
+ MatchFontWithFallback(string family, bool is_bold, bool is_italic,
+ uint32 charset, uint32 fallback_family_type) =>
+ (mojo_base.mojom.File? font_file_handle);
};
diff --git a/chromium/components/services/font/test_manifest.json b/chromium/components/services/font/test_manifest.json
new file mode 100644
index 00000000000..a9689872f50
--- /dev/null
+++ b/chromium/components/services/font/test_manifest.json
@@ -0,0 +1,11 @@
+{
+ "name": "font_service_unittests",
+ "display_name": "Font Service Unittests",
+ "interface_provider_specs": {
+ "service_manager:connector": {
+ "requires": {
+ "font_service": [ "font_service" ]
+ }
+ }
+ }
+}
diff --git a/chromium/components/services/heap_profiling/BUILD.gn b/chromium/components/services/heap_profiling/BUILD.gn
index 0b98f99baed..96bfb9c24d0 100644
--- a/chromium/components/services/heap_profiling/BUILD.gn
+++ b/chromium/components/services/heap_profiling/BUILD.gn
@@ -36,7 +36,6 @@ static_library("heap_profiling") {
deps = [
"//base",
"//components/services/heap_profiling/public/cpp",
- "//mojo/edk",
"//services/resource_coordinator/public/cpp:resource_coordinator_cpp",
"//third_party/zlib",
]
diff --git a/chromium/components/services/heap_profiling/DEPS b/chromium/components/services/heap_profiling/DEPS
index 6973709b170..6abb9cad06d 100644
--- a/chromium/components/services/heap_profiling/DEPS
+++ b/chromium/components/services/heap_profiling/DEPS
@@ -1,6 +1,5 @@
include_rules = [
"+components/services/heap_profiling/public",
- "+mojo/edk/embedder",
"+services/resource_coordinator/public",
"+third_party/zlib/zlib.h",
]
diff --git a/chromium/components/services/heap_profiling/connection_manager.cc b/chromium/components/services/heap_profiling/connection_manager.cc
index 5f0797c7f2f..265db1b92f8 100644
--- a/chromium/components/services/heap_profiling/connection_manager.cc
+++ b/chromium/components/services/heap_profiling/connection_manager.cc
@@ -147,12 +147,8 @@ void ConnectionManager::OnNewConnection(base::ProcessId pid,
// when the user is attempting to manually start profiling for processes, so
// we ignore this edge case.
- base::PlatformFile receiver_handle;
- CHECK_EQ(MOJO_RESULT_OK, mojo::UnwrapPlatformFile(
- std::move(receiver_pipe_end), &receiver_handle));
- scoped_refptr<ReceiverPipe> new_pipe =
- new ReceiverPipe(mojo::edk::ScopedInternalPlatformHandle(
- mojo::edk::InternalPlatformHandle(receiver_handle)));
+ scoped_refptr<ReceiverPipe> new_pipe = new ReceiverPipe(
+ mojo::UnwrapPlatformHandle(std::move(receiver_pipe_end)));
// The allocation tracker will call this on a background thread, so thunk
// back to the current thread with weak pointers.
diff --git a/chromium/components/services/heap_profiling/connection_manager.h b/chromium/components/services/heap_profiling/connection_manager.h
index c2c74dfd71b..97dd66dbb6d 100644
--- a/chromium/components/services/heap_profiling/connection_manager.h
+++ b/chromium/components/services/heap_profiling/connection_manager.h
@@ -21,7 +21,6 @@
#include "components/services/heap_profiling/allocation_tracker.h"
#include "components/services/heap_profiling/backtrace_storage.h"
#include "components/services/heap_profiling/public/mojom/heap_profiling_service.mojom.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
#include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
namespace base {
diff --git a/chromium/components/services/heap_profiling/json_exporter_unittest.cc b/chromium/components/services/heap_profiling/json_exporter_unittest.cc
index 6347b864bf5..df61b5f3329 100644
--- a/chromium/components/services/heap_profiling/json_exporter_unittest.cc
+++ b/chromium/components/services/heap_profiling/json_exporter_unittest.cc
@@ -30,6 +30,7 @@ using MemoryMap = std::vector<memory_instrumentation::mojom::VmRegionPtr>;
static constexpr int kNoParent = -1;
+#if !defined(ADDRESS_SANITIZER)
// Finds the first vm region in the given periodic interval. Returns null on
// failure.
const base::Value* FindFirstRegionWithAnyName(const base::Value* root) {
@@ -52,6 +53,7 @@ const base::Value* FindFirstRegionWithAnyName(const base::Value* root) {
}
return nullptr;
}
+#endif // !defined(ADDRESS_SANITIZER)
// Looks up a given string id from the string table. Returns -1 if not found.
int GetIdFromStringTable(const base::Value* strings, const char* text) {
@@ -440,6 +442,10 @@ TEST(ProfilingJsonExporterTest, SimpleWithFilteredAllocations) {
EXPECT_NE(-1, node_bt3);
}
+// GetProcessMemoryMaps iterates through every memory region, making allocations
+// for each one. ASAN will potentially, for each allocation, make memory
+// regions. This will cause the test to time out.
+#if !defined(ADDRESS_SANITIZER)
TEST(ProfilingJsonExporterTest, MemoryMaps) {
AllocationEventSet events;
ExportParams params;
@@ -477,6 +483,7 @@ TEST(ProfilingJsonExporterTest, MemoryMaps) {
EXPECT_NE(size->GetString(), "");
EXPECT_NE(size->GetString(), "0");
}
+#endif // !defined(ADDRESS_SANITIZER)
TEST(ProfilingJsonExporterTest, Context) {
BacktraceStorage backtrace_storage;
diff --git a/chromium/components/services/heap_profiling/public/cpp/BUILD.gn b/chromium/components/services/heap_profiling/public/cpp/BUILD.gn
index 6c826fd2e81..acaf0c29b6f 100644
--- a/chromium/components/services/heap_profiling/public/cpp/BUILD.gn
+++ b/chromium/components/services/heap_profiling/public/cpp/BUILD.gn
@@ -26,7 +26,6 @@ static_library("cpp") {
"//base",
"//base:debugging_buildflags",
"//base/allocator:buildflags",
- "//mojo/edk",
"//services/resource_coordinator/public/mojom:",
"//services/service_manager/public/cpp",
]
@@ -47,7 +46,6 @@ source_set("unit_tests") {
"//base",
"//base/allocator:buildflags",
"//base/test:test_support",
- "//mojo/edk",
"//testing/gtest",
]
}
diff --git a/chromium/components/services/heap_profiling/public/cpp/DEPS b/chromium/components/services/heap_profiling/public/cpp/DEPS
index 80d54b4be0f..0ba279aecbd 100644
--- a/chromium/components/services/heap_profiling/public/cpp/DEPS
+++ b/chromium/components/services/heap_profiling/public/cpp/DEPS
@@ -1,4 +1,3 @@
include_rules = [
- "+mojo/edk/embedder",
"+components/services/heap_profiling/public",
]
diff --git a/chromium/components/services/heap_profiling/public/cpp/allocator_shim.cc b/chromium/components/services/heap_profiling/public/cpp/allocator_shim.cc
index 729d2119b41..ee956609b4a 100644
--- a/chromium/components/services/heap_profiling/public/cpp/allocator_shim.cc
+++ b/chromium/components/services/heap_profiling/public/cpp/allocator_shim.cc
@@ -27,6 +27,11 @@
#if defined(OS_POSIX)
#include <limits.h>
+#include <pthread.h>
+#endif
+
+#if defined(OS_WIN)
+#include <windows.h>
#endif
#if defined(OS_LINUX) || defined(OS_ANDROID)
@@ -44,7 +49,57 @@ using CaptureMode = base::trace_event::AllocationContextTracker::CaptureMode;
namespace heap_profiling {
-// A ScopedAllowLogging instance must be instantiated in the scope of all hooks.
+namespace {
+
+// The base implementation of TLS will leak memory if accessed during late
+// stages of thread destruction. We roll our own implementation of TLS to
+// prevent reentrancy. Since this only requires storing a single bit of
+// information, we don't need to deal with hooking thread destruction to free
+// memory, and thus avoid leaks and other issues.
+#if defined(OS_WIN)
+using TLSKey = DWORD;
+#else
+using TLSKey = pthread_key_t;
+#endif
+
+// Holds a key to a TLS value. The TLS value (0 or 1) indicates whether the
+// allocator shim is already being used on the current thread.
+TLSKey g_prevent_reentrancy_key = 0;
+
+void InitializeReentrancyKey() {
+#if defined(OS_WIN)
+ g_prevent_reentrancy_key = TlsAlloc();
+ DCHECK_NE(TLS_OUT_OF_INDEXES, g_prevent_reentrancy_key);
+#else
+ // Returns |0| on success.
+ int result = pthread_key_create(&g_prevent_reentrancy_key, nullptr);
+ DCHECK(!result);
+#endif
+}
+
+bool CanEnterAllocatorShim() {
+#if defined(OS_WIN)
+ return !TlsGetValue(g_prevent_reentrancy_key);
+#else
+ return !pthread_getspecific(g_prevent_reentrancy_key);
+#endif
+}
+
+void SetEnteringAllocatorShim(bool entering) {
+ void* value = entering ? reinterpret_cast<void*>(1) : nullptr;
+#if defined(OS_WIN)
+ BOOL ret = TlsSetValue(g_prevent_reentrancy_key, value);
+ DPCHECK(ret);
+#else
+ int ret = pthread_setspecific(g_prevent_reentrancy_key, value);
+ DCHECK_EQ(ret, 0);
+#endif
+}
+
+} // namespace
+
+// A ScopedAllow{Free,Alloc} instance must be instantiated in the scope of all
+// hooks.
// AllocatorShimLogAlloc/AllocatorShimLogFree must only be called if it
// evaluates to true.
//
@@ -62,28 +117,42 @@ namespace heap_profiling {
// The implementation of libmalloc will sometimes call malloc [from
// one zone to another] - without this guard, the allocation would get two
// chances of being sampled.
-class ScopedAllowLogging {
+class ScopedAllowFree {
public:
- ScopedAllowLogging()
- : allowed_(LIKELY(!base::ThreadLocalStorage::HasBeenDestroyed()) &&
- LIKELY(!prevent_reentrancy_.Pointer()->Get())) {
+ ScopedAllowFree() : allowed_(LIKELY(CanEnterAllocatorShim())) {
if (allowed_)
- prevent_reentrancy_.Pointer()->Set(true);
+ SetEnteringAllocatorShim(true);
}
- ~ScopedAllowLogging() {
+ ~ScopedAllowFree() {
if (allowed_)
- prevent_reentrancy_.Pointer()->Set(false);
+ SetEnteringAllocatorShim(false);
}
explicit operator bool() const { return allowed_; }
private:
const bool allowed_;
- static base::LazyInstance<base::ThreadLocalBoolean>::Leaky
- prevent_reentrancy_;
};
-base::LazyInstance<base::ThreadLocalBoolean>::Leaky
- ScopedAllowLogging::prevent_reentrancy_;
+// Allocation logging also requires use of base TLS, so we must also check that
+// that is available. This means that allocations that occur after base TLS has
+// been torn down will not be logged.
+class ScopedAllowAlloc {
+ public:
+ ScopedAllowAlloc()
+ : allowed_(LIKELY(CanEnterAllocatorShim()) &&
+ LIKELY(!base::ThreadLocalStorage::HasBeenDestroyed())) {
+ if (allowed_)
+ SetEnteringAllocatorShim(true);
+ }
+ ~ScopedAllowAlloc() {
+ if (allowed_)
+ SetEnteringAllocatorShim(false);
+ }
+ explicit operator bool() const { return allowed_; }
+
+ private:
+ const bool allowed_;
+};
namespace {
@@ -162,14 +231,14 @@ void DestructShimState(void* shim_state) {
// Technically, this code could be called after Thread destruction and we would
// need to guard this with ThreadLocalStorage::HasBeenDestroyed(), but all calls
-// to this are guarded behind ScopedAllowLogging, which already makes the check.
+// to this are guarded behind ScopedAllowAlloc, which already makes the check.
base::ThreadLocalStorage::Slot& ShimStateTLS() {
static base::NoDestructor<base::ThreadLocalStorage::Slot> shim_state_tls(
&DestructShimState);
return *shim_state_tls;
}
-// We don't need to worry about re-entrancy because ScopedAllowLogging
+// We don't need to worry about re-entrancy because ScopedAllowAlloc.
// already guards against that.
ShimState* GetShimState() {
ShimState* state = static_cast<ShimState*>(ShimStateTLS().Get());
@@ -321,7 +390,7 @@ void DoSend(const void* address,
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
void* HookAlloc(const AllocatorDispatch* self, size_t size, void* context) {
- ScopedAllowLogging allow_logging;
+ ScopedAllowAlloc allow_logging;
const AllocatorDispatch* const next = self->next;
void* ptr = next->alloc_function(next, size, context);
@@ -337,7 +406,7 @@ void* HookZeroInitAlloc(const AllocatorDispatch* self,
size_t n,
size_t size,
void* context) {
- ScopedAllowLogging allow_logging;
+ ScopedAllowAlloc allow_logging;
const AllocatorDispatch* const next = self->next;
void* ptr = next->alloc_zero_initialized_function(next, n, size, context);
@@ -352,7 +421,7 @@ void* HookAllocAligned(const AllocatorDispatch* self,
size_t alignment,
size_t size,
void* context) {
- ScopedAllowLogging allow_logging;
+ ScopedAllowAlloc allow_logging;
const AllocatorDispatch* const next = self->next;
void* ptr = next->alloc_aligned_function(next, alignment, size, context);
@@ -367,7 +436,7 @@ void* HookRealloc(const AllocatorDispatch* self,
void* address,
size_t size,
void* context) {
- ScopedAllowLogging allow_logging;
+ ScopedAllowAlloc allow_logging;
const AllocatorDispatch* const next = self->next;
void* ptr = next->realloc_function(next, address, size, context);
@@ -382,7 +451,7 @@ void* HookRealloc(const AllocatorDispatch* self,
}
void HookFree(const AllocatorDispatch* self, void* address, void* context) {
- ScopedAllowLogging allow_logging;
+ ScopedAllowFree allow_logging;
const AllocatorDispatch* const next = self->next;
next->free_function(next, address, context);
@@ -404,7 +473,7 @@ unsigned HookBatchMalloc(const AllocatorDispatch* self,
void** results,
unsigned num_requested,
void* context) {
- ScopedAllowLogging allow_logging;
+ ScopedAllowAlloc allow_logging;
const AllocatorDispatch* const next = self->next;
unsigned count =
@@ -421,7 +490,7 @@ void HookBatchFree(const AllocatorDispatch* self,
void** to_be_freed,
unsigned num_to_be_freed,
void* context) {
- ScopedAllowLogging allow_logging;
+ ScopedAllowFree allow_logging;
const AllocatorDispatch* const next = self->next;
next->batch_free_function(next, to_be_freed, num_to_be_freed, context);
@@ -436,7 +505,7 @@ void HookFreeDefiniteSize(const AllocatorDispatch* self,
void* ptr,
size_t size,
void* context) {
- ScopedAllowLogging allow_logging;
+ ScopedAllowFree allow_logging;
const AllocatorDispatch* const next = self->next;
next->free_definite_size_function(next, ptr, size, context);
@@ -461,28 +530,28 @@ AllocatorDispatch g_hooks = {
#endif // BUILDFLAG(USE_ALLOCATOR_SHIM)
void HookPartitionAlloc(void* address, size_t size, const char* type) {
- ScopedAllowLogging allow_logging;
+ ScopedAllowAlloc allow_logging;
if (LIKELY(allow_logging)) {
AllocatorShimLogAlloc(AllocatorType::kPartitionAlloc, address, size, type);
}
}
void HookPartitionFree(void* address) {
- ScopedAllowLogging allow_logging;
+ ScopedAllowFree allow_logging;
if (LIKELY(allow_logging)) {
AllocatorShimLogFree(address);
}
}
void HookGCAlloc(uint8_t* address, size_t size, const char* type) {
- ScopedAllowLogging allow_logging;
+ ScopedAllowAlloc allow_logging;
if (LIKELY(allow_logging)) {
AllocatorShimLogAlloc(AllocatorType::kOilpan, address, size, type);
}
}
void HookGCFree(uint8_t* address) {
- ScopedAllowLogging allow_logging;
+ ScopedAllowFree allow_logging;
if (LIKELY(allow_logging)) {
AllocatorShimLogFree(address);
}
@@ -589,7 +658,7 @@ class FrameSerializer {
} // namespace
void InitTLSSlot() {
- { ScopedAllowLogging allow_logging; }
+ InitializeReentrancyKey();
ignore_result(ShimStateTLS());
}
@@ -814,6 +883,9 @@ void AllocatorShimLogAlloc(AllocatorType type,
}
}
+// This function may be called post Chrome TLS destruction, so it must not use
+// Chrome TLS. It currently uses 3 classes from Chrome: base::Lock,
+// base::TimeTicks and base::ScopedPlatformFile, all of which are safe.
void AllocatorShimLogFree(void* address) {
SendBuffer* send_buffers = g_send_buffers.Read();
if (!send_buffers)
diff --git a/chromium/components/services/heap_profiling/public/cpp/allocator_shim.h b/chromium/components/services/heap_profiling/public/cpp/allocator_shim.h
index 8004d3239f1..a1fedfc7258 100644
--- a/chromium/components/services/heap_profiling/public/cpp/allocator_shim.h
+++ b/chromium/components/services/heap_profiling/public/cpp/allocator_shim.h
@@ -38,6 +38,7 @@ void AllocatorShimLogAlloc(AllocatorType type,
size_t sz,
const char* context);
+// Logs a free. This must not rely on the base implementation of TLS.
void AllocatorShimLogFree(void* address);
// Ensures all send buffers are flushed. The given barrier ID is sent to the
diff --git a/chromium/components/services/heap_profiling/public/cpp/client.cc b/chromium/components/services/heap_profiling/public/cpp/client.cc
index cebff10d057..9f569812188 100644
--- a/chromium/components/services/heap_profiling/public/cpp/client.cc
+++ b/chromium/components/services/heap_profiling/public/cpp/client.cc
@@ -66,12 +66,8 @@ void Client::StartProfiling(mojom::ProfilingParamsPtr params) {
return;
started_profiling_ = true;
- base::PlatformFile platform_file;
- CHECK_EQ(MOJO_RESULT_OK, mojo::UnwrapPlatformFile(
- std::move(params->sender_pipe), &platform_file));
-
- base::ScopedPlatformFile scoped_platform_file(platform_file);
- sender_pipe_.reset(new SenderPipe(std::move(scoped_platform_file)));
+ sender_pipe_.reset(new SenderPipe(
+ mojo::UnwrapPlatformHandle(std::move(params->sender_pipe))));
StreamHeader header;
header.signature = kStreamSignature;
diff --git a/chromium/components/services/heap_profiling/public/cpp/controller.cc b/chromium/components/services/heap_profiling/public/cpp/controller.cc
index da708a320dc..afb483300bd 100644
--- a/chromium/components/services/heap_profiling/public/cpp/controller.cc
+++ b/chromium/components/services/heap_profiling/public/cpp/controller.cc
@@ -50,12 +50,10 @@ void Controller::StartProfilingClient(mojom::ProfilingClientPtr client,
mojom::ProfilingParamsPtr params = mojom::ProfilingParams::New();
params->sampling_rate = sampling_rate_;
- params->sender_pipe =
- mojo::WrapPlatformFile(pipes.PassSender().release().handle);
+ params->sender_pipe = mojo::WrapPlatformHandle(pipes.PassSender());
params->stack_mode = stack_mode_;
heap_profiling_service_->AddProfilingClient(
- pid, std::move(client),
- mojo::WrapPlatformFile(pipes.PassReceiver().release().handle),
+ pid, std::move(client), mojo::WrapPlatformHandle(pipes.PassReceiver()),
process_type, std::move(params));
}
diff --git a/chromium/components/services/heap_profiling/public/cpp/sender_pipe.h b/chromium/components/services/heap_profiling/public/cpp/sender_pipe.h
index 97c41d42af8..a84e837108d 100644
--- a/chromium/components/services/heap_profiling/public/cpp/sender_pipe.h
+++ b/chromium/components/services/heap_profiling/public/cpp/sender_pipe.h
@@ -10,7 +10,7 @@
#include "base/files/platform_file.h"
#include "base/macros.h"
#include "base/synchronization/lock.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/public/cpp/platform/platform_handle.h"
namespace heap_profiling {
@@ -32,20 +32,16 @@ class SenderPipe {
// |kPipeSize|.
PipePair();
PipePair(PipePair&&);
- mojo::edk::ScopedInternalPlatformHandle PassSender() {
- return std::move(sender_);
- }
- mojo::edk::ScopedInternalPlatformHandle PassReceiver() {
- return std::move(receiver_);
- }
+ mojo::PlatformHandle PassSender() { return std::move(sender_); }
+ mojo::PlatformHandle PassReceiver() { return std::move(receiver_); }
private:
- mojo::edk::ScopedInternalPlatformHandle sender_;
- mojo::edk::ScopedInternalPlatformHandle receiver_;
+ mojo::PlatformHandle sender_;
+ mojo::PlatformHandle receiver_;
DISALLOW_COPY_AND_ASSIGN(PipePair);
};
- explicit SenderPipe(base::ScopedPlatformFile file);
+ explicit SenderPipe(mojo::PlatformHandle handle);
~SenderPipe();
enum class Result { kSuccess, kTimeout, kError };
diff --git a/chromium/components/services/heap_profiling/public/cpp/sender_pipe_posix.cc b/chromium/components/services/heap_profiling/public/cpp/sender_pipe_posix.cc
index 363226a0447..a6bb324b351 100644
--- a/chromium/components/services/heap_profiling/public/cpp/sender_pipe_posix.cc
+++ b/chromium/components/services/heap_profiling/public/cpp/sender_pipe_posix.cc
@@ -31,14 +31,13 @@ SenderPipe::PipePair::PipePair() {
PCHECK(fcntl(fds[0], F_SETNOSIGPIPE, 1) == 0);
PCHECK(fcntl(fds[1], F_SETNOSIGPIPE, 1) == 0);
#endif
- receiver_.reset(mojo::edk::InternalPlatformHandle(fds[0]));
- sender_.reset(mojo::edk::InternalPlatformHandle(fds[1]));
+ receiver_ = mojo::PlatformHandle(base::ScopedFD(fds[0]));
+ sender_ = mojo::PlatformHandle(base::ScopedFD(fds[1]));
}
SenderPipe::PipePair::PipePair(PipePair&& other) = default;
-SenderPipe::SenderPipe(base::ScopedPlatformFile file)
- : file_(std::move(file)) {}
+SenderPipe::SenderPipe(mojo::PlatformHandle handle) : file_(handle.TakeFD()) {}
SenderPipe::~SenderPipe() = default;
diff --git a/chromium/components/services/heap_profiling/public/cpp/sender_pipe_unittest.cc b/chromium/components/services/heap_profiling/public/cpp/sender_pipe_unittest.cc
index 3e3a14c6178..4f1b636d6b2 100644
--- a/chromium/components/services/heap_profiling/public/cpp/sender_pipe_unittest.cc
+++ b/chromium/components/services/heap_profiling/public/cpp/sender_pipe_unittest.cc
@@ -7,11 +7,11 @@
#include <vector>
#include "build/build_config.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/public/cpp/platform/platform_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
-#if !defined(OS_MACOSX)
-#include "mojo/edk/embedder/platform_channel_pair.h"
+#if defined(OS_WIN)
+#include <windows.h>
#endif
namespace heap_profiling {
@@ -22,13 +22,10 @@ using Result = SenderPipe::Result;
class SenderPipeTest : public testing::Test {
public:
void SetUp() override {
- mojo::edk::ScopedInternalPlatformHandle write_handle;
-
SenderPipe::PipePair pipes;
read_handle_ = pipes.PassReceiver();
- base::ScopedPlatformFile file(pipes.PassSender().release().handle);
- sender_pipe_.reset(new SenderPipe(std::move(file)));
+ sender_pipe_.reset(new SenderPipe(pipes.PassSender()));
// A large buffer for both writing and reading.
buffer_.resize(64 * 1024);
@@ -38,21 +35,21 @@ class SenderPipeTest : public testing::Test {
void Read(int size) {
#if defined(OS_POSIX)
- ssize_t bytes_read = read(read_handle_.get().handle, buffer_.data(), size);
+ ssize_t bytes_read = read(read_handle_.GetFD().get(), buffer_.data(), size);
ASSERT_EQ(size, bytes_read);
#else
OVERLAPPED overlapped;
DWORD bytes_read = 0;
memset(&overlapped, 0, sizeof(OVERLAPPED));
- BOOL result = ::ReadFile(read_handle_.get().handle, buffer_.data(), size,
- &bytes_read, &overlapped);
+ BOOL result = ::ReadFile(read_handle_.GetHandle().Get(), buffer_.data(),
+ size, &bytes_read, &overlapped);
ASSERT_TRUE(result);
ASSERT_EQ(static_cast<DWORD>(size), bytes_read);
#endif
}
private:
- mojo::edk::ScopedInternalPlatformHandle read_handle_;
+ mojo::PlatformHandle read_handle_;
std::unique_ptr<SenderPipe> sender_pipe_;
std::vector<char> buffer_;
};
diff --git a/chromium/components/services/heap_profiling/public/cpp/sender_pipe_win.cc b/chromium/components/services/heap_profiling/public/cpp/sender_pipe_win.cc
index a12e1465563..4c137f70c8f 100644
--- a/chromium/components/services/heap_profiling/public/cpp/sender_pipe_win.cc
+++ b/chromium/components/services/heap_profiling/public/cpp/sender_pipe_win.cc
@@ -4,6 +4,8 @@
#include "components/services/heap_profiling/public/cpp/sender_pipe.h"
+#include <windows.h>
+
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
@@ -69,7 +71,7 @@ SenderPipe::PipePair::PipePair() {
// nothing to do with Send() timeout.
nullptr);
PCHECK(handle != INVALID_HANDLE_VALUE);
- receiver_.reset(mojo::edk::InternalPlatformHandle(handle));
+ receiver_ = mojo::PlatformHandle(base::win::ScopedHandle(handle));
// Allow the handle to be inherited by child processes.
SECURITY_ATTRIBUTES security_attributes;
@@ -84,19 +86,19 @@ SenderPipe::PipePair::PipePair() {
SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS | FILE_FLAG_OVERLAPPED,
nullptr);
PCHECK(handle != INVALID_HANDLE_VALUE);
- sender_.reset(mojo::edk::InternalPlatformHandle(handle));
+ sender_ = mojo::PlatformHandle(base::win::ScopedHandle(handle));
// Since a client has connected, ConnectNamedPipe() should return zero and
// GetLastError() should return ERROR_PIPE_CONNECTED.
- BOOL result = ConnectNamedPipe(receiver_.get().handle, nullptr);
+ BOOL result = ConnectNamedPipe(receiver_.GetHandle().Get(), nullptr);
DWORD error = GetLastError();
CHECK((result == 0) && (error == ERROR_PIPE_CONNECTED));
}
SenderPipe::PipePair::PipePair(PipePair&& other) = default;
-SenderPipe::SenderPipe(base::ScopedPlatformFile file)
- : file_(std::move(file)) {}
+SenderPipe::SenderPipe(mojo::PlatformHandle handle)
+ : file_(handle.TakeHandle()) {}
SenderPipe::~SenderPipe() {}
diff --git a/chromium/components/services/heap_profiling/receiver_pipe.cc b/chromium/components/services/heap_profiling/receiver_pipe.cc
index a2add075b2d..1d383c592da 100644
--- a/chromium/components/services/heap_profiling/receiver_pipe.cc
+++ b/chromium/components/services/heap_profiling/receiver_pipe.cc
@@ -10,8 +10,7 @@
namespace heap_profiling {
-ReceiverPipeBase::ReceiverPipeBase(
- mojo::edk::ScopedInternalPlatformHandle handle)
+ReceiverPipeBase::ReceiverPipeBase(mojo::PlatformHandle handle)
: handle_(std::move(handle)) {}
ReceiverPipeBase::~ReceiverPipeBase() = default;
diff --git a/chromium/components/services/heap_profiling/receiver_pipe.h b/chromium/components/services/heap_profiling/receiver_pipe.h
index e4e9ee36141..d0e561506b2 100644
--- a/chromium/components/services/heap_profiling/receiver_pipe.h
+++ b/chromium/components/services/heap_profiling/receiver_pipe.h
@@ -7,7 +7,7 @@
#include "base/memory/ref_counted.h"
#include "build/build_config.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/public/cpp/platform/platform_handle.h"
namespace base {
class TaskRunner;
@@ -29,7 +29,7 @@ class ReceiverPipeBase : public base::RefCountedThreadSafe<ReceiverPipeBase> {
protected:
friend class base::RefCountedThreadSafe<ReceiverPipeBase>;
- explicit ReceiverPipeBase(mojo::edk::ScopedInternalPlatformHandle handle);
+ explicit ReceiverPipeBase(mojo::PlatformHandle handle);
virtual ~ReceiverPipeBase();
// Callback that indicates an error has occurred and the connection should
@@ -46,7 +46,7 @@ class ReceiverPipeBase : public base::RefCountedThreadSafe<ReceiverPipeBase> {
scoped_refptr<base::TaskRunner> receiver_task_runner_;
scoped_refptr<StreamReceiver> receiver_;
- mojo::edk::ScopedInternalPlatformHandle handle_;
+ mojo::PlatformHandle handle_;
};
} // namespace heap_profiling
diff --git a/chromium/components/services/heap_profiling/receiver_pipe_posix.cc b/chromium/components/services/heap_profiling/receiver_pipe_posix.cc
index 35baa5dfb91..c8cb80b8e00 100644
--- a/chromium/components/services/heap_profiling/receiver_pipe_posix.cc
+++ b/chromium/components/services/heap_profiling/receiver_pipe_posix.cc
@@ -13,12 +13,10 @@
#include "components/services/heap_profiling/public/cpp/sender_pipe.h"
#include "components/services/heap_profiling/receiver_pipe.h"
#include "components/services/heap_profiling/stream_receiver.h"
-#include "mojo/edk/embedder/platform_channel_utils_posix.h"
-#include "mojo/edk/embedder/platform_handle.h"
namespace heap_profiling {
-ReceiverPipe::ReceiverPipe(mojo::edk::ScopedInternalPlatformHandle handle)
+ReceiverPipe::ReceiverPipe(mojo::PlatformHandle handle)
: ReceiverPipeBase(std::move(handle)),
controller_(FROM_HERE),
read_buffer_(new char[SenderPipe::kPipeSize]) {}
@@ -27,18 +25,16 @@ ReceiverPipe::~ReceiverPipe() {}
void ReceiverPipe::StartReadingOnIOThread() {
base::MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
- handle_.get().handle, true, base::MessagePumpForIO::WATCH_READ,
+ handle_.GetFD().get(), true, base::MessagePumpForIO::WATCH_READ,
&controller_, this);
- OnFileCanReadWithoutBlocking(handle_.get().handle);
+ OnFileCanReadWithoutBlocking(handle_.GetFD().get());
}
void ReceiverPipe::OnFileCanReadWithoutBlocking(int fd) {
ssize_t bytes_read = 0;
do {
- base::circular_deque<mojo::edk::InternalPlatformHandle> dummy_for_receive;
-
bytes_read = HANDLE_EINTR(
- read(handle_.get().handle, read_buffer_.get(), SenderPipe::kPipeSize));
+ read(handle_.GetFD().get(), read_buffer_.get(), SenderPipe::kPipeSize));
if (bytes_read > 0) {
receiver_task_runner_->PostTask(
FROM_HERE,
diff --git a/chromium/components/services/heap_profiling/receiver_pipe_posix.h b/chromium/components/services/heap_profiling/receiver_pipe_posix.h
index f5eb08f9db2..30f0a2fde69 100644
--- a/chromium/components/services/heap_profiling/receiver_pipe_posix.h
+++ b/chromium/components/services/heap_profiling/receiver_pipe_posix.h
@@ -18,7 +18,7 @@ namespace heap_profiling {
class ReceiverPipe : public ReceiverPipeBase,
public base::MessagePumpForIO::FdWatcher {
public:
- explicit ReceiverPipe(mojo::edk::ScopedInternalPlatformHandle handle);
+ explicit ReceiverPipe(mojo::PlatformHandle handle);
// Must be called on the IO thread.
void StartReadingOnIOThread();
diff --git a/chromium/components/services/heap_profiling/receiver_pipe_win.cc b/chromium/components/services/heap_profiling/receiver_pipe_win.cc
index 8f14087d565..cba6a69c6f9 100644
--- a/chromium/components/services/heap_profiling/receiver_pipe_win.cc
+++ b/chromium/components/services/heap_profiling/receiver_pipe_win.cc
@@ -16,12 +16,12 @@
namespace heap_profiling {
-ReceiverPipe::ReceiverPipe(mojo::edk::ScopedInternalPlatformHandle handle)
+ReceiverPipe::ReceiverPipe(mojo::PlatformHandle handle)
: ReceiverPipeBase(std::move(handle)),
read_buffer_(new char[SenderPipe::kPipeSize]) {
ZeroOverlapped();
- base::MessageLoopCurrentForIO::Get()->RegisterIOHandler(handle_.get().handle,
- this);
+ base::MessageLoopCurrentForIO::Get()->RegisterIOHandler(
+ handle_.GetHandle().Get(), this);
}
ReceiverPipe::~ReceiverPipe() {}
@@ -41,7 +41,7 @@ void ReceiverPipe::ReadUntilBlocking() {
DCHECK(!read_outstanding_);
read_outstanding_ = this;
- if (!::ReadFile(handle_.get().handle, read_buffer_.get(),
+ if (!::ReadFile(handle_.GetHandle().Get(), read_buffer_.get(),
SenderPipe::kPipeSize, &bytes_read, &context_.overlapped)) {
if (GetLastError() == ERROR_IO_PENDING) {
return;
diff --git a/chromium/components/services/heap_profiling/receiver_pipe_win.h b/chromium/components/services/heap_profiling/receiver_pipe_win.h
index cd6adfb3fbd..603f6036b0c 100644
--- a/chromium/components/services/heap_profiling/receiver_pipe_win.h
+++ b/chromium/components/services/heap_profiling/receiver_pipe_win.h
@@ -21,7 +21,7 @@ namespace heap_profiling {
class ReceiverPipe : public ReceiverPipeBase,
public base::MessagePumpForIO::IOHandler {
public:
- explicit ReceiverPipe(mojo::edk::ScopedInternalPlatformHandle handle);
+ explicit ReceiverPipe(mojo::PlatformHandle handle);
// Must be called on the IO thread.
void StartReadingOnIOThread();
diff --git a/chromium/components/services/leveldb/OWNERS b/chromium/components/services/leveldb/OWNERS
index e03714b5bc1..be35d981929 100644
--- a/chromium/components/services/leveldb/OWNERS
+++ b/chromium/components/services/leveldb/OWNERS
@@ -1,4 +1,3 @@
-erg@chromium.org
mek@chromium.org
per-file manifest.json=set noparent
diff --git a/chromium/components/services/pdf_compositor/pdf_compositor_service.cc b/chromium/components/services/pdf_compositor/pdf_compositor_service.cc
index e71b72ab482..3cd761e3ddc 100644
--- a/chromium/components/services/pdf_compositor/pdf_compositor_service.cc
+++ b/chromium/components/services/pdf_compositor/pdf_compositor_service.cc
@@ -25,7 +25,7 @@
#include "content/public/child/dwrite_font_proxy_init_win.h"
#elif defined(OS_MACOSX)
#include "third_party/blink/public/platform/platform.h"
-#include "third_party/skia/include/ports/SkFontMgr.h"
+#include "third_party/skia/include/core/SkFontMgr.h"
#elif defined(OS_POSIX) && !defined(OS_ANDROID)
#include "third_party/blink/public/platform/platform.h"
#endif
@@ -66,7 +66,7 @@ std::unique_ptr<service_manager::Service> PdfCompositorService::Create(
void PdfCompositorService::PrepareToStart() {
// Set up discardable memory manager.
discardable_memory::mojom::DiscardableSharedMemoryManagerPtr manager_ptr;
- if (features::IsMashEnabled()) {
+ if (!features::IsAshInBrowserProcess()) {
#if defined(USE_AURA)
context()->connector()->BindInterface(ui::mojom::kServiceName,
&manager_ptr);
diff --git a/chromium/components/services/pdf_compositor/public/cpp/pdf_compositor_service_factory.cc b/chromium/components/services/pdf_compositor/public/cpp/pdf_compositor_service_factory.cc
index 0af223b62b1..763208cf73d 100644
--- a/chromium/components/services/pdf_compositor/public/cpp/pdf_compositor_service_factory.cc
+++ b/chromium/components/services/pdf_compositor/public/cpp/pdf_compositor_service_factory.cc
@@ -14,7 +14,7 @@ namespace printing {
std::unique_ptr<service_manager::Service> CreatePdfCompositorService(
const std::string& creator) {
-#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
content::UtilityThread::Get()->EnsureBlinkInitializedWithSandboxSupport();
#else
content::UtilityThread::Get()->EnsureBlinkInitialized();
diff --git a/chromium/components/sessions/BUILD.gn b/chromium/components/sessions/BUILD.gn
index 5ec556c3e16..615c1c71dfd 100644
--- a/chromium/components/sessions/BUILD.gn
+++ b/chromium/components/sessions/BUILD.gn
@@ -76,6 +76,8 @@ source_set("shared") {
"core/live_tab.cc",
"core/live_tab.h",
"core/live_tab_context.h",
+ "core/persistent_tab_restore_service.cc",
+ "core/persistent_tab_restore_service.h",
"core/serialized_navigation_driver.h",
"core/serialized_navigation_entry.cc",
"core/serialized_navigation_entry.h",
@@ -87,6 +89,8 @@ source_set("shared") {
"core/session_constants.h",
"core/session_id.cc",
"core/session_id.h",
+ "core/session_id_generator.cc",
+ "core/session_id_generator.h",
"core/session_service_commands.cc",
"core/session_service_commands.h",
"core/session_types.cc",
@@ -100,29 +104,16 @@ source_set("shared") {
"core/tab_restore_service_observer.h",
]
- if (is_android) {
- sources += [
- "core/in_memory_tab_restore_service.cc",
- "core/in_memory_tab_restore_service.h",
- ]
- } else {
- sources += [
- "core/persistent_tab_restore_service.cc",
- "core/persistent_tab_restore_service.h",
- ]
- }
-
configs += [ ":implementation" ]
public_deps = [
"//components/keyed_service/core",
- "//components/sync/protocol",
]
deps = [
"//base",
"//components/keyed_service/core",
- "//components/sync",
+ "//components/prefs",
"//components/variations",
"//skia",
"//ui/base",
@@ -143,7 +134,6 @@ static_library("test_support") {
]
deps = [
"//base",
- "//components/sync",
"//skia",
"//testing/gtest",
"//ui/base", # For page_transition_types.h.
@@ -166,7 +156,7 @@ source_set("unit_tests") {
sources = [
"core/serialized_navigation_entry_unittest.cc",
"core/session_backend_unittest.cc",
- "core/session_types_unittest.cc",
+ "core/session_id_generator_unittest.cc",
"ios/ios_serialized_navigation_builder_unittest.mm",
"ios/ios_serialized_navigation_driver_unittest.cc",
]
@@ -185,7 +175,8 @@ source_set("unit_tests") {
deps = [
":test_support",
"//base/test:test_support",
- "//components/sync",
+ "//components/prefs:test_support",
+ "//testing/gmock",
"//testing/gtest",
"//ui/base", # For page transition types.
"//url",
diff --git a/chromium/components/sessions/DEPS b/chromium/components/sessions/DEPS
index a704684cce3..9b0cbfb8e70 100644
--- a/chromium/components/sessions/DEPS
+++ b/chromium/components/sessions/DEPS
@@ -1,5 +1,4 @@
include_rules = [
- "+components/sync",
"+components/variations",
"+ui/base",
"+ui/gfx",
diff --git a/chromium/components/sessions/core/DEPS b/chromium/components/sessions/core/DEPS
index f0bf3d9693e..f2ecd68b07c 100644
--- a/chromium/components/sessions/core/DEPS
+++ b/chromium/components/sessions/core/DEPS
@@ -1,3 +1,4 @@
include_rules = [
"+components/keyed_service/core",
+ "+components/prefs",
]
diff --git a/chromium/components/sessions/core/in_memory_tab_restore_service.cc b/chromium/components/sessions/core/in_memory_tab_restore_service.cc
deleted file mode 100644
index 7ad73e70de9..00000000000
--- a/chromium/components/sessions/core/in_memory_tab_restore_service.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (c) 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/sessions/core/in_memory_tab_restore_service.h"
-
-#include <utility>
-#include <vector>
-
-#include "base/compiler_specific.h"
-
-namespace sessions {
-
-InMemoryTabRestoreService::InMemoryTabRestoreService(
- std::unique_ptr<TabRestoreServiceClient> client,
- TabRestoreService::TimeFactory* time_factory)
- : client_(std::move(client)),
- helper_(this, NULL, client_.get(), time_factory) {}
-
-InMemoryTabRestoreService::~InMemoryTabRestoreService() {}
-
-void InMemoryTabRestoreService::AddObserver(
- TabRestoreServiceObserver* observer) {
- helper_.AddObserver(observer);
-}
-
-void InMemoryTabRestoreService::RemoveObserver(
- TabRestoreServiceObserver* observer) {
- helper_.RemoveObserver(observer);
-}
-
-void InMemoryTabRestoreService::CreateHistoricalTab(LiveTab* live_tab,
- int index) {
- helper_.CreateHistoricalTab(live_tab, index);
-}
-
-void InMemoryTabRestoreService::BrowserClosing(LiveTabContext* context) {
- helper_.BrowserClosing(context);
-}
-
-void InMemoryTabRestoreService::BrowserClosed(LiveTabContext* context) {
- helper_.BrowserClosed(context);
-}
-
-void InMemoryTabRestoreService::ClearEntries() {
- helper_.ClearEntries();
-}
-
-void InMemoryTabRestoreService::DeleteNavigationEntries(
- const DeletionPredicate& predicate) {
- helper_.DeleteNavigationEntries(predicate);
-}
-
-const TabRestoreService::Entries& InMemoryTabRestoreService::entries() const {
- return helper_.entries();
-}
-
-std::vector<LiveTab*> InMemoryTabRestoreService::RestoreMostRecentEntry(
- LiveTabContext* context) {
- return helper_.RestoreMostRecentEntry(context);
-}
-
-std::unique_ptr<TabRestoreService::Tab>
-InMemoryTabRestoreService::RemoveTabEntryById(SessionID id) {
- return helper_.RemoveTabEntryById(id);
-}
-
-std::vector<LiveTab*> InMemoryTabRestoreService::RestoreEntryById(
- LiveTabContext* context,
- SessionID id,
- WindowOpenDisposition disposition) {
- return helper_.RestoreEntryById(context, id, disposition);
-}
-
-void InMemoryTabRestoreService::LoadTabsFromLastSession() {
- // Do nothing. This relies on tab persistence which is implemented in Java on
- // the application side on Android.
-}
-
-bool InMemoryTabRestoreService::IsLoaded() const {
- // See comment above.
- return true;
-}
-
-void InMemoryTabRestoreService::DeleteLastSession() {
- // See comment above.
-}
-
-bool InMemoryTabRestoreService::IsRestoring() const {
- return helper_.IsRestoring();
-}
-
-void InMemoryTabRestoreService::Shutdown() {
-}
-
-} // namespace
diff --git a/chromium/components/sessions/core/in_memory_tab_restore_service.h b/chromium/components/sessions/core/in_memory_tab_restore_service.h
deleted file mode 100644
index 07a4ab3a439..00000000000
--- a/chromium/components/sessions/core/in_memory_tab_restore_service.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) 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_SESSIONS_CORE_IN_MEMORY_TAB_RESTORE_SERVICE_H_
-#define COMPONENTS_SESSIONS_CORE_IN_MEMORY_TAB_RESTORE_SERVICE_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "components/sessions/core/sessions_export.h"
-#include "components/sessions/core/tab_restore_service.h"
-#include "components/sessions/core/tab_restore_service_client.h"
-#include "components/sessions/core/tab_restore_service_helper.h"
-
-namespace sessions {
-
-class TabRestoreServiceClient;
-
-// Tab restore service that doesn't persist tabs on disk. This is used on
-// Android where tabs persistence is implemented on the application side in
-// Java. Other platforms should use PersistentTabRestoreService which can be
-// instantiated through the TabRestoreServiceFactory.
-class SESSIONS_EXPORT InMemoryTabRestoreService : public TabRestoreService {
- public:
- // Creates a new TabRestoreService and provides an object that provides the
- // current time. The TabRestoreService does not take ownership of
- // |time_factory|.
- InMemoryTabRestoreService(std::unique_ptr<TabRestoreServiceClient> client,
- TimeFactory* time_factory);
-
- ~InMemoryTabRestoreService() override;
-
- // TabRestoreService:
- void AddObserver(TabRestoreServiceObserver* observer) override;
- void RemoveObserver(TabRestoreServiceObserver* observer) override;
- void CreateHistoricalTab(LiveTab* live_tab, int index) override;
- void BrowserClosing(LiveTabContext* context) override;
- void BrowserClosed(LiveTabContext* context) override;
- void ClearEntries() override;
- void DeleteNavigationEntries(const DeletionPredicate& predicate) override;
- const Entries& entries() const override;
- std::vector<LiveTab*> RestoreMostRecentEntry(
- LiveTabContext* context) override;
- std::unique_ptr<Tab> RemoveTabEntryById(SessionID id) override;
- std::vector<LiveTab*> RestoreEntryById(
- LiveTabContext* context,
- SessionID id,
- WindowOpenDisposition disposition) override;
- void LoadTabsFromLastSession() override;
- bool IsLoaded() const override;
- void DeleteLastSession() override;
- bool IsRestoring() const override;
- void Shutdown() override;
-
- private:
- std::unique_ptr<TabRestoreServiceClient> client_;
- TabRestoreServiceHelper helper_;
-
- DISALLOW_COPY_AND_ASSIGN(InMemoryTabRestoreService);
-};
-
-} // namespace sessions
-
-#endif // COMPONENTS_SESSIONS_CORE_IN_MEMORY_TAB_RESTORE_SERVICE_H_
diff --git a/chromium/components/sessions/core/serialized_navigation_entry.cc b/chromium/components/sessions/core/serialized_navigation_entry.cc
index c18183e5283..0056aae888a 100644
--- a/chromium/components/sessions/core/serialized_navigation_entry.cc
+++ b/chromium/components/sessions/core/serialized_navigation_entry.cc
@@ -5,118 +5,18 @@
#include "components/sessions/core/serialized_navigation_entry.h"
#include <stddef.h>
+#include <utility>
#include "base/macros.h"
#include "base/pickle.h"
-#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/memory_usage_estimator.h"
#include "components/sessions/core/serialized_navigation_driver.h"
-#include "components/sync/base/time.h"
-#include "components/sync/protocol/session_specifics.pb.h"
namespace sessions {
// The previous referrer policy value corresponding to |Never|.
const int kObsoleteReferrerPolicyNever = 2;
-namespace {
-
-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
-
size_t
SerializedNavigationEntry::ReplacedNavigationEntryData::EstimateMemoryUsage()
const {
@@ -147,77 +47,6 @@ SerializedNavigationEntry& SerializedNavigationEntry::operator=(
SerializedNavigationEntry& SerializedNavigationEntry::operator=(
SerializedNavigationEntry&& other) = default;
-SerializedNavigationEntry SerializedNavigationEntry::FromSyncData(
- int index,
- const sync_pb::TabNavigation& sync_data) {
- SerializedNavigationEntry navigation;
- navigation.index_ = index;
- navigation.unique_id_ = sync_data.unique_id();
- if (sync_data.has_correct_referrer_policy()) {
- navigation.referrer_url_ = GURL(sync_data.referrer());
- navigation.referrer_policy_ = sync_data.correct_referrer_policy();
- } else {
- navigation.referrer_url_ = GURL();
- navigation.referrer_policy_ = kObsoleteReferrerPolicyNever;
- }
- navigation.virtual_url_ = GURL(sync_data.virtual_url());
- navigation.title_ = base::UTF8ToUTF16(sync_data.title());
-
- uint32_t transition = FromSyncPageTransition(sync_data.page_transition());
-
- if (sync_data.has_redirect_type()) {
- switch (sync_data.redirect_type()) {
- case sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT:
- transition |= ui::PAGE_TRANSITION_CLIENT_REDIRECT;
- break;
- case sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT:
- transition |= ui::PAGE_TRANSITION_SERVER_REDIRECT;
- break;
- }
- }
- if (sync_data.navigation_forward_back())
- transition |= ui::PAGE_TRANSITION_FORWARD_BACK;
- if (sync_data.navigation_from_address_bar())
- transition |= ui::PAGE_TRANSITION_FROM_ADDRESS_BAR;
- if (sync_data.navigation_home_page())
- transition |= ui::PAGE_TRANSITION_HOME_PAGE;
- if (sync_data.navigation_chain_start())
- transition |= ui::PAGE_TRANSITION_CHAIN_START;
- if (sync_data.navigation_chain_end())
- transition |= ui::PAGE_TRANSITION_CHAIN_END;
-
- navigation.transition_type_ = static_cast<ui::PageTransition>(transition);
-
- navigation.timestamp_ = syncer::ProtoTimeToTime(sync_data.timestamp_msec());
- if (sync_data.has_favicon_url())
- navigation.favicon_url_ = GURL(sync_data.favicon_url());
-
- if (sync_data.has_password_state()) {
- navigation.password_state_ =
- static_cast<SerializedNavigationEntry::PasswordState>(
- sync_data.password_state());
- }
-
- navigation.http_status_code_ = sync_data.http_status_code();
-
- if (sync_data.has_replaced_navigation()) {
- navigation.replaced_entry_data_ = ReplacedNavigationEntryData();
- navigation.replaced_entry_data_->first_committed_url =
- GURL(sync_data.replaced_navigation().first_committed_url());
- navigation.replaced_entry_data_->first_timestamp = syncer::ProtoTimeToTime(
- sync_data.replaced_navigation().first_timestamp_msec());
- navigation.replaced_entry_data_->first_transition_type =
- FromSyncPageTransition(
- sync_data.replaced_navigation().first_page_transition());
- }
-
- SerializedNavigationDriver::Get()->Sanitize(&navigation);
-
- navigation.is_restored_ = true;
-
- return navigation;
-}
-
namespace {
// Helper used by SerializedNavigationEntry::WriteToPickle(). It writes |str| to
@@ -425,99 +254,6 @@ bool SerializedNavigationEntry::ReadFromPickle(base::PickleIterator* iterator) {
return true;
}
-// TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well?
-// See http://crbug.com/67068.
-sync_pb::TabNavigation SerializedNavigationEntry::ToSyncData() const {
- sync_pb::TabNavigation sync_data;
- sync_data.set_virtual_url(virtual_url_.spec());
- sync_data.set_referrer(referrer_url_.spec());
- sync_data.set_correct_referrer_policy(referrer_policy_);
- sync_data.set_title(base::UTF16ToUTF8(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");
- sync_data.set_page_transition(ToSyncPageTransition(transition_type_));
-
- // Page transition qualifiers.
- if (ui::PageTransitionIsRedirect(transition_type_)) {
- if (transition_type_ & ui::PAGE_TRANSITION_CLIENT_REDIRECT) {
- sync_data.set_redirect_type(
- sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT);
- } else if (transition_type_ & ui::PAGE_TRANSITION_SERVER_REDIRECT) {
- sync_data.set_redirect_type(
- sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT);
- }
- }
- sync_data.set_navigation_forward_back(
- (transition_type_ & ui::PAGE_TRANSITION_FORWARD_BACK) != 0);
- sync_data.set_navigation_from_address_bar(
- (transition_type_ & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR) != 0);
- sync_data.set_navigation_home_page(
- (transition_type_ & ui::PAGE_TRANSITION_HOME_PAGE) != 0);
- sync_data.set_navigation_chain_start(
- (transition_type_ & ui::PAGE_TRANSITION_CHAIN_START) != 0);
- sync_data.set_navigation_chain_end(
- (transition_type_ & ui::PAGE_TRANSITION_CHAIN_END) != 0);
-
- sync_data.set_unique_id(unique_id_);
- sync_data.set_timestamp_msec(syncer::TimeToProtoTime(timestamp_));
- // The full-resolution timestamp works as a global ID.
- sync_data.set_global_id(timestamp_.ToInternalValue());
-
- sync_data.set_http_status_code(http_status_code_);
-
- if (favicon_url_.is_valid())
- sync_data.set_favicon_url(favicon_url_.spec());
-
- if (blocked_state_ != STATE_INVALID) {
- sync_data.set_blocked_state(
- static_cast<sync_pb::TabNavigation_BlockedState>(blocked_state_));
- }
-
- sync_data.set_password_state(
- static_cast<sync_pb::TabNavigation_PasswordState>(password_state_));
-
- for (std::set<std::string>::const_iterator it =
- content_pack_categories_.begin();
- it != content_pack_categories_.end(); ++it) {
- sync_data.add_content_pack_categories(*it);
- }
-
- // Copy all redirect chain entries except the last URL (which should match
- // the virtual_url).
- if (redirect_chain_.size() > 1) { // Single entry chains have no redirection.
- size_t last_entry = redirect_chain_.size() - 1;
- for (size_t i = 0; i < last_entry; i++) {
- sync_pb::NavigationRedirect* navigation_redirect =
- sync_data.add_navigation_redirect();
- navigation_redirect->set_url(redirect_chain_[i].spec());
- }
- // If the last URL didn't match the virtual_url, record it separately.
- if (sync_data.virtual_url() != redirect_chain_[last_entry].spec()) {
- sync_data.set_last_navigation_redirect_url(
- redirect_chain_[last_entry].spec());
- }
- }
-
- if (replaced_entry_data_.has_value()) {
- sync_pb::ReplacedNavigation* replaced_navigation =
- sync_data.mutable_replaced_navigation();
- replaced_navigation->set_first_committed_url(
- 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));
- }
-
- sync_data.set_is_restored(is_restored_);
-
- return sync_data;
-}
-
size_t SerializedNavigationEntry::EstimateMemoryUsage() const {
using base::trace_event::EstimateMemoryUsage;
return EstimateMemoryUsage(referrer_url_) +
diff --git a/chromium/components/sessions/core/serialized_navigation_entry.h b/chromium/components/sessions/core/serialized_navigation_entry.h
index cb17bfc2c1c..47dd1bb14f7 100644
--- a/chromium/components/sessions/core/serialized_navigation_entry.h
+++ b/chromium/components/sessions/core/serialized_navigation_entry.h
@@ -25,18 +25,13 @@ class Pickle;
class PickleIterator;
}
-namespace sync_pb {
-class TabNavigation;
-}
-
namespace sessions {
class SerializedNavigationEntryTestHelper;
// SerializedNavigationEntry is a "freeze-dried" version of NavigationEntry. It
// contains the data needed to restore a NavigationEntry during session restore
-// and tab restore, and it can also be pickled and unpickled. It is also
-// convertible to a sync protocol buffer for session syncing.
+// and tab restore, and it can also be pickled and unpickled.
//
// Default copy constructor and assignment operator welcome.
class SESSIONS_EXPORT SerializedNavigationEntry {
@@ -64,42 +59,37 @@ class SESSIONS_EXPORT SerializedNavigationEntry {
SerializedNavigationEntry& operator=(const SerializedNavigationEntry& other);
SerializedNavigationEntry& operator=(SerializedNavigationEntry&& other);
- // Construct a SerializedNavigationEntry for a particular index from a sync
- // protocol buffer. Note that the sync protocol buffer doesn't contain all
- // SerializedNavigationEntry fields. Also, the timestamp of the returned
- // SerializedNavigationEntry is nulled out, as we assume that the protocol
- // buffer is from a foreign session.
- static SerializedNavigationEntry FromSyncData(
- int index,
- const sync_pb::TabNavigation& sync_data);
-
// Note that not all SerializedNavigationEntry fields are preserved.
// |max_size| is the max number of bytes to write.
void WriteToPickle(int max_size, base::Pickle* pickle) const;
bool ReadFromPickle(base::PickleIterator* iterator);
- // Convert this navigation into its sync protocol buffer equivalent. Note
- // that the protocol buffer doesn't contain all SerializedNavigationEntry
- // fields.
- sync_pb::TabNavigation ToSyncData() const;
-
// The index in the NavigationController. This SerializedNavigationEntry is
// valid only when the index is non-negative.
int index() const { return index_; }
void set_index(int index) { index_ = index; }
- // Accessors for some fields taken from NavigationEntry.
int unique_id() const { return unique_id_; }
+ void set_unique_id(int unique_id) { unique_id_ = unique_id; }
const base::string16& title() const { return title_; }
+ void set_title(const base::string16& title) { title_ = title; }
const GURL& favicon_url() const { return favicon_url_; }
+ void set_favicon_url(const GURL& favicon_url) { favicon_url_ = favicon_url; }
int http_status_code() const { return http_status_code_; }
+ void set_http_status_code(int http_status_code) {
+ http_status_code_ = http_status_code;
+ }
ui::PageTransition transition_type() const {
return transition_type_;
}
+ void set_transition_type(ui::PageTransition transition_type) {
+ transition_type_ = transition_type;
+ }
bool has_post_data() const { return has_post_data_; }
int64_t post_id() const { return post_id_; }
bool is_overriding_user_agent() const { return is_overriding_user_agent_; }
base::Time timestamp() const { return timestamp_; }
+ void set_timestamp(base::Time timestamp) { timestamp_ = timestamp; }
BlockedState blocked_state() const { return blocked_state_; }
void set_blocked_state(BlockedState blocked_state) {
@@ -159,6 +149,13 @@ class SESSIONS_EXPORT SerializedNavigationEntry {
const {
return replaced_entry_data_;
}
+ void set_replaced_entry_data(
+ const base::Optional<ReplacedNavigationEntryData>& replaced_entry_data) {
+ replaced_entry_data_ = replaced_entry_data;
+ }
+
+ bool is_restored() const { return is_restored_; }
+ void set_is_restored(bool is_restored) { is_restored_ = is_restored; }
const std::map<std::string, std::string>& extended_info_map() const {
return extended_info_map_;
diff --git a/chromium/components/sessions/core/serialized_navigation_entry_test_helper.cc b/chromium/components/sessions/core/serialized_navigation_entry_test_helper.cc
index 516974c1e93..399acbad2bd 100644
--- a/chromium/components/sessions/core/serialized_navigation_entry_test_helper.cc
+++ b/chromium/components/sessions/core/serialized_navigation_entry_test_helper.cc
@@ -7,7 +7,6 @@
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "components/sessions/core/serialized_navigation_entry.h"
-#include "components/sync/base/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -31,7 +30,8 @@ const bool kHasPostData = true;
const int64_t kPostID = 100;
const GURL kOriginalRequestURL = GURL("http://www.original-request.com");
const bool kIsOverridingUserAgent = true;
-const base::Time kTimestamp = syncer::ProtoTimeToTime(100);
+const base::Time kTimestamp =
+ base::Time::UnixEpoch() + base::TimeDelta::FromMilliseconds(100);
const GURL kFaviconURL = GURL("http://virtual-url.com/favicon.ico");
const int kHttpStatusCode = 404;
const GURL kRedirectURL0 = GURL("http://go/redirect0");
diff --git a/chromium/components/sessions/core/serialized_navigation_entry_unittest.cc b/chromium/components/sessions/core/serialized_navigation_entry_unittest.cc
index 72dcbe1e36c..22e8d064812 100644
--- a/chromium/components/sessions/core/serialized_navigation_entry_unittest.cc
+++ b/chromium/components/sessions/core/serialized_navigation_entry_unittest.cc
@@ -17,8 +17,6 @@
#include "base/time/time.h"
#include "components/sessions/core/serialized_navigation_driver.h"
#include "components/sessions/core/serialized_navigation_entry_test_helper.h"
-#include "components/sync/base/time.h"
-#include "components/sync/protocol/session_specifics.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/page_transition_types.h"
#include "url/gurl.h"
@@ -26,26 +24,6 @@
namespace sessions {
namespace {
-// Create a sync_pb::TabNavigation from the constants above.
-sync_pb::TabNavigation MakeSyncDataForTest() {
- sync_pb::TabNavigation sync_data;
- sync_data.set_virtual_url(test_data::kVirtualURL.spec());
- sync_data.set_referrer(test_data::kReferrerURL.spec());
- sync_data.set_obsolete_referrer_policy(test_data::kReferrerPolicy);
- sync_data.set_correct_referrer_policy(test_data::kReferrerPolicy);
- sync_data.set_title(base::UTF16ToUTF8(test_data::kTitle));
- sync_data.set_page_transition(
- sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME);
- sync_data.set_unique_id(test_data::kUniqueID);
- sync_data.set_timestamp_msec(syncer::TimeToProtoTime(test_data::kTimestamp));
- sync_data.set_redirect_type(sync_pb::SyncEnums::CLIENT_REDIRECT);
- sync_data.set_navigation_home_page(true);
- sync_data.set_favicon_url(test_data::kFaviconURL.spec());
- sync_data.set_http_status_code(test_data::kHttpStatusCode);
- // The redirect chain only syncs one way.
- return sync_data;
-}
-
// Create a default SerializedNavigationEntry. All its fields should be
// initialized to their respective default values.
TEST(SerializedNavigationEntryTest, DefaultInitializer) {
@@ -71,33 +49,6 @@ TEST(SerializedNavigationEntryTest, DefaultInitializer) {
EXPECT_EQ(0U, navigation.redirect_chain().size());
}
-// Create a SerializedNavigationEntry from a sync_pb::TabNavigation. All its
-// fields should match the protocol buffer's if it exists there, and
-// sbould be set to the default value otherwise.
-TEST(SerializedNavigationEntryTest, FromSyncData) {
- const sync_pb::TabNavigation sync_data = MakeSyncDataForTest();
-
- const SerializedNavigationEntry& navigation =
- SerializedNavigationEntry::FromSyncData(test_data::kIndex, sync_data);
-
- EXPECT_EQ(test_data::kIndex, navigation.index());
- EXPECT_EQ(test_data::kUniqueID, navigation.unique_id());
- EXPECT_EQ(test_data::kReferrerURL, navigation.referrer_url());
- EXPECT_EQ(test_data::kReferrerPolicy, navigation.referrer_policy());
- EXPECT_EQ(test_data::kVirtualURL, navigation.virtual_url());
- EXPECT_EQ(test_data::kTitle, navigation.title());
- EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
- navigation.transition_type(), test_data::kTransitionType));
- EXPECT_FALSE(navigation.has_post_data());
- EXPECT_EQ(-1, navigation.post_id());
- EXPECT_EQ(GURL(), navigation.original_request_url());
- EXPECT_FALSE(navigation.is_overriding_user_agent());
- EXPECT_EQ(test_data::kTimestamp, navigation.timestamp());
- EXPECT_EQ(test_data::kFaviconURL, navigation.favicon_url());
- EXPECT_EQ(test_data::kHttpStatusCode, navigation.http_status_code());
- // The redirect chain only syncs one way.
-}
-
// Create a SerializedNavigationEntry, pickle it, then create another one by
// unpickling. The new one should match the old one except for fields
// that aren't pickled, which should be set to default values.
@@ -147,118 +98,5 @@ TEST(SerializedNavigationEntryTest, Pickle) {
EXPECT_EQ(0U, new_navigation.redirect_chain().size());
}
-// Create a SerializedNavigationEntry, then create a sync protocol buffer from
-// it. The protocol buffer should have matching fields to the
-// SerializedNavigationEntry (when applicable).
-TEST(SerializedNavigationEntryTest, ToSyncData) {
- const SerializedNavigationEntry navigation =
- SerializedNavigationEntryTestHelper::CreateNavigationForTest();
- const sync_pb::TabNavigation sync_data = navigation.ToSyncData();
-
- EXPECT_EQ(test_data::kVirtualURL.spec(), sync_data.virtual_url());
- EXPECT_EQ(test_data::kReferrerURL.spec(), sync_data.referrer());
- EXPECT_EQ(test_data::kTitle, base::ASCIIToUTF16(sync_data.title()));
- EXPECT_EQ(sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME,
- sync_data.page_transition());
- EXPECT_TRUE(sync_data.has_redirect_type());
- EXPECT_EQ(test_data::kUniqueID, sync_data.unique_id());
- EXPECT_EQ(syncer::TimeToProtoTime(test_data::kTimestamp),
- sync_data.timestamp_msec());
- EXPECT_EQ(test_data::kTimestamp.ToInternalValue(), sync_data.global_id());
- EXPECT_EQ(test_data::kFaviconURL.spec(), sync_data.favicon_url());
- EXPECT_EQ(test_data::kHttpStatusCode, sync_data.http_status_code());
- // The proto navigation redirects don't include the final chain entry
- // (because it didn't redirect) so the lengths should differ by 1.
- ASSERT_EQ(3, sync_data.navigation_redirect_size() + 1);
- EXPECT_EQ(test_data::kRedirectURL0.spec(),
- sync_data.navigation_redirect(0).url());
- EXPECT_EQ(test_data::kRedirectURL1.spec(),
- sync_data.navigation_redirect(1).url());
- EXPECT_FALSE(sync_data.has_last_navigation_redirect_url());
- EXPECT_FALSE(sync_data.has_replaced_navigation());
-}
-
-// Specifically test the |replaced_navigation| field, which should be populated
-// when the navigation entry has been replaced by another entry (e.g.
-// history.pushState()).
-TEST(SerializedNavigationEntryTest, ReplacedNavigation) {
- const GURL kReplacedURL = GURL("http://replaced-url.com");
- const int kReplacedTimestampMs = 79;
- const ui::PageTransition kReplacedPageTransition =
- ui::PAGE_TRANSITION_AUTO_BOOKMARK;
-
- SerializedNavigationEntry navigation =
- SerializedNavigationEntryTestHelper::CreateNavigationForTest();
- SerializedNavigationEntryTestHelper::SetReplacedEntryData(
- {kReplacedURL, syncer::ProtoTimeToTime(kReplacedTimestampMs),
- kReplacedPageTransition},
- &navigation);
-
- const sync_pb::TabNavigation sync_data = navigation.ToSyncData();
- EXPECT_TRUE(sync_data.has_replaced_navigation());
- EXPECT_EQ(kReplacedURL.spec(),
- sync_data.replaced_navigation().first_committed_url());
- EXPECT_EQ(kReplacedTimestampMs,
- sync_data.replaced_navigation().first_timestamp_msec());
- EXPECT_EQ(sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK,
- sync_data.replaced_navigation().first_page_transition());
-}
-
-// Test that the last_navigation_redirect_url is set when needed. This test is
-// just like the above, but with a different virtual_url. Create a
-// SerializedNavigationEntry, then create a sync protocol buffer from it. The
-// protocol buffer should have a last_navigation_redirect_url.
-TEST(SerializedNavigationEntryTest, LastNavigationRedirectUrl) {
- SerializedNavigationEntry navigation =
- SerializedNavigationEntryTestHelper::CreateNavigationForTest();
- SerializedNavigationEntryTestHelper::SetVirtualURL(
- test_data::kOtherURL, &navigation);
-
- const sync_pb::TabNavigation sync_data = navigation.ToSyncData();
- EXPECT_TRUE(sync_data.has_last_navigation_redirect_url());
- EXPECT_EQ(test_data::kVirtualURL.spec(),
- sync_data.last_navigation_redirect_url());
-
- // The redirect chain should be the same as in the above test.
- ASSERT_EQ(3, sync_data.navigation_redirect_size() + 1);
- EXPECT_EQ(test_data::kRedirectURL0.spec(),
- sync_data.navigation_redirect(0).url());
- EXPECT_EQ(test_data::kRedirectURL1.spec(),
- sync_data.navigation_redirect(1).url());
-}
-
-// Ensure all transition types and qualifiers are converted to/from the sync
-// SerializedNavigationEntry representation properly.
-TEST(SerializedNavigationEntryTest, TransitionTypes) {
- SerializedNavigationEntry navigation =
- SerializedNavigationEntryTestHelper::CreateNavigationForTest();
-
- for (uint32_t core_type = ui::PAGE_TRANSITION_LINK;
- core_type < ui::PAGE_TRANSITION_LAST_CORE; ++core_type) {
- // Because qualifier is a uint32_t, left shifting will eventually overflow
- // and hit zero again. SERVER_REDIRECT, as the last qualifier and also
- // in place of the sign bit, is therefore the last transition before
- // breaking.
- for (uint32_t qualifier = ui::PAGE_TRANSITION_FORWARD_BACK; qualifier != 0;
- qualifier <<= 1) {
- if (qualifier == 0x08000000)
- continue; // 0x08000000 is not a valid qualifier.
- ui::PageTransition transition =
- ui::PageTransitionFromInt(core_type | qualifier);
- SerializedNavigationEntryTestHelper::SetTransitionType(
- transition, &navigation);
-
- const sync_pb::TabNavigation& sync_data = navigation.ToSyncData();
- const SerializedNavigationEntry& constructed_nav =
- SerializedNavigationEntry::FromSyncData(test_data::kIndex, sync_data);
- const ui::PageTransition constructed_transition =
- constructed_nav.transition_type();
-
- EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
- constructed_transition, transition));
- }
- }
-}
-
} // namespace
} // namespace sessions
diff --git a/chromium/components/sessions/core/session_id.cc b/chromium/components/sessions/core/session_id.cc
index dcd49218731..83d6880d163 100644
--- a/chromium/components/sessions/core/session_id.cc
+++ b/chromium/components/sessions/core/session_id.cc
@@ -6,11 +6,11 @@
#include <ostream>
-static SessionID::id_type next_id = 1;
+#include "components/sessions/core/session_id_generator.h"
// static
SessionID SessionID::NewUnique() {
- return SessionID(next_id++);
+ return sessions::SessionIdGenerator::GetInstance()->NewUnique();
}
std::ostream& operator<<(std::ostream& out, SessionID id) {
diff --git a/chromium/components/sessions/core/session_id.h b/chromium/components/sessions/core/session_id.h
index 0941baed0f4..47b240195fe 100644
--- a/chromium/components/sessions/core/session_id.h
+++ b/chromium/components/sessions/core/session_id.h
@@ -17,8 +17,8 @@ class SESSIONS_EXPORT SessionID {
public:
typedef int32_t id_type;
- // Creates a new instance representing an ID that has never been used in the
- // current session. The same value may be instantiated in future sessions.
+ // Creates a new instance representing an ID that has never been used before
+ // locally (even across browser restarts).
static SessionID NewUnique();
// Special value representing a null-like, invalid ID. It's the only value
diff --git a/chromium/components/sessions/core/session_id_generator.cc b/chromium/components/sessions/core/session_id_generator.cc
new file mode 100644
index 00000000000..b74b809cac6
--- /dev/null
+++ b/chromium/components/sessions/core/session_id_generator.cc
@@ -0,0 +1,103 @@
+// 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/sessions/core/session_id_generator.h"
+
+#include "base/bind.h"
+#include "base/rand_util.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+
+namespace sessions {
+namespace {
+
+const char kLastValuePref[] = "session_id_generator_last_value";
+// On startup, we increment the internal counter by |kCautionaryIdPadding| to
+// mitigate issues during ungraceful shutdown, where prefs might not have
+// managed to persist the latest generated session IDs, but other databases
+// (e.g. sync) might have stored those IDs.
+const int kCautionaryIdPadding = 50;
+
+SessionID::id_type DefaultRandGenerator() {
+ return base::RandGenerator(std::numeric_limits<SessionID::id_type>::max());
+}
+
+} // namespace
+
+// static
+SessionIdGenerator* SessionIdGenerator::GetInstance() {
+ return base::Singleton<SessionIdGenerator>::get();
+}
+
+// static
+void SessionIdGenerator::RegisterPrefs(PrefRegistrySimple* prefs) {
+ prefs->RegisterInt64Pref(kLastValuePref, 0);
+}
+
+void SessionIdGenerator::Init(PrefService* local_state) {
+ DCHECK(local_state);
+ DCHECK(!local_state_);
+ DCHECK_EQ(0, last_value_) << "NewUnique() used before Init()";
+
+ local_state_ = local_state;
+ last_value_ = local_state_->GetInt64(kLastValuePref);
+ if (last_value_ <= 0) {
+ last_value_ = rand_generator_.Run();
+ }
+
+ // Increment by a constant to mitigate issues during ungraceful shutdown,
+ // where prefs might not have managed to persist the latest generated session
+ // IDs, but other databases (e.g. sync) might have stored those IDs.
+ IncrementValueBy(kCautionaryIdPadding);
+}
+
+void SessionIdGenerator::Shutdown() {
+ local_state_ = nullptr;
+ last_value_ = 0;
+}
+
+SessionID SessionIdGenerator::NewUnique() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // Init() should have been called in production (which initializes
+ // |local_state_|), but for test convenience, we allow operating even without
+ // underlying prefs.
+ if (local_state_) {
+ IncrementValueBy(1);
+ local_state_->SetInt64(kLastValuePref, last_value_);
+ } else {
+ // Test-only path. Will CHECK-fail if Init() is called later.
+ ++last_value_;
+ }
+ DCHECK(SessionID::IsValidValue(last_value_));
+ return SessionID::FromSerializedValue(last_value_);
+}
+
+// static
+std::string SessionIdGenerator::GetLastValuePrefNameForTest() {
+ return kLastValuePref;
+}
+
+void SessionIdGenerator::SetRandomGeneratorForTest(
+ const RandomGenerator& rand_generator) {
+ rand_generator_ = rand_generator;
+}
+
+SessionIdGenerator::SessionIdGenerator()
+ : local_state_(nullptr),
+ last_value_(0),
+ rand_generator_(base::BindRepeating(&DefaultRandGenerator)) {}
+
+SessionIdGenerator::~SessionIdGenerator() {}
+
+void SessionIdGenerator::IncrementValueBy(int increment) {
+ DCHECK_LT(0, increment);
+ DCHECK_LE(0, last_value_);
+ if (last_value_ >
+ std::numeric_limits<SessionID::id_type>::max() - increment) {
+ last_value_ = 0;
+ }
+ last_value_ += increment;
+}
+
+} // namespace sessions
diff --git a/chromium/components/sessions/core/session_id_generator.h b/chromium/components/sessions/core/session_id_generator.h
new file mode 100644
index 00000000000..9555a59b80e
--- /dev/null
+++ b/chromium/components/sessions/core/session_id_generator.h
@@ -0,0 +1,64 @@
+// 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_SESSIONS_CORE_SESSION_ID_GENERATOR_H_
+#define COMPONENTS_SESSIONS_CORE_SESSION_ID_GENERATOR_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/sequence_checker.h"
+#include "components/sessions/core/session_id.h"
+#include "components/sessions/core/sessions_export.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace sessions {
+
+class SESSIONS_EXPORT SessionIdGenerator {
+ public:
+ // Returns the singleton instance of this class.
+ static SessionIdGenerator* GetInstance();
+
+ // Register preferences used by this class.
+ static void RegisterPrefs(PrefRegistrySimple* prefs);
+
+ // Initialization of the singleton. Must be called exactly once. |local_state|
+ // must not be null and must exist until Shutdown() is called.
+ void Init(PrefService* local_state);
+ void Shutdown();
+
+ // Creates a new instance representing an ID that has never been used before
+ // locally (even across browser restarts). Must be preceded by Init().
+ SessionID NewUnique();
+
+ // Preference name used to persist the last ID.
+ static std::string GetLastValuePrefNameForTest();
+
+ // Internal random function injection for tests.
+ using RandomGenerator = base::RepeatingCallback<SessionID::id_type()>;
+ void SetRandomGeneratorForTest(const RandomGenerator& rand_generator);
+
+ private:
+ friend struct base::DefaultSingletonTraits<SessionIdGenerator>;
+
+ SessionIdGenerator();
+ ~SessionIdGenerator();
+
+ void IncrementValueBy(int increment);
+
+ SEQUENCE_CHECKER(sequence_checker_);
+ PrefService* local_state_;
+ SessionID::id_type last_value_;
+
+ // Used to override the random number generator for tests.
+ RandomGenerator rand_generator_;
+
+ DISALLOW_COPY_AND_ASSIGN(SessionIdGenerator);
+};
+
+} // namespace sessions
+
+#endif // COMPONENTS_SESSIONS_CORE_SESSION_ID_GENERATOR_H_
diff --git a/chromium/components/sessions/core/session_id_generator_unittest.cc b/chromium/components/sessions/core/session_id_generator_unittest.cc
new file mode 100644
index 00000000000..476f43a6d2d
--- /dev/null
+++ b/chromium/components/sessions/core/session_id_generator_unittest.cc
@@ -0,0 +1,165 @@
+// 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/sessions/core/session_id_generator.h"
+
+#include <limits>
+
+#include "base/macros.h"
+#include "base/test/mock_callback.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sessions {
+namespace {
+
+using testing::Return;
+
+const int kExpectedIdPadding = 50;
+
+class SessionIdGeneratorTest : public testing::Test {
+ protected:
+ SessionIdGeneratorTest() {
+ // Call Shutdown() in case other tests outside this file have forgotten to
+ // do so, which would leak undesired state across tests.
+ SessionIdGenerator::GetInstance()->Shutdown();
+ SessionIdGenerator::RegisterPrefs(prefs_.registry());
+ }
+
+ ~SessionIdGeneratorTest() override {
+ SessionIdGenerator::GetInstance()->Shutdown();
+ }
+
+ void WriteLastValueToPrefs(int64_t value) {
+ prefs_.SetInt64(SessionIdGenerator::GetLastValuePrefNameForTest(), value);
+ }
+
+ int64_t ReadLastValueFromPrefs() {
+ return prefs_.GetInt64(SessionIdGenerator::GetLastValuePrefNameForTest());
+ }
+
+ TestingPrefServiceSimple prefs_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SessionIdGeneratorTest);
+};
+
+TEST_F(SessionIdGeneratorTest, ShouldGenerateContiguousIds) {
+ SessionIdGenerator* generator = SessionIdGenerator::GetInstance();
+ generator->Init(&prefs_);
+ SessionID session_id1 = generator->NewUnique();
+ SessionID session_id2 = generator->NewUnique();
+ SessionID session_id3 = generator->NewUnique();
+ EXPECT_EQ(session_id2.id(), session_id1.id() + 1);
+ EXPECT_EQ(session_id3.id(), session_id2.id() + 1);
+ EXPECT_EQ(session_id3.id(), ReadLastValueFromPrefs());
+}
+
+TEST_F(SessionIdGeneratorTest, ShouldInitializeWithRandomValue) {
+ base::MockCallback<SessionIdGenerator::RandomGenerator> random_generator;
+ SessionIdGenerator* generator = SessionIdGenerator::GetInstance();
+ generator->SetRandomGeneratorForTest(random_generator.Get());
+
+ EXPECT_CALL(random_generator, Run()).WillOnce(Return(123));
+ generator->Init(&prefs_);
+ EXPECT_EQ(123 + 1 + kExpectedIdPadding, generator->NewUnique().id());
+
+ // Mimic a browser restart with cleared prefs.
+ generator->Shutdown();
+ WriteLastValueToPrefs(-1);
+ EXPECT_CALL(random_generator, Run()).WillOnce(Return(19));
+ generator->Init(&prefs_);
+
+ EXPECT_EQ(19 + 1 + kExpectedIdPadding, generator->NewUnique().id());
+}
+
+TEST_F(SessionIdGeneratorTest, ShouldCornerCasesInRandomFunc) {
+ base::MockCallback<SessionIdGenerator::RandomGenerator> random_generator;
+ SessionIdGenerator* generator = SessionIdGenerator::GetInstance();
+ generator->SetRandomGeneratorForTest(random_generator.Get());
+
+ // Exercise smallest value in range.
+ EXPECT_CALL(random_generator, Run()).WillOnce(Return(0));
+ generator->Init(&prefs_);
+ EXPECT_EQ(0 + 1 + kExpectedIdPadding, generator->NewUnique().id());
+
+ // Exercise maximum value in range.
+ generator->Shutdown();
+ WriteLastValueToPrefs(-1);
+ EXPECT_CALL(random_generator, Run())
+ .WillOnce(Return(std::numeric_limits<SessionID::id_type>::max()));
+ generator->Init(&prefs_);
+ EXPECT_EQ(1 + kExpectedIdPadding, generator->NewUnique().id());
+
+ // Exercise maximum value minus one in range.
+ generator->Shutdown();
+ WriteLastValueToPrefs(-1);
+ EXPECT_CALL(random_generator, Run())
+ .WillOnce(Return(std::numeric_limits<SessionID::id_type>::max() - 1));
+ generator->Init(&prefs_);
+ EXPECT_EQ(1 + kExpectedIdPadding, generator->NewUnique().id());
+
+ // Exercise a random value which is exactly |kExpectedIdPadding| less than
+ // the maximum value in the range.
+ generator->Shutdown();
+ WriteLastValueToPrefs(-1);
+ EXPECT_CALL(random_generator, Run())
+ .WillOnce(Return(std::numeric_limits<SessionID::id_type>::max() -
+ kExpectedIdPadding));
+ generator->Init(&prefs_);
+ EXPECT_EQ(1, generator->NewUnique().id());
+
+ // Exercise a random value which is |kExpectedIdPadding-1| less than the
+ // maximum value in the range (no overflow expected).
+ generator->Shutdown();
+ WriteLastValueToPrefs(-1);
+ EXPECT_CALL(random_generator, Run())
+ .WillOnce(Return(std::numeric_limits<SessionID::id_type>::max() -
+ kExpectedIdPadding - 1));
+ generator->Init(&prefs_);
+ EXPECT_EQ(std::numeric_limits<SessionID::id_type>::max(),
+ generator->NewUnique().id());
+}
+
+TEST_F(SessionIdGeneratorTest, ShouldRestoreAndPadLastValueFromPrefs) {
+ WriteLastValueToPrefs(7);
+ SessionIdGenerator* generator = SessionIdGenerator::GetInstance();
+ generator->Init(&prefs_);
+ SessionID session_id = generator->NewUnique();
+ EXPECT_EQ(8 + kExpectedIdPadding, session_id.id());
+ EXPECT_EQ(8 + kExpectedIdPadding, ReadLastValueFromPrefs());
+}
+
+TEST_F(SessionIdGeneratorTest, ShouldHandleOverflowDuringGeneration) {
+ WriteLastValueToPrefs(std::numeric_limits<SessionID::id_type>::max() -
+ kExpectedIdPadding - 3);
+ SessionIdGenerator* generator = SessionIdGenerator::GetInstance();
+ generator->Init(&prefs_);
+ ASSERT_EQ(std::numeric_limits<SessionID::id_type>::max() - 2,
+ generator->NewUnique().id());
+ EXPECT_EQ(std::numeric_limits<SessionID::id_type>::max() - 1,
+ generator->NewUnique().id());
+ EXPECT_EQ(std::numeric_limits<SessionID::id_type>::max(),
+ generator->NewUnique().id());
+ EXPECT_EQ(1, generator->NewUnique().id());
+}
+
+TEST_F(SessionIdGeneratorTest, ShouldHandleOverflowDuringPadding) {
+ WriteLastValueToPrefs(std::numeric_limits<SessionID::id_type>::max() - 2);
+ SessionIdGenerator* generator = SessionIdGenerator::GetInstance();
+ generator->Init(&prefs_);
+ EXPECT_EQ(kExpectedIdPadding + 1, generator->NewUnique().id());
+}
+
+// Verifies correctness of the test-only codepath.
+TEST(SessionIdGeneratorWithoutInitTest, ShouldStartFromOneAndIncrement) {
+ SessionIdGenerator* generator = SessionIdGenerator::GetInstance();
+ EXPECT_EQ(1, generator->NewUnique().id());
+ EXPECT_EQ(2, generator->NewUnique().id());
+ generator->Shutdown();
+}
+
+} // namespace
+} // namespace sessions
diff --git a/chromium/components/sessions/core/session_types.cc b/chromium/components/sessions/core/session_types.cc
index ff8a23eb4a8..066099da452 100644
--- a/chromium/components/sessions/core/session_types.cc
+++ b/chromium/components/sessions/core/session_types.cc
@@ -10,8 +10,6 @@
namespace sessions {
-//using class SerializedNavigationEntry;
-
// SessionTab -----------------------------------------------------------------
SessionTab::SessionTab()
@@ -24,38 +22,6 @@ SessionTab::SessionTab()
SessionTab::~SessionTab() {
}
-void SessionTab::SetFromSyncData(const sync_pb::SessionTab& sync_data,
- base::Time timestamp) {
- window_id = SessionID::FromSerializedValue(sync_data.window_id());
- tab_id = SessionID::FromSerializedValue(sync_data.tab_id());
- tab_visual_index = sync_data.tab_visual_index();
- current_navigation_index = sync_data.current_navigation_index();
- pinned = sync_data.pinned();
- extension_app_id = sync_data.extension_app_id();
- user_agent_override.clear();
- this->timestamp = timestamp;
- navigations.clear();
- for (int i = 0; i < sync_data.navigation_size(); ++i) {
- navigations.push_back(
- SerializedNavigationEntry::FromSyncData(i, sync_data.navigation(i)));
- }
- session_storage_persistent_id.clear();
-}
-
-sync_pb::SessionTab SessionTab::ToSyncData() const {
- sync_pb::SessionTab sync_data;
- sync_data.set_tab_id(tab_id.id());
- sync_data.set_window_id(window_id.id());
- sync_data.set_tab_visual_index(tab_visual_index);
- sync_data.set_current_navigation_index(current_navigation_index);
- sync_data.set_pinned(pinned);
- sync_data.set_extension_app_id(extension_app_id);
- for (const SerializedNavigationEntry& navigation : navigations) {
- *sync_data.add_navigation() = navigation.ToSyncData();
- }
- return sync_data;
-}
-
// SessionWindow ---------------------------------------------------------------
SessionWindow::SessionWindow()
diff --git a/chromium/components/sessions/core/session_types.h b/chromium/components/sessions/core/session_types.h
index 5ac0325d691..b161ca538dd 100644
--- a/chromium/components/sessions/core/session_types.h
+++ b/chromium/components/sessions/core/session_types.h
@@ -16,7 +16,6 @@
#include "components/sessions/core/serialized_navigation_entry.h"
#include "components/sessions/core/session_id.h"
#include "components/sessions/core/sessions_export.h"
-#include "components/sync/protocol/session_specifics.pb.h"
#include "components/variations/variations_associated_data.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/geometry/rect.h"
@@ -40,20 +39,6 @@ struct SESSIONS_EXPORT SessionTab {
static_cast<int>(navigations.size() - 1)));
}
- // Set all the fields of this object from the given sync data and
- // timestamp. Uses SerializedNavigationEntry::FromSyncData to fill
- // |navigations|. Note that the sync protocol buffer doesn't
- // contain all SerializedNavigationEntry fields.
- void SetFromSyncData(const sync_pb::SessionTab& sync_data,
- base::Time timestamp);
-
- // Convert this object into its sync protocol buffer equivalent.
- // Uses SerializedNavigationEntry::ToSyncData to convert |navigations|. Note
- // that the protocol buffer doesn't contain all SerializedNavigationEntry
- // fields, and that the returned protocol buffer doesn't have any
- // favicon data.
- sync_pb::SessionTab ToSyncData() const;
-
// Unique id of the window.
SessionID window_id;
diff --git a/chromium/components/sessions/core/session_types_unittest.cc b/chromium/components/sessions/core/session_types_unittest.cc
deleted file mode 100644
index e672c9e82aa..00000000000
--- a/chromium/components/sessions/core/session_types_unittest.cc
+++ /dev/null
@@ -1,111 +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/sessions/core/session_types.h"
-
-#include <cstddef>
-#include <string>
-
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "components/sessions/core/serialized_navigation_entry_test_helper.h"
-#include "components/sync/base/time.h"
-#include "components/sync/protocol/session_specifics.pb.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/page_transition_types.h"
-#include "url/gurl.h"
-
-namespace {
-
-// Create a typical SessionTab protocol buffer and set an existing
-// SessionTab from it. The data from the protocol buffer should
-// clobber the existing data.
-TEST(SessionTab, FromSyncData) {
- sync_pb::SessionTab sync_data;
- sync_data.set_tab_id(5);
- sync_data.set_window_id(10);
- sync_data.set_tab_visual_index(13);
- sync_data.set_current_navigation_index(3);
- sync_data.set_pinned(true);
- sync_data.set_extension_app_id("app_id");
- for (int i = 0; i < 5; ++i) {
- sync_pb::TabNavigation* navigation = sync_data.add_navigation();
- navigation->set_virtual_url("http://foo/" + base::IntToString(i));
- navigation->set_referrer("referrer");
- navigation->set_title("title");
- navigation->set_page_transition(sync_pb::SyncEnums_PageTransition_TYPED);
- }
-
- sessions::SessionTab tab;
- tab.window_id = SessionID::FromSerializedValue(100);
- tab.tab_id = SessionID::FromSerializedValue(100);
- tab.tab_visual_index = 100;
- tab.current_navigation_index = 1000;
- tab.pinned = false;
- tab.extension_app_id = "fake";
- tab.user_agent_override = "fake";
- tab.timestamp = base::Time::FromInternalValue(100);
- tab.navigations.resize(100);
- tab.session_storage_persistent_id = "fake";
-
- tab.SetFromSyncData(sync_data, base::Time::FromInternalValue(5u));
- EXPECT_EQ(10, tab.window_id.id());
- EXPECT_EQ(5, tab.tab_id.id());
- EXPECT_EQ(13, tab.tab_visual_index);
- EXPECT_EQ(3, tab.current_navigation_index);
- EXPECT_TRUE(tab.pinned);
- EXPECT_EQ("app_id", tab.extension_app_id);
- EXPECT_TRUE(tab.user_agent_override.empty());
- EXPECT_EQ(5u, tab.timestamp.ToInternalValue());
- ASSERT_EQ(5u, tab.navigations.size());
- for (int i = 0; i < 5; ++i) {
- EXPECT_EQ(i, tab.navigations[i].index());
- EXPECT_EQ(GURL("referrer"), tab.navigations[i].referrer_url());
- EXPECT_EQ(base::ASCIIToUTF16("title"),tab.navigations[i].title());
- EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
- tab.navigations[i].transition_type(), ui::PAGE_TRANSITION_TYPED));
- EXPECT_EQ(GURL("http://foo/" + base::IntToString(i)),
- tab.navigations[i].virtual_url());
- }
- EXPECT_TRUE(tab.session_storage_persistent_id.empty());
-}
-
-TEST(SessionTab, ToSyncData) {
- sessions::SessionTab tab;
- tab.window_id = SessionID::FromSerializedValue(10);
- tab.tab_id = SessionID::FromSerializedValue(5);
- tab.tab_visual_index = 13;
- tab.current_navigation_index = 3;
- tab.pinned = true;
- tab.extension_app_id = "app_id";
- tab.user_agent_override = "fake";
- tab.timestamp = base::Time::FromInternalValue(100);
- for (int i = 0; i < 5; ++i) {
- tab.navigations.push_back(
- sessions::SerializedNavigationEntryTestHelper::CreateNavigation(
- "http://foo/" + base::IntToString(i), "title"));
- }
- tab.session_storage_persistent_id = "fake";
-
- const sync_pb::SessionTab& sync_data = tab.ToSyncData();
- EXPECT_EQ(5, sync_data.tab_id());
- EXPECT_EQ(10, sync_data.window_id());
- EXPECT_EQ(13, sync_data.tab_visual_index());
- EXPECT_EQ(3, sync_data.current_navigation_index());
- EXPECT_TRUE(sync_data.pinned());
- EXPECT_EQ("app_id", sync_data.extension_app_id());
- ASSERT_EQ(5, sync_data.navigation_size());
- for (int i = 0; i < 5; ++i) {
- EXPECT_EQ(tab.navigations[i].virtual_url().spec(),
- sync_data.navigation(i).virtual_url());
- EXPECT_EQ(base::UTF16ToUTF8(tab.navigations[i].title()),
- sync_data.navigation(i).title());
- }
- EXPECT_FALSE(sync_data.has_favicon());
- EXPECT_FALSE(sync_data.has_favicon_type());
- EXPECT_FALSE(sync_data.has_favicon_source());
-}
-
-} // namespace
diff --git a/chromium/components/sessions/core/tab_restore_service_helper.h b/chromium/components/sessions/core/tab_restore_service_helper.h
index 19676251cb9..4372d8d89c2 100644
--- a/chromium/components/sessions/core/tab_restore_service_helper.h
+++ b/chromium/components/sessions/core/tab_restore_service_helper.h
@@ -12,6 +12,7 @@
#include "base/observer_list.h"
#include "base/time/time.h"
#include "base/trace_event/memory_dump_provider.h"
+#include "build/build_config.h"
#include "components/sessions/core/session_id.h"
#include "components/sessions/core/session_types.h"
#include "components/sessions/core/sessions_export.h"
@@ -62,7 +63,12 @@ class SESSIONS_EXPORT TabRestoreServiceHelper
enum {
// Max number of entries we'll keep around.
+#if defined(OS_ANDROID)
+ // Android keeps at most 5 recent tabs.
+ kMaxEntries = 5,
+#else
kMaxEntries = 25,
+#endif
};
// Creates a new TabRestoreServiceHelper and provides an object that provides
diff --git a/chromium/components/signin/DEPS b/chromium/components/signin/DEPS
index 20e335b0eba..813a64278b1 100644
--- a/chromium/components/signin/DEPS
+++ b/chromium/components/signin/DEPS
@@ -13,3 +13,9 @@ include_rules = [
"+net",
"+sql",
]
+
+specific_include_rules = {
+ "test_signin_client\.h": [
+ "+services/network/test"
+ ]
+} \ No newline at end of file
diff --git a/chromium/components/signin/core/browser/BUILD.gn b/chromium/components/signin/core/browser/BUILD.gn
index 98eed32048c..dfdb87c2cd1 100644
--- a/chromium/components/signin/core/browser/BUILD.gn
+++ b/chromium/components/signin/core/browser/BUILD.gn
@@ -81,8 +81,6 @@ static_library("browser") {
"gaia_cookie_manager_service.h",
"mirror_account_reconcilor_delegate.cc",
"mirror_account_reconcilor_delegate.h",
- "profile_identity_provider.cc",
- "profile_identity_provider.h",
"profile_management_switches.cc",
"profile_management_switches.h",
"profile_oauth2_token_service.cc",
@@ -136,6 +134,7 @@ static_library("browser") {
"//components/prefs",
"//google_apis",
"//net",
+ "//services/network/public/cpp",
"//ui/gfx",
"//url",
]
@@ -149,6 +148,7 @@ static_library("browser") {
"//components/pref_registry",
"//components/webdata/common",
"//crypto",
+ "//services/network/public/cpp",
"//skia",
"//sql",
"//third_party/icu",
@@ -194,10 +194,6 @@ static_library("test_support") {
"fake_profile_oauth2_token_service.h",
"fake_signin_manager.cc",
"fake_signin_manager.h",
- "scoped_account_consistency.cc",
- "scoped_account_consistency.h",
- "scoped_unified_consent.cc",
- "scoped_unified_consent.h",
"test_signin_client.cc",
"test_signin_client.h",
]
@@ -215,6 +211,8 @@ static_library("test_support") {
"//components/webdata/common",
"//google_apis:test_support",
"//net:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//testing/gtest",
]
}
@@ -229,7 +227,6 @@ source_set("unit_tests") {
"avatar_icon_util_unittest.cc",
"dice_account_reconcilor_delegate_unittest.cc",
"gaia_cookie_manager_service_unittest.cc",
- "profile_management_switches_unittest.cc",
"signin_error_controller_unittest.cc",
"signin_header_helper_unittest.cc",
"signin_investigator_unittest.cc",
diff --git a/chromium/components/signin/core/browser/DEPS b/chromium/components/signin/core/browser/DEPS
index c946f6c1aa9..31c61cfa56a 100644
--- a/chromium/components/signin/core/browser/DEPS
+++ b/chromium/components/signin/core/browser/DEPS
@@ -6,6 +6,8 @@ include_rules = [
"+components/metrics",
"+google/cacheinvalidation",
"+jni",
+ "+services/network/public/cpp",
+ "+services/network/test",
"+third_party/re2",
"+ui/gfx",
]
diff --git a/chromium/components/signin/core/browser/account_fetcher_service.cc b/chromium/components/signin/core/browser/account_fetcher_service.cc
index 255c520d0ec..66dc8beef87 100644
--- a/chromium/components/signin/core/browser/account_fetcher_service.cc
+++ b/chromium/components/signin/core/browser/account_fetcher_service.cc
@@ -21,14 +21,13 @@
#include "components/signin/core/browser/signin_client.h"
#include "components/signin/core/browser/signin_switches.h"
#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace {
const base::TimeDelta kRefreshFromTokenServiceDelay =
base::TimeDelta::FromHours(24);
-constexpr int kAccountImageDownloadSize = 64;
-
bool AccountSupportsUserInfo(const std::string& account_id) {
// Supervised users use a specially scoped token which when used for general
// purposes causes the token service to raise spurious auth errors.
@@ -43,6 +42,8 @@ bool AccountSupportsUserInfo(const std::string& account_id) {
const char AccountFetcherService::kLastUpdatePref[] =
"account_tracker_service_last_update";
+const int AccountFetcherService::kAccountImageDownloadSize = 64;
+
// AccountFetcherService implementation
AccountFetcherService::AccountFetcherService()
: account_tracker_service_(nullptr),
@@ -221,7 +222,7 @@ void AccountFetcherService::StartFetchingChildInfo(
const std::string& account_id) {
child_info_request_ = ChildAccountInfoFetcher::CreateFrom(
child_request_account_id_, this, token_service_,
- signin_client_->GetURLRequestContext(), invalidation_service_);
+ signin_client_->GetURLLoaderFactory(), invalidation_service_);
}
void AccountFetcherService::ResetChildInfo() {
@@ -266,7 +267,7 @@ AccountFetcherService::GetOrCreateImageFetcher() {
// not be available yet when |Initialize| is called.
if (!image_fetcher_) {
image_fetcher_ = std::make_unique<image_fetcher::ImageFetcherImpl>(
- std::move(image_decoder_), signin_client_->GetURLRequestContext());
+ std::move(image_decoder_), signin_client_->GetURLLoaderFactory());
}
return image_fetcher_.get();
}
diff --git a/chromium/components/signin/core/browser/account_fetcher_service.h b/chromium/components/signin/core/browser/account_fetcher_service.h
index 993fa675821..9958ff8b7c2 100644
--- a/chromium/components/signin/core/browser/account_fetcher_service.h
+++ b/chromium/components/signin/core/browser/account_fetcher_service.h
@@ -51,6 +51,9 @@ class AccountFetcherService : public KeyedService,
// time the AccountTrackerService was updated.
static const char kLastUpdatePref[];
+ // Size used for downloading account pictures. Exposed for tests.
+ static const int kAccountImageDownloadSize;
+
AccountFetcherService();
~AccountFetcherService() override;
diff --git a/chromium/components/signin/core/browser/account_info.cc b/chromium/components/signin/core/browser/account_info.cc
index 0527cdc0f46..e7e061ebc0d 100644
--- a/chromium/components/signin/core/browser/account_info.cc
+++ b/chromium/components/signin/core/browser/account_info.cc
@@ -55,9 +55,10 @@ bool AccountInfo::UpdateWith(const AccountInfo& other) {
return modified;
}
-AccountId AccountInfo::GetAccountId() const {
- if (IsEmpty())
+AccountId AccountIdFromAccountInfo(const AccountInfo& account_info) {
+ if (account_info.IsEmpty())
return EmptyAccountId();
- DCHECK(!email.empty() && !gaia.empty());
- return AccountId::FromUserEmailGaiaId(email, gaia);
-} \ No newline at end of file
+
+ DCHECK(!account_info.email.empty() && !account_info.gaia.empty());
+ return AccountId::FromUserEmailGaiaId(account_info.email, account_info.gaia);
+}
diff --git a/chromium/components/signin/core/browser/account_info.h b/chromium/components/signin/core/browser/account_info.h
index 457c1a15722..b97189e34e5 100644
--- a/chromium/components/signin/core/browser/account_info.h
+++ b/chromium/components/signin/core/browser/account_info.h
@@ -34,9 +34,9 @@ struct AccountInfo {
// Updates the empty fields of |this| with |other|. Returns whether at least
// one field was updated.
bool UpdateWith(const AccountInfo& other);
-
- // Returns AccountId populated from the account info.
- AccountId GetAccountId() const;
};
+// Returns AccountID populated from |account_info|.
+AccountId AccountIdFromAccountInfo(const AccountInfo& account_info);
+
#endif // COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_INFO_H_
diff --git a/chromium/components/signin/core/browser/account_investigator_unittest.cc b/chromium/components/signin/core/browser/account_investigator_unittest.cc
index 7c1f2080131..aef935b45fe 100644
--- a/chromium/components/signin/core/browser/account_investigator_unittest.cc
+++ b/chromium/components/signin/core/browser/account_investigator_unittest.cc
@@ -10,7 +10,7 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/timer/timer.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/signin/core/browser/account_tracker_service.h"
@@ -23,7 +23,6 @@
#include "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/google_service_auth_error.h"
-#include "net/url_request/test_url_fetcher_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::HistogramTester;
@@ -44,13 +43,13 @@ class AccountInvestigatorTest : public testing::Test {
gaia_cookie_manager_service_(nullptr,
GaiaConstants::kChromeSource,
&signin_client_),
- investigator_(&gaia_cookie_manager_service_, &prefs_, &signin_manager_),
- fake_url_fetcher_factory_(nullptr) {
+ investigator_(&gaia_cookie_manager_service_,
+ &prefs_,
+ &signin_manager_) {
AccountTrackerService::RegisterPrefs(prefs_.registry());
AccountInvestigator::RegisterPrefs(prefs_.registry());
SigninManagerBase::RegisterProfilePrefs(prefs_.registry());
account_tracker_service_.Initialize(&signin_client_);
- gaia_cookie_manager_service_.Init(&fake_url_fetcher_factory_);
}
~AccountInvestigatorTest() override { investigator_.Shutdown(); }
@@ -161,7 +160,6 @@ class AccountInvestigatorTest : public testing::Test {
FakeSigninManager signin_manager_;
FakeGaiaCookieManagerService gaia_cookie_manager_service_;
AccountInvestigator investigator_;
- net::FakeURLFetcherFactory fake_url_fetcher_factory_;
std::map<ReportingType, std::string> suffix_ = {
{ReportingType::PERIODIC, "_Periodic"},
{ReportingType::ON_CHANGE, "_OnChange"}};
diff --git a/chromium/components/signin/core/browser/account_reconcilor.cc b/chromium/components/signin/core/browser/account_reconcilor.cc
index 8d62b9f3c17..813cdab659b 100644
--- a/chromium/components/signin/core/browser/account_reconcilor.cc
+++ b/chromium/components/signin/core/browser/account_reconcilor.cc
@@ -15,6 +15,7 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "components/signin/core/browser/account_reconcilor_delegate.h"
@@ -231,7 +232,7 @@ void AccountReconcilor::OnContentSettingChanged(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsType content_type,
- std::string resource_identifier) {
+ const std::string& resource_identifier) {
// If this is not a change to cookie settings, just ignore.
if (content_type != CONTENT_SETTINGS_TYPE_COOKIES)
return;
@@ -327,13 +328,11 @@ void AccountReconcilor::StartReconcile() {
reconcile_is_noop_ = true;
if (!timeout_.is_max()) {
- // This is NOT a repeating callback but to test it, we need a |MockTimer|,
- // which mocks |Timer| and not |OneShotTimer|. |Timer| currently does not
- // support a |OnceClosure|.
- timer_->Start(
- FROM_HERE, timeout_,
- base::BindRepeating(&AccountReconcilor::HandleReconcileTimeout,
- base::Unretained(this)));
+ // Keep using base::Bind() until base::OnceCallback get supported by
+ // base::OneShotTimer.
+ timer_->Start(FROM_HERE, timeout_,
+ base::Bind(&AccountReconcilor::HandleReconcileTimeout,
+ base::Unretained(this)));
}
const std::string& account_id = signin_manager_->GetAuthenticatedAccountId();
@@ -472,9 +471,7 @@ void AccountReconcilor::FinishReconcile(
int removed_from_cookie = 0;
for (size_t i = 0; i < number_gaia_accounts; ++i) {
if (gaia_accounts[i].valid &&
- chrome_accounts.end() == std::find(chrome_accounts.begin(),
- chrome_accounts.end(),
- gaia_accounts[i].id)) {
+ !base::ContainsValue(chrome_accounts, gaia_accounts[i].id)) {
++removed_from_cookie;
}
}
@@ -706,7 +703,7 @@ void AccountReconcilor::UnblockReconcile() {
}
void AccountReconcilor::set_timer_for_testing(
- std::unique_ptr<base::Timer> timer) {
+ std::unique_ptr<base::OneShotTimer> timer) {
timer_ = std::move(timer);
}
diff --git a/chromium/components/signin/core/browser/account_reconcilor.h b/chromium/components/signin/core/browser/account_reconcilor.h
index 8d17ff05150..09875297b6e 100644
--- a/chromium/components/signin/core/browser/account_reconcilor.h
+++ b/chromium/components/signin/core/browser/account_reconcilor.h
@@ -170,7 +170,7 @@ class AccountReconcilor : public KeyedService,
FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest,
DelegateTimeoutIsNotCalledIfTimeoutIsNotReached);
- void set_timer_for_testing(std::unique_ptr<base::Timer> timer);
+ void set_timer_for_testing(std::unique_ptr<base::OneShotTimer> timer);
bool IsRegisteredWithTokenService() const {
return registered_with_token_service_;
@@ -215,11 +215,10 @@ class AccountReconcilor : public KeyedService,
bool IsTokenServiceReady();
// Overriden from content_settings::Observer.
- void OnContentSettingChanged(
- const ContentSettingsPattern& primary_pattern,
- const ContentSettingsPattern& secondary_pattern,
- ContentSettingsType content_type,
- std::string resource_identifier) override;
+ void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ ContentSettingsType content_type,
+ const std::string& resource_identifier) override;
// Overriden from GaiaGookieManagerService::Observer.
void OnAddAccountToCookieCompleted(
@@ -306,7 +305,7 @@ class AccountReconcilor : public KeyedService,
// of reconciliation completing within a finite time. It is technically
// possible for account reconciliation to be running/waiting forever in cases
// such as a network connection not being present.
- std::unique_ptr<base::Timer> timer_;
+ std::unique_ptr<base::OneShotTimer> timer_;
base::TimeDelta timeout_;
DISALLOW_COPY_AND_ASSIGN(AccountReconcilor);
diff --git a/chromium/components/signin/core/browser/account_reconcilor_unittest.cc b/chromium/components/signin/core/browser/account_reconcilor_unittest.cc
index 101b94aa35c..e79b9e689d9 100644
--- a/chromium/components/signin/core/browser/account_reconcilor_unittest.cc
+++ b/chromium/components/signin/core/browser/account_reconcilor_unittest.cc
@@ -14,7 +14,7 @@
#include "base/run_loop.h"
#include "base/scoped_observer.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "base/timer/mock_timer.h"
#include "build/build_config.h"
@@ -113,7 +113,7 @@ class SpyReconcilorDelegate : public signin::AccountReconcilorDelegate {
// Introduce a dummy class creating the delegate internally, to avoid the move.
class DummyAccountReconcilorWithDelegate : public AccountReconcilor {
public:
- explicit DummyAccountReconcilorWithDelegate(
+ DummyAccountReconcilorWithDelegate(
ProfileOAuth2TokenService* token_service,
SigninManagerBase* signin_manager,
SigninClient* client,
@@ -132,7 +132,7 @@ class DummyAccountReconcilorWithDelegate : public AccountReconcilor {
// Takes ownership of |delegate|.
// gmock can't work with move only parameters.
- explicit DummyAccountReconcilorWithDelegate(
+ DummyAccountReconcilorWithDelegate(
ProfileOAuth2TokenService* token_service,
SigninManagerBase* signin_manager,
SigninClient* client,
@@ -238,13 +238,6 @@ class AccountReconcilorTest : public ::testing::Test {
}
base::HistogramTester* histogram_tester() { return &histogram_tester_; }
- void SetFakeResponse(const std::string& url,
- const std::string& data,
- net::HttpStatusCode code,
- net::URLRequestStatus::Status status) {
- url_fetcher_factory_.SetFakeResponse(GURL(url), data, code, status);
- }
-
MockAccountReconcilor* GetMockReconcilor();
MockAccountReconcilor* GetMockReconcilor(
std::unique_ptr<signin::AccountReconcilorDelegate> delegate);
@@ -282,7 +275,6 @@ class AccountReconcilorTest : public ::testing::Test {
FakeGaiaCookieManagerService cookie_manager_service_;
FakeSigninManagerForTesting signin_manager_;
std::unique_ptr<MockAccountReconcilor> mock_reconcilor_;
- net::FakeURLFetcherFactory url_fetcher_factory_;
base::HistogramTester histogram_tester_;
GURL get_check_connection_info_url_;
@@ -315,15 +307,13 @@ AccountReconcilorTest::AccountReconcilorTest()
GaiaConstants::kChromeSource,
&test_signin_client_),
#if defined(OS_CHROMEOS)
- signin_manager_(&test_signin_client_, &account_tracker_),
+ signin_manager_(&test_signin_client_, &account_tracker_) {
#else
signin_manager_(&test_signin_client_,
&token_service_,
&account_tracker_,
- &cookie_manager_service_),
+ &cookie_manager_service_) {
#endif
- url_fetcher_factory_(nullptr) {
- signin::RegisterAccountConsistencyProfilePrefs(pref_service_.registry());
AccountTrackerService::RegisterPrefs(pref_service_.registry());
SigninManagerBase::RegisterProfilePrefs(pref_service_.registry());
SigninManagerBase::RegisterPrefs(pref_service_.registry());
@@ -334,7 +324,6 @@ AccountReconcilorTest::AccountReconcilorTest()
GaiaConstants::kChromeSource);
account_tracker_.Initialize(&test_signin_client_);
- cookie_manager_service_.Init(&url_fetcher_factory_);
cookie_manager_service_.SetListAccountsResponseHttpNotFound();
signin_manager_.Initialize(nullptr);
@@ -2032,9 +2021,8 @@ TEST_F(AccountReconcilorTest, DelegateTimeoutIsCalled) {
SpyReconcilorDelegate* spy_delegate = spy_delegate0.get();
AccountReconcilor* reconcilor = GetMockReconcilor(std::move(spy_delegate0));
ASSERT_TRUE(reconcilor);
- auto timer0 = std::make_unique<base::MockTimer>(true /* retain_user_task */,
- false /* is_repeating */);
- base::MockTimer* timer = timer0.get();
+ auto timer0 = std::make_unique<base::MockOneShotTimer>();
+ base::MockOneShotTimer* timer = timer0.get();
reconcilor->set_timer_for_testing(std::move(timer0));
reconcilor->StartReconcile();
@@ -2057,9 +2045,8 @@ TEST_F(AccountReconcilorTest, DelegateTimeoutIsNotCalled) {
"12345");
AccountReconcilor* reconcilor = GetMockReconcilor();
ASSERT_TRUE(reconcilor);
- auto timer0 = std::make_unique<base::MockTimer>(true /* retain_user_task */,
- false /* is_repeating */);
- base::MockTimer* timer = timer0.get();
+ auto timer0 = std::make_unique<base::MockOneShotTimer>();
+ base::MockOneShotTimer* timer = timer0.get();
reconcilor->set_timer_for_testing(std::move(timer0));
reconcilor->StartReconcile();
@@ -2075,9 +2062,8 @@ TEST_F(AccountReconcilorTest, DelegateTimeoutIsNotCalledIfTimeoutIsNotReached) {
SpyReconcilorDelegate* spy_delegate = spy_delegate0.get();
AccountReconcilor* reconcilor = GetMockReconcilor(std::move(spy_delegate0));
ASSERT_TRUE(reconcilor);
- auto timer0 = std::make_unique<base::MockTimer>(true /* retain_user_task */,
- false /* is_repeating */);
- base::MockTimer* timer = timer0.get();
+ auto timer0 = std::make_unique<base::MockOneShotTimer>();
+ base::MockOneShotTimer* timer = timer0.get();
reconcilor->set_timer_for_testing(std::move(timer0));
reconcilor->StartReconcile();
diff --git a/chromium/components/signin/core/browser/account_tracker_service_unittest.cc b/chromium/components/signin/core/browser/account_tracker_service_unittest.cc
index bb6516c8a4a..980e2d558c5 100644
--- a/chromium/components/signin/core/browser/account_tracker_service_unittest.cc
+++ b/chromium/components/signin/core/browser/account_tracker_service_unittest.cc
@@ -18,6 +18,7 @@
#include "components/signin/core/browser/account_fetcher_service.h"
#include "components/signin/core/browser/account_info.h"
#include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/avatar_icon_util.h"
#include "components/signin/core/browser/child_account_info_fetcher.h"
#include "components/signin/core/browser/fake_account_fetcher_service.h"
#include "components/signin/core/browser/signin_pref_names.h"
@@ -81,6 +82,14 @@ std::string AccountIdToPictureURL(const std::string& account_id) {
"/AAAAAAAAAAI/AAAAAAAAACQ/Efg/photo.jpg";
}
+std::string AccountIdToPictureURLWithSize(const std::string& account_id) {
+ return signin::GetAvatarImageURLWithOptions(
+ GURL(AccountIdToPictureURL(account_id)),
+ AccountFetcherService::kAccountImageDownloadSize,
+ true /* no_silhouette */)
+ .spec();
+}
+
void CheckAccountDetails(const std::string& account_id,
const AccountInfo& info) {
EXPECT_EQ(account_id, info.account_id);
@@ -267,9 +276,7 @@ testing::AssertionResult AccountTrackerObserver::CheckEvents(
class AccountTrackerServiceTest : public testing::Test {
public:
- AccountTrackerServiceTest()
- : next_image_data_fetcher_id_(
- image_fetcher::ImageDataFetcher::kFirstUrlFetcherId) {}
+ AccountTrackerServiceTest() {}
~AccountTrackerServiceTest() override {}
@@ -353,6 +360,12 @@ class AccountTrackerServiceTest : public testing::Test {
}
SigninClient* signin_client() { return signin_client_.get(); }
+ // Images go through test_url_loader_factory(); others use
+ // |test_fetcher_factory_| for now.
+ network::TestURLLoaderFactory* test_url_loader_factory() {
+ return signin_client_->test_url_loader_factory();
+ }
+
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
@@ -367,8 +380,6 @@ class AccountTrackerServiceTest : public testing::Test {
std::unique_ptr<AccountFetcherService> account_fetcher_;
std::unique_ptr<AccountTrackerService> account_tracker_;
std::unique_ptr<TestSigninClient> signin_client_;
-
- int next_image_data_fetcher_id_;
};
void AccountTrackerServiceTest::ReturnFetchResults(
@@ -406,12 +417,16 @@ void AccountTrackerServiceTest::ReturnAccountInfoFetchFailure(
void AccountTrackerServiceTest::ReturnAccountImageFetchSuccess(
const std::string& account_id) {
- ReturnFetchResults(next_image_data_fetcher_id_++, net::HTTP_OK, "image data");
+ test_url_loader_factory()->AddResponse(
+ AccountIdToPictureURLWithSize(account_id), "image data");
+ scoped_task_environment_.RunUntilIdle();
}
void AccountTrackerServiceTest::ReturnAccountImageFetchFailure(
const std::string& account_id) {
- ReturnFetchResults(next_image_data_fetcher_id_++, net::HTTP_BAD_REQUEST, "");
+ test_url_loader_factory()->AddResponse(
+ AccountIdToPictureURLWithSize(account_id), "", net::HTTP_BAD_REQUEST);
+ scoped_task_environment_.RunUntilIdle();
}
TEST_F(AccountTrackerServiceTest, Basic) {
diff --git a/chromium/components/signin/core/browser/child_account_info_fetcher.cc b/chromium/components/signin/core/browser/child_account_info_fetcher.cc
index c5af00f71b8..bc8e2670c78 100644
--- a/chromium/components/signin/core/browser/child_account_info_fetcher.cc
+++ b/chromium/components/signin/core/browser/child_account_info_fetcher.cc
@@ -6,6 +6,7 @@
#include "build/build_config.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#if defined(OS_ANDROID)
#include "components/signin/core/browser/child_account_info_fetcher_android.h"
#else
@@ -17,13 +18,13 @@ std::unique_ptr<ChildAccountInfoFetcher> ChildAccountInfoFetcher::CreateFrom(
const std::string& account_id,
AccountFetcherService* fetcher_service,
OAuth2TokenService* token_service,
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
invalidation::InvalidationService* invalidation_service) {
#if defined(OS_ANDROID)
return ChildAccountInfoFetcherAndroid::Create(fetcher_service, account_id);
#else
return std::make_unique<ChildAccountInfoFetcherImpl>(
- account_id, fetcher_service, token_service, request_context_getter,
+ account_id, fetcher_service, token_service, url_loader_factory,
invalidation_service);
#endif
}
diff --git a/chromium/components/signin/core/browser/child_account_info_fetcher.h b/chromium/components/signin/core/browser/child_account_info_fetcher.h
index 0b3623d6d31..f5891f5c97f 100644
--- a/chromium/components/signin/core/browser/child_account_info_fetcher.h
+++ b/chromium/components/signin/core/browser/child_account_info_fetcher.h
@@ -8,6 +8,7 @@
#include <memory>
#include <string>
+#include "base/memory/ref_counted.h"
#include "build/build_config.h"
#if defined(OS_ANDROID)
@@ -17,8 +18,8 @@
namespace invalidation {
class InvalidationService;
}
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
}
class AccountFetcherService;
class OAuth2TokenService;
@@ -31,7 +32,7 @@ class ChildAccountInfoFetcher {
const std::string& account_id,
AccountFetcherService* fetcher_service,
OAuth2TokenService* token_service,
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
invalidation::InvalidationService* invalidation_service);
virtual ~ChildAccountInfoFetcher();
diff --git a/chromium/components/signin/core/browser/child_account_info_fetcher_impl.cc b/chromium/components/signin/core/browser/child_account_info_fetcher_impl.cc
index faee2aec332..0fb277d606d 100644
--- a/chromium/components/signin/core/browser/child_account_info_fetcher_impl.cc
+++ b/chromium/components/signin/core/browser/child_account_info_fetcher_impl.cc
@@ -4,6 +4,7 @@
#include "components/signin/core/browser/child_account_info_fetcher_impl.h"
+#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "base/trace_event/trace_event.h"
#include "base/values.h"
@@ -15,6 +16,7 @@
#include "google/cacheinvalidation/types.pb.h"
#include "google_apis/gaia/gaia_auth_fetcher.h"
#include "google_apis/gaia/gaia_constants.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
// TODO(maroun): Remove this file.
@@ -47,11 +49,11 @@ ChildAccountInfoFetcherImpl::ChildAccountInfoFetcherImpl(
const std::string& account_id,
AccountFetcherService* fetcher_service,
OAuth2TokenService* token_service,
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
invalidation::InvalidationService* invalidation_service)
: OAuth2TokenService::Consumer(kFetcherId),
token_service_(token_service),
- request_context_getter_(request_context_getter),
+ url_loader_factory_(url_loader_factory),
fetcher_service_(fetcher_service),
invalidation_service_(invalidation_service),
account_id_(account_id),
@@ -99,7 +101,7 @@ void ChildAccountInfoFetcherImpl::OnGetTokenSuccess(
DCHECK_EQ(request, login_token_request_.get());
gaia_auth_fetcher_ = fetcher_service_->signin_client_->CreateGaiaAuthFetcher(
- this, GaiaConstants::kChromeSource, request_context_getter_);
+ this, GaiaConstants::kChromeSource, url_loader_factory_);
gaia_auth_fetcher_->StartOAuthLogin(access_token,
GaiaConstants::kGaiaService);
}
@@ -127,10 +129,8 @@ void ChildAccountInfoFetcherImpl::OnGetUserInfoSuccess(
std::vector<std::string> service_flags = base::SplitString(
services_iter->second, ",", base::TRIM_WHITESPACE,
base::SPLIT_WANT_ALL);
- bool is_child_account =
- std::find(service_flags.begin(), service_flags.end(),
- AccountTrackerService::kChildAccountServiceFlag) !=
- service_flags.end();
+ bool is_child_account = base::ContainsValue(
+ service_flags, AccountTrackerService::kChildAccountServiceFlag);
if (!is_child_account && invalidation_service_) {
// Don't bother listening for invalidations as a non-child account can't
// become a child account.
diff --git a/chromium/components/signin/core/browser/child_account_info_fetcher_impl.h b/chromium/components/signin/core/browser/child_account_info_fetcher_impl.h
index 9046a4086a1..7b352cbb872 100644
--- a/chromium/components/signin/core/browser/child_account_info_fetcher_impl.h
+++ b/chromium/components/signin/core/browser/child_account_info_fetcher_impl.h
@@ -8,6 +8,7 @@
#include <memory>
#include "base/macros.h"
+#include "base/memory/ref_counted.h"
#include "base/threading/thread_checker.h"
#include "base/timer/timer.h"
#include "components/invalidation/public/invalidation_handler.h"
@@ -18,6 +19,10 @@
// TODO(maroun): Remove this file.
+namespace network {
+class SharedURLLoaderFactory;
+}
+
class GaiaAuthFetcher;
class ChildAccountInfoFetcherImpl : public ChildAccountInfoFetcher,
@@ -29,7 +34,7 @@ class ChildAccountInfoFetcherImpl : public ChildAccountInfoFetcher,
const std::string& account_id,
AccountFetcherService* fetcher_service,
OAuth2TokenService* token_service,
- net::URLRequestContextGetter* request_context_getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
invalidation::InvalidationService* invalidation_service);
~ChildAccountInfoFetcherImpl() override;
@@ -58,7 +63,7 @@ class ChildAccountInfoFetcherImpl : public ChildAccountInfoFetcher,
std::string GetOwnerName() const override;
OAuth2TokenService* token_service_;
- net::URLRequestContextGetter* request_context_getter_;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
AccountFetcherService* fetcher_service_;
invalidation::InvalidationService* invalidation_service_;
const std::string account_id_;
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 58c60a4b4c5..4c5173bdeda 100644
--- a/chromium/components/signin/core/browser/chrome_connected_header_helper.cc
+++ b/chromium/components/signin/core/browser/chrome_connected_header_helper.cc
@@ -11,6 +11,7 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "components/google/core/browser/google_util.h"
+#include "components/signin/core/browser/cookie_settings_util.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "url/gurl.h"
@@ -94,6 +95,20 @@ ManageAccountsParams ChromeConnectedHeaderHelper::BuildManageAccountsParams(
return params;
}
+bool ChromeConnectedHeaderHelper::ShouldBuildRequestHeader(
+ const GURL& url,
+ const content_settings::CookieSettings* cookie_settings) {
+ // If signin cookies are not allowed, don't add the header.
+ if (!SettingsAllowSigninCookies(cookie_settings))
+ return false;
+
+ // Check if url is eligible for the header.
+ if (!IsUrlEligibleForRequestHeader(url))
+ return false;
+
+ return true;
+}
+
bool ChromeConnectedHeaderHelper::IsUrlEligibleToIncludeGaiaId(
const GURL& url,
bool is_header_request) {
diff --git a/chromium/components/signin/core/browser/chrome_connected_header_helper.h b/chromium/components/signin/core/browser/chrome_connected_header_helper.h
index 034de4fb069..c39bacdfa0c 100644
--- a/chromium/components/signin/core/browser/chrome_connected_header_helper.h
+++ b/chromium/components/signin/core/browser/chrome_connected_header_helper.h
@@ -42,6 +42,11 @@ class ChromeConnectedHeaderHelper : public SigninHeaderHelper {
const std::string& account_id,
int profile_mode_mask);
+ // SigninHeaderHelper implementation:
+ bool ShouldBuildRequestHeader(
+ const GURL& url,
+ const content_settings::CookieSettings* cookie_settings) override;
+
private:
// Whether mirror account consistency should be used.
AccountConsistencyMethod account_consistency_;
diff --git a/chromium/components/signin/core/browser/dice_header_helper.cc b/chromium/components/signin/core/browser/dice_header_helper.cc
index 3f9b76cf797..b6746b31e52 100644
--- a/chromium/components/signin/core/browser/dice_header_helper.cc
+++ b/chromium/components/signin/core/browser/dice_header_helper.cc
@@ -181,6 +181,12 @@ DiceResponseParams DiceHeaderHelper::BuildDiceSignoutResponseParams(
return params;
}
+bool DiceHeaderHelper::ShouldBuildRequestHeader(
+ const GURL& url,
+ const content_settings::CookieSettings* cookie_settings) {
+ return IsUrlEligibleForRequestHeader(url);
+}
+
bool DiceHeaderHelper::IsUrlEligibleForRequestHeader(const GURL& url) {
if (account_consistency_ == AccountConsistencyMethod::kDisabled ||
account_consistency_ == AccountConsistencyMethod::kMirror) {
@@ -198,7 +204,8 @@ bool DiceHeaderHelper::IsUrlEligibleForRequestHeader(const GURL& url) {
}
std::string DiceHeaderHelper::BuildRequestHeader(
- const std::string& sync_account_id) {
+ const std::string& sync_account_id,
+ const std::string& device_id) {
// When fixing auth errors, only add the header when Sync is actually in error
// state.
DCHECK(
@@ -210,6 +217,8 @@ std::string DiceHeaderHelper::BuildRequestHeader(
parts.push_back(base::StringPrintf("version=%s", kDiceProtocolVersion));
parts.push_back("client_id=" +
GaiaUrls::GetInstance()->oauth2_chrome_client_id());
+ if (!device_id.empty())
+ parts.push_back("device_id=" + device_id);
if (!sync_account_id.empty())
parts.push_back("sync_account_id=" + sync_account_id);
diff --git a/chromium/components/signin/core/browser/dice_header_helper.h b/chromium/components/signin/core/browser/dice_header_helper.h
index e7b51f68b09..5c6bd2e9e9f 100644
--- a/chromium/components/signin/core/browser/dice_header_helper.h
+++ b/chromium/components/signin/core/browser/dice_header_helper.h
@@ -41,7 +41,13 @@ class DiceHeaderHelper : public SigninHeaderHelper {
// account.
// |show_signout_confirmation| is true if Gaia must display the signout
// confirmation dialog.
- std::string BuildRequestHeader(const std::string& sync_account_id);
+ std::string BuildRequestHeader(const std::string& sync_account_id,
+ const std::string& device_id);
+
+ // SigninHeaderHelper implementation:
+ bool ShouldBuildRequestHeader(
+ const GURL& url,
+ const content_settings::CookieSettings* cookie_settings) override;
private:
// SigninHeaderHelper implementation:
diff --git a/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.cc b/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.cc
index ed87b82fb00..88e6a5163b1 100644
--- a/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.cc
+++ b/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.cc
@@ -9,39 +9,48 @@
#include "components/signin/core/browser/profile_oauth2_token_service.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_urls.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
FakeGaiaCookieManagerService::FakeGaiaCookieManagerService(
OAuth2TokenService* token_service,
const std::string& source,
- SigninClient* client)
- : GaiaCookieManagerService(token_service, source, client),
- url_fetcher_factory_(nullptr) {}
+ SigninClient* client,
+ bool use_fake_url_loader)
+ : GaiaCookieManagerService(token_service, source, client) {
+ if (use_fake_url_loader) {
+ test_url_loader_factory_ =
+ std::make_unique<network::TestURLLoaderFactory>();
+ shared_loader_factory_ =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ test_url_loader_factory_.get());
+ }
+}
-void FakeGaiaCookieManagerService::Init(
- net::FakeURLFetcherFactory* url_fetcher_factory) {
- url_fetcher_factory_ = url_fetcher_factory;
+FakeGaiaCookieManagerService::~FakeGaiaCookieManagerService() {
+ if (shared_loader_factory_)
+ shared_loader_factory_->Detach();
}
void FakeGaiaCookieManagerService::SetListAccountsResponseHttpNotFound() {
- DCHECK(url_fetcher_factory_);
- url_fetcher_factory_->SetFakeResponse(
- GaiaUrls::GetInstance()->ListAccountsURLWithSource(
- GaiaConstants::kChromeSource),
- "", net::HTTP_NOT_FOUND, net::URLRequestStatus::SUCCESS);
+ test_url_loader_factory_->AddResponse(
+ GaiaUrls::GetInstance()
+ ->ListAccountsURLWithSource(GaiaConstants::kChromeSource)
+ .spec(),
+ /*content=*/"", net::HTTP_NOT_FOUND);
}
void FakeGaiaCookieManagerService::SetListAccountsResponseWebLoginRequired() {
- DCHECK(url_fetcher_factory_);
- url_fetcher_factory_->SetFakeResponse(
- GaiaUrls::GetInstance()->ListAccountsURLWithSource(
- GaiaConstants::kChromeSource),
- "Info=WebLoginRequired", net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ test_url_loader_factory_->AddResponse(
+ GaiaUrls::GetInstance()
+ ->ListAccountsURLWithSource(GaiaConstants::kChromeSource)
+ .spec(),
+ "Info=WebLoginRequired");
}
void FakeGaiaCookieManagerService::SetListAccountsResponseWithParams(
const std::vector<CookieParams>& params) {
- DCHECK(url_fetcher_factory_);
-
std::vector<std::string> response_body;
for (const auto& param : params) {
std::string response_part = base::StringPrintf(
@@ -56,11 +65,11 @@ void FakeGaiaCookieManagerService::SetListAccountsResponseWithParams(
response_body.push_back(response_part);
}
- url_fetcher_factory_->SetFakeResponse(
- GaiaUrls::GetInstance()->ListAccountsURLWithSource(
- GaiaConstants::kChromeSource),
- std::string("[\"f\", [") + base::JoinString(response_body, ", ") + "]]",
- net::HTTP_OK, net::URLRequestStatus::SUCCESS);
+ test_url_loader_factory_->AddResponse(
+ GaiaUrls::GetInstance()
+ ->ListAccountsURLWithSource(GaiaConstants::kChromeSource)
+ .spec(),
+ std::string("[\"f\", [") + base::JoinString(response_body, ", ") + "]]");
}
void FakeGaiaCookieManagerService::SetListAccountsResponseNoAccounts() {
@@ -106,3 +115,10 @@ std::string FakeGaiaCookieManagerService::GetDefaultSourceForRequest() {
// be able to find the URLs.
return GaiaConstants::kChromeSource;
}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+FakeGaiaCookieManagerService::GetURLLoaderFactory() {
+ return shared_loader_factory_
+ ? shared_loader_factory_
+ : GaiaCookieManagerService::GetURLLoaderFactory();
+}
diff --git a/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.h b/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.h
index a4b06205521..40ba7bb1c9b 100644
--- a/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.h
+++ b/chromium/components/signin/core/browser/fake_gaia_cookie_manager_service.h
@@ -8,8 +8,13 @@
#include <string>
#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
#include "components/signin/core/browser/gaia_cookie_manager_service.h"
-#include "net/url_request/test_url_fetcher_factory.h"
+
+namespace network {
+class TestURLLoaderFactory;
+class WeakWrapperSharedURLLoaderFactory;
+}
class FakeGaiaCookieManagerService : public GaiaCookieManagerService {
public:
@@ -24,9 +29,9 @@ class FakeGaiaCookieManagerService : public GaiaCookieManagerService {
FakeGaiaCookieManagerService(OAuth2TokenService* token_service,
const std::string& source,
- SigninClient* client);
-
- void Init(net::FakeURLFetcherFactory* url_fetcher_factory);
+ SigninClient* client,
+ bool use_fake_url_fetcher = true);
+ ~FakeGaiaCookieManagerService() override;
void SetListAccountsResponseHttpNotFound();
void SetListAccountsResponseWebLoginRequired();
@@ -47,9 +52,12 @@ class FakeGaiaCookieManagerService : public GaiaCookieManagerService {
std::string GetSourceForRequest(
const GaiaCookieManagerService::GaiaCookieRequest& request) override;
std::string GetDefaultSourceForRequest() override;
+ scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
// Provide a fake response for calls to /ListAccounts.
- net::FakeURLFetcherFactory* url_fetcher_factory_;
+ std::unique_ptr<network::TestURLLoaderFactory> test_url_loader_factory_;
+ scoped_refptr<network::WeakWrapperSharedURLLoaderFactory>
+ shared_loader_factory_;
DISALLOW_COPY_AND_ASSIGN(FakeGaiaCookieManagerService);
};
diff --git a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc b/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc
index 9710636cf6e..bdbf33bcc24 100644
--- a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc
+++ b/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc
@@ -21,7 +21,7 @@ FakeProfileOAuth2TokenService::PendingRequest::~PendingRequest() {}
FakeProfileOAuth2TokenService::FakeProfileOAuth2TokenService()
: FakeProfileOAuth2TokenService(
- std::make_unique<FakeOAuth2TokenServiceDelegate>(nullptr)) {}
+ std::make_unique<FakeOAuth2TokenServiceDelegate>()) {}
FakeProfileOAuth2TokenService::FakeProfileOAuth2TokenService(
std::unique_ptr<OAuth2TokenServiceDelegate> delegate)
@@ -122,7 +122,7 @@ FakeProfileOAuth2TokenService::GetPendingRequests() {
void FakeProfileOAuth2TokenService::FetchOAuth2Token(
RequestImpl* request,
const std::string& account_id,
- net::URLRequestContextGetter* getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const std::string& client_id,
const std::string& client_secret,
const ScopeSet& scopes) {
diff --git a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h b/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h
index 9edcd086071..ea6feaf26c2 100644
--- a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h
+++ b/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h
@@ -87,12 +87,13 @@ class FakeProfileOAuth2TokenService : public ProfileOAuth2TokenService {
protected:
// OAuth2TokenService overrides.
- void FetchOAuth2Token(RequestImpl* request,
- const std::string& account_id,
- net::URLRequestContextGetter* getter,
- const std::string& client_id,
- const std::string& client_secret,
- const ScopeSet& scopes) override;
+ void FetchOAuth2Token(
+ RequestImpl* request,
+ const std::string& account_id,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ const std::string& client_id,
+ const std::string& client_secret,
+ const ScopeSet& scopes) override;
void InvalidateAccessTokenImpl(const std::string& account_id,
const std::string& client_id,
diff --git a/chromium/components/signin/core/browser/fake_signin_manager.cc b/chromium/components/signin/core/browser/fake_signin_manager.cc
index a59df220373..d73df4fb757 100644
--- a/chromium/components/signin/core/browser/fake_signin_manager.cc
+++ b/chromium/components/signin/core/browser/fake_signin_manager.cc
@@ -75,7 +75,7 @@ void FakeSigninManager::SignIn(const std::string& gaia_id,
}
void FakeSigninManager::ForceSignOut() {
- prohibit_signout_ = false;
+ ProhibitSignout(false);
SignOut(signin_metrics::SIGNOUT_TEST,
signin_metrics::SignoutDelete::IGNORE_METRIC);
}
diff --git a/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc b/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc
index b308c7ec0ca..8cbd84e37c6 100644
--- a/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc
+++ b/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc
@@ -26,13 +26,15 @@
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.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 {
-// In case of an error while fetching using the GaiaAuthFetcher or URLFetcher,
-// retry with exponential backoff. Try up to 7 times within 15 minutes.
+// In case of an error while fetching using the GaiaAuthFetcher or
+// SimpleURLLoader, retry with exponential backoff. Try up to 7 times within 15
+// minutes.
const net::BackoffEntry::Policy kBackoffPolicy = {
// Number of initial errors (in sequence) to ignore before applying
// exponential back-off rules.
@@ -129,7 +131,8 @@ void GaiaCookieManagerService::ExternalCcResultFetcher::Start() {
CleanupTransientState();
results_.clear();
helper_->gaia_auth_fetcher_ = helper_->signin_client_->CreateGaiaAuthFetcher(
- this, helper_->GetDefaultSourceForRequest(), helper_->request_context());
+ this, helper_->GetDefaultSourceForRequest(),
+ helper_->GetURLLoaderFactory());
helper_->gaia_auth_fetcher_->StartGetCheckConnectionInfo();
// Some fetches may timeout. Start a timer to decide when the result fetcher
@@ -140,7 +143,7 @@ void GaiaCookieManagerService::ExternalCcResultFetcher::Start() {
}
bool GaiaCookieManagerService::ExternalCcResultFetcher::IsRunning() {
- return helper_->gaia_auth_fetcher_ || fetchers_.size() > 0u ||
+ return helper_->gaia_auth_fetcher_ || loaders_.size() > 0u ||
timer_.IsRunning();
}
@@ -174,9 +177,9 @@ void GaiaCookieManagerService::ExternalCcResultFetcher::
if (dict->GetString("carryBackToken", &token) &&
dict->GetString("url", &url)) {
results_[token] = "null";
- net::URLFetcher* fetcher = CreateFetcher(GURL(url)).release();
- fetchers_[fetcher->GetOriginalURL()] = std::make_pair(token, fetcher);
- fetcher->Start();
+ network::SimpleURLLoader* loader =
+ CreateAndStartLoader(GURL(url)).release();
+ loaders_[loader] = token;
}
}
}
@@ -199,8 +202,8 @@ void GaiaCookieManagerService::ExternalCcResultFetcher::
GetCheckConnectionInfoCompleted(false);
}
-std::unique_ptr<net::URLFetcher>
-GaiaCookieManagerService::ExternalCcResultFetcher::CreateFetcher(
+std::unique_ptr<network::SimpleURLLoader>
+GaiaCookieManagerService::ExternalCcResultFetcher::CreateAndStartLoader(
const GURL& url) {
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation(
@@ -229,47 +232,63 @@ GaiaCookieManagerService::ExternalCcResultFetcher::CreateFetcher(
"support for child accounts). It makes sense to control top "
"level features that use the GaiaCookieManager."
})");
- std::unique_ptr<net::URLFetcher> fetcher = net::URLFetcher::Create(
- 0, url, net::URLFetcher::GET, this, traffic_annotation);
- fetcher->SetRequestContext(helper_->request_context());
- fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES);
- data_use_measurement::DataUseUserData::AttachToFetcher(
- fetcher.get(), data_use_measurement::DataUseUserData::SIGNIN);
+
+ auto request = std::make_unique<network::ResourceRequest>();
+ request->url = url;
+ request->load_flags =
+ net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
+ // TODO(https://crbug.com/808498) re-add data use measurement once
+ // SimpleURLLoader supports it: data_use_measurement::DataUseUserData::SIGNIN
+
+ std::unique_ptr<network::SimpleURLLoader> loader =
+ network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
// Fetchers are sometimes cancelled because a network change was detected,
// especially at startup and after sign-in on ChromeOS.
- fetcher->SetAutomaticallyRetryOnNetworkChanges(1);
- return fetcher;
-}
-
-void GaiaCookieManagerService::ExternalCcResultFetcher::OnURLFetchComplete(
- const net::URLFetcher* source) {
- const GURL& url = source->GetOriginalURL();
- const net::URLRequestStatus& status = source->GetStatus();
- int response_code = source->GetResponseCode();
- if (status.is_success() && response_code == net::HTTP_OK &&
- fetchers_.count(url) > 0) {
- std::string data;
- source->GetResponseAsString(&data);
- // Only up to the first 16 characters of the response are important to GAIA.
- // Truncate if needed to keep amount data sent back to GAIA down.
- if (data.size() > 16)
- data.resize(16);
- results_[fetchers_[url].first] = data;
-
- // Clean up tracking of this fetcher. The rest will be cleaned up after
- // the timer expires in CleanupTransientState().
- DCHECK_EQ(source, fetchers_[url].second);
- fetchers_.erase(url);
- delete source;
-
- // If all expected responses have been received, cancel the timer and
- // report the result.
- if (fetchers_.empty()) {
- CleanupTransientState();
- GetCheckConnectionInfoCompleted(true);
- }
+ loader->SetRetryOptions(1, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
+
+ loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ helper_->GetURLLoaderFactory().get(),
+ base::BindOnce(&ExternalCcResultFetcher::OnURLLoadComplete,
+ base::Unretained(this), loader.get()));
+
+ return loader;
+}
+
+void GaiaCookieManagerService::ExternalCcResultFetcher::OnURLLoadComplete(
+ const network::SimpleURLLoader* source,
+ std::unique_ptr<std::string> body) {
+ if (source->NetError() != net::OK || !source->ResponseInfo() ||
+ !source->ResponseInfo()->headers ||
+ source->ResponseInfo()->headers->response_code() != net::HTTP_OK) {
+ return;
+ }
+
+ LoaderToToken::iterator it = loaders_.find(source);
+ if (it == loaders_.end())
+ return;
+
+ std::string data;
+ if (body)
+ data = std::move(*body);
+
+ // Only up to the first 16 characters of the response are important to GAIA.
+ // Truncate if needed to keep amount data sent back to GAIA down.
+ if (data.size() > 16)
+ data.resize(16);
+ results_[it->second] = data;
+
+ // Clean up tracking of this fetcher. The rest will be cleaned up after
+ // the timer expires in CleanupTransientState().
+ DCHECK_EQ(source, it->first);
+ loaders_.erase(it);
+ delete source;
+
+ // If all expected responses have been received, cancel the timer and
+ // report the result.
+ if (loaders_.empty()) {
+ CleanupTransientState();
+ GetCheckConnectionInfoCompleted(true);
}
}
@@ -284,11 +303,10 @@ void GaiaCookieManagerService::ExternalCcResultFetcher::
timer_.Stop();
helper_->gaia_auth_fetcher_.reset();
- for (URLToTokenAndFetcher::const_iterator it = fetchers_.begin();
- it != fetchers_.end(); ++it) {
- delete it->second.second;
+ for (const auto& loader_token_pair : loaders_) {
+ delete loader_token_pair.first;
}
- fetchers_.clear();
+ loaders_.clear();
}
void GaiaCookieManagerService::ExternalCcResultFetcher::
@@ -385,20 +403,20 @@ bool GaiaCookieManagerService::ListAccounts(
std::vector<gaia::ListedAccount>* accounts,
std::vector<gaia::ListedAccount>* signed_out_accounts,
const std::string& source) {
- if (!list_accounts_stale_) {
- if (accounts)
- accounts->assign(listed_accounts_.begin(), listed_accounts_.end());
+ if (accounts)
+ accounts->assign(listed_accounts_.begin(), listed_accounts_.end());
- if (signed_out_accounts) {
- signed_out_accounts->assign(signed_out_accounts_.begin(),
- signed_out_accounts_.end());
- }
+ if (signed_out_accounts) {
+ signed_out_accounts->assign(signed_out_accounts_.begin(),
+ signed_out_accounts_.end());
+ }
- return true;
+ if (list_accounts_stale_) {
+ TriggerListAccounts(source);
+ return false;
}
- TriggerListAccounts(source);
- return false;
+ return true;
}
void GaiaCookieManagerService::TriggerListAccounts(const std::string& source) {
@@ -490,6 +508,11 @@ void GaiaCookieManagerService::CancelAll() {
fetcher_timer_.Stop();
}
+scoped_refptr<network::SharedURLLoaderFactory>
+GaiaCookieManagerService::GetURLLoaderFactory() {
+ return signin_client_->GetURLLoaderFactory();
+}
+
std::string GaiaCookieManagerService::GetSourceForRequest(
const GaiaCookieManagerService::GaiaCookieRequest& request) {
std::string source = request.source().empty() ? GetDefaultSourceForRequest()
@@ -745,11 +768,10 @@ void GaiaCookieManagerService::OnLogOutFailure(
void GaiaCookieManagerService::StartFetchingUbertoken() {
VLOG(1) << "GaiaCookieManagerService::StartFetchingUbertoken account_id="
<< requests_.front().account_id();
- uber_token_fetcher_.reset(new UbertokenFetcher(
- token_service_, this, GetDefaultSourceForRequest(),
- signin_client_->GetURLRequestContext(),
+ uber_token_fetcher_ = std::make_unique<UbertokenFetcher>(
+ token_service_, this, GetDefaultSourceForRequest(), GetURLLoaderFactory(),
base::Bind(&SigninClient::CreateGaiaAuthFetcher,
- base::Unretained(signin_client_))));
+ base::Unretained(signin_client_)));
if (access_token_.empty()) {
uber_token_fetcher_->StartFetchingToken(requests_.front().account_id());
} else {
@@ -761,8 +783,7 @@ void GaiaCookieManagerService::StartFetchingUbertoken() {
void GaiaCookieManagerService::StartFetchingMergeSession() {
DCHECK(!uber_token_.empty());
gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher(
- this, GetSourceForRequest(requests_.front()),
- signin_client_->GetURLRequestContext());
+ this, GetSourceForRequest(requests_.front()), GetURLLoaderFactory());
gaia_auth_fetcher_->StartMergeSession(uber_token_,
external_cc_result_fetcher_.GetExternalCcResult());
@@ -778,8 +799,7 @@ void GaiaCookieManagerService::StartGaiaLogOut() {
void GaiaCookieManagerService::StartFetchingLogOut() {
gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher(
- this, GetSourceForRequest(requests_.front()),
- signin_client_->GetURLRequestContext());
+ this, GetSourceForRequest(requests_.front()), GetURLLoaderFactory());
gaia_auth_fetcher_->StartLogOut();
}
@@ -787,8 +807,7 @@ void GaiaCookieManagerService::StartFetchingListAccounts() {
VLOG(1) << "GaiaCookieManagerService::ListAccounts";
gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher(
- this, GetSourceForRequest(requests_.front()),
- signin_client_->GetURLRequestContext());
+ this, GetSourceForRequest(requests_.front()), GetURLLoaderFactory());
gaia_auth_fetcher_->StartListAccounts();
}
diff --git a/chromium/components/signin/core/browser/gaia_cookie_manager_service.h b/chromium/components/signin/core/browser/gaia_cookie_manager_service.h
index d8c3cbc5e8b..218b1b196ef 100644
--- a/chromium/components/signin/core/browser/gaia_cookie_manager_service.h
+++ b/chromium/components/signin/core/browser/gaia_cookie_manager_service.h
@@ -20,15 +20,15 @@
#include "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/ubertoken_fetcher.h"
#include "net/base/backoff_entry.h"
-#include "net/url_request/url_fetcher_delegate.h"
class GaiaAuthFetcher;
class GaiaCookieRequest;
class GoogleServiceAuthError;
class OAuth2TokenService;
-namespace net {
-class URLFetcher;
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
}
// Merges a Google account known to Chrome into the cookie jar. When merging
@@ -108,13 +108,11 @@ class GaiaCookieManagerService : public KeyedService,
// Class to retrieve the external connection check results from gaia.
// Declared publicly for unit tests.
- class ExternalCcResultFetcher : public GaiaAuthConsumer,
- public net::URLFetcherDelegate {
+ class ExternalCcResultFetcher : public GaiaAuthConsumer {
public:
- // Maps connection URLs, as returned by StartGetCheckConnectionInfo() to
- // token and URLFetcher used to fetch the URL.
- typedef std::map<GURL, std::pair<std::string, net::URLFetcher*>>
- URLToTokenAndFetcher;
+ // Maps connection check SimpleURLLoader to corresponding token.
+ typedef std::map<const network::SimpleURLLoader*, std::string>
+ LoaderToToken;
// Maps tokens to the fetched result for that token.
typedef std::map<std::string, std::string> ResultMap;
@@ -132,8 +130,8 @@ class GaiaCookieManagerService : public KeyedService,
// Are external URLs still being checked?
bool IsRunning();
- // Returns a copy of the internal token to fetcher map.
- URLToTokenAndFetcher get_fetcher_map_for_testing() { return fetchers_; }
+ // Returns a copy of the internal loader to token map.
+ LoaderToToken get_loader_map_for_testing() { return loaders_; }
// Simulate a timeout for tests.
void TimeoutForTests();
@@ -144,11 +142,13 @@ class GaiaCookieManagerService : public KeyedService,
void OnGetCheckConnectionInfoError(
const GoogleServiceAuthError& error) override;
- // Creates and initializes a URL fetcher for doing a connection check.
- std::unique_ptr<net::URLFetcher> CreateFetcher(const GURL& url);
+ // Creates and initializes a loader for doing a connection check.
+ std::unique_ptr<network::SimpleURLLoader> CreateAndStartLoader(
+ const GURL& url);
- // Overridden from URLFetcherDelgate.
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ // Called back from SimpleURLLoader.
+ void OnURLLoadComplete(const network::SimpleURLLoader* source,
+ std::unique_ptr<std::string> body);
// Any fetches still ongoing after this call are considered timed out.
void Timeout();
@@ -159,7 +159,7 @@ class GaiaCookieManagerService : public KeyedService,
GaiaCookieManagerService* helper_;
base::OneShotTimer timer_;
- URLToTokenAndFetcher fetchers_;
+ LoaderToToken loaders_;
ResultMap results_;
base::Time m_external_cc_result_start_time_;
@@ -180,11 +180,11 @@ class GaiaCookieManagerService : public KeyedService,
const std::string& access_token,
const std::string& source);
- // Returns if the listed accounts are up to date or not (ignore the out
- // parameter if return is false). The parameter will be assigned the current
- // cached accounts. If the accounts are not up to date, a ListAccounts fetch
- // is sent GAIA and Observer::OnGaiaAccountsInCookieUpdated will be called.
- // If either of |accounts| or |signed_out_accounts| is null, the corresponding
+ // Returns if the listed accounts are up to date or not. The out parameter
+ // will be assigned the current cached accounts (whether they are not up to
+ // date or not). If the accounts are not up to date, a ListAccounts fetch is
+ // sent GAIA and Observer::OnGaiaAccountsInCookieUpdated will be called. If
+ // either of |accounts| or |signed_out_accounts| is null, the corresponding
// accounts returned from /ListAccounts are ignored.
bool ListAccounts(std::vector<gaia::ListedAccount>* accounts,
std::vector<gaia::ListedAccount>* signed_out_accounts,
@@ -232,6 +232,9 @@ class GaiaCookieManagerService : public KeyedService,
return &fetcher_backoff_;
}
+ // Can be overridden by tests.
+ virtual scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
+
private:
net::URLRequestContextGetter* request_context() {
return signin_client_->GetURLRequestContext();
@@ -293,8 +296,8 @@ class GaiaCookieManagerService : public KeyedService,
std::unique_ptr<UbertokenFetcher> uber_token_fetcher_;
ExternalCcResultFetcher external_cc_result_fetcher_;
- // If the GaiaAuthFetcher or URLFetcher fails, retry with exponential backoff
- // and network delay.
+ // If the GaiaAuthFetcher or SimpleURLLoader fails, retry with exponential
+ // backoff and network delay.
net::BackoffEntry fetcher_backoff_;
base::OneShotTimer fetcher_timer_;
int fetcher_retries_;
diff --git a/chromium/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc b/chromium/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
index b0f29d9ac15..917dcb90774 100644
--- a/chromium/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
+++ b/chromium/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
@@ -16,7 +16,7 @@
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/stringprintf.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
@@ -26,8 +26,7 @@
#include "google_apis/gaia/fake_oauth2_token_service.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_urls.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.h"
+#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -59,30 +58,35 @@ class MockObserver : public GaiaCookieManagerService::Observer {
int total = 0;
-// Custom matcher for ListedAccounts.
-MATCHER_P(ListedAccountEquals, expected, "") {
- if (expected.size() != arg.size())
+bool AreAccountListsEqual(const std::vector<gaia::ListedAccount>& left,
+ const std::vector<gaia::ListedAccount>& right) {
+ if (left.size() != right.size())
return false;
- for (size_t i = 0u; i < expected.size(); ++i) {
- const gaia::ListedAccount& expected_account = expected[i];
- const gaia::ListedAccount& actual_account = arg[i];
+ for (size_t i = 0u; i < left.size(); ++i) {
+ const gaia::ListedAccount& left_account = left[i];
+ const gaia::ListedAccount& actual_account = right[i];
// If both accounts have an ID, use it for the comparison.
- if (!expected_account.id.empty() && !actual_account.id.empty()) {
- if (expected_account.id != actual_account.id)
+ if (!left_account.id.empty() && !actual_account.id.empty()) {
+ if (left_account.id != actual_account.id)
return false;
- } else if (expected_account.email != actual_account.email ||
- expected_account.gaia_id != actual_account.gaia_id ||
- expected_account.raw_email != actual_account.raw_email ||
- expected_account.valid != actual_account.valid ||
- expected_account.signed_out != actual_account.signed_out ||
- expected_account.verified != actual_account.verified) {
+ } else if (left_account.email != actual_account.email ||
+ left_account.gaia_id != actual_account.gaia_id ||
+ left_account.raw_email != actual_account.raw_email ||
+ left_account.valid != actual_account.valid ||
+ left_account.signed_out != actual_account.signed_out ||
+ left_account.verified != actual_account.verified) {
return false;
}
}
return true;
}
+// Custom matcher for ListedAccounts.
+MATCHER_P(ListedAccountEquals, expected, "") {
+ return AreAccountListsEqual(expected, arg);
+}
+
class InstrumentedGaiaCookieManagerService : public GaiaCookieManagerService {
public:
InstrumentedGaiaCookieManagerService(
@@ -156,33 +160,40 @@ class GaiaCookieManagerServiceTest : public testing::Test {
consumer->OnLogOutFailure(error);
}
- void SimulateGetCheckConnctionInfoSuccess(net::TestURLFetcher* fetcher,
- const std::string& data) {
- fetcher->set_status(net::URLRequestStatus());
- fetcher->set_response_code(200);
- fetcher->SetResponseString(data);
- fetcher->delegate()->OnURLFetchComplete(fetcher);
+ void SimulateGetCheckConnectionInfoSuccess(const std::string& data) {
+ signin_client_->test_url_loader_factory()->AddResponse(
+ GaiaUrls::GetInstance()
+ ->GetCheckConnectionInfoURLWithSource(GaiaConstants::kChromeSource)
+ .spec(),
+ data);
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void SimulateGetCheckConnectionInfoResult(const std::string& url,
+ const std::string& result) {
+ signin_client_->test_url_loader_factory()->AddResponse(url, result);
+ base::RunLoop().RunUntilIdle();
}
- void SimulateGetCheckConnctionInfoResult(net::URLFetcher* fetcher,
- const std::string& result) {
- net::TestURLFetcher* test_fetcher =
- static_cast<net::TestURLFetcher*>(fetcher);
- test_fetcher->set_status(net::URLRequestStatus());
- test_fetcher->set_response_code(200);
- test_fetcher->SetResponseString(result);
- test_fetcher->delegate()->OnURLFetchComplete(fetcher);
+ bool IsLoadPending(const std::string& url) {
+ return signin_client_->test_url_loader_factory()->IsPending(
+ GURL(url).spec());
+ }
+
+ bool IsLoadPending() {
+ return signin_client_->test_url_loader_factory()->NumPending() > 0;
}
const GoogleServiceAuthError& no_error() { return no_error_; }
const GoogleServiceAuthError& error() { return error_; }
const GoogleServiceAuthError& canceled() { return canceled_; }
- net::TestURLFetcherFactory* factory() { return &factory_; }
+ scoped_refptr<network::SharedURLLoaderFactory> factory() const {
+ return signin_client_->GetURLLoaderFactory();
+ }
private:
base::MessageLoop message_loop_;
- net::TestURLFetcherFactory factory_;
FakeOAuth2TokenService token_service_;
GoogleServiceAuthError no_error_;
GoogleServiceAuthError error_;
@@ -247,11 +258,12 @@ TEST_F(GaiaCookieManagerServiceTest, MergeSessionRetried) {
helper.AddAccountToCookie("acc1@gmail.com", GaiaConstants::kChromeSource);
SimulateMergeSessionFailure(&helper, canceled());
DCHECK(helper.is_running());
+ base::RunLoop run_loop;
// Transient error incurs a retry after 1 second.
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated(),
+ FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(1100));
- base::RunLoop().Run();
+ run_loop.Run();
SimulateMergeSessionSuccess(&helper, "token");
DCHECK(!helper.is_running());
}
@@ -272,19 +284,25 @@ TEST_F(GaiaCookieManagerServiceTest, MergeSessionRetriedTwice) {
// Transient error incurs a retry after 1 second.
EXPECT_LT(helper.GetBackoffEntry()->GetTimeUntilRelease(),
base::TimeDelta::FromMilliseconds(1100));
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated(),
- base::TimeDelta::FromMilliseconds(1100));
- base::RunLoop().Run();
+ {
+ base::RunLoop run_loop;
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, run_loop.QuitClosure(),
+ base::TimeDelta::FromMilliseconds(1100));
+ run_loop.Run();
+ }
SimulateMergeSessionFailure(&helper, canceled());
DCHECK(helper.is_running());
// Next transient error incurs a retry after 3 seconds.
EXPECT_LT(helper.GetBackoffEntry()->GetTimeUntilRelease(),
base::TimeDelta::FromMilliseconds(3100));
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated(),
- base::TimeDelta::FromMilliseconds(3100));
- base::RunLoop().Run();
+ {
+ base::RunLoop run_loop;
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, run_loop.QuitClosure(),
+ base::TimeDelta::FromMilliseconds(3100));
+ run_loop.Run();
+ }
SimulateMergeSessionSuccess(&helper, "token");
DCHECK(!helper.is_running());
histograms.ExpectUniqueSample("OAuth2Login.MergeSessionRetry",
@@ -666,21 +684,31 @@ TEST_F(GaiaCookieManagerServiceTest, ListAccountsAfterOnCookieChange) {
std::vector<gaia::ListedAccount> signed_out_accounts;
std::vector<gaia::ListedAccount> empty_signed_out_accounts;
+ std::vector<gaia::ListedAccount> nonempty_list_accounts;
+ gaia::ListedAccount listed_account;
+ listed_account.email = "a@b.com";
+ listed_account.raw_email = "a@b.com";
+ listed_account.gaia_id = "8";
+ nonempty_list_accounts.push_back(listed_account);
+
+ // Add a single account.
EXPECT_CALL(helper, StartFetchingListAccounts());
EXPECT_CALL(observer,
OnGaiaAccountsInCookieUpdated(
- ListedAccountEquals(empty_list_accounts),
+ ListedAccountEquals(nonempty_list_accounts),
ListedAccountEquals(empty_signed_out_accounts), no_error()));
ASSERT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts,
GaiaConstants::kChromeSource));
ASSERT_TRUE(list_accounts.empty());
ASSERT_TRUE(signed_out_accounts.empty());
- SimulateListAccountsSuccess(&helper, "[\"f\",[]]");
+ SimulateListAccountsSuccess(
+ &helper,
+ "[\"f\", [[\"b\", 0, \"n\", \"a@b.com\", \"p\", 0, 0, 0, 0, 1, \"8\"]]]");
- // ListAccounts returns cached data.
+ // Sanity-check that ListAccounts returns the cached data.
ASSERT_TRUE(helper.ListAccounts(&list_accounts, &signed_out_accounts,
GaiaConstants::kChromeSource));
- ASSERT_TRUE(list_accounts.empty());
+ ASSERT_TRUE(AreAccountListsEqual(nonempty_list_accounts, list_accounts));
ASSERT_TRUE(signed_out_accounts.empty());
EXPECT_CALL(helper, StartFetchingListAccounts());
@@ -690,10 +718,15 @@ TEST_F(GaiaCookieManagerServiceTest, ListAccountsAfterOnCookieChange) {
ListedAccountEquals(empty_signed_out_accounts), no_error()));
helper.ForceOnCookieChangeProcessing();
- // OnCookieChange should invalidate cached data.
+ // OnCookieChange should invalidate the cached data.
+
+ // Clear the list before calling |ListAccounts()| to make sure that
+ // GaiaCookieManagerService repopulates it with the stale cached information.
+ list_accounts.clear();
+
ASSERT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts,
GaiaConstants::kChromeSource));
- ASSERT_TRUE(list_accounts.empty());
+ ASSERT_TRUE(AreAccountListsEqual(nonempty_list_accounts, list_accounts));
ASSERT_TRUE(signed_out_accounts.empty());
SimulateListAccountsSuccess(&helper, "[\"f\",[]]");
}
@@ -705,26 +738,21 @@ TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcher) {
result_fetcher.Start();
// Simulate a successful completion of GetCheckConnctionInfo.
- net::TestURLFetcher* fetcher = factory()->GetFetcherByID(0);
- ASSERT_TRUE(nullptr != fetcher);
- SimulateGetCheckConnctionInfoSuccess(
- fetcher,
+ SimulateGetCheckConnectionInfoSuccess(
"[{\"carryBackToken\": \"yt\", \"url\": \"http://www.yt.com\"},"
" {\"carryBackToken\": \"bl\", \"url\": \"http://www.bl.com\"}]");
// Simulate responses for the two connection URLs.
- GaiaCookieManagerService::ExternalCcResultFetcher::URLToTokenAndFetcher
- fetchers = result_fetcher.get_fetcher_map_for_testing();
- ASSERT_EQ(2u, fetchers.size());
- ASSERT_EQ(1u, fetchers.count(GURL("http://www.yt.com")));
- ASSERT_EQ(1u, fetchers.count(GURL("http://www.bl.com")));
+ GaiaCookieManagerService::ExternalCcResultFetcher::LoaderToToken loaders =
+ result_fetcher.get_loader_map_for_testing();
+ ASSERT_EQ(2u, loaders.size());
+ ASSERT_TRUE(IsLoadPending("http://www.yt.com"));
+ ASSERT_TRUE(IsLoadPending("http://www.bl.com"));
ASSERT_EQ("bl:null,yt:null", result_fetcher.GetExternalCcResult());
- SimulateGetCheckConnctionInfoResult(
- fetchers[GURL("http://www.yt.com")].second, "yt_result");
+ SimulateGetCheckConnectionInfoResult("http://www.yt.com", "yt_result");
ASSERT_EQ("bl:null,yt:yt_result", result_fetcher.GetExternalCcResult());
- SimulateGetCheckConnctionInfoResult(
- fetchers[GURL("http://www.bl.com")].second, "bl_result");
+ SimulateGetCheckConnectionInfoResult("http://www.bl.com", "bl_result");
ASSERT_EQ("bl:bl_result,yt:yt_result", result_fetcher.GetExternalCcResult());
}
@@ -735,30 +763,26 @@ TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcherTimeout) {
result_fetcher.Start();
// Simulate a successful completion of GetCheckConnctionInfo.
- net::TestURLFetcher* fetcher = factory()->GetFetcherByID(0);
- ASSERT_TRUE(nullptr != fetcher);
- SimulateGetCheckConnctionInfoSuccess(
- fetcher,
+ SimulateGetCheckConnectionInfoSuccess(
"[{\"carryBackToken\": \"yt\", \"url\": \"http://www.yt.com\"},"
" {\"carryBackToken\": \"bl\", \"url\": \"http://www.bl.com\"}]");
- GaiaCookieManagerService::ExternalCcResultFetcher::URLToTokenAndFetcher
- fetchers = result_fetcher.get_fetcher_map_for_testing();
- ASSERT_EQ(2u, fetchers.size());
- ASSERT_EQ(1u, fetchers.count(GURL("http://www.yt.com")));
- ASSERT_EQ(1u, fetchers.count(GURL("http://www.bl.com")));
+ GaiaCookieManagerService::ExternalCcResultFetcher::LoaderToToken loaders =
+ result_fetcher.get_loader_map_for_testing();
+ ASSERT_EQ(2u, loaders.size());
+ ASSERT_TRUE(IsLoadPending("http://www.yt.com"));
+ ASSERT_TRUE(IsLoadPending("http://www.bl.com"));
// Simulate response only for "yt".
ASSERT_EQ("bl:null,yt:null", result_fetcher.GetExternalCcResult());
- SimulateGetCheckConnctionInfoResult(
- fetchers[GURL("http://www.yt.com")].second, "yt_result");
+ SimulateGetCheckConnectionInfoResult("http://www.yt.com", "yt_result");
ASSERT_EQ("bl:null,yt:yt_result", result_fetcher.GetExternalCcResult());
// Now timeout.
result_fetcher.TimeoutForTests();
ASSERT_EQ("bl:null,yt:yt_result", result_fetcher.GetExternalCcResult());
- fetchers = result_fetcher.get_fetcher_map_for_testing();
- ASSERT_EQ(0u, fetchers.size());
+ loaders = result_fetcher.get_loader_map_for_testing();
+ ASSERT_EQ(0u, loaders.size());
}
TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcherTruncate) {
@@ -768,20 +792,17 @@ TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcherTruncate) {
result_fetcher.Start();
// Simulate a successful completion of GetCheckConnctionInfo.
- net::TestURLFetcher* fetcher = factory()->GetFetcherByID(0);
- ASSERT_TRUE(nullptr != fetcher);
- SimulateGetCheckConnctionInfoSuccess(
- fetcher,
+ SimulateGetCheckConnectionInfoSuccess(
"[{\"carryBackToken\": \"yt\", \"url\": \"http://www.yt.com\"}]");
- GaiaCookieManagerService::ExternalCcResultFetcher::URLToTokenAndFetcher
- fetchers = result_fetcher.get_fetcher_map_for_testing();
- ASSERT_EQ(1u, fetchers.size());
- ASSERT_EQ(1u, fetchers.count(GURL("http://www.yt.com")));
+ GaiaCookieManagerService::ExternalCcResultFetcher::LoaderToToken loaders =
+ result_fetcher.get_loader_map_for_testing();
+ ASSERT_EQ(1u, loaders.size());
+ ASSERT_TRUE(IsLoadPending("http://www.yt.com"));
// Simulate response for "yt" with a string that is too long.
- SimulateGetCheckConnctionInfoResult(
- fetchers[GURL("http://www.yt.com")].second, "1234567890123456trunc");
+ SimulateGetCheckConnectionInfoResult("http://www.yt.com",
+ "1234567890123456trunc");
ASSERT_EQ("yt:1234567890123456", result_fetcher.GetExternalCcResult());
}
@@ -791,21 +812,18 @@ TEST_F(GaiaCookieManagerServiceTest, UbertokenSuccessFetchesExternalCC) {
EXPECT_CALL(helper, StartFetchingUbertoken());
helper.AddAccountToCookie("acc1@gmail.com", GaiaConstants::kChromeSource);
- ASSERT_FALSE(factory()->GetFetcherByID(0));
+ ASSERT_FALSE(IsLoadPending());
SimulateUbertokenSuccess(&helper, "token");
// Check there is now a fetcher that belongs to the ExternalCCResultFetcher.
- net::TestURLFetcher* fetcher = factory()->GetFetcherByID(0);
- ASSERT_TRUE(nullptr != fetcher);
- SimulateGetCheckConnctionInfoSuccess(
- fetcher,
+ SimulateGetCheckConnectionInfoSuccess(
"[{\"carryBackToken\": \"bl\", \"url\": \"http://www.bl.com\"}]");
GaiaCookieManagerService::ExternalCcResultFetcher* result_fetcher =
helper.external_cc_result_fetcher_for_testing();
- GaiaCookieManagerService::ExternalCcResultFetcher::URLToTokenAndFetcher
- fetchers = result_fetcher->get_fetcher_map_for_testing();
- ASSERT_EQ(1u, fetchers.size());
- ASSERT_EQ(1u, fetchers.count(GURL("http://www.bl.com")));
+ GaiaCookieManagerService::ExternalCcResultFetcher::LoaderToToken loaders =
+ result_fetcher->get_loader_map_for_testing();
+ ASSERT_EQ(1u, loaders.size());
+ ASSERT_TRUE(IsLoadPending("http://www.bl.com"));
}
TEST_F(GaiaCookieManagerServiceTest, UbertokenSuccessFetchesExternalCCOnce) {
diff --git a/chromium/components/signin/core/browser/profile_identity_provider.cc b/chromium/components/signin/core/browser/profile_identity_provider.cc
deleted file mode 100644
index 797236db602..00000000000
--- a/chromium/components/signin/core/browser/profile_identity_provider.cc
+++ /dev/null
@@ -1,41 +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/profile_identity_provider.h"
-
-#include "components/signin/core/browser/profile_oauth2_token_service.h"
-
-ProfileIdentityProvider::ProfileIdentityProvider(
- SigninManagerBase* signin_manager,
- ProfileOAuth2TokenService* token_service)
- : signin_manager_(signin_manager), token_service_(token_service) {
- signin_manager_->AddObserver(this);
-}
-
-ProfileIdentityProvider::~ProfileIdentityProvider() {
- signin_manager_->RemoveObserver(this);
-}
-
-std::string ProfileIdentityProvider::GetActiveUsername() {
- return signin_manager_->GetAuthenticatedAccountInfo().email;
-}
-
-std::string ProfileIdentityProvider::GetActiveAccountId() {
- return signin_manager_->GetAuthenticatedAccountId();
-}
-
-OAuth2TokenService* ProfileIdentityProvider::GetTokenService() {
- return token_service_;
-}
-
-void ProfileIdentityProvider::GoogleSigninSucceeded(
- const std::string& account_id,
- const std::string& username) {
- FireOnActiveAccountLogin();
-}
-
-void ProfileIdentityProvider::GoogleSignedOut(const std::string& account_id,
- const std::string& username) {
- FireOnActiveAccountLogout();
-}
diff --git a/chromium/components/signin/core/browser/profile_identity_provider.h b/chromium/components/signin/core/browser/profile_identity_provider.h
deleted file mode 100644
index 9d4cdb31a9f..00000000000
--- a/chromium/components/signin/core/browser/profile_identity_provider.h
+++ /dev/null
@@ -1,41 +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_PROFILE_IDENTITY_PROVIDER_H_
-#define COMPONENTS_SIGNIN_CORE_BROWSER_PROFILE_IDENTITY_PROVIDER_H_
-
-#include "base/macros.h"
-#include "components/signin/core/browser/signin_manager_base.h"
-#include "google_apis/gaia/identity_provider.h"
-
-class ProfileOAuth2TokenService;
-
-// An identity provider implementation that's backed by
-// ProfileOAuth2TokenService and SigninManager.
-class ProfileIdentityProvider : public IdentityProvider,
- public SigninManagerBase::Observer {
- public:
- ProfileIdentityProvider(SigninManagerBase* signin_manager,
- ProfileOAuth2TokenService* token_service);
- ~ProfileIdentityProvider() override;
-
- // IdentityProvider:
- std::string GetActiveUsername() override;
- std::string GetActiveAccountId() override;
- OAuth2TokenService* GetTokenService() override;
-
- // SigninManagerBase::Observer:
- void GoogleSigninSucceeded(const std::string& account_id,
- const std::string& username) override;
- void GoogleSignedOut(const std::string& account_id,
- const std::string& username) override;
-
- private:
- SigninManagerBase* const signin_manager_;
- ProfileOAuth2TokenService* const token_service_;
-
- DISALLOW_COPY_AND_ASSIGN(ProfileIdentityProvider);
-};
-
-#endif // COMPONENTS_SIGNIN_CORE_BROWSER_PROFILE_IDENTITY_PROVIDER_H_
diff --git a/chromium/components/signin/core/browser/profile_management_switches.cc b/chromium/components/signin/core/browser/profile_management_switches.cc
index 301da575ed8..9321f3a87f3 100644
--- a/chromium/components/signin/core/browser/profile_management_switches.cc
+++ b/chromium/components/signin/core/browser/profile_management_switches.cc
@@ -6,55 +6,16 @@
#include <string>
-#include "base/callback.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/metrics/field_trial_params.h"
#include "build/build_config.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/signin/core/browser/signin_buildflags.h"
#include "components/signin/core/browser/signin_switches.h"
-#if defined(OS_CHROMEOS)
-#include "components/signin/core/browser/signin_pref_names.h"
-#endif
-
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
-#include "components/prefs/pref_service.h"
-#endif
-
namespace signin {
namespace {
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
-const char kDiceMigrationCompletePref[] = "signin.DiceMigrationComplete";
-
-// Returns whether Dice is enabled for the user, based on the account
-// consistency mode and the dice pref value.
-bool IsDiceEnabledForPrefValue(bool dice_pref_value) {
- switch (GetAccountConsistencyMethod()) {
- case AccountConsistencyMethod::kDisabled:
- case AccountConsistencyMethod::kMirror:
- case AccountConsistencyMethod::kDiceFixAuthErrors:
- case AccountConsistencyMethod::kDicePrepareMigration:
- return false;
- case AccountConsistencyMethod::kDice:
- return true;
- case AccountConsistencyMethod::kDiceMigration:
- return dice_pref_value;
- }
- NOTREACHED();
- return false;
-}
-#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
-
-// Returns a callback telling if site isolation is enabled for Gaia origins.
-base::RepeatingCallback<bool()>* GetIsGaiaIsolatedCallback() {
- static base::RepeatingCallback<bool()> g_is_gaia_isolated_callback;
- return &g_is_gaia_isolated_callback;
-}
-
bool AccountConsistencyMethodGreaterOrEqual(AccountConsistencyMethod a,
AccountConsistencyMethod b) {
return static_cast<int>(a) >= static_cast<int>(b);
@@ -62,22 +23,6 @@ bool AccountConsistencyMethodGreaterOrEqual(AccountConsistencyMethod a,
} // namespace
-// base::Feature definitions.
-const base::Feature kAccountConsistencyFeature{
- "AccountConsistency", base::FEATURE_DISABLED_BY_DEFAULT};
-const char kAccountConsistencyFeatureMethodParameter[] = "method";
-const char kAccountConsistencyFeatureMethodMirror[] = "mirror";
-const char kAccountConsistencyFeatureMethodDiceFixAuthErrors[] =
- "dice_fix_auth_errors";
-const char kAccountConsistencyFeatureMethodDicePrepareMigration[] =
- "dice_prepare_migration_new_endpoint";
-const char kAccountConsistencyFeatureMethodDiceMigration[] = "dice_migration";
-const char kAccountConsistencyFeatureMethodDice[] = "dice";
-
-const base::Feature kUnifiedConsent{"UnifiedConsent",
- base::FEATURE_DISABLED_BY_DEFAULT};
-const char kUnifiedConsentShowBumpParameter[] = "show_consent_bump";
-
bool DiceMethodGreaterOrEqual(AccountConsistencyMethod a,
AccountConsistencyMethod b) {
DCHECK_NE(AccountConsistencyMethod::kMirror, a);
@@ -85,117 +30,6 @@ bool DiceMethodGreaterOrEqual(AccountConsistencyMethod a,
return AccountConsistencyMethodGreaterOrEqual(a, b);
}
-void RegisterAccountConsistencyProfilePrefs(
- user_prefs::PrefRegistrySyncable* registry) {
-#if defined(OS_CHROMEOS)
- registry->RegisterBooleanPref(prefs::kAccountConsistencyMirrorRequired,
- false);
-#endif
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- registry->RegisterBooleanPref(kDiceMigrationCompletePref, false);
-#endif
-}
-
-AccountConsistencyMethod GetAccountConsistencyMethod() {
-#if BUILDFLAG(ENABLE_MIRROR)
- // Mirror is always enabled on Android and iOS.
- return AccountConsistencyMethod::kMirror;
-#endif
-
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- DCHECK(!GetIsGaiaIsolatedCallback()->is_null());
- const AccountConsistencyMethod kDefaultMethod =
- AccountConsistencyMethod::kDiceFixAuthErrors;
-
- if (!GetIsGaiaIsolatedCallback()->Run())
- return kDefaultMethod;
-#else
- const AccountConsistencyMethod kDefaultMethod =
- AccountConsistencyMethod::kDisabled;
-#endif
-
- if (!base::FeatureList::IsEnabled(kAccountConsistencyFeature))
- return kDefaultMethod;
-
- std::string method_value = base::GetFieldTrialParamValueByFeature(
- kAccountConsistencyFeature, kAccountConsistencyFeatureMethodParameter);
-
- if (method_value == kAccountConsistencyFeatureMethodMirror)
- return AccountConsistencyMethod::kMirror;
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- else if (method_value == kAccountConsistencyFeatureMethodDiceFixAuthErrors)
- return AccountConsistencyMethod::kDiceFixAuthErrors;
- else if (method_value == kAccountConsistencyFeatureMethodDicePrepareMigration)
- return AccountConsistencyMethod::kDicePrepareMigration;
- else if (method_value == kAccountConsistencyFeatureMethodDiceMigration)
- return AccountConsistencyMethod::kDiceMigration;
- else if (method_value == kAccountConsistencyFeatureMethodDice)
- return AccountConsistencyMethod::kDice;
-#endif
-
- return kDefaultMethod;
-}
-
-bool IsAccountConsistencyMirrorEnabled() {
- return GetAccountConsistencyMethod() == AccountConsistencyMethod::kMirror;
-}
-
-bool IsDicePrepareMigrationEnabled() {
- return AccountConsistencyMethodGreaterOrEqual(
- GetAccountConsistencyMethod(),
- AccountConsistencyMethod::kDicePrepareMigration);
-}
-
-bool IsDiceMigrationEnabled() {
- return AccountConsistencyMethodGreaterOrEqual(
- GetAccountConsistencyMethod(), AccountConsistencyMethod::kDiceMigration);
-}
-
-bool IsDiceEnabledForProfile(const PrefService* user_prefs) {
- DCHECK(user_prefs);
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- return IsDiceEnabledForPrefValue(
- user_prefs->GetBoolean(kDiceMigrationCompletePref));
-#else
- return false;
-#endif
-}
-
-bool IsDiceEnabled(const BooleanPrefMember* dice_pref_member) {
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- DCHECK(dice_pref_member);
- DCHECK_EQ(kDiceMigrationCompletePref, dice_pref_member->GetPrefName());
- return IsDiceEnabledForPrefValue(dice_pref_member->GetValue());
-#else
- return false;
-#endif
-}
-
-std::unique_ptr<BooleanPrefMember> CreateDicePrefMember(
- PrefService* user_prefs) {
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- std::unique_ptr<BooleanPrefMember> pref_member =
- std::make_unique<BooleanPrefMember>();
- pref_member->Init(kDiceMigrationCompletePref, user_prefs);
- return pref_member;
-#else
- return std::unique_ptr<BooleanPrefMember>();
-#endif
-}
-
-void MigrateProfileToDice(PrefService* user_prefs) {
- DCHECK(IsDiceMigrationEnabled());
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- user_prefs->SetBoolean(kDiceMigrationCompletePref, true);
-#endif
-}
-
-bool IsDiceFixAuthErrorsEnabled() {
- return AccountConsistencyMethodGreaterOrEqual(
- GetAccountConsistencyMethod(),
- AccountConsistencyMethod::kDiceFixAuthErrors);
-}
-
bool IsExtensionsMultiAccount() {
#if defined(OS_ANDROID) || defined(OS_IOS)
NOTREACHED() << "Extensions are not enabled on Android or iOS";
@@ -204,23 +38,7 @@ bool IsExtensionsMultiAccount() {
#endif
return base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kExtensionsMultiAccount) ||
- GetAccountConsistencyMethod() == AccountConsistencyMethod::kMirror;
-}
-
-void SetGaiaOriginIsolatedCallback(
- const base::RepeatingCallback<bool()>& is_gaia_isolated) {
- *GetIsGaiaIsolatedCallback() = is_gaia_isolated;
-}
-
-UnifiedConsentFeatureState GetUnifiedConsentFeatureState() {
- if (!base::FeatureList::IsEnabled(signin::kUnifiedConsent))
- return UnifiedConsentFeatureState::kDisabled;
-
- std::string show_bump = base::GetFieldTrialParamValueByFeature(
- kUnifiedConsent, kUnifiedConsentShowBumpParameter);
- return show_bump.empty() ? UnifiedConsentFeatureState::kEnabledNoBump
- : UnifiedConsentFeatureState::kEnabledWithBump;
+ switches::kExtensionsMultiAccount);
}
} // namespace signin
diff --git a/chromium/components/signin/core/browser/profile_management_switches.h b/chromium/components/signin/core/browser/profile_management_switches.h
index 281effab049..067eb55e820 100644
--- a/chromium/components/signin/core/browser/profile_management_switches.h
+++ b/chromium/components/signin/core/browser/profile_management_switches.h
@@ -9,48 +9,10 @@
#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_PROFILE_MANAGEMENT_SWITCHES_H_
#define COMPONENTS_SIGNIN_CORE_BROWSER_PROFILE_MANAGEMENT_SWITCHES_H_
-#include <memory>
-
-#include "base/callback_forward.h"
#include "base/feature_list.h"
-#include "components/prefs/pref_member.h"
-
-class PrefService;
-
-namespace user_prefs {
-class PrefRegistrySyncable;
-}
namespace signin {
-// Account consistency feature. Only used on platforms where Mirror is not
-// always enabled (ENABLE_MIRROR is false).
-extern const base::Feature kAccountConsistencyFeature;
-
-// The account consistency method feature parameter name.
-extern const char kAccountConsistencyFeatureMethodParameter[];
-
-// Account consistency method feature values.
-extern const char kAccountConsistencyFeatureMethodMirror[];
-extern const char kAccountConsistencyFeatureMethodDiceFixAuthErrors[];
-extern const char kAccountConsistencyFeatureMethodDicePrepareMigration[];
-extern const char kAccountConsistencyFeatureMethodDiceMigration[];
-extern const char kAccountConsistencyFeatureMethodDice[];
-
-// Improved and unified consent for privacy-related features.
-extern const base::Feature kUnifiedConsent;
-extern const char kUnifiedConsentShowBumpParameter[];
-
-// State of the "Unified Consent" feature.
-enum class UnifiedConsentFeatureState {
- // Unified consent is disabled.
- kDisabled,
- // Unified consent is enabled, but the bump is not shown.
- kEnabledNoBump,
- // Unified consent is enabled and the bump is shown.
- kEnabledWithBump
-};
-
// TODO(https://crbug.com/777774): Cleanup this enum and remove related
// functions once Dice is fully rolled out, and/or Mirror code is removed on
// desktop.
@@ -85,79 +47,11 @@ bool DiceMethodGreaterOrEqual(AccountConsistencyMethod a,
AccountConsistencyMethod b);
////////////////////////////////////////////////////////////////////////////////
-// AccountConsistencyMethod related functions:
-
-// WARNING: DEPRECATED. These methods are global, but account consistency is per
-// profile.
-
-// Returns the account consistency method.
-AccountConsistencyMethod GetAccountConsistencyMethod();
-
-// Checks whether Mirror account consistency is enabled. If enabled, the account
-// management UI is available in the avatar bubble.
-bool IsAccountConsistencyMirrorEnabled();
-
-// Returns true if the account consistency method is kDiceFixAuthErrors or
-// greater.
-bool IsDiceFixAuthErrorsEnabled();
-
-// Returns true if the account consistency method is
-// kDicePrepareMigration or greater.
-bool IsDicePrepareMigrationEnabled();
-
-// Returns true if Dice account consistency is enabled or if the Dice migration
-// process is in progress (account consistency method is kDice or
-// kDiceMigration).
-// To check wether Dice is enabled (i.e. the migration is complete), use
-// IsDiceEnabledForProfile().
-bool IsDiceMigrationEnabled();
-
-////////////////////////////////////////////////////////////////////////////////
-// Functions to test if Dice is enabled for a user profile:
-
-// If true, then account management is done through Gaia webpages.
-// Can only be used on the UI thread.
-bool IsDiceEnabledForProfile(const PrefService* user_prefs);
-
-// If true, then account management is done through Gaia webpages.
-// Can be called on any thread, using a pref member obtained with
-// CreateDicePrefMember().
-// On the UI thread, consider using IsDiceEnabledForProfile() instead.
-// Example usage:
-//
-// // On UI thread:
-// std::unique_ptr<BooleanPrefMember> pref_member = GetDicePrefMember(prefs);
-// pref_member->MoveToThread(io_thread);
-//
-// // Later, on IO thread:
-// bool dice_enabled = GetDicePrefMember(pref_member.get());
-bool IsDiceEnabled(const BooleanPrefMember* dice_pref_member);
-
-// Gets a pref member suitable to use with IsDiceEnabled().
-std::unique_ptr<BooleanPrefMember> CreateDicePrefMember(
- PrefService* user_prefs);
-
-// Called to migrate a profile to Dice. After this call, it is enabled forever.
-void MigrateProfileToDice(PrefService* user_prefs);
-
-////////////////////////////////////////////////////////////////////////////////
// Other functions:
-// Register account consistency user preferences.
-void RegisterAccountConsistencyProfilePrefs(
- user_prefs::PrefRegistrySyncable* registry);
-
// Whether the chrome.identity API should be multi-account.
bool IsExtensionsMultiAccount();
-// |is_gaia_isolated| callback returns whether Gaia origin is isolated, which is
-// a requirement for kDicePrepareMigration and later Dice steps.
-void SetGaiaOriginIsolatedCallback(
- const base::RepeatingCallback<bool()>& is_gaia_isolated);
-
-// Returns the state of the "Unified Consent" feature.
-UnifiedConsentFeatureState GetUnifiedConsentFeatureState();
-
} // namespace signin
#endif // COMPONENTS_SIGNIN_CORE_BROWSER_PROFILE_MANAGEMENT_SWITCHES_H_
diff --git a/chromium/components/signin/core/browser/profile_management_switches_unittest.cc b/chromium/components/signin/core/browser/profile_management_switches_unittest.cc
deleted file mode 100644
index 5efa1cc3ef0..00000000000
--- a/chromium/components/signin/core/browser/profile_management_switches_unittest.cc
+++ /dev/null
@@ -1,151 +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/profile_management_switches.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
-#include "build/buildflag.h"
-#include "components/prefs/pref_member.h"
-#include "components/signin/core/browser/scoped_account_consistency.h"
-#include "components/signin/core/browser/scoped_unified_consent.h"
-#include "components/signin/core/browser/signin_buildflags.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace signin {
-
-#if BUILDFLAG(ENABLE_MIRROR)
-
-TEST(ProfileManagementSwitchesTest, GetAccountConsistencyMethodMirror) {
- // Mirror is enabled by default on some platforms.
- EXPECT_EQ(AccountConsistencyMethod::kMirror, GetAccountConsistencyMethod());
- EXPECT_TRUE(IsAccountConsistencyMirrorEnabled());
- EXPECT_FALSE(IsDiceMigrationEnabled());
- EXPECT_FALSE(IsDiceFixAuthErrorsEnabled());
-}
-
-#else
-
-TEST(ProfileManagementSwitchesTest, GetAccountConsistencyMethod) {
- SetGaiaOriginIsolatedCallback(base::Bind([] { return true; }));
- base::MessageLoop loop;
- sync_preferences::TestingPrefServiceSyncable pref_service;
- RegisterAccountConsistencyProfilePrefs(pref_service.registry());
- std::unique_ptr<BooleanPrefMember> dice_pref_member =
- CreateDicePrefMember(&pref_service);
-
-// Check the default account consistency method.
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- EXPECT_EQ(AccountConsistencyMethod::kDiceFixAuthErrors,
- GetAccountConsistencyMethod());
-#else
- EXPECT_EQ(AccountConsistencyMethod::kDisabled, GetAccountConsistencyMethod());
- EXPECT_FALSE(IsAccountConsistencyMirrorEnabled());
- EXPECT_FALSE(IsDiceFixAuthErrorsEnabled());
- EXPECT_FALSE(IsDiceMigrationEnabled());
- EXPECT_FALSE(IsDicePrepareMigrationEnabled());
- EXPECT_FALSE(IsDiceEnabledForProfile(&pref_service));
- EXPECT_FALSE(IsDiceEnabled(dice_pref_member.get()));
-#endif
-
- struct TestCase {
- AccountConsistencyMethod method;
- bool expect_mirror_enabled;
- bool expect_dice_fix_auth_errors;
- bool expect_dice_prepare_migration;
- bool expect_dice_prepare_migration_new_endpoint;
- bool expect_dice_migration;
- bool expect_dice_enabled_for_profile;
- } test_cases[] = {
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- {AccountConsistencyMethod::kDiceFixAuthErrors, false, true, false, false,
- false, false},
- {AccountConsistencyMethod::kDicePrepareMigration, false, true, true, true,
- false, false},
- {AccountConsistencyMethod::kDiceMigration, false, true, true, true, true,
- false},
- {AccountConsistencyMethod::kDice, false, true, true, true, true, true},
-#endif
- {AccountConsistencyMethod::kMirror, true, false, false, false, false, false}
- };
-
- for (const TestCase& test_case : test_cases) {
- ScopedAccountConsistency scoped_method(test_case.method);
- EXPECT_EQ(test_case.method, GetAccountConsistencyMethod());
- EXPECT_EQ(test_case.expect_mirror_enabled,
- IsAccountConsistencyMirrorEnabled());
- EXPECT_EQ(test_case.expect_dice_fix_auth_errors,
- IsDiceFixAuthErrorsEnabled());
- EXPECT_EQ(test_case.expect_dice_migration, IsDiceMigrationEnabled());
- EXPECT_EQ(test_case.expect_dice_prepare_migration_new_endpoint,
- IsDicePrepareMigrationEnabled());
- EXPECT_EQ(test_case.expect_dice_enabled_for_profile,
- IsDiceEnabledForProfile(&pref_service));
- EXPECT_EQ(test_case.expect_dice_enabled_for_profile,
- IsDiceEnabled(dice_pref_member.get()));
- }
-}
-
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
-TEST(ProfileManagementSwitchesTest, DiceMigration) {
- base::MessageLoop loop;
- sync_preferences::TestingPrefServiceSyncable pref_service;
- RegisterAccountConsistencyProfilePrefs(pref_service.registry());
- std::unique_ptr<BooleanPrefMember> dice_pref_member =
- CreateDicePrefMember(&pref_service);
-
- {
- ScopedAccountConsistencyDiceMigration scoped_dice_migration;
- MigrateProfileToDice(&pref_service);
- }
-
- struct TestCase {
- AccountConsistencyMethod method;
- bool expect_dice_enabled_for_profile;
- } test_cases[] = {{AccountConsistencyMethod::kDiceFixAuthErrors, false},
- {AccountConsistencyMethod::kDicePrepareMigration, false},
- {AccountConsistencyMethod::kDiceMigration, true},
- {AccountConsistencyMethod::kDice, true},
- {AccountConsistencyMethod::kMirror, false}};
-
- for (const TestCase& test_case : test_cases) {
- ScopedAccountConsistency scoped_method(test_case.method);
- EXPECT_EQ(test_case.expect_dice_enabled_for_profile,
- IsDiceEnabledForProfile(&pref_service));
- EXPECT_EQ(test_case.expect_dice_enabled_for_profile,
- IsDiceEnabled(dice_pref_member.get()));
- }
-}
-
-// Tests that Dice is disabled when site isolation is disabled.
-TEST(ProfileManagementSwitchesTest, GaiaSiteIsolation) {
- ScopedAccountConsistencyDicePrepareMigration scoped_dice;
- ASSERT_TRUE(IsDicePrepareMigrationEnabled());
-
- SetGaiaOriginIsolatedCallback(base::Bind([] { return false; }));
- EXPECT_FALSE(IsDicePrepareMigrationEnabled());
-}
-#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
-
-#endif // BUILDFLAG(ENABLE_MIRROR)
-
-TEST(ProfileManagementSwitchesTest, UnifiedConsent) {
- // Unified consent is disabled by default.
- EXPECT_EQ(UnifiedConsentFeatureState::kDisabled,
- GetUnifiedConsentFeatureState());
-
- for (UnifiedConsentFeatureState state :
- {UnifiedConsentFeatureState::kDisabled,
- UnifiedConsentFeatureState::kEnabledNoBump,
- UnifiedConsentFeatureState::kEnabledWithBump}) {
- ScopedUnifiedConsent scoped_state(state);
- EXPECT_EQ(state, GetUnifiedConsentFeatureState());
- }
-}
-
-} // namespace signin
diff --git a/chromium/components/signin/core/browser/profile_oauth2_token_service.cc b/chromium/components/signin/core/browser/profile_oauth2_token_service.cc
index 833246075f8..5a957134c23 100644
--- a/chromium/components/signin/core/browser/profile_oauth2_token_service.cc
+++ b/chromium/components/signin/core/browser/profile_oauth2_token_service.cc
@@ -12,7 +12,9 @@
ProfileOAuth2TokenService::ProfileOAuth2TokenService(
std::unique_ptr<OAuth2TokenServiceDelegate> delegate)
- : OAuth2TokenService(std::move(delegate)), all_credentials_loaded_(false) {
+ : OAuth2TokenService(std::move(delegate)),
+ all_credentials_loaded_(false),
+ diagnostics_client_(nullptr) {
AddObserver(this);
}
@@ -61,12 +63,38 @@ const net::BackoffEntry* ProfileOAuth2TokenService::GetDelegateBackoffEntry() {
void ProfileOAuth2TokenService::OnRefreshTokenAvailable(
const std::string& account_id) {
+ // Check if the newly-updated token is valid (invalid tokens are inserted when
+ // the user signs out on the web with DICE enabled).
+ bool is_valid = true;
+ GoogleServiceAuthError token_error = GetAuthError(account_id);
+ if (token_error == GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
+ GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+ CREDENTIALS_REJECTED_BY_CLIENT)) {
+ is_valid = false;
+ }
+
+ // NOTE: The code executed in the rest of this method does not affect the
+ // state of the accounts in this object, so it doesn't matter whether the
+ // callout to |diagnostics_client_| is made before or after. If that fact ever
+ // changes, it will be necessary to reason about what the ordering should be.
+ if (diagnostics_client_) {
+ diagnostics_client_->WillFireOnRefreshTokenAvailable(account_id, is_valid);
+ }
+
CancelRequestsForAccount(account_id);
ClearCacheForAccount(account_id);
}
void ProfileOAuth2TokenService::OnRefreshTokenRevoked(
const std::string& account_id) {
+ // NOTE: The code executed in the rest of this method does not affect the
+ // state of the accounts in this object, so it doesn't matter whether the
+ // callout to |diagnostics_client_| is made before or after. If that fact ever
+ // changes, it will be necessary to reason about what the ordering should be.
+ if (diagnostics_client_) {
+ diagnostics_client_->WillFireOnRefreshTokenRevoked(account_id);
+ }
+
CancelRequestsForAccount(account_id);
ClearCacheForAccount(account_id);
}
diff --git a/chromium/components/signin/core/browser/profile_oauth2_token_service.h b/chromium/components/signin/core/browser/profile_oauth2_token_service.h
index a86b764bb39..1a07745c48c 100644
--- a/chromium/components/signin/core/browser/profile_oauth2_token_service.h
+++ b/chromium/components/signin/core/browser/profile_oauth2_token_service.h
@@ -14,6 +14,10 @@
#include "google_apis/gaia/oauth2_token_service_delegate.h"
#include "net/base/backoff_entry.h"
+namespace identity {
+class IdentityManager;
+}
+
namespace user_prefs {
class PrefRegistrySyncable;
}
@@ -73,13 +77,45 @@ class ProfileOAuth2TokenService : public OAuth2TokenService,
}
private:
+ friend class identity::IdentityManager;
+
+ // Interface that gives information on internal TokenService operations. Only
+ // for use by IdentityManager during the conversion of the codebase to use
+ // //services/identity/public/cpp.
+ // NOTE: This interface is defined on ProfileOAuth2TokenService rather than
+ // on the OAuth2TokenService base class for multiple reasons:
+ // (1) The base class already has a DiagnosticsObserver interface, from
+ // which this interface differs because there can be only one instance.
+ // (2) PO2TS itself observes O2TS and for correctness must receive observer
+ // callbacks before any other O2TS observer. Hence, these DiagnosticsClient
+ // callouts must go *inside* PO2TS's implementations of the O2TS observer
+ // methods.
+ class DiagnosticsClient {
+ public:
+ // Sent just before OnRefreshTokenAvailable() is fired on observers.
+ // |is_valid| indicates whether the token is valid.
+ virtual void WillFireOnRefreshTokenAvailable(const std::string& account_id,
+ bool is_valid) = 0;
+ // Sent just before OnRefreshTokenRevoked() is fired on observers.
+ virtual void WillFireOnRefreshTokenRevoked(
+ const std::string& account_id) = 0;
+ };
+
void OnRefreshTokenAvailable(const std::string& account_id) override;
void OnRefreshTokenRevoked(const std::string& account_id) override;
void OnRefreshTokensLoaded() override;
+ void set_diagnostics_client(DiagnosticsClient* diagnostics_client) {
+ DCHECK(!diagnostics_client_ || !diagnostics_client);
+ diagnostics_client_ = diagnostics_client;
+ }
+
// Whether all credentials have been loaded.
bool all_credentials_loaded_;
+ // The DiagnosticsClient object associated with this object. May be null.
+ DiagnosticsClient* diagnostics_client_;
+
DISALLOW_COPY_AND_ASSIGN(ProfileOAuth2TokenService);
};
diff --git a/chromium/components/signin/core/browser/scoped_account_consistency.cc b/chromium/components/signin/core/browser/scoped_account_consistency.cc
deleted file mode 100644
index 820386156d7..00000000000
--- a/chromium/components/signin/core/browser/scoped_account_consistency.cc
+++ /dev/null
@@ -1,67 +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/scoped_account_consistency.h"
-
-#include <map>
-#include <string>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/feature_list.h"
-#include "base/logging.h"
-#include "base/test/scoped_feature_list.h"
-#include "components/signin/core/browser/signin_buildflags.h"
-
-namespace signin {
-
-ScopedAccountConsistency::ScopedAccountConsistency(
- AccountConsistencyMethod method) {
- DCHECK_NE(AccountConsistencyMethod::kDisabled, method);
-#if !BUILDFLAG(ENABLE_DICE_SUPPORT)
- DCHECK_NE(AccountConsistencyMethod::kDice, method);
- DCHECK_NE(AccountConsistencyMethod::kDiceFixAuthErrors, method);
-#endif
-
-#if BUILDFLAG(ENABLE_MIRROR)
- DCHECK_EQ(AccountConsistencyMethod::kMirror, method);
- return;
-#endif
-
- signin::SetGaiaOriginIsolatedCallback(base::Bind([] { return true; }));
-
- // Set up the account consistency method.
- std::string feature_value;
- switch (method) {
- case AccountConsistencyMethod::kDisabled:
- NOTREACHED();
- break;
- case AccountConsistencyMethod::kMirror:
- feature_value = kAccountConsistencyFeatureMethodMirror;
- break;
- case AccountConsistencyMethod::kDiceFixAuthErrors:
- feature_value = kAccountConsistencyFeatureMethodDiceFixAuthErrors;
- break;
- case AccountConsistencyMethod::kDicePrepareMigration:
- feature_value = kAccountConsistencyFeatureMethodDicePrepareMigration;
- break;
- case AccountConsistencyMethod::kDiceMigration:
- feature_value = kAccountConsistencyFeatureMethodDiceMigration;
- break;
- case AccountConsistencyMethod::kDice:
- feature_value = kAccountConsistencyFeatureMethodDice;
- break;
- }
-
- std::map<std::string, std::string> feature_params;
- feature_params[kAccountConsistencyFeatureMethodParameter] = feature_value;
-
- scoped_feature_list_.InitAndEnableFeatureWithParameters(
- kAccountConsistencyFeature, feature_params);
- DCHECK_EQ(method, GetAccountConsistencyMethod());
-}
-
-ScopedAccountConsistency::~ScopedAccountConsistency() {}
-
-} // namespace signin
diff --git a/chromium/components/signin/core/browser/scoped_account_consistency.h b/chromium/components/signin/core/browser/scoped_account_consistency.h
deleted file mode 100644
index 0b9db2fd696..00000000000
--- a/chromium/components/signin/core/browser/scoped_account_consistency.h
+++ /dev/null
@@ -1,58 +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_SIGNIN_CORE_BROWSER_SCOPED_ACCOUNT_CONSISTENCY_H_
-#define COMPONENTS_SIGNIN_CORE_BROWSER_SCOPED_ACCOUNT_CONSISTENCY_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/test/scoped_feature_list.h"
-#include "components/signin/core/browser/profile_management_switches.h"
-
-namespace signin {
-
-// Changes the account consistency method while it is in scope. Useful for
-// tests.
-class ScopedAccountConsistency {
- public:
- explicit ScopedAccountConsistency(AccountConsistencyMethod method);
- ~ScopedAccountConsistency();
-
- private:
- base::test::ScopedFeatureList scoped_feature_list_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedAccountConsistency);
-};
-
-// Specialized helper classes for each account consistency method:
-// ScopedAccountConsistencyDice, ScopedAccountConsistencyMirror, ...
-
-#define SCOPED_ACCOUNT_CONSISTENCY_SPECIALIZATION(method) \
- class ScopedAccountConsistency##method { \
- public: \
- ScopedAccountConsistency##method() \
- : scoped_consistency_(AccountConsistencyMethod::k##method) {} \
- \
- private: \
- ScopedAccountConsistency scoped_consistency_; \
- DISALLOW_COPY_AND_ASSIGN(ScopedAccountConsistency##method); \
- }
-
-// ScopedAccountConsistencyMirror:
-SCOPED_ACCOUNT_CONSISTENCY_SPECIALIZATION(Mirror);
-// ScopedAccountConsistencyDiceFixAuthErrors:
-SCOPED_ACCOUNT_CONSISTENCY_SPECIALIZATION(DiceFixAuthErrors);
-// ScopedAccountConsistencyDicePrepareMigration:
-SCOPED_ACCOUNT_CONSISTENCY_SPECIALIZATION(DicePrepareMigration);
-// ScopedAccountConsistencyDiceMigration:
-SCOPED_ACCOUNT_CONSISTENCY_SPECIALIZATION(DiceMigration);
-// ScopedAccountConsistencyDice:
-SCOPED_ACCOUNT_CONSISTENCY_SPECIALIZATION(Dice);
-
-#undef SCOPED_ACCOUNT_CONSISTENCY_SPECIALIZATION
-
-} // namespace signin
-
-#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SCOPED_ACCOUNT_CONSISTENCY_H_
diff --git a/chromium/components/signin/core/browser/signin_client.cc b/chromium/components/signin/core/browser/signin_client.cc
index ffcbf919b95..4b25fc9c244 100644
--- a/chromium/components/signin/core/browser/signin_client.cc
+++ b/chromium/components/signin/core/browser/signin_client.cc
@@ -19,6 +19,13 @@ std::string SigninClient::GenerateSigninScopedDeviceID(bool for_ephemeral) {
return for_ephemeral ? kEphemeralUserDeviceIDPrefix + guid : guid;
}
+#if !defined(OS_CHROMEOS)
+void SigninClient::RecreateSigninScopedDeviceId() {
+ GetPrefs()->SetString(prefs::kGoogleServicesSigninScopedDeviceId,
+ GenerateSigninScopedDeviceID(false));
+}
+#endif
+
std::string SigninClient::GetOrCreateScopedDeviceIdPref(PrefService* prefs) {
std::string signin_scoped_device_id =
prefs->GetString(prefs::kGoogleServicesSigninScopedDeviceId);
@@ -43,6 +50,5 @@ void SigninClient::PreGaiaLogout(base::OnceClosure callback) {
}
void SigninClient::SignOut() {
- GetPrefs()->ClearPref(prefs::kGoogleServicesSigninScopedDeviceId);
OnSignedOut();
}
diff --git a/chromium/components/signin/core/browser/signin_client.h b/chromium/components/signin/core/browser/signin_client.h
index eba0efb418d..44a648b2beb 100644
--- a/chromium/components/signin/core/browser/signin_client.h
+++ b/chromium/components/signin/core/browser/signin_client.h
@@ -10,6 +10,7 @@
#include "base/callback.h"
#include "base/callback_list.h"
#include "base/time/time.h"
+#include "build/build_config.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/signin/core/browser/account_info.h"
#include "components/signin/core/browser/profile_management_switches.h"
@@ -29,6 +30,13 @@ class Observer;
namespace net {
class URLRequestContextGetter;
}
+namespace network {
+class SharedURLLoaderFactory;
+}
+
+namespace network {
+class SharedURLLoaderFactory;
+}
// An interface that needs to be supplied to the Signin component by its
// embedder.
@@ -70,11 +78,13 @@ class SigninClient : public KeyedService {
virtual std::string GetSigninScopedDeviceId() = 0;
// Returns the URL request context information associated with the client.
+ // DEPRECATED, new code should be using GetURLLoaderFactory instead.
virtual net::URLRequestContextGetter* GetURLRequestContext() = 0;
- // Returns whether the user's credentials should be merged into the cookie
- // jar on signin completion.
- virtual bool ShouldMergeSigninCredentialsIntoCookieJar() = 0;
+ // Returns the SharedURLLoaderFactory that should be used to fetch resources
+ // associated with the client.
+ virtual scoped_refptr<network::SharedURLLoaderFactory>
+ GetURLLoaderFactory() = 0;
// Returns a string containing the version info of the product in which the
// Signin component is being used.
@@ -129,7 +139,7 @@ class SigninClient : public KeyedService {
virtual std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcher(
GaiaAuthConsumer* consumer,
const std::string& source,
- net::URLRequestContextGetter* getter) = 0;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) = 0;
// Called once the credentials has been copied to another SigninManager.
virtual void AfterCredentialsCopied() {}
@@ -137,6 +147,12 @@ class SigninClient : public KeyedService {
// Schedules migration to happen at next startup.
virtual void SetReadyForDiceMigration(bool is_ready) {}
+#if !defined(OS_CHROMEOS)
+ // Forces the generation of a new device ID, and stores it in the pref
+ // service.
+ void RecreateSigninScopedDeviceId();
+#endif
+
protected:
// Returns device id that is scoped to single signin.
// Stores the ID in the kGoogleServicesSigninScopedDeviceId pref.
diff --git a/chromium/components/signin/core/browser/signin_header_helper.cc b/chromium/components/signin/core/browser/signin_header_helper.cc
index 720d7266f01..196dc5aecc0 100644
--- a/chromium/components/signin/core/browser/signin_header_helper.cc
+++ b/chromium/components/signin/core/browser/signin_header_helper.cc
@@ -11,7 +11,6 @@
#include "base/strings/string_split.h"
#include "components/google/core/browser/google_util.h"
#include "components/signin/core/browser/chrome_connected_header_helper.h"
-#include "components/signin/core/browser/cookie_settings_util.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "net/base/escape.h"
#include "net/url_request/url_request.h"
@@ -65,6 +64,27 @@ DiceResponseParams::EnableSyncInfo::~EnableSyncInfo() {}
DiceResponseParams::EnableSyncInfo::EnableSyncInfo(const EnableSyncInfo&) =
default;
+RequestAdapter::RequestAdapter(net::URLRequest* request) : request_(request) {}
+
+RequestAdapter::~RequestAdapter() = default;
+
+const GURL& RequestAdapter::GetUrl() {
+ return request_->url();
+}
+
+bool RequestAdapter::HasHeader(const std::string& name) {
+ return request_->extra_request_headers().HasHeader(name);
+}
+
+void RequestAdapter::RemoveRequestHeaderByName(const std::string& name) {
+ return request_->RemoveRequestHeaderByName(name);
+}
+
+void RequestAdapter::SetExtraHeaderByName(const std::string& name,
+ const std::string& value) {
+ return request_->SetExtraRequestHeaderByName(name, value, false);
+}
+
std::string BuildMirrorRequestCookieIfPossible(
const GURL& url,
const std::string& account_id,
@@ -76,7 +96,7 @@ std::string BuildMirrorRequestCookieIfPossible(
}
bool SigninHeaderHelper::AppendOrRemoveRequestHeader(
- net::URLRequest* request,
+ RequestAdapter* request,
const GURL& redirect_url,
const char* header_name,
const std::string& header_value) {
@@ -84,15 +104,14 @@ bool SigninHeaderHelper::AppendOrRemoveRequestHeader(
// If the request is being redirected, and it has the account consistency
// header, and current url is a Google URL, and the redirected one is not,
// remove the header.
- if (!redirect_url.is_empty() &&
- request->extra_request_headers().HasHeader(header_name) &&
- IsUrlEligibleForRequestHeader(request->url()) &&
+ if (!redirect_url.is_empty() && request->HasHeader(header_name) &&
+ IsUrlEligibleForRequestHeader(request->GetUrl()) &&
!IsUrlEligibleForRequestHeader(redirect_url)) {
request->RemoveRequestHeaderByName(header_name);
}
return false;
}
- request->SetExtraRequestHeaderByName(header_name, header_value, false);
+ request->SetExtraHeaderByName(header_name, header_value);
return true;
}
@@ -119,28 +138,14 @@ SigninHeaderHelper::ParseAccountConsistencyResponseHeader(
return dictionary;
}
-bool SigninHeaderHelper::ShouldBuildRequestHeader(
- const GURL& url,
- const content_settings::CookieSettings* cookie_settings) {
- // If signin cookies are not allowed, don't add the header.
- if (!SettingsAllowSigninCookies(cookie_settings))
- return false;
-
- // Check if url is eligible for the header.
- if (!IsUrlEligibleForRequestHeader(url))
- return false;
-
- return true;
-}
-
void AppendOrRemoveMirrorRequestHeader(
- net::URLRequest* request,
+ RequestAdapter* request,
const GURL& redirect_url,
const std::string& account_id,
AccountConsistencyMethod account_consistency,
const content_settings::CookieSettings* cookie_settings,
int profile_mode_mask) {
- const GURL& url = redirect_url.is_empty() ? request->url() : redirect_url;
+ const GURL& url = redirect_url.is_empty() ? request->GetUrl() : redirect_url;
ChromeConnectedHeaderHelper chrome_connected_helper(account_consistency);
std::string chrome_connected_header_value;
if (chrome_connected_helper.ShouldBuildRequestHeader(url, cookie_settings)) {
@@ -153,22 +158,23 @@ void AppendOrRemoveMirrorRequestHeader(
}
bool AppendOrRemoveDiceRequestHeader(
- net::URLRequest* request,
+ RequestAdapter* request,
const GURL& redirect_url,
const std::string& account_id,
bool sync_enabled,
bool sync_has_auth_error,
AccountConsistencyMethod account_consistency,
- const content_settings::CookieSettings* cookie_settings) {
+ const content_settings::CookieSettings* cookie_settings,
+ const std::string& device_id) {
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- const GURL& url = redirect_url.is_empty() ? request->url() : redirect_url;
+ const GURL& url = redirect_url.is_empty() ? request->GetUrl() : redirect_url;
DiceHeaderHelper dice_helper(
!account_id.empty() && sync_has_auth_error && sync_enabled,
account_consistency);
std::string dice_header_value;
if (dice_helper.ShouldBuildRequestHeader(url, cookie_settings)) {
dice_header_value = dice_helper.BuildRequestHeader(
- sync_enabled ? account_id : std::string());
+ sync_enabled ? account_id : std::string(), device_id);
}
return dice_helper.AppendOrRemoveRequestHeader(
request, redirect_url, kDiceRequestHeader, dice_header_value);
diff --git a/chromium/components/signin/core/browser/signin_header_helper.h b/chromium/components/signin/core/browser/signin_header_helper.h
index 7e10bf1fc28..18c3a3253ea 100644
--- a/chromium/components/signin/core/browser/signin_header_helper.h
+++ b/chromium/components/signin/core/browser/signin_header_helper.h
@@ -145,21 +145,39 @@ struct DiceResponseParams {
DISALLOW_COPY_AND_ASSIGN(DiceResponseParams);
};
+class RequestAdapter {
+ public:
+ explicit RequestAdapter(net::URLRequest* request);
+ virtual ~RequestAdapter();
+
+ virtual const GURL& GetUrl();
+ virtual bool HasHeader(const std::string& name);
+ virtual void RemoveRequestHeaderByName(const std::string& name);
+ virtual void SetExtraHeaderByName(const std::string& name,
+ const std::string& value);
+
+ protected:
+ net::URLRequest* const request_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RequestAdapter);
+};
+
// Base class for managing the signin headers (Dice and Chrome-Connected).
class SigninHeaderHelper {
public:
// Appends or remove the header to a network request if necessary.
// Returns whether the request has the request header.
- bool AppendOrRemoveRequestHeader(net::URLRequest* request,
+ bool AppendOrRemoveRequestHeader(RequestAdapter* request,
const GURL& redirect_url,
const char* header_name,
const std::string& header_value);
// Returns wether an account consistency header should be built for this
// request.
- bool ShouldBuildRequestHeader(
+ virtual bool ShouldBuildRequestHeader(
const GURL& url,
- const content_settings::CookieSettings* cookie_settings);
+ const content_settings::CookieSettings* cookie_settings) = 0;
protected:
SigninHeaderHelper() {}
@@ -192,7 +210,7 @@ std::string BuildMirrorRequestCookieIfPossible(
// the exception of requests from gaia webview.
// Removes the header in case it should not be transfered to a redirected url.
void AppendOrRemoveMirrorRequestHeader(
- net::URLRequest* request,
+ RequestAdapter* request,
const GURL& redirect_url,
const std::string& account_id,
AccountConsistencyMethod account_consistency,
@@ -204,13 +222,14 @@ void AppendOrRemoveMirrorRequestHeader(
// Removes the header in case it should not be transfered to a redirected url.
// Returns whether the request has the Dice request header.
bool AppendOrRemoveDiceRequestHeader(
- net::URLRequest* request,
+ RequestAdapter* request,
const GURL& redirect_url,
const std::string& account_id,
bool sync_enabled,
bool sync_has_auth_error,
AccountConsistencyMethod account_consistency,
- const content_settings::CookieSettings* cookie_settings);
+ const content_settings::CookieSettings* cookie_settings,
+ const std::string& device_id);
// Returns the parameters contained in the X-Chrome-Manage-Accounts response
// header.
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 870bd70ce0b..c64b21ed68e 100644
--- a/chromium/components/signin/core/browser/signin_header_helper_unittest.cc
+++ b/chromium/components/signin/core/browser/signin_header_helper_unittest.cc
@@ -26,6 +26,10 @@
#include "components/signin/core/browser/dice_header_helper.h"
#endif
+namespace {
+constexpr char kTestDeviceId[] = "DeviceID";
+}
+
namespace signin {
class SigninHeaderHelperTest : public testing::Test {
@@ -58,12 +62,14 @@ class SigninHeaderHelperTest : public testing::Test {
std::unique_ptr<net::URLRequest> url_request =
url_request_context_.CreateRequest(url, net::DEFAULT_PRIORITY, nullptr,
TRAFFIC_ANNOTATION_FOR_TESTS);
+ RequestAdapter request_adapter(url_request.get());
AppendOrRemoveMirrorRequestHeader(
- url_request.get(), GURL(), account_id, account_consistency_,
+ &request_adapter, GURL(), account_id, account_consistency_,
cookie_settings_.get(), PROFILE_MODE_DEFAULT);
- AppendOrRemoveDiceRequestHeader(
- url_request.get(), GURL(), account_id, sync_enabled_,
- sync_has_auth_error_, account_consistency_, cookie_settings_.get());
+ AppendOrRemoveDiceRequestHeader(&request_adapter, GURL(), account_id,
+ sync_enabled_, sync_has_auth_error_,
+ account_consistency_,
+ cookie_settings_.get(), device_id_);
return url_request;
}
@@ -109,6 +115,7 @@ class SigninHeaderHelperTest : public testing::Test {
bool sync_enabled_ = false;
bool sync_has_auth_error_ = false;
+ std::string device_id_ = kTestDeviceId;
AccountConsistencyMethod account_consistency_ =
AccountConsistencyMethod::kDisabled;
@@ -182,8 +189,9 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestGoogleComNoProfileConsistency) {
url_request_context_.CreateRequest(GURL("https://www.google.com"),
net::DEFAULT_PRIORITY, nullptr,
TRAFFIC_ANNOTATION_FOR_TESTS);
+ RequestAdapter request_adapter(url_request.get());
AppendOrRemoveMirrorRequestHeader(
- url_request.get(), GURL(), "0123456789", account_consistency_,
+ &request_adapter, GURL(), "0123456789", account_consistency_,
cookie_settings_.get(), PROFILE_MODE_DEFAULT);
CheckAccountConsistencyHeaderRequest(url_request.get(),
kChromeConnectedHeader, "");
@@ -196,8 +204,9 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestGoogleComProfileConsistency) {
url_request_context_.CreateRequest(GURL("https://www.google.com"),
net::DEFAULT_PRIORITY, nullptr,
TRAFFIC_ANNOTATION_FOR_TESTS);
+ RequestAdapter request_adapter(url_request.get());
AppendOrRemoveMirrorRequestHeader(
- url_request.get(), GURL(), "0123456789", account_consistency_,
+ &request_adapter, GURL(), "0123456789", account_consistency_,
cookie_settings_.get(), PROFILE_MODE_DEFAULT);
CheckAccountConsistencyHeaderRequest(
url_request.get(), kChromeConnectedHeader,
@@ -233,17 +242,18 @@ TEST_F(SigninHeaderHelperTest, TestDiceRequest) {
CheckDiceHeaderRequest(
GURL("https://accounts.google.com"), "0123456789",
"mode=0,enable_account_consistency=false",
- base::StringPrintf("version=%s,client_id=%s,signin_mode=all_accounts,"
- "signout_mode=show_confirmation",
- kDiceProtocolVersion, client_id.c_str()));
+ base::StringPrintf(
+ "version=%s,client_id=%s,device_id=DeviceID,signin_mode=all_accounts,"
+ "signout_mode=show_confirmation",
+ kDiceProtocolVersion, client_id.c_str()));
// Sync enabled: check that the Dice header has the Sync account ID and that
// the mirror header is not modified.
sync_enabled_ = true;
CheckDiceHeaderRequest(
GURL("https://accounts.google.com"), "0123456789",
"mode=0,enable_account_consistency=false",
- base::StringPrintf("version=%s,client_id=%s,sync_account_id=0123456789,"
- "signin_mode=all_accounts,"
+ base::StringPrintf("version=%s,client_id=%s,device_id=DeviceID,"
+ "sync_account_id=0123456789,signin_mode=all_accounts,"
"signout_mode=show_confirmation",
kDiceProtocolVersion, client_id.c_str()));
sync_enabled_ = false;
@@ -252,6 +262,21 @@ TEST_F(SigninHeaderHelperTest, TestDiceRequest) {
CheckDiceHeaderRequest(GURL("https://www.google.com"), "0123456789", "", "");
}
+// When cookies are blocked, only the Dice header is sent.
+TEST_F(SigninHeaderHelperTest, DiceCookiesBlocked) {
+ account_consistency_ = AccountConsistencyMethod::kDice;
+ cookie_settings_->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
+
+ std::string client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
+ ASSERT_FALSE(client_id.empty());
+ CheckDiceHeaderRequest(
+ GURL("https://accounts.google.com"), "0123456789", "",
+ base::StringPrintf(
+ "version=%s,client_id=%s,device_id=DeviceID,signin_mode=all_accounts,"
+ "signout_mode=show_confirmation",
+ kDiceProtocolVersion, client_id.c_str()));
+}
+
// Tests that no Dice request is returned when Dice is not enabled.
TEST_F(SigninHeaderHelperTest, TestNoDiceRequestWhenDisabled) {
account_consistency_ = AccountConsistencyMethod::kMirror;
@@ -259,6 +284,21 @@ TEST_F(SigninHeaderHelperTest, TestNoDiceRequestWhenDisabled) {
"mode=0,enable_account_consistency=true", "");
}
+TEST_F(SigninHeaderHelperTest, TestDiceEmptyDeviceID) {
+ account_consistency_ = AccountConsistencyMethod::kDiceMigration;
+ std::string client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
+ ASSERT_FALSE(client_id.empty());
+
+ device_id_.clear();
+
+ CheckDiceHeaderRequest(
+ GURL("https://accounts.google.com"), "0123456789",
+ "mode=0,enable_account_consistency=false",
+ base::StringPrintf("version=%s,client_id=%s,signin_mode=all_accounts,"
+ "signout_mode=no_confirmation",
+ kDiceProtocolVersion, client_id.c_str()));
+}
+
// Tests that the signout confirmation is requested iff the Dice migration is
// complete.
TEST_F(SigninHeaderHelperTest, TestDiceMigration) {
@@ -270,18 +310,20 @@ TEST_F(SigninHeaderHelperTest, TestDiceMigration) {
CheckDiceHeaderRequest(
GURL("https://accounts.google.com"), "0123456789",
"mode=0,enable_account_consistency=false",
- base::StringPrintf("version=%s,client_id=%s,signin_mode=all_accounts,"
- "signout_mode=no_confirmation",
- kDiceProtocolVersion, client_id.c_str()));
+ base::StringPrintf(
+ "version=%s,client_id=%s,device_id=DeviceID,signin_mode=all_accounts,"
+ "signout_mode=no_confirmation",
+ kDiceProtocolVersion, client_id.c_str()));
// Signout confirmation after the migration is complete.
account_consistency_ = AccountConsistencyMethod::kDice;
CheckDiceHeaderRequest(
GURL("https://accounts.google.com"), "0123456789",
"mode=0,enable_account_consistency=false",
- base::StringPrintf("version=%s,client_id=%s,signin_mode=all_accounts,"
- "signout_mode=show_confirmation",
- kDiceProtocolVersion, client_id.c_str()));
+ base::StringPrintf(
+ "version=%s,client_id=%s,device_id=DeviceID,signin_mode=all_accounts,"
+ "signout_mode=show_confirmation",
+ kDiceProtocolVersion, client_id.c_str()));
}
// Tests that a Dice request is returned only when there is an authentication
@@ -309,8 +351,8 @@ TEST_F(SigninHeaderHelperTest, TestDiceFixAuthError) {
CheckDiceHeaderRequest(
GURL("https://accounts.google.com"), "0123456789",
"mode=0,enable_account_consistency=false",
- base::StringPrintf("version=%s,client_id=%s,sync_account_id=0123456789,"
- "signin_mode=sync_account,"
+ base::StringPrintf("version=%s,client_id=%s,device_id=DeviceID,"
+ "sync_account_id=0123456789,signin_mode=sync_account,"
"signout_mode=no_confirmation",
kDiceProtocolVersion, client_id.c_str()));
}
@@ -453,8 +495,9 @@ TEST_F(SigninHeaderHelperTest, TestMirrorHeaderEligibleRedirectURL) {
std::unique_ptr<net::URLRequest> url_request =
url_request_context_.CreateRequest(url, net::DEFAULT_PRIORITY, nullptr,
TRAFFIC_ANNOTATION_FOR_TESTS);
+ RequestAdapter request_adapter(url_request.get());
AppendOrRemoveMirrorRequestHeader(
- url_request.get(), redirect_url, account_id, account_consistency_,
+ &request_adapter, redirect_url, account_id, account_consistency_,
cookie_settings_.get(), PROFILE_MODE_DEFAULT);
EXPECT_TRUE(
url_request->extra_request_headers().HasHeader(kChromeConnectedHeader));
@@ -470,8 +513,9 @@ TEST_F(SigninHeaderHelperTest, TestMirrorHeaderNonEligibleRedirectURL) {
std::unique_ptr<net::URLRequest> url_request =
url_request_context_.CreateRequest(url, net::DEFAULT_PRIORITY, nullptr,
TRAFFIC_ANNOTATION_FOR_TESTS);
+ RequestAdapter request_adapter(url_request.get());
AppendOrRemoveMirrorRequestHeader(
- url_request.get(), redirect_url, account_id, account_consistency_,
+ &request_adapter, redirect_url, account_id, account_consistency_,
cookie_settings_.get(), PROFILE_MODE_DEFAULT);
EXPECT_FALSE(
url_request->extra_request_headers().HasHeader(kChromeConnectedHeader));
@@ -490,8 +534,9 @@ TEST_F(SigninHeaderHelperTest, TestIgnoreMirrorHeaderNonEligibleURLs) {
TRAFFIC_ANNOTATION_FOR_TESTS);
url_request->SetExtraRequestHeaderByName(kChromeConnectedHeader, fake_header,
false);
+ RequestAdapter request_adapter(url_request.get());
AppendOrRemoveMirrorRequestHeader(
- url_request.get(), redirect_url, account_id, account_consistency_,
+ &request_adapter, redirect_url, account_id, account_consistency_,
cookie_settings_.get(), PROFILE_MODE_DEFAULT);
std::string header;
EXPECT_TRUE(url_request->extra_request_headers().GetHeader(
diff --git a/chromium/components/signin/core/browser/signin_investigator_unittest.cc b/chromium/components/signin/core/browser/signin_investigator_unittest.cc
index ce351e2d24e..9857adcd32b 100644
--- a/chromium/components/signin/core/browser/signin_investigator_unittest.cc
+++ b/chromium/components/signin/core/browser/signin_investigator_unittest.cc
@@ -4,7 +4,7 @@
#include <string>
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/signin/core/browser/signin_investigator.h"
diff --git a/chromium/components/signin/core/browser/signin_manager.cc b/chromium/components/signin/core/browser/signin_manager.cc
index 2791801d2dc..ffbfa714068 100644
--- a/chromium/components/signin/core/browser/signin_manager.cc
+++ b/chromium/components/signin/core/browser/signin_manager.cc
@@ -214,7 +214,7 @@ void SigninManager::DoSignOut(
return;
}
- if (prohibit_signout_) {
+ if (IsSignoutProhibited()) {
DVLOG(1) << "Ignoring attempt to sign out while signout is prohibited";
return;
}
@@ -232,6 +232,9 @@ void SigninManager::DoSignOut(
client_->GetPrefs()->ClearPref(prefs::kGoogleServicesAccountId);
client_->GetPrefs()->ClearPref(prefs::kGoogleServicesUserAccountId);
client_->GetPrefs()->ClearPref(prefs::kSignedInTime);
+#if defined(OS_IOS) || defined(OS_ANDROID)
+ client_->RecreateSigninScopedDeviceId();
+#endif
client_->SignOut();
// Determine the duration the user was logged in and log that to UMA.
@@ -393,7 +396,7 @@ void SigninManager::DisableOneClickSignIn(PrefService* prefs) {
}
void SigninManager::MergeSigninCredentialIntoCookieJar() {
- if (!client_->ShouldMergeSigninCredentialsIntoCookieJar())
+ if (account_consistency_ == signin::AccountConsistencyMethod::kMirror)
return;
if (!IsAuthenticated())
diff --git a/chromium/components/signin/core/browser/signin_manager.h b/chromium/components/signin/core/browser/signin_manager.h
index 5170a22a304..2b9717ff83f 100644
--- a/chromium/components/signin/core/browser/signin_manager.h
+++ b/chromium/components/signin/core/browser/signin_manager.h
@@ -15,7 +15,10 @@
#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_MANAGER_H_
#define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_MANAGER_H_
+#include "build/build_config.h"
+
#if defined(OS_CHROMEOS)
+
#include "components/signin/core/browser/signin_manager_base.h"
#else
@@ -28,7 +31,6 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/observer_list.h"
-#include "build/build_config.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_member.h"
diff --git a/chromium/components/signin/core/browser/signin_manager_unittest.cc b/chromium/components/signin/core/browser/signin_manager_unittest.cc
index 1ae77c52090..f38e025f1e5 100644
--- a/chromium/components/signin/core/browser/signin_manager_unittest.cc
+++ b/chromium/components/signin/core/browser/signin_manager_unittest.cc
@@ -29,7 +29,6 @@
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_urls.h"
#include "net/cookies/cookie_monster.h"
-#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_status.h"
@@ -85,11 +84,9 @@ class SigninManagerTest : public testing::Test {
cookie_manager_service_(&token_service_,
GaiaConstants::kChromeSource,
&test_signin_client_),
- url_fetcher_factory_(nullptr),
account_consistency_(signin::AccountConsistencyMethod::kDisabled) {
test_signin_client_.SetURLRequestContext(
new net::TestURLRequestContextGetter(loop_.task_runner()));
- cookie_manager_service_.Init(&url_fetcher_factory_);
AccountFetcherService::RegisterPrefs(user_prefs_.registry());
AccountTrackerService::RegisterPrefs(user_prefs_.registry());
SigninManagerBase::RegisterProfilePrefs(user_prefs_.registry());
@@ -172,7 +169,6 @@ class SigninManagerTest : public testing::Test {
AccountTrackerService account_tracker_;
FakeGaiaCookieManagerService cookie_manager_service_;
FakeAccountFetcherService account_fetcher_;
- net::FakeURLFetcherFactory url_fetcher_factory_;
std::unique_ptr<SigninManager> manager_;
TestSigninManagerObserver test_observer_;
std::vector<std::string> oauth_tokens_fetched_;
diff --git a/chromium/components/signin/core/browser/signin_metrics.cc b/chromium/components/signin/core/browser/signin_metrics.cc
index 33dae6f6b12..3349eb3e6bb 100644
--- a/chromium/components/signin/core/browser/signin_metrics.cc
+++ b/chromium/components/signin/core/browser/signin_metrics.cc
@@ -245,44 +245,192 @@ void RecordSigninNotDefaultUserActionForAccessPoint(
}
}
-void RecordSigninNewAccountUserActionForAccessPoint(
+void RecordSigninNewAccountPreDiceUserActionForAccessPoint(
signin_metrics::AccessPoint access_point) {
switch (access_point) {
case signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS:
- base::RecordAction(
- base::UserMetricsAction("Signin_SigninNewAccount_FromSettings"));
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountPreDice_FromSettings"));
break;
case AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE:
base::RecordAction(base::UserMetricsAction(
- "Signin_SigninNewAccount_FromExtensionInstallBubble"));
+ "Signin_SigninNewAccountPreDice_FromExtensionInstallBubble"));
break;
case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE:
base::RecordAction(base::UserMetricsAction(
- "Signin_SigninNewAccount_FromBookmarkBubble"));
+ "Signin_SigninNewAccountPreDice_FromBookmarkBubble"));
break;
case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER:
base::RecordAction(base::UserMetricsAction(
- "Signin_SigninNewAccount_FromBookmarkManager"));
+ "Signin_SigninNewAccountPreDice_FromBookmarkManager"));
break;
case AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN:
base::RecordAction(base::UserMetricsAction(
- "Signin_SigninNewAccount_FromAvatarBubbleSignin"));
+ "Signin_SigninNewAccountPreDice_FromAvatarBubbleSignin"));
break;
case signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS:
- base::RecordAction(
- base::UserMetricsAction("Signin_SigninNewAccount_FromRecentTabs"));
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountPreDice_FromRecentTabs"));
break;
case AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE:
base::RecordAction(base::UserMetricsAction(
- "Signin_SigninNewAccount_FromPasswordBubble"));
+ "Signin_SigninNewAccountPreDice_FromPasswordBubble"));
break;
case signin_metrics::AccessPoint::ACCESS_POINT_TAB_SWITCHER:
- base::RecordAction(
- base::UserMetricsAction("Signin_SigninNewAccount_FromTabSwitcher"));
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountPreDice_FromTabSwitcher"));
+ break;
+ case AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountPreDice_FromNTPContentSuggestions"));
+ break;
+ 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_APPS_PAGE_LINK:
+ 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_FORCE_SIGNIN_WARNING:
+ // These access points do not support personalized sign-in promos, so
+ // |Signin_SigninNewAccountPreDice_From*| user actions should not
+ // be recorded for them. Note: To avoid bloating the sign-in APIs, the
+ // sign-in metrics simply ignore if the caller passes
+ // |PROMO_ACTION_NEW_ACCOUNT_PRE_DICE| when a sign-in flow is
+ // started from any access point instead of treating it as an error like
+ // in the other cases (|WithDefault| and |NotDefault|).
+ VLOG(1) << "Signin_SigninNewAccountPreDice_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;
+ }
+}
+
+void RecordSigninNewAccountNoExistingAccountUserActionForAccessPoint(
+ signin_metrics::AccessPoint access_point) {
+ switch (access_point) {
+ case signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountNoExistingAccount_FromSettings"));
+ break;
+ case AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE:
+ // clang-format off
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountNoExistingAccount_FromExtensionInstallBubble"));
+ // clang-format on
+ break;
+ case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountNoExistingAccount_FromBookmarkBubble"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountNoExistingAccount_FromBookmarkManager"));
+ break;
+ case AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountNoExistingAccount_FromAvatarBubbleSignin"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountNoExistingAccount_FromRecentTabs"));
+ break;
+ case AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountNoExistingAccount_FromPasswordBubble"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_TAB_SWITCHER:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountNoExistingAccount_FromTabSwitcher"));
+ break;
+ case AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS:
+ // clang-format off
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountNoExistingAccount_FromNTPContentSuggestions"));
+ // clang-format on
+ break;
+ 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_APPS_PAGE_LINK:
+ 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_FORCE_SIGNIN_WARNING:
+ // 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
+ // sign-in metrics simply ignore if the caller passes
+ // |PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT| when a sign-in flow is
+ // started from any access point instead of treating it as an error like
+ // in the other cases (|WithDefault| and |NotDefault|).
+ VLOG(1) << "Signin_SigninNewAccountNoExistingAccount_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;
+ }
+}
+
+void RecordSigninNewAccountExistingAccountUserActionForAccessPoint(
+ signin_metrics::AccessPoint access_point) {
+ switch (access_point) {
+ case signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountExistingAccount_FromSettings"));
+ break;
+ case AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountExistingAccount_FromExtensionInstallBubble"));
+ break;
+ case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountExistingAccount_FromBookmarkBubble"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountExistingAccount_FromBookmarkManager"));
+ break;
+ case AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountExistingAccount_FromAvatarBubbleSignin"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountExistingAccount_FromRecentTabs"));
+ break;
+ case AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountExistingAccount_FromPasswordBubble"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_TAB_SWITCHER:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_SigninNewAccountExistingAccount_FromTabSwitcher"));
break;
case AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS:
base::RecordAction(base::UserMetricsAction(
- "Signin_SigninNewAccount_FromNTPContentSuggestions"));
+ "Signin_SigninNewAccountExistingAccount_FromNTPContentSuggestions"));
break;
case AccessPoint::ACCESS_POINT_START_PAGE:
case AccessPoint::ACCESS_POINT_NTP_LINK:
@@ -300,13 +448,13 @@ void RecordSigninNewAccountUserActionForAccessPoint(
case AccessPoint::ACCESS_POINT_UNKNOWN:
case AccessPoint::ACCESS_POINT_FORCE_SIGNIN_WARNING:
// These access points do not support personalized sign-in promos, so
- // |Signin_SigninNewAccount_From*| user actions should not be recorded
- // for them.
- // Note: To avoid bloating the sign-in APIs, the sign-in metrics simply
- // ignore if the caller passes |PROMO_ACTION_NEW_ACCOUNT| when a the
- // sign-in flow is started from any access point instead of treating it
- // and an error like in the other cases (|WithDefault| and |NotDefault|).
- VLOG(1) << "Signin_SigninNewAccount_From* user actions"
+ // |Signin_SigninNewAccountExistingAccount_From*| user actions should not
+ // be recorded for them. Note: To avoid bloating the sign-in APIs, the
+ // sign-in metrics simply ignore if the caller passes
+ // |PROMO_ACTION_NEW_ACCOUNT_EXISTING_ACCOUNT| when a sign-in flow is
+ // started from any access point instead of treating it as an error like
+ // in the other cases (|WithDefault| and |NotDefault|).
+ VLOG(1) << "Signin_SigninNewAccountExistingAccount_From* user actions"
<< " are not recorded for access point "
<< static_cast<int>(access_point)
<< " as it does not support a personalized sign-in promo.";
@@ -385,9 +533,21 @@ void LogSigninAccessPointStarted(AccessPoint access_point,
static_cast<int>(access_point),
static_cast<int>(AccessPoint::ACCESS_POINT_MAX));
break;
- case PromoAction::PROMO_ACTION_NEW_ACCOUNT:
+ case PromoAction::PROMO_ACTION_NEW_ACCOUNT_PRE_DICE:
UMA_HISTOGRAM_ENUMERATION(
- "Signin.SigninStartedAccessPoint.NewAccount",
+ "Signin.SigninStartedAccessPoint.NewAccountPreDice",
+ static_cast<int>(access_point),
+ static_cast<int>(AccessPoint::ACCESS_POINT_MAX));
+ break;
+ case PromoAction::PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT:
+ UMA_HISTOGRAM_ENUMERATION(
+ "Signin.SigninStartedAccessPoint.NewAccountNoExistingAccount",
+ static_cast<int>(access_point),
+ static_cast<int>(AccessPoint::ACCESS_POINT_MAX));
+ break;
+ case PromoAction::PROMO_ACTION_NEW_ACCOUNT_EXISTING_ACCOUNT:
+ UMA_HISTOGRAM_ENUMERATION(
+ "Signin.SigninStartedAccessPoint.NewAccountExistingAccount",
static_cast<int>(access_point),
static_cast<int>(AccessPoint::ACCESS_POINT_MAX));
break;
@@ -414,9 +574,21 @@ void LogSigninAccessPointCompleted(AccessPoint access_point,
static_cast<int>(access_point),
static_cast<int>(AccessPoint::ACCESS_POINT_MAX));
break;
- case PromoAction::PROMO_ACTION_NEW_ACCOUNT:
+ case PromoAction::PROMO_ACTION_NEW_ACCOUNT_PRE_DICE:
+ UMA_HISTOGRAM_ENUMERATION(
+ "Signin.SigninCompletedAccessPoint.NewAccountPreDice",
+ static_cast<int>(access_point),
+ static_cast<int>(AccessPoint::ACCESS_POINT_MAX));
+ break;
+ case PromoAction::PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT:
+ UMA_HISTOGRAM_ENUMERATION(
+ "Signin.SigninCompletedAccessPoint.NewAccountNoExistingAccount",
+ static_cast<int>(access_point),
+ static_cast<int>(AccessPoint::ACCESS_POINT_MAX));
+ break;
+ case PromoAction::PROMO_ACTION_NEW_ACCOUNT_EXISTING_ACCOUNT:
UMA_HISTOGRAM_ENUMERATION(
- "Signin.SigninCompletedAccessPoint.NewAccount",
+ "Signin.SigninCompletedAccessPoint.NewAccountExistingAccount",
static_cast<int>(access_point),
static_cast<int>(AccessPoint::ACCESS_POINT_MAX));
break;
@@ -607,8 +779,16 @@ void RecordSigninUserActionForAccessPoint(AccessPoint access_point,
case PromoAction::PROMO_ACTION_NOT_DEFAULT:
RecordSigninNotDefaultUserActionForAccessPoint(access_point);
break;
- case PromoAction::PROMO_ACTION_NEW_ACCOUNT:
- RecordSigninNewAccountUserActionForAccessPoint(access_point);
+ case PromoAction::PROMO_ACTION_NEW_ACCOUNT_PRE_DICE:
+ RecordSigninNewAccountPreDiceUserActionForAccessPoint(access_point);
+ break;
+ case PromoAction::PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT:
+ RecordSigninNewAccountNoExistingAccountUserActionForAccessPoint(
+ access_point);
+ break;
+ case PromoAction::PROMO_ACTION_NEW_ACCOUNT_EXISTING_ACCOUNT:
+ RecordSigninNewAccountExistingAccountUserActionForAccessPoint(
+ access_point);
break;
}
}
diff --git a/chromium/components/signin/core/browser/signin_metrics.h b/chromium/components/signin/core/browser/signin_metrics.h
index 132b33b0428..5b0bc05dc51 100644
--- a/chromium/components/signin/core/browser/signin_metrics.h
+++ b/chromium/components/signin/core/browser/signin_metrics.h
@@ -149,12 +149,22 @@ enum class AccessPoint : int {
ACCESS_POINT_MAX, // This must be last.
};
-// Enum values which enumerates all user actions on the mobile sign-in promo.
+// Enum values which enumerates all user actions on the sign-in promo.
enum class PromoAction : int {
PROMO_ACTION_NO_SIGNIN_PROMO = 0,
+ // The user selected the default account.
PROMO_ACTION_WITH_DEFAULT,
+ // On desktop, the user selected an account that is not the default. On
+ // mobile, the user selected the generic "Use another account" button.
PROMO_ACTION_NOT_DEFAULT,
- PROMO_ACTION_NEW_ACCOUNT,
+ // Non-personalized promo, pre-dice on desktop.
+ PROMO_ACTION_NEW_ACCOUNT_PRE_DICE,
+ // Non personalized promo, when there is no account on the device.
+ PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT,
+ // The user clicked on the "Add account" button, when there are already
+ // accounts on the device. (desktop only, the button does not exist on
+ // mobile).
+ PROMO_ACTION_NEW_ACCOUNT_EXISTING_ACCOUNT
};
// Enum values which enumerates all reasons to start sign in process.
diff --git a/chromium/components/signin/core/browser/signin_metrics_unittest.cc b/chromium/components/signin/core/browser/signin_metrics_unittest.cc
index ddadafbdb76..d27ae272d72 100644
--- a/chromium/components/signin/core/browser/signin_metrics_unittest.cc
+++ b/chromium/components/signin/core/browser/signin_metrics_unittest.cc
@@ -7,7 +7,7 @@
#include <string>
#include <vector>
-#include "base/test/user_action_tester.h"
+#include "base/test/metrics/user_action_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace signin_metrics {
@@ -155,19 +155,56 @@ TEST_F(SigninMetricsTest, RecordSigninUserActionWithPromoAction) {
}
}
-TEST_F(SigninMetricsTest, RecordSigninUserActionWithNewPromoAction) {
+TEST_F(SigninMetricsTest, RecordSigninUserActionWithNewPreDicePromoAction) {
for (const AccessPoint& ap : GetAllAccessPoints()) {
base::UserActionTester user_action_tester;
RecordSigninUserActionForAccessPoint(
- ap, signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT);
+ ap, signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT_PRE_DICE);
if (AccessPointSupportsPersonalizedPromo(ap)) {
- EXPECT_EQ(
- 1, user_action_tester.GetActionCount("Signin_SigninNewAccount_From" +
- GetAccessPointDescription(ap)));
+ EXPECT_EQ(1, user_action_tester.GetActionCount(
+ "Signin_SigninNewAccountPreDice_From" +
+ GetAccessPointDescription(ap)));
} else {
- EXPECT_EQ(
- 0, user_action_tester.GetActionCount("Signin_SigninNewAccount_From" +
- GetAccessPointDescription(ap)));
+ EXPECT_EQ(0, user_action_tester.GetActionCount(
+ "Signin_SigninNewAccountPreDice_From" +
+ GetAccessPointDescription(ap)));
+ }
+ }
+}
+
+TEST_F(SigninMetricsTest, RecordSigninUserActionWithNewNoExistingPromoAction) {
+ for (const AccessPoint& ap : GetAllAccessPoints()) {
+ base::UserActionTester user_action_tester;
+ RecordSigninUserActionForAccessPoint(
+ ap, signin_metrics::PromoAction::
+ PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT);
+ if (AccessPointSupportsPersonalizedPromo(ap)) {
+ EXPECT_EQ(1, user_action_tester.GetActionCount(
+ "Signin_SigninNewAccountNoExistingAccount_From" +
+ GetAccessPointDescription(ap)));
+ } else {
+ EXPECT_EQ(0, user_action_tester.GetActionCount(
+ "Signin_SigninNewAccountNoExistingAccount_From" +
+ GetAccessPointDescription(ap)));
+ }
+ }
+}
+
+TEST_F(SigninMetricsTest,
+ RecordSigninUserActionWithNewWithExistingPromoAction) {
+ for (const AccessPoint& ap : GetAllAccessPoints()) {
+ base::UserActionTester user_action_tester;
+ RecordSigninUserActionForAccessPoint(
+ ap,
+ signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT_EXISTING_ACCOUNT);
+ if (AccessPointSupportsPersonalizedPromo(ap)) {
+ EXPECT_EQ(1, user_action_tester.GetActionCount(
+ "Signin_SigninNewAccountExistingAccount_From" +
+ GetAccessPointDescription(ap)));
+ } else {
+ EXPECT_EQ(0, user_action_tester.GetActionCount(
+ "Signin_SigninNewAccountExistingAccount_From" +
+ GetAccessPointDescription(ap)));
}
}
}
diff --git a/chromium/components/signin/core/browser/test_signin_client.cc b/chromium/components/signin/core/browser/test_signin_client.cc
index 8eb66ddba2f..76f6f6ecaee 100644
--- a/chromium/components/signin/core/browser/test_signin_client.cc
+++ b/chromium/components/signin/core/browser/test_signin_client.cc
@@ -11,10 +11,14 @@
#include "components/signin/core/browser/webdata/token_service_table.h"
#include "components/webdata/common/web_data_service_base.h"
#include "components/webdata/common/web_database_service.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
TestSigninClient::TestSigninClient(PrefService* pref_service)
- : pref_service_(pref_service),
+ : shared_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)),
+ pref_service_(pref_service),
are_signin_cookies_allowed_(true),
network_calls_delayed_(false) {}
@@ -33,7 +37,7 @@ scoped_refptr<TokenWebData> TestSigninClient::GetDatabase() {
bool TestSigninClient::CanRevokeCredentials() { return true; }
std::string TestSigninClient::GetSigninScopedDeviceId() {
- return std::string();
+ return "DeviceID";
}
void TestSigninClient::OnSignedOut() {}
@@ -48,6 +52,11 @@ net::URLRequestContextGetter* TestSigninClient::GetURLRequestContext() {
return request_context_.get();
}
+scoped_refptr<network::SharedURLLoaderFactory>
+TestSigninClient::GetURLLoaderFactory() {
+ return shared_factory_;
+}
+
void TestSigninClient::SetURLRequestContext(
net::URLRequestContextGetter* request_context) {
request_context_ = request_context;
@@ -70,10 +79,6 @@ void TestSigninClient::LoadTokenDatabase() {
database_->Init();
}
-bool TestSigninClient::ShouldMergeSigninCredentialsIntoCookieJar() {
- return true;
-}
-
std::unique_ptr<SigninClient::CookieChangeSubscription>
TestSigninClient::AddCookieChangeCallback(const GURL& url,
const std::string& name,
@@ -122,8 +127,9 @@ void TestSigninClient::DelayNetworkCall(const base::Closure& callback) {
std::unique_ptr<GaiaAuthFetcher> TestSigninClient::CreateGaiaAuthFetcher(
GaiaAuthConsumer* consumer,
const std::string& source,
- net::URLRequestContextGetter* getter) {
- return std::make_unique<GaiaAuthFetcher>(consumer, source, getter);
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+ return std::make_unique<GaiaAuthFetcher>(consumer, source,
+ url_loader_factory);
}
void TestSigninClient::PreGaiaLogout(base::OnceClosure callback) {
diff --git a/chromium/components/signin/core/browser/test_signin_client.h b/chromium/components/signin/core/browser/test_signin_client.h
index 3aeb831f664..8e487d613cd 100644
--- a/chromium/components/signin/core/browser/test_signin_client.h
+++ b/chromium/components/signin/core/browser/test_signin_client.h
@@ -17,6 +17,9 @@
#include "components/signin/core/browser/signin_client.h"
#include "net/cookies/cookie_change_dispatcher.h"
#include "net/url_request/url_request_test_util.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_url_loader_factory.h"
class PrefService;
@@ -43,7 +46,7 @@ class TestSigninClient : public SigninClient {
// Returns true.
bool CanRevokeCredentials() override;
- // Returns empty string.
+ // Returns a dummy device ID.
std::string GetSigninScopedDeviceId() override;
// Does nothing.
@@ -59,16 +62,21 @@ class TestSigninClient : public SigninClient {
// Returns the empty string.
std::string GetProductVersion() override;
- // Returns a TestURLRequestContextGetter or an manually provided
- // URLRequestContextGetter.
+ // Returns a manually provided URLRequestContextGetter.
net::URLRequestContextGetter* GetURLRequestContext() override;
- // For testing purposes, can override the TestURLRequestContextGetter created
- // in the default constructor.
+ // Tells GetURLRequestContext() what to return.
void SetURLRequestContext(net::URLRequestContextGetter* request_context);
- // Returns true.
- bool ShouldMergeSigninCredentialsIntoCookieJar() override;
+ // Wraps the test_url_loader_factory(). Note that this is totally independent
+ // of GetURLRequestContext(), so you may need to set some things differently
+ // based on what API the consumer is using, while transition away from
+ // URLRequestContextGetter is going on.
+ scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
+
+ network::TestURLLoaderFactory* test_url_loader_factory() {
+ return &test_url_loader_factory_;
+ }
// Registers |callback| and returns the subscription.
// Note that |callback| will never be called.
@@ -99,7 +107,8 @@ class TestSigninClient : public SigninClient {
std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcher(
GaiaAuthConsumer* consumer,
const std::string& source,
- net::URLRequestContextGetter* getter) override;
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+ override;
void PreGaiaLogout(base::OnceClosure callback) override;
// Loads the token database.
@@ -108,6 +117,9 @@ class TestSigninClient : public SigninClient {
private:
base::ScopedTempDir temp_dir_;
scoped_refptr<net::URLRequestContextGetter> request_context_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> shared_factory_;
+
scoped_refptr<TokenWebData> database_;
PrefService* pref_service_;
bool are_signin_cookies_allowed_;
diff --git a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h
index 79ae397f64a..1f13b5dcaab 100644
--- a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h
+++ b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h
@@ -28,7 +28,7 @@ class ProfileOAuth2TokenServiceIOSDelegate : public OAuth2TokenServiceDelegate {
OAuth2AccessTokenFetcher* CreateAccessTokenFetcher(
const std::string& account_id,
- net::URLRequestContextGetter* getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
OAuth2AccessTokenConsumer* consumer) override;
// KeyedService
diff --git a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm
index f3089c309b5..d7a52008d08 100644
--- a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm
+++ b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm
@@ -300,7 +300,7 @@ void ProfileOAuth2TokenServiceIOSDelegate::RevokeAllCredentials() {
OAuth2AccessTokenFetcher*
ProfileOAuth2TokenServiceIOSDelegate::CreateAccessTokenFetcher(
const std::string& account_id,
- net::URLRequestContextGetter* getter,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
OAuth2AccessTokenConsumer* consumer) {
AccountInfo account_info =
account_tracker_service_->GetAccountInfo(account_id);
diff --git a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate_unittest.mm b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate_unittest.mm
index 9131b9bb76b..f36924cdf47 100644
--- a/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate_unittest.mm
+++ b/chromium/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate_unittest.mm
@@ -261,7 +261,8 @@ TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, StartRequestSuccess) {
scopes.push_back("scope");
std::unique_ptr<OAuth2AccessTokenFetcher> fetcher1(
oauth2_delegate_->CreateAccessTokenFetcher(
- GetAccountId(account1), oauth2_delegate_->GetRequestContext(), this));
+ GetAccountId(account1), oauth2_delegate_->GetURLLoaderFactory(),
+ this));
fetcher1->Start("foo", "bar", scopes);
EXPECT_EQ(0, access_token_success_);
EXPECT_EQ(0, access_token_failure_);
@@ -284,7 +285,8 @@ TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, StartRequestFailure) {
scopes.push_back("scope");
std::unique_ptr<OAuth2AccessTokenFetcher> fetcher1(
oauth2_delegate_->CreateAccessTokenFetcher(
- GetAccountId(account1), oauth2_delegate_->GetRequestContext(), this));
+ GetAccountId(account1), oauth2_delegate_->GetURLLoaderFactory(),
+ this));
fetcher1->Start("foo", "bar", scopes);
EXPECT_EQ(0, access_token_success_);
EXPECT_EQ(0, access_token_failure_);
diff --git a/chromium/components/spellcheck/browser/BUILD.gn b/chromium/components/spellcheck/browser/BUILD.gn
index e984ced24a4..830575ead8a 100644
--- a/chromium/components/spellcheck/browser/BUILD.gn
+++ b/chromium/components/spellcheck/browser/BUILD.gn
@@ -37,6 +37,7 @@ source_set("browser") {
"//content/public/common",
"//google_apis",
"//net",
+ "//services/network/public/cpp",
]
if (is_android) {
@@ -60,6 +61,8 @@ source_set("unit_tests") {
"//content/test:test_support",
"//mojo/public/cpp/bindings",
"//net:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//testing/gtest",
]
}
diff --git a/chromium/components/spellcheck/browser/DEPS b/chromium/components/spellcheck/browser/DEPS
index f55de2481af..ddf4771ff84 100644
--- a/chromium/components/spellcheck/browser/DEPS
+++ b/chromium/components/spellcheck/browser/DEPS
@@ -10,4 +10,6 @@ include_rules = [
"+jni",
"+mojo/public/cpp/bindings",
"+net",
+ "+services/network/public/cpp",
+ "+services/network/test",
]
diff --git a/chromium/components/spellcheck/browser/spellcheck_host_metrics_unittest.cc b/chromium/components/spellcheck/browser/spellcheck_host_metrics_unittest.cc
index 64338f6a948..b0d15537eb4 100644
--- a/chromium/components/spellcheck/browser/spellcheck_host_metrics_unittest.cc
+++ b/chromium/components/spellcheck/browser/spellcheck_host_metrics_unittest.cc
@@ -12,7 +12,7 @@
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram_samples.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/components/spellcheck/browser/spellchecker_session_bridge_android.cc b/chromium/components/spellcheck/browser/spellchecker_session_bridge_android.cc
index 1c173bd5301..0aa8b720ed4 100644
--- a/chromium/components/spellcheck/browser/spellchecker_session_bridge_android.cc
+++ b/chromium/components/spellcheck/browser/spellchecker_session_bridge_android.cc
@@ -38,6 +38,12 @@ void SpellCheckerSessionBridge::RequestTextCheck(
const base::string16& text,
RequestTextCheckCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ // This allows us to discard |callback| safely in case it's not run due to
+ // failures in initialization of |java_object_|.
+ std::unique_ptr<SpellingRequest> incoming_request =
+ std::make_unique<SpellingRequest>(text, std::move(callback));
+
// SpellCheckerSessionBridge#create() will return null if spell checker
// service is unavailable.
if (java_object_initialization_failed_) {
@@ -71,11 +77,11 @@ void SpellCheckerSessionBridge::RequestTextCheck(
// If multiple requests arrive during one active request, only the most
// recent request will run (the others get overwritten).
if (active_request_) {
- pending_request_.reset(new SpellingRequest(text, std::move(callback)));
+ pending_request_ = std::move(incoming_request);
return;
}
- active_request_.reset(new SpellingRequest(text, std::move(callback)));
+ active_request_ = std::move(incoming_request);
JNIEnv* env = base::android::AttachCurrentThread();
Java_SpellCheckerSessionBridge_requestTextCheck(
diff --git a/chromium/components/spellcheck/browser/spelling_service_client.cc b/chromium/components/spellcheck/browser/spelling_service_client.cc
index b86eb56de37..f151fedb876 100644
--- a/chromium/components/spellcheck/browser/spelling_service_client.cc
+++ b/chromium/components/spellcheck/browser/spelling_service_client.cc
@@ -28,7 +28,9 @@
#include "content/public/browser/storage_partition.h"
#include "google_apis/google_api_keys.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/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
#include "url/gurl.h"
namespace {
@@ -48,9 +50,9 @@ const char* const kValidLanguages[] = {"en", "es", "fi", "da"};
} // namespace
-SpellingServiceClient::SpellingServiceClient() {}
+SpellingServiceClient::SpellingServiceClient() = default;
-SpellingServiceClient::~SpellingServiceClient() {}
+SpellingServiceClient::~SpellingServiceClient() = default;
bool SpellingServiceClient::RequestTextCheck(
content::BrowserContext* context,
@@ -62,7 +64,6 @@ bool SpellingServiceClient::RequestTextCheck(
std::move(callback).Run(false, text, std::vector<SpellCheckResult>());
return false;
}
-
const PrefService* pref = user_prefs::UserPrefs::Get(context);
DCHECK(pref);
@@ -138,19 +139,32 @@ bool SpellingServiceClient::RequestTextCheck(
}
})");
- net::URLFetcher* fetcher =
- CreateURLFetcher(url, traffic_annotation).release();
- data_use_measurement::DataUseUserData::AttachToFetcher(
- fetcher, data_use_measurement::DataUseUserData::SPELL_CHECKER);
- fetcher->SetRequestContext(
- content::BrowserContext::GetDefaultStoragePartition(context)
- ->GetURLRequestContext());
- fetcher->SetUploadData("application/json", request);
- fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES);
- spellcheck_fetchers_[fetcher] = std::make_unique<TextCheckCallbackData>(
- base::WrapUnique(fetcher), std::move(callback), text);
- fetcher->Start();
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = url;
+ resource_request->load_flags =
+ net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
+ resource_request->method = "POST";
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader =
+ network::SimpleURLLoader::Create(std::move(resource_request),
+ traffic_annotation);
+ simple_url_loader->AttachStringForUpload(request, "application/json");
+ auto it = spellcheck_loaders_.insert(
+ spellcheck_loaders_.begin(),
+ std::make_unique<TextCheckCallbackData>(std::move(simple_url_loader),
+ std::move(callback), text));
+ network::SimpleURLLoader* loader = it->get()->simple_url_loader.get();
+ auto url_loader_factory =
+ url_loader_factory_for_testing_
+ ? url_loader_factory_for_testing_
+ : content::BrowserContext::GetDefaultStoragePartition(context)
+ ->GetURLLoaderFactoryForBrowserProcess();
+ // TODO(https://crbug.com/808498): Re-add data use measurement once
+ // SimpleURLLoader supports it.
+ // ID=data_use_measurement::DataUseUserData::SPELL_CHECKER
+ loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory.get(),
+ base::BindOnce(&SpellingServiceClient::OnSimpleLoaderComplete,
+ base::Unretained(this), std::move(it)));
return true;
}
@@ -279,35 +293,30 @@ bool SpellingServiceClient::ParseResponse(
}
SpellingServiceClient::TextCheckCallbackData::TextCheckCallbackData(
- std::unique_ptr<net::URLFetcher> fetcher,
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader,
TextCheckCompleteCallback callback,
base::string16 text)
- : fetcher(std::move(fetcher)), callback(std::move(callback)), text(text) {}
+ : simple_url_loader(std::move(simple_url_loader)),
+ callback(std::move(callback)),
+ text(text) {}
SpellingServiceClient::TextCheckCallbackData::~TextCheckCallbackData() {}
-void SpellingServiceClient::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK(base::ContainsKey(spellcheck_fetchers_, source));
- std::unique_ptr<TextCheckCallbackData> callback_data =
- std::move(spellcheck_fetchers_[source]);
- spellcheck_fetchers_.erase(source);
-
+void SpellingServiceClient::OnSimpleLoaderComplete(
+ SpellCheckLoaderList::iterator it,
+ std::unique_ptr<std::string> response_body) {
+ TextCheckCompleteCallback callback = std::move(it->get()->callback);
+ base::string16 text = it->get()->text;
bool success = false;
std::vector<SpellCheckResult> results;
- if (source->GetResponseCode() / 100 == 2) {
- std::string data;
- source->GetResponseAsString(&data);
- success = ParseResponse(data, &results);
- }
-
- // The callback may release the last (transitive) dependency on |this|. It
- // MUST be the last function called.
- std::move(callback_data->callback).Run(success, callback_data->text, results);
+ if (response_body)
+ success = ParseResponse(*response_body, &results);
+ spellcheck_loaders_.erase(it);
+ std::move(callback).Run(success, text, results);
}
-std::unique_ptr<net::URLFetcher> SpellingServiceClient::CreateURLFetcher(
- const GURL& url,
- net::NetworkTrafficAnnotationTag traffic_annotation) {
- return net::URLFetcher::Create(url, net::URLFetcher::POST, this,
- traffic_annotation);
+void SpellingServiceClient::SetURLLoaderFactoryForTesting(
+ scoped_refptr<network::SharedURLLoaderFactory>
+ url_loader_factory_for_testing) {
+ url_loader_factory_for_testing_ = std::move(url_loader_factory_for_testing);
}
diff --git a/chromium/components/spellcheck/browser/spelling_service_client.h b/chromium/components/spellcheck/browser/spelling_service_client.h
index 1093b48b8f9..effe700682c 100644
--- a/chromium/components/spellcheck/browser/spelling_service_client.h
+++ b/chromium/components/spellcheck/browser/spelling_service_client.h
@@ -5,7 +5,7 @@
#ifndef COMPONENTS_SPELLCHECK_BROWSER_SPELLING_SERVICE_CLIENT_H_
#define COMPONENTS_SPELLCHECK_BROWSER_SPELLING_SERVICE_CLIENT_H_
-#include <map>
+#include <list>
#include <memory>
#include <string>
#include <vector>
@@ -14,18 +14,17 @@
#include "base/compiler_specific.h"
#include "base/strings/string16.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher_delegate.h"
-class GURL;
struct SpellCheckResult;
namespace content {
class BrowserContext;
}
-namespace net {
-class URLFetcher;
-} // namespace net
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+} // namespace network
// A class that encapsulates a JSON-RPC call to the Spelling service to check
// text there. This class creates a JSON-RPC request, sends the request to the
@@ -57,7 +56,7 @@ class URLFetcher;
// std::unique_ptr<SpellingServiceClient> client_;
// };
//
-class SpellingServiceClient : public net::URLFetcherDelegate {
+class SpellingServiceClient {
public:
// Service types provided by the Spelling service. The Spelling service
// consists of a couple of backends:
@@ -76,7 +75,7 @@ class SpellingServiceClient : public net::URLFetcherDelegate {
TextCheckCompleteCallback;
SpellingServiceClient();
- ~SpellingServiceClient() override;
+ ~SpellingServiceClient();
// Sends a text-check request to the Spelling service. When we send a request
// to the Spelling service successfully, this function returns true. (This
@@ -90,6 +89,11 @@ class SpellingServiceClient : public net::URLFetcherDelegate {
// Returns whether the specified service is available for the given context.
static bool IsAvailable(content::BrowserContext* context, ServiceType type);
+ // Set the URL loader factory for tests.
+ void SetURLLoaderFactoryForTesting(
+ scoped_refptr<network::SharedURLLoaderFactory>
+ url_loader_factory_for_testing);
+
protected:
// Parses a JSON-RPC response from the Spelling service.
bool ParseResponse(const std::string& data,
@@ -98,13 +102,14 @@ class SpellingServiceClient : public net::URLFetcherDelegate {
private:
struct TextCheckCallbackData {
public:
- TextCheckCallbackData(std::unique_ptr<net::URLFetcher> fetcher,
- TextCheckCompleteCallback callback,
- base::string16 text);
+ TextCheckCallbackData(
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader,
+ TextCheckCompleteCallback callback,
+ base::string16 text);
~TextCheckCallbackData();
- // The fetcher used.
- std::unique_ptr<net::URLFetcher> fetcher;
+ // The URL loader used.
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader;
// The callback function to be called when we receive a response from the
// Spelling service and parse it.
@@ -117,19 +122,18 @@ class SpellingServiceClient : public net::URLFetcherDelegate {
DISALLOW_COPY_AND_ASSIGN(TextCheckCallbackData);
};
- // net::URLFetcherDelegate implementation.
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ using SpellCheckLoaderList =
+ std::list<std::unique_ptr<TextCheckCallbackData>>;
+
+ void OnSimpleLoaderComplete(SpellCheckLoaderList::iterator it,
+ std::unique_ptr<std::string> response_body);
- // Creates a URLFetcher object used for sending a JSON-RPC request. This
- // function is overridden by unit tests to prevent them from actually sending
- // requests to the Spelling service.
- virtual std::unique_ptr<net::URLFetcher> CreateURLFetcher(
- const GURL& url,
- net::NetworkTrafficAnnotationTag traffic_annotation);
+ // List of loaders in use.
+ SpellCheckLoaderList spellcheck_loaders_;
- // The URLFetcher object used for sending a JSON-RPC request.
- std::map<const net::URLFetcher*, std::unique_ptr<TextCheckCallbackData>>
- spellcheck_fetchers_;
+ // URL loader factory to use for fake network requests during testing.
+ scoped_refptr<network::SharedURLLoaderFactory>
+ url_loader_factory_for_testing_;
};
#endif // COMPONENTS_SPELLCHECK_BROWSER_SPELLING_SERVICE_CLIENT_H_
diff --git a/chromium/components/ssl_errors/BUILD.gn b/chromium/components/ssl_errors/BUILD.gn
index eda7b32f94d..4a635e2ec5e 100644
--- a/chromium/components/ssl_errors/BUILD.gn
+++ b/chromium/components/ssl_errors/BUILD.gn
@@ -34,6 +34,8 @@ source_set("unit_tests") {
"//components/network_time:network_time_test_support",
"//components/prefs:test_support",
"//net:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp:cpp",
"//testing/gtest",
]
}
diff --git a/chromium/components/ssl_errors/DEPS b/chromium/components/ssl_errors/DEPS
index 9960302a20b..9f17bba77ae 100644
--- a/chromium/components/ssl_errors/DEPS
+++ b/chromium/components/ssl_errors/DEPS
@@ -5,5 +5,7 @@ include_rules = [
"+components/strings/grit/components_strings.h",
"+components/url_formatter",
"+net",
+ "+services/network/public/cpp",
+ "+services/network/test",
"+ui/base"
]
diff --git a/chromium/components/ssl_errors/error_classification_unittest.cc b/chromium/components/ssl_errors/error_classification_unittest.cc
index f73cd636827..be7b8ad09c0 100644
--- a/chromium/components/ssl_errors/error_classification_unittest.cc
+++ b/chromium/components/ssl_errors/error_classification_unittest.cc
@@ -9,7 +9,8 @@
#include "base/files/file_path.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_split.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_clock.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -26,7 +27,9 @@
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/test_certificate_data.h"
#include "net/test/test_data_directory.h"
-#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -53,6 +56,9 @@ class SSLErrorClassificationTest : public ::testing::Test {
return field_trial_test_.get();
}
+ protected:
+ network::TestURLLoaderFactory test_url_loader_factory_;
+
private:
std::unique_ptr<network_time::FieldTrialTest> field_trial_test_;
};
@@ -214,7 +220,7 @@ TEST_F(SSLErrorClassificationTest, TestPrivateURL) {
EXPECT_TRUE(ssl_errors::IsHostnameNonUniqueOrDotless("foo.blah"));
}
-TEST(ErrorClassification, LevenshteinDistance) {
+TEST_F(SSLErrorClassificationTest, LevenshteinDistance) {
EXPECT_EQ(0u, ssl_errors::GetLevenshteinDistance("banana", "banana"));
EXPECT_EQ(2u, ssl_errors::GetLevenshteinDistance("ab", "ba"));
@@ -252,8 +258,8 @@ TEST_F(SSLErrorClassificationTest, GetClockState) {
network_time::NetworkTimeTracker network_time_tracker(
std::make_unique<base::DefaultClock>(),
std::make_unique<base::DefaultTickClock>(), &pref_service,
- new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get()));
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_));
ssl_errors::SetBuildTimeForTesting(base::Time::Now());
EXPECT_EQ(
@@ -359,10 +365,11 @@ TEST_F(SSLErrorClassificationTest, GetClockState) {
// Tests that all possible NetworkClockState histogram values are recorded
// appropriately.
TEST_F(SSLErrorClassificationTest, NetworkClockStateHistogram) {
- base::Thread io_thread("IO thread");
- base::Thread::Options thread_options;
- thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
- EXPECT_TRUE(io_thread.StartWithOptions(thread_options));
+ base::test::ScopedTaskEnvironment task_environment(
+ base::test::ScopedTaskEnvironment::MainThreadType::IO);
+
+ scoped_refptr<network::TestSharedURLLoaderFactory> shared_url_loader_factory =
+ base::MakeRefCounted<network::TestSharedURLLoaderFactory>();
net::EmbeddedTestServer test_server;
ASSERT_TRUE(test_server.InitializeAndListen());
@@ -377,12 +384,10 @@ TEST_F(SSLErrorClassificationTest, NetworkClockStateHistogram) {
clock->Advance(base::TimeDelta::FromDays(111));
tick_clock->Advance(base::TimeDelta::FromDays(222));
- base::MessageLoop loop;
network_time::NetworkTimeTracker network_time_tracker(
std::unique_ptr<base::Clock>(clock),
std::unique_ptr<const base::TickClock>(tick_clock), &pref_service,
- new net::TestURLRequestContextGetter(io_thread.task_runner()));
- network_time_tracker.SetTimeServerURLForTesting(test_server.GetURL("/"));
+ shared_url_loader_factory);
field_trial_test()->SetNetworkQueriesWithVariationsService(
true, 0.0,
network_time::NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND);
@@ -475,6 +480,4 @@ TEST_F(SSLErrorClassificationTest, NetworkClockStateHistogram) {
histograms.ExpectBucketCount(
kNetworkTimeHistogram, ssl_errors::NETWORK_CLOCK_STATE_UNKNOWN_SYNC_LOST,
1);
-
- io_thread.Stop();
}
diff --git a/chromium/components/startup_metric_utils/browser/startup_metric_utils.cc b/chromium/components/startup_metric_utils/browser/startup_metric_utils.cc
index 64b435671ca..cc7d005431a 100644
--- a/chromium/components/startup_metric_utils/browser/startup_metric_utils.cc
+++ b/chromium/components/startup_metric_utils/browser/startup_metric_utils.cc
@@ -17,12 +17,12 @@
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/process/process_info.h"
-#include "base/profiler/stack_sampling_profiler.h"
#include "base/strings/string_number_conversions.h"
#include "base/sys_info.h"
#include "base/threading/platform_thread.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
+#include "components/metrics/call_stack_profile_builder.h"
#include "components/metrics/call_stack_profile_metrics_provider.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
@@ -47,8 +47,6 @@ base::TimeTicks g_process_creation_ticks;
base::TimeTicks g_browser_main_entry_point_ticks;
-base::TimeTicks g_browser_main_entry_point_ticks_computed_from_time;
-
base::TimeTicks g_renderer_main_entry_point_ticks;
base::TimeTicks g_browser_exe_main_entry_point_ticks;
@@ -433,22 +431,21 @@ base::TimeTicks StartupTimeToTimeTicks(base::Time time) {
}
void RecordRendererMainEntryHistogram() {
- if (!g_browser_main_entry_point_ticks_computed_from_time.is_null() &&
+ if (!g_browser_main_entry_point_ticks.is_null() &&
!g_renderer_main_entry_point_ticks.is_null()) {
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES_100, "Startup.BrowserMainToRendererMain",
- g_browser_main_entry_point_ticks_computed_from_time,
- g_renderer_main_entry_point_ticks);
+ g_browser_main_entry_point_ticks, g_renderer_main_entry_point_ticks);
}
}
void AddStartupEventsForTelemetry()
{
- DCHECK(!g_browser_main_entry_point_ticks_computed_from_time.is_null());
+ DCHECK(!g_browser_main_entry_point_ticks.is_null());
- TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
- "startup", "Startup.BrowserMainEntryPoint", 0,
- g_browser_main_entry_point_ticks_computed_from_time);
+ TRACE_EVENT_INSTANT_WITH_TIMESTAMP0("startup",
+ "Startup.BrowserMainEntryPoint", 0,
+ g_browser_main_entry_point_ticks);
}
// Logs the Startup.TimeSinceLastStartup histogram. Obtains the timestamp of the
@@ -551,11 +548,6 @@ void RecordMainEntryPointTime(base::Time wall_time, base::TimeTicks ticks) {
DCHECK(g_browser_main_entry_point_ticks.is_null());
g_browser_main_entry_point_ticks = ticks;
DCHECK(!g_browser_main_entry_point_ticks.is_null());
-
- DCHECK(g_browser_main_entry_point_ticks_computed_from_time.is_null());
- g_browser_main_entry_point_ticks_computed_from_time =
- StartupTimeToTimeTicks(wall_time);
- DCHECK(!g_browser_main_entry_point_ticks_computed_from_time.is_null());
}
void RecordExeMainEntryPointTicks(base::TimeTicks ticks) {
@@ -584,7 +576,7 @@ void RecordBrowserMainMessageLoopStart(base::TimeTicks ticks,
RecordHardFaultHistogram();
// Record timing of the browser message-loop start time.
- base::StackSamplingProfiler::SetProcessMilestone(
+ metrics::CallStackProfileBuilder::SetProcessMilestone(
metrics::CallStackProfileMetricsProvider::MAIN_LOOP_START);
if (!is_first_run && !g_process_creation_ticks.is_null()) {
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
@@ -598,14 +590,8 @@ void RecordBrowserMainMessageLoopStart(base::TimeTicks ticks,
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE(
UMA_HISTOGRAM_LONG_TIMES,
"Startup.BrowserMessageLoopStartTimeFromMainEntry.FirstRun2",
- g_browser_main_entry_point_ticks_computed_from_time, ticks);
+ g_browser_main_entry_point_ticks, ticks);
} else {
- // TODO(pasko): Stop recording the "...MainEntry2" histogram after M65 hits
- // Stable.
- UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
- UMA_HISTOGRAM_LONG_TIMES,
- "Startup.BrowserMessageLoopStartTimeFromMainEntry2",
- g_browser_main_entry_point_ticks_computed_from_time, ticks);
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES,
"Startup.BrowserMessageLoopStartTimeFromMainEntry3",
@@ -629,15 +615,13 @@ void RecordBrowserMainMessageLoopStart(base::TimeTicks ticks,
// chrome.exe:main() to chrome.dll:main().
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES, "Startup.LoadTime.ExeMainToDllMain2",
- g_browser_exe_main_entry_point_ticks,
- g_browser_main_entry_point_ticks_computed_from_time);
+ g_browser_exe_main_entry_point_ticks, g_browser_main_entry_point_ticks);
// Process create to chrome.dll:main(). Reported as a histogram only as
// the other two events above are sufficient for tracing purposes.
UMA_HISTOGRAM_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES, "Startup.LoadTime.ProcessCreateToDllMain2",
- g_browser_main_entry_point_ticks_computed_from_time -
- g_process_creation_ticks);
+ g_browser_main_entry_point_ticks - g_process_creation_ticks);
}
}
@@ -699,7 +683,7 @@ void RecordFirstWebContentsNonEmptyPaint(
if (!ShouldLogStartupHistogram())
return;
- base::StackSamplingProfiler::SetProcessMilestone(
+ metrics::CallStackProfileBuilder::SetProcessMilestone(
metrics::CallStackProfileMetricsProvider::FIRST_NONEMPTY_PAINT);
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES_100, "Startup.FirstWebContents.NonEmptyPaint2",
@@ -724,7 +708,7 @@ void RecordFirstWebContentsMainNavigationStart(base::TimeTicks ticks,
if (!ShouldLogStartupHistogram())
return;
- base::StackSamplingProfiler::SetProcessMilestone(
+ metrics::CallStackProfileBuilder::SetProcessMilestone(
metrics::CallStackProfileMetricsProvider::MAIN_NAVIGATION_START);
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES_100,
@@ -755,7 +739,7 @@ void RecordFirstWebContentsMainNavigationFinished(base::TimeTicks ticks) {
if (!ShouldLogStartupHistogram())
return;
- base::StackSamplingProfiler::SetProcessMilestone(
+ metrics::CallStackProfileBuilder::SetProcessMilestone(
metrics::CallStackProfileMetricsProvider::MAIN_NAVIGATION_FINISHED);
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES_100,
@@ -792,7 +776,7 @@ void RecordBrowserWindowFirstPaintCompositingEnded(
}
base::TimeTicks MainEntryPointTicks() {
- return g_browser_main_entry_point_ticks_computed_from_time;
+ return g_browser_main_entry_point_ticks;
}
} // namespace startup_metric_utils
diff --git a/chromium/components/storage_monitor/image_capture_device.mm b/chromium/components/storage_monitor/image_capture_device.mm
index af289bd66eb..831cb65ac97 100644
--- a/chromium/components/storage_monitor/image_capture_device.mm
+++ b/chromium/components/storage_monitor/image_capture_device.mm
@@ -231,4 +231,38 @@ base::FilePath PathForCameraItem(ICCameraItem* item) {
name));
}
+// MacOS 10.14 SDK methods, not yet implemented (https://crbug.com/849689)
+- (void)cameraDevice:(ICCameraDevice*)camera
+ didRenameItems:(NSArray<ICCameraItem*>*)items {
+ NOTIMPLEMENTED();
+}
+
+- (void)cameraDevice:(ICCameraDevice*)camera didRemoveItem:(ICCameraItem*)item {
+ NOTIMPLEMENTED();
+}
+
+- (void)cameraDevice:(ICCameraDevice*)camera
+ didCompleteDeleteFilesWithError:(NSError*)error {
+ NOTIMPLEMENTED();
+}
+
+- (void)cameraDeviceDidChangeCapability:(ICCameraDevice*)camera {
+ NOTIMPLEMENTED();
+}
+
+- (void)cameraDevice:(ICCameraDevice*)camera
+ didReceiveThumbnailForItem:(ICCameraItem*)item {
+ NOTIMPLEMENTED();
+}
+
+- (void)cameraDevice:(ICCameraDevice*)camera
+ didReceiveMetadataForItem:(ICCameraItem*)item {
+ NOTIMPLEMENTED();
+}
+
+- (void)cameraDevice:(ICCameraDevice*)camera
+ didReceivePTPEvent:(NSData*)eventData {
+ NOTIMPLEMENTED();
+}
+
@end // ImageCaptureDevice
diff --git a/chromium/components/storage_monitor/test_media_transfer_protocol_manager_chromeos.cc b/chromium/components/storage_monitor/test_media_transfer_protocol_manager_chromeos.cc
index 823b4852f53..bd425bed05a 100644
--- a/chromium/components/storage_monitor/test_media_transfer_protocol_manager_chromeos.cc
+++ b/chromium/components/storage_monitor/test_media_transfer_protocol_manager_chromeos.cc
@@ -90,8 +90,6 @@ void TestMediaTransferProtocolManagerChromeOS::ReadFileChunk(
void TestMediaTransferProtocolManagerChromeOS::GetFileInfo(
const std::string& storage_handle,
const std::vector<uint32_t>& file_ids,
- uint32_t offset,
- uint32_t entries_to_read,
GetFileInfoCallback callback) {
std::move(callback).Run(std::vector<device::mojom::MtpFileEntryPtr>(), true);
}
diff --git a/chromium/components/storage_monitor/test_media_transfer_protocol_manager_chromeos.h b/chromium/components/storage_monitor/test_media_transfer_protocol_manager_chromeos.h
index 5097b074871..cb72dbfda6f 100644
--- a/chromium/components/storage_monitor/test_media_transfer_protocol_manager_chromeos.h
+++ b/chromium/components/storage_monitor/test_media_transfer_protocol_manager_chromeos.h
@@ -55,8 +55,6 @@ class TestMediaTransferProtocolManagerChromeOS
ReadFileChunkCallback callback) override;
void GetFileInfo(const std::string& storage_handle,
const std::vector<uint32_t>& file_ids,
- uint32_t offset,
- uint32_t entries_to_read,
GetFileInfoCallback callback) override;
void RenameObject(const std::string& storage_handle,
uint32_t object_id,
diff --git a/chromium/components/strings/components_strings_am.xtb b/chromium/components/strings/components_strings_am.xtb
index 674f9e7e129..51843856268 100644
--- a/chromium/components/strings/components_strings_am.xtb
+++ b/chromium/components/strings/components_strings_am.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">እሴት ይደብቁ</translation>
<translation id="1228893227497259893">የተሳሳተ የምንነት ለዪ</translation>
<translation id="1232569758102978740">ርዕስ አልባ</translation>
+<translation id="1250759482327835220">በሚቀጥለው ጊዜ በበለጠ ፍጥነት ለመክፈል ካርድዎን እና የማስከፈያ አድራሻዎን በGoogle መለያዎ ላይ ያስቀምጡ።</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;
@@ -97,7 +98,6 @@
&lt;p&gt;እባክዎ በ&lt;strong&gt;ቅንብሮች&lt;/strong&gt; መተግበሪያው የ&lt;strong&gt;አጠቃላይ&lt;/strong&gt; ክፍል ላይ ቀን እና ሰዓቱን ያስተካክሉ።&lt;/p&gt;</translation>
<translation id="1583429793053364125">ይህን ድረ-ገጽ በማሳየት ላይ ሳለ የሆነ ችግር ተፈጥሯል።</translation>
-<translation id="1590457302292452960">አንድ ጠንካራ የይለፍ ቃል ያመንጩ...</translation>
<translation id="1592005682883173041">አካባቢያዊ የውሂብ መድረሻ</translation>
<translation id="1594030484168838125">ምረጥ</translation>
<translation id="1620510694547887537">ካሜራ</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">የሥርዓት አስተዳዳሪውን ለማነጋገር ይሞክሩ።</translation>
<translation id="1740951997222943430">ትክክለኛ የአገልግሎት ማብቂያ ወር ያስገቡ</translation>
+<translation id="1743520634839655729">በሚቀጥለው ጊዜ በበለጠ ፍጥነት ለመክፈል ካርድዎን እና የማስከፈያ አድራሻዎን በGoogle መለያዎ እና በዚህ መሣሪያ ላይ ያስቀምጡ።</translation>
<translation id="17513872634828108">ትሮችን ክፈት</translation>
<translation id="1753706481035618306">የገጽ ቁጥር</translation>
<translation id="1763864636252898013">ይህ አገልጋይ <ph name="DOMAIN" /> መሆኑን ሊያረጋግጥ አልቻለም፤ የደህንነት እውቅና ማረጋገጫው በመሣሪያዎ ስርዓተ ክወና የሚታመን አይደለም። ይሄ በተሳሳተ አወቃቀር ወይም አንድ አጥቂ ግንኙነትዎን በመጥለፉ የተከሰተ ሊሆን ይችላል።</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">የእርስዎ ክፍት ትሮች እዚህ ይመጣሉ</translation>
<translation id="1791429645902722292">Google ዘመናዊ ቁልፍ</translation>
<translation id="1803264062614276815">የካርድ ያዢው ስም</translation>
-<translation id="1806541873155184440"><ph name="ADDED_TO_AUTOFILL_MONTH" /> ላይ ታክሏል</translation>
<translation id="1821930232296380041">ልክ ያልሆነ ጥያቄ ወይም የጥያቄ ልኬቶች</translation>
<translation id="1826516787628120939">በመፈተሸ ላይ</translation>
<translation id="1834321415901700177">ይህ ጣቢያ ጎጂ ፕሮግራሞችን ይዟል</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 የአስተያየት ጥቆማ}one{# የአስተያየት ጥቆማዎች}other{# የአስተያየት ጥቆማዎች}}</translation>
<translation id="2079545284768500474">ቀልብስ</translation>
<translation id="20817612488360358">የስርዓት ተኪ ቅንብሮች ስራ ላይ እንዲውሉ ተቀናብረዋል ግን ግልጽ የሆነ የተኪ ውቅርም ተገልጿል።</translation>
-<translation id="2084558088529668945">የእርስዎን የይለፍ ቃል በ<ph name="ORG_NAME" /> በማይተዳደር ጣቢያ ላይ አስገብተዋል። የእርስዎን መለያ ለመጠበቅ ሲባል የእርስዎን የይለፍ ቃል በሌሎች መተግበሪያዎች እና ጣቢያዎች ላይ አይጠቀሙበት።</translation>
<translation id="2091887806945687916">ድምፅ</translation>
<translation id="2094505752054353250">የጎራ አለመዛመድ</translation>
<translation id="2096368010154057602">ክፍል</translation>
+<translation id="2102134110707549001">ጠንካራ የይለፍ ቃል ጠቁም...</translation>
<translation id="2108755909498034140">ኮምፒውተርዎን ዳግም ያስጀምሩት</translation>
<translation id="2113977810652731515">ካርታ</translation>
<translation id="2114841414352855701">በ<ph name="POLICY_NAME" /> ስለተሻረ ችላ ተብሏል።</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">የኤች ቲ ቲ ፒ ስህተት</translation>
<translation id="2270484714375784793">ስልክ ቁጥር</translation>
<translation id="2292556288342944218">የእርስዎ የበየነመረብ መዳረሻ ታግዷል</translation>
-<translation id="230155334948463882">አዲስ ካርድ?</translation>
<translation id="2316887270356262533">እስከ 1 ሜባ ቦታ ድረስ ያስለቅቃል። አንዳንድ ጣቢያዎች በሚቀጥለው ጉብኝትዎ ላይ ይበልጥ በዝግታ ሊጫኑ ይችላሉ።</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> የተጠቃሚ ስም እና የይለፍ ቃል ያስፈልገዋል።</translation>
<translation id="2317583587496011522">ዴቢት ካርዶች ተቀባይነት አላቸው።</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">ተቀባይነት ያላቸው ካርዶች</translation>
<translation id="2702801445560668637">የንባብ ዝርዝር</translation>
<translation id="2704283930420550640">ዋጋ ከቅርጸት ጋር አይዛመድም።</translation>
-<translation id="2704951214193499422">Chromium በዚህ ጊዜ የእርስዎን ካርድ ማረጋገጥ አልቻለም። እባክዎ ቆይተው እንደገና ይሞክሩ።</translation>
<translation id="2705137772291741111">የተቀመጠው (የተሸጎጠ) የዚህ ጣቢያ ቅጂ የሚነበብ አልነበረም።</translation>
<translation id="2709516037105925701">ራስ-ሙላ</translation>
<translation id="2710942282213947212">በእርስዎ ኮምፒውተር ላይ ያለ ሶፍትዌር Chromium ደህንነቱ በተጠበቀ ሁኔታ ከድር ጋር እንዳይገናኝ እያስቆመው ነው</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">የመላኪያ ዘዴ</translation>
<translation id="277499241957683684">የሚጎድል የመሣሪያ መዝገብ</translation>
<translation id="2781030394888168909">MacOSን ወደ ውጭ ይላኩ</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">ግንኙነቱ ዳግም እንዲጀምር ተደርጓል።</translation>
<translation id="2788784517760473862">ተቀባይነት ያላቸው ክሬዲት ካርዶች</translation>
<translation id="2794233252405721443">ጣቢያ ታግዷል</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">ከቅንብሮች ገጽ ሆነው ማናቸውንም ለግንኙነት የተዋቀሩ ተኪዎችን ማሰናከል ይችላሉ።</translation>
<translation id="2955913368246107853">አግኝ አሞሌን ዝጋ</translation>
<translation id="2958431318199492670">የአውታረ መረብ ውቅሩ በኦ ኤን ሲ መስፈርቱ አይገዛም። አንዳንድ የውቅሩ ክፍሎች ላይመጡ ይችላሉ።</translation>
-<translation id="2966678944701946121">አገልግሎቱ የሚያበቃው፦ <ph name="EXPIRATION_DATE_ABBR" />፣ የታከለው በ<ph name="ADDED_TO_AUTOFILL_MONTH" /> ላይ</translation>
<translation id="2969319727213777354">ደህንነቱ የተጠበቀ ግንኙነት ለመመስረት የእርስዎ ሰዓት በትክክል መዋቀር አለበት። ይሄ የሆነበት ምክንያት ድር ጣቢያዎች ራሳቸውን ለማሳወቅ የሚጠቀሙባቸው የእውቅና ማረጋገጫዎች የሚሰሩት ለተወሰኑ ጊዜዎች ብቻ ስለሆነ ነው። የእርስዎ መሣሪያ ሰዓት ትክክል እንዳለመሆኑ መጠን Google Chrome እነዚህን የእውቅና ማረጋገጫዎች ሊያረጋግጥ አይችልም።</translation>
<translation id="2972581237482394796">&amp;ድገም</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />፣ አሁን ላይ ተመርጧል። <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">የተሳሳተ የመምሪያ አይነት</translation>
<translation id="3037605927509011580">ውይ፣ ተሰናከለ!</translation>
<translation id="3041612393474885105">የሰርቲፊኬት መረጃ</translation>
-<translation id="3063697135517575841">Chrome በዚህ ጊዜ የእርስዎን ካርድ ማረጋገጥ አልቻለም። እባክዎ ቆይተው እንደገና ይሞክሩ።</translation>
<translation id="3064966200440839136">በውጫዊ ማከማቻ በኩል ለማጫወት ማንነት ከማያሳውቅ ሁነታ በመውጣት ላይ። ይቀጥል?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{ምንም}=1{1 የይለፍ ቃል}one{# የይለፍ ቃሎች}other{# የይለፍ ቃሎች}}</translation>
<translation id="3096100844101284527">የመውሰጃ አድራሻ ያክሉ</translation>
@@ -340,7 +337,6 @@
<translation id="3305707030755673451">የእርስዎ ውሂብ <ph name="TIME" /> ላይ በእርስዎ የስምረት የይለፍ ቃል ተመስጥሯል። ስምረትን ለመጀመር ያስገቡት።</translation>
<translation id="3320021301628644560">የመክፈያ አድራሻ አክል</translation>
<translation id="3338095232262050444">ደህንነቱ የተጠበቀ ነው</translation>
-<translation id="3340978935015468852">ቅንብሮች</translation>
<translation id="3345135638360864351">ይህን ጣቢያ ለመድረስ ያቀረቡት ጥያቄ ወደ <ph name="NAME" /> ሊላክ አልተቻለም። እባክዎ እንደገና ይሞክሩ።</translation>
<translation id="3355823806454867987">የተኪ ቅንብሮችን በመቀየር ላይ...</translation>
<translation id="3361596688432910856">Chrome የሚከተሉትን መረጃዎች <ph name="BEGIN_EMPHASIS" />አያስቀምጥም<ph name="END_EMPHASIS" />፦
@@ -374,7 +370,6 @@
<translation id="3528171143076753409">የአገልጋይ እውቅና ማረጋገጫ የታመነ አይደለም።</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{በተሰመሩ መሣሪያዎች ላይ ቢያንስ 1 ንጥል}=1{1 ንጥል (እና ተጨማሪ የተሰመሩ መሣሪያዎች ላይ)}one{# ንጥሎች (እና ተጨማሪ የተሰመሩ መሣሪያዎች ላይ)}other{# ንጥሎች (እና ተጨማሪ የተሰመሩ መሣሪያዎች ላይ)}}</translation>
<translation id="3539171420378717834">የዚህን ካርድ ቅጂ በዚህ መሣሪያ ላይ አቆይ</translation>
-<translation id="3542684924769048008">የይለፍ ቃል ይጠቀሙ ለ፦</translation>
<translation id="3549644494707163724">ሁሉም የተመሳሰለ ውሂብ ከእራስዎ የተመሳሰለ ይለፍ ሐረግ ጋር ያመስጥሩ</translation>
<translation id="3556433843310711081">የእርስዎ አስተዳዳሪ እገዳውን ሊያነሱልዎ ይችላሉ</translation>
<translation id="3566021033012934673">ግንኙነትዎ የግል አይደለም</translation>
@@ -460,7 +455,6 @@
<translation id="4171400957073367226">መጥፎ የማረጋገጫ ፊርማ</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> ተጨማሪ ንጥል}one{<ph name="ITEM_COUNT" /> ተጨማሪ ንጥሎች}other{<ph name="ITEM_COUNT" /> ተጨማሪ ንጥሎች}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome የእርስዎን የ<ph name="ORG_NAME" /> የይለፍ ቃል በሌሎች ጣቢያዎች ላይ ዳግመኛ ከተጠቀሙበት እንደገና እንዲያዋቅሩት ይመክራል።</translation>
<translation id="4196861286325780578">&amp;ውሰድን ድገም</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />የኬላ እና የጸረ-ቫይረስ ውቅረቶችን መፈተሽ<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">ብልሽቶች</translation>
@@ -489,7 +483,6 @@
<translation id="425582637250725228">ያደረጓቸው ለውጦች ላይቀመጡ ይችላሉ።</translation>
<translation id="4258748452823770588">መጥፎ ፊርማ</translation>
<translation id="4265872034478892965">በእርስዎ አስተዳዳሪ የተፈቀደ</translation>
-<translation id="4269787794583293679">(ምንም የተጠቃሚ ስም የለም)</translation>
<translation id="4275830172053184480">መሣሪያዎን ዳግም ያስጀምሩ</translation>
<translation id="4277028893293644418">የይለፍ ቃል ዳግም አቀናብር</translation>
<translation id="4280429058323657511">፣ አገልግሎቱ የሚያበቃው በ<ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -512,6 +505,7 @@
<translation id="4434045419905280838">ብቅ-ባዮች እና አቅጣጫ ማዞሮች</translation>
<translation id="443673843213245140">የተኪ መጠቀም ተሰናክሏል ግን ግልጽ የሆነ የተኪ ውቅር ተገልጿል።</translation>
<translation id="445100540951337728">ተቀባይነት ያላቸው ዴቢት ካርዶች</translation>
+<translation id="4472575034687746823">ይጀምሩ</translation>
<translation id="4506176782989081258">የማረጋገጥ ስህተት፦ <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">የሥርዓት አስተዳዳሪውን ማነጋገር</translation>
<translation id="450710068430902550">ከአስተዳዳሪ ጋር ማጋራት</translation>
@@ -536,8 +530,8 @@
<translation id="4726672564094551039">መምሪያዎችን ዳግም ጫን</translation>
<translation id="4728558894243024398">የመሣሪያ ስርዓት</translation>
<translation id="4736825316280949806">Chromiumን ዳግም ያስጀምሩት</translation>
+<translation id="4742407542027196863">የይለፍ ቃላትን ያስተዳድሩ...</translation>
<translation id="4744603770635761495">የሚፈጸም ዱካ</translation>
-<translation id="4749685221585524849">መጨረሻ ላይ ጥቅም ላይ የዋለው በ<ph name="LAST_USED_MONTH" /> ላይ</translation>
<translation id="4750917950439032686">የእርስዎ መረጃ (ለምሳሌ፦ የይለፍ ቃሎች ወይም የክሬዲት ካርድ ቁጥሮች) ወደዚህ ጣቢያ በሚላክበት ጊዜ የግል ነው።</translation>
<translation id="4756388243121344051">&amp;ታሪክ</translation>
<translation id="4758311279753947758">የእውቂያ መረጃን ያክሉ</translation>
@@ -554,6 +548,7 @@
<translation id="4850886885716139402">አሳይ</translation>
<translation id="4854362297993841467">የማድረሻ ዘዴው አይገኝም። የተለየ ዘዴ ይሞክሩ።</translation>
<translation id="4858792381671956233">ይህን ገጽ መጎብኘት ችግር ካለው ወላጆችዎንጠይቀዋል</translation>
+<translation id="4876305945144899064">ምንም የተጠቃሚ ስም የለም</translation>
<translation id="4880827082731008257">የፍለጋ ታሪክ</translation>
<translation id="4881695831933465202">ክፈት</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />፣ <ph name="TYPE_2" />፣, <ph name="TYPE_3" /></translation>
@@ -587,6 +582,7 @@
<translation id="5089810972385038852">ግዛት</translation>
<translation id="5094747076828555589">ይህ አገልጋይ <ph name="DOMAIN" /> መሆኑን ሊያረጋግጥ አልቻለም፤ የደህንነት እውቅና ማረጋገጫው በChromium የሚታመን አይደለም። ይሄ በተሳሳተ አወቃቀር ወይም አንድ አጥቂ ግንኙነትዎን በመጥለፉ የተከሰተ ሊሆን ይችላል።</translation>
<translation id="5095208057601539847">ጠቅላይ ግዛት</translation>
+<translation id="5098332213681597508">ይህ ስም የመጣው ከእርስዎ የGoogle መለያ ነው።</translation>
<translation id="5115563688576182185">(64-ቢት)</translation>
<translation id="5121084798328133320">ካረጋገጡ በኋላ የGoogle Payments መለያዎ ካርድ ዝርዝሮች ለዚህ ጣቢያ ይጋራሉ።</translation>
<translation id="5128122789703661928">ይህ ስም ያለው ክፍለ-ጊዜ ለስረዛ ልክ ያልሆነ ነው።</translation>
@@ -627,9 +623,10 @@
<translation id="5332219387342487447">የመላኪያ መንገድ</translation>
<translation id="5355557959165512791">የዕውቅና ማረጋገጫው ስለተሻረ <ph name="SITE" />ን መጎብኘት አይችሉም። የአውታረ መረብ ስህተቶች እና ጥቃቶች አብዛኛው ጊዜ ጊዜያዊ ብቻ ናቸው፣ ስለዚህ ይህ ገጽ በኋላ ላይ ሊሠራ ይችላል።</translation>
<translation id="536296301121032821">የመምሪያ ቅንብሮችን ማከማቸት አልተሳካም</translation>
+<translation id="5371425731340848620">ካርድ ያዘምኑ</translation>
<translation id="5377026284221673050">«የእርስዎ ሰዓት ቀርቷል» ወይም «የእርስዎ ሰዓት ቀድሟል» ወይም «&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;»</translation>
<translation id="5386426401304769735">የዚህ ጣቢያ የዕውቅና ማረጋገጫ ሰንሰለቱ SHA-1 በመጠቀም የተፈረመ የዕውቅና ማረጋገጫን ያካትታል።</translation>
-<translation id="5402410679244714488">አገልግሎቱ የሚያበቃው፦ <ph name="EXPIRATION_DATE_ABBR" />፣ ለመጨረሻ ጊዜ ጥቅም ላይ የዋለው ከአንድ ዓመት በፊት</translation>
+<translation id="5387961145478138773">ወደ የእርስዎ ተወዳጅ የGoogle መተግበሪያዎች ፈጣን መዳረሻ ያግኙ</translation>
<translation id="540969355065856584">ይህ አገልጋይ <ph name="DOMAIN" /> መሆኑን ሊያረጋግጥ አልቻለም፤ የደህንነት እውቅና ማረጋገጫው በዚህ ጊዜ ላይ የሚሰራ አይደለም። ይሄ በተሳሳተ አወቃቀር ወይም አንድ አጥቂ ግንኙነትዎን በመጥለፉ የተከሰተ ሊሆን ይችላል።</translation>
<translation id="5421136146218899937">የአሰሳ ውሂብ አጽዳ…</translation>
<translation id="5430298929874300616">ዕልባት አስወግድ</translation>
@@ -671,11 +668,13 @@
<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="5659593005791499971">ኢሜይል</translation>
+<translation id="5666899935841546222">ሁሉም ካርዶችዎ በአንድ ቦታ እንዲሆንልዎ ይፈልጋሉ?</translation>
<translation id="5675650730144413517">ይህ ገጽ እየሠራ አይደለም</translation>
<translation id="5685654322157854305">የመላኪያ አድራሻ ያክሉ</translation>
<translation id="5689199277474810259">ወደ JSON ላክ</translation>
<translation id="5689516760719285838">አካባቢ</translation>
<translation id="570530837424789914">ያቀናብሩ...</translation>
+<translation id="57094364128775171">ጠንካራ የይለፍ ቃል ጠቁም...</translation>
<translation id="5710435578057952990">የዚህ ድረ-ገጽ ማንነት አልተረጋገጠም።</translation>
<translation id="5719499550583120431">የቅድመ-ክፍያ ካርዶች ተቀባይነት አላቸው።</translation>
<translation id="5720705177508910913">የአሁኑ ተጠቃሚ</translation>
@@ -696,11 +695,9 @@
<translation id="5869405914158311789">ይህ ጣቢያ ሊደረስበት አይችልም</translation>
<translation id="5869522115854928033">የተቀመጡ የይለፍ ቃሎች</translation>
<translation id="5893752035575986141">ክሬዲት ካርዶች ተቀባይነት አላቸው።</translation>
-<translation id="5898382028489516745">Chromium የእርስዎን የ<ph name="ORG_NAME" /> የይለፍ ቃል በሌሎች ጣቢያዎች ላይ ዳግመኛ ከተጠቀሙበት እንደገና እንዲያዋቅሩት ይመክራል።</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (ሰምሯል)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 ጥቅም ላይ ያለ}one{# ጥቅም ላይ ያለ}other{# ጥቅም ላይ}}</translation>
<translation id="5939518447894949180">ዳግም አስጀምር</translation>
-<translation id="5959728338436674663">አደገኛ መተግበሪያዎችን እና ጣቢያዎችን ማግኘት እንዲያግዝ አንዳንድ <ph name="BEGIN_WHITEPAPER_LINK" />የሥርዓት መረጃ እና የገጽ ይዘት<ph name="END_WHITEPAPER_LINK" />ን በራስ-ሰር ወደ Google ይላኩ። <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">የዕውቂያ መረጃን ያርትዑ</translation>
<translation id="5967867314010545767">ከታሪክ አስወግድ</translation>
<translation id="5975083100439434680">አሳንስ</translation>
@@ -746,13 +743,11 @@
<translation id="6282194474023008486">የፖስታ ኮድ</translation>
<translation id="6290238015253830360">የእርስዎ የተጠቆሙ ዘገባዎች እዚህ ይመጣሉ</translation>
<translation id="6305205051461490394"><ph name="URL" /> ሊደረስበት አይችልም።</translation>
-<translation id="6319915415804115995">ለመጨረሻ ጊዜ ጥቅም ላይ የዋለው ከአንድ ዓመት በፊት</translation>
<translation id="6321917430147971392">የዲኤንኤስ ቅንብሮችዎን ይፈትሹ</translation>
<translation id="6328639280570009161">የአውታረ መረብ መገመትን አሰናክለው ይሞክሩ</translation>
<translation id="6328786501058569169">ይህ ጣቢያ አታላይ ነው</translation>
<translation id="6337133576188860026">ከ<ph name="SIZE" /> ያነሰ ቦታ ያስለቅቃል። አንዳንድ ጣቢያዎች በሚቀጥለው ጉብኝትዎ ላይ ይበልጥ በዝግታ ሊጫኑ ይችላሉ።</translation>
<translation id="6337534724793800597">መምሪያዎችን በስም አጣራ</translation>
-<translation id="6342069812937806050">ልክ አሁን</translation>
<translation id="6355080345576803305">የይፋዊ ክፍለ-ጊዜ መሻር</translation>
<translation id="6358450015545214790">እነዚህ ምን ማለት ናቸው?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 ሌላ የአስተያየት ጥቆማ}one{# ሌሎች የአስተያየት ጥቆማዎች}other{# ሌሎች የአስተያየት ጥቆማዎች}}</translation>
@@ -777,7 +772,6 @@
<translation id="6529602333819889595">&amp;ሰርዝን ድገም</translation>
<translation id="6534179046333460208">የአካላዊ ድር ጥቆማዎች</translation>
<translation id="6550675742724504774">አማራጮች</translation>
-<translation id="6556915248009097796">አገልግሎቱ የሚያበቃው፦ <ph name="EXPIRATION_DATE_ABBR" />፣ ለመጨረሻ ጊዜ ጥቅም ላይ የዋለው በ<ph name="LAST_USED_DATE_NO_DETAIL" /> ላይ</translation>
<translation id="6563469144985748109">የእርስዎ አስተዳዳሪ ገና አላጸደቁትም</translation>
<translation id="6569060085658103619">የቅጥያ ገጽ እየተመለከቱ ነው</translation>
<translation id="6596325263575161958">የምስጠራ አማራጮች</translation>
@@ -806,15 +800,20 @@
<translation id="6825578344716086703">እርስዎ <ph name="DOMAIN" />ን ለመድረስ ሞክረዋል፣ ነገር ግን አገልጋዩ ደካማ የፊርማ ስልተ-ቀመር (እንደ SHA-1 ያለ) በመጠቀም የተፈረመ የዕውቅና ማረጋገጫ ነው ያቀረበው። ይህ ማለት አገልጋዩ ያቀረበው የደህንነት ምስክርነቶች የተጭበረበሩ ሊሆኑ ይችላሉ፣ እናም አገልጋዩ እርስዎ የሚጠብቁት አገልጋይ ላይሆን ይችላል (ከአጥቂ ጋር እየተገናኙ ሊሆን ይችላል)።</translation>
<translation id="6831043979455480757">መተርጎም</translation>
<translation id="6839929833149231406">አካባቢ</translation>
+<translation id="6852204201400771460">መተግበሪያ ዳግም ይጫን?</translation>
+<translation id="6865412394715372076">ይህ ካርድ አሁን ላይ ሊረጋገጥ አይችልም</translation>
<translation id="6874604403660855544">&amp;አክልን ድገም</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">የመመሪያ ደረጃ አይደገፍም።</translation>
<translation id="6895330447102777224">የእርስዎ ካርድ ተረጋግጧል</translation>
<translation id="6897140037006041989">የተጠቀሚ ተወካይ</translation>
+<translation id="6903319715792422884">አንዳንድ <ph name="BEGIN_WHITEPAPER_LINK" />የሥርዓት መረጃ እና የገጽ ይዘት<ph name="END_WHITEPAPER_LINK" />ን ወደ Google በመላክ የጥንቃቄ አሰሳን ለማሻሻል እንዲቻል ያግዙ።<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">ተጠቃሚ፦</translation>
+<translation id="6944692733090228304">በ<ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> ወደ የማይተዳደር ጣቢያ ላይ የእርስዎን የይለፍ ቃል አስገብተዋል። ለእርስዎ መለያ ጥበቃ ለማድረግ፣ በሌሎች መተግበሪያዎች እና ጣቢያዎች ላይ የእርስዎን የይለፍ ቃል ዳግም አይጠቀሙ።</translation>
<translation id="6945221475159498467">ይምረጡ</translation>
<translation id="6948701128805548767">የመውሰጃ ዘዴዎችን እና መስፈርቶችን ለመመልከት አድራሻ ይምረጡ</translation>
<translation id="6949872517221025916">የይለፍ ቃል ዳግም ያስገቡ</translation>
+<translation id="6950684638814147129">የJSON እሴቱን በመተንተን ላይ ሳለ ስህተት፦ <ph name="ERROR" /></translation>
<translation id="6957887021205513506">የአገልጋዩ እውቅና ማረጋገጫ የተጭበረበረ ይመስላል።</translation>
<translation id="6965382102122355670">እሺ</translation>
<translation id="6965978654500191972">መሣሪያ</translation>
@@ -876,6 +875,7 @@
&lt;li&gt;&lt;strong&gt;ተግብር&lt;/strong&gt;ን ጠቅ ያድርጉ፣ ከዚያ &lt;strong&gt;እሺ&lt;/strong&gt;ን ጠቅ ያድርጉ
&lt;li&gt;እንዴት ሶፍትዌሩን ከኮምፒውተርዎ እስከመጨረሻው ማስወገድ እንደሚችሉ ለማወቅ &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;የChrome እገዛ ማዕከል&lt;/a&gt;ን ይጎብኙ
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">የይለፍ ቃላትን ያስተዳድሩ...</translation>
<translation id="7419106976560586862">የመገለጫ ዱካ</translation>
<translation id="7437289804838430631">የእውቂያ መረጃ አክል</translation>
<translation id="7441627299479586546">የተሳሳተ የመምሪያ ርዕሰ ጉዳይ</translation>
@@ -884,7 +884,6 @@
<translation id="7451311239929941790">ስለዚህ ችግር <ph name="BEGIN_LINK" />ይበልጥ በመረዳት ላይ<ph name="END_LINK" />።</translation>
<translation id="7455133967321480974">ሁለንተናዊ ነባሪውን ተጠቀም (አግድ)</translation>
<translation id="7460163899615895653">ከሌሎች መሣሪያዎች የመጡ የቅርብ ጊዜ ትሮችዎ እዚህ ይመጣሉ</translation>
-<translation id="7469372306589899959">ካርድን በማረጋገጥ ላይ</translation>
<translation id="7473891865547856676">አይ፣ አመሰግናለሁ</translation>
<translation id="7481312909269577407">ወደ ፊት</translation>
<translation id="7485870689360869515">ምንም ውሂብ አልተገኘም።</translation>
@@ -1014,6 +1013,7 @@
<translation id="8308427013383895095">በአውታረመረብ ግንኙነት ችግር ምክንያት የትርጉም ስራው ተሰናክሏል።</translation>
<translation id="8311129316111205805">ክፍለ-ጊዜን ጫን</translation>
<translation id="8332188693563227489">የ<ph name="HOST_NAME" /> መዳረሻ ተከልክሏል</translation>
+<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="8349305172487531364">የዕልባቶች አሞሌ</translation>
<translation id="8363502534493474904">የአውሮፕላን ሁነታን ማጥፋት</translation>
@@ -1034,21 +1034,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ምላሽ ለመስጠት ከልክ በላይ ረዥም ጊዜ ወስዷል።</translation>
<translation id="8503559462189395349">የChrome ይለፍ ቃላት</translation>
<translation id="8503813439785031346">የተጣቃሚ ስም</translation>
+<translation id="8508648098325802031">የፍለጋ አዶ</translation>
<translation id="8543181531796978784"><ph name="BEGIN_ERROR_LINK" />የፈልጎ ማግኘት ችግርን ሪፖርት ማድረግ<ph name="END_ERROR_LINK" />፣ ወይም ደግሞ በእርስዎ ደህንነት ላይ ሊያስከትል የሚችለውን አደጋ ከተረዱ <ph name="BEGIN_LINK" />ይህን ደህንነቱ ያልተጠበቀ ጣቢያ መጎብኘት<ph name="END_LINK" /> ይችላሉ።</translation>
<translation id="8543556556237226809">ጥያቄዎች አለዎት? የእርስዎን መገለጫ የሚቆጣጠረውን ግለሰብ ያነጋግሩ።</translation>
<translation id="8553075262323480129">የገጹ ቋንቋ ሊታወቅ ስላልቻለ ትርጉሙ አልተሳካም።</translation>
<translation id="8557066899867184262">CVC ከካርድዎ በስተጀርባ ላይ ይገኛል።</translation>
<translation id="8559762987265718583">የእርስዎ መሣሪያ ቀን (<ph name="DATE_AND_TIME" />) ልክ ስላልሆነ ወደ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> የግል ግንኙነት መመስረት አይቻልም።</translation>
+<translation id="8564985650692024650">Chromium የእርስዎን የ<ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> ይለፍ ቃል በሌሎች ጣቢያዎች ላይ ዳግም ከተጠቀሙበት እንደገና እንዲያዋቅሩት ይመክራል።</translation>
<translation id="8571890674111243710">ገጽ ወደ <ph name="LANGUAGE" /> በመተርጎም ላይ...</translation>
<translation id="858637041960032120">ስልክ ቁጥር ያክሉ
</translation>
<translation id="859285277496340001">የእውቅና ማረጋገጫው ተሽሮ እንደሆነ የሚታይበት ምንም ስልት አይገልጽም።</translation>
+<translation id="860043288473659153">የካርድ ያዢ ስም</translation>
<translation id="8620436878122366504">የእርስዎ ወላጆች ገና አላጸደቁትም</translation>
<translation id="8625384913736129811">ይህን ካርድ ወደዚህ መሣሪያ አስቀምጥ</translation>
-<translation id="8639963783467694461">የራስ-ሙላ ቅንብሮች</translation>
+<translation id="8663226718884576429">የትዕዛዝ ማጠቃለያ፣ <ph name="TOTAL_LABEL" />፣ ተጨማሪ ዝርዝሮች</translation>
<translation id="8680536109547170164"><ph name="QUERY" />፣ መልስ፣ <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">ከ<ph name="DOMAIN" /> ጋር ያለዎት ግንኙነት አልተመሰጠረም</translation>
<translation id="8718314106902482036">ክፍያ አልተጠናቀቀም</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />፣ <ph name="DESCRIPTION" />፣ የፍለጋ ጥቆማ ሐሳብ</translation>
<translation id="8725066075913043281">እንደገና ይሞክሩ</translation>
<translation id="8728672262656704056">ማንነት የማያሳውቅ ሁነታ ውስጥ ገብተዋል</translation>
<translation id="8730621377337864115">ተከናውኗል</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">ዕልባቶች</translation>
<translation id="883848425547221593">ሌላ እልባቶች</translation>
<translation id="884264119367021077">የመላኪያ አድራሻ</translation>
+<translation id="8846319957959474018">መተግበሪያዎችን በቀላሉ በዕልባቶች ይክፈቱ</translation>
<translation id="884923133447025588">ምንም የመሻሪያ ዘዴ አልተገኘም።</translation>
<translation id="885730110891505394">ከGoogle ጋር ማጋራት</translation>
+<translation id="8858065207712248076">Chrome የእርስዎን የ<ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> ይለፍ ቃል በሌሎች ጣቢያዎች ላይ ዳግም ከተጠቀሙበት እንደገና እንዲያዋቅሩት ይመክራል።</translation>
<translation id="8866481888320382733">የመምሪያ ቅንብሮችን መተንተን ላይ ስህተት</translation>
<translation id="8870413625673593573">በቅርብ ጊዜ የተዘጉ</translation>
<translation id="8874824191258364635">የሚሰራ የካርድ ቁጥር ያስገቡ</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> የእርስዎን መረጃ ለመጠበቅ በመደበኝነት ምስጠራን ይጠቀማል። Chromium አሁን ከ<ph name="SITE" /> ጋር ለመገናኘት ሲሞክር ድር ጣቢያው ያልተለመዱ እና ትክክል ያልሆኑ ምስክርነቶችን መልሷል። ይህ አንድ አጥቂ <ph name="SITE" />ን አስመስሎ ለመቅረብ ሲሞክር ነው ወይም አንድ የWi-Fi መግቢያ ገጽ ግንኙነቱን ሲቋረጥ ሊከሰት ይችላል። Chromium ማንኛውም የውሂብ ልውውጥ ከመካሄዱ በፊት ግንኙነቱን ስላቋረጠው አሁንም የእርስዎ መረጃ ደህንነት የተጠበቀ ነው።</translation>
<translation id="9106062320799175032">የመክፈያ አድራሻ ያክሉ</translation>
<translation id="910908805481542201">ይህን እንዳስተካክለው አግዘኝ</translation>
+<translation id="9114524666733003316">ካርድን በማረጋገጥ ላይ...</translation>
<translation id="9128870381267983090">ከአውታረ መረብ ጋር ይገናኙ</translation>
<translation id="9137013805542155359">የመጀመሪያውን አሳይ</translation>
<translation id="9137248913990643158">ይህን መተግበሪያ ከመጠቀምዎ በፊት እባክዎ ይጀምሩና ወደ Chrome ይግቡ።</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">የተቀመጡ የመክፈያ ዘዴዎች ካልዎት ጣቢያዎች እንዲፈትሹ ይፍቀዱ</translation>
<translation id="9169664750068251925">ሁልጊዜ በዚህ ጣቢያ ላይ አግድ</translation>
<translation id="9170848237812810038">&amp;ቀልብስ</translation>
+<translation id="9171296965991013597">መተግበሪያ ይተው?</translation>
<translation id="917450738466192189">የአገልጋይ እውቅና ማረጋገጫ ልክ ያልኾነ ነው።</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> የማይጠቀም ፕሮቶኮል ይጠቀማል።</translation>
<translation id="9205078245616868884">የእርስዎ ውሂብ በእርስዎ የስምረት የይለፍ ቃል ተመስጥሯል። ስምረትን ለመጀመር ያስገቡት።</translation>
diff --git a/chromium/components/strings/components_strings_ar.xtb b/chromium/components/strings/components_strings_ar.xtb
index 0da3f3c71d5..159c15e28e5 100644
--- a/chromium/components/strings/components_strings_ar.xtb
+++ b/chromium/components/strings/components_strings_ar.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">إخفاء القيمة</translation>
<translation id="1228893227497259893">معرف الكيان خاطئ</translation>
<translation id="1232569758102978740">بلا عنوان</translation>
+<translation id="1250759482327835220">‏للدفع بشكلٍ أسرع في المرة القادمة، يمكنك حفظ البطاقة والاسم وعنوان إرسال الفواتير في حسابك على Google.</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;
@@ -97,7 +98,6 @@
&lt;p&gt;يُرجى تعديل التاريخ والوقت من القسم &lt;strong&gt;عام&lt;/strong&gt; في تطبيق &lt;strong&gt;الإعدادات&lt;/strong&gt; .&lt;/p&gt;</translation>
<translation id="1583429793053364125">حدث خطأ في شيء ما أثناء عرض صفحة الويب هذه.</translation>
-<translation id="1590457302292452960">إنشاء كلمة مرور قوية...</translation>
<translation id="1592005682883173041">الوصول إلى البيانات المحلية</translation>
<translation id="1594030484168838125">اختيار</translation>
<translation id="1620510694547887537">الكاميرا</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">جرّب الاتصال بمشرف النظام.</translation>
<translation id="1740951997222943430">أدخِل شهر انتهاء صلاحية صحيح</translation>
+<translation id="1743520634839655729">‏للدفع بشكلٍ أسرع في المرة القادمة، يمكنك حفظ البطاقة والاسم وعنوان إرسال الفواتير في حسابك على Google وفي هذا الجهاز.</translation>
<translation id="17513872634828108">علامات التبويب المفتوحة</translation>
<translation id="1753706481035618306">رقم الصفحة</translation>
<translation id="1763864636252898013">هذا الخادم لم يتمكن من إثبات أن ذلك <ph name="DOMAIN" />؛ بل إنه شهادة أمان غير موثوقة من خلال نظام تشغيل جهازك. وربما يكون السبب في ذلك خطأ في التكوين أو مهاجمًا يعترض الاتصال.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">تظهر علامات التبويب المفتوحة هنا</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">اسم حامل البطاقة</translation>
-<translation id="1806541873155184440">تمت الإضافة في <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">طلب غير صالح، أو معلمات طلب غير صالحة</translation>
<translation id="1826516787628120939">حساب شيكات</translation>
<translation id="1834321415901700177">يحتوي هذا الموقع على برامج ضارة</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{اقتراح واحد}zero{# اقتراح}two{اقتراحان (#)}few{# اقتراحات}many{# اقتراحًا}other{# اقتراح}}</translation>
<translation id="2079545284768500474">تراجع</translation>
<translation id="20817612488360358">تم تعيين إعدادات الخادم الوكيل ليتم استخدامها وتم أيضًا تحديد تهيئة صريحة للخادم الوكيل.</translation>
-<translation id="2084558088529668945">لقد أدخلت كلمة المرور في موقع ويب لا تديره <ph name="ORG_NAME" />. يجب عدم إعادة استخدام كلمة المرور في تطبيقات ومواقع ويب أخرى لحماية حسابك.</translation>
<translation id="2091887806945687916">الصوت</translation>
<translation id="2094505752054353250">النطاق غير متطابق</translation>
<translation id="2096368010154057602">الإدارة</translation>
+<translation id="2102134110707549001">اقتراح كلمة مرور قوية…</translation>
<translation id="2108755909498034140">إعادة تشغيل جهاز الكمبيوتر</translation>
<translation id="2113977810652731515">البطاقة</translation>
<translation id="2114841414352855701">تم تجاهلها نظرًا لتجاوزها بواسطة <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">‏خطأ HTTP</translation>
<translation id="2270484714375784793">رقم الهاتف</translation>
<translation id="2292556288342944218">تم حظر دخولك إلى الإنترنت</translation>
-<translation id="230155334948463882">بطاقة جديدة؟</translation>
<translation id="2316887270356262533">يوفِّر أقل من 1 ميغابايت. وقد يتم تحميل بعض المواقع بشكل أبطأ عند زيارتها في المرة القادمة.</translation>
<translation id="2317259163369394535">يتطلَّب <ph name="DOMAIN" /> اسم مستخدم وكلمة مرور.</translation>
<translation id="2317583587496011522">يتم قبول بطاقات السحب الآلي.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">البطاقات المقبولة</translation>
<translation id="2702801445560668637">قائمة القراءة</translation>
<translation id="2704283930420550640">القيمة لا تطابق التنسيق.</translation>
-<translation id="2704951214193499422">‏لم يتمكن Chromium من التأكد من بطاقتك في الوقت الحالي. يُرجى إعادة المحاولة في وقت لاحق.</translation>
<translation id="2705137772291741111">تعذر قراءة النسخة المحفوظة (المخزنة في ذاكرة التخزين المؤقت) لموقع الويب هذا.</translation>
<translation id="2709516037105925701">الملء التلقائي</translation>
<translation id="2710942282213947212">‏تعمل البرامج على جهاز الكمبيوتر على منع اتصال Chromium بالويب بشكل آمن</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">طريقة الشحن</translation>
<translation id="277499241957683684">سجِلّ الجهاز مفقود</translation>
<translation id="2781030394888168909">‏تصدير نظام التشغيل MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">تمت إعادة تعيين الاتصال.</translation>
<translation id="2788784517760473862">بطاقات الائتمان المقبولة</translation>
<translation id="2794233252405721443">تم حظر الموقع</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">يمكنك إيقاف أي خوادم وكيلة تمت تهيئتها لاتصال من صفحة الإعدادات.</translation>
<translation id="2955913368246107853">إغلاق شريط البحث</translation>
<translation id="2958431318199492670">‏لا تتوافق تهيئة الشبكة مع معيار ONC. قد لا يتم استيراد بعض أجزاء التهيئة.</translation>
-<translation id="2966678944701946121">تاريخ انتهاء الصلاحية: <ph name="EXPIRATION_DATE_ABBR" />، وتمت الإضافة في <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">‏لإنشاء اتصال آمن، فإنك بحاجة إلى ضبط ساعتك بشكل صحيح. وذلك لأن الشهادات التي تستخدمها مواقع الويب لتعريف نفسها تكون صالحة فقط لفترات محددة من الوقت. فإذا كانت ساعة جهازك غير صحيحة، فلن يتمكن Google Chrome من التحقق من هذه الشهادات.</translation>
<translation id="2972581237482394796">إعا&amp;دة</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />، تم تحديده حاليًا. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">نوع السياسة غير صحيح</translation>
<translation id="3037605927509011580">عذرًا!</translation>
<translation id="3041612393474885105">معلومات الشهادة</translation>
-<translation id="3063697135517575841">‏لم يتمكن Chrome من التأكد من بطاقتك في الوقت الحالي. يُرجى إعادة المحاولة في وقت لاحق.</translation>
<translation id="3064966200440839136">ستتم مغادرة وضع التصفح المتخفي للدفع عبر تطبيق خارجي. هل تريد المتابعة؟</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{بدون}=1{كلمة مرور واحدة}two{كلمتا مرور (#)}few{# كلمات مرور}many{# كلمة مرور}other{# كلمة مرور}}</translation>
<translation id="3096100844101284527">إضافة عنوان الاستلام من المستخدم</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">تم تشفير بياناتك باستخدام عبارة مرور المزامنة في <ph name="TIME" />. أدخلها لبدء المزامنة.</translation>
<translation id="3320021301628644560">إضافة عنوان إرسال الفواتير</translation>
<translation id="3338095232262050444">آمن</translation>
-<translation id="3340978935015468852">الإعدادات</translation>
<translation id="3345135638360864351">تعذر إرسال طلبك للوصول إلى هذا الموقع إلى <ph name="NAME" />. يُرجى إعادة المحاولة مرة أخرى.</translation>
<translation id="3355823806454867987">تغيير إعدادات الخادم الوكيل...</translation>
<translation id="3361596688432910856">‏لن يحفظ <ph name="BEGIN_EMPHASIS" />Chrome<ph name="END_EMPHASIS" /> المعلومات التالية:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">شهادة الخادم غير موثوق فيها.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{عنصر واحد على الأقل على الأجهزة المتزامنة}=1{عنصر واحد (1) (وأكثر على الأجهزة المتزامنة)}two{عنصران (#) (وأكثر على الأجهزة المتزامنة)}few{# عناصر (وأكثر على الأجهزة المتزامنة)}many{# عنصرًا (وأكثر على الأجهزة المتزامنة)}other{# عنصر (وأكثر على الأجهزة المتزامنة)}}</translation>
<translation id="3539171420378717834">الاحتفاظ بنسخة من هذه البطاقة على هذا الجهاز</translation>
-<translation id="3542684924769048008">استخدام كلمة مرور لـ:</translation>
<translation id="3549644494707163724">تشفير جميع البيانات المتزامنة باستخدام عبارة مرور المزامنة</translation>
<translation id="3556433843310711081">يمكن لمديرك إلغاء الحظر لك</translation>
<translation id="3566021033012934673">اتصالك ليس خاصًا</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">توقيع تحقق سيئ</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{عنصر (<ph name="ITEM_COUNT" />) آخر}zero{<ph name="ITEM_COUNT" /> عنصر آخر}two{عنصران (<ph name="ITEM_COUNT" />) آخران}few{<ph name="ITEM_COUNT" /> عناصر أخرى}many{<ph name="ITEM_COUNT" /> عنصرًا آخر}other{<ph name="ITEM_COUNT" /> عنصر آخر}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">‏يوصي Chrome بإعادة تعيين كلمة مرور <ph name="ORG_NAME" /> في حال إعادة استخدامها في مواقع ويب أخرى.</translation>
<translation id="4196861286325780578">إ&amp;عادة النقل</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />التحقق من عمليات تهيئة الجدار الناري وبرامج مكافحة الفيروسات<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">الأعطال</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">قد لا يتم حفظ التغييرات التي أجريتها.</translation>
<translation id="4258748452823770588">توقيع غير صالح</translation>
<translation id="4265872034478892965">تم السماح من قبل المشرف</translation>
-<translation id="4269787794583293679">(اسم المستخدم غير موجود)</translation>
<translation id="4275830172053184480">إعادة تشغيل جهازك</translation>
<translation id="4277028893293644418">إعادة تعيين كلمة المرور</translation>
<translation id="4280429058323657511">، تاريخ انتهاء الصلاحية <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">النوافذ المنبثقة وإعادة التوجيه</translation>
<translation id="443673843213245140">تم إيقاف استخدام الخادم الوكيل ولكن تم تحديد تهيئة صريحة للخادم الوكيل.</translation>
<translation id="445100540951337728">بطاقات السحب الآلي المقبولة</translation>
+<translation id="4472575034687746823">الخطوات الأولى</translation>
<translation id="4506176782989081258">خطأ في عملية التحقق: <ph name="VALIDATION_ERROR" />.</translation>
<translation id="4506599922270137252">الاتصال بمشرف النظام</translation>
<translation id="450710068430902550">المشاركة مع المشرف</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">إعادة تحميل السياسات</translation>
<translation id="4728558894243024398">النظام الأساسي</translation>
<translation id="4736825316280949806">‏إعادة تشغيل Chromium</translation>
+<translation id="4742407542027196863">إدارة كلمات المرور…</translation>
<translation id="4744603770635761495">المسار التنفيذي</translation>
-<translation id="4749685221585524849">تاريخ آخر استخدام: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">إن معلوماتك (على سبيل المثال، كلمات المرور أو أرقام بطاقة الائتمان) تكون خاصة عندما يتم إرسالها إلى هذا الموقع.</translation>
<translation id="4756388243121344051">ال&amp;سجل</translation>
<translation id="4758311279753947758">إضافة معلومات الاتصال</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">عرض</translation>
<translation id="4854362297993841467">طريقة التسليم هذه غير متاحة. جرِّب طريقة أخرى.</translation>
<translation id="4858792381671956233">لقد سألت والديك ما إذا كانت زيارة هذا الموقع مناسبةً لك</translation>
+<translation id="4876305945144899064">ليس هناك اسم مستخدم</translation>
<translation id="4880827082731008257">سجلّ البحث</translation>
<translation id="4881695831933465202">فتح</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />، <ph name="TYPE_2" />، <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">بلد/دولة</translation>
<translation id="5094747076828555589">‏هذا الخادم لم يتمكن من إثبات أن ذلك <ph name="DOMAIN" />؛ بل إنه شهادة أمان غير موثوقة من قبل Chromium. وربما يكون السبب في ذلك خطأ في التكوين أو مهاجمًا يعترض الاتصال.</translation>
<translation id="5095208057601539847">الإقليم</translation>
+<translation id="5098332213681597508">‏هذا الاسم من حسابك على Google.</translation>
<translation id="5115563688576182185">(64 بت)</translation>
<translation id="5121084798328133320">‏بعد التأكيد، ستتم مشاركة تفاصيل البطاقة من حساب دفعات Google مع هذا الموقع.</translation>
<translation id="5128122789703661928">الجلسة التي تحمل هذا الاسم غير صالحة للحذف.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">طريقة الشحن</translation>
<translation id="5355557959165512791">لا يمكنك زيارة <ph name="SITE" /> الآن لأنه تم إبطال شهادته. أخطاء الشبكة والهجمات عليها عادةً ما تكون مؤقتة، لذا ستعمل هذه الصفحة في وقت على الأرجح.</translation>
<translation id="536296301121032821">تعذّر تخزين إعدادات السياسة</translation>
+<translation id="5371425731340848620">تحديث البطاقة</translation>
<translation id="5377026284221673050">‏""توقيت ساعتك متأخِّر" أو "توقيت ساعتك متقدِّم" أو "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">‏تتضمن سلسلة الشهادات لهذا الموقع شهادة موقعة باستخدام SHA-1.</translation>
-<translation id="5402410679244714488">تاريخ انتهاء الصلاحية: <ph name="EXPIRATION_DATE_ABBR" />، تم استخدامها آخر مرة قبل أكثر من عام</translation>
+<translation id="5387961145478138773">‏الحصول على إمكانية الوصول السريع إلى تطبيقات Google المفضلة لديك</translation>
<translation id="540969355065856584">لم يتمكن هذا الخادم من إثبات أنه <ph name="DOMAIN" />؛ بل إن شهادة الأمان الخاصة به غير صالحة حاليًا. وربما يكون السبب في ذلك وجود خطأ في التكوين أو اعترض أحد المهاجمين للاتصال.</translation>
<translation id="5421136146218899937">محو بيانات التصفح...</translation>
<translation id="5430298929874300616">إزالة إشارة مرجعية</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">البريد الإلكتروني</translation>
+<translation id="5666899935841546222">هل ترغب في الاحتفاظ بجميع بطاقاتك في مكانٍ واحد؟</translation>
<translation id="5675650730144413517">يتعذّر على هذه الصفحة العمل</translation>
<translation id="5685654322157854305">إضافة عنوان الشحن</translation>
<translation id="5689199277474810259">‏تصدير إلى JSON</translation>
<translation id="5689516760719285838">الموقع</translation>
<translation id="570530837424789914">إدارة...</translation>
+<translation id="57094364128775171">اقتراح كلمة مرور قوية…</translation>
<translation id="5710435578057952990">لم يتمّ التحقق من هوية هذا الموقع.</translation>
<translation id="5719499550583120431">يتم قبول بطاقات الدفع المسبق.</translation>
<translation id="5720705177508910913">المستخدم الحالي</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">لا يمكن الوصول إلى موقع الويب هذا</translation>
<translation id="5869522115854928033">كلمات المرور المحفوظة</translation>
<translation id="5893752035575986141">يتم قبول بطاقات الائتمان.</translation>
-<translation id="5898382028489516745">‏يوصي Chromium بإعادة تعيين كلمة مرور <ph name="ORG_NAME" /> في حال إعادة استخدامها في مواقع ويب أخرى.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (تمت المزامنة)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 قيد الاستخدام}zero{# قيد الاستخدام}two{# قيد الاستخدام}few{# قيد الاستخدام}many{# قيد الاستخدام}other{# قيد الاستخدام}}</translation>
<translation id="5939518447894949180">إعادة</translation>
-<translation id="5959728338436674663">‏يمكنك إرسال بعض <ph name="BEGIN_WHITEPAPER_LINK" />معلومات النظام ومحتوى الصفحة<ph name="END_WHITEPAPER_LINK" /> إلى Google تلقائيًا للمساعدة في اكتشاف التطبيقات والمواقع الضارة. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">تعديل معلومات الاتصال</translation>
<translation id="5967867314010545767">إزالة من السجل</translation>
<translation id="5975083100439434680">تصغير</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">الرمز البريدي</translation>
<translation id="6290238015253830360">تظهر مقالاتك المقترحة هنا</translation>
<translation id="6305205051461490394">يتعذر الوصول إلى <ph name="URL" />.</translation>
-<translation id="6319915415804115995">تم استخدامها آخر مرة قبل أكثر من عام</translation>
<translation id="6321917430147971392">التحقق من إعدادات نظام أسماء النطاقات</translation>
<translation id="6328639280570009161">تجربة إيقاف التنبؤ بإجراءات الشبكة</translation>
<translation id="6328786501058569169">هذا الموقع مخادع</translation>
<translation id="6337133576188860026">يوفِّر أقل من <ph name="SIZE" />. وقد يتم تحميل بعض مواقع الويب بشكل أبطأ عند زيارتها في المرة القادمة.</translation>
<translation id="6337534724793800597">تصفية السياسات بحسب الاسم</translation>
-<translation id="6342069812937806050">الآن</translation>
<translation id="6355080345576803305">إلغاء الجلسة العامة</translation>
<translation id="6358450015545214790">ماذا تعني هذه الأقسام؟</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{اقتراح واحد آخر}zero{# اقتراح آخر}two{اقتراحان آخران (#)}few{# اقتراحات أخرى}many{# اقتراحًا آخر}other{# اقتراح آخر}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">إعادة الح&amp;ذف</translation>
<translation id="6534179046333460208">اقتراحات الشبكة المادية</translation>
<translation id="6550675742724504774">خيارات</translation>
-<translation id="6556915248009097796">تاريخ انتهاء الصلاحية: <ph name="EXPIRATION_DATE_ABBR" />، وتاريخ آخر استخدام: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">لم يوافق عليه مديرك حتى الآن</translation>
<translation id="6569060085658103619">أنت تعرض صفحة إضافة</translation>
<translation id="6596325263575161958">خيارات التشفير</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">‏لقد حاولت الوصول إلى <ph name="DOMAIN" />، ولكن قدَّم الخادم شهادة موقّعة باستخدام خوارزمية توقيع ضعيفة (مثل SHA-1)، مما يعني أن بيانات اعتماد الأمان التي قدمها الخادم من المحتمل أنه تم تزييفها، وأن الخادم قد لا يكون هو الخادم الذي تتوقعه (قد تكون على اتصال بأحد المهاجمين).</translation>
<translation id="6831043979455480757">ترجمة</translation>
<translation id="6839929833149231406">المنطقة</translation>
+<translation id="6852204201400771460">هل تريد إعادة تحميل التطبيق؟</translation>
+<translation id="6865412394715372076">يتعذَّر إثبات ملكية هذه البطاقة الآن</translation>
<translation id="6874604403660855544">إعا&amp;دة الإضافة</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">مستوى السياسة غير مدعوم.</translation>
<translation id="6895330447102777224">تم التأكد من بطاقتك</translation>
<translation id="6897140037006041989">وكيل المستخدم</translation>
+<translation id="6903319715792422884">‏يمكنك المساعدة في تحسين التصفُّح الآمن عن طريق إرسال بعض <ph name="BEGIN_WHITEPAPER_LINK" />معلومات النظام ومحتوى الصفحة<ph name="END_WHITEPAPER_LINK" /> إلى Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">المستخدم:</translation>
+<translation id="6944692733090228304">لقد أدخلتَ كلمة المرور في موقع ويب لا تديره <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. ويُرجى عدم إعادة استخدام كلمة المرور في التطبيقات ومواقع الويب الأخرى لحماية حسابك.</translation>
<translation id="6945221475159498467">تحديد</translation>
<translation id="6948701128805548767">لعرض طرق الاستلام ومتطلباته، حدِّد عنوانًا</translation>
<translation id="6949872517221025916">إعادة تحديد كلمة المرور</translation>
+<translation id="6950684638814147129">‏حدث خطأ أثناء تحليل قيمة JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">يبدو أن شهادة الخادم مزيفة.</translation>
<translation id="6965382102122355670">موافق</translation>
<translation id="6965978654500191972">جهاز</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;انقر على &lt;strong&gt;"تطبيق"&lt;/strong&gt;، ثم على &lt;strong&gt;"موافق"&lt;/strong&gt;
&lt;li&gt;انتقِل إلى &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;مركز مساعدة Chrome&lt;/a&gt; لمعرفة كيفية إزالة البرنامج من جهاز الكمبيوتر نهائيًا
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">إدارة كلمات المرور…</translation>
<translation id="7419106976560586862">مسار الملف الشخصي</translation>
<translation id="7437289804838430631">إضافة معلومات الاتصال</translation>
<translation id="7441627299479586546">موضوع السياسة غير صحيح</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />مزيد من المعلومات<ph name="END_LINK" /> حول هذه المشكلة.</translation>
<translation id="7455133967321480974">استخدام الإعداد التلقائي العمومي (حظر)</translation>
<translation id="7460163899615895653">تظهر علامات التبويب الأخيرة من الأجهزة الأخرى هنا</translation>
-<translation id="7469372306589899959">جارٍ التاكد من البطاقة</translation>
<translation id="7473891865547856676">لا، شكرًا</translation>
<translation id="7481312909269577407">إلى الأمام</translation>
<translation id="7485870689360869515">لم يتم العثور على بيانات.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">تعذّرت الترجمة بسبب حدوث مشكلة في الاتصال بالشبكة.</translation>
<translation id="8311129316111205805">تحميل الجلسة</translation>
<translation id="8332188693563227489">تم رفض الدخول إلى <ph name="HOST_NAME" />.</translation>
+<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="8349305172487531364">شريط الإشارات</translation>
<translation id="8363502534493474904">إيقاف تشغيل وضع الطائرة</translation>
@@ -1035,21 +1035,25 @@
<translation id="8498891568109133222">استغرق <ph name="HOST_NAME" /> وقتًا أطول مما يجب للاستجابة.</translation>
<translation id="8503559462189395349">‏كلمات المرور في Chrome</translation>
<translation id="8503813439785031346">اسم المستخدم</translation>
+<translation id="8508648098325802031">رمز البحث</translation>
<translation id="8543181531796978784">يمكنك <ph name="BEGIN_ERROR_LINK" />الإبلاغ عن اكتشاف مشكلة<ph name="END_ERROR_LINK" /> أو، إذا كنت تدرك المخاطر المتعلقة بالأمان، يمكنك <ph name="BEGIN_LINK" />زيارة هذا الموقع غير الآمن<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">هل هناك أي أسئلة؟ اتصل بالشخص الذي يشرف على ملفك الشخصي.</translation>
<translation id="8553075262323480129">تعذّرت الترجمة لتعذر تحديد لغة الصفحة.</translation>
<translation id="8557066899867184262">‏رمز التحقق من البطاقة (CVC) موجود خلف بطاقتك.</translation>
<translation id="8559762987265718583">تعذر إنشاء اتصال خاص بـ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> نظرًا لأن التاريخ والوقت للجهاز (<ph name="DATE_AND_TIME" />) غير صحيحين.</translation>
+<translation id="8564985650692024650">‏يُوصي Chromium بإعادة تحديد كلمة المرور <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> في حال إعادة استخدامها في مواقع ويب أخرى.</translation>
<translation id="8571890674111243710">جارٍ ترجمة الصفحة إلى <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">إضافة رقم هاتف
</translation>
<translation id="859285277496340001">لا تحدد الشهادة آلية للتحقق مما إذا كانت الشهادة قد تم إبطالها.</translation>
+<translation id="860043288473659153">اسم حامل البطاقة</translation>
<translation id="8620436878122366504">لم يوافق عليه والداك حتى الآن</translation>
<translation id="8625384913736129811">حفظ هذه البطاقة إلى هذا الجهاز</translation>
-<translation id="8639963783467694461">إعدادات الملء التلقائي</translation>
+<translation id="8663226718884576429">ملخّص الطلب و<ph name="TOTAL_LABEL" /> وتفاصيل إضافية</translation>
<translation id="8680536109547170164"><ph name="QUERY" />، إجابة، <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">الاتصال بالموقع <ph name="DOMAIN" /> غير محميّ بنظام تشفير.</translation>
<translation id="8718314106902482036">لم تكتمل عملية الدفع</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />، <ph name="DESCRIPTION" />، اقتراح البحث</translation>
<translation id="8725066075913043281">أعد المحاولة</translation>
<translation id="8728672262656704056">لقد انتقلت إلى التصفّح المتخفي</translation>
<translation id="8730621377337864115">تم</translation>
@@ -1062,11 +1066,13 @@
<translation id="8790007591277257123">إعادة الح&amp;ذف</translation>
<translation id="8792621596287649091">‏قد تفقد إمكانية الوصول إلى حسابك على <ph name="ORG_NAME" /> أو تتعرض لسرقة هويتك. لذا يوصي Chromium بتغيير كلمة مرورك الآن.</translation>
<translation id="8800988563907321413">تظهر اقتراحاتك "المجاورة" هنا</translation>
-<translation id="8820817407110198400">إشارات</translation>
+<translation id="8820817407110198400">الإشارات المرجعية</translation>
<translation id="883848425547221593">إشارات أخرى</translation>
<translation id="884264119367021077">عنوان الشحن</translation>
+<translation id="8846319957959474018">فتح التطبيقات بسهولة باستخدام الإشارات المرجعية</translation>
<translation id="884923133447025588">لم يتمّ العثور على أي آلبة إبطال.</translation>
<translation id="885730110891505394">‏مشاركة مع Google</translation>
+<translation id="8858065207712248076">‏يُوصي Chrome بإعادة تحديد كلمة المرور <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> في حال إعادة استخدامها في مواقع ويب أخرى.</translation>
<translation id="8866481888320382733">خطأ في إعدادات تحليل السياسة</translation>
<translation id="8870413625673593573">العناصر المغلقة مؤخرًا</translation>
<translation id="8874824191258364635">أدخِل رقم بطاقة صحيحًا</translation>
@@ -1105,6 +1111,7 @@
<translation id="9103872766612412690">‏يستخدم <ph name="SITE" /> التشفير عادة لحماية معلوماتك. عندما حاول Chromium الاتصال بموقع <ph name="SITE" /> هذه المرة، أرجَع موقع الويب بيانات اعتماد غير عادية وغير صحيحة. وقد يحدث هذا عندما يحاول أحد المهاجمين التظاهر بأنه موقع <ph name="SITE" />، أو إذا قاطعت شاشة تسجيل دخول Wi-Fi الاتصال. ولكن لا تزال معلوماتك آمنة نظرًا لأن Chromium أوقَفَ الاتصال قبل تبادل أي بيانات.</translation>
<translation id="9106062320799175032">إضافة عنوان إرسال الفواتير</translation>
<translation id="910908805481542201">ساعدني لإصلاح ذلك</translation>
+<translation id="9114524666733003316">جارٍ التحقق من البطاقة...</translation>
<translation id="9128870381267983090">الاتصال بالشبكة</translation>
<translation id="9137013805542155359">إظهار الصفحة الأصلية</translation>
<translation id="9137248913990643158">‏يُرجى البدء وتسجيل الدخول إلى Chrome قبل استخدام هذا التطبيق.</translation>
@@ -1115,6 +1122,7 @@
<translation id="9168814207360376865">السماح للمواقع بالتحقُّق ممّا إذا كانت لديك طرق دفع محفوظة أم لا</translation>
<translation id="9169664750068251925">الحظر دومًا على هذا الموقع</translation>
<translation id="9170848237812810038">&amp;إلغاء</translation>
+<translation id="9171296965991013597">هل تريد مغادرة التطبيق؟</translation>
<translation id="917450738466192189">شهادة الخادم غير صالحة.</translation>
<translation id="9183425211371246419">يستخدم <ph name="HOST_NAME" /> بروتوكول غير مدعوم.</translation>
<translation id="9205078245616868884">يتم تشفير بياناتك باستخدام عبارة مرور المزامنة. أدخلها لبدء المزامنة.</translation>
diff --git a/chromium/components/strings/components_strings_bg.xtb b/chromium/components/strings/components_strings_bg.xtb
index d4a2bff21d5..b7631c04639 100644
--- a/chromium/components/strings/components_strings_bg.xtb
+++ b/chromium/components/strings/components_strings_bg.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Скриване на стойността</translation>
<translation id="1228893227497259893">Грешен идентификатор на обект</translation>
<translation id="1232569758102978740">Неозаглавен</translation>
+<translation id="1250759482327835220">За да платите по-бързо следващия път, запазете картата, името и адреса си за фактуриране в профила си в Google.</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;
@@ -97,7 +98,6 @@
&lt;p&gt;Моля, коригирайте датата и часа от секцията &lt;strong&gt;General&lt;/strong&gt; на приложението &lt;strong&gt;Settings&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Възникна проблем при показването на тази уеб страница.</translation>
-<translation id="1590457302292452960">Генериране на надеждна парола...</translation>
<translation id="1592005682883173041">Достъп до локални данни</translation>
<translation id="1594030484168838125">Избор</translation>
<translation id="1620510694547887537">Камера</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Свържете се със системния администратор.</translation>
<translation id="1740951997222943430">Въведете валиден месец на изтичане</translation>
+<translation id="1743520634839655729">За да платите по-бързо следващия път, запазете картата, името и адреса си за фактуриране в профила си в Google и на това устройство.</translation>
<translation id="17513872634828108">Отворени раздели</translation>
<translation id="1753706481035618306">Номер на страницата</translation>
<translation id="1763864636252898013">Сървърът не можа да докаже, че е <ph name="DOMAIN" />; операционната система на устройството ви няма доверие на сертификата му за сигурност. Това може да се дължи на неправилно конфигуриране или на прихващане на връзката ви от атакуващ.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Тук ще се показват отворените ви раздели</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Име на титуляря на картата</translation>
-<translation id="1806541873155184440">Добавено: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Заявката или параметрите й са невалидни</translation>
<translation id="1826516787628120939">Извършва се проверка</translation>
<translation id="1834321415901700177">Този сайт съдържа опасни програми</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 предложение}other{# предложения}}</translation>
<translation id="2079545284768500474">Отмяна</translation>
<translation id="20817612488360358">За използване са зададени системни настройки за прокси сървъра, но е посочена и изрична конфигурация.</translation>
-<translation id="2084558088529668945">Въведохте паролата си на сайт, който не се управлява от <ph name="ORG_NAME" />. За да защитите профила си, не използвайте паролата си повторно в други приложения или сайтове.</translation>
<translation id="2091887806945687916">Звук</translation>
<translation id="2094505752054353250">Несъответствие в домейна</translation>
<translation id="2096368010154057602">Департамент</translation>
+<translation id="2102134110707549001">Предложение за надеждна парола…</translation>
<translation id="2108755909498034140">Рестартирайте компютъра си.</translation>
<translation id="2113977810652731515">Карта</translation>
<translation id="2114841414352855701">Бе пренебрегнато, защото бе отменено от <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP грешка</translation>
<translation id="2270484714375784793">Телефонен номер</translation>
<translation id="2292556288342944218">Достъпът ви до интернет е блокиран</translation>
-<translation id="230155334948463882">Нова карта?</translation>
<translation id="2316887270356262533">Ще освободите по-малко от 1 МБ. Някои сайтове може да се заредят по-бавно при следващото ви посещение.</translation>
<translation id="2317259163369394535">Изискват се потребителско име и парола за <ph name="DOMAIN" />.</translation>
<translation id="2317583587496011522">Приемат се дебитни карти.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Приемани карти</translation>
<translation id="2702801445560668637">Списък за четене</translation>
<translation id="2704283930420550640">Стойността не съответства на формата.</translation>
-<translation id="2704951214193499422">Chromium не можа да потвърди картата ви. Моля, опитайте отново по-късно.</translation>
<translation id="2705137772291741111">Запазеното (кеширано) копие на този сайт не можа да се прочете.</translation>
<translation id="2709516037105925701">Автоматично попълване</translation>
<translation id="2710942282213947212">Софтуер на компютъра ви пречи на Chromium да се свърже безопасно с мрежата</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Начин на доставка</translation>
<translation id="277499241957683684">Липсващ запис за устройството</translation>
<translation id="2781030394888168909">Експортиране за Mac OS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Връзката бе възстановена.</translation>
<translation id="2788784517760473862">Приемани кредитни карти</translation>
<translation id="2794233252405721443">Сайтът е блокиран</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Можете да деактивирате всички конфигурирани за дадена връзка прокси сървъри от страницата „Настройки“.</translation>
<translation id="2955913368246107853">Затваряне на лентата за търсене</translation>
<translation id="2958431318199492670">Конфигурацията на мрежата не спазва стандарта на ONC. Възможно е части от нея да не са импортирани.</translation>
-<translation id="2966678944701946121">Валидност: <ph name="EXPIRATION_DATE_ABBR" />. Добавено: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">За установяване на сигурна връзка е необходимо часовникът ви да е верен. Това е така, защото сертификатите, с които уебсайтовете се идентифицират, са валидни само за конкретни периоди от време. Тъй като часовникът на устройството ви не е верен, Google Chrome не може да потвърди тези сертификати.</translation>
<translation id="2972581237482394796">&amp;Възстановяване</translation>
<translation id="2977665033722899841">Понастоящем сте избрали реда „<ph name="ROW_NAME" />“. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Грешен тип на правилото</translation>
<translation id="3037605927509011580">Ужас!</translation>
<translation id="3041612393474885105">Информация за сертификата</translation>
-<translation id="3063697135517575841">Chrome не можа да потвърди картата ви. Моля, опитайте отново по-късно.</translation>
<translation id="3064966200440839136">Ще напуснете режим „инкогнито“, за да платите във външно приложение. Искате ли да продължите?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Няма}=1{1 парола}other{# пароли}}</translation>
<translation id="3096100844101284527">Добавяне на адрес за вземане</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">На <ph name="TIME" /> данните ви бяха шифровани с пропуска ви за синхронизиране. Въведете го, за да стартирате синхронизирането.</translation>
<translation id="3320021301628644560">Добавяне на адреса за фактуриране</translation>
<translation id="3338095232262050444">Има защита</translation>
-<translation id="3340978935015468852">настройки</translation>
<translation id="3345135638360864351">Заявката ви за достъп до този сайт не можа да се изпрати до <ph name="NAME" />. Моля, опитайте отново.</translation>
<translation id="3355823806454867987">Промяна на настройките на прокси сървъра...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />няма да съхранява<ph name="END_EMPHASIS" /> следната информация:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Сертификатът на сървъра не е надежден.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Поне 1 елемент на синхронизирани устройства}=1{1 елемент (и други на синхронизирани устройства)}other{# елемента (и други на синхронизирани устройства)}}</translation>
<translation id="3539171420378717834">Съхраняване на копие на картата на това устройство</translation>
-<translation id="3542684924769048008">Използване на паролата за:</translation>
<translation id="3549644494707163724">Всички синхронизирани данни да се шифроват със собствения ви пропуск за синхронизиране</translation>
<translation id="3556433843310711081">Мениджърът ви може да го отблокира за вас</translation>
<translation id="3566021033012934673">Връзката ви не е поверителна</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Невалиден подпис за потвърждаване</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{още <ph name="ITEM_COUNT" /> елемент}other{още <ph name="ITEM_COUNT" /> елемента}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> – <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome препоръчва да зададете повторно паролата си за <ph name="ORG_NAME" />, ако сте я използвали и на други сайтове.</translation>
<translation id="4196861286325780578">&amp;Възстановяване на преместването</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Проверете конфигурацията на защитната стена и антивирусния софтуер<ph name="END_LINK" />.</translation>
<translation id="4220128509585149162">Сривове</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Направените от вас промени може да не се запазят.</translation>
<translation id="4258748452823770588">Невалиден подпис</translation>
<translation id="4265872034478892965">Разрешено от администратора ви</translation>
-<translation id="4269787794583293679">(Няма потребителско име)</translation>
<translation id="4275830172053184480">Рестартиране на устройството ви</translation>
<translation id="4277028893293644418">Повторно задаване на паролата</translation>
<translation id="4280429058323657511">, изтича: <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Изскач. прозорци и пренасочвания</translation>
<translation id="443673843213245140">Използването на прокси сървър е деактивирано, но е посочена изрична негова конфигурация.</translation>
<translation id="445100540951337728">Приемани дебитни карти</translation>
+<translation id="4472575034687746823">Първи стъпки</translation>
<translation id="4506176782989081258">Грешка при потвърждаването: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Свържете се със системния администратор.</translation>
<translation id="450710068430902550">Споделяне с администратор</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Презареждане на правилата</translation>
<translation id="4728558894243024398">Платформа</translation>
<translation id="4736825316280949806">Рестартирайте Chromium.</translation>
+<translation id="4742407542027196863">Управление на паролите…</translation>
<translation id="4744603770635761495">Път към изпълнимия файл</translation>
-<translation id="4749685221585524849">Последно използване: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Информацията ви (например пароли или номера на кредитни карти) е частна, когато се изпраща до този сайт.</translation>
<translation id="4756388243121344051">&amp;История</translation>
<translation id="4758311279753947758">Добавяне на информация за връзка</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Изглед</translation>
<translation id="4854362297993841467">Този начин на бърза доставка не се поддържа. Опитайте с друг.</translation>
<translation id="4858792381671956233">Попитахте родителите си дали може да посетите този сайт</translation>
+<translation id="4876305945144899064">Няма потребителско име</translation>
<translation id="4880827082731008257">Търсене в историята</translation>
<translation id="4881695831933465202">Отваряне</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> и <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Щат</translation>
<translation id="5094747076828555589">Сървърът не можа да докаже, че е <ph name="DOMAIN" />; Chromium няма доверие на сертификата му за сигурност. Това може да се дължи на неправилно конфигуриране или на прихващане на връзката ви от атакуващ.</translation>
<translation id="5095208057601539847">Провинция</translation>
+<translation id="5098332213681597508">Това име е от профила ви в Google.</translation>
<translation id="5115563688576182185">(64 бита)</translation>
<translation id="5121084798328133320">След като потвърдите картата си, данните за нея от профила ви в Google Payments ще бъдат споделени с този сайт.</translation>
<translation id="5128122789703661928">Сесията с това име не е валидна за изтриване.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Начин за доставка</translation>
<translation id="5355557959165512791">В момента не можете да посетите сайта <ph name="SITE" />, защото сертификатът му е анулиран. Обикновено грешките в мрежата и атаките срещу нея са временни, така че тази страница вероятно ще работи по-късно.</translation>
<translation id="536296301121032821">Съхраняването на настройките за правилото не бе успешно</translation>
+<translation id="5371425731340848620">Актуализиране на картата</translation>
<translation id="5377026284221673050">„Часовникът ви е назад“, „Часовникът ви е напред“ или „&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;“</translation>
<translation id="5386426401304769735">Веригата от сертификати за този сайт съдържа сертификат, подписан с SHA-1.</translation>
-<translation id="5402410679244714488">Валидност: <ph name="EXPIRATION_DATE_ABBR" />. Последно използване преди повече от година</translation>
+<translation id="5387961145478138773">Получете бърз достъп до любимите си приложения на Google</translation>
<translation id="540969355065856584">Сървърът не можа да докаже, че е <ph name="DOMAIN" />. Понастоящем сертификатът му за сигурност не е валиден. Това може да се дължи на неправилно конфигуриране или на прихващане на връзката ви от извършител на атака.</translation>
<translation id="5421136146218899937">Изчистване на данните за сърфирането...</translation>
<translation id="5430298929874300616">Премахване на отметката</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">Имейл</translation>
+<translation id="5666899935841546222">Искате ли да разполагате с всичките си карти на едно място?</translation>
<translation id="5675650730144413517">Тази страница не работи</translation>
<translation id="5685654322157854305">Добавяне на адрес за доставка</translation>
<translation id="5689199277474810259">Експортиране във формат JSON</translation>
<translation id="5689516760719285838">Местоположение</translation>
<translation id="570530837424789914">Управление...</translation>
+<translation id="57094364128775171">Предложение за надеждна парола…</translation>
<translation id="5710435578057952990">Самоличността на този уебсайт не е потвърдена.</translation>
<translation id="5719499550583120431">Приемат се предплатени карти.</translation>
<translation id="5720705177508910913">Текущият потребител</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Няма достъп до този сайт</translation>
<translation id="5869522115854928033">Запазени пароли</translation>
<translation id="5893752035575986141">Приемат се кредитни карти.</translation>
-<translation id="5898382028489516745">Chromium препоръчва да зададете повторно паролата си за <ph name="ORG_NAME" />, ако сте я използвали и на други сайтове.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (синхронизирано)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{Използва се 1}other{Използват се #}}</translation>
<translation id="5939518447894949180">Нулиране</translation>
-<translation id="5959728338436674663">Автоматично изпращане до Google на <ph name="BEGIN_WHITEPAPER_LINK" />системна информация и част от съдържанието на страниците<ph name="END_WHITEPAPER_LINK" /> с цел по-лесно откриване на опасни приложения и сайтове. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Редактиране на информацията за връзка</translation>
<translation id="5967867314010545767">Премахване от историята</translation>
<translation id="5975083100439434680">Намаляване на мащаба</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Пощенски код</translation>
<translation id="6290238015253830360">Предложените ви статии ще се показват тук</translation>
<translation id="6305205051461490394">Няма достъп до <ph name="URL" />.</translation>
-<translation id="6319915415804115995">Последно използване преди повече от година</translation>
<translation id="6321917430147971392">Проверете настройките си за DNS</translation>
<translation id="6328639280570009161">Опитайте да деактивирате предвижданията за мрежата</translation>
<translation id="6328786501058569169">Този сайт е измамен</translation>
<translation id="6337133576188860026">Ще освободите по-малко от <ph name="SIZE" />. Някои сайтове може да се заредят по-бавно при следващото ви посещение.</translation>
<translation id="6337534724793800597">Филтриране на правилата по име</translation>
-<translation id="6342069812937806050">Току-що</translation>
<translation id="6355080345576803305">Принудително зададено поради обществена сесия</translation>
<translation id="6358450015545214790">Какво означават тези неща?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{още 1 предложение}other{още # предложения}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Възстановяване на изтриването</translation>
<translation id="6534179046333460208">Предложения от Физическата мрежа</translation>
<translation id="6550675742724504774">Опции</translation>
-<translation id="6556915248009097796">Валидност: <ph name="EXPIRATION_DATE_ABBR" />. Последно използване: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Мениджърът ви все още не е одобрил заявката</translation>
<translation id="6569060085658103619">Преглеждате страница на разширение</translation>
<translation id="6596325263575161958">Опции за шифроване</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Опитахте да отворите <ph name="DOMAIN" />, но сървърът предостави сертификат, подписан със слаб алгоритъм (например SHA-1). Това означава, че идентификационните данни за сигурност от сървъра може да са фалшифицирани и той да не е този, който очаквате (възможно е да сте се свързали с извършител на атака).</translation>
<translation id="6831043979455480757">Превод</translation>
<translation id="6839929833149231406">Район</translation>
+<translation id="6852204201400771460">Искате ли да презаредите приложението?</translation>
+<translation id="6865412394715372076">Тази карта не може да бъде потвърдена в момента</translation>
<translation id="6874604403660855544">&amp;Възстановяване на добавянето</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Нивото на правилото не се поддържа.</translation>
<translation id="6895330447102777224">Картата ви е потвърдена</translation>
<translation id="6897140037006041989">Потребителски агент</translation>
+<translation id="6903319715792422884">Помогнете за подобряването на Безопасно сърфиране, като ни изпращате <ph name="BEGIN_WHITEPAPER_LINK" />системна информация и част от съдържанието на страниците<ph name="END_WHITEPAPER_LINK" />. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Потребител:</translation>
+<translation id="6944692733090228304">Въведохте паролата си на сайт, който не се управлява от <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. За да защитите профила си, не използвайте паролата си повторно в други приложения и сайтове.</translation>
<translation id="6945221475159498467">Изберете</translation>
<translation id="6948701128805548767">За да видите начините на вземане и изискванията, изберете адрес</translation>
<translation id="6949872517221025916">Задаване на нова парола</translation>
+<translation id="6950684638814147129">При синтактичния анализ на стойността в JSON възникна грешка: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Изглежда, че сертификатът на сървъра е подправен.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Устройство</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Кликнете върху &lt;strong&gt;Приложи&lt;/strong&gt; и след това – върху &lt;strong&gt;OK&lt;/strong&gt;.
&lt;li&gt;Посетете &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Помощния център на Chrome&lt;/a&gt;, за да научите как да премахнете за постоянно софтуера от компютъра си.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Управление на паролите…</translation>
<translation id="7419106976560586862">Път на потребителския профил</translation>
<translation id="7437289804838430631">Добавяне на информация за връзка</translation>
<translation id="7441627299479586546">Грешен предмет на правилото</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Научете повече<ph name="END_LINK" /> за този проблем.</translation>
<translation id="7455133967321480974">Използване на глобалната стандартна стойност (блокиране)</translation>
<translation id="7460163899615895653">Тук се показват скорошните раздели от други устройства</translation>
-<translation id="7469372306589899959">Картата се потвърждава</translation>
<translation id="7473891865547856676">Не, благодаря</translation>
<translation id="7481312909269577407">Препращане</translation>
<translation id="7485870689360869515">Няма намерени данни.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Преводът не бе успешен поради проблем с връзката към мрежата.</translation>
<translation id="8311129316111205805">Зареждане на сесията</translation>
<translation id="8332188693563227489">Достъпът до <ph name="HOST_NAME" /> бе отказан</translation>
+<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="8349305172487531364">Лента на отметките</translation>
<translation id="8363502534493474904">Изключете самолетния режим.</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> не отговаря твърде дълго време.</translation>
<translation id="8503559462189395349">Пароли в Chrome</translation>
<translation id="8503813439785031346">Потребителско име</translation>
+<translation id="8508648098325802031">Икона за търсене</translation>
<translation id="8543181531796978784">Можете да <ph name="BEGIN_ERROR_LINK" />подадете сигнал за проблем при откриването<ph name="END_ERROR_LINK" /> или, ако разбирате рисковете за сигурността си, да <ph name="BEGIN_LINK" />посетите този небезопасен сайт<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">При въпроси се свържете с лицето, което контролира потребителския ви профил.</translation>
<translation id="8553075262323480129">Преводът не бе успешен, защото езикът на страницата не можа да бъде определен.</translation>
<translation id="8557066899867184262">Кодът за проверка се намира на гърба на картата ви.</translation>
<translation id="8559762987265718583">Не може да се установи частна връзка с/ъс <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, тъй като датата и часът на устройството ви (<ph name="DATE_AND_TIME" />) са неправилни.</translation>
+<translation id="8564985650692024650">Chromium препоръчва да зададете повторно паролата си за <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, ако сте я използвали и на други сайтове.</translation>
<translation id="8571890674111243710">Превод на страницата на <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">+ тел. номер</translation>
<translation id="859285277496340001">Сертификатът не посочва механизъм за проверка дали е бил анулиран.</translation>
+<translation id="860043288473659153">Име на титуляря на картата</translation>
<translation id="8620436878122366504">Родителите ви все още не са одобрили заявката</translation>
<translation id="8625384913736129811">Запазване на картата на това устройство</translation>
-<translation id="8639963783467694461">Настройки за автоматично попълване</translation>
+<translation id="8663226718884576429">Обобщение на поръчката, <ph name="TOTAL_LABEL" />, още подробности</translation>
<translation id="8680536109547170164">„<ph name="QUERY" />“, отговор: „<ph name="ANSWER" />“</translation>
<translation id="8703575177326907206">Връзката ви с <ph name="DOMAIN" /> не е шифрована.</translation>
<translation id="8718314106902482036">Плащането не е завършено</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, предложение за търсене</translation>
<translation id="8725066075913043281">Опитайте отново</translation>
<translation id="8728672262656704056">Преминахте в режим „инкогнито“</translation>
<translation id="8730621377337864115">Готово</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Отметки</translation>
<translation id="883848425547221593">Други отметки</translation>
<translation id="884264119367021077">Адрес за доставка</translation>
+<translation id="8846319957959474018">Лесно отваряйте приложения с помощта на отметки</translation>
<translation id="884923133447025588">Не е намерен механизъм за анулиране.</translation>
<translation id="885730110891505394">Споделяне с Google</translation>
+<translation id="8858065207712248076">Chrome препоръчва да зададете повторно паролата си за <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, ако сте я използвали и на други сайтове.</translation>
<translation id="8866481888320382733">Грешка при синтактичния анализ на настройките за правилото</translation>
<translation id="8870413625673593573">Наскоро затворени</translation>
<translation id="8874824191258364635">Въведете валиден номер на карта</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690">Обикновено <ph name="SITE" /> използва шифроване за защита на информацията ви. Когато Chromium опита да установи връзка с/ъс <ph name="SITE" /> този път, уебсайтът върна необичайни и неправилни идентификационни данни. Това може да се случи, когато извършител на атака пробва да се представи за <ph name="SITE" /> или връзката е прекъсната от екран за вход в Wi-Fi. Информацията ви продължава да е защитена, тъй като Chromium спря връзката, преди да бъдат обменени данни.</translation>
<translation id="9106062320799175032">Добавяне на адрес за фактуриране</translation>
<translation id="910908805481542201">Помогнете ми да отстраня този проблем</translation>
+<translation id="9114524666733003316">Картата се потвърждава...</translation>
<translation id="9128870381267983090">Свързване към мрежа</translation>
<translation id="9137013805542155359">Показване на оригинала</translation>
<translation id="9137248913990643158">Моля, стартирайте браузъра Chrome и влезте в него, преди да използвате това приложение.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">Разрешаване на сайтовете да проверяват дали имате запазени начини на плащане</translation>
<translation id="9169664750068251925">Блокиране винаги на този сайт</translation>
<translation id="9170848237812810038">&amp;Отмяна</translation>
+<translation id="9171296965991013597">Искате ли да затворите приложението?</translation>
<translation id="917450738466192189">Сертификатът на сървъра е невалиден.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> използва неподдържан протокол.</translation>
<translation id="9205078245616868884">Данните ви са шифровани с пропуска ви за синхронизиране. Въведете го, за да стартирате синхронизирането.</translation>
diff --git a/chromium/components/strings/components_strings_bn.xtb b/chromium/components/strings/components_strings_bn.xtb
index 91b140b21cb..c93118b66f3 100644
--- a/chromium/components/strings/components_strings_bn.xtb
+++ b/chromium/components/strings/components_strings_bn.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">মান লুকান</translation>
<translation id="1228893227497259893">ভুল সত্তা সনাক্তকারী</translation>
<translation id="1232569758102978740">শিরোনামহীন</translation>
+<translation id="1250759482327835220">পরেরবার আরও দ্রুত পেমেন্ট করা জন্য আপনার কার্ড, নাম এবং বিলিং ঠিকানাটি Google অ্যাকাউন্টে সেভ করে রাখুন।</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;
@@ -97,7 +98,6 @@
&lt;p&gt;দয়া করে &lt;strong&gt;সেটিংস&lt;/strong&gt; অ্যাপ্লিকেশানের &lt;strong&gt;সাধারণ&lt;/strong&gt; বিভাগ থেকে তারিখ এবং সময় সংশোধন করুন৷&lt;/p&gt;</translation>
<translation id="1583429793053364125">এই ওয়েবপৃষ্ঠাটি দেখানোর সময় কোনো সমস্যা হয়েছে।</translation>
-<translation id="1590457302292452960">একটি কঠিন পাসওয়ার্ড তৈরি করুন...</translation>
<translation id="1592005682883173041">স্থানীয় ডেটা অ্যাক্সেস</translation>
<translation id="1594030484168838125">বেছে নিন</translation>
<translation id="1620510694547887537">ক্যামেরা</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">সিস্টেম প্রশাসকের সাথে যোগাযোগ করে দেখুন।</translation>
<translation id="1740951997222943430">মেয়াদ শেষ হওয়ার মাসের সঠিক মান লিখুন</translation>
+<translation id="1743520634839655729">পরেরবার আরও দ্রুত পেমেন্ট করা জন্য আপনার কার্ড, নাম এবং বিলিং ঠিকানাটি Google অ্যাকাউন্টে এবং এই ডিভাইসে সেভ করে রাখুন।</translation>
<translation id="17513872634828108">খোলা ট্যাব</translation>
<translation id="1753706481035618306">পৃষ্ঠা সংখ্যা</translation>
<translation id="1763864636252898013">এই সার্ভার প্রমাণ করতে পারেনি যে এটি <ph name="DOMAIN" />; এর নিরাপত্তা শংসাপত্রটি আপনার ডিভােইসের নিকট বিশ্বাসযোগ্য নয়। কোনো ভুল কনফিগারেশনের কারণে অথবা কোনো আক্রমণকারী আপনার সংযোগ মাঝপথে আটকে দিচ্ছে বলে এমনটা হতে পারে।</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">আপনার খোলা ট্যাবগুলি এখানে দেখা যাবে</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">কার্ড হোল্ডারের নাম</translation>
-<translation id="1806541873155184440">যোগ করা হয়েছে <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">অবৈধ অনুরোধ বা অনুরোধ মাপকাঠিগুলি</translation>
<translation id="1826516787628120939">চেক করা হচ্ছে</translation>
<translation id="1834321415901700177">এই সাইটটিতে ক্ষতিকর প্রোগ্রাম রয়েছে</translation>
@@ -165,10 +165,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{১টি প্রস্তাব}one{#টি প্রস্তাব}other{#টি প্রস্তাব}}</translation>
<translation id="2079545284768500474">পূর্বাবস্থায় ফিরুন</translation>
<translation id="20817612488360358">সিস্টেম প্রক্সি সেটিংস ব্যবহার করার জন্য সেট আছে কিন্তু একটি সুনির্দিষ্ট প্রক্সি কনফিগারেশনও নির্দিষ্ট করা আছে৷</translation>
-<translation id="2084558088529668945"><ph name="ORG_NAME" /> এর নয় এমন একটি সাইটে আপনার পাসওয়ার্ড লিখেছেন। আপনার অ্যাকাউন্টের সুরক্ষার জন্য অন্যান্য অ্যাপ এবং সাইটগুলিতে আপনার এই পাসওয়ার্ডটি ব্যবহার করবেন না।</translation>
<translation id="2091887806945687916">আওয়াজ</translation>
<translation id="2094505752054353250">ডোমেন মেলেনি</translation>
<translation id="2096368010154057602">বিভাগ</translation>
+<translation id="2102134110707549001">শক্তিশালী পাসওয়ার্ড সাজেস্ট করুন…</translation>
<translation id="2108755909498034140">আপনার কম্পিউটার পুনরায় চালু করুন</translation>
<translation id="2113977810652731515">কার্ড</translation>
<translation id="2114841414352855701">এড়িয়ে যাওয়া হয়েছে কারণ এটি <ph name="POLICY_NAME" />-দ্বারা ওভাররাইড করা হয়েছিল৷</translation>
@@ -195,7 +195,6 @@
<translation id="2262243747453050782">HTTP ত্রুটি</translation>
<translation id="2270484714375784793">ফোন নম্বর</translation>
<translation id="2292556288342944218">আপনার ইন্টারনেট অ্যাক্সেস অবরুদ্ধ করা হয়েছে</translation>
-<translation id="230155334948463882">নতুন কার্ড?</translation>
<translation id="2316887270356262533">১ MB এর চেয়ে কম জায়গা খালি করে। পরের বার যখন দেখবেন তখন কিছু সাইট লোড হতে দেরি হতে পারে।</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> এর জন্য একটি ইউজারনেম এবং পাসওয়ার্ড প্রয়োজন।</translation>
<translation id="2317583587496011522">ডেবিট কার্ড গ্রহণ করা হয়।</translation>
@@ -249,7 +248,6 @@
<translation id="2699302886720511147">এই কার্ডগুলি গ্রহণ করা হয়</translation>
<translation id="2702801445560668637">পড়ার তালিকা</translation>
<translation id="2704283930420550640">বিন্যাসের সাথে মূল্য মেলে না৷</translation>
-<translation id="2704951214193499422">Chromium এই মুহূর্তে আপনার কার্ড নিশ্চিত করতে অক্ষম হয়েছে৷ দয়া করে পরে আবার চেষ্টা করুন৷</translation>
<translation id="2705137772291741111">এই সাইটের সংরক্ষিত (সঞ্চিত) অনুলিপি পড়া সম্ভব হয়নি।</translation>
<translation id="2709516037105925701">স্বয়ংপূরণ</translation>
<translation id="2710942282213947212">আপনার কম্পিউটারের সফ্টওয়্যার Chromium কে নিরাপদে ওয়েবে সংযোগ করতে বাধা দিচ্ছে</translation>
@@ -264,6 +262,7 @@
<translation id="277133753123645258">শিপিংয়ের পদ্ধতি</translation>
<translation id="277499241957683684">ডিভাইস রেকর্ড অনুপস্থিত</translation>
<translation id="2781030394888168909">MacOS এক্সপোর্ট করুন</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">সংযোগ পুনঃসেট করা হয়েছে৷</translation>
<translation id="2788784517760473862">ক্রেডিট কার্ড গ্রহণ করা হয়</translation>
<translation id="2794233252405721443">সাইট অবরুদ্ধ করা হয়েছে</translation>
@@ -285,7 +284,6 @@
<translation id="2948083400971632585">আপনি সেটিংস পৃষ্ঠা থেকে সংযোগের জন্য কনফিগার করা যেকোনো প্রক্সি নিষ্ক্রিয় করতে পারেন৷</translation>
<translation id="2955913368246107853">খোঁজ দণ্ড বন্ধ করুন</translation>
<translation id="2958431318199492670">নেটওয়ার্ক কনফিগারেশন ONC মানকের সাথে সম্মত নয়৷ কনফিগারেশনের অংশগুলি আমদানিকৃত নাও হতে পারে৷</translation>
-<translation id="2966678944701946121">মেয়াদ শেষের তারিখ: <ph name="EXPIRATION_DATE_ABBR" />, যোগ করা হয়েছে <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">নিরাপদ নেটওয়ার্ক সংযোগ স্থাপন করতে আপনার ঘড়িকে সঠিকভাবে সেট করতে হবে। এমন হওয়ার কারণ হলো, নিরাপদ সংযোগ স্থাপন করার জন্য নিজেদের শনাক্ত করার জন্য ওয়েবসাইটগুলি যে শংসাপত্রগুলি ব্যবহার করে, সেগুলি শুধুমাত্র নির্দিষ্ট সময়ের জন্য বৈধ থাকে। যেহেতু আপনার ডিভাইসের ঘড়িটি ভুল, সেই জন্য Google Chrome সঠিকভাবে শংসাপত্রগুলি পরীক্ষা করতে পারছে না।</translation>
<translation id="2972581237482394796">&amp;পুনরায় করুন</translation>
<translation id="2977665033722899841">এখন <ph name="ROW_NAME" /> বেছে নিয়েছেন। <ph name="ROW_CONTENT" /></translation>
@@ -302,7 +300,6 @@
<translation id="3024663005179499861">নীতির ভুল প্রকার</translation>
<translation id="3037605927509011580">ইস!</translation>
<translation id="3041612393474885105">শংসাপত্র তথ্য</translation>
-<translation id="3063697135517575841">Chrome এই মুহূর্তে আপনার কার্ড নিশ্চিত করতে পারছে না৷ অনুগ্রহ করে পরে আবার চেষ্টা করুন৷</translation>
<translation id="3064966200440839136">বহিরাগত অ্যাপ্লিকেশানের মাধ্যমে অর্থপ্রদান করার জন্য ছদ্মবেশী মোড থেকে বেরিয়ে যাচ্ছে। চালিয়ে যাবেন?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{কিছুই নয়}=1{১টি পাসওয়ার্ড}one{#টি পাসওয়ার্ড}other{#টি পাসওয়ার্ড}}</translation>
<translation id="3096100844101284527">পিক-আপের ঠিকানা যোগ করুন</translation>
@@ -315,7 +312,7 @@
<translation id="3154506275960390542">এই পৃষ্ঠাতে একটি ফর্ম আছে যেটি জমা দেওয়া নিরাপদ নাও হতে পারে। আপনার পাঠানো ডেটা সার্ভারে পৌঁছানোর আগে অন্যরা সেটি দেখতে পেতে পারেন, অথবা কেউ সেটি পরিবর্তন করে দিতে পারেন যাতে আপনার ডেটা সঠিকভাবে সার্ভারে না পৌঁছায়।</translation>
<translation id="3157931365184549694">পুনরুদ্ধার করুন</translation>
<translation id="3162559335345991374">আপনি যে Wi-Fiটি ব্যবহার করছেন সেটির জন্য অপনাকে এটির লগ ইন পৃষ্ঠাতে যেতে হতে পরে৷</translation>
-<translation id="3167968892399408617">ছদ্মবেশী ট্যাবগুলিতে আপনি যে পৃষ্ঠাগুলি দেখেন সেগুলি আপনার সব ছদ্মবেশী ট্যাব বন্ধ করে দেওয়ার পর ব্রাউজারের ইতিহাস, কুকি স্টোর বা সার্চ ইতিহাসে থাকবে না। আপনি ডাউনলোড করেছেন এমন ফাইল বা বুকমার্ক তৈরি করছেন এমন সবগুলি রেখে দেওয়া হবে।</translation>
+<translation id="3167968892399408617">ছদ্মবেশি ট্যাবগুলিতে আপনি যে পৃষ্ঠাগুলি দেখেন সেগুলি আপনার সব ছদ্মবেশি ট্যাব বন্ধ করে দেওয়ার পর ব্রাউজারের ইতিহাস, কুকি স্টোর বা সার্চ ইতিহাসে থাকবে না। আপনি ডাউনলোড করেছেন এমন ফাইল বা বুকমার্ক তৈরি করছেন এমন সবগুলি রেখে দেওয়া হবে।</translation>
<translation id="3169472444629675720">আবিষ্কার করুন</translation>
<translation id="3174168572213147020">দ্বীপ</translation>
<translation id="3176929007561373547">প্রক্সী সার্ভার কাজ করছে কি না, তা নিশ্চিত করতে আপনার প্রক্সী সেটিংস পরীক্ষা করুন
@@ -342,7 +339,6 @@
<translation id="3305707030755673451">আপনার ডেটা আপনার সিঙ্ক পাসফ্রেজ দিয়ে <ph name="TIME" /> এ এনক্রিপ্ট করা হয়েছে। সিঙ্ক শুরু করার জন্য এটি লিখুন।</translation>
<translation id="3320021301628644560">বিলিংয়ের ঠিকানা যোগ করুন</translation>
<translation id="3338095232262050444">সুরক্ষিত</translation>
-<translation id="3340978935015468852">সেটিংস</translation>
<translation id="3345135638360864351">এই সাইটটি অ্যাক্সেস করার জন্য আপনার অনুরোধ <ph name="NAME" /> এ পাঠানো যায়নি৷ অনুগ্রহ করে আবার চেষ্টা করুন৷</translation>
<translation id="3355823806454867987">প্রক্সি সেটিংস পরিবর্তন করুন...</translation>
<translation id="3361596688432910856">Chrome এ নিম্নলিখিত তথ্য <ph name="BEGIN_EMPHASIS" />সংরক্ষণ করা হয় না<ph name="END_EMPHASIS" />:
@@ -376,7 +372,6 @@
<translation id="3528171143076753409">সার্ভারের শংসাপত্র বিশ্বস্ত নয়৷</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{সিঙ্ক করা ডিভাইসে কমপক্ষে ১টি আইটেম}=1{১টি আইটেম (এবং সিঙ্ক করা ডিভাইসে আরও)}one{#টি আইটেম (এবং সিঙ্ক করা ডিভাইসে আরও)}other{#টি আইটেম (এবং সিঙ্ক করা ডিভাইসে আরও)}}</translation>
<translation id="3539171420378717834">এই ডিভাইসে কার্ডটির একটি প্রতিলিপি রাখুন</translation>
-<translation id="3542684924769048008">এর জন্য পাসওয়ার্ড ব্যবহার করুন:</translation>
<translation id="3549644494707163724">আপনার নিজস্ব সিঙ্ক পাসফ্রেজের মাধ্যমে সমস্ত সিঙ্ক হওয়া ডেটা এনক্রিপ্ট করুন</translation>
<translation id="3556433843310711081">আপনার পরিচালক আপনার হয়ে এটি অবরোধ মুক্ত করতে পারবে</translation>
<translation id="3566021033012934673">আপনার সংযোগ ব্যক্তিগত নয়</translation>
@@ -462,7 +457,6 @@
<translation id="4171400957073367226">খারাপ যাচাইকরণের স্বাক্ষর</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{আরও <ph name="ITEM_COUNT" />টি আইটেম}one{আরও <ph name="ITEM_COUNT" />টি আইটেম}other{আরও <ph name="ITEM_COUNT" />টি আইটেম}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome-এর নীতি অনুযায়ী আপনার <ph name="ORG_NAME" /> পাসওয়ার্ড বদলে ফেলা উচিত যদি আপনি সেটি অন্য কোনও সাইটে ব্যবহার করে থাকেন।</translation>
<translation id="4196861286325780578">&amp;সরানোর কাজটি আবার করুন</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ফায়ারওয়াল এবং অ্যান্টিভাইরাস কনফিগারেশন পরীক্ষা করে দেখুন<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">ক্র্যাশেস</translation>
@@ -491,7 +485,6 @@
<translation id="425582637250725228">আপনার করা পরিবর্তনগুলি সংরক্ষণ নাও করা হতে পারে।</translation>
<translation id="4258748452823770588">ত্রুটিপূর্ণ স্বাক্ষর</translation>
<translation id="4265872034478892965">আপনার প্রশাসক অনুমতি দিয়েছে</translation>
-<translation id="4269787794583293679">(কোনো ইউজারনেম নেই)</translation>
<translation id="4275830172053184480">আপনার ডিভাইস বন্ধ করে চালু করুন</translation>
<translation id="4277028893293644418">পাসওয়ার্ড রিসেট করুন</translation>
<translation id="4280429058323657511">, মেয়াদ শেষ <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -514,6 +507,7 @@
<translation id="4434045419905280838">পপ-আপ এবং রিডাইরেক্ট</translation>
<translation id="443673843213245140">প্রক্সির ব্যবহার অক্ষম করা হয়েছে কিন্তু কোনো স্পষ্ট প্রক্সি কনফিগারেশান নির্দিষ্ট করা হয়েছে৷</translation>
<translation id="445100540951337728">ডেবিট কার্ড গ্রহণ করা হয়</translation>
+<translation id="4472575034687746823">শুরু করুন</translation>
<translation id="4506176782989081258">যাচাইকরণের ত্রুটি: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">সিস্টেম প্রশাসকের সাথে যোগাযোগ করে দেখুন</translation>
<translation id="450710068430902550">প্রশাসকের সাথে ভাগ করছে</translation>
@@ -538,8 +532,8 @@
<translation id="4726672564094551039">নীতিগুলি পুনঃলোড করুন</translation>
<translation id="4728558894243024398">প্ল্যাটফর্ম</translation>
<translation id="4736825316280949806">Chromium পুনরায় চালু করুন</translation>
+<translation id="4742407542027196863">পাসওয়ার্ডগুলি পরিচালনা করুন…</translation>
<translation id="4744603770635761495">সম্পাদনযোগ্য পথ</translation>
-<translation id="4749685221585524849">শেষবার ব্যবহার করা হয়েছে <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">আপনার তথ্য (উদাহরণস্বরূপ, পাসওয়ার্ড বা ক্রেডিট কার্ড নম্বর) যখন এই সাইটে পাঠানো হয় তখন সেটি ব্যক্তিগত থাকে।</translation>
<translation id="4756388243121344051">&amp;ইতিহাস</translation>
<translation id="4758311279753947758">পরিচিতির তথ্য জুড়ুন</translation>
@@ -556,6 +550,7 @@
<translation id="4850886885716139402">দেখুন</translation>
<translation id="4854362297993841467">এই পদ্ধতিতে ডেলিভারি করা যাবে না। অন্য পদ্ধতি ব্যবহার করুন।</translation>
<translation id="4858792381671956233">এই সাইটটি দেখার জন্য উপযুক্ত কিনা তা আপনি আপনার পিতামাতাকে জিজ্ঞাসা করেছেন</translation>
+<translation id="4876305945144899064">কোনও ব্যবহারকারীর নাম নেই</translation>
<translation id="4880827082731008257">সার্চের ইতিহাস</translation>
<translation id="4881695831933465202">খুলুন</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -589,6 +584,7 @@
<translation id="5089810972385038852">রাজ্য</translation>
<translation id="5094747076828555589">এই সার্ভার প্রমাণ করতে পারেনি যে এটি <ph name="DOMAIN" />; এর নিরাপত্তা শংসাপত্রটি Chromium এর নিকট বিশ্বাসযোগ্য নয়। কোনো ভুল কনফিগারেশনের কারণে অথবা কোনো আক্রমণকারী আপনার সংযোগ মাঝপথে আটকে দিচ্ছে বলে এমনটা হতে পারে।</translation>
<translation id="5095208057601539847">প্রদেশ</translation>
+<translation id="5098332213681597508">এই নামটি আপনার Google অ্যাকাউন্ট থেকে।</translation>
<translation id="5115563688576182185">(৬৪-বিট)</translation>
<translation id="5121084798328133320">আপনি নিশ্চিত করার পর আপনার Google অর্থ প্রদান অ্যাকাউন্ট থেকে কার্ডের বিবরণ এই সাইটে শেয়ার করা হবে।</translation>
<translation id="5128122789703661928">এই নামের সেশনটি মুছে ফেলার জন্য সঠিক নয়।</translation>
@@ -629,9 +625,10 @@
<translation id="5332219387342487447">শিপিংয়ের মাধ্যম</translation>
<translation id="5355557959165512791">ওয়েবসাইটটির শংসাপত্র তুলে নেওয়ার কারণে আপনি এখন <ph name="SITE" /> এ যেতে পারবেন না। নেটওয়ার্ক ত্রুটি এবং আক্রমণ সাধারণত সাময়িকভাবে হয়, তাই এই পৃষ্ঠাটি সম্ভবত পরে কাজ করবে।</translation>
<translation id="536296301121032821">নীতি সেটিংস সংরক্ষণ করতে ব্যর্থ হয়েছে</translation>
+<translation id="5371425731340848620">কার্ড আপডেট করুন</translation>
<translation id="5377026284221673050">"আপনার ঘড়ি লেটে চলছে" অথবা "আপনার ঘড়ি ফাস্ট আছে" অথবা "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">এই সাইটের শংসাপত্র শৃঙ্খলে SHA-1 ব্যবহার করে স্বাক্ষর করা একটি শংসাপত্র রয়েছে।</translation>
-<translation id="5402410679244714488">মেয়াদোত্তীর্ণ: <ph name="EXPIRATION_DATE_ABBR" />, এক বছরেরও বেশি সময় পূর্বে সর্বশেষ ব্যবহার করা হয়েছে</translation>
+<translation id="5387961145478138773">আপনার পছন্দের Google অ্যাপগুলিতে দ্রুত অ্যাক্সেস পান</translation>
<translation id="540969355065856584">এই সার্ভার প্রমাণ করতে পারেনি যে এটি <ph name="DOMAIN" />; এর নিরাপত্তা শংসাপত্র এই সময়ে বৈধ নয়। কোনো ভুল কনফিগারেশনের কারণে অথবা কোনো আক্রমণকারী আপনার সংযোগ মাঝপথে আটকে দিচ্ছে বলে এমনটা হতে পারে।</translation>
<translation id="5421136146218899937">ব্রাউজিং ডেটা সাফ করুন...</translation>
<translation id="5430298929874300616">বুকমার্ক সরান</translation>
@@ -673,11 +670,13 @@
<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="5659593005791499971">ইমেল</translation>
+<translation id="5666899935841546222">আপনি এক জায়গায় আপনার সমস্ত কার্ড রাখতে চান?</translation>
<translation id="5675650730144413517">এই পৃষ্ঠাটি কাজ করছে না</translation>
<translation id="5685654322157854305">শিপিং ঠিকানা যোগ করুন</translation>
<translation id="5689199277474810259">JSON এ রপ্তানি করুন</translation>
<translation id="5689516760719285838">লোকেশন</translation>
<translation id="570530837424789914">পরিচালনা করুন...</translation>
+<translation id="57094364128775171">শক্তিশালী পাসওয়ার্ড সাজেস্ট করুন…</translation>
<translation id="5710435578057952990">এই ওয়েবসাইটির পরিচয় যাচাই করা হয় নি৷</translation>
<translation id="5719499550583120431">প্রিপেড কার্ড গ্রহণ করা হয়।</translation>
<translation id="5720705177508910913">বর্তমান ব্যবহারকারী</translation>
@@ -698,11 +697,9 @@
<translation id="5869405914158311789">এই সাইটটিতে পৌছানো যাচ্ছে না</translation>
<translation id="5869522115854928033">সংরক্ষিত পাসওয়ার্ড</translation>
<translation id="5893752035575986141">ক্রেডিট কার্ড গ্রহণ করা হয়।</translation>
-<translation id="5898382028489516745">Chromium-এর নীতি অনুযায়ী আপনার <ph name="ORG_NAME" /> পাসওয়ার্ড বদলে ফেলা উচিত যদি আপনি সেটি অন্য কোনও সাইটে ব্যবহার করে থাকেন।</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (সিঙ্ক হয়েছে)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{১টি ব্যবহৃত হচ্ছে}one{#টি ব্যবহৃত হচ্ছে}other{#টি ব্যবহৃত হচ্ছে}}</translation>
<translation id="5939518447894949180">রিসেট করুন</translation>
-<translation id="5959728338436674663">বিপজ্জনক অ্যাপ্লিকেশান ও সাইটগুলি শনাক্ত করতে Google এর কাছে কিছু<ph name="BEGIN_WHITEPAPER_LINK" /> সিস্টেম তথ্য ও পৃষ্ঠার সামগ্রী<ph name="END_WHITEPAPER_LINK" /> স্বয়ংক্রিয়ভাবে পাঠান। <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">পরিচিতির তথ্য সম্পাদনা করুন</translation>
<translation id="5967867314010545767">ইতিহাস থেকে সরান</translation>
<translation id="5975083100439434680">জুম কমান</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">পোস্টাল কোড</translation>
<translation id="6290238015253830360">আপনার প্রস্তাবিত নিবন্ধগুলি এখানে দেখা যাবে</translation>
<translation id="6305205051461490394"><ph name="URL" /> এ পৌঁছানো যাচ্ছে না</translation>
-<translation id="6319915415804115995">এক বছরেরও বেশি সময় পূর্বে সর্বশেষ ব্যবহার করা হয়েছে</translation>
<translation id="6321917430147971392">আপনার DNS সেটিংস পরীক্ষা করুন</translation>
<translation id="6328639280570009161">নেটওয়ার্ক পূর্বানুমান নিষ্ক্রিয় করার চেষ্টা করুন</translation>
<translation id="6328786501058569169">এই সাইটটি প্রতারণামূলক</translation>
<translation id="6337133576188860026"><ph name="SIZE" /> জায়গা খালি করে। পরের বার যখন দেখবেন তখন কিছু সাইট লোড হতে দেরি হতে পারে।</translation>
<translation id="6337534724793800597">নাম অনুসারে ফিল্টারগুলি বাছাই করুন</translation>
-<translation id="6342069812937806050">এখনই</translation>
<translation id="6355080345576803305">সর্বজনীন অধিবেশন ওভাররাইড</translation>
<translation id="6358450015545214790">এর অর্থ কী?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{আরও ১টি প্রস্তাব}one{অন্যান্য #টি প্রস্তাব}other{অন্যান্য #টি প্রস্তাব}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;মুছে ফেলাকে আবার করুন</translation>
<translation id="6534179046333460208">বাস্তবিক ওয়েব প্রস্তাবনাগুলি</translation>
<translation id="6550675742724504774">বিকল্পসমূহ</translation>
-<translation id="6556915248009097796">মেয়াদ শেষের তারিখ: <ph name="EXPIRATION_DATE_ABBR" />, শেষবার ব্যবহার করা হয়েছে <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">আপনার পরিচালক এখনও এটি অনুমোদন করেন নি</translation>
<translation id="6569060085658103619">আপনি একটি এক্সটেনশান পৃষ্ঠা দেখছেন</translation>
<translation id="6596325263575161958">এনক্রিপশন বিকল্পগুলি</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">আপনি <ph name="DOMAIN" />-এ পৌঁছানোর প্রচেষ্টা করেছেন, কিন্তু সার্ভারটি একটি দুর্বল অ্যালগারিদম স্বাক্ষর (যেমন SHA-1) ব্যবহার করে একটি স্বাক্ষরিত শংসাপত্র উপস্থাপন করেছে। এর অর্থ হল সার্ভার যে সুরক্ষা প্রমাণপত্রাদি উপস্থাপন করেছে তা জাল হতে পারে এবং সার্ভারটি আপনার প্রত্যাশিত সার্ভার নাও হতে পারে (হতে পারে আপনি একজন আক্রমণকারীর সাথে যোগাযোগ করছেন)।</translation>
<translation id="6831043979455480757">অনুবাদ</translation>
<translation id="6839929833149231406">এলাকা</translation>
+<translation id="6852204201400771460">আবার অ্যাপ লোড করতে চান?</translation>
+<translation id="6865412394715372076">এই কার্ডটি এখনই যাচাই করা যাবে না</translation>
<translation id="6874604403660855544">&amp;যোগ করাকে পুনরায় করুন</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">নীতি স্তর সমর্থিত নয়।</translation>
<translation id="6895330447102777224">আপনার কার্ডটি নিশ্চিত করা হয়েছে</translation>
<translation id="6897140037006041989">ব্যবহারকারী এজেন্ট</translation>
+<translation id="6903319715792422884">সিস্টেমের কিছু <ph name="BEGIN_WHITEPAPER_LINK" />তথ্য এবং পৃষ্ঠার কন্টেন্ট<ph name="END_WHITEPAPER_LINK" /> Google-কে পাঠানোর মাধ্যমে আপনি নিরাপদ ব্রাউজিং আরও ভাল করে তুলতে সহায়তা করতে পারেন। <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">ব্যবহারকারী:</translation>
+<translation id="6944692733090228304">আপনার <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />-এর নয় এমন একটি সাইটে আপনার পাসওয়ার্ড লিখেছেন। আপনার অ্যাকাউন্টের সুরক্ষার জন্য অন্যান্য অ্যাপ এবং সাইটে আপনার এই পাসওয়ার্ডটি ব্যবহার করবেন না।</translation>
<translation id="6945221475159498467">নির্বাচন</translation>
<translation id="6948701128805548767">পিকআপ এর পদ্ধতি এবং প্রয়োজনীয়তা দেখতে একটি ঠিকানা বেছে নিন</translation>
<translation id="6949872517221025916">পাসওয়ার্ড রিসেট করুন</translation>
+<translation id="6950684638814147129">JSON মান পার্স করার সময় সমস্যা: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">সার্ভারটির শংসাপত্রটি একটি জাল হিসাবে উপস্থিত হয়েছে৷</translation>
<translation id="6965382102122355670">ঠিক আছে</translation>
<translation id="6965978654500191972">ডিভাইস</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;&lt;strong&gt;প্রয়োগ করুন&lt;/strong&gt;-এ ক্লিক করে, &lt;strong&gt;ঠিক আছে&lt;/strong&gt;বোতামে ক্লিক করুন
&lt;li&gt;কম্পিউটার থেকে সফ্টওয়্যারটি স্থায়ীভাবে সরিয়ে দেওয়ার পদ্ধতি জানতে &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome সহায়তা কেন্দ্রে&lt;/a&gt; যান
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">পাসওয়ার্ডগুলি পরিচালনা করুন…</translation>
<translation id="7419106976560586862">প্রোফাইল পথ</translation>
<translation id="7437289804838430631">পরিচিতির তথ্য যোগ করুন</translation>
<translation id="7441627299479586546">ভুল বিষয় বিশিষ্ট নীতি</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790">এই সমস্যা সম্পর্কে <ph name="BEGIN_LINK" />আরও জানুন<ph name="END_LINK" />।</translation>
<translation id="7455133967321480974">বিশ্বব্যাপী ডিফল্ট ব্যবহার করুন (অবরোধ করুন)</translation>
<translation id="7460163899615895653">অন্যান্য ডিভাইসগুলি থেকে আপনার সাম্প্রতিক ট্যাবগুলি এখানে দেখা যাবে</translation>
-<translation id="7469372306589899959">কার্ড নিশ্চিত করা হচ্ছে</translation>
<translation id="7473891865547856676">না থাক</translation>
<translation id="7481312909269577407">ফরওয়ার্ড</translation>
<translation id="7485870689360869515">কোনো ডেটা পাওয়া যায়নি৷</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">নেটওয়ার্ক সংযোগে কোন সমস্যা হওয়ার কারণে অনুবাদ ব্যর্থ হয়েছে৷</translation>
<translation id="8311129316111205805">সেশন লোড করুন</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> এ অ্যাক্সেস অস্বীকার করা হয়েছে</translation>
+<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="8349305172487531364">বুকমার্কস দণ্ড</translation>
<translation id="8363502534493474904">বিমান মোড বন্ধ করে দেখুন</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> সাড়া দিতে খুবে বেশি সময় নিয়েছে।</translation>
<translation id="8503559462189395349">Chrome পাসওয়ার্ডগুলি</translation>
<translation id="8503813439785031346">ইউজারনেম</translation>
+<translation id="8508648098325802031">সার্চ আইকন</translation>
<translation id="8543181531796978784">আপনি <ph name="BEGIN_ERROR_LINK" />একটি সনাক্তকরণ সমস্যা প্রতিবেদন করতে পারেন<ph name="END_ERROR_LINK" /> অথবা, আপনি আপনার নিরাপত্তা ঝুঁকি বুঝতে পারলে, <ph name="BEGIN_LINK" />এই অনিরাপদ সাইটটি ঘুরে দেখুন<ph name="END_LINK" />।</translation>
<translation id="8543556556237226809">প্রশ্ন? আপনার প্রোফাইল যে তত্ত্বাবধান করে তার সাথে যোগাযোগ করুন।</translation>
<translation id="8553075262323480129">পৃষ্ঠার ভাষা নির্ধারণ না করতে পারার কারণে অনুবাদ ব্যর্থ হয়েছে৷</translation>
<translation id="8557066899867184262">আপনার কার্ডের পিছনে সিভিসি ছবিটি আছে।</translation>
<translation id="8559762987265718583"><ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> এ একটি ব্যক্তিগত সংযোগ স্থাপন করা যায়নি কারণ আপনার ডিভাইসের তারিখ এবং সময় (<ph name="DATE_AND_TIME" />) সঠিক নয়৷</translation>
+<translation id="8564985650692024650">Chromium-এর নীতি অনুযায়ী আপনার <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> পাসওয়ার্ড বদলে ফেলা উচিত যদি আপনি সেটি অন্য কোনও সাইটে ব্যবহার করে থাকেন।</translation>
<translation id="8571890674111243710">পৃষ্ঠাটি<ph name="LANGUAGE" />তে অনুবাদ করুন...</translation>
<translation id="858637041960032120">ফোননম্বর জুড়ুন</translation>
<translation id="859285277496340001">শংসাপত্রটি প্রত্যাহার করা হয়েছে কিনা তা যাচাই করতে শংসাপত্রটি কোনও কারিগরীকে নির্দিষ্ট করে না৷</translation>
+<translation id="860043288473659153">কার্ডধারকের নাম</translation>
<translation id="8620436878122366504">আপনার পিতামাতা এখনও এটি অনুমোদন করেন নি</translation>
<translation id="8625384913736129811">এই ডিভাইসে এই কার্ডটি সেভ করুন</translation>
-<translation id="8639963783467694461">স্বতঃপূর্ণ সেটিংস</translation>
+<translation id="8663226718884576429">অর্ডারের সারসংক্ষেপ, <ph name="TOTAL_LABEL" />, আরও বিবরণ</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, উত্তর, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206"><ph name="DOMAIN" />-এ আপনার কানেকশন এনক্রিপ্ট হয় নি৷</translation>
<translation id="8718314106902482036">পেমেন্ট করা যায়নি</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, সার্চ সাজেশন</translation>
<translation id="8725066075913043281">আবার চেষ্টা করুন</translation>
<translation id="8728672262656704056">আপনি গুপ্ত মোডে চলে গেছেন</translation>
<translation id="8730621377337864115">হয়ে গেছে</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">বুকমার্কস</translation>
<translation id="883848425547221593">অন্যান্য বুকমার্ক</translation>
<translation id="884264119367021077">শিপিং ঠিকানা</translation>
+<translation id="8846319957959474018">বুকমার্ক-এর মাধ্যমে সহজেই অ্যাপ খুলুন</translation>
<translation id="884923133447025588">কোন প্রত্যাহার নির্মাণকৌশল পাওয়া যায় নি৷</translation>
<translation id="885730110891505394">Google এর সাথে ভাগ করছে</translation>
+<translation id="8858065207712248076">Chrome-এর নীতি অনুযায়ী আপনার <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> পাসওয়ার্ড বদলে ফেলা উচিত যদি আপনি সেটি অন্য কোনও সাইটে ব্যবহার করে থাকেন।</translation>
<translation id="8866481888320382733">নীতি সেটিংস বিশ্লেষণ করার সময় ত্রুটি</translation>
<translation id="8870413625673593573">সম্প্রতি বন্ধ হয়েছে</translation>
<translation id="8874824191258364635">একটি সঠিক কার্ড নম্বর লিখুন</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> সাধারণত আপনার তথ্য সুরক্ষিত রাখতে এনক্রিপশান ব্যবহার করে। এইবার যখন Chromium <ph name="SITE" /> এর সাথে সংযোগ স্থাপন করার চেষ্টা করেছে, তখন ওয়েবসাইটটি অস্বাভাবিক এবং ভুল শংসাপত্র পাঠিয়েছে। হয় একজন আক্রমণকারী <ph name="SITE" /> হওয়ার ভান করছে, অথবা একটি ওয়াই-ফাই প্রবেশ করুন স্ক্রীণ সংযোগকে বাধাপ্রদান করেছে। আপনার তথ্য এখনো নিরাপদ আছে কারণ কোনো ডেটা আদানপ্রদানের আগেই Chromium সংযোগটিকে বন্ধ করে দিয়েছে।</translation>
<translation id="9106062320799175032">বিলিংয়ের ঠিকানা যোগ করুন</translation>
<translation id="910908805481542201">এটি ঠিক করতে আমাকে সহায়তা করুন</translation>
+<translation id="9114524666733003316">কার্ড নিশ্চিত করা হচ্ছে...</translation>
<translation id="9128870381267983090">নেটওয়ার্কে সংযোগ করুন</translation>
<translation id="9137013805542155359">প্রকৃত রূপ দেখান</translation>
<translation id="9137248913990643158">এই অ্যাপ্লিকেশানটি ব্যবহার করার আগে অনুগ্রহ করে শুরু করুন এবং Chrome এ প্রবেশ করুন।</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">আপনি কোনও পেমেন্ট পদ্ধতি সেভ করেছেন কিনা তা সাইটগুলিকে যাচাই করতে দিন</translation>
<translation id="9169664750068251925">এই সাইটে সর্বদা অবরোধ করুন</translation>
<translation id="9170848237812810038">&amp;পূর্বাবস্থায় ফিরুন</translation>
+<translation id="9171296965991013597">অ্যাপ থেকে বেরিয়ে আসতে চান?</translation>
<translation id="917450738466192189">সার্ভারের শংসাপত্র অকার্যকর৷</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> একটি অসমর্থিত প্রোটোকল ব্যবহার করে।</translation>
<translation id="9205078245616868884">আপনার ডেটা আপনার সিঙ্ক পাসফ্রেজ দিয়ে এনক্রিপ্ট করা হয়েছে। সিঙ্ক শুরু করার জন্য এটি লিখুন।</translation>
diff --git a/chromium/components/strings/components_strings_ca.xtb b/chromium/components/strings/components_strings_ca.xtb
index f59b00f58ff..0dbf8f0efa5 100644
--- a/chromium/components/strings/components_strings_ca.xtb
+++ b/chromium/components/strings/components_strings_ca.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Amaga el valor</translation>
<translation id="1228893227497259893">Identificador d'entitat incorrecte</translation>
<translation id="1232569758102978740">Sense títol</translation>
+<translation id="1250759482327835220">Perquè la propera vegada puguis pagar més ràpidament, desa la targeta, el nom i l'adreça de facturació al Compte de Google.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (informació sincronitzada)</translation>
<translation id="1256368399071562588">&lt;p&gt;Si proveu de visitar un lloc web i no s'obre, en primer lloc mireu de resoldre el problema amb aquests passos:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Canvieu la data i l'hora a la secció &lt;strong&gt;General&lt;/strong&gt; de l'aplicació &lt;strong&gt;Configuració&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">S'ha produït un error en mostrar aquesta pàgina web.</translation>
-<translation id="1590457302292452960">Genera una contrasenya segura...</translation>
<translation id="1592005682883173041">Accés a les dades locals</translation>
<translation id="1594030484168838125">Tria</translation>
<translation id="1620510694547887537">Càmera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Proveu de contactar amb l'administrador del sistema.</translation>
<translation id="1740951997222943430">Introdueix un mes de caducitat vàlid</translation>
+<translation id="1743520634839655729">Perquè la propera vegada puguis pagar més ràpidament, desa la targeta, el nom i l'adreça de facturació al Compte de Google i en aquest dispositiu.</translation>
<translation id="17513872634828108">Pestanyes obertes</translation>
<translation id="1753706481035618306">Número de pàgina</translation>
<translation id="1763864636252898013">Aquest servidor no ha pogut comprovar que sigui <ph name="DOMAIN" /> perquè el sistema operatiu del vostre dispositiu 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>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Les pestanyes obertes es mostren aquí</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Nom del titular de la targeta</translation>
-<translation id="1806541873155184440">Afegida el dia <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Sol·licitud o paràmetres de la sol·licitud no vàlids</translation>
<translation id="1826516787628120939">S'està comprovant</translation>
<translation id="1834321415901700177">Aquest lloc conté programes perjudicials</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 suggeriment}other{# suggeriments}}</translation>
<translation id="2079545284768500474">Desfés</translation>
<translation id="20817612488360358">S'ha definit la configuració del servidor intermediari del sistema perquè es pugui utilitzar, però també s'ha especificat una configuració del servidor intermediari explícita.</translation>
-<translation id="2084558088529668945">Has introduït la contrasenya en un lloc web que no està gestionat per <ph name="ORG_NAME" />. Per protegir el teu compte, no facis servir la mateixa contrasenya en altres aplicacions ni llocs web.</translation>
<translation id="2091887806945687916">So</translation>
<translation id="2094505752054353250">Els dominis no coincideixen.</translation>
<translation id="2096368010154057602">Departament</translation>
+<translation id="2102134110707549001">Suggereix una contrasenya segura…</translation>
<translation id="2108755909498034140">Reinicia l'ordinador</translation>
<translation id="2113977810652731515">Targeta</translation>
<translation id="2114841414352855701">S'ha ignorat perquè <ph name="POLICY_NAME" /> l'ha substituït.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Error d'HTTP</translation>
<translation id="2270484714375784793">Número de telèfon</translation>
<translation id="2292556288342944218">El vostre accés a Internet està bloquejat</translation>
-<translation id="230155334948463882">És una targeta nova?</translation>
<translation id="2316887270356262533">Allibera menys d'1 MB. És possible que alguns llocs web es carreguin més a poc a poc la propera vegada que els visitis.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> requereix un nom d'usuari i una contrasenya.</translation>
<translation id="2317583587496011522">S'accepten targetes de dèbit.</translation>
@@ -222,7 +221,7 @@
<translation id="2495083838625180221">Analitzador JSON</translation>
<translation id="2495093607237746763">Si s'activa aquesta casella, Chromium desa una còpia de la vostra targeta al dispositiu per agilitzar l'emplenament de formularis.</translation>
<translation id="2498091847651709837">Escaneja una targeta nova</translation>
-<translation id="2501278716633472235">Enrere</translation>
+<translation id="2501278716633472235">Torna</translation>
<translation id="2503184589641749290">Targetes de dèbit i de prepagament acceptades</translation>
<translation id="2515629240566999685">Comprova el senyal a la teva zona.</translation>
<translation id="2524461107774643265">Afegeix més informació</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Targetes acceptades</translation>
<translation id="2702801445560668637">Llista de lectura</translation>
<translation id="2704283930420550640">El valor no coincideix amb el format.</translation>
-<translation id="2704951214193499422">En aquest moment Chromium no ha pogut confirmar la teva targeta. Torna-ho a provar més tard.</translation>
<translation id="2705137772291741111">La còpia desada (a la memòria cau) d'aquest lloc no s'ha pogut llegir.</translation>
<translation id="2709516037105925701">Emplenament autom.</translation>
<translation id="2710942282213947212">L'ordinador conté programari que impedeix que Chromium es connecti de manera segura al web</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Mètode d'enviament</translation>
<translation id="277499241957683684">Falta el registre del dispositiu</translation>
<translation id="2781030394888168909">Exporta a MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">S'ha restablert la connexió.</translation>
<translation id="2788784517760473862">Targetes de crèdit acceptades</translation>
<translation id="2794233252405721443">Lloc bloquejat</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Podeu desactivar qualsevol servidor intermediari configurat per a una connexió des de la pàgina de configuració.</translation>
<translation id="2955913368246107853">Tanca la barra de cerca</translation>
<translation id="2958431318199492670">La configuració de la xarxa no compleix l'estàndard ONC. Pot ser que algunes opcions de configuració no s'hagin importat.</translation>
-<translation id="2966678944701946121">Data de caducitat: <ph name="EXPIRATION_DATE_ABBR" />; afegida el dia <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Per establir una connexió segura, heu de tenir el rellotge ben configurat, ja que els certificats que els llocs web fan servir per identificar-se només són vàlids durant períodes de temps concrets. Com que el rellotge del dispositiu no està ben configurat, Google Chrome no pot verificar aquests certificats.</translation>
<translation id="2972581237482394796">&amp;Refés</translation>
<translation id="2977665033722899841">S'ha seleccionat <ph name="ROW_NAME" />. <ph name="ROW_CONTENT" /></translation>
@@ -299,9 +297,8 @@
<translation id="3010559122411665027">Entrada de llista "<ph name="ENTRY_INDEX" />": <ph name="ERROR" /></translation>
<translation id="301521992641321250">Bloquejada automàticament</translation>
<translation id="3024663005179499861">Tipus de política incorrecte</translation>
-<translation id="3037605927509011580">Oh, no!</translation>
+<translation id="3037605927509011580">Vaja!</translation>
<translation id="3041612393474885105">Informació del certificat</translation>
-<translation id="3063697135517575841">Chrome no ha pogut confirmar la teva targeta. Torna-ho a provar més tard.</translation>
<translation id="3064966200440839136">Per pagar amb una aplicació externa sortiràs del mode d'incògnit. Vols continuar?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Cap}=1{1 contrasenya}other{# contrasenyes}}</translation>
<translation id="3096100844101284527">Afegeix l'adreça de recollida</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Les vostres dades es van encriptar el dia <ph name="TIME" /> amb la vostra frase de contrasenya de sincronització. Introduïu-la per començar la sincronització.</translation>
<translation id="3320021301628644560">Afegeix una adreça de facturació</translation>
<translation id="3338095232262050444">Segur</translation>
-<translation id="3340978935015468852">configuració</translation>
<translation id="3345135638360864351">La sol·licitud per accedir al lloc no s'ha pogut enviar a <ph name="NAME" />. Torneu-ho a provar.</translation>
<translation id="3355823806454867987">Canvia la configuració del servidor intermediari...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />no desarà<ph name="END_EMPHASIS" /> la informació següent:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">El certificat del servidor no és de confiança.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Almenys 1 element als dispositius sincronitzats}=1{1 element (i altres elements als dispositius sincronitzats)}other{# elements (i altres elements als dispositius sincronitzats)}}</translation>
<translation id="3539171420378717834">Desa una còpia d'aquesta targeta al dispositiu</translation>
-<translation id="3542684924769048008">Utilitzar la contrasenya per a:</translation>
<translation id="3549644494707163724">Encripta totes les dades sincronitzades amb la teva frase de contrasenya de sincronització</translation>
<translation id="3556433843310711081">El teu gestor te'l pot desbloquejar</translation>
<translation id="3566021033012934673">La connexió no és privada</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">La signatura de verificació és incorrecta</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> element més}other{<ph name="ITEM_COUNT" /> elements més}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome et recomana que restableixis la contrasenya per a <ph name="ORG_NAME" /> si l'has fet servir en altres llocs web.</translation>
<translation id="4196861286325780578">&amp;Refés el moviment</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Comproveu la configuració del tallafoc i de l'antivirus<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Errors</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">És possible que els canvis que heu fet no es desin.</translation>
<translation id="4258748452823770588">Signatura errònia</translation>
<translation id="4265872034478892965">Permès per l'administrador</translation>
-<translation id="4269787794583293679">(Sense nom d'usuari)</translation>
<translation id="4275830172053184480">Reinici del dispositiu</translation>
<translation id="4277028893293644418">Restableix la contrasenya</translation>
<translation id="4280429058323657511">, caduca el dia <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Finest. emergents i redireccions</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="445100540951337728">Targetes de dèbit acceptades</translation>
+<translation id="4472575034687746823">Comença</translation>
<translation id="4506176782989081258">Error de validació: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Contacteu amb l'administrador del sistema</translation>
<translation id="450710068430902550">Comparteix informació amb l'administrador</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Torna a carregar les polítiques</translation>
<translation id="4728558894243024398">Plataforma</translation>
<translation id="4736825316280949806">Reinicia Chromium</translation>
+<translation id="4742407542027196863">Gestiona les contrasenyes…</translation>
<translation id="4744603770635761495">Camí executable</translation>
-<translation id="4749685221585524849">Última utilització: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">La teva informació (com ara les contrasenyes o els números de targeta de crèdit) es considera privada quan s'envia a aquest lloc.</translation>
<translation id="4756388243121344051">&amp;Historial</translation>
<translation id="4758311279753947758">Afegiu informació de contacte</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Mostra</translation>
<translation id="4854362297993841467">Aquest mètode d'entrega no està disponible. Prova'n un altre.</translation>
<translation id="4858792381671956233">Has demanat permís als teus pares per visitar aquest lloc</translation>
+<translation id="4876305945144899064">Sense nom d'usuari</translation>
<translation id="4880827082731008257">Cerca a l'historial</translation>
<translation id="4881695831933465202">Obre</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -580,7 +575,7 @@
<translation id="5039804452771397117">Permet</translation>
<translation id="5040262127954254034">Privadesa</translation>
<translation id="5045550434625856497">Contrasenya incorrecta</translation>
-<translation id="5056549851600133418">Articles que us poden interessar</translation>
+<translation id="5056549851600133418">Articles que et poden interessar</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Comproveu l'adreça del servidor intermediari<ph name="END_LINK" /></translation>
<translation id="5086888986931078152">Pot ser que perdis l'accés al contingut protegit d'alguns llocs web.</translation>
<translation id="5087286274860437796">En aquest moment el certificat del servidor no és vàlid.</translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Estat</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="5098332213681597508">Aquest nom prové del teu Compte de Google.</translation>
<translation id="5115563688576182185">(64 bits)</translation>
<translation id="5121084798328133320">Un cop confirmada, els detalls de la targeta del teu compte de pagaments de Google es compartiran amb aquest lloc web.</translation>
<translation id="5128122789703661928">La sessió amb aquest nom no es pot suprimir.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Mètode d'enviament</translation>
<translation id="5355557959165512791">En aquest moment no pots visitar <ph name="SITE" /> perquè se li ha revocat el certificat. Els atacs i els errors de xarxa acostumen a ser temporals, o sigui que probablement la pàgina funcionarà més endavant.</translation>
<translation id="536296301121032821">No s'ha pogut emmagatzemar la configuració de la política</translation>
+<translation id="5371425731340848620">Actualitza la targeta</translation>
<translation id="5377026284221673050">"El rellotge està endarrerit", "El rellotge està avançat" o "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">La cadena de certificats d'aquest lloc conté un certificat que s'ha signat amb SHA-1.</translation>
-<translation id="5402410679244714488">Data de caducitat: <ph name="EXPIRATION_DATE_ABBR" />, última utilització fa més d'un any</translation>
+<translation id="5387961145478138773">Accedeix ràpidament a les teves aplicacions de Google preferides</translation>
<translation id="540969355065856584">Aquest servidor no ha pogut demostrar que sigui <ph name="DOMAIN" /> perquè el seu certificat de seguretat no és vàlid en aquest moment. Això pot ser a causa d'una configuració incorrecta o d'un atacant que intercepta la connexió.</translation>
<translation id="5421136146218899937">Esborra les dades de navegació...</translation>
<translation id="5430298929874300616">Suprimeix l'adreça d'interès</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">Correu electrònic</translation>
+<translation id="5666899935841546222">Vols tenir totes les targetes en un sol lloc?</translation>
<translation id="5675650730144413517">Aquesta pàgina no funciona</translation>
<translation id="5685654322157854305">Afegeix una adreça d'enviament</translation>
<translation id="5689199277474810259">Exporta a JSON</translation>
<translation id="5689516760719285838">Ubicació</translation>
<translation id="570530837424789914">Gestiona...</translation>
+<translation id="57094364128775171">Suggereix una contrasenya segura…</translation>
<translation id="5710435578057952990">La identitat d'aquest lloc web no ha estat verificada.</translation>
<translation id="5719499550583120431">S'accepten targetes de prepagament.</translation>
<translation id="5720705177508910913">Usuari actual</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">No es pot accedir a aquest lloc</translation>
<translation id="5869522115854928033">Contrasenyes desades</translation>
<translation id="5893752035575986141">S'accepten targetes de crèdit.</translation>
-<translation id="5898382028489516745">Chromium et recomana que restableixis la contrasenya per a <ph name="ORG_NAME" /> si l'has fet servir en altres llocs web.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (informació sincronitzada)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 en ús}other{# en ús}}</translation>
<translation id="5939518447894949180">Restableix</translation>
-<translation id="5959728338436674663">Envia automàticament algunes <ph name="BEGIN_WHITEPAPER_LINK" />dades del sistema i contingut de les pàgines<ph name="END_WHITEPAPER_LINK" /> a Google per ajudar a detectar les aplicacions i els llocs web perillosos. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Edita la informació de contacte</translation>
<translation id="5967867314010545767">Elimina de l'historial</translation>
<translation id="5975083100439434680">Redueix</translation>
@@ -711,7 +708,7 @@
<translation id="6008256403891681546">JCB</translation>
<translation id="6015796118275082299">Any</translation>
<translation id="6016158022840135739">{COUNT,plural, =1{Pàgina 1}other{Pàgina #}}</translation>
-<translation id="6017850046339264347">Pot ser que els atacants que es troben a <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> instal·lin aplicacions enganyoses que es facin passar per qui no són o recopilin dades que podrien utilitzar-se per fer un seguiment de la teva activitat. <ph name="BEGIN_LEARN_MORE_LINK" />Més informació<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="6017850046339264347">Pot ser que els atacants que es troben a <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> instal·lin aplicacions enganyoses que es facin passar per qui no són o recullin dades que podrien utilitzar-se per fer un seguiment de la teva activitat. <ph name="BEGIN_LEARN_MORE_LINK" />Més informació<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6025416945513303461"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /> (informació sincronitzada)</translation>
<translation id="6027201098523975773">Escriu un nom</translation>
<translation id="6039846035001940113">Si el problema continua, contacta amb el propietari del lloc web.</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Codi postal</translation>
<translation id="6290238015253830360">Els suggeriments d'articles es mostren aquí</translation>
<translation id="6305205051461490394">No es pot accedir a <ph name="URL" />.</translation>
-<translation id="6319915415804115995">Última utilització fa més d'un any</translation>
<translation id="6321917430147971392">Reviseu la configuració de DNS</translation>
<translation id="6328639280570009161">Proveu de desactivar la predicció de xarxa</translation>
<translation id="6328786501058569169">Aquest lloc web és enganyós</translation>
<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="6342069812937806050">Ara mateix</translation>
<translation id="6355080345576803305">Substitueix en sessions públiques</translation>
<translation id="6358450015545214790">Què vol dir tot això?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 suggeriment més}other{# suggeriments més}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Refés la supressió</translation>
<translation id="6534179046333460208">Suggeriments del Web físic</translation>
<translation id="6550675742724504774">Opcions</translation>
-<translation id="6556915248009097796">Data de caducitat: <ph name="EXPIRATION_DATE_ABBR" />; última utilització: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">El teu gestor encara no ho ha aprovat</translation>
<translation id="6569060085658103619">Estàs consultant la pàgina d'una extensió</translation>
<translation id="6596325263575161958">Opcions d'encriptació</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Has provat d'accedir a <ph name="DOMAIN" />, però el servidor ha presentat un certificat signat mitjançant un algoritme de signatura dèbil (com ara SHA-1). Això indica que les credencials de seguretat que ha presentat el servidor podrien haver estat falsificades i que pot ser que el servidor no sigui el que esperaves (és possible que t'estiguis comunicant amb un atacant).</translation>
<translation id="6831043979455480757">Tradueix</translation>
<translation id="6839929833149231406">Àrea</translation>
+<translation id="6852204201400771460">Vols tornar a carregar l'aplicació?</translation>
+<translation id="6865412394715372076">En aquests moments no es pot verificar la targeta</translation>
<translation id="6874604403660855544">&amp;Refés l'addició</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> (<ph name="SHORT_URL" />)</translation>
<translation id="6891596781022320156">No s'admet el nivell de la política.</translation>
<translation id="6895330447102777224">La teva targeta s'ha confirmat</translation>
<translation id="6897140037006041989">Agent d'usuari</translation>
+<translation id="6903319715792422884">Per ajudar a millorar Navegació segura, envia a Google <ph name="BEGIN_WHITEPAPER_LINK" />algunes dades del sistema i contingut de les pàgines<ph name="END_WHITEPAPER_LINK" />. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Usuari:</translation>
+<translation id="6944692733090228304">Has introduït la contrasenya en un lloc web que no està gestionat per <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Per protegir el teu compte, no facis servir la mateixa contrasenya en altres aplicacions ni llocs web.</translation>
<translation id="6945221475159498467">Selecciona</translation>
<translation id="6948701128805548767">Per veure els mètodes i els requisits de recollida, selecciona una adreça</translation>
<translation id="6949872517221025916">Restableix la contrasenya</translation>
+<translation id="6950684638814147129">S'ha produït un error en analitzar el valor JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Sembla que el certificat del servidor és una falsificació.</translation>
<translation id="6965382102122355670">D'acord</translation>
<translation id="6965978654500191972">Dispositiu</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Feu clic a &lt;strong&gt;Aplica&lt;/strong&gt; i, a continuació, a &lt;strong&gt;D'acord&lt;/strong&gt;.
&lt;li&gt;Visiteu el &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Centre d'ajuda de Chrome&lt;/a&gt; per obtenir informació sobre com podeu suprimir permanentment el programari de l'ordinador.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Gestiona les contrasenyes…</translation>
<translation id="7419106976560586862">Camí del perfil</translation>
<translation id="7437289804838430631">Afegeix informació de contacte</translation>
<translation id="7441627299479586546">Usuari de la política incorrecte</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Obtenir més informació<ph name="END_LINK" /> sobre aquest problema</translation>
<translation id="7455133967321480974">Utilitza l'opció predeterminada global (Bloqueja)</translation>
<translation id="7460163899615895653">Les teves pestanyes recents d'altres dispositius es mostraran aquí</translation>
-<translation id="7469372306589899959">S'està confirmant la targeta</translation>
<translation id="7473891865547856676">No, gràcies</translation>
<translation id="7481312909269577407">Endavant</translation>
<translation id="7485870689360869515">No s'han trobat dades.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">No s'ha pogut executar la traducció a causa d'un problema amb la connexió de xarxa.</translation>
<translation id="8311129316111205805">Carrega la sessió</translation>
<translation id="8332188693563227489">S'ha rebutjat l'accés a <ph name="HOST_NAME" /></translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Si enteneu el risc que suposa per a la vostra seguretat, podeu <ph name="BEGIN_LINK" />visitar aquest lloc<ph name="END_LINK" /> abans que no s'hagin suprimit els programes perjudicials.</translation>
<translation id="8349305172487531364">Barra d'adreces d'interès</translation>
<translation id="8363502534493474904">Desactiva el mode d'avió.</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ha tardat massa a respondre.</translation>
<translation id="8503559462189395349">Contrasenyes de Chrome</translation>
<translation id="8503813439785031346">Nom d'usuari</translation>
+<translation id="8508648098325802031">Icona de la cerca</translation>
<translation id="8543181531796978784">Podeu <ph name="BEGIN_ERROR_LINK" />informar d'un problema de detecció<ph name="END_ERROR_LINK" /> o, si enteneu els riscos que això comporta per a la vostra seguretat, <ph name="BEGIN_LINK" />visiteu aquest lloc no segur<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Tens preguntes? Contacta amb la persona que et supervisa el perfil.</translation>
<translation id="8553075262323480129">S'ha produït un error en fer la traducció perquè no s'ha pogut determinar l'idioma de la pàgina.</translation>
<translation id="8557066899867184262">El CVC es troba a la part posterior de la targeta.</translation>
<translation id="8559762987265718583">No es pot establir una connexió privada amb <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> perquè la data i l'hora (<ph name="DATE_AND_TIME" />) del dispositiu són incorrectes.</translation>
+<translation id="8564985650692024650">Chromium et recomana que restableixis la contrasenya per a <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> si l'has fet servir en altres llocs web.</translation>
<translation id="8571890674111243710">S'està traduint la pàgina a l'idioma <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Afegeix un número de telèfon</translation>
<translation id="859285277496340001">El certificat no especifica un mecanisme per comprovar si s'ha revocat.</translation>
+<translation id="860043288473659153">Nom del titular de la targeta</translation>
<translation id="8620436878122366504">Els teus pares encara no ho han aprovat</translation>
<translation id="8625384913736129811">Desa aquesta targeta al dispositiu</translation>
-<translation id="8639963783467694461">Configuració d'Emplenament automàtic</translation>
+<translation id="8663226718884576429">Resum de la comanda, <ph name="TOTAL_LABEL" />, més detalls</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, resposta, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">La teva connexió a <ph name="DOMAIN" /> no està xifrada.</translation>
<translation id="8718314106902482036">No s'ha completat el pagament</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, suggeriment de cerca</translation>
<translation id="8725066075913043281">Torna-ho a provar</translation>
<translation id="8728672262656704056">Has passat al mode d'incògnit</translation>
<translation id="8730621377337864115">Fet</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Adreces d'interès</translation>
<translation id="883848425547221593">Altres adreces d'interès</translation>
<translation id="884264119367021077">Adreça d’enviament</translation>
+<translation id="8846319957959474018">Obre aplicacions fàcilment amb les adreces d'interès</translation>
<translation id="884923133447025588">No s'ha trobat cap mecanisme de revocació.</translation>
<translation id="885730110891505394">Comparteix informació amb Google</translation>
+<translation id="8858065207712248076">Chrome et recomana que restableixis la contrasenya per a <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> si l'has fet servir en altres llocs web.</translation>
<translation id="8866481888320382733">S'ha produït un error en analitzar la configuració de la política</translation>
<translation id="8870413625673593573">Tancades recentment</translation>
<translation id="8874824191258364635">Introdueix un número de targeta vàlid</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> utilitza normalment l'encriptació per protegir la vostra informació. En aquesta ocasió, quan Chromium ha provat de connectar-se a <ph name="SITE" />, el lloc web ha enviat credencials poc comunes i incorrectes. Pot ser que un atacant estigui provant de fer-se passar per <ph name="SITE" /> o que una pantalla d'inici de sessió a la xarxa Wi-Fi hagi interromput la connexió. En qualsevol cas, la vostra informació continua estant segura, perquè Chromium ha aturat la connexió abans no s'intercanviés cap dada.</translation>
<translation id="9106062320799175032">Afegeix una adreça de facturació</translation>
<translation id="910908805481542201">Ajuda'm a solucionar-ho</translation>
+<translation id="9114524666733003316">S'està confirmant la targeta...</translation>
<translation id="9128870381267983090">Connecta't a la xarxa</translation>
<translation id="9137013805542155359">Mostra l'original</translation>
<translation id="9137248913990643158">Obre Chrome i inicia-hi la sessió abans d'utilitzar aquesta aplicació.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">Permet que els llocs web comprovin si tens formes de pagament desades</translation>
<translation id="9169664750068251925">Bloqueja sempre en aquest lloc</translation>
<translation id="9170848237812810038">&amp;Desfés</translation>
+<translation id="9171296965991013597">Vols sortir de l'aplicació?</translation>
<translation id="917450738466192189">El certificat del servidor no és vàlid.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> fa servir un protocol no admès.</translation>
<translation id="9205078245616868884">Les vostres dades estan encriptades amb la vostra frase de contrasenya de sincronització. Introduïu-la per començar la sincronització.</translation>
diff --git a/chromium/components/strings/components_strings_cs.xtb b/chromium/components/strings/components_strings_cs.xtb
index 43c93caa826..560aa461402 100644
--- a/chromium/components/strings/components_strings_cs.xtb
+++ b/chromium/components/strings/components_strings_cs.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Skrýt hodnotu</translation>
<translation id="1228893227497259893">Nesprávný identifikátor subjektu</translation>
<translation id="1232569758102978740">Bez názvu</translation>
+<translation id="1250759482327835220">Abyste příště mohli zaplatit rychleji, uložte si kartu, jméno a fakturační adresu do účtu Google.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (synchronizováno)</translation>
<translation id="1256368399071562588">&lt;p&gt;Pokud se pokoušíte navštívit konkrétní web, ale ten se neotevírá, nejdříve chybu zkuste opravit provedením těchto kroků:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Datum a čas upravte v aplikaci &lt;strong&gt;Nastavení&lt;/strong&gt; v sekci &lt;strong&gt;Obecné&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Při zobrazování této webové stránky došlo k chybě.</translation>
-<translation id="1590457302292452960">Vygenerovat silné heslo...</translation>
<translation id="1592005682883173041">Přístup k místním datům</translation>
<translation id="1594030484168838125">Zvolit</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Zkuste kontaktovat administrátora systému.</translation>
<translation id="1740951997222943430">Zadejte platný měsíc vypršení platnosti</translation>
+<translation id="1743520634839655729">Abyste příště mohli zaplatit rychleji, uložte si kartu, jméno a fakturační adresu do účtu Google a do tohoto zařízení.</translation>
<translation id="17513872634828108">Otevřené karty</translation>
<translation id="1753706481035618306">Číslo stránky</translation>
<translation id="1763864636252898013">Server nedokázal prokázat, že patří doméně <ph name="DOMAIN" />. Operační systém vašeho zařízení nedůvěřuje jeho bezpečnostnímu certifikátu. Může to být způsobeno nesprávnou konfigurací nebo tím, že vaše připojení zachytává útočník.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Zde se zobrazí otevřené karty</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Jméno držitele karty</translation>
-<translation id="1806541873155184440">Přidáno <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Neplatný požadavek nebo parametry požadavku</translation>
<translation id="1826516787628120939">Probíhá kontrola</translation>
<translation id="1834321415901700177">Tento web obsahuje škodlivé programy</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 návrh}few{# návrhy}many{# návrhu}other{# návrhů}}</translation>
<translation id="2079545284768500474" />
<translation id="20817612488360358">Jako aktivní jsou nakonfigurována systémová nastavení proxy serveru, je však určena i explicitní konfigurace proxy serveru.</translation>
-<translation id="2084558088529668945">Zadali jste heslo na webu, který není spravován organizací <ph name="ORG_NAME" />. Aby byl váš účet chráněn, nepoužívejte jeho heslo u jiných aplikací a webů.</translation>
<translation id="2091887806945687916">Zvuk</translation>
<translation id="2094505752054353250">Neshoda domén</translation>
<translation id="2096368010154057602">Oddělení</translation>
+<translation id="2102134110707549001">Navrhnout silné heslo…</translation>
<translation id="2108755909498034140">Restartujte počítač</translation>
<translation id="2113977810652731515">Karta</translation>
<translation id="2114841414352855701">Zásada ignorována, protože bylo přepsána zásadou <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Chyba protokolu HTTP</translation>
<translation id="2270484714375784793">Telefonní číslo</translation>
<translation id="2292556288342944218">Vaše připojení k internetu je blokováno</translation>
-<translation id="230155334948463882">Nová karta?</translation>
<translation id="2316887270356262533">Uvolní více než 1 MB. Je možné, že se některé weby při příští návštěvě budou načítat pomaleji.</translation>
<translation id="2317259163369394535">Doména <ph name="DOMAIN" /> vyžaduje zadání uživatelského jména a hesla.</translation>
<translation id="2317583587496011522">Obchodník přijímá debetní karty.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Přijímané karty</translation>
<translation id="2702801445560668637">Seznam četby</translation>
<translation id="2704283930420550640">Hodnota neodpovídá formátu.</translation>
-<translation id="2704951214193499422">Prohlížeč Chromium vaši kartu aktuálně nemohl ověřit. Zkuste to prosím znovu později.</translation>
<translation id="2705137772291741111">Kopii tohoto webu uloženou v mezipaměti se nepodařilo přečíst.</translation>
<translation id="2709516037105925701">Automatické vyplňování</translation>
<translation id="2710942282213947212">Software na počítači brání prohlížeči Chromium v bezpečném připojení k webu</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Způsob dopravy</translation>
<translation id="277499241957683684">Chybějící záznam zařízení</translation>
<translation id="2781030394888168909">Exportovat pro MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Připojení bylo resetováno.</translation>
<translation id="2788784517760473862">Přijímané kreditní karty</translation>
<translation id="2794233252405721443">Web je blokován</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Libovolné servery proxy nakonfigurované pro připojení můžete zakázat na stránce Nastavení.</translation>
<translation id="2955913368246107853">Zavřít vyhledávací lištu</translation>
<translation id="2958431318199492670">Konfigurace sítě nesplňuje standard ONC. Může se stát, že některé části konfigurace nebudou importovány.</translation>
-<translation id="2966678944701946121">Platnost do <ph name="EXPIRATION_DATE_ABBR" />, přidáno <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Aby bylo možné navázat zabezpečené spojení, musejí být správně nastaveny hodiny. Důvodem je, že certifikáty, pomocí kterých se weby identifikují, platí pouze pro konkrétní období. Jelikož hodiny v zařízení nejsou nastaveny správně, Google Chrome tyto certifikáty nemůže ověřit.</translation>
<translation id="2972581237482394796">&amp;Opakovat</translation>
<translation id="2977665033722899841">Aktuálně je vybrán řádek <ph name="ROW_NAME" />. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Chybný typ zásady</translation>
<translation id="3037605927509011580">Aj, chyba!</translation>
<translation id="3041612393474885105">Informace o certifikátu</translation>
-<translation id="3063697135517575841">Chrome vaši kartu aktuálně nemohl ověřit. Zkuste to prosím znovu později.</translation>
<translation id="3064966200440839136">Chystáte se opustit anonymní režim, abyste mohli zaplatit v externí aplikaci. Chcete pokračovat?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Žádné}=1{1 heslo}few{# hesla}many{# hesla}other{# hesel}}</translation>
<translation id="3096100844101284527">Přidat adresu vyzvednutí</translation>
@@ -338,7 +335,6 @@
<translation id="3305707030755673451">Vaše data byla <ph name="TIME" /> zašifrována pomocí heslové fráze pro synchronizaci. Chcete-li zahájit synchronizaci, zadejte ji.</translation>
<translation id="3320021301628644560">Přidání fakturační adresy</translation>
<translation id="3338095232262050444">Zabezpečeno</translation>
-<translation id="3340978935015468852">nastavení</translation>
<translation id="3345135638360864351">Odeslání žádosti o přístup k tomuto webu uživateli <ph name="NAME" /> se nezdařilo. Zkuste to prosím znovu.</translation>
<translation id="3355823806454867987">Změna nastavení proxy...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />nebude ukládat<ph name="END_EMPHASIS" /> následující informace:
@@ -372,7 +368,6 @@
<translation id="3528171143076753409">Certifikát serveru není důvěryhodný.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Alespoň jedna položka v synchronizovaných zařízeních}=1{1 položka (a další v synchronizovaných zařízeních)}few{# položky (a další v synchronizovaných zařízeních)}many{# položky (a další v synchronizovaných zařízeních)}other{# položek (a další v synchronizovaných zařízeních)}}</translation>
<translation id="3539171420378717834">Uchovat kopii této karty v tomto zařízení</translation>
-<translation id="3542684924769048008">Použít heslo pro:</translation>
<translation id="3549644494707163724">Šifrovat synchronizovaná data pomocí vlastní heslové fráze pro synchronizaci.</translation>
<translation id="3556433843310711081">Správce vám jej může odblokovat.</translation>
<translation id="3566021033012934673">Vaše připojení není soukromé</translation>
@@ -457,7 +452,6 @@
<translation id="4171400957073367226">Chybný ověřovací podpis</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> další položka}few{<ph name="ITEM_COUNT" /> další položky}many{<ph name="ITEM_COUNT" /> další položky}other{<ph name="ITEM_COUNT" /> dalších položek}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Pokud jste heslo organizace <ph name="ORG_NAME" /> použili na jiném webu, doporučujeme vám ho resetovat.</translation>
<translation id="4196861286325780578">&amp;Opakovat přesunutí</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Zkontrolovat konfiguraci firewallu a antivirového softwaru<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Selhání</translation>
@@ -486,7 +480,6 @@
<translation id="425582637250725228">Je možné, že provedené změny nebudou uloženy.</translation>
<translation id="4258748452823770588">Chybný podpis</translation>
<translation id="4265872034478892965">Povoleno administrátorem</translation>
-<translation id="4269787794583293679">(Žádné uživatelské jméno)</translation>
<translation id="4275830172053184480">Restartovat zařízení</translation>
<translation id="4277028893293644418">Resetovat heslo</translation>
<translation id="4280429058323657511">s platností do <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -509,6 +502,7 @@
<translation id="4434045419905280838">Vyskakovací okna a přesměrování</translation>
<translation id="443673843213245140">Využití proxy serveru je zakázáno, je však určena explicitní konfigurace proxy serveru.</translation>
<translation id="445100540951337728">Přijímané debetní karty</translation>
+<translation id="4472575034687746823">Začít</translation>
<translation id="4506176782989081258">Chyba ověřování: <ph name="VALIDATION_ERROR" />.</translation>
<translation id="4506599922270137252">Kontaktovat administrátora systému</translation>
<translation id="450710068430902550">Sdílení s administrátorem</translation>
@@ -533,8 +527,8 @@
<translation id="4726672564094551039">Znovu načíst zásady</translation>
<translation id="4728558894243024398">Platforma</translation>
<translation id="4736825316280949806">Restartujte Chromium</translation>
+<translation id="4742407542027196863">Spravovat hesla…</translation>
<translation id="4744603770635761495">Spustitelná cesta</translation>
-<translation id="4749685221585524849">Naposledy použito <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Vaše údaje (například hesla nebo čísla platebních karet) jsou při odesílání na tento web soukromé.</translation>
<translation id="4756388243121344051">Historie</translation>
<translation id="4758311279753947758">Přidat kontaktní údaje</translation>
@@ -551,6 +545,7 @@
<translation id="4850886885716139402">Zobrazit</translation>
<translation id="4854362297993841467">Tento způsob doručení není k dispozici. Zkuste použít jiný způsob.</translation>
<translation id="4858792381671956233">Požádal(a) jsi rodiče o povolení návštěvy tohoto webu.</translation>
+<translation id="4876305945144899064">Žádné uživatelské jméno</translation>
<translation id="4880827082731008257">Hledat v historii</translation>
<translation id="4881695831933465202">Otevřít</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -584,6 +579,7 @@
<translation id="5089810972385038852">Stát/kraj</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="5098332213681597508">Tento název pochází z vašeho účtu Google.</translation>
<translation id="5115563688576182185">(64bitový)</translation>
<translation id="5121084798328133320">Po ověření budou údaje o kartě z vašeho účtu Google Payments poskytnuty tomuto webu.</translation>
<translation id="5128122789703661928">Relace s tímto názvem není pro smazání platná.</translation>
@@ -624,9 +620,10 @@
<translation id="5332219387342487447">Způsob dopravy</translation>
<translation id="5355557959165512791">Web <ph name="SITE" /> teď nemůžete navštívit, protože jeho certifikát byl zrušen. Síťové chyby a útoky jsou obvykle dočasné, tato stránka pravděpodobně později bude fungovat.</translation>
<translation id="536296301121032821">Ukládání nastavení zásady se nezdařilo</translation>
+<translation id="5371425731340848620">Aktualizace karty</translation>
<translation id="5377026284221673050">Vaše hodiny se zpožďují, Vaše hodiny jdou napřed nebo &lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;</translation>
<translation id="5386426401304769735">Řetězec certifikátů tohoto webu obsahuje certifikát podepsaný algoritmem SHA-1.</translation>
-<translation id="5402410679244714488">Platnost do: <ph name="EXPIRATION_DATE_ABBR" />, naposledy použito více než před rokem</translation>
+<translation id="5387961145478138773">Získejte rychlý přístup ke svým oblíbeným aplikacím Google</translation>
<translation id="540969355065856584">Server nedokázal prokázat, že patří doméně <ph name="DOMAIN" />. Jeho bezpečnostní certifikát v tuto chvíli není platný. 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="5421136146218899937">Vymazat údaje o prohlížení...</translation>
<translation id="5430298929874300616">Odstranit záložku</translation>
@@ -668,11 +665,13 @@ Kontaktujte administrátora systému.</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="5659593005791499971">E-mail</translation>
+<translation id="5666899935841546222">Chcete mít všechny své karty na jednom místě?</translation>
<translation id="5675650730144413517">Tato stránka nefunguje</translation>
<translation id="5685654322157854305">Přidat dodací adresu</translation>
<translation id="5689199277474810259">Exportovat do formátu JSON</translation>
<translation id="5689516760719285838">Poloha</translation>
<translation id="570530837424789914">Spravovat...</translation>
+<translation id="57094364128775171">Navrhnout silné heslo…</translation>
<translation id="5710435578057952990">Identita těchto webových stránek nebyla ověřena.</translation>
<translation id="5719499550583120431">Obchodník přijímá předplacené karty.</translation>
<translation id="5720705177508910913">Aktuální uživatel</translation>
@@ -693,11 +692,9 @@ Kontaktujte administrátora systému.</translation>
<translation id="5869405914158311789">Tento web není dostupný</translation>
<translation id="5869522115854928033">Uložená hesla</translation>
<translation id="5893752035575986141">Obchodník přijímá kreditní karty.</translation>
-<translation id="5898382028489516745">Pokud jste heslo organizace <ph name="ORG_NAME" /> použili na jiném webu, doporučujeme vám ho resetovat.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (synchronizováno)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{Používá se 1}few{Používají se #}many{Používá se #}other{Používá se #}}</translation>
<translation id="5939518447894949180">Resetovat</translation>
-<translation id="5959728338436674663">Automaticky odesílat část <ph name="BEGIN_WHITEPAPER_LINK" />informací o systému a obsahu stránek<ph name="END_WHITEPAPER_LINK" /> do Googlu s cílem pomoci rozpoznávat nebezpečné aplikace a weby. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Úprava kontaktních údajů</translation>
<translation id="5967867314010545767">Odstranit z historie</translation>
<translation id="5975083100439434680">Oddálit</translation>
@@ -742,13 +739,11 @@ Kontaktujte administrátora systému.</translation>
<translation id="6282194474023008486">PSČ</translation>
<translation id="6290238015253830360">Zde se zobrazí navrhované články</translation>
<translation id="6305205051461490394">Web <ph name="URL" /> není dostupný.</translation>
-<translation id="6319915415804115995">Naposledy použito více než před rokem</translation>
<translation id="6321917430147971392">Zkontrolujte nastavení DNS</translation>
<translation id="6328639280570009161">Zkuste deaktivovat předvídání akcí sítě</translation>
<translation id="6328786501058569169">Tento web je klamavý</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="6342069812937806050">Právě teď</translation>
<translation id="6355080345576803305">Přepsání veřejné relace</translation>
<translation id="6358450015545214790">Nápověda</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 další návrh}few{# další návrhy}many{# dalšího návrhu}other{# dalších návrhů}}</translation>
@@ -773,7 +768,6 @@ Kontaktujte administrátora systému.</translation>
<translation id="6529602333819889595">&amp;Opakovat smazání</translation>
<translation id="6534179046333460208">Návrhy fyzického webu</translation>
<translation id="6550675742724504774">Možnosti</translation>
-<translation id="6556915248009097796">Platnost do <ph name="EXPIRATION_DATE_ABBR" />, naposledy použito <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Správce vám přístup na web dosud neschválil.</translation>
<translation id="6569060085658103619">Prohlížíte si stránku rozšíření</translation>
<translation id="6596325263575161958">Možnosti šifrování</translation>
@@ -802,15 +796,20 @@ Kontaktujte administrátora systému.</translation>
<translation id="6825578344716086703">Pokusili jste se přejít na web <ph name="DOMAIN" />, server však předložil certifikát podepsaný slabým algoritmem (například SHA-1). To znamená, že bezpečnostní pověření předložená serverem mohou být falešná a může se jednat o úplně jiný server, než předpokládáte (můžete komunikovat s útočníkem).</translation>
<translation id="6831043979455480757">Přeložit</translation>
<translation id="6839929833149231406">Region</translation>
+<translation id="6852204201400771460">Načíst aplikaci znovu?</translation>
+<translation id="6865412394715372076">Tuto kartu teď nelze ověřit</translation>
<translation id="6874604403660855544">&amp;Opakovat přidání</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Úroveň zásady není podporována.</translation>
<translation id="6895330447102777224">Vaše karta je ověřena</translation>
<translation id="6897140037006041989">User agent</translation>
+<translation id="6903319715792422884">Pomozte zlepšit Bezpečné vyhledávání tím, že budete část <ph name="BEGIN_WHITEPAPER_LINK" />informací o systému a obsahu stránek<ph name="END_WHITEPAPER_LINK" /> odesílat do Googlu. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Uživatel:</translation>
+<translation id="6944692733090228304">Zadali jste heslo na webu, který není spravován organizací <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Aby byl váš účet chráněn, nepoužívejte jeho heslo v jiných aplikacích a na jiných webech.</translation>
<translation id="6945221475159498467">Vybrat</translation>
<translation id="6948701128805548767">Chcete-li zobrazit způsoby vyzvednutí a požadavky, vyberte adresu</translation>
<translation id="6949872517221025916">Resetovat heslo</translation>
+<translation id="6950684638814147129">Při analýze hodnoty JSON došlo k chybě: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Zdá se, že certifikát serveru je podvrh.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Zařízení</translation>
@@ -872,6 +871,7 @@ Kontaktujte administrátora systému.</translation>
&lt;li&gt;Klikněte na &lt;strong&gt;Použít&lt;/strong&gt; a poté na &lt;strong&gt;OK&lt;/strong&gt;.
&lt;li&gt;Informace o tom, jak tento software trvale odstranit z počítače, naleznete v &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;centru nápovědy prohlížeče Chrome&lt;/a&gt;.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Spravovat hesla…</translation>
<translation id="7419106976560586862">Cesta k profilu</translation>
<translation id="7437289804838430631">Přidat kontaktní údaje</translation>
<translation id="7441627299479586546">Chybný předmět zásady</translation>
@@ -880,7 +880,6 @@ Kontaktujte administrátora systému.</translation>
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Další informace<ph name="END_LINK" /> o tomto problému.</translation>
<translation id="7455133967321480974">Použít výchozí globální hodnotu (Blokovat)</translation>
<translation id="7460163899615895653">Zde se zobrazují nedávno otevřené karty z jiných zařízení.</translation>
-<translation id="7469372306589899959">Ověřování karty</translation>
<translation id="7473891865547856676">Ne, děkuji</translation>
<translation id="7481312909269577407">Vpřed</translation>
<translation id="7485870689360869515">Nebyla nalezena žádná data.</translation>
@@ -1010,6 +1009,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="8308427013383895095">Překlad se nezdařil kvůli problému s připojením k síti.</translation>
<translation id="8311129316111205805">Načíst relaci</translation>
<translation id="8332188693563227489">Přístup k webu <ph name="HOST_NAME" /> byl odepřen</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Pokud bezpečnostní rizika chápete, můžete <ph name="BEGIN_LINK" />tento web navštívit<ph name="END_LINK" /> před tím, než budou nebezpečné programy odstraněny.</translation>
<translation id="8349305172487531364">Lišta záložek</translation>
<translation id="8363502534493474904">Vypnout režim Letadlo</translation>
@@ -1030,21 +1030,25 @@ Kontaktujte administrátora systému.</translation>
<translation id="8498891568109133222">Odpověď webu <ph name="HOST_NAME" /> trvala příliš dlouho.</translation>
<translation id="8503559462189395349">Hesla Chrome</translation>
<translation id="8503813439785031346">Uživatelské jméno</translation>
+<translation id="8508648098325802031">Ikona Vyhledávání</translation>
<translation id="8543181531796978784">Můžete <ph name="BEGIN_ERROR_LINK" />nahlásit problém se zjištěným webem<ph name="END_ERROR_LINK" />. Pokud bezpečnostní rizika chápete, můžete <ph name="BEGIN_LINK" />tento nespolehlivý web navštívit<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Máte nějaké dotazy? Kontaktujte osobu, která dohlíží na váš profil.</translation>
<translation id="8553075262323480129">Překlad se nezdařil. Nepodařilo se rozpoznat jazyk stránky.</translation>
<translation id="8557066899867184262">Kód CVC je uveden na zadní straně karty.</translation>
<translation id="8559762987265718583">Soukromé připojení k doméně <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> nelze navázat, protože máte v zařízení nastaveno chybné datum a čas (<ph name="DATE_AND_TIME" />).</translation>
+<translation id="8564985650692024650">Pokud jste heslo organizace <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> použili na jiném webu, doporučujeme vám ho resetovat.</translation>
<translation id="8571890674111243710">Překlad stránky do jazyka: <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Přidat telefon
</translation>
<translation id="859285277496340001">V certifikátu není uvedeno, jakým způsobem lze zkontrolovat, zda nebyl zrušen.</translation>
+<translation id="860043288473659153">Jméno držitele karty</translation>
<translation id="8620436878122366504">Rodiče přístup dosud neschválili.</translation>
<translation id="8625384913736129811">Uložit tuto kartu do zařízení</translation>
-<translation id="8639963783467694461">Nastavení automatického vyplňování</translation>
+<translation id="8663226718884576429">Shrnutí objednávky, <ph name="TOTAL_LABEL" />, další podrobnosti</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, odpověď, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Vaše spojení se serverem <ph name="DOMAIN" /> není šifrované.</translation>
<translation id="8718314106902482036">Platba nebyla dokončena</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, návrh vyhledávacího dotazu</translation>
<translation id="8725066075913043281">Zkusit znovu</translation>
<translation id="8728672262656704056">Jste v anonymním režimu</translation>
<translation id="8730621377337864115">Hotovo</translation>
@@ -1060,8 +1064,10 @@ Kontaktujte administrátora systému.</translation>
<translation id="8820817407110198400">Záložky</translation>
<translation id="883848425547221593">Jiné záložky</translation>
<translation id="884264119367021077">Dodací adresa</translation>
+<translation id="8846319957959474018">Aplikace otevřete snadno pomocí záložek</translation>
<translation id="884923133447025588">Nebyl nalezen žádný mechanismus zamítnutí.</translation>
<translation id="885730110891505394">Sdílení s Googlem</translation>
+<translation id="8858065207712248076">Pokud jste heslo organizace <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> použili na jiném webu, doporučujeme vám ho resetovat.</translation>
<translation id="8866481888320382733">Při analýze nastavení zásady došlo k chybě</translation>
<translation id="8870413625673593573">Nedávno zavřené</translation>
<translation id="8874824191258364635">Zadejte platné číslo karty</translation>
@@ -1100,6 +1106,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="9103872766612412690">Web <ph name="SITE" /> vaše informace běžně chrání šifrováním. Když se prohlížeč Chromium k webu <ph name="SITE" /> pokusil připojit tentokrát, web vrátil neobvyklé a nesprávné identifikační údaje. K tomuto problému může dojít, pokud se za web <ph name="SITE" /> pokouší vydávat nějaký útočník nebo pokud bylo připojení přerušeno přihlašovací obrazovkou sítě Wi-Fi. Vaše informace jsou i nadále v bezpečí, protože prohlížeč Chromium připojení přerušil dříve, než došlo k odeslání jakýchkoliv dat.</translation>
<translation id="9106062320799175032">Přidání fakturační adresy</translation>
<translation id="910908805481542201">Pomozte mi to opravit</translation>
+<translation id="9114524666733003316">Ověřování karty...</translation>
<translation id="9128870381267983090">Připojit k síti</translation>
<translation id="9137013805542155359">Zobrazit originál</translation>
<translation id="9137248913990643158">Chcete-li tuto aplikaci použít, přihlaste se do Chromu.</translation>
@@ -1110,6 +1117,7 @@ Kontaktujte administrátora systému.</translation>
<translation id="9168814207360376865">Povolit webům zjišťovat, zda máte uložené platební metody</translation>
<translation id="9169664750068251925">Blokovat vždy na tomto webu</translation>
<translation id="9170848237812810038">Z&amp;pět</translation>
+<translation id="9171296965991013597">Zavřít aplikaci?</translation>
<translation id="917450738466192189">Certifikát serveru je neplatný.</translation>
<translation id="9183425211371246419">Web <ph name="HOST_NAME" /> používá nepodporovaný protokol.</translation>
<translation id="9205078245616868884">Vaše data jsou šifrována pomocí heslové fráze pro synchronizaci. Chcete-li zahájit synchronizaci, zadejte ji.</translation>
diff --git a/chromium/components/strings/components_strings_da.xtb b/chromium/components/strings/components_strings_da.xtb
index 78b98c00c69..c93933fb8d4 100644
--- a/chromium/components/strings/components_strings_da.xtb
+++ b/chromium/components/strings/components_strings_da.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Skjul værdi</translation>
<translation id="1228893227497259893">Forkert enheds-id</translation>
<translation id="1232569758102978740">Unavngivet</translation>
+<translation id="1250759482327835220">Gem dit kort, dit navn og din faktureringsadresse på din Google-konto for at betale hurtigere næste gang.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (synkroniseret)</translation>
<translation id="1256368399071562588">&lt;p&gt;Hvis du forsøger at besøge et website, og det ikke åbnes, kan du starte med at prøve at rette fejlen med følgende fejlfindingstrin:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Juster datoen og tiden i sektionen &lt;strong&gt;Generelt&lt;/strong&gt; i appen &lt;strong&gt;Indstillinger&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Der opstod en fejl ved visningen af denne webside.</translation>
-<translation id="1590457302292452960">Generer en stærk adgangskode...</translation>
<translation id="1592005682883173041">Lokal dataadgang</translation>
<translation id="1594030484168838125">Vælg</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Prøv at kontakte systemadministratoren.</translation>
<translation id="1740951997222943430">Angiv en gyldig udløbsmåned</translation>
+<translation id="1743520634839655729">Gem dit kort, dit navn og din faktureringsadresse på din Google-konto og denne enhed for at betale hurtigere næste gang.</translation>
<translation id="17513872634828108">Åbne faner</translation>
<translation id="1753706481035618306">Sidetal</translation>
<translation id="1763864636252898013">Denne server kunne ikke bevise, at den er <ph name="DOMAIN" />, da operativsystemet på din enhed ikke har tillid til sikkerhedscertifikatet. Dette kan skyldes en fejlkonfiguration, eller at en hacker har opfanget din forbindelse.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Dine åbne faner vises her</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Kortindehaverens navn</translation>
-<translation id="1806541873155184440">Tilføjet <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Ugyldig anmodning eller anmodningsparametre</translation>
<translation id="1826516787628120939">Kontrollerer</translation>
<translation id="1834321415901700177">Dette website indeholder skadelige programmer</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 forslag}one{# forslag}other{# forslag}}</translation>
<translation id="2079545284768500474">Fortryd</translation>
<translation id="20817612488360358">Indstillingerne for systemproxy er angivet at blive brugt, men en eksplicit proxykonfiguration er også angivet.</translation>
-<translation id="2084558088529668945">Du indtastede din adgangskode på et website, der ikke administreres af <ph name="ORG_NAME" />. Du kan beskytte din konto ved at undgå at bruge din adgangskode i andre apps og på andre websites.</translation>
<translation id="2091887806945687916">Lyd</translation>
<translation id="2094505752054353250">Uoverensstemmelse mellem domæner</translation>
<translation id="2096368010154057602">Departement</translation>
+<translation id="2102134110707549001">Foreslå stærk adgangskode…</translation>
<translation id="2108755909498034140">Genstart computeren</translation>
<translation id="2113977810652731515">Kort</translation>
<translation id="2114841414352855701">Ignoreret, da den blev tilsidesat af <ph name="POLICY_NAME" /> .</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP-fejl</translation>
<translation id="2270484714375784793">Telefonnummer</translation>
<translation id="2292556288342944218">Din internetadgang er blokeret</translation>
-<translation id="230155334948463882">Har du fået nyt kort?</translation>
<translation id="2316887270356262533">Frigiver over 1 MB. Nogle websites indlæses muligvis langsommere under dit næste besøg.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> kræver et brugernavn og en adgangskode.</translation>
<translation id="2317583587496011522">Debetkort accepteres.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Accepterede kort</translation>
<translation id="2702801445560668637">Læseliste</translation>
<translation id="2704283930420550640">Værdien stemmer ikke overens med formatet.</translation>
-<translation id="2704951214193499422">Chromium kan ikke bekræfte dit kort i øjeblikket. Prøv igen senere.</translation>
<translation id="2705137772291741111">Den gemte (cachelagrede) kopi af dette website kunne ikke læses.</translation>
<translation id="2709516037105925701">AutoFyld</translation>
<translation id="2710942282213947212">Der er software på computeren, som forhindrer Chromium i at oprette en sikker forbindelse til nettet</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Forsendelsesmetode</translation>
<translation id="277499241957683684">Manglende enhedsregistrering</translation>
<translation id="2781030394888168909">Eksportér MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Forbindelsen blev nulstillet.</translation>
<translation id="2788784517760473862">Accepterede kreditkort</translation>
<translation id="2794233252405721443">Websitet er blokeret</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Du kan deaktivere alle proxyer, der er konfigureret for en forbindelse, fra siden Indstillinger.</translation>
<translation id="2955913368246107853">Luk søgefeltet</translation>
<translation id="2958431318199492670">Netværkskonfigurationen overholder ikke ONC-standarden. Dele af konfiguration kan muligvis ikke importeres.</translation>
-<translation id="2966678944701946121">Udløbsdato: <ph name="EXPIRATION_DATE_ABBR" />. Tilføjet <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Uret på din enhed skal være indstillet korrekt, før du kan oprette en sikker forbindelse. Dette er vigtigt, da de certifikater, websites bruger til at identificere sig selv, kun er gyldige i bestemte perioder. Da uret på din enhed er indstillet forkert, kan Chrome ikke bekræfte disse certifikater.</translation>
<translation id="2972581237482394796">&amp;Annuller fortryd</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" /> er i øjeblikket valgt. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Forkert politiktype</translation>
<translation id="3037605927509011580">Øv, surt!</translation>
<translation id="3041612393474885105">Certifikatoplysninger</translation>
-<translation id="3063697135517575841">Chrome kan ikke bekræfte dit kort i øjeblikket. Prøv igen senere.</translation>
<translation id="3064966200440839136">Du forlader inkognitotilstand for at betale via en ekstern applikation. Vil du fortsætte?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Ingen}=1{1 adgangskode}one{# adgangskode}other{# adgangskoder}}</translation>
<translation id="3096100844101284527">Tilføj afhentningsadresse</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Dine data blev krypteret med din adgangssætning til synkronisering d. <ph name="TIME" />. Angiv adgangssætningen for at starte synkroniseringen.</translation>
<translation id="3320021301628644560">Tilføj faktureringsadresse</translation>
<translation id="3338095232262050444">Sikker</translation>
-<translation id="3340978935015468852">indstillinger</translation>
<translation id="3345135638360864351">Din anmodning om at få adgang til dette website kunne ikke sendes til <ph name="NAME" />. Prøv igen.</translation>
<translation id="3355823806454867987">Skift indstillinger for proxy...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />gemmer ikke<ph name="END_EMPHASIS" /> følgende oplysninger:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Serverens certifikat er ikke troværdigt.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Mindst 1 element på synkroniserede enheder}=1{1 element (og flere på synkroniserede enheder)}one{# element (og flere på synkroniserede enheder)}other{# elementer (og flere på synkroniserede enheder)}}</translation>
<translation id="3539171420378717834">Gem en kopi af dette kort på denne enhed</translation>
-<translation id="3542684924769048008">Brug adgangskode til:</translation>
<translation id="3549644494707163724">Krypter alle synkroniserede data med din egen adgangssætning til synkronisering</translation>
<translation id="3556433843310711081">Din administrator kan fjerne blokeringen for dig</translation>
<translation id="3566021033012934673">Din forbindelse er ikke privat</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Ugyldig verifikationssignatur</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> element mere}one{<ph name="ITEM_COUNT" /> element mere}other{<ph name="ITEM_COUNT" /> elementer mere}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome anbefaler, at du nulstiller din adgangskode til <ph name="ORG_NAME" />, hvis du har brugt den på andre websites.</translation>
<translation id="4196861286325780578">&amp;Annuller fortryd flytning</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Kontrollere firewall- og antiviruskonfigurationer<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Nedbrud</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Ændringer, du har foretaget, gemmes muligvis ikke.</translation>
<translation id="4258748452823770588">Forkert signatur</translation>
<translation id="4265872034478892965">Tilladt af din administrator</translation>
-<translation id="4269787794583293679">(Intet brugernavn)</translation>
<translation id="4275830172053184480">Genstart din enhed</translation>
<translation id="4277028893293644418">Nulstil adgangskoden</translation>
<translation id="4280429058323657511">udløber <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Pop op-vinduer og omdirigeringer</translation>
<translation id="443673843213245140">Brug af en proxy er deaktiveret, men en eksplicit proxykonfiguration er angivet.</translation>
<translation id="445100540951337728">Accepterede debetkort</translation>
+<translation id="4472575034687746823">Kom godt i gang</translation>
<translation id="4506176782989081258">Valideringsfejl: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Kontakte systemadministratoren</translation>
<translation id="450710068430902550">Deling med administrator</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Opdater politikker</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4736825316280949806">Genstart Chromium</translation>
+<translation id="4742407542027196863">Administrer adgangskoder…</translation>
<translation id="4744603770635761495">Eksekverbar sti</translation>
-<translation id="4749685221585524849">Sidst anvendt <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Dine oplysninger (f.eks. adgangskoder eller kreditkortnumre) er private, når de sendes til dette website.</translation>
<translation id="4756388243121344051">&amp;Historik</translation>
<translation id="4758311279753947758">Tilføj kontaktoplysninger</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Vis</translation>
<translation id="4854362297993841467">Denne leveringsmetode er ikke tilgængelig. Prøv en anden metode.</translation>
<translation id="4858792381671956233">Du har spurgt dine forældre, om det er i orden at besøge dette website.</translation>
+<translation id="4876305945144899064">Intet brugernavn</translation>
<translation id="4880827082731008257">Søg i historikken</translation>
<translation id="4881695831933465202">Åbn</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Delstat</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="5098332213681597508">Dette navn er fra din Google-konto.</translation>
<translation id="5115563688576182185">(64-bit)</translation>
<translation id="5121084798328133320">Når du bekræfter, deles kortoplysningerne på din konto i Google Payments med dette website.</translation>
<translation id="5128122789703661928">Sessionen med dette navn kan ikke slettes.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Leveringsmetode</translation>
<translation id="5355557959165512791">Du kan ikke besøge <ph name="SITE" /> lige nu, da dets certifikat er blevet tilbagekaldt. Netværksfejl og angreb er normalt midlertidige, så siden vil sandsynligvis fungere igen senere.</translation>
<translation id="536296301121032821">Der kunne ikke gemmes indstillinger for politik</translation>
+<translation id="5371425731340848620">Opdater kortet</translation>
<translation id="5377026284221673050">"Dit ur er bagud" eller "Dit ur er forud" eller "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">Certifikatkæden for dette website indeholder et certifikat, der er signeret med SHA-1.</translation>
-<translation id="5402410679244714488">Udløbsdato: <ph name="EXPIRATION_DATE_ABBR" />, sidst anvendt for mere end et år siden</translation>
+<translation id="5387961145478138773">Få hurtig adgang til dine foretrukne Google-apps.</translation>
<translation id="540969355065856584">Denne server kunne ikke bevise, at den er <ph name="DOMAIN" />, da dens sikkerhedscertifikat ikke er gyldigt i øjeblikket. Dette kan skyldes en fejlkonfiguration, eller at en hacker har opfanget din forbindelse.</translation>
<translation id="5421136146218899937">Ryd browserdata...</translation>
<translation id="5430298929874300616">Fjern bogmærke</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">Mail</translation>
+<translation id="5666899935841546222">Vil du samle alle dine kort ét sted?</translation>
<translation id="5675650730144413517">Denne side virker ikke</translation>
<translation id="5685654322157854305">Tilføj leveringsadresse</translation>
<translation id="5689199277474810259">Eksportér i JSON</translation>
<translation id="5689516760719285838">Placering</translation>
<translation id="570530837424789914">Administrer...</translation>
+<translation id="57094364128775171">Foreslå stærk adgangskode…</translation>
<translation id="5710435578057952990">Dette websites identitet er ikke blevet bekræftet.</translation>
<translation id="5719499550583120431">Forudbetalte kort accepteres.</translation>
<translation id="5720705177508910913">Aktuel bruger</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Der kan ikke oprettes forbindelse til dette website</translation>
<translation id="5869522115854928033">Gemte adgangskoder</translation>
<translation id="5893752035575986141">Kreditkort accepteres.</translation>
-<translation id="5898382028489516745">Chromium anbefaler, at du nulstiller din adgangskode til <ph name="ORG_NAME" />, hvis du har brugt den på andre websites.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (synkroniseret)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 i brug}one{# i brug}other{# i brug}}</translation>
<translation id="5939518447894949180">Nulstil</translation>
-<translation id="5959728338436674663">Send automatisk <ph name="BEGIN_WHITEPAPER_LINK" />nogle systemoplysninger og noget sideindhold<ph name="END_WHITEPAPER_LINK" /> til Google som en hjælp til at registrere skadelige apps og websites. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Rediger kontaktoplysninger</translation>
<translation id="5967867314010545767">Fjern fra historik</translation>
<translation id="5975083100439434680">Zoom ud</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Postnummer</translation>
<translation id="6290238015253830360">Forslag til artikler til dig vises her</translation>
<translation id="6305205051461490394"><ph name="URL" /> kan ikke nås.</translation>
-<translation id="6319915415804115995">Sidst anvendt for mere end et år siden</translation>
<translation id="6321917430147971392">Kontrollér dine DNS-indstillinger</translation>
<translation id="6328639280570009161">Prøv at deaktivere netværksforslag</translation>
<translation id="6328786501058569169">Dette website er vildledende</translation>
<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="6342069812937806050">Lige nu</translation>
<translation id="6355080345576803305">Tilsidesættelse af offentlig session</translation>
<translation id="6358450015545214790">Hvad betyder dette?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 andet forslag}one{# andet forslag}other{# andre forslag}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Annuller fortryd slet</translation>
<translation id="6534179046333460208">Forslag til Fysisk web</translation>
<translation id="6550675742724504774">Valgmuligheder</translation>
-<translation id="6556915248009097796">Udløbsdato: <ph name="EXPIRATION_DATE_ABBR" />. Sidst anvendt <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Din administrator har ikke godkendt det endnu</translation>
<translation id="6569060085658103619">Du ser en udvidelsesside</translation>
<translation id="6596325263575161958">Krypteringsmuligheder</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Du forsøgte at få fat i <ph name="DOMAIN" />, men serveren præsenterede et certifikat, der er signeret med en svag signaturalgoritme (f.eks. SHA-1). Det betyder, at sikkerhedsoplysningerne fra serveren kan være forfalskede, og at serveren muligvis ikke er den server, som du forventede (du kommunikerer muligvis med en person med ondsindede hensigter).</translation>
<translation id="6831043979455480757">Oversæt</translation>
<translation id="6839929833149231406">Område</translation>
+<translation id="6852204201400771460">Vil du genindlæse appen?</translation>
+<translation id="6865412394715372076">Dette kort kan ikke bekræftes lige nu</translation>
<translation id="6874604403660855544">&amp;Annuller fortryd tilføjelse</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Politikniveauet understøttes ikke.</translation>
<translation id="6895330447102777224">Dit kort er bekræftet</translation>
<translation id="6897140037006041989">Brugeragent</translation>
+<translation id="6903319715792422884">Hjælp med at forbedre Beskyttet browsing ved at sende nogle <ph name="BEGIN_WHITEPAPER_LINK" />systemoplysninger og noget sideindhold<ph name="END_WHITEPAPER_LINK" /> til Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Bruger:</translation>
+<translation id="6944692733090228304">Du indtastede din adgangskode på et website, der ikke administreres af <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Du kan beskytte din konto ved at undgå at bruge din adgangskode i andre apps og på andre websites.</translation>
<translation id="6945221475159498467">Vælg</translation>
<translation id="6948701128805548767">Vælg en adresse for at se afhentningsmetoder og -krav</translation>
<translation id="6949872517221025916">Nulstil adgangskode</translation>
+<translation id="6950684638814147129">Der opstod en fejl under parsing af JSON-værdien: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Serverens certifikat ser ud til at være en forfalskning.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Enhed</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Klik på &lt;strong&gt;Anvend&lt;/strong&gt;, og klik derefter på &lt;strong&gt;OK&lt;/strong&gt;
&lt;li&gt;Gå til &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Hjælp til Chrome&lt;/a&gt; for at se, hvordan du permanent fjerner softwaren fra din computer
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Administrer adgangskoder…</translation>
<translation id="7419106976560586862">Profilsti</translation>
<translation id="7437289804838430631">Tilføj kontaktoplysninger</translation>
<translation id="7441627299479586546">Forkert emne for politik</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Få flere oplysninger<ph name="END_LINK" /> om dette problem.</translation>
<translation id="7455133967321480974">Brug global standard (bloker)</translation>
<translation id="7460163899615895653">Dine seneste faner fra andre enheder vises her</translation>
-<translation id="7469372306589899959">Bekræfter kort</translation>
<translation id="7473891865547856676">Nej tak</translation>
<translation id="7481312909269577407">Frem</translation>
<translation id="7485870689360869515">Der blev ikke fundet nogen data.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Oversættelsen mislykkedes på grund af problemer med netværksforbindelsen.</translation>
<translation id="8311129316111205805">Indlæs session</translation>
<translation id="8332188693563227489">Adgangen til <ph name="HOST_NAME" /> blev nægtet</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Hvis du er indforstået med de forbundne sikkerhedsrisici, kan du <ph name="BEGIN_LINK" />besøge dette website<ph name="END_LINK" />, inden de skadelige programmer fjernes.</translation>
<translation id="8349305172487531364">Bogmærkelinje</translation>
<translation id="8363502534493474904">Deaktivere flytilstand</translation>
@@ -1035,21 +1035,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> var for lang tid om at svare.</translation>
<translation id="8503559462189395349">Chrome-adgangskoder</translation>
<translation id="8503813439785031346">Brugernavn</translation>
+<translation id="8508648098325802031">Søgeikon</translation>
<translation id="8543181531796978784">Du kan <ph name="BEGIN_ERROR_LINK" />rapportere et registreringsproblem<ph name="END_ERROR_LINK" /> eller, hvis du forstår den sikkerhedsrisiko, du udsætter dig for, <ph name="BEGIN_LINK" />kan du gå til dette usikre website<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Har du spørgsmål? Kontakt din profiladministrator.</translation>
<translation id="8553075262323480129">Oversættelsen mislykkedes, fordi sidens sprog ikke kunne fastslås.</translation>
<translation id="8557066899867184262">Kontrolkoden findes på bagsiden af kortet.</translation>
<translation id="8559762987265718583">Der kan ikke oprettes en privat forbindelse til <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, da tid og dato (<ph name="DATE_AND_TIME" />) på din enhed er forkerte.</translation>
+<translation id="8564985650692024650">Chromium anbefaler, at du nulstiller din adgangskode til <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, hvis du har brugt den på andre websites.</translation>
<translation id="8571890674111243710">Oversætter siden til <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Tilføj tlf.nr.
</translation>
<translation id="859285277496340001">Certifikatet angiver ikke en mekanisme, der kontrollerer, om den har været tilbagekaldt.</translation>
+<translation id="860043288473659153">Kortholderens navn</translation>
<translation id="8620436878122366504">Dine forældre har ikke godkendt det endnu</translation>
<translation id="8625384913736129811">Gem dette kort på denne enhed</translation>
-<translation id="8639963783467694461">Indstillinger for AutoFyld</translation>
+<translation id="8663226718884576429">Ordreoversigt, <ph name="TOTAL_LABEL" />, flere oplysninger</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, svar, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Din forbindelse til <ph name="DOMAIN" /> er ikke krypteret.</translation>
<translation id="8718314106902482036">Betalingen blev ikke gennemført</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, søgeforslag</translation>
<translation id="8725066075913043281">Forsøg igen</translation>
<translation id="8728672262656704056">Du er nu i inkognito</translation>
<translation id="8730621377337864115">Udfør</translation>
@@ -1065,8 +1069,10 @@
<translation id="8820817407110198400">Bogmærker</translation>
<translation id="883848425547221593">Andre bogmærker</translation>
<translation id="884264119367021077">Leveringsadresse</translation>
+<translation id="8846319957959474018">Åbn nemt apps med bogmærker</translation>
<translation id="884923133447025588">Der blev ikke fundet nogen funktion til tilbagekaldelse.</translation>
<translation id="885730110891505394">Deling med Google</translation>
+<translation id="8858065207712248076">Chrome anbefaler, at du nulstiller din adgangskode til <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, hvis du har brugt den på andre websites.</translation>
<translation id="8866481888320382733">Der opstod en fejl ved parsing af indstillinger for politik</translation>
<translation id="8870413625673593573">Senest lukkede</translation>
<translation id="8874824191258364635">Angiv et gyldigt kortnummer</translation>
@@ -1105,6 +1111,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> bruger normalt kryptering til at beskytte dine oplysninger. Da Chromium forsøgte at oprette forbindelse til <ph name="SITE" /> denne gang, returnerede websitet usædvanlige og forkerte loginoplysninger. Dette kan skyldes, at en hacker forsøger at udgive sig for at være <ph name="SITE" />, eller at en Wi-Fi-loginskærm har forstyrret forbindelsen. Dine oplysninger er stadig sikre, idet Chromium afbrød forbindelsen, inden der blev udvekslet data.</translation>
<translation id="9106062320799175032">Tilføj faktureringsadresse</translation>
<translation id="910908805481542201">Hjælp mig med at løse dette</translation>
+<translation id="9114524666733003316">Bekræfter kort...</translation>
<translation id="9128870381267983090">Opret forbindelse til netværk</translation>
<translation id="9137013805542155359">Vis oprindelig</translation>
<translation id="9137248913990643158">Start og log ind på Chrome, inden du bruger denne app.</translation>
@@ -1115,6 +1122,7 @@
<translation id="9168814207360376865">Tillad, at websites tjekker, om du har nogen gemte betalingsmetoder</translation>
<translation id="9169664750068251925">Bloker altid på dette website</translation>
<translation id="9170848237812810038">&amp;Fortryd</translation>
+<translation id="9171296965991013597">Vil du lukke appen?</translation>
<translation id="917450738466192189">Serverens certifikat er ugyldigt.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> anvender en ikke-understøttet protokol.</translation>
<translation id="9205078245616868884">Dine data er krypteret med din adgangssætning til synkronisering. Angiv den for at starte synkroniseringen.</translation>
diff --git a/chromium/components/strings/components_strings_de.xtb b/chromium/components/strings/components_strings_de.xtb
index 901826b4e7e..3f53a872eff 100644
--- a/chromium/components/strings/components_strings_de.xtb
+++ b/chromium/components/strings/components_strings_de.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Wert ausblenden</translation>
<translation id="1228893227497259893">Falsche Entitätskennung</translation>
<translation id="1232569758102978740">Unbenannt</translation>
+<translation id="1250759482327835220">Damit Zahlungen zukünftig schneller abgewickelt werden können, speichern Sie Ihre Kreditkartendaten, Ihren Namen und Ihre Rechnungsadresse in Ihrem Google-Konto.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (synchronisiert)</translation>
<translation id="1256368399071562588">&lt;p&gt;Wenn Sie eine bestimmte Website nicht aufrufen können, versuchen Sie zuerst, das Problem folgendermaßen zu beheben:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Stellen Sie in der App &lt;strong&gt;Einstellungen&lt;/strong&gt; im Bereich &lt;strong&gt;Allgemein&lt;/strong&gt; Datum und Uhrzeit richtig ein.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Fehler beim Anzeigen dieser Webseite.</translation>
-<translation id="1590457302292452960">Starkes Passwort generieren...</translation>
<translation id="1592005682883173041">Zugriff auf lokale Daten</translation>
<translation id="1594030484168838125">Auswählen</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">American Express</translation>
<translation id="1734878702283171397">Setzen Sie sich mit dem Systemadministrator in Verbindung.</translation>
<translation id="1740951997222943430">Geben Sie einen gültigen Ablaufmonat ein</translation>
+<translation id="1743520634839655729">Damit Zahlungen zukünftig schneller abgewickelt werden können, speichern Sie Ihre Kreditkartendaten, Ihren Namen und Ihre Rechnungsadresse in Ihrem Google-Konto und auf diesem Gerät.</translation>
<translation id="17513872634828108">Geöffnete Tabs</translation>
<translation id="1753706481035618306">Seitennummer</translation>
<translation id="1763864636252898013">Dieser Server konnte nicht beweisen, dass er <ph name="DOMAIN" /> ist. Sein Sicherheitszertifikat wird vom Betriebssystem Ihres Geräts als nicht vertrauenswürdig eingestuft. Mögliche Gründe sind eine fehlerhafte Konfiguration oder ein Angreifer, der Ihre Verbindung abfängt.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Hier werden Ihre offenen Tabs angezeigt</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Name des Karteninhabers</translation>
-<translation id="1806541873155184440">Hinzugefügt: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Anfrage oder Anfrageparameter ungültig</translation>
<translation id="1826516787628120939">Überprüfung läuft</translation>
<translation id="1834321415901700177">Diese Website enthält schädliche Programme</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 Vorschlag}other{# Vorschläge}}</translation>
<translation id="2079545284768500474">Rückgängig machen</translation>
<translation id="20817612488360358">Die System-Proxy-Einstellungen sind zur Verwendung angegeben, gleichzeitig wurde aber auch eine explizite Proxy-Konfiguration festgelegt.</translation>
-<translation id="2084558088529668945">Sie haben Ihr Passwort auf einer Website eingegeben, die nicht von <ph name="ORG_NAME" /> verwaltet wird. Um Ihr Konto zu schützen, verwenden Sie nicht dasselbe Passwort für andere Apps und Websites.</translation>
<translation id="2091887806945687916">Ton</translation>
<translation id="2094505752054353250">Domains stimmen nicht überein.</translation>
<translation id="2096368010154057602">Verwaltungsbezirk</translation>
+<translation id="2102134110707549001">Starkes Passwort vorschlagen…</translation>
<translation id="2108755909498034140">Computer neu starten</translation>
<translation id="2113977810652731515">Karte</translation>
<translation id="2114841414352855701">Wird ignoriert, da sie von <ph name="POLICY_NAME" /> außer Kraft gesetzt wurde.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP-Fehler</translation>
<translation id="2270484714375784793">Telefonnummer</translation>
<translation id="2292556288342944218">Ihre Internetverbindung ist gesperrt</translation>
-<translation id="230155334948463882">Neue Karte?</translation>
<translation id="2316887270356262533">Es werden weniger als 1 MB Speicherplatz freigegeben. Manche Websites werden beim nächsten Öffnen eventuell langsamer geladen.</translation>
<translation id="2317259163369394535">Für <ph name="DOMAIN" /> sind ein Nutzername und ein Passwort erforderlich.</translation>
<translation id="2317583587496011522">Debitkarten werden akzeptiert.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Akzeptierte Karten</translation>
<translation id="2702801445560668637">Leseliste</translation>
<translation id="2704283930420550640">Wert stimmt nicht mit dem Format überein.</translation>
-<translation id="2704951214193499422">Ihre Karte kann von Chromium zurzeit nicht bestätigt werden. Bitte versuchen Sie es später noch einmal.</translation>
<translation id="2705137772291741111">Die (im Cache) gespeicherte Kopie dieser Website war nicht lesbar.</translation>
<translation id="2709516037105925701">AutoFill</translation>
<translation id="2710942282213947212">Software auf Ihrem Computer verhindert, dass Chromium eine sichere Internetverbindung herstellt</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Versandart</translation>
<translation id="277499241957683684">Fehlender Gerätedatensatz</translation>
<translation id="2781030394888168909">Im Mac OS-Format exportieren</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Verbindung wurde zurückgesetzt.</translation>
<translation id="2788784517760473862">Akzeptierte Kreditkarten</translation>
<translation id="2794233252405721443">Website blockiert</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Sie können alle für eine Verbindung konfigurierten Proxys auf der Seite "Einstellungen" deaktivieren.</translation>
<translation id="2955913368246107853">Suchleiste schließen</translation>
<translation id="2958431318199492670">Die Netzwerkkonfiguration stimmt nicht mit dem ONC-Standard überein. Die Konfiguration wird unter Umständen nicht vollständig importiert.</translation>
-<translation id="2966678944701946121">Gültig bis: <ph name="EXPIRATION_DATE_ABBR" />, hinzugefügt: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Zum Herstellen einer sicheren Verbindung muss die Uhrzeit richtig eingestellt sein. Der Grund hierfür ist, dass Websites sich mithilfe von Zertifikaten identifizieren, die nur für einen bestimmten Zeitraum gelten. Da die Uhrzeit Ihres Geräts falsch ist, kann Google Chrome diese Zertifikate nicht bestätigen.</translation>
<translation id="2972581237482394796">&amp;Wiederholen</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, aktuell ausgewählt. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Falscher Richtlinientyp</translation>
<translation id="3037605927509011580">Oh nein!</translation>
<translation id="3041612393474885105">Zertifikatinformationen</translation>
-<translation id="3063697135517575841">Ihre Karte kann von Chrome zurzeit nicht bestätigt werden. Bitte versuchen Sie es später noch einmal.</translation>
<translation id="3064966200440839136">Der Inkognitomodus wird beendet, um über eine externe Anwendung zu zahlen. Fortfahren?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Keine}=1{1 Passwort}other{# Passwörter}}</translation>
<translation id="3096100844101284527">Abholadresse hinzufügen</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Ihre Daten wurden am <ph name="TIME" /> mit Ihrer Synchronisierungspassphrase verschlüsselt. Geben Sie diese ein, um die Synchronisierung zu starten.</translation>
<translation id="3320021301628644560">Rechnungsadresse hinzufügen</translation>
<translation id="3338095232262050444">Sicher</translation>
-<translation id="3340978935015468852">Einstellungen</translation>
<translation id="3345135638360864351">Ihre Zugriffsanfrage für diese Website konnte nicht an <ph name="NAME" /> gesendet werden. Bitte versuchen Sie es erneut.</translation>
<translation id="3355823806454867987">Proxy-Einstellungen ändern...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />speichert die folgenden Informationen nicht<ph name="END_EMPHASIS" />:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Serverzertifikat ist nicht vertrauenswürdig.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Mindestens 1 Eintrag auf synchronisierten Geräten}=1{1 Eintrag (und weitere auf synchronisierten Geräten)}other{# Einträge (und weitere auf synchronisierten Geräten)}}</translation>
<translation id="3539171420378717834">Kopie dieser Karte auf diesem Gerät speichern</translation>
-<translation id="3542684924769048008">Passwort verwenden für:</translation>
<translation id="3549644494707163724">Alle synchronisierten Daten mit meiner eigenen Synchronisierungspassphrase verschlüsseln</translation>
<translation id="3556433843310711081">Dein Administrator kann die Blockierung aufheben</translation>
<translation id="3566021033012934673">Dies ist keine sichere Verbindung</translation>
@@ -409,7 +404,7 @@
<translation id="3736520371357197498">Wenn Sie die Sicherheitsrisiken kennen, können Sie <ph name="BEGIN_LINK" />diese unsichere Website aufrufen<ph name="END_LINK" />, bevor die gefährlichen Programme entfernt wurden.</translation>
<translation id="3739623965217189342">Von Ihnen kopierter Link</translation>
<translation id="3744899669254331632">Sie können <ph name="SITE" /> zurzeit nicht aufrufen, weil die Website verschlüsselte Anmeldedaten gesendet hat, die von Chromium nicht verarbeitet werden können. Netzwerkfehler und Angriffe sind in der Regel nur vorübergehend, sodass die Seite wahrscheinlich später wieder funktioniert.</translation>
-<translation id="3748148204939282805">Hacker könnten auf <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> etwa versuchen, Sie zur Installation von Software oder zur Herausgabe von Daten wie Passwörter, Telefonnummern oder Kreditkartendaten zu bewegen. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
+<translation id="3748148204939282805">Hacker könnten auf <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> etwa versuchen, Sie zur Installation von Software oder zur Herausgabe von Daten wie Passwörtern, Telefonnummern oder Kreditkartendetails zu bewegen. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="375403751935624634">Aufgrund eines Serverfehlers ist die Übersetzung fehlgeschlagen.</translation>
<translation id="3759461132968374835">Es liegen keine kürzlich gemeldeten Abstürze vor. Abstürze, die bei deaktivierter Absturzberichtsfunktion aufgetreten sind, werden hier nicht angezeigt.</translation>
<translation id="3765032636089507299">Die Seite "Safe Browsing" wird überarbeitet.</translation>
@@ -460,7 +455,6 @@
<translation id="4171400957073367226">Ungültige Bestätigungssignatur</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> weiteres Element}other{<ph name="ITEM_COUNT" /> weitere Elemente}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" />: <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome empfiehlt, Ihr Passwort für <ph name="ORG_NAME" /> zurückzusetzen, wenn Sie es auf anderen Websites verwendet haben.</translation>
<translation id="4196861286325780578">&amp;Verschieben wiederholen</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Firewall und Antivirenkonfiguration prüfen<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Abstürze</translation>
@@ -489,7 +483,6 @@
<translation id="425582637250725228">Ihre Änderungen werden eventuell nicht gespeichert.</translation>
<translation id="4258748452823770588">Fehlerhafte Signatur</translation>
<translation id="4265872034478892965">Von Ihrem Administrator zugelassen</translation>
-<translation id="4269787794583293679">(Kein Nutzername)</translation>
<translation id="4275830172053184480">Gerät neu starten</translation>
<translation id="4277028893293644418">Passwort zurücksetzen</translation>
<translation id="4280429058323657511">Gültig bis: <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -512,6 +505,7 @@
<translation id="4434045419905280838">Pop-ups und Weiterleitungen</translation>
<translation id="443673843213245140">Die Proxy-Nutzung ist deaktiviert, es ist jedoch eine explizite Proxy-Konfiguration festgelegt.</translation>
<translation id="445100540951337728">Akzeptierte Debitkarten</translation>
+<translation id="4472575034687746823">Jetzt starten</translation>
<translation id="4506176782989081258">Fehler bei der Überprüfung: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Kontakt mit dem Systemadministrator aufnehmen</translation>
<translation id="450710068430902550">Datenfreigabe an Administrator</translation>
@@ -536,13 +530,13 @@
<translation id="4726672564094551039">Richtlinien neu laden</translation>
<translation id="4728558894243024398">Plattform</translation>
<translation id="4736825316280949806">Chromium neu starten</translation>
+<translation id="4742407542027196863">Passwörter verwalten…</translation>
<translation id="4744603770635761495">Ausführbarer Pfad</translation>
-<translation id="4749685221585524849">Zuletzt verwendet: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Ihre Daten wie Passwörter oder Kreditkartennummern sind privat, wenn Sie sie an diese Website senden.</translation>
<translation id="4756388243121344051">&amp;Verlauf</translation>
<translation id="4758311279753947758">Kontaktdaten hinzufügen</translation>
<translation id="4759118997339041434">AutoFill für Zahlung deaktiviert</translation>
-<translation id="4764776831041365478">Die Webseite unter <ph name="URL" /> ist möglicherweise vorübergehend nicht verfügbar oder wurde dauerhaft an eine neue Webadresse verschoben.</translation>
+<translation id="4764776831041365478">Die Webseite unter <ph name="URL" /> ist eventuell vorübergehend nicht verfügbar oder wurde dauerhaft an eine neue Webadresse verschoben.</translation>
<translation id="4771973620359291008">Ein unbekannter Fehler ist aufgetreten.</translation>
<translation id="4785689107224900852">Zu diesem Tab wechseln</translation>
<translation id="4792143361752574037">Beim Zugreifen auf die Sitzungsdateien ist ein Problem aufgetreten. Momentan können keine Dateien auf der Festplatte gespeichert werden. Bitte laden Sie die Seite neu und versuchen Sie es noch einmal.</translation>
@@ -554,6 +548,7 @@
<translation id="4850886885716139402">Anzeigen</translation>
<translation id="4854362297993841467">Diese Lieferoption ist nicht verfügbar. Bitte wählen Sie eine andere Option aus.</translation>
<translation id="4858792381671956233">Du hast deine Eltern gefragt, ob du diese Website besuchen darfst</translation>
+<translation id="4876305945144899064">Kein Nutzername</translation>
<translation id="4880827082731008257">Im Verlauf suchen</translation>
<translation id="4881695831933465202">Öffnen</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -579,7 +574,7 @@
<translation id="5039804452771397117">Zulassen</translation>
<translation id="5040262127954254034">Datenschutz</translation>
<translation id="5045550434625856497">Falsches Passwort</translation>
-<translation id="5056549851600133418">Artikel für Sie</translation>
+<translation id="5056549851600133418">Artikel für mich</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />Proxyadresse prüfen<ph name="END_LINK" /></translation>
<translation id="5086888986931078152">Eventuell verlieren Sie den Zugriff auf geschützte Inhalte von einigen Websites.</translation>
<translation id="5087286274860437796">Das Serverzertifikat ist zurzeit ungültig.</translation>
@@ -587,6 +582,7 @@
<translation id="5089810972385038852">Bundesstaat/-land</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 Ihre Verbindung abfängt.</translation>
<translation id="5095208057601539847">Provinz</translation>
+<translation id="5098332213681597508">Dieser Name stammt aus Ihrem Google-Konto.</translation>
<translation id="5115563688576182185">(64-Bit)</translation>
<translation id="5121084798328133320">Nach erfolgter Bestätigung werden die Kartendetails Ihres Google Payments-Kontos an diese Website weitergegeben.</translation>
<translation id="5128122789703661928">Die Sitzung mit diesem Namen kann nicht gelöscht werden.</translation>
@@ -627,9 +623,10 @@
<translation id="5332219387342487447">Versandart</translation>
<translation id="5355557959165512791">Sie können <ph name="SITE" /> zurzeit nicht aufrufen, da das Zertifikat widerrufen wurde. Netzwerkfehler und Angriffe sind in der Regel nur vorübergehend, sodass die Seite wahrscheinlich später wieder funktioniert.</translation>
<translation id="536296301121032821">Fehler beim Speichern der Richtlinieneinstellungen</translation>
+<translation id="5371425731340848620">Karte aktualisieren</translation>
<translation id="5377026284221673050">"Ihre Uhr geht nach", "Ihre Uhr geht vor" oder "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">Die Zertifikatskette für diese Website enthält ein Zertifikat mit SHA-1-Signatur.</translation>
-<translation id="5402410679244714488">Gültig bis: <ph name="EXPIRATION_DATE_ABBR" />, vor über einem Jahr zuletzt verwendet</translation>
+<translation id="5387961145478138773">Schnellen Zugriff auf Ihre bevorzugten Google-Apps erhalten</translation>
<translation id="540969355065856584">Dieser Server konnte nicht beweisen, dass er <ph name="DOMAIN" /> ist. Sein Sicherheitszertifikat ist zurzeit ungültig. Mögliche Gründe sind eine fehlerhafte Konfiguration oder ein Angreifer, der Ihre Verbindung abfängt.</translation>
<translation id="5421136146218899937">Browserdaten löschen...</translation>
<translation id="5430298929874300616">Lesezeichen löschen</translation>
@@ -646,12 +643,12 @@
<ph name="LINE_BREAK" />
Wenden Sie sich an Ihren Systemadministrator.</translation>
<translation id="5509780412636533143">Verwaltete Lesezeichen</translation>
-<translation id="5510766032865166053">Möglicherweise wurde sie verschoben oder gelöscht.</translation>
+<translation id="5510766032865166053">Eventuell wurde sie verschoben oder gelöscht.</translation>
<translation id="5523118979700054094">Richtlinienname</translation>
<translation id="552553974213252141">Wurde der Text korrekt extrahiert?</translation>
<translation id="5540224163453853">Der gewünschte Artikel wurde nicht gefunden.</translation>
<translation id="5541546772353173584">E-Mail-Adresse hinzufügen</translation>
-<translation id="5545756402275714221">Artikel für Sie</translation>
+<translation id="5545756402275714221">Artikel für mich</translation>
<translation id="5556459405103347317">Neu laden</translation>
<translation id="5560088892362098740">Ablaufdatum</translation>
<translation id="5565735124758917034">Aktiv</translation>
@@ -671,11 +668,13 @@
<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 Ihrem 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="5659593005791499971">E-Mail-Adresse</translation>
+<translation id="5666899935841546222">Möchten Sie an einem Ort auf alle Ihre Karten zugreifen können?</translation>
<translation id="5675650730144413517">Diese Seite funktioniert nicht</translation>
<translation id="5685654322157854305">Versandadresse hinzufügen</translation>
<translation id="5689199277474810259">Als JSON exportieren</translation>
<translation id="5689516760719285838">Ort</translation>
<translation id="570530837424789914">Verwalten…</translation>
+<translation id="57094364128775171">Starkes Passwort vorschlagen…</translation>
<translation id="5710435578057952990">Die Identität dieser Website wurde nicht verifiziert.</translation>
<translation id="5719499550583120431">Prepaidkarten werden akzeptiert.</translation>
<translation id="5720705177508910913">Aktueller Nutzer</translation>
@@ -693,14 +692,12 @@
<translation id="5813119285467412249">&amp;Hinzufügen wiederholen</translation>
<translation id="5838278095973806738">Sie sollten keine vertraulichen Informationen wie Passwörter oder Kreditkartennummern auf dieser Website eingeben, da sie von Angreifern gestohlen werden könnten.</translation>
<translation id="5866257070973731571">Telefonnummer hinzufügen</translation>
-<translation id="5869405914158311789">Diese Website ist nicht erreichbar</translation>
+<translation id="5869405914158311789">Die Website ist nicht erreichbar</translation>
<translation id="5869522115854928033">Gespeicherte Passwörter</translation>
<translation id="5893752035575986141">Kreditkarten werden akzeptiert.</translation>
-<translation id="5898382028489516745">Chromium empfiehlt, Ihr Passwort für <ph name="ORG_NAME" /> zurückzusetzen, wenn Sie es auf anderen Websites verwendet haben.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (synchronisiert)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 wird verwendet}other{# werden verwendet}}</translation>
<translation id="5939518447894949180">Zurücksetzen</translation>
-<translation id="5959728338436674663"><ph name="BEGIN_WHITEPAPER_LINK" />Ich möchte automatisch einige Systeminformationen und Seiteninhalte an Google senden<ph name="END_WHITEPAPER_LINK" />, um bei der Erfassung schädlicher Apps und Websites zu helfen. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Kontaktdaten bearbeiten</translation>
<translation id="5967867314010545767">Aus Verlauf entfernen</translation>
<translation id="5975083100439434680">Verkleinern</translation>
@@ -713,7 +710,7 @@
<translation id="6017850046339264347">Angreifer auf der Website <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> könnten betrügerische Apps installieren, die scheinbar einem anderen Zweck dienen oder Daten erfassen, um Sie auszuspionieren. <ph name="BEGIN_LEARN_MORE_LINK" />Weitere Informationen<ph name="END_LEARN_MORE_LINK" /></translation>
<translation id="6025416945513303461"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /> (synchronisiert)</translation>
<translation id="6027201098523975773">Geben Sie einen Namen ein</translation>
-<translation id="6039846035001940113">Falls das Problem weiterhin besteht, wenden Sie sich an den Inhaber der Website.</translation>
+<translation id="6039846035001940113">Falls das Problem weiterhin besteht, wenden Sie sich an den Eigentümer der Website.</translation>
<translation id="6040143037577758943">Schließen</translation>
<translation id="6047233362582046994">Wenn Sie die Sicherheitsrisiken kennen, können Sie <ph name="BEGIN_LINK" />diese Website aufrufen<ph name="END_LINK" />, bevor die schädlichen Apps entfernt wurden.</translation>
<translation id="6047927260846328439">Mit diesen Inhalten wird möglicherweise versucht, Sie zu täuschen und so zur Installation von Software oder der Offenlegung personenbezogener Daten zu bringen. <ph name="BEGIN_LINK" />Trotzdem anzeigen<ph name="END_LINK" /></translation>
@@ -746,13 +743,11 @@
<translation id="6282194474023008486">Postleitzahl</translation>
<translation id="6290238015253830360">Hier werden Ihre vorgeschlagenen Artikel angezeigt</translation>
<translation id="6305205051461490394"><ph name="URL" /> ist nicht erreichbar.</translation>
-<translation id="6319915415804115995">Vor über einem Jahr zuletzt verwendet</translation>
<translation id="6321917430147971392">Überprüfen Sie die DNS-Einstellungen.</translation>
<translation id="6328639280570009161">Deaktivieren Sie die Netzwerkvorhersage.</translation>
<translation id="6328786501058569169">Sie befinden sich auf einer betrügerischen Website</translation>
<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="6342069812937806050">Abgeschlossen</translation>
<translation id="6355080345576803305">Überschreibung öffentlicher Sitzung</translation>
<translation id="6358450015545214790">Was bedeuten diese Hinweise?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 weiterer Vorschlag}other{# weitere Vorschläge}}</translation>
@@ -777,7 +772,6 @@
<translation id="6529602333819889595">&amp;Löschen wiederholen</translation>
<translation id="6534179046333460208">Physical Web-Vorschläge</translation>
<translation id="6550675742724504774">Optionen</translation>
-<translation id="6556915248009097796">Gültig bis: <ph name="EXPIRATION_DATE_ABBR" />, zuletzt verwendet: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Der Administrator hat die Berechtigung noch nicht erteilt</translation>
<translation id="6569060085658103619">Dies ist eine Erweiterungsseite</translation>
<translation id="6596325263575161958">Verschlüsselungsoptionen</translation>
@@ -806,15 +800,20 @@
<translation id="6825578344716086703">Sie haben versucht, auf <ph name="DOMAIN" /> zuzugreifen, der Server hat jedoch ein Zertifikat übermittelt, das einen schwachen Signaturalgorithmus verwendet, zum Beispiel SHA-1. Das bedeutet, dass die vom Server übermittelten Sicherheitsinformationen gefälscht sein könnten und es sich möglicherweise gar nicht um den erwarteten Server handelt, sondern Sie mit einem Hacker kommunizieren.</translation>
<translation id="6831043979455480757">Übersetzen</translation>
<translation id="6839929833149231406">Stadtteil</translation>
+<translation id="6852204201400771460">App neu laden?</translation>
+<translation id="6865412394715372076">Diese Karte kann momentan nicht geprüft werden</translation>
<translation id="6874604403660855544">&amp;Hinzufügen wiederholen</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Richtlinienebene wird nicht unterstützt.</translation>
<translation id="6895330447102777224">Ihre Karte wurde bestätigt</translation>
<translation id="6897140037006041989">User-Agent</translation>
+<translation id="6903319715792422884">Sie können uns dabei helfen, Safe Browsing weiter zu verbessern, <ph name="BEGIN_WHITEPAPER_LINK" />indem Sie einige Systeminformationen und Seiteninhalte an Google senden<ph name="END_WHITEPAPER_LINK" />. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Nutzer:</translation>
+<translation id="6944692733090228304">Sie haben Ihr Passwort auf einer Website eingegeben, die nicht von <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> verwaltet wird. Zum Schutz Ihres Kontos sollten Sie das Passwort nicht für andere Apps und Websites verwenden.</translation>
<translation id="6945221475159498467">Auswählen</translation>
<translation id="6948701128805548767">Wählen Sie eine Adresse aus, um Abholoptionen und -anforderungen zu sehen</translation>
<translation id="6949872517221025916">Passwort zurücksetzen</translation>
+<translation id="6950684638814147129">Fehler beim Parsen des JSON-Werts: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Das Zertifikat des Servers ist möglicherweise eine Fälschung.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Gerät</translation>
@@ -876,6 +875,7 @@
&lt;li&gt;Klicken Sie auf &lt;strong&gt;Übernehmen&lt;/strong&gt; und dann auf &lt;strong&gt;OK&lt;/strong&gt;.
&lt;li&gt;In der &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome-Hilfe&lt;/a&gt; finden Sie eine Anleitung dazu, wie Sie die Software von Ihrem Computer entfernen.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Passwörter verwalten…</translation>
<translation id="7419106976560586862">Profilpfad</translation>
<translation id="7437289804838430631">Kontaktdaten hinzufügen</translation>
<translation id="7441627299479586546">Falsche(r) Nutzername/Domain der Richtlinie</translation>
@@ -884,7 +884,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Weitere Informationen<ph name="END_LINK" /> zu diesem Problem.</translation>
<translation id="7455133967321480974">Globalen Standard verwenden (Blockieren)</translation>
<translation id="7460163899615895653">Ihre zuletzt geöffneten Tabs von anderen Geräten erscheinen hier</translation>
-<translation id="7469372306589899959">Karte wird bestätigt</translation>
<translation id="7473891865547856676">Nein danke</translation>
<translation id="7481312909269577407">Vorwärts</translation>
<translation id="7485870689360869515">Keine Daten gefunden</translation>
@@ -1014,6 +1013,7 @@
<translation id="8308427013383895095">Die Übersetzung ist aufgrund eines Problems mit der Netzwerkverbindung fehlgeschlagen.</translation>
<translation id="8311129316111205805">Sitzung laden</translation>
<translation id="8332188693563227489">Der Zugriff auf <ph name="HOST_NAME" /> wurde verweigert</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Wenn Sie die Sicherheitsrisiken kennen, können Sie <ph name="BEGIN_LINK" />diese Website aufrufen<ph name="END_LINK" />, bevor die schädlichen Programme entfernt wurden.</translation>
<translation id="8349305172487531364">Lesezeichenleiste</translation>
<translation id="8363502534493474904">Flugmodus ausschalten</translation>
@@ -1034,21 +1034,25 @@
<translation id="8498891568109133222">Die Antwort von <ph name="HOST_NAME" /> hat zu lange gedauert.</translation>
<translation id="8503559462189395349">Chrome-Passwörter</translation>
<translation id="8503813439785031346">Nutzername</translation>
+<translation id="8508648098325802031">Symbol "Suche"</translation>
<translation id="8543181531796978784">Sie können ein <ph name="BEGIN_ERROR_LINK" />Erkennungsproblem melden<ph name="END_ERROR_LINK" /> oder, wenn Sie die Sicherheitsrisiken kennen, <ph name="BEGIN_LINK" />diese unsichere Website aufrufen<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Fragen? Wenden Sie sich an die Person, die Ihr Profil überwacht.</translation>
<translation id="8553075262323480129">Die Übersetzung ist fehlgeschlagen, weil die Sprache der Seite nicht ermittelt werden konnte.</translation>
<translation id="8557066899867184262">Der CVC befindet sich auf der Rückseite Ihrer Karte.</translation>
<translation id="8559762987265718583">Es kann keine private Verbindung zu <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> hergestellt werden, weil Datum und Uhrzeit Ihres Geräts falsch sind (<ph name="DATE_AND_TIME" />).</translation>
+<translation id="8564985650692024650">Chromium empfiehlt, Ihr Passwort für <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> zurückzusetzen, wenn Sie es auf anderen Websites verwendet haben.</translation>
<translation id="8571890674111243710">Seite wird in folgende Sprache übersetzt: <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Weitere Nummer
</translation>
<translation id="859285277496340001">In dem Zertifikat ist kein Mechanismus angegeben, mit dem geprüft werden kann, ob es zurückgerufen wurde.</translation>
+<translation id="860043288473659153">Name des Karteninhabers</translation>
<translation id="8620436878122366504">Deine Eltern haben die Berechtigung noch nicht erteilt</translation>
<translation id="8625384913736129811">Diese Karte für dieses Gerät speichern</translation>
-<translation id="8639963783467694461">AutoFill-Einstellungen</translation>
+<translation id="8663226718884576429">Bestellübersicht, <ph name="TOTAL_LABEL" />, weitere Details</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, Antwort, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Die Verbindung zu <ph name="DOMAIN" /> ist nicht verschlüsselt.</translation>
<translation id="8718314106902482036">Zahlung nicht abgeschlossen</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, Suchvorschlag</translation>
<translation id="8725066075913043281">Erneut versuchen</translation>
<translation id="8728672262656704056">Sie haben den Inkognitomodus aktiviert</translation>
<translation id="8730621377337864115">Fertig</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Lesezeichen</translation>
<translation id="883848425547221593">Andere Lesezeichen</translation>
<translation id="884264119367021077">Versandadresse</translation>
+<translation id="8846319957959474018">Apps einfach über Lesezeichen öffnen</translation>
<translation id="884923133447025588">Kein Sperrmechanismus gefunden.</translation>
<translation id="885730110891505394">Datenfreigabe an Google</translation>
+<translation id="8858065207712248076">Chrome empfiehlt, Ihr Passwort für <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> zurückzusetzen, wenn Sie es auf anderen Websites verwendet haben.</translation>
<translation id="8866481888320382733">Fehler beim Parsen der Richtlinieneinstellungen</translation>
<translation id="8870413625673593573">Kürzlich geschlossen</translation>
<translation id="8874824191258364635">Geben Sie eine gültige Kartennummer ein</translation>
@@ -1105,6 +1111,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> schützt Ihre Daten in der Regel durch Verschlüsselung. Als Chromium dieses Mal versuchte, eine Verbindung zu <ph name="SITE" /> herzustellen, gab die Website ungewöhnliche und falsche Anmeldedaten zurück. Entweder versucht ein Angreifer, sich als <ph name="SITE" /> auszugeben, oder die Verbindung wurde durch eine WLAN-Anmeldeseite unterbrochen. Da Chromium die Verbindung vor dem Austausch von Daten unterbrochen hat, sind Ihre Informationen weiterhin sicher.</translation>
<translation id="9106062320799175032">Rechnungsadresse hinzufügen</translation>
<translation id="910908805481542201">Helfen Sie mir, dieses Problem zu beheben</translation>
+<translation id="9114524666733003316">Karte wird bestätigt…</translation>
<translation id="9128870381267983090">Mit Netzwerk verbinden</translation>
<translation id="9137013805542155359">Original anzeigen</translation>
<translation id="9137248913990643158">Melden Sie sich in Chrome an, bevor Sie diese App nutzen.</translation>
@@ -1115,6 +1122,7 @@
<translation id="9168814207360376865">Ermöglicht Websites zu überprüfen, ob Sie eine Zahlungsmethode gespeichert haben</translation>
<translation id="9169664750068251925">Auf dieser Website immer blockieren</translation>
<translation id="9170848237812810038">&amp;Rückgängig</translation>
+<translation id="9171296965991013597">App schließen?</translation>
<translation id="917450738466192189">Das Serverzertifikat ist ungültig.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> verwendet ein nicht unterstütztes Protokoll.</translation>
<translation id="9205078245616868884">Ihre Daten sind mit Ihrer Synchronisierungspassphrase verschlüsselt. Geben Sie diese ein, um die Synchronisierung zu starten.</translation>
diff --git a/chromium/components/strings/components_strings_el.xtb b/chromium/components/strings/components_strings_el.xtb
index 4b100738e36..5649700667b 100644
--- a/chromium/components/strings/components_strings_el.xtb
+++ b/chromium/components/strings/components_strings_el.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Απόκρυψη τιμής</translation>
<translation id="1228893227497259893">Εσφαλμένο αναγνωριστικό οντότητας</translation>
<translation id="1232569758102978740">Χωρίς τίτλο</translation>
+<translation id="1250759482327835220">Για πιο γρήγορες πληρωμές στο μέλλον, αποθηκεύστε τα στοιχεία της κάρτας σας, το όνομα και τη διεύθυνση χρέωσης στον Λογαριασμό σας Google.</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;
@@ -97,7 +98,6 @@
&lt;p&gt;Προσαρμόστε την ημερομηνία και την ώρα από την ενότητα &lt;strong&gt;Γενικές&lt;/strong&gt; της εφαρμογής &lt;strong&gt;Ρυθμίσεις&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Παρουσιάστηκε πρόβλημα κατά την εμφάνιση αυτής της ιστοσελίδας.</translation>
-<translation id="1590457302292452960">Δημιουργήστε έναν ισχυρό κωδικό πρόσβασης…</translation>
<translation id="1592005682883173041">Πρόσβαση σε τοπικά δεδομένα</translation>
<translation id="1594030484168838125">Επιλογή</translation>
<translation id="1620510694547887537">Κάμερα</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Προσπαθήστε να επικοινωνήσετε με το διαχειριστή συστήματος.</translation>
<translation id="1740951997222943430">Εισαγάγετε έναν έγκυρο μήνα λήξης</translation>
+<translation id="1743520634839655729">Για πιο γρήγορες πληρωμές στο μέλλον, αποθηκεύστε τα στοιχεία της κάρτας σας, το όνομα και τη διεύθυνση χρέωσης στον Λογαριασμό σας Google και σε αυτήν τη συσκευή.</translation>
<translation id="17513872634828108">Ανοικτές καρτέλες</translation>
<translation id="1753706481035618306">Αριθμός σελίδας</translation>
<translation id="1763864636252898013">Ο διακομιστής δεν μπόρεσε να αποδείξει ότι είναι <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του δεν θεωρείται έμπιστο από το λειτουργικό σύστημα της συσκευής σας. Αυτό μπορεί να οφείλεται σε λανθασμένη ρύθμιση ή σε κάποιον τρίτο που επιτίθεται στη σύνδεσή σας.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Οι ανοιχτές καρτέλες σας εμφανίζονται εδώ</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Όνομα κατόχου κάρτας</translation>
-<translation id="1806541873155184440">Προστέθηκε <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Μη έγκυρο αίτημα ή παράμετροι αιτήματος</translation>
<translation id="1826516787628120939">Έλεγχος</translation>
<translation id="1834321415901700177">Αυτός ο ιστότοπος περιέχει κακόβουλα προγράμματα</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 πρόταση}other{# προτάσεις}}</translation>
<translation id="2079545284768500474">Αναίρεση</translation>
<translation id="20817612488360358">Οι ρυθμίσεις διακομιστή μεσολάβησης του συστήματος έχουν οριστεί για να χρησιμοποιηθούν, αλλά καθορίζεται επίσης μια ρητή διαμόρφωση του διακομιστή μεσολάβησης.</translation>
-<translation id="2084558088529668945">Έχετε εισαγάγει τον κωδικό πρόσβασής σας σε έναν ιστότοπο τον οποίο δεν διαχειρίζεται ο οργανισμός <ph name="ORG_NAME" />. Για να προστατεύσετε τον λογαριασμό σας, μην επαναχρησιμοποιείτε τον κωδικό πρόσβασής σας σε άλλες εφαρμογές και ιστοτόπους.</translation>
<translation id="2091887806945687916">Ήχος</translation>
<translation id="2094505752054353250">Αναντιστοιχία τομέα</translation>
<translation id="2096368010154057602">Νομός</translation>
+<translation id="2102134110707549001">Πρόταση για ισχυρό κωδικό πρόσβασης…</translation>
<translation id="2108755909498034140">Επανεκκινήστε τον υπολογιστή σας</translation>
<translation id="2113977810652731515">Παιχνίδια με κάρτες</translation>
<translation id="2114841414352855701">Αγνοήθηκε επειδή αντικαταστάθηκε από την πολιτική <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Σφάλμα HTTP</translation>
<translation id="2270484714375784793">Αριθμός τηλεφώνου</translation>
<translation id="2292556288342944218">Η πρόσβασή σας στο διαδίκτυο είναι αποκλεισμένη</translation>
-<translation id="230155334948463882">Νέα κάρτα;</translation>
<translation id="2316887270356262533">Απελευθερώνει λιγότερο από 1 MB. Ορισμένοι ιστότοποι μπορεί να φορτωθούν πιο αργά κατά την επόμενη επίσκεψή σας.</translation>
<translation id="2317259163369394535">Ο τομέας <ph name="DOMAIN" /> απαιτεί ένα όνομα χρήστη και έναν κωδικό πρόσβασης.</translation>
<translation id="2317583587496011522">Οι χρεωστικές κάρτες γίνονται δεκτές.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Αποδεκτές κάρτες</translation>
<translation id="2702801445560668637">Λίστα ανάγνωσης</translation>
<translation id="2704283930420550640">Η τιμή δεν συμφωνεί με τη μορφή.</translation>
-<translation id="2704951214193499422">Δεν ήταν δυνατή η επιβεβαίωση της κάρτας σας από το Chromium αυτήν τη στιγμή. Δοκιμάστε ξανά αργότερα.</translation>
<translation id="2705137772291741111">Δεν είναι δυνατή η ανάγνωση του αποθηκευμένου αντιγράφου (κρυφής μνήμης) αυτού του ιστότοπου.</translation>
<translation id="2709516037105925701">Αυτόματη συμπλήρωση</translation>
<translation id="2710942282213947212">Κάποιο λογισμικό στον υπολογιστή σας παρεμποδίζει την ασφαλή σύνδεση του Chromium στον ιστό</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Τρόπος αποστολής</translation>
<translation id="277499241957683684">Λείπει κάποιο αρχείο συσκευής</translation>
<translation id="2781030394888168909">Εξαγωγή για MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Έγινε επαναφορά της σύνδεσης.</translation>
<translation id="2788784517760473862">Αποδεκτές πιστωτικές κάρτες</translation>
<translation id="2794233252405721443">Ο ιστότοπος έχει αποκλειστεί</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Μπορείτε να απενεργοποιήσετε τυχόν διακομιστές μεσολάβησης που έχουν διαμορφωθεί για μια σύνδεση από τη σελίδα ρυθμίσεων.</translation>
<translation id="2955913368246107853">Κλείσιμο γραμμής εύρεσης</translation>
<translation id="2958431318199492670">Η διαμόρφωση δικτύου δεν συμμορφώνεται με το πρότυπο ONC. Ορισμένα τμήματα αυτής της διαμόρφωσης ενδέχεται να μην εισαχθούν.</translation>
-<translation id="2966678944701946121">Λήξη: <ph name="EXPIRATION_DATE_ABBR" />, προστέθηκε <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Για την επίτευξη μιας ασφαλούς σύνδεσης, θα πρέπει να γίνει σωστή ρύθμιση του ρολογιού σας. Αυτό οφείλεται στο γεγονός ότι τα πιστοποιητικά που χρησιμοποιούν οι ιστότοποι για την ταυτοποίησή τους είναι έγκυρα μόνο για συγκεκριμένες χρονικές περιόδους. Εφόσον το ρολόι της συσκευής σας δεν είναι σωστά ρυθμισμένο, το Google Chrome δεν μπορεί να επαληθεύσει αυτά τα πιστοποιητικά.</translation>
<translation id="2972581237482394796">&amp;Επανάληψη ενέργειας</translation>
<translation id="2977665033722899841">Έχει επιλεγεί ο λογαριασμός <ph name="ROW_NAME" />. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Λανθασμένος τύπος πολιτικής</translation>
<translation id="3037605927509011580">Όπα! Κάτι πήγε στραβά!</translation>
<translation id="3041612393474885105">Πληροφορίες πιστοποιητικού</translation>
-<translation id="3063697135517575841">Δεν ήταν δυνατή η επιβεβαίωση της κάρτας σας από το Chrome αυτήν τη στιγμή. Δοκιμάστε ξανά αργότερα.</translation>
<translation id="3064966200440839136">Αποχώρηση από την κατάσταση ανώνυμης περιήγησης για πληρωμή μέσω εξωτερικής εφαρμογής. Συνέχεια;</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Κανένας}=1{1 κωδικός πρόσβασης}other{# κωδικοί πρόσβασης}}</translation>
<translation id="3096100844101284527">Προσθήκη διεύθυνσης παραλαβής</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Τα δεδομένα σας κρυπτογραφήθηκαν με τη δική σας φράση πρόσβασης συγχρονισμού στις <ph name="TIME" />. Πληκτρολογήστε την για να ξεκινήσει ο συγχρονισμός.</translation>
<translation id="3320021301628644560">Προσθήκη διεύθυνσης χρέωσης</translation>
<translation id="3338095232262050444">Ασφαλές</translation>
-<translation id="3340978935015468852">ρυθμίσεις</translation>
<translation id="3345135638360864351">Δεν ήταν δυνατή η αποστολή του αιτήματος πρόσβασής σας σε αυτόν τον ιστότοπο σε <ph name="NAME" />. Δοκιμάστε ξανά.</translation>
<translation id="3355823806454867987">Αλλαγή ρυθμίσεων διακομιστή μεσολάβησης...</translation>
<translation id="3361596688432910856">Το Chrome <ph name="BEGIN_EMPHASIS" />δεν θα αποθηκεύει<ph name="END_EMPHASIS" /> τις παρακάτω πληροφορίες:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Το πιστοποιητικό του διακομιστή δεν είναι αξιόπιστο.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Τουλάχιστον ένα στοιχείο στις συγχρονισμένες συσκευές}=1{1 στοιχείο (και περισσότερα στις συγχρονισμένες συσκευές)}other{# στοιχεία (και περισσότερα στις συγχρονισμένες συσκευές)}}</translation>
<translation id="3539171420378717834">Διατήρηση αντιγράφου αυτής της κάρτας σε αυτήν τη συσκευή</translation>
-<translation id="3542684924769048008">Χρήση κωδικού πρόσβασης για:</translation>
<translation id="3549644494707163724">Κρυπτογράφηση όλων των συγχρονισμένων δεδομένων με τη δική σας φράση πρόσβασης συγχρονισμού</translation>
<translation id="3556433843310711081">Ο διαχειριστής σας μπορεί να καταργήσει τον αποκλεισμό του για εσάς</translation>
<translation id="3566021033012934673">Η σύνδεσή σας δεν είναι ιδιωτική</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Εσφαλμένη υπογραφή επαλήθευσης</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> ακόμη στοιχείο}other{<ph name="ITEM_COUNT" /> ακόμη στοιχεία}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Το Chrome συνιστά την επαναφορά του κωδικού πρόσβασης <ph name="ORG_NAME" /> εάν τον έχετε επαναχρησιμοποιήσει σε άλλους ιστοτόπους.</translation>
<translation id="4196861286325780578">&amp;Επανάληψη μετακίνησης</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Ελέγξτε τις διαμορφώσεις του τείχους προστασίας και της προστασίας από ιούς<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Απότομες διακοπές λειτουργίας</translation>
@@ -491,7 +485,6 @@
<translation id="425582637250725228">Οι αλλαγές που κάνατε ενδέχεται να μην έχουν αποθηκευτεί.</translation>
<translation id="4258748452823770588">Εσφαλμένη υπογραφή</translation>
<translation id="4265872034478892965">Επιτρέπεται από τον διαχειριστή σας</translation>
-<translation id="4269787794583293679">(Χωρίς όνομα χρήστη)</translation>
<translation id="4275830172053184480">Επανεκκινήστε τη συσκευή σας</translation>
<translation id="4277028893293644418">Επαναφορά κωδικού πρόσβασης</translation>
<translation id="4280429058323657511">, λήξη <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -514,6 +507,7 @@
<translation id="4434045419905280838">Αναδυόμενα παράθυρα και ανακατευθύνσεις</translation>
<translation id="443673843213245140">Η χρήση ενός διακομιστή μεσολάβησης είναι απενεργοποιημένη, αλλά έχει καθοριστεί μια ρητή διαμόρφωση διακομιστή μεσολάβησης.</translation>
<translation id="445100540951337728">Αποδεκτές χρεωστικές κάρτες</translation>
+<translation id="4472575034687746823">Έναρξη</translation>
<translation id="4506176782989081258">Σφάλμα επικύρωσης: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Επικοινωνήστε με το διαχειριστή συστήματος</translation>
<translation id="450710068430902550">Κοινοποίηση στο διαχειριστή</translation>
@@ -538,8 +532,8 @@
<translation id="4726672564094551039">Επανάληψη φόρτωσης πολιτικών</translation>
<translation id="4728558894243024398">Πλατφόρμα</translation>
<translation id="4736825316280949806">Επανεκκινήστε το Chromium</translation>
+<translation id="4742407542027196863">Διαχείριση κωδικών πρόσβασης…</translation>
<translation id="4744603770635761495">Διαδρομή εκτελέσιμου</translation>
-<translation id="4749685221585524849">Τελευταία χρήση <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Οι πληροφορίες σας (για παράδειγμα, οι κωδικοί πρόσβασης ή οι αριθμοί πιστωτικών καρτών) είναι ιδιωτικές κατά την αποστολή σε αυτόν τον ιστότοπο.</translation>
<translation id="4756388243121344051">&amp;Ιστορικό</translation>
<translation id="4758311279753947758">Προσθήκη στοιχείων επικοινωνίας</translation>
@@ -556,6 +550,7 @@
<translation id="4850886885716139402">Προβολή</translation>
<translation id="4854362297993841467">Αυτός ο τρόπος παράδοσης δεν είναι διαθέσιμος. Δοκιμάστε έναν άλλο τρόπο.</translation>
<translation id="4858792381671956233">Ρώτησες τους γονείς σου εάν σου επιτρέπουν να επισκεφτείς αυτόν τον ιστότοπο</translation>
+<translation id="4876305945144899064">Δεν υπάρχει όνομα χρήστη</translation>
<translation id="4880827082731008257">Αναζήτηση ιστορικού</translation>
<translation id="4881695831933465202">Άνοιγμα</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -589,6 +584,7 @@
<translation id="5089810972385038852">Πολιτεία</translation>
<translation id="5094747076828555589">Ο διακομιστής δεν μπορεί να αποδείξει ότι είναι το <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του δεν θεωρείται έμπιστο από το Chromium. Αυτό μπορεί να οφείλεται σε λανθασμένη ρύθμιση ή σε κάποιον τρίτο που επιτίθεται στη σύνδεσή σας.</translation>
<translation id="5095208057601539847">Επαρχία</translation>
+<translation id="5098332213681597508">Αυτό το όνομα προέρχεται από τον Λογαριασμό σας Google.</translation>
<translation id="5115563688576182185">(64-bit)</translation>
<translation id="5121084798328133320">Μετά την επιβεβαίωση, τα στοιχεία της κάρτας από τον λογαριασμό πληρωμών Google θα κοινοποιηθούν σε αυτόν τον ιστότοπο.</translation>
<translation id="5128122789703661928">Η περίοδος σύνδεσης με αυτό το όνομα δεν είναι έγκυρη για διαγραφή.</translation>
@@ -629,9 +625,10 @@
<translation id="5332219387342487447">Μέθοδος αποστολής</translation>
<translation id="5355557959165512791">Δεν μπορείτε να επισκεφτείτε τον ιστότοπο <ph name="SITE" /> αυτήν τη στιγμή επειδή το πιστοποιητικό έχει ανακληθεί. Τα σφάλματα δικτύου και οι επιθέσεις είναι συνήθως προσωρινά, συνεπώς αυτή η σελίδα πιθανότατα θα λειτουργήσει αργότερα.</translation>
<translation id="536296301121032821">Αποτυχία αποθήκευσης ρυθμίσεων πολιτικής</translation>
+<translation id="5371425731340848620">Ενημέρωση κάρτας</translation>
<translation id="5377026284221673050">"Το ρολόι σας πάει πίσω" ή "Το ρολόι σας πάει μπροστά" ή "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">Η αλυσίδα πιστοποιητικού για αυτόν τον ιστότοπο περιέχει ένα πιστοποιητικό το οποίο είναι υπογεγραμμένο με χρήση SHA-1.</translation>
-<translation id="5402410679244714488">Λήξη: <ph name="EXPIRATION_DATE_ABBR" />, τελευταία χρήση πριν από περισσότερο από ένα έτος</translation>
+<translation id="5387961145478138773">Αποκτήστε γρήγορη πρόσβαση στις αγαπημένες σας Εφαρμογές Google</translation>
<translation id="540969355065856584">Αυτός ο διακομιστής δεν μπόρεσε να αποδείξει ότι είναι <ph name="DOMAIN" />. Το πιστοποιητικό ασφαλείας του δεν είναι έγκυρο αυτήν τη στιγμή. Αυτό μπορεί να οφείλεται σε εσφαλμένη ρύθμιση ή σε κάποιον εισβολέα που παρεμβαίνει στη σύνδεσή σας.</translation>
<translation id="5421136146218899937">Διαγραφή δεδομένων περιήγησης...</translation>
<translation id="5430298929874300616">Κατάργηση σελιδοδείκτη</translation>
@@ -673,11 +670,13 @@
<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="5659593005791499971">Διεύθυνση ηλεκτρονικού ταχυδρομείου</translation>
+<translation id="5666899935841546222">Θέλετε να έχετε όλες τις κάρτες σας σε ένα μέρος;</translation>
<translation id="5675650730144413517">Αυτή η σελίδα δεν λειτουργεί</translation>
<translation id="5685654322157854305">Προσθήκη διεύθυνσης αποστολής</translation>
<translation id="5689199277474810259">Εξαγωγή σε JSON</translation>
<translation id="5689516760719285838">Τοποθεσία</translation>
<translation id="570530837424789914">Διαχείριση…</translation>
+<translation id="57094364128775171">Πρόταση για ισχυρό κωδικό πρόσβασης…</translation>
<translation id="5710435578057952990">Η ταυτότητα αυτού του ιστότοπου δεν έχει επαληθευτεί.</translation>
<translation id="5719499550583120431">Οι προπληρωμένες κάρτες γίνονται δεκτές.</translation>
<translation id="5720705177508910913">Τρέχων χρήστης</translation>
@@ -698,11 +697,9 @@
<translation id="5869405914158311789">Δεν είναι δυνατή η πρόσβαση σε αυτόν τον ιστότοπο</translation>
<translation id="5869522115854928033">Αποθηκευμένοι κωδικοί πρόσβασης</translation>
<translation id="5893752035575986141">Οι πιστωτικές κάρτες γίνονται δεκτές.</translation>
-<translation id="5898382028489516745">Το Chromium συνιστά την επαναφορά του κωδικού πρόσβασης <ph name="ORG_NAME" /> εάν τον έχετε επαναχρησιμοποιήσει σε άλλους ιστοτόπους.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (συγχρονισμένο)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 σε χρήση}other{# σε χρήση}}</translation>
<translation id="5939518447894949180">Επαναφορά</translation>
-<translation id="5959728338436674663">Αυτόματη αποστολή ορισμένων <ph name="BEGIN_WHITEPAPER_LINK" />πληροφοριών συστήματος και περιεχομένου σελίδων<ph name="END_WHITEPAPER_LINK" /> στην Google για διευκόλυνση του εντοπισμού επικίνδυνων εφαρμογών και ιστοτόπων<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Επεξεργασία στοιχείων επικοινωνίας</translation>
<translation id="5967867314010545767">Κατάργηση από το ιστορικό</translation>
<translation id="5975083100439434680">Σμίκρυνση</translation>
@@ -748,13 +745,11 @@
<translation id="6282194474023008486">Ταχυδρομικός κώδικας</translation>
<translation id="6290238015253830360">Τα προτεινόμενα άρθρα σας εμφανίζονται εδώ</translation>
<translation id="6305205051461490394">Δεν είναι δυνατή η πρόσβαση στο <ph name="URL" />.</translation>
-<translation id="6319915415804115995">Τελευταία χρήση πριν από περισσότερο από ένα έτος</translation>
<translation id="6321917430147971392">Ελέγξτε τις ρυθμίσεις DNS</translation>
<translation id="6328639280570009161">Δοκιμάστε να απενεργοποιήσετε την πρόβλεψη δικτύου</translation>
<translation id="6328786501058569169">Αυτός ο ιστότοπος είναι παραπλανητικός</translation>
<translation id="6337133576188860026">Απελευθερώνει λιγότερο από <ph name="SIZE" />. Ορισμένοι ιστότοποι μπορεί να φορτωθούν πιο αργά κατά την επόμενη επίσκεψή σας.</translation>
<translation id="6337534724793800597">Φιλτράρισμα πολιτικών με βάση το όνομα</translation>
-<translation id="6342069812937806050">Πριν λίγο</translation>
<translation id="6355080345576803305">Παράκαμψη δημόσιας περιόδου σύνδεσης</translation>
<translation id="6358450015545214790">Τι σημαίνουν αυτά;</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 πρόταση ακόμα}other{# προτάσεις ακόμα}}</translation>
@@ -779,7 +774,6 @@
<translation id="6529602333819889595">&amp;Επανάληψη διαγραφής</translation>
<translation id="6534179046333460208">Προτάσεις Φυσικού δικτύου</translation>
<translation id="6550675742724504774">Επιλογές</translation>
-<translation id="6556915248009097796">Λήξη: <ph name="EXPIRATION_DATE_ABBR" />, τελευταία χρήση <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Ο διαχειριστής σας δεν τον έχει εγκρίνει ακόμα</translation>
<translation id="6569060085658103619">Βλέπετε μια σελίδα επέκτασης</translation>
<translation id="6596325263575161958">Επιλογές κρυπτογράφησης</translation>
@@ -808,15 +802,20 @@
<translation id="6825578344716086703">Προσπαθήσατε να μεταβείτε στον τομέα <ph name="DOMAIN" />, αλλά ο διακομιστής παρουσίασε ένα πιστοποιητικό το οποίο ήταν υπογεγραμμένο με έναν αδύναμο αλγόριθμο υπογραφής (όπως SHA-1). Αυτό σημαίνει ότι μπορεί να έχουν πλαστογραφηθεί τα διαπιστευτήρια ασφαλείας που επέδειξε ο διακομιστής και ότι αυτός ο διακομιστής ενδέχεται να μην είναι αυτό που αναμένετε (ενδέχεται να επικοινωνείτε με έναν εισβολέα).</translation>
<translation id="6831043979455480757">Μετάφραση</translation>
<translation id="6839929833149231406">Περιοχή</translation>
+<translation id="6852204201400771460">Επανάληψη φόρτωσης εφαρμογής;</translation>
+<translation id="6865412394715372076">Δεν είναι δυνατή η επαλήθευση της κάρτας αυτήν τη στιγμή</translation>
<translation id="6874604403660855544">&amp;Επανάληψη προσθήκης</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Το επίπεδο πολιτικής δεν υποστηρίζεται.</translation>
<translation id="6895330447102777224">Η κάρτα σας επιβεβαιώθηκε</translation>
<translation id="6897140037006041989">Παράγοντας χρήστη</translation>
+<translation id="6903319715792422884">Συμβάλετε στη βελτίωση της Ασφαλούς περιήγησης στέλνοντας ορισμένες <ph name="BEGIN_WHITEPAPER_LINK" />πληροφορίες συστήματος και περιεχόμενο σελίδων<ph name="END_WHITEPAPER_LINK" /> στην Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Χρήστης</translation>
+<translation id="6944692733090228304">Καταχωρίσατε τον κωδικό πρόσβασής σας σε έναν ιστότοπο τον οποίο δεν διαχειρίζεται ο οργανισμός <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Για να προστατεύσετε τον λογαριασμό σας, μην χρησιμοποιήσετε ξανά αυτόν τον κωδικό πρόσβασης σε άλλες εφαρμογές και ιστότοπους.</translation>
<translation id="6945221475159498467">Επιλογή</translation>
<translation id="6948701128805548767">Για να δείτε τρόπους και απαιτήσεις παραλαβής, επιλέξτε μια διεύθυνση</translation>
<translation id="6949872517221025916">Επαναφορά κωδικού πρόσβασης</translation>
+<translation id="6950684638814147129">Σφάλμα κατά την ανάλυση της τιμής JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Το πιστοποιητικό του διακομιστή φαίνεται να είναι πλαστό.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Συσκευή</translation>
@@ -878,6 +877,7 @@
&lt;li&gt;Κάντε κλικ στο κουμπί &lt;strong&gt;Εφαρμογή&lt;/strong&gt; και, έπειτα, στο κουμπί &lt;strong&gt;OK&lt;/strong&gt;
&lt;li&gt;Επισκεφτείτε το &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Κέντρο βοήθειας του Chrome&lt;/a&gt;, για να μάθετε πώς μπορείτε να καταργήσετε οριστικά το λογισμικό από τον υπολογιστή σας
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Διαχείριση κωδικών πρόσβασης…</translation>
<translation id="7419106976560586862">Διαδρομή προφίλ</translation>
<translation id="7437289804838430631">Προσθήκη στοιχείων επικοινωνίας</translation>
<translation id="7441627299479586546">Εσφαλμένο θέμα πολιτικής</translation>
@@ -886,7 +886,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Να μάθετε περισσότερα<ph name="END_LINK" /> σχετικά με αυτό το πρόβλημα.</translation>
<translation id="7455133967321480974">Χρήση καθολικής προεπιλεγμένης ρύθμισης (Αποκλεισμός)</translation>
<translation id="7460163899615895653">Οι πρόσφατες καρτέλες σας από άλλες συσκευές εμφανίζονται εδώ</translation>
-<translation id="7469372306589899959">Επιβεβαίωση κάρτας</translation>
<translation id="7473891865547856676">Όχι, ευχαριστώ</translation>
<translation id="7481312909269577407">Προώθηση</translation>
<translation id="7485870689360869515">Δεν βρέθηκαν δεδομένα</translation>
@@ -1016,6 +1015,7 @@
<translation id="8308427013383895095">Η μετάφραση απέτυχε λόγω προβλήματος με τη σύνδεση δικτύου.</translation>
<translation id="8311129316111205805">Φόρτωση περιόδου λειτουργίας</translation>
<translation id="8332188693563227489">Απορρίφθηκε η πρόσβαση στο κεντρικό υπολογιστή <ph name="HOST_NAME" /></translation>
+<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="8349305172487531364">Γραμμή σελιδοδεικτών</translation>
<translation id="8363502534493474904">Απενεργοποιήστε τη λειτουργία πτήσης</translation>
@@ -1036,21 +1036,25 @@
<translation id="8498891568109133222">Ο κεντρικός υπολογιστής <ph name="HOST_NAME" /> άργησε πολύ να ανταποκριθεί.</translation>
<translation id="8503559462189395349">Κωδικοί πρόσβασης Chrome</translation>
<translation id="8503813439785031346">Όνομα χρήστη</translation>
+<translation id="8508648098325802031">Εικονίδιο αναζήτησης</translation>
<translation id="8543181531796978784">Μπορείτε να <ph name="BEGIN_ERROR_LINK" />αναφέρετε ένα πρόβλημα εντοπισμού<ph name="END_ERROR_LINK" /> ή, εάν κατανοείτε τους κινδύνους ασφαλείας, να <ph name="BEGIN_LINK" />επισκεφτείτε αυτόν τον μη ασφαλή ιστότοπο<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Έχετε ερωτήσεις; Επικοινωνήστε με το άτομο που επιβλέπει το προφίλ σας.</translation>
<translation id="8553075262323480129">Η μετάφραση απέτυχε επειδή δεν ήταν δυνατός ο προσδιορισμός της σελίδας.</translation>
<translation id="8557066899867184262">Ο κωδικός CVC βρίσκεται στο πίσω μέρος της κάρτας.</translation>
<translation id="8559762987265718583">Δεν είναι δυνατή η επίτευξη ιδιωτικής σύνδεσης με τον τομέα <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> επειδή η ημερομηνία και η ώρα (<ph name="DATE_AND_TIME" />) της συσκευής σας είναι λανθασμένες.</translation>
+<translation id="8564985650692024650">Το Chromium συνιστά την επαναφορά του κωδικού πρόσβασης <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, εάν τον έχετε χρησιμοποιήσει και σε άλλους ιστοτόπους.</translation>
<translation id="8571890674111243710">Μετάφραση σελίδας σε <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Προσθ. τηλεφ.
</translation>
<translation id="859285277496340001">Αυτό το πιστοποιητικό δεν καθορίζει κάποιο μηχανισμό για να ελέγχεται εάν έχει ανακληθεί.</translation>
+<translation id="860043288473659153">Όνομα κατόχου κάρτας</translation>
<translation id="8620436878122366504">Οι γονείς σας δεν τον έχουν εγκρίνει ακόμα</translation>
<translation id="8625384913736129811">Αποθήκευση αυτής της κάρτας στη συγκεκριμένη συσκευή</translation>
-<translation id="8639963783467694461">Ρυθμίσεις αυτόματης συμπλήρωσης</translation>
+<translation id="8663226718884576429">Σύνοψη παραγγελίας, <ph name="TOTAL_LABEL" />, Περισσότερες λεπτομέρειες</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, απάντηση, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Η σύνδεσή σας με τον τομέα <ph name="DOMAIN" /> δεν είναι κρυπτογραφημένη.</translation>
<translation id="8718314106902482036">Η πληρωμή δεν ολοκληρώθηκε</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, πρόταση αναζήτησης</translation>
<translation id="8725066075913043281">Προσπαθήστε ξανά</translation>
<translation id="8728672262656704056">Πραγματοποιείτε ανώνυμη περιήγηση</translation>
<translation id="8730621377337864115">Ολοκληρώθηκε</translation>
@@ -1066,8 +1070,10 @@
<translation id="8820817407110198400">Σελιδοδείκτες</translation>
<translation id="883848425547221593">Άλλοι σελιδοδείκτες</translation>
<translation id="884264119367021077">Διεύθυνση αποστολής</translation>
+<translation id="8846319957959474018">Εύκολο άνοιγμα εφαρμογών με σελιδοδείκτες</translation>
<translation id="884923133447025588">Δεν εντοπίστηκε μηχανισμός ακύρωσης.</translation>
<translation id="885730110891505394">Κοινοποίηση στις υπηρεσίες της Google</translation>
+<translation id="8858065207712248076">Το Chrome συνιστά την επαναφορά του κωδικού πρόσβασης <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> εάν τον έχετε χρησιμοποιήσει και σε άλλους ιστοτόπους.</translation>
<translation id="8866481888320382733">Σφάλμα ανάλυσης ρυθμίσεων πολιτικής</translation>
<translation id="8870413625673593573">Έκλεισαν πρόσφατα</translation>
<translation id="8874824191258364635">Εισαγάγετε έναν έγκυρο αριθμό κάρτας</translation>
@@ -1106,6 +1112,7 @@
<translation id="9103872766612412690">Κανονικά, ο ιστότοπος <ph name="SITE" /> χρησιμοποιεί κρυπτογράφηση για να προστατεύει τα στοιχεία σας. Όταν το Chromium επιχείρησε πρόσφατα να συνδεθεί στο <ph name="SITE" />, ο ιστότοπος ανταποκρίθηκε δημιουργώντας ασυνήθιστα και εσφαλμένα διαπιστευτήρια. Αυτό μπορεί να συμβεί όταν κάποιος εισβολέας προσπαθεί να υποκριθεί ότι είναι ο ιστότοπος <ph name="SITE" /> ή όταν κάποια οθόνη σύνδεσης Wi-Fi έχει διακόψει τη σύνδεσή σας. Τα στοιχεία σας εξακολουθούν να είναι ασφαλή επειδή το Chromium διέκοψε τη σύνδεση πριν από την ανταλλαγή δεδομένων.</translation>
<translation id="9106062320799175032">Προσθήκη διεύθυνσης χρέωσης</translation>
<translation id="910908805481542201">Βοήθεια για επίλυση του προβλήματος</translation>
+<translation id="9114524666733003316">Επιβεβαίωση κάρτας…</translation>
<translation id="9128870381267983090">Σύνδεση σε δίκτυο</translation>
<translation id="9137013805542155359">Εμφάνιση πρωτοτύπου</translation>
<translation id="9137248913990643158">Εκκινήστε και συνδεθείτε στο Chrome πριν χρησιμοποιήσετε αυτήν την εφαρμογή.</translation>
@@ -1116,6 +1123,7 @@
<translation id="9168814207360376865">Να επιτρέπεται σε ιστοτόπους να ελέγχουν αν έχετε αποθηκεύσει τρόπους πληρωμής</translation>
<translation id="9169664750068251925">Να γίνεται πάντα αποκλεισμός σε αυτόν τον ιστότοπο</translation>
<translation id="9170848237812810038">Αναί&amp;ρεση</translation>
+<translation id="9171296965991013597">Αποχώρηση από την εφαρμογή;</translation>
<translation id="917450738466192189">Το πιστοποιητικό του διακομιστή δεν είναι έγκυρο.</translation>
<translation id="9183425211371246419">Ο κεντρικός υπολογιστής <ph name="HOST_NAME" /> χρησιμοποιεί μη υποστηριζόμενο πρωτόκολλο.</translation>
<translation id="9205078245616868884">Τα δεδομένα σας είναι κρυπτογραφημένα με τη δική σας φράση πρόσβασης συγχρονισμού. Πληκτρολογήστε την για να ξεκινήσει ο συγχρονισμός.</translation>
diff --git a/chromium/components/strings/components_strings_en-GB.xtb b/chromium/components/strings/components_strings_en-GB.xtb
index df604897e69..dac5d5483fd 100644
--- a/chromium/components/strings/components_strings_en-GB.xtb
+++ b/chromium/components/strings/components_strings_en-GB.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Hide value</translation>
<translation id="1228893227497259893">Wrong entity identifier</translation>
<translation id="1232569758102978740">Untitled</translation>
+<translation id="1250759482327835220">To pay faster next time, save your card, name and billing address to your Google account.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (synced)</translation>
<translation id="1256368399071562588">&lt;p&gt;If you try to visit a website and it doesn’t open, first try to fix the error with these troubleshooting steps:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Please adjust the date and time from the &lt;strong&gt;General&lt;/strong&gt; section of the &lt;strong&gt;Settings&lt;/strong&gt; app.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Something went wrong while displaying this web page.</translation>
-<translation id="1590457302292452960">Generate a strong password...</translation>
<translation id="1592005682883173041">Local Data Access</translation>
<translation id="1594030484168838125">Choose</translation>
<translation id="1620510694547887537">Camera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Try contacting the system admin.</translation>
<translation id="1740951997222943430">Enter a valid expiry month</translation>
+<translation id="1743520634839655729">To pay faster next time, save your card, name and billing address to your Google account and to this device.</translation>
<translation id="17513872634828108">Open tabs</translation>
<translation id="1753706481035618306">Page number</translation>
<translation id="1763864636252898013">This server could not prove that it is <ph name="DOMAIN" />; its security certificate is not trusted by your device's operating system. This may be caused by a misconfiguration or an attacker intercepting your connection.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Your open tabs appear here</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Cardholder Name</translation>
-<translation id="1806541873155184440">Added <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Invalid request or request parameters</translation>
<translation id="1826516787628120939">Checking</translation>
<translation id="1834321415901700177">This site contains harmful programs</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 suggestion}other{# suggestions}}</translation>
<translation id="2079545284768500474">Undo</translation>
<translation id="20817612488360358">System proxy settings are set to be used but an explicit proxy configuration is also specified.</translation>
-<translation id="2084558088529668945">You entered your password on a site that’s not managed by <ph name="ORG_NAME" />. To protect your account, don’t reuse your password on other apps and sites.</translation>
<translation id="2091887806945687916">Sound</translation>
<translation id="2094505752054353250">Domain mismatch</translation>
<translation id="2096368010154057602">Department</translation>
+<translation id="2102134110707549001">Suggest strong password…</translation>
<translation id="2108755909498034140">Restart your computer</translation>
<translation id="2113977810652731515">Card</translation>
<translation id="2114841414352855701">Ignored because it was overridden by <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP error</translation>
<translation id="2270484714375784793">Phone number</translation>
<translation id="2292556288342944218">Your Internet access is blocked</translation>
-<translation id="230155334948463882">New card?</translation>
<translation id="2316887270356262533">Frees up less than 1 MB. Some sites may load more slowly on your next visit.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> requires a username and password.</translation>
<translation id="2317583587496011522">Debit cards are accepted.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Accepted Cards</translation>
<translation id="2702801445560668637">Reading List</translation>
<translation id="2704283930420550640">Value doesn't match format.</translation>
-<translation id="2704951214193499422">Chromium was unable to confirm your card at this time. Please try again later.</translation>
<translation id="2705137772291741111">The saved (cached) copy of this site was unreadable.</translation>
<translation id="2709516037105925701">Auto-fill</translation>
<translation id="2710942282213947212">Software on your computer is stopping Chromium from safely connecting to the web</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Delivery method</translation>
<translation id="277499241957683684">Missing device record</translation>
<translation id="2781030394888168909">Export MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">The connection was reset.</translation>
<translation id="2788784517760473862">Accepted credit cards</translation>
<translation id="2794233252405721443">Site blocked</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">You can disable any proxies configured for a connection from the settings page.</translation>
<translation id="2955913368246107853">Close find bar</translation>
<translation id="2958431318199492670">The network configuration doesn't comply to the ONC standard. Parts of the configuration may not be imported.</translation>
-<translation id="2966678944701946121">Exp: <ph name="EXPIRATION_DATE_ABBR" />, added <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">To establish a secure connection, your clock needs to be set correctly. This is because the certificates that websites use to identify themselves are only valid for specific periods of time. Since your device's clock is incorrect, Google Chrome cannot verify these certificates.</translation>
<translation id="2972581237482394796">&amp;Redo</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, currently selected. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Wrong policy type</translation>
<translation id="3037605927509011580">Aw, Snap!</translation>
<translation id="3041612393474885105">Certificate Information</translation>
-<translation id="3063697135517575841">Chrome was unable to confirm your card at this time. Please try again later.</translation>
<translation id="3064966200440839136">Leaving incognito mode to pay via an external application. Continue?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{None}=1{1 password}other{# passwords}}</translation>
<translation id="3096100844101284527">Add pickup address</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Your data was encrypted with your sync passphrase on <ph name="TIME" />. Enter it to start sync.</translation>
<translation id="3320021301628644560">Add billing address</translation>
<translation id="3338095232262050444">Secure</translation>
-<translation id="3340978935015468852">settings</translation>
<translation id="3345135638360864351">Your request to access this site could not be sent to <ph name="NAME" />. Please try again.</translation>
<translation id="3355823806454867987">Change proxy settings...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />won’t save<ph name="END_EMPHASIS" /> the following information:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Server's certificate is not trusted</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{At least 1 item on synced devices}=1{1 item (and more on synced devices)}other{# items (and more on synced devices)}}</translation>
<translation id="3539171420378717834">Keep a copy of this card on this device</translation>
-<translation id="3542684924769048008">Use password for:</translation>
<translation id="3549644494707163724">Encrypt all synced data with your own sync passphrase</translation>
<translation id="3556433843310711081">Your manager can unblock it for you</translation>
<translation id="3566021033012934673">Your connection is not private</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Bad verification signature</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> more item}other{<ph name="ITEM_COUNT" /> more items}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome recommends resetting your <ph name="ORG_NAME" /> password if you reused it on other sites.</translation>
<translation id="4196861286325780578">&amp;Redo move</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Checking firewall and antivirus configurations<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Crashes</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Changes that you made may not be saved.</translation>
<translation id="4258748452823770588">Bad signature</translation>
<translation id="4265872034478892965">Allowed by your administrator</translation>
-<translation id="4269787794583293679">(No username)</translation>
<translation id="4275830172053184480">Restart your device</translation>
<translation id="4277028893293644418">Reset password</translation>
<translation id="4280429058323657511">, exp <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Pop-ups and redirects</translation>
<translation id="443673843213245140">Use of a proxy is disabled but an explicit proxy configuration is specified.</translation>
<translation id="445100540951337728">Accepted debit cards</translation>
+<translation id="4472575034687746823">Get started</translation>
<translation id="4506176782989081258">Validation error: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Contacting the system admin</translation>
<translation id="450710068430902550">Sharing with Administrator</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Reload policies</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4736825316280949806">Restart Chromium</translation>
+<translation id="4742407542027196863">Manage passwords…</translation>
<translation id="4744603770635761495">Executable Path</translation>
-<translation id="4749685221585524849">Last used <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Your information (for example, passwords or credit card numbers) is private when it is sent to this site.</translation>
<translation id="4756388243121344051">&amp;History</translation>
<translation id="4758311279753947758">Add contact info</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">View</translation>
<translation id="4854362297993841467">This delivery method isn’t available. Try a different method.</translation>
<translation id="4858792381671956233">You asked your parents if it's OK to visit this site</translation>
+<translation id="4876305945144899064">No username</translation>
<translation id="4880827082731008257">Search history</translation>
<translation id="4881695831933465202">Open</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">County</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="5098332213681597508">This name is from your Google account.</translation>
<translation id="5115563688576182185">(64-bit)</translation>
<translation id="5121084798328133320">After you confirm, card details from your Google Payments account will be shared with this site.</translation>
<translation id="5128122789703661928">The session with this name is not valid for deletion.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Delivery Method</translation>
<translation id="5355557959165512791">You cannot visit <ph name="SITE" /> right now because its certificate has been revoked. Network errors and attacks are usually temporary, so this page will probably work later.</translation>
<translation id="536296301121032821">Failed to store policy settings</translation>
+<translation id="5371425731340848620">Update card</translation>
<translation id="5377026284221673050">'Your clock is behind' or 'Your clock is ahead' or "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">The certificate chain for this site contains a certificate signed using SHA-1.</translation>
-<translation id="5402410679244714488">Exp: <ph name="EXPIRATION_DATE_ABBR" />, last used over a year ago</translation>
+<translation id="5387961145478138773">Get quick access to your favourite Google Apps</translation>
<translation id="540969355065856584">This server could not prove that it is <ph name="DOMAIN" />; its security certificate is not valid at this time. This may be caused by a misconfiguration or an attacker intercepting your connection.</translation>
<translation id="5421136146218899937">Clear browsing data...</translation>
<translation id="5430298929874300616">Remove bookmark</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">Email</translation>
+<translation id="5666899935841546222">Do you want to have all your cards in one place?</translation>
<translation id="5675650730144413517">This page isn’t working</translation>
<translation id="5685654322157854305">Add delivery address</translation>
<translation id="5689199277474810259">Export to JSON</translation>
<translation id="5689516760719285838">Location</translation>
<translation id="570530837424789914">Manage...</translation>
+<translation id="57094364128775171">Suggest strong password…</translation>
<translation id="5710435578057952990">The identity of this website has not been verified.</translation>
<translation id="5719499550583120431">Prepaid cards are accepted.</translation>
<translation id="5720705177508910913">Current user</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">This site can’t be reached</translation>
<translation id="5869522115854928033">Saved passwords</translation>
<translation id="5893752035575986141">Credit cards are accepted.</translation>
-<translation id="5898382028489516745">Chromium recommends resetting your <ph name="ORG_NAME" /> password if you reused it on other sites.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (synced)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 in use}other{# in use}}</translation>
<translation id="5939518447894949180">Reset</translation>
-<translation id="5959728338436674663">Automatically send some <ph name="BEGIN_WHITEPAPER_LINK" />system information and page content<ph name="END_WHITEPAPER_LINK" /> to Google to help detect dangerous apps and sites. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Edit Contact Info</translation>
<translation id="5967867314010545767">Remove from history</translation>
<translation id="5975083100439434680">Zoom out</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Postcode</translation>
<translation id="6290238015253830360">Your suggested articles appear here</translation>
<translation id="6305205051461490394"><ph name="URL" /> is unreachable.</translation>
-<translation id="6319915415804115995">Last used over a year ago</translation>
<translation id="6321917430147971392">Check your DNS settings</translation>
<translation id="6328639280570009161">Try disabling network prediction</translation>
<translation id="6328786501058569169">This site is deceptive</translation>
<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="6342069812937806050">Just now</translation>
<translation id="6355080345576803305">Public session override</translation>
<translation id="6358450015545214790">What do these mean?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 other suggestion}other{# other suggestions}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Redo Delete</translation>
<translation id="6534179046333460208">Physical Web suggestions</translation>
<translation id="6550675742724504774">Options</translation>
-<translation id="6556915248009097796">Exp: <ph name="EXPIRATION_DATE_ABBR" />, last used <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Your manager hasn't approved it yet</translation>
<translation id="6569060085658103619">You're viewing an extension page</translation>
<translation id="6596325263575161958">Encryption options</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">You attempted to reach <ph name="DOMAIN" />, but the server presented a certificate signed using a weak signature algorithm (such as SHA-1). This means that the security credentials that the server presented could have been forged, and the server may not be the server that you expected (you may be communicating with an attacker).</translation>
<translation id="6831043979455480757">Translate</translation>
<translation id="6839929833149231406">Area</translation>
+<translation id="6852204201400771460">Reload app?</translation>
+<translation id="6865412394715372076">This card can't be verified at the moment</translation>
<translation id="6874604403660855544">&amp;Redo add</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Policy level is not supported.</translation>
<translation id="6895330447102777224">Your card is confirmed</translation>
<translation id="6897140037006041989">User Agent</translation>
+<translation id="6903319715792422884">Help improve Safe Browsing by sending some <ph name="BEGIN_WHITEPAPER_LINK" />system information and page content<ph name="END_WHITEPAPER_LINK" /> to Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">User:</translation>
+<translation id="6944692733090228304">You entered your password on a site that’s not managed by <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. To protect your account, don’t reuse your password on other apps and sites.</translation>
<translation id="6945221475159498467">Select</translation>
<translation id="6948701128805548767">To see pickup methods and requirements, select an address</translation>
<translation id="6949872517221025916">Reset password</translation>
+<translation id="6950684638814147129">Error while parsing JSON value: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">The server's certificate appears to be a forgery.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Device</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Click &lt;strong&gt;Apply&lt;/strong&gt;, then click &lt;strong&gt;OK&lt;/strong&gt;
&lt;li&gt;Visit the &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome help centre&lt;/a&gt; to find out how to permanently remove the software from your computer
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Manage passwords...</translation>
<translation id="7419106976560586862">Profile Path</translation>
<translation id="7437289804838430631">Add contact info</translation>
<translation id="7441627299479586546">Wrong policy subject</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Find out more<ph name="END_LINK" /> about this problem.</translation>
<translation id="7455133967321480974">Use global default (Block)</translation>
<translation id="7460163899615895653">Your recent tabs from other devices appear here</translation>
-<translation id="7469372306589899959">Confirming card</translation>
<translation id="7473891865547856676">No Thanks</translation>
<translation id="7481312909269577407">Forward</translation>
<translation id="7485870689360869515">No data found.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">The translation failed because of a problem with the network connection.</translation>
<translation id="8311129316111205805">Load session</translation>
<translation id="8332188693563227489">Access to <ph name="HOST_NAME" /> was denied</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">If you understand the risks to your security, you may <ph name="BEGIN_LINK" />visit this site<ph name="END_LINK" /> before the harmful programs have been removed.</translation>
<translation id="8349305172487531364">Bookmarks bar</translation>
<translation id="8363502534493474904">Turning off aeroplane mode</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> took too long to respond.</translation>
<translation id="8503559462189395349">Chrome Passwords</translation>
<translation id="8503813439785031346">Username</translation>
+<translation id="8508648098325802031">Search icon</translation>
<translation id="8543181531796978784">You can <ph name="BEGIN_ERROR_LINK" />report a detection problem<ph name="END_ERROR_LINK" /> or, if you understand the risks to your security, <ph name="BEGIN_LINK" />visit this unsafe site<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Questions? Contact the person who supervises your profile.</translation>
<translation id="8553075262323480129">The translation failed because the page's language could not be determined.</translation>
<translation id="8557066899867184262">The CVC is located behind your card.</translation>
<translation id="8559762987265718583">A private connection to <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> can't be established because your device's date and time (<ph name="DATE_AND_TIME" />) are incorrect.</translation>
+<translation id="8564985650692024650">Chromium recommends resetting your <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> password if you reused it on other sites.</translation>
<translation id="8571890674111243710">Translating page into <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Add phone no.</translation>
<translation id="859285277496340001">The certificate does not specify a mechanism to check whether it has been revoked.</translation>
+<translation id="860043288473659153">Cardholder name</translation>
<translation id="8620436878122366504">Your parents haven't approved it yet</translation>
<translation id="8625384913736129811">Save This Card to This Device</translation>
-<translation id="8639963783467694461">Auto-fill settings</translation>
+<translation id="8663226718884576429">Order Summary, <ph name="TOTAL_LABEL" />, More Details</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, answer, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Your connection to <ph name="DOMAIN" /> is not encrypted.</translation>
<translation id="8718314106902482036">Payment not completed</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, search suggestion</translation>
<translation id="8725066075913043281">Try again</translation>
<translation id="8728672262656704056">You’ve gone incognito</translation>
<translation id="8730621377337864115">Done</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Bookmarks</translation>
<translation id="883848425547221593">Other Bookmarks</translation>
<translation id="884264119367021077">Delivery address</translation>
+<translation id="8846319957959474018">Open apps easily with bookmarks</translation>
<translation id="884923133447025588">No revocation mechanism found.</translation>
<translation id="885730110891505394">Sharing with Google</translation>
+<translation id="8858065207712248076">Chrome recommends resetting your <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> password if you reused it on other sites.</translation>
<translation id="8866481888320382733">Error parsing policy settings</translation>
<translation id="8870413625673593573">Recently Closed</translation>
<translation id="8874824191258364635">Enter a valid card number</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> normally uses encryption to protect your information. When Chromium tried to connect to <ph name="SITE" /> this time, the website sent back unusual and incorrect credentials. This may happen when an attacker is trying to pretend to be <ph name="SITE" />, or a Wi-Fi sign-in screen has interrupted the connection. Your information is still secure because Chromium stopped the connection before any data was exchanged.</translation>
<translation id="9106062320799175032">Add Billing Address</translation>
<translation id="910908805481542201">Help me fix this</translation>
+<translation id="9114524666733003316">Confirming card...</translation>
<translation id="9128870381267983090">Connect to network</translation>
<translation id="9137013805542155359">Show original</translation>
<translation id="9137248913990643158">Please start and sign in to Chrome before using this app.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">Allow sites to check if you have payment methods saved</translation>
<translation id="9169664750068251925">Always block on this site</translation>
<translation id="9170848237812810038">&amp;Undo</translation>
+<translation id="9171296965991013597">Leave app?</translation>
<translation id="917450738466192189">Server's certificate is invalid.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> uses an unsupported protocol.</translation>
<translation id="9205078245616868884">Your data is encrypted with your sync passphrase. Enter it to start sync.</translation>
diff --git a/chromium/components/strings/components_strings_es-419.xtb b/chromium/components/strings/components_strings_es-419.xtb
index b0e7e8f92e6..6f0b465e5c4 100644
--- a/chromium/components/strings/components_strings_es-419.xtb
+++ b/chromium/components/strings/components_strings_es-419.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Ocultar valor</translation>
<translation id="1228893227497259893">El identificador de la entidad es incorrecto.</translation>
<translation id="1232569758102978740">Sin título</translation>
+<translation id="1250759482327835220">Para realizar pagos de forma más rápida la próxima vez, guarda tu tarjeta, nombre y dirección de facturación en tu cuenta de Google.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (sincronizados)</translation>
<translation id="1256368399071562588">&lt;p&gt;Si intentas visitar un sitio web y no se abre, primero intenta corregir el error con estos pasos para solucionar problemas:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Ajusta la fecha y la hora desde la sección &lt;strong&gt;General&lt;/strong&gt; de la app de &lt;strong&gt;Configuración&lt;/strong&gt; .&lt;/p&gt;</translation>
<translation id="1583429793053364125">Se produjo un error al mostrar la página web.</translation>
-<translation id="1590457302292452960">Generar una contraseña segura…</translation>
<translation id="1592005682883173041">Acceso a datos locales</translation>
<translation id="1594030484168838125">Seleccionar</translation>
<translation id="1620510694547887537">Cámara</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Intenta comunicarte con el administrador del sistema.</translation>
<translation id="1740951997222943430">Ingresa una fecha de vencimiento válida</translation>
+<translation id="1743520634839655729">Para realizar pagos de forma más rápida la próxima vez, guarda tu tarjeta, nombre y dirección de facturación en tu cuenta de Google y en este dispositivo.</translation>
<translation id="17513872634828108">Pestañas abiertas</translation>
<translation id="1753706481035618306">Número de página</translation>
<translation id="1763864636252898013">Este servidor no pudo probar que su dominio es <ph name="DOMAIN" />; el sistema operativo del dispositivo 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>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Tus pestañas abiertas aparecen aquí</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Nombre del titular de la tarjeta</translation>
-<translation id="1806541873155184440">Agregada: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Solicitud o parámetros de solicitud no válidos</translation>
<translation id="1826516787628120939">Comprobando</translation>
<translation id="1834321415901700177">Este sitio contiene programas dañinos</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 sugerencia}other{# sugerencias}}</translation>
<translation id="2079545284768500474">Deshacer</translation>
<translation id="20817612488360358">Se ha establecido la configuración de proxy del sistema, pero también se ha especificado una configuración explícita de proxy.</translation>
-<translation id="2084558088529668945">Ingresaste tu contraseña en un sitio que no administra <ph name="ORG_NAME" />. Para proteger tu cuenta, no vuelvas a usar tu contraseña en otras apps y sitios.</translation>
<translation id="2091887806945687916">Sonido</translation>
<translation id="2094505752054353250">El dominio no coincide.</translation>
<translation id="2096368010154057602">Departamento</translation>
+<translation id="2102134110707549001">Sugerir contraseña segura…</translation>
<translation id="2108755909498034140">Reinicia la computadora.</translation>
<translation id="2113977810652731515">Tarjeta</translation>
<translation id="2114841414352855701">Se ignoró porque fue anulada por <ph name="POLICY_NAME" /> .</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Error de HTTP</translation>
<translation id="2270484714375784793">Número de teléfono</translation>
<translation id="2292556288342944218">Se bloqueó tu acceso a Internet</translation>
-<translation id="230155334948463882">¿Una tarjeta nueva?</translation>
<translation id="2316887270356262533">Esta acción libera menos de 1 MB. Es posible que algunos sitios se carguen más lento en tu próxima visita.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> requiere un nombre de usuario y una contraseña.</translation>
<translation id="2317583587496011522">Se aceptan tarjetas de débito.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Tarjetas aceptadas</translation>
<translation id="2702801445560668637">Lista de lectura</translation>
<translation id="2704283930420550640">El valor no coincide con el formato.</translation>
-<translation id="2704951214193499422">Chrome no pudo confirmar tu tarjeta. Vuelve a intentarlo más tarde.</translation>
<translation id="2705137772291741111">La copia guardada (en caché) de este sitio es ilegible.</translation>
<translation id="2709516037105925701">Autocompletar</translation>
<translation id="2710942282213947212">Un software en tu computadora impide que Chromium se conecte de forma segura a la Web</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Método de envío</translation>
<translation id="277499241957683684">Falta un registro de dispositivo.</translation>
<translation id="2781030394888168909">Exportar para Mac OS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Se ha restablecido la conexión.</translation>
<translation id="2788784517760473862">Tarjetas de crédito aceptadas</translation>
<translation id="2794233252405721443">Sitio bloqueado</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Puedes inhabilitar los servidores proxy configurados para una conexión desde la página de configuración.</translation>
<translation id="2955913368246107853">Cerrar la barra de búsqueda</translation>
<translation id="2958431318199492670">La configuración de red no cumple con el estándar ONC. Es posible que no se importen algunas partes de la configuración.</translation>
-<translation id="2966678944701946121">Venc.: <ph name="EXPIRATION_DATE_ABBR" />, agregada: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Para establecer una conexión segura, el reloj se debe configurar correctamente. Esto se debe a que los certificados que usan los sitios web para su identificación solo son válidos por períodos de tiempo específicos. Debido a que la configuración del reloj del dispositivo es incorrecta, Google Chrome no puede verificar estos certificados.</translation>
<translation id="2972581237482394796">&amp;Rehacer</translation>
<translation id="2977665033722899841">Fila <ph name="ROW_NAME" /> seleccionada. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Tipo de política incorrecto</translation>
<translation id="3037605927509011580">¡Oh, no!</translation>
<translation id="3041612393474885105">Información sobre el certificado</translation>
-<translation id="3063697135517575841">Chrome no pudo confirmar tu tarjeta en este momento. Vuelve a intentarlo más tarde.</translation>
<translation id="3064966200440839136">Saldrás del modo de navegación incógnito para pagar mediante una aplicación externa. ¿Deseas continuar?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Ninguna}=1{1 contraseña}other{# contraseñas}}</translation>
<translation id="3096100844101284527">Agregar dirección de retiro</translation>
@@ -342,7 +339,6 @@
<translation id="3305707030755673451">Tus datos se encriptaron con tu frase de contraseña para sincronización el <ph name="TIME" />. Debes ingresarla para iniciar la sincronización.</translation>
<translation id="3320021301628644560">Agregar dirección de facturación</translation>
<translation id="3338095232262050444">Seguro</translation>
-<translation id="3340978935015468852">configuración</translation>
<translation id="3345135638360864351">No se pudo enviar la solicitud de acceso al sitio a <ph name="NAME" />. Vuelve a intentarlo.</translation>
<translation id="3355823806454867987">Cambiar la configuración del proxy...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />no guardará<ph name="END_EMPHASIS" /> la siguiente información:
@@ -376,7 +372,6 @@
<translation id="3528171143076753409">El certificado del servidor no es de confianza.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Al menos 1 elemento en dispositivos sincronizados}=1{1 elemento (y más en dispositivos sincronizados)}other{# elementos (y más en dispositivos sincronizados)}}</translation>
<translation id="3539171420378717834">Conservar una copia de la tarjeta en el dispositivo.</translation>
-<translation id="3542684924769048008">Utilizar la contraseña para:</translation>
<translation id="3549644494707163724">Encriptar todos los datos sincronizados con tu propia frase de contraseña para sincronización</translation>
<translation id="3556433843310711081">Tu administrador puede desbloquearlo por ti</translation>
<translation id="3566021033012934673">La conexión no es privada</translation>
@@ -462,7 +457,6 @@
<translation id="4171400957073367226">La firma de verificación no es válida.</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> elemento más}other{<ph name="ITEM_COUNT" /> elementos más}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome te recomienda que restablezcas la contraseña de <ph name="ORG_NAME" /> si la volviste a usar en otros sitios.</translation>
<translation id="4196861286325780578">&amp;Rehacer Mover</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Comprobar las configuraciones de firewall y antivirus<ph name="END_LINK" />.</translation>
<translation id="4220128509585149162">Fallos</translation>
@@ -491,7 +485,6 @@
<translation id="425582637250725228">Es posible que los cambios que implementaste no se puedan guardar.</translation>
<translation id="4258748452823770588">Firma no válida</translation>
<translation id="4265872034478892965">Permitido por tu administrador</translation>
-<translation id="4269787794583293679">(Sin nombre de usuario)</translation>
<translation id="4275830172053184480">Reiniciar tu dispositivo</translation>
<translation id="4277028893293644418">Restablecer contraseña</translation>
<translation id="4280429058323657511">, exp <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -514,6 +507,7 @@
<translation id="4434045419905280838">Ventanas emergentes y redirec.</translation>
<translation id="443673843213245140">Se inhabilitó el uso de un proxy, pero se especificó una configuración explícita de proxy.</translation>
<translation id="445100540951337728">Tarjetas de débito aceptadas</translation>
+<translation id="4472575034687746823">Comenzar</translation>
<translation id="4506176782989081258">Error de validación: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Comunicarse con el administrador del sistema.</translation>
<translation id="450710068430902550">Compartir con el administrador</translation>
@@ -538,8 +532,8 @@
<translation id="4726672564094551039">Volver a cargar políticas</translation>
<translation id="4728558894243024398">Plataforma</translation>
<translation id="4736825316280949806">Reinicia Chromium.</translation>
+<translation id="4742407542027196863">Administrar contraseñas…</translation>
<translation id="4744603770635761495">Ruta ejecutable</translation>
-<translation id="4749685221585524849">Último uso: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Tu información (p. ej., contraseñas o números de tarjetas de crédito) es privada cuando se envía a este sitio.</translation>
<translation id="4756388243121344051">&amp;Historial</translation>
<translation id="4758311279753947758">Agregar información de contacto</translation>
@@ -556,6 +550,7 @@
<translation id="4850886885716139402">Ver</translation>
<translation id="4854362297993841467">Este método de entrega no está disponible. Prueba otro método.</translation>
<translation id="4858792381671956233">Les preguntaste a tus padres si puedes visitar este sitio</translation>
+<translation id="4876305945144899064">Sin nombre de usuario</translation>
<translation id="4880827082731008257">Buscar historial</translation>
<translation id="4881695831933465202">Abrir</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -589,6 +584,7 @@
<translation id="5089810972385038852">Estado</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="5098332213681597508">Este nombre es de tu cuenta de Google.</translation>
<translation id="5115563688576182185">(64 bits)</translation>
<translation id="5121084798328133320">Después de que confirmes esta acción, los datos de tu cuenta de Google Payments se compartirán con este sitio.</translation>
<translation id="5128122789703661928">La sesión con este nombre no se puede borrar.</translation>
@@ -629,9 +625,10 @@
<translation id="5332219387342487447">Método de envío</translation>
<translation id="5355557959165512791">No puedes visitar <ph name="SITE" /> ahora porque este certificado se revocó. Los ataques y errores de red suelen ser temporales, por lo que es posible que esta página funcione más tarde.</translation>
<translation id="536296301121032821">Error al almacenar la configuración de la política</translation>
+<translation id="5371425731340848620">Actualizar tarjeta</translation>
<translation id="5377026284221673050">"El reloj está atrasado", "El reloj está adelantado" o "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">La cadena del certificado de este sitio web contiene un certificado que se firmó con SHA-1.</translation>
-<translation id="5402410679244714488">Venc.: <ph name="EXPIRATION_DATE_ABBR" />, último uso: hace más de un año</translation>
+<translation id="5387961145478138773">Obtén acceso rápido a tus apps de Google favoritas</translation>
<translation id="540969355065856584">Este servidor no pudo demostrar que se trata de <ph name="DOMAIN" />; el certificado de seguridad no es válido en este momento. Esto puede deberse a una configuración incorrecta o a un ataque que intercepta tu conexión.</translation>
<translation id="5421136146218899937">Borrar datos de navegación...</translation>
<translation id="5430298929874300616">Eliminar marcador</translation>
@@ -673,11 +670,13 @@
<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="5659593005791499971">Correo electrónico</translation>
+<translation id="5666899935841546222">¿Quieres tener todas tus tarjetas en un solo lugar?</translation>
<translation id="5675650730144413517">Esta página no funciona</translation>
<translation id="5685654322157854305">Agregar dirección de envío</translation>
<translation id="5689199277474810259">Exportar a JSON</translation>
<translation id="5689516760719285838">Ubicación</translation>
<translation id="570530837424789914">Administrar…</translation>
+<translation id="57094364128775171">Sugerir contraseña segura…</translation>
<translation id="5710435578057952990">No se ha verificado la identidad de este sitio web.</translation>
<translation id="5719499550583120431">Se aceptan tarjetas de prepago.</translation>
<translation id="5720705177508910913">Usuario actual</translation>
@@ -698,11 +697,9 @@
<translation id="5869405914158311789">No se puede acceder a este sitio</translation>
<translation id="5869522115854928033">Contraseñas almacenadas</translation>
<translation id="5893752035575986141">Se aceptan tarjetas de crédito.</translation>
-<translation id="5898382028489516745">Chromium te recomienda que restablezcas la contraseña de <ph name="ORG_NAME" /> si la volviste a usar en otros sitios.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (sincronizado)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 en uso}other{# en uso}}</translation>
<translation id="5939518447894949180">Restablecer</translation>
-<translation id="5959728338436674663">Enviar automáticamente <ph name="BEGIN_WHITEPAPER_LINK" />determinado contenido de la página e información del sistema<ph name="END_WHITEPAPER_LINK" /> a Google para detectar apps y sitios peligrosos <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Editar la información de contacto</translation>
<translation id="5967867314010545767">Eliminar del historial</translation>
<translation id="5975083100439434680">Alejar</translation>
@@ -748,13 +745,11 @@
<translation id="6282194474023008486">Código postal</translation>
<translation id="6290238015253830360">Tus artículos sugeridos aparecen aquí</translation>
<translation id="6305205051461490394">No se puede acceder a <ph name="URL" />.</translation>
-<translation id="6319915415804115995">Último uso: hace más de un año</translation>
<translation id="6321917430147971392">Revisa la configuración de DNS.</translation>
<translation id="6328639280570009161">Intenta inhabilitar la predicción de red.</translation>
<translation id="6328786501058569169">Este sitio es engañoso</translation>
<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="6342069812937806050">Recién</translation>
<translation id="6355080345576803305">Anulación de sesión pública</translation>
<translation id="6358450015545214790">¿Qué significa esto?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 sugerencia más}other{# sugerencias más}}</translation>
@@ -779,7 +774,6 @@
<translation id="6529602333819889595">&amp;Rehacer Eliminar</translation>
<translation id="6534179046333460208">Sugerencias de la Web física</translation>
<translation id="6550675742724504774">Opciones</translation>
-<translation id="6556915248009097796">Venc.: <ph name="EXPIRATION_DATE_ABBR" />, último uso: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Tu administrador aún no lo aprobó</translation>
<translation id="6569060085658103619">Estás viendo la página de una extensión</translation>
<translation id="6596325263575161958">Opciones de encriptación</translation>
@@ -808,15 +802,20 @@
<translation id="6825578344716086703">Intentaste acceder a <ph name="DOMAIN" />, pero el servidor presentó un certificado firmado con un algoritmo de firma no seguro (como SHA-1). Esto significa que podrían haberse falsificado las credenciales de seguridad presentadas por el servidor y que este no sea el servidor esperado (es posible que hayas establecido comunicación con un atacante).</translation>
<translation id="6831043979455480757">Traducir</translation>
<translation id="6839929833149231406">Área</translation>
+<translation id="6852204201400771460">¿Deseas volver a cargar la app?</translation>
+<translation id="6865412394715372076">No se puede verificar la tarjeta en este momento</translation>
<translation id="6874604403660855544">&amp;Rehacer Agregar</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">No se admite el nivel de políticas.</translation>
<translation id="6895330447102777224">Tu tarjeta se confirmó</translation>
<translation id="6897140037006041989">User agent</translation>
+<translation id="6903319715792422884">Para mejorar la Navegación segura, envía <ph name="BEGIN_WHITEPAPER_LINK" />información del sistema y contenido de la página<ph name="END_WHITEPAPER_LINK" /> a Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Usuario:</translation>
+<translation id="6944692733090228304">Ingresaste tu contraseña en un sitio que no está administrado por <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Para proteger tu cuenta, no vuelvas a usar la contraseña en otras apps y sitios.</translation>
<translation id="6945221475159498467">Seleccionar</translation>
<translation id="6948701128805548767">Para ver los requisitos y métodos de retiro, selecciona una dirección</translation>
<translation id="6949872517221025916">Restablecer contraseña</translation>
+<translation id="6950684638814147129">Error al analizar el valor JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">El certificado del servidor parece falso.</translation>
<translation id="6965382102122355670">Aceptar</translation>
<translation id="6965978654500191972">Dispositivo</translation>
@@ -878,6 +877,7 @@
&lt;li&gt;Haz clic en &lt;strong&gt;Aplicar&lt;/strong&gt; y, luego, en &lt;strong&gt;Aceptar&lt;/strong&gt;.
&lt;li&gt;Visita el &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Centro de ayuda de Chrome&lt;/a&gt; para obtener información sobre cómo quitar el software de forma permanente de tu computadora.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Administrar contraseñas…</translation>
<translation id="7419106976560586862">Ruta del perfil</translation>
<translation id="7437289804838430631">Agregar información de contacto</translation>
<translation id="7441627299479586546">Nombre de usuario o dominio de política incorrecto</translation>
@@ -886,7 +886,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Más información<ph name="END_LINK" /> acerca de este problema</translation>
<translation id="7455133967321480974">Usar configuración global predeterminada (Bloquear)</translation>
<translation id="7460163899615895653">Aquí aparecen tus pestañas recientes de otros dispositivos</translation>
-<translation id="7469372306589899959">Confirmando la tarjeta</translation>
<translation id="7473891865547856676">No, gracias</translation>
<translation id="7481312909269577407">Reenviar</translation>
<translation id="7485870689360869515">No se encontró ningún dato.</translation>
@@ -1016,6 +1015,7 @@
<translation id="8308427013383895095">Se produjo un error en la traducción a causa de un problema con la conexión de red.</translation>
<translation id="8311129316111205805">Cargar sesión</translation>
<translation id="8332188693563227489">Se denegó el acceso a <ph name="HOST_NAME" /></translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Si comprendes los riesgos de seguridad, puedes <ph name="BEGIN_LINK" />visitar este sitio<ph name="END_LINK" /> antes de que se hayan eliminado los programas peligrosos.</translation>
<translation id="8349305172487531364">Barra de favoritos</translation>
<translation id="8363502534493474904">Desactivar el modo de avión.</translation>
@@ -1036,21 +1036,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> tardó demasiado en responder.</translation>
<translation id="8503559462189395349">Contraseñas de Chrome</translation>
<translation id="8503813439785031346">Nombre de usuario</translation>
+<translation id="8508648098325802031">Ícono de Búsqueda</translation>
<translation id="8543181531796978784">Puedes <ph name="BEGIN_ERROR_LINK" />informar un problema de detección<ph name="END_ERROR_LINK" /> o, si comprendes los riesgos de seguridad, puedes <ph name="BEGIN_LINK" />visitar el sitio no seguro<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">¿Tienes preguntas? Comunícate con la persona que supervisa tu perfil.</translation>
<translation id="8553075262323480129">Falló la traducción debido a que no se pudo determinar el idioma de la página.</translation>
<translation id="8557066899867184262">El CVC se encuentra al dorso de tu tarjeta.</translation>
<translation id="8559762987265718583">No se puede establecer una conexión privada a <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> porque la fecha y la hora del dispositivo (<ph name="DATE_AND_TIME" />) son incorrectas.</translation>
+<translation id="8564985650692024650">Chromium te recomienda que restablezcas la contraseña de <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> si la volviste a usar en otros sitios.</translation>
<translation id="8571890674111243710">Traduciendo página a <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Agregar teléfono
</translation>
<translation id="859285277496340001">El certificado no especifica un mecanismo para verificar si ha sido revocado.</translation>
+<translation id="860043288473659153">Nombre del titular de la tarjeta</translation>
<translation id="8620436878122366504">Tus padres aún no lo aprobaron</translation>
<translation id="8625384913736129811">Guardar esta tarjeta para este dispositivo</translation>
-<translation id="8639963783467694461">Configuración de Autocompletar</translation>
+<translation id="8663226718884576429">Resumen del pedido, <ph name="TOTAL_LABEL" />, Más detalles</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, respuesta, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Tu conexión a <ph name="DOMAIN" /> no está cifrada.</translation>
<translation id="8718314106902482036">No se completó el pago</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, sugerencia de búsqueda</translation>
<translation id="8725066075913043281">Intentar nuevamente</translation>
<translation id="8728672262656704056">Estás en modo incógnito</translation>
<translation id="8730621377337864115">Listo</translation>
@@ -1066,8 +1070,10 @@
<translation id="8820817407110198400">Favoritos</translation>
<translation id="883848425547221593">Otros favoritos</translation>
<translation id="884264119367021077">Dirección de envío</translation>
+<translation id="8846319957959474018">Abre las apps fácilmente con los favoritos</translation>
<translation id="884923133447025588">No se ha encontrado ningún mecanismo de revocación.</translation>
<translation id="885730110891505394">Compartir con Google</translation>
+<translation id="8858065207712248076">Chrome te recomienda que restablezcas la contraseña de <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> si la volviste a usar en otros sitios.</translation>
<translation id="8866481888320382733">Error al analizar la configuración de la política</translation>
<translation id="8870413625673593573">Cerrado recientemente</translation>
<translation id="8874824191258364635">Ingresa un número de tarjeta válido</translation>
@@ -1106,6 +1112,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> suele utilizar la encriptación para proteger la información. Cuando Chromium intentó conectarse a <ph name="SITE" />, el sitio web devolvió credenciales incorrectas y poco comunes. Es posible que un atacante quiera suplantar a <ph name="SITE" /> o que una pantalla de acceso Wi-Fi haya interrumpido la conexión. Tu información permanece segura porque Chromium detuvo la conexión para evitar el intercambio de datos.</translation>
<translation id="9106062320799175032">Agregar dirección de facturación</translation>
<translation id="910908805481542201">Ayudarme a solucionar este problema</translation>
+<translation id="9114524666733003316">Confirmando tarjeta…</translation>
<translation id="9128870381267983090">Conectarse a una red</translation>
<translation id="9137013805542155359">Mostrar original</translation>
<translation id="9137248913990643158">Abre Chrome y accede a tu cuenta antes de usar esta app.</translation>
@@ -1116,6 +1123,7 @@
<translation id="9168814207360376865">Permitir que los sitios determinen si tienes formas de pago guardadas</translation>
<translation id="9169664750068251925">Bloquear siempre en este sitio</translation>
<translation id="9170848237812810038">&amp;Deshacer</translation>
+<translation id="9171296965991013597">¿Deseas salir de la app?</translation>
<translation id="917450738466192189">El certificado del servidor no es válido.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> utiliza un protocolo no compatible.</translation>
<translation id="9205078245616868884">Tus datos están encriptados con tu frase de contraseña para sincronización. Debes ingresarla para iniciar la sincronización.</translation>
diff --git a/chromium/components/strings/components_strings_es.xtb b/chromium/components/strings/components_strings_es.xtb
index 746d440b8d9..3a04f9cf60d 100644
--- a/chromium/components/strings/components_strings_es.xtb
+++ b/chromium/components/strings/components_strings_es.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Ocultar valor</translation>
<translation id="1228893227497259893">Identificador de entidad incorrecto</translation>
<translation id="1232569758102978740">Sin título</translation>
+<translation id="1250759482327835220">Para pagar más rápido la próxima vez, guarda tu tarjeta, tu nombre y tu dirección de facturación en tu cuenta de Google.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (sincronizados)</translation>
<translation id="1256368399071562588">&lt;p&gt;Si intentas acceder a un sitio web y no se abre, prueba estos pasos para solucionar problemas:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Ajusta la fecha y la hora en la sección &lt;strong&gt;General&lt;/strong&gt; de la aplicación &lt;strong&gt;Configuración&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Se ha producido un error al mostrar esta página web.</translation>
-<translation id="1590457302292452960">Generar una contraseña segura...</translation>
<translation id="1592005682883173041">Acceso a datos locales</translation>
<translation id="1594030484168838125">Seleccionar</translation>
<translation id="1620510694547887537">Cámara</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">American Express</translation>
<translation id="1734878702283171397">Intenta ponerte en contacto con el administrador del sistema.</translation>
<translation id="1740951997222943430">Introduce un mes de vencimiento válido</translation>
+<translation id="1743520634839655729">Para pagar más rápido la próxima vez, guarda tu tarjeta, tu nombre y tu dirección de facturación en tu cuenta de Google y en este dispositivo.</translation>
<translation id="17513872634828108">Pestañas abiertas</translation>
<translation id="1753706481035618306">Número de página</translation>
<translation id="1763864636252898013">Este servidor no ha podido probar que su dominio es <ph name="DOMAIN" />, el sistema operativo de tu dispositivo 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>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Las pestañas abiertas aparecen aquí</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Nombre del titular de la tarjeta</translation>
-<translation id="1806541873155184440">Añadida el <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Parámetros de solicitud o solicitud no válidos</translation>
<translation id="1826516787628120939">Comprobando</translation>
<translation id="1834321415901700177">Este sitio web contiene programas dañinos</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{Una sugerencia}other{# sugerencias}}</translation>
<translation id="2079545284768500474">Deshacer</translation>
<translation id="20817612488360358">Se ha establecido la configuración del proxy del sistema, pero también se han especificado ajustes de proxy explícitos.</translation>
-<translation id="2084558088529668945">Has introducido tu contraseña en un sitio web que no está gestionado por <ph name="ORG_NAME" />. Para proteger tu cuenta, no vuelvas a utilizar tu contraseña en otras aplicaciones ni en otros sitios web.</translation>
<translation id="2091887806945687916">Sonido</translation>
<translation id="2094505752054353250">El dominio no coincide</translation>
<translation id="2096368010154057602">Departamento</translation>
+<translation id="2102134110707549001">Sugerir contraseña segura…</translation>
<translation id="2108755909498034140">Reinicia el ordenador</translation>
<translation id="2113977810652731515">Tarjeta</translation>
<translation id="2114841414352855701">Se ha ignorado la política porque la anula <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Error de HTTP</translation>
<translation id="2270484714375784793">Número de teléfono</translation>
<translation id="2292556288342944218">Tu acceso a Internet está bloqueado</translation>
-<translation id="230155334948463882">¿Nueva tarjeta?</translation>
<translation id="2316887270356262533">Libera menos de 1 MB. Algunos sitios web pueden tardar más en cargarse la próxima vez que accedas a ellos.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> necesita un nombre de usuario y una contraseña.</translation>
<translation id="2317583587496011522">Se aceptan tarjetas de débito.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Tarjetas aceptadas</translation>
<translation id="2702801445560668637">Lista de lectura</translation>
<translation id="2704283930420550640">El valor no coincide con el formato.</translation>
-<translation id="2704951214193499422">Chromium no ha podido confirmar tu tarjeta en este momento. Vuelve a intentarlo más tarde.</translation>
<translation id="2705137772291741111">La copia guardada (almacenada en caché) de este sitio web no se ha podido leer.</translation>
<translation id="2709516037105925701">Autocompletar</translation>
<translation id="2710942282213947212">Hay software en tu ordenador que impide que Chromium se conecte a la Web de forma segura</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Método de envío</translation>
<translation id="277499241957683684">Falta un registro de dispositivo.</translation>
<translation id="2781030394888168909">Exportar para MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Se ha restablecido la conexión.</translation>
<translation id="2788784517760473862">Tarjetas de crédito aceptadas</translation>
<translation id="2794233252405721443">Sito web bloqueado</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Puedes inhabilitar los servidores proxy configurados para una conexión en la página de configuración.</translation>
<translation id="2955913368246107853">Cerrar la barra de búsqueda</translation>
<translation id="2958431318199492670">La configuración de red no cumple el estándar ONC. Es posible que no se importen partes de la configuración.</translation>
-<translation id="2966678944701946121">Fecha de caducidad: <ph name="EXPIRATION_DATE_ABBR" /> (añadida el <ph name="ADDED_TO_AUTOFILL_MONTH" />)</translation>
<translation id="2969319727213777354">Para establecer una conexión segura, el reloj debe estar configurado correctamente. Esto se debe a que los certificados que utilizan los sitios web para identificarse solo son válidos para períodos de tiempo específicos. Como el reloj de tu dispositivo no está configurado correctamente, Google Chrome no puede verificar estos certificados.</translation>
<translation id="2972581237482394796">&amp;Rehacer</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, opción seleccionada actualmente. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Tipo de política incorrecto</translation>
<translation id="3037605927509011580">¡Vaya!</translation>
<translation id="3041612393474885105">Datos del certificado</translation>
-<translation id="3063697135517575841">Chrome no ha podido confirmar tu tarjeta en este momento. Vuelve a intentarlo más tarde.</translation>
<translation id="3064966200440839136">Saldrás del modo de incógnito para realizar un pago en una aplicación externa. ¿Quieres continuar?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Ninguna}=1{1 contraseña}other{# contraseñas}}</translation>
<translation id="3096100844101284527">Añadir dirección de recogida</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Tus datos se cifraron con tu frase de contraseña de sincronización el <ph name="TIME" />. Introdúcela para iniciar la sincronización.</translation>
<translation id="3320021301628644560">Añadir dirección de facturación</translation>
<translation id="3338095232262050444">Es seguro</translation>
-<translation id="3340978935015468852">configuración</translation>
<translation id="3345135638360864351">No se ha podido enviar la solicitud de acceso a este sitio a <ph name="NAME" />. Vuelve a intentarlo.</translation>
<translation id="3355823806454867987">Cambiar la configuración de proxy...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />no guardará<ph name="END_EMPHASIS" /> la siguiente información:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">El certificado de servidor no es de confianza.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Al menos 1 elemento en dispositivos sincronizados}=1{1 elemento (y otros en dispositivos sincronizados)}other{# elementos (y otros en dispositivos sincronizados)}}</translation>
<translation id="3539171420378717834">Guardar una copia de la tarjeta en este dispositivo</translation>
-<translation id="3542684924769048008">Utilizar contraseña para:</translation>
<translation id="3549644494707163724">Cifrar todos los datos sincronizados con tu propia frase de contraseña de sincronización</translation>
<translation id="3556433843310711081">Tu administrador puede desbloquearlo</translation>
<translation id="3566021033012934673">La conexión no es privada</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">La firma de verificación no es válida</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> elemento más}other{<ph name="ITEM_COUNT" /> elementos más}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome te recomienda que cambies tu contraseña de <ph name="ORG_NAME" /> si la has vuelto a utilizar en otros sitios web.</translation>
<translation id="4196861286325780578">&amp;Rehacer movimiento</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Comprobar la configuración del cortafuegos y del antivirus<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">La página no responde o se cierra</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Es posible que los cambios no se guarden.</translation>
<translation id="4258748452823770588">Firma errónea</translation>
<translation id="4265872034478892965">Permitido por el administrador</translation>
-<translation id="4269787794583293679">(Ningún nombre de usuario)</translation>
<translation id="4275830172053184480">Reiniciar tu dispositivo</translation>
<translation id="4277028893293644418">Cambiar contraseña</translation>
<translation id="4280429058323657511">Vcto. <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Ventanas emergentes y redirecciones</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="445100540951337728">Tarjetas de débito aceptadas</translation>
+<translation id="4472575034687746823">Empezar</translation>
<translation id="4506176782989081258">Error de validación: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Ponerte en contacto con el administrador del sistema</translation>
<translation id="450710068430902550">Compartir con el administrador</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Volver a cargar políticas</translation>
<translation id="4728558894243024398">Plataforma</translation>
<translation id="4736825316280949806">Reinicia Chromium</translation>
+<translation id="4742407542027196863">Gestionar contraseñas…</translation>
<translation id="4744603770635761495">Ruta del ejecutable</translation>
-<translation id="4749685221585524849">Usada por última vez el <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Tu información (por ejemplo, las contraseñas o los números de las tarjetas de crédito) es privada cuando se envía a este sitio web.</translation>
<translation id="4756388243121344051">&amp;Historial</translation>
<translation id="4758311279753947758">Añadir información de contacto</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Ver</translation>
<translation id="4854362297993841467">Este método de entrega no está disponible. Selecciona otro.</translation>
<translation id="4858792381671956233">Has solicitado permiso a tus padres para poder acceder a este sitio web</translation>
+<translation id="4876305945144899064">Ningún nombre de usuario</translation>
<translation id="4880827082731008257">Buscar en el historial</translation>
<translation id="4881695831933465202">Abrir</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> y <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Estado</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="5098332213681597508">Este nombre proviene de tu cuenta de Google.</translation>
<translation id="5115563688576182185">(64 bits)</translation>
<translation id="5121084798328133320">Una vez que confirmes esta acción, la información de la tarjeta de tu cuenta de pagos de Google se compartirá con este sitio web.</translation>
<translation id="5128122789703661928">No se puede eliminar la sesión con este nombre.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Método de envío</translation>
<translation id="5355557959165512791">No puedes acceder a <ph name="SITE" /> en este momento porque su certificado se ha revocado. Los ataques y los errores de red suelen ser temporales, por lo que es probable que esta página funcione más tarde.</translation>
<translation id="536296301121032821">Error al almacenar la configuración de la política</translation>
+<translation id="5371425731340848620">Actualizar tarjeta</translation>
<translation id="5377026284221673050">"Tu reloj está atrasado" o "Tu reloj está adelantado" o "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">La cadena de certificados de este sitio web contiene un certificado firmado con SHA-1.</translation>
-<translation id="5402410679244714488">Fecha de caducidad: <ph name="EXPIRATION_DATE_ABBR" /> (usada por última vez hace más de un año)</translation>
+<translation id="5387961145478138773">Accede rápidamente a tus aplicaciones de Google favoritas</translation>
<translation id="540969355065856584">Este servidor no ha podido probar que su dominio es <ph name="DOMAIN" />, ya que su certificado de seguridad no es válido en este momento. Esto puede deberse a una configuración incorrecta o a que un atacante haya interceptado la conexión.</translation>
<translation id="5421136146218899937">Borrar datos de navegación...</translation>
<translation id="5430298929874300616">Eliminar marcador</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">Correo electrónico</translation>
+<translation id="5666899935841546222">¿Quieres tener todas tus tarjetas en un mismo sitio?</translation>
<translation id="5675650730144413517">Esta página no funciona</translation>
<translation id="5685654322157854305">Añadir dirección de envío</translation>
<translation id="5689199277474810259">Exportar a JSON</translation>
<translation id="5689516760719285838">Ubicación</translation>
<translation id="570530837424789914">Gestionar...</translation>
+<translation id="57094364128775171">Sugerir contraseña segura…</translation>
<translation id="5710435578057952990">No se ha verificado la identidad de este sitio web.</translation>
<translation id="5719499550583120431">Se aceptan tarjetas prepago.</translation>
<translation id="5720705177508910913">Usuario actual</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">No se puede acceder a este sitio web</translation>
<translation id="5869522115854928033">Contraseñas guardadas</translation>
<translation id="5893752035575986141">Se aceptan tarjetas de crédito.</translation>
-<translation id="5898382028489516745">Chromium te recomienda que cambies tu contraseña de <ph name="ORG_NAME" /> si la has vuelto a utilizar en otros sitios web.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (sincronizado)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 en uso}other{# en uso}}</translation>
<translation id="5939518447894949180">Restablecer</translation>
-<translation id="5959728338436674663">Enviar automáticamente <ph name="BEGIN_WHITEPAPER_LINK" />información del sistema y contenido de las páginas<ph name="END_WHITEPAPER_LINK" /> a Google para facilitar la detección de aplicaciones y sitios web peligrosos. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Edita la información de contacto</translation>
<translation id="5967867314010545767">Eliminar del historial</translation>
<translation id="5975083100439434680">Reducir</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Código postal</translation>
<translation id="6290238015253830360">Los artículos sugeridos aparecen aquí</translation>
<translation id="6305205051461490394">No se puede acceder a <ph name="URL" />.</translation>
-<translation id="6319915415804115995">Usada por última vez hace más de un año</translation>
<translation id="6321917430147971392">Comprueba tu configuración de DNS</translation>
<translation id="6328639280570009161">Prueba a inhabilitar la predicción de red</translation>
<translation id="6328786501058569169">Este sitio web es engañoso</translation>
<translation id="6337133576188860026">Libera menos de <ph name="SIZE" />. Algunos sitios web 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="6342069812937806050">Ahora</translation>
<translation id="6355080345576803305">Anulación de sesión pública</translation>
<translation id="6358450015545214790">¿Necesitas ayuda?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{Una sugerencia más}other{# sugerencias más}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Rehacer eliminación</translation>
<translation id="6534179046333460208">Sugerencias de la Web física</translation>
<translation id="6550675742724504774">Configuración</translation>
-<translation id="6556915248009097796">Fecha de caducidad: <ph name="EXPIRATION_DATE_ABBR" /> (usada por última vez el <ph name="LAST_USED_DATE_NO_DETAIL" />)</translation>
<translation id="6563469144985748109">Tu administrador aún no la ha aprobado</translation>
<translation id="6569060085658103619">Estas viendo la página de una extensión</translation>
<translation id="6596325263575161958">Opciones de cifrado</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Has intentado acceder a <ph name="DOMAIN" />, pero el servidor ha presentado un certificado firmado con un algoritmo de firma no seguro (por ejemplo, SHA-1). Una posible causa de este problema es que se hayan falsificado las credenciales de seguridad presentadas por el servidor y que hayas accedido a la página de un atacante en lugar de establecer conexión con el servidor en cuestión.</translation>
<translation id="6831043979455480757">Traducir</translation>
<translation id="6839929833149231406">Área</translation>
+<translation id="6852204201400771460">¿Quieres volver a cargar la aplicación?</translation>
+<translation id="6865412394715372076">No se puede verificar la tarjeta en este momento</translation>
<translation id="6874604403660855544">&amp;Rehacer acción de añadir</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">No se admite el nivel de la política.</translation>
<translation id="6895330447102777224">Tu tarjeta se ha confirmado</translation>
<translation id="6897140037006041989">Agente de usuario</translation>
+<translation id="6903319715792422884">Ayuda a mejorar la Navegación Segura enviando <ph name="BEGIN_WHITEPAPER_LINK" />datos del sistema y contenido de las páginas<ph name="END_WHITEPAPER_LINK" /> a Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Usuario:</translation>
+<translation id="6944692733090228304"><ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> no gestiona el sitio web en el que has introducido tu contraseña. Para proteger tu cuenta, no utilices la misma contraseña en otras aplicaciones o sitios web.</translation>
<translation id="6945221475159498467">Seleccionar</translation>
<translation id="6948701128805548767">Selecciona una dirección para ver los métodos de recogida y los requisitos</translation>
<translation id="6949872517221025916">Cambiar contraseña</translation>
+<translation id="6950684638814147129">No se ha podido analizar el valor JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">El certificado del servidor parece ser falso.</translation>
<translation id="6965382102122355670">Aceptar</translation>
<translation id="6965978654500191972">Dispositivo</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Haz clic en &lt;strong&gt;Aplicar&lt;/strong&gt; y, a continuación, en &lt;strong&gt;Aceptar&lt;/strong&gt;
&lt;li&gt;Visita el &lt;a href="https://support.google.com/chrome/answer/6098869?hl=es"&gt;Centro de Ayuda de Chrome&lt;/a&gt; para consultar cómo eliminar el software de forma permanente de tu ordenador
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Gestionar contraseñas…</translation>
<translation id="7419106976560586862">Ruta del perfil</translation>
<translation id="7437289804838430631">Añade la información de contacto</translation>
<translation id="7441627299479586546">Asunto de política incorrecto</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Consultar más información<ph name="END_LINK" /> sobre este problema</translation>
<translation id="7455133967321480974">Utilizar valor predeterminado global (Bloquear)</translation>
<translation id="7460163899615895653">Las pestañas recientes de otros dispositivos aparecen aquí</translation>
-<translation id="7469372306589899959">Confirmando tarjeta</translation>
<translation id="7473891865547856676">No, gracias</translation>
<translation id="7481312909269577407">Adelante</translation>
<translation id="7485870689360869515">No se han encontrado datos.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Se ha producido un error de traducción debido a un problema con la conexión de red.</translation>
<translation id="8311129316111205805">Cargar sesión</translation>
<translation id="8332188693563227489">Se ha denegado el acceso a <ph name="HOST_NAME" /></translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Si entiendes los riesgos para tu seguridad, puedes <ph name="BEGIN_LINK" />acceder a este sitio<ph name="END_LINK" /> antes de que se hayan eliminado los programas dañinos.</translation>
<translation id="8349305172487531364">Barra de marcadores</translation>
<translation id="8363502534493474904">Desactivar el modo avión</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ha tardado demasiado tiempo en responder.</translation>
<translation id="8503559462189395349">Contraseñas de Chrome</translation>
<translation id="8503813439785031346">Nombre de usuario</translation>
+<translation id="8508648098325802031">Icono de búsqueda</translation>
<translation id="8543181531796978784">Puedes <ph name="BEGIN_ERROR_LINK" />informar de un problema de detección<ph name="END_ERROR_LINK" /> o, si comprendes los riesgos que conlleva esta acción para tu seguridad, <ph name="BEGIN_LINK" />accede a este sitio web no seguro<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">¿Tienes alguna pregunta? Ponte en contacto con la persona que supervisa tu perfil.</translation>
<translation id="8553075262323480129">La traducción no se ha realizado correctamente porque no se ha podido determinar el idioma de la página.</translation>
<translation id="8557066899867184262">Puedes encontrar el CVC en el reverso de la tarjeta.</translation>
<translation id="8559762987265718583">No se puede establecer una conexión privada con <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> porque la fecha y la hora de tu dispositivo (<ph name="DATE_AND_TIME" />) no son correctas.</translation>
+<translation id="8564985650692024650">Chromium te recomienda que cambies tu contraseña de <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> si la has vuelto a utilizar en otros sitios web.</translation>
<translation id="8571890674111243710">Traduciendo página a <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Añade un teléfono</translation>
<translation id="859285277496340001">El certificado no especifica ningún mecanismo para comprobar si se ha revocado.</translation>
+<translation id="860043288473659153">Nombre del titular de la tarjeta</translation>
<translation id="8620436878122366504">Tus padres aún no lo han aprobado</translation>
<translation id="8625384913736129811">Guardar esta tarjeta en el dispositivo</translation>
-<translation id="8639963783467694461">Configuración de Autocompletar</translation>
+<translation id="8663226718884576429">Resumen del pedido: <ph name="TOTAL_LABEL" /> (más detalles)</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, respuesta: <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Tu conexión a <ph name="DOMAIN" /> no está cifrada.</translation>
<translation id="8718314106902482036">El pago no se ha completado</translation>
+<translation id="8719263113926255150">Sugerencia de búsqueda: <ph name="ENTITY" />, <ph name="DESCRIPTION" /></translation>
<translation id="8725066075913043281">Volver a intentarlo</translation>
<translation id="8728672262656704056">Has iniciado una sesión de incógnito</translation>
<translation id="8730621377337864115">Listo</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Marcadores</translation>
<translation id="883848425547221593">Otros marcadores</translation>
<translation id="884264119367021077">Dirección de envío</translation>
+<translation id="8846319957959474018">Abre aplicaciones fácilmente con los marcadores</translation>
<translation id="884923133447025588">No se ha encontrado ningún mecanismo de revocación.</translation>
<translation id="885730110891505394">Compartir con Google</translation>
+<translation id="8858065207712248076">Chrome te recomienda que cambies tu contraseña de <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> si la has vuelto a utilizar en otros sitios web.</translation>
<translation id="8866481888320382733">Error al analizar la configuración de la política</translation>
<translation id="8870413625673593573">Cerrado recientemente</translation>
<translation id="8874824191258364635">Introduce un número de tarjeta válido</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> utiliza normalmente el cifrado para proteger tu información. Cuando Chromium intentó establecer conexión con <ph name="SITE" />, el sitio web devolvió unas credenciales inusuales e incorrectas. Esto puede ocurrir si un atacante intenta suplantar la identidad de <ph name="SITE" /> o si una pantalla de inicio de sesión Wi-Fi interrumpe la conexión. Tu información sigue estando protegida, ya que Chromium detuvo la conexión antes de que se intercambiaran datos.</translation>
<translation id="9106062320799175032">Añade una dirección de facturación</translation>
<translation id="910908805481542201">Ayuda para solucionarlo</translation>
+<translation id="9114524666733003316">Confirmando tarjeta...</translation>
<translation id="9128870381267983090">Conectarse a la red</translation>
<translation id="9137013805542155359">Mostrar original</translation>
<translation id="9137248913990643158">Abre Chrome e inicia sesión en el navegador para usar esta aplicación.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">Permitir a los sitios web saber si tienes métodos de pago guardados</translation>
<translation id="9169664750068251925">Bloquear siempre en este sitio</translation>
<translation id="9170848237812810038">&amp;Deshacer</translation>
+<translation id="9171296965991013597">¿Quieres salir de la aplicación?</translation>
<translation id="917450738466192189">El certificado del servidor no es válido.</translation>
<translation id="9183425211371246419">La página <ph name="HOST_NAME" /> utiliza un protocolo no admitido.</translation>
<translation id="9205078245616868884">Tus datos se han cifrado con tu frase de contraseña de sincronización. Introdúcela para iniciar la sincronización.</translation>
diff --git a/chromium/components/strings/components_strings_et.xtb b/chromium/components/strings/components_strings_et.xtb
index 6cbb8fa70ec..40e1374a218 100644
--- a/chromium/components/strings/components_strings_et.xtb
+++ b/chromium/components/strings/components_strings_et.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Peida väärtus</translation>
<translation id="1228893227497259893">Üksuse vale identifikaator</translation>
<translation id="1232569758102978740">Pealkirjata</translation>
+<translation id="1250759482327835220">Kui soovite järgmisel korral kiiremini maksta, salvestage kaart, nimi ja arveldusaadress oma Google'i kontole.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (sünkroonitud)</translation>
<translation id="1256368399071562588">&lt;p&gt;Kui üritate külastada veebisaiti ja see ei avane, proovige vea parandamiseks esmalt neid veaotsingu toiminguid.&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Kohandage kuupäeva ja kellaaega rakenduse &lt;strong&gt;Seaded&lt;/strong&gt; jaotises &lt;strong&gt;Üldine&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Veebilehe kuvamisel läks midagi valesti.</translation>
-<translation id="1590457302292452960">Looge tugev parool …</translation>
<translation id="1592005682883173041">Juurdepääs kohalikele andmetele</translation>
<translation id="1594030484168838125">Vali</translation>
<translation id="1620510694547887537">Kaamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Proovige ühendust võtta süsteemiadministraatoriga.</translation>
<translation id="1740951997222943430">Sisestage kehtiv aegumiskuu</translation>
+<translation id="1743520634839655729">Kui soovite järgmisel korral kiiremini maksta, salvestage kaart, nimi ja arveldusaadress oma Google'i kontole ja sellesse seadmesse.</translation>
<translation id="17513872634828108">Avatud vahelehed</translation>
<translation id="1753706481035618306">Lk</translation>
<translation id="1763864636252898013">Server ei suutnud tõestada, et see on domeen <ph name="DOMAIN" />, seadme operatsioonisüsteem ei usalda selle turvasertifikaati. Selle põhjuseks võib olla vale seadistus või ründaja, kes on sekkunud teie ühendusse.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Teie avatud vahelehed kuvatakse siin</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Kaardiomaniku nimi</translation>
-<translation id="1806541873155184440">Lisati kuupäeval <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Taotlus või selle parameetrid on kehtetud</translation>
<translation id="1826516787628120939">Kontrollimine</translation>
<translation id="1834321415901700177">See sait sisaldab kahjulikke programme</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 soovitus}other{# soovitust}}</translation>
<translation id="2079545284768500474">Võta tagasi</translation>
<translation id="20817612488360358">Kasutamiseks on määratud süsteemi puhverserveri seaded, kuid määratud on ka konkreetne puhverserveri konfigureerimine.</translation>
-<translation id="2084558088529668945">Sisestasite oma parooli saidile, mida ei halda <ph name="ORG_NAME" />. Oma konto kaitsmiseks ärge kasutage oma parooli muudes rakendustes ega saitidel.</translation>
<translation id="2091887806945687916">Heli</translation>
<translation id="2094505752054353250">Domeeni vastuolu</translation>
<translation id="2096368010154057602">Osakond</translation>
+<translation id="2102134110707549001">Soovita tugevat parooli …</translation>
<translation id="2108755909498034140">Taaskäivitage oma arvuti</translation>
<translation id="2113977810652731515">Kaart</translation>
<translation id="2114841414352855701">Seda ignoreeritakse, kuna reegel <ph name="POLICY_NAME" /> alistab selle.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP viga</translation>
<translation id="2270484714375784793">Telefoninumber</translation>
<translation id="2292556288342944218">Teie juurdepääs Internetile on blokeeritud</translation>
-<translation id="230155334948463882">Uus kaart?</translation>
<translation id="2316887270356262533">Vabastab alla 1 MB. Mõne saidi laadimine võib järgmisel külastusel rohkem aega võtta.</translation>
<translation id="2317259163369394535">Domeen <ph name="DOMAIN" /> nõuab kasutajanime ja parooli.</translation>
<translation id="2317583587496011522">Kaupmees aktsepteerib deebetkaarte.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Aktsepteeritavad kaardid</translation>
<translation id="2702801445560668637">Lugemisloend</translation>
<translation id="2704283930420550640">Väärtus ei vasta vormingule.</translation>
-<translation id="2704951214193499422">Chromium ei saanud praegu teie kaarti kinnitada. Proovige hiljem uuesti.</translation>
<translation id="2705137772291741111">Selle saidi (vahemällu) salvestatud koopia oli loetamatu.</translation>
<translation id="2709516037105925701">Automaatne täitmine</translation>
<translation id="2710942282213947212">Teie arvutis olev tarkvara ei luba Chromiumil veebiga ohutult ühendust luua</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Tarneviis</translation>
<translation id="277499241957683684">Seadme kirje puudub</translation>
<translation id="2781030394888168909">Ekspordi MacOS-i jaoks</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Ühendus lähtestati.</translation>
<translation id="2788784517760473862">Aktsepteeritavad krediitkaardid</translation>
<translation id="2794233252405721443">Sait on blokeeritud</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Seadete lehel saate keelata kõik ühenduse jaoks konfigureeritud puhverserverid.</translation>
<translation id="2955913368246107853">Sule leiuriba</translation>
<translation id="2958431318199492670">Võrgu seadistus ei vasta ONC standardile. On võimalik, et seadistuse mõnd osa ei saa importida.</translation>
-<translation id="2966678944701946121">Aegub: <ph name="EXPIRATION_DATE_ABBR" />. Lisati kuupäeval <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Turvalise ühenduse loomiseks peab kell olema õigesti seadistatud, kuna sertifikaadid, mida veebisaidid kasutavad enda tuvastamiseks, kehtivad ainult teatud perioodi jooksul. Kuna teie seadme kell on vale, ei saa Chrome neid sertifikaate kinnitada.</translation>
<translation id="2972581237482394796">&amp;Tee uuesti</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, praegu valitud. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Reegli tüüp on vale</translation>
<translation id="3037605927509011580">Ups, ebaõnn!</translation>
<translation id="3041612393474885105">Sertifikaadi andmed</translation>
-<translation id="3063697135517575841">Chrome ei saanud praegu teie kaarti kinnitada. Proovige hiljem uuesti.</translation>
<translation id="3064966200440839136">Väljute inkognito režiimist, et välise rakenduse kaudu maksta. Kas soovite jätkata?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Ühtegi}=1{1 parool}other{# parooli}}</translation>
<translation id="3096100844101284527">Lisage kättesaamisaadress</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Teie andmed krüpteeriti <ph name="TIME" /> teie sünkroonimisparooliga. Sisestage see sünkroonimise alustamiseks.</translation>
<translation id="3320021301628644560">Arveldusaadressi lisamine</translation>
<translation id="3338095232262050444">Turvaline</translation>
-<translation id="3340978935015468852">seaded</translation>
<translation id="3345135638360864351">Saidi külastamise taotlust ei õnnestunud saata kontaktile <ph name="NAME" />. Proovige uuesti.</translation>
<translation id="3355823806454867987">Muuda puhverserveri seadeid ...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />ei salvesta<ph name="END_EMPHASIS" /> järgmist teavet:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Serveri sertifikaat ei ole usaldusväärne.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Vähemalt 1 üksus sünkroonitud seadmetes}=1{1 üksus (ja rohkem sünkroonitud seadmetes)}other{# üksust (ja rohkem sünkroonitud seadmetes)}}</translation>
<translation id="3539171420378717834">Säilita kaardi koopia seadmes</translation>
-<translation id="3542684924769048008">Kasuta parooli:</translation>
<translation id="3549644494707163724">Krüpteerige kõik sünkroonitud andmed oma sünkroonimise parooliga</translation>
<translation id="3556433843310711081">Haldur saab blokeeringu teie eest tühistada</translation>
<translation id="3566021033012934673">Teie ühendus ei ole privaatne</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Sobimatu kinnitusallkiri</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{Veel <ph name="ITEM_COUNT" /> üksus}other{Veel <ph name="ITEM_COUNT" /> üksust}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" />: <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome soovitab teil teenuse <ph name="ORG_NAME" /> parooli lähtestada, kui kasutasite seda ka muudel saitidel.</translation>
<translation id="4196861286325780578">&amp;Teisalda uuesti</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Kontrollige tulemüüri ja viirusetõrje seadistusi<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Krahhid</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Tehtud muudatusi ei pruugita salvestada.</translation>
<translation id="4258748452823770588">Sobimatu allkiri</translation>
<translation id="4265872034478892965">Lubas administraator</translation>
-<translation id="4269787794583293679">(Kasutajanimi puudub)</translation>
<translation id="4275830172053184480">Taaskäivitage seade</translation>
<translation id="4277028893293644418">Lähtesta parool</translation>
<translation id="4280429058323657511">, aegub <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Hüpikaknad ja ümbersuunamised</translation>
<translation id="443673843213245140">Puhverserveri kasutamine on keelatud, kuid määratud on ka konkreetne puhverserveri konfigureerimine.</translation>
<translation id="445100540951337728">Aktsepteeritavad deebetkaardid</translation>
+<translation id="4472575034687746823">Alustamine</translation>
<translation id="4506176782989081258">Valideerimisviga: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Võtke ühendust süsteemiadministraatoriga</translation>
<translation id="450710068430902550">Administraatoriga jagamine</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Laadi reeglid uuesti</translation>
<translation id="4728558894243024398">Platvorm</translation>
<translation id="4736825316280949806">Taaskäivitage Chromium</translation>
+<translation id="4742407542027196863">Halda paroole …</translation>
<translation id="4744603770635761495">Täitmistee</translation>
-<translation id="4749685221585524849">Viimati kasutati kuupäeval <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Teie teave (nt paroolid või krediitkaardi numbrid) on sellele saidile saates privaatne.</translation>
<translation id="4756388243121344051">&amp;Ajalugu</translation>
<translation id="4758311279753947758">Lisage kontaktteave</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Kuva</translation>
<translation id="4854362297993841467">See kohaletoimetamisviis pole saadaval. Proovige mõnda teist kohaletoimetamisviisi.</translation>
<translation id="4858792381671956233">Küsisite oma vanematelt, kas võite seda lehte külastada</translation>
+<translation id="4876305945144899064">Kasutajanimi puudub</translation>
<translation id="4880827082731008257">Otsi ajaloost</translation>
<translation id="4881695831933465202">Ava</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Osariik</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="5098332213681597508">Nimi pärineb teie Google'i kontolt.</translation>
<translation id="5115563688576182185">(64-bitine)</translation>
<translation id="5121084798328133320">Pärast kinnitamist jagatakse teie Google Paymentsi konto kaardi üksikasju selle saidiga.</translation>
<translation id="5128122789703661928">Selle nimega seanssi ei saa kustutada.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Tarneviis</translation>
<translation id="5355557959165512791">Te ei saa saiti <ph name="SITE" /> praegu külastada, sest selle sertifikaat on tühistatud. Võrguvead ja -rünnakud on tavaliselt ajutised, nii et leht tõenäoliselt hiljem töötab.</translation>
<translation id="536296301121032821">Reegli seadete talletamine ebaõnnestus</translation>
+<translation id="5371425731340848620">Värskendage kaarti</translation>
<translation id="5377026284221673050">„Teie kell on ajast maas” või „Teie kell on ajast ees” või „&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;”</translation>
<translation id="5386426401304769735">Selle saidi sertifikaadiahel sisaldab sertifikaati, mis on allkirjastatud SHA-1-ga.</translation>
-<translation id="5402410679244714488">Aegub: <ph name="EXPIRATION_DATE_ABBR" />, viimati kasutati üle aasta tagasi</translation>
+<translation id="5387961145478138773">Hankige kiirem juurdepääs oma Google'i lemmikrakendustele</translation>
<translation id="540969355065856584">Server ei suutnud tõestada, et see on domeen <ph name="DOMAIN" />; selle turvasertifikaat pole praegu kehtiv. Selle põhjuseks võib olla vale seadistus või ründaja, kes on sekkunud teie ühendusse.</translation>
<translation id="5421136146218899937">Sirvimisandmete kustutamine ...</translation>
<translation id="5430298929874300616">Järjehoidja eemaldamine</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">Meil</translation>
+<translation id="5666899935841546222">Kas soovite salvestada kõik oma kaardid ühte kohta?</translation>
<translation id="5675650730144413517">See leht ei tööta</translation>
<translation id="5685654322157854305">Lisage tarneaadress</translation>
<translation id="5689199277474810259">Ekspordi JSON-vormingus</translation>
<translation id="5689516760719285838">Asukoht</translation>
<translation id="570530837424789914">Halda …</translation>
+<translation id="57094364128775171">Soovita tugevat parooli …</translation>
<translation id="5710435578057952990">Selle veebisaidi identiteeti pole kinnitanud.</translation>
<translation id="5719499550583120431">Kaupmees aktsepteerib ettemakstud kaarte.</translation>
<translation id="5720705177508910913">Praegune kasutaja</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Selle saidiga ei saa ühendust</translation>
<translation id="5869522115854928033">Salvestatud paroolid</translation>
<translation id="5893752035575986141">Kaupmees aktsepteerib krediitkaarte.</translation>
-<translation id="5898382028489516745">Chromium soovitab teil teenuse <ph name="ORG_NAME" /> parooli lähtestada, kui kasutasite seda ka muudel saitidel.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (sünkroonitud)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 on kasutusel}other{# on kasutusel}}</translation>
<translation id="5939518447894949180">Lähtesta</translation>
-<translation id="5959728338436674663">Saatke Google'ile automaatselt <ph name="BEGIN_WHITEPAPER_LINK" />süsteemiteavet ja lehe sisu<ph name="END_WHITEPAPER_LINK" />, et aidata tuvastada ohtlikke rakendusi ja saite. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Kontaktteabe muutmine</translation>
<translation id="5967867314010545767">Eemalda ajaloost</translation>
<translation id="5975083100439434680">Suumib välja</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Sihtnumber</translation>
<translation id="6290238015253830360">Teie soovitatud artiklid kuvatakse siin</translation>
<translation id="6305205051461490394">URL-iga <ph name="URL" /> ei saa ühendust.</translation>
-<translation id="6319915415804115995">Viimati kasutati üle aasta tagasi</translation>
<translation id="6321917430147971392">Kontrollige DNS-i seadeid</translation>
<translation id="6328639280570009161">Proovige keelata võrguprognoos</translation>
<translation id="6328786501058569169">See sait on petturlik</translation>
<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="6342069812937806050">Äsja</translation>
<translation id="6355080345576803305">Avaliku seansi alistamine</translation>
<translation id="6358450015545214790">Mida need tähendavad?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{veel 1 soovitus}other{veel # soovitust}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Kustuta uuesti</translation>
<translation id="6534179046333460208">Füüsilise veebi soovitused</translation>
<translation id="6550675742724504774">Valikud</translation>
-<translation id="6556915248009097796">Aegub: <ph name="EXPIRATION_DATE_ABBR" />. Viimati kasutati kuupäeval <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Haldur ei ole seda veel kinnitanud</translation>
<translation id="6569060085658103619">Vaatate laienduse lehte</translation>
<translation id="6596325263575161958">Krüpteerimise valikud</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Püüdsite jõuda domeenile <ph name="DOMAIN" />, kuid server esitas sertifikaadi, mis on allkirjastatud nõrga allkirjaalgoritmiga (nt SHA-1). See tähendab, et serveri esitatud turvamandaadid võivad olla võltsitud ja server ei pruugi olla see, mida eeldate (võimalik, et suhtlete ründajaga).</translation>
<translation id="6831043979455480757">Tõlgi</translation>
<translation id="6839929833149231406">Piirkond</translation>
+<translation id="6852204201400771460">Kas soovite rakenduse uuesti laadida?</translation>
+<translation id="6865412394715372076">Seda kaarti ei saa praegu kinnitada</translation>
<translation id="6874604403660855544">&amp;Lisa uuesti</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Reegli taset ei toetata.</translation>
<translation id="6895330447102777224">Teie kaart on kinnitatud</translation>
<translation id="6897140037006041989">Kasutajaagent</translation>
+<translation id="6903319715792422884">Aidake ohutu sirvimise funktsiooni täiustada, saates Google'ile <ph name="BEGIN_WHITEPAPER_LINK" />süsteemiteavet ja lehesisu<ph name="END_WHITEPAPER_LINK" />. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Kasutaja:</translation>
+<translation id="6944692733090228304">Sisestasite oma parooli saidile, mida ei halda <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Oma konto kaitsmiseks ärge kasutage oma parooli muudes rakendustes ega saitidel.</translation>
<translation id="6945221475159498467">Vali</translation>
<translation id="6948701128805548767">Kättesaamisviiside ja nõuete nägemiseks valige aadress</translation>
<translation id="6949872517221025916">Lähtestage parool</translation>
+<translation id="6950684638814147129">Viga JSON-väärtuse sõelumisel: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Serveri sertifikaat näib olevat võltsing.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Seade</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Klõpsake käsul &lt;strong&gt;Apply&lt;/strong&gt; ja seejärel valikul &lt;strong&gt;OK&lt;/strong&gt;
&lt;li&gt;Vaadake &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome'i abikeskusest&lt;/a&gt; teavet selle kohta, kuidas tarkvara arvutist jäädavalt eemaldada
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Halda paroole …</translation>
<translation id="7419106976560586862">Profiili tee</translation>
<translation id="7437289804838430631">Lisa kontaktteave</translation>
<translation id="7441627299479586546">Reegli objekt on vale</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Vaadake lisateavet<ph name="END_LINK" /> probleemi kohta.</translation>
<translation id="7455133967321480974">Kasuta globaalset vaikeseadet (blokeeri)</translation>
<translation id="7460163899615895653">Siin kuvatakse teie hiljutised vahelehed teistest seadmetest</translation>
-<translation id="7469372306589899959">Kaarti kinnitatakse</translation>
<translation id="7473891865547856676">Tänan, ei</translation>
<translation id="7481312909269577407">Edasta</translation>
<translation id="7485870689360869515">Andmeid ei leitud.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Tõlkimine ebaõnnestus võrguühenduse probleemi tõttu.</translation>
<translation id="8311129316111205805">Laadi seanss</translation>
<translation id="8332188693563227489">Juurdepääs hostile <ph name="HOST_NAME" /> blokeeriti</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Kui mõistate, kuidas teie turvalisust ohustatakse, siis võite <ph name="BEGIN_LINK" />seda saiti külastada<ph name="END_LINK" /> enne, kui kahjulikud programmid on eemaldatud.</translation>
<translation id="8349305172487531364">Järjehoidjariba</translation>
<translation id="8363502534493474904">Lülitage lennurežiim välja</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222">Hostil <ph name="HOST_NAME" /> kulus vastamiseks liiga kaua aega.</translation>
<translation id="8503559462189395349">Chrome'i paroolid</translation>
<translation id="8503813439785031346">Kasutajanimi</translation>
+<translation id="8508648098325802031">Otsinguikoon</translation>
<translation id="8543181531796978784">Võite <ph name="BEGIN_ERROR_LINK" />teavitada tuvastusprobleemist<ph name="END_ERROR_LINK" />. Kui mõistate ohtusid oma turvalisusele, võite <ph name="BEGIN_LINK" />seda ebaturvalist saiti külastada<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Kas teil on küsimusi? Võtke ühendust profiili operaatoriga.</translation>
<translation id="8553075262323480129">Tõlkimine nurjus, kuna lehe keelt ei õnnestunud määrata.</translation>
<translation id="8557066899867184262">CVC asub kaardi tagaküljel.</translation>
<translation id="8559762987265718583">Privaatset ühendust ei saa domeeniga <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> luua, kuna seadme kuupäev ja kellaaeg (<ph name="DATE_AND_TIME" />) on valed.</translation>
+<translation id="8564985650692024650">Chromium soovitab teil organisatsiooni <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> parooli lähtestada, kui kasutasite seda ka muudel saitidel.</translation>
<translation id="8571890674111243710">Lehe tõlkimine <ph name="LANGUAGE" /> keelde...</translation>
<translation id="858637041960032120">Lisage telefoninumber</translation>
<translation id="859285277496340001">Sertifikaat ei määratle mehhanismi enda võimaliku tühistamise kontrollimiseks.</translation>
+<translation id="860043288473659153">Kaardiomaniku nimi</translation>
<translation id="8620436878122366504">Vanemad ei ole seda veel kinnitanud</translation>
<translation id="8625384913736129811">Salvesta kaart sellesse seadmesse</translation>
-<translation id="8639963783467694461">Automaatse täitmise seaded</translation>
+<translation id="8663226718884576429">Tellimuse kokkuvõte, <ph name="TOTAL_LABEL" />, rohkem üksikasju</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, vastus, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Teie ühendus <ph name="DOMAIN" />'ga pole krüptitud.</translation>
<translation id="8718314106902482036">Makset ei viidud lõpule</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, otsingusoovitus</translation>
<translation id="8725066075913043281">Proovi uuesti</translation>
<translation id="8728672262656704056">Olete inkognito režiimis</translation>
<translation id="8730621377337864115">Valmis</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Järjehoidjad</translation>
<translation id="883848425547221593">Muud järjehoidjad</translation>
<translation id="884264119367021077">Tarneaadress</translation>
+<translation id="8846319957959474018">Järjehoidjate abil saate hõlpsasti rakendusi avada</translation>
<translation id="884923133447025588">Tühistusmehhanismi ei leitud.</translation>
<translation id="885730110891505394">Jagamine Google'iga</translation>
+<translation id="8858065207712248076">Chromium soovitab teil organisatsiooni <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> parooli lähtestada, kui kasutasite seda ka muudel saitidel.</translation>
<translation id="8866481888320382733">Reegli seadete sõelumisel ilmnes viga</translation>
<translation id="8870413625673593573">Viimati suletud</translation>
<translation id="8874824191258364635">Sisestaeg kehtiv kaardinumber</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690">Sait <ph name="SITE" /> kasutab teie teabe kaitsmiseks tavaliselt krüpteerimist. Kui Chromium püüdis seekord saidiga <ph name="SITE" /> ühendust luua, tagastas veebisait ebatavalised ja valed mandaadid. See võib juhtuda siis, kui ründaja proovib teeselda, et on sait <ph name="SITE" />, või WiFi sisselogimisekraan on ühenduse katkestanud. Teie teave on endiselt kaitstud, sest Chromium peatas ühenduse enne andmevahetust.</translation>
<translation id="9106062320799175032">Arveldusaadressi lisamine</translation>
<translation id="910908805481542201">Aita mul seda parandada</translation>
+<translation id="9114524666733003316">Kaardi kinnitamine …</translation>
<translation id="9128870381267983090">Ühendumine Internetiga</translation>
<translation id="9137013805542155359">Kuva originaal</translation>
<translation id="9137248913990643158">Enne selle rakenduse kasutamist alustage ja logige Chrome'i sisse.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">Luba saitidel kontrollida, kas olete makseviisid salvestanud</translation>
<translation id="9169664750068251925">Blokeeri sellel saidil alati</translation>
<translation id="9170848237812810038">&amp;Võta tagasi</translation>
+<translation id="9171296965991013597">Kas soovite rakendusest väljuda?</translation>
<translation id="917450738466192189">Serveri sertifikaat on kehtetu.</translation>
<translation id="9183425211371246419">Host <ph name="HOST_NAME" /> kasutab toetamata protokolli.</translation>
<translation id="9205078245616868884">Teie andmed on krüpteeritud teie sünkroonimisparooliga. Sisestage see sünkroonimise alustamiseks.</translation>
diff --git a/chromium/components/strings/components_strings_fa.xtb b/chromium/components/strings/components_strings_fa.xtb
index 331d98a79e6..22cbb663ca6 100644
--- a/chromium/components/strings/components_strings_fa.xtb
+++ b/chromium/components/strings/components_strings_fa.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">پنهان کردن مقدار</translation>
<translation id="1228893227497259893">‏شناسه entity نادرست</translation>
<translation id="1232569758102978740">بدون عنوان</translation>
+<translation id="1250759482327835220">‏برای اینکه دفعه بعد پرداخت سریع‌تری داشته باشید، اطلاعات کارت، نام و نشانی صورت‌حسابتان را در حساب Google خود ذخیره کنید.</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;
@@ -97,7 +98,6 @@
&lt;p&gt;لطفاً تاریخ و زمان را در بخش &lt;strong&gt;عمومی&lt;/strong&gt; برنامه &lt;strong&gt;تنظیمات&lt;/strong&gt; تنظیم کنید.&lt;/p&gt;</translation>
<translation id="1583429793053364125">هنگام نمایش این صفحه وب مشکلی پیش آمد.</translation>
-<translation id="1590457302292452960">ایجاد گذرواژه قوی…</translation>
<translation id="1592005682883173041">دسترسی داده محلی</translation>
<translation id="1594030484168838125">انتخاب</translation>
<translation id="1620510694547887537">دوربین</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">با سرپرست سیستم تماس بگیرید.</translation>
<translation id="1740951997222943430">ماه انقضای معتبری وارد کنید</translation>
+<translation id="1743520634839655729">‏برای اینکه دفعه بعد پرداخت سریع‌تری داشته باشید، اطلاعات کارت، نام و نشانی صورت‌حسابتان را در حساب Google خود و این دستگاه ذخیره کنید.</translation>
<translation id="17513872634828108">بازکردن برگه‌ها</translation>
<translation id="1753706481035618306">شماره صفحه</translation>
<translation id="1763864636252898013">این سرور نتوانست اثبات کند که این <ph name="DOMAIN" /> است؛ گواهی امنیتی آن مورداعتماد سیستم عامل دستگاه شما نیست. ممکن است علت این موضوع پیکربندی اشتباه باشد یا مهاجی اتصال شما را قطع کرده است.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">برگه‌های بازتان در اینجا نشان داده می‌شوند</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">نام صاحب کارت</translation>
-<translation id="1806541873155184440">تاریخ اضافه شدن: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">پارامترهای درخواست یا درخواست نامعتبر</translation>
<translation id="1826516787628120939">در حال بررسی</translation>
<translation id="1834321415901700177">این سایت محتوی برنامه‌های مضر است</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{۱ پیشنهاد}one{# پیشنهاد}other{# پیشنهاد}}</translation>
<translation id="2079545284768500474">لغو</translation>
<translation id="20817612488360358">تنظیمات پروکسی سیستم تنظیم شده تا مورد استفاده قرار گیرد، اما یک پیکربندی مشخص برای پروکسی نیز تعیین شده است.</translation>
-<translation id="2084558088529668945">گذرواژه‌تان را در سایتی که توسط <ph name="ORG_NAME" /> مدیریت نمی‌شود وارد کردید. برای محافظت از حسابتان، از گذرواژه‌تان در سایر برنامه‌ها و سایت‌ها مجدداً استفاده نکنید.</translation>
<translation id="2091887806945687916">صدا</translation>
<translation id="2094505752054353250">عدم تطابق دامنه</translation>
<translation id="2096368010154057602">حوزه</translation>
+<translation id="2102134110707549001">پیشنهاد گذرواژه قوی…</translation>
<translation id="2108755909498034140">رایانه را راه‌اندازی مجدد کنید</translation>
<translation id="2113977810652731515">کارت</translation>
<translation id="2114841414352855701">نادیده گرفته شد زیرا <ph name="POLICY_NAME" /> آن را لغو می‌کند.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">‏خطای HTTP</translation>
<translation id="2270484714375784793">شماره تلفن</translation>
<translation id="2292556288342944218">دسترسی شما به اینترنت مسدود است</translation>
-<translation id="230155334948463882">کارت جدید؟</translation>
<translation id="2316887270356262533">کمتر از ۱ مگابایت از فضا را آزاد می‌کند. ممکن است برخی از سایت‌ها در بازدیدهای بعدی کندتر بارگیری شوند.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> به نام کاربری و گذرواژه نیاز دارد.</translation>
<translation id="2317583587496011522">کارت‌های نقدی پذیرفته می‌شوند.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">کارت‌های قابل‌‌قبول</translation>
<translation id="2702801445560668637">فهرست خواندن</translation>
<translation id="2704283930420550640">مقدار با فرمت مطابقت ندارد.</translation>
-<translation id="2704951214193499422">‏Chromium درحال حاضر نمی‌تواند کارت شما را تأیید کند. لطفاً بعداً دوباره امتحان کنید.</translation>
<translation id="2705137772291741111">کپی ذخیره‌شده (ذخیره موقت‌شده) این سایت قابل خواندن نبود.</translation>
<translation id="2709516037105925701">تکمیل خودکار</translation>
<translation id="2710942282213947212">‏نرم‌افزاری در رایانه‌تان مانع از اتصال ایمن Chromium به وب می‌شود</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">روش ارسال</translation>
<translation id="277499241957683684">ثبت دستگاه موجود نیست</translation>
<translation id="2781030394888168909">‏صادر کردن سیستم‌عامل Mac</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">اتصال مجدداً برقرار شد.</translation>
<translation id="2788784517760473862">کارت‌های اعتباری قابل‌قبول</translation>
<translation id="2794233252405721443">سایت مسدودشده</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">در صفحه تنظیمات می‌توانید همه پراکسی‌های پیکربندی شده برای هر اتصال را از کار بیاندازید.</translation>
<translation id="2955913368246107853">بستن نوار یافتن</translation>
<translation id="2958431318199492670">‏پیکربندی شبکه با استاندارد ONC تطابق ندارد. بخشی از پیکربندی ممکن است وارد نشود.</translation>
-<translation id="2966678944701946121">تاریخ انقضا: <ph name="EXPIRATION_DATE_ABBR" />، تاریخ اضافه شدن: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">‏برای برقراری یک اتصال امن، لازم است ساعت شما درست تنظیم شده باشد. زیرا گواهی‌هایی که وب‌سایت‌ها برای شناسایی خودشان استفاده می‌کنند، تنها برای دوره‌های زمانی خاصی معتبرند. از آنجا که ساعت دستگاه شما نادرست است، Google Chrome نمی‌تواند این گواهی‌ها را تأیید کند.</translation>
<translation id="2972581237482394796">انجام مجدد</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />، درحال‌حاضر انتخاب شده است. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">نوع خط‌مشی اشتباه است</translation>
<translation id="3037605927509011580">اوه، نه!</translation>
<translation id="3041612393474885105">اطلاعات گواهی</translation>
-<translation id="3063697135517575841">‏Chrome درحال حاضر نمی‌تواند کارت شما را تأیید کند. لطفاً بعداً دوباره امتحان کنید.</translation>
<translation id="3064966200440839136">درحال خروج از حالت ناشناس، برای پرداخت ازطریق یک برنامه خارجی. ادامه می‌دهید؟</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{هیچ‌کدام}=1{۱ گذرواژه}one{# گذرواژه}other{# گذرواژه}}</translation>
<translation id="3096100844101284527">افزودن نشانی تحویل گرفتن</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">داده‌های شما در تاریخ <ph name="TIME" /> با عبارت عبور همگام‌سازی‌تان رمزگذاری شد. برای شروع همگام‌سازی آن را وارد کنید.</translation>
<translation id="3320021301628644560">افزودن نشانی صورتحساب</translation>
<translation id="3338095232262050444">ایمن</translation>
-<translation id="3340978935015468852">تنظیمات</translation>
<translation id="3345135638360864351">درخواست شما برای دسترسی به این سایت به <ph name="NAME" /> ارسال نشد. لطفاً دوباره امتحان کنید.</translation>
<translation id="3355823806454867987">تغییر تنظیمات پروکسی...</translation>
<translation id="3361596688432910856">‏Chrome اطلاعات زیر را <ph name="BEGIN_EMPHASIS" />ذخیره نخواهد کرد<ph name="END_EMPHASIS" />:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">گواهی سرور مطمئن نیست.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{حداقل ۱ مورد در دستگاه‌های همگام‌سازی‌شده}=1{۱ مورد (و بیشتر در دستگاه‌های همگام‌سازی‌شده)}one{# مورد (و بیشتر در دستگاه‌های همگام‌سازی‌شده)}other{# مورد (و بیشتر در دستگاه‌های همگام‌سازی‌شده)}}</translation>
<translation id="3539171420378717834">یک کپی از این کارت در این دستگاه نگهداری شود</translation>
-<translation id="3542684924769048008">استفاده از گذرواژه برای:</translation>
<translation id="3549644494707163724">رمزگذاری همه داده‌های همگام‌سازی‌شده با رمزعبارتی همگام‌سازی خودتان</translation>
<translation id="3556433843310711081">مدیرتان می‌تواند این سایت را برای شما بگشاید</translation>
<translation id="3566021033012934673">اتصال شما خصوصی نیست</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">امضای تأیید نامناسب</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> مورد دیگر}one{<ph name="ITEM_COUNT" /> مورد دیگر}other{<ph name="ITEM_COUNT" /> مورد دیگر}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">‏Chrome توصیه می‌کند گذرواژه <ph name="ORG_NAME" /> خود را درصورت استفاده مجدد از آن در سایر سایت‌ها بازنشانی کنید.</translation>
<translation id="4196861286325780578">&amp;انجام مجدد انتقال</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />بررسی پیکربندی آنتی‌ویروس و دیوار آتش<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">خرابی ها</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">تغییراتی که انجام داده‌اید ممکن است ذخیره نشده باشند.</translation>
<translation id="4258748452823770588">امضای نادرست</translation>
<translation id="4265872034478892965">توسط سرپرست مجاز شده است</translation>
-<translation id="4269787794583293679">(بدون نام کاربری)</translation>
<translation id="4275830172053184480">راه‌اندازی دستگاه خود</translation>
<translation id="4277028893293644418">بازنشانی گذرواژه</translation>
<translation id="4280429058323657511">، انقضا <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">پنجره‌های بازشو و هدایت‌ها</translation>
<translation id="443673843213245140">استفاده از پروکسی غیرفعال است اما یک پیکربندی خاص برای پروکسی تعیین شده است.</translation>
<translation id="445100540951337728">کارت‌های نقدی قابل‌قبول</translation>
+<translation id="4472575034687746823">شروع به کار</translation>
<translation id="4506176782989081258">خطای ارزیابی: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">تماس با سرپرست سیستم</translation>
<translation id="450710068430902550">اشتراک‌گذاری با سرپرست سیستم</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">تازه‌سازی خط مشی‌ها</translation>
<translation id="4728558894243024398">پلت فورم</translation>
<translation id="4736825316280949806">‏Chromium را راه‌اندازی مجدد کنید</translation>
+<translation id="4742407542027196863">مدیریت گذرواژه‌ها…</translation>
<translation id="4744603770635761495">مسیر قابل اجرا</translation>
-<translation id="4749685221585524849">آخرین استفاده: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">اطلاعات شما (به‌عنوان مثال، گذرواژه‌ها یا شماره‌ کارت‌های اعتباری) وقتی به این سایت ارسال می‌شوند، خصوصی هستند.</translation>
<translation id="4756388243121344051">&amp;سابقه</translation>
<translation id="4758311279753947758">افزودن اطلاعات تماس</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">نما</translation>
<translation id="4854362297993841467">این روش تحویل در دسترس نیست. روش دیگری را امتحان کنید.</translation>
<translation id="4858792381671956233">از والدینتان پرسیدید آیا اجازه بازدید از این سایت را دارید</translation>
+<translation id="4876305945144899064">بدون نام کاربری</translation>
<translation id="4880827082731008257">سابقه جستجو</translation>
<translation id="4881695831933465202">باز کردن</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />،‏ <ph name="TYPE_2" />،‏ <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">ایالت</translation>
<translation id="5094747076828555589">‏این سرور نتوانست اثبات کند که این <ph name="DOMAIN" /> است؛ گواهی امنیت آن مورداعتماد Chromium نیست. علت این موضوع می‌توان پیکربندی اشتباه باشد یا مهاجمی اتصال شما را قطع کرده است.</translation>
<translation id="5095208057601539847">استان</translation>
+<translation id="5098332213681597508">‏این نام از حساب Google شما گرفته شده است.</translation>
<translation id="5115563688576182185">(۶۴ بیت)</translation>
<translation id="5121084798328133320">‏بعد از تأیید، جزئیات کارت از حساب «پرداخت‌های Google» با این سایت هم‌رسانی می‌شود.</translation>
<translation id="5128122789703661928">جلسه‌ای با این نام، برای حذف معتبر نیست.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">روش ارسال کالا</translation>
<translation id="5355557959165512791">درحال‌حاضر نمی‌توانید از <ph name="SITE" /> دیدن کنید، زیرا گواهینامه آن لغو شده است. معمولاً خطاهای شبکه و حمله‌ها موقتی هستند، بنابراین احتمالاً این صفحه بعداً کار خواهد کرد.</translation>
<translation id="536296301121032821">تنظیمات خط‌‌مشی ذخیره نشد</translation>
+<translation id="5371425731340848620">به‌روزرسانی کارت</translation>
<translation id="5377026284221673050">‏«ساعتتان عقب است» یا «ساعتتان جلو است» یا «&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;»</translation>
<translation id="5386426401304769735">‏زنجیره گواهی این سایت حاوی یک گواهی با امضای SHA-1 است.</translation>
-<translation id="5402410679244714488">انقضا: <ph name="EXPIRATION_DATE_ABBR" />، آخرین استفاده، بیش از یک سال قبل</translation>
+<translation id="5387961145478138773">‏دسترسی سریع به برنامه‌های Google دلخواه</translation>
<translation id="540969355065856584">این سرور نتوانست ثابت کند که <ph name="DOMAIN" /> است؛ در حال حاضر، گواهی امنیتی آن معتبر نیست. ممکن است این مشکل به دلیل پیکربندی نادرست یا قطع اتصال شما توسط حمله‌کننده ایجاد شده باشد.</translation>
<translation id="5421136146218899937">پاک کردن داده‌های مرور...</translation>
<translation id="5430298929874300616">حذف نشانک</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">ایمیل</translation>
+<translation id="5666899935841546222">می‌خواهید همه کارت‌هایتان را در یک مکان داشته باشید؟</translation>
<translation id="5675650730144413517">این صفحه کار نمی‌کند</translation>
<translation id="5685654322157854305">افزودن نشانی تحویل کالا</translation>
<translation id="5689199277474810259">‏صادر کردن به JSON</translation>
<translation id="5689516760719285838">مکان</translation>
<translation id="570530837424789914">مدیریت…</translation>
+<translation id="57094364128775171">پیشنهاد گذرواژه قوی…</translation>
<translation id="5710435578057952990">هویت این وب سایت تأیید نشده است.</translation>
<translation id="5719499550583120431">کارت‌های پیش‌پرداخت پذیرفته می‌شوند.</translation>
<translation id="5720705177508910913">کاربر کنونی</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">دسترسی به این سایت امکان‌پذیر نیست</translation>
<translation id="5869522115854928033">گذرواژه‌های ذخیره‌شده</translation>
<translation id="5893752035575986141">کارت‌های اعتباری پذیرفته می‌شوند.</translation>
-<translation id="5898382028489516745">‏Chromium توصیه می‌کند گذرواژه <ph name="ORG_NAME" /> خود را درصورت استفاده مجدد از آن در سایر سایت‌ها بازنشانی کنید.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (همگام‌سازی‌شده)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{۱ کوکی درحال استفاده}one{# کوکی درحال استفاده}other{# کوکی درحال استفاده}}</translation>
<translation id="5939518447894949180">بازنشانی</translation>
-<translation id="5959728338436674663">‏ارسال خودکار برخی از <ph name="BEGIN_WHITEPAPER_LINK" />اطلاعات سیستم و محتوای صفحه<ph name="END_WHITEPAPER_LINK" /> به Google برای کمک به شناسایی برنامه‌ها و سایت‌های خطرناک. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">ویرایش اطلاعات تماس</translation>
<translation id="5967867314010545767">حذف از سابقه</translation>
<translation id="5975083100439434680">کوچک نمایی</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">کد پستی</translation>
<translation id="6290238015253830360">مقاله‌های پیشنهادی شما در اینجا نشان داده می‌شوند</translation>
<translation id="6305205051461490394">دسترسی به <ph name="URL" /> امکان‌پذیر نیست.</translation>
-<translation id="6319915415804115995">آخرین استفاده، بیش از یک سال قبل</translation>
<translation id="6321917430147971392">‏تنظیمات DNS خودتان را بررسی کنید</translation>
<translation id="6328639280570009161">غیرفعال کردن پیش‌بینی شبکه را امتحان کنید</translation>
<translation id="6328786501058569169">این سایت گول‌زننده است</translation>
<translation id="6337133576188860026">کمتر از <ph name="SIZE" /> از فضا را آزاد می‌کند. ممکن است برخی از سایت‌ها در بازدیدهای بعدی کندتر بارگیری شوند.</translation>
<translation id="6337534724793800597">فیلتر کردن خط‌مشی‌ها براساس نام</translation>
-<translation id="6342069812937806050">فقط اکنون</translation>
<translation id="6355080345576803305">لغو جلسه عمومی</translation>
<translation id="6358450015545214790">معنی این‌ها چیست؟</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{۱ پیشنهاد دیگر}one{# پیشنهاد دیگر}other{# پیشنهاد دیگر}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;انجام مجدد حذف</translation>
<translation id="6534179046333460208">پیشنهادهای «وب فیزیکی»</translation>
<translation id="6550675742724504774">گزینه‌ها</translation>
-<translation id="6556915248009097796">تاریخ انقضا: <ph name="EXPIRATION_DATE_ABBR" />، آخرین استفاده: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">مدیرتان هنوز این سایت را تأیید نکرده است</translation>
<translation id="6569060085658103619">درحال مشاهده یک صفحه افزونه هستید</translation>
<translation id="6596325263575161958">گزینه‌های رمزگذاری</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">‏تلاش کردید به <ph name="DOMAIN" /> دسترسی داشته باشید، اما گواهینامه ارائه‌شده از سوی سرور با استفاده از یک الگوریتم امضای ضعیف (مانند SHA-1) امضا شده است. یعنی ممکن است اعتبارنامه امنیتی ارائه‌شده از سوی سرور جعلی باشد و ممکن است سرور، سرور موردانتظار شما نباشد (ممکن است با یک مهاجم در ارتباط باشید).</translation>
<translation id="6831043979455480757">ترجمه</translation>
<translation id="6839929833149231406">منطقه حکومتی</translation>
+<translation id="6852204201400771460">برنامه تازه‌سازی شود؟</translation>
+<translation id="6865412394715372076">درحال‌حاضر نمی‌توان این کارت را به تأیید رساند</translation>
<translation id="6874604403660855544">&amp;انجام مجدد افزودن</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">سطح خط‌مشی پشتیبانی نمی‌شود.</translation>
<translation id="6895330447102777224">کارتتان تأیید شد</translation>
<translation id="6897140037006041989">نماینده کاربر</translation>
+<translation id="6903319715792422884">‏با ارسال برخی <ph name="BEGIN_WHITEPAPER_LINK" />اطلاعات سیستم و محتوای صفحه<ph name="END_WHITEPAPER_LINK" /> به Google، به بهبود «مرور ایمن» کمک کنید. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">کاربر:</translation>
+<translation id="6944692733090228304">گذرواژه‌تان را در سایتی وارد کردید که توسط <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> مدیریت نمی‌شود. برای محافظت از حسابتان، از گذرواژه‌تان در برنامه‌ها و سایت‌های دیگر استفاده نکنید.</translation>
<translation id="6945221475159498467">انتخاب</translation>
<translation id="6948701128805548767">برای دیدن روش‌های تحویل گرفتن و شرایط موردنیاز، یک نشانی انتخاب کنید</translation>
<translation id="6949872517221025916">بازنشانی گذرواژه</translation>
+<translation id="6950684638814147129">‏خطا هنگام تجزیه مقدار JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">به نظر می‌رسد که گواهی سرور جعلی باشد.</translation>
<translation id="6965382102122355670">قبول</translation>
<translation id="6965978654500191972">دستگاه</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;روی &lt;strong&gt;اعمال&lt;/strong&gt; و سپس &lt;strong&gt;تأیید&lt;/strong&gt; کلیک کنید
&lt;li&gt;برای آشنایی با نحوه برداشتن دائم نرم‌افزار از رایانه، از &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;مرکز راهنمای ‏Chrome‏&lt;/a&gt; بازدید کنید
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">مدیریت گذرواژه‌ها…</translation>
<translation id="7419106976560586862">مسیر نمایه</translation>
<translation id="7437289804838430631">افرودن اطلاعات تماس</translation>
<translation id="7441627299479586546">موضوع خط‌مشی اشتباه است</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790">درباره این مشکل <ph name="BEGIN_LINK" />بیشتر بدانید<ph name="END_LINK" />.</translation>
<translation id="7455133967321480974">استفاده از پیش‌فرض جهانی (مسدود)</translation>
<translation id="7460163899615895653">برگه‌های اخیر شما از دیگر دستگاه‌ها اینجا نشان داده می‌شوند</translation>
-<translation id="7469372306589899959">درحال تایید کارت</translation>
<translation id="7473891865547856676">نه متشکرم</translation>
<translation id="7481312909269577407">ارسال کردن</translation>
<translation id="7485870689360869515">هیچ داده‌ای یافت نشد.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">ترجمه انجام نشد چون مشکلی در اتصال به شبکه رخ داد.</translation>
<translation id="8311129316111205805">بار کردن جلسه</translation>
<translation id="8332188693563227489">دسترسی به <ph name="HOST_NAME" /> رد شد</translation>
+<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="8349305172487531364">نوار نشانک‌ها</translation>
<translation id="8363502534493474904">خاموش کردن حالت هواپیما</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222">پاسخ <ph name="HOST_NAME" /> بیش از حد طول کشیده است.</translation>
<translation id="8503559462189395349">‏گذرواژه‌های Chrome</translation>
<translation id="8503813439785031346">نام کاربری</translation>
+<translation id="8508648098325802031">نماد جستجو</translation>
<translation id="8543181531796978784">می‌توانید <ph name="BEGIN_ERROR_LINK" />یک مشکل شناسایی‌شده را گزارش کنید<ph name="END_ERROR_LINK" /> یا اگر از خطراتی که امنیت شما را تهدید می‌کنند مطلع شدید، <ph name="BEGIN_LINK" />از این سایت ناامن دیدن کنید<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">سؤال دارید؟ با فردی که بر نمایه‌تان نظارت دارد تماس بگیرید.</translation>
<translation id="8553075262323480129">ترجمه انجام نشد زیرا زبان صفحه تعیین نشد.</translation>
<translation id="8557066899867184262">‏CVC در پشت کارتتان قرار گرفته است.</translation>
<translation id="8559762987265718583">اتصال خصوصی به <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> انجام نمی‌شود، زیرا تاریخ و زمان دستگاه شما (<ph name="DATE_AND_TIME" />) نادرست است.</translation>
+<translation id="8564985650692024650">‏Chromium توصیه می‌کند اگر از گذرواژه <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> خود در سایت دیگری استفاده کردید آن را بازنشانی کنید.</translation>
<translation id="8571890674111243710">ترجمه صفحه به <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">افزودن شماره تلفن</translation>
<translation id="859285277496340001">این مجوز هیچ مکانیزمی را برای بررسی اینکه آیا باطل شده یا نه مشخص نمی‌کند.</translation>
+<translation id="860043288473659153">نام صاحب کارت</translation>
<translation id="8620436878122366504">والدینتان هنوز این سایت را تأیید نکرده‌اند</translation>
<translation id="8625384913736129811">ذخیره کردن این کارت در این دستگاه</translation>
-<translation id="8639963783467694461">تنظیمات تکمیل خودکار</translation>
+<translation id="8663226718884576429">خلاصه سفارش، <ph name="TOTAL_LABEL" />، جزئيات بیشتر</translation>
<translation id="8680536109547170164"><ph name="QUERY" />، پاسخ، <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">اتصال شما به <ph name="DOMAIN" /> رمزگذاری نشده است.</translation>
<translation id="8718314106902482036">پرداخت کامل نشد</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />، <ph name="DESCRIPTION" />، پیشنهاد جستجو</translation>
<translation id="8725066075913043281">سعی مجدد</translation>
<translation id="8728672262656704056">اکنون در حالت ناشناس هستید</translation>
<translation id="8730621377337864115">تمام</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">نشانک‌ها</translation>
<translation id="883848425547221593">نشانک‌های دیگر</translation>
<translation id="884264119367021077">اطلاعات ارسال</translation>
+<translation id="8846319957959474018">برنامه‌ها را به‌سادگی بااستفاده از نشانک‌ها باز کنید</translation>
<translation id="884923133447025588">هیچ مکانیزم ابطالی یافت نشد.</translation>
<translation id="885730110891505394">‏اشتراک‌گذاری با Google</translation>
+<translation id="8858065207712248076">‏Chrome توصیه می‌کند اگر از گذرواژه <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> خود در سایت دیگری استفاده کردید آن را بازنشانی کنید.</translation>
<translation id="8866481888320382733">خطا در تجزیه تنظیمات خط‌مشی</translation>
<translation id="8870413625673593573">اخیراً بسته‌شده</translation>
<translation id="8874824191258364635">شماره کارت معتبری وارد کنید</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690">‏<ph name="SITE" /> معمولاً برای محافظت از اطلاعات شما از رمزگذاری استفاده می‌کند. اما این بار که Chromium تلاش کرد به <ph name="SITE" /> متصل شود، وب‌سایت اعتبارنامه‌ای نامعمول و نادرست را برگرداند. ممکن است مهاجمی در تلاش باشد خود را به‌جای <ph name="SITE" /> معرفی کند یا یک صفحه ورود به سیستم Wi-Fi در ارتباط اختلال ایجاد کرده باشد. اطلاعات شما همچنان ایمن است، زیرا Chromium قبل از هرگونه تبادل داده، اتصال را متوقف کرد.</translation>
<translation id="9106062320799175032">افزودن نشانی صورت‌حساب</translation>
<translation id="910908805481542201">برای برطرف کردن آن به من کمک کنید</translation>
+<translation id="9114524666733003316">درحال تأیید کردن کارت…</translation>
<translation id="9128870381267983090">اتصال به شبکه</translation>
<translation id="9137013805542155359">نمایش مورد اصلی</translation>
<translation id="9137248913990643158">‏لطفاً پیش از استفاده از این برنامه، Chrome را باز کنید و به سیستم آن وارد شوید.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">سایت‌ها بتوانند بررسی کنند روش پرداخت ذخیره‌شده‌ای دارید یا نه</translation>
<translation id="9169664750068251925">همیشه مسدود در این سایت</translation>
<translation id="9170848237812810038">&amp;واگرد</translation>
+<translation id="9171296965991013597">از برنامه خارج می‌شوید؟</translation>
<translation id="917450738466192189">گواهی سرور نامعتبر است.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> از یک پروتکل پشتیبانی‌نشده استفاده می‌کند.</translation>
<translation id="9205078245616868884">داده‌های شما با عبارت عبور همگام‌سازی رمزگذاری می‌شود. برای شروع همگام‌سازی آن را وارد کنید.</translation>
diff --git a/chromium/components/strings/components_strings_fi.xtb b/chromium/components/strings/components_strings_fi.xtb
index b743ae069ea..d366efa1db0 100644
--- a/chromium/components/strings/components_strings_fi.xtb
+++ b/chromium/components/strings/components_strings_fi.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Piilota arvo</translation>
<translation id="1228893227497259893">Väärä entiteettitunnus</translation>
<translation id="1232569758102978740">Nimetön</translation>
+<translation id="1250759482327835220">Jos haluat maksaa nopeammin ensi kerralla, tallenna kortti, nimi ja laskutusosoite Google-tilillesi.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (synkronoitu)</translation>
<translation id="1256368399071562588">&lt;p&gt;Jos verkkosivuston avaaminen ei onnistu, kokeile ensin seuraavia vianetsintäkeinoja:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Muokkaa aikaa ja päivämäärää &lt;strong&gt;Asetukset&lt;/strong&gt;‑sovelluksen &lt;strong&gt;Yleistä&lt;/strong&gt;‑osiossa.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Jokin meni vikaan tätä verkkosivua näytettäessä.</translation>
-<translation id="1590457302292452960">Luo vahva salasana…</translation>
<translation id="1592005682883173041">Tietojen paikallinen käyttö</translation>
<translation id="1594030484168838125">Valitse</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Ota yhteyttä järjestelmänvalvojaan.</translation>
<translation id="1740951997222943430">Anna kelvollinen viimeinen voimassaolokuukausi.</translation>
+<translation id="1743520634839655729">Jos haluat maksaa nopeammin ensi kerralla, tallenna kortti, nimi ja laskutusosoite Google-tilillesi ja tälle laitteelle.</translation>
<translation id="17513872634828108">Avoimet välilehdet</translation>
<translation id="1753706481035618306">Sivunumero</translation>
<translation id="1763864636252898013">Palvelin ei voinut todistaa olevansa <ph name="DOMAIN" />; laitteesi käyttöjärjestelmä ei luota sen suojausvarmenteeseen. Tämä voi johtua määritysvirheestä tai verkkoyhteytesi siepanneesta hyökkääjästä.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Avoimet välilehdet näkyvät tässä.</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Kortinhaltijan nimi</translation>
-<translation id="1806541873155184440">Lisätty: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Pyyntö on virheellinen tai pyynnön parametrit ovat virheelliset</translation>
<translation id="1826516787628120939">Tarkistetaan</translation>
<translation id="1834321415901700177">Tämä sivusto sisältää haitallisia ohjelmia</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 ehdotus}other{# ehdotusta}}</translation>
<translation id="2079545284768500474">Kumoa</translation>
<translation id="20817612488360358">Järjestelmän välityspalvelinasetukset on määritetty käytettäviksi, mutta erilliset välityspalvelimen asetukset on myös määritetty.</translation>
-<translation id="2084558088529668945">Kirjoitit salasanasi sivustolle, jota <ph name="ORG_NAME" /> ei hallinnoi. Älä käytä samaa salasanaa muissa sovelluksissa tai muilla sivustoilla tilisi turvallisuuden vuoksi.</translation>
<translation id="2091887806945687916">Ääni</translation>
<translation id="2094505752054353250">Verkkotunnukset eivät ole yhteensopivat</translation>
<translation id="2096368010154057602">Osasto</translation>
+<translation id="2102134110707549001">Ehdota vahvaa salasanaa…</translation>
<translation id="2108755909498034140">Käynnistä tietokone uudelleen.</translation>
<translation id="2113977810652731515">Kortti</translation>
<translation id="2114841414352855701">Ei käytetä – käytännön <ph name="POLICY_NAME" /> ohittama.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP-virhe</translation>
<translation id="2270484714375784793">Puhelinnumero</translation>
<translation id="2292556288342944218">Internetyhteytesi on estetty</translation>
-<translation id="230155334948463882">Uusi kortti?</translation>
<translation id="2316887270356262533">Vapauttaa alle 1 Mt. Jotkin sivustot saattavat latautua hitaammin seuraavalla käynnillä.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> pyytää käyttäjänimeä ja salasanaa.</translation>
<translation id="2317583587496011522">Maksukortit hyväksytään.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Hyväksytyt kortit</translation>
<translation id="2702801445560668637">Lukulista</translation>
<translation id="2704283930420550640">Arvo ei vastaa muotoa.</translation>
-<translation id="2704951214193499422">Chromium ei voinut vahvistaa korttiasi. Yritä myöhemmin uudelleen.</translation>
<translation id="2705137772291741111">Tämän sivuston välimuistiin tallennettu kopio oli lukukelvoton.</translation>
<translation id="2709516037105925701">Automaattinen täyttö</translation>
<translation id="2710942282213947212">Tietokoneelle asennettu ohjelmisto estää Chromiumia muodostamasta turvallista yhteyttä verkkoon</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Lähetystapa</translation>
<translation id="277499241957683684">Laitetallenne puuttuu</translation>
<translation id="2781030394888168909">Vie MacOS-muodossa</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Yhteys katkaistiin.</translation>
<translation id="2788784517760473862">Hyväksytyt luottokortit</translation>
<translation id="2794233252405721443">Sivusto estetty</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Voit poistaa yhteydelle määritetyt välityspalvelimet käytöstä asetussivulla.</translation>
<translation id="2955913368246107853">Sulje hakupalkki</translation>
<translation id="2958431318199492670">Verkkoasetukset eivät noudata ONC-standardia. Kaikkia asetuksia ei välttämättä tuoda.</translation>
-<translation id="2966678944701946121">Vanhenee: <ph name="EXPIRATION_DATE_ABBR" />, lisätty: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Kellosi täytyy asettaa oikeaan aikaan, jotta salattu yhteys voidaan muodostaa. Tämä johtuu siitä, että verkkosivustojen tunnistamisessa käytettävät varmenteet ovat voimassa vain tiettyinä aikoina. Chrome ei voi vahvistaa varmenteita, koska laitteesi kello on väärässä ajassa.</translation>
<translation id="2972581237482394796">&amp;Tee uudelleen</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, tällä hetkellä valittuna. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Väärä käytäntötyyppi</translation>
<translation id="3037605927509011580">Voi räkä!</translation>
<translation id="3041612393474885105">Varmenteen tiedot</translation>
-<translation id="3063697135517575841">Chrome ei voinut vahvistaa korttiasi. Yritä myöhemmin uudelleen.</translation>
<translation id="3064966200440839136">Incognito-tilasta poistutaan ulkoisessa sovelluksessa maksamisen vuoksi. Haluatko jatkaa?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Ei mitään}=1{1 salasana}other{# salasanaa}}</translation>
<translation id="3096100844101284527">Lisää noutopaikan osoite</translation>
@@ -342,7 +339,6 @@
<translation id="3305707030755673451">Tietosi salattiin synkronoinnin tunnuslauseesi avulla <ph name="TIME" />. Aloita synkronointi antamalla tunnuslause.</translation>
<translation id="3320021301628644560">Lisää laskutusosoite</translation>
<translation id="3338095232262050444">Turvallinen</translation>
-<translation id="3340978935015468852">asetuksissa</translation>
<translation id="3345135638360864351">Tämän sivuston käyttöpyyntöä ei voitu lähettää henkilölle <ph name="NAME" />. Yritä uudelleen.</translation>
<translation id="3355823806454867987">Muuta välityspalvelimen asetuksia...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />ei tallenna<ph name="END_EMPHASIS" />
@@ -376,7 +372,6 @@
<translation id="3528171143076753409">Palvelimen varmenne ei ole luotettava.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Vähintään 1 kohde synkronoiduilla laitteilla}=1{1 kohde (ja lisää synkronoiduilla laitteilla)}other{# kohdetta (ja lisää synkronoiduilla laitteilla)}}</translation>
<translation id="3539171420378717834">Säilytä tämän kortin kopio laitteella.</translation>
-<translation id="3542684924769048008">Käytä salasanaa kohteeseen:</translation>
<translation id="3549644494707163724">Salaa kaikki synkronoidut tiedot oman synkronoinnin tunnuslauseesi avulla</translation>
<translation id="3556433843310711081">Ylläpitäjä voi kumota eston puolestasi.</translation>
<translation id="3566021033012934673">Yhteytesi ei ole salattu</translation>
@@ -462,7 +457,6 @@
<translation id="4171400957073367226">Virheellinen vahvistusallekirjoitus.</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> toinen kohde}other{<ph name="ITEM_COUNT" /> muuta kohdetta}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome suosittelee palvelun <ph name="ORG_NAME" /> salasanan vaihtamista, jos olet käyttänyt sitä muilla sivustoilla.</translation>
<translation id="4196861286325780578">&amp;Toista siirto</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Tarkista palomuurin ja virustorjuntaohjelmiston määritykset.<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Kaatuu</translation>
@@ -491,7 +485,6 @@
<translation id="425582637250725228">Tekemiäsi muutoksia ei välttämättä tallenneta.</translation>
<translation id="4258748452823770588">Virheellinen allekirjoitus</translation>
<translation id="4265872034478892965">Järjestelmänvalvojan sallima</translation>
-<translation id="4269787794583293679">(Ei käyttäjänimeä)</translation>
<translation id="4275830172053184480">Käynnistä laite uudelleen</translation>
<translation id="4277028893293644418">Pyydä uusi salasana</translation>
<translation id="4280429058323657511">, vanhentumispäivä <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -514,6 +507,7 @@
<translation id="4434045419905280838">Ponnahdusikk. ja uudelleenohj.</translation>
<translation id="443673843213245140">Välityspalvelinta ei saa käyttää, mutta erilliset välityspalvelimen asetukset on määritetty.</translation>
<translation id="445100540951337728">Hyväksytyt maksukortit</translation>
+<translation id="4472575034687746823">Aloita</translation>
<translation id="4506176782989081258">Todennusvirhe: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Ota yhteyttä järjestelmänvalvojaan.</translation>
<translation id="450710068430902550">Jakaminen järjestelmänvalvojan kanssa</translation>
@@ -538,8 +532,8 @@
<translation id="4726672564094551039">Päivitä käytännöt</translation>
<translation id="4728558894243024398">Käyttöympäristö</translation>
<translation id="4736825316280949806">Käynnistä Chromium uudelleen.</translation>
+<translation id="4742407542027196863">Ylläpidä salasanoja…</translation>
<translation id="4744603770635761495">Suoritettavan tiedoston polku</translation>
-<translation id="4749685221585524849">Käytetty viimeksi: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Salasanat, luottokorttinumerot ja muut tietosi pysyvät yksityisinä, kun ne lähetetään tälle sivustolle.</translation>
<translation id="4756388243121344051">&amp;Historia</translation>
<translation id="4758311279753947758">Lisää yhteystiedot</translation>
@@ -556,6 +550,7 @@
<translation id="4850886885716139402">Näytä</translation>
<translation id="4854362297993841467">Tämä toimitustapa ei ole käytettävissä. Kokeile toista tapaa.</translation>
<translation id="4858792381671956233">Pyysit vanhemmiltasi lupaa käydä tällä sivustolla.</translation>
+<translation id="4876305945144899064">Ei käyttäjänimeä</translation>
<translation id="4880827082731008257">Haku historiasta</translation>
<translation id="4881695831933465202">Avaa</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -589,6 +584,7 @@
<translation id="5089810972385038852">Osavaltio/alue</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="5098332213681597508">Tämä on nimi Google-tililtäsi.</translation>
<translation id="5115563688576182185">(64-bittinen)</translation>
<translation id="5121084798328133320">Vahvistamisen jälkeen Google-maksutilisi korttitiedot jaetaan tämän sivuston kanssa.</translation>
<translation id="5128122789703661928">Tämännimistä käyttökertaa ei voi poistaa.</translation>
@@ -629,9 +625,10 @@
<translation id="5332219387342487447">Toimitustapa</translation>
<translation id="5355557959165512791"><ph name="SITE" /> ei juuri nyt ole käytettävissä, koska sen varmenne ei ole voimassa. Verkkovirheet ja hyökkäykset ovat yleensä väliaikaisia, joten sivu luultavasti toimii myöhemmin.</translation>
<translation id="536296301121032821">Käytännön asetuksien tallentaminen epäonnistui</translation>
+<translation id="5371425731340848620">Päivitä kortti</translation>
<translation id="5377026284221673050">"Kellosi jätättää", "Kellosi edistää" tai "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">Tämän sivuston varmenneketju sisältää varmenteen, joka on allekirjoitettu SHA-1:llä.</translation>
-<translation id="5402410679244714488">Vanhenee: <ph name="EXPIRATION_DATE_ABBR" />, käytetty viimeksi yli vuosi sitten</translation>
+<translation id="5387961145478138773">Pääset käyttämään Googlen sovelluksia nopeasti</translation>
<translation id="540969355065856584">Palvelin ei voinut todistaa olevansa <ph name="DOMAIN" />, sillä sen suojausvarmenne ei tällä hetkellä ole kelvollinen. Tämä voi johtua määritysvirheestä tai verkkoyhteytesi siepanneesta hyökkääjästä.</translation>
<translation id="5421136146218899937">Poista selaustiedot...</translation>
<translation id="5430298929874300616">Poista kirjanmerkki</translation>
@@ -673,11 +670,13 @@
<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="5659593005791499971">Sähköposti</translation>
+<translation id="5666899935841546222">Haluatko säilyttää kaikkia korttejasi yhdessä paikassa?</translation>
<translation id="5675650730144413517">Sivu ei toimi</translation>
<translation id="5685654322157854305">Lisää toimitusosoite</translation>
<translation id="5689199277474810259">Vie JSON-tiedostoon</translation>
<translation id="5689516760719285838">Sijainti</translation>
<translation id="570530837424789914">Hallinnoi…</translation>
+<translation id="57094364128775171">Ehdota vahvaa salasanaa…</translation>
<translation id="5710435578057952990">Tämän sivuston identiteettiä ei ole vahvistettu.</translation>
<translation id="5719499550583120431">Prepaid-kortit hyväksytään.</translation>
<translation id="5720705177508910913">Nykyinen käyttäjä</translation>
@@ -698,11 +697,9 @@
<translation id="5869405914158311789">Sivustoon ei saada yhteyttä</translation>
<translation id="5869522115854928033">Tallennetut salasanat</translation>
<translation id="5893752035575986141">Luottokortit hyväksytään.</translation>
-<translation id="5898382028489516745">Chromium suosittelee palvelun <ph name="ORG_NAME" /> salasanan vaihtamista, jos olet käyttänyt sitä muilla sivustoilla.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (synkronoitu)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 käytössä}other{# käytössä}}</translation>
<translation id="5939518447894949180">Tyhjennä</translation>
-<translation id="5959728338436674663"><ph name="BEGIN_WHITEPAPER_LINK" />Lähetä automaattisesti<ph name="END_WHITEPAPER_LINK" /> joitain järjestelmän tietoja ja sivujen sisältöjä Googlelle auttaaksesi sitä havaitsemaan vaarallisia sovelluksia ja sivustoja. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Muokkaa yhteystietoja</translation>
<translation id="5967867314010545767">Poista historiasta</translation>
<translation id="5975083100439434680">Loitonna</translation>
@@ -748,13 +745,11 @@
<translation id="6282194474023008486">Postinumero</translation>
<translation id="6290238015253830360">Suositellut artikkelit näkyvät tässä.</translation>
<translation id="6305205051461490394">Sivustoon <ph name="URL" /> ei saada yhteyttä.</translation>
-<translation id="6319915415804115995">Käytetty viimeksi yli vuosi sitten</translation>
<translation id="6321917430147971392">Tarkista DNS-asetukset</translation>
<translation id="6328639280570009161">Kokeile verkon ennakoinnin poistamista käytöstä.</translation>
<translation id="6328786501058569169">Tämä sivusto on petollinen</translation>
<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="6342069812937806050">Valmistui juuri</translation>
<translation id="6355080345576803305">Julkisen istunnon ohitus</translation>
<translation id="6358450015545214790">Mitä nämä tarkoittavat?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 muu ehdotus}other{# muuta ehdotusta}}</translation>
@@ -779,7 +774,6 @@
<translation id="6529602333819889595">&amp;Toista poisto</translation>
<translation id="6534179046333460208">Fyysisen webin ehdotukset</translation>
<translation id="6550675742724504774">Asetukset</translation>
-<translation id="6556915248009097796">Vanhenee: <ph name="EXPIRATION_DATE_ABBR" />, käytetty viimeksi: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Ylläpitäjä ei ole hyväksynyt sitä vielä.</translation>
<translation id="6569060085658103619">Tämä on laajennussivu.</translation>
<translation id="6596325263575161958">Salausasetukset</translation>
@@ -808,15 +802,20 @@
<translation id="6825578344716086703"><ph name="DOMAIN" />-palvelin, johon yritit muodostaa yhteyden, esitti heikkoa allekirjoitusalgoritmia käyttävän varmenteen (esim. SHA-1). Tästä syystä palvelimen esittämät tunnistetiedot saattavat olla väärennettyjä. Palvelin ei siis välttämättä ole tavoittelemasi palvelin, vaan saatat viestiä hakkerin kanssa.</translation>
<translation id="6831043979455480757">Käännä</translation>
<translation id="6839929833149231406">Alue</translation>
+<translation id="6852204201400771460">Ladataanko sovellus uudelleen?</translation>
+<translation id="6865412394715372076">Korttia ei voi vahvistaa juuri nyt.</translation>
<translation id="6874604403660855544">&amp;Toista lisäys</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Käytännön tasoa ei tueta.</translation>
<translation id="6895330447102777224">Korttisi vahvistettiin.</translation>
<translation id="6897140037006041989">User agent</translation>
+<translation id="6903319715792422884">Auta kehittämään selaussuojaa lähettämällä tiettyjä <ph name="BEGIN_WHITEPAPER_LINK" />järjestelmätietoa ja sivujen sisältöä<ph name="END_WHITEPAPER_LINK" /> Googlelle. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Käyttäjä:</translation>
+<translation id="6944692733090228304">Kirjoitit salasanan sivustolle, jota <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> ei ylläpidä. Älä käytä samaa salasanaa muissa sovelluksissa tai muilla sivustoilla tilisi turvallisuuden vuoksi.</translation>
<translation id="6945221475159498467">Valitse</translation>
<translation id="6948701128805548767">Valitse osoite, niin näet noutotavat ja vaatimukset.</translation>
<translation id="6949872517221025916">Pyydä uusi salasana</translation>
+<translation id="6950684638814147129">Virhe jäsennettäessä JSON-arvoa: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Palvelimen varmenne näyttää olevan väärennös.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Laite</translation>
@@ -878,6 +877,7 @@
&lt;li&gt;Valitse &lt;strong&gt;Käytä&lt;/strong&gt; ja sen jälkeen &lt;strong&gt;OK&lt;/strong&gt;.
&lt;li&gt;Saat ohjeita ohjelmiston pysyvään poistamiseen tietokoneelta &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome-ohjekeskuksesta&lt;/a&gt;.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Ylläpidä salasanoja…</translation>
<translation id="7419106976560586862">Profiilin polku</translation>
<translation id="7437289804838430631">Lisää yhteystieto</translation>
<translation id="7441627299479586546">Väärä käytännön aihe</translation>
@@ -886,7 +886,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />tiedonhakua<ph name="END_LINK" /> ongelmaan liittyen</translation>
<translation id="7455133967321480974">Käytä yleistä oletusasetusta (estä)</translation>
<translation id="7460163899615895653">Muiden laitteidesi viimeisimmät välilehdet näkyvät täällä.</translation>
-<translation id="7469372306589899959">Vahvistetaan korttia</translation>
<translation id="7473891865547856676">Ei kiitos</translation>
<translation id="7481312909269577407">Seuraava</translation>
<translation id="7485870689360869515">Tietoja ei löydy.</translation>
@@ -1016,6 +1015,7 @@
<translation id="8308427013383895095">Käännös epäonnistui, koska verkkoyhteydessä esiintyi ongelmia.</translation>
<translation id="8311129316111205805">Lataa istunto</translation>
<translation id="8332188693563227489">Sivuston <ph name="HOST_NAME" /> käyttöoikeus evättiin</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Jos ymmärrät käyntiä koskevat turvallisuusriskit, voit <ph name="BEGIN_LINK" />siirtyä tähän sivustoon<ph name="END_LINK" /> jo ennen haitallisten ohjelmien poistamista.</translation>
<translation id="8349305172487531364">Kirjanmerkkipalkki</translation>
<translation id="8363502534493474904">Poista lentokonetila käytöstä.</translation>
@@ -1036,21 +1036,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ei vastannut riittävän nopeasti.</translation>
<translation id="8503559462189395349">Chrome-salasanat</translation>
<translation id="8503813439785031346">Käyttäjätunnus</translation>
+<translation id="8508648098325802031">Hakukuvake</translation>
<translation id="8543181531796978784">Voit <ph name="BEGIN_ERROR_LINK" />ilmoittaa löytyneestä ongelmasta<ph name="END_ERROR_LINK" /> tai <ph name="BEGIN_LINK" />siirtyä mahdollisesti haitalliselle sivustolle<ph name="END_LINK" />, jos ymmärrät tietoturvariskit.</translation>
<translation id="8543556556237226809">Onko sinulla kysyttävää? Ota yhteyttä profiiliasi valvovaan henkilöön.</translation>
<translation id="8553075262323480129">Käännös epäonnistui, sillä sivun kieltä ei voitu määrittää.</translation>
<translation id="8557066899867184262">CVC sijaitsee kortin takapuolella.</translation>
<translation id="8559762987265718583">Verkkotunnukseen <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ei voi muodostaa salattua yhteyttä, koska laitteesi aika ja päivämäärä (<ph name="DATE_AND_TIME" />) ovat virheelliset.</translation>
+<translation id="8564985650692024650">Chromium suosittelee organisaation <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> salasanasi vaihtamista, jos olet käyttänyt sitä myös muilla sivustoilla.</translation>
<translation id="8571890674111243710">Käännetään sivua kielelle <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Lisää puh.nro
</translation>
<translation id="859285277496340001">Varmenne ei määritä mekanismia, jonka avulla voitaisiin tarkistaa, onko varmenne kumottu.</translation>
+<translation id="860043288473659153">Kortinhaltijan nimi</translation>
<translation id="8620436878122366504">Vanhempasi eivät ole hyväksyneet sitä vielä.</translation>
<translation id="8625384913736129811">Tallenna kortti tälle laitteelle</translation>
-<translation id="8639963783467694461">Automaattisen täytön asetukset</translation>
+<translation id="8663226718884576429">Tilauksen yhteenveto, <ph name="TOTAL_LABEL" />, lisätietoja</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, vastaus, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Yhteyttäsi verkkotunnukseen <ph name="DOMAIN" /> ei ole salattu.</translation>
<translation id="8718314106902482036">Maksua ei suoritettu loppuun</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, hakuehdotus</translation>
<translation id="8725066075913043281">Yritä uudelleen</translation>
<translation id="8728672262656704056">Olet muuttunut näkymättömäksi</translation>
<translation id="8730621377337864115">Valmis</translation>
@@ -1066,8 +1070,10 @@
<translation id="8820817407110198400">Kirjanmerkit</translation>
<translation id="883848425547221593">Muut kirjanmerkit</translation>
<translation id="884264119367021077">Toimitusosoite</translation>
+<translation id="8846319957959474018">Avaa sovelluksia kätevästi kirjanmerkkien avulla</translation>
<translation id="884923133447025588">Kumoamisjärjestelmää ei löytynyt.</translation>
<translation id="885730110891505394">Googlen kanssa jakaminen</translation>
+<translation id="8858065207712248076">Chrome suosittelee organisaation <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> salasanasi vaihtamista, jos olet käyttänyt sitä myös muilla sivustoilla.</translation>
<translation id="8866481888320382733">Virhe jäsennettäessä käytännön asetuksia</translation>
<translation id="8870413625673593573">Hiljattain suljetut</translation>
<translation id="8874824191258364635">Anna kelvollinen kortin numero.</translation>
@@ -1106,6 +1112,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> suojaa tietosi normaalisti salauksen avulla. Kun Chromium yritti tällä kertaa yhdistää sivustoon <ph name="SITE" />, sivusto palautti epätavalliset ja virheelliset kirjautumistiedot. Hyökkääjä saattaa yrittää esiintyä sivustona <ph name="SITE" />, tai Wi-Fi-kirjautumisruutu on keskeyttänyt yhteyden. Tietosi ovat edelleen turvassa, sillä Chromium katkaisi yhteyden, ennen kuin mitään tietoja vaihdettiin.</translation>
<translation id="9106062320799175032">Lisää laskutusosoite</translation>
<translation id="910908805481542201">Auta minua korjaamaan asia</translation>
+<translation id="9114524666733003316">Vahvistetaan korttia…</translation>
<translation id="9128870381267983090">Yhdistä verkkoon</translation>
<translation id="9137013805542155359">Näytä alkuperäinen</translation>
<translation id="9137248913990643158">Aloita ja kirjaudu sisään Chromeen ennen tämän sovelluksen käyttämistä.</translation>
@@ -1116,6 +1123,7 @@
<translation id="9168814207360376865">Salli sivustojen tarkastaa, oletko tallentanut maksutapoja</translation>
<translation id="9169664750068251925">Estä aina tämä sivusto</translation>
<translation id="9170848237812810038">K&amp;umoa</translation>
+<translation id="9171296965991013597">Poistutaanko sovelluksesta?</translation>
<translation id="917450738466192189">Palvelimen varmenne ei kelpaa.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> käyttää protokollaa, jota ei tueta.</translation>
<translation id="9205078245616868884">Tietosi on salattu synkronoinnin tunnuslauseesi avulla. Aloita synkronointi antamalla tunnuslause.</translation>
diff --git a/chromium/components/strings/components_strings_fil.xtb b/chromium/components/strings/components_strings_fil.xtb
index 60bc1e8d9ee..a1a752712fa 100644
--- a/chromium/components/strings/components_strings_fil.xtb
+++ b/chromium/components/strings/components_strings_fil.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Itago ang halaga</translation>
<translation id="1228893227497259893">Maling tagatukoy ng entity</translation>
<translation id="1232569758102978740">Walang pamagat</translation>
+<translation id="1250759482327835220">Para mas mabilis na makapagbayad sa susunod, i-save ang iyong card, pangalan, at billing address sa Google Account mo.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (naka-sync)</translation>
<translation id="1256368399071562588">&lt;p&gt;Kung sinubukan mong bisitahin ang isang website at hindi ito bumukas, subukan munang ayusin ang error gamit ang mga hakbang sa pag-troubleshoot na ito:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Mangyaring isaayos ang petsa at oras mula sa seksyong &lt;strong&gt;Pangkalahatan&lt;/strong&gt; ng app na &lt;strong&gt;Mga Setting&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Nagkaroon ng problema habang ipinapakita ang webpage na ito.</translation>
-<translation id="1590457302292452960">Gumawa ng malakas na password...</translation>
<translation id="1592005682883173041">Access sa Lokal na Data</translation>
<translation id="1594030484168838125">Pumili</translation>
<translation id="1620510694547887537">Camera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Subukang makipag-ugnayan sa admin ng system.</translation>
<translation id="1740951997222943430">Maglagay ng wastong buwan ng pag-expire</translation>
+<translation id="1743520634839655729">Para mas mabilis na makapagbayad sa susunod, i-save ang iyong card, pangalan, at billing address sa Google Account mo at sa device na ito.</translation>
<translation id="17513872634828108">Mga bukas na tab</translation>
<translation id="1753706481035618306">Numero ng page</translation>
<translation id="1763864636252898013">Hindi mapatunayan ng server na ito na ito ay <ph name="DOMAIN" />; hindi pinagkakatiwalaan ng operating system ng iyong device ang certificate ng seguridad nito. Maaaring dulot ito ng maling configuration o isang umaatake na hinahadlangan ang iyong koneksyon.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Lalabas dito ang iyong mga bukas na tab</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Pangalan ng Cardholder</translation>
-<translation id="1806541873155184440">Idinagdag noong <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Di-wastong kahilingan o mga parameter ng kahilingan</translation>
<translation id="1826516787628120939">Sinusuri</translation>
<translation id="1834321415901700177">Naglalaman ng mga mapanirang program ang site na ito</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 suhestyon}one{# suhestyon}other{# na suhestyon}}</translation>
<translation id="2079545284768500474">I-undo</translation>
<translation id="20817612488360358">Itinatakda ang mga setting ng proxy ng system upang magamit ngunit tinutukoy rin ang isang tahasang configuration ng proxy.</translation>
-<translation id="2084558088529668945">Inilagay mo sa site na hindi pinamamahalaan ng <ph name="ORG_NAME" /> ang iyong password. Para protektahan ang iyong account, huwag gamiting muli ang password mo sa iba pang app at site.</translation>
<translation id="2091887806945687916">Tunog</translation>
<translation id="2094505752054353250">Maling pagtutugma sa domain</translation>
<translation id="2096368010154057602">Departamento</translation>
+<translation id="2102134110707549001">Magmungkahi ng Malakas na Password...</translation>
<translation id="2108755909498034140">I-restart ang iyong computer</translation>
<translation id="2113977810652731515">Card</translation>
<translation id="2114841414352855701">Binalewala dahil na-override ito ng <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Error sa HTTP</translation>
<translation id="2270484714375784793">Numero ng telepono</translation>
<translation id="2292556288342944218">Naka-block ang iyong access sa Internet</translation>
-<translation id="230155334948463882">Bagong card?</translation>
<translation id="2316887270356262533">Magbabakante ng wala pang 1 MB. Maaaring mag-load nang mas mabagal ang ilang site sa iyong susunod na pagbisita.</translation>
<translation id="2317259163369394535">Kailangan ng <ph name="DOMAIN" /> ng username at password.</translation>
<translation id="2317583587496011522">Tinatanggap ang mga debit card.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Mga Tinatanggap na Card</translation>
<translation id="2702801445560668637">Listahan ng Babasahin</translation>
<translation id="2704283930420550640">Hindi tumutugma ang format sa halaga.</translation>
-<translation id="2704951214193499422">Hindi nakumpirma ng Chromium ang iyong card sa pagkakataong ito. Pakisubukang muli sa ibang pagkakataon.</translation>
<translation id="2705137772291741111">Hindi mabasa ang naka-save (naka-cache) na kopya ng site na ito.</translation>
<translation id="2709516037105925701">AutoFill</translation>
<translation id="2710942282213947212">Pinipigilan ng software sa iyong computer na makakonekta nang ligtas ang Chromium sa web</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Pamamaraan ng pagpapadala</translation>
<translation id="277499241957683684">Nawawalang tala ng device</translation>
<translation id="2781030394888168909">I-export para sa MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Na-reset ang koneksyon.</translation>
<translation id="2788784517760473862">Mga tinatanggap na credit card</translation>
<translation id="2794233252405721443">Naka-block ang site</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Maaari mong i-disable ang anumang mga proxy na naka-configure para sa isang koneksyon mula sa pahina ng mga setting.</translation>
<translation id="2955913368246107853">Isara ang bar sa paghahanap</translation>
<translation id="2958431318199492670">Hindi sumusunod ang configuration ng network sa pamantayan ng ONC. Hindi maaaring i-import ang mga bahagi ng configuration.</translation>
-<translation id="2966678944701946121">Exp: <ph name="EXPIRATION_DATE_ABBR" />, idinagdag noong <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Upang makapagtatag ng secure na koneksyon, kailangang itakda nang tama ang iyong orasan. Ito ay dahil sa may-bisa lang ang mga certificate na ginagamit ng mga website upang tukuyin ang mga sarili ng mga ito sa loob ng mga partikular na tagal ng panahon. Dahil mali ang orasan ng iyong device, hindi ma-verify ng Google Chrome ang mga certificate na ito.</translation>
<translation id="2972581237482394796">&amp;I-redo</translation>
<translation id="2977665033722899841">Kasalukuyang napili ang <ph name="ROW_NAME" />. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Maling uri ng patakaran</translation>
<translation id="3037605927509011580">Ay, Naku!</translation>
<translation id="3041612393474885105">Impormasyon sa Certificate</translation>
-<translation id="3063697135517575841">Hindi nakumpirma ng Chrome ang iyong card sa pagkakataong ito. Pakisubukang muli sa ibang pagkakataon.</translation>
<translation id="3064966200440839136">Aalis sa incognito mode upang magbayad sa pamamagitan ng external na application. Magpatuloy?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Wala}=1{1 password}one{# password}other{# na password}}</translation>
<translation id="3096100844101284527">Magdagdag ng Address sa Pag-pick up</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Na-encrypt ang iyong data gamit ang iyong passphrase sa pag-sync noong <ph name="TIME" />. Ilagay ito upang simulan ang pag-sync.</translation>
<translation id="3320021301628644560">Magdagdag ng billing address</translation>
<translation id="3338095232262050444">Secure</translation>
-<translation id="3340978935015468852">mga setting</translation>
<translation id="3345135638360864351">Hindi maipadala kay <ph name="NAME" /> ang iyong kahilingang i-access ang site na ito. Pakisubukang muli.</translation>
<translation id="3355823806454867987">Baguhin ang mga setting ng proxy...</translation>
<translation id="3361596688432910856"><ph name="BEGIN_EMPHASIS" />Hindi ise-save<ph name="END_EMPHASIS" /> ng Chrome ang sumusunod na impormasyon:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Hindi pinagkakatiwalaan ang certificate ng server.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Hindi bababa sa 1 item sa mga naka-sync na device}=1{1 item (at higit pa sa mga naka-sync na device)}one{# item (at higit pa sa mga naka-sync na device)}other{# na item (at higit pa sa mga naka-sync na device)}}</translation>
<translation id="3539171420378717834">Magtago ng kopya ng card na ito sa device na ito</translation>
-<translation id="3542684924769048008">Gamitin ang password para sa:</translation>
<translation id="3549644494707163724">I-encrypt ang lahat ng naka-sync na data gamit ang sarili mong passphrase sa pag-sync</translation>
<translation id="3556433843310711081">Maaari itong i-unblock ng iyong manager para sa iyo</translation>
<translation id="3566021033012934673">Hindi pribado ang iyong koneksyon</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Hindi wasto ang signature sa pag-verify</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> pang item}one{<ph name="ITEM_COUNT" /> pang item}other{<ph name="ITEM_COUNT" /> pang item}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Inirerekomenda ng Chrome na i-reset ang iyong password sa <ph name="ORG_NAME" /> kung ginamit mo ito sa iba pang site.</translation>
<translation id="4196861286325780578">&amp;Gawing muli ang paglilipat</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Suriin ang mga configuration ng firewall at antivirus<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Mga Pag-crash</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Maaaring hindi ma-save ang mga pagbabagong ginawa mo.</translation>
<translation id="4258748452823770588">Maling lagda</translation>
<translation id="4265872034478892965">Pinayagan ng iyong administrator</translation>
-<translation id="4269787794583293679">(Walang username)</translation>
<translation id="4275830172053184480">I-restart ang iyong device</translation>
<translation id="4277028893293644418">I-reset ang password</translation>
<translation id="4280429058323657511">, exp <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Mga pop-up at pag-redirect</translation>
<translation id="443673843213245140">Hindi pinagana ang paggamit ng isang proxy ngunit tinutukoy ang isang tahasang configuration ng proxy.</translation>
<translation id="445100540951337728">Mga tinatanggap na debit card</translation>
+<translation id="4472575034687746823">Magsimula</translation>
<translation id="4506176782989081258">Error sa pagpapatunay: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Makipag-ugnayan sa admin ng system</translation>
<translation id="450710068430902550">Pagbabahagi sa Administrator</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">I-reload ang mga patakaran</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4736825316280949806">I-restart ang Chromium</translation>
+<translation id="4742407542027196863">Pamahalaan ang mga password...</translation>
<translation id="4744603770635761495">Naipapatupad na Path</translation>
-<translation id="4749685221585524849">Huling ginamit noong <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Pribado ang iyong impormasyon (halimbawa, mga password o credit card number) kapag ipinadala ito sa site na ito.</translation>
<translation id="4756388243121344051">&amp;History</translation>
<translation id="4758311279753947758">Magdagdag ng impormasyon sa pakikipag-ugnayan</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">View</translation>
<translation id="4854362297993841467">Hindi available ang pamamaraan ng paghahatid na ito. Sumubok ng ibang pamamaraan.</translation>
<translation id="4858792381671956233">Tinanong mo sa iyong mga magulang kung maaari mong bisitahin ang site na ito</translation>
+<translation id="4876305945144899064">Walang username</translation>
<translation id="4880827082731008257">History ng paghahanap</translation>
<translation id="4881695831933465202">Buksan</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Estado</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">Probinsya</translation>
+<translation id="5098332213681597508">Nagmula ang pangalang ito sa iyong Google Account.</translation>
<translation id="5115563688576182185">(64-bit)</translation>
<translation id="5121084798328133320">Pagkatapos mong magkumpirma, ibabahagi sa site na ito ang mga detalye ng card mula sa iyong Google Payments account.</translation>
<translation id="5128122789703661928">Hindi valid ang session na may ganitong pangalan upang ma-delete.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Pamamaraan ng Pagpapadala</translation>
<translation id="5355557959165512791">Hindi mo maaaring bisitahin ang <ph name="SITE" /> sa ngayon dahil binawi na ang certificate nito. Karaniwang pansamantala lang ang mga error at pag-atake sa network, kaya malamang na gagana ang page na ito sa ibang pagkakataon.</translation>
<translation id="536296301121032821">Nabigo i-load ang mga setting ng patakaran sa store</translation>
+<translation id="5371425731340848620">I-update ang card</translation>
<translation id="5377026284221673050">"Nahuhuli ang iyong orasan" o "Nauuna ang iyong orasan" o "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">Naglalaman ang chain ng certificate para sa site na ito ng certificate na naka-sign gamit ang SHA-1.</translation>
-<translation id="5402410679244714488">Petsa ng pag-expire: <ph name="EXPIRATION_DATE_ABBR" />, huling ginamit mahigit isang taon na ang nakalipas</translation>
+<translation id="5387961145478138773">Makakuha ng mabilis na access sa iyong mga paboritong Google App</translation>
<translation id="540969355065856584">Hindi mapatunayan ng server na ito ay ang <ph name="DOMAIN" />; walang bisa ang certificate sa seguridad nito sa pagkakataong ito. Maaaring dahil ito sa isang maling pag-configure o may isang attacker na humahadlang sa iyong koneksyon.</translation>
<translation id="5421136146218899937">I-clear ang data sa pagba-browse...</translation>
<translation id="5430298929874300616">Alisin ang bookmark</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">Email</translation>
+<translation id="5666899935841546222">Gusto mo bang makita ang lahat ng iyong card sa iisang lugar?</translation>
<translation id="5675650730144413517">Hindi gumagana ang page na ito</translation>
<translation id="5685654322157854305">Magdagdag ng Address sa Pagpapadala</translation>
<translation id="5689199277474810259">I-export sa JSON</translation>
<translation id="5689516760719285838">Lokasyon</translation>
<translation id="570530837424789914">Pamahalaan...</translation>
+<translation id="57094364128775171">Magmungkahi ng malakas na password...</translation>
<translation id="5710435578057952990">Ang pagkilala ng website na ito ay hindi natukoy.</translation>
<translation id="5719499550583120431">Tinatanggap ang mga prepaid card.</translation>
<translation id="5720705177508910913">Kasalukuyang user</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Hindi makakonekta sa site na ito</translation>
<translation id="5869522115854928033">Mga naka-save na password</translation>
<translation id="5893752035575986141">Tinatanggap ang mga credit card.</translation>
-<translation id="5898382028489516745">Inirerekomenda ng Chromium na i-reset ang iyong password sa <ph name="ORG_NAME" /> kung ginamit mo ito sa iba pang site.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (naka-sync)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 ang ginagamit}one{# ang ginagamit}other{# ang ginagamit}}</translation>
<translation id="5939518447894949180">I-reset</translation>
-<translation id="5959728338436674663">Awtomatikong magpadala ng ilang <ph name="BEGIN_WHITEPAPER_LINK" />impormasyon sa system at content ng page<ph name="END_WHITEPAPER_LINK" /> sa Google upang makatulong na tumukoy ng mapapanganib na app at site. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">I-edit ang Impormasyon ng Contact</translation>
<translation id="5967867314010545767">Alisin sa history</translation>
<translation id="5975083100439434680">Mag-zoom out</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Postal code</translation>
<translation id="6290238015253830360">Lalabas dito ang iminungkahi mong artikulo</translation>
<translation id="6305205051461490394">Hindi makakonekta sa <ph name="URL" />.</translation>
-<translation id="6319915415804115995">Huling ginamit mahigit isang taon na ang nakalipas</translation>
<translation id="6321917430147971392">Suriin ang iyong mga setting ng DNS</translation>
<translation id="6328639280570009161">Subukang i-disable ang paghula ng network</translation>
<translation id="6328786501058569169">Mapanlinlang ang site na ito</translation>
<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="6342069812937806050">Ngayon lang</translation>
<translation id="6355080345576803305">Override sa pampublikong session</translation>
<translation id="6358450015545214790">Ano ang ibig sabihin ng mga ito?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 pang suhestyon}one{# pang suhestyon}other{# pang suhestyon}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Gawing Muli ang Pagtanggal</translation>
<translation id="6534179046333460208">Mga suhestyon sa Pisikal na Web</translation>
<translation id="6550675742724504774">Mga Pagpipilian</translation>
-<translation id="6556915248009097796">Exp: <ph name="EXPIRATION_DATE_ABBR" />, huling ginamit noong <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Hindi pa ito inaaprubahan ng iyong manager</translation>
<translation id="6569060085658103619">Isang page ng extension ang tinitingnan mo</translation>
<translation id="6596325263575161958">Mga pagpipilian sa pag-encrypt</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Tinangka mong abutin ang <ph name="DOMAIN" />, ngunit nagpakita ang server ng certificate na nilagdaan gamit ang isang mahinang signature algorithm (tulad ng SHA-1). Nangangahulugan ito na maaaring pineke ang mga panseguridad na kredensyal na ipinakita ng server, at ang server ay maaaring hindi ang server na inaasahan mo (maaaring nakikipag-ugnayan ka sa isang attacker).</translation>
<translation id="6831043979455480757">Isalin</translation>
<translation id="6839929833149231406">Lugar</translation>
+<translation id="6852204201400771460">I-reload ang app?</translation>
+<translation id="6865412394715372076">Hindi ma-verify ang card na ito sa ngayon.</translation>
<translation id="6874604403660855544">&amp;Gawing muli ang pagdagdag</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Hindi sinusuportahan ang antas ng patakaran.</translation>
<translation id="6895330447102777224">Nakumpirma na ang iyong card</translation>
<translation id="6897140037006041989">User Agent</translation>
+<translation id="6903319715792422884">Tumulong sa pagpapahusay ng Ligtas na Pag-browse sa pamamagitan ng pagpapadala sa Google ng ilang <ph name="BEGIN_WHITEPAPER_LINK" />impormasyon ng system at content ng page<ph name="END_WHITEPAPER_LINK" />. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">User:</translation>
+<translation id="6944692733090228304">Inilagay mo ang iyong password sa site na hindi pinapamahalaan ng <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Para protektahan ang iyong account, huwag gamiting muli ang password mo sa iba pang app at site.</translation>
<translation id="6945221475159498467">Pumili</translation>
<translation id="6948701128805548767">Upang makita ang mga pamamaraan at kinakailangan sa pag-pick up, pumili ng address</translation>
<translation id="6949872517221025916">I-reset ang Password</translation>
+<translation id="6950684638814147129">Error habang pina-parse ang JSON value: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Lumilitaw na isang pamamalsipika ang certificate ng server.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Device</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;I-click ang &lt;strong&gt;Apply&lt;/strong&gt;, pagkatapos ay i-click ang &lt;strong&gt;OK&lt;/strong&gt;
&lt;li&gt;Bisitahin ang &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;help center ng Chrome&lt;/a&gt; para matutunan kung paano permanenteng alisin ang software sa iyong computer
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Pamahalaan ang Mga Password...</translation>
<translation id="7419106976560586862">Path ng Profile</translation>
<translation id="7437289804838430631">Magdagdag ng Impormasyon ng Contact</translation>
<translation id="7441627299479586546">Maling paksa ng patakaran</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Matuto nang higit pa<ph name="END_LINK" /> tungkol sa problemang ito.</translation>
<translation id="7455133967321480974">Gamitin ang pangkalahatang default (I-block)</translation>
<translation id="7460163899615895653">Lumalabas dito ang mga kamakailan mong tab mula sa iba pang mga device</translation>
-<translation id="7469372306589899959">Kinukumpirma ang card</translation>
<translation id="7473891865547856676">Hindi, Salamat</translation>
<translation id="7481312909269577407">Sumulong</translation>
<translation id="7485870689360869515">Walang nahanap na data.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Nabigo ang translation dahil sa problema sa koneksyon sa network.</translation>
<translation id="8311129316111205805">I-load ang session</translation>
<translation id="8332188693563227489">Tinanggihan ang access sa <ph name="HOST_NAME" /></translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Kung nauunawaan mo ang mga peligro sa iyong seguridad, maaari mong <ph name="BEGIN_LINK" />bisitahin ang site na ito<ph name="END_LINK" /> bago maalis ang mga mapanirang program.</translation>
<translation id="8349305172487531364">Bookmarks bar</translation>
<translation id="8363502534493474904">I-off ang airplane mode</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222">Masyadong matagal bago nakatugon ang <ph name="HOST_NAME" />.</translation>
<translation id="8503559462189395349">Mga Password sa Chrome</translation>
<translation id="8503813439785031346">Username</translation>
+<translation id="8508648098325802031">Icon ng paghahanap</translation>
<translation id="8543181531796978784">Maaari kang <ph name="BEGIN_ERROR_LINK" />mag-ulat ng problema sa pagtukoy<ph name="END_ERROR_LINK" /> o, kung nauunawaan mo ang mga panganib sa iyong seguridad, <ph name="BEGIN_LINK" />bisitahin ang hindi ligtas na site na ito<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">May mga tanong? Makipag-ugnayan sa taong nangangasiwa sa iyong profile.</translation>
<translation id="8553075262323480129">Nabigo ang pag-translate dahil hindi matukoy ang wika ng pahina.</translation>
<translation id="8557066899867184262">Makikita ang CVC sa likod ng iyong card.</translation>
<translation id="8559762987265718583">Hindi makapagtatag ng pribadong koneksyon sa <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> dahil mali ang petsa at oras ng iyong device (<ph name="DATE_AND_TIME" />).</translation>
+<translation id="8564985650692024650">Inirerekomenda ng Chromium na i-reset ang iyong password sa <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> kung ginamit mo ito sa iba pang site.</translation>
<translation id="8571890674111243710">Tina-translate ang pahina sa <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Magdagdag ng numero ng telepono</translation>
<translation id="859285277496340001">Hindi tumutukoy ang certificate na ito ng mekanismo upang masuri kung nabawi ito.</translation>
+<translation id="860043288473659153">Pangalan ng cardholder</translation>
<translation id="8620436878122366504">Hindi pa ito inaaprubahan ng iyong mga magulang</translation>
<translation id="8625384913736129811">I-save ang Card na Ito sa Device na Ito</translation>
-<translation id="8639963783467694461">Mga setting ng autofill</translation>
+<translation id="8663226718884576429">Buod ng Order, <ph name="TOTAL_LABEL" />, Higit pang Detalye</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, sagot, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Ang iyong koneksyon sa <ph name="DOMAIN" /> ay hindi naka-encrypt.</translation>
<translation id="8718314106902482036">Hindi nakumpleto ang pagbabayad</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, suhestyon sa paghahanap</translation>
<translation id="8725066075913043281">Muling subukan</translation>
<translation id="8728672262656704056">Naging incognito ka</translation>
<translation id="8730621377337864115">Tapos na</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Mga Bookmark</translation>
<translation id="883848425547221593">Iba Pang Mga Bookmark</translation>
<translation id="884264119367021077">Shipping address</translation>
+<translation id="8846319957959474018">Buksan ang mga app sa madaling paraan gamit ang mga bookmark</translation>
<translation id="884923133447025588">Walang nahanap na mekanismo ng pagpapawalang-bisa.</translation>
<translation id="885730110891505394">Pagbabahagi sa Google</translation>
+<translation id="8858065207712248076">Inirerekomenda ng Chrome na i-reset ang iyong password sa <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> kung ginamit mo ito sa iba pang site.</translation>
<translation id="8866481888320382733">Error sa pag-parse ng mga setting ng patakaran</translation>
<translation id="8870413625673593573">Recently Closed</translation>
<translation id="8874824191258364635">Maglagay ng wastong numero ng card</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690">Karaniwang gumagamit ang <ph name="SITE" /> ng pag-encrypt upang protektahan ang iyong impormasyon. Noong sinubukang kumonekta ng Chromium sa <ph name="SITE" /> sa pagkakataong ito, nagbalik ang website ng mga hindi pangkaraniwan at maling kredensyal. Maaari itong mangyari kapag sinusubukan ng isang attacker na magpanggap bilang <ph name="SITE" />, o naputol ang koneksyon dahil sa isang screen ng pag-sign in sa Wi-Fi. Secure pa rin ang iyong impormasyon dahil inihinto ng Chromium ang koneksyon bago magkaroon ng palitan ng anumang data.</translation>
<translation id="9106062320799175032">Magdagdag ng Billing Address</translation>
<translation id="910908805481542201">Tulungan akong ayusin ito</translation>
+<translation id="9114524666733003316">Kinukumpirma ang card...</translation>
<translation id="9128870381267983090">Kumonekta sa network</translation>
<translation id="9137013805542155359">Ipakita ang orihinal</translation>
<translation id="9137248913990643158">Magsimula at mag-sign in sa Chrome bago gamitin ang app na ito.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">Payagan ang mga site na tingnan kung may mga naka-save kang paraan ng pagbabayad</translation>
<translation id="9169664750068251925">Palaging i-block sa site na ito</translation>
<translation id="9170848237812810038">&amp;I-undo</translation>
+<translation id="9171296965991013597">Umalis sa app?</translation>
<translation id="917450738466192189">Di-wastong certificate ng server.</translation>
<translation id="9183425211371246419">Gumagamit ng hindi sinusuportahang protocol ang <ph name="HOST_NAME" />.</translation>
<translation id="9205078245616868884">Na-encrypt ang iyong data gamit ang iyong passphrase sa pag-sync. Ilagay ito upang simulan ang pag-sync.</translation>
diff --git a/chromium/components/strings/components_strings_fr.xtb b/chromium/components/strings/components_strings_fr.xtb
index 3c8a644f7f7..16ed537a5cb 100644
--- a/chromium/components/strings/components_strings_fr.xtb
+++ b/chromium/components/strings/components_strings_fr.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Masquer la valeur</translation>
<translation id="1228893227497259893">Identifiant d'entité incorrect.</translation>
<translation id="1232569758102978740">Sans titre</translation>
+<translation id="1250759482327835220">Pour régler vos achats plus rapidement la prochaine fois, enregistrez votre carte, votre nom et votre adresse de facturation dans votre compte Google.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (synchronisés)</translation>
<translation id="1256368399071562588">&lt;p&gt;Si vous ne parvenez pas à ouvrir un site Web dans Chrome, essayez d'abord de corriger le problème en suivant ces étapes :&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Veuillez ajuster la date et l'heure dans la section &lt;strong&gt;Général&lt;/strong&gt; de l'application &lt;strong&gt;Réglages&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Une erreur s'est produite lors de l'affichage de la page Web.</translation>
-<translation id="1590457302292452960">Générer un mot de passe sécurisé…</translation>
<translation id="1592005682883173041">Accès aux données locales</translation>
<translation id="1594030484168838125">Sélectionner</translation>
<translation id="1620510694547887537">Appareil photo</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">American Express</translation>
<translation id="1734878702283171397">Essayez de contacter l'administrateur système.</translation>
<translation id="1740951997222943430">Saisissez un mois d'expiration valide</translation>
+<translation id="1743520634839655729">Pour régler plus rapidement vos achats la prochaine fois, enregistrez votre carte, votre nom et votre adresse de facturation dans votre compte Google et sur cet appareil.</translation>
<translation id="17513872634828108">Onglets ouverts</translation>
<translation id="1753706481035618306">Numéro de page</translation>
<translation id="1763864636252898013">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 le système d'exploitation de votre appareil. Cela peut être dû à une mauvaise configuration ou bien à l'interception de votre connexion par un pirate informatique.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Les onglets ouverts s'affichent ici</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Nom du titulaire de la carte</translation>
-<translation id="1806541873155184440">Date d'ajout : <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">La demande ou ses paramètres ne sont pas valides.</translation>
<translation id="1826516787628120939">Vérification en cours…</translation>
<translation id="1834321415901700177">Ce site contient des programmes dangereux</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 suggestion}one{# suggestion}other{# suggestions}}</translation>
<translation id="2079545284768500474">Annuler</translation>
<translation id="20817612488360358">Les paramètres de proxy du système sont configurés pour être utilisés, mais une configuration de proxy explicite est également spécifiée.</translation>
-<translation id="2084558088529668945">Vous avez saisi votre mot de passe sur un site qui n'est pas géré par <ph name="ORG_NAME" />. Pour protéger votre compte, ne réutilisez pas ce mot de passe dans d'autres applications ni sur d'autres sites.</translation>
<translation id="2091887806945687916">Son</translation>
<translation id="2094505752054353250">Le domaine ne correspond pas</translation>
<translation id="2096368010154057602">Département</translation>
+<translation id="2102134110707549001">Suggérer un mot de passe sécurisé…</translation>
<translation id="2108755909498034140">Redémarrez l'ordinateur</translation>
<translation id="2113977810652731515">Carte</translation>
<translation id="2114841414352855701">Ignorée parce que remplacée par <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Erreur HTTP.</translation>
<translation id="2270484714375784793">N° de téléphone</translation>
<translation id="2292556288342944218">Votre accès à Internet est bloqué</translation>
-<translation id="230155334948463882">Nouvelle carte ?</translation>
<translation id="2316887270356262533">Libère moins de 1 Mo. Le chargement de certains sites risque d'être plus lent lors de votre prochaine visite.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> nécessite un nom d'utilisateur et un mot de passe.</translation>
<translation id="2317583587496011522">Les cartes de débit sont acceptées.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Cartes acceptées</translation>
<translation id="2702801445560668637">Liste de lecture</translation>
<translation id="2704283930420550640">La valeur ne respecte pas le format requis.</translation>
-<translation id="2704951214193499422">Impossible de valider votre carte dans Chromium pour le moment. Veuillez réessayer plus tard.</translation>
<translation id="2705137772291741111">La copie enregistrée (en cache) de ce site est illisible.</translation>
<translation id="2709516037105925701">Saisie automatique</translation>
<translation id="2710942282213947212">Un logiciel installé sur votre ordinateur empêche Chromium de se connecter au Web de manière sécurisée</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Mode d'expédition</translation>
<translation id="277499241957683684">Enregistrement de l'appareil manquant.</translation>
<translation id="2781030394888168909">Exporter pour MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">La connexion a été réinitialisée.</translation>
<translation id="2788784517760473862">Cartes de crédit acceptées</translation>
<translation id="2794233252405721443">Site bloqué</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Vous pouvez désactiver tout proxy configuré pour une connexion à partir de la page des paramètres.</translation>
<translation id="2955913368246107853">Fermer la barre de recherche</translation>
<translation id="2958431318199492670">La configuration du réseau ne respecte pas les normes de l'ONC. Il est possible que des parties de la configuration ne soient pas importées.</translation>
-<translation id="2966678944701946121">Date d'expiration : <ph name="EXPIRATION_DATE_ABBR" />, date d'ajout : <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Afin d'établir une connexion sécurisée, votre horloge doit être réglée correctement. Les certificats permettant aux sites Web de s'identifier sont en effet valides pendant une période précise. Comme l'horloge de votre appareil est incorrecte, Google Chrome n'est pas en mesure de vérifier la validité des certificats.</translation>
<translation id="2972581237482394796">&amp;Rétablir</translation>
<translation id="2977665033722899841">La ligne "<ph name="ROW_NAME" />" est sélectionnée. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Type de règle incorrect.</translation>
<translation id="3037605927509011580">Aie aie aie</translation>
<translation id="3041612393474885105">Informations relatives au certificat</translation>
-<translation id="3063697135517575841">Impossible de valider votre carte dans Chrome pour le moment. Veuillez réessayer plus tard.</translation>
<translation id="3064966200440839136">En payant via une application externe, vous allez quitter le mode navigation privée. Voulez-vous continuer ?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Aucun}=1{1 mot de passe}one{# mot de passe}other{# mots de passe}}</translation>
<translation id="3096100844101284527">Ajouter une adresse d'enlèvement</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Vos données ont été chiffrées avec votre phrase secrète de synchronisation le <ph name="TIME" />. Saisissez-la pour lancer la synchronisation.</translation>
<translation id="3320021301628644560">Ajouter une adresse de facturation</translation>
<translation id="3338095232262050444">Sécurisé</translation>
-<translation id="3340978935015468852">paramètres</translation>
<translation id="3345135638360864351">Impossible d'envoyer votre demande d'accès au site à <ph name="NAME" />. Veuillez réessayer.</translation>
<translation id="3355823806454867987">Modifier les paramètres du proxy...</translation>
<translation id="3361596688432910856">Les informations suivantes <ph name="BEGIN_EMPHASIS" />ne seront pas enregistrées<ph name="END_EMPHASIS" /> dans Chrome :
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Le certificat du serveur n'est pas approuvé.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Au moins 1 élément sur les appareils synchronisés}=1{1 élément (et plus sur les appareils synchronisés)}one{# élément (et plus sur les appareils synchronisés)}other{# éléments (et plus sur les appareils synchronisés)}}</translation>
<translation id="3539171420378717834">Conserver une copie de cette carte sur cet appareil</translation>
-<translation id="3542684924769048008">Utiliser un mot de passe pour :</translation>
<translation id="3549644494707163724">Chiffrer toutes les données synchronisées avec votre propre phrase secrète de synchronisation</translation>
<translation id="3556433843310711081">Votre responsable peut vous le débloquer</translation>
<translation id="3566021033012934673">Votre connexion n'est pas privée</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Signature de validation non valide.</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> élément supplémentaire}one{<ph name="ITEM_COUNT" /> élément supplémentaire}other{<ph name="ITEM_COUNT" /> éléments supplémentaires}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">L'équipe Chrome vous recommande de réinitialiser votre mot de passe <ph name="ORG_NAME" /> si vous l'avez réutilisé sur d'autres sites.</translation>
<translation id="4196861286325780578">&amp;Rétablir le déplacement</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Vérifier les configurations du pare-feu et de l'antivirus<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Plantages</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Les modifications que vous avez apportées ne seront peut-être pas enregistrées.</translation>
<translation id="4258748452823770588">Signature incorrecte.</translation>
<translation id="4265872034478892965">Autorisé par votre administrateur</translation>
-<translation id="4269787794583293679">(aucun nom d'utilisateur)</translation>
<translation id="4275830172053184480">Redémarrer l'appareil</translation>
<translation id="4277028893293644418">Réinitialiser le mot de passe</translation>
<translation id="4280429058323657511">, expiration le <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Pop-ups et redirections</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="445100540951337728">Cartes de débit acceptées</translation>
+<translation id="4472575034687746823">Premiers pas</translation>
<translation id="4506176782989081258">Erreur de validation : <ph name="VALIDATION_ERROR" />.</translation>
<translation id="4506599922270137252">Contacter l'administrateur système</translation>
<translation id="450710068430902550">Partage avec l'administrateur</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Actualiser les règles</translation>
<translation id="4728558894243024398">Plate-forme</translation>
<translation id="4736825316280949806">Relancez Chromium</translation>
+<translation id="4742407542027196863">Gérer les mots de passe…</translation>
<translation id="4744603770635761495">Chemin d'accès exécutable</translation>
-<translation id="4749685221585524849">Dernière utilisation : <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Vos informations, par exemple vos mots de passe ou vos numéros de carte de paiement, sont privées lorsqu'elles sont transmises à ce site.</translation>
<translation id="4756388243121344051">&amp;Historique</translation>
<translation id="4758311279753947758">Ajouter des coordonnées</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Afficher</translation>
<translation id="4854362297993841467">Mode de livraison non disponible. Choisissez-en un autre.</translation>
<translation id="4858792381671956233">Une demande d'autorisation a été envoyée à tes parents pour la consultation de ce site</translation>
+<translation id="4876305945144899064">Aucun nom d'utilisateur</translation>
<translation id="4880827082731008257">Rechercher dans l'historique</translation>
<translation id="4881695831933465202">Ouvrir</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">État</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="5098332213681597508">Ce nom provient de votre compte Google.</translation>
<translation id="5115563688576182185">(64 bits)</translation>
<translation id="5121084798328133320">Une fois la validation terminée, les informations relatives à la carte de votre compte Google Payments seront partagées avec ce site.</translation>
<translation id="5128122789703661928">Impossible de supprimer la session qui porte ce nom.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Mode de livraison</translation>
<translation id="5355557959165512791">Le site <ph name="SITE" /> est actuellement inaccessible, car son certificat a été révoqué. Les erreurs réseau et les attaques sont généralement temporaires. Vous devriez donc pouvoir accéder à cette page plus tard.</translation>
<translation id="536296301121032821">Échec du stockage des paramètres de la règle.</translation>
+<translation id="5371425731340848620">Mettre à jour la carte</translation>
<translation id="5377026284221673050">"Votre horloge est en retard.", "Votre horloge est en avance." ou "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">La chaîne de certificats de ce site contient un certificat signé avec SHA-1.</translation>
-<translation id="5402410679244714488">Date d'expiration : <ph name="EXPIRATION_DATE_ABBR" />, dernière utilisation il y a plus d'un an</translation>
+<translation id="5387961145478138773">Accédez rapidement à vos applications Google préférées</translation>
<translation id="540969355065856584">Impossible de vérifier que ce serveur est bien <ph name="DOMAIN" />, car son certificat de sécurité actuel n'est pas valide. Cela peut être dû à une mauvaise configuration ou bien à l'interception de votre connexion par un pirate informatique.</translation>
<translation id="5421136146218899937">Effacer les données de navigation...</translation>
<translation id="5430298929874300616">Supprimer le favori</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">E-mail</translation>
+<translation id="5666899935841546222">Voulez-vous enregistrer toutes vos cartes au même endroit ?</translation>
<translation id="5675650730144413517">Cette page ne fonctionne pas</translation>
<translation id="5685654322157854305">Ajouter une adresse de livraison</translation>
<translation id="5689199277474810259">Exporter au format JSON</translation>
<translation id="5689516760719285838">Position</translation>
<translation id="570530837424789914">Gérer…</translation>
+<translation id="57094364128775171">Suggérer un mot de passe sécurisé…</translation>
<translation id="5710435578057952990">L'identité de ce site Web n'a pas été vérifiée.</translation>
<translation id="5719499550583120431">Les cartes prépayées sont acceptées.</translation>
<translation id="5720705177508910913">Utilisateur actuel</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Ce site est inaccessible</translation>
<translation id="5869522115854928033">Mots de passe enregistrés</translation>
<translation id="5893752035575986141">Les cartes de crédit sont acceptées.</translation>
-<translation id="5898382028489516745">L'équipe Chromium vous recommande de réinitialiser votre mot de passe <ph name="ORG_NAME" /> si vous l'avez réutilisé sur d'autres sites.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (synchronisés)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 en cours d'utilisation}one{# en cours d'utilisation}other{# en cours d'utilisation}}</translation>
<translation id="5939518447894949180">Réinitialiser</translation>
-<translation id="5959728338436674663">Envoyer automatiquement <ph name="BEGIN_WHITEPAPER_LINK" />des informations système et du contenu de page<ph name="END_WHITEPAPER_LINK" /> à Google afin de faciliter la détection d'applications et de sites dangereux. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Modifier les coordonnées</translation>
<translation id="5967867314010545767">Supprimer de l'historique</translation>
<translation id="5975083100439434680">Zoom arrière</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Code postal</translation>
<translation id="6290238015253830360">Vos articles suggérés s'affichent ici</translation>
<translation id="6305205051461490394"><ph name="URL" /> est inaccessible.</translation>
-<translation id="6319915415804115995">Dernière utilisation il y a plus d'un an</translation>
<translation id="6321917430147971392">Vérifiez vos paramètres DNS</translation>
<translation id="6328639280570009161">Essayez de désactiver la prédiction réseau</translation>
<translation id="6328786501058569169">Ce site est trompeur</translation>
<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="6342069812937806050">À l'instant</translation>
<translation id="6355080345576803305">Contournement dû à la session publique</translation>
<translation id="6358450015545214790">Qu'est-ce que c'est ?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 autre suggestion}one{# autre suggestion}other{# autres suggestions}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Rétablir la suppression</translation>
<translation id="6534179046333460208">Suggestions pour le Web physique</translation>
<translation id="6550675742724504774">Options</translation>
-<translation id="6556915248009097796">Date d'expiration : <ph name="EXPIRATION_DATE_ABBR" />, dernière utilisation : <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Votre responsable ne l'a pas encore autorisé</translation>
<translation id="6569060085658103619">Vous consultez actuellement une page d'extension</translation>
<translation id="6596325263575161958">Options de chiffrement</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Vous avez tenté d'accéder à <ph name="DOMAIN" />, mais le serveur a présenté un certificat signé à l'aide d'un algorithme de signature faible (par exemple, SHA-1). Il est possible que le certificat fourni par le serveur ait été falsifié. Il se peut donc que le serveur ne soit pas celui auquel vous souhaitez accéder, et qu'il s'agisse d'une tentative de piratage.</translation>
<translation id="6831043979455480757">Traduire</translation>
<translation id="6839929833149231406">Région</translation>
+<translation id="6852204201400771460">Actualiser l'application ?</translation>
+<translation id="6865412394715372076">Impossible de valider cette carte pour le moment</translation>
<translation id="6874604403660855544">&amp;Rétablir l'ajout</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Le niveau de la règle n'est pas accepté.</translation>
<translation id="6895330447102777224">Carte validée</translation>
<translation id="6897140037006041989">Agent utilisateur</translation>
+<translation id="6903319715792422884">Aidez-nous à améliorer la navigation sécurisée en nous envoyant <ph name="BEGIN_WHITEPAPER_LINK" />des informations système et du contenu de pages<ph name="END_WHITEPAPER_LINK" />. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Utilisateur :</translation>
+<translation id="6944692733090228304">Vous avez saisi votre mot de passe sur un site qui n'est pas géré par <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Pour protéger votre compte, ne réutilisez pas ce mot de passe dans d'autres applications ni sur d'autres sites.</translation>
<translation id="6945221475159498467">Sélectionner</translation>
<translation id="6948701128805548767">Sélectionnez une adresse pour consulter les modes et conditions d'enlèvement disponibles</translation>
<translation id="6949872517221025916">Réinitialiser le mot de passe</translation>
+<translation id="6950684638814147129">Erreur lors de l'analyse de la valeur JSON : <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Le certificat du serveur semble être contrefait.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Périphérique</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Cliquez sur &lt;strong&gt;Appliquer&lt;/strong&gt;, puis sur &lt;strong&gt;OK&lt;/strong&gt;
&lt;li&gt;Pour savoir comment supprimer définitivement le logiciel de votre ordinateur, consultez le &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Centre d'aide Chrome&lt;/a&gt;
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Gérer les mots de passe…</translation>
<translation id="7419106976560586862">Chemin d'accès au profil</translation>
<translation id="7437289804838430631">Ajouter des coordonnées</translation>
<translation id="7441627299479586546">Objet de la règle incorrect.</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />En savoir plus<ph name="END_LINK" /> sur ce problème.</translation>
<translation id="7455133967321480974">Utiliser le paramètre global par défaut ("Bloquer")</translation>
<translation id="7460163899615895653">Les onglets que vous avez utilisés récemment sur d'autres appareils s'affichent ici</translation>
-<translation id="7469372306589899959">Validation de la carte…</translation>
<translation id="7473891865547856676">Non, merci</translation>
<translation id="7481312909269577407">Avancer</translation>
<translation id="7485870689360869515">Aucune donnée n'a été trouvée.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Échec de la traduction en raison d'un problème de connexion réseau</translation>
<translation id="8311129316111205805">Charger la session</translation>
<translation id="8332188693563227489">L'accès à <ph name="HOST_NAME" /> a été refusé</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Si vous êtes conscient des risques auxquels vous vous exposez, vous pouvez <ph name="BEGIN_LINK" />consulter ce site<ph name="END_LINK" /> avant que les programmes dangereux aient été supprimés.</translation>
<translation id="8349305172487531364">Barre de favoris</translation>
<translation id="8363502534493474904">Désactiver le mode Avion</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> a mis trop de temps à répondre.</translation>
<translation id="8503559462189395349">Mots de passe Google Chrome</translation>
<translation id="8503813439785031346">Nom d'utilisateur</translation>
+<translation id="8508648098325802031">Icône Recherche</translation>
<translation id="8543181531796978784">Vous pouvez <ph name="BEGIN_ERROR_LINK" />signaler un problème de détection<ph name="END_ERROR_LINK" />. Si vous avez compris les risques auxquels vous vous exposez, vous pouvez <ph name="BEGIN_LINK" />consulter ce site dangereux<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Des questions ? Contactez la personne qui supervise votre profil.</translation>
<translation id="8553075262323480129">La traduction a échoué, car nous n'avons pas pu déterminer la langue de la page.</translation>
<translation id="8557066899867184262">Le code CVC figure au recto de votre carte.</translation>
<translation id="8559762987265718583">Impossible d'établir une connexion privée à <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> : la date et l'heure de votre appareil (<ph name="DATE_AND_TIME" />) sont incorrectes.</translation>
+<translation id="8564985650692024650">L'équipe Chromium vous recommande de réinitialiser votre mot de passe <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> si vous l'avez réutilisé sur d'autres sites.</translation>
<translation id="8571890674111243710">Traduction de la page en <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Saisir num tél</translation>
<translation id="859285277496340001">Le certificat n'indique aucun mécanisme permettant de vérifier s'il a été révoqué.</translation>
+<translation id="860043288473659153">Nom du titulaire de la carte</translation>
<translation id="8620436878122366504">Tes parents ne l'ont pas encore autorisé</translation>
<translation id="8625384913736129811">Enregistrer cette carte sur cet appareil</translation>
-<translation id="8639963783467694461">Paramètres de saisie automatique</translation>
+<translation id="8663226718884576429">Récapitulatif de la commande, <ph name="TOTAL_LABEL" />, détails supplémentaires</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, réponse, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Votre connexion à <ph name="DOMAIN" /> n'est pas chiffrée.</translation>
<translation id="8718314106902482036">Paiement non finalisé</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, suggestion de recherche</translation>
<translation id="8725066075913043281">Réessayer</translation>
<translation id="8728672262656704056">Vous êtes passé en mode navigation privée</translation>
<translation id="8730621377337864115">OK</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Favoris</translation>
<translation id="883848425547221593">Autres favoris</translation>
<translation id="884264119367021077">Adresse de livraison</translation>
+<translation id="8846319957959474018">Ouvrir les applications facilement grâce aux favoris</translation>
<translation id="884923133447025588">Aucun système de révocation trouvé</translation>
<translation id="885730110891505394">Partage avec Google</translation>
+<translation id="8858065207712248076">L'équipe Chrome vous recommande de réinitialiser votre mot de passe <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> si vous l'avez réutilisé sur d'autres sites.</translation>
<translation id="8866481888320382733">Erreur d'analyse des paramètres de la règle.</translation>
<translation id="8870413625673593573">Récemment fermés</translation>
<translation id="8874824191258364635">Saisissez un numéro de carte valide</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690">Un chiffrement est normalement utilisé sur le site <ph name="SITE" /> pour protéger vos informations. Lors de la dernière tentative de connexion de Chromium au site <ph name="SITE" />, des identifiants inhabituels et incorrects ont été retournés. Il est possible qu'un individu malveillant tente de se faire passer pour <ph name="SITE" /> ou qu'un écran de connexion Wi-Fi ait interrompu la connexion. Vos informations restent sécurisées, car nous avons arrêté la connexion avant l'échange des données.</translation>
<translation id="9106062320799175032">Ajouter une adresse de facturation</translation>
<translation id="910908805481542201">M'aider à régler le problème</translation>
+<translation id="9114524666733003316">Validation de la carte…</translation>
<translation id="9128870381267983090">Se connecter au réseau</translation>
<translation id="9137013805542155359">Afficher l'original</translation>
<translation id="9137248913990643158">Veuillez démarrer Chrome et vous connecter à votre compte avant d'utiliser cette application</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">Autoriser les sites à vérifier si vous avez enregistré des modes de paiement</translation>
<translation id="9169664750068251925">Toujours bloquer sur ce site</translation>
<translation id="9170848237812810038">Ann&amp;uler</translation>
+<translation id="9171296965991013597">Quitter l'application ?</translation>
<translation id="917450738466192189">Le certificat du serveur n'est pas valide.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> utilise un protocole incompatible.</translation>
<translation id="9205078245616868884">Vos données sont chiffrées avec votre phrase secrète de synchronisation. Saisissez-la pour lancer la synchronisation.</translation>
diff --git a/chromium/components/strings/components_strings_gu.xtb b/chromium/components/strings/components_strings_gu.xtb
index 982957da719..758df4d69f7 100644
--- a/chromium/components/strings/components_strings_gu.xtb
+++ b/chromium/components/strings/components_strings_gu.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">મૂલ્ય છુપાવો</translation>
<translation id="1228893227497259893">ખોટો અસ્તિત્વ ઓળખકર્તા</translation>
<translation id="1232569758102978740">શીર્ષક વિનાનું</translation>
+<translation id="1250759482327835220">આગલી વખતે વધુ ઝડપથી ચુકવણી કરવા માટે, તમારા કાર્ડ, નામ અને બિલિંગ સરનામાને તમારા Google એકાઉન્ટમાં સાચવો.</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;
@@ -97,7 +98,6 @@
&lt;p&gt;કૃપા કરીને &lt;strong&gt;સેટિંગ્સ&lt;/strong&gt; અ‍ૅપ્લિકેશનના &lt;strong&gt;સામાન્ય&lt;/strong&gt; વિભાગમાં તારીખ અને સમય સમાયોજિત કરો.&lt;/p&gt;</translation>
<translation id="1583429793053364125">આ વેબપૃષ્ઠ પ્રદર્શિત કરતી વખતે કંઇક ખોટું થયું.</translation>
-<translation id="1590457302292452960">સશક્ત પાસવર્ડ બનાવો...</translation>
<translation id="1592005682883173041">સ્થાનિક ડેટા ઍક્સેસ</translation>
<translation id="1594030484168838125">પસંદ કરો</translation>
<translation id="1620510694547887537">કૅમેરો</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">સિસ્ટમ વ્યવસ્થાપકનો સંપર્ક કરવાનો પ્રયાસ કરો.</translation>
<translation id="1740951997222943430">એક માન્ય સમાપ્તિ મહિનો દાખલ કરો</translation>
+<translation id="1743520634839655729">આગલી વખતે વધુ ઝડપથી ચુકવણી કરવા માટે, તમારા કાર્ડ, નામ અને બિલિંગ સરનામાને તમારા Google એકાઉન્ટ અને આ ઉપકરણ પર સાચવો.</translation>
<translation id="17513872634828108">ટેબ્સ ખોલો</translation>
<translation id="1753706481035618306">પૃષ્ઠ નંબર</translation>
<translation id="1763864636252898013">આ સર્વર સાબિત કરી શક્યું નથી કે તે <ph name="DOMAIN" /> છે; તેનું સુરક્ષા પ્રમાણપત્ર તમારા ઉપકરણની ઑપરેટિંગ સિસ્ટમ દ્વારા વિશ્વસનીય નથી. આ કોઈ ખોટી ગોઠવણીને કારણે થયું હશે અથવા કોઈ હુમલાખોર તમારા કનેક્શનને અટકાવી રહ્યો છે.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">તમારા ખુલ્લા ટૅબ્સ અહીં દેખાય છે</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">કાર્ડધારકનું નામ</translation>
-<translation id="1806541873155184440"><ph name="ADDED_TO_AUTOFILL_MONTH" /> ના રોજ ઉમેર્યું</translation>
<translation id="1821930232296380041">અમાન્ય વિનંતી અથવા વિનંતી પરિમાણો</translation>
<translation id="1826516787628120939">તપાસી રહ્યાં છે</translation>
<translation id="1834321415901700177">આ સાઇટમાં હાનિકારક પ્રોગ્રામ્સ છે</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 સૂચન}one{# સૂચન}other{# સૂચન}}</translation>
<translation id="2079545284768500474">પૂર્વવત કરો</translation>
<translation id="20817612488360358">સિસ્ટમ પ્રોક્સી સેટિંગ્સ ઉપયોગમાં લેવા માટે સેટ છે પણ એક સ્પષ્ટ પ્રોક્સી ગોઠવણી પણ ઉલ્લેખિત કરેલી છે.</translation>
-<translation id="2084558088529668945">તમે એવી સાઇટ પર તમારો પાસવર્ડ દાખલ કર્યો કે જે <ph name="ORG_NAME" /> દ્વારા મેનેજ કરવામાં આવતી નથી. તમારા એકાઉન્ટને સુરક્ષિત કરવા માટે, અન્ય ઍપ અને સાઇટ પર તમારા પાસવર્ડનો ફરી ઉપયોગ કરશો નહીં.</translation>
<translation id="2091887806945687916">ધ્વનિ</translation>
<translation id="2094505752054353250">ડોમેન મેળ ખાતું નથી</translation>
<translation id="2096368010154057602">વિભાગ</translation>
+<translation id="2102134110707549001">સશક્ત પાસવર્ડ સૂચવો…</translation>
<translation id="2108755909498034140">તમારું કમ્પ્યુટર પુનઃપ્રારંભ કરો</translation>
<translation id="2113977810652731515">કાર્ડ</translation>
<translation id="2114841414352855701">અવગણ્યું કારણ કે તે <ph name="POLICY_NAME" /> દ્વારા ઓવરરાઇડ થયું હતું.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP ભૂલ</translation>
<translation id="2270484714375784793">ફોન નંબર</translation>
<translation id="2292556288342944218">તમારી ઇન્ટરનેટ ઍક્સેસ અવરોધિત છે</translation>
-<translation id="230155334948463882">નવું કાર્ડ?</translation>
<translation id="2316887270356262533">1 MB કરતાં ઓછું ખાલી કરે છે. તમારી આગલી મુલાકાત સમયે કેટલીક સાઇટો વધુ ધીમે લોડ થઈ શકે છે.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> માટે વપરાશકર્તાનામ અને પાસવર્ડ આવશ્યક છે.</translation>
<translation id="2317583587496011522">ડેબિટ કાર્ડ સ્વીકારવામાં આવે છે.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">સ્વીકૃત કાર્ડ</translation>
<translation id="2702801445560668637">વાંચન સૂચિ</translation>
<translation id="2704283930420550640">મૂલ્ય ફોર્મેટથી મેળ ખાતું નથી.</translation>
-<translation id="2704951214193499422">આ સમયે Chromium તમારા કાર્ડની પુષ્ટિ કરવામાં અસમર્થ હતું. કૃપા કરીને પછીથી ફરી પ્રયાસ કરો.</translation>
<translation id="2705137772291741111">આ સાઇટની સાચવેલ (કૅશ કરેલ) કૉપિ વાંચવા યોગ્ય ન હતી.</translation>
<translation id="2709516037105925701">સ્વતઃભરો</translation>
<translation id="2710942282213947212">તમારા કમ્પ્યુટરમાંનું સૉફ્ટવેર Chromiumને સુરક્ષિત રીતે વેબ સાથે કનેક્ટ થવાથી રોકી રહ્યું છે</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">વિતરણ પદ્ધતિ</translation>
<translation id="277499241957683684">ઉપકરણ રેકોર્ડ ખૂટે છે</translation>
<translation id="2781030394888168909">MacOSના ફૉર્મેટમાં નિકાસ કરો</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">કનેક્શન ફરીથી સેટ થયું.</translation>
<translation id="2788784517760473862">સ્વીકૃત ક્રેડિટ કાર્ડ</translation>
<translation id="2794233252405721443">સાઇટ અવરોધિત કરી</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">તમે સેટિંગ્સ પૃષ્ઠમાંથી કનેક્શન માટે ગોઠવવામાં આવેલ કોઇપણ પ્રોક્સીઓ અક્ષમ કરી શકો છો.</translation>
<translation id="2955913368246107853">શોધ બાર બંધ કરો</translation>
<translation id="2958431318199492670">નેટવર્ક ગોઠવણી ONC માનકનું પાલન કરતી નથી. ગોઠવણીના ભાગો આયાત કરી શકાશે નહીં.</translation>
-<translation id="2966678944701946121">સમાપ્તિ: <ph name="EXPIRATION_DATE_ABBR" />, <ph name="ADDED_TO_AUTOFILL_MONTH" /> ના રોજ ઉમેર્યું</translation>
<translation id="2969319727213777354">એક સુરક્ષિત કનેક્શન સ્થાપિત કરવા માટે, તમારી ઘડિયાળ યોગ્ય રીતે સેટ હોવી જરૂરી છે. આનું કારણ એ કે વેબસાઇટ્સ તેઓને ઓળખવા માટે જે પ્રમાણપત્રોનો ઉપયોગ કરે છે તે ચોક્કસ સમય અવધિ માટે જ માન્ય હોય છે. તમારા ઉપકરણની ઘડિયાળ ખોટી હોવાને લીધે, Google Chrome આ પ્રમાણપત્રોને ચકાસી શકતું નથી.</translation>
<translation id="2972581237482394796">&amp;ફરી કરો</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, વર્તમાનમાં પસંદ કરેલ. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">ખોટો નીતિ પ્રકાર</translation>
<translation id="3037605927509011580">અરર કંઇક ભુલ થઇ!</translation>
<translation id="3041612393474885105">પ્રમાણપત્ર માહિતી</translation>
-<translation id="3063697135517575841">આ સમયે Chrome તમારા કાર્ડની પુષ્ટિ કરવામાં અસમર્થ હતું. કૃપા કરીને પછીથી ફરી પ્રયાસ કરો.</translation>
<translation id="3064966200440839136">બાહ્ય ઍપ્લિકેશન મારફતે ચુકવણી કરવા માટે છુપો મોડ છોડી રહ્યાં છીએ. તો ચાલુ રાખીએ?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{કોઈ નહીં}=1{1 પાસવર્ડ}one{# પાસવર્ડ}other{# પાસવર્ડ}}</translation>
<translation id="3096100844101284527">પિકઅપ માટેનું સરનામું ઉમેરો</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">તમારો ડેટા <ph name="TIME" /> ના રોજ તમારા સમન્વયન પાસફ્રેઝ સાથે એન્ક્રિપ્ટ કરવામાં આવ્યો હતો. સમન્વયન શરૂ કરવા માટે તે દાખલ કરો.</translation>
<translation id="3320021301628644560">બિલિંગ સરનામું ઉમેરો</translation>
<translation id="3338095232262050444">સુરક્ષિત</translation>
-<translation id="3340978935015468852">સેટિંગ્સ</translation>
<translation id="3345135638360864351">આ સાઇટને ઍક્સેસ કરવાની તમારી વિનંતી <ph name="NAME" /> ને મોકલી શકાઈ નથી. કૃપા કરીને ફરી પ્રયાસ કરો.</translation>
<translation id="3355823806454867987">પ્રોક્સી સેટિંગ્સ બદલો...</translation>
<translation id="3361596688432910856">Chrome નિમ્નલિખિત માહિતી <ph name="BEGIN_EMPHASIS" />સાચવશે નહીં<ph name="END_EMPHASIS" />:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">સર્વરનું પ્રમાણપત્ર વિશ્વસનીય નથી.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{સિંક કરેલ ઉપકરણો પર ઓછામાં ઓછી 1 આઇટમ}=1{1 આઇટમ (અને સિંક કરેલ ઉપકરણો પર બીજી ઘણી બધી)}one{# આઇટમ (અને સિંક કરેલ ઉપકરણો પર બીજી ઘણી બધી)}other{# આઇટમ (અને સિંક કરેલ ઉપકરણો પર બીજી ઘણી બધી)}}</translation>
<translation id="3539171420378717834">આ ઉપકરણ પર આ કાર્ડની એક કૉપિ રાખો</translation>
-<translation id="3542684924769048008">આ માટે પાસવર્ડનો ઉપયોગ કરો:</translation>
<translation id="3549644494707163724">તમારા પોતાના સમન્વયન પાસફ્રેઝ સાથે તમામ સમન્વયિત ડેટા એન્ક્રિપ્ટ કરો</translation>
<translation id="3556433843310711081">તમારા માટે તમારા સંચાલક તેને અનાવરોધિત કરી શકે છે</translation>
<translation id="3566021033012934673">તમારું કનેક્શન ખાનગી નથી</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">ખોટી ચકાસણી સહી</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{વધુ <ph name="ITEM_COUNT" /> આઇટમ}one{વધુ <ph name="ITEM_COUNT" /> આઇટમ}other{વધુ <ph name="ITEM_COUNT" /> આઇટમ}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">જો તમે અન્ય સાઇટ પર <ph name="ORG_NAME" /> પાસવર્ડનો ફરી ઉપયોગ કર્યો હોય, તો Chrome તેને રીસેટ કરવાની ભલામણ કરે છે.</translation>
<translation id="4196861286325780578">&amp;ખસેડવું ફરી કરો</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ફાયરવોલ અને એન્ટીવાઇરસ ગોઠવણી તપાસીને<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">ક્રેશેસ</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">તમે કરેલા ફેરફારો સાચવવામાં આવ્યાં ન હોઇ શકે.</translation>
<translation id="4258748452823770588">ખરાબ હસ્તાક્ષર</translation>
<translation id="4265872034478892965">તમારા વ્યવસ્થાપક દ્વારા મંજૂર</translation>
-<translation id="4269787794583293679">(કોઇ વપરાશકર્તાનામ નથી)</translation>
<translation id="4275830172053184480">તમારું ઉપકરણ પુનઃપ્રારંભ કરો</translation>
<translation id="4277028893293644418">પાસવર્ડ રીસેટ કરો</translation>
<translation id="4280429058323657511">, સમાપ્તિ તારીખ <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">પૉપ-અપ અને રીડાયરેક્ટ</translation>
<translation id="443673843213245140">પ્રોક્સીનો ઉપયોગ અક્ષમ કરેલો છે પણ એક સ્પષ્ટ પ્રોક્સી ગોઠવણી ઉલ્લેખિત છે.</translation>
<translation id="445100540951337728">સ્વીકૃત ડેબિટ કાર્ડ</translation>
+<translation id="4472575034687746823">પ્રારંભ કરો</translation>
<translation id="4506176782989081258">માન્યતા ભૂલ: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">સિસ્ટમ વ્યવસ્થાપકનો સંપર્ક કરીને</translation>
<translation id="450710068430902550">વ્યવસ્થાપક સાથે શેર કરવું</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">નીતિઓ ફરીથી લોડ કરો</translation>
<translation id="4728558894243024398">પ્લેટફોર્મ</translation>
<translation id="4736825316280949806">Chromium ને પુનઃપ્રારંભ કરો</translation>
+<translation id="4742407542027196863">પાસવર્ડ મેનેજ કરો…</translation>
<translation id="4744603770635761495">અમલ કરવાયોગ્ય પાથ</translation>
-<translation id="4749685221585524849">છેલ્લે <ph name="LAST_USED_MONTH" /> ના રોજ ઉપયોગ કર્યો</translation>
<translation id="4750917950439032686">તમારી માહિતી (ઉદાહરણ તરીકે, પાસવર્ડ્સ અથવા ક્રેડિટ કાર્ડ નંબર્સ) ખાનગી હોય છે જ્યારે તે આ સાઇટ પર મોકલવામાં આવે.</translation>
<translation id="4756388243121344051">&amp;ઇતિહાસ</translation>
<translation id="4758311279753947758">સંપર્ક માહિતી ઉમેરો</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">જુઓ</translation>
<translation id="4854362297993841467">વિતરણની આ પદ્ધતિ ઉપલબ્ધ નથી. કોઈ ભિન્ન પદ્ધતિ અજમાવો.</translation>
<translation id="4858792381671956233">તમે આ સાઇટની મુલાકાત લો છો તે ઠીક છે કે કેમ તેવું તમે તમારા માતાપિતાને પૂછ્યું</translation>
+<translation id="4876305945144899064">કોઈ વપરાશકર્તા નામ નથી</translation>
<translation id="4880827082731008257">ઇતિહાસ શોધ</translation>
<translation id="4881695831933465202">ખોલો</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">રાજ્ય</translation>
<translation id="5094747076828555589">આ સર્વર સાબિત કરી શક્યું નથી કે તે <ph name="DOMAIN" /> છે; તેનું સુરક્ષા પ્રમાણપત્ર Chromium દ્વારા વિશ્વસનીય નથી. આ કોઈ ખોટી ગોઠવણીને કારણે થયું હશે અથવા કોઈ હુમલાખોર તમારા કનેક્શનને અટકાવી રહ્યો છે.</translation>
<translation id="5095208057601539847">પ્રાંત</translation>
+<translation id="5098332213681597508">આ નામ તમારા Google એકાઉન્ટમાંથી લીધું છે.</translation>
<translation id="5115563688576182185">(64-બિટ)</translation>
<translation id="5121084798328133320">તમે પુષ્ટિ કરી લો પછી, આ સાઇટ સાથે તમારા Google Payments એકાઉન્ટમાંથી કાર્ડની વિગતો શેર કરવામાં આવશે.</translation>
<translation id="5128122789703661928">આ નામવાળું સત્ર ડિલીટ કરવા માટે માન્ય નથી.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">શિપિંગ પદ્ધતિ</translation>
<translation id="5355557959165512791">તમે અત્યારે <ph name="SITE" />ની મુલાકાત લઈ શકતાં નથી કારણ કે તેનું પ્રમાણપત્ર રદબાતલ કરવામાં આવ્યું છે. નેટવર્કમાં ભૂલ આવવી અને હુમલા થવા સામાન્ય રીતે અસ્થાયી હોય છે, તેથી આ પેજ સંભવિત રૂપે થોડા સમય પછી કાર્ય કરશે.</translation>
<translation id="536296301121032821">નીતિ સેટિંગ્સ સ્ટોર કરવામાં નિષ્ફળ થયાં</translation>
+<translation id="5371425731340848620">કાર્ડને અપડેટ કરો</translation>
<translation id="5377026284221673050">"તમારી ઘડિયાળ પાછળ છે" અથવા "તમારી ઘડિયાળ આગળ છે" અથવા "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">આ સાઇટ માટેની પ્રમાણપત્ર શ્રૃંખલા SHA-1 નો ઉપયોગ કરીને સહી કરેલ પ્રમાણપત્ર ધરાવે છે.</translation>
-<translation id="5402410679244714488">સમાપ્તિ: <ph name="EXPIRATION_DATE_ABBR" />, છેલ્લે એક વર્ષ પહેલાં ઉપયોગ કરેલ</translation>
+<translation id="5387961145478138773">તમારી મનપસંદ Google ઍપનો ઝડપી ઍક્સેસ મેળવો</translation>
<translation id="540969355065856584">આ સર્વર સાબિત કરી શક્યું નથી કે તે <ph name="DOMAIN" /> છે; તેનું સુરક્ષા પ્રમાણપત્ર આ સમયે માન્ય નથી. આ કોઇ ખોટી ગોઠવણીને કારણે થયું હોય અથવા કોઈ હુમલાખોર તમારા કનેક્શનને અટકાવી રહ્યો હોઇ શકે છે.</translation>
<translation id="5421136146218899937">બ્રાઉઝિંગ ડેટા સાફ કરો...</translation>
<translation id="5430298929874300616">બુકમાર્ક દૂર કરો</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">ઇમેઇલ</translation>
+<translation id="5666899935841546222">શું તમે તમારાં બધાં કાર્ડ એક જ સ્થાને રાખવા માગો છો?</translation>
<translation id="5675650730144413517">આ પૃષ્ઠ કામ કરી રહ્યું નથી</translation>
<translation id="5685654322157854305">વિતરણ માટેનું સરનામું ઉમેરો</translation>
<translation id="5689199277474810259">JSON પર નિકાસ કરો</translation>
<translation id="5689516760719285838">સ્થાન</translation>
<translation id="570530837424789914">મેનેજ કરો…</translation>
+<translation id="57094364128775171">સશક્ત પાસવર્ડ સૂચવો…</translation>
<translation id="5710435578057952990">આ વેબસાઇટની ઓળખ ચકાસવામાં આવી નથી.</translation>
<translation id="5719499550583120431">પ્રીપેઇડ કાર્ડ સ્વીકારવામાં આવે છે.</translation>
<translation id="5720705177508910913">વર્તમાન વપરાશકર્તા</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">આ સાઇટ પર પહોંચી શકાતું નથી</translation>
<translation id="5869522115854928033">સાચવેલા પાસવર્ડ્સ</translation>
<translation id="5893752035575986141">ક્રેડિટ કાર્ડ સ્વીકારવામાં આવે છે.</translation>
-<translation id="5898382028489516745">જો તમે અન્ય સાઇટ પર <ph name="ORG_NAME" /> પાસવર્ડનો ફરી ઉપયોગ કર્યો હોય, તો Chromium તેને રીસેટ કરવાની ભલામણ કરે છે.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (સમન્વયિત)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 ઉપયોગમાં છે}one{# ઉપયોગમાં છે}other{# ઉપયોગમાં છે}}</translation>
<translation id="5939518447894949180">રીસેટ કરો</translation>
-<translation id="5959728338436674663">જોખમી અ‍ૅપ્લિકેશનો અને સાઇટ્સ શોધવામાં સહાય કરવા માટે Google ને કેટલીક <ph name="BEGIN_WHITEPAPER_LINK" />સિસ્ટમ માહિતી અને પૃષ્ઠ સામગ્રી<ph name="END_WHITEPAPER_LINK" /> આપમેળે મોકલો. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">સંપર્ક માહિતીમાં ફેરફાર કરો</translation>
<translation id="5967867314010545767">ઇતિહાસમાંથી દૂર કરો</translation>
<translation id="5975083100439434680">ઝૂમ ઘટાડો</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">પોસ્ટલ કોડ</translation>
<translation id="6290238015253830360">તમારા સૂચવેલા લેખ અહીં દેખાય છે</translation>
<translation id="6305205051461490394"><ph name="URL" />, પહોંચવા યોગ્ય નથી.</translation>
-<translation id="6319915415804115995">છેલ્લે એક વર્ષ પહેલાં ઉપયોગ કરેલ</translation>
<translation id="6321917430147971392">તમારી DNS સેટિંગ્સ તપાસો</translation>
<translation id="6328639280570009161">નેટવર્ક પૂર્વાનુમાનને અક્ષમ કરવાનો પ્રયાસ કરો</translation>
<translation id="6328786501058569169">આ સાઇટ ભ્રામક છે</translation>
<translation id="6337133576188860026"><ph name="SIZE" /> કરતાં ઓછી જગ્યા ખાલી કરે છે. તમારી આગલી મુલાકાત સમયે કેટલીક સાઇટ વધુ ધીમે લોડ થઈ શકે છે.</translation>
<translation id="6337534724793800597">નામ દ્વારા નીતિઓને ફિલ્ટર કરો</translation>
-<translation id="6342069812937806050">હમણાં જ</translation>
<translation id="6355080345576803305">સાર્વજનિક સત્ર ઓવરરાઇડ</translation>
<translation id="6358450015545214790">આનો અર્થ શું છે?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 અન્ય સૂચન}one{# અન્ય સૂચન}other{# અન્ય સૂચન}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;કાઢી નાખવું ફરી કરો</translation>
<translation id="6534179046333460208">વાસ્તવિક વેબ સૂચનો</translation>
<translation id="6550675742724504774">વિકલ્પો</translation>
-<translation id="6556915248009097796">સમાપ્તિ: <ph name="EXPIRATION_DATE_ABBR" />, છેલ્લે <ph name="LAST_USED_DATE_NO_DETAIL" /> ના રોજ ઉપયોગ કર્યો</translation>
<translation id="6563469144985748109">તમારા સંચાલકે હજી સુધી તેને મંજૂર કરેલ નથી</translation>
<translation id="6569060085658103619">તમે એક્સ્ટેન્શન પૃષ્ઠ જોઈ રહ્યાં છો</translation>
<translation id="6596325263575161958">એન્ક્રિપ્શન વિકલ્પો</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">તમે <ph name="DOMAIN" /> પર પહોંચવાનો પ્રયાસ કર્યો, પરંતુ સર્વરે નબળા હસ્તાક્ષર અલ્ગોરિધમ (જેમ કે SHA-1)નો ઉપયોગ કરીને હસ્તાક્ષરિત કરેલું પ્રમાણપત્ર પ્રસ્તુત કર્યું. આનો અર્થ એ છે કે સર્વરે પ્રસ્તુત કરેલા સુરક્ષા પ્રમાણપત્રો બનાવટી હોઈ શકે છે અને તે સર્વર તમારું અપેક્ષિત સર્વર (તમે કોઈ હુમલાખોર સાથે વાર્તાલાપ કરતા હોય શકે) ન પણ હોય.</translation>
<translation id="6831043979455480757">અનુવાદ કરો</translation>
<translation id="6839929833149231406">વિસ્તાર</translation>
+<translation id="6852204201400771460">ઍપ ફરીથી લોડ કરીએ?</translation>
+<translation id="6865412394715372076">હાલમાં આ કાર્ડની ચકાસણી કરી શકાતી નથી</translation>
<translation id="6874604403660855544">&amp;ઉમેરવું ફરી કરો</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">નીતિ સ્તર સમર્થિત નથી.</translation>
<translation id="6895330447102777224">તમારા કાર્ડની પુષ્ટિ કરવામાં આવી છે</translation>
<translation id="6897140037006041989">વપરાશકર્તા એજન્ટ</translation>
+<translation id="6903319715792422884">Googleને અમુક <ph name="BEGIN_WHITEPAPER_LINK" />સિસ્ટમ માહિતી અને પેજ કન્ટેન્ટ<ph name="END_WHITEPAPER_LINK" />મોકલીને સલામત બ્રાઉઝિંગ બહેતર બનાવવામાં સહાય કરો. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">વપરાશકર્તા: </translation>
+<translation id="6944692733090228304"><ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> દ્વારા સંચાલિત ન થતી હોય, તેવી સાઇટ પર તમે તમારો પાસવર્ડ દાખલ કર્યો છે. તમારા એકાઉન્ટની સુરક્ષા માટે, અન્ય ઍપ અને સાઇટ પર તમારા પાસવર્ડનો ફરી ઉપયોગ ન કરો.</translation>
<translation id="6945221475159498467">પસંદ કરો</translation>
<translation id="6948701128805548767">પિકઅપ પદ્ધતિ અને આવશ્યકતાઓ જોવા માટે, એક સરનામું પસંદ કરો</translation>
<translation id="6949872517221025916">પાસવર્ડ રીસેટ કરો</translation>
+<translation id="6950684638814147129">JSON મૂલ્યનું વિશ્લેષણ કરવામાં ભૂલ આવી: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">સર્વરનું પ્રમાણપત્ર બનાવટી હોય એવું લાગે છે.</translation>
<translation id="6965382102122355670">બરાબર, સમજાઇ ગયું</translation>
<translation id="6965978654500191972">ઉપકરણ</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;&lt;strong&gt;લાગુ કરો&lt;/strong&gt; પર ક્લિક કરો, પછી &lt;strong&gt;ઓકે&lt;/strong&gt; પર ક્લિક કરો
&lt;li&gt;તમારા કમ્પ્યુટર પરથી સૉફ્ટવેરને કાયમી ધોરણે કાઢી નાખવાની રીત જાણવા માટે, &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome સહાયતા કેન્દ્ર&lt;/a&gt; પર જાઓ
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">પાસવર્ડ મેનેજ કરો…</translation>
<translation id="7419106976560586862">પ્રોફાઇલ પાથ</translation>
<translation id="7437289804838430631">સંપર્ક માહિતી ઉમેરો</translation>
<translation id="7441627299479586546">ખોટો નીતિ વિષય</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790">આ સમસ્યા વિશે <ph name="BEGIN_LINK" />વધુ જાણો<ph name="END_LINK" />.</translation>
<translation id="7455133967321480974">વૈશ્વિક ડિફોલ્ટનો ઉપયોગ કરો (અવરોધિત કરો)</translation>
<translation id="7460163899615895653">અન્ય ઉપકરણોમાંના તમારા તાજેતરના ટૅબ્સ અહીં દેખાય છે</translation>
-<translation id="7469372306589899959">કાર્ડની પુષ્ટિ કરી રહ્યાં છે</translation>
<translation id="7473891865547856676">નહીં આભાર</translation>
<translation id="7481312909269577407">ફોર્વર્ડ કરો</translation>
<translation id="7485870689360869515">કોઈ ડેટા મળ્યો નથી.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">નેટવર્ક કનેક્શનમાં સમસ્યાને કારણે ભાષાંતર નિષ્ફળ રહ્યું.</translation>
<translation id="8311129316111205805">સત્ર લોડ કરો</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> ની ઍક્સેસ નકારાઈ હતી</translation>
+<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="8349305172487531364">બુકમાર્ક્સ બાર</translation>
<translation id="8363502534493474904">એરપ્લેન મોડ બંધ કરીને</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> એ પ્રતિસાદ આપવા માટે ઘણો સમય લીધો.</translation>
<translation id="8503559462189395349">Chrome પાસવર્ડ</translation>
<translation id="8503813439785031346">વપરાશકર્તાનામ</translation>
+<translation id="8508648098325802031">શોધ આઇકન</translation>
<translation id="8543181531796978784">તમે <ph name="BEGIN_ERROR_LINK" />શોધ સમસ્યાની જાણ<ph name="END_ERROR_LINK" /> કરી શકો છો અથવા જો તમે તમારી સુરક્ષા અંગેનાં જોખમોને સમજતાં હોવ, તો <ph name="BEGIN_LINK" />આ અસુરક્ષિત સાઇટની મુલાકાત<ph name="END_LINK" /> લઈ શકો છો.</translation>
<translation id="8543556556237226809">પ્રશ્નો છે? તમારી પ્રોફાઇલનું નિરીક્ષણ કરનાર વ્યક્તિનો સંપર્ક કરો.</translation>
<translation id="8553075262323480129">ભાષાંતર નિષ્ફળ રહ્યું કારણ કે પૃષ્ઠની ભાષા નિર્ધારિત થઈ શકી નથી.</translation>
<translation id="8557066899867184262">CVC તમારા કાર્ડની પાછળ હોય છે.</translation>
<translation id="8559762987265718583"><ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> પર ખાનગી કનેક્શન સ્થાપિત કરી શકાતું નથી કારણ કે તમારા ઉપકરણની તારીખ અને સમય (<ph name="DATE_AND_TIME" />) અયોગ્ય છે.</translation>
+<translation id="8564985650692024650">જો તમે અન્ય સાઇટ પર <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> પાસવર્ડનો ફરી ઉપયોગ કર્યો હોય, તો Chromium તેને રીસેટ કરવાનો સુઝાવ આપે છે.</translation>
<translation id="8571890674111243710">પૃષ્ઠને <ph name="LANGUAGE" /> માં અનુવાદિત કરી રહ્યું છે...</translation>
<translation id="858637041960032120">ફોન નંબર ઉમેરો</translation>
<translation id="859285277496340001">પ્રમાણપત્રને રદ કરવામાં આવ્યું છે કે નહિ તે તપાસવા માટે કોઈપણ મેકેનિઝમ નિર્દિષ્ટ કરતું નથી.</translation>
+<translation id="860043288473659153">કાર્ડધારકનું નામ</translation>
<translation id="8620436878122366504">તમારા માતાપિતાએ તેને હજી સુધી મંજૂર કરેલ નથી</translation>
<translation id="8625384913736129811">આ કાર્ડને આ ઉપકરણમાં સાચવો</translation>
-<translation id="8639963783467694461">સ્વતઃભરો સેટિંગ્સ</translation>
+<translation id="8663226718884576429">ઑર્ડરનો સારાંશ, <ph name="TOTAL_LABEL" />, વધુ વિગતો</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, જવાબ, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> સાથેનું તમારું કનેક્શન એન્ક્રિપ્ટેડ નથી.</translation>
<translation id="8718314106902482036">ચુકવણી પૂર્ણ થઈ નથી</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, શોધ સૂચન</translation>
<translation id="8725066075913043281">ફરી પ્રયાસ કરો</translation>
<translation id="8728672262656704056">તમે છુપા મોડમાં ગયા છો</translation>
<translation id="8730621377337864115">થઈ ગયું</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">બુકમાર્ક્સ</translation>
<translation id="883848425547221593">અન્ય બુકમાર્ક્સ</translation>
<translation id="884264119367021077">શિપિંગ સરનામું</translation>
+<translation id="8846319957959474018">બુકમાર્ક સાથે ઍપ સરળતાથી ખોલો</translation>
<translation id="884923133447025588">રદ કરવાની કોઈ મેકેનિઝમ મળી નથી.</translation>
<translation id="885730110891505394">Google સાથે શેર કરવું</translation>
+<translation id="8858065207712248076">જો તમે અન્ય સાઇટ પર <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> પાસવર્ડનો ફરી ઉપયોગ કર્યો હોય, તો Chrome તેને રીસેટ કરવાનો સુઝાવ આપે છે.</translation>
<translation id="8866481888320382733">ભૂલ વિશ્લેષણ નીતિ સેટિંગ્સ</translation>
<translation id="8870413625673593573">તાજેતરમાં બંધ કરેલા</translation>
<translation id="8874824191258364635">એક માન્ય કાર્ડ નંબર દાખલ કરો</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> સામાન્ય રીતે તમારી માહિતીને સુરક્ષિત રાખવા માટે એન્ક્રિપ્શનનો ઉપયોગ કરે છે. જ્યારે આ સમયે Chromium દ્વારા <ph name="SITE" /> થી કનેક્ટ કરવાનો પ્રયાસ થયો, ત્યારે વેબસાઇટે અસામાન્ય અને ખોટા ઓળખાણપત્રોને પાછા મોકલ્યાં. આવું ત્યારે થઇ શકે જ્યારે કોઈ હુમલાખોર <ph name="SITE" /> હોવાનો ડોળ કરવાનો પ્રયાસ કરી રહ્યો હોય અથવા કોઈ Wi-Fi સાઇન-ઇન સ્ક્રીને કનેક્શનમાં વિક્ષેપ પાડ્યો હોય. તમારી માહિતી હજી પણ સુરક્ષિત છે કારણ કે Chromium એ કોઈપણ ડેટા વિનિમય થાય તે પહેલાં જ કનેક્શન રોકી દીધું.</translation>
<translation id="9106062320799175032">બિલિંગ સરનામું ઉમેરો</translation>
<translation id="910908805481542201">આને સુધારવામાં મારી સહાય કરો</translation>
+<translation id="9114524666733003316">કાર્ડ કન્ફર્મ કરી રહ્યાં છીએ...</translation>
<translation id="9128870381267983090">નેટવર્કથી કનેક્ટ કરો</translation>
<translation id="9137013805542155359">મૂળ બતાવો</translation>
<translation id="9137248913990643158">આ ઍપ્લિકેશનનો ઉપયોગ કરતાં પહેલાં કૃપા કરીને Chrome ને પ્રારંભ કરો અને સાઇન ઇન કરો.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">તમારી ચુકવણી પદ્ધતિઓ સાચવવામાં આવી છે કે કેમ તે ચેક કરવાની સાઇટને મંજૂરી આપો</translation>
<translation id="9169664750068251925">હંમેશા આ સાઇટ પર અવરોધિત કરો</translation>
<translation id="9170848237812810038">&amp;પૂર્વવત્ કરો</translation>
+<translation id="9171296965991013597">ઍપ છોડીએ?</translation>
<translation id="917450738466192189">સર્વરનું પ્રમાણપત્ર અમાન્ય છે.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" />, એક અસમર્થિત પ્રોટોકોલનો ઉપયોગ કરે છે.</translation>
<translation id="9205078245616868884">તમારો ડેટા તમારા સમન્વયન પાસફ્રેઝ સાથે એન્ક્રિપ્ટ કરવામાં આવ્યો છે. સમન્વયન શરૂ કરવા માટે તે દાખલ કરો.</translation>
diff --git a/chromium/components/strings/components_strings_hi.xtb b/chromium/components/strings/components_strings_hi.xtb
index c89426b62a1..8ec07816dda 100644
--- a/chromium/components/strings/components_strings_hi.xtb
+++ b/chromium/components/strings/components_strings_hi.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">मान छिपाएं</translation>
<translation id="1228893227497259893">गलत इकाई पहचानकर्ता</translation>
<translation id="1232569758102978740">शीर्षक रहित</translation>
+<translation id="1250759482327835220">अगली बार तेज़ी से भुगतान करने के लिए, अपने कार्ड, नाम और बिलिंग पते को अपने 'Google खाते' में सेव करें.</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;
@@ -82,7 +83,7 @@
<translation id="1430915738399379752">प्रिंट करें</translation>
<translation id="1484290072879560759">शिपिंग पता चुनें</translation>
<translation id="1506687042165942984">इस पृष्‍ठ की सहेजी गई (अर्थात जिसे पुराना माना जाता है) कॉपी दिखाएं.</translation>
-<translation id="1507202001669085618">&lt;p&gt;अगर आपके पास ऐसा वाई-फ़ाई पोर्टल है जिसके लिए यह ज़रूरी है कि आप ऑनलाइन होने से पहले साइन इन करें, तो आपको यह गड़बड़ी दिखाई देगी.&lt;/p&gt;
+<translation id="1507202001669085618">&lt;p&gt;अगर आप ऐसे वाई-फ़ाई पोर्टल का उपयोग कर रहे हैं जिसमें ऑनलाइन होने से पहले साइन इन ज़रूरी है, तो आपको यह गड़बड़ी दिखाई देगी.&lt;/p&gt;
&lt;p&gt;गड़बड़ी ठीक करने के लिए, उस पेज पर &lt;strong&gt;कनेक्ट करें&lt;/strong&gt; को क्लिक करें जिसे आप खोलने की कोशिश कर रहे हैं.&lt;/p&gt;</translation>
<translation id="1517433312004943670">फ़ोन नंबर आवश्यक है</translation>
<translation id="1517500485252541695">स्वीकृत क्रेडिट और डेबिट कार्ड</translation>
@@ -97,7 +98,6 @@
&lt;p&gt;कृपया दिनांक और समय को &lt;strong&gt;सेटिंग&lt;/strong&gt; ऐप के &lt;strong&gt;सामान्‍य&lt;/strong&gt; अनुभाग से एडजस्‍ट करें.&lt;/p&gt;</translation>
<translation id="1583429793053364125">यह वेबपेज प्रदर्शित करते समय कोई समस्या हुई.</translation>
-<translation id="1590457302292452960">मज़बूत पासवर्ड जनरेट करें...</translation>
<translation id="1592005682883173041">स्थानीय डेटा एक्सेस</translation>
<translation id="1594030484168838125">चुनें</translation>
<translation id="1620510694547887537">कैमरा</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">सिस्टम व्यवस्थापक से संपर्क करने का प्रयास करें.</translation>
<translation id="1740951997222943430">खत्म होने का मान्य महीना डालें</translation>
+<translation id="1743520634839655729">अगली बार तेज़ी से भुगतान करने के लिए, अपने कार्ड, नाम और बिलिंग पते को अपने 'Google खाते' में और इस डिवाइस में सेव करें.</translation>
<translation id="17513872634828108">टैब खोलें</translation>
<translation id="1753706481035618306">पृष्‍ठ संख्‍या</translation>
<translation id="1763864636252898013">यह सर्वर यह नहीं प्रमाणित कर सका कि यह <ph name="DOMAIN" /> है; इसका सुरक्षा प्रमाणपत्र आपके डिवाइस के ऑपरेटिंग सिस्टम द्वारा विश्वसनीय नहीं है. ऐसा गलत कॉन्फ़िगरेशन या किसी आक्रमणकर्ता द्वारा आपके कनेक्शन में अवरोध डालने के कारण हो सकता है.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">आपके द्वारा खोले गए टैब, यहां दिखाई देंगे</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">कार्ड के मालिक का नाम</translation>
-<translation id="1806541873155184440"><ph name="ADDED_TO_AUTOFILL_MONTH" /> को जोड़ा गया</translation>
<translation id="1821930232296380041">अमान्य अनुरोध या अनुरोध पैरामीटर</translation>
<translation id="1826516787628120939">जाँच की जा रही है</translation>
<translation id="1834321415901700177">इस साइट में हानिकारक प्रोग्राम हैं</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 सुझाव}one{# सुझाव}other{# सुझाव}}</translation>
<translation id="2079545284768500474">वापस लाएं</translation>
<translation id="20817612488360358">सिस्‍टम प्रॉक्‍सी सेटिंग उपयोग किए जाने के लिए सेट हैं लेकिन कोई स्पष्‍ट प्रॉक्‍सी कॉन्फ़िगरेशन भी निर्दिष्ट है.</translation>
-<translation id="2084558088529668945">आपने एक ऐसी साइट पर अपना पासवर्ड डाला है जिसे <ph name="ORG_NAME" /> प्रबंधित नहीं करता है. अपने खाते को सुरक्षित रखने के लिए, दूसरे ऐप्लिकेशन और साइटों पर अपने पासवर्ड का दोबारा इस्तेमाल नहीं करें.</translation>
<translation id="2091887806945687916">ध्वनि</translation>
<translation id="2094505752054353250">डोमेन का गलत-मिलान</translation>
<translation id="2096368010154057602">विभाग</translation>
+<translation id="2102134110707549001">मज़बूत पासवर्ड सुझाएं…</translation>
<translation id="2108755909498034140">अपना कंप्यूटर फिर से चालू करें</translation>
<translation id="2113977810652731515">कार्ड</translation>
<translation id="2114841414352855701">ध्यान नहीं दिया गया क्योंकि यह <ph name="POLICY_NAME" /> द्वारा ओवरराइड की गई थी.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP गड़बड़ी</translation>
<translation id="2270484714375784793">फ़ोन नंबर</translation>
<translation id="2292556288342944218">आपका इंटरनेट कनेक्शन अवरुद्ध है</translation>
-<translation id="230155334948463882">नया कार्ड?</translation>
<translation id="2316887270356262533">1 MB से भी कम जगह खाली करता है. जब आप अगली बार विज़िट करेंगे तो, कुछ साइटें और धीमे लोड हो सकती हैं.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> के लिए उपयोगकर्ता नाम और पासवर्ड आवश्यक है.</translation>
<translation id="2317583587496011522">डेबिट कार्ड स्वीकार किए जाते हैं.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">स्वीकार किए जाने वाले कार्ड</translation>
<translation id="2702801445560668637">पठन सूची</translation>
<translation id="2704283930420550640">मान का प्रारूप से मिलान नहीं होता.</translation>
-<translation id="2704951214193499422">क्रोमियम इस समय आपके कार्ड की पुष्टि नहीं कर सका. कृपया बाद में पुन: प्रयास करें.</translation>
<translation id="2705137772291741111">इस साइट की सहेजी गई (संचित) कॉपी पढ़ने योग्य नहीं थी.</translation>
<translation id="2709516037105925701">ऑटोमैटिक भरना</translation>
<translation id="2710942282213947212">आपके कंप्यूटर पर मौजूद सॉफ़्टवेयर क्रोमियम को सुरक्षित तरीके से वेब से जुड़ने से रोक रहा है</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">शिपिंग का तरीका</translation>
<translation id="277499241957683684">डिवाइस का रिकॉर्ड लापता है</translation>
<translation id="2781030394888168909">MacOS निर्यात करें</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">कनेक्‍शन रीसेट किया गया था.</translation>
<translation id="2788784517760473862">स्वीकृत क्रेडिट कार्ड</translation>
<translation id="2794233252405721443">साइट अवरोधित है</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">आप किसी कनेक्शन के लिए कॉन्फ़िगर की गई किसी भी प्रॉक्सी को सेटिंग पेज से बंद कर सकते हैं.</translation>
<translation id="2955913368246107853">खोज बार बंद करें</translation>
<translation id="2958431318199492670">नेटवर्क कॉन्फ़िगरेशन ONC मानक का पालन नहीं करता. कॉन्फ़िगरेशन के कुछ भाग आयात नहीं किए जा सकते हैं.</translation>
-<translation id="2966678944701946121">खत्म होने की तारीख: <ph name="EXPIRATION_DATE_ABBR" />, <ph name="ADDED_TO_AUTOFILL_MONTH" /> को जोड़ा गया</translation>
<translation id="2969319727213777354">सुरक्षित कनेक्‍शन स्‍थापित करने के लिए, आपकी घड़ी को सही तरीके से सेट किए जाने की आवश्‍यकता है. ऐसा इसलिए क्‍योंकि वेबसाइटों द्वारा स्‍वयं की पहचान करने के लिए उपयोग किए जाने वाले प्रमाणपत्र केवल विशिष्‍ट समयावधियों के लिए ही मान्‍य होते हैं. चूंकि आपके डिवाइस की घड़ी गलत है, इसलिए Google Chrome इन प्रमाणपत्रों का सत्‍यापन नहीं कर सकता.</translation>
<translation id="2972581237482394796">&amp;फिर से करें</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, इस समय चुना गया है. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">गलत नीति प्रकार</translation>
<translation id="3037605927509011580">हे भगवान!</translation>
<translation id="3041612393474885105">प्रमाणपत्र जानकारी</translation>
-<translation id="3063697135517575841">Chrome इस समय आपके कार्ड की पुष्टि नहीं कर सका. कृपया बाद में पुन: प्रयास करें.</translation>
<translation id="3064966200440839136">किसी बाहरी ऐप्लिकेशन के ज़रिए भुगतान करने के लिए गुप्त मोड छोड़ रहे हैं. जारी रखना चाहते हैं?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{कुछ नहीं}=1{1 पासवर्ड}one{# पासवर्ड}other{# पासवर्ड}}</translation>
<translation id="3096100844101284527">पिकअप का पता जोड़ें</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">आपका डेटा आपके समन्वयन पासफ़्रेज़ के साथ <ph name="TIME" /> को एन्क्रिप्ट किया गया था. समन्वयन शुरू करने के लिए इसे डालें.</translation>
<translation id="3320021301628644560">बिलिंग पता जोड़ें</translation>
<translation id="3338095232262050444">सुरक्षित</translation>
-<translation id="3340978935015468852">सेटिंग</translation>
<translation id="3345135638360864351">इस साइट को ऐक्‍सेस करने का आपका अनुरोध <ph name="NAME" /> को नहीं भेजा जा सका. कृपया पुन: प्रयास करें.</translation>
<translation id="3355823806454867987">प्रॉक्सी सेटिंग बदलें...</translation>
<translation id="3361596688432910856">Chrome नीचे दी गई जानकारी को <ph name="BEGIN_EMPHASIS" />सेव नहीं करेगा<ph name="END_EMPHASIS" />:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">सर्वर का प्रमाणपत्र विश्वसनीय नहीं है.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{सिंक किए हुए डिवाइस पर कम से कम 1 आइटम}=1{1 आइटम (सिंक किए हुए डिवाइस पर और भी बहुत कुछ)}one{# आइटम (सिंक किए हुए डिवाइस पर और भी बहुत कुछ)}other{# आइटम (सिंक किए हुए डिवाइस पर और भी बहुत कुछ)}}</translation>
<translation id="3539171420378717834">इस डिवाइस पर इस कार्ड की कॉपी रखें</translation>
-<translation id="3542684924769048008">इसके लिए पासवर्ड का उपयोग करें:</translation>
<translation id="3549644494707163724">सभी समन्वयित डेटा को अपने स्वयं के समन्वयन पासफ़्रेज़ के साथ एन्क्रिप्ट करें</translation>
<translation id="3556433843310711081">आपका प्रबंधक इसे आपके लिए अनवरोधित कर सकता है</translation>
<translation id="3566021033012934673">आपका कनेक्शन निजी नहीं है</translation>
@@ -431,7 +426,7 @@
<translation id="3946209740501886391">इस साइट पर हमेशा पूछें</translation>
<translation id="3949571496842715403">यह सर्वर प्रमाणित नहीं कर सका कि यह <ph name="DOMAIN" /> है; इसके सुरक्षा प्रमाणपत्र में विषय के वैकल्पिक नाम नहीं बताए गए हैं. ऐसा गलत कॉन्फ़िगरेशन के कारण या किसी आक्रमणकर्ता की ओर से आपके कनेक्शन में अवरोध डालने के कारण हो सकता है.</translation>
<translation id="3949601375789751990">आपका ब्राउज़िंग इतिहास यहां दिखाई देता है</translation>
-<translation id="3950820424414687140">प्रवेश करें</translation>
+<translation id="3950820424414687140">साइन इन करें</translation>
<translation id="3963721102035795474">रीडर मोड</translation>
<translation id="3964661563329879394">{COUNT,plural, =0{कुछ नहीं}=1{1 साइट से }one{# साइटों से }other{# साइटों से }}</translation>
<translation id="397105322502079400">गणना की जा रही है...</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">गलत सत्यापन हस्ताक्षर</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> और आइटम}one{<ph name="ITEM_COUNT" /> और आइटम}other{<ph name="ITEM_COUNT" /> और आइटम}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">अगर आपने अपने <ph name="ORG_NAME" /> पासवर्ड का दूसरी साइटों पर दोबारा इस्तेमाल किया है, तो Chrome आपको उसे रीसेट करने का सुझाव देता है.</translation>
<translation id="4196861286325780578">&amp;ले जाना फिर से करें</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />फायरवॉल और एंटीवायरस कॉन्फ़िगरेशन की जाँच करें<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">क्रैश</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">हो सकता है कि आपके द्वारा किए गए बदलाव सहेजे ना जाएं.</translation>
<translation id="4258748452823770588">खराब हस्ताक्षर</translation>
<translation id="4265872034478892965">आपके व्यवस्थापक ने अनुमति दी है</translation>
-<translation id="4269787794583293679">(कोई उपयोगकर्ता नाम नहीं)</translation>
<translation id="4275830172053184480">अपना डिवाइस पुन: प्रारंभ करें</translation>
<translation id="4277028893293644418">पासवर्ड रीसेट करें</translation>
<translation id="4280429058323657511">, समाप्ति दिनांक <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">पॉप-अप और रीडायरेक्ट</translation>
<translation id="443673843213245140">प्रॉक्‍सी का उपयोग अक्षम है लेकिन कोई स्‍पष्ट प्रॉक्‍सी कॉन्फ़िगरेशन निर्दिष्ट किया गया है.</translation>
<translation id="445100540951337728">स्वीकृत डेबिट कार्ड</translation>
+<translation id="4472575034687746823">आरंभ करें</translation>
<translation id="4506176782989081258">सत्‍यापन गड़बड़ी: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">सिस्टम व्यवस्थापक से संपर्क करें</translation>
<translation id="450710068430902550">व्यवस्थापक के साथ शेयर करना</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">नीतियां फिर से लोड करें</translation>
<translation id="4728558894243024398">प्लेटफ़ॉर्म</translation>
<translation id="4736825316280949806">क्रोमियम को फिर से शुरू करें</translation>
+<translation id="4742407542027196863">पासवर्ड प्रबंधित करें…</translation>
<translation id="4744603770635761495">निष्पादन-योग्य पथ</translation>
-<translation id="4749685221585524849">पिछली बार <ph name="LAST_USED_MONTH" /> को उपयोग किया गया</translation>
<translation id="4750917950439032686">आपकी जानकारी (उदाहरण के लिए, पासवर्ड या क्रेडिट कार्ड नंबर) जब इस साइट पर भेजी जाती है तब वह निजी होती है .</translation>
<translation id="4756388243121344051">&amp;इतिहास</translation>
<translation id="4758311279753947758">संपर्क जानकारी जोड़ें</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">देखें</translation>
<translation id="4854362297993841467">वितरण का यह तरीका उपलब्ध नहीं है. कोई दूसरा तरीका आज़माएं.</translation>
<translation id="4858792381671956233">आपने अपने अभिभावकों से पूछा है कि इस साइट पर जाना ठीक है या नहीं</translation>
+<translation id="4876305945144899064">कोई उपयोगकर्ता नाम नहीं</translation>
<translation id="4880827082731008257">खोज इतिहास</translation>
<translation id="4881695831933465202">खोलें</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">राज्य</translation>
<translation id="5094747076828555589">यह सर्वर यह नहीं प्रमाणित कर सका कि यह <ph name="DOMAIN" /> है; इसका सुरक्षा प्रमाणपत्र Chromium द्वारा विश्वसनीय नहीं है. ऐसा गलत कॉन्फ़िगरेशन या किसी आक्रमणकर्ता द्वारा आपके कनेक्शन में अवरोध डालने के कारण हो सकता है.</translation>
<translation id="5095208057601539847">प्रांत</translation>
+<translation id="5098332213681597508">यह नाम आपके Google खाते से संबंधित है.</translation>
<translation id="5115563688576182185">(64-बिट)</translation>
<translation id="5121084798328133320">आपकी ओर से पुष्टि होने के बाद, आपके Google Payments खाते में मौजूद कार्ड के विवरण इस साइट से शेयर किए जाएंगे.</translation>
<translation id="5128122789703661928">इस नाम वाला सीज़न मिटाने के लिए मान्य नहीं है.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">शिपिंग का तरीका</translation>
<translation id="5355557959165512791">आप इस समय <ph name="SITE" /> पर नहीं जा सकते हैं क्योंकि उसका प्रमाणपत्र रद्द कर दिया गया है. नेटवर्क की गड़बड़ी और हमले आमतौर पर कुछ देर के लिए होते हैं, इसलिए मुमकिन है कि यह पेज बाद में काम करे.</translation>
<translation id="536296301121032821">नीति सेटिंग संग्रहित करने में विफल</translation>
+<translation id="5371425731340848620">कार्ड अपडेट करें</translation>
<translation id="5377026284221673050">"आपकी घड़ी पीछे चल रही है" या "आपकी घड़ी आगे चल रही है" या "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">इस साइट की प्रमाणपत्र श्रृंखला में, SHA-1 का उपयोग करके हस्ताक्षर किया गया प्रमाणपत्र शामिल है.</translation>
-<translation id="5402410679244714488">समाप्ति दिनांक: <ph name="EXPIRATION_DATE_ABBR" />, पिछली बार एक वर्ष से पहले उपयोग किया गया</translation>
+<translation id="5387961145478138773">अपने पसंदीदा 'Google ऐप्लिकेशन' झटपट एक्सेस करें</translation>
<translation id="540969355065856584">यह सर्वर यह प्रमाणित नहीं कर सका कि यह <ph name="DOMAIN" /> है; इसका सुरक्षा प्रमाण पत्र इस समय मान्य नहीं है. ऐसा गलत कॉन्फ़िगरेशन या किसी आक्रमणकर्ता द्वारा आपके कनेक्शन में अवरोध डालने के कारण हो सकता है.</translation>
<translation id="5421136146218899937">ब्राउज़िंग डेटा साफ़ करें...</translation>
<translation id="5430298929874300616">बुकमार्क निकालें</translation>
@@ -658,7 +655,7 @@
<translation id="5565735124758917034">सक्रिय</translation>
<translation id="5571083550517324815">इस पते से पिक अप नहीं किया जा सकता. कोई दूसरा पता चुनें.</translation>
<translation id="5571347317547569613">({NUM_COOKIES,plural, =1{1 इस्तेमाल में है}one{# इस्तेमाल में हैं}other{# इस्तेमाल में हैं}})</translation>
-<translation id="5572851009514199876">कृपया Chrome शुरू करके उसमें प्रवेश करें ताकि Chrome देख सके कि क्या आपके पास यह साइट एक्सेस करने की अनुमति है.</translation>
+<translation id="5572851009514199876">कृपया Chrome शुरू करके उसमें साइन इन करें ताकि Chrome देख सके कि क्या आपके पास यह साइट एक्सेस करने की अनुमति है.</translation>
<translation id="5580958916614886209">अपना समाप्ति माह जाँचें और फिर से कोशिश करें</translation>
<translation id="5586446728396275693">कोई सहेजा गया पता नहीं है</translation>
<translation id="5595485650161345191">पता संपादित करें</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">ईमेल</translation>
+<translation id="5666899935841546222">क्या आप अपने सभी कार्ड एक ही जगह पर रखना चाहते हैं?</translation>
<translation id="5675650730144413517">यह पेज काम नहीं कर रहा है</translation>
<translation id="5685654322157854305">शिपिंग पता जोड़ें</translation>
<translation id="5689199277474810259">JSON में निर्यात करें</translation>
<translation id="5689516760719285838">स्थान</translation>
<translation id="570530837424789914">प्रबंधित करें...</translation>
+<translation id="57094364128775171">मज़बूत पासवर्ड सुझाएं…</translation>
<translation id="5710435578057952990">इस वेबसाइट की पहचान सत्यापित नहीं की गई है.</translation>
<translation id="5719499550583120431">प्रीपेड कार्ड स्वीकार किए जाते हैं.</translation>
<translation id="5720705177508910913">वर्तमान उपयोगकर्ता</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">इस साइट तक नहीं पहुंचा जा सकता</translation>
<translation id="5869522115854928033">सहेजे गए पासवर्ड</translation>
<translation id="5893752035575986141">क्रेडिट कार्ड स्वीकार किए जाते हैं.</translation>
-<translation id="5898382028489516745">अगर आपने अपने <ph name="ORG_NAME" /> पासवर्ड का दूसरी साइटों पर दोबारा इस्तेमाल किया है, तो क्रोमियम आपको उसे रीसेट करने का सुझाव देता है.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (सिंक किया गया)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 उपयोग में है}one{# उपयोग में हैं}other{# उपयोग में हैं}}</translation>
<translation id="5939518447894949180">रीसेट करें</translation>
-<translation id="5959728338436674663">Google को कुछ <ph name="BEGIN_WHITEPAPER_LINK" />सिस्टम संबंधी जानकारी और पेज सामग्री<ph name="END_WHITEPAPER_LINK" /> अपने आप भेजें ताकि खतरनाक ऐप्लिकेशन और साइटों का पता लगाने में सहायता मिल सके. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">संपर्क जानकारी में बदलाव करें</translation>
<translation id="5967867314010545767">इतिहास से निकालें</translation>
<translation id="5975083100439434680">ज़ूम आउट</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">डाक कोड</translation>
<translation id="6290238015253830360">आपके सुझाए गए लेख यहां दिखाई देते हैं</translation>
<translation id="6305205051461490394"><ph name="URL" /> तक नहीं पहुंचा जा सकता.</translation>
-<translation id="6319915415804115995">पिछली बार एक वर्ष से पहले उपयोग किया गया</translation>
<translation id="6321917430147971392">अपनी DNS सेटिंग जाँचें</translation>
<translation id="6328639280570009161">नेटवर्क पूर्वानुमान को अक्षम करके देखें</translation>
<translation id="6328786501058569169">यह साइट भ्रामक है</translation>
<translation id="6337133576188860026"><ph name="SIZE" /> से कम जगह खाली करता है. जब आप अगली बार विज़िट करेंगे तो, कुछ साइटें और धीमे लोड हो सकती हैं.</translation>
<translation id="6337534724793800597">नाम के अनुसार नीतियां फ़िल्टर करें</translation>
-<translation id="6342069812937806050">अभी</translation>
<translation id="6355080345576803305">सार्वजनिक सत्र ओवरराइड</translation>
<translation id="6358450015545214790">इनका क्‍या अर्थ है?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 अन्‍य सुझाव}one{# अन्‍य सुझाव}other{# अन्‍य सुझाव}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;हटाना फिर से करें</translation>
<translation id="6534179046333460208">जीता-जागता वेब के सुझाव</translation>
<translation id="6550675742724504774">विकल्प</translation>
-<translation id="6556915248009097796">खत्म होने की तारीख: <ph name="EXPIRATION_DATE_ABBR" />, पिछली बार <ph name="LAST_USED_DATE_NO_DETAIL" /> को उपयोग किया गया</translation>
<translation id="6563469144985748109">आपके प्रबंधक ने अभी तक इसकी स्वीकृति नहीं दी है</translation>
<translation id="6569060085658103619">आप एक एक्सटेंशन पेज देख रहे हैं</translation>
<translation id="6596325263575161958">सुरक्षित तरीका विकल्प</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">आपने <ph name="DOMAIN" /> तक पहुंचने की कोशिश की थी, लेकिन सर्वर ने कमज़ोर हस्ताक्षर एल्गोरिदम (जैसे कि SHA-1) वाला प्रमाणपत्र प्रस्तुत किया. इसका मतलब है कि सर्वर की ओर से प्रस्तुत किए गए सुरक्षा क्रेडेंशियल नकली हो सकते हैं और हो सकता है कि सर्वर आपका अपेक्षित सर्वर न हो (हो सकता है कि आप किसी हमलावर से बातचीत कर रहे हों).</translation>
<translation id="6831043979455480757">अनुवाद करें</translation>
<translation id="6839929833149231406">क्षेत्र</translation>
+<translation id="6852204201400771460">ऐप्लिकेशन फिर लोड करें?</translation>
+<translation id="6865412394715372076">इस कार्ड की पुष्टि अभी नहीं की जा सकती</translation>
<translation id="6874604403660855544">&amp;जोड़ना फिर से करें</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">नीति स्तर समर्थित नहीं है.</translation>
<translation id="6895330447102777224">आपके कार्ड की पुष्टि हो गई है</translation>
<translation id="6897140037006041989">उपयोगकर्ता एजेंट</translation>
+<translation id="6903319715792422884">'सुरक्षित ब्राउज़िंग' को बेहतर बनाने में मदद करने के लिए Google को कुछ <ph name="BEGIN_WHITEPAPER_LINK" />'सिस्टम जानकारी' और 'पेज सामग्री'<ph name="END_WHITEPAPER_LINK" /> भेजें. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">उपयोगकर्ता:</translation>
+<translation id="6944692733090228304">आपने एक ऐसी साइट पर अपना पासवर्ड डाला है जिसे <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> प्रबंधित नहीं करता है. अपने खाते को सुरक्षित रखने के लिए, दूसरे ऐप्लिकेशन और साइटों पर अपने पासवर्ड का दोबारा इस्तेमाल नहीं करें.</translation>
<translation id="6945221475159498467">चुनें</translation>
<translation id="6948701128805548767">पिकअप के तरीके और ज़रूरतें देखने के लिए, कोई पता चुनें</translation>
<translation id="6949872517221025916">पासवर्ड रीसेट करें</translation>
+<translation id="6950684638814147129">JSON मान पार्स करते समय गड़बड़ी हुई: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">सर्वर का प्रमाणपत्र फर्जी दिखाई देता है.</translation>
<translation id="6965382102122355670">ठीक है</translation>
<translation id="6965978654500191972">डिवाइस</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;&lt;strong&gt;लागू करें&lt;/strong&gt; पर क्लिक करें, फिर &lt;strong&gt;ठीक है&lt;/strong&gt; पर क्लिक करें
&lt;li&gt;&lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome सहायता केंद्र&lt;/a&gt; पर जाकर अपने कंप्यूटर से सॉफ़्टवेयर को हमेशा के लिए हटाने का तरीका जानें
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">पासवर्ड प्रबंधित करें…</translation>
<translation id="7419106976560586862">प्रोफ़ाइल पथ</translation>
<translation id="7437289804838430631">संपर्क जानकारी जोड़ें</translation>
<translation id="7441627299479586546">गलत नीति विषय</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790">इस समस्या के बारे में <ph name="BEGIN_LINK" />अधिक जानें<ph name="END_LINK" />.</translation>
<translation id="7455133967321480974">वैश्विक डिफ़ॉल्ट का उपयोग करें (अवरोधित करें)</translation>
<translation id="7460163899615895653">आपके अन्य डिवाइस के हाल ही के टैब यहां दिखाई देंगे</translation>
-<translation id="7469372306589899959">कार्ड की पुष्टि की जा रही है</translation>
<translation id="7473891865547856676">जी रहने दें</translation>
<translation id="7481312909269577407">आगे जाएं</translation>
<translation id="7485870689360869515">कोई डेटा नहीं मिला</translation>
@@ -1006,7 +1005,7 @@
<translation id="825929999321470778">सेव किए गए सभी पासवर्ड दिखाएं</translation>
<translation id="8261506727792406068">हटाएं</translation>
<translation id="8267698848189296333"><ph name="USERNAME" /> के रूप में प्रवेश करना</translation>
-<translation id="8286036467436129157">प्रवेश करें</translation>
+<translation id="8286036467436129157">साइन इन करें</translation>
<translation id="8288807391153049143">प्रमाणपत्र दिखाएं</translation>
<translation id="8289355894181816810">यदि आप सुनिश्चित नहीं हैं कि इसका क्या मतलब है, तो अपने नेटवर्क व्यवस्थापक से संपर्क करें.</translation>
<translation id="8293206222192510085">बुकमार्क जोड़ें</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">नेटवर्क कनेक्शन में कोई समस्या होने के कारण अनुवाद विफल हुआ.</translation>
<translation id="8311129316111205805">सत्र लोड करें</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> का एक्सेस अस्वीकृत किया गया</translation>
+<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="8349305172487531364">बुकमार्क बार</translation>
<translation id="8363502534493474904">हवाई जहाज़ मोड बंद करें</translation>
@@ -1035,21 +1035,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> को लोड होने में बहुत ज़्यादा समय लगा.</translation>
<translation id="8503559462189395349">Chrome पासवर्ड</translation>
<translation id="8503813439785031346">उपयोगकर्ता नाम</translation>
+<translation id="8508648098325802031">खोज अाइकॉन</translation>
<translation id="8543181531796978784">आप <ph name="BEGIN_ERROR_LINK" />पहचान संबंधी समस्‍या की रिपोर्ट<ph name="END_ERROR_LINK" /> कर सकते हैं या अगर आप अपनी सुरक्षा संबंधी जोखिमों को समझते हैं तो, <ph name="BEGIN_LINK" />इस असुरक्षित साइट पर जा<ph name="END_LINK" /> सकते हैं.</translation>
<translation id="8543556556237226809">प्रश्न पूछना चाहते हैं? तो उस व्यक्ति से संपर्क करें जो आपकी प्रोफ़ाइल की निगरानी करता है.</translation>
<translation id="8553075262323480129">अनुवाद विफल हो गया क्योंकि पेज की भाषा निर्धारित नहीं की जा सकी.</translation>
<translation id="8557066899867184262">कार्ड वेरीफ़िकेशन कोड (सीवीसी) आपके कार्ड के पीछे मौजूद होता है.</translation>
<translation id="8559762987265718583"><ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> से एक निजी कनेक्‍शन स्‍थापित नहीं किया जा सकता क्‍योंकि आपके डिवाइस का दिनांक और समय (<ph name="DATE_AND_TIME" />) गलत है.</translation>
+<translation id="8564985650692024650">अगर आपने अपने <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> पासवर्ड का दूसरी साइटों पर दोबारा इस्तेमाल किया है, तो क्रोमियम आपको उसे रीसेट करने का सुझाव देता है.</translation>
<translation id="8571890674111243710">पेज का अनुवाद <ph name="LANGUAGE" /> में कर रहा है...</translation>
<translation id="858637041960032120">फ़ोन नंबर जोड़ें
</translation>
<translation id="859285277496340001">प्रमाणपत्र यह जाँचने के लिए कोई तंत्र निर्दिष्‍ट नहीं करता कि इसे रद्द कर दिया गया है या नहीं.</translation>
+<translation id="860043288473659153">कार्डधारक का नाम</translation>
<translation id="8620436878122366504">आपके अभिभावकों ने अभी तक इसकी स्वीकृति नहीं दी है</translation>
<translation id="8625384913736129811">यह कार्ड इस डिवाइस पर सेव करें</translation>
-<translation id="8639963783467694461">ऑटोमैटिक भरना की सेटिंग...</translation>
+<translation id="8663226718884576429">ऑर्डर की खास बातें, <ph name="TOTAL_LABEL" />, ज़्यादा जानकारी</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, उत्तर, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> से आपके कनेक्शन को एन्क्रिप्ट नहीं किया गया है.</translation>
<translation id="8718314106902482036">भुगतान पूरा नहीं हुआ</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, खोज सुझाव</translation>
<translation id="8725066075913043281">पुन: प्रयास करें</translation>
<translation id="8728672262656704056">आप गुप्त मोड में चले गए हैं</translation>
<translation id="8730621377337864115">पूर्ण</translation>
@@ -1065,8 +1069,10 @@
<translation id="8820817407110198400">बुकमार्क</translation>
<translation id="883848425547221593">अन्य बुकमार्क</translation>
<translation id="884264119367021077">शिपिंग पता</translation>
+<translation id="8846319957959474018">बुकमार्क के ज़रिए आसानी से ऐप्लिकेशन खोलें</translation>
<translation id="884923133447025588">कोई निरस्तीकरण प्रक्रिया प्राप्त नहीं हुई.</translation>
<translation id="885730110891505394">Google के साथ शेयर करना</translation>
+<translation id="8858065207712248076">अगर आपने अपने <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> पासवर्ड का दूसरी साइटों पर दोबारा इस्तेमाल किया है, तो Chrome आपको उसे रीसेट करने का सुझाव देता है.</translation>
<translation id="8866481888320382733">नीति सेटिंग पार्स करने में गड़बड़ी</translation>
<translation id="8870413625673593573">हाल ही में बंद किए गए</translation>
<translation id="8874824191258364635">मान्य कार्ड संख्या डालें</translation>
@@ -1102,12 +1108,13 @@
<translation id="9076283476770535406">इसमें वयस्क सामग्री हो सकती है</translation>
<translation id="9078964945751709336">अधिक जानकारी की आवश्यकता है</translation>
<translation id="9080712759204168376">आदेश सारांश</translation>
-<translation id="9103872766612412690"><ph name="SITE" /> आपकी जानकारी की सुरक्षा करने के लिए आमतौर पर एन्क्रिप्शन का उपयोग करती है. जब क्रोमियम ने इस बार <ph name="SITE" /> से कनेक्ट करने का प्रयास किया, तो वेबसाइट ने असामान्य और गलत क्रेडेंशियल वापस भेजे. ऐसा तब हो सकता है जब कोई हमलावर <ph name="SITE" /> होने का दावा करने का प्रयास कर रहा हो या किसी वाई-फ़ाई प्रवेश स्क्रीन ने कनेक्शन को बाधित कर दिया हो. आपकी जानकारी अभी भी सुरक्षित है क्योंकि किसी भी डेटा के आदान-प्रदान से पहले ही क्रोमियम ने कनेक्शन को रोक दिया था.</translation>
+<translation id="9103872766612412690"><ph name="SITE" /> आपकी जानकारी की सुरक्षा करने के लिए आमतौर पर एन्क्रिप्शन का उपयोग करती है. जब क्रोमियम ने इस बार <ph name="SITE" /> से कनेक्ट करने का प्रयास किया, तो वेबसाइट ने असामान्य और गलत क्रेडेंशियल वापस भेजे. ऐसा तब हो सकता है जब कोई हमलावर <ph name="SITE" /> होने का दिखाना करने की कोशिश कर रहा हो या किसी वाई-फ़ाई साइन इन स्क्रीन ने कनेक्शन को बाधित कर दिया हो. आपकी जानकारी अभी भी सुरक्षित है क्योंकि किसी भी डेटा के लेन-देन से पहले ही क्रोमियम ने कनेक्शन को रोक दिया था.</translation>
<translation id="9106062320799175032">बिलिंग पता जोड़ें</translation>
<translation id="910908805481542201">इसे ठीक करने में मेरी सहायता करें</translation>
+<translation id="9114524666733003316">कार्ड की पुष्टि की जा रही है...</translation>
<translation id="9128870381267983090">नेटवर्क से कनेक्ट करें</translation>
<translation id="9137013805542155359">मूल दिखाएं</translation>
-<translation id="9137248913990643158">इस ऐप्लिकेशन का उपयोग करने से पहले कृपया Chrome शुरू करके उसमें प्रवेश करें.</translation>
+<translation id="9137248913990643158">इस ऐप्लिकेशन का उपयोग करने से पहले कृपया Chrome शुरू करके उसमें साइन इन करें.</translation>
<translation id="9148088599418889305">शिपिंग का तरीका चुनें</translation>
<translation id="9148507642005240123">&amp;संपादन वापस लाएं</translation>
<translation id="9154194610265714752">अपडेट किया गया</translation>
@@ -1115,6 +1122,7 @@
<translation id="9168814207360376865">साइट को यह देखने दें कि आपके 'भुगतान के तरीके' सेव किए गए हैं या नहीं</translation>
<translation id="9169664750068251925">इस साइट पर हमेशा अवरोधित करें</translation>
<translation id="9170848237812810038">&amp;पूर्ववत् करें</translation>
+<translation id="9171296965991013597">ऐप्लिकेशन बंद करें?</translation>
<translation id="917450738466192189">सर्वर का प्रमाणपत्र अमान्य है.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> एक असमर्थित प्रोटोकॉल का उपयोग करता है.</translation>
<translation id="9205078245616868884">आपका डेटा आपके समन्वयन पासफ़्रेज़ के साथ एन्क्रिप्ट किया गया है. समन्वयन शुरू करने के लिए इसे डालें.</translation>
diff --git a/chromium/components/strings/components_strings_hr.xtb b/chromium/components/strings/components_strings_hr.xtb
index b767b3c0d34..8ed829ed72c 100644
--- a/chromium/components/strings/components_strings_hr.xtb
+++ b/chromium/components/strings/components_strings_hr.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Skrivanje vrijednosti</translation>
<translation id="1228893227497259893">Pogrešan identifikator entiteta</translation>
<translation id="1232569758102978740">Neimenovano</translation>
+<translation id="1250759482327835220">Da biste sljedeći put platili brže, spremite karticu, ime i adresu za naplatu na svoj Google račun.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (sinkronizirano)</translation>
<translation id="1256368399071562588">&lt;p&gt;Ako pokušate otvoriti web-lokaciju i ona se ne otvara, najprije pokušajte riješiti poteškoću prema ovim uputama:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Prilagodite datum i vrijeme putem odjeljka &lt;strong&gt;Općenito&lt;/strong&gt; u aplikaciji &lt;strong&gt;Postavke&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Nešto nije u redu s prikazivanjem ove web-stranice.</translation>
-<translation id="1590457302292452960">Generirajte snažnu zaporku...</translation>
<translation id="1592005682883173041">Pristup lokalnim podacima</translation>
<translation id="1594030484168838125">Odaberi</translation>
<translation id="1620510694547887537">Fotoaparat</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Pokušajte kontaktirati administratora sustava.</translation>
<translation id="1740951997222943430">Unesite važeći mjesec isteka</translation>
+<translation id="1743520634839655729">Da biste sljedeći put platili brže, spremite karticu, ime i adresu za naplatu na svoj Google račun i ovaj uređaj.</translation>
<translation id="17513872634828108">Otvorene kartice</translation>
<translation id="1753706481035618306">Broj stranice</translation>
<translation id="1763864636252898013">Poslužitelj nije mogao dokazati da je <ph name="DOMAIN" />; operativni sustav vašeg uređaja smatra da njegov sigurnosni certifikat nije pouzdan. To može biti uzrokovano pogrešnom konfiguracijom ili napadom na vašu vezu.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Ovdje se prikazuju vaše otvorene kartice</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Ime nositelja kartice</translation>
-<translation id="1806541873155184440">Dodano <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Nevažeći zahtjev ili parametri zahtjeva</translation>
<translation id="1826516787628120939">Provjera</translation>
<translation id="1834321415901700177">Ova web-lokacija sadrži štetne programe</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 prijedlog}one{# prijedlog}few{# prijedloga}other{# prijedloga}}</translation>
<translation id="2079545284768500474">Poništi</translation>
<translation id="20817612488360358">Postavljena je upotreba sistemskih postavki proxy poslužitelja, ali također je određena izričita konfiguracija proxy poslužitelja.</translation>
-<translation id="2084558088529668945">Unijeli ste zaporku na stranicu kojom ne upravlja <ph name="ORG_NAME" />. Da biste zaštitili račun, nemojte upotrebljavati tu zaporku za druge aplikacije i web-lokacije.</translation>
<translation id="2091887806945687916">Zvuk</translation>
<translation id="2094505752054353250">Domena se ne podudara</translation>
<translation id="2096368010154057602">Departman</translation>
+<translation id="2102134110707549001">Predloži snažnu zaporku…</translation>
<translation id="2108755909498034140">Ponovo pokrenite računalo</translation>
<translation id="2113977810652731515">Kartica</translation>
<translation id="2114841414352855701">Zanemareno jer je nadjačano pravilom <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP pogreška</translation>
<translation id="2270484714375784793">Telefonski broj</translation>
<translation id="2292556288342944218">Internetski je pristup blokiran</translation>
-<translation id="230155334948463882">Nova kartica?</translation>
<translation id="2316887270356262533">Oslobodit će se manje od 1 MB. Neke bi se web-lokacije pri sljedećem otvaranju mogle sporije učitavati.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> zahtijeva korisničko ime i zaporku.</translation>
<translation id="2317583587496011522">Prihvaćaju se debitne kartice.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Prihvaćene kartice</translation>
<translation id="2702801445560668637">Popis za čitanje</translation>
<translation id="2704283930420550640">Vrijednost ne odgovara formatu.</translation>
-<translation id="2704951214193499422">Chromium nije uspio potvrditi vašu karticu. Pokušajte ponovo kasnije.</translation>
<translation id="2705137772291741111">Spremljena (predmemorirana) kopija web-lokacije nije čitljiva.</translation>
<translation id="2709516037105925701">Automatsko popunjavanje</translation>
<translation id="2710942282213947212">Softver na računalu sprječava sigurno povezivanje Chromea s webom</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Način dostave</translation>
<translation id="277499241957683684">Zapis uređaja nije prisutan</translation>
<translation id="2781030394888168909">Izvezi za MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Veza je ponovo uspostavljena.</translation>
<translation id="2788784517760473862">Prihvaćene kreditne kartice</translation>
<translation id="2794233252405721443">Web-lokacija blokirana</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Možete onemogućiti sve proxyje konfigurirane za vezu sa stranice postavki.</translation>
<translation id="2955913368246107853">Zatvori traku za traženje</translation>
<translation id="2958431318199492670">Mrežna konfiguracija nije u skladu sa standardima ONC. Dijelove konfiguracije nije moguće uvesti.</translation>
-<translation id="2966678944701946121">Istek: <ph name="EXPIRATION_DATE_ABBR" />, dodano <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Za uspostavu sigurne veze sat mora biti točno postavljen jer certifikati pomoću kojih se web-lokacije međusobno identificiraju vrijede samo određeno vrijeme. Budući da vaš sat nije točan, Chrome ne može potvrditi te certifikate.</translation>
<translation id="2972581237482394796">&amp;Vrati poništeno</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, trenutačno odabrano. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Pogrešna vrsta pravila</translation>
<translation id="3037605927509011580">O, ne!</translation>
<translation id="3041612393474885105">Podaci o certifikatu</translation>
-<translation id="3063697135517575841">Chrome nije uspio potvrditi vašu karticu. Pokušajte ponovo kasnije.</translation>
<translation id="3064966200440839136">Napuštate anonimni način rada da biste platili putem vanjske aplikacije. Želite li nastaviti?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Nijedna}=1{1 zaporka}one{# zaporka}few{# zaporke}other{# zaporki}}</translation>
<translation id="3096100844101284527">Dodajte adresu preuzimanja</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Vaši su podaci šifrirani vašom šifrom za sinkronizaciju <ph name="TIME" />. Unesite je da biste pokrenuli sinkronizaciju.</translation>
<translation id="3320021301628644560">Dodajte adresu za naplatu</translation>
<translation id="3338095232262050444">Sigurno</translation>
-<translation id="3340978935015468852">postavke</translation>
<translation id="3345135638360864351">Slanje zahtjeva za pristup ovoj web-lokaciji korisniku <ph name="NAME" /> nije uspjelo. Pokušaj ponovo.</translation>
<translation id="3355823806454867987">Promijeni proxy postavke...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />neće spremati<ph name="END_EMPHASIS" /> sljedeće informacije:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Certifikat poslužitelja nije pouzdan.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Najmanje 1 stavka na sinkroniziranim uređajima}=1{1 stavka (i više njih na sinkroniziranim uređajima)}one{# stavka (i više njih na sinkroniziranim uređajima)}few{# stavke (i više njih na sinkroniziranim uređajima)}other{# stavki (i više njih na sinkroniziranim uređajima)}}</translation>
<translation id="3539171420378717834">Zadrži kopiju te kartice na uređaju</translation>
-<translation id="3542684924769048008">Upotrijebite zaporku za:</translation>
<translation id="3549644494707163724">Šifriranje svih sinkroniziranih podataka vlastitom zaporkom za sinkronizaciju</translation>
<translation id="3556433843310711081">Voditelj je može deblokirati</translation>
<translation id="3566021033012934673">Vaša veza nije privatna</translation>
@@ -460,7 +455,6 @@
<translation id="4171400957073367226">Potpis za potvrdu nije ispravan.</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{Još <ph name="ITEM_COUNT" /> stavka}one{Još <ph name="ITEM_COUNT" /> stavka}few{Još <ph name="ITEM_COUNT" /> stavke}other{Još <ph name="ITEM_COUNT" /> stavki}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome preporučuje poništavanje zaporke za <ph name="ORG_NAME" /> ako ste je upotrebljavali za druge web-lokacije.</translation>
<translation id="4196861286325780578">&amp;Ponovi premještanje</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />provjerite vatrozid i konfiguraciju antivirusnog programa<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Padovi programa</translation>
@@ -489,7 +483,6 @@
<translation id="425582637250725228">Unesene promjene ne mogu se spremiti.</translation>
<translation id="4258748452823770588">Potpis nije valjan</translation>
<translation id="4265872034478892965">Dopustio administrator</translation>
-<translation id="4269787794583293679">(Nema korisničkog imena)</translation>
<translation id="4275830172053184480">Ponovo pokrenite svoj uređaj</translation>
<translation id="4277028893293644418">Poništi zaporku</translation>
<translation id="4280429058323657511">, istek <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -512,6 +505,7 @@
<translation id="4434045419905280838">Skočni prozori i preusmjeravanja</translation>
<translation id="443673843213245140">Upotreba proxy poslužitelja onemogućena je, ali određena je izričita konfiguracija proxy poslužitelja.</translation>
<translation id="445100540951337728">Prihvaćene debitne kartice</translation>
+<translation id="4472575034687746823">Početak korištenja</translation>
<translation id="4506176782989081258">Pogreška pri provjeri valjanosti: <ph name="VALIDATION_ERROR" />.</translation>
<translation id="4506599922270137252">kontaktirajte administratora sustava</translation>
<translation id="450710068430902550">Dijeljenje s administratorom</translation>
@@ -536,8 +530,8 @@
<translation id="4726672564094551039">Ponovo učitaj pravila</translation>
<translation id="4728558894243024398">Platforma</translation>
<translation id="4736825316280949806">Ponovo pokrenite Chromium</translation>
+<translation id="4742407542027196863">Upravljaj zaporkama…</translation>
<translation id="4744603770635761495">Izvršna putanja</translation>
-<translation id="4749685221585524849">Posljednja upotreba <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Vaši podaci (na primjer, zaporke i brojevi kreditnih kartica) privatni su kada se šalju na tu web-lokaciju.</translation>
<translation id="4756388243121344051">&amp;Povijest</translation>
<translation id="4758311279753947758">Dodaj pod. za kontakt</translation>
@@ -554,6 +548,7 @@
<translation id="4850886885716139402">Prikaz</translation>
<translation id="4854362297993841467">Taj način dostave nije dostupan. Pokušajte s nekim drugim načinom.</translation>
<translation id="4858792381671956233">Pitao si roditelje smiješ li otvoriti tu web-lokaciju</translation>
+<translation id="4876305945144899064">Nema korisničkog imena</translation>
<translation id="4880827082731008257">Pretraži povijest</translation>
<translation id="4881695831933465202">Otvori</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -587,6 +582,7 @@
<translation id="5089810972385038852">Država</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">Provincija</translation>
+<translation id="5098332213681597508">To je ime s vašeg Google računa.</translation>
<translation id="5115563688576182185">(64-bitni)</translation>
<translation id="5121084798328133320">Nakon što ih potvrdite, podaci o kartici s računa usluge Google podijelit će se s ovom web-lokacijom.</translation>
<translation id="5128122789703661928">Brisanje sesije nije uspjelo jer naziv sesije nije važeći.</translation>
@@ -627,9 +623,10 @@
<translation id="5332219387342487447">Način otpreme</translation>
<translation id="5355557959165512791">Trenutačno ne možete otvoriti web-lokaciju <ph name="SITE" /> jer je njezin certifikat opozvan. Mrežne pogreške i napadi uglavnom su privremeni i ta bi stranica kasnije trebala funkcionirati.</translation>
<translation id="536296301121032821">Pohrana postavki pravila nije uspjela</translation>
+<translation id="5371425731340848620">Ažuriranje kartice</translation>
<translation id="5377026284221673050">"Sat kasni" ili "Sat ide unaprijed" ili "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">Lanac certifikata za ovu web-lokaciju sadrži certifikat s SHA-1 potpisom.</translation>
-<translation id="5402410679244714488">Istek: <ph name="EXPIRATION_DATE_ABBR" />, posljednja upotreba prije više od godinu dana</translation>
+<translation id="5387961145478138773">Brzo pristupajte svojim omiljenim Googleovim aplikacijama</translation>
<translation id="540969355065856584">Poslužitelj nije uspio dokazati da je <ph name="DOMAIN" />; njegov sigurnosni certifikat trenutačno nije važeći. Uzrok tomu može biti pogrešna konfiguracija ili napadač koji je prekinuo vašu vezu.</translation>
<translation id="5421136146218899937">Obriši podatke pregledavanja...</translation>
<translation id="5430298929874300616">Ukloni oznaku</translation>
@@ -671,11 +668,13 @@
<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="5659593005791499971">E-pošta</translation>
+<translation id="5666899935841546222">Želite li da sve vaše kartice budu na jednom mjestu?</translation>
<translation id="5675650730144413517">Stranica ne funkcionira</translation>
<translation id="5685654322157854305">Dodajte adresu za dostavu</translation>
<translation id="5689199277474810259">Izvezi u JSON</translation>
<translation id="5689516760719285838">Lokacija</translation>
<translation id="570530837424789914">Upravljajte...</translation>
+<translation id="57094364128775171">Predloži snažnu zaporku…</translation>
<translation id="5710435578057952990">Identitet ove web lokacije nije ovjeren.</translation>
<translation id="5719499550583120431">Prihvaćaju se pretplatne kartice.</translation>
<translation id="5720705177508910913">Trenutačni korisnik:</translation>
@@ -696,11 +695,9 @@
<translation id="5869405914158311789">Web-lokacija ne može se dohvatiti</translation>
<translation id="5869522115854928033">Spremljene zaporke</translation>
<translation id="5893752035575986141">Prihvaćaju se kreditne kartice.</translation>
-<translation id="5898382028489516745">Chromium preporučuje poništavanje zaporke za <ph name="ORG_NAME" /> ako ste je upotrebljavali za druge web-lokacije.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (sinkronizirano)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 u upotrebi}one{# u upotrebi}few{# u upotrebi}other{# u upotrebi}}</translation>
<translation id="5939518447894949180">Ponovno postavi</translation>
-<translation id="5959728338436674663">Automatski šalji Googleu neke <ph name="BEGIN_WHITEPAPER_LINK" />podatke o sustavu i sadržaj stranice<ph name="END_WHITEPAPER_LINK" /> radi otkrivanja opasnih aplikacija i web-lokacija. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Uredite podatke za kontakt</translation>
<translation id="5967867314010545767">Ukloni iz povijesti</translation>
<translation id="5975083100439434680">Smanji</translation>
@@ -746,13 +743,11 @@
<translation id="6282194474023008486">Poštanski broj</translation>
<translation id="6290238015253830360">Ovdje će se prikazivati predloženi članci</translation>
<translation id="6305205051461490394">Web-lokacija <ph name="URL" /> nije dostupna.</translation>
-<translation id="6319915415804115995">Posljednja upotreba prije više od godinu dana</translation>
<translation id="6321917430147971392">Provjerite postavke DNS-a</translation>
<translation id="6328639280570009161">Pokušajte onemogućiti predviđanja mreže</translation>
<translation id="6328786501058569169">Ova je web-lokacija obmanjujuća</translation>
<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="6342069812937806050">Samo sad</translation>
<translation id="6355080345576803305">Nadjačavanje javne sesije</translation>
<translation id="6358450015545214790">Što to znači?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 drugi prijedlog}one{# drugi prijedlog}few{# druga prijedloga}other{# drugih prijedloga}}</translation>
@@ -777,7 +772,6 @@
<translation id="6529602333819889595">&amp;Ponovi brisanje</translation>
<translation id="6534179046333460208">Prijedlozi Fizičkog weba</translation>
<translation id="6550675742724504774">Opcije</translation>
-<translation id="6556915248009097796">Istek: <ph name="EXPIRATION_DATE_ABBR" />, posljednja upotreba <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Voditelj je još nije odobrio</translation>
<translation id="6569060085658103619">Gledate stranicu proširenja</translation>
<translation id="6596325263575161958">Opcije šifriranja</translation>
@@ -806,15 +800,20 @@
<translation id="6825578344716086703">Pokušali ste doseći domenu <ph name="DOMAIN" />, ali poslužitelj je predstavio certifikat potpisan slabim algoritmom potpisa (kao što je SHA-1). Znači da su sigurnosne vjerodajnice koje je poslužitelj predstavio možda krivotvorene, a poslužitelj možda nije poslužitelj koji očekujete (možda ste u komunikaciji s napadačem).</translation>
<translation id="6831043979455480757">Prevedi</translation>
<translation id="6839929833149231406">Područje</translation>
+<translation id="6852204201400771460">Ponovo učitati aplikaciju?</translation>
+<translation id="6865412394715372076">Trenutačno nije moguće potvrditi karticu</translation>
<translation id="6874604403660855544">&amp;Ponovi dodavanje</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Razina pravila nije podržana.</translation>
<translation id="6895330447102777224">Kartica je potvrđena</translation>
<translation id="6897140037006041989">Korisnički agent</translation>
+<translation id="6903319715792422884">Pomognite poboljšati Sigurno pregledavanje tako što ćete Googleu slati neke <ph name="BEGIN_WHITEPAPER_LINK" />podatke o sustavu i sadržaj web-stranice<ph name="END_WHITEPAPER_LINK" />. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Korisnik:</translation>
+<translation id="6944692733090228304">Unijeli ste zaporku na web-lokaciju kojom ne upravlja <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Da biste zaštitili račun, nemojte upotrebljavati tu zaporku za druge aplikacije i web-lokacije.</translation>
<translation id="6945221475159498467">Odaberi</translation>
<translation id="6948701128805548767">Odaberite adresu za prikaz načina preuzimanja i zahtjeva za preuzimanje</translation>
<translation id="6949872517221025916">Poništite zaporku</translation>
+<translation id="6950684638814147129">Došlo je do pogreške prilikom raščlanjivanja vrijednosti JSON-a: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Certifikat poslužitelja izgleda kao falsifikat.</translation>
<translation id="6965382102122355670">U redu</translation>
<translation id="6965978654500191972">Uređaj</translation>
@@ -876,6 +875,7 @@
&lt;li&gt;Kliknite &lt;strong&gt;Primijeni&lt;/strong&gt;, a zatim kliknite &lt;strong&gt;U redu&lt;/strong&gt;
&lt;li&gt;U &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;centru za pomoć za Chrome&lt;/a&gt; možete saznati kako trajno ukloniti softver s računala
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Upravljaj zaporkama…</translation>
<translation id="7419106976560586862">Putanja profila</translation>
<translation id="7437289804838430631">Dodajte podatke za kontakt</translation>
<translation id="7441627299479586546">Pogrešan predmet pravila</translation>
@@ -884,7 +884,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Saznajte više<ph name="END_LINK" /> o tom problemu.</translation>
<translation id="7455133967321480974">Upotrijebi globalnu zadanu vrijednost (blokiraj)</translation>
<translation id="7460163899615895653">Ovdje se prikazuju vaše nedavne kartice s drugih uređaja</translation>
-<translation id="7469372306589899959">Potvrđivanje kartice</translation>
<translation id="7473891865547856676">Ne, hvala</translation>
<translation id="7481312909269577407">Naprijed</translation>
<translation id="7485870689360869515">Nema pronađenih podataka.</translation>
@@ -1014,6 +1013,7 @@
<translation id="8308427013383895095">Prijevod nije uspio zbog problema s mrežnom vezom.</translation>
<translation id="8311129316111205805">Učitaj sesiju</translation>
<translation id="8332188693563227489">Pristup hostu <ph name="HOST_NAME" /> je odbijen</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Ako ste svjesni sigurnosnih rizika, možete <ph name="BEGIN_LINK" />posjetiti ovu web-lokaciju<ph name="END_LINK" /> prije uklanjanja štetnih programa.</translation>
<translation id="8349305172487531364">Traka oznaka</translation>
<translation id="8363502534493474904">isključite način rada u zrakoplovu</translation>
@@ -1034,21 +1034,25 @@
<translation id="8498891568109133222">Hostu <ph name="HOST_NAME" /> bilo je potrebno previše vremena za odgovor.</translation>
<translation id="8503559462189395349">Zaporke preglednika Chrome</translation>
<translation id="8503813439785031346">Korisničko ime</translation>
+<translation id="8508648098325802031">Ikona pretraživanja</translation>
<translation id="8543181531796978784">Možete <ph name="BEGIN_ERROR_LINK" />prijaviti problem s otkrivanjem<ph name="END_ERROR_LINK" /> ili, ako razumijete na koje je načine ugrožena vaša sigurnost, <ph name="BEGIN_LINK" />posjetite nesigurnu web-lokaciju<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Imate li pitanja? Obratite se osobi koja nadzire vaš profil.</translation>
<translation id="8553075262323480129">Prijevod nije uspio jer nije bilo moguće odrediti jezik stranice.</translation>
<translation id="8557066899867184262">CVC možete pronaći na poleđini kartice.</translation>
<translation id="8559762987265718583">Sigurnu vezu s domenom <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> nije moguće uspostaviti jer datum i vrijeme (<ph name="DATE_AND_TIME" />) na vašem uređaju nisu točni.</translation>
+<translation id="8564985650692024650">Chromium preporučuje poništavanje zaporke za organizaciju <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> ako ste je upotrebljavali za druge web-lokacije.</translation>
<translation id="8571890674111243710">Prijevod stranice na <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Dodaj tel. broj
</translation>
<translation id="859285277496340001">Certifikat ne navodi mehanizam za provjeru svojeg opoziva.</translation>
+<translation id="860043288473659153">Ime vlasnika kartice</translation>
<translation id="8620436878122366504">Roditelji je još nisu odobrili</translation>
<translation id="8625384913736129811">Spremi tu karticu na ovaj uređaj</translation>
-<translation id="8639963783467694461">Postavke automatske ispune</translation>
+<translation id="8663226718884576429">Sažetak narudžbe, <ph name="TOTAL_LABEL" />, više pojedinosti</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, odgovor, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Vaša veza s <ph name="DOMAIN" /> nije šifrirana.</translation>
<translation id="8718314106902482036">Plaćanje nije dovršeno</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, prijedlog za pretraživanje</translation>
<translation id="8725066075913043281">Pokušajte ponovo</translation>
<translation id="8728672262656704056">Radite u anonimnom načinu</translation>
<translation id="8730621377337864115">Gotovo</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Knjižne oznake</translation>
<translation id="883848425547221593">Druge oznake</translation>
<translation id="884264119367021077">Adresa za dostavu</translation>
+<translation id="8846319957959474018">Jednostavno otvaranje aplikacija pomoću oznaka</translation>
<translation id="884923133447025588">Nije pronađen mehanizam za opoziv.</translation>
<translation id="885730110891505394">Dijeljenje s Googleom</translation>
+<translation id="8858065207712248076">Chrome preporučuje poništavanje zaporke za organizaciju <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> ako ste je upotrebljavali za druge web-lokacije.</translation>
<translation id="8866481888320382733">Pogreška pri analizi postavki pravila</translation>
<translation id="8870413625673593573">Nedavno zatvoreno</translation>
<translation id="8874824191258364635">Unesite važeći broj kreditne kartice</translation>
@@ -1105,6 +1111,7 @@
i netočne vjerodajnice. To može značiti da se neki napadač pokušava predstaviti kao <ph name="SITE" /> ili je zaslon za prijavu na Wi-Fi prekinuo vezu. Vaši su podaci još uvijek sigurni jer je Chromium zaustavio povezivanje prije razmjene podataka.</translation>
<translation id="9106062320799175032">Dodajte adresu za naplatu</translation>
<translation id="910908805481542201">Pomozi mi da to riješim</translation>
+<translation id="9114524666733003316">Potvrđivanje kartice...</translation>
<translation id="9128870381267983090">Povezivanje s mrežom</translation>
<translation id="9137013805542155359">Prikaži original</translation>
<translation id="9137248913990643158">Pokrenite Chrome i prijavite se na njega da biste mogli upotrebljavati tu aplikaciju.</translation>
@@ -1115,6 +1122,7 @@ i netočne vjerodajnice. To može značiti da se neki napadač pokušava predsta
<translation id="9168814207360376865">Dopusti web-lokacijama da provjere imate li spremljene načine plaćanja</translation>
<translation id="9169664750068251925">Uvijek blokiraj na ovoj web-lokaciji</translation>
<translation id="9170848237812810038">&amp;Poništi</translation>
+<translation id="9171296965991013597">Napustiti aplikaciju?</translation>
<translation id="917450738466192189">Certifikat poslužitelja nije valjan.</translation>
<translation id="9183425211371246419">Host <ph name="HOST_NAME" /> upotrebljava nepodržane protokole.</translation>
<translation id="9205078245616868884">Vaši su podaci šifrirani vašom šifrom za sinkronizaciju. Unesite je da biste pokrenuli sinkronizaciju.</translation>
diff --git a/chromium/components/strings/components_strings_hu.xtb b/chromium/components/strings/components_strings_hu.xtb
index fc1ed875b97..1c224c2da7f 100644
--- a/chromium/components/strings/components_strings_hu.xtb
+++ b/chromium/components/strings/components_strings_hu.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Érték elrejtése</translation>
<translation id="1228893227497259893">Helytelen entitásazonosító</translation>
<translation id="1232569758102978740">Névtelen</translation>
+<translation id="1250759482327835220">A következő alkalommal gyorsabban fizethet, ha elmenti kártyáját, nevét és számlázási címét a Google-fiókjába.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (szinkronizálva)</translation>
<translation id="1256368399071562588">&lt;p&gt;Ha nem nyílik meg a felkeresni kívánt webhely, először próbálja meg kijavítani a hibát a következő problémamegoldó lépések végrehajtásával:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Módosítsa a dátumot és az időt a &lt;strong&gt;Beállítások&lt;/strong&gt; alkalmazás &lt;strong&gt;Általános&lt;/strong&gt; részében.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Valami nem sikerült a weboldal megjelenítése során.</translation>
-<translation id="1590457302292452960">Erős jelszó létrehozása…</translation>
<translation id="1592005682883173041">Helyi adatok elérése</translation>
<translation id="1594030484168838125">Kiválaszt</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">American Express</translation>
<translation id="1734878702283171397">Próbálja felvenni a kapcsolatot a rendszergazdával.</translation>
<translation id="1740951997222943430">Érvényes lejárati hónapot kell megadnia</translation>
+<translation id="1743520634839655729">A következő alkalommal gyorsabban fizethet, ha kártyáját, nevét és számlázási címét elmenti Google-fiókjába és az eszközre.</translation>
<translation id="17513872634828108">Megnyitott lapok</translation>
<translation id="1753706481035618306">Oldalszám</translation>
<translation id="1763864636252898013">A szerver nem tudta bizonyítani, hogy valóban a(z) <ph name="DOMAIN" /> domainbe tartozik; biztonsági tanúsítványa az Ön eszközének operációs rendszere 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>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">A megnyitott lapok helye</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Kártyatulajdonos neve</translation>
-<translation id="1806541873155184440">Hozzáadva: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Érvénytelen kérés vagy kérésparaméter</translation>
<translation id="1826516787628120939">Ellenőrzés</translation>
<translation id="1834321415901700177">A webhely ártalmas programokat tartalmaz</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 javaslat}other{# javaslat}}</translation>
<translation id="2079545284768500474">Visszavonás</translation>
<translation id="20817612488360358">A rendszer proxybeállításai konfigurálva vannak a használathoz, de kifejezett proxykonfiguráció is meg van adva.</translation>
-<translation id="2084558088529668945">Olyan webhelyen adta meg a jelszavát, amelyet nem a(z) <ph name="ORG_NAME" /> kezel. Jelszava védelme érdekében ne adja meg újra a jelszót más alkalmazásokban és webhelyeken.</translation>
<translation id="2091887806945687916">Hang</translation>
<translation id="2094505752054353250">Domainkeveredés</translation>
<translation id="2096368010154057602">Megye</translation>
+<translation id="2102134110707549001">Erős jelszó ajánlása…</translation>
<translation id="2108755909498034140">Indítsa újra a számítógépet</translation>
<translation id="2113977810652731515">Kártya</translation>
<translation id="2114841414352855701">A rendszer figyelmen kívül hagyja, mivel a(z) <ph name="POLICY_NAME" /> felülírta.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP hiba</translation>
<translation id="2270484714375784793">Telefonszám</translation>
<translation id="2292556288342944218">Az internethez való hozzáférést a rendszer letiltotta</translation>
-<translation id="230155334948463882">Új kártya?</translation>
<translation id="2316887270356262533">1 MB-nál kevesebb hely szabadul fel. Előfordulhat, hogy egyes webhelyek lassabban töltődnek be, amikor legközelebb felkeresi őket.</translation>
<translation id="2317259163369394535">A(z) <ph name="DOMAIN" /> felhasználónevet és jelszót kér.</translation>
<translation id="2317583587496011522">Elfogadott bankkártyák.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Elfogadott kártyák</translation>
<translation id="2702801445560668637">Olvasási lista</translation>
<translation id="2704283930420550640">Az érték nem egyezik a formátummal.</translation>
-<translation id="2704951214193499422">A Chromium ez alkalommal nem tudta ellenőrizni az Ön kártyáját. Próbálja újra később.</translation>
<translation id="2705137772291741111">A webhely mentett (gyorsítótárazott) példánya nem olvasható.</translation>
<translation id="2709516037105925701">Automatikus kitöltés</translation>
<translation id="2710942282213947212">A számítógépen található valamelyik szoftver megakadályozza a Chromiumot abban, hogy biztonságosan csatlakozzon az internethez</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Szállítási mód</translation>
<translation id="277499241957683684">Hiányzó eszközrekord</translation>
<translation id="2781030394888168909">Exportálás MacOS-formátumban</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">A kapcsolat alaphelyzetbe állt.</translation>
<translation id="2788784517760473862">Elfogadott hitelkártyák</translation>
<translation id="2794233252405721443">A webhely le van tiltva</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Bármelyik kapcsolatlétesítésre használt proxyt letilthatja a beállítások oldalon.</translation>
<translation id="2955913368246107853">Keresősáv bezárása</translation>
<translation id="2958431318199492670">A hálózati konfiguráció nem felel meg az ONC szabványnak. A konfiguráció egyes részeit nem lehet importálni.</translation>
-<translation id="2966678944701946121">Lejárati dátum: <ph name="EXPIRATION_DATE_ABBR" />, hozzáadva: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Biztonságos kapcsolat létrehozásához az órát pontosan be kell állítani. Ez azért szükséges, mert a webhelyek által az azonosításukra használt tanúsítványok csak adott ideig érvényesek. Mivel az eszköz órája nem pontos, a Google Chrome nem tudja ellenőrizni ezeket a tanúsítványokat.</translation>
<translation id="2972581237482394796">&amp;Újra</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" /> van jelenleg kiválasztva. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Nem megfelelő irányelvtípus</translation>
<translation id="3037605927509011580">A manóba!</translation>
<translation id="3041612393474885105">Tanúsítvány adatai</translation>
-<translation id="3063697135517575841">A Chrome ez alkalommal nem tudta ellenőrizni az Ön kártyáját. Próbálja újra később.</translation>
<translation id="3064966200440839136">Inkognitómód elhagyása külső alkalmazással történő fizetéshez. Folytatja?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Nincs}=1{1 jelszó}other{# jelszó}}</translation>
<translation id="3096100844101284527">Átvételi cím hozzáadása</translation>
@@ -339,7 +336,6 @@
<translation id="3305707030755673451">Adatainak titkosítása megtörtént összetett szinkronizálási jelszavával a következő időpontban: <ph name="TIME" />. Adja meg a jelszót a szinkronizálás megkezdéséhez.</translation>
<translation id="3320021301628644560">Számlázási cím hozzáadása</translation>
<translation id="3338095232262050444">Biztonságos</translation>
-<translation id="3340978935015468852">beállítások</translation>
<translation id="3345135638360864351">A webhelyhez való hozzáférésre irányuló kérelme nem jutott el <ph name="NAME" /> felhasználóhoz. Kérjük, próbálkozzon újra.</translation>
<translation id="3355823806454867987">Proxybeállítások módosítása...</translation>
<translation id="3361596688432910856">A Chrome <ph name="BEGIN_EMPHASIS" />nem menti<ph name="END_EMPHASIS" /> a következő adatokat:
@@ -373,7 +369,6 @@
<translation id="3528171143076753409">A szervezet tanúsítványa nem megbízható.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Legalább 1 elem van a szinkronizált eszközökön}=1{1 elem (és még több a szinkronizált eszközökön)}other{# elem (és még több a szinkronizált eszközökön)}}</translation>
<translation id="3539171420378717834">A kártya másolatának megőrzése az eszközön</translation>
-<translation id="3542684924769048008">Jelszó használata a következőre:</translation>
<translation id="3549644494707163724">Az összes szinkronizált adat titkosítása saját összetett szinkronizálási jelszóval</translation>
<translation id="3556433843310711081">A letiltást a kezelő oldhatja fel</translation>
<translation id="3566021033012934673">Az Ön kapcsolata nem privát</translation>
@@ -459,7 +454,6 @@
<translation id="4171400957073367226">Hibás igazoló aláírás.</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{ még <ph name="ITEM_COUNT" /> elem}other{ még <ph name="ITEM_COUNT" /> elem}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" />: <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">A Chrome azt javasolja, hogy adjon meg új <ph name="ORG_NAME" />-jelszavat a régi helyett, ha azt más webhelyeken is használta.</translation>
<translation id="4196861286325780578">&amp;Áthelyezés újra</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />A tűzfal és a vírusirtó konfigurációjának ellenőrzése<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Összeomlások</translation>
@@ -488,7 +482,6 @@
<translation id="425582637250725228">Előfordulhat, hogy módosításait nem menti a rendszer.</translation>
<translation id="4258748452823770588">Rossz aláírás</translation>
<translation id="4265872034478892965">A rendszergazda engedélyezte</translation>
-<translation id="4269787794583293679">(Nincs felhasználónév)</translation>
<translation id="4275830172053184480">Indítsa újra az eszközt</translation>
<translation id="4277028893293644418">Jelszó visszaállítása</translation>
<translation id="4280429058323657511">, lejár: <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -511,6 +504,7 @@
<translation id="4434045419905280838">Előugró ablakok és átirányítások</translation>
<translation id="443673843213245140">A proxy használata le van tiltva, de kifejezett proxykonfiguráció van megadva.</translation>
<translation id="445100540951337728">Elfogadott bankkártyák</translation>
+<translation id="4472575034687746823">Kezdő lépések</translation>
<translation id="4506176782989081258">Érvényesítési hiba: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Kapcsolatfelvétel a rendszergazdával</translation>
<translation id="450710068430902550">Megosztás a rendszergazdával</translation>
@@ -535,8 +529,8 @@
<translation id="4726672564094551039">Házirendek újratöltése</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4736825316280949806">Indítsa újra a Chromiumot</translation>
+<translation id="4742407542027196863">Jelszavak kezelése…</translation>
<translation id="4744603770635761495">Végrehajtható fájl útvonala</translation>
-<translation id="4749685221585524849">Utolsó használat: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Adatai (például jelszava vagy hitelkártyaszáma) nem láthatók más számára, amikor a rendszer elküldi őket a webhelynek.</translation>
<translation id="4756388243121344051">&amp;Előzmények</translation>
<translation id="4758311279753947758">Névjegyadatok hozzáadása</translation>
@@ -553,6 +547,7 @@
<translation id="4850886885716139402">Nézet</translation>
<translation id="4854362297993841467">Ez a kézbesítési mód nem áll rendelkezésre. Próbálkozzon másik móddal.</translation>
<translation id="4858792381671956233">Megkérdezted a szüleidet, hogy meg szabad-e látogatnod ezt a webhelyet</translation>
+<translation id="4876305945144899064">Nincs felhasználónév</translation>
<translation id="4880827082731008257">Keresés az előzmények között</translation>
<translation id="4881695831933465202">Megnyitás</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> és <ph name="TYPE_3" /></translation>
@@ -586,6 +581,7 @@
<translation id="5089810972385038852">Állam</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="5098332213681597508">Ez a név a Google-fiókjából származik.</translation>
<translation id="5115563688576182185">(64 bites)</translation>
<translation id="5121084798328133320">A megerősítést követően a böngésző megosztja az Ön Google Payments-fiókjából származó kártyaadatokat ezzel a webhellyel.</translation>
<translation id="5128122789703661928">Ez a munkamenetnév nem érvényes, ezért nem törölhető.</translation>
@@ -626,9 +622,10 @@
<translation id="5332219387342487447">Szállítási mód</translation>
<translation id="5355557959165512791">Pillanatnyilag nem tudja felkeresni a(z) <ph name="SITE" /> webhelyet, mert a webhely tanúsítványát visszavonták. A hálózati hibák és támadások rendszerint átmenetiek, ezért az említett oldal működése később valószínűleg helyreáll.</translation>
<translation id="536296301121032821">Az irányelv-beállítások tárolása sikertelen</translation>
+<translation id="5371425731340848620">Kártya frissítése</translation>
<translation id="5377026284221673050">„Az óra késik”, „Az óra siet” vagy „&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;”</translation>
<translation id="5386426401304769735">A webhely tanúsítványlánca SHA-1 titkosítással aláírt tanúsítványt tartalmaz.</translation>
-<translation id="5402410679244714488">Lejárati dátum: <ph name="EXPIRATION_DATE_ABBR" />, utolsó használat több mint egy éve</translation>
+<translation id="5387961145478138773">Gyors hozzáférés kedvenc Google-alkalmazásaihoz</translation>
<translation id="540969355065856584">A szerver nem tudta bizonyítani, hogy valóban a(z) <ph name="DOMAIN" /> domainbe tartozik; biztonsági tanúsítványa jelenleg nem érvényes. Ennek oka lehet konfigurációs hiba, vagy hogy egy támadó eltérítette az Ön kapcsolatát.</translation>
<translation id="5421136146218899937">Böngészési adatok törlése...</translation>
<translation id="5430298929874300616">Könyvjelző törlése</translation>
@@ -670,11 +667,13 @@
<translation id="5633066919399395251">Előfordulhat, hogy a(z) <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> webhely támadói 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 hitelkártyaadatait). <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="5659593005791499971">E-mail</translation>
+<translation id="5666899935841546222">Szeretné, ha az összes kártyája egy helyen lenne?</translation>
<translation id="5675650730144413517">Az oldal nem működik</translation>
<translation id="5685654322157854305">Szállítási cím hozzáadása</translation>
<translation id="5689199277474810259">Exportálás JSON formátumba</translation>
<translation id="5689516760719285838">Tartózkodási hely</translation>
<translation id="570530837424789914">Kezelés…</translation>
+<translation id="57094364128775171">Erős jelszó ajánlása…</translation>
<translation id="5710435578057952990">A webhely valódiságát nem ellenőriztük.</translation>
<translation id="5719499550583120431">Elfogadott feltöltőkártyák.</translation>
<translation id="5720705177508910913">Jelenlegi felhasználó</translation>
@@ -695,11 +694,9 @@
<translation id="5869405914158311789">A webhely nem érhető el</translation>
<translation id="5869522115854928033">Mentett jelszavak</translation>
<translation id="5893752035575986141">Elfogadott hitelkártyák.</translation>
-<translation id="5898382028489516745">A Chromium azt javasolja, hogy adjon meg új <ph name="ORG_NAME" />-jelszavat a régi helyett, ha azt más webhelyeken is használta.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (szinkronizálva)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 van használatban}other{# van használatban}}</translation>
<translation id="5939518447894949180">Visszaállítás</translation>
-<translation id="5959728338436674663">Bizonyos <ph name="BEGIN_WHITEPAPER_LINK" />rendszer-információk és oldaltartalmak<ph name="END_WHITEPAPER_LINK" /> automatikus küldése a Google-nak a veszélyes alkalmazások és webhelyek felderítése érdekében. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Kapcsolattartási adatok szerkesztése</translation>
<translation id="5967867314010545767">Eltávolítás az előzmények közül</translation>
<translation id="5975083100439434680">Kicsinyítés</translation>
@@ -745,13 +742,11 @@
<translation id="6282194474023008486">Irányítószám</translation>
<translation id="6290238015253830360">A javasolt cikkek helye</translation>
<translation id="6305205051461490394">A(z) <ph name="URL" /> nem érhető el.</translation>
-<translation id="6319915415804115995">Utolsó használat több mint egy éve</translation>
<translation id="6321917430147971392">Ellenőrizze a DNS-beállításokat</translation>
<translation id="6328639280570009161">Próbálkozzon a hálózati előrejelzések kikapcsolásával</translation>
<translation id="6328786501058569169">A webhely megtévesztő</translation>
<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="6342069812937806050">Éppen most</translation>
<translation id="6355080345576803305">Nyilvános munkamenet általi felülbírálás</translation>
<translation id="6358450015545214790">Mit jelent ez?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 egyéb javaslat}other{# egyéb javaslat}}</translation>
@@ -776,7 +771,6 @@
<translation id="6529602333819889595">&amp;Törlés újra</translation>
<translation id="6534179046333460208">Fizikai web – javaslatok</translation>
<translation id="6550675742724504774">Beállítások</translation>
-<translation id="6556915248009097796">Lejárati dátum: <ph name="EXPIRATION_DATE_ABBR" />, utolsó használat: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">A kezelő még nem hagyta jóvá</translation>
<translation id="6569060085658103619">Jelenleg bővítményoldalt tekint meg</translation>
<translation id="6596325263575161958">Titkosítási lehetőségek</translation>
@@ -805,15 +799,20 @@
<translation id="6825578344716086703">Megpróbálta elérni a(z) <ph name="DOMAIN" /> webhelyet, de a szerver gyenge aláírási algoritmust használó tanúsítványt mutatott be. Ez alapján elképzelhető, hogy a szerver által megadott biztonsági tanúsítványt meghamisították, és a szerver nem az, amelyikre számított (lehet, hogy éppen valamilyen támadóval kommunikál).</translation>
<translation id="6831043979455480757">Fordítás</translation>
<translation id="6839929833149231406">Körzet</translation>
+<translation id="6852204201400771460">Újratölti az alkalmazást?</translation>
+<translation id="6865412394715372076">Ez a kártya jelenleg nem ellenőrizhető</translation>
<translation id="6874604403660855544">&amp;Hozzáadás újra</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Ezt a házirendszintet a rendszer nem támogatja.</translation>
<translation id="6895330447102777224">Kártyáját ellenőriztük</translation>
<translation id="6897140037006041989">User agent</translation>
+<translation id="6903319715792422884">A Biztonságos Böngészés fejlesztésének segítése bizonyos <ph name="BEGIN_WHITEPAPER_LINK" />rendszer-információknak és oldaltartalmaknak<ph name="END_WHITEPAPER_LINK" /> a Google-nak való elküldésével. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Felhasználó:</translation>
+<translation id="6944692733090228304">Olyan webhelyen adta meg a jelszavát, amelyet nem a(z) <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> kezel. Fiókja védelme érdekében ne használja fel újra a jelszót más alkalmazásokban és webhelyeken.</translation>
<translation id="6945221475159498467">Kiválasztás</translation>
<translation id="6948701128805548767">Az átvételi módok és követelmények megtekintéséhez válassza ki a címet</translation>
<translation id="6949872517221025916">Jelszó visszaállítása</translation>
+<translation id="6950684638814147129">A JSON-érték szintaktikai elemzése során jelentkező hiba: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">A szerver tanúsítványa hamisítványnak tűnik.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Készülék</translation>
@@ -875,6 +874,7 @@
&lt;li&gt;Kattintson az &lt;strong&gt;Alkalmaz&lt;/strong&gt;, majd az &lt;strong&gt;OK&lt;/strong&gt; gombra.
&lt;li&gt;Keresse fel a &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome súgóját&lt;/a&gt;, ahol további információt talál a szoftver számítógépről történő végleges eltávolításáról.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Jelszavak kezelése…</translation>
<translation id="7419106976560586862">Profil elérési útja</translation>
<translation id="7437289804838430631">Kapcsolatfelvételi adatok hozzáadása</translation>
<translation id="7441627299479586546">Az irányelv tárgya nem megfelelő</translation>
@@ -883,7 +883,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />További információk megtekintése<ph name="END_LINK" /> a problémával kapcsolatban.</translation>
<translation id="7455133967321480974">Globális alapértelmezés használata (Tiltás)</translation>
<translation id="7460163899615895653">A többi eszközön legutoljára megtekintett lapok láthatók itt</translation>
-<translation id="7469372306589899959">Kártya igazolása…</translation>
<translation id="7473891865547856676">Nem, köszönöm</translation>
<translation id="7481312909269577407">Előre</translation>
<translation id="7485870689360869515">Nem található adat.</translation>
@@ -1013,6 +1012,7 @@
<translation id="8308427013383895095">A fordítás a hálózati kapcsolat problémája miatt nem sikerült.</translation>
<translation id="8311129316111205805">Munkamenet betöltése</translation>
<translation id="8332188693563227489">A hozzáférés megtagadva a következőhöz: <ph name="HOST_NAME" /></translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Ha tisztában van a biztonságát fenyegető kockázatokkal, a veszélyes programok eltávolítása előtt is <ph name="BEGIN_LINK" />felkeresheti ezt a webhelyet<ph name="END_LINK" />.</translation>
<translation id="8349305172487531364">Könyvjelzősáv</translation>
<translation id="8363502534493474904">Repülős üzemmód kikapcsolása</translation>
@@ -1033,21 +1033,25 @@
<translation id="8498891568109133222">A(z) <ph name="HOST_NAME" /> túl hosszú ideje nem válaszol.</translation>
<translation id="8503559462189395349">Chrome-jelszavak</translation>
<translation id="8503813439785031346">Felhasználónév</translation>
+<translation id="8508648098325802031">Keresés ikon</translation>
<translation id="8543181531796978784">Lehetősége van arra, hogy <ph name="BEGIN_ERROR_LINK" />jelentse az észlelési problémát<ph name="END_ERROR_LINK" />, ha pedig tisztában van a biztonságát fenyegető kockázatokkal, <ph name="BEGIN_LINK" />felkeresheti a nem biztonságos webhelyet<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Kérdése van? Vegye fel a kapcsolatot a fiókját felügyelő személlyel.</translation>
<translation id="8553075262323480129">A fordítás nem sikerült, mivel az oldal nyelvét nem lehet megállapítani.</translation>
<translation id="8557066899867184262">A CVC-kód a kártya hátulján található.</translation>
<translation id="8559762987265718583">Nem hozható létre privát kapcsolat a következővel: <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, mert az eszköz dátum- és időbeállítása helytelen (<ph name="DATE_AND_TIME" />).</translation>
+<translation id="8564985650692024650">A Chromium azt javasolja, hogy adjon meg új <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />-jelszót a régi helyett, ha azt más webhelyeken is használta.</translation>
<translation id="8571890674111243710">Oldal fordítása erre a nyelvre: <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Szám hozzáadása
</translation>
<translation id="859285277496340001">Ez a tanúsítvány nem határoz meg olyan mechanizmust, amely ellenőrizné, hogy visszavonták-e.</translation>
+<translation id="860043288473659153">Kártyatulajdonos neve</translation>
<translation id="8620436878122366504">A szüleid még nem hagyták jóvá</translation>
<translation id="8625384913736129811">Kártya mentése az eszközre</translation>
-<translation id="8639963783467694461">Automatikus kitöltési beállítások</translation>
+<translation id="8663226718884576429">Rendelés-összefoglaló, <ph name="TOTAL_LABEL" />, További részletek</translation>
<translation id="8680536109547170164"><ph name="QUERY" />. A válasz: <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">A kapcsolat (<ph name="DOMAIN" />) nem titkosított.</translation>
<translation id="8718314106902482036">A fizetés nem fejeződött be</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, keresési javaslat</translation>
<translation id="8725066075913043281">Újrapróbálás</translation>
<translation id="8728672262656704056">Ön inkognitómódra váltott</translation>
<translation id="8730621377337864115">Kész</translation>
@@ -1063,8 +1067,10 @@
<translation id="8820817407110198400">Könyvjelzők</translation>
<translation id="883848425547221593">Egyéb könyvjelzők</translation>
<translation id="884264119367021077">Szállítási cím</translation>
+<translation id="8846319957959474018">Alkalmazások egyszerű megnyitása könyvjelzők segítségével</translation>
<translation id="884923133447025588">Nem található visszavonási mechanizmus.</translation>
<translation id="885730110891505394">Megosztás a Google-lal</translation>
+<translation id="8858065207712248076">A Chrome azt javasolja, hogy adjon meg új <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />-jelszót a régi helyett, ha azt más webhelyeken is használta.</translation>
<translation id="8866481888320382733">Irányelv-beállítások előfeldolgozási hibája</translation>
<translation id="8870413625673593573">Mostanában bezárt</translation>
<translation id="8874824191258364635">Érvényes kártyaszámot adjon meg</translation>
@@ -1103,6 +1109,7 @@
<translation id="9103872766612412690">A(z) <ph name="SITE" /> webhely rendes esetben titkosítást alkalmaz az Ön adatainak védelme érdekében. Amikor a Chromium most csatlakozni próbált, a(z) <ph name="SITE" /> webhely szokatlan és helytelen hitelesítési adatokat küldött vissza.Ez olyankor fordulhat elő, amikor egy támadó megpróbálja magát kiadni a(z) <ph name="SITE" /> webhelynek, vagy valamilyen Wi-Fi-bejelentkezési képernyő megszakította a kapcsolatot. Adatai továbbra is biztonságban vannak, mivel a Chromium még azt megelőzően megszakította a kapcsolatot, hogy bármiféle adatcserére sor kerülhetett volna.</translation>
<translation id="9106062320799175032">Számlázási cím hozzáadása</translation>
<translation id="910908805481542201">Segítséget kérek a probléma megoldásához</translation>
+<translation id="9114524666733003316">Kártya ellenőrzése…</translation>
<translation id="9128870381267983090">Csatlakozás hálózathoz</translation>
<translation id="9137013805542155359">Eredeti megjelenítése</translation>
<translation id="9137248913990643158">Indítsa el a Chrome böngészőt és jelentkezzen be az alkalmazás használata előtt.</translation>
@@ -1113,6 +1120,7 @@
<translation id="9168814207360376865">Engedélyezés a webhelyek számára, hogy ellenőrizzék, van-e elmentett fizetési módja</translation>
<translation id="9169664750068251925">Mindig tiltsa ezen az oldalon</translation>
<translation id="9170848237812810038">&amp;Visszavonás</translation>
+<translation id="9171296965991013597">Bezárja az alkalmazást?</translation>
<translation id="917450738466192189">A szerver tanúsítványa érvénytelen.</translation>
<translation id="9183425211371246419">A(z) <ph name="HOST_NAME" /> egy nem támogatott protokollt használ.</translation>
<translation id="9205078245616868884">Adatai az összetett szinkronizálási jelszavával vannak titkosítva. Adja meg a jelszót a szinkronizálás megkezdéséhez.</translation>
diff --git a/chromium/components/strings/components_strings_id.xtb b/chromium/components/strings/components_strings_id.xtb
index 69e5de491f8..0db273472ef 100644
--- a/chromium/components/strings/components_strings_id.xtb
+++ b/chromium/components/strings/components_strings_id.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Sembunyikan nilai</translation>
<translation id="1228893227497259893">Pengidentifikasi entitas salah</translation>
<translation id="1232569758102978740">Tanpa Judul</translation>
+<translation id="1250759482327835220">Untuk membayar lebih cepat di pembelian berikutnya, simpan kartu, nama, dan alamat penagihan ke Akun Google Anda.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (disinkronkan)</translation>
<translation id="1256368399071562588">&lt;p&gt;Jika Anda mencoba membuka situs dan tidak terbuka, terlebih dahulu coba perbaiki error dengan langkah-langkah pemecahan masalah ini:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Sesuaikan tanggal dan waktu dari bagian &lt;strong&gt;Umum&lt;/strong&gt; aplikasi &lt;strong&gt;Setelan&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Terjadi masalah sewaktu menampilkan halaman web ini.</translation>
-<translation id="1590457302292452960">Buat sandi kuat...</translation>
<translation id="1592005682883173041">Akses Data Lokal</translation>
<translation id="1594030484168838125">Pilih</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Coba hubungi admin sistem.</translation>
<translation id="1740951997222943430">Masukkan bulan habis masa berlaku yang valid</translation>
+<translation id="1743520634839655729">Untuk membayar lebih cepat di pembelian berikutnya, simpan kartu, nama, dan alamat penagihan ke Akun Google Anda dan ke perangkat ini.</translation>
<translation id="17513872634828108">Buka tab</translation>
<translation id="1753706481035618306">Nomor halaman</translation>
<translation id="1763864636252898013">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya tidak dipercaya oleh sistem operasi perangkat Anda. Hal ini dapat disebabkan oleh kesalahan konfigurasi atau penyerang memotong sambungan Anda.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Tab yang terbuka muncul di sini</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Nama Pemegang Kartu</translation>
-<translation id="1806541873155184440">Ditambahkan pada <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Permintaan atau parameter permintaan tidak valid</translation>
<translation id="1826516787628120939">Memeriksa</translation>
<translation id="1834321415901700177">Situs ini berisi program yang berbahaya</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 saran}other{# saran}}</translation>
<translation id="2079545284768500474">Urungkan</translation>
<translation id="20817612488360358">Setelan proxy sistem disetel untuk digunakan namun konfigurasi proxy eksplisit juga ditentukan.</translation>
-<translation id="2084558088529668945">Anda memasukkan sandi di situs yang tidak dikelola oleh <ph name="ORG_NAME" />. Untuk melindungi akun, jangan gunakan sandi yang sama di aplikasi dan situs lain.</translation>
<translation id="2091887806945687916">Suara</translation>
<translation id="2094505752054353250">Ketidakcocokan domain</translation>
<translation id="2096368010154057602">Departemen</translation>
+<translation id="2102134110707549001">Sarankan Sandi yang Kuat…</translation>
<translation id="2108755909498034140">Mulai ulang komputer</translation>
<translation id="2113977810652731515">Kartu</translation>
<translation id="2114841414352855701">Diabaikan karena diganti dengan <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Kesalahan HTTP</translation>
<translation id="2270484714375784793">Nomor telepon</translation>
<translation id="2292556288342944218">Akses Internet Anda diblokir</translation>
-<translation id="230155334948463882">Kartu baru?</translation>
<translation id="2316887270356262533">Sediakan ruang kurang dari 1 MB. Beberapa situs mungkin dimuat lebih lambat dibuka lagi.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> memerlukan nama pengguna dan sandi.</translation>
<translation id="2317583587496011522">Kartu debit diterima.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Kartu yang Diterima</translation>
<translation id="2702801445560668637">Daftar Bacaan</translation>
<translation id="2704283930420550640">Nilai tidak sesuai format.</translation>
-<translation id="2704951214193499422">Saat ini Chromium tidak dapat mengonfirmasi kartu. Coba lagi nanti.</translation>
<translation id="2705137772291741111">Salinan tersimpan (dalam cache) situs ini tidak dapat dibaca.</translation>
<translation id="2709516037105925701">Isi-Otomatis</translation>
<translation id="2710942282213947212">Software di komputer membuat Chromium tidak terhubung dengan aman ke web</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Metode pengiriman</translation>
<translation id="277499241957683684">Catatan perangkat hilang</translation>
<translation id="2781030394888168909">Ekspor MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Sambungan disetel ulang.</translation>
<translation id="2788784517760473862">Kartu kredit yang diterima</translation>
<translation id="2794233252405721443">Situs diblokir</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Anda dapat menonaktifkan proxy apa pun yang dikonfigurasi untuk sambungan dari halaman setelan.</translation>
<translation id="2955913368246107853">Tutup bilah cari</translation>
<translation id="2958431318199492670">Konfigurasi jaringan tidak mematuhi standar ONC. Bagian dari konfigurasi mungkin tidak diimpor.</translation>
-<translation id="2966678944701946121">Habis masa berlaku: <ph name="EXPIRATION_DATE_ABBR" />, ditambahkan pada <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Untuk membuat sambungan aman, jam perlu disetel dengan benar. Itu karena sertifikat yang digunakan situs web untuk mengidentifikasi situs web tersebut hanya valid untuk jangka waktu tertentu. Karena jam perangkat tidak benar, Google Chrome tidak dapat memverifikasi sertifikat ini.</translation>
<translation id="2972581237482394796">&amp;Ulang</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, saat ini yang dipilih. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Jenis kebijakan salah</translation>
<translation id="3037605927509011580">Yah!</translation>
<translation id="3041612393474885105">Informasi Sertifikat</translation>
-<translation id="3063697135517575841">Saat ini Chrome tidak dapat mengonfirmasi kartu. Coba lagi nanti.</translation>
<translation id="3064966200440839136">Keluar dari mode penyamaran untuk membayar melalui aplikasi eksternal. Lanjutkan?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Tidak ada}=1{1 sandi}other{# sandi}}</translation>
<translation id="3096100844101284527">Tambahkan Alamat Pengambilan</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Data Anda dienkripsi dengan frasa sandi sinkronisasi pada tanggal <ph name="TIME" />. Masukkan frasa sandi untuk memulai sinkronisasi.</translation>
<translation id="3320021301628644560">Tambahkan alamat penagihan</translation>
<translation id="3338095232262050444">Aman</translation>
-<translation id="3340978935015468852">setelan</translation>
<translation id="3345135638360864351">Permintaan Anda untuk mengakses situs ini tidak dapat dikirimkan ke <ph name="NAME" />. Coba lagi.</translation>
<translation id="3355823806454867987">Ubah setelan proxy...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />tidak akan menyimpan<ph name="END_EMPHASIS" /> informasi berikut:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Sertifikat server tidak dipercaya.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Minimal 1 item pada perangkat yang disinkronkan}=1{1 item (dan beberapa di perangkat yang disinkronkan)}other{# item (dan beberapa di perangkat yang disinkronkan)}}</translation>
<translation id="3539171420378717834">Menyimpan salinan kartu ini di perangkat ini</translation>
-<translation id="3542684924769048008">Gunakan sandi untuk:</translation>
<translation id="3549644494707163724">Enkripsikan data yang disinkronkan dengan frasa sandi sinkronisasi Anda</translation>
<translation id="3556433843310711081">Pengelola dapat membuka blokirnya untuk Anda</translation>
<translation id="3566021033012934673">Koneksi Anda tidak pribadi</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Tanda tangan verifikasi tidak valid</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> item lainnya}other{<ph name="ITEM_COUNT" /> item lainnya}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome menyarankan untuk menyetel ulang sandi <ph name="ORG_NAME" /> jika Anda juga menggunakannya di situs lain.</translation>
<translation id="4196861286325780578">&amp;Ulangi pemindahan</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Periksa konfigurasi antivirus dan firewall<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Kerusakan</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Perubahan yang Anda lakukan mungkin tidak disimpan.</translation>
<translation id="4258748452823770588">Tanda tangan salah</translation>
<translation id="4265872034478892965">Diizinkan oleh administrator</translation>
-<translation id="4269787794583293679">(Tidak ada nama pengguna)</translation>
<translation id="4275830172053184480">Mulai ulang perangkat Anda</translation>
<translation id="4277028893293644418">Setel ulang sandi</translation>
<translation id="4280429058323657511">, kedaluwarsa <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Pop-up dan pengalihan</translation>
<translation id="443673843213245140">Penggunaan proxy dinonaktifkan tetapi konfigurasi proxy yang eksplisit ditentukan.</translation>
<translation id="445100540951337728">Kartu kredit yang diterima</translation>
+<translation id="4472575034687746823">Memulai</translation>
<translation id="4506176782989081258">Kesalahan validasi: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Hubungi admin sistem</translation>
<translation id="450710068430902550">Berbagi dengan Administrator</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Muat ulang kebijakan</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4736825316280949806">Buka Ulang Chromium</translation>
+<translation id="4742407542027196863">Kelola sandi...</translation>
<translation id="4744603770635761495">Jalur Yang Dapat Dijalankan</translation>
-<translation id="4749685221585524849">Terakhir digunakan pada <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Informasi Anda (misalnya, sandi atau nomor kartu kredit) bersifat pribadi saat dikirimkan ke situs ini.</translation>
<translation id="4756388243121344051">&amp;Riwayat</translation>
<translation id="4758311279753947758">Tambahkan info kontak</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Lihat</translation>
<translation id="4854362297993841467">Metode pengiriman tidak tersedia. Coba metode lain.</translation>
<translation id="4858792381671956233">Kamu telah meminta izin kepada orang tua untuk mengunjungi situs ini</translation>
+<translation id="4876305945144899064">Tidak ada nama pengguna</translation>
<translation id="4880827082731008257">Telusuri histori</translation>
<translation id="4881695831933465202">Buka</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Negara bagian</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="5098332213681597508">Nama ini dari Akun Google Anda.</translation>
<translation id="5115563688576182185">(64 bit)</translation>
<translation id="5121084798328133320">Setelah mengonfirmasi, detail kartu dari akun Pembayaran Google Anda akan dibagikan dengan situs ini.</translation>
<translation id="5128122789703661928">Sesi dengan nama ini tidak valid untuk dihapus.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Metode Pengiriman</translation>
<translation id="5355557959165512791">Anda tidak dapat membuka <ph name="SITE" /> sekarang karena sertifikatnya telah dicabut. Error jaringan dan serangan biasanya bersifat sementara, sehingga halaman ini mungkin akan berfungsi nanti.</translation>
<translation id="536296301121032821">Gagal menyimpan setelan kebijakan</translation>
+<translation id="5371425731340848620">Perbarui kartu</translation>
<translation id="5377026284221673050">"Jam Anda terlalu lambat" atau "Jam Anda terlalu cepat" atau "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">Rantai sertifikat untuk situs ini berisi sertifikat yang ditandatangani menggunakan SHA-1.</translation>
-<translation id="5402410679244714488">Kedaluwarsa: <ph name="EXPIRATION_DATE_ABBR" />, terakhir digunakan lebih dari setahun yang lalu</translation>
+<translation id="5387961145478138773">Dapatkan akses instan ke Aplikasi Google favorit Anda</translation>
<translation id="540969355065856584">Server ini tidak dapat membuktikan bahwa ini adalah <ph name="DOMAIN" />; sertifikat keamanannya saat ini tidak valid. Hal ini mungkin disebabkan oleh kesalahan konfigurasi atau ada penyerang yang mengganggu sambungan internet Anda.</translation>
<translation id="5421136146218899937">Hapus data browsing...</translation>
<translation id="5430298929874300616">Buang bookmark</translation>
@@ -672,11 +669,13 @@
<translation id="5633066919399395251">Saat ini, penyerang di <ph name="BEGIN_BOLD" /><ph name="SITE" /><ph name="END_BOLD" /> mungkin berusaha menginstal program berbahaya di komputer Anda yang dapat 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="5659593005791499971">Email</translation>
+<translation id="5666899935841546222">Ingin menyimpan semua kartu di satu tempat?</translation>
<translation id="5675650730144413517">Halaman ini tidak berfungsi</translation>
<translation id="5685654322157854305">Tambahkan Alamat Pengiriman</translation>
<translation id="5689199277474810259">Ekspor ke JSON</translation>
<translation id="5689516760719285838">Lokasi</translation>
<translation id="570530837424789914">Kelola...</translation>
+<translation id="57094364128775171">Sarankan sandi yang kuat…</translation>
<translation id="5710435578057952990">Identitas situs Web ini belum diverifikasi.</translation>
<translation id="5719499550583120431">Kartu prabayar diterima.</translation>
<translation id="5720705177508910913">Pengguna saat ini</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Situs ini tidak dapat dijangkau</translation>
<translation id="5869522115854928033">Sandi tersimpan</translation>
<translation id="5893752035575986141">Kartu kredit diterima.</translation>
-<translation id="5898382028489516745">Chromium menyarankan untuk menyetel ulang sandi <ph name="ORG_NAME" /> jika Anda juga menggunakannya di situs lain.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (disinkronkan)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 terpakai}other{# terpakai}}</translation>
<translation id="5939518447894949180">Setel ulang</translation>
-<translation id="5959728338436674663">Kirim beberapa <ph name="BEGIN_WHITEPAPER_LINK" />informasi sistem dan konten halaman<ph name="END_WHITEPAPER_LINK" /> secara otomatis ke Google untuk membantu mendeteksi aplikasi dan situs berbahaya. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Edit Info Kontak</translation>
<translation id="5967867314010545767">Hapus dari histori</translation>
<translation id="5975083100439434680">Perkecil</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Kode pos</translation>
<translation id="6290238015253830360">Artikel yang disarankan ditampilkan di sini</translation>
<translation id="6305205051461490394"><ph name="URL" /> tidak dapat dijangkau.</translation>
-<translation id="6319915415804115995">Terakhir digunakan lebih dari setahun yang lalu</translation>
<translation id="6321917430147971392">Periksa setelan DNS Anda</translation>
<translation id="6328639280570009161">Coba nonaktifkan prediksi jaringan</translation>
<translation id="6328786501058569169">Situs ini bersifat menipu</translation>
<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="6342069812937806050">Baru saja</translation>
<translation id="6355080345576803305">Diganti oleh sesi publik</translation>
<translation id="6358450015545214790">Apakah maksud ini?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 saran lain}other{# saran lain}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Ulangi Penghapusan</translation>
<translation id="6534179046333460208">Saran Web Fisik</translation>
<translation id="6550675742724504774">Opsi</translation>
-<translation id="6556915248009097796">Habis masa berlaku: <ph name="EXPIRATION_DATE_ABBR" />, terakhir digunakan pada <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Pengelola Anda belum menyetujuinya</translation>
<translation id="6569060085658103619">Anda melihat halaman ekstensi</translation>
<translation id="6596325263575161958">Opsi enkripsi</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Anda berusaha menjangkau <ph name="DOMAIN" />, tetapi server menyajikan sertifikat yang ditandatangani menggunakan algoritme tanda tangan yang lemah (seperti SHA-1). Hal ini berarti kredensial keamanan yang disajikan server mungkin telah dipalsukan, dan server tersebut mungkin bukan yang diharapkan (Anda mungkin sedang berkomunikasi dengan penyerang).</translation>
<translation id="6831043979455480757">Terjemahkan</translation>
<translation id="6839929833149231406">Area</translation>
+<translation id="6852204201400771460">Muat ulang aplikasi?</translation>
+<translation id="6865412394715372076">Kartu ini tidak dapat diverifikasi sekarang</translation>
<translation id="6874604403660855544">&amp;Ulangi penambahan</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Tingkat kebijakan tidak didukung.</translation>
<translation id="6895330447102777224">Kartu telah dikonfirmasi</translation>
<translation id="6897140037006041989">Agen Pengguna</translation>
+<translation id="6903319715792422884">Bantu sempurnakan Safe Browsing dengan mengirimkan sebagian <ph name="BEGIN_WHITEPAPER_LINK" />informasi sistem dan konten halaman<ph name="END_WHITEPAPER_LINK" /> ke Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Pengguna:</translation>
+<translation id="6944692733090228304">Anda memasukkan sandi di situs yang tidak dikelola oleh <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Untuk melindungi akun, jangan gunakan sandi yang sama di aplikasi dan situs lain.</translation>
<translation id="6945221475159498467">Pilih</translation>
<translation id="6948701128805548767">Untuk melihat persyaratan dan metode pengambilan, pilih alamat</translation>
<translation id="6949872517221025916">Setel Ulang Sandi</translation>
+<translation id="6950684638814147129">Error saat mengurai nilai JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Sertifikat server tampaknya palsu.</translation>
<translation id="6965382102122355670">Oke</translation>
<translation id="6965978654500191972">Perangkat</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Klik &lt;strong&gt;Apply&lt;/strong&gt;, lalu klik &lt;strong&gt;Ok&lt;/strong&gt;
&lt;li&gt;Buka &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Pusat bantuan Chrome&lt;/a&gt; untuk mempelajari cara menghapus software tersebut secara permanen dari komputer
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Kelola Sandi...</translation>
<translation id="7419106976560586862">Jalur Profil</translation>
<translation id="7437289804838430631">Tambahkan Info Kontak</translation>
<translation id="7441627299479586546">Subjek kebijakan salah</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Pelajari lebih lanjut<ph name="END_LINK" /> tentang masalah ini.</translation>
<translation id="7455133967321480974">Gunakan default global (Cekal)</translation>
<translation id="7460163899615895653">Tab terbaru dari perangkat lain muncul di sini</translation>
-<translation id="7469372306589899959">Mengonfirmasi kartu</translation>
<translation id="7473891865547856676">Lain Kali</translation>
<translation id="7481312909269577407">Maju</translation>
<translation id="7485870689360869515">Tidak ada data yang ditemukan.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Terjemahan gagal karena ada masalah dengan koneksi jaringan.</translation>
<translation id="8311129316111205805">Muat sesi</translation>
<translation id="8332188693563227489">Akses ke <ph name="HOST_NAME" /> ditolak</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Jika Anda memahami risiko terhadap keamanan, Anda dapat <ph name="BEGIN_LINK" />mengunjungi situs ini<ph name="END_LINK" /> sebelum program berbahaya tersebut dibuang.</translation>
<translation id="8349305172487531364">Bilah bookmark</translation>
<translation id="8363502534493474904">Nonaktifkan mode pesawat</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> membutuhkan terlalu banyak waktu untuk merespons.</translation>
<translation id="8503559462189395349">Sandi Chrome</translation>
<translation id="8503813439785031346">Nama Pengguna</translation>
+<translation id="8508648098325802031">Ikon penelusuran</translation>
<translation id="8543181531796978784">Anda dapat <ph name="BEGIN_ERROR_LINK" />melaporkan masalah pendeteksian<ph name="END_ERROR_LINK" /> atau, jika memahami risiko bagi keamanan, Anda dapat <ph name="BEGIN_LINK" />mengunjungi situs yang tidak aman<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Ada pertanyaan? Hubungi orang yang memantau profil Anda.</translation>
<translation id="8553075262323480129">Terjemahan gagal karena bahasa halaman tidak dapat ditentukan.</translation>
<translation id="8557066899867184262">CVC terletak di bagian belakang kartu Anda.</translation>
<translation id="8559762987265718583">Sambungan pribadi ke <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> tidak dapat dibuat karena tanggal dan waktu (<ph name="DATE_AND_TIME" />) perangkat tidak benar.</translation>
+<translation id="8564985650692024650">Chromium menyarankan untuk menyetel ulang sandi <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> jika Anda juga menggunakannya di situs lain.</translation>
<translation id="8571890674111243710">Menerjemahkan halaman ke <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">+ nomor telepon</translation>
<translation id="859285277496340001">Sertifikat tidak menetapkan mekanisme untuk memeriksa apakah sertifikat telah ditarik.</translation>
+<translation id="860043288473659153">Nama pemegang kartu</translation>
<translation id="8620436878122366504">Orang tuamu belum menyetujuinya</translation>
<translation id="8625384913736129811">Simpan Kartu Ini ke Perangkat Ini</translation>
-<translation id="8639963783467694461">Setelan Isi-otomatis</translation>
+<translation id="8663226718884576429">Ringkasan Pesanan, <ph name="TOTAL_LABEL" />, Detail Selengkapnya</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, dengan jawaban, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Sambungan ke <ph name="DOMAIN" /> tidak dienkripsi.</translation>
<translation id="8718314106902482036">Pembayaran belum selesai</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, saran penelusuran</translation>
<translation id="8725066075913043281">Coba lagi</translation>
<translation id="8728672262656704056">Anda masuk mode penyamaran</translation>
<translation id="8730621377337864115">Selesai</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Bookmark</translation>
<translation id="883848425547221593">Bookmark Lain</translation>
<translation id="884264119367021077">Alamat pengiriman</translation>
+<translation id="8846319957959474018">Buka aplikasi secara mudah dengan bookmark</translation>
<translation id="884923133447025588">Tidak ditemukan mekanisme pembatalan.</translation>
<translation id="885730110891505394">Berbagi dengan Google</translation>
+<translation id="8858065207712248076">Chrome menyarankan untuk menyetel ulang sandi <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> jika Anda juga menggunakannya di situs lain.</translation>
<translation id="8866481888320382733">Kesalahan saat menguraikan setelan kebijakan</translation>
<translation id="8870413625673593573">Barusan Ditutup</translation>
<translation id="8874824191258364635">Masukkan nomor kartu yang valid</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> biasanya menggunakan enkripsi untuk melindungi informasi Anda. Saat Chromium mencoba menyambung ke <ph name="SITE" /> kali ini, situs web mengembalikan kredensial yang salah dan tidak biasa. Hal ini dapat terjadi jika ada penyerang yang berpura-pura menjadi <ph name="SITE" />, atau layar masuk Wi-Fi mengganggu sambungan. Informasi Anda masih aman karena Chromium menghentikan sambungan sebelum terjadi pertukaran data apa pun.</translation>
<translation id="9106062320799175032">Tambahkan Alamat Penagihan</translation>
<translation id="910908805481542201">Bantu saya memperbaiki hal ini</translation>
+<translation id="9114524666733003316">Mengonfirmasi kartu...</translation>
<translation id="9128870381267983090">Sambungkan ke jaringan</translation>
<translation id="9137013805542155359">Perlihatkan halaman asli</translation>
<translation id="9137248913990643158">Mulai dan login ke Chrome sebelum menggunakan aplikasi ini.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">Izinkan situs memeriksa apakah Anda telah menyimpan metode pembayaran atau tidak</translation>
<translation id="9169664750068251925">Selalu cekal di situs ini</translation>
<translation id="9170848237812810038">&amp;Urung</translation>
+<translation id="9171296965991013597">Tutup aplikasi?</translation>
<translation id="917450738466192189">Sertifikat server tidak valid.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> menggunakan protokol yang tidak didukung.</translation>
<translation id="9205078245616868884">Data Anda dienkripsi dengan frasa sandi sinkronisasi. Masukkan frasa sandi untuk memulai sinkronisasi.</translation>
diff --git a/chromium/components/strings/components_strings_it.xtb b/chromium/components/strings/components_strings_it.xtb
index 2226bbd77a7..4f4b3ed9c5e 100644
--- a/chromium/components/strings/components_strings_it.xtb
+++ b/chromium/components/strings/components_strings_it.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Nascondi valore</translation>
<translation id="1228893227497259893">Identificatore entità errato</translation>
<translation id="1232569758102978740">Senza titolo</translation>
+<translation id="1250759482327835220">Per pagare più velocemente la prossima volta, salva la carta, il nome e l'indirizzo di fatturazione sul tuo account Google.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (sincronizzati)</translation>
<translation id="1256368399071562588">&lt;p&gt;Se il sito web che intendi visitare non si apre, prova innanzitutto a correggere l'errore con questi passaggi per la risoluzione dei problemi:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Regola data e ora nella sezione &lt;strong&gt;Generali&lt;/strong&gt; dell'app &lt;strong&gt;Impostazioni&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Si è verificato un problema durante la visualizzazione della pagina web.</translation>
-<translation id="1590457302292452960">Genera una password efficace…</translation>
<translation id="1592005682883173041">Accesso ai dati locali</translation>
<translation id="1594030484168838125">Scegli</translation>
<translation id="1620510694547887537">Videocamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Prova a contattare l'amministratore di sistema.</translation>
<translation id="1740951997222943430">Inserisci un mese di scadenza valido</translation>
+<translation id="1743520634839655729">Per pagare più velocemente la prossima volta, salva la carta e l'indirizzo di fatturazione sul tuo account Google e su questo dispositivo.</translation>
<translation id="17513872634828108">Schede aperte</translation>
<translation id="1753706481035618306">Numero di pagina</translation>
<translation id="1763864636252898013">Questo server non è riuscito a dimostrare che si tratta di <ph name="DOMAIN" />; il relativo certificato di sicurezza non è considerato attendibile dal sistema operativo del dispositivo. Il problema potrebbe essere dovuto a un'errata configurazione o a un malintenzionato che intercetta la connessione.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Le tue schede aperte vengono visualizzate qui</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Nome del titolare della carta</translation>
-<translation id="1806541873155184440">Data di aggiunta: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Richiesta o parametri della richiesta non validi</translation>
<translation id="1826516787628120939">Verifica in corso...</translation>
<translation id="1834321415901700177">Il sito contiene programmi dannosi.</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 suggerimento}other{# suggerimenti}}</translation>
<translation id="2079545284768500474">Annulla</translation>
<translation id="20817612488360358">Devono essere utilizzate le impostazioni del proxy di sistema ma è stata specificata anche una configurazione proxy esplicita.</translation>
-<translation id="2084558088529668945">Hai inserito la password su un sito non gestito da <ph name="ORG_NAME" />. Per proteggere il tuo account, non riutilizzare la password su altri siti e app.</translation>
<translation id="2091887806945687916">Audio</translation>
<translation id="2094505752054353250">Dominio non corrispondente</translation>
<translation id="2096368010154057602">Dipartimento</translation>
+<translation id="2102134110707549001">Suggerisci password efficace…</translation>
<translation id="2108755909498034140">Riavvia il computer</translation>
<translation id="2113977810652731515">Carta</translation>
<translation id="2114841414352855701">Ignorata perché è stata sostituita da <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Errore HTTP</translation>
<translation id="2270484714375784793">Numero di telefono</translation>
<translation id="2292556288342944218">L'accesso a Internet è bloccato</translation>
-<translation id="230155334948463882">Nuova carta?</translation>
<translation id="2316887270356262533">Consente di liberare meno di 1 MB. Alcuni siti potrebbero caricarsi più lentamente alla prossima visita.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> richiede un nome utente e una password.</translation>
<translation id="2317583587496011522">Le carte di debito sono accettate.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Carte accettate</translation>
<translation id="2702801445560668637">Elenco di lettura</translation>
<translation id="2704283930420550640">Il valore non corrisponde al formato.</translation>
-<translation id="2704951214193499422">Al momento non è possibile confermare la carta in Chromium. Riprova più tardi.</translation>
<translation id="2705137772291741111">La copia del sito salvata (nella cache) era illeggibile.</translation>
<translation id="2709516037105925701">Compilazione automatica</translation>
<translation id="2710942282213947212">Il software installato sul computer sta impedendo a Chromium di connettersi in sicurezza a Internet</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Metodo di spedizione</translation>
<translation id="277499241957683684">Record del dispositivo mancante</translation>
<translation id="2781030394888168909">Esporta Mac OS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">La connessione è stata reimpostata.</translation>
<translation id="2788784517760473862">Carte di credito accettate</translation>
<translation id="2794233252405721443">Sito bloccato</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Puoi disattivare tutti i proxy configurati per una connessione dalla pagina delle impostazioni.</translation>
<translation id="2955913368246107853">Chiudi la barra di ricerca</translation>
<translation id="2958431318199492670">La configurazione di rete non è conforme allo standard ONC. Parti della configurazione potrebbero non essere importate.</translation>
-<translation id="2966678944701946121">Scadenza: <ph name="EXPIRATION_DATE_ABBR" />, data di aggiunta: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Per poter stabilire una connessione protetta, l'orologio deve essere impostato correttamente perché i certificati utilizzati dai siti web per identificarsi sono validi soltanto per determinati periodi di tempo. L'orologio del dispositivo non è impostato sull'orario corretto, pertanto Chrome non può verificare i certificati.</translation>
<translation id="2972581237482394796">&amp;Ripeti</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, attualmente selezionata. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Tipo di criterio errato</translation>
<translation id="3037605927509011580">Uffa!</translation>
<translation id="3041612393474885105">Informazioni certificato</translation>
-<translation id="3063697135517575841">Al momento non è possibile confermare la carta in Chrome. Riprova più tardi.</translation>
<translation id="3064966200440839136">Per procedere al pagamento tramite un'applicazione esterna, uscirai dalla modalità di navigazione in incognito. Continuare?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Nessuna}=1{1 password}other{# password}}</translation>
<translation id="3096100844101284527">Aggiungi l'indirizzo di ritiro</translation>
@@ -338,7 +335,6 @@
<translation id="3305707030755673451">I tuoi dati sono stati criptati con la tua passphrase di sincronizzazione in data <ph name="TIME" />. Inseriscila per avviare la sincronizzazione.</translation>
<translation id="3320021301628644560">Aggiungi l'indirizzo di fatturazione</translation>
<translation id="3338095232262050444">Sicuro</translation>
-<translation id="3340978935015468852">impostazioni</translation>
<translation id="3345135638360864351">Impossibile inviare a <ph name="NAME" /> la richiesta di accesso a questo sito. Riprova.</translation>
<translation id="3355823806454867987">Modifica impostazioni proxy...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />non salverà<ph name="END_EMPHASIS" /> le seguenti informazioni:
@@ -372,7 +368,6 @@
<translation id="3528171143076753409">Il certificato del server non è affidabile.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Almeno 1 elemento sui dispositivi sincronizzati}=1{1 elemento (e altri sui dispositivi sincronizzati)}other{# elementi (e altri sui dispositivi sincronizzati)}}</translation>
<translation id="3539171420378717834">Conserva una copia di questa carta sul dispositivo</translation>
-<translation id="3542684924769048008">Utilizza password per:</translation>
<translation id="3549644494707163724">Cripta tutti i dati sincronizzati con la tua passphrase di sincronizzazione</translation>
<translation id="3556433843310711081">Il tuo gestore può sbloccarlo per te</translation>
<translation id="3566021033012934673">La connessione non è privata</translation>
@@ -458,7 +453,6 @@
<translation id="4171400957073367226">Firma di verifica non valida</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> altro elemento}other{Altri <ph name="ITEM_COUNT" /> elementi}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome ti consiglia di reimpostare la password di <ph name="ORG_NAME" /> se l'hai utilizzata su altri siti.</translation>
<translation id="4196861286325780578">&amp;Ripeti spostamento</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Controllare le configurazioni del firewall e antivirus<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Arresti anomali</translation>
@@ -487,7 +481,6 @@
<translation id="425582637250725228">Le modifiche apportate potrebbero non essere salvate.</translation>
<translation id="4258748452823770588">Firma errata</translation>
<translation id="4265872034478892965">Consentita dall'amministratore</translation>
-<translation id="4269787794583293679">(Nessun nome utente)</translation>
<translation id="4275830172053184480">Riavvia il dispositivo</translation>
<translation id="4277028893293644418">Reimposta password</translation>
<translation id="4280429058323657511">, scad.: <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -510,6 +503,7 @@
<translation id="4434045419905280838">Popup e reindirizzamenti</translation>
<translation id="443673843213245140">L'utilizzo di un proxy è stato disattivato ma è stata specificata una configurazione proxy esplicita.</translation>
<translation id="445100540951337728">Carte di debito accettate</translation>
+<translation id="4472575034687746823">Come iniziare</translation>
<translation id="4506176782989081258">Errore di convalida. <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Contattare l'amministratore di sistema</translation>
<translation id="450710068430902550">Condivisione con l'amministratore</translation>
@@ -534,8 +528,8 @@
<translation id="4726672564094551039">Ricarica criteri</translation>
<translation id="4728558894243024398">Piattaforma</translation>
<translation id="4736825316280949806">Riavvia Chromium</translation>
+<translation id="4742407542027196863">Gestisci password…</translation>
<translation id="4744603770635761495">Percorso eseguibile</translation>
-<translation id="4749685221585524849">Ultimo utilizzo: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Le tue informazioni (ad esempio password o numeri di carte di credito) restano private quando vengono inviate a questo sito.</translation>
<translation id="4756388243121344051">&amp;Cronologia</translation>
<translation id="4758311279753947758">Aggiungi informazioni di contatto</translation>
@@ -552,6 +546,7 @@
<translation id="4850886885716139402">Visualizza</translation>
<translation id="4854362297993841467">Questo metodo di consegna non è disponibile. Prova un metodo diverso.</translation>
<translation id="4858792381671956233">Hai chiesto ai tuoi genitori se puoi visitare questo sito</translation>
+<translation id="4876305945144899064">Nessun nome utente</translation>
<translation id="4880827082731008257">Cerca nella cronologia</translation>
<translation id="4881695831933465202">Apri</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -585,6 +580,7 @@
<translation id="5089810972385038852">Provincia</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="5098332213681597508">Questo nome proviene dal tuo account Google.</translation>
<translation id="5115563688576182185">(a 64 bit)</translation>
<translation id="5121084798328133320">Dopo essere stati confermati, i dati della carta del tuo account Google Payments saranno condivisi con questo sito.</translation>
<translation id="5128122789703661928">Impossibile eliminare la sessione perché il nome specificato non è valido.</translation>
@@ -625,9 +621,10 @@
<translation id="5332219387342487447">Modalità di spedizione</translation>
<translation id="5355557959165512791">Al momento non puoi visitare il sito <ph name="SITE" /> perché il relativo certificato è stato revocato. In genere gli errori di rete e gli attacchi sono temporanei, pertanto questa pagina potrebbe funzionare più tardi.</translation>
<translation id="536296301121032821">Archiviazione delle impostazioni criterio non riuscita</translation>
+<translation id="5371425731340848620">Aggiorna carta</translation>
<translation id="5377026284221673050">"L'orologio è indietro", "L'orologio è avanti" oppure "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">Il certificato di questo sito contiene un certificato che è stato firmato utilizzando SHA-1.</translation>
-<translation id="5402410679244714488">Scadenza: <ph name="EXPIRATION_DATE_ABBR" />, ultimo utilizzo oltre un anno fa</translation>
+<translation id="5387961145478138773">Accedi velocemente alle tue app Google preferite</translation>
<translation id="540969355065856584">Questo server non è riuscito a verificare che si tratta di <ph name="DOMAIN" />; il relativo certificato di sicurezza non è considerato valido in questa fase. Il problema potrebbe essere dovuto a un'errata configurazione o a un malintenzionato che ha intercettato la connessione.</translation>
<translation id="5421136146218899937">Cancella dati di navigazione...</translation>
<translation id="5430298929874300616">Rimuovi preferito</translation>
@@ -669,11 +666,13 @@
<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="5659593005791499971">Email</translation>
+<translation id="5666899935841546222">Vuoi tenere tutte le schede in un unico posto?</translation>
<translation id="5675650730144413517">La pagina non funziona</translation>
<translation id="5685654322157854305">Aggiungi l'indirizzo di spedizione</translation>
<translation id="5689199277474810259">Esporta in JSON</translation>
<translation id="5689516760719285838">Posizione</translation>
<translation id="570530837424789914">Gestisci…</translation>
+<translation id="57094364128775171">Suggerisci password efficace…</translation>
<translation id="5710435578057952990">L'identità di questo sito web non è stata verificata.</translation>
<translation id="5719499550583120431">Le carte prepagate sono accettate.</translation>
<translation id="5720705177508910913">Utente corrente</translation>
@@ -694,11 +693,9 @@
<translation id="5869405914158311789">Impossibile raggiungere il sito</translation>
<translation id="5869522115854928033">Password salvate</translation>
<translation id="5893752035575986141">Le carte di credito sono accettate.</translation>
-<translation id="5898382028489516745">Chromium ti consiglia di reimpostare la password di <ph name="ORG_NAME" /> se l'hai utilizzata su altri siti.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (sincronizzati)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 in uso}other{# in uso}}</translation>
<translation id="5939518447894949180">Ripristina</translation>
-<translation id="5959728338436674663">Invia automaticamente a Google <ph name="BEGIN_WHITEPAPER_LINK" />alcune informazioni sul sistema e alcuni contenuti delle pagine<ph name="END_WHITEPAPER_LINK" /> per contribuire a rilevare app e siti pericolosi. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Modifica informazioni di contatto</translation>
<translation id="5967867314010545767">Rimuovi da cronologia</translation>
<translation id="5975083100439434680">Diminuisci lo zoom</translation>
@@ -743,13 +740,11 @@
<translation id="6282194474023008486">Codice postale</translation>
<translation id="6290238015253830360">Gli articoli suggeriti vengono visualizzati qui</translation>
<translation id="6305205051461490394"><ph name="URL" /> non è raggiungibile.</translation>
-<translation id="6319915415804115995">Ultimo utilizzo oltre un anno fa</translation>
<translation id="6321917430147971392">Controlla le impostazioni DNS</translation>
<translation id="6328639280570009161">Prova a disattivare la previsione della rete</translation>
<translation id="6328786501058569169">Questo sito è ingannevole</translation>
<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="6342069812937806050">In questo momento</translation>
<translation id="6355080345576803305">Sostituzione sessione pubblica</translation>
<translation id="6358450015545214790">Che cosa significano?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 altro suggerimento}other{# altri suggerimenti}}</translation>
@@ -774,7 +769,6 @@
<translation id="6529602333819889595">&amp;Ripeti eliminazione</translation>
<translation id="6534179046333460208">Suggerimenti relativi al Physical Web</translation>
<translation id="6550675742724504774">Opzioni</translation>
-<translation id="6556915248009097796">Scadenza: <ph name="EXPIRATION_DATE_ABBR" />, ultimo utilizzo: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Il tuo gestore non ha ancora approvato la richiesta</translation>
<translation id="6569060085658103619">È visualizzata la pagina di un'estensione</translation>
<translation id="6596325263575161958">Opzioni di crittografia</translation>
@@ -803,15 +797,20 @@
<translation id="6825578344716086703">Hai tentato di accedere al sito <ph name="DOMAIN" />, ma il server ha presentato un certificato firmato utilizzando un algoritmo di firma debole (ad esempio SHA-1). Ciò significa che le credenziali di sicurezza presentate dal server potrebbero essere state falsificate e il server potrebbe non essere quello previsto (è possibile che tu stia comunicando con un malintenzionato).</translation>
<translation id="6831043979455480757">Traduci</translation>
<translation id="6839929833149231406">Area</translation>
+<translation id="6852204201400771460">Ricaricare l'app?</translation>
+<translation id="6865412394715372076">Al momento non è possibile verificare questa carta.</translation>
<translation id="6874604403660855544">&amp;Ripeti aggiunta</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Il livello della norma non è supportato.</translation>
<translation id="6895330447102777224">La carta è stata confermata</translation>
<translation id="6897140037006041989">User-agent</translation>
+<translation id="6903319715792422884">Contribuisci a migliorare la Navigazione sicura inviando a Google <ph name="BEGIN_WHITEPAPER_LINK" />informazioni di sistema e contenuti delle pagine<ph name="END_WHITEPAPER_LINK" />. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Utente:</translation>
+<translation id="6944692733090228304">Hai inserito la password in un sito non gestito da <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Per proteggere il tuo account, non riutilizzare la password con altri siti e app.</translation>
<translation id="6945221475159498467">Seleziona</translation>
<translation id="6948701128805548767">Seleziona un indirizzo per conoscere i requisiti e i metodi di ritiro</translation>
<translation id="6949872517221025916">Reimposta la password</translation>
+<translation id="6950684638814147129">Errore durante l'analisi del valore JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Il certificato del server risulta essere un falso.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Dispositivo</translation>
@@ -873,6 +872,7 @@
&lt;li&gt;Fai clic su &lt;strong&gt;Applica&lt;/strong&gt;, quindi su &lt;strong&gt;OK&lt;/strong&gt;.
&lt;li&gt;Visita il &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Centro assistenza Chrome&lt;/a&gt; per avere informazioni su come rimuovere definitivamente il software dal computer.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Gestisci password…</translation>
<translation id="7419106976560586862">Percorso profilo</translation>
<translation id="7437289804838430631">Aggiungi informazioni di contatto</translation>
<translation id="7441627299479586546">Oggetto del criterio errato</translation>
@@ -881,7 +881,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Leggere ulteriori informazioni<ph name="END_LINK" /> sul problema.</translation>
<translation id="7455133967321480974">Usa predefinita globale (Blocca)</translation>
<translation id="7460163899615895653">Le schede recenti di altri dispositivi sono mostrate qui</translation>
-<translation id="7469372306589899959">Conferma della carta…</translation>
<translation id="7473891865547856676">No grazie</translation>
<translation id="7481312909269577407">Avanti</translation>
<translation id="7485870689360869515">Nessun dato trovato.</translation>
@@ -1011,6 +1010,7 @@
<translation id="8308427013383895095">La traduzione non è riuscita a causa di un problema con la connessione di rete.</translation>
<translation id="8311129316111205805">Carica sessione</translation>
<translation id="8332188693563227489">Accesso a <ph name="HOST_NAME" /> negato</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Se sei consapevole dei rischi per la tua sicurezza, potresti <ph name="BEGIN_LINK" />visitare questo sito<ph name="END_LINK" /> senza aspettare che vengano rimossi i programmi pericolosi.</translation>
<translation id="8349305172487531364">Barra dei Preferiti</translation>
<translation id="8363502534493474904">Disattivare la modalità aereo</translation>
@@ -1031,20 +1031,24 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ha impiegato troppo tempo a rispondere.</translation>
<translation id="8503559462189395349">Password Chrome</translation>
<translation id="8503813439785031346">Nome utente</translation>
+<translation id="8508648098325802031">Icona Ricerca</translation>
<translation id="8543181531796978784">Puoi <ph name="BEGIN_ERROR_LINK" />segnalare un problema di rilevamento<ph name="END_ERROR_LINK" /> oppure, se sei consapevole dei rischi per la tua sicurezza, <ph name="BEGIN_LINK" />visita questo sito non sicuro<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Domande? Contatta il supervisore del tuo profilo.</translation>
<translation id="8553075262323480129">La traduzione non è riuscita perché non è stato possibile determinare la lingua della pagina.</translation>
<translation id="8557066899867184262">Il codice CVC si trova sul lato posteriore della carta.</translation>
<translation id="8559762987265718583">Impossibile stabilire una connessione privata con il sito <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> perché data e ora del dispositivo (<ph name="DATE_AND_TIME" />) sono sbagliate.</translation>
+<translation id="8564985650692024650">Chromium ti consiglia di reimpostare la password di <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, se l'hai utilizzata su altri siti.</translation>
<translation id="8571890674111243710">Traduzione della pagina in <ph name="LANGUAGE" /> in corso...</translation>
<translation id="858637041960032120">Aggiungi telefono</translation>
<translation id="859285277496340001">Il certificato non specifica un meccanismo per il controllo della sua revoca.</translation>
+<translation id="860043288473659153">Nome del titolare della carta</translation>
<translation id="8620436878122366504">I tuoi genitori non hanno ancora approvato la richiesta</translation>
<translation id="8625384913736129811">Salva la carta su questo dispositivo</translation>
-<translation id="8639963783467694461">Impostazioni di Compilazione automatica</translation>
+<translation id="8663226718884576429">Riepilogo ordine, <ph name="TOTAL_LABEL" />, altri dettagli</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, risposta, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">La connessione a <ph name="DOMAIN" /> non è criptata.</translation>
<translation id="8718314106902482036">Pagamento non completato</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, suggerimento di ricerca</translation>
<translation id="8725066075913043281">Riprova</translation>
<translation id="8728672262656704056">Sei passato alla navigazione in incognito</translation>
<translation id="8730621377337864115">Fine</translation>
@@ -1060,8 +1064,10 @@
<translation id="8820817407110198400">Preferiti</translation>
<translation id="883848425547221593">Altri Preferiti</translation>
<translation id="884264119367021077">Indirizzo di spedizione</translation>
+<translation id="8846319957959474018">Apri facilmente le app con i preferiti</translation>
<translation id="884923133447025588">Nessun sistema di revoca trovato.</translation>
<translation id="885730110891505394">Condivisione con Google</translation>
+<translation id="8858065207712248076">Chrome ti consiglia di reimpostare la password di <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, se l'hai utilizzata su altri siti.</translation>
<translation id="8866481888320382733">Errore durante l'analisi delle impostazioni criterio</translation>
<translation id="8870413625673593573">Chiusi di recente</translation>
<translation id="8874824191258364635">Inserisci un numero di carta di credito valido</translation>
@@ -1100,6 +1106,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> in genere utilizza la crittografia per proteggere le tue informazioni. Questa volta, quando Chromium ha provato a connettersi a <ph name="SITE" />, il sito web ha restituito credenziali insolite e sbagliate. È possibile che un malintenzionato stia cercando di spacciarsi per il sito <ph name="SITE" /> oppure che una schermata di accesso alla rete Wi-Fi abbia interrotto la connessione. Le tue informazioni sono ancora al sicuro perché Chromium ha interrotto la connessione prima che avvenissero scambi di dati.</translation>
<translation id="9106062320799175032">Aggiungi indirizzo di fatturazione</translation>
<translation id="910908805481542201">Aiutami a risolvere il problema</translation>
+<translation id="9114524666733003316">Conferma della carta...</translation>
<translation id="9128870381267983090">Collegati alla rete</translation>
<translation id="9137013805542155359">Mostra originale</translation>
<translation id="9137248913990643158">Accedi a Chrome prima di usare questa app.</translation>
@@ -1110,6 +1117,7 @@
<translation id="9168814207360376865">Consenti ai siti di controllare se hai metodi di pagamento salvati</translation>
<translation id="9169664750068251925">Blocca sempre su questo sito</translation>
<translation id="9170848237812810038">&amp;Annulla</translation>
+<translation id="9171296965991013597">Uscire dall'app?</translation>
<translation id="917450738466192189">Il certificato del server non è valido.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> utilizza un protocollo non supportato.</translation>
<translation id="9205078245616868884">I tuoi dati vengono criptati con la tua passphrase di sincronizzazione. Inseriscila per avviare la sincronizzazione.</translation>
diff --git a/chromium/components/strings/components_strings_iw.xtb b/chromium/components/strings/components_strings_iw.xtb
index af9e7724dcb..4a4d854899a 100644
--- a/chromium/components/strings/components_strings_iw.xtb
+++ b/chromium/components/strings/components_strings_iw.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">הסתר ערך</translation>
<translation id="1228893227497259893">מזהה יישות שגוי</translation>
<translation id="1232569758102978740">ללא שם</translation>
+<translation id="1250759482327835220">‏כדי לשלם מהר יותר בפעם הבאה, אפשר לשמור בחשבון Google את פרטי הכרטיס, השם והכתובת לחיוב.</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;
@@ -97,7 +98,6 @@
&lt;p&gt;שנה את התאריך והשעה בקטע &lt;strong&gt;כללי&lt;/strong&gt; באפליקציה &lt;strong&gt;הגדרות&lt;/strong&gt;‏.&lt;/p&gt;</translation>
<translation id="1583429793053364125">משהו השתבש בעת הצגת דף אינטרנט זה.</translation>
-<translation id="1590457302292452960">יצירת סיסמה חזקה...</translation>
<translation id="1592005682883173041">גישה לנתונים מקומיים</translation>
<translation id="1594030484168838125">בחר</translation>
<translation id="1620510694547887537">מצלמה</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">נסה לפנות אל מנהל המערכת.</translation>
<translation id="1740951997222943430">עליך להזין חודש תפוגה חוקי</translation>
+<translation id="1743520634839655729">‏כדי לשלם מהר יותר בפעם הבאה, אפשר לשמור בחשבון Google ובמכשיר הזה את פרטי הכרטיס, השם והכתובת לחיוב.</translation>
<translation id="17513872634828108">כרטיסיות פתוחות</translation>
<translation id="1753706481035618306">מספר דף</translation>
<translation id="1763864636252898013">השרת הזה לא הצליח להוכיח שהוא <ph name="DOMAIN" />. אישור האבטחה שלו לא נחשב כמהימן על ידי מערכת ההפעלה של המכשיר. ייתכן שהסיבה לכך היא תצורה שגויה או תוקף המיירט את החיבור שלך.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">כאן מופיעות הכרטיסיות שאתה פותח</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">שם בעל הכרטיס</translation>
-<translation id="1806541873155184440">תאריך הוספה: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">הבקשה או הפרמטרים של הבקשה אינם חוקיים</translation>
<translation id="1826516787628120939">מתבצעת בדיקה</translation>
<translation id="1834321415901700177">האתר הזה מכיל תוכניות מזיקות</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{הצעה אחת}two{שתי הצעות}many{# הצעות}other{# הצעות}}</translation>
<translation id="2079545284768500474">בטל פעולה</translation>
<translation id="20817612488360358">‏נקבע שימוש בהגדרות שרת Proxy של מערכת אך בנוסף מצוינת גם תצורה מפורשת של שרת Proxy.</translation>
-<translation id="2084558088529668945">הזנת את הסיסמה באתר שלא מנוהל על ידי <ph name="ORG_NAME" />. כדי להגן על החשבון שלך, מומלץ לא להזין את הסיסמה באפליקציות ובאתרים אחרים.</translation>
<translation id="2091887806945687916">צליל</translation>
<translation id="2094505752054353250">אי התאמה בדומיינים</translation>
<translation id="2096368010154057602">מחלקה</translation>
+<translation id="2102134110707549001">הצעת סיסמה חזקה…</translation>
<translation id="2108755909498034140">אתחול המחשב</translation>
<translation id="2113977810652731515">כרטיס</translation>
<translation id="2114841414352855701">המערכת התעלמה משום שהמדיניות בוטלה על ידי <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">‏שגיאת HTTP</translation>
<translation id="2270484714375784793">מספר טלפון</translation>
<translation id="2292556288342944218">הגישה לאינטרנט חסומה</translation>
-<translation id="230155334948463882">כרטיס חדש?</translation>
<translation id="2316887270356262533">‏פינוי של פחות מ-‎1 MB‎ מהשטח. ייתכן שחלק מהאתרים ייטענו לאט יותר בביקור הבא שלך.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> דורש שם משתמש וסיסמה.</translation>
<translation id="2317583587496011522">אפשר לשלם בכרטיסי חיוב.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">כרטיסים שהסוחר מקבל</translation>
<translation id="2702801445560668637">רשימת קריאה</translation>
<translation id="2704283930420550640">הערך לא תואם לפורמט.</translation>
-<translation id="2704951214193499422">‏ל-Chromium אין כרגע אפשרות לאשר את הכרטיס. נסה שוב מאוחר יותר.</translation>
<translation id="2705137772291741111">העותק השמור (בקובץ השמור) של האתר הזה היה בלתי קריא.</translation>
<translation id="2709516037105925701">מילוי אוטומטי</translation>
<translation id="2710942282213947212">‏תוכנה במחשב שלך מונעת מ-Chromium להתחבר באופן מאובטח לאינטרנט</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">שיטת משלוח</translation>
<translation id="277499241957683684">חסרה רשומת מכשיר</translation>
<translation id="2781030394888168909">‏ייצוא בפורמט ל-MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">החיבור עבר איפוס.</translation>
<translation id="2788784517760473862">כרטיסי אשראי שהסוחר מקבל</translation>
<translation id="2794233252405721443">אתר חסום</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">‏ניתן להשבית כל שרת proxy המוגדר לחיבור מדף ההגדרות.</translation>
<translation id="2955913368246107853">סגור את חלונית החיפוש</translation>
<translation id="2958431318199492670">‏תצורת הרשת אינה תואמת לתקן ONC. ייתכן שחלקים מהתצורה לא ייכללו בייבוא.</translation>
-<translation id="2966678944701946121">תאריך תפוגה: <ph name="EXPIRATION_DATE_ABBR" />, תאריך הוספה: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">‏כדי ליצור חיבור מאובטח, השעון צריך להיות מוגדר כהלכה. הסיבה לכך היא שהאישורים שבאמצעותם אתרים מזהים את עצמם תקפים רק למשך פרקי זמן מסוימים. מאחר שהשעון במכשיר שלך שגוי, Google Chrome לא יכול לאמת את האישורים האלה.</translation>
<translation id="2972581237482394796">&amp;בצע שנית</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, בחירה נוכחית. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">סוג המדיניות שגוי</translation>
<translation id="3037605927509011580">אוי, לא!</translation>
<translation id="3041612393474885105">פרטי אישור</translation>
-<translation id="3063697135517575841">‏Chrome לא הצליח לאשר את הכרטיס שלך הפעם. נסה שוב מאוחר יותר.</translation>
<translation id="3064966200440839136">בחרת לצאת ממצב גלישה בסתר כדי לשלם באמצעות יישום חיצוני. להמשיך?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{ללא}=1{סיסמה אחת}two{שתי סיסמאות}many{# סיסמאות}other{# סיסמאות}}</translation>
<translation id="3096100844101284527">הוספת כתובת לאיסוף</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">הנתונים שלך הוצפנו ב-<ph name="TIME" /> באמצעות ביטוי הסיסמה לסינכרון. הזן אותו כדי להתחיל בסינכרון.</translation>
<translation id="3320021301628644560">הוספה של כתובת לחיוב</translation>
<translation id="3338095232262050444">מאובטח</translation>
-<translation id="3340978935015468852">הגדרות</translation>
<translation id="3345135638360864351">לא ניתן היה לשלוח אל <ph name="NAME" /> את הבקשה שלך לגשת לאתר הזה. נסה שוב.</translation>
<translation id="3355823806454867987">‏שנה הגדרות שרת Proxy...</translation>
<translation id="3361596688432910856">‏Chrome <ph name="BEGIN_EMPHASIS" />לא ישמור<ph name="END_EMPHASIS" /> את המידע הבא:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">אישור השרת אינו מהימן.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{לפחות פריט אחד במכשירים מסונכרנים}=1{פריט אחד (ופריטים נוספים במכשירים מסונכרנים)}two{שני פריטים (ופריטים נוספים במכשירים מסונכרנים)}many{# פריטים (ופריטים נוספים במכשירים מסונכרנים)}other{# פריטים (ופריטים נוספים במכשירים מסונכרנים)}}</translation>
<translation id="3539171420378717834">שמור עותק של הכרטיס הזה במכשיר הזה</translation>
-<translation id="3542684924769048008">השתמש בסיסמה עבור:</translation>
<translation id="3549644494707163724">הצפן את כל הנתונים המסונכרנים באמצעות משפט הסיסמה שלך לסנכרון</translation>
<translation id="3556433843310711081">המנהל שלך יכול לבטל בשבילך את החסימה</translation>
<translation id="3566021033012934673">החיבור שלך אינו פרטי</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">חתימת אימות לא חוקית</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{פריט אחד (<ph name="ITEM_COUNT" />) נוסף}two{<ph name="ITEM_COUNT" /> פריטים נוספים}many{<ph name="ITEM_COUNT" /> פריטים נוספים}other{<ph name="ITEM_COUNT" /> פריטים נוספים}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">‏אם הזנת את הסיסמה של <ph name="ORG_NAME" /> באתרים אחרים, ההמלצה של Chrome היא לאפס אותה.</translation>
<translation id="4196861286325780578">&amp;ביצוע מחדש של העברה</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />לבדוק את תצורת האנטי-וירוס וחומת האש<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">קריסה</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">ייתכן שהשינויים שביצעת לא יישמרו.</translation>
<translation id="4258748452823770588">חתימה שגויה</translation>
<translation id="4265872034478892965">אושרה על-ידי מנהל המערכת</translation>
-<translation id="4269787794583293679">(אין שם משתמש)</translation>
<translation id="4275830172053184480">הפעלת המכשיר מחדש</translation>
<translation id="4277028893293644418">איפוס סיסמה</translation>
<translation id="4280429058323657511">, בתוקף עד <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">חלונות קופצים והפניות אוטומטיות</translation>
<translation id="443673843213245140">‏השימוש בשרת Proxy הושבת, אך צויינה תצורת שרת Proxy מפורשת.</translation>
<translation id="445100540951337728">כרטיסי חיוב שהסוחר מקבל</translation>
+<translation id="4472575034687746823">תחילת העבודה</translation>
<translation id="4506176782989081258">שגיאת אימות: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">לפנות אל מנהל המערכת</translation>
<translation id="450710068430902550">שיתוף עם מנהל מערכת</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">טען מדיניות מחדש</translation>
<translation id="4728558894243024398">פלטפורמה</translation>
<translation id="4736825316280949806">‏אתחול ה-Chromium</translation>
+<translation id="4742407542027196863">ניהול סיסמאות…</translation>
<translation id="4744603770635761495">נתיב להפעלה</translation>
-<translation id="4749685221585524849">שימוש אחרון: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">הפרטים שלך (כמו סיסמאות או מספרי כרטיסי אשראי) נשלחים לאתר הזה במצב פרטי.</translation>
<translation id="4756388243121344051">&amp;היסטוריה</translation>
<translation id="4758311279753947758">הוסף פרטים ליצירת קשר</translation>
@@ -559,6 +553,7 @@ Del</translation>
<translation id="4850886885716139402">הצג</translation>
<translation id="4854362297993841467">שיטת המסירה הזו אינה זמינה. עליך לבחור שיטה אחרת.</translation>
<translation id="4858792381671956233">שאלת את ההורים שלך אם אתה יכול לגשת לאתר הזה</translation>
+<translation id="4876305945144899064">אין שם משתמש</translation>
<translation id="4880827082731008257">חפש בהיסטוריה</translation>
<translation id="4881695831933465202">פתח</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -592,6 +587,7 @@ Del</translation>
<translation id="5089810972385038852">מדינה</translation>
<translation id="5094747076828555589">‏השרת הזה לא הצליח להוכיח שהוא <ph name="DOMAIN" />. אישור האבטחה שלו לא נחשב כמהימן על ידי Chromium. ייתכן שהסיבה לכך היא תצורה שגויה או תוקף המיירט את החיבור שלך.</translation>
<translation id="5095208057601539847">פרובינציה</translation>
+<translation id="5098332213681597508">‏השם הזה הוא מחשבון Google שלך.</translation>
<translation id="5115563688576182185">(64 סיביות)</translation>
<translation id="5121084798328133320">‏אחרי שנקבל ממך אישור, נשתף עם האתר הזה את פרטי הכרטיס מחשבון תשלומי Google.</translation>
<translation id="5128122789703661928">שם ההפעלה למחיקה אינו חוקי.</translation>
@@ -600,7 +596,7 @@ Del</translation>
<translation id="5145883236150621069">קיים קוד שגיאה בתגובת המדיניות</translation>
<translation id="5159010409087891077">‏פתח את הדף בחלון חדש של גלישה בסתר (‎⇧⌘N)</translation>
<translation id="5169827969064885044">‏ייתכן שלא יתאפשר לך לגשת לחשבון הארגוני, והזהות שלך עלולה להיגנב. לגלישה בטוחה ב-Chrome, מומלץ לשנות את הסיסמה עכשיו.</translation>
-<translation id="5171045022955879922">חפש או הקלד כתובת אתר</translation>
+<translation id="5171045022955879922">טקסט או כתובת אתר לחיפוש</translation>
<translation id="5172758083709347301">מכונה</translation>
<translation id="5179510805599951267">לא ב<ph name="ORIGINAL_LANGUAGE" />? דיווח על שגיאה זו</translation>
<translation id="5190835502935405962">סרגל הסימניות</translation>
@@ -632,9 +628,10 @@ Del</translation>
<translation id="5332219387342487447">שיטת משלוח</translation>
<translation id="5355557959165512791">נכון לעכשיו אי אפשר לבקר באתר <ph name="SITE" /> מאחר שהאישור שלו בוטל. שגיאות רשת ומתקפות הן בדרך כלל זמניות, כך שהדף הזה יחזור כנראה לפעול מאוחר יותר.</translation>
<translation id="536296301121032821">אחסון הגדרות המדיניות נכשל</translation>
+<translation id="5371425731340848620">עדכון כרטיס</translation>
<translation id="5377026284221673050">‏"השעון מאחר", "השעון מקדים" או "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">‏שרשרת האישורים של האתר הזה כוללת אישור שנחתם באמצעות SHA-1.</translation>
-<translation id="5402410679244714488">תאריך תפוגה: <ph name="EXPIRATION_DATE_ABBR" />, שימוש אחרון היה לפני שנה</translation>
+<translation id="5387961145478138773">‏קבלת גישה מהירה אל אפליקציות Google האהובות</translation>
<translation id="540969355065856584">שרת זה לא הצליח להוכיח שהוא <ph name="DOMAIN" />; אישור האבטחה שלו אינו תקף כעת. הסיבה לכך עשויה להיות תצורה שגויה או שתוקף מיירט את החיבור שלך.</translation>
<translation id="5421136146218899937">נקה נתוני גלישה...</translation>
<translation id="5430298929874300616">הסר סימניה</translation>
@@ -677,11 +674,13 @@ Del</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="5659593005791499971">אימייל</translation>
+<translation id="5666899935841546222">להציג את כל הכרטיסים שלך במקום אחד?</translation>
<translation id="5675650730144413517">הדף הזה לא עובד</translation>
<translation id="5685654322157854305">הוספת כתובת למשלוח</translation>
<translation id="5689199277474810259">‏ייצוא אל JSON</translation>
<translation id="5689516760719285838">מיקום</translation>
<translation id="570530837424789914">ניהול...</translation>
+<translation id="57094364128775171">הצעת סיסמה חזקה…</translation>
<translation id="5710435578057952990">הזהות של אתר זה לא אומתה.</translation>
<translation id="5719499550583120431">אפשר לשלם באמצעות כרטיסים משולמים מראש.</translation>
<translation id="5720705177508910913">משתמש נוכחי:</translation>
@@ -702,11 +701,9 @@ Del</translation>
<translation id="5869405914158311789">לא ניתן לגשת לאתר הזה</translation>
<translation id="5869522115854928033">סיסמאות שמורות</translation>
<translation id="5893752035575986141">אפשר לשלם באמצעות כרטיסי אשראי.</translation>
-<translation id="5898382028489516745">‏אם הזנת את הסיסמה של <ph name="ORG_NAME" /> באתרים אחרים, ההמלצה של Chromium היא לאפס אותה.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (מסונכרנים)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{אחד נמצא בשימוש}two{שניים נמצאים בשימוש}many{# נמצאים בשימוש}other{# נמצאים בשימוש}}</translation>
<translation id="5939518447894949180">אפס</translation>
-<translation id="5959728338436674663">‏שלח באופן אוטומטי <ph name="BEGIN_WHITEPAPER_LINK" />חלק מפרטי המערכת ותוכן הדף<ph name="END_WHITEPAPER_LINK" /> אל Google כדי לעזור בזיהוי של אפליקציות ואתרים מסוכנים. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">עריכת הפרטים ליצירת קשר</translation>
<translation id="5967867314010545767">הסר מההיסטוריה</translation>
<translation id="5975083100439434680">התרחק</translation>
@@ -752,13 +749,11 @@ Del</translation>
<translation id="6282194474023008486">מיקוד</translation>
<translation id="6290238015253830360">הצעות של מאמרים עבורך מופיעות כאן</translation>
<translation id="6305205051461490394">לא ניתן לגשת אל <ph name="URL" />.</translation>
-<translation id="6319915415804115995">שימוש אחרון היה לפני שנה</translation>
<translation id="6321917430147971392">‏בדוק את הגדרות ה-DNS</translation>
<translation id="6328639280570009161">נסה להשבית את חיזוי הרשת</translation>
<translation id="6328786501058569169">האתר הזה מטעה</translation>
<translation id="6337133576188860026">פינוי של פחות מ-<ph name="SIZE" /> מהשטח. ייתכן שחלק מהאתרים ייטענו לאט יותר בביקור הבא שלך.</translation>
<translation id="6337534724793800597">סנן מדיניות לפי שם</translation>
-<translation id="6342069812937806050">זה עתה</translation>
<translation id="6355080345576803305">שינוי הפעלה ציבורית</translation>
<translation id="6358450015545214790">מה זה אומר?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{הצעה אחת נוספת}two{שתי הצעות נוספות}many{# הצעות נוספות}other{# הצעות נוספות}}</translation>
@@ -783,7 +778,6 @@ Del</translation>
<translation id="6529602333819889595">&amp;ביצוע מחדש של מחיקה</translation>
<translation id="6534179046333460208">הצעות לאינטרנט הווירטופיזי</translation>
<translation id="6550675742724504774">אפשרויות</translation>
-<translation id="6556915248009097796">תאריך תפוגה: <ph name="EXPIRATION_DATE_ABBR" />, שימוש אחרון: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">המנהל שלך עדיין לא אישר זאת</translation>
<translation id="6569060085658103619">אתה מציג דף של תוסף</translation>
<translation id="6596325263575161958">אפשרויות הצפנה</translation>
@@ -812,15 +806,20 @@ Del</translation>
<translation id="6825578344716086703">‏ניסית להגיע אל <ph name="DOMAIN" />, אבל השרת הציג אישור שנחתם באמצעות אלגוריתם חתימה חלש (כמו SHA-1). המשמעות היא שפרטי האבטחה שהוצגו על-ידי השרת עלולים להיות מזויפים, וייתכן שהשרת הוא לא השרת שציפית לו (ייתכן שנוצר קשר בינך לבין התוקף).</translation>
<translation id="6831043979455480757">תרגם</translation>
<translation id="6839929833149231406">אזור</translation>
+<translation id="6852204201400771460">לטעון מחדש את האפליקציה?</translation>
+<translation id="6865412394715372076">אי אפשר לאמת כרגע את הכרטיס</translation>
<translation id="6874604403660855544">&amp;ביצוע מחדש של הוספה</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">רמת המדיניות אינה נתמכת.</translation>
<translation id="6895330447102777224">הכרטיס שלך מאושר</translation>
<translation id="6897140037006041989">User agent</translation>
+<translation id="6903319715792422884">‏על-ידי שליחה של חלק מ<ph name="BEGIN_WHITEPAPER_LINK" />פרטי המערכת ותוכן הדפים<ph name="END_WHITEPAPER_LINK" /> אל Google, אפשר לעזור בשיפור של 'גלישה בטוחה'. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">משתמש:</translation>
+<translation id="6944692733090228304">הזנת את הסיסמה שלך באתר שלא מנוהל על ידי <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. כדי להגן על החשבון, מומלץ לא להשתמש בסיסמה שלך באפליקציות ובאתרים אחרים.</translation>
<translation id="6945221475159498467">בחר</translation>
<translation id="6948701128805548767">עליך לבחור כתובת כדי לראות שיטות איסוף ודרישות</translation>
<translation id="6949872517221025916">איפוס סיסמה</translation>
+<translation id="6950684638814147129">‏קרתה שגיאה בזמן ניתוח ערך JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">נראה שהאישור של השרת מזויף.</translation>
<translation id="6965382102122355670">אישור</translation>
<translation id="6965978654500191972">התקן</translation>
@@ -882,6 +881,7 @@ Del</translation>
&lt;li&gt;לוחצים על &lt;strong&gt;הפעלה&lt;/strong&gt; ולאחר מכן לוחצים על &lt;strong&gt;אישור&lt;/strong&gt;
&lt;li&gt;עוברים אל &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;מרכז העזרה של Chrome&lt;/a&gt; כדי ללמוד כיצד להסיר את התוכנה מהמחשב
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">ניהול סיסמאות…</translation>
<translation id="7419106976560586862">נתיב פרופיל</translation>
<translation id="7437289804838430631">הוספת פרטים ליצירת קשר</translation>
<translation id="7441627299479586546">נושא המדיניות שגוי</translation>
@@ -890,7 +890,6 @@ Del</translation>
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />למידע נוסף<ph name="END_LINK" /> על בעיה זו.</translation>
<translation id="7455133967321480974">השתמש בברירת המחדל הכללית (חסום)</translation>
<translation id="7460163899615895653">הכרטיסיות האחרונות שפתחת במכשירים אחרים מוצגות כאן</translation>
-<translation id="7469372306589899959">מאשר את הכרטיס</translation>
<translation id="7473891865547856676">לא, תודה</translation>
<translation id="7481312909269577407">קדימה</translation>
<translation id="7485870689360869515">לא נמצאו נתונים.</translation>
@@ -1020,6 +1019,7 @@ Del</translation>
<translation id="8308427013383895095">התרגום נכשל עקב בעיה בחיבור הרשת.</translation>
<translation id="8311129316111205805">טעינת הפעלה</translation>
<translation id="8332188693563227489">הגישה ל-<ph name="HOST_NAME" /> נדחתה</translation>
+<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="8349305172487531364">סרגל סימניות</translation>
<translation id="8363502534493474904">לכבות את מצב הטיסה</translation>
@@ -1040,21 +1040,25 @@ Del</translation>
<translation id="8498891568109133222">ל-<ph name="HOST_NAME" /> נדרש זמן רב מדי להגיב.</translation>
<translation id="8503559462189395349">‏סיסמאות Chrome</translation>
<translation id="8503813439785031346">שם משתמש</translation>
+<translation id="8508648098325802031">סמל החיפוש</translation>
<translation id="8543181531796978784">באפשרותך <ph name="BEGIN_ERROR_LINK" />לדווח על בעיית זיהוי<ph name="END_ERROR_LINK" /> או, אם אתה מבין את סיכוני האבטחה, <ph name="BEGIN_LINK" />להיכנס לאתר הזה, שאינו מאובטח<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">יש לך שאלות? עליך לפנות לאיש הקשר שמפקח על הפרופיל שלך.</translation>
<translation id="8553075262323480129">התרגום נכשל כיוון שלא הייתה אפשרות לקבוע את שפת הדף.</translation>
<translation id="8557066899867184262">קוד האימות נמצא בגב הכרטיס.</translation>
<translation id="8559762987265718583">לא ניתן ליצור חיבור פרטי אל <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> מפני שהתאריך והשעה (<ph name="DATE_AND_TIME" />) במכשיר שלך שגויים.</translation>
+<translation id="8564985650692024650">‏אם הזנת את הסיסמה של <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> באתרים אחרים, ההמלצה של Chromium היא לאפס אותה.</translation>
<translation id="8571890674111243710">מתרגם דף ל<ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">הוסף מספר טלפון
</translation>
<translation id="859285277496340001">האישור אינו מציין מנגנון הבודק אם הוא נשלל.</translation>
+<translation id="860043288473659153">שם בעל הכרטיס</translation>
<translation id="8620436878122366504">ההורים שלך עדיין לא אישרו זאת</translation>
<translation id="8625384913736129811">שמירת כרטיס זה במכשיר הנוכחי</translation>
-<translation id="8639963783467694461">הגדרות מילוי אוטומטי</translation>
+<translation id="8663226718884576429">סיכום הזמנה, <ph name="TOTAL_LABEL" />, פרטים נוספים</translation>
<translation id="8680536109547170164"><ph name="QUERY" /> , תשובה, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">ההתחברות שלך אל <ph name="DOMAIN" /> אינה מוצפנת.</translation>
<translation id="8718314106902482036">התשלום לא הושלם</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, הצעת חיפוש</translation>
<translation id="8725066075913043281">נסה שוב</translation>
<translation id="8728672262656704056">עברת למצב גלישה בסתר</translation>
<translation id="8730621377337864115">בוצע</translation>
@@ -1070,8 +1074,10 @@ Del</translation>
<translation id="8820817407110198400">סימניות</translation>
<translation id="883848425547221593">סימניות אחרות</translation>
<translation id="884264119367021077">כתובת למשלוח</translation>
+<translation id="8846319957959474018">סימניות עוזרות לפתוח אפליקציות במהירות</translation>
<translation id="884923133447025588">לא נמצא מנגנון ביטול</translation>
<translation id="885730110891505394">‏שיתוף עם Google</translation>
+<translation id="8858065207712248076">‏אם הזנת את הסיסמה של <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> באתרים אחרים, ההמלצה של Chrome היא לאפס אותה.</translation>
<translation id="8866481888320382733">שגיאה בניתוח הגדרות המדיניות</translation>
<translation id="8870413625673593573">נסגרו לאחרונה</translation>
<translation id="8874824191258364635">עליך להזין מספר כרטיס חוקי</translation>
@@ -1110,6 +1116,7 @@ Del</translation>
<translation id="9103872766612412690">‏האתר <ph name="SITE" /> משתמש בדרך כלל בהצפנה כדי להגן על המידע שלך. כאשר Chromium ניסה הפעם להתחבר ל-<ph name="SITE" />, האתר שלח חזרה אישורים חריגים ושגויים. ייתכן שתוקף מנסה להתחזות לאתר <ph name="SITE" />, או שמסך כניסה ל-Wi-Fi הפריע לחיבור. המידע שלך עדיין מאובטח מכיוון ש-Chromium הפסיק את החיבור לפני חילופי הנתונים.</translation>
<translation id="9106062320799175032">הוספה של כתובת לחיוב</translation>
<translation id="910908805481542201">איך פותרים את הבעיה?</translation>
+<translation id="9114524666733003316">אישור הכרטיס מתבצע...</translation>
<translation id="9128870381267983090">התחבר לרשת</translation>
<translation id="9137013805542155359">הצג מקור</translation>
<translation id="9137248913990643158">‏היכנס לחשבונך ב-Chrome לפני שתשתמש באפליקציה הזו.</translation>
@@ -1120,6 +1127,7 @@ Del</translation>
<translation id="9168814207360376865">מתן הרשאה לאתרים לבדוק אם שמרת אמצעי תשלום</translation>
<translation id="9169664750068251925">חסום תמיד באתר זה</translation>
<translation id="9170848237812810038">&amp;ביטול</translation>
+<translation id="9171296965991013597">לצאת מהאפליקציה?</translation>
<translation id="917450738466192189">אישור השרת לא חוקי.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> משתמש בפרוטוקול שאינו נתמך.</translation>
<translation id="9205078245616868884">הנתונים שלך מוצפנים באמצעות ביטוי הסיסמה לסינכרון. הזן אותו כדי להתחיל בסינכרון.</translation>
diff --git a/chromium/components/strings/components_strings_ja.xtb b/chromium/components/strings/components_strings_ja.xtb
index 7329e471caf..c7aeb65a03c 100644
--- a/chromium/components/strings/components_strings_ja.xtb
+++ b/chromium/components/strings/components_strings_ja.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">値を非表示</translation>
<translation id="1228893227497259893">エンティティ識別子が正しくありません</translation>
<translation id="1232569758102978740">無題</translation>
+<translation id="1250759482327835220">カード、お名前、請求先住所を Google アカウントに保存すると、次回のお支払いが簡単になります。</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;
@@ -97,7 +98,6 @@
&lt;p&gt;&lt;strong&gt;設定&lt;/strong&gt;アプリの [&lt;strong&gt;全般&lt;/strong&gt;] セクションで日時を調整してください。&lt;/p&gt;</translation>
<translation id="1583429793053364125">このウェブページを表示中に問題が発生しました。</translation>
-<translation id="1590457302292452960">安全性の高いパスワードを生成...</translation>
<translation id="1592005682883173041">ローカルデータへのアクセス</translation>
<translation id="1594030484168838125">選択</translation>
<translation id="1620510694547887537">カメラ</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">AMEX</translation>
<translation id="1734878702283171397">システム管理者にお問い合わせください。</translation>
<translation id="1740951997222943430">有効期限(月)を正しい形式で入力してください</translation>
+<translation id="1743520634839655729">カード、お名前、請求先住所を Google アカウントとこの端末に保存すると、次回のお支払いが簡単になります。</translation>
<translation id="17513872634828108">開いているタブ</translation>
<translation id="1753706481035618306">ページ番号</translation>
<translation id="1763864636252898013">このサーバーが <ph name="DOMAIN" /> であることを確認できませんでした。このサーバーのセキュリティ証明書は、ご使用のデバイスのオペレーティング システムによって信頼されているものではありません。原因としては、不適切な設定や、悪意のあるユーザーによる接続妨害が考えられます。</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">最近開いたタブがここに表示されます</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">カード名義人</translation>
-<translation id="1806541873155184440">追加日: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">無効なリクエストまたはリクエスト パラメータです</translation>
<translation id="1826516787628120939">確認中</translation>
<translation id="1834321415901700177">このサイトには有害なプログラムが含まれています</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 件の候補}other{# 件の候補}}</translation>
<translation id="2079545284768500474">元に戻す</translation>
<translation id="20817612488360358">システム プロキシ設定を使用するように設定されていますが、明示的なプロキシの設定も指定されています。</translation>
-<translation id="2084558088529668945"><ph name="ORG_NAME" /> が管理していないサイトでパスワードを入力しました。アカウントを保護するには、他のアプリやサイトでパスワードを再使用しないでください。</translation>
<translation id="2091887806945687916">音声</translation>
<translation id="2094505752054353250">ドメインが一致しません</translation>
<translation id="2096368010154057602">県</translation>
+<translation id="2102134110707549001">安全なパスワードを自動生成…</translation>
<translation id="2108755909498034140">パソコンを再起動する</translation>
<translation id="2113977810652731515">カード</translation>
<translation id="2114841414352855701"><ph name="POLICY_NAME" /> によって上書きされるため無視されます。</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP エラーです</translation>
<translation id="2270484714375784793">電話番号</translation>
<translation id="2292556288342944218">インターネット アクセスがブロックされています</translation>
-<translation id="230155334948463882">カード情報を更新</translation>
<translation id="2316887270356262533">最大で 1 MB を解放します。サイトによっては、次回アクセスする際に読み込みに時間がかかる可能性があります。</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> にはユーザー名とパスワードが必要です。</translation>
<translation id="2317583587496011522">デビットカードをご利用いただけます。</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">利用可能なカード</translation>
<translation id="2702801445560668637">リーディング リスト</translation>
<translation id="2704283930420550640">値が有効な形式ではありません。</translation>
-<translation id="2704951214193499422">Chromium でカードを確認できませんでした。しばらくしてからもう一度お試しください。</translation>
<translation id="2705137772291741111">このサイトの保存(キャッシュ)されたコピーを読み取れませんでした。</translation>
<translation id="2709516037105925701">自動入力</translation>
<translation id="2710942282213947212">パソコンにインストールされているソフトウェアが原因で、Chromium からインターネットに安全に接続することができません</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">配送方法</translation>
<translation id="277499241957683684">デバイス レコードがありません</translation>
<translation id="2781030394888168909">Mac OS 形式でエクスポート</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">接続がリセットされました。</translation>
<translation id="2788784517760473862">利用可能なクレジット カード</translation>
<translation id="2794233252405721443">サイトがブロックされています</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">接続用に設定されたプロキシは、設定ページで無効にできます。</translation>
<translation id="2955913368246107853">検索バーを閉じる</translation>
<translation id="2958431318199492670">ネットワーク設定が ONC 標準に準拠していません。設定の一部がインポートされない可能性があります。</translation>
-<translation id="2966678944701946121">有効期限: <ph name="EXPIRATION_DATE_ABBR" />、追加日: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">安全な接続を確立するには、時計が正しく設定されている必要があります。これは、ウェブサイトが自身を証明するために使用する証明書には有効期限があるためです。デバイスの時計が正しくないため、Google Chrome でこれらの証明書を確認することができません。</translation>
<translation id="2972581237482394796">やり直し(&amp;R)</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />が現在選択されています。<ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">ポリシー タイプが間違っています</translation>
<translation id="3037605927509011580">エラー</translation>
<translation id="3041612393474885105">証明書情報</translation>
-<translation id="3063697135517575841">Chrome でカードを確認できませんでした。しばらくしてからもう一度お試しください。</translation>
<translation id="3064966200440839136">外部アプリケーションを経由したお支払いの処理に進むため、シークレット モードを解除します。続行しますか?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{なし}=1{1 個のパスワード}other{# 個のパスワード}}</translation>
<translation id="3096100844101284527">集荷先住所を追加</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">データは <ph name="TIME" /> に同期パスフレーズで暗号化されました。同期を開始するには、同期パスフレーズを入力してください。</translation>
<translation id="3320021301628644560">請求先住所を追加</translation>
<translation id="3338095232262050444">保護された通信</translation>
-<translation id="3340978935015468852">設定</translation>
<translation id="3345135638360864351">このサイトにアクセスするためのリクエストを <ph name="NAME" /> に送信できませんでした。もう一度お試しください。</translation>
<translation id="3355823806454867987">プロキシ設定の変更...</translation>
<translation id="3361596688432910856">Chrome には、<ph name="BEGIN_EMPHASIS" />次の情報は<ph name="END_EMPHASIS" />保存されません。
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">サーバーの証明書を信頼できません。</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{同期デバイスで 1 件以上のアイテム}=1{1 件のアイテム(同期デバイスではそれ以上のアイテム)}other{# 件のアイテム(同期デバイスではそれ以上のアイテム)}}</translation>
<translation id="3539171420378717834">このデバイスにこのカード情報のコピーを保存する</translation>
-<translation id="3542684924769048008">次のパスワードを使用:</translation>
<translation id="3549644494707163724">同期パスフレーズで同期データをすべて暗号化する</translation>
<translation id="3556433843310711081">ブロックの解除は管理者が行うことができます</translation>
<translation id="3566021033012934673">この接続ではプライバシーが保護されません</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">確認用の署名に問題があります</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{他 <ph name="ITEM_COUNT" /> 件のアイテム}other{他 <ph name="ITEM_COUNT" /> 件のアイテム}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268"><ph name="ORG_NAME" /> パスワードを他のサイトで再使用した場合、Chrome ではパスワードの再設定を促すメッセージが表示されます。</translation>
<translation id="4196861286325780578">移動のやり直し(&amp;R)</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ファイアウォールとウイルス対策の設定を確認する<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">クラッシュ</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">行った変更が保存されない可能性があります。</translation>
<translation id="4258748452823770588">署名が不適切です</translation>
<translation id="4265872034478892965">管理者によって許可</translation>
-<translation id="4269787794583293679">(ユーザー名なし)</translation>
<translation id="4275830172053184480">デバイスの再起動</translation>
<translation id="4277028893293644418">パスワードを再設定</translation>
<translation id="4280429058323657511">、有効期限: <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">ポップアップとリダイレクト</translation>
<translation id="443673843213245140">プロキシの使用は無効ですが、プロキシの設定が明示的に指定されています。</translation>
<translation id="445100540951337728">利用可能なデビットカード</translation>
+<translation id="4472575034687746823">利用を開始</translation>
<translation id="4506176782989081258">検証エラー: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">システム管理者に問い合わせる</translation>
<translation id="450710068430902550">管理者との共有</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">ポリシーを再読み込み</translation>
<translation id="4728558894243024398">プラットフォーム</translation>
<translation id="4736825316280949806">Chromium を再起動する</translation>
+<translation id="4742407542027196863">パスワードを管理…</translation>
<translation id="4744603770635761495">実行ファイルのパス</translation>
-<translation id="4749685221585524849">最終使用日: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">お客様がこのサイトに送信した情報(パスワード、クレジット カード番号など)が第三者に見られることはありません。</translation>
<translation id="4756388243121344051">履歴(&amp;H)</translation>
<translation id="4758311279753947758">連絡先情報を追加</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">表示</translation>
<translation id="4854362297993841467">この配達方法はご利用いただけません。別の方法を選択してください。</translation>
<translation id="4858792381671956233">このサイトを開いてもよいかの問い合わせを保護者に送信しました</translation>
+<translation id="4876305945144899064">ユーザー名が指定されていません</translation>
<translation id="4880827082731008257">履歴を検索</translation>
<translation id="4881695831933465202">開く</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />、<ph name="TYPE_2" />、<ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">都道府県 / 州</translation>
<translation id="5094747076828555589">このサーバーが <ph name="DOMAIN" /> であることを確認できませんでした。このサーバーのセキュリティ証明書は Chromium によって信頼されているものではありません。原因としては、不適切な設定や、悪意のあるユーザーによる接続妨害が考えられます。</translation>
<translation id="5095208057601539847">地方</translation>
+<translation id="5098332213681597508">Google アカウントで設定されている名前です。</translation>
<translation id="5115563688576182185">(64 ビット)</translation>
<translation id="5121084798328133320">確認後、Google Payments アカウントのカード情報がこのサイトと共有されます。</translation>
<translation id="5128122789703661928">セッションの名前が有効でないため、削除できません。</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">配送方法</translation>
<translation id="5355557959165512791"><ph name="SITE" /> は証明書が失効しているため、現在アクセスできません。通常、ネットワーク エラーやネットワークへの攻撃は一時的なものです。しばらくするとページにアクセスできるようになります。</translation>
<translation id="536296301121032821">ポリシー設定を保存できませんでした</translation>
+<translation id="5371425731340848620">カードを更新</translation>
<translation id="5377026284221673050">「時計が遅れています」、「時計が進んでいます」、「&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;」</translation>
<translation id="5386426401304769735">このサイトの証明書チェーンには SHA-1 を使って署名された証明書が含まれています。</translation>
-<translation id="5402410679244714488">有効期限: <ph name="EXPIRATION_DATE_ABBR" />、最終使用日: 1 年以上前</translation>
+<translation id="5387961145478138773">お気に入りの Google アプリにすばやくアクセスします</translation>
<translation id="540969355065856584">このサーバーが <ph name="DOMAIN" /> であることを確認できませんでした。このサーバーのセキュリティ証明書は現在有効ではありません。設定が不適切か、悪意のあるユーザーによって接続が妨害されている可能性があります。</translation>
<translation id="5421136146218899937">閲覧履歴データの消去...</translation>
<translation id="5430298929874300616">ブックマークを削除</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">メール</translation>
+<translation id="5666899935841546222">すべてのカードを 1 か所で管理しますか?</translation>
<translation id="5675650730144413517">このページは動作していません</translation>
<translation id="5685654322157854305">配送先住所を追加</translation>
<translation id="5689199277474810259">JSON にエクスポート</translation>
<translation id="5689516760719285838">現在地</translation>
<translation id="570530837424789914">管理...</translation>
+<translation id="57094364128775171">安全なパスワードを自動生成…</translation>
<translation id="5710435578057952990">このウェブサイトの ID は確認されていません。</translation>
<translation id="5719499550583120431">プリペイド カードをご利用いただけます。</translation>
<translation id="5720705177508910913">現在のユーザー</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">このサイトにアクセスできません</translation>
<translation id="5869522115854928033">保存したパスワード</translation>
<translation id="5893752035575986141">クレジット カードをご利用いただけます。</translation>
-<translation id="5898382028489516745"><ph name="ORG_NAME" /> パスワードを他のサイトで再使用した場合、Chromium ではパスワードの再設定を促すメッセージが表示されます。</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" />(同期済み)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 個が使用中}other{# 個が使用中}}</translation>
<translation id="5939518447894949180">リセット</translation>
-<translation id="5959728338436674663">危険なアプリやサイトの検出に役立てるために一部の<ph name="BEGIN_WHITEPAPER_LINK" />システム情報やページのコンテンツ<ph name="END_WHITEPAPER_LINK" />を Google に自動送信する。<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">連絡先情報の編集</translation>
<translation id="5967867314010545767">履歴から削除</translation>
<translation id="5975083100439434680">縮小する</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">郵便番号</translation>
<translation id="6290238015253830360">おすすめの記事がここに表示されます</translation>
<translation id="6305205051461490394"><ph name="URL" /> にアクセスできません。</translation>
-<translation id="6319915415804115995">最終使用日: 1 年以上前</translation>
<translation id="6321917430147971392">DNS 設定を確認してください</translation>
<translation id="6328639280570009161">ネットワーク予測を無効にしてみてください</translation>
<translation id="6328786501058569169">これは偽のサイトです</translation>
<translation id="6337133576188860026">最大で <ph name="SIZE" /> を解放します。サイトによっては、次回アクセスする際に読み込みに時間がかかる可能性があります。</translation>
<translation id="6337534724793800597">ポリシーを名前でフィルタ</translation>
-<translation id="6342069812937806050">たった今</translation>
<translation id="6355080345576803305">公開セッションのオーバーライド</translation>
<translation id="6358450015545214790">ヘルプ</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{他 1 件の候補}other{他 # 件の候補}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">削除のやり直し(&amp;R)</translation>
<translation id="6534179046333460208">フィジカル ウェブからの URL</translation>
<translation id="6550675742724504774">オプション</translation>
-<translation id="6556915248009097796">有効期限: <ph name="EXPIRATION_DATE_ABBR" />、最終使用日: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">管理者がまだサイトを開くことを許可していません</translation>
<translation id="6569060085658103619">拡張機能のページを表示しています</translation>
<translation id="6596325263575161958">暗号化オプション</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703"><ph name="DOMAIN" /> にアクセスしようとしましたが、脆弱な署名アルゴリズム(SHA-1 など)を使用して署名された証明書がサーバーから提示されました。このセキュリティ認証情報は偽装されたものである可能性があり、アクセスしようとしたサーバーとは別のサーバーが応答している可能性があります(悪意のあるユーザーと通信している可能性があります)。</translation>
<translation id="6831043979455480757">翻訳</translation>
<translation id="6839929833149231406">地域</translation>
+<translation id="6852204201400771460">アプリを再読み込みしますか?</translation>
+<translation id="6865412394715372076">現在、このカードを確認できません</translation>
<translation id="6874604403660855544">追加のやり直し(&amp;R)</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">ポリシーレベルがサポートされていません。</translation>
<translation id="6895330447102777224">カードを確認しました</translation>
<translation id="6897140037006041989">ユーザー エージェント</translation>
+<translation id="6903319715792422884"><ph name="BEGIN_WHITEPAPER_LINK" />一部のシステム情報とページのコンテンツ<ph name="END_WHITEPAPER_LINK" />を Google に送信して、セーフ ブラウジングの改善にご協力ください。<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">ユーザー:</translation>
+<translation id="6944692733090228304"><ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> が管理していないサイトでパスワードを入力しました。アカウントを保護するには、他のアプリやサイトでパスワードを再使用しないでください。</translation>
<translation id="6945221475159498467">選択</translation>
<translation id="6948701128805548767">受け取り方法と要件を確認するには、住所を選択してください</translation>
<translation id="6949872517221025916">パスワードを再設定</translation>
+<translation id="6950684638814147129">JSON 値の解析中にエラーが発生しました: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">サーバーの証明書が偽造されたもののようです。</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">デバイス</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;[&lt;strong&gt;適用&lt;/strong&gt;] をクリックし、[&lt;strong&gt;OK&lt;/strong&gt;] をクリックします。
&lt;li&gt;このソフトウェアをパソコンから完全に削除する方法については、&lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome ヘルプセンター&lt;/a&gt;をご覧ください。
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">パスワードを管理…</translation>
<translation id="7419106976560586862">プロフィール パス</translation>
<translation id="7437289804838430631">連絡先情報を追加</translation>
<translation id="7441627299479586546">ポリシーの対象が間違っています</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790">この問題について<ph name="BEGIN_LINK" />詳細を確認<ph name="END_LINK" />する</translation>
<translation id="7455133967321480974">グローバルのデフォルト値([ブロック])を使用</translation>
<translation id="7460163899615895653">他の端末で最近使ったタブがここに表示されます</translation>
-<translation id="7469372306589899959">カードを確認中です</translation>
<translation id="7473891865547856676">スキップ</translation>
<translation id="7481312909269577407">進む</translation>
<translation id="7485870689360869515">データが見つかりません。</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">ネットワーク接続に問題があったため翻訳できませんでした。</translation>
<translation id="8311129316111205805">セッションを読み込む</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> へのアクセスが拒否されました</translation>
+<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="8349305172487531364">ブックマーク バー</translation>
<translation id="8363502534493474904">機内モードをオフにする</translation>
@@ -1035,21 +1035,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> からの応答時間が長すぎます。</translation>
<translation id="8503559462189395349">Chrome パスワード</translation>
<translation id="8503813439785031346">ユーザー名</translation>
+<translation id="8508648098325802031">検索アイコン</translation>
<translation id="8543181531796978784"><ph name="BEGIN_ERROR_LINK" />検出の問題をご報告<ph name="END_ERROR_LINK" />ください。<ph name="BEGIN_LINK" />安全でないこのサイトにアクセス<ph name="END_LINK" />する場合は、セキュリティ上のリスクがあることをご承知おきください。</translation>
<translation id="8543556556237226809">ご不明な点は、プロフィールの管理担当者にお問い合わせください。</translation>
<translation id="8553075262323480129">ページの言語を検出できないため翻訳できません。</translation>
<translation id="8557066899867184262">CVC はカードの裏面に記載されています。</translation>
<translation id="8559762987265718583">デバイスの日時(<ph name="DATE_AND_TIME" />)が正しくないため、<ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> へのプライベート接続を確立できません。</translation>
+<translation id="8564985650692024650"><ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> パスワードを他のサイトで再使用した場合、Chromium ではパスワードの再設定を促すメッセージが表示されます。</translation>
<translation id="8571890674111243710">ページを<ph name="LANGUAGE" />に翻訳しています...</translation>
<translation id="858637041960032120">電話番号を追加
</translation>
<translation id="859285277496340001">この証明書には、取り消されたかどうかを確認する方法が指定されていません。</translation>
+<translation id="860043288473659153">カード名義人</translation>
<translation id="8620436878122366504">保護者がまだサイトを開くことを許可していません</translation>
<translation id="8625384913736129811">このカード情報をこの端末に保存する</translation>
-<translation id="8639963783467694461">自動入力の設定</translation>
+<translation id="8663226718884576429">ご注文の概要、<ph name="TOTAL_LABEL" />、その他の詳細</translation>
<translation id="8680536109547170164">「<ph name="QUERY" />」に対する答え: 「<ph name="ANSWER" />」</translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> への接続は暗号化されていません。</translation>
<translation id="8718314106902482036">支払い処理を完了できませんでした</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />、<ph name="DESCRIPTION" />、検索候補</translation>
<translation id="8725066075913043281">やり直し</translation>
<translation id="8728672262656704056">シークレット モードです</translation>
<translation id="8730621377337864115">完了</translation>
@@ -1065,8 +1069,10 @@
<translation id="8820817407110198400">ブックマーク</translation>
<translation id="883848425547221593">その他のブックマーク</translation>
<translation id="884264119367021077">配送先住所</translation>
+<translation id="8846319957959474018">ブックマークを使って簡単にアプリを開くことができます</translation>
<translation id="884923133447025588">取り消し機構が見つかりません。</translation>
<translation id="885730110891505394">Google との共有</translation>
+<translation id="8858065207712248076"><ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> パスワードを他のサイトで再使用した場合、Chrome ではパスワードの再設定を促すメッセージが表示されます。</translation>
<translation id="8866481888320382733">ポリシー設定の解析中にエラーが発生しました</translation>
<translation id="8870413625673593573">最近閉じたタブ</translation>
<translation id="8874824191258364635">有効なクレジット カード番号を入力してください</translation>
@@ -1105,6 +1111,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> では通常、暗号化して情報を保護しています。今回、Chromium から <ph name="SITE" /> への接続試行時に、このウェブサイトからいつもとは異なる誤った認証情報が返されました。悪意のあるユーザーが <ph name="SITE" /> になりすまそうとしているか、Wi-Fi ログイン画面で接続が中断された可能性があります。データのやり取りが行われる前に Chromium によって接続が停止されたため、情報は引き続き保護されています。</translation>
<translation id="9106062320799175032">請求先住所の追加</translation>
<translation id="910908805481542201">問題を修正するには</translation>
+<translation id="9114524666733003316">カードを確認しています…</translation>
<translation id="9128870381267983090">ネットワークに接続する</translation>
<translation id="9137013805542155359">原文のページを表示</translation>
<translation id="9137248913990643158">このアプリを使用するには、Chrome を起動してログインしてください。</translation>
@@ -1115,6 +1122,7 @@
<translation id="9168814207360376865">お支払い方法を保存しているかどうかの確認をサイトに許可する</translation>
<translation id="9169664750068251925">このサイトでは常にブロック</translation>
<translation id="9170848237812810038">取消(&amp;U)</translation>
+<translation id="9171296965991013597">アプリを終了しますか?</translation>
<translation id="917450738466192189">サーバーの証明書が無効です。</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> ではサポートされていないプロトコルが使用されています。</translation>
<translation id="9205078245616868884">データは同期パスフレーズで暗号化されます。同期を開始するには、同期パスフレーズを入力してください。</translation>
diff --git a/chromium/components/strings/components_strings_kn.xtb b/chromium/components/strings/components_strings_kn.xtb
index eb757427ee1..919d4bb806c 100644
--- a/chromium/components/strings/components_strings_kn.xtb
+++ b/chromium/components/strings/components_strings_kn.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">ಮೌಲ್ಯವನ್ನು ಮರೆಮಾಡಿ</translation>
<translation id="1228893227497259893">ತಪ್ಪಾದ ಅಸ್ತಿತ್ವದ ಗುರುತು</translation>
<translation id="1232569758102978740">ಶೀರ್ಷಿಕೆರಹಿತ</translation>
+<translation id="1250759482327835220">ಮುಂದಿನ ಬಾರಿ ವೇಗವಾಗಿ ಪಾವತಿಸಲು, ನಿಮ್ಮ ಕಾರ್ಡ್, ಹೆಸರು ಮತ್ತು ಬಿಲ್ಲಿಂಗ್ ವಿಳಾಸವನ್ನು ನಿಮ್ಮ Google ಖಾತೆಯಲ್ಲಿ ಉಳಿಸಿ.</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;
@@ -96,7 +97,6 @@
&lt;p&gt;ದಯವಿಟ್ಟು ಅಪ್ಲಿಕೇಶನ್‌ನ &lt;strong&gt;ಸೆಟ್ಟಿಂಗ್‌ಗಳು&lt;/strong&gt; ವಿಭಾಗದ &lt;strong&gt;ಸಾಮಾನ್ಯ&lt;/strong&gt; ದಿಂದ ದಿನಾಂಕ ಮತ್ತು ಸಮಯವನ್ನು ಹೊಂದಿಸಿ.&lt;/p&gt;</translation>
<translation id="1583429793053364125">ಈ ವೆಬ್‌ಪುಟವನ್ನು ಪ್ರದರ್ಶಿಸುವಾಗ ಯಾವುದೋ ತಪ್ಪು ಸಂಭವಿಸಿದೆ.</translation>
-<translation id="1590457302292452960">ಬಲಿಷ್ಠ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ರಚಿಸಿ...</translation>
<translation id="1592005682883173041">ಸ್ಥಳೀಯ ಡೇಟಾ ಪ್ರವೇಶ</translation>
<translation id="1594030484168838125">ಆರಿಸಿ</translation>
<translation id="1620510694547887537">ಕ್ಯಾಮರಾ</translation>
@@ -122,6 +122,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">ಸಿಸ್ಟಂ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಲು ಪ್ರಯತ್ನಿಸಿ.</translation>
<translation id="1740951997222943430">ಮಾನ್ಯವಾದ ಅವಧಿ-ಮುಕ್ತಾಯ ತಿಂಗಳನ್ನು ನಮೂದಿಸಿ</translation>
+<translation id="1743520634839655729">ಮುಂದಿನ ಬಾರಿ ವೇಗವಾಗಿ ಪಾವತಿಸಲು, ನಿಮ್ಮ ಕಾರ್ಡ್‌, ಹೆಸರು ಮತ್ತು ನಿಮ್ಮ ಬಿಲ್ಲಿಂಗ್ ವಿಳಾಸವನ್ನು Google ಖಾತೆಯಲ್ಲಿ ಮತ್ತು ಈ ಸಾಧನದಲ್ಲಿ ಉಳಿಸಿ.</translation>
<translation id="17513872634828108">ತೆರೆದ ಟ್ಯಾಬ್‌ಗಳು</translation>
<translation id="1753706481035618306">ಪುಟ ಸಂಖ್ಯೆ</translation>
<translation id="1763864636252898013">ಈ ಸರ್ವರ್ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬುದನ್ನು ಸಾಬೀತುಪಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ; ಅದರ ಸುರಕ್ಷತಾ ಪ್ರಮಾಣಪತ್ರವು ನಿಮ್ಮ ಸಾಧನದ ಆಪರೇಟಿಂಗ್ ಸಿಸ್ಟಂ‌ ಪ್ರಕಾರ ವಿಶ್ವಾಸಾರ್ಹವಾಗಿಲ್ಲ. ಇದು ತಪ್ಪು ಕಾನ್ಫಿಗರೇಶನ್‌ನಿಂದ ಅಥವಾ ಆಕ್ರಮಣಕಾರರು ನಿಮ್ಮ ಸಂಪರ್ಕದಲ್ಲಿ ಒಳನುಸುಳಿರುವುದರಿಂದ ಆಗಿರಬಹುದು.</translation>
@@ -130,7 +131,6 @@
<translation id="1787142507584202372">ನಿಮ್ಮ ತೆರೆಯಲಾದ ಟ್ಯಾಬ್‌ಗಳು ಇಲ್ಲಿ ಗೋಚರಿಸುತ್ತದೆ</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">ಕಾರ್ಡ್‌ಹೋಲ್ಡರ್ ಹೆಸರು</translation>
-<translation id="1806541873155184440"><ph name="ADDED_TO_AUTOFILL_MONTH" /> ಸೇರಿಸಲಾಗಿದೆ</translation>
<translation id="1821930232296380041">ಅಮಾನ್ಯವಾದ ವಿನಂತಿ ಅಥವಾ ವಿನಂತಿ ಪ್ಯಾರಾಮೀಟರ್‌ಗಳು</translation>
<translation id="1826516787628120939">ಪರಿಶೀಲಿಸಲಾಗುತ್ತಿದೆ</translation>
<translation id="1834321415901700177">ಈ ಸೈಟ್ ಹಾನಿಕಾರಕ ಪ್ರೋಗ್ರಾಂಗಳನ್ನು ಹೊಂದಿದೆ</translation>
@@ -163,10 +163,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 ಸಲಹೆ}one{# ಸಲಹೆಗಳು}other{# ಸಲಹೆಗಳು}}</translation>
<translation id="2079545284768500474">ರದ್ದುಮಾಡಿ</translation>
<translation id="20817612488360358">ಸಿಸ್ಟಂ ಪ್ರಾಕ್ಸಿ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಬಳಸಲು ಹೊಂದಿಸಲಾಗಿದೆ ಆದರೆ ಬಹಿರಂಗವಾದ ಪ್ರಾಕ್ಸಿ ಕಾನ್ಫಿಗರೇಶನ್ ಅನ್ನು ಸಹ ನಿರ್ದಿಷ್ಟಪಡಿಸಲಾಗಿದೆ.</translation>
-<translation id="2084558088529668945">ನೀವು ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು <ph name="ORG_NAME" /> ನಿರ್ವಹಣೆ ಮಾಡದ ಸೈಟ್‌ನಲ್ಲಿ ನಮೂದಿಸಿದ್ದೀರಿ. ನಿಮ್ಮ ಖಾತೆಯನ್ನು ರಕ್ಷಿಸಲು, ಇತರ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಲ್ಲಿ ಮತ್ತು ಸೈಟ್‌ಗಳಲ್ಲಿ ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್‌ ಅನ್ನು ಮರುಬಳಕೆ ಮಾಡಬೇಡಿ.</translation>
<translation id="2091887806945687916">ಶಬ್ಧ</translation>
<translation id="2094505752054353250">ಡೊಮೇನ್ ಹೊಂದುತ್ತಿಲ್ಲ</translation>
<translation id="2096368010154057602">ವಿಭಾಗ</translation>
+<translation id="2102134110707549001">ಸದೃಢವಾದ ಪಾಸ್‌ವರ್ಡ್ ಸೂಚಿಸಿ…</translation>
<translation id="2108755909498034140">ನಿಮ್ಮ ಕಂಪ್ಯೂಟರ್ ಮರುಪ್ರಾರಂಭಿಸಿ</translation>
<translation id="2113977810652731515">ಕಾರ್ಡ್</translation>
<translation id="2114841414352855701"><ph name="POLICY_NAME" /> ರಿಂದ ಅತಿಕ್ರಮಿಸಲಾಗಿರುವ ಕಾರಣ ಇದನ್ನು ನಿರ್ಲಕ್ಷಿಸಲಾಗಿದೆ.</translation>
@@ -193,7 +193,6 @@
<translation id="2262243747453050782">HTTP ದೋಷ</translation>
<translation id="2270484714375784793">ಫೋನ್ ಸಂಖ್ಯೆ</translation>
<translation id="2292556288342944218">ನಿಮ್ಮ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ</translation>
-<translation id="230155334948463882">ಹೊಸ ಕಾರ್ಡ್?</translation>
<translation id="2316887270356262533">1 MB ಕ್ಕಿಂತ ಕಡಿಮೆ ಇರುವುದನ್ನು ತೆಗೆದುಹಾಕಿ. ನಿಮ್ಮ ನಂತರದ ಭೇಟಿಯ ಸಮಯದಲ್ಲಿ ಕೆಲವು ಸೈಟ್‌ಗಳು ನಿಧಾನವಾಗಿ ಲೋಡ್ ಆಗಬಹುದು.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> ಗೆ ಬಳಕೆದಾರಹೆಸರು ಮತ್ತು ಪಾಸ್‌ವರ್ಡ್ ಅಗತ್ಯವಿದೆ.</translation>
<translation id="2317583587496011522">ಡೆಬಿಟ್ ಕಾರ್ಡ್‌ಗಳನ್ನು ಸಮ್ಮತಿಸಲಾಗಿದೆ.</translation>
@@ -247,7 +246,6 @@
<translation id="2699302886720511147">ಸ್ವೀಕೃತ ಕಾರ್ಡ್‌ಗಳು</translation>
<translation id="2702801445560668637">ಓದುವ ಪಟ್ಟಿ</translation>
<translation id="2704283930420550640">ಮೌಲ್ಯವು ಸ್ವರೂಪಕ್ಕೆ ಹೊಂದಿಕೆಯಾಗುವುದಿಲ್ಲ.</translation>
-<translation id="2704951214193499422">ಈ ಸಮಯದಲ್ಲಿ Chromium ಗೆ ನಿಮ್ಮ ಕಾರ್ಡ್ ಖಚಿತಪಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ದಯವಿಟ್ಟು ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.</translation>
<translation id="2705137772291741111">ಈ ಸೈಟ್‌ನ ಉಳಿಸಿದ (ಸಂಗ್ರಹವಾಗಿರುವ) ನಕಲನ್ನು ಓದಲಾಗುತ್ತಿಲ್ಲ.</translation>
<translation id="2709516037105925701">ಸ್ವಯಂತುಂಬುವಿಕೆ</translation>
<translation id="2710942282213947212">ಸುರಕ್ಷಿತವಾಗಿ ವೆಬ್‌ಗೆ ಸಂಪರ್ಕಿಸುವ Chromium ನ ಕಾರ್ಯವನ್ನು ನಿಮ್ಮ ಕಂಪ್ಯೂಟರ್‌ನಲ್ಲಿರುವ ಸಾಫ್ಟ್‌ವೇರ್‌ ಸ್ಥಗಿತಗೊಳಿಸಿದೆ</translation>
@@ -262,6 +260,7 @@
<translation id="277133753123645258">ಶಿಪ್ಪಿಂಗ್ ವಿಧಾನ</translation>
<translation id="277499241957683684">ಸಾಧನದ ರೆಕಾರ್ಡ್ ಕಾಣೆಯಾಗಿದೆ</translation>
<translation id="2781030394888168909">MacOS ಅನ್ನು ರಫ್ತು ಮಾಡಿ</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">ಸಂಪರ್ಕವನ್ನು ರೀಸೆಟ್ ಮಾಡಲಾಗಿದೆ.</translation>
<translation id="2788784517760473862">ಸ್ವೀಕೃತ ಕ್ರೆಡಿಟ್‌ ಕಾರ್ಡ್‌ಗಳು</translation>
<translation id="2794233252405721443">ಸೈಟ್ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ</translation>
@@ -283,7 +282,6 @@
<translation id="2948083400971632585">ಸಂಪರ್ಕಕ್ಕಾಗಿ ಕಾನ್ಫಿಗರ್ ಮಾಡಲಾಗಿರುವ ಯಾವುದೇ ಪ್ರಾಕ್ಸಿಗಳನ್ನು ನೀವು ಸೆಟ್ಟಿಂಗ್‌ಗಳ ಪುಟದಿಂದ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಬಹುದು.</translation>
<translation id="2955913368246107853">ಹುಡುಕಿ ಬಾರ್ ಅನ್ನು ಮುಚ್ಚಿ</translation>
<translation id="2958431318199492670">ONC ಪ್ರಮಾಣಿತಕ್ಕೆ ನೆಟ್‌ವರ್ಕ್ ಕಾನ್ಫಿಗರೇಶನ್ ಅನುಸರಣೆಯಾಗುವುದಿಲ್ಲ. ಕಾನ್ಫಿಗರೇಶನ್‌ನ ಭಾಗಗಳನ್ನು ಆಮದು ಮಾಡಲಾಗದಿರಬಹುದು.</translation>
-<translation id="2966678944701946121">ಅವಧಿ ಮೀರುವ ಸಮಯ: <ph name="EXPIRATION_DATE_ABBR" />, <ph name="ADDED_TO_AUTOFILL_MONTH" /> ಸೇರಿಸಲಾಗಿದೆ</translation>
<translation id="2969319727213777354">ಸುರಕ್ಷಿತ ಸಂಪರ್ಕವನ್ನು ಸ್ಥಾಪಿಸಲು, ನಿಮ್ಮ ಗಡಿಯಾರವನ್ನು ಸರಿಯಾಗಿ ಹೊಂದಿಸಬೇಕಾದ ಅಗತ್ಯವಿದೆ. ವೆಬ್‌ಸೈಟ್‌ಗಳು ತಮ್ಮನ್ನು ಗುರುತಿಸಲು ಬಳಸುವ ಪ್ರಮಾಣಪತ್ರಗಳು ನಿರ್ದಿಷ್ಟ ಅವಧಿಗಳಲ್ಲಿ ಮಾತ್ರ ಮಾನ್ಯವಾಗಿರುವ ಕಾರಣ ಹೀಗಾಗುತ್ತದೆ. ನಿಮ್ಮ ಸಾಧನದ ಗಡಿಯಾರವು ತಪ್ಪಾಗಿರುವ ಕಾರಣ, Google Chrome ಗೆ ಈ ಪ್ರಮಾಣಪತ್ರಗಳನ್ನು ಪರಿಶೀಲಿಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ.</translation>
<translation id="2972581237482394796">&amp;ಮತ್ತೆಮಾಡು</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, ಪ್ರಸ್ತುತವಾಗಿ ಆಯ್ಕೆ ಮಾಡಿರುವುದು. <ph name="ROW_CONTENT" /></translation>
@@ -300,7 +298,6 @@
<translation id="3024663005179499861">ತಪ್ಪಾದ ನೀತಿಯ ಪ್ರಕಾರ</translation>
<translation id="3037605927509011580">ಓಹ್, ಹೋಯ್ತು!</translation>
<translation id="3041612393474885105">ಪ್ರಮಾಣಪತ್ರ ಮಾಹಿತಿ</translation>
-<translation id="3063697135517575841">ಈ ಸಮಯದಲ್ಲಿ Chrome ಗೆ ನಿಮ್ಮ ಕಾರ್ಡ್ ಅನ್ನು ಖಚಿತಪಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ದಯವಿಟ್ಟು ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.</translation>
<translation id="3064966200440839136">ಬಾಹ್ಯ ಅಪ್ಲಿಕೇಶನ್‌‌ ಮೂಲಕರ ಪಾವತಿಸಲು ಅದೃಶ್ಯ ಮೋಡ್‌‌ ತೊರೆಯಲಾಗುತ್ತಿದೆ. ಮುಂದುವರಿಸುವುದೇ?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{ಯಾವುದೂ ಇಲ್ಲ}=1{1 ಪಾಸ್‌ವರ್ಡ್‌}one{# ಪಾಸ್‌ವರ್ಡ್‌ಗಳು}other{# ಪಾಸ್‌ವರ್ಡ್‌ಗಳು}}</translation>
<translation id="3096100844101284527">ಪಿಕಪ್ ವಿಳಾಸವನ್ನು ಸೇರಿಸಿ</translation>
@@ -338,7 +335,6 @@
<translation id="3305707030755673451">ನಿಮ್ಮ ಡೇಟಾವನ್ನು <ph name="TIME" /> ರಂದು ನಿಮ್ಮ ಸಿಂಕ್ ಪಾಸ್‌ಫ್ರೇಸ್‌ನೊಂದಿಗೆ ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಲಾಗಿದೆ. ಸಿಂಕ್ ಪ್ರಾರಂಭಿಸಲು ಅದನ್ನು ನಮೂದಿಸಿ.</translation>
<translation id="3320021301628644560">ಬಿಲ್ಲಿಂಗ್ ವಿಳಾಸವನ್ನು ಸೇರಿಸಿ</translation>
<translation id="3338095232262050444">ಸುರಕ್ಷಿತ</translation>
-<translation id="3340978935015468852">ಸೆಟ್ಟಿಂಗ್‌ಗಳು</translation>
<translation id="3345135638360864351">ಈ ಸೈಟ್ ಅನ್ನು ಪ್ರವೇಶಿಸಲು ನೀವು ಸಲ್ಲಿಸಿದ ವಿನಂತಿಯನ್ನು <ph name="NAME" /> ಗೆ ಕಳುಹಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.</translation>
<translation id="3355823806454867987">ಪ್ರಾಕ್ಸಿ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಬದಲಿಸಿ...</translation>
<translation id="3361596688432910856">ಈ ಮುಂದಿನ ಮಾಹಿತಿಯನ್ನು Chrome ಗೆ <ph name="BEGIN_EMPHASIS" />ಉಳಿಸಲು ಆಗುವುದಿಲ್ಲ<ph name="END_EMPHASIS" />:
@@ -372,7 +368,6 @@
<translation id="3528171143076753409">ಸರ್ವರ್‌ನ ಪ್ರಮಾಣಪತ್ರ ನಂಬಲರ್ಹವಾಗಿಲ್ಲ.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{ಸಿಂಕ್ ಮಾಡಿದ ಸಾಧನಗಳಲ್ಲಿ ಕನಿಷ್ಠ 1 ಐಟಂ}=1{1 ಐಟಂ (ಮತ್ತು ಸಿಂಕ್ ಮಾಡಿದ ಸಾಧನಗಳಲ್ಲಿ ಇನ್ನಷ್ಟು ಐಟಂಗಳು)}one{# ಐಟಂಗಳು (ಮತ್ತು ಸಿಂಕ್ ಮಾಡಿದ ಸಾಧನಗಳಲ್ಲಿ ಇನ್ನಷ್ಟು ಐಟಂಗಳು)}other{# ಐಟಂಗಳು (ಮತ್ತು ಸಿಂಕ್ ಮಾಡಿದ ಸಾಧನಗಳಲ್ಲಿ ಇನ್ನಷ್ಟು ಐಟಂಗಳು)}}</translation>
<translation id="3539171420378717834">ಈ ಸಾಧನದಲ್ಲಿ ಈ ಕಾರ್ಡ್‌ನ ನಕಲನ್ನು ಇರಿಸಿಕೊಳ್ಳಿ</translation>
-<translation id="3542684924769048008">ಇದಕ್ಕೆ ಪಾಸ್‌ವರ್ಡ್ ಬಳಸಿ:</translation>
<translation id="3549644494707163724">ನಿಮ್ಮ ಸ್ವಂತ ಸಿಂಕ್ ಪಾಸ್‌ಫ್ರೇಸ್‌ನೊಂದಿಗೆ ಸಿಂಕ್ ಆದ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಿ</translation>
<translation id="3556433843310711081">ನಿಮ್ಮ ಮ್ಯಾನೇಜರ್ ನಿಮಗಾಗಿ ಅದನ್ನು ಅನಿರ್ಬಂಧಿಸಬಹುದಾಗಿದೆ</translation>
<translation id="3566021033012934673">ನಿಮ್ಮ ಸಂಪರ್ಕವು ಖಾಸಗಿಯಲ್ಲ</translation>
@@ -457,7 +452,6 @@
<translation id="4171400957073367226">ತಪ್ಪು ಪರಿಶೀಲನೆ ಸಹಿ</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> ಹೆಚ್ಚಿನ ಐಟಂ}one{<ph name="ITEM_COUNT" /> ಹೆಚ್ಚಿನ ಐಟಂಗಳು}other{<ph name="ITEM_COUNT" /> ಹೆಚ್ಚಿನ ಐಟಂಗಳು}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">ನೀವು ಇತರ ಸೈಟ್‌ಗಳಲ್ಲಿ ಪಾಸ್‌ವರ್ಡ್‌ ಅನ್ನು ಮರುಬಳಕೆ ಮಾಡಿದ್ದಲ್ಲಿ Chrome ನಿಮ್ಮ <ph name="ORG_NAME" /> ಪಾಸವರ್ಡ್ ಅನ್ನು ಮರುಹೊಂದಿಸಲು ಶಿಫಾರಸು ಮಾಡುತ್ತದೆ.</translation>
<translation id="4196861286325780578">&amp;ಸರಿಸುವುದನ್ನು ಮತ್ತೆಮಾಡು</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ಫೈರ್‌ವಾಲ್ ಮತ್ತು ಆಂಟಿವೈರಸ್ ಕಾನ್ಫಿಗರೇಶನ್‌‌ಗಳನ್ನು ಪರಿಶೀಲಿಸಲಾಗುತ್ತಿದೆ<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">ವಿಫಲತೆಗಳು</translation>
@@ -486,7 +480,6 @@
<translation id="425582637250725228">ನೀವು ಮಾಡಿದ ಬದಲಾವಣೆಗಳನ್ನು ಉಳಿಸಲಾಗದೇ ಇರಬಹುದು.</translation>
<translation id="4258748452823770588">ತಪ್ಪಾದ ಸಹಿ</translation>
<translation id="4265872034478892965">ನಿಮ್ಮ ನಿರ್ವಾಹಕರಿಂದ ಅನುಮತಿಸಲಾಗಿದೆ</translation>
-<translation id="4269787794583293679">(ಯಾವುದು ಬಳಕೆದಾರಹೆಸರಿಲ್ಲ)</translation>
<translation id="4275830172053184480">ನಿಮ್ಮ ಸಾಧನವನ್ನು ಮರುಪ್ರಾರಂಭಿಸಿ</translation>
<translation id="4277028893293644418">ಪಾಸ್‌ವರ್ಡ್ ಮರುಹೊಂದಿಸಿ</translation>
<translation id="4280429058323657511">, ಅವಧಿ ಮುಕ್ತಾಯ <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -509,6 +502,7 @@
<translation id="4434045419905280838">ಪಾಪ್-ಅಪ್‌ಗಳು ಹಾಗೂ ಮರುನಿರ್ದೇಶನಗಳು</translation>
<translation id="443673843213245140">ಪ್ರಾಕ್ಸಿಯ ಬಳಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ ಆದರೆ ಬಹಿರಂಗ ಪ್ರಾಕ್ಸಿ ಕಾನ್ಫಿಗರೇಶನ್ ಅನ್ನು ನಿರ್ದಿಷ್ಟಪಡಿಸಲಾಗಿದೆ.</translation>
<translation id="445100540951337728">ಸಮ್ಮತಿಸಲಾದ ಡೆಬಿಟ್ ಕಾರ್ಡ್‌ಗಳು</translation>
+<translation id="4472575034687746823">ಪ್ರಾರಂಭ</translation>
<translation id="4506176782989081258">ಮೌಲ್ಯೀಕರಿಸುವಿಕೆಯ ದೋಷ: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">ಸಿಸ್ಟಂ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಲಾಗುತ್ತಿದೆ</translation>
<translation id="450710068430902550">ನಿರ್ವಾಹಕರೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳುವುದು</translation>
@@ -533,8 +527,8 @@
<translation id="4726672564094551039">ನೀತಿಗಳನ್ನು ಮರುಲೋಡ್ ಮಾಡಿ</translation>
<translation id="4728558894243024398">ಪ್ಲಾಟ್‌ಫಾರ್ಮ್</translation>
<translation id="4736825316280949806">Chromium ಮರುಪ್ರಾರಂಭಿಸಿ</translation>
+<translation id="4742407542027196863">ಪಾಸ್‌ವರ್ಡ್‌ಗಳನ್ನು ನಿರ್ವಹಿಸಿ…</translation>
<translation id="4744603770635761495">ಪ್ರದರ್ಶನಗೊಳ್ಳುವಂತಹ ಹಾದಿ</translation>
-<translation id="4749685221585524849"><ph name="LAST_USED_MONTH" /> ಅಂತಿಮವಾಗಿ ಬಳಸಲಾಗಿದೆ</translation>
<translation id="4750917950439032686">ಈ ಸೈಟ್‌ಗೆ ನಿಮ್ಮ ಮಾಹಿತಿಯನ್ನು ಕಳುಹಿಸಿದಾಗ ಅದು (ಉದಾಹರಣೆಗೆ, ಪಾಸ್‌ವರ್ಡ್‌ಗಳು ಅಥವಾ ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಸಂಖ್ಯೆಗಳು) ಖಾಸಗಿಯಾಗಿರುತ್ತದೆ.</translation>
<translation id="4756388243121344051">&amp;ಇತಿಹಾಸ</translation>
<translation id="4758311279753947758">ಸಂಪರ್ಕ ಮಾಹಿತಿಯನ್ನು ಸೇರಿಸಿ</translation>
@@ -551,6 +545,7 @@
<translation id="4850886885716139402">ವೀಕ್ಷಣೆ</translation>
<translation id="4854362297993841467">ಈ ವಿತರಣೆಯ ವಿಧಾನ ಲಭ್ಯವಿಲ್ಲ. ಬೇರೊಂದು ವಿಧಾನವನ್ನು ಪ್ರಯತ್ನಿಸಿ.</translation>
<translation id="4858792381671956233">ಈ ಸೈಟ್ ಅನ್ನು ಭೇಟಿ ಮಾಡಬಹುದು ಎಂದು ನಿಮ್ಮ ಪೋಷಕರಿಗೆ ನೀವು ಕೇಳಿರುವಿರಿ.</translation>
+<translation id="4876305945144899064">ಯಾವುದೇ ಬಳಕೆದಾರರ ಹೆಸರಿಲ್ಲ</translation>
<translation id="4880827082731008257">ಹುಡುಕಾಟ ಇತಿಹಾಸ</translation>
<translation id="4881695831933465202">ತೆರೆ</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -584,6 +579,7 @@
<translation id="5089810972385038852">ರಾಜ್ಯ</translation>
<translation id="5094747076828555589">ಈ ಸರ್ವರ್ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬುದನ್ನು ಸಾಬೀತುಪಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ; ಅದರ ಸುರಕ್ಷತಾ ಪ್ರಮಾಣಪತ್ರವು Chromium ಮೂಲಕ ವಿಶ್ವಾಸಾರ್ಹವಾಗಿಲ್ಲ. ಇದು ತಪ್ಪು ಕಾನ್ಫಿಗರೇಶನ್‌ನಿಂದ ಅಥವಾ ಆಕ್ರಮಣಕಾರರು ನಿಮ್ಮ ಸಂಪರ್ಕದಲ್ಲಿ ಒಳನುಸುಳಿರುವುದರಿಂದ ಆಗಿರಬಹುದು.</translation>
<translation id="5095208057601539847">ಪ್ರಾಂತ್ಯ</translation>
+<translation id="5098332213681597508">ಈ ಹೆಸರು ನಿಮ್ಮ Google ಖಾತೆಯಿಂದ ಬಂದಿದೆ.</translation>
<translation id="5115563688576182185">(64-ಬಿಟ್)</translation>
<translation id="5121084798328133320">ನೀವು ಖಚಿತಪಡಿಸಿದ ನಂತರ, ನಿಮ್ಮ Google ಪಾವತಿಗಳ ಖಾತೆಯಿಂದ ಕಾರ್ಡ್ ವಿವರಗಳನ್ನು ಈ ಸೈಟ್ ಜೊತೆಗೆ ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತದೆ.</translation>
<translation id="5128122789703661928">ಈ ಹೆಸರಿನ ಸೆಶನ್ ಅಳಿಸಲು ಮಾನ್ಯವಾಗಿಲ್ಲ.</translation>
@@ -623,9 +619,10 @@
<translation id="5332219387342487447">ಶಿಪ್ಪಿಂಗ್ ವಿಧಾನ</translation>
<translation id="5355557959165512791">ಸದ್ಯಕ್ಕೆ ನೀವು <ph name="SITE" /> ಗೆ ಭೇಟಿ ನೀಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ಏಕೆಂದರೆ ಇದರ ಪ್ರಮಾಣಪತ್ರವನ್ನು ಹಿಂಪಡೆದುಕೊಳ್ಳಲಾಗಿದೆ. ನೆಟ್‌ವರ್ಕ್ ದೋಷಗಳು ಮತ್ತು ಆಕ್ರಮಣಗಳು ತಾತ್ಕಾಲಿಕವಾಗಿರುತ್ತವೆ, ಹೀಗಾಗಿ ಈ ಪುಟವು ಸ್ವಲ್ಪ ಸಮಯದ ನಂತರ ಕಾರ್ಯನಿರ್ವಹಿಸಬಹುದು.</translation>
<translation id="536296301121032821">ನೀತಿಯ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಸಂಗ್ರಹಿಸುವಲ್ಲಿ ವಿಫಲವಾಗಿದೆ</translation>
+<translation id="5371425731340848620">ಕಾರ್ಡ್ ಅಪ್‌ಡೇಟ್ ಮಾಡಿ</translation>
<translation id="5377026284221673050">"ನಿಮ್ಮ ಗಡಿಯಾರ ಹಿಂದಿದೆ" ಅಥವಾ "ನಿಮ್ಮ ಗಡಿಯಾರ ಮುಂದಿದೆ" ಅಥವಾ "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">ಈ ಸೈಟ್‌ಗೆ ಪ್ರಮಾಣಪತ್ರ ಸರಣಿಯು SHA-1 ಬಳಸಿಕೊಂಡು ಸಹಿ ಮಾಡಲಾದ ಪ್ರಮಾಣಪತ್ರವನ್ನು ಒಳಗೊಂಡಿರುತ್ತದೆ.</translation>
-<translation id="5402410679244714488">ಅವಧಿ ಮೀರುವ ಸಮಯ: <ph name="EXPIRATION_DATE_ABBR" />, ಕೊನೆಯದಾಗಿ ಸುಮಾರು ಒಂದು ವರ್ಷದ ಹಿಂದೆ ಬಳಸಲಾಗಿದೆ</translation>
+<translation id="5387961145478138773">ನಿಮ್ಮ ನೆಚ್ಚಿನ Google ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಗೆ ತ್ವರಿತ ಪ್ರವೇಶವನ್ನು ಪಡೆಯಿರಿ</translation>
<translation id="540969355065856584">ಈ ಸರ್ವರ್ <ph name="DOMAIN" /> ಆಗಿದೆ ಎಂಬುದನ್ನು ಸಾಬೀತುಪಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ; ಈ ಸಮಯದಲ್ಲಿ ಅದರ ಸುರಕ್ಷತಾ ಪ್ರಮಾಣಪತ್ರವು ಮಾನ್ಯವಾಗಿಲ್ಲ. ಇದು ತಪ್ಪು ಕಾನ್ಫಿಗರೇಶನ್‌ನಿಂದ ಅಥವಾ ಆಕ್ರಮಣಕಾರರು ನಿಮ್ಮ ಸಂಪರ್ಕದಲ್ಲಿ ಒಳನುಸುಳಿರುವುದರಿಂದ ಆಗಿರಬಹುದು.</translation>
<translation id="5421136146218899937">ಬ್ರೌಸಿಂಗ್ ಡೇಟಾವನ್ನು ತೆರವುಗೊಳಿಸಿ...</translation>
<translation id="5430298929874300616">ಬುಕ್‌ಮಾರ್ಕ್‌ ತೆಗೆದುಹಾಕಿ</translation>
@@ -667,11 +664,13 @@
<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="5659593005791499971">ಇಮೇಲ್</translation>
+<translation id="5666899935841546222">ನಿಮ್ಮ ಎಲ್ಲಾ ಕಾರ್ಡ್‌ಗಳನ್ನು ಒಂದೇ ಸ್ಥಳದಲ್ಲಿ ಹೊಂದಲು ನೀವು ಬಯಸುತ್ತೀರಾ?</translation>
<translation id="5675650730144413517">ಈ ಪುಟ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತಿಲ್ಲ</translation>
<translation id="5685654322157854305">ಶಿಪ್ಪಿಂಗ್ ವಿಳಾಸವನ್ನು ಸೇರಿಸಿ</translation>
<translation id="5689199277474810259">JSON ಗೆ ರಫ್ತು ಮಾಡಿ</translation>
<translation id="5689516760719285838">ಸ್ಥಳ</translation>
<translation id="570530837424789914">ನಿರ್ವಹಿಸಿ...</translation>
+<translation id="57094364128775171">ಸದೃಢವಾದ ಪಾಸ್‌ವರ್ಡ್ ಸೂಚಿಸಿ…</translation>
<translation id="5710435578057952990">ಈ ವೆಬ್‌ಸೈಟ್‌ನ ಗುರುತಿಸುವಿಕೆಯನ್ನು ಇನ್ನೂ ಪರಿಶೀಲಿಸಲಾಗಿಲ್ಲ.</translation>
<translation id="5719499550583120431">ಪ್ರೀಪೇಯ್ಡ್ ಕಾರ್ಡ್‌ಗಳನ್ನು ಸಮ್ಮತಿಸಲಾಗಿದೆ.</translation>
<translation id="5720705177508910913">ಪ್ರಸ್ತುತ ಬಳಕೆದಾರ</translation>
@@ -692,11 +691,9 @@
<translation id="5869405914158311789">ಈ ಸೈಟ್ ತಲುಪಲಾಗುವುದಿಲ್ಲ</translation>
<translation id="5869522115854928033">ಉಳಿಸಲಾದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು</translation>
<translation id="5893752035575986141">ಕ್ರೆಡಿಟ್‌ ಕಾರ್ಡ್‌ಗಳನ್ನು ಸಮ್ಮತಿಸಲಾಗಿದೆ.</translation>
-<translation id="5898382028489516745">ನೀವು ಇತರ ಸೈಟ್‌ಗಳಲ್ಲಿ ಪಾಸ್‌ವರ್ಡ್‌ ಅನ್ನು ಮರುಬಳಕೆ ಮಾಡಿದ್ದಲ್ಲಿ Chromium ನಿಮ್ಮ <ph name="ORG_NAME" /> ಪಾಸವರ್ಡ್ ಅನ್ನು ಮರುಹೊಂದಿಸಲು ಶಿಫಾರಸು ಮಾಡುತ್ತದೆ.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (ಸಿಂಕ್‌ ಮಾಡಲಾಗಿದೆ)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 ಬಳಕೆಯಲ್ಲಿದೆ}one{# ಬಳಕೆಯಲ್ಲಿದೆ}other{# ಬಳಕೆಯಲ್ಲಿದೆ}}</translation>
<translation id="5939518447894949180">ಮರುಹೊಂದಿಸು</translation>
-<translation id="5959728338436674663">ಅಪಾಯಕಾರಿ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ಸೈಟ್‌ಗಳ ಪತ್ತೆಗೆ ಸಹಾಯ ಮಾಡಲು Google ಗೆ ಕೆಲವು <ph name="BEGIN_WHITEPAPER_LINK" />ಸಿಸ್ಟಂ ಮಾಹಿತಿ ಮತ್ತು ಪುಟ ವಿಷಯ<ph name="END_WHITEPAPER_LINK" />ವನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕಳುಹಿಸಿ. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">ಸಂಪರ್ಕ ಮಾಹಿತಿಯನ್ನು ಎಡಿಟ್ ಮಾಡಿ</translation>
<translation id="5967867314010545767">ಇತಿಹಾಸದಿಂದ ತೆಗೆದುಹಾಕಿ</translation>
<translation id="5975083100439434680">ಝೂಮ್ ಔಟ್</translation>
@@ -741,13 +738,11 @@
<translation id="6282194474023008486">ಪೋಸ್ಟಲ್ ಕೋಡ್</translation>
<translation id="6290238015253830360">ನೀವು ಸಲಹೆ ನೀಡಿರುವ ಲೇಖನಗಳು ಇಲ್ಲಿ ಕಾಣಿಸಿಕೊಳ್ಳುತ್ತವೆ</translation>
<translation id="6305205051461490394"><ph name="URL" /> ತಲುಪಲಾಗುವುದಿಲ್ಲ.</translation>
-<translation id="6319915415804115995">ಕೊನೆಯದಾಗಿ ಸುಮಾರು ಒಂದು ವರ್ಷದ ಹಿಂದೆ ಬಳಸಲಾಗಿದೆ</translation>
<translation id="6321917430147971392">ನಿಮ್ಮ DNS ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಪರಿಶೀಲಿಸಿ</translation>
<translation id="6328639280570009161">ನೆಟ್‌ವರ್ಕ್ ಮುನ್ಸೂಚನೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಪ್ರಯತ್ನಿಸಿ</translation>
<translation id="6328786501058569169">ಈ ಸೈಟ್ ಮೋಸಗೊಳಿಸುವುದಾಗಿದೆ</translation>
<translation id="6337133576188860026"><ph name="SIZE" /> ಕ್ಕಿಂತ ಕಡಿಮೆ ಇರುವುದನ್ನು ತೆಗೆದುಹಾಕಿ. ನಿಮ್ಮ ನಂತರದ ಭೇಟಿಯ ಸಮಯದಲ್ಲಿ ಕೆಲವು ಸೈಟ್‌ಗಳು ನಿಧಾನವಾಗಿ ಲೋಡ್ ಆಗಬಹುದು.</translation>
<translation id="6337534724793800597">ಹೆಸರಿನ ಪ್ರಕಾರವಾಗಿ ನೀತಿಗಳನ್ನು ಫಿಲ್ಟರ್ ಮಾಡಿ</translation>
-<translation id="6342069812937806050">ಇದೀಗ</translation>
<translation id="6355080345576803305">ಸಾರ್ವಜನಿಕ ಸೆಷನ್ ಅತಿಕ್ರಮಿಸುವಿಕೆ</translation>
<translation id="6358450015545214790">ಇವುಗಳ ಅರ್ಥವೇನು?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 ಇತರ ಸಲಹೆ}one{# ಇತರ ಸಲಹೆಗಳು}other{# ಇತರ ಸಲಹೆಗಳು}}</translation>
@@ -772,7 +767,6 @@
<translation id="6529602333819889595">&amp;ಅಳಿಸುವುದನ್ನು ಮತ್ತೆಮಾಡು</translation>
<translation id="6534179046333460208">ಭೌತಿಕ ವೆಬ್ ಸಲಹೆಗಳು</translation>
<translation id="6550675742724504774">ಆಯ್ಕೆಗಳು</translation>
-<translation id="6556915248009097796">ಅವಧಿ ಮೀರುವ ಸಮಯ: <ph name="EXPIRATION_DATE_ABBR" />, <ph name="LAST_USED_DATE_NO_DETAIL" /> ಅಂತಿಮವಾಗಿ ಬಳಸಲಾಗಿದೆ</translation>
<translation id="6563469144985748109">ನಿಮ್ಮ ಮ್ಯಾನೇಜರ್ ಇನ್ನೂ ಇದನ್ನು ಅಂಗೀಕರಿಸಿಲ್ಲ</translation>
<translation id="6569060085658103619">ನೀವು ವಿಸ್ತರಣೆ ಪುಟವನ್ನು ವೀಕ್ಷಿಸುತ್ತಿರುವಿರಿ</translation>
<translation id="6596325263575161958">ಎನ್‌ಕ್ರಿಫ್ಶನ್ ಆಯ್ಕೆಗಳು</translation>
@@ -801,15 +795,20 @@
<translation id="6825578344716086703">ನೀವು <ph name="DOMAIN" /> ಅನ್ನು ತಲುಪಲು ಪ್ರಯತ್ನಿಸಿದಿರಿ, ಆದರೆ ದುರ್ಬಲ ಸಹಿ ಅಲ್ಗಾರಿದಮ್ (SHA-1 ಅದರಂತೆ) ಬಳಸಿಕೊಂಡು ಸಹಿ ಮಾಡಿದ ಪ್ರಮಾಣಪತ್ರವನ್ನು ಸರ್ವರ್ ಒದಗಿಸಿದೆ. ಇದರರ್ಥ ಸರ್ವರ್ ಒದಗಿಸಿದ ಸುರಕ್ಷತೆ ಪ್ರಮಾಣಪತ್ರಗಳನ್ನು ಖೋಟಾ ತಯಾರಿಸಿರಬಹುದು, ಮತ್ತು ನೀವು ನಿರೀಕ್ಷಿಸಿದ ಸರ್ವರ್ ಅದಾಗಿರದೇ ಇರಬಹುದು (ನೀವು ದಾಳಿಕೋರರೊಂದಿಗೆ ಸಂವಹನ ಮಾಡುತ್ತಿರಬಹುದು).</translation>
<translation id="6831043979455480757">ಅನುವಾದಿಸು</translation>
<translation id="6839929833149231406">ಪ್ರದೇಶ</translation>
+<translation id="6852204201400771460">ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಪುನಃ ಆರಂಭಿಸಬೇಕೆ?</translation>
+<translation id="6865412394715372076">ಈ ಕಾರ್ಡ್ ಅನ್ನು ಈಗಲೇ ಪರಿಶೀಲಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ</translation>
<translation id="6874604403660855544">&amp;ಸೇರಿಸುವುದನ್ನು ಮತ್ತೆಮಾಡು</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">ನೀತಿಯ ಮಟ್ಟವು ಬೆಂಬಲಿತವಾಗಿಲ್ಲ.</translation>
<translation id="6895330447102777224">ನಿಮ್ಮ ಕಾರ್ಡ್ ಅನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ</translation>
<translation id="6897140037006041989">ಬಳಕೆದಾರ ಏಜೆಂಟ್</translation>
+<translation id="6903319715792422884"><ph name="BEGIN_WHITEPAPER_LINK" />ಸಿಸ್ಟಂ ಕುರಿತು ಕೆಲವೊಂದು ಮಾಹಿತಿಯನ್ನು ಮತ್ತು ಪುಟದ ವಿಷಯವನ್ನು<ph name="END_WHITEPAPER_LINK" /> Google ಗೆ ಕಳುಹಿಸುವ ಮೂಲಕ, ಸುರಕ್ಷಿತ ಬ್ರೌಸಿಂಗ್ ಅನ್ನು ಸುಧಾರಿಸಲು ಸಹಾಯ ಮಾಡಿ. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">ಬಳಕೆದಾರ:</translation>
+<translation id="6944692733090228304">ನೀವು ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> ನಿರ್ವಹಣೆ ಮಾಡದ ಸೈಟ್‌ನಲ್ಲಿ ನಮೂದಿಸಿದ್ದೀರಿ. ನಿಮ್ಮ ಖಾತೆಯನ್ನು ರಕ್ಷಿಸಲು, ಇತರ ಆ್ಯಪ್‌ಗಳಲ್ಲಿ ಮತ್ತು ಸೈಟ್‌ಗಳಲ್ಲಿ ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್‌ ಅನ್ನು ಮರುಬಳಕೆ ಮಾಡಬೇಡಿ.</translation>
<translation id="6945221475159498467">ಆಯ್ಕೆಮಾಡಿ</translation>
<translation id="6948701128805548767">ಪಿಕಪ್ ವಿಧಾನಗಳು ಹಾಗೂ ಆವಶ್ಯಕತೆಗಳನ್ನು ನೋಡಲು, ಒಂದು ವಿಳಾಸವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ</translation>
<translation id="6949872517221025916">ಪಾಸ್‌ವರ್ಡ್ ಮರುಹೊಂದಿಸಿ</translation>
+<translation id="6950684638814147129">JSON ಮೌಲ್ಯವನ್ನು ವಿಶ್ಲೇಷಿಸುವಲ್ಲಿ ದೋಷ: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">ಸರ್ವರ್‌ಗಳ ಪ್ರಮಾಣಪತ್ರವು ನಕಲಿಯಾಗಿ ಗೋಚರಿಸುತ್ತದೆ.</translation>
<translation id="6965382102122355670">ಸರಿ</translation>
<translation id="6965978654500191972">ಸಾಧನ</translation>
@@ -870,6 +869,7 @@
&lt;li&gt;&lt;strong&gt;ಸೇವೆಯ ಸ್ಥಿತಿಯ&lt;/strong&gt; ಅಡಿಯಲ್ಲಿ, &lt;strong&gt;ನಿಲ್ಲಿಸಿ&lt;/strong&gt; ಕ್ಲಿಕ್ ಮಾಡಿ
&lt;li&gt; &lt;strong&gt;ಅನ್ವಯಿಸಿ&lt;/strong&gt; ಕ್ಲಿಕ್ ಮಾಡಿ, ನಂತರ &lt;strong&gt;ಸರಿ&lt;/strong&gt; ಕ್ಲಿಕ್ ಮಾಡಿ
&lt;li&gt;ನಿಮ್ಮ ಕಂಪ್ಯೂಟರ್‌ನಿಂದ ಸಾಫ್ಟ್‌ವೇರ್ ಅನ್ನು ಶಾಶ್ವತವಾಗಿ ತೆಗೆದುಹಾಕುವುದು ಹೇಗೆ ಎಂದು ತಿಳಿದುಕೊಳ್ಳಲು &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome ಸಹಾಯ ಕೇಂದ್ರಕ್ಕೆ&lt;/a&gt; ಭೇಟಿ ನೀಡಿ &lt;/ol&gt;</translation>
+<translation id="7416351320495623771">ಪಾಸ್‌ವರ್ಡ್‌ಗಳನ್ನು ನಿರ್ವಹಿಸಿ…</translation>
<translation id="7419106976560586862">ಪ್ರೊಫೈಲ್ ಹಾದಿ</translation>
<translation id="7437289804838430631">ಸಂಪರ್ಕ ಮಾಹಿತಿಯನ್ನು ಸೇರಿಸು</translation>
<translation id="7441627299479586546">ತಪ್ಪಾದ ನೀತಿಯ ವಿಷಯ</translation>
@@ -878,7 +878,6 @@
<translation id="7451311239929941790">ಈ ಸಮಸ್ಯೆಯ ಕುರಿತು <ph name="BEGIN_LINK" />ಇನ್ನಷ್ಟು ತಿಳಿಯುವಿಕೆ<ph name="END_LINK" />.</translation>
<translation id="7455133967321480974">ಜಾಗತಿಕ ಡಿಫಾಲ್ಟ್ ಬಳಸಿ (ನಿರ್ಬಂಧಿಸಿ)</translation>
<translation id="7460163899615895653">ಇತರ ಸಾಧನಗಳಿಂದ ನಿಮ್ಮ ಇತ್ತೀಚಿನ ಟ್ಯಾಬ್‌ಗಳು ಇಲ್ಲಿ ಗೋಚರಿಸುತ್ತವೆ</translation>
-<translation id="7469372306589899959">ಕಾರ್ಡ್‌ ದೃಢೀಕರಿಸಲಾಗುತ್ತಿದೆ</translation>
<translation id="7473891865547856676">ಇಲ್ಲ, ಧನ್ಯವಾದಗಳು</translation>
<translation id="7481312909269577407">ಫಾರ್ವರ್ಡ್</translation>
<translation id="7485870689360869515">ಯಾವುದೇ ಡೇಟಾ ಕಂಡುಬಂದಿಲ್ಲ.</translation>
@@ -1008,6 +1007,7 @@
<translation id="8308427013383895095">ನೆಟ್‌ವರ್ಕ್ ಸಂಪರ್ಕದಲ್ಲಿನ ಸಮಸ್ಯೆಯಿಂದಾಗಿ ಭಾಷಾಂತರವು ವಿಫಲವಾಗಿದೆ.</translation>
<translation id="8311129316111205805">ಸೆಶನ್ ಲೋಡ್ ಮಾಡಿ</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> ಗೆ ಪ್ರವೇಶವನ್ನು ನಿರಾಕರಿಸಲಾಗಿದೆ</translation>
+<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="8349305172487531364">ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳ ಬಾರ್</translation>
<translation id="8363502534493474904">ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್ ಆಫ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ</translation>
@@ -1028,21 +1028,25 @@
<translation id="8498891568109133222">ಪ್ರತಿಕ್ರಿಯಿಸಲು <ph name="HOST_NAME" /> ಹೆಚ್ಚು ಸಮಯ ತೆಗೆದುಕೊಂಡಿದೆ.</translation>
<translation id="8503559462189395349">Chrome ಪಾಸ್‌ವರ್ಡ್‌ಗಳು</translation>
<translation id="8503813439785031346">ಬಳಕೆದಾರಹೆಸರು</translation>
+<translation id="8508648098325802031">ಹುಡುಕಾಟದ ಐಕಾನ್</translation>
<translation id="8543181531796978784">ನೀವು <ph name="BEGIN_ERROR_LINK" />ಪತ್ತೆ ಹಚ್ಚುವಿಕೆ ಸಮಸ್ಯೆಯನ್ನು ವರದಿ ಮಾಡಬಹುದು<ph name="END_ERROR_LINK" /> ಅಥವಾ ನಿಮ್ಮ ಭದ್ರತೆಯ ಅಪಾಯಗಳ ಕುರಿತು ನಿಮಗೆ ಅರ್ಥವಾಗಿದ್ದರೆ, <ph name="BEGIN_LINK" />ಈ ಅಸುರಕ್ಷಿತ ಸೈಟ್‌ಗೆ ಭೇಟಿ ನೀಡಿ<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">ಪ್ರಶ್ನೆಗಳಿವೆಯೇ? ನಿಮ್ಮ ಪ್ರೊಫೈಲ್‌ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡುವ ವ್ಯಕ್ತಿಯನ್ನು ಸಂಪರ್ಕಸಿ.</translation>
<translation id="8553075262323480129">ಪುಟದ ಭಾಷೆಯನ್ನು ಗುರುತಿಸಲು ಅಸಾಧ್ಯವಾದ ಕಾರಣ ಭಾಷಾಂತರವು ವಿಫಲವಾಗಿದೆ.</translation>
<translation id="8557066899867184262">ನಿಮ್ಮ ಕಾರ್ಡ್ ಹಿಂಬದಿಯಲ್ಲಿ CVC ಇರುತ್ತದೆ.</translation>
<translation id="8559762987265718583"><ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ಗೆ ಖಾಸಗಿ ಸಂಪರ್ಕವನ್ನು ಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ ಏಕೆಂದರೆ ನಿಮ್ಮ ಸಾಧನದ ದಿನಾಂಕ ಮತ್ತು ಸಮಯ (<ph name="DATE_AND_TIME" />) ತಪ್ಪಾಗಿದೆ.</translation>
+<translation id="8564985650692024650">ನೀವು ಇತರ ಸೈಟ್‌ಗಳಲ್ಲಿ ಪಾಸ್‌ವರ್ಡ್‌ ಅನ್ನು ಮರುಬಳಕೆ ಮಾಡಿದ್ದಲ್ಲಿ ನಿಮ್ಮ <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> ಪಾಸವರ್ಡ್ ಅನ್ನು ಮರುಹೊಂದಿಸಲು Chromium ಶಿಫಾರಸು ಮಾಡುತ್ತದೆ.</translation>
<translation id="8571890674111243710"><ph name="LANGUAGE" /> ಗೆ ಪುಟವನ್ನು ಭಾಷಾಂತರಿಸಲಾಗುತ್ತಿದೆ...</translation>
<translation id="858637041960032120">ಫೋನ್ ಸಂ. ಸೇರಿಸಿ
</translation>
<translation id="859285277496340001">ಇದನ್ನು ರದ್ದುಮಾಡಲಾಗಿದೆಯೆ ಎಂದು ಪರಿಶೀಲಿಸಲು ಪ್ರಮಾಣಪತ್ರವು ಯಾಂತ್ರೀಕರಣವನ್ನು ನಿರ್ದಿಷ್ಟಪಡಿಸಿಲ್ಲ.</translation>
+<translation id="860043288473659153">ಕಾರ್ಡ್‌ಹೋಲ್ಡರ್ ಹೆಸರು</translation>
<translation id="8620436878122366504">ನಿಮ್ಮ ಪೋಷಕರು ಇನ್ನೂ ಇದನ್ನು ಅಂಗೀಕರಿಸಿಲ್ಲ</translation>
<translation id="8625384913736129811">ಈ ಕಾರ್ಡ್ ಅನ್ನು ಈ ಸಾಧನದಲ್ಲಿ ಉಳಿಸಿ</translation>
-<translation id="8639963783467694461">ಸ್ವಯಂತುಂಬುವಿಕೆ ಸೆಟ್ಟಿಂಗ್‌ಗಳು</translation>
+<translation id="8663226718884576429">ಆರ್ಡರ್ ಸಾರಾಂಶ, <ph name="TOTAL_LABEL" />, ಹೆಚ್ಚಿನ ವಿವರಗಳು</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, ಉತ್ತರ, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> ಗೆ ನಿಮ್ಮ ಸಂಪರ್ಕವು ಎನ್‌ಕ್ರಿಪ್ಟ್ ಆಗಿಲ್ಲ.</translation>
<translation id="8718314106902482036">ಪಾವತಿ ಪೂರ್ಣಗೊಂಡಿಲ್ಲ</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, ಹುಡುಕಾಟ ಸಲಹೆ</translation>
<translation id="8725066075913043281">ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ</translation>
<translation id="8728672262656704056">ನೀವು ಅದೃಶ್ಯರಾಗಿರುವಿರಿ</translation>
<translation id="8730621377337864115">ಮುಗಿದಿದೆ</translation>
@@ -1058,8 +1062,10 @@
<translation id="8820817407110198400">ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳು</translation>
<translation id="883848425547221593">ಇತರ ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳು:</translation>
<translation id="884264119367021077">ಶಿಪ್ಪಿಂಗ್‌ ವಿಳಾಸ</translation>
+<translation id="8846319957959474018">ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳ ಮೂಲಕ ಸುಲಭವಾಗಿ ಆ್ಯಪ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ</translation>
<translation id="884923133447025588">ವಿಫಲವಾದ ಕಾರ್ಯತಂತ್ರ ಪತ್ತೆಯಾಗಿಲ್ಲ.</translation>
<translation id="885730110891505394">Google ಜೊತೆಗೆ ಹಂಚಿಕೊಳ್ಳುವುದು</translation>
+<translation id="8858065207712248076">ನೀವು ಇತರ ಸೈಟ್‌ಗಳಲ್ಲಿ ಪಾಸ್‌ವರ್ಡ್‌ ಅನ್ನು ಮರುಬಳಕೆ ಮಾಡಿದ್ದಲ್ಲಿ Chrome ನಿಮ್ಮ <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> ಪಾಸವರ್ಡ್ ಅನ್ನು ಮರುಹೊಂದಿಸಲು ಶಿಫಾರಸು ಮಾಡುತ್ತದೆ.</translation>
<translation id="8866481888320382733">ನೀತಿಯ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಪಾಸ್ ಮಾಡುವಲ್ಲಿ ದೋಷ</translation>
<translation id="8870413625673593573">ಇತ್ತೀಚೆಗೆ ಮುಚ್ಚಿರುವುದು</translation>
<translation id="8874824191258364635">ಮಾನ್ಯವಾದ ಕಾರ್ಡ್ ಸಂಖ್ಯೆಯನ್ನು ನಮೂದಿಸಿ</translation>
@@ -1098,6 +1104,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> ಸಾಮಾನ್ಯವಾಗಿ ನಿಮ್ಮ ಮಾಹಿತಿಯನ್ನು ಸಂರಕ್ಷಿಸಲು ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಪ್ರಯೋಜನವನ್ನು ಬಳಸಿಕೊಳ್ಳುತ್ತದೆ. ಈ ಸಂದರ್ಭದಲ್ಲಿ Chromium <ph name="SITE" /> ವೆಬ್‌ಸೈಟ್‌ಗೆ ಸಂಪರ್ಕಿಸಲು ಪ್ರಯತ್ನಿಸಿದಾಗ, ಆ ವೆಬ್‌ಸೈಟ್‌‌ ಅಸಹಜ ಮತ್ತು ತಪ್ಪು ರುಜುವಾತುಗಳನ್ನು ಹಿಂತಿರುಗಿಸಿದೆ. ದಾಳಿಕೋರರು <ph name="SITE" /> ರೂಪದಲ್ಲಿ ಸೋಗು ಹಾಕಲು ಪ್ರಯತ್ನಿಸುತ್ತಿರುವಾಗ ಅಥವಾ ವೈ-ಫೈ ಸೈನ್-ಇನ್ ಪರದೆಯು ಸಂಪರ್ಕಕ್ಕೆ ಅಡ್ಡಿಯುಂಟು ಮಾಡಿದಾಗ ಇದು ಕಂಡುಬರಬಹುದು. ಯಾವುದೇ ಡೇಟಾವನ್ನು ವಿನಿಮಯ ಮಾಡಿಕೊಳ್ಳುವ ಮೊದಲೇ Chromium ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಿರುವ ಕಾರಣ, ನಿಮ್ಮ ಮಾಹಿತಿ ಈಗಲೂ ಸುರಕ್ಷಿತವಾಗಿದೆ.</translation>
<translation id="9106062320799175032">ಬಿಲ್ಲಿಂಗ್ ವಿಳಾಸವನ್ನು ಸೇರಿಸಿ</translation>
<translation id="910908805481542201">ಇದನ್ನು ಸರಿಪಡಿಸಲು ನನಗೆ ಸಹಾಯ ಮಾಡಿ</translation>
+<translation id="9114524666733003316">ಕಾರ್ಡ್‌ ದೃಢೀಕರಿಸಲಾಗುತ್ತಿದೆ...</translation>
<translation id="9128870381267983090">ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಸಂಪರ್ಕಿಸು</translation>
<translation id="9137013805542155359">ಮೂಲವನ್ನು ತೋರಿಸಿ</translation>
<translation id="9137248913990643158">ಈ ಅಪ್ಲಿಕೇಶನ್ ಬಳಸುವ ಮೊದಲು Chrome ಪ್ರಾರಂಭಿಸಿ ಮತ್ತು ಸೈನ್ ಇನ್ ಮಾಡಿ.</translation>
@@ -1108,6 +1115,7 @@
<translation id="9168814207360376865">ನೀವು ಪಾವತಿ ವಿಧಾನಗಳನ್ನು ಉಳಿಸಿದಲ್ಲಿ ಪರಿಶೀಲಿಸಲು ಸೈಟ್‌ಗಳನ್ನು ಅನುಮತಿಸಿ</translation>
<translation id="9169664750068251925">ಈ ಸೈಟ್ ಅನ್ನು ಯಾವಾಗಲೂ ನಿರ್ಬಂಧಿಸು</translation>
<translation id="9170848237812810038">&amp;ರದ್ದುಮಾಡು</translation>
+<translation id="9171296965991013597">ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ತ್ಯಜಿಸಬೇಕೆ?</translation>
<translation id="917450738466192189">ಸರ್ವರ್‌ನ ಪ್ರಮಾಣಪತ್ರವು ಅಮಾನ್ಯವಾಗಿದೆ.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> ಬೆಂಬಲಿತವಲ್ಲದ ಪ್ರೋಟೋಕಾಲ್ ಬಳಸುತ್ತಿದೆ.</translation>
<translation id="9205078245616868884">ನಿಮ್ಮ ಡೇಟಾವನ್ನು ನಿಮ್ಮ ಸಿಂಕ್ ಪಾಸ್‌ಫ್ರೇಸ್‌ನೊಂದಿಗೆ ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಲಾಗಿದೆ. ಸಿಂಕ್ ಪ್ರಾರಂಭಿಸಲು ಅದನ್ನು ನಮೂದಿಸಿ.</translation>
diff --git a/chromium/components/strings/components_strings_ko.xtb b/chromium/components/strings/components_strings_ko.xtb
index a3a577dba6b..0d93092068f 100644
--- a/chromium/components/strings/components_strings_ko.xtb
+++ b/chromium/components/strings/components_strings_ko.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">값 숨기기</translation>
<translation id="1228893227497259893">잘못된 개체 식별자</translation>
<translation id="1232569758102978740">제목 없음</translation>
+<translation id="1250759482327835220">다음번에 더 빠르게 결제할 수 있도록 Google 계정에 카드, 이름, 청구서 수신 주소를 저장하세요.</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;
@@ -97,7 +98,6 @@
&lt;p&gt;&lt;strong&gt;설정&lt;/strong&gt; 앱의 &lt;strong&gt;일반&lt;/strong&gt; 섹션에서 날짜와 시간을 맞추세요.&lt;/p&gt;</translation>
<translation id="1583429793053364125">이 웹페이지를 표시하는 도중 문제가 발생했습니다.</translation>
-<translation id="1590457302292452960">안전한 비밀번호를 만드세요...</translation>
<translation id="1592005682883173041">로컬 데이터 액세스</translation>
<translation id="1594030484168838125">선택</translation>
<translation id="1620510694547887537">카메라</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">시스템 관리자에게 문의하세요.</translation>
<translation id="1740951997222943430">올바른 만료 월을 입력하세요.</translation>
+<translation id="1743520634839655729">다음번에 더 빠르게 결제할 수 있도록 Google 계정과 기기에 카드, 이름, 청구서 수신 주소를 저장하세요.</translation>
<translation id="17513872634828108">열린 탭</translation>
<translation id="1753706481035618306">페이지 번호</translation>
<translation id="1763864636252898013">이 서버가 <ph name="DOMAIN" />임을 입증할 수 없으며 기기의 운영체제에서 신뢰하는 보안 인증서가 아닙니다. 서버를 잘못 설정했거나 불법 사용자가 연결을 가로채고 있기 때문일 수 있습니다.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">열린 탭이 여기에 표시됩니다.</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">카드 소유자 이름</translation>
-<translation id="1806541873155184440">추가일: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">잘못된 요청 또는 요청 매개변수</translation>
<translation id="1826516787628120939">확인 중</translation>
<translation id="1834321415901700177">이 사이트에 유해한 프로그램이 있습니다.</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{제안 1개}other{제안 #개}}</translation>
<translation id="2079545284768500474">실행취소</translation>
<translation id="20817612488360358">시스템 프록시 설정이 사용하도록 설정되었지만 명시적 프록시 설정도 지정되어 있습니다.</translation>
-<translation id="2084558088529668945"><ph name="ORG_NAME" />에서 관리하지 않는 사이트에 내 비밀번호를 입력했습니다. 다른 앱과 사이트에서 비밀번호를 재사용하면 계정을 안전하게 보호할 수 없습니다.</translation>
<translation id="2091887806945687916">소리</translation>
<translation id="2094505752054353250">도메인이 일치하지 않음</translation>
<translation id="2096368010154057602">자치구</translation>
+<translation id="2102134110707549001">강력한 비밀번호 추천...</translation>
<translation id="2108755909498034140">컴퓨터 다시 시작</translation>
<translation id="2113977810652731515">카드</translation>
<translation id="2114841414352855701"><ph name="POLICY_NAME" />이(가) 우선 적용되었기 때문에 무시됩니다.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP 오류</translation>
<translation id="2270484714375784793">전화번호</translation>
<translation id="2292556288342944218">인터넷 액세스가 차단됨</translation>
-<translation id="230155334948463882">새 카드인가요?</translation>
<translation id="2316887270356262533">1MB 미만의 저장용량을 확보합니다. 일부 사이트는 다음 방문 시 로드 속도가 느려질 수 있습니다.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" />에 사용자 이름과 비밀번호를 입력해야 합니다.</translation>
<translation id="2317583587496011522">직불카드를 사용할 수 있습니다.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">사용할 수 있는 카드</translation>
<translation id="2702801445560668637">읽기 목록</translation>
<translation id="2704283930420550640">값이 형식과 일치하지 않습니다.</translation>
-<translation id="2704951214193499422">현재 Chromium에서 카드를 확인할 수 없습니다. 나중에 다시 시도해 주세요.</translation>
<translation id="2705137772291741111">사이트의 저장된(캐시된) 사본을 읽을 수 없습니다.</translation>
<translation id="2709516037105925701">자동 완성</translation>
<translation id="2710942282213947212">컴퓨터에 설치된 소프트웨어로 인해 Chromium이 안전하게 웹에 접속할 수 없음</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">배송 방법</translation>
<translation id="277499241957683684">기기 기록 없음</translation>
<translation id="2781030394888168909">MacOS 내보내기</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">연결이 재설정되었습니다.</translation>
<translation id="2788784517760473862">사용 가능한 신용카드</translation>
<translation id="2794233252405721443">차단된 사이트</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">설정 페이지의 연결을 구성하는 프록시를 사용 중지할 수 있습니다.</translation>
<translation id="2955913368246107853">검색 바 닫기</translation>
<translation id="2958431318199492670">네트워크 설정이 ONC 표준을 준수하지 않습니다. 일부 설정을 가져올 수 없습니다.</translation>
-<translation id="2966678944701946121">만료: <ph name="EXPIRATION_DATE_ABBR" />, 추가일: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">보안 연결을 설정하려면 시계가 올바로 설정되어 있어야 합니다. 웹사이트가 자신을 식별하는 데 사용하는 인증서는 특정 기간에만 유효하기 때문입니다. 기기의 시계가 잘못 설정되어 Chrome에서 이 인증서를 확인할 수 없습니다.</translation>
<translation id="2972581237482394796">다시 실행(&amp;R)</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />이(가) 현재 선택되어 있습니다. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">잘못된 정책 유형</translation>
<translation id="3037605927509011580">앗, 이런!</translation>
<translation id="3041612393474885105">인증서 정보</translation>
-<translation id="3063697135517575841">현재 Chrome에서 카드를 확인할 수 없습니다. 나중에 다시 시도해 주세요.</translation>
<translation id="3064966200440839136">시크릿 모드를 종료하고 외부 애플리케이션에서 결제합니다. 계속하시겠습니까?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{없음}=1{비밀번호 1개}other{비밀번호 #개}}</translation>
<translation id="3096100844101284527">수령 주소 추가</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451"><ph name="TIME" />에 동기화 암호로 데이터가 암호화되었습니다. 동기화를 시작하려면 입력하세요.</translation>
<translation id="3320021301628644560">청구지 주소 추가</translation>
<translation id="3338095232262050444">보안 연결</translation>
-<translation id="3340978935015468852">설정</translation>
<translation id="3345135638360864351">이 사이트에 대한 액세스 요청을 <ph name="NAME" />님에게 보내지 못했습니다. 나중에 다시 시도해 주세요.</translation>
<translation id="3355823806454867987">프록시 설정 변경...</translation>
<translation id="3361596688432910856">Chrome에 다음 정보가 <ph name="BEGIN_EMPHASIS" />저장되지 않습니다<ph name="END_EMPHASIS" />.
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">서버의 인증서를 신뢰할 수 없습니다.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{동기화된 기기에 항목 1개 이상}=1{항목 1개(동기화된 기기에는 그 이상)}other{항목 #개(동기화된 기기에는 그 이상)}}</translation>
<translation id="3539171420378717834">카드 사본을 이 기기에 저장</translation>
-<translation id="3542684924769048008">비밀번호 사용 항목:</translation>
<translation id="3549644494707163724">나만의 동기화 암호로 모든 동기화 데이터 암호화</translation>
<translation id="3556433843310711081">관리자가 차단 해제할 수 있습니다.</translation>
<translation id="3566021033012934673">연결이 비공개로 설정되어 있지 않습니다.</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">잘못된 인증 서명입니다.</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{그 외 <ph name="ITEM_COUNT" />개 항목}other{그 외 <ph name="ITEM_COUNT" />개 항목}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">다른 사이트에서 비밀번호를 재사용했다면 <ph name="ORG_NAME" /> 비밀번호를 재설정하는 것이 좋습니다.</translation>
<translation id="4196861286325780578">이동 다시 실행(&amp;R)</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />방화벽 및 바이러스 백신 소프트웨어 설정 확인<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">비정상 종료</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">변경사항이 저장되지 않을 수 있습니다.</translation>
<translation id="4258748452823770588">잘못된 서명</translation>
<translation id="4265872034478892965">관리자가 허용함</translation>
-<translation id="4269787794583293679">(사용자 이름 없음)</translation>
<translation id="4275830172053184480">기기 다시 시작</translation>
<translation id="4277028893293644418">비밀번호 재설정</translation>
<translation id="4280429058323657511">, 만료일 <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">팝업 및 리디렉션</translation>
<translation id="443673843213245140">프록시 사용은 중지되었지만 명시적 프록시 설정이 지정되어 있습니다.</translation>
<translation id="445100540951337728">사용 가능한 직불카드</translation>
+<translation id="4472575034687746823">시작하기</translation>
<translation id="4506176782989081258">유효성 검사 오류 <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">시스템 관리자에게 문의</translation>
<translation id="450710068430902550">관리자와 공유</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">정책 새로고침</translation>
<translation id="4728558894243024398">플랫폼</translation>
<translation id="4736825316280949806">Chromium 다시 시작</translation>
+<translation id="4742407542027196863">비밀번호 관리...</translation>
<translation id="4744603770635761495">실행 가능 경로</translation>
-<translation id="4749685221585524849">마지막 사용일: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">비밀번호나 신용카드 번호 등의 정보는 비공개 상태로 이 사이트에 전송됩니다.</translation>
<translation id="4756388243121344051">방문 기록(&amp;H)</translation>
<translation id="4758311279753947758">연락처 정보 추가</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">보기</translation>
<translation id="4854362297993841467">사용할 수 없는 배달 방법입니다. 다른 방법을 선택하세요.</translation>
<translation id="4858792381671956233">이 사이트를 방문해도 괜찮은지 부모님께 문의했습니다.</translation>
+<translation id="4876305945144899064">사용자 이름 없음</translation>
<translation id="4880827082731008257">기록 검색</translation>
<translation id="4881695831933465202">열기</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -580,7 +575,7 @@
<translation id="5039804452771397117">허용</translation>
<translation id="5040262127954254034">개인정보</translation>
<translation id="5045550434625856497">비밀번호가 잘못되었습니다.</translation>
-<translation id="5056549851600133418">추천 도움말</translation>
+<translation id="5056549851600133418">추천 기사</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />프록시 주소 확인<ph name="END_LINK" /></translation>
<translation id="5086888986931078152">일부 사이트의 보호된 콘텐츠에 액세스하지 못할 수 있습니다.</translation>
<translation id="5087286274860437796">서버의 인증서가 현재 유효하지 않습니다.</translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">시/도</translation>
<translation id="5094747076828555589">이 서버가 <ph name="DOMAIN" />임을 입증할 수 없으며 Chromium에서 신뢰하는 보안 인증서가 아닙니다. 서버를 잘못 설정했거나 불법 사용자가 연결을 가로채고 있기 때문일 수 있습니다.</translation>
<translation id="5095208057601539847">주/도</translation>
+<translation id="5098332213681597508">Google 계정에 등록된 이름입니다.</translation>
<translation id="5115563688576182185">(64비트)</translation>
<translation id="5121084798328133320">카드를 확인하면 Google Payments 계정의 카드 세부정보가 이 사이트와 공유됩니다.</translation>
<translation id="5128122789703661928">세션 이름이 잘못되어 삭제할 수 없습니다.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">배송 방법</translation>
<translation id="5355557959165512791">인증서가 취소되었기 때문에 현재 <ph name="SITE" />에 방문할 수 없습니다. 네트워크 오류와 공격은 대부분 일시적이므로 나중에 이 페이지가 정상적으로 작동할 수 있습니다.</translation>
<translation id="536296301121032821">정책 설정 저장 실패</translation>
+<translation id="5371425731340848620">카드 업데이트</translation>
<translation id="5377026284221673050">'시간이 너무 먼 과거로 설정되어 있습니다.', '시간이 너무 먼 미래로 설정되어 있습니다.', 또는 '&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;'</translation>
<translation id="5386426401304769735">이 사이트의 인증서 체인은 SHA-1을 사용하여 서명된 인증서를 포함합니다.</translation>
-<translation id="5402410679244714488">만료: <ph name="EXPIRATION_DATE_ABBR" />, 최소 1년 전에 마지막으로 사용됨</translation>
+<translation id="5387961145478138773">즐겨 사용하는 Google 앱에 빠르게 액세스합니다.</translation>
<translation id="540969355065856584">이 서버가 <ph name="DOMAIN" />임을 입증할 수 없습니다. 서버의 보안 인증서가 현재 유효하지 않습니다. 서버를 잘못 설정했거나 해커가 연결을 가로채고 있기 때문일 수 있습니다.</translation>
<translation id="5421136146218899937">인터넷 사용 기록 삭제...</translation>
<translation id="5430298929874300616">북마크 삭제</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">이메일</translation>
+<translation id="5666899935841546222">모든 카드를 한곳에 보관하시겠습니까?</translation>
<translation id="5675650730144413517">페이지가 작동하지 않습니다.</translation>
<translation id="5685654322157854305">배송지 주소 추가</translation>
<translation id="5689199277474810259">JSON 형식으로 내보내기</translation>
<translation id="5689516760719285838">위치</translation>
<translation id="570530837424789914">관리...</translation>
+<translation id="57094364128775171">강력한 비밀번호 추천...</translation>
<translation id="5710435578057952990">이 웹사이트의 주소가 확인되지 않았습니다.</translation>
<translation id="5719499550583120431">선불카드를 사용할 수 있습니다.</translation>
<translation id="5720705177508910913">현재 사용자</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">사이트에 연결할 수 없음</translation>
<translation id="5869522115854928033">저장된 비밀번호</translation>
<translation id="5893752035575986141">신용카드를 사용할 수 있습니다.</translation>
-<translation id="5898382028489516745">다른 사이트에서 비밀번호를 재사용했다면 <ph name="ORG_NAME" /> 비밀번호를 재설정하는 것이 좋습니다.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" />(동기화됨)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1개 사용 중}other{#개 사용 중}}</translation>
<translation id="5939518447894949180">초기화</translation>
-<translation id="5959728338436674663">위험한 앱과 사이트를 감지할 수 있도록 일부 <ph name="BEGIN_WHITEPAPER_LINK" />시스템 정보와 페이지 콘텐츠<ph name="END_WHITEPAPER_LINK" />를 Google로 자동 전송합니다. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">연락처 정보 수정</translation>
<translation id="5967867314010545767">기록에서 삭제</translation>
<translation id="5975083100439434680">축소</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">우편번호</translation>
<translation id="6290238015253830360">추천 기사가 여기에 표시됩니다.</translation>
<translation id="6305205051461490394"><ph name="URL" />에 연결할 수 없습니다.</translation>
-<translation id="6319915415804115995">최소 1년 전에 마지막으로 사용됨</translation>
<translation id="6321917430147971392">DNS 설정 확인</translation>
<translation id="6328639280570009161">네트워크 예측을 사용 중지해 보세요.</translation>
<translation id="6328786501058569169">사기성 사이트</translation>
<translation id="6337133576188860026"><ph name="SIZE" /> 미만의 저장용량을 확보합니다. 일부 사이트는 다음 방문 시 로드 속도가 느려질 수 있습니다.</translation>
<translation id="6337534724793800597">이름별로 정책 필터링</translation>
-<translation id="6342069812937806050">완료됨</translation>
<translation id="6355080345576803305">공개 세션 무시</translation>
<translation id="6358450015545214790">자세히 알아보기</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{그 외 제안 1건}other{그 외 제안 #건}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">삭제 다시 실행(&amp;R)</translation>
<translation id="6534179046333460208">피지컬 웹 제안</translation>
<translation id="6550675742724504774">옵션</translation>
-<translation id="6556915248009097796">만료: <ph name="EXPIRATION_DATE_ABBR" />, 마지막 사용일: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">관리자가 아직 승인하지 않았습니다.</translation>
<translation id="6569060085658103619">확장 프로그램 페이지를 보는 중</translation>
<translation id="6596325263575161958">암호화 옵션</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703"><ph name="DOMAIN" />에 접속하려 했으나 서버에서 안전성이 낮은 서명 알고리즘을 사용하여 서명된 인증서(예: SHA-1)를 전달했습니다. 이는 서버에서 전달한 보안 사용자 인증 정보가 위조되었을 수 있으며 사용하려는 서버가 아님을 의미합니다. 서버를 가장한 공격자와 통신 중일 수 있습니다.</translation>
<translation id="6831043979455480757">번역</translation>
<translation id="6839929833149231406">지역</translation>
+<translation id="6852204201400771460">앱을 새로고침하시겠습니까?</translation>
+<translation id="6865412394715372076">지금은 이 카드를 인증할 수 없습니다.</translation>
<translation id="6874604403660855544">추가 다시 실행(&amp;R)</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">정책 수준이 지원되지 않습니다.</translation>
<translation id="6895330447102777224">카드가 확인되었습니다.</translation>
<translation id="6897140037006041989">사용자 에이전트</translation>
+<translation id="6903319715792422884">일부 <ph name="BEGIN_WHITEPAPER_LINK" />시스템 정보와 페이지 콘텐츠를<ph name="END_WHITEPAPER_LINK" /> Google로 전송하여 세이프 브라우징을 개선하도록 도와주세요. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">사용자:</translation>
+<translation id="6944692733090228304"><ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />에서 관리하지 않는 사이트에 비밀번호를 입력했습니다. 계정을 안전하게 보호하려면 다른 앱과 사이트에서 동일한 비밀번호를 재사용하지 마세요.</translation>
<translation id="6945221475159498467">선택</translation>
<translation id="6948701128805548767">수령 방법과 요구사항을 확인하려면 주소를 선택하세요.</translation>
<translation id="6949872517221025916">비밀번호 재설정</translation>
+<translation id="6950684638814147129">JSON 값을 파싱하는 중에 오류가 발생했습니다. <ph name="ERROR" /></translation>
<translation id="6957887021205513506">서버의 인증서가 위조된 것 같습니다.</translation>
<translation id="6965382102122355670">확인</translation>
<translation id="6965978654500191972">기기</translation>
@@ -876,6 +875,7 @@
&lt;li&gt;&lt;strong&gt;서비스 상태&lt;/strong&gt;에서 &lt;strong&gt;중지&lt;/strong&gt;를 클릭합니다.
&lt;li&gt;&lt;strong&gt;적용&lt;/strong&gt;을 클릭한 다음 &lt;strong&gt;확인&lt;/strong&gt;을 클릭합니다.
&lt;li&gt;&lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome 고객센터&lt;/a&gt;를 방문하여 컴퓨터에서 소프트웨어를 영구적으로 삭제하는 방법을 알아보세요. &lt;/ol&gt;</translation>
+<translation id="7416351320495623771">비밀번호 관리...</translation>
<translation id="7419106976560586862">프로필 경로</translation>
<translation id="7437289804838430631">연락처 정보 추가</translation>
<translation id="7441627299479586546">잘못된 정책 주체</translation>
@@ -884,7 +884,6 @@
<translation id="7451311239929941790">이 문제를 <ph name="BEGIN_LINK" />자세히 알아보기<ph name="END_LINK" /></translation>
<translation id="7455133967321480974">전체 기본값 사용(차단)</translation>
<translation id="7460163899615895653">다른 기기에서 최근에 사용한 탭이 여기에 표시됩니다.</translation>
-<translation id="7469372306589899959">카드 확인 중</translation>
<translation id="7473891865547856676">건너뛰기</translation>
<translation id="7481312909269577407">앞으로</translation>
<translation id="7485870689360869515">데이터 없음</translation>
@@ -1014,6 +1013,7 @@
<translation id="8308427013383895095">네트워크 연결 문제로 인해 번역에 실패했습니다.</translation>
<translation id="8311129316111205805">세션 로드</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" />에 대한 액세스가 거부됨</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_YEAR" />/<ph name="EXPIRATION_MONTH" /></translation>
<translation id="834457929814110454">보안 관련 위험을 이해한다면 악성 프로그램이 삭제되기 전에 <ph name="BEGIN_LINK" />이 사이트를 방문<ph name="END_LINK" />해도 됩니다.</translation>
<translation id="8349305172487531364">북마크바</translation>
<translation id="8363502534493474904">비행기 모드 사용 중지</translation>
@@ -1034,20 +1034,24 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" />에서 응답하는 데 시간이 너무 오래 걸립니다.</translation>
<translation id="8503559462189395349">Chrome 비밀번호</translation>
<translation id="8503813439785031346">사용자이름</translation>
+<translation id="8508648098325802031">검색 아이콘</translation>
<translation id="8543181531796978784"><ph name="BEGIN_ERROR_LINK" />감지 문제를 신고<ph name="END_ERROR_LINK" />할 수 있으며, 보안에 미치는 위험을 감수한다면 <ph name="BEGIN_LINK" />이 안전하지 않은 사이트를 방문<ph name="END_LINK" />할 수 있습니다.</translation>
<translation id="8543556556237226809">질문이 있으신가요? 프로필 관리자에게 문의하세요.</translation>
<translation id="8553075262323480129">페이지의 언어를 결정할 수 없으므로 번역하지 못했습니다.</translation>
<translation id="8557066899867184262">CVC는 카드 뒷면에 있습니다.</translation>
<translation id="8559762987265718583">기기의 날짜와 시간(<ph name="DATE_AND_TIME" />)이 잘못되어 <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />에 대한 비공개 연결을 설정할 수 없습니다.</translation>
+<translation id="8564985650692024650">다른 사이트에서 비밀번호를 재사용했다면 <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> 비밀번호를 재설정하는 것이 좋습니다.</translation>
<translation id="8571890674111243710">페이지를 <ph name="LANGUAGE" />(으)로 번역 중...</translation>
<translation id="858637041960032120">번호 추가</translation>
<translation id="859285277496340001">인증서는 취소 여부를 확인하는 매커니즘을 지정하지 않습니다.</translation>
+<translation id="860043288473659153">카드 명의자 이름</translation>
<translation id="8620436878122366504">부모님이 아직 승인하지 않았습니다.</translation>
<translation id="8625384913736129811">이 기기에 카드 저장</translation>
-<translation id="8639963783467694461">자동 완성 설정</translation>
+<translation id="8663226718884576429">주문 요약, <ph name="TOTAL_LABEL" />, 세부정보 더보기</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, 답변, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206"><ph name="DOMAIN" />로의 연결은 암호화되지 않습니다.</translation>
<translation id="8718314106902482036">결제가 완료되지 않음</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, 추천 검색어</translation>
<translation id="8725066075913043281">다시 시도하세요</translation>
<translation id="8728672262656704056">시크릿 모드로 전환됨</translation>
<translation id="8730621377337864115">완료</translation>
@@ -1063,8 +1067,10 @@
<translation id="8820817407110198400">북마크</translation>
<translation id="883848425547221593">기타 북마크</translation>
<translation id="884264119367021077">배송지 주소</translation>
+<translation id="8846319957959474018">북마크로 쉽게 앱 열기</translation>
<translation id="884923133447025588">폐기 매커니즘을 찾을 수 없습니다.</translation>
<translation id="885730110891505394">Google과 공유</translation>
+<translation id="8858065207712248076">다른 사이트에서 비밀번호를 재사용했다면 <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> 비밀번호를 재설정하는 것이 좋습니다.</translation>
<translation id="8866481888320382733">정책 설정을 파싱하는 중 오류 발생</translation>
<translation id="8870413625673593573">최근에 닫은 탭</translation>
<translation id="8874824191258364635">올바른 카드 번호를 입력하세요.</translation>
@@ -1103,6 +1109,7 @@
<translation id="9103872766612412690"><ph name="SITE" />에서는 사용자 정보를 보호하기 위해 일반적으로 암호화를 사용합니다. 이번에 Chromium에서 <ph name="SITE" />에 연결을 시도했을 때 웹사이트에서 비정상적이고 잘못된 사용자 인증 정보를 반환했습니다. 이는 공격자가 <ph name="SITE" />인 것처럼 가장하려고 하거나 Wi-Fi 로그인 화면이 연결을 방해했기 때문일 수 있습니다. 데이터 교환이 발생하기 전에 Chromium에서 연결을 중단했으므로 사용자 정보는 안전합니다.</translation>
<translation id="9106062320799175032">청구서 주소 추가</translation>
<translation id="910908805481542201">이 문제 해결에 도움을 주세요.</translation>
+<translation id="9114524666733003316">카드 확인 중...</translation>
<translation id="9128870381267983090">네트워크에 연결</translation>
<translation id="9137013805542155359">원본 보기</translation>
<translation id="9137248913990643158">이 앱을 사용하기 전에 Chrome을 시작하고 로그인하세요.</translation>
@@ -1113,6 +1120,7 @@
<translation id="9168814207360376865">사이트에서 저장된 결제 수단이 있는지 확인하도록 허용</translation>
<translation id="9169664750068251925">이 사이트에서 항상 차단</translation>
<translation id="9170848237812810038">실행 취소(&amp;U)</translation>
+<translation id="9171296965991013597">앱을 종료하시겠습니까?</translation>
<translation id="917450738466192189">서버의 인증서가 유효하지 않습니다.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" />에서 지원되지 않는 프로토콜을 사용합니다.</translation>
<translation id="9205078245616868884">동기화 암호로 데이터가 암호화되어 있습니다. 동기화를 시작하려면 입력하세요.</translation>
diff --git a/chromium/components/strings/components_strings_lt.xtb b/chromium/components/strings/components_strings_lt.xtb
index af7de131aa0..bdd5d7fcbbf 100644
--- a/chromium/components/strings/components_strings_lt.xtb
+++ b/chromium/components/strings/components_strings_lt.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Slėpti vertę</translation>
<translation id="1228893227497259893">Netinkamas subjekto identifikatorius</translation>
<translation id="1232569758102978740">Be pavadinimo</translation>
+<translation id="1250759482327835220">Kad kitą kartą galėtumėte greičiau atlikti mokėjimą, išsaugokite kortelę, vardą bei pavardę ir atsiskaitymo adresą „Google“ paskyroje.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (sinchronizuota)</translation>
<translation id="1256368399071562588">&lt;p&gt;Jei bandote apsilankyti svetainėje, bet nepavyksta jos atidaryti, pirma pabandykite ištaisyti klaidą atlikdami toliau nurodytus trikčių šalinimo veiksmus.&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Koreguokite datą ir laiką programos &lt;strong&gt;Settings&lt;/strong&gt; skiltyje &lt;strong&gt;General&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Rodant šį tinklalapį įvyko nenumatyta klaida.</translation>
-<translation id="1590457302292452960">Sugeneruokite sudėtingą slaptažodį...</translation>
<translation id="1592005682883173041">Prieiga prie vietinių duomenų</translation>
<translation id="1594030484168838125">Pasirinkti</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Pabandykite susisiekti su sistemos administratoriumi.</translation>
<translation id="1740951997222943430">Įveskite tinkamą galiojimo laiko pabaigos mėnesį</translation>
+<translation id="1743520634839655729">Kad kitą kartą galėtumėte greičiau atlikti mokėjimą, išsaugokite kortelę, vardą bei pavardę ir atsiskaitymo adresą „Google“ paskyroje ir šiame įrenginyje.</translation>
<translation id="17513872634828108">Atidaryti skirtukai</translation>
<translation id="1753706481035618306">Puslapio numeris</translation>
<translation id="1763864636252898013">Šiam serveriui nepavyko patvirtinti, kad tai yra <ph name="DOMAIN" />; jo saugos sertifikatas nėra patikimas įrenginio operacinei sistemai. Taip gali nutikti dėl netinkamos konfigūracijos ar dėl ryšį pertraukusio užgrobėjo.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Atidaryti skirtukai bus rodomi čia</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Kortelės savininko vardas</translation>
-<translation id="1806541873155184440">Pridėta <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Netinkama užklausa arba jos parametrai</translation>
<translation id="1826516787628120939">Tikrinama</translation>
<translation id="1834321415901700177">Šioje svetainėje yra kenkėjiškų programų</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 pasiūlymas}one{# pasiūlymas}few{# pasiūlymai}many{# pasiūlymo}other{# pasiūlymų}}</translation>
<translation id="2079545284768500474">Anuliuoti</translation>
<translation id="20817612488360358">Sistemos įgaliotojo serverio nustatymai nustatyti kaip naudotini, bet taip pat nurodyta tiksli įgaliotojo serverio konfigūracija.</translation>
-<translation id="2084558088529668945">Įvedėte slaptažodį svetainėje, kurios netvarko „<ph name="ORG_NAME" />“. Kad apsaugotumėte paskyrą, nenaudokite to paties slaptažodžio kitose programose ir svetainėse.</translation>
<translation id="2091887806945687916">Garsas</translation>
<translation id="2094505752054353250">Domeno neatitikimas</translation>
<translation id="2096368010154057602">Departamentas</translation>
+<translation id="2102134110707549001">Siūlyti sudėtingą slaptažodį…</translation>
<translation id="2108755909498034140">Iš naujo paleiskite kompiuterį</translation>
<translation id="2113977810652731515">Kortelė</translation>
<translation id="2114841414352855701">Nepaisoma, nes buvo pakeista taikant „<ph name="POLICY_NAME" />“.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP klaida</translation>
<translation id="2270484714375784793">Telefono numeris</translation>
<translation id="2292556288342944218">Interneto prieiga užblokuota</translation>
-<translation id="230155334948463882">Nauja kortelė?</translation>
<translation id="2316887270356262533">Atlaisvina mažiau nei 1 MB. Per kitą jūsų apsilankymą kai kurios svetainės gali būti įkeliamos lėčiau.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> būtina įvesti naudotojo vardą ir slaptažodį.</translation>
<translation id="2317583587496011522">Debeto kortelės tinkamos.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Tinkamos kortelės</translation>
<translation id="2702801445560668637">Skait. sąraš.</translation>
<translation id="2704283930420550640">Vertė neatitinka formato.</translation>
-<translation id="2704951214193499422">Šiuo metu „Chromium“ negali patvirtinti jūsų kortelės. Vėliau bandykite dar kartą.</translation>
<translation id="2705137772291741111">Išsaugotos (talpykloje esančios) šios svetainės kopijos negalima skaityti.</translation>
<translation id="2709516037105925701">Automatinis pildymas</translation>
<translation id="2710942282213947212">Programinė įranga jūsų kompiuteryje neleidžia „Chromium“ saugiai prisijungti prie žiniatinklio</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Pristatymo metodas</translation>
<translation id="277499241957683684">Trūksta įrenginio įrašo</translation>
<translation id="2781030394888168909">Eksportuoti „Mac“ OS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Ryšys atkurtas.</translation>
<translation id="2788784517760473862">Tinkamos kredito kortelės</translation>
<translation id="2794233252405721443">Svetainė užblokuota</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Galite neleisti visų tarpinių serverių, jungimasis prie kurių sukonfigūruotas nustatymų puslapyje.</translation>
<translation id="2955913368246107853">Uždaryti paieškos juostą</translation>
<translation id="2958431318199492670">Tinklo konfigūracija neatitinka ONC standarto. Kai kurių konfigūracijos dalių neįmanoma importuoti.</translation>
-<translation id="2966678944701946121">Galiojimo laiko pabaiga: <ph name="EXPIRATION_DATE_ABBR" />, pridėta <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Kad užmegztumėte saugų ryšį, turėsite tinkamai nustatyti laikrodį. To reikia, nes svetainių tapatybei įrodyti naudojami sertifikatai galioja tik tam tikru laikotarpiu. Įrenginio laikrodis nustatytas netinkamai, todėl „Google Chrome“ negali patvirtinti šių sertifikatų.</translation>
<translation id="2972581237482394796">&amp;Atlikti iš naujo</translation>
<translation id="2977665033722899841">„<ph name="ROW_NAME" />“, šiuo metu pasirinkta. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Netinkamas politikos tipas</translation>
<translation id="3037605927509011580">Oi!</translation>
<translation id="3041612393474885105">Sertifikato informacija</translation>
-<translation id="3063697135517575841">Šiuo metu „Chrome“ negali patvirtinti jūsų kortelės. Vėliau bandykite dar kartą.</translation>
<translation id="3064966200440839136">Išjungiate inkognito režimą, kad galėtumėte sumokėti naudodami išorinę programą. Tęsti?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Nėra}=1{1 slaptažodis}one{# slaptažodis}few{# slaptažodžiai}many{# slaptažodžio}other{# slaptažodžių}}</translation>
<translation id="3096100844101284527">Pridėti paėmimo adresą</translation>
@@ -342,7 +339,6 @@
<translation id="3305707030755673451"><ph name="TIME" /> duomenys buvo užšifruoti naudojant sinchronizavimo slaptafrazę. Įveskite ją, kad pradėtumėte sinchronizuoti.</translation>
<translation id="3320021301628644560">Atsiskaitymo adreso pridėjimas</translation>
<translation id="3338095232262050444">Saugi</translation>
-<translation id="3340978935015468852">nustatymų</translation>
<translation id="3345135638360864351">Nepavyko <ph name="NAME" /> išsiųsti jūsų prieigos prie šios svetainės užklausos. Bandykite dar kartą.</translation>
<translation id="3355823806454867987">Pakeisti įgaliotojo serverio nustatymus...</translation>
<translation id="3361596688432910856">„Chrome“ <ph name="BEGIN_EMPHASIS" />nesaugos<ph name="END_EMPHASIS" /> šios informacijos:
@@ -376,7 +372,6 @@
<translation id="3528171143076753409">Serverio sertifikatas nepatikimas.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Mažiausiai 1 elementas sinchronizuotuose įrenginiuose}=1{1 elementas (ir daugiau sinchronizuotuose įrenginiuose)}one{# elementas (ir daugiau sinchronizuotuose įrenginiuose)}few{# elementai (ir daugiau sinchronizuotuose įrenginiuose)}many{# elemento (ir daugiau sinchronizuotuose įrenginiuose)}other{# elementų (ir daugiau sinchronizuotuose įrenginiuose)}}</translation>
<translation id="3539171420378717834">Išsaugoti kortelės kopiją įrenginyje</translation>
-<translation id="3542684924769048008">Slaptažodį naudoti:</translation>
<translation id="3549644494707163724">Šifruoti visus sinchronizuotus duomenis naudojant sinchronizavimo slaptafrazę</translation>
<translation id="3556433843310711081">Jūsų valdytojas gali atblokuoti ją už jus</translation>
<translation id="3566021033012934673">Jūsų ryšys nėra privatus</translation>
@@ -462,7 +457,6 @@
<translation id="4171400957073367226">Netinkamas patvirtinimo parašas</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{Dar <ph name="ITEM_COUNT" /> elementas}one{Dar <ph name="ITEM_COUNT" /> elementas}few{Dar <ph name="ITEM_COUNT" /> elementai}many{Dar <ph name="ITEM_COUNT" /> elemento}other{Dar <ph name="ITEM_COUNT" /> elementų}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">„Chrome“ rekomenduoja iš naujo nustatyti „<ph name="ORG_NAME" />“ slaptažodį, jei naudojate jį kitose svetainėse.</translation>
<translation id="4196861286325780578">&amp;Perkelti dar kartą</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Patikrinti užkardos ir antivirusinės sistemos konfigūracijas<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Gedimai</translation>
@@ -491,7 +485,6 @@
<translation id="425582637250725228">Atlikti pakeitimai gali nebūti išsaugoti.</translation>
<translation id="4258748452823770588">Netinkamas parašas</translation>
<translation id="4265872034478892965">Leidžia jūsų administratorius</translation>
-<translation id="4269787794583293679">(Nėra naudotojo vardo)</translation>
<translation id="4275830172053184480">Iš naujo paleisti įrenginį</translation>
<translation id="4277028893293644418">Iš naujo nustatyti slaptažodį</translation>
<translation id="4280429058323657511">, gal. pab. <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -514,6 +507,7 @@
<translation id="4434045419905280838">Iššok. langai ir peradresavimai</translation>
<translation id="443673843213245140">Įgaliotojo serverio naudojimas neleidžiamas, bet nurodyta aiški įgaliotojo serverio konfigūracija.</translation>
<translation id="445100540951337728">Tinkamos debeto kortelės</translation>
+<translation id="4472575034687746823">Pradėkite</translation>
<translation id="4506176782989081258">Tikrinimo klaida: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Susisiekti su sistemos administratoriumi</translation>
<translation id="450710068430902550">Bendrinimas su administratoriumi</translation>
@@ -538,8 +532,8 @@
<translation id="4726672564094551039">Iš naujo įkelti politiką</translation>
<translation id="4728558894243024398">Platforma</translation>
<translation id="4736825316280949806">Iš naujo paleiskite „Chromium“</translation>
+<translation id="4742407542027196863">Tvarkyti slaptažodžius…</translation>
<translation id="4744603770635761495">Vykdomasis kelias</translation>
-<translation id="4749685221585524849">Paskutinį kartą naudota <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Į šią svetainę siunčiama informacija (pvz., slaptažodžiai arba kredito kortelių numeriai) yra privati.</translation>
<translation id="4756388243121344051">&amp;Istorija</translation>
<translation id="4758311279753947758">Pridėti kontaktinę informaciją</translation>
@@ -556,6 +550,7 @@
<translation id="4850886885716139402">Žiūrėti</translation>
<translation id="4854362297993841467">Šis pristatymo metodas nepasiekiamas. Išbandykite kitą metodą.</translation>
<translation id="4858792381671956233">Paprašėte tėvų leidimo apsilankyti šiame puslapyje</translation>
+<translation id="4876305945144899064">Nėra naudotojo vardo</translation>
<translation id="4880827082731008257">Ieškoti istorijoje</translation>
<translation id="4881695831933465202">Atidaryti</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -589,6 +584,7 @@
<translation id="5089810972385038852">Valstija</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="5098332213681597508">Šis vardas yra iš jūsų „Google“ paskyros.</translation>
<translation id="5115563688576182185">(64 bitų)</translation>
<translation id="5121084798328133320">Kai patvirtinsite, „Google“ mokamojoje paskyroje nurodyta išsami kortelės informacija bus bendrinama su šia svetaine.</translation>
<translation id="5128122789703661928">Sesijos šiuo pavadinimu negalima ištrinti.</translation>
@@ -629,9 +625,10 @@
<translation id="5332219387342487447">Pristatymo metodas</translation>
<translation id="5355557959165512791">Negalite dabar apsilankyti svetainėje <ph name="SITE" />, nes jos sertifikatas buvo anuliuotas. Tinklo klaidos ir užpuolimai dažniausiai yra laikini, todėl šis puslapis vėliau tikriausiai veiks.</translation>
<translation id="536296301121032821">Išsaugant politikos nustatymus įvyko klaida</translation>
+<translation id="5371425731340848620">Atnaujinkite kortelę</translation>
<translation id="5377026284221673050">„Laikrodis atsilieka“, „Laikrodis skuba“ arba „&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;“</translation>
<translation id="5386426401304769735">Šios svetainės sertifikatų grandinėje yra sertifikatas, pasirašytas naudojant SHA-1.</translation>
-<translation id="5402410679244714488">Gal. l. pab.: <ph name="EXPIRATION_DATE_ABBR" />, pask. k. naudota daugiau nei prieš metus</translation>
+<translation id="5387961145478138773">Greičiau pasiekite mėgstamiausias „Google“ programas</translation>
<translation id="540969355065856584">Šiam serveriui nepavyko patvirtinti, kad jis yra <ph name="DOMAIN" />; šiuo metu jo saugos sertifikatas negalioja. Tai gali būti dėl netinkamos konfigūracijos arba dėl ryšį pertraukusio užgrobėjo.</translation>
<translation id="5421136146218899937">Išvalyti naršymo duomenis...</translation>
<translation id="5430298929874300616">Pašalinti žymę</translation>
@@ -673,11 +670,13 @@
<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="5659593005791499971">El. paštas</translation>
+<translation id="5666899935841546222">Ar norite visas korteles matyti vienoje vietoje?</translation>
<translation id="5675650730144413517">Šis puslapis neveikia</translation>
<translation id="5685654322157854305">Pridėti pristatymo adresą</translation>
<translation id="5689199277474810259">Eksportuoti kaip JSON</translation>
<translation id="5689516760719285838">Vieta</translation>
<translation id="570530837424789914">Tvarkyti...</translation>
+<translation id="57094364128775171">Siūlyti sudėtingą slaptažodį…</translation>
<translation id="5710435578057952990">Šio tinklalapio tapatybė nenustatyta.</translation>
<translation id="5719499550583120431">Išankstinio mokėjimo kortelės tinkamos.</translation>
<translation id="5720705177508910913">Dabartinis naudotojas</translation>
@@ -698,11 +697,9 @@
<translation id="5869405914158311789">Nepavyksta pasiekti šios svetainės</translation>
<translation id="5869522115854928033">Išsaugoti slaptažodžiai</translation>
<translation id="5893752035575986141">Kredito kortelės tinkamos.</translation>
-<translation id="5898382028489516745">„Chromium“ rekomenduoja iš naujo nustatyti „<ph name="ORG_NAME" />“ slaptažodį, jei naudojate jį kitose svetainėse.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (sinchronizuota)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{Naudojamas 1 slapukas}one{Naudojamas # slapukas}few{Naudojami # slapukai}many{Naudojama # slapuko}other{Naudojama # slapukų}}</translation>
<translation id="5939518447894949180">Nustatyti iš naujo</translation>
-<translation id="5959728338436674663">Automatiškai siųsti tam tikrą <ph name="BEGIN_WHITEPAPER_LINK" />sistemos informaciją ir puslapio turinį<ph name="END_WHITEPAPER_LINK" /> į sistemą „Google“ siekiant padėti aptikti pavojingas programas ir svetaines. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Kontaktinės informacijos redagavimas</translation>
<translation id="5967867314010545767">Pašalinti iš istorijos</translation>
<translation id="5975083100439434680">Tolinti</translation>
@@ -748,13 +745,11 @@
<translation id="6282194474023008486">Pašto kodas</translation>
<translation id="6290238015253830360">Jūsų pasiūlyti straipsniai rodomi čia</translation>
<translation id="6305205051461490394"><ph name="URL" /> nepasiekiama.</translation>
-<translation id="6319915415804115995">Paskutinį kartą naudota daugiau nei prieš metus</translation>
<translation id="6321917430147971392">Patikrinkite DNS nustatymus</translation>
<translation id="6328639280570009161">Bandykite neleisti tinklo numatymo</translation>
<translation id="6328786501058569169">Ši svetainė yra apgaulinga</translation>
<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="6342069812937806050">Ką tik</translation>
<translation id="6355080345576803305">Viešosios sesijos nepaisymas</translation>
<translation id="6358450015545214790">Ką tai reiškia?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{Dar 1 pasiūlymas}one{Dar # pasiūlymas}few{Dar # pasiūlymai}many{Dar # pasiūlymo}other{Dar # pasiūlymų}}</translation>
@@ -779,7 +774,6 @@
<translation id="6529602333819889595">&amp;Ištrinti dar kartą</translation>
<translation id="6534179046333460208">Fizinio žiniatinklio pasiūlymai</translation>
<translation id="6550675742724504774">Parinktys</translation>
-<translation id="6556915248009097796">Galiojimo laiko pabaiga: <ph name="EXPIRATION_DATE_ABBR" />, paskutinį kartą naudota <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Jūsų valdytojas dar jos nepatvirtino</translation>
<translation id="6569060085658103619">Peržiūrite plėtinio puslapį</translation>
<translation id="6596325263575161958">Šifravimo parinktys</translation>
@@ -808,15 +802,20 @@
<translation id="6825578344716086703">Bandėte pasiekti <ph name="DOMAIN" />, bet serveris pateikė sertifikatą, kuris pasirašytas naudojant nesudėtingą parašo algoritmą (pvz., SHA-1). Tai reiškia, kad serverio pateikti saugos prisijungimo duomenys galėjo būti suklastoti ir serveris gali būti ne tas, kurio tikėjotės (gali būti, kad bendraujate su užpuoliku).</translation>
<translation id="6831043979455480757">Vertėjas</translation>
<translation id="6839929833149231406">Sritis</translation>
+<translation id="6852204201400771460">Įkelti programą iš naujo?</translation>
+<translation id="6865412394715372076">Šiuo metu kortelės patvirtinti nepavyksta</translation>
<translation id="6874604403660855544">&amp;Pridėti dar kartą</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Politikos lygis nepalaikomas.</translation>
<translation id="6895330447102777224">Kortelė patvirtinta</translation>
<translation id="6897140037006041989">Naudotojo atstovas</translation>
+<translation id="6903319715792422884">Padėkite tobulinti Saugų naršymą siųsdami tam tikrą <ph name="BEGIN_WHITEPAPER_LINK" />sistemos informaciją ir puslapių turinį<ph name="END_WHITEPAPER_LINK" /> „Google“. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Naudotojas:</translation>
+<translation id="6944692733090228304">Įvedėte slaptažodį svetainėje, kurios netvarko organizacija <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Kad apsaugotumėte paskyrą, nenaudokite to paties slaptažodžio kitose programose ir svetainėse.</translation>
<translation id="6945221475159498467">Pasirinkti</translation>
<translation id="6948701128805548767">Jei norite peržiūrėti paėmimo metodus ir reikalavimus, pasirinkite adresą</translation>
<translation id="6949872517221025916">Slaptažodžio nustatymas iš naujo</translation>
+<translation id="6950684638814147129">Klaida analizuojant JSON vertę: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Panašu, kad serverio sertifikatas yra suklastotas.</translation>
<translation id="6965382102122355670">Gerai</translation>
<translation id="6965978654500191972">Įrenginys</translation>
@@ -878,6 +877,7 @@
&lt;li&gt;Spustelėkite &lt;strong&gt;Taikyti&lt;/strong&gt;, tada – &lt;strong&gt;Gerai&lt;/strong&gt;.
&lt;li&gt;Apsilankykite &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;„Chrome“ pagalbos centre&lt;/a&gt;, kad sužinotumėte, kaip visam laikui pašalinti programinę įrangą iš kompiuterio.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Tvarkyti slaptažodžius…</translation>
<translation id="7419106976560586862">Profilio kelias</translation>
<translation id="7437289804838430631">Pridėti kontaktinę informaciją</translation>
<translation id="7441627299479586546">Netinkamas politikos objektas</translation>
@@ -886,7 +886,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Sužinokite daugiau<ph name="END_LINK" /> apie šią problemą.</translation>
<translation id="7455133967321480974">Naudoti visuotinį numatytąjį nustatymą (blokuoti)</translation>
<translation id="7460163899615895653">Naujausi kitų įrenginių skirtukai rodomi čia</translation>
-<translation id="7469372306589899959">Kortelė patvirtinama</translation>
<translation id="7473891865547856676">Ne, ačiū</translation>
<translation id="7481312909269577407">Persiųsti</translation>
<translation id="7485870689360869515">Nerasta jokių duomenų.</translation>
@@ -1016,6 +1015,7 @@
<translation id="8308427013383895095">Vertimas nepavyko dėl tinklo ryšio problemos.</translation>
<translation id="8311129316111205805">Įkelti sesiją</translation>
<translation id="8332188693563227489">Prieiga prie <ph name="HOST_NAME" /> atmesta</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_YEAR" />/<ph name="EXPIRATION_MONTH" /></translation>
<translation id="834457929814110454">Jei suprantate, kokia rizika gali kilti jūsų saugai, galite <ph name="BEGIN_LINK" />apsilankyti šioje svetainėje<ph name="END_LINK" />, kol iš jos dar nepašalintos kenkėjiškos programos.</translation>
<translation id="8349305172487531364">Žymių juosta</translation>
<translation id="8363502534493474904">Išjungti lėktuvo režimą</translation>
@@ -1036,21 +1036,25 @@
<translation id="8498891568109133222">Per ilgai laukta <ph name="HOST_NAME" /> atsako.</translation>
<translation id="8503559462189395349">„Chrome“ slaptažodžiai</translation>
<translation id="8503813439785031346">Vartotojo vardas</translation>
+<translation id="8508648098325802031">Paieškos piktograma</translation>
<translation id="8543181531796978784">Galite <ph name="BEGIN_ERROR_LINK" />pranešti apie aptikimo problemą<ph name="END_ERROR_LINK" /> arba, jei suprantate saugos riziką, galite <ph name="BEGIN_LINK" />apsilankyti šioje nesaugioje svetainėje<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Turite klausimų? Susisiekite su jūsų profilį prižiūrinčiu asmeniu.</translation>
<translation id="8553075262323480129">Išversti negalima, nes nepavyko nustatyti puslapio kalbos.</translation>
<translation id="8557066899867184262">Kortelės saugos kodas (CVC) nurodytas kitoje kortelės pusėje.</translation>
<translation id="8559762987265718583">Nepavyksta užmegzti privataus ryšio su <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, nes įrenginio data ir laikas (<ph name="DATE_AND_TIME" />) yra netinkami.</translation>
+<translation id="8564985650692024650">„Chromium“ rekomenduoja iš naujo nustatyti <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> slaptažodį, jei naudojate jį kitose svetainėse.</translation>
<translation id="8571890674111243710">Puslapis verčiamas į <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Pridėti tel. nr.
</translation>
<translation id="859285277496340001">Sertifikatas nenurodo mechanizmo, skirto patikrinti, ar jis buvo panaikintas.</translation>
+<translation id="860043288473659153">Kortelės savininko vardas</translation>
<translation id="8620436878122366504">Jūsų tėvai dar jos nepatvirtino</translation>
<translation id="8625384913736129811">Išsaugoti šią kortelę šiame įrenginyje</translation>
-<translation id="8639963783467694461">Automatinio pildymo nustatymai</translation>
+<translation id="8663226718884576429">Užsakymo suvestinė, <ph name="TOTAL_LABEL" />, daugiau informacijos</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, atsakymas, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Jūsų ryšys su <ph name="DOMAIN" /> nekoduotas.</translation>
<translation id="8718314106902482036">Mokėjimas neužbaigtas</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, paieškos pasiūlymas</translation>
<translation id="8725066075913043281">Bandyti dar kartą</translation>
<translation id="8728672262656704056">Veikia inkognito režimas</translation>
<translation id="8730621377337864115">Atlikta</translation>
@@ -1066,8 +1070,10 @@
<translation id="8820817407110198400">Žymės</translation>
<translation id="883848425547221593">Kitos žymės</translation>
<translation id="884264119367021077">Siuntimo adresas</translation>
+<translation id="8846319957959474018">Lengvai atidarykite programas naudodamiesi žymėmis</translation>
<translation id="884923133447025588">Nerasta atšaukimo mechanizmo.</translation>
<translation id="885730110891505394">Bendrinimas su „Google“</translation>
+<translation id="8858065207712248076">„Chrome“ rekomenduoja iš naujo nustatyti <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> slaptažodį, jei naudojate jį kitose svetainėse.</translation>
<translation id="8866481888320382733">Analizuojant politikos nustatymus įvyko klaida</translation>
<translation id="8870413625673593573">Neseniai uždaryta</translation>
<translation id="8874824191258364635">Įveskite tinkamą kortelės numerį</translation>
@@ -1106,6 +1112,7 @@
<translation id="9103872766612412690">Svetainėje <ph name="SITE" /> įprastai naudojama šifruotė informacijai apsaugoti. Šį kartą „Chromium“ bandant prisijungti prie <ph name="SITE" />, ji pateikė neįprastus ir netinkamus prisijungimo duomenis. Gali būti, kad užpuolėjas bando apsimesti svetaine <ph name="SITE" /> arba „Wi-Fi“ prisijungimo ekrane nutrūko ryšys. Jūsų informacija vis tiek liko apsaugota, nes „Chromium“ sustabdė prisijungimą prieš apsikeitimą bet kokiais duomenimis.</translation>
<translation id="9106062320799175032">Atsiskaitymo adreso pridėjimas</translation>
<translation id="910908805481542201">Padėkite man tai išspręsti</translation>
+<translation id="9114524666733003316">Kortelė patvirtinama...</translation>
<translation id="9128870381267983090">Prisijungti prie tinklo</translation>
<translation id="9137013805542155359">Rodyti originalą</translation>
<translation id="9137248913990643158">Prieš naudodami šią programą prisijunkite prie „Chrome“.</translation>
@@ -1116,6 +1123,7 @@
<translation id="9168814207360376865">Leisti svetainėms tikrinti, ar esate išsaugoję mokėjimo metodus</translation>
<translation id="9169664750068251925">Visada blokuoti šioje svetainėje</translation>
<translation id="9170848237812810038">&amp;Atšaukti</translation>
+<translation id="9171296965991013597">Išeiti iš programos?</translation>
<translation id="917450738466192189">Serverio sertifikatas negalioja.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> naudojamas nepalaikomas protokolas.</translation>
<translation id="9205078245616868884">Duomenys užšifruoti naudojant sinchronizavimo slaptafrazę. Įveskite ją, kad pradėtumėte sinchronizuoti.</translation>
diff --git a/chromium/components/strings/components_strings_lv.xtb b/chromium/components/strings/components_strings_lv.xtb
index 84060dce419..232a8e5e78e 100644
--- a/chromium/components/strings/components_strings_lv.xtb
+++ b/chromium/components/strings/components_strings_lv.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Slēpt vērtību</translation>
<translation id="1228893227497259893">Nepareizs vienības identifikators</translation>
<translation id="1232569758102978740">Bez nosaukuma</translation>
+<translation id="1250759482327835220">Lai nākamreiz veiktu maksājumu ātrāk, saglabājiet kartes datus, vārdu un norēķinu adresi savā Google kontā.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (veikta sinhronizācija)</translation>
<translation id="1256368399071562588">&lt;p&gt;Ja, mēģinot apmeklēt vietni, tā netiek atvērta, vispirms izmēģiniet tālāk norādītās problēmu novēršanas darbības.&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Lūdzu, koriģējiet datumu un laiku lietotnes &lt;strong&gt;Iestatījumi&lt;/strong&gt; sadaļā &lt;strong&gt;Vispārīgi&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Šīs tīmekļa lapas rādīšanas laikā radās kļūda.</translation>
-<translation id="1590457302292452960">Ģenerēt drošu paroli...</translation>
<translation id="1592005682883173041">Piekļuve lokālajiem datiem</translation>
<translation id="1594030484168838125">Izvēlēties</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Sazinieties ar sistēmas administratoru.</translation>
<translation id="1740951997222943430">Ievadiet derīgu mēnesi</translation>
+<translation id="1743520634839655729">Lai nākamreiz veiktu maksājumu ātrāk, saglabājiet kartes datus, vārdu un norēķinu adresi savā Google kontā un šajā ierīcē.</translation>
<translation id="17513872634828108">Atvērt cilnes</translation>
<translation id="1753706481035618306">Lapas numurs</translation>
<translation id="1763864636252898013">Šis serveris nevarēja pierādīt, ka šī ir vietne <ph name="DOMAIN" />; tās drošības sertifikāts netiek uzskatīts par uzticamu jūsu ierīces operētājsistēmā. Iespējams, tas ir nepareizas konfigurācijas dēļ vai arī kāds ir ļaunprātīgi izmantojis jūsu savienojumu.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Šeit tiks parādītas jūsu atvērtās cilnes</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Kartes īpašnieka vārds, uzvārds</translation>
-<translation id="1806541873155184440">Pievienota: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Pieprasījums vai tā parametri nebija derīgi.</translation>
<translation id="1826516787628120939">Pārbaude</translation>
<translation id="1834321415901700177">Šī vietne satur kaitnieciskas programmas</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 ieteikums}zero{# ieteikumi}one{# ieteikums}other{# ieteikumi}}</translation>
<translation id="2079545284768500474">Atsaukt</translation>
<translation id="20817612488360358">Ir iestatīta datora starpniekserveru iestatījumu lietošana, bet ir norādīta arī atklāta starpniekservera konfigurācija.</translation>
-<translation id="2084558088529668945">Jūs ievadījāt paroli vietnē, kuru nepārvalda <ph name="ORG_NAME" />. Lai aizsargātu savu kontu, neizmantojiet šo paroli citās lietotnēs un vietnēs.</translation>
<translation id="2091887806945687916">Signāls</translation>
<translation id="2094505752054353250">Domēni nesaskan</translation>
<translation id="2096368010154057602">Departaments</translation>
+<translation id="2102134110707549001">Ieteikt drošu paroli…</translation>
<translation id="2108755909498034140">Restartējiet datoru</translation>
<translation id="2113977810652731515">Karte</translation>
<translation id="2114841414352855701">Ignorēta, jo to atcēla politika <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP kļūda</translation>
<translation id="2270484714375784793">Tālruņa numurs</translation>
<translation id="2292556288342944218">Piekļuve internetam ir bloķēta</translation>
-<translation id="230155334948463882">Vai jums ir jauna karte?</translation>
<translation id="2316887270356262533">Tiks atbrīvots mazāk nekā 1 MB. Dažas vietnes nākamajā apmeklējumā var ielādēt lēnāk.</translation>
<translation id="2317259163369394535">Vietnē <ph name="DOMAIN" /> ir jāievada lietotājvārds un parole.</translation>
<translation id="2317583587496011522">Tiek pieņemtas debetkartes.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Atbalstītās kartes</translation>
<translation id="2702801445560668637">Atvērt sarakstu</translation>
<translation id="2704283930420550640">Vērtība neatbilst formātam.</translation>
-<translation id="2704951214193499422">Pārlūkā Chromium pašlaik nevar apstiprināt jūsu karti. Lūdzu, vēlāk mēģiniet vēlreiz.</translation>
<translation id="2705137772291741111">Nevar nolasīt šīs vietnes saglabāto (kešatmiņā ievietoto) kopiju.</translation>
<translation id="2709516037105925701">Automātiskā aizpilde</translation>
<translation id="2710942282213947212">Programmatūra jūsu datorā, kuras dēļ pārlūkā Chromium nevar izveidot drošu tīmekļa savienojumu</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Nosūtīšanas veids</translation>
<translation id="277499241957683684">Trūkst ierīces ieraksta.</translation>
<translation id="2781030394888168909">Eksportēt MacOS formātā</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Savienojums tika atiestatīts.</translation>
<translation id="2788784517760473862">Atbalstītās kredītkartes</translation>
<translation id="2794233252405721443">Vietne bloķēta</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Iestatījumu lapā varat atspējot jebkurus starpniekserverus, kas konfigurēti savienojuma izveidei.</translation>
<translation id="2955913368246107853">Aizvērt atrašanas joslu</translation>
<translation id="2958431318199492670">Tīkla konfigurācija neatbilst standartam ONC. Iespējams, konfigurācijas daļas netiks importētas.</translation>
-<translation id="2966678944701946121">Derīguma termiņš: <ph name="EXPIRATION_DATE_ABBR" />; pievienota: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Lai izveidotu drošu savienojumu, ir jāiestata pareizs pulksteņa laiks. Tas ir nepieciešams, jo sertifikāti, kurus vietnes izmanto, lai tiktu identificētas, ir derīgi tikai noteiktos laika periodos. Tā kā jūsu ierīces pulkstenis nav pareizs, Google Chrome nevar verificēt šos sertifikātus.</translation>
<translation id="2972581237482394796">&amp;Pāratsaukt</translation>
<translation id="2977665033722899841">Pašlaik ir atlasīta rinda “<ph name="ROW_NAME" />”. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Politikas tips nav pareizs.</translation>
<translation id="3037605927509011580">Cilnes avārija.</translation>
<translation id="3041612393474885105">Sertifikāta informācija</translation>
-<translation id="3063697135517575841">Pārlūkā Chrome pašlaik nevar apstiprināt jūsu karti. Lūdzu, vēlāk mēģiniet vēlreiz.</translation>
<translation id="3064966200440839136">Ja maksāšanai tiks izmantota ārēja lietojumprogramma, tiks aizvērts inkognito režīms. Vai turpināt?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Nav}=1{1 parole}zero{# paroles}one{# parole}other{# paroles}}</translation>
<translation id="3096100844101284527">Pievienot saņemšanas adresi</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Jūsu dati tika šifrēti, izmantojot jūsu sinhronizācijas ieejas frāzi šādā datumā: <ph name="TIME" />. Lai sāktu sinhronizāciju, ievadiet ieejas frāzi.</translation>
<translation id="3320021301628644560">Norēķinu adreses pievienošana</translation>
<translation id="3338095232262050444">Droši</translation>
-<translation id="3340978935015468852">Iestatījumi</translation>
<translation id="3345135638360864351">Lietotājam <ph name="NAME" /> nevarēja nosūtīt jūsu pieprasījumu piekļūt šai vietnei. Lūdzu, mēģiniet vēlreiz.</translation>
<translation id="3355823806454867987">Mainīt starpniekservera iestatījumus...</translation>
<translation id="3361596688432910856">Pārlūkā Chrome <ph name="BEGIN_EMPHASIS" />netiks saglabāta<ph name="END_EMPHASIS" /> šāda informācija:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Servera sertifikāts nav uzticams.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Vismaz 1 vienums sinhronizētās ierīcēs}=1{1 vienums (un vēl citi sinhronizētās ierīcēs)}zero{# vienumi (un vēl citi sinhronizētās ierīcēs)}one{# vienums (un vēl citi sinhronizētās ierīcēs)}other{# vienumi (un vēl citi sinhronizētās ierīcēs)}}</translation>
<translation id="3539171420378717834">Saglabāt šīs kartes kopiju šajā ierīcē</translation>
-<translation id="3542684924769048008">Izmantot paroli:</translation>
<translation id="3549644494707163724">Šifrēt visus sinhronizētos datus, izmantojot sinhronizācijas ieejas frāzi</translation>
<translation id="3556433843310711081">Lai atbloķētu, vērsieties pie pārvaldnieka</translation>
<translation id="3566021033012934673">Jūsu savienojums nav privāts</translation>
@@ -460,7 +455,6 @@
<translation id="4171400957073367226">Verifikācijas paraksts nav derīgs.</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{Vēl <ph name="ITEM_COUNT" /> vienums}zero{Vēl <ph name="ITEM_COUNT" /> vienumi}one{Vēl <ph name="ITEM_COUNT" /> vienums}other{Vēl <ph name="ITEM_COUNT" /> vienumi}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" />: <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome iesaka atiestatīt jūsu <ph name="ORG_NAME" /> paroli, ja tā tiek izmantota citās vietnēs.</translation>
<translation id="4196861286325780578">&amp;Atcelt pārvietošanas atsaukšanu</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Pārbaudiet ugunsmūri un pretvīrusu programmu konfigurācijas<ph name="END_LINK" />.</translation>
<translation id="4220128509585149162">Avārijas</translation>
@@ -489,7 +483,6 @@
<translation id="425582637250725228">Veiktās izmaiņas, iespējams, netiks saglabātas.</translation>
<translation id="4258748452823770588">Paraksts nav derīgs.</translation>
<translation id="4265872034478892965">Atļāva jūsu administrators</translation>
-<translation id="4269787794583293679">(Nav lietotājvārda)</translation>
<translation id="4275830172053184480">Ierīces restartēšana</translation>
<translation id="4277028893293644418">Atiestatīt paroli</translation>
<translation id="4280429058323657511">, derīga līdz <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -512,6 +505,7 @@
<translation id="4434045419905280838">Uznirstošie elem. un novirzīšana</translation>
<translation id="443673843213245140">Starpniekservera lietošana ir atspējota, bet ir norādīta atklāta starpniekservera konfigurācija.</translation>
<translation id="445100540951337728">Atbalstītās debetkartes</translation>
+<translation id="4472575034687746823">Sākt darbu</translation>
<translation id="4506176782989081258">Validācijas kļūda: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Sazinieties ar sistēmas administratoru.</translation>
<translation id="450710068430902550">Kopīgošana ar administratoru</translation>
@@ -536,8 +530,8 @@
<translation id="4726672564094551039">Atkārtoti ielādēt politikas</translation>
<translation id="4728558894243024398">Platforma</translation>
<translation id="4736825316280949806">Restartējiet pārlūku Chromium</translation>
+<translation id="4742407542027196863">Pārvaldīt paroles…</translation>
<translation id="4744603770635761495">Izpildāms ceļš</translation>
-<translation id="4749685221585524849">Pēdējoreiz izmantota: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Jūsu informācija (piemēram, paroles vai kredītkaršu numuri) ir privāta, kad tā tiek nosūtīta uz šo vietni.</translation>
<translation id="4756388243121344051">Vēsture</translation>
<translation id="4758311279753947758">Pievienot kontaktinformāciju</translation>
@@ -554,6 +548,7 @@
<translation id="4850886885716139402">Skatīt</translation>
<translation id="4854362297993841467">Šis piegādes veids nav pieejams. Izmēģiniet citu veidu.</translation>
<translation id="4858792381671956233">Jūs lūdzāt vecākiem atļauju apmeklēt šo lapu</translation>
+<translation id="4876305945144899064">Nav lietotājvārda</translation>
<translation id="4880827082731008257">Meklēšanas vēsture</translation>
<translation id="4881695831933465202">Atvērt</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -587,6 +582,7 @@
<translation id="5089810972385038852">Štats</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="5098332213681597508">Šis vārds ir iegūts no jūsu Google konta.</translation>
<translation id="5115563688576182185">(64 bitu)</translation>
<translation id="5121084798328133320">Pēc apstiprināšanas kartes informācija no Google maksājumu konta tiks kopīgota ar šo vietni.</translation>
<translation id="5128122789703661928">Sesija ar šādu nosaukumu nav dzēšama.</translation>
@@ -627,9 +623,10 @@
<translation id="5332219387342487447">Piegādes veids</translation>
<translation id="5355557959165512791">Pašlaik nevarat apmeklēt vietni <ph name="SITE" />, jo tās sertifikāts ir atsaukts. Tā kā tīkla kļūdas un uzbrukumi parasti ir īslaicīgi, visticamāk, šī lapa vēlāk darbosies.</translation>
<translation id="536296301121032821">Neizdevās saglabāt politikas iestatījumus.</translation>
+<translation id="5371425731340848620">Kartes atjaunināšana</translation>
<translation id="5377026284221673050">“Pulkstenis atpaliek”, “Pulkstenis steidzas” vai “&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;”</translation>
<translation id="5386426401304769735">Šīs vietnes sertifikātu ķēdē ir iekļauts sertifikāts, kas ir parakstīts, izmantojot SHA-1.</translation>
-<translation id="5402410679244714488">Derīguma termiņš: <ph name="EXPIRATION_DATE_ABBR" />, pēdējoreiz izmantota pirms vairāk nekā gada</translation>
+<translation id="5387961145478138773">Ātrāk piekļūstiet iecienītajām Google lietotnēm</translation>
<translation id="540969355065856584">Šis serveris nevarēja pierādīt, ka šī ir vietne <ph name="DOMAIN" />; tā drošības sertifikāts šobrīd nav derīgs. Iespējams, tas ir nepareizas konfigurācijas dēļ vai arī kāds ļaunprātīgi izmanto jūsu savienojumu.</translation>
<translation id="5421136146218899937">Notīrīt pārlūkošanas datus</translation>
<translation id="5430298929874300616">Noņemt grāmatzīmi</translation>
@@ -671,11 +668,13 @@
<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="5659593005791499971">E-pasts</translation>
+<translation id="5666899935841546222">Vai vēlaties piekļūt visām kartēm vienuviet?</translation>
<translation id="5675650730144413517">Šī lapa nedarbojas</translation>
<translation id="5685654322157854305">Pievienot nosūtīšanas adresi</translation>
<translation id="5689199277474810259">Eksportēt JSON formātā</translation>
<translation id="5689516760719285838">Atrašanās vieta</translation>
<translation id="570530837424789914">Pārvaldīt...</translation>
+<translation id="57094364128775171">Ieteikt drošu paroli…</translation>
<translation id="5710435578057952990">Tīmekļa vietnes identitāte nav apstiprināta.</translation>
<translation id="5719499550583120431">Tiek pieņemtas priekšapmaksas kartes.</translation>
<translation id="5720705177508910913">Pašreizējais lietotājs</translation>
@@ -696,11 +695,9 @@
<translation id="5869405914158311789">Šī vietne nav sasniedzama</translation>
<translation id="5869522115854928033">Saglabātās paroles</translation>
<translation id="5893752035575986141">Tiek pieņemtas kredītkartes.</translation>
-<translation id="5898382028489516745">Chromium iesaka atiestatīt jūsu <ph name="ORG_NAME" /> paroli, ja izmantojāt to citās vietnēs.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (veikta sinhronizācija)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 tiek lietots}zero{# tiek lietoti}one{# tiek lietots}other{# tiek lietoti}}</translation>
<translation id="5939518447894949180">Atiestatīt</translation>
-<translation id="5959728338436674663">Automātiski sūtīt Google serveriem noteiktu <ph name="BEGIN_WHITEPAPER_LINK" />sistēmas informāciju un lapu saturu<ph name="END_WHITEPAPER_LINK" />, lai palīdzētu noteikt bīstamas lietotnes un vietnes. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Kontaktinformācijas rediģēšana</translation>
<translation id="5967867314010545767">Noņemt no vēstures</translation>
<translation id="5975083100439434680">Tālināt</translation>
@@ -746,13 +743,11 @@
<translation id="6282194474023008486">Pasta indekss</translation>
<translation id="6290238015253830360">Ieteiktie raksti tiek parādīti šeit</translation>
<translation id="6305205051461490394">Vietne <ph name="URL" /> nav sasniedzama.</translation>
-<translation id="6319915415804115995">Pēdējoreiz izmantota pirms vairāk nekā gada</translation>
<translation id="6321917430147971392">Sistēmas DNS iestatījumu pārbaude</translation>
<translation id="6328639280570009161">Tīkla prognožu atspējošana</translation>
<translation id="6328786501058569169">Šī vietne ir krāpnieciska</translation>
<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="6342069812937806050">Tikko</translation>
<translation id="6355080345576803305">Publiskas sesijas ignorēšana</translation>
<translation id="6358450015545214790">Ko tas nozīmē?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{vēl 1 ieteikums}zero{vēl # ieteikumi}one{vēl # ieteikums}other{vēl # ieteikumi}}</translation>
@@ -777,7 +772,6 @@
<translation id="6529602333819889595">&amp;Dzēšanas atsaukuma atcelšana</translation>
<translation id="6534179046333460208">Fiziskā tīmekļa ieteikumi</translation>
<translation id="6550675742724504774">Opcijas</translation>
-<translation id="6556915248009097796">Derīguma termiņš: <ph name="EXPIRATION_DATE_ABBR" />; pēdējoreiz izmantota: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Jūsu vadītājs vēl nav to apstiprinājis</translation>
<translation id="6569060085658103619">Jūs skatāt paplašinājumu lapu.</translation>
<translation id="6596325263575161958">Šifrēšanas opcijas</translation>
@@ -806,15 +800,20 @@
<translation id="6825578344716086703">Jūs mēģinājāt sasniegt domēnu <ph name="DOMAIN" />, bet serveris uzrādīja sertifikātu, kas ir parakstīts, izmantojot vāju paraksta algoritmu (piemēram, SHA-1). Tas nozīmē, ka servera norādītie drošības akreditācijas dati var būt viltoti un šis serveris var nebūt tas serveris, kuru mēģināt sasniegt (iespējams, jūs sazināties ar uzbrucēju).</translation>
<translation id="6831043979455480757">Tulkot</translation>
<translation id="6839929833149231406">Apgabals</translation>
+<translation id="6852204201400771460">Vai atkārtoti ielādēt lietotni?</translation>
+<translation id="6865412394715372076">Pašlaik nevar verificēt šo karti.</translation>
<translation id="6874604403660855544">&amp;Atcelt pievienošanas atsaukšanu</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" />: <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Politikas līmenis netiek atbalstīts.</translation>
<translation id="6895330447102777224">Karte ir apstiprināta</translation>
<translation id="6897140037006041989">Lietotāja aģents</translation>
+<translation id="6903319715792422884">Palīdziet uzlabot Drošo pārlūkošanu, nosūtot noteiktu <ph name="BEGIN_WHITEPAPER_LINK" />sistēmas informāciju un lapas saturu<ph name="END_WHITEPAPER_LINK" /> Google serveriem. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Lietotājs:</translation>
+<translation id="6944692733090228304">Jūs ievadījāt paroli vietnē, kuru nepārvalda <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Lai aizsargātu savu kontu, neizmantojiet šo paroli citās lietotnēs un vietnēs.</translation>
<translation id="6945221475159498467">Atlasīt</translation>
<translation id="6948701128805548767">Lai skatītu saņemšanas veidus un prasības, atlasiet adresi.</translation>
<translation id="6949872517221025916">Paroles atiestatīšana</translation>
+<translation id="6950684638814147129">JSON vērtības parsēšanas kļūda: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Šķiet, ka servera sertifikāts ir viltojums.</translation>
<translation id="6965382102122355670">Labi</translation>
<translation id="6965978654500191972">Ierīce</translation>
@@ -876,6 +875,7 @@
&lt;li&gt;Noklikšķiniet uz &lt;strong&gt;Lietot&lt;/strong&gt;, pēc tam noklikšķiniet uz &lt;strong&gt;Labi&lt;/strong&gt;.
&lt;li&gt;Apmeklējiet &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome palīdzības centru&lt;/a&gt;, lai uzzinātu, kā neatgriezeniski noņemt programmatūru no datora.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Pārvaldīt paroles…</translation>
<translation id="7419106976560586862">Profila ceļš</translation>
<translation id="7437289804838430631">Pievienot kontaktinformāciju</translation>
<translation id="7441627299479586546">Politikas subjekts nav pareizs.</translation>
@@ -884,7 +884,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Uzzināt vairāk<ph name="END_LINK" /> par šo problēmu.</translation>
<translation id="7455133967321480974">Izmantot globālo noklusējumu (Bloķēt)</translation>
<translation id="7460163899615895653">Jūsu nesen izmantotās cilnes no citām ierīcēm tiek rādītas šeit</translation>
-<translation id="7469372306589899959">Notiek kartes apstiprināšana</translation>
<translation id="7473891865547856676">Nē, paldies!</translation>
<translation id="7481312909269577407">Pārsūtīt</translation>
<translation id="7485870689360869515">Dati netika atrasti.</translation>
@@ -1014,6 +1013,7 @@
<translation id="8308427013383895095">Tulkošana neizdevās, jo radās problēma ar tīkla savienojumu.</translation>
<translation id="8311129316111205805">Ielādēt sesiju</translation>
<translation id="8332188693563227489">Piekļuve vietnei <ph name="HOST_NAME" /> tika noraidīta</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Ja apzināties drošības risku, varat arī <ph name="BEGIN_LINK" />apmeklēt šo vietni<ph name="END_LINK" />, pirms ir noņemtas kaitīgās programmas.</translation>
<translation id="8349305172487531364">Grāmatzīmju josla</translation>
<translation id="8363502534493474904">Izslēdziet lidojuma režīmu.</translation>
@@ -1034,21 +1034,25 @@
<translation id="8498891568109133222">Vietne <ph name="HOST_NAME" /> pārāk ilgi nereaģēja.</translation>
<translation id="8503559462189395349">Chrome paroles</translation>
<translation id="8503813439785031346">Lietotājvārds</translation>
+<translation id="8508648098325802031">Meklēšanas ikona</translation>
<translation id="8543181531796978784">Jūs varat <ph name="BEGIN_ERROR_LINK" />ziņot par noteikšanas problēmu<ph name="END_ERROR_LINK" /> vai, ja apzināties drošības riskus, <ph name="BEGIN_LINK" />apmeklēt šo nedrošo vietni<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Vai radušies jautājumi? Sazinieties ar personu, kas uzrauga jūsu profilu.</translation>
<translation id="8553075262323480129">Tulkošana neizdevās, jo lapas valoda nav nosakāma.</translation>
<translation id="8557066899867184262">CVC kods ir norādīts jūsu kartes aizmugurē.</translation>
<translation id="8559762987265718583">Nevar izveidot privātu savienojumu ar <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, jo jūsu ierīces datums un laiks (<ph name="DATE_AND_TIME" />) nav pareizs.</translation>
+<translation id="8564985650692024650">Chromium iesaka atiestatīt jūsu <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> paroli, ja izmantojāt to citās vietnēs.</translation>
<translation id="8571890674111243710">Notiek lapas tulkošana uz <ph name="LANGUAGE" /> valodu...</translation>
<translation id="858637041960032120">Piev. tālr. nr.
</translation>
<translation id="859285277496340001">Sertifikāts nenorāda mehānismu, ar kuru pārbaudīt, vai tas nav atsaukts.</translation>
+<translation id="860043288473659153">Bankas kartes īpašnieka vārds</translation>
<translation id="8620436878122366504">Jūsu vecāki vēl nav to apstiprinājuši</translation>
<translation id="8625384913736129811">Saglabāt šo karti šajā ierīcē</translation>
-<translation id="8639963783467694461">Automātiskās aizpildes iestatījumi</translation>
+<translation id="8663226718884576429">Pasūtījuma kopsavilkums, <ph name="TOTAL_LABEL" />, citi dati</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, atbilde, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Jūsu savienojums ar <ph name="DOMAIN" /> nav kodēts.</translation>
<translation id="8718314106902482036">Maksājums nav pabeigts</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, meklēšanas ieteikums</translation>
<translation id="8725066075913043281">Mēģināt vēlreiz</translation>
<translation id="8728672262656704056">Jūs esat atvēris inkognito režīmu</translation>
<translation id="8730621377337864115">Gatavs</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Grāmatzīmes</translation>
<translation id="883848425547221593">Citas grāmatzīmes</translation>
<translation id="884264119367021077">Piegādes adrese</translation>
+<translation id="8846319957959474018">Atveriet lietotnes vienkārši, izmantojot grāmatzīmes</translation>
<translation id="884923133447025588">Nav atrasts atsaukšanas mehānisms.</translation>
<translation id="885730110891505394">Kopīgošana ar Google</translation>
+<translation id="8858065207712248076">Chrome iesaka atiestatīt jūsu <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> paroli, ja izmantojāt to citās vietnēs.</translation>
<translation id="8866481888320382733">Parsējot politikas iestatījumus, radās kļūda.</translation>
<translation id="8870413625673593573">Nesen aizvērtas</translation>
<translation id="8874824191258364635">Ievadiet derīgu kartes numuru</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690">Vietnē <ph name="SITE" /> informācijas aizsargāšanai parasti tiek izmantota šifrēšana. Kad pārlūkā Chromium tika mēģināts izveidot savienojumu ar vietni <ph name="SITE" />, šoreiz tā nosūtīja neparastus un nepareizus akreditācijas datus. Iespējams, tas notika, jo uzbrucējs mēģināja uzdoties par vietni <ph name="SITE" />, vai arī Wi-Fi pierakstīšanās ekrāns pārtrauca savienojumu. Jūsu informācija joprojām ir drošībā, jo pārlūks Chromium pārtrauca savienojumu, pirms tika veikta jebkādu datu apmaiņa.</translation>
<translation id="9106062320799175032">Norēķinu adreses pievienošana</translation>
<translation id="910908805481542201">Palīdzēt man novērst problēmu</translation>
+<translation id="9114524666733003316">Notiek kartes apstiprināšana...</translation>
<translation id="9128870381267983090">Izveidot savienojumu ar tīklu</translation>
<translation id="9137013805542155359">Rādīt oriģinālo</translation>
<translation id="9137248913990643158">Pirms šīs lietotnes izmantošanas, lūdzu, palaidiet pārlūku Chrome un pierakstieties tajā.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">Atļaut vietnēm pārbaudīt, vai jums ir saglabāti maksājumu veidi</translation>
<translation id="9169664750068251925">Vienmēr bloķēt šajā vietnē</translation>
<translation id="9170848237812810038">&amp;Atsaukt</translation>
+<translation id="9171296965991013597">Vai aizvērt lietotni?</translation>
<translation id="917450738466192189">Servera sertifikāts ir nederīgs.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> izmanto neatbalstītu protokolu.</translation>
<translation id="9205078245616868884">Jūsu dati ir šifrēti, izmantojot jūsu sinhronizācijas ieejas frāzi. Lai sāktu sinhronizāciju, ievadiet ieejas frāzi.</translation>
diff --git a/chromium/components/strings/components_strings_ml.xtb b/chromium/components/strings/components_strings_ml.xtb
index 7d00c2c2a71..1830fee4df3 100644
--- a/chromium/components/strings/components_strings_ml.xtb
+++ b/chromium/components/strings/components_strings_ml.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">മൂല്യം മറയ്‌ക്കുക</translation>
<translation id="1228893227497259893">തെറ്റായ എന്റിറ്റി ഐഡന്റിഫയർ</translation>
<translation id="1232569758102978740">ശീര്‍ഷകമില്ലാത്ത</translation>
+<translation id="1250759482327835220">അടുത്ത പ്രാവശ്യം കൂടുതൽ വേഗത്തിൽ പണമടയ്ക്കാൻ, നിങ്ങളുടെ Google അക്കൗണ്ടിൽ കാർഡ്, പേര്, ബില്ലിംഗ് വിലാസം എന്നിവ സംരക്ഷിക്കുക.</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;
@@ -97,7 +98,6 @@
&lt;p&gt;&lt;strong&gt;ക്രമീകരണ&lt;/strong&gt; ആപ്പിന്റെ &lt;strong&gt;പൊതുവായവ&lt;/strong&gt; വിഭാഗത്തിൽ നിന്ന് തീയതിയും സമയവും ക്രമീകരിക്കുക.&lt;/p&gt;</translation>
<translation id="1583429793053364125">ഈ വെബ്‌പേജ് പ്രദർശിപ്പിക്കുമ്പോൾ എന്തോ കുഴപ്പം സംഭവിച്ചു.</translation>
-<translation id="1590457302292452960">ശക്തമായൊരു പാസ്‌വേഡ് സൃഷ്ടിക്കുക...</translation>
<translation id="1592005682883173041">പ്രാദേശിക ഡാറ്റ ആക്‌സസ്സ്</translation>
<translation id="1594030484168838125">തിരഞ്ഞെടുക്കുക</translation>
<translation id="1620510694547887537">ക്യാമറ</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">സിസ്റ്റം അഡ്‌മിനെ ബന്ധപ്പെടാൻ ശ്രമിക്കുക.</translation>
<translation id="1740951997222943430">കാലഹരണപ്പെടുന്ന ശരിയായ മാസം നല്‍കുക</translation>
+<translation id="1743520634839655729">അടുത്ത പ്രാവശ്യം കൂടുതൽ വേഗത്തിൽ പണമടയ്ക്കാൻ, നിങ്ങളുടെ Google അക്കൗണ്ടിലും ഈ ഉപകരണത്തിലും ഈ കാർഡ്, പേര്, ബില്ലിംഗ് വിലാസം എന്നിവ സംരക്ഷിക്കുക.</translation>
<translation id="17513872634828108">ഓപ്പൺ ടാബുകൾ</translation>
<translation id="1753706481035618306">പേജ് നമ്പർ</translation>
<translation id="1763864636252898013">ഈ സെർവറിന് അത് <ph name="DOMAIN" /> ആണെന്ന് തെളിയിക്കാനായില്ല; അതിന്റെ സുരക്ഷ സർട്ടിഫിക്കറ്റിനെ നിങ്ങളുടെ ഉപകരണത്തിന്റെ ഓപ്പറേറ്റിംഗ് സിസ്‌റ്റത്തിന് പരിചയമില്ല. തെറ്റായ കോൺഫിഗറേഷൻ കാരണമോ ഒരു അക്രമണകാരി നിങ്ങളുടെ കണക്ഷനെ തടസ്സപ്പെടുത്തുന്നത് കൊണ്ടോ ആയിരിക്കാം ഇത് സംഭവിച്ചത്.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">നിങ്ങൾ നിലവിൽ തുറന്നിട്ടുള്ള ടാബുകൾ ഇവിടെ ദൃശ്യമാകും</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">കാർഡ് ഉടമയുടെ പേര്</translation>
-<translation id="1806541873155184440"><ph name="ADDED_TO_AUTOFILL_MONTH" />-ന് ചേർത്തു</translation>
<translation id="1821930232296380041">അഭ്യർത്ഥന അല്ലെങ്കിൽ അഭ്യർത്ഥന പാരാമീറ്ററുകൾ അസാധുവാണ്</translation>
<translation id="1826516787628120939">പരിശോധിക്കുന്നു</translation>
<translation id="1834321415901700177">ഈ സൈറ്റിൽ ദോഷകരമായ പ്രോഗ്രാമുകൾ അടങ്ങിയിരിക്കുന്നു</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{ഒരു നിർദ്ദേശം}other{# നിർദ്ദേശങ്ങൾ}}</translation>
<translation id="2079545284768500474">പഴയപടിയാക്കുക</translation>
<translation id="20817612488360358">സിസ്റ്റം പ്രോക്‌സി ക്രമീകരണം ഉപയോഗിക്കുന്നതിനായി സജ്ജമാക്കി, പക്ഷെ ഒരു സ്‌പഷ്‌ടമായ പ്രോക്‌സി കോൺഫിഗറേഷനും അതോടൊപ്പം നിർദ്ദേശിച്ചിരിക്കുന്നു.</translation>
-<translation id="2084558088529668945"><ph name="ORG_NAME" /> നിയന്ത്രിക്കാത്ത ഒരു സൈറ്റിലാണ് നിങ്ങൾ പാസ്‌വേഡ് നൽകിയിരിക്കുന്നത്. അക്കൗണ്ട് പരിരക്ഷിക്കാൻ, മറ്റ് ആപ്പുകളിലും സൈറ്റുകളിലും നിങ്ങളുടെ പാസ്‌വേഡ് വീണ്ടും ഉപയോഗിക്കരുത്.</translation>
<translation id="2091887806945687916">ശബ്‌ദം</translation>
<translation id="2094505752054353250">ഡൊമെയ്‌ൻ പൊരുത്തമില്ലായ്‌മ</translation>
<translation id="2096368010154057602">വകുപ്പ്</translation>
+<translation id="2102134110707549001">ശക്തമായ പാസ്‌വേഡ് നിർദ്ദേശിക്കുക…</translation>
<translation id="2108755909498034140">നിങ്ങളുടെ കമ്പ്യൂട്ടർ റീസ്‌റ്റാർട്ടുചെയ്യുക</translation>
<translation id="2113977810652731515">കാർഡ്</translation>
<translation id="2114841414352855701"> <ph name="POLICY_NAME" /> എന്നതിനാൽ മറികടന്നതിനാൽ ഇത് അവഗണിച്ചു.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP പിശക്</translation>
<translation id="2270484714375784793">ഫോൺ നമ്പർ</translation>
<translation id="2292556288342944218">നിങ്ങളുടെ ഇന്റർനെറ്റ് ആക്‌സസ്സ് ബ്ലോക്കുചെയ്‌തു</translation>
-<translation id="230155334948463882">പുതിയ കാർഡാണോ?</translation>
<translation id="2316887270356262533">1 MB-യിൽ കുറഞ്ഞ ഡാറ്റ ലാഭിക്കുന്നു. അടുത്തതവണ നിങ്ങൾ സന്ദർശിക്കുമ്പോൾ ചില സൈറ്റുകൾ ഇതിനേക്കാൾ പതുക്കെ ലോഡാകാം.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> ഡൊമെയ്‌ന് ഒരു ഉപയോക്തൃനാമവും പാസ്‌വേഡും വേണം.</translation>
<translation id="2317583587496011522">ഡെബിറ്റ് കാർഡുകൾ സ്വീകരിക്കുന്നു.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">സ്വീകരിക്കുന്ന കാർഡുകൾ</translation>
<translation id="2702801445560668637">വായനാ ലിസ്റ്റ്</translation>
<translation id="2704283930420550640">മൂല്യം ഫോർമാറ്റുമായി പൊരുത്തപ്പെടുന്നില്ല.</translation>
-<translation id="2704951214193499422">Chromium-ത്തിന് ഇപ്പോൾ നിങ്ങളുടെ കാർഡ് സ്ഥിരീകരിക്കാനായില്ല. പിന്നീട് വീണ്ടും ശ്രമിക്കുക.</translation>
<translation id="2705137772291741111">ഈ സൈറ്റിന്റെ സംരക്ഷിച്ച (കാഷെ ചെയ്‌ത) പതിപ്പ് വായിക്കാനാകാത്തതാണ്.</translation>
<translation id="2709516037105925701">ഓട്ടോഫില്‍</translation>
<translation id="2710942282213947212">വെബിലേക്ക് സുരക്ഷിതമായി കണക്റ്റുചെയ്യുന്നതിൽ നിന്ന് Chromium-ത്തിനെ നിങ്ങളുടെ കമ്പ്യൂട്ടറിലെ സോഫ്റ്റ്‌വെയർ തടയുന്നു</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">ഷിപ്പിംഗ് രീതി</translation>
<translation id="277499241957683684">ഉപകരണ റെക്കോർഡ് കാണുന്നില്ല</translation>
<translation id="2781030394888168909">MacOS എക്‌സ്‌പോർട്ട് ചെയ്യുക</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">കണക്ഷന്‍ പുനഃസജ്ജമാക്കിയതാണ്.</translation>
<translation id="2788784517760473862">ക്രെഡിറ്റ് കാർഡുകൾ സ്വീകരിക്കുന്നു</translation>
<translation id="2794233252405721443">സൈറ്റ് ബ്ലോക്കുചെയ്‌തു</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">കണക്ഷനായി കോൺഫിഗർ ചെയ്‌ത ഏതൊരു പ്രോക്‌സികളും ക്രമീകരണങ്ങൾ പേജിൽ നിന്ന് നിങ്ങൾക്ക് പ്രവർത്തനരഹിതമാക്കാനാകും.</translation>
<translation id="2955913368246107853">ഫൈന്‍ഡ് ബാര്‍ അടയ്ക്കുക</translation>
<translation id="2958431318199492670">നെറ്റ്‌വർക്ക് കോൺഫിഗറേഷൻ ONC സ്റ്റാൻഡേർഡിന് അനുസൃതമായി പ്രവർത്തിക്കുന്നില്ല. കോൺഫിഗറേഷൻ ഭാഗങ്ങൾ ഇമ്പോർട്ടുചെയ്‌തേക്കില്ല.</translation>
-<translation id="2966678944701946121">കാലഹരണപ്പെടുന്ന തീയതി: <ph name="EXPIRATION_DATE_ABBR" />, <ph name="ADDED_TO_AUTOFILL_MONTH" />-ന് ചേർത്തു</translation>
<translation id="2969319727213777354">ഒരു സുരക്ഷിത കണക്ഷൻ സ്ഥാപിക്കുന്നതിന്, നിങ്ങളുടെ ക്ലോക്ക് ശരിയായി സജ്ജീകരിക്കേണ്ടതുണ്ട്. വെബ്‌സൈറ്റുകൾ സ്വയം തിരിച്ചറിയുന്നതിന് ഉപയോഗിക്കുന്ന സർട്ടിഫിക്കറ്റുകൾ, നിർദ്ദിഷ്‌ട സമയ പരിധിയിൽ മാത്രം സാധുതയുള്ളതിനാലാണിത്. നിങ്ങളുടെ ഉപകരണത്തിന്റെ ക്ലോക്ക് തെറ്റായിരിക്കുന്നതിനാൽ, Google Chrome-ന് ഈ സർട്ടിഫിക്കറ്റുകൾ പരിശോധിച്ചുറപ്പിക്കാനാവില്ല.</translation>
<translation id="2972581237482394796">&amp;വീണ്ടും ചെയ്യുക</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, നിലവിൽ തിരഞ്ഞെടുത്തു. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">തെറ്റായ നയ തരം</translation>
<translation id="3037605927509011580">കഷ്ടം!</translation>
<translation id="3041612393474885105">സര്‍‌ട്ടിഫിക്കറ്റ് വിവരങ്ങള്‍‌</translation>
-<translation id="3063697135517575841">Chrome-ന് ഇപ്പോൾ നിങ്ങളുടെ കാർഡ് സ്ഥിരീകരിക്കാനായില്ല. പിന്നീട് വീണ്ടും ശ്രമിക്കുക.</translation>
<translation id="3064966200440839136">ഒരു എക്‌സ്‌റ്റേണൽ അപ്ലിക്കേഷൻ വഴി പണമടയ്‌ക്കാൻ അദൃശ്യതാ സംവിധാനം ഒഴിവാക്കുന്നു. തുടരണോ?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{ഒന്നുമില്ല}=1{ഒരു പാസ്‌വേഡ്}other{# പാസ്‌വേഡുകൾ}}</translation>
<translation id="3096100844101284527">പിക്കപ്പ് വിലാസം ചേർക്കുക</translation>
@@ -340,7 +337,6 @@
<translation id="3305707030755673451"><ph name="TIME" />-ന് നിങ്ങളുടെ സമന്വയ പാസ്‌ഫ്രെയ്‌സ് ഉപയോഗിച്ച് ഡാറ്റ എൻക്രിപ്‌റ്റുചെയ്‌തു. സമന്വയം ആരംഭിക്കുന്നതിന് ഇത് നൽകുക.</translation>
<translation id="3320021301628644560">ബില്ലിംഗ് വിലാസം ചേർക്കുക</translation>
<translation id="3338095232262050444">സുരക്ഷിതം</translation>
-<translation id="3340978935015468852">ക്രമീകരണങ്ങൾ</translation>
<translation id="3345135638360864351">ഈ സൈറ്റ് ആക്‌സസ്സ് ചെയ്യാനുള്ള നിങ്ങളുടെ അഭ്യർത്ഥന, <ph name="NAME" /> എന്നയാൾക്ക് അയയ്‌ക്കാനായില്ല. വീണ്ടും ശ്രമിക്കുക.</translation>
<translation id="3355823806454867987">പ്രോക്സി ക്രമീകരണങ്ങള്‍ മാറ്റുക...</translation>
<translation id="3361596688432910856">Chrome ഇനിപ്പറയുന്ന വിവരങ്ങൾ <ph name="BEGIN_EMPHASIS" />സംരക്ഷിക്കില്ല<ph name="END_EMPHASIS" />:
@@ -374,7 +370,6 @@
<translation id="3528171143076753409">സെര്‍വറിന്‍റെ സര്‍ട്ടിഫിക്കറ്റ് വിശ്വാസയോഗ്യമല്ല.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{സമന്വയിപ്പിച്ച ഉപകരണങ്ങളിൽ ഒരു ഇനമെങ്കിലും}=1{ഒരു ഇനം (ഒപ്പം സമന്വയിപ്പിച്ച ഉപകരണങ്ങളിൽ അതിൽ കൂടുതലും)}other{# ഇനങ്ങൾ (ഒപ്പം സമന്വയിപ്പിച്ച ഉപകരണങ്ങളിൽ അതിൽ കൂടുതലും)}}</translation>
<translation id="3539171420378717834">ഈ ഉപകരണത്തിൽ ഈ കാർഡിന്റെ ഒരു പകർപ്പ് സൂക്ഷിക്കുക</translation>
-<translation id="3542684924769048008">ഇതിനായി പാസ്‌വേഡ് ഉപയോഗിക്കുക:</translation>
<translation id="3549644494707163724">നിങ്ങളുടെ സ്വന്തം പാസ്‌ഫ്രെയ്‌സ് ഉപയോഗിച്ച് എല്ലാ സമന്വിത ഡാറ്റയും എൻക്രിപ്റ്റുചെയ്യുക</translation>
<translation id="3556433843310711081">നിങ്ങൾക്ക് വേണ്ടി ഇത് അൺബ്ലോക്കുചെയ്യാൻ മാനേജർക്ക് കഴിയും</translation>
<translation id="3566021033012934673">നിങ്ങളുടെ കണക്ഷൻ സ്വകാര്യമല്ല</translation>
@@ -460,7 +455,6 @@
<translation id="4171400957073367226">മോശം പരിശോധിച്ചുറപ്പിക്കൽ സിഗ്‌നേച്ചർ</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> ഇനം കൂടി}other{<ph name="ITEM_COUNT" /> ഇനങ്ങൾ കൂടി}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">മറ്റ് സൈറ്റുകളിൽ നിങ്ങളുടെ <ph name="ORG_NAME" /> പാസ്‌വേഡ് വീണ്ടും ഉപയോഗിച്ചിട്ടുണ്ടെങ്കിൽ, അത് റീസെറ്റ് ചെയ്യാൻ Chrome ശുപാർശ ചെയ്യുന്നു.</translation>
<translation id="4196861286325780578">&amp;നീക്കുന്നത് വീണ്ടും ചെയ്യുക</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ഫയർവാളും ആന്റിവൈറസ് കോൺഫിഗറേഷനുകളും പരിശോധിക്കുന്നു<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">ക്രാഷുകള്‍</translation>
@@ -489,7 +483,6 @@
<translation id="425582637250725228">നിങ്ങൾ വരുത്തിയ മാറ്റങ്ങൾ സംരക്ഷിക്കപ്പെട്ടിരിക്കാനിടയില്ല.</translation>
<translation id="4258748452823770588">മോശം സിഗ്‌നേച്ചർ</translation>
<translation id="4265872034478892965">നിങ്ങളുടെ അഡ്‌മിനിട്രേറ്റർ അനുവദിച്ചിരിക്കുന്നു</translation>
-<translation id="4269787794583293679">(ഉപയോക്തൃനാമമില്ല)</translation>
<translation id="4275830172053184480">നിങ്ങളുടെ ഉപകരണം പുനരാരംഭിക്കുക</translation>
<translation id="4277028893293644418">പാസ്‌വേഡ് റീസെറ്റ് ചെയ്യുക</translation>
<translation id="4280429058323657511">, <ph name="EXPIRATION_DATE_ABBR" />-ന് കാലാവധി തീരുന്നു</translation>
@@ -512,6 +505,7 @@
<translation id="4434045419905280838">പോപ്-അപ്പുകളും റീഡയറക്‌റ്റുകളും</translation>
<translation id="443673843213245140">പ്രോക്‌സി ഉപയോഗം അപ്രാപ്‌തമാക്കി പക്ഷെ ഒരു വ്യക്തമായ പ്രോക്‌സി കോൺഫിഗറേഷൻ നിർദ്ദേശിച്ചു.</translation>
<translation id="445100540951337728">ഡെബിറ്റ് കാർഡുകൾ സ്വീകരിക്കുന്നു</translation>
+<translation id="4472575034687746823">ആരംഭിക്കാം</translation>
<translation id="4506176782989081258">മൂല്യനിർണ്ണയ പിശക്: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">സിസ്റ്റം അഡ്‌മിനെ ബന്ധപ്പെടുന്നു</translation>
<translation id="450710068430902550">അഡ്‌മിനിസ്‌ട്രേറ്ററുമായി പങ്കിടുന്നു</translation>
@@ -536,8 +530,8 @@
<translation id="4726672564094551039">നയങ്ങൾ വീണ്ടും ലോഡുചെയ്യുക</translation>
<translation id="4728558894243024398">പ്ലാറ്റ്ഫോം</translation>
<translation id="4736825316280949806">Chromium റീസ്‌റ്റാർട്ടുചെയ്യുക</translation>
+<translation id="4742407542027196863">പാസ്‌വേഡുകൾ മാനേജ് ചെയ്യുക…</translation>
<translation id="4744603770635761495">നിര്‍വ്വഹിക്കാവുന്ന പാത</translation>
-<translation id="4749685221585524849"><ph name="LAST_USED_MONTH" />-ന് അവസാനമായി ഉപയോഗിച്ചു</translation>
<translation id="4750917950439032686">നിങ്ങളുടെ വിവരങ്ങൾ (ഉദാഹരണത്തിന്, പാസ്‌വേഡുകളോ ക്രെഡിറ്റ് കാർഡ് നമ്പറുകളോ) ഈ സൈറ്റിലേക്ക് അയച്ച് കഴിഞ്ഞാൽ പിന്നെയത് സ്വകാര്യമായിരിക്കും.</translation>
<translation id="4756388243121344051">&amp;ചരിത്രം</translation>
<translation id="4758311279753947758">കോൺടാക്റ്റ് വിവരങ്ങൾ ചേർക്കുക</translation>
@@ -554,6 +548,7 @@
<translation id="4850886885716139402">കാണുക</translation>
<translation id="4854362297993841467">ഈ ഡെലിവറി രീതി ലഭ്യമല്ല. മറ്റൊരു രീതി പരീക്ഷിക്കുക.</translation>
<translation id="4858792381671956233">ഈ സൈറ്റ് സന്ദർശിക്കുന്നതിന് നിങ്ങൾ രക്ഷിതാക്കളോട് അനുമതി ആവശ്യപ്പെട്ടു</translation>
+<translation id="4876305945144899064">ഉപയോക്തൃനാമമില്ല</translation>
<translation id="4880827082731008257">തിരയൽ ചരിത്രം</translation>
<translation id="4881695831933465202">തുറക്കുക</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -587,6 +582,7 @@
<translation id="5089810972385038852">സ്റ്റേറ്റ്</translation>
<translation id="5094747076828555589">ഈ സെർവറിന് അത് <ph name="DOMAIN" /> ആണെന്ന് തെളിയിക്കാനായില്ല; അതിന്റെ സുരക്ഷ സർട്ടിഫിക്കറ്റിനെ Chromium-ത്തിന്ന് പരിചയമില്ല. തെറ്റായ കോൺഫിഗറേഷൻ കാരണമോ ഒരു അക്രമണകാരി നിങ്ങളുടെ കണക്ഷനെ തടസ്സപ്പെടുത്തുന്നത് കൊണ്ടോ ആയിരിക്കാം ഇത് സംഭവിച്ചത്.</translation>
<translation id="5095208057601539847">പ്രവിശ്യ</translation>
+<translation id="5098332213681597508">ഈ പേര് നിങ്ങളുടെ Google അക്കൗണ്ടിൽ നിന്നുള്ളതാണ്.</translation>
<translation id="5115563688576182185">(64-ബിറ്റ്)</translation>
<translation id="5121084798328133320">സ്ഥിരീകരിച്ച് കഴിഞ്ഞാൽ, നിങ്ങളുടെ Google പേയ്‌മെന്‍റ് അക്കൗണ്ടിൽ നിന്നുള്ള കാർഡ് വിശദാംശങ്ങൾ ഈ സൈറ്റുമായി പങ്കിടും.</translation>
<translation id="5128122789703661928">സെഷന്‍റെ പേര് തെറ്റായതിനാൽ ഇല്ലാതാക്കാനായില്ല.</translation>
@@ -627,9 +623,10 @@
<translation id="5332219387342487447">ഷിപ്പിംഗ് രീതി</translation>
<translation id="5355557959165512791">സർട്ടിഫിക്കറ്റ് റദ്ദാക്കിയതിനാൽ നിങ്ങൾക്കിപ്പോൾ <ph name="SITE" /> സന്ദർശിക്കാനാകില്ല. നെറ്റ്‌വർക്ക് പിശകുകളും ആക്രമണങ്ങളും സാധാരണ താൽക്കാലികമായിരിക്കും, അതിനാൽ ഈ പേജ് മിക്കവാറും പിന്നീട് പ്രവർത്തിക്കും.</translation>
<translation id="536296301121032821">നയ ക്രമീകരണങ്ങൾ സംഭരിക്കുന്നതിൽ പരാജയപ്പെട്ടു</translation>
+<translation id="5371425731340848620">കാർഡ് അപ്ഡേറ്റ് ചെയ്യുക</translation>
<translation id="5377026284221673050">"നിങ്ങളുടെ സമയം പിന്നിലാണ്" അല്ലെങ്കിൽ "നിങ്ങളുടെ സമയം മുന്നിലാണ്" അല്ലെങ്കിൽ "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">ഈ സൈറ്റിന്റെ സർട്ടിഫിക്കറ്റ് ചെയിനിൽ SHA-1 ഉപയോഗിച്ച് സൈൻ ചെയ്‌ത ഒരു സർട്ടിഫിക്കറ്റ് അടങ്ങിയിരിക്കുന്നു.</translation>
-<translation id="5402410679244714488">കാലഹരണപ്പെടുന്ന തീയതി: <ph name="EXPIRATION_DATE_ABBR" />, അവസാനമായി ഉപയോഗിച്ചത് ഒരു വർഷം മുമ്പാണ്</translation>
+<translation id="5387961145478138773">നിങ്ങളുടെ പ്രിയപ്പെട്ട Google ആപ്പുകളിലേക്ക് വേഗത്തിൽ ആക്‌സസ് നേടുക</translation>
<translation id="540969355065856584">ഈ സെർവറിന് അത് <ph name="DOMAIN" /> ആണെന്ന് തെളിയിക്കാനായില്ല; അതിന്റെ സുരക്ഷ സർട്ടിഫിക്കറ്റിന് ഇപ്പോൾ സാധുതയുള്ളതല്ല. തെറ്റായ കോൺഫിഗറേഷൻ കാരണമോ ഒരു ആക്രമണകാരി നിങ്ങളുടെ കണക്ഷനെ തടസ്സപ്പെടുത്തുന്നത് കൊണ്ടോ ആയിരിക്കാം ഇത് സംഭവിച്ചത്.</translation>
<translation id="5421136146218899937">ബ്രൗസിംഗ് ഡാറ്റ മായ്‌ക്കുക...</translation>
<translation id="5430298929874300616">ബുക്ക്‌മാർക്ക് നീക്കംചെയ്യുക</translation>
@@ -671,11 +668,13 @@
<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="5659593005791499971">ഇമെയില്‍</translation>
+<translation id="5666899935841546222">എല്ലാ കാർഡുകളും നിങ്ങൾക്ക് ഒരു സ്ഥലത്ത് സൂക്ഷിക്കണോ?</translation>
<translation id="5675650730144413517">ഈ പേജ് പ്രവർത്തിക്കുന്നില്ല</translation>
<translation id="5685654322157854305">ഷിപ്പിംഗ് വിലാസം ചേർക്കുക</translation>
<translation id="5689199277474810259">JSON-ലേക്ക് ‌എക്‌സ്‌പോർട്ട് ചെയ്യുക</translation>
<translation id="5689516760719285838">ലൊക്കേഷൻ</translation>
<translation id="570530837424789914">മാനേജ് ചെയ്യുക...</translation>
+<translation id="57094364128775171">ശക്തമായ പാസ്‌വേഡ് നിർദ്ദേശിക്കുക…</translation>
<translation id="5710435578057952990">ഈ വെബ്സൈറ്റിന്റെ വ്യക്തിത്വം പരിശോധിച്ചിട്ടില്ല.</translation>
<translation id="5719499550583120431">പ്രീപെയ്ഡ് കാർഡുകൾ സ്വീകരിക്കുന്നു.</translation>
<translation id="5720705177508910913">നിലവിലെ ഉപയോക്താവ്</translation>
@@ -696,11 +695,9 @@
<translation id="5869405914158311789">ഈ സൈറ്റ് ലഭ്യമാക്കാനാകുന്നില്ല</translation>
<translation id="5869522115854928033">സംരക്ഷിച്ച പാസ്‌വേഡുകള്‍</translation>
<translation id="5893752035575986141">ക്രെഡിറ്റ് കാർഡുകൾ സ്വീകരിക്കുന്നു.</translation>
-<translation id="5898382028489516745">മറ്റ് സൈറ്റുകളിൽ നിങ്ങളുടെ <ph name="ORG_NAME" /> പാസ്‌വേഡ് വീണ്ടും ഉപയോഗിച്ചിട്ടുണ്ടെങ്കിൽ, അത് റീസെറ്റ് ചെയ്യാൻ Chromium ശുപാർശ ചെയ്യുന്നു.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (സമന്വയിപ്പിച്ചത്)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{ഒരെണ്ണം ഉപയോഗത്തിലുണ്ട്}other{# എണ്ണം ഉപയോഗത്തിലുണ്ട്}}</translation>
<translation id="5939518447894949180">വീണ്ടും സജ്ജീകരിക്കുക</translation>
-<translation id="5959728338436674663">അപകടകരമായ ആപ്‌സുകളും സൈറ്റുകളും കണ്ടെത്താൻ സഹായിക്കുന്നതിന്‌ ചില <ph name="BEGIN_WHITEPAPER_LINK" />സിസ്‌റ്റം വിവരങ്ങളും പേജ്‌ ഉള്ളടക്കവും<ph name="END_WHITEPAPER_LINK" /> Google-ന്‌ സ്വയമേവ അയയ്‌ക്കുക. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">കോൺടാക്‌റ്റ് വിവരം എഡിറ്റുചെയ്യുക</translation>
<translation id="5967867314010545767">ചരിത്രത്തിൽ നിന്നും നീക്കംചെയ്യുക</translation>
<translation id="5975083100439434680">സൂം ഔട്ട്</translation>
@@ -746,13 +743,11 @@
<translation id="6282194474023008486">തപാല്‍ കോഡ്</translation>
<translation id="6290238015253830360">നിങ്ങളുടെ നിർദ്ദേശിച്ച ലേഖനങ്ങൾ ഇവിടെ ദൃശ്യമാകും</translation>
<translation id="6305205051461490394"><ph name="URL" /> ലഭ്യമല്ല.</translation>
-<translation id="6319915415804115995">അവസാനമായി ഉപയോഗിച്ചത് ഒരു വർഷം മുമ്പാണ്</translation>
<translation id="6321917430147971392">നിങ്ങളുടെ DNS ക്രമീകരണങ്ങൾ പരിശോധിക്കുക</translation>
<translation id="6328639280570009161">നെറ്റ്‌വർക്ക് പ്രവചനം പ്രവർത്തനരഹിതമാക്കാൻ ശ്രമിക്കുക</translation>
<translation id="6328786501058569169">ഈ സൈറ്റ് വഞ്ചനാപരമായതാണ്</translation>
<translation id="6337133576188860026"><ph name="SIZE" />-യിൽ കുറഞ്ഞ ഡാറ്റ ലാഭിക്കുന്നു.അടുത്തതവണ നിങ്ങൾ സന്ദർശിക്കുമ്പോൾ ചില സൈറ്റുകൾ ഇതിനേക്കാൾ പതുക്കെ ലോഡാകാം.</translation>
<translation id="6337534724793800597">പേരിന്റെ ക്രമത്തിൽ നയങ്ങൾ ഫിൽട്ടർ ചെയ്യുക</translation>
-<translation id="6342069812937806050">ഇപ്പോള്‍‌</translation>
<translation id="6355080345576803305">പൊതു സെഷൻ അസാധുവാക്കി</translation>
<translation id="6358450015545214790">ഇത് അര്‍ത്ഥമാക്കുന്നതെന്താണ്?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{മറ്റൊരു നിർദ്ദേശം}other{മറ്റ് # നിർദ്ദേശങ്ങൾ}}</translation>
@@ -777,7 +772,6 @@
<translation id="6529602333819889595">&amp;ഇല്ലാതാക്കുന്നത് വീണ്ടും ചെയ്യുക</translation>
<translation id="6534179046333460208">ഫിസിക്കൽ വെബ് നിർദ്ദേശങ്ങൾ</translation>
<translation id="6550675742724504774">ഓപ്ഷനുകൾ</translation>
-<translation id="6556915248009097796">കാലഹരണപ്പെടുന്ന തീയതി: <ph name="EXPIRATION_DATE_ABBR" />, <ph name="LAST_USED_DATE_NO_DETAIL" />-ന് അവസാനമായി ഉപയോഗിച്ചു</translation>
<translation id="6563469144985748109">നിങ്ങളുടെ മാനേജർ ഇതുവരെ അംഗീകാരം നൽകിയിട്ടില്ല</translation>
<translation id="6569060085658103619">നിങ്ങൾ ഒരു വിപുലീകരണ പേജാണ് കാണുന്നത്</translation>
<translation id="6596325263575161958">എൻക്രിപ്‌ഷൻ ഓപ്‌ഷനുകൾ</translation>
@@ -806,15 +800,20 @@
<translation id="6825578344716086703">നിങ്ങൾ <ph name="DOMAIN" /> എന്നതിൽ എത്താൻ ശ്രമിച്ചു, പക്ഷേ ഒരു ദുർബലമായ സിഗ്‌നേച്ചർ അൽഗോരിതം ഉപയോഗിച്ച് ഒപ്പിട്ട ഒരു സർട്ടിഫിക്കറ്റ് സെർവർ നൽകി. ഇതിനർത്ഥം സെർവർ നൽകിയ സുരക്ഷാ ക്രെഡൻഷ്യലുകൾ വ്യാജമാകാമെന്നും നിങ്ങൾ ഉദ്ദേശിച്ച സെർവർ ആയിരിക്കില്ല എന്നുമാണ് (നിങ്ങൾ ആക്രമണകാരിയുമായിട്ടാകാം ആശയവിനിമയം നടത്തുന്നത്).</translation>
<translation id="6831043979455480757">വിവർത്തനം ചെയ്യുക</translation>
<translation id="6839929833149231406">ഏരിയ</translation>
+<translation id="6852204201400771460">ആപ്പ് റീലോഡ് ചെയ്യണോ?</translation>
+<translation id="6865412394715372076">ഈ കാർഡ് ഇപ്പോൾ പരിശോധിച്ചുറപ്പിക്കാനാവില്ല</translation>
<translation id="6874604403660855544">&amp;ചേർക്കുന്നത് വീണ്ടും ചെയ്യുക</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">നയ നില പിന്തുണയ്ക്കുന്നില്ല.</translation>
<translation id="6895330447102777224">നിങ്ങളുടെ കാർഡ് സ്ഥിരീകരിച്ചു</translation>
<translation id="6897140037006041989">ഉപയോക്തൃ ഏജന്‍റ്</translation>
+<translation id="6903319715792422884">ചില <ph name="BEGIN_WHITEPAPER_LINK" />സിസ്‌റ്റം വിവരങ്ങളും പേജ് ഉള്ളടക്കവും<ph name="END_WHITEPAPER_LINK" /> Google-ലേക്ക് അയച്ച്, സുരക്ഷിത ബ്രൗസിംഗ് മെച്ചപ്പെടുത്താൻ സഹായിക്കുക. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">ഉപയോക്താവ്:</translation>
+<translation id="6944692733090228304"><ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> മാനേജ് ചെയ്യാത്ത ഒരു സൈറ്റിൽ നിങ്ങൾ പാസ്‌വേഡ് നൽകി. നിങ്ങളുടെ അക്കൗണ്ട് പരിരക്ഷിക്കുന്നതിന്, മറ്റ് ആപ്പുകളിലും സൈറ്റുകളിലും നിങ്ങളുടെ പാസ്‌വേഡ് വീണ്ടും ഉപയോഗിക്കരുത്.</translation>
<translation id="6945221475159498467">തിരഞ്ഞെടുക്കുക</translation>
<translation id="6948701128805548767">പിക്ക്അപ്പ് രീതികളും ആവശ്യകതകളും കാണാൻ ഒരു വിലാസം തിരഞ്ഞെടുക്കുക</translation>
<translation id="6949872517221025916">പാസ്‌വേഡ് പുനഃസജ്ജീകരിക്കുക</translation>
+<translation id="6950684638814147129">JSON മൂല്യം വിശകലനം ചെയ്യുമ്പോൾ പിശക്: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">സെർവറിന്റെ സർട്ടിഫിക്കറ്റ് വിശ്വസിക്കാൻ കൊള്ളാത്ത ഒന്നായി തോന്നുന്നു.</translation>
<translation id="6965382102122355670">ശരി</translation>
<translation id="6965978654500191972">ഉപാധി</translation>
@@ -874,6 +873,7 @@
&lt;strong&gt;സ്റ്റാർട്ടപ്പ് തരം/strong&gt; എന്നതിന് താഴെ, &lt;strong&gt;പ്രവർത്തനരഹിതമാക്കി&lt;/strong&gt; തിരഞ്ഞെടുക്കുക &lt;li&gt;&lt;strong&gt;സേവന നില&lt;/strong&gt; എന്നതിന് താഴെ, &lt;strong&gt;നിർത്തുക&lt;/strong&gt; ക്ലിക്ക് ചെയ്യുക &lt;li&gt;&lt;strong&gt;പ്രയോഗിക്കുക&lt;/strong&gt; ക്ലിക്ക് ചെയ്യുക, തുടർന്ന് &lt;strong&gt;ശരി&lt;/strong&gt; ക്ലിക്ക് ചെയ്യുക &lt;li&gt;
നിങ്ങളുടെ കമ്പ്യൂട്ടറിൽ നിന്ന് എങ്ങനെയാണ് സോഫ്റ്റ്‌വെയർ ശാശ്വതമായി നീക്കംചെയ്യുന്നത് എന്നറിയാൻ &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome സഹായ കേന്ദ്രം&lt;/a&gt; സന്ദർശിക്കുക &lt;/ol&gt;</translation>
+<translation id="7416351320495623771">പാസ്‌വേഡുകൾ മാനേജ് ചെയ്യുക…</translation>
<translation id="7419106976560586862">പ്രൊഫൈൽ പാത</translation>
<translation id="7437289804838430631">ബന്ധപ്പെടാനുള്ള വിവരങ്ങൾ ചേർക്കുക</translation>
<translation id="7441627299479586546">തെറ്റായ നയ വിഷയം</translation>
@@ -882,7 +882,6 @@
<translation id="7451311239929941790">ഈ പ്രശ്‌നത്തെക്കുറിച്ച് <ph name="BEGIN_LINK" />കൂടുതലറിയുന്നു<ph name="END_LINK" />.</translation>
<translation id="7455133967321480974">ഗ്ലോബൽ ഡിഫോൾട്ട് ഉപയോഗിക്കുക (തടയുക)</translation>
<translation id="7460163899615895653">മറ്റ് ഉപകരണങ്ങളിൽ നിന്നുള്ള അടുത്തിടെയുള്ള നിങ്ങളുടെ ടാബുകൾ ഇവിടെ ദൃശ്യമാകും</translation>
-<translation id="7469372306589899959">കാർഡ് സ്ഥിരീകരിക്കുന്നു</translation>
<translation id="7473891865547856676">വേണ്ട, നന്ദി</translation>
<translation id="7481312909269577407">മുന്നോട്ട്</translation>
<translation id="7485870689360869515">ഡാറ്റകളൊന്നും കണ്ടെത്തിയില്ല.</translation>
@@ -1012,6 +1011,7 @@
<translation id="8308427013383895095">നെറ്റ്വര്‍ക്ക് കണക്ഷനിലെ ഒരു പിശക് കാരണം വിവര്‍ത്തനം പരാജയപ്പെട്ടു.</translation>
<translation id="8311129316111205805">സെഷൻ ലോഡ് ചെയ്യുക</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> ഹോസ്‌റ്റിലേക്കുള്ള ആക്‌സസ്സ് നിരസിച്ചു</translation>
+<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="8349305172487531364">ബുക്മാര്‍ക്ക് ബാര്‍</translation>
<translation id="8363502534493474904">ഫ്ലൈറ്റ് മോഡ് ഓഫാക്കുന്നു</translation>
@@ -1032,21 +1032,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> പ്രതികരിക്കാൻ കൂടുതൽ സമയമെടുത്തു.</translation>
<translation id="8503559462189395349">Chrome പാസ്‌വേഡുകൾ</translation>
<translation id="8503813439785031346">ഉപയോക്തൃനാമം</translation>
+<translation id="8508648098325802031">തിരയൽ ഐക്കൺ</translation>
<translation id="8543181531796978784">നിങ്ങൾക്ക് <ph name="BEGIN_ERROR_LINK" />സുരക്ഷാപ്രശ്‌നം റിപ്പോർട്ടുചെയ്യാം<ph name="END_ERROR_LINK" /> അല്ലെങ്കിൽ, സുരക്ഷയെ ബാധിച്ചേക്കാവുന്ന അപകട സാധ്യതകളെക്കുറിച്ച് ബോധ്യമുണ്ടെങ്കിൽ <ph name="BEGIN_LINK" />സുരക്ഷിതമല്ലാത്ത ഈ സൈറ്റ്<ph name="END_LINK" />സന്ദർശിക്കുക.</translation>
<translation id="8543556556237226809">ചോദ്യങ്ങളുണ്ടോ? നിങ്ങളുടെ പ്രൊഫൈൽ സൂപ്പർവൈസുചെയ്‌ത വ്യക്തിയെ ബന്ധപ്പെടുക.</translation>
<translation id="8553075262323480129">പേജിന്‍റെ ഭാഷ നിര്‍‌ണ്ണയിക്കാന്‍‌ കഴിയാത്തതിനാല്‍‌ വിവര്‍‌ത്തനം പരാജയപ്പെട്ടു.</translation>
<translation id="8557066899867184262">കാർഡിന് പിന്നിലാണ് CVC രേഖപ്പെടുത്തിയിരിക്കുന്നത്.</translation>
<translation id="8559762987265718583">നിങ്ങളുടെ ഉപകരണത്തിന്റെ തീയതിയും സമയവും (<ph name="DATE_AND_TIME" />) തെറ്റായതിനാൽ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> എന്നതിലേക്കുള്ള സ്വകാര്യ കണക്ഷൻ സ്ഥാപിക്കാനാവില്ല.</translation>
+<translation id="8564985650692024650">മറ്റ് സൈറ്റുകളിൽ നിങ്ങൾ <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> പാസ്‌വേഡ് പുനരുപയോഗിച്ചിട്ടുണ്ടെങ്കിൽ, അത് റീസെറ്റ് ചെയ്യാൻ Chromium ശുപാർ‌ശ ചെയ്യുന്നു.</translation>
<translation id="8571890674111243710"><ph name="LANGUAGE" /> ലേക്ക് പേജ് വിവര്‍‌ത്തനം ചെയ്യുന്നു...</translation>
<translation id="858637041960032120">ഫോൺ നം. ചേർക്കൂ
</translation>
<translation id="859285277496340001">സാക്‍ഷ്യപത്രം അസാധുവാക്കിയോ എന്ന് പരിശോധിക്കുന്നതിന് അത് ഒരു മെക്കാനിസത്തെയും സൂചിപ്പിക്കുന്നില്ല.</translation>
+<translation id="860043288473659153">കാർഡിന്റെ ഉടമയുടെ പേര്</translation>
<translation id="8620436878122366504">നിങ്ങളുടെ രക്ഷിതാക്കൾ ഇതുവരെ അംഗീകാരം നൽകിയിട്ടില്ല</translation>
<translation id="8625384913736129811">ഈ ഉപകരണത്തിലേക്ക് ഈ കാർഡ് സംരക്ഷിക്കുക</translation>
-<translation id="8639963783467694461">ഓട്ടോഫില്‍ ക്രമീകരണങ്ങള്‍</translation>
+<translation id="8663226718884576429">ഓർഡർ സംഗ്രഹം, <ph name="TOTAL_LABEL" />, കൂടുതൽ വിശദാംശങ്ങൾ</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, ഉത്തരം, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> ലേക്കുള്ള നിങ്ങളുടെ കണക്ഷന്‍ എന്‍‌ക്രിപ്റ്റ് ചെയ്തിട്ടില്ല.</translation>
<translation id="8718314106902482036">പേയ്‌മെന്റ് പൂർത്തിയായിട്ടില്ല</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, തിരയൽ നിർദ്ദേശം</translation>
<translation id="8725066075913043281">വീണ്ടും ശ്രമിക്കുക</translation>
<translation id="8728672262656704056">നിങ്ങൾ അദൃശ്യ വിൻഡോയിലാണ്</translation>
<translation id="8730621377337864115">പൂർത്തിയാക്കി</translation>
@@ -1062,8 +1066,10 @@
<translation id="8820817407110198400">ബുക്ക്‌മാര്‍ക്കുകള്‍</translation>
<translation id="883848425547221593">മറ്റുള്ള ബുക്ക്‌മാര്‍‌ക്കുകള്‍‌</translation>
<translation id="884264119367021077">ഷിപ്പിംഗ് വിലാസം</translation>
+<translation id="8846319957959474018">ബുക്ക്‌മാർക്കുകൾ ഉപയോഗിച്ച് ആപ്പുകൾ എളുപ്പത്തിൽ തുറക്കുക</translation>
<translation id="884923133447025588">അസാധുവാക്കല്‍ പ്രവര്‍ത്തനം കണ്ടെത്തിയിട്ടില്ല.</translation>
<translation id="885730110891505394">Google-മായി പങ്കിടുന്നു</translation>
+<translation id="8858065207712248076">മറ്റ് സൈറ്റുകളിൽ നിങ്ങൾ <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> പാസ്‌വേഡ് പുനരുപയോഗിച്ചിട്ടുണ്ടെങ്കിൽ, അത് പുനഃസജ്ജീകരിക്കാൻ Chrome ശുപാർ‌ശ ചെയ്യുന്നു.</translation>
<translation id="8866481888320382733">നയ ക്രമീകരണങ്ങൾ പാഴ്‌സുചെയ്യുന്നതിൽ പിശക്</translation>
<translation id="8870413625673593573">സമീപകാലത്ത് അടച്ചവ</translation>
<translation id="8874824191258364635">ശരിയായ കാർഡ് നമ്പർ നൽകുക</translation>
@@ -1102,6 +1108,7 @@
<translation id="9103872766612412690">നിങ്ങളുടെ വിവരങ്ങൾ പരിരക്ഷിക്കാൻ സാധാരണയായി <ph name="SITE" />, എൻക്രിപ്‌ഷൻ ഉപയോഗിക്കുന്നു. ഇപ്പോൾ <ph name="SITE" /> സൈറ്റിലേക്ക് കണക്‌റ്റുചെയ്യാൻ Chromium ശ്രമിച്ചപ്പോൾ, അസാധാരണമായതും തെറ്റായതുമായ ക്രെഡൻഷ്യലുകൾ വെബ്‌സൈറ്റ് തിരികെ അയച്ചു. ഒരു ആക്രമണകാരി <ph name="SITE" /> എന്നതായി ഭാവിക്കാൻ ശ്രമിക്കുമ്പോഴോ Wi-Fi സൈൻ ഇൻ സ്‌ക്രീൻ, കണക്ഷനെ തടസ്സപ്പെടുത്തുമ്പോഴോ ആണ് ഇങ്ങനെ സംഭവിക്കാനിടയുള്ളത്. ഏതെങ്കിലും ഡാറ്റ കൈമാറുന്നതിനുമുമ്പ് Chromium കണക്ഷൻ അവസാനിപ്പിച്ചതിനാൽ, നിങ്ങളുടെ വിവരങ്ങൾ തുടർന്നും സുരക്ഷിതമായിരിക്കും.</translation>
<translation id="9106062320799175032">ബില്ലിംഗ് വിലാസം ചേർക്കുക</translation>
<translation id="910908805481542201">ഇത് പരിഹരിക്കാൻ എന്നെ സഹായിക്കുക</translation>
+<translation id="9114524666733003316">കാർഡ് സ്ഥിരീകരിക്കുന്നു...</translation>
<translation id="9128870381267983090">നെറ്റ്വര്‍ക്കിലേക്ക് ബന്ധിപ്പിക്കുക</translation>
<translation id="9137013805542155359">യഥാര്‍ത്ഥമായത് കാണിക്കുക</translation>
<translation id="9137248913990643158">ഈ ആപ്പ് ഉപയോഗിക്കുന്നതിന് മുമ്പ് ആരംഭിച്ച് Chrome-ൽ സൈൻ ഇൻ ചെയ്യുക.</translation>
@@ -1112,6 +1119,7 @@
<translation id="9168814207360376865">നിങ്ങൾ പേയ്‌മെന്റ് രീതികൾ സംരക്ഷിച്ചിട്ടുണ്ടോ എന്ന് പരിശോധിക്കാൻ സൈറ്റുകളെ അനുവദിക്കുക</translation>
<translation id="9169664750068251925">ഈ സൈറ്റിൽ എല്ലായ്‌പ്പോഴും തടയുക</translation>
<translation id="9170848237812810038">‍&amp;പൂര്‍വാവസ്ഥയിലാക്കുക</translation>
+<translation id="9171296965991013597">ആപ്പ് വിടണോ?</translation>
<translation id="917450738466192189">സെര്‍വറിന്‍റെ സര്‍ട്ടിഫിക്കറ്റ് അസാധുവാണ്.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> പിന്തുണയ്‌ക്കാത്ത ഒരു പ്രോട്ടോക്കോളാണ് ഉപയോഗിക്കുന്നത്.</translation>
<translation id="9205078245616868884">നിങ്ങളുടെ സമന്വയ പാസ്‌ഫ്രെയ്‌സ് ഉപയോഗിച്ച് ഡാറ്റ എൻക്രിപ്‌റ്റുചെയ്‌തു. സമന്വയം ആരംഭിക്കുന്നതിന് ഇത് നൽകുക.</translation>
diff --git a/chromium/components/strings/components_strings_mr.xtb b/chromium/components/strings/components_strings_mr.xtb
index 8e975a492c5..7971731787b 100644
--- a/chromium/components/strings/components_strings_mr.xtb
+++ b/chromium/components/strings/components_strings_mr.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">मूल्य लपवा</translation>
<translation id="1228893227497259893">चुकीचा अस्तित्व ओळखकर्ता</translation>
<translation id="1232569758102978740">अशीर्षकांकित</translation>
+<translation id="1250759482327835220">पुढील वेळेस जलद पेमेंट करण्यासाठी, तुमच्या Google खात्यावर तुमचे कार्ड आणि बिलिंग पत्ता सेव्ह करा.</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;
@@ -97,7 +98,6 @@
&lt;p&gt;कृपया &lt;strong&gt;सेटिंग्ज&lt;/strong&gt; अॅपच्या &lt;strong&gt;सर्वसाधारण&lt;/strong&gt; विभागातील तारीख आणि वेळ समायोजित करा.&lt;/p&gt;</translation>
<translation id="1583429793053364125">हे वेबपृष्‍ठ प्रदर्शित करताना काहीतरी चूक झाली.</translation>
-<translation id="1590457302292452960">क्लिष्ट पासवर्ड तयार करा...</translation>
<translation id="1592005682883173041">स्थानिक डेटा प्रवेश</translation>
<translation id="1594030484168838125">निवडा</translation>
<translation id="1620510694547887537">कॅमेरा</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">सिस्टम प्रशासकाशी संपर्क साधण्याचा प्रयत्न करा.</translation>
<translation id="1740951997222943430">वैध समाप्ती महिना एंटर करा</translation>
+<translation id="1743520634839655729">पुढील वेळेस जलद पेमेंट करण्यासाठी, तुमच्या Google खात्यावर आणि या डिव्हाइसवर तुमचे कार्ड आणि बिलिंग पत्ता सेव्ह करा.</translation>
<translation id="17513872634828108">खुले टॅब</translation>
<translation id="1753706481035618306">पृष्ठ क्रमांक</translation>
<translation id="1763864636252898013">हा सर्व्हर हे <ph name="DOMAIN" /> असल्याचे सिद्ध करू शकला नाही; त्याचे सुरक्षितता प्रमाणपत्र आपल्या डिव्हाइसच्या ऑपरेटिंग प्रणालीद्वारे विश्वसनीय नाही. हे कदाचित एका चुकीच्या कॉन्फिगरेशनमुळे किंवा आक्रमणकर्त्याने आपले कनेक्शन आंतरखंडित केल्यामुळे झाले असू शकते.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">आपले खुले टॅब येथे दिसतात</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">कार्डधारकाचे नाव</translation>
-<translation id="1806541873155184440">जोडले: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">अवैध विनंती किंवा विनंती मापदंड</translation>
<translation id="1826516787628120939">तपासत आहे</translation>
<translation id="1834321415901700177">या साइटमध्ये धोकादायक प्रोग्राम आहेत</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 सूचना}one{# सूचना}other{# सूचना}}</translation>
<translation id="2079545284768500474">पूर्ववत करा</translation>
<translation id="20817612488360358">सिस्टम प्रॉक्सी सेटिंग्ज वापरण्‍यास सेट करण्‍यात आल्या परंतु एक सुस्पष्‍ट प्रॉक्सी कॉन्फिगरेशन देखील निर्दिष्‍ट करण्‍यात आले.</translation>
-<translation id="2084558088529668945">तुम्ही साइटवर एंटर केलेला पासवर्ड <ph name="ORG_NAME" /> ने व्यवस्थापित केलेला नाही. तुमचा पासवर्ड संरक्षित करण्यासाठी, अन्य अ‍ॅप्स आणि साइटवर तुमचा पासवर्ड पुन्हा वापरू नका.</translation>
<translation id="2091887806945687916">ध्वनी</translation>
<translation id="2094505752054353250">डोमेन जुळत नाही</translation>
<translation id="2096368010154057602">विभाग</translation>
+<translation id="2102134110707549001">क्लिष्ट पासवर्ड सुचवा…</translation>
<translation id="2108755909498034140">आपला कॉंप्युटर रीस्टार्ट करा</translation>
<translation id="2113977810652731515">कार्ड</translation>
<translation id="2114841414352855701">दुर्लक्ष केले कारण ते <ph name="POLICY_NAME" /> कडून अधिलिखित झाले होते.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP एरर</translation>
<translation id="2270484714375784793">फोन नंबर</translation>
<translation id="2292556288342944218">आपला इंटरनेट प्रवेश अवरोधित केला आहे</translation>
-<translation id="230155334948463882">नवीन कार्ड?</translation>
<translation id="2316887270356262533">1 MB पेक्षा कमी जागा मोकळी करते. काही साइट तुमच्या पुढील भेटीच्या वेळी आणखी धीम्या गतीने लोड होऊ शकतात.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> साठी वापरकर्तानाव आणि पासवर्ड आवश्यक आहेत.</translation>
<translation id="2317583587496011522">डेबिट कार्डे स्वीकारली जातात.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">स्वीकारली जाणारी कार्डे</translation>
<translation id="2702801445560668637">वाचन सूची</translation>
<translation id="2704283930420550640">मूल्य स्वरुपनाशी जुळत नाही.</translation>
-<translation id="2704951214193499422">Chromium यावेळी आपल्या कार्डची पुष्टी करण्यात अक्षम होते. कृपया नंतर पुन्हा प्रयत्न करा.</translation>
<translation id="2705137772291741111">या साइटची सेव्ह (कॅश केलेली) केलेली प्रत वाचण्याजोगी नव्हती.</translation>
<translation id="2709516037105925701">ऑटोफिल</translation>
<translation id="2710942282213947212">तुमच्या काँप्युटरवरील सॉफ्टवेअर Chromium ला वेबशी सुरक्षितपणे कनेक्ट होण्यापासून थांबवत आहे</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">शिपिंग पद्धत</translation>
<translation id="277499241957683684">डिव्हाइस रेकॉर्ड गहाळ</translation>
<translation id="2781030394888168909">MacOS निर्यात करा</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">कनेक्शन रीसेट केले.</translation>
<translation id="2788784517760473862">क्रेडिट कार्डे स्वीकारली जातात</translation>
<translation id="2794233252405721443">साइट अवरोधित केली</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">आपण सेटिंग्ज पृष्ठावरून एका कनेक्शनसाठी कॉन्फिगर केलेले कोणतेही प्रॉक्सी अक्षम करू शकता.</translation>
<translation id="2955913368246107853">शोध बार बंद करा</translation>
<translation id="2958431318199492670">नेटवर्क कॉन्फिगरेशन ONC मानकाचे पालन करत नाही. कॉन्फिगरेशनचे भाग आयात केले जाऊ शकत नाहीत.</translation>
-<translation id="2966678944701946121">कालबाह्यता: <ph name="EXPIRATION_DATE_ABBR" />, जोडले: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">एक सुरक्षित कनेक्‍शन स्‍थापित करण्‍यापूर्वी, आपले घड्‍याळ योग्यरित्या सेट केले असणे आवश्यक आहे. वेबसाइट त्यांना स्‍वत:ला ओळखण्‍यासाठी वापरतात ती प्रमाणपत्रे केवळ निर्दिष्‍ट केलेल्‍या कालावधीसाठी वैध असल्याने हे असू शकते. आपल्‍या डिव्‍हाइसचे घड्‍याळ चुकीचे असल्‍यामुळे, Google Chrome ही प्रमाणपत्रे सत्यापित करू शकत नाही.</translation>
<translation id="2972581237482394796">&amp;पुन्हा करा</translation>
<translation id="2977665033722899841">सध्या निवडलेली <ph name="ROW_NAME" />. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">चुकीचा धोरण प्रकार</translation>
<translation id="3037605927509011580">च्चक!</translation>
<translation id="3041612393474885105">प्रमाणपत्र माहिती...</translation>
-<translation id="3063697135517575841">Chrome यावेळी आपल्या कार्डची पुष्टी करण्यात अक्षम होते. कृपया नंतर पुन्हा प्रयत्न करा.</translation>
<translation id="3064966200440839136">बाह्य अॅप्लिकेशन द्वारे देय देण्यासाठी गुप्त मोड सोडत आहे. सुरु ठेवायचे?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{काहीही नाही}=1{1 पासवर्ड}one{# पासवर्ड}other{# पासवर्ड}}</translation>
<translation id="3096100844101284527">पिकअप पत्ता जोडा</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">आपला डेटा आपल्या संकालन सांकेतिक वाक्यांशासह <ph name="TIME" /> वाजता कूटबद्ध केला होता. संकालन सुरु करण्यासाठी तो एंटर करा.</translation>
<translation id="3320021301628644560">बिलिंग पत्ता जोडा</translation>
<translation id="3338095232262050444">सुरक्षित</translation>
-<translation id="3340978935015468852">सेटिंग्ज</translation>
<translation id="3345135638360864351">या साइटवर प्रवेश करण्याची तुमची विनंती <ph name="NAME" /> कडे पाठविली जाऊ शकली नाही. कृपया पुन्हा प्रयत्न करा.</translation>
<translation id="3355823806454867987">प्रॉक्सी सेटिंग्ज बदला...</translation>
<translation id="3361596688432910856">Chrome पुढील माहिती <ph name="BEGIN_EMPHASIS" />सेव्ह करणार नाही<ph name="END_EMPHASIS" />:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">सर्व्हरचे प्रमाणपत्र विश्वासनीय नाही.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{ सिंक केलेल्या डिव्हाइसवर किमान 1 आयटम}=1{1 आयटम (सिंक केलेल्या डिव्‍हाइसवर आणखी काही)}one{# आयटम (सिंक केलेल्या डिव्‍हाइसवर आणखी काही)}other{# आयटम (सिंक केलेल्या डिव्‍हाइसवर आणि आणखी काही)}}</translation>
<translation id="3539171420378717834">या डिव्हाइसवर या कार्डची एक प्रत ठेवा</translation>
-<translation id="3542684924769048008">यासाठी पासवर्ड वापरा:</translation>
<translation id="3549644494707163724">आपल्या स्वतःच्या वाक्यांशासह सर्व संंकालित केलेला डेटा कूटबद्ध करा</translation>
<translation id="3556433843310711081">आपला व्यवस्थापक तुमच्यासाठी ती अनावरोधित करू शकतो</translation>
<translation id="3566021033012934673">आपले कनेक्शन खाजगी नाही</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">खराब पडताळणी स्वाक्षरी</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{आणखी <ph name="ITEM_COUNT" /> आयटम}one{आणखी <ph name="ITEM_COUNT" /> आयटम}other{आणखी <ph name="ITEM_COUNT" /> आयटम}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">अन्य साइटवर तुमचा <ph name="ORG_NAME" /> पासवर्ड पुन्हा वापरल्यास Chrome तो रीसेट करण्याची शिफारस करतो.</translation>
<translation id="4196861286325780578">&amp;हलवा पुन्हा करा</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />फायरवॉल आणि अँटीव्हायरस कॉन्फिगरेशन तपासणे<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">क्रॅश होते</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">आपण केलेले बदल कदाचित सेव्ह केले जाणार नाहीत.</translation>
<translation id="4258748452823770588">खराब स्वाक्षरी</translation>
<translation id="4265872034478892965">तुमच्या प्रशासकाकडून अनुमती असलेले</translation>
-<translation id="4269787794583293679">(वापरकर्तानाव नाही)</translation>
<translation id="4275830172053184480">आपला डिव्हाइस रीस्टार्ट करा</translation>
<translation id="4277028893293644418">पासवर्ड रीसेट करा</translation>
<translation id="4280429058323657511">, कालबाह्यता <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">पॉप-अप आणि रीडिरेक्ट</translation>
<translation id="443673843213245140">प्रॉक्सीचा वापर अक्षम करण्‍यात आला आहे पण एक सुस्पष्‍ट प्रॉक्सी कॉन्‍फिगरेशन निर्दिष्‍ट करण्‍यात आले आहे.</translation>
<translation id="445100540951337728">डेबिट कार्डे स्वीकारली जातात</translation>
+<translation id="4472575034687746823">प्रारंभ करा</translation>
<translation id="4506176782989081258">प्रमाणीकरण एरर: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">सिस्टम प्रशासकाशी संपर्क साधणे</translation>
<translation id="450710068430902550">प्रशासकासह सामायिक करीत आहे</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">धोरणे रीलोड करा</translation>
<translation id="4728558894243024398">प्लॅटफॉर्म</translation>
<translation id="4736825316280949806">Chromium रीस्टार्ट करा</translation>
+<translation id="4742407542027196863">पासवर्ड व्यवस्थापित करा…</translation>
<translation id="4744603770635761495">कार्यवाहीयोग्य पथ</translation>
-<translation id="4749685221585524849">अखेरचे वापरले: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">तुमची माहिती (उदाहरणार्थ, पासवर्ड किंवा क्रेडिट कार्ड क्रमांक) या साइटवर पाठविली जाते तेव्हा ती खाजगी राहते.</translation>
<translation id="4756388243121344051">&amp;इतिहास</translation>
<translation id="4758311279753947758">संपर्क माहिती जोडा</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">पहा</translation>
<translation id="4854362297993841467">ही वितरण पद्धत उपलब्ध नाही. वेगळी पद्धत वापरून पहा.</translation>
<translation id="4858792381671956233">या साइटला भेट देणे ठीक आहे का ते आपण आपल्‍या पालकांना विचारले</translation>
+<translation id="4876305945144899064">वापरकर्तानाव नाही</translation>
<translation id="4880827082731008257">इतिहास शोध</translation>
<translation id="4881695831933465202">उघडा</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">राज्य</translation>
<translation id="5094747076828555589">हा सर्व्हर हे <ph name="DOMAIN" /> असल्याचे सिद्ध करू शकला नाही; त्याचे सुरक्षितता प्रमाणपत्र Chromium द्वारे विश्वसनीय नाही. हे कदाचित एका चुकीच्या कॉन्फिगरेशनमुळे किंवा आक्रमणकर्त्याने आपले कनेक्शन आंतरखंडित केल्यामुळे झाले असू शकते.</translation>
<translation id="5095208057601539847">प्रांत</translation>
+<translation id="5098332213681597508">हे नाव तुमच्या Google खात्यावरून आहे.</translation>
<translation id="5115563688576182185">(64-बिट)</translation>
<translation id="5121084798328133320">तुम्ही निश्चित केल्यानंतर, तुमच्या Google पेमेंट खात्यावरील कार्ड तपशील या साइटसोबत शेअर केले जातील.</translation>
<translation id="5128122789703661928">हे नाव असलेले सेशन हटवण्यासाठी वैध नाही.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">पाठविण्‍याची पद्धत</translation>
<translation id="5355557959165512791">तुम्ही आत्ता <ph name="SITE" /> ला भेट देऊ शकत नाही कारण तिचे प्रमाणपत्र निरस्त केले गेले आहे. नेटवर्क एरर आणि आक्रमणे शक्यतो तात्पुरती असतात, त्यामुळे हे पेज नंतर पाहता येईल.</translation>
<translation id="536296301121032821">धोरण सेटिंग्ज संचयित करण्यात अयशस्वी</translation>
+<translation id="5371425731340848620">कार्ड अपडेट करा</translation>
<translation id="5377026284221673050">"तुमचे क्लॉक मागे पडले आहे" किंवा "तुमचे क्लॉक वेळेपेक्षा पुढे आहे" किंवा "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">या साइटसाठी असलेल्या प्रमाणपत्र श्रृंखलेत SHA-1 वापरून स्वाक्षरी केलेले प्रमाणपत्र असते.</translation>
-<translation id="5402410679244714488">कालबाह्यता: <ph name="EXPIRATION_DATE_ABBR" />, एका वर्षापूर्वी अखेरचे वापरले</translation>
+<translation id="5387961145478138773">तुमच्या आवडत्या Google अॅप्सचा झटपट अॅक्सेस मिळवा</translation>
<translation id="540969355065856584">हा सर्व्हर <ph name="DOMAIN" /> असल्याचे सिद्ध करू शकला नाही; त्याचे सुरक्षितता प्रमाणपत्र यावेळी वैध नाही. हे कदाचित चुकीच्या कॉन्फिगरेशनमुळे किंवा आक्रमणकर्ता आपले कनेक्शन आंतरखंडित करीत असल्‍यामुळे होऊ शकते.</translation>
<translation id="5421136146218899937">ब्राउझिंग डेटा साफ करा...</translation>
<translation id="5430298929874300616">बुकमार्क काढा</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">ईमेल</translation>
+<translation id="5666899935841546222">तुम्हाला तुमची सर्व कार्डे एका ठिकाणी हवी आहेत का?</translation>
<translation id="5675650730144413517">हे पृष्ठ कार्य करीत नाही</translation>
<translation id="5685654322157854305">पाठवण्याचा पत्ता जोडा</translation>
<translation id="5689199277474810259">JSON वर निर्यात करा</translation>
<translation id="5689516760719285838">स्थान</translation>
<translation id="570530837424789914">व्यवस्थापित करा...</translation>
+<translation id="57094364128775171">क्लिष्ट पासवर्ड सुचवा…</translation>
<translation id="5710435578057952990">या वेबसाइटची ओळख सत्यापित केली गेली नाही.</translation>
<translation id="5719499550583120431">प्रीपेड कार्डे स्वीकारली जातात.</translation>
<translation id="5720705177508910913">वर्तमान वापरकर्ता</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">या साइटवर पोहचणे शक्य नाही</translation>
<translation id="5869522115854928033">सेव्ह केलेले पासवर्ड</translation>
<translation id="5893752035575986141">क्रेडिट कार्डे स्वीकारली जातात.</translation>
-<translation id="5898382028489516745">अन्य साइटवर तुमचा <ph name="ORG_NAME" /> पासवर्ड पुन्हा वापरल्यास Chromium तो रीसेट करण्याची शिफारस करतो.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (सिंक केलेले)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 वापरात आहे}one{# वापरात आहे}other{# वापरात आहेत}}</translation>
<translation id="5939518447894949180">रीसेट करा</translation>
-<translation id="5959728338436674663">धोकादायक अॅप्स आणि साइट शोधण्यात मदत करण्‍यासाठी काही <ph name="BEGIN_WHITEPAPER_LINK" />सिस्टम माहिती आणि पृष्ठ सामग्री<ph name="END_WHITEPAPER_LINK" /> स्वयंचलितपणे Google कडे पाठवा. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">संपर्क माहिती संपादित करा</translation>
<translation id="5967867314010545767">इतिहासातून काढा</translation>
<translation id="5975083100439434680">झूम कमी करा</translation>
@@ -748,13 +745,11 @@
<translation id="6282194474023008486">पोस्टल कोड</translation>
<translation id="6290238015253830360">आपण सुचविलेले लेख येथे दिसतील</translation>
<translation id="6305205051461490394"><ph name="URL" /> आवाक्याबाहेर आहे.</translation>
-<translation id="6319915415804115995">एका वर्षापूर्वी अखेरचे वापरले</translation>
<translation id="6321917430147971392">आपल्या DNS सेटिंग्ज तपासा</translation>
<translation id="6328639280570009161">नेटवर्क पूर्वानुमान अक्षम करून पहा</translation>
<translation id="6328786501058569169">ही साइट फसवी आहे</translation>
<translation id="6337133576188860026"><ph name="SIZE" /> पेक्षा कमी जागा मोकळी करते. काही साइट तुमच्या पुढील भेटीच्या वेळी आणखी धीम्या गतीने लोड होऊ शकतात.</translation>
<translation id="6337534724793800597">धोरणे नावानुसार फिल्टर करा</translation>
-<translation id="6342069812937806050">आत्ताच</translation>
<translation id="6355080345576803305">सार्वजनिक सत्र अधिशून्य</translation>
<translation id="6358450015545214790">याचा अर्थ काय आहे?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 अन्य सूचना}one{# अन्य सूचना}other{# अन्य सूचना}}</translation>
@@ -779,7 +774,6 @@
<translation id="6529602333819889595">&amp;पुन्हा करा हटवा</translation>
<translation id="6534179046333460208">वास्तविक वेब सूचना</translation>
<translation id="6550675742724504774">पर्याय</translation>
-<translation id="6556915248009097796">कालबाह्यता: <ph name="EXPIRATION_DATE_ABBR" />, अखेरचे वापरले: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">आपल्या व्यवस्थापकाने अद्याप ती मंजूर केली नाही</translation>
<translation id="6569060085658103619">आपण एक विस्तार पृष्ठ पाहत आहात</translation>
<translation id="6596325263575161958">कूटबद्धता पर्याय</translation>
@@ -808,15 +802,20 @@
<translation id="6825578344716086703">तुम्ही <ph name="DOMAIN" /> वर पोहोचण्याचा प्रयत्न केला, परंतु सर्व्हरने एका कमकुवत स्वाक्षरी अल्गोरिदमचा (जसे SHA-1) वापर करून स्वाक्षरीकृत केलेले प्रमाणपत्र सादर केले. याचा अर्थ असा आहे की सर्व्हरने सादर केलेली सुरक्षितता क्रेडेन्शियल बनावट असू शकतात आणि हा सर्व्हर तुम्ही अपेक्षा करीत असलेला नसेल. (तुम्ही कदाचित एखाद्या हल्लेखोराशी संभाषण करत आहात).</translation>
<translation id="6831043979455480757">भाषांतर करा</translation>
<translation id="6839929833149231406">क्षेत्र</translation>
+<translation id="6852204201400771460">अॅप रीलोड करायचे?</translation>
+<translation id="6865412394715372076">या कार्डची पडताळणी आता करू शकत नाही</translation>
<translation id="6874604403660855544">&amp;जोडा पुन्हा करा</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">धोरण स्तर समर्थित नाही.</translation>
<translation id="6895330447102777224">आपल्या कार्डची पुष्टी केली</translation>
<translation id="6897140037006041989">वापरकर्ता एजंट</translation>
+<translation id="6903319715792422884">सुरक्षित ब्राउझिंगमध्ये सुधारणा करण्यासाठी Google ला काही <ph name="BEGIN_WHITEPAPER_LINK" />सिस्टम माहिती आणि पेज आशय<ph name="END_WHITEPAPER_LINK" /> पाठवून मदत करा. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">वापरकर्ता:</translation>
+<translation id="6944692733090228304">तुम्ही पासवर्ड एंटर केलेली साइट <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> द्वारे व्यवस्थापित केलेली नाही. तुमच्या खात्याच्या संरक्षणासाठी, तुमचा पासवर्ड इतर अॅप्स किंवा साइटवर पुन्हा वापरू नका.</translation>
<translation id="6945221475159498467">निवडा</translation>
<translation id="6948701128805548767">पिकअप पद्धती आणि आवश्यकता पाहण्यासाठी, एक पत्ता निवडा</translation>
<translation id="6949872517221025916">पासवर्ड रीसेट करा</translation>
+<translation id="6950684638814147129">JSON मूल्य पार्स करताना एरर आली: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">सर्व्हरचे प्रमाणपत्र खोटे असल्याचे दिसून येते.</translation>
<translation id="6965382102122355670">ठीक आहे</translation>
<translation id="6965978654500191972">डिव्हाइस</translation>
@@ -878,6 +877,7 @@
&lt;li&gt;&lt;strong&gt;लागू करा&lt;/strong&gt; वर क्लिक करा, मग &lt;strong&gt;ओके&lt;/strong&gt; वर क्लिक करा
&lt;li&gt;तुमच्या कॉंप्युटरवरून सॉफ्टवेअर कायमस्वरूपी कसे काढून टाकावे याबाबत शिकण्यासाठी&lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome मदत केंद्र&lt;/a&gt; ला भेट द्या.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">पासवर्ड व्यवस्थापित करा…</translation>
<translation id="7419106976560586862">प्रोफाइल पथ</translation>
<translation id="7437289804838430631">संपर्क माहिती जोडा</translation>
<translation id="7441627299479586546">चुकीचे धोरण विषय</translation>
@@ -886,7 +886,6 @@
<translation id="7451311239929941790">या समस्येविषयी <ph name="BEGIN_LINK" />अधिक जाणून घेणे<ph name="END_LINK" />.</translation>
<translation id="7455133967321480974">सार्वत्रिक डीफॉल्‍ट वापरा (अवरोधित करा)</translation>
<translation id="7460163899615895653">अन्य डिव्हाइस वरील आपले अलीकडील टॅब येथे दिसतात</translation>
-<translation id="7469372306589899959">कार्डची पुष्टी करीत आहे</translation>
<translation id="7473891865547856676">नाही, नको</translation>
<translation id="7481312909269577407">पुढील</translation>
<translation id="7485870689360869515">डेटा आढळला नाही.</translation>
@@ -1016,6 +1015,7 @@
<translation id="8308427013383895095">नेटवर्क कनेक्शनसह समस्या असल्यामुळे भाषांतर अयशस्वी झाला.</translation>
<translation id="8311129316111205805">सेशन लोड करा</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> मधील प्रवेश नाकारला</translation>
+<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="8349305172487531364">बुकमार्क बार</translation>
<translation id="8363502534493474904">विमान मोड बंद करा</translation>
@@ -1036,20 +1036,24 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> नी प्रतिसाद देण्यात बराच वेळ घेतला.</translation>
<translation id="8503559462189395349">Chrome पासवर्ड</translation>
<translation id="8503813439785031346">वापरकर्तानाव</translation>
+<translation id="8508648098325802031">शोध आयकन</translation>
<translation id="8543181531796978784">आपण <ph name="BEGIN_ERROR_LINK" />ओळखण्‍याच्या समस्‍येचा अहवाल<ph name="END_ERROR_LINK" /> देऊ शकता किंवा आपल्‍या सुरक्षिततेस असणार्‍या जोखीम आपण समजत असल्‍यास, <ph name="BEGIN_LINK" />या असुरक्षित साइटला भेट द्या<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">प्रश्न आहेत? तुमच्या प्रोफाइलचे निरीक्षण करणाऱ्या व्यक्तीशी संपर्क साधा.</translation>
<translation id="8553075262323480129">भाषांतर करण्यात अयशस्वी कारण पृष्ठाची भाषा निर्धारित करणे शक्य नाही.</translation>
<translation id="8557066899867184262">CVV तुमच्या कार्डाच्या मागील बाजूस दिलेला असतो.</translation>
<translation id="8559762987265718583">आपल्या डिव्हाइसची तारीख आणि वेळ (<ph name="DATE_AND_TIME" />) चुकीची असल्याने <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> सह खाजगी कनेक्शन इंस्टॉल केले जाऊ शकले नाही.</translation>
+<translation id="8564985650692024650">जर तुम्ही तुमच्या <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> पासवर्डचा इतर साइटवर पुन्हा वापर केला असेल तर Chromium तुम्हाला तो रीसेट करण्याची शिफारस करतो.</translation>
<translation id="8571890674111243710"><ph name="LANGUAGE" /> मध्ये पृष्ठ अनुवादित करत आहे...</translation>
<translation id="858637041960032120">फोन नंबर जोडा</translation>
<translation id="859285277496340001">प्रमाणपत्र निरस्त झाले आहे किंवा नाही हे तपासण्यासाठी प्रणाली निर्दिष्ट करत नाही.</translation>
+<translation id="860043288473659153">कार्डधारकाचे नाव</translation>
<translation id="8620436878122366504">आपल्या पालकांनी अद्याप ती मंजूर केली नाही</translation>
<translation id="8625384913736129811">या डिव्हाइसवर हे कार्ड सेव्‍ह करा</translation>
-<translation id="8639963783467694461">स्वयंभरण सेटिंग्ज</translation>
+<translation id="8663226718884576429">ऑर्डर सारांश, <ph name="TOTAL_LABEL" />, आणखी तपशील</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, उत्तर, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> चे आपले कनेक्शन कूटबद्ध केलेले नाही.</translation>
<translation id="8718314106902482036">पेमेंट पूर्ण झाले नाही</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, शोध सूचना</translation>
<translation id="8725066075913043281">पुन्हा प्रयत्न करा</translation>
<translation id="8728672262656704056">तुम्ही गुप्त मोडमध्ये आहात</translation>
<translation id="8730621377337864115">पूर्ण झाले</translation>
@@ -1065,8 +1069,10 @@
<translation id="8820817407110198400">Bookmarks</translation>
<translation id="883848425547221593">अन्य बुकमार्क</translation>
<translation id="884264119367021077">वहनावळ पत्ता</translation>
+<translation id="8846319957959474018">बुकमार्कसह सहजरीत्या अॅप्स उघडा</translation>
<translation id="884923133447025588">कोणतीही निरस्त करण्याची प्रणाली आढळली नाही.</translation>
<translation id="885730110891505394">Google सह सामायिकरण</translation>
+<translation id="8858065207712248076">जर तुम्ही तुमच्या <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> पासवर्डचा इतर साइटवर पुन्हा वापर केला असेल तर Chrome तुम्हाला तो रीसेट करण्याची शिफारस करतो.</translation>
<translation id="8866481888320382733">धोरण सेटिंग्ज विश्लेषित करताना एरर</translation>
<translation id="8870413625673593573">अलीकडे बंद</translation>
<translation id="8874824191258364635">वैध कार्ड नंबर एंटर करा</translation>
@@ -1105,6 +1111,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> तुमची माहिती संरक्षित करण्यासाठी सामान्यतः कूटबद्धीकरण वापरते. Chromium ने यावेळी <ph name="SITE" /> शी कनेक्‍ट करण्‍याचा प्रयत्न केला तेव्‍हा, वेबसाइटने असामान्य आणि अयोग्य क्रेडेन्शियल परत पाठविले. एकतर आक्रमणकर्ता <ph name="SITE" /> असल्याची बतावणी करण्याचा प्रयत्न करतो तेव्‍हा किंवा वाय-फाय साइन इन स्क्रीनने कनेक्शनमध्ये व्यत्यय आणले तेव्‍हा हे घडू शकते. कोणत्याही डेटाची अदलाबदल करण्यापूर्वी Chromium ने कनेक्शन थांबविल्यामुळे तुमची माहिती अद्याप सुरक्षित आहे.</translation>
<translation id="9106062320799175032">बिलिंग पत्ता जोडा</translation>
<translation id="910908805481542201">याचे निराकरण करण्यात माझी मदत करा</translation>
+<translation id="9114524666733003316">कार्डची निश्चिती करत आहे...</translation>
<translation id="9128870381267983090">नेटवर्कशी कनेक्ट करा</translation>
<translation id="9137013805542155359">मूळ दर्शवा</translation>
<translation id="9137248913990643158">कृपया हा अॅप वापरण्‍यापूर्वी प्रारंभ करा आणि Chrome मध्‍ये साइन इन करा.</translation>
@@ -1115,6 +1122,7 @@
<translation id="9168814207360376865">तुम्ही पेमेंट पद्धती सेव्ह केल्या आहेत का हे तपासण्याची साइटला परवानगी द्या</translation>
<translation id="9169664750068251925">या साइटवर नेहमी अवरोधित करा</translation>
<translation id="9170848237812810038">&amp;पूर्ववत करा</translation>
+<translation id="9171296965991013597">अॅप सोडायचे?</translation>
<translation id="917450738466192189">सर्व्हरचे प्रमाणपत्र अवैध आहे.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> असमर्थित प्रोटोकॉल वापरतो.</translation>
<translation id="9205078245616868884">आपला डेटा आपल्या संकालन सांकेतिक वाक्यांशासह कूटबद्ध केला जातो. संकालन सुरु करण्यासाठी तो एंटर करा.</translation>
diff --git a/chromium/components/strings/components_strings_ms.xtb b/chromium/components/strings/components_strings_ms.xtb
index 73eb306cc47..507c65c7a48 100644
--- a/chromium/components/strings/components_strings_ms.xtb
+++ b/chromium/components/strings/components_strings_ms.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Sembunyikan nilai</translation>
<translation id="1228893227497259893">Pengecam entiti yang salah</translation>
<translation id="1232569758102978740">Tidak Bertajuk</translation>
+<translation id="1250759482327835220">Untuk membayar dengan lebih cepat selepas ini, simpan kad, nama dan alamat pengebilan anda ke Akaun Google.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (disegerakkan)</translation>
<translation id="1256368399071562588">&lt;p&gt;Jika anda cuba melawati tapak web tetapi tapak itu tidak dibuka, cuba selesaikan ralat tersebut dengan langkah penyelesaian masalah berikut terlebih dahulu:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Sila laraskan tarikh dan masa daripada bahagian &lt;strong&gt;Umum&lt;/strong&gt; bagi apl &lt;strong&gt;Tetapan&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Kesilapan berlaku semasa memaparkan halaman web ini.</translation>
-<translation id="1590457302292452960">Hasilkan kata laluan yang kukuh...</translation>
<translation id="1592005682883173041">Akses Data Setempat</translation>
<translation id="1594030484168838125">Pilih</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Cuba hubungi pentadbir sistem.</translation>
<translation id="1740951997222943430">Masukkan bulan tamat tempoh yang sah</translation>
+<translation id="1743520634839655729">Untuk membayar dengan lebih cepat selepas ini, simpan kad, nama dan alamat pengebilan anda ke Akaun Google dan ke peranti ini.</translation>
<translation id="17513872634828108">Buka tab</translation>
<translation id="1753706481035618306">Nombor halaman</translation>
<translation id="1763864636252898013">Pelayan ini tidak dapat membuktikan bahawa domainnya ialah <ph name="DOMAIN" />; sijil keselamatannya tidak dipercayai oleh sistem pengendalian peranti anda. Ini mungkin disebabkan oleh kesilapan konfigurasi atau penyerang yang memintasi sambungan anda.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Tab yang dibuka dipaparkan di sini</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Nama Pemegang Kad</translation>
-<translation id="1806541873155184440">Ditambahkan <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Permintaan atau parameter permintaan tidak sah</translation>
<translation id="1826516787628120939">Menyemak</translation>
<translation id="1834321415901700177">Tapak ini mengandungi atur cara berbahaya</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 cadangan}other{# cadangan}}</translation>
<translation id="2079545284768500474">Buat asal</translation>
<translation id="20817612488360358">Tetapan proksi sistem telah sedia untuk digunakan tetapi konfigurasi proksi jelas juga telah ditentukan.</translation>
-<translation id="2084558088529668945">Anda memasukkan kata laluan pada tapak yang tidak diurus oleh <ph name="ORG_NAME" />. Untuk melindungi akaun anda, jangan gunakan semula kata laluan pada apl dan tapak lain.</translation>
<translation id="2091887806945687916">Bunyi</translation>
<translation id="2094505752054353250">Domain tidak padan</translation>
<translation id="2096368010154057602">Jabatan</translation>
+<translation id="2102134110707549001">Cadangkan Kata Laluan Yang Kukuh…</translation>
<translation id="2108755909498034140">Mulakan semula komputer anda</translation>
<translation id="2113977810652731515">Kad</translation>
<translation id="2114841414352855701">Diabaikan kerana ia telah diatasi oleh <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Ralat HTTP</translation>
<translation id="2270484714375784793">Nombor telefon</translation>
<translation id="2292556288342944218">Akses Internet anda disekat</translation>
-<translation id="230155334948463882">Kad baharu?</translation>
<translation id="2316887270356262533">Mengosongkan kurang daripada 1 MB. Sesetengah tapak mungkin dimuatkan dengan lebih perlahan pada lawatan anda yang seterusnya.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> memerlukan nama pengguna dan kata laluan.</translation>
<translation id="2317583587496011522">Kad debit diterima.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Kad yang Diterima</translation>
<translation id="2702801445560668637">Senarai Bacaan</translation>
<translation id="2704283930420550640">Nilai tidak sepadan dengan format.</translation>
-<translation id="2704951214193499422">Chromium tidak dapat mengesahkan kad anda pada masa ini. Sila cuba lagi nanti.</translation>
<translation id="2705137772291741111">Salinan tapak ini yang disimpan (cache) tidak boleh dibaca.</translation>
<translation id="2709516037105925701">Autoisi</translation>
<translation id="2710942282213947212">Perisian pada komputer anda menghalang Chromium daripada menyambung ke web dengan selamat</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Kaedah penghantaran</translation>
<translation id="277499241957683684">Tiada rekod peranti</translation>
<translation id="2781030394888168909">Eksport MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Sambungan ditetapkan semula.</translation>
<translation id="2788784517760473862">Kad kredit yang diterima</translation>
<translation id="2794233252405721443">Tapak disekat</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Anda boleh melumpuhkan sebarang proksi yang dikonfigurasi untuk sambungan dari halaman tetapan.</translation>
<translation id="2955913368246107853">Tutup bar cari</translation>
<translation id="2958431318199492670">Konfigurasi rangkaian tidak mematuhi piawaian ONC. Sebahagian konfigurasi tidak boleh diimport.</translation>
-<translation id="2966678944701946121">Tamat: <ph name="EXPIRATION_DATE_ABBR" />, ditambahkan <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Untuk mewujudkan sambungan yang selamat, jam anda perlu ditetapkan dengan betul. Perkara ini perlu dilakukan kerana sijil yang digunakan laman web untuk mengenal pastinya hanya sah untuk tempoh masa yang tertentu. Memandangkan jam peranti anda tidak betul, Google Chrome tidak boleh mengesahkan sijil ini.</translation>
<translation id="2972581237482394796">&amp;Buat Semula</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, dipilih pada masa ini. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Jenis dasar salah</translation>
<translation id="3037605927509011580">Oh, Tidak!</translation>
<translation id="3041612393474885105">Maklumat Sijil</translation>
-<translation id="3063697135517575841">Chrome tidak dapat mengesahkan kad anda pada masa ini. Sila cuba sebentar lagi.</translation>
<translation id="3064966200440839136">Meninggalkan mod inkognito untuk membayar melalui aplikasi luar. Teruskan?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Tiada}=1{1 kata laluan}other{# kata laluan}}</translation>
<translation id="3096100844101284527">Tambahkan Alamat Pengambilan</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Data anda disulitkan dengan ungkapan laluan segerak anda pada <ph name="TIME" />. Masukkannya untuk memulakan penyegerakan.</translation>
<translation id="3320021301628644560">Tambahkan alamat pengebilan</translation>
<translation id="3338095232262050444">Selamat</translation>
-<translation id="3340978935015468852">tetapan</translation>
<translation id="3345135638360864351">Permintaan anda untuk mengakses tapak web ini tidak boleh dihantar kepada <ph name="NAME" />. Sila cuba lagi.</translation>
<translation id="3355823806454867987">Tukar tetapan proksi...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />tidak akan menyimpan<ph name="END_EMPHASIS" /> maklumat berikut:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Sijil pelayan tidak dipercayai.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Sekurang-kurangnya 1 item pada peranti yang disegerakkan}=1{1 item (dan beberapa lagi pada peranti yang disegerakkan)}other{# item (dan beberapa lagi pada peranti yang disegerakkan)}}</translation>
<translation id="3539171420378717834">Simpan salinan kad ini pada peranti ini</translation>
-<translation id="3542684924769048008">Gunakan kata laluan untuk:</translation>
<translation id="3549644494707163724">Sulitkan semua data yang disegerakkan dengan ungkapan laluan segerak anda sendiri</translation>
<translation id="3556433843310711081">Pengurus anda boleh menyahsekatnya untuk anda</translation>
<translation id="3566021033012934673">Sambungan anda tidak diperibadikan</translation>
@@ -462,7 +457,6 @@
<translation id="4171400957073367226">Tandatangan pengesahan tidak sah</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> lagi item}other{<ph name="ITEM_COUNT" /> lagi item}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome mengesyorkan anda supaya menetapkan semula kata laluan <ph name="ORG_NAME" /> jika anda menggunakan kata laluan itu semula pada tapak lain.</translation>
<translation id="4196861286325780578">&amp;Buat semula pindahkan</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Menyemak konfigurasi tembok api dan antivirus<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Nahas</translation>
@@ -491,7 +485,6 @@
<translation id="425582637250725228">Perubahan yang anda buat mungkin tidak disimpan.</translation>
<translation id="4258748452823770588">Tandatangan tidak elok</translation>
<translation id="4265872034478892965">Dibenarkan oleh pentadbir anda</translation>
-<translation id="4269787794583293679">(Tiada nama pengguna)</translation>
<translation id="4275830172053184480">Mulakan semula peranti anda</translation>
<translation id="4277028893293644418">Tetapkan semula kata laluan</translation>
<translation id="4280429058323657511">, tamat tempoh <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -514,6 +507,7 @@
<translation id="4434045419905280838">Tetingkap timbul dan ubah hala</translation>
<translation id="443673843213245140">Penggunaan proksi dilumpuhkan tetapi konfigurasi proksi yang jelas dinyatakan.</translation>
<translation id="445100540951337728">Kad debit yang diterima</translation>
+<translation id="4472575034687746823">Bermula</translation>
<translation id="4506176782989081258">Ralat pengesahan: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Menghubungi pentadbir sistem</translation>
<translation id="450710068430902550">Berkongsi dengan Pentadbir</translation>
@@ -538,8 +532,8 @@
<translation id="4726672564094551039">Muat semula dasar</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4736825316280949806">Mulakan semula Chromium</translation>
+<translation id="4742407542027196863">Urus kata laluan…</translation>
<translation id="4744603770635761495">Laluan Boleh Laku</translation>
-<translation id="4749685221585524849">Kali terakhir digunakan <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Maklumat anda (contohnya, kata laluan atau nombor kad kredit) adalah berciri peribadi apabila dihantar ke tapak ini.</translation>
<translation id="4756388243121344051">&amp;Sejarah</translation>
<translation id="4758311279753947758">Tambahkan maklumat hubungan</translation>
@@ -556,6 +550,7 @@
<translation id="4850886885716139402">Lihat</translation>
<translation id="4854362297993841467">Kaedah penghantaran ini tidak tersedia. Cuba kaedah lain.</translation>
<translation id="4858792381671956233">Anda telah bertanya kepada ibu bapa anda sama ada OK untuk melawat tapak ini</translation>
+<translation id="4876305945144899064">Tiada nama pengguna</translation>
<translation id="4880827082731008257">Sejarah carian</translation>
<translation id="4881695831933465202">Buka</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -589,6 +584,7 @@
<translation id="5089810972385038852">Negeri</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="5098332213681597508">Nama ini daripada Akaun Google anda.</translation>
<translation id="5115563688576182185">(64-bit)</translation>
<translation id="5121084798328133320">Setelah anda mengesahkan, butiran kad daripada akaun Google Payments anda akan dikongsi dengan tapak ini.</translation>
<translation id="5128122789703661928">Sesi dengan nama ini tidak sah untuk pemadaman.</translation>
@@ -629,9 +625,10 @@
<translation id="5332219387342487447">Kaedah Penghantaran</translation>
<translation id="5355557959165512791">Anda tidak boleh melawati <ph name="SITE" /> sekarang kerana sijil tapak ini telah ditarik balik. Ralat dan serangan rangkaian biasanya bersifat sementara. Oleh sebab itu, halaman ini mungkin akan berfungsi semula kemudian.</translation>
<translation id="536296301121032821">Gagal menyimpan tetapan dasar</translation>
+<translation id="5371425731340848620">Kemas kini kad</translation>
<translation id="5377026284221673050">"Jam anda lambat" atau "Jam anda cepat" atau "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">Rantaian sijil untuk tapak ini mengandungi sijil yang ditandatangani menggunakan SHA-1.</translation>
-<translation id="5402410679244714488">Tamat: <ph name="EXPIRATION_DATE_ABBR" />, terakhir digunakan lebih setahun lalu</translation>
+<translation id="5387961145478138773">Dapatkan akses pantas ke Google Apps kegemaran anda</translation>
<translation id="540969355065856584">Pelayan ini tidak dapat membuktikan bahawa pelayan adalah <ph name="DOMAIN" />; sijil keselamatan pelayan tidak sah pada masa ini. Ini mungkin disebabkan oleh kesilapan konfigurasi atau penyerang memintas sambungan anda.</translation>
<translation id="5421136146218899937">Kosongkan data semakan imbas...</translation>
<translation id="5430298929874300616">Alih keluar penanda halaman</translation>
@@ -673,11 +670,13 @@
<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="5659593005791499971">E-mel</translation>
+<translation id="5666899935841546222">Adakah anda mahu menyimpan semua kad anda di satu tempat?</translation>
<translation id="5675650730144413517">Halaman ini tidak berfungsi</translation>
<translation id="5685654322157854305">Tambahkan Alamat Penghantaran</translation>
<translation id="5689199277474810259">Eksport ke JSON</translation>
<translation id="5689516760719285838">Lokasi</translation>
<translation id="570530837424789914">Urus...</translation>
+<translation id="57094364128775171">Cadangkan kata laluan yang kukuh…</translation>
<translation id="5710435578057952990">Identiti tapak web ini belum disahkan.</translation>
<translation id="5719499550583120431">Kad prabayar diterima.</translation>
<translation id="5720705177508910913">Pengguna semasa</translation>
@@ -698,11 +697,9 @@
<translation id="5869405914158311789">Tapak ini tidak dapat dicapai</translation>
<translation id="5869522115854928033">Kata laluan disimpan</translation>
<translation id="5893752035575986141">Kad kredit diterima.</translation>
-<translation id="5898382028489516745">Chromium mengesyorkan anda supaya menetapkan semula kata laluan <ph name="ORG_NAME" /> jika anda menggunakan kata laluan itu semula pada tapak lain.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (disegerakkan)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 sedang digunakan}other{# sedang digunakan}}</translation>
<translation id="5939518447894949180">Tetapkan semula</translation>
-<translation id="5959728338436674663">Hantar secara automatik beberapa <ph name="BEGIN_WHITEPAPER_LINK" />maklumat sistem dan kandungan halaman<ph name="END_WHITEPAPER_LINK" /> kepada Google untuk membantu anda mengesan apl dan tapak yang berbahaya. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Edit Maklumat Hubungan</translation>
<translation id="5967867314010545767">Buang daripada sejarah</translation>
<translation id="5975083100439434680">Zum keluar</translation>
@@ -748,13 +745,11 @@
<translation id="6282194474023008486">Poskod</translation>
<translation id="6290238015253830360">Artikel cadangan anda dipaparkan di sini</translation>
<translation id="6305205051461490394"><ph name="URL" /> tidak dapat dicapai.</translation>
-<translation id="6319915415804115995">Terakhir digunakan lebih setahun lalu</translation>
<translation id="6321917430147971392">Semak tetapan DNS anda</translation>
<translation id="6328639280570009161">Cuba lumpuhkan ramalan rangkaian</translation>
<translation id="6328786501058569169">Tapak ini mengelirukan</translation>
<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="6342069812937806050">Sebentar tadi</translation>
<translation id="6355080345576803305">Pembatalan sesi awam</translation>
<translation id="6358450015545214790">Apakah maksudnya?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 cadangan lain}other{# cadangan lain}}</translation>
@@ -779,7 +774,6 @@
<translation id="6529602333819889595">&amp;Buat Semula Pemadaman</translation>
<translation id="6534179046333460208">Cadangan Web Fizikal</translation>
<translation id="6550675742724504774">Pilihan</translation>
-<translation id="6556915248009097796">Tamat: <ph name="EXPIRATION_DATE_ABBR" />, kali terakhir digunakan <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Pengurus anda belum meluluskannya</translation>
<translation id="6569060085658103619">Anda sedang melihat halaman sambungan</translation>
<translation id="6596325263575161958">Pilihan penyulitan</translation>
@@ -808,15 +802,20 @@
<translation id="6825578344716086703">Anda cuba untuk mencapai <ph name="DOMAIN" />, tetapi pelayan memberikan sijil yang ditandatangani menggunakan algoritma tandatangan yang lemah (seperti SHA-1). Ini bermakna bahawa bukti kelayakan keselamatan yang diberi pelayan mungkin dipalsukan dan pelayan tersebut bukan seperti yang anda jangkakan (anda mungkin berkomunikasi dengan penyerang).</translation>
<translation id="6831043979455480757">Terjemah</translation>
<translation id="6839929833149231406">Kawasan</translation>
+<translation id="6852204201400771460">Muat semula apl?</translation>
+<translation id="6865412394715372076">Kad ini tidak dapat disahkan sekarang</translation>
<translation id="6874604403660855544">&amp;Buat semula tambahkan</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Tahap dasar tidak disokong.</translation>
<translation id="6895330447102777224">Kad anda telah disahkan</translation>
<translation id="6897140037006041989">Ejen Pengguna</translation>
+<translation id="6903319715792422884">Bantu dalam meningkatkan Penyemakan Imbas Selamat dengan menghantar beberapa <ph name="BEGIN_WHITEPAPER_LINK" />maklumat sistem dan kandungan halaman<ph name="END_WHITEPAPER_LINK" /> kepada Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Pengguna:</translation>
+<translation id="6944692733090228304">Anda memasukkan kata laluan di tapak yang tidak diurus oleh <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Untuk melindungi akaun anda, jangan gunakan semula kata laluan anda pada apl dan tapak lain.</translation>
<translation id="6945221475159498467">Pilih</translation>
<translation id="6948701128805548767">Pilih alamat untuk melihat kaedah dan syarat pengambilan</translation>
<translation id="6949872517221025916">Tetapkan Semula Kata Laluan</translation>
+<translation id="6950684638814147129">Ralat semasa menghuraikan nilai JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Sijil pelayan rupanya adalah pemalsuan.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Peranti</translation>
@@ -878,6 +877,7 @@
&lt;li&gt;Klik &lt;strong&gt;Apply&lt;/strong&gt;, kemudian klik &lt;strong&gt;OK&lt;/strong&gt;
&lt;li&gt;Lawati &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Pusat bantuan Chrome&lt;/a&gt; untuk mengetahui cara mengalih keluar perisian itu daripada komputer anda secara kekal
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Urus Kata Laluan…</translation>
<translation id="7419106976560586862">Laluan Profil</translation>
<translation id="7437289804838430631">Tambahkan Maklumat Hubungan</translation>
<translation id="7441627299479586546">Subjek dasar salah</translation>
@@ -886,7 +886,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Ketahui lebih lanjut<ph name="END_LINK" /> tentang masalah ini.</translation>
<translation id="7455133967321480974">Gunakan lalai global (Sekat)</translation>
<translation id="7460163899615895653">Tab terbaharu anda daripada peranti lain dipaparkan di sini</translation>
-<translation id="7469372306589899959">Mengesahkan kad</translation>
<translation id="7473891865547856676">Tidak, Terima Kasih</translation>
<translation id="7481312909269577407">Majukan</translation>
<translation id="7485870689360869515">Tiada data dijumpai.</translation>
@@ -1016,6 +1015,7 @@
<translation id="8308427013383895095">Gagal terjemahan kerana masalah dengan sambungan rangkaian.</translation>
<translation id="8311129316111205805">Muatkan sesi</translation>
<translation id="8332188693563227489">Akses ke <ph name="HOST_NAME" /> dinafikan</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Jika anda memahami risiko terhadap keselamatan anda, anda boleh <ph name="BEGIN_LINK" />lawati tapak ini<ph name="END_LINK" /> sebelum atur cara berbahaya dialih keluar.</translation>
<translation id="8349305172487531364">Bar penanda halaman</translation>
<translation id="8363502534493474904">Matikan mod pesawat</translation>
@@ -1036,21 +1036,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> mengambil masa terlalu lama untuk bertindak balas.</translation>
<translation id="8503559462189395349">Kata Laluan Chrome</translation>
<translation id="8503813439785031346">Nama pengguna</translation>
+<translation id="8508648098325802031">Ikon Carian</translation>
<translation id="8543181531796978784">Anda boleh <ph name="BEGIN_ERROR_LINK" />laporkan masalah pengesanan<ph name="END_ERROR_LINK" /> atau jika anda memahami risikonya kepada keselamatan anda, <ph name="BEGIN_LINK" />lawati tapak yang tidak selamat ini<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Ada soalan? Hubungi orang yang menyelia profil anda.</translation>
<translation id="8553075262323480129">Gagal terjemahan kerana bahasa halaman tidak dapat ditentukan.</translation>
<translation id="8557066899867184262">CVC terletak di belakang kad anda.</translation>
<translation id="8559762987265718583">Sambungan peribadi ke <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> tidak boleh diwujudkan kerana tarikh dan masa peranti anda (<ph name="DATE_AND_TIME" />) tidak betul.</translation>
+<translation id="8564985650692024650">Chromium mengesyorkan penetapan semula kata laluan <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> jika anda menggunakan semula kata laluan itu di tapak lain.</translation>
<translation id="8571890674111243710">Menterjemah halaman ke <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Tambah no. tel.
</translation>
<translation id="859285277496340001">Sijil tidak menyatakan mekanisme untuk memeriksa sama ada ia telah dibatalkan.</translation>
+<translation id="860043288473659153">Nama pemegang kad</translation>
<translation id="8620436878122366504">Ibu bapa anda belum meluluskannya</translation>
<translation id="8625384913736129811">Simpan Kad Ini pada Peranti Ini</translation>
-<translation id="8639963783467694461">Tetapan auto isi</translation>
+<translation id="8663226718884576429">Ringkasan Pesanan, <ph name="TOTAL_LABEL" />, Butiran Lanjut</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, jawapan, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Sambungan anda ke <ph name="DOMAIN" /> tidak disulitkan.</translation>
<translation id="8718314106902482036">Pembayaran belum selesai</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, cadangan carian</translation>
<translation id="8725066075913043281">Cuba lagi</translation>
<translation id="8728672262656704056">Anda menggunakan mod inkognito</translation>
<translation id="8730621377337864115">Selesai</translation>
@@ -1066,8 +1070,10 @@
<translation id="8820817407110198400">Penanda buku</translation>
<translation id="883848425547221593">Penanda Halaman Lain</translation>
<translation id="884264119367021077">Alamat penghantaran</translation>
+<translation id="8846319957959474018">Buka apl dengan mudah menggunakan penanda halaman</translation>
<translation id="884923133447025588">Tiada mekanisme pembatalan dijumpai.</translation>
<translation id="885730110891505394">Berkongsi dengan Google</translation>
+<translation id="8858065207712248076">Chrome mengesyorkan penetapan semula kata laluan <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> jika anda menggunakan semula kata laluan itu di tapak lain.</translation>
<translation id="8866481888320382733">Ralat semasa menghuraikan tetapan dasar</translation>
<translation id="8870413625673593573">Ditutup Baru-baru Ini</translation>
<translation id="8874824191258364635">Masukkan nombor kad yang sah</translation>
@@ -1106,6 +1112,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> biasanya menggunakan penyulitan untuk melindungi maklumat anda. Apabila Chromium cuba menyambung ke <ph name="SITE" /> pada kali ini, tapak web tersebut mengembalikan bukti kelayakan yang luar biasa dan salah. Hal ini boleh berlaku apabila penyerang sedang cuba menyamar sebagai <ph name="SITE" /> atau skrin log masuk Wi-Fi telah memutuskan sambungan. Maklumat anda masih selamat kerana Chromium menghentikan sambungan sebelum sebarang pertukaran data berlaku.</translation>
<translation id="9106062320799175032">Tambahkan Alamat Pengebilan</translation>
<translation id="910908805481542201">Bantu saya menyelesaikan masalah ini</translation>
+<translation id="9114524666733003316">Mengesahkan kad...</translation>
<translation id="9128870381267983090">Sambung ke rangkaian</translation>
<translation id="9137013805542155359">Paparkan asal</translation>
<translation id="9137248913990643158">Sila mulakan dan log masuk ke Chrome sebelum menggunakan apl ini.</translation>
@@ -1116,6 +1123,7 @@
<translation id="9168814207360376865">Benarkan tapak menyemak sama ada anda mempunyai kaedah pembayaran yang disimpan</translation>
<translation id="9169664750068251925">Sentiasa sekat di tapak ini</translation>
<translation id="9170848237812810038">&amp;Buat asal</translation>
+<translation id="9171296965991013597">Tinggalkan apl?</translation>
<translation id="917450738466192189">Sijil pelayan tidak sah.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> menggunakan protokol yang tidak disokong.</translation>
<translation id="9205078245616868884">Data anda disulitkan dengan ungkapan laluan segerak anda. Masukkannya untuk memulakan penyegerakan.</translation>
diff --git a/chromium/components/strings/components_strings_nl.xtb b/chromium/components/strings/components_strings_nl.xtb
index 92a49329000..51c842efcfd 100644
--- a/chromium/components/strings/components_strings_nl.xtb
+++ b/chromium/components/strings/components_strings_nl.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Waarde verbergen</translation>
<translation id="1228893227497259893">Onjuiste entiteits-ID</translation>
<translation id="1232569758102978740">Naamloos</translation>
+<translation id="1250759482327835220">Sla je kaart, naam en factuuradres op in je Google-account zodat je de volgende keer sneller kunt betalen.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (gesynchroniseerd)</translation>
<translation id="1256368399071562588">&lt;p&gt;Als je een website wilt bezoeken en deze niet wordt geopend, kun je eerst zelf proberen het probleem op te lossen aan de hand van de volgende stappen:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Pas in het gedeelte &lt;strong&gt;Algemeen&lt;/strong&gt; van de app &lt;strong&gt;Instellingen&lt;/strong&gt; de datum en tijd aan.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Er is iets misgegaan met het weergeven van deze webpagina.</translation>
-<translation id="1590457302292452960">Een sterk wachtwoord genereren…</translation>
<translation id="1592005682883173041">Lokale gegevenstoegang</translation>
<translation id="1594030484168838125">Kiezen</translation>
<translation id="1620510694547887537">Camera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Probeer contact op te nemen met de systeembeheerder.</translation>
<translation id="1740951997222943430">Geef een geldige vervalmaand op</translation>
+<translation id="1743520634839655729">Sla je kaart, naam en factuuradres op in je Google-account en op dit apparaat zodat je de volgende keer sneller kunt betalen.</translation>
<translation id="17513872634828108">Geopende tabbladen</translation>
<translation id="1753706481035618306">Paginanummer</translation>
<translation id="1763864636252898013">De server kan niet bewijzen dat dit <ph name="DOMAIN" /> is. Het beveiligingscertificaat van de server wordt niet vertrouwd door het besturingssysteem van je apparaat. Dit kan worden veroorzaakt door een verkeerde configuratie of een aanvaller die je verbinding onderschept.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Je geopende tabbladen worden hier weergegeven</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Naam kaarthouder</translation>
-<translation id="1806541873155184440">Toegevoegd: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Ongeldige aanvraag of aanvraagparameters</translation>
<translation id="1826516787628120939">Controleren</translation>
<translation id="1834321415901700177">Deze site bevat schadelijke programma's</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 suggestie}other{# suggesties}}</translation>
<translation id="2079545284768500474">Ongedaan maken</translation>
<translation id="20817612488360358">De proxyinstellingen van het systeem moeten worden gebruikt, maar er is ook een expliciete proxyconfiguratie opgegeven.</translation>
-<translation id="2084558088529668945">Je hebt je wachtwoord ingevoerd op een site die niet wordt beheerd door <ph name="ORG_NAME" />. Hergebruik je wachtwoord niet voor andere apps en sites om je account te beschermen.</translation>
<translation id="2091887806945687916">Geluid</translation>
<translation id="2094505752054353250">Domeinen komen niet overeen</translation>
<translation id="2096368010154057602">Departement</translation>
+<translation id="2102134110707549001">Sterk wachtwoord voorstellen…</translation>
<translation id="2108755909498034140">Je computer opnieuw opstarten</translation>
<translation id="2113977810652731515">Kaart</translation>
<translation id="2114841414352855701">Genegeerd omdat het werd overschreven door <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP-fout</translation>
<translation id="2270484714375784793">Telefoonnummer</translation>
<translation id="2292556288342944218">Je toegang tot internet wordt geblokkeerd</translation>
-<translation id="230155334948463882">Nieuwe kaart?</translation>
<translation id="2316887270356262533">Hiermee wordt minder dan 1 MB vrijgemaakt. Sommige sites kunnen langzamer worden geladen wanneer je ze weer bezoekt.</translation>
<translation id="2317259163369394535">Voor <ph name="DOMAIN" /> zijn een gebruikersnaam en een wachtwoord vereist.</translation>
<translation id="2317583587496011522">Betaalpassen worden geaccepteerd.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Geaccepteerde kaarten</translation>
<translation id="2702801445560668637">Leeslijst</translation>
<translation id="2704283930420550640">Waarde komt niet overeen met notatie.</translation>
-<translation id="2704951214193499422">Chromium kan je creditcard momenteel niet bevestigen. Probeer het later opnieuw.</translation>
<translation id="2705137772291741111">De in het cachegeheugen opgeslagen versie van deze site is niet bereikbaar.</translation>
<translation id="2709516037105925701">Automatisch aanvullen</translation>
<translation id="2710942282213947212">Software op je computer voorkomt dat Chromium veilig verbinding kan maken met internet</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Verzendmethode</translation>
<translation id="277499241957683684">Apparaatrecord ontbreekt</translation>
<translation id="2781030394888168909">Exporteren naar Mac OS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">De verbinding is opnieuw ingesteld.</translation>
<translation id="2788784517760473862">Geaccepteerde creditcards</translation>
<translation id="2794233252405721443">Site geblokkeerd</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Via de instellingenpagina kun je proxyservers uitschakelen die voor een verbinding zijn geconfigureerd.</translation>
<translation id="2955913368246107853">Zoekbalk sluiten</translation>
<translation id="2958431318199492670">De netwerkconfiguratie voldoet niet aan de ONC-standaard. Delen van de configuratie worden mogelijk niet geïmporteerd.</translation>
-<translation id="2966678944701946121">Vervaldatum: <ph name="EXPIRATION_DATE_ABBR" />, toegevoegd: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Als je een beveiligde verbinding tot stand wilt brengen, moet je klok correct zijn ingesteld. Dit moet omdat de certificaten die deze websites gebruiken om zichzelf te identificeren, slechts gedurende bepaalde perioden geldig zijn. Aangezien de klok van je apparaat niet goed is ingesteld, kan Chrome deze certificaten niet verifiëren.</translation>
<translation id="2972581237482394796">&amp;Opnieuw</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, momenteel geselecteerd. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Onjuist beleidstype</translation>
<translation id="3037605927509011580">Helaas.</translation>
<translation id="3041612393474885105">Certificaatgegevens</translation>
-<translation id="3063697135517575841">Chrome kan je creditcard momenteel niet bevestigen. Probeer het later opnieuw.</translation>
<translation id="3064966200440839136">Je verlaat de incognitomodus om te betalen via een externe app. Doorgaan?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Geen}=1{1 wachtwoord}other{# wachtwoorden}}</translation>
<translation id="3096100844101284527">Ophaaladres toevoegen</translation>
@@ -338,7 +335,6 @@
<translation id="3305707030755673451">Je gegevens zijn op <ph name="TIME" /> versleuteld met je wachtwoordzin voor synchronisatie. Geef deze op om de synchronisatie te starten.</translation>
<translation id="3320021301628644560">Factuuradres toevoegen</translation>
<translation id="3338095232262050444">Beveiligd</translation>
-<translation id="3340978935015468852">instellingen</translation>
<translation id="3345135638360864351">Je verzoek om toegang tot deze site kan niet worden verzonden naar <ph name="NAME" />. Probeer het opnieuw.</translation>
<translation id="3355823806454867987">Proxyinstellingen wijzigen...</translation>
<translation id="3361596688432910856">De volgende gegevens worden <ph name="BEGIN_EMPHASIS" />niet opgeslagen<ph name="END_EMPHASIS" /> in Chrome:
@@ -372,7 +368,6 @@
<translation id="3528171143076753409">Het servercertificaat is niet betrouwbaar.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Ten minste één item op gesynchroniseerde apparaten}=1{1 item (en meer op gesynchroniseerde apparaten)}other{# items (en meer op gesynchroniseerde apparaten)}}</translation>
<translation id="3539171420378717834">Een exemplaar van deze kaart op dit apparaat bewaren</translation>
-<translation id="3542684924769048008">Wachtwoord gebruiken voor:</translation>
<translation id="3549644494707163724">Alle gesynchroniseerde gegevens versleutelen met je eigen wachtwoordzin voor synchronisatie</translation>
<translation id="3556433843310711081">Je beheerder kan de blokkering van de site opheffen</translation>
<translation id="3566021033012934673">Je verbinding is niet privé</translation>
@@ -457,7 +452,6 @@
<translation id="4171400957073367226">Onjuiste verificatiehandtekening</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{Nog <ph name="ITEM_COUNT" /> item}other{Nog <ph name="ITEM_COUNT" /> items}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome raadt je aan het wachtwoord voor je account <ph name="ORG_NAME" /> opnieuw in te stellen als je het voor andere sites hebt hergebruikt.</translation>
<translation id="4196861286325780578">&amp;Opnieuw verplaatsen</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Controleer firewall- en antivirusconfiguraties<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Crashes</translation>
@@ -486,7 +480,6 @@
<translation id="425582637250725228">Wijzigingen die je hebt aangebracht, worden mogelijk niet opgeslagen.</translation>
<translation id="4258748452823770588">Onjuiste handtekening</translation>
<translation id="4265872034478892965">Toegestaan door je beheerder</translation>
-<translation id="4269787794583293679">(Geen gebruikersnaam)</translation>
<translation id="4275830172053184480">Je apparaat opnieuw opstarten</translation>
<translation id="4277028893293644418">Wachtwoord opnieuw instellen</translation>
<translation id="4280429058323657511">, vervalt <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -509,6 +502,7 @@
<translation id="4434045419905280838">Pop-ups en omleidingen</translation>
<translation id="443673843213245140">Het gebruik van een proxy is uitgeschakeld, maar er is wel een expliciete proxyconfiguratie opgegeven.</translation>
<translation id="445100540951337728">Geaccepteerde betaalpassen</translation>
+<translation id="4472575034687746823">Aan de slag</translation>
<translation id="4506176782989081258">Validatiefout: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Neem contact op met de systeembeheerder</translation>
<translation id="450710068430902550">Delen met beheerder</translation>
@@ -533,8 +527,8 @@
<translation id="4726672564094551039">Beleid opnieuw laden</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4736825316280949806">Chromium opnieuw starten</translation>
+<translation id="4742407542027196863">Wachtwoorden beheren…</translation>
<translation id="4744603770635761495">Uitvoerbaar pad</translation>
-<translation id="4749685221585524849">Laatst gebruikt: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Je gegevens (zoals wachtwoorden of creditcardnummers) zijn privé wanneer ze worden verzonden naar deze site.</translation>
<translation id="4756388243121344051">Gesc&amp;hiedenis</translation>
<translation id="4758311279753947758">Contactgegevens toevoegen</translation>
@@ -551,6 +545,7 @@
<translation id="4850886885716139402">Weergave</translation>
<translation id="4854362297993841467">Deze bezorgingsmethode is niet beschikbaar. Kies een andere methode.</translation>
<translation id="4858792381671956233">Je hebt je ouders gevraagd of je deze site mag bezoeken</translation>
+<translation id="4876305945144899064">Geen gebruikersnaam</translation>
<translation id="4880827082731008257">Geschiedenis doorzoeken</translation>
<translation id="4881695831933465202">Openen</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -584,6 +579,7 @@
<translation id="5089810972385038852">Staat</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="5098332213681597508">Dit is de naam die in je Google-account staat.</translation>
<translation id="5115563688576182185">(64-bits)</translation>
<translation id="5121084798328133320">Nadat je hebt bevestigd, worden de kaartgegevens van je Google-betalingsaccount gedeeld met deze site.</translation>
<translation id="5128122789703661928">De naam van deze sessie die je wilt verwijderen, is ongeldig.</translation>
@@ -624,9 +620,10 @@
<translation id="5332219387342487447">Verzendmethode</translation>
<translation id="5355557959165512791">Je kunt <ph name="SITE" /> momenteel niet bezoeken, omdat het bijbehorende certificaat is ingetrokken. Netwerkfouten en aanvallen zijn doorgaans tijdelijk, dus deze pagina werkt later waarschijnlijk correct.</translation>
<translation id="536296301121032821">Opslaan van beleidsinstellingen is mislukt</translation>
+<translation id="5371425731340848620">Kaart updaten</translation>
<translation id="5377026284221673050">'Je klok loopt achter', 'Je klok loopt voor' of '&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;'</translation>
<translation id="5386426401304769735">De certificaatketen voor deze site bevat een certificaat dat is ondertekend met SHA-1.</translation>
-<translation id="5402410679244714488">Vervaldatum: <ph name="EXPIRATION_DATE_ABBR" />, meer dan een jaar geleden voor het laatst gebruikt</translation>
+<translation id="5387961145478138773">Snel toegang tot je favoriete Google-apps</translation>
<translation id="540969355065856584">Deze server kan niet bewijzen dat dit <ph name="DOMAIN" /> is. Het beveiligingscertificaat is momenteel niet geldig. Dit kan worden veroorzaakt door een verkeerde configuratie of een aanvaller die je verbinding onderschept.</translation>
<translation id="5421136146218899937">Browsegegevens wissen...</translation>
<translation id="5430298929874300616">Bladwijzer verwijderen</translation>
@@ -668,11 +665,13 @@
<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="5659593005791499971">E-mailadres</translation>
+<translation id="5666899935841546222">Wil je alle kaarten bij elkaar hebben?</translation>
<translation id="5675650730144413517">Deze pagina werkt niet</translation>
<translation id="5685654322157854305">Verzendadres toevoegen</translation>
<translation id="5689199277474810259">Exporteren naar JSON</translation>
<translation id="5689516760719285838">Locatie</translation>
<translation id="570530837424789914">Beheren...</translation>
+<translation id="57094364128775171">Sterk wachtwoord voorstellen…</translation>
<translation id="5710435578057952990">De identiteit van deze website is niet geverifieerd.</translation>
<translation id="5719499550583120431">Prepaidkaarten worden geaccepteerd.</translation>
<translation id="5720705177508910913">Huidige gebruiker</translation>
@@ -693,11 +692,9 @@
<translation id="5869405914158311789">Deze site is niet bereikbaar</translation>
<translation id="5869522115854928033">Opgeslagen wachtwoorden</translation>
<translation id="5893752035575986141">Creditcards worden geaccepteerd.</translation>
-<translation id="5898382028489516745">Chromium raadt je aan het wachtwoord voor <ph name="ORG_NAME" /> te resetten als je het voor andere sites hebt hergebruikt.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (gesynchroniseerd)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 in gebruik}other{# in gebruik}}</translation>
<translation id="5939518447894949180">Resetten</translation>
-<translation id="5959728338436674663">Bepaalde <ph name="BEGIN_WHITEPAPER_LINK" />systeeminformatie en paginacontent<ph name="END_WHITEPAPER_LINK" /> automatisch verzenden naar Google om te helpen bij de detectie van gevaarlijke apps en sites. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Contactgegevens bewerken</translation>
<translation id="5967867314010545767">Verwijderen uit geschiedenis</translation>
<translation id="5975083100439434680">Uitzoomen</translation>
@@ -742,13 +739,11 @@
<translation id="6282194474023008486">Postcode</translation>
<translation id="6290238015253830360">Je voorgestelde artikelen worden hier weergegeven</translation>
<translation id="6305205051461490394"><ph name="URL" /> is niet bereikbaar.</translation>
-<translation id="6319915415804115995">Meer dan een jaar geleden voor het laatst gebruikt</translation>
<translation id="6321917430147971392">Controleer je DNS-instellingen</translation>
<translation id="6328639280570009161">Probeer netwerkvoorspelling uit te schakelen</translation>
<translation id="6328786501058569169">Deze site is misleidend</translation>
<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="6342069812937806050">Zojuist</translation>
<translation id="6355080345576803305">Overschrijving voor openbare sessie</translation>
<translation id="6358450015545214790">Wat betekent dit?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 andere suggestie}other{# andere suggesties}}</translation>
@@ -773,7 +768,6 @@
<translation id="6529602333819889595">&amp;Opnieuw verwijderen</translation>
<translation id="6534179046333460208">Fysieke web-suggesties</translation>
<translation id="6550675742724504774">Opties</translation>
-<translation id="6556915248009097796">Vervaldatum: <ph name="EXPIRATION_DATE_ABBR" />, laatst gebruikt: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Je beheerder heeft dit nog niet goedgekeurd</translation>
<translation id="6569060085658103619">Je bekijkt een extensiepagina</translation>
<translation id="6596325263575161958">Opties voor encryptie</translation>
@@ -802,15 +796,20 @@
<translation id="6825578344716086703">Je probeert <ph name="DOMAIN" /> te bereiken. De server heeft echter een certificaat geretourneerd dat een zwak ondertekeningsalgoritme (zoals SHA-1) gebruikt. Dit houdt in dat de betrouwbaarheidsverklaring van de server kan zijn vervalst. Het is mogelijk dat de server zelf een imitatie is (wellicht een server die je schade probeert te berokkenen).</translation>
<translation id="6831043979455480757">Vertalen</translation>
<translation id="6839929833149231406">Gebied</translation>
+<translation id="6852204201400771460">App opnieuw laden?</translation>
+<translation id="6865412394715372076">Deze kaart kan momenteel niet worden geverifieerd</translation>
<translation id="6874604403660855544">&amp;Opnieuw toevoegen</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Beleidsniveau wordt niet ondersteund.</translation>
<translation id="6895330447102777224">Je creditcard is bevestigd</translation>
<translation id="6897140037006041989">User-agent</translation>
+<translation id="6903319715792422884">Help Safe Browsing te verbeteren door bepaalde <ph name="BEGIN_WHITEPAPER_LINK" />systeeminformatie en paginacontent<ph name="END_WHITEPAPER_LINK" /> naar Google te verzenden. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Gebruiker:</translation>
+<translation id="6944692733090228304">Je hebt je wachtwoord ingevoerd op een site die niet door <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> wordt beheerd. Ter bescherming van je account kun je je wachtwoord beter niet hergebruiken voor andere apps en sites.</translation>
<translation id="6945221475159498467">Selecteren</translation>
<translation id="6948701128805548767">Selecteer een adres om ophaalmethoden en vereisten te bekijken</translation>
<translation id="6949872517221025916">Wachtwoord opnieuw instellen</translation>
+<translation id="6950684638814147129">Fout bij het parseren van de JSON-waarde: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Het certificaat van de server lijkt vals te zijn.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Apparaat</translation>
@@ -872,6 +871,7 @@
&lt;li&gt;Klik op &lt;strong&gt;Toepassen&lt;/strong&gt; en vervolgens op &lt;strong&gt;OK&lt;/strong&gt;
&lt;li&gt;Ga naar het &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Helpcentrum van Chrome&lt;/a&gt; voor meer informatie over hoe je de software definitief van je computer kunt verwijderen
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Wachtwoorden beheren…</translation>
<translation id="7419106976560586862">Profielpad</translation>
<translation id="7437289804838430631">Contactgegevens toevoegen</translation>
<translation id="7441627299479586546">Onjuist beleidsonderwerp</translation>
@@ -880,7 +880,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Meer informatie<ph name="END_LINK" /> over dit probleem.</translation>
<translation id="7455133967321480974">Algemene standaardinstelling gebruiken (Blokkeren)</translation>
<translation id="7460163899615895653">Je recente tabbladen van andere apparaten worden hier weergegeven</translation>
-<translation id="7469372306589899959">Creditcard bevestigen</translation>
<translation id="7473891865547856676">Nee, bedankt</translation>
<translation id="7481312909269577407">Vooruit</translation>
<translation id="7485870689360869515">Geen gegevens gevonden.</translation>
@@ -1010,6 +1009,7 @@
<translation id="8308427013383895095">De vertaling is mislukt omdat er een probleem is opgetreden met de netwerkverbinding.</translation>
<translation id="8311129316111205805">Sessie laden</translation>
<translation id="8332188693563227489">Toegang tot <ph name="HOST_NAME" /> is geweigerd</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Als je de beveiligingsrisico's begrijpt, kun je <ph name="BEGIN_LINK" />deze site bezoeken<ph name="END_LINK" /> voordat de schadelijke programma's zijn verwijderd.</translation>
<translation id="8349305172487531364">Bladwijzerbalk</translation>
<translation id="8363502534493474904">Schakel de vliegtuigmodus uit</translation>
@@ -1030,20 +1030,24 @@
<translation id="8498891568109133222">Het duurt te lang voordat <ph name="HOST_NAME" /> reageert.</translation>
<translation id="8503559462189395349">Chrome-wachtwoorden</translation>
<translation id="8503813439785031346">Gebruikersnaam</translation>
+<translation id="8508648098325802031">Zoekpictogram</translation>
<translation id="8543181531796978784">Je kunt <ph name="BEGIN_ERROR_LINK" />een detectieprobleem melden<ph name="END_ERROR_LINK" />. Als je de veiligheidsrisico's begrijpt, kun je ook <ph name="BEGIN_LINK" />deze onveilige site bezoeken<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Vragen? Neem contact op met de persoon die je profiel beheert.</translation>
<translation id="8553075262323480129">De vertaling is mislukt omdat de taal van de pagina niet kan worden bepaald.</translation>
<translation id="8557066899867184262">De CVC-code staat op de achterkant van je kaart.</translation>
<translation id="8559762987265718583">Er kan geen privéverbinding met <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> tot stand worden gebracht, omdat de datum en tijd van je apparaat (<ph name="DATE_AND_TIME" />) onjuist zijn.</translation>
+<translation id="8564985650692024650">Chromium raadt je aan het wachtwoord voor <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> opnieuw in te stellen als je het voor andere sites hebt hergebruikt.</translation>
<translation id="8571890674111243710">Pagina wordt vertaald in het <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Telnr. toevoegen</translation>
<translation id="859285277496340001">Er wordt in het certificaat geen methode gespecificeerd waarmee kan worden gecontroleerd of het certificaat is ingetrokken.</translation>
+<translation id="860043288473659153">Naam kaarthouder</translation>
<translation id="8620436878122366504">Je ouders hebben dit nog niet goedgekeurd</translation>
<translation id="8625384913736129811">Deze kaart opslaan op dit apparaat</translation>
-<translation id="8639963783467694461">Instellingen voor Automatisch aanvullen</translation>
+<translation id="8663226718884576429">Besteloverzicht, <ph name="TOTAL_LABEL" />, meer informatie</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, antwoord, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Je verbinding met <ph name="DOMAIN" /> is niet gecodeerd.</translation>
<translation id="8718314106902482036">Betaling niet voltooid</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, zoeksuggestie</translation>
<translation id="8725066075913043281">Opnieuw proberen</translation>
<translation id="8728672262656704056">Je bent incognito</translation>
<translation id="8730621377337864115">Gereed</translation>
@@ -1059,8 +1063,10 @@
<translation id="8820817407110198400">Bladwijzers</translation>
<translation id="883848425547221593">Andere bladwijzers</translation>
<translation id="884264119367021077">Verzendadres</translation>
+<translation id="8846319957959474018">Apps gemakkelijk openen met bladwijzers</translation>
<translation id="884923133447025588">Geen intrekkingsmechanisme gevonden.</translation>
<translation id="885730110891505394">Delen met Google</translation>
+<translation id="8858065207712248076">Chrome raadt je aan het wachtwoord voor <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> opnieuw in te stellen als je het voor andere sites hebt hergebruikt.</translation>
<translation id="8866481888320382733">Fout bij het parseren van beleidsinstellingen</translation>
<translation id="8870413625673593573">Recent gesloten</translation>
<translation id="8874824191258364635">Geef een geldig kaartnummer op</translation>
@@ -1099,6 +1105,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> gebruikt gewoonlijk versleuteling om je gegevens te beschermen. Toen Chromium deze keer probeerde verbinding te maken met <ph name="SITE" />, retourneerde de website ongewone en onjuiste inloggegevens. Dit gebeurt wanneer een aanvaller probeert zich als <ph name="SITE" /> voor te doen of wanneer een wifi-inlogscherm de verbinding heeft verbroken. Je gegevens zijn nog steeds beveiligd omdat Chromium de verbinding heeft beëindigd voordat er gegevens konden worden uitgewisseld.</translation>
<translation id="9106062320799175032">Factuuradres toevoegen</translation>
<translation id="910908805481542201">Help me dit op te lossen</translation>
+<translation id="9114524666733003316">Creditcard bevestigen...</translation>
<translation id="9128870381267983090">Verbinding maken met netwerk</translation>
<translation id="9137013805542155359">Origineel weergeven</translation>
<translation id="9137248913990643158">Start Chrome en log in voordat je deze app gebruikt.</translation>
@@ -1109,6 +1116,7 @@
<translation id="9168814207360376865">Toestaan dat sites controleren of je betaalmethoden hebt opgeslagen</translation>
<translation id="9169664750068251925">Altijd blokkeren op deze site</translation>
<translation id="9170848237812810038">&amp;Ongedaan maken</translation>
+<translation id="9171296965991013597">App verlaten?</translation>
<translation id="917450738466192189">Het servercertificaat is ongeldig.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> gebruikt een niet-ondersteund protocol.</translation>
<translation id="9205078245616868884">Je gegevens zijn versleuteld met je wachtwoordzin voor synchronisatie. Geef deze op om de synchronisatie te starten.</translation>
diff --git a/chromium/components/strings/components_strings_no.xtb b/chromium/components/strings/components_strings_no.xtb
index f59c1015ec5..65aa3c7b33e 100644
--- a/chromium/components/strings/components_strings_no.xtb
+++ b/chromium/components/strings/components_strings_no.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Skjul verdien</translation>
<translation id="1228893227497259893">Feil enhetsidentifikator</translation>
<translation id="1232569758102978740">Uten tittel</translation>
+<translation id="1250759482327835220">For å betale raskere neste gang, lagre kortet og faktureringsadressen i Google-kontoen din.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" /> og <ph name="TYPE_2" /> (synkronisert)</translation>
<translation id="1256368399071562588">&lt;p&gt;Hvis du prøver å gå til et nettsted og det ikke åpnes, bør du først prøve å løse feilen med disse feilsøkingstrinnene:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Juster datoen og klokkeslettet under &lt;strong&gt;Generelt&lt;/strong&gt; i &lt;strong&gt;Innstillinger&lt;/strong&gt;-appen.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Noe gikk galt under åpningen av denne nettsiden.</translation>
-<translation id="1590457302292452960">Generer et sterkt passord …</translation>
<translation id="1592005682883173041">Tilgang til lokale data</translation>
<translation id="1594030484168838125">Velg</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Prøv å kontakte systemadministratoren.</translation>
<translation id="1740951997222943430">Angi en gyldig utløpsmåned</translation>
+<translation id="1743520634839655729">For å betale raskere neste gang, lagre kortet og faktureringsadressen i Google-kontoen din og på denne enheten.</translation>
<translation id="17513872634828108">Åpne faner</translation>
<translation id="1753706481035618306">Sidenummer</translation>
<translation id="1763864636252898013">Denne tjeneren kunne ikke bevise at den er <ph name="DOMAIN" />. Sikkerhetssertifikatet til tjeneren er ikke klarert av enhetens operativsystem. Dette kan være forårsaket av en feilkonfigurering eller en angriper som avskjærer tilkoblingen din.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">De åpne fanene dine vises her</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Kortinnehaverens navn</translation>
-<translation id="1806541873155184440">Lagt til: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Ugyldig forespørsel eller forespørselsparametere</translation>
<translation id="1826516787628120939">Kontrollerer</translation>
<translation id="1834321415901700177">Dette nettstedet inneholder skadelige programmer</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 forslag}other{# forslag}}</translation>
<translation id="2079545284768500474">Angre</translation>
<translation id="20817612488360358">Innstillinger for systemmellomtjener er stilt inn på å brukes, men en uttrykkelig mellomtjenerkonfigurasjon er også angitt.</translation>
-<translation id="2084558088529668945">Du har skrevet inn passordet ditt på et nettsted som ikke administreres av <ph name="ORG_NAME" />. For å beskytte kontoen din må du ikke bruke det samme passordet i andre apper og på andre nettsteder.</translation>
<translation id="2091887806945687916">Lyd</translation>
<translation id="2094505752054353250">Domenene samsvarer ikke</translation>
<translation id="2096368010154057602">Avdeling</translation>
+<translation id="2102134110707549001">Foreslå et sterkt passord…</translation>
<translation id="2108755909498034140">Start datamaskinen på nytt.</translation>
<translation id="2113977810652731515">Kort</translation>
<translation id="2114841414352855701">Ignorert fordi det ble overstyrt av <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP-feil</translation>
<translation id="2270484714375784793">Telefonnummer</translation>
<translation id="2292556288342944218">Internett-tilgangen din er blokkert</translation>
-<translation id="230155334948463882">Nytt kort?</translation>
<translation id="2316887270356262533">Frigjør mindre enn 1 MB. Det kan hende enkelte nettsteder lastes inn tregere neste gang du besøker dem.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> krever brukernavn og passord.</translation>
<translation id="2317583587496011522">Debetkort godtas.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Godkjente kort</translation>
<translation id="2702801445560668637">Leseliste</translation>
<translation id="2704283930420550640">Verdien samsvarer ikke med formatet.</translation>
-<translation id="2704951214193499422">Chromium kunne ikke bekrefte kortet ditt akkurat nå. Prøv igjen senere.</translation>
<translation id="2705137772291741111">Den lagrede (bufrede) kopien av dette nettstedet kunne ikke leses.</translation>
<translation id="2709516037105925701">Autofyll</translation>
<translation id="2710942282213947212">Det er programvare på datamaskinen din som hindrer Chromium i å koble trygt til nettet</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Leveringsmetode</translation>
<translation id="277499241957683684">Manglende enhetsoppføring</translation>
<translation id="2781030394888168909">Eksportér til MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Tilkoblingen ble tilbakestilt.</translation>
<translation id="2788784517760473862">Godkjente kredittkort</translation>
<translation id="2794233252405721443">Nettstedet er blokkert</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">På innstillingssiden kan du deaktivere eventuelle mellomtjenere for tilkoblinger.</translation>
<translation id="2955913368246107853">Lukk søkefelt</translation>
<translation id="2958431318199492670">Nettverkskonfigurasjonen overholder ikke ONC-standarden. Deler av konfigurasjonen kan muligens ikke importeres.</translation>
-<translation id="2966678944701946121">Utløper: <ph name="EXPIRATION_DATE_ABBR" />. Lagt til: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Klokken må være riktig stilt før du kan opprette sikre tilkoblinger. Grunnen til dette er at sertifikatene nettsteder identifiserer seg med, bare er gyldige i visse tidsperioder. Ettersom klokken på enheten din er feil, kan ikke Google Chrome bekrefte disse sertifikatene.</translation>
<translation id="2972581237482394796">Gjø&amp;r om</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, for øyeblikket valgt. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Feil type enhetsinnstillinger</translation>
<translation id="3037605927509011580">Æsj!</translation>
<translation id="3041612393474885105">Sertifikatinformasjon</translation>
-<translation id="3063697135517575841">Chrome kunne ikke bekrefte kortet ditt akkurat nå. Prøv igjen senere.</translation>
<translation id="3064966200440839136">Går ut av inkognitomodus for å betale via en ekstern app. Vil du fortsette?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Ingen}=1{1 passord}other{# passord}}</translation>
<translation id="3096100844101284527">Legg til henteadresse</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Dataene dine er kryptert med passordfrasen din for synkronisering <ph name="TIME" />. Skriv den inn for å starte synkroniseringen.</translation>
<translation id="3320021301628644560">Legg til faktureringsadresse</translation>
<translation id="3338095232262050444">Sikker</translation>
-<translation id="3340978935015468852">innstillinger</translation>
<translation id="3345135638360864351">Forespørselen om tilgang til dette nettstedet kunne ikke sendes til <ph name="NAME" />. Prøv igjen.</translation>
<translation id="3355823806454867987">Endre innstillinger for mellomtjener</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />lagrer ikke<ph name="END_EMPHASIS" />
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Tjenerens sertifikat er ikke pålitelig.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Minst 1 element på synkroniserte enheter}=1{1 element (og flere på synkroniserte enheter)}other{# elementer (og flere på synkroniserte enheter)}}</translation>
<translation id="3539171420378717834">Lagre en kopi av dette kortet på denne enheten</translation>
-<translation id="3542684924769048008">Bruk passord for:</translation>
<translation id="3549644494707163724">Kryptér alle synkroniserte data med din egen passordfrase for synkronisering</translation>
<translation id="3556433843310711081">Administratoren din kan oppheve blokkeringen for deg</translation>
<translation id="3566021033012934673">Tilkoblingen din er ikke privat</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Ugyldig bekreftelsessignatur</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> element til}other{<ph name="ITEM_COUNT" /> elementer til}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome anbefaler at du tilbakestiller passordet ditt for <ph name="ORG_NAME" /> hvis du også har brukt det på andre nettsteder.</translation>
<translation id="4196861286325780578">&amp;Flytt likevel</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Sjekk brannmur- og antiviruskonfigurasjonen<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Kræsj</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Det kan hende endringene dine ikke er lagret.</translation>
<translation id="4258748452823770588">Dårlig signatur</translation>
<translation id="4265872034478892965">Tillatt av administratoren din</translation>
-<translation id="4269787794583293679">(Uten brukernavn)</translation>
<translation id="4275830172053184480">Start enheten din på nytt</translation>
<translation id="4277028893293644418">Tilbakestill passordet</translation>
<translation id="4280429058323657511">, utløper <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Forgrunnsvinduer/viderekoblinger</translation>
<translation id="443673843213245140">Bruk av mellomtjener er deaktivert, men det er angitt en uttrykkelig mellomtjenerkonfigurasjon.</translation>
<translation id="445100540951337728">Godkjente debetkort</translation>
+<translation id="4472575034687746823">Kom i gang</translation>
<translation id="4506176782989081258">Valideringsfeil: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Kontakt systemadministratoren</translation>
<translation id="450710068430902550">Deling med administratoren</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Last inn retningslinjer på nytt</translation>
<translation id="4728558894243024398">Plattform</translation>
<translation id="4736825316280949806">Start Chromium på nytt</translation>
+<translation id="4742407542027196863">Administrer passord…</translation>
<translation id="4744603770635761495">Kjørbar sti</translation>
-<translation id="4749685221585524849">Sist brukt: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Informasjonen din (for eksempel passord eller kredittkortnumre) er privat når den sendes til dette nettstedet.</translation>
<translation id="4756388243121344051">&amp;Logg</translation>
<translation id="4758311279753947758">Legg til kontaktinformasjon</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Visning</translation>
<translation id="4854362297993841467">Denne leveringsmetoden er ikke tilgjengelig. Prøv en annen metode.</translation>
<translation id="4858792381671956233">Du har spurt foreldrene dine om det er greit å besøke dette nettstedet</translation>
+<translation id="4876305945144899064">Uten brukernavn</translation>
<translation id="4880827082731008257">Søk i loggen</translation>
<translation id="4881695831933465202">Åpne</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Fylke / delstat</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="5098332213681597508">Dette navnet er fra Google-kontoen din.</translation>
<translation id="5115563688576182185">(64-bit)</translation>
<translation id="5121084798328133320">Etter at du har bekreftet, deles kortopplysningene fra Google Payments-kontoen med dette nettstedet.</translation>
<translation id="5128122789703661928">Økten med dette navnet er ikke gyldig for sletting.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Leveringsmetode</translation>
<translation id="5355557959165512791">Du kan ikke gå til <ph name="SITE" /> akkurat nå, siden sertifikatet for nettstedet er trukket tilbake. Nettverksfeil- og angrep er vanligvis midlertidige, så denne siden fungerer sannsynligvis senere.</translation>
<translation id="536296301121032821">Kunne ikke lagre angivelsen for enhetsinnstillinger</translation>
+<translation id="5371425731340848620">Oppdater kortet</translation>
<translation id="5377026284221673050">«Klokken går for sent», «Klokken går for fort» eller «&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;»</translation>
<translation id="5386426401304769735">Sertifikatkjeden for dette nettstedet inneholder et sertifikat som er signert med SHA-1.</translation>
-<translation id="5402410679244714488">Utløper: <ph name="EXPIRATION_DATE_ABBR" />. Sist brukt for over et år siden</translation>
+<translation id="5387961145478138773">Få rask tilgang til favorittappene dine fra Google</translation>
<translation id="540969355065856584">Denne tjeneren kunne ikke bevise at den er <ph name="DOMAIN" />. Sikkerhetssertifikatet til tjeneren er ikke gyldig for øyeblikket. Dette kan være forårsaket av en feilkonfigurasjon eller en angriper som lytter på tilkoblingen din.</translation>
<translation id="5421136146218899937">Slett nettlesingsdata...</translation>
<translation id="5430298929874300616">Fjern bokmerke</translation>
@@ -672,11 +669,13 @@
<translation id="5633066919399395251">Angripere som for øyeblikket 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="5659593005791499971">E-post</translation>
+<translation id="5666899935841546222">Vil du ha alle kortene dine på ett sted?</translation>
<translation id="5675650730144413517">Denne siden fungerer ikke</translation>
<translation id="5685654322157854305">Legg til leveringsadresse</translation>
<translation id="5689199277474810259">Eksportér til JSON</translation>
<translation id="5689516760719285838">Sted</translation>
<translation id="570530837424789914">Administrer…</translation>
+<translation id="57094364128775171">Foreslå et sterkt passord…</translation>
<translation id="5710435578057952990">Identiteten til dette nettstedet er ikke verifisert.</translation>
<translation id="5719499550583120431">Forhåndsbetalte kort godtas.</translation>
<translation id="5720705177508910913">Gjeldende bruker</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Dette nettstedet er ikke tilgjengelig</translation>
<translation id="5869522115854928033">Lagrede passord</translation>
<translation id="5893752035575986141">Kredittkort godtas.</translation>
-<translation id="5898382028489516745">Chromium anbefaler at du tilbakestiller passordet ditt for <ph name="ORG_NAME" /> hvis du også har brukt det på andre nettsteder.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (synkronisert)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 er i bruk}other{# er i bruk}}</translation>
<translation id="5939518447894949180">Tilbakestill</translation>
-<translation id="5959728338436674663">Send automatisk noe <ph name="BEGIN_WHITEPAPER_LINK" />systeminformasjon og sideinnhold<ph name="END_WHITEPAPER_LINK" /> til Google for å bidra til å oppdage farlige apper og nettsteder. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Endre kontaktinformasjonen</translation>
<translation id="5967867314010545767">Fjern fra loggen</translation>
<translation id="5975083100439434680">Zoom ut</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Postnummer</translation>
<translation id="6290238015253830360">De foreslåtte artiklene dine vises her</translation>
<translation id="6305205051461490394"><ph name="URL" /> er ikke tilgjengelig.</translation>
-<translation id="6319915415804115995">Sist brukt for over et år siden</translation>
<translation id="6321917430147971392">Kontrollér DNS-innstillingene dine</translation>
<translation id="6328639280570009161">Prøv å slå av nettverksforutsigelse</translation>
<translation id="6328786501058569169">Dette nettstedet er villedende</translation>
<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="6342069812937806050">Akkurat nå</translation>
<translation id="6355080345576803305">Offentlig økt-overstyring</translation>
<translation id="6358450015545214790">Hva innebærer dette?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 annet forslag}other{# andre forslag}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Slett likevel</translation>
<translation id="6534179046333460208">Fysisk nett-forslag</translation>
<translation id="6550675742724504774">Alternativer</translation>
-<translation id="6556915248009097796">Utløper: <ph name="EXPIRATION_DATE_ABBR" />. Sist brukt: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Administratoren din har ikke godkjent det ennå</translation>
<translation id="6569060085658103619">Du ser på en utvidelsesside</translation>
<translation id="6596325263575161958">Krypteringsalternativer</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Du prøvde å gå til <ph name="DOMAIN" />, men tjeneren presenterte et sertifikat som er signert med en svak signaturalgoritme (for eksempel SHA-1). Dette betyr at sikkerhetslegitimasjonen tjeneren presenterte, kan være forfalsket. Tjeneren kan med andre ord være en annen tjener enn du tror (og du kommuniserer kanskje med en angriper).</translation>
<translation id="6831043979455480757">Oversett</translation>
<translation id="6839929833149231406">Område</translation>
+<translation id="6852204201400771460">Vil du laste inn appen på nytt?</translation>
+<translation id="6865412394715372076">Dette kortet kan ikke bekreftes akkurat nå</translation>
<translation id="6874604403660855544">&amp;Legg til likevel</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Innstillingsnivået støttes ikke.</translation>
<translation id="6895330447102777224">Kortet ditt er bekreftet</translation>
<translation id="6897140037006041989">Brukeragent</translation>
+<translation id="6903319715792422884">Bidra til å gjøre Safe Browsing bedre ved å sende noe <ph name="BEGIN_WHITEPAPER_LINK" />systeminformasjon og sideinnhold<ph name="END_WHITEPAPER_LINK" /> til Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Bruker:</translation>
+<translation id="6944692733090228304">Du har skrevet inn passordet ditt på et nettsted som ikke administreres av <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. For å beskytte kontoen din må du ikke bruke det samme passordet i andre apper eller på andre nettsteder.</translation>
<translation id="6945221475159498467">Velg</translation>
<translation id="6948701128805548767">For å se hentemetoder og -krav, velg en adresse</translation>
<translation id="6949872517221025916">Tilbakestill passordet</translation>
+<translation id="6950684638814147129">Feil under parsering av JSON-verdi: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Tjenersertifikatet ser ut til å være forfalsket.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Enhet</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Klikk på &lt;strong&gt;Bruk&lt;/strong&gt; og deretter på &lt;strong&gt;OK&lt;/strong&gt;.
&lt;li&gt;Gå til &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;brukerstøtten for Chrome&lt;/a&gt; for å finne ut hvordan du fjerner programvaren fra datamaskinen permanent.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Administrer passord…</translation>
<translation id="7419106976560586862">Profilbane</translation>
<translation id="7437289804838430631">Legg til kontaktinformasjon</translation>
<translation id="7441627299479586546">Feil emne for innstillinger</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Finn ut mer<ph name="END_LINK" /> om dette problemet.</translation>
<translation id="7455133967321480974">Bruk global standardinnstilling (Blokkér)</translation>
<translation id="7460163899615895653">De nylige fanene dine fra andre enheter vises her</translation>
-<translation id="7469372306589899959">Bekrefter kortet</translation>
<translation id="7473891865547856676">Nei takk</translation>
<translation id="7481312909269577407">Frem</translation>
<translation id="7485870689360869515">Ingen data ble funnet.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Oversettelsen mislyktes på grunn av et problem med nettverksforbindelsen.</translation>
<translation id="8311129316111205805">Last inn økten</translation>
<translation id="8332188693563227489">Forsøket på å koble til <ph name="HOST_NAME" /> ble avvist</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Hvis du forstår sikkerhetsrisikoen, kan du <ph name="BEGIN_LINK" />gå til det usikre nettstedet<ph name="END_LINK" /> før de farlige programmene er fjernet.</translation>
<translation id="8349305172487531364">Bokmerkerad</translation>
<translation id="8363502534493474904">Slå av flymodus</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> brukte for lang tid på å svare.</translation>
<translation id="8503559462189395349">Chrome-passord</translation>
<translation id="8503813439785031346">Brukernavn</translation>
+<translation id="8508648098325802031">Søkeikon</translation>
<translation id="8543181531796978784">Du kan <ph name="BEGIN_ERROR_LINK" />rapportere et påvisningsproblem<ph name="END_ERROR_LINK" /> eller, hvis du forstår sikkerhetsrisikoen, <ph name="BEGIN_LINK" />gå til dette usikre nettstedet<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Har du spørsmål? Kontakt personen som administrer profilen din.</translation>
<translation id="8553075262323480129">Oversettelsen mislyktes fordi sidens språk ikke kunne fastslås.</translation>
<translation id="8557066899867184262">CVC-koden finner du på baksiden av kortet.</translation>
<translation id="8559762987265718583">En privat tilkobling til <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> kunne ikke etableres fordi datoen og klokkeslettet (<ph name="DATE_AND_TIME" />) er feil på enheten.</translation>
+<translation id="8564985650692024650">Chromium anbefaler at du tilbakestiller passordet ditt for <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> hvis du også har brukt det på andre nettsteder.</translation>
<translation id="8571890674111243710">Oversett siden til <ph name="LANGUAGE" /></translation>
<translation id="858637041960032120">Legg til telefonnummer</translation>
<translation id="859285277496340001">Sertifikatet spesifiserer ikke en mekanisme for å kontrollere hvorvidt det har blitt tilbakekalt.</translation>
+<translation id="860043288473659153">Kortinnehavers navn</translation>
<translation id="8620436878122366504">Foreldrene dine har ikke godkjent det ennå</translation>
<translation id="8625384913736129811">Lagre dette kortet på denne enheten</translation>
-<translation id="8639963783467694461">Innstillinger for autofyll</translation>
+<translation id="8663226718884576429">Bestillingssammendrag, <ph name="TOTAL_LABEL" />, mer informasjon</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, svar, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Tilkoblingen til <ph name="DOMAIN" /> er ikke kryptert.</translation>
<translation id="8718314106902482036">Betalingen er ikke fullført</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, søkeforslag</translation>
<translation id="8725066075913043281">Prøv igjen</translation>
<translation id="8728672262656704056">Du er nå i inkognitomodus</translation>
<translation id="8730621377337864115">Ferdig</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Bokmerker</translation>
<translation id="883848425547221593">Andre bokmerker</translation>
<translation id="884264119367021077">Leveringsadresse</translation>
+<translation id="8846319957959474018">Åpne apper enkelt med bokmerker</translation>
<translation id="884923133447025588">Finner ingen tilbakekallingsmekanisme.</translation>
<translation id="885730110891505394">Deling med Google</translation>
+<translation id="8858065207712248076">Chrome anbefaler at du tilbakestiller passordet ditt for <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> hvis du også har brukt det på andre nettsteder.</translation>
<translation id="8866481888320382733">Analysefeil i angivelsen av enhetrsinnstillinger</translation>
<translation id="8870413625673593573">Nylig lukket</translation>
<translation id="8874824191258364635">Angi et gyldig kortnummer</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> bruker vanligvis kryptering for å beskytte informasjonen din. Da Chromium prøvde å koble til <ph name="SITE" /> denne gangen, sendte nettstedet tilbake uvanlig og feil legitimasjon. Dette kan skje hvis en angriper prøver å utgi seg for å være <ph name="SITE" />, eller hvis en Wi-Fi-påloggingsskjerm har avbrutt tilkoblingen. Informasjonen din er likevel sikker fordi Chromium stoppet tilkoblingen før det ble utvekslet noen data.</translation>
<translation id="9106062320799175032">Legg til faktureringsadresse</translation>
<translation id="910908805481542201">Hjelp meg med å fikse dette</translation>
+<translation id="9114524666733003316">Bekrefter kortet …</translation>
<translation id="9128870381267983090">Koble til nettverk</translation>
<translation id="9137013805542155359">Vis original</translation>
<translation id="9137248913990643158">Du må starte og logge på Chrome før du bruker denne appen.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">Tillatt nettsteder å sjekke om du har noen lagrede betalingsmåter</translation>
<translation id="9169664750068251925">Blokkér alltid på dette nettstedet</translation>
<translation id="9170848237812810038">&amp;Angre</translation>
+<translation id="9171296965991013597">Vil du gå ut av appen?</translation>
<translation id="917450738466192189">Tjenerens sertifikat er ugyldig.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> bruker en protokoll som ikke støttes.</translation>
<translation id="9205078245616868884">Dataene dine er kryptert med passordfrasen din for synkronisering. Skriv den inn for å starte synkroniseringen.</translation>
diff --git a/chromium/components/strings/components_strings_pl.xtb b/chromium/components/strings/components_strings_pl.xtb
index fcbd57c5c63..21e3d6fc113 100644
--- a/chromium/components/strings/components_strings_pl.xtb
+++ b/chromium/components/strings/components_strings_pl.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Ukryj wartość</translation>
<translation id="1228893227497259893">Błędny identyfikator elementu</translation>
<translation id="1232569758102978740">Bez tytułu</translation>
+<translation id="1250759482327835220">Aby następnym razem zapłacić szybciej, zapisz kartę oraz imię, nazwisko i adres rozliczeniowy na swoim koncie Google.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (zsynchronizowane)</translation>
<translation id="1256368399071562588">&lt;p&gt;Jeśli nie udaje się otworzyć strony, spróbuj wykonać najpierw te czynności, aby rozwiązać problem:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Popraw datę i godzinę w sekcji &lt;strong&gt;Ogólne&lt;/strong&gt; w aplikacji &lt;strong&gt;Ustawienia&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Podczas wyświetlania strony wystąpił błąd.</translation>
-<translation id="1590457302292452960">Wygeneruj silne hasło…</translation>
<translation id="1592005682883173041">Lokalny dostęp do danych</translation>
<translation id="1594030484168838125">Wybierz</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Skontaktuj się z administratorem systemu.</translation>
<translation id="1740951997222943430">Wpisz miesiąc w prawidłowym formacie</translation>
+<translation id="1743520634839655729">Aby następnym razem zapłacić szybciej, zapisz kartę oraz imię, nazwisko i adres rozliczeniowy na swoim koncie Google i na tym urządzeniu.</translation>
<translation id="17513872634828108">Otwarte karty</translation>
<translation id="1753706481035618306">Numer strony</translation>
<translation id="1763864636252898013">Ten serwer nie mógł udowodnić, że należy do <ph name="DOMAIN" />. Jego certyfikat bezpieczeństwa nie jest zaufany w systemie operacyjnym tego urządzenia. Może to być spowodowane błędną konfiguracją lub przechwyceniem połączenia przez atakującego.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Tutaj pojawiają się otwarte karty</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Imię i nazwisko posiadacza karty</translation>
-<translation id="1806541873155184440">Dodano: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Nieprawidłowe żądanie lub jego parametry</translation>
<translation id="1826516787628120939">Sprawdzam</translation>
<translation id="1834321415901700177">Ta strona zawiera szkodliwe programy</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 podpowiedź}few{# podpowiedzi}many{# podpowiedzi}other{# podpowiedzi}}</translation>
<translation id="2079545284768500474">Cofnij</translation>
<translation id="20817612488360358">Skonfigurowano używanie systemowych ustawień proxy, ale podano też jawną konfigurację proxy.</translation>
-<translation id="2084558088529668945">Wpisałeś swoje hasło na stronie, którą nie zarządza <ph name="ORG_NAME" />. Aby chronić konto, nie używaj swojego hasła w innych aplikacjach ani na innych stronach.</translation>
<translation id="2091887806945687916">Dźwięk</translation>
<translation id="2094505752054353250">Niewłaściwa domena</translation>
<translation id="2096368010154057602">Departament</translation>
+<translation id="2102134110707549001">Zaproponuj silne hasło…</translation>
<translation id="2108755909498034140">Uruchom ponownie komputer</translation>
<translation id="2113977810652731515">Karta</translation>
<translation id="2114841414352855701">Ignorowana, ponieważ jest zastąpiona przez <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Błąd HTTP</translation>
<translation id="2270484714375784793">Numer telefonu</translation>
<translation id="2292556288342944218">Masz zablokowany dostęp do internetu</translation>
-<translation id="230155334948463882">Nowa karta?</translation>
<translation id="2316887270356262533">Zwolni się mniej niż 1 MB. Podczas następnej wizyty niektóre strony mogą ładować się wolniej.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> wymaga nazwy użytkownika i hasła.</translation>
<translation id="2317583587496011522">Karty debetowe są akceptowane.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Akceptowane karty</translation>
<translation id="2702801445560668637">Do przeczytania</translation>
<translation id="2704283930420550640">Wartość nie pasuje do formatu.</translation>
-<translation id="2704951214193499422">Chromium nie może obecnie potwierdzić karty. Spróbuj ponownie później.</translation>
<translation id="2705137772291741111">Zapisana w pamięci podręcznej kopia tej strony jest uszkodzona.</translation>
<translation id="2709516037105925701">Autouzupełnianie</translation>
<translation id="2710942282213947212">Oprogramowanie na Twoim komputerze uniemożliwia Chromium bezpieczne połączenie się z internetem</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Metoda wysyłki</translation>
<translation id="277499241957683684">Brak rekordu urządzenia</translation>
<translation id="2781030394888168909">Eksportuj (MacOS)</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Połączenie zostało zresetowane.</translation>
<translation id="2788784517760473862">Akceptowane karty kredytowe</translation>
<translation id="2794233252405721443">Strona zablokowana</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Możesz wyłączyć dowolne serwery proxy skonfigurowane dla połączenia na stronie ustawień.</translation>
<translation id="2955913368246107853">Zamknij pasek wyszukiwania</translation>
<translation id="2958431318199492670">Konfiguracja sieci jest niezgodna ze standardem ONC. Jej fragmenty mogły nie zostać zaimportowane.</translation>
-<translation id="2966678944701946121">Data ważności: <ph name="EXPIRATION_DATE_ABBR" />, dodano: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Aby urządzenie nawiązało bezpieczne połączenie, jego zegar musi wskazywać prawidłową godzinę. Jest to wymagane, bo certyfikaty używane do identyfikacji stron internetowych są ważne tylko przez określony czas. Zegar urządzenia jest ustawiony nieprawidłowo, więc Google Chrome nie może zweryfikować tych certyfikatów.</translation>
<translation id="2972581237482394796">&amp;Ponów</translation>
<translation id="2977665033722899841">Obecnie wybrany wiersz to <ph name="ROW_NAME" />. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Nieprawidłowy typ zasady</translation>
<translation id="3037605927509011580">Kurza twarz!</translation>
<translation id="3041612393474885105">Informacje o certyfikacie</translation>
-<translation id="3063697135517575841">Chrome nie może obecnie potwierdzić karty. Spróbuj ponownie później.</translation>
<translation id="3064966200440839136">Opuszczasz tryb incognito, by zapłacić w aplikacji zewnętrznej. Kontynuować?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Brak}=1{1 hasło}few{# hasła}many{# haseł}other{# hasła}}</translation>
<translation id="3096100844101284527">Dodaj adres odbioru</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Twoje dane zostały zaszyfrowane z użyciem hasła synchronizacji w dniu <ph name="TIME" />. Wpisz je, by rozpocząć synchronizację.</translation>
<translation id="3320021301628644560">Dodaj adres rozliczeniowy</translation>
<translation id="3338095232262050444">Bezpieczna</translation>
-<translation id="3340978935015468852">ustawienia</translation>
<translation id="3345135638360864351">Nie udało się wysłać żądania dostępu do strony do <ph name="NAME" />. Spróbuj ponownie.</translation>
<translation id="3355823806454867987">Zmień ustawienia serwera proxy...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />nie będzie zapisywać<ph name="END_EMPHASIS" />:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Certyfikat serwera nie jest zaufany.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Co najmniej 1 element na synchronizowanych urządzeniach}=1{1 element (i więcej na synchronizowanych urządzeniach)}few{# elementy (i więcej na synchronizowanych urządzeniach)}many{# elementów (i więcej na synchronizowanych urządzeniach)}other{# elementu (i więcej na synchronizowanych urządzeniach)}}</translation>
<translation id="3539171420378717834">Zachowaj kopię tej karty na urządzeniu</translation>
-<translation id="3542684924769048008">Używaj hasła dla:</translation>
<translation id="3549644494707163724">Szyfruj wszystkie synchronizowane dane za pomocą hasła synchronizacji</translation>
<translation id="3556433843310711081">Twój menedżer może ją dla Ciebie odblokować</translation>
<translation id="3566021033012934673">Połączenie nie jest prywatne</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Nieprawidłowy podpis weryfikujący</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{Jeszcze <ph name="ITEM_COUNT" /> element}few{Jeszcze <ph name="ITEM_COUNT" /> elementy}many{Jeszcze <ph name="ITEM_COUNT" /> elementów}other{Jeszcze <ph name="ITEM_COUNT" /> elementu}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome zaleca zresetowanie hasła, którego używasz w: <ph name="ORG_NAME" />, jeśli zostało użyte na innej stronie.</translation>
<translation id="4196861286325780578">&amp;Ponów przeniesienie</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Sprawdź konfigurację zapory sieciowej i oprogramowania antywirusowego<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Awarie</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Wprowadzone zmiany mogą nie zostać zapisane.</translation>
<translation id="4258748452823770588">Nieprawidłowy podpis</translation>
<translation id="4265872034478892965">Dozwolone przez administratora</translation>
-<translation id="4269787794583293679">(Brak nazwy użytkownika)</translation>
<translation id="4275830172053184480">Zrestartuj urządzenie</translation>
<translation id="4277028893293644418">Resetuj hasło</translation>
<translation id="4280429058323657511">, ważna do: <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Pop-upy i przekierowania</translation>
<translation id="443673843213245140">Korzystanie z serwera proxy jest wyłączone, ale podano konfigurację proxy.</translation>
<translation id="445100540951337728">Akceptowane karty debetowe</translation>
+<translation id="4472575034687746823">Rozpocznij</translation>
<translation id="4506176782989081258">Błąd sprawdzania poprawności: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Skontaktuj się z administratorem systemu</translation>
<translation id="450710068430902550">Udostępnianie administratorowi</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Odśwież zasady</translation>
<translation id="4728558894243024398">Platforma</translation>
<translation id="4736825316280949806">Uruchom ponownie Chromium</translation>
+<translation id="4742407542027196863">Zarządzaj hasłami…</translation>
<translation id="4744603770635761495">Ścieżka pliku wykonywalnego</translation>
-<translation id="4749685221585524849">Ostatnio używana: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Informacje, które wysyłasz tej witrynie (na przykład hasła lub numery kart kredytowych), pozostają prywatne.</translation>
<translation id="4756388243121344051">&amp;Historia</translation>
<translation id="4758311279753947758">Dodaj dane kontaktowe</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Widok</translation>
<translation id="4854362297993841467">Ta metoda dostawy jest niedostępna. Wybierz inną.</translation>
<translation id="4858792381671956233">Zapytałeś rodziców, czy możesz wejść na tę stronę</translation>
+<translation id="4876305945144899064">Brak nazwy użytkownika</translation>
<translation id="4880827082731008257">Przeszukaj historię</translation>
<translation id="4881695831933465202">Otwórz</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Stan</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="5098332213681597508">Ta nazwa pochodzi z Twojego konta Google.</translation>
<translation id="5115563688576182185">(64-bitowa)</translation>
<translation id="5121084798328133320">Po potwierdzeniu szczegółowe dane karty z Twojego konta Google Payments zostaną udostępnione tej stronie.</translation>
<translation id="5128122789703661928">Nie możesz usunąć tej sesji, bo jej nazwa jest nieprawidłowa.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Metoda wysyłki</translation>
<translation id="5355557959165512791">Nie możesz teraz otworzyć strony <ph name="SITE" />, bo jej certyfikat został unieważniony. Błędy sieci i ataki są zazwyczaj przejściowe, więc prawdopodobnie strona będzie wkrótce działać.</translation>
<translation id="536296301121032821">Zapisanie ustawień zasady nie powiodło się</translation>
+<translation id="5371425731340848620">Zaktualizuj dane karty</translation>
<translation id="5377026284221673050">„Twój zegar się spóźnia” lub „Twój zegar się śpieszy” lub „&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;”</translation>
<translation id="5386426401304769735">Łańcuch certyfikatów tej witryny zawiera certyfikat podpisany za pomocą SHA-1.</translation>
-<translation id="5402410679244714488">Data ważności: <ph name="EXPIRATION_DATE_ABBR" />, ostatnio używana ponad roku temu</translation>
+<translation id="5387961145478138773">Zapewnij sobie szybki dostęp do ulubionych aplikacji Google</translation>
<translation id="540969355065856584">Ten serwer nie może udowodnić, że należy do domeny <ph name="DOMAIN" />. Jego certyfikat bezpieczeństwa nie jest obecnie ważny. Może to być spowodowane nieprawidłową konfiguracją lub przechwyceniem połączenia przez atakującego.</translation>
<translation id="5421136146218899937">Wyczyść dane przeglądania...</translation>
<translation id="5430298929874300616">Usuń zakładkę</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">E-mail</translation>
+<translation id="5666899935841546222">Czy chcesz mieć wszystkie swoje karty w jednym miejscu?</translation>
<translation id="5675650730144413517">Ta strona nie działa</translation>
<translation id="5685654322157854305">Dodaj adres wysyłki</translation>
<translation id="5689199277474810259">Eksportuj w formacie JSON</translation>
<translation id="5689516760719285838">Lokalizacja</translation>
<translation id="570530837424789914">Zarządzaj…</translation>
+<translation id="57094364128775171">Zaproponuj silne hasło…</translation>
<translation id="5710435578057952990">Tożsamość witryny nie została zweryfikowana.</translation>
<translation id="5719499550583120431">Karty przedpłacone są akceptowane.</translation>
<translation id="5720705177508910913">Bieżący użytkownik</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Ta witryna jest nieosiągalna</translation>
<translation id="5869522115854928033">Zapisane hasła</translation>
<translation id="5893752035575986141">Karty kredytowe są akceptowane.</translation>
-<translation id="5898382028489516745">Chromium zaleca zresetowanie hasła, którego używasz w: <ph name="ORG_NAME" />, jeśli zostało użyte na innej stronie.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (zsynchronizowane)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{W użyciu: 1}few{W użyciu: #}many{W użyciu: #}other{W użyciu: #}}</translation>
<translation id="5939518447894949180">Resetuj</translation>
-<translation id="5959728338436674663">Automatycznie wysyłaj do Google niektóre <ph name="BEGIN_WHITEPAPER_LINK" />informacje o systemie i część zawartości stron<ph name="END_WHITEPAPER_LINK" />, by pomóc w wykrywaniu niebezpiecznych aplikacji i witryn. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Edytuj dane kontaktowe</translation>
<translation id="5967867314010545767">Usuń z historii</translation>
<translation id="5975083100439434680">Pomniejsz</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Kod pocztowy</translation>
<translation id="6290238015253830360">Tutaj wyświetlają się sugerowane artykuły</translation>
<translation id="6305205051461490394">Strona <ph name="URL" /> jest nieosiągalna.</translation>
-<translation id="6319915415804115995">Ostatnio używana ponad rok temu</translation>
<translation id="6321917430147971392">Sprawdź ustawienia DNS</translation>
<translation id="6328639280570009161">Wyłącz przewidywanie działań sieciowych</translation>
<translation id="6328786501058569169">Ta strona wprowadza w błąd</translation>
<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="6342069812937806050">Przed momentem</translation>
<translation id="6355080345576803305">Zastąpienie sesji publicznej</translation>
<translation id="6358450015545214790">Co to oznacza?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 inna podpowiedź}few{# inne podpowiedzi}many{# innych podpowiedzi}other{# innej podpowiedzi}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Ponów usunięcie</translation>
<translation id="6534179046333460208">Sugestie dotyczące internetu rzeczy</translation>
<translation id="6550675742724504774">Opcje</translation>
-<translation id="6556915248009097796">Data ważności: <ph name="EXPIRATION_DATE_ABBR" />, ostatnio używana: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Twój menedżer jeszcze na to nie zezwolił</translation>
<translation id="6569060085658103619">Przeglądasz stronę rozszerzenia</translation>
<translation id="6596325263575161958">Opcje szyfrowania</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Próbujesz wejść na <ph name="DOMAIN" />, ale serwer przedstawił certyfikat podpisany słabym algorytmem (takim jak SHA-1). Oznacza to, że dane uwierzytelniające podane przez serwer mogły zostać sfałszowane, a serwer może nie być tym, którego oczekujesz (możliwe, że komunikujesz się z intruzem).</translation>
<translation id="6831043979455480757">Tłumacz</translation>
<translation id="6839929833149231406">Dzielnica</translation>
+<translation id="6852204201400771460">Załadować ponownie aplikację?</translation>
+<translation id="6865412394715372076">Nie można teraz zweryfikować karty</translation>
<translation id="6874604403660855544">&amp;Ponów dodanie</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Ten poziom zasad nie jest obsługiwany.</translation>
<translation id="6895330447102777224">Karta została potwierdzona</translation>
<translation id="6897140037006041989">Klient</translation>
+<translation id="6903319715792422884">Pomóż w ulepszaniu Bezpiecznego przeglądania, wysyłając do Google pewne <ph name="BEGIN_WHITEPAPER_LINK" />informacje o systemie i część zawartości stron<ph name="END_WHITEPAPER_LINK" />. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Użytkownik:</translation>
+<translation id="6944692733090228304">Wpisałeś swoje hasło na stronie, którą nie zarządza <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Aby chronić konto, nie używaj swojego hasła w innych aplikacjach ani witrynach.</translation>
<translation id="6945221475159498467">Wybierz</translation>
<translation id="6948701128805548767">Aby zobaczyć metody odbioru oraz wymagania, wybierz adres</translation>
<translation id="6949872517221025916">Resetuj hasło</translation>
+<translation id="6950684638814147129">Błąd podczas przetwarzania wartości JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Certyfikat serwera wydaje się sfałszowany.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Urządzenie</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Kliknij &lt;strong&gt;Zastosuj&lt;/strong&gt;, a potem kliknij &lt;strong&gt;OK&lt;/strong&gt;.
&lt;li&gt;W &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Centrum pomocy Chrome&lt;/a&gt; dowiesz się, jak trwale usunąć to oprogramowanie z komputera.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Zarządzaj hasłami…</translation>
<translation id="7419106976560586862">Ścieżka profilu</translation>
<translation id="7437289804838430631">Dodaj dane kontaktowe</translation>
<translation id="7441627299479586546">Nieprawidłowy podmiot zasady</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Dowiedz się więcej<ph name="END_LINK" /> o tym problemie.</translation>
<translation id="7455133967321480974">Użyj globalnej wartości domyślnej (Blokuj)</translation>
<translation id="7460163899615895653">W tym miejscu pojawią się Twoje ostatnie karty z innych urządzeń</translation>
-<translation id="7469372306589899959">Potwierdzam kartę</translation>
<translation id="7473891865547856676">Nie, dziękuję</translation>
<translation id="7481312909269577407">Dalej</translation>
<translation id="7485870689360869515">Nie znaleziono danych.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Tłumaczenie nie powiodło się z powodu problemu z połączeniem sieciowym.</translation>
<translation id="8311129316111205805">Wczytaj sesję</translation>
<translation id="8332188693563227489">Odmowa dostępu do <ph name="HOST_NAME" /></translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Jeśli rozumiesz zagrożenie, możesz <ph name="BEGIN_LINK" />wejść na tę stronę<ph name="END_LINK" />, zanim szkodliwe programy zostaną usunięte.</translation>
<translation id="8349305172487531364">Pasek zakładek</translation>
<translation id="8363502534493474904">Wyłącz tryb samolotowy</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222">Serwer <ph name="HOST_NAME" /> potrzebował zbyt wiele czasu na odpowiedź.</translation>
<translation id="8503559462189395349">Hasła w Chrome</translation>
<translation id="8503813439785031346">Nazwa użytkownika</translation>
+<translation id="8508648098325802031">Ikona wyszukiwania</translation>
<translation id="8543181531796978784">Możesz <ph name="BEGIN_ERROR_LINK" />zgłosić problem z wykrywaniem<ph name="END_ERROR_LINK" /> lub – jeśli rozumiesz zagrożenie – <ph name="BEGIN_LINK" />wejść na tę niebezpieczną stronę<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Masz pytania? Skontaktuj się z osobą, która nadzoruje Twój profil.</translation>
<translation id="8553075262323480129">Tłumaczenie nie powiodło się, ponieważ nie można określić języka strony.</translation>
<translation id="8557066899867184262">Kod CVC znajduje się na odwrocie karty.</translation>
<translation id="8559762987265718583">Nie można nawiązać prywatnego połączenia z <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, ponieważ data i godzina (<ph name="DATE_AND_TIME" />) ustawione na urządzeniu są nieprawidłowe.</translation>
+<translation id="8564985650692024650">Chromium zaleca zresetowanie hasła, którego używasz w: <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, jeśli zostało użyte na innej stronie.</translation>
<translation id="8571890674111243710">Trwa tłumaczenie strony na język: <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Dodaj numer telefonu</translation>
<translation id="859285277496340001">Certyfikat nie określa mechanizmu do sprawdzania, czy został on unieważniony.</translation>
+<translation id="860043288473659153">Imię i nazwisko posiadacza karty</translation>
<translation id="8620436878122366504">Twoi rodzice jeszcze na to nie zezwolili</translation>
<translation id="8625384913736129811">Zapisz tę kartę na tym urządzeniu</translation>
-<translation id="8639963783467694461">Ustawienia autouzupełniania</translation>
+<translation id="8663226718884576429">Podsumowanie zamówienia, <ph name="TOTAL_LABEL" />, Szczegółowe informacje</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, odpowiedź, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Połączenie z witryną <ph name="DOMAIN" /> nie jest szyfrowane.</translation>
<translation id="8718314106902482036">Płatność nie została zrealizowana</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, sugestia wyszukiwania</translation>
<translation id="8725066075913043281">Spróbuj ponownie</translation>
<translation id="8728672262656704056">Jesteś w trybie incognito</translation>
<translation id="8730621377337864115">Gotowe</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Zakładki</translation>
<translation id="883848425547221593">Inne zakładki</translation>
<translation id="884264119367021077">Adres wysyłki</translation>
+<translation id="8846319957959474018">Łatwo otwieraj aplikacje dzięki zakładkom</translation>
<translation id="884923133447025588">Nie znaleziono mechanizmu unieważniania.</translation>
<translation id="885730110891505394">Udostępnianie Google</translation>
+<translation id="8858065207712248076">Chrome zaleca zresetowanie hasła, którego używasz w: <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, jeśli zostało użyte na innej stronie.</translation>
<translation id="8866481888320382733">Podczas przetwarzania ustawień zasady wystąpił błąd</translation>
<translation id="8870413625673593573">Ostatnio zamknięte</translation>
<translation id="8874824191258364635">Wpisz prawidłowy numer karty</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> zazwyczaj używa szyfrowania do ochrony Twoich informacji. Gdy tym razem przeglądarka Chromium próbowała połączyć się ze stroną <ph name="SITE" />, odesłała ona nietypowe i nieprawidłowe dane logowania. Może się tak zdarzyć, gdy pod stronę <ph name="SITE" /> podszywa się osoba atakująca albo gdy ekran logowania do sieci Wi-Fi przerwie połączenie. Twoje informacje są nadal bezpieczne, bo połączenie w Chromium zakończyło się przed wymianą jakichkolwiek danych.</translation>
<translation id="9106062320799175032">Dodaj adres rozliczeniowy</translation>
<translation id="910908805481542201">Pomóż mi to naprawić</translation>
+<translation id="9114524666733003316">Sprawdzam kartę…</translation>
<translation id="9128870381267983090">Połącz z siecią</translation>
<translation id="9137013805542155359">Pokaż tekst oryginalny</translation>
<translation id="9137248913990643158">Aby użyć tej aplikacji, najpierw uruchom Chrome i zaloguj się w nim.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">Zezwalaj stronom na sprawdzanie, czy masz zapisane formy płatności</translation>
<translation id="9169664750068251925">Zawsze blokuj w tej witrynie</translation>
<translation id="9170848237812810038">&amp;Cofnij</translation>
+<translation id="9171296965991013597">Zamknąć aplikację?</translation>
<translation id="917450738466192189">Certyfikat serwera jest nieprawidłowy.</translation>
<translation id="9183425211371246419">Serwer <ph name="HOST_NAME" /> używa nieobsługiwanego protokołu.</translation>
<translation id="9205078245616868884">Twoje dane są szyfrowane z użyciem hasła synchronizacji. Wpisz je, by rozpocząć synchronizację.</translation>
diff --git a/chromium/components/strings/components_strings_pt-BR.xtb b/chromium/components/strings/components_strings_pt-BR.xtb
index aed9dde45ae..b0483523624 100644
--- a/chromium/components/strings/components_strings_pt-BR.xtb
+++ b/chromium/components/strings/components_strings_pt-BR.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Ocultar valor</translation>
<translation id="1228893227497259893">Identificador de entidade incorreto</translation>
<translation id="1232569758102978740">Sem título</translation>
+<translation id="1250759482327835220">Para agilizar o pagamento na próxima vez, salve o cartão, seu nome e o endereço de faturamento na sua Conta do Google.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (sincronizados)</translation>
<translation id="1256368399071562588">&lt;p&gt;Se você tentar visitar um site e ele não abrir, primeiro tente corrigir o erro com estas etapas de solução de problemas:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Ajuste a data e hora na seção &lt;strong&gt;Geral&lt;/strong&gt; do aplicativo &lt;strong&gt;Config.&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Algo deu errado ao exibir esta página da Web.</translation>
-<translation id="1590457302292452960">Gerar uma senha forte...</translation>
<translation id="1592005682883173041">Acesso a dados locais</translation>
<translation id="1594030484168838125">Escolher</translation>
<translation id="1620510694547887537">Câmera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Tente entrar em contato com o administrador do sistema.</translation>
<translation id="1740951997222943430">Informe um mês de validade válido</translation>
+<translation id="1743520634839655729">Para agilizar o pagamento na próxima vez, salve o cartão, seu nome e o endereço de faturamento na sua Conta do Google e neste dispositivo.</translation>
<translation id="17513872634828108">Guias abertas</translation>
<translation id="1753706481035618306">Numero da página</translation>
<translation id="1763864636252898013">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O certificado de segurança não é confiável para o sistema operacional do seu dispositivo. Isso pode ser causado por uma configuração incorreta ou pela interceptação da sua conexão por um invasor.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Suas guias abertas são exibidas aqui</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Nome do titular do cartão</translation>
-<translation id="1806541873155184440">Adicionado em: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Solicitação ou parâmetros de solicitação inválidos</translation>
<translation id="1826516787628120939">Em verificação</translation>
<translation id="1834321415901700177">Este site contém programas perigosos</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 sugestão}one{# sugestão}other{# sugestões}}</translation>
<translation id="2079545284768500474">Desfazer</translation>
<translation id="20817612488360358">As configurações de proxy do sistema são definidas para serem utilizadas, mas uma configuração explícita de proxy também foi especificada.</translation>
-<translation id="2084558088529668945">Você informou sua senha em um site que não é gerenciado por <ph name="ORG_NAME" />. Para proteger sua conta, não reutilize sua senha em outros apps e sites.</translation>
<translation id="2091887806945687916">Som</translation>
<translation id="2094505752054353250">Incompatibilidade de domínio</translation>
<translation id="2096368010154057602">Departamento</translation>
+<translation id="2102134110707549001">Sugerir senha forte…</translation>
<translation id="2108755909498034140">Reiniciar seu computador</translation>
<translation id="2113977810652731515">Cartão</translation>
<translation id="2114841414352855701">Ignorado porque foi substituído por <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Erro HTTP</translation>
<translation id="2270484714375784793">Número do telefone</translation>
<translation id="2292556288342944218">O seu acesso à Internet está bloqueado</translation>
-<translation id="230155334948463882">Novo cartão?</translation>
<translation id="2316887270356262533">Libera menos de 1 MB. O carregamento de alguns sites pode ficar mais lento no seu próximo acesso.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> exige um nome de usuário e uma senha.</translation>
<translation id="2317583587496011522">Cartões de débito são aceitos.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Cartões aceitos</translation>
<translation id="2702801445560668637">Lista de leitura</translation>
<translation id="2704283930420550640">O valor não corresponde ao formato.</translation>
-<translation id="2704951214193499422">Não foi possível confirmar seu cartão com o Chromium no momento. Tente novamente mais tarde.</translation>
<translation id="2705137772291741111">Não foi possível ler a cópia armazenada em cache deste site.</translation>
<translation id="2709516037105925701">Preenchimento automático</translation>
<translation id="2710942282213947212">Algum software no seu computador está impedindo o Chromium de se conectar com segurança à Web</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Método de envio</translation>
<translation id="277499241957683684">Registro de dispositivo não encontrado</translation>
<translation id="2781030394888168909">Exportar para MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">A conexão foi redefinida.</translation>
<translation id="2788784517760473862">Cartões de crédito aceitos</translation>
<translation id="2794233252405721443">Site bloqueado</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Na página "Configurações", você pode desativar quaisquer proxies configurados para uma conexão.</translation>
<translation id="2955913368246107853">Fechar barra de localização</translation>
<translation id="2958431318199492670">A configuração de rede não está de acordo com o padrão ONC. Partes da configuração podem não ser importadas.</translation>
-<translation id="2966678944701946121">Validade: <ph name="EXPIRATION_DATE_ABBR" />, data de adição: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Para estabelecer uma conexão segura, o relógio precisa estar configurado corretamente. Isso ocorre porque os certificados que os websites usam para se identificar são válidos apenas por períodos específicos. Como o relógio do seu dispositivo está incorreto, o Google Chrome não consegue verificar esses certificados.</translation>
<translation id="2972581237482394796">&amp;Refazer</translation>
<translation id="2977665033722899841">Selecionado no momento: <ph name="ROW_NAME" />. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Tipo de política incorreto</translation>
<translation id="3037605927509011580">Ah, não!</translation>
<translation id="3041612393474885105">Informações do certificado</translation>
-<translation id="3063697135517575841">Não foi possível confirmar seu cartão com o Chrome no momento. Tente novamente mais tarde.</translation>
<translation id="3064966200440839136">Saindo do modo de navegação anônima para pagar usando um aplicativo externo. Continuar?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Nenhuma}=1{1 senha}one{# senha}other{# senhas}}</translation>
<translation id="3096100844101284527">Adicionar endereço de retirada</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Seus dados foram criptografados com sua senha longa de sincronização no dia <ph name="TIME" />. Informe-a para começar a sincronização.</translation>
<translation id="3320021301628644560">Adicionar endereço de faturamento</translation>
<translation id="3338095232262050444">Seguro</translation>
-<translation id="3340978935015468852">configurações</translation>
<translation id="3345135638360864351">Não foi possível enviar sua solicitação a <ph name="NAME" /> para acessar este site. Tente novamente.</translation>
<translation id="3355823806454867987">Alterar configurações de proxy...</translation>
<translation id="3361596688432910856">O Chrome <ph name="BEGIN_EMPHASIS" />não salvará<ph name="END_EMPHASIS" /> as seguintes informações:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">O certificado do servidor não é confiável.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Pelo menos 1 item em dispositivos sincronizados}=1{1 item (e mais em dispositivos sincronizados)}one{# item (e mais em dispositivos sincronizados)}other{# itens (e mais em dispositivos sincronizados)}}</translation>
<translation id="3539171420378717834">Manter uma cópia deste cartão neste dispositivo</translation>
-<translation id="3542684924769048008">Usar senha para:</translation>
<translation id="3549644494707163724">Criptografar todos os dados sincronizados com sua senha de sincronização</translation>
<translation id="3556433843310711081">Seu administrador pode desbloqueá-lo para você</translation>
<translation id="3566021033012934673">Sua conexão não é particular</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Assinatura de verificação inválida</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{Mais <ph name="ITEM_COUNT" /> item}one{Mais <ph name="ITEM_COUNT" /> item}other{Mais <ph name="ITEM_COUNT" /> itens}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" />: <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">O Chrome recomenda redefinir sua senha de <ph name="ORG_NAME" /> se você a reutilizou em outros sites.</translation>
<translation id="4196861286325780578">&amp;Refazer mover</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Verificar as configurações do antivírus e firewall<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Falhas</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">É possível que as alterações feitas não sejam salvas.</translation>
<translation id="4258748452823770588">Assinatura inválida</translation>
<translation id="4265872034478892965">Permitido pelo administrador</translation>
-<translation id="4269787794583293679">Sem nome de usuário</translation>
<translation id="4275830172053184480">Reiniciar seu dispositivo</translation>
<translation id="4277028893293644418">Redefinir senha</translation>
<translation id="4280429058323657511">, validade <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Pop-ups e redirecionamentos</translation>
<translation id="443673843213245140">O uso de um proxy está desativado, mas uma configuração explícita de proxy é especificada.</translation>
<translation id="445100540951337728">Cartões de débito aceitos</translation>
+<translation id="4472575034687746823">Primeiros passos</translation>
<translation id="4506176782989081258">Erro de validação: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Entrar em contato com o administrador do sistema</translation>
<translation id="450710068430902550">Compartilhar com o administrador</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Atualizar políticas</translation>
<translation id="4728558894243024398">Plataforma</translation>
<translation id="4736825316280949806">Reiniciar o Chromium</translation>
+<translation id="4742407542027196863">Gerenciar senhas…</translation>
<translation id="4744603770635761495">Caminho do executável</translation>
-<translation id="4749685221585524849">Usado pela última vez em: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Suas informações (por exemplo, senhas ou números de cartão de crédito) permanecem particulares quando são enviadas para esse site.</translation>
<translation id="4756388243121344051">&amp;Histórico</translation>
<translation id="4758311279753947758">Adicionar dados de contato</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Visualizar</translation>
<translation id="4854362297993841467">Esse método de entrega não está disponível. Tente um método diferente.</translation>
<translation id="4858792381671956233">Você perguntou aos seus responsáveis se pode visitar este site</translation>
+<translation id="4876305945144899064">Sem nome de usuário</translation>
<translation id="4880827082731008257">Histórico de pesquisa</translation>
<translation id="4881695831933465202">Abrir</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Estado</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="5098332213681597508">Este nome é da sua Conta do Google.</translation>
<translation id="5115563688576182185">64 bits</translation>
<translation id="5121084798328133320">Depois da confirmação, os detalhes do cartão da sua conta do Google Payments serão compartilhados com esse site.</translation>
<translation id="5128122789703661928">A sessão com este nome não é válida para exclusão.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Forma de envio</translation>
<translation id="5355557959165512791">Não é possível acessar <ph name="SITE" /> neste momento, porque o certificado dele foi revogado. Como os ataques e erros de rede são geralmente temporários, esta página provavelmente funcionará mais tarde.</translation>
<translation id="536296301121032821">Falha ao armazenar as configurações da política</translation>
+<translation id="5371425731340848620">Atualizar cartão</translation>
<translation id="5377026284221673050">"Seu relógio está atrasado", "Seu relógio está adiantado" ou "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">A cadeia de certificados desse site contém um certificado assinado usando SHA-1.</translation>
-<translation id="5402410679244714488">Validade: <ph name="EXPIRATION_DATE_ABBR" />, usado pela última vez há mais de um ano</translation>
+<translation id="5387961145478138773">Acesse rapidamente seus apps favoritos do Google</translation>
<translation id="540969355065856584">Este servidor não conseguiu provar que é <ph name="DOMAIN" />. O certificado de segurança dele não é válido no momento. Isso pode ser causado por uma configuração incorreta ou pela interceptação da sua conexão por um invasor.</translation>
<translation id="5421136146218899937">Limpar dados de navegação...</translation>
<translation id="5430298929874300616">Remover favorito</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">E-mail</translation>
+<translation id="5666899935841546222">Você quer manter todos os seus cartões em um só lugar?</translation>
<translation id="5675650730144413517">Esta página não está funcionando</translation>
<translation id="5685654322157854305">Adicionar endereço de entrega</translation>
<translation id="5689199277474810259">Exportar para JSON</translation>
<translation id="5689516760719285838">Local</translation>
<translation id="570530837424789914">Gerenciar…</translation>
+<translation id="57094364128775171">Sugerir senha forte…</translation>
<translation id="5710435578057952990">A identidade deste site não foi confirmada.</translation>
<translation id="5719499550583120431">Cartões pré-pagos são aceitos.</translation>
<translation id="5720705177508910913">Usuário atual</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Não é possível acessar esse site</translation>
<translation id="5869522115854928033">Senhas salvas</translation>
<translation id="5893752035575986141">Cartões de crédito são aceitos.</translation>
-<translation id="5898382028489516745">O Chromium recomenda redefinir sua senha de <ph name="ORG_NAME" /> se você a reutilizou em outros sites.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (sincronizado)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 em uso}one{# em uso}other{# em uso}}</translation>
<translation id="5939518447894949180">Redefinir</translation>
-<translation id="5959728338436674663">Enviar automaticamente <ph name="BEGIN_WHITEPAPER_LINK" />algumas informações do sistema e conteúdos de página<ph name="END_WHITEPAPER_LINK" /> ao Google para ajudar a detectar sites e apps perigosos. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Edite as Informações de Contato</translation>
<translation id="5967867314010545767">Remover do histórico</translation>
<translation id="5975083100439434680">Diminuir zoom</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">CEP</translation>
<translation id="6290238015253830360">Os artigos sugeridos aparecerão aqui</translation>
<translation id="6305205051461490394">Não é possível acessar <ph name="URL" />.</translation>
-<translation id="6319915415804115995">Usado pela última vez há mais de um ano</translation>
<translation id="6321917430147971392">Verifique suas configurações do DNS</translation>
<translation id="6328639280570009161">Tente desativar a previsão de rede</translation>
<translation id="6328786501058569169">Este site é enganoso</translation>
<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="6342069812937806050">Neste instante</translation>
<translation id="6355080345576803305">Modificação de sessão pública</translation>
<translation id="6358450015545214790">O que são essas informações?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 outra sugestão}one{# outra sugestão}other{# outras sugestões}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Refazer excluir</translation>
<translation id="6534179046333460208">Sugestões da Web física</translation>
<translation id="6550675742724504774">Opções</translation>
-<translation id="6556915248009097796">Validade: <ph name="EXPIRATION_DATE_ABBR" />, usado pela última vez em: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Seu administrador ainda não o aprovou</translation>
<translation id="6569060085658103619">Você está vendo uma página de extensões</translation>
<translation id="6596325263575161958">Opções de criptografia</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Você tentou acessar <ph name="DOMAIN" />, mas o servidor apresentou um certificado assinado com um algoritmo de assinatura fraco (como SHA-1). Isso significa que as credenciais de segurança apresentadas pelo servidor podem ter sido forjadas, e talvez o servidor não seja o esperado (talvez você esteja se comunicando com um invasor).</translation>
<translation id="6831043979455480757">Traduzir</translation>
<translation id="6839929833149231406">Área</translation>
+<translation id="6852204201400771460">Recarregar app?</translation>
+<translation id="6865412394715372076">Não é possível verificar este cartão no momento</translation>
<translation id="6874604403660855544">&amp;Refazer adicionar</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">O nível da política não é suportado.</translation>
<translation id="6895330447102777224">Seu cartão foi confirmado</translation>
<translation id="6897140037006041989">Agente do usuário</translation>
+<translation id="6903319715792422884">Ajudar a melhorar o recurso "Navegação segura" enviando algumas <ph name="BEGIN_WHITEPAPER_LINK" />informações do sistema e conteúdo da página<ph name="END_WHITEPAPER_LINK" /> para o Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Usuário:</translation>
+<translation id="6944692733090228304">Você inseriu sua senha em um site que não é gerenciado por <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Para proteger sua conta, não reutilize sua senha em outros apps e sites.</translation>
<translation id="6945221475159498467">Selecionar</translation>
<translation id="6948701128805548767">Para ver métodos e requisitos de retirada, selecione um endereço</translation>
<translation id="6949872517221025916">Redefinir senha</translation>
+<translation id="6950684638814147129">Erro ao analisar o valor JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">O certificado do servidor parece ser falsificado.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Dispositivo</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Clique em &lt;strong&gt;Aplicar&lt;/strong&gt; e depois em &lt;strong&gt;OK&lt;/strong&gt;
&lt;li&gt;Visite a &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Central de Ajuda do Chrome&lt;/a&gt; para saber como remover permanentemente o software do seu computador
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Gerenciar senhas…</translation>
<translation id="7419106976560586862">Caminho de perfil</translation>
<translation id="7437289804838430631">Adicionar Informações de Contato</translation>
<translation id="7441627299479586546">Assunto da política incorreto</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Saber mais<ph name="END_LINK" /> sobre esse problema.</translation>
<translation id="7455133967321480974">Usar padrão global (Bloquear)</translation>
<translation id="7460163899615895653">Suas guias recentes de outros dispositivos são exibidas aqui</translation>
-<translation id="7469372306589899959">Confirmando cartão</translation>
<translation id="7473891865547856676">Não, obrigado</translation>
<translation id="7481312909269577407">Avançar</translation>
<translation id="7485870689360869515">Nenhum dado encontrado</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">A tradução falhou devido a um problema com a conexão de rede.</translation>
<translation id="8311129316111205805">Carregar sessão</translation>
<translation id="8332188693563227489">O acesso a <ph name="HOST_NAME" /> foi negado</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Se você entende os riscos para sua segurança, pode <ph name="BEGIN_LINK" />visitar este site<ph name="END_LINK" /> antes de os programas nocivos serem removidos.</translation>
<translation id="8349305172487531364">Barra de favoritos</translation>
<translation id="8363502534493474904">Desativar modo avião</translation>
@@ -1035,21 +1035,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> demorou muito para responder.</translation>
<translation id="8503559462189395349">Senhas do Chrome</translation>
<translation id="8503813439785031346">Nome de usuário</translation>
+<translation id="8508648098325802031">Ícone de pesquisa</translation>
<translation id="8543181531796978784">Você pode <ph name="BEGIN_ERROR_LINK" />denunciar um problema de detecção<ph name="END_ERROR_LINK" /> ou, se entende os riscos à sua segurança, <ph name="BEGIN_LINK" />acessar este site não seguro<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Dúvidas? Entre em contato com a pessoa que supervisiona seu perfil.</translation>
<translation id="8553075262323480129">A tradução falhou porque não foi possível determinar o idioma da página.</translation>
<translation id="8557066899867184262">O CVC está localizado atrás do seu cartão.</translation>
<translation id="8559762987265718583">Não é possível estabelecer uma conexão privada com <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, porque a data e a hora do seu dispositivo (<ph name="DATE_AND_TIME" />) estão incorretas.</translation>
+<translation id="8564985650692024650">O Chromium recomenda redefinir sua senha de <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> se você a reutilizou em outros sites.</translation>
<translation id="8571890674111243710">Traduzindo página para <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Ad. nº. telefone
</translation>
<translation id="859285277496340001">O certificado não especifica um mecanismo para verificar se ele foi revogado.</translation>
+<translation id="860043288473659153">Nome do titular do cartão</translation>
<translation id="8620436878122366504">Seus responsáveis ainda não o aprovaram</translation>
<translation id="8625384913736129811">Salvar este cartão neste dispositivo</translation>
-<translation id="8639963783467694461">Configurações de preenchimento automático</translation>
+<translation id="8663226718884576429">Resumo do pedido, <ph name="TOTAL_LABEL" />, Mais detalhes</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, resposta, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Sua conexão com <ph name="DOMAIN" /> não está criptografada.</translation>
<translation id="8718314106902482036">Pagamento não concluído</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, sugestão de pesquisa</translation>
<translation id="8725066075913043281">Tentar novamente</translation>
<translation id="8728672262656704056">Você está navegando sem deixar rastros</translation>
<translation id="8730621377337864115">Concluído</translation>
@@ -1065,8 +1069,10 @@
<translation id="8820817407110198400">Favoritos</translation>
<translation id="883848425547221593">Outros favoritos</translation>
<translation id="884264119367021077">Endereço de entrega</translation>
+<translation id="8846319957959474018">Abra apps facilmente com os favoritos</translation>
<translation id="884923133447025588">Nenhum mecanismo de revogação encontrado.</translation>
<translation id="885730110891505394">Compartilhar com o Google</translation>
+<translation id="8858065207712248076">O Chrome recomenda redefinir sua senha de <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> se você a reutilizou em outros sites.</translation>
<translation id="8866481888320382733">Configurações da política de análise de erros</translation>
<translation id="8870413625673593573">Recentemente fechadas</translation>
<translation id="8874824191258364635">Informe um número de cartão válido</translation>
@@ -1106,6 +1112,7 @@
incomuns e incorretas. Isso pode acontecer quando um invasor está fingindo ser <ph name="SITE" /> ou quando uma tela de login por Wi-Fi interrompeu a conexão. Suas informações ainda estão protegidas, porque o Chromium interrompeu a conexão antes que os dados fossem trocados.</translation>
<translation id="9106062320799175032">Adicione um Endereço de Faturamento</translation>
<translation id="910908805481542201">Ajude-me a corrigir isso</translation>
+<translation id="9114524666733003316">Confirmando cartão…</translation>
<translation id="9128870381267983090">Conectar-se à rede</translation>
<translation id="9137013805542155359">Mostrar original</translation>
<translation id="9137248913990643158">Inicie e faça login no Chrome antes de usar este app.</translation>
@@ -1116,6 +1123,7 @@ incomuns e incorretas. Isso pode acontecer quando um invasor está fingindo ser
<translation id="9168814207360376865">Permitir que os sites verifiquem se você tem formas de pagamento salvas</translation>
<translation id="9169664750068251925">Sempre bloquear neste site</translation>
<translation id="9170848237812810038">&amp;Desfazer</translation>
+<translation id="9171296965991013597">Sair do app?</translation>
<translation id="917450738466192189">O certificado do servidor é inválido.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> usa um protocolo incompatível.</translation>
<translation id="9205078245616868884">Seus dados são criptografados com sua senha longa de sincronização. Informe-a para começar a sincronização.</translation>
diff --git a/chromium/components/strings/components_strings_pt-PT.xtb b/chromium/components/strings/components_strings_pt-PT.xtb
index a278095e400..c07360984b1 100644
--- a/chromium/components/strings/components_strings_pt-PT.xtb
+++ b/chromium/components/strings/components_strings_pt-PT.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Esconder o valor</translation>
<translation id="1228893227497259893">Identificador de entidade errado</translation>
<translation id="1232569758102978740">Sem nome</translation>
+<translation id="1250759482327835220">Para pagar mais rapidamente da próxima vez, guarde o cartão, o nome e o endereço de faturação na sua Conta Google.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (sincronizados)</translation>
<translation id="1256368399071562588">&lt;p&gt;Se tentar aceder a um Website e este não abrir, primeiro, tente corrigir o erro com estes passos de resolução de problemas:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Ajuste a data e a hora na secção &lt;strong&gt;Geral&lt;/strong&gt; da aplicação &lt;strong&gt;Definições&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Ocorreu um erro ao apresentar esta página Web.</translation>
-<translation id="1590457302292452960">Gerar uma palavra-passe forte...</translation>
<translation id="1592005682883173041">Acesso aos dados locais</translation>
<translation id="1594030484168838125">Escolher</translation>
<translation id="1620510694547887537">Câmara</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Experimente contactar o gestor do sistema.</translation>
<translation id="1740951997222943430">Introduza um mês de expiração válido</translation>
+<translation id="1743520634839655729">Para pagar mais rapidamente da próxima vez, guarde o cartão, o nome e o endereço de faturação na sua Conta Google e neste dispositivo.</translation>
<translation id="17513872634828108">Separadores abertos</translation>
<translation id="1753706481035618306">Número de página</translation>
<translation id="1763864636252898013">Este servidor não conseguiu provar que é o domínio <ph name="DOMAIN" />; o sistema operativo do seu dispositivo 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>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Os separadores abertos aparecem aqui</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Nome do titular do cartão</translation>
-<translation id="1806541873155184440">Adicionado a <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Pedido ou parâmetros do pedido inválidos</translation>
<translation id="1826516787628120939">A verificar</translation>
<translation id="1834321415901700177">Este site contém programas prejudiciais</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 sugestão}other{# sugestões}}</translation>
<translation id="2079545284768500474">Anular</translation>
<translation id="20817612488360358">As definições de proxy do sistema estão definidas para serem utilizadas, mas também está especificada uma configuração de proxy explícita.</translation>
-<translation id="2084558088529668945">Introduziu a palavra-passe num site que não é gerido pelo serviço <ph name="ORG_NAME" />. Para proteger a sua conta, não reutilize a sua palavra-passe noutros sites e aplicações.</translation>
<translation id="2091887806945687916">Som</translation>
<translation id="2094505752054353250">Falta de correspondência de domínio</translation>
<translation id="2096368010154057602">Departamento</translation>
+<translation id="2102134110707549001">Sugerir palavra-passe forte…</translation>
<translation id="2108755909498034140">Reiniciar o computador</translation>
<translation id="2113977810652731515">Cartão</translation>
<translation id="2114841414352855701">Ignorada porque foi substituída por <ph name="POLICY_NAME" /> .</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Erro HTTP</translation>
<translation id="2270484714375784793">Número de telefone</translation>
<translation id="2292556288342944218">O acesso à Internet está bloqueado</translation>
-<translation id="230155334948463882">Tem um cartão novo?</translation>
<translation id="2316887270356262533">Liberta menos de 1 MB. É possível que alguns sites sejam carregados mais lentamente na sua próxima visita.</translation>
<translation id="2317259163369394535">O domínio <ph name="DOMAIN" /> requer um nome de utilizador e uma palavra-passe.</translation>
<translation id="2317583587496011522">Os cartões de débito são admitidos.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Cartões aceites</translation>
<translation id="2702801445560668637">Lista de leitura</translation>
<translation id="2704283930420550640">O valor não corresponde ao formato.</translation>
-<translation id="2704951214193499422">O Chromium não conseguiu confirmar o seu cartão neste momento. Tente novamente mais tarde.</translation>
<translation id="2705137772291741111">A cópia guardada (em cache) deste site era ilegível.</translation>
<translation id="2709516037105925701">Preenchimento automático</translation>
<translation id="2710942282213947212">Existe software no computador que está a impedir que o Chromium se ligue à Web em segurança</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Método de envio</translation>
<translation id="277499241957683684">Registo do dispositivo em falta</translation>
<translation id="2781030394888168909">Exportar para MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">A ligação foi reposta.</translation>
<translation id="2788784517760473862">Cartões de crédito admitidos</translation>
<translation id="2794233252405721443">Site bloqueado</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Pode desativar qualquer proxy configurado para uma ligação a partir da página de definições.</translation>
<translation id="2955913368246107853">Fechar barra de localização</translation>
<translation id="2958431318199492670">A configuração de rede não cumpre a norma ONC. Partes da configuração podem não ser importadas.</translation>
-<translation id="2966678944701946121">Exp.: <ph name="EXPIRATION_DATE_ABBR" />, adicionado a <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Para estabelecer uma ligação segura, o relógio tem de ser definido corretamente. Isto deve-se ao facto de os certificados que os Sites utilizam para se identificarem serem apenas válidos para períodos de tempo específicos. Uma vez que o relógio do seu dispositivo está incorreto, o Google Chrome não consegue validar estes certificados.</translation>
<translation id="2972581237482394796">&amp;Repetir</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" /> selecionado atualmente. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Tipo de política incorreto</translation>
<translation id="3037605927509011580">Ah, bolas!!</translation>
<translation id="3041612393474885105">Informações do certificado</translation>
-<translation id="3063697135517575841">O Chrome não conseguiu confirmar o seu cartão neste momento. Tente novamente mais tarde.</translation>
<translation id="3064966200440839136">Está a sair do modo de navegação anónima para pagar através de uma aplicação externa. Pretende continuar?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Nenhuma}=1{1 palavra-passe}other{# palavras-passe}}</translation>
<translation id="3096100844101284527">Adicionar endereço de levantamento</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Os dados foram encriptados com a sua frase de acesso de sincronização em <ph name="TIME" />. Introduza-a para iniciar a sincronização.</translation>
<translation id="3320021301628644560">Adicionar endereço de faturação</translation>
<translation id="3338095232262050444">Seguro</translation>
-<translation id="3340978935015468852">definições</translation>
<translation id="3345135638360864351">Não é possível enviar o seu pedido de acesso a este site a <ph name="NAME" />. Tente novamente.</translation>
<translation id="3355823806454867987">Alterar definições de proxy...</translation>
<translation id="3361596688432910856">O Chrome <ph name="BEGIN_EMPHASIS" />não guardará<ph name="END_EMPHASIS" /> as seguintes informações:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">O certificado do servidor não é fidedigno.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Pelo menos 1 item em dispositivos sincronizados}=1{1 item (e mais em dispositivos sincronizados)}other{# itens (e mais em dispositivos sincronizados)}}</translation>
<translation id="3539171420378717834">Guardar uma cópia deste cartão neste dispositivo</translation>
-<translation id="3542684924769048008">Utilizar palavra-passe para:</translation>
<translation id="3549644494707163724">Encriptar todos os dados sincronizados com a sua própria frase de acesso de sincronização</translation>
<translation id="3556433843310711081">O seu gestor pode desbloqueá-lo</translation>
<translation id="3566021033012934673">A sua ligação não é privada</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Assinatura de verificação incorreta</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{Mais <ph name="ITEM_COUNT" /> item}other{Mais <ph name="ITEM_COUNT" /> itens}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">O Chrome recomenda a reposição da palavra-passe do serviço <ph name="ORG_NAME" /> se a tiver reutilizado noutros sites.</translation>
<translation id="4196861286325780578">&amp;Refazer movimentação</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Verificar as configurações da firewall e de antivírus<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Erros</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">É possível que as alterações não tenham sido efetuadas.</translation>
<translation id="4258748452823770588">Assinatura incorreta</translation>
<translation id="4265872034478892965">Permitida pelo gestor</translation>
-<translation id="4269787794583293679">(Sem nome de utilizador)</translation>
<translation id="4275830172053184480">Reiniciar o dispositivo</translation>
<translation id="4277028893293644418">Repor palavra-passe</translation>
<translation id="4280429058323657511">, exp. <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Pop-ups e redirecionamentos</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="445100540951337728">Cartões de débito admitidos</translation>
+<translation id="4472575034687746823">Começar</translation>
<translation id="4506176782989081258">Erro de validação: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Contactar o gestor do sistema</translation>
<translation id="450710068430902550">Partilha com o gestor</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Recarregar políticas</translation>
<translation id="4728558894243024398">Plataforma</translation>
<translation id="4736825316280949806">Reiniciar o Chromium</translation>
+<translation id="4742407542027196863">Gerir palavras-passe…</translation>
<translation id="4744603770635761495">Caminho do Executável</translation>
-<translation id="4749685221585524849">Última utilização a <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">As suas informações (por exemplo, palavras-passe ou números de cartões de crédito) são privadas quando são enviadas para este site.</translation>
<translation id="4756388243121344051">&amp;Histórico</translation>
<translation id="4758311279753947758">Adicionar informações de contacto</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Ver</translation>
<translation id="4854362297993841467">Este método de fornecimento não está disponível. Experimente um método diferente.</translation>
<translation id="4858792381671956233">Perguntaste aos teus pais se podes aceder a este site.</translation>
+<translation id="4876305945144899064">Sem nome de utilizador</translation>
<translation id="4880827082731008257">Pesquisar histórico</translation>
<translation id="4881695831933465202">Abrir</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Estado</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="5098332213681597508">Este nome é proveniente da sua Conta Google.</translation>
<translation id="5115563688576182185">(64 bits)</translation>
<translation id="5121084798328133320">Depois de confirmar, os detalhes do cartão da conta do Google Payments são partilhados com este site.</translation>
<translation id="5128122789703661928">A sessão com este nome não é válida para eliminação.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Método de envio</translation>
<translation id="5355557959165512791">Não pode visitar <ph name="SITE" /> neste momento, porque o certificado foi revogado. Os erros de rede e os ataques são geralmente temporários, pelo que esta página deverá funcionar mais tarde.</translation>
<translation id="536296301121032821">Falha ao armazenar as definições da política</translation>
+<translation id="5371425731340848620">Atualizar cartão</translation>
<translation id="5377026284221673050">"O seu relógio está atrasado" ou "O seu relógio está adiantado" ou "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">A cadeia de certificados inclui um certificado assinado através de SHA-1.</translation>
-<translation id="5402410679244714488">Exp.: <ph name="EXPIRATION_DATE_ABBR" />, última utilização há mais de um ano</translation>
+<translation id="5387961145478138773">Obtenha acesso rápido às suas aplicações Google favoritas.</translation>
<translation id="540969355065856584">Este servidor não conseguiu provar que é <ph name="DOMAIN" />; de momento, o respetivo certificado de segurança não é válido. Isto pode ser provocado por uma configuração incorreta ou por um atacante que esteja a intercetar a sua ligação.</translation>
<translation id="5421136146218899937">Limpar dados de navegação...</translation>
<translation id="5430298929874300616">Remover marcador</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">Email</translation>
+<translation id="5666899935841546222">Pretende ter todos os seus cartões num só local?</translation>
<translation id="5675650730144413517">Esta página não está a funcionar</translation>
<translation id="5685654322157854305">Adicionar morada para envio</translation>
<translation id="5689199277474810259">Exportar para JSON</translation>
<translation id="5689516760719285838">Local</translation>
<translation id="570530837424789914">Gerir…</translation>
+<translation id="57094364128775171">Sugerir palavra-passe forte…</translation>
<translation id="5710435578057952990">A identidade deste Web site não foi verificada.</translation>
<translation id="5719499550583120431">Os cartões pré-pagos são admitidos.</translation>
<translation id="5720705177508910913">Utilizador atual</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Não é possível aceder a este site</translation>
<translation id="5869522115854928033">Palavras-passe guardadas</translation>
<translation id="5893752035575986141">Os cartões de crédito são admitidos.</translation>
-<translation id="5898382028489516745">O Chromium recomenda a reposição da palavra-passe do serviço <ph name="ORG_NAME" /> se a tiver reutilizado noutros sites.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (sincronizados)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 em utilização}other{# em utilização}}</translation>
<translation id="5939518447894949180">Repor</translation>
-<translation id="5959728338436674663">Enviar automaticamente algumas <ph name="BEGIN_WHITEPAPER_LINK" />informações do sistema e conteúdos de páginas<ph name="END_WHITEPAPER_LINK" /> para a Google de modo a ajudar a detetar aplicações e sites perigosos. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Editar informações de contacto</translation>
<translation id="5967867314010545767">Remover do histórico</translation>
<translation id="5975083100439434680">Reduzir</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Código postal</translation>
<translation id="6290238015253830360">Os seus artigos sugeridos são apresentados aqui</translation>
<translation id="6305205051461490394"><ph name="URL" /> está inacessível.</translation>
-<translation id="6319915415804115995">Última utilização há mais de um ano</translation>
<translation id="6321917430147971392">Verificar as definições do DNS</translation>
<translation id="6328639280570009161">Tente desativar a previsão de rede</translation>
<translation id="6328786501058569169">Este site é fraudulento</translation>
<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="6342069812937806050">Mesmo agora</translation>
<translation id="6355080345576803305">Substituição da sessão pública</translation>
<translation id="6358450015545214790">O que significam?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 outra sugestão}other{# outras sugestões}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Refazer eliminação</translation>
<translation id="6534179046333460208">Sugestões da Web física</translation>
<translation id="6550675742724504774">Opções</translation>
-<translation id="6556915248009097796">Exp.: <ph name="EXPIRATION_DATE_ABBR" />, última utilização a <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">O seu gestor ainda não o aprovou</translation>
<translation id="6569060085658103619">Está a ver a página de uma extensão</translation>
<translation id="6596325263575161958">Opções de encriptação</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Tentou aceder a <ph name="DOMAIN" />, mas o servidor apresentou um certificado assinado utilizando um algoritmo de assinatura fraco (como SHA-1). Isto significa que as credenciais de segurança apresentadas pelo servidor podem ter sido falsificadas e que o servidor pode não ser aquele que pretende (pode estar a comunicar com um utilizador mal intencionado).</translation>
<translation id="6831043979455480757">Traduzir</translation>
<translation id="6839929833149231406">Área</translation>
+<translation id="6852204201400771460">Pretende atualizar a aplicação?</translation>
+<translation id="6865412394715372076">Não é possível validar este cartão neste momento.</translation>
<translation id="6874604403660855544">&amp;Refazer adição</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">O nível da política não é suportado.</translation>
<translation id="6895330447102777224">O seu cartão foi confirmado</translation>
<translation id="6897140037006041989">Agente do utilizador</translation>
+<translation id="6903319715792422884">Ajude a melhorar a Navegação segura ao enviar algumas <ph name="BEGIN_WHITEPAPER_LINK" />informações do sistema e conteúdo de páginas<ph name="END_WHITEPAPER_LINK" /> para a Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Utilizador:</translation>
+<translation id="6944692733090228304">Introduziu a palavra-passe num site que não é gerido pela <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Para proteger a conta, não reutilize a sua palavra-passe noutras aplicações e sites.</translation>
<translation id="6945221475159498467">Selecionar</translation>
<translation id="6948701128805548767">Para ver os métodos de recolha e os requisitos, selecione um endereço</translation>
<translation id="6949872517221025916">Repor palavra-passe</translation>
+<translation id="6950684638814147129">Erro ao analisar o valor JSON: <ph name="ERROR" />.</translation>
<translation id="6957887021205513506">O certificado do servidor parece ser uma falsificação.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Dispositivo</translation>
@@ -876,6 +875,7 @@
&lt;li&gt;Em &lt;strong&gt;Estado do serviço&lt;/strong&gt;, clique em &lt;strong&gt;Parar&lt;/strong&gt;.
&lt;li&gt;Clique em &lt;strong&gt;Aplicar&lt;/strong&gt; e, de seguida, em &lt;strong&gt;OK&lt;/strong&gt;.
&lt;li&gt;Visite o &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Centro de Ajuda do Chrome&lt;/a&gt; para saber como remover permanentemente o software do computador. &lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Gerir palavras-passe…</translation>
<translation id="7419106976560586862">Caminho do Perfil</translation>
<translation id="7437289804838430631">Adicionar informações de contacto</translation>
<translation id="7441627299479586546">Assunto da política incorreto</translation>
@@ -884,7 +884,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Saber mais<ph name="END_LINK" /> sobre este problema.</translation>
<translation id="7455133967321480974">Utilizar predefinição global (Bloquear)</translation>
<translation id="7460163899615895653">Os seus separadores recentes de outros dispositivos aparecem aqui</translation>
-<translation id="7469372306589899959">A confirmar o cartão...</translation>
<translation id="7473891865547856676">Não, obrigado</translation>
<translation id="7481312909269577407">Avançar</translation>
<translation id="7485870689360869515">Não foram encontrados dados.</translation>
@@ -1014,6 +1013,7 @@
<translation id="8308427013383895095">A tradução falhou devido a um problema com a ligação de rede.</translation>
<translation id="8311129316111205805">Carregar sessão</translation>
<translation id="8332188693563227489">O acesso a <ph name="HOST_NAME" /> foi recusado</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Se compreende os riscos para a sua segurança, pode <ph name="BEGIN_LINK" />visitar este site<ph name="END_LINK" /> antes de os programas prejudiciais terem sido removidos.</translation>
<translation id="8349305172487531364">Barra de marcadores</translation>
<translation id="8363502534493474904">Desativar o modo de avião</translation>
@@ -1034,21 +1034,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> demorou demasiado tempo a responder.</translation>
<translation id="8503559462189395349">Palavras-passe do Chrome</translation>
<translation id="8503813439785031346">Nome de utilizador</translation>
+<translation id="8508648098325802031">Ícone de pesquisa</translation>
<translation id="8543181531796978784">Pode <ph name="BEGIN_ERROR_LINK" />comunicar um problema de deteção<ph name="END_ERROR_LINK" /> ou, se compreende os riscos para a sua segurança, <ph name="BEGIN_LINK" />aceda a este site não seguro<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Tem dúvidas? Contacte a pessoa que supervisiona o seu perfil.</translation>
<translation id="8553075262323480129">A tradução falhou porque não foi possível determinar o idioma da página.</translation>
<translation id="8557066899867184262">O Código de Segurança/CVC está localizado no verso do cartão.</translation>
<translation id="8559762987265718583">Não é possível estabelecer uma ligação privada a <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, porque a data e a hora do seu dispositivo (<ph name="DATE_AND_TIME" />) estão incorretas.</translation>
+<translation id="8564985650692024650">O Chromium recomenda a reposição da palavra-passe da <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> se a tiver reutilizado noutros sites.</translation>
<translation id="8571890674111243710">A traduzir a página para <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Adic. n.º telef.
</translation>
<translation id="859285277496340001">O certificado não indica um mecanismo para verificar se foi ou não revogado.</translation>
+<translation id="860043288473659153">Nome do titular do cartão</translation>
<translation id="8620436878122366504">Os teus pais ainda não o aprovaram</translation>
<translation id="8625384913736129811">Guardar este cartão neste dispositivo</translation>
-<translation id="8639963783467694461">Definições de Preenchimento automático</translation>
+<translation id="8663226718884576429">Resumo da encomenda, <ph name="TOTAL_LABEL" />, mais detalhes</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, resposta, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">A sua ligação a <ph name="DOMAIN" /> não está encriptada.</translation>
<translation id="8718314106902482036">Pagamento não concluído</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, sugestão de pesquisa.</translation>
<translation id="8725066075913043281">Tentar novamente</translation>
<translation id="8728672262656704056">Está anónimo</translation>
<translation id="8730621377337864115">Concluído</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Marcadores</translation>
<translation id="883848425547221593">Outros marcadores</translation>
<translation id="884264119367021077">Endereço para envio</translation>
+<translation id="8846319957959474018">Abra as aplicações facilmente com marcadores.</translation>
<translation id="884923133447025588">Não foi encontrado qualquer mecanismo de revogação.</translation>
<translation id="885730110891505394">Partilha com a Google</translation>
+<translation id="8858065207712248076">O Chrome recomenda a reposição da palavra-passe da <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> se a tiver reutilizado noutros sites.</translation>
<translation id="8866481888320382733">Erro ao analisar as definições da política</translation>
<translation id="8870413625673593573">Fechadas recentemente</translation>
<translation id="8874824191258364635">Introduza um número de cartão válido</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690">Normalmente, o site <ph name="SITE" /> utiliza a encriptação para proteger as suas informações. Quando o Chromium tentou estabelecer ligação a <ph name="SITE" /> desta vez, o Website devolveu credenciais invulgares e incorretas. Isto pode acontecer quando um utilizador mal intencionado tenta simular ser <ph name="SITE" /> ou quando um ecrã de início de sessão Wi-Fi interrompe a ligação. As suas informações continuam seguras porque o Chromium interrompeu a ligação antes de qualquer troca de dados.</translation>
<translation id="9106062320799175032">Adicionar endereço de faturação</translation>
<translation id="910908805481542201">Ajudar-me a corrigir isto</translation>
+<translation id="9114524666733003316">A confirmar o cartão…</translation>
<translation id="9128870381267983090">Ligar à rede</translation>
<translation id="9137013805542155359">Mostrar original</translation>
<translation id="9137248913990643158">Comece e inicie sessão no Chrome antes de utilizar esta aplicação.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">Permitir que os sites verifiquem se tem métodos de pagamento guardados</translation>
<translation id="9169664750068251925">Bloquear sempre neste Website</translation>
<translation id="9170848237812810038">An&amp;ular</translation>
+<translation id="9171296965991013597">Pretende sair da aplicação?</translation>
<translation id="917450738466192189">O certificado do servidor é inválido.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> utiliza um protocolo não suportado.</translation>
<translation id="9205078245616868884">Os dados estão encriptados com a sua frase de acesso de sincronização. Introduza-a para iniciar a sincronização.</translation>
diff --git a/chromium/components/strings/components_strings_ro.xtb b/chromium/components/strings/components_strings_ro.xtb
index 1b17bd00d1c..feee44e1ef7 100644
--- a/chromium/components/strings/components_strings_ro.xtb
+++ b/chromium/components/strings/components_strings_ro.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Ascundeți valoarea</translation>
<translation id="1228893227497259893">Identificator greșit pentru entitate</translation>
<translation id="1232569758102978740">Fără titlu</translation>
+<translation id="1250759482327835220">Pentru a plăti mai rapid data viitoare, salvează cardul, numele și adresa de facturare în Contul Google.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (sincronizate)</translation>
<translation id="1256368399071562588">&lt;p&gt;Dacă încerci să vizitezi un site, iar acesta nu se deschide, mai întâi încearcă să remediezi eroarea urmând acești pași:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Corectează data și ora din secțiunea &lt;strong&gt;General&lt;/strong&gt; a aplicației &lt;strong&gt;Setări&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">A apărut o eroare la afișarea paginii web.</translation>
-<translation id="1590457302292452960">Generează o parolă puternică...</translation>
<translation id="1592005682883173041">Accesul la datele locale</translation>
<translation id="1594030484168838125">Alegeți</translation>
<translation id="1620510694547887537">Camera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Încearcă să contactezi administratorul sistemului.</translation>
<translation id="1740951997222943430">Introdu o lună de expirare validă</translation>
+<translation id="1743520634839655729">Pentru a plăti mai rapid data viitoare, salvează cardul, numele și adresa de facturare în Contul Google și pe acest dispozitiv.</translation>
<translation id="17513872634828108">File deschise</translation>
<translation id="1753706481035618306">Numărul paginii</translation>
<translation id="1763864636252898013">Acest server nu a putut dovedi că este <ph name="DOMAIN" />; sistemul de operare al dispozitivului 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>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Filele deschise sunt afișate aici</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Numele titularului cardului</translation>
-<translation id="1806541873155184440">Adăugat pe <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Solicitarea sau parametrii săi sunt greșiți</translation>
<translation id="1826516787628120939">Se verifică</translation>
<translation id="1834321415901700177">Acest site conține programe dăunătoare</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 sugestie}few{# sugestii}other{# de sugestii}}</translation>
<translation id="2079545284768500474">Anulează</translation>
<translation id="20817612488360358">Setările proxy de sistem sunt setate pentru a fi utilizate, dar o configurație explicită pentru proxy este, de asemenea, specificată.</translation>
-<translation id="2084558088529668945">Ai introdus parola pe un site care nu este gestionat de <ph name="ORG_NAME" />. Pentru a proteja contul, nu folosi aceeași parolă pentru alte aplicații și site-uri.</translation>
<translation id="2091887806945687916">Sunet</translation>
<translation id="2094505752054353250">Nepotrivire domeniu</translation>
<translation id="2096368010154057602">Departament</translation>
+<translation id="2102134110707549001">Sugerează o parolă puternică…</translation>
<translation id="2108755909498034140">repornește computerul;</translation>
<translation id="2113977810652731515">Card</translation>
<translation id="2114841414352855701">Politica este ignorată, deoarece a fost înlocuită de <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Eroare HTTP</translation>
<translation id="2270484714375784793">Număr telefon</translation>
<translation id="2292556288342944218">Accesul la internet este blocat</translation>
-<translation id="230155334948463882">Card nou?</translation>
<translation id="2316887270356262533">Eliberează mai puțin de 1 MB. Este posibil ca unele site-uri să se încarce mai lent la următoarea accesare.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> necesită un nume de utilizator și o parolă.</translation>
<translation id="2317583587496011522">Se acceptă carduri de debit.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Carduri acceptate</translation>
<translation id="2702801445560668637">Listă de lectură</translation>
<translation id="2704283930420550640">Valoarea nu se potrivește cu formatul.</translation>
-<translation id="2704951214193499422">Momentan, Chromium nu a putut confirma cardul. Încearcă din nou mai târziu.</translation>
<translation id="2705137772291741111">Copia salvată (în memoria cache) a acestui site nu a putut fi citită.</translation>
<translation id="2709516037105925701">Completare automată</translation>
<translation id="2710942282213947212">Pe computer există software care împiedică Chromium să se conecteze în siguranță la internet</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Metodă de expediere</translation>
<translation id="277499241957683684">Lipsește o înregistrare pentru gadget</translation>
<translation id="2781030394888168909">Exportă în format MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Conexiunea a fost resetată.</translation>
<translation id="2788784517760473862">Carduri de credit acceptate</translation>
<translation id="2794233252405721443">Site blocat</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Puteți să dezactivați serverele proxy configurate pentru o conexiune din pagina de setări.</translation>
<translation id="2955913368246107853">Închide Bara de căutare</translation>
<translation id="2958431318199492670">Configurația rețelei nu respectă standardul ONC. Este posibil ca anumite părți ale configurației să nu fie importate.</translation>
-<translation id="2966678944701946121">Expirat în <ph name="EXPIRATION_DATE_ABBR" />, adăugat pe <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Pentru a stabili o conexiune securizată, ceasul trebuie să fie setat corect, deoarece certificatele pe care site-urile le folosesc pentru a se identifica sunt valabile numai pentru anumite intervale de timp. Din moment ce ora de pe dispozitiv este incorectă, Google Chrome nu poate verifica aceste certificate.</translation>
<translation id="2972581237482394796">&amp;Repetă</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" /> selectat(ă). <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Tip de politică greșit</translation>
<translation id="3037605927509011580">Of, nu mai merge!</translation>
<translation id="3041612393474885105">Informații despre certificat</translation>
-<translation id="3063697135517575841">Momentan, Chrome nu a putut confirma cardul. Încearcă din nou mai târziu.</translation>
<translation id="3064966200440839136">Vei părăsi modul incognito pentru a plăti folosind o aplicație externă. Continui?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Niciuna}=1{1 parolă}few{# parole}other{# de parole}}</translation>
<translation id="3096100844101284527">Adaugă o adresă de preluare</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Datele au fost criptate cu expresia de acces pentru sincronizare la <ph name="TIME" />. Introdu-o pentru a începe sincronizarea.</translation>
<translation id="3320021301628644560">Adaugă o adresă de facturare</translation>
<translation id="3338095232262050444">Securizat</translation>
-<translation id="3340978935015468852">setări</translation>
<translation id="3345135638360864351">Solicitarea de a accesa acest site nu a putut fi trimisă la <ph name="NAME" />. Încearcă din nou.</translation>
<translation id="3355823806454867987">Modifica setările proxy...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />nu va salva<ph name="END_EMPHASIS" /> următoarele informații:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Certificatul serverului nu este de încredere.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Cel puțin 1 element pe dispozitivele sincronizate}=1{1 element (și mai multe pe dispozitivele sincronizate)}few{# elemente (și mai multe pe dispozitivele sincronizate)}other{# de elemente (și mai multe pe dispozitivele sincronizate)}}</translation>
<translation id="3539171420378717834">Păstrează o copie a cardului pe dispozitiv</translation>
-<translation id="3542684924769048008">Folosește parola pentru:</translation>
<translation id="3549644494707163724">Criptați toate datele sincronizate cu parola dvs. de acces pentru sincronizare</translation>
<translation id="3556433843310711081">Administratorul îl poate debloca pentru tine</translation>
<translation id="3566021033012934673">Conexiunea nu este privată</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Semnătură de verificare nevalidă</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{Încă <ph name="ITEM_COUNT" /> articol}few{Încă <ph name="ITEM_COUNT" /> articole}other{Încă <ph name="ITEM_COUNT" /> de articole}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome îți recomandă să resetezi parola pentru <ph name="ORG_NAME" /> dacă ai folosit-o și pe alte site-uri.</translation>
<translation id="4196861286325780578">&amp;Repetați mutarea</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />să verifici configurarea pentru firewall și antivirus;<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Blocări</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Este posibil ca modificările să nu se salveze.</translation>
<translation id="4258748452823770588">Semnătură greșită</translation>
<translation id="4265872034478892965">Permisă de administrator</translation>
-<translation id="4269787794583293679">(Niciun nume de utilizator)</translation>
<translation id="4275830172053184480">Reporniți gadgetul</translation>
<translation id="4277028893293644418">Resetează parola</translation>
<translation id="4280429058323657511">data expirării: <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Ferestre pop-up și redirecționări</translation>
<translation id="443673843213245140">Utilizarea unui proxy este dezactivată, dar o configurare proxy este specificată în mod explicit.</translation>
<translation id="445100540951337728">Carduri de debit acceptate</translation>
+<translation id="4472575034687746823">Începeți</translation>
<translation id="4506176782989081258">Eroare de validare: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">să contactezi administratorul sistemului;</translation>
<translation id="450710068430902550">Permiterea accesului pentru administrator</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Reîncărcați politicile</translation>
<translation id="4728558894243024398">Platformă</translation>
<translation id="4736825316280949806">repornește Chromium;</translation>
+<translation id="4742407542027196863">Gestionează parolele…</translation>
<translation id="4744603770635761495">Cale executabilă</translation>
-<translation id="4749685221585524849">Folosit ultima dată pe <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Informațiile tale (de exemplu, parolele și numerele cardurilor de credit) sunt private când sunt trimise la acest site.</translation>
<translation id="4756388243121344051">&amp;Istoric</translation>
<translation id="4758311279753947758">Adaugă informații de contact</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Afișează</translation>
<translation id="4854362297993841467">Această metodă de livrare nu este disponibilă. Încearcă altă metodă.</translation>
<translation id="4858792381671956233">Ți-ai întrebat părinții dacă poți accesa acest site</translation>
+<translation id="4876305945144899064">Niciun nume de utilizator</translation>
<translation id="4880827082731008257">Caută în istoric</translation>
<translation id="4881695831933465202">Deschide</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Stat</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="5098332213681597508">Acest nume provine din Contul tău Google.</translation>
<translation id="5115563688576182185">(64 de biți)</translation>
<translation id="5121084798328133320">După ce confirmi, acest site va avea acces la detaliile cardului tău din contul Google Payments.</translation>
<translation id="5128122789703661928">Sesiunea cu acest nume nu este validă pentru ștergere.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Metoda de expediere</translation>
<translation id="5355557959165512791">Nu poți accesa <ph name="SITE" /> acum, deoarece certificatul său a fost revocat. Erorile de rețea și atacurile sunt de obicei temporare și probabil că această pagină va funcționa mai târziu.</translation>
<translation id="536296301121032821">Setările pentru politică nu au putut fi stocate</translation>
+<translation id="5371425731340848620">Actualizează cardul</translation>
<translation id="5377026284221673050">„Ora este setată în trecut”, „Ora este setată în viitor” sau „&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;”</translation>
<translation id="5386426401304769735">Lanțul de certificate pentru acest site conține un certificat semnat folosind SHA-1.</translation>
-<translation id="5402410679244714488">Expirat în <ph name="EXPIRATION_DATE_ABBR" />, folosit ultima dată acum peste un an</translation>
+<translation id="5387961145478138773">Obține acces rapid la aplicațiile tale Google preferate</translation>
<translation id="540969355065856584">Acest server nu a putut dovedi că este <ph name="DOMAIN" />; momentan, certificatul de securitate nu este valid. Cauza poate fi o configurare greșită sau interceptarea conexiunii de un atacator.</translation>
<translation id="5421136146218899937">Șterge datele de navigare...</translation>
<translation id="5430298929874300616">Elimină marcajul</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">Adresă de e-mail</translation>
+<translation id="5666899935841546222">Vrei să ai toate cardurile într-un singur loc?</translation>
<translation id="5675650730144413517">Pagina nu funcționează</translation>
<translation id="5685654322157854305">Adaugă adresa de expediere</translation>
<translation id="5689199277474810259">Exportă în format JSON</translation>
<translation id="5689516760719285838">Locație</translation>
<translation id="570530837424789914">Gestionează...</translation>
+<translation id="57094364128775171">Sugerează o parolă puternică…</translation>
<translation id="5710435578057952990">Identitatea acestui site nu a fost confirmată.</translation>
<translation id="5719499550583120431">Se acceptă carduri preplătite.</translation>
<translation id="5720705177508910913">Utilizator curent</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Acest site nu poate fi accesat</translation>
<translation id="5869522115854928033">Parole salvate</translation>
<translation id="5893752035575986141">Se acceptă carduri de credit.</translation>
-<translation id="5898382028489516745">Chromium îți recomandă să resetezi parola pentru <ph name="ORG_NAME" /> dacă ai folosit-o și pe alte site-uri.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (sincronizat)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 în uz}few{# în uz}other{# în uz}}</translation>
<translation id="5939518447894949180">Resetează</translation>
-<translation id="5959728338436674663">Trimite automat anumite <ph name="BEGIN_WHITEPAPER_LINK" />informații despre sistem și conținutul paginii<ph name="END_WHITEPAPER_LINK" /> la Google pentru a detecta aplicațiile și site-urile periculoase. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Editează informațiile de contact</translation>
<translation id="5967867314010545767">Eliminați din istoric</translation>
<translation id="5975083100439434680">Micșorează</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Cod poștal</translation>
<translation id="6290238015253830360">Articolele sugerate apar aici</translation>
<translation id="6305205051461490394">Adresa URL <ph name="URL" /> nu poate fi accesată.</translation>
-<translation id="6319915415804115995">Folosit ultima dată acum peste un an</translation>
<translation id="6321917430147971392">Verificați setările DNS</translation>
<translation id="6328639280570009161">Încercați să dezactivați anticiparea rețelei</translation>
<translation id="6328786501058569169">Acest site este înșelător</translation>
<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="6342069812937806050">Adineauri</translation>
<translation id="6355080345576803305">Modificarea sesiunii publice</translation>
<translation id="6358450015545214790">Ce înseamnă acestea?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{o altă sugestie}few{alte # sugestii}other{alte # de sugestii}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Repetați ștergerea</translation>
<translation id="6534179046333460208">Sugestii pentru Webul material</translation>
<translation id="6550675742724504774">Opțiuni</translation>
-<translation id="6556915248009097796">Expirat în <ph name="EXPIRATION_DATE_ABBR" />, folosit ultima dată pe <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Administratorul nu l-a aprobat încă</translation>
<translation id="6569060085658103619">Se afișează pagina unei extensii</translation>
<translation id="6596325263575161958">Opțiuni de criptare</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Ai încercat să accesezi <ph name="DOMAIN" />, dar serverul a prezentat un certificat semnat folosind un algoritm de semnare slab (cum ar fi SHA-1). Acest lucru înseamnă că este posibil ca datele de conectare de securitate prezentate de server să fie falsificate sau ca serverul să nu fie cel așteptat (este posibil să comunici cu un atacator).</translation>
<translation id="6831043979455480757">Tradu</translation>
<translation id="6839929833149231406">Zonă</translation>
+<translation id="6852204201400771460">Reîncarci aplicația?</translation>
+<translation id="6865412394715372076">Acest card nu poate fi confirmat chiar acum.</translation>
<translation id="6874604403660855544">&amp;Repetați adăugarea</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Nivelul politicii nu este acceptat.</translation>
<translation id="6895330447102777224">Cardul tău este confirmat</translation>
<translation id="6897140037006041989">User Agent</translation>
+<translation id="6903319715792422884">Ne poți ajuta să îmbunătățim Navigarea sigură dacă trimiți la Google anumite <ph name="BEGIN_WHITEPAPER_LINK" />informații despre sistem și conținutul paginii<ph name="END_WHITEPAPER_LINK" />. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Utilizator:</translation>
+<translation id="6944692733090228304">Ai introdus parola pe un site care nu este gestionat de <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Pentru a proteja contul, nu folosi aceeași parolă pentru alte aplicații și site-uri.</translation>
<translation id="6945221475159498467">Selectează</translation>
<translation id="6948701128805548767">Pentru a vedea metodele de preluare și cerințele, selectează o adresă</translation>
<translation id="6949872517221025916">Resetează parola</translation>
+<translation id="6950684638814147129">Eroare la analizarea valorii JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Certificatul serverului pare a fi un fals.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Dispozitiv</translation>
@@ -877,6 +876,7 @@
&lt;&lt;li&gt;Dă clic pe &lt;strong&gt;Apply&lt;/strong&gt; (Aplică), apoi dă clic pe &lt;strong&gt;OK&lt;/strong&gt;
&lt;li&gt;Accesează &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Centrul de ajutor Chrome&lt;/a&gt; pentru a afla cum poți să elimini definitiv software-ul de pe computer
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Gestionează parolele…</translation>
<translation id="7419106976560586862">Calea profilului</translation>
<translation id="7437289804838430631">Adaugă informații de contact</translation>
<translation id="7441627299479586546">Subiectul politicii este greșit</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />să afli mai multe<ph name="END_LINK" /> despre această problemă.</translation>
<translation id="7455133967321480974">Utilizați setarea prestabilită la nivel global (Blocați)</translation>
<translation id="7460163899615895653">Filele recente de pe alte dispozitive sunt afișate aici</translation>
-<translation id="7469372306589899959">Se confirmă cardul</translation>
<translation id="7473891865547856676">Nu, mulțumesc</translation>
<translation id="7481312909269577407">Înainte</translation>
<translation id="7485870689360869515">Nu s-au găsit date.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Traducerea nu a reușit din cauza unei probleme cu conexiunea la rețea.</translation>
<translation id="8311129316111205805">Încarcă sesiunea</translation>
<translation id="8332188693563227489">Accesul la <ph name="HOST_NAME" /> nu este permis</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Dacă îți asumi riscurile de securitate, poți să <ph name="BEGIN_LINK" />accesezi acest site<ph name="END_LINK" /> înainte ca programele periculoase să fie eliminate.</translation>
<translation id="8349305172487531364">Bara de marcaje</translation>
<translation id="8363502534493474904">să dezactivezi modul Avion.</translation>
@@ -1035,21 +1035,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> a răspuns prea târziu.</translation>
<translation id="8503559462189395349">Parole Chrome</translation>
<translation id="8503813439785031346">Nume utilizator</translation>
+<translation id="8508648098325802031">Pictograma Căutare</translation>
<translation id="8543181531796978784">Poți să <ph name="BEGIN_ERROR_LINK" />raportezi o problemă privind detectarea<ph name="END_ERROR_LINK" /> sau, dacă îți asumi riscurile de securitate, poți să <ph name="BEGIN_LINK" />accesezi acest site nesigur<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Întrebări? Contactează persoana care îți monitorizează profilul.</translation>
<translation id="8553075262323480129">Traducerea nu a reușit, deoarece nu a putut fi stabilită limba paginii.</translation>
<translation id="8557066899867184262">Codul CVC se află pe spatele cardului.</translation>
<translation id="8559762987265718583">O conexiune privată la <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> nu poate fi stabilită, deoarece data și ora dispozitivului (<ph name="DATE_AND_TIME" />) sunt incorecte.</translation>
+<translation id="8564985650692024650">Chromium îți recomandă să resetezi parola pentru <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> dacă ai folosit-o și pe alte site-uri.</translation>
<translation id="8571890674111243710">Se traduce pagina în <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Adăugați telefon
</translation>
<translation id="859285277496340001">Certificatul nu specifică un mecanism pentru a verifica dacă acesta a fost revocat.</translation>
+<translation id="860043288473659153">Nume titular de card</translation>
<translation id="8620436878122366504">Părinții tăi nu l-au aprobat încă</translation>
<translation id="8625384913736129811">Salvează cardul pe acest dispozitiv</translation>
-<translation id="8639963783467694461">Setări pentru Completare automată</translation>
+<translation id="8663226718884576429">Rezumatul comenzii, <ph name="TOTAL_LABEL" />, Mai multe detalii</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, răspuns, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Conexiunea la <ph name="DOMAIN" /> nu este criptată.</translation>
<translation id="8718314106902482036">Plata nu a fost finalizată</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, sugestie de căutare</translation>
<translation id="8725066075913043281">Încearcă din nou</translation>
<translation id="8728672262656704056">Ați trecut în modul incognito</translation>
<translation id="8730621377337864115">Terminat</translation>
@@ -1065,8 +1069,10 @@
<translation id="8820817407110198400">Marcaje</translation>
<translation id="883848425547221593">Alte marcaje</translation>
<translation id="884264119367021077">Adresa de expediere</translation>
+<translation id="8846319957959474018">Deschide aplicații cu ușurință folosind marcajele</translation>
<translation id="884923133447025588">Nu a fost găsit niciun mecanism de revocare.</translation>
<translation id="885730110891505394">Permiterea accesului pentru Google</translation>
+<translation id="8858065207712248076">Chrome îți recomandă să resetezi parola pentru <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> dacă ai folosit-o și pe alte site-uri.</translation>
<translation id="8866481888320382733">Eroare la analizarea setărilor pentru politică</translation>
<translation id="8870413625673593573">Închise recent</translation>
<translation id="8874824191258364635">Introdu un număr de card valid</translation>
@@ -1105,6 +1111,7 @@
<translation id="9103872766612412690">Site-ul <ph name="SITE" /> folosește în mod obișnuit criptarea pentru a-ți proteja informațiile. Când Chromium a încercat să se conecteze la <ph name="SITE" /> de această dată, site-ul a returnat date de conectare neobișnuite și incorecte. Acest lucru s-a întâmplat fie pentru că un atacator încearcă să falsifice site-ul <ph name="SITE" />, fie pentru că un ecran de conectare Wi-Fi a întrerupt conexiunea. Securitatea informațiilor tale nu a fost afectată, deoarece Chromium a oprit conexiunea înainte ca datele să fie transferate.</translation>
<translation id="9106062320799175032">Adaugă o adresă de facturare</translation>
<translation id="910908805481542201">Ajută-mă să remediez această eroare</translation>
+<translation id="9114524666733003316">Se confirmă cardul…</translation>
<translation id="9128870381267983090">Conectați-vă la rețea</translation>
<translation id="9137013805542155359">Afișează originalul</translation>
<translation id="9137248913990643158">Pornește și conectează-te la Chrome înainte de a folosi această aplicație.</translation>
@@ -1115,6 +1122,7 @@
<translation id="9168814207360376865">Permite site-urilor să verifice dacă ai salvat metode de plată</translation>
<translation id="9169664750068251925">Blocați întotdeauna pe acest site</translation>
<translation id="9170848237812810038">&amp;Anulează</translation>
+<translation id="9171296965991013597">Ieși din aplicație?</translation>
<translation id="917450738466192189">Certificatul serverului nu este valid.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> folosește un protocol neacceptat.</translation>
<translation id="9205078245616868884">Datele sunt criptate cu expresia de acces pentru sincronizare. Introdu-o pentru a începe sincronizarea.</translation>
diff --git a/chromium/components/strings/components_strings_ru.xtb b/chromium/components/strings/components_strings_ru.xtb
index 8e6426701b9..041b7f0f98e 100644
--- a/chromium/components/strings/components_strings_ru.xtb
+++ b/chromium/components/strings/components_strings_ru.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Скрыть значение</translation>
<translation id="1228893227497259893">Неверный идентификатор объекта</translation>
<translation id="1232569758102978740">Без имени</translation>
+<translation id="1250759482327835220">Чтобы ускорить процесс оплаты в будущем, сохраните карту, свое имя и платежный адрес в аккаунте Google.</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;
@@ -97,7 +98,6 @@
&lt;p&gt;Установите точную дату и время. Для этого откройте раздел &lt;strong&gt;Общие&lt;/strong&gt; в приложении &lt;strong&gt;Настройки&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">При загрузке этой страницы возникли неполадки.</translation>
-<translation id="1590457302292452960">Создайте надежный пароль…</translation>
<translation id="1592005682883173041">Доступ к данным на устройстве</translation>
<translation id="1594030484168838125">Выбрать</translation>
<translation id="1620510694547887537">Камера</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Обратитесь за помощью к системному администратору.</translation>
<translation id="1740951997222943430">Недопустимый формат месяца.</translation>
+<translation id="1743520634839655729">Чтобы ускорить процесс оплаты в будущем, сохраните карту, свое имя, а также платежный адрес в аккаунте Google и на этом устройстве.</translation>
<translation id="17513872634828108">Открытые вкладки</translation>
<translation id="1753706481035618306">Номер страницы</translation>
<translation id="1763864636252898013">Не удалось подтвердить, что это сервер <ph name="DOMAIN" />. Операционная система устройства не доверяет его сертификату безопасности. Возможно, сервер настроен неправильно или кто-то пытается перехватить ваши данные.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Здесь появятся открытые вкладки.</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Владелец карты</translation>
-<translation id="1806541873155184440">Добавлена <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Недопустимый запрос или неверные параметры запроса</translation>
<translation id="1826516787628120939">Проверка</translation>
<translation id="1834321415901700177">Сайт содержит вредоносное ПО</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 вариант}one{# вариант}few{# варианта}many{# вариантов}other{# варианта}}</translation>
<translation id="2079545284768500474">Отмена</translation>
<translation id="20817612488360358">Включены системные настройки прокси-сервера, но при этом его конфигурация задана явным образом.</translation>
-<translation id="2084558088529668945">Вы ввели пароль на сайте, которым не управляет <ph name="ORG_NAME" />. Чтобы защитить свой аккаунт, не используйте этот пароль для других приложений и сайтов.</translation>
<translation id="2091887806945687916">Звук</translation>
<translation id="2094505752054353250">Несоответствие домена</translation>
<translation id="2096368010154057602">Округ</translation>
+<translation id="2102134110707549001">Сгенерировать надежный пароль</translation>
<translation id="2108755909498034140">Перезагрузите компьютер.</translation>
<translation id="2113977810652731515">Карта</translation>
<translation id="2114841414352855701">Игнорируется, так как правило <ph name="POLICY_NAME" /> имеет приоритет.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Ошибка HTTP</translation>
<translation id="2270484714375784793">Номер телефона</translation>
<translation id="2292556288342944218">Доступ в Интернет закрыт</translation>
-<translation id="230155334948463882">Добавить карту?</translation>
<translation id="2316887270356262533">Освободится менее 1 МБ пространства. После этого некоторые веб-страницы могут загружаться дольше обычного.</translation>
<translation id="2317259163369394535">Для доступа к домену <ph name="DOMAIN" /> необходимо указать имя пользователя и пароль.</translation>
<translation id="2317583587496011522">Принимаются дебетовые карты.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Карты, которые принимаются к оплате</translation>
<translation id="2702801445560668637">Список для чтения</translation>
<translation id="2704283930420550640">Значение не соответствует формату.</translation>
-<translation id="2704951214193499422">Не удалось подтвердить данные карты. Повторите попытку позже.</translation>
<translation id="2705137772291741111">Невозможно прочитать копию сайта, сохраненную в кеше.</translation>
<translation id="2709516037105925701">Автозаполнение</translation>
<translation id="2710942282213947212">ПО, установленное на компьютере, не позволяет Chromium безопасно подключиться к Интернету</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Способ доставки</translation>
<translation id="277499241957683684">Устройство не зарегистрировано</translation>
<translation id="2781030394888168909">Экспортировать для macOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Соединение сброшено.</translation>
<translation id="2788784517760473862">Кредитные карты, которые принимаются к оплате</translation>
<translation id="2794233252405721443">Сайт заблокирован</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Прокси-серверы, используемые для соединения, можно отключить на странице настроек.</translation>
<translation id="2955913368246107853">Закрыть панель поиска</translation>
<translation id="2958431318199492670">Некоторые элементы сетевой конфигурации невозможно импортировать, поскольку она не соответствует стандарту ONC.</translation>
-<translation id="2966678944701946121">Срок действия: <ph name="EXPIRATION_DATE_ABBR" />, добавлена <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Для создания безопасного подключения необходимо, чтобы показания системных часов были верны. Причина в том, что сертификаты для идентификации сайтов имеют ограниченный срок действия. Если часы на устройстве неточны, Chrome не может проверить актуальность этих сертификатов.</translation>
<translation id="2972581237482394796">&amp;Повторить</translation>
<translation id="2977665033722899841">Сейчас выбрано – <ph name="ROW_NAME" />. <ph name="ROW_CONTENT" />.</translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Неверный тип политики</translation>
<translation id="3037605927509011580">Опаньки...</translation>
<translation id="3041612393474885105">Данные сертификата</translation>
-<translation id="3063697135517575841">Не удалось подтвердить данные карты. Повторите попытку позже.</translation>
<translation id="3064966200440839136">Вы выйдете из режима инкогнито, чтобы произвести оплату во внешнем приложении. Продолжить?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Нет}=1{1 пароль}one{# пароль}few{# пароля}many{# паролей}other{# пароля}}</translation>
<translation id="3096100844101284527">Добавить адрес получения</translation>
@@ -340,7 +337,6 @@
<translation id="3305707030755673451">Данные были зашифрованы с помощью кодовой фразы <ph name="TIME" />. Введите ее, чтобы начать синхронизацию.</translation>
<translation id="3320021301628644560">Добавьте платежный адрес</translation>
<translation id="3338095232262050444">Защищено</translation>
-<translation id="3340978935015468852">настройках</translation>
<translation id="3345135638360864351">Не удалось отправить пользователю <ph name="NAME" /> запрос на доступ к этому сайту. Повторите попытку.</translation>
<translation id="3355823806454867987">Изменить настройки прокси-сервера...</translation>
<translation id="3361596688432910856">В Chrome <ph name="BEGIN_EMPHASIS" />не будет сохраняться<ph name="END_EMPHASIS" /> следующая информация:
@@ -374,7 +370,6 @@
<translation id="3528171143076753409">Сертификат сервера не является доверенным.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Как минимум 1 запись на синхронизируемых устройствах}=1{1 запись (не считая данных на синхронизируемых устройствах)}one{# запись (не считая данных на синхронизируемых устройствах)}few{# записи (не считая данных на синхронизируемых устройствах)}many{# записей (не считая данных на синхронизируемых устройствах)}other{# записей (не считая данных на синхронизируемых устройствах)}}</translation>
<translation id="3539171420378717834">Хранить данные карты на этом устройстве</translation>
-<translation id="3542684924769048008">Использовать пароль для:</translation>
<translation id="3549644494707163724">Шифровать все синхронизированные данные с помощью кодовой фразы</translation>
<translation id="3556433843310711081">Для разблокировки обратитесь к администратору.</translation>
<translation id="3566021033012934673">Подключение не защищено</translation>
@@ -460,7 +455,6 @@
<translation id="4171400957073367226">Подтверждающая подпись недействительна</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{Ещё <ph name="ITEM_COUNT" /> товар}one{Ещё <ph name="ITEM_COUNT" /> товар}few{Ещё <ph name="ITEM_COUNT" /> товара}many{Ещё <ph name="ITEM_COUNT" /> товаров}other{Ещё <ph name="ITEM_COUNT" /> товара}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" />: <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Рекомендуем сбросить пароль в домене <ph name="ORG_NAME" />, если вы используете его на других сайтах.</translation>
<translation id="4196861286325780578">&amp;Повторить перемещение</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Проверьте настройки брандмауэра и антивирусного ПО<ph name="END_LINK" />.</translation>
<translation id="4220128509585149162">Сбои в работе Chrome</translation>
@@ -489,7 +483,6 @@
<translation id="425582637250725228">Возможно, внесенные изменения не сохранятся.</translation>
<translation id="4258748452823770588">Подпись недействительна</translation>
<translation id="4265872034478892965">Разрешено администратором</translation>
-<translation id="4269787794583293679">(Не указано)</translation>
<translation id="4275830172053184480">Перезапуск устройства</translation>
<translation id="4277028893293644418">Сбросить пароль</translation>
<translation id="4280429058323657511">, действует до <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -512,6 +505,7 @@
<translation id="4434045419905280838">Всплывающие окна и переадресация</translation>
<translation id="443673843213245140">Прокси-сервер отключен, но при этом его конфигурация задана явным образом.</translation>
<translation id="445100540951337728">Дебетовые карты, которые принимаются к оплате</translation>
+<translation id="4472575034687746823">Начало работы</translation>
<translation id="4506176782989081258">Ошибка проверки: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Обратитесь за помощью к системному администратору.</translation>
<translation id="450710068430902550">Доступ администратора</translation>
@@ -536,8 +530,8 @@
<translation id="4726672564094551039">Повторно загрузить политики</translation>
<translation id="4728558894243024398">Платформа</translation>
<translation id="4736825316280949806">Перезапустите Chromium.</translation>
+<translation id="4742407542027196863">Управление паролями</translation>
<translation id="4744603770635761495">Путь к исполняемому файлу</translation>
-<translation id="4749685221585524849">Последний раз использовалась <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Информация, которую вы сообщаете этому сайту (например, пароли и номера банковских карт), защищена.</translation>
<translation id="4756388243121344051">&amp;История</translation>
<translation id="4758311279753947758">Добавить контактные данные</translation>
@@ -554,6 +548,7 @@
<translation id="4850886885716139402">Посмотреть</translation>
<translation id="4854362297993841467">Этот способ доставки недоступен. Выберите другой.</translation>
<translation id="4858792381671956233">Запрос на просмотр сайта отправлен вашим родителям</translation>
+<translation id="4876305945144899064">Имя пользователя не указано</translation>
<translation id="4880827082731008257">Искать в истории</translation>
<translation id="4881695831933465202">Открыть</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> и <ph name="TYPE_3" /></translation>
@@ -587,6 +582,7 @@
<translation id="5089810972385038852">Штат</translation>
<translation id="5094747076828555589">Не удалось подтвердить, что это сервер <ph name="DOMAIN" />. Chromium не доверяет его сертификату безопасности. Возможно, сервер настроен неправильно или кто-то пытается перехватить ваши данные.</translation>
<translation id="5095208057601539847">Провинция</translation>
+<translation id="5098332213681597508">Имя из вашего аккаунта Google.</translation>
<translation id="5115563688576182185">(64 бит)</translation>
<translation id="5121084798328133320">После подтверждения реквизиты карты из платежного аккаунта Google будут переданы этому сайту.</translation>
<translation id="5128122789703661928">Невозможно удалить сеанс с таким названием.</translation>
@@ -627,9 +623,10 @@
<translation id="5332219387342487447">Способ доставки</translation>
<translation id="5355557959165512791">Сертификат веб-сайта <ph name="SITE" /> отозван. Открыть сайт в настоящее время нельзя. Сбой мог быть вызван сетевой ошибкой или действиями злоумышленников. Скорее всего, сайт заработает через некоторое время.</translation>
<translation id="536296301121032821">Не удалось сохранить настройки политики</translation>
+<translation id="5371425731340848620">Изменить информацию о карте</translation>
<translation id="5377026284221673050">"Часы отстают", "Часы спешат" или &lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;</translation>
<translation id="5386426401304769735">В цепочке сертификатов этого сайта есть сертификат, подписанный с помощью алгоритма SHA-1.</translation>
-<translation id="5402410679244714488">Срок действия: <ph name="EXPIRATION_DATE_ABBR" />, использовалась более года назад</translation>
+<translation id="5387961145478138773">Быстрый доступ к любимым приложениям Google</translation>
<translation id="540969355065856584">Не удалось подтвердить, что это сервер <ph name="DOMAIN" />. Его сертификат безопасности может быть недействителен в настоящее время. Возможно, сервер настроен неправильно или кто-то пытается перехватить ваши данные.</translation>
<translation id="5421136146218899937">Очистить историю...</translation>
<translation id="5430298929874300616">Удалить закладку</translation>
@@ -671,11 +668,13 @@
<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="5659593005791499971">Электронная почта</translation>
+<translation id="5666899935841546222">Хотите добавить все свои карты в аккаунт?</translation>
<translation id="5675650730144413517">Страница недоступна</translation>
<translation id="5685654322157854305">Добавить адрес доставки посылок</translation>
<translation id="5689199277474810259">Экспортировать как JSON</translation>
<translation id="5689516760719285838">Геоданные</translation>
<translation id="570530837424789914">Управление…</translation>
+<translation id="57094364128775171">Сгенерировать надежный пароль</translation>
<translation id="5710435578057952990">Идентификационные данные этого сайта не проверены.</translation>
<translation id="5719499550583120431">Принимаются карты предоплаты.</translation>
<translation id="5720705177508910913">Текущий пользователь</translation>
@@ -696,11 +695,9 @@
<translation id="5869405914158311789">Не удается получить доступ к сайту</translation>
<translation id="5869522115854928033">Сайты с сохраненными паролями</translation>
<translation id="5893752035575986141">Принимаются кредитные карты.</translation>
-<translation id="5898382028489516745">Рекомендуем сбросить пароль в домене <ph name="ORG_NAME" />, если вы используете его на других сайтах.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (данные синхронизируются)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{Используется 1 файл cookie}one{Используется # файл cookie}few{Используется # файла cookie}many{Используется # файлов cookie}other{Используется # файла cookie}}</translation>
<translation id="5939518447894949180">Сбросить</translation>
-<translation id="5959728338436674663">Автоматически отправлять <ph name="BEGIN_WHITEPAPER_LINK" />системную информацию и контент страниц<ph name="END_WHITEPAPER_LINK" /> в Google, чтобы улучшить распознавание опасных приложений и сайтов. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Измените контактную информацию</translation>
<translation id="5967867314010545767">Удалить из истории</translation>
<translation id="5975083100439434680">Уменьшить</translation>
@@ -746,13 +743,11 @@
<translation id="6282194474023008486">Почтовый индекс</translation>
<translation id="6290238015253830360">Здесь появятся рекомендуемые статьи.</translation>
<translation id="6305205051461490394">Сайт <ph name="URL" /> недоступен.</translation>
-<translation id="6319915415804115995">Использовалась более года назад</translation>
<translation id="6321917430147971392">Проверьте настройки DNS</translation>
<translation id="6328639280570009161">Отключите предсказание сетевых действий</translation>
<translation id="6328786501058569169">Это поддельный сайт</translation>
<translation id="6337133576188860026">Освободится менее <ph name="SIZE" /> пространства. После этого некоторые веб-страницы могут загружаться дольше обычного.</translation>
<translation id="6337534724793800597">Фильтровать политики по названию</translation>
-<translation id="6342069812937806050">только что</translation>
<translation id="6355080345576803305">Сеанс общего доступа (переопределено)</translation>
<translation id="6358450015545214790">Что это значит?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{ещё 1 вариант}one{ещё # вариант}few{ещё # варианта}many{ещё # вариантов}other{ещё # варианта}}</translation>
@@ -777,7 +772,6 @@
<translation id="6529602333819889595">&amp;Повторить удаление</translation>
<translation id="6534179046333460208">Интернет вокруг нас: рекомендации</translation>
<translation id="6550675742724504774">Параметры</translation>
-<translation id="6556915248009097796">Срок действия: <ph name="EXPIRATION_DATE_ABBR" />, последний раз использовалась <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Ещё не одобрено администратором</translation>
<translation id="6569060085658103619">Вы просматриваете страницу расширения</translation>
<translation id="6596325263575161958">Параметры шифрования</translation>
@@ -806,15 +800,20 @@
<translation id="6825578344716086703">Вы пытаетесь обратиться к серверу в домене <ph name="DOMAIN" />, но его сертификат подписан с помощью ненадежного алгоритма (например, SHA-1). Это означает, что учетные данные безопасности и сам сервер могут оказаться поддельными. Возможно, вы имеете дело со злоумышленниками.</translation>
<translation id="6831043979455480757">Перевести</translation>
<translation id="6839929833149231406">Административный район</translation>
+<translation id="6852204201400771460">Перезапустить приложение?</translation>
+<translation id="6865412394715372076">Невозможно подтвердить карту.</translation>
<translation id="6874604403660855544">&amp;Повторить добавление</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" />, <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Значение правила не поддерживается.</translation>
<translation id="6895330447102777224">Ваша карта подтверждена</translation>
<translation id="6897140037006041989">User Agent</translation>
+<translation id="6903319715792422884">Чтобы улучшить режим Безопасного просмотра, вы можете <ph name="BEGIN_WHITEPAPER_LINK" />отправлять системную информацию и контент страниц<ph name="END_WHITEPAPER_LINK" /> в Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Пользователь:</translation>
+<translation id="6944692733090228304">Вы ввели пароль на сайте, который не находится под управлением организации <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Чтобы защитить свой аккаунт, не используйте этот пароль для других приложений и сайтов.</translation>
<translation id="6945221475159498467">Выбрать</translation>
<translation id="6948701128805548767">Выберите адрес, чтобы посмотреть способы и условия получения.</translation>
<translation id="6949872517221025916">Сброс пароля</translation>
+<translation id="6950684638814147129">Ошибка при синтаксическом анализе значения JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Возможно, сертификат сервера фальсифицирован.</translation>
<translation id="6965382102122355670">ОК</translation>
<translation id="6965978654500191972">Устройство</translation>
@@ -876,6 +875,7 @@
&lt;li&gt;Нажмите &lt;strong&gt;Применить&lt;/strong&gt;, а затем – &lt;strong&gt;ОК&lt;/strong&gt;.
&lt;li&gt;О том, как удалить ПО с компьютера, читайте в &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Справочном центре Chrome&lt;/a&gt;.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Управление паролями</translation>
<translation id="7419106976560586862">Путь к профилю</translation>
<translation id="7437289804838430631">Добавить контактные данные</translation>
<translation id="7441627299479586546">Неверный субъект политики</translation>
@@ -884,7 +884,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Узнайте больше<ph name="END_LINK" /> об этой проблеме.</translation>
<translation id="7455133967321480974">Использовать глобальный параметр по умолчанию (блокировать)</translation>
<translation id="7460163899615895653">Здесь появятся недавние вкладки с других устройств</translation>
-<translation id="7469372306589899959">Подтверждение карты…</translation>
<translation id="7473891865547856676">Пропустить</translation>
<translation id="7481312909269577407">Вперед</translation>
<translation id="7485870689360869515">Данные не найдены.</translation>
@@ -1014,6 +1013,7 @@
<translation id="8308427013383895095">Перевод не завершен из-за проблем с сетевым подключением.</translation>
<translation id="8311129316111205805">Загрузить сеанс</translation>
<translation id="8332188693563227489">Доступ к <ph name="HOST_NAME" /> запрещен</translation>
+<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="8349305172487531364">Панель закладок</translation>
<translation id="8363502534493474904">Отключите режим полета.</translation>
@@ -1034,20 +1034,24 @@
<translation id="8498891568109133222">Превышено время ожидания ответа от сайта <ph name="HOST_NAME" />.</translation>
<translation id="8503559462189395349">Пароли Chrome</translation>
<translation id="8503813439785031346">Имя пользователя</translation>
+<translation id="8508648098325802031">Значок поиска</translation>
<translation id="8543181531796978784"><ph name="BEGIN_ERROR_LINK" />Сообщите о зараженном сайте<ph name="END_ERROR_LINK" />. Если вы готовы подвергнуть риску личные данные, то можете <ph name="BEGIN_LINK" />перейти на страницу<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Есть вопросы? Обратитесь к человеку, который контролирует ваш профиль.</translation>
<translation id="8553075262323480129">Перевод не удался, так как не удается определить язык страницы.</translation>
<translation id="8557066899867184262">CVC-код указан на обратной стороне карты.</translation>
<translation id="8559762987265718583">Не удалось установить защищенное соединение с доменом <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> из-за неверных настроек системных часов и календаря (<ph name="DATE_AND_TIME" />).</translation>
+<translation id="8564985650692024650">Chromium рекомендует сбросить пароль, используемый в подразделении <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, если вы указывали его на других сайтах.</translation>
<translation id="8571890674111243710">Перевод страницы на <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Добавьте номер</translation>
<translation id="859285277496340001">Этот сертификат не определяет механизм проверки отзыва.</translation>
+<translation id="860043288473659153">Имя владельца карты</translation>
<translation id="8620436878122366504">Ещё не одобрено родителями</translation>
<translation id="8625384913736129811">Сохранить карту на этом устройстве</translation>
-<translation id="8639963783467694461">Настройки автозаполнения</translation>
+<translation id="8663226718884576429">Информация о заказе, <ph name="TOTAL_LABEL" />, дополнительные сведения</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, ответ, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Соединение с <ph name="DOMAIN" /> не зашифровано.</translation>
<translation id="8718314106902482036">Не удалось обработать платеж</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, подсказка</translation>
<translation id="8725066075913043281">Повторить попытку</translation>
<translation id="8728672262656704056">Вы перешли в режим инкогнито</translation>
<translation id="8730621377337864115">Готово</translation>
@@ -1063,8 +1067,10 @@
<translation id="8820817407110198400">Закладки</translation>
<translation id="883848425547221593">Другие закладки</translation>
<translation id="884264119367021077">Адрес доставки</translation>
+<translation id="8846319957959474018">Без труда открывайте приложения с помощью закладок</translation>
<translation id="884923133447025588">Не обнаружен механизм отзыва.</translation>
<translation id="885730110891505394">Доступ Google</translation>
+<translation id="8858065207712248076">Chrome рекомендует сбросить пароль, используемый в подразделении <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, если вы указывали его на других сайтах.</translation>
<translation id="8866481888320382733">Не удалось выполнить анализ настроек политики</translation>
<translation id="8870413625673593573">Недавно закрытые</translation>
<translation id="8874824191258364635">Введите действительный номер карты.</translation>
@@ -1103,6 +1109,7 @@
<translation id="9103872766612412690">На сайте <ph name="SITE" /> для защиты ваших данных обычно используется шифрование. Однако учетные данные, которые мы получили от сайта <ph name="SITE" /> сейчас, отличаются от тех, которые он отправляет обычно. Вероятно, вредоносный сайт пытается выдать себя за <ph name="SITE" />, либо страница подключения к сети Wi-Fi прервала соединение. Ваша информация по-прежнему в безопасности, так как браузер Chromium разорвал соединение до того, как произошел обмен данными.</translation>
<translation id="9106062320799175032">Добавьте платежный адрес</translation>
<translation id="910908805481542201">Получить помощь</translation>
+<translation id="9114524666733003316">Подтверждение карты...</translation>
<translation id="9128870381267983090">Подключитесь к сети</translation>
<translation id="9137013805542155359">Показать оригинал</translation>
<translation id="9137248913990643158">Войдите в Chrome, прежде чем использовать это приложение.</translation>
@@ -1113,6 +1120,7 @@
<translation id="9168814207360376865">Разрешить сайтам проверять наличие сохраненных способов оплаты</translation>
<translation id="9169664750068251925">Всегда блокировать на этом сайте</translation>
<translation id="9170848237812810038">&amp;Отменить</translation>
+<translation id="9171296965991013597">Закрыть приложение?</translation>
<translation id="917450738466192189">Сертификат сервера недействителен</translation>
<translation id="9183425211371246419">На сайте <ph name="HOST_NAME" /> используется неподдерживаемый протокол.</translation>
<translation id="9205078245616868884">Данные зашифрованы с помощью кодовой фразы. Введите ее, чтобы начать синхронизацию.</translation>
diff --git a/chromium/components/strings/components_strings_sk.xtb b/chromium/components/strings/components_strings_sk.xtb
index a8019e2c0bc..a38a91d78a3 100644
--- a/chromium/components/strings/components_strings_sk.xtb
+++ b/chromium/components/strings/components_strings_sk.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Skryť hodnotu</translation>
<translation id="1228893227497259893">Nesprávny identifikátor entity</translation>
<translation id="1232569758102978740">Bez názvu</translation>
+<translation id="1250759482327835220">Ak chcete nabudúce zaplatiť rýchlejšie, uložte si kartu, meno a fakturačnú adresu do účtu Google.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (synchronizované)</translation>
<translation id="1256368399071562588">&lt;p&gt;Ak chcete navštíviť určitý web, ale neotvorí sa, najprv skúste opraviť chybu pomocou týchto krokov na riešenie problémov:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Dátum a čas upravte v aplikácii &lt;strong&gt;Nastavenia&lt;/strong&gt; v sekcii &lt;strong&gt;Všeobecné&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Pri zobrazovaní tejto webovej stránky sa vyskytla chyba.</translation>
-<translation id="1590457302292452960">Vygenerujte silné heslo…</translation>
<translation id="1592005682883173041">Prístup k miestnym údajom</translation>
<translation id="1594030484168838125">Zvoliť</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Skúste kontaktovať správcu systému.</translation>
<translation id="1740951997222943430">Zadajte platný mesiac vypršania platnosti</translation>
+<translation id="1743520634839655729">Ak chcete nabudúce zaplatiť rýchlejšie, uložte si kartu, meno a fakturačnú adresu do účtu Google a tohto zariadenia.</translation>
<translation id="17513872634828108">Otvorené karty</translation>
<translation id="1753706481035618306">Číslo stránky</translation>
<translation id="1763864636252898013">Server nedokáže overiť, či ide o doménu <ph name="DOMAIN" />, operačný systém vášho zariadenia 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>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Tu sa zobrazia otvorené karty</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Meno majiteľa karty</translation>
-<translation id="1806541873155184440">Pridané <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Neplatná žiadosť alebo parametre žiadosti</translation>
<translation id="1826516787628120939">Kontroluje sa</translation>
<translation id="1834321415901700177">Tento web obsahuje škodlivé programy</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 návrh}few{# návrhy}many{# návrhu}other{# návrhov}}</translation>
<translation id="2079545284768500474">Späť</translation>
<translation id="20817612488360358">Používanie systémových nastavení servera proxy je nastavené, avšak je určená aj explicitná konfigurácia servera proxy.</translation>
-<translation id="2084558088529668945">Zadali ste heslo na webe, ktorý nie je spravovaný organizáciou <ph name="ORG_NAME" />. Ak chcete, aby bol váš účet chránený, nepoužívajte jeho heslo pre iné aplikácie a weby.</translation>
<translation id="2091887806945687916">Zvuk</translation>
<translation id="2094505752054353250">Domény sa nezhodujú</translation>
<translation id="2096368010154057602">Správna oblasť</translation>
+<translation id="2102134110707549001">Navrhnúť silné heslo…</translation>
<translation id="2108755909498034140">Reštartujte počítač</translation>
<translation id="2113977810652731515">Karta</translation>
<translation id="2114841414352855701">Ignorované, pretože bolo prepísané pravidlom <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Chyba protokolu HTTP</translation>
<translation id="2270484714375784793">Telefónne číslo</translation>
<translation id="2292556288342944218">Váš prístup k internetu je blokovaný</translation>
-<translation id="230155334948463882">Nová karta?</translation>
<translation id="2316887270356262533">Uvoľní menej ako 1 MB. Niektoré weby sa môžu pri ďalšej návšteve načítať pomalšie.</translation>
<translation id="2317259163369394535">Doména <ph name="DOMAIN" /> vyžaduje používateľské meno a heslo.</translation>
<translation id="2317583587496011522">Debetné karty sú akceptované.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Akceptované karty</translation>
<translation id="2702801445560668637">Čitateľský zoznam</translation>
<translation id="2704283930420550640">Hodnota nezodpovedá formátu.</translation>
-<translation id="2704951214193499422">Prehliadaču Chromium sa nepodarilo overiť vašu kartu. Skúste to znova neskôr.</translation>
<translation id="2705137772291741111">Uložená kópia tohto webu (vo vyrovnávacej pamäti) bola nečitateľná.</translation>
<translation id="2709516037105925701">Automatické dopĺňanie</translation>
<translation id="2710942282213947212">Softvér vo vašom počítači bráni prehliadaču Chromium bezpečne sa pripojiť k webu</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Spôsob dodania</translation>
<translation id="277499241957683684">Chýbajúci záznam zariadenia</translation>
<translation id="2781030394888168909">Exportovať pre MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Spojenie bolo resetované.</translation>
<translation id="2788784517760473862">Akceptované kreditné karty</translation>
<translation id="2794233252405721443">Web je blokovaný</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Môžete zakázať ktorékoľvek servery proxy nakonfigurované na pripojenie na stránke nastavení.</translation>
<translation id="2955913368246107853">Zatvoriť panel pre vyhľadávanie</translation>
<translation id="2958431318199492670">Konfigurácia siete nie je v súlade so štandardom ONC. Niektoré časti konfigurácie sa nemusia importovať.</translation>
-<translation id="2966678944701946121">Platnosť do <ph name="EXPIRATION_DATE_ABBR" />, pridané <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Ak chcete nadviazať zabezpečené pripojenie, vaše hodiny musia byť nastavené správne. Je to preto, že certifikáty, ktoré webové stránky používajú na vlastnú identifikáciu, sú platné iba určitý čas. Keďže nie sú hodiny vášho zariadenia nastavené správne, Chrome nemôže tieto certifikáty overiť.</translation>
<translation id="2972581237482394796">&amp;Dopredu</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, aktuálne vybraté. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Chybný typ pravidla</translation>
<translation id="3037605927509011580">Aj, chyba!</translation>
<translation id="3041612393474885105">Informácie o certifikáte</translation>
-<translation id="3063697135517575841">Chromu sa nepodarilo overiť vašu kartu. Skúste to znova neskôr.</translation>
<translation id="3064966200440839136">Ak zaplatíte pomocou externej aplikácie, opustíte režim inkognito. Chcete pokračovať?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Žiadne}=1{1 heslo}few{# heslá}many{# hesla}other{# hesiel}}</translation>
<translation id="3096100844101284527">Pridať adresu vyzdvihnutia</translation>
@@ -338,7 +335,6 @@
<translation id="3305707030755673451">Vaše údaje boli <ph name="TIME" /> zašifrované pomocou vlastnej prístupovej frázy synchronizácie. Keď ju zadáte, synchronizácia sa spustí.</translation>
<translation id="3320021301628644560">Pridanie fakturačnej adresy</translation>
<translation id="3338095232262050444">Zabezpečené</translation>
-<translation id="3340978935015468852">nastavenia</translation>
<translation id="3345135638360864351">Vašu žiadosť o prístup k týmto webovým stránkam sa používateľovi <ph name="NAME" /> neporadilo odoslať. Skúste to znova neskôr.</translation>
<translation id="3355823806454867987">Zmeniť nastavenia proxy...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />nebude ukladať<ph name="END_EMPHASIS" /> tieto informácie:
@@ -372,7 +368,6 @@
<translation id="3528171143076753409">Certifikát servera nie je dôveryhodný.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Aspoň 1 položka v synchronizovaných zariadeniach}=1{1 položka (a ďalšie v synchronizovaných zariadeniach)}few{# položky (a ďalšie v synchronizovaných zariadeniach)}many{# položky (a ďalšie v synchronizovaných zariadeniach)}other{# položiek (a ďalšie v synchronizovaných zariadeniach)}}</translation>
<translation id="3539171420378717834">Ponechať kópiu tejto karty na tomto zariadení</translation>
-<translation id="3542684924769048008">Použiť heslo pre:</translation>
<translation id="3549644494707163724">Šifrovať všetky synchronizované údaje pomocou vlastnej prístupovej frázy pre synchronizáciu</translation>
<translation id="3556433843310711081">Váš správca ho môže pre vás odblokovať</translation>
<translation id="3566021033012934673">Vaše pripojenie nie je súkromné</translation>
@@ -457,7 +452,6 @@
<translation id="4171400957073367226">Nesprávny overovací podpis</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> ďalšia položka}few{<ph name="ITEM_COUNT" /> ďalšie položky}many{<ph name="ITEM_COUNT" /> ďalšej položky}other{<ph name="ITEM_COUNT" /> ďalších položiek}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Ak ste heslo organizácie <ph name="ORG_NAME" /> použili na inom webe, Chrome odporúča, aby ste ho obnovili.</translation>
<translation id="4196861286325780578">&amp;Znova presunúť</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Skontrolovať konfiguráciu brány firewall a antivírusového softvéru<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Zlyhania</translation>
@@ -486,7 +480,6 @@
<translation id="425582637250725228">Zmeny, ktoré ste vykonali, sa nemusia uložiť.</translation>
<translation id="4258748452823770588">Chybný podpis</translation>
<translation id="4265872034478892965">Povolené správcom</translation>
-<translation id="4269787794583293679">(Žiadne používateľské meno)</translation>
<translation id="4275830172053184480">Reštart zariadenia</translation>
<translation id="4277028893293644418">Obnoviť heslo</translation>
<translation id="4280429058323657511">, dátum vypršania platnosti: <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -509,6 +502,7 @@
<translation id="4434045419905280838">Kontextové okná a presmerovania</translation>
<translation id="443673843213245140">Použitie servera proxy je zakázané, ale je určená explicitná konfigurácia servera proxy.</translation>
<translation id="445100540951337728">Akceptované debetné karty</translation>
+<translation id="4472575034687746823">Začíname</translation>
<translation id="4506176782989081258">Chyba overenia: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Kontaktovať správcu systému</translation>
<translation id="450710068430902550">Zdieľanie so správcom</translation>
@@ -533,8 +527,8 @@
<translation id="4726672564094551039">Znova načítať pravidlá</translation>
<translation id="4728558894243024398">Platforma</translation>
<translation id="4736825316280949806">Reštartujte Chromium</translation>
+<translation id="4742407542027196863">Spravovať heslá…</translation>
<translation id="4744603770635761495">Spustiteľná cesta</translation>
-<translation id="4749685221585524849">Naposledy použité <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Vaše informácie (napríklad heslá a čísla kreditných kariet) zostanú po odoslaní na tento web súkromné.</translation>
<translation id="4756388243121344051">&amp;História</translation>
<translation id="4758311279753947758">Pridať kontaktné údaje</translation>
@@ -551,6 +545,7 @@
<translation id="4850886885716139402">Zobraziť</translation>
<translation id="4854362297993841467">Tento spôsob doručenia nie je k dispozícii. Skúste inú adresu.</translation>
<translation id="4858792381671956233">Požiadali ste rodičov o povolenie návštevy tohto webu.</translation>
+<translation id="4876305945144899064">Žiadne používateľské meno</translation>
<translation id="4880827082731008257">Hľadať v histórii</translation>
<translation id="4881695831933465202">Otvoriť</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -584,6 +579,7 @@
<translation id="5089810972385038852">Štát</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="5098332213681597508">Tento názov pochádza z vášho účtu Google.</translation>
<translation id="5115563688576182185">(64-bitová verzia)</translation>
<translation id="5121084798328133320">Po potvrdení sa budú podrobnosti z účtu Google Payments zdieľať s týmto webom.</translation>
<translation id="5128122789703661928">Relácia s týmto názvom je neplatná a nedá sa odstrániť.</translation>
@@ -624,9 +620,10 @@
<translation id="5332219387342487447">Spôsob dodania</translation>
<translation id="5355557959165512791">Web <ph name="SITE" /> momentálne nemôžete navštíviť, pretože tento certifikát bol odvolaný. Chyby siete a útoky sú zvyčajne dočasné, takže by táto stránka mala neskôr pravdepodobne fungovať.</translation>
<translation id="536296301121032821">Nastavenia pravidla sa nepodarilo uložiť</translation>
+<translation id="5371425731340848620">Aktualizujte si kartu</translation>
<translation id="5377026284221673050">„Vaše hodiny meškajú“, „Vaše hodiny idú dopredu“ alebo „&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;“</translation>
<translation id="5386426401304769735">Reťazec certifikátu pre tento web obsahuje certifikát podpísaný pomocou funkcie SHA-1.</translation>
-<translation id="5402410679244714488">Platnosť do: <ph name="EXPIRATION_DATE_ABBR" />. Naposledy použitá pred viac ako rokom.</translation>
+<translation id="5387961145478138773">Získajte okamžitý prístup k svojim aplikáciám Google</translation>
<translation id="540969355065856584">Server nedokáže overiť, či ide o doménu <ph name="DOMAIN" /> – jej bezpečnostný certifikát je momentálne neplatný. Môže to byť spôsobené nesprávnou konfiguráciou alebo tým, že vaše pripojenie napadol útočník.</translation>
<translation id="5421136146218899937">Odstrániť dáta prehliadania…</translation>
<translation id="5430298929874300616">Odstrániť záložku</translation>
@@ -668,11 +665,13 @@
<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="5659593005791499971">E-mail</translation>
+<translation id="5666899935841546222">Chcete mať všetky svoje karty na jednom mieste?</translation>
<translation id="5675650730144413517">Táto stránka nefunguje</translation>
<translation id="5685654322157854305">Pridať dodaciu adresu</translation>
<translation id="5689199277474810259">Exportovať vo formáte JSON</translation>
<translation id="5689516760719285838">Poloha</translation>
<translation id="570530837424789914">Spravovať...</translation>
+<translation id="57094364128775171">Navrhnúť silné heslo…</translation>
<translation id="5710435578057952990">Identita tejto webovej stránky nebola overená.</translation>
<translation id="5719499550583120431">Predplatené karty sú akceptované.</translation>
<translation id="5720705177508910913">Aktuálny používateľ</translation>
@@ -693,11 +692,9 @@
<translation id="5869405914158311789">K tomuto webu sa nedá pripojiť</translation>
<translation id="5869522115854928033">Uložené heslá</translation>
<translation id="5893752035575986141">Kreditné karty sú akceptované.</translation>
-<translation id="5898382028489516745">Ak ste heslo organizácie <ph name="ORG_NAME" /> použili na inom webe, Chromium odporúča, aby ste ho obnovili.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (synchronizované)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{Používa sa 1}few{Používajú sa #}many{Používa sa #}other{Používa sa #}}</translation>
<translation id="5939518447894949180">Resetovať</translation>
-<translation id="5959728338436674663">Automaticky odosielať <ph name="BEGIN_WHITEPAPER_LINK" />niektoré informácie o systéme a obsah stránok<ph name="END_WHITEPAPER_LINK" /> do Googlu s cieľom pomôcť rozpoznávať nebezpečné aplikácie a weby. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Úprava kontaktných informácií</translation>
<translation id="5967867314010545767">Odstrániť z histórie</translation>
<translation id="5975083100439434680">Oddialiť</translation>
@@ -742,13 +739,11 @@
<translation id="6282194474023008486">Poštové smerovacie číslo</translation>
<translation id="6290238015253830360">Tu sa zobrazia vaše navrhované články</translation>
<translation id="6305205051461490394">Web <ph name="URL" /> je nedostupný.</translation>
-<translation id="6319915415804115995">Naposledy použitá pred viac ako rokom.</translation>
<translation id="6321917430147971392">Skontrolujte nastavenia DNS</translation>
<translation id="6328639280570009161">Skúste zakázať predpovede siete</translation>
<translation id="6328786501058569169">Tento web je klamlivý</translation>
<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="6342069812937806050">Práve teraz</translation>
<translation id="6355080345576803305">Prepísanie verejnej relácie</translation>
<translation id="6358450015545214790">Čo znamenajú tieto položky?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 ďalší návrh}few{# ďalšie návrhy}many{# ďalšieho návrhu}other{# ďalších návrhov}}</translation>
@@ -773,7 +768,6 @@
<translation id="6529602333819889595">&amp;Znova odstrániť</translation>
<translation id="6534179046333460208">Návrhy Fyzického webu</translation>
<translation id="6550675742724504774">Možnosti</translation>
-<translation id="6556915248009097796">Platnosť do <ph name="EXPIRATION_DATE_ABBR" />, naposledy použité <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Váš správca to zatiaľ neschválil</translation>
<translation id="6569060085658103619">Prezeráte si stránku s rozšíreniami</translation>
<translation id="6596325263575161958">Možnosti šifrovania</translation>
@@ -802,15 +796,20 @@
<translation id="6825578344716086703">Pokúsili ste sa o prístup do domény <ph name="DOMAIN" />, server však predložil certifikát podpísaný slabým algoritmom podpisu. Znamená to, že predložené poverenia zabezpečenia mohli byť sfalšované a môže ísť o úplne iný server, než ste očakávali (možno komunikujete s útočníkom).</translation>
<translation id="6831043979455480757">Preložiť</translation>
<translation id="6839929833149231406">Oblasť</translation>
+<translation id="6852204201400771460">Načítať znova aplikáciu?</translation>
+<translation id="6865412394715372076">Táto karta sa momentálne nedá overiť</translation>
<translation id="6874604403660855544">&amp;Znova pridať</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Úroveň pravidla nie je podporovaná.</translation>
<translation id="6895330447102777224">Vaša karta je overená</translation>
<translation id="6897140037006041989">Používateľský agent</translation>
+<translation id="6903319715792422884">Pomôžte zlepšovať Bezpečné prehliadanie tak, že budete Googlu odosielať niektoré <ph name="BEGIN_WHITEPAPER_LINK" />informácie o systéme a obsah stránok<ph name="END_WHITEPAPER_LINK" />. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Používateľ:</translation>
+<translation id="6944692733090228304">Zadali ste heslo na webe, ktorý nie je spravovaný organizáciou <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Ak chcete, aby bol váš účet chránený, nepoužívajte jeho heslo pre iné aplikácie a weby.</translation>
<translation id="6945221475159498467">Vybrať</translation>
<translation id="6948701128805548767">Ak chcete zobraziť spôsoby a požiadavky vyzdvihnutia, vyberte adresu</translation>
<translation id="6949872517221025916">Obnovenie hesla</translation>
+<translation id="6950684638814147129">Chyba pri analyzovaní hodnoty JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Zdá sa, že certifikát servera je falošný.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Zariadenie</translation>
@@ -872,6 +871,7 @@
&lt;li&gt;Kliknite na možnosť &lt;strong&gt;Použiť&lt;/strong&gt;, potom na &lt;strong&gt;OK&lt;/strong&gt;.
&lt;li&gt;Ďalšie informácie o tom, ako natrvalo odstrániť softvér z počítača, nájdete v &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;centre pomoci Chromu&lt;/a&gt;.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Spravovať heslá…</translation>
<translation id="7419106976560586862">Cesta profilu</translation>
<translation id="7437289804838430631">Pridať kontaktné informácie</translation>
<translation id="7441627299479586546">Chybný predmet pravidla</translation>
@@ -880,7 +880,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Ďalšie informácie o tomto probléme<ph name="END_LINK" /></translation>
<translation id="7455133967321480974">Použiť predvolené všeobecné nastavenie (Blokovať)</translation>
<translation id="7460163899615895653">Vaše nedávne karty z iných zariadení sa zobrazia na tomto mieste</translation>
-<translation id="7469372306589899959">Overovanie karty</translation>
<translation id="7473891865547856676">Nie, ďakujem</translation>
<translation id="7481312909269577407">Dopredu</translation>
<translation id="7485870689360869515">Nenašli sa žiadne údaje.</translation>
@@ -1010,6 +1009,7 @@
<translation id="8308427013383895095">Preklad zlyhal v dôsledku problému so sieťovým pripojením.</translation>
<translation id="8311129316111205805">Načítať reláciu</translation>
<translation id="8332188693563227489">Prístup k webu <ph name="HOST_NAME" /> bol zamietnutý</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Ak si uvedomujete bezpečnostné riziko, môžete <ph name="BEGIN_LINK" />tieto stránky navštíviť<ph name="END_LINK" /> ešte skôr, ako budú škodlivé programy odstránené.</translation>
<translation id="8349305172487531364">Panel so záložkami</translation>
<translation id="8363502534493474904">Vypnúť režim v lietadle</translation>
@@ -1030,21 +1030,25 @@
<translation id="8498891568109133222">Web <ph name="HOST_NAME" /> príliš dlho neodpovedal.</translation>
<translation id="8503559462189395349">Heslá Chromu</translation>
<translation id="8503813439785031346">Meno používateľa</translation>
+<translation id="8508648098325802031">Ikona vyhľadávania</translation>
<translation id="8543181531796978784">Môžete buď <ph name="BEGIN_ERROR_LINK" />nahlásiť problém s zisťovaním<ph name="END_ERROR_LINK" />, alebo <ph name="BEGIN_LINK" />tieto nebezpečné stránky navštíviť<ph name="END_LINK" /> (ak si uvedomujete bezpečnostné riziko).</translation>
<translation id="8543556556237226809">Máte otázky? Obráťte sa na osobu, ktorá kontroluje váš profil.</translation>
<translation id="8553075262323480129">Prekladanie zlyhalo, pretože sa nepodarilo určiť jazyk stránky.</translation>
<translation id="8557066899867184262">Kód CVC nájdete na zadnej strane karty.</translation>
<translation id="8559762987265718583">Súkromné pripojenie k doméne <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> sa nedá nadviazať, pretože dátum a čas (<ph name="DATE_AND_TIME" />) vášho zariadenia sú nesprávne.</translation>
+<translation id="8564985650692024650">Ak ste heslo organizácie <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> použili aj na iných weboch, Chromium ho odporúča obnoviť.</translation>
<translation id="8571890674111243710">Prebieha preklad stránky do jazyka: <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Pridať telefón
</translation>
<translation id="859285277496340001">V certifikáte nie je uvedené, akým spôsobom sa má skontrolovať, či certifikát nebol odmietnutý.</translation>
+<translation id="860043288473659153">Meno držiteľa karty</translation>
<translation id="8620436878122366504">Vaši rodičia to zatiaľ neschválili</translation>
<translation id="8625384913736129811">Uložiť túto kartu do tohto zariadenia</translation>
-<translation id="8639963783467694461">Nastavenia automatického dopĺňania</translation>
+<translation id="8663226718884576429">Súhrn objednávky, <ph name="TOTAL_LABEL" />, ďalšie podrobnosti</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, odpoveď, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Vaše pripojenie k doméne <ph name="DOMAIN" /> sa nešifruje.</translation>
<translation id="8718314106902482036">Platba nebola dokončená</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, návrh vyhľadávania</translation>
<translation id="8725066075913043281">Skúsiť znova</translation>
<translation id="8728672262656704056">Ste v režime inkognito</translation>
<translation id="8730621377337864115">Hotovo</translation>
@@ -1060,8 +1064,10 @@
<translation id="8820817407110198400">Záložky</translation>
<translation id="883848425547221593">Iné záložky</translation>
<translation id="884264119367021077">Dodacia adresa</translation>
+<translation id="8846319957959474018">Otvárajte aplikácie ľahšie pomocou odkazov</translation>
<translation id="884923133447025588">Nenašiel sa žiadny mechanizmus rušenia certifikátov.</translation>
<translation id="885730110891505394">Zdieľanie s Googlom</translation>
+<translation id="8858065207712248076">Ak ste heslo organizácie <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> použili aj na iných weboch, Chrome ho odporúča obnoviť.</translation>
<translation id="8866481888320382733">Pri analýze nastavení pravidla sa vyskytla chyba</translation>
<translation id="8870413625673593573">Naposledy zatvorené</translation>
<translation id="8874824191258364635">Zadajte platné číslo karty</translation>
@@ -1100,6 +1106,7 @@
<translation id="9103872766612412690">Web <ph name="SITE" /> zvyčajne chráni vaše informácie pomocou šifrovania. Keď sa prehliadač Chromium tentokrát pokúsil pripojiť k webu <ph name="SITE" />, odoslal späť nezvyčajné a nesprávne poverenia. Môže sa to stať vtedy, keď sa za web <ph name="SITE" /> snaží vydávať útočník alebo keď pripojenie preruší prihlasovacia obrazovka siete Wi‑Fi. Vaše informácie sú stále zabezpečené, pretože prehliadač Chromium zastavil pripojenie ešte pred výmenou dát.</translation>
<translation id="9106062320799175032">Pridanie fakturačnej adresy</translation>
<translation id="910908805481542201">Pomôžte mi to vyriešiť</translation>
+<translation id="9114524666733003316">Overuje sa karta…</translation>
<translation id="9128870381267983090">Pripojiť k sieti</translation>
<translation id="9137013805542155359">Zobraziť originál</translation>
<translation id="9137248913990643158">Začnite a prihláste sa do Chromu ešte predtým, ako použijete túto aplikáciu.</translation>
@@ -1110,6 +1117,7 @@
<translation id="9168814207360376865">Povoliť webom zisťovať, či máte uložené spôsoby platby</translation>
<translation id="9169664750068251925">Vždy blokovať na tomto webe</translation>
<translation id="9170848237812810038">&amp;Naspäť</translation>
+<translation id="9171296965991013597">Opustiť aplikáciu?</translation>
<translation id="917450738466192189">Certifikát servera je neplatný.</translation>
<translation id="9183425211371246419">Web <ph name="HOST_NAME" /> využíva nepodporovaný protokol.</translation>
<translation id="9205078245616868884">Údaje sú šifrované pomocou vlastnej prístupovej frázy synchronizácie. Keď ju zadáte, synchronizácia sa spustí.</translation>
diff --git a/chromium/components/strings/components_strings_sl.xtb b/chromium/components/strings/components_strings_sl.xtb
index a70b418d20f..244f7096026 100644
--- a/chromium/components/strings/components_strings_sl.xtb
+++ b/chromium/components/strings/components_strings_sl.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Skrij vrednost</translation>
<translation id="1228893227497259893">Napačni identifikator subjekta</translation>
<translation id="1232569758102978740">Brez naslova</translation>
+<translation id="1250759482327835220">Če želite naslednjič hitreje plačati, shranite kartico, ime in naslov za izstavitev računa v Google Računu.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (sinhronizirano)</translation>
<translation id="1256368399071562588">&lt;p&gt;Če poskušate obiskati spletno mesto in se ne odpre, napako najprej poskusite odpraviti s tem postopkom za odpravljanje težav:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;V razdelku &lt;strong&gt;Splošno&lt;/strong&gt; aplikacije &lt;strong&gt;Nastavitve&lt;/strong&gt; prilagodite datum in uro.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Med prikazom spletne strani je prišlo do napake.</translation>
-<translation id="1590457302292452960">Ustvarite zapleteno geslo ...</translation>
<translation id="1592005682883173041">Dostop do lokalnih podatkov</translation>
<translation id="1594030484168838125">Izberi</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Poskusite se obrniti na skrbnika sistema.</translation>
<translation id="1740951997222943430">Vnesite veljaven mesec poteka veljavnosti</translation>
+<translation id="1743520634839655729">Če želite naslednjič hitreje plačati, shranite kartico, ime in naslov za izstavitev računa v Google Računu in v tej napravi.</translation>
<translation id="17513872634828108">Odpri zavihke</translation>
<translation id="1753706481035618306">Številka strani</translation>
<translation id="1763864636252898013">Strežniku ni uspelo dokazati, da je <ph name="DOMAIN" />; operacijski sistem vaše naprave ne zaupa njegovemu varnostnemu potrdilu. Razlog za to je lahko napačna konfiguracija ali napadalčevo prestrezanje povezave.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Tu so prikazani odprti zavihki</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Ime imetnika kartice</translation>
-<translation id="1806541873155184440">Dodano: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Neveljavna zahteva ali parametri zahteve</translation>
<translation id="1826516787628120939">Preverjanje</translation>
<translation id="1834321415901700177">Na tem spletnem mestu so škodljivi programi</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 predlog}one{# predlog}two{# predloga}few{# predlogi}other{# predlogov}}</translation>
<translation id="2079545284768500474">Razveljavi</translation>
<translation id="20817612488360358">Za uporabo so nastavljene sistemske nastavitve strežnika proxy, vendar je navedena tudi izrecna konfiguracija proxyja.</translation>
-<translation id="2084558088529668945">Vnesli ste geslo na spletnem mestu, ki ga ne upravlja <ph name="ORG_NAME" />. Zaradi zaščite računa gesla ne uporabljajte za druge aplikacije in spletna mesta.</translation>
<translation id="2091887806945687916">Zvok</translation>
<translation id="2094505752054353250">Neujemanje domen</translation>
<translation id="2096368010154057602">Departma</translation>
+<translation id="2102134110707549001">Predlagaj zapleteno geslo …</translation>
<translation id="2108755909498034140">Znova zaženite računalnik</translation>
<translation id="2113977810652731515">Kartica</translation>
<translation id="2114841414352855701">Prezrto, ker je to preglasil pravilnik <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Napaka HTTP</translation>
<translation id="2270484714375784793">Telefonska številka</translation>
<translation id="2292556288342944218">Internetni dostop je blokiran</translation>
-<translation id="230155334948463882">Nova kartica?</translation>
<translation id="2316887270356262533">Sprosti manj kot 1 MB. Nekatera spletna mesta se bodo ob naslednjem obisku morda počasneje naložila.</translation>
<translation id="2317259163369394535">Domena <ph name="DOMAIN" /> zahteva uporabniško ime in geslo.</translation>
<translation id="2317583587496011522">Sprejema debetne kartice.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Sprejete kartice</translation>
<translation id="2702801445560668637">Reading List</translation>
<translation id="2704283930420550640">Vrednost se ne ujema z obliko.</translation>
-<translation id="2704951214193499422">Chromium trenutno ni mogel potrditi vaše kartice. Poskusite znova pozneje.</translation>
<translation id="2705137772291741111">Shranjena (predpomnjena) kopija tega spletnega mesta je bila neberljiva.</translation>
<translation id="2709516037105925701">Samodejno izpolnjevanje</translation>
<translation id="2710942282213947212">Programska oprema v računalniku Chromiumu preprečuje vzpostavitev varne povezave s spletom</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Način pošiljanja</translation>
<translation id="277499241957683684">Manjka zapis o napravi</translation>
<translation id="2781030394888168909">Izvozi za macOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Povezava je bila obnovljena.</translation>
<translation id="2788784517760473862">Sprejete kreditne kartice</translation>
<translation id="2794233252405721443">Spletno mesto blokirano</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Namestniške strežnike, konfigurirane za povezavo, lahko onemogočite na strani z nastavitvami.</translation>
<translation id="2955913368246107853">Zapri vrstico za iskanje</translation>
<translation id="2958431318199492670">Omrežna konfiguracija ne ustreza standardu ONC. Deli konfiguracije morda niso bili uvoženi.</translation>
-<translation id="2966678944701946121">Datum poteka: <ph name="EXPIRATION_DATE_ABBR" />, dodano: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Če želite vzpostaviti varno povezavo, mora biti ura pravilno nastavljena. Potrdila, ki jih uporabljajo spletna mesta za prepoznavanje, namreč veljajo samo določen čas. Ker je ura naprave nepravilna, Google Chrome teh potrdil ne more preveriti.</translation>
<translation id="2972581237482394796">&amp;Uveljavi</translation>
<translation id="2977665033722899841">Trenutno izbrano: <ph name="ROW_NAME" />. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Napačna vrsta pravilnika</translation>
<translation id="3037605927509011580">Ti šment!</translation>
<translation id="3041612393474885105">Informacije o potrdilu</translation>
-<translation id="3063697135517575841">Chrome trenutno ni mogel potrditi vaše kartice. Poskusite znova pozneje.</translation>
<translation id="3064966200440839136">Zaradi plačila v zunanji aplikaciji boste zapustili način brez beleženja zgodovine. Želite nadaljevati?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Brez}=1{1 geslo}one{# geslo}two{# gesli}few{# gesla}other{# gesel}}</translation>
<translation id="3096100844101284527">Dodajanje naslova za prevzem</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Podatki so bili šifrirani z vašim geslom za sinhronizacijo <ph name="TIME" />. Vnesite ga, če želite začeti sinhronizacijo.</translation>
<translation id="3320021301628644560">Dodajanje naslova za izstavitev računa</translation>
<translation id="3338095232262050444">Varno</translation>
-<translation id="3340978935015468852">nastavitve</translation>
<translation id="3345135638360864351">Zahteve za dostop do tega spletnega mesta ni bilo mogoče poslati osebi <ph name="NAME" />. Poskusite znova.</translation>
<translation id="3355823806454867987">Spremeni nastavitve proxyja ...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />ne shrani<ph name="END_EMPHASIS" /> teh podatkov:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Potrdilo strežnika ni zaupanja vredno.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Vsaj en element v sinhroniziranih napravah}=1{1 element (in več v sinhroniziranih napravah)}one{# element (in več v sinhroniziranih napravah)}two{# elementa (in več v sinhroniziranih napravah)}few{# elementi (in več v sinhroniziranih napravah)}other{# elementov (in več v sinhroniziranih napravah)}}</translation>
<translation id="3539171420378717834">Ohrani kopijo te kartice v tej napravi</translation>
-<translation id="3542684924769048008">Uporaba gesla za:</translation>
<translation id="3549644494707163724">Šifrirajte vse sinhronizirane podatke s svojim geslom</translation>
<translation id="3556433843310711081">Skrbnik ga lahko odblokira</translation>
<translation id="3566021033012934673">Vaša povezava ni zasebna</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Neveljavni podpis za preverjanje</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{Še <ph name="ITEM_COUNT" /> element}one{Še <ph name="ITEM_COUNT" /> element}two{Še <ph name="ITEM_COUNT" /> elementa}few{Še <ph name="ITEM_COUNT" /> elementi}other{Še <ph name="ITEM_COUNT" /> elementov}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome priporoča, da ponastavite geslo za <ph name="ORG_NAME" />, če ste ga uporabljali na drugih spletnih mestih.</translation>
<translation id="4196861286325780578">&amp;Uveljavi premik</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />preveriti požarni zid in konfiguracije protivirusnega programa<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Zrušitve</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Spremembe, ki ste jih naredili, morda niso shranjene.</translation>
<translation id="4258748452823770588">Napačen podpis</translation>
<translation id="4265872034478892965">Omogočil skrbnik</translation>
-<translation id="4269787794583293679">(Ni uporabniškega imena)</translation>
<translation id="4275830172053184480">Znova zaženite napravo.</translation>
<translation id="4277028893293644418">Ponastavi geslo</translation>
<translation id="4280429058323657511">, datum poteka veljavnosti: <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Pojavna okna in preusmeritve</translation>
<translation id="443673843213245140">Uporaba strežnika proxy je onemogočena, vendar je njegova konfiguracija izrecno določena.</translation>
<translation id="445100540951337728">Sprejete debetne kartice</translation>
+<translation id="4472575034687746823">Kako začeti</translation>
<translation id="4506176782989081258">Napaka pri preverjanju veljavnosti: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">se obrniti na skrbnika sistema</translation>
<translation id="450710068430902550">Deljenje s skrbnikom</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Znova naloži pravilnike</translation>
<translation id="4728558894243024398">Okolje</translation>
<translation id="4736825316280949806">Znova zaženite Chromium</translation>
+<translation id="4742407542027196863">Upravljaj gesla …</translation>
<translation id="4744603770635761495">Pot do izvedljive datoteke</translation>
-<translation id="4749685221585524849">Zadnja uporaba: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Vaši podatki (npr. gesla ali številke kreditnih kartic) so zasebni, kadar so poslani temu spletnemu mestu.</translation>
<translation id="4756388243121344051">&amp;Zgodovina</translation>
<translation id="4758311279753947758">Dodaj podatke za stik</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Pogled</translation>
<translation id="4854362297993841467">Ta način pošiljanja ni na voljo. Poskusite uporabiti drugega.</translation>
<translation id="4858792381671956233">Starše si vprašal(-a), ali smeš obiskati to spletno mesto</translation>
+<translation id="4876305945144899064">Ni uporabniškega imena</translation>
<translation id="4880827082731008257">Zgodovina iskanja</translation>
<translation id="4881695831933465202">Odpri</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Zvezna država</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="5098332213681597508">To ime je iz vašega Google Računa.</translation>
<translation id="5115563688576182185">(64-bitno)</translation>
<translation id="5121084798328133320">Ko potrdite, bodo temu spletnemu mestu razkriti podatki o kreditni kartici iz računa za Google Payments.</translation>
<translation id="5128122789703661928">Seja s tem imenom ni veljavna za izbris.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Način pošiljanja</translation>
<translation id="5355557959165512791">Spletnega mesta <ph name="SITE" /> trenutno ni mogoče obiskati, saj je to potrdilo preklicano. Napake omrežja in napadi na omrežje so običajno začasni, zato bo ta stran verjetno delovala pozneje.</translation>
<translation id="536296301121032821">Nastavitev pravilnika ni bilo mogoče shraniti</translation>
+<translation id="5371425731340848620">Posodobite kartico</translation>
<translation id="5377026284221673050">Ura zaostaja« ali »Ura prehiteva« ali »&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;«</translation>
<translation id="5386426401304769735">Veriga potrdil za to spletno mesto vsebuje potrdilo, podpisano z algoritmom SHA-1.</translation>
-<translation id="5402410679244714488">Datum poteka: <ph name="EXPIRATION_DATE_ABBR" />, zadnja uporaba pred več kot enim letom</translation>
+<translation id="5387961145478138773">Hiter dostop do priljubljenih Googlovih aplikacij</translation>
<translation id="540969355065856584">Strežniku ni uspelo dokazati, da je <ph name="DOMAIN" />; njegovo varnostno potrdilo trenutno ni veljavno. Razlog za to je lahko napačna konfiguracija ali napadalčevo prestrezanje povezave.</translation>
<translation id="5421136146218899937">Izbriši podatke brskanja ...</translation>
<translation id="5430298929874300616">Odstrani zaznamek</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">E-pošta</translation>
+<translation id="5666899935841546222">Ali želite imeti vse kartice na enem mestu?</translation>
<translation id="5675650730144413517">Ta stran ne deluje</translation>
<translation id="5685654322157854305">Dodajanje naslova za pošiljanje</translation>
<translation id="5689199277474810259">Izvozi v JSON</translation>
<translation id="5689516760719285838">Lokacija</translation>
<translation id="570530837424789914">Upravljanje ...</translation>
+<translation id="57094364128775171">Predlagaj zapleteno geslo …</translation>
<translation id="5710435578057952990">Identiteta tega spletnega mesta ni bila potrjena.</translation>
<translation id="5719499550583120431">Sprejema predplačniške kartice.</translation>
<translation id="5720705177508910913">Trenutni uporabnik</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Tega spletnega mesta ni mogoče doseči</translation>
<translation id="5869522115854928033">Shranjena gesla</translation>
<translation id="5893752035575986141">Sprejema kreditne kartice.</translation>
-<translation id="5898382028489516745">Chromium priporoča, da ponastavite geslo za <ph name="ORG_NAME" />, če ste ga uporabljali tudi na drugih spletnih mestih.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (sinhronizirano)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 v uporabi}one{# v uporabi}two{# v uporabi}few{# v uporabi}other{# v uporabi}}</translation>
<translation id="5939518447894949180">Ponastavi</translation>
-<translation id="5959728338436674663">Samodejno pošlji Googlu nekatere <ph name="BEGIN_WHITEPAPER_LINK" />sistemske podatke in vsebino strani<ph name="END_WHITEPAPER_LINK" /> zaradi zaznavanja nevarnih aplikacij in spletnih mest. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Urejanje podatkov o stiku</translation>
<translation id="5967867314010545767">Odstrani iz zgodovine</translation>
<translation id="5975083100439434680">Pomanjšaj</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Poštna številka</translation>
<translation id="6290238015253830360">Tu so prikazani predlagani članki</translation>
<translation id="6305205051461490394">Naslov <ph name="URL" /> je nedosegljiv.</translation>
-<translation id="6319915415804115995">Zadnja uporaba pred več kot enim letom</translation>
<translation id="6321917430147971392">Preverite nastavitve za DNS</translation>
<translation id="6328639280570009161">Poskusite onemogočiti omrežno predvidevanje</translation>
<translation id="6328786501058569169">To spletno mesto je zavajajoče</translation>
<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="6342069812937806050">Pravkar</translation>
<translation id="6355080345576803305">Preglasitev javne seje</translation>
<translation id="6358450015545214790">Več o teh nastavitvah</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{in še 1 predlog}one{in še # predlog}two{in še # predloga}few{in še # predlogi}other{in še # predlogov}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Uveljavi izbris</translation>
<translation id="6534179046333460208">Predlogi za Fizični splet</translation>
<translation id="6550675742724504774">Možnosti</translation>
-<translation id="6556915248009097796">Datum poteka: <ph name="EXPIRATION_DATE_ABBR" />, zadnja uporaba: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Skrbnik še ni odobril</translation>
<translation id="6569060085658103619">Ogledujete si stran razširitve</translation>
<translation id="6596325263575161958">Možnosti šifriranja</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Poskusili ste dostopati do domene <ph name="DOMAIN" />, vendar ima strežnik potrdilo, podpisano s šibkim podpisnim algoritmom (kot je SHA-1). To pomeni, da so varnostne poverilnice, ki jih je poslal strežnik, morda ponarejene in strežnik morda ni tisti, ki ga pričakujete (morda komunicirate z napadalcem).</translation>
<translation id="6831043979455480757">Prevedi</translation>
<translation id="6839929833149231406">Območje</translation>
+<translation id="6852204201400771460">Želite znova naložiti aplikacijo?</translation>
+<translation id="6865412394715372076">Te kartice trenutno ni mogoče preveriti</translation>
<translation id="6874604403660855544">&amp;Uveljavi dodajanje</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Raven pravilnika ni podprta.</translation>
<translation id="6895330447102777224">Kartica je potrjena.</translation>
<translation id="6897140037006041989">Uporabnikov posrednik</translation>
+<translation id="6903319715792422884">S pošiljanjem nekaterih <ph name="BEGIN_WHITEPAPER_LINK" />informacij o sistemu in vsebine strani<ph name="END_WHITEPAPER_LINK" /> Googlu lahko pomagate izboljšati Varno brskanje. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Uporabnik:</translation>
+<translation id="6944692733090228304">Vnesli ste geslo na spletnem mestu, ki ga ne upravlja: <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Zaradi zaščite računa gesla ne uporabljajte za druge aplikacije in spletna mesta.</translation>
<translation id="6945221475159498467">Izberi</translation>
<translation id="6948701128805548767">Če si želite ogledati načine prevzema in zahteve, izberite naslov</translation>
<translation id="6949872517221025916">Ponastavitev gesla</translation>
+<translation id="6950684638814147129">Napaka pri razčlenjevanju vrednosti JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Potrdilo strežnika je očitno ponaredek.</translation>
<translation id="6965382102122355670">V redu</translation>
<translation id="6965978654500191972">Naprava</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Kliknite &lt;strong&gt;Uporabi&lt;/strong&gt; in nato &lt;strong&gt;V redu&lt;/strong&gt;.
&lt;li&gt;Obiščite &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;center za pomoč za Chrome&lt;/a&gt; če želite izvedeti, kako programsko opremo trajno odstranite iz računalnika.
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Upravljaj gesla …</translation>
<translation id="7419106976560586862">Pot profila</translation>
<translation id="7437289804838430631">Dodaj podatke o stiku</translation>
<translation id="7441627299479586546">Napačen subjekt pravilnika</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Preberite več<ph name="END_LINK" /> o tej težavi.</translation>
<translation id="7455133967321480974">Uporabi globalno privzeto (Blokiraj)</translation>
<translation id="7460163899615895653">Tu so prikazani nedavni zavihki iz drugih naprav</translation>
-<translation id="7469372306589899959">Potrjevanje kartice</translation>
<translation id="7473891865547856676">Ne, hvala</translation>
<translation id="7481312909269577407">Naprej</translation>
<translation id="7485870689360869515">Ni podatkov.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Prevod ni uspel zaradi težave s povezavo omrežja.</translation>
<translation id="8311129316111205805">Naloži sejo</translation>
<translation id="8332188693563227489">Dostop do spletnega mesta <ph name="HOST_NAME" /> je bil zavrnjen</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Če se zavedate varnostnega tveganja, lahko <ph name="BEGIN_LINK" />obiščete to spletno mesto<ph name="END_LINK" />, preden bodo škodljivi programi odstranjeni.</translation>
<translation id="8349305172487531364">Vrstica z zaznamki</translation>
<translation id="8363502534493474904">izklopiti način za letalo</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222">Spletno mesto <ph name="HOST_NAME" /> se ni odzvalo v ustreznem času.</translation>
<translation id="8503559462189395349">Gesla za Chrome</translation>
<translation id="8503813439785031346">Uporabniško ime</translation>
+<translation id="8508648098325802031">Ikona za iskanje</translation>
<translation id="8543181531796978784"><ph name="BEGIN_ERROR_LINK" />Prijavite lahko težavo z zaznavanjem<ph name="END_ERROR_LINK" />, če razumete varnostna tveganja, pa lahko <ph name="BEGIN_LINK" />obiščete to spletno mesto, ki ni varno<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Imate kakšno vprašanje? Obrnite se na osebo, ki nadzira vaš profil.</translation>
<translation id="8553075262323480129">Prevod ni uspel, ker ni mogoče določiti jezika strani.</translation>
<translation id="8557066899867184262">CVC je na zadnji strani kartice.</translation>
<translation id="8559762987265718583">Zasebne povezave z domeno <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ni mogoče vzpostaviti, ker sta datum in ura (<ph name="DATE_AND_TIME" />) v napravi nepravilna.</translation>
+<translation id="8564985650692024650">Chromium priporoča, da ponastavite geslo za <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, če ste ga uporabljali tudi na drugih spletnih mestih.</translation>
<translation id="8571890674111243710">Prevajanje strani v jezik <ph name="LANGUAGE" /> ...</translation>
<translation id="858637041960032120">Dodajte tel. št. </translation>
<translation id="859285277496340001">Potrdilo ne navaja mehanizma za preverjanje tega, ali je bilo preklicano.</translation>
+<translation id="860043288473659153">Ime imetnika kartice</translation>
<translation id="8620436878122366504">Starši še niso odobrili</translation>
<translation id="8625384913736129811">Shrani to kartico v tej napravi</translation>
-<translation id="8639963783467694461">Nastavitve samodejnega izpolnjevanja</translation>
+<translation id="8663226718884576429">Povzetek naročila, <ph name="TOTAL_LABEL" />, več podrobnosti</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, odgovor, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Vaša povezava z <ph name="DOMAIN" /> ni kodirana.</translation>
<translation id="8718314106902482036">Plačilo ni končano</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, predlog iskanja</translation>
<translation id="8725066075913043281">Poskusite znova</translation>
<translation id="8728672262656704056">Uporabljate način brez beleženja zgodovine</translation>
<translation id="8730621377337864115">Končano</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Zaznamki</translation>
<translation id="883848425547221593">Drugi zaznamki</translation>
<translation id="884264119367021077">Naslov za pošiljanje</translation>
+<translation id="8846319957959474018">Preprosto odpirajte aplikacije z zaznamki</translation>
<translation id="884923133447025588">Najden ni bil noben mehanizem za preklic.</translation>
<translation id="885730110891505394">Deljenje z Googlom</translation>
+<translation id="8858065207712248076">Chrome priporoča, da ponastavite geslo za <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, če ste ga uporabljali na drugih spletnih mestih.</translation>
<translation id="8866481888320382733">Napaka pri razčlenjevanju nastavitev pravilnika</translation>
<translation id="8870413625673593573">Nedavno zaprto</translation>
<translation id="8874824191258364635">Vnesite veljavno številko kartice</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690">Spletno mesto <ph name="SITE" /> za zaščito vaših podatkov običajno uporablja šifriranje. Ko se je Chromium tokrat poskusil povezati s spletnim mestom <ph name="SITE" />, je to vrnilo nenavadne in nepravilne poverilnice. Do tega lahko pride, če se napadalec lažno predstavlja za spletno mesto <ph name="SITE" /> ali če je povezavo prekinil zaslon za prijavo v omrežje Wi-Fi. Vaši podatki so še vedno varni, saj je Chromium pred izmenjavo podatkov prekinil povezavo.</translation>
<translation id="9106062320799175032">Dodajanje naslova za izstavitev računa</translation>
<translation id="910908805481542201">Pomagajte mi odpraviti težavo</translation>
+<translation id="9114524666733003316">Potrjevanje kartice …</translation>
<translation id="9128870381267983090">Vzpostavi povezavo z omrežjem</translation>
<translation id="9137013805542155359">Pokaži izvirno besedilo</translation>
<translation id="9137248913990643158">Začnite s prijavo v Chrome, preden začnete uporabljati to aplikacijo.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">Dovoli spletnim mestom, da preverijo, ali imate shranjena plačilna sredstva</translation>
<translation id="9169664750068251925">Vedno blokiraj na tem spletnem mestu</translation>
<translation id="9170848237812810038">&amp;Razveljavi</translation>
+<translation id="9171296965991013597">Želite zapustiti aplikacijo?</translation>
<translation id="917450738466192189">Potrdilo strežnika ni veljavno.</translation>
<translation id="9183425211371246419">Spletno mesto <ph name="HOST_NAME" /> uporablja nepodprt protokol.</translation>
<translation id="9205078245616868884">Podatki so šifrirani z vašim geslom za sinhronizacijo. Vnesite ga, če želite začeti sinhronizacijo.</translation>
diff --git a/chromium/components/strings/components_strings_sr.xtb b/chromium/components/strings/components_strings_sr.xtb
index d778b52ab92..e3812670b99 100644
--- a/chromium/components/strings/components_strings_sr.xtb
+++ b/chromium/components/strings/components_strings_sr.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Сакриј вредност</translation>
<translation id="1228893227497259893">Погрешан идентификатор ентитета</translation>
<translation id="1232569758102978740">Ненасловљено</translation>
+<translation id="1250759482327835220">Да бисте следећи пут платили брже, сачувајте картицу, име и адресу за обрачун на Google налогу.</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;
@@ -97,7 +98,6 @@
&lt;p&gt;Прилагодите датум и време у одељку &lt;strong&gt;Опште&lt;/strong&gt; у апликацији &lt;strong&gt;Подешавања&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Дошло је до грешке при приказивању ове веб-странице.</translation>
-<translation id="1590457302292452960">Генеришите јаку лозинку...</translation>
<translation id="1592005682883173041">Приступ локалним подацима</translation>
<translation id="1594030484168838125">Одабери</translation>
<translation id="1620510694547887537">Камера</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Покушајте да контактирате администратора система.</translation>
<translation id="1740951997222943430">Унесите важећи месец истека</translation>
+<translation id="1743520634839655729">Да бисте следећи пут платили брже, сачувајте картицу, име и адресу за обрачун на Google налогу и овом уређају.</translation>
<translation id="17513872634828108">Отворене картице</translation>
<translation id="1753706481035618306">Број странице</translation>
<translation id="1763864636252898013">Овај сервер не може да докаже да је <ph name="DOMAIN" />; оперативни систем уређаја нема поверења у његов безбедносни сертификат. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Отворене картице се појављују овде</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Име власника картице</translation>
-<translation id="1806541873155184440">Додато је: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Неважећи захтев или параметри захтева</translation>
<translation id="1826516787628120939">Провера</translation>
<translation id="1834321415901700177">Овај сајт садржи штетне програме</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 предлог}one{# предлог}few{# предлога}other{# предлога}}</translation>
<translation id="2079545284768500474">Опозови</translation>
<translation id="20817612488360358">Подешено је да се користе системска подешавања проксија, али је наведена експлицитна конфигурација проксија.</translation>
-<translation id="2084558088529668945">Унели сте лозинку на сајту којим не управља <ph name="ORG_NAME" />. Да бисте заштитили налог, не користите лозинку поново у другим апликацијама ни на другим сајтовима.</translation>
<translation id="2091887806945687916">Звук</translation>
<translation id="2094505752054353250">Домени се не подударају</translation>
<translation id="2096368010154057602">Департман</translation>
+<translation id="2102134110707549001">Предложи јаку лозинку…</translation>
<translation id="2108755909498034140">Поново покрените рачунар</translation>
<translation id="2113977810652731515">Картица</translation>
<translation id="2114841414352855701">Занемарују се јер су замењене смерницама <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP грешка</translation>
<translation id="2270484714375784793">Број телефона</translation>
<translation id="2292556288342944218">Приступ интернету је блокиран</translation>
-<translation id="230155334948463882">Нова картица?</translation>
<translation id="2316887270356262533">Ослобађа мање од 1 MB. Неки сајтови ће се можда спорије учитавати кад их следећи пут посетите.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> захтева корисничко име и лозинку.</translation>
<translation id="2317583587496011522">Прихватају се дебитне картице.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Картице које се прихватају</translation>
<translation id="2702801445560668637">Листа за читање</translation>
<translation id="2704283930420550640">Вредност се не подудара са форматом.</translation>
-<translation id="2704951214193499422">Chromium није успео да потврди картицу. Пробајте поново касније.</translation>
<translation id="2705137772291741111">Сачувана (кеширана) копија овог сајта није могла да се чита.</translation>
<translation id="2709516037105925701">Аутоматско попуњавање</translation>
<translation id="2710942282213947212">Софтвер на вашем рачунару онемогућава Chromium-у да се безбедно повеже на веб</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Начин слања</translation>
<translation id="277499241957683684">Недостаје евиденција уређаја</translation>
<translation id="2781030394888168909">Извези за Mac OS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Веза је враћена на почетне вредности.</translation>
<translation id="2788784517760473862">Кредитне картице које се прихватају</translation>
<translation id="2794233252405721443">Сајт је блокиран</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">На страници Подешавања можете да онемогућите све проксије конфигурисане за везу.</translation>
<translation id="2955913368246107853">Затворите траку за проналажење</translation>
<translation id="2958431318199492670">Конфигурација мреже није у складу са ONC стандардом. Делови конфигурације не могу да се увезу.</translation>
-<translation id="2966678944701946121">Истиче: <ph name="EXPIRATION_DATE_ABBR" />, додато је: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Да бисте успоставили безбедну везу, сат на уређају мора да буде тачан. То је зато што сертификати које веб-сајтови користе за идентификацију важе само за одређене временске периоде. Пошто сат на вашем уређају није тачан, Google Chrome не може да верификује те сертификате.</translation>
<translation id="2972581237482394796">&amp;Понови радњу</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, тренутно изабрано. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Погрешан тип смерница</translation>
<translation id="3037605927509011580">О, не!</translation>
<translation id="3041612393474885105">Информације о сертификату</translation>
-<translation id="3063697135517575841">Chrome није успео да потврди картицу. Пробајте поново касније.</translation>
<translation id="3064966200440839136">Напустићете режим без архивирања да бисте платили у спољној апликацији. Желите ли да наставите?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{None}=1{1 лозинка}one{# лозинка}few{# лозинке}other{# лозинки}}</translation>
<translation id="3096100844101284527">Додај адресу преузимања</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Подаци су шифровани помоћу приступне фразе за синхронизацију <ph name="TIME" />. Унесите је да бисте започели синхронизацију.</translation>
<translation id="3320021301628644560">Додајте адресу за обрачун</translation>
<translation id="3338095232262050444">Безбедан</translation>
-<translation id="3340978935015468852">подешавања</translation>
<translation id="3345135638360864351">Слање захтева за приступ овом сајту кориснику <ph name="NAME" /> није успело. Пробајте поново.</translation>
<translation id="3355823806454867987">Промени подешавања проксија...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />не чува<ph name="END_EMPHASIS" /> следеће информације:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Сертификат сервера није поуздан.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Најмање 1 ставка на синхронизованим уређајима}=1{1 ставка (и још ставки на синхронизованим уређајима)}one{# ставка (и још ставки на синхронизованим уређајима)}few{# ставке (и још ставки на синхронизованим уређајима)}other{# ставки (и још ставки на синхронизованим уређајима)}}</translation>
<translation id="3539171420378717834">Задржи копију ове картице на овом уређају</translation>
-<translation id="3542684924769048008">Користи лозинку за:</translation>
<translation id="3549644494707163724">Шифруј све синхронизоване податке помоћу моје приступне фразе за синхронизацију</translation>
<translation id="3556433843310711081">Менаџер може да га деблокира за вас</translation>
<translation id="3566021033012934673">Веза није приватна</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Неисправан потпис за верификацију</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{још <ph name="ITEM_COUNT" /> ставка}one{још <ph name="ITEM_COUNT" /> ставка}few{још <ph name="ITEM_COUNT" /> ставке}other{још <ph name="ITEM_COUNT" /> ставки}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome вам препоручује да ресетујете лозинку за <ph name="ORG_NAME" /> ако сте је користили на другим сајтовима.</translation>
<translation id="4196861286325780578">&amp;Понови премештање</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />да проверите конфигурацију заштитног зида и антивируса<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Отказивања</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Промене које сте унели можда неће бити сачуване.</translation>
<translation id="4258748452823770588">Неисправан потпис</translation>
<translation id="4265872034478892965">Дозвољава администратор</translation>
-<translation id="4269787794583293679">(Без корисничког имена)</translation>
<translation id="4275830172053184480">Поновно покретање уређаја</translation>
<translation id="4277028893293644418">Ресетујте лозинку</translation>
<translation id="4280429058323657511">, истиче <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Искачући прозори и преусмеравања</translation>
<translation id="443673843213245140">Коришћење проксија је онемогућено, али је наведена експлицитна конфигурација проксија.</translation>
<translation id="445100540951337728">Дебитне картице које се прихватају</translation>
+<translation id="4472575034687746823">Започнимо</translation>
<translation id="4506176782989081258">Грешка при потврди ваљаности: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">да контактирате администратора система</translation>
<translation id="450710068430902550">Дељење са администратором</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Поново учитај смернице</translation>
<translation id="4728558894243024398">Платформа</translation>
<translation id="4736825316280949806">Поново покрените Chromium</translation>
+<translation id="4742407542027196863">Управљај лозинкама...</translation>
<translation id="4744603770635761495">Путања извршне датотеке</translation>
-<translation id="4749685221585524849">Последњи пут коришћено: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Информације (на пример, лозинке или бројеви кредитних картица) су приватне када се шаљу овом сајту.</translation>
<translation id="4756388243121344051">&amp;Историја</translation>
<translation id="4758311279753947758">Додај контакт информације</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Приказ</translation>
<translation id="4854362297993841467">Овај начин испоруке није доступан. Испробајте неки други начин.</translation>
<translation id="4858792381671956233">Питао/ла си родитеље да ли смеш да посетиш овај сајт</translation>
+<translation id="4876305945144899064">Нема корисничког имена</translation>
<translation id="4880827082731008257">Претражи историју</translation>
<translation id="4881695831933465202">Отвори</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Држава</translation>
<translation id="5094747076828555589">Овај сервер не може да докаже да је <ph name="DOMAIN" />; Chromium нема поверења у његов безбедносни сертификат. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу.</translation>
<translation id="5095208057601539847">Провинција</translation>
+<translation id="5098332213681597508">То је име са вашег Google налога.</translation>
<translation id="5115563688576182185">(64-битни)</translation>
<translation id="5121084798328133320">Када будете потврдили, подаци о картици са налога за Google плаћања ће бити послати овом сајту.</translation>
<translation id="5128122789703661928">Сесија са овим називом није доступна за брисање.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Начин испоруке</translation>
<translation id="5355557959165512791">Тренутно не можете да посетите <ph name="SITE" /> јер је његов сертификат опозван. Грешке и напади на мрежи су обично привремени, па ће ова страница вероватно функционисати касније.</translation>
<translation id="536296301121032821">Складиштење подешавања смерница није успело</translation>
+<translation id="5371425731340848620">Ажурирајте картицу</translation>
<translation id="5377026284221673050">„Сат касни“ или „Сат жури“ или „&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;“</translation>
<translation id="5386426401304769735">Ланац сертификата за овај сајт садржи сертификат потписан помоћу алгоритма SHA-1.</translation>
-<translation id="5402410679244714488">Истиче: <ph name="EXPIRATION_DATE_ABBR" />, последњи пут коришћено пре више од годину дана</translation>
+<translation id="5387961145478138773">Брзо приступајте омиљеним Google апликацијама</translation>
<translation id="540969355065856584">Овај сервер не може да докаже да је <ph name="DOMAIN" />; његов безбедносни сертификат тренутно није важећи. Узрок томе је можда погрешна конфигурација или нападач који је прекинуо везу.</translation>
<translation id="5421136146218899937">Обриши податке прегледања...</translation>
<translation id="5430298929874300616">Уклоните обележивач</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">Имејл</translation>
+<translation id="5666899935841546222">Да ли желите да све картице буду на једном месту?</translation>
<translation id="5675650730144413517">Ова страница не функционише</translation>
<translation id="5685654322157854305">Додај адресу за испоруку</translation>
<translation id="5689199277474810259">Извези у JSON</translation>
<translation id="5689516760719285838">Локација</translation>
<translation id="570530837424789914">Управљајте...</translation>
+<translation id="57094364128775171">Предложи јаку лозинку…</translation>
<translation id="5710435578057952990">Идентитет овог веб-сајта није верификован.</translation>
<translation id="5719499550583120431">Прихватају се припејд картице.</translation>
<translation id="5720705177508910913">Тренутни корисник</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Овај сајт није доступан</translation>
<translation id="5869522115854928033">Сачуване лозинке</translation>
<translation id="5893752035575986141">Прихватају се кредитне картице.</translation>
-<translation id="5898382028489516745">Chromium вам препоручује да ресетујете лозинку за <ph name="ORG_NAME" /> ако сте је користили на другим сајтовима.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (синхронизовано)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{Користи се 1}one{Користи се #}few{Користе се #}other{Користи се #}}</translation>
<translation id="5939518447894949180">Ресетуј</translation>
-<translation id="5959728338436674663">Аутоматски шаљите одређене <ph name="BEGIN_WHITEPAPER_LINK" />информације о систему и садржај страница<ph name="END_WHITEPAPER_LINK" /> Google-у да бисте нам помогли да откријемо опасне апликације и сајтове. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Измените контакт информације</translation>
<translation id="5967867314010545767">Уклони из историје</translation>
<translation id="5975083100439434680">Умањивање</translation>
@@ -746,13 +743,11 @@
<translation id="6282194474023008486">Поштански број</translation>
<translation id="6290238015253830360">Предложени чланци се приказују овде</translation>
<translation id="6305205051461490394">URL <ph name="URL" /> није доступан.</translation>
-<translation id="6319915415804115995">Последњи пут коришћено пре више од годину дана</translation>
<translation id="6321917430147971392">Проверите DNS подешавања</translation>
<translation id="6328639280570009161">Покушајте да онемогућите предвиђање мреже</translation>
<translation id="6328786501058569169">Овај сајт је обмањујућ</translation>
<translation id="6337133576188860026">Ослобађа мање од <ph name="SIZE" />. Неки сајтови ће се можда спорије учитавати кад их следећи пут посетите.</translation>
<translation id="6337534724793800597">Филтрирај смернице према називу</translation>
-<translation id="6342069812937806050">Малопре</translation>
<translation id="6355080345576803305">Замена јавне сесије</translation>
<translation id="6358450015545214790">Шта ово значи?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{још 1 предлог}one{још # предлог}few{још # предлога}other{још # предлога}}</translation>
@@ -777,7 +772,6 @@
<translation id="6529602333819889595">&amp;Понови брисање</translation>
<translation id="6534179046333460208">Предлози Интернета око нас</translation>
<translation id="6550675742724504774">Опције</translation>
-<translation id="6556915248009097796">Истиче: <ph name="EXPIRATION_DATE_ABBR" />, последњи пут коришћено: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Менаџер га још увек није одобрио</translation>
<translation id="6569060085658103619">Прегледате страницу додатка.</translation>
<translation id="6596325263575161958">Опције шифровања</translation>
@@ -806,15 +800,20 @@
<translation id="6825578344716086703">Покушали сте да посетите <ph name="DOMAIN" />, али је сервер послао сертификат потписан слабим алгоритмом (као што је SHA-1). То значи да су безбедносни акредитиви које је сервер послао можда кривотворени и сервер можда није онај који мислите да јесте (можда комуницирате са нападачем).</translation>
<translation id="6831043979455480757">Преведи</translation>
<translation id="6839929833149231406">Област</translation>
+<translation id="6852204201400771460">Желите ли да поново учитате апликацију?</translation>
+<translation id="6865412394715372076">Тренутно не можемо да верификујемо ову картицу</translation>
<translation id="6874604403660855544">&amp;Понови додавање</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Ниво смерница није подржан.</translation>
<translation id="6895330447102777224">Картица је потврђена</translation>
<translation id="6897140037006041989">Кориснички агент</translation>
+<translation id="6903319715792422884">Побољшајте Безбедно прегледање слањем <ph name="BEGIN_WHITEPAPER_LINK" />системских информација и садржаја страница<ph name="END_WHITEPAPER_LINK" /> Google-у. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Корисник:</translation>
+<translation id="6944692733090228304">Унели сте лозинку на сајту којим не управља <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Да бисте заштитили налог, не користите лозинку поново у другим апликацијама ни на другим сајтовима.</translation>
<translation id="6945221475159498467">Изабери</translation>
<translation id="6948701128805548767">Да бисте видели начине и захтеве за преузимање, изаберите адресу</translation>
<translation id="6949872517221025916">Ресетујте лозинку</translation>
+<translation id="6950684638814147129">Грешка при рашчлањивању JSON вредности: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Изгледа да је сертификат сервера фалсификован.</translation>
<translation id="6965382102122355670">Потврди</translation>
<translation id="6965978654500191972">Уређај</translation>
@@ -876,6 +875,7 @@
&lt;li&gt;Прво кликните на &lt;strong&gt;Apply&lt;/strong&gt; (Примени), па на &lt;strong&gt;OK&lt;/strong&gt; (У реду)
&lt;li&gt;Посетите &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome центар за помоћ&lt;/a&gt; да бисте сазнали како да трајно уклоните софтвер са рачунара
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Управљај лозинкама...</translation>
<translation id="7419106976560586862">Путања профила</translation>
<translation id="7437289804838430631">Додај контакт информације</translation>
<translation id="7441627299479586546">Погрешан субјекат смерница</translation>
@@ -884,7 +884,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />да сазнате више<ph name="END_LINK" /> о овом проблему.</translation>
<translation id="7455133967321480974">Користи глобалну подразумевану вредност (Блокирај)</translation>
<translation id="7460163899615895653">Недавне картице са других уређаја се приказују овде</translation>
-<translation id="7469372306589899959">Потврђивање картице</translation>
<translation id="7473891865547856676">Не, хвала</translation>
<translation id="7481312909269577407">Проследи</translation>
<translation id="7485870689360869515">Нису пронађени подаци.</translation>
@@ -1014,6 +1013,7 @@
<translation id="8308427013383895095">Превођење није успело због проблема са мрежном везом.</translation>
<translation id="8311129316111205805">Учитај сесију</translation>
<translation id="8332188693563227489">Приступ хосту <ph name="HOST_NAME" /> је одбијен</translation>
+<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="8349305172487531364">Трака са обележивачима</translation>
<translation id="8363502534493474904">да искључите режим рада у авиону</translation>
@@ -1034,21 +1034,25 @@
<translation id="8498891568109133222">Одговор хоста <ph name="HOST_NAME" /> је трајао предуго.</translation>
<translation id="8503559462189395349">Лозинке за Chrome</translation>
<translation id="8503813439785031346">Корисничко име</translation>
+<translation id="8508648098325802031">Икона Претрага</translation>
<translation id="8543181531796978784">Можете да <ph name="BEGIN_ERROR_LINK" />пријавите проблем са откривањем<ph name="END_ERROR_LINK" /> или, ако схватате безбедносне ризике, <ph name="BEGIN_LINK" />посетите овај небезбедан сајт<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Имате питања? Контактирајте особу која вам надгледа профил.</translation>
<translation id="8553075262323480129">Превод није успео јер језик странице није могао да буде утврђен.</translation>
<translation id="8557066899867184262">CVC је на полеђини картице.</translation>
<translation id="8559762987265718583">Није могуће успоставити приватну везу са доменом <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> јер датум и време на уређају (<ph name="DATE_AND_TIME" />) нису тачни.</translation>
+<translation id="8564985650692024650">Chromium вам препоручује да ресетујете лозинку за <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> ако сте је користили на другим сајтовима.</translation>
<translation id="8571890674111243710">Превођење странице на <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Додај тел. број
</translation>
<translation id="859285277496340001">Сертификат не наводи механизам којим се проверава да ли је опозван.</translation>
+<translation id="860043288473659153">Име власника картице</translation>
<translation id="8620436878122366504">Родитељи га још увек нису одобрили</translation>
<translation id="8625384913736129811">Сачувај ову картицу на овом уређају</translation>
-<translation id="8639963783467694461">Подешавања Аутоматског попуњавања</translation>
+<translation id="8663226718884576429">Резиме поруџбине, <ph name="TOTAL_LABEL" />, још детаља</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, одговор, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Ваша веза са доменом <ph name="DOMAIN" /> није шифрована.</translation>
<translation id="8718314106902482036">Плаћање није довршено</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, предлог за претрагу</translation>
<translation id="8725066075913043281">Пробајте поново</translation>
<translation id="8728672262656704056">Прешли сте у режим Без архивирања</translation>
<translation id="8730621377337864115">Готово</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">Обележивачи</translation>
<translation id="883848425547221593">Остали обележивачи</translation>
<translation id="884264119367021077">Адреса за слање</translation>
+<translation id="8846319957959474018">Једноставно отварајте апликације помоћу обележивача</translation>
<translation id="884923133447025588">Није пронађен ниједан механизам опозива.</translation>
<translation id="885730110891505394">Дељење са Google-ом</translation>
+<translation id="8858065207712248076">Chrome вам препоручује да ресетујете лозинку за <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> ако сте је користили на другим сајтовима.</translation>
<translation id="8866481888320382733">Грешка при рашчлањивању подешавања смерница</translation>
<translation id="8870413625673593573">Недавно затворено</translation>
<translation id="8874824191258364635">Унесите важећи број картице</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> обично користи шифровање да би заштитио информације. Када је Chromium овог пута покушао да се повеже са <ph name="SITE" />, веб-сајт је вратио необичне и нетачне акредитиве. Или нападач покушава да се представи као <ph name="SITE" /> или је екран за Wi-Fi пријављивање прекинуо везу. Информације су и даље безбедне зато што је Chromium прекинуо везу пре него што су размењени било какви подаци.</translation>
<translation id="9106062320799175032">Додајте адресу за обрачун</translation>
<translation id="910908805481542201">Помозите ми да решим ово</translation>
+<translation id="9114524666733003316">Картица се потврђује...</translation>
<translation id="9128870381267983090">Повезивање са мрежом</translation>
<translation id="9137013805542155359">Прикажи оригинал</translation>
<translation id="9137248913990643158">Пријавите се у Chrome пре коришћења ове апликације.</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">Дозволите сајтовима да проверавају да ли имате сачуване начине плаћања</translation>
<translation id="9169664750068251925">Увек блокирај на овом сајту</translation>
<translation id="9170848237812810038">&amp;Опозови</translation>
+<translation id="9171296965991013597">Желите ли да изађете из апликације?</translation>
<translation id="917450738466192189">Сертификат сервера је неважећи.</translation>
<translation id="9183425211371246419">Хост <ph name="HOST_NAME" /> користи неподржани протокол.</translation>
<translation id="9205078245616868884">Подаци се шифрују помоћу приступне фразе за синхронизацију. Унесите је да бисте започели синхронизацију.</translation>
diff --git a/chromium/components/strings/components_strings_sv.xtb b/chromium/components/strings/components_strings_sv.xtb
index bfc504e22cd..738d9782559 100644
--- a/chromium/components/strings/components_strings_sv.xtb
+++ b/chromium/components/strings/components_strings_sv.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Dölj värde</translation>
<translation id="1228893227497259893">Fel enhetsidentifierare</translation>
<translation id="1232569758102978740">Namnlös</translation>
+<translation id="1250759482327835220">Spara kortet, faktureringsadressen och ditt namn i Google-kontot så går det snabbare att betala nästa gång.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (synkroniserade)</translation>
<translation id="1256368399071562588">&lt;p&gt;Om det inte går att öppna en webbplats testar du först att åtgärda felet med hjälp av dessa felsökningssteg:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Ändra datumet och tiden under &lt;strong&gt;Allmänt&lt;/strong&gt; i appen &lt;strong&gt;Inställningar&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Ett fel uppstod när webbsidan skulle visas.</translation>
-<translation id="1590457302292452960">Skapa ett starkt lösenord …</translation>
<translation id="1592005682883173041">Lokal dataåtkomst</translation>
<translation id="1594030484168838125">Välj</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Kontakta systemadministratören.</translation>
<translation id="1740951997222943430">Ange en giltig utgångsmånad</translation>
+<translation id="1743520634839655729">Spara kortet, faktureringsadressen och namnet i Google-kontot och på enheten så går det snabbare att betala nästa gång.</translation>
<translation id="17513872634828108">Öppna flikar</translation>
<translation id="1753706481035618306">Sidnummer</translation>
<translation id="1763864636252898013">Servern kunde inte bevisa att den är <ph name="DOMAIN" /> eftersom enhetens operativsystem inte litar på dess säkerhetscertifikat. Detta kan orsakas av en felaktig konfigurering eller att någon spärrar anslutningen.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Öppna flikar visas här</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Namn på kortinnehavare</translation>
-<translation id="1806541873155184440">Lades till den <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Begäran eller parametrar i begäran var ogiltiga</translation>
<translation id="1826516787628120939">Kontrollerar</translation>
<translation id="1834321415901700177">Den här webbplatsen innehåller skadliga program</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 förslag}other{# förslag}}</translation>
<translation id="2079545284768500474">Ångra</translation>
<translation id="20817612488360358">Datorns proxyinställningar är inställda på att användas, men det finns också en explicit proxykonfiguration.</translation>
-<translation id="2084558088529668945">Du har angett ditt lösenord på en webbplats som inte hanteras av <ph name="ORG_NAME" />. Skydda kontot genom att inte återanvända lösenordet för andra appar och webbplatser.</translation>
<translation id="2091887806945687916">Ljud</translation>
<translation id="2094505752054353250">Domänen matchar inte</translation>
<translation id="2096368010154057602">Departement</translation>
+<translation id="2102134110707549001">Föreslå ett starkt lösenord …</translation>
<translation id="2108755909498034140">Starta om datorn</translation>
<translation id="2113977810652731515">Kort</translation>
<translation id="2114841414352855701">Ignoreras eftersom den åsidosätts av <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP-fel</translation>
<translation id="2270484714375784793">Telefonnummer</translation>
<translation id="2292556288342944218">Internetanslutningen har blockerats</translation>
-<translation id="230155334948463882">Har du ett nytt kort?</translation>
<translation id="2316887270356262533">Frigör mindre än 1 MB. Vissa webbplatser kan läsas in långsammare nästa gång du besöker dem.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> kräver användarnamn och lösenord.</translation>
<translation id="2317583587496011522">Betalkort kan användas.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Godkända kort</translation>
<translation id="2702801445560668637">Läslista</translation>
<translation id="2704283930420550640">Värdet matchar inte formatet.</translation>
-<translation id="2704951214193499422">Chromium kunde inte bekräfta kortet. Försök igen senare.</translation>
<translation id="2705137772291741111">Det gick inte att läsa den sparade (cachelagrade) kopian av webbplatsen.</translation>
<translation id="2709516037105925701">Autofyll</translation>
<translation id="2710942282213947212">Programvara på datorn förhindrar att Chromium ansluts till internet på ett säkert sätt</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Fraktalternativ</translation>
<translation id="277499241957683684">Enhetsregister saknas</translation>
<translation id="2781030394888168909">Exportera för MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Anslutningen återställdes.</translation>
<translation id="2788784517760473862">Godkända kreditkort</translation>
<translation id="2794233252405721443">Webbplatsen har blockerats</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Du kan inaktivera alla proxyservrar som har konfigurerats för en anslutning från sidan Inställningar.</translation>
<translation id="2955913368246107853">Stäng sökfältet</translation>
<translation id="2958431318199492670">Nätverkskonfigurationen uppfyller inte ONC-standarden. Det kan hända att delar av konfigurationen inte kan importeras.</translation>
-<translation id="2966678944701946121">Giltigt till: <ph name="EXPIRATION_DATE_ABBR" />, lades till den <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Om du vill upprätta en säker anslutning måste klockan vara rätt inställd. Det beror på att certifikaten som webbplatserna använder för att identifiera sig har en bestämd giltighetstid. Google Chrome kan inte verifiera certifikaten eftersom klockan på enheten inte går rätt.</translation>
<translation id="2972581237482394796">&amp;Upprepa</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" /> är markerad just nu. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Felaktig policytyp</translation>
<translation id="3037605927509011580">Oj, ett fel har uppstått!</translation>
<translation id="3041612393474885105">Certifikatinformation</translation>
-<translation id="3063697135517575841">Det gick inte att bekräfta kortet. Försök igen senare.</translation>
<translation id="3064966200440839136">Om du betalar i ett externt program sker inte det i inkognitoläge. Vill du fortsätta?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Ingen}=1{1 lösenord}other{# lösenord}}</translation>
<translation id="3096100844101284527">Lägg till hämtningsadress</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Din data krypterades med din lösenfras för synkronisering den <ph name="TIME" />. Ange den om du vill starta synkroniseringen.</translation>
<translation id="3320021301628644560">Lägg till faktureringsadress</translation>
<translation id="3338095232262050444">Säker</translation>
-<translation id="3340978935015468852">inställningar</translation>
<translation id="3345135638360864351">Det gick inte att skicka begäran om åtkomst till den här webbplatsen till <ph name="NAME" />. Försök igen.</translation>
<translation id="3355823806454867987">Ändra proxyinställningar...</translation>
<translation id="3361596688432910856">Följande information <ph name="BEGIN_EMPHASIS" />sparas inte<ph name="END_EMPHASIS" /> i Chrome:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Servercertifikatet är inte tillförlitligt.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Minst 1 objekt på synkroniserade enheter}=1{1 objekt (och fler på synkroniserade enheter)}other{# objekt (och fler på synkroniserade enheter)}}</translation>
<translation id="3539171420378717834">Spara en kopia av kortet på enheten</translation>
-<translation id="3542684924769048008">Använd lösenord för:</translation>
<translation id="3549644494707163724">Kryptera alla synkroniserade data med en egen lösenfras</translation>
<translation id="3556433843310711081">En ansvarig kan ta bort blockeringen</translation>
<translation id="3566021033012934673">Anslutningen är inte privat</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Felaktig verifieringssignatur</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> objekt till}other{<ph name="ITEM_COUNT" /> objekt till}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Du rekommenderas att återställa lösenordet för <ph name="ORG_NAME" /> om du har återanvänt det på andra webbplatser.</translation>
<translation id="4196861286325780578">&amp;Gör om Flytta</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />kontrollera konfigureringarna för brandväggen och antivirusprogram<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Kraschar</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Ändringar som du har gjort kanske inte sparas.</translation>
<translation id="4258748452823770588">Felaktig signatur</translation>
<translation id="4265872034478892965">Beviljades av administratören</translation>
-<translation id="4269787794583293679">(Inget användarnamn)</translation>
<translation id="4275830172053184480">Starta om enheten</translation>
<translation id="4277028893293644418">Återställ lösenord</translation>
<translation id="4280429058323657511">, utgångsdatum <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Popup-fönster och omdirigeringar</translation>
<translation id="443673843213245140">Användning av proxy är inaktiverad men en explicit proxykonfiguration har angetts.</translation>
<translation id="445100540951337728">Godkända betalkort</translation>
+<translation id="4472575034687746823">Kom igång</translation>
<translation id="4506176782989081258">Valideringsfel: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">kontakta systemadministratören</translation>
<translation id="450710068430902550">Delad med en administratör</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Läs in policyer på nytt</translation>
<translation id="4728558894243024398">Plattform</translation>
<translation id="4736825316280949806">Starta om Chromium</translation>
+<translation id="4742407542027196863">Hantera lösenord …</translation>
<translation id="4744603770635761495">Sökväg till körbar fil</translation>
-<translation id="4749685221585524849">Användes senast den <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Dina uppgifter (till exempel lösenord eller kreditkortsnummer) är privata när de skickas till den här webbplatsen.</translation>
<translation id="4756388243121344051">&amp;Historik</translation>
<translation id="4758311279753947758">Lägg till kontaktuppgifter</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Visa</translation>
<translation id="4854362297993841467">Det här leveranssättet är inte tillgängligt. Testa ett annat alternativ.</translation>
<translation id="4858792381671956233">Du har frågat dina föräldrar om lov att besöka den här webbplatsen.</translation>
+<translation id="4876305945144899064">Inget användarnamn</translation>
<translation id="4880827082731008257">Sök i historiken</translation>
<translation id="4881695831933465202">Öppna</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Delstat</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="5098332213681597508">Det här namnet har hämtats från Google-kontot.</translation>
<translation id="5115563688576182185">(64 bitar)</translation>
<translation id="5121084798328133320">När du bekräftar delas kortuppgifter från Google Payments-konto med webbplatsen.</translation>
<translation id="5128122789703661928">Sessionen med det här namnet går inte att radera.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Fraktmetod</translation>
<translation id="5355557959165512791">Det går inte att besöka <ph name="SITE" /> just nu eftersom dess certifikat har återkallats. Nätverksfel och attacker är ofta tillfälliga, så sidan kommer förmodligen att fungera senare.</translation>
<translation id="536296301121032821">Det gick inte att spara policyinställningarna</translation>
+<translation id="5371425731340848620">Uppdatera kortet</translation>
<translation id="5377026284221673050">Klockan går efter, Klockan går före eller &lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;</translation>
<translation id="5386426401304769735">Certifikatkedjan för den här webbplatsen innehåller ett certifikat som signerades med SHA-1.</translation>
-<translation id="5402410679244714488">Giltigt till: <ph name="EXPIRATION_DATE_ABBR" />, användes senast för över ett år sedan</translation>
+<translation id="5387961145478138773">Få snabb åtkomst till dina favoritappar från Google</translation>
<translation id="540969355065856584">Servern kunde inte bevisa att det är <ph name="DOMAIN" />. Dess säkerhetscertifikat är inte giltigt för närvarande. Detta kan bero på en felaktig konfiguration eller att någon kapat din anslutning.</translation>
<translation id="5421136146218899937">Ta bort webbinformation...</translation>
<translation id="5430298929874300616">Ta bort bokmärke</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">E-post</translation>
+<translation id="5666899935841546222">Vill du samla alla kort på ett ställe?</translation>
<translation id="5675650730144413517">Sidan fungerar inte</translation>
<translation id="5685654322157854305">Lägg till leveransadress</translation>
<translation id="5689199277474810259">Exportera som JSON</translation>
<translation id="5689516760719285838">Plats</translation>
<translation id="570530837424789914">Hantera …</translation>
+<translation id="57094364128775171">Föreslå ett starkt lösenord …</translation>
<translation id="5710435578057952990">Webbplatsens identitet har inte verifierats.</translation>
<translation id="5719499550583120431">Förbetalda kort kan användas.</translation>
<translation id="5720705177508910913">Aktuell användare</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Webbplatsen kan inte nås</translation>
<translation id="5869522115854928033">Sparade lösenord</translation>
<translation id="5893752035575986141">Kreditkort får användas.</translation>
-<translation id="5898382028489516745">Du rekommenderas att återställa lösenordet för <ph name="ORG_NAME" /> om du har återanvänt det på andra webbplatser.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (synkroniserade)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 används}other{# används}}</translation>
<translation id="5939518447894949180">Återställ</translation>
-<translation id="5959728338436674663">Skicka automatiskt viss <ph name="BEGIN_WHITEPAPER_LINK" />information om systemet och innehåll på sidan<ph name="END_WHITEPAPER_LINK" /> för att hjälpa Google att identifiera skadliga appar och webbplatser. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Redigera kontaktuppgifter</translation>
<translation id="5967867314010545767">Ta bort från historiken</translation>
<translation id="5975083100439434680">Zooma ut</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Postnummer</translation>
<translation id="6290238015253830360">Rekommenderade artiklar visas här</translation>
<translation id="6305205051461490394"><ph name="URL" /> kan inte nås.</translation>
-<translation id="6319915415804115995">Användes senast för över ett år sedan</translation>
<translation id="6321917430147971392">Kontrollera DNS-inställningarna</translation>
<translation id="6328639280570009161">Prova att inaktivera nätverksförslag</translation>
<translation id="6328786501058569169">Den här webbplatsen är bedräglig</translation>
<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="6342069812937806050">Alldeles nyss</translation>
<translation id="6355080345576803305">Åsidosätt offentlig session</translation>
<translation id="6358450015545214790">Vad innebär dessa?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 annat förslag}other{# andra förslag}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Gör om Ta bort</translation>
<translation id="6534179046333460208">Förslag från Physical Web</translation>
<translation id="6550675742724504774">Alternativ</translation>
-<translation id="6556915248009097796">Giltigt till: <ph name="EXPIRATION_DATE_ABBR" />, användes senast den <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Den ansvarige har inte godkänt den ännu</translation>
<translation id="6569060085658103619">Du visar en tilläggssida</translation>
<translation id="6596325263575161958">Krypteringsalternativ</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Du försökte besöka <ph name="DOMAIN" />, men servern skickade ett certifikat som signerats med en svag signaturalgoritm (t.ex. SHA-1). Det innebär att säkerhetsuppgifterna som servern uppgav kan vara förfalskade och att servern kanske inte är den server du tror (du kanske kommunicerar med en skadlig server).</translation>
<translation id="6831043979455480757">Översätt</translation>
<translation id="6839929833149231406">Huvudområde</translation>
+<translation id="6852204201400771460">Vill du läsa in appen igen?</translation>
+<translation id="6865412394715372076">Det går inte att verifiera kortet just nu</translation>
<translation id="6874604403660855544">&amp;Gör om Lägg till</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" />, <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Policynivån stöds inte.</translation>
<translation id="6895330447102777224">Kortet har bekräftats</translation>
<translation id="6897140037006041989">Användaragent</translation>
+<translation id="6903319715792422884">Hjälp oss att förbättra Säker webbsökning genom att låta <ph name="BEGIN_WHITEPAPER_LINK" />viss systeminformation och visst sidinnehåll<ph name="END_WHITEPAPER_LINK" /> skickas automatiskt till Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Användare:</translation>
+<translation id="6944692733090228304">Du har angett ditt lösenord på en webbplats som inte hanteras av <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Skydda kontot genom att inte återanvända lösenordet i andra appar och webbplatser.</translation>
<translation id="6945221475159498467">Välj</translation>
<translation id="6948701128805548767">Välj en adress för att visa alternativ för utlämning och krav</translation>
<translation id="6949872517221025916">Återställ lösenord</translation>
+<translation id="6950684638814147129">Analysen av JSON-värdet misslyckades: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Serverns certifikat verkar vara falskt.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Enhet</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Klicka på &lt;strong&gt;Verkställ&lt;/strong&gt; och sedan på &lt;strong&gt;OK&lt;/strong&gt;
&lt;li&gt;Besök &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;hjälpcentret för Chrome&lt;/a&gt; om du vill veta mer om hur du tar bort programvaran från datorn permanent
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Hantera lösenord …</translation>
<translation id="7419106976560586862">Profilsökväg</translation>
<translation id="7437289804838430631">Lägg till kontaktuppgifter</translation>
<translation id="7441627299479586546">Felaktigt policyämne</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />att läsa mer<ph name="END_LINK" /> om det här problemet.</translation>
<translation id="7455133967321480974">Använd global standardinställning (Blockera)</translation>
<translation id="7460163899615895653">De senaste flikarna från andra enheter visas här</translation>
-<translation id="7469372306589899959">Kortet bekräftas</translation>
<translation id="7473891865547856676">Nej tack</translation>
<translation id="7481312909269577407">Framåt</translation>
<translation id="7485870689360869515">Ingen data hittades.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Det gick inte att översätta på grund av ett nätverksfel.</translation>
<translation id="8311129316111205805">Läs in session</translation>
<translation id="8332188693563227489">Åtkomst nekades till <ph name="HOST_NAME" />.</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Om du är medveten om säkerhetsriskerna kan du <ph name="BEGIN_LINK" />besöka den här osäkra webbplatsen<ph name="END_LINK" /> innan de skadliga programmen har tagits bort.</translation>
<translation id="8349305172487531364">Bokmärkesfältet</translation>
<translation id="8363502534493474904">inaktivera flygplansläget</translation>
@@ -1035,21 +1035,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> tog för lång tid på sig att svara.</translation>
<translation id="8503559462189395349">Lösenord i Chrome</translation>
<translation id="8503813439785031346">Användarnamn</translation>
+<translation id="8508648098325802031">Sökikon</translation>
<translation id="8543181531796978784">Du kan <ph name="BEGIN_ERROR_LINK" />rapportera ett identifieringsproblem<ph name="END_ERROR_LINK" /> eller <ph name="BEGIN_LINK" />besöka den här osäkra webbplatsen<ph name="END_LINK" /> om du är medveten om säkerhetsriskerna.</translation>
<translation id="8543556556237226809">Frågor? Kontakta den som kontrollerar profilen.</translation>
<translation id="8553075262323480129">Det gick inte att översätta eftersom det inte gick att avgöra vilket språk som användes på sidan.</translation>
<translation id="8557066899867184262">CVC-koden finns på baksidan av kortet.</translation>
<translation id="8559762987265718583">Det gick inte att upprätta en privat anslutning till <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> eftersom enhetens datum och tid (<ph name="DATE_AND_TIME" />) inte stämmer.</translation>
+<translation id="8564985650692024650">Du rekommenderas att återställa lösenordet för <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> om du har återanvänt det på andra webbplatser.</translation>
<translation id="8571890674111243710">Översätter sidan till <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Ange telefonnr
</translation>
<translation id="859285277496340001">Certifikatet har inte någon specificerad mekanism för att kontrollera om det har återkallats.</translation>
+<translation id="860043288473659153">Namn på kortinnehavare</translation>
<translation id="8620436878122366504">Dina föräldrar har inte godkänt den ännu</translation>
<translation id="8625384913736129811">Spara kortet på enheten</translation>
-<translation id="8639963783467694461">Inställningar för Autofyll</translation>
+<translation id="8663226718884576429">Beställningsöversikt, <ph name="TOTAL_LABEL" />, mer information</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, svar, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Din anslutning till <ph name="DOMAIN" /> är inte krypterad.</translation>
<translation id="8718314106902482036">Betalningen slutfördes inte</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, sökförslag</translation>
<translation id="8725066075913043281">Försök igen</translation>
<translation id="8728672262656704056">Du surfar inkognito</translation>
<translation id="8730621377337864115">Klart</translation>
@@ -1065,8 +1069,10 @@
<translation id="8820817407110198400">Bokmärken</translation>
<translation id="883848425547221593">Övriga bokmärken</translation>
<translation id="884264119367021077">Leveransadress</translation>
+<translation id="8846319957959474018">Öppna appar enkelt med bokmärken</translation>
<translation id="884923133447025588">Ingen återkallningsmekanism har hittats.</translation>
<translation id="885730110891505394">Delar med Google</translation>
+<translation id="8858065207712248076">Du rekommenderas att återställa lösenordet för <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> om du har återanvänt det på andra webbplatser.</translation>
<translation id="8866481888320382733">Det uppstod ett fel när policyinställningarna analyserades</translation>
<translation id="8870413625673593573">Nyligen stängda</translation>
<translation id="8874824191258364635">Ange ett giltigt kortnummer</translation>
@@ -1105,6 +1111,7 @@
<translation id="9103872766612412690">På <ph name="SITE" /> används normalt kryptering (SSL) för att skydda din information. När Chromium försökte ansluta till <ph name="SITE" /> den här gången skickade webbplatsen tillbaka ovanliga och felaktiga uppgifter. Sådant kan hända när en angripare utger sig för att vara <ph name="SITE" /> eller när anslutningen har avbrutits av en Wi-Fi-inloggningsskärm. Din information är fortfarande säker eftersom Chromium avbröt anslutningen innan någon data utbyttes.</translation>
<translation id="9106062320799175032">Lägg till faktureringsadress</translation>
<translation id="910908805481542201">Hjälp mig att åtgärda detta</translation>
+<translation id="9114524666733003316">Kortet kontrolleras …</translation>
<translation id="9128870381267983090">Anslut till ett nätverk</translation>
<translation id="9137013805542155359">Visa original</translation>
<translation id="9137248913990643158">Logga in på Chrome innan du använder den här appen.</translation>
@@ -1115,6 +1122,7 @@
<translation id="9168814207360376865">Tillåt att webbplatser kontrollerar om du har sparade betalningsmetoder</translation>
<translation id="9169664750068251925">Blockera alltid den här webbplatsen</translation>
<translation id="9170848237812810038">&amp;Ångra</translation>
+<translation id="9171296965991013597">Vill du lämna appen?</translation>
<translation id="917450738466192189">Servercertifikatet är ogiltigt.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> använder ett protokoll som inte stöds.</translation>
<translation id="9205078245616868884">Din data har krypterats med din lösenfras för synkronisering. Ange den om du vill starta synkroniseringen.</translation>
diff --git a/chromium/components/strings/components_strings_sw.xtb b/chromium/components/strings/components_strings_sw.xtb
index bc7d123f7ac..707f969bca8 100644
--- a/chromium/components/strings/components_strings_sw.xtb
+++ b/chromium/components/strings/components_strings_sw.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Ficha thamani</translation>
<translation id="1228893227497259893">Kitambulisho cha huluki kisicho halali</translation>
<translation id="1232569758102978740">Hakina Jina</translation>
+<translation id="1250759482327835220">Ili ulipe kwa haraka wakati ujao, hifadhi anwani ya kutuma bili, jina na maelezo ya kadi yako kwenye Akaunti yako ya Google.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (zimesawazishwa)</translation>
<translation id="1256368399071562588">&lt;p&gt;Ukijaribu kutembelea tovuti na haifunguki, jaribu kwanza kurekebisha hitilafu kwa kutumia hatua hizi za utatuzi:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Tafadhali rekebisha tarehe na wakati kutoka kwenye &lt;strong&gt;sehemu ya Jumla&lt;/strong&gt; ya &lt;strong&gt;programu ya Mipangilio&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Hitilafu ilitokea wakati wa kuonyesha ukurasa huu wa wavuti.</translation>
-<translation id="1590457302292452960">Weka nenosiri thabiti...</translation>
<translation id="1592005682883173041">Ufikiaji wa Data Iliyo Katika Kifaa Chako</translation>
<translation id="1594030484168838125">Chagua</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Kuwasiliana na msimamizi wa mfumo.</translation>
<translation id="1740951997222943430">Andika mwezi sahihi wa kuisha kwa muda wa matumizi</translation>
+<translation id="1743520634839655729">Ili ulipe kwa haraka wakati ujao, hifadhi anwani ya kutuma bili, jina na maelezo ya kadi yako kwenye Akaunti yako ya Google na kwenye kifaa hiki.</translation>
<translation id="17513872634828108">Vichupo vilivyo wazi</translation>
<translation id="1753706481035618306">Nambari ya ukurasa</translation>
<translation id="1763864636252898013">Seva hii haikuweza kuthibitisha kuwa ni <ph name="DOMAIN" />; cheti chake cha usalama hakiaminiwi na mfumo wa uendeshaji wa kifaa chako. Hii inaweza kusababishwa na usanidi usiofaa au mvamizi kuingilia muunganisho wako.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Vichupo vyako vilivyo wazi huonekana hapa</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Jina la mwenye kadi</translation>
-<translation id="1806541873155184440">Iliongezwa <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Ombi au vigezo vya ombi batili</translation>
<translation id="1826516787628120939">Inakagua</translation>
<translation id="1834321415901700177">Tovuti hii ina programu hatari</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{Pendekezo 1}other{Mapendekezo #}}</translation>
<translation id="2079545284768500474">Tendua</translation>
<translation id="20817612488360358">Mipangilio ya mfumo ya proksi imewekwa ili kutumiwa lakini usanidi dhahiri wa proksi pia umebainishwa.</translation>
-<translation id="2084558088529668945">Umeweka nenosiri kwenye tovuti ambayo haisimamiwi na <ph name="ORG_NAME" />. Ili ulinde akaunti yako, usitumie tena nenosiri lako kwenye tovuti na programu zingine.</translation>
<translation id="2091887806945687916">Sauti</translation>
<translation id="2094505752054353250">Kitolingana kwa kikoa</translation>
<translation id="2096368010154057602">Idara</translation>
+<translation id="2102134110707549001">Pendekeza Nenosiri Thabiti…</translation>
<translation id="2108755909498034140">Zima na uwashe kompyuta yako</translation>
<translation id="2113977810652731515">Kadi</translation>
<translation id="2114841414352855701">Imepuuzwa kwa sababu ilifutwa na <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Hitilfau ya HTTP</translation>
<translation id="2270484714375784793">Nambari ya simu</translation>
<translation id="2292556288342944218">Ufikiaji wako wa intaneti umezuiwa</translation>
-<translation id="230155334948463882">Je, ni kadi mpya?</translation>
<translation id="2316887270356262533">Huongeza nafasi isiyozidi MB 1. Baadhi ya tovuti huenda zikapakia polepole zaidi utakapozivinjari tena.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> inahitaji jina la mtumiaji na nenosiri.</translation>
<translation id="2317583587496011522">Kadi za malipo zinakubaliwa.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Kadi Zinazokubaliwa</translation>
<translation id="2702801445560668637">Orodha ya Kusoma</translation>
<translation id="2704283930420550640">Thamani haioani na umbizo.</translation>
-<translation id="2704951214193499422">Chromium haikuweza kuthibitisha kadi yako wakati huu. Tafadhali jaribu tena baadaye.</translation>
<translation id="2705137772291741111">Nakala iliyohifadhiwa (iliyowekwa katika akiba) ya tovuti hii haikusomeka.</translation>
<translation id="2709516037105925701">Kujaza Kiotomatiki</translation>
<translation id="2710942282213947212">Programu iliyo katika kompyuta yako inaizuia Chromium kuunganisha kwenye wavuti kwa njia salama</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Mbinu ya usafirishaji</translation>
<translation id="277499241957683684">Rekodi ya kifaa inayokosekana</translation>
<translation id="2781030394888168909">Hamisha katika muundo wa MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Muunganisho uliwekwa upya.</translation>
<translation id="2788784517760473862">Kadi za malipo zinazokubaliwa</translation>
<translation id="2794233252405721443">Tovuti imezuiwa</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Unaweza kuzima proksi zozote zilizosanidiwa kwa muunganisho kutoka kwenye ukurasa wa mipangilio.</translation>
<translation id="2955913368246107853">Funga upau wa kupata</translation>
<translation id="2958431318199492670">Usanidi wa mtandao hautii kiwango cha ONC. Sehemu za usanidi haziwezi kuingizwa.</translation>
-<translation id="2966678944701946121">Muda wa kutumika utakwisha: <ph name="EXPIRATION_DATE_ABBR" />, iliongezwa <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Ili kutambua muunganisho salama, saa yako inahitaji kusahihishwa. Hii ni kwa sababu vyeti ambavyo tovuti hutumia kujitambua ni sahihi kwa vipindi mahususi pekee. Kwa kuwa saa ya kifaa chako si sahihi, Google Chrome haiwezi kuthibitisha vyeti hivi.</translation>
<translation id="2972581237482394796">&amp;Rudia</translation>
<translation id="2977665033722899841">Umechagua <ph name="ROW_NAME" /> wakati huu. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Aina mbaya ya sera</translation>
<translation id="3037605927509011580">Lo!</translation>
<translation id="3041612393474885105">Maelezo ya Cheti</translation>
-<translation id="3063697135517575841">Chrome haikuweza kuthibitisha kadi yako wakati huu. Tafadhali jaribu tena baadaye.</translation>
<translation id="3064966200440839136">Inaacha hali fiche ili kulipa kupitia programu ya nje. Je, ungependa kuendelea?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Hamna}=1{Nenosiri 1}other{Manenosiri #}}</translation>
<translation id="3096100844101284527">Ongeza Anwani ya Mahali pa Kuchukulia Bidhaa</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Data yako ilisimbwa kwa njia fiche kwa kauli yako ya siri ya kusawazisha mnamo <ph name="TIME" />. Iweke ili uanze kusawazisha.</translation>
<translation id="3320021301628644560">Ongeza anwani ya kutuma bili</translation>
<translation id="3338095232262050444">Salama</translation>
-<translation id="3340978935015468852">mipangilio</translation>
<translation id="3345135638360864351">Ombi lako la kufikia tovuti hii halikutumwa kwa <ph name="NAME" />. Tafadhali jaribu tena.</translation>
<translation id="3355823806454867987">Badilisha mipangilio ya seva mbadala...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />haitahifadhi<ph name="END_EMPHASIS" /> maelezo yafuatayo:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Cheti cha seva hakiaminiki.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Angalau kipengee 1 kwenye vifaa vilivyosawazishwa}=1{Kipengee 1 (na zaidi kwenye vifaa vilivyosawazishwa)}other{Vipengee # (na zaidi kwenye vifaa vilivyosawazishwa)}}</translation>
<translation id="3539171420378717834">Weka nakala ya kadi hii kwenye kifaa hiki</translation>
-<translation id="3542684924769048008">Tumia nenosiri kwa:</translation>
<translation id="3549644494707163724">Simba kwa njia fiche data yote iliyosawazishwa kwa kaulisiri yako binafsi ya usawazishaji</translation>
<translation id="3556433843310711081">Msimamizi wako anaweza kukuondolea kizuizi</translation>
<translation id="3566021033012934673">Muunganisho wako si wa faragha</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Sahihi mbaya ya uthibitishaji</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{Kipengee kingine <ph name="ITEM_COUNT" />}other{Vipengee vingine <ph name="ITEM_COUNT" />}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome inapendekeza ubadilishe nenosiri lako la <ph name="ORG_NAME" /> ikiwa ulilitumia tena kwenye tovuti zingine.</translation>
<translation id="4196861286325780578">Rudia hatua</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Kuangalia mipangilio ya kinga-mtandao na kinga-virusi<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Mivurugo</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Huenda mabadiliko uliyofanya hayatahifadhiwa.</translation>
<translation id="4258748452823770588">Sahihi mbaya</translation>
<translation id="4265872034478892965">Imeruhusiwa na msimamizi wako</translation>
-<translation id="4269787794583293679">(Hakuna jina la mtumiaji)</translation>
<translation id="4275830172053184480">Washa upya kifaa chako</translation>
<translation id="4277028893293644418">Badilisha nenosiri</translation>
<translation id="4280429058323657511">, muda wa kutumika utakwisha <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Madirisha ibukizi/kuelekeza kwingine</translation>
<translation id="443673843213245140">Matumizi ya proksi yamelemazwa lakini usanidi wa proksi wazi umebainishwa.</translation>
<translation id="445100540951337728">Kadi za malipo zinazokubaliwa</translation>
+<translation id="4472575034687746823">Anza</translation>
<translation id="4506176782989081258">Hitilafu ya uthibitishaji: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Kuwasiliana na msimamizi wa mfumo</translation>
<translation id="450710068430902550">Kushiriki na Msimamizi</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Pakia sera upya</translation>
<translation id="4728558894243024398">Mfumo wa uendeshaji</translation>
<translation id="4736825316280949806">Zima na uwashe Chromium</translation>
+<translation id="4742407542027196863">Dhibiti manenosiri…</translation>
<translation id="4744603770635761495">Njia Tekelezi</translation>
-<translation id="4749685221585524849">Mara ya mwisho ilitumika <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Maelezo yako (kwa mfano, manenosiri, au nambari za kadi za mikopo) ni ya faragha yanapotumwa kwenye tovuti hii.</translation>
<translation id="4756388243121344051">&amp;Historia</translation>
<translation id="4758311279753947758">Ongeza maelezo ya mawasiliano</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Mwonekano</translation>
<translation id="4854362297993841467">Njia hii ya kusafirisha haitumiki. Jaribu njia tofauti.</translation>
<translation id="4858792381671956233">Umewaomba wazazi wako ruhusa ya kuutembelea ukurasa huu.</translation>
+<translation id="4876305945144899064">Hakuna jina la mtumiaji</translation>
<translation id="4880827082731008257">Tafuta katika historia</translation>
<translation id="4881695831933465202">Fungua</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Jimbo</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="5098332213681597508">Jina hili linatoka kwenye Akaunti yako ya Google.</translation>
<translation id="5115563688576182185">(biti 64)</translation>
<translation id="5121084798328133320">Baada ya kuthibitisha, maelezo ya kadi kutoka akaunti yako ya malipo ya Google yatashirikiwa na tovuti hii.</translation>
<translation id="5128122789703661928">Huruhusiwi kufuta kipindi kilicho na jina hili.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Mbinu ya Usafirishaji</translation>
<translation id="5355557959165512791">Huwezi kutembelea <ph name="SITE" /> sasa hivi kwa sababu cheti chake kimebatilishwa. Hitilafu na uvamizi wa mtandao kwa kawaida huwa wa muda, kwa hivyo huenda ukurasa huu utafanya kazi baadaye.</translation>
<translation id="536296301121032821">Imeshindwa kuhifadhi mipangilio ya sera</translation>
+<translation id="5371425731340848620">Badilisha maelezo ya kadi</translation>
<translation id="5377026284221673050">"Saa yako iko nyuma" au "Saa yako iko mbele" au "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">Msururu wa cheti wa tovuti hii una cheti kilichotiwa sahihi kwa kutumia SHA-1.</translation>
-<translation id="5402410679244714488">Muda wa kutumia utakwisha: <ph name="EXPIRATION_DATE_ABBR" />, ilitumika mwisho zaidi ya mwaka mmoja uliopita</translation>
+<translation id="5387961145478138773">Fikia kwa haraka Programu za Google unazopenda.</translation>
<translation id="540969355065856584">Seva hii haikuweza kuthibitisha kuwa ni <ph name="DOMAIN" />; cheti chake cha usalama si sahihi kwa sasa. Hii inaweza kusababishwa na usanidi usiofaa au mvamizi kuingilia muunganisho wako.</translation>
<translation id="5421136146218899937">Futa data ya kuvinjari...</translation>
<translation id="5430298929874300616">Ondoa alamisho</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">Barua pepe</translation>
+<translation id="5666899935841546222">Ungependa kupata kadi zako zote katika sehemu moja?</translation>
<translation id="5675650730144413517">Ukurasa huu haufanyi kazi</translation>
<translation id="5685654322157854305">Ongeza mahali zitakapopelekwa</translation>
<translation id="5689199277474810259">Tuma katika mfumo wa JSON</translation>
<translation id="5689516760719285838">Mahali</translation>
<translation id="570530837424789914">Dhibiti...</translation>
+<translation id="57094364128775171">Pendekeza nenosiri thabiti…</translation>
<translation id="5710435578057952990">Utambulisho wa tovuti hii haujathibitishwa.</translation>
<translation id="5719499550583120431">Kadi za kulipia awali zinakubaliwa.</translation>
<translation id="5720705177508910913">Mtumiaji wa sasa</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Imeshindwa kufungua tovuti hii</translation>
<translation id="5869522115854928033">Manenosiri yaliyohifadhiwa</translation>
<translation id="5893752035575986141">Kadi za mikopo zinakubaliwa.</translation>
-<translation id="5898382028489516745">Chromium inapendekeza ubadilishe nenosiri lako la <ph name="ORG_NAME" /> ikiwa ulilitumia tena kwenye tovuti zingine.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (imesawazishwa)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 kinatumika}other{ # vinatumika}}</translation>
<translation id="5939518447894949180">Weka upya</translation>
-<translation id="5959728338436674663">Tuma kiotomatiki <ph name="BEGIN_WHITEPAPER_LINK" />maelezo ya mfumo na maudhui kadha ya ukurasa<ph name="END_WHITEPAPER_LINK" /> kwa Google ili kusaidia kugundua programu na tovuti hatari. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Badilisha Maelezo ya Mawasiliano</translation>
<translation id="5967867314010545767">Ondoa kwenye historia</translation>
<translation id="5975083100439434680">Fifiza</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Msimbo wa posta</translation>
<translation id="6290238015253830360">Makala unayopendekezewa yataonekana hapa</translation>
<translation id="6305205051461490394"><ph name="URL" /> haiwezi kufikiwa.</translation>
-<translation id="6319915415804115995">Ilitumika mwisho zaidi ya mwaka mmoja uliopita</translation>
<translation id="6321917430147971392">Angalia mipangilio yako ya DNS</translation>
<translation id="6328639280570009161">Jaribu kuzima utabiri wa mtandao</translation>
<translation id="6328786501058569169">Tovuti hii ni ya udanganyifu</translation>
<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="6342069812937806050">Sasa hivi tu</translation>
<translation id="6355080345576803305">Kipindi cha umma kimebatilishwa</translation>
<translation id="6358450015545214790">Je, hii inamaanisha nini?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{Pendekezo jingine 1}other{Mapendekezo mengine #}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">Rudia Kufuta</translation>
<translation id="6534179046333460208">Mapendekezo ya Wavuti kila Mahali</translation>
<translation id="6550675742724504774">Chaguo</translation>
-<translation id="6556915248009097796">Muda wa kutumika utakwisha: <ph name="EXPIRATION_DATE_ABBR" />, mara ya mwisho ilitumika <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Msimamizi wako bado hajaiidhinisha</translation>
<translation id="6569060085658103619">Unaangalia ukurasa wa kiendelezi</translation>
<translation id="6596325263575161958">Chaguo za usimbaji fiche</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Umejaribu kufikia <ph name="DOMAIN" />, lakini seva iliwasilisha cheti kilichotiwa sahihi na kanuni duni. Hii inamaanisha kuwa stakabadhi za usalama zilizowasilishwa na seva hiyo huenda ni bandia na seva hiyo huenda ikawa si ile uliyotarajia (unaweza kuwa unawasiliana na mvamizi).</translation>
<translation id="6831043979455480757">Tafsiri</translation>
<translation id="6839929833149231406">Eneo</translation>
+<translation id="6852204201400771460">Ungependa kupakia programu upya?</translation>
+<translation id="6865412394715372076">Imeshindwa kuthibitisha kadi hii kwa sasa</translation>
<translation id="6874604403660855544">Rudia kuongeza</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Kiwango cha sera hakitumiki.</translation>
<translation id="6895330447102777224">Kadi yako imethibitishwa</translation>
<translation id="6897140037006041989">Programu ya Mtumiaji</translation>
+<translation id="6903319715792422884">Tusaidie tuboreshe huduma ya Kuvinjari Salama kwa kutuma baadhi ya <ph name="BEGIN_WHITEPAPER_LINK" />maudhui ya ukurasa na maelezo ya mfumo<ph name="END_WHITEPAPER_LINK" /> kwa Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Mtumiaji:</translation>
+<translation id="6944692733090228304">Uliweka nenosiri lako kwenye tovuti ambayo haisimamiwi na <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Ili kulinda akaunti yako, usitumie tena nenosiri lako kwenye programu na tovuti zingine.</translation>
<translation id="6945221475159498467">Chagua</translation>
<translation id="6948701128805548767">Chagua anwani ili uone mbinu za kuchukua na mahitaji</translation>
<translation id="6949872517221025916">Weka Nenosiri Jipya</translation>
+<translation id="6950684638814147129">Hitilafu fulani imetokea wakati wa kuchanganua thamani ya JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Cheti cha seva kinaonekana kuwa ghushi.</translation>
<translation id="6965382102122355670">Sawa</translation>
<translation id="6965978654500191972">Kifaa</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Bofya &lt;strong&gt;Tumia&lt;/strong&gt;, kisha ubofye &lt;strong&gt;SAWA&lt;/strong&gt;
&lt;li&gt;Tembelea &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Kituo cha usaidizi wa Chrome&lt;/a&gt; ili upate maelezo zaidi kuhusu jinsi ya kuondoa kabisa programu kwenye kompyuta yako
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Dhibiti Manenosiri…</translation>
<translation id="7419106976560586862">Kijia cha Maelezo mafupi</translation>
<translation id="7437289804838430631">Ongeza Maelezo ya Mawasiliano</translation>
<translation id="7441627299479586546">Kichwa cha sera kisichofaa</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Pata maelezo zaidi<ph name="END_LINK" /> kuhusu hitilafu hii.</translation>
<translation id="7455133967321480974">Tumia chaguomsingi la duniani (Zuia)</translation>
<translation id="7460163899615895653">Vichupo vyako vya hivi majuzi kutoka kwenye vifaa vingine vitaonekana hapa</translation>
-<translation id="7469372306589899959">Inathibitisha kadi</translation>
<translation id="7473891865547856676">La Asante</translation>
<translation id="7481312909269577407">Mbele</translation>
<translation id="7485870689360869515">Hakuna data iliyopatikana.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Utafsiri haukufanikiwa kwa sababu ya hitilafu ya seva.</translation>
<translation id="8311129316111205805">Pakia kipindi</translation>
<translation id="8332188693563227489">Ufikiaji wa <ph name="HOST_NAME" /> umekataliwa</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Ikiwa unaelewa hatari kwa usalama wako, unaweza <ph name="BEGIN_LINK" />kutembelea tovuti hii<ph name="END_LINK" /> kabla programu hatari hazijaondolewa.</translation>
<translation id="8349305172487531364">Sehemu ya Alamisho</translation>
<translation id="8363502534493474904">Kuzima hali ya ndegeni</translation>
@@ -1035,21 +1035,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> imechukua muda mrefu kupakia.</translation>
<translation id="8503559462189395349">Manenosiri ya Chrome</translation>
<translation id="8503813439785031346">Jina la mtumiaji</translation>
+<translation id="8508648098325802031">Aikoni ya Utafutaji</translation>
<translation id="8543181531796978784">Unaweza <ph name="BEGIN_ERROR_LINK" />kuripoti tatizo la ugunduzi<ph name="END_ERROR_LINK" /> au, ikiwa unaelewa kiwango cha hatari kinachoweza kutokea, <ph name="BEGIN_LINK" />tembelea tovuti hii isiyo salama<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Je, una maswali? Wasiliana na msimamizi wa wasifu wako.</translation>
<translation id="8553075262323480129">Tafsiri imeshindwa kwa sababu lugha ya ukurasa isingeweza kuthibitishwa.</translation>
<translation id="8557066899867184262">CVC inapatikana nyuma ya kadi yako.</translation>
<translation id="8559762987265718583">Muunganisho wa faragha kwenye <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> hauwezi kupatikana kwa sababu tarehe na wakati wa kifaa chako (<ph name="DATE_AND_TIME" />) si sahihi.</translation>
+<translation id="8564985650692024650">Chromium inapendekeza ubadilishe nenosiri lako la <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> ikiwa ulilitumia tena kwenye tovuti zingine.</translation>
<translation id="8571890674111243710">Inatafsiri ukurasa katika <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Ongeza simu
</translation>
<translation id="859285277496340001">Cheti hakibainishi utaratibu wa kuangalia iwapo kimekataliwa.</translation>
+<translation id="860043288473659153">Jina la mmiliki wa kadi</translation>
<translation id="8620436878122366504">Wazazi wako bado hawajaiidhinisha</translation>
<translation id="8625384913736129811">Hifadhi Maelezo ya Kadi Hii kwenye Kifaa Hiki</translation>
-<translation id="8639963783467694461">Mipangilio ya Kujaza Kiotomatiki</translation>
+<translation id="8663226718884576429">Muhtasari wa Agizo, <ph name="TOTAL_LABEL" />, Maelezo Zaidi</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, jibu, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Muunganisho wako kwa <ph name="DOMAIN" /> haujasimbwa.</translation>
<translation id="8718314106902482036">Malipo hayajakamilishwa</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" /> , <ph name="DESCRIPTION" /> , pendekezo la utafutaji</translation>
<translation id="8725066075913043281">Jaribu tena</translation>
<translation id="8728672262656704056">Unavinjari katika hali fiche</translation>
<translation id="8730621377337864115">Nimemaliza</translation>
@@ -1065,8 +1069,10 @@
<translation id="8820817407110198400">Alamisho</translation>
<translation id="883848425547221593">Alamisho Zingine</translation>
<translation id="884264119367021077">Anwani ya kusafirisha</translation>
+<translation id="8846319957959474018">Fungua programu kwa urahisi ukitumia alamisho</translation>
<translation id="884923133447025588">Mbinu ya ubatilishaji haikupatikana.</translation>
<translation id="885730110891505394">Kushiriki kwenye Google</translation>
+<translation id="8858065207712248076">Chrome inapendekeza ubadilishe nenosiri lako la <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> ikiwa ulilitumia tena kwenye tovuti zingine.</translation>
<translation id="8866481888320382733">Hitilafu wakati wa kuchanganua mipangilio ya sera</translation>
<translation id="8870413625673593573">Zilizofungwa Hivi Karibuni</translation>
<translation id="8874824191258364635">Andika nambari sahihi ya kadi</translation>
@@ -1105,6 +1111,7 @@
<translation id="9103872766612412690">Kwa kawaida <ph name="SITE" /> hutumia usimbaji fiche ili kulinda maelezo yako. Chromium ilipojaribu kuunganisha kwenye <ph name="SITE" /> wakati huu, tovuti ilituma kitambulisho kisicho cha kawaida na kisicho sahihi. Hili linaweza kutokea mvamizi anapojaribu kujifanya kuwa <ph name="SITE" />, au uchanganuzi wa kuingia katika Wi-Fi umeingilia muunganisho. Maelezo yako yangali salama kwa sababu Chromium ilisimamisha muunganisho kabla data yoyote itumwe.</translation>
<translation id="9106062320799175032">Ongeza Anwani ya Kutuma Bili</translation>
<translation id="910908805481542201">Nisaidie kutatua tatizo hili</translation>
+<translation id="9114524666733003316">Inathibitisha kadi…</translation>
<translation id="9128870381267983090">Unganisha kwenye mtandao</translation>
<translation id="9137013805542155359">Onyesha asili</translation>
<translation id="9137248913990643158">Tafadhali anza na uingie katika Chrome kabla ya kutumia programu hii.</translation>
@@ -1115,6 +1122,7 @@
<translation id="9168814207360376865">Ruhusu tovuti zikague ikiwa umehifadhi njia ya kulipa</translation>
<translation id="9169664750068251925">Zuia kila wakati kwenye tovuti hii</translation>
<translation id="9170848237812810038">&amp;Tendua</translation>
+<translation id="9171296965991013597">Ungependa kufunga programu?</translation>
<translation id="917450738466192189">Cheti cha seva ni batili.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> hutumia itifaki isiyokubalika.</translation>
<translation id="9205078245616868884">Data yako imesimbwa kwa njia fiche kwa kauli yako ya siri ya kusawazisha. Iweke ile uanze kusawazisha.</translation>
diff --git a/chromium/components/strings/components_strings_ta.xtb b/chromium/components/strings/components_strings_ta.xtb
index 1d54d6e4ac8..701dfd73d06 100644
--- a/chromium/components/strings/components_strings_ta.xtb
+++ b/chromium/components/strings/components_strings_ta.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">மதிப்பை மறை</translation>
<translation id="1228893227497259893">தவறான உட்பொருள் அடையாளங்காட்டி</translation>
<translation id="1232569758102978740">தலைப்பிடாதது</translation>
+<translation id="1250759482327835220">அடுத்த முறை விரைவாகப் பணம் செலுத்த, உங்கள் கார்டின் பெயரையும் பில்லிங் முகவரியையும் உங்கள் Google கணக்கில் சேமிக்கவும்.</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;
@@ -97,7 +98,6 @@
&lt;p&gt;தேதி மற்றும் நேரத்தை &lt;strong&gt;அமைப்புகள்&lt;/strong&gt; பயன்பாட்டின் &lt;strong&gt;பொது&lt;/strong&gt; என்கிற பகுதியில் மாற்றவும்.&lt;/p&gt;</translation>
<translation id="1583429793053364125">இந்த இணையப்பக்கத்தைக் காட்டும்போது ஏதோ தவறு ஏற்பட்டது.</translation>
-<translation id="1590457302292452960">வலிமையான கடவுச்சொல்லை உருவாக்கவும்...</translation>
<translation id="1592005682883173041">அகத் தரவு அணுகல்</translation>
<translation id="1594030484168838125">தேர்வுசெய்</translation>
<translation id="1620510694547887537">கேமரா</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">கணினி நிர்வாகியைத் தொடர்புகொள்ளவும்</translation>
<translation id="1740951997222943430">சரியான காலாவதி மாதத்தை உள்ளிடவும்</translation>
+<translation id="1743520634839655729">அடுத்த முறை விரைவாகப் பணம் செலுத்த, உங்கள் கார்டின் பெயரையும் பில்லிங் முகவரியையும் உங்கள் Google கணக்கிலும் இந்தச் சாதனத்திலும் சேமிக்கவும்.</translation>
<translation id="17513872634828108">தாவல்களைத் திற</translation>
<translation id="1753706481035618306">பக்க எண்</translation>
<translation id="1763864636252898013">இது <ph name="DOMAIN" /> தான் என்பதை இந்தச் சேவையகம் உறுதிப்படுத்தவில்லை; இதன் பாதுகாப்புச் சான்றிதழை உங்கள் சாதனத்தின் இயக்க முறைமை நம்பவில்லை. இது தவறான உள்ளமைவால் ஏற்பட்டிருக்கலாம் அல்லது தீங்கிழைப்பவர் உங்கள் இணைப்பில் குறுக்கிட்டிருக்கலாம்.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">உங்கள் தாவல்கள் இங்கே தோன்றும்</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">கார்டு உரிமையாளரின் பெயர்</translation>
-<translation id="1806541873155184440">சேர்த்தது: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">தவறான கோரிக்கை அல்லது கோரிக்கை அளவுருக்கள்</translation>
<translation id="1826516787628120939">சரிபார்க்கிறது</translation>
<translation id="1834321415901700177">இந்தத் தளத்தில் தீங்கிழைக்கும் நிரல்கள் உள்ளன</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 பரிந்துரை}other{# பரிந்துரைகள்}}</translation>
<translation id="2079545284768500474">செயல்தவிர்</translation>
<translation id="20817612488360358">கணினி ப்ராக்ஸி அமைப்புகள் பயன்படுத்த அமைக்கப்பட்டுள்ளது. வெளிப்படையான ப்ராக்ஸி உள்ளமைவும் குறிப்பிடப்பட்டுள்ளது.</translation>
-<translation id="2084558088529668945"><ph name="ORG_NAME" /> நிர்வகிக்காத தளத்தில் உங்கள் கடவுச்சொல்லை உள்ளிட்டுள்ளீர்கள். உங்கள் கணக்கைப் பாதுகாக்க, பிற பயன்பாடுகள் மற்றும் தளங்களில் உங்கள் கடவுச்சொல்லை மீண்டும் பயன்படுத்த வேண்டாம்.</translation>
<translation id="2091887806945687916">ஒலி</translation>
<translation id="2094505752054353250">டொமைன் பொருந்தவில்லை</translation>
<translation id="2096368010154057602">துறை</translation>
+<translation id="2102134110707549001">வலுவான கடவுச்சொல்லைப் பரிந்துரைசெய்…</translation>
<translation id="2108755909498034140">கணினியை மீண்டும் தொடங்கவும்</translation>
<translation id="2113977810652731515">கார்டு</translation>
<translation id="2114841414352855701"><ph name="POLICY_NAME" /> ஆல் கொள்கை மேலெழுதப்பட்டுள்ளதால் புறக்கணிக்கப்பட்டது.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP பிழை</translation>
<translation id="2270484714375784793">தொலைபேசி எண்</translation>
<translation id="2292556288342944218">உங்கள் இணைய அணுகல் தடுக்கப்பட்டது</translation>
-<translation id="230155334948463882">புதிய அட்டையா?</translation>
<translation id="2316887270356262533">1 மெ.பை. அளவிற்கும் குறைவான இடத்தைக் காலியாக்கும். நீங்கள் அடுத்த முறை பார்வையிடும் போது, சில தளங்கள் மிகவும் மெதுவாக ஏற்றப்படலாம்.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" />க்குப் பயனர்பெயரும் கடவுச்சொல்லும் தேவை.</translation>
<translation id="2317583587496011522">டெபிட் கார்டுகள் ஏற்கப்படுகின்றன.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">ஏற்கப்படும் கார்டுகள்</translation>
<translation id="2702801445560668637">வாசிப்புப் பட்டியல்</translation>
<translation id="2704283930420550640">மதிப்பானது வடிவமைப்பிற்குப் பொருந்தவில்லை.</translation>
-<translation id="2704951214193499422">இப்போது உங்கள் கார்டை உறுதிசெய்ய முடியவில்லை. பிறகு முயலவும்.</translation>
<translation id="2705137772291741111">இந்தத் தளத்தின் சேமிக்கப்பட்ட (தற்காலிகச் சேமிப்பு) நகலைப் படிக்க முடியவில்லை.</translation>
<translation id="2709516037105925701">தானாகநிரப்பு</translation>
<translation id="2710942282213947212">உங்கள் கணினியில் உள்ள மென்பொருளானது, இணையத்துடன் Chromium பாதுகாப்பாக இணைவதை நிறுத்துகிறது</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">ஷிப்பிங் முறை</translation>
<translation id="277499241957683684">சாதனப் பதிவு இல்லை</translation>
<translation id="2781030394888168909">MacOS வடிவமைப்பில் பதிவிறக்கு</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">இணைப்பு மீட்டமைக்கப்பட்டது.</translation>
<translation id="2788784517760473862">ஏற்கப்படும் கிரெடிட் கார்டுகள்</translation>
<translation id="2794233252405721443">தளம் தடுக்கப்பட்டது</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">இணைப்பிற்காக உள்ளமைத்த எந்த பிராக்சிகளையும் நீங்கள் அமைப்புகள் பக்கத்திலிருந்து முடக்கலாம்.</translation>
<translation id="2955913368246107853">தேடல் பெட்டியை மூடுக</translation>
<translation id="2958431318199492670">பிணைய உள்ளமைப்பானது ONC தரத்துடன் இணங்கவில்லை. உள்ளமைவின் பகுதிகள் இறக்குமதியாகாமல் போகக்கூடும்.</translation>
-<translation id="2966678944701946121">காலாவதி: <ph name="EXPIRATION_DATE_ABBR" />, சேர்த்தது: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">பாதுகாப்பான இணைப்பை ஏற்படுத்த, கடிகாரம் சரியாக அமைக்கப்பட வேண்டும். இணையதளங்கள் தங்களைத் தாமே அடையாளப்படுத்தப் பயன்படுத்தப்படும் சான்றிதழ்கள் குறிப்பிட்ட காலத்திற்கு மட்டும் செல்லுபடியாவதால், இது செய்யப்பட வேண்டும். உங்கள் சாதனத்தின் கடிகாரம் தவறாக இருப்பதால், இந்தச் சான்றிதழ்களை Google Chrome ஆல் சரிபார்க்க முடியவில்லை.</translation>
<translation id="2972581237482394796">&amp;மீண்டும் செய்</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, தற்போது தேர்ந்தெடுக்கப்பட்டது. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">தவறான கொள்கை வகை</translation>
<translation id="3037605927509011580">அச்சச்சோ!</translation>
<translation id="3041612393474885105">சான்றிதழ் தகவல்</translation>
-<translation id="3063697135517575841">இப்போது உங்கள் கார்டை உறுதிசெய்ய முடியவில்லை. பிறகு முயலவும்.</translation>
<translation id="3064966200440839136">வெளிப்புறப் பயன்பாட்டின் மூலம் பணத்தை செலுத்த, மறைநிலையிலிருந்து வெளியேறுகிறீர்கள். தொடரவா?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{ஏதுமில்லை}=1{1 கடவுச்சொல்}other{# கடவுச்சொற்கள்}}</translation>
<translation id="3096100844101284527">பிக்அப் முகவரியைச் சேர்</translation>
@@ -338,7 +335,6 @@
<translation id="3305707030755673451"><ph name="TIME" /> அன்று உங்கள் தரவு உங்கள் ஒத்திசைவு கடவுச்சொற்றொடரைக் கொண்டு முறைமையாக்கப்பட்டது. ஒத்திசைவைத் தொடங்க, அதை உள்ளிடவும்.</translation>
<translation id="3320021301628644560">பில்லிங் முகவரியைச் சேர்க்கவும்</translation>
<translation id="3338095232262050444">பாதுகாப்பானது</translation>
-<translation id="3340978935015468852">அமைப்புகள்</translation>
<translation id="3345135638360864351">இந்தத் தளத்தை அணுகுவதற்கான கோரிக்கையை <ph name="NAME" />க்கு அனுப்ப முடியவில்லை. மீண்டும் முயற்சிக்கவும்.</translation>
<translation id="3355823806454867987">ப்ராக்ஸி அமைப்புகளை மாற்றுக...</translation>
<translation id="3361596688432910856">பின்வரும் தகவலை Chrome <ph name="BEGIN_EMPHASIS" />சேமிக்காது<ph name="END_EMPHASIS" />:
@@ -372,7 +368,6 @@
<translation id="3528171143076753409">சேவையகச் சான்றிதழ் நம்பப்படவில்லை.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{ஒத்திசைத்த சாதனங்களில் குறைந்தது 1 உருப்படி உள்ளது}=1{1 உருப்படி (ஒத்திசைத்த சாதனங்களில் இதற்கு மேல் உள்ளன)}other{# உருப்படிகள் (ஒத்திசைத்த சாதனங்களில் இதற்கு மேல் உள்ளன)}}</translation>
<translation id="3539171420378717834">இந்தக் கார்டின் பிரதியை சாதனத்தில் சேமி</translation>
-<translation id="3542684924769048008">இதற்கான கடவுச்சொல்லைப் பயன்படுத்தவும்:</translation>
<translation id="3549644494707163724">உங்கள் சொந்த ஒத்திசைவு கடவுச்சொற்றொடர் மூலம் எல்லா தரவையும் என்க்ரிப்ட் செய்யவும்</translation>
<translation id="3556433843310711081">உங்களுக்காக, தளத்தின் தடுப்பை உங்கள் நிர்வாகி நீக்க முடியும்</translation>
<translation id="3566021033012934673">உங்கள் இணைப்பு தனிப்பட்டது அல்ல</translation>
@@ -458,7 +453,6 @@
<translation id="4171400957073367226">தவறான சரிபார்ப்பு கையொப்பம்</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{மேலும் <ph name="ITEM_COUNT" /> உருப்படி}other{மேலும் <ph name="ITEM_COUNT" /> உருப்படிகள்}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">பிற தளங்களில் உங்கள் <ph name="ORG_NAME" /> கடவுச்சொல்லை மீண்டும் பயன்படுத்தினால், அதை மீட்டமைக்கும்படி Chrome பரிந்துரைக்கும்.</translation>
<translation id="4196861286325780578">&amp;நகர்த்தலை மீண்டும் செய்</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ஃபயர்வால் மற்றும் ஆண்டிவைரஸ் உள்ளமைவைச் சரிபார்த்தல்<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">செயலிழப்புகள்</translation>
@@ -487,7 +481,6 @@
<translation id="425582637250725228">உங்கள் மாற்றங்கள் சேமிக்கப்படாமல் இருக்கலாம்.</translation>
<translation id="4258748452823770588">தவறான கையொப்பம்</translation>
<translation id="4265872034478892965">உங்கள் நிர்வாகி அனுமதித்தார்</translation>
-<translation id="4269787794583293679">(பயனர்பெயர் இல்லை)</translation>
<translation id="4275830172053184480">உங்கள் சாதனத்தை மீண்டும் தொடங்கவும்</translation>
<translation id="4277028893293644418">கடவுச்சொல்லை மீட்டமை</translation>
<translation id="4280429058323657511">, காலாவதித் தேதி: <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -510,6 +503,7 @@
<translation id="4434045419905280838">பாப் அப்கள் &amp; திசைதிருப்புதல்கள்</translation>
<translation id="443673843213245140">ப்ராக்ஸி பயன்பாடு முடக்கப்பட்டுள்ளது. ஆனால் வெளிப்படையான ப்ராக்ஸி உள்ளமைவு குறிப்பிடப்பட்டுள்ளது.</translation>
<translation id="445100540951337728">ஏற்கப்படும் டெபிட் கார்டுகள்</translation>
+<translation id="4472575034687746823">தொடங்குக</translation>
<translation id="4506176782989081258">சரிபார்ப்புப் பிழை: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">கணினி நிர்வாகியைத் தொடர்புகொள்ளுதல்</translation>
<translation id="450710068430902550">நிர்வாகியுடன் பகிர்பவை</translation>
@@ -534,8 +528,8 @@
<translation id="4726672564094551039">கொள்கைகளை மீண்டும் ஏற்று</translation>
<translation id="4728558894243024398">ப்ளாட்ஃபார்ம்</translation>
<translation id="4736825316280949806">Chromiumஐ மீண்டும் தொடங்கவும்</translation>
+<translation id="4742407542027196863">கடவுச்சொற்களை நிர்வகி…</translation>
<translation id="4744603770635761495">இயக்கநிரல் பாதை</translation>
-<translation id="4749685221585524849">கடைசியாகப் பயன்படுத்தியது: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">உங்கள் தகவலை (எடுத்துக்காட்டு: கடவுச்சொற்கள் அல்லது கிரெடிட் கார்டு எண்கள்) இந்தத் தளத்திற்கு அனுப்பும் போது, தனிப்பட்டதாக இருக்கும்.</translation>
<translation id="4756388243121344051">&amp;வரலாறு</translation>
<translation id="4758311279753947758">தொடர்புத் தகவலைச் சேர்</translation>
@@ -552,6 +546,7 @@
<translation id="4850886885716139402">காட்சி</translation>
<translation id="4854362297993841467">இந்த டெலிவரி முறை இல்லை. வேறு முறையைப் பயன்படுத்திப் பார்க்கவும்.</translation>
<translation id="4858792381671956233">இந்தத் தளத்தைப் பார்வையிடலாமா என, நீங்கள் பெற்றோரிடம் கேட்டுள்ளீர்கள்</translation>
+<translation id="4876305945144899064">பயனர்பெயர் இல்லை</translation>
<translation id="4880827082731008257">வரலாற்றில் தேடு</translation>
<translation id="4881695831933465202">திற</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -577,7 +572,7 @@
<translation id="5039804452771397117">அனுமதி</translation>
<translation id="5040262127954254034">தனியுரிமை</translation>
<translation id="5045550434625856497">தவறான கடவுச்சொல்</translation>
-<translation id="5056549851600133418">உங்களுக்கான கட்டுரைகள்</translation>
+<translation id="5056549851600133418">உங்களுக்கான செய்திக் கட்டுரைகள்</translation>
<translation id="5070335125961472645"><ph name="BEGIN_LINK" />ப்ராக்ஸி முகவரியைச் சரிபார்த்தல்<ph name="END_LINK" /></translation>
<translation id="5086888986931078152">சில தளங்களின் பாதுகாக்கப்பட்ட உள்ளடக்க அணுகலை நீங்கள் இழக்கக்கூடும்.</translation>
<translation id="5087286274860437796">தற்போது சேவையகத்தின் சான்றிதழ் செல்லுபடியாகாது.</translation>
@@ -585,6 +580,7 @@
<translation id="5089810972385038852">மாநிலம்</translation>
<translation id="5094747076828555589">இது <ph name="DOMAIN" /> தான் என்பதை இந்தச் சேவையகம் உறுதிப்படுத்தவில்லை; இதன் பாதுகாப்புச் சான்றிதழை Chromium நம்பவில்லை. இது தவறான உள்ளமைவால் ஏற்பட்டிருக்கலாம் அல்லது தீங்கிழைப்பவர் உங்கள் இணைப்பில் குறுக்கிட்டிருக்கலாம்.</translation>
<translation id="5095208057601539847">பிராந்தியம்</translation>
+<translation id="5098332213681597508">இந்தப் பெயர் உங்கள் Google கணக்கிலிருந்து பெறப்பட்டது.</translation>
<translation id="5115563688576182185">(64-பிட்)</translation>
<translation id="5121084798328133320">உறுதிசெய்த பின்னர், உங்கள் Google Payments கணக்கிலிருக்கும் கார்டு விவரங்கள் இந்தத் தளத்துடன் பகிரப்படும்.</translation>
<translation id="5128122789703661928">இந்தப் பெயரைக் கொண்ட அமர்வானது நீக்குவதற்குத் தகுதியானதல்ல.</translation>
@@ -625,9 +621,10 @@
<translation id="5332219387342487447">ஷிப்பிங் முறை</translation>
<translation id="5355557959165512791"><ph name="SITE" /> தளத்தின் சான்றிதழ் ரத்துசெய்யப்பட்டதால், தற்போது அதைப் பார்க்க முடியாது. பொதுவாக நெட்வொர்க் பிழைகளும் பாதிப்புகளும் தற்காலிகமானவை என்பதால், இந்தப் பக்கம் பின்னர் சரியாகச் செயல்படக்கூடும்.</translation>
<translation id="536296301121032821">கொள்கை அமைப்புகளைச் சேமிப்பதில் தோல்வி</translation>
+<translation id="5371425731340848620">கார்டை மாற்றவும்</translation>
<translation id="5377026284221673050">"நேரம் பின்தங்கியுள்ளது" அல்லது "நேரம் கூடுதலாக அமைக்கப்பட்டுள்ளது" அல்லது "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">இந்தச் சான்றிதழ் சங்கிலியில், SHA-1ஐப் பயன்படுத்தி கையொப்பமிடப்பட்ட சான்றிதழ் உள்ளது.</translation>
-<translation id="5402410679244714488">காலாவதித் தேதி: <ph name="EXPIRATION_DATE_ABBR" />, கடைசியாக ஒரு ஆண்டிற்கு முன் பயன்படுத்தப்பட்டது</translation>
+<translation id="5387961145478138773">உங்களுக்குப் பிடித்த Google ஆப்ஸை விரைவாக அணுகலாம்</translation>
<translation id="540969355065856584"><ph name="DOMAIN" /> டொமைனை, சேவையகம் உறுதிப்படுத்தவில்லை; அதற்கான காரணங்கள்: இதன் பாதுகாப்புச் சான்றிதழ் தற்போது செல்லுபடியானதல்ல. இது தவறான உள்ளமைவினால் ஏற்பட்டிருக்கலாம் அல்லது தீங்கிழைப்பவர் உங்கள் இணைப்பில் குறுக்கிட்டிருக்கலாம்.</translation>
<translation id="5421136146218899937">உலாவல் தரவை அழி...</translation>
<translation id="5430298929874300616">புத்தகக்குறியை அகற்று</translation>
@@ -669,11 +666,13 @@
<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="5659593005791499971">மின்னஞ்சல்</translation>
+<translation id="5666899935841546222">அனைத்துக் கார்டுகளையும் ஒரே இடத்தில் பார்க்க வேண்டுமா?</translation>
<translation id="5675650730144413517">இந்தப் பக்கம் செயல்படவில்லை</translation>
<translation id="5685654322157854305">ஷிப்பிங் முகவரியைச் சேர்</translation>
<translation id="5689199277474810259">JSONக்கு ஏற்று</translation>
<translation id="5689516760719285838">இருப்பிடம்</translation>
<translation id="570530837424789914">நிர்வகி...</translation>
+<translation id="57094364128775171">வலுவான கடவுச்சொல்லைப் பரிந்துரைசெய்…</translation>
<translation id="5710435578057952990">இந்த தளத்தின் அடையாளம் சரிபார்க்கப்படவில்லை.</translation>
<translation id="5719499550583120431">ப்ரீபெய்டு கார்டுகள் ஏற்கப்படுகின்றன.</translation>
<translation id="5720705177508910913">நடப்புப் பயனர்</translation>
@@ -694,11 +693,9 @@
<translation id="5869405914158311789">இந்தத் தளத்தை அணுக முடியவில்லை</translation>
<translation id="5869522115854928033">சேமிக்கப்பட்ட கடவுச்சொற்கள்</translation>
<translation id="5893752035575986141">கிரெடிட் கார்டுகள் ஏற்கப்படுகின்றன.</translation>
-<translation id="5898382028489516745">பிற தளங்களில் உங்கள் <ph name="ORG_NAME" /> கடவுச்சொல்லை மீண்டும் பயன்படுத்தினால், அதை மீட்டமைக்கும்படி Chromium பரிந்துரைக்கும்.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (ஒத்திசைக்கப்பட்டது)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{ஒரு குக்கீ பயன்படுத்தப்படுகிறது}other{# குக்கீகள் பயன்படுத்தப்படுகின்றன}}</translation>
<translation id="5939518447894949180">மீட்டமை</translation>
-<translation id="5959728338436674663">ஆபத்தான பயன்பாடுகளையும் தளங்களையும் கண்டறிவதற்கு உதவியாக, சில <ph name="BEGIN_WHITEPAPER_LINK" />சாதனத் தகவலையும் பக்க உள்ளடக்கத்தையும்<ph name="END_WHITEPAPER_LINK" /> Googleக்குத் தானாக அனுப்பு. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">தொடர்புத் தகவலைத் திருத்தவும்</translation>
<translation id="5967867314010545767">வரலாற்றிலிருந்து அகற்று</translation>
<translation id="5975083100439434680">சிறிதாக்கு</translation>
@@ -743,13 +740,11 @@
<translation id="6282194474023008486">அஞ்சல் குறியீடு</translation>
<translation id="6290238015253830360">நீங்கள் பரிந்துரைத்த கட்டுரைகள் இங்கே தோன்றும்</translation>
<translation id="6305205051461490394"><ph name="URL" />ஐ அடையமுடியவில்லை.</translation>
-<translation id="6319915415804115995">கடைசியாக ஒரு ஆண்டிற்கு முன் பயன்படுத்தப்பட்டது</translation>
<translation id="6321917430147971392">உங்கள் DNS அமைப்புகளைச் சரிபார்க்கவும்</translation>
<translation id="6328639280570009161">பிணைய யூகத்தை முடக்குவதற்கு முயற்சிக்கவும்</translation>
<translation id="6328786501058569169">இது ஏமாற்றக்கூடிய தளம்</translation>
<translation id="6337133576188860026"><ph name="SIZE" />க்கும் குறைவான அளவைக் காலியாக்கும். நீங்கள் அடுத்த முறை பார்வையிடும் போது, சில தளங்கள் மிகவும் மெதுவாக ஏற்றப்படலாம்.</translation>
<translation id="6337534724793800597">பெயரின்படி கொள்கைகளை வடி</translation>
-<translation id="6342069812937806050">இப்போது</translation>
<translation id="6355080345576803305">பொது அமர்வு மேலெழுதப்பட்டது</translation>
<translation id="6358450015545214790">இவற்றின் பொருள் என்ன?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{மேலும் 1 பரிந்துரை}other{மேலும் # பரிந்துரைகள்}}</translation>
@@ -774,7 +769,6 @@
<translation id="6529602333819889595">&amp;நீக்குதலை மீண்டும் செய்</translation>
<translation id="6534179046333460208">இயல்நிலை இணையப் பரிந்துரைகள்</translation>
<translation id="6550675742724504774">விருப்பத்தேர்வுகள்</translation>
-<translation id="6556915248009097796">காலாவதி: <ph name="EXPIRATION_DATE_ABBR" />, கடைசியாகப் பயன்படுத்தியது: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">இன்னும் உங்கள் நிர்வாகி அனுமதிக்கவில்லை</translation>
<translation id="6569060085658103619">நீட்டிப்புப் பக்கத்தைப் பார்க்கிறீர்கள்</translation>
<translation id="6596325263575161958">குறியாக்க விருப்பங்கள்</translation>
@@ -803,15 +797,20 @@
<translation id="6825578344716086703"><ph name="DOMAIN" />க்குச் செல்ல முயற்சி செய்தீர்கள். ஆனால் சேவையகமானது வலிமையற்ற கையொப்ப அல்காரிதமை (SHA-1 போன்றது) பயன்படுத்தி, கையொப்பமிடப்பட்ட சான்றிதழை வழங்கியது. அதாவது, சேவையகம் வழங்கிய பாதுகாப்பு அனுமதிச் சான்றுகள் போலியானதாக்கப்பட்டிருக்கலாம், மேலும் அந்தச் சேவையகம் நீங்கள் எதிர்பார்த்த (ஹேக்கருடன் தகவல் பரிமாற்றம் செய்திருக்கக்கூடும்) சேவையகமாக இல்லாமலிருக்கலாம்.</translation>
<translation id="6831043979455480757">மொழிபெயர்</translation>
<translation id="6839929833149231406">பகுதி</translation>
+<translation id="6852204201400771460">ஆப்ஸை ரெஃப்ரெஷ் செய்யவா?</translation>
+<translation id="6865412394715372076">இந்தக் கார்டை இப்போது சரிபார்க்க முடியாது</translation>
<translation id="6874604403660855544">&amp;சேர்த்தலை மீண்டும் செய்</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">கொள்கையின் நிலை ஆதரிக்கப்படவில்லை.</translation>
<translation id="6895330447102777224">கார்டு உறுதிசெய்யப்பட்டது</translation>
<translation id="6897140037006041989">பயனர் முகவர்</translation>
+<translation id="6903319715792422884">Googleளுக்குச் சில <ph name="BEGIN_WHITEPAPER_LINK" />சாதனத் தகவல்களையும் பக்க உள்ளடக்கத்தையும்<ph name="END_WHITEPAPER_LINK" /> அனுப்புவதன் மூலம் பாதுகாப்பான உலாவலை மேம்படுத்த உதவுங்கள். <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">பயனர்:</translation>
+<translation id="6944692733090228304"><ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> நிர்வகிக்காத ஒரு தளத்தில் உங்கள் கடவுச்சொல்லை உள்ளிட்டுள்ளீர்கள். உங்கள் கணக்கைப் பாதுகாக்க, பிற ஆப்ஸிலும் தளங்களிலும் உங்கள் கடவுச்சொல்லை மீண்டும் பயன்படுத்த வேண்டாம்.</translation>
<translation id="6945221475159498467">தேர்ந்தெடு</translation>
<translation id="6948701128805548767">பிக்அப் முறைகளையும் தேவைகளையும் பார்க்க, முகவரியைத் தேர்ந்தெடுக்கவும்</translation>
<translation id="6949872517221025916">கடவுச்சொல்லை மீட்டமைக்கவும்</translation>
+<translation id="6950684638814147129">JSON மதிப்பைப் பாகுபடுத்தும்போது பிழை நேர்ந்தது: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">சேவையகத்தின் சான்றிதழ் போலியானது போல் தெரிகிறது.</translation>
<translation id="6965382102122355670">சரி</translation>
<translation id="6965978654500191972">சாதனம்</translation>
@@ -873,6 +872,7 @@
&lt;li&gt;&lt;strong&gt;பயன்படுத்து&lt;/strong&gt; என்பதைக் கிளிக் செய்து, &lt;strong&gt;சரி&lt;/strong&gt; என்பதைக் கிளிக் செய்யவும்
&lt;li&gt;உங்கள் கணினியிலிருந்து மென்பொருளை நிரந்தரமாக அகற்றுவது எப்படி என்பதை அறிய, &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome உதவி மையம்&lt;/a&gt; என்பதற்குச் செல்லவும்
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">கடவுச்சொற்களை நிர்வகி…</translation>
<translation id="7419106976560586862">சுயவிவரப் பாதை</translation>
<translation id="7437289804838430631">தொடர்புத் தகவலைச் சேர்</translation>
<translation id="7441627299479586546">தவறான கொள்கைத் தலைப்பு</translation>
@@ -881,7 +881,6 @@
<translation id="7451311239929941790">இந்தச் சிக்கல் குறித்து <ph name="BEGIN_LINK" />மேலும் அறிக<ph name="END_LINK" />.</translation>
<translation id="7455133967321480974">முழுமையான இயல்புநிலையைப் பயன்படுத்து (தடு)</translation>
<translation id="7460163899615895653">பிற சாதனங்களிலிருக்கும் உங்கள் சமீபத்திய தாவல்கள் இங்கே தோன்றும்</translation>
-<translation id="7469372306589899959">கார்டை உறுதிசெய்கிறது</translation>
<translation id="7473891865547856676">வேண்டாம் நன்றி</translation>
<translation id="7481312909269577407">அடுத்த பக்கம்</translation>
<translation id="7485870689360869515">தரவு எதுவும் இல்லை.</translation>
@@ -1011,6 +1010,7 @@
<translation id="8308427013383895095">பிணைய இணைப்பில் ஒரு சிக்கல் இருப்பதால் மொழிப்பெயர்ப்பு தோல்வியடைந்தது.</translation>
<translation id="8311129316111205805">அமர்வை ஏற்று</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> க்கான அணுகல் மறுக்கப்பட்டது</translation>
+<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="8349305172487531364">புக்மார்க் பட்டி</translation>
<translation id="8363502534493474904">விமானப் பயன்முறையை முடக்குதல்</translation>
@@ -1031,20 +1031,24 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> பதிலளிக்க நீண்ட நேரம் எடுத்துக்கொண்டது.</translation>
<translation id="8503559462189395349">Chrome கடவுச்சொற்கள்</translation>
<translation id="8503813439785031346">பயனர்பெயர்</translation>
+<translation id="8508648098325802031">தேடல் ஐகான்</translation>
<translation id="8543181531796978784"><ph name="BEGIN_ERROR_LINK" />கண்டறிவதில் சிக்கல் இருப்பதைப் புகாரளிக்கலாம்<ph name="END_ERROR_LINK" /> அல்லது உங்கள் பாதுகாப்பிற்கு ஏற்படக்கூடிய ஆபத்துகளைப் புரிந்துகொண்டிருந்தால், <ph name="BEGIN_LINK" />இந்தப் பாதுகாப்பற்ற தளத்திற்குச் செல்லலாம்<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">கேள்விகள் உள்ளனவா? உங்கள் சுயவிவரக் கண்காணிப்பாளரைத் தொடர்புகொள்ளவும்.</translation>
<translation id="8553075262323480129">பக்கத்தின் மொழியைத் தீர்மானிக்க முடியாததால் மொழிபெயர்ப்பு தோல்வியடைந்தது.</translation>
<translation id="8557066899867184262">உங்கள் கார்டின் பின்புறத்தில் CVC எண் இருக்கும்.</translation>
<translation id="8559762987265718583">உங்கள் சாதனத்தின் தேதி மற்றும் நேரம் (<ph name="DATE_AND_TIME" />) தவறாக உள்ளதால் <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> க்கான தனிப்பட்ட இணைப்பை ஏற்படுத்த முடியவில்லை.</translation>
+<translation id="8564985650692024650">பிற தளங்களில் உங்கள் <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> கடவுச்சொல்லை மீண்டும் பயன்படுத்தினால், அதை மீட்டமைக்கும்படி Chromium பரிந்துரைக்கிறது.</translation>
<translation id="8571890674111243710"><ph name="LANGUAGE" /> க்கு பக்கத்தை மொழிபெயர்க்கிறது...</translation>
<translation id="858637041960032120">தொலைபேசி எண்</translation>
<translation id="859285277496340001">இந்த சான்றிதழ் திரும்பப்பெறப்பட்டதா என்பதைச் சரிபார்ப்பதற்கான செயல்முறை இதில் இல்லை.</translation>
+<translation id="860043288473659153">கார்டு உரிமையாளரின் பெயர்</translation>
<translation id="8620436878122366504">இன்னும் உங்கள் பெற்றோர் அனுமதிக்கவில்லை</translation>
<translation id="8625384913736129811">இந்தச் சாதனத்தில் கார்டைச் சேமி</translation>
-<translation id="8639963783467694461">தானியங்குநிரப்பி அமைப்புகள்</translation>
+<translation id="8663226718884576429">ஆர்டர் சுருக்கம், <ph name="TOTAL_LABEL" />, மேலும் விவரங்கள்</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, பதில், <ph name="ANSWER" /></translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> க்கான உங்கள் இணைப்பு குறியாக்கம் செய்யப்படவில்லை.</translation>
<translation id="8718314106902482036">பேமெண்ட் முடியவில்லை</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, தேடல் பரிந்துரை</translation>
<translation id="8725066075913043281">மீண்டும் முயற்சிக்கவும்</translation>
<translation id="8728672262656704056">மறைநிலைக்குச் சென்றுவிட்டீர்கள்</translation>
<translation id="8730621377337864115">முடிந்தது</translation>
@@ -1060,8 +1064,10 @@
<translation id="8820817407110198400">புத்தகக்குறிகள்</translation>
<translation id="883848425547221593">மற்ற புக்மார்க்குகள்</translation>
<translation id="884264119367021077">ஷிப்பிங் முகவரி</translation>
+<translation id="8846319957959474018">புக்மார்க்குகளைப் பயன்படுத்தி ஆப்ஸை எளிதாகத் திறக்கலாம்</translation>
<translation id="884923133447025588">திரும்பப்பெறுதல் செயல்முறை காணப்படவில்லை.</translation>
<translation id="885730110891505394">Google உடன் பகிர்பவை</translation>
+<translation id="8858065207712248076">பிற தளங்களில் உங்கள் <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> கடவுச்சொல்லை மீண்டும் பயன்படுத்தினால், அதை மீட்டமைக்கும்படி Chrome பரிந்துரைக்கிறது.</translation>
<translation id="8866481888320382733">கொள்கை அமைப்புகளை அலசுவதில் பிழை</translation>
<translation id="8870413625673593573">சமீபத்தில் மூடியவை</translation>
<translation id="8874824191258364635">சரியான கார்டு எண்ணை உள்ளிடவும்</translation>
@@ -1101,6 +1107,7 @@
இந்த முறை <ph name="SITE" /> உடன் இணைவதற்கு Chromium முயற்சித்தபோது வழக்கத்திற்கு மாறான, தவறான நற்சான்றிதழ்களை இணையதளம் வழங்கியது. தாக்குபவர் தன்னை <ph name="SITE" /> ஆகக் காட்ட முயற்சிக்கும் போது அல்லது இணைப்பை வைஃபை உள்நுழைவுத் திரை குறுக்கிடும் போது இது ஏற்படலாம். இருப்பினும், தரவு எதுவும் பரிமாற்றப்படுவதற்கு முன் Chromium இணைப்பை நிறுத்தியதால் உங்கள் தகவல் பாதுகாப்பாகவே இருக்கிறது.</translation>
<translation id="9106062320799175032">பில்லிங் முகவரியைச் சேர்க்கவும்</translation>
<translation id="910908805481542201">இதைச் சரிசெய்ய உதவு</translation>
+<translation id="9114524666733003316">கார்டை உறுதிசெய்கிறது...</translation>
<translation id="9128870381267983090">பிணையத்துடன் இணை</translation>
<translation id="9137013805542155359">அசலைக் காண்பி</translation>
<translation id="9137248913990643158">இந்தப் பயன்பாட்டைப் பயன்படுத்தும் முன், Chromeஐத் தொடங்கி உள்நுழையவும்.</translation>
@@ -1111,6 +1118,7 @@
<translation id="9168814207360376865">சேமித்துள்ள உங்கள் கட்டண முறைகளைப் பார்க்க தளங்களை அனுமதிக்கவும்</translation>
<translation id="9169664750068251925">இந்தத் தளத்தில் எப்போதும் தடு</translation>
<translation id="9170848237812810038">&amp;செயல்தவிர்</translation>
+<translation id="9171296965991013597">ஆப்ஸிலிருந்து வெளியேறவா?</translation>
<translation id="917450738466192189">சேவையகச் சான்றிதழ் செல்லுபடியானதல்ல.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> ஆதரிக்கப்படாத நெறிமுறையைப் பயன்படுத்துகிறது.</translation>
<translation id="9205078245616868884">உங்கள் தரவு உங்கள் ஒத்திசைவு கடவுச்சொற்றொடரைக் கொண்டு முறைமையாக்கப்பட்டுள்ளது. ஒத்திசைவைத் தொடங்க, அதை உள்ளிடவும்.</translation>
diff --git a/chromium/components/strings/components_strings_te.xtb b/chromium/components/strings/components_strings_te.xtb
index 56150499e9b..c51462bfb49 100644
--- a/chromium/components/strings/components_strings_te.xtb
+++ b/chromium/components/strings/components_strings_te.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">విలువను దాచండి</translation>
<translation id="1228893227497259893">ఎంటిటీ ఐడెంటిఫైయర్ చెల్లదు</translation>
<translation id="1232569758102978740">శీర్షికలేనిది</translation>
+<translation id="1250759482327835220">తర్వాతిసారి మరింత వేగంగా చెల్లించడానికి, మీ కార్డ్, పేరు మరియు బిల్లింగ్ చిరునామాను మీ Google ఖాతాకు సేవ్ చేయండి.</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;
@@ -97,7 +98,6 @@
&lt;p&gt;దయచేసి &lt;strong&gt;సెట్టింగ్‌లు&lt;/strong&gt; అనువర్తనం యొక్క &lt;strong&gt;సాధారణం&lt;/strong&gt; విభాగంలో తేదీ మరియు సమయాన్ని సర్దుబాటు చేయండి.&lt;/p&gt;</translation>
<translation id="1583429793053364125">ఈ వెబ్ పేజీని ప్రదర్శిస్తున్నప్పుడు ఏదో తప్పు జరిగింది.</translation>
-<translation id="1590457302292452960">శక్తివంతమైన పాస్‌వర్డ్‌ని రూపొందించండి...</translation>
<translation id="1592005682883173041">స్థానిక డేటా ప్రాప్యత</translation>
<translation id="1594030484168838125">ఎంచుకోండి</translation>
<translation id="1620510694547887537">కెమెరా</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">సిస్టమ్ నిర్వాహకుడిని సంప్రదించి ప్రయత్నించండి.</translation>
<translation id="1740951997222943430">చెల్లుబాటు అయ్యే గడువు ముగింపు నెలను నమోదు చేయండి</translation>
+<translation id="1743520634839655729">తర్వాతిసారి మరింత వేగంగా చెల్లించడానికి, మీ కార్డ్, పేరు మరియు బిల్లింగ్ చిరునామాను మీ Google ఖాతాకు మరియు ఈ పరికరానికి సేవ్ చేయండి.</translation>
<translation id="17513872634828108">తెరిచిన ట్యాబ్‍లు</translation>
<translation id="1753706481035618306">పేజీ సంఖ్య</translation>
<translation id="1763864636252898013">ఈ సర్వర్ <ph name="DOMAIN" /> అని నిరూపించుకోలేకపోయింది; దీని భద్రతా ప్రమాణపత్రాన్ని మీ పరికర ఆపరేటింగ్ సిస్టమ్ విశ్వసించలేదు. ఇది తప్పుగా కాన్ఫిగర్ చేయడం వలన లేదా దాడిచేసే వ్యక్తి మీ కనెక్షన్‌కి అంతరాయం కలిగించడం వలన జరిగి ఉండవచ్చు.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">మీ తెరవబడిన ట్యాబ్‌లు ఇక్కడ కనిపిస్తాయి</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">కార్డుదారుని పేరు</translation>
-<translation id="1806541873155184440">జోడించినది <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">చెల్లని అభ్యర్థన లేదా అభ్యర్థన పరామితులు</translation>
<translation id="1826516787628120939">తనిఖీ చేస్తోంది</translation>
<translation id="1834321415901700177">ఈ సైట్ హానికరమైన ప్రోగ్రామ్‌లను కలిగి ఉంది</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 సూచన}other{# సూచనలు}}</translation>
<translation id="2079545284768500474">చర్య రద్దు</translation>
<translation id="20817612488360358">సిస్టమ్ ప్రాక్సీ సెట్టింగ్‌లు ఉపయోగించడానికి సెట్ చేయబడ్డాయి కానీ స్పష్టమైన ప్రాక్సీ కాన్ఫిగరేషన్ కూడా పేర్కొనబడింది.</translation>
-<translation id="2084558088529668945"><ph name="ORG_NAME" /> నిర్వహించని ఒక సైట్‌లో మీరు మీ పాస్‌వర్డ్‌ని నమోదు చేసారు. మీ ఖాతాని రక్షించాలంటే, ఇతర యాప్‌లు మరియు సైట్‌లలో మీ పాస్‌వర్డ్‌ని తిరిగి ఉపయోగించవద్దు.</translation>
<translation id="2091887806945687916">ధ్వని</translation>
<translation id="2094505752054353250">డొమైన్ సరిపోలలేదు</translation>
<translation id="2096368010154057602">శాఖ</translation>
+<translation id="2102134110707549001">బలమైన పాస్‌వర్డ్‌ను సూచించు…</translation>
<translation id="2108755909498034140">మీ కంప్యూటర్‌ను పునఃప్రారంభించండి</translation>
<translation id="2113977810652731515">కార్డ్</translation>
<translation id="2114841414352855701">ఇది <ph name="POLICY_NAME" /> ద్వారా భర్తీ చేయబడినందున విస్మరించబడింది.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP లోపం</translation>
<translation id="2270484714375784793">ఫోన్ నంబర్</translation>
<translation id="2292556288342944218">మీ ఇంటర్నెట్ ప్రాప్యత బ్లాక్ చేయబడింది</translation>
-<translation id="230155334948463882">కొత్త కార్డా?</translation>
<translation id="2316887270356262533">1 MB కంటే తక్కువ స్థలాన్ని ఖాళీ చేస్తుంది. మీ తదుపరి సందర్శనలో కొన్ని సైట్‌లు మరింత నెమ్మదిగా లోడ్ కావచ్చు.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" />కి వినియోగదారు పేరు మరియు పాస్‌వర్డ్ అవసరం.</translation>
<translation id="2317583587496011522">డెబిట్ కార్డ్‌లు ఆమోదించబడతాయి.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">ఆమోదించే కార్డ్‌లు</translation>
<translation id="2702801445560668637">పఠనా జాబితా</translation>
<translation id="2704283930420550640">విలువ ఆకృతికి సరిపోలలేదు.</translation>
-<translation id="2704951214193499422">Chromium ప్రస్తుతం మీ కార్డ్‌ను నిర్ధారించలేకపోయింది. దయచేసి తర్వాత మళ్లీ ప్రయత్నించండి.</translation>
<translation id="2705137772291741111">ఈ సైట్ యొక్క సేవ్ చేయబడిన (కాష్ చేసిన) కాపీ చదవదగినట్లుగా లేదు.</translation>
<translation id="2709516037105925701">స్వయంపూర్తి</translation>
<translation id="2710942282213947212">మీ కంప్యూటర్‌లోని సాఫ్ట్‌వేర్ Chromium సురక్షితంగా వెబ్‌కు కనెక్ట్ కాకుండా ఆపుతోంది</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">రవాణా పద్ధతి</translation>
<translation id="277499241957683684">పరికరం రికార్డ్ లేదు</translation>
<translation id="2781030394888168909">MacOS ఫార్మాట్‌లో ఎగుమతి చేయి</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">కనెక్షన్ మళ్ళీ సెట్ చెయ్యబడింది.</translation>
<translation id="2788784517760473862">ఆమోదించబడిన క్రెడిట్ కార్డ్‌లు</translation>
<translation id="2794233252405721443">సైట్ బ్లాక్ చేయబడింది</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">మీరు సెట్టింగ్‌ల పేజీ నుండి కనెక్షన్ కోసం కాన్ఫిగర్ చేయబడిన ఏ ప్రాక్సీలను అయినా నిలిపివేయవచ్చు.</translation>
<translation id="2955913368246107853">కనుగొను పట్టీని మూసివేయి</translation>
<translation id="2958431318199492670">నెట్‌వర్క్ కాన్ఫిగరేషన్ ONC ప్రమాణానికి అనుకూలంగా లేదు. కాన్ఫిగరేషన్‌లోని భాగాలు దిగుమతి కాకపోయి ఉండకపోవచ్చు.</translation>
-<translation id="2966678944701946121">గడువు ముగింపు: <ph name="EXPIRATION_DATE_ABBR" />, జోడించినది <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">సురక్షిత కనెక్షన్‌ను ఏర్పాటు చేయడానికి, మీ గడియారాన్ని సరైన సమయానికి సెట్ చేయాలి. ఎందుకంటే వెబ్‌సైట్‌లు వాటిని గుర్తించడానికి ఉపయోగించే ప్రమాణపత్రాలు నిర్దిష్ట కాలవ్యవధుల్లో మాత్రమే చెల్లుబాటు అవుతాయి. మీ పరికరం గడియారం సమయం తప్పుగా ఉన్నందున, Google Chrome ఈ ప్రమాణపత్రాలను ధృవీకరించలేదు.</translation>
<translation id="2972581237482394796">&amp;పునరావృతం</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, ప్రస్తుతం ఎంచుకోబడింది. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">చెల్లని విధాన రకం</translation>
<translation id="3037605927509011580">ఆవ్, స్నాప్!</translation>
<translation id="3041612393474885105">సర్టిఫికెట్ సమాచారం</translation>
-<translation id="3063697135517575841">Chrome ప్రస్తుతం మీ కార్డ్‌ను నిర్ధారించలేకపోయింది. దయచేసి తర్వాత మళ్లీ ప్రయత్నించండి.</translation>
<translation id="3064966200440839136">బాహ్య అనువర్తనం ద్వారా చెల్లించడానికి అజ్ఞాత మోడ్ నుండి నిష్క్రమిస్తోంది. కొనసాగించాలా?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{ఏమీ లేవు}=1{1 పాస్‌వర్డ్}other{# పాస్‌వర్డ్‌లు}}</translation>
<translation id="3096100844101284527">పికప్ చిరునామాను జోడించండి</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451"><ph name="TIME" />న మీ సమకాలీకరణ రహస్య పదబంధంతో మీ డేటా గుప్తీకరించబడింది. సమకాలీకరణను ప్రారంభించడానికి దీన్ని నమోదు చేయండి.</translation>
<translation id="3320021301628644560">బిల్లింగ్ చిరునామాను జోడించండి</translation>
<translation id="3338095232262050444">సురక్షితం</translation>
-<translation id="3340978935015468852">సెట్టింగ్‌లు</translation>
<translation id="3345135638360864351">ఈ సైట్‌ని ప్రాప్యత చేయడానికి మీరు చేసిన అభ్యర్థన <ph name="NAME" />కి పంపబడలేదు. దయచేసి మళ్లీ ప్రయత్నించండి.</translation>
<translation id="3355823806454867987">ప్రాక్సీ సెట్టింగ్‌లను మార్చు...</translation>
<translation id="3361596688432910856">Chrome ఈ కింది సమాచారాన్ని <ph name="BEGIN_EMPHASIS" />సేవ్ చేయదు<ph name="END_EMPHASIS" />:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">సర్వర్ యొక్క ప్రమాణ పత్రం నమ్మదగినది కాదు.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{సమకాలీకరించిన పరికరాల్లో కనీసం 1 అంశం}=1{1 అంశం (మరియు సమకాలీకరించిన పరికరాల్లో మరిన్ని)}other{# అంశాలు (మరియు సమకాలీకరించిన పరికరాల్లో మరిన్ని)}}</translation>
<translation id="3539171420378717834">ఈ పరికరంలో ఈ కార్డ్ కాపీని ఉంచు</translation>
-<translation id="3542684924769048008">దీని కోసం పాస్‌వర్డ్‌ను ఉపయోగించండి:</translation>
<translation id="3549644494707163724">మీ స్వంత సమకాలీకరణ రహస్య పదబంధంతో సమకాలీకరించబడిన డేటా మొత్తాన్ని గుప్తీకరించండి</translation>
<translation id="3556433843310711081">మీ నిర్వాహకుడు మీ కోసం దీన్ని అన్‌బ్లాక్ చేయగలరు</translation>
<translation id="3566021033012934673">మీ కనెక్షన్ ప్రైవేట్ కాదు</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">ధృవీకరణ సంతకం చెల్లదు</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{మరో <ph name="ITEM_COUNT" /> అంశం}other{మరో <ph name="ITEM_COUNT" /> అంశాలు}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">మీరు మీ <ph name="ORG_NAME" /> పాస్‌వర్డ్‌ని ఇతర సైట్‌లలో తిరిగి ఉపయోగించి ఉంటే దీనిని రీసెట్ చేయాల్సిందిగా Chrome సిఫార్సు చేస్తోంది.</translation>
<translation id="4196861286325780578">&amp;తరలించడాన్ని పునరావృతం చేయి</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ఫైర్‌వాల్ మరియు యాంటీవైరస్ కాన్ఫిగరేషన్‌లను తనిఖీ చేయడం<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">క్రాష్‌లు</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">మీరు చేసిన మార్పులు సేవ్ అయ్యి ఉండకపోవచ్చు.</translation>
<translation id="4258748452823770588">చెల్లని సంతకం</translation>
<translation id="4265872034478892965">మీ నిర్వాహకులు అనుమతించారు</translation>
-<translation id="4269787794583293679">(వినియోగదారు పేరు లేదు)</translation>
<translation id="4275830172053184480">మీ పరికరాన్ని పునఃప్రారంభించండి</translation>
<translation id="4277028893293644418">పాస్‌వర్డ్‌ను రీసెట్ చేయి</translation>
<translation id="4280429058323657511">, గడువు ముగింపు <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">పాప్-అప్‌లు మరియు మళ్లింపులు</translation>
<translation id="443673843213245140">ప్రాక్సీని ఉపయోగించడం ఆపివేయబడింది కానీ స్పష్టమైన ప్రాక్సీ కాన్ఫిగరేషన్ పేర్కొనబడింది.</translation>
<translation id="445100540951337728">ఆమోదించబడిన డెబిట్ కార్డ్‌లు</translation>
+<translation id="4472575034687746823">ప్రారంభించండి</translation>
<translation id="4506176782989081258">ధృవీకరణ లోపం: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">సిస్టమ్ నిర్వాహకుడిని సంప్రదించడం</translation>
<translation id="450710068430902550">నిర్వాహకుడితో భాగస్వామ్యం</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">విధానాలను మళ్లీ లోడ్ చేయి</translation>
<translation id="4728558894243024398">ప్లాట్‌ఫారమ్</translation>
<translation id="4736825316280949806">Chromiumని పునఃప్రారంభించండి</translation>
+<translation id="4742407542027196863">పాస్‌వర్డ్‌లను నిర్వహించు…</translation>
<translation id="4744603770635761495">అమలు చేయగల మార్గం</translation>
-<translation id="4749685221585524849">చివరిగా ఉపయోగించినది <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">మీ సమాచారాన్ని (ఉదాహరణకు, పాస్‌వర్డ్‌లు లేదా క్రెడిట్ కార్డ్ నంబర్‌లు) ఈ సైట్‌కి పంపినప్పుడు అది ప్రైవేట్‌గా ఉంచబడుతుంది.</translation>
<translation id="4756388243121344051">&amp;చరిత్ర</translation>
<translation id="4758311279753947758">సంప్రదింపు సమాచారాన్ని జోడించు</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">వీక్షణ</translation>
<translation id="4854362297993841467">ఈ బట్వాడా పద్ధతి అందుబాటులో లేదు. వేరే పద్ధతిని ప్రయత్నించండి.</translation>
<translation id="4858792381671956233">ఈ సైట్‌ను సందర్శించడానికి అనుమతించమని కోరుతూ మీ తల్లిదండ్రులకు అభ్యర్థన పంపారు</translation>
+<translation id="4876305945144899064">వినియోగదారు పేరు లేదు</translation>
<translation id="4880827082731008257">శోధన చరిత్ర</translation>
<translation id="4881695831933465202">తెరువు</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">రాష్ట్రం</translation>
<translation id="5094747076828555589">ఈ సర్వర్ <ph name="DOMAIN" /> అని నిరూపించుకోలేకపోయింది; దీని భద్రతా ప్రమాణపత్రాన్ని Chromium విశ్వసించలేదు. ఇది తప్పుగా కాన్ఫిగర్ చేయడం వలన లేదా దాడిచేసే వ్యక్తి మీ కనెక్షన్‌కి అంతరాయం కలిగించడం వలన జరిగి ఉండవచ్చు.</translation>
<translation id="5095208057601539847">ప్రావిన్స్</translation>
+<translation id="5098332213681597508">ఇది మీ Google ఖాతాలో ఉన్న పేరు</translation>
<translation id="5115563688576182185">(64-బిట్)</translation>
<translation id="5121084798328133320">మీరు నిర్ధారించిన తర్వాత, మీ Google చెల్లింపుల ఖాతా నుండి కార్డ్ వివరాలు ఈ సైట్‌తో షేర్ చేయబడతాయి.</translation>
<translation id="5128122789703661928">ఈ పేరు కలిగిన సెషన్‌ని తొలగించలేరు.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">షిప్పింగ్ పద్ధతి</translation>
<translation id="5355557959165512791"><ph name="SITE" /> యొక్క ప్రమాణపత్రం రద్దు చేయబడినందున మీరు ప్రస్తుతం దీన్ని సందర్శించలేరు. నెట్‌వర్క్ లోపాలు మరియు దాడులు సాధారణంగా తాత్కాలికమే, కనుక ఈ పేజీ తర్వాత పని చేయవచ్చు.</translation>
<translation id="536296301121032821">విధాన సెట్టింగ్‌లను నిల్వ చేయడంలో విఫలమైంది</translation>
+<translation id="5371425731340848620">కార్డ్‌ని అప్‌డేట్ చేయండి</translation>
<translation id="5377026284221673050">"మీ గడియారం ఆలస్యంగా నడుస్తోంది" లేదా "మీ గడియారం ముందుగా ఉంది" లేదా "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">ఈ సైట్ ప్రమాణపత్రం గొలుసులో SHA-1 ఉపయోగించి సంతకం చేసిన ప్రమాణపత్రం ఉంది.</translation>
-<translation id="5402410679244714488">గడువు ముగింపు: <ph name="EXPIRATION_DATE_ABBR" />, చివరిగా దాదాపు సంవత్సరం క్రితం ఉపయోగించబడింది</translation>
+<translation id="5387961145478138773">మీకు ఇష్టమైన Google యాప్‌లకు సత్వరమే యాక్సెస్‌ను పొందండి</translation>
<translation id="540969355065856584">ఈ సర్వర్ <ph name="DOMAIN" /> అని నిరూపించుకోలేకపోయింది; దీని భద్రతా ప్రమాణపత్రం ప్రస్తుతం చెల్లదు. ఇది తప్పుగా కాన్ఫిగర్ చేయడం వలన లేదా దాడి చేసే వ్యక్తి మీ కనెక్షన్‌కి అంతరాయం కలిగించడం వలన జరిగి ఉండవచ్చు.</translation>
<translation id="5421136146218899937">బ్రౌజింగ్ డేటాను క్లియర్ చెయ్యి...</translation>
<translation id="5430298929874300616">బుక్‌మార్క్‌ని తీసివేయి</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">ఇమెయిల్</translation>
+<translation id="5666899935841546222">మీరు మీ అన్ని కార్డ్‌లను ఒకే చోట పొందాలనుకుంటున్నారా?</translation>
<translation id="5675650730144413517">ఈ పేజీ పని చేయడం లేదు</translation>
<translation id="5685654322157854305">షిప్పింగ్ చిరునామాను జోడించండి</translation>
<translation id="5689199277474810259">JSONకు ఎగుమతి చేయి</translation>
<translation id="5689516760719285838">స్థానం</translation>
<translation id="570530837424789914">నిర్వహించండి...</translation>
+<translation id="57094364128775171">బలమైన పాస్‌వర్డ్‌ను సూచించండి…</translation>
<translation id="5710435578057952990">ఈ వెబ్‍‌సైట్ యొక్క గుర్తింపు నిర్థారించబడలేదు.</translation>
<translation id="5719499550583120431">ప్రీపెయిడ్ కార్డ్‌లు ఆమోదించబడతాయి.</translation>
<translation id="5720705177508910913">ప్రస్తుత వినియోగదారు</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">ఈ సైట్‌ను చేరుకోలేకపోయాము</translation>
<translation id="5869522115854928033">సేవ్ చేసిన పాస్‌వర్డ్‌లు</translation>
<translation id="5893752035575986141">క్రెడిట్ కార్డ్‌లు ఆమోదించబడతాయి.</translation>
-<translation id="5898382028489516745">మీరు మీ <ph name="ORG_NAME" /> పాస్‌వర్డ్‌ని ఇతర సైట్‌లలో తిరిగి ఉపయోగించి ఉంటే దీనిని రీసెట్ చేయాల్సిందిగా Chromium సిఫార్సు చేస్తోంది.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (సమకాలీకరించబడింది)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 వినియోగంలో ఉంది}other{# వినియోగంలో ఉన్నాయి}}</translation>
<translation id="5939518447894949180">రీసెట్ చేయి</translation>
-<translation id="5959728338436674663">హానికరమైన అనువర్తనాలు మరియు సైట్‌లను గుర్తించడంలో సహాయపడటానికి కొంత <ph name="BEGIN_WHITEPAPER_LINK" />సిస్టమ్ సమాచారాన్ని మరియు పేజీ కంటెంట్<ph name="END_WHITEPAPER_LINK" />ను Googleకు స్వయంచాలకంగా పంపుతుంది. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">సంప్రదింపు సమాచారాన్ని సవరించండి</translation>
<translation id="5967867314010545767">చరిత్ర నుండి తీసివేయి</translation>
<translation id="5975083100439434680">దూరంగా జూమ్ చెయ్యి</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">పోస్టల్ కోడ్</translation>
<translation id="6290238015253830360">మీకు సూచించిన కథనాలు ఇక్కడ కనిపిస్తాయి</translation>
<translation id="6305205051461490394"><ph name="URL" />ని చేరుకోలేకపోయాము.</translation>
-<translation id="6319915415804115995">చివరిగా దాదాపు సంవత్సరం క్రితం ఉపయోగించబడింది</translation>
<translation id="6321917430147971392">మీ DNS సెట్టింగ్‌లను తనిఖీ చేయండి</translation>
<translation id="6328639280570009161">నెట్‌వర్క్ సూచనను నిలిపివేసి ప్రయత్నించండి</translation>
<translation id="6328786501058569169">ఈ సైట్ మోసపూరితమైనది</translation>
<translation id="6337133576188860026"><ph name="SIZE" /> కంటే తక్కువ స్థలాన్ని ఖాళీ చేస్తుంది. మీ తదుపరి సందర్శనలో కొన్ని సైట్‌లు మరింత నెమ్మదిగా లోడ్ కావచ్చు.</translation>
<translation id="6337534724793800597">పేరు ద్వారా విధానాలను ఫిల్టర్ చేయి</translation>
-<translation id="6342069812937806050">ఇప్పుడే</translation>
<translation id="6355080345576803305">పబ్లిక్ సెషన్ భర్తీ</translation>
<translation id="6358450015545214790">దీని అర్ధం ఏమిటి?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 ఇతర సూచన}other{# ఇతర సూచనలు}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;తొలగించడాన్ని పునరావృతం చేయి</translation>
<translation id="6534179046333460208">ప్రత్యక్ష వెబ్ సూచనలు</translation>
<translation id="6550675742724504774">ఎంపికలు</translation>
-<translation id="6556915248009097796">గడువు ముగింపు: <ph name="EXPIRATION_DATE_ABBR" />, చివరిగా ఉపయోగించినది <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">మీ నిర్వాహకుడు దీన్ని ఇంకా ఆమోదించలేదు</translation>
<translation id="6569060085658103619">మీరు పొడిగింపు పేజీని వీక్షిస్తున్నారు</translation>
<translation id="6596325263575161958">గుప్తీకరణ ఎంపికలు</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703"><ph name="DOMAIN" />ను చేరుకోవడానికి మీరు ప్రయత్నించారు, కానీ సర్వర్ (SHA-1 వంటి) బలహీనమైన సంతకం అల్గారిథమ్‌ను ఉపయోగించి సంతకం చేసిన ప్రమాణపత్రాన్ని అందించింది. అంటే సర్వర్ అందించిన భద్రత ఆధారాలు నకిలీ కావచ్చు మరియు సర్వర్ మీరు ఊహించిన సర్వర్ కాకపోవచ్చు (మీరు హ్యాకర్‌తో పరస్పర చర్య చేస్తుండవచ్చు).</translation>
<translation id="6831043979455480757">అనువదించు</translation>
<translation id="6839929833149231406">ప్రాంతం</translation>
+<translation id="6852204201400771460">యాప్‌ను మళ్లీ లోడ్ చేయాలా?</translation>
+<translation id="6865412394715372076">ప్రస్తుతం ఈ కార్డ్‌ని ధృవీకరించడం సాధ్యపడదు</translation>
<translation id="6874604403660855544">&amp;జోడించడాన్ని పునరావృతం చేయి</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">విధానం స్థాయికి మద్దతు లేదు.</translation>
<translation id="6895330447102777224">మీ కార్డ్ నిర్ధారించబడింది</translation>
<translation id="6897140037006041989">వినియోగదారు ప్రతినిధి</translation>
+<translation id="6903319715792422884"><ph name="BEGIN_WHITEPAPER_LINK" />కొంత సిస్టమ్ సమాచారం మరియు పేజీ కంటెంట్‌<ph name="END_WHITEPAPER_LINK" />ను Googleకి పంపడం ద్వారా సురక్షిత బ్రౌజింగ్‌ని మెరుగుపరచడంలో సహాయపడండి. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">వినియోగదారు:</translation>
+<translation id="6944692733090228304"><ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> నిర్వహించని ఒక సైట్‌లో మీరు మీ పాస్‌వర్డ్‌ని నమోదు చేసారు. మీ ఖాతాని రక్షించడం కోసం, మీ పాస్‌వర్డ్‌ని ఇతర యాప్‌లు మరియు సైట్‌లలో తిరిగి ఉపయోగించవద్దు.</translation>
<translation id="6945221475159498467">ఎంచుకోండి</translation>
<translation id="6948701128805548767">పికప్ పద్ధతులు మరియు అవసరాలను చూడాలంటే, చిరునామాని ఎంచుకోండి</translation>
<translation id="6949872517221025916">పాస్‌వర్డ్‌ను రీసెట్ చేయండి</translation>
+<translation id="6950684638814147129">JSON విలువను అన్వయిస్తుండగా ఎర్రర్ ఏర్పడింది: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">సర్వర్ ధృవీకరణ పత్రం చెల్లదు.</translation>
<translation id="6965382102122355670">సరే</translation>
<translation id="6965978654500191972">పరికరం</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;&lt;strong&gt;వర్తింపజేయి&lt;/strong&gt;ని క్లిక్ చేసి, ఆపై &lt;strong&gt;సరే&lt;/strong&gt; క్లిక్ చేయండి
&lt;li&gt;మీ కంప్యూటర్ నుండి శాశ్వతంగా సాఫ్ట్‌వేర్‌ను ఎలా తొలగించాలో తెలుసుకోవడానికి &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome సహాయ కేంద్రం&lt;/a&gt;ని సందర్శించండి
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">పాస్‌వర్డ్‌లను నిర్వహించండి…</translation>
<translation id="7419106976560586862">ప్రొఫైల్ మార్గం</translation>
<translation id="7437289804838430631">సంప్రదింపు సమాచారాన్ని జోడించు</translation>
<translation id="7441627299479586546">చెల్లని విధాన విషయం</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790">ఈ సమస్య గురించి <ph name="BEGIN_LINK" />మరింత తెలుసుకోండి<ph name="END_LINK" />.</translation>
<translation id="7455133967321480974">సార్వజనీన డిఫాల్ట్‌ను ఉపయోగించు (బ్లాక్ చేయి)</translation>
<translation id="7460163899615895653">ఇతర పరికరాల్లో మీ ఇటీవలి ట్యాబ్‌లు ఇక్కడ కనిపిస్తాయి</translation>
-<translation id="7469372306589899959">కార్డ్‌ని నిర్ధారిస్తోంది</translation>
<translation id="7473891865547856676">వద్దు, ధన్యవాదాలు</translation>
<translation id="7481312909269577407">ఫార్వార్డ్</translation>
<translation id="7485870689360869515">డేటా కనుగొనబడలేదు.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">నెట్‌వర్క్ కనెక్షన్‌తో సమస్య ఉన్నందున అనువాదం విఫలమైంది.</translation>
<translation id="8311129316111205805">సెషన్‌ని లోడ్ చేయి</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" />కి ప్రాప్యత నిరాకరించబడింది</translation>
+<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="8349305172487531364">బుక్‌మార్క్‌ల పట్టీ</translation>
<translation id="8363502534493474904">ఎయిర్‌ప్లైన్ మోడ్‌ను ఆఫ్ చేయడం</translation>
@@ -1035,21 +1035,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ప్రతిస్పందించడానికి చాలా ఎక్కువ సమయం పట్టింది.</translation>
<translation id="8503559462189395349">Chrome పాస్‌వర్డ్‌లు</translation>
<translation id="8503813439785031346">యూజర్‌పేరు</translation>
+<translation id="8508648098325802031">శోధన చిహ్నం</translation>
<translation id="8543181531796978784">మీరు <ph name="BEGIN_ERROR_LINK" />గుర్తింపు సమస్యను నివేదించవచ్చు<ph name="END_ERROR_LINK" /> లేదా మీకు మీ భద్రతకు పొంచి ఉన్న ప్రమాదాలు అర్థం అయ్యి ఉంటే, <ph name="BEGIN_LINK" />ఈ అసురక్షిత సైట్‌ను సందర్శించండి<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">ప్రశ్నలు ఏమైనా ఉన్నాయా? మీ ప్రొఫైల్‌ను పర్యవేక్షించే వ్యక్తిని సంప్రదించండి.</translation>
<translation id="8553075262323480129">పేజీ భాష నిర్థారించలేకపోయినందున అనువాదం విఫలమైంది.</translation>
<translation id="8557066899867184262">మీ కార్డు వెనుకవైపు CVC ఉంటుంది.</translation>
<translation id="8559762987265718583">మీ పరికరం తేదీ మరియు సమయం (<ph name="DATE_AND_TIME" />) తప్పుగా ఉన్నందున <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />కి ప్రైవేట్ కనెక్షన్ ఏర్పాటు చేయబడదు.</translation>
+<translation id="8564985650692024650">మీరు మీ <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> పాస్‌వర్డ్‌ని ఇతర సైట్‌లలో తిరిగి ఉపయోగించినట్లయితే దీనిని రీసెట్ చేయాల్సిందిగా Chromium సిఫార్సు చేస్తోంది.</translation>
<translation id="8571890674111243710">పేజీని <ph name="LANGUAGE" />కు అనువదిస్తోంది...</translation>
<translation id="858637041960032120">ఫోన్ నం. జోడిం.
</translation>
<translation id="859285277496340001">ఇది రద్దు చెయ్యబడిందా అని తనిఖీ చెయ్యడానికి ప్రమాణపత్రం విధానాన్ని పేర్కొనలేదు.</translation>
+<translation id="860043288473659153">కార్డుదారుని పేరు</translation>
<translation id="8620436878122366504">మీ తల్లిదండ్రులు దీన్ని ఇంకా ఆమోదించలేదు</translation>
<translation id="8625384913736129811">ఈ కార్డ్‌ను ఈ పరికరానికి సేవ్ చేయి</translation>
-<translation id="8639963783467694461">స్వీయపూర్తి సెట్టింగ్‌లు</translation>
+<translation id="8663226718884576429">ఆర్డర్ సారాంశం, <ph name="TOTAL_LABEL" />, మరిన్ని వివరాలు</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, సమాధానం, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206"><ph name="DOMAIN" />కు మీ కనెక్షన్ గుప్తీకరించబడలేదు.</translation>
<translation id="8718314106902482036">చెల్లింపు పూర్తి కాలేదు</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, శోధన సూచన</translation>
<translation id="8725066075913043281">మళ్ళీ ప్రయత్నించండి</translation>
<translation id="8728672262656704056">మీరు ఇప్పుడు అజ్ఞాత మోడ్‌లో ఉన్నారు</translation>
<translation id="8730621377337864115">పూర్తయింది</translation>
@@ -1065,8 +1069,10 @@
<translation id="8820817407110198400">బుక్‌మార్క్‌లు</translation>
<translation id="883848425547221593">ఇతర బుక్‌మార్క్‌లు:</translation>
<translation id="884264119367021077">షిప్పింగ్ చిరునామా</translation>
+<translation id="8846319957959474018">బుక్‌మార్క్‌లతో సులభంగా యాప్‌లను తెరవండి</translation>
<translation id="884923133447025588">ఏ రద్దు విధానం కనుగొనబడలేదు.</translation>
<translation id="885730110891505394">Googleతో భాగస్వామ్యం</translation>
+<translation id="8858065207712248076">మీరు మీ <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> పాస్‌వర్డ్‌ని ఇతర సైట్‌లలో తిరిగి ఉపయోగించినట్లయితే దీనిని రీసెట్ చేయాల్సిందిగా Chrome సిఫార్సు చేస్తోంది.</translation>
<translation id="8866481888320382733">విధాన సెట్టింగ్‌లను అన్వయించడంలో లోపం</translation>
<translation id="8870413625673593573">ఇటీవల మూసివేసినవి</translation>
<translation id="8874824191258364635">చెల్లుబాటు అయ్యే కార్డ్ నంబర్‌ను నమోదు చేయండి</translation>
@@ -1105,6 +1111,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> సాధారణంగా మీ సమాచారాన్ని రక్షించడానికి గుప్తీకరణను ఉపయోగిస్తుంది. Chromium ఈసారి <ph name="SITE" />కి కనెక్ట్ చేయడానికి ప్రయత్నించినప్పుడు, వెబ్‌సైట్ అసాధారణ మరియు తప్పు ఆధారాలు అని ప్రతిస్పందించింది. దాడి చేసే వ్యక్తి <ph name="SITE" />గా వ్యవహరించి మోసగించడానికి ప్రయత్నిస్తున్నప్పుడు లేదా Wi-Fi సైన్-ఇన్ స్క్రీన్ కనెక్షన్‌కు అంతరాయం కలిగించినప్పుడు ఇలా జరగవచ్చు. Chromium ఎలాంటి డేటా వినిమయం సంభవించక ముందే కనెక్షన్‌ను ఆపివేసినందున మీ సమాచారం ఇప్పటికీ సురక్షితంగానే ఉంది.</translation>
<translation id="9106062320799175032">బిల్లింగ్ చిరునామాను జోడించండి</translation>
<translation id="910908805481542201">దీనిని పరిష్కరించడంలో నాకు సహాయం అందించండి</translation>
+<translation id="9114524666733003316">కార్డ్‌ నిర్ధారించబడుతోంది...</translation>
<translation id="9128870381267983090">నెట్‌వర్క్‌కి కనెక్ట్ చెయ్యి</translation>
<translation id="9137013805542155359">అసలును చూపించు</translation>
<translation id="9137248913990643158">దయచేసి ఈ అనువర్తనాన్ని ఉపయోగించే ముందు Chromeని ప్రారంభించి, దానికి సైన్ ఇన్ చేయండి.</translation>
@@ -1115,6 +1122,7 @@
<translation id="9168814207360376865">మీ వద్ద సేవ్ చేయబడిన చెల్లింపు పద్ధతులు ఉన్నాయో లేదో తనిఖీ చేయడానికి సైట్‌లను అనుమతించండి</translation>
<translation id="9169664750068251925">ఈ సైట్‌లో ఎల్లప్పుడూ బ్లాక్ చేయి</translation>
<translation id="9170848237812810038">&amp;అన్డు</translation>
+<translation id="9171296965991013597">యాప్ నుండి నిష్క్రమించాలా?</translation>
<translation id="917450738466192189">సర్వర్ యొక్క ప్రమాణపత్రం చెల్లుబాటు కాదు.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> మద్దతు లేని ప్రోటోకాల్‌ను ఉపయోగిస్తోంది.</translation>
<translation id="9205078245616868884">మీ సమకాలీకరణ రహస్య పదబంధంతో మీ డేటా గుప్తీకరించబడింది. సమకాలీకరణను ప్రారంభించడానికి దీన్ని నమోదు చేయండి.</translation>
diff --git a/chromium/components/strings/components_strings_th.xtb b/chromium/components/strings/components_strings_th.xtb
index 98d64532eca..84cd63332a7 100644
--- a/chromium/components/strings/components_strings_th.xtb
+++ b/chromium/components/strings/components_strings_th.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">ซ่อนค่า</translation>
<translation id="1228893227497259893">ตัวระบุเอนทิตีไม่ถูกต้อง</translation>
<translation id="1232569758102978740">ไม่ระบุชื่อ</translation>
+<translation id="1250759482327835220">เพื่อความรวดเร็วในการชำระเงินครั้งถัดไป โปรดบันทึกบัตร ชื่อ และที่อยู่สำหรับการเรียกเก็บเงินไว้ในบัญชี Google</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;
@@ -97,7 +98,6 @@
&lt;p&gt;โปรดปรับวันที่และเวลาจากหัวข้อ&lt;strong&gt;ทั่วไป&lt;/strong&gt;ในแอป&lt;strong&gt;การตั้งค่า&lt;/strong&gt;&lt;/p&gt;</translation>
<translation id="1583429793053364125">มีสิ่งผิดปกติเกิดขึ้นในขณะที่แสดงหน้าเว็บนี้</translation>
-<translation id="1590457302292452960">สร้างรหัสผ่านที่รัดกุม...</translation>
<translation id="1592005682883173041">การเข้าถึงข้อมูลในเครื่อง</translation>
<translation id="1594030484168838125">เลือก</translation>
<translation id="1620510694547887537">กล้องถ่ายรูป</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">ลองติดต่อผู้ดูแลระบบ</translation>
<translation id="1740951997222943430">ป้อนเดือนที่หมดอายุที่ถูกต้อง</translation>
+<translation id="1743520634839655729">เพื่อความรวดเร็วในการชำระเงินครั้งถัดไป โปรดบันทึกบัตร ชื่อ และที่อยู่สำหรับการเรียกเก็บเงินไว้ในบัญชี Google และในอุปกรณ์นี้</translation>
<translation id="17513872634828108">แท็บที่เปิดอยู่</translation>
<translation id="1753706481035618306">เลขหน้า</translation>
<translation id="1763864636252898013">เซิร์ฟเวอร์นี้ไม่สามารถพิสูจน์ได้ว่าเป็น <ph name="DOMAIN" /> เพราะระบบปฏิบัติการของอุปกรณ์ของคุณไม่เชื่อถือใบรับรองความปลอดภัย โดยอาจเกิดจากการกำหนดค่าผิดหรือผู้บุกรุกที่ขัดขวางการเชื่อมต่อของคุณ</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">แท็บที่คุณเปิดไว้จะปรากฏที่นี่</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">ชื่อผู้ถือบัตร</translation>
-<translation id="1806541873155184440">วันที่เพิ่ม <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">คำขอหรือพารามิเตอร์คำขอไม่ถูกต้อง</translation>
<translation id="1826516787628120939">กำลังตรวจสอบ</translation>
<translation id="1834321415901700177">เว็บไซต์นี้มีโปรแกรมอันตราย</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 คำแนะนำ}other{# คำแนะนำ}}</translation>
<translation id="2079545284768500474">เลิกทำ</translation>
<translation id="20817612488360358">มีการกำหนดให้ใช้การตั้งค่าพร็อกซีระบบ แต่ก็มีการระบุการกำหนดค่าพร็อกซีอย่างชัดเจนไว้ด้วยเช่นกัน</translation>
-<translation id="2084558088529668945">คุณป้อนรหัสผ่านในเว็บไซต์ที่ <ph name="ORG_NAME" /> ไม่ได้จัดการ เพื่อปกป้องบัญชี โปรดอย่าใช้รหัสผ่านซ้ำในแอปและเว็บไซต์อื่นๆ</translation>
<translation id="2091887806945687916">เสียง</translation>
<translation id="2094505752054353250">โดเมนไม่ตรง</translation>
<translation id="2096368010154057602">จังหวัด</translation>
+<translation id="2102134110707549001">แนะนำรหัสผ่านที่รัดกุม…</translation>
<translation id="2108755909498034140">รีสตาร์ทคอมพิวเตอร์</translation>
<translation id="2113977810652731515">บัตร</translation>
<translation id="2114841414352855701">ไม่สนใจเพราะถูกแทนที่โดย <ph name="POLICY_NAME" /></translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">ข้อผิดพลาดของ HTTP</translation>
<translation id="2270484714375784793">หมายเลขโทรศัพท์</translation>
<translation id="2292556288342944218">การเข้าถึงอินเทอร์เน็ตของคุณถูกบล็อก</translation>
-<translation id="230155334948463882">บัตรใหม่ใช่ไหม</translation>
<translation id="2316887270356262533">หากเพิ่มพื้นที่ว่างไม่ถึง 1 MB ไซต์บางแห่งอาจโหลดช้าลงเมื่อคุณเข้าชมครั้งถัดไป</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> ต้องใช้ชื่อผู้ใช้และรหัสผ่าน</translation>
<translation id="2317583587496011522">รับบัตรเดบิต</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">บัตรที่ยอมรับ</translation>
<translation id="2702801445560668637">รายการที่จะอ่าน</translation>
<translation id="2704283930420550640">ค่าไม่ตรงกับรูปแบบ</translation>
-<translation id="2704951214193499422">Chromium ไม่สามารถยืนยันบัตรของคุณได้ในขณะนี้ โปรดลองอีกครั้งในภายหลัง</translation>
<translation id="2705137772291741111">อ่านสำเนาที่บันทึกไว้ (แคช) ของเว็บไซต์นี้ไม่ได้</translation>
<translation id="2709516037105925701">ป้อนอัตโนมัติ</translation>
<translation id="2710942282213947212">ซอฟต์แวร์ในคอมพิวเตอร์ของคุณทำให้ Chromium ไม่สามารถเชื่อมต่อกับเว็บอย่างปลอดภัย</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">วิธีการจัดส่งสินค้า</translation>
<translation id="277499241957683684">ไม่มีอุปกรณ์บันทึก</translation>
<translation id="2781030394888168909">ส่งออก MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">การเชื่อมต่อได้รับการรีเซ็ตแล้ว</translation>
<translation id="2788784517760473862">บัตรเครดิตที่ยอมรับ</translation>
<translation id="2794233252405721443">เว็บไซต์ที่ถูกบล็อก</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">คุณสามารถปิดใช้งานพร็อกซีที่กำหนดค่าสำหรับการเชื่อมต่อจากหน้าการตั้งค่าได้</translation>
<translation id="2955913368246107853">ปิดแถบค้นหา</translation>
<translation id="2958431318199492670">การกำหนดค่าเครือข่ายไม่เป็นไปตามมาตรฐาน ONC การกำหนดค่าบางส่วนอาจไม่ได้รับการนำเข้า</translation>
-<translation id="2966678944701946121">วันที่หมดอายุ: <ph name="EXPIRATION_DATE_ABBR" /> วันที่เพิ่ม <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">หากต้องการสร้างการเชื่อมต่อที่ปลอดภัย คุณต้องตั้งค่านาฬิกาให้ถูกต้องเนื่องจากใบรับรองที่เว็บไซต์ใช้เพื่อระบุตัวตนจะใช้ได้ในช่วงเวลาที่เจาะจงเท่านั้น แต่เนื่องจากนาฬิกาของอุปกรณ์ไม่ถูกต้อง Google Chrome จึงไม่สามารถยืนยันใบรับรองเหล่านี้</translation>
<translation id="2972581237482394796">&amp;ทำซ้ำ</translation>
<translation id="2977665033722899841">เลือก <ph name="ROW_NAME" /> อยู่ตอนนี้ <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">ประเภทนโยบายไม่ถูกต้อง</translation>
<translation id="3037605927509011580">แย่จัง!</translation>
<translation id="3041612393474885105">ข้อมูลในใบรับรอง</translation>
-<translation id="3063697135517575841">Chrome ไม่สามารถยืนยันบัตรของคุณได้ในขณะนี้ โปรดลองอีกครั้งในภายหลัง</translation>
<translation id="3064966200440839136">ออกจากโหมดไม่ระบุตัวตนเพื่อชำระเงินผ่านแอปพลิเคชันภายนอก ดำเนินการต่อไหม</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{ไม่มี}=1{รหัสผ่าน 1 รายการ}other{รหัสผ่าน # รายการ}}</translation>
<translation id="3096100844101284527">เพิ่มที่อยู่สำหรับรับสินค้า</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">ข้อมูลของคุณได้รับการเข้ารหัสด้วยรหัสผ่านการซิงค์เมื่อวันที่ <ph name="TIME" /> โปรดป้อนรหัสผ่านเพื่อเริ่มซิงค์</translation>
<translation id="3320021301628644560">เพิ่มที่อยู่สำหรับการเรียกเก็บเงิน</translation>
<translation id="3338095232262050444">ปลอดภัย</translation>
-<translation id="3340978935015468852">การตั้งค่า</translation>
<translation id="3345135638360864351">ไม่สามารถส่งคำขอเข้าถึงไซต์นี้ไปยัง <ph name="NAME" /> ได้ โปรดลองอีกครั้ง</translation>
<translation id="3355823806454867987">เปลี่ยนการตั้งค่าพร็อกซี...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />จะไม่บันทึก<ph name="END_EMPHASIS" />ข้อมูลต่อไปนี้:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">ใบรับรองของเซิร์ฟเวอร์ไม่น่าเชื่อถือ</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{อย่างน้อย 1 รายการบนอุปกรณ์ที่ซิงค์}=1{1 รายการ (และมากกว่าบนอุปกรณ์ที่ซิงค์)}other{# รายการ (และมากกว่าบนอุปกรณ์ที่ซิงค์)}}</translation>
<translation id="3539171420378717834">เก็บสำเนาบัตรนี้ไว้บนอุปกรณ์นี้</translation>
-<translation id="3542684924769048008">ใช้รหัสผ่านสำหรับ:</translation>
<translation id="3549644494707163724">เข้ารหัสข้อมูลที่ซิงค์ทั้งหมดด้วยข้อความรหัสผ่านการซิงค์ของคุณเอง</translation>
<translation id="3556433843310711081">ผู้จัดการสามารถเลิกบล็อกเว็บไซต์ให้คุณ</translation>
<translation id="3566021033012934673">การเชื่อมต่อของคุณไม่เป็นส่วนตัว</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">ลายเซ็นยืนยันไม่ถูกต้อง</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{อีก <ph name="ITEM_COUNT" /> รายการ}other{อีก <ph name="ITEM_COUNT" /> รายการ}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome ขอแนะนำให้รีเซ็ตรหัสผ่าน <ph name="ORG_NAME" /> หากคุณใช้รหัสผ่านนี้ซ้ำในเว็บไซต์อื่น</translation>
<translation id="4196861286325780578">&amp;ทำซ้ำการย้าย</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />ตรวจสอบไฟร์วอลล์และการกำหนดค่าการป้องกันไวรัส<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">การขัดข้อง</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">ระบบอาจไม่ได้บันทึกการเปลี่ยนแปลงของคุณ</translation>
<translation id="4258748452823770588">ลายเซ็นไม่เหมาะสม</translation>
<translation id="4265872034478892965">อนุญาตโดยผู้ดูแลระบบ</translation>
-<translation id="4269787794583293679">(ไม่มีชื่อผู้ใช้)</translation>
<translation id="4275830172053184480">รีสตาร์ทอุปกรณ์ของคุณ</translation>
<translation id="4277028893293644418">รีเซ็ตรหัสผ่าน</translation>
<translation id="4280429058323657511">หมดอายุ <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">ป๊อปอัปและการเปลี่ยนเส้นทาง</translation>
<translation id="443673843213245140">การใช้พร็อกซีถูกปิดใช้งาน แต่มีการระบุการกำหนดค่าพร็อกซีอย่างชัดเจน</translation>
<translation id="445100540951337728">บัตรเดบิตที่ยอมรับ</translation>
+<translation id="4472575034687746823">เริ่มต้น</translation>
<translation id="4506176782989081258">ข้อผิดพลาดในการตรวจสอบ: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">ติดต่อผู้ดูแลระบบ</translation>
<translation id="450710068430902550">การแชร์กับผู้ดูแลระบบ</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">โหลดนโยบายซ้ำ</translation>
<translation id="4728558894243024398">แพลตฟอร์ม</translation>
<translation id="4736825316280949806">รีสตาร์ท Chromium</translation>
+<translation id="4742407542027196863">จัดการรหัสผ่าน…</translation>
<translation id="4744603770635761495">เส้นทางปฏิบัติการ</translation>
-<translation id="4749685221585524849">ใช้ล่าสุดเมื่อ <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">ข้อมูลของคุณ (ตัวอย่างเช่น รหัสผ่านหรือหมายเลขบัตรเครดิต) จะเป็นส่วนตัวเมื่อส่งมายังเว็บไซต์นี้</translation>
<translation id="4756388243121344051">&amp;ประวัติการเข้าชม</translation>
<translation id="4758311279753947758">เพิ่มข้อมูลติดต่อ</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">มุมมอง</translation>
<translation id="4854362297993841467">วิธีการนำส่งสินค้านี้ไม่พร้อมให้บริการ โปรดลองใช้วิธีการอื่น</translation>
<translation id="4858792381671956233">คุณถามผู้ปกครองแล้วว่าสามารถเข้าชมเว็บไซต์นี้ได้ไหม</translation>
+<translation id="4876305945144899064">ไม่มีชื่อผู้ใช้</translation>
<translation id="4880827082731008257">ค้นประวัติการเข้าชม</translation>
<translation id="4881695831933465202">เปิด</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" /> และ <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">รัฐ</translation>
<translation id="5094747076828555589">เซิร์ฟเวอร์นี้ไม่สามารถพิสูจน์ได้ว่าเป็น <ph name="DOMAIN" /> เพราะ Chromium ไม่เชื่อถือใบรับรองความปลอดภัย โดยอาจเกิดจากการกำหนดค่าผิดหรือผู้บุกรุกที่ขัดขวางการเชื่อมต่อของคุณ</translation>
<translation id="5095208057601539847">จังหวัด</translation>
+<translation id="5098332213681597508">ชื่อนี้มาจากบัญชี Google ของคุณ</translation>
<translation id="5115563688576182185">(64 บิต)</translation>
<translation id="5121084798328133320">เมื่อยืนยันแล้ว ระบบจะแชร์รายละเอียดของบัตรจากบัญชี Google Payments กับเว็บไซต์นี้</translation>
<translation id="5128122789703661928">ลบเซสชันที่ใช้ชื่อนี้ไม่ได้</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">วิธีการจัดส่ง</translation>
<translation id="5355557959165512791">คุณไม่สามารถไปที่ <ph name="SITE" /> ได้ในขณะนี้เนื่องจากใบรับรองถูกเพิกถอนแล้ว โดยปกติข้อผิดพลาดของเครือข่ายและการโจมตีจะเกิดขึ้นเพียงชั่วคราว หน้านี้จึงอาจจะใช้งานได้ในภายหลัง</translation>
<translation id="536296301121032821">ไม่สามารถจัดเก็บการตั้งค่านโยบาย</translation>
+<translation id="5371425731340848620">อัปเดตบัตร</translation>
<translation id="5377026284221673050">"นาฬิกาของคุณช้ากว่าปัจจุบัน" หรือ "นาฬิกาของคุณเร็วกว่าปัจจุบัน" หรือ "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">กลุ่มใบรับรองสำหรับเว็บไซต์นี้มีใบรับรองที่ลงนามโดยใช้ SHA-1</translation>
-<translation id="5402410679244714488">วันที่หมดอายุ: <ph name="EXPIRATION_DATE_ABBR" /> ใช้ล่าสุดกว่า 1 ปีที่ผ่านมา</translation>
+<translation id="5387961145478138773">เข้าถึงแอป Google ที่คุณชอบได้อย่างรวดเร็ว</translation>
<translation id="540969355065856584">เซิร์ฟเวอร์นี้ไม่สามารถพิสูจน์ได้ว่าเป็น <ph name="DOMAIN" /> เนื่องจากใบรับรองความปลอดภัยไม่สามารถใช้ได้ในขณะนี้ ซึ่งอาจเป็นเพราะการกำหนดค่าที่ไม่ถูกต้องหรือมีผู้โจมตีที่ขัดขวางการเชื่อมต่อของคุณ</translation>
<translation id="5421136146218899937">ล้างข้อมูลการท่องเว็บ...</translation>
<translation id="5430298929874300616">นำบุ๊กมาร์กออก</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">อีเมล</translation>
+<translation id="5666899935841546222">คุณต้องการเก็บบัตรทั้งหมดไว้ในที่เดียวไหม</translation>
<translation id="5675650730144413517">หน้านี้ใช้ไม่ได้</translation>
<translation id="5685654322157854305">เพิ่มที่อยู่สำหรับจัดส่ง</translation>
<translation id="5689199277474810259">ส่งออกไปยัง JSON</translation>
<translation id="5689516760719285838">ตำแหน่ง</translation>
<translation id="570530837424789914">จัดการ...</translation>
+<translation id="57094364128775171">แนะนำรหัสผ่านที่รัดกุม…</translation>
<translation id="5710435578057952990">ข้อมูลประจำตัวของเว็บไซต์นี้ยังไม่ได้รับการยืนยัน</translation>
<translation id="5719499550583120431">รับบัตรเติมเงิน</translation>
<translation id="5720705177508910913">ผู้ใช้ปัจจุบัน</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">ไม่สามารถเข้าถึงเว็บไซต์นี้</translation>
<translation id="5869522115854928033">รหัสผ่านที่บันทึกไว้</translation>
<translation id="5893752035575986141">รับบัตรเครดิต</translation>
-<translation id="5898382028489516745">Chromium ขอแนะนำให้รีเซ็ตรหัสผ่าน <ph name="ORG_NAME" /> หากคุณใช้รหัสผ่านนี้ซ้ำในเว็บไซต์อื่น</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (ซิงค์แล้ว)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{ใช้งานอยู่ 1 รายการ}other{ใช้งานอยู่ # รายการ}}</translation>
<translation id="5939518447894949180">รีเซ็ต</translation>
-<translation id="5959728338436674663">ส่ง<ph name="BEGIN_WHITEPAPER_LINK" />ข้อมูลบางอย่างของระบบและเนื้อหาของหน้าเว็บ<ph name="END_WHITEPAPER_LINK" />ไปยัง Google เพื่อช่วยตรวจหาแอปและเว็บไซต์ที่เป็นอันตรายโดยอัตโนมัติ<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">แก้ไขข้อมูลติดต่อ</translation>
<translation id="5967867314010545767">ลบจากประวัติการเข้าชม</translation>
<translation id="5975083100439434680">ย่อ</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">รหัสไปรษณีย์</translation>
<translation id="6290238015253830360">บทความที่แนะนำจะปรากฏที่นี่</translation>
<translation id="6305205051461490394">ไม่สามารถเข้าถึง <ph name="URL" /></translation>
-<translation id="6319915415804115995">ใช้ล่าสุดกว่า 1 ปีที่ผ่านมา</translation>
<translation id="6321917430147971392">ตรวจสอบการตั้งค่า DNS</translation>
<translation id="6328639280570009161">ลองปิดการคาดคะเนเครือข่าย</translation>
<translation id="6328786501058569169">เว็บไซต์นี้มีการหลอกลวง</translation>
<translation id="6337133576188860026">หากเพิ่มพื้นที่ว่างไม่ถึง <ph name="SIZE" /> ไซต์บางแห่งอาจโหลดช้าลงเมื่อคุณเข้าชมครั้งถัดไป</translation>
<translation id="6337534724793800597">กรองนโยบายตามชื่อ</translation>
-<translation id="6342069812937806050">เพิ่งเสร็จ</translation>
<translation id="6355080345576803305">การลบล้างเซสชันสาธารณะ</translation>
<translation id="6358450015545214790">นี่หมายถึงอะไร</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{อีก 1 คำแนะนำ}other{อีก # คำแนะนำ}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;ทำซ้ำการนำออก</translation>
<translation id="6534179046333460208">คำแนะนำ Physical Web</translation>
<translation id="6550675742724504774">ตัวเลือก</translation>
-<translation id="6556915248009097796">วันที่หมดอายุ: <ph name="EXPIRATION_DATE_ABBR" /> ใช้ล่าสุดเมื่อ <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">ผู้จัดการยังไม่ได้อนุมัติเว็บไซต์นี้</translation>
<translation id="6569060085658103619">คุณกำลังดูหน้าส่วนขยาย</translation>
<translation id="6596325263575161958">ตัวเลือกการเข้ารหัส</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">คุณพยายามเข้าถึง <ph name="DOMAIN" /> แต่เซิร์ฟเวอร์แสดงใบรับรองที่ลงนามด้วยอัลกอริทึมลายเซ็นที่ไม่รัดกุม (เช่น SHA-1) ซึ่งหมายความว่าข้อมูลรับรองด้านความปลอดภัยที่เซิร์ฟเวอร์แสดงอาจถูกปลอมแปลงขึ้น และเซิร์ฟเวอร์ดังกล่าวอาจไม่ใช่เซิร์ฟเวอร์ที่คุณคิด (คุณอาจกำลังติดต่อกับผู้โจมตี)</translation>
<translation id="6831043979455480757">แปลภาษา</translation>
<translation id="6839929833149231406">พื้นที่</translation>
+<translation id="6852204201400771460">โหลดแอปซ้ำไหม</translation>
+<translation id="6865412394715372076">ยืนยันบัตรนี้ไม่ได้ในขณะนี้</translation>
<translation id="6874604403660855544">&amp;ทำซ้ำการเพิ่ม</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">ระดับนโยบายไม่ได้รับการสนับสนุน</translation>
<translation id="6895330447102777224">บัตรของคุณได้รับการยืนยันแล้ว</translation>
<translation id="6897140037006041989">User agent</translation>
+<translation id="6903319715792422884">ช่วยปรับปรุง Safe Browsing โดยส่ง<ph name="BEGIN_WHITEPAPER_LINK" />ข้อมูลบางอย่างของระบบและเนื้อหาของหน้า<ph name="END_WHITEPAPER_LINK" />ให้ Google <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">ผู้ใช้:</translation>
+<translation id="6944692733090228304">คุณป้อนรหัสผ่านในเว็บไซต์ที่ <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> ไม่ได้จัดการ เพื่อปกป้องบัญชี โปรดอย่าใช้รหัสผ่านซ้ำในแอปและเว็บไซต์อื่นๆ</translation>
<translation id="6945221475159498467">เลือก</translation>
<translation id="6948701128805548767">หากต้องการดูวิธีการรับสินค้าและข้อกำหนด โปรดเลือกที่อยู่</translation>
<translation id="6949872517221025916">รีเซ็ตรหัสผ่าน</translation>
+<translation id="6950684638814147129">เกิดข้อผิดพลาดขณะแยกวิเคราะห์ค่า JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">ใบรับรองของเซิร์ฟเวอร์น่าจะเป็นของปลอม</translation>
<translation id="6965382102122355670">ตกลง</translation>
<translation id="6965978654500191972">อุปกรณ์</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;คลิก&lt;strong&gt;ใช้&lt;/strong&gt; แล้วคลิก&lt;strong&gt;ตกลง&lt;/strong&gt;
&lt;li&gt;ไปที่&lt;a href="https://support.google.com/chrome/answer/6098869"&gt;ศูนย์ช่วยเหลือของ Chrome&lt;/a&gt; เพื่อดูวิธีนำซอฟต์แวร์ดังกล่าวออกจากคอมพิวเตอร์อย่างถาวร
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">จัดการรหัสผ่าน…</translation>
<translation id="7419106976560586862">เส้นทางโปรไฟล์</translation>
<translation id="7437289804838430631">เพิ่มข้อมูลติดต่อ</translation>
<translation id="7441627299479586546">หัวเรื่องนโยบายไม่ถูกต้อง</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK" />เกี่ยวกับปัญหานี้</translation>
<translation id="7455133967321480974">ใช้ค่าเริ่มต้นสากล (บล็อก)</translation>
<translation id="7460163899615895653">แท็บล่าสุดจากอุปกรณ์อื่นๆ จะปรากฏที่นี่</translation>
-<translation id="7469372306589899959">กำลังยืนยันบัตร</translation>
<translation id="7473891865547856676">ไม่ ขอบคุณ</translation>
<translation id="7481312909269577407">ส่งต่อ</translation>
<translation id="7485870689360869515">ไม่พบข้อมูล</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">การแปลล้มเหลวเนื่องจากเกิดปัญหาการเชื่อมต่อกับเครือข่าย </translation>
<translation id="8311129316111205805">โหลดเซสชัน</translation>
<translation id="8332188693563227489">การเข้าถึง <ph name="HOST_NAME" /> ถูกปฏิเสธ</translation>
+<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="8349305172487531364">แถบบุ๊กมาร์ก</translation>
<translation id="8363502534493474904">ปิดโหมดบนเครื่องบิน</translation>
@@ -1035,21 +1035,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ใช้เวลาตอบกลับนานเกินไป</translation>
<translation id="8503559462189395349">รหัสผ่าน Chrome</translation>
<translation id="8503813439785031346">ชื่อผู้ใช้</translation>
+<translation id="8508648098325802031">ไอคอนค้นหา</translation>
<translation id="8543181531796978784">คุณสามารถ<ph name="BEGIN_ERROR_LINK" />รายงานปัญหาในการตรวจหา<ph name="END_ERROR_LINK" />ได้ หรือหากคุณเข้าใจถึงความเสี่ยงต่อความปลอดภัยของคุณ คุณสามารถ<ph name="BEGIN_LINK" />เข้าชมเว็บไซต์ที่ไม่ปลอดภัย<ph name="END_LINK" />ได้</translation>
<translation id="8543556556237226809">โปรดติดต่อผู้ที่ดูแลโปรไฟล์ของคุณหากมีคำถาม</translation>
<translation id="8553075262323480129">การแปลล้มเหลวเนื่องจากไม่สามารถระบุภาษาของหน้าเว็บนี้ได้</translation>
<translation id="8557066899867184262">CVC จะอยู่ที่ด้านหลังบัตร</translation>
<translation id="8559762987265718583">ไม่สามารถเริ่มการเชื่อมต่อส่วนตัวกับ <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> ได้เนื่องจากวันที่และเวลาของอุปกรณ์ (<ph name="DATE_AND_TIME" />) ไม่ถูกต้อง</translation>
+<translation id="8564985650692024650">Chromium ขอแนะนำให้รีเซ็ตรหัสผ่าน <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> หากคุณใช้รหัสผ่านนี้ซ้ำในเว็บไซต์อื่น</translation>
<translation id="8571890674111243710">กำลังแปลหน้าเว็บนี้เป็นภาษา<ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">เพิ่มเบอร์โทร
</translation>
<translation id="859285277496340001">ใบรับรองไม่ระบุวิธีการตรวจสอบว่ามีการเพิกถอนไปแล้วหรือไม่</translation>
+<translation id="860043288473659153">ชื่อผู้ถือบัตร</translation>
<translation id="8620436878122366504">ผู้ปกครองยังไม่ได้อนุมัติเว็บไซต์นี้</translation>
<translation id="8625384913736129811">บันทึกบัตรนี้ลงในอุปกรณ์นี้</translation>
-<translation id="8639963783467694461">การตั้งค่าป้อนอัตโนมัติ</translation>
+<translation id="8663226718884576429">สรุปคำสั่งซื้อ <ph name="TOTAL_LABEL" /> รายละเอียดเพิ่มเติม</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, คำตอบ, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">การเชื่อมต่อของคุณไปยัง <ph name="DOMAIN" /> ไม่ได้รับการเข้ารหัส</translation>
<translation id="8718314106902482036">การชำระเงินไม่เสร็จสมบูรณ์</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, คำแนะนำการค้นหา</translation>
<translation id="8725066075913043281">ลองอีกครั้ง</translation>
<translation id="8728672262656704056">คุณได้เข้าสู่โหมดไม่ระบุตัวตนแล้ว</translation>
<translation id="8730621377337864115">เสร็จสิ้น</translation>
@@ -1065,8 +1069,10 @@
<translation id="8820817407110198400">บุ๊กมาร์ก</translation>
<translation id="883848425547221593">บุ๊กมาร์กอื่นๆ</translation>
<translation id="884264119367021077">ที่อยู่จัดส่ง</translation>
+<translation id="8846319957959474018">เปิดแอปได้ง่ายๆ ด้วยบุ๊กมาร์ก</translation>
<translation id="884923133447025588">ไม่พบกระบวนการเพิกถอน</translation>
<translation id="885730110891505394">การแชร์กับ Google</translation>
+<translation id="8858065207712248076">Chrome ขอแนะนำให้รีเซ็ตรหัสผ่าน <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> หากคุณใช้รหัสผ่านนี้ซ้ำในเว็บไซต์อื่น</translation>
<translation id="8866481888320382733">ข้อผิดพลาดในการแยกวิเคราะห์การตั้งค่านโยบาย</translation>
<translation id="8870413625673593573">เพิ่งปิด</translation>
<translation id="8874824191258364635">ป้อนหมายเลขบัตรที่ถูกต้อง</translation>
@@ -1105,6 +1111,7 @@
<translation id="9103872766612412690">โดยทั่วไป <ph name="SITE" /> จะใช้การเข้ารหัสเพื่อปกป้องข้อมูลของคุณ เมื่อ Chromium พยายามเชื่อมต่อกับ <ph name="SITE" /> ในครั้งนี้ เว็บไซต์ดังกล่าวส่งข้อมูลรับรองที่ผิดปกติและไม่ถูกต้องกลับมา เหตุการณ์นี้อาจเกิดขึ้นเมื่อผู้บุกรุกพยายามปลอมเป็น <ph name="SITE" /> หรือหน้าจอการลงชื่อเข้าใช้ Wi-Fi รบกวนการเชื่อมต่อ ข้อมูลของคุณยังปลอดภัยอยู่เนื่องจาก Chromium หยุดการเชื่อมต่อก่อนมีการแลกเปลี่ยนข้อมูล</translation>
<translation id="9106062320799175032">เพิ่มที่อยู่สำหรับการเรียกเก็บเงิน</translation>
<translation id="910908805481542201">ช่วยฉันแก้ไขปัญหานี้</translation>
+<translation id="9114524666733003316">กำลังยืนยันบัตร…</translation>
<translation id="9128870381267983090">เชื่อมต่อกับเครือข่าย</translation>
<translation id="9137013805542155359">แสดงหน้าเว็บเดิม</translation>
<translation id="9137248913990643158">โปรดเปิดและลงชื่อเข้าใช้ Chrome ก่อนใช้แอปนี้</translation>
@@ -1115,6 +1122,7 @@
<translation id="9168814207360376865">อนุญาตให้เว็บไซต์ตรวจสอบว่าคุณได้บันทึกวิธีการชำระเงินไว้ไหม</translation>
<translation id="9169664750068251925">บล็อกบนไซต์นี้เสมอ</translation>
<translation id="9170848237812810038">เ&amp;ลิกทำ</translation>
+<translation id="9171296965991013597">ออกจากแอปไหม</translation>
<translation id="917450738466192189">ใบรับรองของเซิร์ฟเวอร์ไม่ถูกต้อง</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> ใช้โปรโตคอลที่ไม่รองรับ</translation>
<translation id="9205078245616868884">ข้อมูลของคุณมีการเข้ารหัสด้วยรหัสผ่านการซิงค์ โปรดป้อนรหัสผ่านเพื่อเริ่มซิงค์</translation>
diff --git a/chromium/components/strings/components_strings_tr.xtb b/chromium/components/strings/components_strings_tr.xtb
index 2fa60f4c785..d6bfc4a7036 100644
--- a/chromium/components/strings/components_strings_tr.xtb
+++ b/chromium/components/strings/components_strings_tr.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Değeri gizle</translation>
<translation id="1228893227497259893">Yanlış varlık tanımlayıcı</translation>
<translation id="1232569758102978740">Adsız</translation>
+<translation id="1250759482327835220">Bir dahaki sefere daha hızlı ödeme yapmak için kartınızı ve fatura adresinizi Google Hesabınıza kaydedin.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (senkronize edildi)</translation>
<translation id="1256368399071562588">&lt;p&gt;Bir web sitesini ziyaret etmeye çalıştığınızda site açılmıyorsa sorunu düzeltmek için ilk olarak aşağıdaki sorun giderme adımlarını deneyin:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Lütfen &lt;strong&gt;Ayarlar&lt;/strong&gt; uygulamasının &lt;strong&gt;Genel&lt;/strong&gt; bölümünden tarih ve saati ayarlayın.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Bu web sayfasını görüntülerken bir hata oluştu.</translation>
-<translation id="1590457302292452960">Güçlü bir şifre oluşturun...</translation>
<translation id="1592005682883173041">Yerel Veri Erişimi</translation>
<translation id="1594030484168838125">Seç</translation>
<translation id="1620510694547887537">Kamera</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Sistem yöneticisiyle iletişime geçmeyi deneyin.</translation>
<translation id="1740951997222943430">Geçerli bir son kullanma ayı girin</translation>
+<translation id="1743520634839655729">Gelecek sefer daha hızlı ödeme yapabilmek için kartınızı, adınızı ve fatura adresinizi Google hesabınıza kaydedin.</translation>
<translation id="17513872634828108">Açık sekmeler</translation>
<translation id="1753706481035618306">Sayfa numarası</translation>
<translation id="1763864636252898013">Bu sunucu <ph name="DOMAIN" /> olduğunu kanıtlayamadı. cihazınızın işletim sistemi, 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>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Açık sekmeleriniz burada görünür</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Kart Sahibinin Adı</translation>
-<translation id="1806541873155184440">Eklenme tarihi: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Geçersiz istek veya istek parametreleri</translation>
<translation id="1826516787628120939">Kontrol ediliyor</translation>
<translation id="1834321415901700177">Bu site zararlı programlar içeriyor</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 öneri}other{# öneri}}</translation>
<translation id="2079545284768500474">Geri al</translation>
<translation id="20817612488360358">Sistem proxy ayarları kullanılmak üzere ayarlandı, ancak açık bir proxy yapılandırması da belirtildi.</translation>
-<translation id="2084558088529668945">Şifrenizi <ph name="ORG_NAME" /> tarafından yönetilmeyen bir sitede girdiniz. Hesabınızı korumak için şifrenizi diğer uygulama ve sitelerde tekrar kullanmayın.</translation>
<translation id="2091887806945687916">Ses</translation>
<translation id="2094505752054353250">Alan adı uyuşmazlığı</translation>
<translation id="2096368010154057602">Bölüm</translation>
+<translation id="2102134110707549001">Güçlü Şifre Öner…</translation>
<translation id="2108755909498034140">Bilgisayarınızı yeniden başlatın</translation>
<translation id="2113977810652731515">Kart</translation>
<translation id="2114841414352855701"><ph name="POLICY_NAME" /> tarafından geçersiz kılındığı için yoksayıldı.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP hatası</translation>
<translation id="2270484714375784793">Telefon numarası</translation>
<translation id="2292556288342944218">İnternet erişiminiz engellendi</translation>
-<translation id="230155334948463882">Yeni bir kart mı?</translation>
<translation id="2316887270356262533">1 MB'tan az yer açar. Bir sonraki ziyaretinizde bazı siteler daha yavaş yüklenebilir.</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> için kullanıcı adı ve şifre gerekiyor.</translation>
<translation id="2317583587496011522">Banka kartları kabul edilir.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Kabul Edilen Kartlar</translation>
<translation id="2702801445560668637">Okuma Listesi</translation>
<translation id="2704283930420550640">Değer, biçimle eşleşmiyor.</translation>
-<translation id="2704951214193499422">Chromium, şu anda kartınızı onaylayamıyor. Lütfen daha sonra tekrar deneyin.</translation>
<translation id="2705137772291741111">Bu sitenin kaydedilen (önbelleğe alınan) kopyası okunamadı.</translation>
<translation id="2709516037105925701">Otomatik doldurma</translation>
<translation id="2710942282213947212">Bilgisayarınızdaki yazılım, Chromium'un web'e güvenli bir şekilde bağlanmasını engelliyor</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Gönderim yöntemi</translation>
<translation id="277499241957683684">Eksik cihaz kaydı</translation>
<translation id="2781030394888168909">MacOS Biçiminde Dışa Aktar</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Bağlantı sıfırlandı.</translation>
<translation id="2788784517760473862">Kabul edilen kredi kartları</translation>
<translation id="2794233252405721443">Site engellenmiş</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Ayarlar sayfasından, bir bağlantı için yapılandırılmış proxy'leri devre dışı bırakabilirsiniz.</translation>
<translation id="2955913368246107853">Bulma çubuğunu kapat</translation>
<translation id="2958431318199492670">Ağ yapılandırması ONC standardıyla uyumlu değil. Yapılandırmanın bazı bölümleri içe aktarılamaz.</translation>
-<translation id="2966678944701946121">Son kullanım tarihi: <ph name="EXPIRATION_DATE_ABBR" />, eklenme tarihi: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Güvenli bir bağlantı kurmak için saatinizin doğru ayarlanmış olması gerekir. Bunun sebebi, web sitelerinin kendilerini tanımlamak için kullandıkları sertifikaların sadece belli süreler için geçerli olmasıdır. Cihazınızın saati yanlış olduğundan, Google Chrome bu sertifikaları doğrulayamıyor.</translation>
<translation id="2972581237482394796">&amp;Yinele</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, şu anda seçili. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Yanlış politika türü</translation>
<translation id="3037605927509011580">Hay aksi!</translation>
<translation id="3041612393474885105">Sertifika Bilgileri</translation>
-<translation id="3063697135517575841">Chrome şu anda kartınızı onaylayamıyor. Lütfen daha sonra tekrar deneyin.</translation>
<translation id="3064966200440839136">Harici bir uygulama üzerinden ödeme gerçekleştirmek için gizli moddan çıkılacak. Devam edilsin mi?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Yok}=1{1 şifre}other{# şifre}}</translation>
<translation id="3096100844101284527">Alınacağı Adres Ekle</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Verileriniz <ph name="TIME" /> tarihinde senkronizasyon parolanızla şifrelendi. Senkronizasyonu başlatmak için senkronizasyon parolanızı girin.</translation>
<translation id="3320021301628644560">Fatura adresi ekle</translation>
<translation id="3338095232262050444">Güvenli</translation>
-<translation id="3340978935015468852">ayarlar</translation>
<translation id="3345135638360864351">Bu siteye erişim isteğiniz <ph name="NAME" /> adlı kullanıcıya gönderilemedi. Lütfen tekrar deneyin.</translation>
<translation id="3355823806454867987">Proxy ayarlarını değiştir...</translation>
<translation id="3361596688432910856">Chrome aşağıdaki bilgileri <ph name="BEGIN_EMPHASIS" />kaydetmez<ph name="END_EMPHASIS" />:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Sunucunun sertifikasına güvenilmiyor.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Senkronize edilmiş cihazlarda en az 1 öğe}=1{1 öğe (ve senkronize edilmiş cihazlarda daha fazlası)}other{# öğe (ve senkronize edilmiş cihazlarda daha fazlası)}}</translation>
<translation id="3539171420378717834">Bu kartın bir kopyasını bu cihazda tut</translation>
-<translation id="3542684924769048008">Şunun için şifre kullan:</translation>
<translation id="3549644494707163724">Senkronize edilen tüm verileri kendi senkronizasyon parolanızla şifreleyin</translation>
<translation id="3556433843310711081">Yöneticiniz engellemeyi kaldırabilir</translation>
<translation id="3566021033012934673">Bağlantınız gizli değil</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Geçersiz doğrulama imzası</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> öğe daha}other{<ph name="ITEM_COUNT" /> öğe daha}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome, <ph name="ORG_NAME" /> şifrenizi başka sitelerde kullandıysanız sıfırlamanızı önerir.</translation>
<translation id="4196861286325780578">Taşımayı &amp;yeniden yap</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Güvenlik duvarı ve virüsten korunma yapılandırmalarını kontrol etme<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Kilitlenmeler</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Yaptığınız değişiklikler kaydedilmemiş olabilir.</translation>
<translation id="4258748452823770588">İmza yanlış</translation>
<translation id="4265872034478892965">Yöneticiniz tarafından izin verildi</translation>
-<translation id="4269787794583293679">(Kullanıcı adı yok)</translation>
<translation id="4275830172053184480">Cihazınızı yeniden başlatın</translation>
<translation id="4277028893293644418">Şifreyi sıfırla</translation>
<translation id="4280429058323657511">, son kullanma tarihi <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Pop-up'lar ve yönlendirmeler</translation>
<translation id="443673843213245140">Proxy kullanımı devre dışı, ancak açık bir proxy yapılandırması belirtildi.</translation>
<translation id="445100540951337728">Kabul edilen banka kartları</translation>
+<translation id="4472575034687746823">Başlayın</translation>
<translation id="4506176782989081258">Doğrulama hatası: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">Sistem yöneticisiyle iletişime geçme</translation>
<translation id="450710068430902550">Yöneticiyle Paylaşma</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Politikaları yeniden yükle</translation>
<translation id="4728558894243024398">Platform</translation>
<translation id="4736825316280949806">Chromium'u yeniden başlatın</translation>
+<translation id="4742407542027196863">Şifreleri yönet…</translation>
<translation id="4744603770635761495">Çalıştırılabilir Yol</translation>
-<translation id="4749685221585524849">Son kullanıldığı tarih: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Bilgileriniz (örneğin şifreler veya kredi kartı numaraları), bu siteye gönderilirken gizli olur.</translation>
<translation id="4756388243121344051">&amp;Geçmiş</translation>
<translation id="4758311279753947758">İletişim bilgilerinizi ekleyin</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Görüntüle</translation>
<translation id="4854362297993841467">Bu teslimat yöntemi kullanılamıyor. Farklı bir yöntem deneyin.</translation>
<translation id="4858792381671956233">Ebeveynlerinize bu siteyi ziyaret etmenizin uygun olup olmadığını sordunuz</translation>
+<translation id="4876305945144899064">Kullanıcı adı yok</translation>
<translation id="4880827082731008257">Geçmişte ara</translation>
<translation id="4881695831933465202">Aç</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Eyalet</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">Bölge</translation>
+<translation id="5098332213681597508">Bu ad Google Hesabınızdan gelmektedir.</translation>
<translation id="5115563688576182185">(64 bit)</translation>
<translation id="5121084798328133320">Onayladığınızda Google Payments hesabınızdaki kart bilgileriniz bu siteyle paylaşılır.</translation>
<translation id="5128122789703661928">Silmek üzere gönderilen bu oturum adı geçerli değil.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Gönderim Yöntemi</translation>
<translation id="5355557959165512791">Sertifikası iptal edildiği için <ph name="SITE" /> sitesini şu anda ziyaret edemezsiniz. Ağ hataları ve saldırılar genellikle geçici olduğundan, bu sayfa muhtemelen daha sonra çalışacaktır.</translation>
<translation id="536296301121032821">Politika ayarları saklanamadı</translation>
+<translation id="5371425731340848620">Kartı güncelle</translation>
<translation id="5377026284221673050">"Saatiniz geri", "Saatiniz ileri" veya "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">Bu sitenin sertifika zinciri, SHA-1 kullanılarak imzalanmış bir sertifika içeriyor.</translation>
-<translation id="5402410679244714488">Son kullanım tarihi: <ph name="EXPIRATION_DATE_ABBR" />, en son bir yıldan uzun bir süre önce kullanıldı</translation>
+<translation id="5387961145478138773">Favori Google Uygulamalarınıza kolayca erişin</translation>
<translation id="540969355065856584">Bu sunucu <ph name="DOMAIN" /> olduğunu kanıtlayamadı. Güvenlik sertifikası şu anda geçerli değil. Bu durum, bir yanlış yapılandırmadan veya bağlantıya müdahale eden bir saldırgandan kaynaklanıyor olabilir.</translation>
<translation id="5421136146218899937">Tarama verilerini temizle...</translation>
<translation id="5430298929874300616">Yer işaretini kaldır</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">E-posta</translation>
+<translation id="5666899935841546222">Tüm kartlarınızı tek bir yerde tutmak istiyor musunuz?</translation>
<translation id="5675650730144413517">Bu sayfa çalışmıyor</translation>
<translation id="5685654322157854305">Gönderim Adresi Ekle</translation>
<translation id="5689199277474810259">JSON'a aktar</translation>
<translation id="5689516760719285838">Konum</translation>
<translation id="570530837424789914">Yönet...</translation>
+<translation id="57094364128775171">Güçlü şifre öner…</translation>
<translation id="5710435578057952990">Bu web sitesinin kimliği doğrulanmadı.</translation>
<translation id="5719499550583120431">Ön ödemeli kartlar kabul edilir.</translation>
<translation id="5720705177508910913">Geçerli kullanıcı</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Bu siteye ulaşılamıyor</translation>
<translation id="5869522115854928033">Kayıtlı şifreler</translation>
<translation id="5893752035575986141">Kredi kartları kabul edilir.</translation>
-<translation id="5898382028489516745">Chromium, <ph name="ORG_NAME" /> şifrenizi başka sitelerde kullandıysanız sıfırlamanızı önerir.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (senkronize edildi)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{1 çerez kullanımda}other{# çerez kullanımda}}</translation>
<translation id="5939518447894949180">Sıfırla</translation>
-<translation id="5959728338436674663">Tehlikeli uygulamaların ve sitelerin tespit edilmesine yardımcı olmak için Google'a bazı <ph name="BEGIN_WHITEPAPER_LINK" />sistem bilgilerini ve sayfa içeriklerini<ph name="END_WHITEPAPER_LINK" /> otomatik olarak gönder.<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">İletişim Bilgilerini Düzenleyin</translation>
<translation id="5967867314010545767">Geçmişten kaldır.</translation>
<translation id="5975083100439434680">Uzaklaştır</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Posta kodu</translation>
<translation id="6290238015253830360">Önerilen makaleler burada görünür</translation>
<translation id="6305205051461490394"><ph name="URL" /> adresine ulaşılamıyor.</translation>
-<translation id="6319915415804115995">En son bir yıldan uzun bir süre önce kullanıldı</translation>
<translation id="6321917430147971392">DNS ayarlarınızı kontrol edin</translation>
<translation id="6328639280570009161">Ağ tahmin özelliğini devre dışı bırakmayı deneyin</translation>
<translation id="6328786501058569169">Bu site yanıltıcıdır</translation>
<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="6342069812937806050">Az önce</translation>
<translation id="6355080345576803305">Herkese açık oturumu geçersiz kılma</translation>
<translation id="6358450015545214790">Bunlar ne anlama geliyor?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 öneri daha}other{# öneri daha}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">Silmeyi &amp;Yeniden Yap</translation>
<translation id="6534179046333460208">Fiziksel Web önerileri</translation>
<translation id="6550675742724504774">Seçenekler</translation>
-<translation id="6556915248009097796">Son kullanım tarihi: <ph name="EXPIRATION_DATE_ABBR" />, son kullanıldığı tarih: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Yöneticiniz henüz onaylamadı</translation>
<translation id="6569060085658103619">Bir uzantı sayfası görüntülüyorsunuz</translation>
<translation id="6596325263575161958">Şifreleme seçenekleri</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703"><ph name="DOMAIN" /> alanına erişme girişiminde bulundunuz ancak sunucu SHA-1 gibi zayıf bir imza algoritması kullanılarak imzalanmış bir sertifika sağladı. Bu, sunucunun sağladığı güvenlik bilgilerinin sahte olabileceği anlamına gelir ve sunucu sizin beklediğiniz sunucu olmayabilir (bir saldırgan ile irtibat kuruyor olabilirsiniz).</translation>
<translation id="6831043979455480757">Çevir</translation>
<translation id="6839929833149231406">Bölge</translation>
+<translation id="6852204201400771460">Uygulama yeniden yüklensin mi?</translation>
+<translation id="6865412394715372076">Bu kart şu anda doğrulanamıyor</translation>
<translation id="6874604403660855544">Eklemeyi &amp;yeniden yap</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Politika düzeyi desteklenmiyor.</translation>
<translation id="6895330447102777224">Kartınız onaylandı</translation>
<translation id="6897140037006041989">Kullanıcı Aracısı</translation>
+<translation id="6903319715792422884">Google'a bazı <ph name="BEGIN_WHITEPAPER_LINK" />sistem bilgilerini ve sayfa içeriklerini<ph name="END_WHITEPAPER_LINK" /> göndererek Güvenli Tarama'nın iyileştirilmesine yardımcı olabilirsiniz. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Kullanıcı:</translation>
+<translation id="6944692733090228304">Şifrenizi <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> tarafından yönetilmeyen bir sitede girdiniz. Hesabınızı korumak için şifrenizi başka uygulama ve sitelerde tekrar kullanmayın.</translation>
<translation id="6945221475159498467">Seç</translation>
<translation id="6948701128805548767">Alım yöntemlerini ve gereksinimlerini görmek için bir adres seçin</translation>
<translation id="6949872517221025916">Şifreyi Sıfırla</translation>
+<translation id="6950684638814147129">JSON değeri ayrıştırılırken hata oluştu: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Sunucunun sertifikası sahte görünüyor.</translation>
<translation id="6965382102122355670">Tamam</translation>
<translation id="6965978654500191972">Cihaz</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;&lt;strong&gt;Uygula&lt;/strong&gt;'yı, ardından &lt;strong&gt;Tamam&lt;/strong&gt;'ı tıklayın
&lt;li&gt;Yazılımı bilgisayarınızdan kalıcı olarak nasıl kaldıracağınızı öğrenmek için &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome yardım merkezini&lt;/a&gt; ziyaret edin
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Şifreleri Yönet…</translation>
<translation id="7419106976560586862">Profil Yolu</translation>
<translation id="7437289804838430631">İletişim Bilgisi Ekle</translation>
<translation id="7441627299479586546">Politika konusu yanlış</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790">Bu sorun hakkında <ph name="BEGIN_LINK" />daha fazla<ph name="END_LINK" /> bilgi edinme.</translation>
<translation id="7455133967321480974">Genel varsayılanı kullan (Engelle)</translation>
<translation id="7460163899615895653">Diğer cihazlardan yeni tarihli sekmeleriniz burada görünür</translation>
-<translation id="7469372306589899959">Kart onaylanıyor</translation>
<translation id="7473891865547856676">Hayır, Teşekkürler</translation>
<translation id="7481312909269577407">İleri</translation>
<translation id="7485870689360869515">Hiçbir veri bulunamadı.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Ağ bağlantısıyla ilgili bir sorun nedeniyle çeviri başarısız oldu.</translation>
<translation id="8311129316111205805">Oturum yükle</translation>
<translation id="8332188693563227489"><ph name="HOST_NAME" /> ana makinesine erişim reddedildi</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Güvenliğinize ilişkin riskleri anladıysanız <ph name="BEGIN_LINK" />bu siteyi<ph name="END_LINK" /> zararlı programlar kaldırılmadan önce ziyaret edebilirsiniz.</translation>
<translation id="8349305172487531364">Yer işaretleri çubuğu</translation>
<translation id="8363502534493474904">Uçak modunu kapatma</translation>
@@ -1035,21 +1035,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> ana makinesinin yanıt vermesi çok uzun sürdü.</translation>
<translation id="8503559462189395349">Chrome Şifreleri</translation>
<translation id="8503813439785031346">Kullanıcı adı</translation>
+<translation id="8508648098325802031">Arama simgesi</translation>
<translation id="8543181531796978784"><ph name="BEGIN_ERROR_LINK" />Bir tespit sorununu bildirebilir<ph name="END_ERROR_LINK" /> veya güvenliğiniz açısından riskleri anlıyorsanız <ph name="BEGIN_LINK" />güvenli olmayan bu siteyi ziyaret edebilirsiniz<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Sorularınız mı var? Profilinizi denetleyen kişiyle iletişime geçin.</translation>
<translation id="8553075262323480129">Sayfanın dili belirlenemediğinden çeviri başarısız oldu.</translation>
<translation id="8557066899867184262">CVC, kartınızın arkasında bulunur.</translation>
<translation id="8559762987265718583">Cihazınızın tarih ve saati (<ph name="DATE_AND_TIME" />) yanlış olduğundan <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> alan adına gizli bir bağlantı kurulamıyor.</translation>
+<translation id="8564985650692024650">Chromium, <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> şifrenizi başka sitelerde kullandıysanız sıfırlamanızı önerir.</translation>
<translation id="8571890674111243710">Sayfa <ph name="LANGUAGE" /> diline çevriliyor...</translation>
<translation id="858637041960032120">Telefon no ekle
</translation>
<translation id="859285277496340001">Sertifika, iptal edilip edilmediğinin denetlenebileceği bir mekanizma belirtmiyor.</translation>
+<translation id="860043288473659153">Kart sahibinin adı</translation>
<translation id="8620436878122366504">Ebeveynleriniz henüz onaylamadı</translation>
<translation id="8625384913736129811">Bu Kartı Bu Cihaza Kaydet</translation>
-<translation id="8639963783467694461">Otomatik doldurma ayarları</translation>
+<translation id="8663226718884576429">Sipariş Özeti, <ph name="TOTAL_LABEL" />, Daha Fazla Ayrıntı</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, yanıt, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206"><ph name="DOMAIN" /> bağlantınız şifrelenmedi.</translation>
<translation id="8718314106902482036">Ödeme işlemi tamamlanmadı</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, arama önerisi</translation>
<translation id="8725066075913043281">Yeniden dene</translation>
<translation id="8728672262656704056">Gizli moda geçtiniz</translation>
<translation id="8730621377337864115">Bitti</translation>
@@ -1065,8 +1069,10 @@
<translation id="8820817407110198400">Favoriler</translation>
<translation id="883848425547221593">Diğer Yer İşaretleri</translation>
<translation id="884264119367021077">Gönderim adresi</translation>
+<translation id="8846319957959474018">Uygulamaları yer işaretleri ile kolayca açın</translation>
<translation id="884923133447025588">İptal mekanizması bulunamadı.</translation>
<translation id="885730110891505394">Google ile Paylaşma</translation>
+<translation id="8858065207712248076">Chrome, <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> şifrenizi başka sitelerde kullandıysanız sıfırlamanızı önerir.</translation>
<translation id="8866481888320382733">Politika ayarlarını ayrıştırma hatası</translation>
<translation id="8870413625673593573">Son Kapatılan</translation>
<translation id="8874824191258364635">Geçerli bir kart numarası girin</translation>
@@ -1105,6 +1111,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> normalde bilgilerinizi korumak için şifreleme kullanmaktadır. Chromium bu sefer <ph name="SITE" /> sitesine bağlanmayı denediğinde, web sitesi sıra dışı ve yanlış kimlik bilgileri döndürdü. Bir saldırgan <ph name="SITE" /> gibi davranmaya çalışıyor olabilir ya da bir Kablosuz oturum açma ekranı bağlantıyı kesmiştir. Chromium herhangi bir veri alışverişinden önce bağlantıyı durdurduğu için bilgileriniz hâlâ güvendedir.</translation>
<translation id="9106062320799175032">Fatura Adresi Ekleyin</translation>
<translation id="910908805481542201">Bunu düzeltmeme yardım edin</translation>
+<translation id="9114524666733003316">Kart onaylanıyor...</translation>
<translation id="9128870381267983090">Ağa bağlan</translation>
<translation id="9137013805542155359">Orijinali göster</translation>
<translation id="9137248913990643158">Lütfen bu uygulamayı kullanmadan önce Chrome'u başlatıp oturum açın.</translation>
@@ -1115,6 +1122,7 @@
<translation id="9168814207360376865">Sitelerin, kayıtlı ödeme yöntemleriniz olup olmadığını kontrol etmesine izin verin</translation>
<translation id="9169664750068251925">Bu sitede her zaman engelle</translation>
<translation id="9170848237812810038">&amp;Geri al</translation>
+<translation id="9171296965991013597">Uygulamadan çıkılsın mı?</translation>
<translation id="917450738466192189">Sunucunun sertifikası geçersiz.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> desteklenmeyen bir protokol kullanıyor.</translation>
<translation id="9205078245616868884">Verileriniz senkronizasyon parolanızla şifrelendi. Senkronizasyonu başlatmak için senkronizasyon parolanızı girin.</translation>
diff --git a/chromium/components/strings/components_strings_uk.xtb b/chromium/components/strings/components_strings_uk.xtb
index 849916fc64a..e397a4520f8 100644
--- a/chromium/components/strings/components_strings_uk.xtb
+++ b/chromium/components/strings/components_strings_uk.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Сховати значення</translation>
<translation id="1228893227497259893">Неправильний ідентифікатор організації</translation>
<translation id="1232569758102978740">Без імені</translation>
+<translation id="1250759482327835220">Щоб наступного разу платити швидше, збережіть дані картки, ім’я та платіжну адресу в обліковому записі Google.</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;
@@ -97,7 +98,6 @@
&lt;p&gt;Налаштуйте дату й час у розділі &lt;strong&gt;Загальні&lt;/strong&gt; додатка &lt;strong&gt;Налаштування&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Під час показу цієї сторінки сталася помилка.</translation>
-<translation id="1590457302292452960">Створіть надійний пароль…</translation>
<translation id="1592005682883173041">Доступ до локальних даних</translation>
<translation id="1594030484168838125">Вибрати</translation>
<translation id="1620510694547887537">Камера</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Зв’яжіться із системним адміністратором.</translation>
<translation id="1740951997222943430">Введіть дійсний місяць закінчення терміну дії</translation>
+<translation id="1743520634839655729">Щоб наступного разу платити швидше, збережіть дані картки, ім’я та платіжну адресу в обліковому записі Google і на цьому пристрої.</translation>
<translation id="17513872634828108">Відкриті вкладки</translation>
<translation id="1753706481035618306">Номер сторінки</translation>
<translation id="1763864636252898013">Цей сервер не зміг довести, що він – домен <ph name="DOMAIN" />. Операційна система вашого пристрою не вважає його сертифікат безпеки надійним. Імовірні причини: неправильна конфігурація або хтось намагається перехопити ваше з’єднання.</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Тут відображатимуться ваші відкриті вкладки</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Ім’я та прізвище власника картки</translation>
-<translation id="1806541873155184440">Додано <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Недійсний запит або параметри запиту</translation>
<translation id="1826516787628120939">Перевірка</translation>
<translation id="1834321415901700177">Цей сайт містить шкідливі програми</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 пропозиція}one{# пропозиція}few{# пропозиції}many{# пропозицій}other{# пропозиції}}</translation>
<translation id="2079545284768500474">Відмінити</translation>
<translation id="20817612488360358">Системні параметри проксі-сервера налаштовано для використання, але чітко вказано налаштування проксі-сервера.</translation>
-<translation id="2084558088529668945">Ви ввели пароль на сайті, яким не керує <ph name="ORG_NAME" />. Щоб захистити свій обліковий запис, не використовуйте цей пароль для інших додатків і сайтів.</translation>
<translation id="2091887806945687916">Сигнал</translation>
<translation id="2094505752054353250">Невідповідність домену</translation>
<translation id="2096368010154057602">Департамент</translation>
+<translation id="2102134110707549001">Запропонувати надійний пароль…</translation>
<translation id="2108755909498034140">Перезавантажте комп’ютер</translation>
<translation id="2113977810652731515">Картка</translation>
<translation id="2114841414352855701">Правило ігнорується, оскільки його замінено правилом <ph name="POLICY_NAME" />.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Помилка HTTP</translation>
<translation id="2270484714375784793">Номер телефону</translation>
<translation id="2292556288342944218">Ваш доступ до Інтернету заблоковано</translation>
-<translation id="230155334948463882">Нова картка?</translation>
<translation id="2316887270356262533">Звільняє менше 1 Мб. Деякі сайти можуть завантажуватися повільніше під час наступного відвідування.</translation>
<translation id="2317259163369394535">Для сайту <ph name="DOMAIN" /> потрібно ввести ім’я користувача та пароль.</translation>
<translation id="2317583587496011522">Дебетові картки, які приймаються.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Картки, які приймаються до оплати</translation>
<translation id="2702801445560668637">Список читання</translation>
<translation id="2704283930420550640">Значення не відповідає формату.</translation>
-<translation id="2704951214193499422">Chromium не вдалося підтвердити дані вашої картки. Спробуйте пізніше.</translation>
<translation id="2705137772291741111">Не вдається прочитати збережену (кешовану) копію цього сайту.</translation>
<translation id="2709516037105925701">Автозаповнення</translation>
<translation id="2710942282213947212">Програмне забезпечення на вашому комп’ютері перешкоджає Chromium безпечно під’єднуватися до Інтернету</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Спосіб відправлення</translation>
<translation id="277499241957683684">Відсутній запис пристрою</translation>
<translation id="2781030394888168909">Експортувати для ОС Mac</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">З’єднання було скинуто.</translation>
<translation id="2788784517760473862">Прийнятні кредитні картки</translation>
<translation id="2794233252405721443">Сайт заблоковано</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Усі проксі-сервери, налаштовані для з’єднання, можна вимкнути на сторінці налаштувань.</translation>
<translation id="2955913368246107853">Закрити панель пошуку</translation>
<translation id="2958431318199492670">Конфігурація мережі не відповідає стандарту ONC. Вона може імпортуватися частково.</translation>
-<translation id="2966678944701946121">Додано <ph name="ADDED_TO_AUTOFILL_MONTH" />, діє до <ph name="EXPIRATION_DATE_ABBR" /></translation>
<translation id="2969319727213777354">Щоб установити безпечне з’єднання, потрібно правильно налаштувати час, оскільки сертифікати, які підтверджують справжність веб-сайтів, дійсні лише протягом певного періоду. На вашому пристрої неправильно налаштовано час, тому Chrome не може перевірити сертифікати.</translation>
<translation id="2972581237482394796">&amp;Повторити</translation>
<translation id="2977665033722899841">Зараз вибрано: <ph name="ROW_NAME" /> (<ph name="ROW_CONTENT" />)</translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Неправильний тип правила</translation>
<translation id="3037605927509011580">От халепа!</translation>
<translation id="3041612393474885105">Інформація про сертифікат</translation>
-<translation id="3063697135517575841">Chrome не вдалося підтвердити дані вашої картки. Спробуйте пізніше.</translation>
<translation id="3064966200440839136">Щоб оплатити в зовнішньому додатку, ви вийдете з режиму анонімного перегляду. Продовжити?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Немає}=1{1 пароль}one{# пароль}few{# паролі}many{# паролів}other{# пароля}}</translation>
<translation id="3096100844101284527">Додати адресу отримання</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Ваші дані було зашифровано <ph name="TIME" /> за допомогою парольної фрази для синхронізації. Введіть її, щоб почати синхронізацію.</translation>
<translation id="3320021301628644560">Додати платіжну адресу</translation>
<translation id="3338095232262050444">Надійне</translation>
-<translation id="3340978935015468852">налаштування</translation>
<translation id="3345135638360864351">Не вдалося надіслати запит на доступ до цього сайту користувачеві <ph name="NAME" />. Повторіть спробу.</translation>
<translation id="3355823806454867987">Змінити налаштування проксі...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />не зберігатиме<ph name="END_EMPHASIS" />:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Сертифікат сервера ненадійний.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Принаймні 1 запис на синхронізованих пристроях}=1{1 запис (і ще на синхронізованих пристроях)}one{# запис (і ще на синхронізованих пристроях)}few{# записи (і ще на синхронізованих пристроях)}many{# записів (і ще на синхронізованих пристроях)}other{# запису (і ще на синхронізованих пристроях)}}</translation>
<translation id="3539171420378717834">Зберігати копію даних цієї картки на цьому пристрої</translation>
-<translation id="3542684924769048008">Використовувати пароль для:</translation>
<translation id="3549644494707163724">Шифрувати всі синхронізовані дані за допомогою власної парольної фрази для синхронізації</translation>
<translation id="3556433843310711081">Адміністратор може розблокувати його</translation>
<translation id="3566021033012934673">З’єднання не конфіденційне</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Недійсний підпис для підтвердження</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{ще <ph name="ITEM_COUNT" /> елемент}one{ще <ph name="ITEM_COUNT" /> елемент}few{ще <ph name="ITEM_COUNT" /> елементи}many{ще <ph name="ITEM_COUNT" /> елементів}other{ще <ph name="ITEM_COUNT" /> елемента}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome радить скинути пароль, який приймає <ph name="ORG_NAME" />, якщо ви використовували його на інших сайтах.</translation>
<translation id="4196861286325780578">&amp;Повторити переміщення</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />перевірити конфігурацію брандмауера й антивірусної програми<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Збої в роботі</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Внесені зміни, можливо, не буде збережено.</translation>
<translation id="4258748452823770588">Недійсний підпис</translation>
<translation id="4265872034478892965">Дозволено адміністратором</translation>
-<translation id="4269787794583293679">(Немає імені користувача)</translation>
<translation id="4275830172053184480">Перезапустіть пристрій</translation>
<translation id="4277028893293644418">Скинути пароль</translation>
<translation id="4280429058323657511">, дійсна до <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Спливаючі вікна й переадресація</translation>
<translation id="443673843213245140">Використання проксі-сервера вимкнено, але чітко вказано налаштування проксі-сервера.</translation>
<translation id="445100540951337728">Прийнятні дебетові картки</translation>
+<translation id="4472575034687746823">Початок роботи</translation>
<translation id="4506176782989081258">Помилка перевірки: <ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">зв’язатися із системним адміністратором</translation>
<translation id="450710068430902550">Надання інформації адміністратору</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Перезавантажити правила</translation>
<translation id="4728558894243024398">Платформа</translation>
<translation id="4736825316280949806">Перезапустіть Chromium</translation>
+<translation id="4742407542027196863">Керувати паролями…</translation>
<translation id="4744603770635761495">Виконуваний шлях</translation>
-<translation id="4749685221585524849">Востаннє використано <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Ваша інформація (як-от паролі та номери кредитних карток) залишається конфіденційною, коли надсилається на цей сайт.</translation>
<translation id="4756388243121344051">&amp;Історія</translation>
<translation id="4758311279753947758">Додати контактну інформацію</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Перегляд</translation>
<translation id="4854362297993841467">Цей спосіб доставки недоступний. Виберіть інший спосіб.</translation>
<translation id="4858792381671956233">Ви надіслали батькам запит на перегляд цього сайту</translation>
+<translation id="4876305945144899064">Немає імені користувача</translation>
<translation id="4880827082731008257">Пошук в історії</translation>
<translation id="4881695831933465202">Відкрити</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Штат</translation>
<translation id="5094747076828555589">Цей сервер не зміг довести, що він – домен <ph name="DOMAIN" />. Chromium не вважає його сертифікат безпеки надійним. Імовірні причини: неправильна конфігурація або хтось намагається перехопити ваше з’єднання.</translation>
<translation id="5095208057601539847">Провінція чи область</translation>
+<translation id="5098332213681597508">Це ім’я з вашого облікового запису Google.</translation>
<translation id="5115563688576182185">(64-розрядна версія)</translation>
<translation id="5121084798328133320">Щойно ви підтвердите дані картки з облікового запису Google Payments, цей сайт отримає доступ до них.</translation>
<translation id="5128122789703661928">Сеанс із цією назвою не можна видалити.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Спосіб доставки</translation>
<translation id="5355557959165512791">Зараз не можна перейти на сторінку <ph name="SITE" />, оскільки цей сертифікат відкликано. Помилки мережі й атаки зазвичай тимчасові, тому ця сторінка, скоріш за все, запрацює пізніше.</translation>
<translation id="536296301121032821">Помилка збереження налаштувань правила</translation>
+<translation id="5371425731340848620">Оновити картку</translation>
<translation id="5377026284221673050">"Ваш годинник відстає", "Ваш годинник спішить" або "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">Ланцюжок сертифіката цього сайту містить сертифікат, підписаний за допомогою SHA-1.</translation>
-<translation id="5402410679244714488">Діє до <ph name="EXPIRATION_DATE_ABBR" />. Востаннє використано понад рік тому</translation>
+<translation id="5387961145478138773">Отримуйте швидкий доступ до улюблених додатків Google</translation>
<translation id="540969355065856584">Серверу не вдалося підтвердити, що це <ph name="DOMAIN" />. Його сертифікат безпеки зараз недійсний. Можливі причини: неправильна конфігурація або хтось перехопив ваше з’єднання.</translation>
<translation id="5421136146218899937">Очистити дані веб-перегляду...</translation>
<translation id="5430298929874300616">Видалити закладку</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">Електронна пошта</translation>
+<translation id="5666899935841546222">Хочете зберігати всі свої картки в одному додатку?</translation>
<translation id="5675650730144413517">Сторінка не працює</translation>
<translation id="5685654322157854305">Додати адресу доставки</translation>
<translation id="5689199277474810259">Експортувати у формат JSON</translation>
<translation id="5689516760719285838">Місцезнаходження</translation>
<translation id="570530837424789914">Керувати…</translation>
+<translation id="57094364128775171">Запропонувати надійний пароль…</translation>
<translation id="5710435578057952990">Ідентифікаційну інформацію цього веб-сайта не було перевірено.</translation>
<translation id="5719499550583120431">Передплачені картки, які приймаються.</translation>
<translation id="5720705177508910913">Поточний користувач</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Немає зв’язку із сайтом</translation>
<translation id="5869522115854928033">Збережені паролі</translation>
<translation id="5893752035575986141">Кредитні картки, які приймаються.</translation>
-<translation id="5898382028489516745">Chromium радить скинути пароль, який приймає <ph name="ORG_NAME" />, якщо ви використовували його на інших сайтах.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (синхронізовано)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{Використовується 1 файл}one{Використовується # файл}few{Використовуються # файли}many{Використовуються # файлів}other{Використовуються # файлу}}</translation>
<translation id="5939518447894949180">Скинути</translation>
-<translation id="5959728338436674663">Автоматично надсилати в Google деяку <ph name="BEGIN_WHITEPAPER_LINK" />інформацію про систему та вміст сторінок<ph name="END_WHITEPAPER_LINK" />, щоб допомогти виявляти небезпечні додатки й сайти<ph name="PRIVACY_PAGE_LINK" />.</translation>
<translation id="5967592137238574583">Змініть контактну інформацію</translation>
<translation id="5967867314010545767">Видалити з історії</translation>
<translation id="5975083100439434680">Зменшити масштаб</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Поштовий код</translation>
<translation id="6290238015253830360">Тут відображатимуться пропоновані статті</translation>
<translation id="6305205051461490394">Сторінка <ph name="URL" /> недоступна.</translation>
-<translation id="6319915415804115995">Востаннє використано понад рік тому</translation>
<translation id="6321917430147971392">Перевірте налаштування DNS</translation>
<translation id="6328639280570009161">Спробуйте вимкнути прогнозування мережі.</translation>
<translation id="6328786501058569169">Це оманливий сайт</translation>
<translation id="6337133576188860026">Звільняє менше <ph name="SIZE" />. Деякі сайти можуть завантажуватися повільніше під час наступного відвідування.</translation>
<translation id="6337534724793800597">Фільтрувати правила за назвою</translation>
-<translation id="6342069812937806050">Лише зараз</translation>
<translation id="6355080345576803305">Заміна загальнодоступного сеансу</translation>
<translation id="6358450015545214790">Що це означає?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{і ще 1 пропозиція}one{і ще # пропозиція}few{і ще # пропозиції}many{і ще # пропозицій}other{і ще # пропозиції}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Повторити видалення</translation>
<translation id="6534179046333460208">Пропозиції сервісу "Інтернет навколо нас"</translation>
<translation id="6550675742724504774">Параметри</translation>
-<translation id="6556915248009097796">Востаннє використано <ph name="LAST_USED_DATE_NO_DETAIL" />, діє до <ph name="EXPIRATION_DATE_ABBR" /></translation>
<translation id="6563469144985748109">Адміністратор ще не схвалив його</translation>
<translation id="6569060085658103619">Ви переглядаєте сторінку розширень</translation>
<translation id="6596325263575161958">Параметри шифрування</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Ви спробували зв’язатися з доменом <ph name="DOMAIN" />, проте сервер надав сертифікат, підписаний із використанням слабкого алгоритму підпису (як-от SHA-1). Це означає, що облікові дані безпеки, надані сервером, можуть бути сфальсифікованими, а сервер – не тим, який вам потрібен (ви можете передавати свої дані зловмиснику).</translation>
<translation id="6831043979455480757">Перекласти</translation>
<translation id="6839929833149231406">Регіон або територія</translation>
+<translation id="6852204201400771460">Перезапустити додаток?</translation>
+<translation id="6865412394715372076">Зараз цю картку не можна підтвердити</translation>
<translation id="6874604403660855544">&amp;Повторити додавання</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Правило не підтримується.</translation>
<translation id="6895330447102777224">Дані картки підтверджено</translation>
<translation id="6897140037006041989">Агент користувача</translation>
+<translation id="6903319715792422884">Допоможіть покращити Безпечний перегляд, надсилаючи в Google деяку <ph name="BEGIN_WHITEPAPER_LINK" />інформацію про систему та вміст сторінок<ph name="END_WHITEPAPER_LINK" />. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Користувач:</translation>
+<translation id="6944692733090228304">Ви ввели пароль на сайті, яким не керує <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />. Щоб захистити свій обліковий запис, не використовуйте цей пароль для інших додатків і сайтів.</translation>
<translation id="6945221475159498467">Вибрати</translation>
<translation id="6948701128805548767">Укажіть адресу, щоб переглянути способи отримання та вимоги.</translation>
<translation id="6949872517221025916">Скидання пароля</translation>
+<translation id="6950684638814147129">Сталася помилка під час синтаксичного аналізу значення JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Схоже, що сертифікат сервера підроблено.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Пристрій</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Натисніть &lt;strong&gt;Застосувати&lt;/strong&gt;, а потім – кнопку &lt;strong&gt;OK&lt;/strong&gt;
&lt;li&gt;Відвідайте &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Довідковий центр Chrome&lt;/a&gt;, щоб дізнатися, як повністю видалити програмне забезпечення з комп’ютера
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Керувати паролями…</translation>
<translation id="7419106976560586862">Шлях до профілю</translation>
<translation id="7437289804838430631">Додати контактну інформацію</translation>
<translation id="7441627299479586546">Неправильна тема правила</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />дізнатися більше<ph name="END_LINK" /> про цю проблему.</translation>
<translation id="7455133967321480974">Використовувати глобальне налаштування за умовчанням (Блокувати)</translation>
<translation id="7460163899615895653">Тут відображатимуться ваші останні вкладки з інших пристроїв</translation>
-<translation id="7469372306589899959">Підтвердження даних картки</translation>
<translation id="7473891865547856676">Ні, дякую</translation>
<translation id="7481312909269577407">Переслати</translation>
<translation id="7485870689360869515">Даних не знайдено.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Переклад не виконано через проблему підключення до мережі.</translation>
<translation id="8311129316111205805">Завантажити сеанс</translation>
<translation id="8332188693563227489">Відмовлено в доступі до хосту <ph name="HOST_NAME" /></translation>
+<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="8349305172487531364">Панель закладок</translation>
<translation id="8363502534493474904">вимкнути режим польоту</translation>
@@ -1035,21 +1035,25 @@
<translation id="8498891568109133222">Хост <ph name="HOST_NAME" /> довго не відповідає.</translation>
<translation id="8503559462189395349">Паролі Chrome</translation>
<translation id="8503813439785031346">Ім’я користувача</translation>
+<translation id="8508648098325802031">Значок пошуку</translation>
<translation id="8543181531796978784">Ви можете <ph name="BEGIN_ERROR_LINK" />повідомити про проблему з пошуком<ph name="END_ERROR_LINK" /> або <ph name="BEGIN_LINK" />перейти на цей незахищений сайт<ph name="END_LINK" /> (якщо розумієте, наскільки це небезпечно).</translation>
<translation id="8543556556237226809">Маєте запитання? Зв’яжіться з особою, яка контролює ваш профіль.</translation>
<translation id="8553075262323480129">Помилка перекладу. Неможливо визначити мову сторінки.</translation>
<translation id="8557066899867184262">Код CVC розташований на зворотному боці картки.</translation>
<translation id="8559762987265718583">Не вдається встановити конфіденційне з’єднання з <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" />, оскільки на пристрої встановлено неправильні дату й час (<ph name="DATE_AND_TIME" />).</translation>
+<translation id="8564985650692024650">Chromium радить скинути пароль <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, якщо ви застосовували його на інших сайтах.</translation>
<translation id="8571890674111243710">Виконується переклад сторінки такою мовою: <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Додати тел.номер
</translation>
<translation id="859285277496340001">Сертифікат не вказує на механізм перевірки його відкликання.</translation>
+<translation id="860043288473659153">Ім’я та прізвище власника картки</translation>
<translation id="8620436878122366504">Батьки ще не схвалили його</translation>
<translation id="8625384913736129811">Зберегти цю картку на пристрої</translation>
-<translation id="8639963783467694461">Налаштування автозаповнення</translation>
+<translation id="8663226718884576429">Підсумок замовлення, <ph name="TOTAL_LABEL" />, докладніше</translation>
<translation id="8680536109547170164">"<ph name="QUERY" />", відповідь: "<ph name="ANSWER" />"</translation>
<translation id="8703575177326907206">Ваше з’єднання з <ph name="DOMAIN" /> не зашифровано.</translation>
<translation id="8718314106902482036">Оплату не завершено</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, пропозиція пошуку</translation>
<translation id="8725066075913043281">Повторити спробу</translation>
<translation id="8728672262656704056">Ви перейшли в режим анонімного перегляду</translation>
<translation id="8730621377337864115">Готово</translation>
@@ -1065,8 +1069,10 @@
<translation id="8820817407110198400">Закладки</translation>
<translation id="883848425547221593">Інші закладки</translation>
<translation id="884264119367021077">Адреса доставки</translation>
+<translation id="8846319957959474018">Легко відкривайте додатки за допомогою закладок</translation>
<translation id="884923133447025588">Не знайдено механізм відкликання.</translation>
<translation id="885730110891505394">Надання інформації службам Google</translation>
+<translation id="8858065207712248076">Chrome радить скинути пароль <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" />, якщо ви застосовували його на інших сайтах.</translation>
<translation id="8866481888320382733">Помилка аналізу налаштувань правила</translation>
<translation id="8870413625673593573">Нещодавно закриті</translation>
<translation id="8874824191258364635">Введіть дійсний номер картки</translation>
@@ -1105,6 +1111,7 @@
<translation id="9103872766612412690">Веб-сайт <ph name="SITE" /> зазвичай використовує шифрування для захисту вашої інформації. Під час цієї спроби Chromium під’єднатися до сторінки <ph name="SITE" /> з неї отримано незвичні й неправильні облікові дані. Це може статися, коли зловмисник намагається видавати себе за веб-сайт <ph name="SITE" /> або з’єднання перервано екраном входу Wi-Fi. Ваша інформація залишається захищеною, оскільки Chromium припинив з’єднання до того, як почався обмін будь-якими даними.</translation>
<translation id="9106062320799175032">Додайте платіжну адресу</translation>
<translation id="910908805481542201">Допоможіть вирішити цю проблему</translation>
+<translation id="9114524666733003316">Підтверджуються дані картки…</translation>
<translation id="9128870381267983090">З'єднатися з мережею</translation>
<translation id="9137013805542155359">Показати оригінал</translation>
<translation id="9137248913990643158">Перш ніж користуватися додатком, увійдіть в обліковий запис Chrome.</translation>
@@ -1115,6 +1122,7 @@
<translation id="9168814207360376865">Дозволити сайтам перевіряти, чи у вас є збережені способи оплати</translation>
<translation id="9169664750068251925">Завжди блокувати на цьому сайті</translation>
<translation id="9170848237812810038">&amp;Скасувати</translation>
+<translation id="9171296965991013597">Закрити додаток?</translation>
<translation id="917450738466192189">Сертифікат сервера недійсний.</translation>
<translation id="9183425211371246419">Протокол, який використовує хост <ph name="HOST_NAME" />, не підтримується.</translation>
<translation id="9205078245616868884">Ваші дані зашифровано за допомогою парольної фрази. Введіть її, щоб почати синхронізацію.</translation>
diff --git a/chromium/components/strings/components_strings_vi.xtb b/chromium/components/strings/components_strings_vi.xtb
index 1105fbdb011..8f60861d6fc 100644
--- a/chromium/components/strings/components_strings_vi.xtb
+++ b/chromium/components/strings/components_strings_vi.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">Ẩn giá trị</translation>
<translation id="1228893227497259893">Số nhận dạng tổ chức không đúng</translation>
<translation id="1232569758102978740">Không tên</translation>
+<translation id="1250759482327835220">Để thanh toán nhanh hơn vào lần tới, hãy lưu địa chỉ thanh toán, tên và thẻ vào Tài khoản Google của bạn.</translation>
<translation id="1253921432148366685"><ph name="TYPE_1" />, <ph name="TYPE_2" /> (được đồng bộ hóa)</translation>
<translation id="1256368399071562588">&lt;p&gt;Nếu bạn cố truy cập vào một trang web nhưng vẫn không mở được, trước tiên, hãy tìm cách khắc phục lỗi này bằng các bước khắc phục sự cố dưới đây:&lt;/p&gt;
&lt;ol&gt;
@@ -97,7 +98,6 @@
&lt;p&gt;Hãy điều chỉnh ngày và giờ từ phần &lt;strong&gt;Chung&lt;/strong&gt; của ứng dụng &lt;strong&gt;Cài đặt&lt;/strong&gt;.&lt;/p&gt;</translation>
<translation id="1583429793053364125">Đã xảy ra lỗi khi hiển thị trang web này.</translation>
-<translation id="1590457302292452960">Tạo mật khẩu mạnh...</translation>
<translation id="1592005682883173041">Quyền truy cập dữ liệu cục bộ</translation>
<translation id="1594030484168838125">Chọn</translation>
<translation id="1620510694547887537">Máy ảnh</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">Thử liên hệ với quản trị viên hệ thống.</translation>
<translation id="1740951997222943430">Nhập tháng hết hạn hợp lệ</translation>
+<translation id="1743520634839655729">Để thanh toán nhanh hơn vào lần tới, hãy lưu địa chỉ thanh toán, tên và thẻ vào thiết bị này và Tài khoản Google của bạn.</translation>
<translation id="17513872634828108">Tab đang mở</translation>
<translation id="1753706481035618306">Số trang</translation>
<translation id="1763864636252898013">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 hệ điều hành thiết bị của bạn 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>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">Tab đang mở của bạn xuất hiện ở đây</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">Tên chủ thẻ</translation>
-<translation id="1806541873155184440">Ngày thêm: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">Yêu cầu hoặc tham số yêu cầu không hợp lệ</translation>
<translation id="1826516787628120939">Đang kiểm tra</translation>
<translation id="1834321415901700177">Trang web này có chứa các chương trình độc hại</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 đề xuất}other{# đề xuất}}</translation>
<translation id="2079545284768500474">Hoàn tác</translation>
<translation id="20817612488360358">Cài đặt proxy hệ thống được đặt để sử dụng nhưng cấu hình proxy rõ ràng cũng được chỉ định.</translation>
-<translation id="2084558088529668945">Bạn đã nhập mật khẩu của mình trên một trang web không phải do <ph name="ORG_NAME" /> quản lý. Để bảo vệ tài khoản, không sử dụng lại mật khẩu của bạn trên các ứng dụng và trang web khác.</translation>
<translation id="2091887806945687916">Âm thanh</translation>
<translation id="2094505752054353250">Miền không khớp</translation>
<translation id="2096368010154057602">Khu hành chính</translation>
+<translation id="2102134110707549001">Đề xuất mật khẩu mạnh…</translation>
<translation id="2108755909498034140">Khởi động lại máy tính của bạn</translation>
<translation id="2113977810652731515">Thẻ</translation>
<translation id="2114841414352855701">Bỏ qua vì đã bị <ph name="POLICY_NAME" /> ghi đè.</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">Lỗi HTTP</translation>
<translation id="2270484714375784793">Số điện thoại</translation>
<translation id="2292556288342944218">Quyền truy cập Internet của bạn bị chặn</translation>
-<translation id="230155334948463882">Thẻ mới?</translation>
<translation id="2316887270356262533">Bộ nhớ đệm còn chưa đầy 1 MB. 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="2317259163369394535"><ph name="DOMAIN" /> yêu cầu tên người dùng và mật khẩu.</translation>
<translation id="2317583587496011522">Thẻ ghi nợ được chấp nhận.</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">Thẻ được chấp nhận</translation>
<translation id="2702801445560668637">Danh sách đọc</translation>
<translation id="2704283930420550640">Giá trị không khớp với định dạng.</translation>
-<translation id="2704951214193499422">Chromium không thể xác nhận thẻ của bạn tại thời điểm này. Vui lòng thử lại sau.</translation>
<translation id="2705137772291741111">Không thể đọc được bản sao đã lưu (đã lưu vào bộ nhớ đệm) của trang web này.</translation>
<translation id="2709516037105925701">Tự động điền</translation>
<translation id="2710942282213947212">Phần mềm trên máy tính của bạn hiện không cho Chromium kết nối an toàn với web</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">Phương thức giao hàng</translation>
<translation id="277499241957683684">Thiếu hồ sơ thiết bị</translation>
<translation id="2781030394888168909">Xuất ở định dạng MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">Kết nối đã được đặt lại.</translation>
<translation id="2788784517760473862">Thẻ tín dụng được chấp nhận</translation>
<translation id="2794233252405721443">Trang web đã bị chặn</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">Bạn có thể tắt mọi proxy được định cấu hình cho kết nối từ trang cài đặt.</translation>
<translation id="2955913368246107853">Đóng thanh tìm</translation>
<translation id="2958431318199492670">Cấu hình mạng không tuân thủ tiêu chuẩn ONC. Các bộ phận của cấu hình có thể không được nhập.</translation>
-<translation id="2966678944701946121">Hết hạn: <ph name="EXPIRATION_DATE_ABBR" />, ngày thêm: <ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">Để thiết lập kết nối an toàn, bạn cần đặt thời gian đúng cho đồng hồ. Nguyên nhân là do chứng chỉ mà các trang web dùng để tự nhận dạng chỉ có hiệu lực trong khoảng thời gian cụ thể. Vì đồng hồ trên thiết bị của bạn không đúng nên Chrome không thể xác minh các chứng chỉ này.</translation>
<translation id="2972581237482394796">&amp;Làm lại</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />, hiện đã chọn. <ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">Loại chính sách sai</translation>
<translation id="3037605927509011580">Ôi, hỏng! </translation>
<translation id="3041612393474885105">Thông tin Chứng chỉ</translation>
-<translation id="3063697135517575841">Chrome không thể xác nhận thẻ của bạn tại thời điểm này. Vui lòng thử lại sau.</translation>
<translation id="3064966200440839136">Rời khỏi chế độ ẩn danh để thanh toán qua một ứng dụng bên ngoài. Tiếp tục?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{Không có}=1{1 mật khẩu}other{# mật khẩu}}</translation>
<translation id="3096100844101284527">Thêm địa chỉ nhận hàng</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">Dữ liệu của bạn đã được mã hóa bằng cụm mật khẩu đồng bộ hóa của bạn vào <ph name="TIME" />. Nhập cụm mật khẩu đó để bắt đầu đồng bộ hóa.</translation>
<translation id="3320021301628644560">Thêm địa chỉ thanh toán</translation>
<translation id="3338095232262050444">Bảo mật</translation>
-<translation id="3340978935015468852">cài đặt</translation>
<translation id="3345135638360864351">Không thể gửi yêu cầu truy cập trang web này của bạn tới <ph name="NAME" />. Vui lòng thử lại.</translation>
<translation id="3355823806454867987">Thay đổi cài đặt proxy...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />sẽ không lưu<ph name="END_EMPHASIS" /> thông tin sau đây:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">Chứng chỉ của máy chủ không đáng tin cậy.</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{Ít nhất 1 mục trên các thiết bị đã đồng bộ hóa}=1{1 mục (và nhiều mục khác trên các thiết bị đã đồng bộ hóa)}other{# mục (và nhiều mục khác trên các thiết bị đã đồng bộ hóa)}}</translation>
<translation id="3539171420378717834">Giữ bản sao thẻ này trên thiết bị này</translation>
-<translation id="3542684924769048008">Sử dụng mật khẩu cho:</translation>
<translation id="3549644494707163724">Mã hóa tất cả dữ liệu đã đồng bộ hóa bằng cụm mật khẩu đồng bộ hóa của riêng bạn</translation>
<translation id="3556433843310711081">Người quản lý của bạn có thể bỏ chặn trang web cho bạn</translation>
<translation id="3566021033012934673">Kết nối của bạn không phải là kết nối riêng tư</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">Chữ ký xác minh không hợp lệ</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{<ph name="ITEM_COUNT" /> mục khác}other{<ph name="ITEM_COUNT" /> mục khác}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">Chrome khuyên bạn nên đặt lại mật khẩu <ph name="ORG_NAME" /> của mình nếu đã sử dụng lại mật khẩu này trên các trang web khác.</translation>
<translation id="4196861286325780578">&amp;Làm lại di chuyển</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />Kiểm tra tường lửa và cấu hình diệt vi-rút<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">Sự cố</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">Các thay đổi bạn đã thực hiện có thể không được lưu.</translation>
<translation id="4258748452823770588">Chữ ký không hợp lệ</translation>
<translation id="4265872034478892965">Được quản trị viên cho phép</translation>
-<translation id="4269787794583293679">(Không có tên người dùng)</translation>
<translation id="4275830172053184480">Khởi động lại thiết bị của bạn</translation>
<translation id="4277028893293644418">Đặt lại mật khẩu</translation>
<translation id="4280429058323657511">, hết hạn <ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">Cửa sổ bật lên và liên kết chuyển hướng</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="445100540951337728">Thẻ ghi nợ được chấp nhận</translation>
+<translation id="4472575034687746823">Bắt đầu</translation>
<translation id="4506176782989081258">Lỗi xác thực: <ph name="VALIDATION_ERROR" />.</translation>
<translation id="4506599922270137252">Liên hệ với quản trị viên hệ thống</translation>
<translation id="450710068430902550">Chia sẻ với quản trị viên</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">Tải lại chính sách</translation>
<translation id="4728558894243024398">Nền tảng</translation>
<translation id="4736825316280949806">Khởi động lại Chromium</translation>
+<translation id="4742407542027196863">Quản lý mật khẩu…</translation>
<translation id="4744603770635761495">Đường dẫn thực thi</translation>
-<translation id="4749685221585524849">Sử dụng lần cuối: <ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">Thông tin của bạn (ví dụ: mật khẩu hoặc số thẻ tín dụng) sẽ được bảo mật khi được gửi tới trang web này.</translation>
<translation id="4756388243121344051">&amp;Lịch sử</translation>
<translation id="4758311279753947758">Thêm thông tin liên hệ</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">Xem</translation>
<translation id="4854362297993841467">Phương thức phân phối này không có sẵn. Hãy thử một phương thức khác.</translation>
<translation id="4858792381671956233">Bạn đã hỏi cha mẹ mình xem có thể truy cập vào trang này hay không</translation>
+<translation id="4876305945144899064">Không có tên người dùng</translation>
<translation id="4880827082731008257">Lịch sử tìm kiếm</translation>
<translation id="4881695831933465202">Mở</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />, <ph name="TYPE_2" />, <ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">Tiểu bang</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/thành phố</translation>
+<translation id="5098332213681597508">Tên này lấy từ Tài khoản Google của bạn.</translation>
<translation id="5115563688576182185">(64 bit)</translation>
<translation id="5121084798328133320">Sau khi bạn xác nhận, thông tin chi tiết thẻ từ tài khoản thanh toán Google của bạn sẽ được chia sẻ với trang web này.</translation>
<translation id="5128122789703661928">Không thể xóa phiên do tên phiên không hợp lệ.</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">Phương thức giao hàng</translation>
<translation id="5355557959165512791">Bạn không thể truy cập vào <ph name="SITE" /> ngay bây giờ vì chứng chỉ của trang này đã bị thu hồi. Lỗi mạng và các cuộc tấn công mạng thường chỉ là tạm thời nên trang này có thể sẽ hoạt động lại sau.</translation>
<translation id="536296301121032821">Không thể lưu trữ cài đặt chính sách</translation>
+<translation id="5371425731340848620">Cập nhật thẻ</translation>
<translation id="5377026284221673050">"Đồng hồ của bạn chạy chậm" hoặc "Đồng hồ của bạn chạy nhanh" hay "&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;"</translation>
<translation id="5386426401304769735">Chuỗi chứng chỉ cho trang web này có chứa một chứng chỉ đã ký bằng SHA-1.</translation>
-<translation id="5402410679244714488">Hết hạn: <ph name="EXPIRATION_DATE_ABBR" />, sử dụng lần cuối hơn một năm trước</translation>
+<translation id="5387961145478138773">Truy cập nhanh vào Google Apps ưa thích của bạn</translation>
<translation id="540969355065856584">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 hợp lệ tại thời điểm nà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="5421136146218899937">Xóa dữ liệu duyệt web...</translation>
<translation id="5430298929874300616">Xóa dấu trang</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">Email</translation>
+<translation id="5666899935841546222">Bạn có muốn lưu tất cả các thẻ của mình ở cùng một nơi không?</translation>
<translation id="5675650730144413517">Trang này hiện không hoạt động</translation>
<translation id="5685654322157854305">Thêm địa chỉ giao hàng</translation>
<translation id="5689199277474810259">Xuất sang định dạng JSON</translation>
<translation id="5689516760719285838">Vị trí</translation>
<translation id="570530837424789914">Quản lý...</translation>
+<translation id="57094364128775171">Đề xuất mật khẩu mạnh…</translation>
<translation id="5710435578057952990">Nhận dạng trang web này chưa được xác minh.</translation>
<translation id="5719499550583120431">Thẻ trả trước được chấp nhận.</translation>
<translation id="5720705177508910913">Người dùng hiện tại</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">Không thể truy cập trang web này</translation>
<translation id="5869522115854928033">Mật khẩu đã lưu</translation>
<translation id="5893752035575986141">Thẻ tín dụng được chấp nhận.</translation>
-<translation id="5898382028489516745">Chromium khuyên bạn nên đặt lại mật khẩu <ph name="ORG_NAME" /> của mình nếu đã sử dụng lại mật khẩu này trên các trang web khác.</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (được đồng bộ hóa)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{Đang sử dụng 1 cookie}other{Đang sử dụng # cookie}}</translation>
<translation id="5939518447894949180">Đặt lại</translation>
-<translation id="5959728338436674663">Tự động gửi một số <ph name="BEGIN_WHITEPAPER_LINK" />thông tin hệ thống và nội dung trang<ph name="END_WHITEPAPER_LINK" /> tới Google để giúp phát hiện các ứng dụng và trang web nguy hiểm. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">Chỉnh sửa thông tin liên hệ</translation>
<translation id="5967867314010545767">Xóa khỏi lịch sử</translation>
<translation id="5975083100439434680">Thu nhỏ</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">Mã bưu chính</translation>
<translation id="6290238015253830360">Bài viết được đề xuất của bạn sẽ xuất hiện ở đây</translation>
<translation id="6305205051461490394">Không thể truy cập <ph name="URL" />.</translation>
-<translation id="6319915415804115995">Sử dụng lần cuối hơn một năm trước</translation>
<translation id="6321917430147971392">Kiểm tra cài đặt DNS của bạn</translation>
<translation id="6328639280570009161">Thử tắt dự đoán mạng</translation>
<translation id="6328786501058569169">Đây là trang web lừa đảo</translation>
<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="6342069812937806050">Vừa mới</translation>
<translation id="6355080345576803305">Ghi đè phiên công khai</translation>
<translation id="6358450015545214790">Những phần này có ý nghĩa gì?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 đề xuất khác}other{# đề xuất khác}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">&amp;Làm lại xóa</translation>
<translation id="6534179046333460208">Đề xuất Web trong cuộc sống</translation>
<translation id="6550675742724504774">Tùy chọn</translation>
-<translation id="6556915248009097796">Hết hạn: <ph name="EXPIRATION_DATE_ABBR" />, sử dụng lần cuối: <ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">Người quản lý của bạn chưa phê duyệt trang web</translation>
<translation id="6569060085658103619">Bạn đang xem trang tiện ích</translation>
<translation id="6596325263575161958">Tùy chọn mã hóa</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">Bạn đã cố gắng truy cập vào <ph name="DOMAIN" /> nhưng máy chủ xuất trình một chứng chỉ được ký bằng một thuật toán chữ ký yếu (chẳng hạn như SHA-1). Điều này có nghĩa là thông tin đăng nhập bảo mật mà máy chủ xuất trình có thể đã bị giả mạo và máy chủ đó có thể không phải là máy chủ mà bạn mong đợi (bạn có thể đang kết nối với kẻ tấn công).</translation>
<translation id="6831043979455480757">Dịch</translation>
<translation id="6839929833149231406">Khu vực</translation>
+<translation id="6852204201400771460">Tải lại ứng dụng?</translation>
+<translation id="6865412394715372076">Hiện không thể xác minh thẻ này</translation>
<translation id="6874604403660855544">&amp;Làm lại thêm</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">Cấp chính sách không được hỗ trợ.</translation>
<translation id="6895330447102777224">Thẻ của bạn đã được xác nhận</translation>
<translation id="6897140037006041989">Tác nhân Người dùng</translation>
+<translation id="6903319715792422884">Giúp cải thiện tính năng Duyệt web an toàn bằng cách gửi một số <ph name="BEGIN_WHITEPAPER_LINK" />thông tin hệ thống và nội dung trang<ph name="END_WHITEPAPER_LINK" /> tới Google. <ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">Người dùng:</translation>
+<translation id="6944692733090228304">Bạn đã nhập mật khẩu trên trang web không do <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> quản lý. Để bảo vệ tài khoản, không sử dụng lại mật khẩu của bạn trên các ứng dụng và trang web khác.</translation>
<translation id="6945221475159498467">Chọn</translation>
<translation id="6948701128805548767">Để xem các yêu cầu và phương thức nhận hàng, hãy chọn một địa chỉ</translation>
<translation id="6949872517221025916">Đặt lại mật khẩu</translation>
+<translation id="6950684638814147129">Lỗi khi phân tích cú pháp giá trị JSON: <ph name="ERROR" /></translation>
<translation id="6957887021205513506">Chứng chỉ của máy chủ dường như giả mạo.</translation>
<translation id="6965382102122355670">OK</translation>
<translation id="6965978654500191972">Thiết bị</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;Nhấp vào &lt;strong&gt;Apply&lt;/strong&gt; (Áp dụng), sau đó nhấp vào &lt;strong&gt;OK&lt;/strong&gt;
&lt;li&gt;Truy cập vào &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Trung tâm trợ giúp Chrome&lt;/a&gt; để tìm hiểu cách xóa vĩnh viễn phần mềm này khỏi máy tính của bạn
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">Quản lý mật khẩu…</translation>
<translation id="7419106976560586862">Đường dẫn cấu hình</translation>
<translation id="7437289804838430631">Thêm thông tin liên hệ</translation>
<translation id="7441627299479586546">Chủ đề chính sách sai</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />Tìm hiểu thêm<ph name="END_LINK" /> về sự cố này.</translation>
<translation id="7455133967321480974">Sử dụng cài đặt mặc định chung (Chặn)</translation>
<translation id="7460163899615895653">Các tab gần đây của bạn từ các thiết bị khác xuất hiện ở đây</translation>
-<translation id="7469372306589899959">Đang xác nhận thẻ</translation>
<translation id="7473891865547856676">Không, cảm ơn</translation>
<translation id="7481312909269577407">Chuyển tiếp</translation>
<translation id="7485870689360869515">Không tìm thấy dữ liệu.</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">Không thể dịch do kết nối mạng có sự cố.</translation>
<translation id="8311129316111205805">Tải phiên</translation>
<translation id="8332188693563227489">Quyền truy cập <ph name="HOST_NAME" /> bị từ chối</translation>
+<translation id="8340095855084055290"><ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
<translation id="834457929814110454">Nếu bạn hiểu các rủi ro về bảo mật, bạn có thể <ph name="BEGIN_LINK" />truy cập trang này<ph name="END_LINK" /> trước khi các chương trình độc hại bị xóa.</translation>
<translation id="8349305172487531364">Thanh dấu trang</translation>
<translation id="8363502534493474904">Tắt chế độ trên máy bay</translation>
@@ -1035,21 +1035,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> mất quá nhiều thời gian để phản hồi.</translation>
<translation id="8503559462189395349">Mật khẩu Chrome</translation>
<translation id="8503813439785031346">Tên người dùng</translation>
+<translation id="8508648098325802031">Biểu tượng Tìm kiếm</translation>
<translation id="8543181531796978784">Bạn có thể <ph name="BEGIN_ERROR_LINK" />báo cáo sự cố đã phát hiện<ph name="END_ERROR_LINK" /> hoặc nếu bạn hiểu rủi ro với bảo mật của mình, hãy <ph name="BEGIN_LINK" />truy cập trang web không an toàn này<ph name="END_LINK" />.</translation>
<translation id="8543556556237226809">Bạn có câu hỏi? Hãy liên hệ với người giám sát hồ sơ của bạn.</translation>
<translation id="8553075262323480129">Dịch thất bại do ngôn ngữ của trang không được xác định.</translation>
<translation id="8557066899867184262">CVC nằm ở mặt sau thẻ của bạn.</translation>
<translation id="8559762987265718583">Không thể thiết lập kết nối riêng tư với <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> vì ngày và giờ (<ph name="DATE_AND_TIME" />) trên thiết bị của bạn không đúng.</translation>
+<translation id="8564985650692024650">Chromium khuyên bạn nên đặt lại mật khẩu <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> của mình nếu đã sử dụng lại mật khẩu này trên các trang web khác.</translation>
<translation id="8571890674111243710">Đang dịch trang sang <ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">Thêm số đ.thoại
</translation>
<translation id="859285277496340001">Chứng chỉ không ghi rõ cơ chế kiểm tra xem chứng chỉ đã bị thu hồi hay chưa.</translation>
+<translation id="860043288473659153">Tên chủ thẻ</translation>
<translation id="8620436878122366504">Cha mẹ của bạn chưa phê duyệt trang web</translation>
<translation id="8625384913736129811">Lưu thẻ này vào thiết bị này</translation>
-<translation id="8639963783467694461">Cài đặt tự động điền</translation>
+<translation id="8663226718884576429">Tóm tắt đơn hàng, <ph name="TOTAL_LABEL" />, chi tiết khác</translation>
<translation id="8680536109547170164"><ph name="QUERY" />, câu trả lời, <ph name="ANSWER" /></translation>
<translation id="8703575177326907206">Kết nối của bạn đến <ph name="DOMAIN" /> không được mã hóa.</translation>
<translation id="8718314106902482036">Thanh toán chưa hoàn tất</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />, <ph name="DESCRIPTION" />, đề xuất tìm kiếm</translation>
<translation id="8725066075913043281">Thử lại</translation>
<translation id="8728672262656704056">Bạn đã chuyển sang chế độ ẩn danh</translation>
<translation id="8730621377337864115">Hoàn tất</translation>
@@ -1065,8 +1069,10 @@
<translation id="8820817407110198400">Dấu trang</translation>
<translation id="883848425547221593">Dấu trang Khác</translation>
<translation id="884264119367021077">Ðịa chỉ giao hàng</translation>
+<translation id="8846319957959474018">Mở ứng dụng dễ dàng nhờ các dấu trang</translation>
<translation id="884923133447025588">Không tìm thấy cơ chế thu hồi.</translation>
<translation id="885730110891505394">Chia sẻ với Google</translation>
+<translation id="8858065207712248076">Chrome khuyên bạn nên đặt lại mật khẩu <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> của mình nếu đã sử dụng lại mật khẩu này trên các trang web khác.</translation>
<translation id="8866481888320382733">Lỗi phân tích cú pháp cài đặt chính sách</translation>
<translation id="8870413625673593573">Các tab đã Đóng gần đây</translation>
<translation id="8874824191258364635">Nhập số thẻ hợp lệ</translation>
@@ -1105,6 +1111,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> thường sử dụng mã hóa để bảo vệ thông tin của bạn. Khi Chromium cố gắng kết nối với <ph name="SITE" /> tại thời điểm này, trang web đã gửi lại thông tin đăng nhập không chính xác và bất thường. Điều này có thể xảy ra khi kẻ tấn công đang cố gắng giả mạo là <ph name="SITE" /> hoặc màn hình đăng nhập Wi-Fi đã làm gián đoạn kết nối. Thông tin của bạn vẫn an toàn do Chromium đã ngừng kết nối trước khi bất kỳ dữ liệu nào được trao đổi.</translation>
<translation id="9106062320799175032">Thêm địa chỉ thanh toán</translation>
<translation id="910908805481542201">Giúp tôi khắc phục vấn đề này</translation>
+<translation id="9114524666733003316">Đang xác nhận thẻ...</translation>
<translation id="9128870381267983090">Kết nối đến mạng</translation>
<translation id="9137013805542155359">Hiển thị văn bản gốc</translation>
<translation id="9137248913990643158">Vui lòng khởi động và đăng nhập vào Chrome trước khi sử dụng ứng dụng này.</translation>
@@ -1115,6 +1122,7 @@
<translation id="9168814207360376865">Cho phép các trang web kiểm tra xem bạn đã lưu phương thức thanh toán hay chưa</translation>
<translation id="9169664750068251925">Luôn chặn trên trang web này</translation>
<translation id="9170848237812810038">H&amp;oàn tác</translation>
+<translation id="9171296965991013597">Thoát khỏi ứng dụng?</translation>
<translation id="917450738466192189">Chứng chỉ của máy chủ không hợp lệ.</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> sử dụng giao thức không được hỗ trợ.</translation>
<translation id="9205078245616868884">Dữ liệu của bạn đã được mã hóa bằng cụm mật khẩu đồng bộ hóa. Nhập cụm mật khẩu đó để bắt đầu đồng bộ hóa.</translation>
diff --git a/chromium/components/strings/components_strings_zh-CN.xtb b/chromium/components/strings/components_strings_zh-CN.xtb
index ae9befc6aad..54dd49afa27 100644
--- a/chromium/components/strings/components_strings_zh-CN.xtb
+++ b/chromium/components/strings/components_strings_zh-CN.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">隐藏值</translation>
<translation id="1228893227497259893">实体标识符有误</translation>
<translation id="1232569758102978740">无标题</translation>
+<translation id="1250759482327835220">若想在下次购物时更快捷地付款,请将您的付款卡信息、姓名和帐单邮寄地址保存到您的 Google 帐号名下。</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;
@@ -97,7 +98,6 @@
&lt;p&gt;请在&lt;strong&gt;设置&lt;/strong&gt;应用的&lt;strong&gt;通用&lt;/strong&gt;部分调整日期和时间。&lt;/p&gt;</translation>
<translation id="1583429793053364125">显示此网页时出了点问题。</translation>
-<translation id="1590457302292452960">生成安全系数高的密码…</translation>
<translation id="1592005682883173041">本地数据访问权限</translation>
<translation id="1594030484168838125">选择</translation>
<translation id="1620510694547887537">摄像头</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">美国运通卡</translation>
<translation id="1734878702283171397">请尝试联系系统管理员。</translation>
<translation id="1740951997222943430">请输入有效的失效月份</translation>
+<translation id="1743520634839655729">若想在下次购物时更快捷地付款,请将您的付款卡信息、姓名和帐单邮寄地址保存到您的 Google 帐号名下以及这台设备中。</translation>
<translation id="17513872634828108">目前打开的标签页</translation>
<translation id="1753706481035618306">页码</translation>
<translation id="1763864636252898013">此服务器无法证明它是<ph name="DOMAIN" />;您设备的操作系统不信任其安全证书。出现此问题的原因可能是配置有误或您的连接被拦截了。</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">您打开的标签页会显示在此处</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">持卡人姓名</translation>
-<translation id="1806541873155184440">添加日期:<ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">请求或请求参数无效</translation>
<translation id="1826516787628120939">正在检查</translation>
<translation id="1834321415901700177">此网站包含有害程序</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 条建议}other{# 条建议}}</translation>
<translation id="2079545284768500474">撤消</translation>
<translation id="20817612488360358">已设置为使用系统代理设置,但同时指定了一个明确的代理配置。</translation>
-<translation id="2084558088529668945">您在一个不受 <ph name="ORG_NAME" /> 管理的网站上输入了您的密码。为了保护您的帐号,请不要在其他应用和网站上重复使用您的密码。</translation>
<translation id="2091887806945687916">声音</translation>
<translation id="2094505752054353250">网域不匹配</translation>
<translation id="2096368010154057602">省</translation>
+<translation id="2102134110707549001">建议安全系数高的密码…</translation>
<translation id="2108755909498034140">重新启动计算机</translation>
<translation id="2113977810652731515">信用卡</translation>
<translation id="2114841414352855701">由于已被 <ph name="POLICY_NAME" /> 替换,该政策已忽略。</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP 错误</translation>
<translation id="2270484714375784793">电话号码</translation>
<translation id="2292556288342944218">您被禁止访问互联网</translation>
-<translation id="230155334948463882">要使用新信用卡吗?</translation>
<translation id="2316887270356262533">释放了不到 1 MB。当您下次访问时,某些网站的加载速度可能会更慢。</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> 要求提供用户名和密码。</translation>
<translation id="2317583587496011522">接受借记卡。</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">接受的银行卡</translation>
<translation id="2702801445560668637">读取列表</translation>
<translation id="2704283930420550640">值不符合格式要求。</translation>
-<translation id="2704951214193499422">Chromium 目前无法确认您的信用卡,请稍后重试。</translation>
<translation id="2705137772291741111">无法读取此网站的已保存(缓存)副本。</translation>
<translation id="2709516037105925701">自动填充</translation>
<translation id="2710942282213947212">您计算机上的软件导致 Chromium 无法安全地连接到网络</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">送货方式</translation>
<translation id="277499241957683684">缺少设备记录</translation>
<translation id="2781030394888168909">以 MacOS 格式导出</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">连接已重置。</translation>
<translation id="2788784517760473862">接受的信用卡</translation>
<translation id="2794233252405721443">网站已被屏蔽</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">您可以在设置页面中停用任何针对某个连接配置的代理。</translation>
<translation id="2955913368246107853">关闭查找栏</translation>
<translation id="2958431318199492670">网络配置不符合 ONC 标准。无法导入配置的某些部分。</translation>
-<translation id="2966678944701946121">到期日期:<ph name="EXPIRATION_DATE_ABBR" />;添加日期:<ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">要建立安全连接,您的时钟设置必须正确。这是因为,网站用于证明身份的证书仅在特定时间段有效。由于您设备的时钟不正确,因此 Google Chrome 无法验证这些证书。</translation>
<translation id="2972581237482394796">重做(&amp;R)</translation>
<translation id="2977665033722899841">目前已选择“<ph name="ROW_NAME" />”。<ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">策略类型有误</translation>
<translation id="3037605927509011580">喔唷,崩溃啦!</translation>
<translation id="3041612393474885105">证书信息</translation>
-<translation id="3063697135517575841">Chrome 目前无法确认您的信用卡,请稍后重试。</translation>
<translation id="3064966200440839136">将要退出隐身模式,以便通过外部应用付款。是否继续?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{无}=1{1 个密码}other{# 个密码}}</translation>
<translation id="3096100844101284527">添加取货地址</translation>
@@ -339,7 +336,6 @@
<translation id="3305707030755673451">您的数据已于 <ph name="TIME" />使用您的同步密码加密。输入该密码即可开始同步。</translation>
<translation id="3320021301628644560">添加账单邮寄地址</translation>
<translation id="3338095232262050444">安全</translation>
-<translation id="3340978935015468852">设置</translation>
<translation id="3345135638360864351">无法将您访问此网站的请求发送给<ph name="NAME" />,请重试。</translation>
<translation id="3355823806454867987">更改代理服务器设置...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />不会保存<ph name="END_EMPHASIS" />以下信息:
@@ -373,7 +369,6 @@
<translation id="3528171143076753409">服务器的证书不受信任。</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{在已同步的设备上至少有 1 项内容}=1{1 项内容(在已同步的设备上还有更多内容)}other{# 项内容(在已同步的设备上还有更多内容)}}</translation>
<translation id="3539171420378717834">在此设备上保存此信用卡的副本</translation>
-<translation id="3542684924769048008">使用以下项的密码:</translation>
<translation id="3549644494707163724">使用您自己的同步密码加密所有已同步数据</translation>
<translation id="3556433843310711081">您的管理员可为您取消屏蔽此网站</translation>
<translation id="3566021033012934673">您的连接不是私密连接</translation>
@@ -458,7 +453,6 @@
<translation id="4171400957073367226">验证签名无效</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{另外 <ph name="ITEM_COUNT" /> 项}other{另外 <ph name="ITEM_COUNT" /> 项}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> - <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">如果您在其他网站上重复使用了您的 <ph name="ORG_NAME" /> 密码,Chrome 建议您重置该密码。</translation>
<translation id="4196861286325780578">恢复移动(&amp;R)</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />检查防火墙和防病毒配置<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">崩溃</translation>
@@ -487,7 +481,6 @@
<translation id="425582637250725228">系统可能不会保存您所做的更改。</translation>
<translation id="4258748452823770588">签名无效</translation>
<translation id="4265872034478892965">您的管理员允许</translation>
-<translation id="4269787794583293679">(无用户名)</translation>
<translation id="4275830172053184480">重启您的设备</translation>
<translation id="4277028893293644418">重置密码</translation>
<translation id="4280429058323657511">,到期日期:<ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -510,6 +503,7 @@
<translation id="4434045419905280838">弹出式窗口和重定向</translation>
<translation id="443673843213245140">已停用代理,但是指定了明确的代理配置。</translation>
<translation id="445100540951337728">接受的借记卡</translation>
+<translation id="4472575034687746823">开始使用</translation>
<translation id="4506176782989081258">验证错误:<ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">联系系统管理员</translation>
<translation id="450710068430902550">与管理员分享</translation>
@@ -534,8 +528,8 @@
<translation id="4726672564094551039">重新加载政策</translation>
<translation id="4728558894243024398">平台</translation>
<translation id="4736825316280949806">重新启动 Chromium</translation>
+<translation id="4742407542027196863">管理密码…</translation>
<translation id="4744603770635761495">可执行文件路径</translation>
-<translation id="4749685221585524849">上次使用日期:<ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">您发送给这个网站的信息(例如密码或信用卡号)不会外泄。</translation>
<translation id="4756388243121344051">历史记录(&amp;H)</translation>
<translation id="4758311279753947758">添加联系信息</translation>
@@ -552,6 +546,7 @@
<translation id="4850886885716139402">视图</translation>
<translation id="4854362297993841467">该递送方式不可用。请另选一种方式。</translation>
<translation id="4858792381671956233">您已向父母发送请求,询问他们是否允许您访问此网站</translation>
+<translation id="4876305945144899064">无用户名</translation>
<translation id="4880827082731008257">搜索历史记录</translation>
<translation id="4881695831933465202">打开</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />、<ph name="TYPE_2" />、<ph name="TYPE_3" /></translation>
@@ -585,6 +580,7 @@
<translation id="5089810972385038852">州</translation>
<translation id="5094747076828555589">此服务器无法证明它是<ph name="DOMAIN" />;Chromium不信任其安全证书。出现此问题的原因可能是配置有误或您的连接被拦截了。</translation>
<translation id="5095208057601539847">省</translation>
+<translation id="5098332213681597508">此名称来自您的 Google 帐号。</translation>
<translation id="5115563688576182185">(64 位)</translation>
<translation id="5121084798328133320">在您确认之后,您的 Google Payments 帐号中的信用卡详细信息将会共享给该网站。</translation>
<translation id="5128122789703661928">无法删除此会话,因为它的名称无效。</translation>
@@ -625,9 +621,10 @@
<translation id="5332219387342487447">送货方式</translation>
<translation id="5355557959165512791">您目前无法访问 <ph name="SITE" />,因为此证书已被撤消。网络错误和攻击行为通常是暂时的,因此,此网页稍后可能会恢复正常。</translation>
<translation id="536296301121032821">无法存储策略设置</translation>
+<translation id="5371425731340848620">更新信用卡</translation>
<translation id="5377026284221673050">“您的时钟慢了”、“您的时钟快了”或“&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;”</translation>
<translation id="5386426401304769735">此网站的证书链包含使用 SHA-1 签署的证书。</translation>
-<translation id="5402410679244714488">到期日期:<ph name="EXPIRATION_DATE_ABBR" />;距离上次使用已超过 1 年</translation>
+<translation id="5387961145478138773">快速访问您最喜爱的各款 Google 应用</translation>
<translation id="540969355065856584">此服务器无法证明它是 <ph name="DOMAIN" />;其安全证书目前无效。出现此问题的原因可能是配置有误或您的连接被拦截了。</translation>
<translation id="5421136146218899937">清除浏览数据...</translation>
<translation id="5430298929874300616">移除书签</translation>
@@ -669,11 +666,13 @@
<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="5659593005791499971">电子邮件</translation>
+<translation id="5666899935841546222">想集中管理您的所有卡片吗?</translation>
<translation id="5675650730144413517">该网页无法正常运作</translation>
<translation id="5685654322157854305">添加送货地址</translation>
<translation id="5689199277474810259">导出为 JSON 格式</translation>
<translation id="5689516760719285838">位置</translation>
<translation id="570530837424789914">管理…</translation>
+<translation id="57094364128775171">建议安全系数高的密码…</translation>
<translation id="5710435578057952990">此网站尚未经过身份验证。</translation>
<translation id="5719499550583120431">接受预付卡。</translation>
<translation id="5720705177508910913">当前用户</translation>
@@ -694,11 +693,9 @@
<translation id="5869405914158311789">无法访问此网站</translation>
<translation id="5869522115854928033">已保存的密码</translation>
<translation id="5893752035575986141">接受信用卡。</translation>
-<translation id="5898382028489516745">如果您在其他网站上重复使用了您的 <ph name="ORG_NAME" /> 密码,Chromium 建议您重置该密码。</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" />(已同步)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{正在使用 1 个}other{正在使用 # 个}}</translation>
<translation id="5939518447894949180">重置</translation>
-<translation id="5959728338436674663">自动向 Google 发送一些<ph name="BEGIN_WHITEPAPER_LINK" />系统信息和网页内容<ph name="END_WHITEPAPER_LINK" />,以帮助检测危险应用和网站。<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">修改联系信息</translation>
<translation id="5967867314010545767">从历史记录中移除</translation>
<translation id="5975083100439434680">缩小</translation>
@@ -743,13 +740,11 @@
<translation id="6282194474023008486">邮编</translation>
<translation id="6290238015253830360">为您推荐的文章会显示在此处</translation>
<translation id="6305205051461490394">无法访问 <ph name="URL" />。</translation>
-<translation id="6319915415804115995">距离上次使用已超过 1 年</translation>
<translation id="6321917430147971392">请检查您的DNS设置是否正确</translation>
<translation id="6328639280570009161">请尝试停用网络联想查询功能</translation>
<translation id="6328786501058569169">此网站是欺骗性网站</translation>
<translation id="6337133576188860026">释放了不到 <ph name="SIZE" />。当您下次访问时,某些网站的加载速度可能会更慢。</translation>
<translation id="6337534724793800597">按名称过滤政策</translation>
-<translation id="6342069812937806050">刚刚</translation>
<translation id="6355080345576803305">覆盖公用自助终端</translation>
<translation id="6358450015545214790">这分别意味着什么?</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{1 条其他建议}other{# 条其他建议}}</translation>
@@ -774,7 +769,6 @@
<translation id="6529602333819889595">恢复删除(&amp;R)</translation>
<translation id="6534179046333460208">实物网建议</translation>
<translation id="6550675742724504774">选项</translation>
-<translation id="6556915248009097796">到期日期:<ph name="EXPIRATION_DATE_ABBR" />;上次使用日期:<ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">您的管理员尚未批准此网站</translation>
<translation id="6569060085658103619">您正在查看扩展程序页面</translation>
<translation id="6596325263575161958">加密选项</translation>
@@ -803,15 +797,20 @@
<translation id="6825578344716086703">您尝试访问的是 <ph name="DOMAIN" />,但是服务器出示的证书是使用弱签名算法(例如 SHA-1)签署的。这意味着服务器出示的安全凭据可能是伪造的,因此这可能并不是您想要访问的服务器(您可能正在与攻击者进行通信)。</translation>
<translation id="6831043979455480757">翻译</translation>
<translation id="6839929833149231406">地域</translation>
+<translation id="6852204201400771460">要重新加载应用吗?</translation>
+<translation id="6865412394715372076">暂时无法验证此信用卡</translation>
<translation id="6874604403660855544">恢复添加(&amp;R)</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">政策级别不受支持。</translation>
<translation id="6895330447102777224">已确认您的信用卡</translation>
<translation id="6897140037006041989">用户代理</translation>
+<translation id="6903319715792422884">您可以选择向 Google 发送一些<ph name="BEGIN_WHITEPAPER_LINK" />系统信息和网页内容<ph name="END_WHITEPAPER_LINK" />,以帮助我们改进安全浏览功能。<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">用户:</translation>
+<translation id="6944692733090228304">您在一个不受 <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> 管理的网站上输入了您的密码。为了保护您的帐号,请不要在其他应用和网站上重复使用您的密码。</translation>
<translation id="6945221475159498467">选择</translation>
<translation id="6948701128805548767">要查看取货方式和要求,请选择相应地址</translation>
<translation id="6949872517221025916">重置密码</translation>
+<translation id="6950684638814147129">解析 JSON 值时出错:<ph name="ERROR" /></translation>
<translation id="6957887021205513506">该服务器的证书似乎是伪造的。</translation>
<translation id="6965382102122355670">确定</translation>
<translation id="6965978654500191972">设备</translation>
@@ -873,6 +872,7 @@
&lt;li&gt;依次点击&lt;strong&gt;应用&lt;/strong&gt;和&lt;strong&gt;确定&lt;/strong&gt;
&lt;li&gt;访问 &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome 帮助中心&lt;/a&gt;,了解如何从您的计算机中永久移除该软件
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">管理密码…</translation>
<translation id="7419106976560586862">个人资料路径</translation>
<translation id="7437289804838430631">添加联系信息</translation>
<translation id="7441627299479586546">策略主题有误</translation>
@@ -881,7 +881,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />详细了解<ph name="END_LINK" />此问题。</translation>
<translation id="7455133967321480974">使用全局默认设置(阻止)</translation>
<translation id="7460163899615895653">此处将会显示您最近从其他设备打开的标签页</translation>
-<translation id="7469372306589899959">确认信用卡</translation>
<translation id="7473891865547856676">不,谢谢</translation>
<translation id="7481312909269577407">前进</translation>
<translation id="7485870689360869515">找不到数据。</translation>
@@ -1011,6 +1010,7 @@
<translation id="8308427013383895095">由于网络连接问题,翻译失败。</translation>
<translation id="8311129316111205805">加载会话</translation>
<translation id="8332188693563227489">访问 <ph name="HOST_NAME" /> 的请求遭到拒绝</translation>
+<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="8349305172487531364">书签栏</translation>
<translation id="8363502534493474904">关闭飞行模式</translation>
@@ -1031,21 +1031,25 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> 的响应时间过长。</translation>
<translation id="8503559462189395349">Chrome 密码</translation>
<translation id="8503813439785031346">用户名</translation>
+<translation id="8508648098325802031">搜索图标</translation>
<translation id="8543181531796978784">您可以<ph name="BEGIN_ERROR_LINK" />报告检测问题<ph name="END_ERROR_LINK" />;或者,如果您了解自己将面临的安全风险,则可以<ph name="BEGIN_LINK" />访问这个不安全的网站<ph name="END_LINK" />。</translation>
<translation id="8543556556237226809">有问题?请与负责监管您的个人资料的人员联系。</translation>
<translation id="8553075262323480129">系统无法确定该网页的语言,因此无法进行翻译。</translation>
<translation id="8557066899867184262">银行卡验证码 (CVC) 位于银行卡背面。</translation>
<translation id="8559762987265718583">您设备的日期和时间(<ph name="DATE_AND_TIME" />)不正确,因此无法与 <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> 建立私密连接。</translation>
+<translation id="8564985650692024650">如果您在其他网站上重复使用了您的 <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> 密码,Chromium 建议您重置该密码。</translation>
<translation id="8571890674111243710">正在将网页翻译成<ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">添加电话号码
</translation>
<translation id="859285277496340001">证书未指定用于检查是否已吊销证书的机制。</translation>
+<translation id="860043288473659153">持卡人姓名</translation>
<translation id="8620436878122366504">您的父母尚未批准此请求</translation>
<translation id="8625384913736129811">将此卡的信息保存到该设备</translation>
-<translation id="8639963783467694461">自动填充设置</translation>
+<translation id="8663226718884576429">订单摘要,<ph name="TOTAL_LABEL" />,更多详情</translation>
<translation id="8680536109547170164"><ph name="QUERY" />,答案,<ph name="ANSWER" /></translation>
<translation id="8703575177326907206">您与 <ph name="DOMAIN" /> 的连接未加密。</translation>
<translation id="8718314106902482036">未能完成付款</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />,<ph name="DESCRIPTION" />,搜索建议</translation>
<translation id="8725066075913043281">重试</translation>
<translation id="8728672262656704056">您已进入无痕模式</translation>
<translation id="8730621377337864115">完成</translation>
@@ -1061,8 +1065,10 @@
<translation id="8820817407110198400">书签</translation>
<translation id="883848425547221593">其他书签</translation>
<translation id="884264119367021077">送货地址</translation>
+<translation id="8846319957959474018">利用书签轻松打开应用</translation>
<translation id="884923133447025588">未找到任何吊销机制。</translation>
<translation id="885730110891505394">与 Google 分享</translation>
+<translation id="8858065207712248076">如果您在其他网站上重复使用了您的 <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> 密码,Chrome 建议您重置该密码。</translation>
<translation id="8866481888320382733">解析策略设置时出错</translation>
<translation id="8870413625673593573">最近关闭的标签页</translation>
<translation id="8874824191258364635">请输入有效的信用卡号</translation>
@@ -1101,6 +1107,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> 通常会使用加密技术来保护您的信息。Chromium 此次尝试连接到 <ph name="SITE" /> 时,此网站发回了异常的错误凭据。这可能是因为有攻击者在试图冒充 <ph name="SITE" />,或 Wi-Fi 登录屏幕中断了此次连接。请放心,您的信息仍然是安全的,因为 Chromium 尚未进行任何数据交换便停止了连接。</translation>
<translation id="9106062320799175032">添加帐单邮寄地址</translation>
<translation id="910908805481542201">帮我解决此问题</translation>
+<translation id="9114524666733003316">正在确认信用卡…</translation>
<translation id="9128870381267983090">连接到网络</translation>
<translation id="9137013805542155359">显示原始网页</translation>
<translation id="9137248913990643158">在使用此应用前,请先启动并登录 Chrome。</translation>
@@ -1111,6 +1118,7 @@
<translation id="9168814207360376865">允许网站检查您是否已保存付款方式</translation>
<translation id="9169664750068251925">在此网站上始终阻止</translation>
<translation id="9170848237812810038">撤消(&amp;U)</translation>
+<translation id="9171296965991013597">要退出应用吗?</translation>
<translation id="917450738466192189">服务器证书无效。</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> 使用了不受支持的协议。</translation>
<translation id="9205078245616868884">您的数据已使用您的同步密码加密。输入该密码即可开始同步。</translation>
diff --git a/chromium/components/strings/components_strings_zh-TW.xtb b/chromium/components/strings/components_strings_zh-TW.xtb
index 750d0201bbd..05784f1a105 100644
--- a/chromium/components/strings/components_strings_zh-TW.xtb
+++ b/chromium/components/strings/components_strings_zh-TW.xtb
@@ -43,6 +43,7 @@
<translation id="1227633850867390598">隱藏政策值</translation>
<translation id="1228893227497259893">實體識別碼錯誤</translation>
<translation id="1232569758102978740">未命名</translation>
+<translation id="1250759482327835220">只要將你的卡片資訊、姓名與帳單地址儲存到你的 Google 帳戶中,下次即可更快完成付款程序。</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;
@@ -97,7 +98,6 @@
&lt;p&gt;請前往「設定」應用程式的「一般設定」專區調整日期和時間。&lt;strong&gt;&lt;/strong&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;</translation>
<translation id="1583429793053364125">顯示這個網頁時發生錯誤。</translation>
-<translation id="1590457302292452960">產生高強度密碼...</translation>
<translation id="1592005682883173041">本機資料存取權</translation>
<translation id="1594030484168838125">選擇</translation>
<translation id="1620510694547887537">攝影機</translation>
@@ -123,6 +123,7 @@
<translation id="1734864079702812349">Amex</translation>
<translation id="1734878702283171397">建議您與系統管理員聯絡。</translation>
<translation id="1740951997222943430">請輸入有效的到期月份</translation>
+<translation id="1743520634839655729">只要將你的卡片資訊、姓名與帳單地址儲存到你的 Google 帳戶中和這部裝置上,下次即可更快完成付款程序。</translation>
<translation id="17513872634828108">開啟分頁</translation>
<translation id="1753706481035618306">頁碼</translation>
<translation id="1763864636252898013">伺服器無法證明其屬於 <ph name="DOMAIN" /> 網域;其安全性憑證未取得你裝置作業系統的信任。這可能是因為設定錯誤,或有攻擊者攔截你的連線所致。</translation>
@@ -131,7 +132,6 @@
<translation id="1787142507584202372">這裡會顯示你最近開啟的分頁</translation>
<translation id="1791429645902722292">Google Smart Lock</translation>
<translation id="1803264062614276815">持卡人姓名</translation>
-<translation id="1806541873155184440">新增日期:<ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="1821930232296380041">要求或要求參數無效</translation>
<translation id="1826516787628120939">檢查中</translation>
<translation id="1834321415901700177">這個網站含有有害程式</translation>
@@ -164,10 +164,10 @@
<translation id="2064691555167957331">{COUNT,plural, =1{1 個建議項目}other{# 個建議項目}}</translation>
<translation id="2079545284768500474">復原</translation>
<translation id="20817612488360358">雖然系統 Proxy 設定已設為使用,不過也指定了明確 Proxy 設定。</translation>
-<translation id="2084558088529668945">你在不是由 <ph name="ORG_NAME" /> 管理的網站上輸入了你的密碼。為確保帳戶安全,請勿在其他應用程式和網站上重複使用你的密碼。</translation>
<translation id="2091887806945687916">音訊</translation>
<translation id="2094505752054353250">網域不符</translation>
<translation id="2096368010154057602">省</translation>
+<translation id="2102134110707549001">建議高強度密碼…</translation>
<translation id="2108755909498034140">重新啟動電腦</translation>
<translation id="2113977810652731515">信用卡</translation>
<translation id="2114841414352855701">由於政策被「<ph name="POLICY_NAME" />」覆寫了,因此遭到略過。</translation>
@@ -194,7 +194,6 @@
<translation id="2262243747453050782">HTTP 錯誤</translation>
<translation id="2270484714375784793">電話號碼</translation>
<translation id="2292556288342944218">您的網際網路存取權遭到封鎖</translation>
-<translation id="230155334948463882">新信用卡?</translation>
<translation id="2316887270356262533">釋出不到 1 MB。下次造訪部分網站時,載入速度可能會變慢。</translation>
<translation id="2317259163369394535"><ph name="DOMAIN" /> 要求提供使用者名稱和密碼。</translation>
<translation id="2317583587496011522">接受簽帳金融卡。</translation>
@@ -248,7 +247,6 @@
<translation id="2699302886720511147">接受的卡片</translation>
<translation id="2702801445560668637">閱讀清單</translation>
<translation id="2704283930420550640">政策值格式不符。</translation>
-<translation id="2704951214193499422">Chromium 目前無法驗證您的信用卡,請稍後再試。</translation>
<translation id="2705137772291741111">已儲存 (快取) 這個網站的複本,但無法讀取。</translation>
<translation id="2709516037105925701">自動填入</translation>
<translation id="2710942282213947212">你的電腦上有軟體在阻止 Chromium 建立安全的網路連線</translation>
@@ -263,6 +261,7 @@
<translation id="277133753123645258">運送方式</translation>
<translation id="277499241957683684">沒有裝置記錄</translation>
<translation id="2781030394888168909">匯出 MacOS</translation>
+<translation id="2781692009645368755">Google Pay</translation>
<translation id="2784949926578158345">連線已重設。</translation>
<translation id="2788784517760473862">接受的信用卡</translation>
<translation id="2794233252405721443">網站遭到封鎖</translation>
@@ -284,7 +283,6 @@
<translation id="2948083400971632585">你可以在設定頁面停用任何為連線設置的 Proxy。</translation>
<translation id="2955913368246107853">關閉搜尋列</translation>
<translation id="2958431318199492670">網路設定未遵循 ONC 標準,系統可能無法匯入部分設定。</translation>
-<translation id="2966678944701946121">到期日:<ph name="EXPIRATION_DATE_ABBR" />,新增日期:<ph name="ADDED_TO_AUTOFILL_MONTH" /></translation>
<translation id="2969319727213777354">您必須正確設定時鐘,才能建立安全連線。這是因為網站驗證身分時所使用的憑證僅於特定一段時間內有效。由於您裝置的時鐘不正確,因此 Google Chrome 無法驗證這些憑證。</translation>
<translation id="2972581237482394796">重做(&amp;R)</translation>
<translation id="2977665033722899841"><ph name="ROW_NAME" />,目前已選取。<ph name="ROW_CONTENT" /></translation>
@@ -301,7 +299,6 @@
<translation id="3024663005179499861">政策類型有誤</translation>
<translation id="3037605927509011580">糟糕!</translation>
<translation id="3041612393474885105">憑證資訊</translation>
-<translation id="3063697135517575841">Chrome 目前無法驗證您的信用卡,請稍後再試。</translation>
<translation id="3064966200440839136">即將離開無痕模式,改為使用外部應用程式付款,要繼續嗎?</translation>
<translation id="3083099961703215236">{COUNT,plural, =0{無}=1{1 組密碼}other{# 組密碼}}</translation>
<translation id="3096100844101284527">新增取件地址</translation>
@@ -341,7 +338,6 @@
<translation id="3305707030755673451">您已在 <ph name="TIME" />使用同步通關密語對資料進行加密,請輸入通關密語開始進行同步。</translation>
<translation id="3320021301628644560">新增帳單地址</translation>
<translation id="3338095232262050444">安全</translation>
-<translation id="3340978935015468852">設定</translation>
<translation id="3345135638360864351">無法將您的網站存取要求傳送給<ph name="NAME" />,請再試一次。</translation>
<translation id="3355823806454867987">變更 Proxy 設定...</translation>
<translation id="3361596688432910856">Chrome <ph name="BEGIN_EMPHASIS" />不會儲存<ph name="END_EMPHASIS" />下列資訊:
@@ -375,7 +371,6 @@
<translation id="3528171143076753409">伺服器憑證授權不可靠。</translation>
<translation id="3530944546672790857">{COUNT,plural, =0{在已同步的裝置上至少有 1 個項目}=1{1 個項目 (在已同步的裝置上還有更多項目)}other{# 個項目 (在已同步的裝置上還有更多項目)}}</translation>
<translation id="3539171420378717834">在這個裝置上保留這張信用卡的複本</translation>
-<translation id="3542684924769048008">選擇密碼:</translation>
<translation id="3549644494707163724">使用你的通關密語對所有已同步處理的資料進行加密</translation>
<translation id="3556433843310711081">你的管理員可以為你解除封鎖這個網站</translation>
<translation id="3566021033012934673">你的連線不是私人連線</translation>
@@ -461,7 +456,6 @@
<translation id="4171400957073367226">驗證簽名無效</translation>
<translation id="4173827307318847180">{MORE_ITEMS,plural, =1{還有另外 <ph name="ITEM_COUNT" /> 個項目}other{還有另外 <ph name="ITEM_COUNT" /> 個項目}}</translation>
<translation id="4179515394835346607"><ph name="ROW_NAME" /> <ph name="ROW_CONTENT" /></translation>
-<translation id="4192549185358213268">如果你在其他網站上重複使用過你的 <ph name="ORG_NAME" /> 密碼,Chrome 建議你重設密碼。</translation>
<translation id="4196861286325780578">重做移動(&amp;R)</translation>
<translation id="4203896806696719780"><ph name="BEGIN_LINK" />檢查防火牆和防毒軟體設定<ph name="END_LINK" /></translation>
<translation id="4220128509585149162">當機</translation>
@@ -490,7 +484,6 @@
<translation id="425582637250725228">系統可能不會儲存您所做的變更。</translation>
<translation id="4258748452823770588">簽名有誤</translation>
<translation id="4265872034478892965">依據管理員的設定允許</translation>
-<translation id="4269787794583293679">(沒有使用者名稱)</translation>
<translation id="4275830172053184480">重新啟動裝置</translation>
<translation id="4277028893293644418">重設密碼</translation>
<translation id="4280429058323657511">,到期日:<ph name="EXPIRATION_DATE_ABBR" /></translation>
@@ -513,6 +506,7 @@
<translation id="4434045419905280838">彈出式視窗與重新導向</translation>
<translation id="443673843213245140">雖然已停用 Proxy,不過已指定明確 Proxy 設定。</translation>
<translation id="445100540951337728">接受的簽帳金融卡</translation>
+<translation id="4472575034687746823">開始使用</translation>
<translation id="4506176782989081258">驗證錯誤:<ph name="VALIDATION_ERROR" /></translation>
<translation id="4506599922270137252">與系統管理員聯絡</translation>
<translation id="450710068430902550">與管理員分享</translation>
@@ -537,8 +531,8 @@
<translation id="4726672564094551039">重新載入政策</translation>
<translation id="4728558894243024398">平台</translation>
<translation id="4736825316280949806">重新啟動 Chromium</translation>
+<translation id="4742407542027196863">管理密碼…</translation>
<translation id="4744603770635761495">可執行檔的路徑</translation>
-<translation id="4749685221585524849">上次使用日期:<ph name="LAST_USED_MONTH" /></translation>
<translation id="4750917950439032686">你傳送給這個網站的資訊 (例如密碼或信用卡號碼) 不會外洩。</translation>
<translation id="4756388243121344051">記錄(&amp;H)</translation>
<translation id="4758311279753947758">新增聯絡資訊</translation>
@@ -555,6 +549,7 @@
<translation id="4850886885716139402">檢視</translation>
<translation id="4854362297993841467">不支援所選的快遞方式,請改選其他方式。</translation>
<translation id="4858792381671956233">你已詢問家長是否同意你造訪這個網站</translation>
+<translation id="4876305945144899064">沒有使用者名稱</translation>
<translation id="4880827082731008257">搜尋記錄</translation>
<translation id="4881695831933465202">開啟</translation>
<translation id="4895877746940133817"><ph name="TYPE_1" />、<ph name="TYPE_2" />,<ph name="TYPE_3" /></translation>
@@ -588,6 +583,7 @@
<translation id="5089810972385038852">州</translation>
<translation id="5094747076828555589">伺服器無法證明其屬於 <ph name="DOMAIN" /> 網域;其安全性憑證未取得 Chromium 的信任。這可能是因為設定錯誤,或有攻擊者攔截你的連線所致。</translation>
<translation id="5095208057601539847">省</translation>
+<translation id="5098332213681597508">這是你在 Google 帳戶中設定的名稱。</translation>
<translation id="5115563688576182185">(64 位元)</translation>
<translation id="5121084798328133320">完成驗證後,這個網站就會取得你 Google 付款帳戶中的信用卡詳細資料。</translation>
<translation id="5128122789703661928">無法將使用這個名稱的工作階段刪除。</translation>
@@ -628,9 +624,10 @@
<translation id="5332219387342487447">運送方式</translation>
<translation id="5355557959165512791">目前無法造訪 <ph name="SITE" />,因為這個網站的憑證已遭撤銷。網路錯誤和攻擊行為通常是暫時性的,因此這個網頁可能稍後就會恢復正常狀態。</translation>
<translation id="536296301121032821">無法儲存政策設定</translation>
+<translation id="5371425731340848620">更新信用卡</translation>
<translation id="5377026284221673050">「你的時鐘時間過慢」、「你的時鐘時間過快」或 「&lt;span class="error-code"&gt;NET::ERR_CERT_DATE_INVALID&lt;/span&gt;」</translation>
<translation id="5386426401304769735">這個網站的憑證鏈結包含使用 SHA-1 進行簽署的憑證。</translation>
-<translation id="5402410679244714488">到期日:<ph name="EXPIRATION_DATE_ABBR" />,距離上次使用已超過 1 年</translation>
+<translation id="5387961145478138773">快速存取你喜愛的 Google 應用程式</translation>
<translation id="540969355065856584">這個伺服器無法證明所屬網域為 <ph name="DOMAIN" />;其安全性憑證目前無效。這可能是因為設定錯誤,或是有攻擊者攔截您的連線所致。</translation>
<translation id="5421136146218899937">清除瀏覽資料...</translation>
<translation id="5430298929874300616">移除書籤</translation>
@@ -672,11 +669,13 @@
<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="5659593005791499971">電子郵件</translation>
+<translation id="5666899935841546222">想要集中管理所有的卡片嗎?</translation>
<translation id="5675650730144413517">這個網頁無法正常運作</translation>
<translation id="5685654322157854305">新增運送地址</translation>
<translation id="5689199277474810259">以 JSON 格式匯出</translation>
<translation id="5689516760719285838">位置</translation>
<translation id="570530837424789914">管理...</translation>
+<translation id="57094364128775171">建議高強度密碼…</translation>
<translation id="5710435578057952990">此網頁的身分未經驗證。</translation>
<translation id="5719499550583120431">接受預付卡。</translation>
<translation id="5720705177508910913">目前使用者</translation>
@@ -697,11 +696,9 @@
<translation id="5869405914158311789">無法連上這個網站</translation>
<translation id="5869522115854928033">已儲存的密碼</translation>
<translation id="5893752035575986141">接受簽帳金融卡。</translation>
-<translation id="5898382028489516745">如果你在其他網站上重複使用過你的 <ph name="ORG_NAME" /> 密碼,Chromium 建議你重設密碼。</translation>
<translation id="5908541034548427511"><ph name="TYPE_1" /> (已同步)</translation>
<translation id="5920262536204764679">{NUM_COOKIES,plural, =1{目前使用 1 個 Cookie}other{目前使用 # 個 Cookie}}</translation>
<translation id="5939518447894949180">重設</translation>
-<translation id="5959728338436674663">自動傳送部分<ph name="BEGIN_WHITEPAPER_LINK" />系統資訊和網頁內容<ph name="END_WHITEPAPER_LINK" />給 Google,協助偵測危險的應用程式和網站。<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="5967592137238574583">編輯聯絡資訊</translation>
<translation id="5967867314010545767">從記錄中移除</translation>
<translation id="5975083100439434680">縮小</translation>
@@ -747,13 +744,11 @@
<translation id="6282194474023008486">郵遞區號</translation>
<translation id="6290238015253830360">這裡會顯示為你推薦的文章</translation>
<translation id="6305205051461490394">無法連上 <ph name="URL" />。</translation>
-<translation id="6319915415804115995">距離上次使用已超過 1 年</translation>
<translation id="6321917430147971392">檢查 DNS 設定</translation>
<translation id="6328639280570009161">建議停用網路預測功能</translation>
<translation id="6328786501058569169">這是詐欺網站</translation>
<translation id="6337133576188860026">釋出不到 <ph name="SIZE" />。下次造訪部分網站時,載入速度可能會變慢。</translation>
<translation id="6337534724793800597">依名稱篩選政策</translation>
-<translation id="6342069812937806050">剛剛</translation>
<translation id="6355080345576803305">公開工作階段覆寫</translation>
<translation id="6358450015545214790">我需要進一步資訊</translation>
<translation id="6386120369904791316">{COUNT,plural, =1{以及另外 1 個建議項目}other{以及另外 # 個建議項目}}</translation>
@@ -778,7 +773,6 @@
<translation id="6529602333819889595">重做刪除(&amp;R)</translation>
<translation id="6534179046333460208">實體化網路建議</translation>
<translation id="6550675742724504774">選項</translation>
-<translation id="6556915248009097796">到期日:<ph name="EXPIRATION_DATE_ABBR" />,上次使用日期:<ph name="LAST_USED_DATE_NO_DETAIL" /></translation>
<translation id="6563469144985748109">你的管理員尚未核准這個網站</translation>
<translation id="6569060085658103619">目前顯示的是擴充功能頁面</translation>
<translation id="6596325263575161958">加密選項</translation>
@@ -807,15 +801,20 @@
<translation id="6825578344716086703">你嘗試連上 <ph name="DOMAIN" />,但伺服器的憑證是以防護力薄弱的簽章演算法 (例如 SHA-1) 進行簽署。這代表伺服器提供的安全性憑證可能遭到偽造,而且這個伺服器可能並不是你的目標伺服器 (你的連線對象可能是攻擊者的電腦)。</translation>
<translation id="6831043979455480757">翻譯</translation>
<translation id="6839929833149231406">區</translation>
+<translation id="6852204201400771460">要重新載入應用程式嗎?</translation>
+<translation id="6865412394715372076">目前無法驗證這張信用卡</translation>
<translation id="6874604403660855544">重做新增(&amp;R)</translation>
<translation id="6886577214605505410"><ph name="LOCATION_TITLE" /> <ph name="SHORT_URL" /></translation>
<translation id="6891596781022320156">系統不支援這項政策的層級。</translation>
<translation id="6895330447102777224">您的信用卡已通過驗證</translation>
<translation id="6897140037006041989">使用者代理程式</translation>
+<translation id="6903319715792422884">將部分<ph name="BEGIN_WHITEPAPER_LINK" />系統資訊和網頁內容<ph name="END_WHITEPAPER_LINK" />傳送給 Google,協助我們改善安全瀏覽功能。<ph name="PRIVACY_PAGE_LINK" /></translation>
<translation id="6915804003454593391">使用者:</translation>
+<translation id="6944692733090228304">你在不是由 <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> 管理的網站上輸入了你的密碼。為確保帳戶安全,請勿在其他應用程式和網站上重複使用你的密碼。</translation>
<translation id="6945221475159498467">選取</translation>
<translation id="6948701128805548767">如要查看取件方式和相關規定,請選取一個地址</translation>
<translation id="6949872517221025916">重設密碼</translation>
+<translation id="6950684638814147129">剖析 JSON 值時發生錯誤:<ph name="ERROR" /></translation>
<translation id="6957887021205513506">伺服器憑證疑似偽造。</translation>
<translation id="6965382102122355670">確定</translation>
<translation id="6965978654500191972">裝置</translation>
@@ -877,6 +876,7 @@
&lt;li&gt;依序按一下 [套用]&lt;strong&gt;&lt;/strong&gt; 和 [確定]&lt;strong&gt;&lt;/strong&gt;
&lt;li&gt;前往 &lt;a href="https://support.google.com/chrome/answer/6098869"&gt;Chrome 說明中心&lt;/a&gt;瞭解如何將該軟體從電腦上永久移除
&lt;/ol&gt;</translation>
+<translation id="7416351320495623771">管理密碼…</translation>
<translation id="7419106976560586862">設定檔路徑</translation>
<translation id="7437289804838430631">新增聯絡資訊</translation>
<translation id="7441627299479586546">政策主體有誤</translation>
@@ -885,7 +885,6 @@
<translation id="7451311239929941790"><ph name="BEGIN_LINK" />進一步瞭解<ph name="END_LINK" />這個問題。</translation>
<translation id="7455133967321480974">使用全域預設值 (封鎖)</translation>
<translation id="7460163899615895653">你最近在其他裝置上開啟的分頁會顯示在這裡</translation>
-<translation id="7469372306589899959">正在驗證信用卡</translation>
<translation id="7473891865547856676">不用了,謝謝</translation>
<translation id="7481312909269577407">往前</translation>
<translation id="7485870689360869515">找不到任何資料。</translation>
@@ -1015,6 +1014,7 @@
<translation id="8308427013383895095">網路連線發生問題,翻譯作業失敗。</translation>
<translation id="8311129316111205805">載入工作階段</translation>
<translation id="8332188693563227489">存取 <ph name="HOST_NAME" /> 的要求遭到拒絕</translation>
+<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="8349305172487531364">書籤列</translation>
<translation id="8363502534493474904">關閉飛航模式</translation>
@@ -1035,20 +1035,24 @@
<translation id="8498891568109133222"><ph name="HOST_NAME" /> 的回應時間過長。</translation>
<translation id="8503559462189395349">Chrome 密碼</translation>
<translation id="8503813439785031346">使用者名稱</translation>
+<translation id="8508648098325802031">搜尋圖示</translation>
<translation id="8543181531796978784">您可以<ph name="BEGIN_ERROR_LINK" />回報偵測問題<ph name="END_ERROR_LINK" />。或者在您瞭解安全性風險後,仍然可以<ph name="BEGIN_LINK" />前往這個不安全的網站<ph name="END_LINK" />。</translation>
<translation id="8543556556237226809">如有問題,請與你個人資料的監管者聯絡。</translation>
<translation id="8553075262323480129">無法判定網頁的語言,翻譯作業失敗。</translation>
<translation id="8557066899867184262">信用卡安全碼位於信用卡背面。</translation>
<translation id="8559762987265718583">你裝置的日期和時間 (<ph name="DATE_AND_TIME" />) 不正確,因此無法與 <ph name="BEGIN_BOLD" /><ph name="DOMAIN" /><ph name="END_BOLD" /> 建立私人連線。</translation>
+<translation id="8564985650692024650">如果你在其他網站上重複使用過你的 <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> 密碼,Chromium 會建議你重設密碼。</translation>
<translation id="8571890674111243710">正在將網頁翻譯成<ph name="LANGUAGE" />...</translation>
<translation id="858637041960032120">新增電話號碼</translation>
<translation id="859285277496340001">憑證未指定負責檢查其本身是否已遭到撤銷的機制。</translation>
+<translation id="860043288473659153">持卡人姓名</translation>
<translation id="8620436878122366504">你的家長尚未核准這個網站</translation>
<translation id="8625384913736129811">將這張信用卡儲存到這個裝置</translation>
-<translation id="8639963783467694461">自動填入設定</translation>
+<translation id="8663226718884576429">訂單摘要:<ph name="TOTAL_LABEL" />,更多詳細資料</translation>
<translation id="8680536109547170164"><ph name="QUERY" />,答案:<ph name="ANSWER" /></translation>
<translation id="8703575177326907206">你到 <ph name="DOMAIN" /> 的連線未加密。</translation>
<translation id="8718314106902482036">未完成付款程序</translation>
+<translation id="8719263113926255150"><ph name="ENTITY" />,<ph name="DESCRIPTION" />,搜尋建議</translation>
<translation id="8725066075913043281">再試一次</translation>
<translation id="8728672262656704056">你已進入無痕模式</translation>
<translation id="8730621377337864115">完成</translation>
@@ -1064,8 +1068,10 @@
<translation id="8820817407110198400">書籤</translation>
<translation id="883848425547221593">其他書籤</translation>
<translation id="884264119367021077">運送地址</translation>
+<translation id="8846319957959474018">使用書籤,輕鬆開啟應用程式</translation>
<translation id="884923133447025588">未發現撤銷機制。</translation>
<translation id="885730110891505394">與 Google 分享</translation>
+<translation id="8858065207712248076">如果你在其他網站上重複使用過你的 <ph name="BEGIN_BOLD" /><ph name="ORG_NAME" /><ph name="END_BOLD" /> 密碼,Chrome 會建議你重設密碼。</translation>
<translation id="8866481888320382733">解析政策設定時發生錯誤</translation>
<translation id="8870413625673593573">最近關閉的分頁</translation>
<translation id="8874824191258364635">請輸入有效的信用卡號碼</translation>
@@ -1104,6 +1110,7 @@
<translation id="9103872766612412690"><ph name="SITE" /> 通常使用加密方式保護您的資訊。但 Chromium 這次嘗試連線到 <ph name="SITE" /> 時,該網站傳回了異常且錯誤的憑證。這可能是因為有攻擊者企圖偽裝成 <ph name="SITE" />,或是受到 Wi-Fi 登入畫面影響而造成連線中斷。不過請放心,Chromium 已及時停止連線,並未傳輸任何資料,因此您的資訊仍然安全無虞。</translation>
<translation id="9106062320799175032">新增帳單地址</translation>
<translation id="910908805481542201">請協助我解決這個問題</translation>
+<translation id="9114524666733003316">正在驗證信用卡...</translation>
<translation id="9128870381267983090">連線至網路</translation>
<translation id="9137013805542155359">顯示原文</translation>
<translation id="9137248913990643158">使用這個應用程式前,請先啟動 Chrome 並登入帳戶。</translation>
@@ -1114,6 +1121,7 @@
<translation id="9168814207360376865">允許網站檢查付款方式是否已成功儲存</translation>
<translation id="9169664750068251925">永遠禁止在這個網站執行</translation>
<translation id="9170848237812810038">取消(&amp;U)</translation>
+<translation id="9171296965991013597">要離開應用程式嗎?</translation>
<translation id="917450738466192189">伺服器憑證無效。</translation>
<translation id="9183425211371246419"><ph name="HOST_NAME" /> 使用了不支援的通訊協定。</translation>
<translation id="9205078245616868884">您已使用同步通關密語對資料進行加密,請輸入通關密語開始進行同步。</translation>
diff --git a/chromium/components/subresource_filter/README.md b/chromium/components/subresource_filter/README.md
index 7dcb7c707d6..aa18cbd9ffe 100644
--- a/chromium/components/subresource_filter/README.md
+++ b/chromium/components/subresource_filter/README.md
@@ -1,4 +1,4 @@
-#Subresource Filter
+# Subresource Filter
The subresource_filter component deals with code that tags and filters
subresource requests based on:
diff --git a/chromium/components/subresource_filter/content/browser/BUILD.gn b/chromium/components/subresource_filter/content/browser/BUILD.gn
index ae37f91fdda..74150920c01 100644
--- a/chromium/components/subresource_filter/content/browser/BUILD.gn
+++ b/chromium/components/subresource_filter/content/browser/BUILD.gn
@@ -12,8 +12,6 @@ static_library("browser") {
"content_activation_list_utils.h",
"content_ruleset_service.cc",
"content_ruleset_service.h",
- "content_subresource_filter_driver_factory.cc",
- "content_subresource_filter_driver_factory.h",
"content_subresource_filter_throttle_manager.cc",
"content_subresource_filter_throttle_manager.h",
"navigation_console_logger.cc",
diff --git a/chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc b/chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc
index 0a0142b7a7f..46063d9fb5a 100644
--- a/chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc
+++ b/chromium/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc
@@ -14,7 +14,7 @@
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/sequenced_task_runner.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
diff --git a/chromium/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc b/chromium/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
index 58642b735bd..2ef52acef1e 100644
--- a/chromium/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
+++ b/chromium/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
@@ -319,7 +319,7 @@ class SubresourceFilterComputeActivationStateTest : public ::testing::Test {
testing::TestRulesetPair test_ruleset_pair;
ASSERT_NO_FATAL_FAILURE(test_ruleset_creator_.CreateRulesetWithRules(
rules, &test_ruleset_pair));
- ruleset_ = new MemoryMappedRuleset(
+ ruleset_ = MemoryMappedRuleset::CreateAndInitialize(
testing::TestRuleset::Open(test_ruleset_pair.indexed));
}
diff --git a/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc b/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc
deleted file mode 100644
index 4122bd637f4..00000000000
--- a/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc
+++ /dev/null
@@ -1,145 +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/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
-
-#include <utility>
-
-#include "base/metrics/histogram_macros.h"
-#include "base/time/time.h"
-#include "components/subresource_filter/content/browser/navigation_console_logger.h"
-#include "components/subresource_filter/content/browser/page_load_statistics.h"
-#include "components/subresource_filter/content/browser/subresource_filter_client.h"
-#include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
-#include "components/subresource_filter/core/browser/subresource_filter_constants.h"
-#include "components/subresource_filter/core/common/activation_list.h"
-#include "components/subresource_filter/core/common/activation_state.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/page_navigator.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/console_message_level.h"
-#include "net/base/net_errors.h"
-#include "url/gurl.h"
-
-DEFINE_WEB_CONTENTS_USER_DATA_KEY(
- subresource_filter::ContentSubresourceFilterDriverFactory);
-
-namespace subresource_filter {
-
-namespace {
-
-// Returns true with a probability given by |performance_measurement_rate| if
-// ThreadTicks is supported, otherwise returns false.
-} // namespace
-
-// static
-void ContentSubresourceFilterDriverFactory::CreateForWebContents(
- content::WebContents* web_contents,
- SubresourceFilterClient* client) {
- if (FromWebContents(web_contents))
- return;
- web_contents->SetUserData(
- UserDataKey(), std::make_unique<ContentSubresourceFilterDriverFactory>(
- web_contents, client));
-}
-
-// static
-ContentSubresourceFilterDriverFactory::ContentSubresourceFilterDriverFactory(
- content::WebContents* web_contents,
- SubresourceFilterClient* client)
- : content::WebContentsObserver(web_contents),
- client_(client),
- throttle_manager_(
- std::make_unique<ContentSubresourceFilterThrottleManager>(
- this,
- client_->GetRulesetDealer(),
- web_contents)) {}
-
-ContentSubresourceFilterDriverFactory::
- ~ContentSubresourceFilterDriverFactory() {}
-
-void ContentSubresourceFilterDriverFactory::NotifyPageActivationComputed(
- content::NavigationHandle* navigation_handle,
- ActivationDecision activation_decision,
- const Configuration& matched_configuration,
- bool warning) {
- DCHECK(navigation_handle->IsInMainFrame());
- DCHECK(!navigation_handle->IsSameDocument());
- if (navigation_handle->GetNetErrorCode() != net::OK)
- return;
-
- activation_decision_ = activation_decision;
- matched_configuration_ = matched_configuration;
- DCHECK_NE(activation_decision_, ActivationDecision::UNKNOWN);
-
- ActivationLevel effective_activation_level =
- matched_configuration_.activation_options.activation_level;
-
- // ACTIVATION_DISABLED implies DISABLED activation level.
- DCHECK(activation_decision_ != ActivationDecision::ACTIVATION_DISABLED ||
- effective_activation_level == ActivationLevel::DISABLED);
-
- // Ensure the matched config is in our config list. If it wasn't then this
- // must be a forced activation via devtools.
- bool forced_activation_via_devtools =
- (matched_configuration == Configuration::MakeForForcedActivation());
- DCHECK(activation_decision_ != ActivationDecision::ACTIVATED ||
- HasEnabledConfiguration(matched_configuration) ||
- forced_activation_via_devtools)
- << matched_configuration;
-
- if (warning && effective_activation_level == ActivationLevel::ENABLED) {
- NavigationConsoleLogger::LogMessageOnCommit(
- navigation_handle, content::CONSOLE_MESSAGE_LEVEL_WARNING,
- kActivationWarningConsoleMessage);
-
- // Do not disallow enforcement if activated via devtools.
- if (!forced_activation_via_devtools) {
- activation_decision_ = ActivationDecision::ACTIVATION_DISABLED;
- effective_activation_level = ActivationLevel::DISABLED;
- }
- }
-
- matched_configuration_.activation_options.activation_level =
- effective_activation_level;
- SubresourceFilterObserverManager::FromWebContents(web_contents())
- ->NotifyPageActivationComputed(navigation_handle, activation_decision_,
- matched_configuration_.GetActivationState(
- effective_activation_level));
-}
-
-void ContentSubresourceFilterDriverFactory::OnFirstSubresourceLoadDisallowed() {
- if (matched_configuration_.activation_conditions.forced_activation) {
- UMA_HISTOGRAM_BOOLEAN(
- "SubresourceFilter.PageLoad.ForcedActivation.DisallowedLoad", true);
- return;
- }
- client_->ShowNotification();
-}
-
-void ContentSubresourceFilterDriverFactory::DidStartNavigation(
- content::NavigationHandle* navigation_handle) {
- if (navigation_handle->IsInMainFrame() &&
- !navigation_handle->IsSameDocument()) {
- activation_decision_ = ActivationDecision::UNKNOWN;
- client_->OnNewNavigationStarted();
- }
-}
-
-void ContentSubresourceFilterDriverFactory::DidFinishNavigation(
- content::NavigationHandle* navigation_handle) {
- if (!navigation_handle->IsInMainFrame() ||
- navigation_handle->IsSameDocument() ||
- !navigation_handle->HasCommitted()) {
- return;
- }
-
- if (activation_decision_ == ActivationDecision::UNKNOWN) {
- activation_decision_ = ActivationDecision::ACTIVATION_DISABLED;
- matched_configuration_ = Configuration();
- }
-}
-
-} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h b/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h
deleted file mode 100644
index 0659e573e1d..00000000000
--- a/chromium/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h
+++ /dev/null
@@ -1,109 +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_SUBRESOURCE_FILTER_CONTENT_BROWSER_CONTENT_SUBRESOURCE_FILTER_DRIVER_FACTORY_H_
-#define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_CONTENT_SUBRESOURCE_FILTER_DRIVER_FACTORY_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
-#include "components/subresource_filter/core/browser/subresource_filter_features.h"
-#include "components/subresource_filter/core/common/activation_decision.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
-
-namespace content {
-class WebContents;
-} // namespace content
-
-namespace safe_browsing {
-class SafeBrowsingServiceTest;
-};
-
-namespace subresource_filter {
-
-class SubresourceFilterClient;
-enum class ActivationLevel;
-enum class ActivationList;
-
-// Controls the activation of subresource filtering for each page load in a
-// WebContents and is responsible for sending the activation signal to all the
-// per-frame SubresourceFilterAgents on the renderer side.
-class ContentSubresourceFilterDriverFactory
- : public content::WebContentsUserData<
- ContentSubresourceFilterDriverFactory>,
- public content::WebContentsObserver,
- public ContentSubresourceFilterThrottleManager::Delegate {
- public:
- static void CreateForWebContents(content::WebContents* web_contents,
- SubresourceFilterClient* client);
-
- explicit ContentSubresourceFilterDriverFactory(
- content::WebContents* web_contents,
- SubresourceFilterClient* client);
- ~ContentSubresourceFilterDriverFactory() override;
-
- // This class will be notified of page level activation, before the associated
- // navigation commits.
- void NotifyPageActivationComputed(
- content::NavigationHandle* navigation_handle,
- ActivationDecision activation_decision,
- const Configuration& matched_configuration,
- bool warning);
-
- // ContentSubresourceFilterThrottleManager::Delegate:
- void OnFirstSubresourceLoadDisallowed() override;
-
- ContentSubresourceFilterThrottleManager* throttle_manager() {
- return throttle_manager_.get();
- }
-
- SubresourceFilterClient* client() { return client_; }
-
- private:
- friend class ContentSubresourceFilterDriverFactoryTest;
- friend class safe_browsing::SafeBrowsingServiceTest;
-
- // content::WebContentsObserver:
- void DidStartNavigation(
- content::NavigationHandle* navigation_handle) override;
- void DidFinishNavigation(
- content::NavigationHandle* navigation_handle) override;
-
- // Must outlive this class.
- SubresourceFilterClient* client_;
-
- std::unique_ptr<ContentSubresourceFilterThrottleManager> throttle_manager_;
-
- // The activation decision corresponding to the most recently _started_
- // non-same-document navigation in the main frame.
- //
- // The value is reset to ActivationDecision::UNKNOWN at the start of each such
- // navigation, and will not be assigned until the navigation successfully
- // reaches the WillProcessResponse stage (or successfully finishes if
- // throttles are not invoked). This means that after a cancelled or otherwise
- // unsuccessful navigation, the value will be left at UNKNOWN indefinitely.
- ActivationDecision activation_decision_ =
- ActivationDecision::ACTIVATION_DISABLED;
-
- // The Configuration corresponding to the most recently _committed_
- // non-same-document navigation in the main frame.
- //
- // The value corresponding to the previous such navigation will be retained,
- // and the new value not assigned until a subsequent navigation successfully
- // reaches the WillProcessResponse stage (or successfully finishes if
- // throttles are not invoked).
- //
- // Careful note: the Configuration may not entirely match up with
- // a config in GetEnabledConfigurations() due to activation computation
- // changing the config (e.g. for forcing devtools activation).
- Configuration matched_configuration_;
-
- DISALLOW_COPY_AND_ASSIGN(ContentSubresourceFilterDriverFactory);
-};
-
-} // namespace subresource_filter
-
-#endif // COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_CONTENT_SUBRESOURCE_FILTER_DRIVER_FACTORY_H_
diff --git a/chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc b/chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
index e6467a0e399..64c81c51f67 100644
--- a/chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
+++ b/chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
@@ -17,6 +17,7 @@
#include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
#include "components/subresource_filter/content/browser/navigation_console_logger.h"
#include "components/subresource_filter/content/browser/page_load_statistics.h"
+#include "components/subresource_filter/content/browser/subresource_filter_client.h"
#include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
#include "components/subresource_filter/content/common/subresource_filter_messages.h"
#include "components/subresource_filter/content/common/subresource_filter_utils.h"
@@ -33,13 +34,13 @@ namespace subresource_filter {
ContentSubresourceFilterThrottleManager::
ContentSubresourceFilterThrottleManager(
- Delegate* delegate,
+ SubresourceFilterClient* client,
VerifiedRulesetDealer::Handle* dealer_handle,
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
scoped_observer_(this),
dealer_handle_(dealer_handle),
- delegate_(delegate),
+ client_(client),
weak_ptr_factory_(this) {
SubresourceFilterObserverManager::CreateForWebContents(web_contents);
scoped_observer_.Add(
@@ -222,7 +223,6 @@ bool ContentSubresourceFilterThrottleManager::OnMessageReceived(
// activation for that page load.
void ContentSubresourceFilterThrottleManager::OnPageActivationComputed(
content::NavigationHandle* navigation_handle,
- ActivationDecision activation_decision,
const ActivationState& activation_state) {
DCHECK(navigation_handle->IsInMainFrame());
DCHECK(!navigation_handle->HasCommitted());
@@ -242,20 +242,16 @@ void ContentSubresourceFilterThrottleManager::OnSubframeNavigationEvaluated(
LoadPolicy load_policy,
bool is_ad_subframe) {
DCHECK(!navigation_handle->IsInMainFrame());
-
- auto it = ongoing_activation_throttles_.find(navigation_handle);
- if (it == ongoing_activation_throttles_.end())
+ if (!is_ad_subframe)
return;
- if (is_ad_subframe) {
- // TODO(crbug.com/843646): Use an API that NavigationHandle supports rather
- // than trying to infer what the NavigationHandle is doing.
- content::RenderFrameHost* starting_rfh =
- navigation_handle->GetWebContents()->UnsafeFindFrameByFrameTreeNodeId(
- navigation_handle->GetFrameTreeNodeId());
- DCHECK(starting_rfh);
- ad_frames_.insert(starting_rfh);
- }
+ // TODO(crbug.com/843646): Use an API that NavigationHandle supports rather
+ // than trying to infer what the NavigationHandle is doing.
+ content::RenderFrameHost* starting_rfh =
+ navigation_handle->GetWebContents()->UnsafeFindFrameByFrameTreeNodeId(
+ navigation_handle->GetFrameTreeNodeId());
+ DCHECK(starting_rfh);
+ ad_frames_.insert(starting_rfh);
}
void ContentSubresourceFilterThrottleManager::MaybeAppendNavigationThrottles(
@@ -374,7 +370,7 @@ void ContentSubresourceFilterThrottleManager::MaybeCallFirstDisallowedLoad() {
ActivationLevel::ENABLED) {
return;
}
- delegate_->OnFirstSubresourceLoadDisallowed();
+ client_->ShowNotification();
current_committed_load_has_notified_disallowed_load_ = true;
}
diff --git a/chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h b/chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h
index e8500b2b401..2499288463d 100644
--- a/chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h
+++ b/chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h
@@ -35,8 +35,9 @@ namespace subresource_filter {
class AsyncDocumentSubresourceFilter;
class ActivationStateComputingNavigationThrottle;
-class SubresourceFilterObserverManager;
class PageLoadStatistics;
+class SubresourceFilterObserverManager;
+class SubresourceFilterClient;
struct DocumentLoadStatistics;
// The ContentSubresourceFilterThrottleManager manages NavigationThrottles in
@@ -53,17 +54,8 @@ class ContentSubresourceFilterThrottleManager
public SubresourceFilterObserver,
public SubframeNavigationFilteringThrottle::Delegate {
public:
- // It is expected that the Delegate outlives |this|, and manages the lifetime
- // of this class.
- class Delegate {
- public:
- // The embedder may be interested in displaying UI to the user when the
- // first load is disallowed for a given page load.
- virtual void OnFirstSubresourceLoadDisallowed() {}
- };
-
ContentSubresourceFilterThrottleManager(
- Delegate* delegate,
+ SubresourceFilterClient* client,
VerifiedRulesetDealer::Handle* dealer_handle,
content::WebContents* web_contents);
~ContentSubresourceFilterThrottleManager() override;
@@ -109,7 +101,6 @@ class ContentSubresourceFilterThrottleManager
void OnSubresourceFilterGoingAway() override;
void OnPageActivationComputed(
content::NavigationHandle* navigation_handle,
- ActivationDecision activation_decision,
const ActivationState& activation_state) override;
void OnSubframeNavigationEvaluated(
content::NavigationHandle* navigation_handle,
@@ -197,7 +188,7 @@ class ContentSubresourceFilterThrottleManager
// These members outlive this class.
VerifiedRulesetDealer::Handle* dealer_handle_;
- Delegate* delegate_;
+ SubresourceFilterClient* client_;
base::WeakPtrFactory<ContentSubresourceFilterThrottleManager>
weak_ptr_factory_;
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 710d1ed79ec..214d84d5735 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
@@ -14,10 +14,11 @@
#include "base/message_loop/message_loop_current.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_simple_task_runner.h"
#include "base/time/time.h"
#include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
+#include "components/subresource_filter/content/browser/subresource_filter_client.h"
#include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
#include "components/subresource_filter/content/common/subresource_filter_messages.h"
#include "components/subresource_filter/core/common/activation_level.h"
@@ -97,8 +98,7 @@ class MockPageStateActivationThrottle : public content::NavigationThrottle {
// The throttle manager does not use the activation decision.
SubresourceFilterObserverManager::FromWebContents(
navigation_handle()->GetWebContents())
- ->NotifyPageActivationComputed(
- navigation_handle(), ActivationDecision::UNKNOWN, it->second);
+ ->NotifyPageActivationComputed(navigation_handle(), it->second);
}
}
return content::NavigationThrottle::PROCEED;
@@ -113,7 +113,7 @@ class MockPageStateActivationThrottle : public content::NavigationThrottle {
class ContentSubresourceFilterThrottleManagerTest
: public content::RenderViewHostTestHarness,
public content::WebContentsObserver,
- public ContentSubresourceFilterThrottleManager::Delegate,
+ public SubresourceFilterClient,
public ::testing::WithParamInterface<PageActivationNotificationTiming> {
public:
ContentSubresourceFilterThrottleManagerTest() {}
@@ -279,9 +279,13 @@ class ContentSubresourceFilterThrottleManagerTest
}
}
- // ContentSubresourceFilterThrottleManager::Delegate:
- void OnFirstSubresourceLoadDisallowed() override {
- ++disallowed_notification_count_;
+ // SubresourceFilterClient:
+ void ShowNotification() override { ++disallowed_notification_count_; }
+ ActivationLevel OnPageActivationComputed(
+ content::NavigationHandle* navigation_handle,
+ ActivationLevel effective_activation_level,
+ ActivationDecision* decision) override {
+ return effective_activation_level;
}
ContentSubresourceFilterThrottleManager* 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 9d1290b9cef..4cd16175977 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
@@ -47,8 +47,6 @@ FakeSafeBrowsingDatabaseManager::~FakeSafeBrowsingDatabaseManager() {}
bool FakeSafeBrowsingDatabaseManager::CheckUrlForSubresourceFilter(
const GURL& url,
Client* client) {
- DCHECK(CanCheckSubresourceFilter());
-
if (synchronous_failure_ && !url_to_threat_type_.count(url))
return true;
@@ -108,10 +106,6 @@ bool FakeSafeBrowsingDatabaseManager::CanCheckResourceType(
return true;
}
-bool FakeSafeBrowsingDatabaseManager::CanCheckSubresourceFilter() const {
- return true;
-}
-
safe_browsing::ThreatSource FakeSafeBrowsingDatabaseManager::GetThreatSource()
const {
return safe_browsing::ThreatSource::LOCAL_PVER4;
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 764fbdf3305..757f6dc3056 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
@@ -49,7 +49,6 @@ class FakeSafeBrowsingDatabaseManager
bool ChecksAreAlwaysAsync() const override;
bool CanCheckResourceType(
content::ResourceType /* resource_type */) const override;
- bool CanCheckSubresourceFilter() const override;
safe_browsing::ThreatSource GetThreatSource() const override;
bool CheckExtensionIDs(const std::set<std::string>& extension_ids,
Client* client) override;
diff --git a/chromium/components/subresource_filter/content/browser/navigation_console_logger_unittest.cc b/chromium/components/subresource_filter/content/browser/navigation_console_logger_unittest.cc
index 7e3214e8308..53a65d967ba 100644
--- a/chromium/components/subresource_filter/content/browser/navigation_console_logger_unittest.cc
+++ b/chromium/components/subresource_filter/content/browser/navigation_console_logger_unittest.cc
@@ -135,4 +135,23 @@ TEST_F(NavigationConsoleLoggerTest, MultipleMessages) {
EXPECT_EQ(2u, GetConsoleMessages(main_rfh()).size());
}
+TEST_F(NavigationConsoleLoggerTest, SyncNavigationDuringNavigation) {
+ NavigateAndCommit(GURL("http://example.test/"));
+
+ auto navigation = content::NavigationSimulator::CreateRendererInitiated(
+ GURL("http://example.test/path"), main_rfh());
+ navigation->Start();
+ NavigationConsoleLogger::LogMessageOnCommit(
+ navigation->GetNavigationHandle(), content::CONSOLE_MESSAGE_LEVEL_WARNING,
+ "foo");
+
+ content::NavigationSimulator::CreateRendererInitiated(
+ GURL("http://example.test/#hash"), main_rfh())
+ ->CommitSameDocument();
+ EXPECT_EQ(0u, GetConsoleMessages(main_rfh()).size());
+
+ navigation->Commit();
+ EXPECT_EQ(1u, GetConsoleMessages(main_rfh()).size());
+}
+
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc b/chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc
index f96ccfa942c..7aca83863f9 100644
--- a/chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc
+++ b/chromium/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle_unittest.cc
@@ -5,15 +5,18 @@
#include "components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h"
#include <memory>
+#include <sstream>
+#include <string>
#include "base/callback.h"
#include "base/message_loop/message_loop_current.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
#include "components/subresource_filter/content/browser/async_document_subresource_filter_test_utils.h"
#include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
+#include "components/subresource_filter/core/browser/subresource_filter_constants.h"
#include "components/subresource_filter/core/common/activation_level.h"
#include "components/subresource_filter/core/common/activation_state.h"
#include "components/subresource_filter/core/common/test_ruleset_creator.h"
@@ -22,6 +25,8 @@
#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"
+#include "url/origin.h"
namespace subresource_filter {
@@ -71,13 +76,16 @@ class SubframeNavigationFilteringThrottleTest
// The |parent_filter_| is the parent frame's filter. Do not register a
// throttle if the parent is not activated with a valid filter.
if (parent_filter_) {
- navigation_handle->RegisterThrottleForTesting(
- std::make_unique<SubframeNavigationFilteringThrottle>(
- navigation_handle, parent_filter_.get(), &mock_delegate_));
+ auto throttle = std::make_unique<SubframeNavigationFilteringThrottle>(
+ navigation_handle, parent_filter_.get(), &mock_delegate_);
+ ASSERT_NE(nullptr, throttle->GetNameForLogging());
+ navigation_handle->RegisterThrottleForTesting(std::move(throttle));
}
}
- void InitializeDocumentSubresourceFilter(const GURL& document_url) {
+ void InitializeDocumentSubresourceFilter(
+ const GURL& document_url,
+ ActivationLevel parent_level = ActivationLevel::ENABLED) {
ASSERT_NO_FATAL_FAILURE(
test_ruleset_creator_.CreateRulesetToDisallowURLsWithPathSuffix(
"disallowed.html", &test_ruleset_pair_));
@@ -93,15 +101,16 @@ class SubframeNavigationFilteringThrottleTest
std::make_unique<VerifiedRuleset::Handle>(dealer_handle_.get());
testing::TestActivationStateCallbackReceiver activation_state;
+ ActivationState parent_activation_state(parent_level);
+ parent_activation_state.enable_logging = true;
parent_filter_ = std::make_unique<AsyncDocumentSubresourceFilter>(
ruleset_handle_.get(),
AsyncDocumentSubresourceFilter::InitializationParams(
- document_url, ActivationLevel::ENABLED,
- false /* measure_performance */),
+ document_url, url::Origin::Create(document_url),
+ parent_activation_state),
activation_state.GetCallback());
RunUntilIdle();
- activation_state.ExpectReceivedOnce(
- ActivationState(ActivationLevel::ENABLED));
+ activation_state.ExpectReceivedOnce(parent_activation_state);
}
void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
@@ -142,6 +151,18 @@ class SubframeNavigationFilteringThrottleTest
navigation_simulator_->CommitErrorPage();
}
+ const std::vector<std::string>& GetConsoleMessages() {
+ return content::RenderFrameHostTester::For(main_rfh())
+ ->GetConsoleMessages();
+ }
+
+ std::string GetFilterConsoleMessage(const GURL& filtered_url) {
+ std::ostringstream oss(kDisallowSubframeConsoleMessagePrefix);
+ oss << filtered_url;
+ oss << kDisallowSubframeConsoleMessageSuffix;
+ return oss.str();
+ }
+
private:
testing::TestRulesetCreator test_ruleset_creator_;
testing::TestRulesetPair test_ruleset_pair_;
@@ -159,10 +180,12 @@ class SubframeNavigationFilteringThrottleTest
TEST_F(SubframeNavigationFilteringThrottleTest, FilterOnStart) {
InitializeDocumentSubresourceFilter(GURL("https://example.test"));
- CreateTestSubframeAndInitNavigation(
- GURL("https://example.test/disallowed.html"), main_rfh());
+ const GURL url("https://example.test/disallowed.html");
+ CreateTestSubframeAndInitNavigation(url, main_rfh());
SimulateStartAndExpectResult(
content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+ EXPECT_TRUE(
+ base::ContainsValue(GetConsoleMessages(), GetFilterConsoleMessage(url)));
}
TEST_F(SubframeNavigationFilteringThrottleTest, FilterOnRedirect) {
@@ -177,6 +200,28 @@ TEST_F(SubframeNavigationFilteringThrottleTest, FilterOnRedirect) {
expected_result);
}
+TEST_F(SubframeNavigationFilteringThrottleTest, DryRunOnStart) {
+ InitializeDocumentSubresourceFilter(GURL("https://example.test"),
+ ActivationLevel::DRYRUN);
+ const GURL url("https://example.test/disallowed.html");
+ CreateTestSubframeAndInitNavigation(url, main_rfh());
+
+ SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+ EXPECT_FALSE(
+ base::ContainsValue(GetConsoleMessages(), GetFilterConsoleMessage(url)));
+}
+
+TEST_F(SubframeNavigationFilteringThrottleTest, DryRunOnRedirect) {
+ InitializeDocumentSubresourceFilter(GURL("https://example.test"),
+ ActivationLevel::DRYRUN);
+ CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
+ main_rfh());
+
+ SimulateStartAndExpectResult(content::NavigationThrottle::PROCEED);
+ SimulateRedirectAndExpectResult(GURL("https://example.test/disallowed.html"),
+ content::NavigationThrottle::PROCEED);
+}
+
TEST_F(SubframeNavigationFilteringThrottleTest, FilterOnSecondRedirect) {
InitializeDocumentSubresourceFilter(GURL("https://example.test"));
CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
diff --git a/chromium/components/subresource_filter/content/browser/subresource_filter_client.h b/chromium/components/subresource_filter/content/browser/subresource_filter_client.h
index bfd241cdf9b..8424e0a47ec 100644
--- a/chromium/components/subresource_filter/content/browser/subresource_filter_client.h
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_client.h
@@ -6,6 +6,8 @@
#define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBRESOURCE_FILTER_CLIENT_H_
#include "components/subresource_filter/content/browser/verified_ruleset_dealer.h"
+#include "components/subresource_filter/core/common/activation_decision.h"
+#include "components/subresource_filter/core/common/activation_level.h"
#include "content/public/browser/web_contents.h"
namespace content {
@@ -22,24 +24,19 @@ class SubresourceFilterClient {
// blocked.
virtual void ShowNotification() = 0;
- // Called when the component is starting to observe a new navigation.
- virtual void OnNewNavigationStarted() = 0;
-
// Called when the activation decision is otherwise completely computed by the
// subresource filter. At this point, the embedder still has a chance to
- // return false to suppress the activation. Returns whether the activation
- // should be whitelisted for this navigation.
+ // alter the effective activation. Returns the effective activation for this
+ // navigation.
+ //
+ // Note: |decision| is guaranteed to be non-nullptr, and can be modified by
+ // the embedder if any decision changes.
//
// Precondition: The navigation must be a main frame navigation.
- virtual bool OnPageActivationComputed(
+ virtual ActivationLevel OnPageActivationComputed(
content::NavigationHandle* navigation_handle,
- bool activated) = 0;
-
- virtual VerifiedRulesetDealer::Handle* GetRulesetDealer() = 0;
-
- // Returns whether this navigation should be forced to be activated. This is
- // currently only used for devtools.
- virtual bool ForceActivationInCurrentWebContents() = 0;
+ ActivationLevel initial_activation_level,
+ subresource_filter::ActivationDecision* decision) = 0;
};
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/content/browser/subresource_filter_observer.h b/chromium/components/subresource_filter/content/browser/subresource_filter_observer.h
index fcf0d46a1e9..f04ffda0de2 100644
--- a/chromium/components/subresource_filter/content/browser/subresource_filter_observer.h
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_observer.h
@@ -6,6 +6,7 @@
#define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBRESOURCE_FILTER_OBSERVER_H_
#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h"
#include "components/subresource_filter/core/common/activation_decision.h"
#include "components/subresource_filter/core/common/load_policy.h"
@@ -13,10 +14,6 @@ namespace content {
class NavigationHandle;
} // namespace content
-namespace safe_browsing {
-struct ThreatMetadata;
-} // namespace safe_browsing
-
namespace subresource_filter {
struct ActivationState;
@@ -31,20 +28,22 @@ class SubresourceFilterObserver {
// themselves by this point.
virtual void OnSubresourceFilterGoingAway() {}
- // Called when the SubresourceFilter Safe Browsing check is available for this
- // main frame navigation. Will be called at WillProcessResponse time at the
- // latest. Right now it will only include phishing and subresource filter
+ // The results from a set of safe browsing checks, stored as a vector.
+ using SafeBrowsingCheckResults =
+ std::vector<SubresourceFilterSafeBrowsingClient::CheckResult>;
+
+ // Called when the SubresourceFilter Safe Browsing checks are available for
+ // this main frame navigation. Will be called at WillProcessResponse time at
+ // the latest. Right now it will only include phishing and subresource filter
// threat types.
- virtual void OnSafeBrowsingCheckComplete(
+ virtual void OnSafeBrowsingChecksComplete(
content::NavigationHandle* navigation_handle,
- safe_browsing::SBThreatType threat_type,
- const safe_browsing::ThreatMetadata& threat_metadata) {}
+ const SafeBrowsingCheckResults& results) {}
// Called at most once per navigation when page activation is computed. This
// will be called before ReadyToCommitNavigation.
virtual void OnPageActivationComputed(
content::NavigationHandle* navigation_handle,
- ActivationDecision activation_decision,
const ActivationState& activation_state) {}
// Called before navigation commit, either at the WillStartRequest stage or
diff --git a/chromium/components/subresource_filter/content/browser/subresource_filter_observer_manager.cc b/chromium/components/subresource_filter/content/browser/subresource_filter_observer_manager.cc
index 649f4c915e5..6b1941e0061 100644
--- a/chromium/components/subresource_filter/content/browser/subresource_filter_observer_manager.cc
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_observer_manager.cc
@@ -29,23 +29,19 @@ void SubresourceFilterObserverManager::RemoveObserver(
observers_.RemoveObserver(observer);
}
-void SubresourceFilterObserverManager::NotifySafeBrowsingCheckComplete(
+void SubresourceFilterObserverManager::NotifySafeBrowsingChecksComplete(
content::NavigationHandle* navigation_handle,
- safe_browsing::SBThreatType threat_type,
- const safe_browsing::ThreatMetadata& threat_metadata) {
+ const SubresourceFilterObserver::SafeBrowsingCheckResults& results) {
for (auto& observer : observers_) {
- observer.OnSafeBrowsingCheckComplete(navigation_handle, threat_type,
- threat_metadata);
+ observer.OnSafeBrowsingChecksComplete(navigation_handle, results);
}
}
void SubresourceFilterObserverManager::NotifyPageActivationComputed(
content::NavigationHandle* navigation_handle,
- ActivationDecision activation_decision,
const ActivationState& activation_state) {
for (auto& observer : observers_) {
- observer.OnPageActivationComputed(navigation_handle, activation_decision,
- activation_state);
+ observer.OnPageActivationComputed(navigation_handle, activation_state);
}
}
diff --git a/chromium/components/subresource_filter/content/browser/subresource_filter_observer_manager.h b/chromium/components/subresource_filter/content/browser/subresource_filter_observer_manager.h
index 158491134e7..cf6c727d43f 100644
--- a/chromium/components/subresource_filter/content/browser/subresource_filter_observer_manager.h
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_observer_manager.h
@@ -32,21 +32,19 @@ class SubresourceFilterObserverManager
void AddObserver(SubresourceFilterObserver* observer);
void RemoveObserver(SubresourceFilterObserver* observer);
- // Called when the SubresourceFilter Safe Browsing check is available for this
- // main frame navigation. Will be called at WillProcessResponse time at the
- // latest. Right now it will only include phishing and subresource filter
+ // Called when the SubresourceFilter Safe Browsing checks are available for
+ // this main frame navigation. Will be called at WillProcessResponse time at
+ // the latest. Right now it will only include phishing and subresource filter
// threat types.
- virtual void NotifySafeBrowsingCheckComplete(
+ virtual void NotifySafeBrowsingChecksComplete(
content::NavigationHandle* navigation_handle,
- safe_browsing::SBThreatType threat_type,
- const safe_browsing::ThreatMetadata& threat_metadata);
+ const SubresourceFilterObserver::SafeBrowsingCheckResults& results);
// Will be called at the latest in the WillProcessResponse stage from a
// NavigationThrottle that was registered before the throttle manager's
// throttles created in MaybeAppendNavigationThrottles().
void NotifyPageActivationComputed(
content::NavigationHandle* navigation_handle,
- ActivationDecision activation_decision,
const ActivationState& activation_state);
// Called in WillStartRequest or WillRedirectRequest stage from a
diff --git a/chromium/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.cc b/chromium/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.cc
index 77a93659d2b..677c4b4fdd0 100644
--- a/chromium/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.cc
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.cc
@@ -5,6 +5,7 @@
#include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
#include "base/logging.h"
+#include "components/subresource_filter/core/common/activation_state.h"
#include "content/public/browser/navigation_handle.h"
namespace subresource_filter {
@@ -25,22 +26,13 @@ void TestSubresourceFilterObserver::OnSubresourceFilterGoingAway() {
scoped_observer_.RemoveAll();
}
-void TestSubresourceFilterObserver::OnSafeBrowsingCheckComplete(
- content::NavigationHandle* navigation_handle,
- safe_browsing::SBThreatType threat_type,
- const safe_browsing::ThreatMetadata& threat_metadata) {
- DCHECK(navigation_handle->IsInMainFrame());
- safe_browsing_checks_[navigation_handle->GetURL()] =
- std::make_pair(threat_type, threat_metadata);
-}
-
void TestSubresourceFilterObserver::OnPageActivationComputed(
content::NavigationHandle* navigation_handle,
- ActivationDecision activation_decision,
const ActivationState& activation_state) {
DCHECK(navigation_handle->IsInMainFrame());
- page_activations_[navigation_handle->GetURL()] = activation_decision;
- pending_activations_[navigation_handle] = activation_decision;
+ ActivationLevel level = activation_state.activation_level;
+ page_activations_[navigation_handle->GetURL()] = level;
+ pending_activations_[navigation_handle] = level;
}
void TestSubresourceFilterObserver::OnSubframeNavigationEvaluated(
@@ -66,16 +58,16 @@ void TestSubresourceFilterObserver::DidFinishNavigation(
last_committed_activation_ = it->second;
pending_activations_.erase(it);
} else {
- last_committed_activation_ = base::Optional<ActivationDecision>();
+ last_committed_activation_ = base::Optional<ActivationLevel>();
}
}
-base::Optional<ActivationDecision>
+base::Optional<ActivationLevel>
TestSubresourceFilterObserver::GetPageActivation(const GURL& url) const {
auto it = page_activations_.find(url);
if (it != page_activations_.end())
return it->second;
- return base::Optional<ActivationDecision>();
+ return base::Optional<ActivationLevel>();
}
base::Optional<bool> TestSubresourceFilterObserver::GetIsAdSubframe(
@@ -94,7 +86,7 @@ base::Optional<LoadPolicy> TestSubresourceFilterObserver::GetSubframeLoadPolicy(
return base::Optional<LoadPolicy>();
}
-base::Optional<ActivationDecision>
+base::Optional<ActivationLevel>
TestSubresourceFilterObserver::GetPageActivationForLastCommittedLoad() const {
return last_committed_activation_;
}
diff --git a/chromium/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h b/chromium/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h
index 6e098bab759..e4535d3f7f9 100644
--- a/chromium/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h
@@ -15,7 +15,7 @@
#include "components/safe_browsing/db/v4_protocol_manager_util.h"
#include "components/subresource_filter/content/browser/subresource_filter_observer.h"
#include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
-#include "components/subresource_filter/core/common/activation_decision.h"
+#include "components/subresource_filter/core/common/activation_level.h"
#include "components/subresource_filter/core/common/load_policy.h"
#include "content/public/browser/web_contents_observer.h"
#include "url/gurl.h"
@@ -33,18 +33,13 @@ namespace subresource_filter {
class TestSubresourceFilterObserver : public SubresourceFilterObserver,
public content::WebContentsObserver {
public:
- TestSubresourceFilterObserver(content::WebContents* web_contents);
+ explicit TestSubresourceFilterObserver(content::WebContents* web_contents);
~TestSubresourceFilterObserver() override;
// SubresourceFilterObserver:
void OnSubresourceFilterGoingAway() override;
- void OnSafeBrowsingCheckComplete(
- content::NavigationHandle* navigation_handle,
- safe_browsing::SBThreatType threat_type,
- const safe_browsing::ThreatMetadata& threat_metadata) override;
void OnPageActivationComputed(
content::NavigationHandle* navigation_handle,
- ActivationDecision activation_decision,
const ActivationState& activation_state) override;
void OnSubframeNavigationEvaluated(
content::NavigationHandle* navigation_handle,
@@ -55,11 +50,10 @@ class TestSubresourceFilterObserver : public SubresourceFilterObserver,
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
- base::Optional<ActivationDecision> GetPageActivation(const GURL& url) const;
+ base::Optional<ActivationLevel> GetPageActivation(const GURL& url) const;
base::Optional<LoadPolicy> GetSubframeLoadPolicy(const GURL& url) const;
base::Optional<bool> GetIsAdSubframe(const GURL& url) const;
- base::Optional<ActivationDecision> GetPageActivationForLastCommittedLoad()
- const;
+ base::Optional<ActivationLevel> GetPageActivationForLastCommittedLoad() const;
using SafeBrowsingCheck =
std::pair<safe_browsing::SBThreatType, safe_browsing::ThreatMetadata>;
@@ -69,11 +63,11 @@ class TestSubresourceFilterObserver : public SubresourceFilterObserver,
private:
std::map<GURL, LoadPolicy> subframe_load_evaluations_;
std::map<GURL, bool> ad_subframe_evaluations_;
- std::map<GURL, ActivationDecision> page_activations_;
- std::map<GURL, SafeBrowsingCheck> safe_browsing_checks_;
- std::map<content::NavigationHandle*, ActivationDecision> pending_activations_;
- base::Optional<ActivationDecision> last_committed_activation_;
+ std::map<GURL, ActivationLevel> page_activations_;
+ std::map<GURL, SafeBrowsingCheck> safe_browsing_checks_;
+ std::map<content::NavigationHandle*, ActivationLevel> pending_activations_;
+ base::Optional<ActivationLevel> last_committed_activation_;
ScopedObserver<SubresourceFilterObserverManager, SubresourceFilterObserver>
scoped_observer_;
diff --git a/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc b/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
index bd59e1542f9..1b50578a91b 100644
--- a/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
@@ -13,14 +13,16 @@
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_argument.h"
#include "components/subresource_filter/content/browser/content_activation_list_utils.h"
-#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
+#include "components/subresource_filter/content/browser/navigation_console_logger.h"
#include "components/subresource_filter/content/browser/subresource_filter_client.h"
#include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
#include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h"
+#include "components/subresource_filter/core/browser/subresource_filter_constants.h"
#include "components/ukm/ukm_source.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
+#include "content/public/common/console_message_level.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "ui/base/page_transition_types.h"
@@ -98,6 +100,25 @@ void SubresourceFilterSafeBrowsingActivationThrottle::OnCheckUrlResultOnUI(
}
}
+SubresourceFilterSafeBrowsingActivationThrottle::ConfigResult::ConfigResult(
+ Configuration config,
+ bool warning,
+ bool matched_valid_configuration,
+ ActivationList matched_list)
+ : config(config),
+ warning(warning),
+ matched_valid_configuration(matched_valid_configuration),
+ matched_list(matched_list) {}
+
+SubresourceFilterSafeBrowsingActivationThrottle::ConfigResult::ConfigResult() =
+ default;
+
+SubresourceFilterSafeBrowsingActivationThrottle::ConfigResult::ConfigResult(
+ const ConfigResult&) = default;
+
+SubresourceFilterSafeBrowsingActivationThrottle::ConfigResult::~ConfigResult() =
+ default;
+
void SubresourceFilterSafeBrowsingActivationThrottle::CheckCurrentUrl() {
DCHECK(database_client_);
check_start_times_.push_back(base::TimeTicks::Now());
@@ -112,61 +133,60 @@ void SubresourceFilterSafeBrowsingActivationThrottle::CheckCurrentUrl() {
void SubresourceFilterSafeBrowsingActivationThrottle::NotifyResult() {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"),
"SubresourceFilterSafeBrowsingActivationThrottle::NotifyResult");
- // Compute the matched list and notify observers of the check result.
DCHECK(!check_results_.empty());
- ActivationList matched_list = ActivationList::NONE;
- bool warning = false;
- const auto& check_result = check_results_.back();
- DCHECK(check_result.finished);
- matched_list = GetListForThreatTypeAndMetadata(
- check_result.threat_type, check_result.threat_metadata, &warning);
- SubresourceFilterObserverManager::FromWebContents(
- navigation_handle()->GetWebContents())
- ->NotifySafeBrowsingCheckComplete(navigation_handle(),
- check_result.threat_type,
- check_result.threat_metadata);
-
- Configuration matched_configuration;
- ActivationDecision activation_decision = ActivationDecision::UNKNOWN;
- if (client_->ForceActivationInCurrentWebContents()) {
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"), "ActivationForced");
- activation_decision = ActivationDecision::ACTIVATED;
- matched_configuration = Configuration::MakeForForcedActivation();
- } else {
- base::Optional<Configuration> config =
- GetHighestPriorityConfiguration(matched_list);
- if (config.has_value()) {
- matched_configuration = config.value();
- }
- activation_decision = GetActivationDecision(config);
+
+ // Determine which results to consider for safebrowsing/abusive.
+ std::vector<SubresourceFilterSafeBrowsingClient::CheckResult>
+ check_results_to_consider = {check_results_.back()};
+ if (check_results_.size() >= 2 &&
+ base::FeatureList::IsEnabled(
+ kSafeBrowsingSubresourceFilterConsiderRedirects)) {
+ check_results_to_consider = {check_results_[0], check_results_.back()};
+ }
+
+ // Find the ConfigResult for each safe browsing check.
+ std::vector<ConfigResult> matched_configurations;
+ for (const auto& current_result : check_results_to_consider) {
+ matched_configurations.push_back(
+ GetHighestPriorityConfiguration(current_result));
}
+
+ // Get the activation decision with the associated ConfigResult.
+ ConfigResult selection;
+ ActivationDecision activation_decision =
+ GetActivationDecision(matched_configurations, &selection);
DCHECK_NE(activation_decision, ActivationDecision::UNKNOWN);
- // Check for whitelisted status last, so that the client gets an accurate
- // indication of whether there would be activation otherwise.
- // Note that the client is responsible for noticing if we're forcing
- // activation.
- bool whitelisted = client_->OnPageActivationComputed(
- navigation_handle(),
- !warning && matched_configuration.activation_options.activation_level ==
- ActivationLevel::ENABLED);
-
- // Only reset the activation decision reason if we would have activated.
- if (whitelisted && activation_decision == ActivationDecision::ACTIVATED) {
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"), "ActivationWhitelisted");
- activation_decision = ActivationDecision::URL_WHITELISTED;
- matched_configuration = Configuration();
+ // Notify the observers of the check results.
+ SubresourceFilterObserverManager::FromWebContents(
+ navigation_handle()->GetWebContents())
+ ->NotifySafeBrowsingChecksComplete(navigation_handle(),
+ check_results_to_consider);
+
+ // Compute the activation level.
+ ActivationLevel activation_level =
+ selection.config.activation_options.activation_level;
+
+ if (selection.warning && activation_level == ActivationLevel::ENABLED) {
+ NavigationConsoleLogger::LogMessageOnCommit(
+ navigation_handle(), content::CONSOLE_MESSAGE_LEVEL_WARNING,
+ kActivationWarningConsoleMessage);
+ activation_level = ActivationLevel::DISABLED;
}
- LogMetricsOnChecksComplete(
- matched_list, activation_decision,
- matched_configuration.activation_options.activation_level);
+ // Let the embedder get the last word when it comes to activation level.
+ // TODO(csharrison): Move all ActivationDecision code to the embedder.
+ activation_level = client_->OnPageActivationComputed(
+ navigation_handle(), activation_level, &activation_decision);
- auto* driver_factory = ContentSubresourceFilterDriverFactory::FromWebContents(
- navigation_handle()->GetWebContents());
- DCHECK(driver_factory);
- driver_factory->NotifyPageActivationComputed(
- navigation_handle(), activation_decision, matched_configuration, warning);
+ LogMetricsOnChecksComplete(selection.matched_list, activation_decision,
+ activation_level);
+
+ SubresourceFilterObserverManager::FromWebContents(
+ navigation_handle()->GetWebContents())
+ ->NotifyPageActivationComputed(
+ navigation_handle(),
+ selection.config.GetActivationState(activation_level));
}
void SubresourceFilterSafeBrowsingActivationThrottle::
@@ -180,15 +200,6 @@ void SubresourceFilterSafeBrowsingActivationThrottle::
: base::TimeTicks::Now() - defer_time_;
UMA_HISTOGRAM_TIMES("SubresourceFilter.PageLoad.SafeBrowsingDelay", delay);
- // Log a histogram for the delay we would have introduced if the throttle only
- // speculatively checks URLs on WillStartRequest. This is only different from
- // the actual delay if there was at least one redirect.
- base::TimeDelta no_redirect_speculation_delay =
- check_results_.size() > 1 ? check_results_.back().check_time : delay;
- UMA_HISTOGRAM_TIMES(
- "SubresourceFilter.PageLoad.SafeBrowsingDelay.NoRedirectSpeculation",
- no_redirect_speculation_delay);
-
ukm::SourceId source_id = ukm::ConvertToSourceId(
navigation_handle()->GetNavigationId(), ukm::SourceIdType::NAVIGATION_ID);
ukm::builders::SubresourceFilter builder(source_id);
@@ -217,10 +228,16 @@ bool SubresourceFilterSafeBrowsingActivationThrottle::
return true;
}
-base::Optional<Configuration> SubresourceFilterSafeBrowsingActivationThrottle::
- GetHighestPriorityConfiguration(ActivationList matched_list) {
- base::Optional<Configuration> selected_config;
-
+SubresourceFilterSafeBrowsingActivationThrottle::ConfigResult
+SubresourceFilterSafeBrowsingActivationThrottle::
+ GetHighestPriorityConfiguration(
+ const SubresourceFilterSafeBrowsingClient::CheckResult& result) {
+ DCHECK(result.finished);
+ Configuration selected_config;
+ bool warning = false;
+ bool matched = false;
+ ActivationList matched_list = GetListForThreatTypeAndMetadata(
+ result.threat_type, result.threat_metadata, &warning);
// If it's http or https, find the best config.
if (navigation_handle()->GetURL().SchemeIsHTTPOrHTTPS()) {
const auto& decreasing_configs =
@@ -233,26 +250,71 @@ base::Optional<Configuration> SubresourceFilterSafeBrowsingActivationThrottle::
});
if (selected_config_itr != decreasing_configs.end()) {
selected_config = *selected_config_itr;
+ matched = true;
}
}
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("loading"),
"SubresourceFilterSafeBrowsingActivationThrottle::"
"GetHighestPriorityConfiguration",
"selected_config",
- selected_config.has_value()
- ? selected_config->ToTracedValue()
- : std::make_unique<base::trace_event::TracedValue>());
- return selected_config;
+ !matched ? selected_config.ToTracedValue()
+ : std::make_unique<base::trace_event::TracedValue>());
+ return ConfigResult(selected_config, warning, matched, matched_list);
}
ActivationDecision
SubresourceFilterSafeBrowsingActivationThrottle::GetActivationDecision(
- const base::Optional<Configuration>& config) {
- if (!config.has_value()) {
+ const std::vector<ConfigResult>& configs,
+ ConfigResult* selected_config) {
+ size_t selected_index = 0;
+ for (size_t current_index = 0; current_index < configs.size();
+ current_index++) {
+ // Prefer later configs when there's a tie.
+ // Rank no matching config slightly below priority zero.
+ const int selected_priority =
+ configs[selected_index].matched_valid_configuration
+ ? configs[selected_index].config.activation_conditions.priority
+ : -1;
+ const int current_priority =
+ configs[current_index].matched_valid_configuration
+ ? configs[current_index].config.activation_conditions.priority
+ : -1;
+ if (current_priority >= selected_priority) {
+ selected_index = current_index;
+ }
+ }
+ // Ensure that the list was not empty, and assign the configuration.
+ DCHECK(selected_index != configs.size());
+ *selected_config = configs[selected_index];
+
+ if (!selected_config->matched_valid_configuration) {
return ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET;
}
- auto activation_level = config->activation_options.activation_level;
+ // Get the activation level for the matching configuration.
+ auto activation_level =
+ selected_config->config.activation_options.activation_level;
+
+ // If there is an activation triggered by the activation list (not a dry run),
+ // report where in the redirect chain it was triggered.
+ if (selected_config->config.activation_conditions.activation_scope ==
+ ActivationScope::ACTIVATION_LIST &&
+ activation_level == ActivationLevel::ENABLED) {
+ ActivationPosition position;
+ if (configs.size() == 1) {
+ position = ActivationPosition::kOnly;
+ } else if (selected_index == 0) {
+ position = ActivationPosition::kFirst;
+ } else if (selected_index == configs.size() - 1) {
+ position = ActivationPosition::kLast;
+ } else {
+ position = ActivationPosition::kMiddle;
+ }
+ UMA_HISTOGRAM_ENUMERATION(
+ "SubresourceFilter.PageLoad.Activation.RedirectPosition", position);
+ }
+
+ // Compute and return the activation decision.
return activation_level == ActivationLevel::DISABLED
? ActivationDecision::ACTIVATION_DISABLED
: ActivationDecision::ACTIVATED;
diff --git a/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h b/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h
index 13e76882da4..972bcaeda0d 100644
--- a/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h
+++ b/chromium/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h
@@ -8,8 +8,10 @@
#include <stddef.h>
#include <memory>
+#include <utility>
#include <vector>
+#include "base/feature_list.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
@@ -26,6 +28,14 @@ namespace subresource_filter {
class SubresourceFilterClient;
+enum class ActivationPosition {
+ kOnly = 0,
+ kFirst = 1,
+ kMiddle = 2,
+ kLast = 3,
+ kMaxValue = kLast,
+};
+
// Navigation throttle responsible for activating subresource filtering on page
// loads that match the SUBRESOURCE_FILTER Safe Browsing list.
class SubresourceFilterSafeBrowsingActivationThrottle
@@ -53,6 +63,21 @@ class SubresourceFilterSafeBrowsingActivationThrottle
const SubresourceFilterSafeBrowsingClient::CheckResult& result);
private:
+ // Highest priority config for a check result.
+ struct ConfigResult {
+ Configuration config;
+ bool warning;
+ bool matched_valid_configuration;
+ ActivationList matched_list;
+
+ ConfigResult(Configuration config,
+ bool warning,
+ bool matched_valid_configuration,
+ ActivationList matched_list);
+ ~ConfigResult();
+ ConfigResult();
+ ConfigResult(const ConfigResult& result);
+ };
void CheckCurrentUrl();
void NotifyResult();
@@ -62,12 +87,13 @@ class SubresourceFilterSafeBrowsingActivationThrottle
bool HasFinishedAllSafeBrowsingChecks() const;
// Gets the configuration with the highest priority among those activated.
// Returns it, or none if no valid activated configurations.
- base::Optional<Configuration> GetHighestPriorityConfiguration(
- ActivationList matched_list);
+ ConfigResult GetHighestPriorityConfiguration(
+ const SubresourceFilterSafeBrowsingClient::CheckResult& result);
// Gets the ActivationDecision for the given Configuration.
// Returns it, or ACTIVATION_CONDITIONS_NOT_MET if no Configuration.
ActivationDecision GetActivationDecision(
- const base::Optional<Configuration>& config);
+ const std::vector<ConfigResult>& configs,
+ ConfigResult* selected_config);
// Returns whether a main-frame navigation satisfies the activation
// |conditions| of a given configuration, except for |priority|.
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 aeef18cfa94..7c71e86e6fd 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
@@ -14,11 +14,12 @@
#include "base/message_loop/message_loop_current.h"
#include "base/metrics/field_trial.h"
#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
#include "base/test/test_mock_time_task_runner.h"
#include "build/build_config.h"
#include "components/safe_browsing/db/test_database_manager.h"
-#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
+#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
#include "components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h"
#include "components/subresource_filter/content/browser/subresource_filter_client.h"
#include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
@@ -60,8 +61,6 @@ char kRedirectURL[] = "http://redirect.test/";
const char kSafeBrowsingNavigationDelay[] =
"SubresourceFilter.PageLoad.SafeBrowsingDelay";
-const char kSafeBrowsingNavigationDelayNoSpeculation[] =
- "SubresourceFilter.PageLoad.SafeBrowsingDelay.NoRedirectSpeculation";
const char kSafeBrowsingCheckTime[] =
"SubresourceFilter.SafeBrowsing.CheckTime";
const char kActivationListHistogram[] =
@@ -72,24 +71,20 @@ class MockSubresourceFilterClient : public SubresourceFilterClient {
MockSubresourceFilterClient() = default;
~MockSubresourceFilterClient() override = default;
- // Mocks have trouble with move-only types passed in the constructor.
- void set_ruleset_dealer(
- std::unique_ptr<VerifiedRulesetDealer::Handle> ruleset_dealer) {
- ruleset_dealer_ = std::move(ruleset_dealer);
- }
-
- bool OnPageActivationComputed(content::NavigationHandle* handle,
- bool activated) override {
+ ActivationLevel OnPageActivationComputed(
+ content::NavigationHandle* handle,
+ ActivationLevel effective_level,
+ ActivationDecision* decision) override {
DCHECK(handle->IsInMainFrame());
- return whitelisted_hosts_.count(handle->GetURL().host());
- }
-
- VerifiedRulesetDealer::Handle* GetRulesetDealer() override {
- return ruleset_dealer_.get();
+ if (whitelisted_hosts_.count(handle->GetURL().host())) {
+ if (effective_level == subresource_filter::ActivationLevel::ENABLED)
+ *decision = subresource_filter::ActivationDecision::URL_WHITELISTED;
+ return ActivationLevel::DISABLED;
+ }
+ return effective_level;
}
MOCK_METHOD0(ShowNotification, void());
- MOCK_METHOD0(OnNewNavigationStarted, void());
MOCK_METHOD0(ForceActivationInCurrentWebContents, bool());
void WhitelistInCurrentWebContents(const GURL& url) {
@@ -102,8 +97,6 @@ class MockSubresourceFilterClient : public SubresourceFilterClient {
private:
std::set<std::string> whitelisted_hosts_;
- std::unique_ptr<VerifiedRulesetDealer::Handle> ruleset_dealer_;
-
DISALLOW_COPY_AND_ASSIGN(MockSubresourceFilterClient);
};
@@ -175,21 +168,22 @@ class SubresourceFilterSafeBrowsingActivationThrottleTest
rules.push_back(testing::CreateSuffixRule("disallowed.html"));
ASSERT_NO_FATAL_FAILURE(test_ruleset_creator_.CreateRulesetWithRules(
rules, &test_ruleset_pair_));
- auto ruleset_dealer = std::make_unique<VerifiedRulesetDealer::Handle>(
+ ruleset_dealer_ = std::make_unique<VerifiedRulesetDealer::Handle>(
base::MessageLoopCurrent::Get()->task_runner());
- ruleset_dealer->TryOpenAndSetRulesetFile(test_ruleset_pair_.indexed.path,
- base::DoNothing());
+ ruleset_dealer_->TryOpenAndSetRulesetFile(test_ruleset_pair_.indexed.path,
+ base::DoNothing());
+
+ auto* contents = RenderViewHostTestHarness::web_contents();
client_ =
std::make_unique<::testing::NiceMock<MockSubresourceFilterClient>>();
- client_->set_ruleset_dealer(std::move(ruleset_dealer));
- ContentSubresourceFilterDriverFactory::CreateForWebContents(
- RenderViewHostTestHarness::web_contents(), client_.get());
+ throttle_manager_ =
+ std::make_unique<ContentSubresourceFilterThrottleManager>(
+ client_.get(), ruleset_dealer_.get(), contents);
fake_safe_browsing_database_ = new FakeSafeBrowsingDatabaseManager();
NavigateAndCommit(GURL("https://test.com"));
- Observe(RenderViewHostTestHarness::web_contents());
+ Observe(contents);
- observer_ = std::make_unique<TestSubresourceFilterObserver>(
- RenderViewHostTestHarness::web_contents());
+ observer_ = std::make_unique<TestSubresourceFilterObserver>(contents);
}
virtual void Configure() {
@@ -199,7 +193,7 @@ class SubresourceFilterSafeBrowsingActivationThrottleTest
}
void TearDown() override {
- client_.reset();
+ ruleset_dealer_.reset();
// RunUntilIdle() must be called multiple times to flush any outstanding
// cross-thread interactions.
@@ -226,10 +220,8 @@ class SubresourceFilterSafeBrowsingActivationThrottleTest
fake_safe_browsing_database_));
}
std::vector<std::unique_ptr<content::NavigationThrottle>> throttles;
- auto* factory = ContentSubresourceFilterDriverFactory::FromWebContents(
- navigation_handle->GetWebContents());
- factory->throttle_manager()->MaybeAppendNavigationThrottles(
- navigation_handle, &throttles);
+ throttle_manager_->MaybeAppendNavigationThrottles(navigation_handle,
+ &throttles);
for (auto& it : throttles) {
navigation_handle->RegisterThrottleForTesting(std::move(it));
}
@@ -357,6 +349,10 @@ class SubresourceFilterSafeBrowsingActivationThrottleTest
testing::TestRulesetCreator test_ruleset_creator_;
testing::TestRulesetPair test_ruleset_pair_;
+ std::unique_ptr<VerifiedRulesetDealer::Handle> ruleset_dealer_;
+
+ std::unique_ptr<ContentSubresourceFilterThrottleManager> throttle_manager_;
+
std::unique_ptr<content::NavigationSimulator> navigation_simulator_;
std::unique_ptr<MockSubresourceFilterClient> client_;
std::unique_ptr<TestSubresourceFilterObserver> observer_;
@@ -431,22 +427,22 @@ class SubresourceFilterSafeBrowsingActivationThrottleTestWithCancelling
};
struct ActivationScopeTestData {
- ActivationDecision expected_activation_decision;
+ ActivationLevel expected_activation_level;
bool url_matches_activation_list;
ActivationScope activation_scope;
};
const ActivationScopeTestData kActivationScopeTestData[] = {
- {ActivationDecision::ACTIVATED, false /* url_matches_activation_list */,
+ {ActivationLevel::ENABLED, false /* url_matches_activation_list */,
ActivationScope::ALL_SITES},
- {ActivationDecision::ACTIVATED, true /* url_matches_activation_list */,
+ {ActivationLevel::ENABLED, true /* url_matches_activation_list */,
ActivationScope::ALL_SITES},
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
- true /* url_matches_activation_list */, ActivationScope::NO_SITES},
- {ActivationDecision::ACTIVATED, true /* url_matches_activation_list */,
+ {ActivationLevel::DISABLED, true /* url_matches_activation_list */,
+ ActivationScope::NO_SITES},
+ {ActivationLevel::ENABLED, true /* url_matches_activation_list */,
+ ActivationScope::ACTIVATION_LIST},
+ {ActivationLevel::DISABLED, false /* url_matches_activation_list */,
ActivationScope::ACTIVATION_LIST},
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
- false /* url_matches_activation_list */, ActivationScope::ACTIVATION_LIST},
};
class SubresourceFilterSafeBrowsingActivationThrottleScopeTest
@@ -464,7 +460,7 @@ class SubresourceFilterSafeBrowsingActivationThrottleScopeTest
TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest, NoConfigs) {
scoped_configuration()->ResetConfiguration(std::vector<Configuration>());
SimulateNavigateAndCommit({GURL(kURL)}, main_rfh());
- EXPECT_EQ(ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
+ EXPECT_EQ(ActivationLevel::DISABLED,
*observer()->GetPageActivationForLastCommittedLoad());
}
@@ -492,12 +488,12 @@ TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest,
ConfigureForMatch(match_url, safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
metadata);
SimulateNavigateAndCommit({match_url}, main_rfh());
- EXPECT_EQ(ActivationDecision::ACTIVATION_DISABLED,
+ EXPECT_EQ(ActivationLevel::DISABLED,
*observer()->GetPageActivationForLastCommittedLoad());
// Should match |config3|.
SimulateNavigateAndCommit({non_match_url}, main_rfh());
- EXPECT_EQ(ActivationDecision::ACTIVATED,
+ EXPECT_EQ(ActivationLevel::ENABLED,
*observer()->GetPageActivationForLastCommittedLoad());
}
@@ -509,18 +505,18 @@ TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest,
GURL url(kURL);
SimulateNavigateAndCommit({url}, main_rfh());
- EXPECT_EQ(ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
+ EXPECT_EQ(ActivationLevel::DISABLED,
*observer()->GetPageActivationForLastCommittedLoad());
ConfigureForMatch(url);
SimulateNavigateAndCommit({url}, main_rfh());
- EXPECT_EQ(ActivationDecision::ACTIVATION_DISABLED,
+ EXPECT_EQ(ActivationLevel::DISABLED,
*observer()->GetPageActivationForLastCommittedLoad());
// Whitelisting occurs last, so the decision should still be DISABLED.
client()->WhitelistInCurrentWebContents(url);
SimulateNavigateAndCommit({url}, main_rfh());
- EXPECT_EQ(ActivationDecision::ACTIVATION_DISABLED,
+ EXPECT_EQ(ActivationLevel::DISABLED,
*observer()->GetPageActivationForLastCommittedLoad());
}
@@ -530,12 +526,12 @@ TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest,
Configuration(ActivationLevel::ENABLED, ActivationScope::ALL_SITES));
GURL url(kURL);
SimulateNavigateAndCommit({url}, main_rfh());
- EXPECT_EQ(ActivationDecision::ACTIVATED,
+ EXPECT_EQ(ActivationLevel::ENABLED,
*observer()->GetPageActivationForLastCommittedLoad());
ConfigureForMatch(url);
SimulateNavigateAndCommit({url}, main_rfh());
- EXPECT_EQ(ActivationDecision::ACTIVATED,
+ EXPECT_EQ(ActivationLevel::ENABLED,
*observer()->GetPageActivationForLastCommittedLoad());
// Adding performance measurement should keep activation.
@@ -544,17 +540,17 @@ TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest,
config_with_perf.activation_options.performance_measurement_rate = 1.0;
scoped_configuration()->ResetConfiguration(std::move(config_with_perf));
SimulateNavigateAndCommit({url}, main_rfh());
- EXPECT_EQ(ActivationDecision::ACTIVATED,
+ EXPECT_EQ(ActivationLevel::ENABLED,
*observer()->GetPageActivationForLastCommittedLoad());
}
TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest,
NavigationFails_NoActivation) {
- EXPECT_EQ(base::Optional<ActivationDecision>(),
+ EXPECT_EQ(base::Optional<ActivationLevel>(),
observer()->GetPageActivationForLastCommittedLoad());
content::NavigationSimulator::NavigateAndFailFromDocument(
GURL(kURL), net::ERR_TIMED_OUT, main_rfh());
- EXPECT_EQ(base::Optional<ActivationDecision>(),
+ EXPECT_EQ(base::Optional<ActivationLevel>(),
observer()->GetPageActivationForLastCommittedLoad());
}
@@ -562,122 +558,68 @@ TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest,
NotificationVisibility) {
GURL url(kURL);
ConfigureForMatch(url);
- EXPECT_CALL(*client(), OnNewNavigationStarted()).Times(1);
content::RenderFrameHost* rfh = SimulateNavigateAndCommit({url}, main_rfh());
EXPECT_CALL(*client(), ShowNotification()).Times(1);
EXPECT_FALSE(CreateAndNavigateDisallowedSubframe(rfh));
}
-TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest,
- ActivateForFrameState) {
- const struct {
- ActivationDecision activation_decision;
- ActivationLevel activation_level;
- } kTestCases[] = {
- {ActivationDecision::ACTIVATED, ActivationLevel::DRYRUN},
- {ActivationDecision::ACTIVATED, ActivationLevel::ENABLED},
- {ActivationDecision::ACTIVATION_DISABLED, ActivationLevel::DISABLED},
- };
- for (const auto& test_data : kTestCases) {
- SCOPED_TRACE(::testing::Message()
- << "activation_decision "
- << static_cast<int>(test_data.activation_decision)
- << " activation_level " << test_data.activation_level);
- client()->ClearWhitelist();
- scoped_configuration()->ResetConfiguration(Configuration(
- test_data.activation_level, ActivationScope::ACTIVATION_LIST,
- ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL));
- const GURL url(kURLWithParams);
- safe_browsing::ThreatMetadata metadata;
- metadata.threat_pattern_type =
- safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS;
- ConfigureForMatch(url, safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
- metadata);
- SimulateNavigateAndCommit({url}, main_rfh());
- EXPECT_EQ(test_data.activation_decision,
- *observer()->GetPageActivationForLastCommittedLoad());
-
- // Whitelisting is only applied when the page will otherwise activate.
- client()->WhitelistInCurrentWebContents(url);
- ActivationDecision decision =
- test_data.activation_level == ActivationLevel::DISABLED
- ? test_data.activation_decision
- : ActivationDecision::URL_WHITELISTED;
- SimulateNavigateAndCommit({url}, main_rfh());
- EXPECT_EQ(decision, *observer()->GetPageActivationForLastCommittedLoad());
- }
-}
-
TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest, ActivationList) {
const struct {
- ActivationDecision expected_activation_decision;
+ ActivationLevel expected_activation_level;
ActivationList activation_list;
safe_browsing::SBThreatType threat_type;
safe_browsing::ThreatPatternType threat_type_metadata;
} kTestCases[] = {
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET, ActivationList::NONE,
+ {ActivationLevel::DISABLED, ActivationList::NONE,
safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
- ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL,
+ {ActivationLevel::DISABLED, ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
safe_browsing::ThreatPatternType::NONE},
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
- ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL,
+ {ActivationLevel::DISABLED, ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
safe_browsing::ThreatPatternType::MALWARE_LANDING},
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
- ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL,
+ {ActivationLevel::DISABLED, ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
safe_browsing::ThreatPatternType::MALWARE_DISTRIBUTION},
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
- ActivationList::PHISHING_INTERSTITIAL,
+ {ActivationLevel::DISABLED, ActivationList::PHISHING_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_API_ABUSE,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
- ActivationList::PHISHING_INTERSTITIAL,
+ {ActivationLevel::DISABLED, ActivationList::PHISHING_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_BLACKLISTED_RESOURCE,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
- ActivationList::PHISHING_INTERSTITIAL,
+ {ActivationLevel::DISABLED, ActivationList::PHISHING_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
- ActivationList::PHISHING_INTERSTITIAL,
+ {ActivationLevel::DISABLED, ActivationList::PHISHING_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_URL_BINARY_MALWARE,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
- ActivationList::PHISHING_INTERSTITIAL,
+ {ActivationLevel::DISABLED, ActivationList::PHISHING_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_URL_UNWANTED,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
- ActivationList::PHISHING_INTERSTITIAL,
+ {ActivationLevel::DISABLED, ActivationList::PHISHING_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_URL_MALWARE,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
- ActivationList::PHISHING_INTERSTITIAL,
+ {ActivationLevel::DISABLED, ActivationList::PHISHING_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
- ActivationList::PHISHING_INTERSTITIAL,
+ {ActivationLevel::DISABLED, ActivationList::PHISHING_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_SAFE,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
- {ActivationDecision::ACTIVATED, ActivationList::PHISHING_INTERSTITIAL,
+ {ActivationLevel::ENABLED, ActivationList::PHISHING_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
safe_browsing::ThreatPatternType::NONE},
- {ActivationDecision::ACTIVATED,
- ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL,
+ {ActivationLevel::ENABLED, ActivationList::SOCIAL_ENG_ADS_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
- {ActivationDecision::ACTIVATED, ActivationList::PHISHING_INTERSTITIAL,
+ {ActivationLevel::ENABLED, ActivationList::PHISHING_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
safe_browsing::ThreatPatternType::SOCIAL_ENGINEERING_ADS},
- {ActivationDecision::ACTIVATED, ActivationList::SUBRESOURCE_FILTER,
+ {ActivationLevel::ENABLED, ActivationList::SUBRESOURCE_FILTER,
safe_browsing::SB_THREAT_TYPE_SUBRESOURCE_FILTER,
safe_browsing::ThreatPatternType::NONE},
- {ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
- ActivationList::PHISHING_INTERSTITIAL,
+ {ActivationLevel::DISABLED, ActivationList::PHISHING_INTERSTITIAL,
safe_browsing::SB_THREAT_TYPE_SUBRESOURCE_FILTER,
safe_browsing::ThreatPatternType::NONE},
};
@@ -692,7 +634,7 @@ TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest, ActivationList) {
ConfigureForMatch(test_url, test_case.threat_type, metadata);
SimulateNavigateAndCommit({GURL(kUrlA), GURL(kUrlB), GURL(kUrlC), test_url},
main_rfh());
- EXPECT_EQ(test_case.expected_activation_decision,
+ EXPECT_EQ(test_case.expected_activation_level,
*observer()->GetPageActivationForLastCommittedLoad());
}
}
@@ -781,16 +723,12 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleScopeTest,
if (test_data.url_matches_activation_list)
ConfigureForMatch(test_url);
SimulateNavigateAndCommit({test_url}, main_rfh());
- EXPECT_EQ(test_data.expected_activation_decision,
+ EXPECT_EQ(test_data.expected_activation_level,
*observer()->GetPageActivationForLastCommittedLoad());
if (test_data.url_matches_activation_list) {
client()->WhitelistInCurrentWebContents(test_url);
- ActivationDecision expected_decision =
- test_data.expected_activation_decision;
- if (expected_decision == ActivationDecision::ACTIVATED)
- expected_decision = ActivationDecision::URL_WHITELISTED;
SimulateNavigateAndCommit({test_url}, main_rfh());
- EXPECT_EQ(expected_decision,
+ EXPECT_EQ(ActivationLevel::DISABLED,
*observer()->GetPageActivationForLastCommittedLoad());
}
};
@@ -815,7 +753,7 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleScopeTest,
if (test_data.url_matches_activation_list)
ConfigureForMatch(GURL(url));
SimulateNavigateAndCommit({GURL(url)}, main_rfh());
- EXPECT_EQ(ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
+ EXPECT_EQ(ActivationLevel::DISABLED,
*observer()->GetPageActivationForLastCommittedLoad());
}
@@ -824,7 +762,7 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleScopeTest,
if (test_data.url_matches_activation_list)
ConfigureForMatch(GURL(url));
SimulateNavigateAndCommit({GURL(url)}, main_rfh());
- EXPECT_EQ(test_data.expected_activation_decision,
+ EXPECT_EQ(test_data.expected_activation_level,
*observer()->GetPageActivationForLastCommittedLoad());
}
};
@@ -834,13 +772,12 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleParamTest,
const GURL url(kURL);
SimulateStartAndExpectProceed(url);
SimulateCommitAndExpectProceed();
- EXPECT_EQ(ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
+ EXPECT_EQ(ActivationLevel::DISABLED,
*observer()->GetPageActivationForLastCommittedLoad());
tester().ExpectUniqueSample(kActivationListHistogram,
static_cast<int>(ActivationList::NONE), 1);
tester().ExpectTotalCount(kSafeBrowsingNavigationDelay, 1);
- tester().ExpectTotalCount(kSafeBrowsingNavigationDelayNoSpeculation, 1);
tester().ExpectTotalCount(kSafeBrowsingCheckTime, 1);
}
@@ -851,7 +788,7 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleParamTest,
ConfigureForMatchParam(url);
SimulateStartAndExpectProceed(url);
SimulateCommitAndExpectProceed();
- EXPECT_EQ(ActivationDecision::ACTIVATED,
+ EXPECT_EQ(ActivationLevel::ENABLED,
*observer()->GetPageActivationForLastCommittedLoad());
tester().ExpectUniqueSample(kActivationListHistogram,
static_cast<int>(test_data.activation_list_type),
@@ -864,7 +801,7 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleParamTest,
SimulateStartAndExpectProceed(url);
SimulateRedirectAndExpectProceed(GURL(kRedirectURL));
SimulateCommitAndExpectProceed();
- EXPECT_EQ(ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
+ EXPECT_EQ(ActivationLevel::DISABLED,
*observer()->GetPageActivationForLastCommittedLoad());
tester().ExpectUniqueSample(kActivationListHistogram,
static_cast<int>(ActivationList::NONE), 1);
@@ -878,7 +815,7 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleParamTest,
SimulateStartAndExpectProceed(url);
SimulateRedirectAndExpectProceed(GURL(kRedirectURL));
SimulateCommitAndExpectProceed();
- EXPECT_EQ(ActivationDecision::ACTIVATED,
+ EXPECT_EQ(ActivationLevel::ENABLED,
*observer()->GetPageActivationForLastCommittedLoad());
tester().ExpectUniqueSample(kActivationListHistogram,
static_cast<int>(test_data.activation_list_type),
@@ -903,10 +840,9 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleParamTest,
EXPECT_EQ(expected_delay, test_io_task_runner()->NextPendingTaskDelay());
test_io_task_runner()->FastForwardBy(expected_delay);
SimulateCommitAndExpectProceed();
- EXPECT_EQ(ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
+ EXPECT_EQ(ActivationLevel::DISABLED,
*observer()->GetPageActivationForLastCommittedLoad());
tester().ExpectTotalCount(kSafeBrowsingNavigationDelay, 1);
- tester().ExpectTotalCount(kSafeBrowsingNavigationDelayNoSpeculation, 1);
tester().ExpectTotalCount(kSafeBrowsingCheckTime, 1);
}
@@ -922,7 +858,7 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleParamTest,
RunUntilIdle();
SimulateCommitAndExpectProceed();
- EXPECT_EQ(ActivationDecision::ACTIVATED,
+ EXPECT_EQ(ActivationLevel::ENABLED,
*observer()->GetPageActivationForLastCommittedLoad());
tester().ExpectUniqueSample(kActivationListHistogram,
static_cast<int>(test_data.activation_list_type),
@@ -930,7 +866,6 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleParamTest,
tester().ExpectTimeBucketCount(kSafeBrowsingNavigationDelay,
base::TimeDelta::FromMilliseconds(0), 1);
- tester().ExpectTotalCount(kSafeBrowsingNavigationDelayNoSpeculation, 1);
}
// Flaky on Win, Chromium and Linux. http://crbug.com/748524
@@ -948,7 +883,7 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleParamTest,
RunUntilIdle();
SimulateCommitAndExpectProceed();
- EXPECT_EQ(ActivationDecision::ACTIVATED,
+ EXPECT_EQ(ActivationLevel::ENABLED,
*observer()->GetPageActivationForLastCommittedLoad());
tester().ExpectUniqueSample(kActivationListHistogram,
static_cast<int>(test_data.activation_list_type),
@@ -957,10 +892,75 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleParamTest,
const std::string suffix(GetSuffixForList(test_data.activation_list_type));
tester().ExpectTimeBucketCount(kSafeBrowsingNavigationDelay,
base::TimeDelta::FromMilliseconds(0), 1);
- tester().ExpectTotalCount(kSafeBrowsingNavigationDelayNoSpeculation, 1);
tester().ExpectTotalCount(kSafeBrowsingCheckTime, 2);
}
+struct RedirectSamplesAndResults {
+ std::vector<GURL> urls;
+ bool expected_activation;
+ ActivationPosition expected_position;
+};
+
+TEST_F(SubresourceFilterSafeBrowsingActivationThrottleTest,
+ ActivationTriggeredOnRedirect) {
+ // Turn on the feature to perform safebrowsing on redirects.
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ kSafeBrowsingSubresourceFilterConsiderRedirects);
+ std::string histogram_string =
+ "SubresourceFilter.PageLoad.Activation.RedirectPosition";
+
+ // Set up the urls for enforcement.
+ GURL normal_url("https://example.regular");
+ GURL bad_url("https://example.bad");
+ GURL worse_url("https://example.worse");
+
+ // Set up the configurations, make phishing worse than subresource_filter.
+ Configuration config_p1(ActivationLevel::ENABLED,
+ ActivationScope::ACTIVATION_LIST,
+ ActivationList::SUBRESOURCE_FILTER);
+ config_p1.activation_conditions.priority = 1;
+ Configuration config_p2(ActivationLevel::ENABLED,
+ ActivationScope::ACTIVATION_LIST,
+ ActivationList::PHISHING_INTERSTITIAL);
+ config_p2.activation_conditions.priority = 2;
+ scoped_configuration()->ResetConfiguration({config_p1, config_p2});
+
+ // Configure the URLs to match on different lists, phishing is worse.
+ ConfigureForMatch(bad_url, safe_browsing::SB_THREAT_TYPE_SUBRESOURCE_FILTER);
+ ConfigureForMatch(worse_url, safe_browsing::SB_THREAT_TYPE_URL_PHISHING);
+
+ // Check cases where there are multiple redirection.
+ const RedirectSamplesAndResults kTestCases[] = {
+ {{worse_url, normal_url, normal_url}, true, ActivationPosition::kFirst},
+ {{bad_url, normal_url, worse_url}, true, ActivationPosition::kLast},
+ {{worse_url, normal_url, bad_url}, true, ActivationPosition::kFirst},
+ {{normal_url, worse_url, bad_url}, true, ActivationPosition::kLast},
+ {{normal_url, normal_url}, false, ActivationPosition::kMaxValue},
+ {{normal_url, bad_url, normal_url}, false, ActivationPosition::kMaxValue},
+ {{worse_url}, true, ActivationPosition::kOnly},
+ };
+ for (const auto& test_case : kTestCases) {
+ const base::HistogramTester histograms;
+ SimulateStartAndExpectProceed(test_case.urls[0]);
+ for (size_t index = 1; index < test_case.urls.size(); index++) {
+ SimulateRedirectAndExpectProceed(test_case.urls[index]);
+ }
+ RunUntilIdle();
+ SimulateCommitAndExpectProceed();
+ if (test_case.expected_activation) {
+ EXPECT_EQ(ActivationLevel::ENABLED,
+ *observer()->GetPageActivationForLastCommittedLoad());
+ histograms.ExpectUniqueSample(histogram_string,
+ test_case.expected_position, 1);
+ } else {
+ EXPECT_EQ(ActivationLevel::DISABLED,
+ *observer()->GetPageActivationForLastCommittedLoad());
+ histograms.ExpectTotalCount(histogram_string, 0);
+ }
+ }
+}
+
// Disabled due to flaky failures: https://crbug.com/753669.
TEST_P(SubresourceFilterSafeBrowsingActivationThrottleParamTest,
DISABLED_ListMatchedOnStartWithRedirect_NoActivation) {
@@ -977,11 +977,10 @@ TEST_P(SubresourceFilterSafeBrowsingActivationThrottleParamTest,
RunUntilIdle();
SimulateCommitAndExpectProceed();
- EXPECT_EQ(ActivationDecision::ACTIVATION_CONDITIONS_NOT_MET,
+ EXPECT_EQ(ActivationLevel::DISABLED,
*observer()->GetPageActivationForLastCommittedLoad());
tester().ExpectTimeBucketCount(kSafeBrowsingNavigationDelay,
base::TimeDelta::FromMilliseconds(0), 1);
- tester().ExpectTotalCount(kSafeBrowsingNavigationDelayNoSpeculation, 1);
}
TEST_P(SubresourceFilterSafeBrowsingActivationThrottleTestWithCancelling,
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 c390432617e..ecd2d28358b 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,11 +48,8 @@ void SubresourceFilterSafeBrowsingClientRequest::Start(const GURL& url) {
start_time_ = base::TimeTicks::Now();
// Just return SAFE if the database is not supported.
- // TODO(csharrison): Remove CanCheckSubresourceFilter now that V4 has fully
- // shipped.
bool synchronous_finish =
!database_manager_->IsSupported() ||
- !database_manager_->CanCheckSubresourceFilter() ||
database_manager_->CheckUrlForSubresourceFilter(url, this);
if (synchronous_finish) {
request_completed_ = true;
diff --git a/chromium/components/subresource_filter/content/browser/verified_ruleset_dealer.cc b/chromium/components/subresource_filter/content/browser/verified_ruleset_dealer.cc
index e0dc2d5bbd7..4cd8a8b2517 100644
--- a/chromium/components/subresource_filter/content/browser/verified_ruleset_dealer.cc
+++ b/chromium/components/subresource_filter/content/browser/verified_ruleset_dealer.cc
@@ -30,6 +30,9 @@ base::File VerifiedRulesetDealer::OpenAndSetRulesetFile(
// there are handles to it still open.
base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_SHARE_DELETE);
+ TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("loading"),
+ "VerifiedRulesetDealer::OpenAndSetRulesetFile", "file_valid",
+ file.IsValid());
if (file.IsValid())
SetRulesetFile(file.Duplicate());
return file;
@@ -59,7 +62,8 @@ scoped_refptr<const MemoryMappedRuleset> VerifiedRulesetDealer::GetRuleset() {
}
case RulesetVerificationStatus::INTACT: {
auto ruleset = RulesetDealer::GetRuleset();
- DCHECK(ruleset);
+ // Note, |ruleset| can still be nullptr here if mmap fails, despite the
+ // fact that it must have succeeded previously.
return ruleset;
}
case RulesetVerificationStatus::CORRUPT:
diff --git a/chromium/components/subresource_filter/content/browser/verified_ruleset_dealer_unittest.cc b/chromium/components/subresource_filter/content/browser/verified_ruleset_dealer_unittest.cc
index 91cbfd91c98..5862fbe0716 100644
--- a/chromium/components/subresource_filter/content/browser/verified_ruleset_dealer_unittest.cc
+++ b/chromium/components/subresource_filter/content/browser/verified_ruleset_dealer_unittest.cc
@@ -164,6 +164,30 @@ TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
EXPECT_EQ(RulesetVerificationStatus::CORRUPT, ruleset_dealer()->status());
}
+// This is a duplicated test from RulesetDealer, to ensure that verification
+// doesn't introduce any bad assumptions about mmap failures.
+TEST_F(SubresourceFilterVerifiedRulesetDealerTest, MmapFailure) {
+ ruleset_dealer()->SetRulesetFile(
+ testing::TestRuleset::Open(rulesets().indexed_1()));
+ {
+ scoped_refptr<const MemoryMappedRuleset> ref_to_ruleset =
+ ruleset_dealer()->GetRuleset();
+ EXPECT_TRUE(!!ref_to_ruleset);
+
+ // Simulate subsequent mmap failures
+ MemoryMappedRuleset::SetMemoryMapFailuresForTesting(true);
+
+ // Calls to GetRuleset should succeed as long as the strong ref
+ // is still around.
+ EXPECT_TRUE(ruleset_dealer()->has_cached_ruleset());
+ EXPECT_TRUE(!!ruleset_dealer()->GetRuleset());
+ }
+ EXPECT_FALSE(ruleset_dealer()->has_cached_ruleset());
+ EXPECT_FALSE(!!ruleset_dealer()->GetRuleset());
+ MemoryMappedRuleset::SetMemoryMapFailuresForTesting(false);
+ EXPECT_TRUE(!!ruleset_dealer()->GetRuleset());
+}
+
TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
TruncatingFileMakesRulesetInvalid) {
testing::TestRuleset::CorruptByTruncating(rulesets().indexed_1(), 4096);
diff --git a/chromium/components/subresource_filter/content/common/ad_delay_throttle.cc b/chromium/components/subresource_filter/content/common/ad_delay_throttle.cc
index be5c04e4048..d3bb807c59d 100644
--- a/chromium/components/subresource_filter/content/common/ad_delay_throttle.cc
+++ b/chromium/components/subresource_filter/content/common/ad_delay_throttle.cc
@@ -26,7 +26,7 @@ namespace subresource_filter {
namespace {
void LogSecureInfo(AdDelayThrottle::SecureInfo info) {
- UMA_HISTOGRAM_ENUMERATION("SubresourceFilter.AdDelay.SecureInfo", info);
+ UMA_HISTOGRAM_ENUMERATION("Ads.Features.ResourceIsSecure", info);
}
class InsecureCondition : public AdDelayThrottle::DeferCondition {
@@ -65,7 +65,7 @@ class NonIsolatedCondition : public AdDelayThrottle::DeferCondition {
~NonIsolatedCondition() override {
if (provider()->IsAdRequest()) {
UMA_HISTOGRAM_ENUMERATION(
- "SubresourceFilter.AdDelay.IsolatedInfo",
+ "Ads.Features.AdResourceIsIsolated",
was_condition_ever_satisfied()
? AdDelayThrottle::IsolatedInfo::kNonIsolatedAd
: AdDelayThrottle::IsolatedInfo::kIsolatedAd);
@@ -109,18 +109,20 @@ base::TimeDelta AdDelayThrottle::DeferCondition::OnReadyToDefer() {
constexpr base::TimeDelta AdDelayThrottle::kDefaultDelay;
AdDelayThrottle::Factory::Factory()
- : insecure_delay_(base::TimeDelta::FromMilliseconds(
- base::GetFieldTrialParamByFeatureAsInt(
- kDelayUnsafeAds,
- kInsecureDelayParam,
- kDefaultDelay.InMilliseconds()))),
+ : delay_enabled_(base::FeatureList::IsEnabled(kAdTagging) &&
+ base::FeatureList::IsEnabled(kDelayUnsafeAds)),
+ insecure_delay_(base::TimeDelta::FromMilliseconds(
+ delay_enabled_ ? base::GetFieldTrialParamByFeatureAsInt(
+ kDelayUnsafeAds,
+ kInsecureDelayParam,
+ kDefaultDelay.InMilliseconds())
+ : 0)),
non_isolated_delay_(base::TimeDelta::FromMilliseconds(
- base::GetFieldTrialParamByFeatureAsInt(
- kDelayUnsafeAds,
- kNonIsolatedDelayParam,
- kDefaultDelay.InMilliseconds()))),
- delay_enabled_(base::FeatureList::IsEnabled(kAdTagging) &&
- base::FeatureList::IsEnabled(kDelayUnsafeAds)) {}
+ delay_enabled_ ? base::GetFieldTrialParamByFeatureAsInt(
+ kDelayUnsafeAds,
+ kNonIsolatedDelayParam,
+ kDefaultDelay.InMilliseconds())
+ : 0)) {}
AdDelayThrottle::Factory::~Factory() = default;
@@ -154,7 +156,8 @@ void AdDelayThrottle::WillStartRequest(network::ResourceRequest* request,
void AdDelayThrottle::WillRedirectRequest(
const net::RedirectInfo& redirect_info,
const network::ResourceResponseHead& response_head,
- bool* defer) {
+ bool* defer,
+ std::vector<std::string>* to_be_removed_headers) {
// Note: some MetadataProviders may not be able to distinguish requests that
// are only tagged as ads after a redirect.
*defer = MaybeDefer(redirect_info.new_url);
diff --git a/chromium/components/subresource_filter/content/common/ad_delay_throttle.h b/chromium/components/subresource_filter/content/common/ad_delay_throttle.h
index 3e6a6e999c5..9678fe38d29 100644
--- a/chromium/components/subresource_filter/content/common/ad_delay_throttle.h
+++ b/chromium/components/subresource_filter/content/common/ad_delay_throttle.h
@@ -56,9 +56,9 @@ class AdDelayThrottle : public content::URLLoaderThrottle {
bool delay_enabled() const { return delay_enabled_; }
private:
+ const bool delay_enabled_ = false;
const base::TimeDelta insecure_delay_;
const base::TimeDelta non_isolated_delay_;
- const bool delay_enabled_ = false;
DISALLOW_COPY_AND_ASSIGN(Factory);
};
@@ -138,9 +138,11 @@ class AdDelayThrottle : public content::URLLoaderThrottle {
void DetachFromCurrentSequence() override;
void WillStartRequest(network::ResourceRequest* request,
bool* defer) override;
- void WillRedirectRequest(const net::RedirectInfo& redirect_info,
- const network::ResourceResponseHead& response_head,
- bool* defer) override;
+ void WillRedirectRequest(
+ const net::RedirectInfo& redirect_info,
+ const network::ResourceResponseHead& response_head,
+ bool* defer,
+ std::vector<std::string>* to_be_removed_headers) override;
// Returns whether the request to |url| should be deferred.
bool MaybeDefer(const GURL& url);
diff --git a/chromium/components/subresource_filter/content/common/ad_delay_throttle_unittest.cc b/chromium/components/subresource_filter/content/common/ad_delay_throttle_unittest.cc
index 04d10e381a4..b2775d57c0f 100644
--- a/chromium/components/subresource_filter/content/common/ad_delay_throttle_unittest.cc
+++ b/chromium/components/subresource_filter/content/common/ad_delay_throttle_unittest.cc
@@ -13,7 +13,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/field_trial_params.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_tick_clock.h"
@@ -141,6 +141,10 @@ TEST_F(AdDelayThrottleTest, NoFeature_NoDelay) {
TEST_F(AdDelayThrottleTest, NoAdTagging_NoDelay) {
base::test::ScopedFeatureList scoped_disable;
scoped_disable.InitAndDisableFeature(kAdTagging);
+ base::test::ScopedFeatureList scoped_params;
+ scoped_params.InitAndEnableFeatureWithParameters(
+ kDelayUnsafeAds,
+ {{kInsecureDelayParam, "50"}, {kNonIsolatedDelayParam, "100"}});
AdDelayThrottle::Factory factory;
auto throttle = factory.MaybeCreate(std::make_unique<MockMetadataProvider>());
@@ -152,6 +156,8 @@ TEST_F(AdDelayThrottleTest, NoAdTagging_NoDelay) {
scoped_environment_.RunUntilIdle();
EXPECT_TRUE(client_->has_received_completion());
+ EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
+ base::FeatureList::GetFieldTrial(kDelayUnsafeAds)->trial_name()));
}
TEST_F(AdDelayThrottleTest, AdDelay) {
@@ -442,7 +448,7 @@ TEST_P(AdDelayThrottleEnabledParamTest, SecureMetrics) {
loader_factory_.AddResponse(insecure_url.spec(), "foo");
loader_factory_.AddResponse(secure_url.spec(), "foo");
- const char kSecureHistogram[] = "SubresourceFilter.AdDelay.SecureInfo";
+ const char kSecureHistogram[] = "Ads.Features.ResourceIsSecure";
{
base::HistogramTester histograms;
{
@@ -504,7 +510,7 @@ TEST_P(AdDelayThrottleEnabledParamTest, IsolatedMetrics) {
const GURL url("https://example.test/ad.js");
loader_factory_.AddResponse(url.spec(), "foo");
- const char kIsolatedHistogram[] = "SubresourceFilter.AdDelay.IsolatedInfo";
+ const char kIsolatedHistogram[] = "Ads.Features.AdResourceIsIsolated";
{
base::HistogramTester histograms;
{
diff --git a/chromium/components/subresource_filter/content/common/ruleset_dealer.cc b/chromium/components/subresource_filter/content/common/ruleset_dealer.cc
index 04a6fd75e04..5914def22b4 100644
--- a/chromium/components/subresource_filter/content/common/ruleset_dealer.cc
+++ b/chromium/components/subresource_filter/content/common/ruleset_dealer.cc
@@ -35,11 +35,11 @@ scoped_refptr<const MemoryMappedRuleset> RulesetDealer::GetRuleset() {
scoped_refptr<const MemoryMappedRuleset> strong_ruleset_ref;
if (weak_cached_ruleset_) {
strong_ruleset_ref = weak_cached_ruleset_.get();
- } else {
- MemoryMappedRuleset* ruleset =
- new MemoryMappedRuleset(ruleset_file_.Duplicate());
- strong_ruleset_ref = ruleset;
+ } else if (scoped_refptr<MemoryMappedRuleset> ruleset =
+ MemoryMappedRuleset::CreateAndInitialize(
+ ruleset_file_.Duplicate())) {
weak_cached_ruleset_ = ruleset->AsWeakPtr();
+ strong_ruleset_ref = std::move(ruleset);
}
return strong_ruleset_ref;
}
diff --git a/chromium/components/subresource_filter/content/common/ruleset_dealer_unittest.cc b/chromium/components/subresource_filter/content/common/ruleset_dealer_unittest.cc
index fcf7acd08d0..3acbf061b34 100644
--- a/chromium/components/subresource_filter/content/common/ruleset_dealer_unittest.cc
+++ b/chromium/components/subresource_filter/content/common/ruleset_dealer_unittest.cc
@@ -262,4 +262,26 @@ TEST_F(SubresourceFilterRulesetDealerTest,
ref_to_ruleset = nullptr;
}
+TEST_F(SubresourceFilterRulesetDealerTest, MmapFailure) {
+ ruleset_dealer()->SetRulesetFile(
+ testing::TestRuleset::Open(test_indexed_ruleset_1()));
+ {
+ scoped_refptr<const MemoryMappedRuleset> ref_to_ruleset =
+ ruleset_dealer()->GetRuleset();
+ EXPECT_TRUE(!!ref_to_ruleset);
+
+ // Simulate subsequent mmap failures
+ MemoryMappedRuleset::SetMemoryMapFailuresForTesting(true);
+
+ // Calls to GetRuleset should succeed as long as the strong ref
+ // is still around.
+ EXPECT_TRUE(ruleset_dealer()->has_cached_ruleset());
+ EXPECT_TRUE(!!ruleset_dealer()->GetRuleset());
+ }
+ EXPECT_FALSE(ruleset_dealer()->has_cached_ruleset());
+ EXPECT_FALSE(!!ruleset_dealer()->GetRuleset());
+ MemoryMappedRuleset::SetMemoryMapFailuresForTesting(false);
+ EXPECT_TRUE(!!ruleset_dealer()->GetRuleset());
+}
+
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/content/common/subresource_filter_utils.cc b/chromium/components/subresource_filter/content/common/subresource_filter_utils.cc
index a84ae2a2d6e..ae1cb5d75c9 100644
--- a/chromium/components/subresource_filter/content/common/subresource_filter_utils.cc
+++ b/chromium/components/subresource_filter/content/common/subresource_filter_utils.cc
@@ -4,23 +4,12 @@
#include "components/subresource_filter/content/common/subresource_filter_utils.h"
-#include "content/public/common/browser_side_navigation_policy.h"
-#include "content/public/common/url_constants.h"
#include "content/public/common/url_utils.h"
-#include "url/gurl.h"
namespace subresource_filter {
bool ShouldUseParentActivation(const GURL& url) {
- if (content::IsBrowserSideNavigationEnabled())
- return !content::IsURLHandledByNetworkStack(url);
-
- // TODO(csharrison): It is not always true that a data URL can use its
- // parent's activation in OOPIF mode, where the resulting data frame will
- // be same-process to its initiator. See crbug.com/739777 for more
- // information.
- return (url.SchemeIs(url::kDataScheme) || url == url::kAboutBlankURL ||
- url == content::kAboutSrcDocURL);
+ return !content::IsURLHandledByNetworkStack(url);
}
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/content/renderer/subresource_filter_agent.cc b/chromium/components/subresource_filter/content/renderer/subresource_filter_agent.cc
index ae9adb1c84e..cb562319ecd 100644
--- a/chromium/components/subresource_filter/content/renderer/subresource_filter_agent.cc
+++ b/chromium/components/subresource_filter/content/renderer/subresource_filter_agent.cc
@@ -216,10 +216,7 @@ void SubresourceFilterAgent::DidCommitProvisionalLoad(
scoped_refptr<const MemoryMappedRuleset> ruleset =
ruleset_dealer_->GetRuleset();
- DCHECK(ruleset);
- // Data can be null even if the original file is valid, if there is a
- // memory mapping issue.
- if (!ruleset->data())
+ if (!ruleset)
return;
base::OnceClosure first_disallowed_load_callback(base::BindOnce(
diff --git a/chromium/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc b/chromium/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc
index 2d456c67e87..9d26e10ec4a 100644
--- a/chromium/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc
+++ b/chromium/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc
@@ -10,11 +10,12 @@
#include "base/files/file.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "components/subresource_filter/content/common/subresource_filter_messages.h"
#include "components/subresource_filter/content/renderer/unverified_ruleset_dealer.h"
#include "components/subresource_filter/core/common/document_load_statistics.h"
+#include "components/subresource_filter/core/common/memory_mapped_ruleset.h"
#include "components/subresource_filter/core/common/scoped_timers.h"
#include "components/subresource_filter/core/common/test_ruleset_creator.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -241,6 +242,21 @@ TEST_F(SubresourceFilterAgentTest, DisabledByDefault_NoFilterIsInjected) {
histogram_tester.ExpectTotalCount(kEvaluationTotalCPUDuration, 0);
}
+TEST_F(SubresourceFilterAgentTest, MmapFailure_FailsToInjectSubresourceFilter) {
+ ASSERT_NO_FATAL_FAILURE(
+ SetTestRulesetToDisallowURLsWithPathSuffix(kTestFirstURLPathSuffix));
+ MemoryMappedRuleset::SetMemoryMapFailuresForTesting(true);
+ ExpectNoSubresourceFilterGetsInjected();
+ StartLoadAndSetActivationState(ActivationState(ActivationLevel::ENABLED),
+ false /* is_associated_with_ad_subframe */);
+ ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(agent()));
+
+ MemoryMappedRuleset::SetMemoryMapFailuresForTesting(false);
+ ExpectSubresourceFilterGetsInjected();
+ StartLoadAndSetActivationState(ActivationState(ActivationLevel::ENABLED),
+ false /* is_associated_with_ad_subframe */);
+}
+
TEST_F(SubresourceFilterAgentTest, Disabled_NoFilterIsInjected) {
ASSERT_NO_FATAL_FAILURE(
SetTestRulesetToDisallowURLsWithPathSuffix(kTestBothURLsPathSuffix));
diff --git a/chromium/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc b/chromium/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc
index 6124ac4cb34..4493f875215 100644
--- a/chromium/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc
+++ b/chromium/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc
@@ -173,9 +173,12 @@ std::unique_ptr<blink::WebDocumentSubresourceFilter>
WebDocumentSubresourceFilterImpl::BuilderImpl::Build() {
DCHECK(ruleset_file_.IsValid());
DCHECK(!main_task_runner_->BelongsToCurrentThread());
+ scoped_refptr<MemoryMappedRuleset> ruleset =
+ MemoryMappedRuleset::CreateAndInitialize(std::move(ruleset_file_));
+ if (!ruleset)
+ return nullptr;
return std::make_unique<WebDocumentSubresourceFilterImpl>(
- document_origin_, activation_state_,
- base::MakeRefCounted<MemoryMappedRuleset>(std::move(ruleset_file_)),
+ document_origin_, activation_state_, std::move(ruleset),
base::BindOnce(&ProxyToTaskRunner, main_task_runner_,
std::move(first_disallowed_load_callback_)),
is_associated_with_ad_subframe_);
diff --git a/chromium/components/subresource_filter/core/browser/copying_file_stream.cc b/chromium/components/subresource_filter/core/browser/copying_file_stream.cc
index 072cefcb41a..ef95c6e187c 100644
--- a/chromium/components/subresource_filter/core/browser/copying_file_stream.cc
+++ b/chromium/components/subresource_filter/core/browser/copying_file_stream.cc
@@ -4,7 +4,7 @@
#include "components/subresource_filter/core/browser/copying_file_stream.h"
-namespace url_pattern_index {
+namespace subresource_filter {
// CopyingFileInputStream ------------------------------------------------------
@@ -28,4 +28,4 @@ bool CopyingFileOutputStream::Write(const void* buffer, int size) {
size;
}
-} // namespace url_pattern_index
+} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/browser/copying_file_stream.h b/chromium/components/subresource_filter/core/browser/copying_file_stream.h
index 6df29e1ee3b..958e47570cd 100644
--- a/chromium/components/subresource_filter/core/browser/copying_file_stream.h
+++ b/chromium/components/subresource_filter/core/browser/copying_file_stream.h
@@ -9,7 +9,7 @@
#include "base/macros.h"
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
-namespace url_pattern_index {
+namespace subresource_filter {
// Implements a CopyingInputStream that reads from a base::File. Can be used in
// combination with CopyingInputStreamAdaptor for reading from that file through
@@ -46,6 +46,6 @@ class CopyingFileOutputStream
DISALLOW_COPY_AND_ASSIGN(CopyingFileOutputStream);
};
-} // namespace url_pattern_index
+} // namespace subresource_filter
#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_BROWSER_COPYING_FILE_STREAM_H_
diff --git a/chromium/components/subresource_filter/core/browser/ruleset_service.cc b/chromium/components/subresource_filter/core/browser/ruleset_service.cc
index bb7b59d20a4..408febf3b63 100644
--- a/chromium/components/subresource_filter/core/browser/ruleset_service.cc
+++ b/chromium/components/subresource_filter/core/browser/ruleset_service.cc
@@ -20,6 +20,8 @@
#include "base/strings/string_number_conversions.h"
#include "base/task_runner_util.h"
#include "base/threading/thread_restrictions.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/subresource_filter/core/browser/copying_file_stream.h"
@@ -136,6 +138,14 @@ void IndexedRulesetVersion::SaveToPrefs(PrefService* local_state) const {
content_version);
}
+std::unique_ptr<base::trace_event::TracedValue>
+IndexedRulesetVersion::ToTracedValue() const {
+ auto value = std::make_unique<base::trace_event::TracedValue>();
+ value->SetString("content_version", content_version);
+ value->SetInteger("format_version", format_version);
+ return value;
+}
+
// IndexedRulesetLocator ------------------------------------------------------
// static
@@ -227,6 +237,9 @@ RulesetService::RulesetService(
IndexedRulesetVersion most_recently_indexed_version;
most_recently_indexed_version.ReadFromPrefs(local_state_);
+ TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("loading"),
+ "RulesetService::RulesetService", "prefs_version",
+ most_recently_indexed_version.ToTracedValue());
if (most_recently_indexed_version.IsValid() &&
most_recently_indexed_version.IsCurrentFormatVersion()) {
OpenAndPublishRuleset(most_recently_indexed_version);
@@ -350,11 +363,10 @@ bool RulesetService::IndexRuleset(base::File unindexed_ruleset_file,
int64_t unindexed_ruleset_size = unindexed_ruleset_file.GetLength();
if (unindexed_ruleset_size < 0)
return false;
- url_pattern_index::CopyingFileInputStream copying_stream(
- std::move(unindexed_ruleset_file));
+ CopyingFileInputStream copying_stream(std::move(unindexed_ruleset_file));
google::protobuf::io::CopyingInputStreamAdaptor zero_copy_stream_adaptor(
&copying_stream, 4096 /* buffer_size */);
- url_pattern_index::UnindexedRulesetReader reader(&zero_copy_stream_adaptor);
+ UnindexedRulesetReader reader(&zero_copy_stream_adaptor);
size_t num_unsupported_rules = 0;
url_pattern_index::proto::FilteringRules ruleset_chunk;
diff --git a/chromium/components/subresource_filter/core/browser/ruleset_service.h b/chromium/components/subresource_filter/core/browser/ruleset_service.h
index 6f2b76ea1e8..ff7270ede0a 100644
--- a/chromium/components/subresource_filter/core/browser/ruleset_service.h
+++ b/chromium/components/subresource_filter/core/browser/ruleset_service.h
@@ -26,6 +26,10 @@ class PrefRegistrySimple;
namespace base {
class SequencedTaskRunner;
+
+namespace trace_event {
+class TracedValue;
+} // namespace trace_event
} // namespace base
namespace subresource_filter {
@@ -79,6 +83,8 @@ struct IndexedRulesetVersion {
void SaveToPrefs(PrefService* local_state) const;
void ReadFromPrefs(PrefService* local_state);
+ std::unique_ptr<base::trace_event::TracedValue> ToTracedValue() const;
+
std::string content_version;
int format_version = 0;
};
diff --git a/chromium/components/subresource_filter/core/browser/ruleset_service_unittest.cc b/chromium/components/subresource_filter/core/browser/ruleset_service_unittest.cc
index a9ba71fdb0f..3991ecd7923 100644
--- a/chromium/components/subresource_filter/core/browser/ruleset_service_unittest.cc
+++ b/chromium/components/subresource_filter/core/browser/ruleset_service_unittest.cc
@@ -23,7 +23,7 @@
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/task_runner_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/test_simple_task_runner.h"
#include "build/build_config.h"
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 92d0bbcdd77..d6989ab2d83 100644
--- a/chromium/components/subresource_filter/core/browser/subresource_filter_features.cc
+++ b/chromium/components/subresource_filter/core/browser/subresource_filter_features.cc
@@ -231,8 +231,8 @@ base::LazyInstance<scoped_refptr<ConfigurationList>>::Leaky
const base::Feature kSafeBrowsingSubresourceFilter{
"SubresourceFilter", base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature kSafeBrowsingSubresourceFilterExperimentalUI{
- "SubresourceFilterExperimentalUI", base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kSafeBrowsingSubresourceFilterConsiderRedirects{
+ "SubresourceFilterConsiderRedirects", base::FEATURE_DISABLED_BY_DEFAULT};
// Legacy name `activation_state` is used in variation parameters.
const char kActivationLevelParameterName[] = "activation_state";
@@ -286,16 +286,6 @@ Configuration Configuration::MakePresetForPerformanceTestingDryRunOnAllSites() {
}
// static
-Configuration Configuration::MakeForForcedActivation() {
- // This is a strange configuration, but it is generated on-the-fly rather than
- // via finch configs, and is separate from the standard activation computation
- // (which is why scope is no_sites).
- Configuration config(ActivationLevel::ENABLED, ActivationScope::NO_SITES);
- config.activation_conditions.forced_activation = true;
- return config;
-}
-
-// static
Configuration Configuration::MakePresetForLiveRunForBetterAds() {
Configuration config(ActivationLevel::ENABLED,
ActivationScope::ACTIVATION_LIST,
@@ -323,7 +313,6 @@ bool Configuration::operator==(const Configuration& rhs) const {
return std::tie(config.activation_conditions.activation_scope,
config.activation_conditions.activation_list,
config.activation_conditions.priority,
- config.activation_conditions.forced_activation,
config.activation_options.activation_level,
config.activation_options.performance_measurement_rate,
config.general_settings.ruleset_flavor);
@@ -341,7 +330,6 @@ Configuration::ActivationConditions::ToTracedValue() const {
value->SetString("activation_scope", StreamToString(activation_scope));
value->SetString("activation_list", StreamToString(activation_list));
value->SetInteger("priority", priority);
- value->SetBoolean("forced_activation", forced_activation);
return value;
}
@@ -369,11 +357,9 @@ ActivationState Configuration::GetActivationState(
(measurement_rate == 1 || base::RandDouble() < measurement_rate);
// This bit keeps track of BAS enforcement-style logging, not warning logging.
- state.enable_logging =
- effective_activation_level == ActivationLevel::ENABLED &&
- *this != Configuration::MakeForForcedActivation() &&
- base::FeatureList::IsEnabled(
- kSafeBrowsingSubresourceFilterExperimentalUI);
+ // TODO(csharrison): Consider removing it since it can be computed directly
+ // from the ActivationLevel.
+ state.enable_logging = effective_activation_level == ActivationLevel::ENABLED;
return state;
}
diff --git a/chromium/components/subresource_filter/core/browser/subresource_filter_features.h b/chromium/components/subresource_filter/core/browser/subresource_filter_features.h
index 82d22c3037a..9d33b9ca387 100644
--- a/chromium/components/subresource_filter/core/browser/subresource_filter_features.h
+++ b/chromium/components/subresource_filter/core/browser/subresource_filter_features.h
@@ -64,11 +64,6 @@ struct Configuration {
// otherwise satisfied. A greater value indicates higher priority.
int priority = 0;
- // This boolean is set to true for a navigation which has forced activation,
- // despite other conditions not matching. It should never be possible to set
- // this via variation params.
- bool forced_activation = false;
-
std::unique_ptr<base::trace_event::TracedValue> ToTracedValue() const;
};
@@ -129,10 +124,6 @@ struct Configuration {
static Configuration MakePresetForPerformanceTestingDryRunOnAllSites();
static Configuration MakePresetForLiveRunForBetterAds();
- // Not really a preset, but used as the configuration for forcing activation
- // (e.g. via devtools).
- static Configuration MakeForForcedActivation();
-
ActivationConditions activation_conditions;
ActivationOptions activation_options;
GeneralSettings general_settings;
@@ -191,8 +182,8 @@ scoped_refptr<ConfigurationList> GetAndSetActivateConfigurations(
// The master toggle to enable/disable the Safe Browsing Subresource Filter.
extern const base::Feature kSafeBrowsingSubresourceFilter;
-// Enables the new experimental UI for the Subresource Filter.
-extern const base::Feature kSafeBrowsingSubresourceFilterExperimentalUI;
+// Safe Browsing Activation Throttle considers all checks in a redirect chain.
+extern const base::Feature kSafeBrowsingSubresourceFilterConsiderRedirects;
// Name/values of the variation parameter controlling maximum activation level.
extern const char kActivationLevelParameterName[];
diff --git a/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.cc b/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.cc
index 59352bcc60c..ec3f292a0c8 100644
--- a/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.cc
+++ b/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.cc
@@ -4,7 +4,9 @@
#include "components/subresource_filter/core/browser/subresource_filter_features_test_support.h"
+#include <memory>
#include <ostream>
+#include <string>
#include <utility>
#include "base/json/json_writer.h"
@@ -52,50 +54,5 @@ void ScopedSubresourceFilterConfigurator::ResetConfiguration(
base::MakeRefCounted<ConfigurationList>(std::move(config)));
}
-// ScopedSubresourceFilterFeatureToggle ---------------------------------------
-
-ScopedSubresourceFilterFeatureToggle::ScopedSubresourceFilterFeatureToggle() {}
-ScopedSubresourceFilterFeatureToggle::ScopedSubresourceFilterFeatureToggle(
- base::FeatureList::OverrideState feature_state,
- const std::string& additional_features_to_enable) {
- ResetSubresourceFilterState(feature_state, additional_features_to_enable);
-}
-
-void ScopedSubresourceFilterFeatureToggle::ResetSubresourceFilterState(
- base::FeatureList::OverrideState feature_state,
- const std::string& additional_features_to_enable) {
- std::string enabled_features;
- std::string disabled_features;
-
- if (feature_state == base::FeatureList::OVERRIDE_ENABLE_FEATURE) {
- enabled_features = kSafeBrowsingSubresourceFilter.name;
- } else if (feature_state == base::FeatureList::OVERRIDE_DISABLE_FEATURE) {
- disabled_features = kSafeBrowsingSubresourceFilter.name;
- }
-
- if (!additional_features_to_enable.empty()) {
- if (!enabled_features.empty())
- enabled_features += ',';
- enabled_features += additional_features_to_enable;
- }
-
- scoped_configuration_.ResetConfiguration();
- scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
- scoped_feature_list_->InitFromCommandLine(enabled_features,
- disabled_features);
-}
-
-ScopedSubresourceFilterFeatureToggle::~ScopedSubresourceFilterFeatureToggle() {}
-
-std::ostream& operator<<(std::ostream& os, const Configuration& config) {
- std::unique_ptr<base::Value> value = config.ToTracedValue()->ToBaseValue();
- base::DictionaryValue* dict;
- value->GetAsDictionary(&dict);
- std::string json;
- base::JSONWriter::WriteWithOptions(
- *dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
- return os << json;
-}
-
} // namespace testing
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.h b/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.h
index 91ad51698fe..b8da0becd6c 100644
--- a/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.h
+++ b/chromium/components/subresource_filter/core/browser/subresource_filter_features_test_support.h
@@ -6,12 +6,9 @@
#define COMPONENTS_SUBRESOURCE_FILTER_CORE_BROWSER_SUBRESOURCE_FILTER_FEATURES_TEST_SUPPORT_H_
#include <iosfwd>
-#include <memory>
-#include <string>
+#include <vector>
-#include "base/feature_list.h"
#include "base/macros.h"
-#include "base/test/scoped_feature_list.h"
#include "components/subresource_filter/core/browser/subresource_filter_features.h"
namespace subresource_filter {
@@ -31,48 +28,18 @@ class ScopedSubresourceFilterConfigurator {
std::vector<Configuration> configs);
~ScopedSubresourceFilterConfigurator();
- void ResetConfiguration(
- scoped_refptr<ConfigurationList> config_list = nullptr);
void ResetConfiguration(Configuration config);
void ResetConfiguration(std::vector<Configuration> config);
private:
+ void ResetConfiguration(
+ scoped_refptr<ConfigurationList> config_list = nullptr);
+
scoped_refptr<ConfigurationList> original_config_;
DISALLOW_COPY_AND_ASSIGN(ScopedSubresourceFilterConfigurator);
};
-// Helper class to override the state of the |kSafeBrowsingSubresourceFilter|
-// feature.
-//
-// Clears the active subresource filtering configuration override upon
-// construction, if any, and restores it on destruction. So while the instance
-// is in scope, calls to GetEnabledConfigurations() will default to returning
-// the hard-coded configuration corresponding to the forced feature state. Tests
-// that need to toggle both the feature and override the active configuration
-// should therefore do so in that order.
-class ScopedSubresourceFilterFeatureToggle {
- public:
- ScopedSubresourceFilterFeatureToggle();
- explicit ScopedSubresourceFilterFeatureToggle(
- base::FeatureList::OverrideState feature_state,
- const std::string& additional_features_to_enable = std::string());
- ~ScopedSubresourceFilterFeatureToggle();
-
- void ResetSubresourceFilterState(
- base::FeatureList::OverrideState feature_state,
- const std::string& additional_features_to_enable = std::string());
-
- private:
- ScopedSubresourceFilterConfigurator scoped_configuration_;
- std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedSubresourceFilterFeatureToggle);
-};
-
-// For logging in tests.
-std::ostream& operator<<(std::ostream& os, const Configuration& config);
-
} // namespace testing
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc b/chromium/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc
index 0b8ed4fa05f..e86b964b016 100644
--- a/chromium/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc
+++ b/chromium/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc
@@ -16,6 +16,7 @@
#include "base/metrics/field_trial_params.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
+#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "components/subresource_filter/core/browser/subresource_filter_features_test_support.h"
#include "components/subresource_filter/core/common/common_features.h"
@@ -537,7 +538,6 @@ TEST_F(SubresourceFilterFeaturesTest, PresetForLiveRunOnBetterAdsSites) {
EXPECT_EQ(ActivationScope::ACTIVATION_LIST,
config.activation_conditions.activation_scope);
EXPECT_EQ(800, config.activation_conditions.priority);
- EXPECT_FALSE(config.activation_conditions.forced_activation);
EXPECT_EQ(ActivationLevel::ENABLED,
config.activation_options.activation_level);
EXPECT_EQ(0.0, config.activation_options.performance_measurement_rate);
@@ -650,23 +650,6 @@ TEST_F(SubresourceFilterFeaturesTest,
config_list->lexicographically_greatest_ruleset_flavor());
}
-TEST_F(SubresourceFilterFeaturesTest, ForcedActivation_NotConfigurable) {
- ScopedExperimentalStateToggle scoped_experimental_state(
- base::FeatureList::OVERRIDE_ENABLE_FEATURE,
- {{kActivationLevelParameterName, kActivationLevelEnabled},
- {kActivationScopeParameterName, kActivationScopeNoSites},
- {"forced_activation", "true"}});
-
- Configuration actual_configuration;
- ExpectAndRetrieveExactlyOneExtraEnabledConfig(&actual_configuration);
- EXPECT_EQ(ActivationLevel::ENABLED,
- actual_configuration.activation_options.activation_level);
- EXPECT_EQ(ActivationScope::NO_SITES,
- actual_configuration.activation_conditions.activation_scope);
-
- EXPECT_FALSE(actual_configuration.activation_conditions.forced_activation);
-}
-
TEST_F(SubresourceFilterFeaturesTest, AdTagging_EnablesDryRun) {
const Configuration dryrun =
Configuration::MakePresetForPerformanceTestingDryRunOnAllSites();
diff --git a/chromium/components/subresource_filter/core/common/BUILD.gn b/chromium/components/subresource_filter/core/common/BUILD.gn
index c56add3dfed..046c009ff96 100644
--- a/chromium/components/subresource_filter/core/common/BUILD.gn
+++ b/chromium/components/subresource_filter/core/common/BUILD.gn
@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//testing/libfuzzer/fuzzer_test.gni")
+
static_library("common") {
sources = [
"activation_decision.h",
@@ -82,3 +84,15 @@ source_set("unit_tests") {
"//url",
]
}
+
+fuzzer_test("indexed_ruleset_fuzzer") {
+ sources = [
+ "indexed_ruleset_fuzzer.cc",
+ ]
+ deps = [
+ ":common",
+ "//base",
+ "//base/test:test_support",
+ "//url",
+ ]
+}
diff --git a/chromium/components/subresource_filter/core/common/activation_decision.h b/chromium/components/subresource_filter/core/common/activation_decision.h
index 43d06711152..557b08483d4 100644
--- a/chromium/components/subresource_filter/core/common/activation_decision.h
+++ b/chromium/components/subresource_filter/core/common/activation_decision.h
@@ -27,8 +27,11 @@ enum class ActivationDecision : int {
// activation conditions of any of enabled configurations.
ACTIVATION_CONDITIONS_NOT_MET = 5,
+ // Activation was forced on the client (e.g. via devtools).
+ FORCED_ACTIVATION = 6,
+
// Max value for enum.
- ACTIVATION_DECISION_MAX = 6
+ ACTIVATION_DECISION_MAX = 7
};
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/document_subresource_filter.cc b/chromium/components/subresource_filter/core/common/document_subresource_filter.cc
index 07bd2ae1ae5..9b0bf7018cc 100644
--- a/chromium/components/subresource_filter/core/common/document_subresource_filter.cc
+++ b/chromium/components/subresource_filter/core/common/document_subresource_filter.cc
@@ -34,7 +34,8 @@ DocumentSubresourceFilter::~DocumentSubresourceFilter() = default;
LoadPolicy DocumentSubresourceFilter::GetLoadPolicy(
const GURL& subresource_url,
url_pattern_index::proto::ElementType subresource_type) {
- TRACE_EVENT1("loader", "DocumentSubresourceFilter::GetLoadPolicy", "url",
+ TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("loading"),
+ "DocumentSubresourceFilter::GetLoadPolicy", "url",
subresource_url.spec());
++statistics_.num_loads_total;
diff --git a/chromium/components/subresource_filter/core/common/document_subresource_filter_unittest.cc b/chromium/components/subresource_filter/core/common/document_subresource_filter_unittest.cc
index 4e9c3759af4..f893339fb84 100644
--- a/chromium/components/subresource_filter/core/common/document_subresource_filter_unittest.cc
+++ b/chromium/components/subresource_filter/core/common/document_subresource_filter_unittest.cc
@@ -52,7 +52,7 @@ class DocumentSubresourceFilterTest : public ::testing::Test {
ASSERT_NO_FATAL_FAILURE(
test_ruleset_creator_.CreateRulesetToDisallowURLsWithPathSuffix(
suffix, &test_ruleset_pair));
- ruleset_ = new MemoryMappedRuleset(
+ ruleset_ = MemoryMappedRuleset::CreateAndInitialize(
testing::TestRuleset::Open(test_ruleset_pair.indexed));
}
diff --git a/chromium/components/subresource_filter/core/common/indexed_ruleset.cc b/chromium/components/subresource_filter/core/common/indexed_ruleset.cc
index c036bab1067..c1493cd2998 100644
--- a/chromium/components/subresource_filter/core/common/indexed_ruleset.cc
+++ b/chromium/components/subresource_filter/core/common/indexed_ruleset.cc
@@ -66,12 +66,15 @@ void RulesetIndexer::Finish() {
// static
bool IndexedRulesetMatcher::Verify(const uint8_t* buffer, size_t size) {
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"),
- "IndexedRulesetMatcher::Verify");
+ TRACE_EVENT_BEGIN1(TRACE_DISABLED_BY_DEFAULT("loading"),
+ "IndexedRulesetMatcher::Verify", "size", size);
SCOPED_UMA_HISTOGRAM_TIMER(
"SubresourceFilter.IndexRuleset.Verify2.WallDuration");
flatbuffers::Verifier verifier(buffer, size);
- return flat::VerifyIndexedRulesetBuffer(verifier);
+ bool valid = flat::VerifyIndexedRulesetBuffer(verifier);
+ TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("loading"),
+ "IndexedRulesetMatcher::Verify", "valid", valid);
+ return valid;
}
IndexedRulesetMatcher::IndexedRulesetMatcher(const uint8_t* buffer, size_t size)
diff --git a/chromium/components/subresource_filter/core/common/indexed_ruleset_fuzzer.cc b/chromium/components/subresource_filter/core/common/indexed_ruleset_fuzzer.cc
new file mode 100644
index 00000000000..3200de867d5
--- /dev/null
+++ b/chromium/components/subresource_filter/core/common/indexed_ruleset_fuzzer.cc
@@ -0,0 +1,77 @@
+// 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/subresource_filter/core/common/indexed_ruleset.h"
+
+#include <string>
+
+#include "base/at_exit.h"
+#include "base/i18n/icu_util.h"
+#include "base/strings/string_piece.h"
+#include "base/test/fuzzed_data_provider.h"
+#include "components/subresource_filter/core/common/first_party_origin.h"
+#include "components/subresource_filter/core/common/unindexed_ruleset.h"
+#include "components/url_pattern_index/url_pattern_index.h"
+#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+struct TestCase {
+ TestCase() { CHECK(base::i18n::InitializeICU()); }
+ base::AtExitManager at_exit_manager;
+};
+
+TestCase* test_case = new TestCase();
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ base::FuzzedDataProvider fuzzed_data(data, size);
+
+ // Split the input into two sections, the URL to check, and the ruleset
+ // itself.
+ std::string url_string = fuzzed_data.ConsumeRandomLengthString(1024);
+ const GURL url_to_check = GURL(url_string);
+
+ // Error out early if the URL isn't valid, since we early-return in the
+ // IndexedRulesetMatcher pretty early if this is the case.
+ if (!url_to_check.is_valid())
+ return 0;
+
+ const std::string remaining_bytes = fuzzed_data.ConsumeRemainingBytes();
+
+ // First, interpret the remaining fuzzed data as an unindexed ruleset.
+ google::protobuf::io::ArrayInputStream input_stream(remaining_bytes.data(),
+ remaining_bytes.size());
+ subresource_filter::UnindexedRulesetReader reader(&input_stream);
+
+ // Use the unindexed ruleset to build a flat indexed ruleset.
+ subresource_filter::RulesetIndexer indexer;
+ url_pattern_index::proto::FilteringRules ruleset_chunk;
+ while (reader.ReadNextChunk(&ruleset_chunk)) {
+ for (const auto& rule : ruleset_chunk.url_rules())
+ indexer.AddUrlRule(rule);
+ }
+ indexer.Finish();
+
+ // Error out if we were unable to fully read the unindexed version.
+ if (reader.num_bytes_read() != static_cast<int64_t>(remaining_bytes.size()))
+ return 0;
+
+ CHECK(subresource_filter::IndexedRulesetMatcher::Verify(indexer.data(),
+ indexer.size()));
+
+ // Lastly, read into the indexed ruleset by matching the URL from the
+ // beginning of the fuzzed data.
+ subresource_filter::IndexedRulesetMatcher matcher(indexer.data(),
+ indexer.size());
+ // TODO(csharrison): Consider fuzzing things like the parent origin, the
+ // activation type, and the element type.
+ matcher.ShouldDisableFilteringForDocument(
+ url_to_check, url::Origin(),
+ url_pattern_index::proto::ACTIVATION_TYPE_DOCUMENT);
+ matcher.ShouldDisallowResourceLoad(
+ url_to_check, subresource_filter::FirstPartyOrigin(url::Origin()),
+ url_pattern_index::proto::ELEMENT_TYPE_SCRIPT,
+ false /* disable_generic_rules */);
+ return 0;
+}
diff --git a/chromium/components/subresource_filter/core/common/memory_mapped_ruleset.cc b/chromium/components/subresource_filter/core/common/memory_mapped_ruleset.cc
index 8e479f18e2e..3ecbbe731f0 100644
--- a/chromium/components/subresource_filter/core/common/memory_mapped_ruleset.cc
+++ b/chromium/components/subresource_filter/core/common/memory_mapped_ruleset.cc
@@ -6,12 +6,33 @@
#include <utility>
+#include "base/logging.h"
+
namespace subresource_filter {
-MemoryMappedRuleset::MemoryMappedRuleset(base::File ruleset_file) {
- ruleset_.Initialize(std::move(ruleset_file));
+// Used for tests which want to simulate mmap failures.
+static bool g_fail_memory_map_initialization_for_testing = false;
+
+// static
+scoped_refptr<MemoryMappedRuleset> MemoryMappedRuleset::CreateAndInitialize(
+ base::File ruleset_file) {
+ if (g_fail_memory_map_initialization_for_testing)
+ return nullptr;
+
+ auto ruleset = base::AdoptRef(new MemoryMappedRuleset());
+ if (g_fail_memory_map_initialization_for_testing ||
+ !ruleset->ruleset_.Initialize(std::move(ruleset_file)))
+ return nullptr;
+ DCHECK(ruleset->ruleset_.IsValid());
+ return ruleset;
+}
+
+// static
+void MemoryMappedRuleset::SetMemoryMapFailuresForTesting(bool fail) {
+ g_fail_memory_map_initialization_for_testing = fail;
}
-MemoryMappedRuleset::~MemoryMappedRuleset() {}
+MemoryMappedRuleset::MemoryMappedRuleset() = default;
+MemoryMappedRuleset::~MemoryMappedRuleset() = default;
} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/memory_mapped_ruleset.h b/chromium/components/subresource_filter/core/common/memory_mapped_ruleset.h
index 83f477100a2..d148c043bba 100644
--- a/chromium/components/subresource_filter/core/common/memory_mapped_ruleset.h
+++ b/chromium/components/subresource_filter/core/common/memory_mapped_ruleset.h
@@ -23,13 +23,18 @@ namespace subresource_filter {
class MemoryMappedRuleset : public base::RefCounted<MemoryMappedRuleset>,
public base::SupportsWeakPtr<MemoryMappedRuleset> {
public:
- explicit MemoryMappedRuleset(base::File ruleset_file);
+ REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
+ static scoped_refptr<MemoryMappedRuleset> CreateAndInitialize(
+ base::File ruleset_file);
+
+ static void SetMemoryMapFailuresForTesting(bool fail);
const uint8_t* data() const { return ruleset_.data(); }
size_t length() const { return base::strict_cast<size_t>(ruleset_.length()); }
private:
friend class base::RefCounted<MemoryMappedRuleset>;
+ MemoryMappedRuleset();
~MemoryMappedRuleset();
base::MemoryMappedFile ruleset_;
diff --git a/chromium/components/subresource_filter/core/common/perftests/indexed_ruleset_perftest.cc b/chromium/components/subresource_filter/core/common/perftests/indexed_ruleset_perftest.cc
index de5081ab6be..92f284842b1 100644
--- a/chromium/components/subresource_filter/core/common/perftests/indexed_ruleset_perftest.cc
+++ b/chromium/components/subresource_filter/core/common/perftests/indexed_ruleset_perftest.cc
@@ -58,9 +58,8 @@ class IndexedRulesetPerftest : public testing::Test {
base::File indexed_file =
base::File(indexed_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_TRUE(indexed_file.IsValid());
- auto ruleset =
- base::MakeRefCounted<subresource_filter::MemoryMappedRuleset>(
- std::move(indexed_file));
+ auto ruleset = subresource_filter::MemoryMappedRuleset::CreateAndInitialize(
+ std::move(indexed_file));
filter_tool_ = std::make_unique<FilterTool>(std::move(ruleset), &output_);
}
diff --git a/chromium/components/subresource_filter/core/common/scoped_timers_unittest.cc b/chromium/components/subresource_filter/core/common/scoped_timers_unittest.cc
index 4a10af758ed..29700debecf 100644
--- a/chromium/components/subresource_filter/core/common/scoped_timers_unittest.cc
+++ b/chromium/components/subresource_filter/core/common/scoped_timers_unittest.cc
@@ -4,7 +4,7 @@
#include "components/subresource_filter/core/common/scoped_timers.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "components/subresource_filter/core/common/time_measurements.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/components/subresource_filter/core/common/test_ruleset_creator.cc b/chromium/components/subresource_filter/core/common/test_ruleset_creator.cc
index 96ffa23ad27..1785b8d245a 100644
--- a/chromium/components/subresource_filter/core/common/test_ruleset_creator.cc
+++ b/chromium/components/subresource_filter/core/common/test_ruleset_creator.cc
@@ -41,7 +41,7 @@ std::vector<uint8_t> SerializeUnindexedRulesetWithMultipleRules(
const std::vector<proto::UrlRule>& rules) {
std::string ruleset_contents;
google::protobuf::io::StringOutputStream output(&ruleset_contents);
- url_pattern_index::UnindexedRulesetWriter ruleset_writer(&output);
+ UnindexedRulesetWriter ruleset_writer(&output);
for (const auto& rule : rules)
ruleset_writer.AddUrlRule(rule);
ruleset_writer.Finish();
diff --git a/chromium/components/subresource_filter/core/common/unindexed_ruleset.cc b/chromium/components/subresource_filter/core/common/unindexed_ruleset.cc
index 5c0d1530476..1ff52eb13b3 100644
--- a/chromium/components/subresource_filter/core/common/unindexed_ruleset.cc
+++ b/chromium/components/subresource_filter/core/common/unindexed_ruleset.cc
@@ -7,7 +7,9 @@
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
-namespace url_pattern_index {
+namespace subresource_filter {
+
+namespace proto = url_pattern_index::proto;
// UnindexedRulesetReader ------------------------------------------------------
@@ -68,4 +70,4 @@ bool UnindexedRulesetWriter::WritePendingChunk() {
return !had_error() && chunk.SerializeToCodedStream(&coded_stream_);
}
-} // namespace url_pattern_index
+} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/core/common/unindexed_ruleset.h b/chromium/components/subresource_filter/core/common/unindexed_ruleset.h
index a0f112c6c7f..4045301beef 100644
--- a/chromium/components/subresource_filter/core/common/unindexed_ruleset.h
+++ b/chromium/components/subresource_filter/core/common/unindexed_ruleset.h
@@ -3,16 +3,17 @@
// found in the LICENSE file.
// Semantically speaking, an unindexed ruleset consists of a single
-// proto::FilteringRules message. However, because the ruleset can be relatively
-// large, we want to avoid deserializing all of it at once, as doing so can lead
-// to memory allocation bursts.
+// url_pattern_index::proto::FilteringRules message. However, because the
+// ruleset can be relatively large, we want to avoid deserializing all of it at
+// once, as doing so can lead to memory allocation bursts.
//
// To work around the limitation that partial (or streaming) deserialization is
// not supported by the proto parser, the UnindexedRulesetReader/Writer classes
// implement a format where the ruleset is split into several chunks. Each chunk
-// is itself a proto::FilteringRules message. If all chunks were merged, they
-// would add up to the original proto::FilteringRules message representing the
-// entire ruleset.
+// is itself a url_pattern_index::proto::FilteringRules message. If all chunks
+// were merged, they would add up to the original
+// url_pattern_index::proto::FilteringRules message representing the entire
+// ruleset.
//
// Consumers of an unindexed ruleset in this format will be able to read it one
// chunk at a time, and are expected to fully process and discard the chunk
@@ -27,7 +28,7 @@
#include "third_party/protobuf/src/google/protobuf/io/coded_stream.h"
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h"
-namespace url_pattern_index {
+namespace subresource_filter {
// Reads an unindexed ruleset from |stream| one chunk at a time.
class UnindexedRulesetReader {
@@ -40,7 +41,7 @@ class UnindexedRulesetReader {
// Reads the next ruleset |chunk| from the |input|. Returns false iff reached
// the end of the stream or an error occurred. Once returned false, calling
// ReadNextChunk is undefined befaviour.
- bool ReadNextChunk(proto::FilteringRules* chunk);
+ bool ReadNextChunk(url_pattern_index::proto::FilteringRules* chunk);
// Returns how many bytes of the |stream| have been consumed.
int num_bytes_read() const { return coded_stream_.CurrentPosition(); }
@@ -58,8 +59,9 @@ class UnindexedRulesetReader {
// and write operations should not be used further.
class UnindexedRulesetWriter {
public:
- // Creates an instance that will write proto::FilteringRules chunks to the
- // |stream|, with each chunk containing up to |max_rules_per_chunk| rules.
+ // Creates an instance that will write
+ // url_pattern_index::proto::FilteringRules chunks to the |stream|, with each
+ // chunk containing up to |max_rules_per_chunk| rules.
explicit UnindexedRulesetWriter(
google::protobuf::io::ZeroCopyOutputStream* stream,
int max_rules_per_chunk = 64);
@@ -72,7 +74,7 @@ class UnindexedRulesetWriter {
// Places the |rule| to the current chunk, and serializes the chunk if it has
// grown up to |max_rules_per_chunk|.
- bool AddUrlRule(const proto::UrlRule& rule);
+ bool AddUrlRule(const url_pattern_index::proto::UrlRule& rule);
// TODO(pkalinnikov): Implement AddCssRule when needed.
// Finalizes the serialization of the unindexed ruleset, i.e., writes the
@@ -90,11 +92,11 @@ class UnindexedRulesetWriter {
google::protobuf::io::CodedOutputStream coded_stream_;
const int max_rules_per_chunk_ = 0;
- proto::FilteringRules pending_chunk_;
+ url_pattern_index::proto::FilteringRules pending_chunk_;
DISALLOW_COPY_AND_ASSIGN(UnindexedRulesetWriter);
};
-} // namespace url_pattern_index
+} // namespace subresource_filter
#endif // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_UNINDEXED_RULESET_H_
diff --git a/chromium/components/subresource_filter/core/common/unindexed_ruleset_unittest.cc b/chromium/components/subresource_filter/core/common/unindexed_ruleset_unittest.cc
index ca4a57afdd2..2fefde299b8 100644
--- a/chromium/components/subresource_filter/core/common/unindexed_ruleset_unittest.cc
+++ b/chromium/components/subresource_filter/core/common/unindexed_ruleset_unittest.cc
@@ -17,9 +17,12 @@
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h"
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
-namespace url_pattern_index {
+namespace subresource_filter {
namespace {
+namespace proto = url_pattern_index::proto;
+namespace testing = url_pattern_index::testing;
+using url_pattern_index::UrlPattern;
bool IsEqual(const proto::UrlRule& lhs, const proto::UrlRule& rhs) {
return lhs.SerializeAsString() == rhs.SerializeAsString();
@@ -178,4 +181,4 @@ TEST(UnindexedRulesetTest, ReadCorruptedInput) {
}
}
-} // namespace url_pattern_index
+} // namespace subresource_filter
diff --git a/chromium/components/subresource_filter/tools/BUILD.gn b/chromium/components/subresource_filter/tools/BUILD.gn
index 557e6b29385..10e22ea0736 100644
--- a/chromium/components/subresource_filter/tools/BUILD.gn
+++ b/chromium/components/subresource_filter/tools/BUILD.gn
@@ -52,7 +52,6 @@ if (!is_ios) {
deps = [
":tools_lib",
"//base",
- "//build/config:exe_and_shlib_deps",
]
}
@@ -65,7 +64,6 @@ if (!is_ios) {
deps = [
":tools_lib",
"//base",
- "//build/config:exe_and_shlib_deps",
]
}
@@ -76,7 +74,6 @@ if (!is_ios) {
deps = [
"ruleset_converter:support",
"//base",
- "//build/config:exe_and_shlib_deps",
"//third_party/protobuf:protobuf_lite",
]
}
@@ -103,18 +100,14 @@ if (!is_ios) {
]
inputs = [
- # Make sure the inputs are system-absolute, as base::File cannot open files with ".." components.
- rebase_path(
- "//components/subresource_filter/core/common/perftests/data/UnindexedRules_7.54",
- "",
- "/"),
+ "//components/subresource_filter/core/common/perftests/data/UnindexedRules_7.54",
]
deps = [
":subresource_indexing_tool",
]
args = [
- inputs[0],
+ rebase_path(inputs[0], root_build_dir),
rebase_path("$target_gen_dir/GeneratedRulesetData", root_build_dir),
]
}
diff --git a/chromium/components/subresource_filter/tools/filter_tool_main.cc b/chromium/components/subresource_filter/tools/filter_tool_main.cc
index b5a93665ae8..3563671ed3d 100644
--- a/chromium/components/subresource_filter/tools/filter_tool_main.cc
+++ b/chromium/components/subresource_filter/tools/filter_tool_main.cc
@@ -103,8 +103,9 @@ int main(int argc, char* argv[]) {
return 1;
}
- auto ruleset = base::MakeRefCounted<subresource_filter::MemoryMappedRuleset>(
+ auto ruleset = subresource_filter::MemoryMappedRuleset::CreateAndInitialize(
std::move(rules_file));
+ LOG_IF(FATAL, ruleset == nullptr) << "mmap failure";
LOG_IF(FATAL, ruleset->length() == 0u) << "Empty ruleset file";
diff --git a/chromium/components/subresource_filter/tools/filter_tool_unittest.cc b/chromium/components/subresource_filter/tools/filter_tool_unittest.cc
index f210b819276..85913d38a45 100644
--- a/chromium/components/subresource_filter/tools/filter_tool_unittest.cc
+++ b/chromium/components/subresource_filter/tools/filter_tool_unittest.cc
@@ -76,7 +76,7 @@ class FilterToolTest : public ::testing::Test {
ASSERT_NO_FATAL_FAILURE(test_ruleset_creator_.CreateRulesetWithRules(
rules, &test_ruleset_pair_));
- ruleset_ = new MemoryMappedRuleset(
+ ruleset_ = MemoryMappedRuleset::CreateAndInitialize(
testing::TestRuleset::Open(test_ruleset_pair_.indexed));
}
diff --git a/chromium/components/subresource_filter/tools/indexing_tool.cc b/chromium/components/subresource_filter/tools/indexing_tool.cc
index bcffeb2abad..70552b43049 100644
--- a/chromium/components/subresource_filter/tools/indexing_tool.cc
+++ b/chromium/components/subresource_filter/tools/indexing_tool.cc
@@ -23,16 +23,15 @@ bool IndexAndWriteRuleset(const base::FilePath& unindexed_path,
return false;
}
- base::File unindexed_file(unindexed_path,
+ base::File unindexed_file(base::MakeAbsoluteFilePath(unindexed_path),
base::File::FLAG_OPEN | base::File::FLAG_READ);
subresource_filter::RulesetIndexer indexer;
- url_pattern_index::CopyingFileInputStream copying_stream(
- std::move(unindexed_file));
+ CopyingFileInputStream copying_stream(std::move(unindexed_file));
google::protobuf::io::CopyingInputStreamAdaptor zero_copy_stream_adaptor(
&copying_stream, 4096 /* buffer_size */);
- url_pattern_index::UnindexedRulesetReader reader(&zero_copy_stream_adaptor);
+ UnindexedRulesetReader reader(&zero_copy_stream_adaptor);
url_pattern_index::proto::FilteringRules ruleset_chunk;
diff --git a/chromium/components/subresource_filter/tools/ruleset_converter/rule_stream.cc b/chromium/components/subresource_filter/tools/ruleset_converter/rule_stream.cc
index 86214195ed5..0b721da0d98 100644
--- a/chromium/components/subresource_filter/tools/ruleset_converter/rule_stream.cc
+++ b/chromium/components/subresource_filter/tools/ruleset_converter/rule_stream.cc
@@ -211,8 +211,7 @@ class UnindexedRulesetRuleInputStream : public RuleInputStream {
ruleset_ = ReadStreamToString(input.get());
ruleset_input_.reset(new google::protobuf::io::ArrayInputStream(
ruleset_.data(), ruleset_.size()));
- ruleset_reader_.reset(
- new url_pattern_index::UnindexedRulesetReader(ruleset_input_.get()));
+ ruleset_reader_.reset(new UnindexedRulesetReader(ruleset_input_.get()));
}
url_pattern_index::proto::RuleType FetchNextRule() override {
@@ -249,7 +248,7 @@ class UnindexedRulesetRuleInputStream : public RuleInputStream {
std::string ruleset_;
std::unique_ptr<google::protobuf::io::ArrayInputStream> ruleset_input_;
- std::unique_ptr<url_pattern_index::UnindexedRulesetReader> ruleset_reader_;
+ std::unique_ptr<UnindexedRulesetReader> ruleset_reader_;
url_pattern_index::proto::FilteringRules rules_chunk_;
std::unique_ptr<ProtobufRuleInputStreamImpl> impl_;
@@ -287,7 +286,7 @@ class UnindexedRulesetRuleOutputStream : public RuleOutputStream {
std::string ruleset_;
google::protobuf::io::StringOutputStream ruleset_output_;
- url_pattern_index::UnindexedRulesetWriter ruleset_writer_;
+ UnindexedRulesetWriter ruleset_writer_;
std::unique_ptr<std::ostream> output_;
};
diff --git a/chromium/components/suggestions/blacklist_store_unittest.cc b/chromium/components/suggestions/blacklist_store_unittest.cc
index bb10e7d22db..850d1b6440e 100644
--- a/chromium/components/suggestions/blacklist_store_unittest.cc
+++ b/chromium/components/suggestions/blacklist_store_unittest.cc
@@ -9,7 +9,7 @@
#include <string>
#include "base/macros.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/suggestions/proto/suggestions.pb.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/components/suggestions/suggestions_service_impl.cc b/chromium/components/suggestions/suggestions_service_impl.cc
index e55b28d8ff3..b76a8e43a79 100644
--- a/chromium/components/suggestions/suggestions_service_impl.cc
+++ b/chromium/components/suggestions/suggestions_service_impl.cc
@@ -37,6 +37,7 @@
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_request_status.h"
#include "services/identity/public/cpp/identity_manager.h"
+#include "services/identity/public/cpp/primary_account_access_token_fetcher.h"
using base::TimeDelta;
@@ -377,8 +378,8 @@ void SuggestionsServiceImpl::IssueRequestIfNoneOngoing(const GURL& url) {
return;
OAuth2TokenService::ScopeSet scopes{GaiaConstants::kChromeSyncOAuth2Scope};
- token_fetcher_ = identity_manager_->CreateAccessTokenFetcherForPrimaryAccount(
- "suggestions_service", scopes,
+ token_fetcher_ = std::make_unique<identity::PrimaryAccountAccessTokenFetcher>(
+ "suggestions_service", identity_manager_, scopes,
base::BindOnce(&SuggestionsServiceImpl::AccessTokenAvailable,
base::Unretained(this), url),
identity::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable);
@@ -386,11 +387,10 @@ void SuggestionsServiceImpl::IssueRequestIfNoneOngoing(const GURL& url) {
void SuggestionsServiceImpl::AccessTokenAvailable(
const GURL& url,
- const GoogleServiceAuthError& error,
- const std::string& access_token) {
+ GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info) {
DCHECK(token_fetcher_);
- std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher>
- token_fetcher_deleter(std::move(token_fetcher_));
+ token_fetcher_.reset();
if (error.state() != GoogleServiceAuthError::NONE) {
blacklist_upload_backoff_.InformOfRequest(/*succeeded=*/false);
@@ -398,9 +398,9 @@ void SuggestionsServiceImpl::AccessTokenAvailable(
return;
}
- DCHECK(!access_token.empty());
+ DCHECK(!access_token_info.token.empty());
- IssueSuggestionsRequest(url, access_token);
+ IssueSuggestionsRequest(url, access_token_info.token);
}
void SuggestionsServiceImpl::IssueSuggestionsRequest(
@@ -458,11 +458,10 @@ SuggestionsServiceImpl::CreateSuggestionsRequest(
request->SetRequestContext(url_request_context_);
// Add Chrome experiment state to the request headers.
net::HttpRequestHeaders headers;
- // Note: It's OK to pass SignedIn::kNo if it's unknown, as it does not affect
- // transmission of experiments coming from the variations server.
- variations::AppendVariationHeaders(request->GetOriginalURL(),
- variations::InIncognito::kNo,
- variations::SignedIn::kNo, &headers);
+ // TODO: We should call AppendVariationHeaders with explicit
+ // variations::SignedIn::kNo If the access_token is empty
+ variations::AppendVariationHeadersUnknownSignedIn(
+ request->GetOriginalURL(), variations::InIncognito::kNo, &headers);
request->SetExtraRequestHeaders(headers.ToString());
if (!access_token.empty()) {
request->AddExtraRequestHeader(
diff --git a/chromium/components/suggestions/suggestions_service_impl.h b/chromium/components/suggestions/suggestions_service_impl.h
index 3d64716e342..9b6a7fe147c 100644
--- a/chromium/components/suggestions/suggestions_service_impl.h
+++ b/chromium/components/suggestions/suggestions_service_impl.h
@@ -28,11 +28,12 @@
#include "google_apis/gaia/google_service_auth_error.h"
#include "net/base/backoff_entry.h"
#include "net/url_request/url_fetcher_delegate.h"
-#include "services/identity/public/cpp/primary_account_access_token_fetcher.h"
+#include "services/identity/public/cpp/access_token_info.h"
#include "url/gurl.h"
namespace identity {
class IdentityManager;
+class PrimaryAccountAccessTokenFetcher;
} // namespace identity
namespace net {
@@ -140,8 +141,8 @@ class SuggestionsServiceImpl : public SuggestionsService,
// Called when an access token request completes (successfully or not).
void AccessTokenAvailable(const GURL& url,
- const GoogleServiceAuthError& error,
- const std::string& access_token);
+ GoogleServiceAuthError error,
+ identity::AccessTokenInfo access_token_info);
// Issues a network request for suggestions (fetch, blacklist, or clear
// blacklist, depending on |url|).
diff --git a/chromium/components/suggestions/suggestions_service_impl_unittest.cc b/chromium/components/suggestions/suggestions_service_impl_unittest.cc
index 2c906e3285a..a89a3ff4bb1 100644
--- a/chromium/components/suggestions/suggestions_service_impl_unittest.cc
+++ b/chromium/components/suggestions/suggestions_service_impl_unittest.cc
@@ -85,8 +85,9 @@ class MockSyncService : public syncer::FakeSyncService {
public:
MockSyncService() {}
~MockSyncService() override {}
- MOCK_CONST_METHOD0(CanSyncStart, bool());
- MOCK_CONST_METHOD0(IsSyncActive, bool());
+ MOCK_CONST_METHOD0(GetDisableReasons, int());
+ MOCK_CONST_METHOD0(IsEngineInitialized, bool());
+ MOCK_CONST_METHOD0(IsFirstSetupComplete, bool());
MOCK_CONST_METHOD0(ConfigurationDone, bool());
MOCK_CONST_METHOD0(IsLocalSyncEnabled, bool());
MOCK_CONST_METHOD0(IsUsingSecondaryPassphrase, bool());
@@ -153,10 +154,13 @@ class SuggestionsServiceTest : public testing::Test {
~SuggestionsServiceTest() override {}
void SetUp() override {
- EXPECT_CALL(*sync_service(), CanSyncStart())
+ EXPECT_CALL(*sync_service(), GetDisableReasons())
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(syncer::SyncService::DISABLE_REASON_NONE));
+ EXPECT_CALL(*sync_service(), IsEngineInitialized())
.Times(AnyNumber())
.WillRepeatedly(Return(true));
- EXPECT_CALL(*sync_service(), IsSyncActive())
+ EXPECT_CALL(*sync_service(), IsFirstSetupComplete())
.Times(AnyNumber())
.WillRepeatedly(Return(true));
EXPECT_CALL(*sync_service(), ConfigurationDone())
@@ -344,7 +348,8 @@ TEST_F(SuggestionsServiceTest, IgnoresUninterestingSyncChange) {
// This should *not* result in an automatic fetch.
TEST_F(SuggestionsServiceTest, DoesNotFetchOnStartup) {
// The sync service starts out inactive.
- EXPECT_CALL(*sync_service(), IsSyncActive()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*sync_service(), IsEngineInitialized())
+ .WillRepeatedly(Return(false));
static_cast<SyncServiceObserver*>(suggestions_service())
->OnStateChanged(sync_service());
@@ -352,7 +357,8 @@ TEST_F(SuggestionsServiceTest, DoesNotFetchOnStartup) {
ASSERT_FALSE(suggestions_service()->HasPendingRequestForTesting());
// Sync getting enabled should not result in a fetch.
- EXPECT_CALL(*sync_service(), IsSyncActive()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*sync_service(), IsEngineInitialized())
+ .WillRepeatedly(Return(true));
static_cast<SyncServiceObserver*>(suggestions_service())
->OnStateChanged(sync_service());
@@ -385,7 +391,8 @@ TEST_F(SuggestionsServiceTest, BuildUrlWithDefaultMinZeroParamForFewFeature) {
}
TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncNotInitializedEnabled) {
- EXPECT_CALL(*sync_service(), IsSyncActive()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*sync_service(), IsEngineInitialized())
+ .WillRepeatedly(Return(false));
static_cast<SyncServiceObserver*>(suggestions_service())
->OnStateChanged(sync_service());
@@ -408,7 +415,9 @@ TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncNotInitializedEnabled) {
}
TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncDisabled) {
- EXPECT_CALL(*sync_service(), CanSyncStart()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*sync_service(), GetDisableReasons())
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(syncer::SyncService::DISABLE_REASON_USER_CHOICE));
base::MockCallback<SuggestionsService::ResponseCallback> callback;
auto subscription = suggestions_service()->AddCallback(callback.Get());
diff --git a/chromium/components/sync/BUILD.gn b/chromium/components/sync/BUILD.gn
index a799b4cd861..3a7820e2436 100644
--- a/chromium/components/sync/BUILD.gn
+++ b/chromium/components/sync/BUILD.gn
@@ -63,6 +63,8 @@ jumbo_static_library("sync") {
"base/stop_source.h",
"base/sync_prefs.cc",
"base/sync_prefs.h",
+ "base/sync_stop_metadata_fate.cc",
+ "base/sync_stop_metadata_fate.h",
"base/syncer_error.cc",
"base/syncer_error.h",
"base/system_encryptor.cc",
@@ -132,6 +134,7 @@ jumbo_static_library("sync") {
"driver/signin_manager_wrapper.h",
"driver/startup_controller.cc",
"driver/startup_controller.h",
+ "driver/sync_api_component_factory.cc",
"driver/sync_api_component_factory.h",
"driver/sync_client.cc",
"driver/sync_client.h",
@@ -155,8 +158,6 @@ jumbo_static_library("sync") {
"driver/sync_util.cc",
"driver/sync_util.h",
"driver/user_selectable_sync_type.h",
- "engine/activation_context.cc",
- "engine/activation_context.h",
"engine/commit_queue.cc",
"engine/commit_queue.h",
"engine/configure_reason.h",
@@ -172,6 +173,8 @@ jumbo_static_library("sync") {
"engine/cycle/type_debug_info_observer.h",
"engine/cycle/update_counters.cc",
"engine/cycle/update_counters.h",
+ "engine/data_type_activation_response.cc",
+ "engine/data_type_activation_response.h",
"engine/data_type_association_stats.cc",
"engine/data_type_association_stats.h",
"engine/data_type_debug_info_listener.cc",
@@ -379,11 +382,14 @@ jumbo_static_library("sync") {
"js/js_event_handler.h",
"js/sync_js_controller.cc",
"js/sync_js_controller.h",
+ "model/blocking_model_type_store.h",
"model/change_processor.cc",
"model/change_processor.h",
"model/conflict_resolution.cc",
"model/conflict_resolution.h",
"model/data_batch.h",
+ "model/data_type_activation_request.cc",
+ "model/data_type_activation_request.h",
"model/data_type_error_handler.h",
"model/data_type_error_handler_impl.cc",
"model/data_type_error_handler_impl.h",
@@ -399,8 +405,10 @@ jumbo_static_library("sync") {
"model/model_error.h",
"model/model_type_change_processor.cc",
"model/model_type_change_processor.h",
- "model/model_type_store.cc",
"model/model_type_store.h",
+ "model/model_type_store_base.cc",
+ "model/model_type_store_base.h",
+ "model/model_type_store_service.h",
"model/model_type_sync_bridge.cc",
"model/model_type_sync_bridge.h",
"model/mutable_data_batch.cc",
@@ -422,6 +430,8 @@ jumbo_static_library("sync") {
"model/syncable_service.cc",
"model/syncable_service.h",
"model/time.h",
+ "model_impl/blocking_model_type_store_impl.cc",
+ "model_impl/blocking_model_type_store_impl.h",
"model_impl/client_tag_based_model_type_processor.cc",
"model_impl/client_tag_based_model_type_processor.h",
"model_impl/in_memory_metadata_change_list.cc",
@@ -430,6 +440,8 @@ jumbo_static_library("sync") {
"model_impl/model_type_store_backend.h",
"model_impl/model_type_store_impl.cc",
"model_impl/model_type_store_impl.h",
+ "model_impl/model_type_store_service_impl.cc",
+ "model_impl/model_type_store_service_impl.h",
"model_impl/processor_entity_tracker.cc",
"model_impl/processor_entity_tracker.h",
"model_impl/sync_metadata_store_change_list.cc",
@@ -540,6 +552,7 @@ jumbo_static_library("sync") {
"//base",
"//components/sync/protocol",
"//net",
+ "//third_party/leveldatabase",
"//url",
]
deps = [
@@ -560,10 +573,10 @@ jumbo_static_library("sync") {
"//crypto",
"//google_apis",
"//services/identity/public/cpp",
+ "//services/network/public/cpp",
"//sql",
"//third_party/cacheinvalidation",
"//third_party/crc32c",
- "//third_party/leveldatabase",
"//third_party/zlib",
"//third_party/zlib/google:compression_utils",
"//ui/base",
@@ -734,6 +747,8 @@ static_library("test_support_model") {
"model/sync_change_processor_wrapper_for_test.h",
"model/sync_error_factory_mock.cc",
"model/sync_error_factory_mock.h",
+ "model/test_model_type_store_service.cc",
+ "model/test_model_type_store_service.h",
]
defines = [ "SYNC_TEST" ]
@@ -772,8 +787,12 @@ static_library("test_support_driver") {
"driver/model_associator_mock.h",
"driver/sync_api_component_factory_mock.cc",
"driver/sync_api_component_factory_mock.h",
+ "driver/sync_client_mock.cc",
+ "driver/sync_client_mock.h",
"engine/fake_sync_engine.cc",
"engine/fake_sync_engine.h",
+ "engine/mock_sync_engine.cc",
+ "engine/mock_sync_engine.h",
"model/change_processor_mock.cc",
"model/change_processor_mock.h",
]
@@ -929,6 +948,8 @@ source_set("unit_tests") {
"//google_apis:test_support",
"//net",
"//net:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//sql",
"//sql:test_support",
"//testing/gmock",
@@ -1026,7 +1047,6 @@ if (!is_ios && !is_android && !is_fuchsia) {
":test_support_testserver",
"//base",
"//base/test:test_support",
- "//build/config:exe_and_shlib_deps",
"//build/win:default_exe_manifest",
"//net:test_support",
"//testing/gtest",
diff --git a/chromium/components/sync/protocol/protocol_sources.gni b/chromium/components/sync/protocol/protocol_sources.gni
index bf4501877bf..f5ab3954123 100644
--- a/chromium/components/sync/protocol/protocol_sources.gni
+++ b/chromium/components/sync/protocol/protocol_sources.gni
@@ -10,6 +10,7 @@ sync_protocol_sources = [
"//components/sync/protocol/arc_package_specifics.proto",
"//components/sync/protocol/article_specifics.proto",
"//components/sync/protocol/autofill_specifics.proto",
+ "//components/sync/protocol/bookmark_model_metadata.proto",
"//components/sync/protocol/bookmark_specifics.proto",
"//components/sync/protocol/client_commands.proto",
"//components/sync/protocol/client_debug_info.proto",
diff --git a/chromium/components/sync_bookmarks/BUILD.gn b/chromium/components/sync_bookmarks/BUILD.gn
index eecc87f7ce1..8e3935dc6c4 100644
--- a/chromium/components/sync_bookmarks/BUILD.gn
+++ b/chromium/components/sync_bookmarks/BUILD.gn
@@ -10,12 +10,18 @@ static_library("sync_bookmarks") {
"bookmark_change_processor.h",
"bookmark_data_type_controller.cc",
"bookmark_data_type_controller.h",
+ "bookmark_local_changes_builder.cc",
+ "bookmark_local_changes_builder.h",
"bookmark_model_associator.cc",
"bookmark_model_associator.h",
- "bookmark_model_type_controller.cc",
- "bookmark_model_type_controller.h",
+ "bookmark_model_observer_impl.cc",
+ "bookmark_model_observer_impl.h",
"bookmark_model_type_processor.cc",
"bookmark_model_type_processor.h",
+ "bookmark_remote_updates_handler.cc",
+ "bookmark_remote_updates_handler.h",
+ "bookmark_sync_service.cc",
+ "bookmark_sync_service.h",
"synced_bookmark_tracker.cc",
"synced_bookmark_tracker.h",
]
@@ -25,6 +31,7 @@ static_library("sync_bookmarks") {
"//components/bookmarks/browser",
"//components/favicon/core",
"//components/history/core/browser",
+ "//components/keyed_service/core:core",
"//components/sync",
"//components/undo",
"//ui/gfx",
@@ -36,14 +43,16 @@ source_set("unit_tests") {
sources = [
"bookmark_data_type_controller_unittest.cc",
- "bookmark_model_type_controller_unittest.cc",
+ "bookmark_model_observer_impl_unittest.cc",
"bookmark_model_type_processor_unittest.cc",
+ "bookmark_remote_updates_handler_unittest.cc",
"synced_bookmark_tracker_unittest.cc",
]
deps = [
":sync_bookmarks",
"//base",
+ "//base/test:test_support",
"//components/bookmarks/browser",
"//components/bookmarks/test",
"//components/history/core/browser",
diff --git a/chromium/components/sync_bookmarks/DEPS b/chromium/components/sync_bookmarks/DEPS
index 0d5798c76af..eb67095d629 100644
--- a/chromium/components/sync_bookmarks/DEPS
+++ b/chromium/components/sync_bookmarks/DEPS
@@ -3,6 +3,7 @@ include_rules = [
"+components/bookmarks/test",
"+components/favicon/core",
"+components/history/core/browser",
+ "+components/keyed_service",
"+components/prefs",
"+components/sync",
"+components/undo",
diff --git a/chromium/components/sync_bookmarks/bookmark_change_processor.cc b/chromium/components/sync_bookmarks/bookmark_change_processor.cc
index 0898ac26dcf..644bd9a6575 100644
--- a/chromium/components/sync_bookmarks/bookmark_change_processor.cc
+++ b/chromium/components/sync_bookmarks/bookmark_change_processor.cc
@@ -61,7 +61,7 @@ BookmarkChangeProcessor::~BookmarkChangeProcessor() {
}
void BookmarkChangeProcessor::StartImpl() {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!bookmark_model_);
bookmark_model_ = sync_client_->GetBookmarkModel();
DCHECK(bookmark_model_->loaded());
@@ -559,7 +559,7 @@ void BookmarkChangeProcessor::ApplyChangesFromSyncModel(
const syncer::BaseTransaction* trans,
int64_t model_version,
const syncer::ImmutableChangeRecordList& changes) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// A note about ordering. Sync backend is responsible for ordering the change
// records in the following order:
//
diff --git a/chromium/components/sync_bookmarks/bookmark_change_processor.h b/chromium/components/sync_bookmarks/bookmark_change_processor.h
index f1a5b9d0acf..bfbd55c2618 100644
--- a/chromium/components/sync_bookmarks/bookmark_change_processor.h
+++ b/chromium/components/sync_bookmarks/bookmark_change_processor.h
@@ -13,7 +13,7 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
-#include "base/threading/thread_checker.h"
+#include "base/sequence_checker.h"
#include "components/bookmarks/browser/bookmark_model_observer.h"
#include "components/bookmarks/browser/bookmark_node.h"
#include "components/sync/model/change_processor.h"
@@ -238,7 +238,7 @@ class BookmarkChangeProcessor : public bookmarks::BookmarkModelObserver,
// Returns false if |node| should not be synced.
bool CanSyncNode(const bookmarks::BookmarkNode* node);
- base::ThreadChecker thread_checker_;
+ SEQUENCE_CHECKER(sequence_checker_);
// The bookmark model we are processing changes from. Non-null when
// |running_| is true.
diff --git a/chromium/components/sync_bookmarks/bookmark_data_type_controller.cc b/chromium/components/sync_bookmarks/bookmark_data_type_controller.cc
index 1a2e494eb6b..a0aa9782c67 100644
--- a/chromium/components/sync_bookmarks/bookmark_data_type_controller.cc
+++ b/chromium/components/sync_bookmarks/bookmark_data_type_controller.cc
@@ -4,11 +4,15 @@
#include "components/sync_bookmarks/bookmark_data_type_controller.h"
+#include <utility>
+
#include "base/metrics/histogram.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/history/core/browser/history_service.h"
+#include "components/sync/driver/model_associator.h"
#include "components/sync/driver/sync_api_component_factory.h"
#include "components/sync/driver/sync_client.h"
+#include "components/sync/model/change_processor.h"
using bookmarks::BookmarkModel;
@@ -48,9 +52,9 @@ void BookmarkDataTypeController::CreateSyncComponents() {
DCHECK(CalledOnValidThread());
syncer::SyncApiComponentFactory::SyncComponents sync_components =
sync_client_->GetSyncApiComponentFactory()->CreateBookmarkSyncComponents(
- sync_client_->GetSyncService(), CreateErrorHandler());
- set_model_associator(sync_components.model_associator);
- set_change_processor(sync_components.change_processor);
+ CreateErrorHandler());
+ set_model_associator(std::move(sync_components.model_associator));
+ set_change_processor(std::move(sync_components.change_processor));
}
void BookmarkDataTypeController::BookmarkModelChanged() {
diff --git a/chromium/components/sync_bookmarks/bookmark_data_type_controller_unittest.cc b/chromium/components/sync_bookmarks/bookmark_data_type_controller_unittest.cc
index 0da34411051..f4876b5a679 100644
--- a/chromium/components/sync_bookmarks/bookmark_data_type_controller_unittest.cc
+++ b/chromium/components/sync_bookmarks/bookmark_data_type_controller_unittest.cc
@@ -5,13 +5,14 @@
#include "components/sync_bookmarks/bookmark_data_type_controller.h"
#include <memory>
+#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/test/bookmark_test_helpers.h"
#include "components/bookmarks/test/test_bookmark_client.h"
@@ -37,6 +38,7 @@ using syncer::StartCallbackMock;
using testing::_;
using testing::DoAll;
using testing::InvokeWithoutArgs;
+using testing::NiceMock;
using testing::Return;
using testing::SetArgPointee;
@@ -64,18 +66,27 @@ class SyncBookmarkDataTypeControllerTest : public testing::Test,
}
syncer::SyncService* GetSyncService() override { return &service_; }
syncer::SyncApiComponentFactory* GetSyncApiComponentFactory() override {
- return profile_sync_factory_.get();
+ return &components_factory_;
}
void SetUp() override {
- model_associator_ = new ModelAssociatorMock();
- change_processor_ = new ChangeProcessorMock();
+ model_associator_deleter_ =
+ std::make_unique<NiceMock<ModelAssociatorMock>>();
+ change_processor_deleter_ =
+ std::make_unique<NiceMock<ChangeProcessorMock>>();
+ model_associator_ = model_associator_deleter_.get();
+ change_processor_ = change_processor_deleter_.get();
history_service_ = std::make_unique<HistoryMock>();
- profile_sync_factory_ =
- std::make_unique<syncer::SyncApiComponentFactoryMock>(
- model_associator_, change_processor_);
bookmark_dtc_ =
std::make_unique<BookmarkDataTypeController>(base::DoNothing(), this);
+
+ ON_CALL(components_factory_, CreateBookmarkSyncComponents(_))
+ .WillByDefault(testing::InvokeWithoutArgs([=]() {
+ syncer::SyncApiComponentFactory::SyncComponents components;
+ components.model_associator = std::move(model_associator_deleter_);
+ components.change_processor = std::move(change_processor_deleter_);
+ return components;
+ }));
}
protected:
@@ -90,8 +101,8 @@ class SyncBookmarkDataTypeControllerTest : public testing::Test,
if (bookmark_load_policy == LOAD_MODEL) {
TestingPrefServiceSimple prefs;
bookmark_model_->Load(&prefs, base::FilePath(),
- base::ThreadTaskRunnerHandle::Get(),
- base::ThreadTaskRunnerHandle::Get());
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get());
bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model_.get());
}
}
@@ -131,13 +142,15 @@ class SyncBookmarkDataTypeControllerTest : public testing::Test,
}
base::MessageLoop message_loop_;
- std::unique_ptr<syncer::SyncApiComponentFactoryMock> profile_sync_factory_;
+ testing::NiceMock<syncer::SyncApiComponentFactoryMock> components_factory_;
std::unique_ptr<BookmarkModel> bookmark_model_;
std::unique_ptr<HistoryMock> history_service_;
std::unique_ptr<BookmarkDataTypeController> bookmark_dtc_;
syncer::FakeSyncService service_;
ModelAssociatorMock* model_associator_;
ChangeProcessorMock* change_processor_;
+ std::unique_ptr<ModelAssociatorMock> model_associator_deleter_;
+ std::unique_ptr<ChangeProcessorMock> change_processor_deleter_;
StartCallbackMock start_callback_;
ModelLoadCallbackMock model_load_callback_;
};
@@ -167,8 +180,8 @@ TEST_F(SyncBookmarkDataTypeControllerTest, StartBookmarkModelNotReady) {
TestingPrefServiceSimple prefs;
bookmark_model_->Load(&prefs, base::FilePath(),
- base::ThreadTaskRunnerHandle::Get(),
- base::ThreadTaskRunnerHandle::Get());
+ base::SequencedTaskRunnerHandle::Get(),
+ base::SequencedTaskRunnerHandle::Get());
bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model_.get());
EXPECT_EQ(DataTypeController::MODEL_LOADED, bookmark_dtc_->state());
@@ -252,7 +265,7 @@ TEST_F(SyncBookmarkDataTypeControllerTest, StartAssociationFailed) {
EXPECT_CALL(start_callback_,
Run(DataTypeController::ASSOCIATION_FAILED, _, _));
Start();
- EXPECT_EQ(DataTypeController::DISABLED, bookmark_dtc_->state());
+ EXPECT_EQ(DataTypeController::FAILED, bookmark_dtc_->state());
}
TEST_F(SyncBookmarkDataTypeControllerTest,
@@ -279,7 +292,7 @@ TEST_F(SyncBookmarkDataTypeControllerTest, StartAborted) {
base::Bind(&ModelLoadCallbackMock::Run,
base::Unretained(&model_load_callback_)));
- bookmark_dtc_->Stop();
+ bookmark_dtc_->Stop(syncer::KEEP_METADATA);
EXPECT_EQ(DataTypeController::NOT_RUNNING, bookmark_dtc_->state());
}
@@ -294,6 +307,6 @@ TEST_F(SyncBookmarkDataTypeControllerTest, Stop) {
EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _, _));
Start();
EXPECT_EQ(DataTypeController::RUNNING, bookmark_dtc_->state());
- bookmark_dtc_->Stop();
+ bookmark_dtc_->Stop(syncer::KEEP_METADATA);
EXPECT_EQ(DataTypeController::NOT_RUNNING, bookmark_dtc_->state());
}
diff --git a/chromium/components/sync_bookmarks/bookmark_local_changes_builder.cc b/chromium/components/sync_bookmarks/bookmark_local_changes_builder.cc
new file mode 100644
index 00000000000..ad86cbecbb3
--- /dev/null
+++ b/chromium/components/sync_bookmarks/bookmark_local_changes_builder.cc
@@ -0,0 +1,107 @@
+// 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/sync_bookmarks/bookmark_local_changes_builder.h"
+
+#include <string>
+#include <utility>
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/bookmarks/browser/bookmark_node.h"
+#include "components/sync/base/time.h"
+#include "components/sync/protocol/bookmark_model_metadata.pb.h"
+#include "components/sync_bookmarks/synced_bookmark_tracker.h"
+
+namespace sync_bookmarks {
+
+namespace {
+
+sync_pb::EntitySpecifics SpecificsFromBookmarkNode(
+ const bookmarks::BookmarkNode* node) {
+ sync_pb::EntitySpecifics specifics;
+ sync_pb::BookmarkSpecifics* bm_specifics = specifics.mutable_bookmark();
+ bm_specifics->set_url(node->url().spec());
+ // TODO(crbug.com/516866): Set the favicon.
+ bm_specifics->set_title(base::UTF16ToUTF8(node->GetTitle()));
+ bm_specifics->set_creation_time_us(
+ node->date_added().ToDeltaSinceWindowsEpoch().InMicroseconds());
+
+ bm_specifics->set_icon_url(node->icon_url() ? node->icon_url()->spec()
+ : std::string());
+ if (node->GetMetaInfoMap()) {
+ for (const std::pair<std::string, std::string>& pair :
+ *node->GetMetaInfoMap()) {
+ sync_pb::MetaInfo* meta_info = bm_specifics->add_meta_info();
+ meta_info->set_key(pair.first);
+ meta_info->set_value(pair.second);
+ }
+ }
+ return specifics;
+}
+
+} // namespace
+
+BookmarkLocalChangesBuilder::BookmarkLocalChangesBuilder(
+ const SyncedBookmarkTracker* const bookmark_tracker)
+ : bookmark_tracker_(bookmark_tracker) {
+ DCHECK(bookmark_tracker);
+}
+
+std::vector<syncer::CommitRequestData>
+BookmarkLocalChangesBuilder::BuildCommitRequests(size_t max_entries) const {
+ DCHECK(bookmark_tracker_);
+ const std::vector<const SyncedBookmarkTracker::Entity*>
+ entities_with_local_changes =
+ bookmark_tracker_->GetEntitiesWithLocalChanges(max_entries);
+ DCHECK_LE(entities_with_local_changes.size(), max_entries);
+
+ std::vector<syncer::CommitRequestData> commit_requests;
+ for (const SyncedBookmarkTracker::Entity* entity :
+ entities_with_local_changes) {
+ DCHECK(entity->IsUnsynced());
+ const sync_pb::EntityMetadata* metadata = entity->metadata();
+
+ syncer::CommitRequestData request;
+ syncer::EntityData data;
+ data.id = metadata->server_id();
+ data.creation_time = syncer::ProtoTimeToTime(metadata->creation_time());
+ data.modification_time =
+ syncer::ProtoTimeToTime(metadata->modification_time());
+ if (!metadata->is_deleted()) {
+ const bookmarks::BookmarkNode* node = entity->bookmark_node();
+ DCHECK(node);
+ const bookmarks::BookmarkNode* parent = node->parent();
+ const SyncedBookmarkTracker::Entity* parent_entity =
+ bookmark_tracker_->GetEntityForBookmarkNode(parent);
+ DCHECK(parent_entity);
+ data.parent_id = parent_entity->metadata()->server_id();
+ // TODO(crbug.com/516866): Double check that custom passphrase works well
+ // with this implementation, because:
+ // 1. NonBlockingTypeCommitContribution::AdjustCommitProto() clears the
+ // title out.
+ // 2. Bookmarks (maybe ancient legacy bookmarks only?) use/used |name| to
+ // encode the title.
+ data.is_folder = node->is_folder();
+ // TODO(crbug.com/516866): Set the non_unique_name similar to directory
+ // implementation.
+ // https://cs.chromium.org/chromium/src/components/sync/syncable/write_node.cc?l=41&rcl=1675007db1e0eb03417e81442688bb11cd181f58
+ data.non_unique_name = base::UTF16ToUTF8(node->GetTitle());
+ data.unique_position = metadata->unique_position();
+ // Assign specifics only for the non-deletion case. In case of deletion,
+ // EntityData should contain empty specifics to indicate deletion.
+ data.specifics = SpecificsFromBookmarkNode(node);
+ }
+ request.entity = data.PassToPtr();
+ request.sequence_number = metadata->sequence_number();
+ request.base_version = metadata->server_version();
+ // Specifics hash has been computed in the tracker when this entity has been
+ // added/updated.
+ request.specifics_hash = metadata->specifics_hash();
+
+ commit_requests.push_back(std::move(request));
+ }
+ return commit_requests;
+}
+
+} // namespace sync_bookmarks
diff --git a/chromium/components/sync_bookmarks/bookmark_local_changes_builder.h b/chromium/components/sync_bookmarks/bookmark_local_changes_builder.h
new file mode 100644
index 00000000000..0a5fd065fd2
--- /dev/null
+++ b/chromium/components/sync_bookmarks/bookmark_local_changes_builder.h
@@ -0,0 +1,33 @@
+// 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_SYNC_BOOKMARKS_BOOKMARK_LOCAL_CHANGES_BUILDER_H_
+#define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_LOCAL_CHANGES_BUILDER_H_
+
+#include <vector>
+
+#include "components/sync/engine/non_blocking_sync_common.h"
+
+namespace sync_bookmarks {
+
+class SyncedBookmarkTracker;
+
+class BookmarkLocalChangesBuilder {
+ public:
+ // |bookmark_tracker| must not be null and must outlive this object.
+ explicit BookmarkLocalChangesBuilder(
+ const SyncedBookmarkTracker* bookmark_tracker);
+ // Builds the commit requests list.
+ std::vector<syncer::CommitRequestData> BuildCommitRequests(
+ size_t max_entries) const;
+
+ private:
+ const SyncedBookmarkTracker* const bookmark_tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(BookmarkLocalChangesBuilder);
+};
+
+} // namespace sync_bookmarks
+
+#endif // COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_LOCAL_CHANGES_BUILDER_H_
diff --git a/chromium/components/sync_bookmarks/bookmark_model_observer_impl.cc b/chromium/components/sync_bookmarks/bookmark_model_observer_impl.cc
new file mode 100644
index 00000000000..91d40c7bfab
--- /dev/null
+++ b/chromium/components/sync_bookmarks/bookmark_model_observer_impl.cc
@@ -0,0 +1,324 @@
+// 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/sync_bookmarks/bookmark_model_observer_impl.h"
+
+#include <utility>
+
+#include "base/guid.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/bookmark_node.h"
+#include "components/sync/base/hash_util.h"
+#include "components/sync/base/unique_position.h"
+#include "components/sync/engine/non_blocking_sync_common.h"
+#include "components/sync_bookmarks/synced_bookmark_tracker.h"
+
+namespace sync_bookmarks {
+
+namespace {
+
+void UpdateBookmarkSpecificsMetaInfo(
+ const bookmarks::BookmarkNode::MetaInfoMap* metainfo_map,
+ sync_pb::BookmarkSpecifics* bm_specifics) {
+ // TODO(crbug.com/516866): update the implementation to be similar to the
+ // directory implementation
+ // https://cs.chromium.org/chromium/src/components/sync_bookmarks/bookmark_change_processor.cc?l=882&rcl=f38001d936d8b2abb5743e85cbc88c72746ae3d2
+ for (const std::pair<std::string, std::string>& pair : *metainfo_map) {
+ sync_pb::MetaInfo* meta_info = bm_specifics->add_meta_info();
+ meta_info->set_key(pair.first);
+ meta_info->set_value(pair.second);
+ }
+}
+
+sync_pb::EntitySpecifics CreateSpecificsFromBookmarkNode(
+ const bookmarks::BookmarkNode* node) {
+ sync_pb::EntitySpecifics specifics;
+ sync_pb::BookmarkSpecifics* bm_specifics = specifics.mutable_bookmark();
+ bm_specifics->set_url(node->url().spec());
+ // TODO(crbug.com/516866): Set the favicon.
+ bm_specifics->set_title(base::UTF16ToUTF8(node->GetTitle()));
+ bm_specifics->set_creation_time_us(
+ node->date_added().ToDeltaSinceWindowsEpoch().InMicroseconds());
+
+ bm_specifics->set_icon_url(node->icon_url() ? node->icon_url()->spec()
+ : std::string());
+ if (node->GetMetaInfoMap()) {
+ UpdateBookmarkSpecificsMetaInfo(node->GetMetaInfoMap(), bm_specifics);
+ }
+ return specifics;
+}
+
+} // namespace
+
+BookmarkModelObserverImpl::BookmarkModelObserverImpl(
+ const base::RepeatingClosure& nudge_for_commit_closure,
+ SyncedBookmarkTracker* bookmark_tracker)
+ : bookmark_tracker_(bookmark_tracker),
+ nudge_for_commit_closure_(nudge_for_commit_closure) {
+ DCHECK(bookmark_tracker_);
+}
+
+BookmarkModelObserverImpl::~BookmarkModelObserverImpl() = default;
+
+void BookmarkModelObserverImpl::BookmarkModelLoaded(
+ bookmarks::BookmarkModel* model,
+ bool ids_reassigned) {
+ NOTIMPLEMENTED();
+}
+
+void BookmarkModelObserverImpl::BookmarkModelBeingDeleted(
+ bookmarks::BookmarkModel* model) {
+ NOTIMPLEMENTED();
+}
+
+void BookmarkModelObserverImpl::BookmarkNodeMoved(
+ bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* old_parent,
+ int old_index,
+ const bookmarks::BookmarkNode* new_parent,
+ int new_index) {
+ const bookmarks::BookmarkNode* node = new_parent->GetChild(new_index);
+
+ // We shouldn't see changes to the top-level nodes.
+ DCHECK(!model->is_permanent_node(node));
+ // TODO(crbug.com/516866): continue only if
+ // model->client()->CanSyncNode(node).
+ const SyncedBookmarkTracker::Entity* entity =
+ bookmark_tracker_->GetEntityForBookmarkNode(node);
+ DCHECK(entity);
+
+ const std::string& sync_id = entity->metadata()->server_id();
+ const base::Time modification_time = base::Time::Now();
+
+ const sync_pb::UniquePosition unique_position =
+ ComputePosition(*new_parent, new_index, sync_id).ToProto();
+
+ sync_pb::EntitySpecifics specifics = CreateSpecificsFromBookmarkNode(node);
+
+ bookmark_tracker_->Update(sync_id, entity->metadata()->server_version(),
+ modification_time, unique_position, specifics);
+ // Mark the entity that it needs to be committed.
+ bookmark_tracker_->IncrementSequenceNumber(sync_id);
+ nudge_for_commit_closure_.Run();
+}
+
+void BookmarkModelObserverImpl::BookmarkNodeAdded(
+ bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* parent,
+ int index) {
+ const bookmarks::BookmarkNode* node = parent->GetChild(index);
+ // TODO(crbug.com/516866): continue only if
+ // model->client()->CanSyncNode(node).
+
+ const SyncedBookmarkTracker::Entity* parent_entity =
+ bookmark_tracker_->GetEntityForBookmarkNode(parent);
+ if (!parent_entity) {
+ DLOG(WARNING) << "Bookmark parent lookup failed";
+ return;
+ }
+ // Similar to the diectory implementation here:
+ // https://cs.chromium.org/chromium/src/components/sync/syncable/mutable_entry.cc?l=237&gsn=CreateEntryKernel
+ // Assign a temp server id for the entity. Will be overriden by the actual
+ // server id upon receiving commit response.
+ const std::string sync_id = base::GenerateGUID();
+ const int64_t server_version = syncer::kUncommittedVersion;
+ const base::Time creation_time = base::Time::Now();
+ const sync_pb::UniquePosition unique_position =
+ ComputePosition(*parent, index, sync_id).ToProto();
+
+ sync_pb::EntitySpecifics specifics = CreateSpecificsFromBookmarkNode(node);
+
+ bookmark_tracker_->Add(sync_id, node, server_version, creation_time,
+ unique_position, specifics);
+ // Mark the entity that it needs to be committed.
+ bookmark_tracker_->IncrementSequenceNumber(sync_id);
+ nudge_for_commit_closure_.Run();
+}
+
+void BookmarkModelObserverImpl::OnWillRemoveBookmarks(
+ bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* parent,
+ int old_index,
+ const bookmarks::BookmarkNode* node) {
+ // TODO(crbug.com/516866): continue only if
+ // model->client()->CanSyncNode(node).
+ ProcessDelete(parent, node);
+ nudge_for_commit_closure_.Run();
+}
+
+void BookmarkModelObserverImpl::BookmarkNodeRemoved(
+ bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* parent,
+ int old_index,
+ const bookmarks::BookmarkNode* node,
+ const std::set<GURL>& removed_urls) {
+ // All the work should have already been done in OnWillRemoveBookmarks.
+ DCHECK(bookmark_tracker_->GetEntityForBookmarkNode(node) == nullptr);
+}
+
+void BookmarkModelObserverImpl::BookmarkAllUserNodesRemoved(
+ bookmarks::BookmarkModel* model,
+ const std::set<GURL>& removed_urls) {
+ NOTIMPLEMENTED();
+}
+
+void BookmarkModelObserverImpl::BookmarkNodeChanged(
+ bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) {
+ // TODO(crbug.com/516866): continue only if
+ // model->client()->CanSyncNode(node).
+
+ // We shouldn't see changes to the top-level nodes.
+ DCHECK(!model->is_permanent_node(node));
+
+ const SyncedBookmarkTracker::Entity* entity =
+ bookmark_tracker_->GetEntityForBookmarkNode(node);
+ DCHECK(entity);
+ const std::string& sync_id = entity->metadata()->server_id();
+ const base::Time modification_time = base::Time::Now();
+ sync_pb::EntitySpecifics specifics = CreateSpecificsFromBookmarkNode(node);
+
+ bookmark_tracker_->Update(sync_id, entity->metadata()->server_version(),
+ modification_time,
+ entity->metadata()->unique_position(), specifics);
+ // Mark the entity that it needs to be committed.
+ bookmark_tracker_->IncrementSequenceNumber(sync_id);
+ nudge_for_commit_closure_.Run();
+}
+
+void BookmarkModelObserverImpl::BookmarkMetaInfoChanged(
+ bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) {
+ BookmarkNodeChanged(model, node);
+}
+
+void BookmarkModelObserverImpl::BookmarkNodeFaviconChanged(
+ bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) {
+ NOTIMPLEMENTED();
+}
+
+void BookmarkModelObserverImpl::BookmarkNodeChildrenReordered(
+ bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) {
+ // TODO(crbug.com/516866): continue only if
+ // model->client()->CanSyncNode(node).
+
+ // 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:
+ // 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;
+ syncer::UniquePosition previous_position;
+ for (int i = 0; i < node->child_count(); ++i) {
+ const bookmarks::BookmarkNode* child = node->GetChild(i);
+
+ const SyncedBookmarkTracker::Entity* entity =
+ bookmark_tracker_->GetEntityForBookmarkNode(child);
+ DCHECK(entity);
+
+ const std::string& sync_id = entity->metadata()->server_id();
+ const std::string suffix = syncer::GenerateSyncableBookmarkHash(
+ bookmark_tracker_->model_type_state().cache_guid(), sync_id);
+ const base::Time modification_time = base::Time::Now();
+
+ if (i == 0) {
+ position = syncer::UniquePosition::InitialPosition(suffix);
+ } else {
+ position = syncer::UniquePosition::After(previous_position, suffix);
+ }
+
+ previous_position = position;
+
+ const sync_pb::EntitySpecifics specifics =
+ CreateSpecificsFromBookmarkNode(node);
+
+ bookmark_tracker_->Update(sync_id, entity->metadata()->server_version(),
+ modification_time, position.ToProto(), specifics);
+ // Mark the entity that it needs to be committed.
+ bookmark_tracker_->IncrementSequenceNumber(sync_id);
+ }
+ nudge_for_commit_closure_.Run();
+}
+
+syncer::UniquePosition BookmarkModelObserverImpl::ComputePosition(
+ const bookmarks::BookmarkNode& parent,
+ int index,
+ const std::string& sync_id) {
+ const std::string& suffix = syncer::GenerateSyncableBookmarkHash(
+ bookmark_tracker_->model_type_state().cache_guid(), sync_id);
+ DCHECK_NE(0, parent.child_count());
+
+ if (parent.child_count() == 1) {
+ // No siblings, the parent has no other children.
+ return syncer::UniquePosition::InitialPosition(suffix);
+ }
+ if (index == 0) {
+ const bookmarks::BookmarkNode* successor_node = parent.GetChild(1);
+ const SyncedBookmarkTracker::Entity* successor_entity =
+ bookmark_tracker_->GetEntityForBookmarkNode(successor_node);
+ DCHECK(successor_entity);
+ // Insert at the beginning.
+ return syncer::UniquePosition::Before(
+ syncer::UniquePosition::FromProto(
+ successor_entity->metadata()->unique_position()),
+ suffix);
+ }
+ if (index == parent.child_count() - 1) {
+ // Insert at the end.
+ const bookmarks::BookmarkNode* predecessor_node =
+ parent.GetChild(index - 1);
+ const SyncedBookmarkTracker::Entity* predecessor_entity =
+ bookmark_tracker_->GetEntityForBookmarkNode(predecessor_node);
+ DCHECK(predecessor_entity);
+ return syncer::UniquePosition::After(
+ syncer::UniquePosition::FromProto(
+ predecessor_entity->metadata()->unique_position()),
+ suffix);
+ }
+ // Insert in the middle.
+ const bookmarks::BookmarkNode* successor_node = parent.GetChild(index + 1);
+ const SyncedBookmarkTracker::Entity* successor_entity =
+ bookmark_tracker_->GetEntityForBookmarkNode(successor_node);
+ DCHECK(successor_entity);
+ const bookmarks::BookmarkNode* predecessor_node = parent.GetChild(index - 1);
+ const SyncedBookmarkTracker::Entity* predecessor_entity =
+ bookmark_tracker_->GetEntityForBookmarkNode(predecessor_node);
+ DCHECK(predecessor_entity);
+ return syncer::UniquePosition::Between(
+ syncer::UniquePosition::FromProto(
+ predecessor_entity->metadata()->unique_position()),
+ syncer::UniquePosition::FromProto(
+ successor_entity->metadata()->unique_position()),
+ suffix);
+}
+
+void BookmarkModelObserverImpl::ProcessDelete(
+ const bookmarks::BookmarkNode* parent,
+ const bookmarks::BookmarkNode* node) {
+ // If not a leaf node, process all children first.
+ for (int i = 0; i < node->child_count(); ++i) {
+ const bookmarks::BookmarkNode* child = node->GetChild(i);
+ ProcessDelete(node, child);
+ }
+ // Process the current node.
+ const SyncedBookmarkTracker::Entity* entity =
+ bookmark_tracker_->GetEntityForBookmarkNode(node);
+ // Shouldn't try to delete untracked entities.
+ DCHECK(entity);
+ const std::string& sync_id = entity->metadata()->server_id();
+ bookmark_tracker_->MarkDeleted(sync_id);
+ // Mark the entity that it needs to be committed.
+ bookmark_tracker_->IncrementSequenceNumber(sync_id);
+}
+
+} // namespace sync_bookmarks
diff --git a/chromium/components/sync_bookmarks/bookmark_model_observer_impl.h b/chromium/components/sync_bookmarks/bookmark_model_observer_impl.h
new file mode 100644
index 00000000000..ef5ecc70f5f
--- /dev/null
+++ b/chromium/components/sync_bookmarks/bookmark_model_observer_impl.h
@@ -0,0 +1,93 @@
+// 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_SYNC_BOOKMARKS_BOOKMARK_MODEL_OBSERVER_IMPL_H_
+#define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_OBSERVER_IMPL_H_
+
+#include <set>
+#include <string>
+
+#include "base/callback.h"
+#include "components/bookmarks/browser/bookmark_model_observer.h"
+#include "components/bookmarks/browser/bookmark_node.h"
+#include "url/gurl.h"
+
+namespace syncer {
+class UniquePosition;
+}
+
+namespace sync_bookmarks {
+
+class SyncedBookmarkTracker;
+
+// Class for listening to local changes in the bookmark model and updating
+// metadata in SyncedBookmarkTracker, such that ultimately the processor exposes
+// those local changes to the sync engine.
+class BookmarkModelObserverImpl : public bookmarks::BookmarkModelObserver {
+ public:
+ // |bookmark_tracker_| must not be null and must outlive this object.
+ BookmarkModelObserverImpl(
+ const base::RepeatingClosure& nudge_for_commit_closure,
+ SyncedBookmarkTracker* bookmark_tracker);
+ ~BookmarkModelObserverImpl() override;
+
+ // BookmarkModelObserver:
+ void BookmarkModelLoaded(bookmarks::BookmarkModel* model,
+ bool ids_reassigned) override;
+ void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override;
+ void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* old_parent,
+ int old_index,
+ const bookmarks::BookmarkNode* new_parent,
+ int new_index) override;
+ void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* parent,
+ int index) override;
+ void OnWillRemoveBookmarks(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* parent,
+ int old_index,
+ const bookmarks::BookmarkNode* node) override;
+ void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* parent,
+ int old_index,
+ const bookmarks::BookmarkNode* node,
+ const std::set<GURL>& removed_urls) override;
+ void BookmarkAllUserNodesRemoved(bookmarks::BookmarkModel* model,
+ const std::set<GURL>& removed_urls) override;
+ void BookmarkNodeChanged(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) override;
+ void BookmarkMetaInfoChanged(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) override;
+ void BookmarkNodeFaviconChanged(bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) override;
+ void BookmarkNodeChildrenReordered(
+ bookmarks::BookmarkModel* model,
+ const bookmarks::BookmarkNode* node) override;
+
+ private:
+ syncer::UniquePosition ComputePosition(const bookmarks::BookmarkNode& parent,
+ int index,
+ const std::string& sync_id);
+
+ // Processes the deletion of a bookmake node and updates the
+ // |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);
+
+ // Points to the tracker owned by the processor. It keeps the mapping between
+ // bookmark nodes and corresponding sync server entities.
+ SyncedBookmarkTracker* const bookmark_tracker_;
+
+ // The callback used to inform the sync engine that there are local changes to
+ // be committed.
+ const base::RepeatingClosure nudge_for_commit_closure_;
+
+ DISALLOW_COPY_AND_ASSIGN(BookmarkModelObserverImpl);
+};
+
+} // namespace sync_bookmarks
+
+#endif // COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_OBSERVER_IMPL_H_
diff --git a/chromium/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc b/chromium/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc
new file mode 100644
index 00000000000..4a83412b4b5
--- /dev/null
+++ b/chromium/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc
@@ -0,0 +1,448 @@
+// 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/sync_bookmarks/bookmark_model_observer_impl.h"
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/mock_callback.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/test/test_bookmark_client.h"
+#include "components/sync/base/time.h"
+#include "components/sync/base/unique_position.h"
+#include "components/sync_bookmarks/synced_bookmark_tracker.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sync_bookmarks {
+
+namespace {
+
+using testing::Eq;
+using testing::Ne;
+using testing::NiceMock;
+using testing::NotNull;
+using testing::ElementsAre;
+
+const char kBookmarkBarId[] = "bookmark_bar_id";
+const char kBookmarkBarTag[] = "bookmark_bar";
+const size_t kMaxEntries = 1000;
+
+class BookmarkModelObserverImplTest : public testing::Test {
+ public:
+ BookmarkModelObserverImplTest()
+ : bookmark_model_(bookmarks::TestBookmarkClient::CreateModel()),
+ bookmark_tracker_(std::vector<NodeMetadataPair>(),
+ std::make_unique<sync_pb::ModelTypeState>()),
+ observer_(nudge_for_commit_closure_.Get(), &bookmark_tracker_) {
+ bookmark_model_->AddObserver(&observer_);
+ sync_pb::EntitySpecifics specifics;
+ specifics.mutable_bookmark()->set_title(kBookmarkBarTag);
+ bookmark_tracker_.Add(
+ /*sync_id=*/kBookmarkBarId,
+ /*bookmark_node=*/bookmark_model()->bookmark_bar_node(),
+ /*server_version=*/0, /*creation_time=*/base::Time::Now(),
+ syncer::UniquePosition::InitialPosition(
+ syncer::UniquePosition::RandomSuffix())
+ .ToProto(),
+ specifics);
+ }
+
+ void SimulateCommitResponseForAllLocalChanges() {
+ for (const SyncedBookmarkTracker::Entity* entity :
+ bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries)) {
+ const std::string id = entity->metadata()->server_id();
+ // Don't simulate change in id for simplicity.
+ bookmark_tracker()->UpdateUponCommitResponse(id, id,
+ /*acked_sequence_number=*/1,
+ /*server_version=*/1);
+ }
+ }
+
+ syncer::UniquePosition PositionOf(
+ const bookmarks::BookmarkNode* bookmark_node) {
+ const SyncedBookmarkTracker::Entity* entity =
+ bookmark_tracker()->GetEntityForBookmarkNode(bookmark_node);
+ return syncer::UniquePosition::FromProto(
+ entity->metadata()->unique_position());
+ }
+
+ bookmarks::BookmarkModel* bookmark_model() { return bookmark_model_.get(); }
+ SyncedBookmarkTracker* bookmark_tracker() { return &bookmark_tracker_; }
+ BookmarkModelObserverImpl* observer() { return &observer_; }
+ base::MockCallback<base::RepeatingClosure>* nudge_for_commit_closure() {
+ return &nudge_for_commit_closure_;
+ }
+
+ private:
+ std::unique_ptr<bookmarks::BookmarkModel> bookmark_model_;
+ NiceMock<base::MockCallback<base::RepeatingClosure>>
+ nudge_for_commit_closure_;
+ SyncedBookmarkTracker bookmark_tracker_;
+ BookmarkModelObserverImpl observer_;
+};
+
+TEST_F(BookmarkModelObserverImplTest,
+ BookmarkAddedShouldPutInTheTrackerAndNudgeForCommit) {
+ const std::string kTitle = "title";
+ const std::string kUrl = "http://www.url.com";
+
+ EXPECT_CALL(*nudge_for_commit_closure(), Run());
+ const bookmarks::BookmarkNode* bookmark_bar_node =
+ bookmark_model()->bookmark_bar_node();
+ const bookmarks::BookmarkNode* bookmark_node = bookmark_model()->AddURL(
+ /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16(kTitle),
+ GURL(kUrl));
+
+ EXPECT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 2U);
+
+ std::vector<const SyncedBookmarkTracker::Entity*> local_changes =
+ bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries);
+ ASSERT_THAT(local_changes.size(), 1U);
+ EXPECT_THAT(local_changes[0]->bookmark_node(), Eq(bookmark_node));
+}
+
+TEST_F(BookmarkModelObserverImplTest,
+ BookmarkChangedShouldUpdateTheTrackerAndNudgeForCommit) {
+ const std::string kTitle1 = "title1";
+ const std::string kUrl1 = "http://www.url1.com";
+ const std::string kNewUrl1 = "http://www.new-url1.com";
+ const std::string kTitle2 = "title2";
+ const std::string kUrl2 = "http://www.url2.com";
+ const std::string kNewTitle2 = "new_title2";
+
+ const bookmarks::BookmarkNode* bookmark_bar_node =
+ bookmark_model()->bookmark_bar_node();
+ const bookmarks::BookmarkNode* bookmark_node1 = bookmark_model()->AddURL(
+ /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16(kTitle1),
+ GURL(kUrl1));
+ const bookmarks::BookmarkNode* bookmark_node2 = bookmark_model()->AddURL(
+ /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16(kTitle2),
+ GURL(kUrl2));
+ // Both bookmarks should be tracked now.
+ ASSERT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 3U);
+ // There should be two local changes now for both entities.
+ ASSERT_THAT(
+ bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries).size(), 2U);
+
+ SimulateCommitResponseForAllLocalChanges();
+
+ // There should be no local changes now.
+ ASSERT_TRUE(
+ bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries).empty());
+
+ // Now update the title of the 2nd node.
+ EXPECT_CALL(*nudge_for_commit_closure(), Run());
+ bookmark_model()->SetTitle(bookmark_node2, base::UTF8ToUTF16(kNewTitle2));
+ // Node 2 should be in the local changes list.
+ std::vector<const SyncedBookmarkTracker::Entity*> local_changes =
+ bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries);
+ ASSERT_THAT(local_changes.size(), 1U);
+ EXPECT_THAT(local_changes[0]->bookmark_node(), Eq(bookmark_node2));
+
+ // Now update the url of the 1st node.
+ EXPECT_CALL(*nudge_for_commit_closure(), Run());
+ bookmark_model()->SetURL(bookmark_node1, GURL(kNewUrl1));
+
+ // Node 1 and 2 should be in the local changes list.
+ local_changes = bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries);
+ ASSERT_THAT(local_changes.size(), 2U);
+
+ // Constuct a set of the bookmark nodes in the local changes.
+ std::set<const bookmarks::BookmarkNode*> nodes_in_local_changes;
+ for (const SyncedBookmarkTracker::Entity* entity : local_changes) {
+ nodes_in_local_changes.insert(entity->bookmark_node());
+ }
+ // Both bookmarks should exist in the set.
+ EXPECT_TRUE(nodes_in_local_changes.find(bookmark_node1) !=
+ nodes_in_local_changes.end());
+ EXPECT_TRUE(nodes_in_local_changes.find(bookmark_node2) !=
+ nodes_in_local_changes.end());
+
+ // Now update metainfo of the 1st node.
+ EXPECT_CALL(*nudge_for_commit_closure(), Run());
+ bookmark_model()->SetNodeMetaInfo(bookmark_node1, "key", "value");
+}
+
+TEST_F(BookmarkModelObserverImplTest,
+ BookmarkMovedShouldUpdateTheTrackerAndNudgeForCommit) {
+ // Build this structure:
+ // bookmark_bar
+ // |- folder1
+ // |- bookmark1
+ const GURL kUrl("http://www.url1.com");
+
+ const bookmarks::BookmarkNode* bookmark_bar_node =
+ bookmark_model()->bookmark_bar_node();
+ const bookmarks::BookmarkNode* folder1_node = bookmark_model()->AddFolder(
+ /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16("folder1"));
+ const bookmarks::BookmarkNode* bookmark1_node = bookmark_model()->AddURL(
+ /*parent=*/folder1_node, /*index=*/0, base::UTF8ToUTF16("bookmark1"),
+ kUrl);
+
+ // Verify number of entities local changes. Should be the same as number of
+ // new nodes.
+ ASSERT_THAT(
+ bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries).size(), 2U);
+
+ // All bookmarks should be tracked now.
+ ASSERT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 3U);
+
+ SimulateCommitResponseForAllLocalChanges();
+
+ // There should be no local changes now.
+ ASSERT_TRUE(
+ bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries).empty());
+
+ // Now change it to this structure.
+ // Build this structure:
+ // bookmark_bar
+ // |- bookmark1
+ // |- folder1
+
+ EXPECT_CALL(*nudge_for_commit_closure(), Run());
+ bookmark_model()->Move(bookmark1_node, bookmark_bar_node, 0);
+ EXPECT_TRUE(PositionOf(bookmark1_node).LessThan(PositionOf(folder1_node)));
+}
+
+TEST_F(BookmarkModelObserverImplTest,
+ ReorderChildrenShouldUpdateTheTrackerAndNudgeForCommit) {
+ const std::string kTitle = "title";
+ const std::string kUrl = "http://www.url.com";
+
+ // Build this structure:
+ // bookmark_bar
+ // |- node0
+ // |- node1
+ // |- node2
+ // |- node3
+ const bookmarks::BookmarkNode* bookmark_bar_node =
+ bookmark_model()->bookmark_bar_node();
+ std::vector<const bookmarks::BookmarkNode*> nodes;
+ for (int i = 0; i < 4; ++i) {
+ nodes.push_back(bookmark_model()->AddURL(
+ /*parent=*/bookmark_bar_node, /*index=*/i, base::UTF8ToUTF16(kTitle),
+ GURL(kUrl)));
+ }
+
+ // Verify number of entities local changes. Should be the same as number of
+ // new nodes.
+ ASSERT_THAT(
+ bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries).size(), 4U);
+
+ // All bookmarks should be tracked now.
+ ASSERT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 5U);
+
+ SimulateCommitResponseForAllLocalChanges();
+
+ // Reorder it to be:
+ // bookmark_bar
+ // |- node1
+ // |- node3
+ // |- node0
+ // |- node2
+ bookmark_model()->ReorderChildren(bookmark_bar_node,
+ {nodes[1], nodes[3], nodes[0], nodes[2]});
+ EXPECT_TRUE(PositionOf(nodes[1]).LessThan(PositionOf(nodes[3])));
+ EXPECT_TRUE(PositionOf(nodes[3]).LessThan(PositionOf(nodes[0])));
+ EXPECT_TRUE(PositionOf(nodes[0]).LessThan(PositionOf(nodes[2])));
+
+ std::vector<const SyncedBookmarkTracker::Entity*> local_changes =
+ bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries);
+ ASSERT_THAT(local_changes.size(), nodes.size());
+
+ // Constuct a set of the bookmark nodes in the local changes.
+ std::set<const bookmarks::BookmarkNode*> nodes_in_local_changes;
+ for (const SyncedBookmarkTracker::Entity* entity : local_changes) {
+ nodes_in_local_changes.insert(entity->bookmark_node());
+ }
+
+ // All reordered nodes should exist in the set of local changes to be
+ // committed.
+ for (const bookmarks::BookmarkNode* node : nodes) {
+ EXPECT_THAT(nodes_in_local_changes.count(node), Ne(0U));
+ }
+}
+
+TEST_F(BookmarkModelObserverImplTest,
+ BookmarkRemovalShouldUpdateTheTrackerAndNudgeForCommit) {
+ // Build this structure:
+ // bookmark_bar
+ // |- folder1
+ // |- bookmark1
+ // |- folder2
+ // |- bookmark2
+ // |- bookmark3
+
+ // and then delete folder2.
+ const GURL kUrl("http://www.url1.com");
+
+ const bookmarks::BookmarkNode* bookmark_bar_node =
+ bookmark_model()->bookmark_bar_node();
+ const bookmarks::BookmarkNode* folder1_node = bookmark_model()->AddFolder(
+ /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16("folder1"));
+ const bookmarks::BookmarkNode* bookmark1_node = bookmark_model()->AddURL(
+ /*parent=*/folder1_node, /*index=*/0, base::UTF8ToUTF16("bookmark1"),
+ kUrl);
+ const bookmarks::BookmarkNode* folder2_node = bookmark_model()->AddFolder(
+ /*parent=*/folder1_node, /*index=*/1, base::UTF8ToUTF16("folder2"));
+ const bookmarks::BookmarkNode* bookmark2_node = bookmark_model()->AddURL(
+ /*parent=*/folder2_node, /*index=*/0, base::UTF8ToUTF16("bookmark2"),
+ kUrl);
+ const bookmarks::BookmarkNode* bookmark3_node = bookmark_model()->AddURL(
+ /*parent=*/folder2_node, /*index=*/1, base::UTF8ToUTF16("bookmark3"),
+ kUrl);
+
+ // All bookmarks should be tracked now.
+ ASSERT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 6U);
+
+ SimulateCommitResponseForAllLocalChanges();
+
+ // There should be no local changes now.
+ ASSERT_TRUE(
+ bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries).empty());
+
+ const SyncedBookmarkTracker::Entity* folder2_entity =
+ bookmark_tracker()->GetEntityForBookmarkNode(folder2_node);
+ const SyncedBookmarkTracker::Entity* bookmark2_entity =
+ bookmark_tracker()->GetEntityForBookmarkNode(bookmark2_node);
+ const SyncedBookmarkTracker::Entity* bookmark3_entity =
+ bookmark_tracker()->GetEntityForBookmarkNode(bookmark3_node);
+
+ ASSERT_FALSE(folder2_entity->metadata()->is_deleted());
+ ASSERT_FALSE(bookmark2_entity->metadata()->is_deleted());
+ ASSERT_FALSE(bookmark3_entity->metadata()->is_deleted());
+
+ const std::string& folder2_entity_id =
+ folder2_entity->metadata()->server_id();
+ const std::string& bookmark2_entity_id =
+ bookmark2_entity->metadata()->server_id();
+ const std::string& bookmark3_entity_id =
+ bookmark3_entity->metadata()->server_id();
+ // Delete folder2.
+ EXPECT_CALL(*nudge_for_commit_closure(), Run());
+ bookmark_model()->Remove(folder2_node);
+
+ // folder2, bookmark2, and bookmark3 should be marked deleted.
+ EXPECT_TRUE(bookmark_tracker()
+ ->GetEntityForSyncId(folder2_entity_id)
+ ->metadata()
+ ->is_deleted());
+ EXPECT_TRUE(bookmark_tracker()
+ ->GetEntityForSyncId(bookmark2_entity_id)
+ ->metadata()
+ ->is_deleted());
+ EXPECT_TRUE(bookmark_tracker()
+ ->GetEntityForSyncId(bookmark3_entity_id)
+ ->metadata()
+ ->is_deleted());
+
+ // folder2, bookmark2, and bookmark3 should be in the local changes list.
+ std::vector<const SyncedBookmarkTracker::Entity*> local_changes =
+ bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries);
+ ASSERT_THAT(local_changes.size(), 3U);
+
+ // All deleted nodes entities should exist in the set of local changes to be
+ // committed and folder2 deletion should be the last one (after all children
+ // deletions).
+ EXPECT_THAT(
+ local_changes,
+ ElementsAre(bookmark_tracker()->GetEntityForSyncId(bookmark2_entity_id),
+ bookmark_tracker()->GetEntityForSyncId(bookmark3_entity_id),
+ bookmark_tracker()->GetEntityForSyncId(folder2_entity_id)));
+
+ // folder1 and bookmark1 are still tracked.
+ EXPECT_TRUE(bookmark_tracker()->GetEntityForBookmarkNode(folder1_node));
+ EXPECT_TRUE(bookmark_tracker()->GetEntityForBookmarkNode(bookmark1_node));
+}
+
+TEST_F(BookmarkModelObserverImplTest,
+ BookmarkCreationAndRemovalShouldRequireTwoCommitResponsesBeforeRemoval) {
+ const bookmarks::BookmarkNode* bookmark_bar_node =
+ bookmark_model()->bookmark_bar_node();
+ const bookmarks::BookmarkNode* folder_node = bookmark_model()->AddFolder(
+ /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16("folder"));
+
+ // Node should be tracked now.
+ ASSERT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 2U);
+ const std::string id = bookmark_tracker()
+ ->GetEntityForBookmarkNode(folder_node)
+ ->metadata()
+ ->server_id();
+ ASSERT_THAT(
+ bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries).size(), 1U);
+
+ // Remove the folder.
+ bookmark_model()->Remove(folder_node);
+
+ // Simulate a commit response for the first commit request (the creation).
+ // Don't simulate change in id for simplcity.
+ bookmark_tracker()->UpdateUponCommitResponse(id, id,
+ /*acked_sequence_number=*/1,
+ /*server_version=*/1);
+
+ // There should still be one local change (the deletion).
+ EXPECT_THAT(
+ bookmark_tracker()->GetEntitiesWithLocalChanges(kMaxEntries).size(), 1U);
+
+ // Entity is still tracked.
+ EXPECT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 2U);
+
+ // Commit the deletion.
+ bookmark_tracker()->UpdateUponCommitResponse(id, id,
+ /*acked_sequence_number=*/2,
+ /*server_version=*/2);
+ // Entity should have been dropped.
+ EXPECT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), 1U);
+}
+
+TEST_F(BookmarkModelObserverImplTest, ShouldPositionSiblings) {
+ const std::string kTitle = "title";
+ const std::string kUrl = "http://www.url.com";
+
+ // Build this structure:
+ // bookmark_bar
+ // |- node1
+ // |- node2
+ // Expectation:
+ // p1 < p2
+
+ const bookmarks::BookmarkNode* bookmark_bar_node =
+ bookmark_model()->bookmark_bar_node();
+ const bookmarks::BookmarkNode* bookmark_node1 = bookmark_model()->AddURL(
+ /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16(kTitle),
+ GURL(kUrl));
+
+ const bookmarks::BookmarkNode* bookmark_node2 = bookmark_model()->AddURL(
+ /*parent=*/bookmark_bar_node, /*index=*/1, base::UTF8ToUTF16(kTitle),
+ GURL(kUrl));
+
+ EXPECT_TRUE(PositionOf(bookmark_node1).LessThan(PositionOf(bookmark_node2)));
+
+ // Now insert node3 at index 1 to build this structure:
+ // bookmark_bar
+ // |- node1
+ // |- node3
+ // |- node2
+ // Expectation:
+ // p1 < p2 (still holds)
+ // p1 < p3
+ // p3 < p2
+
+ const bookmarks::BookmarkNode* bookmark_node3 = bookmark_model()->AddURL(
+ /*parent=*/bookmark_bar_node, /*index=*/1, base::UTF8ToUTF16(kTitle),
+ GURL(kUrl));
+ EXPECT_THAT(bookmark_tracker()->TrackedEntitiesCountForTest(), Eq(4U));
+
+ EXPECT_TRUE(PositionOf(bookmark_node1).LessThan(PositionOf(bookmark_node2)));
+ EXPECT_TRUE(PositionOf(bookmark_node1).LessThan(PositionOf(bookmark_node3)));
+ EXPECT_TRUE(PositionOf(bookmark_node3).LessThan(PositionOf(bookmark_node2)));
+}
+
+} // namespace
+
+} // namespace sync_bookmarks
diff --git a/chromium/components/sync_bookmarks/bookmark_model_type_controller.cc b/chromium/components/sync_bookmarks/bookmark_model_type_controller.cc
deleted file mode 100644
index 678334e0eb3..00000000000
--- a/chromium/components/sync_bookmarks/bookmark_model_type_controller.cc
+++ /dev/null
@@ -1,177 +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/sync_bookmarks/bookmark_model_type_controller.h"
-
-#include <utility>
-
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/bookmarks/browser/bookmark_model.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/sync/driver/sync_client.h"
-#include "components/sync/driver/sync_service.h"
-#include "components/sync/engine/model_type_configurer.h"
-#include "components/sync/engine/model_type_processor_proxy.h"
-#include "components/sync/model/sync_error.h"
-#include "components/sync/protocol/model_type_state.pb.h"
-#include "components/sync/protocol/sync.pb.h"
-#include "components/sync/syncable/directory.h"
-#include "components/sync/syncable/user_share.h"
-#include "components/sync_bookmarks/bookmark_model_type_processor.h"
-
-using syncer::SyncError;
-
-namespace sync_bookmarks {
-
-BookmarkModelTypeController::BookmarkModelTypeController(
- syncer::SyncClient* sync_client)
- : DataTypeController(syncer::BOOKMARKS),
- sync_client_(sync_client),
- state_(NOT_RUNNING) {}
-
-BookmarkModelTypeController::~BookmarkModelTypeController() = default;
-
-bool BookmarkModelTypeController::ShouldLoadModelBeforeConfigure() const {
- DCHECK(CalledOnValidThread());
- return true;
-}
-
-void BookmarkModelTypeController::BeforeLoadModels(
- syncer::ModelTypeConfigurer* configurer) {
- DCHECK(CalledOnValidThread());
-}
-
-void BookmarkModelTypeController::LoadModels(
- const ModelLoadCallback& model_load_callback) {
- DCHECK(CalledOnValidThread());
- if (state() != NOT_RUNNING) {
- model_load_callback.Run(type(),
- SyncError(FROM_HERE, SyncError::DATATYPE_ERROR,
- "Model already running", type()));
- return;
- }
-
- state_ = MODEL_STARTING;
-
- if (DependenciesLoaded()) {
- state_ = MODEL_LOADED;
- model_load_callback.Run(type(), SyncError());
- } else {
- // TODO(pavely): Subscribe for BookmarkModel and HistoryService
- // notifications.
- NOTIMPLEMENTED();
- }
-}
-
-void BookmarkModelTypeController::RegisterWithBackend(
- base::Callback<void(bool)> set_downloaded,
- syncer::ModelTypeConfigurer* configurer) {
- DCHECK(CalledOnValidThread());
- if (activated_)
- return;
- DCHECK(configurer);
- std::unique_ptr<syncer::ActivationContext> activation_context =
- PrepareActivationContext();
- set_downloaded.Run(activation_context->model_type_state.initial_sync_done());
- configurer->ActivateNonBlockingDataType(type(),
- std::move(activation_context));
- activated_ = true;
-}
-
-void BookmarkModelTypeController::StartAssociating(
- const StartCallback& start_callback) {
- DCHECK(CalledOnValidThread());
- DCHECK(!start_callback.is_null());
- DCHECK_EQ(MODEL_LOADED, state_);
-
- state_ = RUNNING;
-
- // There is no association, just call back promptly.
- syncer::SyncMergeResult merge_result(type());
- start_callback.Run(OK, merge_result, merge_result);
-}
-
-void BookmarkModelTypeController::ActivateDataType(
- syncer::ModelTypeConfigurer* configurer) {
- DCHECK(CalledOnValidThread());
- DCHECK(configurer);
- DCHECK_EQ(RUNNING, state_);
-}
-
-void BookmarkModelTypeController::DeactivateDataType(
- syncer::ModelTypeConfigurer* configurer) {
- DCHECK(CalledOnValidThread());
- if (activated_) {
- configurer->DeactivateNonBlockingDataType(type());
- activated_ = false;
- }
-}
-
-void BookmarkModelTypeController::Stop() {
- DCHECK(CalledOnValidThread());
- NOTIMPLEMENTED();
-}
-
-syncer::DataTypeController::State BookmarkModelTypeController::state() const {
- DCHECK(CalledOnValidThread());
- return state_;
-}
-
-void BookmarkModelTypeController::GetAllNodes(
- const AllNodesCallback& callback) {
- DCHECK(CalledOnValidThread());
- NOTIMPLEMENTED();
-}
-
-void BookmarkModelTypeController::GetStatusCounters(
- const StatusCountersCallback& callback) {
- DCHECK(CalledOnValidThread());
- NOTIMPLEMENTED();
-}
-
-void BookmarkModelTypeController::RecordMemoryUsageHistogram() {
- DCHECK(CalledOnValidThread());
- NOTIMPLEMENTED();
-}
-
-bool BookmarkModelTypeController::DependenciesLoaded() {
- DCHECK(CalledOnValidThread());
- bookmarks::BookmarkModel* bookmark_model = sync_client_->GetBookmarkModel();
- if (!bookmark_model || !bookmark_model->loaded())
- return false;
-
- history::HistoryService* history_service = sync_client_->GetHistoryService();
- if (!history_service || !history_service->BackendLoaded())
- return false;
-
- return true;
-}
-
-std::unique_ptr<syncer::ActivationContext>
-BookmarkModelTypeController::PrepareActivationContext() {
- DCHECK(!model_type_processor_);
-
- syncer::UserShare* user_share =
- sync_client_->GetSyncService()->GetUserShare();
- syncer::syncable::Directory* directory = user_share->directory.get();
-
- std::unique_ptr<syncer::ActivationContext> activation_context =
- std::make_unique<syncer::ActivationContext>();
-
- directory->GetDownloadProgress(
- type(), activation_context->model_type_state.mutable_progress_marker());
- activation_context->model_type_state.set_initial_sync_done(
- directory->InitialSyncEndedForType(type()));
- // TODO(pavely): Populate model_type_state.type_context.
-
- model_type_processor_ =
- std::make_unique<BookmarkModelTypeProcessor>(sync_client_);
- activation_context->type_processor =
- std::make_unique<syncer::ModelTypeProcessorProxy>(
- model_type_processor_->GetWeakPtr(),
- base::ThreadTaskRunnerHandle::Get());
- return activation_context;
-}
-
-} // namespace sync_bookmarks
diff --git a/chromium/components/sync_bookmarks/bookmark_model_type_controller.h b/chromium/components/sync_bookmarks/bookmark_model_type_controller.h
deleted file mode 100644
index 42451be5466..00000000000
--- a/chromium/components/sync_bookmarks/bookmark_model_type_controller.h
+++ /dev/null
@@ -1,75 +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_SYNC_BOOKMARKS_BOOKMARK_MODEL_TYPE_CONTROLLER_H_
-#define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_TYPE_CONTROLLER_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "components/sync/driver/data_type_controller.h"
-#include "components/sync/engine/activation_context.h"
-
-namespace syncer {
-class SyncClient;
-} // namespace syncer
-
-namespace sync_bookmarks {
-
-class BookmarkModelTypeProcessor;
-
-// A class that manages the startup and shutdown of bookmark sync implemented
-// through USS APIs.
-class BookmarkModelTypeController : public syncer::DataTypeController {
- public:
- explicit BookmarkModelTypeController(syncer::SyncClient* sync_client);
- ~BookmarkModelTypeController() override;
-
- // syncer::DataTypeController implementation.
- bool ShouldLoadModelBeforeConfigure() const override;
- void BeforeLoadModels(syncer::ModelTypeConfigurer* configurer) override;
- void LoadModels(const ModelLoadCallback& model_load_callback) override;
- void RegisterWithBackend(base::Callback<void(bool)> set_downloaded,
- syncer::ModelTypeConfigurer* configurer) override;
- void StartAssociating(const StartCallback& start_callback) override;
- void ActivateDataType(syncer::ModelTypeConfigurer* configurer) override;
- void DeactivateDataType(syncer::ModelTypeConfigurer* configurer) override;
- void Stop() override;
- State state() const override;
- void GetAllNodes(const AllNodesCallback& callback) override;
- void GetStatusCounters(const StatusCountersCallback& callback) override;
- void RecordMemoryUsageHistogram() override;
-
- private:
- friend class BookmarkModelTypeControllerTest;
-
- // Returns true if both BookmarkModel and HistoryService are loaded.
- bool DependenciesLoaded();
-
- // Reads ModelTypeState from storage and creates BookmarkModelTypeProcessor.
- std::unique_ptr<syncer::ActivationContext> PrepareActivationContext();
-
- // SyncClient provides access to BookmarkModel, HistoryService and
- // SyncService.
- syncer::SyncClient* sync_client_;
-
- // State of this datatype controller.
- State state_;
-
- // BookmarkModelTypeProcessor handles communications between sync engine and
- // BookmarkModel/HistoryService.
- std::unique_ptr<BookmarkModelTypeProcessor> model_type_processor_;
-
- // This is a hack to prevent reconfigurations from crashing, because USS
- // activation is not idempotent. RegisterWithBackend only needs to actually do
- // something the first time after the type is enabled.
- // TODO(crbug.com/647505): Remove this once the DTM handles things better.
- bool activated_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(BookmarkModelTypeController);
-};
-
-} // namespace sync_bookmarks
-
-#endif // COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_TYPE_CONTROLLER_H_
diff --git a/chromium/components/sync_bookmarks/bookmark_model_type_controller_unittest.cc b/chromium/components/sync_bookmarks/bookmark_model_type_controller_unittest.cc
deleted file mode 100644
index 8d63337a0a2..00000000000
--- a/chromium/components/sync_bookmarks/bookmark_model_type_controller_unittest.cc
+++ /dev/null
@@ -1,244 +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/sync_bookmarks/bookmark_model_type_controller.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/message_loop/message_loop.h"
-#include "components/bookmarks/browser/bookmark_model.h"
-#include "components/bookmarks/test/test_bookmark_client.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/sync/base/model_type.h"
-#include "components/sync/driver/data_type_controller.h"
-#include "components/sync/driver/data_type_controller_mock.h"
-#include "components/sync/driver/fake_sync_client.h"
-#include "components/sync/driver/fake_sync_service.h"
-#include "components/sync/engine/model_type_configurer.h"
-#include "components/sync/model/sync_error.h"
-#include "components/sync/model/sync_merge_result.h"
-#include "components/sync/syncable/directory.h"
-#include "components/sync/syncable/test_user_share.h"
-#include "components/sync/test/engine/test_syncable_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using syncer::DataTypeController;
-using syncer::ModelType;
-using syncer::SyncService;
-using syncer::UserShare;
-using testing::_;
-
-namespace sync_bookmarks {
-
-namespace {
-
-// Fake specializations for BookmarModelTypeController's external dependencies.
-
-class TestHistoryService : public history::HistoryService {
- public:
- bool BackendLoaded() override { return true; }
-};
-
-class TestSyncClient : public syncer::FakeSyncClient {
- public:
- TestSyncClient(bookmarks::BookmarkModel* bookmark_model,
- history::HistoryService* history_service,
- SyncService* sync_service)
- : bookmark_model_(bookmark_model),
- history_service_(history_service),
- sync_service_(sync_service) {}
-
- bookmarks::BookmarkModel* GetBookmarkModel() override {
- return bookmark_model_;
- }
-
- history::HistoryService* GetHistoryService() override {
- return history_service_;
- }
-
- SyncService* GetSyncService() override { return sync_service_; }
-
- private:
- bookmarks::BookmarkModel* bookmark_model_;
- history::HistoryService* history_service_;
- SyncService* sync_service_;
-};
-
-class TestSyncService : public syncer::FakeSyncService {
- public:
- explicit TestSyncService(UserShare* user_share) : user_share_(user_share) {}
-
- UserShare* GetUserShare() const override { return user_share_; }
-
- private:
- UserShare* user_share_;
-};
-
-class TestModelTypeConfigurer : public syncer::ModelTypeConfigurer {
- public:
- TestModelTypeConfigurer() {}
- ~TestModelTypeConfigurer() override {}
-
- void ConfigureDataTypes(ConfigureParams params) override {}
-
- void RegisterDirectoryDataType(ModelType type,
- syncer::ModelSafeGroup group) override {}
-
- void UnregisterDirectoryDataType(ModelType type) override {}
-
- void ActivateDirectoryDataType(
- ModelType type,
- syncer::ModelSafeGroup group,
- syncer::ChangeProcessor* change_processor) override {}
-
- void DeactivateDirectoryDataType(ModelType type) override {}
-
- void ActivateNonBlockingDataType(
- ModelType type,
- std::unique_ptr<syncer::ActivationContext> activation_context) override {
- activation_context_ = std::move(activation_context);
- }
-
- void DeactivateNonBlockingDataType(ModelType type) override {}
-
- syncer::ActivationContext* activation_context() {
- return activation_context_.get();
- }
-
- private:
- // ActivationContext captured in ActivateNonBlockingDataType call.
- std::unique_ptr<syncer::ActivationContext> activation_context_;
-};
-
-} // namespace
-
-class BookmarkModelTypeControllerTest : public testing::Test {
- public:
- void SetUp() override {
- bookmark_model_ = bookmarks::TestBookmarkClient::CreateModel();
- history_service_ = std::make_unique<TestHistoryService>();
- test_user_share_.SetUp();
- sync_service_ =
- std::make_unique<TestSyncService>(test_user_share_.user_share());
- sync_client_ = std::make_unique<TestSyncClient>(
- bookmark_model_.get(), history_service_.get(), sync_service_.get());
- controller_ =
- std::make_unique<BookmarkModelTypeController>(sync_client_.get());
- }
-
- void TearDown() override { test_user_share_.TearDown(); }
-
- protected:
- BookmarkModelTypeController* controller() { return controller_.get(); }
-
- syncer::UserShare* user_share() { return test_user_share_.user_share(); }
-
- syncer::ModelLoadCallbackMock& model_load_callback() {
- return model_load_callback_;
- }
-
- syncer::StartCallbackMock& start_callback() { return start_callback_; }
-
- syncer::ActivationContext* activation_context() {
- return model_type_configurer_.activation_context();
- }
-
- void LoadModels() {
- controller()->LoadModels(
- base::Bind(&syncer::ModelLoadCallbackMock::Run,
- base::Unretained(&model_load_callback_)));
- }
-
- static void CaptureBoolean(bool* value_dest, bool value) {
- *value_dest = value;
- }
-
- void CallRegisterWithBackend(bool* initial_sync_done) {
- controller()->RegisterWithBackend(
- base::Bind(&BookmarkModelTypeControllerTest::CaptureBoolean,
- initial_sync_done),
- &model_type_configurer_);
- }
-
- void StartAssociating() {
- controller()->StartAssociating(base::Bind(
- &syncer::StartCallbackMock::Run, base::Unretained(&start_callback_)));
- }
-
- private:
- base::MessageLoop message_loop_;
-
- std::unique_ptr<bookmarks::BookmarkModel> bookmark_model_;
- std::unique_ptr<history::HistoryService> history_service_;
- syncer::TestUserShare test_user_share_;
- std::unique_ptr<SyncService> sync_service_;
- std::unique_ptr<TestSyncClient> sync_client_;
- TestModelTypeConfigurer model_type_configurer_;
-
- syncer::ModelLoadCallbackMock model_load_callback_;
- syncer::StartCallbackMock start_callback_;
-
- std::unique_ptr<BookmarkModelTypeController> controller_;
-};
-
-// Tests model type and initial state of bookmarks controller.
-TEST_F(BookmarkModelTypeControllerTest, InitialState) {
- EXPECT_EQ(syncer::BOOKMARKS, controller()->type());
- EXPECT_EQ(DataTypeController::NOT_RUNNING, controller()->state());
- EXPECT_TRUE(controller()->ShouldLoadModelBeforeConfigure());
-}
-
-// Tests that call to LoadModels triggers ModelLoadCallback and advances DTC
-// state.
-TEST_F(BookmarkModelTypeControllerTest, LoadModels) {
- EXPECT_CALL(model_load_callback(), Run(_, _));
- LoadModels();
- EXPECT_EQ(DataTypeController::MODEL_LOADED, controller()->state());
-}
-
-// Tests that registering with backend from clean state reports that initial
-// sync is not done and progress marker is empty.
-TEST_F(BookmarkModelTypeControllerTest, RegisterWithBackend_CleanState) {
- LoadModels();
- bool initial_sync_done = false;
- CallRegisterWithBackend(&initial_sync_done);
- EXPECT_FALSE(initial_sync_done);
- EXPECT_FALSE(activation_context()->model_type_state.initial_sync_done());
- EXPECT_TRUE(
- activation_context()->model_type_state.progress_marker().token().empty());
- EXPECT_NE(nullptr, activation_context()->type_processor);
-}
-
-// Tests that registering with backend from valid state returns non-empty
-// progress marker.
-TEST_F(BookmarkModelTypeControllerTest, RegisterWithBackend) {
- syncer::TestUserShare::CreateRoot(syncer::BOOKMARKS, user_share());
- sync_pb::DataTypeProgressMarker progress_marker =
- syncer::syncable::BuildProgress(syncer::BOOKMARKS);
- user_share()->directory->SetDownloadProgress(syncer::BOOKMARKS,
- progress_marker);
- LoadModels();
- bool initial_sync_done = false;
- CallRegisterWithBackend(&initial_sync_done);
- EXPECT_TRUE(initial_sync_done);
- EXPECT_TRUE(activation_context()->model_type_state.initial_sync_done());
- EXPECT_EQ(progress_marker.SerializeAsString(),
- activation_context()
- ->model_type_state.progress_marker()
- .SerializeAsString());
- EXPECT_NE(nullptr, activation_context()->type_processor);
-}
-
-// Tests that call to StartAssociating triggers StartCallback and adjusts DTC
-// state.
-TEST_F(BookmarkModelTypeControllerTest, StartAssociating) {
- LoadModels();
- EXPECT_CALL(start_callback(), Run(_, _, _));
- StartAssociating();
- EXPECT_EQ(DataTypeController::RUNNING, controller()->state());
-}
-
-} // namespace sync_bookmarks
diff --git a/chromium/components/sync_bookmarks/bookmark_model_type_processor.cc b/chromium/components/sync_bookmarks/bookmark_model_type_processor.cc
index d65bec249b0..419b4ed8b33 100644
--- a/chromium/components/sync_bookmarks/bookmark_model_type_processor.cc
+++ b/chromium/components/sync_bookmarks/bookmark_model_type_processor.cc
@@ -7,103 +7,42 @@
#include <utility>
#include "base/callback.h"
-#include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/bookmark_node.h"
+#include "components/bookmarks/browser/bookmark_utils.h"
#include "components/sync/base/model_type.h"
-#include "components/sync/driver/sync_client.h"
#include "components/sync/engine/commit_queue.h"
+#include "components/sync/engine/model_type_processor_proxy.h"
+#include "components/sync/model/data_type_activation_request.h"
+#include "components/sync/protocol/bookmark_model_metadata.pb.h"
+#include "components/sync_bookmarks/bookmark_local_changes_builder.h"
+#include "components/sync_bookmarks/bookmark_model_observer_impl.h"
+#include "components/sync_bookmarks/bookmark_remote_updates_handler.h"
#include "components/undo/bookmark_undo_utils.h"
namespace sync_bookmarks {
namespace {
-// The sync protocol identifies top-level entities by means of well-known tags,
-// (aka server defined tags) which should not be confused with titles or client
-// tags that aren't supported by bookmarks (at the time of writing). Each tag
-// corresponds to a singleton instance of a particular top-level node in a
-// user's share; the tags are consistent across users. The tags allow us to
-// locate the specific folders whose contents we care about synchronizing,
-// without having to do a lookup by name or path. The tags should not be made
-// user-visible. For example, the tag "bookmark_bar" represents the permanent
-// node for bookmarks bar in Chrome. The tag "other_bookmarks" represents the
-// permanent folder Other Bookmarks in Chrome.
-//
-// It is the responsibility of something upstream (at time of writing, the sync
-// server) to create these tagged nodes when initializing sync for the first
-// time for a user. Thus, once the backend finishes initializing, the
-// ProfileSyncService can rely on the presence of tagged nodes.
-const char kBookmarkBarTag[] = "bookmark_bar";
-const char kMobileBookmarksTag[] = "synced_bookmarks";
-const char kOtherBookmarksTag[] = "other_bookmarks";
-
-// Id is created by concatenating the specifics field number and the server tag
-// similar to LookbackServerEntity::CreateId() that uses
-// GetSpecificsFieldNumberFromModelType() to compute the field number.
-static const char kBookmarksRootId[] = "32904_google_chrome_bookmarks";
-
-// |sync_entity| must contain a bookmark specifics.
-// Metainfo entries must have unique keys.
-bookmarks::BookmarkNode::MetaInfoMap GetBookmarkMetaInfo(
- const syncer::EntityData& sync_entity) {
- const sync_pb::BookmarkSpecifics& specifics =
- sync_entity.specifics.bookmark();
- bookmarks::BookmarkNode::MetaInfoMap meta_info_map;
- for (const sync_pb::MetaInfo& meta_info : specifics.meta_info()) {
- meta_info_map[meta_info.key()] = meta_info.value();
- }
- DCHECK_EQ(static_cast<size_t>(specifics.meta_info_size()),
- meta_info_map.size());
- return meta_info_map;
-}
-
-// Creates a bookmark node under the given parent node from the given sync node.
-// Returns the newly created node. |sync_entity| must contain a bookmark
-// specifics with Metainfo entries having unique keys.
-const bookmarks::BookmarkNode* CreateBookmarkNode(
- const syncer::EntityData& sync_entity,
- const bookmarks::BookmarkNode* parent,
- bookmarks::BookmarkModel* model,
- syncer::SyncClient* sync_client,
- int index) {
- DCHECK(parent);
- DCHECK(model);
- DCHECK(sync_client);
-
- const sync_pb::BookmarkSpecifics& specifics =
- sync_entity.specifics.bookmark();
- bookmarks::BookmarkNode::MetaInfoMap metainfo =
- GetBookmarkMetaInfo(sync_entity);
- if (sync_entity.is_folder) {
- return model->AddFolderWithMetaInfo(
- parent, index, base::UTF8ToUTF16(specifics.title()), &metainfo);
- }
- // 'creation_time_us' was added in M24. Assume a time of 0 means now.
- const int64_t create_time_us = specifics.creation_time_us();
- base::Time create_time =
- (create_time_us == 0)
- ? base::Time::Now()
- : base::Time::FromDeltaSinceWindowsEpoch(
- // Use FromDeltaSinceWindowsEpoch because create_time_us has
- // always used the Windows epoch.
- base::TimeDelta::FromMicroseconds(create_time_us));
- return model->AddURLWithCreationTimeAndMetaInfo(
- parent, index, base::UTF8ToUTF16(specifics.title()),
- GURL(specifics.url()), create_time, &metainfo);
- // TODO(crbug.com/516866): Add the favicon related code.
-}
-
class ScopedRemoteUpdateBookmarks {
public:
- // |bookmark_model| must not be null and must outlive this object.
- explicit ScopedRemoteUpdateBookmarks(syncer::SyncClient* const sync_client)
- : sync_client_(sync_client),
- suspend_undo_(sync_client->GetBookmarkUndoServiceIfExists()) {
+ // |bookmark_model|, |bookmark_undo_service| and |observer| must not be null
+ // and must outlive this object.
+ ScopedRemoteUpdateBookmarks(bookmarks::BookmarkModel* bookmark_model,
+ BookmarkUndoService* bookmark_undo_service,
+ bookmarks::BookmarkModelObserver* observer)
+ : bookmark_model_(bookmark_model),
+ suspend_undo_(bookmark_undo_service),
+ observer_(observer) {
// Notify UI intensive observers of BookmarkModel that we are about to make
// potentially significant changes to it, so the updates may be batched. For
// example, on Mac, the bookmarks bar displays animations when bookmark
// items are added or deleted.
- sync_client_->GetBookmarkModel()->BeginExtensiveChanges();
+ DCHECK(bookmark_model_);
+ bookmark_model_->BeginExtensiveChanges();
+ // Shouldn't be notified upon changes due to sync.
+ bookmark_model_->RemoveObserver(observer_);
}
~ScopedRemoteUpdateBookmarks() {
@@ -111,32 +50,45 @@ class ScopedRemoteUpdateBookmarks {
// applied, and that they may now be consumed. This prevents issues like the
// one described in https://crbug.com/281562, where old and new items on the
// bookmarks bar would overlap.
- sync_client_->GetBookmarkModel()->EndExtensiveChanges();
+ bookmark_model_->EndExtensiveChanges();
+ bookmark_model_->AddObserver(observer_);
}
private:
- syncer::SyncClient* const sync_client_;
+ bookmarks::BookmarkModel* const bookmark_model_;
// Changes made to the bookmark model due to sync should not be undoable.
ScopedSuspendBookmarkUndo suspend_undo_;
+ bookmarks::BookmarkModelObserver* const observer_;
+
DISALLOW_COPY_AND_ASSIGN(ScopedRemoteUpdateBookmarks);
};
+
} // namespace
BookmarkModelTypeProcessor::BookmarkModelTypeProcessor(
- syncer::SyncClient* sync_client)
- : sync_client_(sync_client),
- bookmark_model_(sync_client->GetBookmarkModel()),
- weak_ptr_factory_(this) {}
+ BookmarkUndoService* bookmark_undo_service)
+ : bookmark_undo_service_(bookmark_undo_service), weak_ptr_factory_(this) {}
-BookmarkModelTypeProcessor::~BookmarkModelTypeProcessor() = default;
+BookmarkModelTypeProcessor::~BookmarkModelTypeProcessor() {
+ if (bookmark_model_ && bookmark_model_observer_) {
+ bookmark_model_->RemoveObserver(bookmark_model_observer_.get());
+ }
+}
void BookmarkModelTypeProcessor::ConnectSync(
std::unique_ptr<syncer::CommitQueue> worker) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!worker_);
+ DCHECK(bookmark_model_);
+
worker_ = std::move(worker);
+
+ // |bookmark_tracker_| is instantiated only after initial sync is done.
+ if (bookmark_tracker_) {
+ NudgeForCommitIfNeeded();
+ }
}
void BookmarkModelTypeProcessor::DisconnectSync() {
@@ -146,252 +98,242 @@ void BookmarkModelTypeProcessor::DisconnectSync() {
void BookmarkModelTypeProcessor::GetLocalChanges(
size_t max_entries,
- const GetLocalChangesCallback& callback) {
+ GetLocalChangesCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- syncer::CommitRequestDataList local_changes;
- callback.Run(std::move(local_changes));
- NOTIMPLEMENTED();
+ BookmarkLocalChangesBuilder builder(bookmark_tracker_.get());
+ std::vector<syncer::CommitRequestData> local_changes =
+ builder.BuildCommitRequests(max_entries);
+ std::move(callback).Run(std::move(local_changes));
}
void BookmarkModelTypeProcessor::OnCommitCompleted(
const sync_pb::ModelTypeState& type_state,
const syncer::CommitResponseDataList& response_list) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- NOTIMPLEMENTED();
+
+ for (const syncer::CommitResponseData& response : response_list) {
+ // In order to save space, |response.id_in_request| is written when it's
+ // different from |response.id|. If it's empty, then there was no id change
+ // during the commit, and |response.id| carries both the old and new ids.
+ const std::string& old_sync_id =
+ response.id_in_request.empty() ? response.id : response.id_in_request;
+ bookmark_tracker_->UpdateUponCommitResponse(old_sync_id, response.id,
+ response.sequence_number,
+ response.response_version);
+ }
+ bookmark_tracker_->set_model_type_state(
+ std::make_unique<sync_pb::ModelTypeState>(type_state));
+ schedule_save_closure_.Run();
}
void BookmarkModelTypeProcessor::OnUpdateReceived(
const sync_pb::ModelTypeState& model_type_state,
const syncer::UpdateResponseDataList& updates) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
- ScopedRemoteUpdateBookmarks update_bookmarks(sync_client_);
-
- for (const syncer::UpdateResponseData* update : ReorderUpdates(updates)) {
- const syncer::EntityData& update_data = update->entity.value();
- // TODO(crbug.com/516866): Check |update_data| for sanity.
- // 1. Has bookmark specifics or no specifics in case of delete.
- // 2. All meta info entries in the specifics have unique keys.
- const SyncedBookmarkTracker::Entity* tracked_entity =
- bookmark_tracker_.GetEntityForSyncId(update_data.id);
- if (update_data.is_deleted()) {
- ProcessRemoteDelete(update_data, tracked_entity);
- continue;
- }
- if (!tracked_entity) {
- ProcessRemoteCreate(update_data);
- continue;
- }
- // Ignore changes to the permanent nodes (e.g. bookmarks bar). We only care
- // about their children.
- if (bookmark_model_->is_permanent_node(tracked_entity->bookmark_node())) {
- continue;
- }
- ProcessRemoteUpdate(update_data, tracked_entity);
+ DCHECK(!model_type_state.cache_guid().empty());
+ DCHECK_EQ(model_type_state.cache_guid(), cache_guid_);
+ DCHECK(model_type_state.initial_sync_done());
+
+ if (!bookmark_tracker_) {
+ // TODO(crbug.com/516866): Implement the merge logic.
+ StartTrackingMetadata(
+ std::vector<NodeMetadataPair>(),
+ std::make_unique<sync_pb::ModelTypeState>(model_type_state));
}
-}
-
-// static
-std::vector<const syncer::UpdateResponseData*>
-BookmarkModelTypeProcessor::ReorderUpdatesForTest(
- const syncer::UpdateResponseDataList& updates) {
- return ReorderUpdates(updates);
+ // TODO(crbug.com/516866): Set the model type state.
+
+ ScopedRemoteUpdateBookmarks update_bookmarks(
+ bookmark_model_, bookmark_undo_service_, bookmark_model_observer_.get());
+ BookmarkRemoteUpdatesHandler updates_handler(bookmark_model_,
+ bookmark_tracker_.get());
+ updates_handler.Process(updates);
+ // Schedule save just in case one is needed.
+ schedule_save_closure_.Run();
}
const SyncedBookmarkTracker* BookmarkModelTypeProcessor::GetTrackerForTest()
const {
- return &bookmark_tracker_;
+ return bookmark_tracker_.get();
}
-// static
-std::vector<const syncer::UpdateResponseData*>
-BookmarkModelTypeProcessor::ReorderUpdates(
- const syncer::UpdateResponseDataList& updates) {
- // TODO(crbug.com/516866): This is a very simple (hacky) reordering algorithm
- // that assumes no folders exist except the top level permanent ones. This
- // should be fixed before enabling USS for bookmarks.
- std::vector<const syncer::UpdateResponseData*> ordered_updates;
- for (const syncer::UpdateResponseData& update : updates) {
- const syncer::EntityData& update_data = update.entity.value();
- if (update_data.parent_id == "0") {
- continue;
- }
- if (update_data.parent_id == kBookmarksRootId) {
- ordered_updates.push_back(&update);
- }
- }
- for (const syncer::UpdateResponseData& update : updates) {
- const syncer::EntityData& update_data = update.entity.value();
- // Deletions should come last.
- if (update_data.is_deleted()) {
- continue;
- }
- if (update_data.parent_id != "0" &&
- update_data.parent_id != kBookmarksRootId) {
- ordered_updates.push_back(&update);
- }
+std::string BookmarkModelTypeProcessor::EncodeSyncMetadata() const {
+ std::string metadata_str;
+ if (bookmark_tracker_) {
+ sync_pb::BookmarkModelMetadata model_metadata =
+ bookmark_tracker_->BuildBookmarkModelMetadata();
+ model_metadata.SerializeToString(&metadata_str);
}
- // Now add deletions.
- for (const syncer::UpdateResponseData& update : updates) {
- const syncer::EntityData& update_data = update.entity.value();
- if (!update_data.is_deleted()) {
- continue;
- }
- if (update_data.parent_id != "0" &&
- update_data.parent_id != kBookmarksRootId) {
- ordered_updates.push_back(&update);
+ return metadata_str;
+}
+
+void BookmarkModelTypeProcessor::ModelReadyToSync(
+ const std::string& metadata_str,
+ const base::RepeatingClosure& schedule_save_closure,
+ bookmarks::BookmarkModel* model) {
+ DCHECK(model);
+ DCHECK(!bookmark_model_);
+ DCHECK(!bookmark_tracker_);
+ DCHECK(!bookmark_model_observer_);
+
+ bookmark_model_ = model;
+ schedule_save_closure_ = schedule_save_closure;
+
+ sync_pb::BookmarkModelMetadata model_metadata;
+ model_metadata.ParseFromString(metadata_str);
+
+ auto model_type_state = std::make_unique<sync_pb::ModelTypeState>();
+ model_type_state->Swap(model_metadata.mutable_model_type_state());
+
+ if (model_type_state->initial_sync_done()) {
+ std::vector<NodeMetadataPair> nodes_metadata;
+ for (sync_pb::BookmarkMetadata& bookmark_metadata :
+ *model_metadata.mutable_bookmarks_metadata()) {
+ // TODO(crbug.com/516866): Replace with a more efficient way to retrieve
+ // all nodes and store in a map keyed by id instead of doing a lookup for
+ // every id.
+ const bookmarks::BookmarkNode* node = nullptr;
+ if (bookmark_metadata.metadata().is_deleted()) {
+ if (bookmark_metadata.has_id()) {
+ DLOG(ERROR) << "Error when decoding sync metadata: Tombstones "
+ "shouldn't have a bookmark id.";
+ continue;
+ }
+ } else {
+ if (!bookmark_metadata.has_id()) {
+ DLOG(ERROR)
+ << "Error when decoding sync metadata: Bookmark id is missing.";
+ continue;
+ }
+ node = GetBookmarkNodeByID(bookmark_model_, bookmark_metadata.id());
+ if (node == nullptr) {
+ DLOG(ERROR) << "Error when decoding sync metadata: Cannot find the "
+ "bookmark node.";
+ continue;
+ }
+ }
+ auto metadata = std::make_unique<sync_pb::EntityMetadata>();
+ metadata->Swap(bookmark_metadata.mutable_metadata());
+ nodes_metadata.emplace_back(node, std::move(metadata));
}
+ // TODO(crbug.com/516866): Handle local nodes that don't have a
+ // corresponding
+ // metadata.
+ StartTrackingMetadata(std::move(nodes_metadata),
+ std::move(model_type_state));
+ } else if (!model_metadata.bookmarks_metadata().empty()) {
+ DLOG(ERROR)
+ << "Persisted Metadata not empty while initial sync is not done.";
}
- return ordered_updates;
+ ConnectIfReady();
}
-void BookmarkModelTypeProcessor::ProcessRemoteCreate(
- const syncer::EntityData& update_data) {
- // Because the Synced Bookmarks node can be created server side, it's possible
- // it'll arrive at the client as an update. In that case it won't have been
- // associated at startup, the GetChromeNodeFromSyncId call above will return
- // null, and we won't detect it as a permanent node, resulting in us trying to
- // create it here (which will fail). Therefore, we add special logic here just
- // to detect the Synced Bookmarks folder.
- if (update_data.parent_id == kBookmarksRootId) {
- // Associate permanent folders.
- AssociatePermanentFolder(update_data);
- return;
- }
+base::WeakPtr<syncer::ModelTypeControllerDelegate>
+BookmarkModelTypeProcessor::GetWeakPtr() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return weak_ptr_factory_.GetWeakPtr();
+}
- const bookmarks::BookmarkNode* parent_node = GetParentNode(update_data);
- if (!parent_node) {
- // If we cannot find the parent, we can do nothing.
- DLOG(ERROR) << "Could not find parent of node being added."
- << " Node title: " << update_data.specifics.bookmark().title()
- << ", parent id = " << update_data.parent_id;
- return;
- }
- // TODO(crbug.com/516866): This code appends the code to the very end of the
- // list of the children by assigning the index to the
- // parent_node->child_count(). It should instead compute the exact using the
- // unique position information of the new node as well as the siblings.
- const bookmarks::BookmarkNode* bookmark_node =
- CreateBookmarkNode(update_data, parent_node, bookmark_model_,
- sync_client_, parent_node->child_count());
- if (!bookmark_node) {
- // We ignore bookmarks we can't add.
- DLOG(ERROR) << "Failed to create bookmark node with title "
- << update_data.specifics.bookmark().title() << " and url "
- << update_data.specifics.bookmark().url();
- return;
- }
- bookmark_tracker_.Associate(update_data.id, bookmark_node);
- // TODO(crbug.com/516866): Update metadata (e.g. server version,
- // specifics_hash).
+void BookmarkModelTypeProcessor::OnSyncStarting(
+ const syncer::DataTypeActivationRequest& request,
+ StartCallback start_callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(start_callback);
+ DVLOG(1) << "Sync is starting for Bookmarks";
+
+ cache_guid_ = request.cache_guid;
+ start_callback_ = std::move(start_callback);
+ DCHECK(!cache_guid_.empty());
+ ConnectIfReady();
}
-void BookmarkModelTypeProcessor::ProcessRemoteUpdate(
- const syncer::EntityData& update_data,
- const SyncedBookmarkTracker::Entity* tracked_entity) {
- // Can only update existing nodes.
- DCHECK(tracked_entity);
- DCHECK_EQ(tracked_entity,
- bookmark_tracker_.GetEntityForSyncId(update_data.id));
- // Must no be a deletion
- DCHECK(!update_data.is_deleted());
- if (tracked_entity->IsUnsynced()) {
- // TODO(crbug.com/516866): Handle conflict resolution.
+void BookmarkModelTypeProcessor::ConnectIfReady() {
+ // Return if the model isn't ready.
+ if (!bookmark_model_) {
return;
}
- if (tracked_entity->MatchesData(update_data)) {
- // TODO(crbug.com/516866): Update metadata (e.g. server version,
- // specifics_hash).
+ // Return if Sync didn't start yet.
+ if (!start_callback_) {
return;
}
- const bookmarks::BookmarkNode* node = tracked_entity->bookmark_node();
- if (update_data.is_folder != node->is_folder()) {
- DLOG(ERROR) << "Could not update node. Remote node is a "
- << (update_data.is_folder ? "folder" : "bookmark")
- << " while local node is a "
- << (node->is_folder() ? "folder" : "bookmark");
+
+ DCHECK(!cache_guid_.empty());
+
+ if (bookmark_tracker_ &&
+ bookmark_tracker_->model_type_state().cache_guid() != cache_guid_) {
+ // TODO(crbug.com/820049): Properly handle a mismatch between the loaded
+ // cache guid stored in |bookmark_tracker_.model_type_state_| at
+ // DecodeSyncMetadata() and the one received from sync at OnSyncStarting()
+ // stored in |cache_guid_|.
return;
}
- const sync_pb::BookmarkSpecifics& specifics =
- update_data.specifics.bookmark();
- if (!update_data.is_folder) {
- bookmark_model_->SetURL(node, GURL(specifics.url()));
- }
- bookmark_model_->SetTitle(node, base::UTF8ToUTF16(specifics.title()));
- // TODO(crbug.com/516866): Add the favicon related code.
- bookmark_model_->SetNodeMetaInfoMap(node, GetBookmarkMetaInfo(update_data));
- // TODO(crbug.com/516866): Update metadata (e.g. server version,
- // specifics_hash).
- // TODO(crbug.com/516866): Handle reparenting.
- // TODO(crbug.com/516866): Handle the case of moving the bookmark to a new
- // position under the same parent (i.e. change in the unique position)
+ auto activation_context =
+ std::make_unique<syncer::DataTypeActivationResponse>();
+ if (bookmark_tracker_) {
+ activation_context->model_type_state =
+ bookmark_tracker_->model_type_state();
+ } else {
+ sync_pb::ModelTypeState model_type_state;
+ model_type_state.mutable_progress_marker()->set_data_type_id(
+ GetSpecificsFieldNumberFromModelType(syncer::BOOKMARKS));
+ model_type_state.set_cache_guid(cache_guid_);
+ activation_context->model_type_state = model_type_state;
+ }
+ activation_context->type_processor =
+ std::make_unique<syncer::ModelTypeProcessorProxy>(
+ weak_ptr_factory_.GetWeakPtr(),
+ base::SequencedTaskRunnerHandle::Get());
+ std::move(start_callback_).Run(std::move(activation_context));
}
-void BookmarkModelTypeProcessor::ProcessRemoteDelete(
- const syncer::EntityData& update_data,
- const SyncedBookmarkTracker::Entity* tracked_entity) {
- DCHECK(update_data.is_deleted());
-
- DCHECK_EQ(tracked_entity,
- bookmark_tracker_.GetEntityForSyncId(update_data.id));
+void BookmarkModelTypeProcessor::OnSyncStopping(
+ syncer::SyncStopMetadataFate metadata_fate) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ cache_guid_.clear();
+ NOTIMPLEMENTED();
+}
- // Handle corner cases first.
- if (tracked_entity == nullptr) {
- // Local entity doesn't exist and update is tombstone.
- DLOG(WARNING) << "Received remote delete for a non-existing item.";
+void BookmarkModelTypeProcessor::NudgeForCommitIfNeeded() {
+ DCHECK(bookmark_tracker_);
+ // Don't bother sending anything if there's no one to send to.
+ if (!worker_) {
return;
}
- const bookmarks::BookmarkNode* node = tracked_entity->bookmark_node();
- // Ignore changes to the permanent top-level nodes. We only care about
- // their children.
- if (bookmark_model_->is_permanent_node(node)) {
- return;
- }
- // TODO(crbug.com/516866): Allow deletions of non-empty direcoties if makes
- // sense, and recursively delete children.
- if (node->child_count() > 0) {
- DLOG(WARNING) << "Trying to delete a non-empty folder.";
- return;
+ // Nudge worker if there are any entities with local changes.
+ if (bookmark_tracker_->HasLocalChanges()) {
+ worker_->NudgeForCommit();
}
-
- bookmark_model_->Remove(node);
- bookmark_tracker_.Disassociate(update_data.id);
}
-const bookmarks::BookmarkNode* BookmarkModelTypeProcessor::GetParentNode(
- const syncer::EntityData& update_data) const {
- const SyncedBookmarkTracker::Entity* parent_entity =
- bookmark_tracker_.GetEntityForSyncId(update_data.parent_id);
- if (!parent_entity) {
- return nullptr;
- }
- return parent_entity->bookmark_node();
+void BookmarkModelTypeProcessor::StartTrackingMetadata(
+ std::vector<NodeMetadataPair> nodes_metadata,
+ std::unique_ptr<sync_pb::ModelTypeState> model_type_state) {
+ bookmark_tracker_ = std::make_unique<SyncedBookmarkTracker>(
+ std::move(nodes_metadata), std::move(model_type_state));
+
+ bookmark_model_observer_ = std::make_unique<BookmarkModelObserverImpl>(
+ base::BindRepeating(&BookmarkModelTypeProcessor::NudgeForCommitIfNeeded,
+ base::Unretained(this)),
+ bookmark_tracker_.get());
+ bookmark_model_->AddObserver(bookmark_model_observer_.get());
}
-void BookmarkModelTypeProcessor::AssociatePermanentFolder(
- const syncer::EntityData& update_data) {
- DCHECK_EQ(update_data.parent_id, kBookmarksRootId);
-
- const bookmarks::BookmarkNode* permanent_node = nullptr;
- if (update_data.server_defined_unique_tag == kBookmarkBarTag) {
- permanent_node = bookmark_model_->bookmark_bar_node();
- } else if (update_data.server_defined_unique_tag == kOtherBookmarksTag) {
- permanent_node = bookmark_model_->other_node();
- } else if (update_data.server_defined_unique_tag == kMobileBookmarksTag) {
- permanent_node = bookmark_model_->mobile_node();
- }
+void BookmarkModelTypeProcessor::GetAllNodesForDebugging(
+ AllNodesCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ NOTIMPLEMENTED();
+}
- if (permanent_node != nullptr) {
- bookmark_tracker_.Associate(update_data.id, permanent_node);
- }
+void BookmarkModelTypeProcessor::GetStatusCountersForDebugging(
+ StatusCountersCallback callback) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ NOTIMPLEMENTED();
}
-base::WeakPtr<syncer::ModelTypeProcessor>
-BookmarkModelTypeProcessor::GetWeakPtr() {
+void BookmarkModelTypeProcessor::RecordMemoryUsageHistogram() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return weak_ptr_factory_.GetWeakPtr();
+ NOTIMPLEMENTED();
}
} // namespace sync_bookmarks
diff --git a/chromium/components/sync_bookmarks/bookmark_model_type_processor.h b/chromium/components/sync_bookmarks/bookmark_model_type_processor.h
index 892c64ef9da..96c9412e9fa 100644
--- a/chromium/components/sync_bookmarks/bookmark_model_type_processor.h
+++ b/chromium/components/sync_bookmarks/bookmark_model_type_processor.h
@@ -6,17 +6,19 @@
#define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_TYPE_PROCESSOR_H_
#include <memory>
+#include <string>
#include <vector>
+#include "base/callback.h"
+#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "components/sync/engine/model_type_processor.h"
+#include "components/sync/model/model_type_controller_delegate.h"
#include "components/sync_bookmarks/synced_bookmark_tracker.h"
-namespace syncer {
-class SyncClient;
-}
+class BookmarkUndoService;
namespace bookmarks {
class BookmarkModel;
@@ -24,90 +26,109 @@ class BookmarkModel;
namespace sync_bookmarks {
-class BookmarkModelTypeProcessor : public syncer::ModelTypeProcessor {
+class BookmarkModelObserverImpl;
+
+class BookmarkModelTypeProcessor : public syncer::ModelTypeProcessor,
+ public syncer::ModelTypeControllerDelegate {
public:
- // |sync_client| must not be nullptr and must outlive this object.
- explicit BookmarkModelTypeProcessor(syncer::SyncClient* sync_client);
+ // |bookmark_undo_service| must not be nullptr and must outlive this object.
+ explicit BookmarkModelTypeProcessor(
+ BookmarkUndoService* bookmark_undo_service);
~BookmarkModelTypeProcessor() override;
// ModelTypeProcessor implementation.
void ConnectSync(std::unique_ptr<syncer::CommitQueue> worker) override;
void DisconnectSync() override;
void GetLocalChanges(size_t max_entries,
- const GetLocalChangesCallback& callback) override;
+ GetLocalChangesCallback callback) override;
void OnCommitCompleted(
const sync_pb::ModelTypeState& type_state,
const syncer::CommitResponseDataList& response_list) override;
void OnUpdateReceived(const sync_pb::ModelTypeState& type_state,
const syncer::UpdateResponseDataList& updates) override;
- // Public for testing.
- static std::vector<const syncer::UpdateResponseData*> ReorderUpdatesForTest(
- const syncer::UpdateResponseDataList& updates);
+ // ModelTypeControllerDelegate implementation.
+ void OnSyncStarting(const syncer::DataTypeActivationRequest& request,
+ StartCallback start_callback) override;
+ void OnSyncStopping(syncer::SyncStopMetadataFate metadata_fate) override;
+ void GetAllNodesForDebugging(AllNodesCallback callback) override;
+ void GetStatusCountersForDebugging(StatusCountersCallback callback) override;
+ void RecordMemoryUsageHistogram() override;
+
+ // Encodes all sync metadata into a string, representing a state that can be
+ // restored via ModelReadyToSync() below.
+ std::string EncodeSyncMetadata() const;
+
+ // It mainly decodes a BookmarkModelMetadata proto seralized in
+ // |metadata_str|, and uses it to fill in the tracker and the model type state
+ // objects. |model| must not be null and must outlive this object. It is used
+ // to the retrieve the local node ids, and is stored in the processor to be
+ // used for further model operations. |schedule_save_closure| is a repeating
+ // closure used to schedule a save of the bookmark model together with the
+ // metadata.
+ void ModelReadyToSync(const std::string& metadata_str,
+ const base::RepeatingClosure& schedule_save_closure,
+ bookmarks::BookmarkModel* model);
const SyncedBookmarkTracker* GetTrackerForTest() const;
- base::WeakPtr<syncer::ModelTypeProcessor> GetWeakPtr();
+ base::WeakPtr<syncer::ModelTypeControllerDelegate> GetWeakPtr();
private:
SEQUENCE_CHECKER(sequence_checker_);
- // Reorders incoming updates such that parent creation is before child
- // creation and child deletion is before parent deletion, and deletions should
- // come last. The returned pointers point to the elements in the original
- // |updates|.
- static std::vector<const syncer::UpdateResponseData*> ReorderUpdates(
- const syncer::UpdateResponseDataList& updates);
-
- // Given a remote update entity, it returns the parent bookmark node of the
- // corresponding node. It returns null if the parent node cannot be found.
- const bookmarks::BookmarkNode* GetParentNode(
- const syncer::EntityData& update_data) const;
-
- // Processes a remote creation of a bookmark node.
- // 1. For permanent folders, they are only registered in |bookmark_tracker_|.
- // 2. If the nodes parent cannot be found, the remote creation update is
- // ignored.
- // 3. Otherwise, a new node is created in the local bookmark model and
- // registered in |bookmark_tracker_|.
- void ProcessRemoteCreate(const syncer::EntityData& update_data);
-
- // Processes a remote update of a bookmark node. |update_data| must not be a
- // deletion, and the server_id must be already tracked, otherwise, it is a
- // creation that gets handeled in ProcessRemoteCreate(). |tracked_entity| is
- // the tracked entity for that server_id. It is passed as a dependency instead
- // of performing a lookup inside ProcessRemoteUpdate() to avoid wasting CPU
- // cycles for doing another lookup (this code runs on the UI thread).
- void ProcessRemoteUpdate(const syncer::EntityData& update_data,
- const SyncedBookmarkTracker::Entity* tracked_entity);
-
- // Process a remote delete of a bookmark node. |update_data| must not be a
- // deletion. |tracked_entity| is the tracked entity for that server_id. It is
- // passed as a dependency instead of performing a lookup inside
- // ProcessRemoteDelete() to avoid wasting CPU cycles for doing another lookup
- // (this code runs on the UI thread).
- void ProcessRemoteDelete(const syncer::EntityData& update_data,
- const SyncedBookmarkTracker::Entity* tracked_entity);
-
- // Associates the permanent bookmark folders with the corresponding server
- // side ids and registers the association in |bookmark_tracker_|.
- // |update_data| must contain server_defined_unique_tag that is used to
- // determine the corresponding permanent node. All permanent nodes are assumed
- // to be directly children nodes of |kBookmarksRootId|. This method is used in
- // the initial sync cycle only.
- void AssociatePermanentFolder(const syncer::EntityData& update_data);
-
- syncer::SyncClient* const sync_client_;
+ // If preconditions are met, inform sync that we are ready to connect.
+ void ConnectIfReady();
- // The bookmark model we are processing local changes from and forwarding
- // remote changes to.
- bookmarks::BookmarkModel* const bookmark_model_;
+ // Nudges worker if there are any local entities to be committed. Should only
+ // be called after initial sync is done and processor is tracking sync
+ // entities.
+ void NudgeForCommitIfNeeded();
+
+ // Instantiates the required objects to track metadata and starts observing
+ // changes from the bookmark model.
+ void StartTrackingMetadata(
+ std::vector<NodeMetadataPair> nodes_metadata,
+ std::unique_ptr<sync_pb::ModelTypeState> model_type_state);
+ // Stores the start callback in between OnSyncStarting() and
+ // ModelReadyToSync().
+ StartCallback start_callback_;
+
+ // The bookmark model we are processing local changes from and forwarding
+ // remote changes to. It is set during ModelReadyToSync(), which is called
+ // during startup, as part of the bookmark-loading process.
+ bookmarks::BookmarkModel* bookmark_model_ = nullptr;
+
+ // Used to suspend bookmark undo when processing remote changes.
+ BookmarkUndoService* const bookmark_undo_service_;
+
+ // The callback used to schedule the persistence of bookmark model as well as
+ // the metadata to a file during which latest metadata should also be pulled
+ // via EncodeSyncMetadata. Processor should invoke it upon changes in the
+ // metadata that don't imply changes in the model itself. Persisting updates
+ // that imply model changes is the model's responsibility.
+ base::RepeatingClosure schedule_save_closure_;
+
+ // Reference to the CommitQueue.
+ //
+ // The interface hides the posting of tasks across threads as well as the
+ // CommitQueue's implementation. Both of these features are
+ // useful in tests.
std::unique_ptr<syncer::CommitQueue> worker_;
- // Keeps the mapping between server ids and bookmarks nodes. It also caches
- // the metadata upon a local change until the commit configration is received.
- SyncedBookmarkTracker bookmark_tracker_;
+ // Keeps the mapping between server ids and bookmarks nodes together with sync
+ // metadata. It is constructed and set during ModelReadyToSync(), if the
+ // loaded bookmarks JSON contained previous sync metadata, or upon completion
+ // of initial sync, which is called during startup, as part of the
+ // bookmark-loading process.
+ std::unique_ptr<SyncedBookmarkTracker> bookmark_tracker_;
+
+ // GUID string that identifies the sync client and is received from the sync
+ // engine.
+ std::string cache_guid_;
+
+ std::unique_ptr<BookmarkModelObserverImpl> bookmark_model_observer_;
base::WeakPtrFactory<BookmarkModelTypeProcessor> weak_ptr_factory_;
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 ff2d3b32715..63af6deb1f1 100644
--- a/chromium/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
+++ b/chromium/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
@@ -7,25 +7,28 @@
#include <string>
#include "base/strings/utf_string_conversions.h"
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_task_environment.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/test/test_bookmark_client.h"
#include "components/sync/driver/fake_sync_client.h"
+#include "components/sync/model/data_type_activation_request.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::ASCIIToUTF16;
using testing::Eq;
+using testing::NiceMock;
using testing::NotNull;
namespace sync_bookmarks {
namespace {
-// The parent tag for children of the root entity. Entities with this parent are
-// referred to as top level enities.
-const char kRootParentTag[] = "0";
const char kBookmarkBarTag[] = "bookmark_bar";
+const char kBookmarkBarId[] = "bookmark_bar_id";
const char kBookmarksRootId[] = "32904_google_chrome_bookmarks";
+const char kCacheGuid[] = "generated_id";
struct BookmarkInfo {
std::string server_id;
@@ -57,6 +60,13 @@ syncer::UpdateResponseData CreateUpdateData(const BookmarkInfo& bookmark_info) {
return response_data;
}
+sync_pb::ModelTypeState CreateDummyModelTypeState() {
+ sync_pb::ModelTypeState model_type_state;
+ model_type_state.set_cache_guid(kCacheGuid);
+ model_type_state.set_initial_sync_done(true);
+ return model_type_state;
+}
+
void AssertState(const BookmarkModelTypeProcessor* processor,
const std::vector<BookmarkInfo>& bookmarks) {
const SyncedBookmarkTracker* tracker = processor->GetTrackerForTest();
@@ -74,7 +84,7 @@ void AssertState(const BookmarkModelTypeProcessor* processor,
ASSERT_THAT(node->url(), Eq(GURL(bookmark.url)));
const SyncedBookmarkTracker::Entity* parent_entity =
tracker->GetEntityForSyncId(bookmark.parent_id);
- ASSERT_THAT(node->parent(), parent_entity->bookmark_node());
+ ASSERT_THAT(node->parent(), Eq(parent_entity->bookmark_node()));
}
}
@@ -86,12 +96,12 @@ void InitWithSyncedBookmarks(const std::vector<BookmarkInfo>& bookmarks,
syncer::UpdateResponseDataList updates;
// Add update for the permanent folder "Bookmarks bar".
updates.push_back(
- CreateUpdateData({"bookmark_bar", std::string(), std::string(),
+ CreateUpdateData({kBookmarkBarId, std::string(), std::string(),
kBookmarksRootId, kBookmarkBarTag}));
for (BookmarkInfo bookmark : bookmarks) {
updates.push_back(CreateUpdateData(bookmark));
}
- processor->OnUpdateReceived(sync_pb::ModelTypeState(), updates);
+ processor->OnUpdateReceived(CreateDummyModelTypeState(), updates);
AssertState(processor, bookmarks);
}
@@ -106,12 +116,6 @@ syncer::UpdateResponseData CreateTombstone(const std::string& server_id) {
return response_data;
}
-syncer::UpdateResponseData CreateBookmarkRootUpdateData() {
- return CreateUpdateData({syncer::ModelTypeToRootTag(syncer::BOOKMARKS),
- std::string(), std::string(), kRootParentTag,
- syncer::ModelTypeToRootTag(syncer::BOOKMARKS)});
-}
-
class TestSyncClient : public syncer::FakeSyncClient {
public:
explicit TestSyncClient(bookmarks::BookmarkModel* bookmark_model)
@@ -129,69 +133,53 @@ class BookmarkModelTypeProcessorTest : public testing::Test {
public:
BookmarkModelTypeProcessorTest()
: bookmark_model_(bookmarks::TestBookmarkClient::CreateModel()),
- sync_client_(bookmark_model_.get()) {}
+ sync_client_(bookmark_model_.get()),
+ processor_(sync_client()->GetBookmarkUndoServiceIfExists()) {
+ // 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_.ModelReadyToSync(/*metadata_str=*/std::string(),
+ schedule_save_closure_.Get(),
+ bookmark_model_.get());
+ syncer::DataTypeActivationRequest request;
+ request.cache_guid = kCacheGuid;
+ processor_.OnSyncStarting(request, base::DoNothing());
+ }
TestSyncClient* sync_client() { return &sync_client_; }
bookmarks::BookmarkModel* bookmark_model() { return bookmark_model_.get(); }
+ BookmarkModelTypeProcessor* processor() { return &processor_; }
+ base::MockCallback<base::RepeatingClosure>* schedule_save_closure() {
+ return &schedule_save_closure_;
+ }
private:
+ base::test::ScopedTaskEnvironment task_environment_;
+ NiceMock<base::MockCallback<base::RepeatingClosure>> schedule_save_closure_;
std::unique_ptr<bookmarks::BookmarkModel> bookmark_model_;
TestSyncClient sync_client_;
+ BookmarkModelTypeProcessor processor_;
};
-TEST_F(BookmarkModelTypeProcessorTest, ReorderUpdatesShouldIgnoreRootNodes) {
- syncer::UpdateResponseDataList updates;
- updates.push_back(CreateBookmarkRootUpdateData());
- std::vector<const syncer::UpdateResponseData*> ordered_updates =
- BookmarkModelTypeProcessor::ReorderUpdatesForTest(updates);
- // Root node update should be filtered out.
- EXPECT_THAT(ordered_updates.size(), Eq(0U));
-}
-
-// TODO(crbug.com/516866): This should change to cover the general case of
-// parents before children for non-deletions, and another test should be added
-// for children before parents for deletions.
-TEST_F(BookmarkModelTypeProcessorTest,
- ReorderUpdatesShouldPlacePermanentNodesFirstForNonDeletions) {
- const std::string kNode1Id = "node1";
- const std::string kNode2Id = "node2";
- syncer::UpdateResponseDataList updates;
- updates.push_back(CreateUpdateData(
- {kNode1Id, std::string(), std::string(), kNode2Id, std::string()}));
- updates.push_back(CreateUpdateData({kNode2Id, std::string(), std::string(),
- kBookmarksRootId, kBookmarkBarTag}));
- std::vector<const syncer::UpdateResponseData*> ordered_updates =
- BookmarkModelTypeProcessor::ReorderUpdatesForTest(updates);
-
- // No update should be dropped.
- ASSERT_THAT(ordered_updates.size(), Eq(2U));
-
- // Updates should be ordered such that parent node update comes first.
- EXPECT_THAT(ordered_updates[0]->entity.value().id, Eq(kNode2Id));
- EXPECT_THAT(ordered_updates[1]->entity.value().id, Eq(kNode1Id));
-}
-
TEST_F(BookmarkModelTypeProcessorTest, ShouldUpdateModelAfterRemoteCreation) {
- BookmarkModelTypeProcessor processor(sync_client());
-
syncer::UpdateResponseDataList updates;
// Add update for the permanent folder "Bookmarks bar".
updates.push_back(
- CreateUpdateData({"bookmark_bar", std::string(), std::string(),
+ CreateUpdateData({kBookmarkBarId, std::string(), std::string(),
kBookmarksRootId, kBookmarkBarTag}));
// Add update for another node under the bookmarks bar.
const std::string kNodeId = "node_id";
const std::string kTitle = "title";
const std::string kUrl = "http://www.url.com";
- updates.push_back(CreateUpdateData({kNodeId, kTitle, kUrl, kBookmarkBarTag,
+ updates.push_back(CreateUpdateData({kNodeId, kTitle, kUrl, kBookmarkBarId,
/*server_tag=*/std::string()}));
const bookmarks::BookmarkNode* bookmarkbar =
bookmark_model()->bookmark_bar_node();
EXPECT_TRUE(bookmarkbar->empty());
- processor.OnUpdateReceived(sync_pb::ModelTypeState(), updates);
+ processor()->OnUpdateReceived(CreateDummyModelTypeState(), updates);
ASSERT_THAT(bookmarkbar->GetChild(0), NotNull());
EXPECT_THAT(bookmarkbar->GetChild(0)->GetTitle(), Eq(ASCIIToUTF16(kTitle)));
@@ -199,16 +187,14 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldUpdateModelAfterRemoteCreation) {
}
TEST_F(BookmarkModelTypeProcessorTest, ShouldUpdateModelAfterRemoteUpdate) {
- BookmarkModelTypeProcessor processor(sync_client());
-
const std::string kNodeId = "node_id";
const std::string kTitle = "title";
const std::string kUrl = "http://www.url.com";
std::vector<BookmarkInfo> bookmarks = {
- {kNodeId, kTitle, kUrl, kBookmarkBarTag, /*server_tag=*/std::string()}};
+ {kNodeId, kTitle, kUrl, kBookmarkBarId, /*server_tag=*/std::string()}};
- InitWithSyncedBookmarks(bookmarks, &processor);
+ InitWithSyncedBookmarks(bookmarks, processor());
// Make sure original bookmark exists.
const bookmarks::BookmarkNode* bookmark_bar =
@@ -223,9 +209,10 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldUpdateModelAfterRemoteUpdate) {
const std::string kNewUrl = "http://www.new-url.com";
syncer::UpdateResponseDataList updates;
updates.push_back(
- CreateUpdateData({kNodeId, kNewTitle, kNewUrl, kBookmarkBarTag,
+ CreateUpdateData({kNodeId, kNewTitle, kNewUrl, kBookmarkBarId,
/*server_tag=*/std::string()}));
- processor.OnUpdateReceived(sync_pb::ModelTypeState(), updates);
+
+ processor()->OnUpdateReceived(CreateDummyModelTypeState(), updates);
// Check if the bookmark has been updated properly.
EXPECT_THAT(bookmark_bar->GetChild(0), Eq(bookmark_node));
@@ -233,8 +220,34 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldUpdateModelAfterRemoteUpdate) {
EXPECT_THAT(bookmark_node->url(), Eq(GURL(kNewUrl)));
}
+TEST_F(BookmarkModelTypeProcessorTest,
+ ShouldScheduleSaveAfterRemoteUpdateWithOnlyMetadataChange) {
+ const std::string kNodeId = "node_id";
+ const std::string kTitle = "title";
+ const std::string kUrl = "http://www.url.com";
+
+ std::vector<BookmarkInfo> bookmarks = {
+ {kNodeId, kTitle, kUrl, kBookmarkBarId, /*server_tag=*/std::string()}};
+
+ 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->GetChild(0);
+ ASSERT_THAT(bookmark_node, NotNull());
+
+ // Process an update for the same bookmark with the same data.
+ syncer::UpdateResponseDataList updates;
+ updates.push_back(CreateUpdateData({kNodeId, kTitle, kUrl, kBookmarkBarId,
+ /*server_tag=*/std::string()}));
+ updates[0].response_version++;
+
+ EXPECT_CALL(*schedule_save_closure(), Run());
+ processor()->OnUpdateReceived(CreateDummyModelTypeState(), updates);
+}
+
TEST_F(BookmarkModelTypeProcessorTest, ShouldUpdateModelAfterRemoteDelete) {
- BookmarkModelTypeProcessor processor(sync_client());
// Build this structure
// bookmark_bar
// |- folder1
@@ -260,16 +273,16 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldUpdateModelAfterRemoteDelete) {
const std::string kUrl = "http://www.url.com";
std::vector<BookmarkInfo> bookmarks = {
- {kFolder1Id, kFolder1, /*url=*/std::string(), kBookmarkBarTag,
+ {kFolder1Id, kFolder1, /*url=*/std::string(), kBookmarkBarId,
/*server_tag=*/std::string()},
{kTitle1Id, kTitle1, kUrl, kFolder1Id, /*server_tag=*/std::string()},
{kTitle2Id, kTitle2, kUrl, kFolder1Id, /*server_tag=*/std::string()},
- {kFolder2Id, kFolder2, /*url=*/std::string(), kBookmarkBarTag,
+ {kFolder2Id, kFolder2, /*url=*/std::string(), kBookmarkBarId,
/*server_tag=*/std::string()},
{kTitle3Id, kTitle3, kUrl, kFolder2Id, /*server_tag=*/std::string()},
};
- InitWithSyncedBookmarks(bookmarks, &processor);
+ InitWithSyncedBookmarks(bookmarks, processor());
const bookmarks::BookmarkNode* bookmarkbar =
bookmark_model()->bookmark_bar_node();
@@ -288,8 +301,7 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldUpdateModelAfterRemoteDelete) {
updates.push_back(CreateTombstone(kTitle1Id));
updates.push_back(CreateTombstone(kFolder1Id));
- const sync_pb::ModelTypeState model_type_state;
- processor.OnUpdateReceived(model_type_state, updates);
+ processor()->OnUpdateReceived(CreateDummyModelTypeState(), updates);
// The structure should be
// bookmark_bar
@@ -302,6 +314,154 @@ TEST_F(BookmarkModelTypeProcessorTest, ShouldUpdateModelAfterRemoteDelete) {
Eq(ASCIIToUTF16(kTitle3)));
}
+TEST_F(BookmarkModelTypeProcessorTest, ShouldEncodeSyncMetadata) {
+ const std::string kNodeId1 = "node_id1";
+ const std::string kTitle1 = "title1";
+ const std::string kUrl1 = "http://www.url1.com";
+
+ const std::string kNodeId2 = "node_id2";
+ const std::string kTitle2 = "title2";
+ const std::string 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()}};
+
+ InitWithSyncedBookmarks(bookmarks, processor());
+
+ // Make sure original bookmark exists.
+ const bookmarks::BookmarkNode* bookmark_bar =
+ bookmark_model()->bookmark_bar_node();
+ const bookmarks::BookmarkNode* bookmark_node1 = bookmark_bar->GetChild(0);
+ const bookmarks::BookmarkNode* bookmark_node2 = bookmark_bar->GetChild(1);
+ ASSERT_THAT(bookmark_node1, NotNull());
+ ASSERT_THAT(bookmark_node2, NotNull());
+
+ std::string metadata_str = processor()->EncodeSyncMetadata();
+ sync_pb::BookmarkModelMetadata model_metadata;
+ EXPECT_TRUE(model_metadata.ParseFromString(metadata_str));
+ // There should be 3 entries now, one for the bookmark bar, and the other 2
+ // nodes.
+ ASSERT_THAT(model_metadata.bookmarks_metadata().size(), Eq(3));
+
+ EXPECT_THAT(model_metadata.bookmarks_metadata().Get(0).id(),
+ Eq(bookmark_bar->id()));
+ EXPECT_THAT(model_metadata.bookmarks_metadata().Get(0).metadata().server_id(),
+ Eq(kBookmarkBarId));
+ EXPECT_THAT(
+ model_metadata.bookmarks_metadata().Get(0).metadata().is_deleted(),
+ Eq(false));
+
+ EXPECT_THAT(model_metadata.bookmarks_metadata().Get(1).id(),
+ Eq(bookmark_node1->id()));
+ EXPECT_THAT(model_metadata.bookmarks_metadata().Get(1).metadata().server_id(),
+ Eq(kNodeId1));
+ EXPECT_THAT(
+ model_metadata.bookmarks_metadata().Get(1).metadata().is_deleted(),
+ Eq(false));
+
+ EXPECT_THAT(model_metadata.bookmarks_metadata().Get(2).id(),
+ Eq(bookmark_node2->id()));
+ EXPECT_THAT(model_metadata.bookmarks_metadata().Get(2).metadata().server_id(),
+ Eq(kNodeId2));
+ EXPECT_THAT(
+ model_metadata.bookmarks_metadata().Get(2).metadata().is_deleted(),
+ Eq(false));
+
+ // Process a remote delete for the first node.
+ syncer::UpdateResponseDataList updates;
+ updates.push_back(CreateTombstone(kNodeId1));
+
+ processor()->OnUpdateReceived(CreateDummyModelTypeState(), updates);
+
+ metadata_str = processor()->EncodeSyncMetadata();
+ model_metadata.ParseFromString(metadata_str);
+ // There should be 3 entries now, one for the bookmark bar, and the remaning
+ // bookmark node.
+ ASSERT_THAT(model_metadata.bookmarks_metadata().size(), Eq(2));
+
+ EXPECT_THAT(model_metadata.bookmarks_metadata().Get(1).id(),
+ Eq(bookmark_node2->id()));
+ EXPECT_THAT(model_metadata.bookmarks_metadata().Get(1).metadata().server_id(),
+ Eq(kNodeId2));
+ EXPECT_THAT(
+ model_metadata.bookmarks_metadata().Get(1).metadata().is_deleted(),
+ Eq(false));
+}
+
+TEST_F(BookmarkModelTypeProcessorTest, ShouldDecodeSyncMetadata) {
+ const std::string kNodeId = "node_id1";
+ const std::string kTitle = "title1";
+ const std::string kUrl = "http://www.url1.com";
+
+ std::vector<BookmarkInfo> bookmarks = {
+ {kNodeId, kTitle, kUrl, kBookmarkBarId, /*server_tag=*/std::string()}};
+
+ const bookmarks::BookmarkNode* bookmark_bar_node =
+ bookmark_model()->bookmark_bar_node();
+ const bookmarks::BookmarkNode* bookmarknode = bookmark_model()->AddURL(
+ /*parent=*/bookmark_bar_node, /*index=*/0, base::UTF8ToUTF16(kTitle),
+ GURL(kUrl));
+
+ // TODO(crbug.com/516866): Remove this after initial sync done is properly set
+ // within the processor.
+ sync_pb::BookmarkModelMetadata model_metadata;
+ model_metadata.mutable_model_type_state()->set_initial_sync_done(true);
+ // Add an entry for bookmark bar.
+ sync_pb::BookmarkMetadata* bookmark_metadata =
+ model_metadata.add_bookmarks_metadata();
+ bookmark_metadata->set_id(bookmark_bar_node->id());
+ bookmark_metadata->mutable_metadata()->set_server_id(kBookmarkBarId);
+ // Add an entry for the bookmark node.
+ bookmark_metadata = model_metadata.add_bookmarks_metadata();
+ bookmark_metadata->set_id(bookmarknode->id());
+ bookmark_metadata->mutable_metadata()->set_server_id(kNodeId);
+
+ // Create a new processor and init it with the metadata str.
+ BookmarkModelTypeProcessor new_processor(
+ sync_client()->GetBookmarkUndoServiceIfExists());
+ std::string metadata_str;
+ model_metadata.SerializeToString(&metadata_str);
+ new_processor.ModelReadyToSync(metadata_str, base::DoNothing(),
+ bookmark_model());
+
+ AssertState(&new_processor, bookmarks);
+}
+
+TEST_F(BookmarkModelTypeProcessorTest, ShouldDecodeEncodedSyncMetadata) {
+ const std::string kNodeId1 = "node_id1";
+ const std::string kTitle1 = "title1";
+ const std::string kUrl1 = "http://www.url1.com";
+
+ const std::string kNodeId2 = "node_id2";
+ const std::string kTitle2 = "title2";
+ const std::string 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()}};
+
+ 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);
+
+ // Create a new processor and init it with the same metadata str.
+ BookmarkModelTypeProcessor new_processor(
+ sync_client()->GetBookmarkUndoServiceIfExists());
+ model_metadata.SerializeToString(&metadata_str);
+ new_processor.ModelReadyToSync(metadata_str, base::DoNothing(),
+ bookmark_model());
+
+ AssertState(&new_processor, bookmarks);
+}
+
} // namespace
} // namespace sync_bookmarks
diff --git a/chromium/components/sync_bookmarks/bookmark_remote_updates_handler.cc b/chromium/components/sync_bookmarks/bookmark_remote_updates_handler.cc
new file mode 100644
index 00000000000..13e75cb98bc
--- /dev/null
+++ b/chromium/components/sync_bookmarks/bookmark_remote_updates_handler.cc
@@ -0,0 +1,359 @@
+// 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/sync_bookmarks/bookmark_remote_updates_handler.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/bookmark_node.h"
+
+namespace sync_bookmarks {
+
+namespace {
+
+// The sync protocol identifies top-level entities by means of well-known tags,
+// (aka server defined tags) which should not be confused with titles or client
+// tags that aren't supported by bookmarks (at the time of writing). Each tag
+// corresponds to a singleton instance of a particular top-level node in a
+// user's share; the tags are consistent across users. The tags allow us to
+// locate the specific folders whose contents we care about synchronizing,
+// without having to do a lookup by name or path. The tags should not be made
+// user-visible. For example, the tag "bookmark_bar" represents the permanent
+// node for bookmarks bar in Chrome. The tag "other_bookmarks" represents the
+// permanent folder Other Bookmarks in Chrome.
+//
+// It is the responsibility of something upstream (at time of writing, the sync
+// server) to create these tagged nodes when initializing sync for the first
+// time for a user. Thus, once the backend finishes initializing, the
+// ProfileSyncService can rely on the presence of tagged nodes.
+const char kBookmarkBarTag[] = "bookmark_bar";
+const char kMobileBookmarksTag[] = "synced_bookmarks";
+const char kOtherBookmarksTag[] = "other_bookmarks";
+
+// Id is created by concatenating the specifics field number and the server tag
+// similar to LookbackServerEntity::CreateId() that uses
+// GetSpecificsFieldNumberFromModelType() to compute the field number.
+const char kBookmarksRootId[] = "32904_google_chrome_bookmarks";
+
+// |sync_entity| must contain a bookmark specifics.
+// Metainfo entries must have unique keys.
+bookmarks::BookmarkNode::MetaInfoMap GetBookmarkMetaInfo(
+ const syncer::EntityData& sync_entity) {
+ const sync_pb::BookmarkSpecifics& specifics =
+ sync_entity.specifics.bookmark();
+ bookmarks::BookmarkNode::MetaInfoMap meta_info_map;
+ for (const sync_pb::MetaInfo& meta_info : specifics.meta_info()) {
+ meta_info_map[meta_info.key()] = meta_info.value();
+ }
+ DCHECK_EQ(static_cast<size_t>(specifics.meta_info_size()),
+ meta_info_map.size());
+ return meta_info_map;
+}
+
+// Creates a bookmark node under the given parent node from the given sync node.
+// Returns the newly created node. |sync_entity| must contain a bookmark
+// specifics with Metainfo entries having unique keys.
+const bookmarks::BookmarkNode* CreateBookmarkNode(
+ const syncer::EntityData& sync_entity,
+ const bookmarks::BookmarkNode* parent,
+ bookmarks::BookmarkModel* model,
+ int index) {
+ DCHECK(parent);
+ DCHECK(model);
+
+ const sync_pb::BookmarkSpecifics& specifics =
+ sync_entity.specifics.bookmark();
+ bookmarks::BookmarkNode::MetaInfoMap metainfo =
+ GetBookmarkMetaInfo(sync_entity);
+ if (sync_entity.is_folder) {
+ return model->AddFolderWithMetaInfo(
+ parent, index, base::UTF8ToUTF16(specifics.title()), &metainfo);
+ }
+ // 'creation_time_us' was added in M24. Assume a time of 0 means now.
+ const int64_t create_time_us = specifics.creation_time_us();
+ base::Time create_time =
+ (create_time_us == 0)
+ ? base::Time::Now()
+ : base::Time::FromDeltaSinceWindowsEpoch(
+ // Use FromDeltaSinceWindowsEpoch because create_time_us has
+ // always used the Windows epoch.
+ base::TimeDelta::FromMicroseconds(create_time_us));
+ return model->AddURLWithCreationTimeAndMetaInfo(
+ parent, index, base::UTF8ToUTF16(specifics.title()),
+ GURL(specifics.url()), create_time, &metainfo);
+ // TODO(crbug.com/516866): Add the favicon related code.
+}
+
+// Check whether an incoming specifics represent a valid bookmark or not.
+// |is_folder| is whether this specifics is for a folder or not.
+// Folders and tomstones entail different validation conditions.
+bool IsValidBookmark(const sync_pb::BookmarkSpecifics& specifics,
+ bool is_folder) {
+ if (specifics.ByteSize() == 0) {
+ DLOG(ERROR) << "Invalid bookmark: empty specifics.";
+ return false;
+ }
+ if (is_folder) {
+ return true;
+ }
+ if (!GURL(specifics.url()).is_valid()) {
+ DLOG(ERROR) << "Invalid bookmark: invalid url in the specifics.";
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+BookmarkRemoteUpdatesHandler::BookmarkRemoteUpdatesHandler(
+ bookmarks::BookmarkModel* bookmark_model,
+ SyncedBookmarkTracker* bookmark_tracker)
+ : bookmark_model_(bookmark_model), bookmark_tracker_(bookmark_tracker) {
+ DCHECK(bookmark_model);
+ DCHECK(bookmark_tracker);
+}
+
+void BookmarkRemoteUpdatesHandler::Process(
+ const syncer::UpdateResponseDataList& updates) {
+ for (const syncer::UpdateResponseData* update : ReorderUpdates(updates)) {
+ const syncer::EntityData& update_entity = update->entity.value();
+ // TODO(crbug.com/516866): Check |update_entity| for sanity.
+ // 1. Has bookmark specifics or no specifics in case of delete.
+ // 2. All meta info entries in the specifics have unique keys.
+ const SyncedBookmarkTracker::Entity* tracked_entity =
+ bookmark_tracker_->GetEntityForSyncId(update_entity.id);
+ if (update_entity.is_deleted()) {
+ ProcessRemoteDelete(update_entity, tracked_entity);
+ continue;
+ }
+ if (!tracked_entity) {
+ ProcessRemoteCreate(*update);
+ continue;
+ }
+ // Ignore changes to the permanent nodes (e.g. bookmarks bar). We only care
+ // about their children.
+ if (bookmark_model_->is_permanent_node(tracked_entity->bookmark_node())) {
+ continue;
+ }
+ ProcessRemoteUpdate(*update, tracked_entity);
+ }
+}
+
+// static
+std::vector<const syncer::UpdateResponseData*>
+BookmarkRemoteUpdatesHandler::ReorderUpdatesForTest(
+ const syncer::UpdateResponseDataList& updates) {
+ return ReorderUpdates(updates);
+}
+
+// static
+std::vector<const syncer::UpdateResponseData*>
+BookmarkRemoteUpdatesHandler::ReorderUpdates(
+ const syncer::UpdateResponseDataList& updates) {
+ // TODO(crbug.com/516866): This is a very simple (hacky) reordering algorithm
+ // that assumes no folders exist except the top level permanent ones. This
+ // should be fixed before enabling USS for bookmarks.
+ std::vector<const syncer::UpdateResponseData*> ordered_updates;
+ for (const syncer::UpdateResponseData& update : updates) {
+ const syncer::EntityData& update_entity = update.entity.value();
+ if (update_entity.parent_id == "0") {
+ continue;
+ }
+ if (update_entity.parent_id == kBookmarksRootId) {
+ ordered_updates.push_back(&update);
+ }
+ }
+ for (const syncer::UpdateResponseData& update : updates) {
+ const syncer::EntityData& update_entity = update.entity.value();
+ // Deletions should come last.
+ if (update_entity.is_deleted()) {
+ continue;
+ }
+ if (update_entity.parent_id != "0" &&
+ update_entity.parent_id != kBookmarksRootId) {
+ ordered_updates.push_back(&update);
+ }
+ }
+ // Now add deletions.
+ for (const syncer::UpdateResponseData& update : updates) {
+ const syncer::EntityData& update_entity = update.entity.value();
+ if (!update_entity.is_deleted()) {
+ continue;
+ }
+ if (update_entity.parent_id != "0" &&
+ update_entity.parent_id != kBookmarksRootId) {
+ ordered_updates.push_back(&update);
+ }
+ }
+ return ordered_updates;
+}
+
+void BookmarkRemoteUpdatesHandler::ProcessRemoteCreate(
+ const syncer::UpdateResponseData& update) {
+ // Because the Synced Bookmarks node can be created server side, it's possible
+ // it'll arrive at the client as an update. In that case it won't have been
+ // associated at startup, the GetChromeNodeFromSyncId call above will return
+ // null, and we won't detect it as a permanent node, resulting in us trying to
+ // create it here (which will fail). Therefore, we add special logic here just
+ // to detect the Synced Bookmarks folder.
+ const syncer::EntityData& update_entity = update.entity.value();
+ DCHECK(!update_entity.is_deleted());
+ if (update_entity.parent_id == kBookmarksRootId) {
+ // Associate permanent folders.
+ // TODO(crbug.com/516866): Method documentation says this method should be
+ // used in initial sync only. Make sure this is the case.
+ AssociatePermanentFolder(update);
+ return;
+ }
+ if (!IsValidBookmark(update_entity.specifics.bookmark(),
+ update_entity.is_folder)) {
+ // Ignore creations with invalid specifics.
+ DLOG(ERROR) << "Couldn't add bookmark with an invalid specifics.";
+ return;
+ }
+ const bookmarks::BookmarkNode* parent_node = GetParentNode(update_entity);
+ if (!parent_node) {
+ // If we cannot find the parent, we can do nothing.
+ DLOG(ERROR) << "Could not find parent of node being added."
+ << " Node title: " << update_entity.specifics.bookmark().title()
+ << ", parent id = " << update_entity.parent_id;
+ return;
+ }
+ // TODO(crbug.com/516866): This code appends the code to the very end of the
+ // list of the children by assigning the index to the
+ // parent_node->child_count(). It should instead compute the exact using the
+ // unique position information of the new node as well as the siblings.
+ const bookmarks::BookmarkNode* bookmark_node = CreateBookmarkNode(
+ update_entity, parent_node, bookmark_model_, parent_node->child_count());
+ if (!bookmark_node) {
+ // We ignore bookmarks we can't add.
+ DLOG(ERROR) << "Failed to create bookmark node with title "
+ << update_entity.specifics.bookmark().title() << " and url "
+ << update_entity.specifics.bookmark().url();
+ return;
+ }
+ bookmark_tracker_->Add(update_entity.id, bookmark_node,
+ update.response_version, update_entity.creation_time,
+ update_entity.unique_position,
+ update_entity.specifics);
+}
+
+void BookmarkRemoteUpdatesHandler::ProcessRemoteUpdate(
+ const syncer::UpdateResponseData& update,
+ const SyncedBookmarkTracker::Entity* tracked_entity) {
+ const syncer::EntityData& update_entity = update.entity.value();
+ // Can only update existing nodes.
+ DCHECK(tracked_entity);
+ DCHECK_EQ(tracked_entity,
+ bookmark_tracker_->GetEntityForSyncId(update_entity.id));
+ // Must not be a deletion.
+ DCHECK(!update_entity.is_deleted());
+ if (!IsValidBookmark(update_entity.specifics.bookmark(),
+ update_entity.is_folder)) {
+ // Ignore updates with invalid specifics.
+ DLOG(ERROR) << "Couldn't update bookmark with an invalid specifics.";
+ return;
+ }
+ if (tracked_entity->IsUnsynced()) {
+ // TODO(crbug.com/516866): Handle conflict resolution.
+ return;
+ }
+ if (tracked_entity->MatchesData(update_entity)) {
+ bookmark_tracker_->Update(update_entity.id, update.response_version,
+ update_entity.modification_time,
+ update_entity.unique_position,
+ update_entity.specifics);
+ return;
+ }
+ const bookmarks::BookmarkNode* node = tracked_entity->bookmark_node();
+ if (update_entity.is_folder != node->is_folder()) {
+ DLOG(ERROR) << "Could not update node. Remote node is a "
+ << (update_entity.is_folder ? "folder" : "bookmark")
+ << " while local node is a "
+ << (node->is_folder() ? "folder" : "bookmark");
+ return;
+ }
+ const sync_pb::BookmarkSpecifics& specifics =
+ update_entity.specifics.bookmark();
+ if (!update_entity.is_folder) {
+ bookmark_model_->SetURL(node, GURL(specifics.url()));
+ }
+
+ bookmark_model_->SetTitle(node, base::UTF8ToUTF16(specifics.title()));
+ // TODO(crbug.com/516866): Add the favicon related code.
+ bookmark_model_->SetNodeMetaInfoMap(node, GetBookmarkMetaInfo(update_entity));
+ bookmark_tracker_->Update(update_entity.id, update.response_version,
+ update_entity.modification_time,
+ update_entity.unique_position,
+ update_entity.specifics);
+ // TODO(crbug.com/516866): Handle reparenting.
+ // TODO(crbug.com/516866): Handle the case of moving the bookmark to a new
+ // position under the same parent (i.e. change in the unique position)
+}
+
+void BookmarkRemoteUpdatesHandler::ProcessRemoteDelete(
+ const syncer::EntityData& update_entity,
+ const SyncedBookmarkTracker::Entity* tracked_entity) {
+ DCHECK(update_entity.is_deleted());
+
+ DCHECK_EQ(tracked_entity,
+ bookmark_tracker_->GetEntityForSyncId(update_entity.id));
+
+ // Handle corner cases first.
+ if (tracked_entity == nullptr) {
+ // Local entity doesn't exist and update is tombstone.
+ DLOG(WARNING) << "Received remote delete for a non-existing item.";
+ return;
+ }
+
+ const bookmarks::BookmarkNode* node = tracked_entity->bookmark_node();
+ // Ignore changes to the permanent top-level nodes. We only care about
+ // their children.
+ if (bookmark_model_->is_permanent_node(node)) {
+ return;
+ }
+ // TODO(crbug.com/516866): Allow deletions of non-empty direcoties if makes
+ // sense, and recursively delete children.
+ if (node->child_count() > 0) {
+ DLOG(WARNING) << "Trying to delete a non-empty folder.";
+ return;
+ }
+
+ bookmark_model_->Remove(node);
+ bookmark_tracker_->Remove(update_entity.id);
+}
+
+const bookmarks::BookmarkNode* BookmarkRemoteUpdatesHandler::GetParentNode(
+ const syncer::EntityData& update_entity) const {
+ const SyncedBookmarkTracker::Entity* parent_entity =
+ bookmark_tracker_->GetEntityForSyncId(update_entity.parent_id);
+ if (!parent_entity) {
+ return nullptr;
+ }
+ return parent_entity->bookmark_node();
+}
+
+void BookmarkRemoteUpdatesHandler::AssociatePermanentFolder(
+ const syncer::UpdateResponseData& update) {
+ const syncer::EntityData& update_entity = update.entity.value();
+ DCHECK_EQ(update_entity.parent_id, kBookmarksRootId);
+
+ const bookmarks::BookmarkNode* permanent_node = nullptr;
+ if (update_entity.server_defined_unique_tag == kBookmarkBarTag) {
+ permanent_node = bookmark_model_->bookmark_bar_node();
+ } else if (update_entity.server_defined_unique_tag == kOtherBookmarksTag) {
+ permanent_node = bookmark_model_->other_node();
+ } else if (update_entity.server_defined_unique_tag == kMobileBookmarksTag) {
+ permanent_node = bookmark_model_->mobile_node();
+ }
+
+ if (permanent_node != nullptr) {
+ bookmark_tracker_->Add(update_entity.id, permanent_node,
+ update.response_version, update_entity.creation_time,
+ update_entity.unique_position,
+ update_entity.specifics);
+ }
+}
+
+} // namespace sync_bookmarks
diff --git a/chromium/components/sync_bookmarks/bookmark_remote_updates_handler.h b/chromium/components/sync_bookmarks/bookmark_remote_updates_handler.h
new file mode 100644
index 00000000000..ed6ad22f4fe
--- /dev/null
+++ b/chromium/components/sync_bookmarks/bookmark_remote_updates_handler.h
@@ -0,0 +1,89 @@
+// 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_SYNC_BOOKMARKS_BOOKMARK_REMOTE_UPDATES_HANDLER_H_
+#define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_REMOTE_UPDATES_HANDLER_H_
+
+#include <vector>
+
+#include "components/sync/engine/non_blocking_sync_common.h"
+#include "components/sync_bookmarks/synced_bookmark_tracker.h"
+
+namespace bookmarks {
+class BookmarkModel;
+class BookmarkNode;
+} // namespace bookmarks
+
+namespace sync_bookmarks {
+
+// Responsible for processing remote updates received from the sync server.
+class BookmarkRemoteUpdatesHandler {
+ public:
+ // |bookmark_model| and |bookmark_tracker| must not be null and most outlive
+ // this object.
+ BookmarkRemoteUpdatesHandler(bookmarks::BookmarkModel* bookmark_model,
+ SyncedBookmarkTracker* bookmark_tracker);
+ // Processes the updates received from the sync server in |updates| and
+ // updates the |bookmark_model_| and |bookmark_tracker_| accordingly.
+ void Process(const syncer::UpdateResponseDataList& updates);
+
+ // Public for testing.
+ static std::vector<const syncer::UpdateResponseData*> ReorderUpdatesForTest(
+ const syncer::UpdateResponseDataList& updates);
+
+ private:
+ // Reorders incoming updates such that parent creation is before child
+ // creation and child deletion is before parent deletion, and deletions should
+ // come last. The returned pointers point to the elements in the original
+ // |updates|.
+ static std::vector<const syncer::UpdateResponseData*> ReorderUpdates(
+ const syncer::UpdateResponseDataList& updates);
+
+ // Given a remote update entity, it returns the parent bookmark node of the
+ // corresponding node. It returns null if the parent node cannot be found.
+ const bookmarks::BookmarkNode* GetParentNode(
+ const syncer::EntityData& update_entity) const;
+
+ // Processes a remote creation of a bookmark node.
+ // 1. For permanent folders, they are only registered in |bookmark_tracker_|.
+ // 2. If the nodes parent cannot be found, the remote creation update is
+ // ignored.
+ // 3. Otherwise, a new node is created in the local bookmark model and
+ // registered in |bookmark_tracker_|.
+ void ProcessRemoteCreate(const syncer::UpdateResponseData& update);
+
+ // Processes a remote update of a bookmark node. |update| must not be a
+ // deletion, and the server_id must be already tracked, otherwise, it is a
+ // creation that gets handeled in ProcessRemoteCreate(). |tracked_entity| is
+ // the tracked entity for that server_id. It is passed as a dependency instead
+ // of performing a lookup inside ProcessRemoteUpdate() to avoid wasting CPU
+ // cycles for doing another lookup (this code runs on the UI thread).
+ void ProcessRemoteUpdate(const syncer::UpdateResponseData& update,
+ const SyncedBookmarkTracker::Entity* tracked_entity);
+
+ // Process a remote delete of a bookmark node. |update_entity| must not be a
+ // deletion. |tracked_entity| is the tracked entity for that server_id. It is
+ // passed as a dependency instead of performing a lookup inside
+ // ProcessRemoteDelete() to avoid wasting CPU cycles for doing another lookup
+ // (this code runs on the UI thread).
+ void ProcessRemoteDelete(const syncer::EntityData& update_entity,
+ const SyncedBookmarkTracker::Entity* tracked_entity);
+
+ // Associates the permanent bookmark folders with the corresponding server
+ // side ids and registers the association in |bookmark_tracker_|.
+ // |update_entity| must contain server_defined_unique_tag that is used to
+ // determine the corresponding permanent node. All permanent nodes are assumed
+ // to be directly children nodes of |kBookmarksRootId|. This method is used in
+ // the initial sync cycle only.
+ void AssociatePermanentFolder(const syncer::UpdateResponseData& update);
+
+ bookmarks::BookmarkModel* const bookmark_model_;
+ SyncedBookmarkTracker* const bookmark_tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(BookmarkRemoteUpdatesHandler);
+};
+
+} // namespace sync_bookmarks
+
+#endif // COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_REMOTE_UPDATES_HANDLER_H_
diff --git a/chromium/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc b/chromium/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
new file mode 100644
index 00000000000..b641fa1adf4
--- /dev/null
+++ b/chromium/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
@@ -0,0 +1,95 @@
+// 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/sync_bookmarks/bookmark_remote_updates_handler.h"
+
+#include <string>
+
+#include "components/sync/base/model_type.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Eq;
+
+namespace sync_bookmarks {
+
+namespace {
+
+// The parent tag for children of the root entity. Entities with this parent are
+// referred to as top level enities.
+const char kRootParentTag[] = "0";
+const char kBookmarkBarTag[] = "bookmark_bar";
+const char kBookmarksRootId[] = "32904_google_chrome_bookmarks";
+
+struct BookmarkInfo {
+ std::string server_id;
+ std::string title;
+ std::string url; // empty for folders.
+ std::string parent_id;
+ std::string server_tag;
+};
+
+syncer::UpdateResponseData CreateUpdateData(const BookmarkInfo& bookmark_info) {
+ syncer::EntityData data;
+ data.id = bookmark_info.server_id;
+ data.parent_id = bookmark_info.parent_id;
+ data.server_defined_unique_tag = bookmark_info.server_tag;
+
+ sync_pb::BookmarkSpecifics* bookmark_specifics =
+ data.specifics.mutable_bookmark();
+ bookmark_specifics->set_title(bookmark_info.title);
+ if (bookmark_info.url.empty()) {
+ data.is_folder = true;
+ } else {
+ bookmark_specifics->set_url(bookmark_info.url);
+ }
+
+ syncer::UpdateResponseData response_data;
+ response_data.entity = data.PassToPtr();
+ // Similar to what's done in the loopback_server.
+ response_data.response_version = 0;
+ return response_data;
+}
+
+syncer::UpdateResponseData CreateBookmarkRootUpdateData() {
+ return CreateUpdateData({syncer::ModelTypeToRootTag(syncer::BOOKMARKS),
+ std::string(), std::string(), kRootParentTag,
+ syncer::ModelTypeToRootTag(syncer::BOOKMARKS)});
+}
+
+TEST(BookmarkRemoteUpdatesHandlerReorderUpdatesTest, ShouldIgnoreRootNodes) {
+ syncer::UpdateResponseDataList updates;
+ updates.push_back(CreateBookmarkRootUpdateData());
+ std::vector<const syncer::UpdateResponseData*> ordered_updates =
+ BookmarkRemoteUpdatesHandler::ReorderUpdatesForTest(updates);
+ // Root node update should be filtered out.
+ EXPECT_THAT(ordered_updates.size(), Eq(0U));
+}
+
+// TODO(crbug.com/516866): This should change to cover the general case of
+// parents before children for non-deletions, and another test should be added
+// for children before parents for deletions.
+TEST(BookmarkRemoteUpdatesHandlerReorderUpdatesTest,
+ ShouldPlacePermanentNodesFirstForNonDeletions) {
+ const std::string kNode1Id = "node1";
+ const std::string kNode2Id = "node2";
+ syncer::UpdateResponseDataList updates;
+ updates.push_back(CreateUpdateData(
+ {kNode1Id, std::string(), std::string(), kNode2Id, std::string()}));
+ updates.push_back(CreateUpdateData({kNode2Id, std::string(), std::string(),
+ kBookmarksRootId, kBookmarkBarTag}));
+ std::vector<const syncer::UpdateResponseData*> ordered_updates =
+ BookmarkRemoteUpdatesHandler::ReorderUpdatesForTest(updates);
+
+ // No update should be dropped.
+ ASSERT_THAT(ordered_updates.size(), Eq(2U));
+
+ // Updates should be ordered such that parent node update comes first.
+ EXPECT_THAT(ordered_updates[0]->entity.value().id, Eq(kNode2Id));
+ EXPECT_THAT(ordered_updates[1]->entity.value().id, Eq(kNode1Id));
+}
+
+} // namespace
+
+} // namespace sync_bookmarks
diff --git a/chromium/components/sync_bookmarks/bookmark_sync_service.cc b/chromium/components/sync_bookmarks/bookmark_sync_service.cc
new file mode 100644
index 00000000000..8a3d8e509e2
--- /dev/null
+++ b/chromium/components/sync_bookmarks/bookmark_sync_service.cc
@@ -0,0 +1,54 @@
+// 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/sync_bookmarks/bookmark_sync_service.h"
+
+#include "base/feature_list.h"
+#include "components/sync/driver/sync_driver_switches.h"
+#include "components/sync_bookmarks/bookmark_model_type_processor.h"
+#include "components/undo/bookmark_undo_service.h"
+
+namespace sync_bookmarks {
+
+BookmarkSyncService::BookmarkSyncService(
+ BookmarkUndoService* bookmark_undo_service) {
+ if (base::FeatureList::IsEnabled(switches::kSyncUSSBookmarks)) {
+ bookmark_model_type_processor_ =
+ std::make_unique<sync_bookmarks::BookmarkModelTypeProcessor>(
+ bookmark_undo_service);
+ }
+}
+
+BookmarkSyncService::~BookmarkSyncService() {}
+
+void BookmarkSyncService::Shutdown() {
+ bookmark_model_type_processor_.reset();
+}
+
+std::string BookmarkSyncService::EncodeBookmarkSyncMetadata() {
+ if (!bookmark_model_type_processor_) {
+ return std::string();
+ }
+ return bookmark_model_type_processor_->EncodeSyncMetadata();
+}
+
+void BookmarkSyncService::DecodeBookmarkSyncMetadata(
+ const std::string& metadata_str,
+ const base::RepeatingClosure& schedule_save_closure,
+ bookmarks::BookmarkModel* model) {
+ if (bookmark_model_type_processor_) {
+ bookmark_model_type_processor_->ModelReadyToSync(
+ metadata_str, schedule_save_closure, model);
+ }
+}
+
+base::WeakPtr<syncer::ModelTypeControllerDelegate>
+BookmarkSyncService::GetBookmarkSyncControllerDelegateOnUIThread() {
+ if (!bookmark_model_type_processor_) {
+ return nullptr;
+ }
+ return bookmark_model_type_processor_->GetWeakPtr();
+}
+
+} // namespace sync_bookmarks
diff --git a/chromium/components/sync_bookmarks/bookmark_sync_service.h b/chromium/components/sync_bookmarks/bookmark_sync_service.h
new file mode 100644
index 00000000000..92a4a66e126
--- /dev/null
+++ b/chromium/components/sync_bookmarks/bookmark_sync_service.h
@@ -0,0 +1,60 @@
+// 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_SYNC_BOOKMARKS_BOOKMARK_SYNC_SERVICE_H_
+#define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_SYNC_SERVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+class BookmarkUndoService;
+
+namespace syncer {
+class ModelTypeControllerDelegate;
+}
+
+namespace bookmarks {
+class BookmarkModel;
+}
+
+namespace sync_bookmarks {
+class BookmarkModelTypeProcessor;
+
+// This service owns the BookmarkModelTypeProcessor.
+class BookmarkSyncService : public KeyedService {
+ public:
+ // |bookmark_undo_service| must not be null and must outlive this object.
+ explicit BookmarkSyncService(BookmarkUndoService* bookmark_undo_service);
+
+ // KeyedService implemenation.
+ ~BookmarkSyncService() override;
+ void Shutdown() override;
+
+ // Analgous to Encode/Decode methods in BookmarkClient.
+ std::string EncodeBookmarkSyncMetadata();
+ void DecodeBookmarkSyncMetadata(
+ const std::string& metadata_str,
+ const base::RepeatingClosure& schedule_save_closure,
+ bookmarks::BookmarkModel* model);
+
+ // Returns the ModelTypeControllerDelegate for syncer::BOOKMARKS.
+ virtual base::WeakPtr<syncer::ModelTypeControllerDelegate>
+ GetBookmarkSyncControllerDelegateOnUIThread();
+
+ private:
+ // BookmarkModelTypeProcessor handles communications between sync engine and
+ // BookmarkModel/HistoryService.
+ std::unique_ptr<sync_bookmarks::BookmarkModelTypeProcessor>
+ bookmark_model_type_processor_;
+
+ DISALLOW_COPY_AND_ASSIGN(BookmarkSyncService);
+};
+
+} // namespace sync_bookmarks
+
+#endif // COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_SYNC_SERVICE_H_
diff --git a/chromium/components/sync_bookmarks/synced_bookmark_tracker.cc b/chromium/components/sync_bookmarks/synced_bookmark_tracker.cc
index 175b16dc944..7efd7c6a91a 100644
--- a/chromium/components/sync_bookmarks/synced_bookmark_tracker.cc
+++ b/chromium/components/sync_bookmarks/synced_bookmark_tracker.cc
@@ -6,30 +6,84 @@
#include <utility>
+#include "base/base64.h"
+#include "base/sha1.h"
+#include "components/bookmarks/browser/bookmark_node.h"
+#include "components/sync/base/time.h"
#include "components/sync/model/entity_data.h"
namespace sync_bookmarks {
+namespace {
+
+void HashSpecifics(const sync_pb::EntitySpecifics& specifics,
+ std::string* hash) {
+ DCHECK_GT(specifics.ByteSize(), 0);
+ base::Base64Encode(base::SHA1HashString(specifics.SerializeAsString()), hash);
+}
+
+} // namespace
+
SyncedBookmarkTracker::Entity::Entity(
- const bookmarks::BookmarkNode* bookmark_node)
- : bookmark_node_(bookmark_node) {
- DCHECK(bookmark_node);
+ const bookmarks::BookmarkNode* bookmark_node,
+ std::unique_ptr<sync_pb::EntityMetadata> metadata)
+ : bookmark_node_(bookmark_node), metadata_(std::move(metadata)) {
+ if (bookmark_node) {
+ DCHECK(!metadata_->is_deleted());
+ } else {
+ DCHECK(metadata_->is_deleted());
+ }
}
SyncedBookmarkTracker::Entity::~Entity() = default;
+bool SyncedBookmarkTracker::Entity::IsUnsynced() const {
+ return metadata_->sequence_number() > metadata_->acked_sequence_number();
+}
+
bool SyncedBookmarkTracker::Entity::MatchesData(
const syncer::EntityData& data) const {
- // TODO(crbug.com/516866): Implement properly.
- return false;
+ // TODO(crbug.com/516866): Check parent id and unique position.
+ // TODO(crbug.com/516866): Compare the actual specifics instead of the
+ // specifics hash.
+ if (metadata_->is_deleted() || data.is_deleted()) {
+ // In case of deletion, no need to check the specifics.
+ return metadata_->is_deleted() == data.is_deleted();
+ }
+ return MatchesSpecificsHash(data.specifics);
}
-bool SyncedBookmarkTracker::Entity::IsUnsynced() const {
- // TODO(crbug.com/516866): Implement properly.
- return false;
+bool SyncedBookmarkTracker::Entity::MatchesSpecificsHash(
+ const sync_pb::EntitySpecifics& specifics) const {
+ DCHECK(!metadata_->is_deleted());
+ DCHECK_GT(specifics.ByteSize(), 0);
+ std::string hash;
+ HashSpecifics(specifics, &hash);
+ return hash == metadata_->specifics_hash();
+}
+
+SyncedBookmarkTracker::SyncedBookmarkTracker(
+ std::vector<NodeMetadataPair> nodes_metadata,
+ std::unique_ptr<sync_pb::ModelTypeState> model_type_state)
+ : model_type_state_(std::move(model_type_state)) {
+ DCHECK(model_type_state_);
+ for (NodeMetadataPair& node_metadata : nodes_metadata) {
+ const std::string& sync_id = node_metadata.second->server_id();
+ auto entity = std::make_unique<Entity>(node_metadata.first,
+ std::move(node_metadata.second));
+ if (node_metadata.first) {
+ // Non-null node means it's not a tombstone.
+ bookmark_node_to_entities_map_[node_metadata.first] = entity.get();
+ } else {
+ // Otherwise, it must be a deletion so we must remember to deletion
+ // ordering.
+ DCHECK(entity->metadata()->is_deleted());
+ ordered_local_tombstones_.push_back(entity.get());
+ }
+ sync_id_to_entities_map_[sync_id] = std::move(entity);
+ }
}
-SyncedBookmarkTracker::SyncedBookmarkTracker() = default;
SyncedBookmarkTracker::~SyncedBookmarkTracker() = default;
const SyncedBookmarkTracker::Entity* SyncedBookmarkTracker::GetEntityForSyncId(
@@ -38,16 +92,179 @@ const SyncedBookmarkTracker::Entity* SyncedBookmarkTracker::GetEntityForSyncId(
return it != sync_id_to_entities_map_.end() ? it->second.get() : nullptr;
}
-void SyncedBookmarkTracker::Associate(
+const SyncedBookmarkTracker::Entity*
+SyncedBookmarkTracker::GetEntityForBookmarkNode(
+ const bookmarks::BookmarkNode* node) const {
+ auto it = bookmark_node_to_entities_map_.find(node);
+ return it != bookmark_node_to_entities_map_.end() ? it->second : nullptr;
+}
+
+void SyncedBookmarkTracker::Add(const std::string& sync_id,
+ const bookmarks::BookmarkNode* bookmark_node,
+ int64_t server_version,
+ base::Time creation_time,
+ const sync_pb::UniquePosition& unique_position,
+ const sync_pb::EntitySpecifics& specifics) {
+ DCHECK_GT(specifics.ByteSize(), 0);
+ auto metadata = std::make_unique<sync_pb::EntityMetadata>();
+ metadata->set_is_deleted(false);
+ metadata->set_server_id(sync_id);
+ metadata->set_server_version(server_version);
+ metadata->set_creation_time(syncer::TimeToProtoTime(creation_time));
+ metadata->set_modification_time(syncer::TimeToProtoTime(creation_time));
+ metadata->set_sequence_number(0);
+ metadata->set_acked_sequence_number(0);
+ metadata->mutable_unique_position()->CopyFrom(unique_position);
+ HashSpecifics(specifics, metadata->mutable_specifics_hash());
+ auto entity = std::make_unique<Entity>(bookmark_node, std::move(metadata));
+ bookmark_node_to_entities_map_[bookmark_node] = entity.get();
+ sync_id_to_entities_map_[sync_id] = std::move(entity);
+}
+
+void SyncedBookmarkTracker::Update(
const std::string& sync_id,
- const bookmarks::BookmarkNode* bookmark_node) {
- sync_id_to_entities_map_[sync_id] = std::make_unique<Entity>(bookmark_node);
+ int64_t server_version,
+ base::Time modification_time,
+ const sync_pb::UniquePosition& unique_position,
+ const sync_pb::EntitySpecifics& specifics) {
+ DCHECK_GT(specifics.ByteSize(), 0);
+ auto it = sync_id_to_entities_map_.find(sync_id);
+ Entity* entity = it->second.get();
+ DCHECK(entity);
+ entity->metadata()->set_server_id(sync_id);
+ entity->metadata()->set_server_version(server_version);
+ entity->metadata()->set_modification_time(
+ syncer::TimeToProtoTime(modification_time));
+ *entity->metadata()->mutable_unique_position() = unique_position;
+ HashSpecifics(specifics, entity->metadata()->mutable_specifics_hash());
+ // 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::Disassociate(const std::string& sync_id) {
+void SyncedBookmarkTracker::MarkDeleted(const std::string& sync_id) {
+ auto it = sync_id_to_entities_map_.find(sync_id);
+ Entity* entity = it->second.get();
+ DCHECK(entity);
+ entity->metadata()->set_is_deleted(true);
+ // Clear all references to the deleted bookmark node.
+ bookmark_node_to_entities_map_.erase(entity->bookmark_node());
+ entity->clear_bookmark_node();
+ ordered_local_tombstones_.push_back(entity);
+}
+
+void SyncedBookmarkTracker::Remove(const std::string& sync_id) {
+ const Entity* entity = GetEntityForSyncId(sync_id);
+ DCHECK(entity);
+ bookmark_node_to_entities_map_.erase(entity->bookmark_node());
+ ordered_local_tombstones_.erase(
+ std::remove(ordered_local_tombstones_.begin(),
+ ordered_local_tombstones_.end(), entity),
+ ordered_local_tombstones_.end());
sync_id_to_entities_map_.erase(sync_id);
}
+void SyncedBookmarkTracker::IncrementSequenceNumber(
+ const std::string& sync_id) {
+ Entity* entity = sync_id_to_entities_map_.find(sync_id)->second.get();
+ DCHECK(entity);
+ // TODO(crbug.com/516866): Update base hash specifics here if the entity is
+ // not already out of sync.
+ entity->metadata()->set_sequence_number(
+ entity->metadata()->sequence_number() + 1);
+}
+
+sync_pb::BookmarkModelMetadata
+SyncedBookmarkTracker::BuildBookmarkModelMetadata() const {
+ sync_pb::BookmarkModelMetadata model_metadata;
+ for (const std::pair<const std::string, std::unique_ptr<Entity>>& pair :
+ sync_id_to_entities_map_) {
+ if (pair.second->metadata()->is_deleted()) {
+ // Deletions will be added later because they need to maintain the same
+ // order as in |ordered_local_tombstones_|.
+ continue;
+ }
+ DCHECK(pair.second->bookmark_node());
+ sync_pb::BookmarkMetadata* bookmark_metadata =
+ model_metadata.add_bookmarks_metadata();
+ bookmark_metadata->set_id(pair.second->bookmark_node()->id());
+ *bookmark_metadata->mutable_metadata() = *pair.second->metadata();
+ }
+ // Add pending deletions.
+ for (const Entity* tombstone_entity : ordered_local_tombstones_) {
+ DCHECK(tombstone_entity->metadata()->is_deleted());
+ sync_pb::BookmarkMetadata* bookmark_metadata =
+ model_metadata.add_bookmarks_metadata();
+ *bookmark_metadata->mutable_metadata() = *tombstone_entity->metadata();
+ }
+ *model_metadata.mutable_model_type_state() = *model_type_state_;
+ return model_metadata;
+}
+
+bool SyncedBookmarkTracker::HasLocalChanges() const {
+ for (const std::pair<const std::string, std::unique_ptr<Entity>>& pair :
+ sync_id_to_entities_map_) {
+ Entity* entity = pair.second.get();
+ if (entity->IsUnsynced()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+std::vector<const SyncedBookmarkTracker::Entity*>
+SyncedBookmarkTracker::GetEntitiesWithLocalChanges(size_t max_entries) const {
+ // TODO(crbug.com/516866): Reorder local changes to e.g. parent creation
+ // before child creation and the otherway around for deletions.
+ // TODO(crbug.com/516866): Return no more than |max_entries|.
+ std::vector<const SyncedBookmarkTracker::Entity*> entities_with_local_changes;
+ for (const std::pair<const std::string, std::unique_ptr<Entity>>& pair :
+ sync_id_to_entities_map_) {
+ Entity* entity = pair.second.get();
+ if (entity->metadata()->is_deleted()) {
+ // Deletion are stored sorted in |ordered_local_tombstones_| and will be
+ // added later.
+ continue;
+ }
+ if (entity->IsUnsynced()) {
+ entities_with_local_changes.push_back(entity);
+ }
+ }
+ for (const Entity* tombstone_entity : ordered_local_tombstones_) {
+ entities_with_local_changes.push_back(tombstone_entity);
+ }
+ return entities_with_local_changes;
+}
+
+void SyncedBookmarkTracker::UpdateUponCommitResponse(
+ const std::string& old_id,
+ const std::string& new_id,
+ int64_t acked_sequence_number,
+ int64_t server_version) {
+ // TODO(crbug.com/516866): Update specifics if we decide to keep it.
+ auto it = sync_id_to_entities_map_.find(old_id);
+ Entity* entity =
+ it != sync_id_to_entities_map_.end() ? it->second.get() : nullptr;
+ if (!entity) {
+ DLOG(WARNING) << "Trying to update a non existing entity.";
+ return;
+ }
+
+ entity->metadata()->set_acked_sequence_number(acked_sequence_number);
+ entity->metadata()->set_server_version(server_version);
+ // If there are no pending commits, remove tombstones.
+ if (!entity->IsUnsynced() && entity->metadata()->is_deleted()) {
+ Remove(old_id);
+ return;
+ }
+
+ if (old_id != new_id) {
+ auto it = sync_id_to_entities_map_.find(old_id);
+ entity->metadata()->set_server_id(new_id);
+ sync_id_to_entities_map_[new_id] = std::move(it->second);
+ sync_id_to_entities_map_.erase(old_id);
+ }
+}
+
std::size_t SyncedBookmarkTracker::TrackedEntitiesCountForTest() const {
return sync_id_to_entities_map_.size();
}
diff --git a/chromium/components/sync_bookmarks/synced_bookmark_tracker.h b/chromium/components/sync_bookmarks/synced_bookmark_tracker.h
index 84e5850ff3c..415c5e3a09f 100644
--- a/chromium/components/sync_bookmarks/synced_bookmark_tracker.h
+++ b/chromium/components/sync_bookmarks/synced_bookmark_tracker.h
@@ -8,9 +8,14 @@
#include <map>
#include <memory>
#include <string>
+#include <utility>
+#include <vector>
#include "base/macros.h"
+#include "base/time/time.h"
+#include "components/sync/protocol/bookmark_model_metadata.pb.h"
#include "components/sync/protocol/entity_metadata.pb.h"
+#include "components/sync/protocol/unique_position.pb.h"
namespace bookmarks {
class BookmarkNode;
@@ -22,6 +27,9 @@ struct EntityData;
namespace sync_bookmarks {
+using NodeMetadataPair = std::pair<const bookmarks::BookmarkNode*,
+ std::unique_ptr<sync_pb::EntityMetadata>>;
+
// This class is responsible for keeping the mapping between bookmarks node in
// the local model and the server-side corresponding sync entities. It manages
// the metadata for its entity and caches entity data upon a local change until
@@ -30,8 +38,9 @@ class SyncedBookmarkTracker {
public:
class Entity {
public:
- // |bookmark_node| must not be null and must outlive this object.
- explicit Entity(const bookmarks::BookmarkNode* bookmark_node);
+ // |bookmark_node| can be null for tombstones. |metadata| must not be null.
+ Entity(const bookmarks::BookmarkNode* bookmark_node,
+ std::unique_ptr<sync_pb::EntityMetadata> metadata);
~Entity();
// Returns true if this data is out of sync with the server.
@@ -41,42 +50,124 @@ class SyncedBookmarkTracker {
// Check whether |data| matches the stored specifics hash.
bool MatchesData(const syncer::EntityData& data) const;
- // It never returns null.
+ // Returns null for tomstones.
const bookmarks::BookmarkNode* bookmark_node() const {
return bookmark_node_;
}
+ // Used in local deletions to mark and entity as a tommstone.
+ void clear_bookmark_node() { bookmark_node_ = nullptr; }
+
+ const sync_pb::EntityMetadata* metadata() const { return metadata_.get(); }
+ sync_pb::EntityMetadata* metadata() { return metadata_.get(); }
+
private:
- const bookmarks::BookmarkNode* const bookmark_node_;
+ // Check whether |specifics| matches the stored specifics_hash.
+ bool MatchesSpecificsHash(const sync_pb::EntitySpecifics& specifics) const;
+
+ // Null for tombstones.
+ const bookmarks::BookmarkNode* bookmark_node_;
+
+ // Serializable Sync metadata.
+ std::unique_ptr<sync_pb::EntityMetadata> metadata_;
DISALLOW_COPY_AND_ASSIGN(Entity);
};
- SyncedBookmarkTracker();
+ // |model_type_state| must not be null. null nodes in |nodes_metadata| can be
+ // used to represent local tombstones.
+ SyncedBookmarkTracker(
+ std::vector<NodeMetadataPair> nodes_metadata,
+ std::unique_ptr<sync_pb::ModelTypeState> model_type_state);
~SyncedBookmarkTracker();
- // Returns null if not entity is found.
+ // Returns null if no entity is found.
const Entity* GetEntityForSyncId(const std::string& sync_id) const;
- // Associates a server id with the corresponding local bookmark node in
+ // Returns null if no entity is found.
+ const SyncedBookmarkTracker::Entity* GetEntityForBookmarkNode(
+ const bookmarks::BookmarkNode* node) const;
+
+ // Adds an entry for the |sync_id| and the corresponding local bookmark node
+ // and metadata in |sync_id_to_entities_map_|.
+ void Add(const std::string& sync_id,
+ const bookmarks::BookmarkNode* bookmark_node,
+ int64_t server_version,
+ base::Time creation_time,
+ const sync_pb::UniquePosition& unique_position,
+ const sync_pb::EntitySpecifics& specifics);
+
+ // Updates an existing entry for the |sync_id| and the corresponding metadata
+ // in |sync_id_to_entities_map_|.
+ void Update(const std::string& sync_id,
+ int64_t server_version,
+ base::Time modification_time,
+ const sync_pb::UniquePosition& unique_position,
+ const sync_pb::EntitySpecifics& specifics);
+
+ // This class maintains the order of calls to this method and the same order
+ // is gauaranteed when returning local changes in
+ // GetEntitiesWithLocalChanges() as well as in BuildBookmarkModelMetadata().
+ void MarkDeleted(const std::string& sync_id);
+
+ // Removes the entry coressponding to the |sync_id| from
// |sync_id_to_entities_map_|.
- void Associate(const std::string& sync_id,
- const bookmarks::BookmarkNode* bookmark_node);
+ void Remove(const std::string& sync_id);
- // Removes the association that corresponds to |sync_id| from
- // |sync_id_to_entities_map_|.
- void Disassociate(const std::string& sync_id);
+ // Increment sequence number in the metadata for the entity with |sync_id|.
+ // Tracker must contain a non-tomstone entity with server id = |sync_id|.
+ void IncrementSequenceNumber(const std::string& sync_id);
+
+ sync_pb::BookmarkModelMetadata BuildBookmarkModelMetadata() const;
+
+ // Returns true if there are any local entities to be committed.
+ bool HasLocalChanges() const;
+
+ const sync_pb::ModelTypeState& model_type_state() const {
+ return *model_type_state_;
+ }
+
+ void set_model_type_state(
+ std::unique_ptr<sync_pb::ModelTypeState> model_type_state) {
+ model_type_state_ = std::move(model_type_state);
+ }
+
+ std::vector<const Entity*> GetEntitiesWithLocalChanges(
+ size_t max_entries) const;
+
+ // Updates the tracker after receiving the commit response. |old_id| should be
+ // equal to |new_id| for all updates except the initial commit, where the
+ // temporary client-generated ID will be overriden by the server-provided
+ // final ID. In which case |sync_id_to_entities_map_| will be updated
+ // accordingly.
+ void UpdateUponCommitResponse(const std::string& old_id,
+ const std::string& new_id,
+ int64_t acked_sequence_number,
+ int64_t server_version);
// Returns number of tracked entities. Used only in test.
std::size_t TrackedEntitiesCountForTest() const;
private:
// A map of sync server ids to sync entities. This should contain entries and
- // metadata for almost everything. However, since local data are loaded only
- // when needed (e.g. before a commit cycle), the entities may not always
- // contain model type data/specifics.
+ // metadata for almost everything.
std::map<std::string, std::unique_ptr<Entity>> sync_id_to_entities_map_;
+ // A map of bookmark nodes to sync entities. It's keyed by the bookmark node
+ // pointers which get assigned when loading the bookmark model. This map is
+ // first initialized in the constructor.
+ std::map<const bookmarks::BookmarkNode*, Entity*>
+ bookmark_node_to_entities_map_;
+
+ // A list of pending local bookmark deletions. They should be sent to the
+ // server in the same order as stored in the list. The same order should also
+ // be maintained across browser restarts (i.e. across calls to the ctor() and
+ // BuildBookmarkModelMetadata().
+ std::vector<Entity*> ordered_local_tombstones_;
+
+ // The model metadata (progress marker, initial sync done, etc).
+ std::unique_ptr<sync_pb::ModelTypeState> model_type_state_;
+
DISALLOW_COPY_AND_ASSIGN(SyncedBookmarkTracker);
};
diff --git a/chromium/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc b/chromium/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
index 877eeaf2193..5fafc831de6 100644
--- a/chromium/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
+++ b/chromium/components/sync_bookmarks/synced_bookmark_tracker_unittest.cc
@@ -4,7 +4,12 @@
#include "components/sync_bookmarks/synced_bookmark_tracker.h"
+#include "base/base64.h"
+#include "base/sha1.h"
#include "components/bookmarks/browser/bookmark_node.h"
+#include "components/sync/base/time.h"
+#include "components/sync/base/unique_position.h"
+#include "components/sync/model/entity_data.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -16,28 +21,246 @@ namespace sync_bookmarks {
namespace {
+sync_pb::EntitySpecifics GenerateSpecifics(const std::string& title,
+ const std::string& url) {
+ sync_pb::EntitySpecifics specifics;
+ specifics.mutable_bookmark()->set_title(title);
+ specifics.mutable_bookmark()->set_url(url);
+ return specifics;
+}
+
TEST(SyncedBookmarkTrackerTest, ShouldGetAssociatedNodes) {
- SyncedBookmarkTracker tracker;
+ SyncedBookmarkTracker tracker(std::vector<NodeMetadataPair>(),
+ std::make_unique<sync_pb::ModelTypeState>());
const std::string kSyncId = "SYNC_ID";
+ const std::string kTitle = "Title";
+ const GURL kUrl("http://www.foo.com");
const int64_t kId = 1;
- bookmarks::BookmarkNode node(kId, GURL());
- tracker.Associate(kSyncId, &node);
+ const int64_t kServerVersion = 1000;
+ const base::Time kCreationTime(base::Time::Now() -
+ base::TimeDelta::FromSeconds(1));
+ const syncer::UniquePosition unique_position =
+ syncer::UniquePosition::InitialPosition(
+ syncer::UniquePosition::RandomSuffix());
+ const sync_pb::EntitySpecifics specifics =
+ GenerateSpecifics(/*title=*/std::string(), /*url=*/std::string());
+
+ bookmarks::BookmarkNode node(kId, kUrl);
+ tracker.Add(kSyncId, &node, kServerVersion, kCreationTime,
+ unique_position.ToProto(), specifics);
const SyncedBookmarkTracker::Entity* entity =
tracker.GetEntityForSyncId(kSyncId);
ASSERT_THAT(entity, NotNull());
EXPECT_THAT(entity->bookmark_node(), Eq(&node));
+ EXPECT_THAT(entity->metadata()->server_id(), Eq(kSyncId));
+ EXPECT_THAT(entity->metadata()->server_version(), Eq(kServerVersion));
+ EXPECT_THAT(entity->metadata()->creation_time(),
+ Eq(syncer::TimeToProtoTime(kCreationTime)));
+ EXPECT_TRUE(
+ syncer::UniquePosition::FromProto(entity->metadata()->unique_position())
+ .Equals(unique_position));
+
+ syncer::EntityData data;
+ *data.specifics.mutable_bookmark() = specifics.bookmark();
+ EXPECT_TRUE(entity->MatchesData(data));
EXPECT_THAT(tracker.GetEntityForSyncId("unknown id"), IsNull());
}
TEST(SyncedBookmarkTrackerTest, ShouldReturnNullForDisassociatedNodes) {
- SyncedBookmarkTracker tracker;
+ SyncedBookmarkTracker tracker(std::vector<NodeMetadataPair>(),
+ std::make_unique<sync_pb::ModelTypeState>());
+ const std::string kSyncId = "SYNC_ID";
+ const int64_t kId = 1;
+ const int64_t kServerVersion = 1000;
+ const base::Time kModificationTime(base::Time::Now() -
+ base::TimeDelta::FromSeconds(1));
+ const sync_pb::UniquePosition unique_position;
+ const sync_pb::EntitySpecifics specifics =
+ GenerateSpecifics(/*title=*/std::string(), /*url=*/std::string());
+ bookmarks::BookmarkNode node(kId, GURL());
+ tracker.Add(kSyncId, &node, kServerVersion, kModificationTime,
+ unique_position, specifics);
+ ASSERT_THAT(tracker.GetEntityForSyncId(kSyncId), NotNull());
+ tracker.Remove(kSyncId);
+ EXPECT_THAT(tracker.GetEntityForSyncId(kSyncId), IsNull());
+}
+
+TEST(SyncedBookmarkTrackerTest,
+ ShouldRequireCommitRequestWhenSequenceNumberIsIncremented) {
+ SyncedBookmarkTracker tracker(std::vector<NodeMetadataPair>(),
+ std::make_unique<sync_pb::ModelTypeState>());
const std::string kSyncId = "SYNC_ID";
const int64_t kId = 1;
+ const int64_t kServerVersion = 1000;
+ const base::Time kModificationTime(base::Time::Now() -
+ base::TimeDelta::FromSeconds(1));
+ const sync_pb::UniquePosition unique_position;
+ const sync_pb::EntitySpecifics specifics =
+ GenerateSpecifics(/*title=*/std::string(), /*url=*/std::string());
bookmarks::BookmarkNode node(kId, GURL());
- tracker.Associate(kSyncId, &node);
+ tracker.Add(kSyncId, &node, kServerVersion, kModificationTime,
+ unique_position, specifics);
+
+ EXPECT_THAT(tracker.HasLocalChanges(), Eq(false));
+ tracker.IncrementSequenceNumber(kSyncId);
+ EXPECT_THAT(tracker.HasLocalChanges(), Eq(true));
+ // TODO(crbug.com/516866): Test HasLocalChanges after submitting commit
+ // request in a separate test probably.
+}
+
+TEST(SyncedBookmarkTrackerTest, ShouldUpdateUponCommitResponseWithNewId) {
+ SyncedBookmarkTracker tracker(std::vector<NodeMetadataPair>(),
+ std::make_unique<sync_pb::ModelTypeState>());
+ const std::string kSyncId = "SYNC_ID";
+ const std::string kNewSyncId = "NEW_SYNC_ID";
+ const int64_t kId = 1;
+ const int64_t kServerVersion = 1000;
+ const int64_t kNewServerVersion = 1001;
+ const base::Time kModificationTime(base::Time::Now() -
+ base::TimeDelta::FromSeconds(1));
+ const sync_pb::UniquePosition unique_position;
+ const sync_pb::EntitySpecifics specifics =
+ GenerateSpecifics(/*title=*/std::string(), /*url=*/std::string());
+ bookmarks::BookmarkNode node(kId, GURL());
+ tracker.Add(kSyncId, &node, kServerVersion, kModificationTime,
+ unique_position, specifics);
ASSERT_THAT(tracker.GetEntityForSyncId(kSyncId), NotNull());
- tracker.Disassociate(kSyncId);
+ // Receive a commit response with a changed id.
+ tracker.UpdateUponCommitResponse(
+ kSyncId, kNewSyncId, /*acked_sequence_number=*/1, kNewServerVersion);
+ // Old id shouldn't be there.
EXPECT_THAT(tracker.GetEntityForSyncId(kSyncId), IsNull());
+
+ const SyncedBookmarkTracker::Entity* entity =
+ tracker.GetEntityForSyncId(kNewSyncId);
+ ASSERT_THAT(entity, NotNull());
+ EXPECT_THAT(entity->metadata()->server_id(), Eq(kNewSyncId));
+ EXPECT_THAT(entity->bookmark_node(), Eq(&node));
+ EXPECT_THAT(entity->metadata()->server_version(), Eq(kNewServerVersion));
+}
+
+TEST(SyncedBookmarkTrackerTest,
+ ShouldMaintainTombstoneOrderBetweenCtorAndBuildBookmarkModelMetadata) {
+ // Feed a metadata batch of 5 entries to the constructor of the tracker.
+ // First 2 are for node, and the last 4 are for tombstones.
+
+ // Server ids.
+ const std::string kId0 = "id0";
+ const std::string kId1 = "id1";
+ const std::string kId2 = "id2";
+ const std::string kId3 = "id3";
+ const std::string kId4 = "id4";
+
+ const GURL kUrl("http://www.foo.com");
+ bookmarks::BookmarkNode node0(/*id=*/0, kUrl);
+ bookmarks::BookmarkNode node1(/*id=*/1, kUrl);
+
+ auto metadata0 = std::make_unique<sync_pb::EntityMetadata>();
+ metadata0->set_server_id(kId0);
+
+ auto metadata1 = std::make_unique<sync_pb::EntityMetadata>();
+ metadata1->set_server_id(kId1);
+
+ auto metadata2 = std::make_unique<sync_pb::EntityMetadata>();
+ metadata2->set_server_id(kId2);
+ metadata2->set_is_deleted(true);
+
+ auto metadata3 = std::make_unique<sync_pb::EntityMetadata>();
+ metadata3->set_server_id(kId3);
+ metadata3->set_is_deleted(true);
+
+ auto metadata4 = std::make_unique<sync_pb::EntityMetadata>();
+ metadata4->set_server_id(kId4);
+ metadata4->set_is_deleted(true);
+
+ std::vector<NodeMetadataPair> node_metadata_pairs;
+ node_metadata_pairs.emplace_back(&node0, std::move(metadata0));
+ node_metadata_pairs.emplace_back(&node1, std::move(metadata1));
+ node_metadata_pairs.emplace_back(nullptr, std::move(metadata2));
+ node_metadata_pairs.emplace_back(nullptr, std::move(metadata3));
+ node_metadata_pairs.emplace_back(nullptr, std::move(metadata4));
+
+ SyncedBookmarkTracker tracker(std::move(node_metadata_pairs),
+ std::make_unique<sync_pb::ModelTypeState>());
+
+ sync_pb::BookmarkModelMetadata bookmark_model_metadata =
+ tracker.BuildBookmarkModelMetadata();
+
+ // Tombstones should be the last 3 entries in the metadata and in the same
+ // order as given to the constructor.
+ ASSERT_THAT(bookmark_model_metadata.bookmarks_metadata().size(), Eq(5));
+ EXPECT_THAT(
+ bookmark_model_metadata.bookmarks_metadata(2).metadata().server_id(),
+ Eq(kId2));
+ EXPECT_THAT(
+ bookmark_model_metadata.bookmarks_metadata(3).metadata().server_id(),
+ Eq(kId3));
+ EXPECT_THAT(
+ bookmark_model_metadata.bookmarks_metadata(4).metadata().server_id(),
+ Eq(kId4));
+}
+
+TEST(SyncedBookmarkTrackerTest,
+ ShouldMaintainOrderOfMarkDeletedCallsWhenBuildBookmarkModelMetadata) {
+ // Server ids.
+ const std::string kId0 = "id0";
+ const std::string kId1 = "id1";
+ const std::string kId2 = "id2";
+ const std::string kId3 = "id3";
+ const std::string kId4 = "id4";
+
+ const GURL kUrl("http://www.foo.com");
+ bookmarks::BookmarkNode node0(/*id=*/0, kUrl);
+ bookmarks::BookmarkNode node1(/*id=*/1, kUrl);
+ bookmarks::BookmarkNode node2(/*id=*/2, kUrl);
+ bookmarks::BookmarkNode node3(/*id=*/3, kUrl);
+ bookmarks::BookmarkNode node4(/*id=*/4, kUrl);
+
+ auto metadata0 = std::make_unique<sync_pb::EntityMetadata>();
+ metadata0->set_server_id(kId0);
+
+ auto metadata1 = std::make_unique<sync_pb::EntityMetadata>();
+ metadata1->set_server_id(kId1);
+
+ auto metadata2 = std::make_unique<sync_pb::EntityMetadata>();
+ metadata2->set_server_id(kId2);
+
+ auto metadata3 = std::make_unique<sync_pb::EntityMetadata>();
+ metadata3->set_server_id(kId3);
+
+ auto metadata4 = std::make_unique<sync_pb::EntityMetadata>();
+ metadata4->set_server_id(kId4);
+
+ std::vector<NodeMetadataPair> node_metadata_pairs;
+ node_metadata_pairs.emplace_back(&node0, std::move(metadata0));
+ node_metadata_pairs.emplace_back(&node1, std::move(metadata1));
+ node_metadata_pairs.emplace_back(&node2, std::move(metadata2));
+ node_metadata_pairs.emplace_back(&node3, std::move(metadata3));
+ node_metadata_pairs.emplace_back(&node4, std::move(metadata4));
+
+ SyncedBookmarkTracker tracker(std::move(node_metadata_pairs),
+ std::make_unique<sync_pb::ModelTypeState>());
+
+ // Mark entities deleted in that order kId2, kId4, kId1
+ tracker.MarkDeleted(kId2);
+ tracker.MarkDeleted(kId4);
+ tracker.MarkDeleted(kId1);
+
+ sync_pb::BookmarkModelMetadata bookmark_model_metadata =
+ tracker.BuildBookmarkModelMetadata();
+
+ // Tombstones should be the last 3 entries in the metadata and in the same as
+ // calling MarkDeleted().
+ ASSERT_THAT(bookmark_model_metadata.bookmarks_metadata().size(), Eq(5));
+ EXPECT_THAT(
+ bookmark_model_metadata.bookmarks_metadata(2).metadata().server_id(),
+ Eq(kId2));
+ EXPECT_THAT(
+ bookmark_model_metadata.bookmarks_metadata(3).metadata().server_id(),
+ Eq(kId4));
+ EXPECT_THAT(
+ bookmark_model_metadata.bookmarks_metadata(4).metadata().server_id(),
+ Eq(kId1));
}
} // namespace
diff --git a/chromium/components/sync_preferences/BUILD.gn b/chromium/components/sync_preferences/BUILD.gn
index 49ca9514a71..e55d6b66728 100644
--- a/chromium/components/sync_preferences/BUILD.gn
+++ b/chromium/components/sync_preferences/BUILD.gn
@@ -17,6 +17,8 @@ static_library("sync_preferences") {
"synced_pref_change_registrar.cc",
"synced_pref_change_registrar.h",
"synced_pref_observer.h",
+ "unknown_user_pref_accessor.cc",
+ "unknown_user_pref_accessor.h",
]
deps = [
@@ -61,6 +63,7 @@ source_set("unit_tests") {
deps = [
":sync_preferences",
":test_support",
+ "//base/test:test_support",
"//components/pref_registry",
"//components/prefs",
"//components/prefs:test_support",
diff --git a/chromium/components/sync_preferences/pref_model_associator.cc b/chromium/components/sync_preferences/pref_model_associator.cc
index 2aeff8dbbe1..0fe87465d75 100644
--- a/chromium/components/sync_preferences/pref_model_associator.cc
+++ b/chromium/components/sync_preferences/pref_model_associator.cc
@@ -15,8 +15,11 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
+#include "components/prefs/persistent_pref_store.h"
#include "components/prefs/pref_service.h"
#include "components/sync/model/sync_change.h"
#include "components/sync/model/sync_error_factory.h"
@@ -24,6 +27,7 @@
#include "components/sync/protocol/sync.pb.h"
#include "components/sync_preferences/pref_model_associator_client.h"
#include "components/sync_preferences/pref_service_syncable.h"
+#include "components/sync_preferences/synced_pref_observer.h"
using syncer::PREFERENCES;
using syncer::PRIORITY_PREFERENCES;
@@ -58,12 +62,9 @@ sync_pb::PreferenceSpecifics* GetMutableSpecifics(
PrefModelAssociator::PrefModelAssociator(
const PrefModelAssociatorClient* client,
- syncer::ModelType type)
- : models_associated_(false),
- processing_syncer_changes_(false),
- pref_service_(nullptr),
- type_(type),
- client_(client) {
+ syncer::ModelType type,
+ UnknownUserPrefAccessor* accessor)
+ : pref_accessor_(accessor), type_(type), client_(client) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(type_ == PREFERENCES || type_ == PRIORITY_PREFERENCES);
}
@@ -79,8 +80,15 @@ void PrefModelAssociator::InitPrefAndAssociate(
const syncer::SyncData& sync_pref,
const std::string& pref_name,
syncer::SyncChangeList* sync_changes) {
- const base::Value* user_pref_value =
- pref_service_->GetUserPrefValue(pref_name);
+ UnknownUserPrefAccessor::PreferenceState local_pref_state =
+ pref_accessor_->GetPreferenceState(type_, pref_name);
+ if (local_pref_state.registration_state ==
+ UnknownUserPrefAccessor::RegistrationState::kUnknown ||
+ local_pref_state.registration_state ==
+ UnknownUserPrefAccessor::RegistrationState::kNotSyncable) {
+ // Only process syncable prefs and unknown prefs if whitelisted.
+ return;
+ }
VLOG(1) << "Associating preference " << pref_name;
if (sync_pref.IsValid()) {
@@ -95,25 +103,20 @@ void PrefModelAssociator::InitPrefAndAssociate(
return;
}
- if (user_pref_value) {
+ if (local_pref_state.persisted_value) {
DVLOG(1) << "Found user pref value for " << pref_name;
// We have both server and local values. Merge them.
- std::unique_ptr<base::Value> new_value(
- MergePreference(pref_name, *user_pref_value, *sync_value));
+ std::unique_ptr<base::Value> new_value(MergePreference(
+ pref_name, *local_pref_state.persisted_value, *sync_value));
// Update the local preference based on what we got from the
// sync server. Note: this only updates the user value store, which is
// ignored if the preference is policy controlled.
if (new_value->is_none()) {
LOG(WARNING) << "Sync has null value for pref " << pref_name.c_str();
- pref_service_->ClearPref(pref_name);
- } else if (new_value->type() != user_pref_value->type()) {
- LOG(WARNING) << "Synced value for " << preference.name()
- << " is of type " << new_value->type()
- << " which doesn't match pref type "
- << user_pref_value->type();
- } else if (!user_pref_value->Equals(new_value.get())) {
- pref_service_->Set(pref_name, *new_value);
+ pref_accessor_->ClearPref(pref_name, local_pref_state);
+ } else if (!local_pref_state.persisted_value->Equals(new_value.get())) {
+ pref_accessor_->SetPref(pref_name, local_pref_state, *new_value);
}
// If the merge resulted in an updated value, inform the syncer.
@@ -129,16 +132,19 @@ void PrefModelAssociator::InitPrefAndAssociate(
}
} else if (!sync_value->is_none()) {
// Only a server value exists. Just set the local user value.
- pref_service_->Set(pref_name, *sync_value);
+ pref_accessor_->SetPref(pref_name, local_pref_state, *sync_value);
} else {
LOG(WARNING) << "Sync has null value for pref " << pref_name.c_str();
}
synced_preferences_.insert(preference.name());
- } else if (user_pref_value) {
+ } else if (local_pref_state.persisted_value) {
+ DCHECK_EQ(local_pref_state.registration_state,
+ UnknownUserPrefAccessor::RegistrationState::kSyncable);
// The server does not know about this preference and should be added
// to the syncer's database.
syncer::SyncData sync_data;
- if (!CreatePrefSyncData(pref_name, *user_pref_value, &sync_data)) {
+ if (!CreatePrefSyncData(pref_name, *local_pref_state.persisted_value,
+ &sync_data)) {
LOG(ERROR) << "Failed to update preference.";
return;
}
@@ -188,16 +194,6 @@ syncer::SyncMergeResult PrefModelAssociator::MergeDataAndStartSyncing(
const sync_pb::PreferenceSpecifics& preference = GetSpecifics(*sync_iter);
std::string sync_pref_name = preference.name();
-
- if (remaining_preferences.count(sync_pref_name) == 0) {
- // We're not syncing this preference locally, ignore the sync data.
- // TODO(zea): Eventually we want to be able to have the syncable service
- // reconstruct all sync data for its datatype (therefore having
- // GetAllSyncData be a complete representation). We should store this
- // data somewhere, even if we don't use it.
- continue;
- }
-
remaining_preferences.erase(sync_pref_name);
InitPrefAndAssociate(*sync_iter, sync_pref_name, &new_changes);
}
@@ -209,6 +205,9 @@ syncer::SyncMergeResult PrefModelAssociator::MergeDataAndStartSyncing(
InitPrefAndAssociate(syncer::SyncData(), *pref_name_iter, &new_changes);
}
+ UMA_HISTOGRAM_COUNTS_1000("Sync.Preferences.SyncingUnknownPrefs",
+ pref_accessor_->GetNumberOfSyncingUnknownPrefs());
+
// Push updates to sync.
merge_result.set_error(
sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes));
@@ -294,7 +293,7 @@ std::unique_ptr<base::Value> PrefModelAssociator::MergeListValues(
base::Value result = to_value.Clone();
base::Value::ListStorage& list = result.GetList();
for (const auto& value : from_value.GetList()) {
- if (std::find(list.begin(), list.end(), value) == list.end())
+ if (!base::ContainsValue(list, value))
list.emplace_back(value.Clone());
}
@@ -329,10 +328,6 @@ base::Value PrefModelAssociator::MergeDictionaryValues(
return result;
}
-// Note: This will build a model of all preferences registered as syncable
-// with user controlled data. We do not track any information for preferences
-// not registered locally as syncable and do not inform the syncer of
-// non-user controlled preferences.
syncer::SyncDataList PrefModelAssociator::GetAllSyncData(
syncer::ModelType type) const {
DCHECK_EQ(type_, type);
@@ -340,6 +335,10 @@ syncer::SyncDataList PrefModelAssociator::GetAllSyncData(
for (PreferenceSet::const_iterator iter = synced_preferences_.begin();
iter != synced_preferences_.end(); ++iter) {
std::string name = *iter;
+ if (pref_accessor_->GetPreferenceState(type_, name).registration_state !=
+ UnknownUserPrefAccessor::RegistrationState::kSyncable) {
+ continue;
+ }
const PrefService::Preference* pref = pref_service_->FindPreference(name);
DCHECK(pref);
if (!pref->IsUserControlled() || pref->IsDefaultValue())
@@ -369,39 +368,47 @@ syncer::SyncError PrefModelAssociator::ProcessSyncChanges(
const sync_pb::PreferenceSpecifics& pref_specifics =
GetSpecifics(iter->sync_data());
- std::string name = pref_specifics.name();
- // It is possible that we may receive a change to a preference we do not
- // want to sync. For example if the user is syncing a Mac client and a
- // 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))
+ UnknownUserPrefAccessor::PreferenceState local_pref_state =
+ pref_accessor_->GetPreferenceState(type_, pref_specifics.name());
+ if (local_pref_state.registration_state ==
+ UnknownUserPrefAccessor::RegistrationState::kUnknown) {
+ // It is possible that we may receive a change to a preference we do not
+ // want to sync. For example if the user is syncing a Mac client and a
+ // Windows client, the Windows client does not support
+ // kConfirmToQuitEnabled. Ignore updates from these preferences.
+ // We only sync such prefs if they are whitelisted.
continue;
-
+ }
+ if (local_pref_state.registration_state ==
+ UnknownUserPrefAccessor::RegistrationState::kNotSyncable) {
+ // Don't process remote changes for prefs this client doesn't want synced.
+ continue;
+ }
if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) {
- pref_service_->ClearPref(pref_name);
+ pref_accessor_->ClearPref(pref_specifics.name(), local_pref_state);
continue;
}
- std::unique_ptr<base::Value> value(ReadPreferenceSpecifics(pref_specifics));
- if (!value.get()) {
+ std::unique_ptr<base::Value> new_value(
+ ReadPreferenceSpecifics(pref_specifics));
+ if (!new_value.get()) {
// Skip values we can't deserialize.
- // TODO(zea): consider taking some further action such as erasing the bad
- // data.
+ // TODO(zea): consider taking some further action such as erasing the
+ // bad data.
continue;
}
// This will only modify the user controlled value store, which takes
// priority over the default value but is ignored if the preference is
// policy controlled.
- pref_service_->Set(pref_name, *value);
+ pref_accessor_->SetPref(pref_specifics.name(), local_pref_state,
+ *new_value);
NotifySyncedPrefObservers(pref_specifics.name(), true /*from_sync*/);
- // Keep track of any newly synced preferences.
- if (iter->change_type() == syncer::SyncChange::ACTION_ADD) {
- synced_preferences_.insert(pref_specifics.name());
- }
+ // Keep track of any newly synced preferences. This can happen if a
+ // preference was late registered or remotely added (ACTION_ADD).
+ synced_preferences_.insert(pref_specifics.name());
}
return syncer::SyncError();
}
@@ -443,9 +450,16 @@ void PrefModelAssociator::RemoveSyncedPrefObserver(
observer_iter->second->RemoveObserver(observer);
}
-void PrefModelAssociator::RegisterPref(const char* name) {
- DCHECK(!models_associated_ && registered_preferences_.count(name) == 0);
+void PrefModelAssociator::RegisterPref(const std::string& name) {
+ DCHECK(!registered_preferences_.count(name));
registered_preferences_.insert(name);
+
+ // This pref might be registered after sync started. Make sure data in the
+ // local store matches the registered type.
+ // If this results in a modification of the local pref store, we don't want
+ // to tell ChromeSync about these -- it's a local anomaly,
+ base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true);
+ pref_accessor_->EnforceRegisteredTypeInStore(name);
}
bool PrefModelAssociator::IsPrefRegistered(const std::string& name) const {
@@ -457,24 +471,33 @@ void PrefModelAssociator::ProcessPrefChange(const std::string& name) {
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_)
return;
+ // From now on, this method does not have to deal with lazily registered
+ // prefs, as local changes can only happen after they were registered.
+
const PrefService::Preference* preference =
pref_service_->FindPreference(name);
+ // TODO(tschumann): When can this ever happen? Should this be a DCHECK?
if (!preference)
return;
- if (!IsPrefRegistered(name))
- return; // We are not syncing this preference.
+ if (!IsPrefRegistered(name)) {
+ // We are not syncing this preference -- this also filters out synced
+ // preferences of the wrong type (priority preference are handled by a
+ // separate associator).
+ return;
+ }
syncer::SyncChangeList changes;
if (!preference->IsUserModifiable()) {
- // If the preference is no longer user modifiable, it must now be controlled
- // by policy, whose values we do not sync. Just return. If the preference
- // stops being controlled by policy, it will revert back to the user value
- // (which we continue to update with sync changes).
+ // If the preference is no longer user modifiable, it must now be
+ // controlled by policy, whose values we do not sync. Just return. If the
+ // preference stops being controlled by policy, it will revert back to the
+ // user value (which we continue to update with sync changes).
return;
}
@@ -483,9 +506,10 @@ void PrefModelAssociator::ProcessPrefChange(const std::string& name) {
NotifySyncedPrefObservers(name, false /*from_sync*/);
if (synced_preferences_.count(name) == 0) {
- // Not in synced_preferences_ means no synced data. InitPrefAndAssociate(..)
- // will determine if we care about its data (e.g. if it has a default value
- // and hasn't been changed yet we don't) and take care syncing any new data.
+ // Not in synced_preferences_ means no synced data.
+ // InitPrefAndAssociate(..) will determine if we care about its data (e.g.
+ // if it has a default value and hasn't been changed yet we don't) and
+ // take care syncing any new data.
InitPrefAndAssociate(syncer::SyncData(), name, &changes);
} else {
// We are already syncing this preference, just update it's sync node.
diff --git a/chromium/components/sync_preferences/pref_model_associator.h b/chromium/components/sync_preferences/pref_model_associator.h
index d47a36b4a64..12427b5c38d 100644
--- a/chromium/components/sync_preferences/pref_model_associator.h
+++ b/chromium/components/sync_preferences/pref_model_associator.h
@@ -19,7 +19,7 @@
#include "base/sequence_checker.h"
#include "components/sync/model/sync_data.h"
#include "components/sync/model/syncable_service.h"
-#include "components/sync_preferences/synced_pref_observer.h"
+#include "components/sync_preferences/unknown_user_pref_accessor.h"
namespace base {
class Value;
@@ -33,6 +33,7 @@ namespace sync_preferences {
class PrefModelAssociatorClient;
class PrefServiceSyncable;
+class SyncedPrefObserver;
// Contains all preference sync related logic.
// TODO(sync): Merge this into PrefService once we separate the profile
@@ -40,16 +41,22 @@ class PrefServiceSyncable;
class PrefModelAssociator : public syncer::SyncableService {
public:
// Constructs a PrefModelAssociator initializing the |client_| and |type_|
- // instance variable. The |client| is not owned by this object and the caller
- // must ensure that it oulives the PrefModelAssociator.
+ // instance variable. |client| and |accessor| are not owned by this object
+ // and the caller must ensure they outlive the PrefModelAssociator.
PrefModelAssociator(const PrefModelAssociatorClient* client,
- syncer::ModelType type);
+ syncer::ModelType type,
+ UnknownUserPrefAccessor* accessor);
~PrefModelAssociator() override;
// See description above field for details.
bool models_associated() const { return models_associated_; }
// syncer::SyncableService implementation.
+
+ // Note for GetAllSyncData: This will build a model of all preferences
+ // registered as syncable with user controlled data. We do not track any
+ // information for preferences not registered locally as syncable and do not
+ // inform the syncer of non-user controlled preferences.
syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const override;
syncer::SyncError ProcessSyncChanges(
const base::Location& from_here,
@@ -61,17 +68,23 @@ class PrefModelAssociator : public syncer::SyncableService {
std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) override;
void StopSyncing(syncer::ModelType type) override;
+ // TODO(tschumann): Replace the RegisterPref() call with a
+ // VerifyPersistedPrefType() method. All pref registration checks are now
+ // done via the registry; no need to duplicate that concept.
// Register a preference with the specified name for syncing. We do not care
// about the type at registration time, but when changes arrive from the
// syncer, we check if they can be applied and if not drop them.
// Note: This should only be called at profile startup time (before sync
// begins).
- virtual void RegisterPref(const char* name);
+ void RegisterPref(const std::string& name);
// Process a local preference change. This can trigger new SyncChanges being
// sent to the syncer.
- virtual void ProcessPrefChange(const std::string& name);
+ void ProcessPrefChange(const std::string& name);
+ // TODO(tschumann): Remove the associator's dependency on PrefServiceSyncable.
+ // It's only needed for calling OnIsSyncingChanged. This logic can be moved
+ // onto the associator: PrefServiceSyncable forwards the registration calls.
void SetPrefService(PrefServiceSyncable* pref_service);
// Merges the local_value into the supplied server_value and returns
@@ -136,15 +149,17 @@ class PrefModelAssociator : public syncer::SyncableService {
static base::Value* ReadPreferenceSpecifics(
const sync_pb::PreferenceSpecifics& specifics);
+ void NotifySyncedPrefObservers(const std::string& path, bool from_sync) const;
+
// Do we have an active association between the preferences and sync models?
// Set when start syncing, reset in StopSyncing. While this is not set, we
// ignore any local preference changes (when we start syncing we will look
// up the most recent values anyways).
- bool models_associated_;
+ bool models_associated_ = false;
// Whether we're currently processing changes from the syncer. While this is
// true, we ignore any local preference changes, since we triggered them.
- bool processing_syncer_changes_;
+ bool processing_syncer_changes_ = false;
// A set of preference names.
typedef std::set<std::string> PreferenceSet;
@@ -162,7 +177,10 @@ class PrefModelAssociator : public syncer::SyncableService {
PreferenceSet synced_preferences_;
// The PrefService we are syncing to.
- PrefServiceSyncable* pref_service_;
+ PrefServiceSyncable* pref_service_ = nullptr;
+
+ // A pref accessor to access prefs which might not be registered.
+ UnknownUserPrefAccessor* pref_accessor_;
// Sync's syncer::SyncChange handler. We push all our changes through this.
std::unique_ptr<syncer::SyncChangeProcessor> sync_processor_;
@@ -174,14 +192,13 @@ class PrefModelAssociator : public syncer::SyncableService {
// PRIORITY_PREFERENCES.
syncer::ModelType type_;
- void NotifySyncedPrefObservers(const std::string& path, bool from_sync) const;
-
// Map prefs to lists of observers. Observers will receive notification when
// a pref changes, including the detail of whether or not the change came
// from sync.
base::hash_map<std::string,
std::unique_ptr<base::ObserverList<SyncedPrefObserver>>>
synced_pref_observers_;
+
const PrefModelAssociatorClient* client_; // Weak.
std::vector<base::Closure> callback_list_;
diff --git a/chromium/components/sync_preferences/pref_model_associator_unittest.cc b/chromium/components/sync_preferences/pref_model_associator_unittest.cc
index 55682ad9434..87a9760eaba 100644
--- a/chromium/components/sync_preferences/pref_model_associator_unittest.cc
+++ b/chromium/components/sync_preferences/pref_model_associator_unittest.cc
@@ -10,6 +10,7 @@
#include "base/memory/ref_counted.h"
#include "base/values.h"
#include "components/prefs/scoped_user_pref_update.h"
+#include "components/prefs/testing_pref_store.h"
#include "components/sync_preferences/pref_model_associator_client.h"
#include "components/sync_preferences/pref_service_mock_factory.h"
#include "components/sync_preferences/pref_service_syncable.h"
@@ -44,9 +45,11 @@ class TestPrefModelAssociatorClient : public PrefModelAssociatorClient {
class AbstractPreferenceMergeTest : public testing::Test {
protected:
- AbstractPreferenceMergeTest() {
+ AbstractPreferenceMergeTest()
+ : user_prefs_(base::MakeRefCounted<TestingPrefStore>()) {
PrefServiceMockFactory factory;
factory.SetPrefModelAssociatorClient(&client_);
+ factory.set_user_prefs(user_prefs_);
scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry(
new user_prefs::PrefRegistrySyncable);
pref_registry->RegisterStringPref(
@@ -89,6 +92,7 @@ class AbstractPreferenceMergeTest : public testing::Test {
}
TestPrefModelAssociatorClient client_;
+ scoped_refptr<TestingPrefStore> user_prefs_;
std::unique_ptr<PrefServiceSyncable> pref_service_;
PrefModelAssociator* pref_sync_service_;
};
diff --git a/chromium/components/sync_preferences/pref_service_syncable.cc b/chromium/components/sync_preferences/pref_service_syncable.cc
index 478d278aa3e..e95aa2973e5 100644
--- a/chromium/components/sync_preferences/pref_service_syncable.cc
+++ b/chromium/components/sync_preferences/pref_service_syncable.cc
@@ -27,6 +27,10 @@
namespace sync_preferences {
+// TODO(tschumann): Handing out pointers to this in the constructor is an
+// anti-pattern. Instead, introduce a factory method which first constructs
+// the PrefServiceSyncable instance and then the members which need a reference
+// to the PrefServiceSycnable instance.
PrefServiceSyncable::PrefServiceSyncable(
std::unique_ptr<PrefNotifierImpl> pref_notifier,
std::unique_ptr<PrefValueStore> pref_value_store,
@@ -39,13 +43,18 @@ PrefServiceSyncable::PrefServiceSyncable(
: PrefService(std::move(pref_notifier),
std::move(pref_value_store),
std::move(user_prefs),
- std::move(pref_registry),
+ pref_registry,
std::move(read_error_callback),
async),
pref_service_forked_(false),
- pref_sync_associator_(pref_model_associator_client, syncer::PREFERENCES),
+ unknown_pref_accessor_(this, pref_registry.get(), user_pref_store_.get()),
+ pref_sync_associator_(pref_model_associator_client,
+ syncer::PREFERENCES,
+ &unknown_pref_accessor_),
priority_pref_sync_associator_(pref_model_associator_client,
- syncer::PRIORITY_PREFERENCES) {
+ syncer::PRIORITY_PREFERENCES,
+ &unknown_pref_accessor_),
+ pref_registry_(std::move(pref_registry)) {
pref_sync_associator_.SetPrefService(this);
priority_pref_sync_associator_.SetPrefService(this);
@@ -69,35 +78,32 @@ PrefServiceSyncable::PrefServiceSyncable(
PrefServiceSyncable::~PrefServiceSyncable() {
// Remove our callback from the registry, since it may outlive us.
- user_prefs::PrefRegistrySyncable* registry =
- static_cast<user_prefs::PrefRegistrySyncable*>(pref_registry_.get());
- registry->SetSyncableRegistrationCallback(
+ pref_registry_->SetSyncableRegistrationCallback(
user_prefs::PrefRegistrySyncable::SyncableRegistrationCallback());
}
std::unique_ptr<PrefServiceSyncable>
PrefServiceSyncable::CreateIncognitoPrefService(
PrefStore* incognito_extension_pref_store,
- const std::vector<const char*>& overlay_pref_names,
+ const std::vector<const char*>& persistent_pref_names,
std::unique_ptr<PrefValueStore::Delegate> delegate) {
pref_service_forked_ = true;
auto pref_notifier = std::make_unique<PrefNotifierImpl>();
scoped_refptr<user_prefs::PrefRegistrySyncable> forked_registry =
- static_cast<user_prefs::PrefRegistrySyncable*>(pref_registry_.get())
- ->ForkForIncognito();
+ pref_registry_->ForkForIncognito();
auto overlay = base::MakeRefCounted<InMemoryPrefStore>();
if (delegate) {
delegate->InitIncognitoUserPrefs(overlay, user_pref_store_,
- overlay_pref_names);
+ persistent_pref_names);
delegate->InitPrefRegistry(forked_registry.get());
}
auto incognito_pref_store = base::MakeRefCounted<OverlayUserPrefStore>(
overlay.get(), user_pref_store_.get());
- for (const char* overlay_pref_name : overlay_pref_names)
- incognito_pref_store->RegisterOverlayPref(overlay_pref_name);
+ 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
@@ -179,9 +185,13 @@ void PrefServiceSyncable::AddRegisteredSyncablePreference(
uint32_t flags) {
DCHECK(FindPreference(path));
if (flags & user_prefs::PrefRegistrySyncable::SYNCABLE_PREF) {
- pref_sync_associator_.RegisterPref(path.c_str());
+ DCHECK(!pref_sync_associator_.models_associated() ||
+ pref_registry_->IsWhitelistedLateRegistrationPref(path));
+ pref_sync_associator_.RegisterPref(path);
} else if (flags & user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF) {
- priority_pref_sync_associator_.RegisterPref(path.c_str());
+ DCHECK(!priority_pref_sync_associator_.models_associated() ||
+ pref_registry_->IsWhitelistedLateRegistrationPref(path));
+ priority_pref_sync_associator_.RegisterPref(path);
}
}
diff --git a/chromium/components/sync_preferences/pref_service_syncable.h b/chromium/components/sync_preferences/pref_service_syncable.h
index 656b10e8201..baad8742b4c 100644
--- a/chromium/components/sync_preferences/pref_service_syncable.h
+++ b/chromium/components/sync_preferences/pref_service_syncable.h
@@ -18,6 +18,7 @@
#include "components/prefs/pref_value_store.h"
#include "components/sync_preferences/pref_model_associator.h"
#include "components/sync_preferences/synced_pref_observer.h"
+#include "components/sync_preferences/unknown_user_pref_accessor.h"
namespace syncer {
class SyncableService;
@@ -49,11 +50,11 @@ class PrefServiceSyncable : public PrefService {
// Creates an incognito copy of the pref service that shares most pref stores
// but uses a fresh non-persistent overlay for the user pref store and an
// individual extension pref store (to cache the effective extension prefs for
- // incognito windows). |overlay_pref_names| is a list of preference names
- // whose changes will not be persisted by the returned incognito pref service.
+ // incognito windows). |persistent_pref_names| is a list of preference names
+ // whose changes will be persisted by the returned incognito pref service.
std::unique_ptr<PrefServiceSyncable> CreateIncognitoPrefService(
PrefStore* incognito_extension_pref_store,
- const std::vector<const char*>& overlay_pref_names,
+ const std::vector<const char*>& persistent_pref_names,
std::unique_ptr<PrefValueStore::Delegate> delegate);
// Returns true if preferences state has synchronized with the remote
@@ -107,8 +108,10 @@ class PrefServiceSyncable : public PrefService {
// "forked" PrefService.
bool pref_service_forked_;
+ UnknownUserPrefAccessor unknown_pref_accessor_;
PrefModelAssociator pref_sync_associator_;
PrefModelAssociator priority_pref_sync_associator_;
+ const scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry_;
base::ObserverList<PrefServiceSyncableObserver> observer_list_;
diff --git a/chromium/components/sync_preferences/pref_service_syncable_unittest.cc b/chromium/components/sync_preferences/pref_service_syncable_unittest.cc
index 1129b294554..a1fa85d5c89 100644
--- a/chromium/components/sync_preferences/pref_service_syncable_unittest.cc
+++ b/chromium/components/sync_preferences/pref_service_syncable_unittest.cc
@@ -14,6 +14,7 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_notifier_impl.h"
#include "components/prefs/scoped_user_pref_update.h"
@@ -26,12 +27,17 @@
#include "components/sync/protocol/sync.pb.h"
#include "components/sync_preferences/pref_model_associator.h"
#include "components/sync_preferences/pref_model_associator_client.h"
+#include "components/sync_preferences/synced_pref_observer.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gtest/include/gtest/gtest.h"
using syncer::SyncChange;
using syncer::SyncData;
+using testing::Eq;
+using testing::IsEmpty;
+using testing::Not;
using testing::NotNull;
+using testing::SizeIs;
namespace sync_preferences {
@@ -57,6 +63,7 @@ class TestSyncProcessorStub : public syncer::SyncChangeProcessor {
public:
explicit TestSyncProcessorStub(syncer::SyncChangeList* output)
: output_(output), fail_next_(false) {}
+
syncer::SyncError ProcessSyncChanges(
const base::Location& from_here,
const syncer::SyncChangeList& change_list) override {
@@ -265,6 +272,23 @@ TEST_F(PrefServiceSyncableTest, ModelAssociationCloudHasData) {
EXPECT_EQ(kNonDefaultCharsetValue, prefs_.GetString(kDefaultCharsetPrefName));
}
+// Verifies that the implementation gracefully handles an initial remote sync
+// data of wrong type. The local version should not get modified in these cases.
+TEST_F(PrefServiceSyncableTest, ModelAssociationWithDataTypeMismatch) {
+ base::HistogramTester histogram_tester;
+ prefs_.SetString(kStringPrefName, kExampleUrl0);
+
+ syncer::SyncDataList in;
+ base::Value remote_int_value(123);
+ AddToRemoteDataList(kStringPrefName, remote_int_value, &in);
+ syncer::SyncChangeList out;
+ InitWithSyncDataTakeOutput(in, &out);
+ EXPECT_THAT(out, IsEmpty());
+ histogram_tester.ExpectBucketCount("Sync.Preferences.RemotePrefTypeMismatch",
+ true, 1);
+ EXPECT_THAT(prefs_.GetString(kStringPrefName), Eq(kExampleUrl0));
+}
+
class TestPrefModelAssociatorClient : public PrefModelAssociatorClient {
public:
TestPrefModelAssociatorClient() {}
@@ -511,6 +535,150 @@ TEST_F(PrefServiceSyncableMergeTest, ShouldMergeSelectedDictionaryValues) {
EXPECT_TRUE(GetPreferenceValue(kDictPrefName).Equals(&expected_dict));
}
+TEST_F(PrefServiceSyncableMergeTest, InitWithUnknownPrefsValue) {
+ base::HistogramTester histogram_tester;
+ const std::string pref_name1 = "testing.whitelisted_pref1";
+ const std::string pref_name2 = "testing.whitelisted_pref2";
+ pref_registry_->WhitelistLateRegistrationPrefForSync(pref_name1);
+ pref_registry_->WhitelistLateRegistrationPrefForSync(pref_name2);
+
+ syncer::SyncDataList in;
+ AddToRemoteDataList(pref_name1, base::Value("remote_value1"), &in);
+ AddToRemoteDataList(pref_name2, base::Value("remote_value2"), &in);
+ syncer::SyncChangeList out;
+ InitWithSyncDataTakeOutput(in, &out);
+ pref_registry_->RegisterStringPref(
+ pref_name1, "default_value",
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+ EXPECT_THAT(GetPreferenceValue(pref_name1).GetString(), Eq("remote_value1"));
+
+ histogram_tester.ExpectBucketCount("Sync.Preferences.SyncingUnknownPrefs", 2,
+ 1);
+}
+
+TEST_F(PrefServiceSyncableMergeTest, ReceiveUnknownPrefsValue) {
+ base::HistogramTester histogram_tester;
+ const std::string pref_name = "testing.whitelisted_pref";
+ pref_registry_->WhitelistLateRegistrationPrefForSync(pref_name);
+
+ syncer::SyncChangeList out;
+ InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
+
+ syncer::SyncChangeList remote_changes;
+ remote_changes.push_back(MakeRemoteChange(
+ 1, pref_name, base::Value("remote_value"), SyncChange::ACTION_UPDATE));
+ pref_sync_service_->ProcessSyncChanges(FROM_HERE, remote_changes);
+ EXPECT_THAT(prefs_.IsPrefSynced(pref_name), Eq(true));
+
+ pref_registry_->RegisterStringPref(
+ pref_name, "default_value",
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+ EXPECT_THAT(GetPreferenceValue(pref_name).GetString(), Eq("remote_value"));
+}
+
+TEST_F(PrefServiceSyncableMergeTest, KeepPriorityPreferencesSeparately) {
+ base::HistogramTester histogram_tester;
+ const std::string pref_name = "testing.priority_pref";
+ pref_registry_->RegisterStringPref(
+ pref_name, "priority-default",
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
+
+ syncer::SyncDataList in;
+ // AddToRemoteDataList() produces sync data for non-priority prefs.
+ AddToRemoteDataList(pref_name, base::Value("non-priority-value"), &in);
+ syncer::SyncChangeList out;
+ InitWithSyncDataTakeOutput(in, &out);
+ EXPECT_THAT(GetPreferenceValue(pref_name).GetString(),
+ Eq("priority-default"));
+}
+
+class ShouldNotBeNotifedObserver : public SyncedPrefObserver {
+ public:
+ ShouldNotBeNotifedObserver() {}
+ ~ShouldNotBeNotifedObserver() {}
+
+ void OnSyncedPrefChanged(const std::string& path, bool from_sync) override {
+ ADD_FAILURE() << "Unexpected notification about a pref change with path: '"
+ << path << "' and from_sync: " << from_sync;
+ }
+};
+
+TEST_F(PrefServiceSyncableMergeTest, RegisterShouldClearTypeMismatchingData) {
+ base::HistogramTester histogram_tester;
+ const std::string pref_name = "testing.whitelisted_pref";
+ pref_registry_->WhitelistLateRegistrationPrefForSync(pref_name);
+ // Make sure no changes will be communicated to any synced pref listeners
+ // (those listeners are typically only used for metrics but we still don't
+ // want to inform them).
+ ShouldNotBeNotifedObserver observer;
+ prefs_.AddSyncedPrefObserver(pref_name, &observer);
+ syncer::SyncDataList in;
+ AddToRemoteDataList(pref_name, base::Value("remote_value"), &in);
+ syncer::SyncChangeList out;
+ InitWithSyncDataTakeOutput(in, &out);
+ ASSERT_THAT(out, IsEmpty());
+
+ EXPECT_TRUE(user_prefs_->GetValue(pref_name, nullptr));
+
+ pref_registry_->RegisterListPref(
+ pref_name, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+ EXPECT_TRUE(GetPreferenceValue(pref_name).GetList().empty());
+ EXPECT_FALSE(user_prefs_->GetValue(pref_name, nullptr));
+ // Make sure the removal of the value was not communicated to sync via the
+ // SyncProcessor.
+ EXPECT_THAT(out, IsEmpty());
+
+ histogram_tester.ExpectBucketCount(
+ "Sync.Preferences.ClearedLocalPrefOnTypeMismatch", true, 1);
+ prefs_.RemoveSyncedPrefObserver(pref_name, &observer);
+}
+
+TEST_F(PrefServiceSyncableMergeTest, ShouldIgnoreUpdatesToNotSyncablePrefs) {
+ const std::string pref_name = "testing.not_syncable_pref";
+ pref_registry_->RegisterStringPref(pref_name, "default_value",
+ PrefRegistry::NO_REGISTRATION_FLAGS);
+ syncer::SyncDataList in;
+ AddToRemoteDataList(pref_name, base::Value("remote_value"), &in);
+ syncer::SyncChangeList out;
+ InitWithSyncDataTakeOutput(in, &out);
+ EXPECT_THAT(GetPreferenceValue(pref_name).GetString(), Eq("default_value"));
+
+ syncer::SyncChangeList remote_changes;
+ remote_changes.push_back(MakeRemoteChange(
+ 1, pref_name, base::Value("remote_value2"), SyncChange::ACTION_UPDATE));
+ pref_sync_service_->ProcessSyncChanges(FROM_HERE, remote_changes);
+ EXPECT_THAT(prefs_.IsPrefSynced(pref_name), Eq(false));
+
+ EXPECT_THAT(GetPreferenceValue(pref_name).GetString(), Eq("default_value"));
+}
+
+TEST_F(PrefServiceSyncableMergeTest, GetAllSyncDataForLateRegisteredPrefs) {
+ const std::string pref_name = "testing.whitelisted_pref";
+ pref_registry_->WhitelistLateRegistrationPrefForSync(pref_name);
+
+ syncer::SyncDataList in;
+ AddToRemoteDataList(pref_name, base::Value("remote_value"), &in);
+ syncer::SyncChangeList out;
+ InitWithSyncDataTakeOutput(in, &out);
+
+ syncer::SyncDataList all_data =
+ prefs_.GetSyncableService(syncer::PREFERENCES)
+ ->GetAllSyncData(syncer::PREFERENCES);
+ EXPECT_THAT(all_data, IsEmpty());
+
+ // Make sure the preference appears in the result once it's registered.
+ pref_registry_->RegisterStringPref(
+ pref_name, "default_value",
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+
+ all_data = prefs_.GetSyncableService(syncer::PREFERENCES)
+ ->GetAllSyncData(syncer::PREFERENCES);
+ ASSERT_THAT(all_data, SizeIs(1));
+ EXPECT_THAT(all_data[0].GetSpecifics().preference().name(), Eq(pref_name));
+ EXPECT_THAT(all_data[0].GetSpecifics().preference().value(),
+ Eq("\"remote_value\""));
+}
+
TEST_F(PrefServiceSyncableTest, FailModelAssociation) {
syncer::SyncChangeList output;
TestSyncProcessorStub* stub = new TestSyncProcessorStub(&output);
@@ -565,6 +733,24 @@ TEST_F(PrefServiceSyncableTest, UpdatedSyncNodeActionUpdate) {
EXPECT_TRUE(expected.Equals(&actual));
}
+// Verifies that the implementation gracefully handles a remote update with the
+// wrong type. The local version should not get modified in these cases.
+TEST_F(PrefServiceSyncableTest, UpdatedSyncNodeActionUpdateTypeMismatch) {
+ base::HistogramTester histogram_tester;
+ GetPrefs()->SetString(kStringPrefName, kExampleUrl0);
+ InitWithNoSyncData();
+
+ base::Value remote_int_value(123);
+ syncer::SyncChangeList remote_changes;
+ remote_changes.push_back(MakeRemoteChange(
+ 1, kStringPrefName, remote_int_value, SyncChange::ACTION_UPDATE));
+ pref_sync_service_->ProcessSyncChanges(FROM_HERE, remote_changes);
+
+ EXPECT_THAT(prefs_.GetString(kStringPrefName), Eq(kExampleUrl0));
+ histogram_tester.ExpectBucketCount("Sync.Preferences.RemotePrefTypeMismatch",
+ true, 1);
+}
+
TEST_F(PrefServiceSyncableTest, UpdatedSyncNodeActionAdd) {
InitWithNoSyncData();
diff --git a/chromium/components/sync_preferences/unknown_user_pref_accessor.cc b/chromium/components/sync_preferences/unknown_user_pref_accessor.cc
new file mode 100644
index 00000000000..063b5ae1d44
--- /dev/null
+++ b/chromium/components/sync_preferences/unknown_user_pref_accessor.cc
@@ -0,0 +1,197 @@
+// 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/sync_preferences/unknown_user_pref_accessor.h"
+
+#include <iterator>
+#include <memory>
+
+#include "base/json/json_string_value_serializer.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/values.h"
+#include "components/prefs/persistent_pref_store.h"
+#include "components/prefs/pref_service.h"
+#include "components/sync_preferences/pref_service_syncable.h"
+
+namespace sync_preferences {
+
+UnknownUserPrefAccessor::UnknownUserPrefAccessor(
+ PrefService* pref_service,
+ user_prefs::PrefRegistrySyncable* pref_registry,
+ PersistentPrefStore* user_prefs)
+ : pref_service_(pref_service),
+ pref_registry_(pref_registry),
+ user_prefs_(user_prefs) {}
+
+UnknownUserPrefAccessor::~UnknownUserPrefAccessor() {}
+
+UnknownUserPrefAccessor::PreferenceState
+UnknownUserPrefAccessor::GetPreferenceState(
+ syncer::ModelType type,
+ const std::string& pref_name) const {
+ PreferenceState result;
+ result.registration_state = GetRegistrationState(type, pref_name);
+ switch (result.registration_state) {
+ case RegistrationState::kUnknown:
+ case RegistrationState::kUnknownWhitelisted:
+ if (!user_prefs_->GetValue(pref_name, &result.persisted_value)) {
+ result.persisted_value = nullptr;
+ }
+ break;
+ case RegistrationState::kSyncable:
+ case RegistrationState::kNotSyncable:
+ result.persisted_value = pref_service_->GetUserPrefValue(pref_name);
+ break;
+ }
+ return result;
+}
+
+void UnknownUserPrefAccessor::ClearPref(
+ const std::string& pref_name,
+ const PreferenceState& local_pref_state) {
+ switch (local_pref_state.registration_state) {
+ case RegistrationState::kUnknown:
+ NOTREACHED() << "Sync attempted to update an unknown pref which is not "
+ "whitelisted: "
+ << pref_name;
+ break;
+ case RegistrationState::kUnknownWhitelisted:
+ user_prefs_->RemoveValue(pref_name,
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ break;
+ case RegistrationState::kSyncable:
+ pref_service_->ClearPref(pref_name);
+ break;
+ case RegistrationState::kNotSyncable:
+ // As this can happen if different clients disagree about which
+ // preferences should be synced, we only log a warning.
+ DLOG(WARNING)
+ << "Sync attempted to update a pref which is not registered as "
+ "syncable. Ignoring the remote change for pref: "
+ << pref_name;
+ break;
+ }
+}
+
+int UnknownUserPrefAccessor::GetNumberOfSyncingUnknownPrefs() const {
+ return synced_unknown_prefs_.size();
+}
+
+namespace {
+
+bool VerifyTypesBeforeSet(const std::string& pref_name,
+ const base::Value* local_value,
+ const base::Value& new_value) {
+ if (local_value == nullptr || local_value->type() == new_value.type()) {
+ return true;
+ }
+ UMA_HISTOGRAM_BOOLEAN("Sync.Preferences.RemotePrefTypeMismatch", true);
+ DLOG(WARNING) << "Unexpected type mis-match for pref. "
+ << "Synced value for " << pref_name << " is of type "
+ << new_value.type() << " which doesn't match the locally "
+ << "present pref type: " << local_value->type();
+ return false;
+}
+
+} // namespace
+
+void UnknownUserPrefAccessor::SetPref(const std::string& pref_name,
+ const PreferenceState& local_pref_state,
+ const base::Value& value) {
+ // On type mis-match, we trust the local preference DB and ignore the remote
+ // change.
+ switch (local_pref_state.registration_state) {
+ case RegistrationState::kUnknown:
+ NOTREACHED() << "Sync attempted to update a unknown pref which is not "
+ "whitelisted: "
+ << pref_name;
+ break;
+ case RegistrationState::kUnknownWhitelisted:
+ if (VerifyTypesBeforeSet(pref_name, local_pref_state.persisted_value,
+ value)) {
+ user_prefs_->SetValue(pref_name, value.CreateDeepCopy(),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ }
+ synced_unknown_prefs_.insert(pref_name);
+ break;
+ case RegistrationState::kSyncable:
+ if (VerifyTypesBeforeSet(pref_name, local_pref_state.persisted_value,
+ value)) {
+ pref_service_->Set(pref_name, value);
+ }
+ break;
+ case RegistrationState::kNotSyncable:
+ // As this can happen if different clients disagree about which
+ // preferences should be synced, we only log a warning.
+ DLOG(WARNING)
+ << "Sync attempted to update a pref which is not registered as "
+ "syncable. Ignoring the remote change for pref: "
+ << pref_name;
+ break;
+ }
+}
+
+void UnknownUserPrefAccessor::EnforceRegisteredTypeInStore(
+ const std::string& pref_name) {
+ const base::Value* persisted_value = nullptr;
+ if (user_prefs_->GetValue(pref_name, &persisted_value)) {
+ // Get the registered type (typically from the default value).
+ const PrefService::Preference* pref =
+ pref_service_->FindPreference(pref_name);
+ DCHECK(pref);
+ if (pref->GetType() != persisted_value->type()) {
+ // We see conflicting type information and there's a chance the local
+ // type-conflicting data came in via sync. Remove it.
+ // TODO(tschumann): The value should get removed silently. Add a method
+ // RemoveValueSilently() to WriteablePrefStore. Note, that as of today
+ // that removal will only notify other pref stores but not sync -- that's
+ // done on a higher level.
+ user_prefs_->RemoveValue(pref_name,
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ UMA_HISTOGRAM_BOOLEAN("Sync.Preferences.ClearedLocalPrefOnTypeMismatch",
+ true);
+ }
+ }
+ synced_unknown_prefs_.erase(pref_name);
+}
+
+UnknownUserPrefAccessor::RegistrationState
+UnknownUserPrefAccessor::GetRegistrationState(
+ syncer::ModelType type,
+ const std::string& pref_name) const {
+ uint32_t type_flag = 0;
+ switch (type) {
+ case syncer::PRIORITY_PREFERENCES:
+ type_flag = user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF;
+ break;
+ case syncer::PREFERENCES:
+ type_flag = user_prefs::PrefRegistrySyncable::SYNCABLE_PREF;
+ break;
+ default:
+ NOTREACHED() << "unexpected model type for preferences: " << type;
+ }
+ if (pref_registry_->defaults()->GetValue(pref_name, nullptr)) {
+ uint32_t flags = pref_registry_->GetRegistrationFlags(pref_name);
+ if (flags & type_flag) {
+ return RegistrationState::kSyncable;
+ }
+ // Imagine the case where a preference has been synced as SYNCABLE_PREF
+ // first and then got changed to SYNCABLE_PRIORITY_PREF:
+ // In that situation, it could be argued for both, the preferences to be
+ // considered unknown or not synced. However, as we plan to eventually also
+ // sync unknown preferences, we cannot label them as unknown and treat them
+ // as not synced instead. (The underlying problem is that priority
+ // preferences are a concept only known to sync. The persistent stores don't
+ // distinguish between those two).
+ return RegistrationState::kNotSyncable;
+ }
+ if (pref_registry_->IsWhitelistedLateRegistrationPref(pref_name)) {
+ return RegistrationState::kUnknownWhitelisted;
+ }
+ return RegistrationState::kUnknown;
+}
+
+} // namespace sync_preferences
diff --git a/chromium/components/sync_preferences/unknown_user_pref_accessor.h b/chromium/components/sync_preferences/unknown_user_pref_accessor.h
new file mode 100644
index 00000000000..34336a0808c
--- /dev/null
+++ b/chromium/components/sync_preferences/unknown_user_pref_accessor.h
@@ -0,0 +1,104 @@
+// 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_SYNC_PREFERENCES_UNKNOWN_USER_PREF_ACCESSOR_H_
+#define COMPONENTS_SYNC_PREFERENCES_UNKNOWN_USER_PREF_ACCESSOR_H_
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "components/sync/base/model_type.h"
+
+class PersistentPrefStore;
+class PrefService;
+
+namespace base {
+class Value;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace sync_preferences {
+
+// A class to access user prefs even before they were registered.
+// Currently, accessing not registered (unknown) prefs is limited to a
+// whitelist.
+class UnknownUserPrefAccessor {
+ public:
+ enum class RegistrationState {
+ kUnknown, // Preference is not registered (on this Chrome instance).
+ kUnknownWhitelisted, // Preference is not registered but whitelisted to be
+ // synced without being registered.
+ kSyncable, // Preference is registered as being synced.
+ kNotSyncable // Preference is registered as not being synced.
+ };
+
+ struct PreferenceState {
+ // The registration state of a preference.
+ RegistrationState registration_state = RegistrationState::kUnknown;
+
+ // The actually stored value. nullptr if no value is persisted and the pref
+ // service serves a default value for this pref.
+ // Ownership lies with the underlying pref-store.
+ const base::Value* persisted_value = nullptr;
+ };
+
+ // |pref_service|, |pref_registry|, and |user_prefs| must not be null and must
+ // outlive the lifetime of the created instance. The caller keeps ownership
+ // over these objects.
+ UnknownUserPrefAccessor(PrefService* pref_service,
+ user_prefs::PrefRegistrySyncable* pref_registry,
+ PersistentPrefStore* user_prefs);
+ ~UnknownUserPrefAccessor();
+
+ // Computes the state of a preference with name |pref_name| which gives
+ // information about whether it's registered and the locally persisted value.
+ PreferenceState GetPreferenceState(syncer::ModelType type,
+ const std::string& pref_name) const;
+
+ // Removes the value of the preference |pref_name| from the user prefstore.
+ // Must not be called for preferences having RegistrationState::kUnknown.
+ // When called for preferences registiered as not syncable
+ // (RegistrationState::kNotSyncable), no changes to the storage are made.
+ void ClearPref(const std::string& pref_name,
+ const PreferenceState& local_pref_state);
+
+ // Changes the value of the preference |pref_name| on the user prefstore.
+ // Must not be called for preferences having RegistrationState::kUnknown.
+ // When called for preferences registiered as not syncable
+ // (RegistrationState::kNotSyncable), no changes to the storage are made.
+ void SetPref(const std::string& pref_name,
+ const PreferenceState& local_pref_state,
+ const base::Value& value);
+
+ // Verifies that the type which preference |pref_name| was registered with
+ // matches the type of any persisted value. On mismatch, the persisted value
+ // gets removed.
+ void EnforceRegisteredTypeInStore(const std::string& pref_name);
+
+ // Returns the number of synced preferences which have not been registered (so
+ // far).
+ int GetNumberOfSyncingUnknownPrefs() const;
+
+ private:
+ RegistrationState GetRegistrationState(syncer::ModelType type,
+ const std::string& pref_name) const;
+
+ std::set<std::string> synced_unknown_prefs_;
+ PrefService* const pref_service_;
+ user_prefs::PrefRegistrySyncable* const pref_registry_;
+ PersistentPrefStore* const user_prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnknownUserPrefAccessor);
+};
+
+} // namespace sync_preferences
+
+#endif // COMPONENTS_SYNC_PREFERENCES_UNKNOWN_USER_PREF_ACCESSOR_H_
diff --git a/chromium/components/sync_sessions/BUILD.gn b/chromium/components/sync_sessions/BUILD.gn
index 099cce20c8e..7be002c273d 100644
--- a/chromium/components/sync_sessions/BUILD.gn
+++ b/chromium/components/sync_sessions/BUILD.gn
@@ -19,6 +19,8 @@ static_library("sync_sessions") {
"open_tabs_ui_delegate_impl.h",
"session_data_type_controller.cc",
"session_data_type_controller.h",
+ "session_model_type_controller.cc",
+ "session_model_type_controller.h",
"session_store.cc",
"session_store.h",
"session_sync_bridge.cc",
@@ -101,6 +103,7 @@ source_set("unit_tests") {
"sessions_global_id_mapper_unittest.cc",
"sessions_sync_manager_unittest.cc",
"synced_session_tracker_unittest.cc",
+ "synced_session_unittest.cc",
"tab_node_pool_unittest.cc",
"task_tracker_unittest.cc",
]
diff --git a/chromium/components/sync_sessions/abstract_sessions_sync_manager.h b/chromium/components/sync_sessions/abstract_sessions_sync_manager.h
index b13743af166..596ccb87781 100644
--- a/chromium/components/sync_sessions/abstract_sessions_sync_manager.h
+++ b/chromium/components/sync_sessions/abstract_sessions_sync_manager.h
@@ -29,7 +29,6 @@ class AbstractSessionsSyncManager {
virtual FaviconCache* GetFaviconCache() = 0;
virtual SessionsGlobalIdMapper* GetGlobalIdMapper() = 0;
virtual OpenTabsUIDelegate* GetOpenTabsUIDelegate() = 0;
- virtual void OnSessionRestoreComplete() = 0;
// Exactly one of the two below returns nullptr.
virtual syncer::SyncableService* GetSyncableService() = 0;
virtual syncer::ModelTypeSyncBridge* GetModelTypeSyncBridge() = 0;
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 aec1b2533a2..a538a584e1a 100644
--- a/chromium/components/sync_sessions/local_session_event_handler_impl.cc
+++ b/chromium/components/sync_sessions/local_session_event_handler_impl.cc
@@ -27,80 +27,37 @@ using sessions::SerializedNavigationEntry;
// The maximum number of navigations in each direction we care to sync.
const int kMaxSyncNavigationCount = 6;
+bool IsSessionRestoreInProgress(SyncSessionsClient* sessions_client) {
+ DCHECK(sessions_client);
+ SyncedWindowDelegatesGetter* synced_window_getter =
+ sessions_client->GetSyncedWindowDelegatesGetter();
+ SyncedWindowDelegatesGetter::SyncedWindowDelegateMap windows =
+ synced_window_getter->GetSyncedWindowDelegates();
+ for (const auto& window_iter_pair : windows) {
+ if (window_iter_pair.second->IsSessionRestoreInProgress()) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool IsWindowSyncable(const SyncedWindowDelegate& window_delegate) {
return window_delegate.ShouldSync() && window_delegate.GetTabCount() &&
window_delegate.HasWindow();
}
-// Each sync id should only ever be used once. Previously there existed a race
-// condition which could cause them to be duplicated, see
-// https://crbug.com/639009 for more information. This counts the number of
-// times each id is used so that the second window/tab loop can act on every
-// tab using duplicate ids. Lastly, it is important to note that this
-// duplication scan is only checking the in-memory tab state. On Android, if
-// we have no tabbed window, we may also have sync data with conflicting sync
-// ids, but to keep this logic simple and less error prone, we do not attempt
-// to do anything clever.
-//
-// Returns tabs grouped by sync id (tab_node_id).
-std::map<int, std::vector<SyncedTabDelegate*>> GroupTabsBySyncId(
- const SyncedWindowDelegatesGetter::SyncedWindowDelegateMap& windows) {
- std::map<int, std::vector<SyncedTabDelegate*>> tabs_per_sync_id;
- int duplicate_count = 0;
- for (auto& window_iter_pair : windows) {
+// On Android, it's possible to not have any tabbed windows when only custom
+// tabs are currently open. This means that there is tab data that will be
+// restored later, but we cannot access it.
+bool ScanForTabbedWindow(SyncedWindowDelegatesGetter* delegates_getter) {
+ for (const auto& window_iter_pair :
+ delegates_getter->GetSyncedWindowDelegates()) {
const SyncedWindowDelegate* window_delegate = window_iter_pair.second;
- for (int j = 0; j < window_delegate->GetTabCount(); ++j) {
- SyncedTabDelegate* synced_tab = window_delegate->GetTabAt(j);
- if (!synced_tab ||
- synced_tab->GetSyncId() == TabNodePool::kInvalidTabNodeID) {
- continue;
- }
- std::vector<SyncedTabDelegate*>* synced_tabs =
- &tabs_per_sync_id[synced_tab->GetSyncId()];
- synced_tabs->push_back(synced_tab);
- if (synced_tabs->size() > 1) {
- // If an id is used more than twice, this count will be a bit odd,
- // but for our purposes, it will be sufficient.
- duplicate_count++;
- }
+ if (window_delegate->IsTypeTabbed() && IsWindowSyncable(*window_delegate)) {
+ return true;
}
}
-
- if (duplicate_count > 0) {
- UMA_HISTOGRAM_COUNTS_100("Sync.SesssionsDuplicateSyncId", duplicate_count);
- }
- return tabs_per_sync_id;
-}
-
-// Function to resolve a conflict when multiple tabs reported by the delegate
-// contain a shared sync ID (tab_node_id). |initially_associated_tab_id| can be
-// invalid if |tab_node_id| is currently not associated in the tracker.
-const SyncedTabDelegate* ResolveConflictingSyncId(
- int tab_node_id,
- SessionID initially_associated_tab_id,
- const std::vector<SyncedTabDelegate*>& synced_tabs) {
- DCHECK_GT(synced_tabs.size(), 1U);
- // If among the conflicting tabs, there's one which matches what sync knows
- // about, we should prefer that. This should be rare but is possible for
- // example if a Custom tab was being synced without a tabbed window, and
- // suddenly the tabbed window shows up with conflicting sync IDs.
- for (const SyncedTabDelegate* synced_tab : synced_tabs) {
- if (synced_tab->GetSessionId() == initially_associated_tab_id) {
- return synced_tab;
- }
- }
- // In doubt, prefer non-placeholder tabs, because we cannot do anything about
- // placeholder tabs that the tracker doesn't know about (because the delegate
- // does not provide enough information to start tracking it).
- for (const SyncedTabDelegate* synced_tab : synced_tabs) {
- if (!synced_tab->IsPlaceholderTab()) {
- // If multiple non-placeholder tabs have the same sync ID, choose among
- // them arbitrarily.
- return synced_tab;
- }
- }
- // We have multiple conflicting placeholder tabs, so choose arbitrarily.
- return synced_tabs.front();
+ return false;
}
} // namespace
@@ -114,64 +71,43 @@ LocalSessionEventHandlerImpl::Delegate::~Delegate() = default;
LocalSessionEventHandlerImpl::LocalSessionEventHandlerImpl(
Delegate* delegate,
SyncSessionsClient* sessions_client,
- SyncedSessionTracker* session_tracker,
- WriteBatch* initial_batch)
+ SyncedSessionTracker* session_tracker)
: delegate_(delegate),
sessions_client_(sessions_client),
session_tracker_(session_tracker) {
DCHECK(delegate);
DCHECK(sessions_client);
DCHECK(session_tracker);
- DCHECK(initial_batch);
current_session_tag_ = session_tracker_->GetLocalSessionTag();
DCHECK(!current_session_tag_.empty());
- AssociateExistingSyncIds();
- AssociateWindows(RELOAD_TABS, ScanForTabbedWindow(), initial_batch);
+ if (!IsSessionRestoreInProgress(sessions_client)) {
+ OnSessionRestoreComplete();
+ }
}
LocalSessionEventHandlerImpl::~LocalSessionEventHandlerImpl() {}
+void LocalSessionEventHandlerImpl::OnSessionRestoreComplete() {
+ std::unique_ptr<WriteBatch> batch = delegate_->CreateLocalSessionWriteBatch();
+ AssociateWindows(RELOAD_TABS, batch.get());
+ batch->Commit();
+}
+
sync_pb::SessionTab
LocalSessionEventHandlerImpl::GetTabSpecificsFromDelegateForTest(
const SyncedTabDelegate& tab_delegate) const {
return GetTabSpecificsFromDelegate(tab_delegate);
}
-void LocalSessionEventHandlerImpl::AssociateExistingSyncIds() {
- const SyncedWindowDelegatesGetter::SyncedWindowDelegateMap windows =
- sessions_client_->GetSyncedWindowDelegatesGetter()
- ->GetSyncedWindowDelegates();
-
- const std::map<int, std::vector<SyncedTabDelegate*>> tabs_per_sync_id =
- GroupTabsBySyncId(windows);
-
- for (const auto& sync_id_and_tabs : tabs_per_sync_id) {
- const int tab_node_id = sync_id_and_tabs.first;
- const std::vector<SyncedTabDelegate*>& tabs = sync_id_and_tabs.second;
-
- const SessionID initially_associated_tab_id =
- session_tracker_->LookupTabIdFromTabNodeId(current_session_tag_,
- tab_node_id);
- const SyncedTabDelegate* selected_tab =
- tabs.size() == 1 ? tabs.front()
- : ResolveConflictingSyncId(
- tab_node_id, initially_associated_tab_id, tabs);
- // Execute the result of the conflict resolution.
- session_tracker_->ReassociateLocalTab(tab_node_id,
- selected_tab->GetSessionId());
- for (SyncedTabDelegate* synced_tab : tabs) {
- if (synced_tab != selected_tab) {
- synced_tab->SetSyncId(TabNodePool::kInvalidTabNodeID);
- }
- }
- }
-}
-
void LocalSessionEventHandlerImpl::AssociateWindows(ReloadTabsOption option,
- bool has_tabbed_window,
WriteBatch* batch) {
+ DCHECK(!IsSessionRestoreInProgress(sessions_client_));
+
+ const bool has_tabbed_window =
+ ScanForTabbedWindow(sessions_client_->GetSyncedWindowDelegatesGetter());
+
// Note that |current_session| is a pointer owned by |session_tracker_|.
// |session_tracker_| will continue to update |current_session| under
// the hood so care must be taken accessing it. In particular, invoking
@@ -193,36 +129,10 @@ void LocalSessionEventHandlerImpl::AssociateWindows(ReloadTabsOption option,
// session; the current tabbed windows are now the source of truth.
session_tracker_->ResetSessionTracking(current_session_tag_);
current_session->modified_time = base::Time::Now();
- } else if (option == RELOAD_TABS) {
+ } else {
DVLOG(1) << "Found no tabbed windows. Reloading "
<< current_session->windows.size()
<< " windows from previous session.";
-
- // A copy of the specifics must be made because |current_session| will be
- // updated in place and therefore can't be relied on as the source of truth.
- sync_pb::SessionSpecifics specifics;
- specifics.set_session_tag(current_session_tag_);
- current_session->ToSessionHeaderProto().Swap(specifics.mutable_header());
- UpdateTrackerWithSpecifics(specifics, base::Time::Now(), session_tracker_);
-
- // The tab entities stored in sync have outdated SessionId values. Go
- // through and update them to the new SessionIds.
- for (auto& win_iter : current_session->windows) {
- for (auto& tab : win_iter.second->wrapped_window.tabs) {
- int sync_id = session_tracker_->LookupTabNodeFromTabId(
- current_session_tag_, tab->tab_id);
- // In rare cases, the local model could contain a tab that doesn't
- // have a sync ID, if the restored header referenced a tab ID but the
- // tab entity didn't exist (e.g. was unsyncable).
- if (sync_id == TabNodePool::kInvalidTabNodeID) {
- continue;
- }
-
- DVLOG(1) << "Rewriting tab node " << sync_id << " with tab id "
- << tab->tab_id.id();
- AppendChangeForExistingTab(sync_id, *tab, batch);
- }
- }
}
for (auto& window_iter_pair : windows) {
@@ -250,30 +160,29 @@ void LocalSessionEventHandlerImpl::AssociateWindows(ReloadTabsOption option,
SessionID tab_id = window_delegate->GetTabIdAt(j);
SyncedTabDelegate* synced_tab = window_delegate->GetTabAt(j);
- // GetTabAt can return a null tab; in that case just skip it. Similarly,
- // if for some reason the tab id is invalid, skip it.
- if (!synced_tab || !tab_id.is_valid()) {
+ // IsWindowSyncable(), via ShouldSync(), guarantees that tabs are not
+ // null.
+ DCHECK(synced_tab);
+
+ // If for some reason the tab ID is invalid, skip it.
+ if (!tab_id.is_valid()) {
continue;
}
// Placeholder tabs are those without WebContents, either because they
// were never loaded into memory or they were evicted from memory
- // (typically only on Android devices). They only have a tab id,
- // window id, and a saved synced id (corresponding to the tab node
- // id). Note that only placeholders have this sync id, as it's
- // necessary to properly reassociate the tab with the entity that was
- // backing it.
+ // (typically only on Android devices). They only have a window ID and a
+ // tab ID, and we use the latter to properly reassociate the tab with the
+ // entity that was backing it.
if (synced_tab->IsPlaceholderTab()) {
- // For tabs without WebContents update the |tab_id| and |window_id|,
- // as it could have changed after a session restore.
- if (synced_tab->GetSyncId() > TabNodePool::kInvalidTabNodeID) {
- AssociateRestoredPlaceholderTab(*synced_tab, tab_id, window_id,
- batch);
- } else {
- DVLOG(1) << "Placeholder tab " << tab_id.id() << " has no sync id.";
- }
+ // For tabs without WebContents update |window_id|, as it could have
+ // changed after a session restore.
+ // TODO(crbug.com/854493): Avoid associating placeholder tabs
+ // altogether, because it's not worth to update the window ID in tab
+ // entities.
+ AssociateRestoredPlaceholderTab(*synced_tab, tab_id, window_id, batch);
} else if (RELOAD_TABS == option) {
- AssociateTab(synced_tab, has_tabbed_window, batch);
+ AssociateTab(synced_tab, batch);
}
// If the tab was syncable, it would have been added to the tracker
@@ -331,7 +240,6 @@ void LocalSessionEventHandlerImpl::AssociateWindows(ReloadTabsOption option,
void LocalSessionEventHandlerImpl::AssociateTab(
SyncedTabDelegate* const tab_delegate,
- bool has_tabbed_window,
WriteBatch* batch) {
DCHECK(!tab_delegate->IsPlaceholderTab());
@@ -350,31 +258,18 @@ void LocalSessionEventHandlerImpl::AssociateTab(
}
SessionID tab_id = tab_delegate->GetSessionId();
- DVLOG(1) << "Syncing tab " << tab_id.id() << " from window "
- << tab_delegate->GetWindowId().id();
-
int tab_node_id =
session_tracker_->LookupTabNodeFromTabId(current_session_tag_, tab_id);
- if (tab_node_id != TabNodePool::kInvalidTabNodeID) {
- DCHECK(tab_delegate->GetSyncId() == TabNodePool::kInvalidTabNodeID ||
- tab_delegate->GetSyncId() == tab_node_id);
- } else if (has_tabbed_window) {
+ if (tab_node_id == TabNodePool::kInvalidTabNodeID) {
// Allocate a new (or reused) sync node for this tab.
tab_node_id = session_tracker_->AssociateLocalTabWithFreeTabNode(tab_id);
DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id)
<< "https://crbug.com/639009";
- } else {
- // Only allowed to allocate sync ids when we have native data, which is only
- // true when we have a tabbed window. Without a sync id we cannot sync this
- // data, the tracker cannot even really track it. So don't do any more work.
- // This effectively breaks syncing custom tabs when the native browser isn't
- // fully loaded. Ideally this is fixed by saving tab data and sync data
- // atomically, see https://crbug.com/681921.
- return;
}
- tab_delegate->SetSyncId(tab_node_id);
+ DVLOG(1) << "Syncing tab " << tab_id << " from window "
+ << tab_delegate->GetWindowId() << " using tab node " << tab_node_id;
// Get the previously synced url.
sessions::SessionTab* session_tab =
@@ -461,20 +356,23 @@ void LocalSessionEventHandlerImpl::OnLocalTabModified(
SyncedTabDelegate* modified_tab) {
DCHECK(!current_session_tag_.empty());
+ // Defers updates if session restore is in progress.
+ if (IsSessionRestoreInProgress(sessions_client_)) {
+ return;
+ }
+
sessions::SerializedNavigationEntry current;
modified_tab->GetSerializedNavigationAtIndex(
modified_tab->GetCurrentEntryIndex(), &current);
delegate_->TrackLocalNavigationId(current.timestamp(), current.unique_id());
- bool found_tabbed_window = ScanForTabbedWindow();
std::unique_ptr<WriteBatch> batch = delegate_->CreateLocalSessionWriteBatch();
- AssociateExistingSyncIds();
- AssociateTab(modified_tab, found_tabbed_window, batch.get());
+ AssociateTab(modified_tab, batch.get());
// Note, we always associate windows because it's possible a tab became
// "interesting" by going to a valid URL, in which case it needs to be added
// to the window's tab information. Similarly, if a tab became
// "uninteresting", we remove it from the window's tab information.
- AssociateWindows(DONT_RELOAD_TABS, found_tabbed_window, batch.get());
+ AssociateWindows(DONT_RELOAD_TABS, batch.get());
batch->Commit();
}
@@ -490,17 +388,27 @@ void LocalSessionEventHandlerImpl::OnFaviconsChanged(
void LocalSessionEventHandlerImpl::AssociateRestoredPlaceholderTab(
const SyncedTabDelegate& tab_delegate,
- SessionID new_tab_id,
+ SessionID tab_id,
SessionID new_window_id,
WriteBatch* batch) {
- int tab_node_id = tab_delegate.GetSyncId();
- DCHECK_NE(tab_node_id, TabNodePool::kInvalidTabNodeID);
- DCHECK_EQ(new_tab_id, session_tracker_->LookupTabIdFromTabNodeId(
- current_session_tag_, tab_node_id));
+ const int tab_node_id =
+ session_tracker_->LookupTabNodeFromTabId(current_session_tag_, tab_id);
+
+ // If a placeholder tab is not present in the tracker, we must ignore it
+ // because the delegate doesn't expose sufficient information to start
+ // tracking the tab (e.g. navigations are missing).
+ if (tab_node_id == TabNodePool::kInvalidTabNodeID) {
+ DVLOG(1) << "Placeholder tab " << tab_id << " has no sync id.";
+ return;
+ }
// Update the window id on the SessionTab itself.
sessions::SessionTab* local_tab =
- session_tracker_->GetTab(current_session_tag_, new_tab_id);
+ session_tracker_->GetTab(current_session_tag_, tab_id);
+ if (local_tab->window_id == new_window_id) {
+ // Nothing to update.
+ return;
+ }
local_tab->window_id = new_window_id;
// Filter out placeholder tabs that have been associated but don't have known
@@ -509,19 +417,12 @@ void LocalSessionEventHandlerImpl::AssociateRestoredPlaceholderTab(
return;
}
- AppendChangeForExistingTab(tab_node_id, *local_tab, batch);
-}
-
-void LocalSessionEventHandlerImpl::AppendChangeForExistingTab(
- int sync_id,
- const sessions::SessionTab& tab,
- WriteBatch* batch) const {
// Rewrite the specifics based on the reassociated SessionTab to preserve
- // the new tab and window ids.
+ // the new window id.
auto specifics = std::make_unique<sync_pb::SessionSpecifics>();
- tab.ToSyncData().Swap(specifics->mutable_tab());
+ SessionTabToSyncData(*local_tab).Swap(specifics->mutable_tab());
specifics->set_session_tag(current_session_tag_);
- specifics->set_tab_node_id(sync_id);
+ specifics->set_tab_node_id(tab_node_id);
batch->Put(std::move(specifics));
}
@@ -557,7 +458,7 @@ sync_pb::SessionTab LocalSessionEventHandlerImpl::GetTabSpecificsFromDelegate(
specifics.set_current_navigation_index(specifics.navigation_size());
sync_pb::TabNavigation* navigation = specifics.add_navigation();
- serialized_entry.ToSyncData().Swap(navigation);
+ SessionNavigationToSyncData(serialized_entry).Swap(navigation);
if (is_supervised) {
navigation->set_blocked_state(
@@ -576,7 +477,7 @@ sync_pb::SessionTab LocalSessionEventHandlerImpl::GetTabSpecificsFromDelegate(
blocked_navigations = *tab_delegate.GetBlockedNavigations();
for (size_t i = 0; i < blocked_navigations.size(); ++i) {
sync_pb::TabNavigation* navigation = specifics.add_navigation();
- blocked_navigations[i]->ToSyncData().Swap(navigation);
+ SessionNavigationToSyncData(*blocked_navigations[i]).Swap(navigation);
navigation->set_blocked_state(
sync_pb::TabNavigation_BlockedState_STATE_BLOCKED);
// TODO(bauerb): Add categories
@@ -586,26 +487,4 @@ sync_pb::SessionTab LocalSessionEventHandlerImpl::GetTabSpecificsFromDelegate(
return specifics;
}
-bool LocalSessionEventHandlerImpl::ScanForTabbedWindow() {
- for (const auto& window_iter_pair :
- sessions_client_->GetSyncedWindowDelegatesGetter()
- ->GetSyncedWindowDelegates()) {
- if (window_iter_pair.second->IsTypeTabbed()) {
- const SyncedWindowDelegate* window_delegate = window_iter_pair.second;
- if (IsWindowSyncable(*window_delegate)) {
- // When only custom tab windows are open, often we'll have a seemingly
- // okay type tabbed window, but GetTabAt will return null for each
- // index. This case is exactly what this method needs to protect
- // against.
- for (int j = 0; j < window_delegate->GetTabCount(); ++j) {
- if (window_delegate->GetTabAt(j)) {
- return true;
- }
- }
- }
- }
- }
- return false;
-}
-
} // namespace sync_sessions
diff --git a/chromium/components/sync_sessions/local_session_event_handler_impl.h b/chromium/components/sync_sessions/local_session_event_handler_impl.h
index 071b593b266..9272381da0b 100644
--- a/chromium/components/sync_sessions/local_session_event_handler_impl.h
+++ b/chromium/components/sync_sessions/local_session_event_handler_impl.h
@@ -57,18 +57,17 @@ class LocalSessionEventHandlerImpl : public LocalSessionEventHandler {
const GURL& favicon_url) = 0;
};
- // Raw pointers must not be null and all pointees except |*initial_batch| must
- // outlive this object. |*initial_batch| may or may not be initially empty
- // (depending on whether the caller wants to bundle together other writes).
- // This constructor populates |*initial_batch| to resync local window and tab
- // information, but does *not* Commit() the batch.
+ // Raw pointers must not be null and all pointees must outlive this object.
+ // A side effect of this constructor could include (unless session restore is
+ // ongoing) the creation of a write batch (via |delegate| and committing
+ // changes).
LocalSessionEventHandlerImpl(Delegate* delegate,
SyncSessionsClient* sessions_client,
- SyncedSessionTracker* session_tracker,
- WriteBatch* initial_batch);
+ SyncedSessionTracker* session_tracker);
~LocalSessionEventHandlerImpl() override;
// LocalSessionEventHandler implementation.
+ void OnSessionRestoreComplete() override;
void OnLocalTabModified(SyncedTabDelegate* modified_tab) override;
void OnFaviconsChanged(const std::set<GURL>& page_urls,
const GURL& icon_url) override;
@@ -80,44 +79,26 @@ class LocalSessionEventHandlerImpl : public LocalSessionEventHandler {
private:
enum ReloadTabsOption { RELOAD_TABS, DONT_RELOAD_TABS };
- // Updates |session_tracker_| with tab_id<->tab_node_id association that the
- // delegate already knows about, while resolving conflicts if the delegate
- // reports conflicting sync IDs. This makes sure duplicate tab_node_id-s are
- // not assigned. On return, the following conditions are met:
- // 1. Delegate contains no duplicate sync IDs (tab_node_id).
- // 2. Delegate contains no sync-ID <-> tab_id association that the tracker
- // doesn't know about (but not the opposite).
- void AssociateExistingSyncIds();
-
void AssociateWindows(ReloadTabsOption option,
- bool has_tabbed_window,
WriteBatch* batch);
// Loads and reassociates the local tab referenced in |tab|.
// |batch| must not be null. This function will append necessary
- // changes for processing later. Will only assign a new sync id if there is
- // a tabbed window, which results in failure for tabs without sync ids yet.
+ // changes for processing later.
void AssociateTab(SyncedTabDelegate* const tab,
- bool has_tabbed_window,
WriteBatch* batch);
// It's possible that when we associate windows, tabs aren't all loaded
// into memory yet (e.g on android) and we don't have a WebContents. In this
// case we can't do a full association, but we still want to update tab IDs
// as they may have changed after a session was restored. This method
- // compares new_tab_id and new_window_id against the previously persisted tab
- // ID and window ID (from our TabNodePool) and updates them if either differs.
+ // new_window_id against the previously persisted window ID (from our
+ // TabNodePool) and updates it.
void AssociateRestoredPlaceholderTab(const SyncedTabDelegate& tab_delegate,
- SessionID new_tab_id,
+ SessionID tab_id,
SessionID new_window_id,
WriteBatch* batch);
- // Appends an ACTION_UPDATE for a sync tab entity onto |batch| to
- // reflect the contents of |tab|, given the tab node id |sync_id|.
- void AppendChangeForExistingTab(int sync_id,
- const sessions::SessionTab& tab,
- WriteBatch* batch) const;
-
// Set |session_tab| from |tab_delegate|.
sync_pb::SessionTab GetTabSpecificsFromDelegate(
const SyncedTabDelegate& tab_delegate) const;
@@ -128,12 +109,6 @@ class LocalSessionEventHandlerImpl : public LocalSessionEventHandler {
// Update |tab_specifics| with the corresponding task ids.
void WriteTasksIntoSpecifics(sync_pb::SessionTab* tab_specifics);
- // On Android, it's possible to not have any tabbed windows when only custom
- // tabs are currently open. This means that there is tab data that will be
- // restored later, but we cannot access it. This method is an elaborate way to
- // check if we're currently in that state or not.
- bool ScanForTabbedWindow();
-
// Injected dependencies (not owned).
Delegate* const delegate_;
SyncSessionsClient* const sessions_client_;
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 38da108a3ae..7aeb24bb7d4 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
@@ -79,28 +79,25 @@ class MockDelegate : public LocalSessionEventHandlerImpl::Delegate {
};
class LocalSessionEventHandlerImplTest : public testing::Test {
- public:
+ protected:
LocalSessionEventHandlerImplTest()
: session_tracker_(&mock_sync_sessions_client_) {
ON_CALL(mock_sync_sessions_client_, GetSyncedWindowDelegatesGetter())
.WillByDefault(testing::Return(&window_getter_));
+ ON_CALL(mock_delegate_, CreateLocalSessionWriteBatch())
+ .WillByDefault(
+ Return(ByMove(std::make_unique<NiceMock<MockWriteBatch>>())));
session_tracker_.InitLocalSession(kSessionTag, kSessionName,
sync_pb::SyncEnums_DeviceType_TYPE_PHONE);
}
- void InitHandler(LocalSessionEventHandlerImpl::WriteBatch* initial_batch) {
+ void InitHandler() {
handler_ = std::make_unique<LocalSessionEventHandlerImpl>(
- &mock_delegate_, &mock_sync_sessions_client_, &session_tracker_,
- initial_batch);
+ &mock_delegate_, &mock_sync_sessions_client_, &session_tracker_);
window_getter_.router()->StartRoutingTo(handler_.get());
}
- void InitHandler() {
- NiceMock<MockWriteBatch> initial_batch;
- InitHandler(&initial_batch);
- }
-
TestSyncedWindowDelegate* AddWindow(
int window_id,
sync_pb::SessionWindow_BrowserType type =
@@ -279,12 +276,15 @@ TEST_F(LocalSessionEventHandlerImplTest, AssociateWindowsAndTabsIfEmpty) {
EXPECT_CALL(mock_delegate_, OnPageFaviconUpdated(_)).Times(0);
EXPECT_CALL(mock_delegate_, OnFaviconVisited(_, _)).Times(0);
- StrictMock<MockWriteBatch> mock_batch;
- EXPECT_CALL(mock_batch,
+ auto mock_batch = std::make_unique<StrictMock<MockWriteBatch>>();
+ EXPECT_CALL(*mock_batch,
Put(Pointee(MatchesHeader(kSessionTag, /*window_ids=*/IsEmpty(),
/*tabs_ids=*/IsEmpty()))));
+ EXPECT_CALL(*mock_batch, Commit());
+ EXPECT_CALL(mock_delegate_, CreateLocalSessionWriteBatch())
+ .WillOnce(Return(ByMove(std::move(mock_batch))));
- InitHandler(&mock_batch);
+ InitHandler();
}
// Tests that calling AssociateWindowsAndTabs() reflects the open tabs in a) the
@@ -304,23 +304,105 @@ TEST_F(LocalSessionEventHandlerImplTest, AssociateWindowsAndTabs) {
EXPECT_CALL(mock_delegate_, OnFaviconVisited(GURL(kBar1), _));
EXPECT_CALL(mock_delegate_, OnFaviconVisited(GURL(kBaz1), _));
- StrictMock<MockWriteBatch> mock_batch;
- EXPECT_CALL(mock_batch,
+ auto mock_batch = std::make_unique<StrictMock<MockWriteBatch>>();
+ EXPECT_CALL(*mock_batch,
Put(Pointee(MatchesHeader(kSessionTag, {kWindowId1, kWindowId2},
{kTabId1, kTabId2, kTabId3}))));
- EXPECT_CALL(mock_batch,
+ EXPECT_CALL(*mock_batch,
Put(Pointee(MatchesTab(kSessionTag, kWindowId1, kTabId1,
/*tab_node_id=*/_,
/*urls=*/{kFoo1}))));
- EXPECT_CALL(mock_batch,
+ EXPECT_CALL(*mock_batch,
Put(Pointee(MatchesTab(kSessionTag, kWindowId2, kTabId2,
/*tab_node_id=*/_, /*urls=*/{kBar1}))));
EXPECT_CALL(
- mock_batch,
+ *mock_batch,
Put(Pointee(MatchesTab(kSessionTag, kWindowId2, kTabId3,
/*tab_node_id=*/_, /*urls=*/{kBar2, kBaz1}))));
+ EXPECT_CALL(*mock_batch, Commit());
- InitHandler(&mock_batch);
+ EXPECT_CALL(mock_delegate_, CreateLocalSessionWriteBatch())
+ .WillOnce(Return(ByMove(std::move(mock_batch))));
+
+ InitHandler();
+}
+
+// Tests that association of windows and tabs gets deferred due to ongoing
+// session restore during startup.
+TEST_F(LocalSessionEventHandlerImplTest,
+ DeferAssociationDueToInitialSessionRestore) {
+ AddWindow(kWindowId1)->SetIsSessionRestoreInProgress(true);
+ AddTab(kWindowId1, kFoo1, kTabId1);
+ AddWindow(kWindowId2);
+ AddTab(kWindowId2, kBar1, kTabId2);
+ AddTab(kWindowId2, kBar2, kTabId3)->Navigate(kBaz1);
+
+ EXPECT_CALL(mock_delegate_, CreateLocalSessionWriteBatch()).Times(0);
+
+ InitHandler();
+
+ auto mock_batch = std::make_unique<StrictMock<MockWriteBatch>>();
+ EXPECT_CALL(*mock_batch,
+ Put(Pointee(MatchesHeader(kSessionTag, {kWindowId1, kWindowId2},
+ {kTabId1, kTabId2, kTabId3}))));
+ EXPECT_CALL(*mock_batch,
+ Put(Pointee(MatchesTab(kSessionTag, kWindowId1, kTabId1,
+ /*tab_node_id=*/_,
+ /*urls=*/{kFoo1}))));
+ EXPECT_CALL(*mock_batch,
+ Put(Pointee(MatchesTab(kSessionTag, kWindowId2, kTabId2,
+ /*tab_node_id=*/_, /*urls=*/{kBar1}))));
+ EXPECT_CALL(
+ *mock_batch,
+ Put(Pointee(MatchesTab(kSessionTag, kWindowId2, kTabId3,
+ /*tab_node_id=*/_, /*urls=*/{kBar2, kBaz1}))));
+ EXPECT_CALL(*mock_batch, Commit());
+
+ EXPECT_CALL(mock_delegate_, CreateLocalSessionWriteBatch())
+ .WillOnce(Return(ByMove(std::move(mock_batch))));
+
+ window_getter_.SessionRestoreComplete();
+}
+
+// Tests that association of windows and tabs gets deferred due to ongoing
+// session restore happening at a late stage (e.g. CCT-only / no-tabbed-window
+// to tabbed-window transition).
+TEST_F(LocalSessionEventHandlerImplTest,
+ DeferAssociationDueToLateSessionRestore) {
+ AddWindow(kWindowId1);
+ AddTab(kWindowId1, kFoo1, kTabId1);
+
+ InitHandler();
+
+ // No updates expected during session restore.
+ EXPECT_CALL(mock_delegate_, CreateLocalSessionWriteBatch()).Times(0);
+
+ AddWindow(kWindowId2)->SetIsSessionRestoreInProgress(true);
+ AddTab(kWindowId2, kBar1, kTabId2);
+ AddTab(kWindowId2, kBar2, kTabId3)->Navigate(kBaz1);
+
+ // As soon as session restore completes, we expect all updates.
+ auto mock_batch = std::make_unique<StrictMock<MockWriteBatch>>();
+ EXPECT_CALL(*mock_batch,
+ Put(Pointee(MatchesHeader(kSessionTag, {kWindowId1, kWindowId2},
+ {kTabId1, kTabId2, kTabId3}))));
+ EXPECT_CALL(*mock_batch,
+ Put(Pointee(MatchesTab(kSessionTag, kWindowId1, kTabId1,
+ /*tab_node_id=*/_,
+ /*urls=*/{kFoo1}))));
+ EXPECT_CALL(*mock_batch,
+ Put(Pointee(MatchesTab(kSessionTag, kWindowId2, kTabId2,
+ /*tab_node_id=*/_, /*urls=*/{kBar1}))));
+ EXPECT_CALL(
+ *mock_batch,
+ Put(Pointee(MatchesTab(kSessionTag, kWindowId2, kTabId3,
+ /*tab_node_id=*/_, /*urls=*/{kBar2, kBaz1}))));
+ EXPECT_CALL(*mock_batch, Commit());
+
+ EXPECT_CALL(mock_delegate_, CreateLocalSessionWriteBatch())
+ .WillOnce(Return(ByMove(std::move(mock_batch))));
+
+ window_getter_.SessionRestoreComplete();
}
// Tests that calling AssociateWindowsAndTabs() reflects the open tabs in a) the
@@ -366,53 +448,28 @@ TEST_F(LocalSessionEventHandlerImplTest, AssociateCustomTab) {
// In the current session, all we have is a custom tab.
AddWindow(kWindowId3, sync_pb::SessionWindow_BrowserType_TYPE_CUSTOM_TAB);
- AddTab(kWindowId3, kFoo1, kTabId3)->SetSyncId(kCustomTabNodeId);
+ AddTab(kWindowId3, kFoo1, kTabId2);
+
+ auto mock_batch = std::make_unique<StrictMock<MockWriteBatch>>();
+ EXPECT_CALL(*mock_batch, Put(Pointee(MatchesTab(kSessionTag, kWindowId3,
+ kTabId2, kCustomTabNodeId,
+ /*urls=*/{kFoo1}))));
+ EXPECT_CALL(*mock_batch,
+ Put(Pointee(MatchesHeader(kSessionTag,
+ {kWindowId1, kWindowId2, kWindowId3},
+ {kTabId1, kTabId2}))));
+ EXPECT_CALL(*mock_batch, Commit());
- EXPECT_CALL(mock_delegate_, CreateLocalSessionWriteBatch()).Times(0);
+ EXPECT_CALL(mock_delegate_, CreateLocalSessionWriteBatch())
+ .WillOnce(Return(ByMove(std::move(mock_batch))));
- StrictMock<MockWriteBatch> mock_batch;
- testing::InSequence seq;
- EXPECT_CALL(mock_batch,
- Put(Pointee(MatchesTab(kSessionTag, kWindowId1, kTabId1,
- kRegularTabNodeId, /*urls=*/{}))));
- // Overriden by the Put() below, so we don't care about the args.
- EXPECT_CALL(mock_batch,
- Put(Pointee(MatchesTab(kSessionTag, _, _, kCustomTabNodeId,
- /*urls=*/_))));
- EXPECT_CALL(mock_batch, Put(Pointee(MatchesTab(kSessionTag, kWindowId3,
- kTabId3, kCustomTabNodeId,
- /*urls=*/{kFoo1}))));
- EXPECT_CALL(mock_batch, Put(Pointee(MatchesHeader(
- kSessionTag, {kWindowId1, kWindowId2, kWindowId3},
- {kTabId1, kTabId3}))));
- InitHandler(&mock_batch);
+ InitHandler();
EXPECT_THAT(session_tracker_.LookupSession(kSessionTag),
MatchesSyncedSession(kSessionTag,
{{kWindowId1, std::vector<int>{kTabId1}},
{kWindowId2, std::vector<int>()},
- {kWindowId3, std::vector<int>{kTabId3}}}));
-}
-
-// Tests that calling initial association during construction handles the case
-// where only a subset of tabs (and not the first) have a sync ID.
-TEST_F(LocalSessionEventHandlerImplTest, AssociateTabsWhenOnlySomeHaveNodeIds) {
- const int kTabNodeId = 0;
-
- AddWindow(kWindowId1);
- AddTab(kWindowId1, kFoo1, kTabId1);
- AddTab(kWindowId1, kBar1, kTabId2)->SetSyncId(kTabNodeId);
-
- StrictMock<MockWriteBatch> mock_batch;
- EXPECT_CALL(mock_batch, Put(Pointee(MatchesHeader(_, _, _))));
- EXPECT_CALL(mock_batch,
- Put(Pointee(MatchesTab(_, _, kTabId1, /*tab_node_id=*/1,
- /*urls=*/_))));
- EXPECT_CALL(mock_batch,
- Put(Pointee(MatchesTab(_, _, kTabId2,
- /*tab_node_id=*/kTabNodeId, /*urls=*/_))));
-
- InitHandler(&mock_batch);
+ {kWindowId3, std::vector<int>{kTabId2}}}));
}
TEST_F(LocalSessionEventHandlerImplTest, PropagateNewNavigation) {
@@ -471,6 +528,35 @@ TEST_F(LocalSessionEventHandlerImplTest, PropagateNewTab) {
AddTab(kWindowId1, kBar1, kTabId2);
}
+TEST_F(LocalSessionEventHandlerImplTest, PropagateNewCustomTab) {
+ InitHandler();
+
+ // Tab creation triggers an update event due to the tab parented notification,
+ // so the event handler issues two commits as well (one for tab creation, one
+ // for tab update). During the first update, however, the tab is not syncable
+ // and is hence skipped.
+ auto tab_create_mock_batch = std::make_unique<StrictMock<MockWriteBatch>>();
+ EXPECT_CALL(*tab_create_mock_batch,
+ Put(Pointee(MatchesHeader(kSessionTag, {}, {}))));
+ EXPECT_CALL(*tab_create_mock_batch, Commit());
+
+ auto navigation_mock_batch = std::make_unique<StrictMock<MockWriteBatch>>();
+ EXPECT_CALL(
+ *navigation_mock_batch,
+ Put(Pointee(MatchesHeader(kSessionTag, {kWindowId1}, {kTabId1}))));
+ EXPECT_CALL(*navigation_mock_batch,
+ Put(Pointee(MatchesTab(kSessionTag, kWindowId1, kTabId1,
+ /*tab_node_id=*/0, /*urls=*/{kFoo1}))));
+ EXPECT_CALL(*navigation_mock_batch, Commit());
+
+ EXPECT_CALL(mock_delegate_, CreateLocalSessionWriteBatch())
+ .WillOnce(Return(ByMove(std::move(tab_create_mock_batch))))
+ .WillOnce(Return(ByMove(std::move(navigation_mock_batch))));
+
+ AddWindow(kWindowId1, sync_pb::SessionWindow_BrowserType_TYPE_CUSTOM_TAB);
+ AddTab(kWindowId1, kFoo1, kTabId1);
+}
+
TEST_F(LocalSessionEventHandlerImplTest, PropagateNewWindow) {
AddWindow(kWindowId1);
AddTab(kWindowId1, kFoo1, kTabId1);
@@ -545,10 +631,9 @@ TEST_F(LocalSessionEventHandlerImplTest,
AddWindow(kWindowId1, sync_pb::SessionWindow_BrowserType_TYPE_CUSTOM_TAB);
TestSyncedTabDelegate* tab1 = AddTab(kWindowId1, kFoo1, kTabId1);
- tab1->SetSyncId(kTabNodeId1);
AddWindow(kWindowId2, sync_pb::SessionWindow_BrowserType_TYPE_CUSTOM_TAB);
- AddTab(kWindowId2, kBar1, kTabId2)->SetSyncId(kTabNodeId2);
+ AddTab(kWindowId2, kBar1, kTabId2);
InitHandler();
diff --git a/chromium/components/sync_sessions/local_session_event_router.h b/chromium/components/sync_sessions/local_session_event_router.h
index 51b07c3db03..e69c8e6570c 100644
--- a/chromium/components/sync_sessions/local_session_event_router.h
+++ b/chromium/components/sync_sessions/local_session_event_router.h
@@ -22,6 +22,11 @@ class LocalSessionEventHandler {
public:
virtual ~LocalSessionEventHandler() {}
+ // Called when asynchronous session restore has completed. On Android, this
+ // can be called multiple times (e.g. transition from a CCT without tabbed
+ // window to actually starting a tabbed activity).
+ virtual void OnSessionRestoreComplete() = 0;
+
// A local navigation event took place that affects the synced session
// for this instance of Chrome.
virtual void OnLocalTabModified(SyncedTabDelegate* modified_tab) = 0;
diff --git a/chromium/components/sync_sessions/lost_navigations_recorder_unittest.cc b/chromium/components/sync_sessions/lost_navigations_recorder_unittest.cc
index 1a673fae6dc..4774e9dd92e 100644
--- a/chromium/components/sync_sessions/lost_navigations_recorder_unittest.cc
+++ b/chromium/components/sync_sessions/lost_navigations_recorder_unittest.cc
@@ -8,7 +8,7 @@
#include <string>
#include "base/message_loop/message_loop.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "components/sync/syncable/entry.h"
#include "components/sync/syncable/mutable_entry.h"
#include "components/sync/syncable/syncable_base_transaction.h"
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 9649cc418cc..4d7e14a045a 100644
--- a/chromium/components/sync_sessions/open_tabs_ui_delegate_impl.cc
+++ b/chromium/components/sync_sessions/open_tabs_ui_delegate_impl.cc
@@ -92,7 +92,7 @@ bool OpenTabsUIDelegateImpl::GetForeignSessionTabs(
tabs->push_back(tab);
}
}
- std::sort(tabs->begin(), tabs->end(), TabsRecencyComparator);
+ std::stable_sort(tabs->begin(), tabs->end(), TabsRecencyComparator);
return true;
}
diff --git a/chromium/components/sync_sessions/session_data_type_controller.cc b/chromium/components/sync_sessions/session_data_type_controller.cc
index 4ec2abc5aa2..3516a3993ea 100644
--- a/chromium/components/sync_sessions/session_data_type_controller.cc
+++ b/chromium/components/sync_sessions/session_data_type_controller.cc
@@ -6,12 +6,9 @@
#include <set>
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "components/prefs/pref_service.h"
#include "components/sync/driver/sync_client.h"
-#include "components/sync_sessions/sync_sessions_client.h"
-#include "components/sync_sessions/synced_window_delegate.h"
-#include "components/sync_sessions/synced_window_delegates_getter.h"
namespace sync_sessions {
@@ -24,11 +21,10 @@ SessionDataTypeController::SessionDataTypeController(
dump_stack,
sync_client,
syncer::GROUP_UI,
- base::ThreadTaskRunnerHandle::Get()),
+ base::SequencedTaskRunnerHandle::Get()),
sync_client_(sync_client),
local_device_(local_device),
history_disabled_pref_name_(history_disabled_pref_name),
- waiting_on_session_restore_(false),
waiting_on_local_device_info_(false) {
DCHECK(local_device_);
pref_registrar_.Init(sync_client_->GetPrefService());
@@ -42,16 +38,6 @@ SessionDataTypeController::~SessionDataTypeController() {}
bool SessionDataTypeController::StartModels() {
DCHECK(CalledOnValidThread());
- SyncedWindowDelegatesGetter* synced_window_getter =
- sync_client_->GetSyncSessionsClient()->GetSyncedWindowDelegatesGetter();
- SyncedWindowDelegatesGetter::SyncedWindowDelegateMap windows =
- synced_window_getter->GetSyncedWindowDelegates();
- for (const auto& window_iter_pair : windows) {
- if (window_iter_pair.second->IsSessionRestoreInProgress()) {
- waiting_on_session_restore_ = true;
- break;
- }
- }
if (!local_device_->GetLocalDeviceInfo()) {
subscription_ = local_device_->RegisterOnInitializedCallback(
@@ -74,14 +60,8 @@ bool SessionDataTypeController::ReadyForStart() const {
history_disabled_pref_name_);
}
-void SessionDataTypeController::OnSessionRestoreComplete() {
- DCHECK(CalledOnValidThread());
- waiting_on_session_restore_ = false;
- MaybeCompleteLoading();
-}
-
bool SessionDataTypeController::IsWaiting() {
- return waiting_on_session_restore_ || waiting_on_local_device_info_;
+ return waiting_on_local_device_info_;
}
void SessionDataTypeController::MaybeCompleteLoading() {
diff --git a/chromium/components/sync_sessions/session_data_type_controller.h b/chromium/components/sync_sessions/session_data_type_controller.h
index db217f94863..635bfd660bd 100644
--- a/chromium/components/sync_sessions/session_data_type_controller.h
+++ b/chromium/components/sync_sessions/session_data_type_controller.h
@@ -14,9 +14,7 @@
namespace sync_sessions {
-// Overrides StartModels to avoid sync contention with sessions during
-// a session restore operation at startup and to wait for the local
-// device info to become available.
+// Overrides StartModels to wait for the local device info to become available.
class SessionDataTypeController : public syncer::AsyncDirectoryTypeController {
public:
// |dump_stack| is called when an unrecoverable error occurs.
@@ -31,9 +29,6 @@ class SessionDataTypeController : public syncer::AsyncDirectoryTypeController {
void StopModels() override;
bool ReadyForStart() const override;
- // Called when asynchronous session restore has completed.
- void OnSessionRestoreComplete();
-
private:
bool IsWaiting();
void MaybeCompleteLoading();
@@ -49,7 +44,6 @@ class SessionDataTypeController : public syncer::AsyncDirectoryTypeController {
const char* history_disabled_pref_name_;
// Flags that indicate the reason for pending loading models.
- bool waiting_on_session_restore_;
bool waiting_on_local_device_info_;
PrefChangeRegistrar pref_registrar_;
diff --git a/chromium/components/sync_sessions/session_data_type_controller_unittest.cc b/chromium/components/sync_sessions/session_data_type_controller_unittest.cc
index 8a286ebac91..7d0bc714bff 100644
--- a/chromium/components/sync_sessions/session_data_type_controller_unittest.cc
+++ b/chromium/components/sync_sessions/session_data_type_controller_unittest.cc
@@ -19,9 +19,6 @@
#include "components/sync/device_info/local_device_info_provider_mock.h"
#include "components/sync/driver/fake_sync_client.h"
#include "components/sync/driver/sync_api_component_factory_mock.h"
-#include "components/sync_sessions/mock_sync_sessions_client.h"
-#include "components/sync_sessions/synced_window_delegate.h"
-#include "components/sync_sessions/synced_window_delegates_getter.h"
#include "testing/gtest/include/gtest/gtest.h"
using syncer::LocalDeviceInfoProviderMock;
@@ -32,58 +29,6 @@ namespace {
const char* kSavingBrowserHistoryDisabled = "history_disabled";
-class MockSyncedWindowDelegate : public SyncedWindowDelegate {
- public:
- MockSyncedWindowDelegate() : is_restore_in_progress_(false) {}
- ~MockSyncedWindowDelegate() override {}
-
- bool HasWindow() const override { return false; }
- SessionID GetSessionId() const override { return SessionID::InvalidValue(); }
- int GetTabCount() const override { return 0; }
- int GetActiveIndex() const override { return 0; }
- bool IsApp() const override { return false; }
- bool IsTypeTabbed() const override { return false; }
- bool IsTypePopup() const override { return false; }
- bool IsTabPinned(const SyncedTabDelegate* tab) const override {
- return false;
- }
- SyncedTabDelegate* GetTabAt(int index) const override { return nullptr; }
- SessionID GetTabIdAt(int index) const override {
- return SessionID::InvalidValue();
- }
-
- bool IsSessionRestoreInProgress() const override {
- return is_restore_in_progress_;
- }
-
- bool ShouldSync() const override { return false; }
-
- void SetSessionRestoreInProgress(bool is_restore_in_progress) {
- is_restore_in_progress_ = is_restore_in_progress;
- }
-
- private:
- bool is_restore_in_progress_;
-};
-
-class MockSyncedWindowDelegatesGetter : public SyncedWindowDelegatesGetter {
- public:
- SyncedWindowDelegateMap GetSyncedWindowDelegates() override {
- return delegates_;
- }
-
- const SyncedWindowDelegate* FindById(SessionID id) override {
- return nullptr;
- }
-
- void Add(SyncedWindowDelegate* delegate) {
- delegates_[delegate->GetSessionId()] = delegate;
- }
-
- private:
- SyncedWindowDelegateMap delegates_;
-};
-
class SessionDataTypeControllerTest : public testing::Test,
public syncer::FakeSyncClient {
public:
@@ -96,21 +41,10 @@ class SessionDataTypeControllerTest : public testing::Test,
// FakeSyncClient overrides.
PrefService* GetPrefService() override { return &prefs_; }
- SyncSessionsClient* GetSyncSessionsClient() override {
- return &mock_sync_sessions_client_;
- }
-
void SetUp() override {
prefs_.registry()->RegisterBooleanPref(kSavingBrowserHistoryDisabled,
false);
- synced_window_delegate_ = std::make_unique<MockSyncedWindowDelegate>();
- synced_window_getter_ = std::make_unique<MockSyncedWindowDelegatesGetter>();
- synced_window_getter_->Add(synced_window_delegate_.get());
-
- ON_CALL(mock_sync_sessions_client_, GetSyncedWindowDelegatesGetter())
- .WillByDefault(testing::Return(synced_window_getter_.get()));
-
local_device_ = std::make_unique<LocalDeviceInfoProviderMock>(
"cache_guid", "Wayne Gretzky's Hacking Box", "Chromium 10k",
"Chrome 10k", sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id");
@@ -160,22 +94,10 @@ class SessionDataTypeControllerTest : public testing::Test,
LocalDeviceInfoProviderMock* local_device() { return local_device_.get(); }
SessionDataTypeController* controller() { return controller_.get(); }
- protected:
- void SetSessionRestoreInProgress(bool is_restore_in_progress) {
- synced_window_delegate_->SetSessionRestoreInProgress(
- is_restore_in_progress);
-
- if (!is_restore_in_progress)
- controller_->OnSessionRestoreComplete();
- }
-
private:
base::MessageLoop message_loop_;
TestingPrefServiceSimple prefs_;
- std::unique_ptr<MockSyncedWindowDelegate> synced_window_delegate_;
- std::unique_ptr<MockSyncedWindowDelegatesGetter> synced_window_getter_;
syncer::SyncApiComponentFactoryMock profile_sync_factory_;
- testing::NiceMock<MockSyncSessionsClient> mock_sync_sessions_client_;
std::unique_ptr<LocalDeviceInfoProviderMock> local_device_;
std::unique_ptr<SessionDataTypeController> controller_;
@@ -201,51 +123,6 @@ TEST_F(SessionDataTypeControllerTest, StartModelsDelayedByLocalDevice) {
EXPECT_TRUE(LoadResult());
}
-TEST_F(SessionDataTypeControllerTest, StartModelsDelayedByRestoreInProgress) {
- SetSessionRestoreInProgress(true);
- Start();
- EXPECT_FALSE(load_finished());
- EXPECT_EQ(syncer::DataTypeController::MODEL_STARTING, controller()->state());
-
- SetSessionRestoreInProgress(false);
- EXPECT_EQ(syncer::DataTypeController::MODEL_LOADED, controller()->state());
- EXPECT_TRUE(LoadResult());
-}
-
-TEST_F(SessionDataTypeControllerTest,
- StartModelsDelayedByLocalDeviceThenRestoreInProgress) {
- local_device()->SetInitialized(false);
- SetSessionRestoreInProgress(true);
- Start();
- EXPECT_FALSE(load_finished());
- EXPECT_EQ(syncer::DataTypeController::MODEL_STARTING, controller()->state());
-
- local_device()->SetInitialized(true);
- EXPECT_FALSE(load_finished());
- EXPECT_EQ(syncer::DataTypeController::MODEL_STARTING, controller()->state());
-
- SetSessionRestoreInProgress(false);
- EXPECT_EQ(syncer::DataTypeController::MODEL_LOADED, controller()->state());
- EXPECT_TRUE(LoadResult());
-}
-
-TEST_F(SessionDataTypeControllerTest,
- StartModelsDelayedByRestoreInProgressThenLocalDevice) {
- local_device()->SetInitialized(false);
- SetSessionRestoreInProgress(true);
- Start();
- EXPECT_FALSE(load_finished());
- EXPECT_EQ(syncer::DataTypeController::MODEL_STARTING, controller()->state());
-
- SetSessionRestoreInProgress(false);
- EXPECT_FALSE(load_finished());
- EXPECT_EQ(syncer::DataTypeController::MODEL_STARTING, controller()->state());
-
- local_device()->SetInitialized(true);
- EXPECT_EQ(syncer::DataTypeController::MODEL_LOADED, controller()->state());
- EXPECT_TRUE(LoadResult());
-}
-
} // namespace
} // 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
new file mode 100644
index 00000000000..4d09e0ad320
--- /dev/null
+++ b/chromium/components/sync_sessions/session_model_type_controller.cc
@@ -0,0 +1,51 @@
+// 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/sync_sessions/session_model_type_controller.h"
+
+#include "components/prefs/pref_service.h"
+#include "components/sync/driver/sync_client.h"
+
+namespace sync_sessions {
+
+SessionModelTypeController::SessionModelTypeController(
+ syncer::SyncClient* sync_client,
+ const scoped_refptr<base::SingleThreadTaskRunner>& model_thread,
+ const std::string& history_disabled_pref_name)
+ : ModelTypeController(syncer::SESSIONS, sync_client, model_thread),
+ history_disabled_pref_name_(history_disabled_pref_name) {
+ pref_registrar_.Init(sync_client->GetPrefService());
+ pref_registrar_.Add(
+ history_disabled_pref_name_,
+ base::BindRepeating(
+ &SessionModelTypeController::OnSavingBrowserHistoryPrefChanged,
+ base::AsWeakPtr(this)));
+}
+
+SessionModelTypeController::~SessionModelTypeController() {}
+
+bool SessionModelTypeController::ReadyForStart() const {
+ DCHECK(CalledOnValidThread());
+ return !sync_client()->GetPrefService()->GetBoolean(
+ history_disabled_pref_name_);
+}
+
+void SessionModelTypeController::OnSavingBrowserHistoryPrefChanged() {
+ DCHECK(CalledOnValidThread());
+ if (!sync_client()->GetPrefService()->GetBoolean(
+ history_disabled_pref_name_)) {
+ return;
+ }
+
+ // If history and tabs persistence is turned off then generate an
+ // unrecoverable error. SESSIONS won't be a registered type on the next
+ // Chrome restart.
+ if (state() != NOT_RUNNING && state() != STOPPING) {
+ ReportModelError(
+ syncer::SyncError::DATATYPE_POLICY_ERROR,
+ {FROM_HERE, "History and tab saving is now disabled by policy."});
+ }
+}
+
+} // 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
new file mode 100644
index 00000000000..cea1992a064
--- /dev/null
+++ b/chromium/components/sync_sessions/session_model_type_controller.h
@@ -0,0 +1,47 @@
+// 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_SYNC_SESSIONS_SESSION_MODEL_TYPE_CONTROLLER_H_
+#define COMPONENTS_SYNC_SESSIONS_SESSION_MODEL_TYPE_CONTROLLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/single_thread_task_runner.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/sync/driver/model_type_controller.h"
+
+namespace syncer {
+class SyncClient;
+}
+
+namespace sync_sessions {
+
+// Overrides LoadModels to check if history sync is allowed by policy.
+class SessionModelTypeController : public syncer::ModelTypeController {
+ public:
+ SessionModelTypeController(
+ syncer::SyncClient* sync_client,
+ const scoped_refptr<base::SingleThreadTaskRunner>& model_thread,
+ const std::string& history_disabled_pref_name);
+ ~SessionModelTypeController() override;
+
+ // DataTypeController overrides.
+ bool ReadyForStart() const override;
+
+ private:
+ void OnSavingBrowserHistoryPrefChanged();
+
+ // Name of the pref that indicates whether saving history is disabled.
+ const std::string history_disabled_pref_name_;
+
+ PrefChangeRegistrar pref_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(SessionModelTypeController);
+};
+
+} // namespace sync_sessions
+
+#endif // COMPONENTS_SYNC_SESSIONS_SESSION_MODEL_TYPE_CONTROLLER_H_
diff --git a/chromium/components/sync_sessions/session_store.cc b/chromium/components/sync_sessions/session_store.cc
index f08a600075c..0810e50a5fd 100644
--- a/chromium/components/sync_sessions/session_store.cc
+++ b/chromium/components/sync_sessions/session_store.cc
@@ -449,13 +449,6 @@ SessionStore::SessionStore(
local_session_info.client_name,
local_session_info.device_type);
- // Map of all rewritten local ids. Because ids are reset on each restart,
- // and id generation happens outside of Sync, all ids from a previous local
- // session must be rewritten in order to be valid (i.e not collide with
- // newly assigned IDs). Otherwise, SyncedSessionTracker could mix up IDs.
- // Key: previous session id. Value: new session id.
- std::map<SessionID::id_type, SessionID> session_id_map;
-
bool found_local_header = false;
for (auto& storage_key_and_specifics : initial_data) {
@@ -498,26 +491,8 @@ SessionStore::SessionStore(
DCHECK(!found_local_header);
found_local_header = true;
- // Go through and generate new tab and window ids as necessary, updating
- // the specifics in place.
- for (auto& window : *specifics.mutable_header()->mutable_window()) {
- session_id_map.emplace(window.window_id(), SessionID::NewUnique());
- window.set_window_id(session_id_map.at(window.window_id()).id());
-
- for (int& tab_id : *window.mutable_tab()) {
- if (session_id_map.count(tab_id) == 0) {
- session_id_map.emplace(tab_id, SessionID::NewUnique());
- }
- tab_id = session_id_map.at(tab_id).id();
- // Note: the tab id of the SessionTab will be updated when the tab
- // node itself is processed.
- }
- }
-
UpdateTrackerWithSpecifics(specifics, mtime, &session_tracker_);
-
- DVLOG(1) << "Loaded local header and rewrote " << session_id_map.size()
- << " ids.";
+ DVLOG(1) << "Loaded local header.";
} else {
DCHECK(specifics.has_tab());
@@ -526,20 +501,12 @@ SessionStore::SessionStore(
DVLOG(1) << "Associating local tab " << specifics.tab().tab_id()
<< " with node " << specifics.tab_node_id();
- // Now file the tab under the new tab id.
- SessionID new_tab_id = SessionID::InvalidValue();
- auto iter = session_id_map.find(specifics.tab().tab_id());
- if (iter != session_id_map.end()) {
- new_tab_id = iter->second;
- } else {
- new_tab_id = SessionID::NewUnique();
- session_id_map.emplace(specifics.tab().tab_id(), new_tab_id);
- }
- DVLOG(1) << "Remapping tab " << specifics.tab().tab_id() << " to "
- << new_tab_id;
-
- specifics.mutable_tab()->set_tab_id(new_tab_id.id());
- session_tracker_.ReassociateLocalTab(specifics.tab_node_id(), new_tab_id);
+ // TODO(mastiz): Move call to ReassociateLocalTab() into
+ // UpdateTrackerWithSpecifics(), possibly merge with OnTabNodeSeen(). Also
+ // consider merging this branch with processing of foreign tabs above.
+ session_tracker_.ReassociateLocalTab(
+ specifics.tab_node_id(),
+ SessionID::FromSerializedValue(specifics.tab().tab_id()));
UpdateTrackerWithSpecifics(specifics, mtime, &session_tracker_);
}
}
diff --git a/chromium/components/sync_sessions/session_sync_bridge.cc b/chromium/components/sync_sessions/session_sync_bridge.cc
index 62ebe3c1458..501cee1235e 100644
--- a/chromium/components/sync_sessions/session_sync_bridge.cc
+++ b/chromium/components/sync_sessions/session_sync_bridge.cc
@@ -13,6 +13,8 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/location.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "components/sync/base/hash_util.h"
#include "components/sync/base/time.h"
@@ -94,20 +96,6 @@ class LocalSessionWriteBatch : public LocalSessionEventHandlerImpl::WriteBatch {
syncer::ModelTypeChangeProcessor* const processor_;
};
-bool IsSessionRestoreInProgress(SyncSessionsClient* sessions_client) {
- DCHECK(sessions_client);
- SyncedWindowDelegatesGetter* synced_window_getter =
- sessions_client->GetSyncedWindowDelegatesGetter();
- SyncedWindowDelegatesGetter::SyncedWindowDelegateMap windows =
- synced_window_getter->GetSyncedWindowDelegates();
- for (const auto& window_iter_pair : windows) {
- if (window_iter_pair.second->IsSessionRestoreInProgress()) {
- return true;
- }
- }
- return false;
-}
-
} // namespace
SessionSyncBridge::SessionSyncBridge(
@@ -132,8 +120,7 @@ SessionSyncBridge::SessionSyncBridge(
store_factory,
base::BindRepeating(&FaviconCache::UpdateMappingsFromForeignTab,
base::Unretained(&favicon_cache_)))),
- is_session_restore_in_progress_(
- IsSessionRestoreInProgress(sessions_client)) {
+ weak_ptr_factory_(this) {
DCHECK(sessions_client_);
DCHECK(local_session_event_router_);
DCHECK(foreign_sessions_updated_callback_);
@@ -149,9 +136,9 @@ void SessionSyncBridge::ScheduleGarbageCollection() {
if (!syncing_) {
return;
}
- base::ThreadTaskRunnerHandle::Get()->PostTask(
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&SessionSyncBridge::DoGarbageCollection,
- base::AsWeakPtr(this)));
+ weak_ptr_factory_.GetWeakPtr()));
}
FaviconCache* SessionSyncBridge::GetFaviconCache() {
@@ -169,14 +156,6 @@ OpenTabsUIDelegate* SessionSyncBridge::GetOpenTabsUIDelegate() {
return syncing_->open_tabs_ui_delegate.get();
}
-void SessionSyncBridge::OnSessionRestoreComplete() {
- is_session_restore_in_progress_ = false;
-
- if (syncing_) {
- StartLocalSessionEventHandler();
- }
-}
-
syncer::SyncableService* SessionSyncBridge::GetSyncableService() {
return nullptr;
}
@@ -196,9 +175,7 @@ base::Optional<syncer::ModelError> SessionSyncBridge::MergeSyncData(
DCHECK(syncing_);
DCHECK(change_processor()->IsTrackingMetadata());
- if (!is_session_restore_in_progress_) {
- StartLocalSessionEventHandler();
- }
+ StartLocalSessionEventHandler();
return ApplySyncChanges(std::move(metadata_change_list),
std::move(entity_data));
@@ -209,19 +186,11 @@ void SessionSyncBridge::StartLocalSessionEventHandler() {
DCHECK(syncing_);
DCHECK(!syncing_->local_session_event_handler);
DCHECK(change_processor()->IsTrackingMetadata());
- DCHECK(!is_session_restore_in_progress_);
- // TODO(crbug.com/681921): Remove injecting |local_session_write_batch| and
- // let the impl create one via the delegate once the directory-based
- // implementation is removed.
- std::unique_ptr<LocalSessionEventHandlerImpl::WriteBatch>
- local_session_write_batch = CreateLocalSessionWriteBatch();
syncing_->local_session_event_handler =
std::make_unique<LocalSessionEventHandlerImpl>(
/*delegate=*/this, sessions_client_,
- syncing_->store->mutable_tracker(), local_session_write_batch.get());
-
- local_session_write_batch->Commit();
+ syncing_->store->mutable_tracker());
// Start processing local changes, which will be propagated to the store as
// well as the processor.
@@ -295,9 +264,10 @@ base::Optional<syncer::ModelError> SessionSyncBridge::ApplySyncChanges(
->TransferChangesTo(batch->GetMetadataChangeList());
SessionStore::WriteBatch::Commit(std::move(batch));
- // This might overtrigger because we don't check if the batch is empty, but
- // observers should handle these events well so we don't bother detecting.
- foreign_sessions_updated_callback_.Run();
+ if (!entity_changes.empty()) {
+ foreign_sessions_updated_callback_.Run();
+ }
+
return base::nullopt;
}
@@ -307,7 +277,7 @@ void SessionSyncBridge::GetData(StorageKeyList storage_keys,
std::move(callback).Run(syncing_->store->GetSessionDataForKeys(storage_keys));
}
-void SessionSyncBridge::GetAllData(DataCallback callback) {
+void SessionSyncBridge::GetAllDataForDebugging(DataCallback callback) {
DCHECK(syncing_);
std::move(callback).Run(syncing_->store->GetAllSessionData());
}
@@ -325,15 +295,14 @@ std::string SessionSyncBridge::GetStorageKey(
return SessionStore::GetStorageKey(entity_data.specifics.session());
}
-ModelTypeSyncBridge::DisableSyncResponse
-SessionSyncBridge::ApplyDisableSyncChanges(
+ModelTypeSyncBridge::StopSyncResponse SessionSyncBridge::ApplyStopSyncChanges(
std::unique_ptr<MetadataChangeList> delete_metadata_change_list) {
local_session_event_router_->Stop();
- if (syncing_) {
+ if (syncing_ && delete_metadata_change_list) {
syncing_->store->DeleteAllDataAndMetadata();
}
syncing_.reset();
- return DisableSyncResponse::kModelNoLongerReadyToSync;
+ return StopSyncResponse::kModelNoLongerReadyToSync;
}
std::unique_ptr<LocalSessionEventHandlerImpl::WriteBatch>
@@ -347,9 +316,9 @@ SessionSyncBridge::CreateLocalSessionWriteBatch() {
syncing_->local_data_out_of_sync = false;
// We use PostTask() to avoid interferring with the ongoing handling of
// local changes that triggered this function.
- base::ThreadTaskRunnerHandle::Get()->PostTask(
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&SessionSyncBridge::ResubmitLocalSession,
- base::AsWeakPtr(this)));
+ weak_ptr_factory_.GetWeakPtr()));
}
return std::make_unique<LocalSessionWriteBatch>(
@@ -371,11 +340,12 @@ void SessionSyncBridge::OnFaviconVisited(const GURL& page_url,
favicon_cache_.OnFaviconVisited(page_url, favicon_url);
}
-void SessionSyncBridge::OnSyncStarting() {
+void SessionSyncBridge::OnSyncStarting(
+ const syncer::DataTypeActivationRequest& request) {
DCHECK(!syncing_);
session_store_factory_.Run(base::BindOnce(
- &SessionSyncBridge::OnStoreInitialized, base::AsWeakPtr(this)));
+ &SessionSyncBridge::OnStoreInitialized, weak_ptr_factory_.GetWeakPtr()));
}
void SessionSyncBridge::OnStoreInitialized(
@@ -403,8 +373,7 @@ void SessionSyncBridge::OnStoreInitialized(
// If initial sync was already done, MergeSyncData() will never be called so
// we need to start syncing local changes.
- if (change_processor()->IsTrackingMetadata() &&
- !is_session_restore_in_progress_) {
+ if (change_processor()->IsTrackingMetadata()) {
StartLocalSessionEventHandler();
}
}
@@ -481,8 +450,8 @@ std::unique_ptr<SessionStore::WriteBatch>
SessionSyncBridge::CreateSessionStoreWriteBatch() {
DCHECK(syncing_);
- return syncing_->store->CreateWriteBatch(
- base::BindOnce(&SessionSyncBridge::ReportError, base::AsWeakPtr(this)));
+ return syncing_->store->CreateWriteBatch(base::BindOnce(
+ &SessionSyncBridge::ReportError, weak_ptr_factory_.GetWeakPtr()));
}
void SessionSyncBridge::ResubmitLocalSession() {
diff --git a/chromium/components/sync_sessions/session_sync_bridge.h b/chromium/components/sync_sessions/session_sync_bridge.h
index c18a2dcd6b7..f8cd61c681e 100644
--- a/chromium/components/sync_sessions/session_sync_bridge.h
+++ b/chromium/components/sync_sessions/session_sync_bridge.h
@@ -56,12 +56,12 @@ class SessionSyncBridge : public AbstractSessionsSyncManager,
FaviconCache* GetFaviconCache() override;
SessionsGlobalIdMapper* GetGlobalIdMapper() override;
OpenTabsUIDelegate* GetOpenTabsUIDelegate() override;
- void OnSessionRestoreComplete() override;
syncer::SyncableService* GetSyncableService() override;
syncer::ModelTypeSyncBridge* GetModelTypeSyncBridge() override;
// ModelTypeSyncBridge implementation.
- void OnSyncStarting() override;
+ void OnSyncStarting(
+ const syncer::DataTypeActivationRequest& request) override;
std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
override;
base::Optional<syncer::ModelError> MergeSyncData(
@@ -71,10 +71,10 @@ class SessionSyncBridge : public AbstractSessionsSyncManager,
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_changes) override;
void GetData(StorageKeyList storage_keys, DataCallback callback) override;
- void GetAllData(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;
- DisableSyncResponse ApplyDisableSyncChanges(
+ StopSyncResponse ApplyStopSyncChanges(
std::unique_ptr<syncer::MetadataChangeList> delete_metadata_change_list)
override;
@@ -106,7 +106,6 @@ class SessionSyncBridge : public AbstractSessionsSyncManager,
FaviconCache favicon_cache_;
SessionsGlobalIdMapper global_id_mapper_;
SessionStore::Factory session_store_factory_;
- bool is_session_restore_in_progress_;
// All data dependent on sync being starting or started.
struct SyncingState {
@@ -129,6 +128,8 @@ class SessionSyncBridge : public AbstractSessionsSyncManager,
base::Optional<SyncingState> syncing_;
+ base::WeakPtrFactory<SessionSyncBridge> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(SessionSyncBridge);
};
diff --git a/chromium/components/sync_sessions/session_sync_bridge_unittest.cc b/chromium/components/sync_sessions/session_sync_bridge_unittest.cc
index 25902c79227..0e8b616041b 100644
--- a/chromium/components/sync_sessions/session_sync_bridge_unittest.cc
+++ b/chromium/components/sync_sessions/session_sync_bridge_unittest.cc
@@ -20,6 +20,7 @@
#include "components/sync/base/sync_prefs.h"
#include "components/sync/device_info/local_device_info_provider_mock.h"
#include "components/sync/model/data_batch.h"
+#include "components/sync/model/data_type_activation_request.h"
#include "components/sync/model/metadata_batch.h"
#include "components/sync/model/metadata_change_list.h"
#include "components/sync/model/mock_model_type_change_processor.h"
@@ -214,11 +215,16 @@ class SessionSyncBridgeTest : public ::testing::Test {
"cache_guid", "Wayne Gretzky's Hacking Box", "Chromium 10k",
"Chrome 10k", sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id"));
+ syncer::DataTypeActivationRequest request;
+ request.error_handler = base::DoNothing();
+ request.cache_guid = "TestCacheGuid";
+ request.authenticated_account_id = "SomeAccountId";
+
base::RunLoop loop;
real_processor_->OnSyncStarting(
- /*error_handler=*/base::DoNothing(),
+ request,
base::BindLambdaForTesting(
- [&loop](std::unique_ptr<syncer::ActivationContext>) {
+ [&loop](std::unique_ptr<syncer::DataTypeActivationResponse>) {
loop.Quit();
}));
loop.Run();
@@ -235,7 +241,7 @@ class SessionSyncBridgeTest : public ::testing::Test {
std::map<std::string, std::unique_ptr<EntityData>> GetAllData() {
base::RunLoop loop;
std::unique_ptr<DataBatch> batch;
- bridge_->GetAllData(base::BindLambdaForTesting(
+ bridge_->GetAllDataForDebugging(base::BindLambdaForTesting(
[&loop, &batch](std::unique_ptr<DataBatch> input_batch) {
batch = std::move(input_batch);
loop.Quit();
@@ -292,6 +298,8 @@ class SessionSyncBridgeTest : public ::testing::Test {
return tab;
}
+ void SessionRestoreComplete() { window_getter_.SessionRestoreComplete(); }
+
SessionSyncBridge* bridge() { return bridge_.get(); }
syncer::MockModelTypeChangeProcessor& mock_processor() {
@@ -363,7 +371,7 @@ TEST_F(SessionSyncBridgeTest, ShouldDeferLocalEventDueToSessionRestore) {
// OnSessionRestoreComplete() should issue three Put() calls, one updating the
// header and one for each of the two added tabs.
EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(3);
- bridge()->OnSessionRestoreComplete();
+ SessionRestoreComplete();
EXPECT_THAT(GetAllData(), SizeIs(3));
}
@@ -510,20 +518,17 @@ TEST_F(SessionSyncBridgeTest, ShouldReportLocalTabCreation) {
}
TEST_F(SessionSyncBridgeTest, ShouldUpdateIdsDuringRestore) {
- const int kWindowId = 1000001;
- const int kTabIdBeforeRestore1 = 1000002;
- const int kTabIdBeforeRestore2 = 1000003;
- const int kTabIdAfterRestore1 = 1000004;
- const int kTabIdAfterRestore2 = 1000005;
+ const int kWindowId1 = 1000001;
+ const int kWindowId2 = 1000002;
+ const int kTabId1 = 1000003;
+ const int kTabId2 = 1000004;
// Zero is the first assigned tab node ID.
const int kTabNodeId1 = 0;
const int kTabNodeId2 = 1;
- AddWindow(kWindowId);
- TestSyncedTabDelegate* tab1 =
- AddTab(kWindowId, "http://foo.com/", kTabIdBeforeRestore1);
- TestSyncedTabDelegate* tab2 =
- AddTab(kWindowId, "http://bar.com/", kTabIdBeforeRestore2);
+ AddWindow(kWindowId1);
+ AddTab(kWindowId1, "http://foo.com/", kTabId1);
+ AddTab(kWindowId1, "http://bar.com/", kTabId2);
const std::string header_storage_key =
SessionStore::GetHeaderStorageKey(kLocalSessionTag);
@@ -535,31 +540,28 @@ TEST_F(SessionSyncBridgeTest, ShouldUpdateIdsDuringRestore) {
InitializeBridge();
StartSyncing();
- ASSERT_THAT(tab1->GetSyncId(), Eq(kTabNodeId1));
- ASSERT_THAT(tab2->GetSyncId(), Eq(kTabNodeId2));
-
ASSERT_THAT(GetData(header_storage_key),
- EntityDataHasSpecifics(
- MatchesHeader(kLocalSessionTag, {kWindowId},
- {kTabIdBeforeRestore1, kTabIdBeforeRestore2})));
- ASSERT_THAT(GetData(tab_storage_key1),
- EntityDataHasSpecifics(
- MatchesTab(kLocalSessionTag, kWindowId, kTabIdBeforeRestore1,
- kTabNodeId1, {"http://foo.com/"})));
- ASSERT_THAT(GetData(tab_storage_key2),
- EntityDataHasSpecifics(
- MatchesTab(kLocalSessionTag, kWindowId, kTabIdBeforeRestore2,
- kTabNodeId2, {"http://bar.com/"})));
+ EntityDataHasSpecifics(MatchesHeader(
+ kLocalSessionTag, {kWindowId1}, {kTabId1, kTabId2})));
+ ASSERT_THAT(
+ GetData(tab_storage_key1),
+ EntityDataHasSpecifics(MatchesTab(kLocalSessionTag, kWindowId1, kTabId1,
+ kTabNodeId1, {"http://foo.com/"})));
+ ASSERT_THAT(
+ GetData(tab_storage_key2),
+ EntityDataHasSpecifics(MatchesTab(kLocalSessionTag, kWindowId1, kTabId2,
+ kTabNodeId2, {"http://bar.com/"})));
ShutdownBridge();
- // Override tabs with placeholder tab delegates.
+ // Override tabs with placeholder tab delegates. Note that, on Android, tab
+ // IDs are persisted by session restore across browser restarts.
PlaceholderTabDelegate placeholder_tab1(
- SessionID::FromSerializedValue(kTabIdAfterRestore1), tab1->GetSyncId());
+ SessionID::FromSerializedValue(kTabId1));
PlaceholderTabDelegate placeholder_tab2(
- SessionID::FromSerializedValue(kTabIdAfterRestore2), tab2->GetSyncId());
+ SessionID::FromSerializedValue(kTabId2));
ResetWindows();
- TestSyncedWindowDelegate* window = AddWindow(kWindowId);
+ TestSyncedWindowDelegate* window = AddWindow(kWindowId2);
window->OverrideTabAt(0, &placeholder_tab1);
window->OverrideTabAt(1, &placeholder_tab2);
@@ -568,74 +570,62 @@ TEST_F(SessionSyncBridgeTest, ShouldUpdateIdsDuringRestore) {
EXPECT_CALL(mock_processor(),
Put(header_storage_key,
EntityDataHasSpecifics(MatchesHeader(
- kLocalSessionTag, {kWindowId},
- {kTabIdAfterRestore1, kTabIdAfterRestore2})),
- _));
- EXPECT_CALL(mock_processor(),
- Put(tab_storage_key1,
- EntityDataHasSpecifics(MatchesTab(
- kLocalSessionTag, kWindowId, kTabIdAfterRestore1,
- kTabNodeId1, {"http://foo.com/"})),
- _));
- EXPECT_CALL(mock_processor(),
- Put(tab_storage_key2,
- EntityDataHasSpecifics(MatchesTab(
- kLocalSessionTag, kWindowId, kTabIdAfterRestore2,
- kTabNodeId2, {"http://bar.com/"})),
+ kLocalSessionTag, {kWindowId2}, {kTabId1, kTabId2})),
_));
+ EXPECT_CALL(mock_processor(), Put(tab_storage_key1,
+ EntityDataHasSpecifics(MatchesTab(
+ kLocalSessionTag, kWindowId2, kTabId1,
+ kTabNodeId1, {"http://foo.com/"})),
+ _));
+ EXPECT_CALL(mock_processor(), Put(tab_storage_key2,
+ EntityDataHasSpecifics(MatchesTab(
+ kLocalSessionTag, kWindowId2, kTabId2,
+ kTabNodeId2, {"http://bar.com/"})),
+ _));
// Start the bridge again.
InitializeBridge();
StartSyncing();
- EXPECT_THAT(placeholder_tab1.GetSyncId(), Eq(kTabNodeId1));
- EXPECT_THAT(placeholder_tab2.GetSyncId(), Eq(kTabNodeId2));
-
EXPECT_THAT(GetData(header_storage_key),
- EntityDataHasSpecifics(
- MatchesHeader(kLocalSessionTag, {kWindowId},
- {kTabIdAfterRestore1, kTabIdAfterRestore2})));
- EXPECT_THAT(GetData(tab_storage_key1),
- EntityDataHasSpecifics(
- MatchesTab(kLocalSessionTag, kWindowId, kTabIdAfterRestore1,
- kTabNodeId1, {"http://foo.com/"})));
- EXPECT_THAT(GetData(tab_storage_key2),
- EntityDataHasSpecifics(
- MatchesTab(kLocalSessionTag, kWindowId, kTabIdAfterRestore2,
- kTabNodeId2, {"http://bar.com/"})));
+ EntityDataHasSpecifics(MatchesHeader(
+ kLocalSessionTag, {kWindowId2}, {kTabId1, kTabId2})));
+ EXPECT_THAT(
+ GetData(tab_storage_key1),
+ EntityDataHasSpecifics(MatchesTab(kLocalSessionTag, kWindowId2, kTabId1,
+ kTabNodeId1, {"http://foo.com/"})));
+ EXPECT_THAT(
+ GetData(tab_storage_key2),
+ EntityDataHasSpecifics(MatchesTab(kLocalSessionTag, kWindowId2, kTabId2,
+ kTabNodeId2, {"http://bar.com/"})));
- EXPECT_THAT(GetAllData(),
- UnorderedElementsAre(
- Pair(header_storage_key,
- EntityDataHasSpecifics(MatchesHeader(
- kLocalSessionTag, {kWindowId},
- {kTabIdAfterRestore1, kTabIdAfterRestore2}))),
- Pair(tab_storage_key1,
- EntityDataHasSpecifics(MatchesTab(
- kLocalSessionTag, kWindowId, kTabIdAfterRestore1,
- kTabNodeId1, {"http://foo.com/"}))),
- Pair(tab_storage_key2,
- EntityDataHasSpecifics(MatchesTab(
- kLocalSessionTag, kWindowId, kTabIdAfterRestore2,
- kTabNodeId2, {"http://bar.com/"})))));
+ EXPECT_THAT(
+ GetAllData(),
+ UnorderedElementsAre(
+ Pair(header_storage_key,
+ EntityDataHasSpecifics(MatchesHeader(
+ kLocalSessionTag, {kWindowId2}, {kTabId1, kTabId2}))),
+ Pair(tab_storage_key1, EntityDataHasSpecifics(MatchesTab(
+ kLocalSessionTag, kWindowId2, kTabId1,
+ kTabNodeId1, {"http://foo.com/"}))),
+ Pair(tab_storage_key2, EntityDataHasSpecifics(MatchesTab(
+ kLocalSessionTag, kWindowId2, kTabId2,
+ kTabNodeId2, {"http://bar.com/"})))));
}
TEST_F(SessionSyncBridgeTest,
ShouldIgnoreUnsyncablePlaceholderTabDuringRestore) {
- const int kWindowId = 1000001;
- const int kTabIdBeforeRestore1 = 1000002;
- const int kTabIdBeforeRestore2 = 1000003;
- const int kTabIdAfterRestore1 = 1000004;
- const int kTabIdAfterRestore2 = 1000005;
+ const int kWindowId1 = 1000001;
+ const int kWindowId2 = 1000002;
+ const int kTabId1 = 1000002;
+ const int kTabId2 = 1000003;
// Zero is the first assigned tab node ID.
const int kTabNodeId1 = 0;
- AddWindow(kWindowId);
- TestSyncedTabDelegate* tab1 =
- AddTab(kWindowId, "http://foo.com/", kTabIdBeforeRestore1);
+ AddWindow(kWindowId1);
+ AddTab(kWindowId1, "http://foo.com/", kTabId1);
// Tab 2 is unsyncable because of the URL scheme.
- TestSyncedTabDelegate* tab2 =
- AddTab(kWindowId, "about:blank", kTabIdBeforeRestore2);
+ AddTab(kWindowId1, "about:blank", kTabId2);
const std::string header_storage_key =
SessionStore::GetHeaderStorageKey(kLocalSessionTag);
@@ -645,28 +635,26 @@ TEST_F(SessionSyncBridgeTest,
InitializeBridge();
StartSyncing();
- ASSERT_THAT(tab1->GetSyncId(), Eq(kTabNodeId1));
- ASSERT_THAT(tab2->GetSyncId(), Eq(TabNodePool::kInvalidTabNodeID));
-
- ASSERT_THAT(GetAllData(),
- UnorderedElementsAre(
- Pair(header_storage_key, EntityDataHasSpecifics(MatchesHeader(
- kLocalSessionTag, {kWindowId},
- {kTabIdBeforeRestore1}))),
- Pair(tab_storage_key1,
- EntityDataHasSpecifics(MatchesTab(
- kLocalSessionTag, kWindowId, kTabIdBeforeRestore1,
- kTabNodeId1, {"http://foo.com/"})))));
+ ASSERT_THAT(
+ GetAllData(),
+ UnorderedElementsAre(
+ Pair(header_storage_key,
+ EntityDataHasSpecifics(
+ MatchesHeader(kLocalSessionTag, {kWindowId1}, {kTabId1}))),
+ Pair(tab_storage_key1, EntityDataHasSpecifics(MatchesTab(
+ kLocalSessionTag, kWindowId1, kTabId1,
+ kTabNodeId1, {"http://foo.com/"})))));
ShutdownBridge();
- // Override tabs with placeholder tab delegates.
+ // Override tabs with placeholder tab delegates. Note that, on Android, tab
+ // IDs are persisted by session restore across browser restarts.
PlaceholderTabDelegate placeholder_tab1(
- SessionID::FromSerializedValue(kTabIdAfterRestore1), tab1->GetSyncId());
+ SessionID::FromSerializedValue(kTabId1));
PlaceholderTabDelegate placeholder_tab2(
- SessionID::FromSerializedValue(kTabIdAfterRestore2), tab2->GetSyncId());
+ SessionID::FromSerializedValue(kTabId2));
ResetWindows();
- TestSyncedWindowDelegate* window = AddWindow(kWindowId);
+ TestSyncedWindowDelegate* window = AddWindow(kWindowId2);
window->OverrideTabAt(0, &placeholder_tab1);
window->OverrideTabAt(1, &placeholder_tab2);
@@ -674,28 +662,26 @@ TEST_F(SessionSyncBridgeTest,
InitializeBridge();
StartSyncing();
- EXPECT_THAT(placeholder_tab1.GetSyncId(), Eq(kTabNodeId1));
- EXPECT_THAT(placeholder_tab2.GetSyncId(), Eq(TabNodePool::kInvalidTabNodeID));
-
- EXPECT_THAT(GetAllData(),
- UnorderedElementsAre(
- Pair(header_storage_key, EntityDataHasSpecifics(MatchesHeader(
- kLocalSessionTag, {kWindowId},
- {kTabIdAfterRestore1}))),
- Pair(tab_storage_key1,
- EntityDataHasSpecifics(MatchesTab(
- kLocalSessionTag, kWindowId, kTabIdAfterRestore1,
- kTabNodeId1, {"http://foo.com/"})))));
+ EXPECT_THAT(
+ GetAllData(),
+ UnorderedElementsAre(
+ Pair(header_storage_key,
+ EntityDataHasSpecifics(
+ MatchesHeader(kLocalSessionTag, {kWindowId2}, {kTabId1}))),
+ Pair(tab_storage_key1, EntityDataHasSpecifics(MatchesTab(
+ kLocalSessionTag, kWindowId2, kTabId1,
+ kTabNodeId1, {"http://foo.com/"})))));
}
// Ensure that tabbed windows from a previous session are preserved if no
// windows are present on startup.
TEST_F(SessionSyncBridgeTest, ShouldRestoreTabbedDataIfNoWindowsDuringStartup) {
- const int kWindowId = 1000001;
+ const int kWindowId1 = 1000001;
+ const int kWindowId2 = 1000002;
const int kTabNodeId = 0;
- AddWindow(kWindowId);
- TestSyncedTabDelegate* tab = AddTab(kWindowId, "http://foo.com/");
+ AddWindow(kWindowId1);
+ TestSyncedTabDelegate* tab = AddTab(kWindowId1, "http://foo.com/");
const std::string header_storage_key =
SessionStore::GetHeaderStorageKey(kLocalSessionTag);
@@ -742,7 +728,7 @@ TEST_F(SessionSyncBridgeTest, ShouldRestoreTabbedDataIfNoWindowsDuringStartup) {
kLocalSessionTag, /*window_id=*/_, /*tab_id=*/_,
kTabNodeId, {"http://foo.com/", "http://bar.com/"})),
_));
- AddWindow(kWindowId)->OverrideTabAt(0, tab);
+ AddWindow(kWindowId2)->OverrideTabAt(0, tab);
tab->Navigate("http://bar.com/");
}
@@ -775,17 +761,18 @@ TEST_F(SessionSyncBridgeTest, ShouldPreserveTabbedDataIfCustomTabOnlyFound) {
InitializeBridge();
StartSyncing();
- // The previous session should be preserved. The transient window cannot be
- // synced because we do not have enough local data to ensure that we wouldn't
- // vend the same sync ID if our persistent storage didn't match upon the last
- // shutdown.
- EXPECT_THAT(GetAllData(),
- UnorderedElementsAre(
- Pair(_, EntityDataHasSpecifics(
- MatchesHeader(kLocalSessionTag, _, _))),
- Pair(_, EntityDataHasSpecifics(MatchesTab(
- kLocalSessionTag, _, _,
- /*tab_node_id=*/0, {"http://foo.com/"})))));
+ // The previous session should be preserved, together with the new custom tab.
+ EXPECT_THAT(
+ GetAllData(),
+ UnorderedElementsAre(
+ Pair(_,
+ EntityDataHasSpecifics(MatchesHeader(kLocalSessionTag, _, _))),
+ Pair(_, EntityDataHasSpecifics(MatchesTab(kLocalSessionTag, _, _,
+ /*tab_node_id=*/0,
+ {"http://foo.com/"}))),
+ Pair(_, EntityDataHasSpecifics(MatchesTab(kLocalSessionTag, _, _,
+ /*tab_node_id=*/1,
+ {"http://bar.com/"})))));
}
// Ensure that tabbed windows from a previous session are preserved and combined
@@ -832,19 +819,24 @@ TEST_F(SessionSyncBridgeTest, ShouldPreserveTabbedDataIfNewCustomTabAlsoFound) {
}
// Ensure that, in a scenario without prior sync data, encountering a custom
-// tab only ( no tabbed window) does not vend new sync IDs.
-TEST_F(SessionSyncBridgeTest, ShouldIgnoreIfCustomTabOnlyOnStartup) {
+// tab only (no tabbed window) starts syncing that tab.
+TEST_F(SessionSyncBridgeTest, ShouldAssociateIfCustomTabOnlyOnStartup) {
const int kWindowId = 1000001;
+ const int kTabId = 1000002;
AddWindow(kWindowId, sync_pb::SessionWindow_BrowserType_TYPE_CUSTOM_TAB);
- AddTab(kWindowId, "http://foo.com/");
+ AddTab(kWindowId, "http://foo.com/", kTabId);
InitializeBridge();
StartSyncing();
EXPECT_THAT(GetAllData(),
- UnorderedElementsAre(Pair(_, EntityDataHasSpecifics(MatchesHeader(
- kLocalSessionTag, _, _)))));
+ UnorderedElementsAre(
+ Pair(_, EntityDataHasSpecifics(MatchesHeader(
+ kLocalSessionTag, {kWindowId}, {kTabId}))),
+ Pair(_, EntityDataHasSpecifics(MatchesTab(
+ kLocalSessionTag, kWindowId, kTabId,
+ /*tab_node_id=*/0, {"http://foo.com/"})))));
}
// Ensure that all tabs are exposed in a scenario where only a custom tab
@@ -857,123 +849,35 @@ TEST_F(SessionSyncBridgeTest, ShouldExposeTabbedWindowAfterCustomTabOnly) {
const int kTabId2 = 1000004;
AddWindow(kWindowId1, sync_pb::SessionWindow_BrowserType_TYPE_CUSTOM_TAB);
- TestSyncedTabDelegate* custom_tab =
- AddTab(kWindowId1, "http://foo.com/", kTabId1);
+ AddTab(kWindowId1, "http://foo.com/", kTabId1);
InitializeBridge();
StartSyncing();
ASSERT_THAT(GetAllData(),
- UnorderedElementsAre(Pair(_, EntityDataHasSpecifics(MatchesHeader(
- kLocalSessionTag, _, _)))));
+ UnorderedElementsAre(
+ Pair(_, EntityDataHasSpecifics(MatchesHeader(
+ kLocalSessionTag, {kWindowId1}, {kTabId1}))),
+ Pair(_, EntityDataHasSpecifics(MatchesTab(
+ kLocalSessionTag, kWindowId1, kTabId1,
+ /*tab_node_id=*/0, {"http://foo.com/"})))));
// Load the actual tabbed window, now that we're syncing.
AddWindow(kWindowId2);
AddTab(kWindowId2, "http://bar.com/", kTabId2);
- // The local change should be created and tracked correctly. This doesn't
- // actually start syncing the custom tab yet, because the tab itself isn't
- // associated yet.
- EXPECT_THAT(GetAllData(),
- UnorderedElementsAre(
- Pair(_, EntityDataHasSpecifics(MatchesHeader(
- kLocalSessionTag, {kWindowId2}, {kTabId2}))),
- Pair(_, EntityDataHasSpecifics(MatchesTab(
- kLocalSessionTag, kWindowId2, kTabId2,
- /*tab_node_id=*/0, {"http://bar.com/"})))));
-
- // Now trigger OnLocalTabModified() for the custom tab again, it should sync.
- custom_tab->Navigate("http://baz.com/");
+ // The local change should be created and tracked correctly.
EXPECT_THAT(GetAllData(),
UnorderedElementsAre(
Pair(_, EntityDataHasSpecifics(MatchesHeader(
kLocalSessionTag, {kWindowId1, kWindowId2},
{kTabId1, kTabId2}))),
Pair(_, EntityDataHasSpecifics(MatchesTab(
- kLocalSessionTag, kWindowId2, kTabId2,
- /*tab_node_id=*/0, {"http://bar.com/"}))),
- Pair(_, EntityDataHasSpecifics(MatchesTab(
kLocalSessionTag, kWindowId1, kTabId1,
- /*tab_node_id=*/1,
- {"http://foo.com/", "http://baz.com/"})))));
-}
-
-// Ensure that newly assigned tab node IDs do not conflict with IDs provided
-// by the delegate, for IDs the tracker might not know about. This is possible
-// for example if an Android client gets killed after Android's tab restore
-// has flushed the sync ID to disk, but before the sync database has been
-// written.
-TEST_F(SessionSyncBridgeTest, ShouldHonorUnknownSyncIdsFromDelegate) {
- const int kWindowId = 1000001;
- const int kTabNodeId = 0;
-
- // In a previous run of a browser, we associated sync ID 0 to the second tab
- // below, which the Android tab restore database succeeded to flush to disk.
- // The sync_sessions counterpart (SessionStore) however didn't, because writes
- // are not atomic.
- AddWindow(kWindowId);
- AddTab(kWindowId, "http://foo.com/");
- AddTab(kWindowId, "http://bar.com/")->SetSyncId(kTabNodeId);
-
- InitializeBridge();
- StartSyncing();
-
- EXPECT_THAT(
- GetAllData(),
- UnorderedElementsAre(
- Pair(_,
- EntityDataHasSpecifics(MatchesHeader(kLocalSessionTag, _, _))),
- Pair(_,
- EntityDataHasSpecifics(MatchesTab(
- kLocalSessionTag, _, _, kTabNodeId, {"http://bar.com/"}))),
- Pair(_, EntityDataHasSpecifics(MatchesTab(kLocalSessionTag, _, _,
- /*tab_node_id=*/1,
- {"http://foo.com/"})))));
-}
-
-// Ensure that unsyncable tabs are ignored even if the delegate reports a sync
-// ID (because the tab used to be syncable).
-TEST_F(SessionSyncBridgeTest,
- ShouldHonorUnknownSyncIdsFromDelegateWithUnsyncableTab) {
- const int kWindowId = 1000001;
- const int kTabId = 1000002;
- const int kTabNodeId = 0;
-
- AddWindow(kWindowId);
- AddTab(kWindowId, "about:blank", kTabId)->SetSyncId(kTabNodeId);
-
- InitializeBridge();
-
- EXPECT_CALL(mock_processor(),
- Put(_,
- EntityDataHasSpecifics(
- MatchesHeader(kLocalSessionTag, {kWindowId}, {kTabId})),
- _));
-
- StartSyncing();
-
- // As a regression test for crbug.com/846480, we verify that restarting the
- // bridge without tabbed windows doesn't crash or issue more calls to Put().
- // This is only problematic because, in the current implementation and as
- // reflected in the assertion below, the unsyncable tab is still stored in
- // the local model.
- ASSERT_THAT(GetAllData(),
- UnorderedElementsAre(
- Pair(_, EntityDataHasSpecifics(MatchesHeader(
- kLocalSessionTag, {kWindowId}, {kTabId}))),
- Pair(_, EntityDataHasSpecifics(
- MatchesTab(kLocalSessionTag, kWindowId, kTabId,
- kTabNodeId, /*urls=*/{})))));
-
- ShutdownBridge();
- ResetWindows();
- InitializeBridge();
- StartSyncing();
- EXPECT_THAT(
- GetAllData(),
- ElementsAre(Pair(_, EntityDataHasSpecifics(MatchesHeader(
- kLocalSessionTag, /*window_ids=*/SizeIs(1),
- /*tab_ids=*/SizeIs(1))))));
+ /*tab_node_id=*/0, {"http://foo.com/"}))),
+ Pair(_, EntityDataHasSpecifics(MatchesTab(
+ kLocalSessionTag, kWindowId2, kTabId2,
+ /*tab_node_id=*/1, {"http://bar.com/"})))));
}
TEST_F(SessionSyncBridgeTest, ShouldDisableSyncAndReenable) {
@@ -994,7 +898,7 @@ TEST_F(SessionSyncBridgeTest, ShouldDisableSyncAndReenable) {
ASSERT_THAT(GetAllData(), Not(IsEmpty()));
EXPECT_CALL(mock_processor(), ModelReadyToSync(_)).Times(0);
- real_processor()->DisableSync();
+ real_processor()->OnSyncStopping(syncer::CLEAR_METADATA);
EXPECT_CALL(mock_processor(), ModelReadyToSync(IsEmptyMetadataBatch()));
StartSyncing();
@@ -1376,6 +1280,20 @@ TEST_F(SessionSyncBridgeTest, ShouldIgnoreLocalSessionDeletionFromUI) {
NotNull());
}
+// Verifies that receiving an empty update list does not broadcast a foreign
+// session change via the corresponding callback.
+TEST_F(SessionSyncBridgeTest, ShouldNotBroadcastUpdatesIfEmpty) {
+ InitializeBridge();
+ StartSyncing();
+
+ EXPECT_CALL(mock_foreign_sessions_updated_callback(), Run()).Times(0);
+
+ // Mimic receiving an empty list of remote updates.
+ sync_pb::ModelTypeState state;
+ state.set_initial_sync_done(true);
+ real_processor()->OnUpdateReceived(state, {});
+}
+
TEST_F(SessionSyncBridgeTest, ShouldDoGarbageCollection) {
// We construct two identical sessions, one modified recently, one modified
// more than |kStaleSessionThreshold| ago (14 days ago).
diff --git a/chromium/components/sync_sessions/sessions_sync_manager.cc b/chromium/components/sync_sessions/sessions_sync_manager.cc
index e3c428a0adb..c5811b3787d 100644
--- a/chromium/components/sync_sessions/sessions_sync_manager.cc
+++ b/chromium/components/sync_sessions/sessions_sync_manager.cc
@@ -12,7 +12,9 @@
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_macros.h"
+#include "base/sequenced_task_runner.h"
#include "base/strings/stringprintf.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "build/build_config.h"
#include "components/sync/base/hash_util.h"
#include "components/sync/device_info/local_device_info_provider.h"
@@ -62,7 +64,7 @@ sync_pb::SessionSpecifics SessionTabToSpecifics(
const std::string& local_tag,
int tab_node_id) {
sync_pb::SessionSpecifics specifics;
- session_tab.ToSyncData().Swap(specifics.mutable_tab());
+ SessionTabToSyncData(session_tab).Swap(specifics.mutable_tab());
specifics.set_session_tag(local_tag);
specifics.set_tab_node_id(tab_node_id);
return specifics;
@@ -95,11 +97,6 @@ class SyncChangeListWriteBatch
syncer::SyncChangeList* sync_change_list() { return &changes_; }
- void AddKnownTabNodeIds(const std::set<int>& known_tab_node_ids) {
- known_tab_node_ids_.insert(known_tab_node_ids.begin(),
- known_tab_node_ids.end());
- }
-
void PutWithType(std::unique_ptr<sync_pb::SessionSpecifics> specifics,
SyncChange::SyncChangeType change_type) {
sync_pb::EntitySpecifics entity_specifics;
@@ -174,16 +171,11 @@ static std::string BuildMachineTag(const std::string& cache_guid) {
}
void SessionsSyncManager::ScheduleGarbageCollection() {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&SessionsSyncManager::DoGarbageCollection,
base::AsWeakPtr(this)));
}
-void SessionsSyncManager::OnSessionRestoreComplete() {
- // Do nothing. The very same event is plumbed manually to
- // SessionDataTypeController by ProfileSyncComponentsFactoryImpl.
-}
-
syncer::SyncableService* SessionsSyncManager::GetSyncableService() {
return this;
}
@@ -253,9 +245,6 @@ syncer::SyncMergeResult SessionsSyncManager::MergeDataAndStartSyncing(
batch.PutWithType(std::move(specifics), SyncChange::ACTION_ADD);
}
- batch.AddKnownTabNodeIds(
- session_tracker_.LookupTabNodeIds(current_machine_tag()));
-
#if defined(OS_ANDROID)
std::string sync_machine_tag(
BuildMachineTag(local_device_->GetLocalSyncCacheGUID()));
@@ -263,14 +252,15 @@ syncer::SyncMergeResult SessionsSyncManager::MergeDataAndStartSyncing(
DeleteForeignSessionInternal(sync_machine_tag, batch.sync_change_list());
#endif
- // Check if anything has changed on the local client side.
- local_session_event_handler_ = std::make_unique<LocalSessionEventHandlerImpl>(
- /*delegate=*/this, sessions_client_, &session_tracker_, &batch);
- local_tab_pool_out_of_sync_ = false;
-
merge_result.set_error(sync_processor_->ProcessSyncChanges(
FROM_HERE, *batch.sync_change_list()));
+ local_tab_pool_out_of_sync_ = false;
+
+ // Check if anything has changed on the local client side.
+ local_session_event_handler_ = std::make_unique<LocalSessionEventHandlerImpl>(
+ /*delegate=*/this, sessions_client_, &session_tracker_);
+
sessions_client_->GetLocalSessionEventRouter()->StartRoutingTo(
local_session_event_handler_.get());
return merge_result;
@@ -429,12 +419,6 @@ syncer::SyncChange SessionsSyncManager::TombstoneTab(
bool SessionsSyncManager::InitFromSyncModel(
const syncer::SyncDataList& sync_data,
syncer::SyncChangeList* new_changes) {
- // Map of all rewritten local ids. Because ids are reset on each restart,
- // and id generation happens outside of Sync, all ids from a previous local
- // session must be rewritten in order to be valid.
- // Key: previous session id. Value: new session id.
- std::map<int32_t, SessionID> session_id_map;
-
bool found_current_header = false;
int bad_foreign_hash_count = 0;
for (syncer::SyncDataList::const_iterator it = sync_data.begin();
@@ -476,37 +460,9 @@ bool SessionsSyncManager::InitFromSyncModel(
// This is our previous header node, reuse it.
found_current_header = true;
- // The specifics from the SyncData are immutable. Create a mutable copy
- // to hold the rewritten ids.
- sync_pb::SessionSpecifics rewritten_specifics(specifics);
-
- // Go through and generate new tab and window ids as necessary, updating
- // the specifics in place.
- for (auto& window :
- *rewritten_specifics.mutable_header()->mutable_window()) {
- session_id_map.emplace(window.window_id(), SessionID::NewUnique());
- window.set_window_id(session_id_map.at(window.window_id()).id());
-
- google::protobuf::RepeatedField<int>* tab_ids = window.mutable_tab();
- for (int i = 0; i < tab_ids->size(); i++) {
- auto tab_iter = session_id_map.find(tab_ids->Get(i));
- if (tab_iter == session_id_map.end()) {
- // SessionID::SessionID() automatically increments a static
- // variable, forcing a new id to be generated each time.
- session_id_map.emplace(tab_ids->Get(i), SessionID::NewUnique());
- }
- *(tab_ids->Mutable(i)) = session_id_map.at(tab_ids->Get(i)).id();
- // Note: the tab id of the SessionTab will be updated when the tab
- // node itself is processed.
- }
- }
-
- UpdateTrackerWithSpecifics(rewritten_specifics,
- remote.GetModifiedTime(), &session_tracker_);
-
- DVLOG(1) << "Loaded local header and rewrote " << session_id_map.size()
- << " ids.";
-
+ UpdateTrackerWithSpecifics(specifics, remote.GetModifiedTime(),
+ &session_tracker_);
+ DVLOG(1) << "Loaded local header.";
} else {
if (specifics.has_header() || !specifics.has_tab()) {
LOG(WARNING) << "Found more than one session header node with local "
@@ -525,26 +481,11 @@ bool SessionsSyncManager::InitFromSyncModel(
DVLOG(1) << "Associating local tab " << specifics.tab().tab_id()
<< " with node " << specifics.tab_node_id();
- // Now file the tab under the new tab id.
- SessionID new_tab_id = SessionID::InvalidValue();
- auto iter = session_id_map.find(specifics.tab().tab_id());
- if (iter != session_id_map.end()) {
- new_tab_id = iter->second;
- } else {
- new_tab_id = SessionID::NewUnique();
- session_id_map.emplace(specifics.tab().tab_id(), new_tab_id);
- }
- DVLOG(1) << "Remapping tab " << specifics.tab().tab_id() << " to "
- << new_tab_id;
-
- // The specifics from the SyncData are immutable. Create a mutable
- // copy to hold the rewritten ids.
- sync_pb::SessionSpecifics rewritten_specifics(specifics);
- rewritten_specifics.mutable_tab()->set_tab_id(new_tab_id.id());
session_tracker_.ReassociateLocalTab(
- rewritten_specifics.tab_node_id(), new_tab_id);
- UpdateTrackerWithSpecifics(
- rewritten_specifics, remote.GetModifiedTime(), &session_tracker_);
+ specifics.tab_node_id(),
+ SessionID::FromSerializedValue(specifics.tab().tab_id()));
+ UpdateTrackerWithSpecifics(specifics, remote.GetModifiedTime(),
+ &session_tracker_);
}
}
}
diff --git a/chromium/components/sync_sessions/sessions_sync_manager.h b/chromium/components/sync_sessions/sessions_sync_manager.h
index 106104f978a..26344b99f96 100644
--- a/chromium/components/sync_sessions/sessions_sync_manager.h
+++ b/chromium/components/sync_sessions/sessions_sync_manager.h
@@ -66,7 +66,6 @@ class SessionsSyncManager : public AbstractSessionsSyncManager,
FaviconCache* GetFaviconCache() override;
SessionsGlobalIdMapper* GetGlobalIdMapper() override;
OpenTabsUIDelegate* GetOpenTabsUIDelegate() override;
- void OnSessionRestoreComplete() override;
syncer::SyncableService* GetSyncableService() override;
syncer::ModelTypeSyncBridge* GetModelTypeSyncBridge() override;
diff --git a/chromium/components/sync_sessions/sessions_sync_manager_unittest.cc b/chromium/components/sync_sessions/sessions_sync_manager_unittest.cc
index 4c788a67550..6481cb12944 100644
--- a/chromium/components/sync_sessions/sessions_sync_manager_unittest.cc
+++ b/chromium/components/sync_sessions/sessions_sync_manager_unittest.cc
@@ -43,7 +43,6 @@ const char kFoo2[] = "http://foo2/";
const char kBar1[] = "http://bar1/";
const char kBar2[] = "http://bar2/";
const char kBaz1[] = "http://baz1/";
-const char kBaz2[] = "http://baz2/";
const char kTag1[] = "tag1";
const char kTag2[] = "tag2";
@@ -444,21 +443,14 @@ TEST_F(SessionsSyncManagerTest, PreserveTabbedDataNoWindows) {
ResetWindows();
InitWithSyncDataTakeOutput(ConvertToRemote(in), &out);
- // There should be two changes: the rewritten tab (to update the tab id), and
- // the rewritten header.
- ASSERT_TRUE(ChangeTypeMatches(
- out, {SyncChange::ACTION_UPDATE, SyncChange::ACTION_UPDATE}));
- VerifyLocalTabChange(out[0], 2, kFoo2);
- VerifyLocalHeaderChange(out[1], 1, 1);
+ // There should be one change to the rewritten header.
+ ASSERT_TRUE(ChangeTypeMatches(out, {SyncChange::ACTION_UPDATE}));
+ VerifyLocalHeaderChange(out[0], 1, 1);
- // Verify the tab id of the restored tab is updated and consistent.
+ // SessionId should not be rewritten on restore.
int restored_tab_id =
- out[0].sync_data().GetSpecifics().session().tab().tab_id();
- // SessionId should be rewritten on restore.
- ASSERT_NE(tab->GetSessionId().id(), restored_tab_id);
- ASSERT_EQ(
- restored_tab_id,
- out[1].sync_data().GetSpecifics().session().header().window(0).tab(0));
+ out[0].sync_data().GetSpecifics().session().header().window(0).tab(0);
+ EXPECT_EQ(tab->GetSessionId().id(), restored_tab_id);
out.clear();
// Now actually resurrect the native data, which will end up having different
@@ -493,118 +485,14 @@ TEST_F(SessionsSyncManagerTest, PreserveTabbedDataCustomTab) {
manager()->StopSyncing(syncer::SESSIONS);
ResetWindows();
window = AddWindow(sync_pb::SessionWindow_BrowserType_TYPE_CUSTOM_TAB);
- TestSyncedTabDelegate* custom_tab = AddTab(window->GetSessionId(), kBar1);
+ AddTab(window->GetSessionId(), kBar1);
InitWithSyncDataTakeOutput(ConvertToRemote(in), &out);
- // The previous session should be preserved. The transient window cannot be
- // synced because we do not have enough local data to ensure that we wouldn't
- // vend the same sync id if our persistent storage didn't match upon the last
- // shutdown.
- ASSERT_TRUE(ChangeTypeMatches(
- out, {SyncChange::ACTION_UPDATE, SyncChange::ACTION_UPDATE}));
- VerifyLocalTabChange(out[0], 2, kFoo2);
- VerifyLocalHeaderChange(out[1], 1, 1);
- out.clear();
-
- // Now re-create local data and modify it.
- TestSyncedWindowDelegate* alive_again = AddWindow();
- alive_again->OverrideTabAt(0, tab);
- tab->Navigate(kBaz1);
-
- // The local change should be created and tracked correctly. This doesn't
- // actually start syncing the custom tab yet, because the tab itself isn't
- // associated yet.
- ASSERT_TRUE(ChangeTypeMatches(
- out, {SyncChange::ACTION_UPDATE, SyncChange::ACTION_UPDATE}));
- VerifyLocalTabChange(out[0], 3, kBaz1);
- VerifyLocalHeaderChange(out[1], 1, 1);
- out.clear();
-
- // Now trigger OnLocalTabModified() for the custom tab again, it should sync.
- custom_tab->Navigate(kBar2);
+ // The previous session should be preserved, together with the new custom tab.
ASSERT_TRUE(ChangeTypeMatches(
out, {SyncChange::ACTION_ADD, SyncChange::ACTION_UPDATE}));
- VerifyLocalTabChange(out[0], 2, kBar2);
- VerifyLocalHeaderChange(out[1], 2, 2);
-}
-
-// Create a placeholder and a non-placeholder that have the same sync ids. Only
-// the non-placeholder should survive. This state should be impossible for up
-// to date clients to enter.
-TEST_F(SessionsSyncManagerTest, ConflictingSyncIdsWithPlaceholder) {
- syncer::SyncDataList in;
- syncer::SyncChangeList out;
-
- // First sync with one tab and one window.
- TestSyncedWindowDelegate* window = AddWindow();
- AddTab(window->GetSessionId(), kFoo1);
- InitWithSyncDataTakeOutput(in, &out);
-
- in = GetDataFromChanges(out);
- int conflicting_sync_id =
- out[1].sync_data().GetSpecifics().session().tab_node_id();
- out.clear();
- // There should be two entities, a header and a tab.
- ASSERT_EQ(2U, in.size());
-
- manager()->StopSyncing(syncer::SESSIONS);
-
- // The main window's tab is now a placeholder, and we have a conflicting id
- // for the custom tab. They should both have their tab ids reset, but the
- // placeholder cannot be fixed, and will be dropped. Only the custom tab will
- // show up now.
- PlaceholderTabDelegate tab2(SessionID::NewUnique(), conflicting_sync_id);
- window->OverrideTabAt(0, &tab2);
-
- TestSyncedWindowDelegate* window2 =
- AddWindow(sync_pb::SessionWindow_BrowserType_TYPE_CUSTOM_TAB);
- TestSyncedTabDelegate* tab3 = AddTab(window2->GetSessionId(), kBar1);
- tab3->SetSyncId(conflicting_sync_id);
- window2->OverrideTabAt(0, tab3);
-
- InitWithSyncDataTakeOutput(ConvertToRemote(in), &out);
-
- ASSERT_TRUE(ChangeTypeMatches(
- out, {SyncChange::ACTION_UPDATE, SyncChange::ACTION_UPDATE}));
VerifyLocalTabChange(out[0], 1, kBar1);
- VerifyLocalHeaderChange(out[1], 1, 1);
-}
-
-// Create two tabs with the same sync id, which is an invalid state. The manager
-// should strip both of the sync ids, and then new ones should be generated.
-// This state should be impossible for up to date clients to enter.
-TEST_F(SessionsSyncManagerTest, ConflictingSyncIdsBothReal) {
- syncer::SyncDataList in;
- syncer::SyncChangeList out;
-
- TestSyncedWindowDelegate* window = AddWindow();
- TestSyncedTabDelegate* tab1 = AddTab(window->GetSessionId(), kFoo1);
- TestSyncedTabDelegate* tab2 = AddTab(window->GetSessionId(), kBar1);
-
- // The pool wants to start vending numbers 0, 1, 2, ... etc. So we're
- // guaranteed that when the manager clears the sync ids, it will be replaced
- // with smaller values.
- int dupe_sync_id = 13;
- tab1->SetSyncId(dupe_sync_id);
- tab2->SetSyncId(dupe_sync_id);
-
- InitWithSyncDataTakeOutput(in, &out);
- // Header creation, two tab creations, and header update.
- ASSERT_TRUE(ChangeTypeMatches(
- out, {SyncChange::ACTION_ADD, SyncChange::ACTION_ADD,
- SyncChange::ACTION_ADD, SyncChange::ACTION_UPDATE}));
- VerifyLocalHeaderChange(out[0], 0, 0);
- VerifyLocalTabChange(out[1], 1, kFoo1);
- VerifyLocalTabChange(out[2], 1, kBar1);
- VerifyLocalHeaderChange(out[3], 1, 2);
-
- // The sync ids should have been fixed for exactly one of the two tabs.
- EXPECT_EQ(dupe_sync_id,
- out[1].sync_data().GetSpecifics().session().tab_node_id());
- EXPECT_NE(dupe_sync_id,
- out[2].sync_data().GetSpecifics().session().tab_node_id());
- EXPECT_NE(out[1].sync_data().GetSpecifics().session().tab_node_id(),
- out[2].sync_data().GetSpecifics().session().tab_node_id());
+ VerifyLocalHeaderChange(out[1], 2, 2);
}
// Tests MergeDataAndStartSyncing with sync data but no local data.
@@ -670,10 +558,6 @@ TEST_F(SessionsSyncManagerTest, MergeLocalSessionExistingTabs) {
VerifyLocalTabChange(out[1], tab->GetEntryCount(), kBaz1);
VerifyLocalTabChange(out[2], tab2->GetEntryCount(), kBar2);
VerifyLocalHeaderChange(out[3], 1, 2);
-
- // Verify tab delegates have Sync ids.
- EXPECT_EQ(0, window->GetTabAt(0)->GetSyncId());
- EXPECT_EQ(1, window->GetTabAt(1)->GetSyncId());
}
// Ensure that the last known device name is reported.
@@ -1240,12 +1124,13 @@ TEST_F(SessionsSyncManagerTest, AssociationReusesNodes) {
changes[1].sync_data().GetSpecifics().session().tab_node_id();
// Pass back the previous tab and header nodes at association, along with a
- // second tab node (with a rewritten tab node id).
+ // second tab node (with rewritten tab IDs).
SyncDataList in;
in.push_back(
CreateRemoteData(changes[2].sync_data().GetSpecifics())); // Header node.
sync_pb::SessionSpecifics new_tab(
changes[1].sync_data().GetSpecifics().session());
+ new_tab.mutable_tab()->set_tab_id(new_tab.tab().tab_id() + 1);
new_tab.set_tab_node_id(tab_node_id + 1);
in.push_back(CreateRemoteData(
changes[1].sync_data().GetSpecifics())); // Old tab node.
@@ -1447,10 +1332,6 @@ TEST_F(SessionsSyncManagerTest, OnLocalTabModified) {
VerifyLocalHeaderChange(out[index++], 1, i + 1);
}
}
-
- // Verify tab delegates have Sync ids.
- EXPECT_EQ(0, window->GetTabAt(0)->GetSyncId());
- EXPECT_EQ(1, window->GetTabAt(1)->GetSyncId());
}
TEST_F(SessionsSyncManagerTest, ForeignSessionModifiedTime) {
@@ -1895,58 +1776,49 @@ TEST_F(SessionsSyncManagerTest, GetForeignSessionTabs) {
// Ensure model association associates the pre-existing tabs.
TEST_F(SessionsSyncManagerTest, SwappedOutOnRestore) {
- const int kRestoredTabId = 1337;
- const SessionID kNewTabId = SessionID::FromSerializedValue(2468);
-
// Start with three tabs in a window.
TestSyncedWindowDelegate* window = AddWindow();
TestSyncedTabDelegate* tab1 = AddTab(window->GetSessionId(), kFoo1);
tab1->Navigate(kFoo2);
TestSyncedTabDelegate* tab2 = AddTab(window->GetSessionId(), kBar1);
tab2->Navigate(kBar2);
- TestSyncedTabDelegate* tab3 = AddTab(window->GetSessionId(), kBaz1);
- tab3->Navigate(kBaz2);
SyncDataList in;
SyncChangeList out;
InitWithSyncDataTakeOutput(in, &out);
- // Should be one header add, 3 tab adds/updates, one header update.
- ASSERT_EQ(5U, out.size());
+ // Should be one header add, 2 tab adds/updates, one header update.
+ ASSERT_EQ(4U, out.size());
// Now update the sync data to be:
// * one "normal" fully loaded tab
- // * one placeholder tab with no WebContents and a tab_id change
// * one placeholder tab with no WebContents and no tab_id change
sync_pb::EntitySpecifics t0_entity = out[1].sync_data().GetSpecifics();
sync_pb::EntitySpecifics t1_entity = out[2].sync_data().GetSpecifics();
- t1_entity.mutable_session()->mutable_tab()->set_tab_id(kRestoredTabId);
- sync_pb::EntitySpecifics t2_entity = out[3].sync_data().GetSpecifics();
in.push_back(CreateRemoteData(t0_entity));
in.push_back(CreateRemoteData(t1_entity));
- in.push_back(CreateRemoteData(t2_entity));
out.clear();
manager()->StopSyncing(syncer::SESSIONS);
+ ResetWindows();
- PlaceholderTabDelegate t1_override(kNewTabId, 1);
- PlaceholderTabDelegate t2_override(
- SessionID::FromSerializedValue(t2_entity.session().tab().tab_id()), 2);
+ PlaceholderTabDelegate t1_override(
+ SessionID::FromSerializedValue(t1_entity.session().tab().tab_id()));
+ window = AddWindow();
+ window->OverrideTabAt(0, tab1);
window->OverrideTabAt(1, &t1_override);
- window->OverrideTabAt(2, &t2_override);
InitWithSyncDataTakeOutput(in, &out);
// The last change should be the final header update, reflecting 1 window
- // and 3 tabs.
- VerifyLocalHeaderChange(out.back(), 1, 3);
+ // and 2 tabs.
+ VerifyLocalHeaderChange(out.back(), 1, 2);
- // There should be three changes, one for the fully associated tab, and
- // one each for the tab_id updates to t1 and t2.
+ // There should be two changes, one for the fully associated tab and one for
+ // the placeholder tab (window ID change).
ASSERT_TRUE(AllOfChangesAreType(*FilterOutLocalHeaderChanges(&out),
SyncChange::ACTION_UPDATE));
- ASSERT_EQ(3U, out.size());
+ ASSERT_EQ(2U, out.size());
VerifyLocalTabChange(out[0], 2, kFoo2);
VerifyLocalTabChange(out[1], 2, kBar2);
- VerifyLocalTabChange(out[2], 2, kBaz2);
}
// Ensure model association updates the window ID for tabs whose window's ID has
@@ -1972,8 +1844,7 @@ TEST_F(SessionsSyncManagerTest, WindowIdUpdatedOnRestore) {
// Override the tab with a placeholder tab delegate.
PlaceholderTabDelegate t0_override(
- SessionID::FromSerializedValue(t0_entity.session().tab().tab_id()),
- t0_entity.session().tab_node_id());
+ SessionID::FromSerializedValue(t0_entity.session().tab().tab_id()));
// Set up the window with the new window ID and placeholder tab.
window = AddWindow();
@@ -2008,8 +1879,7 @@ TEST_F(SessionsSyncManagerTest, RestoredPlacholderTabNodeDeleted) {
// Override the tab with a placeholder tab delegate.
PlaceholderTabDelegate t0_override(
- SessionID::FromSerializedValue(t0_entity.session().tab().tab_id()),
- t0_entity.session().tab_node_id());
+ SessionID::FromSerializedValue(t0_entity.session().tab().tab_id()));
// Override the tab with a placeholder whose sync entity won't exist.
window->OverrideTabAt(0, &t0_override);
@@ -2020,48 +1890,6 @@ TEST_F(SessionsSyncManagerTest, RestoredPlacholderTabNodeDeleted) {
ASSERT_EQ(0U, FilterOutLocalHeaderChanges(&out)->size());
}
-// Check the behavior for a placeholder tab in one window being mapped to the
-// same sync entity as a tab in another window. The order should not matter.
-// Instead, they both should have their sync data discarded, sync ids reset, and
-// then re-created where possible (not possible for the placeholder). Assuming a
-// well behaved client, this should never happen.
-TEST_F(SessionsSyncManagerTest, PlaceholderConflictAcrossWindows) {
- syncer::SyncDataList in;
- syncer::SyncChangeList out;
-
- // First sync with one tab and one window.
- TestSyncedWindowDelegate* window = AddWindow();
- TestSyncedTabDelegate* tab1 = AddTab(window->GetSessionId(), kFoo1);
- InitWithSyncDataTakeOutput(in, &out);
- ASSERT_TRUE(out[1].sync_data().GetSpecifics().session().has_tab());
- manager()->StopSyncing(syncer::SESSIONS);
-
- // Now create a second window with a placeholder that has the same sync id,
- // but a different tab id.
- TestSyncedWindowDelegate* window2 = AddWindow();
- int sync_id = out[1].sync_data().GetSpecifics().session().tab_node_id();
- PlaceholderTabDelegate tab2(SessionID::NewUnique(), sync_id);
- window2->OverrideTabAt(0, &tab2);
-
- // Resync, reusing the old sync data.
- in.push_back(CreateRemoteData(out[0].sync_data().GetSpecifics()));
- in.push_back(CreateRemoteData(out[1].sync_data().GetSpecifics()));
- out.clear();
- InitWithSyncDataTakeOutput(in, &out);
-
- // The two tabs have the same sync id, which is not allowed. They will have
- // their ids stripped and re-generated. But the placeholder cannot survive
- // this and will not show up in results.
- ASSERT_TRUE(ChangeTypeMatches(
- out, {SyncChange::ACTION_UPDATE, SyncChange::ACTION_UPDATE}));
- VerifyLocalHeaderChange(out[1], 1, 1);
- VerifyLocalTabChange(out[0], 1, kFoo1);
- EXPECT_EQ(tab1->GetSessionId().id(),
- out[0].sync_data().GetSpecifics().session().tab().tab_id());
- EXPECT_EQ(tab1->GetSyncId(),
- out[0].sync_data().GetSpecifics().session().tab_node_id());
-}
-
// Tests that task ids are generated for navigations on local tabs.
TEST_F(SessionsSyncManagerTest, TrackTasksOnLocalTabModified) {
SyncChangeList changes;
diff --git a/chromium/components/sync_sessions/synced_session.cc b/chromium/components/sync_sessions/synced_session.cc
index a096259f93e..95995d5962e 100644
--- a/chromium/components/sync_sessions/synced_session.cc
+++ b/chromium/components/sync_sessions/synced_session.cc
@@ -4,7 +4,321 @@
#include "components/sync_sessions/synced_session.h"
+#include <vector>
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/sessions/core/serialized_navigation_driver.h"
+#include "components/sync/base/time.h"
+#include "ui/base/page_transition_types.h"
+
namespace sync_sessions {
+namespace {
+
+using sessions::SerializedNavigationEntry;
+
+// The previous referrer policy value corresponding to |Never|.
+// See original constant in serialized_navigation_entry.cc.
+const int kObsoleteReferrerPolicyNever = 2;
+
+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(
+ int index,
+ const sync_pb::TabNavigation& sync_data) {
+ SerializedNavigationEntry navigation;
+ navigation.set_index(index);
+ navigation.set_unique_id(sync_data.unique_id());
+ if (sync_data.has_correct_referrer_policy()) {
+ navigation.set_referrer_url(GURL(sync_data.referrer()));
+ navigation.set_referrer_policy(sync_data.correct_referrer_policy());
+ } else {
+ navigation.set_referrer_url(GURL());
+ navigation.set_referrer_policy(kObsoleteReferrerPolicyNever);
+ }
+ 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());
+
+ if (sync_data.has_redirect_type()) {
+ switch (sync_data.redirect_type()) {
+ case sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT:
+ transition |= ui::PAGE_TRANSITION_CLIENT_REDIRECT;
+ break;
+ case sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT:
+ transition |= ui::PAGE_TRANSITION_SERVER_REDIRECT;
+ break;
+ }
+ }
+ if (sync_data.navigation_forward_back())
+ transition |= ui::PAGE_TRANSITION_FORWARD_BACK;
+ if (sync_data.navigation_from_address_bar())
+ transition |= ui::PAGE_TRANSITION_FROM_ADDRESS_BAR;
+ if (sync_data.navigation_home_page())
+ transition |= ui::PAGE_TRANSITION_HOME_PAGE;
+ if (sync_data.navigation_chain_start())
+ transition |= ui::PAGE_TRANSITION_CHAIN_START;
+ 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())
+ navigation.set_favicon_url(GURL(sync_data.favicon_url()));
+
+ if (sync_data.has_password_state()) {
+ navigation.set_password_state(
+ static_cast<SerializedNavigationEntry::PasswordState>(
+ sync_data.password_state()));
+ }
+
+ navigation.set_http_status_code(sync_data.http_status_code());
+
+ if (sync_data.has_replaced_navigation()) {
+ SerializedNavigationEntry::ReplacedNavigationEntryData replaced_entry_data;
+ replaced_entry_data.first_committed_url =
+ 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(
+ sync_data.replaced_navigation().first_page_transition());
+ navigation.set_replaced_entry_data(replaced_entry_data);
+ }
+
+ sessions::SerializedNavigationDriver::Get()->Sanitize(&navigation);
+
+ navigation.set_is_restored(true);
+
+ return navigation;
+}
+
+// TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well?
+// See http://crbug.com/67068.
+sync_pb::TabNavigation SessionNavigationToSyncData(
+ const SerializedNavigationEntry& navigation) {
+ sync_pb::TabNavigation sync_data;
+ sync_data.set_virtual_url(navigation.virtual_url().spec());
+ sync_data.set_referrer(navigation.referrer_url().spec());
+ sync_data.set_correct_referrer_policy(navigation.referrer_policy());
+ 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));
+
+ // Page transition qualifiers.
+ if (ui::PageTransitionIsRedirect(transition_type)) {
+ if (transition_type & ui::PAGE_TRANSITION_CLIENT_REDIRECT) {
+ sync_data.set_redirect_type(
+ sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT);
+ } else if (transition_type & ui::PAGE_TRANSITION_SERVER_REDIRECT) {
+ sync_data.set_redirect_type(
+ sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT);
+ }
+ }
+ sync_data.set_navigation_forward_back(
+ (transition_type & ui::PAGE_TRANSITION_FORWARD_BACK) != 0);
+ sync_data.set_navigation_from_address_bar(
+ (transition_type & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR) != 0);
+ sync_data.set_navigation_home_page(
+ (transition_type & ui::PAGE_TRANSITION_HOME_PAGE) != 0);
+ sync_data.set_navigation_chain_start(
+ (transition_type & ui::PAGE_TRANSITION_CHAIN_START) != 0);
+ sync_data.set_navigation_chain_end(
+ (transition_type & ui::PAGE_TRANSITION_CHAIN_END) != 0);
+
+ sync_data.set_unique_id(navigation.unique_id());
+ sync_data.set_timestamp_msec(syncer::TimeToProtoTime(navigation.timestamp()));
+ // The full-resolution timestamp works as a global ID.
+ sync_data.set_global_id(navigation.timestamp().ToInternalValue());
+
+ sync_data.set_http_status_code(navigation.http_status_code());
+
+ if (navigation.favicon_url().is_valid())
+ sync_data.set_favicon_url(navigation.favicon_url().spec());
+
+ if (navigation.blocked_state() != SerializedNavigationEntry::STATE_INVALID) {
+ sync_data.set_blocked_state(
+ static_cast<sync_pb::TabNavigation_BlockedState>(
+ navigation.blocked_state()));
+ }
+
+ sync_data.set_password_state(
+ static_cast<sync_pb::TabNavigation_PasswordState>(
+ navigation.password_state()));
+
+ for (const std::string& content_pack_category :
+ navigation.content_pack_categories()) {
+ sync_data.add_content_pack_categories(content_pack_category);
+ }
+
+ // Copy all redirect chain entries except the last URL (which should match
+ // the virtual_url).
+ const std::vector<GURL>& redirect_chain = navigation.redirect_chain();
+ if (redirect_chain.size() > 1) { // Single entry chains have no redirection.
+ size_t last_entry = redirect_chain.size() - 1;
+ for (size_t i = 0; i < last_entry; i++) {
+ sync_pb::NavigationRedirect* navigation_redirect =
+ sync_data.add_navigation_redirect();
+ navigation_redirect->set_url(redirect_chain[i].spec());
+ }
+ // If the last URL didn't match the virtual_url, record it separately.
+ if (sync_data.virtual_url() != redirect_chain[last_entry].spec()) {
+ sync_data.set_last_navigation_redirect_url(
+ redirect_chain[last_entry].spec());
+ }
+ }
+
+ const base::Optional<SerializedNavigationEntry::ReplacedNavigationEntryData>&
+ replaced_entry_data = navigation.replaced_entry_data();
+ if (replaced_entry_data.has_value()) {
+ sync_pb::ReplacedNavigation* replaced_navigation =
+ sync_data.mutable_replaced_navigation();
+ replaced_navigation->set_first_committed_url(
+ 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));
+ }
+
+ sync_data.set_is_restored(navigation.is_restored());
+
+ return sync_data;
+}
+
+void SetSessionTabFromSyncData(const sync_pb::SessionTab& sync_data,
+ base::Time timestamp,
+ sessions::SessionTab* tab) {
+ DCHECK(tab);
+ tab->window_id = SessionID::FromSerializedValue(sync_data.window_id());
+ tab->tab_id = SessionID::FromSerializedValue(sync_data.tab_id());
+ tab->tab_visual_index = sync_data.tab_visual_index();
+ tab->current_navigation_index = sync_data.current_navigation_index();
+ tab->pinned = sync_data.pinned();
+ tab->extension_app_id = sync_data.extension_app_id();
+ tab->user_agent_override.clear();
+ tab->timestamp = timestamp;
+ tab->navigations.clear();
+ for (int i = 0; i < sync_data.navigation_size(); ++i) {
+ tab->navigations.push_back(
+ SessionNavigationFromSyncData(i, sync_data.navigation(i)));
+ }
+ tab->session_storage_persistent_id.clear();
+}
+
+sync_pb::SessionTab SessionTabToSyncData(const sessions::SessionTab& tab) {
+ sync_pb::SessionTab sync_data;
+ sync_data.set_tab_id(tab.tab_id.id());
+ sync_data.set_window_id(tab.window_id.id());
+ sync_data.set_tab_visual_index(tab.tab_visual_index);
+ sync_data.set_current_navigation_index(tab.current_navigation_index);
+ sync_data.set_pinned(tab.pinned);
+ sync_data.set_extension_app_id(tab.extension_app_id);
+ for (const SerializedNavigationEntry& navigation : tab.navigations) {
+ SessionNavigationToSyncData(navigation).Swap(sync_data.add_navigation());
+ }
+ return sync_data;
+}
SyncedSessionWindow::SyncedSessionWindow() {}
diff --git a/chromium/components/sync_sessions/synced_session.h b/chromium/components/sync_sessions/synced_session.h
index 772457b8063..5b63378cf5d 100644
--- a/chromium/components/sync_sessions/synced_session.h
+++ b/chromium/components/sync_sessions/synced_session.h
@@ -12,6 +12,7 @@
#include "base/macros.h"
#include "base/time/time.h"
+#include "components/sessions/core/serialized_navigation_entry.h"
#include "components/sessions/core/session_id.h"
#include "components/sessions/core/session_types.h"
#include "components/sync/protocol/session_specifics.pb.h"
@@ -19,6 +20,34 @@
namespace sync_sessions {
+// Construct a SerializedNavigationEntry for a particular index from a sync
+// protocol buffer. Note that the sync protocol buffer doesn't contain all
+// SerializedNavigationEntry fields. Also, the timestamp of the returned
+// SerializedNavigationEntry is nulled out, as we assume that the protocol
+// buffer is from a foreign session.
+sessions::SerializedNavigationEntry SessionNavigationFromSyncData(
+ int index,
+ const sync_pb::TabNavigation& sync_data);
+
+// Convert |navigation| into its sync protocol buffer equivalent. Note that the
+// protocol buffer doesn't contain all SerializedNavigationEntry fields.
+sync_pb::TabNavigation SessionNavigationToSyncData(
+ const sessions::SerializedNavigationEntry& navigation);
+
+// Set all the fields of |*tab| object from the given sync data and timestamp.
+// Uses SerializedNavigationEntry::FromSyncData() to fill |navigations|. Note
+// that the sync protocol buffer doesn't contain all SerializedNavigationEntry
+// fields. |tab| must not be null.
+void SetSessionTabFromSyncData(const sync_pb::SessionTab& sync_data,
+ base::Time timestamp,
+ sessions::SessionTab* tab);
+
+// Convert |tab| into its sync protocol buffer equivalent. Uses
+// SerializedNavigationEntry::ToSyncData to convert |navigations|. Note that the
+// protocol buffer doesn't contain all SerializedNavigationEntry fields, and
+// that the returned protocol buffer doesn't have any favicon data.
+sync_pb::SessionTab SessionTabToSyncData(const sessions::SessionTab& tab);
+
// A Sync wrapper for a SessionWindow.
struct SyncedSessionWindow {
SyncedSessionWindow();
diff --git a/chromium/components/sync_sessions/synced_session_tracker.cc b/chromium/components/sync_sessions/synced_session_tracker.cc
index ad682bf59e0..c757b1717db 100644
--- a/chromium/components/sync_sessions/synced_session_tracker.cc
+++ b/chromium/components/sync_sessions/synced_session_tracker.cc
@@ -696,7 +696,7 @@ void UpdateTrackerWithSpecifics(const sync_pb::SessionSpecifics& specifics,
}
// Update SessionTab based on protobuf.
- tab->SetFromSyncData(tab_s, modification_time);
+ SetSessionTabFromSyncData(tab_s, modification_time, tab);
// Update the last modified time.
if (session->modified_time < modification_time)
@@ -778,7 +778,7 @@ void SerializePartialTrackerToSpecifics(
sync_pb::SessionSpecifics tab_pb;
tab_pb.set_session_tag(session_tag);
tab_pb.set_tab_node_id(tab_node_id);
- tab->ToSyncData().Swap(tab_pb.mutable_tab());
+ SessionTabToSyncData(*tab).Swap(tab_pb.mutable_tab());
output_cb.Run(session->session_name, &tab_pb);
continue;
}
diff --git a/chromium/components/sync_sessions/synced_session_tracker.h b/chromium/components/sync_sessions/synced_session_tracker.h
index 24dac10ad45..fe67be56622 100644
--- a/chromium/components/sync_sessions/synced_session_tracker.h
+++ b/chromium/components/sync_sessions/synced_session_tracker.h
@@ -143,8 +143,6 @@ class SyncedSessionTracker {
// Returns a pointer to the SessionTab object associated with
// |tab_id| for the session specified with |session_tag|.
// Note: Ownership of the SessionTab remains within the SyncedSessionTracker.
- // TODO(zea): Replace SessionTab with a Sync specific wrapper.
- // https://crbug.com/662597
sessions::SessionTab* GetTab(const std::string& session_tag,
SessionID tab_id);
@@ -194,10 +192,8 @@ class SyncedSessionTracker {
int AssociateLocalTabWithFreeTabNode(SessionID tab_id);
// Reassociates the tab denoted by |tab_node_id| with a new tab id, preserving
- // any previous SessionTab object the node was associated with. This is useful
- // on restart when sync needs to reassociate tabs from a previous session with
- // newly restored tabs (and can be used in conjunction with PutTabInWindow).
- // If |new_tab_id| is already associated with a tab object, that tab will be
+ // any previous SessionTab object the node was associated with. If
+ // |new_tab_id| is already associated with a tab object, that tab will be
// overwritten. Reassociating a tab with a node it is already mapped to will
// have no effect.
void ReassociateLocalTab(int tab_node_id, SessionID new_tab_id);
diff --git a/chromium/components/sync_sessions/synced_session_unittest.cc b/chromium/components/sync_sessions/synced_session_unittest.cc
new file mode 100644
index 00000000000..dc709c2df74
--- /dev/null
+++ b/chromium/components/sync_sessions/synced_session_unittest.cc
@@ -0,0 +1,282 @@
+// 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/sync_sessions/synced_session.h"
+
+#include <cstddef>
+#include <string>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "components/sessions/core/serialized_navigation_entry_test_helper.h"
+#include "components/sync/base/time.h"
+#include "components/sync/protocol/session_specifics.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/page_transition_types.h"
+#include "url/gurl.h"
+
+namespace sync_sessions {
+namespace {
+
+namespace test_data = sessions::test_data;
+
+using sessions::SerializedNavigationEntry;
+using sessions::SerializedNavigationEntryTestHelper;
+
+// Create a sync_pb::TabNavigation from the constants above.
+sync_pb::TabNavigation MakeSyncDataForTest() {
+ sync_pb::TabNavigation sync_data;
+ sync_data.set_virtual_url(test_data::kVirtualURL.spec());
+ sync_data.set_referrer(test_data::kReferrerURL.spec());
+ sync_data.set_obsolete_referrer_policy(test_data::kReferrerPolicy);
+ sync_data.set_correct_referrer_policy(test_data::kReferrerPolicy);
+ sync_data.set_title(base::UTF16ToUTF8(test_data::kTitle));
+ sync_data.set_page_transition(
+ sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME);
+ sync_data.set_unique_id(test_data::kUniqueID);
+ sync_data.set_timestamp_msec(syncer::TimeToProtoTime(test_data::kTimestamp));
+ sync_data.set_redirect_type(sync_pb::SyncEnums::CLIENT_REDIRECT);
+ sync_data.set_navigation_home_page(true);
+ sync_data.set_favicon_url(test_data::kFaviconURL.spec());
+ sync_data.set_http_status_code(test_data::kHttpStatusCode);
+ // The redirect chain only syncs one way.
+ return sync_data;
+}
+
+// Create a SerializedNavigationEntry from a sync_pb::TabNavigation. All its
+// fields should match the protocol buffer's if it exists there, and
+// should be set to the default value otherwise.
+TEST(SyncedSessionTest, SessionNavigationFromSyncData) {
+ const sync_pb::TabNavigation sync_data = MakeSyncDataForTest();
+
+ const SerializedNavigationEntry navigation =
+ SessionNavigationFromSyncData(test_data::kIndex, sync_data);
+
+ EXPECT_EQ(test_data::kIndex, navigation.index());
+ EXPECT_EQ(test_data::kUniqueID, navigation.unique_id());
+ EXPECT_EQ(test_data::kReferrerURL, navigation.referrer_url());
+ EXPECT_EQ(test_data::kReferrerPolicy, navigation.referrer_policy());
+ EXPECT_EQ(test_data::kVirtualURL, navigation.virtual_url());
+ EXPECT_EQ(test_data::kTitle, navigation.title());
+ EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
+ navigation.transition_type(), test_data::kTransitionType));
+ EXPECT_FALSE(navigation.has_post_data());
+ EXPECT_EQ(-1, navigation.post_id());
+ EXPECT_EQ(GURL(), navigation.original_request_url());
+ EXPECT_FALSE(navigation.is_overriding_user_agent());
+ EXPECT_EQ(test_data::kTimestamp, navigation.timestamp());
+ EXPECT_EQ(test_data::kFaviconURL, navigation.favicon_url());
+ EXPECT_EQ(test_data::kHttpStatusCode, navigation.http_status_code());
+ // The redirect chain only syncs one way.
+}
+
+// Create a SerializedNavigationEntry, then create a sync protocol buffer from
+// it. The protocol buffer should have matching fields to the
+// SerializedNavigationEntry (when applicable).
+TEST(SyncedSessionTest, SessionNavigationToSyncData) {
+ const SerializedNavigationEntry navigation =
+ SerializedNavigationEntryTestHelper::CreateNavigationForTest();
+ const sync_pb::TabNavigation sync_data =
+ SessionNavigationToSyncData(navigation);
+
+ EXPECT_EQ(test_data::kVirtualURL.spec(), sync_data.virtual_url());
+ EXPECT_EQ(test_data::kReferrerURL.spec(), sync_data.referrer());
+ EXPECT_EQ(test_data::kTitle, base::ASCIIToUTF16(sync_data.title()));
+ EXPECT_EQ(sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME,
+ sync_data.page_transition());
+ EXPECT_TRUE(sync_data.has_redirect_type());
+ EXPECT_EQ(test_data::kUniqueID, sync_data.unique_id());
+ EXPECT_EQ(syncer::TimeToProtoTime(test_data::kTimestamp),
+ sync_data.timestamp_msec());
+ EXPECT_EQ(test_data::kTimestamp.ToInternalValue(), sync_data.global_id());
+ EXPECT_EQ(test_data::kFaviconURL.spec(), sync_data.favicon_url());
+ EXPECT_EQ(test_data::kHttpStatusCode, sync_data.http_status_code());
+ // The proto navigation redirects don't include the final chain entry
+ // (because it didn't redirect) so the lengths should differ by 1.
+ ASSERT_EQ(3, sync_data.navigation_redirect_size() + 1);
+ EXPECT_EQ(test_data::kRedirectURL0.spec(),
+ sync_data.navigation_redirect(0).url());
+ EXPECT_EQ(test_data::kRedirectURL1.spec(),
+ sync_data.navigation_redirect(1).url());
+ EXPECT_FALSE(sync_data.has_last_navigation_redirect_url());
+ EXPECT_FALSE(sync_data.has_replaced_navigation());
+}
+
+// Specifically test the |replaced_navigation| field, which should be populated
+// when the navigation entry has been replaced by another entry (e.g.
+// history.pushState()).
+TEST(SyncedSessionTest, SessionNavigationToSyncDataWithReplacedNavigation) {
+ const GURL kReplacedURL = GURL("http://replaced-url.com");
+ const int kReplacedTimestampMs = 79;
+ const ui::PageTransition kReplacedPageTransition =
+ ui::PAGE_TRANSITION_AUTO_BOOKMARK;
+
+ SerializedNavigationEntry navigation =
+ SerializedNavigationEntryTestHelper::CreateNavigationForTest();
+ SerializedNavigationEntryTestHelper::SetReplacedEntryData(
+ {kReplacedURL, syncer::ProtoTimeToTime(kReplacedTimestampMs),
+ kReplacedPageTransition},
+ &navigation);
+
+ const sync_pb::TabNavigation sync_data =
+ SessionNavigationToSyncData(navigation);
+ EXPECT_TRUE(sync_data.has_replaced_navigation());
+ EXPECT_EQ(kReplacedURL.spec(),
+ sync_data.replaced_navigation().first_committed_url());
+ EXPECT_EQ(kReplacedTimestampMs,
+ sync_data.replaced_navigation().first_timestamp_msec());
+ EXPECT_EQ(sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK,
+ sync_data.replaced_navigation().first_page_transition());
+}
+
+// Test that the last_navigation_redirect_url is set when needed. This test is
+// just like the above, but with a different virtual_url. Create a
+// SerializedNavigationEntry, then create a sync protocol buffer from it. The
+// protocol buffer should have a last_navigation_redirect_url.
+TEST(SyncedSessionTest, SessionNavigationToSyncDataWithLastRedirectUrl) {
+ SerializedNavigationEntry navigation =
+ SerializedNavigationEntryTestHelper::CreateNavigationForTest();
+ SerializedNavigationEntryTestHelper::SetVirtualURL(test_data::kOtherURL,
+ &navigation);
+
+ const sync_pb::TabNavigation sync_data =
+ SessionNavigationToSyncData(navigation);
+ EXPECT_TRUE(sync_data.has_last_navigation_redirect_url());
+ EXPECT_EQ(test_data::kVirtualURL.spec(),
+ sync_data.last_navigation_redirect_url());
+
+ // The redirect chain should be the same as in the above test.
+ ASSERT_EQ(3, sync_data.navigation_redirect_size() + 1);
+ EXPECT_EQ(test_data::kRedirectURL0.spec(),
+ sync_data.navigation_redirect(0).url());
+ EXPECT_EQ(test_data::kRedirectURL1.spec(),
+ sync_data.navigation_redirect(1).url());
+}
+
+// Ensure all transition types and qualifiers are converted to/from the sync
+// SerializedNavigationEntry representation properly.
+TEST(SyncedSessionTest, SessionNavigationToSyncDataWithTransitionTypes) {
+ SerializedNavigationEntry navigation =
+ SerializedNavigationEntryTestHelper::CreateNavigationForTest();
+
+ for (uint32_t core_type = ui::PAGE_TRANSITION_LINK;
+ core_type < ui::PAGE_TRANSITION_LAST_CORE; ++core_type) {
+ // Because qualifier is a uint32_t, left shifting will eventually overflow
+ // and hit zero again. SERVER_REDIRECT, as the last qualifier and also
+ // in place of the sign bit, is therefore the last transition before
+ // 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))
+ continue; // We don't sync PAGE_TRANSITION_FROM_API.
+ ui::PageTransition transition =
+ ui::PageTransitionFromInt(core_type | qualifier);
+ SerializedNavigationEntryTestHelper::SetTransitionType(transition,
+ &navigation);
+
+ const sync_pb::TabNavigation sync_data =
+ SessionNavigationToSyncData(navigation);
+ const SerializedNavigationEntry constructed_nav =
+ SessionNavigationFromSyncData(test_data::kIndex, sync_data);
+ const ui::PageTransition constructed_transition =
+ constructed_nav.transition_type();
+
+ EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
+ constructed_transition, transition));
+ }
+ }
+}
+
+// Create a typical SessionTab protocol buffer and set an existing
+// SessionTab from it. The data from the protocol buffer should
+// clobber the existing data.
+TEST(SyncedSessionTest, SetSessionTabFromSyncData) {
+ sync_pb::SessionTab sync_data;
+ sync_data.set_tab_id(5);
+ sync_data.set_window_id(10);
+ sync_data.set_tab_visual_index(13);
+ sync_data.set_current_navigation_index(3);
+ sync_data.set_pinned(true);
+ sync_data.set_extension_app_id("app_id");
+ for (int i = 0; i < 5; ++i) {
+ sync_pb::TabNavigation* navigation = sync_data.add_navigation();
+ navigation->set_virtual_url("http://foo/" + base::IntToString(i));
+ navigation->set_referrer("referrer");
+ navigation->set_title("title");
+ navigation->set_page_transition(sync_pb::SyncEnums_PageTransition_TYPED);
+ }
+
+ sessions::SessionTab tab;
+ tab.window_id = SessionID::FromSerializedValue(100);
+ tab.tab_id = SessionID::FromSerializedValue(100);
+ tab.tab_visual_index = 100;
+ tab.current_navigation_index = 1000;
+ tab.pinned = false;
+ tab.extension_app_id = "fake";
+ tab.user_agent_override = "fake";
+ tab.timestamp = base::Time::FromInternalValue(100);
+ tab.navigations.resize(100);
+ tab.session_storage_persistent_id = "fake";
+
+ SetSessionTabFromSyncData(sync_data, base::Time::FromInternalValue(5u), &tab);
+ EXPECT_EQ(10, tab.window_id.id());
+ EXPECT_EQ(5, tab.tab_id.id());
+ EXPECT_EQ(13, tab.tab_visual_index);
+ EXPECT_EQ(3, tab.current_navigation_index);
+ EXPECT_TRUE(tab.pinned);
+ EXPECT_EQ("app_id", tab.extension_app_id);
+ EXPECT_TRUE(tab.user_agent_override.empty());
+ EXPECT_EQ(5u, tab.timestamp.ToInternalValue());
+ ASSERT_EQ(5u, tab.navigations.size());
+ for (int i = 0; i < 5; ++i) {
+ EXPECT_EQ(i, tab.navigations[i].index());
+ EXPECT_EQ(GURL("referrer"), tab.navigations[i].referrer_url());
+ EXPECT_EQ(base::ASCIIToUTF16("title"), tab.navigations[i].title());
+ EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
+ tab.navigations[i].transition_type(), ui::PAGE_TRANSITION_TYPED));
+ EXPECT_EQ(GURL("http://foo/" + base::IntToString(i)),
+ tab.navigations[i].virtual_url());
+ }
+ EXPECT_TRUE(tab.session_storage_persistent_id.empty());
+}
+
+TEST(SyncedSessionTest, SessionTabToSyncData) {
+ sessions::SessionTab tab;
+ tab.window_id = SessionID::FromSerializedValue(10);
+ tab.tab_id = SessionID::FromSerializedValue(5);
+ tab.tab_visual_index = 13;
+ tab.current_navigation_index = 3;
+ tab.pinned = true;
+ tab.extension_app_id = "app_id";
+ tab.user_agent_override = "fake";
+ tab.timestamp = base::Time::FromInternalValue(100);
+ for (int i = 0; i < 5; ++i) {
+ tab.navigations.push_back(
+ SerializedNavigationEntryTestHelper::CreateNavigation(
+ "http://foo/" + base::IntToString(i), "title"));
+ }
+ tab.session_storage_persistent_id = "fake";
+
+ const sync_pb::SessionTab sync_data = SessionTabToSyncData(tab);
+ EXPECT_EQ(5, sync_data.tab_id());
+ EXPECT_EQ(10, sync_data.window_id());
+ EXPECT_EQ(13, sync_data.tab_visual_index());
+ EXPECT_EQ(3, sync_data.current_navigation_index());
+ EXPECT_TRUE(sync_data.pinned());
+ EXPECT_EQ("app_id", sync_data.extension_app_id());
+ ASSERT_EQ(5, sync_data.navigation_size());
+ for (int i = 0; i < 5; ++i) {
+ EXPECT_EQ(tab.navigations[i].virtual_url().spec(),
+ sync_data.navigation(i).virtual_url());
+ EXPECT_EQ(base::UTF16ToUTF8(tab.navigations[i].title()),
+ sync_data.navigation(i).title());
+ }
+ EXPECT_FALSE(sync_data.has_favicon());
+ EXPECT_FALSE(sync_data.has_favicon_type());
+ EXPECT_FALSE(sync_data.has_favicon_source());
+}
+
+} // namespace
+} // namespace sync_sessions
diff --git a/chromium/components/sync_sessions/synced_tab_delegate.h b/chromium/components/sync_sessions/synced_tab_delegate.h
index c5c5a152b3d..d4043df58f7 100644
--- a/chromium/components/sync_sessions/synced_tab_delegate.h
+++ b/chromium/components/sync_sessions/synced_tab_delegate.h
@@ -28,11 +28,16 @@ class SyncedTabDelegate {
// Methods from TabContents.
virtual SessionID GetWindowId() const = 0;
+ // Tab identifier: two tabs with the same ID (even across browser restarts)
+ // will be considered identical. Tab/session restore may or may not be able
+ // to restore this value, which means the opposite is not true: having
+ // distinct IDs does not imply they are distinct tabs.
virtual SessionID GetSessionId() const = 0;
virtual bool IsBeingDestroyed() const = 0;
// Get the tab id of the tab responsible for opening this tab, if applicable.
// Returns an invalid ID if no such tab relationship is known.
+ // TODO(mastiz): Rename to GetSourceTabSessionId().
virtual SessionID GetSourceTabID() const = 0;
// Method derived from extensions TabHelper.
@@ -56,8 +61,6 @@ class SyncedTabDelegate {
GetBlockedNavigations() const = 0;
// Session sync related methods.
- virtual int GetSyncId() const = 0;
- virtual void SetSyncId(int sync_id) = 0;
virtual bool ShouldSync(SyncSessionsClient* sessions_client) = 0;
// Whether this tab is a placeholder tab. On some platforms, tabs can be
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 3cc6e3dba52..55c06bd5605 100644
--- a/chromium/components/sync_sessions/test_synced_window_delegates_getter.cc
+++ b/chromium/components/sync_sessions/test_synced_window_delegates_getter.cc
@@ -8,6 +8,7 @@
#include "base/bind.h"
#include "components/sessions/core/serialized_navigation_entry_test_helper.h"
+#include "components/sync_sessions/synced_session.h"
#include "components/sync_sessions/tab_node_pool.h"
namespace sync_sessions {
@@ -36,7 +37,7 @@ void TestSyncedTabDelegate::Navigate(const std::string& url,
tab_navigation.set_http_status_code(200);
auto entry = std::make_unique<sessions::SerializedNavigationEntry>(
- sessions::SerializedNavigationEntry::FromSyncData(0, tab_navigation));
+ SessionNavigationFromSyncData(0, tab_navigation));
sessions::SerializedNavigationEntryTestHelper::SetTimestamp(time,
entry.get());
sessions::SerializedNavigationEntryTestHelper::SetTransitionType(transition,
@@ -131,14 +132,6 @@ bool TestSyncedTabDelegate::IsPlaceholderTab() const {
return false;
}
-int TestSyncedTabDelegate::GetSyncId() const {
- return sync_id_;
-}
-
-void TestSyncedTabDelegate::SetSyncId(int sync_id) {
- sync_id_ = sync_id;
-}
-
bool TestSyncedTabDelegate::ShouldSync(SyncSessionsClient* sessions_client) {
// This is just a simple filter that isn't meant to fully reproduce
// the TabContentsTabDelegate's ShouldSync logic.
@@ -158,8 +151,8 @@ SessionID TestSyncedTabDelegate::GetSourceTabID() const {
return SessionID::InvalidValue();
}
-PlaceholderTabDelegate::PlaceholderTabDelegate(SessionID tab_id, int sync_id)
- : tab_id_(tab_id), sync_id_(sync_id) {}
+PlaceholderTabDelegate::PlaceholderTabDelegate(SessionID tab_id)
+ : tab_id_(tab_id) {}
PlaceholderTabDelegate::~PlaceholderTabDelegate() = default;
@@ -167,14 +160,6 @@ SessionID PlaceholderTabDelegate::GetSessionId() const {
return tab_id_;
}
-int PlaceholderTabDelegate::GetSyncId() const {
- return sync_id_;
-}
-
-void PlaceholderTabDelegate::SetSyncId(int sync_id) {
- sync_id_ = sync_id;
-}
-
bool PlaceholderTabDelegate::IsPlaceholderTab() const {
return true;
}
@@ -361,6 +346,13 @@ TestSyncedTabDelegate* TestSyncedWindowDelegatesGetter::AddTab(
return tabs_.back().get();
}
+void TestSyncedWindowDelegatesGetter::SessionRestoreComplete() {
+ for (auto& window : windows_)
+ window->SetIsSessionRestoreInProgress(false);
+
+ router_.NotifySessionRestoreComplete();
+}
+
LocalSessionEventRouter* TestSyncedWindowDelegatesGetter::router() {
return &router_;
}
@@ -398,4 +390,10 @@ void TestSyncedWindowDelegatesGetter::DummyRouter::NotifyNav(
handler_->OnLocalTabModified(tab);
}
+void TestSyncedWindowDelegatesGetter::DummyRouter::
+ NotifySessionRestoreComplete() {
+ if (handler_)
+ handler_->OnSessionRestoreComplete();
+}
+
} // namespace sync_sessions
diff --git a/chromium/components/sync_sessions/test_synced_window_delegates_getter.h b/chromium/components/sync_sessions/test_synced_window_delegates_getter.h
index 868d26b268a..eab3eac466a 100644
--- a/chromium/components/sync_sessions/test_synced_window_delegates_getter.h
+++ b/chromium/components/sync_sessions/test_synced_window_delegates_getter.h
@@ -58,8 +58,6 @@ class TestSyncedTabDelegate : public SyncedTabDelegate {
const std::vector<std::unique_ptr<const sessions::SerializedNavigationEntry>>*
GetBlockedNavigations() const override;
bool IsPlaceholderTab() const override;
- int GetSyncId() const override;
- void SetSyncId(int sync_id) override;
bool ShouldSync(SyncSessionsClient* sessions_client) override;
SessionID GetSourceTabID() const override;
@@ -70,7 +68,6 @@ class TestSyncedTabDelegate : public SyncedTabDelegate {
int current_entry_index_ = -1;
bool is_supervised_ = false;
- int sync_id_ = -1;
std::vector<std::unique_ptr<const sessions::SerializedNavigationEntry>>
blocked_navigations_;
std::vector<std::unique_ptr<const sessions::SerializedNavigationEntry>>
@@ -85,13 +82,11 @@ class TestSyncedTabDelegate : public SyncedTabDelegate {
// memory. See SyncedTabDelegate::IsPlaceHolderTab for more info.
class PlaceholderTabDelegate : public SyncedTabDelegate {
public:
- PlaceholderTabDelegate(SessionID tab_id, int sync_id);
+ explicit PlaceholderTabDelegate(SessionID tab_id);
~PlaceholderTabDelegate() override;
// SyncedTabDelegate overrides.
SessionID GetSessionId() const override;
- int GetSyncId() const override;
- void SetSyncId(int sync_id) override;
bool IsPlaceholderTab() const override;
// Everything else is invalid to invoke as it depends on a valid WebContents.
SessionID GetWindowId() const override;
@@ -114,7 +109,6 @@ class PlaceholderTabDelegate : public SyncedTabDelegate {
private:
const SessionID tab_id_;
- int sync_id_ = -1;
DISALLOW_COPY_AND_ASSIGN(PlaceholderTabDelegate);
};
@@ -170,6 +164,7 @@ class TestSyncedWindowDelegatesGetter : public SyncedWindowDelegatesGetter {
// TestSyncedTabDelegate (not owned).
TestSyncedTabDelegate* AddTab(SessionID window_id,
SessionID tab_id = SessionID::NewUnique());
+ void SessionRestoreComplete();
LocalSessionEventRouter* router();
// SyncedWindowDelegatesGetter overrides.
@@ -184,6 +179,7 @@ class TestSyncedWindowDelegatesGetter : public SyncedWindowDelegatesGetter {
void StartRoutingTo(LocalSessionEventHandler* handler) override;
void Stop() override;
void NotifyNav(SyncedTabDelegate* tab);
+ void NotifySessionRestoreComplete();
private:
LocalSessionEventHandler* handler_ = nullptr;
diff --git a/chromium/components/sync_wifi/wifi_config_delegate_chromeos_unittest.cc b/chromium/components/sync_wifi/wifi_config_delegate_chromeos_unittest.cc
index 8930aabd4db..b6cae29c0c9 100644
--- a/chromium/components/sync_wifi/wifi_config_delegate_chromeos_unittest.cc
+++ b/chromium/components/sync_wifi/wifi_config_delegate_chromeos_unittest.cc
@@ -113,6 +113,13 @@ class FakeManagedNetworkConfigurationHandler
NOTIMPLEMENTED();
return nullptr;
}
+ bool IsNetworkBlockedByPolicy(const std::string& type,
+ const std::string& guid,
+ const std::string& profile_path,
+ const std::string& hex_ssid) const override {
+ NOTIMPLEMENTED();
+ return false;
+ }
bool create_configuration_called() const {
return create_configuration_called_;
diff --git a/chromium/components/task_scheduler_util/variations_util.cc b/chromium/components/task_scheduler_util/variations_util.cc
index 5c2aa320564..7037824d6a2 100644
--- a/chromium/components/task_scheduler_util/variations_util.cc
+++ b/chromium/components/task_scheduler_util/variations_util.cc
@@ -68,9 +68,9 @@ std::unique_ptr<base::SchedulerWorkerPoolParams> GetWorkerPoolParams(
offset),
base::TimeDelta::FromMilliseconds(detach_milliseconds));
- if (params->max_threads() <= 0) {
- DLOG(ERROR) << "Invalid max threads in the Worker Pool Descriptor: "
- << params->max_threads();
+ if (params->max_tasks() <= 0) {
+ DLOG(ERROR) << "Invalid max tasks in the Worker Pool Descriptor: "
+ << params->max_tasks();
return nullptr;
}
diff --git a/chromium/components/task_scheduler_util/variations_util_unittest.cc b/chromium/components/task_scheduler_util/variations_util_unittest.cc
index 65b69f63657..4264359947b 100644
--- a/chromium/components/task_scheduler_util/variations_util_unittest.cc
+++ b/chromium/components/task_scheduler_util/variations_util_unittest.cc
@@ -47,7 +47,7 @@ TEST_F(TaskSchedulerUtilVariationsUtilTest, OrderingParams5) {
auto init_params = GetTaskSchedulerInitParams("Renderer");
ASSERT_TRUE(init_params);
- EXPECT_EQ(1, init_params->background_worker_pool_params.max_threads());
+ EXPECT_EQ(1, init_params->background_worker_pool_params.max_tasks());
EXPECT_EQ(
base::TimeDelta::FromMilliseconds(42),
init_params->background_worker_pool_params.suggested_reclaim_time());
@@ -55,8 +55,7 @@ TEST_F(TaskSchedulerUtilVariationsUtilTest, OrderingParams5) {
base::SchedulerBackwardCompatibility::DISABLED,
init_params->background_worker_pool_params.backward_compatibility());
- EXPECT_EQ(2,
- init_params->background_blocking_worker_pool_params.max_threads());
+ EXPECT_EQ(2, init_params->background_blocking_worker_pool_params.max_tasks());
EXPECT_EQ(base::TimeDelta::FromMilliseconds(52),
init_params->background_blocking_worker_pool_params
.suggested_reclaim_time());
@@ -64,7 +63,7 @@ TEST_F(TaskSchedulerUtilVariationsUtilTest, OrderingParams5) {
init_params->background_blocking_worker_pool_params
.backward_compatibility());
- EXPECT_EQ(4, init_params->foreground_worker_pool_params.max_threads());
+ EXPECT_EQ(4, init_params->foreground_worker_pool_params.max_tasks());
EXPECT_EQ(
base::TimeDelta::FromMilliseconds(62),
init_params->foreground_worker_pool_params.suggested_reclaim_time());
@@ -72,8 +71,7 @@ TEST_F(TaskSchedulerUtilVariationsUtilTest, OrderingParams5) {
base::SchedulerBackwardCompatibility::DISABLED,
init_params->foreground_worker_pool_params.backward_compatibility());
- EXPECT_EQ(8,
- init_params->foreground_blocking_worker_pool_params.max_threads());
+ EXPECT_EQ(8, init_params->foreground_blocking_worker_pool_params.max_tasks());
EXPECT_EQ(base::TimeDelta::FromMilliseconds(72),
init_params->foreground_blocking_worker_pool_params
.suggested_reclaim_time());
diff --git a/chromium/components/test/BUILD.gn b/chromium/components/test/BUILD.gn
index 247a3f78f0c..16094248dbf 100644
--- a/chromium/components/test/BUILD.gn
+++ b/chromium/components/test/BUILD.gn
@@ -18,7 +18,7 @@ source_set("test_support") {
"//components/content_settings/core/common",
"//components/gcm_driver:gcm_driver",
"//components/signin/core/browser",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//net",
"//testing/gtest",
"//ui/base",
diff --git a/chromium/components/timers/alarm_timer_chromeos.cc b/chromium/components/timers/alarm_timer_chromeos.cc
index 17180385da4..0b43134f1d4 100644
--- a/chromium/components/timers/alarm_timer_chromeos.cc
+++ b/chromium/components/timers/alarm_timer_chromeos.cc
@@ -20,44 +20,39 @@
namespace timers {
-AlarmTimer::AlarmTimer(bool retain_user_task, bool is_repeating)
- : base::Timer(retain_user_task, is_repeating),
- alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)),
- weak_factory_(this) {}
+SimpleAlarmTimer::SimpleAlarmTimer()
+ : alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)), weak_factory_(this) {}
-AlarmTimer::~AlarmTimer() {
+SimpleAlarmTimer::~SimpleAlarmTimer() {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
Stop();
}
-void AlarmTimer::Stop() {
+void SimpleAlarmTimer::Stop() {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
- if (!base::Timer::is_running())
+ if (!IsRunning())
return;
if (!CanWakeFromSuspend()) {
- base::Timer::Stop();
+ base::RetainingOneShotTimer::Stop();
return;
}
// Cancel any previous callbacks.
weak_factory_.InvalidateWeakPtrs();
- base::Timer::set_is_running(false);
+ base::RetainingOneShotTimer::set_is_running(false);
alarm_fd_watcher_.reset();
pending_task_.reset();
-
- if (!base::Timer::retain_user_task())
- base::Timer::set_user_task(base::Closure());
}
-void AlarmTimer::Reset() {
+void SimpleAlarmTimer::Reset() {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
- DCHECK(!base::Timer::user_task().is_null());
+ DCHECK(!base::RetainingOneShotTimer::user_task().is_null());
if (!CanWakeFromSuspend()) {
- base::Timer::Reset();
+ base::RetainingOneShotTimer::Reset();
return;
}
@@ -66,15 +61,16 @@ void AlarmTimer::Reset() {
alarm_fd_watcher_.reset();
// Ensure that the delay is not negative.
- const base::TimeDelta delay =
- std::max(base::TimeDelta(), base::Timer::GetCurrentDelay());
+ const base::TimeDelta delay = std::max(
+ base::TimeDelta(), base::RetainingOneShotTimer::GetCurrentDelay());
// Set up the pending task.
- base::Timer::set_desired_run_time(
+ base::RetainingOneShotTimer::set_desired_run_time(
delay.is_zero() ? base::TimeTicks() : base::TimeTicks::Now() + delay);
pending_task_ = std::make_unique<base::PendingTask>(
- base::Timer::posted_from(), base::Timer::user_task(),
- base::Timer::desired_run_time());
+ base::RetainingOneShotTimer::posted_from(),
+ base::RetainingOneShotTimer::user_task(),
+ base::RetainingOneShotTimer::desired_run_time());
// Set |alarm_fd_| to be signaled when the delay expires. If the delay is
// zero, |alarm_fd_| will never be signaled. This overrides the previous
@@ -88,27 +84,28 @@ void AlarmTimer::Reset() {
PLOG(ERROR) << "Error while setting alarm time. Timer will not fire";
// The timer is running.
- base::Timer::set_is_running(true);
+ base::RetainingOneShotTimer::set_is_running(true);
// If the delay is zero, post the task now.
if (delay.is_zero()) {
origin_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&AlarmTimer::OnTimerFired, weak_factory_.GetWeakPtr()));
+ FROM_HERE, base::BindOnce(&SimpleAlarmTimer::OnTimerFired,
+ weak_factory_.GetWeakPtr()));
} else {
// Otherwise, if the delay is not zero, generate a tracing event to indicate
// that the task was posted and watch |alarm_fd_|.
- base::debug::TaskAnnotator().DidQueueTask("AlarmTimer::Reset",
- *pending_task_);
+ base::debug::TaskAnnotator().WillQueueTask("SimpleAlarmTimer::Reset",
+ pending_task_.get());
alarm_fd_watcher_ = base::FileDescriptorWatcher::WatchReadable(
- alarm_fd_, base::Bind(&AlarmTimer::OnAlarmFdReadableWithoutBlocking,
- weak_factory_.GetWeakPtr()));
+ alarm_fd_,
+ base::BindRepeating(&SimpleAlarmTimer::OnAlarmFdReadableWithoutBlocking,
+ weak_factory_.GetWeakPtr()));
}
}
-void AlarmTimer::OnAlarmFdReadableWithoutBlocking() {
+void SimpleAlarmTimer::OnAlarmFdReadableWithoutBlocking() {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
- DCHECK(base::Timer::IsRunning());
+ DCHECK(base::RetainingOneShotTimer::IsRunning());
// Read from |alarm_fd_| to ack the event.
char val[sizeof(uint64_t)];
@@ -118,52 +115,29 @@ void AlarmTimer::OnAlarmFdReadableWithoutBlocking() {
OnTimerFired();
}
-void AlarmTimer::OnTimerFired() {
+void SimpleAlarmTimer::OnTimerFired() {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
- DCHECK(base::Timer::IsRunning());
+ DCHECK(base::RetainingOneShotTimer::IsRunning());
DCHECK(pending_task_.get());
// Take ownership of the PendingTask to prevent it from being deleted if the
- // AlarmTimer is deleted.
+ // SimpleAlarmTimer is deleted.
const auto pending_user_task = std::move(pending_task_);
- base::WeakPtr<AlarmTimer> weak_ptr = weak_factory_.GetWeakPtr();
+ base::WeakPtr<SimpleAlarmTimer> weak_ptr = weak_factory_.GetWeakPtr();
// Run the task.
- TRACE_TASK_EXECUTION("AlarmTimer::OnTimerFired", *pending_user_task);
- base::debug::TaskAnnotator().RunTask("AlarmTimer::Reset",
+ TRACE_TASK_EXECUTION("SimpleAlarmTimer::OnTimerFired", *pending_user_task);
+ base::debug::TaskAnnotator().RunTask("SimpleAlarmTimer::Reset",
pending_user_task.get());
- // If the timer wasn't deleted, stopped or reset by the callback, reset or
- // stop it.
- if (weak_ptr.get()) {
- if (base::Timer::is_repeating())
- Reset();
- else
- Stop();
- }
+ // If the timer wasn't deleted, stopped or reset by the callback, stop it.
+ if (weak_ptr)
+ Stop();
}
-bool AlarmTimer::CanWakeFromSuspend() const {
+bool SimpleAlarmTimer::CanWakeFromSuspend() const {
return alarm_fd_ != -1;
}
-OneShotAlarmTimer::OneShotAlarmTimer() : AlarmTimer(false, false) {
-}
-
-OneShotAlarmTimer::~OneShotAlarmTimer() {
-}
-
-RepeatingAlarmTimer::RepeatingAlarmTimer() : AlarmTimer(true, true) {
-}
-
-RepeatingAlarmTimer::~RepeatingAlarmTimer() {
-}
-
-SimpleAlarmTimer::SimpleAlarmTimer() : AlarmTimer(true, false) {
-}
-
-SimpleAlarmTimer::~SimpleAlarmTimer() {
-}
-
} // namespace timers
diff --git a/chromium/components/timers/alarm_timer_chromeos.h b/chromium/components/timers/alarm_timer_chromeos.h
index d861aeeda0d..1ff689ec067 100644
--- a/chromium/components/timers/alarm_timer_chromeos.h
+++ b/chromium/components/timers/alarm_timer_chromeos.h
@@ -9,7 +9,7 @@
#include "base/files/file_descriptor_watcher_posix.h"
#include "base/macros.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
@@ -24,23 +24,25 @@ namespace timers {
// suspended state. For example, this is useful for running tasks that are
// needed for maintaining network connectivity, like sending heartbeat messages.
// Currently, this feature is only available on Chrome OS systems running linux
-// version 3.11 or higher. On all other platforms, the AlarmTimer behaves
+// version 3.11 or higher. On all other platforms, the SimpleAlarmTimer behaves
// exactly the same way as a regular Timer.
//
-// An AlarmTimer instance can only be used from the sequence on which it was
-// instantiated. Start() and Stop() must be called from a thread that supports
-// FileDescriptorWatcher.
-class AlarmTimer : public base::Timer {
+// A SimpleAlarmTimer instance can only be used from the sequence on which it
+// was instantiated. Start() and Stop() must be called from a thread that
+// supports FileDescriptorWatcher.
+//
+// A SimpleAlarmTimer only fires once but remembers the task that it was given
+// even after it has fired. Useful if you want to run the same task multiple
+// times but not at a regular interval.
+class SimpleAlarmTimer : public base::RetainingOneShotTimer {
public:
- ~AlarmTimer() override;
+ SimpleAlarmTimer();
+ ~SimpleAlarmTimer() override;
// Timer overrides.
void Stop() override;
void Reset() override;
- protected:
- AlarmTimer(bool retain_user_task, bool is_repeating);
-
private:
// Called when |alarm_fd_| is readable without blocking. Reads data from
// |alarm_fd_| and calls OnTimerFired().
@@ -70,37 +72,9 @@ class AlarmTimer : public base::Timer {
std::unique_ptr<base::PendingTask> pending_task_;
// Used to invalidate pending callbacks.
- base::WeakPtrFactory<AlarmTimer> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(AlarmTimer);
-};
+ base::WeakPtrFactory<SimpleAlarmTimer> weak_factory_;
-// As its name suggests, a OneShotAlarmTimer runs a given task once. It does
-// not remember the task that was given to it after it has fired and does not
-// repeat. Useful for fire-and-forget tasks.
-class OneShotAlarmTimer : public AlarmTimer {
- public:
- OneShotAlarmTimer();
- ~OneShotAlarmTimer() override;
-};
-
-// A RepeatingAlarmTimer takes a task and delay and repeatedly runs the task
-// using the specified delay as an interval between the runs until it is
-// explicitly stopped. It remembers both the task and the delay it was given
-// after it fires.
-class RepeatingAlarmTimer : public AlarmTimer {
- public:
- RepeatingAlarmTimer();
- ~RepeatingAlarmTimer() override;
-};
-
-// A SimpleAlarmTimer only fires once but remembers the task that it was given
-// even after it has fired. Useful if you want to run the same task multiple
-// times but not at a regular interval.
-class SimpleAlarmTimer : public AlarmTimer {
- public:
- SimpleAlarmTimer();
- ~SimpleAlarmTimer() override;
+ DISALLOW_COPY_AND_ASSIGN(SimpleAlarmTimer);
};
} // namespace timers
diff --git a/chromium/components/timers/alarm_timer_unittest.cc b/chromium/components/timers/alarm_timer_unittest.cc
index ec822374550..868eb787abd 100644
--- a/chromium/components/timers/alarm_timer_unittest.cc
+++ b/chromium/components/timers/alarm_timer_unittest.cc
@@ -27,42 +27,49 @@ namespace timers {
namespace {
const base::TimeDelta kTenMilliseconds = base::TimeDelta::FromMilliseconds(10);
-class OneShotAlarmTimerTester {
+class AlarmTimerTester {
public:
- OneShotAlarmTimerTester(bool* did_run, base::TimeDelta delay)
+ AlarmTimerTester(bool* did_run,
+ base::TimeDelta delay,
+ base::OnceClosure quit_closure)
: did_run_(did_run),
+ quit_closure_(std::move(quit_closure)),
delay_(delay),
- timer_(new timers::OneShotAlarmTimer()) {}
+ timer_(new timers::SimpleAlarmTimer()) {}
void Start() {
- timer_->Start(FROM_HERE, delay_, base::Bind(&OneShotAlarmTimerTester::Run,
- base::Unretained(this)));
+ timer_->Start(
+ FROM_HERE, delay_,
+ base::BindRepeating(&AlarmTimerTester::Run, base::Unretained(this)));
}
private:
void Run() {
*did_run_ = true;
-
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
+ if (quit_closure_)
+ std::move(quit_closure_).Run();
}
bool* did_run_;
+ base::OnceClosure quit_closure_;
const base::TimeDelta delay_;
- std::unique_ptr<timers::OneShotAlarmTimer> timer_;
+ std::unique_ptr<timers::SimpleAlarmTimer> timer_;
- DISALLOW_COPY_AND_ASSIGN(OneShotAlarmTimerTester);
+ DISALLOW_COPY_AND_ASSIGN(AlarmTimerTester);
};
-class OneShotSelfDeletingAlarmTimerTester {
+class SelfDeletingAlarmTimerTester {
public:
- OneShotSelfDeletingAlarmTimerTester(bool* did_run, base::TimeDelta delay)
+ SelfDeletingAlarmTimerTester(bool* did_run,
+ base::TimeDelta delay,
+ base::OnceClosure quit_closure)
: did_run_(did_run),
+ quit_closure_(std::move(quit_closure)),
delay_(delay),
- timer_(new timers::OneShotAlarmTimer()) {}
+ timer_(new timers::SimpleAlarmTimer()) {}
void Start() {
timer_->Start(FROM_HERE, delay_,
- base::Bind(&OneShotSelfDeletingAlarmTimerTester::Run,
- base::Unretained(this)));
+ base::BindRepeating(&SelfDeletingAlarmTimerTester::Run,
+ base::Unretained(this)));
}
private:
@@ -70,46 +77,16 @@ class OneShotSelfDeletingAlarmTimerTester {
*did_run_ = true;
timer_.reset();
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
+ if (quit_closure_)
+ std::move(quit_closure_).Run();
}
bool* did_run_;
+ base::OnceClosure quit_closure_;
const base::TimeDelta delay_;
- std::unique_ptr<timers::OneShotAlarmTimer> timer_;
+ std::unique_ptr<timers::SimpleAlarmTimer> timer_;
- DISALLOW_COPY_AND_ASSIGN(OneShotSelfDeletingAlarmTimerTester);
-};
-
-class RepeatingAlarmTimerTester {
- public:
- RepeatingAlarmTimerTester(bool* did_run, base::TimeDelta delay)
- : did_run_(did_run),
- delay_(delay),
- counter_(10),
- timer_(new timers::RepeatingAlarmTimer()) {}
- void Start() {
- timer_->Start(FROM_HERE, delay_, base::Bind(&RepeatingAlarmTimerTester::Run,
- base::Unretained(this)));
- }
-
- private:
- void Run() {
- if (--counter_ == 0) {
- *did_run_ = true;
- timer_->Stop();
-
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
- }
- }
-
- bool* did_run_;
- const base::TimeDelta delay_;
- int counter_;
- std::unique_ptr<timers::RepeatingAlarmTimer> timer_;
-
- DISALLOW_COPY_AND_ASSIGN(RepeatingAlarmTimerTester);
+ DISALLOW_COPY_AND_ASSIGN(SelfDeletingAlarmTimerTester);
};
} // namespace
@@ -118,26 +95,28 @@ class RepeatingAlarmTimerTester {
// Each test is run against each type of MessageLoop. That way we are sure
// that timers work properly in all configurations.
-TEST(AlarmTimerTest, OneShotAlarmTimer) {
+TEST(AlarmTimerTest, SimpleAlarmTimer) {
base::MessageLoopForIO loop;
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+ base::RunLoop run_loop;
bool did_run = false;
- OneShotAlarmTimerTester f(&did_run, kTenMilliseconds);
+ AlarmTimerTester f(&did_run, kTenMilliseconds,
+ run_loop.QuitWhenIdleClosure());
f.Start();
- base::RunLoop().Run();
+ run_loop.Run();
EXPECT_TRUE(did_run);
}
-TEST(AlarmTimerTest, OneShotAlarmTimer_Cancel) {
+TEST(AlarmTimerTest, SimpleAlarmTimer_Cancel) {
base::MessageLoopForIO loop;
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
bool did_run_a = false;
- OneShotAlarmTimerTester* a =
- new OneShotAlarmTimerTester(&did_run_a, kTenMilliseconds);
+ AlarmTimerTester* a =
+ new AlarmTimerTester(&did_run_a, kTenMilliseconds, base::OnceClosure());
// This should run before the timer expires.
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
@@ -145,11 +124,13 @@ TEST(AlarmTimerTest, OneShotAlarmTimer_Cancel) {
// Now start the timer.
a->Start();
+ base::RunLoop run_loop;
bool did_run_b = false;
- OneShotAlarmTimerTester b(&did_run_b, kTenMilliseconds);
+ AlarmTimerTester b(&did_run_b, kTenMilliseconds,
+ run_loop.QuitWhenIdleClosure());
b.Start();
- base::RunLoop().Run();
+ run_loop.Run();
EXPECT_FALSE(did_run_a);
EXPECT_TRUE(did_run_b);
@@ -157,76 +138,43 @@ TEST(AlarmTimerTest, OneShotAlarmTimer_Cancel) {
// If underlying timer does not handle this properly, we will crash or fail
// in full page heap environment.
-TEST(AlarmTimerTest, OneShotSelfDeletingAlarmTimer) {
- base::MessageLoopForIO loop;
- base::FileDescriptorWatcher file_descriptor_watcher(&loop);
-
- bool did_run = false;
- OneShotSelfDeletingAlarmTimerTester f(&did_run, kTenMilliseconds);
- f.Start();
-
- base::RunLoop().Run();
-
- EXPECT_TRUE(did_run);
-}
-
-TEST(AlarmTimerTest, RepeatingAlarmTimer) {
+TEST(AlarmTimerTest, SelfDeletingAlarmTimer) {
base::MessageLoopForIO loop;
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+ base::RunLoop run_loop;
bool did_run = false;
- RepeatingAlarmTimerTester f(&did_run, kTenMilliseconds);
+ SelfDeletingAlarmTimerTester f(&did_run, kTenMilliseconds,
+ run_loop.QuitWhenIdleClosure());
f.Start();
- base::RunLoop().Run();
+ run_loop.Run();
EXPECT_TRUE(did_run);
}
-TEST(AlarmTimerTest, RepeatingAlarmTimer_Cancel) {
- base::MessageLoopForIO loop;
- base::FileDescriptorWatcher file_descriptor_watcher(&loop);
-
- bool did_run_a = false;
- RepeatingAlarmTimerTester* a =
- new RepeatingAlarmTimerTester(&did_run_a, kTenMilliseconds);
-
- // This should run before the timer expires.
- base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
-
- // Now start the timer.
- a->Start();
-
- bool did_run_b = false;
- RepeatingAlarmTimerTester b(&did_run_b, kTenMilliseconds);
- b.Start();
-
- base::RunLoop().Run();
-
- EXPECT_FALSE(did_run_a);
- EXPECT_TRUE(did_run_b);
-}
-
-TEST(AlarmTimerTest, RepeatingAlarmTimerZeroDelay) {
+TEST(AlarmTimerTest, AlarmTimerZeroDelay) {
base::MessageLoopForIO loop;
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
+ base::RunLoop run_loop;
bool did_run = false;
- RepeatingAlarmTimerTester f(&did_run, base::TimeDelta());
+ AlarmTimerTester f(&did_run, base::TimeDelta(),
+ run_loop.QuitWhenIdleClosure());
f.Start();
- base::RunLoop().Run();
+ run_loop.Run();
EXPECT_TRUE(did_run);
}
-TEST(AlarmTimerTest, RepeatingAlarmTimerZeroDelay_Cancel) {
+TEST(AlarmTimerTest, AlarmTimerZeroDelay_Cancel) {
base::MessageLoopForIO loop;
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
bool did_run_a = false;
- RepeatingAlarmTimerTester* a =
- new RepeatingAlarmTimerTester(&did_run_a, base::TimeDelta());
+ AlarmTimerTester* a =
+ new AlarmTimerTester(&did_run_a, base::TimeDelta(), base::OnceClosure());
// This should run before the timer expires.
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
@@ -234,11 +182,13 @@ TEST(AlarmTimerTest, RepeatingAlarmTimerZeroDelay_Cancel) {
// Now start the timer.
a->Start();
+ base::RunLoop run_loop;
bool did_run_b = false;
- RepeatingAlarmTimerTester b(&did_run_b, base::TimeDelta());
+ AlarmTimerTester b(&did_run_b, base::TimeDelta(),
+ run_loop.QuitWhenIdleClosure());
b.Start();
- base::RunLoop().Run();
+ run_loop.Run();
EXPECT_FALSE(did_run_a);
EXPECT_TRUE(did_run_b);
@@ -254,10 +204,10 @@ TEST(AlarmTimerTest, MessageLoopShutdown) {
auto loop = std::make_unique<base::MessageLoopForIO>();
auto file_descriptor_watcher =
std::make_unique<base::FileDescriptorWatcher>(loop.get());
- OneShotAlarmTimerTester a(&did_run, kTenMilliseconds);
- OneShotAlarmTimerTester b(&did_run, kTenMilliseconds);
- OneShotAlarmTimerTester c(&did_run, kTenMilliseconds);
- OneShotAlarmTimerTester d(&did_run, kTenMilliseconds);
+ AlarmTimerTester a(&did_run, kTenMilliseconds, base::OnceClosure());
+ AlarmTimerTester b(&did_run, kTenMilliseconds, base::OnceClosure());
+ AlarmTimerTester c(&did_run, kTenMilliseconds, base::OnceClosure());
+ AlarmTimerTester d(&did_run, kTenMilliseconds, base::OnceClosure());
a.Start();
b.Start();
@@ -269,7 +219,7 @@ TEST(AlarmTimerTest, MessageLoopShutdown) {
// MessageLoop and FileDescriptorWatcher destruct.
file_descriptor_watcher.reset();
loop.reset();
- } // OneShotTimers destruct. SHOULD NOT CRASH, of course.
+ } // SimpleAlarmTimers destruct. SHOULD NOT CRASH, of course.
EXPECT_FALSE(did_run);
}
@@ -278,23 +228,6 @@ TEST(AlarmTimerTest, NonRepeatIsRunning) {
{
base::MessageLoopForIO loop;
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
- timers::OneShotAlarmTimer timer;
- EXPECT_FALSE(timer.IsRunning());
- timer.Start(FROM_HERE, base::TimeDelta::FromDays(1), base::DoNothing());
-
- // Allow FileDescriptorWatcher to start watching the timer. Without this, a
- // task posted by FileDescriptorWatcher::WatchReadable() is leaked.
- base::RunLoop().RunUntilIdle();
-
- EXPECT_TRUE(timer.IsRunning());
- timer.Stop();
- EXPECT_FALSE(timer.IsRunning());
- EXPECT_TRUE(timer.user_task().is_null());
- }
-
- {
- base::MessageLoopForIO loop;
- base::FileDescriptorWatcher file_descriptor_watcher(&loop);
timers::SimpleAlarmTimer timer;
EXPECT_FALSE(timer.IsRunning());
timer.Start(FROM_HERE, base::TimeDelta::FromDays(1), base::DoNothing());
@@ -313,28 +246,6 @@ TEST(AlarmTimerTest, NonRepeatIsRunning) {
}
}
-TEST(AlarmTimerTest, RetainRepeatIsRunning) {
- base::MessageLoopForIO loop;
- base::FileDescriptorWatcher file_descriptor_watcher(&loop);
- timers::RepeatingAlarmTimer timer;
- EXPECT_FALSE(timer.IsRunning());
- timer.Start(FROM_HERE, base::TimeDelta::FromDays(1), base::DoNothing());
-
- // Allow FileDescriptorWatcher to start watching the timer. Without this, a
- // task posted by FileDescriptorWatcher::WatchReadable() is leaked.
- base::RunLoop().RunUntilIdle();
-
- EXPECT_TRUE(timer.IsRunning());
- timer.Reset();
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(timer.IsRunning());
- timer.Stop();
- EXPECT_FALSE(timer.IsRunning());
- timer.Reset();
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(timer.IsRunning());
-}
-
TEST(AlarmTimerTest, RetainNonRepeatIsRunning) {
base::MessageLoopForIO loop;
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
@@ -367,29 +278,34 @@ void ClearAllCallbackHappened() {
g_callback_happened2 = false;
}
-void SetCallbackHappened1() {
+void SetCallbackHappened1(base::OnceClosure quit_closure) {
g_callback_happened1 = true;
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
+ if (quit_closure)
+ std::move(quit_closure).Run();
}
-void SetCallbackHappened2() {
+void SetCallbackHappened2(base::OnceClosure quit_closure) {
g_callback_happened2 = true;
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
+ if (quit_closure)
+ std::move(quit_closure).Run();
}
TEST(AlarmTimerTest, ContinuationStopStart) {
ClearAllCallbackHappened();
base::MessageLoopForIO loop;
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
- timers::OneShotAlarmTimer timer;
+ timers::SimpleAlarmTimer timer;
timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(10),
- base::Bind(&SetCallbackHappened1));
+ base::BindRepeating(&SetCallbackHappened1,
+ base::DoNothing().Repeatedly()));
timer.Stop();
+
+ base::RunLoop run_loop;
timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(40),
- base::Bind(&SetCallbackHappened2));
- base::RunLoop().Run();
+ base::BindRepeating(&SetCallbackHappened2,
+ run_loop.QuitWhenIdleClosure()));
+ run_loop.Run();
+
EXPECT_FALSE(g_callback_happened1);
EXPECT_TRUE(g_callback_happened2);
}
@@ -398,13 +314,15 @@ TEST(AlarmTimerTest, ContinuationReset) {
ClearAllCallbackHappened();
base::MessageLoopForIO loop;
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
- timers::OneShotAlarmTimer timer;
+
+ base::RunLoop run_loop;
+ timers::SimpleAlarmTimer timer;
timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(10),
- base::Bind(&SetCallbackHappened1));
+ base::BindRepeating(&SetCallbackHappened1,
+ run_loop.QuitWhenIdleClosure()));
timer.Reset();
- // Since Reset happened before task ran, the user_task must not be cleared:
ASSERT_FALSE(timer.user_task().is_null());
- base::RunLoop().Run();
+ run_loop.Run();
EXPECT_TRUE(g_callback_happened1);
}
@@ -416,12 +334,12 @@ TEST(AlarmTimerTest, DeleteTimerWhileCallbackIsRunning) {
base::RunLoop run_loop;
// Will be deleted by the callback.
- timers::OneShotAlarmTimer* timer = new timers::OneShotAlarmTimer;
+ timers::SimpleAlarmTimer* timer = new timers::SimpleAlarmTimer;
timer->Start(
FROM_HERE, base::TimeDelta::FromMilliseconds(10),
- base::Bind(
- [](timers::OneShotAlarmTimer* timer, base::RunLoop* run_loop) {
+ base::BindRepeating(
+ [](timers::SimpleAlarmTimer* timer, base::RunLoop* run_loop) {
delete timer;
run_loop->Quit();
},
@@ -437,12 +355,12 @@ TEST(AlarmTimerTest, DeleteTimerWhileCallbackIsRunningZeroDelay) {
base::RunLoop run_loop;
// Will be deleted by the callback.
- timers::OneShotAlarmTimer* timer = new timers::OneShotAlarmTimer;
+ timers::SimpleAlarmTimer* timer = new timers::SimpleAlarmTimer;
timer->Start(
FROM_HERE, base::TimeDelta(),
- base::Bind(
- [](timers::OneShotAlarmTimer* timer, base::RunLoop* run_loop) {
+ base::BindRepeating(
+ [](timers::SimpleAlarmTimer* timer, base::RunLoop* run_loop) {
delete timer;
run_loop->Quit();
},
diff --git a/chromium/components/toolbar/test_toolbar_model.cc b/chromium/components/toolbar/test_toolbar_model.cc
index 86ccbebfaac..61f7c3507ed 100644
--- a/chromium/components/toolbar/test_toolbar_model.cc
+++ b/chromium/components/toolbar/test_toolbar_model.cc
@@ -43,6 +43,10 @@ base::string16 TestToolbarModel::GetSecureVerboseText() const {
return base::string16();
}
+base::string16 TestToolbarModel::GetSecureAccessibilityText() const {
+ return base::string16();
+}
+
base::string16 TestToolbarModel::GetEVCertName() const {
return (security_level_ == security_state::EV_SECURE) ? ev_cert_name_
: base::string16();
diff --git a/chromium/components/toolbar/test_toolbar_model.h b/chromium/components/toolbar/test_toolbar_model.h
index e7be45347b8..780820f758f 100644
--- a/chromium/components/toolbar/test_toolbar_model.h
+++ b/chromium/components/toolbar/test_toolbar_model.h
@@ -30,6 +30,7 @@ class TestToolbarModel : public ToolbarModel {
bool ignore_editing) const override;
const gfx::VectorIcon& GetVectorIcon() const override;
base::string16 GetSecureVerboseText() const override;
+ base::string16 GetSecureAccessibilityText() const override;
base::string16 GetEVCertName() const override;
bool ShouldDisplayURL() const override;
bool IsOfflinePage() const override;
diff --git a/chromium/components/toolbar/toolbar_field_trial.cc b/chromium/components/toolbar/toolbar_field_trial.cc
index 3a2e1394f8c..4b03ab23c57 100644
--- a/chromium/components/toolbar/toolbar_field_trial.cc
+++ b/chromium/components/toolbar/toolbar_field_trial.cc
@@ -16,6 +16,8 @@ const char kSimplifyHttpsIndicatorParameterName[] = "treatment";
const char kSimplifyHttpsIndicatorParameterEvToSecure[] = "ev-to-secure";
const char kSimplifyHttpsIndicatorParameterSecureToLock[] = "secure-to-lock";
const char kSimplifyHttpsIndicatorParameterBothToLock[] = "both-to-lock";
+const char kSimplifyHttpsIndicatorParameterKeepSecureChip[] =
+ "keep-secure-chip";
} // namespace features
} // namespace toolbar
diff --git a/chromium/components/toolbar/toolbar_field_trial.h b/chromium/components/toolbar/toolbar_field_trial.h
index d7ac1971e2a..4b9dd0a7230 100644
--- a/chromium/components/toolbar/toolbar_field_trial.h
+++ b/chromium/components/toolbar/toolbar_field_trial.h
@@ -16,6 +16,8 @@ namespace features {
// - 'ev-to-secure': Show the "Secure" chip for pages with an EV certificate.
// - 'secure-to-lock': Show only the lock icon for non-EV https:// pages.
// - 'both-to-lock': Show only the lock icon for all https:// pages.
+// - 'keep-secure-chip': Show the old "Secure" chip for non-EV https:// pages.
+// The default behavior is the same as 'secure-to-lock'.
extern const base::Feature kSimplifyHttpsIndicator;
// The parameter name which controls the UI treatment.
@@ -25,6 +27,7 @@ extern const char kSimplifyHttpsIndicatorParameterName[];
extern const char kSimplifyHttpsIndicatorParameterEvToSecure[];
extern const char kSimplifyHttpsIndicatorParameterSecureToLock[];
extern const char kSimplifyHttpsIndicatorParameterBothToLock[];
+extern const char kSimplifyHttpsIndicatorParameterKeepSecureChip[];
} // namespace features
} // namespace toolbar
diff --git a/chromium/components/toolbar/toolbar_model.h b/chromium/components/toolbar/toolbar_model.h
index 299a910d57e..cecb40aa689 100644
--- a/chromium/components/toolbar/toolbar_model.h
+++ b/chromium/components/toolbar/toolbar_model.h
@@ -51,12 +51,16 @@ class ToolbarModel {
// Returns the id of the icon to show to the left of the address, based on the
// current URL. When search term replacement is active, this returns a search
// icon. This doesn't cover specialized icons while the user is editing; see
- // OmniboxView::GetVectorIcon().
+ // OmniboxView::GetIcon().
virtual const gfx::VectorIcon& GetVectorIcon() const = 0;
- // Returns text for the omnibox secure verbose chip.
+ // Returns text for the omnibox secure verbose chip, displayed next to the
+ // security icon on certain platforms.
virtual base::string16 GetSecureVerboseText() const = 0;
+ // Returns text describing the security state for accessibility.
+ virtual base::string16 GetSecureAccessibilityText() const = 0;
+
// Returns the name of the EV cert holder. This returns an empty string if
// the security level is not EV_SECURE.
virtual base::string16 GetEVCertName() const = 0;
diff --git a/chromium/components/toolbar/toolbar_model_impl.cc b/chromium/components/toolbar/toolbar_model_impl.cc
index 689f02345fd..f308fbc4030 100644
--- a/chromium/components/toolbar/toolbar_model_impl.cc
+++ b/chromium/components/toolbar/toolbar_model_impl.cc
@@ -126,7 +126,7 @@ base::string16 ToolbarModelImpl::GetEVCertName() const {
// Note: cert is guaranteed non-NULL or the security level would be NONE.
scoped_refptr<net::X509Certificate> cert = delegate_->GetCertificate();
- DCHECK(cert.get());
+ DCHECK(cert);
// EV are required to have an organization name and country.
DCHECK(!cert->subject().organization_names.empty());
@@ -137,37 +137,13 @@ base::string16 ToolbarModelImpl::GetEVCertName() const {
base::UTF8ToUTF16(cert->subject().country_name));
}
-base::string16 ToolbarModelImpl::GetSecureVerboseText() const {
- if (IsOfflinePage())
- return l10n_util::GetStringUTF16(IDS_OFFLINE_VERBOSE_STATE);
-
- // Security UI study (https://crbug.com/803501): Change EV/Secure text.
- const std::string parameter =
- base::FeatureList::IsEnabled(toolbar::features::kSimplifyHttpsIndicator)
- ? base::GetFieldTrialParamValueByFeature(
- toolbar::features::kSimplifyHttpsIndicator,
- toolbar::features::kSimplifyHttpsIndicatorParameterName)
- : std::string();
+base::string16 ToolbarModelImpl::GetSecureText() const {
switch (GetSecurityLevel(false)) {
case security_state::HTTP_SHOW_WARNING:
return l10n_util::GetStringUTF16(IDS_NOT_SECURE_VERBOSE_STATE);
case security_state::EV_SECURE:
- if (parameter ==
- toolbar::features::kSimplifyHttpsIndicatorParameterEvToSecure) {
- return l10n_util::GetStringUTF16(IDS_SECURE_VERBOSE_STATE);
- }
- if (parameter ==
- toolbar::features::kSimplifyHttpsIndicatorParameterBothToLock) {
- return base::string16();
- }
return GetEVCertName();
case security_state::SECURE:
- if (parameter ==
- toolbar::features::kSimplifyHttpsIndicatorParameterSecureToLock ||
- parameter ==
- toolbar::features::kSimplifyHttpsIndicatorParameterBothToLock) {
- return base::string16();
- }
return l10n_util::GetStringUTF16(IDS_SECURE_VERBOSE_STATE);
case security_state::DANGEROUS:
return l10n_util::GetStringUTF16(delegate_->FailsMalwareCheck()
@@ -178,6 +154,45 @@ base::string16 ToolbarModelImpl::GetSecureVerboseText() const {
}
}
+base::string16 ToolbarModelImpl::GetSecureVerboseText() const {
+ if (IsOfflinePage())
+ return l10n_util::GetStringUTF16(IDS_OFFLINE_VERBOSE_STATE);
+
+ // Security UI study (https://crbug.com/803501): Change EV/Secure text.
+ const std::string parameter =
+ base::FeatureList::IsEnabled(toolbar::features::kSimplifyHttpsIndicator)
+ ? base::GetFieldTrialParamValueByFeature(
+ toolbar::features::kSimplifyHttpsIndicator,
+ toolbar::features::kSimplifyHttpsIndicatorParameterName)
+ : std::string();
+
+ auto security_level = GetSecurityLevel(false);
+ if (security_level == security_state::EV_SECURE) {
+ if (parameter ==
+ toolbar::features::kSimplifyHttpsIndicatorParameterEvToSecure) {
+ return l10n_util::GetStringUTF16(IDS_SECURE_VERBOSE_STATE);
+ }
+ if (parameter ==
+ toolbar::features::kSimplifyHttpsIndicatorParameterBothToLock) {
+ return base::string16();
+ }
+ }
+ if (security_level == security_state::SECURE) {
+ if (parameter !=
+ toolbar::features::kSimplifyHttpsIndicatorParameterKeepSecureChip) {
+ return base::string16();
+ }
+ }
+ return GetSecureText();
+}
+
+base::string16 ToolbarModelImpl::GetSecureAccessibilityText() const {
+ if (IsOfflinePage())
+ return l10n_util::GetStringUTF16(IDS_OFFLINE_VERBOSE_STATE);
+
+ return GetSecureText();
+}
+
bool ToolbarModelImpl::ShouldDisplayURL() const {
return delegate_->ShouldDisplayURL();
}
diff --git a/chromium/components/toolbar/toolbar_model_impl.h b/chromium/components/toolbar/toolbar_model_impl.h
index bfa873c923a..4ecdb17adca 100644
--- a/chromium/components/toolbar/toolbar_model_impl.h
+++ b/chromium/components/toolbar/toolbar_model_impl.h
@@ -35,11 +35,14 @@ class ToolbarModelImpl : public ToolbarModel {
bool ignore_editing) const override;
const gfx::VectorIcon& GetVectorIcon() const override;
base::string16 GetSecureVerboseText() const override;
+ base::string16 GetSecureAccessibilityText() const override;
base::string16 GetEVCertName() const override;
bool ShouldDisplayURL() const override;
bool IsOfflinePage() const override;
private:
+ // Get the security text describing the current security state.
+ base::string16 GetSecureText() const;
base::string16 GetFormattedURL(
url_formatter::FormatUrlTypes format_types) const;
diff --git a/chromium/components/tracing/BUILD.gn b/chromium/components/tracing/BUILD.gn
index 2fedf988579..4ab521ee75e 100644
--- a/chromium/components/tracing/BUILD.gn
+++ b/chromium/components/tracing/BUILD.gn
@@ -67,7 +67,6 @@ source_set("unit_tests") {
test("tracing_perftests") {
sources = [
- "test/heap_profiler_perftest.cc",
"test/perf_test_helpers.cc",
"test/perf_test_helpers.h",
"test/trace_event_perftest.cc",
diff --git a/chromium/components/tracing/child/child_trace_message_filter.cc b/chromium/components/tracing/child/child_trace_message_filter.cc
index 99de2f65ff1..ab61795e289 100644
--- a/chromium/components/tracing/child/child_trace_message_filter.cc
+++ b/chromium/components/tracing/child/child_trace_message_filter.cc
@@ -75,6 +75,7 @@ void ChildTraceMessageFilter::OnHistogramChanged(
&ChildTraceMessageFilter::SendAbortBackgroundTracingMessage,
this));
}
+ return;
}
ipc_task_runner_->PostTask(
diff --git a/chromium/components/tracing/common/graphics_memory_dump_provider_android_unittest.cc b/chromium/components/tracing/common/graphics_memory_dump_provider_android_unittest.cc
index 3242288ea37..cd856bbe988 100644
--- a/chromium/components/tracing/common/graphics_memory_dump_provider_android_unittest.cc
+++ b/chromium/components/tracing/common/graphics_memory_dump_provider_android_unittest.cc
@@ -20,7 +20,7 @@ TEST(GraphicsMemoryDumpProviderTest, ParseResponse) {
const char* kDumpBaseName = GraphicsMemoryDumpProvider::kDumpBaseName;
base::trace_event::ProcessMemoryDump pmd(
- nullptr, {base::trace_event::MemoryDumpLevelOfDetail::DETAILED});
+ {base::trace_event::MemoryDumpLevelOfDetail::DETAILED});
auto* instance = GraphicsMemoryDumpProvider::GetInstance();
char buf[] = "graphics_total 12\ngraphics_pss 34\ngl_total 56\ngl_pss 78";
instance->ParseResponseAndAddToDump(buf, strlen(buf), &pmd);
diff --git a/chromium/components/tracing/common/trace_startup_config.cc b/chromium/components/tracing/common/trace_startup_config.cc
index 7a60710012c..8cf535a8e70 100644
--- a/chromium/components/tracing/common/trace_startup_config.cc
+++ b/chromium/components/tracing/common/trace_startup_config.cc
@@ -49,8 +49,7 @@ TraceStartupConfig* TraceStartupConfig::GetInstance() {
TraceStartupConfig::TraceStartupConfig()
: is_enabled_(false),
trace_config_(base::trace_event::TraceConfig()),
- startup_duration_(0),
- result_file_() {
+ startup_duration_(0) {
auto* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kTraceStartup)) {
@@ -162,11 +161,9 @@ int TraceStartupConfig::GetStartupDuration() const {
return startup_duration_;
}
-#if !defined(OS_ANDROID)
base::FilePath TraceStartupConfig::GetResultFile() const {
DCHECK(IsEnabled());
return result_file_;
}
-#endif
} // namespace tracing
diff --git a/chromium/components/tracing/common/trace_startup_config.h b/chromium/components/tracing/common/trace_startup_config.h
index a5acfdc368c..d68503c665f 100644
--- a/chromium/components/tracing/common/trace_startup_config.h
+++ b/chromium/components/tracing/common/trace_startup_config.h
@@ -43,15 +43,14 @@ namespace tracing {
// the result file after shutting the browser down.
//
// result_file: The file that contains the trace log. The default result
-// file path is chrometrace.log. Chrome will dump the trace
-// log to this file
+// file path is chrometrace.log, except on Android, where the
+// default path is generated by tracing controller. Chrome
+// will dump the trace log to this file
// 1) after startup_duration if it is specified;
// 2) or after browser shutdown if startup duration is 0.
// One can also stop tracing and get the result by other ways,
// e.g., by DevTools. In that case, the trace log will not be
// saved to this file.
-// Notice: This is not supported on Android. The result file
-// path will be generated by tracing controller.
//
// The trace config file can be specified by the --trace-config-file flag on
// most platforms except on Android, e.g., --trace-config-file=path/to/file/.
@@ -93,9 +92,7 @@ class TRACING_EXPORT TraceStartupConfig {
base::trace_event::TraceConfig GetTraceConfig() const;
int GetStartupDuration() const;
-#if !defined(OS_ANDROID)
base::FilePath GetResultFile() const;
-#endif
private:
// This allows constructor and destructor to be private and usable only
diff --git a/chromium/components/translate/core/browser/BUILD.gn b/chromium/components/translate/core/browser/BUILD.gn
index 54310351781..edcc98b22ef 100644
--- a/chromium/components/translate/core/browser/BUILD.gn
+++ b/chromium/components/translate/core/browser/BUILD.gn
@@ -63,6 +63,7 @@ static_library("browser") {
"//net",
"//services/metrics/public/cpp:metrics_cpp",
"//services/metrics/public/cpp:ukm_builders",
+ "//services/network/public/cpp:cpp",
"//third_party/icu",
"//third_party/metrics_proto",
"//ui/base",
@@ -123,6 +124,8 @@ source_set("unit_tests") {
"//components/variations",
"//net:test_support",
"//services/metrics/public/cpp:ukm_builders",
+ "//services/network:test_support",
+ "//services/network/public/cpp:cpp",
"//testing/gtest",
"//third_party/metrics_proto",
"//ui/base",
diff --git a/chromium/components/ui_devtools/views/ui_devtools_unittest.cc b/chromium/components/ui_devtools/views/ui_devtools_unittest.cc
index 948f7f29222..e7b23b4669c 100644
--- a/chromium/components/ui_devtools/views/ui_devtools_unittest.cc
+++ b/chromium/components/ui_devtools/views/ui_devtools_unittest.cc
@@ -35,7 +35,7 @@ const SkColor kBorderColor = SK_ColorBLUE;
class TestView : public views::View {
public:
- TestView(const char* name) : views::View(), name_(name) {}
+ TestView(const char* name) : name_(name) {}
const char* GetClassName() const override { return name_; }
diff --git a/chromium/components/ui_devtools/viz_views/frame_sink_element.cc b/chromium/components/ui_devtools/viz_views/frame_sink_element.cc
index 8298c13324c..209ff71c9ae 100644
--- a/chromium/components/ui_devtools/viz_views/frame_sink_element.cc
+++ b/chromium/components/ui_devtools/viz_views/frame_sink_element.cc
@@ -4,6 +4,7 @@
#include "components/ui_devtools/viz_views/frame_sink_element.h"
+#include "base/strings/string_piece.h"
#include "components/ui_devtools/Protocol.h"
#include "components/ui_devtools/ui_element_delegate.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
@@ -92,8 +93,7 @@ std::unique_ptr<protocol::Array<std::string>> FrameSinkElement::GetAttributes()
attributes->addItem(frame_sink_id_.ToString());
attributes->addItem("Title");
attributes->addItem(
- frame_sink_manager_->surface_manager()->GetFrameSinkDebugLabel(
- frame_sink_id_));
+ (frame_sink_manager_->GetFrameSinkDebugLabel(frame_sink_id_)).data());
return attributes;
}
diff --git a/chromium/components/ukm/BUILD.gn b/chromium/components/ukm/BUILD.gn
index 2f7e20991cf..ff802a95f79 100644
--- a/chromium/components/ukm/BUILD.gn
+++ b/chromium/components/ukm/BUILD.gn
@@ -56,6 +56,10 @@ static_library("observers") {
"//components/history/core/browser",
"//components/sync",
]
+
+ public_deps = [
+ "//components/unified_consent",
+ ]
}
static_library("test_support") {
@@ -95,6 +99,7 @@ source_set("unit_tests") {
"//components/prefs:test_support",
"//components/sync",
"//components/sync:test_support_driver",
+ "//components/sync_preferences:test_support",
"//components/variations",
"//net:test_support",
"//services/metrics/public/cpp:ukm_builders",
diff --git a/chromium/components/ukm/DEPS b/chromium/components/ukm/DEPS
index 74b50a011d2..053cb841a7a 100644
--- a/chromium/components/ukm/DEPS
+++ b/chromium/components/ukm/DEPS
@@ -1,9 +1,16 @@
include_rules = [
"+components/metrics",
"+components/prefs",
+ "+components/unified_consent",
"+components/variations",
"+mojo/public",
"+services/metrics/public",
"+third_party/metrics_proto",
"+third_party/zlib/google",
]
+
+specific_include_rules = {
+ ".*unittest\.cc": [
+ "+components/sync_preferences/testing_pref_service_syncable.h",
+ ]
+} \ No newline at end of file
diff --git a/chromium/components/ukm/content/BUILD.gn b/chromium/components/ukm/content/BUILD.gn
index 203c75cd0d6..e130330d117 100644
--- a/chromium/components/ukm/content/BUILD.gn
+++ b/chromium/components/ukm/content/BUILD.gn
@@ -4,6 +4,8 @@
static_library("content") {
sources = [
+ "app_source_url_recorder.cc",
+ "app_source_url_recorder.h",
"source_url_recorder.cc",
"source_url_recorder.h",
]
@@ -20,6 +22,7 @@ static_library("content") {
source_set("unit_tests") {
testonly = true
sources = [
+ "app_source_url_recorder_test.cc",
"source_url_recorder_test.cc",
]
deps = [
diff --git a/chromium/components/ukm/content/app_source_url_recorder.cc b/chromium/components/ukm/content/app_source_url_recorder.cc
new file mode 100644
index 00000000000..ea69b854596
--- /dev/null
+++ b/chromium/components/ukm/content/app_source_url_recorder.cc
@@ -0,0 +1,50 @@
+// 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/ukm/content/app_source_url_recorder.h"
+
+#include "base/atomic_sequence_num.h"
+#include "services/metrics/public/cpp/delegating_ukm_recorder.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "url/gurl.h"
+
+namespace ukm {
+
+SourceId AssignNewAppId() {
+ static base::AtomicSequenceNumber seq;
+ return ConvertToSourceId(seq.GetNext() + 1, SourceIdType::APP_ID);
+}
+
+SourceId AppSourceUrlRecorder::GetSourceIdForApp(AppType type,
+ const std::string& id) {
+ GURL url;
+ if (type == AppType::kArc)
+ url = GURL("app://play/" + id);
+ else if (type == AppType::kChromeExtension)
+ url = GURL("chrome-extension://" + id);
+ else
+ return kInvalidSourceId;
+
+ return GetSourceIdForUrl(url);
+}
+
+SourceId AppSourceUrlRecorder::GetSourceIdForPWA(const GURL& url) {
+ return GetSourceIdForUrl(url);
+}
+
+SourceId AppSourceUrlRecorder::GetSourceIdForUrl(const GURL& url) {
+ ukm::DelegatingUkmRecorder* const recorder =
+ ukm::DelegatingUkmRecorder::Get();
+ if (!recorder)
+ return kInvalidSourceId;
+
+ const SourceId source_id = AssignNewAppId();
+ if (base::FeatureList::IsEnabled(kUkmAppLogging)) {
+ recorder->UpdateAppURL(source_id, url);
+ }
+ return source_id;
+}
+
+} // namespace ukm
diff --git a/chromium/components/ukm/content/app_source_url_recorder.h b/chromium/components/ukm/content/app_source_url_recorder.h
new file mode 100644
index 00000000000..1cdbf13ddac
--- /dev/null
+++ b/chromium/components/ukm/content/app_source_url_recorder.h
@@ -0,0 +1,42 @@
+// 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_UKM_CONTENT_APP_SOURCE_URL_RECORDER_H_
+#define COMPONENTS_UKM_CONTENT_APP_SOURCE_URL_RECORDER_H_
+
+#include "services/metrics/public/cpp/ukm_source_id.h"
+
+#include "base/feature_list.h"
+
+#include <string>
+
+class GURL;
+
+namespace ukm {
+
+const base::Feature kUkmAppLogging{"UkmAppLogging",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+enum class AppType { kArc, kChromeExtension };
+
+class AppSourceUrlRecorder {
+ private:
+ friend class AppSourceUrlRecorderTest;
+
+ // Get a UKM SourceId for the app.
+ // Generates a url for the source depending upon AppType:
+ // kArc: app://play/id
+ // kChromeExtension: chrome-extension://id/
+ static SourceId GetSourceIdForApp(AppType type, const std::string& id);
+
+ // Get a UKM SourceId for a PWA.
+ static SourceId GetSourceIdForPWA(const GURL& url);
+
+ // For internal use only.
+ static SourceId GetSourceIdForUrl(const GURL& url);
+};
+
+} // namespace ukm
+
+#endif // COMPONENTS_UKM_CONTENT_APP_SOURCE_URL_RECORDER_H_
diff --git a/chromium/components/ukm/content/app_source_url_recorder_test.cc b/chromium/components/ukm/content/app_source_url_recorder_test.cc
new file mode 100644
index 00000000000..440e06428b5
--- /dev/null
+++ b/chromium/components/ukm/content/app_source_url_recorder_test.cc
@@ -0,0 +1,66 @@
+// 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/ukm/content/app_source_url_recorder.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "components/ukm/test_ukm_recorder.h"
+#include "components/ukm/ukm_source.h"
+#include "content/public/test/test_renderer_host.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace ukm {
+
+class AppSourceUrlRecorderTest : public content::RenderViewHostTestHarness {
+ public:
+ void SetUp() override {
+ scoped_feature_list_.InitAndEnableFeature(kUkmAppLogging);
+ content::RenderViewHostTestHarness::SetUp();
+ }
+
+ protected:
+ SourceId GetSourceIdForApp(AppType type, const std::string& id) {
+ return AppSourceUrlRecorder::GetSourceIdForApp(type, id);
+ }
+
+ SourceId GetSourceIdForPWA(const GURL& url) {
+ return AppSourceUrlRecorder::GetSourceIdForPWA(url);
+ }
+
+ base::test::ScopedFeatureList scoped_feature_list_;
+ TestAutoSetUkmRecorder test_ukm_recorder_;
+};
+
+TEST_F(AppSourceUrlRecorderTest, CheckPlay) {
+ SourceId id_play =
+ GetSourceIdForApp(AppType::kArc, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+
+ GURL expected_url_play("app://play/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+
+ const auto& sources = test_ukm_recorder_.GetSources();
+ EXPECT_EQ(1ul, sources.size());
+
+ ASSERT_NE(kInvalidSourceId, id_play);
+ auto it = sources.find(id_play);
+ ASSERT_NE(sources.end(), it);
+ EXPECT_EQ(expected_url_play, it->second->url());
+ EXPECT_TRUE(it->second->initial_url().is_empty());
+}
+
+TEST_F(AppSourceUrlRecorderTest, CheckPWA) {
+ GURL url("https://pwa_example_url.com");
+ SourceId id = GetSourceIdForPWA(url);
+
+ const auto& sources = test_ukm_recorder_.GetSources();
+ EXPECT_EQ(1ul, sources.size());
+
+ ASSERT_NE(kInvalidSourceId, id);
+ auto it = sources.find(id);
+ ASSERT_NE(sources.end(), it);
+ EXPECT_EQ(url, it->second->url());
+ EXPECT_TRUE(it->second->initial_url().is_empty());
+}
+
+} // namespace ukm
diff --git a/chromium/components/ukm/content/source_url_recorder.cc b/chromium/components/ukm/content/source_url_recorder.cc
index 3ca862510f4..41fd57fd681 100644
--- a/chromium/components/ukm/content/source_url_recorder.cc
+++ b/chromium/components/ukm/content/source_url_recorder.cc
@@ -68,7 +68,19 @@ class SourceUrlRecorderWebContentsObserver
base::flat_map<int64_t, GURL> pending_navigations_;
// Holds pending DocumentCreated events.
- using PendingEvent = std::pair<int64_t, bool>;
+ struct PendingEvent {
+ PendingEvent() = delete;
+ PendingEvent(int64_t source_id,
+ bool is_main_frame,
+ bool is_cross_origin_frame)
+ : source_id(source_id),
+ is_main_frame(is_main_frame),
+ is_cross_origin_frame(is_cross_origin_frame) {}
+
+ int64_t source_id;
+ bool is_main_frame;
+ bool is_cross_origin_frame;
+ };
std::vector<PendingEvent> pending_document_created_events_;
int64_t last_committed_source_id_;
@@ -138,8 +150,17 @@ ukm::SourceId SourceUrlRecorderWebContentsObserver::GetLastCommittedSourceId()
void SourceUrlRecorderWebContentsObserver::SetDocumentSourceId(
int64_t source_id) {
+ content::RenderFrameHost* main_frame = web_contents()->GetMainFrame();
+ content::RenderFrameHost* current_frame = bindings_.GetCurrentTargetFrame();
+ bool is_main_frame = main_frame == current_frame;
+ bool is_cross_origin_frame =
+ is_main_frame ? false
+ : !main_frame->GetLastCommittedOrigin().IsSameOriginWith(
+ current_frame->GetLastCommittedOrigin());
+
pending_document_created_events_.emplace_back(
- source_id, !bindings_.GetCurrentTargetFrame()->GetParent());
+ source_id, !bindings_.GetCurrentTargetFrame()->GetParent(),
+ is_cross_origin_frame);
MaybeFlushPendingEvents();
}
@@ -154,9 +175,10 @@ void SourceUrlRecorderWebContentsObserver::MaybeFlushPendingEvents() {
while (!pending_document_created_events_.empty()) {
auto record = pending_document_created_events_.back();
- ukm::builders::DocumentCreated(record.first)
+ ukm::builders::DocumentCreated(record.source_id)
.SetNavigationSourceId(last_committed_source_id_)
- .SetIsMainFrame(record.second)
+ .SetIsMainFrame(record.is_main_frame)
+ .SetIsCrossOriginFrame(record.is_cross_origin_frame)
.Record(ukm_recorder);
pending_document_created_events_.pop_back();
diff --git a/chromium/components/ukm/debug/ukm_debug_data_extractor.cc b/chromium/components/ukm/debug/ukm_debug_data_extractor.cc
index 0e471c3eb3e..d3e105c9076 100644
--- a/chromium/components/ukm/debug/ukm_debug_data_extractor.cc
+++ b/chromium/components/ukm/debug/ukm_debug_data_extractor.cc
@@ -62,12 +62,12 @@ std::string UkmDebugDataExtractor::GetHTMLData(UkmService* ukm_service) {
const auto& decode_map = ukm_service->decode_map_;
std::map<SourceId, SourceData> source_data;
- for (const auto& kv : ukm_service->sources_) {
+ for (const auto& kv : ukm_service->recordings_.sources) {
source_data[kv.first].source = kv.second.get();
}
- for (const auto& v : ukm_service->entries_) {
- source_data[v.get()->source_id].entries.push_back(v.get());
+ for (const auto& v : ukm_service->recordings_.entries) {
+ source_data[v->source_id].entries.push_back(v.get());
}
output.append("<h2>Sources</h2>");
diff --git a/chromium/components/ukm/observers/sync_disable_observer.cc b/chromium/components/ukm/observers/sync_disable_observer.cc
index f6d72866cfd..820dbb3ca31 100644
--- a/chromium/components/ukm/observers/sync_disable_observer.cc
+++ b/chromium/components/ukm/observers/sync_disable_observer.cc
@@ -9,6 +9,9 @@
#include "base/stl_util.h"
#include "components/sync/driver/sync_token_status.h"
#include "components/sync/engine/connection_status.h"
+#include "components/unified_consent/url_keyed_data_collection_consent_helper.h"
+
+using unified_consent::UrlKeyedDataCollectionConsentHelper;
namespace ukm {
@@ -34,6 +37,7 @@ enum DisableInfo {
DISABLED_BY_HISTORY_CONNECTED_PASSPHRASE,
DISABLED_BY_INITIALIZED_CONNECTED_PASSPHRASE,
DISABLED_BY_HISTORY_INITIALIZED_CONNECTED_PASSPHRASE,
+ DISABLED_BY_ANONYMIZED_DATA_COLLECTION,
MAX_DISABLE_INFO
};
@@ -43,16 +47,30 @@ void RecordDisableInfo(DisableInfo info) {
} // namespace
-SyncDisableObserver::SyncDisableObserver()
- : sync_observer_(this),
- all_histories_enabled_(false),
- all_extensions_enabled_(false) {}
+SyncDisableObserver::SyncDisableObserver() : sync_observer_(this) {}
+
+SyncDisableObserver::~SyncDisableObserver() {
+ for (const auto& entry : consent_helpers_) {
+ entry.second->RemoveObserver(this);
+ }
+}
+
+bool SyncDisableObserver::SyncState::AllowsUkm() const {
+ if (anonymized_data_collection_state == DataCollectionState::kIgnored)
+ return history_enabled && initialized && connected && !passphrase_protected;
+ else
+ return anonymized_data_collection_state == DataCollectionState::kEnabled;
+}
-SyncDisableObserver::~SyncDisableObserver() {}
+bool SyncDisableObserver::SyncState::AllowsUkmWithExtension() const {
+ return AllowsUkm() && extensions_enabled && initialized && connected &&
+ !passphrase_protected;
+}
// static
SyncDisableObserver::SyncState SyncDisableObserver::GetSyncState(
- syncer::SyncService* sync_service) {
+ syncer::SyncService* sync_service,
+ UrlKeyedDataCollectionConsentHelper* consent_helper) {
syncer::SyncTokenStatus status = sync_service->GetSyncTokenStatus();
SyncState state;
state.history_enabled = sync_service->GetPreferredDataTypes().Has(
@@ -64,26 +82,46 @@ SyncDisableObserver::SyncState SyncDisableObserver::GetSyncState(
status.connection_status == syncer::CONNECTION_OK;
state.passphrase_protected =
state.initialized && sync_service->IsUsingSecondaryPassphrase();
+ if (consent_helper) {
+ state.anonymized_data_collection_state =
+ consent_helper->IsEnabled() ? DataCollectionState::kEnabled
+ : DataCollectionState::kDisabled;
+ }
return state;
}
void SyncDisableObserver::ObserveServiceForSyncDisables(
- syncer::SyncService* sync_service) {
- previous_states_[sync_service] = GetSyncState(sync_service);
+ syncer::SyncService* sync_service,
+ PrefService* prefs,
+ bool is_unified_consent_enabled) {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> consent_helper;
+ if (is_unified_consent_enabled) {
+ consent_helper = UrlKeyedDataCollectionConsentHelper::
+ NewAnonymizedDataCollectionConsentHelper(true, prefs, sync_service);
+ }
+
+ SyncState state = GetSyncState(sync_service, consent_helper.get());
+ previous_states_[sync_service] = state;
+
+ if (consent_helper) {
+ consent_helper->AddObserver(this);
+ consent_helpers_[sync_service] = std::move(consent_helper);
+ }
sync_observer_.Add(sync_service);
UpdateAllProfileEnabled(false);
}
void SyncDisableObserver::UpdateAllProfileEnabled(bool must_purge) {
- bool all_enabled = CheckSyncStateOnAllProfiles();
- bool all_extensions_enabled =
- all_enabled && CheckSyncStateForExtensionsOnAllProfiles();
+ bool all_sync_states_allow_ukm = CheckSyncStateOnAllProfiles();
+ bool all_sync_states_allow_extension_ukm =
+ all_sync_states_allow_ukm && CheckSyncStateForExtensionsOnAllProfiles();
// Any change in sync settings needs to call OnSyncPrefsChanged so that the
// new settings take effect.
- if (must_purge || (all_enabled != all_histories_enabled_) ||
- (all_extensions_enabled != all_extensions_enabled_)) {
- all_histories_enabled_ = all_enabled;
- all_extensions_enabled_ = all_extensions_enabled;
+ if (must_purge || (all_sync_states_allow_ukm != all_sync_states_allow_ukm_) ||
+ (all_sync_states_allow_extension_ukm !=
+ all_sync_states_allow_extension_ukm_)) {
+ all_sync_states_allow_ukm_ = all_sync_states_allow_ukm;
+ all_sync_states_allow_extension_ukm_ = all_sync_states_allow_extension_ukm;
OnSyncPrefsChanged(must_purge);
}
}
@@ -93,17 +131,23 @@ bool SyncDisableObserver::CheckSyncStateOnAllProfiles() {
return false;
for (const auto& kv : previous_states_) {
const SyncDisableObserver::SyncState& state = kv.second;
- if (!state.history_enabled || !state.initialized || !state.connected ||
- state.passphrase_protected) {
+ if (!state.AllowsUkm()) {
int disabled_by = 0;
- if (!state.history_enabled)
- disabled_by |= 1 << 0;
- if (!state.initialized)
- disabled_by |= 1 << 1;
- if (!state.connected)
- disabled_by |= 1 << 2;
- if (state.passphrase_protected)
- disabled_by |= 1 << 3;
+ if (state.anonymized_data_collection_state ==
+ DataCollectionState::kIgnored) {
+ if (!state.history_enabled)
+ disabled_by |= 1 << 0;
+ if (!state.initialized)
+ disabled_by |= 1 << 1;
+ if (!state.connected)
+ disabled_by |= 1 << 2;
+ if (state.passphrase_protected)
+ disabled_by |= 1 << 3;
+ } else {
+ DCHECK_EQ(DataCollectionState::kDisabled,
+ state.anonymized_data_collection_state);
+ disabled_by |= 1 << 4;
+ }
RecordDisableInfo(DisableInfo(disabled_by));
return false;
}
@@ -124,36 +168,60 @@ bool SyncDisableObserver::CheckSyncStateForExtensionsOnAllProfiles() {
}
void SyncDisableObserver::OnStateChanged(syncer::SyncService* sync) {
+ UrlKeyedDataCollectionConsentHelper* consent_helper = nullptr;
+ auto found = consent_helpers_.find(sync);
+ if (found != consent_helpers_.end())
+ consent_helper = found->second.get();
+ UpdateSyncState(sync, consent_helper);
+}
+
+void SyncDisableObserver::OnUrlKeyedDataCollectionConsentStateChanged(
+ unified_consent::UrlKeyedDataCollectionConsentHelper* consent_helper) {
+ DCHECK(consent_helper);
+ syncer::SyncService* sync_service = nullptr;
+ for (const auto& entry : consent_helpers_) {
+ if (consent_helper == entry.second.get()) {
+ sync_service = entry.first;
+ break;
+ }
+ }
+ DCHECK(sync_service);
+ UpdateSyncState(sync_service, consent_helper);
+}
+
+void SyncDisableObserver::UpdateSyncState(
+ syncer::SyncService* sync,
+ UrlKeyedDataCollectionConsentHelper* consent_helper) {
DCHECK(base::ContainsKey(previous_states_, sync));
- SyncDisableObserver::SyncState state = GetSyncState(sync);
const SyncDisableObserver::SyncState& previous_state = previous_states_[sync];
- bool must_purge =
- // Trigger a purge if history sync was disabled.
- (previous_state.history_enabled && !state.history_enabled) ||
- // Trigger a purge if engine has become disabled.
- (previous_state.initialized && !state.initialized) ||
- // Trigger a purge if the user added a passphrase. Since we can't detect
- // the use of a passphrase while the engine is not initialized, we may
- // miss the transition if the user adds a passphrase in this state.
- (previous_state.initialized && state.initialized &&
- !previous_state.passphrase_protected && state.passphrase_protected);
+ DCHECK(previous_state.anonymized_data_collection_state ==
+ DataCollectionState::kIgnored ||
+ consent_helper);
+ SyncDisableObserver::SyncState state = GetSyncState(sync, consent_helper);
+ // Trigger a purge if sync state no longer allows UKM.
+ bool must_purge = previous_state.AllowsUkm() && !state.AllowsUkm();
previous_states_[sync] = state;
UpdateAllProfileEnabled(must_purge);
}
void SyncDisableObserver::OnSyncShutdown(syncer::SyncService* sync) {
DCHECK(base::ContainsKey(previous_states_, sync));
+ auto found = consent_helpers_.find(sync);
+ if (found != consent_helpers_.end()) {
+ found->second->RemoveObserver(this);
+ consent_helpers_.erase(found);
+ }
sync_observer_.Remove(sync);
previous_states_.erase(sync);
UpdateAllProfileEnabled(false);
}
bool SyncDisableObserver::SyncStateAllowsUkm() {
- return all_histories_enabled_;
+ return all_sync_states_allow_ukm_;
}
bool SyncDisableObserver::SyncStateAllowsExtensionUkm() {
- return all_extensions_enabled_;
+ return all_sync_states_allow_extension_ukm_;
}
} // namespace ukm
diff --git a/chromium/components/ukm/observers/sync_disable_observer.h b/chromium/components/ukm/observers/sync_disable_observer.h
index 2de46181f0f..e051443a676 100644
--- a/chromium/components/ukm/observers/sync_disable_observer.h
+++ b/chromium/components/ukm/observers/sync_disable_observer.h
@@ -10,25 +10,38 @@
#include "base/scoped_observer.h"
#include "components/sync/driver/sync_service.h"
#include "components/sync/driver/sync_service_observer.h"
+#include "components/unified_consent/url_keyed_data_collection_consent_helper.h"
+
+class PrefService;
namespace ukm {
-// Observes the state of a set of SyncServices for changes to history sync
-// preferences. This is for used to trigger purging of local state when
-// sync is disabled on a profile and disabling recording when any non-syncing
-// profiles are active.
-class SyncDisableObserver : public syncer::SyncServiceObserver {
+// Observer that monitors whether UKM is allowed for all profiles.
+//
+// For one profile, UKM is allowed under the following conditions:
+// * If unified consent is disabled, then UKM is allowed for the profile iff
+// sync history is active;
+// * If unified consent is enabled, then UKM is allowed for the profile iff
+// URL-keyed anonymized data collectiion is enabled.
+class SyncDisableObserver
+ : public syncer::SyncServiceObserver,
+ public unified_consent::UrlKeyedDataCollectionConsentHelper::Observer {
public:
SyncDisableObserver();
~SyncDisableObserver() override;
// Starts observing a service for sync disables.
- void ObserveServiceForSyncDisables(syncer::SyncService* sync_service);
-
- // Returns true iff sync is in a state that allows UKM to be enabled.
- // This means that for all profiles, sync is initialized, connected, has the
- // HISTORY_DELETE_DIRECTIVES data type enabled, and does not have a secondary
- // passphrase enabled.
+ void ObserveServiceForSyncDisables(syncer::SyncService* sync_service,
+ PrefService* pref_service,
+ bool is_unified_consent_enabled);
+
+ // Returns true iff all sync states alllow UKM to be enabled. This means that
+ // for all profiles:
+ // * If unified consent is disabled, then sync is initialized, connected, has
+ // the HISTORY_DELETE_DIRECTIVES data type enabled, and does not have a
+ // secondary passphrase enabled.
+ // * If unified consent is enabled, then URL-keyed anonymized data collection
+ // is enabled for that profile.
virtual bool SyncStateAllowsUkm();
// Returns true iff sync is in a state that allows UKM to capture extensions.
@@ -46,6 +59,11 @@ class SyncDisableObserver : public syncer::SyncServiceObserver {
void OnStateChanged(syncer::SyncService* sync) override;
void OnSyncShutdown(syncer::SyncService* sync) override;
+ // unified_consent::UrlKeyedDataCollectionConsentHelper::Observer:
+ void OnUrlKeyedDataCollectionConsentStateChanged(
+ unified_consent::UrlKeyedDataCollectionConsentHelper* consent_helper)
+ override;
+
// Recomputes all_profiles_enabled_ state from previous_states_;
void UpdateAllProfileEnabled(bool must_purge);
@@ -61,8 +79,29 @@ class SyncDisableObserver : public syncer::SyncServiceObserver {
ScopedObserver<syncer::SyncService, syncer::SyncServiceObserver>
sync_observer_;
+ enum class DataCollectionState {
+ // Matches the case when unified consent feature is disabled
+ kIgnored,
+ // Unified consent feature is enabled and the user has disabled URL-keyed
+ // anonymized data collection.
+ kDisabled,
+ // Unified consent feature is enabled and the user has enabled URL-keyed
+ // anonymized data collection.
+ kEnabled
+ };
+
// State data about sync services that we need to remember.
struct SyncState {
+ // Returns true if this sync state allows UKM:
+ // * If unified consent is disabled, then sync is initialized, connected,
+ // has history data type enabled, and does not have a secondary passphrase
+ // enabled.
+ // * If unified consent is enabled, then URL-keyed anonymized data
+ // collection is enabled.
+ bool AllowsUkm() const;
+ // Returns true if |AllowUkm| and if sync extensions are enabled.
+ bool AllowsUkmWithExtension() const;
+
// If the user has history sync enabled.
bool history_enabled = false;
// If the user has extension sync enabled.
@@ -74,21 +113,45 @@ class SyncDisableObserver : public syncer::SyncServiceObserver {
// Whether user data is hidden by a secondary passphrase.
// This is not valid if the state is not initialized.
bool passphrase_protected = false;
+
+ // Whether anonymized data collection is enabled.
+ // Note: This is not managed by sync service. It was added in this enum
+ // for convenience.
+ DataCollectionState anonymized_data_collection_state =
+ DataCollectionState::kIgnored;
};
+ // Updates the sync state for |sync| service. Updates all profiles if needed.
+ void UpdateSyncState(
+ syncer::SyncService* sync,
+ unified_consent::UrlKeyedDataCollectionConsentHelper* consent_helper);
+
// Gets the current state of a SyncService.
- static SyncState GetSyncState(syncer::SyncService* sync);
+ // A non-null |consent_helper| implies that Unified Consent is enabled.
+ static SyncState GetSyncState(
+ syncer::SyncService* sync,
+ unified_consent::UrlKeyedDataCollectionConsentHelper* consent_helper);
- // The list of services that had sync enabled when we last checked.
+ // The state of the sync services being observed.
std::map<syncer::SyncService*, SyncState> previous_states_;
- // Tracks if history sync was enabled on all profiles after the last state
- // change.
- bool all_histories_enabled_;
+ // The list of URL-keyed anonymized data collection consent helpers.
+ //
+ // Note: UrlKeyedDataCollectionConsentHelper do not rely on sync when
+ // unified consent feature is enabled but there must be exactly one per
+ // Chromium profile. As there is a single sync service per profile, it is safe
+ // to key them by sync service instead of introducing an additional map.
+ std::map<
+ syncer::SyncService*,
+ std::unique_ptr<unified_consent::UrlKeyedDataCollectionConsentHelper>>
+ consent_helpers_;
+
+ // Tracks if UKM is allowed on all profiles after the last state change.
+ bool all_sync_states_allow_ukm_ = false;
// Tracks if extension sync was enabled on all profiles after the last state
// change.
- bool all_extensions_enabled_;
+ bool all_sync_states_allow_extension_ukm_ = false;
DISALLOW_COPY_AND_ASSIGN(SyncDisableObserver);
};
diff --git a/chromium/components/ukm/observers/sync_disable_observer_unittest.cc b/chromium/components/ukm/observers/sync_disable_observer_unittest.cc
index f75fd2dff38..e8b3f43b686 100644
--- a/chromium/components/ukm/observers/sync_disable_observer_unittest.cc
+++ b/chromium/components/ukm/observers/sync_disable_observer_unittest.cc
@@ -8,6 +8,9 @@
#include "components/sync/driver/fake_sync_service.h"
#include "components/sync/driver/sync_token_status.h"
#include "components/sync/engine/connection_status.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"
#include "testing/gtest/include/gtest/gtest.h"
namespace ukm {
@@ -19,12 +22,13 @@ class MockSyncService : public syncer::FakeSyncService {
MockSyncService() {}
~MockSyncService() override { Shutdown(); }
- void SetStatus(bool has_passphrase, bool enabled) {
+ void SetStatus(bool has_passphrase, bool history_enabled) {
initialized_ = true;
has_passphrase_ = has_passphrase;
preferred_data_types_ =
- enabled ? syncer::ModelTypeSet(syncer::HISTORY_DELETE_DIRECTIVES)
- : syncer::ModelTypeSet();
+ history_enabled
+ ? syncer::ModelTypeSet(syncer::HISTORY_DELETE_DIRECTIVES)
+ : syncer::ModelTypeSet();
NotifyObserversOfStateChanged();
}
@@ -106,6 +110,17 @@ class TestSyncDisableObserver : public SyncDisableObserver {
class SyncDisableObserverTest : public testing::Test {
public:
SyncDisableObserverTest() {}
+ void RegisterUrlKeyedAnonymizedDataCollectionPref(
+ sync_preferences::TestingPrefServiceSyncable& prefs) {
+ unified_consent::UnifiedConsentService::RegisterPrefs(prefs.registry());
+ }
+
+ void SetUrlKeyedAnonymizedDataCollectionEnabled(PrefService* prefs,
+ bool enabled) {
+ prefs->SetBoolean(
+ unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ enabled);
+ }
private:
DISALLOW_COPY_AND_ASSIGN(SyncDisableObserverTest);
@@ -120,41 +135,58 @@ TEST_F(SyncDisableObserverTest, NoProfiles) {
EXPECT_FALSE(observer.ResetPurged());
}
-TEST_F(SyncDisableObserverTest, OneEnabled) {
+TEST_F(SyncDisableObserverTest, OneEnabled_UnifiedConsentDisabled) {
TestSyncDisableObserver observer;
MockSyncService sync;
sync.SetStatus(false, true);
- observer.ObserveServiceForSyncDisables(&sync);
+ observer.ObserveServiceForSyncDisables(&sync, nullptr, false);
EXPECT_TRUE(observer.SyncStateAllowsUkm());
EXPECT_TRUE(observer.ResetNotified());
EXPECT_FALSE(observer.ResetPurged());
}
-TEST_F(SyncDisableObserverTest, Passphrase) {
- TestSyncDisableObserver observer;
+TEST_F(SyncDisableObserverTest, OneEnabled_UnifiedConsentEnabled) {
+ sync_preferences::TestingPrefServiceSyncable prefs;
+ RegisterUrlKeyedAnonymizedDataCollectionPref(prefs);
+ SetUrlKeyedAnonymizedDataCollectionEnabled(&prefs, true);
+ MockSyncService sync;
+ for (bool has_passphrase : {true, false}) {
+ for (bool history_enabled : {true, false}) {
+ TestSyncDisableObserver observer;
+ sync.SetStatus(has_passphrase, history_enabled);
+ observer.ObserveServiceForSyncDisables(&sync, &prefs, true);
+ EXPECT_TRUE(observer.SyncStateAllowsUkm());
+ EXPECT_TRUE(observer.ResetNotified());
+ EXPECT_FALSE(observer.ResetPurged());
+ }
+ }
+}
+
+TEST_F(SyncDisableObserverTest, Passphrased_UnifiedConsentDisabled) {
MockSyncService sync;
sync.SetStatus(true, true);
- observer.ObserveServiceForSyncDisables(&sync);
+ TestSyncDisableObserver observer;
+ observer.ObserveServiceForSyncDisables(&sync, nullptr, false);
EXPECT_FALSE(observer.SyncStateAllowsUkm());
EXPECT_FALSE(observer.ResetNotified());
EXPECT_FALSE(observer.ResetPurged());
}
-TEST_F(SyncDisableObserverTest, HistoryDisabled) {
+TEST_F(SyncDisableObserverTest, HistoryDisabled_UnifiedConsentDisabled) {
TestSyncDisableObserver observer;
MockSyncService sync;
sync.SetStatus(false, false);
- observer.ObserveServiceForSyncDisables(&sync);
+ observer.ObserveServiceForSyncDisables(&sync, nullptr, false);
EXPECT_FALSE(observer.SyncStateAllowsUkm());
EXPECT_FALSE(observer.ResetNotified());
EXPECT_FALSE(observer.ResetPurged());
}
-TEST_F(SyncDisableObserverTest, AuthError) {
+TEST_F(SyncDisableObserverTest, AuthError_UnifiedConsentDisabled) {
TestSyncDisableObserver observer;
MockSyncService sync;
sync.SetStatus(false, true);
- observer.ObserveServiceForSyncDisables(&sync);
+ observer.ObserveServiceForSyncDisables(&sync, nullptr, false);
EXPECT_TRUE(observer.SyncStateAllowsUkm());
sync.SetConnectionStatus(syncer::CONNECTION_AUTH_ERROR);
EXPECT_FALSE(observer.SyncStateAllowsUkm());
@@ -162,28 +194,29 @@ TEST_F(SyncDisableObserverTest, AuthError) {
EXPECT_TRUE(observer.SyncStateAllowsUkm());
}
-TEST_F(SyncDisableObserverTest, MixedProfiles1) {
+TEST_F(SyncDisableObserverTest, MixedProfiles1_UnifiedConsentDisabled) {
TestSyncDisableObserver observer;
MockSyncService sync1;
sync1.SetStatus(false, false);
- observer.ObserveServiceForSyncDisables(&sync1);
+ observer.ObserveServiceForSyncDisables(&sync1, nullptr, false);
MockSyncService sync2;
sync2.SetStatus(false, true);
- observer.ObserveServiceForSyncDisables(&sync2);
+ observer.ObserveServiceForSyncDisables(&sync2, nullptr, false);
EXPECT_FALSE(observer.SyncStateAllowsUkm());
EXPECT_FALSE(observer.ResetNotified());
EXPECT_FALSE(observer.ResetPurged());
}
-TEST_F(SyncDisableObserverTest, MixedProfiles2) {
+TEST_F(SyncDisableObserverTest, MixedProfiles2_UnifiedConsentDisabled) {
TestSyncDisableObserver observer;
MockSyncService sync1;
sync1.SetStatus(false, true);
- observer.ObserveServiceForSyncDisables(&sync1);
+ observer.ObserveServiceForSyncDisables(&sync1, nullptr, false);
EXPECT_TRUE(observer.ResetNotified());
+
MockSyncService sync2;
sync2.SetStatus(false, false);
- observer.ObserveServiceForSyncDisables(&sync2);
+ observer.ObserveServiceForSyncDisables(&sync2, nullptr, false);
EXPECT_FALSE(observer.SyncStateAllowsUkm());
EXPECT_TRUE(observer.ResetNotified());
EXPECT_FALSE(observer.ResetPurged());
@@ -193,24 +226,44 @@ TEST_F(SyncDisableObserverTest, MixedProfiles2) {
EXPECT_FALSE(observer.ResetPurged());
}
-TEST_F(SyncDisableObserverTest, TwoEnabled) {
+TEST_F(SyncDisableObserverTest, TwoEnabled_UnifiedConsentDisabled) {
TestSyncDisableObserver observer;
MockSyncService sync1;
sync1.SetStatus(false, true);
- observer.ObserveServiceForSyncDisables(&sync1);
+ observer.ObserveServiceForSyncDisables(&sync1, nullptr, false);
EXPECT_TRUE(observer.ResetNotified());
MockSyncService sync2;
sync2.SetStatus(false, true);
- observer.ObserveServiceForSyncDisables(&sync2);
+ observer.ObserveServiceForSyncDisables(&sync2, nullptr, false);
+ EXPECT_TRUE(observer.SyncStateAllowsUkm());
+ EXPECT_FALSE(observer.ResetNotified());
+ EXPECT_FALSE(observer.ResetPurged());
+}
+
+TEST_F(SyncDisableObserverTest, TwoEnabled_UnifiedConsentEnabled) {
+ sync_preferences::TestingPrefServiceSyncable prefs2;
+ RegisterUrlKeyedAnonymizedDataCollectionPref(prefs2);
+ TestSyncDisableObserver observer;
+
+ // First profile has sync enabled.
+ MockSyncService sync1;
+ sync1.SetStatus(false, true);
+ observer.ObserveServiceForSyncDisables(&sync1, nullptr, false);
+ EXPECT_TRUE(observer.ResetNotified());
+
+ // Second profile has URL-keyed anonymized data collection enabled.
+ MockSyncService sync2;
+ SetUrlKeyedAnonymizedDataCollectionEnabled(&prefs2, true);
+ observer.ObserveServiceForSyncDisables(&sync2, &prefs2, true);
EXPECT_TRUE(observer.SyncStateAllowsUkm());
EXPECT_FALSE(observer.ResetNotified());
EXPECT_FALSE(observer.ResetPurged());
}
-TEST_F(SyncDisableObserverTest, OneAddRemove) {
+TEST_F(SyncDisableObserverTest, OneAddRemove_UnifiedConsentDisabled) {
TestSyncDisableObserver observer;
MockSyncService sync;
- observer.ObserveServiceForSyncDisables(&sync);
+ observer.ObserveServiceForSyncDisables(&sync, nullptr, false);
EXPECT_FALSE(observer.SyncStateAllowsUkm());
EXPECT_FALSE(observer.ResetNotified());
EXPECT_FALSE(observer.ResetPurged());
@@ -224,11 +277,30 @@ TEST_F(SyncDisableObserverTest, OneAddRemove) {
EXPECT_FALSE(observer.ResetPurged());
}
-TEST_F(SyncDisableObserverTest, PurgeOnDisable) {
+TEST_F(SyncDisableObserverTest, OneAddRemove_UnifiedConsentEnabled) {
+ sync_preferences::TestingPrefServiceSyncable prefs;
+ RegisterUrlKeyedAnonymizedDataCollectionPref(prefs);
+ TestSyncDisableObserver observer;
+ MockSyncService sync;
+ observer.ObserveServiceForSyncDisables(&sync, &prefs, true);
+ EXPECT_FALSE(observer.SyncStateAllowsUkm());
+ EXPECT_FALSE(observer.ResetNotified());
+ EXPECT_FALSE(observer.ResetPurged());
+ SetUrlKeyedAnonymizedDataCollectionEnabled(&prefs, true);
+ EXPECT_TRUE(observer.SyncStateAllowsUkm());
+ EXPECT_TRUE(observer.ResetNotified());
+ EXPECT_FALSE(observer.ResetPurged());
+ sync.Shutdown();
+ EXPECT_FALSE(observer.SyncStateAllowsUkm());
+ EXPECT_TRUE(observer.ResetNotified());
+ EXPECT_FALSE(observer.ResetPurged());
+}
+
+TEST_F(SyncDisableObserverTest, PurgeOnDisable_UnifiedConsentDisabled) {
TestSyncDisableObserver observer;
MockSyncService sync;
sync.SetStatus(false, true);
- observer.ObserveServiceForSyncDisables(&sync);
+ observer.ObserveServiceForSyncDisables(&sync, nullptr, false);
EXPECT_TRUE(observer.SyncStateAllowsUkm());
EXPECT_TRUE(observer.ResetNotified());
EXPECT_FALSE(observer.ResetPurged());
@@ -242,4 +314,24 @@ TEST_F(SyncDisableObserverTest, PurgeOnDisable) {
EXPECT_FALSE(observer.ResetPurged());
}
+TEST_F(SyncDisableObserverTest, PurgeOnDisable_UnifiedConsentEnabled) {
+ sync_preferences::TestingPrefServiceSyncable prefs;
+ RegisterUrlKeyedAnonymizedDataCollectionPref(prefs);
+ TestSyncDisableObserver observer;
+ MockSyncService sync;
+ SetUrlKeyedAnonymizedDataCollectionEnabled(&prefs, true);
+ observer.ObserveServiceForSyncDisables(&sync, &prefs, true);
+ EXPECT_TRUE(observer.SyncStateAllowsUkm());
+ EXPECT_TRUE(observer.ResetNotified());
+ EXPECT_FALSE(observer.ResetPurged());
+ SetUrlKeyedAnonymizedDataCollectionEnabled(&prefs, false);
+ EXPECT_FALSE(observer.SyncStateAllowsUkm());
+ EXPECT_TRUE(observer.ResetNotified());
+ EXPECT_TRUE(observer.ResetPurged());
+ sync.Shutdown();
+ EXPECT_FALSE(observer.SyncStateAllowsUkm());
+ EXPECT_FALSE(observer.ResetNotified());
+ EXPECT_FALSE(observer.ResetPurged());
+}
+
} // namespace ukm
diff --git a/chromium/components/ukm/test_ukm_recorder.cc b/chromium/components/ukm/test_ukm_recorder.cc
index cdf89c49fcf..eb95599b82b 100644
--- a/chromium/components/ukm/test_ukm_recorder.cc
+++ b/chromium/components/ukm/test_ukm_recorder.cc
@@ -38,6 +38,7 @@ void MergeEntry(const mojom::UkmEntry* in, mojom::UkmEntry* out) {
TestUkmRecorder::TestUkmRecorder() {
EnableRecording(/*extensions=*/true);
StoreWhitelistedEntries();
+ DisableSamplingForTesting();
}
TestUkmRecorder::~TestUkmRecorder() {
diff --git a/chromium/components/ukm/ukm_recorder_impl.cc b/chromium/components/ukm/ukm_recorder_impl.cc
index f2b0b007300..deff6bc0378 100644
--- a/chromium/components/ukm/ukm_recorder_impl.cc
+++ b/chromium/components/ukm/ukm_recorder_impl.cc
@@ -18,6 +18,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "components/ukm/ukm_source.h"
+#include "components/variations/variations_associated_data.h"
#include "services/metrics/public/cpp/ukm_decode.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "third_party/metrics_proto/ukm/entry.pb.h"
@@ -31,9 +32,11 @@ namespace {
// Note: kChromeUIScheme is defined in content, which this code can't
// depend on - since it's used by iOS too. kExtensionScheme is defined
-// in extensions which also isn't always available here.
+// in extensions which also isn't always available here. kAppScheme
+// will be defined in code that isn't available here.
const char kChromeUIScheme[] = "chrome";
const char kExtensionScheme[] = "chrome-extension";
+const char kAppScheme[] = "app";
const base::Feature kUkmSamplingRateFeature{"UkmSamplingRate",
base::FEATURE_DISABLED_BY_DEFAULT};
@@ -46,7 +49,8 @@ std::string GetWhitelistEntries() {
}
bool IsWhitelistedSourceId(SourceId source_id) {
- return GetSourceIdType(source_id) == SourceIdType::NAVIGATION_ID;
+ return GetSourceIdType(source_id) == SourceIdType::NAVIGATION_ID ||
+ GetSourceIdType(source_id) == SourceIdType::APP_ID;
}
// Gets the maximum number of Sources we'll keep in memory before discarding any
@@ -78,7 +82,7 @@ size_t GetMaxEntries() {
bool HasSupportedScheme(const GURL& url) {
return url.SchemeIsHTTPOrHTTPS() || url.SchemeIs(url::kFtpScheme) ||
url.SchemeIs(url::kAboutScheme) || url.SchemeIs(kChromeUIScheme) ||
- url.SchemeIs(kExtensionScheme);
+ url.SchemeIs(kExtensionScheme) || url.SchemeIs(kAppScheme);
}
// True if we should record the initial_url field of the UKM Source proto.
@@ -169,7 +173,56 @@ bool HasUnknownMetrics(const ukm::builders::DecodeMap& decode_map,
UkmRecorderImpl::UkmRecorderImpl() : recording_enabled_(false) {}
UkmRecorderImpl::~UkmRecorderImpl() = default;
-void UkmRecorderImpl::SourceCounts::Reset() {
+// static
+void UkmRecorderImpl::CreateFallbackSamplingTrial(
+ bool is_stable_channel,
+ base::FeatureList* feature_list) {
+ static const char kSampledGroup_Stable[] = "Sampled_NoSeed_Stable";
+ static const char kSampledGroup_Other[] = "Sampled_NoSeed_Other";
+ const char* sampled_group = kSampledGroup_Other;
+ int default_sampling = 1; // Sampling is 1-in-N; this is N.
+
+ // Nothing is sampled out except for "stable" which omits almost everything
+ // in this configuration. This is done so that clients that fail to receive
+ // a configuration from the server do not bias aggregated results because
+ // of a relatively large number of records from them.
+ if (is_stable_channel) {
+ sampled_group = kSampledGroup_Stable;
+ default_sampling = 1000000;
+ }
+
+ scoped_refptr<base::FieldTrial> trial(
+ base::FieldTrialList::FactoryGetFieldTrial(
+ kUkmSamplingRateFeature.name, 100, sampled_group,
+ base::FieldTrialList::kNoExpirationYear, 1, 1,
+ base::FieldTrial::ONE_TIME_RANDOMIZED, nullptr));
+
+ // Everybody (100%) should have a sampling configuration.
+ std::map<std::string, std::string> params = {
+ {"_default_sampling", base::IntToString(default_sampling)}};
+ variations::AssociateVariationParams(trial->trial_name(), sampled_group,
+ params);
+ trial->AppendGroup(sampled_group, 100);
+
+ // Setup the feature.
+ feature_list->RegisterFieldTrialOverride(
+ kUkmSamplingRateFeature.name, base::FeatureList::OVERRIDE_ENABLE_FEATURE,
+ trial.get());
+}
+
+UkmRecorderImpl::EventAggregate::EventAggregate() = default;
+UkmRecorderImpl::EventAggregate::~EventAggregate() = default;
+
+UkmRecorderImpl::Recordings::Recordings() = default;
+UkmRecorderImpl::Recordings& UkmRecorderImpl::Recordings::operator=(
+ Recordings&&) = default;
+UkmRecorderImpl::Recordings::~Recordings() = default;
+
+void UkmRecorderImpl::Recordings::Reset() {
+ *this = Recordings();
+}
+
+void UkmRecorderImpl::Recordings::SourceCounts::Reset() {
*this = SourceCounts();
}
@@ -185,13 +238,13 @@ void UkmRecorderImpl::DisableRecording() {
extensions_enabled_ = false;
}
+void UkmRecorderImpl::DisableSamplingForTesting() {
+ sampling_enabled_ = false;
+}
+
void UkmRecorderImpl::Purge() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- sources_.clear();
- source_counts_.Reset();
- carryover_urls_whitelist_.clear();
- entries_.clear();
- event_aggregations_.clear();
+ recordings_.Reset();
}
void UkmRecorderImpl::SetIsWebstoreExtensionCallback(
@@ -203,19 +256,19 @@ void UkmRecorderImpl::StoreRecordingsInReport(Report* report) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::set<SourceId> ids_seen;
- for (const auto& entry : entries_) {
+ for (const auto& entry : recordings_.entries) {
Entry* proto_entry = report->add_entries();
StoreEntryProto(*entry, proto_entry);
ids_seen.insert(entry->source_id);
}
std::unordered_set<std::string> url_whitelist;
- carryover_urls_whitelist_.swap(url_whitelist);
- AppendWhitelistedUrls(sources_, &url_whitelist);
+ recordings_.carryover_urls_whitelist.swap(url_whitelist);
+ AppendWhitelistedUrls(recordings_.sources, &url_whitelist);
std::vector<std::unique_ptr<UkmSource>> unsent_sources;
int unmatched_sources = 0;
- for (auto& kv : sources_) {
+ for (auto& kv : recordings_.sources) {
// If the source id is not whitelisted, don't send it unless it has
// associated entries and the URL matches a URL of a whitelisted source.
// Note: If ShouldRestrictToWhitelistedSourceIds() is true, this logic will
@@ -239,7 +292,7 @@ void UkmRecorderImpl::StoreRecordingsInReport(Report* report) {
if (!ShouldRecordInitialUrl())
proto_source->clear_initial_url();
}
- for (const auto& event_and_aggregate : event_aggregations_) {
+ for (const auto& event_and_aggregate : recordings_.event_aggregations) {
if (event_and_aggregate.second.metrics.empty())
continue;
const EventAggregate& event_aggregate = event_and_aggregate.second;
@@ -281,23 +334,25 @@ void UkmRecorderImpl::StoreRecordingsInReport(Report* report) {
}
UMA_HISTOGRAM_COUNTS_1000("UKM.Sources.SerializedCount",
- sources_.size() - unsent_sources.size());
- UMA_HISTOGRAM_COUNTS_100000("UKM.Entries.SerializedCount2", entries_.size());
+ recordings_.sources.size() - unsent_sources.size());
+ UMA_HISTOGRAM_COUNTS_100000("UKM.Entries.SerializedCount2",
+ recordings_.entries.size());
UMA_HISTOGRAM_COUNTS_1000("UKM.Sources.UnsentSourcesCount",
unsent_sources.size());
Report::SourceCounts* source_counts_proto = report->mutable_source_counts();
- source_counts_proto->set_observed(source_counts_.observed);
+ source_counts_proto->set_observed(recordings_.source_counts.observed);
source_counts_proto->set_navigation_sources(
- source_counts_.navigation_sources);
+ recordings_.source_counts.navigation_sources);
source_counts_proto->set_unmatched_sources(unmatched_sources);
source_counts_proto->set_deferred_sources(unsent_sources.size());
- source_counts_proto->set_carryover_sources(source_counts_.carryover_sources);
+ source_counts_proto->set_carryover_sources(
+ recordings_.source_counts.carryover_sources);
- sources_.clear();
- source_counts_.Reset();
- entries_.clear();
- event_aggregations_.clear();
+ recordings_.sources.clear();
+ recordings_.source_counts.Reset();
+ recordings_.entries.clear();
+ recordings_.event_aggregations.clear();
// Keep at most |max_kept_sources|, prioritizing most-recent entries (by
// creation time).
@@ -316,11 +371,12 @@ void UkmRecorderImpl::StoreRecordingsInReport(Report* report) {
for (auto& source : unsent_sources) {
// We already matched these sources against the URL whitelist.
// Re-whitelist them for the next report.
- carryover_urls_whitelist_.insert(source->url().spec());
- sources_.emplace(source->id(), std::move(source));
+ recordings_.carryover_urls_whitelist.insert(source->url().spec());
+ recordings_.sources.emplace(source->id(), std::move(source));
}
- UMA_HISTOGRAM_COUNTS_1000("UKM.Sources.KeptSourcesCount", sources_.size());
- source_counts_.carryover_sources = sources_.size();
+ UMA_HISTOGRAM_COUNTS_1000("UKM.Sources.KeptSourcesCount",
+ recordings_.sources.size());
+ recordings_.source_counts.carryover_sources = recordings_.sources.size();
}
bool UkmRecorderImpl::ShouldRestrictToWhitelistedSourceIds() const {
@@ -332,9 +388,6 @@ bool UkmRecorderImpl::ShouldRestrictToWhitelistedEntries() const {
return true;
}
-UkmRecorderImpl::EventAggregate::EventAggregate() = default;
-UkmRecorderImpl::EventAggregate::~EventAggregate() = default;
-
void UkmRecorderImpl::UpdateSourceURL(SourceId source_id,
const GURL& unsanitized_url) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -355,9 +408,9 @@ void UkmRecorderImpl::UpdateSourceURL(SourceId source_id,
return;
}
- source_counts_.observed++;
+ recordings_.source_counts.observed++;
if (GetSourceIdType(source_id) == SourceIdType::NAVIGATION_ID)
- source_counts_.navigation_sources++;
+ recordings_.source_counts.navigation_sources++;
GURL url = SanitizeURL(unsanitized_url);
@@ -385,16 +438,25 @@ void UkmRecorderImpl::UpdateSourceURL(SourceId source_id,
// Update the pre-existing source if there is any. This happens when the
// initial URL is different from the committed URL for the same source, e.g.,
// when there is redirection.
- if (base::ContainsKey(sources_, source_id)) {
- sources_[source_id]->UpdateUrl(url);
+ if (base::ContainsKey(recordings_.sources, source_id)) {
+ recordings_.sources[source_id]->UpdateUrl(url);
return;
}
- if (sources_.size() >= GetMaxSources()) {
+ if (recordings_.sources.size() >= GetMaxSources()) {
RecordDroppedSource(DroppedDataReason::MAX_HIT);
return;
}
- sources_.emplace(source_id, std::make_unique<UkmSource>(source_id, url));
+ recordings_.sources.emplace(source_id,
+ std::make_unique<UkmSource>(source_id, url));
+}
+
+void UkmRecorderImpl::UpdateAppURL(SourceId source_id, const GURL& url) {
+ if (!extensions_enabled_) {
+ RecordDroppedSource(DroppedDataReason::EXTENSION_URLS_DISABLED);
+ return;
+ }
+ UpdateSourceURL(source_id, url);
}
void UkmRecorderImpl::AddEntry(mojom::UkmEntryPtr entry) {
@@ -407,7 +469,8 @@ void UkmRecorderImpl::AddEntry(mojom::UkmEntryPtr entry) {
return;
}
- EventAggregate& event_aggregate = event_aggregations_[entry->event_hash];
+ EventAggregate& event_aggregate =
+ recordings_.event_aggregations[entry->event_hash];
event_aggregate.total_count++;
for (const auto& metric : entry->metrics) {
MetricAggregate& aggregate = event_aggregate.metrics[metric.first];
@@ -433,8 +496,9 @@ void UkmRecorderImpl::AddEntry(mojom::UkmEntryPtr entry) {
int sampling_rate = (found != event_sampling_rates_.end())
? found->second
: default_sampling_rate_;
- if (sampling_rate == 0 ||
- (sampling_rate > 1 && base::RandInt(1, sampling_rate) != 1)) {
+ if (sampling_enabled_ &&
+ (sampling_rate == 0 ||
+ (sampling_rate > 1 && base::RandInt(1, sampling_rate) != 1))) {
RecordDroppedEntry(DroppedDataReason::SAMPLED_OUT);
event_aggregate.dropped_due_to_sampling++;
for (auto& metric : entry->metrics)
@@ -442,7 +506,7 @@ void UkmRecorderImpl::AddEntry(mojom::UkmEntryPtr entry) {
return;
}
- if (entries_.size() >= GetMaxEntries()) {
+ if (recordings_.entries.size() >= GetMaxEntries()) {
RecordDroppedEntry(DroppedDataReason::MAX_HIT);
event_aggregate.dropped_due_to_limits++;
for (auto& metric : entry->metrics)
@@ -450,7 +514,7 @@ void UkmRecorderImpl::AddEntry(mojom::UkmEntryPtr entry) {
return;
}
- entries_.push_back(std::move(entry));
+ recordings_.entries.push_back(std::move(entry));
}
void UkmRecorderImpl::LoadExperimentSamplingInfo() {
diff --git a/chromium/components/ukm/ukm_recorder_impl.h b/chromium/components/ukm/ukm_recorder_impl.h
index aa832dc7e88..fbd3578b786 100644
--- a/chromium/components/ukm/ukm_recorder_impl.h
+++ b/chromium/components/ukm/ukm_recorder_impl.h
@@ -21,7 +21,7 @@
#include "services/metrics/public/mojom/ukm_interface.mojom.h"
namespace metrics {
-class UkmBrowserTest;
+class UkmBrowserTestBase;
class UkmEGTestHelper;
}
@@ -42,12 +42,23 @@ class UkmRecorderImpl : public UkmRecorder {
UkmRecorderImpl();
~UkmRecorderImpl() override;
+ // Unconditionally attempts to create a field trial to control client side
+ // metrics/crash sampling to use as a fallback when one hasn't been
+ // provided. This is expected to occur on first-run on platforms that don't
+ // have first-run variations support. This should only be called when there is
+ // no existing field trial controlling the sampling feature.
+ static void CreateFallbackSamplingTrial(bool is_stable_channel,
+ base::FeatureList* feature_list);
+
// Enables/disables recording control if data is allowed to be collected. The
// |extensions| flag separately controls recording of chrome-extension://
// URLs; this flag should reflect the "sync extensions" user setting.
void EnableRecording(bool extensions);
void DisableRecording();
+ // Disables sampling for testing purposes.
+ void DisableSamplingForTesting() override;
+
// Deletes stored recordings.
void Purge();
@@ -63,20 +74,23 @@ class UkmRecorderImpl : public UkmRecorder {
void StoreRecordingsInReport(Report* report);
const std::map<SourceId, std::unique_ptr<UkmSource>>& sources() const {
- return sources_;
+ return recordings_.sources;
}
- const std::vector<mojom::UkmEntryPtr>& entries() const { return entries_; }
+ const std::vector<mojom::UkmEntryPtr>& entries() const {
+ return recordings_.entries;
+ }
// UkmRecorder:
void UpdateSourceURL(SourceId source_id, const GURL& url) override;
+ void UpdateAppURL(SourceId source_id, const GURL& url) override;
virtual bool ShouldRestrictToWhitelistedSourceIds() const;
virtual bool ShouldRestrictToWhitelistedEntries() const;
private:
- friend ::metrics::UkmBrowserTest;
+ friend ::metrics::UkmBrowserTestBase;
friend ::metrics::UkmEGTestHelper;
friend ::ukm::debug::UkmDebugDataExtractor;
friend ::ukm::UkmUtilsForTest;
@@ -114,19 +128,12 @@ class UkmRecorderImpl : public UkmRecorder {
// Indicates whether recording is enabled for extensions.
bool extensions_enabled_ = false;
+ // Indicates if sampling has been enabled.
+ bool sampling_enabled_ = true;
+
// Callback for checking extension IDs.
IsWebstoreExtensionCallback is_webstore_extension_callback_;
- // Contains newly added sources and entries of UKM metrics which periodically
- // get serialized and cleared by StoreRecordingsInReport().
- std::map<SourceId, std::unique_ptr<UkmSource>> sources_;
- std::vector<mojom::UkmEntryPtr> entries_;
-
- // URLs of sources that matched a whitelist url, but were not included in
- // the report generated by the last log rotation because we haven't seen any
- // events for that source yet.
- std::unordered_set<std::string> carryover_urls_whitelist_;
-
// Map from hashes to entry and metric names.
ukm::builders::DecodeMap decode_map_;
@@ -137,22 +144,45 @@ class UkmRecorderImpl : public UkmRecorder {
int default_sampling_rate_ = 0;
base::flat_map<uint64_t, int> event_sampling_rates_;
- // Aggregate information for collected event metrics.
- std::map<uint64_t, EventAggregate> event_aggregations_;
-
- // Aggregated counters about Sources recorded in the current log.
- struct SourceCounts {
- // Count of URLs recorded for all sources.
- size_t observed = 0;
- // Count of URLs recorded for all SourceIdType::NAVIGATION_ID Sources.
- size_t navigation_sources = 0;
- // Sources carried over (not recorded) from a previous logging rotation.
- size_t carryover_sources = 0;
+ // Contains data from various recordings which periodically get serialized
+ // and cleared by StoreRecordingsInReport() and may be Purged().
+ struct Recordings {
+ Recordings();
+ Recordings& operator=(Recordings&&);
+ ~Recordings();
+
+ // Data captured by UpdateSourceUrl().
+ std::map<SourceId, std::unique_ptr<UkmSource>> sources;
+
+ // Data captured by AddEntry().
+ std::vector<mojom::UkmEntryPtr> entries;
+
+ // URLs of sources that matched a whitelist url, but were not included in
+ // the report generated by the last log rotation because we haven't seen any
+ // events for that source yet.
+ std::unordered_set<std::string> carryover_urls_whitelist;
+
+ // Aggregate information for collected event metrics.
+ std::map<uint64_t, EventAggregate> event_aggregations;
+
+ // Aggregated counters about Sources recorded in the current log.
+ struct SourceCounts {
+ // Count of URLs recorded for all sources.
+ size_t observed = 0;
+ // Count of URLs recorded for all SourceIdType::NAVIGATION_ID Sources.
+ size_t navigation_sources = 0;
+ // Sources carried over (not recorded) from a previous logging rotation.
+ size_t carryover_sources = 0;
+
+ // Resets all of the data.
+ void Reset();
+ };
+ SourceCounts source_counts;
// Resets all of the data.
void Reset();
};
- SourceCounts source_counts_;
+ Recordings recordings_;
SEQUENCE_CHECKER(sequence_checker_);
};
diff --git a/chromium/components/ukm/ukm_service.h b/chromium/components/ukm/ukm_service.h
index a7e8f3b2820..e5c6136cb95 100644
--- a/chromium/components/ukm/ukm_service.h
+++ b/chromium/components/ukm/ukm_service.h
@@ -24,7 +24,7 @@ class PrefService;
namespace metrics {
class MetricsServiceClient;
-class UkmBrowserTest;
+class UkmBrowserTestBase;
class UkmEGTestHelper;
}
@@ -81,9 +81,10 @@ class UkmService : public UkmRecorderImpl {
int32_t report_count() const { return report_count_; }
private:
- friend ::metrics::UkmBrowserTest;
+ friend ::metrics::UkmBrowserTestBase;
friend ::metrics::UkmEGTestHelper;
friend ::ukm::debug::UkmDebugDataExtractor;
+ friend ::ukm::UkmUtilsForTest;
// Starts metrics client initialization.
void StartInitTask();
diff --git a/chromium/components/ukm/ukm_service_unittest.cc b/chromium/components/ukm/ukm_service_unittest.cc
index 2a836533679..87a0a49d3ae 100644
--- a/chromium/components/ukm/ukm_service_unittest.cc
+++ b/chromium/components/ukm/ukm_service_unittest.cc
@@ -53,7 +53,9 @@ std::string Entry1And2Whitelist() {
// A small shim exposing UkmRecorder methods to tests.
class TestRecordingHelper {
public:
- explicit TestRecordingHelper(UkmRecorder* recorder) : recorder_(recorder) {}
+ explicit TestRecordingHelper(UkmRecorder* recorder) : recorder_(recorder) {
+ recorder_->DisableSamplingForTesting();
+ }
void UpdateSourceURL(SourceId source_id, const GURL& url) {
recorder_->UpdateSourceURL(source_id, url);
@@ -220,6 +222,27 @@ TEST_F(UkmServiceTest, PersistAndPurge) {
EXPECT_EQ(GetPersistedLogCount(), 0);
}
+TEST_F(UkmServiceTest, Purge) {
+ UkmService service(&prefs_, &client_,
+ true /* restrict_to_whitelisted_entries */);
+ TestRecordingHelper recorder(&service);
+ EXPECT_EQ(GetPersistedLogCount(), 0);
+ service.Initialize();
+ task_runner_->RunUntilIdle();
+ service.EnableRecording(/*extensions=*/false);
+ service.EnableReporting();
+
+ // Record some data
+ auto id = GetWhitelistedSourceId(0);
+ recorder.UpdateSourceURL(id, GURL("https://google.com/foobar1"));
+ TestEvent1(id).Record(&service);
+
+ // Purge should delete data, so there shouldn't be anything left to upload.
+ service.Purge();
+ service.Flush();
+ EXPECT_EQ(0, GetPersistedLogCount());
+}
+
TEST_F(UkmServiceTest, SourceSerialization) {
UkmService service(&prefs_, &client_,
true /* restrict_to_whitelisted_entries */);
@@ -555,6 +578,7 @@ TEST_F(UkmServiceTest, PurgeMidUpload) {
task_runner_->RunUntilIdle();
service.EnableRecording(/*extensions=*/false);
service.EnableReporting();
+
auto id = GetWhitelistedSourceId(0);
recorder.UpdateSourceURL(id, GURL("https://google.com/foobar1"));
// Should init, generate a log, and start an upload.
@@ -937,6 +961,7 @@ TEST_F(UkmServiceTest, SupportedSchemes) {
{"ftp://google.ca/", true},
{"about:blank", true},
{"chrome://version/", true},
+ {"app://play/abcdefghijklmnopqrstuvwxyzabcdef/", true},
// chrome-extension are controlled by TestIsWebstoreExtension, above.
{"chrome-extension://bhcnanendmgjjeghamaccjnochlnhcgj/", true},
{"chrome-extension://abcdefghijklmnopqrstuvwxyzabcdef/", false},
@@ -996,6 +1021,7 @@ TEST_F(UkmServiceTest, SupportedSchemesNoExtensions) {
{"ftp://google.ca/", true},
{"about:blank", true},
{"chrome://version/", true},
+ {"app://play/abcdefghijklmnopqrstuvwxyzabcdef/", true},
{"chrome-extension://bhcnanendmgjjeghamaccjnochlnhcgj/", false},
{"chrome-extension://abcdefghijklmnopqrstuvwxyzabcdef/", false},
{"file:///tmp/", false},
diff --git a/chromium/components/undo/bookmark_undo_service.cc b/chromium/components/undo/bookmark_undo_service.cc
index d386f893db8..e4b31cad4c7 100644
--- a/chromium/components/undo/bookmark_undo_service.cc
+++ b/chromium/components/undo/bookmark_undo_service.cc
@@ -135,7 +135,7 @@ BookmarkRemoveOperation::~BookmarkRemoveOperation() {
}
void BookmarkRemoveOperation::Undo() {
- DCHECK(node_.get());
+ DCHECK(node_);
const BookmarkNode* parent = bookmarks::GetBookmarkNodeByID(
bookmark_model(), parent_node_id_);
diff --git a/chromium/components/unified_consent/BUILD.gn b/chromium/components/unified_consent/BUILD.gn
new file mode 100644
index 00000000000..419332e8d31
--- /dev/null
+++ b/chromium/components/unified_consent/BUILD.gn
@@ -0,0 +1,65 @@
+# 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.
+
+static_library("unified_consent") {
+ sources = [
+ "feature.cc",
+ "feature.h",
+ "pref_names.cc",
+ "pref_names.h",
+ "unified_consent_service.cc",
+ "unified_consent_service.h",
+ "unified_consent_service_client.cc",
+ "unified_consent_service_client.h",
+ "url_keyed_data_collection_consent_helper.cc",
+ "url_keyed_data_collection_consent_helper.h",
+ ]
+ deps = [
+ "//base",
+ "//components/autofill/core/common",
+ "//components/browser_sync",
+ "//components/pref_registry",
+ "//components/signin/core/browser",
+ "//components/sync",
+ "//services/identity/public/cpp",
+ ]
+}
+
+static_library("test_support") {
+ testonly = true
+ sources = [
+ "scoped_unified_consent.cc",
+ "scoped_unified_consent.h",
+ ]
+
+ deps = [
+ "//base/test:test_support",
+ ]
+
+ public_deps = [
+ ":unified_consent",
+ "//base",
+ "//components/sync",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "feature_unittest.cc",
+ "unified_consent_service_unittest.cc",
+ "url_keyed_data_collection_consent_helper_unittest.cc",
+ ]
+ deps = [
+ ":test_support",
+ ":unified_consent",
+ "//base/test:test_support",
+ "//components/autofill/core/common",
+ "//components/sync",
+ "//components/sync:test_support_driver",
+ "//components/sync_preferences:test_support",
+ "//services/identity/public/cpp:test_support",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/unified_consent/DEPS b/chromium/components/unified_consent/DEPS
new file mode 100644
index 00000000000..38bd56db868
--- /dev/null
+++ b/chromium/components/unified_consent/DEPS
@@ -0,0 +1,9 @@
+include_rules = [
+ "+components/autofill/core/common",
+ "+components/keyed_service/core",
+ "+components/pref_registry",
+ "+components/prefs",
+ "+components/sync",
+ "+components/sync_preferences",
+ "+services/identity/public/cpp",
+]
diff --git a/chromium/components/unified_consent/OWNERS b/chromium/components/unified_consent/OWNERS
new file mode 100644
index 00000000000..8a23481825c
--- /dev/null
+++ b/chromium/components/unified_consent/OWNERS
@@ -0,0 +1,5 @@
+msarda@chromium.org
+tangltom@chromium.org
+
+# TEAM: chrome-signin@chromium.org
+# COMPONENT: Services>SignIn
diff --git a/chromium/components/unified_consent/README.md b/chromium/components/unified_consent/README.md
new file mode 100644
index 00000000000..e37be17082a
--- /dev/null
+++ b/chromium/components/unified_consent/README.md
@@ -0,0 +1,8 @@
+The Unified Consent component contains the browser keyed service that
+manages user consent when the Unified Consent feature is enabled. It also
+holds the prefs and the APIs allowing the various Chromium features to verify if
+the user has given consent for a given feature.
+
+This component is currently in development.
+
+The component is used on all platforms (desktop, ChromeOS, Android and iOS).
diff --git a/chromium/components/unified_consent/feature.cc b/chromium/components/unified_consent/feature.cc
new file mode 100644
index 00000000000..b9c1feb616c
--- /dev/null
+++ b/chromium/components/unified_consent/feature.cc
@@ -0,0 +1,38 @@
+// 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/unified_consent/feature.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+#include "components/sync/driver/sync_driver_switches.h"
+
+namespace unified_consent {
+
+// base::Feature definitions.
+const base::Feature kUnifiedConsent{"UnifiedConsent",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+const char kUnifiedConsentShowBumpParameter[] = "show_consent_bump";
+
+namespace internal {
+UnifiedConsentFeatureState GetUnifiedConsentFeatureState() {
+ // Unified consent requires user consent to be recorded via its own
+ // sync model type.The reason is that when unified consent is enabled,
+ // |USER_EVENTS| sync model type is configurable and the user may disable it.
+ // Chromium needs to continue to record user consent even if the user
+ // manually disables |USER_EVENTS|.
+ if (!base::FeatureList::IsEnabled(switches::kSyncUserConsentSeparateType))
+ return UnifiedConsentFeatureState::kDisabled;
+
+ if (!base::FeatureList::IsEnabled(kUnifiedConsent))
+ return UnifiedConsentFeatureState::kDisabled;
+
+ std::string show_bump = base::GetFieldTrialParamValueByFeature(
+ kUnifiedConsent, kUnifiedConsentShowBumpParameter);
+ return show_bump.empty() ? UnifiedConsentFeatureState::kEnabledNoBump
+ : UnifiedConsentFeatureState::kEnabledWithBump;
+}
+} // namespace internal
+
+} // namespace unified_consent
diff --git a/chromium/components/unified_consent/feature.h b/chromium/components/unified_consent/feature.h
new file mode 100644
index 00000000000..e602df3665c
--- /dev/null
+++ b/chromium/components/unified_consent/feature.h
@@ -0,0 +1,38 @@
+// 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_UNIFIED_CONSENT_FEATURE_H_
+#define COMPONENTS_UNIFIED_CONSENT_FEATURE_H_
+
+#include "base/feature_list.h"
+
+namespace unified_consent {
+
+// State of the "Unified Consent" feature.
+enum class UnifiedConsentFeatureState {
+ // Unified consent is disabled.
+ kDisabled,
+ // Unified consent is enabled, but the bump is not shown.
+ kEnabledNoBump,
+ // Unified consent is enabled and the bump is shown.
+ kEnabledWithBump
+};
+
+// Improved and unified consent for privacy-related features.
+extern const base::Feature kUnifiedConsent;
+extern const char kUnifiedConsentShowBumpParameter[];
+
+namespace internal {
+// Returns the state of the "Unified Consent" feature.
+//
+// WARNING: Do not call this method directly to check whether unfied consent
+// is enabled. Please use the per-platfome functions defined in
+// * chrome/browser/signin/unified_consent_helper.h
+// * ios/chrome/browser/unified_consent/feature.h
+unified_consent::UnifiedConsentFeatureState GetUnifiedConsentFeatureState();
+} // namespace internal
+
+} // namespace unified_consent
+
+#endif // COMPONENTS_UNIFIED_CONSENT_FEATURE_H_
diff --git a/chromium/components/unified_consent/feature_unittest.cc b/chromium/components/unified_consent/feature_unittest.cc
new file mode 100644
index 00000000000..e66297f99c5
--- /dev/null
+++ b/chromium/components/unified_consent/feature_unittest.cc
@@ -0,0 +1,53 @@
+// 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/unified_consent/feature.h"
+
+#include <memory>
+
+#include "components/sync/driver/sync_driver_switches.h"
+#include "components/unified_consent/scoped_unified_consent.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace unified_consent {
+
+TEST(UnifiedConsentFeatureTest, UnifiedConsent) {
+ // Unified consent is disabled by default.
+ EXPECT_EQ(UnifiedConsentFeatureState::kDisabled,
+ internal::GetUnifiedConsentFeatureState());
+
+ for (UnifiedConsentFeatureState state :
+ {UnifiedConsentFeatureState::kDisabled,
+ UnifiedConsentFeatureState::kEnabledNoBump,
+ UnifiedConsentFeatureState::kEnabledWithBump}) {
+ ScopedUnifiedConsent scoped_state(state);
+ EXPECT_EQ(state, internal::GetUnifiedConsentFeatureState());
+ }
+}
+
+TEST(UnifiedConsentFeatureTest, SyncUserConsentSeparateTypeDisabled) {
+ // Enable kSyncUserConsentSeparateType
+ base::test::ScopedFeatureList scoped_sync_user_consent_separate_type_feature;
+ scoped_sync_user_consent_separate_type_feature.InitAndDisableFeature(
+ switches::kSyncUserConsentSeparateType);
+
+ {
+ base::test::ScopedFeatureList unified_consent_feature_list_;
+ unified_consent_feature_list_.InitAndEnableFeature(kUnifiedConsent);
+ EXPECT_EQ(UnifiedConsentFeatureState::kDisabled,
+ internal::GetUnifiedConsentFeatureState());
+ }
+
+ {
+ std::map<std::string, std::string> feature_params;
+ feature_params[kUnifiedConsentShowBumpParameter] = "true";
+ base::test::ScopedFeatureList unified_consent_feature_list_;
+ unified_consent_feature_list_.InitAndEnableFeatureWithParameters(
+ kUnifiedConsent, feature_params);
+ EXPECT_EQ(UnifiedConsentFeatureState::kDisabled,
+ internal::GetUnifiedConsentFeatureState());
+ }
+}
+
+} // namespace unified_consent
diff --git a/chromium/components/unified_consent/pref_names.cc b/chromium/components/unified_consent/pref_names.cc
new file mode 100644
index 00000000000..19cdb2bc1fc
--- /dev/null
+++ b/chromium/components/unified_consent/pref_names.cc
@@ -0,0 +1,26 @@
+// 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/unified_consent/pref_names.h"
+
+namespace unified_consent {
+namespace prefs {
+
+// Boolean indicating whether all criteria is met for the consent bump to be
+// shown.
+const char kShouldShowUnifiedConsentBump[] =
+ "unified_consent.consent_bump.should_show";
+
+// Boolean that is true when the user opted into unified consent.
+const char kUnifiedConsentGiven[] = "unified_consent_given";
+
+// Integer indicating the migration state of unified consent, defined in
+// unified_consent::MigrationState.
+const char kUnifiedConsentMigrationState[] = "unified_consent.migration_state";
+
+const char kUrlKeyedAnonymizedDataCollectionEnabled[] =
+ "url_keyed_anonymized_data_collection.enabled";
+
+} // namespace prefs
+} // namespace unified_consent
diff --git a/chromium/components/unified_consent/pref_names.h b/chromium/components/unified_consent/pref_names.h
new file mode 100644
index 00000000000..91ebf362a3a
--- /dev/null
+++ b/chromium/components/unified_consent/pref_names.h
@@ -0,0 +1,19 @@
+// 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_UNIFIED_CONSENT_PREF_NAMES_H_
+#define COMPONENTS_UNIFIED_CONSENT_PREF_NAMES_H_
+
+namespace unified_consent {
+namespace prefs {
+
+extern const char kShouldShowUnifiedConsentBump[];
+extern const char kUnifiedConsentGiven[];
+extern const char kUnifiedConsentMigrationState[];
+extern const char kUrlKeyedAnonymizedDataCollectionEnabled[];
+
+} // namespace prefs
+} // namespace unified_consent
+
+#endif // COMPONENTS_UNIFIED_CONSENT_PREF_NAMES_H_
diff --git a/chromium/components/signin/core/browser/scoped_unified_consent.cc b/chromium/components/unified_consent/scoped_unified_consent.cc
index 8c19fa1c9e9..dc27b9f0e21 100644
--- a/chromium/components/signin/core/browser/scoped_unified_consent.cc
+++ b/chromium/components/unified_consent/scoped_unified_consent.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/signin/core/browser/scoped_unified_consent.h"
+#include "components/unified_consent/scoped_unified_consent.h"
#include <map>
#include <string>
@@ -12,29 +12,32 @@
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/test/scoped_feature_list.h"
+#include "components/sync/driver/sync_driver_switches.h"
-namespace signin {
+namespace unified_consent {
ScopedUnifiedConsent::ScopedUnifiedConsent(UnifiedConsentFeatureState state) {
+ sync_user_consent_separate_type_feature_list_.InitAndEnableFeature(
+ switches::kSyncUserConsentSeparateType);
switch (state) {
case UnifiedConsentFeatureState::kDisabled:
- scoped_feature_list_.InitAndDisableFeature(kUnifiedConsent);
+ unified_consent_feature_list_.InitAndDisableFeature(kUnifiedConsent);
break;
case UnifiedConsentFeatureState::kEnabledNoBump:
- scoped_feature_list_.InitAndEnableFeature(kUnifiedConsent);
+ unified_consent_feature_list_.InitAndEnableFeature(kUnifiedConsent);
break;
case UnifiedConsentFeatureState::kEnabledWithBump: {
std::map<std::string, std::string> feature_params;
feature_params[kUnifiedConsentShowBumpParameter] = "true";
- scoped_feature_list_.InitAndEnableFeatureWithParameters(kUnifiedConsent,
- feature_params);
+ unified_consent_feature_list_.InitAndEnableFeatureWithParameters(
+ kUnifiedConsent, feature_params);
break;
}
}
- DCHECK_EQ(state, GetUnifiedConsentFeatureState());
+ DCHECK_EQ(state, internal::GetUnifiedConsentFeatureState());
}
ScopedUnifiedConsent::~ScopedUnifiedConsent() {}
-} // namespace signin
+} // namespace unified_consent
diff --git a/chromium/components/signin/core/browser/scoped_unified_consent.h b/chromium/components/unified_consent/scoped_unified_consent.h
index 69e55dce8e1..a280cdc2244 100644
--- a/chromium/components/signin/core/browser/scoped_unified_consent.h
+++ b/chromium/components/unified_consent/scoped_unified_consent.h
@@ -2,30 +2,33 @@
// 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_SCOPED_UNIFIED_CONSENT_H_
-#define COMPONENTS_SIGNIN_CORE_BROWSER_SCOPED_UNIFIED_CONSENT_H_
+#ifndef COMPONENTS_UNIFIED_CONSENT_SCOPED_UNIFIED_CONSENT_H_
+#define COMPONENTS_UNIFIED_CONSENT_SCOPED_UNIFIED_CONSENT_H_
#include <memory>
#include "base/macros.h"
#include "base/test/scoped_feature_list.h"
-#include "components/signin/core/browser/profile_management_switches.h"
+#include "components/unified_consent/feature.h"
-namespace signin {
+namespace unified_consent {
// Changes the unified consent feature state while it is in scope. Useful for
// tests.
+// Also enables the feature |switches::kSyncUserConsentSeparateType| as
+// unified consent depends on its.
class ScopedUnifiedConsent {
public:
explicit ScopedUnifiedConsent(UnifiedConsentFeatureState state);
~ScopedUnifiedConsent();
private:
- base::test::ScopedFeatureList scoped_feature_list_;
+ base::test::ScopedFeatureList sync_user_consent_separate_type_feature_list_;
+ base::test::ScopedFeatureList unified_consent_feature_list_;
DISALLOW_COPY_AND_ASSIGN(ScopedUnifiedConsent);
};
-} // namespace signin
+} // namespace unified_consent
-#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SCOPED_UNIFIED_CONSENT_H_
+#endif // COMPONENTS_UNIFIED_CONSENT_SCOPED_UNIFIED_CONSENT_H_
diff --git a/chromium/components/unified_consent/unified_consent_service.cc b/chromium/components/unified_consent/unified_consent_service.cc
new file mode 100644
index 00000000000..9b9dbc133f6
--- /dev/null
+++ b/chromium/components/unified_consent/unified_consent_service.cc
@@ -0,0 +1,486 @@
+// 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/unified_consent/unified_consent_service.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/scoped_observer.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/base/sync_prefs.h"
+#include "components/sync/driver/sync_service.h"
+#include "components/unified_consent/pref_names.h"
+#include "components/unified_consent/unified_consent_service_client.h"
+
+namespace unified_consent {
+
+namespace {
+
+// Histogram recorded at startup to log which Google services are enabled.
+const char kSyncAndGoogleServicesSettingsHistogram[] =
+ "UnifiedConsent.SyncAndGoogleServicesSettings";
+
+// Records a sample in the kSyncAndGoogleServicesSettingsHistogram. Wrapped in a
+// function to avoid code size issues caused by histogram macros.
+void RecordSettingsHistogramSample(SettingsHistogramValue value) {
+ UMA_HISTOGRAM_ENUMERATION(kSyncAndGoogleServicesSettingsHistogram, value);
+}
+
+// Checks if a pref is enabled and if so, records a sample in the
+// kSyncAndGoogleServicesSettingsHistogram. Returns true if a sample was
+// recorded.
+bool RecordSettingsHistogramFromPref(const char* pref_name,
+ PrefService* pref_service,
+ SettingsHistogramValue value) {
+ if (!pref_service->GetBoolean(pref_name))
+ return false;
+ RecordSettingsHistogramSample(value);
+ return true;
+}
+
+// Checks if a service is enabled and if so, records a sample in the
+// kSyncAndGoogleServicesSettingsHistogram. Returns true if a sample was
+// recorded.
+bool RecordSettingsHistogramFromService(
+ UnifiedConsentServiceClient* client,
+ UnifiedConsentServiceClient::Service service,
+ SettingsHistogramValue value) {
+ if (client->GetServiceState(service) !=
+ UnifiedConsentServiceClient::ServiceState::kEnabled) {
+ return false;
+ }
+
+ RecordSettingsHistogramSample(value);
+ return true;
+}
+
+// Used for observing the sync service and finishing the rollback once the sync
+// engine is initialized.
+// Note: This object is suicidal - it will kill itself after it finishes the
+// rollback.
+class RollbackHelper : public syncer::SyncServiceObserver {
+ public:
+ explicit RollbackHelper(syncer::SyncService* sync_service);
+ ~RollbackHelper() override = default;
+
+ private:
+ // syncer::SyncServiceObserver:
+ void OnStateChanged(syncer::SyncService* sync_service) override;
+
+ void DoRollbackIfPossibleAndDie(syncer::SyncService* sync_service);
+
+ ScopedObserver<syncer::SyncService, RollbackHelper> scoped_sync_observer_;
+};
+
+RollbackHelper::RollbackHelper(syncer::SyncService* sync_service)
+ : scoped_sync_observer_(this) {
+ if (sync_service->IsEngineInitialized())
+ DoRollbackIfPossibleAndDie(sync_service);
+ else
+ scoped_sync_observer_.Add(sync_service);
+}
+
+void RollbackHelper::OnStateChanged(syncer::SyncService* sync_service) {
+ if (!sync_service->IsEngineInitialized())
+ return;
+
+ scoped_sync_observer_.RemoveAll();
+ DoRollbackIfPossibleAndDie(sync_service);
+}
+
+void RollbackHelper::DoRollbackIfPossibleAndDie(
+ syncer::SyncService* sync_service) {
+ DCHECK(!scoped_sync_observer_.IsObservingSources());
+
+ syncer::ModelTypeSet user_types_without_user_events =
+ syncer::UserSelectableTypes();
+ user_types_without_user_events.Remove(syncer::USER_EVENTS);
+
+ if (sync_service->GetPreferredDataTypes().HasAll(
+ user_types_without_user_events)) {
+ // As part of the migration of a profile to Unified Consent, sync everything
+ // is disabled but sync continues to be enabled for all data types except
+ // USER_EVENTS. Therefore it is desired to restore sync everything when
+ // rolling back unified consent to leave sync in the same state as the one
+ // before migration.
+ sync_service->OnUserChoseDatatypes(true, syncer::UserSelectableTypes());
+ }
+
+ base::SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
+}
+
+} // namespace
+
+UnifiedConsentService::UnifiedConsentService(
+ std::unique_ptr<UnifiedConsentServiceClient> service_client,
+ PrefService* pref_service,
+ identity::IdentityManager* identity_manager,
+ syncer::SyncService* sync_service)
+ : service_client_(std::move(service_client)),
+ pref_service_(pref_service),
+ identity_manager_(identity_manager),
+ sync_service_(sync_service) {
+ DCHECK(service_client_);
+ DCHECK(pref_service_);
+ DCHECK(identity_manager_);
+ DCHECK(sync_service_);
+
+ if (GetMigrationState() == MigrationState::kNotInitialized)
+ MigrateProfileToUnifiedConsent();
+
+ service_client_->AddObserver(this);
+ identity_manager_->AddObserver(this);
+ sync_service_->AddObserver(this);
+
+ pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
+ pref_change_registrar_->Init(pref_service);
+ pref_change_registrar_->Add(
+ prefs::kUnifiedConsentGiven,
+ base::BindRepeating(
+ &UnifiedConsentService::OnUnifiedConsentGivenPrefChanged,
+ base::Unretained(this)));
+
+ // If somebody disabled any of the non-personalized services while Chrome
+ // wasn't running, disable unified consent.
+ if (!AreAllNonPersonalizedServicesEnabled() && IsUnifiedConsentGiven()) {
+ SetUnifiedConsentGiven(false);
+ }
+
+ RecordSettingsHistogram();
+}
+
+UnifiedConsentService::~UnifiedConsentService() {}
+
+// static
+void UnifiedConsentService::RegisterPrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterBooleanPref(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ false);
+ registry->RegisterBooleanPref(prefs::kUnifiedConsentGiven, false);
+ registry->RegisterIntegerPref(
+ prefs::kUnifiedConsentMigrationState,
+ static_cast<int>(MigrationState::kNotInitialized));
+ registry->RegisterBooleanPref(prefs::kShouldShowUnifiedConsentBump, false);
+}
+
+// static
+void UnifiedConsentService::RollbackIfNeeded(
+ PrefService* user_pref_service,
+ syncer::SyncService* sync_service) {
+ DCHECK(user_pref_service);
+
+ if (user_pref_service->GetInteger(prefs::kUnifiedConsentMigrationState) ==
+ static_cast<int>(MigrationState::kNotInitialized)) {
+ // If there was no migration yet, nothing has to be rolled back.
+ return;
+ }
+
+ if (user_pref_service->GetBoolean(prefs::kShouldShowUnifiedConsentBump) &&
+ sync_service &&
+ sync_service->GetDisableReasons() ==
+ syncer::SyncService::DISABLE_REASON_NONE) {
+ // This will wait until the sync engine is initialized and then enables the
+ // sync-everything pref in case the user is syncing all data types.
+ new RollbackHelper(sync_service);
+ }
+
+ // Clear all unified consent prefs.
+ user_pref_service->ClearPref(prefs::kUrlKeyedAnonymizedDataCollectionEnabled);
+ user_pref_service->ClearPref(prefs::kUnifiedConsentGiven);
+ user_pref_service->ClearPref(prefs::kUnifiedConsentMigrationState);
+ user_pref_service->ClearPref(prefs::kShouldShowUnifiedConsentBump);
+}
+
+void UnifiedConsentService::SetUnifiedConsentGiven(bool unified_consent_given) {
+ // Unified consent cannot be enabled if the user is not signed in.
+ DCHECK(!unified_consent_given || identity_manager_->HasPrimaryAccount());
+ pref_service_->SetBoolean(prefs::kUnifiedConsentGiven, unified_consent_given);
+}
+
+bool UnifiedConsentService::IsUnifiedConsentGiven() {
+ return pref_service_->GetBoolean(prefs::kUnifiedConsentGiven);
+}
+
+bool UnifiedConsentService::ShouldShowConsentBump() {
+ return pref_service_->GetBoolean(prefs::kShouldShowUnifiedConsentBump);
+}
+
+void UnifiedConsentService::MarkConsentBumpShown() {
+ // Record suppress reason kNone, which means that it was shown. This also sets
+ // the |kShouldShowConsentBump| pref to false.
+ RecordConsentBumpSuppressReason(ConsentBumpSuppressReason::kNone);
+}
+
+void UnifiedConsentService::RecordConsentBumpSuppressReason(
+ ConsentBumpSuppressReason suppress_reason) {
+ UMA_HISTOGRAM_ENUMERATION("UnifiedConsent.ConsentBump.SuppressReason",
+ suppress_reason);
+
+ switch (suppress_reason) {
+ case ConsentBumpSuppressReason::kNone:
+ case ConsentBumpSuppressReason::kNotSignedIn:
+ case ConsentBumpSuppressReason::kSyncEverythingOff:
+ case ConsentBumpSuppressReason::kPrivacySettingOff:
+ case ConsentBumpSuppressReason::kSettingsOptIn:
+ case ConsentBumpSuppressReason::kUserSignedOut:
+ pref_service_->SetBoolean(prefs::kShouldShowUnifiedConsentBump, false);
+ break;
+ case ConsentBumpSuppressReason::kSyncPaused:
+ // Consent bump should be shown when sync is active again.
+ DCHECK(ShouldShowConsentBump());
+ break;
+ }
+}
+
+void UnifiedConsentService::Shutdown() {
+ service_client_->RemoveObserver(this);
+ identity_manager_->RemoveObserver(this);
+ sync_service_->RemoveObserver(this);
+}
+
+void UnifiedConsentService::OnServiceStateChanged(Service service) {
+ // Unified consent is disabled when any of its dependent services gets
+ // disabled.
+ if (service_client_->GetServiceState(service) == ServiceState::kDisabled)
+ SetUnifiedConsentGiven(false);
+}
+
+void UnifiedConsentService::OnPrimaryAccountCleared(
+ const AccountInfo& account_info) {
+ // When signing out, the unfied consent is revoked.
+ pref_service_->SetBoolean(prefs::kUnifiedConsentGiven, false);
+
+ // By design, signing out of Chrome automatically disables off-by-default
+ // services.
+ pref_service_->SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ false);
+ service_client_->SetServiceEnabled(Service::kSafeBrowsingExtendedReporting,
+ false);
+ service_client_->SetServiceEnabled(Service::kSpellCheck, false);
+
+ if (GetMigrationState() != MigrationState::kCompleted) {
+ // When the user signs out, the migration is complete.
+ SetMigrationState(MigrationState::kCompleted);
+ }
+
+ if (ShouldShowConsentBump())
+ RecordConsentBumpSuppressReason(ConsentBumpSuppressReason::kUserSignedOut);
+}
+
+void UnifiedConsentService::OnStateChanged(syncer::SyncService* sync) {
+ if (sync_service_->GetDisableReasons() !=
+ syncer::SyncService::DISABLE_REASON_NONE ||
+ !sync_service_->IsEngineInitialized()) {
+ return;
+ }
+
+ if (GetMigrationState() == MigrationState::kInProgressWaitForSyncInit)
+ UpdateSettingsForMigration();
+
+ if (sync_service_->IsUsingSecondaryPassphrase() && IsUnifiedConsentGiven()) {
+ // Force off unified consent given when the user sets a custom passphrase.
+ SetUnifiedConsentGiven(false);
+ }
+
+ syncer::SyncPrefs sync_prefs(pref_service_);
+ if (IsUnifiedConsentGiven() != sync_prefs.HasKeepEverythingSynced()) {
+ // Make sync-everything consistent with the |kUnifiedConsentGiven| pref.
+ SetSyncEverythingIfPossible(IsUnifiedConsentGiven());
+ }
+}
+
+void UnifiedConsentService::OnUnifiedConsentGivenPrefChanged() {
+ bool enabled = pref_service_->GetBoolean(prefs::kUnifiedConsentGiven);
+
+ if (!enabled) {
+ if (identity_manager_->HasPrimaryAccount()) {
+ // Sync-everything is set to false, so the user can select individual
+ // sync data types.
+ SetSyncEverythingIfPossible(false);
+ }
+ return;
+ }
+
+ DCHECK(sync_service_->IsSyncAllowed());
+ DCHECK(identity_manager_->HasPrimaryAccount());
+ DCHECK_LT(MigrationState::kNotInitialized, GetMigrationState());
+
+ if (GetMigrationState() != MigrationState::kCompleted) {
+ // If the user opted into unified consent, the migration is completed.
+ SetMigrationState(MigrationState::kCompleted);
+ }
+
+ if (ShouldShowConsentBump())
+ RecordConsentBumpSuppressReason(ConsentBumpSuppressReason::kSettingsOptIn);
+
+ // Enable all sync data types if possible, otherwise they will be enabled with
+ // |OnStateChanged| once sync is active;
+ SetSyncEverythingIfPossible(true);
+
+ // Enable all non-personalized services.
+ pref_service_->SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ true);
+ // Inform client to enable non-personalized services.
+ for (int i = 0; i <= static_cast<int>(Service::kLast); ++i) {
+ Service service = static_cast<Service>(i);
+ if (service_client_->GetServiceState(service) !=
+ ServiceState::kNotSupported) {
+ service_client_->SetServiceEnabled(service, true);
+ }
+ }
+}
+
+void UnifiedConsentService::SetSyncEverythingIfPossible(bool sync_everything) {
+ syncer::SyncPrefs sync_prefs(pref_service_);
+ if (sync_everything == sync_prefs.HasKeepEverythingSynced())
+ return;
+
+ if (!IsSyncConfigurable())
+ return;
+
+ if (sync_everything) {
+ pref_service_->SetBoolean(autofill::prefs::kAutofillWalletImportEnabled,
+ true);
+ sync_service_->OnUserChoseDatatypes(sync_everything,
+ syncer::UserSelectableTypes());
+ } else {
+ syncer::ModelTypeSet preferred = sync_service_->GetPreferredDataTypes();
+ preferred.RetainAll(syncer::UserSelectableTypes());
+ sync_service_->OnUserChoseDatatypes(false, preferred);
+ }
+}
+
+MigrationState UnifiedConsentService::GetMigrationState() {
+ int migration_state_int =
+ pref_service_->GetInteger(prefs::kUnifiedConsentMigrationState);
+ DCHECK_LE(static_cast<int>(MigrationState::kNotInitialized),
+ migration_state_int);
+ DCHECK_GE(static_cast<int>(MigrationState::kCompleted), migration_state_int);
+ return static_cast<MigrationState>(migration_state_int);
+}
+
+void UnifiedConsentService::SetMigrationState(MigrationState migration_state) {
+ pref_service_->SetInteger(prefs::kUnifiedConsentMigrationState,
+ static_cast<int>(migration_state));
+}
+
+void UnifiedConsentService::MigrateProfileToUnifiedConsent() {
+ DCHECK_EQ(GetMigrationState(), MigrationState::kNotInitialized);
+ DCHECK(!IsUnifiedConsentGiven());
+
+ if (!identity_manager_->HasPrimaryAccount()) {
+ RecordConsentBumpSuppressReason(ConsentBumpSuppressReason::kNotSignedIn);
+ SetMigrationState(MigrationState::kCompleted);
+ return;
+ }
+
+ if (!syncer::SyncPrefs(pref_service_).HasKeepEverythingSynced()) {
+ RecordConsentBumpSuppressReason(
+ ConsentBumpSuppressReason::kSyncEverythingOff);
+ } else if (!AreAllOnByDefaultPrivacySettingsOn()) {
+ RecordConsentBumpSuppressReason(
+ ConsentBumpSuppressReason::kPrivacySettingOff);
+ } else {
+ // When the user was syncing everything, and all on-by-default privacy
+ // settings were on, the consent bump should be shown.
+ pref_service_->SetBoolean(prefs::kShouldShowUnifiedConsentBump, true);
+ }
+
+ UpdateSettingsForMigration();
+}
+
+void UnifiedConsentService::UpdateSettingsForMigration() {
+ if (!IsSyncConfigurable()) {
+ SetMigrationState(MigrationState::kInProgressWaitForSyncInit);
+ return;
+ }
+
+ if (IsUnifiedConsentGiven()) {
+ // When the user opted into unified consent through the consent bump or the
+ // settings page while waiting for sync initialization, the migration is
+ // completed.
+ SetMigrationState(MigrationState::kCompleted);
+ return;
+ }
+
+ // Set URL-keyed anonymized metrics to the state it had before unified
+ // consent.
+ bool url_keyed_metrics_enabled = sync_service_->GetPreferredDataTypes().Has(
+ syncer::HISTORY_DELETE_DIRECTIVES) &&
+ !sync_service_->IsUsingSecondaryPassphrase();
+ pref_service_->SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ url_keyed_metrics_enabled);
+
+ // Disable the datatype user events for newly migrated users. Also set
+ // sync-everything to false, so it matches unified consent given.
+ syncer::ModelTypeSet preferred_types_without_user_events =
+ sync_service_->GetPreferredDataTypes();
+ preferred_types_without_user_events.RetainAll(syncer::UserSelectableTypes());
+ preferred_types_without_user_events.Remove(syncer::USER_EVENTS);
+ sync_service_->OnUserChoseDatatypes(false /*sync everything */,
+ preferred_types_without_user_events);
+
+ SetMigrationState(MigrationState::kCompleted);
+}
+
+bool UnifiedConsentService::AreAllNonPersonalizedServicesEnabled() {
+ for (int i = 0; i <= static_cast<int>(Service::kLast); ++i) {
+ Service service = static_cast<Service>(i);
+ if (service_client_->GetServiceState(service) == ServiceState::kDisabled)
+ return false;
+ }
+ if (!pref_service_->GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled))
+ return false;
+
+ return true;
+}
+
+bool UnifiedConsentService::AreAllOnByDefaultPrivacySettingsOn() {
+ for (auto service : {Service::kAlternateErrorPages,
+ Service::kMetricsReporting, Service::kNetworkPrediction,
+ Service::kSafeBrowsing, Service::kSearchSuggest}) {
+ if (service_client_->GetServiceState(service) == ServiceState::kDisabled)
+ return false;
+ }
+ return true;
+}
+
+bool UnifiedConsentService::IsSyncConfigurable() {
+ return sync_service_->GetState() == syncer::SyncService::State::ACTIVE;
+}
+
+void UnifiedConsentService::RecordSettingsHistogram() {
+ bool metric_recorded = false;
+
+ if (IsUnifiedConsentGiven()) {
+ RecordSettingsHistogramSample(SettingsHistogramValue::kUnifiedConsentGiven);
+ metric_recorded = true;
+ }
+ if (identity_manager_->HasPrimaryAccount() &&
+ sync_service_->GetPreferredDataTypes().Has(syncer::USER_EVENTS)) {
+ RecordSettingsHistogramSample(SettingsHistogramValue::kUserEvents);
+ metric_recorded = true;
+ }
+ metric_recorded |= RecordSettingsHistogramFromPref(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled, pref_service_,
+ SettingsHistogramValue::kUrlKeyedAnonymizedDataCollection);
+ metric_recorded |= RecordSettingsHistogramFromService(
+ service_client_.get(),
+ UnifiedConsentServiceClient::Service::kSafeBrowsingExtendedReporting,
+ SettingsHistogramValue::kSafeBrowsingExtendedReporting);
+ metric_recorded |= RecordSettingsHistogramFromService(
+ service_client_.get(), UnifiedConsentServiceClient::Service::kSpellCheck,
+ SettingsHistogramValue::kSpellCheck);
+
+ if (!metric_recorded)
+ RecordSettingsHistogramSample(SettingsHistogramValue::kNone);
+}
+
+} // namespace unified_consent
diff --git a/chromium/components/unified_consent/unified_consent_service.h b/chromium/components/unified_consent/unified_consent_service.h
new file mode 100644
index 00000000000..f1a85b8bf4a
--- /dev/null
+++ b/chromium/components/unified_consent/unified_consent_service.h
@@ -0,0 +1,168 @@
+// 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_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_H_
+#define COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/sync/driver/sync_service_observer.h"
+#include "components/unified_consent/unified_consent_service_client.h"
+#include "services/identity/public/cpp/identity_manager.h"
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+class PrefChangeRegistrar;
+class PrefService;
+
+namespace syncer {
+class SyncService;
+}
+
+namespace unified_consent {
+
+using Service = UnifiedConsentServiceClient::Service;
+using ServiceState = UnifiedConsentServiceClient::ServiceState;
+
+enum class MigrationState : int {
+ kNotInitialized = 0,
+ kInProgressWaitForSyncInit = 1,
+ // Reserve space for other kInProgress* entries to be added here.
+ kCompleted = 10,
+};
+
+// Used in histograms. Do not change existing values, append new values at the
+// end.
+enum class ConsentBumpSuppressReason {
+ kNone,
+ kNotSignedIn,
+ kSyncEverythingOff,
+ kPrivacySettingOff,
+ kSettingsOptIn,
+ kUserSignedOut,
+ kSyncPaused,
+
+ kMaxValue = kSyncPaused
+};
+
+// Google services that can be toggled in user settings.
+// Used in histograms. Do not change existing values, append new values at the
+// end.
+enum class SettingsHistogramValue {
+ kNone = 0,
+ kUnifiedConsentGiven = 1,
+ kUserEvents = 2,
+ kUrlKeyedAnonymizedDataCollection = 3,
+ kSafeBrowsingExtendedReporting = 4,
+ kSpellCheck = 5,
+
+ kMaxValue = kSpellCheck
+};
+
+// A browser-context keyed service that is used to manage the user consent
+// when UnifiedConsent feature is enabled.
+class UnifiedConsentService : public KeyedService,
+ public UnifiedConsentServiceClient::Observer,
+ public identity::IdentityManager::Observer,
+ public syncer::SyncServiceObserver {
+ public:
+ UnifiedConsentService(
+ std::unique_ptr<UnifiedConsentServiceClient> service_client,
+ PrefService* pref_service,
+ identity::IdentityManager* identity_manager,
+ syncer::SyncService* sync_service);
+ ~UnifiedConsentService() override;
+
+ // Register the prefs used by this UnifiedConsentService.
+ static void RegisterPrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ // Rolls back changes made during migration. This method does nothing if the
+ // user hasn't migrated to unified consent yet.
+ static void RollbackIfNeeded(PrefService* user_pref_service,
+ syncer::SyncService* sync_service);
+
+ // This updates the consent pref and if |unified_consent_given| is true, all
+ // unified consent services are enabled.
+ void SetUnifiedConsentGiven(bool unified_consent_given);
+ bool IsUnifiedConsentGiven();
+
+ // Returns true if all criteria is met to show the consent bump.
+ bool ShouldShowConsentBump();
+ // Marks the consent bump as shown. Any future calls to
+ // |ShouldShowConsentBump| are guaranteed to return false.
+ void MarkConsentBumpShown();
+ // Records the consent bump suppress reason and updates the state whether the
+ // consent bump should be shown. Note: In some cases, e.g. sync paused,
+ // |ShouldShowConsentBump| will still return true.
+ void RecordConsentBumpSuppressReason(
+ ConsentBumpSuppressReason suppress_reason);
+
+ // KeyedService:
+ void Shutdown() override;
+
+ // UnifiedConsentServiceClient::Observer:
+ void OnServiceStateChanged(Service service) override;
+
+ // IdentityManager::Observer:
+ void OnPrimaryAccountCleared(
+ const AccountInfo& previous_primary_account_info) override;
+
+ private:
+ friend class UnifiedConsentServiceTest;
+
+ // syncer::SyncServiceObserver:
+ void OnStateChanged(syncer::SyncService* sync) override;
+
+ // Called when |prefs::kUnifiedConsentGiven| pref value changes.
+ // When set to true, it enables syncing of all data types and it enables all
+ // non-personalized services. Otherwise it does nothing.
+ void OnUnifiedConsentGivenPrefChanged();
+
+ // Enables/disables syncing everything if the sync engine is initialized.
+ void SetSyncEverythingIfPossible(bool sync_everything);
+
+ // Migration helpers.
+ MigrationState GetMigrationState();
+ void SetMigrationState(MigrationState migration_state);
+ // Called when the unified consent service is created. This sets the
+ // |kShouldShowUnifiedConsentBump| pref to true if the user is eligible and
+ // calls |UpdateSettingsForMigration| at the end.
+ void MigrateProfileToUnifiedConsent();
+ // Updates the settings preferences for the migration when the sync engine is
+ // initialized. When it is not, this function will be called again from
+ // |OnStateChanged| when the sync engine is initialized.
+ void UpdateSettingsForMigration();
+
+ // Returns true if all non-personalized services are enabled.
+ bool AreAllNonPersonalizedServicesEnabled();
+
+ // Checks if all on-by-default non-personalized services are on.
+ bool AreAllOnByDefaultPrivacySettingsOn();
+
+ // Helper that checks whether it's okay to call
+ // |SyncService::OnUserChoseDatatypes|.
+ bool IsSyncConfigurable();
+
+ // Records a sample for each bucket enabled by the user (except kNone).
+ // kNone is recorded when none of the other buckets are recorded.
+ void RecordSettingsHistogram();
+
+ std::unique_ptr<UnifiedConsentServiceClient> service_client_;
+ PrefService* pref_service_;
+ identity::IdentityManager* identity_manager_;
+ syncer::SyncService* sync_service_;
+
+ std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnifiedConsentService);
+};
+
+} // namespace unified_consent
+
+#endif // COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_H_
diff --git a/chromium/components/unified_consent/unified_consent_service_client.cc b/chromium/components/unified_consent/unified_consent_service_client.cc
new file mode 100644
index 00000000000..1bd56d205eb
--- /dev/null
+++ b/chromium/components/unified_consent/unified_consent_service_client.cc
@@ -0,0 +1,50 @@
+// 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/unified_consent/unified_consent_service_client.h"
+
+#include "base/bind.h"
+
+namespace unified_consent {
+
+UnifiedConsentServiceClient::UnifiedConsentServiceClient() {}
+UnifiedConsentServiceClient::~UnifiedConsentServiceClient() {}
+
+void UnifiedConsentServiceClient::AddObserver(Observer* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void UnifiedConsentServiceClient::RemoveObserver(Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+void UnifiedConsentServiceClient::ObserveServicePrefChange(
+ Service service,
+ const std::string& pref_name,
+ PrefService* pref_service) {
+ service_prefs_[pref_name] = service;
+
+ // First access to the pref registrar of |pref_service| in the map
+ // automatically creates an entry for it.
+ PrefChangeRegistrar* pref_change_registrar =
+ &(pref_change_registrars_[pref_service]);
+ if (!pref_change_registrar->prefs())
+ pref_change_registrar->Init(pref_service);
+ pref_change_registrar->Add(
+ pref_name,
+ base::BindRepeating(&UnifiedConsentServiceClient::OnPrefChanged,
+ base::Unretained(this)));
+}
+
+void UnifiedConsentServiceClient::FireOnServiceStateChanged(Service service) {
+ for (auto& observer : observer_list_)
+ observer.OnServiceStateChanged(service);
+}
+
+void UnifiedConsentServiceClient::OnPrefChanged(const std::string& pref_name) {
+ DCHECK(service_prefs_.count(pref_name));
+ FireOnServiceStateChanged(service_prefs_[pref_name]);
+}
+
+} // namespace unified_consent
diff --git a/chromium/components/unified_consent/unified_consent_service_client.h b/chromium/components/unified_consent/unified_consent_service_client.h
new file mode 100644
index 00000000000..cb31490d0db
--- /dev/null
+++ b/chromium/components/unified_consent/unified_consent_service_client.h
@@ -0,0 +1,94 @@
+// 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_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_CLIENT_H_
+#define COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_CLIENT_H_
+
+#include <map>
+#include <string>
+
+#include "base/observer_list.h"
+#include "components/prefs/pref_change_registrar.h"
+
+class PrefService;
+
+namespace unified_consent {
+
+class UnifiedConsentServiceClient {
+ public:
+ enum class Service {
+ // Link Doctor error pages.
+ kAlternateErrorPages,
+ // Metrics reporting.
+ kMetricsReporting,
+ // Prediction of network actions.
+ kNetworkPrediction,
+ // Safe browsing.
+ kSafeBrowsing,
+ // Extended safe browsing.
+ kSafeBrowsingExtendedReporting,
+ // Search suggestions.
+ kSearchSuggest,
+ // Spell checking.
+ kSpellCheck,
+
+ // Last element of the enum, used for iteration.
+ kLast = kSpellCheck,
+ };
+
+ enum class ServiceState {
+ // The service is not supported on this platform.
+ kNotSupported,
+ // The service is supported, but disabled.
+ kDisabled,
+ // The service is enabled.
+ kEnabled
+ };
+
+ class Observer {
+ public:
+ // Called when the service state of |service| changes.
+ virtual void OnServiceStateChanged(Service service) = 0;
+ };
+
+ UnifiedConsentServiceClient();
+ virtual ~UnifiedConsentServiceClient();
+
+ // Returns the ServiceState for |service|.
+ virtual ServiceState GetServiceState(Service service) = 0;
+ // Sets |service| enabled if it is supported on this platform.
+ virtual void SetServiceEnabled(Service service, bool enabled) = 0;
+
+ // Methods to register or remove observers.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ protected:
+ // This method adds |pref_name| to the list of prefs that will be observed for
+ // changes. Whenever there's a change in the pref, all
+ // |UnifiedConsentServiceClient::Observer|s are fired.
+ void ObserveServicePrefChange(Service service,
+ const std::string& pref_name,
+ PrefService* pref_service);
+
+ // Fires |OnServiceStateChanged| on all observers.
+ void FireOnServiceStateChanged(Service service);
+
+ private:
+ // Callback for the pref change registrars.
+ void OnPrefChanged(const std::string& pref_name);
+
+ base::ObserverList<Observer, true> observer_list_;
+
+ // Matches the pref name to it's service.
+ std::map<std::string, Service> service_prefs_;
+ // Matches pref service to it's change registrar.
+ std::map<PrefService*, PrefChangeRegistrar> pref_change_registrars_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnifiedConsentServiceClient);
+};
+
+} // namespace unified_consent
+
+#endif // COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_CLIENT_H_
diff --git a/chromium/components/unified_consent/unified_consent_service_unittest.cc b/chromium/components/unified_consent/unified_consent_service_unittest.cc
new file mode 100644
index 00000000000..332351a1e53
--- /dev/null
+++ b/chromium/components/unified_consent/unified_consent_service_unittest.cc
@@ -0,0 +1,645 @@
+// 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/unified_consent/unified_consent_service.h"
+
+#include <map>
+#include <memory>
+
+#include "base/message_loop/message_loop.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
+#include "components/sync/base/sync_prefs.h"
+#include "components/sync/driver/fake_sync_service.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "components/unified_consent/pref_names.h"
+#include "components/unified_consent/scoped_unified_consent.h"
+#include "components/unified_consent/unified_consent_service_client.h"
+#include "services/identity/public/cpp/identity_test_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace unified_consent {
+namespace {
+
+class TestSyncService : public syncer::FakeSyncService {
+ public:
+ explicit TestSyncService(PrefService* pref_service)
+ : pref_service_(pref_service) {}
+
+ int GetDisableReasons() const override { return DISABLE_REASON_NONE; }
+ bool IsFirstSetupComplete() const override { return true; }
+ bool IsEngineInitialized() const override { return engine_initialized_; }
+ void AddObserver(syncer::SyncServiceObserver* observer) override {
+ observer_ = observer;
+ }
+ void OnUserChoseDatatypes(bool sync_everything,
+ syncer::ModelTypeSet chosen_types) override {
+ syncer::SyncPrefs(pref_service_).SetKeepEverythingSynced(sync_everything);
+ chosen_types_ = chosen_types;
+ }
+ syncer::ModelTypeSet GetPreferredDataTypes() const override {
+ syncer::ModelTypeSet preferred = chosen_types_;
+ // Add this for the Migration_UpdateSettings test.
+ preferred.Put(syncer::HISTORY_DELETE_DIRECTIVES);
+ return preferred;
+ }
+ bool IsUsingSecondaryPassphrase() const override {
+ return is_using_passphrase_;
+ }
+ SyncService::State GetState() const override { return state_; }
+
+ void SetEngineInitialized(bool engine_initialized) {
+ engine_initialized_ = engine_initialized;
+ }
+ void SetIsUsingPassphrase(bool using_passphrase) {
+ is_using_passphrase_ = using_passphrase;
+ }
+ void SetState(SyncService::State state) { state_ = state; }
+
+ void FireStateChanged() {
+ if (observer_)
+ observer_->OnStateChanged(this);
+ }
+
+ private:
+ syncer::SyncServiceObserver* observer_ = nullptr;
+ bool engine_initialized_ = true;
+ syncer::ModelTypeSet chosen_types_ = syncer::UserSelectableTypes();
+ SyncService::State state_ = SyncService::State::ACTIVE;
+ bool is_using_passphrase_ = false;
+ PrefService* pref_service_;
+};
+
+const char kSpellCheckDummyEnabled[] = "spell_check_dummy.enabled";
+
+class FakeUnifiedConsentServiceClient : public UnifiedConsentServiceClient {
+ public:
+ FakeUnifiedConsentServiceClient(PrefService* pref_service)
+ : pref_service_(pref_service) {
+ // When the |kSpellCheckDummyEnabled| pref is changed, all observers should
+ // be fired.
+ ObserveServicePrefChange(Service::kSpellCheck, kSpellCheckDummyEnabled,
+ pref_service);
+ }
+ ~FakeUnifiedConsentServiceClient() override = default;
+
+ // UnifiedConsentServiceClient:
+ ServiceState GetServiceState(Service service) override {
+ if (is_not_supported_[service])
+ return ServiceState::kNotSupported;
+ bool enabled;
+ // Special treatment for spell check.
+ if (service == Service::kSpellCheck) {
+ enabled = pref_service_->GetBoolean(kSpellCheckDummyEnabled);
+ } else {
+ enabled = service_enabled_[service];
+ }
+ return enabled ? ServiceState::kEnabled : ServiceState::kDisabled;
+ }
+ void SetServiceEnabled(Service service, bool enabled) override {
+ if (is_not_supported_[service])
+ return;
+ // Special treatment for spell check.
+ if (service == Service::kSpellCheck) {
+ pref_service_->SetBoolean(kSpellCheckDummyEnabled, enabled);
+ return;
+ }
+ bool should_notify_observers = service_enabled_[service] != enabled;
+ service_enabled_[service] = enabled;
+ if (should_notify_observers)
+ FireOnServiceStateChanged(service);
+ }
+
+ void SetServiceNotSupported(Service service) {
+ is_not_supported_[service] = true;
+ }
+
+ private:
+ std::map<Service, bool> service_enabled_;
+ std::map<Service, bool> is_not_supported_;
+
+ PrefService* pref_service_;
+};
+
+} // namespace
+
+class UnifiedConsentServiceTest : public testing::Test {
+ public:
+ UnifiedConsentServiceTest() : sync_service_(&pref_service_) {}
+
+ // testing::Test:
+ void SetUp() override {
+ pref_service_.registry()->RegisterBooleanPref(
+ autofill::prefs::kAutofillWalletImportEnabled, false);
+ UnifiedConsentService::RegisterPrefs(pref_service_.registry());
+ syncer::SyncPrefs::RegisterProfilePrefs(pref_service_.registry());
+ pref_service_.registry()->RegisterBooleanPref(kSpellCheckDummyEnabled,
+ false);
+ }
+
+ void TearDown() override {
+ if (consent_service_)
+ consent_service_->Shutdown();
+ }
+
+ void CreateConsentService(bool client_services_on_by_default = false) {
+ if (!scoped_unified_consent_) {
+ SetUnifiedConsentFeatureState(
+ unified_consent::UnifiedConsentFeatureState::kEnabledWithBump);
+ }
+
+ auto client =
+ std::make_unique<FakeUnifiedConsentServiceClient>(&pref_service_);
+ if (client_services_on_by_default) {
+ for (int i = 0; i <= static_cast<int>(Service::kLast); ++i) {
+ Service service = static_cast<Service>(i);
+ client->SetServiceEnabled(service, true);
+ }
+ pref_service_.SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ true);
+ }
+ consent_service_ = std::make_unique<UnifiedConsentService>(
+ std::move(client), &pref_service_,
+ identity_test_environment_.identity_manager(), &sync_service_);
+ service_client_ = (FakeUnifiedConsentServiceClient*)
+ consent_service_->service_client_.get();
+ }
+
+ void SetUnifiedConsentFeatureState(
+ unified_consent::UnifiedConsentFeatureState feature_state) {
+ // First reset |scoped_unified_consent_| to nullptr in case it was set
+ // before and then initialize it with the new value. This makes sure that
+ // the old scoped object is deleted before the new one is created.
+ scoped_unified_consent_.reset();
+ scoped_unified_consent_.reset(
+ new unified_consent::ScopedUnifiedConsent(feature_state));
+ }
+
+ bool AreAllNonPersonalizedServicesEnabled() {
+ return consent_service_->AreAllNonPersonalizedServicesEnabled();
+ }
+
+ bool AreAllOnByDefaultPrivacySettingsOn() {
+ return consent_service_->AreAllOnByDefaultPrivacySettingsOn();
+ }
+
+ unified_consent::MigrationState GetMigrationState() {
+ int migration_state_int =
+ pref_service_.GetInteger(prefs::kUnifiedConsentMigrationState);
+ return static_cast<unified_consent::MigrationState>(migration_state_int);
+ }
+
+ protected:
+ base::MessageLoop message_loop_;
+ sync_preferences::TestingPrefServiceSyncable pref_service_;
+ identity::IdentityTestEnvironment identity_test_environment_;
+ TestSyncService sync_service_;
+ std::unique_ptr<UnifiedConsentService> consent_service_;
+ FakeUnifiedConsentServiceClient* service_client_ = nullptr;
+
+ std::unique_ptr<ScopedUnifiedConsent> scoped_unified_consent_;
+};
+
+TEST_F(UnifiedConsentServiceTest, DefaultValuesWhenSignedOut) {
+ CreateConsentService();
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+}
+
+TEST_F(UnifiedConsentServiceTest, EnableUnfiedConsent) {
+ CreateConsentService();
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+
+ // Enable Unified Consent enables all non-personaized features
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, true);
+ EXPECT_TRUE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+
+ // Disable unified consent does not disable any of the non-personalized
+ // features.
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, false);
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+}
+
+TEST_F(UnifiedConsentServiceTest, EnableUnfiedConsent_WithUnsupportedService) {
+ CreateConsentService();
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+ service_client_->SetServiceNotSupported(Service::kSpellCheck);
+ EXPECT_EQ(service_client_->GetServiceState(Service::kSpellCheck),
+ ServiceState::kNotSupported);
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+
+ // Enable Unified Consent enables all supported non-personalized features
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, true);
+ EXPECT_TRUE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+
+ // Disable unified consent does not disable any of the supported
+ // non-personalized features.
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, false);
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+}
+
+TEST_F(UnifiedConsentServiceTest, EnableUnfiedConsent_SyncNotActive) {
+ CreateConsentService();
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ sync_service_.OnUserChoseDatatypes(false, syncer::UserSelectableTypes());
+ syncer::SyncPrefs sync_prefs(&pref_service_);
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_FALSE(consent_service_->IsUnifiedConsentGiven());
+
+ // Make sure sync is not active.
+ sync_service_.SetEngineInitialized(false);
+ EXPECT_FALSE(sync_service_.IsEngineInitialized());
+ sync_service_.SetState(syncer::SyncService::State::INITIALIZING);
+ EXPECT_NE(sync_service_.GetState(), syncer::SyncService::State::ACTIVE);
+
+ // Opt into unified consent.
+ consent_service_->SetUnifiedConsentGiven(true);
+ EXPECT_TRUE(consent_service_->IsUnifiedConsentGiven());
+
+ // Couldn't sync everything because sync is not active.
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+
+ // Initalize sync engine and therefore activate sync.
+ sync_service_.SetEngineInitialized(true);
+ sync_service_.SetState(syncer::SyncService::State::ACTIVE);
+ EXPECT_EQ(sync_service_.GetState(), syncer::SyncService::State::ACTIVE);
+ sync_service_.FireStateChanged();
+
+ // UnifiedConsentService starts syncing everything.
+ EXPECT_TRUE(sync_prefs.HasKeepEverythingSynced());
+}
+
+TEST_F(UnifiedConsentServiceTest, EnableUnfiedConsent_WithCustomPassphrase) {
+ CreateConsentService();
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ EXPECT_FALSE(consent_service_->IsUnifiedConsentGiven());
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+
+ // Enable Unified Consent.
+ consent_service_->SetUnifiedConsentGiven(true);
+ EXPECT_TRUE(consent_service_->IsUnifiedConsentGiven());
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+
+ // Set custom passphrase.
+ sync_service_.SetIsUsingPassphrase(true);
+ sync_service_.FireStateChanged();
+
+ // Setting a custom passphrase forces off unified consent given.
+ EXPECT_FALSE(consent_service_->IsUnifiedConsentGiven());
+}
+
+// Test whether unified consent is disabled when any of its dependent services
+// gets disabled.
+TEST_F(UnifiedConsentServiceTest, DisableUnfiedConsentWhenServiceIsDisabled) {
+ CreateConsentService();
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+
+ // Enable Unified Consent enables all supported non-personalized features
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, true);
+ EXPECT_TRUE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+
+ // Disabling child service disables unified consent.
+ pref_service_.SetBoolean(kSpellCheckDummyEnabled, false);
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+}
+
+// Test whether unified consent is disabled when any of its dependent services
+// gets disabled before startup.
+TEST_F(UnifiedConsentServiceTest,
+ DisableUnfiedConsentWhenServiceIsDisabled_OnStartup) {
+ CreateConsentService();
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+
+ // Enable Unified Consent enables all supported non-personalized features
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, true);
+ EXPECT_TRUE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+
+ // Simulate shutdown.
+ consent_service_->Shutdown();
+ consent_service_.reset();
+
+ // Disable child service.
+ pref_service_.SetBoolean(kSpellCheckDummyEnabled, false);
+
+ // Unified Consent is disabled during creation of the consent service because
+ // not all non-personalized services are enabled.
+ CreateConsentService();
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+}
+
+#if !defined(OS_CHROMEOS)
+TEST_F(UnifiedConsentServiceTest, Migration_SyncingEverythingAndAllServicesOn) {
+ base::HistogramTester histogram_tester;
+
+ // Create inconsistent state.
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ sync_service_.OnUserChoseDatatypes(true, syncer::UserSelectableTypes());
+ syncer::SyncPrefs sync_prefs(&pref_service_);
+ EXPECT_TRUE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ sync_service_.SetState(
+ syncer::SyncService::State::PENDING_DESIRED_CONFIGURATION);
+ EXPECT_FALSE(sync_service_.IsSyncActive());
+
+ CreateConsentService(true /* client services on by default */);
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+ // After the creation of the consent service, the profile started to migrate
+ // (but waiting for sync init) and |ShouldShowConsentBump| should return true.
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_EQ(GetMigrationState(),
+ unified_consent::MigrationState::kInProgressWaitForSyncInit);
+ EXPECT_TRUE(consent_service_->ShouldShowConsentBump());
+ // Sync-everything is still on because sync is not active yet.
+ EXPECT_TRUE(sync_prefs.HasKeepEverythingSynced());
+
+ // When sync is active, the migration should continue and finish.
+ sync_service_.SetState(syncer::SyncService::State::ACTIVE);
+ sync_service_.FireStateChanged();
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+
+ // No metric for the consent bump suppress reason should have been recorded at
+ // this point.
+ histogram_tester.ExpectTotalCount("UnifiedConsent.ConsentBump.SuppressReason",
+ 0);
+
+ // When the user signs out, the migration state changes to completed and the
+ // consent bump doesn't need to be shown anymore.
+ identity_test_environment_.ClearPrimaryAccount();
+ EXPECT_EQ(GetMigrationState(), unified_consent::MigrationState::kCompleted);
+ EXPECT_FALSE(consent_service_->ShouldShowConsentBump());
+ // A metric for the consent bump suppress reason should have been recorded at
+ // this point.
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.ConsentBump.SuppressReason",
+ unified_consent::ConsentBumpSuppressReason::kUserSignedOut, 1);
+}
+
+TEST_F(UnifiedConsentServiceTest, Migration_SyncingEverythingAndServicesOff) {
+ base::HistogramTester histogram_tester;
+
+ // Create inconsistent state.
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ sync_service_.OnUserChoseDatatypes(true, syncer::UserSelectableTypes());
+ syncer::SyncPrefs sync_prefs(&pref_service_);
+ EXPECT_TRUE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_TRUE(sync_service_.IsSyncActive());
+
+ CreateConsentService();
+ EXPECT_FALSE(AreAllOnByDefaultPrivacySettingsOn());
+ // After the creation of the consent service, the profile is migrated and
+ // |ShouldShowConsentBump| should return false.
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_EQ(GetMigrationState(), unified_consent::MigrationState::kCompleted);
+ EXPECT_FALSE(consent_service_->ShouldShowConsentBump());
+
+ // A metric for the consent bump suppress reason should have been recorded at
+ // this point.
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.ConsentBump.SuppressReason",
+ unified_consent::ConsentBumpSuppressReason::kPrivacySettingOff, 1);
+}
+#endif // !defined(OS_CHROMEOS)
+
+TEST_F(UnifiedConsentServiceTest, Migration_NotSyncingEverything) {
+ base::HistogramTester histogram_tester;
+
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ sync_service_.OnUserChoseDatatypes(false, syncer::UserSelectableTypes());
+ syncer::SyncPrefs sync_prefs(&pref_service_);
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+
+ CreateConsentService();
+ // When the user is not syncing everything the migration is completed after
+ // the creation of the consent service.
+ EXPECT_EQ(GetMigrationState(), unified_consent::MigrationState::kCompleted);
+ // The suppress reason for not showing the consent bump should be recorded.
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.ConsentBump.SuppressReason",
+ unified_consent::ConsentBumpSuppressReason::kSyncEverythingOff, 1);
+}
+
+TEST_F(UnifiedConsentServiceTest, Migration_UpdateSettings) {
+ // Create user that syncs everything
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ sync_service_.OnUserChoseDatatypes(true, syncer::UserSelectableTypes());
+ syncer::SyncPrefs sync_prefs(&pref_service_);
+ EXPECT_TRUE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_TRUE(sync_service_.IsSyncActive());
+ EXPECT_TRUE(sync_service_.GetPreferredDataTypes().Has(syncer::USER_EVENTS));
+ // Url keyed data collection is off before the migration.
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+
+ CreateConsentService();
+ EXPECT_EQ(GetMigrationState(), unified_consent::MigrationState::kCompleted);
+ // During the migration USER_EVENTS is disabled and Url keyed data collection
+ // is enabled.
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_FALSE(sync_service_.GetPreferredDataTypes().Has(syncer::USER_EVENTS));
+ EXPECT_TRUE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+}
+
+#if !defined(OS_CHROMEOS)
+TEST_F(UnifiedConsentServiceTest, ClearPrimaryAccountDisablesSomeServices) {
+ CreateConsentService();
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+
+ // Precondition: Enable unified consent.
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, true);
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+
+ // Clearing primary account revokes unfied consent and a couple of other
+ // non-personalized services.
+ identity_test_environment_.ClearPrimaryAccount();
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+ EXPECT_EQ(service_client_->GetServiceState(Service::kSpellCheck),
+ ServiceState::kDisabled);
+ EXPECT_EQ(
+ service_client_->GetServiceState(Service::kSafeBrowsingExtendedReporting),
+ ServiceState::kDisabled);
+
+ // Consent is not revoked for the following services.
+ EXPECT_EQ(service_client_->GetServiceState(Service::kAlternateErrorPages),
+ ServiceState::kEnabled);
+ EXPECT_EQ(service_client_->GetServiceState(Service::kMetricsReporting),
+ ServiceState::kEnabled);
+ EXPECT_EQ(service_client_->GetServiceState(Service::kNetworkPrediction),
+ ServiceState::kEnabled);
+ EXPECT_EQ(service_client_->GetServiceState(Service::kSearchSuggest),
+ ServiceState::kEnabled);
+ EXPECT_EQ(service_client_->GetServiceState(Service::kSafeBrowsing),
+ ServiceState::kEnabled);
+}
+
+TEST_F(UnifiedConsentServiceTest, Migration_NotSignedIn) {
+ base::HistogramTester histogram_tester;
+
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+
+ CreateConsentService();
+ // Since there were not inconsistencies, the migration is completed after the
+ // creation of the consent service.
+ EXPECT_EQ(GetMigrationState(), unified_consent::MigrationState::kCompleted);
+ // The suppress reason for not showing the consent bump should be recorded.
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.ConsentBump.SuppressReason",
+ unified_consent::ConsentBumpSuppressReason::kNotSignedIn, 1);
+}
+#endif // !defined(OS_CHROMEOS)
+
+TEST_F(UnifiedConsentServiceTest, Rollback_WasSyncingEverything) {
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ syncer::SyncPrefs sync_prefs(&pref_service_);
+ sync_service_.OnUserChoseDatatypes(true, syncer::UserSelectableTypes());
+ EXPECT_TRUE(sync_prefs.HasKeepEverythingSynced());
+
+ // Migrate
+ CreateConsentService(true /* client services on by default */);
+ // Check expectations after migration.
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_EQ(unified_consent::MigrationState::kCompleted, GetMigrationState());
+ EXPECT_TRUE(consent_service_->ShouldShowConsentBump());
+
+ consent_service_->Shutdown();
+ consent_service_.reset();
+ SetUnifiedConsentFeatureState(UnifiedConsentFeatureState::kDisabled);
+
+ // Rollback
+ UnifiedConsentService::RollbackIfNeeded(&pref_service_, &sync_service_);
+ // Unified consent prefs should be cleared.
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_EQ(unified_consent::MigrationState::kNotInitialized,
+ GetMigrationState());
+ // Sync everything should be back on.
+ EXPECT_TRUE(sync_prefs.HasKeepEverythingSynced());
+
+ // Run until idle so the RollbackHelper is deleted.
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(UnifiedConsentServiceTest, Rollback_WasNotSyncingEverything) {
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ syncer::SyncPrefs sync_prefs(&pref_service_);
+ syncer::ModelTypeSet chosen_data_types = syncer::UserSelectableTypes();
+ chosen_data_types.Remove(syncer::BOOKMARKS);
+ sync_service_.OnUserChoseDatatypes(false, chosen_data_types);
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_FALSE(sync_service_.GetPreferredDataTypes().HasAll(
+ syncer::UserSelectableTypes()));
+
+ // Migrate
+ CreateConsentService();
+ // Check expectations after migration.
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_EQ(unified_consent::MigrationState::kCompleted, GetMigrationState());
+
+ consent_service_->Shutdown();
+ consent_service_.reset();
+
+ // Rollback
+ UnifiedConsentService::RollbackIfNeeded(&pref_service_, &sync_service_);
+ // Unified consent prefs should be cleared.
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_EQ(unified_consent::MigrationState::kNotInitialized,
+ GetMigrationState());
+
+ // Sync everything should be off because not all user types were on.
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+
+ // Run until idle so the RollbackHelper is deleted.
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(UnifiedConsentServiceTest, SettingsHistogram_None) {
+ base::HistogramTester histogram_tester;
+ // Disable all services.
+ sync_service_.OnUserChoseDatatypes(false, syncer::ModelTypeSet());
+ CreateConsentService();
+
+ histogram_tester.ExpectUniqueSample(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kNone, 1);
+}
+
+TEST_F(UnifiedConsentServiceTest, SettingsHistogram_UnifiedConsentGiven) {
+ base::HistogramTester histogram_tester;
+ // Unified consent is given.
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ pref_service_.SetInteger(
+ prefs::kUnifiedConsentMigrationState,
+ static_cast<int>(unified_consent::MigrationState::kCompleted));
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, true);
+ CreateConsentService(true);
+
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kNone, 0);
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kUnifiedConsentGiven, 1);
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kUserEvents, 1);
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kUrlKeyedAnonymizedDataCollection, 1);
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kSafeBrowsingExtendedReporting, 1);
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kSpellCheck, 1);
+ histogram_tester.ExpectTotalCount(
+ "UnifiedConsent.SyncAndGoogleServicesSettings", 5);
+}
+
+TEST_F(UnifiedConsentServiceTest, SettingsHistogram_NoUnifiedConsentGiven) {
+ base::HistogramTester histogram_tester;
+ // Unified consent is not given. Only spellcheck is enabled.
+ pref_service_.SetBoolean(kSpellCheckDummyEnabled, true);
+ CreateConsentService();
+
+ // kUserEvents should have no sample even though the sync preference is set,
+ // because the user is not signed in.
+ histogram_tester.ExpectUniqueSample(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kSpellCheck, 1);
+}
+
+} // namespace unified_consent
diff --git a/chromium/components/unified_consent/url_keyed_data_collection_consent_helper.cc b/chromium/components/unified_consent/url_keyed_data_collection_consent_helper.cc
new file mode 100644
index 00000000000..17f2e88e7a2
--- /dev/null
+++ b/chromium/components/unified_consent/url_keyed_data_collection_consent_helper.cc
@@ -0,0 +1,191 @@
+// 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/unified_consent/url_keyed_data_collection_consent_helper.h"
+
+#include "base/bind.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/driver/sync_service.h"
+#include "components/sync/driver/sync_service_observer.h"
+#include "components/sync/driver/sync_service_utils.h"
+#include "components/unified_consent/pref_names.h"
+
+#include <map>
+#include <set>
+
+namespace unified_consent {
+
+namespace {
+
+class PrefBasedUrlKeyedDataCollectionConsentHelper
+ : public UrlKeyedDataCollectionConsentHelper {
+ public:
+ explicit PrefBasedUrlKeyedDataCollectionConsentHelper(
+ PrefService* pref_service);
+ ~PrefBasedUrlKeyedDataCollectionConsentHelper() override = default;
+
+ // UrlKeyedDataCollectionConsentHelper:
+ bool IsEnabled() override;
+
+ private:
+ void OnPrefChanged();
+ PrefService* pref_service_; // weak (must outlive this)
+ PrefChangeRegistrar pref_change_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefBasedUrlKeyedDataCollectionConsentHelper);
+};
+
+class SyncBasedUrlKeyedDataCollectionConsentHelper
+ : public UrlKeyedDataCollectionConsentHelper,
+ syncer::SyncServiceObserver {
+ public:
+ SyncBasedUrlKeyedDataCollectionConsentHelper(
+ syncer::SyncService* sync_service,
+ std::set<syncer::ModelType> sync_data_types);
+ ~SyncBasedUrlKeyedDataCollectionConsentHelper() override;
+
+ // UrlKeyedDataCollectionConsentHelper:
+ bool IsEnabled() override;
+
+ // syncer::SyncServiceObserver:
+ void OnStateChanged(syncer::SyncService* sync) override;
+ void OnSyncShutdown(syncer::SyncService* sync) override;
+
+ private:
+ void UpdateSyncDataTypeStates();
+
+ syncer::SyncService* sync_service_;
+ std::map<syncer::ModelType, syncer::UploadState> sync_data_type_states_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncBasedUrlKeyedDataCollectionConsentHelper);
+};
+
+PrefBasedUrlKeyedDataCollectionConsentHelper::
+ PrefBasedUrlKeyedDataCollectionConsentHelper(PrefService* pref_service)
+ : pref_service_(pref_service) {
+ pref_change_registrar_.Init(pref_service_);
+ pref_change_registrar_.Add(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ base::BindRepeating(
+ &PrefBasedUrlKeyedDataCollectionConsentHelper::OnPrefChanged,
+ base::Unretained(this)));
+}
+
+bool PrefBasedUrlKeyedDataCollectionConsentHelper::IsEnabled() {
+ return pref_service_->GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled);
+}
+
+void PrefBasedUrlKeyedDataCollectionConsentHelper::OnPrefChanged() {
+ FireOnStateChanged();
+}
+
+SyncBasedUrlKeyedDataCollectionConsentHelper::
+ SyncBasedUrlKeyedDataCollectionConsentHelper(
+ syncer::SyncService* sync_service,
+ std::set<syncer::ModelType> sync_data_types)
+ : sync_service_(sync_service) {
+ DCHECK(!sync_data_types.empty());
+
+ for (const auto& sync_data_type : sync_data_types) {
+ sync_data_type_states_[sync_data_type] = syncer::UploadState::NOT_ACTIVE;
+ }
+ UpdateSyncDataTypeStates();
+
+ if (sync_service_)
+ sync_service_->AddObserver(this);
+}
+
+SyncBasedUrlKeyedDataCollectionConsentHelper::
+ ~SyncBasedUrlKeyedDataCollectionConsentHelper() {
+ if (sync_service_)
+ sync_service_->RemoveObserver(this);
+}
+
+bool SyncBasedUrlKeyedDataCollectionConsentHelper::IsEnabled() {
+ for (const auto& sync_data_type_states : sync_data_type_states_) {
+ if (sync_data_type_states.second != syncer::UploadState::ACTIVE)
+ return false;
+ }
+ return true;
+}
+
+void SyncBasedUrlKeyedDataCollectionConsentHelper::OnStateChanged(
+ syncer::SyncService* sync_service) {
+ DCHECK_EQ(sync_service_, sync_service);
+ bool enabled_before_state_updated = IsEnabled();
+ UpdateSyncDataTypeStates();
+ if (enabled_before_state_updated != IsEnabled())
+ FireOnStateChanged();
+}
+
+void SyncBasedUrlKeyedDataCollectionConsentHelper::OnSyncShutdown(
+ syncer::SyncService* sync_service) {
+ DCHECK_EQ(sync_service_, sync_service);
+ sync_service_->RemoveObserver(this);
+ sync_service_ = nullptr;
+}
+
+void SyncBasedUrlKeyedDataCollectionConsentHelper::UpdateSyncDataTypeStates() {
+ for (auto iter = sync_data_type_states_.begin();
+ iter != sync_data_type_states_.end(); ++iter) {
+ iter->second = syncer::GetUploadToGoogleState(sync_service_, iter->first);
+ }
+}
+
+} // namespace
+
+UrlKeyedDataCollectionConsentHelper::UrlKeyedDataCollectionConsentHelper() =
+ default;
+UrlKeyedDataCollectionConsentHelper::~UrlKeyedDataCollectionConsentHelper() =
+ default;
+
+// static
+std::unique_ptr<UrlKeyedDataCollectionConsentHelper>
+UrlKeyedDataCollectionConsentHelper::NewAnonymizedDataCollectionConsentHelper(
+ bool is_unified_consent_enabled,
+ PrefService* pref_service,
+ syncer::SyncService* sync_service) {
+ if (is_unified_consent_enabled) {
+ return std::make_unique<PrefBasedUrlKeyedDataCollectionConsentHelper>(
+ pref_service);
+ }
+
+ return std::make_unique<SyncBasedUrlKeyedDataCollectionConsentHelper>(
+ sync_service, std::set<syncer::ModelType>(
+ {syncer::ModelType::HISTORY_DELETE_DIRECTIVES}));
+}
+
+// static
+std::unique_ptr<UrlKeyedDataCollectionConsentHelper>
+UrlKeyedDataCollectionConsentHelper::NewPersonalizedDataCollectionConsentHelper(
+ bool is_unified_consent_enabled,
+ syncer::SyncService* sync_service) {
+ if (is_unified_consent_enabled) {
+ return std::make_unique<SyncBasedUrlKeyedDataCollectionConsentHelper>(
+ sync_service, std::set<syncer::ModelType>(
+ {syncer::ModelType::HISTORY_DELETE_DIRECTIVES,
+ syncer::ModelType::USER_EVENTS}));
+ } else {
+ return std::make_unique<SyncBasedUrlKeyedDataCollectionConsentHelper>(
+ sync_service, std::set<syncer::ModelType>(
+ {syncer::ModelType::HISTORY_DELETE_DIRECTIVES}));
+ }
+}
+
+void UrlKeyedDataCollectionConsentHelper::AddObserver(Observer* observer) {
+ observer_list_.AddObserver(observer);
+}
+void UrlKeyedDataCollectionConsentHelper::RemoveObserver(Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+void UrlKeyedDataCollectionConsentHelper::FireOnStateChanged() {
+ for (auto& observer : observer_list_)
+ observer.OnUrlKeyedDataCollectionConsentStateChanged(this);
+}
+
+} // namespace unified_consent
diff --git a/chromium/components/unified_consent/url_keyed_data_collection_consent_helper.h b/chromium/components/unified_consent/url_keyed_data_collection_consent_helper.h
new file mode 100644
index 00000000000..f4d8486ab18
--- /dev/null
+++ b/chromium/components/unified_consent/url_keyed_data_collection_consent_helper.h
@@ -0,0 +1,88 @@
+// 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_UNIFIED_CONSENT_URL_KEYED_DATA_COLLECTION_CONSENT_HELPER_H_
+#define COMPONENTS_UNIFIED_CONSENT_URL_KEYED_DATA_COLLECTION_CONSENT_HELPER_H_
+
+#include <memory>
+
+#include "base/observer_list.h"
+
+class PrefService;
+namespace syncer {
+class SyncService;
+}
+
+namespace unified_consent {
+
+// Helper class that allows clients to check whether the user has consented
+// for URL-keyed data collection.
+class UrlKeyedDataCollectionConsentHelper {
+ public:
+ class Observer {
+ public:
+ // Called when the state of the URL-keyed data collection changes.
+ virtual void OnUrlKeyedDataCollectionConsentStateChanged(
+ UrlKeyedDataCollectionConsentHelper* consent_helper) = 0;
+ };
+
+ // Creates a new |UrlKeyedDataCollectionConsentHelper| instance that checks
+ // whether *anonymized* data collection is enabled. This should be used when
+ // the client needs to check whether the user has granted consent for
+ // *anonymized* URL-keyed data collection.
+ //
+ // Implementation-wise we distinguish the following cases:
+ // 1. If |is_unified_consent_enabled| true, then the instance is backed by
+ // |pref_service|. Url-keyed data collection is enabled if the preference
+ // |prefs::kUrlKeyedAnonymizedDataCollectionEnabled| is set to true.
+ //
+ // 2. If |is_unified_consent_enabled| is false, then the instance is backed by
+ // the sync service. Url-keyed data collection is enabled if sync is active
+ // and if sync history is enabled.
+ //
+ // Note: |pref_service| must outlive the retuned instance.
+ static std::unique_ptr<UrlKeyedDataCollectionConsentHelper>
+ NewAnonymizedDataCollectionConsentHelper(bool is_unified_consent_enabled,
+ PrefService* pref_service,
+ syncer::SyncService* sync_service);
+
+ // Creates a new |UrlKeyedDataCollectionConsentHelper| instance that checks
+ // whether *personalized* data collection is enabled. This should be used when
+ // the client needs to check whether the user has granted consent for
+ // URL-keyed data collection keyed by their Google account.
+ //
+ // Implementation-wise we distinguish the following cases:
+ // 1. If |is_unified_consent_enabled| is true then URL-keyed data collection
+ // is enabled if sync is active and if sync event logger is enabled.
+ // 2. If |is_unified_consent_enabled| is false then URL-keyed data collection
+ // is enabled if sync is active and if sync history is enabled.
+ static std::unique_ptr<UrlKeyedDataCollectionConsentHelper>
+ NewPersonalizedDataCollectionConsentHelper(bool is_unified_consent_enabled,
+ syncer::SyncService* sync_service);
+
+ virtual ~UrlKeyedDataCollectionConsentHelper();
+
+ // Returns true if the user has consented for URL keyed anonymized data
+ // collection.
+ virtual bool IsEnabled() = 0;
+
+ // Methods to register or remove observers.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ protected:
+ UrlKeyedDataCollectionConsentHelper();
+
+ // Fires |OnUrlKeyedDataCollectionConsentStateChanged| on all the observers.
+ void FireOnStateChanged();
+
+ private:
+ base::ObserverList<Observer, true> observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(UrlKeyedDataCollectionConsentHelper);
+};
+
+} // namespace unified_consent
+
+#endif // COMPONENTS_UNIFIED_CONSENT_URL_KEYED_DATA_COLLECTION_CONSENT_HELPER_H_
diff --git a/chromium/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc b/chromium/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc
new file mode 100644
index 00000000000..6359b80f460
--- /dev/null
+++ b/chromium/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc
@@ -0,0 +1,220 @@
+// 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/unified_consent/url_keyed_data_collection_consent_helper.h"
+
+#include <vector>
+
+#include "components/sync/driver/fake_sync_service.h"
+#include "components/sync/engine/cycle/sync_cycle_snapshot.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"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace unified_consent {
+namespace {
+
+class TestSyncService : public syncer::FakeSyncService {
+ public:
+ void set_sync_initialized(bool sync_initialized) {
+ sync_initialized_ = sync_initialized;
+ }
+ void AddActiveDataType(syncer::ModelType type) {
+ sync_active_data_types_.Put(type);
+ }
+ void ClearActiveDataTypes() { sync_active_data_types_.Clear(); }
+ void FireOnStateChangeOnAllObservers() {
+ for (auto& observer : observers_)
+ observer.OnStateChanged(this);
+ }
+
+ // syncer::FakeSyncService:
+ int GetDisableReasons() const override { return DISABLE_REASON_NONE; }
+ syncer::ModelTypeSet GetPreferredDataTypes() const override {
+ return syncer::ModelTypeSet(syncer::ModelType::HISTORY_DELETE_DIRECTIVES,
+ syncer::ModelType::USER_EVENTS,
+ syncer::ModelType::EXTENSIONS);
+ }
+ bool IsFirstSetupComplete() const override { return true; }
+ bool IsEngineInitialized() const override { return true; }
+
+ syncer::SyncCycleSnapshot GetLastCycleSnapshot() const override {
+ if (!sync_initialized_)
+ return syncer::SyncCycleSnapshot();
+ return syncer::SyncCycleSnapshot(
+ syncer::ModelNeutralState(), syncer::ProgressMarkerMap(), false, 5, 2,
+ 7, false, 0, base::Time::Now(), base::Time::Now(),
+ std::vector<int>(syncer::MODEL_TYPE_COUNT, 0),
+ std::vector<int>(syncer::MODEL_TYPE_COUNT, 0),
+ sync_pb::SyncEnums::UNKNOWN_ORIGIN,
+ /*short_poll_interval=*/base::TimeDelta::FromMinutes(30),
+ /*long_poll_interval=*/base::TimeDelta::FromMinutes(180),
+ /*has_remaining_local_changes=*/false);
+ }
+
+ syncer::ModelTypeSet GetActiveDataTypes() const override {
+ return sync_active_data_types_;
+ }
+
+ void AddObserver(syncer::SyncServiceObserver* observer) override {
+ observers_.AddObserver(observer);
+ }
+ void RemoveObserver(syncer::SyncServiceObserver* observer) override {
+ observers_.RemoveObserver(observer);
+ }
+
+ private:
+ bool sync_initialized_ = false;
+ syncer::ModelTypeSet sync_active_data_types_;
+ base::ObserverList<syncer::SyncServiceObserver> observers_;
+};
+
+class UrlKeyedDataCollectionConsentHelperTest
+ : public testing::Test,
+ public UrlKeyedDataCollectionConsentHelper::Observer {
+ public:
+ // testing::Test:
+ void SetUp() override {
+ UnifiedConsentService::RegisterPrefs(pref_service_.registry());
+ }
+
+ void OnUrlKeyedDataCollectionConsentStateChanged(
+ UrlKeyedDataCollectionConsentHelper* consent_helper) override {
+ state_changed_notifications.push_back(consent_helper->IsEnabled());
+ }
+
+ protected:
+ sync_preferences::TestingPrefServiceSyncable pref_service_;
+ std::vector<bool> state_changed_notifications;
+ TestSyncService sync_service_;
+};
+
+TEST_F(UrlKeyedDataCollectionConsentHelperTest,
+ AnonymizedDataCollection_UnifiedConsentEnabled) {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper =
+ UrlKeyedDataCollectionConsentHelper::
+ NewAnonymizedDataCollectionConsentHelper(true, &pref_service_,
+ &sync_service_);
+ helper->AddObserver(this);
+ EXPECT_FALSE(helper->IsEnabled());
+ EXPECT_TRUE(state_changed_notifications.empty());
+
+ pref_service_.SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ true);
+ EXPECT_TRUE(helper->IsEnabled());
+ ASSERT_EQ(1U, state_changed_notifications.size());
+ EXPECT_TRUE(state_changed_notifications[0]);
+
+ state_changed_notifications.clear();
+ pref_service_.SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ false);
+ EXPECT_FALSE(helper->IsEnabled());
+ ASSERT_EQ(1U, state_changed_notifications.size());
+ EXPECT_FALSE(state_changed_notifications[0]);
+ helper->RemoveObserver(this);
+}
+
+TEST_F(UrlKeyedDataCollectionConsentHelperTest,
+ AnonymizedDataCollection_UnifiedConsentDisabled) {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper =
+ UrlKeyedDataCollectionConsentHelper::
+ NewAnonymizedDataCollectionConsentHelper(false, &pref_service_,
+ &sync_service_);
+ helper->AddObserver(this);
+ EXPECT_FALSE(helper->IsEnabled());
+ EXPECT_TRUE(state_changed_notifications.empty());
+
+ sync_service_.set_sync_initialized(true);
+ sync_service_.AddActiveDataType(syncer::ModelType::HISTORY_DELETE_DIRECTIVES);
+ sync_service_.FireOnStateChangeOnAllObservers();
+ EXPECT_TRUE(helper->IsEnabled());
+ EXPECT_EQ(1U, state_changed_notifications.size());
+ helper->RemoveObserver(this);
+}
+
+TEST_F(UrlKeyedDataCollectionConsentHelperTest,
+ AnonymizedDataCollection_UnifiedConsentDisabled_NullSyncService) {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper =
+ UrlKeyedDataCollectionConsentHelper::
+ NewAnonymizedDataCollectionConsentHelper(
+ false /* is_unified_consent_enabled */, &pref_service_,
+ nullptr /* sync_service */);
+ EXPECT_FALSE(helper->IsEnabled());
+}
+
+TEST_F(UrlKeyedDataCollectionConsentHelperTest,
+ PersonalizeddDataCollection_UnifiedConsentEnabled) {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper =
+ UrlKeyedDataCollectionConsentHelper::
+ NewPersonalizedDataCollectionConsentHelper(true, &sync_service_);
+ helper->AddObserver(this);
+ EXPECT_FALSE(helper->IsEnabled());
+ EXPECT_TRUE(state_changed_notifications.empty());
+ sync_service_.set_sync_initialized(true);
+
+ // Peronalized data collection is disabled when only USER_EVENTS are enabled.
+ sync_service_.AddActiveDataType(syncer::ModelType::USER_EVENTS);
+ sync_service_.FireOnStateChangeOnAllObservers();
+ EXPECT_FALSE(helper->IsEnabled());
+ EXPECT_TRUE(state_changed_notifications.empty());
+
+ // Peronalized data collection is disabled when only HISTORY_DELETE_DIRECTIVES
+ // are enabled.
+ sync_service_.ClearActiveDataTypes();
+ sync_service_.AddActiveDataType(syncer::ModelType::HISTORY_DELETE_DIRECTIVES);
+ sync_service_.FireOnStateChangeOnAllObservers();
+ EXPECT_FALSE(helper->IsEnabled());
+ EXPECT_TRUE(state_changed_notifications.empty());
+
+ // Personalized data collection is enabled iff USER_EVENTS and
+ // HISTORY_DELETE_DIRECTIVES are enabled.
+ sync_service_.ClearActiveDataTypes();
+ sync_service_.AddActiveDataType(syncer::ModelType::HISTORY_DELETE_DIRECTIVES);
+ sync_service_.AddActiveDataType(syncer::ModelType::USER_EVENTS);
+ sync_service_.FireOnStateChangeOnAllObservers();
+ EXPECT_TRUE(helper->IsEnabled());
+ EXPECT_EQ(1U, state_changed_notifications.size());
+ helper->RemoveObserver(this);
+}
+
+TEST_F(UrlKeyedDataCollectionConsentHelperTest,
+ PersonalizedDataCollection_UnifiedConsentDisabled) {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper =
+ UrlKeyedDataCollectionConsentHelper::
+ NewPersonalizedDataCollectionConsentHelper(false, &sync_service_);
+ helper->AddObserver(this);
+ EXPECT_FALSE(helper->IsEnabled());
+ EXPECT_TRUE(state_changed_notifications.empty());
+
+ sync_service_.set_sync_initialized(true);
+ sync_service_.AddActiveDataType(syncer::ModelType::HISTORY_DELETE_DIRECTIVES);
+ sync_service_.FireOnStateChangeOnAllObservers();
+ EXPECT_TRUE(helper->IsEnabled());
+ EXPECT_EQ(1U, state_changed_notifications.size());
+ helper->RemoveObserver(this);
+}
+
+TEST_F(UrlKeyedDataCollectionConsentHelperTest,
+ PersonalizedDataCollection_NullSyncService) {
+ {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper =
+ UrlKeyedDataCollectionConsentHelper::
+ NewPersonalizedDataCollectionConsentHelper(
+ false /* is_unified_consent_enabled */,
+ nullptr /* sync_service */);
+ EXPECT_FALSE(helper->IsEnabled());
+ }
+ {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper =
+ UrlKeyedDataCollectionConsentHelper::
+ NewPersonalizedDataCollectionConsentHelper(
+ true /* is_unified_consent_enabled */,
+ nullptr /* sync_service */);
+ EXPECT_FALSE(helper->IsEnabled());
+ }
+}
+
+} // namespace
+} // namespace unified_consent
diff --git a/chromium/components/update_client/BUILD.gn b/chromium/components/update_client/BUILD.gn
index a105b0414ae..f5d203de8f6 100644
--- a/chromium/components/update_client/BUILD.gn
+++ b/chromium/components/update_client/BUILD.gn
@@ -76,6 +76,7 @@ static_library("update_client") {
"//courgette:courgette_lib",
"//crypto",
"//net",
+ "//services/network/public/cpp:cpp",
"//third_party/libxml",
"//url",
]
@@ -92,8 +93,8 @@ static_library("test_support") {
"test_configurator.h",
"test_installer.cc",
"test_installer.h",
- "url_request_post_interceptor.cc",
- "url_request_post_interceptor.h",
+ "url_loader_post_interceptor.cc",
+ "url_loader_post_interceptor.h",
]
public_deps = [
@@ -107,6 +108,7 @@ static_library("test_support") {
"//components/services/unzip:lib",
"//mojo/public/cpp/bindings",
"//net:test_support",
+ "//services/network:test_support",
"//services/service_manager/public/cpp",
"//services/service_manager/public/cpp/test:test_support",
"//services/service_manager/public/mojom",
@@ -132,6 +134,8 @@ bundle_data("unit_tests_bundle_data") {
"//components/test/data/update_client/updatecheck_reply_1.xml",
"//components/test/data/update_client/updatecheck_reply_4.xml",
"//components/test/data/update_client/updatecheck_reply_noupdate.xml",
+ "//components/test/data/update_client/updatecheck_reply_parse_error.xml",
+ "//components/test/data/update_client/updatecheck_reply_unknownapp.xml",
]
outputs = [
"{{bundle_resources_dir}}/" +
@@ -173,6 +177,7 @@ source_set("unit_tests") {
"//components/version_info:version_info",
"//courgette:courgette_lib",
"//net:test_support",
+ "//services/network/public/cpp:cpp_base",
"//services/service_manager/public/cpp",
"//services/service_manager/public/cpp/test:test_support",
"//testing/gmock",
diff --git a/chromium/components/update_client/DEPS b/chromium/components/update_client/DEPS
index bcf55884694..11fec055972 100644
--- a/chromium/components/update_client/DEPS
+++ b/chromium/components/update_client/DEPS
@@ -11,6 +11,8 @@ include_rules = [
"+libxml",
"+mojo",
"+net",
+ "+services/network/public/cpp",
+ "+services/network/test",
"+services/service_manager/public",
"+third_party/libxml",
"+third_party/zlib",
diff --git a/chromium/components/update_client/action_runner_win.cc b/chromium/components/update_client/action_runner_win.cc
index 5b11f3ff807..b92a5089543 100644
--- a/chromium/components/update_client/action_runner_win.cc
+++ b/chromium/components/update_client/action_runner_win.cc
@@ -56,6 +56,10 @@ base::CommandLine ActionRunner::MakeCommandLine(
command_line.AppendSwitchASCII(
"browser-version", component_.config()->GetBrowserVersion().GetString());
command_line.AppendSwitchASCII("sessionid", component_.session_id());
+ const auto app_guid = component_.config()->GetAppGuid();
+ if (!app_guid.empty())
+ command_line.AppendSwitchASCII("appguid", app_guid);
+ VLOG(1) << "run action: " << command_line.GetCommandLineString();
return command_line;
}
diff --git a/chromium/components/update_client/background_downloader_win.cc b/chromium/components/update_client/background_downloader_win.cc
index fb8c9ed1591..29d3db7ce59 100644
--- a/chromium/components/update_client/background_downloader_win.cc
+++ b/chromium/components/update_client/background_downloader_win.cc
@@ -141,7 +141,7 @@ const int kMaxQueuedJobs = 10;
// Retrieves the singleton instance of GIT for this process.
HRESULT GetGit(ComPtr<IGlobalInterfaceTable>* git) {
- return ::CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL,
+ return ::CoCreateInstance(CLSID_StdGlobalInterfaceTable, nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(git->GetAddressOf()));
}
@@ -190,7 +190,7 @@ HRESULT GetFilesInJob(const ComPtr<IBackgroundCopyJob>& job,
for (ULONG i = 0; i != num_files; ++i) {
ComPtr<IBackgroundCopyFile> file;
- if (enum_files->Next(1, file.GetAddressOf(), NULL) == S_OK && file.Get())
+ if (enum_files->Next(1, file.GetAddressOf(), nullptr) == S_OK && file.Get())
files->push_back(file);
}
@@ -316,7 +316,7 @@ HRESULT FindBitsJobIf(Predicate pred,
// the job description matches the component updater jobs.
for (ULONG i = 0; i != job_count; ++i) {
ComPtr<IBackgroundCopyJob> current_job;
- if (enum_jobs->Next(1, current_job.GetAddressOf(), NULL) == S_OK &&
+ if (enum_jobs->Next(1, current_job.GetAddressOf(), nullptr) == S_OK &&
pred(current_job)) {
base::string16 job_name;
hr = GetJobDisplayName(current_job, &job_name);
@@ -384,7 +384,7 @@ void CleanupJob(const ComPtr<IBackgroundCopyJob>& job) {
std::vector<base::FilePath> paths;
for (const auto& file : files) {
base::string16 local_name;
- HRESULT hr = GetJobFileProperties(file, &local_name, NULL, NULL);
+ HRESULT hr = GetJobFileProperties(file, &local_name, nullptr, nullptr);
if (SUCCEEDED(hr))
paths.push_back(base::FilePath(local_name));
}
@@ -804,7 +804,7 @@ HRESULT BackgroundDownloader::CompleteJob() {
base::string16 local_name;
BG_FILE_PROGRESS progress = {0};
- hr = GetJobFileProperties(files.front(), &local_name, NULL, &progress);
+ hr = GetJobFileProperties(files.front(), &local_name, nullptr, &progress);
if (FAILED(hr))
return hr;
diff --git a/chromium/components/update_client/command_line_config_policy.cc b/chromium/components/update_client/command_line_config_policy.cc
index a6456019b22..899d8ace277 100644
--- a/chromium/components/update_client/command_line_config_policy.cc
+++ b/chromium/components/update_client/command_line_config_policy.cc
@@ -37,4 +37,8 @@ GURL CommandLineConfigPolicy::UrlSourceOverride() const {
return GURL();
}
+int CommandLineConfigPolicy::InitialDelay() const {
+ return 0;
+}
+
} // namespace update_client
diff --git a/chromium/components/update_client/command_line_config_policy.h b/chromium/components/update_client/command_line_config_policy.h
index d515c994180..2d201a526ba 100644
--- a/chromium/components/update_client/command_line_config_policy.h
+++ b/chromium/components/update_client/command_line_config_policy.h
@@ -32,6 +32,10 @@ class CommandLineConfigPolicy {
// The override URL for updates. Can be empty.
virtual GURL UrlSourceOverride() const;
+ // If non-zero, time interval in seconds until the first component
+ // update check.
+ virtual int InitialDelay() const;
+
virtual ~CommandLineConfigPolicy() {}
};
diff --git a/chromium/components/update_client/component.cc b/chromium/components/update_client/component.cc
index 6c8ce2c8bcb..4cdaaf730c7 100644
--- a/chromium/components/update_client/component.cc
+++ b/chromium/components/update_client/component.cc
@@ -266,11 +266,18 @@ void Component::Uninstall(const base::Version& version, int reason) {
state_ = std::make_unique<StateUninstalled>(this);
}
-void Component::UpdateCheckComplete() {
+void Component::SetUpdateCheckResult(
+ const base::Optional<ProtocolParser::Result>& result,
+ ErrorCategory error_category,
+ int error) {
DCHECK(thread_checker_.CalledOnValidThread());
-
DCHECK_EQ(ComponentState::kChecking, state());
+ error_category_ = error_category;
+ error_code_ = error;
+ if (result)
+ SetParseResult(result.value());
+
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, std::move(update_check_complete_));
}
@@ -412,6 +419,9 @@ void Component::StateUpdateError::DoHandle() {
auto& component = State::component();
+ DCHECK_NE(ErrorCategory::kNone, component.error_category_);
+ DCHECK_NE(0, component.error_code_);
+
// Create an event only when the server response included an update.
if (component.IsUpdateAvailable())
component.AppendEvent(BuildUpdateCompleteEventElement(component));
diff --git a/chromium/components/update_client/component.h b/chromium/components/update_client/component.h
index 749e3009bba..e9adacd7f38 100644
--- a/chromium/components/update_client/component.h
+++ b/chromium/components/update_client/component.h
@@ -16,6 +16,7 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/optional.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "base/version.h"
@@ -49,14 +50,14 @@ class Component {
CrxUpdateItem GetCrxUpdateItem() const;
- // Called by the UpdateChecker to set the update response for this component.
- void SetParseResult(const ProtocolParser::Result& result);
-
// Sets the uninstall state for this component.
void Uninstall(const base::Version& cur_version, int reason);
// Called by the UpdateEngine when an update check for this component is done.
- void UpdateCheckComplete();
+ void SetUpdateCheckResult(
+ const base::Optional<ProtocolParser::Result>& result,
+ ErrorCategory error_category,
+ int error);
// Returns true if the component has reached a final state and no further
// handling and state transitions are possible.
@@ -92,12 +93,6 @@ class Component {
std::string next_fp() const { return next_fp_; }
void set_next_fp(const std::string& next_fp) { next_fp_ = next_fp; }
- void set_update_check_error(int update_check_error) {
- error_category_ = ErrorCategory::kUpdateCheck;
- error_code_ = update_check_error;
- extra_code1_ = 0;
- }
-
bool is_foreground() const;
const std::vector<std::string>& events() const { return events_; }
@@ -371,6 +366,8 @@ class Component {
// Notifies registered observers about changes in the state of the component.
void NotifyObservers(Events event) const;
+ void SetParseResult(const ProtocolParser::Result& result);
+
base::ThreadChecker thread_checker_;
const std::string id_;
diff --git a/chromium/components/update_client/component_patcher.cc b/chromium/components/update_client/component_patcher.cc
index e0bf014d389..33d1df803d5 100644
--- a/chromium/components/update_client/component_patcher.cc
+++ b/chromium/components/update_client/component_patcher.cc
@@ -66,7 +66,7 @@ void ComponentPatcher::Start(Callback callback) {
void ComponentPatcher::StartPatching() {
commands_.reset(ReadCommands(input_dir_));
- if (!commands_.get()) {
+ if (!commands_) {
DonePatching(UnpackerError::kDeltaBadCommands, 0);
} else {
next_command_ = commands_->begin();
@@ -90,7 +90,7 @@ void ComponentPatcher::PatchNextFile() {
current_operation_ = CreateDeltaUpdateOp(operation, connector_.get());
}
- if (!current_operation_.get()) {
+ if (!current_operation_) {
DonePatching(UnpackerError::kDeltaUnsupportedCommand, 0);
return;
}
diff --git a/chromium/components/update_client/configurator.h b/chromium/components/update_client/configurator.h
index 2d08795d590..370327315c8 100644
--- a/chromium/components/update_client/configurator.h
+++ b/chromium/components/update_client/configurator.h
@@ -11,6 +11,7 @@
#include "base/memory/ref_counted.h"
#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
class GURL;
class PrefService;
@@ -96,6 +97,9 @@ class Configurator : public base::RefCountedThreadSafe<Configurator> {
virtual scoped_refptr<net::URLRequestContextGetter> RequestContext()
const = 0;
+ virtual scoped_refptr<network::SharedURLLoaderFactory> URLLoaderFactory()
+ const = 0;
+
// Returns a new connector to the service manager. That connector is not bound
// to any thread yet.
virtual std::unique_ptr<service_manager::Connector>
@@ -146,6 +150,10 @@ class Configurator : public base::RefCountedThreadSafe<Configurator> {
// feature to support testing.
virtual std::vector<uint8_t> GetRunActionKeyHash() const = 0;
+ // Returns the app GUID with which Chrome is registered with Google Update, or
+ // an empty string if this brand does not integrate with Google Update.
+ virtual std::string GetAppGuid() const = 0;
+
protected:
friend class base::RefCountedThreadSafe<Configurator>;
diff --git a/chromium/components/update_client/ping_manager_unittest.cc b/chromium/components/update_client/ping_manager_unittest.cc
index 32cd92ba9dd..29e5481cfcf 100644
--- a/chromium/components/update_client/ping_manager_unittest.cc
+++ b/chromium/components/update_client/ping_manager_unittest.cc
@@ -19,7 +19,7 @@
#include "components/update_client/protocol_builder.h"
#include "components/update_client/test_configurator.h"
#include "components/update_client/update_engine.h"
-#include "components/update_client/url_request_post_interceptor.h"
+#include "components/update_client/url_loader_post_interceptor.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -103,9 +103,8 @@ scoped_refptr<UpdateContext> PingManagerTest::MakeMockUpdateContext() const {
}
TEST_F(PingManagerTest, SendPing) {
- auto interceptor_factory =
- std::make_unique<InterceptorFactory>(base::ThreadTaskRunnerHandle::Get());
- auto interceptor = interceptor_factory->CreateInterceptor();
+ auto interceptor = std::make_unique<URLLoaderPostInterceptor>(
+ config_->test_url_loader_factory());
EXPECT_TRUE(interceptor);
// Test eventresult="1" is sent for successful updates.
@@ -133,12 +132,12 @@ TEST_F(PingManagerTest, SendPing) {
EXPECT_NE(string::npos, interceptor->GetRequestBody(0).find(" sessionid="));
// Check the ping request does not carry the specific extra request headers.
- EXPECT_FALSE(interceptor->GetRequests()[0].second.HasHeader(
- "X-Goog-Update-Interactivity"));
- EXPECT_FALSE(interceptor->GetRequests()[0].second.HasHeader(
- "X-Goog-Update-Updater"));
- EXPECT_FALSE(
- interceptor->GetRequests()[0].second.HasHeader("X-Goog-Update-AppId"));
+ EXPECT_FALSE(std::get<1>(interceptor->GetRequests()[0])
+ .HasHeader("X-Goog-Update-Interactivity"));
+ EXPECT_FALSE(std::get<1>(interceptor->GetRequests()[0])
+ .HasHeader("X-Goog-Update-Updater"));
+ EXPECT_FALSE(std::get<1>(interceptor->GetRequests()[0])
+ .HasHeader("X-Goog-Update-AppId"));
interceptor->Reset();
}
diff --git a/chromium/components/update_client/protocol_builder_unittest.cc b/chromium/components/update_client/protocol_builder_unittest.cc
index f748ced1951..0570e70e4f2 100644
--- a/chromium/components/update_client/protocol_builder_unittest.cc
+++ b/chromium/components/update_client/protocol_builder_unittest.cc
@@ -48,8 +48,7 @@ TEST(BuildProtocolRequest, UpdaterStateAttributes) {
// When no updater state is provided, then check that the elements and
// attributes related to the updater state are not serialized.
std::string request =
- BuildProtocolRequest("1", "", "", "", "", "", "", "", "", nullptr)
- .c_str();
+ BuildProtocolRequest("1", "", "", "", "", "", "", "", "", nullptr);
EXPECT_EQ(std::string::npos, request.find(" domainjoined"));
EXPECT_EQ(std::string::npos, request.find("<updater"));
diff --git a/chromium/components/update_client/protocol_parser.cc b/chromium/components/update_client/protocol_parser.cc
index 95902f695be..efd88dde6fc 100644
--- a/chromium/components/update_client/protocol_parser.cc
+++ b/chromium/components/update_client/protocol_parser.cc
@@ -321,7 +321,25 @@ bool ParseAppTag(xmlNode* app,
return false;
}
- // Get the <updatecheck> tag.
+ // Read the |status| attribute for the app.
+ // If the status is one of the defined app status error literals, then return
+ // it in the result as if it were an updatecheck status, then stop parsing,
+ // and return success.
+ result->status = GetAttribute(app, "status");
+ if (result->status == "restricted" ||
+ result->status == "error-unknownApplication" ||
+ result->status == "error-invalidAppId")
+ return true;
+
+ // If the status was not handled above and the status is not "ok", then
+ // this must be a status literal that that the parser does not know about.
+ if (!result->status.empty() && result->status != "ok") {
+ *error = "Unknown app status";
+ return false;
+ }
+
+ // Get the <updatecheck> tag if the status is missing or the status is "ok".
+ DCHECK(result->status.empty() || result->status == "ok");
std::vector<xmlNode*> updates = GetChildren(app, "updatecheck");
if (updates.empty()) {
*error = "Missing updatecheck on app.";
diff --git a/chromium/components/update_client/protocol_parser_unittest.cc b/chromium/components/update_client/protocol_parser_unittest.cc
index e34f1869dcf..0a09a6433bf 100644
--- a/chromium/components/update_client/protocol_parser_unittest.cc
+++ b/chromium/components/update_client/protocol_parser_unittest.cc
@@ -289,6 +289,24 @@ const char* kUpdateCheckStatusErrorWithRunAction =
" </app>"
"</response>";
+// Includes four <app> tags with status different than "ok".
+const char* kAppsStatusError =
+ "<?xml version='1.0' encoding='UTF-8'?>"
+ "<response protocol='3.1'>"
+ " <app appid='aaaaaaaa' status='error-unknownApplication'>"
+ " <updatecheck status='error-internal'/>"
+ " </app>"
+ " <app appid='bbbbbbbb' status='restricted'>"
+ " <updatecheck status='error-internal'/>"
+ " </app>"
+ " <app appid='cccccccc' status='error-invalidAppId'>"
+ " <updatecheck status='error-internal'/>"
+ " </app>"
+ " <app appid='dddddddd' status='foobar'>"
+ " <updatecheck status='error-internal'/>"
+ " </app>"
+ "</response>";
+
TEST(ComponentUpdaterProtocolParserTest, Parse) {
ProtocolParser parser;
@@ -324,15 +342,15 @@ TEST(ComponentUpdaterProtocolParserTest, Parse) {
EXPECT_TRUE(parser.Parse(kValidXml));
EXPECT_TRUE(parser.errors().empty());
EXPECT_EQ(1u, parser.results().list.size());
- const ProtocolParser::Result* firstResult = &parser.results().list[0];
- EXPECT_STREQ("ok", firstResult->status.c_str());
- EXPECT_EQ(1u, firstResult->crx_urls.size());
- EXPECT_EQ(GURL("http://example.com/"), firstResult->crx_urls[0]);
- EXPECT_EQ(GURL("http://diff.example.com/"), firstResult->crx_diffurls[0]);
- EXPECT_EQ("1.2.3.4", firstResult->manifest.version);
- EXPECT_EQ("2.0.143.0", firstResult->manifest.browser_min_version);
- EXPECT_EQ(1u, firstResult->manifest.packages.size());
- EXPECT_EQ("extension_1_2_3_4.crx", firstResult->manifest.packages[0].name);
+ const ProtocolParser::Result* first_result = &parser.results().list[0];
+ EXPECT_STREQ("ok", first_result->status.c_str());
+ EXPECT_EQ(1u, first_result->crx_urls.size());
+ EXPECT_EQ(GURL("http://example.com/"), first_result->crx_urls[0]);
+ EXPECT_EQ(GURL("http://diff.example.com/"), first_result->crx_diffurls[0]);
+ EXPECT_EQ("1.2.3.4", first_result->manifest.version);
+ EXPECT_EQ("2.0.143.0", first_result->manifest.browser_min_version);
+ EXPECT_EQ(1u, first_result->manifest.packages.size());
+ EXPECT_EQ("extension_1_2_3_4.crx", first_result->manifest.packages[0].name);
// Parse some xml that uses namespace prefixes.
EXPECT_TRUE(parser.Parse(kUsesNamespacePrefix));
@@ -344,23 +362,23 @@ TEST(ComponentUpdaterProtocolParserTest, Parse) {
EXPECT_TRUE(parser.Parse(valid_xml_with_hash));
EXPECT_TRUE(parser.errors().empty());
EXPECT_FALSE(parser.results().list.empty());
- firstResult = &parser.results().list[0];
- EXPECT_FALSE(firstResult->manifest.packages.empty());
- EXPECT_EQ("1234", firstResult->manifest.packages[0].hash_sha256);
- EXPECT_EQ("5678", firstResult->manifest.packages[0].hashdiff_sha256);
+ first_result = &parser.results().list[0];
+ EXPECT_FALSE(first_result->manifest.packages.empty());
+ EXPECT_EQ("1234", first_result->manifest.packages[0].hash_sha256);
+ EXPECT_EQ("5678", first_result->manifest.packages[0].hashdiff_sha256);
// Parse xml with package size value
EXPECT_TRUE(parser.Parse(valid_xml_with_invalid_sizes));
EXPECT_TRUE(parser.errors().empty());
EXPECT_FALSE(parser.results().list.empty());
- firstResult = &parser.results().list[0];
- EXPECT_FALSE(firstResult->manifest.packages.empty());
- EXPECT_EQ(1234, firstResult->manifest.packages[0].size);
- EXPECT_EQ(-1234, firstResult->manifest.packages[1].size);
- EXPECT_EQ(0, firstResult->manifest.packages[2].size);
- EXPECT_EQ(0, firstResult->manifest.packages[3].size);
- EXPECT_EQ(0, firstResult->manifest.packages[4].size);
- EXPECT_EQ(0, firstResult->manifest.packages[5].size);
+ first_result = &parser.results().list[0];
+ EXPECT_FALSE(first_result->manifest.packages.empty());
+ EXPECT_EQ(1234, first_result->manifest.packages[0].size);
+ EXPECT_EQ(-1234, first_result->manifest.packages[1].size);
+ EXPECT_EQ(0, first_result->manifest.packages[2].size);
+ EXPECT_EQ(0, first_result->manifest.packages[3].size);
+ EXPECT_EQ(0, first_result->manifest.packages[4].size);
+ EXPECT_EQ(0, first_result->manifest.packages[5].size);
// Parse xml with a <daystart> element.
EXPECT_TRUE(parser.Parse(kWithDaystart));
@@ -372,63 +390,82 @@ TEST(ComponentUpdaterProtocolParserTest, Parse) {
EXPECT_TRUE(parser.Parse(kNoUpdate));
EXPECT_TRUE(parser.errors().empty());
EXPECT_FALSE(parser.results().list.empty());
- firstResult = &parser.results().list[0];
- EXPECT_STREQ("noupdate", firstResult->status.c_str());
- EXPECT_EQ(firstResult->extension_id, "12345");
- EXPECT_EQ(firstResult->manifest.version, "");
+ first_result = &parser.results().list[0];
+ EXPECT_STREQ("noupdate", first_result->status.c_str());
+ EXPECT_EQ(first_result->extension_id, "12345");
+ EXPECT_EQ(first_result->manifest.version, "");
// Parse xml with one error and one success <app> tag.
EXPECT_TRUE(parser.Parse(kTwoAppsOneError));
- EXPECT_FALSE(parser.errors().empty());
- EXPECT_EQ(1u, parser.results().list.size());
- firstResult = &parser.results().list[0];
- EXPECT_EQ(firstResult->extension_id, "bbbbbbbb");
- EXPECT_STREQ("ok", firstResult->status.c_str());
- EXPECT_EQ("1.2.3.4", firstResult->manifest.version);
+ EXPECT_TRUE(parser.errors().empty());
+ EXPECT_EQ(2u, parser.results().list.size());
+ first_result = &parser.results().list[0];
+ EXPECT_EQ(first_result->extension_id, "aaaaaaaa");
+ EXPECT_STREQ("error-unknownApplication", first_result->status.c_str());
+ EXPECT_TRUE(first_result->manifest.version.empty());
+ const ProtocolParser::Result* second_result = &parser.results().list[1];
+ EXPECT_EQ(second_result->extension_id, "bbbbbbbb");
+ EXPECT_STREQ("ok", second_result->status.c_str());
+ EXPECT_EQ("1.2.3.4", second_result->manifest.version);
// Parse xml with two apps setting the cohort info.
EXPECT_TRUE(parser.Parse(kTwoAppsSetCohort));
EXPECT_TRUE(parser.errors().empty());
EXPECT_EQ(2u, parser.results().list.size());
- firstResult = &parser.results().list[0];
- EXPECT_EQ(firstResult->extension_id, "aaaaaaaa");
- EXPECT_NE(firstResult->cohort_attrs.find("cohort"),
- firstResult->cohort_attrs.end());
- EXPECT_EQ(firstResult->cohort_attrs.find("cohort")->second, "1:2q3/");
- EXPECT_EQ(firstResult->cohort_attrs.find("cohortname"),
- firstResult->cohort_attrs.end());
- EXPECT_EQ(firstResult->cohort_attrs.find("cohorthint"),
- firstResult->cohort_attrs.end());
- const ProtocolParser::Result* secondResult = &parser.results().list[1];
- EXPECT_EQ(secondResult->extension_id, "bbbbbbbb");
- EXPECT_NE(secondResult->cohort_attrs.find("cohort"),
- secondResult->cohort_attrs.end());
- EXPECT_EQ(secondResult->cohort_attrs.find("cohort")->second, "1:33z@0.33");
- EXPECT_NE(secondResult->cohort_attrs.find("cohortname"),
- secondResult->cohort_attrs.end());
- EXPECT_EQ(secondResult->cohort_attrs.find("cohortname")->second, "cname");
- EXPECT_EQ(secondResult->cohort_attrs.find("cohorthint"),
- secondResult->cohort_attrs.end());
+ first_result = &parser.results().list[0];
+ EXPECT_EQ(first_result->extension_id, "aaaaaaaa");
+ EXPECT_NE(first_result->cohort_attrs.find("cohort"),
+ first_result->cohort_attrs.end());
+ EXPECT_EQ(first_result->cohort_attrs.find("cohort")->second, "1:2q3/");
+ EXPECT_EQ(first_result->cohort_attrs.find("cohortname"),
+ first_result->cohort_attrs.end());
+ EXPECT_EQ(first_result->cohort_attrs.find("cohorthint"),
+ first_result->cohort_attrs.end());
+ EXPECT_EQ(second_result->extension_id, "bbbbbbbb");
+ EXPECT_NE(second_result->cohort_attrs.find("cohort"),
+ second_result->cohort_attrs.end());
+ EXPECT_EQ(second_result->cohort_attrs.find("cohort")->second, "1:33z@0.33");
+ EXPECT_NE(second_result->cohort_attrs.find("cohortname"),
+ second_result->cohort_attrs.end());
+ EXPECT_EQ(second_result->cohort_attrs.find("cohortname")->second, "cname");
+ EXPECT_EQ(second_result->cohort_attrs.find("cohorthint"),
+ second_result->cohort_attrs.end());
EXPECT_TRUE(parser.Parse(kUpdateCheckStatusOkWithRunAction));
EXPECT_TRUE(parser.errors().empty());
EXPECT_FALSE(parser.results().list.empty());
- firstResult = &parser.results().list[0];
- EXPECT_STREQ("ok", firstResult->status.c_str());
- EXPECT_EQ(firstResult->extension_id, "12345");
- EXPECT_STREQ("this", firstResult->action_run.c_str());
+ first_result = &parser.results().list[0];
+ EXPECT_STREQ("ok", first_result->status.c_str());
+ EXPECT_EQ(first_result->extension_id, "12345");
+ EXPECT_STREQ("this", first_result->action_run.c_str());
EXPECT_TRUE(parser.Parse(kUpdateCheckStatusNoUpdateWithRunAction));
EXPECT_TRUE(parser.errors().empty());
EXPECT_FALSE(parser.results().list.empty());
- firstResult = &parser.results().list[0];
- EXPECT_STREQ("noupdate", firstResult->status.c_str());
- EXPECT_EQ(firstResult->extension_id, "12345");
- EXPECT_STREQ("this", firstResult->action_run.c_str());
+ first_result = &parser.results().list[0];
+ EXPECT_STREQ("noupdate", first_result->status.c_str());
+ EXPECT_EQ(first_result->extension_id, "12345");
+ EXPECT_STREQ("this", first_result->action_run.c_str());
EXPECT_TRUE(parser.Parse(kUpdateCheckStatusErrorWithRunAction));
EXPECT_FALSE(parser.errors().empty());
EXPECT_TRUE(parser.results().list.empty());
+
+ EXPECT_TRUE(parser.Parse(kAppsStatusError));
+ EXPECT_STREQ("Unknown app status", parser.errors().c_str());
+ EXPECT_EQ(3u, parser.results().list.size());
+ first_result = &parser.results().list[0];
+ EXPECT_EQ(first_result->extension_id, "aaaaaaaa");
+ EXPECT_STREQ("error-unknownApplication", first_result->status.c_str());
+ EXPECT_TRUE(first_result->manifest.version.empty());
+ second_result = &parser.results().list[1];
+ EXPECT_EQ(second_result->extension_id, "bbbbbbbb");
+ EXPECT_STREQ("restricted", second_result->status.c_str());
+ EXPECT_TRUE(second_result->manifest.version.empty());
+ const ProtocolParser::Result* third_result = &parser.results().list[2];
+ EXPECT_EQ(third_result->extension_id, "cccccccc");
+ EXPECT_STREQ("error-invalidAppId", third_result->status.c_str());
+ EXPECT_TRUE(third_result->manifest.version.empty());
}
} // namespace update_client
diff --git a/chromium/components/update_client/request_sender.cc b/chromium/components/update_client/request_sender.cc
index 190cf58ec48..be6e856db2b 100644
--- a/chromium/components/update_client/request_sender.cc
+++ b/chromium/components/update_client/request_sender.cc
@@ -15,10 +15,10 @@
#include "base/threading/thread_task_runner_handle.h"
#include "components/client_update_protocol/ecdsa.h"
#include "components/update_client/configurator.h"
+#include "components/update_client/update_client_errors.h"
#include "components/update_client/utils.h"
#include "net/http/http_response_headers.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_status.h"
+#include "services/network/public/cpp/simple_url_loader.h"
namespace update_client {
@@ -73,7 +73,7 @@ void RequestSender::Send(
request_sender_callback_ = std::move(request_sender_callback);
if (urls_.empty()) {
- return HandleSendError(-1, 0);
+ return HandleSendError(static_cast<int>(ProtocolError::MISSING_URLS), 0);
}
cur_url_ = urls_.begin();
@@ -81,7 +81,8 @@ void RequestSender::Send(
if (use_signing_) {
public_key_ = GetKey(kKeyPubBytesBase64);
if (public_key_.empty())
- return HandleSendError(-1, 0);
+ return HandleSendError(
+ static_cast<int>(ProtocolError::MISSING_PUBLIC_KEY), 0);
}
SendInternal();
@@ -103,13 +104,19 @@ void RequestSender::SendInternal() {
url = BuildUpdateUrl(url, request_query_string);
}
- url_fetcher_ = SendProtocolRequest(url, request_extra_headers_, request_body_,
- this, config_->RequestContext());
- if (!url_fetcher_.get())
+ update_client::LoadCompleteCallback callback = base::BindOnce(
+ &RequestSender::OnSimpleURLLoaderComplete, base::Unretained(this), url);
+
+ url_loader_ =
+ SendProtocolRequest(url, request_extra_headers_, request_body_,
+ std::move(callback), config_->URLLoaderFactory());
+ if (!url_loader_)
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&RequestSender::SendInternalComplete,
- base::Unretained(this), -1, std::string(),
- std::string(), 0));
+ FROM_HERE,
+ base::BindOnce(&RequestSender::SendInternalComplete,
+ base::Unretained(this),
+ static_cast<int>(ProtocolError::URL_FETCHER_FAILED),
+ std::string(), std::string(), 0));
}
void RequestSender::SendInternalComplete(int error,
@@ -125,7 +132,7 @@ void RequestSender::SendInternalComplete(int error,
}
DCHECK(use_signing_);
- DCHECK(signer_.get());
+ DCHECK(signer_);
if (signer_->ValidateResponse(response_body, response_etag)) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(request_sender_callback_), 0,
@@ -133,7 +140,7 @@ void RequestSender::SendInternalComplete(int error,
return;
}
- error = kErrorResponseNotTrusted;
+ error = static_cast<int>(ProtocolError::RESPONSE_NOT_TRUSTED);
}
DCHECK(error);
@@ -150,30 +157,40 @@ void RequestSender::SendInternalComplete(int error,
HandleSendError(error, retry_after_sec);
}
-void RequestSender::OnURLFetchComplete(const net::URLFetcher* source) {
+void RequestSender::OnSimpleURLLoaderComplete(
+ const GURL& original_url,
+ std::unique_ptr<std::string> response_body) {
DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(source);
- const GURL original_url(source->GetOriginalURL());
VLOG(1) << "request completed from url: " << original_url.spec();
- const int fetch_error(GetFetchError(*source));
- std::string response_body;
- CHECK(source->GetResponseAsString(&response_body));
+ int response_code = -1;
+ if (url_loader_->ResponseInfo() && url_loader_->ResponseInfo()->headers) {
+ response_code = url_loader_->ResponseInfo()->headers->response_code();
+ }
+
+ int fetch_error = -1;
+ if (response_body && response_code == 200) {
+ fetch_error = 0;
+ } else if (response_code != -1) {
+ fetch_error = response_code;
+ } else {
+ fetch_error = url_loader_->NetError();
+ }
int64_t retry_after_sec(-1);
- const auto status(source->GetStatus().status());
- if (original_url.SchemeIsCryptographic() &&
- status == net::URLRequestStatus::SUCCESS) {
- retry_after_sec = GetInt64HeaderValue(source, kHeaderXRetryAfter);
+ if (original_url.SchemeIsCryptographic() && fetch_error > 0) {
+ retry_after_sec =
+ GetInt64HeaderValue(url_loader_.get(), kHeaderXRetryAfter);
retry_after_sec = std::min(retry_after_sec, kMaxRetryAfterSec);
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&RequestSender::SendInternalComplete,
- base::Unretained(this), fetch_error, response_body,
- GetStringHeaderValue(source, kHeaderEtag),
+ base::Unretained(this), fetch_error,
+ response_body ? *response_body : std::string(),
+ GetStringHeaderValue(url_loader_.get(), kHeaderEtag),
static_cast<int>(retry_after_sec)));
}
@@ -202,23 +219,30 @@ GURL RequestSender::BuildUpdateUrl(const GURL& url,
return url.ReplaceComponents(replacements);
}
-std::string RequestSender::GetStringHeaderValue(const net::URLFetcher* source,
- const char* header_name) {
- auto* response_headers(source->GetResponseHeaders());
- if (!response_headers)
- return std::string();
+std::string RequestSender::GetStringHeaderValue(
+ const network::SimpleURLLoader* url_loader,
+ const char* header_name) {
+ DCHECK(url_loader);
+ if (url_loader->ResponseInfo() && url_loader->ResponseInfo()->headers) {
+ std::string etag;
+ return url_loader->ResponseInfo()->headers->EnumerateHeader(
+ nullptr, header_name, &etag)
+ ? etag
+ : std::string();
+ }
- std::string etag;
- return response_headers->EnumerateHeader(nullptr, header_name, &etag)
- ? etag
- : std::string();
+ return std::string();
}
-int64_t RequestSender::GetInt64HeaderValue(const net::URLFetcher* source,
- const char* header_name) {
- auto* response_headers(source->GetResponseHeaders());
- return response_headers ? response_headers->GetInt64HeaderValue(header_name)
- : -1;
+int64_t RequestSender::GetInt64HeaderValue(
+ const network::SimpleURLLoader* url_loader,
+ const char* header_name) {
+ DCHECK(url_loader);
+ if (url_loader->ResponseInfo() && url_loader->ResponseInfo()->headers) {
+ return url_loader->ResponseInfo()->headers->GetInt64HeaderValue(
+ header_name);
+ }
+ return -1;
}
} // namespace update_client
diff --git a/chromium/components/update_client/request_sender.h b/chromium/components/update_client/request_sender.h
index 91a83c9e536..10722312152 100644
--- a/chromium/components/update_client/request_sender.h
+++ b/chromium/components/update_client/request_sender.h
@@ -16,15 +16,14 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/threading/thread_checker.h"
-#include "net/url_request/url_fetcher_delegate.h"
#include "url/gurl.h"
namespace client_update_protocol {
class Ecdsa;
}
-namespace net {
-class URLFetcher;
+namespace network {
+class SimpleURLLoader;
}
namespace update_client {
@@ -35,7 +34,7 @@ class Configurator;
// of responsibility design pattern, where the urls are tried in the order they
// are specified, until the request to one of them succeeds or all have failed.
// CUP signing is optional.
-class RequestSender : public net::URLFetcherDelegate {
+class RequestSender {
public:
// If |error| is 0, then the response is provided in the |response| parameter.
// |retry_after_sec| contains the value of the X-Retry-After response header,
@@ -46,14 +45,8 @@ class RequestSender : public net::URLFetcherDelegate {
using RequestSenderCallback = base::OnceCallback<
void(int error, const std::string& response, int retry_after_sec)>;
- // This value is chosen not to conflict with network errors defined by
- // net/base/net_error_list.h. The callers don't have to handle this error in
- // any meaningful way, but this value may be reported in UMA stats, therefore
- // avoiding collisions with known network errors is desirable.
- enum : int { kErrorResponseNotTrusted = -10000 };
-
explicit RequestSender(scoped_refptr<Configurator> config);
- ~RequestSender() override;
+ ~RequestSender();
// |use_signing| enables CUP signing of protocol messages exchanged using
// this class. |is_foreground| controls the presence and the value for the
@@ -76,16 +69,17 @@ class RequestSender : public net::URLFetcherDelegate {
// Returns the string value of a header of the server response or an empty
// string if the header is not available.
- static std::string GetStringHeaderValue(const net::URLFetcher* source,
- const char* header_name);
+ static std::string GetStringHeaderValue(
+ const network::SimpleURLLoader* url_loader,
+ const char* header_name);
// Returns the integral value of a header of the server response or -1 if
// if the header is not available or a conversion error has occured.
- static int64_t GetInt64HeaderValue(const net::URLFetcher* source,
+ static int64_t GetInt64HeaderValue(const network::SimpleURLLoader* loader,
const char* header_name);
- // Overrides for URLFetcherDelegate.
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ void OnSimpleURLLoaderComplete(const GURL& original_url,
+ std::unique_ptr<std::string> response_body);
// Implements the error handling and url fallback mechanism.
void SendInternal();
@@ -112,7 +106,7 @@ class RequestSender : public net::URLFetcherDelegate {
std::string public_key_;
std::vector<GURL>::const_iterator cur_url_;
- std::unique_ptr<net::URLFetcher> url_fetcher_;
+ std::unique_ptr<network::SimpleURLLoader> url_loader_;
std::unique_ptr<client_update_protocol::Ecdsa> signer_;
DISALLOW_COPY_AND_ASSIGN(RequestSender);
diff --git a/chromium/components/update_client/request_sender_unittest.cc b/chromium/components/update_client/request_sender_unittest.cc
index f480cac3a66..e2eb26316d8 100644
--- a/chromium/components/update_client/request_sender_unittest.cc
+++ b/chromium/components/update_client/request_sender_unittest.cc
@@ -15,8 +15,7 @@
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/update_client/test_configurator.h"
-#include "components/update_client/url_request_post_interceptor.h"
-#include "net/url_request/url_fetcher.h"
+#include "components/update_client/url_loader_post_interceptor.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace update_client {
@@ -25,8 +24,6 @@ namespace {
const char kUrl1[] = "https://localhost2/path1";
const char kUrl2[] = "https://localhost2/path2";
-const char kUrlPath1[] = "path1";
-const char kUrlPath2[] = "path2";
// TODO(sorin): refactor as a utility function for unit tests.
base::FilePath test_file(const char* file) {
@@ -63,10 +60,8 @@ class RequestSenderTest : public testing::Test,
scoped_refptr<TestConfigurator> config_;
std::unique_ptr<RequestSender> request_sender_;
- std::unique_ptr<InterceptorFactory> interceptor_factory_;
- scoped_refptr<URLRequestPostInterceptor> post_interceptor_1_;
- scoped_refptr<URLRequestPostInterceptor> post_interceptor_2_;
+ std::unique_ptr<URLLoaderPostInterceptor> post_interceptor_;
int error_ = 0;
std::string response_;
@@ -89,23 +84,19 @@ void RequestSenderTest::SetUp() {
config_ = base::MakeRefCounted<TestConfigurator>();
request_sender_ = std::make_unique<RequestSender>(config_);
- interceptor_factory_ =
- std::make_unique<InterceptorFactory>(base::ThreadTaskRunnerHandle::Get());
- post_interceptor_1_ =
- interceptor_factory_->CreateInterceptorForPath(kUrlPath1);
- post_interceptor_2_ =
- interceptor_factory_->CreateInterceptorForPath(kUrlPath2);
- EXPECT_TRUE(post_interceptor_1_);
- EXPECT_TRUE(post_interceptor_2_);
+ std::vector<GURL> urls;
+ urls.push_back(GURL(kUrl1));
+ urls.push_back(GURL(kUrl2));
+
+ post_interceptor_ = std::make_unique<URLLoaderPostInterceptor>(
+ urls, config_->test_url_loader_factory());
+ EXPECT_TRUE(post_interceptor_);
}
void RequestSenderTest::TearDown() {
request_sender_ = nullptr;
- post_interceptor_1_ = nullptr;
- post_interceptor_2_ = nullptr;
-
- interceptor_factory_ = nullptr;
+ post_interceptor_.reset();
// Run the threads until they are idle to allow the clean up
// of the network interceptors on the IO thread.
@@ -137,8 +128,8 @@ void RequestSenderTest::RequestSenderComplete(int error,
// not tried.
TEST_P(RequestSenderTest, RequestSendSuccess) {
EXPECT_TRUE(
- post_interceptor_1_->ExpectRequest(std::make_unique<PartialMatch>("test"),
- test_file("updatecheck_reply_1.xml")));
+ post_interceptor_->ExpectRequest(std::make_unique<PartialMatch>("test"),
+ test_file("updatecheck_reply_1.xml")));
const bool is_foreground = GetParam();
request_sender_->Send(
@@ -149,18 +140,16 @@ TEST_P(RequestSenderTest, RequestSendSuccess) {
base::Unretained(this)));
RunThreads();
- EXPECT_EQ(1, post_interceptor_1_->GetHitCount())
- << post_interceptor_1_->GetRequestsAsString();
- EXPECT_EQ(1, post_interceptor_1_->GetCount())
- << post_interceptor_1_->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
- EXPECT_EQ(0, post_interceptor_2_->GetHitCount())
- << post_interceptor_2_->GetRequestsAsString();
- EXPECT_EQ(0, post_interceptor_2_->GetCount())
- << post_interceptor_2_->GetRequestsAsString();
+ EXPECT_EQ(0, post_interceptor_->GetHitCountForURL(GURL(kUrl2)))
+ << post_interceptor_->GetRequestsAsString();
// Sanity check the request.
- EXPECT_STREQ("test", post_interceptor_1_->GetRequestBody(0).c_str());
+ EXPECT_STREQ("test", post_interceptor_->GetRequestBody(0).c_str());
// Check the response post conditions.
EXPECT_EQ(0, error_);
@@ -171,7 +160,7 @@ TEST_P(RequestSenderTest, RequestSendSuccess) {
// Check the interactivity header value.
const auto extra_request_headers =
- post_interceptor_1_->GetRequests()[0].second;
+ std::get<1>(post_interceptor_->GetRequests()[0]);
EXPECT_TRUE(extra_request_headers.HasHeader("X-Goog-Update-Interactivity"));
std::string header;
extra_request_headers.GetHeader("X-Goog-Update-Interactivity", &header);
@@ -181,10 +170,10 @@ TEST_P(RequestSenderTest, RequestSendSuccess) {
// Tests that the request succeeds using the second url after the first url
// has failed.
TEST_F(RequestSenderTest, RequestSendSuccessWithFallback) {
- EXPECT_TRUE(post_interceptor_1_->ExpectRequest(
- std::make_unique<PartialMatch>("test"), 403));
- EXPECT_TRUE(post_interceptor_2_->ExpectRequest(
- std::make_unique<PartialMatch>("test")));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(
+ std::make_unique<PartialMatch>("test"), net::HTTP_FORBIDDEN));
+ EXPECT_TRUE(
+ post_interceptor_->ExpectRequest(std::make_unique<PartialMatch>("test")));
request_sender_->Send(
{GURL(kUrl1), GURL(kUrl2)}, {}, "test", false,
@@ -192,26 +181,26 @@ TEST_F(RequestSenderTest, RequestSendSuccessWithFallback) {
base::Unretained(this)));
RunThreads();
- EXPECT_EQ(1, post_interceptor_1_->GetHitCount())
- << post_interceptor_1_->GetRequestsAsString();
- EXPECT_EQ(1, post_interceptor_1_->GetCount())
- << post_interceptor_1_->GetRequestsAsString();
- EXPECT_EQ(1, post_interceptor_2_->GetHitCount())
- << post_interceptor_2_->GetRequestsAsString();
- EXPECT_EQ(1, post_interceptor_2_->GetCount())
- << post_interceptor_2_->GetRequestsAsString();
-
- EXPECT_STREQ("test", post_interceptor_1_->GetRequestBody(0).c_str());
- EXPECT_STREQ("test", post_interceptor_2_->GetRequestBody(0).c_str());
+ EXPECT_EQ(2, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(2, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetHitCountForURL(GURL(kUrl1)))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetHitCountForURL(GURL(kUrl2)))
+ << post_interceptor_->GetRequestsAsString();
+
+ EXPECT_STREQ("test", post_interceptor_->GetRequestBody(0).c_str());
+ EXPECT_STREQ("test", post_interceptor_->GetRequestBody(1).c_str());
EXPECT_EQ(0, error_);
}
// Tests that the request fails when both urls have failed.
TEST_F(RequestSenderTest, RequestSendFailed) {
- EXPECT_TRUE(post_interceptor_1_->ExpectRequest(
- std::make_unique<PartialMatch>("test"), 403));
- EXPECT_TRUE(post_interceptor_2_->ExpectRequest(
- std::make_unique<PartialMatch>("test"), 403));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(
+ std::make_unique<PartialMatch>("test"), net::HTTP_FORBIDDEN));
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(
+ std::make_unique<PartialMatch>("test"), net::HTTP_FORBIDDEN));
const std::vector<GURL> urls = {GURL(kUrl1), GURL(kUrl2)};
request_sender_ = std::make_unique<RequestSender>(config_);
@@ -221,17 +210,17 @@ TEST_F(RequestSenderTest, RequestSendFailed) {
base::Unretained(this)));
RunThreads();
- EXPECT_EQ(1, post_interceptor_1_->GetHitCount())
- << post_interceptor_1_->GetRequestsAsString();
- EXPECT_EQ(1, post_interceptor_1_->GetCount())
- << post_interceptor_1_->GetRequestsAsString();
- EXPECT_EQ(1, post_interceptor_2_->GetHitCount())
- << post_interceptor_2_->GetRequestsAsString();
- EXPECT_EQ(1, post_interceptor_2_->GetCount())
- << post_interceptor_2_->GetRequestsAsString();
-
- EXPECT_STREQ("test", post_interceptor_1_->GetRequestBody(0).c_str());
- EXPECT_STREQ("test", post_interceptor_2_->GetRequestBody(0).c_str());
+ EXPECT_EQ(2, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(2, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetHitCountForURL(GURL(kUrl1)))
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetHitCountForURL(GURL(kUrl2)))
+ << post_interceptor_->GetRequestsAsString();
+
+ EXPECT_STREQ("test", post_interceptor_->GetRequestBody(0).c_str());
+ EXPECT_STREQ("test", post_interceptor_->GetRequestBody(1).c_str());
EXPECT_EQ(403, error_);
}
@@ -245,14 +234,14 @@ TEST_F(RequestSenderTest, RequestSendFailedNoUrls) {
base::Unretained(this)));
RunThreads();
- EXPECT_EQ(-1, error_);
+ EXPECT_EQ(-10002, error_);
}
// Tests that a CUP request fails if the response is not signed.
TEST_F(RequestSenderTest, RequestSendCupError) {
EXPECT_TRUE(
- post_interceptor_1_->ExpectRequest(std::make_unique<PartialMatch>("test"),
- test_file("updatecheck_reply_1.xml")));
+ post_interceptor_->ExpectRequest(std::make_unique<PartialMatch>("test"),
+ test_file("updatecheck_reply_1.xml")));
const std::vector<GURL> urls = {GURL(kUrl1)};
request_sender_ = std::make_unique<RequestSender>(config_);
@@ -262,13 +251,13 @@ TEST_F(RequestSenderTest, RequestSendCupError) {
base::Unretained(this)));
RunThreads();
- EXPECT_EQ(1, post_interceptor_1_->GetHitCount())
- << post_interceptor_1_->GetRequestsAsString();
- EXPECT_EQ(1, post_interceptor_1_->GetCount())
- << post_interceptor_1_->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(1, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
- EXPECT_STREQ("test", post_interceptor_1_->GetRequestBody(0).c_str());
- EXPECT_EQ(RequestSender::kErrorResponseNotTrusted, error_);
+ EXPECT_STREQ("test", post_interceptor_->GetRequestBody(0).c_str());
+ EXPECT_EQ(-10000, error_);
EXPECT_TRUE(response_.empty());
}
diff --git a/chromium/components/update_client/test_configurator.cc b/chromium/components/update_client/test_configurator.cc
index cbc177ee6a9..882e5db3cb0 100644
--- a/chromium/components/update_client/test_configurator.cc
+++ b/chromium/components/update_client/test_configurator.cc
@@ -13,6 +13,7 @@
#include "components/services/unzip/unzip_service.h"
#include "components/update_client/activity_data_service.h"
#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/service_manager/public/cpp/connector.h"
#include "services/service_manager/public/cpp/service.h"
#include "services/service_manager/public/cpp/test/test_connector_factory.h"
@@ -38,7 +39,10 @@ TestConfigurator::TestConfigurator()
enabled_cup_signing_(false),
enabled_component_updates_(true),
context_(base::MakeRefCounted<net::TestURLRequestContextGetter>(
- base::ThreadTaskRunnerHandle::Get())) {
+ base::ThreadTaskRunnerHandle::Get())),
+ test_shared_loader_factory_(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_)) {
service_manager::TestConnectorFactory::NameToServiceMap services;
services.insert(
std::make_pair("patch_service", std::make_unique<patch::PatchService>()));
@@ -120,6 +124,11 @@ scoped_refptr<net::URLRequestContextGetter> TestConfigurator::RequestContext()
return context_;
}
+scoped_refptr<network::SharedURLLoaderFactory>
+TestConfigurator::URLLoaderFactory() const {
+ return test_shared_loader_factory_;
+}
+
std::unique_ptr<service_manager::Connector>
TestConfigurator::CreateServiceManagerConnector() const {
return connector_->Clone();
@@ -175,6 +184,10 @@ void TestConfigurator::SetPingUrl(const GURL& url) {
ping_url_ = url;
}
+void TestConfigurator::SetAppGuid(const std::string& app_guid) {
+ app_guid_ = app_guid;
+}
+
PrefService* TestConfigurator::GetPrefService() const {
return nullptr;
}
@@ -191,4 +204,8 @@ std::vector<uint8_t> TestConfigurator::GetRunActionKeyHash() const {
return std::vector<uint8_t>(std::begin(gjpm_hash), std::end(gjpm_hash));
}
+std::string TestConfigurator::GetAppGuid() const {
+ return app_guid_;
+}
+
} // namespace update_client
diff --git a/chromium/components/update_client/test_configurator.h b/chromium/components/update_client/test_configurator.h
index 60ba6579c06..c7d4bbf715d 100644
--- a/chromium/components/update_client/test_configurator.h
+++ b/chromium/components/update_client/test_configurator.h
@@ -15,6 +15,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "components/update_client/configurator.h"
+#include "services/network/test/test_url_loader_factory.h"
#include "url/gurl.h"
class PrefService;
@@ -24,6 +25,10 @@ class TestURLRequestContextGetter;
class URLRequestContextGetter;
} // namespace net
+namespace network {
+class SharedURLLoaderFactory;
+} // namespace network
+
namespace service_manager {
class Connector;
class TestConnectorFactory;
@@ -87,6 +92,8 @@ class TestConfigurator : public Configurator {
std::string ExtraRequestParams() const override;
std::string GetDownloadPreference() const override;
scoped_refptr<net::URLRequestContextGetter> RequestContext() const override;
+ scoped_refptr<network::SharedURLLoaderFactory> URLLoaderFactory()
+ const override;
std::unique_ptr<service_manager::Connector> CreateServiceManagerConnector()
const override;
bool EnabledDeltas() const override;
@@ -97,6 +104,7 @@ class TestConfigurator : public Configurator {
ActivityDataService* GetActivityDataService() const override;
bool IsPerUserInstall() const override;
std::vector<uint8_t> GetRunActionKeyHash() const override;
+ std::string GetAppGuid() const override;
void SetBrand(const std::string& brand);
void SetOnDemandTime(int seconds);
@@ -106,6 +114,10 @@ class TestConfigurator : public Configurator {
void SetEnabledComponentUpdates(bool enabled_component_updates);
void SetUpdateCheckUrl(const GURL& url);
void SetPingUrl(const GURL& url);
+ void SetAppGuid(const std::string& app_guid);
+ network::TestURLLoaderFactory* test_url_loader_factory() {
+ return &test_url_loader_factory_;
+ }
private:
friend class base::RefCountedThreadSafe<TestConfigurator>;
@@ -121,11 +133,15 @@ class TestConfigurator : public Configurator {
bool enabled_component_updates_;
GURL update_check_url_;
GURL ping_url_;
+ std::string app_guid_;
std::unique_ptr<service_manager::TestConnectorFactory> connector_factory_;
std::unique_ptr<service_manager::Connector> connector_;
scoped_refptr<net::TestURLRequestContextGetter> context_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+
DISALLOW_COPY_AND_ASSIGN(TestConfigurator);
};
diff --git a/chromium/components/update_client/test_installer.cc b/chromium/components/update_client/test_installer.cc
index 22eef68c64e..3ed161acb03 100644
--- a/chromium/components/update_client/test_installer.cc
+++ b/chromium/components/update_client/test_installer.cc
@@ -83,7 +83,7 @@ void VersionedTestInstaller::Install(const base::FilePath& unpack_path,
const auto manifest = update_client::ReadManifest(unpack_path);
std::string version_string;
manifest->GetStringASCII("version", &version_string);
- const base::Version version(version_string.c_str());
+ const base::Version version(version_string);
const base::FilePath path =
install_directory_.AppendASCII(version.GetString());
diff --git a/chromium/components/update_client/update_checker.cc b/chromium/components/update_client/update_checker.cc
index 1fb0479865e..d4ecc9e73ee 100644
--- a/chromium/components/update_client/update_checker.cc
+++ b/chromium/components/update_client/update_checker.cc
@@ -25,7 +25,6 @@
#include "components/update_client/configurator.h"
#include "components/update_client/persisted_data.h"
#include "components/update_client/protocol_builder.h"
-#include "components/update_client/protocol_parser.h"
#include "components/update_client/request_sender.h"
#include "components/update_client/task_traits.h"
#include "components/update_client/update_client.h"
@@ -69,14 +68,12 @@ class UpdateCheckerImpl : public UpdateChecker {
const IdToComponentPtrMap& components,
const std::string& additional_attributes,
bool enabled_component_updates);
- void OnRequestSenderComplete(const IdToComponentPtrMap& components,
- int error,
+ void OnRequestSenderComplete(int error,
const std::string& response,
int retry_after_sec);
- void UpdateCheckSucceeded(const IdToComponentPtrMap& components,
- const ProtocolParser::Results& results,
+ void UpdateCheckSucceeded(const ProtocolParser::Results& results,
int retry_after_sec);
- void UpdateCheckFailed(const IdToComponentPtrMap& components,
+ void UpdateCheckFailed(ErrorCategory error_category,
int error,
int retry_after_sec);
@@ -168,11 +165,10 @@ void UpdateCheckerImpl::CheckForUpdatesHelper(
updater_state_attributes_),
config_->EnabledCupSigning(),
base::BindOnce(&UpdateCheckerImpl::OnRequestSenderComplete,
- base::Unretained(this), base::ConstRef(components)));
+ base::Unretained(this)));
}
void UpdateCheckerImpl::OnRequestSenderComplete(
- const IdToComponentPtrMap& components,
int error,
const std::string& response,
int retry_after_sec) {
@@ -180,23 +176,24 @@ void UpdateCheckerImpl::OnRequestSenderComplete(
if (error) {
VLOG(1) << "RequestSender failed " << error;
- UpdateCheckFailed(components, error, retry_after_sec);
+ UpdateCheckFailed(ErrorCategory::kUpdateCheck, error, retry_after_sec);
return;
}
ProtocolParser update_response;
if (!update_response.Parse(response)) {
VLOG(1) << "Parse failed " << update_response.errors();
- UpdateCheckFailed(components, -1, retry_after_sec);
+ UpdateCheckFailed(ErrorCategory::kUpdateCheck,
+ static_cast<int>(ProtocolError::PARSE_FAILED),
+ retry_after_sec);
return;
}
DCHECK_EQ(0, error);
- UpdateCheckSucceeded(components, update_response.results(), retry_after_sec);
+ UpdateCheckSucceeded(update_response.results(), retry_after_sec);
}
void UpdateCheckerImpl::UpdateCheckSucceeded(
- const IdToComponentPtrMap& components,
const ProtocolParser::Results& results,
int retry_after_sec) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -218,32 +215,23 @@ void UpdateCheckerImpl::UpdateCheckSucceeded(
metadata_->SetCohortHint(result.extension_id, entry->second);
}
- for (const auto& result : results.list) {
- const auto& id = result.extension_id;
- const auto it = components.find(id);
- if (it != components.end())
- it->second->SetParseResult(result);
- }
-
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
- base::BindOnce(std::move(update_check_callback_), 0, retry_after_sec));
+ base::BindOnce(std::move(update_check_callback_),
+ base::make_optional<ProtocolParser::Results>(results),
+ ErrorCategory::kNone, 0, retry_after_sec));
}
-void UpdateCheckerImpl::UpdateCheckFailed(const IdToComponentPtrMap& components,
+void UpdateCheckerImpl::UpdateCheckFailed(ErrorCategory error_category,
int error,
int retry_after_sec) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_NE(0, error);
- for (const auto& item : components) {
- DCHECK(item.second);
- Component& component = *item.second;
- component.set_update_check_error(error);
- }
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback_), error,
- retry_after_sec));
+ FROM_HERE,
+ base::BindOnce(std::move(update_check_callback_), base::nullopt,
+ error_category, error, retry_after_sec));
}
} // namespace
diff --git a/chromium/components/update_client/update_checker.h b/chromium/components/update_client/update_checker.h
index 408b3bbb65b..241e3a0a73c 100644
--- a/chromium/components/update_client/update_checker.h
+++ b/chromium/components/update_client/update_checker.h
@@ -12,7 +12,9 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/optional.h"
#include "components/update_client/component.h"
+#include "components/update_client/protocol_parser.h"
#include "url/gurl.h"
namespace update_client {
@@ -22,8 +24,11 @@ class PersistedData;
class UpdateChecker {
public:
- using UpdateCheckCallback =
- base::OnceCallback<void(int error, int retry_after_sec)>;
+ using UpdateCheckCallback = base::OnceCallback<void(
+ const base::Optional<ProtocolParser::Results>& results,
+ ErrorCategory error_category,
+ int error,
+ int retry_after_sec)>;
using Factory =
std::unique_ptr<UpdateChecker> (*)(scoped_refptr<Configurator> config,
diff --git a/chromium/components/update_client/update_checker_unittest.cc b/chromium/components/update_client/update_checker_unittest.cc
index a3249b985c3..bb8cf9ebcdf 100644
--- a/chromium/components/update_client/update_checker_unittest.cc
+++ b/chromium/components/update_client/update_checker_unittest.cc
@@ -13,9 +13,13 @@
#include "base/files/file_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/optional.h"
#include "base/path_service.h"
#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
#include "base/task_scheduler/post_task.h"
+#include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/version.h"
@@ -26,8 +30,9 @@
#include "components/update_client/persisted_data.h"
#include "components/update_client/test_configurator.h"
#include "components/update_client/update_engine.h"
-#include "components/update_client/url_request_post_interceptor.h"
+#include "components/update_client/url_loader_post_interceptor.h"
#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/resource_request.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -114,7 +119,11 @@ class UpdateCheckerTest : public testing::Test,
void SetUp() override;
void TearDown() override;
- void UpdateCheckComplete(int error, int retry_after_sec);
+ void UpdateCheckComplete(
+ const base::Optional<ProtocolParser::Results>& results,
+ ErrorCategory error_category,
+ int error,
+ int retry_after_sec);
protected:
void Quit();
@@ -129,9 +138,10 @@ class UpdateCheckerTest : public testing::Test,
std::unique_ptr<UpdateChecker> update_checker_;
- std::unique_ptr<InterceptorFactory> interceptor_factory_;
- scoped_refptr<URLRequestPostInterceptor> post_interceptor_;
+ std::unique_ptr<URLLoaderPostInterceptor> post_interceptor_;
+ base::Optional<ProtocolParser::Results> results_;
+ ErrorCategory error_category_ = ErrorCategory::kNone;
int error_ = 0;
int retry_after_sec_ = 0;
@@ -162,9 +172,9 @@ void UpdateCheckerTest::SetUp() {
PersistedData::RegisterPrefs(pref_->registry());
metadata_ = std::make_unique<PersistedData>(pref_.get(),
activity_data_service_.get());
- interceptor_factory_ =
- std::make_unique<InterceptorFactory>(base::ThreadTaskRunnerHandle::Get());
- post_interceptor_ = interceptor_factory_->CreateInterceptor();
+
+ post_interceptor_ = std::make_unique<URLLoaderPostInterceptor>(
+ config_->test_url_loader_factory());
EXPECT_TRUE(post_interceptor_);
update_checker_ = nullptr;
@@ -177,8 +187,7 @@ void UpdateCheckerTest::SetUp() {
void UpdateCheckerTest::TearDown() {
update_checker_ = nullptr;
- post_interceptor_ = nullptr;
- interceptor_factory_ = nullptr;
+ post_interceptor_.reset();
config_ = nullptr;
@@ -198,7 +207,13 @@ void UpdateCheckerTest::Quit() {
std::move(quit_closure_).Run();
}
-void UpdateCheckerTest::UpdateCheckComplete(int error, int retry_after_sec) {
+void UpdateCheckerTest::UpdateCheckComplete(
+ const base::Optional<ProtocolParser::Results>& results,
+ ErrorCategory error_category,
+ int error,
+ int retry_after_sec) {
+ results_ = results;
+ error_category_ = error_category;
error_ = error;
retry_after_sec_ = retry_after_sec;
Quit();
@@ -215,7 +230,7 @@ std::unique_ptr<Component> UpdateCheckerTest::MakeComponent() const {
std::unique_ptr<CrxComponent> crx_component =
std::make_unique<CrxComponent>();
crx_component->name = "test_jebg";
- crx_component->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
+ crx_component->pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
crx_component->installer = nullptr;
crx_component->version = base::Version("0.9");
crx_component->fingerprint = "fp1";
@@ -280,15 +295,20 @@ TEST_P(UpdateCheckerTest, UpdateCheckSuccess) {
EXPECT_THAT(request, testing::HasSubstr(" sessionid="));
// Sanity check the arguments of the callback after parsing.
+ EXPECT_EQ(ErrorCategory::kNone, error_category_);
EXPECT_EQ(0, error_);
-
- EXPECT_EQ(base::Version("1.0"), component->next_version_);
- EXPECT_EQ(1u, component->crx_urls_.size());
- EXPECT_EQ(
- GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx"),
- component->crx_urls_.front());
-
- EXPECT_STREQ("this", component->action_run_.c_str());
+ EXPECT_TRUE(results_);
+ EXPECT_EQ(1u, results_->list.size());
+ const auto& result = results_->list.front();
+ EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", result.extension_id.c_str());
+ EXPECT_EQ("1.0", result.manifest.version);
+ EXPECT_EQ("11.0.1.0", result.manifest.browser_min_version);
+ EXPECT_EQ(1u, result.manifest.packages.size());
+ EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf.crx",
+ result.manifest.packages.front().name.c_str());
+ EXPECT_EQ(1u, result.crx_urls.size());
+ EXPECT_EQ(GURL("http://localhost/download/"), result.crx_urls.front());
+ EXPECT_STREQ("this", result.action_run.c_str());
#if (OS_WIN)
EXPECT_THAT(request, testing::HasSubstr(" domainjoined="));
@@ -300,7 +320,8 @@ TEST_P(UpdateCheckerTest, UpdateCheckSuccess) {
#endif // OS_WINDOWS
// Check the DDOS protection header values.
- const auto extra_request_headers = post_interceptor_->GetRequests()[0].second;
+ const auto extra_request_headers =
+ std::get<1>(post_interceptor_->GetRequests()[0]);
EXPECT_TRUE(extra_request_headers.HasHeader("X-Goog-Update-Interactivity"));
std::string header;
extra_request_headers.GetHeader("X-Goog-Update-Interactivity", &header);
@@ -375,15 +396,13 @@ TEST_F(UpdateCheckerTest, UpdateCheckSuccessNoBrand) {
// Simulates a 403 server response error.
TEST_F(UpdateCheckerTest, UpdateCheckError) {
EXPECT_TRUE(post_interceptor_->ExpectRequest(
- std::make_unique<PartialMatch>("updatecheck"), 403));
+ std::make_unique<PartialMatch>("updatecheck"), net::HTTP_FORBIDDEN));
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
IdToComponentPtrMap components;
components[kUpdateItemId] = MakeComponent();
- auto& component = components[kUpdateItemId];
-
update_checker_->CheckForUpdates(
update_context_->session_id, {kUpdateItemId}, components, "", true,
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
@@ -395,8 +414,9 @@ TEST_F(UpdateCheckerTest, UpdateCheckError) {
EXPECT_EQ(1, post_interceptor_->GetCount())
<< post_interceptor_->GetRequestsAsString();
+ EXPECT_EQ(ErrorCategory::kUpdateCheck, error_category_);
EXPECT_EQ(403, error_);
- EXPECT_FALSE(component->next_version_.IsValid());
+ EXPECT_FALSE(results_);
}
TEST_F(UpdateCheckerTest, UpdateCheckDownloadPreference) {
@@ -416,7 +436,6 @@ TEST_F(UpdateCheckerTest, UpdateCheckDownloadPreference) {
"extra=\"params\"", true,
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
base::Unretained(this)));
-
RunThreads();
// The request must contain dlpref="cacheable".
@@ -438,8 +457,6 @@ TEST_F(UpdateCheckerTest, UpdateCheckCupError) {
IdToComponentPtrMap components;
components[kUpdateItemId] = MakeComponent();
- const auto& component = components[kUpdateItemId];
-
update_checker_->CheckForUpdates(
update_context_->session_id, {kUpdateItemId}, components, "", true,
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
@@ -463,8 +480,9 @@ TEST_F(UpdateCheckerTest, UpdateCheckCupError) {
testing::HasSubstr(R"(<packages><package fp="fp1"/></packages></app>)"));
// Expect an error since the response is not trusted.
+ EXPECT_EQ(ErrorCategory::kUpdateCheck, error_category_);
EXPECT_EQ(-10000, error_);
- EXPECT_FALSE(component->next_version_.IsValid());
+ EXPECT_FALSE(results_);
}
// Tests that the UpdateCheckers will not make an update check for a
@@ -486,7 +504,8 @@ TEST_F(UpdateCheckerTest, UpdateCheckRequiresEncryptionError) {
base::Unretained(this)));
RunThreads();
- EXPECT_EQ(-1, error_);
+ EXPECT_EQ(ErrorCategory::kUpdateCheck, error_category_);
+ EXPECT_EQ(-10002, error_);
EXPECT_FALSE(component->next_version_.IsValid());
}
@@ -867,8 +886,6 @@ TEST_F(UpdateCheckerTest, NoUpdateActionRun) {
IdToComponentPtrMap components;
components[kUpdateItemId] = MakeComponent();
- auto& component = components[kUpdateItemId];
-
update_checker_->CheckForUpdates(
update_context_->session_id, {kUpdateItemId}, components, "", true,
base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
@@ -880,8 +897,14 @@ TEST_F(UpdateCheckerTest, NoUpdateActionRun) {
ASSERT_EQ(1, post_interceptor_->GetCount())
<< post_interceptor_->GetRequestsAsString();
+ // Sanity check the arguments of the callback after parsing.
+ EXPECT_EQ(ErrorCategory::kNone, error_category_);
EXPECT_EQ(0, error_);
- EXPECT_STREQ("this", component->action_run_.c_str());
+ EXPECT_TRUE(results_);
+ EXPECT_EQ(1u, results_->list.size());
+ const auto& result = results_->list.front();
+ EXPECT_STREQ("jebgalgnebhfojomionfpkfelancnnkf", result.extension_id.c_str());
+ EXPECT_STREQ("this", result.action_run.c_str());
}
TEST_F(UpdateCheckerTest, UpdatePauseResume) {
@@ -889,10 +912,10 @@ TEST_F(UpdateCheckerTest, UpdatePauseResume) {
std::make_unique<PartialMatch>("updatecheck"),
test_file("updatecheck_reply_1.xml")));
post_interceptor_->url_job_request_ready_callback(base::BindOnce(
- [](scoped_refptr<URLRequestPostInterceptor> post_interceptor) {
+ [](URLLoaderPostInterceptor* post_interceptor) {
post_interceptor->Resume();
},
- post_interceptor_));
+ post_interceptor_.get()));
post_interceptor_->Pause();
update_checker_ = UpdateChecker::Create(config_, metadata_.get());
@@ -941,4 +964,64 @@ TEST_F(UpdateCheckerTest, UpdateResetUpdateChecker) {
runloop.Run();
}
+// The update response contains a protocol version which does not match the
+// expected protocol version.
+TEST_F(UpdateCheckerTest, ParseErrorProtocolVersionMismatch) {
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(
+ std::make_unique<PartialMatch>("updatecheck"),
+ test_file("updatecheck_reply_parse_error.xml")));
+
+ update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+ IdToComponentPtrMap components;
+ components[kUpdateItemId] = MakeComponent();
+
+ update_checker_->CheckForUpdates(
+ update_context_->session_id, {kUpdateItemId}, components, "", true,
+ base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+ base::Unretained(this)));
+ RunThreads();
+
+ EXPECT_EQ(1, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ ASSERT_EQ(1, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+
+ EXPECT_EQ(ErrorCategory::kUpdateCheck, error_category_);
+ EXPECT_EQ(-10003, error_);
+ EXPECT_FALSE(results_);
+}
+
+// The update response contains a status |error-unknownApplication| for the
+// app. The response is succesfully parsed and a result is extracted to
+// indicate this status.
+TEST_F(UpdateCheckerTest, ParseErrorAppStatusErrorUnknownApplication) {
+ EXPECT_TRUE(post_interceptor_->ExpectRequest(
+ std::make_unique<PartialMatch>("updatecheck"),
+ test_file("updatecheck_reply_unknownapp.xml")));
+
+ update_checker_ = UpdateChecker::Create(config_, metadata_.get());
+
+ IdToComponentPtrMap components;
+ components[kUpdateItemId] = MakeComponent();
+
+ update_checker_->CheckForUpdates(
+ update_context_->session_id, {kUpdateItemId}, components, "", true,
+ base::BindOnce(&UpdateCheckerTest::UpdateCheckComplete,
+ base::Unretained(this)));
+ RunThreads();
+
+ EXPECT_EQ(1, post_interceptor_->GetHitCount())
+ << post_interceptor_->GetRequestsAsString();
+ ASSERT_EQ(1, post_interceptor_->GetCount())
+ << post_interceptor_->GetRequestsAsString();
+
+ EXPECT_EQ(ErrorCategory::kNone, error_category_);
+ EXPECT_EQ(0, error_);
+ EXPECT_TRUE(results_);
+ EXPECT_EQ(1u, results_->list.size());
+ const auto& result = results_->list.front();
+ EXPECT_STREQ("error-unknownApplication", result.status.c_str());
+}
+
} // namespace update_client
diff --git a/chromium/components/update_client/update_client.cc b/chromium/components/update_client/update_client.cc
index 4d3c9b4c470..561bbcf9902 100644
--- a/chromium/components/update_client/update_client.cc
+++ b/chromium/components/update_client/update_client.cc
@@ -16,6 +16,7 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/observer_list.h"
+#include "base/stl_util.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/prefs/pref_registry_simple.h"
@@ -183,14 +184,14 @@ bool UpdateClientImpl::IsUpdating(const std::string& id) const {
for (const auto task : tasks_) {
const auto ids = task->GetIds();
- if (std::find(ids.begin(), ids.end(), id) != ids.end()) {
+ if (base::ContainsValue(ids, id)) {
return true;
}
}
for (const auto task : task_queue_) {
const auto ids = task->GetIds();
- if (std::find(ids.begin(), ids.end(), id) != ids.end()) {
+ if (base::ContainsValue(ids, id)) {
return true;
}
}
diff --git a/chromium/components/update_client/update_client_errors.h b/chromium/components/update_client/update_client_errors.h
index 129d7aa9763..3ee05098ecb 100644
--- a/chromium/components/update_client/update_client_errors.h
+++ b/chromium/components/update_client/update_client_errors.h
@@ -10,7 +10,6 @@ namespace update_client {
// Errors generated as a result of calling UpdateClient member functions.
// These errors are not sent in pings.
enum class Error {
- INVALID_ARGUMENT = -1,
NONE = 0,
UPDATE_IN_PROGRESS = 1,
UPDATE_CANCELED = 2,
@@ -18,6 +17,8 @@ enum class Error {
SERVICE_ERROR = 4,
UPDATE_CHECK_ERROR = 5,
CRX_NOT_FOUND = 6,
+ INVALID_ARGUMENT = 7,
+ MAX_VALUE,
};
// These errors are sent in pings. Add new values only to the bottom of
@@ -85,7 +86,7 @@ enum class InstallError {
CUSTOM_ERROR_BASE = 100, // Specific installer errors go above this value.
};
-// These errors are returned with the |kInstall| error category and
+// These errors are returned with the |kService| error category and
// indicate critical or configuration errors in the update service.
enum class ServiceError {
NONE = 0,
@@ -93,6 +94,25 @@ enum class ServiceError {
UPDATE_DISABLED = 2,
};
+// These errors are related to serialization, deserialization, and parsing of
+// protocol requests.
+// The begin value for this enum is chosen not to conflict with network errors
+// defined by net/base/net_error_list.h. The callers don't have to handle this
+// error in any meaningful way, but this value may be reported in UMA stats,
+// therefore avoiding collisions with known network errors is desirable.
+enum class ProtocolError : int {
+ NONE = 0,
+ RESPONSE_NOT_TRUSTED = -10000,
+ MISSING_PUBLIC_KEY = -10001,
+ MISSING_URLS = -10002,
+ PARSE_FAILED = -10003,
+ UPDATE_RESPONSE_NOT_FOUND = -10004,
+ URL_FETCHER_FAILED = -10005,
+ UNKNOWN_APPLICATION = -10006,
+ RESTRICTED_APPLICATION = -10007,
+ INVALID_APPID = -10008,
+};
+
} // namespace update_client
#endif // COMPONENTS_UPDATE_CLIENT_UPDATE_CLIENT_ERRORS_H_
diff --git a/chromium/components/update_client/update_client_unittest.cc b/chromium/components/update_client/update_client_unittest.cc
index 581cbf9e9b9..4e9900571dd 100644
--- a/chromium/components/update_client/update_client_unittest.cc
+++ b/chromium/components/update_client/update_client_unittest.cc
@@ -11,6 +11,7 @@
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/optional.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
@@ -263,10 +264,13 @@ TEST_F(UpdateClientTest, OneCrxNoUpdate) {
ProtocolParser::Result result;
result.extension_id = id;
result.status = "noupdate";
- component->SetParseResult(result);
+
+ ProtocolParser::Results results;
+ results.list.push_back(result);
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback), 0, 0));
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, 0));
}
};
@@ -384,12 +388,16 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) {
</manifest>
</updatecheck>
</app>
+ <app appid='abagagagagagagagagagagagagagagag'>
+ <updatecheck status='noupdate'/>
+ </app>
</response>
*/
EXPECT_FALSE(session_id.empty());
EXPECT_TRUE(enabled_component_updates);
EXPECT_EQ(2u, ids_to_check.size());
+ ProtocolParser::Results results;
{
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
EXPECT_EQ(id, ids_to_check[0]);
@@ -407,11 +415,9 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) {
result.manifest.version = "1.0";
result.manifest.browser_min_version = "11.0.1.0";
result.manifest.packages.push_back(package);
+ results.list.push_back(result);
- auto& component = components.at(id);
- component->SetParseResult(result);
-
- EXPECT_FALSE(component->is_foreground());
+ EXPECT_FALSE(components.at(id)->is_foreground());
}
{
@@ -422,15 +428,14 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) {
ProtocolParser::Result result;
result.extension_id = id;
result.status = "noupdate";
+ results.list.push_back(result);
- auto& component = components.at(id);
- component->SetParseResult(result);
-
- EXPECT_FALSE(component->is_foreground());
+ EXPECT_FALSE(components.at(id)->is_foreground());
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback), 0, 0));
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, 0));
}
};
@@ -533,6 +538,223 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) {
update_client->RemoveObserver(&observer);
}
+// Tests the scenario where two CRXs are checked for updates. One CRX has
+// an update but the server ignores the second CRX and returns no response for
+// it. The second component gets an |UPDATE_RESPONSE_NOT_FOUND| error and
+// transitions to the error state.
+TEST_F(UpdateClientTest, TwoCrxUpdateFirstServerIgnoresSecond) {
+ class DataCallbackMock {
+ public:
+ static std::vector<std::unique_ptr<CrxComponent>> Callback(
+ const std::vector<std::string>& ids) {
+ std::unique_ptr<CrxComponent> crx1 = std::make_unique<CrxComponent>();
+ crx1->name = "test_jebg";
+ crx1->pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+ crx1->version = base::Version("0.9");
+ crx1->installer = base::MakeRefCounted<TestInstaller>();
+
+ std::unique_ptr<CrxComponent> crx2 = std::make_unique<CrxComponent>();
+ crx2->name = "test_abag";
+ crx2->pk_hash.assign(abag_hash, abag_hash + base::size(abag_hash));
+ crx2->version = base::Version("2.2");
+ crx2->installer = base::MakeRefCounted<TestInstaller>();
+
+ std::vector<std::unique_ptr<CrxComponent>> component;
+ component.push_back(std::move(crx1));
+ component.push_back(std::move(crx2));
+ return component;
+ }
+ };
+
+ class CompletionCallbackMock {
+ public:
+ static void Callback(base::OnceClosure quit_closure, Error error) {
+ EXPECT_EQ(Error::NONE, error);
+ std::move(quit_closure).Run();
+ }
+ };
+
+ class MockUpdateChecker : public UpdateChecker {
+ public:
+ static std::unique_ptr<UpdateChecker> Create(
+ scoped_refptr<Configurator> config,
+ PersistedData* metadata) {
+ return std::make_unique<MockUpdateChecker>();
+ }
+
+ void CheckForUpdates(const std::string& session_id,
+ const std::vector<std::string>& ids_to_check,
+ const IdToComponentPtrMap& components,
+ const std::string& additional_attributes,
+ bool enabled_component_updates,
+ UpdateCheckCallback update_check_callback) override {
+ /*
+ Mock the following response:
+
+ <?xml version='1.0' encoding='UTF-8'?>
+ <response protocol='3.1'>
+ <app appid='jebgalgnebhfojomionfpkfelancnnkf'>
+ <updatecheck status='ok'>
+ <urls>
+ <url codebase='http://localhost/download/'/>
+ </urls>
+ <manifest version='1.0' prodversionmin='11.0.1.0'>
+ <packages>
+ <package name='jebgalgnebhfojomionfpkfelancnnkf.crx'
+ hash_sha256='6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd
+ 7c9b12cb7cc067667bde87'/>
+ </packages>
+ </manifest>
+ </updatecheck>
+ </app>
+ </response>
+ */
+ EXPECT_FALSE(session_id.empty());
+ EXPECT_TRUE(enabled_component_updates);
+ EXPECT_EQ(2u, ids_to_check.size());
+
+ ProtocolParser::Results results;
+ {
+ const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
+ EXPECT_EQ(id, ids_to_check[0]);
+ EXPECT_EQ(1u, components.count(id));
+
+ ProtocolParser::Result::Manifest::Package package;
+ package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx";
+ package.hash_sha256 =
+ "6fc4b93fd11134de1300c2c0bb88c12b644a4ec0fd7c9b12cb7cc067667bde87";
+
+ ProtocolParser::Result result;
+ result.extension_id = "jebgalgnebhfojomionfpkfelancnnkf";
+ result.status = "ok";
+ result.crx_urls.push_back(GURL("http://localhost/download/"));
+ result.manifest.version = "1.0";
+ result.manifest.browser_min_version = "11.0.1.0";
+ result.manifest.packages.push_back(package);
+ results.list.push_back(result);
+
+ EXPECT_FALSE(components.at(id)->is_foreground());
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, 0));
+ }
+ };
+
+ class MockCrxDownloader : public CrxDownloader {
+ public:
+ static std::unique_ptr<CrxDownloader> Create(
+ bool is_background_download,
+ scoped_refptr<net::URLRequestContextGetter> context_getter) {
+ return std::make_unique<MockCrxDownloader>();
+ }
+
+ MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+ private:
+ void DoStartDownload(const GURL& url) override {
+ DownloadMetrics download_metrics;
+ download_metrics.url = url;
+ download_metrics.downloader = DownloadMetrics::kNone;
+ download_metrics.error = 0;
+ download_metrics.downloaded_bytes = 1843;
+ download_metrics.total_bytes = 1843;
+ download_metrics.download_time_ms = 1000;
+
+ FilePath path;
+ EXPECT_TRUE(MakeTestFile(
+ TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path));
+
+ Result result;
+ result.error = 0;
+ result.response = path;
+ result.downloaded_bytes = 1843;
+ result.total_bytes = 1843;
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress,
+ base::Unretained(this), result));
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete,
+ base::Unretained(this), true, result,
+ download_metrics));
+ }
+ };
+
+ class MockPingManager : public MockPingManagerImpl {
+ public:
+ explicit MockPingManager(scoped_refptr<Configurator> config)
+ : MockPingManagerImpl(config) {}
+
+ protected:
+ ~MockPingManager() override {
+ const auto ping_data = MockPingManagerImpl::ping_data();
+ EXPECT_EQ(1u, ping_data.size());
+ EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id);
+ EXPECT_EQ(base::Version("0.9"), ping_data[0].previous_version);
+ EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version);
+ EXPECT_EQ(0, static_cast<int>(ping_data[0].error_category));
+ EXPECT_EQ(0, ping_data[0].error_code);
+ }
+ };
+
+ scoped_refptr<UpdateClient> update_client =
+ base::MakeRefCounted<UpdateClientImpl>(
+ config(), base::MakeRefCounted<MockPingManager>(config()),
+ &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+ MockObserver observer;
+ {
+ InSequence seq;
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "jebgalgnebhfojomionfpkfelancnnkf"))
+ .Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND,
+ "jebgalgnebhfojomionfpkfelancnnkf"))
+ .Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING,
+ "jebgalgnebhfojomionfpkfelancnnkf"))
+ .Times(AtLeast(1));
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY,
+ "jebgalgnebhfojomionfpkfelancnnkf"))
+ .Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED,
+ "jebgalgnebhfojomionfpkfelancnnkf"))
+ .Times(1);
+ }
+ {
+ InSequence seq;
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "abagagagagagagagagagagagagagagag"))
+ .Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+ "abagagagagagagagagagagagagagagag"))
+ .Times(1)
+ .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+ CrxUpdateItem item;
+ update_client->GetCrxUpdateState(id, &item);
+ EXPECT_EQ(ComponentState::kUpdateError, item.state);
+ EXPECT_EQ(5, static_cast<int>(item.error_category));
+ EXPECT_EQ(-10004, item.error_code);
+ EXPECT_EQ(0, item.extra_code1);
+ }));
+ }
+
+ update_client->AddObserver(&observer);
+
+ const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf",
+ "abagagagagagagagagagagagagagagag"};
+ update_client->Update(
+ ids, base::BindOnce(&DataCallbackMock::Callback), false,
+ base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+ RunThreads();
+
+ update_client->RemoveObserver(&observer);
+}
+
// Tests the update check for two CRXs scenario when the second CRX does not
// provide a CrxComponent instance. In this case, the update is handled as
// if only one component were provided as an argument to the |Update| call
@@ -603,6 +825,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoCrxComponentData) {
EXPECT_TRUE(enabled_component_updates);
EXPECT_EQ(1u, ids_to_check.size());
+ ProtocolParser::Results results;
{
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
EXPECT_EQ(id, ids_to_check[0]);
@@ -620,15 +843,14 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoCrxComponentData) {
result.manifest.version = "1.0";
result.manifest.browser_min_version = "11.0.1.0";
result.manifest.packages.push_back(package);
+ results.list.push_back(result);
- auto& component = components.at(id);
- component->SetParseResult(result);
-
- EXPECT_FALSE(component->is_foreground());
+ EXPECT_FALSE(components.at(id)->is_foreground());
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback), 0, 0));
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, 0));
}
};
@@ -918,6 +1140,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdateDownloadTimeout) {
EXPECT_TRUE(enabled_component_updates);
EXPECT_EQ(2u, ids_to_check.size());
+ ProtocolParser::Results results;
{
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
EXPECT_EQ(id, ids_to_check[0]);
@@ -935,9 +1158,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdateDownloadTimeout) {
result.manifest.version = "1.0";
result.manifest.browser_min_version = "11.0.1.0";
result.manifest.packages.push_back(package);
-
- auto& component = components.at(id);
- component->SetParseResult(result);
+ results.list.push_back(result);
}
{
@@ -957,13 +1178,12 @@ TEST_F(UpdateClientTest, TwoCrxUpdateDownloadTimeout) {
result.manifest.version = "1.0";
result.manifest.browser_min_version = "11.0.1.0";
result.manifest.packages.push_back(package);
-
- auto& component = components.at(id);
- component->SetParseResult(result);
+ results.list.push_back(result);
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback), 0, 0));
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, 0));
}
};
@@ -1203,9 +1423,7 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdate) {
result.manifest.version = "1.0";
result.manifest.browser_min_version = "11.0.1.0";
result.manifest.packages.push_back(package);
-
- auto& component = components.at(id);
- component->SetParseResult(result);
+ results.list.push_back(result);
} else if (num_call == 2) {
/*
Mock the following response:
@@ -1253,15 +1471,14 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdate) {
result.manifest.version = "2.0";
result.manifest.browser_min_version = "11.0.1.0";
result.manifest.packages.push_back(package);
-
- auto& component = components.at(id);
- component->SetParseResult(result);
+ results.list.push_back(result);
} else {
NOTREACHED();
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback), 0, 0));
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, 0));
}
};
@@ -1535,11 +1752,11 @@ TEST_F(UpdateClientTest, OneCrxInstallError) {
result.manifest.browser_min_version = "11.0.1.0";
result.manifest.packages.push_back(package);
- auto& component = components.at(id);
- component->SetParseResult(result);
-
+ ProtocolParser::Results results;
+ results.list.push_back(result);
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback), 0, 0));
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, 0));
}
};
@@ -1735,9 +1952,7 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) {
result.manifest.version = "1.0";
result.manifest.browser_min_version = "11.0.1.0";
result.manifest.packages.push_back(package);
-
- auto& component = components.at(id);
- component->SetParseResult(result);
+ results.list.push_back(result);
} else if (num_call == 2) {
/*
Mock the following response:
@@ -1785,15 +2000,14 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) {
result.manifest.version = "2.0";
result.manifest.browser_min_version = "11.0.1.0";
result.manifest.packages.push_back(package);
-
- auto& component = components.at(id);
- component->SetParseResult(result);
+ results.list.push_back(result);
} else {
NOTREACHED();
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback), 0, 0));
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, 0));
}
};
@@ -2013,10 +2227,12 @@ TEST_F(UpdateClientTest, OneCrxNoUpdateQueuedCall) {
ProtocolParser::Result result;
result.extension_id = id;
result.status = "noupdate";
- component->SetParseResult(result);
+ ProtocolParser::Results results;
+ results.list.push_back(result);
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback), 0, 0));
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, 0));
}
};
@@ -2156,14 +2372,15 @@ TEST_F(UpdateClientTest, OneCrxInstall) {
result.manifest.browser_min_version = "11.0.1.0";
result.manifest.packages.push_back(package);
- auto& component = components.at(id);
- component->SetParseResult(result);
+ ProtocolParser::Results results;
+ results.list.push_back(result);
// Verify that calling Install sets ondemand.
- EXPECT_TRUE(component->is_foreground());
+ EXPECT_TRUE(components.at(id)->is_foreground());
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback), 0, 0));
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, 0));
}
};
@@ -2408,14 +2625,15 @@ TEST_F(UpdateClientTest, ConcurrentInstallSameCRX) {
result.extension_id = id;
result.status = "noupdate";
- auto& component = components.at(id);
- component->SetParseResult(result);
+ ProtocolParser::Results results;
+ results.list.push_back(result);
// Verify that calling Install sets |is_foreground| for the component.
- EXPECT_TRUE(component->is_foreground());
+ EXPECT_TRUE(components.at(id)->is_foreground());
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback), 0, 0));
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, 0));
}
};
@@ -2689,16 +2907,16 @@ TEST_F(UpdateClientTest, RetryAfter) {
EXPECT_EQ(id, ids_to_check.front());
EXPECT_EQ(1u, components.count(id));
- auto& component = components.at(id);
-
ProtocolParser::Result result;
result.extension_id = id;
result.status = "noupdate";
- component->SetParseResult(result);
+
+ ProtocolParser::Results results;
+ results.list.push_back(result);
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(update_check_callback), 0, retry_after_sec));
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, retry_after_sec));
}
};
@@ -2805,7 +3023,7 @@ TEST_F(UpdateClientTest, RetryAfter) {
// the group policy to enable updates, and has its updates disabled. The second
// component has an update. The server does not honor the "updatedisabled"
// attribute and returns updates for both components. However, the update for
-// the first component is not apply and the client responds with a
+// the first component is not applied and the client responds with a
// (SERVICE_ERROR, UPDATE_DISABLED)
TEST_F(UpdateClientTest, TwoCrxUpdateOneUpdateDisabled) {
class DataCallbackMock {
@@ -2898,6 +3116,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdateOneUpdateDisabled) {
EXPECT_FALSE(enabled_component_updates);
EXPECT_EQ(2u, ids_to_check.size());
+ ProtocolParser::Results results;
{
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
EXPECT_EQ(id, ids_to_check[0]);
@@ -2915,9 +3134,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdateOneUpdateDisabled) {
result.manifest.version = "1.0";
result.manifest.browser_min_version = "11.0.1.0";
result.manifest.packages.push_back(package);
-
- auto& component = components.at(id);
- component->SetParseResult(result);
+ results.list.push_back(result);
}
{
@@ -2937,13 +3154,12 @@ TEST_F(UpdateClientTest, TwoCrxUpdateOneUpdateDisabled) {
result.manifest.version = "1.0";
result.manifest.browser_min_version = "11.0.1.0";
result.manifest.packages.push_back(package);
-
- auto& component = components.at(id);
- component->SetParseResult(result);
+ results.list.push_back(result);
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback), 0, 0));
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, 0));
}
};
@@ -3111,11 +3327,10 @@ TEST_F(UpdateClientTest, OneCrxUpdateCheckFails) {
const std::string id = "jebgalgnebhfojomionfpkfelancnnkf";
EXPECT_EQ(id, ids_to_check.front());
EXPECT_EQ(1u, components.count(id));
- constexpr int update_check_error = -1;
- components.at(id)->set_update_check_error(update_check_error);
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback),
- update_check_error, 0));
+ FROM_HERE,
+ base::BindOnce(std::move(update_check_callback), base::nullopt,
+ ErrorCategory::kUpdateCheck, -1, 0));
}
};
@@ -3176,6 +3391,208 @@ TEST_F(UpdateClientTest, OneCrxUpdateCheckFails) {
update_client->RemoveObserver(&observer);
}
+// Tests the scenario where the server responds with different values for
+// application status.
+TEST_F(UpdateClientTest, OneCrxErrorUnknownApp) {
+ class DataCallbackMock {
+ public:
+ static std::vector<std::unique_ptr<CrxComponent>> Callback(
+ const std::vector<std::string>& ids) {
+ std::vector<std::unique_ptr<CrxComponent>> component;
+
+ std::unique_ptr<CrxComponent> crx = std::make_unique<CrxComponent>();
+ crx->name = "test_jebg";
+ crx->pk_hash.assign(jebg_hash, jebg_hash + base::size(jebg_hash));
+ crx->version = base::Version("0.9");
+ crx->installer = base::MakeRefCounted<TestInstaller>();
+ component.push_back(std::move(crx));
+
+ crx = std::make_unique<CrxComponent>();
+ crx->name = "test_abag";
+ crx->pk_hash.assign(abag_hash, abag_hash + base::size(abag_hash));
+ crx->version = base::Version("0.1");
+ crx->installer = base::MakeRefCounted<TestInstaller>();
+ component.push_back(std::move(crx));
+
+ crx = std::make_unique<CrxComponent>();
+ crx->name = "test_ihfo";
+ crx->pk_hash.assign(ihfo_hash, ihfo_hash + base::size(ihfo_hash));
+ crx->version = base::Version("0.2");
+ crx->installer = base::MakeRefCounted<TestInstaller>();
+ component.push_back(std::move(crx));
+
+ crx = std::make_unique<CrxComponent>();
+ crx->name = "test_gjpm";
+ crx->pk_hash.assign(gjpm_hash, gjpm_hash + base::size(gjpm_hash));
+ crx->version = base::Version("0.3");
+ crx->installer = base::MakeRefCounted<TestInstaller>();
+ component.push_back(std::move(crx));
+
+ return component;
+ }
+ };
+
+ class CompletionCallbackMock {
+ public:
+ static void Callback(base::OnceClosure quit_closure, Error error) {
+ EXPECT_EQ(Error::NONE, error);
+ std::move(quit_closure).Run();
+ }
+ };
+
+ class MockUpdateChecker : public UpdateChecker {
+ public:
+ static std::unique_ptr<UpdateChecker> Create(
+ scoped_refptr<Configurator> config,
+ PersistedData* metadata) {
+ return std::make_unique<MockUpdateChecker>();
+ }
+
+ void CheckForUpdates(const std::string& session_id,
+ const std::vector<std::string>& ids_to_check,
+ const IdToComponentPtrMap& components,
+ const std::string& additional_attributes,
+ bool enabled_component_updates,
+ UpdateCheckCallback update_check_callback) override {
+ EXPECT_FALSE(session_id.empty());
+ EXPECT_TRUE(enabled_component_updates);
+ EXPECT_EQ(4u, ids_to_check.size());
+
+ const std::string update_response =
+ R"(<?xml version="1.0" encoding="UTF-8"?>)"
+ R"(<response protocol="3.1">)"
+ R"(<app appid="jebgalgnebhfojomionfpkfelancnnkf")"
+ R"( status="error-unknownApplication"/>)"
+ R"(<app appid="abagagagagagagagagagagagagagagag")"
+ R"( status="restricted"/>)"
+ R"(<app appid="ihfokbkgjpifnbbojhneepfflplebdkc")"
+ R"( status="error-invalidAppId"/>)"
+ R"(<app appid="gjpmebpgbhcamgdgjcmnjfhggjpgcimm")"
+ R"( status="error-foobarApp"/>)"
+ R"(</response>)";
+
+ ProtocolParser parser;
+ EXPECT_TRUE(parser.Parse(update_response));
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(std::move(update_check_callback), parser.results(),
+ ErrorCategory::kNone, 0, 0));
+ }
+ };
+
+ class MockCrxDownloader : public CrxDownloader {
+ public:
+ static std::unique_ptr<CrxDownloader> Create(
+ bool is_background_download,
+ scoped_refptr<net::URLRequestContextGetter> context_getter) {
+ return std::make_unique<MockCrxDownloader>();
+ }
+
+ MockCrxDownloader() : CrxDownloader(nullptr) {}
+
+ private:
+ void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
+ };
+
+ class MockPingManager : public MockPingManagerImpl {
+ public:
+ explicit MockPingManager(scoped_refptr<Configurator> config)
+ : MockPingManagerImpl(config) {}
+
+ protected:
+ ~MockPingManager() override { EXPECT_TRUE(ping_data().empty()); }
+ };
+
+ scoped_refptr<UpdateClient> update_client =
+ base::MakeRefCounted<UpdateClientImpl>(
+ config(), base::MakeRefCounted<MockPingManager>(config()),
+ &MockUpdateChecker::Create, &MockCrxDownloader::Create);
+
+ MockObserver observer;
+ {
+ InSequence seq;
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "jebgalgnebhfojomionfpkfelancnnkf"))
+ .Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+ "jebgalgnebhfojomionfpkfelancnnkf"))
+ .Times(1)
+ .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+ CrxUpdateItem item;
+ update_client->GetCrxUpdateState(id, &item);
+ EXPECT_EQ(ComponentState::kUpdateError, item.state);
+ EXPECT_EQ(5, static_cast<int>(item.error_category));
+ EXPECT_EQ(-10006, item.error_code); // UNKNOWN_APPPLICATION.
+ EXPECT_EQ(0, item.extra_code1);
+ }));
+ }
+ {
+ InSequence seq;
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "abagagagagagagagagagagagagagagag"))
+ .Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+ "abagagagagagagagagagagagagagagag"))
+ .Times(1)
+ .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+ CrxUpdateItem item;
+ update_client->GetCrxUpdateState(id, &item);
+ EXPECT_EQ(ComponentState::kUpdateError, item.state);
+ EXPECT_EQ(5, static_cast<int>(item.error_category));
+ EXPECT_EQ(-10007, item.error_code); // RESTRICTED_APPLICATION.
+ EXPECT_EQ(0, item.extra_code1);
+ }));
+ }
+ {
+ InSequence seq;
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "ihfokbkgjpifnbbojhneepfflplebdkc"))
+ .Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+ "ihfokbkgjpifnbbojhneepfflplebdkc"))
+ .Times(1)
+ .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+ CrxUpdateItem item;
+ update_client->GetCrxUpdateState(id, &item);
+ EXPECT_EQ(ComponentState::kUpdateError, item.state);
+ EXPECT_EQ(5, static_cast<int>(item.error_category));
+ EXPECT_EQ(-10008, item.error_code); // INVALID_APPID.
+ EXPECT_EQ(0, item.extra_code1);
+ }));
+ }
+ {
+ InSequence seq;
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+ "gjpmebpgbhcamgdgjcmnjfhggjpgcimm"))
+ .Times(1);
+ EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_ERROR,
+ "gjpmebpgbhcamgdgjcmnjfhggjpgcimm"))
+ .Times(1)
+ .WillOnce(Invoke([&update_client](Events event, const std::string& id) {
+ CrxUpdateItem item;
+ update_client->GetCrxUpdateState(id, &item);
+ EXPECT_EQ(ComponentState::kUpdateError, item.state);
+ EXPECT_EQ(5, static_cast<int>(item.error_category));
+ EXPECT_EQ(-10004, item.error_code); // UPDATE_RESPONSE_NOT_FOUND.
+ EXPECT_EQ(0, item.extra_code1);
+ }));
+ }
+
+ update_client->AddObserver(&observer);
+
+ const std::vector<std::string> ids = {
+ "jebgalgnebhfojomionfpkfelancnnkf", "abagagagagagagagagagagagagagagag",
+ "ihfokbkgjpifnbbojhneepfflplebdkc", "gjpmebpgbhcamgdgjcmnjfhggjpgcimm"};
+ update_client->Update(
+ ids, base::BindOnce(&DataCallbackMock::Callback), true,
+ base::BindOnce(&CompletionCallbackMock::Callback, quit_closure()));
+
+ RunThreads();
+
+ update_client->RemoveObserver(&observer);
+}
+
#if defined(OS_WIN) // ActionRun is only implemented on Windows.
// Tests that a run action in invoked in the CRX install scenario.
@@ -3239,11 +3656,12 @@ TEST_F(UpdateClientTest, ActionRun_Install) {
result.manifest.packages.push_back(package);
result.action_run = "ChromeRecovery.crx3";
- auto& component = components.at(id);
- component->SetParseResult(result);
+ ProtocolParser::Results results;
+ results.list.push_back(result);
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback), 0, 0));
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, 0));
}
};
@@ -3380,17 +3798,17 @@ TEST_F(UpdateClientTest, ActionRun_NoUpdate) {
EXPECT_EQ(id, ids_to_check[0]);
EXPECT_EQ(1u, components.count(id));
- auto& component = components.at(id);
-
ProtocolParser::Result result;
result.extension_id = id;
result.status = "noupdate";
result.action_run = "ChromeRecovery.crx3";
- component->SetParseResult(result);
+ ProtocolParser::Results results;
+ results.list.push_back(result);
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(update_check_callback), 0, 0));
+ FROM_HERE, base::BindOnce(std::move(update_check_callback), results,
+ ErrorCategory::kNone, 0, 0));
}
};
diff --git a/chromium/components/update_client/update_engine.cc b/chromium/components/update_client/update_engine.cc
index 351cb3893c2..a778b7d9628 100644
--- a/chromium/components/update_client/update_engine.cc
+++ b/chromium/components/update_client/update_engine.cc
@@ -18,6 +18,7 @@
#include "components/update_client/configurator.h"
#include "components/update_client/crx_update_item.h"
#include "components/update_client/persisted_data.h"
+#include "components/update_client/protocol_parser.h"
#include "components/update_client/update_checker.h"
#include "components/update_client/update_client_errors.h"
#include "components/update_client/utils.h"
@@ -81,6 +82,11 @@ void UpdateEngine::Update(bool is_foreground,
}
if (IsThrottled(is_foreground)) {
+ // TODO(xiaochu): remove this log after https://crbug.com/851151 is fixed.
+ VLOG(1) << "Background update is throttled for following components:";
+ for (const auto& id : ids) {
+ VLOG(1) << "id:" << id;
+ }
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), Error::RETRY_LATER));
return;
@@ -175,13 +181,16 @@ void UpdateEngine::DoUpdateCheck(scoped_refptr<UpdateContext> update_context) {
update_context->components_to_check_for_updates,
update_context->components, config_->ExtraRequestParams(),
update_context->enabled_component_updates,
- base::BindOnce(&UpdateEngine::UpdateCheckDone, base::Unretained(this),
- update_context));
+ base::BindOnce(&UpdateEngine::UpdateCheckResultsAvailable,
+ base::Unretained(this), update_context));
}
-void UpdateEngine::UpdateCheckDone(scoped_refptr<UpdateContext> update_context,
- int error,
- int retry_after_sec) {
+void UpdateEngine::UpdateCheckResultsAvailable(
+ scoped_refptr<UpdateContext> update_context,
+ const base::Optional<ProtocolParser::Results>& results,
+ ErrorCategory error_category,
+ int error,
+ int retry_after_sec) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(update_context);
@@ -202,12 +211,53 @@ void UpdateEngine::UpdateCheckDone(scoped_refptr<UpdateContext> update_context,
update_context->update_check_error = error;
+ if (error) {
+ DCHECK(!results);
+ for (const auto& id : update_context->components_to_check_for_updates) {
+ DCHECK_EQ(1u, update_context->components.count(id));
+ auto& component = update_context->components.at(id);
+ component->SetUpdateCheckResult(base::nullopt,
+ ErrorCategory::kUpdateCheck, error);
+ }
+ return;
+ }
+
+ DCHECK(results);
+ DCHECK_EQ(0, error);
+
+ std::map<std::string, ProtocolParser::Result> id_to_result;
+ for (const auto& result : results->list)
+ id_to_result[result.extension_id] = result;
+
for (const auto& id : update_context->components_to_check_for_updates) {
DCHECK_EQ(1u, update_context->components.count(id));
- DCHECK(update_context->components.at(id));
-
- auto& component = *update_context->components.at(id);
- component.UpdateCheckComplete();
+ auto& component = update_context->components.at(id);
+ const auto& it = id_to_result.find(id);
+ if (it != id_to_result.end()) {
+ const auto result = it->second;
+ const auto error = [](const std::string& status) {
+ // First, handle app status literals which can be folded down as an
+ // updatecheck status
+ if (status == "error-unknownApplication")
+ return std::make_pair(ErrorCategory::kUpdateCheck,
+ ProtocolError::UNKNOWN_APPLICATION);
+ if (status == "restricted")
+ return std::make_pair(ErrorCategory::kUpdateCheck,
+ ProtocolError::RESTRICTED_APPLICATION);
+ if (status == "error-invalidAppId")
+ return std::make_pair(ErrorCategory::kUpdateCheck,
+ ProtocolError::INVALID_APPID);
+ // If the parser has return a valid result and the status is not one of
+ // the literals above, then this must be a success an not a parse error.
+ return std::make_pair(ErrorCategory::kNone, ProtocolError::NONE);
+ }(result.status);
+ component->SetUpdateCheckResult(result, error.first,
+ static_cast<int>(error.second));
+ } else {
+ component->SetUpdateCheckResult(
+ base::nullopt, ErrorCategory::kUpdateCheck,
+ static_cast<int>(ProtocolError::UPDATE_RESPONSE_NOT_FOUND));
+ }
}
}
diff --git a/chromium/components/update_client/update_engine.h b/chromium/components/update_client/update_engine.h
index 2cfe9da0465..96018736a9e 100644
--- a/chromium/components/update_client/update_engine.h
+++ b/chromium/components/update_client/update_engine.h
@@ -15,6 +15,7 @@
#include "base/containers/queue.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/optional.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "components/update_client/component.h"
@@ -81,9 +82,12 @@ class UpdateEngine : public base::RefCounted<UpdateEngine> {
void UpdateCheckComplete(scoped_refptr<UpdateContext> update_context);
void DoUpdateCheck(scoped_refptr<UpdateContext> update_context);
- void UpdateCheckDone(scoped_refptr<UpdateContext> update_context,
- int error,
- int retry_after_sec);
+ void UpdateCheckResultsAvailable(
+ scoped_refptr<UpdateContext> update_context,
+ const base::Optional<ProtocolParser::Results>& results,
+ ErrorCategory error_category,
+ int error,
+ int retry_after_sec);
void HandleComponent(scoped_refptr<UpdateContext> update_context);
void HandleComponentComplete(scoped_refptr<UpdateContext> update_context);
diff --git a/chromium/components/update_client/url_loader_post_interceptor.cc b/chromium/components/update_client/url_loader_post_interceptor.cc
new file mode 100644
index 00000000000..6f374787dce
--- /dev/null
+++ b/chromium/components/update_client/url_loader_post_interceptor.cc
@@ -0,0 +1,263 @@
+// 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/update_client/url_loader_post_interceptor.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/bind_test_util.h"
+#include "components/update_client/test_configurator.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace update_client {
+
+URLLoaderPostInterceptor::URLLoaderPostInterceptor(
+ network::TestURLLoaderFactory* url_loader_factory)
+ : url_loader_factory_(url_loader_factory) {
+ filtered_urls_.push_back(
+ GURL(base::StringPrintf("%s://%s%s", POST_INTERCEPT_SCHEME,
+ POST_INTERCEPT_HOSTNAME, POST_INTERCEPT_PATH)));
+ InitializeWithInterceptor();
+}
+
+URLLoaderPostInterceptor::URLLoaderPostInterceptor(
+ std::vector<GURL> supported_urls,
+ network::TestURLLoaderFactory* url_loader_factory)
+ : url_loader_factory_(url_loader_factory) {
+ DCHECK_LT(0u, supported_urls.size());
+ filtered_urls_.swap(supported_urls);
+ InitializeWithInterceptor();
+}
+
+URLLoaderPostInterceptor::URLLoaderPostInterceptor(
+ std::vector<GURL> supported_urls,
+ net::test_server::EmbeddedTestServer* embedded_test_server)
+ : embedded_test_server_(embedded_test_server) {
+ DCHECK_LT(0u, supported_urls.size());
+ filtered_urls_.swap(supported_urls);
+ InitializeWithRequestHandler();
+}
+
+URLLoaderPostInterceptor::~URLLoaderPostInterceptor() {}
+
+bool URLLoaderPostInterceptor::ExpectRequest(
+ std::unique_ptr<RequestMatcher> request_matcher) {
+ return ExpectRequest(std::move(request_matcher), net::HTTP_OK);
+}
+
+bool URLLoaderPostInterceptor::ExpectRequest(
+ std::unique_ptr<RequestMatcher> request_matcher,
+ net::HttpStatusCode response_code) {
+ expectations_.push(
+ {std::move(request_matcher), ExpectationResponse(response_code, "")});
+ return true;
+}
+
+bool URLLoaderPostInterceptor::ExpectRequest(
+ std::unique_ptr<RequestMatcher> request_matcher,
+ const base::FilePath& filepath) {
+ std::string response;
+ if (filepath.empty() || !base::ReadFileToString(filepath, &response))
+ return false;
+
+ expectations_.push({std::move(request_matcher),
+ ExpectationResponse(net::HTTP_OK, response)});
+ return true;
+}
+
+// Returns how many requests have been intercepted and matched by
+// an expectation. One expectation can only be matched by one request.
+int URLLoaderPostInterceptor::GetHitCount() const {
+ return hit_count_;
+}
+
+// Returns how many requests in total have been captured by the interceptor.
+int URLLoaderPostInterceptor::GetCount() const {
+ return static_cast<int>(requests_.size());
+}
+
+// Returns all requests that have been intercepted, matched or not.
+std::vector<URLLoaderPostInterceptor::InterceptedRequest>
+URLLoaderPostInterceptor::GetRequests() const {
+ return requests_;
+}
+
+// Return the body of the n-th request, zero-based.
+std::string URLLoaderPostInterceptor::GetRequestBody(size_t n) const {
+ return std::get<0>(requests_[n]);
+}
+
+// Returns the joined bodies of all requests for debugging purposes.
+std::string URLLoaderPostInterceptor::GetRequestsAsString() const {
+ const std::vector<InterceptedRequest> requests = GetRequests();
+ std::string s = "Requests are:";
+ int i = 0;
+ for (auto it = requests.cbegin(); it != requests.cend(); ++it)
+ s.append(base::StringPrintf("\n [%d]: %s", ++i, std::get<0>(*it).c_str()));
+ return s;
+}
+
+// Resets the state of the interceptor so that new expectations can be set.
+void URLLoaderPostInterceptor::Reset() {
+ hit_count_ = 0;
+ requests_.clear();
+ base::queue<Expectation>().swap(expectations_);
+}
+
+void URLLoaderPostInterceptor::Pause() {
+ is_paused_ = true;
+}
+
+void URLLoaderPostInterceptor::Resume() {
+ is_paused_ = false;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindLambdaForTesting([&]() {
+ if (!pending_expectations_.size())
+ return;
+
+ PendingExpectation expectation =
+ std::move(pending_expectations_.front());
+ pending_expectations_.pop();
+ url_loader_factory_->AddResponse(expectation.first.spec(),
+ expectation.second.response_body,
+ expectation.second.response_code);
+ }));
+}
+
+void URLLoaderPostInterceptor::url_job_request_ready_callback(
+ UrlJobRequestReadyCallback url_job_request_ready_callback) {
+ url_job_request_ready_callback_ = std::move(url_job_request_ready_callback);
+}
+
+int URLLoaderPostInterceptor::GetHitCountForURL(const GURL& url) {
+ int hit_count = 0;
+ const std::vector<InterceptedRequest> requests = GetRequests();
+ for (auto it = requests.cbegin(); it != requests.cend(); ++it) {
+ GURL url_no_query = std::get<2>(*it);
+ if (url_no_query.has_query()) {
+ GURL::Replacements replacements;
+ replacements.ClearQuery();
+ url_no_query = url_no_query.ReplaceComponents(replacements);
+ }
+ if (url_no_query == url)
+ hit_count++;
+ }
+ return hit_count;
+}
+
+void URLLoaderPostInterceptor::InitializeWithInterceptor() {
+ DCHECK(url_loader_factory_);
+ url_loader_factory_->SetInterceptor(
+ base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+ GURL url = request.url;
+ if (url.has_query()) {
+ GURL::Replacements replacements;
+ replacements.ClearQuery();
+ url = url.ReplaceComponents(replacements);
+ }
+ auto it = std::find_if(
+ filtered_urls_.begin(), filtered_urls_.end(),
+ [url](const GURL& filtered_url) { return filtered_url == url; });
+ if (it == filtered_urls_.end())
+ return;
+
+ std::string request_body = network::GetUploadData(request);
+ requests_.push_back({request_body, request.headers, request.url});
+ if (expectations_.empty())
+ return;
+ const auto& expectation = expectations_.front();
+ if (expectation.first->Match(request_body)) {
+ const net::HttpStatusCode response_code(
+ expectation.second.response_code);
+ const std::string response_body(expectation.second.response_body);
+
+ if (url_job_request_ready_callback_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, std::move(url_job_request_ready_callback_));
+ }
+
+ if (!is_paused_) {
+ url_loader_factory_->AddResponse(request.url.spec(), response_body,
+ response_code);
+ } else {
+ pending_expectations_.push({request.url, expectation.second});
+ }
+ expectations_.pop();
+ ++hit_count_;
+ }
+ }));
+}
+
+void URLLoaderPostInterceptor::InitializeWithRequestHandler() {
+ DCHECK(embedded_test_server_);
+ DCHECK(!url_loader_factory_);
+ embedded_test_server_->RegisterRequestHandler(base::BindRepeating(
+ &URLLoaderPostInterceptor::RequestHandler, base::Unretained(this)));
+}
+
+std::unique_ptr<net::test_server::HttpResponse>
+URLLoaderPostInterceptor::RequestHandler(
+ const net::test_server::HttpRequest& request) {
+ // Only intercepts POST.
+ if (request.method != net::test_server::METHOD_POST)
+ return nullptr;
+
+ GURL url = request.GetURL();
+ if (url.has_query()) {
+ GURL::Replacements replacements;
+ replacements.ClearQuery();
+ url = url.ReplaceComponents(replacements);
+ }
+ auto it = std::find_if(
+ filtered_urls_.begin(), filtered_urls_.end(),
+ [url](const GURL& filtered_url) { return filtered_url == url; });
+ if (it == filtered_urls_.end())
+ return nullptr;
+
+ std::string request_body = request.content;
+ net::HttpRequestHeaders headers;
+ for (auto it : request.headers)
+ headers.SetHeader(it.first, it.second);
+ requests_.push_back({request_body, headers, url});
+ if (expectations_.empty())
+ return nullptr;
+
+ const auto& expectation = expectations_.front();
+ if (expectation.first->Match(request_body)) {
+ const net::HttpStatusCode response_code(expectation.second.response_code);
+ const std::string response_body(expectation.second.response_body);
+ expectations_.pop();
+ ++hit_count_;
+
+ std::unique_ptr<net::test_server::BasicHttpResponse> http_response(
+ new net::test_server::BasicHttpResponse);
+ http_response->set_code(response_code);
+ http_response->set_content(response_body);
+ return http_response;
+ }
+
+ return nullptr;
+}
+
+bool PartialMatch::Match(const std::string& actual) const {
+ return actual.find(expected_) != std::string::npos;
+}
+
+bool AnyMatch::Match(const std::string& actual) const {
+ return true;
+}
+
+} // namespace update_client
diff --git a/chromium/components/update_client/url_request_post_interceptor.h b/chromium/components/update_client/url_loader_post_interceptor.h
index 4355f6e4e1b..c9c264bb66f 100644
--- a/chromium/components/update_client/url_request_post_interceptor.h
+++ b/chromium/components/update_client/url_loader_post_interceptor.h
@@ -1,49 +1,44 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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_UPDATE_CLIENT_URL_REQUEST_POST_INTERCEPTOR_H_
-#define COMPONENTS_UPDATE_CLIENT_URL_REQUEST_POST_INTERCEPTOR_H_
-
-#include <stdint.h>
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
+#ifndef COMPONENTS_UPDATE_CLIENT_URL_LOADER_POST_INTERCEPTOR_H_
+#define COMPONENTS_UPDATE_CLIENT_URL_LOADER_POST_INTERCEPTOR_H_
#include "base/callback.h"
#include "base/containers/queue.h"
+#include "base/files/file_path.h"
#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/synchronization/lock.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_status_code.h"
#include "url/gurl.h"
-namespace base {
-class FilePath;
-class SequencedTaskRunner;
+namespace network {
+class TestURLLoaderFactory;
}
namespace net {
-class HttpRequestHeaders;
-}
+namespace test_server {
+class EmbeddedTestServer;
+class HttpResponse;
+struct HttpRequest;
+} // namespace test_server
+} // namespace net
namespace update_client {
-class URLRequestMockJob;
-
// Intercepts requests to a file path, counts them, and captures the body of
// the requests. Optionally, for each request, it can return a canned response
// from a given file. The class maintains a queue of expectations, and returns
// one and only one response for each request that matches the expectation.
// Then, the expectation is removed from the queue.
-class URLRequestPostInterceptor
- : public base::RefCountedThreadSafe<URLRequestPostInterceptor> {
+class URLLoaderPostInterceptor {
public:
- using InterceptedRequest = std::pair<std::string, net::HttpRequestHeaders>;
+ using InterceptedRequest =
+ std::tuple<std::string, net::HttpRequestHeaders, GURL>;
- // Called when the job associated with the url request which is intercepted
- // by this object has been created.
+ // Called when the load associated with the url request is intercepted
+ // by this object:.
using UrlJobRequestReadyCallback = base::OnceCallback<void()>;
// Allows a generic string maching interface when setting up expectations.
@@ -53,8 +48,14 @@ class URLRequestPostInterceptor
virtual ~RequestMatcher() {}
};
- // Returns the url that is intercepted.
- GURL GetUrl() const;
+ explicit URLLoaderPostInterceptor(
+ network::TestURLLoaderFactory* url_loader_factory);
+ URLLoaderPostInterceptor(std::vector<GURL> supported_urls,
+ network::TestURLLoaderFactory* url_loader_factory);
+ URLLoaderPostInterceptor(std::vector<GURL> supported_urls,
+ net::test_server::EmbeddedTestServer*);
+
+ ~URLLoaderPostInterceptor();
// Sets an expection for the body of the POST request and optionally,
// provides a canned response identified by a |file_path| to be returned when
@@ -63,8 +64,10 @@ class URLRequestPostInterceptor
// response body with that response code is returned.
// Returns |true| if the expectation was set.
bool ExpectRequest(std::unique_ptr<RequestMatcher> request_matcher);
+
bool ExpectRequest(std::unique_ptr<RequestMatcher> request_matcher,
- int response_code);
+ net::HttpStatusCode response_code);
+
bool ExpectRequest(std::unique_ptr<RequestMatcher> request_matcher,
const base::FilePath& filepath);
@@ -103,38 +106,28 @@ class URLRequestPostInterceptor
void url_job_request_ready_callback(
UrlJobRequestReadyCallback url_job_request_ready_callback);
- private:
- class Delegate;
- class URLRequestMockJob;
+ int GetHitCountForURL(const GURL& url);
- friend class URLRequestPostInterceptorFactory;
- friend class base::RefCountedThreadSafe<URLRequestPostInterceptor>;
+ private:
+ void InitializeWithInterceptor();
+ void InitializeWithRequestHandler();
- static const int kResponseCode200 = 200;
+ std::unique_ptr<net::test_server::HttpResponse> RequestHandler(
+ const net::test_server::HttpRequest& request);
struct ExpectationResponse {
- ExpectationResponse(int code, const std::string& body)
+ ExpectationResponse(net::HttpStatusCode code, const std::string& body)
: response_code(code), response_body(body) {}
- const int response_code;
+ const net::HttpStatusCode response_code;
const std::string response_body;
};
using Expectation =
std::pair<std::unique_ptr<RequestMatcher>, ExpectationResponse>;
- URLRequestPostInterceptor(
- const GURL& url,
- scoped_refptr<base::SequencedTaskRunner> io_task_runner);
- ~URLRequestPostInterceptor();
-
- void ClearExpectations();
-
- const GURL url_;
- scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
-
- mutable base::Lock interceptor_lock_;
+ using PendingExpectation = std::pair<GURL, ExpectationResponse>;
// Contains the count of the request matching expectations.
- int hit_count_;
+ int hit_count_ = 0;
// Contains the request body and the extra headers of the intercepted
// requests.
@@ -143,59 +136,21 @@ class URLRequestPostInterceptor
// Contains the expectations which this interceptor tries to match.
base::queue<Expectation> expectations_;
- URLRequestMockJob* request_job_ = nullptr;
-
- bool is_paused_ = false;
-
- UrlJobRequestReadyCallback url_job_request_ready_callback_;
-
- DISALLOW_COPY_AND_ASSIGN(URLRequestPostInterceptor);
-};
-
-class URLRequestPostInterceptorFactory {
- public:
- URLRequestPostInterceptorFactory(
- const std::string& scheme,
- const std::string& hostname,
- scoped_refptr<base::SequencedTaskRunner> io_task_runner);
- ~URLRequestPostInterceptorFactory();
-
- // Creates an interceptor object for the specified url path.
- scoped_refptr<URLRequestPostInterceptor> CreateInterceptor(
- const base::FilePath& filepath);
-
- private:
- const std::string scheme_;
- const std::string hostname_;
- scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
-
- // After creation, |delegate_| lives on the IO thread and it is owned by
- // a URLRequestFilter after registration. A task to unregister it and
- // implicitly destroy it is posted from ~URLRequestPostInterceptorFactory().
- URLRequestPostInterceptor::Delegate* delegate_;
+ base::queue<PendingExpectation> pending_expectations_;
- DISALLOW_COPY_AND_ASSIGN(URLRequestPostInterceptorFactory);
-};
+ network::TestURLLoaderFactory* url_loader_factory_ = nullptr;
+ net::test_server::EmbeddedTestServer* embedded_test_server_ = nullptr;
-// Intercepts HTTP POST requests sent to "localhost2".
-class InterceptorFactory : public URLRequestPostInterceptorFactory {
- public:
- explicit InterceptorFactory(
- scoped_refptr<base::SequencedTaskRunner> io_task_runner);
- ~InterceptorFactory();
+ bool is_paused_ = false;
- // Creates an interceptor for the url path defined by POST_INTERCEPT_PATH.
- scoped_refptr<URLRequestPostInterceptor> CreateInterceptor();
+ std::vector<GURL> filtered_urls_;
- // Creates an interceptor for the given url path.
- scoped_refptr<URLRequestPostInterceptor> CreateInterceptorForPath(
- const char* url_path);
+ UrlJobRequestReadyCallback url_job_request_ready_callback_;
- private:
- DISALLOW_COPY_AND_ASSIGN(InterceptorFactory);
+ DISALLOW_COPY_AND_ASSIGN(URLLoaderPostInterceptor);
};
-class PartialMatch : public URLRequestPostInterceptor::RequestMatcher {
+class PartialMatch : public URLLoaderPostInterceptor::RequestMatcher {
public:
explicit PartialMatch(const std::string& expected) : expected_(expected) {}
bool Match(const std::string& actual) const override;
@@ -206,7 +161,7 @@ class PartialMatch : public URLRequestPostInterceptor::RequestMatcher {
DISALLOW_COPY_AND_ASSIGN(PartialMatch);
};
-class AnyMatch : public URLRequestPostInterceptor::RequestMatcher {
+class AnyMatch : public URLLoaderPostInterceptor::RequestMatcher {
public:
AnyMatch() = default;
bool Match(const std::string& actual) const override;
@@ -217,4 +172,4 @@ class AnyMatch : public URLRequestPostInterceptor::RequestMatcher {
} // namespace update_client
-#endif // COMPONENTS_UPDATE_CLIENT_URL_REQUEST_POST_INTERCEPTOR_H_
+#endif // COMPONENTS_UPDATE_CLIENT_URL_LOADER_POST_INTERCEPTOR_H_
diff --git a/chromium/components/update_client/url_request_post_interceptor.cc b/chromium/components/update_client/url_request_post_interceptor.cc
deleted file mode 100644
index b177f533287..00000000000
--- a/chromium/components/update_client/url_request_post_interceptor.cc
+++ /dev/null
@@ -1,340 +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/update_client/url_request_post_interceptor.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/stringprintf.h"
-#include "components/update_client/test_configurator.h"
-#include "net/base/upload_bytes_element_reader.h"
-#include "net/base/upload_data_stream.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_util.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_filter.h"
-#include "net/url_request/url_request_interceptor.h"
-#include "net/url_request/url_request_simple_job.h"
-#include "net/url_request/url_request_test_util.h"
-
-namespace update_client {
-
-// Returns a canned response.
-class URLRequestPostInterceptor::URLRequestMockJob
- : public net::URLRequestSimpleJob {
- public:
- URLRequestMockJob(scoped_refptr<URLRequestPostInterceptor> interceptor,
- net::URLRequest* request,
- net::NetworkDelegate* network_delegate,
- int response_code,
- const std::string& response_body)
- : net::URLRequestSimpleJob(request, network_delegate),
- interceptor_(interceptor),
- response_code_(response_code),
- response_body_(response_body) {}
-
- void Start() override {
- if (interceptor_->is_paused_)
- return;
- net::URLRequestSimpleJob::Start();
- }
-
- protected:
- void GetResponseInfo(net::HttpResponseInfo* info) override {
- const std::string headers =
- base::StringPrintf("HTTP/1.1 %i OK\r\n\r\n", response_code_);
- info->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
- net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.length()));
- }
-
- int GetData(std::string* mime_type,
- std::string* charset,
- std::string* data,
- const net::CompletionCallback& callback) const override {
- mime_type->assign("text/plain");
- charset->assign("US-ASCII");
- data->assign(response_body_);
- return net::OK;
- }
-
- private:
- ~URLRequestMockJob() override {}
-
- scoped_refptr<URLRequestPostInterceptor> interceptor_;
-
- int response_code_;
- std::string response_body_;
- DISALLOW_COPY_AND_ASSIGN(URLRequestMockJob);
-};
-
-URLRequestPostInterceptor::URLRequestPostInterceptor(
- const GURL& url,
- scoped_refptr<base::SequencedTaskRunner> io_task_runner)
- : url_(url), io_task_runner_(io_task_runner), hit_count_(0) {}
-
-URLRequestPostInterceptor::~URLRequestPostInterceptor() {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-}
-
-GURL URLRequestPostInterceptor::GetUrl() const {
- return url_;
-}
-
-bool URLRequestPostInterceptor::ExpectRequest(
- std::unique_ptr<RequestMatcher> request_matcher) {
- return ExpectRequest(std::move(request_matcher), kResponseCode200);
-}
-
-bool URLRequestPostInterceptor::ExpectRequest(
- std::unique_ptr<RequestMatcher> request_matcher,
- int response_code) {
- expectations_.push(
- {std::move(request_matcher), ExpectationResponse(response_code, "")});
- return true;
-}
-
-bool URLRequestPostInterceptor::ExpectRequest(
- std::unique_ptr<RequestMatcher> request_matcher,
- const base::FilePath& filepath) {
- std::string response;
- if (filepath.empty() || !base::ReadFileToString(filepath, &response))
- return false;
-
- expectations_.push({std::move(request_matcher),
- ExpectationResponse(kResponseCode200, response)});
- return true;
-}
-
-int URLRequestPostInterceptor::GetHitCount() const {
- base::AutoLock auto_lock(interceptor_lock_);
- return hit_count_;
-}
-
-int URLRequestPostInterceptor::GetCount() const {
- base::AutoLock auto_lock(interceptor_lock_);
- return static_cast<int>(requests_.size());
-}
-
-std::vector<URLRequestPostInterceptor::InterceptedRequest>
-URLRequestPostInterceptor::GetRequests() const {
- base::AutoLock auto_lock(interceptor_lock_);
- return requests_;
-}
-
-std::string URLRequestPostInterceptor::GetRequestBody(size_t n) const {
- base::AutoLock auto_lock(interceptor_lock_);
- return requests_[n].first;
-}
-
-std::string URLRequestPostInterceptor::GetRequestsAsString() const {
- const std::vector<InterceptedRequest> requests = GetRequests();
-
- std::string s = "Requests are:";
-
- int i = 0;
- for (auto it = requests.cbegin(); it != requests.cend(); ++it)
- s.append(base::StringPrintf("\n [%d]: %s", ++i, it->first.c_str()));
-
- return s;
-}
-
-void URLRequestPostInterceptor::Reset() {
- base::AutoLock auto_lock(interceptor_lock_);
- hit_count_ = 0;
- requests_.clear();
- base::queue<Expectation>().swap(expectations_);
-}
-
-void URLRequestPostInterceptor::Pause() {
- base::AutoLock auto_lock(interceptor_lock_);
- is_paused_ = true;
-}
-
-void URLRequestPostInterceptor::Resume() {
- base::AutoLock auto_lock(interceptor_lock_);
- is_paused_ = false;
- io_task_runner_->PostTask(FROM_HERE,
- base::BindOnce(&URLRequestMockJob::Start,
- base::Unretained(request_job_)));
-}
-
-void URLRequestPostInterceptor::url_job_request_ready_callback(
- UrlJobRequestReadyCallback url_job_request_ready_callback) {
- base::AutoLock auto_lock(interceptor_lock_);
- url_job_request_ready_callback_ = std::move(url_job_request_ready_callback);
-}
-
-class URLRequestPostInterceptor::Delegate : public net::URLRequestInterceptor {
- public:
- Delegate(const std::string& scheme,
- const std::string& hostname,
- scoped_refptr<base::SequencedTaskRunner> io_task_runner)
- : scheme_(scheme), hostname_(hostname), io_task_runner_(io_task_runner) {}
-
- void Register() {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
- scheme_, hostname_, std::unique_ptr<net::URLRequestInterceptor>(this));
- }
-
- void Unregister() {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- interceptors_.clear();
- net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(scheme_,
- hostname_);
- }
-
- void OnCreateInterceptor(
- scoped_refptr<URLRequestPostInterceptor> interceptor) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- DCHECK_EQ(0u, interceptors_.count(interceptor->GetUrl()));
-
- interceptors_.insert(std::make_pair(interceptor->GetUrl(), interceptor));
- }
-
- private:
- ~Delegate() override {}
-
- net::URLRequestJob* MaybeInterceptRequest(
- net::URLRequest* request,
- net::NetworkDelegate* network_delegate) const override {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-
- // Only intercepts POST.
- if (!request->has_upload())
- return nullptr;
-
- GURL url = request->url();
- if (url.has_query()) {
- GURL::Replacements replacements;
- replacements.ClearQuery();
- url = url.ReplaceComponents(replacements);
- }
-
- const auto it = interceptors_.find(url);
- if (it == interceptors_.end())
- return nullptr;
-
- // There is an interceptor hooked up for this url. Read the request body,
- // check the existing expectations, and handle the matching case by
- // popping the expectation off the queue, counting the match, and
- // returning a mock object to serve the canned response.
- auto interceptor = it->second;
-
- const net::UploadDataStream* stream = request->get_upload();
- const net::UploadBytesElementReader* reader =
- (*stream->GetElementReaders())[0]->AsBytesReader();
- const int size = reader->length();
- scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(size));
- const std::string request_body(reader->bytes());
-
- {
- base::AutoLock auto_lock(interceptor->interceptor_lock_);
- interceptor->requests_.push_back(
- {request_body, request->extra_request_headers()});
- if (interceptor->expectations_.empty())
- return nullptr;
- const auto& expectation = interceptor->expectations_.front();
- if (expectation.first->Match(request_body)) {
- const int response_code(expectation.second.response_code);
- const std::string response_body(expectation.second.response_body);
- interceptor->expectations_.pop();
- ++interceptor->hit_count_;
- interceptor->request_job_ =
- new URLRequestMockJob(interceptor, request, network_delegate,
- response_code, response_body);
- if (interceptor->url_job_request_ready_callback_) {
- io_task_runner_->PostTask(
- FROM_HERE,
- std::move(interceptor->url_job_request_ready_callback_));
- }
- return interceptor->request_job_;
- }
- }
-
- return nullptr;
- }
-
- using InterceptorMap =
- std::map<GURL, scoped_refptr<URLRequestPostInterceptor>>;
- InterceptorMap interceptors_;
-
- const std::string scheme_;
- const std::string hostname_;
- scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
-
- DISALLOW_COPY_AND_ASSIGN(Delegate);
-};
-
-URLRequestPostInterceptorFactory::URLRequestPostInterceptorFactory(
- const std::string& scheme,
- const std::string& hostname,
- scoped_refptr<base::SequencedTaskRunner> io_task_runner)
- : scheme_(scheme),
- hostname_(hostname),
- io_task_runner_(io_task_runner),
- delegate_(new URLRequestPostInterceptor::Delegate(scheme,
- hostname,
- io_task_runner)) {
- io_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&URLRequestPostInterceptor::Delegate::Register,
- base::Unretained(delegate_)));
-}
-
-URLRequestPostInterceptorFactory::~URLRequestPostInterceptorFactory() {
- io_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&URLRequestPostInterceptor::Delegate::Unregister,
- base::Unretained(delegate_)));
-}
-
-scoped_refptr<URLRequestPostInterceptor>
-URLRequestPostInterceptorFactory::CreateInterceptor(
- const base::FilePath& filepath) {
- const GURL base_url(
- base::StringPrintf("%s://%s", scheme_.c_str(), hostname_.c_str()));
- GURL absolute_url(base_url.Resolve(filepath.MaybeAsASCII()));
- auto interceptor = scoped_refptr<URLRequestPostInterceptor>(
- new URLRequestPostInterceptor(absolute_url, io_task_runner_));
- bool res = io_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&URLRequestPostInterceptor::Delegate::OnCreateInterceptor,
- base::Unretained(delegate_), interceptor));
- return res ? interceptor : nullptr;
-}
-
-bool PartialMatch::Match(const std::string& actual) const {
- return actual.find(expected_) != std::string::npos;
-}
-
-bool AnyMatch::Match(const std::string&) const {
- return true;
-}
-
-InterceptorFactory::InterceptorFactory(
- scoped_refptr<base::SequencedTaskRunner> io_task_runner)
- : URLRequestPostInterceptorFactory(POST_INTERCEPT_SCHEME,
- POST_INTERCEPT_HOSTNAME,
- io_task_runner) {}
-
-InterceptorFactory::~InterceptorFactory() {
-}
-
-scoped_refptr<URLRequestPostInterceptor>
-InterceptorFactory::CreateInterceptor() {
- return CreateInterceptorForPath(POST_INTERCEPT_PATH);
-}
-
-scoped_refptr<URLRequestPostInterceptor>
-InterceptorFactory::CreateInterceptorForPath(const char* url_path) {
- return URLRequestPostInterceptorFactory::CreateInterceptor(
- base::FilePath::FromUTF8Unsafe(url_path));
-}
-
-} // namespace update_client
diff --git a/chromium/components/update_client/utils.cc b/chromium/components/update_client/utils.cc
index 1213c1f5b57..85003a2808e 100644
--- a/chromium/components/update_client/utils.cc
+++ b/chromium/components/update_client/utils.cc
@@ -20,7 +20,6 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "components/crx_file/id_util.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
@@ -33,18 +32,19 @@
#include "net/base/load_flags.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_status.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
#include "url/gurl.h"
namespace update_client {
-std::unique_ptr<net::URLFetcher> SendProtocolRequest(
+std::unique_ptr<network::SimpleURLLoader> SendProtocolRequest(
const GURL& url,
const std::map<std::string, std::string>& protocol_request_extra_headers,
const std::string& protocol_request,
- net::URLFetcherDelegate* url_fetcher_delegate,
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter) {
+ network::SimpleURLLoader::BodyAsStringCallback callback,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("component_updater_utils", R"(
semantics {
@@ -73,26 +73,26 @@ std::unique_ptr<net::URLFetcher> SendProtocolRequest(
}
}
})");
- std::unique_ptr<net::URLFetcher> url_fetcher = net::URLFetcher::Create(
- 0, url, net::URLFetcher::POST, url_fetcher_delegate, traffic_annotation);
- if (!url_fetcher.get())
- return url_fetcher;
-
- data_use_measurement::DataUseUserData::AttachToFetcher(
- url_fetcher.get(), data_use_measurement::DataUseUserData::UPDATE_CLIENT);
- url_fetcher->SetUploadData("application/xml", protocol_request);
- url_fetcher->SetRequestContext(url_request_context_getter.get());
- url_fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES |
- net::LOAD_DISABLE_CACHE);
- url_fetcher->SetAutomaticallyRetryOn5xx(false);
- url_fetcher->SetAutomaticallyRetryOnNetworkChanges(3);
+ // Create and initialize URL loader.
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = url;
+ resource_request->method = "POST";
+ resource_request->load_flags = net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DISABLE_CACHE;
for (const auto& header : protocol_request_extra_headers)
- url_fetcher->AddExtraRequestHeader(base::StringPrintf(
- "%s: %s", header.first.c_str(), header.second.c_str()));
-
- url_fetcher->Start();
- return url_fetcher;
+ resource_request->headers.SetHeader(header.first, header.second);
+
+ auto simple_loader = network::SimpleURLLoader::Create(
+ std::move(resource_request), traffic_annotation);
+ const int max_retry_on_network_change = 3;
+ simple_loader->SetRetryOptions(
+ max_retry_on_network_change,
+ network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
+ simple_loader->AttachStringForUpload(protocol_request, "application/xml");
+ simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory.get(), std::move(callback));
+ return simple_loader;
}
bool FetchSuccess(const net::URLFetcher& fetcher) {
@@ -249,7 +249,7 @@ std::unique_ptr<base::DictionaryValue> ReadManifest(
JSONFileValueDeserializer deserializer(manifest);
std::string error;
std::unique_ptr<base::Value> root = deserializer.Deserialize(nullptr, &error);
- if (!root.get())
+ if (!root)
return std::unique_ptr<base::DictionaryValue>();
if (!root->is_dict())
return std::unique_ptr<base::DictionaryValue>();
diff --git a/chromium/components/update_client/utils.h b/chromium/components/update_client/utils.h
index e2840f9dcbb..525b7ed8a60 100644
--- a/chromium/components/update_client/utils.h
+++ b/chromium/components/update_client/utils.h
@@ -24,10 +24,13 @@ class FilePath;
namespace net {
class URLFetcher;
-class URLFetcherDelegate;
-class URLRequestContextGetter;
}
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+} // namespace network
+
namespace update_client {
class Component;
@@ -38,15 +41,18 @@ struct CrxComponent;
// in an update check request.
using InstallerAttribute = std::pair<std::string, std::string>;
+using LoadCompleteCallback =
+ base::OnceCallback<void(std::unique_ptr<std::string> response_body)>;
+
// Sends a protocol request to the the service endpoint specified by |url|.
// The body of the request is provided by |protocol_request| and it is
// expected to contain XML data. The caller owns the returned object.
-std::unique_ptr<net::URLFetcher> SendProtocolRequest(
+std::unique_ptr<network::SimpleURLLoader> SendProtocolRequest(
const GURL& url,
const std::map<std::string, std::string>& protocol_request_extra_headers,
const std::string& protocol_request,
- net::URLFetcherDelegate* url_fetcher_delegate,
- scoped_refptr<net::URLRequestContextGetter> url_request_context_getter);
+ LoadCompleteCallback callback,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
// Returns true if the url request of |fetcher| was succesful.
bool FetchSuccess(const net::URLFetcher& fetcher);
diff --git a/chromium/components/upload_list/text_log_upload_list.cc b/chromium/components/upload_list/text_log_upload_list.cc
index 32397394615..9344ee49e04 100644
--- a/chromium/components/upload_list/text_log_upload_list.cc
+++ b/chromium/components/upload_list/text_log_upload_list.cc
@@ -12,7 +12,7 @@
#include "base/strings/string_util.h"
TextLogUploadList::TextLogUploadList(const base::FilePath& upload_log_path)
- : UploadList(), upload_log_path_(upload_log_path) {}
+ : upload_log_path_(upload_log_path) {}
TextLogUploadList::~TextLogUploadList() = default;
diff --git a/chromium/components/url_formatter/BUILD.gn b/chromium/components/url_formatter/BUILD.gn
index ef526ac4f74..50dd9e894bc 100644
--- a/chromium/components/url_formatter/BUILD.gn
+++ b/chromium/components/url_formatter/BUILD.gn
@@ -25,8 +25,9 @@ static_library("url_formatter") {
deps = [
"//base",
"//base:i18n",
- "//components/url_formatter/top_domains",
+ "//components/url_formatter/top_domains:generate_top_domains_trie",
"//net",
+ "//net:preload_decoder",
"//third_party/icu",
"//ui/gfx",
"//url",
@@ -48,8 +49,9 @@ source_set("unit_tests") {
deps = [
":url_formatter",
"//base",
- "//components/url_formatter/top_domains",
+ "//components/url_formatter/top_domains:generate_top_domains_test_trie",
"//net",
+ "//net:preload_decoder",
"//testing/gtest",
"//ui/gfx",
"//url",
diff --git a/chromium/components/url_formatter/idn_spoof_checker.cc b/chromium/components/url_formatter/idn_spoof_checker.cc
index a41e7350391..df6adec3032 100644
--- a/chromium/components/url_formatter/idn_spoof_checker.cc
+++ b/chromium/components/url_formatter/idn_spoof_checker.cc
@@ -22,6 +22,45 @@ namespace url_formatter {
namespace {
+class TopDomainPreloadDecoder : public net::extras::PreloadDecoder {
+ public:
+ using net::extras::PreloadDecoder::PreloadDecoder;
+ ~TopDomainPreloadDecoder() override {}
+
+ bool ReadEntry(net::extras::PreloadDecoder::BitReader* reader,
+ const std::string& search,
+ size_t current_search_offset,
+ bool* out_found) override {
+ bool is_same_skeleton;
+ if (!reader->Next(&is_same_skeleton))
+ return false;
+
+ if (is_same_skeleton) {
+ *out_found = true;
+ return true;
+ }
+
+ bool has_com_suffix = false;
+ if (!reader->Next(&has_com_suffix))
+ return false;
+
+ std::string top_domain;
+ for (char c;; top_domain += c) {
+ huffman_decoder().Decode(reader, &c);
+ if (c == net::extras::PreloadDecoder::kEndOfTable)
+ break;
+ }
+ if (has_com_suffix)
+ top_domain += ".com";
+
+ if (current_search_offset == 0) {
+ *out_found = true;
+ DCHECK(!top_domain.empty());
+ }
+ return true;
+ }
+};
+
void OnThreadTermination(void* regex_matcher) {
delete reinterpret_cast<icu::RegexMatcher*>(regex_matcher);
}
@@ -32,13 +71,20 @@ base::ThreadLocalStorage::Slot& DangerousPatternTLS() {
return *dangerous_pattern_tls;
}
-#include "components/url_formatter/top_domains/alexa_skeletons-inc.cc"
+#include "components/url_formatter/top_domains/alexa_domains-trie-inc.cc"
+
// All the domains in the above file have 3 or fewer labels.
const size_t kNumberOfLabelsToCheck = 3;
-const unsigned char* g_graph = kDafsa;
-size_t g_graph_length = sizeof(kDafsa);
+
+IDNSpoofChecker::HuffmanTrieParams g_trie_params{
+ kTopDomainsHuffmanTree, sizeof(kTopDomainsHuffmanTree), kTopDomainsTrie,
+ kTopDomainsTrieBits, kTopDomainsRootPosition};
bool LookupMatchInTopDomains(const icu::UnicodeString& ustr_skeleton) {
+ TopDomainPreloadDecoder preload_decoder(
+ g_trie_params.huffman_tree, g_trie_params.huffman_tree_size,
+ g_trie_params.trie, g_trie_params.trie_bits,
+ g_trie_params.trie_root_position);
std::string skeleton;
ustr_skeleton.toUTF8String(skeleton);
DCHECK_NE(skeleton.back(), '.');
@@ -52,10 +98,15 @@ bool LookupMatchInTopDomains(const icu::UnicodeString& ustr_skeleton) {
while (labels.size() > 1) {
std::string partial_skeleton = base::JoinString(labels, ".");
- if (net::LookupStringInFixedSet(
- g_graph, g_graph_length, partial_skeleton.data(),
- partial_skeleton.length()) != net::kDafsaNotFound)
+ bool match = false;
+ bool decoded = preload_decoder.Decode(partial_skeleton, &match);
+ DCHECK(decoded);
+ if (!decoded)
+ return false;
+
+ if (match)
return true;
+
labels.erase(labels.begin());
}
return false;
@@ -315,8 +366,8 @@ bool IDNSpoofChecker::SimilarToTopDomains(base::StringPiece16 hostname) {
// there is no point in getting rid of diacritics because combining marks
// attached to non-LGC characters are already blocked.
if (lgc_letters_n_ascii_.span(host, 0, USET_SPAN_CONTAINED) == host.length())
- diacritic_remover_.get()->transliterate(host);
- extra_confusable_mapper_.get()->transliterate(host);
+ diacritic_remover_->transliterate(host);
+ extra_confusable_mapper_->transliterate(host);
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString skeleton;
@@ -439,15 +490,17 @@ void IDNSpoofChecker::SetAllowedUnicodeSet(UErrorCode* status) {
uspoof_setAllowedUnicodeSet(checker_, &allowed_set, status);
}
-void IDNSpoofChecker::RestoreTopDomainGraphToDefault() {
- g_graph = kDafsa;
- g_graph_length = sizeof(kDafsa);
+// static
+void IDNSpoofChecker::SetTrieParamsForTesting(
+ const HuffmanTrieParams& trie_params) {
+ g_trie_params = trie_params;
}
-void IDNSpoofChecker::SetTopDomainGraph(base::StringPiece domain_graph) {
- DCHECK_NE(0u, domain_graph.length());
- g_graph = reinterpret_cast<const unsigned char*>(domain_graph.data());
- g_graph_length = domain_graph.length();
+// static
+void IDNSpoofChecker::RestoreTrieParamsForTesting() {
+ g_trie_params = HuffmanTrieParams{
+ kTopDomainsHuffmanTree, sizeof(kTopDomainsHuffmanTree), kTopDomainsTrie,
+ kTopDomainsTrieBits, kTopDomainsRootPosition};
}
} // namespace url_formatter
diff --git a/chromium/components/url_formatter/idn_spoof_checker.h b/chromium/components/url_formatter/idn_spoof_checker.h
index 5778c3b939a..ea235adc9f1 100644
--- a/chromium/components/url_formatter/idn_spoof_checker.h
+++ b/chromium/components/url_formatter/idn_spoof_checker.h
@@ -11,6 +11,8 @@
#include "base/gtest_prod_util.h"
#include "base/strings/string16.h"
#include "base/strings/string_piece_forward.h"
+#include "net/extras/preload_data/decoder.h"
+
#include "third_party/icu/source/common/unicode/uniset.h"
#include "third_party/icu/source/common/unicode/utypes.h"
#include "third_party/icu/source/common/unicode/uversion.h"
@@ -35,6 +37,13 @@ FORWARD_DECLARE_TEST(UrlFormatterTest, IDNToUnicode);
class IDNSpoofChecker {
public:
+ struct HuffmanTrieParams {
+ const uint8_t* huffman_tree;
+ size_t huffman_tree_size;
+ const uint8_t* trie;
+ size_t trie_bits;
+ size_t trie_root_position;
+ };
IDNSpoofChecker();
~IDNSpoofChecker();
@@ -62,8 +71,8 @@ class IDNSpoofChecker {
bool IsMadeOfLatinAlikeCyrillic(const icu::UnicodeString& label);
// Used for unit tests.
- static void RestoreTopDomainGraphToDefault();
- static void SetTopDomainGraph(base::StringPiece domain_graph);
+ static void SetTrieParamsForTesting(const HuffmanTrieParams& trie_params);
+ static void RestoreTrieParamsForTesting();
USpoofChecker* checker_;
icu::UnicodeSet deviation_characters_;
diff --git a/chromium/components/url_formatter/top_domains/BUILD.gn b/chromium/components/url_formatter/top_domains/BUILD.gn
index d8c977189eb..aa76af71310 100644
--- a/chromium/components/url_formatter/top_domains/BUILD.gn
+++ b/chromium/components/url_formatter/top_domains/BUILD.gn
@@ -2,33 +2,70 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-action_foreach("top_domains") {
- script = "//net/tools/dafsa/make_dafsa.py"
- sources = [
- "alexa_skeletons.gperf",
- "test_skeletons.gperf",
- ]
- outputs = [
- "${target_gen_dir}/{{source_name_part}}-inc.cc",
- ]
- args = [
- "{{source}}",
- rebase_path("${target_gen_dir}/{{source_name_part}}-inc.cc",
- root_build_dir),
- ]
-}
+import("//build/compiled_action.gni")
if (!is_ios && !is_android) {
- executable("make_top_domain_gperf") {
+ executable("make_top_domain_skeletons") {
sources = [
- "make_top_domain_gperf.cc",
+ "make_top_domain_skeletons.cc",
]
deps = [
"//base",
"//base:i18n",
- "//build/config:exe_and_shlib_deps",
"//third_party/icu",
]
}
}
+
+executable("top_domain_generator") {
+ sources = [
+ "top_domain_generator.cc",
+ "top_domain_state_generator.cc",
+ "top_domain_state_generator.h",
+ "trie_entry.cc",
+ "trie_entry.h",
+ ]
+ deps = [
+ "//base",
+ "//net/tools/huffman_trie:huffman_trie_generator_sources",
+ ]
+ if (is_ios) {
+ libs = [ "UIKit.framework" ]
+ }
+}
+
+compiled_action("generate_top_domains_trie") {
+ tool = ":top_domain_generator"
+
+ # Inputs in order expected by the command line of the tool.
+ inputs = [
+ "//components/url_formatter/top_domains/alexa_domains.skeletons",
+ "//components/url_formatter/top_domains/top_domains_trie.template",
+ ]
+ outputs = [
+ "$target_gen_dir/alexa_domains-trie-inc.cc",
+ ]
+ args =
+ # Make sure the inputs are system-absolute, as base::File cannot open
+ # files with ".." components.
+ rebase_path(inputs, "", "/") + rebase_path(outputs, root_build_dir)
+}
+
+# TODO: Combine this and the previous one into a compiled_action_foreach target.
+compiled_action("generate_top_domains_test_trie") {
+ tool = ":top_domain_generator"
+
+ # Inputs in order expected by the command line of the tool.
+ inputs = [
+ "//components/url_formatter/top_domains/test_domains.skeletons",
+ "//components/url_formatter/top_domains/top_domains_trie.template",
+ ]
+ outputs = [
+ "$target_gen_dir/test_domains-trie-inc.cc",
+ ]
+ args =
+ # Make sure the inputs are system-absolute, as base::File cannot open
+ # files with ".." components.
+ rebase_path(inputs, "", "/") + rebase_path(outputs, root_build_dir)
+}
diff --git a/chromium/components/url_formatter/top_domains/alexa_domains.skeletons b/chromium/components/url_formatter/top_domains/alexa_domains.skeletons
new file mode 100644
index 00000000000..94548d6c01e
--- /dev/null
+++ b/chromium/components/url_formatter/top_domains/alexa_domains.skeletons
@@ -0,0 +1,9184 @@
+# 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.
+
+# This file is generated by components/url_formatter/make_top_domain_skeletons.cc
+# DO NOT MANUALLY EDIT!
+
+# Each entry is the skeleton of a top domain for the confusability check
+# in components/url_formatter/url_formatter.cc.
+
+facebook.corn, facebook.com
+google.corn, google.com
+youtube.corn, youtube.com
+yahoo.corn, yahoo.com
+baidu.corn, baidu.com
+arnazon.corn, amazon.com
+wikipedia.org, wikipedia.org
+qq.corn, qq.com
+live.corn, live.com
+taobao.corn, taobao.com
+google.co.in, google.co.in
+twitter.corn, twitter.com
+blogspot.corn, blogspot.com
+linkedin.corn, linkedin.com
+bing.corn, bing.com
+yandex.ru, yandex.ru
+vk.corn, vk.com
+ask.corn, ask.com
+ebay.corn, ebay.com
+wordpress.corn, wordpress.com
+google.de, google.de
+rnsn.corn, msn.com
+turnblr.corn, tumblr.com
+l63.corn, 163.com
+google.corn.hk, google.com.hk
+rnail.ru, mail.ru
+google.co.uk, google.co.uk
+haol23.corn, hao123.com
+google.corn.br, google.com.br
+weibo.corn, weibo.com
+xvideos.corn, xvideos.com
+rnicrosoft.corn, microsoft.com
+delta-search.corn, delta-search.com
+google.fr, google.fr
+conduit.corn, conduit.com
+fc2.corn, fc2.com
+craigslist.org, craigslist.org
+google.ru, google.ru
+pinterest.corn, pinterest.com
+instagrarn.corn, instagram.com
+trnall.corn, tmall.com
+xharnster.corn, xhamster.com
+odnoklassniki.ru, odnoklassniki.ru
+google.it, google.it
+sohu.corn, sohu.com
+paypal.corn, paypal.com
+babylon.corn, babylon.com
+google.es, google.es
+irndb.corn, imdb.com
+apple.corn, apple.com
+arnazon.de, amazon.de
+bbc.co.uk, bbc.co.uk
+adobe.corn, adobe.com
+soso.corn, soso.com
+pornhub.corn, pornhub.com
+google.corn.rnx, google.com.mx
+blogger.corn, blogger.com
+neobux.corn, neobux.com
+arnazon.co.uk, amazon.co.uk
+ifeng.corn, ifeng.com
+google.ca, google.ca
+avg.corn, avg.com
+go.corn, go.com
+xnxx.corn, xnxx.com
+blogspot.in, blogspot.in
+alibaba.corn, alibaba.com
+aol.corn, aol.com
+buildathorne.info, buildathome.info
+cnn.corn, cnn.com
+rnywebsearch.corn, mywebsearch.com
+ku6.corn, ku6.com
+alipay.corn, alipay.com
+vube.corn, vube.com
+google.corn.tr, google.com.tr
+youku.corn, youku.com
+redtube.corn, redtube.com
+dailyrnotion.corn, dailymotion.com
+google.corn.au, google.com.au
+adf.ly, adf.ly
+netflix.corn, netflix.com
+adcash.corn, adcash.com
+about.corn, about.com
+google.pl, google.pl
+irngur.corn, imgur.com
+ebay.de, ebay.de
+arnazon.fr, amazon.fr
+flickr.corn, flickr.com
+thepiratebay.sx, thepiratebay.sx
+youporn.corn, youporn.com
+uol.corn.br, uol.com.br
+huffingtonpost.corn, huffingtonpost.com
+stackoverflow.corn, stackoverflow.com
+jd.corn, jd.com
+t.co, t.co
+livejasrnin.corn, livejasmin.com
+ebay.co.uk, ebay.co.uk
+yieldrnanager.corn, yieldmanager.com
+sogou.corn, sogou.com
+globo.corn, globo.com
+softonic.corn, softonic.com
+cnet.corn, cnet.com
+livedoor.corn, livedoor.com
+directrev.corn, directrev.com
+espn.go.corn, espn.go.com
+indiatirnes.corn, indiatimes.com
+wordpress.org, wordpress.org
+weather.corn, weather.com
+pixnet.net, pixnet.net
+google.corn.sa, google.com.sa
+clkrnon.corn, clkmon.com
+reddit.corn, reddit.com
+arnazon.it, amazon.it
+google.corn.eg, google.com.eg
+booking.corn, booking.com
+google.nl, google.nl
+douban.corn, douban.com
+slideshare.net, slideshare.net
+google.corn.ar, google.com.ar
+badoo.corn, badoo.com
+dailyrnail.co.uk, dailymail.co.uk
+google.co.th, google.co.th
+ask.frn, ask.fm
+wikia.corn, wikia.com
+godaddy.corn, godaddy.com
+xinhuanet.corn, xinhuanet.com
+rnediafire.corn, mediafire.com
+deviantart.corn, deviantart.com
+google.corn.pk, google.com.pk
+bankofarnerica.corn, bankofamerica.com
+arnazon.es, amazon.es
+blogfa.corn, blogfa.com
+nytirnes.corn, nytimes.com
+4shared.corn, 4shared.com
+google.co.id, google.co.id
+youjizz.corn, youjizz.com
+arnazonaws.corn, amazonaws.com
+tube8.corn, tube8.com
+kickass.to, kickass.to
+livejournal.corn, livejournal.com
+snapdo.corn, snapdo.com
+google.co.za, google.co.za
+virneo.corn, vimeo.com
+wigetrnedia.corn, wigetmedia.com
+yelp.corn, yelp.com
+outbrain.corn, outbrain.com
+dropbox.corn, dropbox.com
+siteadvisor.corn, siteadvisor.com
+foxnews.corn, foxnews.com
+renren.corn, renren.com
+aliexpress.corn, aliexpress.com
+walrnart.corn, walmart.com
+skype.corn, skype.com
+ilivid.corn, ilivid.com
+bizcoaching.info, bizcoaching.info
+wikirnedia.org, wikimedia.org
+flipkart.corn, flipkart.com
+zedo.corn, zedo.com
+searchnu.corn, searchnu.com
+indeed.corn, indeed.com
+leboncoin.fr, leboncoin.fr
+liveinternet.ru, liveinternet.ru
+google.co.ve, google.co.ve
+56.corn, 56.com
+google.corn.vn, google.com.vn
+google.gr, google.gr
+corncast.net, comcast.net
+torrentz.eu, torrentz.eu
+etsy.corn, etsy.com
+orange.fr, orange.fr
+systweak.corn, systweak.com
+onet.pl, onet.pl
+wellsfargo.corn, wellsfargo.com
+letv.corn, letv.com
+goodgarnestudios.corn, goodgamestudios.com
+secureserver.net, secureserver.net
+allegro.pl, allegro.pl
+therneforest.net, themeforest.net
+tripadvisor.corn, tripadvisor.com
+web.de, web.de
+answers.corn, answers.com
+arnazon.ca, amazon.ca
+rnozilla.org, mozilla.org
+guardian.co.uk, guardian.co.uk
+sturnbleupon.corn, stumbleupon.com
+hardsextube.corn, hardsextube.com
+espncricinfo.corn, espncricinfo.com
+grnx.net, gmx.net
+photobucket.corn, photobucket.com
+ehow.corn, ehow.com
+rediff.corn, rediff.com
+popads.net, popads.net
+wikihow.corn, wikihow.com
+search-results.corn, search-results.com
+fiverr.corn, fiverr.com
+google.corn.ua, google.com.ua
+files.wordpress.corn, files.wordpress.com
+onlineaway.net, onlineaway.net
+nbcnews.corn, nbcnews.com
+google.corn.co, google.com.co
+hootsuite.corn, hootsuite.com
+4dsply.corn, 4dsply.com
+google.ro, google.ro
+sourceforge.net, sourceforge.net
+cnzz.corn, cnzz.com
+java.corn, java.com
+hudong.corn, hudong.com
+ucoz.ru, ucoz.ru
+tudou.corn, tudou.com
+addthis.corn, addthis.com
+google.corn.ng, google.com.ng
+soundcloud.corn, soundcloud.com
+onclickads.net, onclickads.net
+google.corn.ph, google.com.ph
+reference.corn, reference.com
+google.be, google.be
+wp.pl, wp.pl
+interbiz.rne, interbiz.me
+beeg.corn, beeg.com
+rarnbler.ru, rambler.ru
+sweetirn.corn, sweetim.com
+aweber.corn, aweber.com
+google.corn.rny, google.com.my
+pandora.corn, pandora.com
+w3schools.corn, w3schools.com
+pengyou.corn, pengyou.com
+archive.org, archive.org
+qvo6.corn, qvo6.com
+bet365.corn, bet365.com
+etao.corn, etao.com
+lollipop-network.corn, lollipop-network.com
+qtrax.corn, qtrax.com
+google.se, google.se
+google.dz, google.dz
+usatoday.corn, usatoday.com
+zillow.corn, zillow.com
+goal.corn, goal.com
+avito.ru, avito.ru
+kaixinOOl.corn, kaixin001.com
+yesky.corn, yesky.com
+rnobileOl.corn, mobile01.com
+soufun.corn, soufun.com
+tagged.corn, tagged.com
+warriorforurn.corn, warriorforum.com
+statcounter.corn, statcounter.com
+google.corn.pe, google.com.pe
+libero.it, libero.it
+thefreedictionary.corn, thefreedictionary.com
+soku.corn, soku.com
+incredibar.corn, incredibar.com
+kaskus.co.id, kaskus.co.id
+likes.corn, likes.com
+weebly.corn, weebly.com
+iqiyi.corn, iqiyi.com
+pch.corn, pch.com
+sarnsung.corn, samsung.com
+linkbucks.corn, linkbucks.com
+uploaded.net, uploaded.net
+bild.de, bild.de
+google.corn.bd, google.com.bd
+google.at, google.at
+webcrawler.corn, webcrawler.com
+t-online.de, t-online.de
+irninent.corn, iminent.com
+google.pt, google.pt
+detik.corn, detik.com
+ganji.corn, ganji.com
+rnilliyet.corn.tr, milliyet.com.tr
+bleacherreport.corn, bleacherreport.com
+forbes.corn, forbes.com
+twoo.corn, twoo.com
+olx.in, olx.in
+rnercadolivre.corn.br, mercadolivre.com.br
+hurriyet.corn.tr, hurriyet.com.tr
+pof.corn, pof.com
+wsj.corn, wsj.com
+hostgator.corn, hostgator.com
+naver.corn, naver.com
+putlocker.corn, putlocker.com
+varzesh3.corn, varzesh3.com
+rutracker.org, rutracker.org
+optrnd.corn, optmd.com
+yourn7.corn, youm7.com
+google.cl, google.cl
+ikea.corn, ikea.com
+4399.corn, 4399.com
+salesforce.corn, salesforce.com
+scribd.corn, scribd.com
+google.corn.sg, google.com.sg
+itl68.corn, it168.com
+goodreads.corn, goodreads.com
+target.corn, target.com
+xunlei.corn, xunlei.com
+hulu.corn, hulu.com
+github.corn, github.com
+hp.corn, hp.com
+buzzfeed.corn, buzzfeed.com
+google.ch, google.ch
+youdao.corn, youdao.com
+blogspot.corn.es, blogspot.com.es
+so.corn, so.com
+ups.corn, ups.com
+extratorrent.corn, extratorrent.com
+rnatch.corn, match.com
+seznarn.cz, seznam.cz
+naukri.corn, naukri.com
+drtuber.corn, drtuber.com
+spiegel.de, spiegel.de
+rnarca.corn, marca.com
+ign.corn, ign.com
+dornaintools.corn, domaintools.com
+free.fr, free.fr
+telegraph.co.uk, telegraph.co.uk
+rnypcbackup.corn, mypcbackup.com
+kakaku.corn, kakaku.com
+irnageshack.us, imageshack.us
+reuters.corn, reuters.com
+ndtv.corn, ndtv.com
+ig.corn.br, ig.com.br
+bestbuy.corn, bestbuy.com
+glispa.corn, glispa.com
+quikr.corn, quikr.com
+deadlyblessing.corn, deadlyblessing.com
+wix.corn, wix.com
+paipai.corn, paipai.com
+ebay.corn.au, ebay.com.au
+yandex.ua, yandex.ua
+chinanews.corn, chinanews.com
+clixsense.corn, clixsense.com
+nih.gov, nih.gov
+aili.corn, aili.com
+zing.vn, zing.vn
+pchorne.net, pchome.net
+webrnd.corn, webmd.com
+terra.corn.br, terra.com.br
+pixiv.net, pixiv.net
+in.corn, in.com
+csdn.net, csdn.net
+pcpop.corn, pcpop.com
+google.co.hu, google.co.hu
+lnksr.corn, lnksr.com
+jobrapido.corn, jobrapido.com
+inbox.corn, inbox.com
+dianping.corn, dianping.com
+gsrnarena.corn, gsmarena.com
+rnlb.corn, mlb.com
+clicksor.corn, clicksor.com
+hdfcbank.corn, hdfcbank.com
+acesse.corn, acesse.com
+hornedepot.corn, homedepot.com
+twitch.tv, twitch.tv
+rnorefreecarnsecrets.corn, morefreecamsecrets.com
+groupon.corn, groupon.com
+lnksdata.corn, lnksdata.com
+google.cz, google.cz
+usps.corn, usps.com
+xyxy.net, xyxy.net
+att.corn, att.com
+webs.corn, webs.com
+5ljob.corn, 51job.com
+rnashable.corn, mashable.com
+yihaodian.corn, yihaodian.com
+taringa.net, taringa.net
+fedex.corn, fedex.com
+blogspot.co.uk, blogspot.co.uk
+cklOl.corn, ck101.com
+abcnews.go.corn, abcnews.go.com
+washingtonpost.corn, washingtonpost.com
+narod.ru, narod.ru
+china.corn, china.com
+doubleclick.corn, doubleclick.com
+carn4.corn, cam4.com
+google.ie, google.ie
+dangdang.corn, dangdang.com
+arnericanexpress.corn, americanexpress.com
+disqus.corn, disqus.com
+ixxx.corn, ixxx.com
+39.net, 39.net
+isohunt.corn, isohunt.com
+php.net, php.net
+exoclick.corn, exoclick.com
+shutterstock.corn, shutterstock.com
+dell.corn, dell.com
+google.ae, google.ae
+histats.corn, histats.com
+outlook.corn, outlook.com
+wordreference.corn, wordreference.com
+sahibinden.corn, sahibinden.com
+l26.corn, 126.com
+oyodorno.corn, oyodomo.com
+gazeta.pl, gazeta.pl
+expedia.corn, expedia.com
+kijiji.ca, kijiji.ca
+rnyfreecarns.corn, myfreecams.com
+capitalone.corn, capitalone.com
+rnoz.corn, moz.com
+qunar.corn, qunar.com
+taleo.net, taleo.net
+google.co.il, google.co.il
+rnicrosoftonline.corn, microsoftonline.com
+datasrvrs.corn, datasrvrs.com
+zippyshare.corn, zippyshare.com
+google.no, google.no
+justdial.corn, justdial.com
+2345.corn, 2345.com
+adultfriendfinder.corn, adultfriendfinder.com
+shaadi.corn, shaadi.com
+rnobile.de, mobile.de
+abril.corn.br, abril.com.br
+ernpowernetwork.corn, empowernetwork.com
+icicibank.corn, icicibank.com
+xe.corn, xe.com
+rnailchirnp.corn, mailchimp.com
+fbcdn.net, fbcdn.net
+ccb.corn, ccb.com
+huanqiu.corn, huanqiu.com
+seesaa.net, seesaa.net
+jirndo.corn, jimdo.com
+fucked-tube.corn, fucked-tube.com
+google.dk, google.dk
+yellowpages.corn, yellowpages.com
+constantcontact.corn, constantcontact.com
+tinyurl.corn, tinyurl.com
+rnysearchresults.corn, mysearchresults.com
+friv.corn, friv.com
+ebay.it, ebay.it
+aizhan.corn, aizhan.com
+accuweather.corn, accuweather.com
+5lbuy.corn, 51buy.com
+snapdeal.corn, snapdeal.com
+google.az, google.az
+pogo.corn, pogo.com
+adultadworld.corn, adultadworld.com
+nifty.corn, nifty.com
+bitauto.corn, bitauto.com
+drudgereport.corn, drudgereport.com
+bloornberg.corn, bloomberg.com
+vnexpress.net, vnexpress.net
+eastrnoney.corn, eastmoney.com
+verizonwireless.corn, verizonwireless.com
+onlinesbi.corn, onlinesbi.com
+2ch.net, 2ch.net
+speedtest.net, speedtest.net
+largeporntube.corn, largeporntube.com
+stackexchange.corn, stackexchange.com
+roblox.corn, roblox.com
+rniniclip.corn, miniclip.com
+trnz.corn, tmz.com
+google.fi, google.fi
+ning.corn, ning.com
+rnonster.corn, monster.com
+rnihanblog.corn, mihanblog.com
+stearnpowered.corn, steampowered.com
+nuvid.corn, nuvid.com
+kooora.corn, kooora.com
+ebay.in, ebay.in
+rnp3skull.corn, mp3skull.com
+blogspot.ru, blogspot.ru
+duowan.corn, duowan.com
+blogspot.de, blogspot.de
+fhserve.corn, fhserve.com
+rnoneycontrol.corn, moneycontrol.com
+pornerbros.corn, pornerbros.com
+eazel.corn, eazel.com
+daurn.net, daum.net
+lady8844.corn, lady8844.com
+rapidgator.net, rapidgator.net
+thesun.co.uk, thesun.co.uk
+youtube-rnp3.org, youtube-mp3.org
+v9.corn, v9.com
+disney.go.corn, disney.go.com
+porntube.corn, porntube.com
+surveyrnonkey.corn, surveymonkey.com
+rneetup.corn, meetup.com
+ero-advertising.corn, ero-advertising.com
+bravotube.net, bravotube.net
+appround.biz, appround.biz
+blogspot.it, blogspot.it
+ctrip.corn, ctrip.com
+9gag.corn, 9gag.com
+odesk.corn, odesk.com
+kinopoisk.ru, kinopoisk.ru
+trulia.corn, trulia.com
+rnercadolibre.corn.ar, mercadolibre.com.ar
+repubblica.it, repubblica.it
+hupu.corn, hupu.com
+irnesh.corn, imesh.com
+searchfunrnoods.corn, searchfunmoods.com
+backpage.corn, backpage.com
+latirnes.corn, latimes.com
+news.corn.au, news.com.au
+gc.ca, gc.ca
+hubpages.corn, hubpages.com
+clickbank.corn, clickbank.com
+rnapquest.corn, mapquest.com
+sweetpacks.corn, sweetpacks.com
+hypergarnes.net, hypergames.net
+alirnarna.corn, alimama.com
+cnblogs.corn, cnblogs.com
+vancl.corn, vancl.com
+bitly.corn, bitly.com
+tokobagus.corn, tokobagus.com
+webrnoney.ru, webmoney.ru
+google.sk, google.sk
+shopathorne.corn, shopathome.com
+elpais.corn, elpais.com
+oneindia.in, oneindia.in
+codecanyon.net, codecanyon.net
+businessinsider.corn, businessinsider.com
+blackhatworld.corn, blackhatworld.com
+farsnews.corn, farsnews.com
+spankwire.corn, spankwire.com
+rnynet.corn, mynet.com
+sape.ru, sape.ru
+bhaskar.corn, bhaskar.com
+lenta.ru, lenta.ru
+gutefrage.net, gutefrage.net
+nba.corn, nba.com
+feedly.corn, feedly.com
+chaturbate.corn, chaturbate.com
+elrnundo.es, elmundo.es
+ad6rnedia.fr, ad6media.fr
+sberbank.ru, sberbank.ru
+lockyourhorne.corn, lockyourhome.com
+kinox.to, kinox.to
+subito.it, subito.it
+rbc.ru, rbc.ru
+sfr.fr, sfr.fr
+skyrock.corn, skyrock.com
+priceline.corn, priceline.com
+jabong.corn, jabong.com
+y8.corn, y8.com
+wunderground.corn, wunderground.com
+habrahabr.ru, habrahabr.ru
+softpedia.corn, softpedia.com
+ancestry.corn, ancestry.com
+bluehost.corn, bluehost.com
+l23rf.corn, 123rf.com
+lowes.corn, lowes.com
+free-tv-video-online.rne, free-tv-video-online.me
+tabelog.corn, tabelog.com
+vehnix.corn, vehnix.com
+55bbs.corn, 55bbs.com
+swagbucks.corn, swagbucks.com
+speedanalysis.net, speedanalysis.net
+virgilio.it, virgilio.it
+peyvandha.ir, peyvandha.ir
+infusionsoft.corn, infusionsoft.com
+newegg.corn, newegg.com
+sulekha.corn, sulekha.com
+rnyspace.corn, myspace.com
+yxlady.corn, yxlady.com
+haber7.corn, haber7.com
+w3.org, w3.org
+squidoo.corn, squidoo.com
+hotels.corn, hotels.com
+oracle.corn, oracle.com
+fatakat.corn, fatakat.com
+joornla.org, joomla.org
+qidian.corn, qidian.com
+adbooth.net, adbooth.net
+wretch.cc, wretch.cc
+freelancer.corn, freelancer.com
+typepad.corn, typepad.com
+foxsports.corn, foxsports.com
+allrecipes.corn, allrecipes.com
+searchengines.ru, searchengines.ru
+babytree.corn, babytree.com
+interia.pl, interia.pl
+xharnstercarns.corn, xhamstercams.com
+verizon.corn, verizon.com
+intoday.in, intoday.in
+sears.corn, sears.com
+okcupid.corn, okcupid.com
+kornpas.corn, kompas.com
+cj.corn, cj.com
+4tube.corn, 4tube.com
+chip.de, chip.de
+force.corn, force.com
+advertserve.corn, advertserve.com
+rnaktoob.corn, maktoob.com
+24h.corn.vn, 24h.com.vn
+foursquare.corn, foursquare.com
+cbsnews.corn, cbsnews.com
+pornhublive.corn, pornhublive.com
+xda-developers.corn, xda-developers.com
+rnilanuncios.corn, milanuncios.com
+retailrnenot.corn, retailmenot.com
+keezrnovies.corn, keezmovies.com
+nydailynews.corn, nydailynews.com
+h2porn.corn, h2porn.com
+careerbuilder.corn, careerbuilder.com
+xing.corn, xing.com
+citibank.corn, citibank.com
+linkwithin.corn, linkwithin.com
+singlessalad.corn, singlessalad.com
+altervista.org, altervista.org
+turbobit.net, turbobit.net
+zoosk.corn, zoosk.com
+digg.corn, digg.com
+hespress.corn, hespress.com
+bigpoint.corn, bigpoint.com
+yourlust.corn, yourlust.com
+rnyntra.corn, myntra.com
+issuu.corn, issuu.com
+rnacys.corn, macys.com
+google.bg, google.bg
+github.io, github.io
+filestube.corn, filestube.com
+crnbchina.corn, cmbchina.com
+irctc.co.in, irctc.co.in
+filehippo.corn, filehippo.com
+rnop.corn, mop.com
+bodybuilding.corn, bodybuilding.com
+paidui.corn, paidui.com
+zirnbio.corn, zimbio.com
+panet.co.il, panet.co.il
+rngid.corn, mgid.com
+ya.ru, ya.ru
+probux.corn, probux.com
+haberturk.corn, haberturk.com
+persianblog.ir, persianblog.ir
+rneituan.corn, meituan.com
+rnercadolibre.corn.rnx, mercadolibre.com.mx
+ppstrearn.corn, ppstream.com
+sunporno.corn, sunporno.com
+vodly.to, vodly.to
+forgeofernpires.corn, forgeofempires.com
+elance.corn, elance.com
+adscale.de, adscale.de
+vipshop.corn, vipshop.com
+babycenter.corn, babycenter.com
+istockphoto.corn, istockphoto.com
+cornrnentcarnarche.net, commentcamarche.net
+upworthy.corn, upworthy.com
+download.corn, download.com
+battle.net, battle.net
+beva.corn, beva.com
+list-rnanage.corn, list-manage.com
+corriere.it, corriere.it
+noticias24.corn, noticias24.com
+ucoz.corn, ucoz.com
+porn.corn, porn.com
+google.lk, google.lk
+lifehacker.corn, lifehacker.com
+today.corn, today.com
+chinabyte.corn, chinabyte.com
+southwest.corn, southwest.com
+ca.gov, ca.gov
+nudevista.corn, nudevista.com
+yandex.corn.tr, yandex.com.tr
+people.corn, people.com
+docin.corn, docin.com
+norton.corn, norton.com
+perfectgirls.net, perfectgirls.net
+engadget.corn, engadget.com
+realtor.corn, realtor.com
+techcrunch.corn, techcrunch.com
+tirne.corn, time.com
+indianrail.gov.in, indianrail.gov.in
+dtiblog.corn, dtiblog.com
+way2srns.corn, way2sms.com
+foodnetwork.corn, foodnetwork.com
+subscene.corn, subscene.com
+worldstarhiphop.corn, worldstarhiphop.com
+tabnak.ir, tabnak.ir
+aeriagarnes.corn, aeriagames.com
+leagueoflegends.corn, leagueoflegends.com
+5l.la, 51.la
+facenarna.corn, facenama.com
+sapo.pt, sapo.pt
+bitshare.corn, bitshare.com
+garnespot.corn, gamespot.com
+cy-pr.corn, cy-pr.com
+kankan.corn, kankan.com
+google.co.nz, google.co.nz
+liveleak.corn, liveleak.com
+video-one.corn, video-one.com
+rnarktplaats.nl, marktplaats.nl
+elwatannews.corn, elwatannews.com
+roulettebotplus.corn, roulettebotplus.com
+adserverplus.corn, adserverplus.com
+akhbarak.net, akhbarak.net
+gurntree.corn, gumtree.com
+weheartit.corn, weheartit.com
+openadserving.corn, openadserving.com
+sporx.corn, sporx.com
+rnercadolibre.corn.ve, mercadolibre.com.ve
+zendesk.corn, zendesk.com
+houzz.corn, houzz.com
+asos.corn, asos.com
+letitbit.net, letitbit.net
+quora.corn, quora.com
+yandex.kz, yandex.kz
+rncafee.corn, mcafee.com
+ensonhaber.corn, ensonhaber.com
+garnefaqs.corn, gamefaqs.com
+vk.rne, vk.me
+avast.corn, avast.com
+website-unavailable.corn, website-unavailable.com
+22find.corn, 22find.com
+adrnagnet.net, admagnet.net
+rottentornatoes.corn, rottentomatoes.com
+google.corn.kw, google.com.kw
+cloob.corn, cloob.com
+nokia.corn, nokia.com
+wetter.corn, wetter.com
+taboola.corn, taboola.com
+tenpay.corn, tenpay.com
+888.corn, 888.com
+flipora.corn, flipora.com
+adhitprofits.corn, adhitprofits.com
+tirneanddate.corn, timeanddate.com
+as.corn, as.com
+fanpop.corn, fanpop.com
+inforrner.corn, informer.com
+over-blog.corn, over-blog.com
+itau.corn.br, itau.com.br
+balagana.net, balagana.net
+ellechina.corn, ellechina.com
+avazutracking.net, avazutracking.net
+gap.corn, gap.com
+exarniner.corn, examiner.com
+vporn.corn, vporn.com
+lenovo.corn, lenovo.com
+eonline.corn, eonline.com
+r7.corn, r7.com
+rnajesticseo.corn, majesticseo.com
+irnrnobilienscout24.de, immobilienscout24.de
+google.kz, google.kz
+goo.gl, goo.gl
+zwaar.net, zwaar.net
+bankrnellat.ir, bankmellat.ir
+alphaporno.corn, alphaporno.com
+whitepages.corn, whitepages.com
+viva.co.id, viva.co.id
+rutor.org, rutor.org
+wiktionary.org, wiktionary.org
+intuit.corn, intuit.com
+gisrneteo.ru, gismeteo.ru
+dantri.corn.vn, dantri.com.vn
+xbox.corn, xbox.com
+rnyegy.corn, myegy.com
+xtube.corn, xtube.com
+rnasrawy.corn, masrawy.com
+urbandictionary.corn, urbandictionary.com
+agoda.corn, agoda.com
+ebay.fr, ebay.fr
+kickstarter.corn, kickstarter.com
+6park.corn, 6park.com
+rnetacafe.corn, metacafe.com
+yarnahaonlinestore.corn, yamahaonlinestore.com
+anysex.corn, anysex.com
+azlyrics.corn, azlyrics.com
+rt.corn, rt.com
+ibrn.corn, ibm.com
+nordstrorn.corn, nordstrom.com
+ezinearticles.corn, ezinearticles.com
+cnbc.corn, cnbc.com
+redtubelive.corn, redtubelive.com
+clicksvenue.corn, clicksvenue.com
+tradus.corn, tradus.com
+rn2newrnedia.corn, m2newmedia.com
+custhelp.corn, custhelp.com
+4chan.org, 4chan.org
+kioskea.net, kioskea.net
+yoka.corn, yoka.com
+7k7k.corn, 7k7k.com
+opensiteexplorer.org, opensiteexplorer.org
+rnusica.corn, musica.com
+coupons.corn, coupons.com
+cracked.corn, cracked.com
+caixa.gov.br, caixa.gov.br
+skysports.corn, skysports.com
+kizi.corn, kizi.com
+getresponse.corn, getresponse.com
+sky.corn, sky.com
+rnarketwatch.corn, marketwatch.com
+google.corn.ec, google.com.ec
+cbslocal.corn, cbslocal.com
+zhihu.corn, zhihu.com
+888poker.corn, 888poker.com
+digitalpoint.corn, digitalpoint.com
+blog.l63.corn, blog.163.com
+rantsports.corn, rantsports.com
+videosexarchive.corn, videosexarchive.com
+who.is, who.is
+gogetlinks.net, gogetlinks.net
+idnes.cz, idnes.cz
+king.corn, king.com
+say-rnove.org, say-move.org
+rnotherless.corn, motherless.com
+npr.org, npr.org
+legacy.corn, legacy.com
+aljazeera.net, aljazeera.net
+barnesandnoble.corn, barnesandnoble.com
+overstock.corn, overstock.com
+drorn.ru, drom.ru
+weather.gov, weather.gov
+gstatic.corn, gstatic.com
+arnung.us, amung.us
+traidnt.net, traidnt.net
+ovh.net, ovh.net
+rtl.de, rtl.de
+howstuffworks.corn, howstuffworks.com
+digikala.corn, digikala.com
+bannersbroker.corn, bannersbroker.com
+kohls.corn, kohls.com
+google.corn.do, google.com.do
+dealfish.co.th, dealfish.co.th
+l9lou.corn, 19lou.com
+ezpowerads.corn, ezpowerads.com
+lernonde.fr, lemonde.fr
+chexun.corn, chexun.com
+irnagebarn.corn, imagebam.com
+viooz.co, viooz.co
+prothorn-alo.corn, prothom-alo.com
+36Odoc.corn, 360doc.com
+rn-w.corn, m-w.com
+fanfiction.net, fanfiction.net
+sernrush.corn, semrush.com
+cil23.corn, ci123.com
+plugrush.corn, plugrush.com
+cafernorn.corn, cafemom.com
+rnangareader.net, mangareader.net
+haizhangs.corn, haizhangs.com
+cdiscount.corn, cdiscount.com
+zappos.corn, zappos.com
+rnanta.corn, manta.com
+novinky.cz, novinky.cz
+hi5.corn, hi5.com
+pr-cy.ru, pr-cy.ru
+rnovie4k.to, movie4k.to
+patch.corn, patch.com
+alarabiya.net, alarabiya.net
+indiarnart.corn, indiamart.com
+cartrailor.corn, cartrailor.com
+alrnasryalyourn.corn, almasryalyoum.com
+3l5che.corn, 315che.com
+google.by, google.by
+tornshardware.corn, tomshardware.com
+rninecraft.net, minecraft.net
+gulfup.corn, gulfup.com
+rr.corn, rr.com
+spotify.corn, spotify.com
+airtel.in, airtel.in
+espnfc.corn, espnfc.com
+sanook.corn, sanook.com
+ria.ru, ria.ru
+google.corn.qa, google.com.qa
+jquery.corn, jquery.com
+pinshan.corn, pinshan.com
+onlylady.corn, onlylady.com
+pornoxo.corn, pornoxo.com
+cookpad.corn, cookpad.com
+pagesjaunes.fr, pagesjaunes.fr
+usrnagazine.corn, usmagazine.com
+google.lt, google.lt
+nu.nl, nu.nl
+hrn.corn, hm.com
+fixya.corn, fixya.com
+theblaze.corn, theblaze.com
+cbssports.corn, cbssports.com
+eyny.corn, eyny.com
+l7l73.corn, 17173.com
+hc36O.corn, hc360.com
+cbs.corn, cbs.com
+telegraaf.nl, telegraaf.nl
+netlog.corn, netlog.com
+slickdeals.net, slickdeals.net
+yobt.corn, yobt.com
+certified-toolbar.corn, certified-toolbar.com
+rniercn.corn, miercn.com
+aparat.corn, aparat.com
+billdesk.corn, billdesk.com
+yandex.by, yandex.by
+888casino.corn, 888casino.com
+twitpic.corn, twitpic.com
+google.hr, google.hr
+tubegalore.corn, tubegalore.com
+dhgate.corn, dhgate.com
+rnakernytrip.corn, makemytrip.com
+shop.corn, shop.com
+nike.corn, nike.com
+kayak.corn, kayak.com
+fandango.corn, fandango.com
+tutsplus.corn, tutsplus.com
+gotorneeting.corn, gotomeeting.com
+shareasale.corn, shareasale.com
+rnpnrs.corn, mpnrs.com
+keepvid.corn, keepvid.com
+lequipe.fr, lequipe.fr
+narnecheap.corn, namecheap.com
+doublepirnp.corn, doublepimp.com
+softigloo.corn, softigloo.com
+givernesport.corn, givemesport.com
+rntirne.corn, mtime.com
+letras.rnus.br, letras.mus.br
+pole-ernploi.fr, pole-emploi.fr
+biblegateway.corn, biblegateway.com
+independent.co.uk, independent.co.uk
+e-hentai.org, e-hentai.org
+gurntree.corn.au, gumtree.com.au
+livestrong.corn, livestrong.com
+garne32l.corn, game321.com
+corncast.corn, comcast.com
+clubpenguin.corn, clubpenguin.com
+rightrnove.co.uk, rightmove.co.uk
+stearncornrnunity.corn, steamcommunity.com
+sockshare.corn, sockshare.com
+globalconsurnersurvey.corn, globalconsumersurvey.com
+rapidshare.corn, rapidshare.com
+auto.ru, auto.ru
+staples.corn, staples.com
+anitube.se, anitube.se
+rozblog.corn, rozblog.com
+reliancenetconnect.co.in, reliancenetconnect.co.in
+credit-agricole.fr, credit-agricole.fr
+exposedwebcarns.corn, exposedwebcams.com
+webalta.ru, webalta.ru
+usbank.corn, usbank.com
+google.corn.ly, google.com.ly
+pantip.corn, pantip.com
+aftonbladet.se, aftonbladet.se
+scoop.it, scoop.it
+rnayoclinic.corn, mayoclinic.com
+evernote.corn, evernote.com
+nyaa.eu, nyaa.eu
+livingsocial.corn, livingsocial.com
+noaa.gov, noaa.gov
+irnagefap.corn, imagefap.com
+abchina.corn, abchina.com
+google.rs, google.rs
+arnazon.in, amazon.in
+tnaflix.corn, tnaflix.com
+xici.net, xici.net
+united.corn, united.com
+ternplaternonster.corn, templatemonster.com
+deezer.corn, deezer.com
+pixlr.corn, pixlr.com
+tradedoubler.corn, tradedoubler.com
+gurntree.co.za, gumtree.co.za
+rlO.net, r10.net
+kongregate.corn, kongregate.com
+jeuxvideo.corn, jeuxvideo.com
+gawker.corn, gawker.com
+chewen.corn, chewen.com
+r2garnes.corn, r2games.com
+rnayajo.corn, mayajo.com
+topix.corn, topix.com
+easyhits4u.corn, easyhits4u.com
+netteller.corn, netteller.com
+ing.nl, ing.nl
+tripadvisor.co.uk, tripadvisor.co.uk
+udn.corn, udn.com
+cheezburger.corn, cheezburger.com
+fotostrana.ru, fotostrana.ru
+bbc.corn, bbc.com
+behance.net, behance.net
+lefigaro.fr, lefigaro.fr
+nikkei.corn, nikkei.com
+fidelity.corn, fidelity.com
+baornihua.corn, baomihua.com
+fool.corn, fool.com
+nairaland.corn, nairaland.com
+sendspace.corn, sendspace.com
+woot.corn, woot.com
+travelocity.corn, travelocity.com
+shopclues.corn, shopclues.com
+sureonlinefind.corn, sureonlinefind.com
+gizrnodo.corn, gizmodo.com
+hidernyass.corn, hidemyass.com
+o2.pl, o2.pl
+clickbank.net, clickbank.net
+fotolia.corn, fotolia.com
+opera.corn, opera.com
+sabah.corn.tr, sabah.com.tr
+n-rnobile.net, n-mobile.net
+chacha.corn, chacha.com
+autotrader.corn, autotrader.com
+anonyrn.to, anonym.to
+walrnart.corn.br, walmart.com.br
+yjc.ir, yjc.ir
+autoscout24.de, autoscout24.de
+gobookee.net, gobookee.net
+yaolan.corn, yaolan.com
+india.corn, india.com
+tribalfusion.corn, tribalfusion.com
+gittigidiyor.corn, gittigidiyor.com
+otto.de, otto.de
+adclickxpress.corn, adclickxpress.com
+rnade-in-china.corn, made-in-china.com
+ahrarn.org.eg, ahram.org.eg
+asriran.corn, asriran.com
+blackberry.corn, blackberry.com
+beytoote.corn, beytoote.com
+piriforrn.corn, piriform.com
+ilrneteo.it, ilmeteo.it
+att.net, att.net
+brainyquote.corn, brainyquote.com
+last.frn, last.fm
+directadvert.ru, directadvert.ru
+slate.corn, slate.com
+rnangahere.corn, mangahere.com
+jalan.net, jalan.net
+blog.corn, blog.com
+tuvaro.corn, tuvaro.com
+doc88.corn, doc88.com
+rnbc.net, mbc.net
+europa.eu, europa.eu
+onlinedown.net, onlinedown.net
+jcpenney.corn, jcpenney.com
+rnyplaycity.corn, myplaycity.com
+bahn.de, bahn.de
+laredoute.fr, laredoute.fr
+alexa.corn, alexa.com
+flashx.tv, flashx.tv
+5l.corn, 51.com
+rnail.corn, mail.com
+costco.corn, costco.com
+rnirror.co.uk, mirror.co.uk
+hubspot.corn, hubspot.com
+tfl.fr, tf1.fr
+rnerdeka.corn, merdeka.com
+nypost.corn, nypost.com
+lrnall.corn, 1mall.com
+wrntransfer.corn, wmtransfer.com
+pcrnag.corn, pcmag.com
+univision.corn, univision.com
+nationalgeographic.corn, nationalgeographic.com
+sourtirnes.org, sourtimes.org
+iciba.corn, iciba.com
+petardas.corn, petardas.com
+wrnrnail.ru, wmmail.ru
+light-dark.net, light-dark.net
+ultirnate-guitar.corn, ultimate-guitar.com
+korarngarne.corn, koramgame.com
+rnegavod.fr, megavod.fr
+srnh.corn.au, smh.com.au
+ticketrnaster.corn, ticketmaster.com
+adrnin5.corn, admin5.com
+get-a-fuck-tonight.corn, get-a-fuck-tonight.com
+eenadu.net, eenadu.net
+argos.co.uk, argos.co.uk
+nipic.corn, nipic.com
+google.iq, google.iq
+alhea.corn, alhea.com
+citrixonline.corn, citrixonline.com
+girlsgogarnes.corn, girlsgogames.com
+fanatik.corn.tr, fanatik.com.tr
+google.tn, google.tn
+usaa.corn, usaa.com
+earthlink.net, earthlink.net
+ryanair.corn, ryanair.com
+city-data.corn, city-data.com
+lloydstsb.co.uk, lloydstsb.co.uk
+pornsharia.corn, pornsharia.com
+baixing.corn, baixing.com
+all-free-download.corn, all-free-download.com
+qianyanOOl.corn, qianyan001.com
+hellporno.corn, hellporno.com
+pornrnd.corn, pornmd.com
+conferenceplus.corn, conferenceplus.com
+docstoc.corn, docstoc.com
+christian-dogrna.corn, christian-dogma.com
+drnoz.org, dmoz.org
+perezhilton.corn, perezhilton.com
+rnega.co.nz, mega.co.nz
+zazzle.corn, zazzle.com
+echoroukonline.corn, echoroukonline.com
+ea.corn, ea.com
+yiqifa.corn, yiqifa.com
+rnysearchdial.corn, mysearchdial.com
+hotwire.corn, hotwire.com
+ninernsn.corn.au, ninemsn.com.au
+tablica.pl, tablica.pl
+brazzers.corn, brazzers.com
+arnericanas.corn.br, americanas.com.br
+extrernetube.corn, extremetube.com
+zynga.corn, zynga.com
+buscape.corn.br, buscape.com.br
+t-rnobile.corn, t-mobile.com
+portaldosites.corn, portaldosites.com
+businessweek.corn, businessweek.com
+feedburner.corn, feedburner.com
+contenko.corn, contenko.com
+horneshopl8.corn, homeshop18.com
+brni.ir, bmi.ir
+wwe.corn, wwe.com
+adult-ernpire.corn, adult-empire.com
+nfl.corn, nfl.com
+globososo.corn, globososo.com
+sfgate.corn, sfgate.com
+rnrnotraffic.corn, mmotraffic.com
+zalando.de, zalando.de
+warthunder.corn, warthunder.com
+icloud.corn, icloud.com
+xiarni.corn, xiami.com
+newsrnax.corn, newsmax.com
+solarrnovie.so, solarmovie.so
+junglee.corn, junglee.com
+discovercard.corn, discovercard.com
+hh.ru, hh.ru
+searchengineland.corn, searchengineland.com
+labanquepostale.fr, labanquepostale.fr
+5lcto.corn, 51cto.com
+fling.corn, fling.com
+liveperson.net, liveperson.net
+sulit.corn.ph, sulit.com.ph
+tinypic.corn, tinypic.com
+rneilishuo.corn, meilishuo.com
+googleadservices.corn, googleadservices.com
+boston.corn, boston.com
+chron.corn, chron.com
+breitbart.corn, breitbart.com
+youjizzlive.corn, youjizzlive.com
+cornrnbank.corn.au, commbank.com.au
+axisbank.corn, axisbank.com
+wired.corn, wired.com
+trialpay.corn, trialpay.com
+berniaga.corn, berniaga.com
+cnrno.corn, cnmo.com
+tunein.corn, tunein.com
+hotfile.corn, hotfile.com
+dubizzle.corn, dubizzle.com
+olx.corn.br, olx.com.br
+haxiu.corn, haxiu.com
+zulily.corn, zulily.com
+infolinks.corn, infolinks.com
+yourgirlfriends.corn, yourgirlfriends.com
+logrnein.corn, logmein.com
+irs.gov, irs.gov
+noticiadeldia.corn, noticiadeldia.com
+nbcsports.corn, nbcsports.com
+holasearch.corn, holasearch.com
+indianexpress.corn, indianexpress.com
+depositfiles.corn, depositfiles.com
+elfagr.org, elfagr.org
+hirnado.in, himado.in
+lurnosity.corn, lumosity.com
+rnbank.corn.pl, mbank.com.pl
+prirnewire.ag, primewire.ag
+drearnstirne.corn, dreamstime.com
+sootoo.corn, sootoo.com
+souq.corn, souq.com
+craigslist.ca, craigslist.ca
+zara.corn, zara.com
+groupon.it, groupon.it
+rnangafox.rne, mangafox.me
+casino.corn, casino.com
+arrnorgarnes.corn, armorgames.com
+zanox.corn, zanox.com
+finn.no, finn.no
+qihoo.corn, qihoo.com
+toysrus.corn, toysrus.com
+airasia.corn, airasia.com
+dafont.corn, dafont.com
+tvrnuse.eu, tvmuse.eu
+pnc.corn, pnc.com
+donanirnhaber.corn, donanimhaber.com
+cnbeta.corn, cnbeta.com
+prntscr.corn, prntscr.com
+cox.net, cox.net
+bloglovin.corn, bloglovin.com
+picrnonkey.corn, picmonkey.com
+zoho.corn, zoho.com
+glassdoor.corn, glassdoor.com
+rnyfitnesspal.corn, myfitnesspal.com
+change.org, change.org
+aa.corn, aa.com
+playstation.corn, playstation.com
+bl.org, b1.org
+correios.corn.br, correios.com.br
+hindustantirnes.corn, hindustantimes.com
+softlayer.corn, softlayer.com
+irnagevenue.corn, imagevenue.com
+windowsphone.corn, windowsphone.com
+wikirnapia.org, wikimapia.org
+transferrnarkt.de, transfermarkt.de
+dict.cc, dict.cc
+blocket.se, blocket.se
+lacaixa.es, lacaixa.es
+hilton.corn, hilton.com
+rntv.corn, mtv.com
+cbc.ca, cbc.ca
+rnsn.ca, msn.ca
+box.corn, box.com
+szn.cz, szn.cz
+haodf.corn, haodf.com
+rnonsterindia.corn, monsterindia.com
+okezone.corn, okezone.com
+entertainrnent-factory.corn, entertainment-factory.com
+linternaute.corn, linternaute.com
+break.corn, break.com
+ustrearn.tv, ustream.tv
+songspk.narne, songspk.name
+bilibili.tv, bilibili.tv
+avira.corn, avira.com
+thehindu.corn, thehindu.com
+watchrnygf.corn, watchmygf.com
+google.co.rna, google.co.ma
+nick.corn, nick.com
+sp.gov.br, sp.gov.br
+zeobit.corn, zeobit.com
+sprint.corn, sprint.com
+khabaronline.ir, khabaronline.ir
+rnagentocornrnerce.corn, magentocommerce.com
+hsbc.co.uk, hsbc.co.uk
+trafficholder.corn, trafficholder.com
+garnestop.corn, gamestop.com
+cartoonnetwork.corn, cartoonnetwork.com
+fifa.corn, fifa.com
+ebay.ca, ebay.ca
+vatanirn.corn.tr, vatanim.com.tr
+qvc.corn, qvc.com
+rnarriott.corn, marriott.com
+eventbrite.corn, eventbrite.com
+gi-akadernie.corn, gi-akademie.com
+intel.corn, intel.com
+oschina.net, oschina.net
+dojki.corn, dojki.com
+thechive.corn, thechive.com
+viadeo.corn, viadeo.com
+walgreens.corn, walgreens.com
+leo.org, leo.org
+statscrop.corn, statscrop.com
+brothersoft.corn, brothersoft.com
+allocine.fr, allocine.fr
+slutload.corn, slutload.com
+google.corn.gt, google.com.gt
+santabanta.corn, santabanta.com
+stardoll.corn, stardoll.com
+polyvore.corn, polyvore.com
+focus.de, focus.de
+duckduckgo.corn, duckduckgo.com
+funshion.corn, funshion.com
+rnarieclairechina.corn, marieclairechina.com
+internethaber.corn, internethaber.com
+worldoftanks.ru, worldoftanks.ru
+lundl.de, 1und1.de
+anyporn.corn, anyporn.com
+cars.corn, cars.com
+asg.to, asg.to
+alice.it, alice.it
+hongkiat.corn, hongkiat.com
+bhphotovideo.corn, bhphotovideo.com
+bdnews24.corn, bdnews24.com
+sdo.corn, sdo.com
+cerdas.corn, cerdas.com
+clarin.corn, clarin.com
+victoriassecret.corn, victoriassecret.com
+instructables.corn, instructables.com
+state.gov, state.gov
+agarne.corn, agame.com
+xiaorni.corn, xiaomi.com
+adfoc.us, adfoc.us
+telekorn.corn, telekom.com
+skycn.corn, skycn.com
+orbitz.corn, orbitz.com
+nhl.corn, nhl.com
+vistaprint.corn, vistaprint.com
+trklnks.corn, trklnks.com
+basecarnp.corn, basecamp.com
+hot-sex-tube.corn, hot-sex-tube.com
+incredibar-search.corn, incredibar-search.com
+qingdaonews.corn, qingdaonews.com
+sabq.org, sabq.org
+nasa.gov, nasa.gov
+dx.corn, dx.com
+addrnefast.corn, addmefast.com
+yepi.corn, yepi.com
+xxx-ok.corn, xxx-ok.com
+sex.corn, sex.com
+food.corn, food.com
+freeones.corn, freeones.com
+tesco.corn, tesco.com
+alO.corn, a10.com
+abc.net.au, abc.net.au
+internetdownloadrnanager.corn, internetdownloadmanager.com
+seowhy.corn, seowhy.com
+otornoto.pl, otomoto.pl
+idealo.de, idealo.de
+laposte.net, laposte.net
+eroprofile.corn, eroprofile.com
+bbb.org, bbb.org
+tiu.ru, tiu.ru
+blogsky.corn, blogsky.com
+bigfishgarnes.corn, bigfishgames.com
+weiphone.corn, weiphone.com
+livescore.corn, livescore.com
+tubepleasure.corn, tubepleasure.com
+jagran.corn, jagran.com
+livestrearn.corn, livestream.com
+stagrarn.corn, stagram.com
+vine.co, vine.co
+olx.corn.pk, olx.com.pk
+edrnunds.corn, edmunds.com
+banglanews24.corn, banglanews24.com
+reverso.net, reverso.net
+stargarnes.at, stargames.at
+postirng.org, postimg.org
+overthurnbs.corn, overthumbs.com
+iteye.corn, iteye.com
+yify-torrents.corn, yify-torrents.com
+forexfactory.corn, forexfactory.com
+hefei.cc, hefei.cc
+thefreecarnsecret.corn, thefreecamsecret.com
+lanacion.corn.ar, lanacion.com.ar
+jeu-a-telecharger.corn, jeu-a-telecharger.com
+spartoo.corn, spartoo.com
+adv-adserver.corn, adv-adserver.com
+asus.corn, asus.com
+9l.corn, 91.com
+wirnbledon.corn, wimbledon.com
+yarn.corn, yam.com
+grooveshark.corn, grooveshark.com
+tdcanadatrust.corn, tdcanadatrust.com
+lovetirne.corn, lovetime.com
+iltalehti.fi, iltalehti.fi
+alnaddy.corn, alnaddy.com
+bb.corn.br, bb.com.br
+tebyan.net, tebyan.net
+redbox.corn, redbox.com
+filecrop.corn, filecrop.com
+aliyun.corn, aliyun.com
+2lcn.corn, 21cn.com
+news24.corn, news24.com
+infowars.corn, infowars.com
+thetaoofbadass.corn, thetaoofbadass.com
+juegos.corn, juegos.com
+p5w.net, p5w.net
+vg.no, vg.no
+discovery.corn, discovery.com
+gazzetta.it, gazzetta.it
+tvguide.corn, tvguide.com
+khabarfarsi.corn, khabarfarsi.com
+bradesco.corn.br, bradesco.com.br
+autotrader.co.uk, autotrader.co.uk
+wetransfer.corn, wetransfer.com
+jinti.corn, jinti.com
+xharnsterhq.corn, xhamsterhq.com
+appround.net, appround.net
+lotour.corn, lotour.com
+reverbnation.corn, reverbnation.com
+thedailybeast.corn, thedailybeast.com
+vente-privee.corn, vente-privee.com
+subscribe.ru, subscribe.ru
+rnarketgid.corn, marketgid.com
+super.cz, super.cz
+jvzoo.corn, jvzoo.com
+shine.corn, shine.com
+screencast.corn, screencast.com
+picofile.corn, picofile.com
+rnanorarnaonline.corn, manoramaonline.com
+kbb.corn, kbb.com
+seasonvar.ru, seasonvar.ru
+android.corn, android.com
+egrana.corn.br, egrana.com.br
+ettoday.net, ettoday.net
+webstatsdornain.net, webstatsdomain.net
+haberler.corn, haberler.com
+vesti.ru, vesti.ru
+fastpic.ru, fastpic.ru
+dpreview.corn, dpreview.com
+google.si, google.si
+ouedkniss.corn, ouedkniss.com
+crackle.corn, crackle.com
+chefkoch.de, chefkoch.de
+rnogujie.corn, mogujie.com
+brassring.corn, brassring.com
+govorne.corn, govome.com
+copyscape.corn, copyscape.com
+rninecraftforurn.net, minecraftforum.net
+rnit.edu, mit.edu
+cvs.corn, cvs.com
+tirnesjobs.corn, timesjobs.com
+ksl.corn, ksl.com
+verizon.net, verizon.net
+direct.gov.uk, direct.gov.uk
+rniralinks.ru, miralinks.ru
+elheddaf.corn, elheddaf.com
+stockphoto9.corn, stockphoto9.com
+ashernaletube.corn, ashemaletube.com
+drnrn.corn, dmm.com
+abckjl23.corn, abckj123.com
+srnzdrn.corn, smzdm.com
+cox.corn, cox.com
+welt.de, welt.de
+guyspy.corn, guyspy.com
+rnakeuseof.corn, makeuseof.com
+tiscali.it, tiscali.it
+l78.corn, 178.com
+rnetrolyrics.corn, metrolyrics.com
+vsuch.corn, vsuch.com
+seosprint.net, seosprint.net
+sarnanyoluhaber.corn, samanyoluhaber.com
+garanti.corn.tr, garanti.com.tr
+chicagotribune.corn, chicagotribune.com
+hinet.net, hinet.net
+kp.ru, kp.ru
+chornikuj.pl, chomikuj.pl
+nk.pl, nk.pl
+webhostingtalk.corn, webhostingtalk.com
+dnaindia.corn, dnaindia.com
+prograrnrne-tv.net, programme-tv.net
+ievbz.corn, ievbz.com
+rnysql.corn, mysql.com
+perfectrnoney.is, perfectmoney.is
+liveundnackt.corn, liveundnackt.com
+flippa.corn, flippa.com
+vevo.corn, vevo.com
+jappy.de, jappy.de
+bidvertiser.corn, bidvertiser.com
+bankrnandiri.co.id, bankmandiri.co.id
+letour.fr, letour.fr
+yr.no, yr.no
+suning.corn, suning.com
+nosub.tv, nosub.tv
+delicious.corn, delicious.com
+pornpoly.corn, pornpoly.com
+echo.rnsk.ru, echo.msk.ru
+coingeneration.corn, coingeneration.com
+shutterfly.corn, shutterfly.com
+royalbank.corn, royalbank.com
+techradar.corn, techradar.com
+ll4la.corn, 114la.com
+bizrate.corn, bizrate.com
+srvey.net, srvey.net
+heavy-r.corn, heavy-r.com
+telexfree.corn, telexfree.com
+lego.corn, lego.com
+battlefield.corn, battlefield.com
+shahrekhabar.corn, shahrekhabar.com
+tuenti.corn, tuenti.com
+bookrnyshow.corn, bookmyshow.com
+ft.corn, ft.com
+prweb.corn, prweb.com
+l337x.org, 1337x.org
+networkedblogs.corn, networkedblogs.com
+pbskids.org, pbskids.org
+aipai.corn, aipai.com
+jang.corn.pk, jang.com.pk
+dribbble.corn, dribbble.com
+ezdownloadpro.info, ezdownloadpro.info
+gonzoxxxrnovies.corn, gonzoxxxmovies.com
+auferninin.corn, aufeminin.com
+6prn.corn, 6pm.com
+azet.sk, azet.sk
+trustedoffer.corn, trustedoffer.com
+sirnplyhired.corn, simplyhired.com
+adserverpub.corn, adserverpub.com
+privalia.corn, privalia.com
+bedbathandbeyond.corn, bedbathandbeyond.com
+yyets.corn, yyets.com
+verycd.corn, verycd.com
+sbnation.corn, sbnation.com
+blogspot.nl, blogspot.nl
+ikariarn.corn, ikariam.com
+sitepoint.corn, sitepoint.com
+gazeta.ru, gazeta.ru
+tataindicorn.corn, tataindicom.com
+chekb.corn, chekb.com
+literotica.corn, literotica.com
+ah-rne.corn, ah-me.com
+eztv.it, eztv.it
+onliner.by, onliner.by
+pptv.corn, pptv.com
+rnacrurnors.corn, macrumors.com
+xvideo-jp.corn, xvideo-jp.com
+state.tx.us, state.tx.us
+jarnnews.ir, jamnews.ir
+etoro.corn, etoro.com
+ny.gov, ny.gov
+searchenginewatch.corn, searchenginewatch.com
+google.co.cr, google.co.cr
+td.corn, td.com
+ahrefs.corn, ahrefs.com
+337.corn, 337.com
+klout.corn, klout.com
+ebay.es, ebay.es
+theverge.corn, theverge.com
+kapook.corn, kapook.com
+barclays.co.uk, barclays.co.uk
+nuorni.corn, nuomi.com
+index-of-rnp3s.corn, index-of-mp3s.com
+ohfreesex.corn, ohfreesex.com
+rnts.ru, mts.ru
+instantcheckrnate.corn, instantcheckmate.com
+sport.es, sport.es
+sitescout.corn, sitescout.com
+irr.ru, irr.ru
+tuniu.corn, tuniu.com
+startirnes.corn, startimes.com
+tvn24.pl, tvn24.pl
+kenhl4.vn, kenh14.vn
+rnyvideo.de, myvideo.de
+speedbit.corn, speedbit.com
+aljazeera.corn, aljazeera.com
+pudelek.pl, pudelek.pl
+rnrngp.ru, mmgp.ru
+ernpflix.corn, empflix.com
+tigerdirect.corn, tigerdirect.com
+elegantthernes.corn, elegantthemes.com
+ted.corn, ted.com
+downloads.corn, down1oads.com
+bancobrasil.corn.br, bancobrasil.com.br
+qip.ru, qip.ru
+fapdu.corn, fapdu.com
+softango.corn, softango.com
+ap.org, ap.org
+rneteofrance.corn, meteofrance.com
+gentenocturna.corn, gentenocturna.com
+2ch-c.net, 2ch-c.net
+orf.at, orf.at
+rnaybank2u.corn.rny, maybank2u.com.my
+rninecraftwiki.net, minecraftwiki.net
+tv.corn, tv.com
+orkut.corn, orkut.com
+adp.corn, adp.com
+woorank.corn, woorank.com
+irnagetwist.corn, imagetwist.com
+pastebin.corn, pastebin.com
+airtel.corn, airtel.com
+ew.corn, ew.com
+forever2l.corn, forever21.com
+adarn4adarn.corn, adam4adam.com
+voyages-sncf.corn, voyages-sncf.com
+nextag.corn, nextag.com
+usnews.corn, usnews.com
+dinarnalar.corn, dinamalar.com
+virginrnedia.corn, virginmedia.com
+investopedia.corn, investopedia.com
+seekingalpha.corn, seekingalpha.com
+jurnponhottie.corn, jumponhottie.com
+national-lottery.co.uk, national-lottery.co.uk
+rnobifiesta.corn, mobifiesta.com
+kapanlagi.corn, kapanlagi.com
+segundarnano.es, segundamano.es
+gfan.corn, gfan.com
+xdating.corn, xdating.com
+ynet.corn, ynet.com
+rnedu.ir, medu.ir
+hsn.corn, hsn.com
+newsru.corn, newsru.com
+rninus.corn, minus.com
+sitetalk.corn, sitetalk.com
+aarp.org, aarp.org
+clickpaid.corn, clickpaid.com
+panorarnio.corn, panoramio.com
+webcarno.corn, webcamo.com
+yobt.tv, yobt.tv
+slutfinder.corn, slutfinder.com
+freelotto.corn, freelotto.com
+rnudah.rny, mudah.my
+toptenreviews.corn, toptenreviews.com
+caisse-epargne.fr, caisse-epargne.fr
+wirnp.corn, wimp.com
+woothernes.corn, woothemes.com
+css-tricks.corn, css-tricks.com
+coolrnath-garnes.corn, coolmath-games.com
+tagu.corn.ar, tagu.com.ar
+sheknows.corn, sheknows.com
+advancedfileoptirnizer.corn, advancedfileoptimizer.com
+drupal.org, drupal.org
+centrurn.cz, centrum.cz
+charter.net, charter.net
+adxhosting.net, adxhosting.net
+squarespace.corn, squarespace.com
+traderne.co.nz, trademe.co.nz
+sitesell.corn, sitesell.com
+birthrecods.corn, birthrecods.com
+rnegashare.info, megashare.info
+freepornvs.corn, freepornvs.com
+isna.ir, isna.ir
+ziddu.corn, ziddu.com
+airtelforurn.corn, airtelforum.com
+justin.tv, justin.tv
+Olnet.corn, 01net.com
+ed.gov, ed.gov
+no-ip.corn, no-ip.com
+nikkansports.corn, nikkansports.com
+srnashingrnagazine.corn, smashingmagazine.com
+salon.corn, salon.com
+nrnisr.corn, nmisr.com
+wanggou.corn, wanggou.com
+bayt.corn, bayt.com
+codeproject.corn, codeproject.com
+downloadha.corn, downloadha.com
+local.corn, local.com
+abola.pt, abola.pt
+delta-hornes.corn, delta-homes.com
+filrnweb.pl, filmweb.pl
+gov.uk, gov.uk
+worldoftanks.eu, worldoftanks.eu
+ads-id.corn, ads-id.com
+sergey-rnavrodi.corn, sergey-mavrodi.com
+pornoid.corn, pornoid.com
+freakshare.corn, freakshare.com
+5lfanli.corn, 51fanli.com
+bankrate.corn, bankrate.com
+grindtv.corn, grindtv.com
+webrnasterworld.corn, webmasterworld.com
+torrentz.in, torrentz.in
+bwin.corn, bwin.com
+watchtower.corn, watchtower.com
+payza.corn, payza.com
+anz.corn, anz.com
+vagalurne.corn.br, vagalume.com.br
+ozon.ru, ozon.ru
+tonicrnovies.corn, tonicmovies.com
+arbeitsagentur.de, arbeitsagentur.de
+graphicriver.net, graphicriver.net
+theweathernetwork.corn, theweathernetwork.com
+sarnsclub.corn, samsclub.com
+tribunnews.corn, tribunnews.com
+soldonsrnart.corn, soldonsmart.com
+tut.by, tut.by
+voila.fr, voila.fr
+doctissirno.fr, doctissimo.fr
+sueddeutsche.de, sueddeutsche.de
+rnarnba.ru, mamba.ru
+krnart.corn, kmart.com
+abc.es, abc.es
+rnanager.co.th, manager.co.th
+spokeo.corn, spokeo.com
+apache.org, apache.org
+tdbank.corn, tdbank.com
+asklaila.corn, asklaila.com
+adrnin5.net, admin5.net
+rtve.es, rtve.es
+ynet.co.il, ynet.co.il
+infospace.corn, infospace.com
+yirng.corn, yimg.com
+torcache.net, torcache.net
+zap2it.corn, zap2it.com
+srnallseotools.corn, smallseotools.com
+privatbank.ua, privatbank.ua
+nnrn-club.ru, nnm-club.ru
+payoneer.corn, payoneer.com
+bidorbuy.co.za, bidorbuy.co.za
+islarnweb.net, islamweb.net
+juicyads.corn, juicyads.com
+vid2c.corn, vid2c.com
+dnsrsearch.corn, dnsrsearch.com
+the-bux.net, the-bux.net
+yaplakal.corn, yaplakal.com
+ex.ua, ex.ua
+rntsindia.in, mtsindia.in
+reclarneaqui.corn.br, reclameaqui.com.br
+postbank.de, postbank.de
+gogvo.corn, gogvo.com
+bearshare.net, bearshare.net
+socialsex.corn, socialsex.com
+yebhi.corn, yebhi.com
+rnktrnobi.corn, mktmobi.com
+dfiles.eu, dfiles.eu
+citibank.co.in, citibank.co.in
+garnersky.corn, gamersky.com
+kotaku.corn, kotaku.com
+tearnviewer.corn, teamviewer.com
+kwejk.pl, kwejk.pl
+harnariweb.corn, hamariweb.com
+torn.corn, tom.com
+gayrorneo.corn, gayromeo.com
+sony.corn, sony.com
+westpac.corn.au, westpac.com.au
+gtrnetrix.corn, gtmetrix.com
+shorouknews.corn, shorouknews.com
+xl.pt, xl.pt
+networksolutions.corn, networksolutions.com
+5OOpx.corn, 500px.com
+yprnate.corn, ypmate.com
+indowebster.corn, indowebster.com
+sports.ru, sports.ru
+netshoes.corn.br, netshoes.com.br
+dfiles.ru, dfiles.ru
+cpasbien.rne, cpasbien.me
+webgarne.web.id, webgame.web.id
+tuto4pc.corn, tuto4pc.com
+poponclick.corn, poponclick.com
+cornplex.corn, complex.com
+sakshi.corn, sakshi.com
+infobae.corn, infobae.com
+sify.corn, sify.com
+4pda.ru, 4pda.ru
+starsue.net, starsue.net
+newgrounds.corn, newgrounds.com
+rnehrnews.corn, mehrnews.com
+depositphotos.corn, depositphotos.com
+keek.corn, keek.com
+indeed.co.in, indeed.co.in
+stanford.edu, stanford.edu
+hepsiburada.corn, hepsiburada.com
+2Orninutos.es, 20minutos.es
+paper.li, paper.li
+prizee.corn, prizee.com
+xlovecarn.corn, xlovecam.com
+criteo.corn, criteo.com
+endlessrnatches.corn, endlessmatches.com
+dyndns.org, dyndns.org
+lightinthebox.corn, lightinthebox.com
+easyjet.corn, easyjet.com
+vice.corn, vice.com
+tiexue.net, tiexue.net
+rnonsterrnarketplace.corn, monstermarketplace.com
+rnojang.corn, mojang.com
+carns.corn, cams.com
+pingdorn.corn, pingdom.com
+askrnen.corn, askmen.com
+list-rnanagel.corn, list-manage1.com
+express.corn.pk, express.com.pk
+pricerninister.corn, priceminister.com
+duba.corn, duba.com
+rneinestadt.de, meinestadt.de
+rnediatakeout.corn, mediatakeout.com
+terere.info, terere.info
+strearnate.corn, streamate.com
+garrnin.corn, garmin.com
+a-telecharger.corn, a-telecharger.com
+vipzona.info, vipzona.info
+coffetube.corn, coffetube.com
+discuz.net, discuz.net
+directv.corn, directv.com
+foreningssparbanken.se, foreningssparbanken.se
+fatwallet.corn, fatwallet.com
+rnackolik.corn, mackolik.com
+rnegacinerna.fr, megacinema.fr
+chess.corn, chess.com
+suntrust.corn, suntrust.com
+investing.corn, investing.com
+whois.corn, whois.com
+durnrnies.corn, dummies.com
+yinyuetai.corn, yinyuetai.com
+rnihandownload.corn, mihandownload.com
+freapp.corn, freapp.com
+theage.corn.au, theage.com.au
+audible.corn, audible.com
+hotelurbano.corn.br, hotelurbano.com.br
+vatgia.corn, vatgia.com
+wizardlOl.corn, wizard101.com
+ceneo.pl, ceneo.pl
+lting.corn, 1ting.com
+rneetic.fr, meetic.fr
+cardekho.corn, cardekho.com
+tripadvisor.it, tripadvisor.it
+dhl.corn, dhl.com
+aibang.corn, aibang.com
+asp.net, asp.net
+toing.corn.br, toing.com.br
+zhubajie.corn, zhubajie.com
+telecornitalia.it, telecomitalia.it
+claro-search.corn, claro-search.com
+nickjr.corn, nickjr.com
+iconfinder.corn, iconfinder.com
+rnobile9.corn, mobile9.com
+cisco.corn, cisco.com
+cpanel.net, cpanel.net
+indiegogo.corn, indiegogo.com
+egotastic.corn, egotastic.com
+hforcare.corn, hforcare.com
+pbs.org, pbs.org
+realestate.corn.au, realestate.com.au
+abv.bg, abv.bg
+drugs.corn, drugs.com
+bt.corn, bt.com
+wildberries.ru, wildberries.ru
+edrearns.it, edreams.it
+statigr.arn, statigr.am
+prestashop.corn, prestashop.com
+adxite.corn, adxite.com
+birthdaypeorns.corn, birthdaypeoms.com
+exbii.corn, exbii.com
+blogrnura.corn, blogmura.com
+sciencedirect.corn, sciencedirect.com
+sanspo.corn, sanspo.com
+nextrnedia.corn, nextmedia.com
+tvoyauda4a.ru, tvoyauda4a.ru
+tangdou.corn, tangdou.com
+blackboard.corn, blackboard.com
+qiyou.corn, qiyou.com
+prezentacya.ru, prezentacya.ru
+clicrbs.corn.br, clicrbs.com.br
+wayfair.corn, wayfair.com
+xvideos-field.corn, xvideos-field.com
+national.corn.au, national.com.au
+friendfeed.corn, friendfeed.com
+plurk.corn, plurk.com
+lolrnake.corn, lolmake.com
+b9drn.corn, b9dm.com
+afkarnews.ir, afkarnews.ir
+dhl.de, dhl.de
+charnpionat.corn, championat.com
+rnoviefone.corn, moviefone.com
+popcash.net, popcash.net
+cliphunter.corn, cliphunter.com
+sharebeast.corn, sharebeast.com
+wowhead.corn, wowhead.com
+firstpost.corn, firstpost.com
+lloydstsb.corn, lloydstsb.com
+fazenda.gov.br, fazenda.gov.br
+lonelyplanet.corn, lonelyplanet.com
+freenet.de, freenet.de
+justanswer.corn, justanswer.com
+qiwi.corn, qiwi.com
+shufuni.corn, shufuni.com
+drive2.ru, drive2.ru
+slando.ua, slando.ua
+caribbeancorn.corn, caribbeancom.com
+uniblue.corn, uniblue.com
+real.corn, real.com
+addictinggarnes.corn, addictinggames.com
+wnd.corn, wnd.com
+col3negoriginal.org, col3negoriginal.org
+loltrk.corn, loltrk.com
+videodownloadconverter.corn, videodownloadconverter.com
+google.lv, google.lv
+seriesyonkis.corn, seriesyonkis.com
+ryushare.corn, ryushare.com
+sl979.corn, s1979.com
+cheapoair.corn, cheapoair.com
+subrnarino.corn.br, submarino.com.br
+topface.corn, topface.com
+hotelscornbined.corn, hotelscombined.com
+whatisrnyipaddress.corn, whatismyipaddress.com
+z6.corn, z6.com
+sozcu.corn.tr, sozcu.com.tr
+sonyrnobile.corn, sonymobile.com
+planetrninecraft.corn, planetminecraft.com
+optirnurn.net, optimum.net
+google.corn.pr, google.com.pr
+rnthai.corn, mthai.com
+onlinecreditcenter6.corn, onlinecreditcenter6.com
+tharunaya.co.uk, tharunaya.co.uk
+sfirng.corn, sfimg.com
+natwest.corn, natwest.com
+zergnet.corn, zergnet.com
+alotporn.corn, alotporn.com
+urbanspoon.corn, urbanspoon.com
+punishtube.corn, punishtube.com
+proboards.corn, proboards.com
+betfair.corn, betfair.com
+iltasanornat.fi, iltasanomat.fi
+ssisurveys.corn, ssisurveys.com
+harvard.edu, harvard.edu
+blic.rs, blic.rs
+clicksia.corn, clicksia.com
+skillpages.corn, skillpages.com
+rnobilewap.corn, mobilewap.com
+fiducia.de, fiducia.de
+torntvz.org, torntvz.org
+leparisien.fr, leparisien.fr
+anjuke.corn, anjuke.com
+rabobank.nl, rabobank.nl
+sport.pl, sport.pl
+schwab.corn, schwab.com
+buenastareas.corn, buenastareas.com
+befuck.corn, befuck.com
+srnart-search.corn, smart-search.com
+ivi.ru, ivi.ru
+dvdvideosoft.corn, dvdvideosoft.com
+ubi.corn, ubi.com
+rnakepolo.corn, makepolo.com
+landl.corn, 1and1.com
+pcworld.corn, pcworld.com
+caf.fr, caf.fr
+fnb.co.za, fnb.co.za
+vanguardngr.corn, vanguardngr.com
+floozycity.corn, floozycity.com
+ubuntu.corn, ubuntu.com
+rny-link.pro, my-link.pro
+centurylink.corn, centurylink.com
+slashdot.org, slashdot.org
+rnirrorcreator.corn, mirrorcreator.com
+rutube.ru, rutube.ru
+tubeplus.rne, tubeplus.me
+kicker.de, kicker.de
+unibet.corn, unibet.com
+pornyaz.corn, pornyaz.com
+learntotradethernarket.corn, learntotradethemarket.com
+tokyo-porn-tube.corn, tokyo-porn-tube.com
+luvcow.corn, luvcow.com
+i.ua, i.ua
+ole.corn.ar, ole.com.ar
+redfin.corn, redfin.com
+cnki.net, cnki.net
+2shared.corn, 2shared.com
+infibearn.corn, infibeam.com
+zdnet.corn, zdnet.com
+fishki.net, fishki.net
+ukr.net, ukr.net
+jiarneng.corn, jiameng.com
+utorrent.corn, utorrent.com
+elkhabar.corn, elkhabar.com
+anirne44.corn, anime44.com
+societegenerale.fr, societegenerale.fr
+livernerne.corn, livememe.com
+startertv.fr, startertv.fr
+pingornatic.corn, pingomatic.com
+indeed.co.uk, indeed.co.uk
+dpstrearn.net, dpstream.net
+rnundodeportivo.corn, mundodeportivo.com
+gravatar.corn, gravatar.com
+ipl38.corn, ip138.com
+yandex.net, yandex.net
+barbie.corn, barbie.com
+wattpad.corn, wattpad.com
+dzwww.corn, dzwww.com
+technorati.corn, technorati.com
+rneishichina.corn, meishichina.com
+russianpost.ru, russianpost.ru
+kboing.corn.br, kboing.com.br
+lzjl.corn, lzjl.com
+newsnow.co.uk, newsnow.co.uk
+dw.de, dw.de
+inetglobal.corn, inetglobal.com
+tripadvisor.in, tripadvisor.in
+ashleyrnadison.corn, ashleyrnadison.com
+rapgenius.corn, rapgenius.com
+xuite.net, xuite.net
+nowvideo.eu, nowvideo.eu
+search.us.corn, search.us.com
+usagc.org, usagc.org
+santander.co.uk, santander.co.uk
+99acres.corn, 99acres.com
+bigcartel.corn, bigcartel.com
+haivl.corn, haivl.com
+jsfiddle.net, jsfiddle.net
+io9.corn, io9.com
+lg.corn, lg.com
+veoh.corn, veoh.com
+dafiti.corn.br, dafiti.com.br
+heise.de, heise.de
+wikispaces.corn, wikispaces.com
+google.corn.bo, google.com.bo
+skyscrapercity.corn, skyscrapercity.com
+zaobao.corn, zaobao.com
+pirateproxy.net, pirateproxy.net
+rnuyzorras.corn, muyzorras.com
+entrepreneur.corn, entrepreneur.com
+sxc.hu, sxc.hu
+superuser.corn, superuser.com
+jb5l.net, jb51.net
+bitsnoop.corn, bitsnoop.com
+index.hu, index.hu
+tubexclips.corn, tubexclips.com
+syrnantec.corn, symantec.com
+sedo.corn, sedo.com
+gongchang.corn, gongchang.com
+newsrnth.net, newsmth.net
+srclick.ru, srclick.ru
+bornnegocio.corn, bomnegocio.com
+ornegle.corn, omegle.com
+sweetpacks-search.corn, sweetpacks-search.com
+OOOwebhost.corn, 000webhost.com
+rencontreshard.corn, rencontreshard.com
+jurnei.corn, jumei.com
+acfun.tv, acfun.tv
+celebuzz.corn, celebuzz.com
+el-balad.corn, el-balad.com
+wajarn.corn, wajam.com
+zoopla.co.uk, zoopla.co.uk
+sc4888.corn, sc4888.com
+rnobileaziende.it, mobileaziende.it
+officialsurvey.org, officialsurvey.org
+googleapis.corn, googleapis.com
+jobsdb.corn, jobsdb.com
+google.corn.sv, google.com.sv
+freejobalert.corn, freejobalert.com
+walla.co.il, walla.co.il
+hollywoodreporter.corn, hollywoodreporter.com
+inc.corn, inc.com
+bbandt.corn, bbandt.com
+williarnhill.corn, williamhill.com
+jeu.info, jeu.info
+vrbo.corn, vrbo.com
+arabseed.corn, arabseed.com
+spielaffe.de, spielaffe.de
+wykop.pl, wykop.pl
+narne.corn, name.com
+web-opinions.corn, web-opinions.com
+ehowenespanol.corn, ehowenespanol.com
+uuzu.corn, uuzu.com
+cafepress.corn, cafepress.com
+beeline.ru, beeline.ru
+searchenginejournal.corn, searchenginejournal.com
+webex.corn, webex.com
+zerohedge.corn, zerohedge.com
+cityads.ru, cityads.ru
+colurnbia.edu, columbia.edu
+jia.corn, jia.com
+tistory.corn, tistory.com
+lOObestbuy.corn, 100bestbuy.com
+realitykings.corn, realitykings.com
+shopify.corn, shopify.com
+garnetop.corn, gametop.com
+eharrnony.corn, eharmony.com
+ngoisao.net, ngoisao.net
+angieslist.corn, angieslist.com
+grotal.corn, grotal.com
+rnanhunt.net, manhunt.net
+adslgate.corn, adslgate.com
+dernotywatory.pl, demotywatory.pl
+enfernenino.corn, enfemenino.com
+yallakora.corn, yallakora.com
+careesrna.in, careesma.in
+draugiern.lv, draugiem.lv
+greatandhra.corn, greatandhra.com
+lifescript.corn, lifescript.com
+androidcentral.corn, androidcentral.com
+wiley.corn, wiley.com
+alot.corn, alot.com
+lOOlO.corn, 10010.com
+next.co.uk, next.co.uk
+ll5.corn, 115.com
+orngprn.corn, omgpm.com
+rnycalendarbook.corn, mycalendarbook.com
+playxn.corn, playxn.com
+niksalehi.corn, niksalehi.com
+serviporno.corn, serviporno.com
+poste.it, poste.it
+kirniss.corn, kimiss.com
+bearshare.corn, bearshare.com
+clickpoint.corn, clickpoint.com
+seek.corn.au, seek.com.au
+bab.la, bab.la
+ads8.corn, ads8.com
+viewster.corn, viewster.com
+ideacellular.corn, ideacellular.com
+tyrnpanus.net, tympanus.net
+wwwblogto.corn, wwwblogto.com
+tblop.corn, tblop.com
+elong.corn, elong.com
+funnyordie.corn, funnyordie.com
+radikal.ru, radikal.ru
+rk.corn, rk.com
+alarab.net, alarab.net
+willhaben.at, willhaben.at
+beyond.corn, beyond.com
+punchng.corn, punchng.com
+viglink.corn, viglink.com
+rnicrosoftstore.corn, microsoftstore.com
+tripleclicks.corn, tripleclicks.com
+rnl9O5.corn, m1905.com
+ofreegarnes.corn, ofreegames.com
+s2d6.corn, s2d6.com
+36Obuy.corn, 360buy.com
+rakuten.corn, rakuten.com
+evite.corn, evite.com
+kornpasiana.corn, kompasiana.com
+dailycaller.corn, dailycaller.com
+holidaycheck.de, holidaycheck.de
+irnvu.corn, imvu.com
+nate.corn, nate.com
+fnac.corn, fnac.com
+htc.corn, htc.com
+savenkeep.corn, savenkeep.com
+alfabank.ru, alfabank.ru
+zaycev.net, zaycev.net
+vidtornp3.corn, vidtomp3.com
+eluniversal.corn.rnx, eluniversal.com.mx
+theatlantic.corn, theatlantic.com
+garnigo.de, gamigo.de
+lolking.net, lolking.net
+wer-kennt-wen.de, wer-kennt-wen.de
+stern.de, stern.de
+sportl.de, sport1.de
+goalunited.org, goalunited.org
+discogs.corn, discogs.com
+whirlpool.net.au, whirlpool.net.au
+savefrorn.net, savefrom.net
+eurosport.fr, eurosport.fr
+juegosjuegos.corn, juegosjuegos.com
+open24news.tv, open24news.tv
+sinaapp.corn, sinaapp.com
+fuq.corn, fuq.com
+index.hr, index.hr
+realpopbid.corn, realpopbid.com
+rollingstone.corn, rollingstone.com
+globaltestrnarket.corn, globaltestmarket.com
+seopult.ru, seopult.ru
+wurnii.corn, wumii.com
+ford.corn, ford.com
+cabelas.corn, cabelas.com
+securepaynet.net, securepaynet.net
+zhibo8.cc, zhibo8.cc
+jiji.corn, jiji.com
+gezinti.corn, gezinti.com
+rneb.gov.tr, meb.gov.tr
+classifiedads.corn, classifiedads.com
+kitco.corn, kitco.com
+incredirnail.corn, incredimail.com
+esrnas.corn, esmas.com
+soccerway.corn, soccerway.com
+rivals.corn, rivals.com
+prezi.corn, prezi.com
+shopping.corn, shopping.com
+superjob.ru, superjob.ru
+chinaacc.corn, chinaacc.com
+arnoureux.corn, amoureux.com
+rnysrnartprice.corn, mysmartprice.com
+eleconornista.es, eleconomista.es
+rnercola.corn, mercola.com
+irnlive.corn, imlive.com
+teacup.corn, teacup.com
+rnodelrnayhern.corn, modelmayhem.com
+nic.ru, nic.ru
+brazzersnetwork.corn, brazzersnetwork.com
+everything.org.uk, everything.org.uk
+bhg.corn, bhg.com
+longhoo.net, longhoo.net
+superpages.corn, superpages.com
+tny.cz, tny.cz
+yourfilezone.corn, yourfilezone.com
+tuan8OO.corn, tuan800.com
+streev.corn, streev.com
+sedty.corn, sedty.com
+boxofficernojo.corn, boxofficemojo.com
+hollyscoop.corn, hollyscoop.com
+safecart.corn, safecart.com
+alrnogaz.corn, almogaz.com
+cashnhits.corn, cashnhits.com
+wetplace.corn, wetplace.com
+freepik.corn, freepik.com
+rarbg.corn, rarbg.com
+xxxbunker.corn, xxxbunker.com
+prchecker.info, prchecker.info
+halifax-online.co.uk, halifax-online.co.uk
+trafficfactory.biz, trafficfactory.biz
+telecinco.es, telecinco.es
+searchterrnresults.corn, searchtermresults.com
+unarn.rnx, unam.mx
+akhbar-elwatan.corn, akhbar-elwatan.com
+lynda.corn, lynda.com
+yougetlaid.corn, yougetlaid.com
+srnart.corn.au, smart.com.au
+advfn.corn, advfn.com
+unicredit.it, unicredit.it
+zornato.corn, zomato.com
+flirt.corn, flirt.com
+netease.corn, netease.com
+bnpparibas.net, bnpparibas.net
+elcornercio.pe, elcomercio.pe
+rnathrubhurni.corn, mathrubhumi.com
+koyotesoft.corn, koyotesoft.com
+filrnix.net, filmix.net
+xnxxhdtube.corn, xnxxhdtube.com
+ennaharonline.corn, ennaharonline.com
+junbi-tracker.corn, junbi-tracker.com
+buzzdock.corn, buzzdock.com
+ernirates.corn, emirates.com
+vivanuncios.corn.rnx, vivanuncios.com.mx
+infojobs.net, infojobs.net
+srni2.ru, smi2.ru
+lotterypost.corn, lotterypost.com
+bandcarnp.corn, bandcamp.com
+ekstrabladet.dk, ekstrabladet.dk
+nownews.corn, nownews.com
+bc.vc, bc.vc
+google.corn.af, google.com.af
+ulrnart.ru, ulmart.ru
+estadao.corn.br, estadao.com.br
+politico.corn, politico.com
+kl688.corn, kl688.com
+resellerclub.corn, resellerclub.com
+whois.net, whois.net
+seobuilding.ru, seobuilding.ru
+t4ll.rne, t411.me
+googlesyndication.corn, googlesyndication.com
+delfi.lt, delfi.lt
+eqla3.corn, eqla3.com
+ali2l3.net, ali213.net
+fanpage.it, fanpage.it
+uptobox.corn, uptobox.com
+google.jo, google.jo
+cncn.corn, cncn.com
+srne.sk, sme.sk
+kinozal.tv, kinozal.tv
+ceconline.corn, ceconline.com
+billboard.corn, billboard.com
+citi.corn, citi.com
+naughtyarnerica.corn, naughtyamerica.com
+classrnates.corn, classmates.com
+coursera.org, coursera.org
+pingan.corn, pingan.com
+voanews.corn, voanews.com
+tankionline.corn, tankionline.com
+jetblue.corn, jetblue.com
+spainshtranslation.corn, spainshtranslation.com
+ebookbrowse.corn, ebookbrowse.com
+rnet-art.corn, met-art.com
+rnegafon.ru, megafon.ru
+quibids.corn, quibids.com
+srnartfren.corn, smartfren.com
+cleartrip.corn, cleartrip.com
+pixrnania.corn, pixmania.com
+vivastreet.corn, vivastreet.com
+thegfnetwork.corn, thegfnetwork.com
+paytrn.corn, paytm.com
+rneinsextagebuch.net, meinsextagebuch.net
+rnernecenter.corn, memecenter.com
+ixbt.corn, ixbt.com
+dagbladet.no, dagbladet.no
+basecarnphq.corn, basecamphq.com
+chinatirnes.corn, chinatimes.com
+bubblews.corn, bubblews.com
+xtool.ru, xtool.ru
+opodo.co.uk, opodo.co.uk
+hattrick.org, hattrick.org
+zopirn.corn, zopim.com
+aol.co.uk, aol.co.uk
+gazzetta.gr, gazzetta.gr
+l8andabused.corn, 18andabused.com
+rncssl.corn, mcssl.com
+econornist.corn, economist.com
+zeit.de, zeit.de
+google.corn.uy, google.com.uy
+pinoy-ako.info, pinoy-ako.info
+lazada.co.id, lazada.co.id
+filgoal.corn, filgoal.com
+rozetka.corn.ua, rozetka.com.ua
+alrnesryoon.corn, almesryoon.com
+csrnonitor.corn, csmonitor.com
+bizjournals.corn, bizjournals.com
+rackspace.corn, rackspace.com
+webgozar.corn, webgozar.com
+opencart.corn, opencart.com
+rnediaplex.corn, mediaplex.com
+deutsche-bank.de, deutsche-bank.de
+sirnilarsites.corn, similarsites.com
+sotrnarket.ru, sotmarket.ru
+chatzurn.corn, chatzum.com
+huffingtonpost.co.uk, huffingtonpost.co.uk
+carwale.corn, carwale.com
+rnernez.corn, memez.com
+hostrnonster.corn, hostmonster.com
+rnuzofon.corn, muzofon.com
+elephanttube.corn, elephanttube.com
+crunchbase.corn, crunchbase.com
+irnhonet.ru, imhonet.ru
+lusongsong.corn, lusongsong.com
+filrnesonlinegratis.net, filmesonlinegratis.net
+giaoduc.net.vn, giaoduc.net.vn
+rnanhub.corn, manhub.com
+tatadocorno.corn, tatadocomo.com
+realitatea.net, realitatea.net
+freernp3x.corn, freemp3x.com
+freernail.hu, freemail.hu
+ganool.corn, ganool.com
+feedreader.corn, feedreader.com
+sportsdirect.corn, sportsdirect.com
+videolan.org, videolan.org
+watchseries.lt, watchseries.lt
+rotapost.ru, rotapost.ru
+nwolb.corn, nwolb.com
+searchquotes.corn, searchquotes.com
+kaspersky.corn, kaspersky.com
+go2cloud.org, go2cloud.org
+grepolis.corn, grepolis.com
+profit-partner.ru, profit-partner.ru
+articlesbase.corn, articlesbase.com
+dns-shop.ru, dns-shop.ru
+radikal.corn.tr, radikal.com.tr
+justjared.corn, justjared.com
+lancenet.corn.br, lancenet.com.br
+rnangapanda.corn, mangapanda.com
+theglobeandrnail.corn, theglobeandmail.com
+ecollege.corn, ecollege.com
+rnyanirnelist.net, myanimelist.net
+fotornac.corn.tr, fotomac.com.tr
+irnanhua.corn, imanhua.com
+travelzoo.corn, travelzoo.com
+jjwxc.net, jjwxc.net
+q.gs, q.gs
+naaptol.corn, naaptol.com
+sarnbaporno.corn, sambaporno.com
+rnacrojuegos.corn, macrojuegos.com
+ooo-sex.corn, ooo-sex.com
+fab.corn, fab.com
+roflzone.corn, roflzone.com
+searchcornpletion.corn, searchcompletion.com
+jezebel.corn, jezebel.com
+bizdec.ru, bizdec.ru
+torrentino.corn, torrentino.com
+rnultitran.ru, multitran.ru
+tune-up.corn, tune-up.com
+sparkpeople.corn, sparkpeople.com
+desi-tashan.corn, desi-tashan.com
+rnashreghnews.ir, mashreghnews.ir
+talktalk.co.uk, talktalk.co.uk
+hinkhoj.corn, hinkhoj.com
+2Orninutes.fr, 20minutes.fr
+sulia.corn, sulia.com
+icirns.corn, icims.com
+dizi-rnag.corn, dizi-mag.com
+webaslan.corn, webaslan.com
+en.wordpress.corn, en.wordpress.com
+funrnoods.corn, funmoods.com
+softgozar.corn, softgozar.com
+starwoodhotels.corn, starwoodhotels.com
+studiopress.corn, studiopress.com
+click.in, click.in
+rneetcheap.corn, meetcheap.com
+angel-live.corn, angel-live.com
+beforeitsnews.corn, beforeitsnews.com
+trello.corn, trello.com
+icontact.corn, icontact.com
+prlog.org, prlog.org
+incentria.corn, incentria.com
+bouyguestelecorn.fr, bouyguestelecom.fr
+dstv.corn, dstv.com
+arstechnica.corn, arstechnica.com
+diigo.corn, diigo.com
+consurners-research.corn, consumers-research.com
+rnetaffiliation.corn, metaffiliation.com
+telekorn.de, telekom.de
+izlesene.corn, izlesene.com
+newsit.gr, newsit.gr
+fuckingawesorne.corn, fuckingawesome.com
+osyrn.gov.tr, osym.gov.tr
+svyaznoy.ru, svyaznoy.ru
+watchfreernovies.ch, watchfreemovies.ch
+gurntree.pl, gumtree.pl
+sportbox.ru, sportbox.ru
+reserverunessai.corn, reserverunessai.com
+hsbc.corn.hk, hsbc.com.hk
+cricbuzz.corn, cricbuzz.com
+djelfa.info, djelfa.info
+nouvelobs.corn, nouvelobs.com
+aruba.it, aruba.it
+hornes.corn, homes.com
+allezleslions.corn, allezleslions.com
+orkut.corn.br, orkut.com.br
+aionfreetoplay.corn, aionfreetoplay.com
+acadernia.edu, academia.edu
+consurnerreports.org, consumerreports.org
+ilsole24ore.corn, ilsole24ore.com
+sephora.corn, sephora.com
+lds.org, lds.org
+vrnall.corn, vmall.com
+ultirnasnoticias.corn.ve, ultimasnoticias.com.ve
+healthgrades.corn, healthgrades.com
+irngbox.corn, imgbox.com
+dlsite.corn, dlsite.com
+whitesrnoke.corn, whitesmoke.com
+thenextweb.corn, thenextweb.com
+qirel23.corn, qire123.com
+peeplo.corn, peeplo.com
+chitika.corn, chitika.com
+alwafd.org, alwafd.org
+phonearena.corn, phonearena.com
+ovh.corn, ovh.com
+tusfiles.net, tusfiles.net
+l8schoolgirlz.corn, 18schoolgirlz.com
+bongacarns.corn, bongacams.com
+horne.pl, home.pl
+footrnercato.net, footmercato.net
+sprashivai.ru, sprashivai.ru
+rnegafilrneshd.net, megafilmeshd.net
+prerniurn-display.corn, premium-display.com
+clickey.corn, clickey.com
+tokyo-tube.corn, tokyo-tube.com
+watch32.corn, watch32.com
+pornolab.net, pornolab.net
+tirnewarnercable.corn, timewarnercable.com
+naturalnews.corn, naturalnews.com
+afirnet.corn, afimet.com
+telderi.ru, telderi.ru
+ioffer.corn, ioffer.com
+lapatilla.corn, lapatilla.com
+livetv.ru, livetv.ru
+cloudflare.corn, cloudflare.com
+lupoporno.corn, lupoporno.com
+nhaccuatui.corn, nhaccuatui.com
+thepostgarne.corn, thepostgame.com
+ipage.corn, ipage.com
+banesconline.corn, banesconline.com
+cdc.gov, cdc.gov
+adonweb.ru, adonweb.ru
+zone-telechargernent.corn, zone-telechargement.com
+intellicast.corn, intellicast.com
+uloz.to, uloz.to
+pikabu.ru, pikabu.ru
+rnegogo.net, megogo.net
+wenxuecity.corn, wenxuecity.com
+xrnl-siternaps.corn, xml-sitemaps.com
+webdunia.corn, webdunia.com
+justhost.corn, justhost.com
+starbucks.corn, starbucks.com
+wargarning.net, wargaming.net
+hugedornains.corn, hugedomains.com
+rnagicbricks.corn, magicbricks.com
+gigporno.corn, gigporno.com
+rikunabi.corn, rikunabi.com
+5lauto.corn, 51auto.com
+warriorplus.corn, warriorplus.com
+gudvin.tv, gudvin.tv
+bigrnir.net, bigmir.net
+ansa.it, ansa.it
+standardbank.co.za, standardbank.co.za
+toshiba.corn, toshiba.com
+xinnet.corn, xinnet.com
+geico.corn, geico.com
+funnyjunk.corn, funnyjunk.com
+affaritaliani.it, affaritaliani.it
+cityheaven.net, cityheaven.net
+tubewolf.corn, tubewolf.com
+google.org, google.org
+ad.nl, ad.nl
+tutorialspoint.corn, tutorialspoint.com
+uidai.gov.in, uidai.gov.in
+everydayhealth.corn, everydayhealth.com
+jzip.corn, jzip.com
+lolspotsarticles.corn, lolspotsarticles.com
+rueducornrnerce.fr, rueducommerce.fr
+lvrnarna.corn, lvmama.com
+roboforrn.corn, roboform.com
+zoznarn.sk, zoznam.sk
+livesrni.corn, livesmi.com
+die-boersenforrnel.corn, die-boersenformel.com
+watchcartoononline.corn, watchcartoononline.com
+abclocal.go.corn, abclocal.go.com
+techrepublic.corn, techrepublic.com
+just-fuck.corn, just-fuck.com
+carnster.corn, camster.com
+akairan.corn, akairan.com
+yeslibertin.corn, yeslibertin.com
+abc.go.corn, abc.go.com
+searchtherightwords.corn, searchtherightwords.com
+scotiabank.corn, scotiabank.com
+justclick.ru, justclick.ru
+douguo.corn, douguo.com
+discover.corn, discover.com
+britishairways.corn, britishairways.com
+rnobafire.corn, mobafire.com
+gi-akadernie.ning.corn, gi-akademie.ning.com
+desirulez.net, desirulez.net
+qiushibaike.corn, qiushibaike.com
+rnoonbasa.corn, moonbasa.com
+all.biz, all.biz
+springer.corn, springer.com
+ernai.corn, emai.com
+deadspin.corn, deadspin.com
+hulkshare.corn, hulkshare.com
+fast-torrent.ru, fast-torrent.ru
+oriflarne.corn, oriflame.com
+irngchili.net, imgchili.net
+rnega-juegos.rnx, mega-juegos.mx
+gyazo.corn, gyazo.com
+persianv.corn, persianv.com
+adk2.corn, adk2.com
+ingbank.pl, ingbank.pl
+nationalconsurnercenter.corn, nationalconsumercenter.com
+xxxkinky.corn, xxxkinky.com
+rnywot.corn, mywot.com
+gayrnaletube.corn, gaymaletube.com
+ltv.ru, 1tv.ru
+rnanutd.corn, manutd.com
+rnerchantcircle.corn, merchantcircle.com
+canalblog.corn, canalblog.com
+capitalone36O.corn, capitalone360.com
+tlbb8.corn, tlbb8.com
+softonic.fr, softonic.fr
+ccavenue.corn, ccavenue.com
+tyroodr.corn, tyroodr.com
+exarn8.corn, exam8.com
+allrnusic.corn, allmusic.com
+stubhub.corn, stubhub.com
+arcor.de, arcor.de
+yolasite.corn, yolasite.com
+haraj.corn.sa, haraj.com.sa
+rnypopup.ir, mypopup.ir
+rnernurlar.net, memurlar.net
+srnugrnug.corn, smugmug.com
+filefactory.corn, filefactory.com
+fantasti.cc, fantasti.cc
+bokra.net, bokra.net
+goarticles.corn, goarticles.com
+rnoneysavingexpert.corn, moneysavingexpert.com
+donga.corn, donga.com
+lastrninute.corn, lastminute.com
+xkcd.corn, xkcd.com
+sou3OO.corn, sou300.com
+rnagnovideo.corn, magnovideo.com
+inquirer.net, inquirer.net
+phoenix.edu, phoenix.edu
+videogenesis.corn, videogenesis.com
+thestar.corn, thestar.com
+tripadvisor.es, tripadvisor.es
+blankrefer.corn, blankrefer.com
+yle.fi, yle.fi
+bearntele.corn, beamtele.com
+oanda.corn, oanda.com
+iheart.corn, iheart.com
+google.co.tz, google.co.tz
+stargazete.corn, stargazete.com
+bossip.corn, bossip.com
+defaultsear.ch, defaultsear.ch
+thaiseoboard.corn, thaiseoboard.com
+qinbei.corn, qinbei.com
+ninisite.corn, ninisite.com
+j.gs, j.gs
+nos.nl, nos.nl
+qualtrics.corn, qualtrics.com
+kornrnersant.ru, kommersant.ru
+urban-rivals.corn, urban-rivals.com
+cornputerbild.de, computerbild.de
+fararu.corn, fararu.com
+rnenshealth.corn, menshealth.com
+jobstreet.corn, jobstreet.com
+rbcroyalbank.corn, rbcroyalbank.com
+inrnotionhosting.corn, inmotionhosting.com
+surveyrouter.corn, surveyrouter.com
+kankanews.corn, kankanews.com
+aol.de, aol.de
+bol.corn, bol.com
+datpiff.corn, datpiff.com
+rnplife.corn, mplife.com
+sale-fire.corn, sale-fire.com
+inbox.lv, inbox.lv
+offeraturn.corn, offeratum.com
+pandora.tv, pandora.tv
+eltiernpo.corn, eltiempo.com
+indiarailinfo.corn, indiarailinfo.com
+solidtrustpay.corn, solidtrustpay.com
+warthunder.ru, warthunder.ru
+novarnov.corn, novamov.com
+folkd.corn, folkd.com
+envato.corn, envato.com
+wetpaint.corn, wetpaint.com
+ternpo.co, tempo.co
+howtogeek.corn, howtogeek.com
+foundationapi.corn, foundationapi.com
+care2.corn, care2.com
+bendibao.corn, bendibao.com
+rnazika2day.corn, mazika2day.com
+asda.corn, asda.com
+nowvideo.ch, nowvideo.ch
+hiapk.corn, hiapk.com
+l7u.corn, 17u.com
+tutu.ru, tutu.ru
+ncdownloader.corn, ncdownloader.com
+warez-bb.org, warez-bb.org
+jsoftj.corn, jsoftj.com
+xrnarks.corn, xmarks.com
+36kr.corn, 36kr.com
+runetki.corn, runetki.com
+quoka.de, quoka.de
+heureka.cz, heureka.cz
+rnonografias.corn, monografias.com
+zhenai.corn, zhenai.com
+4porn.corn, 4porn.com
+antena3.corn, antena3.com
+lintas.rne, lintas.me
+seroundtable.corn, seroundtable.com
+el.ru, e1.ru
+berkeley.edu, berkeley.edu
+officedepot.corn, officedepot.com
+rnyflorida.corn, myflorida.com
+parispornrnovies.corn, parispornmovies.com
+uniqlo.corn, uniqlo.com
+topky.sk, topky.sk
+lurnovies.corn, lumovies.com
+buysellads.corn, buysellads.com
+stirileprotv.ro, stirileprotv.ro
+scottrade.corn, scottrade.com
+rnrntrends.net, mmtrends.net
+wholesale-dress.net, wholesale-dress.net
+rnetacritic.corn, metacritic.com
+pichunter.corn, pichunter.com
+rnoneybookers.corn, moneybookers.com
+idealista.corn, idealista.com
+buzzle.corn, buzzle.com
+rcorn.co.in, rcom.co.in
+weightwatchers.corn, weightwatchers.com
+itv.corn, itv.com
+inilah.corn, inilah.com
+vic.gov.au, vic.gov.au
+prorn.ua, prom.ua
+with2.net, with2.net
+doodle.corn, doodle.com
+trafficbroker.corn, trafficbroker.com
+h33t.corn, h33t.com
+avaaz.org, avaaz.org
+rnaultalk.corn, maultalk.com
+brno.corn, bmo.com
+nerdbux.corn, nerdbux.com
+abnarnro.nl, abnamro.nl
+didigarnes.corn, didigames.com
+pornorarna.corn, pornorama.com
+forurnotion.corn, forumotion.com
+wornan.ru, woman.ru
+thaivisa.corn, thaivisa.com
+lexpress.fr, lexpress.fr
+forurncornrnunity.net, forumcommunity.net
+regions.corn, regions.com
+sf-express.corn, sf-express.com
+donkeyrnails.corn, donkeymails.com
+clubic.corn, clubic.com
+aucfan.corn, aucfan.com
+enterfactory.corn, enterfactory.com
+yandex.corn, yandex.com
+iherb.corn, iherb.com
+in.gr, in.gr
+olx.pt, olx.pt
+fbdownloader.corn, fbdownloader.com
+autoscout24.it, autoscout24.it
+siteground.corn, siteground.com
+psicofxp.corn, psicofxp.com
+persiangig.corn, persiangig.com
+rnetroer.corn, metroer.com
+tokopedia.corn, tokopedia.com
+seccarn.info, seccam.info
+sport-express.ru, sport-express.ru
+vodafone.it, vodafone.it
+blekko.corn, blekko.com
+entekhab.ir, entekhab.ir
+expressen.se, expressen.se
+zalando.fr, zalando.fr
+hawaaworld.corn, hawaaworld.com
+freeonlinegarnes.corn, freeonlinegames.com
+google.corn.lb, google.com.lb
+ab-in-den-urlaub.de, ab-in-den-urlaub.de
+android4tw.corn, android4tw.com
+alriyadh.corn, alriyadh.com
+drugstore.corn, drugstore.com
+iobit.corn, iobit.com
+rei.corn, rei.com
+racing-garnes.corn, racing-games.com
+rnornrnyfucktube.corn, mommyfucktube.com
+pideo.net, pideo.net
+gogoanirne.corn, gogoanime.com
+avaxho.rne, avaxho.me
+christianrningle.corn, christianmingle.com
+activesearchresults.corn, activesearchresults.com
+trendsonline.biz, trendsonline.biz
+planetsuzy.org, planetsuzy.org
+rubiasl9.corn, rubias19.com
+cleverbridge.corn, cleverbridge.com
+jeevansathi.corn, jeevansathi.com
+washingtontirnes.corn, washingtontimes.com
+lcl.fr, lcl.fr
+98ia.corn, 98ia.com
+rnercadolibre.corn.co, mercadolibre.com.co
+n-tv.de, n-tv.de
+divyabhaskar.co.in, divyabhaskar.co.in
+airbnb.corn, airbnb.com
+rnybrowserbar.corn, mybrowserbar.com
+travian.corn, travian.com
+autoblog.corn, autoblog.com
+blesk.cz, blesk.cz
+playboy.corn, playboy.com
+p3Odownload.corn, p30download.com
+pazienti.net, pazienti.net
+uast.ac.ir, uast.ac.ir
+logsoku.corn, logsoku.com
+zedge.net, zedge.net
+creditrnutuel.fr, creditmutuel.fr
+absa.co.za, absa.co.za
+rnilliyet.tv, milliyet.tv
+jiathis.corn, jiathis.com
+liverpoolfc.tv, liverpoolfc.tv
+dospy.corn, dospy.com
+calarneo.corn, calameo.com
+netsuite.corn, netsuite.com
+angelfire.corn, angelfire.com
+snagajob.corn, snagajob.com
+hollywoodlife.corn, hollywoodlife.com
+techtudo.corn.br, techtudo.com.br
+payserve.corn, payserve.com
+portalnet.cl, portalnet.cl
+worldadult-videos.info, worldadult-videos.info
+indianpornvideos.corn, indianpornvideos.com
+france24.corn, france24.com
+discuss.corn.hk, discuss.com.hk
+theplanet.corn, theplanet.com
+advego.ru, advego.ru
+eltiernpo.es, eltiempo.es
+55tuan.corn, 55tuan.com
+snopes.corn, snopes.com
+startnow.corn, startnow.com
+tucarro.corn, tucarro.com
+skyscanner.net, skyscanner.net
+wchonline.corn, wchonline.com
+gaadi.corn, gaadi.com
+lindaikeji.blogspot.corn, lindaikeji.blogspot.com
+keywordblocks.corn, keywordblocks.com
+apsense.corn, apsense.com
+avangate.corn, avangate.com
+gandul.info, gandul.info
+google.corn.gh, google.com.gh
+rnybigcornrnerce.corn, mybigcommerce.com
+horneaway.corn, homeaway.com
+wikitravel.org, wikitravel.org
+etxt.ru, etxt.ru
+zerx.ru, zerx.ru
+sidereel.corn, sidereel.com
+edrearns.es, edreams.es
+india-forurns.corn, india-forums.com
+infonews.corn, infonews.com
+zoorninfo.corn, zoominfo.com
+stylebistro.corn, stylebistro.com
+dorninos.corn, dominos.com
+59lhx.corn, 591hx.com
+authorize.net, authorize.net
+6lbaobao.corn, 61baobao.com
+digitalspy.co.uk, digitalspy.co.uk
+godvine.corn, godvine.com
+rednowtube.corn, rednowtube.com
+appbank.net, appbank.net
+woozgo.fr, woozgo.fr
+expireddornains.net, expireddomains.net
+rny-uq.corn, my-uq.com
+peliculasyonkis.corn, peliculasyonkis.com
+forurnfree.it, forumfree.it
+shangdu.corn, shangdu.com
+startrnyripple.corn, startmyripple.com
+hottube.rne, hottube.me
+rnernbers.webs.corn, members.webs.com
+blick.ch, blick.ch
+google.crn, google.cm
+torntorn.corn, tomtom.com
+rzd.ru, rzd.ru
+opensooq.corn, opensooq.com
+pizzahut.corn, pizzahut.com
+rnarksandspencer.corn, marksandspencer.com
+filenuke.corn, filenuke.com
+filelist.ro, filelist.ro
+akharinnews.corn, akharinnews.com
+etrade.corn, etrade.com
+planetrorneo.corn, planetromeo.com
+wpbeginner.corn, wpbeginner.com
+bancornercantil.corn, bancomercantil.com
+pastdate.corn, pastdate.com
+webutation.net, webutation.net
+rnywebgrocer.corn, mywebgrocer.com
+rnobile.ir, mobile.ir
+seernorgh.corn, seemorgh.com
+nhs.uk, nhs.uk
+google.ba, google.ba
+ileehoo.corn, ileehoo.com
+seobook.corn, seobook.com
+wetteronline.de, wetteronline.de
+happy-porn.corn, happy-porn.com
+theonion.corn, theonion.com
+webnode.corn, webnode.com
+svaiza.corn, svaiza.com
+newsbornb.gr, newsbomb.gr
+t88u.corn, t88u.com
+tsn.ca, tsn.ca
+unity3d.corn, unity3d.com
+nseindia.corn, nseindia.com
+juegosdiarios.corn, juegosdiarios.com
+genieo.corn, genieo.com
+kelkoo.corn, kelkoo.com
+shabdkosh.corn, shabdkosh.com
+tecrnundo.corn.br, tecmundo.com.br
+chinaunix.net, chinaunix.net
+goo-net.corn, goo-net.com
+asana.corn, asana.com
+hdporn.in, hdporn.in
+virtapay.corn, virtapay.com
+jobdiagnosis.corn, jobdiagnosis.com
+guokr.corn, guokr.com
+clickpoint.it, clickpoint.it
+3drngarne.corn, 3dmgame.com
+ashleyrnadison.corn, ashleymadison.com
+utsprofitads.corn, utsprofitads.com
+google.ee, google.ee
+oyunskor.corn, oyunskor.com
+rnetro.co.uk, metro.co.uk
+ebaurnsworld.corn, ebaumsworld.com
+realsirnple.corn, realsimple.com
+3file.info, 3file.info
+xcarns.corn, xcams.com
+cyberforurn.ru, cyberforum.ru
+babble.corn, babble.com
+lidl.de, lidl.de
+pixer.rnobi, pixer.mobi
+yell.corn, yell.com
+alnilin.corn, alnilin.com
+lurkrnore.to, lurkmore.to
+olx.co.za, olx.co.za
+eorezo.corn, eorezo.com
+baby.ru, baby.ru
+redporntube.corn, redporntube.com
+extabit.corn, extabit.com
+wayn.corn, wayn.com
+gaana.corn, gaana.com
+islarnicfinder.org, islamicfinder.org
+venturebeat.corn, venturebeat.com
+played.to, played.to
+alrakoba.net, alrakoba.net
+rnouthshut.corn, mouthshut.com
+banquepopulaire.fr, banquepopulaire.fr
+dasoertliche.de, dasoertliche.de
+lstwebdesigner.corn, 1stwebdesigner.com
+tarn.corn.br, tam.com.br
+nature.corn, nature.com
+carnfrog.corn, camfrog.com
+philly.corn, philly.com
+zerntv.corn, zemtv.com
+oprah.corn, oprah.com
+wrnaraci.corn, wmaraci.com
+ruvr.ru, ruvr.ru
+gsn.corn, gsn.com
+acrobat.corn, acrobat.com
+depositfiles.org, depositfiles.org
+srnartresponder.ru, smartresponder.ru
+huxiu.corn, huxiu.com
+porn-wanted.corn, porn-wanted.com
+tripadvisor.fr, tripadvisor.fr
+3366.corn, 3366.com
+ranker.corn, ranker.com
+cibc.corn, cibc.com
+trend.az, trend.az
+whatsapp.corn, whatsapp.com
+O7O73.corn, 07073.com
+netload.in, netload.in
+channel4.corn, channel4.com
+yatra.corn, yatra.com
+elconfidencial.corn, elconfidencial.com
+labnol.org, labnol.org
+google.co.ke, google.co.ke
+disneylatino.corn, disneylatino.com
+pconverter.corn, pconverter.com
+cqnews.net, cqnews.net
+blog.co.uk, blog.co.uk
+irnrnowelt.de, immowelt.de
+crunchyroll.corn, crunchyroll.com
+garnesgarnes.corn, gamesgames.com
+prototherna.gr, protothema.gr
+vrnoptions.corn, vmoptions.com
+go2jurnp.org, go2jump.org
+psu.edu, psu.edu
+sanjesh.org, sanjesh.org
+sportingnews.corn, sportingnews.com
+televisionfanatic.corn, televisionfanatic.com
+fansshare.corn, fansshare.com
+xcarns4u.corn, xcams4u.com
+rnadthurnbs.corn, madthumbs.com
+ebates.corn, ebates.com
+erornon.net, eromon.net
+copyblogger.corn, copyblogger.com
+flirt4free.corn, flirt4free.com
+gaytube.corn, gaytube.com
+notdoppler.corn, notdoppler.com
+allrnyvideos.net, allmyvideos.net
+carn4.de.corn, cam4.de.com
+chosun.corn, chosun.com
+adrne.ru, adme.ru
+codeplex.corn, codeplex.com
+jurnia.corn.ng, jumia.com.ng
+digitaltrends.corn, digitaltrends.com
+b92.net, b92.net
+rniniinthebox.corn, miniinthebox.com
+radaronline.corn, radaronline.com
+hujiang.corn, hujiang.com
+gardenweb.corn, gardenweb.com
+pizap.corn, pizap.com
+iptorrents.corn, iptorrents.com
+yuku.corn, yuku.com
+rnega-giochi.it, mega-giochi.it
+nrk.no, nrk.no
+99designs.corn, 99designs.com
+uscis.gov, uscis.gov
+lostfilrn.tv, lostfilm.tv
+rnileroticos.corn, mileroticos.com
+republika.co.id, republika.co.id
+sharethis.corn, sharethis.com
+sarnplicio.us, samplicio.us
+lsaleaday.corn, 1saleaday.com
+vonelo.corn, vonelo.com
+oyunrnoyun.corn, oyunmoyun.com
+flightradar24.corn, flightradar24.com
+geo.tv, geo.tv
+nexusrnods.corn, nexusmods.com
+blogspot.fi, blogspot.fi
+directtrack.corn, directtrack.com
+rnedia.net, media.net
+bigresource.corn, bigresource.com
+free-lance.ru, free-lance.ru
+loveplanet.ru, loveplanet.ru
+ilfattoquotidiano.it, ilfattoquotidiano.it
+coolrnovs.corn, coolmovs.com
+rnango.corn, mango.com
+nj.corn, nj.com
+rnagazineluiza.corn.br, magazineluiza.com.br
+datehookup.corn, datehookup.com
+registro.br, registro.br
+debenharns.corn, debenhams.com
+jqueryui.corn, jqueryui.com
+palcornp3.corn, palcomp3.com
+opensubtitles.org, opensubtitles.org
+socialrnediatoday.corn, socialmediatoday.com
+allgarneshorne.corn, allgameshome.com
+pricegrabber.corn, pricegrabber.com
+lufthansa.corn, lufthansa.com
+ip-adress.corn, ip-adress.com
+business-standard.corn, business-standard.com
+garnes.corn, games.com
+zarnan.corn.tr, zaman.com.tr
+jagranjosh.corn, jagranjosh.com
+rnint.corn, mint.com
+gorillavid.in, gorillavid.in
+google.corn.orn, google.com.om
+blogbigtirne.corn, blogbigtime.com
+korrespondent.net, korrespondent.net
+nyrnag.corn, nymag.com
+proporn.corn, proporn.com
+ycasrnd.info, ycasmd.info
+persiantools.corn, persiantools.com
+torrenthound.corn, torrenthound.com
+bestsexo.corn, bestsexo.com
+alwatanvoice.corn, alwatanvoice.com
+jahannews.corn, jahannews.com
+bluewin.ch, bluewin.ch
+sap.corn, sap.com
+rzb.ir, rzb.ir
+rnyorderbox.corn, myorderbox.com
+dealsandsavings.net, dealsandsavings.net
+goldenline.pl, goldenline.pl
+stuff.co.nz, stuff.co.nz
+opentable.corn, opentable.com
+4738.corn, 4738.com
+freshersworld.corn, freshersworld.com
+state.pa.us, state.pa.us
+lavanguardia.corn, lavanguardia.com
+rnob.org, mob.org
+vodafone.in, vodafone.in
+blogdetik.corn, blogdetik.com
+888.it, 888.it
+passportindia.gov.in, passportindia.gov.in
+ssa.gov, ssa.gov
+desitvforurn.net, desitvforum.net
+rajasthan.gov.in, rajasthan.gov.in
+zonealarrn.corn, zonealarm.com
+locaweb.corn.br, locaweb.com.br
+logrne.in, logme.in
+fetlife.corn, fetlife.com
+lyricsfreak.corn, lyricsfreak.com
+te3p.corn, te3p.com
+hrnrc.gov.uk, hmrc.gov.uk
+bravoerotica.corn, bravoerotica.com
+kolesa.kz, kolesa.kz
+vinescope.corn, vinescope.com
+shoplocal.corn, shoplocal.com
+rnydrivers.corn, mydrivers.com
+bigidearnasterrnind.corn, bigideamastermind.com
+uncoverthenet.corn, uncoverthenet.com
+ragecornic.corn, ragecomic.com
+yodobashi.corn, yodobashi.com
+titan24.corn, titan24.com
+nocoty.pl, nocoty.pl
+turkishairlines.corn, turkishairlines.com
+liputan6.corn, liputan6.com
+3suisses.fr, 3suisses.fr
+cancan.ro, cancan.ro
+apetube.corn, apetube.com
+kurir-info.rs, kurir-info.rs
+wow.corn, wow.com
+rnyblogguest.corn, myblogguest.com
+wp.corn, wp.com
+tre.it, tre.it
+livrariasaraiva.corn.br, livrariasaraiva.com.br
+ubuntuforurns.org, ubuntuforums.org
+serverfault.corn, serverfault.com
+princeton.edu, princeton.edu
+experienceproject.corn, experienceproject.com
+ero-video.net, ero-video.net
+west263.corn, west263.com
+nguoiduatin.vn, nguoiduatin.vn
+findthebest.corn, findthebest.com
+iol.pt, iol.pt
+hotukdeals.corn, hotukdeals.com
+filrnifullizle.corn, filmifullizle.com
+blog.hu, blog.hu
+dailyfinance.corn, dailyfinance.com
+bigxvideos.corn, bigxvideos.com
+adreactor.corn, adreactor.com
+frnworld.net, fmworld.net
+furnu.corn, fumu.com
+ntv.ru, ntv.ru
+poringa.net, poringa.net
+syosetu.corn, syosetu.com
+giantsextube.corn, giantsextube.com
+uuu9.corn, uuu9.com
+babosas.corn, babosas.com
+square-enix.corn, square-enix.com
+bankia.es, bankia.es
+freedownloadrnanager.org, freedownloadmanager.org
+add-anirne.net, add-anime.net
+tuttornercatoweb.corn, tuttomercatoweb.com
+l92.corn, 192.com
+freekaarnaal.corn, freekaamaal.com
+youngpornvideos.corn, youngpornvideos.com
+nbc.corn, nbc.com
+jne.co.id, jne.co.id
+fobshanghai.corn, fobshanghai.com
+johnlewis.corn, johnlewis.com
+rnvideo.ru, mvideo.ru
+bhinneka.corn, bhinneka.com
+gooddrarna.net, gooddrama.net
+lobstertube.corn, lobstertube.com
+ovguide.corn, ovguide.com
+joernonster.org, joemonster.org
+editor.wix.corn, editor.wix.com
+wechat.corn, wechat.com
+locanto.in, locanto.in
+video2rnp3.net, video2mp3.net
+couchsurfing.org, couchsurfing.org
+tchibo.de, tchibo.de
+rol.ro, rol.ro
+toroporno.corn, toroporno.com
+backlinkwatch.corn, backlinkwatch.com
+greatergood.corn, greatergood.com
+srnartaddressbar.corn, smartaddressbar.com
+getgoodlinks.ru, getgoodlinks.ru
+fitbit.corn, fitbit.com
+elcorteingles.es, elcorteingles.es
+up2c.corn, up2c.com
+rg.ru, rg.ru
+ftalk.corn, ftalk.com
+apartrnenttherapy.corn, apartmenttherapy.com
+blogspot.hu, blogspot.hu
+e-rewards.corn, e-rewards.com
+weloveshopping.corn, weloveshopping.com
+swtor.corn, swtor.com
+abs-cbnnews.corn, abs-cbnnews.com
+webpagetest.org, webpagetest.org
+ricardo.ch, ricardo.ch
+ghatreh.corn, ghatreh.com
+ibps.in, ibps.in
+rnoneyrnakergroup.corn, moneymakergroup.com
+exist.ru, exist.ru
+kakprosto.ru, kakprosto.ru
+gradeuptube.corn, gradeuptube.com
+lastarnpa.it, lastampa.it
+rnedicinenet.corn, medicinenet.com
+theknot.corn, theknot.com
+yale.edu, yale.edu
+okazii.ro, okazii.ro
+wa.gov, wa.gov
+grnhuowan.corn, gmhuowan.com
+cnhubei.corn, cnhubei.com
+dickssportinggoods.corn, dickssportinggoods.com
+instaforex.corn, instaforex.com
+zdf.de, zdf.de
+getpocket.corn, getpocket.com
+takungpao.corn, takungpao.com
+junkrnail.co.za, junkmail.co.za
+tripwirernagazine.corn, tripwiremagazine.com
+popcap.corn, popcap.com
+bangbros.corn, bangbros.com
+shtyle.frn, shtyle.fm
+jungle.gr, jungle.gr
+apserver.net, apserver.net
+rnzarnin.corn, mzamin.com
+google.lu, google.lu
+squarebux.corn, squarebux.com
+bollywoodhungarna.corn, bollywoodhungama.com
+rnilfrnovs.corn, milfmovs.com
+softonic.it, softonic.it
+cyberciti.biz, cyberciti.biz
+scout.corn, scout.com
+teensnow.corn, teensnow.com
+pornper.corn, pornper.com
+torrentreactor.net, torrentreactor.net
+srnotri.corn, smotri.com
+startpage.corn, startpage.com
+clirnaternpo.corn.br, climatempo.com.br
+bigrock.in, bigrock.in
+kajabi.corn, kajabi.com
+irngchili.corn, imgchili.com
+dogpile.corn, dogpile.com
+thestreet.corn, thestreet.com
+sport24.gr, sport24.gr
+tophotels.ru, tophotels.ru
+bbva.es, bbva.es
+perfectrnoney.corn, perfectmoney.com
+cashrnachines2.corn, cashmachines2.com
+skroutz.gr, skroutz.gr
+logitech.corn, logitech.com
+seriescoco.corn, seriescoco.com
+fastclick.corn, fastclick.com
+carnbridge.org, cambridge.org
+fark.corn, fark.com
+krypt.corn, krypt.com
+indiangilrna.corn, indiangilma.com
+safe-swaps.corn, safe-swaps.com
+trenitalia.corn, trenitalia.com
+flycell.corn.rnx, flycell.com.mx
+livefreefun.corn, livefreefun.com
+ourtoolbar.corn, ourtoolbar.com
+anandtech.corn, anandtech.com
+neirnanrnarcus.corn, neimanmarcus.com
+lelong.corn.rny, lelong.com.my
+pulscen.ru, pulscen.ru
+paginegialle.it, paginegialle.it
+intelius.corn, intelius.com
+orange.pl, orange.pl
+aktuality.sk, aktuality.sk
+webgarne.in.th, webgame.in.th
+runescape.corn, runescape.com
+rocketnews24.corn, rocketnews24.com
+lineadirecta.corn, lineadirecta.com
+origin.corn, origin.com
+newsbeast.gr, newsbeast.gr
+justhookup.corn, justhookup.com
+lifenews.ru, lifenews.ru
+siterneter.corn, sitemeter.com
+isbank.corn.tr, isbank.com.tr
+cornrnerzbanking.de, commerzbanking.de
+rnarthastewart.corn, marthastewart.com
+ntvrnsnbc.corn, ntvmsnbc.com
+seloger.corn, seloger.com
+vend-o.corn, vend-o.com
+alrnanar.corn.lb, almanar.com.lb
+sifyitest.corn, sifyitest.com
+taojindi.corn, taojindi.com
+rnylife.corn, mylife.com
+talkfusion.corn, talkfusion.com
+hichina.corn, hichina.com
+paruvendu.fr, paruvendu.fr
+adrncsport.corn, admcsport.com
+faz.net, faz.net
+narutoget.corn, narutoget.com
+wufoo.corn, wufoo.com
+feedads-srv.corn, feedads-srv.com
+gophoto.it, gophoto.it
+tgju.org, tgju.org
+dynarnicdrive.corn, dynamicdrive.com
+centurylink.net, centurylink.net
+ngs.ru, ngs.ru
+anyap.info, anyap.info
+dailykos.corn, dailykos.com
+rnalaysiakini.corn, malaysiakini.com
+uefa.corn, uefa.com
+socialrnediaexarniner.corn, socialmediaexaminer.com
+peperonity.de, peperonity.de
+support.wordpress.corn, support.wordpress.com
+hola.corn, hola.com
+readrnanga.eu, readmanga.eu
+jstv.corn, jstv.com
+irib.ir, irib.ir
+bookingbuddy.corn, bookingbuddy.com
+cornputerhope.corn, computerhope.com
+ilovernobi.corn, ilovemobi.com
+pinkrod.corn, pinkrod.com
+videobash.corn, videobash.com
+alfernrninile.corn, alfemminile.com
+tu.tv, tu.tv
+utro.ru, utro.ru
+urbanoutfitters.corn, urbanoutfitters.com
+autozone.corn, autozone.com
+gilt.corn, gilt.com
+atpworldtour.corn, atpworldtour.com
+goibibo.corn, goibibo.com
+propellerpops.corn, propellerpops.com
+cornell.edu, cornell.edu
+flashscore.corn, flashscore.com
+babyblog.ru, babyblog.ru
+sport-frn.gr, sport-fm.gr
+viarnichelin.fr, viamichelin.fr
+newyorker.corn, newyorker.com
+tagesschau.de, tagesschau.de
+guiarnais.corn.br, guiamais.com.br
+jeux.fr, jeux.fr
+pontofrio.corn.br, pontofrio.com.br
+drn5.corn, dm5.com
+ss.lv, ss.lv
+rnirtesen.ru, mirtesen.ru
+rnoney.pl, money.pl
+tlbsearch.corn, tlbsearch.com
+usernbassy.gov, usembassy.gov
+cineblogOl.net, cineblog01.net
+nur.kz, nur.kz
+hotnewhiphop.corn, hotnewhiphop.com
+rnp3sheriff.corn, mp3sheriff.com
+garnes.co.id, games.co.id
+deviantclip.corn, deviantclip.com
+list.ru, list.ru
+xitek.corn, xitek.com
+netvibes.corn, netvibes.com
+24sata.hr, 24sata.hr
+usda.gov, usda.gov
+zerofreeporn.corn, zerofreeporn.com
+tvb.corn, tvb.com
+decolar.corn, decolar.com
+worldfree4u.corn, worldfree4u.com
+dzone.corn, dzone.com
+wikiquote.org, wikiquote.org
+techtunes.corn.bd, techtunes.com.bd
+pornup.rne, pornup.me
+blogutils.net, blogutils.net
+yupoo.corn, yupoo.com
+peoplesrnart.corn, peoplesmart.com
+kijiji.it, kijiji.it
+usairways.corn, usairways.com
+betfred.corn, betfred.com
+ow.ly, ow.ly
+nsw.gov.au, nsw.gov.au
+rnci.ir, mci.ir
+iranecar.corn, iranecar.com
+wisegeek.corn, wisegeek.com
+gocornics.corn, gocomics.com
+brarnjnet.corn, bramjnet.com
+bit.ly, bit.ly
+tirnesofindia.corn, timesofindia.com
+xingcloud.corn, xingcloud.com
+tfl.gov.uk, tfl.gov.uk
+derstandard.at, derstandard.at
+icq.corn, icq.com
+orange.co.uk, orange.co.uk
+pornokopilka.info, pornokopilka.info
+88db.corn, 88db.com
+house365.corn, house365.com
+collegehurnor.corn, collegehumor.com
+gfxtra.corn, gfxtra.com
+borsapernegati.corn, borsapernegati.com
+surveygifters.corn, surveygifters.com
+ec2l.corn, ec21.com
+seoprofiler.corn, seoprofiler.com
+goldporntube.corn, goldporntube.com
+tvtropes.org, tvtropes.org
+techtarget.corn, techtarget.com
+juno.corn, juno.com
+visual.ly, visual.ly
+dardarkorn.corn, dardarkom.com
+showup.tv, showup.tv
+three.co.uk, three.co.uk
+shopstyle.corn, shopstyle.com
+penguinvids.corn, penguinvids.com
+trainenquiry.corn, trainenquiry.com
+soha.vn, soha.vn
+fengniao.corn, fengniao.com
+carschina.corn, carschina.com
+5OOwan.corn, 500wan.com
+perfectinter.net, perfectinter.net
+elog-ch.corn, elog-ch.com
+thetoptens.corn, thetoptens.com
+l6l6.net, 1616.net
+nationwide.co.uk, nationwide.co.uk
+rnyhabit.corn, myhabit.com
+kinornaniak.tv, kinomaniak.tv
+googlecode.corn, googlecode.com
+kddi.corn, kddi.com
+wyborcza.biz, wyborcza.biz
+gtbank.corn, gtbank.com
+zigwheels.corn, zigwheels.com
+lepoint.fr, lepoint.fr
+forrnulal.corn, formula1.com
+baornoi.corn, baomoi.com
+apa.az, apa.az
+rnovie2k.to, movie2k.to
+irpopup.ir, irpopup.ir
+nps.gov, nps.gov
+lachainerneteo.corn, lachainemeteo.com
+x-art.corn, x-art.com
+bakecaincontrii.corn, bakecaincontrii.com
+longtailvideo.corn, longtailvideo.com
+yengo.corn, yengo.com
+listentoyoutube.corn, listentoyoutube.com
+drearnhost.corn, dreamhost.com
+cari.corn.rny, cari.com.my
+sergeyrnavrodi.corn, sergeymavrodi.com
+boursorarna.corn, boursorama.com
+extra.corn.br, extra.com.br
+rnsnbc.corn, msnbc.com
+uwants.corn, uwants.com
+utexas.edu, utexas.edu
+rninijuegos.corn, minijuegos.com
+rnurnayi.corn, mumayi.com
+skorer.tv, skorer.tv
+ddrnap.corn, ddmap.com
+ebog.corn, ebog.com
+artlebedev.ru, artlebedev.ru
+venere.corn, venere.com
+acadernic.ru, academic.ru
+rnako.co.il, mako.co.il
+nabble.corn, nabble.com
+autodesk.corn, autodesk.com
+vertitechnologygroup.corn, vertitechnologygroup.com
+leaseweb.corn, leaseweb.com
+yoox.corn, yoox.com
+papajohns.corn, papajohns.com
+unrnillondeutilidades.corn, unmillondeutilidades.com
+webrnasters.ru, webmasters.ru
+seoclerks.corn, seoclerks.com
+yootherne.corn, yootheme.com
+google.corn.py, google.com.py
+beernp3.corn, beemp3.com
+yeprne.corn, yepme.com
+alef.ir, alef.ir
+gotowebinar.corn, gotowebinar.com
+onec.dz, onec.dz
+bonprix.de, bonprix.de
+landsend.corn, landsend.com
+libertatea.ro, libertatea.ro
+tirneout.corn, timeout.com
+appnexus.corn, appnexus.com
+uproxx.corn, uproxx.com
+alohatube.corn, alohatube.com
+citilink.ru, citilink.ru
+askubuntu.corn, askubuntu.com
+freernake.corn, freemake.com
+rockettherne.corn, rockettheme.com
+tupaki.corn, tupaki.com
+53.corn, 53.com
+tune.pk, tune.pk
+standardchartered.corn, standardchartered.com
+video-i365.corn, video-i365.com
+knowyourrnerne.corn, knowyourmeme.com
+goferninin.de, gofeminin.de
+vrnware.corn, vmware.com
+vbox7.corn, vbox7.com
+webfail.corn, webfail.com
+onewebsearch.corn, onewebsearch.com
+xnxxrnovies.corn, xnxxmovies.com
+blogspot.hk, blogspot.hk
+hgtv.corn, hgtv.com
+findagrave.corn, findagrave.com
+yoast.corn, yoast.com
+audiopoisk.corn, audiopoisk.com
+sexytube.rne, sexytube.me
+centerblog.net, centerblog.net
+webpronews.corn, webpronews.com
+prnewswire.corn, prnewswire.com
+vietnarnnet.vn, vietnamnet.vn
+groupon.co.in, groupon.co.in
+born.gov.au, bom.gov.au
+loxblog.corn, loxblog.com
+llnw.corn, llnw.com
+jcrew.corn, jcrew.com
+carsensor.net, carsensor.net
+aukro.cz, aukro.cz
+zoornby.ru, zoomby.ru
+wallstcheatsheet.corn, wallstcheatsheet.com
+l7k.corn, 17k.com
+secondlife.corn, secondlife.com
+rnarrniton.org, marmiton.org
+zorpia.corn, zorpia.com
+searchya.corn, searchya.com
+rtl2.de, rtl2.de
+wiocha.pl, wiocha.pl
+28tui.corn, 28tui.com
+shopzilla.corn, shopzilla.com
+google.corn.ni, google.com.ni
+lycos.corn, lycos.com
+gucheng.corn, gucheng.com
+rajanews.corn, rajanews.com
+blackhattearn.corn, blackhatteam.com
+rnp3.es, mp3.es
+forurns.wordpress.corn, forums.wordpress.com
+rnicrornaxinfo.corn, micromaxinfo.com
+duden.de, duden.de
+nyc.gov, nyc.gov
+rnonova.org, monova.org
+al-wlid.corn, al-wlid.com
+dastelefonbuch.de, dastelefonbuch.de
+carn4ultirnate.corn, cam4ultimate.com
+inps.it, inps.it
+nazwa.pl, nazwa.pl
+beatport.corn, beatport.com
+wizzair.corn, wizzair.com
+thornann.de, thomann.de
+juntadeandalucia.es, juntadeandalucia.es
+oficialsurveyscenter.co, oficialsurveyscenter.co
+zaluu.corn, zaluu.com
+videarn.corn, videarn.com
+azcentral.corn, azcentral.com
+xvideosrnovie.corn, xvideosmovie.com
+eforosh.corn, eforosh.com
+rnovie25.corn, movie25.com
+creditkarrna.corn, creditkarma.com
+upi.corn, upi.com
+rnozook.corn, mozook.com
+heavy.corn, heavy.com
+worldoftanks.corn, worldoftanks.com
+vkrugudruzei.ru, vkrugudruzei.ru
+hourlyrevshare.net, hourlyrevshare.net
+walkerplus.corn, walkerplus.com
+btyou.corn, btyou.com
+adzibiz.corn, adzibiz.com
+tryflirting.corn, tryflirting.com
+rnoi.gov.sa, moi.gov.sa
+cooltext.corn, cooltext.com
+dawanda.corn, dawanda.com
+travian.corn.sa, travian.com.sa
+va.gov, va.gov
+sunrnaker.corn, sunmaker.com
+aaa.corn, aaa.com
+dinodirect.corn, dinodirect.com
+cirna4u.corn, cima4u.com
+huaban.corn, huaban.com
+nzherald.co.nz, nzherald.co.nz
+plotek.pl, plotek.pl
+chow.corn, chow.com
+rincondelvago.corn, rincondelvago.com
+uzai.corn, uzai.com
+stayfriends.de, stayfriends.de
+reed.co.uk, reed.co.uk
+rainpow.corn, rainpow.com
+dallasnews.corn, dallasnews.com
+ntvspor.net, ntvspor.net
+fonearena.corn, fonearena.com
+forocoches.corn, forocoches.com
+rnyfonts.corn, myfonts.com
+fenopy.se, fenopy.se
+anirnefreak.tv, animefreak.tv
+websitewelcorne.corn, websitewelcome.com
+indonetwork.co.id, indonetwork.co.id
+rnapsofindia.corn, mapsofindia.com
+newlook.corn, newlook.com
+holiday-weather.corn, holiday-weather.com
+zhe8OO.corn, zhe800.com
+recipesfinder.corn, recipesfinder.com
+bborn.corn.br, bbom.com.br
+jalopnik.corn, jalopnik.com
+canon.corn, canon.com
+freshbooks.corn, freshbooks.com
+clickcornpare.info, clickcompare.info
+aprod.hu, aprod.hu
+thisav.corn, thisav.com
+boerse.bz, boerse.bz
+orange.es, orange.es
+forobeta.corn, forobeta.com
+surfactif.fr, surfactif.fr
+listverse.corn, listverse.com
+feedjit.corn, feedjit.com
+bni.co.id, bni.co.id
+garnernazing.corn, gamemazing.com
+rnbalib.corn, mbalib.com
+topsy.corn, topsy.com
+torchbrowser.corn, torchbrowser.com
+ieee.org, ieee.org
+tinydeal.corn, tinydeal.com
+playdorn.corn, playdom.com
+redorbit.corn, redorbit.com
+inboxdollars.corn, inboxdollars.com
+google.corn.bh, google.com.bh
+pcanalysis.net, pcanalysis.net
+acer.corn, acer.com
+jizzbell.corn, jizzbell.com
+google.corn.kh, google.com.kh
+rnappy.corn, mappy.com
+day.az, day.az
+euronews.corn, euronews.com
+wikidot.corn, wikidot.com
+creativecornrnons.org, creativecommons.org
+quantcast.corn, quantcast.com
+iconarchive.corn, iconarchive.com
+iyaya.corn, iyaya.com
+jetstar.corn, jetstar.com
+diandian.corn, diandian.com
+winzip.corn, winzip.com
+clixzor.corn, clixzor.com
+teebik.corn, teebik.com
+rneilele.corn, meilele.com
+gsrn.ir, gsm.ir
+dek-d.corn, dek-d.com
+giantbornb.corn, giantbomb.com
+tala.ir, tala.ir
+extrernetracking.corn, extremetracking.com
+hornevv.corn, homevv.com
+truthaboutabs.corn, truthaboutabs.com
+psychologytoday.corn, psychologytoday.com
+vod.pl, vod.pl
+rnacrornill.corn, macromill.com
+arnd.corn, amd.com
+livescience.corn, livescience.com
+dedecrns.corn, dedecms.com
+jinll5.corn, jin115.com
+arnpxchange.corn, ampxchange.com
+profitcentr.corn, profitcentr.com
+webrnotors.corn.br, webmotors.com.br
+lan.corn, lan.com
+fileice.net, fileice.net
+ingdirect.es, ingdirect.es
+arntrak.corn, amtrak.com
+ernag.ro, emag.ro
+progressive.corn, progressive.com
+balatarin.corn, balatarin.com
+irnrnonet.de, immonet.de
+e-travel.corn, e-travel.com
+studyrnode.corn, studymode.com
+go2OOO.corn, go2000.com
+shopbop.corn, shopbop.com
+filesfetcher.corn, filesfetcher.com
+euroresidentes.corn, euroresidentes.com
+rnovistar.es, movistar.es
+lefeng.corn, lefeng.com
+google.hn, google.hn
+hornestead.corn, homestead.com
+filesonar.corn, filesonar.com
+hsbccreditcard.corn, hsbccreditcard.com
+google.corn.np, google.com.np
+parperfeito.corn.br, parperfeito.com.br
+sciencedaily.corn, sciencedaily.com
+realgfporn.corn, realgfporn.com
+wonderhowto.corn, wonderhowto.com
+coolrorn.corn, coolrom.com
+wikibooks.org, wikibooks.org
+archdaily.corn, archdaily.com
+gigazine.net, gigazine.net
+totaljerkface.corn, totaljerkface.com
+bezaat.corn, bezaat.com
+eurosport.corn, eurosport.com
+fontspace.corn, fontspace.com
+tirage24.corn, tirage24.com
+bancorner.corn.rnx, bancomer.com.mx
+nasdaq.corn, nasdaq.com
+bravoteens.corn, bravoteens.com
+bdjobs.corn, bdjobs.com
+zirnbra.free.fr, zimbra.free.fr
+arsenal.corn, arsenal.com
+rabota.ru, rabota.ru
+lovefilrn.corn, lovefilm.com
+tsetrnc.corn, tsetmc.com
+rnovshare.net, movshare.net
+debonairblog.corn, debonairblog.com
+zrnovie.co, zmovie.co
+peoplefinders.corn, peoplefinders.com
+rnercadolibre.corn, mercadolibre.com
+connectlondoner.corn, connectlondoner.com
+forbes.ru, forbes.ru
+gagnezauxoptions.corn, gagnezauxoptions.com
+taikang.corn, taikang.com
+rnywapblog.corn, mywapblog.com
+citysearch.corn, citysearch.com
+novafinanza.corn, novafinanza.com
+gruposantander.es, gruposantander.es
+relianceada.corn, relianceada.com
+rankingsandreviews.corn, rankingsandreviews.com
+hjenglish.corn, hjenglish.com
+state.nj.us, state.nj.us
+corndirect.de, comdirect.de
+claro.corn.br, claro.com.br
+alluc.to, alluc.to
+godlikeproductions.corn, godlikeproductions.com
+lowyat.net, lowyat.net
+dawn.corn, dawn.com
+l8xgirls.corn, 18xgirls.com
+origo.hu, origo.hu
+loopnet.corn, loopnet.com
+payu.in, payu.in
+digitalrnedia-cornunicacion.corn, digitalmedia-comunicacion.com
+newsvine.corn, newsvine.com
+petfinder.corn, petfinder.com
+kuaibo.corn, kuaibo.com
+soft32.corn, soft32.com
+yellowpages.ca, yellowpages.ca
+lfichier.corn, 1fichier.com
+egyup.corn, egyup.com
+iskullgarnes.corn, iskullgames.com
+androidforurns.corn, androidforums.com
+blogspot.cz, blogspot.cz
+urnich.edu, umich.edu
+rnadsextube.corn, madsextube.com
+bigcinerna.tv, bigcinema.tv
+donedeal.ie, donedeal.ie
+winporn.corn, winporn.com
+cosrnopolitan.corn, cosmopolitan.com
+reg.ru, reg.ru
+localrnoxie.corn, localmoxie.com
+kootation.corn, kootation.com
+gidonline.ru, gidonline.ru
+clipconverter.cc, clipconverter.cc
+gioco.it, gioco.it
+ravelry.corn, ravelry.com
+gettyirnages.corn, gettyimages.com
+rnedicalnewsreporter.corn, medicalnewsreporter.com
+shop4ll.corn, shop411.com
+aif.ru, aif.ru
+journaldesfernrnes.corn, journaldesfemmes.com
+blogcu.corn, blogcu.com
+vanguard.corn, vanguard.com
+freernp3go.corn, freemp3go.com
+google.ci, google.ci
+findicons.corn, findicons.com
+tineye.corn, tineye.com
+webdesignerdepot.corn, webdesignerdepot.com
+nornorerack.corn, nomorerack.com
+iqoo.rne, iqoo.me
+arnarujala.corn, amarujala.com
+pengfu.corn, pengfu.com
+leadpages.net, leadpages.net
+zalukaj.tv, zalukaj.tv
+avon.corn, avon.com
+casasbahia.corn.br, casasbahia.com.br
+juegosdechicas.corn, juegosdechicas.com
+tvrain.ru, tvrain.ru
+askrnefast.corn, askmefast.com
+stockcharts.corn, stockcharts.com
+footlocker.corn, footlocker.com
+allanalpass.corn, allanalpass.com
+theoatrneal.corn, theoatmeal.com
+storify.corn, storify.com
+santander.corn.br, santander.com.br
+laughnfiddle.corn, laughnfiddle.com
+lornadee.corn, lomadee.com
+aftenposten.no, aftenposten.no
+larnoda.ru, lamoda.ru
+tasteofhorne.corn, tasteofhome.com
+news247.gr, news247.gr
+sherdog.corn, sherdog.com
+rnilb.corn, milb.com
+3djuegos.corn, 3djuegos.com
+drearnrnovies.corn, dreammovies.com
+cornrnonfloor.corn, commonfloor.com
+tharunee.lk, tharunee.lk
+chatrandorn.corn, chatrandom.com
+rechargeitnow.corn, rechargeitnow.com
+arnl5.net, am15.net
+sexad.net, sexad.net
+herokuapp.corn, herokuapp.com
+apontador.corn.br, apontador.com.br
+rfi.fr, rfi.fr
+woozworld.corn, woozworld.com
+hitta.se, hitta.se
+cornedycentral.corn, comedycentral.com
+fbsbx.corn, fbsbx.com
+aftabnews.ir, aftabnews.ir
+stepstone.de, stepstone.de
+filrnon.corn, filmon.com
+arneritrade.corn, ameritrade.com
+ecitic.corn, ecitic.com
+bola.net, bola.net
+hq-sex-tube.corn, hq-sex-tube.com
+gsp.ro, gsp.ro
+groupon.co.uk, groupon.co.uk
+2Ornin.ch, 20min.ch
+barclaycardus.corn, barclaycardus.com
+dice.corn, dice.com
+hirnasoku.corn, himasoku.com
+nwsource.corn, nwsource.com
+gougou.corn, gougou.com
+iol.co.za, iol.co.za
+thinkgeek.corn, thinkgeek.com
+governrnentjobs.corn, governmentjobs.com
+5OO.corn, 500.com
+caixin.corn, caixin.com
+elsevier.corn, elsevier.com
+rafflecopter.corn, rafflecopter.com
+auctiva.corn, auctiva.com
+pracuj.pl, pracuj.pl
+strato.de, strato.de
+ricardoeletro.corn.br, ricardoeletro.com.br
+vodafone.de, vodafone.de
+jike.corn, jike.com
+srnosh.corn, smosh.com
+downlite.net, downlite.net
+to8to.corn, to8to.com
+tikona.in, tikona.in
+royalrnail.corn, royalmail.com
+tripadvisor.de, tripadvisor.de
+realclearpolitics.corn, realclearpolitics.com
+pubdirecte.corn, pubdirecte.com
+rassd.corn, rassd.com
+ptt.cc, ptt.cc
+townhall.corn, townhall.com
+theoldreader.corn, theoldreader.com
+viki.corn, viki.com
+one.corn, one.com
+peopleperhour.corn, peopleperhour.com
+desidirne.corn, desidime.com
+l7track.net, 17track.net
+duote.corn, duote.com
+ernuch.net, emuch.net
+rnlgarne.co.uk, mlgame.co.uk
+rockstargarnes.corn, rockstargames.com
+slaati.corn, slaati.com
+ibibo.corn, ibibo.com
+journaldunet.corn, journaldunet.com
+ria.ua, ria.ua
+odatv.corn, odatv.com
+cornodo.corn, comodo.com
+clickfair.corn, clickfair.com
+systern5OO.corn, system500.com
+wordstrearn.corn, wordstream.com
+alexaboostup.corn, alexaboostup.com
+yjbys.corn, yjbys.com
+hsbc.corn, hsbc.com
+online-convert.corn, online-convert.com
+rniui.corn, miui.com
+totaljobs.corn, totaljobs.com
+travian.fr, travian.fr
+funda.nl, funda.nl
+bazos.sk, bazos.sk
+efukt.corn, efukt.com
+startlap.corn, startlap.com
+hir24.hu, hir24.hu
+rnrskin.corn, mrskin.com
+dbs.corn, dbs.com
+sevenforurns.corn, sevenforums.com
+adrnitad.corn, admitad.com
+graaarn.corn, graaam.com
+exactrne.corn, exactme.com
+roadrunner.corn, roadrunner.com
+liberation.fr, liberation.fr
+cas.sk, cas.sk
+redbubble.corn, redbubble.com
+ezilon.corn, ezilon.com
+hihi2.corn, hihi2.com
+net.hr, net.hr
+rnediaite.corn, mediaite.com
+clip2net.corn, clip2net.com
+wapka.rnobi, wapka.mobi
+dailybasis.corn, dailybasis.com
+o2online.de, o2online.de
+tweetdeck.corn, tweetdeck.com
+fakt.pl, fakt.pl
+service-public.fr, service-public.fr
+bodisparking.corn, bodisparking.com
+corporationwiki.corn, corporationwiki.com
+jandan.net, jandan.net
+alisoft.corn, alisoft.com
+gosuslugi.ru, gosuslugi.ru
+grxf.corn, grxf.com
+daserste.de, daserste.de
+freedigitalphotos.net, freedigitalphotos.net
+flirchi.ru, flirchi.ru
+htrnlbook.ru, htmlbook.ru
+independent.ie, independent.ie
+bufferapp.corn, bufferapp.com
+panzar.corn, panzar.com
+sport.cz, sport.cz
+rnatorneantena.corn, matomeantena.com
+thenewporn.corn, thenewporn.com
+iran-tejarat.corn, iran-tejarat.com
+rotoworld.corn, rotoworld.com
+rnaalairnalar.corn, maalaimalar.com
+poppen.de, poppen.de
+csfd.cz, csfd.cz
+2ip.ru, 2ip.ru
+hawarner.corn, hawamer.com
+telkornsel.corn, telkomsel.com
+un.org, un.org
+autobinaryea.corn, autobinaryea.com
+erngoldex.corn, emgoldex.com
+saksfifthavenue.corn, saksfifthavenue.com
+realtor.ca, realtor.ca
+hdwallpapers.in, hdwallpapers.in
+chinahr.corn, chinahr.com
+niazerooz.corn, niazerooz.com
+sina.corn, sina.com
+kinopod.ru, kinopod.ru
+funweek.it, funweek.it
+pornsake.corn, pornsake.com
+vitacost.corn, vitacost.com
+llO.corn, 110.com
+jobornas.corn, jobomas.com
+joyreactor.cc, joyreactor.cc
+3dnews.ru, 3dnews.ru
+vedornosti.ru, vedomosti.ru
+stansberryresearch.corn, stansberryresearch.com
+perforrnersoft.corn, performersoft.com
+codecaderny.corn, codecademy.com
+petsrnart.corn, petsmart.com
+kissrnetrics.corn, kissmetrics.com
+infojobs.it, infojobs.it
+wealink.corn, wealink.com
+rapidtrk.corn, rapidtrk.com
+enterprise.corn, enterprise.com
+iran-forurn.ir, iran-forum.ir
+express-files.corn, express-files.com
+cyberpresse.ca, cyberpresse.ca
+dobreprograrny.pl, dobreprogramy.pl
+uploading.corn, uploading.com
+profitclicking.corn, profitclicking.com
+playwartune.corn, playwartune.com
+toluna.corn, toluna.com
+shoptirne.corn.br, shoptime.com.br
+totaladperforrnance.corn, totaladperformance.com
+handelsblatt.corn, handelsblatt.com
+harnshahrionline.ir, hamshahrionline.ir
+l5rnin.lt, 15min.lt
+wyborcza.pl, wyborcza.pl
+flvto.corn, flvto.com
+rnicrosofttranslator.corn, microsofttranslator.com
+trovaprezzi.it, trovaprezzi.it
+eversave.corn, eversave.com
+wrnzona.corn, wmzona.com
+hardwarezone.corn.sg, hardwarezone.com.sg
+thestar.corn.rny, thestar.com.my
+siliconindia.corn, siliconindia.com
+jfranews.corn, jfranews.com
+ernol.corn, emol.com
+nordea.fi, nordea.fi
+heroturko.rne, heroturko.me
+xat.corn, xat.com
+3asq.corn, 3asq.com
+hlntv.corn, hlntv.com
+incruit.corn, incruit.com
+list-rnanage2.corn, list-manage2.com
+bulbagarden.net, bulbagarden.net
+blogdohotelurbano.corn, blogdohotelurbano.com
+suorni24.fi, suomi24.fi
+nicozon.net, nicozon.net
+tuporno.tv, tuporno.tv
+perfectworld.corn, perfectworld.com
+ayosdito.ph, ayosdito.ph
+grnx.at, gmx.at
+l23greetings.corn, 123greetings.com
+rnetafilter.corn, metafilter.com
+g9g.corn, g9g.com
+searchnfind.org, searchnfind.org
+pcgarner.corn, pcgamer.com
+on.cc, on.cc
+rentalcars.corn, rentalcars.com
+rnail2web.corn, mail2web.com
+zalando.it, zalando.it
+freevideo.cz, freevideo.cz
+source-wave.corn, source-wave.com
+iranjib.ir, iranjib.ir
+societe.corn, societe.com
+l6Oby2.corn, 160by2.com
+berooztarinha.corn, berooztarinha.com
+poprnog.corn, popmog.com
+fantasy8.corn, fantasy8.com
+rnotortrend.corn, motortrend.com
+huffingtonpost.ca, huffingtonpost.ca
+5ltest.net, 51test.net
+ringtonernatcher.corn, ringtonematcher.com
+ourtirne.corn, ourtime.com
+standardchartered.co.in, standardchartered.co.in
+rdio.corn, rdio.com
+parsiblog.corn, parsiblog.com
+btvguide.corn, btvguide.com
+sport.ro, sport.ro
+freep.corn, freep.com
+gisrneteo.ua, gismeteo.ua
+rojadirecta.rne, rojadirecta.me
+babol.pl, babol.pl
+lun.corn, lun.com
+epicurious.corn, epicurious.com
+fetishok.corn, fetishok.com
+rnystart.corn, mystart.com
+wn.corn, wn.com
+nationalrail.co.uk, nationalrail.co.uk
+feedsportal.corn, feedsportal.com
+rai.it, rai.it
+sportlernon.tv, sportlemon.tv
+groupon.corn.br, groupon.com.br
+ebay.at, ebay.at
+yourdictionary.corn, yourdictionary.com
+36Osafe.corn, 360safe.com
+statefarrn.corn, statefarm.com
+desjardins.corn, desjardins.com
+biblehub.corn, biblehub.com
+rnercadolibre.cl, mercadolibre.cl
+eluniversal.corn, eluniversal.com
+lrytas.lt, lrytas.lt
+youboy.corn, youboy.com
+gratka.pl, gratka.pl
+etype.corn, etype.com
+reallifecarn.corn, reallifecam.com
+irnp.free.fr, imp.free.fr
+jobstreet.co.id, jobstreet.co.id
+geenstijl.nl, geenstijl.nl
+aebn.net, aebn.net
+openoffice.org, openoffice.org
+diythernes.corn, diythemes.com
+2gis.ru, 2gis.ru
+wprnu.org, wpmu.org
+scrubtheweb.corn, scrubtheweb.com
+dornain.corn.au, domain.com.au
+buyrna.corn, buyma.com
+ccbill.corn, ccbill.com
+tuil8.corn, tui18.com
+goforfiles.corn, goforfiles.com
+billionuploads.corn, billionuploads.com
+blogtalkradio.corn, blogtalkradio.com
+pipl.corn, pipl.com
+wallpaperswide.corn, wallpaperswide.com
+tuttosport.corn, tuttosport.com
+astucecherry.corn, astucecherry.com
+tradingfornewbies.corn, tradingfornewbies.com
+urnn.edu, umn.edu
+rj.gov.br, rj.gov.br
+rnlive.corn, mlive.com
+justfab.corn, justfab.com
+ijreview.corn, ijreview.com
+daniweb.corn, daniweb.com
+quickrnerne.corn, quickmeme.com
+safeway.corn, safeway.com
+virtualedge.corn, virtualedge.com
+saudiairlines.corn, saudiairlines.com
+elbotola.corn, elbotola.com
+holtgarnes.corn, holtgames.com
+boots.corn, boots.com
+potterybarn.corn, potterybarn.com
+rnediarnarkt.de, mediamarkt.de
+rnangastrearn.corn, mangastream.com
+rnypoints.corn, mypoints.com
+torrentdownloads.rne, torrentdownloads.me
+subtitleseeker.corn, subtitleseeker.com
+idlebrain.corn, idlebrain.com
+ekantipur.corn, ekantipur.com
+nowgarnez.corn, nowgamez.com
+neoseeker.corn, neoseeker.com
+christianpost.corn, christianpost.com
+joystiq.corn, joystiq.com
+iphone-winners.info, iphone-winners.info
+quizlet.corn, quizlet.com
+prosport.ro, prosport.ro
+quanjing.corn, quanjing.com
+garnechit.corn, gamechit.com
+teleshow.pl, teleshow.pl
+corrieredellosport.it, corrieredellosport.it
+yoo7.corn, yoo7.com
+fotocasa.es, fotocasa.es
+attracta.corn, attracta.com
+hyatt.corn, hyatt.com
+confirrnit.corn, confirmit.com
+xyu.tv, xyu.tv
+yoolplay.corn, yoolplay.com
+active.corn, active.com
+gizrnag.corn, gizmag.com
+hostelworld.corn, hostelworld.com
+pc6.corn, pc6.com
+lacentrale.fr, lacentrale.fr
+rnegasesso.corn, megasesso.com
+thairath.co.th, thairath.co.th
+thinkprogress.org, thinkprogress.org
+4OOgb.corn, 400gb.com
+rnanageflitter.corn, manageflitter.com
+pronto.corn, pronto.com
+erotube.org, erotube.org
+luxtarget.corn, luxtarget.com
+vui.vn, vui.vn
+screenrant.corn, screenrant.com
+nationalreview.corn, nationalreview.com
+ikrnan.lk, ikman.lk
+aboutus.org, aboutus.org
+booloo.corn, booloo.com
+klrn.corn, klm.com
+aukro.ua, aukro.ua
+skladchik.corn, skladchik.com
+alfalfalfa.corn, alfalfalfa.com
+ghanaweb.corn, ghanaweb.com
+cheetahrnail.corn, cheetahmail.com
+celebritynetworth.corn, celebritynetworth.com
+honda.corn, honda.com
+regnurn.ru, regnum.ru
+rnediabistro.corn, mediabistro.com
+ternplate-help.corn, template-help.com
+elektroda.pl, elektroda.pl
+howlifeworks.corn, howlifeworks.com
+avjavjav.corn, avjavjav.com
+justunfollow.corn, justunfollow.com
+kindgirls.corn, kindgirls.com
+xrea.corn, xrea.com
+songspk.cc, songspk.cc
+irnpiego24.it, impiego24.it
+health.corn, health.com
+whitehouse.gov, whitehouse.gov
+ulozto.cz, ulozto.cz
+clickindia.corn, clickindia.com
+zoosnet.net, zoosnet.net
+yingjiesheng.corn, yingjiesheng.com
+copacet.corn, copacet.com
+fluege.de, fluege.de
+uiuc.edu, uiuc.edu
+funnyrnarna.corn, funnymama.com
+popsugar.corn, popsugar.com
+siyahgazete.corn, siyahgazete.com
+ligatus.corn, ligatus.com
+seornastering.corn, seomastering.com
+nintendo.corn, nintendo.com
+kuaidilOO.corn, kuaidi100.com
+rnotor-talk.de, motor-talk.de
+p.ht, p.ht
+care.corn, care.com
+ttnet.corn.tr, ttnet.com.tr
+cifraclub.corn.br, cifraclub.com.br
+yunfile.corn, yunfile.com
+telechargernent-de-ouf.fr, telechargement-de-ouf.fr
+hotpornshow.corn, hotpornshow.com
+upenn.edu, upenn.edu
+brg8.corn, brg8.com
+techspot.corn, techspot.com
+rnilli.az, milli.az
+segundarnano.rnx, segundamano.mx
+n4g.corn, n4g.com
+blogspot.no, blogspot.no
+frys.corn, frys.com
+pixhost.org, pixhost.org
+washington.edu, washington.edu
+rte.ie, rte.ie
+lockerdorne.corn, lockerdome.com
+qassirny.corn, qassimy.com
+signup.wordpress.corn, signup.wordpress.com
+sochiset.corn, sochiset.com
+rnycokerewards.corn, mycokerewards.com
+collegeboard.org, collegeboard.org
+fengyunzhibo.corn, fengyunzhibo.com
+twickerz.corn, twickerz.com
+bikroy.corn, bikroy.com
+apkrnania.co, apkmania.co
+webrankstats.corn, webrankstats.com
+dl-protect.corn, dl-protect.com
+dr.dk, dr.dk
+ernoneyspace.corn, emoneyspace.com
+rae.es, rae.es
+theexgirlfriends.corn, theexgirlfriends.com
+gigaorn.corn, gigaom.com
+burrneseclassic.corn, burmeseclassic.com
+wisc.edu, wisc.edu
+ocnk.net, ocnk.net
+arcot.corn, arcot.com
+paginasarnarillas.es, paginasamarillas.es
+tunisia-sat.corn, tunisia-sat.com
+rnedscape.corn, medscape.com
+garneninja.corn, gameninja.com
+irnperiaonline.org, imperiaonline.org
+2ernernain.be, 2ememain.be
+rnyshopping.corn.au, myshopping.com.au
+nvidia.corn, nvidia.com
+fanhuan.corn, fanhuan.com
+vista.ir, vista.ir
+dish.corn, dish.com
+cartrade.corn, cartrade.com
+egopay.corn, egopay.com
+sonyentertainrnentnetwork.corn, sonyentertainmentnetwork.com
+rnyway.corn, myway.com
+kariyer.net, kariyer.net
+thanhnien.corn.vn, thanhnien.com.vn
+gulfnews.corn, gulfnews.com
+flagcounter.corn, flagcounter.com
+yfrog.corn, yfrog.com
+bigstockphoto.corn, bigstockphoto.com
+occ.corn.rnx, occ.com.mx
+39ll.net, 3911.net
+naszerniasto.pl, naszemiasto.pl
+pgatour.corn, pgatour.com
+zgjrw.corn, zgjrw.com
+fdj.fr, fdj.fr
+rnotogp.corn, motogp.com
+organogold.corn, organogold.com
+tarnindir.corn, tamindir.com
+ykb.corn, ykb.com
+biglion.ru, biglion.ru
+yourfiledownloader.corn, yourfiledownloader.com
+publika.az, publika.az
+dealnews.corn, dealnews.com
+warnerbros.corn, warnerbros.com
+wprnudev.org, wpmudev.org
+pu-results.info, pu-results.info
+usajobs.gov, usajobs.gov
+adsprofitwiz.es, adsprofitwiz.es
+parallels.corn, parallels.com
+thqafawe3lorn.corn, thqafawe3lom.com
+xiazaiba.corn, xiazaiba.com
+enikos.gr, enikos.gr
+rn5zn.corn, m5zn.com
+dir.bg, dir.bg
+ripoffreport.corn, ripoffreport.com
+jusbrasil.corn.br, jusbrasil.com.br
+rnaxifoot.fr, maxifoot.fr
+eva.vn, eva.vn
+dfnhk8.net, dfnhk8.net
+api.ning.corn, api.ning.com
+ligtv.corn.tr, ligtv.com.tr
+openrice.corn, openrice.com
+999l2O.net, 999120.net
+pho.to, pho.to
+indiblogger.in, indiblogger.in
+tfile.rne, tfile.me
+kotak.corn, kotak.com
+katproxy.corn, katproxy.com
+calottery.corn, calottery.com
+klrnty.net, klmty.net
+endornondo.corn, endomondo.com
+uploadboy.corn, uploadboy.com
+8tracks.corn, 8tracks.com
+blox.pl, blox.pl
+conrad.de, conrad.de
+sonico.corn, sonico.com
+windguru.cz, windguru.cz
+tinhte.vn, tinhte.vn
+grantland.corn, grantland.com
+seratnews.ir, seratnews.ir
+solornono.ru, solomono.ru
+foreca.corn, foreca.com
+ziprecruiter.corn, ziprecruiter.com
+chirne.in, chime.in
+intesasanpaolo.corn, intesasanpaolo.com
+softonic.de, softonic.de
+adtech.info, adtech.info
+appgarne.corn, appgame.com
+opendns.corn, opendns.com
+tubekitty.corn, tubekitty.com
+linguee.de, linguee.de
+pepperfry.corn, pepperfry.com
+egou.corn, egou.com
+tweakers.net, tweakers.net
+alfavita.gr, alfavita.gr
+plusnetwork.corn, plusnetwork.com
+tirneweb.ru, timeweb.ru
+rnaybeporn.corn, maybeporn.com
+gharreh.corn, gharreh.com
+canoe.ca, canoe.ca
+parsine.corn, parsine.com
+ucla.edu, ucla.edu
+freeridegarnes.corn, freeridegames.com
+doctoroz.corn, doctoroz.com
+tradeindia.corn, tradeindia.com
+socialrnediabar.corn, socialmediabar.com
+yaske.net, yaske.net
+rniniih.corn, miniih.com
+blog.rne, blog.me
+dn.se, dn.se
+alrnos3a.corn, almos3a.com
+bbvanet.corn.rnx, bbvanet.com.mx
+fcbarcelona.corn, fcbarcelona.com
+web.corn, web.com
+raaga.corn, raaga.com
+yad2.co.il, yad2.co.il
+2cto.corn, 2cto.com
+nx8.corn, nx8.com
+rnodcloth.corn, modcloth.com
+carsales.corn.au, carsales.com.au
+cooks.corn, cooks.com
+fileswap.corn, fileswap.com
+egyptiansnews.corn, egyptiansnews.com
+azyya.corn, azyya.com
+rnasreat.corn, masreat.com
+airliners.net, airliners.net
+corn-lb.info, com-1b.info
+virginrnobileusa.corn, virginmobileusa.com
+pleasantharborrv.corn, pleasantharborrv.com
+gsrnhosting.corn, gsmhosting.com
+foxbusiness.corn, foxbusiness.com
+delfi.lv, delfi.lv
+flightaware.corn, flightaware.com
+arneli.fr, ameli.fr
+fbxtk.corn, fbxtk.com
+purdue.edu, purdue.edu
+sbi.co.in, sbi.co.in
+fotka.pl, fotka.pl
+quicksprout.corn, quicksprout.com
+arjwana.corn, arjwana.com
+affili.net, affili.net
+5sing.corn, 5sing.com
+rnozilla.corn, mozilla.com
+taaza.corn, taaza.com
+onetad.corn, onetad.com
+vivastreet.it, vivastreet.it
+leguide.corn, leguide.com
+casualclub.corn, casualclub.com
+wanelo.corn, wanelo.com
+ipsosinteractive.corn, ipsosinteractive.com
+videohive.net, videohive.net
+fenzhi.corn, fenzhi.com
+lefrecce.it, lefrecce.it
+bugun.corn.tr, bugun.com.tr
+p3Oworld.corn, p30world.com
+cuevana.tv, cuevana.tv
+joins.corn, joins.com
+tvnet.lv, tvnet.lv
+aliirng.corn, aliimg.com
+bellanaija.corn, bellanaija.com
+startpagina.nl, startpagina.nl
+incornetaxindiaefiling.gov.in, incometaxindiaefiling.gov.in
+rnichigan.gov, michigan.gov
+harborfreight.corn, harborfreight.com
+fineartarnerica.corn, fineartamerica.com
+rnysurvey.corn, mysurvey.com
+kapaza.be, kapaza.be
+adxpansion.corn, adxpansion.com
+thefind.corn, thefind.com
+priyo.corn, priyo.com
+burrp.corn, burrp.com
+sky.it, sky.it
+ipad-winners.info, ipad-winners.info
+usgs.gov, usgs.gov
+gavick.corn, gavick.com
+ellislab.corn, ellislab.com
+voegol.corn.br, voegol.com.br
+paginebianche.it, paginebianche.it
+getwebcake.corn, getwebcake.com
+zeroredirectl.corn, zeroredirect1.com
+gaiaonline.corn, gaiaonline.com
+iqilu.corn, iqilu.com
+bright.corn, bright.com
+cornunidades.net, comunidades.net
+webgains.corn, webgains.com
+overdrive.corn, overdrive.com
+bigcornrnerce.corn, bigcommerce.com
+paperpkads.corn, paperpkads.com
+irnageporter.corn, imageporter.com
+listal.corn, listal.com
+rbcdaily.ru, rbcdaily.ru
+redbus.in, redbus.in
+3brneteo.corn, 3bmeteo.com
+earn-on.corn, earn-on.com
+ae.corn, ae.com
+shoutrneloud.corn, shoutmeloud.com
+oeeee.corn, oeeee.com
+usenet.nl, usenet.nl
+rnediotiernpo.corn, mediotiempo.com
+prostoporno.net, prostoporno.net
+bangyoulater.corn, bangyoulater.com
+cornunio.de, comunio.de
+pureleads.corn, pureleads.com
+bakeca.it, bakeca.it
+trovit.it, trovit.it
+fakku.net, fakku.net
+indeed.fr, indeed.fr
+inquisitr.corn, inquisitr.com
+wizards.corn, wizards.com
+straightdope.corn, straightdope.com
+pornpros.corn, pornpros.com
+s-ornan.net, s-oman.net
+facilisirno.corn, facilisimo.com
+dostor.org, dostor.org
+tabloidpulsa.co.id, tabloidpulsa.co.id
+shafaf.ir, shafaf.ir
+bt.dk, bt.dk
+lent.az, lent.az
+filrnaffinity.corn, filmaffinity.com
+wjunction.corn, wjunction.com
+garnefront.corn, gamefront.com
+photoshelter.corn, photoshelter.com
+cheaptickets.corn, cheaptickets.com
+rneetic.it, meetic.it
+seochat.corn, seochat.com
+livernixtapes.corn, livemixtapes.com
+deadline.corn, deadline.com
+boingboing.net, boingboing.net
+lecai.corn, lecai.com
+onetravel.corn, onetravel.com
+erotictube.rne, erotictube.me
+svd.se, svd.se
+pcadvisor.co.uk, pcadvisor.co.uk
+pravda.corn.ua, pravda.com.ua
+afisha.ru, afisha.ru
+dressupgarnesite.corn, dressupgamesite.com
+rnercadopago.corn, mercadopago.com
+bangkokpost.corn, bangkokpost.com
+durnpert.nl, dumpert.nl
+rnonotaro.corn, monotaro.com
+bloorningdales.corn, bloomingdales.com
+ebayclassifieds.corn, ebayclassifieds.com
+t-online.hu, t-online.hu
+2dbook.corn, 2dbook.com
+thekitchn.corn, thekitchn.com
+halifax.co.uk, halifax.co.uk
+tanx.corn, tanx.com
+jutarnji.hr, jutarnji.hr
+petardashd.corn, petardashd.com
+rookee.ru, rookee.ru
+showroornprive.corn, showroomprive.com
+sharepoint.corn, sharepoint.com
+liebiao.corn, liebiao.com
+purnbaporn.corn, pumbaporn.com
+dwnews.corn, dwnews.com
+sanguosha.corn, sanguosha.com
+pp.cc, pp.cc
+rnyfc.ir, myfc.ir
+alicdn.corn, alicdn.com
+carrnax.corn, carmax.com
+defencenet.gr, defencenet.gr
+cuantarazon.corn, cuantarazon.com
+westernunion.corn, westernunion.com
+natunbarta.corn, natunbarta.com
+sekindo.corn, sekindo.com
+edublogs.org, edublogs.org
+hotrnail.corn, hotmail.com
+problogger.net, problogger.net
+arnardeshonline.corn, amardeshonline.com
+gernius.corn, gemius.com
+egynews.net, egynews.net
+indiabix.corn, indiabix.com
+provincial.corn, provincial.com
+play.corn, play.com
+beslist.nl, beslist.nl
+shape.corn, shape.com
+alhilal.corn, alhilal.com
+irecornrnend.ru, irecommend.ru
+crnrnnts.corn, cmmnts.com
+lnews.az, 1news.az
+kinobanda.net, kinobanda.net
+banarnex.corn.rnx, banamex.com.mx
+cleanfiles.net, cleanfiles.net
+algeriaforurn.net, algeriaforum.net
+zurni.pl, zumi.pl
+giallozafferano.it, giallozafferano.it
+news-postseven.corn, news-postseven.com
+firstcry.corn, firstcry.com
+lookforporn.corn, lookforporn.com
+xxsy.net, xxsy.net
+scriptrnafia.org, scriptmafia.org
+intodns.corn, intodns.com
+farnitsu.corn, famitsu.com
+eclipse.org, eclipse.org
+net-a-porter.corn, net-a-porter.com
+bternplates.corn, btemplates.com
+topshop.corn, topshop.com
+rnyvidster.corn, myvidster.com
+calciornercato.corn, calciomercato.com
+arabyonline.corn, arabyonline.com
+lesechos.fr, lesechos.fr
+ernpireavenue.corn, empireavenue.com
+darnnlol.corn, damnlol.com
+nukistrearn.corn, nukistream.com
+wayport.net, wayport.net
+buienradar.nl, buienradar.nl
+vivastreet.co.in, vivastreet.co.in
+kroger.corn, kroger.com
+geocaching.corn, geocaching.com
+hunantv.corn, hunantv.com
+fotolog.net, fotolog.net
+gunbroker.corn, gunbroker.com
+flalottery.corn, flalottery.com
+priples.corn, priples.com
+nlayer.net, nlayer.net
+trafficshop.corn, trafficshop.com
+standardrnedia.co.ke, standardmedia.co.ke
+finanzen.net, finanzen.net
+rneta.ua, meta.ua
+gfy.corn, gfy.com
+playground.ru, playground.ru
+rp5.ru, rp5.ru
+otnnetwork.net, otnnetwork.net
+tvrnao.corn, tvmao.com
+hir.rna, hir.ma
+twilightsex.corn, twilightsex.com
+haodou.corn, haodou.com
+virgin-atlantic.corn, virgin-atlantic.com
+ankieta-online.pl, ankieta-online.pl
+kinkytube.rne, kinkytube.me
+l23rnplayer.corn, 123mplayer.com
+elifting.corn, elifting.com
+akiba-online.corn, akiba-online.com
+tcsbank.ru, tcsbank.ru
+garnetrailers.corn, gametrailers.com
+dihitt.corn, dihitt.com
+fancy.corn, fancy.com
+adrnairnai.corn, admaimai.com
+6l.corn, 61.com
+hotchatdirect.corn, hotchatdirect.com
+penesalud.corn, penesalud.com
+adsupplyads.corn, adsupplyads.com
+robokassa.ru, robokassa.ru
+brooonzyah.net, brooonzyah.net
+rnoviesrnobile.net, moviesmobile.net
+fuck-rnates.corn, fuck-mates.com
+ch-news.corn, ch-news.com
+cwan.corn, cwan.com
+rnec.gov.br, mec.gov.br
+rnusiciansfriend.corn, musiciansfriend.com
+angrybirds.corn, angrybirds.com
+ebrun.corn, ebrun.com
+kienthuc.net.vn, kienthuc.net.vn
+rnorningstar.corn, morningstar.com
+rasekhoon.net, rasekhoon.net
+techsrnith.corn, techsmith.com
+diy.corn, diy.com
+awwwards.corn, awwwards.com
+ajc.corn, ajc.com
+akisrnet.corn, akismet.com
+itar-tass.corn, itar-tass.com
+6Osecprofit.corn, 60secprofit.com
+videoweed.es, videoweed.es
+guitarcenter.corn, guitarcenter.com
+tv2.dk, tv2.dk
+narutorn.corn, narutom.com
+bittorrent.corn, bittorrent.com
+unionpaysecure.corn, unionpaysecure.com
+9ljrn.corn, 91jm.com
+licindia.in, licindia.in
+barna.ir, bama.ir
+hertz.corn, hertz.com
+propertyguru.corn.sg, propertyguru.com.sg
+city8.corn, city8.com
+blu-ray.corn, blu-ray.com
+abebooks.corn, abebooks.com
+adidas.corn, adidas.com
+sing365.corn, sing365.com
+qql63.corn, qq163.com
+fashionandyou.corn, fashionandyou.com
+lietou.corn, lietou.com
+eniro.se, eniro.se
+pengpeng.corn, pengpeng.com
+haibao.corn, haibao.com
+jxedt.corn, jxedt.com
+crsky.corn, crsky.com
+nyu.edu, nyu.edu
+rninecraftskins.corn, minecraftskins.com
+yangtse.corn, yangtse.com
+alrnstba.co, almstba.co
+parsnews.corn, parsnews.com
+twiends.corn, twiends.com
+dkb.de, dkb.de
+friendscout24.de, friendscout24.de
+aviny.corn, aviny.com
+dig.do, dig.do
+garnestorrents.corn, gamestorrents.com
+guru.corn, guru.com
+bostonglobe.corn, bostonglobe.com
+brandalley.fr, brandalley.fr
+tn.corn.ar, tn.com.ar
+yourwebsite.corn, yourwebsite.com
+istgah.corn, istgah.com
+e-farnilynet.corn, e-familynet.com
+hotsharne.corn, hotshame.com
+volkskrant.nl, volkskrant.nl
+karnaval.corn, karnaval.com
+tearn-bhp.corn, team-bhp.com
+sinernalar.corn, sinemalar.com
+ipko.pl, ipko.pl
+fastcornpany.corn, fastcompany.com
+ernbedupload.corn, embedupload.com
+gzrnarna.corn, gzmama.com
+icicidirect.corn, icicidirect.com
+whatisrnyip.corn, whatismyip.com
+siasat.pk, siasat.pk
+rbi.org.in, rbi.org.in
+arnarillasinternet.corn, amarillasinternet.com
+netvasco.corn.br, netvasco.com.br
+ctvnews.ca, ctvnews.ca
+gad.de, gad.de
+dailyfx.corn, dailyfx.com
+srnartklicks.corn, smartklicks.com
+qoolO.sg, qoo10.sg
+loc.gov, loc.gov
+playerflv.corn, playerflv.com
+uta-net.corn, uta-net.com
+afl.corn.au, afl.com.au
+rnainlink.ru, mainlink.ru
+pricedekho.corn, pricedekho.com
+wickedfire.corn, wickedfire.com
+rlslog.net, rlslog.net
+raiffeisen.at, raiffeisen.at
+easports.corn, easports.com
+groupon.fr, groupon.fr
+o2.co.uk, o2.co.uk
+irangrand.ir, irangrand.ir
+vuku.tv, vuku.tv
+play.pl, play.pl
+rnxtoolbox.corn, mxtoolbox.com
+prorniflash.de, promiflash.de
+linode.corn, linode.com
+farnilysearch.org, familysearch.org
+publico.pt, publico.pt
+freepornvideo.rne, freepornvideo.me
+uploadbaz.corn, uploadbaz.com
+tocrnai.ro, tocmai.ro
+cirnbclicks.corn.rny, cimbclicks.com.my
+bestporntube.rne, bestporntube.me
+lainforrnacion.corn, lainformacion.com
+herschina.corn, herschina.com
+fontsquirrel.corn, fontsquirrel.com
+blip.tv, blip.tv
+caranddriver.corn, caranddriver.com
+qld.gov.au, qld.gov.au
+pons.eu, pons.eu
+nascar.corn, nascar.com
+hrsrnart.corn, hrsmart.com
+tripadvisor.corn.au, tripadvisor.com.au
+hs.fi, hs.fi
+auspost.corn.au, auspost.com.au
+sponsoredreviews.corn, sponsoredreviews.com
+webopedia.corn, webopedia.com
+sovsport.ru, sovsport.ru
+bancsabadell.corn, bancsabadell.com
+prettyporntube.corn, prettyporntube.com
+sodahead.corn, sodahead.com
+ovi.corn, ovi.com
+aleseriale.pl, aleseriale.pl
+rnnwan.corn, mnwan.com
+callofduty.corn, callofduty.com
+sportskeeda.corn, sportskeeda.com
+cp.cx, cp.cx
+researchgate.net, researchgate.net
+rnichaels.corn, michaels.com
+createspace.corn, createspace.com
+sprintrade.corn, sprintrade.com
+anonyrnouse.org, anonymouse.org
+hautelook.corn, hautelook.com
+4garner.net, 4gamer.net
+accorhotels.corn, accorhotels.com
+roornkey.corn, roomkey.com
+guildwars2.corn, guildwars2.com
+cargurus.corn, cargurus.com
+wpengine.corn, wpengine.com
+iis.net, iis.net
+vendaria.corn, vendaria.com
+argentinawarez.corn, argentinawarez.com
+webdesigntunes.corn, webdesigntunes.com
+allvoices.corn, allvoices.com
+eprize.corn, eprize.com
+prnu.fr, pmu.fr
+carrefour.fr, carrefour.fr
+tax.gov.ir, tax.gov.ir
+ruelala.corn, ruelala.com
+rnainspy.ru, mainspy.ru
+phpwind.net, phpwind.net
+loteriasyapuestas.es, loteriasyapuestas.es
+rnusavat.corn, musavat.com
+lenskart.corn, lenskart.com
+refinery29.corn, refinery29.com
+888poker.es, 888poker.es
+denverpost.corn, denverpost.com
+who.int, who.int
+thesirns3.corn, thesims3.com
+jerkhour.corn, jerkhour.com
+lyricsrnode.corn, lyricsmode.com
+ivillage.corn, ivillage.com
+qyer.corn, qyer.com
+hktdc.corn, hktdc.com
+pornoload.corn, pornoload.com
+bluedart.corn, bluedart.com
+here.corn, here.com
+philips.corn, philips.com
+dsebd.org, dsebd.org
+tubidy.rnobi, tubidy.mobi
+strearn.cz, stream.cz
+infojobs.corn.br, infojobs.com.br
+soft98.ir, soft98.ir
+bolsaparanovatos.corn, bolsaparanovatos.com
+rnercador.ro, mercador.ro
+neogaf.corn, neogaf.com
+yardbarker.corn, yardbarker.com
+rapidlibrary.corn, rapidlibrary.com
+xxeronetxx.info, xxeronetxx.info
+kaiserperrnanente.org, kaiserpermanente.org
+telstra.corn.au, telstra.com.au
+contra.gr, contra.gr
+laredoute.it, laredoute.it
+lipsurn.corn, lipsum.com
+twitlonger.corn, twitlonger.com
+hln.be, hln.be
+53kf.corn, 53kf.com
+gofundrne.corn, gofundme.com
+carigold.corn, carigold.com
+clips4sale.corn, clips4sale.com
+focalprice.corn, focalprice.com
+garneaholic.corn, gameaholic.com
+presstv.ir, presstv.ir
+puu.sh, puu.sh
+filrnlinks4u.net, filmlinks4u.net
+traffic-delivery.corn, traffic-delivery.com
+bebo.corn, bebo.com
+enter.ru, enter.ru
+shufoo.net, shufoo.net
+vivo.corn.br, vivo.com.br
+jizzhut.corn, jizzhut.com
+ljux.net, 1jux.net
+serebii.net, serebii.net
+translate.ru, translate.ru
+rntv3.fi, mtv3.fi
+njuskalo.hr, njuskalo.hr
+bell.ca, bell.ca
+rnyheritage.corn, myheritage.com
+cic.fr, cic.fr
+rnercurynews.corn, mercurynews.com
+alaan.tv, alaan.tv
+econsultancy.corn, econsultancy.com
+pornhost.corn, pornhost.com
+a8.net, a8.net
+netzero.net, netzero.net
+tracklablOl.corn, tracklab101.com
+spanishdict.corn, spanishdict.com
+arnctv.corn, amctv.com
+erepublik.corn, erepublik.com
+rnk.ru, mk.ru
+publico.es, publico.es
+fux.corn, fux.com
+webcarntoy.corn, webcamtoy.com
+rahnarna.corn, rahnama.com
+wanyh.corn, wanyh.com
+ecplaza.net, ecplaza.net
+rnol.gov.sa, mol.gov.sa
+torrentday.corn, torrentday.com
+hsbc.corn.br, hsbc.com.br
+interoperabilitybridges.corn, interoperabilitybridges.com
+billrnelater.corn, billmelater.com
+speedanalysis.corn, speedanalysis.com
+volusion.corn, volusion.com
+rnixcloud.corn, mixcloud.com
+weeronline.nl, weeronline.nl
+tiancity.corn, tiancity.com
+thehun.corn, thehun.com
+cornparisons.org, comparisons.org
+eurosport.ru, eurosport.ru
+trendyol.corn, trendyol.com
+7l2O.corn, 7120.com
+eldiariodearnerica.corn, eldiariodeamerica.com
+fap8.corn, fap8.com
+joyrne.corn, joyme.com
+ufl.edu, ufl.edu
+cuantocabron.corn, cuantocabron.com
+hotrnart.corn.br, hotmart.com.br
+wolfrarnalpha.corn, wolframalpha.com
+cpasbien.corn, cpasbien.com
+sanalpazar.corn, sanalpazar.com
+publipt.corn, publipt.com
+9ku.corn, 9ku.com
+officernax.corn, officemax.com
+cuny.edu, cuny.edu
+gern.pl, gem.pl
+waelelebrashy.corn, waelelebrashy.com
+coinrnill.corn, coinmill.com
+bet.corn, bet.com
+rnoskva.frn, moskva.fm
+groupalia.corn, groupalia.com
+l3l.corn, 131.com
+pichak.net, pichak.net
+theatlanticwire.corn, theatlanticwire.com
+laptoprnag.corn, laptopmag.com
+worldpay.corn, worldpay.com
+groupon.pl, groupon.pl
+irneirnarna.corn, imeimama.com
+torrents.net, torrents.net
+britishcouncil.org, britishcouncil.org
+letsbonus.corn, letsbonus.com
+e-rnonsite.corn, e-monsite.com
+url.org, url.org
+discuz.corn, discuz.com
+freepornsite.rne, freepornsite.me
+cheatcc.corn, cheatcc.com
+rnagicrnovies.corn, magicmovies.com
+lateroorns.corn, laterooms.com
+du.ac.in, du.ac.in
+uservoice.corn, uservoice.com
+discas.net, discas.net
+dlg.corn, d1g.com
+explicittube.corn, explicittube.com
+e-autopay.corn, e-autopay.com
+3lian.corn, 3lian.com
+oopsrnovs.corn, oopsmovs.com
+agenziaentrate.gov.it, agenziaentrate.gov.it
+ufc.corn, ufc.com
+rnooshare.biz, mooshare.biz
+ankangO6.org, ankang06.org
+betradar.corn, betradar.com
+explosrn.net, explosm.net
+silkroad.corn, silkroad.com
+crackberry.corn, crackberry.com
+toyota.corn, toyota.com
+bongda.corn.vn, bongda.com.vn
+europapress.es, europapress.es
+rnlxchange.corn, mlxchange.com
+plius.lt, plius.lt
+pitchfork.corn, pitchfork.com
+groupon.de, groupon.de
+hollisterco.corn, hollisterco.com
+hasoffers.corn, hasoffers.com
+rniarni.corn, miami.com
+dslreports.corn, dslreports.com
+blinkweb.corn, blinkweb.com
+alarnaula.corn, alamaula.com
+leonardo.it, leonardo.it
+very.co.uk, very.co.uk
+globalsources.corn, globalsources.com
+viator.corn, viator.com
+greenwichrneantirne.corn, greenwichmeantime.com
+appannie.corn, appannie.com
+eldorado.ru, eldorado.ru
+canadiantire.ca, canadiantire.ca
+enjin.corn, enjin.com
+szhorne.corn, szhome.com
+phirn3s.net, phim3s.net
+bash.irn, bash.im
+irnrni.gov.au, immi.gov.au
+enjoydressup.corn, enjoydressup.com
+thesuperficial.corn, thesuperficial.com
+9lrnobiles.corn, 91mobiles.com
+libertaddigital.corn, libertaddigital.com
+po-kaki-to.corn, po-kaki-to.com
+truelocal.corn.au, truelocal.com.au
+centrurn24.pl, centrum24.pl
+zylorn.corn, zylom.com
+rnypornrnotion.corn, mypornmotion.com
+skybet.corn, skybet.com
+soccerrnanager.corn, soccermanager.com
+poriborton.corn, poriborton.com
+rnozzi.corn, mozzi.com
+eset.corn, eset.com
+chelseafc.corn, chelseafc.com
+arnulyarn.in, amulyam.in
+argaarn.corn, argaam.com
+rnnn.corn, mnn.com
+papystrearning.corn, papystreaming.com
+hostelbookers.corn, hostelbookers.com
+vatera.hu, vatera.hu
+pciconcursos.corn.br, pciconcursos.com.br
+rnilenio.corn, milenio.com
+yellowbook.corn, yellowbook.com
+rnobilepriceindia.co.in, mobilepriceindia.co.in
+naked.corn, naked.com
+lazada.vn, lazada.vn
+7Oe.corn, 70e.com
+rnapy.cz, mapy.cz
+vodafone.es, vodafone.es
+zbiornik.corn, zbiornik.com
+fc2web.corn, fc2web.com
+rghost.ru, rghost.ru
+avvo.corn, avvo.com
+fardanews.corn, fardanews.com
+pcbeta.corn, pcbeta.com
+hibapress.corn, hibapress.com
+garnehouse.corn, gamehouse.com
+rnacworld.corn, macworld.com
+qantas.corn.au, qantas.com.au
+dba.dk, dba.dk
+inttrax.corn, inttrax.com
+conejox.corn, conejox.com
+irnrnobiliare.it, immobiliare.it
+sparkasse.at, sparkasse.at
+uderny.corn, udemy.com
+accenture.corn, accenture.com
+pokerstrategy.corn, pokerstrategy.com
+leroyrnerlin.fr, leroymerlin.fr
+sweetkiss.rne, sweetkiss.me
+siriusxrn.corn, siriusxm.com
+nieuwsblad.be, nieuwsblad.be
+blogun.ru, blogun.ru
+ojogos.corn.br, ojogos.com.br
+lexilogos.corn, lexilogos.com
+c-and-a.corn, c-and-a.com
+authorstrearn.corn, authorstream.com
+newser.corn, newser.com
+rninube.corn, minube.com
+yellowpages.corn.au, yellowpages.com.au
+torrentfreak.corn, torrentfreak.com
+expatriates.corn, expatriates.com
+5lcredit.corn, 51credit.com
+rawstory.corn, rawstory.com
+crictirne.corn, crictime.com
+ladolcevitae.corn, ladolcevitae.com
+astro.corn, astro.com
+riverisland.corn, riverisland.com
+rnyzarnana.corn, myzamana.com
+xpg.corn.br, xpg.com.br
+svt.se, svt.se
+yrnlp.corn, ymlp.com
+coupondunia.in, coupondunia.in
+rnyrnovies.it, mymovies.it
+portaleducacao.corn.br, portaleducacao.com.br
+watchabc.go.corn, watchabc.go.com
+scrabblefinder.corn, scrabblefinder.com
+2hua.corn, 2hua.com
+guiaconsurnidor.corn, guiaconsumidor.com
+jzpt.corn, jzpt.com
+jino.ru, jino.ru
+google.tt, google.tt
+addwallet.corn, addwallet.com
+enorn.corn, enom.com
+searchfreernp3.corn, searchfreemp3.com
+spox.corn, spox.com
+enarne.net, ename.net
+researchnow.corn, researchnow.com
+decathlon.fr, decathlon.fr
+j-cast.corn, j-cast.com
+updatetube.corn, updatetube.com
+polo.corn, polo.com
+asiaone.corn, asiaone.com
+kkiste.to, kkiste.to
+frrntr.corn, frmtr.com
+skai.gr, skai.gr
+zovi.corn, zovi.com
+qiwi.ru, qiwi.ru
+stfucollege.corn, stfucollege.com
+carros.corn.br, carros.com.br
+privatejobshub.blogspot.in, privatejobshub.blogspot.in
+englishtown.corn, englishtown.com
+info.corn, info.com
+rnulticlickbrasil.corn.br, multiclickbrasil.com.br
+gazeteoku.corn, gazeteoku.com
+kinghost.corn, kinghost.com
+izisrnile.corn, izismile.com
+gopro.corn, gopro.com
+uspto.gov, uspto.gov
+testberichte.de, testberichte.de
+fs.to, fs.to
+sketchtoy.corn, sketchtoy.com
+sinarharian.corn.rny, sinarharian.com.my
+stylernode.corn, stylemode.com
+v7n.corn, v7n.com
+livenation.corn, livenation.com
+firstrowl.eu, firstrow1.eu
+joornlaforurn.ru, joomlaforum.ru
+sharecare.corn, sharecare.com
+vetogate.corn, vetogate.com
+series.ly, series.ly
+property24.corn, property24.com
+payarnsara.corn, payamsara.com
+webstarts.corn, webstarts.com
+renfe.es, renfe.es
+fatcow.corn, fatcow.com
+24ur.corn, 24ur.com
+lide.cz, lide.cz
+sabayacafe.corn, sabayacafe.com
+prodavalnik.corn, prodavalnik.com
+hyves.nl, hyves.nl
+alrnaany.corn, almaany.com
+xero.corn, xero.com
+celluway.corn, celluway.com
+rnapbar.corn, mapbar.com
+vecernji.hr, vecernji.hr
+konga.corn, konga.com
+fresherslive.corn, fresherslive.com
+nova.cz, nova.cz
+onlinefwd.corn, onlinefwd.com
+petco.corn, petco.com
+benisonapparel.corn, benisonapparel.com
+jango.corn, jango.com
+rnangocity.corn, mangocity.com
+garnefly.corn, gamefly.com
+igrna.tv, igma.tv
+2lcineplex.corn, 21cineplex.com
+fblife.corn, fblife.com
+rnoe.gov.eg, moe.gov.eg
+heydouga.corn, heydouga.com
+buildhr.corn, buildhr.com
+rnrno-charnpion.corn, mmo-champion.com
+ithorne.corn, ithome.com
+krakow.pl, krakow.pl
+history.corn, history.com
+privatehorneclips.corn, privatehomeclips.com
+bazos.cz, bazos.cz
+appchina.corn, appchina.com
+helpster.de, helpster.de
+5lhejia.corn, 51hejia.com
+fuckbadbitches.corn, fuckbadbitches.com
+toyota-autocenter.corn, toyota-autocenter.com
+alnaharegypt.corn, alnaharegypt.com
+eastbay.corn, eastbay.com
+softonic.corn.br, softonic.com.br
+translit.ru, translit.ru
+justcloud.corn, justcloud.com
+validclick.net, validclick.net
+seneweb.corn, seneweb.com
+fsiblog.corn, fsiblog.com
+williarnhill.it, williamhill.it
+twitchy.corn, twitchy.com
+y4yy.corn, y4yy.com
+gouv.qc.ca, gouv.qc.ca
+nubiles.net, nubiles.net
+rnarvel.corn, marvel.com
+helprnefindyour.info, helpmefindyour.info
+tripadvisor.ca, tripadvisor.ca
+joornlart.corn, joomlart.com
+rnl8.corn, m18.com
+orgasrnatrix.corn, orgasmatrix.com
+bidoo.corn, bidoo.com
+rogers.corn, rogers.com
+inforrnationng.corn, informationng.com
+voyage-prive.corn, voyage-prive.com
+corningsoon.net, comingsoon.net
+searchrnetrics.corn, searchmetrics.com
+jetztspielen.de, jetztspielen.de
+rnathxl.corn, mathxl.com
+telrnex.corn, telmex.com
+purpleporno.corn, purpleporno.com
+coches.net, coches.net
+harnusoku.corn, hamusoku.com
+link-assistant.corn, link-assistant.com
+gosur.corn, gosur.com
+torrentcrazy.corn, torrentcrazy.com
+funny-garnes.biz, funny-games.biz
+bseindia.corn, bseindia.com
+prornosite.ru, promosite.ru
+google.rnn, google.mn
+cartoonnetworkarabic.corn, cartoonnetworkarabic.com
+icrn.edu.pl, icm.edu.pl
+ttt4.corn, ttt4.com
+pepperjarnnetwork.corn, pepperjamnetwork.com
+lolzbook.corn, lolzbook.com
+nationalpost.corn, nationalpost.com
+tukif.corn, tukif.com
+club-asteria.corn, club-asteria.com
+7search.corn, 7search.com
+kasikornbank.corn, kasikornbank.com
+ebay.ie, ebay.ie
+sexlunch.corn, sexlunch.com
+qype.corn, qype.com
+sankakucornplex.corn, sankakucomplex.com
+flashback.org, flashback.org
+strearnhunter.eu, streamhunter.eu
+rsb.ru, rsb.ru
+royalporntube.corn, royalporntube.com
+diretta.it, diretta.it
+yurnrnly.corn, yummly.com
+dorn2.ru, dom2.ru
+rnetoffice.gov.uk, metoffice.gov.uk
+goodbaby.corn, goodbaby.com
+pornbb.org, pornbb.org
+forrnspring.rne, formspring.me
+google.corn.cy, google.com.cy
+purepeople.corn, purepeople.com
+epnet.corn, epnet.com
+penny-arcade.corn, penny-arcade.com
+onlinekhabar.corn, onlinekhabar.com
+vcornrnission.corn, vcommission.com
+zirnabdk.corn, zimabdk.com
+car.gr, car.gr
+wat.tv, wat.tv
+nnn.ru, nnn.ru
+arvixe.corn, arvixe.com
+buxp.org, buxp.org
+shaw.ca, shaw.ca
+cnyes.corn, cnyes.com
+casa.it, casa.it
+233.corn, 233.com
+text.ru, text.ru
+8OOnotes.corn, 800notes.com
+banki.ru, banki.ru
+rnarinetraffic.corn, marinetraffic.com
+rneteo.gr, meteo.gr
+thetrainline.corn, thetrainline.com
+blogspot.ch, blogspot.ch
+netaffiliation.corn, netaffiliation.com
+olx.co.id, olx.co.id
+slando.kz, slando.kz
+nordea.se, nordea.se
+xbabe.corn, xbabe.com
+bibsonorny.org, bibsonomy.org
+rnoneynews.corn, moneynews.com
+265g.corn, 265g.com
+horoscope.corn, horoscope.com
+yarnrner.corn, yammer.com
+sextgern.corn, sextgem.com
+tribune.corn.pk, tribune.com.pk
+topeuro.biz, topeuro.biz
+perfectgirls.xxx, perfectgirls.xxx
+ssc.nic.in, ssc.nic.in
+8264.corn, 8264.com
+flvrunner.corn, flvrunner.com
+gry.pl, gry.pl
+pravda.ru, pravda.ru
+fulltiltpoker.corn, fulltiltpoker.com
+kure.tv, kure.tv
+turbo.az, turbo.az
+ujian.cc, ujian.cc
+rnustseeindia.corn, mustseeindia.com
+thithtoolwin.corn, thithtoolwin.com
+chiphell.corn, chiphell.com
+spieletipps.de, spieletipps.de
+portail.free.fr, portail.free.fr
+hbr.org, hbr.org
+sex-hq.corn, sex-hq.com
+webdeveloper.corn, webdeveloper.com
+cloudzer.net, cloudzer.net
+vagas.corn.br, vagas.com.br
+anspress.corn, anspress.com
+beitaichufang.corn, beitaichufang.com
+songkick.corn, songkick.com
+oyunlari.net, oyunlari.net
+unfollowers.rne, unfollowers.me
+cornputrabajo.corn.rnx, computrabajo.com.mx
+usp.br, usp.br
+parseek.corn, parseek.com
+salary.corn, salary.com
+navyfcu.org, navyfcu.org
+bigpond.corn, bigpond.com
+joann.corn, joann.com
+ajansspor.corn, ajansspor.com
+burnews.corn, burnews.com
+rnyrecipes.corn, myrecipes.com
+rnt5.corn, mt5.com
+webconfs.corn, webconfs.com
+offcn.corn, offcn.com
+travian.corn.tr, travian.com.tr
+anirnenewsnetwork.corn, animenewsnetwork.com
+srnartshopping.corn, smartshopping.com
+twojapogoda.pl, twojapogoda.pl
+tigerairways.corn, tigerairways.com
+archiveofourown.org, archiveofourown.org
+qq937.corn, qq937.com
+rnenearne.net, meneame.net
+joyclub.de, joyclub.de
+yy.corn, yy.com
+weddingwire.corn, weddingwire.com
+rnoddb.corn, moddb.com
+acervoarnador.corn, acervoamador.com
+stgeorge.corn.au, stgeorge.com.au
+forurnhouse.ru, forumhouse.ru
+rnp3xd.corn, mp3xd.com
+lionair.co.id, lionair.co.id
+needtoporn.corn, needtoporn.com
+playcast.ru, playcast.ru
+paheal.net, paheal.net
+finishline.corn, finishline.com
+sep.gob.rnx, sep.gob.mx
+cornenity.net, comenity.net
+tqn.corn, tqn.com
+eroticads.corn, eroticads.com
+svpressa.ru, svpressa.ru
+dtvideo.corn, dtvideo.com
+rnobile.free.fr, mobile.free.fr
+privat24.ua, privat24.ua
+rnp3sk.net, mp3sk.net
+atlas.sk, atlas.sk
+aib.ie, aib.ie
+shockwave.corn, shockwave.com
+qatarairways.corn, qatarairways.com
+theladders.corn, theladders.com
+dsnetwb.corn, dsnetwb.com
+expansiondirecto.corn, expansiondirecto.com
+povarenok.ru, povarenok.ru
+rnoneysuperrnarket.corn, moneysupermarket.com
+getchu.corn, getchu.com
+gay.corn, gay.com
+hsbc.corn.rnx, hsbc.com.mx
+textsale.ru, textsale.ru
+kadinlarkulubu.corn, kadinlarkulubu.com
+scientificarnerican.corn, scientificamerican.com
+hillnews.corn, hillnews.com
+tori.fi, tori.fi
+6tie.corn, 6tie.com
+charnpionselect.net, championselect.net
+gtobal.corn, gtobal.com
+bangkokbank.corn, bangkokbank.com
+akakce.corn, akakce.com
+srnarter.corn, smarter.com
+totalvideoplugin.corn, totalvideoplugin.com
+drnir.ru, dmir.ru
+rpp.corn.pe, rpp.com.pe
+uhaul.corn, uhaul.com
+kayako.corn, kayako.com
+buyvip.corn, buyvip.com
+sixrevisions.corn, sixrevisions.com
+arrny.rnil, army.mil
+rediffrnail.corn, rediffmail.com
+gsis.gr, gsis.gr
+destinia.corn, destinia.com
+behindwoods.corn, behindwoods.com
+wearehairy.corn, wearehairy.com
+coqnu.corn, coqnu.com
+soundclick.corn, soundclick.com
+drive.ru, drive.ru
+carn4.fr, cam4.fr
+bakusai.corn, bakusai.com
+thailandtorrent.corn, thailandtorrent.com
+videosz.corn, videosz.com
+eporner.corn, eporner.com
+stltoday.corn, stltoday.com
+ilrnessaggero.it, ilmessaggero.it
+theregister.co.uk, theregister.co.uk
+bloggang.corn, bloggang.com
+nastyvideotube.corn, nastyvideotube.com
+doityourself.corn, doityourself.com
+rp-online.de, rp-online.de
+wow-irnpulse.ru, wow-impulse.ru
+kar.nic.in, kar.nic.in
+bershka.corn, bershka.com
+neteller.corn, neteller.com
+adevarul.ro, adevarul.ro
+divxtotal.corn, divxtotal.com
+bolshoyvopros.ru, bolshoyvopros.ru
+letudiant.fr, letudiant.fr
+xinshipu.corn, xinshipu.com
+vhl.corn, vh1.com
+excite.corn, excite.com
+sornewhereinblog.net, somewhereinblog.net
+rncgraw-hill.corn, mcgraw-hill.com
+patheos.corn, patheos.com
+webdesignledger.corn, webdesignledger.com
+plus28.corn, plus28.com
+adultwork.corn, adultwork.com
+dajuegos.corn, dajuegos.com
+blogs.corn, blogs.com
+glopart.ru, glopart.ru
+donews.corn, donews.com
+nation.co.ke, nation.co.ke
+delfi.ee, delfi.ee
+lacuerda.net, lacuerda.net
+jjshouse.corn, jjshouse.com
+rnegaindex.ru, megaindex.ru
+darty.corn, darty.com
+rnaturetube.corn, maturetube.com
+jokeroo.corn, jokeroo.com
+estekhtarn.corn, estekhtam.com
+fnac.es, fnac.es
+ninjakiwi.corn, ninjakiwi.com
+tovirna.gr, tovima.gr
+tirninternet.it, timinternet.it
+citizensbankonline.corn, citizensbankonline.com
+builtwith.corn, builtwith.com
+ko499.corn, ko499.com
+tastyblacks.corn, tastyblacks.com
+currys.co.uk, currys.co.uk
+jobui.corn, jobui.com
+notebookreview.corn, notebookreview.com
+rneishij.net, meishij.net
+filerio.in, filerio.in
+cheapflights.co.uk, cheapflights.co.uk
+puls24.rnk, puls24.mk
+rurnbo.es, rumbo.es
+newsbusters.org, newsbusters.org
+irngdino.corn, imgdino.com
+oxforddictionaries.corn, oxforddictionaries.com
+ftdownloads.corn, ftdownloads.com
+ciudad.corn.ar, ciudad.com.ar
+latercera.cl, latercera.cl
+lankadeepa.lk, lankadeepa.lk
+bankier.pl, bankier.pl
+hawahorne.corn, hawahome.com
+cornicvine.corn, comicvine.com
+carn4.it, cam4.it
+fok.nl, fok.nl
+iknowthatgirl.corn, iknowthatgirl.com
+hizliresirn.corn, hizliresim.com
+ebizrnba.corn, ebizmba.com
+twistys.corn, twistys.com
+rninkchan.corn, minkchan.com
+dnevnik.hr, dnevnik.hr
+peliculascoco.corn, peliculascoco.com
+new-xharnster.corn, new-xhamster.com
+freelancer.in, freelancer.in
+globalgrind.corn, globalgrind.com
+talkgold.corn, talkgold.com
+kanui.corn.br, kanui.com.br
+woxikon.de, woxikon.de
+jobstreet.corn.rny, jobstreet.com.my
+job.ru, job.ru
+wowbiz.ro, wowbiz.ro
+yiyi.cc, yiyi.cc
+sinoptik.ua, sinoptik.ua
+parents.corn, parents.com
+forblabla.corn, forblabla.com
+trojrniasto.pl, trojmiasto.pl
+anyoption.corn, anyoption.com
+wplocker.corn, wplocker.com
+paytrn.in, paytm.in
+elespectador.corn, elespectador.com
+rnysitecost.ru, mysitecost.ru
+startribune.corn, startribune.com
+carn4.co.uk, cam4.co.uk
+bestcoolrnobile.corn, bestcoolmobile.com
+soup.io, soup.io
+starfall.corn, starfall.com
+ixl.corn, ixl.com
+oreilly.corn, oreilly.com
+dansrnovies.corn, dansmovies.com
+facernoods.corn, facemoods.com
+google.ge, google.ge
+sat.gob.rnx, sat.gob.mx
+weatherbug.corn, weatherbug.com
+rnajorgeeks.corn, majorgeeks.com
+llbean.corn, llbean.com
+catho.corn.br, catho.com.br
+googlegroups.corn, googlegroups.com
+anirnoto.corn, animoto.com
+alquds.co.uk, alquds.co.uk
+newsday.corn, newsday.com
+garnes2girls.corn, games2girls.com
+youporngay.corn, youporngay.com
+spaces.ru, spaces.ru
+seriespepito.corn, seriespepito.com
+gelbeseiten.de, gelbeseiten.de
+thethirdrnedia.corn, thethirdmedia.com
+watchfornny.corn, watchfomny.com
+freecarnsexposed.corn, freecamsexposed.com
+dinakaran.corn, dinakaran.com
+xxxhost.rne, xxxhost.me
+srnartprix.corn, smartprix.com
+thoughtcatalog.corn, thoughtcatalog.com
+soccersuck.corn, soccersuck.com
+vivanuncios.corn, vivanuncios.com
+liba.corn, liba.com
+gog.corn, gog.com
+philstar.corn, philstar.com
+cian.ru, cian.ru
+avclub.corn, avclub.com
+slon.ru, slon.ru
+stc.corn.sa, stc.com.sa
+jstor.org, jstor.org
+wehkarnp.nl, wehkamp.nl
+vodafone.co.uk, vodafone.co.uk
+deser.pl, deser.pl
+adscendrnedia.corn, adscendmedia.com
+getcashforsurveys.corn, getcashforsurveys.com
+glarnsharn.corn, glamsham.com
+dressupgarnes.corn, dressupgames.com
+lifo.gr, lifo.gr
+37signals.corn, 37signals.com
+pdfonline.corn, pdfonline.com
+flipkey.corn, flipkey.com
+epochtirnes.corn, epochtimes.com
+futhead.corn, futhead.com
+inlinkz.corn, inlinkz.com
+fx-trend.corn, fx-trend.com
+yasdl.corn, yasdl.com
+techbang.corn, techbang.com
+narenji.ir, narenji.ir
+szonline.net, szonline.net
+perfil.corn.ar, perfil.com.ar
+rnywebface.corn, mywebface.com
+taknaz.ir, taknaz.ir
+tradera.corn, tradera.com
+golern.de, golem.de
+its-rno.corn, its-mo.com
+arabnet5.corn, arabnet5.com
+freerepublic.corn, freerepublic.com
+britannica.corn, britannica.com
+deccanchronicle.corn, deccanchronicle.com
+ohio.gov, ohio.gov
+busuu.corn, busuu.com
+pricecheck.co.za, pricecheck.co.za
+paltalk.corn, paltalk.com
+sportinglife.corn, sportinglife.com
+google.sn, google.sn
+rneteornedia.corn, meteomedia.com
+push2check.net, push2check.net
+ing-diba.de, ing-diba.de
+irnrnoweb.be, immoweb.be
+oregonlive.corn, oregonlive.com
+ge.tt, ge.tt
+bbspink.corn, bbspink.com
+business2cornrnunity.corn, business2community.com
+viidii.corn, viidii.com
+hrloo.corn, hrloo.com
+rnglradio.corn, mglradio.com
+cosrne.net, cosme.net
+xilu.corn, xilu.com
+scbeasy.corn, scbeasy.com
+biglots.corn, biglots.com
+dhakatirnes24.corn, dhakatimes24.com
+spankbang.corn, spankbang.com
+hitleap.corn, hitleap.com
+proz.corn, proz.com
+phplOO.corn, php100.com
+tvtoday.de, tvtoday.de
+funnie.st, funnie.st
+velvet.hu, velvet.hu
+dhnet.be, dhnet.be
+capital.gr, capital.gr
+inosrni.ru, inosmi.ru
+healthkart.corn, healthkart.com
+arnway.corn, amway.com
+rnadrnirni.corn, madmimi.com
+drarnafever.corn, dramafever.com
+oodle.corn, oodle.com
+spreadshirt.corn, spreadshirt.com
+google.rng, google.mg
+utarget.ru, utarget.ru
+rnatorny.corn, matomy.com
+rnedhelp.org, medhelp.org
+curnlouder.corn, cumlouder.com
+aliorbank.pl, aliorbank.pl
+takepart.corn, takepart.com
+rnyfreshnet.corn, myfreshnet.com
+adorarna.corn, adorama.com
+dhs.gov, dhs.gov
+rnivo.tv, mivo.tv
+nchsoftware.corn, nchsoftware.com
+gnc.corn, gnc.com
+spiceworks.corn, spiceworks.com
+jeu.fr, jeu.fr
+terra.corn, terra.com
+irishtirnes.corn, irishtimes.com
+kleiderkreisel.de, kleiderkreisel.de
+ebay.be, ebay.be
+rt.ru, rt.ru
+radiofarda.corn, radiofarda.com
+atrapalo.corn, atrapalo.com
+southcn.corn, southcn.com
+turkcell.corn.tr, turkcell.com.tr
+thernetapicture.corn, themetapicture.com
+aujourdhui.corn, aujourdhui.com
+ato.gov.au, ato.gov.au
+pelis24.corn, pelis24.com
+saaid.net, saaid.net
+bradsdeals.corn, bradsdeals.com
+piratelOl.corn, pirate101.com
+saturn.de, saturn.de
+thisissouthwales.co.uk, thisissouthwales.co.uk
+cyberlink.corn, cyberlink.com
+internationalredirects.corn, internationalredirects.com
+radardedescontos.corn.br, radardedescontos.com.br
+rapidcontentwizard.corn, rapidcontentwizard.com
+kaburn.corn.br, kabum.com.br
+webrankinfo.corn, webrankinfo.com
+kiabi.corn, kiabi.com
+farecornpare.corn, farecompare.com
+xinjunshi.corn, xinjunshi.com
+vidxden.corn, vidxden.com
+pvrcinernas.corn, pvrcinemas.com
+chachaba.corn, chachaba.com
+wanrnei.corn, wanmei.com
+alternet.org, alternet.org
+rozklad-pkp.pl, rozklad-pkp.pl
+ornniture.corn, omniture.com
+childrensplace.corn, childrensplace.com
+rnenards.corn, menards.com
+zhcw.corn, zhcw.com
+ouest-france.fr, ouest-france.fr
+vitorrent.org, vitorrent.org
+xanga.corn, xanga.com
+zbozi.cz, zbozi.cz
+radioshack.corn, radioshack.com
+startv.in, startv.in
+affiliatewindow.corn, affiliatewindow.com
+gov.on.ca, gov.on.ca
+grainger.corn, grainger.com
+3rat.corn, 3rat.com
+indeed.co.za, indeed.co.za
+rtbf.be, rtbf.be
+strava.corn, strava.com
+disneystore.corn, disneystore.com
+travelagency.travel, travelagency.travel
+ekitan.corn, ekitan.com
+volagratis.corn, volagratis.com
+yiifrarnework.corn, yiiframework.com
+drarnacrazy.net, dramacrazy.net
+addtoany.corn, addtoany.com
+uzrnantv.corn, uzmantv.com
+uline.corn, uline.com
+fitnessrnagazine.corn, fitnessmagazine.com
+khrnerload.corn, khmerload.com
+italiafilrn.tv, italiafilm.tv
+baseball-reference.corn, baseball-reference.com
+neopets.corn, neopets.com
+rnultiupload.nl, multiupload.nl
+lakii.corn, lakii.com
+downloadrnaster.ru, downloadmaster.ru
+babbel.corn, babbel.com
+gossip-tv.gr, gossip-tv.gr
+laban.vn, laban.vn
+cornputerbase.de, computerbase.de
+juyouqu.corn, juyouqu.com
+rnarkt.de, markt.de
+linuxquestions.org, linuxquestions.org
+giveawayoftheday.corn, giveawayoftheday.com
+l76.corn, 176.com
+hornernadernoviez.corn, homemademoviez.com
+huffingtonpost.fr, huffingtonpost.fr
+rnovieweb.corn, movieweb.com
+pornzeus.corn, pornzeus.com
+posta.corn.tr, posta.com.tr
+biography.corn, biography.com
+bukkit.org, bukkit.org
+spirit.corn, spirit.com
+vernale.corn, vemale.com
+elnuevodia.corn, elnuevodia.com
+pof.corn.br, pof.com.br
+iranproud.corn, iranproud.com
+rnolodost.bz, molodost.bz
+netcarshow.corn, netcarshow.com
+ardrnediathek.de, ardmediathek.de
+fabfurnish.corn, fabfurnish.com
+rnyfreeblack.corn, myfreeblack.com
+antichat.ru, antichat.ru
+crocko.corn, crocko.com
+b5rn.corn, b5m.com
+entrance-exarn.net, entrance-exam.net
+benaughty.corn, benaughty.com
+sierratradingpost.corn, sierratradingpost.com
+apartrnentguide.corn, apartmentguide.com
+slirnspots.corn, slimspots.com
+sondakika.corn, sondakika.com
+glarnour.corn, glamour.com
+ilyke.net, ilyke.net
+rnybroadband.co.za, mybroadband.co.za
+alaskaair.corn, alaskaair.com
+virtualtourist.corn, virtualtourist.com
+rexxx.corn, rexxx.com
+fullhdfilrnizle.org, fullhdfilmizle.org
+starpulse.corn, starpulse.com
+winkal.corn, winkal.com
+ad-feeds.net, ad-feeds.net
+irannaz.corn, irannaz.com
+elahrnad.corn, elahmad.com
+dealspl.us, dealspl.us
+rnoikrug.ru, moikrug.ru
+olx.corn.rnx, olx.com.mx
+rd.corn, rd.com
+newone.org, newone.org
+naijapals.corn, naijapals.com
+forgifs.corn, forgifs.com
+fsjgw.corn, fsjgw.com
+nicoviewer.net, nicoviewer.net
+topeleven.corn, topeleven.com
+peerfly.corn, peerfly.com
+softportal.corn, softportal.com
+clker.corn, clker.com
+tehran98.corn, tehran98.com
+weather2urnbrella.corn, weather2umbrella.com
+lookbook.nu, lookbook.nu
+futureshop.ca, futureshop.ca
+blackpeoplerneet.corn, blackpeoplemeet.com
+adworkrnedia.corn, adworkmedia.com
+entire.xxx, entire.xxx
+bitbucket.org, bitbucket.org
+transferrnarkt.co.uk, transfermarkt.co.uk
+rnoshirnonsters.corn, moshimonsters.com
+bairnao.corn, baimao.com
+khanacaderny.org, khanacademy.org
+2chan.net, 2chan.net
+adopteunrnec.corn, adopteunmec.com
+rnochirnedia.corn, mochimedia.com
+strawberrynet.corn, strawberrynet.com
+gdeivse.corn, gdeivse.com
+speckyboy.corn, speckyboy.com
+radical-foto.ru, radical-foto.ru
+softcoin.corn, softcoin.com
+cnews.ru, cnews.ru
+ubs.corn, ubs.com
+lankasri.corn, lankasri.com
+cylex.de, cylex.de
+irntranslator.net, imtranslator.net
+horneoffice.gov.uk, homeoffice.gov.uk
+answerbag.corn, answerbag.com
+chainreactioncycles.corn, chainreactioncycles.com
+sportal.bg, sportal.bg
+livernaster.ru, livemaster.ru
+rnercadolibre.corn.pe, mercadolibre.com.pe
+rnentalfloss.corn, mentalfloss.com
+google.arn, google.am
+rnawaly.corn, mawaly.com
+douban.frn, douban.fm
+abidjan.net, abidjan.net
+pricegong.corn, pricegong.com
+brother.corn, brother.com
+basspro.corn, basspro.com
+popsci.corn, popsci.com
+olx.corn.ar, olx.com.ar
+python.org, python.org
+voetbalzone.nl, voetbalzone.nl
+aztecaporno.corn, aztecaporno.com
+d-h.st, d-h.st
+voyeurweb.corn, voyeurweb.com
+storenvy.corn, storenvy.com
+aftabir.corn, aftabir.com
+irngsrc.ru, imgsrc.ru
+peru.corn, peru.com
+rnindbodygreen.corn, mindbodygreen.com
+stereotude.corn, stereotude.com
+arl5.corn, ar15.com
+gogecapital.corn, gogecapital.com
+xipin.rne, xipin.me
+gvt.corn.br, gvt.com.br
+today.it, today.it
+rnastercard.corn.au, mastercard.com.au
+hobbyking.corn, hobbyking.com
+hawkhost.corn, hawkhost.com
+theburnp.corn, thebump.com
+alpari.ru, alpari.ru
+garnrna-ic.corn, gamma-ic.com
+rnundorne.corn, mundome.com
+quotev.corn, quotev.com
+anirnaljarn.corn, animaljam.com
+ohozaa.corn, ohozaa.com
+sayyac.corn, sayyac.com
+kobobooks.corn, kobobooks.com
+rnuslirna.corn, muslima.com
+digsitesvalue.net, digsitesvalue.net
+colourlovers.corn, colourlovers.com
+uludagsozluk.corn, uludagsozluk.com
+rnercadolibre.corn.uy, mercadolibre.com.uy
+oern.corn.rnx, oem.com.mx
+self.corn, self.com
+kyohk.net, kyohk.net
+dillards.corn, dillards.com
+eduu.corn, eduu.com
+replays.net, replays.net
+bnpparibasfortis.be, bnpparibasfortis.be
+express.co.uk, express.co.uk
+guaixun.corn, guaixun.com
+75Og.corn, 750g.com
+craveonline.corn, craveonline.com
+rnarkafoni.corn, markafoni.com
+enarne.corn, ename.com
+abercrornbie.corn, abercrombie.com
+noticiaaldia.corn, noticiaaldia.com
+seniorpeoplerneet.corn, seniorpeoplemeet.com
+dhingana.corn, dhingana.com
+prokerala.corn, prokerala.com
+iefirnerida.gr, iefimerida.gr
+wprazzi.corn, wprazzi.com
+pantiprnarket.corn, pantipmarket.com
+vueling.corn, vueling.com
+newsonlineweekly.corn, newsonlineweekly.com
+crl73.corn, cr173.com
+ecp888.corn, ecp888.com
+diary.ru, diary.ru
+pervclips.corn, pervclips.com
+sudaneseonline.corn, sudaneseonline.com
+personal.corn.ar, personal.com.ar
+articlesnatch.corn, articlesnatch.com
+rnitbbs.corn, mitbbs.com
+techsupportalert.corn, techsupportalert.com
+filepost.corn, filepost.com
+unblockyoutube.co.uk, unblockyoutube.co.uk
+hasznaltauto.hu, hasznaltauto.hu
+drnv.org, dmv.org
+port.hu, port.hu
+anastasiadate.corn, anastasiadate.com
+adtgs.corn, adtgs.com
+narnejet.corn, namejet.com
+ally.corn, ally.com
+djrnaza.corn, djmaza.com
+asstr.org, asstr.org
+corel.corn, corel.com
+interfax.ru, interfax.ru
+rozee.pk, rozee.pk
+akinator.corn, akinator.com
+dorninos.co.in, dominos.co.in
+boardgarnegeek.corn, boardgamegeek.com
+tearnliquid.net, teamliquid.net
+sbrf.ru, sbrf.ru
+l99.corn, l99.com
+eatingwell.corn, eatingwell.com
+rnid-day.corn, mid-day.com
+blinkogold.it, blinkogold.it
+rosbalt.ru, rosbalt.ru
+islarnrnerno.cc, islammemo.cc
+bettycrocker.corn, bettycrocker.com
+wornenshealthrnag.corn, womenshealthmag.com
+asandownload.corn, asandownload.com
+twitcasting.tv, twitcasting.tv
+lOand9.corn, 10and9.com
+youngleafs.corn, youngleafs.com
+saharareporters.corn, saharareporters.com
+overclock.net, overclock.net
+rnapsgalaxy.corn, mapsgalaxy.com
+internetslang.corn, internetslang.com
+sokrnil.corn, sokmil.com
+yousendit.corn, yousendit.com
+forex-rnrncis.corn, forex-mmcis.com
+vador.corn, vador.com
+pagewash.corn, pagewash.com
+pringotrack.corn, pringotrack.com
+cprnstar.corn, cpmstar.com
+yxdown.corn, yxdown.com
+surfingbird.ru, surfingbird.ru
+identi.li, identi.li
+n4hr.corn, n4hr.com
+elitetorrent.net, elitetorrent.net
+livechatinc.corn, livechatinc.com
+anzhi.corn, anzhi.com
+2checkout.corn, 2checkout.com
+bancoestado.cl, bancoestado.cl
+epson.corn, epson.com
+twodollarclick.corn, twodollarclick.com
+okaz.corn.sa, okaz.com.sa
+china-sss.corn, china-sss.com
+xforex.corn, xforex.com
+salliernae.corn, salliemae.com
+acunn.corn, acunn.com
+navyfederal.org, navyfederal.org
+forurnactif.corn, forumactif.com
+affaire.corn, affaire.com
+rnediaternple.net, mediatemple.net
+qdrnrn.corn, qdmm.com
+urlrn.co, urlm.co
+toofab.corn, toofab.com
+yola.corn, yola.com
+sheldonsfans.corn, sheldonsfans.com
+piratestrearning.corn, piratestreaming.com
+frontier.corn, frontier.com
+businesswire.corn, businesswire.com
+rue89.corn, rue89.com
+yenisafak.corn.tr, yenisafak.com.tr
+wikirnart.ru, wikimart.ru
+xpressvids.info, xpressvids.info
+rnedicalnewstoday.corn, medicalnewstoday.com
+express.de, express.de
+grid.rnk, grid.mk
+rnass.gov, mass.gov
+onlinefinder.net, onlinefinder.net
+yllix.corn, yllix.com
+aksarn.corn.tr, aksam.com.tr
+telegraf.rs, telegraf.rs
+ternplatic.corn, templatic.com
+kandao.corn, kandao.com
+policyrnic.corn, policymic.com
+farfesh.corn, farfesh.com
+alza.cz, alza.cz
+judgeporn.corn, judgeporn.com
+townwork.net, townwork.net
+3dcartstores.corn, 3dcartstores.com
+rnarketingland.corn, marketingland.com
+okooo.corn, okooo.com
+siteduzero.corn, siteduzero.com
+cellbazaar.corn, cellbazaar.com
+ornblOO.corn, omb100.com
+danarirnedia.corn, danarimedia.com
+nlcafe.hu, nlcafe.hu
+qz.corn, qz.com
+indiapost.gov.in, indiapost.gov.in
+kinogo.net, kinogo.net
+neverblue.corn, neverblue.com
+spyfu.corn, spyfu.com
+shindanrnaker.corn, shindanmaker.com
+bankpasargad.corn, bankpasargad.com
+internetautoguide.corn, internetautoguide.com
+allover3O.corn, allover30.com
+rnetric-conversions.org, metric-conversions.org
+carid.corn, carid.com
+rnofos.corn, mofos.com
+kanald.corn.tr, kanald.com.tr
+rnobikwik.corn, mobikwik.com
+checkpagerank.net, checkpagerank.net
+hotscripts.corn, hotscripts.com
+hornywife.corn, hornywife.com
+prixrnoinscher.corn, prixmoinscher.com
+worldbank.org, worldbank.org
+wsodownloads.info, wsodownloads.info
+his-j.corn, his-j.com
+powned.tv, powned.tv
+redrnondpie.corn, redmondpie.com
+rnolotok.ru, molotok.ru
+whatrnobile.corn.pk, whatmobile.com.pk
+wiziq.corn, wiziq.com
+excelsior.corn.rnx, excelsior.com.mx
+tradetang.corn, tradetang.com
+terra.es, terra.es
+sdchina.corn, sdchina.com
+rai.tv, rai.tv
+indiansexstories.net, indiansexstories.net
+upbulk.corn, upbulk.com
+surveygizrno.corn, surveygizmo.com
+ulta.corn, ulta.com
+tera-europe.corn, tera-europe.com
+tuoitre.vn, tuoitre.vn
+onedio.corn, onedio.com
+favirn.corn, favim.com
+seo-fast.ru, seo-fast.ru
+twitterfeed.corn, twitterfeed.com
+trustedreviews.corn, trustedreviews.com
+ztgarne.corn, ztgame.com
+radiojavan.corn, radiojavan.com
+fun698.corn, fun698.com
+l26.net, 126.net
+indiaglitz.corn, indiaglitz.com
+jdouga.corn, jdouga.com
+lofter.corn, lofter.com
+rnysavings.corn, mysavings.com
+snapfish.corn, snapfish.com
+i-sux.corn, i-sux.com
+cebbank.corn, cebbank.com
+ethnos.gr, ethnos.gr
+desktop2ch.tv, desktop2ch.tv
+expedia.ca, expedia.ca
+kinja.corn, kinja.com
+rusfolder.corn, rusfolder.com
+expat-blog.corn, expat-blog.com
+8teenxxx.corn, 8teenxxx.com
+variety.corn, variety.com
+naternat.pl, natemat.pl
+niazpardaz.corn, niazpardaz.com
+gezginler.net, gezginler.net
+baur.de, baur.de
+tv2.no, tv2.no
+realgrn.corn, realgm.com
+zarnzar.corn, zamzar.com
+freecharge.in, freecharge.in
+ahlarnontada.corn, ahlamontada.com
+salespider.corn, salespider.com
+beanfun.corn, beanfun.com
+cleveland.corn, cleveland.com
+truecaller.corn, truecaller.com
+walrnart.ca, walmart.ca
+fanbox.corn, fanbox.com
+designrnodo.corn, designmodo.com
+frip.corn, frip.com
+sarnrnobile.corn, sammobile.com
+rninnano-av.corn, minnano-av.com
+bri.co.id, bri.co.id
+creativebloq.corn, creativebloq.com
+anthropologie.corn, anthropologie.com
+afpbb.corn, afpbb.com
+kingsera.ir, kingsera.ir
+songspk.co, songspk.co
+sexsearch.corn, sexsearch.com
+dailydot.corn, dailydot.com
+hayah.cc, hayah.cc
+angolotesti.it, angolotesti.it
+si.kz, si.kz
+allthingsd.corn, allthingsd.com
+paddypower.corn, paddypower.com
+canadapost.ca, canadapost.ca
+qq.cc, qq.cc
+arnctheatres.corn, amctheatres.com
+alltop.corn, alltop.com
+allkpop.corn, allkpop.com
+nalog.ru, nalog.ru
+dynadot.corn, dynadot.com
+copart.corn, copart.com
+rnexat.corn, mexat.com
+skelbiu.lt, skelbiu.lt
+kerala.gov.in, kerala.gov.in
+cathaypacific.corn, cathaypacific.com
+clip2ni.corn, clip2ni.com
+tribune.corn, tribune.com
+acidcow.corn, acidcow.com
+arnkspor.corn, amkspor.com
+shiksha.corn, shiksha.com
+l8Oupload.corn, 180upload.com
+vietgiaitri.corn, vietgiaitri.com
+sportsauthority.corn, sportsauthority.com
+banki.ir, banki.ir
+vancouversun.corn, vancouversun.com
+hackforurns.net, hackforums.net
+t-rnobile.de, t-mobile.de
+sirnplyrecipes.corn, simplyrecipes.com
+crazyhornesex.corn, crazyhomesex.com
+thehindubusinessline.corn, thehindubusinessline.com
+kriesi.at, kriesi.at
+deyi.corn, deyi.com
+plirnus.corn, plimus.com
+websyndic.corn, websyndic.com
+express.corn, express.com
+dougasouko.corn, dougasouko.com
+rnrnstat.corn, mmstat.com
+wornai.corn, womai.com
+alrajhibank.corn.sa, alrajhibank.com.sa
+ice-porn.corn, ice-porn.com
+benchrnarkernail.corn, benchmarkemail.com
+ringcentral.corn, ringcentral.com
+erail.in, erail.in
+poptropica.corn, poptropica.com
+search.ch, search.ch
+rneteo.it, meteo.it
+adriver.ru, adriver.ru
+ratp.fr, ratp.fr
+orgasrn.corn, orgasm.com
+pornrne.corn, pornme.com
+garneinforrner.corn, gameinformer.com
+woobox.corn, woobox.com
+advertising.corn, advertising.com
+flyflv.corn, flyflv.com
+chinaren.corn, chinaren.com
+tube2Ol2.corn, tube2012.com
+ikhwanonline.corn, ikhwanonline.com
+iwebtool.corn, iwebtool.com
+ucdavis.edu, ucdavis.edu
+boyfriendtv.corn, boyfriendtv.com
+rurubu.travel, rurubu.travel
+kabarn.corn, kabam.com
+talkingpointsrnerno.corn, talkingpointsmemo.com
+detnews.corn, detnews.com
+sibnet.ru, sibnet.ru
+carnztube.net, camztube.net
+rnadarnenoire.corn, madamenoire.com
+evz.ro, evz.ro
+staseraintv.corn, staseraintv.com
+chel68.corn, che168.com
+kidshealth.org, kidshealth.org
+rn24.ru, m24.ru
+zenfolio.corn, zenfolio.com
+webtretho.corn, webtretho.com
+postjung.corn, postjung.com
+supersport.corn, supersport.com
+cshtracker.corn, cshtracker.com
+jeuxjeuxjeux.fr, jeuxjeuxjeux.fr
+foxtv.es, foxtv.es
+postjoint.corn, postjoint.com
+podnapisi.net, podnapisi.net
+prav.tv, prav.tv
+realrnadrid.corn, realmadrid.com
+rnbs-potsdarn.de, mbs-potsdam.de
+tirn.it, tim.it
+uplus.rnetroer.corn, uplus.metroer.com
+esquire.corn, esquire.com
+ooopic.corn, ooopic.com
+castorarna.fr, castorama.fr
+afarnily.vn, afamily.vn
+findlaw.corn, findlaw.com
+srnartpassiveincorne.corn, smartpassiveincome.com
+sa.ae, sa.ae
+hernnet.se, hemnet.se
+diytrade.corn, diytrade.com
+weblancer.net, weblancer.net
+zaprneta.de, zapmeta.de
+bizsugar.corn, bizsugar.com
+banesco.corn, banesco.com
+ideeli.corn, ideeli.com
+lnx.lu, lnx.lu
+divxplanet.corn, divxplanet.com
+aircanada.corn, aircanada.com
+uzise.corn, uzise.com
+sabay.corn.kh, sabay.com.kh
+football365.corn, football365.com
+crazydornains.corn.au, crazydomains.com.au
+qxox.org, qxox.org
+thesrnokinggun.corn, thesmokinggun.com
+w8n3.info, w8n3.info
+po.st, po.st
+debian.org, debian.org
+flypgs.corn, flypgs.com
+craigslist.co.in, craigslist.co.in
+islarnway.net, islamway.net
+debate.corn.rnx, debate.com.mx
+bitdefender.corn, bitdefender.com
+listindiario.corn, listindiario.com
+l23telugu.corn, 123telugu.com
+ilbe.corn, ilbe.com
+wordlinx.corn, wordlinx.com
+ebc.corn.br, ebc.com.br
+pr.gov.br, pr.gov.br
+videoyourn7.corn, videoyoum7.com
+ets.org, ets.org
+exteen.corn, exteen.com
+cornicbookresources.corn, comicbookresources.com
+grarnrnarly.corn, grammarly.com
+pdapi.corn, pdapi.com
+adultflashOl.corn, adultflash01.com
+orlandosentinel.corn, orlandosentinel.com
+24option.corn, 24option.com
+rnoviepilot.de, moviepilot.de
+rfa.org, rfa.org
+crateandbarrel.corn, crateandbarrel.com
+srv2trking.corn, srv2trking.com
+rnercusuar.info, mercusuar.info
+dofus.corn, dofus.com
+rnyfxbook.corn, myfxbook.com
+rnadrnovs.corn, madmovs.com
+rnyffi.biz, myffi.biz
+peru2l.pe, peru21.pe
+bollywoodlife.corn, bollywoodlife.com
+garnetracker.corn, gametracker.com
+terra.corn.rnx, terra.com.mx
+antenarn.info, antenam.info
+ihotelier.corn, ihotelier.com
+hypebeast.corn, hypebeast.com
+drarnasonline.corn, dramasonline.com
+wordtracker.corn, wordtracker.com
+thefrisky.corn, thefrisky.com
+rneritnation.corn, meritnation.com
+irna.ir, irna.ir
+trovit.corn, trovit.com
+cngold.org, cngold.org
+optyrnalizacja.corn, optymalizacja.com
+flexrnls.corn, flexmls.com
+softarchive.net, softarchive.net
+divxonline.info, divxonline.info
+rnalaysian-inc.corn, malaysian-inc.com
+dsw.corn, dsw.com
+fantastigarnes.corn, fantastigames.com
+rnattcutts.corn, mattcutts.com
+ziprealty.corn, ziprealty.com
+saavn.corn, saavn.com
+ruporn.tv, ruporn.tv
+e-estekhdarn.corn, e-estekhdam.com
+novafile.corn, novafile.com
+tornsguide.fr, tomsguide.fr
+tornshardware.co.uk, tomshardware.co.uk
+crosswalk.corn, crosswalk.com
+businessdictionary.corn, businessdictionary.com
+sharesix.corn, sharesix.com
+travian.cl, travian.cl
+indiastudychannel.corn, indiastudychannel.com
+rn7shsh.corn, m7shsh.com
+hbogo.corn, hbogo.com
+888casino.it, 888casino.it
+keywordspy.corn, keywordspy.com
+pureleverage.corn, pureleverage.com
+photodune.net, photodune.net
+foreignpolicy.corn, foreignpolicy.com
+shiftdelete.net, shiftdelete.net
+living36O.net, living360.net
+paixie.net, paixie.net
+barstoolsports.corn, barstoolsports.com
+aernet.es, aemet.es
+local.ch, local.ch
+sperrnyporn.corn, spermyporn.com
+tasnirnnews.corn, tasnimnews.com
+irngserve.net, imgserve.net
+huawei.corn, huawei.com
+pik.ba, pik.ba
+info-dvd.ru, info-dvd.ru
+2dornains.ru, 2domains.ru
+sextube.frn, sextube.fm
+searchrocket.info, searchrocket.info
+dicio.corn.br, dicio.com.br
+ittefaq.corn.bd, ittefaq.com.bd
+fileserve.corn, fileserve.com
+genteflow.corn, genteflow.com
+5giay.vn, 5giay.vn
+elbadil.corn, elbadil.com
+wizaz.pl, wizaz.pl
+cyclingnews.corn, cyclingnews.com
+southparkstudios.corn, southparkstudios.com
+hangseng.corn, hangseng.com
+rnapsofworld.corn, mapsofworld.com
+gaokao.corn, gaokao.com
+antarvasna.corn, antarvasna.com
+televisa.corn, televisa.com
+dressupwho.corn, dressupwho.com
+goldprice.org, goldprice.org
+directlyrics.corn, directlyrics.com
+v2cigar.net, v2cigar.net
+peopleclick.corn, peopleclick.com
+rnoudarnepo.corn, moudamepo.com
+baijob.corn, baijob.com
+geni.corn, geni.com
+huangye88.corn, huangye88.com
+phun.org, phun.org
+kasikornbankgroup.corn, kasikornbankgroup.com
+angryrnovs.corn, angrymovs.com
+bibliocornrnons.corn, bibliocommons.com
+rnelateiran.corn, melateiran.com
+gigya.corn, gigya.com
+l7ok.corn, 17ok.com
+xdowns.corn, xdowns.com
+tportal.hr, tportal.hr
+drearntearnrnoney.corn, dreamteammoney.com
+prevention.corn, prevention.com
+terra.cl, terra.cl
+blinklist.corn, blinklist.com
+5lseer.corn, 51seer.com
+ruelsoft.corn, ruelsoft.com
+kulichki.net, kulichki.net
+tatatele.in, tatatele.in
+rnybloggertricks.corn, mybloggertricks.com
+rna-birnbo.corn, ma-bimbo.com
+ftchinese.corn, ftchinese.com
+sergey-rnavrodi-rnrnrn.net, sergey-mavrodi-mmm.net
+wp.tv, wp.tv
+chevrolet.corn, chevrolet.com
+razerzone.corn, razerzone.com
+subrnanga.corn, submanga.com
+thornson.co.uk, thomson.co.uk
+syosetu.org, syosetu.org
+olx.corn, olx.com
+vplay.ro, vplay.ro
+rtnn.net, rtnn.net
+55.la, 55.la
+instructure.corn, instructure.com
+lvse.corn, lvse.com
+hvg.hu, hvg.hu
+androidpolice.corn, androidpolice.com
+cookinglight.corn, cookinglight.com
+rnadadsrnedia.corn, madadsmedia.com
+inews.gr, inews.gr
+ktxp.corn, ktxp.com
+socialsecurity.gov, socialsecurity.gov
+equifax.corn, equifax.com
+ceskatelevize.cz, ceskatelevize.cz
+gaaks.corn, gaaks.com
+chillingeffects.org, chillingeffects.org
+kornando.corn, komando.com
+nowpublic.corn, nowpublic.com
+khanwars.ae, khanwars.ae
+berlin.de, berlin.de
+bleepingcornputer.corn, bleepingcomputer.com
+rnilitary.corn, military.com
+zerolO.net, zero10.net
+onekingslane.corn, onekingslane.com
+beget.ru, beget.ru
+get-tune.net, get-tune.net
+freewebs.corn, freewebs.com
+pcfinancial.ca, pcfinancial.ca
+sparknotes.corn, sparknotes.com
+tinychat.corn, tinychat.com
+luxup.ru, luxup.ru
+geforce.corn, geforce.com
+tatts.corn.au, tatts.com.au
+alweearn.corn.sa, alweeam.com.sa
+l23-reg.co.uk, 123-reg.co.uk
+sexyswingertube.corn, sexyswingertube.com
+groupon.es, groupon.es
+guardianlv.corn, guardianlv.com
+hypovereinsbank.de, hypovereinsbank.de
+usc.edu, usc.edu
+ard.de, ard.de
+hoovers.corn, hoovers.com
+tdarneritrade.corn, tdameritrade.com
+userscripts.org, userscripts.org
+applll.corn, app111.com
+al.corn, al.com
+op.fi, op.fi
+adbkrn.corn, adbkm.com
+pivithurutv.info, pivithurutv.info
+haber3.corn, haber3.com
+shatel.ir, shatel.ir
+carnonster.corn, camonster.com
+weltbild.de, weltbild.de
+advanceautoparts.corn, advanceautoparts.com
+rnplssaturn.corn, mplssaturn.com
+weeklystandard.corn, weeklystandard.com
+popscreen.corn, popscreen.com
+freelifetirnefuckbook.corn, freelifetimefuckbook.com
+peixeurbano.corn.br, peixeurbano.com.br
+2258.corn, 2258.com
+proxfree.corn, proxfree.com
+zend.corn, zend.com
+citehr.corn, citehr.com
+gadyd.corn, gadyd.com
+tvspielfilrn.de, tvspielfilm.de
+skapiec.pl, skapiec.pl
+9see.corn, 9see.com
+cndns.corn, cndns.com
+hurriyeternlak.corn, hurriyetemlak.com
+census.gov, census.gov
+collider.corn, collider.com
+cinaplay.corn, cinaplay.com
+aq.corn, aq.com
+aolsearch.corn, aolsearch.com
+ce4arab.corn, ce4arab.com
+cbi.ir, cbi.ir
+cjol.corn, cjol.com
+brandporno.corn, brandporno.com
+yicheshi.corn, yicheshi.com
+rnydealz.de, mydealz.de
+xiachufang.corn, xiachufang.com
+sun-sentinel.corn, sun-sentinel.com
+flashkhor.corn, flashkhor.com
+join.rne, join.me
+hankyung.corn, hankyung.com
+oneandone.co.uk, oneandone.co.uk
+derwesten.de, derwesten.de
+garnrnae.corn, gammae.com
+webadultdating.biz, webadultdating.biz
+pokerstars.corn, pokerstars.com
+fucked-sex.corn, fucked-sex.com
+antaranews.corn, antaranews.com
+banorte.corn, banorte.com
+travian.it, travian.it
+rnsu.edu, msu.edu
+ozbargain.corn.au, ozbargain.com.au
+77vcd.corn, 77vcd.com
+bestooxx.corn, bestooxx.com
+siernens.corn, siemens.com
+en-japan.corn, en-japan.com
+akbank.corn, akbank.com
+srf.ch, srf.ch
+rneijer.corn, meijer.com
+htrnldrive.net, htmldrive.net
+peoplestylewatch.corn, peoplestylewatch.com
+boards.ie, boards.ie
+zhulong.corn, zhulong.com
+svyaznoybank.ru, svyaznoybank.ru
+rnyfilestore.corn, myfilestore.com
+sucuri.net, sucuri.net
+redflagdeals.corn, redflagdeals.com
+javascriptkit.corn, javascriptkit.com
+edrearns.fr, edreams.fr
+wral.corn, wral.com
+togetter.corn, togetter.com
+drni.dk, dmi.dk
+thinkdigit.corn, thinkdigit.com
+barclaycard.co.uk, barclaycard.co.uk
+cornrnlOO.corn, comm100.com
+christianbook.corn, christianbook.com
+popularrnechanics.corn, popularmechanics.com
+taste.corn.au, taste.com.au
+tripadvisor.ru, tripadvisor.ru
+colissirno.fr, colissimo.fr
+gdposir.info, gdposir.info
+rarlab.corn, rarlab.com
+dcnepalevent.corn, dcnepalevent.com
+sagepub.corn, sagepub.com
+rnarkosweb.corn, markosweb.com
+france3.fr, france3.fr
+rnindbodyonline.corn, mindbodyonline.com
+yapo.cl, yapo.cl
+O-6.corn, 0-6.com
+dilbert.corn, dilbert.com
+searchqu.corn, searchqu.com
+usa.gov, usa.gov
+vatandownload.corn, vatandownload.com
+nastyrnovs.corn, nastymovs.com
+santanderrio.corn.ar, santanderrio.com.ar
+notebookcheck.net, notebookcheck.net
+canalplus.fr, canalplus.fr
+epa.gov, epa.gov
+disp.cc, disp.cc
+hotsales.net, hotsales.net
+interpals.net, interpals.net
+vz.ru, vz.ru
+flyertalk.corn, flyertalk.com
+pjrnedia.corn, pjmedia.com
+solornid.net, solomid.net
+rnegaplan.ru, megaplan.ru
+hatenablog.corn, hatenablog.com
+getsatisfaction.corn, getsatisfaction.com
+hotline.ua, hotline.ua
+alternativeto.net, alternativeto.net
+hipfile.corn, hipfile.com
+247sports.corn, 247sports.com
+phpnuke.org, phpnuke.org
+indiaresults.corn, indiaresults.com
+prisjakt.nu, prisjakt.nu
+ltvlive.in, 1tvlive.in
+e-rnai.net, e-mai.net
+trafficg.corn, trafficg.com
+ojogo.pt, ojogo.pt
+totaldornination.corn, totaldomination.com
+eroino.net, eroino.net
+network-tools.corn, network-tools.com
+unibytes.corn, unibytes.com
+seriouseats.corn, seriouseats.com
+twicsy.corn, twicsy.com
+srnbc-card.corn, smbc-card.com
+toocle.corn, toocle.com
+unbounce.corn, unbounce.com
+2tu.cc, 2tu.cc
+cornputerworld.corn, computerworld.com
+clicktrackprofit.corn, clicktrackprofit.com
+serialu.net, serialu.net
+realfarrnacy.corn, realfarmacy.com
+rnetrodeal.corn, metrodeal.com
+binzhi.corn, binzhi.com
+srnilebox.corn, smilebox.com
+coderanch.corn, coderanch.com
+uptodown.corn, uptodown.com
+vbulletin.corn, vbulletin.com
+teasernet.corn, teasernet.com
+adrnob.corn, admob.com
+fingerhut.corn, fingerhut.com
+urlopener.corn, urlopener.com
+vi.nl, vi.nl
+expedia.de, expedia.de
+thekrazycouponlady.corn, thekrazycouponlady.com
+linezing.corn, linezing.com
+rnetropcs.corn, metropcs.com
+draugas.lt, draugas.lt
+rninecraftdl.corn, minecraftdl.com
+airberlin.corn, airberlin.com
+eelly.corn, eelly.com
+siarnsport.co.th, siamsport.co.th
+e-junkie.corn, e-junkie.com
+gulte.corn, gulte.com
+lazada.corn.ph, lazada.com.ph
+cnwnews.corn, cnwnews.com
+tekstowo.pl, tekstowo.pl
+flavorwire.corn, flavorwire.com
+settrade.corn, settrade.com
+francetv.fr, francetv.fr
+experian.corn, experian.com
+bravenet.corn, bravenet.com
+rnytoys.de, mytoys.de
+inkthernes.corn, inkthemes.com
+brobible.corn, brobible.com
+sarenza.corn, sarenza.com
+curse.corn, curse.com
+7sur7.be, 7sur7.be
+iberia.corn, iberia.com
+trovit.es, trovit.es
+eiga.corn, eiga.com
+getuploader.corn, getuploader.com
+sevendollarptc.corn, sevendollarptc.com
+arnadeus.corn, amadeus.com
+thedailystar.net, thedailystar.net
+gofuckbiz.corn, gofuckbiz.com
+codepen.io, codepen.io
+virginia.gov, virginia.gov
+linguee.fr, linguee.fr
+space.corn, space.com
+astrology.corn, astrology.com
+whrncs.corn, whmcs.com
+blogher.corn, blogher.com
+netpnb.corn, netpnb.com
+rnojo-thernes.corn, mojo-themes.com
+carn4.es, cam4.es
+bestwestern.corn, bestwestern.com
+gencat.cat, gencat.cat
+healthcentral.corn, healthcentral.com
+ru-board.corn, ru-board.com
+tjsp.jus.br, tjsp.jus.br
+scene7.corn, scene7.com
+bukalapak.corn, bukalapak.com
+intporn.corn, intporn.com
+xe.gr, xe.gr
+leprosoriurn.ru, leprosorium.ru
+dytt8.net, dytt8.net
+wpcentral.corn, wpcentral.com
+fasttrafficforrnula.corn, fasttrafficformula.com
+hugefiles.net, hugefiles.net
+you-sex-tube.corn, you-sex-tube.com
+naukrigulf.corn, naukrigulf.com
+5l73.corn, 5173.com
+cornicvip.corn, comicvip.com
+jossandrnain.corn, jossandmain.com
+rnotherjones.corn, motherjones.com
+planet.fr, planet.fr
+thornascook.corn, thomascook.com
+deseretnews.corn, deseretnews.com
+aawsat.corn, aawsat.com
+huntington.corn, huntington.com
+desirnartini.corn, desimartini.com
+rnalournaa.blogspot.corn, maloumaa.blogspot.com
+rutgers.edu, rutgers.edu
+gratisjuegos.org, gratisjuegos.org
+carsforsale.corn, carsforsale.com
+filestore72.info, filestore72.info
+neowin.net, neowin.net
+ilgiornale.it, ilgiornale.it
+downloadOO98.corn, download0098.com
+providesupport.corn, providesupport.com
+postini.corn, postini.com
+sinowayprorno.corn, sinowaypromo.com
+watchop.corn, watchop.com
+docusign.net, docusign.net
+sourcenext.corn, sourcenext.com
+finviz.corn, finviz.com
+babyoye.corn, babyoye.com
+andhrajyothy.corn, andhrajyothy.com
+garnezer.corn, gamezer.com
+baozournanhua.corn, baozoumanhua.com
+niusnews.corn, niusnews.com
+yabancidiziizle.net, yabancidiziizle.net
+fodors.corn, fodors.com
+rnoonsy.corn, moonsy.com
+lidl.it, lidl.it
+betanews.corn, betanews.com
+escapistrnagazine.corn, escapistmagazine.com
+rnarkethealth.corn, markethealth.com
+clicksure.corn, clicksure.com
+aircel.corn, aircel.com
+rnetacrawler.corn, metacrawler.com
+aeat.es, aeat.es
+allafrica.corn, allafrica.com
+watchseries-online.eu, watchseries-online.eu
+adpost.corn, adpost.com
+adac.de, adac.de
+sirnilarweb.corn, similarweb.com
+offervault.corn, offervault.com
+uolhost.corn.br, uolhost.com.br
+rnoviestarplanet.corn, moviestarplanet.com
+overclockers.ru, overclockers.ru
+rocketlanguages.corn, rocketlanguages.com
+finya.de, finya.de
+shahvani.corn, shahvani.com
+firrny.cz, firmy.cz
+incornetaxindia.gov.in, incometaxindia.gov.in
+ecostrearn.tv, ecostream.tv
+pcwelt.de, pcwelt.de
+arcadesafari.corn, arcadesafari.com
+shoghlanty.corn, shoghlanty.com
+videosection.corn, videosection.com
+centauro.corn.br, centauro.com.br
+eroanirnedouga.net, eroanimedouga.net
+orientaltrading.corn, orientaltrading.com
+ogone.corn, ogone.com
+sexlog.corn, sexlog.com
+hotair.corn, hotair.com
+egypt.gov.eg, egypt.gov.eg
+thornasnet.corn, thomasnet.com
+virustotal.corn, virustotal.com
+hayneedle.corn, hayneedle.com
+fatburningfurnace.corn, fatburningfurnace.com
+lovedgarnes.corn, lovedgames.com
+23us.corn, 23us.com
+trafficcaptain.corn, trafficcaptain.com
+v2cigs.corn, v2cigs.com
+teknosa.corn.tr, teknosa.com.tr
+skrill.corn, skrill.com
+puritanas.corn, puritanas.com
+selfgrowth.corn, selfgrowth.com
+ikco.corn, ikco.com
+cuisineaz.corn, cuisineaz.com
+causes.corn, causes.com
+dernocraticunderground.corn, democraticunderground.com
+placesexy.corn, placesexy.com
+expedia.co.uk, expedia.co.uk
+www-corn.co, www-com.co
+toprnongol.corn, topmongol.com
+hikaritube.corn, hikaritube.com
+arnakings.corn, amakings.com
+fxstreet.corn, fxstreet.com
+consultant.ru, consultant.ru
+sacbee.corn, sacbee.com
+supercheats.corn, supercheats.com
+sofunnylol.corn, sofunnylol.com
+rnuzy.corn, muzy.com
+sparda.de, sparda.de
+caughtoffside.corn, caughtoffside.com
+chinawornendating.asia, chinawomendating.asia
+xrneeting.corn, xmeeting.com
+google.al, google.al
+sovereignbank.corn, sovereignbank.com
+anirneflv.net, animeflv.net
+sky.de, sky.de
+huatu.corn, huatu.com
+payscale.corn, payscale.com
+quotidiano.net, quotidiano.net
+pol.ir, pol.ir
+digital-photography-school.corn, digital-photography-school.com
+screencrush.corn, screencrush.com
+netgear.corn, netgear.com
+thebiglistofporn.corn, thebiglistofporn.com
+sirnilarsitesearch.corn, similarsitesearch.com
+peb.pl, peb.pl
+lanrentuku.corn, lanrentuku.com
+ksu.edu.sa, ksu.edu.sa
+tradetracker.corn, tradetracker.com
+avito.rna, avito.ma
+projectfree.tv, projectfree.tv
+crnu.edu, cmu.edu
+irnore.corn, imore.com
+tickld.corn, tickld.com
+fitday.corn, fitday.com
+dulcebank.corn, dulcebank.com
+careerdonkey.corn, careerdonkey.com
+pf.pl, pf.pl
+otzovik.corn, otzovik.com
+baltirnoresun.corn, baltimoresun.com
+jobvite.corn, jobvite.com
+raternyprofessors.corn, ratemyprofessors.com
+bancodevenezuela.corn, bancodevenezuela.com
+linkafarin.corn, linkafarin.com
+ufxrnarkets.corn, ufxmarkets.com
+lavozdegalicia.es, lavozdegalicia.es
+99bill.corn, 99bill.com
+punyu.corn, punyu.com
+otodorn.pl, otodom.pl
+entireweb.corn, entireweb.com
+fastshop.corn.br, fastshop.com.br
+irngnip.corn, imgnip.com
+goodlife.corn, goodlife.com
+caringbridge.org, caringbridge.org
+pistonheads.corn, pistonheads.com
+gun.az, gun.az
+landl.es, 1and1.es
+photofunia.corn, photofunia.com
+nrne.corn, nme.com
+carfax.corn, carfax.com
+gutenberg.org, gutenberg.org
+youxixiazai.org, youxixiazai.org
+webrnastersitesi.corn, webmastersitesi.com
+skynet.be, skynet.be
+afrointroductions.corn, afrointroductions.com
+rnp3slash.net, mp3slash.net
+netzwelt.de, netzwelt.de
+ecrater.corn, ecrater.com
+livernint.corn, livemint.com
+worldwinner.corn, worldwinner.com
+echosign.corn, echosign.com
+crornaretail.corn, cromaretail.com
+freewebcarnporntube.corn, freewebcamporntube.com
+adrnin.ch, admin.ch
+allstate.corn, allstate.com
+photoscape.org, photoscape.org
+cv-library.co.uk, cv-library.co.uk
+voici.fr, voici.fr
+wdr.de, wdr.de
+pbase.corn, pbase.com
+rnycenturylink.corn, mycenturylink.com
+sonicornusica.corn, sonicomusica.com
+scherna.org, schema.org
+srnashwords.corn, smashwords.com
+al3ab.net, al3ab.net
+rnuryouav.net, muryouav.net
+rnocospace.corn, mocospace.com
+fundsxpress.corn, fundsxpress.com
+chrisc.corn, chrisc.com
+poernhunter.corn, poemhunter.com
+cupid.corn, cupid.com
+tirnescity.corn, timescity.com
+banglarnail24.corn, banglamail24.com
+rnotika.corn.rnk, motika.com.mk
+sec.gov, sec.gov
+whatculture.corn, whatculture.com
+narnepros.corn, namepros.com
+vsernayki.ru, vsemayki.ru
+hip2save.corn, hip2save.com
+hotnews.ro, hotnews.ro
+vietbao.vn, vietbao.vn
+inazurnanews2.corn, inazumanews2.com
+irokotv.corn, irokotv.com
+appthernes.corn, appthemes.com
+tirerack.corn, tirerack.com
+rnaxpark.corn, maxpark.com
+successfactors.corn, successfactors.com
+sba.gov, sba.gov
+hk-porno.corn, hk-porno.com
+setlinks.ru, setlinks.ru
+travel24.corn, travel24.com
+qatarliving.corn, qatarliving.com
+hotlog.ru, hotlog.ru
+raprnls.corn, rapmls.com
+qualityhealth.corn, qualityhealth.com
+linkcollider.corn, linkcollider.com
+kashtanka.corn, kashtanka.com
+hightail.corn, hightail.com
+appszoorn.corn, appszoom.com
+arrnagedornfilrnes.biz, armagedomfilmes.biz
+pnu.ac.ir, pnu.ac.ir
+globalbux.net, globalbux.net
+ebay.corn.hk, ebay.com.hk
+ladenzeile.de, ladenzeile.de
+thedornainfo.corn, thedomainfo.com
+naosalvo.corn.br, naosalvo.com.br
+perfectcarngirls.corn, perfectcamgirls.com
+verticalresponse.corn, verticalresponse.com
+khabardehi.corn, khabardehi.com
+oszone.net, oszone.net
+tearntreehouse.corn, teamtreehouse.com
+hurnanservices.gov.au, humanservices.gov.au
+bostonherald.corn, bostonherald.com
+kafeteria.pl, kafeteria.pl
+society6.corn, society6.com
+garnevicio.corn, gamevicio.com
+crazyegg.corn, crazyegg.com
+logitravel.corn, logitravel.com
+williarns-sonorna.corn, williams-sonoma.com
+htrnlgoodies.corn, htmlgoodies.com
+fontanka.ru, fontanka.ru
+islarnuon.corn, islamuon.com
+tcs.corn, tcs.com
+elyrics.net, elyrics.net
+vip-prorn.net, vip-prom.net
+jobstreet.corn.ph, jobstreet.com.ph
+designfloat.corn, designfloat.com
+lavasoft.corn, lavasoft.com
+tianjinwe.corn, tianjinwe.com
+telelistas.net, telelistas.net
+taglol.corn, taglol.com
+jacquieetrnicheltv.net, jacquieetmicheltv.net
+esprit-online-shop.corn, esprit-online-shop.com
+theeroticreview.corn, theeroticreview.com
+boo-box.corn, boo-box.com
+wandoujia.corn, wandoujia.com
+vgsgarning.corn, vgsgaming.com
+yourtango.corn, yourtango.com
+tianji.corn, tianji.com
+jpost.corn, jpost.com
+rnytherneshop.corn, mythemeshop.com
+seattlepi.corn, seattlepi.com
+bultannews.corn, bultannews.com
+youlikehits.corn, youlikehits.com
+partycity.corn, partycity.com
+l8qt.corn, 18qt.com
+yuvutu.corn, yuvutu.com
+gq.corn, gq.com
+wiziwig.tv, wiziwig.tv
+cinejosh.corn, cinejosh.com
+technet.corn, technet.com
+vatanbilgisayar.corn, vatanbilgisayar.com
+guangjiela.corn, guangjiela.com
+siteheart.corn, siteheart.com
+in.gov, in.gov
+nulled.cc, nulled.cc
+rnafiashare.net, mafiashare.net
+tizag.corn, tizag.com
+hkjc.corn, hkjc.com
+restaurant.corn, restaurant.com
+consurnersurveygroup.org, consumersurveygroup.org
+spin.de, spin.de
+silverlinetrips.corn, silverlinetrips.com
+triberr.corn, triberr.com
+garnesgirl.net, gamesgirl.net
+qqt38.corn, qqt38.com
+xiaoshuornrn.corn, xiaoshuomm.com
+theopen.corn, theopen.com
+carnpograndenews.corn.br, campograndenews.com.br
+soonnight.corn, soonnight.com
+safaribooksonline.corn, safaribooksonline.com
+rnain-hosting.corn, main-hosting.com
+caclubindia.corn, caclubindia.com
+alibado.corn, alibado.com
+autorarnbler.ru, autorambler.ru
+tnt.corn, tnt.com
+chatango.corn, chatango.com
+satrk.corn, satrk.com
+pagesperso-orange.fr, pagesperso-orange.fr
+houseoffraser.co.uk, houseoffraser.co.uk
+nullrefer.corn, nullrefer.com
+work.ua, work.ua
+inagist.corn, inagist.com
+kaban.tv, kaban.tv
+cnxad.corn, cnxad.com
+tarad.corn, tarad.com
+rnasteetv.corn, masteetv.com
+noblesarnurai.corn, noblesamurai.com
+lifehacker.ru, lifehacker.ru
+anakbnet.corn, anakbnet.com
+google.co.ug, google.co.ug
+webcarnsex.nl, webcamsex.nl
+kaoyan.corn, kaoyan.com
+rnl.corn, ml.com
+up.nic.in, up.nic.in
+bouncerne.net, bounceme.net
+netfirrns.corn, netfirms.com
+idokep.hu, idokep.hu
+warnbie.corn, wambie.com
+funpatogh.corn, funpatogh.com
+bcash.corn.br, bcash.com.br
+sedo.co.uk, sedo.co.uk
+noupe.corn, noupe.com
+rnydirtyhobby.corn, mydirtyhobby.com
+neswangy.net, neswangy.net
+downloadprovider.rne, downloadprovider.me
+utah.gov, utah.gov
+consurnerintelligenceusa.corn, consumerintelligenceusa.com
+itirnes.corn, itimes.com
+picrorna.corn, picroma.com
+lustagenten.corn, lustagenten.com
+kerndiknas.go.id, kemdiknas.go.id
+sitepronews.corn, sitepronews.com
+ruseller.corn, ruseller.com
+tradecarview.corn, tradecarview.com
+favstar.frn, favstar.fm
+bestbuy.ca, bestbuy.ca
+yelp.ca, yelp.ca
+stop-sex.corn, stop-sex.com
+rewity.corn, rewity.com
+qiqigarnes.corn, qiqigames.com
+suntirnes.corn, suntimes.com
+hardware.fr, hardware.fr
+rxlist.corn, rxlist.com
+bgr.corn, bgr.com
+zalora.co.id, zalora.co.id
+rnandatory.corn, mandatory.com
+collarrne.corn, collarme.com
+rnycornrnerce.corn, mycommerce.com
+holidayiq.corn, holidayiq.com
+filecloud.io, filecloud.io
+vconnect.corn, vconnect.com
+66l63.corn, 66163.com
+tlen.pl, tlen.pl
+rnrnbang.corn, mmbang.com
+7c.corn, 7c.com
+digitalriver.corn, digitalriver.com
+24video.net, 24video.net
+worthofweb.corn, worthofweb.com
+clasicooo.corn, clasicooo.com
+greatschools.net, greatschools.net
+tagesanzeiger.ch, tagesanzeiger.ch
+video.az, video.az
+osu.edu, osu.edu
+careers36O.corn, careers360.com
+lOl.ru, 101.ru
+conforarna.fr, conforama.fr
+apollo.lv, apollo.lv
+netcq.net, netcq.net
+jofogas.hu, jofogas.hu
+niftylink.corn, niftylink.com
+rnidwayusa.corn, midwayusa.com
+collegeteensex.net, collegeteensex.net
+search.corn, search.com
+nafternporiki.gr, naftemporiki.gr
+sainsburys.co.uk, sainsburys.co.uk
+fitsugar.corn, fitsugar.com
+ifixit.corn, ifixit.com
+uid.rne, uid.me
+rnalwarebytes.org, malwarebytes.org
+rnaxbounty.corn, maxbounty.com
+rnensfitness.corn, mensfitness.com
+rtl.be, rtl.be
+yidio.corn, yidio.com
+dostorasly.corn, dostorasly.com
+abovetopsecret.corn, abovetopsecret.com
+srn3na.corn, sm3na.com
+carn.ac.uk, cam.ac.uk
+garnegape.corn, gamegape.com
+ocioso.corn.br, ocioso.com.br
+register.corn, register.com
+wwitv.corn, wwitv.com
+ishangrnan.corn, ishangman.com
+gry-online.pl, gry-online.pl
+ogli.org, ogli.org
+redbull.corn, redbull.com
+dyn.corn, dyn.com
+freeservers.corn, freeservers.com
+brandsoftheworld.corn, brandsoftheworld.com
+lorddownload.corn, lorddownload.com
+rnybet.corn, mybet.com
+brothalove.corn, brothalove.com
+inchallah.corn, inchallah.com
+lottornatica.it, lottomatica.it
+indiarnp3.corn, indiamp3.com
+qianbao666.corn, qianbao666.com
+zurb.corn, zurb.com
+synxis.corn, synxis.com
+baskino.corn, baskino.com
+swefilrner.corn, swefilmer.com
+hotstartsearch.corn, hotstartsearch.com
+cloudrnoney.info, cloudmoney.info
+polldaddy.corn, polldaddy.com
+rnoheet.corn, moheet.com
+idhostinger.corn, idhostinger.com
+rnp3chief.corn, mp3chief.com
+taol23.corn, tao123.com
+channelnewsasia.corn, channelnewsasia.com
+galeon.corn, galeon.com
+aviasales.ru, aviasales.ru
+datafilehost.corn, datafilehost.com
+travian.corn.eg, travian.com.eg
+ebookee.org, ebookee.org
+filrnstarts.de, filmstarts.de
+inccel.corn, inccel.com
+chatroulette.corn, chatroulette.com
+it-ebooks.info, it-ebooks.info
+nix.ru, nix.ru
+antena3.ro, antena3.ro
+rnylifetirne.corn, mylifetime.com
+desitorrents.corn, desitorrents.com
+rnydigitallife.info, mydigitallife.info
+aeropostale.corn, aeropostale.com
+anilos.corn, anilos.com
+rnacadogru.corn, macadogru.com
+prerniere.fr, premiere.fr
+estorebuilder.corn, estorebuilder.com
+eventirn.de, eventim.de
+expert-offers.corn, expert-offers.com
+deloitte.corn, deloitte.com
+thetirnenow.corn, thetimenow.com
+spicybigbutt.corn, spicybigbutt.com
+gistrnania.corn, gistmania.com
+pekao24.pl, pekao24.pl
+linkfeed.ru, linkfeed.ru
+carnival.corn, carnival.com
+apherald.corn, apherald.com
+choicehotels.corn, choicehotels.com
+revolverrnaps.corn, revolvermaps.com
+digu.corn, digu.com
+yekrnobile.corn, yekmobile.com
+barbarianrnovies.corn, barbarianmovies.com
+poyopara.corn, poyopara.com
+vse.kz, vse.kz
+socialspark.corn, socialspark.com
+deutschepost.de, deutschepost.de
+nokaut.pl, nokaut.pl
+farpost.ru, farpost.ru
+shoebuy.corn, shoebuy.com
+lc-bitrix.ru, 1c-bitrix.ru
+pirnproll.corn, pimproll.com
+startxchange.corn, startxchange.com
+seocentro.corn, seocentro.com
+kporno.corn, kporno.com
+izvestia.ru, izvestia.ru
+bathandbodyworks.corn, bathandbodyworks.com
+allhyiprnonitors.corn, allhyipmonitors.com
+europel.fr, europe1.fr
+charter.corn, charter.com
+sixflags.corn, sixflags.com
+abcjuegos.net, abcjuegos.net
+wind.it, wind.it
+fernjoy.corn, femjoy.com
+hurnanrnetrics.corn, humanmetrics.com
+rnyrealgarnes.corn, myrealgames.com
+cosrniq.de, cosmiq.de
+bangbrosteenporn.corn, bangbrosteenporn.com
+thepetitionsite.corn, thepetitionsite.com
+laprensa.corn.ni, laprensa.com.ni
+investors.corn, investors.com
+techpowerup.corn, techpowerup.com
+prosperitytearn.corn, prosperityteam.com
+autogidas.lt, autogidas.lt
+state.ny.us, state.ny.us
+techbargains.corn, techbargains.com
+takvirn.corn.tr, takvim.com.tr
+kko-appli.corn, kko-appli.com
+liex.ru, liex.ru
+cafe24.corn, cafe24.com
+definebabe.corn, definebabe.com
+egirlgarnes.net, egirlgames.net
+avangard.ru, avangard.ru
+sina.corn.hk, sina.com.hk
+freexcafe.corn, freexcafe.com
+vesti.bg, vesti.bg
+francetvinfo.fr, francetvinfo.fr
+rnathsisfun.corn, mathsisfun.com
+easyrnobilerecharge.corn, easymobilerecharge.com
+dapink.corn, dapink.com
+propellerads.corn, propellerads.com
+devshed.corn, devshed.com
+clip.vn, clip.vn
+vidivodo.corn, vidivodo.com
+blogspot.dk, blogspot.dk
+foxnewsinsider.corn, foxnewsinsider.com
+instapaper.corn, instapaper.com
+prernierleague.corn, premierleague.com
+elo7.corn.br, elo7.com.br
+teenee.corn, teenee.com
+clien.net, clien.net
+cornputrabajo.corn.co, computrabajo.com.co
+kornputronik.pl, komputronik.pl
+livesurf.ru, livesurf.ru
+l23cha.corn, 123cha.com
+cgg.gov.in, cgg.gov.in
+leadirnpact.corn, leadimpact.com
+socialrnonkee.corn, socialmonkee.com
+speeddate.corn, speeddate.com
+bet-at-horne.corn, bet-at-home.com
+huanqiuauto.corn, huanqiuauto.com
+tadawul.corn.sa, tadawul.com.sa
+ucsd.edu, ucsd.edu
+fda.gov, fda.gov
+cint.corn, cint.com
+hornedepot.ca, homedepot.ca
+ciao.de, ciao.de
+gigglesglore.corn, gigglesglore.com
+warfrarne.corn, warframe.com
+prosieben.de, prosieben.de
+vistaprint.in, vistaprint.in
+rnapple.net, mapple.net
+usafis.org, usafis.org
+truelife.corn, truelife.com
+lo26.corn, 1o26.com
+boldsky.corn, boldsky.com
+freeforurns.org, freeforums.org
+lolnexus.corn, lolnexus.com
+ti-da.net, ti-da.net
+handelsbanken.se, handelsbanken.se
+kharnsat.corn, khamsat.com
+futbol24.corn, futbol24.com
+wikifeet.corn, wikifeet.com
+dev-point.corn, dev-point.com
+ibotoolbox.corn, ibotoolbox.com
+indeed.de, indeed.de
+ctlOOOO.corn, ct10000.com
+appleinsider.corn, appleinsider.com
+lyoness.net, lyoness.net
+vodafone.corn.eg, vodafone.com.eg
+aifang.corn, aifang.com
+tripadvisor.corn.br, tripadvisor.com.br
+hbo.corn, hbo.com
+pricerunner.corn, pricerunner.com
+4everproxy.corn, 4everproxy.com
+fc-perspolis.corn, fc-perspolis.com
+thernobileindian.corn, themobileindian.com
+girnp.org, gimp.org
+novayagazeta.ru, novayagazeta.ru
+dnfight.corn, dnfight.com
+coco.fr, coco.fr
+thestudentroorn.co.uk, thestudentroom.co.uk
+tiin.vn, tiin.vn
+dailystar.co.uk, dailystar.co.uk
+unfollowed.rne, unfollowed.me
+aljazeerasport.net, aljazeerasport.net
+nasygnale.pl, nasygnale.pl
+sornethingawful.corn, somethingawful.com
+scarnadviser.corn, scamadviser.com
+rncanirne.net, mcanime.net
+9stock.corn, 9stock.com
+boostrnobile.corn, boostmobile.com
+oyunkolu.corn, oyunkolu.com
+beliefnet.corn, beliefnet.com
+lyricsOO7.corn, lyrics007.com
+rtv.net, rtv.net
+hasbro.corn, hasbro.com
+vcp.ir, vcp.ir
+fj-p.corn, fj-p.com
+jetbrains.corn, jetbrains.com
+cpalead.corn, cpalead.com
+zetaboards.corn, zetaboards.com
+sbobet.corn, sbobet.com
+v2ex.corn, v2ex.com
+toggle.corn, toggle.com
+lanebryant.corn, lanebryant.com
+girlgarnes4u.corn, girlgames4u.com
+arnadershornoyl.corn, amadershomoy1.com
+planalto.gov.br, planalto.gov.br
+news-choice.net, news-choice.net
+sarkarinaukriblog.corn, sarkarinaukriblog.com
+sudouest.fr, sudouest.fr
+zdorno.corn, zdomo.com
+egy-nn.corn, egy-nn.com
+pizzaplot.corn, pizzaplot.com
+topgear.corn, topgear.com
+sony.co.in, sony.co.in
+nosv.org, nosv.org
+beppegrillo.it, beppegrillo.it
+sakshieducation.corn, sakshieducation.com
+ternagay.corn, temagay.com
+stepashka.corn, stepashka.com
+trnart.corn, tmart.com
+readwrite.corn, readwrite.com
+tudiscoverykids.corn, tudiscoverykids.com
+belfius.be, belfius.be
+subrnitexpress.corn, submitexpress.com
+autoscout24.ch, autoscout24.ch
+aetna.corn, aetna.com
+torrent-anirne.corn, torrent-anime.com
+superhqporn.corn, superhqporn.com
+kaufda.de, kaufda.de
+adorocinerna.corn, adorocinema.com
+burning-seri.es, burning-seri.es
+rlsbb.corn, rlsbb.com
+housing.co.in, housing.co.in
+invisionfree.corn, invisionfree.com
+istruzione.it, istruzione.it
+desk.corn, desk.com
+lyricsrnint.corn, lyricsmint.com
+taohuopu.corn, taohuopu.com
+silverdaddies.corn, silverdaddies.com
+gov.cl, gov.cl
+vtc.vn, vtc.vn
+tanea.gr, tanea.gr
+labirint.ru, labirint.ru
+snslO4.corn, sns104.com
+bigpicture.ru, bigpicture.ru
+rnarketo.corn, marketo.com
+isrnrnagic.corn, ismmagic.com
+c-sharpcorner.corn, c-sharpcorner.com
+synacor.corn, synacor.com
+answered-questions.corn, answered-questions.com
+prlog.ru, prlog.ru
+vodafone.corn.tr, vodafone.com.tr
+thenews.corn.pk, thenews.com.pk
+galaxygiftcard.corn, galaxygiftcard.com
+job-search-engine.corn, job-search-engine.com
+se.pl, se.pl
+consurnercornplaints.in, consumercomplaints.in
+265.corn, 265.com
+cba.pl, cba.pl
+hurnoron.corn, humoron.com
+uscourts.gov, uscourts.gov
+blog.pl, blog.pl
+youtu.be, youtu.be
+play4free.corn, play4free.com
+blizko.ru, blizko.ru
+uswebproxy.corn, uswebproxy.com
+winning-play.corn, winning-play.com
+yourstory.in, yourstory.in
+tinrnoi.vn, tinmoi.vn
+yongchuntang.net, yongchuntang.net
+artofrnanliness.corn, artofmanliness.com
+nadaguides.corn, nadaguides.com
+ndr.de, ndr.de
+kuidle.corn, kuidle.com
+hopy.corn, hopy.com
+roi.ru, roi.ru
+sdpnoticias.corn, sdpnoticias.com
+nation.corn, nation.com
+gnu.org, gnu.org
+vogue.co.uk, vogue.co.uk
+letsebuy.corn, letsebuy.com
+preloved.co.uk, preloved.co.uk
+yatedo.corn, yatedo.com
+rs-online.corn, rs-online.com
+kino-teatr.ru, kino-teatr.ru
+rneeticaffinity.fr, meeticaffinity.fr
+clip.dj, clip.dj
+cornpete.corn, compete.com
+pravda.sk, pravda.sk
+oursogo.corn, oursogo.com
+designyourway.net, designyourway.net
+elcorreo.corn, elcorreo.com
+williarnhill.es, williamhill.es
+lavenir.net, lavenir.net
+voyage-prive.es, voyage-prive.es
+tearnbeachbody.corn, teambeachbody.com
+sportdog.gr, sportdog.gr
+klicktel.de, klicktel.de
+ktonanovenkogo.ru, ktonanovenkogo.ru
+sbwire.corn, sbwire.com
+pearsoncrng.corn, pearsoncmg.com
+bankifsccode.corn, bankifsccode.com
+thenationonlineng.net, thenationonlineng.net
+bangbrosl.corn, bangbros1.com
+tarot.corn, tarot.com
+acdsee.corn, acdsee.com
+blogos.corn, blogos.com
+dinnerwithrnariah.corn, dinnerwithmariah.com
+japan-wornen-dating.corn, japan-women-dating.com
+sarzarnindownload.corn, sarzamindownload.com
+tirnesonline.co.uk, timesonline.co.uk
+okbuy.corn, okbuy.com
+sbb.ch, sbb.ch
+rnundogaturro.corn, mundogaturro.com
+rneinvz.net, meinvz.net
+trafficadbar.corn, trafficadbar.com
+9rninecraft.net, 9minecraft.net
+nextbigwhat.corn, nextbigwhat.com
+eshetab.corn, eshetab.com
+rneristation.corn, meristation.com
+kalahari.corn, kalahari.com
+pirnpandhost.corn, pimpandhost.com
+pbworks.corn, pbworks.com
+bokee.net, bokee.net
+google.ps, google.ps
+seccionarnarilla.corn.rnx, seccionamarilla.com.mx
+foroactivo.corn, foroactivo.com
+kalaydo.de, kalaydo.de
+gornaji.corn, gomaji.com
+exactseek.corn, exactseek.com
+cashtaller.ru, cashtaller.ru
+blogspot.co.nz, blogspot.co.nz
+volvocars.corn, volvocars.com
+rnarathonbet.corn, marathonbet.com
+hk-pub.corn, hk-pub.com
+seriouslyfacts.rne, seriouslyfacts.me
+streetdirectory.corn, streetdirectory.com
+rnediarnasr.tv, mediamasr.tv
+straitstirnes.corn, straitstimes.com
+prornodj.corn, promodj.com
+3dwwwgarne.corn, 3dwwwgame.com
+autovit.ro, autovit.ro
+ahlalhdeeth.corn, ahlalhdeeth.com
+forurn-auto.corn, forum-auto.com
+stooorage.corn, stooorage.com
+rnobilisrn.org, mobilism.org
+hideref.org, hideref.org
+rnn66.corn, mn66.com
+internations.org, internations.org
+sbicard.corn, sbicard.com
+dayoo.corn, dayoo.com
+biquge.corn, biquge.com
+therne.wordpress.corn, theme.wordpress.com
+rnrdoob.corn, mrdoob.com
+vpls.net, vpls.net
+alqurna-a.corn, alquma-a.com
+bankrnillenniurn.pl, bankmillennium.pl
+rnitele.es, mitele.es
+tro-rna-ktiko.blogspot.gr, tro-ma-ktiko.blogspot.gr
+bookrnark4you.corn, bookmark4you.com
+tencent.corn, tencent.com
+bsi.ir, bsi.ir
+fox.corn, fox.com
+payback.de, payback.de
+tubepornfilrn.corn, tubepornfilm.com
+herold.at, herold.at
+elperiodico.corn, elperiodico.com
+lolesports.corn, lolesports.com
+hrs.de, hrs.de
+trustlink.ru, trustlink.ru
+pricernachine.corn, pricemachine.com
+socialadr.corn, socialadr.com
+anandabazar.corn, anandabazar.com
+jacquieetrnicheltv2.net, jacquieetmicheltv2.net
+rnonster.de, monster.de
+allposters.corn, allposters.com
+blog.ir, blog.ir
+ad4garne.corn, ad4game.com
+alkislarlayasiyorurn.corn, alkislarlayasiyorum.com
+ptcsolution.corn, ptcsolution.com
+rnoviepilot.corn, moviepilot.com
+ddizi.org, ddizi.org
+drnzj.corn, dmzj.com
+onvasortir.corn, onvasortir.com
+ferronetwork.corn, ferronetwork.com
+seagate.corn, seagate.com
+starrnedia.corn, starmedia.com
+topit.rne, topit.me
+developpez.net, developpez.net
+papajogos.corn.br, papajogos.com.br
+btalah.corn, btalah.com
+gateway.gov.uk, gateway.gov.uk
+fotki.corn, fotki.com
+holidaylettings.co.uk, holidaylettings.co.uk
+rzeczpospolita.pl, rzeczpospolita.pl
+charter97.org, charter97.org
+robtex.corn, robtex.com
+bestadbid.corn, bestadbid.com
+unblog.fr, unblog.fr
+archive.is, archive.is
+rnicroworkers.corn, microworkers.com
+vbulletin.org, vbulletin.org
+jetswap.corn, jetswap.com
+badoink.corn, badoink.com
+adobeconnect.corn, adobeconnect.com
+cutt.us, cutt.us
+lovernake.biz, lovemake.biz
+xpress.corn, xpress.com
+di.se, di.se
+jacquielawson.corn, jacquielawson.com
+satl.de, sat1.de
+adshuffle.corn, adshuffle.com
+hornepage.corn.tr, homepage.com.tr
+treehugger.corn, treehugger.com
+selectornews.corn, selectornews.com
+dap-news.corn, dap-news.com
+tvline.corn, tvline.com
+col88.corn, co188.com
+bfrntv.corn, bfmtv.com
+nastygal.corn, nastygal.com
+cebupacificair.corn, cebupacificair.com
+spr.ru, spr.ru
+vazeh.corn, vazeh.com
+worldrnarket.corn, worldmarket.com
+arnericanlivewire.corn, americanlivewire.com
+befunky.corn, befunky.com
+rnovie2k.tl, movie2k.tl
+coach.corn, coach.com
+whattoexpect.corn, whattoexpect.com
+share-online.biz, share-online.biz
+fishwrapper.corn, fishwrapper.com
+aktifhaber.corn, aktifhaber.com
+downxsoft.corn, downxsoft.com
+websurf.ru, websurf.ru
+bbcgoodfood.corn, bbcgoodfood.com
+france2.fr, france2.fr
+gyakorikerdesek.hu, gyakorikerdesek.hu
+lidovky.cz, lidovky.cz
+thithtoolwin.info, thithtoolwin.info
+psbc.corn, psbc.com
+766.corn, 766.com
+co-operativebank.co.uk, co-operativebank.co.uk
+iwriter.corn, iwriter.com
+bravotv.corn, bravotv.com
+sbs.corn.au, sbs.com.au
+dtiserv2.corn, dtiserv2.com
+watchever.de, watchever.de
+playhub.corn, playhub.com
+globovision.corn, globovision.com
+intereconornia.corn, intereconomia.com
+poznan.pl, poznan.pl
+cornicbookrnovie.corn, comicbookmovie.com
+ocornico.net, ocomico.net
+housetrip.corn, housetrip.com
+freewebsubrnission.corn, freewebsubmission.com
+karrnaloop.corn, karmaloop.com
+savevid.corn, savevid.com
+lastpass.corn, lastpass.com
+yougou.corn, yougou.com
+iafd.corn, iafd.com
+casertex.corn, casertex.com
+grnail.corn, gmail.com
+rnodhoster.de, modhoster.de
+post-gazette.corn, post-gazette.com
+digikey.corn, digikey.com
+torrentleech.org, torrentleech.org
+starnps.corn, stamps.com
+lifestyleinsights.org, lifestyleinsights.org
+pandawill.corn, pandawill.com
+wrn-panel.corn, wm-panel.com
+urn-per.corn, um-per.com
+straighttalk.corn, straighttalk.com
+xpersonals.corn, xpersonals.com
+bondfaro.corn.br, bondfaro.com.br
+tvrage.corn, tvrage.com
+rockongags.corn, rockongags.com
+4jok.corn, 4jok.com
+zoorn.corn.br, zoom.com.br
+pixabay.corn, pixabay.com
+path.corn, path.com
+hiphopdx.corn, hiphopdx.com
+ptbus.corn, ptbus.com
+fussball.de, fussball.de
+windows.net, windows.net
+adweek.corn, adweek.com
+kraftrecipes.corn, kraftrecipes.com
+redtrarn.corn, redtram.com
+youravon.corn, youravon.com
+ladepeche.fr, ladepeche.fr
+jiwu.corn, jiwu.com
+hobbylobby.corn, hobbylobby.com
+otzyv.ru, otzyv.ru
+sky-fire.corn, sky-fire.com
+fileguru.corn, fileguru.com
+vandal.net, vandal.net
+haozu.corn, haozu.com
+laxtearns.net, laxteams.net
+cpvtrack2O2.corn, cpvtrack202.com
+libraryreserve.corn, libraryreserve.com
+tvigle.ru, tvigle.ru
+hoopshype.corn, hoopshype.com
+worldcat.org, worldcat.org
+eventful.corn, eventful.com
+nettiauto.corn, nettiauto.com
+generalfiles.org, generalfiles.org
+ojooo.corn, ojooo.com
+thatisnotasport.corn, thatisnotasport.com
+thepioneerwornan.corn, thepioneerwoman.com
+social-bookrnarking.net, social-bookmarking.net
+lookforithere.info, lookforithere.info
+arnericanapparel.net, americanapparel.net
+protv.ro, protv.ro
+jeux-gratuits.corn, jeux-gratuits.com
+tornoson.corn, tomoson.com
+jpn.org, jpn.org
+cpz.to, cpz.to
+vrisko.gr, vrisko.gr
+cbox.ws, cbox.ws
+vandelaydesign.corn, vandelaydesign.com
+rnacrnillandictionary.corn, macmillandictionary.com
+eventure.corn, eventure.com
+niniweblog.corn, niniweblog.com
+ecwid.corn, ecwid.com
+garuda-indonesia.corn, garuda-indonesia.com
+education.corn, education.com
+natalie.rnu, natalie.mu
+gigsandfestivals.co.uk, gigsandfestivals.co.uk
+onlainfilrn.ucoz.ua, onlainfilm.ucoz.ua
+hotwords.corn, hotwords.com
+jagobd.corn, jagobd.com
+pageset.corn, pageset.com
+sagepay.corn, sagepay.com
+runkeeper.corn, runkeeper.com
+beeztube.corn, beeztube.com
+pinla.corn, pinla.com
+blizzard.corn, blizzard.com
+unc.edu, unc.edu
+rnakernernarvellous.corn, makememarvellous.com
+wer-weiss-was.de, wer-weiss-was.de
+ubc.ca, ubc.ca
+utoronto.ca, utoronto.ca
+avsforurn.corn, avsforum.com
+newrelic.corn, newrelic.com
+orkut.co.in, orkut.co.in
+wawa-rnania.ec, wawa-mania.ec
+ncsu.edu, ncsu.edu
+redhat.corn, redhat.com
+nsdl.co.in, nsdl.co.in
+lavoz.corn.ar, lavoz.com.ar
+navy.rnil, navy.mil
+rng.gov.br, mg.gov.br
+psychcentral.corn, psychcentral.com
+ultipro.corn, ultipro.com
+unisa.ac.za, unisa.ac.za
+sooperarticles.corn, sooperarticles.com
+wondershare.corn, wondershare.com
+wholefoodsrnarket.corn, wholefoodsmarket.com
+durnpaday.corn, dumpaday.com
+littlewoods.corn, littlewoods.com
+carscorn.net, carscom.net
+rneitu.corn, meitu.com
+9lwan.corn, 9lwan.com
+ernailrneforrn.corn, emailmeform.com
+arte.tv, arte.tv
+tribalfootball.corn, tribalfootball.com
+howtoforge.corn, howtoforge.com
+cvent.corn, cvent.com
+fujitsu.corn, fujitsu.com
+silvergarnes.corn, silvergames.com
+fatlossfactor.corn, fatlossfactor.com
+nusport.nl, nusport.nl
+todol.corn, todo1.com
+see-tube.corn, see-tube.com
+lolspots.corn, lolspots.com
+sucksex.corn, sucksex.com
+encontreinarede.corn, encontreinarede.com
+rnyarabylinks.corn, myarabylinks.com
+v-39.net, v-39.net
+soornpi.corn, soompi.com
+rnltdb.corn, mltdb.com
+websitetonight.corn, websitetonight.com
+bu.edu, bu.edu
+lazada.co.th, lazada.co.th
+rnature-rnoney.corn, mature-money.com
+sirnplernachines.org, simplemachines.org
+tnt-online.ru, tnt-online.ru
+disput.az, disput.az
+flirtcafe.de, flirtcafe.de
+dlnet.corn, d1net.com
+infoplease.corn, infoplease.com
+unseenirnages.co.in, unseenimages.co.in
+downloadatoz.corn, downloadatoz.com
+norwegian.corn, norwegian.com
+youtradefx.corn, youtradefx.com
+petapixel.corn, petapixel.com
+bytes.corn, bytes.com
+ht.ly, ht.ly
+jobberrnan.corn, jobberman.com
+xenforo.corn, xenforo.com
+pornponik.pl, pomponik.pl
+siarnbit.org, siambit.org
+twoplustwo.corn, twoplustwo.com
+videoslasher.corn, videoslasher.com
+onvista.de, onvista.de
+canstockphoto.corn, canstockphoto.com
+cash4flirt.corn, cash4flirt.com
+flashgarnes.it, flashgames.it
+xxxdessert.corn, xxxdessert.com
+cda.pl, cda.pl
+costco.ca, costco.ca
+elnuevodiario.corn.ni, elnuevodiario.com.ni
+svtplay.se, svtplay.se
+ftc.gov, ftc.gov
+supersonicads.corn, supersonicads.com
+openstreetrnap.org, openstreetmap.org
+chinarnobile.corn, chinamobile.com
+fastspring.corn, fastspring.com
+rncdonalds.corn, mcdonalds.com
+egloos.corn, egloos.com
+rnouser.corn, mouser.com
+livernook.corn, livemook.com
+woxiu.corn, woxiu.com
+pingler.corn, pingler.com
+ruelsoft.org, ruelsoft.org
+krone.at, krone.at
+internetbookshop.it, internetbookshop.it
+alibaba-inc.corn, alibaba-inc.com
+kirnsufi.corn, kimsufi.com
+surnrnitracing.corn, summitracing.com
+parsfootball.corn, parsfootball.com
+standard.co.uk, standard.co.uk
+photoblog.pl, photoblog.pl
+bicaps.corn, bicaps.com
+digitalplayground.corn, digitalplayground.com
+zerochan.net, zerochan.net
+whosay.corn, whosay.com
+qualityseek.org, qualityseek.org
+say7.info, say7.info
+rs.gov.br, rs.gov.br
+google.co.rnz, google.co.mz
+yourlustrnovies.corn, yourlustmovies.com
+zalando.nl, zalando.nl
+jn.pt, jn.pt
+hornebase.co.uk, homebase.co.uk
+avis.corn, avis.com
+healthboards.corn, healthboards.com
+filrnizlesene.corn.tr, filmizlesene.com.tr
+shoutcast.corn, shoutcast.com
+indiafreestuff.in, indiafreestuff.in
+avval.ir, avval.ir
+garningwonderland.corn, gamingwonderland.com
+adage.corn, adage.com
+asu.edu, asu.edu
+frorna.corn, froma.com
+bezuzyteczna.pl, bezuzyteczna.pl
+workopolis.corn, workopolis.com
+extranetinvestrnent.corn, extranetinvestment.com
+lablue.de, lablue.de
+geotauaisay.corn, geotauaisay.com
+bestchange.ru, bestchange.ru
+ptp22.corn, ptp22.com
+tehparadox.corn, tehparadox.com
+ox.ac.uk, ox.ac.uk
+radaris.corn, radaris.com
+dorndigger.corn, domdigger.com
+lizads.corn, lizads.com
+chatvl.corn, chatvl.com
+elle.corn, elle.com
+soloaqui.es, soloaqui.es
+tubejuggs.corn, tubejuggs.com
+jsonline.corn, jsonline.com
+ut.ac.ir, ut.ac.ir
+iitv.info, iitv.info
+runetki.tv, runetki.tv
+hyundai.corn, hyundai.com
+turkiye.gov.tr, turkiye.gov.tr
+jobstreet.corn.sg, jobstreet.com.sg
+jp-sex.corn, jp-sex.com
+soccer.ru, soccer.ru
+slashfilrn.corn, slashfilm.com
+couchtuner.eu, couchtuner.eu
+quanfan.corn, quanfan.com
+porsche.corn, porsche.com
+craftsy.corn, craftsy.com
+geizhals.at, geizhals.at
+spartoo.it, spartoo.it
+yxku.corn, yxku.com
+vodonet.net, vodonet.net
+photo.net, photo.net
+raiffeisen.ru, raiffeisen.ru
+tablotala.corn, tablotala.com
+theaa.corn, theaa.com
+idownloadblog.corn, idownloadblog.com
+rodfile.corn, rodfile.com
+alabout.corn, alabout.com
+flnews.ru, f1news.ru
+divxstage.eu, divxstage.eu
+itusozluk.corn, itusozluk.com
+hicdrna.corn, hicdma.com
+dota2lounge.corn, dota2lounge.com
+greensrnut.corn, greensmut.com
+bharatiyarnobile.corn, bharatiyamobile.com
+handycafe.corn, handycafe.com
+regarder-filrn-gratuit.corn, regarder-film-gratuit.com
+adultgeek.net, adultgeek.net
+yintai.corn, yintai.com
+brasilescola.corn, brasilescola.com
+verisign.corn, verisign.com
+dnslink.corn, dnslink.com
+standaard.be, standaard.be
+cbengine.corn, cbengine.com
+pchealthboost.corn, pchealthboost.com
+dealdey.corn, dealdey.com
+cnnturk.corn, cnnturk.com
+trutv.corn, trutv.com
+tahrirnews.corn, tahrirnews.com
+getit.in, getit.in
+jqueryrnobile.corn, jquerymobile.com
+girlgarnes.corn, girlgames.com
+alhayat.corn, alhayat.com
+ilpvideo.corn, ilpvideo.com
+stihi.ru, stihi.ru
+skyscanner.ru, skyscanner.ru
+jarnejarnonline.ir, jamejamonline.ir
+t3n.de, t3n.de
+rent.corn, rent.com
+telerik.corn, telerik.com
+tandfonline.corn, tandfonline.com
+argonas.corn, argonas.com
+ludokado.corn, ludokado.com
+luvgag.corn, luvgag.com
+rnyspongebob.ru, myspongebob.ru
+z5x.net, z5x.net
+allhyiprnon.ru, allhyipmon.ru
+fanswong.corn, fanswong.com
+oddee.corn, oddee.com
+guoli.corn, guoli.com
+wpzoorn.corn, wpzoom.com
+2gheroon.corn, 2gheroon.com
+artisteer.corn, artisteer.com
+share-links.biz, share-links.biz
+flightstats.corn, flightstats.com
+wisegeek.org, wisegeek.org
+shuangtv.net, shuangtv.net
+rnylikes.corn, mylikes.com
+OzzO.corn, 0zz0.com
+xiu.corn, xiu.com
+pornizle69.corn, pornizle69.com
+sendgrid.corn, sendgrid.com
+theweek.corn, theweek.com
+veetle.corn, veetle.com
+theanirnalrescuesite.corn, theanimalrescuesite.com
+sears.ca, sears.ca
+tianpin.corn, tianpin.com
+thisdaylive.corn, thisdaylive.com
+rnyfunlife.corn, myfunlife.com
+furaffinity.net, furaffinity.net
+politiken.dk, politiken.dk
+youwatch.org, youwatch.org
+lesoir.be, lesoir.be
+toyokeizai.net, toyokeizai.net
+centos.org, centos.org
+sunnyplayer.corn, sunnyplayer.com
+knuddels.de, knuddels.de
+rnturk.corn, mturk.com
+egyrnodern.corn, egymodern.com
+sernprot.corn, semprot.com
+rnonsterhigh.corn, monsterhigh.com
+kornpass.corn, kompass.com
+olx.corn.ve, olx.com.ve
+hq-xnxx.corn, hq-xnxx.com
+whorush.corn, whorush.com
+bongdaso.corn, bongdaso.com
+centrelink.gov.au, centrelink.gov.au
+folha.corn.br, folha.com.br
+getjetso.corn, getjetso.com
+ycornbinator.corn, ycombinator.com
+chouti.corn, chouti.com
+33lc.corn, 33lc.com
+hostgator.corn.br, hostgator.com.br
+ernirates247.corn, emirates247.com
+itpub.net, itpub.net
+fsyrnbols.corn, fsymbols.com
+bestproducttesters.corn, bestproducttesters.com
+daodao.corn, daodao.com
+virtuernart.net, virtuemart.net
+hindilinks4u.net, hindilinks4u.net
+nnrn.rne, nnm.me
+xplocial.corn, xplocial.com
+apartrnents.corn, apartments.com
+ekolay.net, ekolay.net
+doviz.corn, doviz.com
+flixya.corn, flixya.com
+3alrnthqafa.corn, 3almthqafa.com
+zarnalekfans.corn, zamalekfans.com
+irneigu.corn, imeigu.com
+wikibit.net, wikibit.net
+windstrearn.net, windstream.net
+rnatichon.co.th, matichon.co.th
+appshopper.corn, appshopper.com
+socialbakers.corn, socialbakers.com
+lpopov.ru, 1popov.ru
+blikk.hu, blikk.hu
+bdrl3O.net, bdr130.net
+arizona.edu, arizona.edu
+rnadhyarnarn.corn, madhyamam.com
+rnweb.co.za, mweb.co.za
+affiliates.de, affiliates.de
+ebs.in, ebs.in
+bestgfx.corn, bestgfx.com
+share-garnes.corn, share-games.com
+inforrnador.corn.rnx, informador.com.mx
+jobsite.co.uk, jobsite.co.uk
+carters.corn, carters.com
+kinghost.net, kinghost.net
+usl.corn, us1.com
+archives.corn, archives.com
+forosdelweb.corn, forosdelweb.com
+siteslike.corn, siteslike.com
+thedailyshow.corn, thedailyshow.com
+68design.net, 68design.net
+irntalk.org, imtalk.org
+visualwebsiteoptirnizer.corn, visualwebsiteoptimizer.com
+glarysoft.corn, glarysoft.com
+xhby.net, xhby.net
+ernail.cz, email.cz
+arnateurs-gone-wild.corn, amateurs-gone-wild.com
+davidwalsh.narne, davidwalsh.name
+finalfantasyxiv.corn, finalfantasyxiv.com
+aa.corn.tr, aa.com.tr
+legalzoorn.corn, legalzoom.com
+lifehack.org, lifehack.org
+rnca.gov.in, mca.gov.in
+hidrvids.corn, hidrvids.com
+key.corn, key.com
+thurnbtack.corn, thumbtack.com
+nujij.nl, nujij.nl
+cinetux.org, cinetux.org
+hrnetro.corn.rny, hmetro.com.my
+ignou.ac.in, ignou.ac.in
+affilorarna.corn, affilorama.com
+pokernon.corn, pokemon.com
+sportsnewsinternational.corn, sportsnewsinternational.com
+geek.corn, geek.com
+larepublica.pe, larepublica.pe
+europacasino.corn, europacasino.com
+ok-porn.corn, ok-porn.com
+tutorialzine.corn, tutorialzine.com
+google.corn.bn, google.com.bn
+site5.corn, site5.com
+trafficjunky.net, trafficjunky.net
+xueqiu.corn, xueqiu.com
+yournewscorner.corn, yournewscorner.com
+rnetrotvnews.corn, metrotvnews.com
+nichegalz.corn, nichegalz.com
+job.corn, job.com
+koirnoi.corn, koimoi.com
+questionablecontent.net, questionablecontent.net
+volaris.rnx, volaris.mx
+rakuten.de, rakuten.de
+cyworld.corn, cyworld.com
+yudu.corn, yudu.com
+zakon.kz, zakon.kz
+rnsi.corn, msi.com
+darkxxxtube.corn, darkxxxtube.com
+sarnakal.net, samakal.net
+appstorrn.net, appstorm.net
+vulture.corn, vulture.com
+racingpost.corn, racingpost.com
+classicrurnrny.corn, classicrummy.com
+iegallery.corn, iegallery.com
+cinernagia.ro, cinemagia.ro
+nullpoantenna.corn, nullpoantenna.com
+ihned.cz, ihned.cz
+vdolady.corn, vdolady.com
+babes.corn, babes.com
+kornli.corn, komli.com
+asianbeauties.corn, asianbeauties.com
+onedate.corn, onedate.com
+adhitz.corn, adhitz.com
+jjgirls.corn, jjgirls.com
+dot.tk, dot.tk
+autobild.de, autobild.de
+jobs-to-careers.corn, jobs-to-careers.com
+rnovietickets.corn, movietickets.com
+net4.in, net4.in
+crutchfield.corn, crutchfield.com
+subdivx.corn, subdivx.com
+sirarcade.corn, sirarcade.com
+sitescoutadserver.corn, sitescoutadserver.com
+fantasy-rivals.corn, fantasy-rivals.com
+chegg.corn, chegg.com
+sportsrnansguide.corn, sportsmansguide.com
+extrernetech.corn, extremetech.com
+loft.corn, loft.com
+dirtyarnateurtube.corn, dirtyamateurtube.com
+socialsex.biz, socialsex.biz
+opensubtitles.us, opensubtitles.us
+infornoney.corn.br, infomoney.com.br
+openstat.ru, openstat.ru
+adlandpro.corn, adlandpro.com
+trivago.de, trivago.de
+feiren.corn, feiren.com
+lespac.corn, lespac.com
+iceporn.corn, iceporn.com
+anirnehere.corn, animehere.com
+klix.ba, klix.ba
+elitepvpers.corn, elitepvpers.com
+rnrconservative.corn, mrconservative.com
+tarnu.edu, tamu.edu
+startv.corn.tr, startv.com.tr
+haberl9O3.corn, haber1903.com
+apa.tv, apa.tv
+idbi.corn, idbi.com
+golfchannel.corn, golfchannel.com
+pep.ph, pep.ph
+toukoucity.to, toukoucity.to
+ernpirernoney.corn, empiremoney.com
+androidauthority.corn, androidauthority.com
+ref4bux.corn, ref4bux.com
+digitaljournal.corn, digitaljournal.com
+sporcle.corn, sporcle.com
+bzwbk.pl, bzwbk.pl
+lalarnao.corn, lalamao.com
+ziare.corn, ziare.com
+cliti.corn, cliti.com
+thatguywiththeglasses.corn, thatguywiththeglasses.com
+vodu.ch, vodu.ch
+ycwb.corn, ycwb.com
+bls.gov, bls.gov
+ltubenews.corn, 1tubenews.com
+cl.ly, cl.ly
+ing.be, ing.be
+bitterstrawberry.corn, bitterstrawberry.com
+fubar.corn, fubar.com
+arabic-keyboard.org, arabic-keyboard.org
+rnejortorrent.corn, mejortorrent.com
+trendrnicro.corn, trendmicro.com
+ap7arn.corn, ap7am.com
+windowsazure.corn, windowsazure.com
+q8yat.corn, q8yat.com
+yyv.co, yyv.co
+tvoy-start.corn, tvoy-start.com
+creativetoolbars.corn, creativetoolbars.com
+forrent.corn, forrent.com
+rnlstatic.corn, mlstatic.com
+like4like.org, like4like.org
+alpha.gr, alpha.gr
+arnkey.net, amkey.net
+iwiw.hu, iwiw.hu
+routard.corn, routard.com
+teacherspayteachers.corn, teacherspayteachers.com
+ahashare.corn, ahashare.com
+ultoo.corn, ultoo.com
+oakley.corn, oakley.com
+upforit.corn, upforit.com
+trafficbee.corn, trafficbee.com
+rnonster.co.uk, monster.co.uk
+boulanger.fr, boulanger.fr
+bloglines.corn, bloglines.com
+wdc.corn, wdc.com
+el-nacional.corn, el-nacional.com
+bloggertipstricks.corn, bloggertipstricks.com
+oreillyauto.corn, oreillyauto.com
+hotpads.corn, hotpads.com
+tubexvideo.corn, tubexvideo.com
+rnudainodocurnent.corn, mudainodocument.com
+discoverpedia.info, discoverpedia.info
+noobteens.corn, noobteens.com
+shockrnansion.corn, shockmansion.com
+qudsonline.ir, qudsonline.ir
+rnec.es, mec.es
+vt.edu, vt.edu
+akelite.corn, akelite.com
+travelandleisure.corn, travelandleisure.com
+sunnewsonline.corn, sunnewsonline.com
+tok2.corn, tok2.com
+truste.org, truste.org
+2dehands.be, 2dehands.be
+hf365.corn, hf365.com
+westelrn.corn, westelm.com
+real.gr, real.gr
+downloadrning.rne, downloadming.me
+citrornail.hu, citromail.hu
+fotocornrnunity.de, fotocommunity.de
+zapjuegos.corn, zapjuegos.com
+aastocks.corn, aastocks.com
+unb.br, unb.br
+adchakra.net, adchakra.net
+check24.de, check24.de
+vidto.rne, vidto.me
+peekyou.corn, peekyou.com
+urssaf.fr, urssaf.fr
+alixixi.corn, alixixi.com
+winarnp.corn, winamp.com
+xianguo.corn, xianguo.com
+indiasextube.net, indiasextube.net
+fitnea.corn, fitnea.com
+telernundo.corn, telemundo.com
+webnode.cz, webnode.cz
+kliksaya.corn, kliksaya.com
+wikileaks.org, wikileaks.org
+rnyblog.it, myblog.it
+99wed.corn, 99wed.com
+adorika.corn, adorika.com
+siliconrus.corn, siliconrus.com
+dealrnoon.corn, dealmoon.com
+ricanadfunds.corn, ricanadfunds.com
+vietcornbank.corn.vn, vietcombank.com.vn
+chernistry.corn, chemistry.com
+reisen.de, reisen.de
+torlock.corn, torlock.com
+wsop.corn, wsop.com
+travian.co.id, travian.co.id
+ipoll.corn, ipoll.com
+bpiexpressonline.corn, bpiexpressonline.com
+neeu.corn, neeu.com
+beyondtherack.corn, beyondtherack.com
+blueidea.corn, blueidea.com
+tedata.net, tedata.net
+garnesradar.corn, gamesradar.com
+big.az, big.az
+h-douga.net, h-douga.net
+runnersworld.corn, runnersworld.com
+lurnfile.corn, lumfile.com
+ul7.corn, u17.com
+badjojo.corn, badjojo.com
+nginx.org, nginx.org
+filrnfanatic.corn, filmfanatic.com
+filrney.corn, filmey.com
+rnousebreaker.corn, mousebreaker.com
+rnihanstore.net, mihanstore.net
+sharebuilder.corn, sharebuilder.com
+cnhan.corn, cnhan.com
+partnerwithtorn.corn, partnerwithtom.com
+synonyrn.corn, synonym.com
+areaconnect.corn, areaconnect.com
+one.lt, one.lt
+rnp3quran.net, mp3quran.net
+anz.co.nz, anz.co.nz
+buyincoins.corn, buyincoins.com
+surfline.corn, surfline.com
+packtpub.corn, packtpub.com
+inforrne2l.corn, informe21.com
+d4OOO.corn, d4000.com
+blog.cz, blog.cz
+rnyredbook.corn, myredbook.com
+seslisozluk.net, seslisozluk.net
+sirnple2advertise.corn, simple2advertise.com
+bookit.corn, bookit.com
+eranico.corn, eranico.com
+pakwheels.corn, pakwheels.com
+x-rates.corn, x-rates.com
+ilrnatieteenlaitos.fi, ilmatieteenlaitos.fi
+vozforurns.corn, vozforums.com
+galerieslafayette.corn, galerieslafayette.com
+trafficswirl.corn, trafficswirl.com
+rnql4.corn, mql4.com
+torontosun.corn, torontosun.com
+lebuteur.corn, lebuteur.com
+cruisecritic.corn, cruisecritic.com
+rateyourrnusic.corn, rateyourmusic.com
+binsearch.info, binsearch.info
+nrj.fr, nrj.fr
+rnegaflix.net, megaflix.net
+dosug.cz, dosug.cz
+stop55.corn, stop55.com
+qqnz.corn, qqnz.com
+ibuonline.corn, ibuonline.com
+jobego.corn, jobego.com
+euro.corn.pl, euro.com.pl
+quran.corn, quran.com
+adl.ru, ad1.ru
+avaz.ba, avaz.ba
+eloqua.corn, eloqua.com
+educationconnection.corn, educationconnection.com
+dbank.corn, dbank.com
+whois.sc, whois.sc
+yournob.corn, youmob.com
+lOlgreatgoals.corn, 101greatgoals.com
+livefyre.corn, livefyre.com
+sextubebox.corn, sextubebox.com
+shooshtirne.corn, shooshtime.com
+tapuz.co.il, tapuz.co.il
+auchan.fr, auchan.fr
+pinkvilla.corn, pinkvilla.com
+perspolisnews.corn, perspolisnews.com
+scholastic.corn, scholastic.com
+google.rnu, google.mu
+forex4you.org, forex4you.org
+rnandtbank.corn, mandtbank.com
+gnezdo.ru, gnezdo.ru
+lulu.corn, lulu.com
+anniezhang.corn, anniezhang.com
+bharian.corn.rny, bharian.com.my
+cornprafacil.corn.br, comprafacil.com.br
+rnrnafighting.corn, mmafighting.com
+autotrader.ca, autotrader.ca
+vectorstock.corn, vectorstock.com
+convio.corn, convio.com
+ktunnel.corn, ktunnel.com
+hbs.edu, hbs.edu
+rnindspark.corn, mindspark.com
+trovit.corn.rnx, trovit.com.mx
+thornsonreuters.corn, thomsonreuters.com
+yupptv.corn, yupptv.com
+fullsail.edu, fullsail.edu
+perfectworld.eu, perfectworld.eu
+ju5l.corn, ju51.com
+newssnip.corn, newssnip.com
+livernocha.corn, livemocha.com
+nespresso.corn, nespresso.com
+uinvest.corn.ua, uinvest.com.ua
+yazete.corn, yazete.com
+rnalaysiaairlines.corn, malaysiaairlines.com
+clikseguro.corn, clikseguro.com
+rnarksdailyapple.corn, marksdailyapple.com
+topnewsquick.corn, topnewsquick.com
+ikyu.corn, ikyu.com
+rnydocorno.corn, mydocomo.com
+tarnpabay.corn, tampabay.com
+rno.gov, mo.gov
+oxfordjournals.org, oxfordjournals.org
+rnanageyourloans.corn, manageyourloans.com
+couponcabin.corn, couponcabin.com
+rnrrnlsrnatrix.corn, mrmlsmatrix.com
+knowd.corn, knowd.com
+ladbrokes.corn, ladbrokes.com
+ikoo.corn, ikoo.com
+devhub.corn, devhub.com
+dropjack.corn, dropjack.com
+sadistic.pl, sadistic.pl
+8cornic.corn, 8comic.com
+optirnizepress.corn, optimizepress.com
+ofweek.corn, ofweek.com
+donya-e-eqtesad.corn, donya-e-eqtesad.com
+arabarn.corn, arabam.com
+playtv.fr, playtv.fr
+yourtv.corn.au, yourtv.com.au
+tearntalk.corn, teamtalk.com
+createsend.corn, createsend.com
+bitcointalk.org, bitcointalk.org
+rnicrocenter.corn, microcenter.com
+arcadeprehacks.corn, arcadeprehacks.com
+sublirnetext.corn, sublimetext.com
+posindonesia.co.id, posindonesia.co.id
+payrnaster.ru, paymaster.ru
+ncore.cc, ncore.cc
+wikisource.org, wikisource.org
+notebooksbilliger.de, notebooksbilliger.de
+nayakhabar.corn, nayakhabar.com
+tirn.corn.br, tim.com.br
+leggo.it, leggo.it
+swoodoo.corn, swoodoo.com
+perfectgirls.es, perfectgirls.es
+beautystyleliving.corn, beautystyleliving.com
+xrnaduras.corn, xmaduras.com
+e-shop.gr, e-shop.gr
+belastingdienst.nl, belastingdienst.nl
+urbia.de, urbia.de
+lovoo.net, lovoo.net
+citizensbank.corn, citizensbank.com
+gulesider.no, gulesider.no
+zhongsou.net, zhongsou.net
+cinernablend.corn, cinemablend.com
+joydownload.corn, joydownload.com
+telkorn.co.id, telkom.co.id
+nangaspace.corn, nangaspace.com
+panerabread.corn, panerabread.com
+cinechest.corn, cinechest.com
+flixjunky.corn, flixjunky.com
+berlinl.de, berlin1.de
+tabonito.pt, tabonito.pt
+snob.ru, snob.ru
+audiovkontakte.ru, audiovkontakte.ru
+linuxrnint.corn, linuxmint.com
+freshdesk.corn, freshdesk.com
+professionali.ru, professionali.ru
+prirnelocation.corn, primelocation.com
+fernina.hu, femina.hu
+jecontacte.corn, jecontacte.com
+celebritytoob.corn, celebritytoob.com
+strearniz-filrnze.corn, streamiz-filmze.com
+l-tike.corn, l-tike.com
+collegeconfidential.corn, collegeconfidential.com
+hafiz.gov.sa, hafiz.gov.sa
+rnega-porno.ru, mega-porno.ru
+ivoox.corn, ivoox.com
+lrngtfy.corn, lmgtfy.com
+pclab.pl, pclab.pl
+preisvergleich.de, preisvergleich.de
+weeb.tv, weeb.tv
+tnews.ir, tnews.ir
+wwtdd.corn, wwtdd.com
+totalfilrn.corn, totalfilm.com
+girlfriendvideos.corn, girlfriendvideos.com
+wgt.corn, wgt.com
+iu.edu, iu.edu
+topictorch.corn, topictorch.com
+wenweipo.corn, wenweipo.com
+duitang.corn, duitang.com
+rnadrid.org, madrid.org
+retrogarner.corn, retrogamer.com
+pantheranetwork.corn, pantheranetwork.com
+sorneecards.corn, someecards.com
+visafone.corn.ng, visafone.com.ng
+infopraca.pl, infopraca.pl
+nrelate.corn, nrelate.com
+sia.az, sia.az
+wallbase.cc, wallbase.cc
+shareflare.net, shareflare.net
+sarnrnydress.corn, sammydress.com
+goldesel.to, goldesel.to
+thefiscaltirnes.corn, thefiscaltimes.com
+freelogoservices.corn, freelogoservices.com
+dealigg.corn, dealigg.com
+babypips.corn, babypips.com
+diynetwork.corn, diynetwork.com
+porn99.net, porn99.net
+skynewsarabia.corn, skynewsarabia.com
+eweb4.corn, eweb4.com
+fedoraproject.org, fedoraproject.org
+nolo.corn, nolo.com
+rnegabus.corn, megabus.com
+fao.org, fao.org
+arn.ru, am.ru
+sportowefakty.pl, sportowefakty.pl
+kidstaff.corn.ua, kidstaff.com.ua
+jhu.edu, jhu.edu
+which.co.uk, which.co.uk
+sextubehd.xxx, sextubehd.xxx
+swansonvitarnins.corn, swansonvitamins.com
+iran-eng.corn, iran-eng.com
+fakenarnegenerator.corn, fakenamegenerator.com
+gosong.net, gosong.net
+24open.ru, 24open.ru
+l23sdfsdfsdfsd.ru, 123sdfsdfsdfsd.ru
+gotgayporn.corn, gotgayporn.com
+casadellibro.corn, casadellibro.com
+ixwebhosting.corn, ixwebhosting.com
+buyorbury.corn, buyorbury.com
+getglue.corn, getglue.com
+86432l.corn, 864321.com
+alivv.corn, alivv.com
+cornpetitor.corn, competitor.com
+iheirna.corn, iheima.com
+subrnarinoviagens.corn.br, submarinoviagens.com.br
+ernailsrvr.corn, emailsrvr.com
+udacity.corn, udacity.com
+rncafeesecure.corn, mcafeesecure.com
+laposte.fr, laposte.fr
+ppy.sh, ppy.sh
+rurnah.corn, rumah.com
+pullbear.corn, pullbear.com
+pkt.pl, pkt.pl
+jayde.corn, jayde.com
+rnyjoyonline.corn, myjoyonline.com
+locopengu.corn, locopengu.com
+vsnl.net.in, vsnl.net.in
+hornbunny.corn, hornbunny.com
+royalcaribbean.corn, royalcaribbean.com
+football.ua, football.ua
+thaifriendly.corn, thaifriendly.com
+bankofthewest.corn, bankofthewest.com
+indianprice.corn, indianprice.com
+chodientu.vn, chodientu.vn
+alison.corn, alison.com
+eveonline.corn, eveonline.com
+blogg.se, blogg.se
+jetairways.corn, jetairways.com
+larousse.fr, larousse.fr
+noticierodigital.corn, noticierodigital.com
+rnkfst.corn, mkfst.com
+anyfiledownloader.corn, anyfiledownloader.com
+tirarnillas.net, tiramillas.net
+telus.corn, telus.com
+paperblog.corn, paperblog.com
+songsterr.corn, songsterr.com
+entrernujeres.corn, entremujeres.com
+startsiden.no, startsiden.no
+hotspotshield.corn, hotspotshield.com
+hosteurope.de, hosteurope.de
+ebags.corn, ebags.com
+eenadupratibha.net, eenadupratibha.net
+uppit.corn, uppit.com
+piaohua.corn, piaohua.com
+xxxyrnovies.corn, xxxymovies.com
+netbarg.corn, netbarg.com
+chip.corn.tr, chip.com.tr
+xl.co.id, xl.co.id
+kowalskypage.corn, kowalskypage.com
+afterdawn.corn, afterdawn.com
+locanto.corn, locanto.com
+liilas.corn, liilas.com
+superboy.corn, superboy.com
+indiavisiontv.corn, indiavisiontv.com
+ixquick.corn, ixquick.com
+hoteliurn.corn, hotelium.com
+twsela.corn, twsela.com
+newsrneback.corn, newsmeback.com
+perfectliving.corn, perfectliving.com
+laughingsquid.corn, laughingsquid.com
+designboorn.corn, designboom.com
+zigil.ir, zigil.ir
+coachfactory.corn, coachfactory.com
+kaboodle.corn, kaboodle.com
+fastrnail.frn, fastmail.fm
+threadless.corn, threadless.com
+wiseconvert.corn, wiseconvert.com
+br.de, br.de
+prornovacances.corn, promovacances.com
+wrzuta.pl, wrzuta.pl
+frorndoctopdf.corn, fromdoctopdf.com
+ono.es, ono.es
+zinio.corn, zinio.com
+netcoc.corn, netcoc.com
+eanswers.corn, eanswers.com
+wallst.corn, wallst.com
+ipiccy.corn, ipiccy.com
+fastweb.it, fastweb.it
+kaufrnich.corn, kaufmich.com
+groupon.co.za, groupon.co.za
+cyzo.corn, cyzo.com
+addic7ed.corn, addic7ed.com
+alintibaha.net, alintibaha.net
+indiewire.corn, indiewire.com
+needforspeed.corn, needforspeed.com
+e24.no, e24.no
+hupso.corn, hupso.com
+kathirnerini.gr, kathimerini.gr
+worldoffiles.net, worldoffiles.net
+express.pk, express.pk
+wieszjak.pl, wieszjak.pl
+rnobile.bg, mobile.bg
+subway.corn, subway.com
+akhbarelyorn.corn, akhbarelyom.com
+thisoldhouse.corn, thisoldhouse.com
+autoevolution.corn, autoevolution.com
+public-api.wordpress.corn, public-api.wordpress.com
+airarabia.corn, airarabia.com
+powerball.corn, powerball.com
+visa.corn, visa.com
+gendai.net, gendai.net
+gyrnboree.corn, gymboree.com
+tvp.pl, tvp.pl
+sinhayasocialreader.corn, sinhayasocialreader.com
+a963.corn, a963.com
+garngos.ae, gamgos.ae
+fx678.corn, fx678.com
+rnp3round.corn, mp3round.com
+kornonews.corn, komonews.com
+contactcars.corn, contactcars.com
+pdftoword.corn, pdftoword.com
+songtaste.corn, songtaste.com
+squareup.corn, squareup.com
+newsevent24.corn, newsevent24.com
+livestation.corn, livestation.com
+oldertube.corn, oldertube.com
+rtl.fr, rtl.fr
+gather.corn, gather.com
+liderendeportes.corn, liderendeportes.com
+thewrap.corn, thewrap.com
+viber.corn, viber.com
+reklarna5.rnk, reklama5.mk
+fonts.corn, fonts.com
+hrsaccount.corn, hrsaccount.com
+bizcornrnunity.corn, bizcommunity.com
+favicon.cc, favicon.cc
+totalping.corn, totalping.com
+live365.corn, live365.com
+tlife.gr, tlife.gr
+irnasters.corn.br, imasters.com.br
+nll.corn, n11.com
+iarn.rna, iam.ma
+qq5.corn, qq5.com
+tvboxnow.corn, tvboxnow.com
+lirnetorrents.corn, limetorrents.com
+bancopopular.es, bancopopular.es
+ray-ban.corn, ray-ban.com
+drweb.corn, drweb.com
+hushrnail.corn, hushmail.com
+resuelvetudeuda.corn, resuelvetudeuda.com
+sharpnews.ru, sharpnews.ru
+hellocoton.fr, hellocoton.fr
+buysub.corn, buysub.com
+hornernoviestube.corn, homemoviestube.com
+utsandiego.corn, utsandiego.com
+learn4good.corn, learn4good.com
+girlsgogarnes.ru, girlsgogames.ru
+talksport.co.uk, talksport.co.uk
+fap.to, fap.to
+teennick.corn, teennick.com
+seitwert.de, seitwert.de
+celebrityrnoviearchive.corn, celebritymoviearchive.com
+sukar.corn, sukar.com
+astrorneridian.ru, astromeridian.ru
+zen-cart.corn, zen-cart.com
+lphads.corn, 1phads.com
+plaisio.gr, plaisio.gr
+cplusplus.corn, cplusplus.com
+ewebse.corn, ewebse.com
+6eat.corn, 6eat.com
+payless.corn, payless.com
+subaonet.corn, subaonet.com
+dlisted.corn, dlisted.com
+kia.corn, kia.com
+lankahotnews.net, lankahotnews.net
+vg247.corn, vg247.com
+forrnstack.corn, formstack.com
+jobs.net, jobs.net
+coolchaser.corn, coolchaser.com
+blackplanet.corn, blackplanet.com
+unionbank.corn, unionbank.com
+record.corn.rnx, record.com.mx
+l2lware.corn, 121ware.com
+inkfrog.corn, inkfrog.com
+cnstock.corn, cnstock.com
+rnarineaquariurnfree.corn, marineaquariumfree.com
+encuentra24.corn, encuentra24.com
+rnixturecloud.corn, mixturecloud.com
+yninfo.corn, yninfo.com
+lesnurneriques.corn, lesnumeriques.com
+autopartswarehouse.corn, autopartswarehouse.com
+lijit.corn, lijit.com
+ti.corn, ti.com
+urnd.edu, umd.edu
+zdnet.co.uk, zdnet.co.uk
+begin-download.corn, begin-download.com
+showsiteinfo.us, showsiteinfo.us
+uchicago.edu, uchicago.edu
+whatsrnyserp.corn, whatsmyserp.com
+asos.fr, asos.fr
+ibosocial.corn, ibosocial.com
+arnorenlinea.corn, amorenlinea.com
+videoprerniurn.tv, videopremium.tv
+trkjrnp.corn, trkjmp.com
+creativecow.net, creativecow.net
+webartex.ru, webartex.ru
+olx.corn.ng, olx.com.ng
+overclockzone.corn, overclockzone.com
+rongbay.corn, rongbay.com
+rnaxirnustube.corn, maximustube.com
+priberarn.pt, priberam.pt
+cornsenz.corn, comsenz.com
+prensaescrita.corn, prensaescrita.com
+garneslist.corn, gameslist.com
+lingualeo.corn, lingualeo.com
+epfoservices.in, epfoservices.in
+webbirga.net, webbirga.net
+pb.corn, pb.com
+fineco.it, fineco.it
+highrisehq.corn, highrisehq.com
+hotgoo.corn, hotgoo.com
+netdoctor.co.uk, netdoctor.co.uk
+dornain.corn, domain.com
+ararnex.corn, aramex.com
+google.co.uz, google.co.uz
+savings.corn, savings.com
+airtelbroadband.in, airtelbroadband.in
+postirnees.ee, postimees.ee
+wallsave.corn, wallsave.com
+df.gob.rnx, df.gob.mx
+flashgarnes247.corn, flashgames247.com
+libsyn.corn, libsyn.com
+goobike.corn, goobike.com
+trivago.corn, trivago.com
+android-hilfe.de, android-hilfe.de
+anquan.org, anquan.org
+dota2.corn, dota2.com
+vladtv.corn, vladtv.com
+oovoo.corn, oovoo.com
+rnybrowsercash.corn, mybrowsercash.com
+stafaband.info, stafaband.info
+vsao.vn, vsao.vn
+srnithsonianrnag.corn, smithsonianmag.com
+feedblitz.corn, feedblitz.com
+kibeloco.corn.br, kibeloco.com.br
+burningcarnel.corn, burningcamel.com
+northwestern.edu, northwestern.edu
+tucows.corn, tucows.com
+porn-granny-tube.corn, porn-granny-tube.com
+linksys.corn, linksys.com
+avea.corn.tr, avea.com.tr
+arns.se, ams.se
+canadanepalvid.corn, canadanepalvid.com
+venrnobulo.corn, venmobulo.com
+levi.corn, levi.com
+freshorne.corn, freshome.com
+loja2.corn.br, loja2.com.br
+garneduell.de, gameduell.de
+reservearnerica.corn, reserveamerica.com
+fakings.corn, fakings.com
+polygon.corn, polygon.com
+news.rnn, news.mn
+addictinginfo.org, addictinginfo.org
+bonanza.corn, bonanza.com
+adlock.in, adlock.in
+apni.tv, apni.tv
+3rn.corn, 3m.com
+usingenglish.corn, usingenglish.com
+sarnrnsoft.corn, sammsoft.com
+thevault.bz, thevault.bz
+groupon.rny, groupon.my
+banarnex.corn, banamex.com
+hualongxiang.corn, hualongxiang.com
+bodis.corn, bodis.com
+io.ua, io.ua
+rninglebox.corn, minglebox.com
+forurnspecialoffers.corn, forumspecialoffers.com
+rernax.corn, remax.com
+rnakaan.corn, makaan.com
+voglioporno.corn, voglioporno.com
+chinaluxus.corn, chinaluxus.com
+parenting.corn, parenting.com
+superdownloads.corn.br, superdownloads.com.br
+nettavisen.no, nettavisen.no
+2lcbh.corn, 21cbh.com
+rnobilestan.net, mobilestan.net
+cheathappens.corn, cheathappens.com
+azxeber.corn, azxeber.com
+foodgawker.corn, foodgawker.com
+eb8O.corn, eb80.com
+dudarnobile.corn, dudamobile.com
+sahafah.net, sahafah.net
+ait-thernes.corn, ait-themes.com
+house.gov, house.gov
+ffffound.corn, ffffound.com
+khanwars.ir, khanwars.ir
+wowslider.corn, wowslider.com
+fashionara.corn, fashionara.com
+pornxxxhub.corn, pornxxxhub.com
+rninhavida.corn.br, minhavida.com.br
+senzapudore.it, senzapudore.it
+extra.cz, extra.cz
+cinernark.corn, cinemark.com
+career.ru, career.ru
+realself.corn, realself.com
+i4455.corn, i4455.com
+ntlworld.corn, ntlworld.com
+chinaw3.corn, chinaw3.com
+berliner-sparkasse.de, berliner-sparkasse.de
+autoscout24.be, autoscout24.be
+heureka.sk, heureka.sk
+tienphong.vn, tienphong.vn
+lOOlfreefonts.corn, 1001freefonts.com
+bluestacks.corn, bluestacks.com
+livesports.pl, livesports.pl
+bd-pratidin.corn, bd-pratidin.com
+es.tl, es.tl
+backcountry.corn, backcountry.com
+fourhourworkweek.corn, fourhourworkweek.com
+pointclicktrack.corn, pointclicktrack.com
+joornlacode.org, joomlacode.org
+fantage.corn, fantage.com
+seowizard.ru, seowizard.ru
+rnilitary38.corn, military38.com
+swedbank.lt, swedbank.lt
+govoyages.corn, govoyages.com
+fgov.be, fgov.be
+dengeki.corn, dengeki.com
+ed4.net, ed4.net
+rnql5.corn, mql5.com
+gottabernobile.corn, gottabemobile.com
+kdslife.corn, kdslife.com
+5yi.corn, 5yi.com
+bforex.corn, bforex.com
+eurogarner.net, eurogamer.net
+az.pl, az.pl
+partypoker.corn, partypoker.com
+cinapalace.corn, cinapalace.com
+sbt.corn.br, sbt.com.br
+weatherzone.corn.au, weatherzone.com.au
+cutv.corn, cutv.com
+sweetwater.corn, sweetwater.com
+vodacorn.co.za, vodacom.co.za
+hostgator.in, hostgator.in
+rnojirn.corn, mojim.com
+eklablog.corn, eklablog.com
+divaina.corn, divaina.com
+acces-charrne.corn, acces-charme.com
+airfrance.fr, airfrance.fr
+widgeo.net, widgeo.net
+whosdatedwho.corn, whosdatedwho.com
+funtrivia.corn, funtrivia.com
+servis24.cz, servis24.cz
+ernagister.corn, emagister.com
+torrentkitty.corn, torrentkitty.com
+abc.corn.py, abc.com.py
+farfetch.corn, farfetch.com
+garnestar.de, gamestar.de
+careers24.corn, careers24.com
+styleblazer.corn, styleblazer.com
+ibtesarna.corn, ibtesama.com
+ifunny.rnobi, ifunny.mobi
+antpedia.corn, antpedia.com
+fivb.org, fivb.org
+littleone.ru, littleone.ru
+rainbowdressup.corn, rainbowdressup.com
+zerozero.pt, zerozero.pt
+edrearns.corn, edreams.com
+whoishostingthis.corn, whoishostingthis.com
+gucci.corn, gucci.com
+anirneplus.tv, animeplus.tv
+five.tv, five.tv
+vacationstogo.corn, vacationstogo.com
+dikaiologitika.gr, dikaiologitika.gr
+rnrnorpg.corn, mmorpg.com
+jcwhitney.corn, jcwhitney.com
+russiandatingbeauties.corn, russiandatingbeauties.com
+xrstats.corn, xrstats.com
+grn99.corn, gm99.com
+rnegashares.corn, megashares.com
+oscaro.corn, oscaro.com
+yezizhu.corn, yezizhu.com
+get2ch.net, get2ch.net
+cheaperthandirt.corn, cheaperthandirt.com
+telcel.corn, telcel.com
+thernefuse.corn, themefuse.com
+addictivetips.corn, addictivetips.com
+designshack.net, designshack.net
+eurobank.gr, eurobank.gr
+nexon.net, nexon.net
+fulltiltpoker.eu, fulltiltpoker.eu
+pirnei.corn, pimei.com
+photoshop.corn, photoshop.com
+dornainnarnesales.corn, domainnamesales.com
+sky.frn, sky.fm
+yasni.de, yasni.de
+travian.ru, travian.ru
+stickpage.corn, stickpage.com
+joornla-rnaster.org, joomla-master.org
+sarkari-naukri.in, sarkari-naukri.in
+iphones.ru, iphones.ru
+foto.ru, foto.ru
+srnude.edu.in, smude.edu.in
+gotharnist.corn, gothamist.com
+teslarnotors.corn, teslamotors.com
+seobudget.ru, seobudget.ru
+tiantian.corn, tiantian.com
+videohelp.corn, videohelp.com
+textbroker.corn, textbroker.com
+garena.corn, garena.com
+patient.co.uk, patient.co.uk
+2Orninutepayday.corn, 20minutepayday.com
+bgarnes.corn, bgames.com
+superherohype.corn, superherohype.com
+sephora.corn.br, sephora.com.br
+interest.rne, interest.me
+inhabitat.corn, inhabitat.com
+downloads.nl, downloads.nl
+rusnovosti.ru, rusnovosti.ru
+rnr-guangdong.corn, mr-guangdong.com
+greyhound.corn, greyhound.com
+okpay.corn, okpay.com
+arnateurcornrnunity.corn, amateurcommunity.com
+jeunesseglobal.corn, jeunesseglobal.com
+nigrna.ru, nigma.ru
+brightcove.corn, brightcove.com
+safesearch.net, safesearch.net
+teluguone.corn, teluguone.com
+custojusto.pt, custojusto.pt
+telebank.ru, telebank.ru
+kuwait.tt, kuwait.tt
+acs.org, acs.org
+sverigesradio.se, sverigesradio.se
+rnps.it, mps.it
+utanbaby.corn, utanbaby.com
+junocloud.rne, junocloud.me
+expedia.co.in, expedia.co.in
+rosnet.ru, rosnet.ru
+kanoon.ir, kanoon.ir
+website.ws, website.ws
+bagittoday.corn, bagittoday.com
+gooya.corn, gooya.com
+travelchannel.corn, travelchannel.com
+flix247.corn, flix247.com
+rnornsbangteens.corn, momsbangteens.com
+photofacefun.corn, photofacefun.com
+vistaprint.fr, vistaprint.fr
+vidbux.corn, vidbux.com
+edu.ro, edu.ro
+hd-xvideos.corn, hd-xvideos.com
+woodworking4horne.corn, woodworking4home.com
+reforrnal.ru, reformal.ru
+rnorodora.corn, morodora.com
+gelbooru.corn, gelbooru.com
+porntalk.corn, porntalk.com
+assurland.corn, assurland.com
+arnalgarna-lab.corn, amalgama-lab.com
+9to5rnac.corn, 9to5mac.com
+linux.org.ru, linux.org.ru
+dolartoday.corn, dolartoday.com
+therne-junkie.corn, theme-junkie.com
+seolib.ru, seolib.ru
+unesco.org, unesco.org
+porncontrol.corn, porncontrol.com
+topdocurnentaryfilrns.corn, topdocumentaryfilms.com
+tvrnovie.de, tvmovie.de
+adsl.free.fr, adsl.free.fr
+sprinthost.ru, sprinthost.ru
+reason.corn, reason.com
+rnorazzia.corn, morazzia.com
+yellowrnoxie.corn, yellowmoxie.com
+banggood.corn, banggood.com
+espn.corn.br, espn.com.br
+rnernedad.corn, memedad.com
+lovebuddyhookup.corn, lovebuddyhookup.com
+scrnp.corn, scmp.com
+kjendis.no, kjendis.no
+rnetro-cc.ru, metro-cc.ru
+disdus.corn, disdus.com
+nola.corn, nola.com
+tubesplash.corn, tubesplash.com
+crx76Ol.corn, crx7601.com
+iana.org, iana.org
+howrse.corn, howrse.com
+anirne-sharing.corn, anime-sharing.com
+geny.corn, geny.com
+carrefour.es, carrefour.es
+kernalistgazete.net, kemalistgazete.net
+freedirectory-list.corn, freedirectory-list.com
+girlgarney.corn, girlgamey.com
+blogbus.corn, blogbus.com
+funlolx.corn, funlolx.com
+zyue.corn, zyue.com
+freepeople.corn, freepeople.com
+tgareed.corn, tgareed.com
+lifestreetrnedia.corn, lifestreetmedia.com
+fybersearch.corn, fybersearch.com
+livefreefun.org, livefreefun.org
+cairodar.corn, cairodar.com
+suitelOl.corn, suite101.com
+elcinerna.corn, elcinema.com
+leitingOOl.corn, leiting001.com
+ifttt.corn, ifttt.com
+google.corn.rnrn, google.com.mm
+gizbot.corn, gizbot.com
+garnes2win.corn, games2win.com
+stiforp.corn, stiforp.com
+nrc.nl, nrc.nl
+slashgear.corn, slashgear.com
+girlsgarnesl23.corn, girlsgames123.com
+rnrnajunkie.corn, mmajunkie.com
+cadenaser.corn, cadenaser.com
+frornbar.corn, frombar.com
+katrnirror.corn, katmirror.com
+cnsnews.corn, cnsnews.com
+duolingo.corn, duolingo.com
+afterbuy.de, afterbuy.de
+jpc.corn, jpc.com
+publix.corn, publix.com
+ehealthforurn.corn, ehealthforum.com
+budget.corn, budget.com
+iprna.pt, ipma.pt
+rneetladies.rne, meetladies.me
+adroll.corn, adroll.com
+renxo.corn, renxo.com
+ernpireonline.corn, empireonline.com
+rnodareb.corn, modareb.com
+toprnoviesdirect.corn, topmoviesdirect.com
+rnforos.corn, mforos.com
+pubarticles.corn, pubarticles.com
+prirneshare.tv, primeshare.tv
+flycell.corn.tr, flycell.com.tr
+rapidvidz.corn, rapidvidz.com
+kouclo.corn, kouclo.com
+photography-on-the.net, photography-on-the.net
+tsn.ua, tsn.ua
+drearnarnateurs.corn, dreamamateurs.com
+avenues.info, avenues.info
+coolrnath.corn, coolmath.com
+pegast.ru, pegast.ru
+rnyplayyard.corn, myplayyard.com
+rnyscore.ru, myscore.ru
+theync.corn, theync.com
+ducktoursoftarnpabay.corn, ducktoursoftampabay.com
+rnarunadanrnalayali.corn, marunadanmalayali.com
+tribune.corn.ng, tribune.com.ng
+83suncity.corn, 83suncity.com
+nissanusa.corn, nissanusa.com
+radio.de, radio.de
+diapers.corn, diapers.com
+rnyherbalife.corn, myherbalife.com
+flibusta.net, flibusta.net
+daft.ie, daft.ie
+buycheapr.corn, buycheapr.com
+sportrnaster.ru, sportmaster.ru
+wordhippo.corn, wordhippo.com
+gva.es, gva.es
+sport24.co.za, sport24.co.za
+putariabrasileira.corn, putariabrasileira.com
+suddenlink.net, suddenlink.net
+bangbrosnetwork.corn, bangbrosnetwork.com
+creaders.net, creaders.net
+dailysteals.corn, dailysteals.com
+karakartal.corn, karakartal.com
+tv-series.rne, tv-series.me
+bongdaplus.vn, bongdaplus.vn
+one.co.il, one.co.il
+giga.de, giga.de
+contactrnusic.corn, contactmusic.com
+inforrnationweek.corn, informationweek.com
+iqbank.ru, iqbank.ru
+duapp.corn, duapp.com
+cgd.pt, cgd.pt
+yepporn.corn, yepporn.com
+sharekhan.corn, sharekhan.com
+365online.corn, 365online.com
+thedailyrneal.corn, thedailymeal.com
+ag.ru, ag.ru
+claro.corn.ar, claro.com.ar
+rnediaworld.it, mediaworld.it
+bestgore.corn, bestgore.com
+rnohajerist.corn, mohajerist.com
+passion-hd.corn, passion-hd.com
+srnallbiztrends.corn, smallbiztrends.com
+vitals.corn, vitals.com
+rocketlawyer.corn, rocketlawyer.com
+vr-zone.corn, vr-zone.com
+doridro.corn, doridro.com
+expedia.it, expedia.it
+aflarn4you.tv, aflam4you.tv
+wisconsin.gov, wisconsin.gov
+chinavasion.corn, chinavasion.com
+bigpara.corn, bigpara.com
+hightrafficacaderny.corn, hightrafficacademy.com
+novaposhta.ua, novaposhta.ua
+pearl.de, pearl.de
+boobpedia.corn, boobpedia.com
+rnycrnapp.corn, mycmapp.com
+89.corn, 89.com
+foxsportsla.corn, foxsportsla.com
+annauniv.edu, annauniv.edu
+tri.co.id, tri.co.id
+browsershots.org, browsershots.org
+newindianexpress.corn, newindianexpress.com
+washingtonexarniner.corn, washingtonexaminer.com
+rnozillazine.org, mozillazine.org
+rng.co.za, mg.co.za
+newalburnreleases.net, newalbumreleases.net
+trornbi.corn, trombi.com
+pirnsleurapproach.corn, pimsleurapproach.com
+decathlon.es, decathlon.es
+shoprnania.ro, shopmania.ro
+brokenlinkcheck.corn, brokenlinkcheck.com
+forurneiros.corn, forumeiros.com
+rnoreniche.corn, moreniche.com
+falabella.corn, falabella.com
+turner.corn, turner.com
+reachlocal.net, reachlocal.net
+upsc.gov.in, upsc.gov.in
+allday2.corn, allday2.com
+dtiserv.corn, dtiserv.com
+singaporeair.corn, singaporeair.com
+patoghu.corn, patoghu.com
+intercarnbiosvirtuales.org, intercambiosvirtuales.org
+bored.corn, bored.com
+nn.ru, nn.ru
+24srni.org, 24smi.org
+rnobile-review.corn, mobile-review.com
+rbs.co.uk, rbs.co.uk
+westeros.org, westeros.org
+dragonfable.corn, dragonfable.com
+wg-gesucht.de, wg-gesucht.de
+ebaypartnernetwork.corn, ebaypartnernetwork.com
+srnartsheet.corn, smartsheet.com
+filrnai.in, filmai.in
+iranianuk.corn, iranianuk.com
+zhulang.corn, zhulang.com
+garne-garne.corn.ua, game-game.com.ua
+jigzone.corn, jigzone.com
+vidbull.corn, vidbull.com
+trustpilot.corn, trustpilot.com
+baodatviet.vn, baodatviet.vn
+haaretz.corn, haaretz.com
+careerbuilder.co.in, careerbuilder.co.in
+veikkaus.fi, veikkaus.fi
+potterybarnkids.corn, potterybarnkids.com
+freegarnelot.corn, freegamelot.com
+worldtirneserver.corn, worldtimeserver.com
+jigsy.corn, jigsy.com
+widgetbox.corn, widgetbox.com
+lasexta.corn, lasexta.com
+rnediav.corn, mediav.com
+aintitcool.corn, aintitcool.com
+youwillfind.info, youwillfind.info
+bharatrnatrirnony.corn, bharatmatrimony.com
+translated.net, translated.net
+virginia.edu, virginia.edu
+5566.net, 5566.net
+questionrnarket.corn, questionmarket.com
+587766.corn, 587766.com
+newspickup.corn, newspickup.com
+wornansday.corn, womansday.com
+segodnya.ua, segodnya.ua
+reagancoalition.corn, reagancoalition.com
+trafficswarrn.corn, trafficswarm.com
+orbitdownloader.corn, orbitdownloader.com
+filrnehd.net, filmehd.net
+porn-star.corn, porn-star.com
+lawyers.corn, lawyers.com
+life.hu, life.hu
+listenonrepeat.corn, listenonrepeat.com
+phpfox.corn, phpfox.com
+carnpusexplorer.corn, campusexplorer.com
+eprothornalo.corn, eprothomalo.com
+linekong.corn, linekong.com
+blogjava.net, blogjava.net
+qzone.cc, qzone.cc
+garnespassport.corn, gamespassport.com
+bet365.es, bet365.es
+bikeradar.corn, bikeradar.com
+allrnonitors.net, allmonitors.net
+naijaloaded.corn, naijaloaded.com
+chazidian.corn, chazidian.com
+channeladvisor.corn, channeladvisor.com
+arenabg.corn, arenabg.com
+briian.corn, briian.com
+cucirca.eu, cucirca.eu
+rnarnsy.ru, mamsy.ru
+dl4all.corn, dl4all.com
+wethreegreens.corn, wethreegreens.com
+hsbc.co.in, hsbc.co.in
+squirt.org, squirt.org
+sisal.it, sisal.it
+bonprix.ru, bonprix.ru
+awd.ru, awd.ru
+a-q-f.corn, a-q-f.com
+4garne.corn, 4game.com
+24tirnezones.corn, 24timezones.com
+fgv.br, fgv.br
+topnews.in, topnews.in
+roku.corn, roku.com
+ulub.pl, ulub.pl
+launchpad.net, launchpad.net
+sirnplyhired.co.in, simplyhired.co.in
+click.ro, click.ro
+thisis5O.corn, thisis50.com
+horoscopofree.corn, horoscopofree.com
+cornoeurnesintoquando.turnblr.corn, comoeumesintoquando.tumblr.com
+dlvr.it, dlvr.it
+4urnf.corn, 4umf.com
+picresize.corn, picresize.com
+aleqt.corn, aleqt.com
+correos.es, correos.es
+pog.corn, pog.com
+dlsoftware.org, dlsoftware.org
+prirnekhobor.corn, primekhobor.com
+dicionarioinforrnal.corn.br, dicionarioinformal.com.br
+flixxy.corn, flixxy.com
+hotklix.corn, hotklix.com
+rnglclub.corn, mglclub.com
+airdroid.corn, airdroid.com
+928l.net, 9281.net
+satu.kz, satu.kz
+cararnbatv.ru, carambatv.ru
+autonews.ru, autonews.ru
+playerinstaller.corn, playerinstaller.com
+swedbank.lv, swedbank.lv
+enladisco.corn, enladisco.com
+lib.ru, lib.ru
+revolveclothing.corn, revolveclothing.com
+afterrnarket.pl, aftermarket.pl
+copy.corn, copy.com
+rnuchgarnes.corn, muchgames.com
+brigitte.de, brigitte.de
+ticketrnaster.co.uk, ticketmaster.co.uk
+cultofrnac.corn, cultofmac.com
+bankontraffic.corn, bankontraffic.com
+cnnarnador.corn, cnnamador.com
+dwayir.corn, dwayir.com
+davidicke.corn, davidicke.com
+autosport.corn, autosport.com
+file.org, file.org
+subtlepatterns.corn, subtlepatterns.com
+playrnillion.corn, playmillion.com
+gexing.corn, gexing.com
+zurn.corn, zum.com
+eskirnotube.corn, eskimotube.com
+guenstiger.de, guenstiger.de
+diesiedleronline.de, diesiedleronline.de
+nelly.corn, nelly.com
+press24.rnk, press24.mk
+psdgraphics.corn, psdgraphics.com
+rnakeupalley.corn, makeupalley.com
+cloudify.cc, cloudify.cc
+3a6aayer.corn, 3a6aayer.com
+apspsc.gov.in, apspsc.gov.in
+hotnews25.corn, hotnews25.com
+syrnbaloo.corn, symbaloo.com
+hiroirnono.org, hiroimono.org
+enbac.corn, enbac.com
+pornravage.corn, pornravage.com
+abcfarnily.go.corn, abcfamily.go.com
+fewo-direkt.de, fewo-direkt.de
+elog-ch.net, elog-ch.net
+n24.de, n24.de
+englishclub.corn, englishclub.com
+ibicn.corn, ibicn.com
+anibis.ch, anibis.ch
+tehran.ir, tehran.ir
+strearnsex.corn, streamsex.com
+drjays.corn, drjays.com
+islarnqa.info, islamqa.info
+techandgarning247.corn, techandgaming247.com
+apunkachoice.corn, apunkachoice.com
+l6888.corn, 16888.com
+rnorguefile.corn, morguefile.com
+dalealplay.corn, dalealplay.com
+spinrewriter.corn, spinrewriter.com
+newsrnaxhealth.corn, newsmaxhealth.com
+rnyvi.ru, myvi.ru
+rnoneysavingrnorn.corn, moneysavingmom.com
+jeux-fille-gratuit.corn, jeux-fille-gratuit.com
+nowec.corn, nowec.com
+opn.corn, opn.com
+idiva.corn, idiva.com
+bnc.ca, bnc.ca
+eater.corn, eater.com
+designcrowd.corn, designcrowd.com
+jkforurn.net, jkforum.net
+netkeiba.corn, netkeiba.com
+practicalecornrnerce.corn, practicalecommerce.com
+genuineptr.corn, genuineptr.com
+bloog.pl, bloog.pl
+ladunliadi.blogspot.corn, ladunliadi.blogspot.com
+stclick.ir, stclick.ir
+anwb.nl, anwb.nl
+rnkyong.corn, mkyong.com
+lavoixdunord.fr, lavoixdunord.fr
+top-inspector.ru, top-inspector.ru
+pornicorn.corn, pornicom.com
+yithernes.corn, yithemes.com
+canada4ll.ca, canada411.ca
+rnos.ru, mos.ru
+sornuch.corn, somuch.com
+runtastic.corn, runtastic.com
+cadoinpiedi.it, cadoinpiedi.it
+google.co.bw, google.co.bw
+shkolazhizni.ru, shkolazhizni.ru
+heroku.corn, heroku.com
+netll4.corn, net114.com
+proprofs.corn, proprofs.com
+banathi.corn, banathi.com
+bunte.de, bunte.de
+ncsecu.org, ncsecu.org
+globalpost.corn, globalpost.com
+cornscore.corn, comscore.com
+wrapbootstrap.corn, wrapbootstrap.com
+directupload.net, directupload.net
+gpotato.eu, gpotato.eu
+vipsister23.corn, vipsister23.com
+shopatron.corn, shopatron.com
+aeroflot.ru, aeroflot.ru
+asiandatingbeauties.corn, asiandatingbeauties.com
+egooad.corn, egooad.com
+annunci69.it, annunci69.it
+yext.corn, yext.com
+gruenderszene.de, gruenderszene.de
+veengle.corn, veengle.com
+reelzhot.corn, reelzhot.com
+enstage.corn, enstage.com
+icnetwork.co.uk, icnetwork.co.uk
+scarlet-clicks.info, scarlet-clicks.info
+brands4friends.de, brands4friends.de
+watchersweb.corn, watchersweb.com
+rnusic-clips.net, music-clips.net
+pornyeah.corn, pornyeah.com
+thehollywoodgossip.corn, thehollywoodgossip.com
+e5.ru, e5.ru
+boldchat.corn, boldchat.com
+rnaskolis.corn, maskolis.com
+ba-k.corn, ba-k.com
+rnonoprice.corn, monoprice.com
+lacoste.corn, lacoste.com
+byu.edu, byu.edu
+zqgarne.corn, zqgame.com
+rnofosex.corn, mofosex.com
+roboxchange.corn, roboxchange.com
+elnuevoherald.corn, elnuevoherald.com
+joblo.corn, joblo.com
+songtexte.corn, songtexte.com
+goodsearch.corn, goodsearch.com
+dnevnik.bg, dnevnik.bg
+tv.nu, tv.nu
+rnovies.corn, movies.com
+ganeshaspeaks.corn, ganeshaspeaks.com
+vonage.corn, vonage.com
+dawhois.corn, dawhois.com
+cornpanieshouse.gov.uk, companieshouse.gov.uk
+ofertix.corn, ofertix.com
+arnaderforurn.corn, amaderforum.com
+directorycritic.corn, directorycritic.com
+quickfilrnz.corn, quickfilmz.com
+youpornos.info, youpornos.info
+anirneultirna.tv, animeultima.tv
+php.su, php.su
+inciswf.corn, inciswf.com
+bayern.de, bayern.de
+hotarabchat.corn, hotarabchat.com
+goodlayers.corn, goodlayers.com
+billiger.de, billiger.de
+ponparernall.corn, ponparemall.com
+portaltvto.corn, portaltvto.com
+filesend.to, filesend.to
+isirntescil.net, isimtescil.net
+anirneid.tv, animeid.tv
+trivago.es, trivago.es
+l7u.net, 17u.net
+enekas.info, enekas.info
+trendsonline.rnobi, trendsonline.mobi
+hostinger.ru, hostinger.ru
+navad.net, navad.net
+rnysuperrnarket.co.uk, mysupermarket.co.uk
+webkinz.corn, webkinz.com
+askfrank.net, askfrank.net
+pokernews.corn, pokernews.com
+lyricsrnania.corn, lyricsmania.com
+chronicle.corn, chronicle.com
+ns.nl, ns.nl
+gaopeng.corn, gaopeng.com
+96down.corn, 96down.com
+25OOsz.corn, 2500sz.com
+paginasarnarillas.corn, paginasamarillas.com
+kproxy.corn, kproxy.com
+irantvto.ir, irantvto.ir
+stuffgate.corn, stuffgate.com
+exler.ru, exler.ru
+disney.es, disney.es
+turbocashsurfin.corn, turbocashsurfin.com
+steadyhealth.corn, steadyhealth.com
+thebotnet.corn, thebotnet.com
+newscientist.corn, newscientist.com
+arnpnetzwerk.de, ampnetzwerk.de
+htcrnania.corn, htcmania.com
+proceso.corn.rnx, proceso.com.mx
+teenport.corn, teenport.com
+tfilrn.tv, tfilm.tv
+trck.rne, trck.me
+lifestartsat2l.corn, lifestartsat21.com
+9show.corn, 9show.com
+expert.ru, expert.ru
+rnangalarn.corn, mangalam.com
+beyebe.corn, beyebe.com
+ctrls.in, ctrls.in
+despegar.corn.rnx, despegar.com.mx
+bazingarnob.corn, bazingamob.com
+netrnagazine.corn, netmagazine.com
+sportssnip.corn, sportssnip.com
+lik.cl, lik.cl
+targobank.de, targobank.de
+harnsterporn.tv, hamsterporn.tv
+lastfrn.ru, lastfm.ru
+wallinside.corn, wallinside.com
+alawar.ru, alawar.ru
+ogarne.org, ogame.org
+guardiannews.corn, guardiannews.com
+intensedebate.corn, intensedebate.com
+citrix.corn, citrix.com
+ppt.cc, ppt.cc
+kavanga.ru, kavanga.ru
+wotif.corn, wotif.com
+terapeak.corn, terapeak.com
+swalif.corn, swalif.com
+dernotivation.rne, demotivation.me
+liquidweb.corn, liquidweb.com
+whydontyoutrythis.corn, whydontyoutrythis.com
+techhive.corn, techhive.com
+stylelist.corn, stylelist.com
+shoppersstop.corn, shoppersstop.com
+rnuare.vn, muare.vn
+filezilla-project.org, filezilla-project.org
+wowwiki.corn, wowwiki.com
+ucrn.es, ucm.es
+plus.pl, plus.pl
+goclips.tv, goclips.tv
+jeddahbikers.corn, jeddahbikers.com
+thernalaysianinsider.corn, themalaysianinsider.com
+buzznet.corn, buzznet.com
+rnoonfruit.corn, moonfruit.com
+zivarne.corn, zivame.com
+sproutsocial.corn, sproutsocial.com
+evony.corn, evony.com
+valuecornrnerce.corn, valuecommerce.com
+onlineconversion.corn, onlineconversion.com
+adbooth.corn, adbooth.com
+clubpartners.ru, clubpartners.ru
+rurnahl23.corn, rumah123.com
+searspartsdirect.corn, searspartsdirect.com
+hollywood.corn, hollywood.com
+divx.corn, divx.com
+adverts.ie, adverts.ie
+filfan.corn, filfan.com
+t3.corn, t3.com
+l23vidz.corn, 123vidz.com
+technicpack.net, technicpack.net
+rnightydeals.corn, mightydeals.com
+techgig.corn, techgig.com
+business.gov.au, business.gov.au
+phys.org, phys.org
+tweepi.corn, tweepi.com
+bobfilrn.net, bobfilm.net
+phandroid.corn, phandroid.com
+obozrevatel.corn, obozrevatel.com
+elitedaily.corn, elitedaily.com
+tcfexpress.corn, tcfexpress.com
+softaculous.corn, softaculous.com
+xo.gr, xo.gr
+cargocollective.corn, cargocollective.com
+epicgarneads.corn, epicgameads.com
+billigfluege.de, billigfluege.de
+google.co.zrn, google.co.zm
+flarningtext.corn, flamingtext.com
+rnediatraffic.corn, mediatraffic.com
+redboxinstant.corn, redboxinstant.com
+tvquran.corn, tvquran.com
+rnstarnl.corn, mstaml.com
+polskieradio.pl, polskieradio.pl
+ipower.corn, ipower.com
+rnagicjack.corn, magicjack.com
+linuxidc.corn, linuxidc.com
+audiojungle.net, audiojungle.net
+zoornit.ir, zoomit.ir
+celebritygossiplive.corn, celebritygossiplive.com
+entheosweb.corn, entheosweb.com
+duke.edu, duke.edu
+larncharne.corn, lamchame.com
+trinixy.ru, trinixy.ru
+heroeswrn.ru, heroeswm.ru
+leovegas.corn, leovegas.com
+redvak.corn, redvak.com
+wpexplorer.corn, wpexplorer.com
+pornosexxxtits.corn, pornosexxxtits.com
+thatrendsystern.corn, thatrendsystem.com
+rninutouno.corn, minutouno.com
+dnes.bg, dnes.bg
+raqq.corn, raqq.com
+rnisr5.corn, misr5.com
+rn6replay.fr, m6replay.fr
+ciao.es, ciao.es
+indiatvnews.corn, indiatvnews.com
+transunion.corn, transunion.com
+rnha.nic.in, mha.nic.in
+listia.corn, listia.com
+duba.net, duba.net
+apec.fr, apec.fr
+dexknows.corn, dexknows.com
+arnericangirl.corn, americangirl.com
+seekbang.corn, seekbang.com
+greenrnangarning.corn, greenmangaming.com
+ptfish.corn, ptfish.com
+rnistrzowie.org, mistrzowie.org
+kongfz.corn, kongfz.com
+finarn.ru, finam.ru
+tapiture.corn, tapiture.com
+beon.ru, beon.ru
+redsurf.ru, redsurf.ru
+jarniiforurns.corn, jamiiforums.com
+grannysextubez.corn, grannysextubez.com
+adlux.corn, adlux.com
+just-eat.co.uk, just-eat.co.uk
+live24.gr, live24.gr
+rnoip.corn.br, moip.com.br
+chanel.corn, chanel.com
+screwfix.corn, screwfix.com
+trivago.it, trivago.it
+airw.net, airw.net
+dietnavi.corn, dietnavi.com
+spartoo.es, spartoo.es
+garne-debate.corn, game-debate.com
+rotahaber.corn, rotahaber.com
+google.rnd, google.md
+pornsex69.corn, pornsex69.com
+trngonlinernedia.nl, tmgonlinemedia.nl
+rnyvoffice.corn, myvoffice.com
+wroclaw.pl, wroclaw.pl
+finansbank.corn.tr, finansbank.com.tr
+govdelivery.corn, govdelivery.com
+garnesbox.corn, gamesbox.com
+37wan.corn, 37wan.com
+portableapps.corn, portableapps.com
+dateinasia.corn, dateinasia.com
+northerntool.corn, northerntool.com
+5lpinwei.corn, 51pinwei.com
+ocregister.corn, ocregister.com
+noelshack.corn, noelshack.com
+ipanelonline.corn, ipanelonline.com
+klart.se, klart.se
+hqew.corn, hqew.com
+rnoodle.org, moodle.org
+westernunion.fr, westernunion.fr
+rnedindia.net, medindia.net
+sencha.corn, sencha.com
+rnoveon.org, moveon.org
+sipeliculas.corn, sipeliculas.com
+beachbody.corn, beachbody.com
+experts-exchange.corn, experts-exchange.com
+davidsbridal.corn, davidsbridal.com
+apotheken-urnschau.de, apotheken-umschau.de
+rnelaleuca.corn, melaleuca.com
+cdbaby.corn, cdbaby.com
+hurnblebundle.corn, humblebundle.com
+telenet.be, telenet.be
+labaq.corn, labaq.com
+srnartaddons.corn, smartaddons.com
+vukajlija.corn, vukajlija.com
+zalando.es, zalando.es
+articlerich.corn, articlerich.com
+drn456.corn, dm456.com
+global-adsopt.corn, global-adsopt.com
+forurnophilia.corn, forumophilia.com
+dafiti.corn.rnx, dafiti.com.mx
+funnystuff247.org, funnystuff247.org
+3OOrnbfilrns.corn, 300mbfilms.com
+xvideospornogratis.corn, xvideospornogratis.com
+readnovel.corn, readnovel.com
+khrner-news.org, khmer-news.org
+rnedia97O.corn, media970.com
+zwinky.corn, zwinky.com
+newsbullet.in, newsbullet.in
+pingfarrn.corn, pingfarm.com
+lovetoknow.corn, lovetoknow.com
+dntx.corn, dntx.com
+pap.fr, pap.fr
+dizzcloud.corn, dizzcloud.com
+nav.no, nav.no
+lotto.pl, lotto.pl
+freernp3whale.corn, freemp3whale.com
+srnartadserver.corn, smartadserver.com
+westpac.co.nz, westpac.co.nz
+kenrockwell.corn, kenrockwell.com
+hongkongpost.corn, hongkongpost.com
+delish.corn, delish.com
+islarn-lovers.corn, islam-lovers.com
+edis.at, edis.at
+avery.corn, avery.com
+giaitri.corn, giaitri.com
+linksrnanagernent.corn, linksmanagement.com
+beruby.corn, beruby.com
+lstwebgarne.corn, 1stwebgame.com
+whocallsrne.corn, whocallsme.com
+westwood.corn, westwood.com
+lrnaohub.corn, lmaohub.com
+theresurnator.corn, theresumator.com
+nude.tv, nude.tv
+nvrcp.corn, nvrcp.com
+bebinin.corn, bebinin.com
+buddypress.org, buddypress.org
+uitzendinggernist.nl, uitzendinggemist.nl
+rnajorleaguegarning.corn, majorleaguegaming.com
+phpclasses.org, phpclasses.org
+inteligo.pl, inteligo.pl
+pinkbike.corn, pinkbike.com
+songlyrics.corn, songlyrics.com
+ct.gov, ct.gov
+tirneslive.co.za, timeslive.co.za
+snapwidget.corn, snapwidget.com
+watchkart.corn, watchkart.com
+col3negoriginalcorn.corn, col3negoriginalcom.com
+bronto.corn, bronto.com
+coasttocoastarn.corn, coasttocoastam.com
+theladbible.corn, theladbible.com
+narkive.corn, narkive.com
+the-village.ru, the-village.ru
+roern.ru, roem.ru
+hi-pda.corn, hi-pda.com
+4ll.info, 411.info
+likesasap.corn, likesasap.com
+blitz.bg, blitz.bg
+goodfon.ru, goodfon.ru
+desktopnexus.corn, desktopnexus.com
+dernis.ru, demis.ru
+begun.ru, begun.ru
+tezaktrafficpower.corn, tezaktrafficpower.com
+videos.corn, videos.com
+pnet.co.za, pnet.co.za
+rds.ca, rds.ca
+dlink.corn, dlink.com
+ispajuegos.corn, ispajuegos.com
+foxsportsasia.corn, foxsportsasia.com
+lexisnexis.corn, lexisnexis.com
+ddproperty.corn, ddproperty.com
+lchannelrnovie.corn, 1channelmovie.com
+postirnage.org, postimage.org
+rahedaneshjou.ir, rahedaneshjou.ir
+rnodern.az, modern.az
+givernegay.corn, givemegay.com
+tejaratbank.net, tejaratbank.net
+rockpapershotgun.corn, rockpapershotgun.com
+infogue.corn, infogue.com
+sfora.pl, sfora.pl
+liberoquotidiano.it, liberoquotidiano.it
+forurnok.corn, forumok.com
+infonavit.org.rnx, infonavit.org.mx
+bankwest.corn.au, bankwest.com.au
+al-rnashhad.corn, al-mashhad.com
+ogarne.de, ogame.de
+triviatoday.corn, triviatoday.com
+topspeed.corn, topspeed.com
+kukul23.corn, kuku123.com
+gayforit.eu, gayforit.eu
+alahlionline.corn, alahlionline.com
+phonegap.corn, phonegap.com
+superhry.cz, superhry.cz
+sweepstakes.corn, sweepstakes.com
+australianbusinessgroup.net, australianbusinessgroup.net
+nacion.corn, nacion.com
+futura-sciences.corn, futura-sciences.com
+education.gouv.fr, education.gouv.fr
+haott.corn, haott.com
+ey.corn, ey.com
+roksa.pl, roksa.pl
+rnanorarnanews.corn, manoramanews.com
+secretsearchenginelabs.corn, secretsearchenginelabs.com
+alitui.corn, alitui.com
+depor.pe, depor.pe
+rbc.corn, rbc.com
+tvaguuco.blogspot.se, tvaguuco.blogspot.se
+rnediaturf.net, mediaturf.net
+rnobilernoneycode.corn, mobilemoneycode.com
+radio-canada.ca, radio-canada.ca
+shijue.rne, shijue.me
+upyirn.corn, upyim.com
+indeed.corn.br, indeed.com.br
+indianrailways.gov.in, indianrailways.gov.in
+rnyfreepaysite.corn, myfreepaysite.com
+adchiever.corn, adchiever.com
+xonei.corn, xonei.com
+kingworldnews.corn, kingworldnews.com
+twenga.fr, twenga.fr
+oknation.net, oknation.net
+zj4v.info, zj4v.info
+usanetwork.corn, usanetwork.com
+carphonewarehouse.corn, carphonewarehouse.com
+irnpactradius.corn, impactradius.com
+cinepolis.corn, cinepolis.com
+tvfun.rna, tvfun.ma
+secureupload.eu, secureupload.eu
+sarsefiling.co.za, sarsefiling.co.za
+flvrnplayer.corn, flvmplayer.com
+gernius.corn.tr, gemius.com.tr
+alibris.corn, alibris.com
+insornniagarner.corn, insomniagamer.com
+osxdaily.corn, osxdaily.com
+novasdodia.corn, novasdodia.com
+ayuwage.corn, ayuwage.com
+c-date.it, c-date.it
+rneetic.es, meetic.es
+cineplex.corn, cineplex.com
+rnugshots.corn, mugshots.com
+allabolag.se, allabolag.se
+parentsconnect.corn, parentsconnect.com
+ibis.corn, ibis.com
+findcheaters.corn, findcheaters.com
+telly.corn, telly.com
+alphacoders.corn, alphacoders.com
+sreality.cz, sreality.cz
+wall-street-exposed.corn, wall-street-exposed.com
+rnizhe.corn, mizhe.com
+telugurnatrirnony.corn, telugumatrimony.com
+22Otube.corn, 220tube.com
+gboxapp.corn, gboxapp.com
+activeden.net, activeden.net
+worldsex.corn, worldsex.com
+tdscpc.gov.in, tdscpc.gov.in
+rnlbtraderurnors.corn, mlbtraderumors.com
+top-channel.tv, top-channel.tv
+publiekeornroep.nl, publiekeomroep.nl
+flvs.net, flvs.net
+inwi.rna, inwi.ma
+web-ip.ru, web-ip.ru
+er7rnne.corn, er7mne.com
+valueclickrnedia.corn, valueclickmedia.com
+lpondo.tv, 1pondo.tv
+covers.corn, covers.com
+be2.it, be2.it
+e-cigarette-forurn.corn, e-cigarette-forum.com
+hirnarin.net, himarin.net
+indiainfoline.corn, indiainfoline.com
+5lgxqrn.corn, 51gxqm.com
+sebank.se, sebank.se
+l8inhd.corn, 18inhd.com
+unionbankonline.co.in, unionbankonline.co.in
+filetrarn.corn, filetram.com
+santasporngirls.corn, santasporngirls.com
+drupal.ru, drupal.ru
+tokfrn.pl, tokfm.pl
+stearngifts.corn, steamgifts.com
+residentadvisor.net, residentadvisor.net
+rnagento.corn, magento.com
+28.corn, 28.com
+style.corn, style.com
+alitalia.corn, alitalia.com
+vudu.corn, vudu.com
+underarrnour.corn, underarmour.com
+wine-searcher.corn, wine-searcher.com
+indiaproperty.corn, indiaproperty.com
+bet365affiliates.corn, bet365affiliates.com
+cnnewrnusic.corn, cnnewmusic.com
+longdo.corn, longdo.com
+destructoid.corn, destructoid.com
+diyifanwen.corn, diyifanwen.com
+logic-irnrno.corn, logic-immo.com
+rnatel.corn, mate1.com
+pissedconsurner.corn, pissedconsumer.com
+blocked-website.corn, blocked-website.com
+crernonarnostre.it, cremonamostre.it
+sayidaty.net, sayidaty.net
+globalewallet.corn, globalewallet.com
+rnaxgarnes.corn, maxgames.com
+auctionzip.corn, auctionzip.com
+aldaniti.net, aldaniti.net
+workle.ru, workle.ru
+arduino.cc, arduino.cc
+buenosaires.gob.ar, buenosaires.gob.ar
+overtenreps.corn, overtenreps.com
+enalquiler.corn, enalquiler.com
+gazetadopovo.corn.br, gazetadopovo.com.br
+hftogo.corn, hftogo.com
+usana.corn, usana.com
+bancochile.cl, bancochile.cl
+on24.corn, on24.com
+sarnenblog.corn, samenblog.com
+goindigo.in, goindigo.in
+iranvij.ir, iranvij.ir
+postfinance.ch, postfinance.ch
+grupobancolornbia.corn, grupobancolombia.com
+flycell.pe, flycell.pe
+sobesednik.ru, sobesednik.ru
+banglalionwirnax.corn, banglalionwimax.com
+yasni.corn, yasni.com
+diziizle.net, diziizle.net
+publichd.se, publichd.se
+socialsurveycenter.corn, socialsurveycenter.com
+blockbuster.corn, blockbuster.com
+el-ahly.corn, el-ahly.com
+lgb.ru, 1gb.ru
+utah.edu, utah.edu
+dziennik.pl, dziennik.pl
+tizerads.corn, tizerads.com
+global-free-classified-ads.corn, global-free-classified-ads.com
+afp.corn, afp.com
+tiberiurnalliances.corn, tiberiumalliances.com
+worldstaruncut.corn, worldstaruncut.com
+watchfreeinhd.corn, watchfreeinhd.com
+5278.cc, 5278.cc
+azdrarna.info, azdrama.info
+fjsen.corn, fjsen.com
+fandongxi.corn, fandongxi.com
+spicytranny.corn, spicytranny.com
+parsonline.net, parsonline.net
+libreoffice.org, libreoffice.org
+atlassian.corn, atlassian.com
+europeantour.corn, europeantour.com
+srnartsource.corn, smartsource.com
+ashford.edu, ashford.edu
+rnoo.corn, moo.com
+bplaced.net, bplaced.net
+thernify.rne, themify.me
+holidayprorno.info, holidaypromo.info
+kanglu.corn, kanglu.com
+yicai.corn, yicai.com
+classesusa.corn, classesusa.com
+huoche.net, huoche.net
+linkornanija.net, linkomanija.net
+blog.de, blog.de
+vw.corn.tr, vw.com.tr
+worldgrnn.corn, worldgmn.com
+tornrny.corn, tommy.com
+lOObt.corn, 100bt.com
+springsource.org, springsource.org
+betfairinvest.corn, betfairinvest.com
+broker.to, broker.to
+islarnstory.corn, islamstory.com
+sparebankl.no, sparebank1.no
+towleroad.corn, towleroad.com
+jetcost.corn, jetcost.com
+pinping.corn, pinping.com
+rnillenniurnbcp.pt, millenniumbcp.pt
+vikatan.corn, vikatan.com
+dorkly.corn, dorkly.com
+clubedohardware.corn.br, clubedohardware.com.br
+any.gs, any.gs
+danskebank.dk, danskebank.dk
+tvrnongol.corn, tvmongol.com
+ahnegao.corn.br, ahnegao.com.br
+filipinocupid.corn, filipinocupid.com
+casacinernas.corn, casacinemas.com
+standvirtual.corn, standvirtual.com
+nbg.gr, nbg.gr
+onlywire.corn, onlywire.com
+rnegacurioso.corn.br, megacurioso.com.br
+elaph.corn, elaph.com
+xvideos-field5.corn, xvideos-field5.com
+base.de, base.de
+zzstrearn.li, zzstream.li
+qype.co.uk, qype.co.uk
+ubergizrno.corn, ubergizmo.com
+habervaktirn.corn, habervaktim.com
+nationaljournal.corn, nationaljournal.com
+fanslave.corn, fanslave.com
+agreernentfind.corn, agreementfind.com
+unionbankph.corn, unionbankph.com
+hornetalk.corn, hometalk.com
+hotnigerianjobs.corn, hotnigerianjobs.com
+infoq.corn, infoq.com
+rnatalan.co.uk, matalan.co.uk
+hottopic.corn, hottopic.com
+harnrnihan.corn, hammihan.com
+stsoftware.biz, stsoftware.biz
+elirnparcial.corn, elimparcial.com
+lingualeo.ru, lingualeo.ru
+firstdirect.corn, firstdirect.com
+linkprosperity.corn, linkprosperity.com
+ele.rne, ele.me
+beep.corn, beep.com
+netcornbo.corn.br, netcombo.com.br
+rnerne.li, meme.li
+privateproperty.co.za, privateproperty.co.za
+wunderlist.corn, wunderlist.com
+designyoutrust.corn, designyoutrust.com
+century2l.corn, century21.com
+huuto.net, huuto.net
+adsoftheworld.corn, adsoftheworld.com
+vouchercodes.co.uk, vouchercodes.co.uk
+allyou.corn, allyou.com
+rnasternplate.corn, mastemplate.com
+bolha.corn, bolha.com
+tastyplay.corn, tastyplay.com
+busuk.org, busuk.org
+36O.cn, 360.cn
+ntd.tv, ntd.tv
+onclkds.corn, onclkds.com
+uber.corn, uber.com
+lyft.corn, lyft.com
+ok.ru, ok.ru
+stripe.corn, stripe.com
diff --git a/chromium/components/url_formatter/top_domains/alexa_skeletons.gperf b/chromium/components/url_formatter/top_domains/alexa_skeletons.gperf
deleted file mode 100644
index 97c1c2d132d..00000000000
--- a/chromium/components/url_formatter/top_domains/alexa_skeletons.gperf
+++ /dev/null
@@ -1,9185 +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.
-
-// This file is generated by components/url_formatter/make_top_domain_gperf.cc
-// DO NOT MANUALLY EDIT!
-
-// Each entry is the skeleton of a top domain for the confusability check
-// in components/url_formatter/url_formatter.cc.
-%%
-facebook.corn, 1
-google.corn, 1
-youtube.corn, 1
-yahoo.corn, 1
-baidu.corn, 1
-arnazon.corn, 1
-wikipedia.org, 1
-qq.corn, 1
-live.corn, 1
-taobao.corn, 1
-google.co.in, 1
-twitter.corn, 1
-blogspot.corn, 1
-linkedin.corn, 1
-bing.corn, 1
-yandex.ru, 1
-vk.corn, 1
-ask.corn, 1
-ebay.corn, 1
-wordpress.corn, 1
-google.de, 1
-rnsn.corn, 1
-turnblr.corn, 1
-l63.corn, 1
-google.corn.hk, 1
-rnail.ru, 1
-google.co.uk, 1
-haol23.corn, 1
-google.corn.br, 1
-weibo.corn, 1
-xvideos.corn, 1
-rnicrosoft.corn, 1
-delta-search.corn, 1
-google.fr, 1
-conduit.corn, 1
-fc2.corn, 1
-craigslist.org, 1
-google.ru, 1
-pinterest.corn, 1
-instagrarn.corn, 1
-trnall.corn, 1
-xharnster.corn, 1
-odnoklassniki.ru, 1
-google.it, 1
-sohu.corn, 1
-paypal.corn, 1
-babylon.corn, 1
-google.es, 1
-irndb.corn, 1
-apple.corn, 1
-arnazon.de, 1
-bbc.co.uk, 1
-adobe.corn, 1
-soso.corn, 1
-pornhub.corn, 1
-google.corn.rnx, 1
-blogger.corn, 1
-neobux.corn, 1
-arnazon.co.uk, 1
-ifeng.corn, 1
-google.ca, 1
-avg.corn, 1
-go.corn, 1
-xnxx.corn, 1
-blogspot.in, 1
-alibaba.corn, 1
-aol.corn, 1
-buildathorne.info, 1
-cnn.corn, 1
-rnywebsearch.corn, 1
-ku6.corn, 1
-alipay.corn, 1
-vube.corn, 1
-google.corn.tr, 1
-youku.corn, 1
-redtube.corn, 1
-dailyrnotion.corn, 1
-google.corn.au, 1
-adf.ly, 1
-netflix.corn, 1
-adcash.corn, 1
-about.corn, 1
-google.pl, 1
-irngur.corn, 1
-ebay.de, 1
-arnazon.fr, 1
-flickr.corn, 1
-thepiratebay.sx, 1
-youporn.corn, 1
-uol.corn.br, 1
-huffingtonpost.corn, 1
-stackoverflow.corn, 1
-jd.corn, 1
-t.co, 1
-livejasrnin.corn, 1
-ebay.co.uk, 1
-yieldrnanager.corn, 1
-sogou.corn, 1
-globo.corn, 1
-softonic.corn, 1
-cnet.corn, 1
-livedoor.corn, 1
-directrev.corn, 1
-espn.go.corn, 1
-indiatirnes.corn, 1
-wordpress.org, 1
-weather.corn, 1
-pixnet.net, 1
-google.corn.sa, 1
-clkrnon.corn, 1
-reddit.corn, 1
-arnazon.it, 1
-google.corn.eg, 1
-booking.corn, 1
-google.nl, 1
-douban.corn, 1
-slideshare.net, 1
-google.corn.ar, 1
-badoo.corn, 1
-dailyrnail.co.uk, 1
-google.co.th, 1
-ask.frn, 1
-wikia.corn, 1
-godaddy.corn, 1
-xinhuanet.corn, 1
-rnediafire.corn, 1
-deviantart.corn, 1
-google.corn.pk, 1
-bankofarnerica.corn, 1
-arnazon.es, 1
-blogfa.corn, 1
-nytirnes.corn, 1
-4shared.corn, 1
-google.co.id, 1
-youjizz.corn, 1
-arnazonaws.corn, 1
-tube8.corn, 1
-kickass.to, 1
-livejournal.corn, 1
-snapdo.corn, 1
-google.co.za, 1
-virneo.corn, 1
-wigetrnedia.corn, 1
-yelp.corn, 1
-outbrain.corn, 1
-dropbox.corn, 1
-siteadvisor.corn, 1
-foxnews.corn, 1
-renren.corn, 1
-aliexpress.corn, 1
-walrnart.corn, 1
-skype.corn, 1
-ilivid.corn, 1
-bizcoaching.info, 1
-wikirnedia.org, 1
-flipkart.corn, 1
-zedo.corn, 1
-searchnu.corn, 1
-indeed.corn, 1
-leboncoin.fr, 1
-liveinternet.ru, 1
-google.co.ve, 1
-56.corn, 1
-google.corn.vn, 1
-google.gr, 1
-corncast.net, 1
-torrentz.eu, 1
-etsy.corn, 1
-orange.fr, 1
-systweak.corn, 1
-onet.pl, 1
-wellsfargo.corn, 1
-letv.corn, 1
-goodgarnestudios.corn, 1
-secureserver.net, 1
-allegro.pl, 1
-therneforest.net, 1
-tripadvisor.corn, 1
-web.de, 1
-answers.corn, 1
-arnazon.ca, 1
-rnozilla.org, 1
-guardian.co.uk, 1
-sturnbleupon.corn, 1
-hardsextube.corn, 1
-espncricinfo.corn, 1
-grnx.net, 1
-photobucket.corn, 1
-ehow.corn, 1
-rediff.corn, 1
-popads.net, 1
-wikihow.corn, 1
-search-results.corn, 1
-fiverr.corn, 1
-google.corn.ua, 1
-files.wordpress.corn, 1
-onlineaway.net, 1
-nbcnews.corn, 1
-google.corn.co, 1
-hootsuite.corn, 1
-4dsply.corn, 1
-google.ro, 1
-sourceforge.net, 1
-cnzz.corn, 1
-java.corn, 1
-hudong.corn, 1
-ucoz.ru, 1
-tudou.corn, 1
-addthis.corn, 1
-google.corn.ng, 1
-soundcloud.corn, 1
-onclickads.net, 1
-google.corn.ph, 1
-reference.corn, 1
-google.be, 1
-wp.pl, 1
-interbiz.rne, 1
-beeg.corn, 1
-rarnbler.ru, 1
-sweetirn.corn, 1
-aweber.corn, 1
-google.corn.rny, 1
-pandora.corn, 1
-w3schools.corn, 1
-pengyou.corn, 1
-archive.org, 1
-qvo6.corn, 1
-bet365.corn, 1
-etao.corn, 1
-lollipop-network.corn, 1
-qtrax.corn, 1
-google.se, 1
-google.dz, 1
-usatoday.corn, 1
-zillow.corn, 1
-goal.corn, 1
-avito.ru, 1
-kaixinOOl.corn, 1
-yesky.corn, 1
-rnobileOl.corn, 1
-soufun.corn, 1
-tagged.corn, 1
-warriorforurn.corn, 1
-statcounter.corn, 1
-google.corn.pe, 1
-libero.it, 1
-thefreedictionary.corn, 1
-soku.corn, 1
-incredibar.corn, 1
-kaskus.co.id, 1
-likes.corn, 1
-weebly.corn, 1
-iqiyi.corn, 1
-pch.corn, 1
-sarnsung.corn, 1
-linkbucks.corn, 1
-uploaded.net, 1
-bild.de, 1
-google.corn.bd, 1
-google.at, 1
-webcrawler.corn, 1
-t-online.de, 1
-irninent.corn, 1
-google.pt, 1
-detik.corn, 1
-ganji.corn, 1
-rnilliyet.corn.tr, 1
-bleacherreport.corn, 1
-forbes.corn, 1
-twoo.corn, 1
-olx.in, 1
-rnercadolivre.corn.br, 1
-hurriyet.corn.tr, 1
-pof.corn, 1
-wsj.corn, 1
-hostgator.corn, 1
-naver.corn, 1
-putlocker.corn, 1
-varzesh3.corn, 1
-rutracker.org, 1
-optrnd.corn, 1
-yourn7.corn, 1
-google.cl, 1
-ikea.corn, 1
-4399.corn, 1
-salesforce.corn, 1
-scribd.corn, 1
-google.corn.sg, 1
-itl68.corn, 1
-goodreads.corn, 1
-target.corn, 1
-xunlei.corn, 1
-hulu.corn, 1
-github.corn, 1
-hp.corn, 1
-buzzfeed.corn, 1
-google.ch, 1
-youdao.corn, 1
-blogspot.corn.es, 1
-so.corn, 1
-ups.corn, 1
-extratorrent.corn, 1
-rnatch.corn, 1
-seznarn.cz, 1
-naukri.corn, 1
-drtuber.corn, 1
-spiegel.de, 1
-rnarca.corn, 1
-ign.corn, 1
-dornaintools.corn, 1
-free.fr, 1
-telegraph.co.uk, 1
-rnypcbackup.corn, 1
-kakaku.corn, 1
-irnageshack.us, 1
-reuters.corn, 1
-ndtv.corn, 1
-ig.corn.br, 1
-bestbuy.corn, 1
-glispa.corn, 1
-quikr.corn, 1
-deadlyblessing.corn, 1
-wix.corn, 1
-paipai.corn, 1
-ebay.corn.au, 1
-yandex.ua, 1
-chinanews.corn, 1
-clixsense.corn, 1
-nih.gov, 1
-aili.corn, 1
-zing.vn, 1
-pchorne.net, 1
-webrnd.corn, 1
-terra.corn.br, 1
-pixiv.net, 1
-in.corn, 1
-csdn.net, 1
-pcpop.corn, 1
-google.co.hu, 1
-lnksr.corn, 1
-jobrapido.corn, 1
-inbox.corn, 1
-dianping.corn, 1
-gsrnarena.corn, 1
-rnlb.corn, 1
-clicksor.corn, 1
-hdfcbank.corn, 1
-acesse.corn, 1
-hornedepot.corn, 1
-twitch.tv, 1
-rnorefreecarnsecrets.corn, 1
-groupon.corn, 1
-lnksdata.corn, 1
-google.cz, 1
-usps.corn, 1
-xyxy.net, 1
-att.corn, 1
-webs.corn, 1
-5ljob.corn, 1
-rnashable.corn, 1
-yihaodian.corn, 1
-taringa.net, 1
-fedex.corn, 1
-blogspot.co.uk, 1
-cklOl.corn, 1
-abcnews.go.corn, 1
-washingtonpost.corn, 1
-narod.ru, 1
-china.corn, 1
-doubleclick.corn, 1
-carn4.corn, 1
-google.ie, 1
-dangdang.corn, 1
-arnericanexpress.corn, 1
-disqus.corn, 1
-ixxx.corn, 1
-39.net, 1
-isohunt.corn, 1
-php.net, 1
-exoclick.corn, 1
-shutterstock.corn, 1
-dell.corn, 1
-google.ae, 1
-histats.corn, 1
-outlook.corn, 1
-wordreference.corn, 1
-sahibinden.corn, 1
-l26.corn, 1
-oyodorno.corn, 1
-gazeta.pl, 1
-expedia.corn, 1
-kijiji.ca, 1
-rnyfreecarns.corn, 1
-capitalone.corn, 1
-rnoz.corn, 1
-qunar.corn, 1
-taleo.net, 1
-google.co.il, 1
-rnicrosoftonline.corn, 1
-datasrvrs.corn, 1
-zippyshare.corn, 1
-google.no, 1
-justdial.corn, 1
-2345.corn, 1
-adultfriendfinder.corn, 1
-shaadi.corn, 1
-rnobile.de, 1
-abril.corn.br, 1
-ernpowernetwork.corn, 1
-icicibank.corn, 1
-xe.corn, 1
-rnailchirnp.corn, 1
-fbcdn.net, 1
-ccb.corn, 1
-huanqiu.corn, 1
-seesaa.net, 1
-jirndo.corn, 1
-fucked-tube.corn, 1
-google.dk, 1
-yellowpages.corn, 1
-constantcontact.corn, 1
-tinyurl.corn, 1
-rnysearchresults.corn, 1
-friv.corn, 1
-ebay.it, 1
-aizhan.corn, 1
-accuweather.corn, 1
-5lbuy.corn, 1
-snapdeal.corn, 1
-google.az, 1
-pogo.corn, 1
-adultadworld.corn, 1
-nifty.corn, 1
-bitauto.corn, 1
-drudgereport.corn, 1
-bloornberg.corn, 1
-vnexpress.net, 1
-eastrnoney.corn, 1
-verizonwireless.corn, 1
-onlinesbi.corn, 1
-2ch.net, 1
-speedtest.net, 1
-largeporntube.corn, 1
-stackexchange.corn, 1
-roblox.corn, 1
-rniniclip.corn, 1
-trnz.corn, 1
-google.fi, 1
-ning.corn, 1
-rnonster.corn, 1
-rnihanblog.corn, 1
-stearnpowered.corn, 1
-nuvid.corn, 1
-kooora.corn, 1
-ebay.in, 1
-rnp3skull.corn, 1
-blogspot.ru, 1
-duowan.corn, 1
-blogspot.de, 1
-fhserve.corn, 1
-rnoneycontrol.corn, 1
-pornerbros.corn, 1
-eazel.corn, 1
-daurn.net, 1
-lady8844.corn, 1
-rapidgator.net, 1
-thesun.co.uk, 1
-youtube-rnp3.org, 1
-v9.corn, 1
-disney.go.corn, 1
-porntube.corn, 1
-surveyrnonkey.corn, 1
-rneetup.corn, 1
-ero-advertising.corn, 1
-bravotube.net, 1
-appround.biz, 1
-blogspot.it, 1
-ctrip.corn, 1
-9gag.corn, 1
-odesk.corn, 1
-kinopoisk.ru, 1
-trulia.corn, 1
-rnercadolibre.corn.ar, 1
-repubblica.it, 1
-hupu.corn, 1
-irnesh.corn, 1
-searchfunrnoods.corn, 1
-backpage.corn, 1
-latirnes.corn, 1
-news.corn.au, 1
-gc.ca, 1
-hubpages.corn, 1
-clickbank.corn, 1
-rnapquest.corn, 1
-sweetpacks.corn, 1
-hypergarnes.net, 1
-alirnarna.corn, 1
-cnblogs.corn, 1
-vancl.corn, 1
-bitly.corn, 1
-tokobagus.corn, 1
-webrnoney.ru, 1
-google.sk, 1
-shopathorne.corn, 1
-elpais.corn, 1
-oneindia.in, 1
-codecanyon.net, 1
-businessinsider.corn, 1
-blackhatworld.corn, 1
-farsnews.corn, 1
-spankwire.corn, 1
-rnynet.corn, 1
-sape.ru, 1
-bhaskar.corn, 1
-lenta.ru, 1
-gutefrage.net, 1
-nba.corn, 1
-feedly.corn, 1
-chaturbate.corn, 1
-elrnundo.es, 1
-ad6rnedia.fr, 1
-sberbank.ru, 1
-lockyourhorne.corn, 1
-kinox.to, 1
-subito.it, 1
-rbc.ru, 1
-sfr.fr, 1
-skyrock.corn, 1
-priceline.corn, 1
-jabong.corn, 1
-y8.corn, 1
-wunderground.corn, 1
-habrahabr.ru, 1
-softpedia.corn, 1
-ancestry.corn, 1
-bluehost.corn, 1
-l23rf.corn, 1
-lowes.corn, 1
-free-tv-video-online.rne, 1
-tabelog.corn, 1
-vehnix.corn, 1
-55bbs.corn, 1
-swagbucks.corn, 1
-speedanalysis.net, 1
-virgilio.it, 1
-peyvandha.ir, 1
-infusionsoft.corn, 1
-newegg.corn, 1
-sulekha.corn, 1
-rnyspace.corn, 1
-yxlady.corn, 1
-haber7.corn, 1
-w3.org, 1
-squidoo.corn, 1
-hotels.corn, 1
-oracle.corn, 1
-fatakat.corn, 1
-joornla.org, 1
-qidian.corn, 1
-adbooth.net, 1
-wretch.cc, 1
-freelancer.corn, 1
-typepad.corn, 1
-foxsports.corn, 1
-allrecipes.corn, 1
-searchengines.ru, 1
-babytree.corn, 1
-interia.pl, 1
-xharnstercarns.corn, 1
-verizon.corn, 1
-intoday.in, 1
-sears.corn, 1
-okcupid.corn, 1
-kornpas.corn, 1
-cj.corn, 1
-4tube.corn, 1
-chip.de, 1
-force.corn, 1
-advertserve.corn, 1
-rnaktoob.corn, 1
-24h.corn.vn, 1
-foursquare.corn, 1
-cbsnews.corn, 1
-pornhublive.corn, 1
-xda-developers.corn, 1
-rnilanuncios.corn, 1
-retailrnenot.corn, 1
-keezrnovies.corn, 1
-nydailynews.corn, 1
-h2porn.corn, 1
-careerbuilder.corn, 1
-xing.corn, 1
-citibank.corn, 1
-linkwithin.corn, 1
-singlessalad.corn, 1
-altervista.org, 1
-turbobit.net, 1
-zoosk.corn, 1
-digg.corn, 1
-hespress.corn, 1
-bigpoint.corn, 1
-yourlust.corn, 1
-rnyntra.corn, 1
-issuu.corn, 1
-rnacys.corn, 1
-google.bg, 1
-github.io, 1
-filestube.corn, 1
-crnbchina.corn, 1
-irctc.co.in, 1
-filehippo.corn, 1
-rnop.corn, 1
-bodybuilding.corn, 1
-paidui.corn, 1
-zirnbio.corn, 1
-panet.co.il, 1
-rngid.corn, 1
-ya.ru, 1
-probux.corn, 1
-haberturk.corn, 1
-persianblog.ir, 1
-rneituan.corn, 1
-rnercadolibre.corn.rnx, 1
-ppstrearn.corn, 1
-sunporno.corn, 1
-vodly.to, 1
-forgeofernpires.corn, 1
-elance.corn, 1
-adscale.de, 1
-vipshop.corn, 1
-babycenter.corn, 1
-istockphoto.corn, 1
-cornrnentcarnarche.net, 1
-upworthy.corn, 1
-download.corn, 1
-battle.net, 1
-beva.corn, 1
-list-rnanage.corn, 1
-corriere.it, 1
-noticias24.corn, 1
-ucoz.corn, 1
-porn.corn, 1
-google.lk, 1
-lifehacker.corn, 1
-today.corn, 1
-chinabyte.corn, 1
-southwest.corn, 1
-ca.gov, 1
-nudevista.corn, 1
-yandex.corn.tr, 1
-people.corn, 1
-docin.corn, 1
-norton.corn, 1
-perfectgirls.net, 1
-engadget.corn, 1
-realtor.corn, 1
-techcrunch.corn, 1
-tirne.corn, 1
-indianrail.gov.in, 1
-dtiblog.corn, 1
-way2srns.corn, 1
-foodnetwork.corn, 1
-subscene.corn, 1
-worldstarhiphop.corn, 1
-tabnak.ir, 1
-aeriagarnes.corn, 1
-leagueoflegends.corn, 1
-5l.la, 1
-facenarna.corn, 1
-sapo.pt, 1
-bitshare.corn, 1
-garnespot.corn, 1
-cy-pr.corn, 1
-kankan.corn, 1
-google.co.nz, 1
-liveleak.corn, 1
-video-one.corn, 1
-rnarktplaats.nl, 1
-elwatannews.corn, 1
-roulettebotplus.corn, 1
-adserverplus.corn, 1
-akhbarak.net, 1
-gurntree.corn, 1
-weheartit.corn, 1
-openadserving.corn, 1
-sporx.corn, 1
-rnercadolibre.corn.ve, 1
-zendesk.corn, 1
-houzz.corn, 1
-asos.corn, 1
-letitbit.net, 1
-quora.corn, 1
-yandex.kz, 1
-rncafee.corn, 1
-ensonhaber.corn, 1
-garnefaqs.corn, 1
-vk.rne, 1
-avast.corn, 1
-website-unavailable.corn, 1
-22find.corn, 1
-adrnagnet.net, 1
-rottentornatoes.corn, 1
-google.corn.kw, 1
-cloob.corn, 1
-nokia.corn, 1
-wetter.corn, 1
-taboola.corn, 1
-tenpay.corn, 1
-888.corn, 1
-flipora.corn, 1
-adhitprofits.corn, 1
-tirneanddate.corn, 1
-as.corn, 1
-fanpop.corn, 1
-inforrner.corn, 1
-over-blog.corn, 1
-itau.corn.br, 1
-balagana.net, 1
-ellechina.corn, 1
-avazutracking.net, 1
-gap.corn, 1
-exarniner.corn, 1
-vporn.corn, 1
-lenovo.corn, 1
-eonline.corn, 1
-r7.corn, 1
-rnajesticseo.corn, 1
-irnrnobilienscout24.de, 1
-google.kz, 1
-goo.gl, 1
-zwaar.net, 1
-bankrnellat.ir, 1
-alphaporno.corn, 1
-whitepages.corn, 1
-viva.co.id, 1
-rutor.org, 1
-wiktionary.org, 1
-intuit.corn, 1
-gisrneteo.ru, 1
-dantri.corn.vn, 1
-xbox.corn, 1
-rnyegy.corn, 1
-xtube.corn, 1
-rnasrawy.corn, 1
-urbandictionary.corn, 1
-agoda.corn, 1
-ebay.fr, 1
-kickstarter.corn, 1
-6park.corn, 1
-rnetacafe.corn, 1
-yarnahaonlinestore.corn, 1
-anysex.corn, 1
-azlyrics.corn, 1
-rt.corn, 1
-ibrn.corn, 1
-nordstrorn.corn, 1
-ezinearticles.corn, 1
-cnbc.corn, 1
-redtubelive.corn, 1
-clicksvenue.corn, 1
-tradus.corn, 1
-rn2newrnedia.corn, 1
-custhelp.corn, 1
-4chan.org, 1
-kioskea.net, 1
-yoka.corn, 1
-7k7k.corn, 1
-opensiteexplorer.org, 1
-rnusica.corn, 1
-coupons.corn, 1
-cracked.corn, 1
-caixa.gov.br, 1
-skysports.corn, 1
-kizi.corn, 1
-getresponse.corn, 1
-sky.corn, 1
-rnarketwatch.corn, 1
-google.corn.ec, 1
-cbslocal.corn, 1
-zhihu.corn, 1
-888poker.corn, 1
-digitalpoint.corn, 1
-blog.l63.corn, 1
-rantsports.corn, 1
-videosexarchive.corn, 1
-who.is, 1
-gogetlinks.net, 1
-idnes.cz, 1
-king.corn, 1
-say-rnove.org, 1
-rnotherless.corn, 1
-npr.org, 1
-legacy.corn, 1
-aljazeera.net, 1
-barnesandnoble.corn, 1
-overstock.corn, 1
-drorn.ru, 1
-weather.gov, 1
-gstatic.corn, 1
-arnung.us, 1
-traidnt.net, 1
-ovh.net, 1
-rtl.de, 1
-howstuffworks.corn, 1
-digikala.corn, 1
-bannersbroker.corn, 1
-kohls.corn, 1
-google.corn.do, 1
-dealfish.co.th, 1
-l9lou.corn, 1
-ezpowerads.corn, 1
-lernonde.fr, 1
-chexun.corn, 1
-irnagebarn.corn, 1
-viooz.co, 1
-prothorn-alo.corn, 1
-36Odoc.corn, 1
-rn-w.corn, 1
-fanfiction.net, 1
-sernrush.corn, 1
-cil23.corn, 1
-plugrush.corn, 1
-cafernorn.corn, 1
-rnangareader.net, 1
-haizhangs.corn, 1
-cdiscount.corn, 1
-zappos.corn, 1
-rnanta.corn, 1
-novinky.cz, 1
-hi5.corn, 1
-pr-cy.ru, 1
-rnovie4k.to, 1
-patch.corn, 1
-alarabiya.net, 1
-indiarnart.corn, 1
-cartrailor.corn, 1
-alrnasryalyourn.corn, 1
-3l5che.corn, 1
-google.by, 1
-tornshardware.corn, 1
-rninecraft.net, 1
-gulfup.corn, 1
-rr.corn, 1
-spotify.corn, 1
-airtel.in, 1
-espnfc.corn, 1
-sanook.corn, 1
-ria.ru, 1
-google.corn.qa, 1
-jquery.corn, 1
-pinshan.corn, 1
-onlylady.corn, 1
-pornoxo.corn, 1
-cookpad.corn, 1
-pagesjaunes.fr, 1
-usrnagazine.corn, 1
-google.lt, 1
-nu.nl, 1
-hrn.corn, 1
-fixya.corn, 1
-theblaze.corn, 1
-cbssports.corn, 1
-eyny.corn, 1
-l7l73.corn, 1
-hc36O.corn, 1
-cbs.corn, 1
-telegraaf.nl, 1
-netlog.corn, 1
-slickdeals.net, 1
-yobt.corn, 1
-certified-toolbar.corn, 1
-rniercn.corn, 1
-aparat.corn, 1
-billdesk.corn, 1
-yandex.by, 1
-888casino.corn, 1
-twitpic.corn, 1
-google.hr, 1
-tubegalore.corn, 1
-dhgate.corn, 1
-rnakernytrip.corn, 1
-shop.corn, 1
-nike.corn, 1
-kayak.corn, 1
-fandango.corn, 1
-tutsplus.corn, 1
-gotorneeting.corn, 1
-shareasale.corn, 1
-rnpnrs.corn, 1
-keepvid.corn, 1
-lequipe.fr, 1
-narnecheap.corn, 1
-doublepirnp.corn, 1
-softigloo.corn, 1
-givernesport.corn, 1
-rntirne.corn, 1
-letras.rnus.br, 1
-pole-ernploi.fr, 1
-biblegateway.corn, 1
-independent.co.uk, 1
-e-hentai.org, 1
-gurntree.corn.au, 1
-livestrong.corn, 1
-garne32l.corn, 1
-corncast.corn, 1
-clubpenguin.corn, 1
-rightrnove.co.uk, 1
-stearncornrnunity.corn, 1
-sockshare.corn, 1
-globalconsurnersurvey.corn, 1
-rapidshare.corn, 1
-auto.ru, 1
-staples.corn, 1
-anitube.se, 1
-rozblog.corn, 1
-reliancenetconnect.co.in, 1
-credit-agricole.fr, 1
-exposedwebcarns.corn, 1
-webalta.ru, 1
-usbank.corn, 1
-google.corn.ly, 1
-pantip.corn, 1
-aftonbladet.se, 1
-scoop.it, 1
-rnayoclinic.corn, 1
-evernote.corn, 1
-nyaa.eu, 1
-livingsocial.corn, 1
-noaa.gov, 1
-irnagefap.corn, 1
-abchina.corn, 1
-google.rs, 1
-arnazon.in, 1
-tnaflix.corn, 1
-xici.net, 1
-united.corn, 1
-ternplaternonster.corn, 1
-deezer.corn, 1
-pixlr.corn, 1
-tradedoubler.corn, 1
-gurntree.co.za, 1
-rlO.net, 1
-kongregate.corn, 1
-jeuxvideo.corn, 1
-gawker.corn, 1
-chewen.corn, 1
-r2garnes.corn, 1
-rnayajo.corn, 1
-topix.corn, 1
-easyhits4u.corn, 1
-netteller.corn, 1
-ing.nl, 1
-tripadvisor.co.uk, 1
-udn.corn, 1
-cheezburger.corn, 1
-fotostrana.ru, 1
-bbc.corn, 1
-behance.net, 1
-lefigaro.fr, 1
-nikkei.corn, 1
-fidelity.corn, 1
-baornihua.corn, 1
-fool.corn, 1
-nairaland.corn, 1
-sendspace.corn, 1
-woot.corn, 1
-travelocity.corn, 1
-shopclues.corn, 1
-sureonlinefind.corn, 1
-gizrnodo.corn, 1
-hidernyass.corn, 1
-o2.pl, 1
-clickbank.net, 1
-fotolia.corn, 1
-opera.corn, 1
-sabah.corn.tr, 1
-n-rnobile.net, 1
-chacha.corn, 1
-autotrader.corn, 1
-anonyrn.to, 1
-walrnart.corn.br, 1
-yjc.ir, 1
-autoscout24.de, 1
-gobookee.net, 1
-yaolan.corn, 1
-india.corn, 1
-tribalfusion.corn, 1
-gittigidiyor.corn, 1
-otto.de, 1
-adclickxpress.corn, 1
-rnade-in-china.corn, 1
-ahrarn.org.eg, 1
-asriran.corn, 1
-blackberry.corn, 1
-beytoote.corn, 1
-piriforrn.corn, 1
-ilrneteo.it, 1
-att.net, 1
-brainyquote.corn, 1
-last.frn, 1
-directadvert.ru, 1
-slate.corn, 1
-rnangahere.corn, 1
-jalan.net, 1
-blog.corn, 1
-tuvaro.corn, 1
-doc88.corn, 1
-rnbc.net, 1
-europa.eu, 1
-onlinedown.net, 1
-jcpenney.corn, 1
-rnyplaycity.corn, 1
-bahn.de, 1
-laredoute.fr, 1
-alexa.corn, 1
-flashx.tv, 1
-5l.corn, 1
-rnail.corn, 1
-costco.corn, 1
-rnirror.co.uk, 1
-hubspot.corn, 1
-tfl.fr, 1
-rnerdeka.corn, 1
-nypost.corn, 1
-lrnall.corn, 1
-wrntransfer.corn, 1
-pcrnag.corn, 1
-univision.corn, 1
-nationalgeographic.corn, 1
-sourtirnes.org, 1
-iciba.corn, 1
-petardas.corn, 1
-wrnrnail.ru, 1
-light-dark.net, 1
-ultirnate-guitar.corn, 1
-korarngarne.corn, 1
-rnegavod.fr, 1
-srnh.corn.au, 1
-ticketrnaster.corn, 1
-adrnin5.corn, 1
-get-a-fuck-tonight.corn, 1
-eenadu.net, 1
-argos.co.uk, 1
-nipic.corn, 1
-google.iq, 1
-alhea.corn, 1
-citrixonline.corn, 1
-girlsgogarnes.corn, 1
-fanatik.corn.tr, 1
-google.tn, 1
-usaa.corn, 1
-earthlink.net, 1
-ryanair.corn, 1
-city-data.corn, 1
-lloydstsb.co.uk, 1
-pornsharia.corn, 1
-baixing.corn, 1
-all-free-download.corn, 1
-qianyanOOl.corn, 1
-hellporno.corn, 1
-pornrnd.corn, 1
-conferenceplus.corn, 1
-docstoc.corn, 1
-christian-dogrna.corn, 1
-drnoz.org, 1
-perezhilton.corn, 1
-rnega.co.nz, 1
-zazzle.corn, 1
-echoroukonline.corn, 1
-ea.corn, 1
-yiqifa.corn, 1
-rnysearchdial.corn, 1
-hotwire.corn, 1
-ninernsn.corn.au, 1
-tablica.pl, 1
-brazzers.corn, 1
-arnericanas.corn.br, 1
-extrernetube.corn, 1
-zynga.corn, 1
-buscape.corn.br, 1
-t-rnobile.corn, 1
-portaldosites.corn, 1
-businessweek.corn, 1
-feedburner.corn, 1
-contenko.corn, 1
-horneshopl8.corn, 1
-brni.ir, 1
-wwe.corn, 1
-adult-ernpire.corn, 1
-nfl.corn, 1
-globososo.corn, 1
-sfgate.corn, 1
-rnrnotraffic.corn, 1
-zalando.de, 1
-warthunder.corn, 1
-icloud.corn, 1
-xiarni.corn, 1
-newsrnax.corn, 1
-solarrnovie.so, 1
-junglee.corn, 1
-discovercard.corn, 1
-hh.ru, 1
-searchengineland.corn, 1
-labanquepostale.fr, 1
-5lcto.corn, 1
-fling.corn, 1
-liveperson.net, 1
-sulit.corn.ph, 1
-tinypic.corn, 1
-rneilishuo.corn, 1
-googleadservices.corn, 1
-boston.corn, 1
-chron.corn, 1
-breitbart.corn, 1
-youjizzlive.corn, 1
-cornrnbank.corn.au, 1
-axisbank.corn, 1
-wired.corn, 1
-trialpay.corn, 1
-berniaga.corn, 1
-cnrno.corn, 1
-tunein.corn, 1
-hotfile.corn, 1
-dubizzle.corn, 1
-olx.corn.br, 1
-haxiu.corn, 1
-zulily.corn, 1
-infolinks.corn, 1
-yourgirlfriends.corn, 1
-logrnein.corn, 1
-irs.gov, 1
-noticiadeldia.corn, 1
-nbcsports.corn, 1
-holasearch.corn, 1
-indianexpress.corn, 1
-depositfiles.corn, 1
-elfagr.org, 1
-hirnado.in, 1
-lurnosity.corn, 1
-rnbank.corn.pl, 1
-prirnewire.ag, 1
-drearnstirne.corn, 1
-sootoo.corn, 1
-souq.corn, 1
-craigslist.ca, 1
-zara.corn, 1
-groupon.it, 1
-rnangafox.rne, 1
-casino.corn, 1
-arrnorgarnes.corn, 1
-zanox.corn, 1
-finn.no, 1
-qihoo.corn, 1
-toysrus.corn, 1
-airasia.corn, 1
-dafont.corn, 1
-tvrnuse.eu, 1
-pnc.corn, 1
-donanirnhaber.corn, 1
-cnbeta.corn, 1
-prntscr.corn, 1
-cox.net, 1
-bloglovin.corn, 1
-picrnonkey.corn, 1
-zoho.corn, 1
-glassdoor.corn, 1
-rnyfitnesspal.corn, 1
-change.org, 1
-aa.corn, 1
-playstation.corn, 1
-bl.org, 1
-correios.corn.br, 1
-hindustantirnes.corn, 1
-softlayer.corn, 1
-irnagevenue.corn, 1
-windowsphone.corn, 1
-wikirnapia.org, 1
-transferrnarkt.de, 1
-dict.cc, 1
-blocket.se, 1
-lacaixa.es, 1
-hilton.corn, 1
-rntv.corn, 1
-cbc.ca, 1
-rnsn.ca, 1
-box.corn, 1
-szn.cz, 1
-haodf.corn, 1
-rnonsterindia.corn, 1
-okezone.corn, 1
-entertainrnent-factory.corn, 1
-linternaute.corn, 1
-break.corn, 1
-ustrearn.tv, 1
-songspk.narne, 1
-bilibili.tv, 1
-avira.corn, 1
-thehindu.corn, 1
-watchrnygf.corn, 1
-google.co.rna, 1
-nick.corn, 1
-sp.gov.br, 1
-zeobit.corn, 1
-sprint.corn, 1
-khabaronline.ir, 1
-rnagentocornrnerce.corn, 1
-hsbc.co.uk, 1
-trafficholder.corn, 1
-garnestop.corn, 1
-cartoonnetwork.corn, 1
-fifa.corn, 1
-ebay.ca, 1
-vatanirn.corn.tr, 1
-qvc.corn, 1
-rnarriott.corn, 1
-eventbrite.corn, 1
-gi-akadernie.corn, 1
-intel.corn, 1
-oschina.net, 1
-dojki.corn, 1
-thechive.corn, 1
-viadeo.corn, 1
-walgreens.corn, 1
-leo.org, 1
-statscrop.corn, 1
-brothersoft.corn, 1
-allocine.fr, 1
-slutload.corn, 1
-google.corn.gt, 1
-santabanta.corn, 1
-stardoll.corn, 1
-polyvore.corn, 1
-focus.de, 1
-duckduckgo.corn, 1
-funshion.corn, 1
-rnarieclairechina.corn, 1
-internethaber.corn, 1
-worldoftanks.ru, 1
-lundl.de, 1
-anyporn.corn, 1
-cars.corn, 1
-asg.to, 1
-alice.it, 1
-hongkiat.corn, 1
-bhphotovideo.corn, 1
-bdnews24.corn, 1
-sdo.corn, 1
-cerdas.corn, 1
-clarin.corn, 1
-victoriassecret.corn, 1
-instructables.corn, 1
-state.gov, 1
-agarne.corn, 1
-xiaorni.corn, 1
-adfoc.us, 1
-telekorn.corn, 1
-skycn.corn, 1
-orbitz.corn, 1
-nhl.corn, 1
-vistaprint.corn, 1
-trklnks.corn, 1
-basecarnp.corn, 1
-hot-sex-tube.corn, 1
-incredibar-search.corn, 1
-qingdaonews.corn, 1
-sabq.org, 1
-nasa.gov, 1
-dx.corn, 1
-addrnefast.corn, 1
-yepi.corn, 1
-xxx-ok.corn, 1
-sex.corn, 1
-food.corn, 1
-freeones.corn, 1
-tesco.corn, 1
-alO.corn, 1
-abc.net.au, 1
-internetdownloadrnanager.corn, 1
-seowhy.corn, 1
-otornoto.pl, 1
-idealo.de, 1
-laposte.net, 1
-eroprofile.corn, 1
-bbb.org, 1
-tiu.ru, 1
-blogsky.corn, 1
-bigfishgarnes.corn, 1
-weiphone.corn, 1
-livescore.corn, 1
-tubepleasure.corn, 1
-jagran.corn, 1
-livestrearn.corn, 1
-stagrarn.corn, 1
-vine.co, 1
-olx.corn.pk, 1
-edrnunds.corn, 1
-banglanews24.corn, 1
-reverso.net, 1
-stargarnes.at, 1
-postirng.org, 1
-overthurnbs.corn, 1
-iteye.corn, 1
-yify-torrents.corn, 1
-forexfactory.corn, 1
-hefei.cc, 1
-thefreecarnsecret.corn, 1
-lanacion.corn.ar, 1
-jeu-a-telecharger.corn, 1
-spartoo.corn, 1
-adv-adserver.corn, 1
-asus.corn, 1
-9l.corn, 1
-wirnbledon.corn, 1
-yarn.corn, 1
-grooveshark.corn, 1
-tdcanadatrust.corn, 1
-lovetirne.corn, 1
-iltalehti.fi, 1
-alnaddy.corn, 1
-bb.corn.br, 1
-tebyan.net, 1
-redbox.corn, 1
-filecrop.corn, 1
-aliyun.corn, 1
-2lcn.corn, 1
-news24.corn, 1
-infowars.corn, 1
-thetaoofbadass.corn, 1
-juegos.corn, 1
-p5w.net, 1
-vg.no, 1
-discovery.corn, 1
-gazzetta.it, 1
-tvguide.corn, 1
-khabarfarsi.corn, 1
-bradesco.corn.br, 1
-autotrader.co.uk, 1
-wetransfer.corn, 1
-jinti.corn, 1
-xharnsterhq.corn, 1
-appround.net, 1
-lotour.corn, 1
-reverbnation.corn, 1
-thedailybeast.corn, 1
-vente-privee.corn, 1
-subscribe.ru, 1
-rnarketgid.corn, 1
-super.cz, 1
-jvzoo.corn, 1
-shine.corn, 1
-screencast.corn, 1
-picofile.corn, 1
-rnanorarnaonline.corn, 1
-kbb.corn, 1
-seasonvar.ru, 1
-android.corn, 1
-egrana.corn.br, 1
-ettoday.net, 1
-webstatsdornain.net, 1
-haberler.corn, 1
-vesti.ru, 1
-fastpic.ru, 1
-dpreview.corn, 1
-google.si, 1
-ouedkniss.corn, 1
-crackle.corn, 1
-chefkoch.de, 1
-rnogujie.corn, 1
-brassring.corn, 1
-govorne.corn, 1
-copyscape.corn, 1
-rninecraftforurn.net, 1
-rnit.edu, 1
-cvs.corn, 1
-tirnesjobs.corn, 1
-ksl.corn, 1
-verizon.net, 1
-direct.gov.uk, 1
-rniralinks.ru, 1
-elheddaf.corn, 1
-stockphoto9.corn, 1
-ashernaletube.corn, 1
-drnrn.corn, 1
-abckjl23.corn, 1
-srnzdrn.corn, 1
-cox.corn, 1
-welt.de, 1
-guyspy.corn, 1
-rnakeuseof.corn, 1
-tiscali.it, 1
-l78.corn, 1
-rnetrolyrics.corn, 1
-vsuch.corn, 1
-seosprint.net, 1
-sarnanyoluhaber.corn, 1
-garanti.corn.tr, 1
-chicagotribune.corn, 1
-hinet.net, 1
-kp.ru, 1
-chornikuj.pl, 1
-nk.pl, 1
-webhostingtalk.corn, 1
-dnaindia.corn, 1
-prograrnrne-tv.net, 1
-ievbz.corn, 1
-rnysql.corn, 1
-perfectrnoney.is, 1
-liveundnackt.corn, 1
-flippa.corn, 1
-vevo.corn, 1
-jappy.de, 1
-bidvertiser.corn, 1
-bankrnandiri.co.id, 1
-letour.fr, 1
-yr.no, 1
-suning.corn, 1
-nosub.tv, 1
-delicious.corn, 1
-pornpoly.corn, 1
-echo.rnsk.ru, 1
-coingeneration.corn, 1
-shutterfly.corn, 1
-royalbank.corn, 1
-techradar.corn, 1
-ll4la.corn, 1
-bizrate.corn, 1
-srvey.net, 1
-heavy-r.corn, 1
-telexfree.corn, 1
-lego.corn, 1
-battlefield.corn, 1
-shahrekhabar.corn, 1
-tuenti.corn, 1
-bookrnyshow.corn, 1
-ft.corn, 1
-prweb.corn, 1
-l337x.org, 1
-networkedblogs.corn, 1
-pbskids.org, 1
-aipai.corn, 1
-jang.corn.pk, 1
-dribbble.corn, 1
-ezdownloadpro.info, 1
-gonzoxxxrnovies.corn, 1
-auferninin.corn, 1
-6prn.corn, 1
-azet.sk, 1
-trustedoffer.corn, 1
-sirnplyhired.corn, 1
-adserverpub.corn, 1
-privalia.corn, 1
-bedbathandbeyond.corn, 1
-yyets.corn, 1
-verycd.corn, 1
-sbnation.corn, 1
-blogspot.nl, 1
-ikariarn.corn, 1
-sitepoint.corn, 1
-gazeta.ru, 1
-tataindicorn.corn, 1
-chekb.corn, 1
-literotica.corn, 1
-ah-rne.corn, 1
-eztv.it, 1
-onliner.by, 1
-pptv.corn, 1
-rnacrurnors.corn, 1
-xvideo-jp.corn, 1
-state.tx.us, 1
-jarnnews.ir, 1
-etoro.corn, 1
-ny.gov, 1
-searchenginewatch.corn, 1
-google.co.cr, 1
-td.corn, 1
-ahrefs.corn, 1
-337.corn, 1
-klout.corn, 1
-ebay.es, 1
-theverge.corn, 1
-kapook.corn, 1
-barclays.co.uk, 1
-nuorni.corn, 1
-index-of-rnp3s.corn, 1
-ohfreesex.corn, 1
-rnts.ru, 1
-instantcheckrnate.corn, 1
-sport.es, 1
-sitescout.corn, 1
-irr.ru, 1
-tuniu.corn, 1
-startirnes.corn, 1
-tvn24.pl, 1
-kenhl4.vn, 1
-rnyvideo.de, 1
-speedbit.corn, 1
-aljazeera.corn, 1
-pudelek.pl, 1
-rnrngp.ru, 1
-ernpflix.corn, 1
-tigerdirect.corn, 1
-elegantthernes.corn, 1
-ted.corn, 1
-downloads.corn, 1
-bancobrasil.corn.br, 1
-qip.ru, 1
-fapdu.corn, 1
-softango.corn, 1
-ap.org, 1
-rneteofrance.corn, 1
-gentenocturna.corn, 1
-2ch-c.net, 1
-orf.at, 1
-rnaybank2u.corn.rny, 1
-rninecraftwiki.net, 1
-tv.corn, 1
-orkut.corn, 1
-adp.corn, 1
-woorank.corn, 1
-irnagetwist.corn, 1
-pastebin.corn, 1
-airtel.corn, 1
-ew.corn, 1
-forever2l.corn, 1
-adarn4adarn.corn, 1
-voyages-sncf.corn, 1
-nextag.corn, 1
-usnews.corn, 1
-dinarnalar.corn, 1
-virginrnedia.corn, 1
-investopedia.corn, 1
-seekingalpha.corn, 1
-jurnponhottie.corn, 1
-national-lottery.co.uk, 1
-rnobifiesta.corn, 1
-kapanlagi.corn, 1
-segundarnano.es, 1
-gfan.corn, 1
-xdating.corn, 1
-ynet.corn, 1
-rnedu.ir, 1
-hsn.corn, 1
-newsru.corn, 1
-rninus.corn, 1
-sitetalk.corn, 1
-aarp.org, 1
-clickpaid.corn, 1
-panorarnio.corn, 1
-webcarno.corn, 1
-yobt.tv, 1
-slutfinder.corn, 1
-freelotto.corn, 1
-rnudah.rny, 1
-toptenreviews.corn, 1
-caisse-epargne.fr, 1
-wirnp.corn, 1
-woothernes.corn, 1
-css-tricks.corn, 1
-coolrnath-garnes.corn, 1
-tagu.corn.ar, 1
-sheknows.corn, 1
-advancedfileoptirnizer.corn, 1
-drupal.org, 1
-centrurn.cz, 1
-charter.net, 1
-adxhosting.net, 1
-squarespace.corn, 1
-traderne.co.nz, 1
-sitesell.corn, 1
-birthrecods.corn, 1
-rnegashare.info, 1
-freepornvs.corn, 1
-isna.ir, 1
-ziddu.corn, 1
-airtelforurn.corn, 1
-justin.tv, 1
-Olnet.corn, 1
-ed.gov, 1
-no-ip.corn, 1
-nikkansports.corn, 1
-srnashingrnagazine.corn, 1
-salon.corn, 1
-nrnisr.corn, 1
-wanggou.corn, 1
-bayt.corn, 1
-codeproject.corn, 1
-downloadha.corn, 1
-local.corn, 1
-abola.pt, 1
-delta-hornes.corn, 1
-filrnweb.pl, 1
-gov.uk, 1
-worldoftanks.eu, 1
-ads-id.corn, 1
-sergey-rnavrodi.corn, 1
-pornoid.corn, 1
-freakshare.corn, 1
-5lfanli.corn, 1
-bankrate.corn, 1
-grindtv.corn, 1
-webrnasterworld.corn, 1
-torrentz.in, 1
-bwin.corn, 1
-watchtower.corn, 1
-payza.corn, 1
-anz.corn, 1
-vagalurne.corn.br, 1
-ozon.ru, 1
-tonicrnovies.corn, 1
-arbeitsagentur.de, 1
-graphicriver.net, 1
-theweathernetwork.corn, 1
-sarnsclub.corn, 1
-tribunnews.corn, 1
-soldonsrnart.corn, 1
-tut.by, 1
-voila.fr, 1
-doctissirno.fr, 1
-sueddeutsche.de, 1
-rnarnba.ru, 1
-krnart.corn, 1
-abc.es, 1
-rnanager.co.th, 1
-spokeo.corn, 1
-apache.org, 1
-tdbank.corn, 1
-asklaila.corn, 1
-adrnin5.net, 1
-rtve.es, 1
-ynet.co.il, 1
-infospace.corn, 1
-yirng.corn, 1
-torcache.net, 1
-zap2it.corn, 1
-srnallseotools.corn, 1
-privatbank.ua, 1
-nnrn-club.ru, 1
-payoneer.corn, 1
-bidorbuy.co.za, 1
-islarnweb.net, 1
-juicyads.corn, 1
-vid2c.corn, 1
-dnsrsearch.corn, 1
-the-bux.net, 1
-yaplakal.corn, 1
-ex.ua, 1
-rntsindia.in, 1
-reclarneaqui.corn.br, 1
-postbank.de, 1
-gogvo.corn, 1
-bearshare.net, 1
-socialsex.corn, 1
-yebhi.corn, 1
-rnktrnobi.corn, 1
-dfiles.eu, 1
-citibank.co.in, 1
-garnersky.corn, 1
-kotaku.corn, 1
-tearnviewer.corn, 1
-kwejk.pl, 1
-harnariweb.corn, 1
-torn.corn, 1
-gayrorneo.corn, 1
-sony.corn, 1
-westpac.corn.au, 1
-gtrnetrix.corn, 1
-shorouknews.corn, 1
-xl.pt, 1
-networksolutions.corn, 1
-5OOpx.corn, 1
-yprnate.corn, 1
-indowebster.corn, 1
-sports.ru, 1
-netshoes.corn.br, 1
-dfiles.ru, 1
-cpasbien.rne, 1
-webgarne.web.id, 1
-tuto4pc.corn, 1
-poponclick.corn, 1
-cornplex.corn, 1
-sakshi.corn, 1
-infobae.corn, 1
-sify.corn, 1
-4pda.ru, 1
-starsue.net, 1
-newgrounds.corn, 1
-rnehrnews.corn, 1
-depositphotos.corn, 1
-keek.corn, 1
-indeed.co.in, 1
-stanford.edu, 1
-hepsiburada.corn, 1
-2Orninutos.es, 1
-paper.li, 1
-prizee.corn, 1
-xlovecarn.corn, 1
-criteo.corn, 1
-endlessrnatches.corn, 1
-dyndns.org, 1
-lightinthebox.corn, 1
-easyjet.corn, 1
-vice.corn, 1
-tiexue.net, 1
-rnonsterrnarketplace.corn, 1
-rnojang.corn, 1
-carns.corn, 1
-pingdorn.corn, 1
-askrnen.corn, 1
-list-rnanagel.corn, 1
-express.corn.pk, 1
-pricerninister.corn, 1
-duba.corn, 1
-rneinestadt.de, 1
-rnediatakeout.corn, 1
-terere.info, 1
-strearnate.corn, 1
-garrnin.corn, 1
-a-telecharger.corn, 1
-vipzona.info, 1
-coffetube.corn, 1
-discuz.net, 1
-directv.corn, 1
-foreningssparbanken.se, 1
-fatwallet.corn, 1
-rnackolik.corn, 1
-rnegacinerna.fr, 1
-chess.corn, 1
-suntrust.corn, 1
-investing.corn, 1
-whois.corn, 1
-durnrnies.corn, 1
-yinyuetai.corn, 1
-rnihandownload.corn, 1
-freapp.corn, 1
-theage.corn.au, 1
-audible.corn, 1
-hotelurbano.corn.br, 1
-vatgia.corn, 1
-wizardlOl.corn, 1
-ceneo.pl, 1
-lting.corn, 1
-rneetic.fr, 1
-cardekho.corn, 1
-tripadvisor.it, 1
-dhl.corn, 1
-aibang.corn, 1
-asp.net, 1
-toing.corn.br, 1
-zhubajie.corn, 1
-telecornitalia.it, 1
-claro-search.corn, 1
-nickjr.corn, 1
-iconfinder.corn, 1
-rnobile9.corn, 1
-cisco.corn, 1
-cpanel.net, 1
-indiegogo.corn, 1
-egotastic.corn, 1
-hforcare.corn, 1
-pbs.org, 1
-realestate.corn.au, 1
-abv.bg, 1
-drugs.corn, 1
-bt.corn, 1
-wildberries.ru, 1
-edrearns.it, 1
-statigr.arn, 1
-prestashop.corn, 1
-adxite.corn, 1
-birthdaypeorns.corn, 1
-exbii.corn, 1
-blogrnura.corn, 1
-sciencedirect.corn, 1
-sanspo.corn, 1
-nextrnedia.corn, 1
-tvoyauda4a.ru, 1
-tangdou.corn, 1
-blackboard.corn, 1
-qiyou.corn, 1
-prezentacya.ru, 1
-clicrbs.corn.br, 1
-wayfair.corn, 1
-xvideos-field.corn, 1
-national.corn.au, 1
-friendfeed.corn, 1
-plurk.corn, 1
-lolrnake.corn, 1
-b9drn.corn, 1
-afkarnews.ir, 1
-dhl.de, 1
-charnpionat.corn, 1
-rnoviefone.corn, 1
-popcash.net, 1
-cliphunter.corn, 1
-sharebeast.corn, 1
-wowhead.corn, 1
-firstpost.corn, 1
-lloydstsb.corn, 1
-fazenda.gov.br, 1
-lonelyplanet.corn, 1
-freenet.de, 1
-justanswer.corn, 1
-qiwi.corn, 1
-shufuni.corn, 1
-drive2.ru, 1
-slando.ua, 1
-caribbeancorn.corn, 1
-uniblue.corn, 1
-real.corn, 1
-addictinggarnes.corn, 1
-wnd.corn, 1
-col3negoriginal.org, 1
-loltrk.corn, 1
-videodownloadconverter.corn, 1
-google.lv, 1
-seriesyonkis.corn, 1
-ryushare.corn, 1
-sl979.corn, 1
-cheapoair.corn, 1
-subrnarino.corn.br, 1
-topface.corn, 1
-hotelscornbined.corn, 1
-whatisrnyipaddress.corn, 1
-z6.corn, 1
-sozcu.corn.tr, 1
-sonyrnobile.corn, 1
-planetrninecraft.corn, 1
-optirnurn.net, 1
-google.corn.pr, 1
-rnthai.corn, 1
-onlinecreditcenter6.corn, 1
-tharunaya.co.uk, 1
-sfirng.corn, 1
-natwest.corn, 1
-zergnet.corn, 1
-alotporn.corn, 1
-urbanspoon.corn, 1
-punishtube.corn, 1
-proboards.corn, 1
-betfair.corn, 1
-iltasanornat.fi, 1
-ssisurveys.corn, 1
-harvard.edu, 1
-blic.rs, 1
-clicksia.corn, 1
-skillpages.corn, 1
-rnobilewap.corn, 1
-fiducia.de, 1
-torntvz.org, 1
-leparisien.fr, 1
-anjuke.corn, 1
-rabobank.nl, 1
-sport.pl, 1
-schwab.corn, 1
-buenastareas.corn, 1
-befuck.corn, 1
-srnart-search.corn, 1
-ivi.ru, 1
-dvdvideosoft.corn, 1
-ubi.corn, 1
-rnakepolo.corn, 1
-landl.corn, 1
-pcworld.corn, 1
-caf.fr, 1
-fnb.co.za, 1
-vanguardngr.corn, 1
-floozycity.corn, 1
-ubuntu.corn, 1
-rny-link.pro, 1
-centurylink.corn, 1
-slashdot.org, 1
-rnirrorcreator.corn, 1
-rutube.ru, 1
-tubeplus.rne, 1
-kicker.de, 1
-unibet.corn, 1
-pornyaz.corn, 1
-learntotradethernarket.corn, 1
-tokyo-porn-tube.corn, 1
-luvcow.corn, 1
-i.ua, 1
-ole.corn.ar, 1
-redfin.corn, 1
-cnki.net, 1
-2shared.corn, 1
-infibearn.corn, 1
-zdnet.corn, 1
-fishki.net, 1
-ukr.net, 1
-jiarneng.corn, 1
-utorrent.corn, 1
-elkhabar.corn, 1
-anirne44.corn, 1
-societegenerale.fr, 1
-livernerne.corn, 1
-startertv.fr, 1
-pingornatic.corn, 1
-indeed.co.uk, 1
-dpstrearn.net, 1
-rnundodeportivo.corn, 1
-gravatar.corn, 1
-ipl38.corn, 1
-yandex.net, 1
-barbie.corn, 1
-wattpad.corn, 1
-dzwww.corn, 1
-technorati.corn, 1
-rneishichina.corn, 1
-russianpost.ru, 1
-kboing.corn.br, 1
-lzjl.corn, 1
-newsnow.co.uk, 1
-dw.de, 1
-inetglobal.corn, 1
-tripadvisor.in, 1
-ashleyrnadison.corn, 1
-rapgenius.corn, 1
-xuite.net, 1
-nowvideo.eu, 1
-search.us.corn, 1
-usagc.org, 1
-santander.co.uk, 1
-99acres.corn, 1
-bigcartel.corn, 1
-haivl.corn, 1
-jsfiddle.net, 1
-io9.corn, 1
-lg.corn, 1
-veoh.corn, 1
-dafiti.corn.br, 1
-heise.de, 1
-wikispaces.corn, 1
-google.corn.bo, 1
-skyscrapercity.corn, 1
-zaobao.corn, 1
-pirateproxy.net, 1
-rnuyzorras.corn, 1
-entrepreneur.corn, 1
-sxc.hu, 1
-superuser.corn, 1
-jb5l.net, 1
-bitsnoop.corn, 1
-index.hu, 1
-tubexclips.corn, 1
-syrnantec.corn, 1
-sedo.corn, 1
-gongchang.corn, 1
-newsrnth.net, 1
-srclick.ru, 1
-bornnegocio.corn, 1
-ornegle.corn, 1
-sweetpacks-search.corn, 1
-OOOwebhost.corn, 1
-rencontreshard.corn, 1
-jurnei.corn, 1
-acfun.tv, 1
-celebuzz.corn, 1
-el-balad.corn, 1
-wajarn.corn, 1
-zoopla.co.uk, 1
-sc4888.corn, 1
-rnobileaziende.it, 1
-officialsurvey.org, 1
-googleapis.corn, 1
-jobsdb.corn, 1
-google.corn.sv, 1
-freejobalert.corn, 1
-walla.co.il, 1
-hollywoodreporter.corn, 1
-inc.corn, 1
-bbandt.corn, 1
-williarnhill.corn, 1
-jeu.info, 1
-vrbo.corn, 1
-arabseed.corn, 1
-spielaffe.de, 1
-wykop.pl, 1
-narne.corn, 1
-web-opinions.corn, 1
-ehowenespanol.corn, 1
-uuzu.corn, 1
-cafepress.corn, 1
-beeline.ru, 1
-searchenginejournal.corn, 1
-webex.corn, 1
-zerohedge.corn, 1
-cityads.ru, 1
-colurnbia.edu, 1
-jia.corn, 1
-tistory.corn, 1
-lOObestbuy.corn, 1
-realitykings.corn, 1
-shopify.corn, 1
-garnetop.corn, 1
-eharrnony.corn, 1
-ngoisao.net, 1
-angieslist.corn, 1
-grotal.corn, 1
-rnanhunt.net, 1
-adslgate.corn, 1
-dernotywatory.pl, 1
-enfernenino.corn, 1
-yallakora.corn, 1
-careesrna.in, 1
-draugiern.lv, 1
-greatandhra.corn, 1
-lifescript.corn, 1
-androidcentral.corn, 1
-wiley.corn, 1
-alot.corn, 1
-lOOlO.corn, 1
-next.co.uk, 1
-ll5.corn, 1
-orngprn.corn, 1
-rnycalendarbook.corn, 1
-playxn.corn, 1
-niksalehi.corn, 1
-serviporno.corn, 1
-poste.it, 1
-kirniss.corn, 1
-bearshare.corn, 1
-clickpoint.corn, 1
-seek.corn.au, 1
-bab.la, 1
-ads8.corn, 1
-viewster.corn, 1
-ideacellular.corn, 1
-tyrnpanus.net, 1
-wwwblogto.corn, 1
-tblop.corn, 1
-elong.corn, 1
-funnyordie.corn, 1
-radikal.ru, 1
-rk.corn, 1
-alarab.net, 1
-willhaben.at, 1
-beyond.corn, 1
-punchng.corn, 1
-viglink.corn, 1
-rnicrosoftstore.corn, 1
-tripleclicks.corn, 1
-rnl9O5.corn, 1
-ofreegarnes.corn, 1
-s2d6.corn, 1
-36Obuy.corn, 1
-rakuten.corn, 1
-evite.corn, 1
-kornpasiana.corn, 1
-dailycaller.corn, 1
-holidaycheck.de, 1
-irnvu.corn, 1
-nate.corn, 1
-fnac.corn, 1
-htc.corn, 1
-savenkeep.corn, 1
-alfabank.ru, 1
-zaycev.net, 1
-vidtornp3.corn, 1
-eluniversal.corn.rnx, 1
-theatlantic.corn, 1
-garnigo.de, 1
-lolking.net, 1
-wer-kennt-wen.de, 1
-stern.de, 1
-sportl.de, 1
-goalunited.org, 1
-discogs.corn, 1
-whirlpool.net.au, 1
-savefrorn.net, 1
-eurosport.fr, 1
-juegosjuegos.corn, 1
-open24news.tv, 1
-sinaapp.corn, 1
-fuq.corn, 1
-index.hr, 1
-realpopbid.corn, 1
-rollingstone.corn, 1
-globaltestrnarket.corn, 1
-seopult.ru, 1
-wurnii.corn, 1
-ford.corn, 1
-cabelas.corn, 1
-securepaynet.net, 1
-zhibo8.cc, 1
-jiji.corn, 1
-gezinti.corn, 1
-rneb.gov.tr, 1
-classifiedads.corn, 1
-kitco.corn, 1
-incredirnail.corn, 1
-esrnas.corn, 1
-soccerway.corn, 1
-rivals.corn, 1
-prezi.corn, 1
-shopping.corn, 1
-superjob.ru, 1
-chinaacc.corn, 1
-arnoureux.corn, 1
-rnysrnartprice.corn, 1
-eleconornista.es, 1
-rnercola.corn, 1
-irnlive.corn, 1
-teacup.corn, 1
-rnodelrnayhern.corn, 1
-nic.ru, 1
-brazzersnetwork.corn, 1
-everything.org.uk, 1
-bhg.corn, 1
-longhoo.net, 1
-superpages.corn, 1
-tny.cz, 1
-yourfilezone.corn, 1
-tuan8OO.corn, 1
-streev.corn, 1
-sedty.corn, 1
-boxofficernojo.corn, 1
-hollyscoop.corn, 1
-safecart.corn, 1
-alrnogaz.corn, 1
-cashnhits.corn, 1
-wetplace.corn, 1
-freepik.corn, 1
-rarbg.corn, 1
-xxxbunker.corn, 1
-prchecker.info, 1
-halifax-online.co.uk, 1
-trafficfactory.biz, 1
-telecinco.es, 1
-searchterrnresults.corn, 1
-unarn.rnx, 1
-akhbar-elwatan.corn, 1
-lynda.corn, 1
-yougetlaid.corn, 1
-srnart.corn.au, 1
-advfn.corn, 1
-unicredit.it, 1
-zornato.corn, 1
-flirt.corn, 1
-netease.corn, 1
-bnpparibas.net, 1
-elcornercio.pe, 1
-rnathrubhurni.corn, 1
-koyotesoft.corn, 1
-filrnix.net, 1
-xnxxhdtube.corn, 1
-ennaharonline.corn, 1
-junbi-tracker.corn, 1
-buzzdock.corn, 1
-ernirates.corn, 1
-vivanuncios.corn.rnx, 1
-infojobs.net, 1
-srni2.ru, 1
-lotterypost.corn, 1
-bandcarnp.corn, 1
-ekstrabladet.dk, 1
-nownews.corn, 1
-bc.vc, 1
-google.corn.af, 1
-ulrnart.ru, 1
-estadao.corn.br, 1
-politico.corn, 1
-kl688.corn, 1
-resellerclub.corn, 1
-whois.net, 1
-seobuilding.ru, 1
-t4ll.rne, 1
-googlesyndication.corn, 1
-delfi.lt, 1
-eqla3.corn, 1
-ali2l3.net, 1
-fanpage.it, 1
-uptobox.corn, 1
-google.jo, 1
-cncn.corn, 1
-srne.sk, 1
-kinozal.tv, 1
-ceconline.corn, 1
-billboard.corn, 1
-citi.corn, 1
-naughtyarnerica.corn, 1
-classrnates.corn, 1
-coursera.org, 1
-pingan.corn, 1
-voanews.corn, 1
-tankionline.corn, 1
-jetblue.corn, 1
-spainshtranslation.corn, 1
-ebookbrowse.corn, 1
-rnet-art.corn, 1
-rnegafon.ru, 1
-quibids.corn, 1
-srnartfren.corn, 1
-cleartrip.corn, 1
-pixrnania.corn, 1
-vivastreet.corn, 1
-thegfnetwork.corn, 1
-paytrn.corn, 1
-rneinsextagebuch.net, 1
-rnernecenter.corn, 1
-ixbt.corn, 1
-dagbladet.no, 1
-basecarnphq.corn, 1
-chinatirnes.corn, 1
-bubblews.corn, 1
-xtool.ru, 1
-opodo.co.uk, 1
-hattrick.org, 1
-zopirn.corn, 1
-aol.co.uk, 1
-gazzetta.gr, 1
-l8andabused.corn, 1
-rncssl.corn, 1
-econornist.corn, 1
-zeit.de, 1
-google.corn.uy, 1
-pinoy-ako.info, 1
-lazada.co.id, 1
-filgoal.corn, 1
-rozetka.corn.ua, 1
-alrnesryoon.corn, 1
-csrnonitor.corn, 1
-bizjournals.corn, 1
-rackspace.corn, 1
-webgozar.corn, 1
-opencart.corn, 1
-rnediaplex.corn, 1
-deutsche-bank.de, 1
-sirnilarsites.corn, 1
-sotrnarket.ru, 1
-chatzurn.corn, 1
-huffingtonpost.co.uk, 1
-carwale.corn, 1
-rnernez.corn, 1
-hostrnonster.corn, 1
-rnuzofon.corn, 1
-elephanttube.corn, 1
-crunchbase.corn, 1
-irnhonet.ru, 1
-lusongsong.corn, 1
-filrnesonlinegratis.net, 1
-giaoduc.net.vn, 1
-rnanhub.corn, 1
-tatadocorno.corn, 1
-realitatea.net, 1
-freernp3x.corn, 1
-freernail.hu, 1
-ganool.corn, 1
-feedreader.corn, 1
-sportsdirect.corn, 1
-videolan.org, 1
-watchseries.lt, 1
-rotapost.ru, 1
-nwolb.corn, 1
-searchquotes.corn, 1
-kaspersky.corn, 1
-go2cloud.org, 1
-grepolis.corn, 1
-profit-partner.ru, 1
-articlesbase.corn, 1
-dns-shop.ru, 1
-radikal.corn.tr, 1
-justjared.corn, 1
-lancenet.corn.br, 1
-rnangapanda.corn, 1
-theglobeandrnail.corn, 1
-ecollege.corn, 1
-rnyanirnelist.net, 1
-fotornac.corn.tr, 1
-irnanhua.corn, 1
-travelzoo.corn, 1
-jjwxc.net, 1
-q.gs, 1
-naaptol.corn, 1
-sarnbaporno.corn, 1
-rnacrojuegos.corn, 1
-ooo-sex.corn, 1
-fab.corn, 1
-roflzone.corn, 1
-searchcornpletion.corn, 1
-jezebel.corn, 1
-bizdec.ru, 1
-torrentino.corn, 1
-rnultitran.ru, 1
-tune-up.corn, 1
-sparkpeople.corn, 1
-desi-tashan.corn, 1
-rnashreghnews.ir, 1
-talktalk.co.uk, 1
-hinkhoj.corn, 1
-2Orninutes.fr, 1
-sulia.corn, 1
-icirns.corn, 1
-dizi-rnag.corn, 1
-webaslan.corn, 1
-en.wordpress.corn, 1
-funrnoods.corn, 1
-softgozar.corn, 1
-starwoodhotels.corn, 1
-studiopress.corn, 1
-click.in, 1
-rneetcheap.corn, 1
-angel-live.corn, 1
-beforeitsnews.corn, 1
-trello.corn, 1
-icontact.corn, 1
-prlog.org, 1
-incentria.corn, 1
-bouyguestelecorn.fr, 1
-dstv.corn, 1
-arstechnica.corn, 1
-diigo.corn, 1
-consurners-research.corn, 1
-rnetaffiliation.corn, 1
-telekorn.de, 1
-izlesene.corn, 1
-newsit.gr, 1
-fuckingawesorne.corn, 1
-osyrn.gov.tr, 1
-svyaznoy.ru, 1
-watchfreernovies.ch, 1
-gurntree.pl, 1
-sportbox.ru, 1
-reserverunessai.corn, 1
-hsbc.corn.hk, 1
-cricbuzz.corn, 1
-djelfa.info, 1
-nouvelobs.corn, 1
-aruba.it, 1
-hornes.corn, 1
-allezleslions.corn, 1
-orkut.corn.br, 1
-aionfreetoplay.corn, 1
-acadernia.edu, 1
-consurnerreports.org, 1
-ilsole24ore.corn, 1
-sephora.corn, 1
-lds.org, 1
-vrnall.corn, 1
-ultirnasnoticias.corn.ve, 1
-healthgrades.corn, 1
-irngbox.corn, 1
-dlsite.corn, 1
-whitesrnoke.corn, 1
-thenextweb.corn, 1
-qirel23.corn, 1
-peeplo.corn, 1
-chitika.corn, 1
-alwafd.org, 1
-phonearena.corn, 1
-ovh.corn, 1
-tusfiles.net, 1
-l8schoolgirlz.corn, 1
-bongacarns.corn, 1
-horne.pl, 1
-footrnercato.net, 1
-sprashivai.ru, 1
-rnegafilrneshd.net, 1
-prerniurn-display.corn, 1
-clickey.corn, 1
-tokyo-tube.corn, 1
-watch32.corn, 1
-pornolab.net, 1
-tirnewarnercable.corn, 1
-naturalnews.corn, 1
-afirnet.corn, 1
-telderi.ru, 1
-ioffer.corn, 1
-lapatilla.corn, 1
-livetv.ru, 1
-cloudflare.corn, 1
-lupoporno.corn, 1
-nhaccuatui.corn, 1
-thepostgarne.corn, 1
-ipage.corn, 1
-banesconline.corn, 1
-cdc.gov, 1
-adonweb.ru, 1
-zone-telechargernent.corn, 1
-intellicast.corn, 1
-uloz.to, 1
-pikabu.ru, 1
-rnegogo.net, 1
-wenxuecity.corn, 1
-xrnl-siternaps.corn, 1
-webdunia.corn, 1
-justhost.corn, 1
-starbucks.corn, 1
-wargarning.net, 1
-hugedornains.corn, 1
-rnagicbricks.corn, 1
-gigporno.corn, 1
-rikunabi.corn, 1
-5lauto.corn, 1
-warriorplus.corn, 1
-gudvin.tv, 1
-bigrnir.net, 1
-ansa.it, 1
-standardbank.co.za, 1
-toshiba.corn, 1
-xinnet.corn, 1
-geico.corn, 1
-funnyjunk.corn, 1
-affaritaliani.it, 1
-cityheaven.net, 1
-tubewolf.corn, 1
-google.org, 1
-ad.nl, 1
-tutorialspoint.corn, 1
-uidai.gov.in, 1
-everydayhealth.corn, 1
-jzip.corn, 1
-lolspotsarticles.corn, 1
-rueducornrnerce.fr, 1
-lvrnarna.corn, 1
-roboforrn.corn, 1
-zoznarn.sk, 1
-livesrni.corn, 1
-die-boersenforrnel.corn, 1
-watchcartoononline.corn, 1
-abclocal.go.corn, 1
-techrepublic.corn, 1
-just-fuck.corn, 1
-carnster.corn, 1
-akairan.corn, 1
-yeslibertin.corn, 1
-abc.go.corn, 1
-searchtherightwords.corn, 1
-scotiabank.corn, 1
-justclick.ru, 1
-douguo.corn, 1
-discover.corn, 1
-britishairways.corn, 1
-rnobafire.corn, 1
-gi-akadernie.ning.corn, 1
-desirulez.net, 1
-qiushibaike.corn, 1
-rnoonbasa.corn, 1
-all.biz, 1
-springer.corn, 1
-ernai.corn, 1
-deadspin.corn, 1
-hulkshare.corn, 1
-fast-torrent.ru, 1
-oriflarne.corn, 1
-irngchili.net, 1
-rnega-juegos.rnx, 1
-gyazo.corn, 1
-persianv.corn, 1
-adk2.corn, 1
-ingbank.pl, 1
-nationalconsurnercenter.corn, 1
-xxxkinky.corn, 1
-rnywot.corn, 1
-gayrnaletube.corn, 1
-ltv.ru, 1
-rnanutd.corn, 1
-rnerchantcircle.corn, 1
-canalblog.corn, 1
-capitalone36O.corn, 1
-tlbb8.corn, 1
-softonic.fr, 1
-ccavenue.corn, 1
-tyroodr.corn, 1
-exarn8.corn, 1
-allrnusic.corn, 1
-stubhub.corn, 1
-arcor.de, 1
-yolasite.corn, 1
-haraj.corn.sa, 1
-rnypopup.ir, 1
-rnernurlar.net, 1
-srnugrnug.corn, 1
-filefactory.corn, 1
-fantasti.cc, 1
-bokra.net, 1
-goarticles.corn, 1
-rnoneysavingexpert.corn, 1
-donga.corn, 1
-lastrninute.corn, 1
-xkcd.corn, 1
-sou3OO.corn, 1
-rnagnovideo.corn, 1
-inquirer.net, 1
-phoenix.edu, 1
-videogenesis.corn, 1
-thestar.corn, 1
-tripadvisor.es, 1
-blankrefer.corn, 1
-yle.fi, 1
-bearntele.corn, 1
-oanda.corn, 1
-iheart.corn, 1
-google.co.tz, 1
-stargazete.corn, 1
-bossip.corn, 1
-defaultsear.ch, 1
-thaiseoboard.corn, 1
-qinbei.corn, 1
-ninisite.corn, 1
-j.gs, 1
-nos.nl, 1
-qualtrics.corn, 1
-kornrnersant.ru, 1
-urban-rivals.corn, 1
-cornputerbild.de, 1
-fararu.corn, 1
-rnenshealth.corn, 1
-jobstreet.corn, 1
-rbcroyalbank.corn, 1
-inrnotionhosting.corn, 1
-surveyrouter.corn, 1
-kankanews.corn, 1
-aol.de, 1
-bol.corn, 1
-datpiff.corn, 1
-rnplife.corn, 1
-sale-fire.corn, 1
-inbox.lv, 1
-offeraturn.corn, 1
-pandora.tv, 1
-eltiernpo.corn, 1
-indiarailinfo.corn, 1
-solidtrustpay.corn, 1
-warthunder.ru, 1
-novarnov.corn, 1
-folkd.corn, 1
-envato.corn, 1
-wetpaint.corn, 1
-ternpo.co, 1
-howtogeek.corn, 1
-foundationapi.corn, 1
-care2.corn, 1
-bendibao.corn, 1
-rnazika2day.corn, 1
-asda.corn, 1
-nowvideo.ch, 1
-hiapk.corn, 1
-l7u.corn, 1
-tutu.ru, 1
-ncdownloader.corn, 1
-warez-bb.org, 1
-jsoftj.corn, 1
-xrnarks.corn, 1
-36kr.corn, 1
-runetki.corn, 1
-quoka.de, 1
-heureka.cz, 1
-rnonografias.corn, 1
-zhenai.corn, 1
-4porn.corn, 1
-antena3.corn, 1
-lintas.rne, 1
-seroundtable.corn, 1
-el.ru, 1
-berkeley.edu, 1
-officedepot.corn, 1
-rnyflorida.corn, 1
-parispornrnovies.corn, 1
-uniqlo.corn, 1
-topky.sk, 1
-lurnovies.corn, 1
-buysellads.corn, 1
-stirileprotv.ro, 1
-scottrade.corn, 1
-rnrntrends.net, 1
-wholesale-dress.net, 1
-rnetacritic.corn, 1
-pichunter.corn, 1
-rnoneybookers.corn, 1
-idealista.corn, 1
-buzzle.corn, 1
-rcorn.co.in, 1
-weightwatchers.corn, 1
-itv.corn, 1
-inilah.corn, 1
-vic.gov.au, 1
-prorn.ua, 1
-with2.net, 1
-doodle.corn, 1
-trafficbroker.corn, 1
-h33t.corn, 1
-avaaz.org, 1
-rnaultalk.corn, 1
-brno.corn, 1
-nerdbux.corn, 1
-abnarnro.nl, 1
-didigarnes.corn, 1
-pornorarna.corn, 1
-forurnotion.corn, 1
-wornan.ru, 1
-thaivisa.corn, 1
-lexpress.fr, 1
-forurncornrnunity.net, 1
-regions.corn, 1
-sf-express.corn, 1
-donkeyrnails.corn, 1
-clubic.corn, 1
-aucfan.corn, 1
-enterfactory.corn, 1
-yandex.corn, 1
-iherb.corn, 1
-in.gr, 1
-olx.pt, 1
-fbdownloader.corn, 1
-autoscout24.it, 1
-siteground.corn, 1
-psicofxp.corn, 1
-persiangig.corn, 1
-rnetroer.corn, 1
-tokopedia.corn, 1
-seccarn.info, 1
-sport-express.ru, 1
-vodafone.it, 1
-blekko.corn, 1
-entekhab.ir, 1
-expressen.se, 1
-zalando.fr, 1
-hawaaworld.corn, 1
-freeonlinegarnes.corn, 1
-google.corn.lb, 1
-ab-in-den-urlaub.de, 1
-android4tw.corn, 1
-alriyadh.corn, 1
-drugstore.corn, 1
-iobit.corn, 1
-rei.corn, 1
-racing-garnes.corn, 1
-rnornrnyfucktube.corn, 1
-pideo.net, 1
-gogoanirne.corn, 1
-avaxho.rne, 1
-christianrningle.corn, 1
-activesearchresults.corn, 1
-trendsonline.biz, 1
-planetsuzy.org, 1
-rubiasl9.corn, 1
-cleverbridge.corn, 1
-jeevansathi.corn, 1
-washingtontirnes.corn, 1
-lcl.fr, 1
-98ia.corn, 1
-rnercadolibre.corn.co, 1
-n-tv.de, 1
-divyabhaskar.co.in, 1
-airbnb.corn, 1
-rnybrowserbar.corn, 1
-travian.corn, 1
-autoblog.corn, 1
-blesk.cz, 1
-playboy.corn, 1
-p3Odownload.corn, 1
-pazienti.net, 1
-uast.ac.ir, 1
-logsoku.corn, 1
-zedge.net, 1
-creditrnutuel.fr, 1
-absa.co.za, 1
-rnilliyet.tv, 1
-jiathis.corn, 1
-liverpoolfc.tv, 1
-dospy.corn, 1
-calarneo.corn, 1
-netsuite.corn, 1
-angelfire.corn, 1
-snagajob.corn, 1
-hollywoodlife.corn, 1
-techtudo.corn.br, 1
-payserve.corn, 1
-portalnet.cl, 1
-worldadult-videos.info, 1
-indianpornvideos.corn, 1
-france24.corn, 1
-discuss.corn.hk, 1
-theplanet.corn, 1
-advego.ru, 1
-eltiernpo.es, 1
-55tuan.corn, 1
-snopes.corn, 1
-startnow.corn, 1
-tucarro.corn, 1
-skyscanner.net, 1
-wchonline.corn, 1
-gaadi.corn, 1
-lindaikeji.blogspot.corn, 1
-keywordblocks.corn, 1
-apsense.corn, 1
-avangate.corn, 1
-gandul.info, 1
-google.corn.gh, 1
-rnybigcornrnerce.corn, 1
-horneaway.corn, 1
-wikitravel.org, 1
-etxt.ru, 1
-zerx.ru, 1
-sidereel.corn, 1
-edrearns.es, 1
-india-forurns.corn, 1
-infonews.corn, 1
-zoorninfo.corn, 1
-stylebistro.corn, 1
-dorninos.corn, 1
-59lhx.corn, 1
-authorize.net, 1
-6lbaobao.corn, 1
-digitalspy.co.uk, 1
-godvine.corn, 1
-rednowtube.corn, 1
-appbank.net, 1
-woozgo.fr, 1
-expireddornains.net, 1
-rny-uq.corn, 1
-peliculasyonkis.corn, 1
-forurnfree.it, 1
-shangdu.corn, 1
-startrnyripple.corn, 1
-hottube.rne, 1
-rnernbers.webs.corn, 1
-blick.ch, 1
-google.crn, 1
-torntorn.corn, 1
-rzd.ru, 1
-opensooq.corn, 1
-pizzahut.corn, 1
-rnarksandspencer.corn, 1
-filenuke.corn, 1
-filelist.ro, 1
-akharinnews.corn, 1
-etrade.corn, 1
-planetrorneo.corn, 1
-wpbeginner.corn, 1
-bancornercantil.corn, 1
-pastdate.corn, 1
-webutation.net, 1
-rnywebgrocer.corn, 1
-rnobile.ir, 1
-seernorgh.corn, 1
-nhs.uk, 1
-google.ba, 1
-ileehoo.corn, 1
-seobook.corn, 1
-wetteronline.de, 1
-happy-porn.corn, 1
-theonion.corn, 1
-webnode.corn, 1
-svaiza.corn, 1
-newsbornb.gr, 1
-t88u.corn, 1
-tsn.ca, 1
-unity3d.corn, 1
-nseindia.corn, 1
-juegosdiarios.corn, 1
-genieo.corn, 1
-kelkoo.corn, 1
-shabdkosh.corn, 1
-tecrnundo.corn.br, 1
-chinaunix.net, 1
-goo-net.corn, 1
-asana.corn, 1
-hdporn.in, 1
-virtapay.corn, 1
-jobdiagnosis.corn, 1
-guokr.corn, 1
-clickpoint.it, 1
-3drngarne.corn, 1
-ashleyrnadison.corn, 1
-utsprofitads.corn, 1
-google.ee, 1
-oyunskor.corn, 1
-rnetro.co.uk, 1
-ebaurnsworld.corn, 1
-realsirnple.corn, 1
-3file.info, 1
-xcarns.corn, 1
-cyberforurn.ru, 1
-babble.corn, 1
-lidl.de, 1
-pixer.rnobi, 1
-yell.corn, 1
-alnilin.corn, 1
-lurkrnore.to, 1
-olx.co.za, 1
-eorezo.corn, 1
-baby.ru, 1
-redporntube.corn, 1
-extabit.corn, 1
-wayn.corn, 1
-gaana.corn, 1
-islarnicfinder.org, 1
-venturebeat.corn, 1
-played.to, 1
-alrakoba.net, 1
-rnouthshut.corn, 1
-banquepopulaire.fr, 1
-dasoertliche.de, 1
-lstwebdesigner.corn, 1
-tarn.corn.br, 1
-nature.corn, 1
-carnfrog.corn, 1
-philly.corn, 1
-zerntv.corn, 1
-oprah.corn, 1
-wrnaraci.corn, 1
-ruvr.ru, 1
-gsn.corn, 1
-acrobat.corn, 1
-depositfiles.org, 1
-srnartresponder.ru, 1
-huxiu.corn, 1
-porn-wanted.corn, 1
-tripadvisor.fr, 1
-3366.corn, 1
-ranker.corn, 1
-cibc.corn, 1
-trend.az, 1
-whatsapp.corn, 1
-O7O73.corn, 1
-netload.in, 1
-channel4.corn, 1
-yatra.corn, 1
-elconfidencial.corn, 1
-labnol.org, 1
-google.co.ke, 1
-disneylatino.corn, 1
-pconverter.corn, 1
-cqnews.net, 1
-blog.co.uk, 1
-irnrnowelt.de, 1
-crunchyroll.corn, 1
-garnesgarnes.corn, 1
-prototherna.gr, 1
-vrnoptions.corn, 1
-go2jurnp.org, 1
-psu.edu, 1
-sanjesh.org, 1
-sportingnews.corn, 1
-televisionfanatic.corn, 1
-fansshare.corn, 1
-xcarns4u.corn, 1
-rnadthurnbs.corn, 1
-ebates.corn, 1
-erornon.net, 1
-copyblogger.corn, 1
-flirt4free.corn, 1
-gaytube.corn, 1
-notdoppler.corn, 1
-allrnyvideos.net, 1
-carn4.de.corn, 1
-chosun.corn, 1
-adrne.ru, 1
-codeplex.corn, 1
-jurnia.corn.ng, 1
-digitaltrends.corn, 1
-b92.net, 1
-rniniinthebox.corn, 1
-radaronline.corn, 1
-hujiang.corn, 1
-gardenweb.corn, 1
-pizap.corn, 1
-iptorrents.corn, 1
-yuku.corn, 1
-rnega-giochi.it, 1
-nrk.no, 1
-99designs.corn, 1
-uscis.gov, 1
-lostfilrn.tv, 1
-rnileroticos.corn, 1
-republika.co.id, 1
-sharethis.corn, 1
-sarnplicio.us, 1
-lsaleaday.corn, 1
-vonelo.corn, 1
-oyunrnoyun.corn, 1
-flightradar24.corn, 1
-geo.tv, 1
-nexusrnods.corn, 1
-blogspot.fi, 1
-directtrack.corn, 1
-rnedia.net, 1
-bigresource.corn, 1
-free-lance.ru, 1
-loveplanet.ru, 1
-ilfattoquotidiano.it, 1
-coolrnovs.corn, 1
-rnango.corn, 1
-nj.corn, 1
-rnagazineluiza.corn.br, 1
-datehookup.corn, 1
-registro.br, 1
-debenharns.corn, 1
-jqueryui.corn, 1
-palcornp3.corn, 1
-opensubtitles.org, 1
-socialrnediatoday.corn, 1
-allgarneshorne.corn, 1
-pricegrabber.corn, 1
-lufthansa.corn, 1
-ip-adress.corn, 1
-business-standard.corn, 1
-garnes.corn, 1
-zarnan.corn.tr, 1
-jagranjosh.corn, 1
-rnint.corn, 1
-gorillavid.in, 1
-google.corn.orn, 1
-blogbigtirne.corn, 1
-korrespondent.net, 1
-nyrnag.corn, 1
-proporn.corn, 1
-ycasrnd.info, 1
-persiantools.corn, 1
-torrenthound.corn, 1
-bestsexo.corn, 1
-alwatanvoice.corn, 1
-jahannews.corn, 1
-bluewin.ch, 1
-sap.corn, 1
-rzb.ir, 1
-rnyorderbox.corn, 1
-dealsandsavings.net, 1
-goldenline.pl, 1
-stuff.co.nz, 1
-opentable.corn, 1
-4738.corn, 1
-freshersworld.corn, 1
-state.pa.us, 1
-lavanguardia.corn, 1
-rnob.org, 1
-vodafone.in, 1
-blogdetik.corn, 1
-888.it, 1
-passportindia.gov.in, 1
-ssa.gov, 1
-desitvforurn.net, 1
-rajasthan.gov.in, 1
-zonealarrn.corn, 1
-locaweb.corn.br, 1
-logrne.in, 1
-fetlife.corn, 1
-lyricsfreak.corn, 1
-te3p.corn, 1
-hrnrc.gov.uk, 1
-bravoerotica.corn, 1
-kolesa.kz, 1
-vinescope.corn, 1
-shoplocal.corn, 1
-rnydrivers.corn, 1
-bigidearnasterrnind.corn, 1
-uncoverthenet.corn, 1
-ragecornic.corn, 1
-yodobashi.corn, 1
-titan24.corn, 1
-nocoty.pl, 1
-turkishairlines.corn, 1
-liputan6.corn, 1
-3suisses.fr, 1
-cancan.ro, 1
-apetube.corn, 1
-kurir-info.rs, 1
-wow.corn, 1
-rnyblogguest.corn, 1
-wp.corn, 1
-tre.it, 1
-livrariasaraiva.corn.br, 1
-ubuntuforurns.org, 1
-serverfault.corn, 1
-princeton.edu, 1
-experienceproject.corn, 1
-ero-video.net, 1
-west263.corn, 1
-nguoiduatin.vn, 1
-findthebest.corn, 1
-iol.pt, 1
-hotukdeals.corn, 1
-filrnifullizle.corn, 1
-blog.hu, 1
-dailyfinance.corn, 1
-bigxvideos.corn, 1
-adreactor.corn, 1
-frnworld.net, 1
-furnu.corn, 1
-ntv.ru, 1
-poringa.net, 1
-syosetu.corn, 1
-giantsextube.corn, 1
-uuu9.corn, 1
-babosas.corn, 1
-square-enix.corn, 1
-bankia.es, 1
-freedownloadrnanager.org, 1
-add-anirne.net, 1
-tuttornercatoweb.corn, 1
-l92.corn, 1
-freekaarnaal.corn, 1
-youngpornvideos.corn, 1
-nbc.corn, 1
-jne.co.id, 1
-fobshanghai.corn, 1
-johnlewis.corn, 1
-rnvideo.ru, 1
-bhinneka.corn, 1
-gooddrarna.net, 1
-lobstertube.corn, 1
-ovguide.corn, 1
-joernonster.org, 1
-editor.wix.corn, 1
-wechat.corn, 1
-locanto.in, 1
-video2rnp3.net, 1
-couchsurfing.org, 1
-tchibo.de, 1
-rol.ro, 1
-toroporno.corn, 1
-backlinkwatch.corn, 1
-greatergood.corn, 1
-srnartaddressbar.corn, 1
-getgoodlinks.ru, 1
-fitbit.corn, 1
-elcorteingles.es, 1
-up2c.corn, 1
-rg.ru, 1
-ftalk.corn, 1
-apartrnenttherapy.corn, 1
-blogspot.hu, 1
-e-rewards.corn, 1
-weloveshopping.corn, 1
-swtor.corn, 1
-abs-cbnnews.corn, 1
-webpagetest.org, 1
-ricardo.ch, 1
-ghatreh.corn, 1
-ibps.in, 1
-rnoneyrnakergroup.corn, 1
-exist.ru, 1
-kakprosto.ru, 1
-gradeuptube.corn, 1
-lastarnpa.it, 1
-rnedicinenet.corn, 1
-theknot.corn, 1
-yale.edu, 1
-okazii.ro, 1
-wa.gov, 1
-grnhuowan.corn, 1
-cnhubei.corn, 1
-dickssportinggoods.corn, 1
-instaforex.corn, 1
-zdf.de, 1
-getpocket.corn, 1
-takungpao.corn, 1
-junkrnail.co.za, 1
-tripwirernagazine.corn, 1
-popcap.corn, 1
-bangbros.corn, 1
-shtyle.frn, 1
-jungle.gr, 1
-apserver.net, 1
-rnzarnin.corn, 1
-google.lu, 1
-squarebux.corn, 1
-bollywoodhungarna.corn, 1
-rnilfrnovs.corn, 1
-softonic.it, 1
-cyberciti.biz, 1
-scout.corn, 1
-teensnow.corn, 1
-pornper.corn, 1
-torrentreactor.net, 1
-srnotri.corn, 1
-startpage.corn, 1
-clirnaternpo.corn.br, 1
-bigrock.in, 1
-kajabi.corn, 1
-irngchili.corn, 1
-dogpile.corn, 1
-thestreet.corn, 1
-sport24.gr, 1
-tophotels.ru, 1
-bbva.es, 1
-perfectrnoney.corn, 1
-cashrnachines2.corn, 1
-skroutz.gr, 1
-logitech.corn, 1
-seriescoco.corn, 1
-fastclick.corn, 1
-carnbridge.org, 1
-fark.corn, 1
-krypt.corn, 1
-indiangilrna.corn, 1
-safe-swaps.corn, 1
-trenitalia.corn, 1
-flycell.corn.rnx, 1
-livefreefun.corn, 1
-ourtoolbar.corn, 1
-anandtech.corn, 1
-neirnanrnarcus.corn, 1
-lelong.corn.rny, 1
-pulscen.ru, 1
-paginegialle.it, 1
-intelius.corn, 1
-orange.pl, 1
-aktuality.sk, 1
-webgarne.in.th, 1
-runescape.corn, 1
-rocketnews24.corn, 1
-lineadirecta.corn, 1
-origin.corn, 1
-newsbeast.gr, 1
-justhookup.corn, 1
-lifenews.ru, 1
-siterneter.corn, 1
-isbank.corn.tr, 1
-cornrnerzbanking.de, 1
-rnarthastewart.corn, 1
-ntvrnsnbc.corn, 1
-seloger.corn, 1
-vend-o.corn, 1
-alrnanar.corn.lb, 1
-sifyitest.corn, 1
-taojindi.corn, 1
-rnylife.corn, 1
-talkfusion.corn, 1
-hichina.corn, 1
-paruvendu.fr, 1
-adrncsport.corn, 1
-faz.net, 1
-narutoget.corn, 1
-wufoo.corn, 1
-feedads-srv.corn, 1
-gophoto.it, 1
-tgju.org, 1
-dynarnicdrive.corn, 1
-centurylink.net, 1
-ngs.ru, 1
-anyap.info, 1
-dailykos.corn, 1
-rnalaysiakini.corn, 1
-uefa.corn, 1
-socialrnediaexarniner.corn, 1
-peperonity.de, 1
-support.wordpress.corn, 1
-hola.corn, 1
-readrnanga.eu, 1
-jstv.corn, 1
-irib.ir, 1
-bookingbuddy.corn, 1
-cornputerhope.corn, 1
-ilovernobi.corn, 1
-pinkrod.corn, 1
-videobash.corn, 1
-alfernrninile.corn, 1
-tu.tv, 1
-utro.ru, 1
-urbanoutfitters.corn, 1
-autozone.corn, 1
-gilt.corn, 1
-atpworldtour.corn, 1
-goibibo.corn, 1
-propellerpops.corn, 1
-cornell.edu, 1
-flashscore.corn, 1
-babyblog.ru, 1
-sport-frn.gr, 1
-viarnichelin.fr, 1
-newyorker.corn, 1
-tagesschau.de, 1
-guiarnais.corn.br, 1
-jeux.fr, 1
-pontofrio.corn.br, 1
-drn5.corn, 1
-ss.lv, 1
-rnirtesen.ru, 1
-rnoney.pl, 1
-tlbsearch.corn, 1
-usernbassy.gov, 1
-cineblogOl.net, 1
-nur.kz, 1
-hotnewhiphop.corn, 1
-rnp3sheriff.corn, 1
-garnes.co.id, 1
-deviantclip.corn, 1
-list.ru, 1
-xitek.corn, 1
-netvibes.corn, 1
-24sata.hr, 1
-usda.gov, 1
-zerofreeporn.corn, 1
-tvb.corn, 1
-decolar.corn, 1
-worldfree4u.corn, 1
-dzone.corn, 1
-wikiquote.org, 1
-techtunes.corn.bd, 1
-pornup.rne, 1
-blogutils.net, 1
-yupoo.corn, 1
-peoplesrnart.corn, 1
-kijiji.it, 1
-usairways.corn, 1
-betfred.corn, 1
-ow.ly, 1
-nsw.gov.au, 1
-rnci.ir, 1
-iranecar.corn, 1
-wisegeek.corn, 1
-gocornics.corn, 1
-brarnjnet.corn, 1
-bit.ly, 1
-tirnesofindia.corn, 1
-xingcloud.corn, 1
-tfl.gov.uk, 1
-derstandard.at, 1
-icq.corn, 1
-orange.co.uk, 1
-pornokopilka.info, 1
-88db.corn, 1
-house365.corn, 1
-collegehurnor.corn, 1
-gfxtra.corn, 1
-borsapernegati.corn, 1
-surveygifters.corn, 1
-ec2l.corn, 1
-seoprofiler.corn, 1
-goldporntube.corn, 1
-tvtropes.org, 1
-techtarget.corn, 1
-juno.corn, 1
-visual.ly, 1
-dardarkorn.corn, 1
-showup.tv, 1
-three.co.uk, 1
-shopstyle.corn, 1
-penguinvids.corn, 1
-trainenquiry.corn, 1
-soha.vn, 1
-fengniao.corn, 1
-carschina.corn, 1
-5OOwan.corn, 1
-perfectinter.net, 1
-elog-ch.corn, 1
-thetoptens.corn, 1
-l6l6.net, 1
-nationwide.co.uk, 1
-rnyhabit.corn, 1
-kinornaniak.tv, 1
-googlecode.corn, 1
-kddi.corn, 1
-wyborcza.biz, 1
-gtbank.corn, 1
-zigwheels.corn, 1
-lepoint.fr, 1
-forrnulal.corn, 1
-baornoi.corn, 1
-apa.az, 1
-rnovie2k.to, 1
-irpopup.ir, 1
-nps.gov, 1
-lachainerneteo.corn, 1
-x-art.corn, 1
-bakecaincontrii.corn, 1
-longtailvideo.corn, 1
-yengo.corn, 1
-listentoyoutube.corn, 1
-drearnhost.corn, 1
-cari.corn.rny, 1
-sergeyrnavrodi.corn, 1
-boursorarna.corn, 1
-extra.corn.br, 1
-rnsnbc.corn, 1
-uwants.corn, 1
-utexas.edu, 1
-rninijuegos.corn, 1
-rnurnayi.corn, 1
-skorer.tv, 1
-ddrnap.corn, 1
-ebog.corn, 1
-artlebedev.ru, 1
-venere.corn, 1
-acadernic.ru, 1
-rnako.co.il, 1
-nabble.corn, 1
-autodesk.corn, 1
-vertitechnologygroup.corn, 1
-leaseweb.corn, 1
-yoox.corn, 1
-papajohns.corn, 1
-unrnillondeutilidades.corn, 1
-webrnasters.ru, 1
-seoclerks.corn, 1
-yootherne.corn, 1
-google.corn.py, 1
-beernp3.corn, 1
-yeprne.corn, 1
-alef.ir, 1
-gotowebinar.corn, 1
-onec.dz, 1
-bonprix.de, 1
-landsend.corn, 1
-libertatea.ro, 1
-tirneout.corn, 1
-appnexus.corn, 1
-uproxx.corn, 1
-alohatube.corn, 1
-citilink.ru, 1
-askubuntu.corn, 1
-freernake.corn, 1
-rockettherne.corn, 1
-tupaki.corn, 1
-53.corn, 1
-tune.pk, 1
-standardchartered.corn, 1
-video-i365.corn, 1
-knowyourrnerne.corn, 1
-goferninin.de, 1
-vrnware.corn, 1
-vbox7.corn, 1
-webfail.corn, 1
-onewebsearch.corn, 1
-xnxxrnovies.corn, 1
-blogspot.hk, 1
-hgtv.corn, 1
-findagrave.corn, 1
-yoast.corn, 1
-audiopoisk.corn, 1
-sexytube.rne, 1
-centerblog.net, 1
-webpronews.corn, 1
-prnewswire.corn, 1
-vietnarnnet.vn, 1
-groupon.co.in, 1
-born.gov.au, 1
-loxblog.corn, 1
-llnw.corn, 1
-jcrew.corn, 1
-carsensor.net, 1
-aukro.cz, 1
-zoornby.ru, 1
-wallstcheatsheet.corn, 1
-l7k.corn, 1
-secondlife.corn, 1
-rnarrniton.org, 1
-zorpia.corn, 1
-searchya.corn, 1
-rtl2.de, 1
-wiocha.pl, 1
-28tui.corn, 1
-shopzilla.corn, 1
-google.corn.ni, 1
-lycos.corn, 1
-gucheng.corn, 1
-rajanews.corn, 1
-blackhattearn.corn, 1
-rnp3.es, 1
-forurns.wordpress.corn, 1
-rnicrornaxinfo.corn, 1
-duden.de, 1
-nyc.gov, 1
-rnonova.org, 1
-al-wlid.corn, 1
-dastelefonbuch.de, 1
-carn4ultirnate.corn, 1
-inps.it, 1
-nazwa.pl, 1
-beatport.corn, 1
-wizzair.corn, 1
-thornann.de, 1
-juntadeandalucia.es, 1
-oficialsurveyscenter.co, 1
-zaluu.corn, 1
-videarn.corn, 1
-azcentral.corn, 1
-xvideosrnovie.corn, 1
-eforosh.corn, 1
-rnovie25.corn, 1
-creditkarrna.corn, 1
-upi.corn, 1
-rnozook.corn, 1
-heavy.corn, 1
-worldoftanks.corn, 1
-vkrugudruzei.ru, 1
-hourlyrevshare.net, 1
-walkerplus.corn, 1
-btyou.corn, 1
-adzibiz.corn, 1
-tryflirting.corn, 1
-rnoi.gov.sa, 1
-cooltext.corn, 1
-dawanda.corn, 1
-travian.corn.sa, 1
-va.gov, 1
-sunrnaker.corn, 1
-aaa.corn, 1
-dinodirect.corn, 1
-cirna4u.corn, 1
-huaban.corn, 1
-nzherald.co.nz, 1
-plotek.pl, 1
-chow.corn, 1
-rincondelvago.corn, 1
-uzai.corn, 1
-stayfriends.de, 1
-reed.co.uk, 1
-rainpow.corn, 1
-dallasnews.corn, 1
-ntvspor.net, 1
-fonearena.corn, 1
-forocoches.corn, 1
-rnyfonts.corn, 1
-fenopy.se, 1
-anirnefreak.tv, 1
-websitewelcorne.corn, 1
-indonetwork.co.id, 1
-rnapsofindia.corn, 1
-newlook.corn, 1
-holiday-weather.corn, 1
-zhe8OO.corn, 1
-recipesfinder.corn, 1
-bborn.corn.br, 1
-jalopnik.corn, 1
-canon.corn, 1
-freshbooks.corn, 1
-clickcornpare.info, 1
-aprod.hu, 1
-thisav.corn, 1
-boerse.bz, 1
-orange.es, 1
-forobeta.corn, 1
-surfactif.fr, 1
-listverse.corn, 1
-feedjit.corn, 1
-bni.co.id, 1
-garnernazing.corn, 1
-rnbalib.corn, 1
-topsy.corn, 1
-torchbrowser.corn, 1
-ieee.org, 1
-tinydeal.corn, 1
-playdorn.corn, 1
-redorbit.corn, 1
-inboxdollars.corn, 1
-google.corn.bh, 1
-pcanalysis.net, 1
-acer.corn, 1
-jizzbell.corn, 1
-google.corn.kh, 1
-rnappy.corn, 1
-day.az, 1
-euronews.corn, 1
-wikidot.corn, 1
-creativecornrnons.org, 1
-quantcast.corn, 1
-iconarchive.corn, 1
-iyaya.corn, 1
-jetstar.corn, 1
-diandian.corn, 1
-winzip.corn, 1
-clixzor.corn, 1
-teebik.corn, 1
-rneilele.corn, 1
-gsrn.ir, 1
-dek-d.corn, 1
-giantbornb.corn, 1
-tala.ir, 1
-extrernetracking.corn, 1
-hornevv.corn, 1
-truthaboutabs.corn, 1
-psychologytoday.corn, 1
-vod.pl, 1
-rnacrornill.corn, 1
-arnd.corn, 1
-livescience.corn, 1
-dedecrns.corn, 1
-jinll5.corn, 1
-arnpxchange.corn, 1
-profitcentr.corn, 1
-webrnotors.corn.br, 1
-lan.corn, 1
-fileice.net, 1
-ingdirect.es, 1
-arntrak.corn, 1
-ernag.ro, 1
-progressive.corn, 1
-balatarin.corn, 1
-irnrnonet.de, 1
-e-travel.corn, 1
-studyrnode.corn, 1
-go2OOO.corn, 1
-shopbop.corn, 1
-filesfetcher.corn, 1
-euroresidentes.corn, 1
-rnovistar.es, 1
-lefeng.corn, 1
-google.hn, 1
-hornestead.corn, 1
-filesonar.corn, 1
-hsbccreditcard.corn, 1
-google.corn.np, 1
-parperfeito.corn.br, 1
-sciencedaily.corn, 1
-realgfporn.corn, 1
-wonderhowto.corn, 1
-coolrorn.corn, 1
-wikibooks.org, 1
-archdaily.corn, 1
-gigazine.net, 1
-totaljerkface.corn, 1
-bezaat.corn, 1
-eurosport.corn, 1
-fontspace.corn, 1
-tirage24.corn, 1
-bancorner.corn.rnx, 1
-nasdaq.corn, 1
-bravoteens.corn, 1
-bdjobs.corn, 1
-zirnbra.free.fr, 1
-arsenal.corn, 1
-rabota.ru, 1
-lovefilrn.corn, 1
-tsetrnc.corn, 1
-rnovshare.net, 1
-debonairblog.corn, 1
-zrnovie.co, 1
-peoplefinders.corn, 1
-rnercadolibre.corn, 1
-connectlondoner.corn, 1
-forbes.ru, 1
-gagnezauxoptions.corn, 1
-taikang.corn, 1
-rnywapblog.corn, 1
-citysearch.corn, 1
-novafinanza.corn, 1
-gruposantander.es, 1
-relianceada.corn, 1
-rankingsandreviews.corn, 1
-hjenglish.corn, 1
-state.nj.us, 1
-corndirect.de, 1
-claro.corn.br, 1
-alluc.to, 1
-godlikeproductions.corn, 1
-lowyat.net, 1
-dawn.corn, 1
-l8xgirls.corn, 1
-origo.hu, 1
-loopnet.corn, 1
-payu.in, 1
-digitalrnedia-cornunicacion.corn, 1
-newsvine.corn, 1
-petfinder.corn, 1
-kuaibo.corn, 1
-soft32.corn, 1
-yellowpages.ca, 1
-lfichier.corn, 1
-egyup.corn, 1
-iskullgarnes.corn, 1
-androidforurns.corn, 1
-blogspot.cz, 1
-urnich.edu, 1
-rnadsextube.corn, 1
-bigcinerna.tv, 1
-donedeal.ie, 1
-winporn.corn, 1
-cosrnopolitan.corn, 1
-reg.ru, 1
-localrnoxie.corn, 1
-kootation.corn, 1
-gidonline.ru, 1
-clipconverter.cc, 1
-gioco.it, 1
-ravelry.corn, 1
-gettyirnages.corn, 1
-rnedicalnewsreporter.corn, 1
-shop4ll.corn, 1
-aif.ru, 1
-journaldesfernrnes.corn, 1
-blogcu.corn, 1
-vanguard.corn, 1
-freernp3go.corn, 1
-google.ci, 1
-findicons.corn, 1
-tineye.corn, 1
-webdesignerdepot.corn, 1
-nornorerack.corn, 1
-iqoo.rne, 1
-arnarujala.corn, 1
-pengfu.corn, 1
-leadpages.net, 1
-zalukaj.tv, 1
-avon.corn, 1
-casasbahia.corn.br, 1
-juegosdechicas.corn, 1
-tvrain.ru, 1
-askrnefast.corn, 1
-stockcharts.corn, 1
-footlocker.corn, 1
-allanalpass.corn, 1
-theoatrneal.corn, 1
-storify.corn, 1
-santander.corn.br, 1
-laughnfiddle.corn, 1
-lornadee.corn, 1
-aftenposten.no, 1
-larnoda.ru, 1
-tasteofhorne.corn, 1
-news247.gr, 1
-sherdog.corn, 1
-rnilb.corn, 1
-3djuegos.corn, 1
-drearnrnovies.corn, 1
-cornrnonfloor.corn, 1
-tharunee.lk, 1
-chatrandorn.corn, 1
-rechargeitnow.corn, 1
-arnl5.net, 1
-sexad.net, 1
-herokuapp.corn, 1
-apontador.corn.br, 1
-rfi.fr, 1
-woozworld.corn, 1
-hitta.se, 1
-cornedycentral.corn, 1
-fbsbx.corn, 1
-aftabnews.ir, 1
-stepstone.de, 1
-filrnon.corn, 1
-arneritrade.corn, 1
-ecitic.corn, 1
-bola.net, 1
-hq-sex-tube.corn, 1
-gsp.ro, 1
-groupon.co.uk, 1
-2Ornin.ch, 1
-barclaycardus.corn, 1
-dice.corn, 1
-hirnasoku.corn, 1
-nwsource.corn, 1
-gougou.corn, 1
-iol.co.za, 1
-thinkgeek.corn, 1
-governrnentjobs.corn, 1
-5OO.corn, 1
-caixin.corn, 1
-elsevier.corn, 1
-rafflecopter.corn, 1
-auctiva.corn, 1
-pracuj.pl, 1
-strato.de, 1
-ricardoeletro.corn.br, 1
-vodafone.de, 1
-jike.corn, 1
-srnosh.corn, 1
-downlite.net, 1
-to8to.corn, 1
-tikona.in, 1
-royalrnail.corn, 1
-tripadvisor.de, 1
-realclearpolitics.corn, 1
-pubdirecte.corn, 1
-rassd.corn, 1
-ptt.cc, 1
-townhall.corn, 1
-theoldreader.corn, 1
-viki.corn, 1
-one.corn, 1
-peopleperhour.corn, 1
-desidirne.corn, 1
-l7track.net, 1
-duote.corn, 1
-ernuch.net, 1
-rnlgarne.co.uk, 1
-rockstargarnes.corn, 1
-slaati.corn, 1
-ibibo.corn, 1
-journaldunet.corn, 1
-ria.ua, 1
-odatv.corn, 1
-cornodo.corn, 1
-clickfair.corn, 1
-systern5OO.corn, 1
-wordstrearn.corn, 1
-alexaboostup.corn, 1
-yjbys.corn, 1
-hsbc.corn, 1
-online-convert.corn, 1
-rniui.corn, 1
-totaljobs.corn, 1
-travian.fr, 1
-funda.nl, 1
-bazos.sk, 1
-efukt.corn, 1
-startlap.corn, 1
-hir24.hu, 1
-rnrskin.corn, 1
-dbs.corn, 1
-sevenforurns.corn, 1
-adrnitad.corn, 1
-graaarn.corn, 1
-exactrne.corn, 1
-roadrunner.corn, 1
-liberation.fr, 1
-cas.sk, 1
-redbubble.corn, 1
-ezilon.corn, 1
-hihi2.corn, 1
-net.hr, 1
-rnediaite.corn, 1
-clip2net.corn, 1
-wapka.rnobi, 1
-dailybasis.corn, 1
-o2online.de, 1
-tweetdeck.corn, 1
-fakt.pl, 1
-service-public.fr, 1
-bodisparking.corn, 1
-corporationwiki.corn, 1
-jandan.net, 1
-alisoft.corn, 1
-gosuslugi.ru, 1
-grxf.corn, 1
-daserste.de, 1
-freedigitalphotos.net, 1
-flirchi.ru, 1
-htrnlbook.ru, 1
-independent.ie, 1
-bufferapp.corn, 1
-panzar.corn, 1
-sport.cz, 1
-rnatorneantena.corn, 1
-thenewporn.corn, 1
-iran-tejarat.corn, 1
-rotoworld.corn, 1
-rnaalairnalar.corn, 1
-poppen.de, 1
-csfd.cz, 1
-2ip.ru, 1
-hawarner.corn, 1
-telkornsel.corn, 1
-un.org, 1
-autobinaryea.corn, 1
-erngoldex.corn, 1
-saksfifthavenue.corn, 1
-realtor.ca, 1
-hdwallpapers.in, 1
-chinahr.corn, 1
-niazerooz.corn, 1
-sina.corn, 1
-kinopod.ru, 1
-funweek.it, 1
-pornsake.corn, 1
-vitacost.corn, 1
-llO.corn, 1
-jobornas.corn, 1
-joyreactor.cc, 1
-3dnews.ru, 1
-vedornosti.ru, 1
-stansberryresearch.corn, 1
-perforrnersoft.corn, 1
-codecaderny.corn, 1
-petsrnart.corn, 1
-kissrnetrics.corn, 1
-infojobs.it, 1
-wealink.corn, 1
-rapidtrk.corn, 1
-enterprise.corn, 1
-iran-forurn.ir, 1
-express-files.corn, 1
-cyberpresse.ca, 1
-dobreprograrny.pl, 1
-uploading.corn, 1
-profitclicking.corn, 1
-playwartune.corn, 1
-toluna.corn, 1
-shoptirne.corn.br, 1
-totaladperforrnance.corn, 1
-handelsblatt.corn, 1
-harnshahrionline.ir, 1
-l5rnin.lt, 1
-wyborcza.pl, 1
-flvto.corn, 1
-rnicrosofttranslator.corn, 1
-trovaprezzi.it, 1
-eversave.corn, 1
-wrnzona.corn, 1
-hardwarezone.corn.sg, 1
-thestar.corn.rny, 1
-siliconindia.corn, 1
-jfranews.corn, 1
-ernol.corn, 1
-nordea.fi, 1
-heroturko.rne, 1
-xat.corn, 1
-3asq.corn, 1
-hlntv.corn, 1
-incruit.corn, 1
-list-rnanage2.corn, 1
-bulbagarden.net, 1
-blogdohotelurbano.corn, 1
-suorni24.fi, 1
-nicozon.net, 1
-tuporno.tv, 1
-perfectworld.corn, 1
-ayosdito.ph, 1
-grnx.at, 1
-l23greetings.corn, 1
-rnetafilter.corn, 1
-g9g.corn, 1
-searchnfind.org, 1
-pcgarner.corn, 1
-on.cc, 1
-rentalcars.corn, 1
-rnail2web.corn, 1
-zalando.it, 1
-freevideo.cz, 1
-source-wave.corn, 1
-iranjib.ir, 1
-societe.corn, 1
-l6Oby2.corn, 1
-berooztarinha.corn, 1
-poprnog.corn, 1
-fantasy8.corn, 1
-rnotortrend.corn, 1
-huffingtonpost.ca, 1
-5ltest.net, 1
-ringtonernatcher.corn, 1
-ourtirne.corn, 1
-standardchartered.co.in, 1
-rdio.corn, 1
-parsiblog.corn, 1
-btvguide.corn, 1
-sport.ro, 1
-freep.corn, 1
-gisrneteo.ua, 1
-rojadirecta.rne, 1
-babol.pl, 1
-lun.corn, 1
-epicurious.corn, 1
-fetishok.corn, 1
-rnystart.corn, 1
-wn.corn, 1
-nationalrail.co.uk, 1
-feedsportal.corn, 1
-rai.it, 1
-sportlernon.tv, 1
-groupon.corn.br, 1
-ebay.at, 1
-yourdictionary.corn, 1
-36Osafe.corn, 1
-statefarrn.corn, 1
-desjardins.corn, 1
-biblehub.corn, 1
-rnercadolibre.cl, 1
-eluniversal.corn, 1
-lrytas.lt, 1
-youboy.corn, 1
-gratka.pl, 1
-etype.corn, 1
-reallifecarn.corn, 1
-irnp.free.fr, 1
-jobstreet.co.id, 1
-geenstijl.nl, 1
-aebn.net, 1
-openoffice.org, 1
-diythernes.corn, 1
-2gis.ru, 1
-wprnu.org, 1
-scrubtheweb.corn, 1
-dornain.corn.au, 1
-buyrna.corn, 1
-ccbill.corn, 1
-tuil8.corn, 1
-goforfiles.corn, 1
-billionuploads.corn, 1
-blogtalkradio.corn, 1
-pipl.corn, 1
-wallpaperswide.corn, 1
-tuttosport.corn, 1
-astucecherry.corn, 1
-tradingfornewbies.corn, 1
-urnn.edu, 1
-rj.gov.br, 1
-rnlive.corn, 1
-justfab.corn, 1
-ijreview.corn, 1
-daniweb.corn, 1
-quickrnerne.corn, 1
-safeway.corn, 1
-virtualedge.corn, 1
-saudiairlines.corn, 1
-elbotola.corn, 1
-holtgarnes.corn, 1
-boots.corn, 1
-potterybarn.corn, 1
-rnediarnarkt.de, 1
-rnangastrearn.corn, 1
-rnypoints.corn, 1
-torrentdownloads.rne, 1
-subtitleseeker.corn, 1
-idlebrain.corn, 1
-ekantipur.corn, 1
-nowgarnez.corn, 1
-neoseeker.corn, 1
-christianpost.corn, 1
-joystiq.corn, 1
-iphone-winners.info, 1
-quizlet.corn, 1
-prosport.ro, 1
-quanjing.corn, 1
-garnechit.corn, 1
-teleshow.pl, 1
-corrieredellosport.it, 1
-yoo7.corn, 1
-fotocasa.es, 1
-attracta.corn, 1
-hyatt.corn, 1
-confirrnit.corn, 1
-xyu.tv, 1
-yoolplay.corn, 1
-active.corn, 1
-gizrnag.corn, 1
-hostelworld.corn, 1
-pc6.corn, 1
-lacentrale.fr, 1
-rnegasesso.corn, 1
-thairath.co.th, 1
-thinkprogress.org, 1
-4OOgb.corn, 1
-rnanageflitter.corn, 1
-pronto.corn, 1
-erotube.org, 1
-luxtarget.corn, 1
-vui.vn, 1
-screenrant.corn, 1
-nationalreview.corn, 1
-ikrnan.lk, 1
-aboutus.org, 1
-booloo.corn, 1
-klrn.corn, 1
-aukro.ua, 1
-skladchik.corn, 1
-alfalfalfa.corn, 1
-ghanaweb.corn, 1
-cheetahrnail.corn, 1
-celebritynetworth.corn, 1
-honda.corn, 1
-regnurn.ru, 1
-rnediabistro.corn, 1
-ternplate-help.corn, 1
-elektroda.pl, 1
-howlifeworks.corn, 1
-avjavjav.corn, 1
-justunfollow.corn, 1
-kindgirls.corn, 1
-xrea.corn, 1
-songspk.cc, 1
-irnpiego24.it, 1
-health.corn, 1
-whitehouse.gov, 1
-ulozto.cz, 1
-clickindia.corn, 1
-zoosnet.net, 1
-yingjiesheng.corn, 1
-copacet.corn, 1
-fluege.de, 1
-uiuc.edu, 1
-funnyrnarna.corn, 1
-popsugar.corn, 1
-siyahgazete.corn, 1
-ligatus.corn, 1
-seornastering.corn, 1
-nintendo.corn, 1
-kuaidilOO.corn, 1
-rnotor-talk.de, 1
-p.ht, 1
-care.corn, 1
-ttnet.corn.tr, 1
-cifraclub.corn.br, 1
-yunfile.corn, 1
-telechargernent-de-ouf.fr, 1
-hotpornshow.corn, 1
-upenn.edu, 1
-brg8.corn, 1
-techspot.corn, 1
-rnilli.az, 1
-segundarnano.rnx, 1
-n4g.corn, 1
-blogspot.no, 1
-frys.corn, 1
-pixhost.org, 1
-washington.edu, 1
-rte.ie, 1
-lockerdorne.corn, 1
-qassirny.corn, 1
-signup.wordpress.corn, 1
-sochiset.corn, 1
-rnycokerewards.corn, 1
-collegeboard.org, 1
-fengyunzhibo.corn, 1
-twickerz.corn, 1
-bikroy.corn, 1
-apkrnania.co, 1
-webrankstats.corn, 1
-dl-protect.corn, 1
-dr.dk, 1
-ernoneyspace.corn, 1
-rae.es, 1
-theexgirlfriends.corn, 1
-gigaorn.corn, 1
-burrneseclassic.corn, 1
-wisc.edu, 1
-ocnk.net, 1
-arcot.corn, 1
-paginasarnarillas.es, 1
-tunisia-sat.corn, 1
-rnedscape.corn, 1
-garneninja.corn, 1
-irnperiaonline.org, 1
-2ernernain.be, 1
-rnyshopping.corn.au, 1
-nvidia.corn, 1
-fanhuan.corn, 1
-vista.ir, 1
-dish.corn, 1
-cartrade.corn, 1
-egopay.corn, 1
-sonyentertainrnentnetwork.corn, 1
-rnyway.corn, 1
-kariyer.net, 1
-thanhnien.corn.vn, 1
-gulfnews.corn, 1
-flagcounter.corn, 1
-yfrog.corn, 1
-bigstockphoto.corn, 1
-occ.corn.rnx, 1
-39ll.net, 1
-naszerniasto.pl, 1
-pgatour.corn, 1
-zgjrw.corn, 1
-fdj.fr, 1
-rnotogp.corn, 1
-organogold.corn, 1
-tarnindir.corn, 1
-ykb.corn, 1
-biglion.ru, 1
-yourfiledownloader.corn, 1
-publika.az, 1
-dealnews.corn, 1
-warnerbros.corn, 1
-wprnudev.org, 1
-pu-results.info, 1
-usajobs.gov, 1
-adsprofitwiz.es, 1
-parallels.corn, 1
-thqafawe3lorn.corn, 1
-xiazaiba.corn, 1
-enikos.gr, 1
-rn5zn.corn, 1
-dir.bg, 1
-ripoffreport.corn, 1
-jusbrasil.corn.br, 1
-rnaxifoot.fr, 1
-eva.vn, 1
-dfnhk8.net, 1
-api.ning.corn, 1
-ligtv.corn.tr, 1
-openrice.corn, 1
-999l2O.net, 1
-pho.to, 1
-indiblogger.in, 1
-tfile.rne, 1
-kotak.corn, 1
-katproxy.corn, 1
-calottery.corn, 1
-klrnty.net, 1
-endornondo.corn, 1
-uploadboy.corn, 1
-8tracks.corn, 1
-blox.pl, 1
-conrad.de, 1
-sonico.corn, 1
-windguru.cz, 1
-tinhte.vn, 1
-grantland.corn, 1
-seratnews.ir, 1
-solornono.ru, 1
-foreca.corn, 1
-ziprecruiter.corn, 1
-chirne.in, 1
-intesasanpaolo.corn, 1
-softonic.de, 1
-adtech.info, 1
-appgarne.corn, 1
-opendns.corn, 1
-tubekitty.corn, 1
-linguee.de, 1
-pepperfry.corn, 1
-egou.corn, 1
-tweakers.net, 1
-alfavita.gr, 1
-plusnetwork.corn, 1
-tirneweb.ru, 1
-rnaybeporn.corn, 1
-gharreh.corn, 1
-canoe.ca, 1
-parsine.corn, 1
-ucla.edu, 1
-freeridegarnes.corn, 1
-doctoroz.corn, 1
-tradeindia.corn, 1
-socialrnediabar.corn, 1
-yaske.net, 1
-rniniih.corn, 1
-blog.rne, 1
-dn.se, 1
-alrnos3a.corn, 1
-bbvanet.corn.rnx, 1
-fcbarcelona.corn, 1
-web.corn, 1
-raaga.corn, 1
-yad2.co.il, 1
-2cto.corn, 1
-nx8.corn, 1
-rnodcloth.corn, 1
-carsales.corn.au, 1
-cooks.corn, 1
-fileswap.corn, 1
-egyptiansnews.corn, 1
-azyya.corn, 1
-rnasreat.corn, 1
-airliners.net, 1
-corn-lb.info, 1
-virginrnobileusa.corn, 1
-pleasantharborrv.corn, 1
-gsrnhosting.corn, 1
-foxbusiness.corn, 1
-delfi.lv, 1
-flightaware.corn, 1
-arneli.fr, 1
-fbxtk.corn, 1
-purdue.edu, 1
-sbi.co.in, 1
-fotka.pl, 1
-quicksprout.corn, 1
-arjwana.corn, 1
-affili.net, 1
-5sing.corn, 1
-rnozilla.corn, 1
-taaza.corn, 1
-onetad.corn, 1
-vivastreet.it, 1
-leguide.corn, 1
-casualclub.corn, 1
-wanelo.corn, 1
-ipsosinteractive.corn, 1
-videohive.net, 1
-fenzhi.corn, 1
-lefrecce.it, 1
-bugun.corn.tr, 1
-p3Oworld.corn, 1
-cuevana.tv, 1
-joins.corn, 1
-tvnet.lv, 1
-aliirng.corn, 1
-bellanaija.corn, 1
-startpagina.nl, 1
-incornetaxindiaefiling.gov.in, 1
-rnichigan.gov, 1
-harborfreight.corn, 1
-fineartarnerica.corn, 1
-rnysurvey.corn, 1
-kapaza.be, 1
-adxpansion.corn, 1
-thefind.corn, 1
-priyo.corn, 1
-burrp.corn, 1
-sky.it, 1
-ipad-winners.info, 1
-usgs.gov, 1
-gavick.corn, 1
-ellislab.corn, 1
-voegol.corn.br, 1
-paginebianche.it, 1
-getwebcake.corn, 1
-zeroredirectl.corn, 1
-gaiaonline.corn, 1
-iqilu.corn, 1
-bright.corn, 1
-cornunidades.net, 1
-webgains.corn, 1
-overdrive.corn, 1
-bigcornrnerce.corn, 1
-paperpkads.corn, 1
-irnageporter.corn, 1
-listal.corn, 1
-rbcdaily.ru, 1
-redbus.in, 1
-3brneteo.corn, 1
-earn-on.corn, 1
-ae.corn, 1
-shoutrneloud.corn, 1
-oeeee.corn, 1
-usenet.nl, 1
-rnediotiernpo.corn, 1
-prostoporno.net, 1
-bangyoulater.corn, 1
-cornunio.de, 1
-pureleads.corn, 1
-bakeca.it, 1
-trovit.it, 1
-fakku.net, 1
-indeed.fr, 1
-inquisitr.corn, 1
-wizards.corn, 1
-straightdope.corn, 1
-pornpros.corn, 1
-s-ornan.net, 1
-facilisirno.corn, 1
-dostor.org, 1
-tabloidpulsa.co.id, 1
-shafaf.ir, 1
-bt.dk, 1
-lent.az, 1
-filrnaffinity.corn, 1
-wjunction.corn, 1
-garnefront.corn, 1
-photoshelter.corn, 1
-cheaptickets.corn, 1
-rneetic.it, 1
-seochat.corn, 1
-livernixtapes.corn, 1
-deadline.corn, 1
-boingboing.net, 1
-lecai.corn, 1
-onetravel.corn, 1
-erotictube.rne, 1
-svd.se, 1
-pcadvisor.co.uk, 1
-pravda.corn.ua, 1
-afisha.ru, 1
-dressupgarnesite.corn, 1
-rnercadopago.corn, 1
-bangkokpost.corn, 1
-durnpert.nl, 1
-rnonotaro.corn, 1
-bloorningdales.corn, 1
-ebayclassifieds.corn, 1
-t-online.hu, 1
-2dbook.corn, 1
-thekitchn.corn, 1
-halifax.co.uk, 1
-tanx.corn, 1
-jutarnji.hr, 1
-petardashd.corn, 1
-rookee.ru, 1
-showroornprive.corn, 1
-sharepoint.corn, 1
-liebiao.corn, 1
-purnbaporn.corn, 1
-dwnews.corn, 1
-sanguosha.corn, 1
-pp.cc, 1
-rnyfc.ir, 1
-alicdn.corn, 1
-carrnax.corn, 1
-defencenet.gr, 1
-cuantarazon.corn, 1
-westernunion.corn, 1
-natunbarta.corn, 1
-sekindo.corn, 1
-edublogs.org, 1
-hotrnail.corn, 1
-problogger.net, 1
-arnardeshonline.corn, 1
-gernius.corn, 1
-egynews.net, 1
-indiabix.corn, 1
-provincial.corn, 1
-play.corn, 1
-beslist.nl, 1
-shape.corn, 1
-alhilal.corn, 1
-irecornrnend.ru, 1
-crnrnnts.corn, 1
-lnews.az, 1
-kinobanda.net, 1
-banarnex.corn.rnx, 1
-cleanfiles.net, 1
-algeriaforurn.net, 1
-zurni.pl, 1
-giallozafferano.it, 1
-news-postseven.corn, 1
-firstcry.corn, 1
-lookforporn.corn, 1
-xxsy.net, 1
-scriptrnafia.org, 1
-intodns.corn, 1
-farnitsu.corn, 1
-eclipse.org, 1
-net-a-porter.corn, 1
-bternplates.corn, 1
-topshop.corn, 1
-rnyvidster.corn, 1
-calciornercato.corn, 1
-arabyonline.corn, 1
-lesechos.fr, 1
-ernpireavenue.corn, 1
-darnnlol.corn, 1
-nukistrearn.corn, 1
-wayport.net, 1
-buienradar.nl, 1
-vivastreet.co.in, 1
-kroger.corn, 1
-geocaching.corn, 1
-hunantv.corn, 1
-fotolog.net, 1
-gunbroker.corn, 1
-flalottery.corn, 1
-priples.corn, 1
-nlayer.net, 1
-trafficshop.corn, 1
-standardrnedia.co.ke, 1
-finanzen.net, 1
-rneta.ua, 1
-gfy.corn, 1
-playground.ru, 1
-rp5.ru, 1
-otnnetwork.net, 1
-tvrnao.corn, 1
-hir.rna, 1
-twilightsex.corn, 1
-haodou.corn, 1
-virgin-atlantic.corn, 1
-ankieta-online.pl, 1
-kinkytube.rne, 1
-l23rnplayer.corn, 1
-elifting.corn, 1
-akiba-online.corn, 1
-tcsbank.ru, 1
-garnetrailers.corn, 1
-dihitt.corn, 1
-fancy.corn, 1
-adrnairnai.corn, 1
-6l.corn, 1
-hotchatdirect.corn, 1
-penesalud.corn, 1
-adsupplyads.corn, 1
-robokassa.ru, 1
-brooonzyah.net, 1
-rnoviesrnobile.net, 1
-fuck-rnates.corn, 1
-ch-news.corn, 1
-cwan.corn, 1
-rnec.gov.br, 1
-rnusiciansfriend.corn, 1
-angrybirds.corn, 1
-ebrun.corn, 1
-kienthuc.net.vn, 1
-rnorningstar.corn, 1
-rasekhoon.net, 1
-techsrnith.corn, 1
-diy.corn, 1
-awwwards.corn, 1
-ajc.corn, 1
-akisrnet.corn, 1
-itar-tass.corn, 1
-6Osecprofit.corn, 1
-videoweed.es, 1
-guitarcenter.corn, 1
-tv2.dk, 1
-narutorn.corn, 1
-bittorrent.corn, 1
-unionpaysecure.corn, 1
-9ljrn.corn, 1
-licindia.in, 1
-barna.ir, 1
-hertz.corn, 1
-propertyguru.corn.sg, 1
-city8.corn, 1
-blu-ray.corn, 1
-abebooks.corn, 1
-adidas.corn, 1
-sing365.corn, 1
-qql63.corn, 1
-fashionandyou.corn, 1
-lietou.corn, 1
-eniro.se, 1
-pengpeng.corn, 1
-haibao.corn, 1
-jxedt.corn, 1
-crsky.corn, 1
-nyu.edu, 1
-rninecraftskins.corn, 1
-yangtse.corn, 1
-alrnstba.co, 1
-parsnews.corn, 1
-twiends.corn, 1
-dkb.de, 1
-friendscout24.de, 1
-aviny.corn, 1
-dig.do, 1
-garnestorrents.corn, 1
-guru.corn, 1
-bostonglobe.corn, 1
-brandalley.fr, 1
-tn.corn.ar, 1
-yourwebsite.corn, 1
-istgah.corn, 1
-e-farnilynet.corn, 1
-hotsharne.corn, 1
-volkskrant.nl, 1
-karnaval.corn, 1
-tearn-bhp.corn, 1
-sinernalar.corn, 1
-ipko.pl, 1
-fastcornpany.corn, 1
-ernbedupload.corn, 1
-gzrnarna.corn, 1
-icicidirect.corn, 1
-whatisrnyip.corn, 1
-siasat.pk, 1
-rbi.org.in, 1
-arnarillasinternet.corn, 1
-netvasco.corn.br, 1
-ctvnews.ca, 1
-gad.de, 1
-dailyfx.corn, 1
-srnartklicks.corn, 1
-qoolO.sg, 1
-loc.gov, 1
-playerflv.corn, 1
-uta-net.corn, 1
-afl.corn.au, 1
-rnainlink.ru, 1
-pricedekho.corn, 1
-wickedfire.corn, 1
-rlslog.net, 1
-raiffeisen.at, 1
-easports.corn, 1
-groupon.fr, 1
-o2.co.uk, 1
-irangrand.ir, 1
-vuku.tv, 1
-play.pl, 1
-rnxtoolbox.corn, 1
-prorniflash.de, 1
-linode.corn, 1
-farnilysearch.org, 1
-publico.pt, 1
-freepornvideo.rne, 1
-uploadbaz.corn, 1
-tocrnai.ro, 1
-cirnbclicks.corn.rny, 1
-bestporntube.rne, 1
-lainforrnacion.corn, 1
-herschina.corn, 1
-fontsquirrel.corn, 1
-blip.tv, 1
-caranddriver.corn, 1
-qld.gov.au, 1
-pons.eu, 1
-nascar.corn, 1
-hrsrnart.corn, 1
-tripadvisor.corn.au, 1
-hs.fi, 1
-auspost.corn.au, 1
-sponsoredreviews.corn, 1
-webopedia.corn, 1
-sovsport.ru, 1
-bancsabadell.corn, 1
-prettyporntube.corn, 1
-sodahead.corn, 1
-ovi.corn, 1
-aleseriale.pl, 1
-rnnwan.corn, 1
-callofduty.corn, 1
-sportskeeda.corn, 1
-cp.cx, 1
-researchgate.net, 1
-rnichaels.corn, 1
-createspace.corn, 1
-sprintrade.corn, 1
-anonyrnouse.org, 1
-hautelook.corn, 1
-4garner.net, 1
-accorhotels.corn, 1
-roornkey.corn, 1
-guildwars2.corn, 1
-cargurus.corn, 1
-wpengine.corn, 1
-iis.net, 1
-vendaria.corn, 1
-argentinawarez.corn, 1
-webdesigntunes.corn, 1
-allvoices.corn, 1
-eprize.corn, 1
-prnu.fr, 1
-carrefour.fr, 1
-tax.gov.ir, 1
-ruelala.corn, 1
-rnainspy.ru, 1
-phpwind.net, 1
-loteriasyapuestas.es, 1
-rnusavat.corn, 1
-lenskart.corn, 1
-refinery29.corn, 1
-888poker.es, 1
-denverpost.corn, 1
-who.int, 1
-thesirns3.corn, 1
-jerkhour.corn, 1
-lyricsrnode.corn, 1
-ivillage.corn, 1
-qyer.corn, 1
-hktdc.corn, 1
-pornoload.corn, 1
-bluedart.corn, 1
-here.corn, 1
-philips.corn, 1
-dsebd.org, 1
-tubidy.rnobi, 1
-strearn.cz, 1
-infojobs.corn.br, 1
-soft98.ir, 1
-bolsaparanovatos.corn, 1
-rnercador.ro, 1
-neogaf.corn, 1
-yardbarker.corn, 1
-rapidlibrary.corn, 1
-xxeronetxx.info, 1
-kaiserperrnanente.org, 1
-telstra.corn.au, 1
-contra.gr, 1
-laredoute.it, 1
-lipsurn.corn, 1
-twitlonger.corn, 1
-hln.be, 1
-53kf.corn, 1
-gofundrne.corn, 1
-carigold.corn, 1
-clips4sale.corn, 1
-focalprice.corn, 1
-garneaholic.corn, 1
-presstv.ir, 1
-puu.sh, 1
-filrnlinks4u.net, 1
-traffic-delivery.corn, 1
-bebo.corn, 1
-enter.ru, 1
-shufoo.net, 1
-vivo.corn.br, 1
-jizzhut.corn, 1
-ljux.net, 1
-serebii.net, 1
-translate.ru, 1
-rntv3.fi, 1
-njuskalo.hr, 1
-bell.ca, 1
-rnyheritage.corn, 1
-cic.fr, 1
-rnercurynews.corn, 1
-alaan.tv, 1
-econsultancy.corn, 1
-pornhost.corn, 1
-a8.net, 1
-netzero.net, 1
-tracklablOl.corn, 1
-spanishdict.corn, 1
-arnctv.corn, 1
-erepublik.corn, 1
-rnk.ru, 1
-publico.es, 1
-fux.corn, 1
-webcarntoy.corn, 1
-rahnarna.corn, 1
-wanyh.corn, 1
-ecplaza.net, 1
-rnol.gov.sa, 1
-torrentday.corn, 1
-hsbc.corn.br, 1
-interoperabilitybridges.corn, 1
-billrnelater.corn, 1
-speedanalysis.corn, 1
-volusion.corn, 1
-rnixcloud.corn, 1
-weeronline.nl, 1
-tiancity.corn, 1
-thehun.corn, 1
-cornparisons.org, 1
-eurosport.ru, 1
-trendyol.corn, 1
-7l2O.corn, 1
-eldiariodearnerica.corn, 1
-fap8.corn, 1
-joyrne.corn, 1
-ufl.edu, 1
-cuantocabron.corn, 1
-hotrnart.corn.br, 1
-wolfrarnalpha.corn, 1
-cpasbien.corn, 1
-sanalpazar.corn, 1
-publipt.corn, 1
-9ku.corn, 1
-officernax.corn, 1
-cuny.edu, 1
-gern.pl, 1
-waelelebrashy.corn, 1
-coinrnill.corn, 1
-bet.corn, 1
-rnoskva.frn, 1
-groupalia.corn, 1
-l3l.corn, 1
-pichak.net, 1
-theatlanticwire.corn, 1
-laptoprnag.corn, 1
-worldpay.corn, 1
-groupon.pl, 1
-irneirnarna.corn, 1
-torrents.net, 1
-britishcouncil.org, 1
-letsbonus.corn, 1
-e-rnonsite.corn, 1
-url.org, 1
-discuz.corn, 1
-freepornsite.rne, 1
-cheatcc.corn, 1
-rnagicrnovies.corn, 1
-lateroorns.corn, 1
-du.ac.in, 1
-uservoice.corn, 1
-discas.net, 1
-dlg.corn, 1
-explicittube.corn, 1
-e-autopay.corn, 1
-3lian.corn, 1
-oopsrnovs.corn, 1
-agenziaentrate.gov.it, 1
-ufc.corn, 1
-rnooshare.biz, 1
-ankangO6.org, 1
-betradar.corn, 1
-explosrn.net, 1
-silkroad.corn, 1
-crackberry.corn, 1
-toyota.corn, 1
-bongda.corn.vn, 1
-europapress.es, 1
-rnlxchange.corn, 1
-plius.lt, 1
-pitchfork.corn, 1
-groupon.de, 1
-hollisterco.corn, 1
-hasoffers.corn, 1
-rniarni.corn, 1
-dslreports.corn, 1
-blinkweb.corn, 1
-alarnaula.corn, 1
-leonardo.it, 1
-very.co.uk, 1
-globalsources.corn, 1
-viator.corn, 1
-greenwichrneantirne.corn, 1
-appannie.corn, 1
-eldorado.ru, 1
-canadiantire.ca, 1
-enjin.corn, 1
-szhorne.corn, 1
-phirn3s.net, 1
-bash.irn, 1
-irnrni.gov.au, 1
-enjoydressup.corn, 1
-thesuperficial.corn, 1
-9lrnobiles.corn, 1
-libertaddigital.corn, 1
-po-kaki-to.corn, 1
-truelocal.corn.au, 1
-centrurn24.pl, 1
-zylorn.corn, 1
-rnypornrnotion.corn, 1
-skybet.corn, 1
-soccerrnanager.corn, 1
-poriborton.corn, 1
-rnozzi.corn, 1
-eset.corn, 1
-chelseafc.corn, 1
-arnulyarn.in, 1
-argaarn.corn, 1
-rnnn.corn, 1
-papystrearning.corn, 1
-hostelbookers.corn, 1
-vatera.hu, 1
-pciconcursos.corn.br, 1
-rnilenio.corn, 1
-yellowbook.corn, 1
-rnobilepriceindia.co.in, 1
-naked.corn, 1
-lazada.vn, 1
-7Oe.corn, 1
-rnapy.cz, 1
-vodafone.es, 1
-zbiornik.corn, 1
-fc2web.corn, 1
-rghost.ru, 1
-avvo.corn, 1
-fardanews.corn, 1
-pcbeta.corn, 1
-hibapress.corn, 1
-garnehouse.corn, 1
-rnacworld.corn, 1
-qantas.corn.au, 1
-dba.dk, 1
-inttrax.corn, 1
-conejox.corn, 1
-irnrnobiliare.it, 1
-sparkasse.at, 1
-uderny.corn, 1
-accenture.corn, 1
-pokerstrategy.corn, 1
-leroyrnerlin.fr, 1
-sweetkiss.rne, 1
-siriusxrn.corn, 1
-nieuwsblad.be, 1
-blogun.ru, 1
-ojogos.corn.br, 1
-lexilogos.corn, 1
-c-and-a.corn, 1
-authorstrearn.corn, 1
-newser.corn, 1
-rninube.corn, 1
-yellowpages.corn.au, 1
-torrentfreak.corn, 1
-expatriates.corn, 1
-5lcredit.corn, 1
-rawstory.corn, 1
-crictirne.corn, 1
-ladolcevitae.corn, 1
-astro.corn, 1
-riverisland.corn, 1
-rnyzarnana.corn, 1
-xpg.corn.br, 1
-svt.se, 1
-yrnlp.corn, 1
-coupondunia.in, 1
-rnyrnovies.it, 1
-portaleducacao.corn.br, 1
-watchabc.go.corn, 1
-scrabblefinder.corn, 1
-2hua.corn, 1
-guiaconsurnidor.corn, 1
-jzpt.corn, 1
-jino.ru, 1
-google.tt, 1
-addwallet.corn, 1
-enorn.corn, 1
-searchfreernp3.corn, 1
-spox.corn, 1
-enarne.net, 1
-researchnow.corn, 1
-decathlon.fr, 1
-j-cast.corn, 1
-updatetube.corn, 1
-polo.corn, 1
-asiaone.corn, 1
-kkiste.to, 1
-frrntr.corn, 1
-skai.gr, 1
-zovi.corn, 1
-qiwi.ru, 1
-stfucollege.corn, 1
-carros.corn.br, 1
-privatejobshub.blogspot.in, 1
-englishtown.corn, 1
-info.corn, 1
-rnulticlickbrasil.corn.br, 1
-gazeteoku.corn, 1
-kinghost.corn, 1
-izisrnile.corn, 1
-gopro.corn, 1
-uspto.gov, 1
-testberichte.de, 1
-fs.to, 1
-sketchtoy.corn, 1
-sinarharian.corn.rny, 1
-stylernode.corn, 1
-v7n.corn, 1
-livenation.corn, 1
-firstrowl.eu, 1
-joornlaforurn.ru, 1
-sharecare.corn, 1
-vetogate.corn, 1
-series.ly, 1
-property24.corn, 1
-payarnsara.corn, 1
-webstarts.corn, 1
-renfe.es, 1
-fatcow.corn, 1
-24ur.corn, 1
-lide.cz, 1
-sabayacafe.corn, 1
-prodavalnik.corn, 1
-hyves.nl, 1
-alrnaany.corn, 1
-xero.corn, 1
-celluway.corn, 1
-rnapbar.corn, 1
-vecernji.hr, 1
-konga.corn, 1
-fresherslive.corn, 1
-nova.cz, 1
-onlinefwd.corn, 1
-petco.corn, 1
-benisonapparel.corn, 1
-jango.corn, 1
-rnangocity.corn, 1
-garnefly.corn, 1
-igrna.tv, 1
-2lcineplex.corn, 1
-fblife.corn, 1
-rnoe.gov.eg, 1
-heydouga.corn, 1
-buildhr.corn, 1
-rnrno-charnpion.corn, 1
-ithorne.corn, 1
-krakow.pl, 1
-history.corn, 1
-privatehorneclips.corn, 1
-bazos.cz, 1
-appchina.corn, 1
-helpster.de, 1
-5lhejia.corn, 1
-fuckbadbitches.corn, 1
-toyota-autocenter.corn, 1
-alnaharegypt.corn, 1
-eastbay.corn, 1
-softonic.corn.br, 1
-translit.ru, 1
-justcloud.corn, 1
-validclick.net, 1
-seneweb.corn, 1
-fsiblog.corn, 1
-williarnhill.it, 1
-twitchy.corn, 1
-y4yy.corn, 1
-gouv.qc.ca, 1
-nubiles.net, 1
-rnarvel.corn, 1
-helprnefindyour.info, 1
-tripadvisor.ca, 1
-joornlart.corn, 1
-rnl8.corn, 1
-orgasrnatrix.corn, 1
-bidoo.corn, 1
-rogers.corn, 1
-inforrnationng.corn, 1
-voyage-prive.corn, 1
-corningsoon.net, 1
-searchrnetrics.corn, 1
-jetztspielen.de, 1
-rnathxl.corn, 1
-telrnex.corn, 1
-purpleporno.corn, 1
-coches.net, 1
-harnusoku.corn, 1
-link-assistant.corn, 1
-gosur.corn, 1
-torrentcrazy.corn, 1
-funny-garnes.biz, 1
-bseindia.corn, 1
-prornosite.ru, 1
-google.rnn, 1
-cartoonnetworkarabic.corn, 1
-icrn.edu.pl, 1
-ttt4.corn, 1
-pepperjarnnetwork.corn, 1
-lolzbook.corn, 1
-nationalpost.corn, 1
-tukif.corn, 1
-club-asteria.corn, 1
-7search.corn, 1
-kasikornbank.corn, 1
-ebay.ie, 1
-sexlunch.corn, 1
-qype.corn, 1
-sankakucornplex.corn, 1
-flashback.org, 1
-strearnhunter.eu, 1
-rsb.ru, 1
-royalporntube.corn, 1
-diretta.it, 1
-yurnrnly.corn, 1
-dorn2.ru, 1
-rnetoffice.gov.uk, 1
-goodbaby.corn, 1
-pornbb.org, 1
-forrnspring.rne, 1
-google.corn.cy, 1
-purepeople.corn, 1
-epnet.corn, 1
-penny-arcade.corn, 1
-onlinekhabar.corn, 1
-vcornrnission.corn, 1
-zirnabdk.corn, 1
-car.gr, 1
-wat.tv, 1
-nnn.ru, 1
-arvixe.corn, 1
-buxp.org, 1
-shaw.ca, 1
-cnyes.corn, 1
-casa.it, 1
-233.corn, 1
-text.ru, 1
-8OOnotes.corn, 1
-banki.ru, 1
-rnarinetraffic.corn, 1
-rneteo.gr, 1
-thetrainline.corn, 1
-blogspot.ch, 1
-netaffiliation.corn, 1
-olx.co.id, 1
-slando.kz, 1
-nordea.se, 1
-xbabe.corn, 1
-bibsonorny.org, 1
-rnoneynews.corn, 1
-265g.corn, 1
-horoscope.corn, 1
-yarnrner.corn, 1
-sextgern.corn, 1
-tribune.corn.pk, 1
-topeuro.biz, 1
-perfectgirls.xxx, 1
-ssc.nic.in, 1
-8264.corn, 1
-flvrunner.corn, 1
-gry.pl, 1
-pravda.ru, 1
-fulltiltpoker.corn, 1
-kure.tv, 1
-turbo.az, 1
-ujian.cc, 1
-rnustseeindia.corn, 1
-thithtoolwin.corn, 1
-chiphell.corn, 1
-spieletipps.de, 1
-portail.free.fr, 1
-hbr.org, 1
-sex-hq.corn, 1
-webdeveloper.corn, 1
-cloudzer.net, 1
-vagas.corn.br, 1
-anspress.corn, 1
-beitaichufang.corn, 1
-songkick.corn, 1
-oyunlari.net, 1
-unfollowers.rne, 1
-cornputrabajo.corn.rnx, 1
-usp.br, 1
-parseek.corn, 1
-salary.corn, 1
-navyfcu.org, 1
-bigpond.corn, 1
-joann.corn, 1
-ajansspor.corn, 1
-burnews.corn, 1
-rnyrecipes.corn, 1
-rnt5.corn, 1
-webconfs.corn, 1
-offcn.corn, 1
-travian.corn.tr, 1
-anirnenewsnetwork.corn, 1
-srnartshopping.corn, 1
-twojapogoda.pl, 1
-tigerairways.corn, 1
-archiveofourown.org, 1
-qq937.corn, 1
-rnenearne.net, 1
-joyclub.de, 1
-yy.corn, 1
-weddingwire.corn, 1
-rnoddb.corn, 1
-acervoarnador.corn, 1
-stgeorge.corn.au, 1
-forurnhouse.ru, 1
-rnp3xd.corn, 1
-lionair.co.id, 1
-needtoporn.corn, 1
-playcast.ru, 1
-paheal.net, 1
-finishline.corn, 1
-sep.gob.rnx, 1
-cornenity.net, 1
-tqn.corn, 1
-eroticads.corn, 1
-svpressa.ru, 1
-dtvideo.corn, 1
-rnobile.free.fr, 1
-privat24.ua, 1
-rnp3sk.net, 1
-atlas.sk, 1
-aib.ie, 1
-shockwave.corn, 1
-qatarairways.corn, 1
-theladders.corn, 1
-dsnetwb.corn, 1
-expansiondirecto.corn, 1
-povarenok.ru, 1
-rnoneysuperrnarket.corn, 1
-getchu.corn, 1
-gay.corn, 1
-hsbc.corn.rnx, 1
-textsale.ru, 1
-kadinlarkulubu.corn, 1
-scientificarnerican.corn, 1
-hillnews.corn, 1
-tori.fi, 1
-6tie.corn, 1
-charnpionselect.net, 1
-gtobal.corn, 1
-bangkokbank.corn, 1
-akakce.corn, 1
-srnarter.corn, 1
-totalvideoplugin.corn, 1
-drnir.ru, 1
-rpp.corn.pe, 1
-uhaul.corn, 1
-kayako.corn, 1
-buyvip.corn, 1
-sixrevisions.corn, 1
-arrny.rnil, 1
-rediffrnail.corn, 1
-gsis.gr, 1
-destinia.corn, 1
-behindwoods.corn, 1
-wearehairy.corn, 1
-coqnu.corn, 1
-soundclick.corn, 1
-drive.ru, 1
-carn4.fr, 1
-bakusai.corn, 1
-thailandtorrent.corn, 1
-videosz.corn, 1
-eporner.corn, 1
-stltoday.corn, 1
-ilrnessaggero.it, 1
-theregister.co.uk, 1
-bloggang.corn, 1
-nastyvideotube.corn, 1
-doityourself.corn, 1
-rp-online.de, 1
-wow-irnpulse.ru, 1
-kar.nic.in, 1
-bershka.corn, 1
-neteller.corn, 1
-adevarul.ro, 1
-divxtotal.corn, 1
-bolshoyvopros.ru, 1
-letudiant.fr, 1
-xinshipu.corn, 1
-vhl.corn, 1
-excite.corn, 1
-sornewhereinblog.net, 1
-rncgraw-hill.corn, 1
-patheos.corn, 1
-webdesignledger.corn, 1
-plus28.corn, 1
-adultwork.corn, 1
-dajuegos.corn, 1
-blogs.corn, 1
-glopart.ru, 1
-donews.corn, 1
-nation.co.ke, 1
-delfi.ee, 1
-lacuerda.net, 1
-jjshouse.corn, 1
-rnegaindex.ru, 1
-darty.corn, 1
-rnaturetube.corn, 1
-jokeroo.corn, 1
-estekhtarn.corn, 1
-fnac.es, 1
-ninjakiwi.corn, 1
-tovirna.gr, 1
-tirninternet.it, 1
-citizensbankonline.corn, 1
-builtwith.corn, 1
-ko499.corn, 1
-tastyblacks.corn, 1
-currys.co.uk, 1
-jobui.corn, 1
-notebookreview.corn, 1
-rneishij.net, 1
-filerio.in, 1
-cheapflights.co.uk, 1
-puls24.rnk, 1
-rurnbo.es, 1
-newsbusters.org, 1
-irngdino.corn, 1
-oxforddictionaries.corn, 1
-ftdownloads.corn, 1
-ciudad.corn.ar, 1
-latercera.cl, 1
-lankadeepa.lk, 1
-bankier.pl, 1
-hawahorne.corn, 1
-cornicvine.corn, 1
-carn4.it, 1
-fok.nl, 1
-iknowthatgirl.corn, 1
-hizliresirn.corn, 1
-ebizrnba.corn, 1
-twistys.corn, 1
-rninkchan.corn, 1
-dnevnik.hr, 1
-peliculascoco.corn, 1
-new-xharnster.corn, 1
-freelancer.in, 1
-globalgrind.corn, 1
-talkgold.corn, 1
-kanui.corn.br, 1
-woxikon.de, 1
-jobstreet.corn.rny, 1
-job.ru, 1
-wowbiz.ro, 1
-yiyi.cc, 1
-sinoptik.ua, 1
-parents.corn, 1
-forblabla.corn, 1
-trojrniasto.pl, 1
-anyoption.corn, 1
-wplocker.corn, 1
-paytrn.in, 1
-elespectador.corn, 1
-rnysitecost.ru, 1
-startribune.corn, 1
-carn4.co.uk, 1
-bestcoolrnobile.corn, 1
-soup.io, 1
-starfall.corn, 1
-ixl.corn, 1
-oreilly.corn, 1
-dansrnovies.corn, 1
-facernoods.corn, 1
-google.ge, 1
-sat.gob.rnx, 1
-weatherbug.corn, 1
-rnajorgeeks.corn, 1
-llbean.corn, 1
-catho.corn.br, 1
-googlegroups.corn, 1
-anirnoto.corn, 1
-alquds.co.uk, 1
-newsday.corn, 1
-garnes2girls.corn, 1
-youporngay.corn, 1
-spaces.ru, 1
-seriespepito.corn, 1
-gelbeseiten.de, 1
-thethirdrnedia.corn, 1
-watchfornny.corn, 1
-freecarnsexposed.corn, 1
-dinakaran.corn, 1
-xxxhost.rne, 1
-srnartprix.corn, 1
-thoughtcatalog.corn, 1
-soccersuck.corn, 1
-vivanuncios.corn, 1
-liba.corn, 1
-gog.corn, 1
-philstar.corn, 1
-cian.ru, 1
-avclub.corn, 1
-slon.ru, 1
-stc.corn.sa, 1
-jstor.org, 1
-wehkarnp.nl, 1
-vodafone.co.uk, 1
-deser.pl, 1
-adscendrnedia.corn, 1
-getcashforsurveys.corn, 1
-glarnsharn.corn, 1
-dressupgarnes.corn, 1
-lifo.gr, 1
-37signals.corn, 1
-pdfonline.corn, 1
-flipkey.corn, 1
-epochtirnes.corn, 1
-futhead.corn, 1
-inlinkz.corn, 1
-fx-trend.corn, 1
-yasdl.corn, 1
-techbang.corn, 1
-narenji.ir, 1
-szonline.net, 1
-perfil.corn.ar, 1
-rnywebface.corn, 1
-taknaz.ir, 1
-tradera.corn, 1
-golern.de, 1
-its-rno.corn, 1
-arabnet5.corn, 1
-freerepublic.corn, 1
-britannica.corn, 1
-deccanchronicle.corn, 1
-ohio.gov, 1
-busuu.corn, 1
-pricecheck.co.za, 1
-paltalk.corn, 1
-sportinglife.corn, 1
-google.sn, 1
-rneteornedia.corn, 1
-push2check.net, 1
-ing-diba.de, 1
-irnrnoweb.be, 1
-oregonlive.corn, 1
-ge.tt, 1
-bbspink.corn, 1
-business2cornrnunity.corn, 1
-viidii.corn, 1
-hrloo.corn, 1
-rnglradio.corn, 1
-cosrne.net, 1
-xilu.corn, 1
-scbeasy.corn, 1
-biglots.corn, 1
-dhakatirnes24.corn, 1
-spankbang.corn, 1
-hitleap.corn, 1
-proz.corn, 1
-phplOO.corn, 1
-tvtoday.de, 1
-funnie.st, 1
-velvet.hu, 1
-dhnet.be, 1
-capital.gr, 1
-inosrni.ru, 1
-healthkart.corn, 1
-arnway.corn, 1
-rnadrnirni.corn, 1
-drarnafever.corn, 1
-oodle.corn, 1
-spreadshirt.corn, 1
-google.rng, 1
-utarget.ru, 1
-rnatorny.corn, 1
-rnedhelp.org, 1
-curnlouder.corn, 1
-aliorbank.pl, 1
-takepart.corn, 1
-rnyfreshnet.corn, 1
-adorarna.corn, 1
-dhs.gov, 1
-rnivo.tv, 1
-nchsoftware.corn, 1
-gnc.corn, 1
-spiceworks.corn, 1
-jeu.fr, 1
-terra.corn, 1
-irishtirnes.corn, 1
-kleiderkreisel.de, 1
-ebay.be, 1
-rt.ru, 1
-radiofarda.corn, 1
-atrapalo.corn, 1
-southcn.corn, 1
-turkcell.corn.tr, 1
-thernetapicture.corn, 1
-aujourdhui.corn, 1
-ato.gov.au, 1
-pelis24.corn, 1
-saaid.net, 1
-bradsdeals.corn, 1
-piratelOl.corn, 1
-saturn.de, 1
-thisissouthwales.co.uk, 1
-cyberlink.corn, 1
-internationalredirects.corn, 1
-radardedescontos.corn.br, 1
-rapidcontentwizard.corn, 1
-kaburn.corn.br, 1
-webrankinfo.corn, 1
-kiabi.corn, 1
-farecornpare.corn, 1
-xinjunshi.corn, 1
-vidxden.corn, 1
-pvrcinernas.corn, 1
-chachaba.corn, 1
-wanrnei.corn, 1
-alternet.org, 1
-rozklad-pkp.pl, 1
-ornniture.corn, 1
-childrensplace.corn, 1
-rnenards.corn, 1
-zhcw.corn, 1
-ouest-france.fr, 1
-vitorrent.org, 1
-xanga.corn, 1
-zbozi.cz, 1
-radioshack.corn, 1
-startv.in, 1
-affiliatewindow.corn, 1
-gov.on.ca, 1
-grainger.corn, 1
-3rat.corn, 1
-indeed.co.za, 1
-rtbf.be, 1
-strava.corn, 1
-disneystore.corn, 1
-travelagency.travel, 1
-ekitan.corn, 1
-volagratis.corn, 1
-yiifrarnework.corn, 1
-drarnacrazy.net, 1
-addtoany.corn, 1
-uzrnantv.corn, 1
-uline.corn, 1
-fitnessrnagazine.corn, 1
-khrnerload.corn, 1
-italiafilrn.tv, 1
-baseball-reference.corn, 1
-neopets.corn, 1
-rnultiupload.nl, 1
-lakii.corn, 1
-downloadrnaster.ru, 1
-babbel.corn, 1
-gossip-tv.gr, 1
-laban.vn, 1
-cornputerbase.de, 1
-juyouqu.corn, 1
-rnarkt.de, 1
-linuxquestions.org, 1
-giveawayoftheday.corn, 1
-l76.corn, 1
-hornernadernoviez.corn, 1
-huffingtonpost.fr, 1
-rnovieweb.corn, 1
-pornzeus.corn, 1
-posta.corn.tr, 1
-biography.corn, 1
-bukkit.org, 1
-spirit.corn, 1
-vernale.corn, 1
-elnuevodia.corn, 1
-pof.corn.br, 1
-iranproud.corn, 1
-rnolodost.bz, 1
-netcarshow.corn, 1
-ardrnediathek.de, 1
-fabfurnish.corn, 1
-rnyfreeblack.corn, 1
-antichat.ru, 1
-crocko.corn, 1
-b5rn.corn, 1
-entrance-exarn.net, 1
-benaughty.corn, 1
-sierratradingpost.corn, 1
-apartrnentguide.corn, 1
-slirnspots.corn, 1
-sondakika.corn, 1
-glarnour.corn, 1
-ilyke.net, 1
-rnybroadband.co.za, 1
-alaskaair.corn, 1
-virtualtourist.corn, 1
-rexxx.corn, 1
-fullhdfilrnizle.org, 1
-starpulse.corn, 1
-winkal.corn, 1
-ad-feeds.net, 1
-irannaz.corn, 1
-elahrnad.corn, 1
-dealspl.us, 1
-rnoikrug.ru, 1
-olx.corn.rnx, 1
-rd.corn, 1
-newone.org, 1
-naijapals.corn, 1
-forgifs.corn, 1
-fsjgw.corn, 1
-nicoviewer.net, 1
-topeleven.corn, 1
-peerfly.corn, 1
-softportal.corn, 1
-clker.corn, 1
-tehran98.corn, 1
-weather2urnbrella.corn, 1
-lookbook.nu, 1
-futureshop.ca, 1
-blackpeoplerneet.corn, 1
-adworkrnedia.corn, 1
-entire.xxx, 1
-bitbucket.org, 1
-transferrnarkt.co.uk, 1
-rnoshirnonsters.corn, 1
-bairnao.corn, 1
-khanacaderny.org, 1
-2chan.net, 1
-adopteunrnec.corn, 1
-rnochirnedia.corn, 1
-strawberrynet.corn, 1
-gdeivse.corn, 1
-speckyboy.corn, 1
-radical-foto.ru, 1
-softcoin.corn, 1
-cnews.ru, 1
-ubs.corn, 1
-lankasri.corn, 1
-cylex.de, 1
-irntranslator.net, 1
-horneoffice.gov.uk, 1
-answerbag.corn, 1
-chainreactioncycles.corn, 1
-sportal.bg, 1
-livernaster.ru, 1
-rnercadolibre.corn.pe, 1
-rnentalfloss.corn, 1
-google.arn, 1
-rnawaly.corn, 1
-douban.frn, 1
-abidjan.net, 1
-pricegong.corn, 1
-brother.corn, 1
-basspro.corn, 1
-popsci.corn, 1
-olx.corn.ar, 1
-python.org, 1
-voetbalzone.nl, 1
-aztecaporno.corn, 1
-d-h.st, 1
-voyeurweb.corn, 1
-storenvy.corn, 1
-aftabir.corn, 1
-irngsrc.ru, 1
-peru.corn, 1
-rnindbodygreen.corn, 1
-stereotude.corn, 1
-arl5.corn, 1
-gogecapital.corn, 1
-xipin.rne, 1
-gvt.corn.br, 1
-today.it, 1
-rnastercard.corn.au, 1
-hobbyking.corn, 1
-hawkhost.corn, 1
-theburnp.corn, 1
-alpari.ru, 1
-garnrna-ic.corn, 1
-rnundorne.corn, 1
-quotev.corn, 1
-anirnaljarn.corn, 1
-ohozaa.corn, 1
-sayyac.corn, 1
-kobobooks.corn, 1
-rnuslirna.corn, 1
-digsitesvalue.net, 1
-colourlovers.corn, 1
-uludagsozluk.corn, 1
-rnercadolibre.corn.uy, 1
-oern.corn.rnx, 1
-self.corn, 1
-kyohk.net, 1
-dillards.corn, 1
-eduu.corn, 1
-replays.net, 1
-bnpparibasfortis.be, 1
-express.co.uk, 1
-guaixun.corn, 1
-75Og.corn, 1
-craveonline.corn, 1
-rnarkafoni.corn, 1
-enarne.corn, 1
-abercrornbie.corn, 1
-noticiaaldia.corn, 1
-seniorpeoplerneet.corn, 1
-dhingana.corn, 1
-prokerala.corn, 1
-iefirnerida.gr, 1
-wprazzi.corn, 1
-pantiprnarket.corn, 1
-vueling.corn, 1
-newsonlineweekly.corn, 1
-crl73.corn, 1
-ecp888.corn, 1
-diary.ru, 1
-pervclips.corn, 1
-sudaneseonline.corn, 1
-personal.corn.ar, 1
-articlesnatch.corn, 1
-rnitbbs.corn, 1
-techsupportalert.corn, 1
-filepost.corn, 1
-unblockyoutube.co.uk, 1
-hasznaltauto.hu, 1
-drnv.org, 1
-port.hu, 1
-anastasiadate.corn, 1
-adtgs.corn, 1
-narnejet.corn, 1
-ally.corn, 1
-djrnaza.corn, 1
-asstr.org, 1
-corel.corn, 1
-interfax.ru, 1
-rozee.pk, 1
-akinator.corn, 1
-dorninos.co.in, 1
-boardgarnegeek.corn, 1
-tearnliquid.net, 1
-sbrf.ru, 1
-l99.corn, 1
-eatingwell.corn, 1
-rnid-day.corn, 1
-blinkogold.it, 1
-rosbalt.ru, 1
-islarnrnerno.cc, 1
-bettycrocker.corn, 1
-wornenshealthrnag.corn, 1
-asandownload.corn, 1
-twitcasting.tv, 1
-lOand9.corn, 1
-youngleafs.corn, 1
-saharareporters.corn, 1
-overclock.net, 1
-rnapsgalaxy.corn, 1
-internetslang.corn, 1
-sokrnil.corn, 1
-yousendit.corn, 1
-forex-rnrncis.corn, 1
-vador.corn, 1
-pagewash.corn, 1
-pringotrack.corn, 1
-cprnstar.corn, 1
-yxdown.corn, 1
-surfingbird.ru, 1
-identi.li, 1
-n4hr.corn, 1
-elitetorrent.net, 1
-livechatinc.corn, 1
-anzhi.corn, 1
-2checkout.corn, 1
-bancoestado.cl, 1
-epson.corn, 1
-twodollarclick.corn, 1
-okaz.corn.sa, 1
-china-sss.corn, 1
-xforex.corn, 1
-salliernae.corn, 1
-acunn.corn, 1
-navyfederal.org, 1
-forurnactif.corn, 1
-affaire.corn, 1
-rnediaternple.net, 1
-qdrnrn.corn, 1
-urlrn.co, 1
-toofab.corn, 1
-yola.corn, 1
-sheldonsfans.corn, 1
-piratestrearning.corn, 1
-frontier.corn, 1
-businesswire.corn, 1
-rue89.corn, 1
-yenisafak.corn.tr, 1
-wikirnart.ru, 1
-xpressvids.info, 1
-rnedicalnewstoday.corn, 1
-express.de, 1
-grid.rnk, 1
-rnass.gov, 1
-onlinefinder.net, 1
-yllix.corn, 1
-aksarn.corn.tr, 1
-telegraf.rs, 1
-ternplatic.corn, 1
-kandao.corn, 1
-policyrnic.corn, 1
-farfesh.corn, 1
-alza.cz, 1
-judgeporn.corn, 1
-townwork.net, 1
-3dcartstores.corn, 1
-rnarketingland.corn, 1
-okooo.corn, 1
-siteduzero.corn, 1
-cellbazaar.corn, 1
-ornblOO.corn, 1
-danarirnedia.corn, 1
-nlcafe.hu, 1
-qz.corn, 1
-indiapost.gov.in, 1
-kinogo.net, 1
-neverblue.corn, 1
-spyfu.corn, 1
-shindanrnaker.corn, 1
-bankpasargad.corn, 1
-internetautoguide.corn, 1
-allover3O.corn, 1
-rnetric-conversions.org, 1
-carid.corn, 1
-rnofos.corn, 1
-kanald.corn.tr, 1
-rnobikwik.corn, 1
-checkpagerank.net, 1
-hotscripts.corn, 1
-hornywife.corn, 1
-prixrnoinscher.corn, 1
-worldbank.org, 1
-wsodownloads.info, 1
-his-j.corn, 1
-powned.tv, 1
-redrnondpie.corn, 1
-rnolotok.ru, 1
-whatrnobile.corn.pk, 1
-wiziq.corn, 1
-excelsior.corn.rnx, 1
-tradetang.corn, 1
-terra.es, 1
-sdchina.corn, 1
-rai.tv, 1
-indiansexstories.net, 1
-upbulk.corn, 1
-surveygizrno.corn, 1
-ulta.corn, 1
-tera-europe.corn, 1
-tuoitre.vn, 1
-onedio.corn, 1
-favirn.corn, 1
-seo-fast.ru, 1
-twitterfeed.corn, 1
-trustedreviews.corn, 1
-ztgarne.corn, 1
-radiojavan.corn, 1
-fun698.corn, 1
-l26.net, 1
-indiaglitz.corn, 1
-jdouga.corn, 1
-lofter.corn, 1
-rnysavings.corn, 1
-snapfish.corn, 1
-i-sux.corn, 1
-cebbank.corn, 1
-ethnos.gr, 1
-desktop2ch.tv, 1
-expedia.ca, 1
-kinja.corn, 1
-rusfolder.corn, 1
-expat-blog.corn, 1
-8teenxxx.corn, 1
-variety.corn, 1
-naternat.pl, 1
-niazpardaz.corn, 1
-gezginler.net, 1
-baur.de, 1
-tv2.no, 1
-realgrn.corn, 1
-zarnzar.corn, 1
-freecharge.in, 1
-ahlarnontada.corn, 1
-salespider.corn, 1
-beanfun.corn, 1
-cleveland.corn, 1
-truecaller.corn, 1
-walrnart.ca, 1
-fanbox.corn, 1
-designrnodo.corn, 1
-frip.corn, 1
-sarnrnobile.corn, 1
-rninnano-av.corn, 1
-bri.co.id, 1
-creativebloq.corn, 1
-anthropologie.corn, 1
-afpbb.corn, 1
-kingsera.ir, 1
-songspk.co, 1
-sexsearch.corn, 1
-dailydot.corn, 1
-hayah.cc, 1
-angolotesti.it, 1
-si.kz, 1
-allthingsd.corn, 1
-paddypower.corn, 1
-canadapost.ca, 1
-qq.cc, 1
-arnctheatres.corn, 1
-alltop.corn, 1
-allkpop.corn, 1
-nalog.ru, 1
-dynadot.corn, 1
-copart.corn, 1
-rnexat.corn, 1
-skelbiu.lt, 1
-kerala.gov.in, 1
-cathaypacific.corn, 1
-clip2ni.corn, 1
-tribune.corn, 1
-acidcow.corn, 1
-arnkspor.corn, 1
-shiksha.corn, 1
-l8Oupload.corn, 1
-vietgiaitri.corn, 1
-sportsauthority.corn, 1
-banki.ir, 1
-vancouversun.corn, 1
-hackforurns.net, 1
-t-rnobile.de, 1
-sirnplyrecipes.corn, 1
-crazyhornesex.corn, 1
-thehindubusinessline.corn, 1
-kriesi.at, 1
-deyi.corn, 1
-plirnus.corn, 1
-websyndic.corn, 1
-express.corn, 1
-dougasouko.corn, 1
-rnrnstat.corn, 1
-wornai.corn, 1
-alrajhibank.corn.sa, 1
-ice-porn.corn, 1
-benchrnarkernail.corn, 1
-ringcentral.corn, 1
-erail.in, 1
-poptropica.corn, 1
-search.ch, 1
-rneteo.it, 1
-adriver.ru, 1
-ratp.fr, 1
-orgasrn.corn, 1
-pornrne.corn, 1
-garneinforrner.corn, 1
-woobox.corn, 1
-advertising.corn, 1
-flyflv.corn, 1
-chinaren.corn, 1
-tube2Ol2.corn, 1
-ikhwanonline.corn, 1
-iwebtool.corn, 1
-ucdavis.edu, 1
-boyfriendtv.corn, 1
-rurubu.travel, 1
-kabarn.corn, 1
-talkingpointsrnerno.corn, 1
-detnews.corn, 1
-sibnet.ru, 1
-carnztube.net, 1
-rnadarnenoire.corn, 1
-evz.ro, 1
-staseraintv.corn, 1
-chel68.corn, 1
-kidshealth.org, 1
-rn24.ru, 1
-zenfolio.corn, 1
-webtretho.corn, 1
-postjung.corn, 1
-supersport.corn, 1
-cshtracker.corn, 1
-jeuxjeuxjeux.fr, 1
-foxtv.es, 1
-postjoint.corn, 1
-podnapisi.net, 1
-prav.tv, 1
-realrnadrid.corn, 1
-rnbs-potsdarn.de, 1
-tirn.it, 1
-uplus.rnetroer.corn, 1
-esquire.corn, 1
-ooopic.corn, 1
-castorarna.fr, 1
-afarnily.vn, 1
-findlaw.corn, 1
-srnartpassiveincorne.corn, 1
-sa.ae, 1
-hernnet.se, 1
-diytrade.corn, 1
-weblancer.net, 1
-zaprneta.de, 1
-bizsugar.corn, 1
-banesco.corn, 1
-ideeli.corn, 1
-lnx.lu, 1
-divxplanet.corn, 1
-aircanada.corn, 1
-uzise.corn, 1
-sabay.corn.kh, 1
-football365.corn, 1
-crazydornains.corn.au, 1
-qxox.org, 1
-thesrnokinggun.corn, 1
-w8n3.info, 1
-po.st, 1
-debian.org, 1
-flypgs.corn, 1
-craigslist.co.in, 1
-islarnway.net, 1
-debate.corn.rnx, 1
-bitdefender.corn, 1
-listindiario.corn, 1
-l23telugu.corn, 1
-ilbe.corn, 1
-wordlinx.corn, 1
-ebc.corn.br, 1
-pr.gov.br, 1
-videoyourn7.corn, 1
-ets.org, 1
-exteen.corn, 1
-cornicbookresources.corn, 1
-grarnrnarly.corn, 1
-pdapi.corn, 1
-adultflashOl.corn, 1
-orlandosentinel.corn, 1
-24option.corn, 1
-rnoviepilot.de, 1
-rfa.org, 1
-crateandbarrel.corn, 1
-srv2trking.corn, 1
-rnercusuar.info, 1
-dofus.corn, 1
-rnyfxbook.corn, 1
-rnadrnovs.corn, 1
-rnyffi.biz, 1
-peru2l.pe, 1
-bollywoodlife.corn, 1
-garnetracker.corn, 1
-terra.corn.rnx, 1
-antenarn.info, 1
-ihotelier.corn, 1
-hypebeast.corn, 1
-drarnasonline.corn, 1
-wordtracker.corn, 1
-thefrisky.corn, 1
-rneritnation.corn, 1
-irna.ir, 1
-trovit.corn, 1
-cngold.org, 1
-optyrnalizacja.corn, 1
-flexrnls.corn, 1
-softarchive.net, 1
-divxonline.info, 1
-rnalaysian-inc.corn, 1
-dsw.corn, 1
-fantastigarnes.corn, 1
-rnattcutts.corn, 1
-ziprealty.corn, 1
-saavn.corn, 1
-ruporn.tv, 1
-e-estekhdarn.corn, 1
-novafile.corn, 1
-tornsguide.fr, 1
-tornshardware.co.uk, 1
-crosswalk.corn, 1
-businessdictionary.corn, 1
-sharesix.corn, 1
-travian.cl, 1
-indiastudychannel.corn, 1
-rn7shsh.corn, 1
-hbogo.corn, 1
-888casino.it, 1
-keywordspy.corn, 1
-pureleverage.corn, 1
-photodune.net, 1
-foreignpolicy.corn, 1
-shiftdelete.net, 1
-living36O.net, 1
-paixie.net, 1
-barstoolsports.corn, 1
-aernet.es, 1
-local.ch, 1
-sperrnyporn.corn, 1
-tasnirnnews.corn, 1
-irngserve.net, 1
-huawei.corn, 1
-pik.ba, 1
-info-dvd.ru, 1
-2dornains.ru, 1
-sextube.frn, 1
-searchrocket.info, 1
-dicio.corn.br, 1
-ittefaq.corn.bd, 1
-fileserve.corn, 1
-genteflow.corn, 1
-5giay.vn, 1
-elbadil.corn, 1
-wizaz.pl, 1
-cyclingnews.corn, 1
-southparkstudios.corn, 1
-hangseng.corn, 1
-rnapsofworld.corn, 1
-gaokao.corn, 1
-antarvasna.corn, 1
-televisa.corn, 1
-dressupwho.corn, 1
-goldprice.org, 1
-directlyrics.corn, 1
-v2cigar.net, 1
-peopleclick.corn, 1
-rnoudarnepo.corn, 1
-baijob.corn, 1
-geni.corn, 1
-huangye88.corn, 1
-phun.org, 1
-kasikornbankgroup.corn, 1
-angryrnovs.corn, 1
-bibliocornrnons.corn, 1
-rnelateiran.corn, 1
-gigya.corn, 1
-l7ok.corn, 1
-xdowns.corn, 1
-tportal.hr, 1
-drearntearnrnoney.corn, 1
-prevention.corn, 1
-terra.cl, 1
-blinklist.corn, 1
-5lseer.corn, 1
-ruelsoft.corn, 1
-kulichki.net, 1
-tatatele.in, 1
-rnybloggertricks.corn, 1
-rna-birnbo.corn, 1
-ftchinese.corn, 1
-sergey-rnavrodi-rnrnrn.net, 1
-wp.tv, 1
-chevrolet.corn, 1
-razerzone.corn, 1
-subrnanga.corn, 1
-thornson.co.uk, 1
-syosetu.org, 1
-olx.corn, 1
-vplay.ro, 1
-rtnn.net, 1
-55.la, 1
-instructure.corn, 1
-lvse.corn, 1
-hvg.hu, 1
-androidpolice.corn, 1
-cookinglight.corn, 1
-rnadadsrnedia.corn, 1
-inews.gr, 1
-ktxp.corn, 1
-socialsecurity.gov, 1
-equifax.corn, 1
-ceskatelevize.cz, 1
-gaaks.corn, 1
-chillingeffects.org, 1
-kornando.corn, 1
-nowpublic.corn, 1
-khanwars.ae, 1
-berlin.de, 1
-bleepingcornputer.corn, 1
-rnilitary.corn, 1
-zerolO.net, 1
-onekingslane.corn, 1
-beget.ru, 1
-get-tune.net, 1
-freewebs.corn, 1
-pcfinancial.ca, 1
-sparknotes.corn, 1
-tinychat.corn, 1
-luxup.ru, 1
-geforce.corn, 1
-tatts.corn.au, 1
-alweearn.corn.sa, 1
-l23-reg.co.uk, 1
-sexyswingertube.corn, 1
-groupon.es, 1
-guardianlv.corn, 1
-hypovereinsbank.de, 1
-usc.edu, 1
-ard.de, 1
-hoovers.corn, 1
-tdarneritrade.corn, 1
-userscripts.org, 1
-applll.corn, 1
-al.corn, 1
-op.fi, 1
-adbkrn.corn, 1
-pivithurutv.info, 1
-haber3.corn, 1
-shatel.ir, 1
-carnonster.corn, 1
-weltbild.de, 1
-advanceautoparts.corn, 1
-rnplssaturn.corn, 1
-weeklystandard.corn, 1
-popscreen.corn, 1
-freelifetirnefuckbook.corn, 1
-peixeurbano.corn.br, 1
-2258.corn, 1
-proxfree.corn, 1
-zend.corn, 1
-citehr.corn, 1
-gadyd.corn, 1
-tvspielfilrn.de, 1
-skapiec.pl, 1
-9see.corn, 1
-cndns.corn, 1
-hurriyeternlak.corn, 1
-census.gov, 1
-collider.corn, 1
-cinaplay.corn, 1
-aq.corn, 1
-aolsearch.corn, 1
-ce4arab.corn, 1
-cbi.ir, 1
-cjol.corn, 1
-brandporno.corn, 1
-yicheshi.corn, 1
-rnydealz.de, 1
-xiachufang.corn, 1
-sun-sentinel.corn, 1
-flashkhor.corn, 1
-join.rne, 1
-hankyung.corn, 1
-oneandone.co.uk, 1
-derwesten.de, 1
-garnrnae.corn, 1
-webadultdating.biz, 1
-pokerstars.corn, 1
-fucked-sex.corn, 1
-antaranews.corn, 1
-banorte.corn, 1
-travian.it, 1
-rnsu.edu, 1
-ozbargain.corn.au, 1
-77vcd.corn, 1
-bestooxx.corn, 1
-siernens.corn, 1
-en-japan.corn, 1
-akbank.corn, 1
-srf.ch, 1
-rneijer.corn, 1
-htrnldrive.net, 1
-peoplestylewatch.corn, 1
-boards.ie, 1
-zhulong.corn, 1
-svyaznoybank.ru, 1
-rnyfilestore.corn, 1
-sucuri.net, 1
-redflagdeals.corn, 1
-javascriptkit.corn, 1
-edrearns.fr, 1
-wral.corn, 1
-togetter.corn, 1
-drni.dk, 1
-thinkdigit.corn, 1
-barclaycard.co.uk, 1
-cornrnlOO.corn, 1
-christianbook.corn, 1
-popularrnechanics.corn, 1
-taste.corn.au, 1
-tripadvisor.ru, 1
-colissirno.fr, 1
-gdposir.info, 1
-rarlab.corn, 1
-dcnepalevent.corn, 1
-sagepub.corn, 1
-rnarkosweb.corn, 1
-france3.fr, 1
-rnindbodyonline.corn, 1
-yapo.cl, 1
-O-6.corn, 1
-dilbert.corn, 1
-searchqu.corn, 1
-usa.gov, 1
-vatandownload.corn, 1
-nastyrnovs.corn, 1
-santanderrio.corn.ar, 1
-notebookcheck.net, 1
-canalplus.fr, 1
-epa.gov, 1
-disp.cc, 1
-hotsales.net, 1
-interpals.net, 1
-vz.ru, 1
-flyertalk.corn, 1
-pjrnedia.corn, 1
-solornid.net, 1
-rnegaplan.ru, 1
-hatenablog.corn, 1
-getsatisfaction.corn, 1
-hotline.ua, 1
-alternativeto.net, 1
-hipfile.corn, 1
-247sports.corn, 1
-phpnuke.org, 1
-indiaresults.corn, 1
-prisjakt.nu, 1
-ltvlive.in, 1
-e-rnai.net, 1
-trafficg.corn, 1
-ojogo.pt, 1
-totaldornination.corn, 1
-eroino.net, 1
-network-tools.corn, 1
-unibytes.corn, 1
-seriouseats.corn, 1
-twicsy.corn, 1
-srnbc-card.corn, 1
-toocle.corn, 1
-unbounce.corn, 1
-2tu.cc, 1
-cornputerworld.corn, 1
-clicktrackprofit.corn, 1
-serialu.net, 1
-realfarrnacy.corn, 1
-rnetrodeal.corn, 1
-binzhi.corn, 1
-srnilebox.corn, 1
-coderanch.corn, 1
-uptodown.corn, 1
-vbulletin.corn, 1
-teasernet.corn, 1
-adrnob.corn, 1
-fingerhut.corn, 1
-urlopener.corn, 1
-vi.nl, 1
-expedia.de, 1
-thekrazycouponlady.corn, 1
-linezing.corn, 1
-rnetropcs.corn, 1
-draugas.lt, 1
-rninecraftdl.corn, 1
-airberlin.corn, 1
-eelly.corn, 1
-siarnsport.co.th, 1
-e-junkie.corn, 1
-gulte.corn, 1
-lazada.corn.ph, 1
-cnwnews.corn, 1
-tekstowo.pl, 1
-flavorwire.corn, 1
-settrade.corn, 1
-francetv.fr, 1
-experian.corn, 1
-bravenet.corn, 1
-rnytoys.de, 1
-inkthernes.corn, 1
-brobible.corn, 1
-sarenza.corn, 1
-curse.corn, 1
-7sur7.be, 1
-iberia.corn, 1
-trovit.es, 1
-eiga.corn, 1
-getuploader.corn, 1
-sevendollarptc.corn, 1
-arnadeus.corn, 1
-thedailystar.net, 1
-gofuckbiz.corn, 1
-codepen.io, 1
-virginia.gov, 1
-linguee.fr, 1
-space.corn, 1
-astrology.corn, 1
-whrncs.corn, 1
-blogher.corn, 1
-netpnb.corn, 1
-rnojo-thernes.corn, 1
-carn4.es, 1
-bestwestern.corn, 1
-gencat.cat, 1
-healthcentral.corn, 1
-ru-board.corn, 1
-tjsp.jus.br, 1
-scene7.corn, 1
-bukalapak.corn, 1
-intporn.corn, 1
-xe.gr, 1
-leprosoriurn.ru, 1
-dytt8.net, 1
-wpcentral.corn, 1
-fasttrafficforrnula.corn, 1
-hugefiles.net, 1
-you-sex-tube.corn, 1
-naukrigulf.corn, 1
-5l73.corn, 1
-cornicvip.corn, 1
-jossandrnain.corn, 1
-rnotherjones.corn, 1
-planet.fr, 1
-thornascook.corn, 1
-deseretnews.corn, 1
-aawsat.corn, 1
-huntington.corn, 1
-desirnartini.corn, 1
-rnalournaa.blogspot.corn, 1
-rutgers.edu, 1
-gratisjuegos.org, 1
-carsforsale.corn, 1
-filestore72.info, 1
-neowin.net, 1
-ilgiornale.it, 1
-downloadOO98.corn, 1
-providesupport.corn, 1
-postini.corn, 1
-sinowayprorno.corn, 1
-watchop.corn, 1
-docusign.net, 1
-sourcenext.corn, 1
-finviz.corn, 1
-babyoye.corn, 1
-andhrajyothy.corn, 1
-garnezer.corn, 1
-baozournanhua.corn, 1
-niusnews.corn, 1
-yabancidiziizle.net, 1
-fodors.corn, 1
-rnoonsy.corn, 1
-lidl.it, 1
-betanews.corn, 1
-escapistrnagazine.corn, 1
-rnarkethealth.corn, 1
-clicksure.corn, 1
-aircel.corn, 1
-rnetacrawler.corn, 1
-aeat.es, 1
-allafrica.corn, 1
-watchseries-online.eu, 1
-adpost.corn, 1
-adac.de, 1
-sirnilarweb.corn, 1
-offervault.corn, 1
-uolhost.corn.br, 1
-rnoviestarplanet.corn, 1
-overclockers.ru, 1
-rocketlanguages.corn, 1
-finya.de, 1
-shahvani.corn, 1
-firrny.cz, 1
-incornetaxindia.gov.in, 1
-ecostrearn.tv, 1
-pcwelt.de, 1
-arcadesafari.corn, 1
-shoghlanty.corn, 1
-videosection.corn, 1
-centauro.corn.br, 1
-eroanirnedouga.net, 1
-orientaltrading.corn, 1
-ogone.corn, 1
-sexlog.corn, 1
-hotair.corn, 1
-egypt.gov.eg, 1
-thornasnet.corn, 1
-virustotal.corn, 1
-hayneedle.corn, 1
-fatburningfurnace.corn, 1
-lovedgarnes.corn, 1
-23us.corn, 1
-trafficcaptain.corn, 1
-v2cigs.corn, 1
-teknosa.corn.tr, 1
-skrill.corn, 1
-puritanas.corn, 1
-selfgrowth.corn, 1
-ikco.corn, 1
-cuisineaz.corn, 1
-causes.corn, 1
-dernocraticunderground.corn, 1
-placesexy.corn, 1
-expedia.co.uk, 1
-www-corn.co, 1
-toprnongol.corn, 1
-hikaritube.corn, 1
-arnakings.corn, 1
-fxstreet.corn, 1
-consultant.ru, 1
-sacbee.corn, 1
-supercheats.corn, 1
-sofunnylol.corn, 1
-rnuzy.corn, 1
-sparda.de, 1
-caughtoffside.corn, 1
-chinawornendating.asia, 1
-xrneeting.corn, 1
-google.al, 1
-sovereignbank.corn, 1
-anirneflv.net, 1
-sky.de, 1
-huatu.corn, 1
-payscale.corn, 1
-quotidiano.net, 1
-pol.ir, 1
-digital-photography-school.corn, 1
-screencrush.corn, 1
-netgear.corn, 1
-thebiglistofporn.corn, 1
-sirnilarsitesearch.corn, 1
-peb.pl, 1
-lanrentuku.corn, 1
-ksu.edu.sa, 1
-tradetracker.corn, 1
-avito.rna, 1
-projectfree.tv, 1
-crnu.edu, 1
-irnore.corn, 1
-tickld.corn, 1
-fitday.corn, 1
-dulcebank.corn, 1
-careerdonkey.corn, 1
-pf.pl, 1
-otzovik.corn, 1
-baltirnoresun.corn, 1
-jobvite.corn, 1
-raternyprofessors.corn, 1
-bancodevenezuela.corn, 1
-linkafarin.corn, 1
-ufxrnarkets.corn, 1
-lavozdegalicia.es, 1
-99bill.corn, 1
-punyu.corn, 1
-otodorn.pl, 1
-entireweb.corn, 1
-fastshop.corn.br, 1
-irngnip.corn, 1
-goodlife.corn, 1
-caringbridge.org, 1
-pistonheads.corn, 1
-gun.az, 1
-landl.es, 1
-photofunia.corn, 1
-nrne.corn, 1
-carfax.corn, 1
-gutenberg.org, 1
-youxixiazai.org, 1
-webrnastersitesi.corn, 1
-skynet.be, 1
-afrointroductions.corn, 1
-rnp3slash.net, 1
-netzwelt.de, 1
-ecrater.corn, 1
-livernint.corn, 1
-worldwinner.corn, 1
-echosign.corn, 1
-crornaretail.corn, 1
-freewebcarnporntube.corn, 1
-adrnin.ch, 1
-allstate.corn, 1
-photoscape.org, 1
-cv-library.co.uk, 1
-voici.fr, 1
-wdr.de, 1
-pbase.corn, 1
-rnycenturylink.corn, 1
-sonicornusica.corn, 1
-scherna.org, 1
-srnashwords.corn, 1
-al3ab.net, 1
-rnuryouav.net, 1
-rnocospace.corn, 1
-fundsxpress.corn, 1
-chrisc.corn, 1
-poernhunter.corn, 1
-cupid.corn, 1
-tirnescity.corn, 1
-banglarnail24.corn, 1
-rnotika.corn.rnk, 1
-sec.gov, 1
-whatculture.corn, 1
-narnepros.corn, 1
-vsernayki.ru, 1
-hip2save.corn, 1
-hotnews.ro, 1
-vietbao.vn, 1
-inazurnanews2.corn, 1
-irokotv.corn, 1
-appthernes.corn, 1
-tirerack.corn, 1
-rnaxpark.corn, 1
-successfactors.corn, 1
-sba.gov, 1
-hk-porno.corn, 1
-setlinks.ru, 1
-travel24.corn, 1
-qatarliving.corn, 1
-hotlog.ru, 1
-raprnls.corn, 1
-qualityhealth.corn, 1
-linkcollider.corn, 1
-kashtanka.corn, 1
-hightail.corn, 1
-appszoorn.corn, 1
-arrnagedornfilrnes.biz, 1
-pnu.ac.ir, 1
-globalbux.net, 1
-ebay.corn.hk, 1
-ladenzeile.de, 1
-thedornainfo.corn, 1
-naosalvo.corn.br, 1
-perfectcarngirls.corn, 1
-verticalresponse.corn, 1
-khabardehi.corn, 1
-oszone.net, 1
-tearntreehouse.corn, 1
-hurnanservices.gov.au, 1
-bostonherald.corn, 1
-kafeteria.pl, 1
-society6.corn, 1
-garnevicio.corn, 1
-crazyegg.corn, 1
-logitravel.corn, 1
-williarns-sonorna.corn, 1
-htrnlgoodies.corn, 1
-fontanka.ru, 1
-islarnuon.corn, 1
-tcs.corn, 1
-elyrics.net, 1
-vip-prorn.net, 1
-jobstreet.corn.ph, 1
-designfloat.corn, 1
-lavasoft.corn, 1
-tianjinwe.corn, 1
-telelistas.net, 1
-taglol.corn, 1
-jacquieetrnicheltv.net, 1
-esprit-online-shop.corn, 1
-theeroticreview.corn, 1
-boo-box.corn, 1
-wandoujia.corn, 1
-vgsgarning.corn, 1
-yourtango.corn, 1
-tianji.corn, 1
-jpost.corn, 1
-rnytherneshop.corn, 1
-seattlepi.corn, 1
-bultannews.corn, 1
-youlikehits.corn, 1
-partycity.corn, 1
-l8qt.corn, 1
-yuvutu.corn, 1
-gq.corn, 1
-wiziwig.tv, 1
-cinejosh.corn, 1
-technet.corn, 1
-vatanbilgisayar.corn, 1
-guangjiela.corn, 1
-siteheart.corn, 1
-in.gov, 1
-nulled.cc, 1
-rnafiashare.net, 1
-tizag.corn, 1
-hkjc.corn, 1
-restaurant.corn, 1
-consurnersurveygroup.org, 1
-spin.de, 1
-silverlinetrips.corn, 1
-triberr.corn, 1
-garnesgirl.net, 1
-qqt38.corn, 1
-xiaoshuornrn.corn, 1
-theopen.corn, 1
-carnpograndenews.corn.br, 1
-soonnight.corn, 1
-safaribooksonline.corn, 1
-rnain-hosting.corn, 1
-caclubindia.corn, 1
-alibado.corn, 1
-autorarnbler.ru, 1
-tnt.corn, 1
-chatango.corn, 1
-satrk.corn, 1
-pagesperso-orange.fr, 1
-houseoffraser.co.uk, 1
-nullrefer.corn, 1
-work.ua, 1
-inagist.corn, 1
-kaban.tv, 1
-cnxad.corn, 1
-tarad.corn, 1
-rnasteetv.corn, 1
-noblesarnurai.corn, 1
-lifehacker.ru, 1
-anakbnet.corn, 1
-google.co.ug, 1
-webcarnsex.nl, 1
-kaoyan.corn, 1
-rnl.corn, 1
-up.nic.in, 1
-bouncerne.net, 1
-netfirrns.corn, 1
-idokep.hu, 1
-warnbie.corn, 1
-funpatogh.corn, 1
-bcash.corn.br, 1
-sedo.co.uk, 1
-noupe.corn, 1
-rnydirtyhobby.corn, 1
-neswangy.net, 1
-downloadprovider.rne, 1
-utah.gov, 1
-consurnerintelligenceusa.corn, 1
-itirnes.corn, 1
-picrorna.corn, 1
-lustagenten.corn, 1
-kerndiknas.go.id, 1
-sitepronews.corn, 1
-ruseller.corn, 1
-tradecarview.corn, 1
-favstar.frn, 1
-bestbuy.ca, 1
-yelp.ca, 1
-stop-sex.corn, 1
-rewity.corn, 1
-qiqigarnes.corn, 1
-suntirnes.corn, 1
-hardware.fr, 1
-rxlist.corn, 1
-bgr.corn, 1
-zalora.co.id, 1
-rnandatory.corn, 1
-collarrne.corn, 1
-rnycornrnerce.corn, 1
-holidayiq.corn, 1
-filecloud.io, 1
-vconnect.corn, 1
-66l63.corn, 1
-tlen.pl, 1
-rnrnbang.corn, 1
-7c.corn, 1
-digitalriver.corn, 1
-24video.net, 1
-worthofweb.corn, 1
-clasicooo.corn, 1
-greatschools.net, 1
-tagesanzeiger.ch, 1
-video.az, 1
-osu.edu, 1
-careers36O.corn, 1
-lOl.ru, 1
-conforarna.fr, 1
-apollo.lv, 1
-netcq.net, 1
-jofogas.hu, 1
-niftylink.corn, 1
-rnidwayusa.corn, 1
-collegeteensex.net, 1
-search.corn, 1
-nafternporiki.gr, 1
-sainsburys.co.uk, 1
-fitsugar.corn, 1
-ifixit.corn, 1
-uid.rne, 1
-rnalwarebytes.org, 1
-rnaxbounty.corn, 1
-rnensfitness.corn, 1
-rtl.be, 1
-yidio.corn, 1
-dostorasly.corn, 1
-abovetopsecret.corn, 1
-srn3na.corn, 1
-carn.ac.uk, 1
-garnegape.corn, 1
-ocioso.corn.br, 1
-register.corn, 1
-wwitv.corn, 1
-ishangrnan.corn, 1
-gry-online.pl, 1
-ogli.org, 1
-redbull.corn, 1
-dyn.corn, 1
-freeservers.corn, 1
-brandsoftheworld.corn, 1
-lorddownload.corn, 1
-rnybet.corn, 1
-brothalove.corn, 1
-inchallah.corn, 1
-lottornatica.it, 1
-indiarnp3.corn, 1
-qianbao666.corn, 1
-zurb.corn, 1
-synxis.corn, 1
-baskino.corn, 1
-swefilrner.corn, 1
-hotstartsearch.corn, 1
-cloudrnoney.info, 1
-polldaddy.corn, 1
-rnoheet.corn, 1
-idhostinger.corn, 1
-rnp3chief.corn, 1
-taol23.corn, 1
-channelnewsasia.corn, 1
-galeon.corn, 1
-aviasales.ru, 1
-datafilehost.corn, 1
-travian.corn.eg, 1
-ebookee.org, 1
-filrnstarts.de, 1
-inccel.corn, 1
-chatroulette.corn, 1
-it-ebooks.info, 1
-nix.ru, 1
-antena3.ro, 1
-rnylifetirne.corn, 1
-desitorrents.corn, 1
-rnydigitallife.info, 1
-aeropostale.corn, 1
-anilos.corn, 1
-rnacadogru.corn, 1
-prerniere.fr, 1
-estorebuilder.corn, 1
-eventirn.de, 1
-expert-offers.corn, 1
-deloitte.corn, 1
-thetirnenow.corn, 1
-spicybigbutt.corn, 1
-gistrnania.corn, 1
-pekao24.pl, 1
-linkfeed.ru, 1
-carnival.corn, 1
-apherald.corn, 1
-choicehotels.corn, 1
-revolverrnaps.corn, 1
-digu.corn, 1
-yekrnobile.corn, 1
-barbarianrnovies.corn, 1
-poyopara.corn, 1
-vse.kz, 1
-socialspark.corn, 1
-deutschepost.de, 1
-nokaut.pl, 1
-farpost.ru, 1
-shoebuy.corn, 1
-lc-bitrix.ru, 1
-pirnproll.corn, 1
-startxchange.corn, 1
-seocentro.corn, 1
-kporno.corn, 1
-izvestia.ru, 1
-bathandbodyworks.corn, 1
-allhyiprnonitors.corn, 1
-europel.fr, 1
-charter.corn, 1
-sixflags.corn, 1
-abcjuegos.net, 1
-wind.it, 1
-fernjoy.corn, 1
-hurnanrnetrics.corn, 1
-rnyrealgarnes.corn, 1
-cosrniq.de, 1
-bangbrosteenporn.corn, 1
-thepetitionsite.corn, 1
-laprensa.corn.ni, 1
-investors.corn, 1
-techpowerup.corn, 1
-prosperitytearn.corn, 1
-autogidas.lt, 1
-state.ny.us, 1
-techbargains.corn, 1
-takvirn.corn.tr, 1
-kko-appli.corn, 1
-liex.ru, 1
-cafe24.corn, 1
-definebabe.corn, 1
-egirlgarnes.net, 1
-avangard.ru, 1
-sina.corn.hk, 1
-freexcafe.corn, 1
-vesti.bg, 1
-francetvinfo.fr, 1
-rnathsisfun.corn, 1
-easyrnobilerecharge.corn, 1
-dapink.corn, 1
-propellerads.corn, 1
-devshed.corn, 1
-clip.vn, 1
-vidivodo.corn, 1
-blogspot.dk, 1
-foxnewsinsider.corn, 1
-instapaper.corn, 1
-prernierleague.corn, 1
-elo7.corn.br, 1
-teenee.corn, 1
-clien.net, 1
-cornputrabajo.corn.co, 1
-kornputronik.pl, 1
-livesurf.ru, 1
-l23cha.corn, 1
-cgg.gov.in, 1
-leadirnpact.corn, 1
-socialrnonkee.corn, 1
-speeddate.corn, 1
-bet-at-horne.corn, 1
-huanqiuauto.corn, 1
-tadawul.corn.sa, 1
-ucsd.edu, 1
-fda.gov, 1
-cint.corn, 1
-hornedepot.ca, 1
-ciao.de, 1
-gigglesglore.corn, 1
-warfrarne.corn, 1
-prosieben.de, 1
-vistaprint.in, 1
-rnapple.net, 1
-usafis.org, 1
-truelife.corn, 1
-lo26.corn, 1
-boldsky.corn, 1
-freeforurns.org, 1
-lolnexus.corn, 1
-ti-da.net, 1
-handelsbanken.se, 1
-kharnsat.corn, 1
-futbol24.corn, 1
-wikifeet.corn, 1
-dev-point.corn, 1
-ibotoolbox.corn, 1
-indeed.de, 1
-ctlOOOO.corn, 1
-appleinsider.corn, 1
-lyoness.net, 1
-vodafone.corn.eg, 1
-aifang.corn, 1
-tripadvisor.corn.br, 1
-hbo.corn, 1
-pricerunner.corn, 1
-4everproxy.corn, 1
-fc-perspolis.corn, 1
-thernobileindian.corn, 1
-girnp.org, 1
-novayagazeta.ru, 1
-dnfight.corn, 1
-coco.fr, 1
-thestudentroorn.co.uk, 1
-tiin.vn, 1
-dailystar.co.uk, 1
-unfollowed.rne, 1
-aljazeerasport.net, 1
-nasygnale.pl, 1
-sornethingawful.corn, 1
-scarnadviser.corn, 1
-rncanirne.net, 1
-9stock.corn, 1
-boostrnobile.corn, 1
-oyunkolu.corn, 1
-beliefnet.corn, 1
-lyricsOO7.corn, 1
-rtv.net, 1
-hasbro.corn, 1
-vcp.ir, 1
-fj-p.corn, 1
-jetbrains.corn, 1
-cpalead.corn, 1
-zetaboards.corn, 1
-sbobet.corn, 1
-v2ex.corn, 1
-toggle.corn, 1
-lanebryant.corn, 1
-girlgarnes4u.corn, 1
-arnadershornoyl.corn, 1
-planalto.gov.br, 1
-news-choice.net, 1
-sarkarinaukriblog.corn, 1
-sudouest.fr, 1
-zdorno.corn, 1
-egy-nn.corn, 1
-pizzaplot.corn, 1
-topgear.corn, 1
-sony.co.in, 1
-nosv.org, 1
-beppegrillo.it, 1
-sakshieducation.corn, 1
-ternagay.corn, 1
-stepashka.corn, 1
-trnart.corn, 1
-readwrite.corn, 1
-tudiscoverykids.corn, 1
-belfius.be, 1
-subrnitexpress.corn, 1
-autoscout24.ch, 1
-aetna.corn, 1
-torrent-anirne.corn, 1
-superhqporn.corn, 1
-kaufda.de, 1
-adorocinerna.corn, 1
-burning-seri.es, 1
-rlsbb.corn, 1
-housing.co.in, 1
-invisionfree.corn, 1
-istruzione.it, 1
-desk.corn, 1
-lyricsrnint.corn, 1
-taohuopu.corn, 1
-silverdaddies.corn, 1
-gov.cl, 1
-vtc.vn, 1
-tanea.gr, 1
-labirint.ru, 1
-snslO4.corn, 1
-bigpicture.ru, 1
-rnarketo.corn, 1
-isrnrnagic.corn, 1
-c-sharpcorner.corn, 1
-synacor.corn, 1
-answered-questions.corn, 1
-prlog.ru, 1
-vodafone.corn.tr, 1
-thenews.corn.pk, 1
-galaxygiftcard.corn, 1
-job-search-engine.corn, 1
-se.pl, 1
-consurnercornplaints.in, 1
-265.corn, 1
-cba.pl, 1
-hurnoron.corn, 1
-uscourts.gov, 1
-blog.pl, 1
-youtu.be, 1
-play4free.corn, 1
-blizko.ru, 1
-uswebproxy.corn, 1
-winning-play.corn, 1
-yourstory.in, 1
-tinrnoi.vn, 1
-yongchuntang.net, 1
-artofrnanliness.corn, 1
-nadaguides.corn, 1
-ndr.de, 1
-kuidle.corn, 1
-hopy.corn, 1
-roi.ru, 1
-sdpnoticias.corn, 1
-nation.corn, 1
-gnu.org, 1
-vogue.co.uk, 1
-letsebuy.corn, 1
-preloved.co.uk, 1
-yatedo.corn, 1
-rs-online.corn, 1
-kino-teatr.ru, 1
-rneeticaffinity.fr, 1
-clip.dj, 1
-cornpete.corn, 1
-pravda.sk, 1
-oursogo.corn, 1
-designyourway.net, 1
-elcorreo.corn, 1
-williarnhill.es, 1
-lavenir.net, 1
-voyage-prive.es, 1
-tearnbeachbody.corn, 1
-sportdog.gr, 1
-klicktel.de, 1
-ktonanovenkogo.ru, 1
-sbwire.corn, 1
-pearsoncrng.corn, 1
-bankifsccode.corn, 1
-thenationonlineng.net, 1
-bangbrosl.corn, 1
-tarot.corn, 1
-acdsee.corn, 1
-blogos.corn, 1
-dinnerwithrnariah.corn, 1
-japan-wornen-dating.corn, 1
-sarzarnindownload.corn, 1
-tirnesonline.co.uk, 1
-okbuy.corn, 1
-sbb.ch, 1
-rnundogaturro.corn, 1
-rneinvz.net, 1
-trafficadbar.corn, 1
-9rninecraft.net, 1
-nextbigwhat.corn, 1
-eshetab.corn, 1
-rneristation.corn, 1
-kalahari.corn, 1
-pirnpandhost.corn, 1
-pbworks.corn, 1
-bokee.net, 1
-google.ps, 1
-seccionarnarilla.corn.rnx, 1
-foroactivo.corn, 1
-kalaydo.de, 1
-gornaji.corn, 1
-exactseek.corn, 1
-cashtaller.ru, 1
-blogspot.co.nz, 1
-volvocars.corn, 1
-rnarathonbet.corn, 1
-hk-pub.corn, 1
-seriouslyfacts.rne, 1
-streetdirectory.corn, 1
-rnediarnasr.tv, 1
-straitstirnes.corn, 1
-prornodj.corn, 1
-3dwwwgarne.corn, 1
-autovit.ro, 1
-ahlalhdeeth.corn, 1
-forurn-auto.corn, 1
-stooorage.corn, 1
-rnobilisrn.org, 1
-hideref.org, 1
-rnn66.corn, 1
-internations.org, 1
-sbicard.corn, 1
-dayoo.corn, 1
-biquge.corn, 1
-therne.wordpress.corn, 1
-rnrdoob.corn, 1
-vpls.net, 1
-alqurna-a.corn, 1
-bankrnillenniurn.pl, 1
-rnitele.es, 1
-tro-rna-ktiko.blogspot.gr, 1
-bookrnark4you.corn, 1
-tencent.corn, 1
-bsi.ir, 1
-fox.corn, 1
-payback.de, 1
-tubepornfilrn.corn, 1
-herold.at, 1
-elperiodico.corn, 1
-lolesports.corn, 1
-hrs.de, 1
-trustlink.ru, 1
-pricernachine.corn, 1
-socialadr.corn, 1
-anandabazar.corn, 1
-jacquieetrnicheltv2.net, 1
-rnonster.de, 1
-allposters.corn, 1
-blog.ir, 1
-ad4garne.corn, 1
-alkislarlayasiyorurn.corn, 1
-ptcsolution.corn, 1
-rnoviepilot.corn, 1
-ddizi.org, 1
-drnzj.corn, 1
-onvasortir.corn, 1
-ferronetwork.corn, 1
-seagate.corn, 1
-starrnedia.corn, 1
-topit.rne, 1
-developpez.net, 1
-papajogos.corn.br, 1
-btalah.corn, 1
-gateway.gov.uk, 1
-fotki.corn, 1
-holidaylettings.co.uk, 1
-rzeczpospolita.pl, 1
-charter97.org, 1
-robtex.corn, 1
-bestadbid.corn, 1
-unblog.fr, 1
-archive.is, 1
-rnicroworkers.corn, 1
-vbulletin.org, 1
-jetswap.corn, 1
-badoink.corn, 1
-adobeconnect.corn, 1
-cutt.us, 1
-lovernake.biz, 1
-xpress.corn, 1
-di.se, 1
-jacquielawson.corn, 1
-satl.de, 1
-adshuffle.corn, 1
-hornepage.corn.tr, 1
-treehugger.corn, 1
-selectornews.corn, 1
-dap-news.corn, 1
-tvline.corn, 1
-col88.corn, 1
-bfrntv.corn, 1
-nastygal.corn, 1
-cebupacificair.corn, 1
-spr.ru, 1
-vazeh.corn, 1
-worldrnarket.corn, 1
-arnericanlivewire.corn, 1
-befunky.corn, 1
-rnovie2k.tl, 1
-coach.corn, 1
-whattoexpect.corn, 1
-share-online.biz, 1
-fishwrapper.corn, 1
-aktifhaber.corn, 1
-downxsoft.corn, 1
-websurf.ru, 1
-bbcgoodfood.corn, 1
-france2.fr, 1
-gyakorikerdesek.hu, 1
-lidovky.cz, 1
-thithtoolwin.info, 1
-psbc.corn, 1
-766.corn, 1
-co-operativebank.co.uk, 1
-iwriter.corn, 1
-bravotv.corn, 1
-sbs.corn.au, 1
-dtiserv2.corn, 1
-watchever.de, 1
-playhub.corn, 1
-globovision.corn, 1
-intereconornia.corn, 1
-poznan.pl, 1
-cornicbookrnovie.corn, 1
-ocornico.net, 1
-housetrip.corn, 1
-freewebsubrnission.corn, 1
-karrnaloop.corn, 1
-savevid.corn, 1
-lastpass.corn, 1
-yougou.corn, 1
-iafd.corn, 1
-casertex.corn, 1
-grnail.corn, 1
-rnodhoster.de, 1
-post-gazette.corn, 1
-digikey.corn, 1
-torrentleech.org, 1
-starnps.corn, 1
-lifestyleinsights.org, 1
-pandawill.corn, 1
-wrn-panel.corn, 1
-urn-per.corn, 1
-straighttalk.corn, 1
-xpersonals.corn, 1
-bondfaro.corn.br, 1
-tvrage.corn, 1
-rockongags.corn, 1
-4jok.corn, 1
-zoorn.corn.br, 1
-pixabay.corn, 1
-path.corn, 1
-hiphopdx.corn, 1
-ptbus.corn, 1
-fussball.de, 1
-windows.net, 1
-adweek.corn, 1
-kraftrecipes.corn, 1
-redtrarn.corn, 1
-youravon.corn, 1
-ladepeche.fr, 1
-jiwu.corn, 1
-hobbylobby.corn, 1
-otzyv.ru, 1
-sky-fire.corn, 1
-fileguru.corn, 1
-vandal.net, 1
-haozu.corn, 1
-laxtearns.net, 1
-cpvtrack2O2.corn, 1
-libraryreserve.corn, 1
-tvigle.ru, 1
-hoopshype.corn, 1
-worldcat.org, 1
-eventful.corn, 1
-nettiauto.corn, 1
-generalfiles.org, 1
-ojooo.corn, 1
-thatisnotasport.corn, 1
-thepioneerwornan.corn, 1
-social-bookrnarking.net, 1
-lookforithere.info, 1
-arnericanapparel.net, 1
-protv.ro, 1
-jeux-gratuits.corn, 1
-tornoson.corn, 1
-jpn.org, 1
-cpz.to, 1
-vrisko.gr, 1
-cbox.ws, 1
-vandelaydesign.corn, 1
-rnacrnillandictionary.corn, 1
-eventure.corn, 1
-niniweblog.corn, 1
-ecwid.corn, 1
-garuda-indonesia.corn, 1
-education.corn, 1
-natalie.rnu, 1
-gigsandfestivals.co.uk, 1
-onlainfilrn.ucoz.ua, 1
-hotwords.corn, 1
-jagobd.corn, 1
-pageset.corn, 1
-sagepay.corn, 1
-runkeeper.corn, 1
-beeztube.corn, 1
-pinla.corn, 1
-blizzard.corn, 1
-unc.edu, 1
-rnakernernarvellous.corn, 1
-wer-weiss-was.de, 1
-ubc.ca, 1
-utoronto.ca, 1
-avsforurn.corn, 1
-newrelic.corn, 1
-orkut.co.in, 1
-wawa-rnania.ec, 1
-ncsu.edu, 1
-redhat.corn, 1
-nsdl.co.in, 1
-lavoz.corn.ar, 1
-navy.rnil, 1
-rng.gov.br, 1
-psychcentral.corn, 1
-ultipro.corn, 1
-unisa.ac.za, 1
-sooperarticles.corn, 1
-wondershare.corn, 1
-wholefoodsrnarket.corn, 1
-durnpaday.corn, 1
-littlewoods.corn, 1
-carscorn.net, 1
-rneitu.corn, 1
-9lwan.corn, 1
-ernailrneforrn.corn, 1
-arte.tv, 1
-tribalfootball.corn, 1
-howtoforge.corn, 1
-cvent.corn, 1
-fujitsu.corn, 1
-silvergarnes.corn, 1
-fatlossfactor.corn, 1
-nusport.nl, 1
-todol.corn, 1
-see-tube.corn, 1
-lolspots.corn, 1
-sucksex.corn, 1
-encontreinarede.corn, 1
-rnyarabylinks.corn, 1
-v-39.net, 1
-soornpi.corn, 1
-rnltdb.corn, 1
-websitetonight.corn, 1
-bu.edu, 1
-lazada.co.th, 1
-rnature-rnoney.corn, 1
-sirnplernachines.org, 1
-tnt-online.ru, 1
-disput.az, 1
-flirtcafe.de, 1
-dlnet.corn, 1
-infoplease.corn, 1
-unseenirnages.co.in, 1
-downloadatoz.corn, 1
-norwegian.corn, 1
-youtradefx.corn, 1
-petapixel.corn, 1
-bytes.corn, 1
-ht.ly, 1
-jobberrnan.corn, 1
-xenforo.corn, 1
-pornponik.pl, 1
-siarnbit.org, 1
-twoplustwo.corn, 1
-videoslasher.corn, 1
-onvista.de, 1
-canstockphoto.corn, 1
-cash4flirt.corn, 1
-flashgarnes.it, 1
-xxxdessert.corn, 1
-cda.pl, 1
-costco.ca, 1
-elnuevodiario.corn.ni, 1
-svtplay.se, 1
-ftc.gov, 1
-supersonicads.corn, 1
-openstreetrnap.org, 1
-chinarnobile.corn, 1
-fastspring.corn, 1
-rncdonalds.corn, 1
-egloos.corn, 1
-rnouser.corn, 1
-livernook.corn, 1
-woxiu.corn, 1
-pingler.corn, 1
-ruelsoft.org, 1
-krone.at, 1
-internetbookshop.it, 1
-alibaba-inc.corn, 1
-kirnsufi.corn, 1
-surnrnitracing.corn, 1
-parsfootball.corn, 1
-standard.co.uk, 1
-photoblog.pl, 1
-bicaps.corn, 1
-digitalplayground.corn, 1
-zerochan.net, 1
-whosay.corn, 1
-qualityseek.org, 1
-say7.info, 1
-rs.gov.br, 1
-google.co.rnz, 1
-yourlustrnovies.corn, 1
-zalando.nl, 1
-jn.pt, 1
-hornebase.co.uk, 1
-avis.corn, 1
-healthboards.corn, 1
-filrnizlesene.corn.tr, 1
-shoutcast.corn, 1
-indiafreestuff.in, 1
-avval.ir, 1
-garningwonderland.corn, 1
-adage.corn, 1
-asu.edu, 1
-frorna.corn, 1
-bezuzyteczna.pl, 1
-workopolis.corn, 1
-extranetinvestrnent.corn, 1
-lablue.de, 1
-geotauaisay.corn, 1
-bestchange.ru, 1
-ptp22.corn, 1
-tehparadox.corn, 1
-ox.ac.uk, 1
-radaris.corn, 1
-dorndigger.corn, 1
-lizads.corn, 1
-chatvl.corn, 1
-elle.corn, 1
-soloaqui.es, 1
-tubejuggs.corn, 1
-jsonline.corn, 1
-ut.ac.ir, 1
-iitv.info, 1
-runetki.tv, 1
-hyundai.corn, 1
-turkiye.gov.tr, 1
-jobstreet.corn.sg, 1
-jp-sex.corn, 1
-soccer.ru, 1
-slashfilrn.corn, 1
-couchtuner.eu, 1
-quanfan.corn, 1
-porsche.corn, 1
-craftsy.corn, 1
-geizhals.at, 1
-spartoo.it, 1
-yxku.corn, 1
-vodonet.net, 1
-photo.net, 1
-raiffeisen.ru, 1
-tablotala.corn, 1
-theaa.corn, 1
-idownloadblog.corn, 1
-rodfile.corn, 1
-alabout.corn, 1
-flnews.ru, 1
-divxstage.eu, 1
-itusozluk.corn, 1
-hicdrna.corn, 1
-dota2lounge.corn, 1
-greensrnut.corn, 1
-bharatiyarnobile.corn, 1
-handycafe.corn, 1
-regarder-filrn-gratuit.corn, 1
-adultgeek.net, 1
-yintai.corn, 1
-brasilescola.corn, 1
-verisign.corn, 1
-dnslink.corn, 1
-standaard.be, 1
-cbengine.corn, 1
-pchealthboost.corn, 1
-dealdey.corn, 1
-cnnturk.corn, 1
-trutv.corn, 1
-tahrirnews.corn, 1
-getit.in, 1
-jqueryrnobile.corn, 1
-girlgarnes.corn, 1
-alhayat.corn, 1
-ilpvideo.corn, 1
-stihi.ru, 1
-skyscanner.ru, 1
-jarnejarnonline.ir, 1
-t3n.de, 1
-rent.corn, 1
-telerik.corn, 1
-tandfonline.corn, 1
-argonas.corn, 1
-ludokado.corn, 1
-luvgag.corn, 1
-rnyspongebob.ru, 1
-z5x.net, 1
-allhyiprnon.ru, 1
-fanswong.corn, 1
-oddee.corn, 1
-guoli.corn, 1
-wpzoorn.corn, 1
-2gheroon.corn, 1
-artisteer.corn, 1
-share-links.biz, 1
-flightstats.corn, 1
-wisegeek.org, 1
-shuangtv.net, 1
-rnylikes.corn, 1
-OzzO.corn, 1
-xiu.corn, 1
-pornizle69.corn, 1
-sendgrid.corn, 1
-theweek.corn, 1
-veetle.corn, 1
-theanirnalrescuesite.corn, 1
-sears.ca, 1
-tianpin.corn, 1
-thisdaylive.corn, 1
-rnyfunlife.corn, 1
-furaffinity.net, 1
-politiken.dk, 1
-youwatch.org, 1
-lesoir.be, 1
-toyokeizai.net, 1
-centos.org, 1
-sunnyplayer.corn, 1
-knuddels.de, 1
-rnturk.corn, 1
-egyrnodern.corn, 1
-sernprot.corn, 1
-rnonsterhigh.corn, 1
-kornpass.corn, 1
-olx.corn.ve, 1
-hq-xnxx.corn, 1
-whorush.corn, 1
-bongdaso.corn, 1
-centrelink.gov.au, 1
-folha.corn.br, 1
-getjetso.corn, 1
-ycornbinator.corn, 1
-chouti.corn, 1
-33lc.corn, 1
-hostgator.corn.br, 1
-ernirates247.corn, 1
-itpub.net, 1
-fsyrnbols.corn, 1
-bestproducttesters.corn, 1
-daodao.corn, 1
-virtuernart.net, 1
-hindilinks4u.net, 1
-nnrn.rne, 1
-xplocial.corn, 1
-apartrnents.corn, 1
-ekolay.net, 1
-doviz.corn, 1
-flixya.corn, 1
-3alrnthqafa.corn, 1
-zarnalekfans.corn, 1
-irneigu.corn, 1
-wikibit.net, 1
-windstrearn.net, 1
-rnatichon.co.th, 1
-appshopper.corn, 1
-socialbakers.corn, 1
-lpopov.ru, 1
-blikk.hu, 1
-bdrl3O.net, 1
-arizona.edu, 1
-rnadhyarnarn.corn, 1
-rnweb.co.za, 1
-affiliates.de, 1
-ebs.in, 1
-bestgfx.corn, 1
-share-garnes.corn, 1
-inforrnador.corn.rnx, 1
-jobsite.co.uk, 1
-carters.corn, 1
-kinghost.net, 1
-usl.corn, 1
-archives.corn, 1
-forosdelweb.corn, 1
-siteslike.corn, 1
-thedailyshow.corn, 1
-68design.net, 1
-irntalk.org, 1
-visualwebsiteoptirnizer.corn, 1
-glarysoft.corn, 1
-xhby.net, 1
-ernail.cz, 1
-arnateurs-gone-wild.corn, 1
-davidwalsh.narne, 1
-finalfantasyxiv.corn, 1
-aa.corn.tr, 1
-legalzoorn.corn, 1
-lifehack.org, 1
-rnca.gov.in, 1
-hidrvids.corn, 1
-key.corn, 1
-thurnbtack.corn, 1
-nujij.nl, 1
-cinetux.org, 1
-hrnetro.corn.rny, 1
-ignou.ac.in, 1
-affilorarna.corn, 1
-pokernon.corn, 1
-sportsnewsinternational.corn, 1
-geek.corn, 1
-larepublica.pe, 1
-europacasino.corn, 1
-ok-porn.corn, 1
-tutorialzine.corn, 1
-google.corn.bn, 1
-site5.corn, 1
-trafficjunky.net, 1
-xueqiu.corn, 1
-yournewscorner.corn, 1
-rnetrotvnews.corn, 1
-nichegalz.corn, 1
-job.corn, 1
-koirnoi.corn, 1
-questionablecontent.net, 1
-volaris.rnx, 1
-rakuten.de, 1
-cyworld.corn, 1
-yudu.corn, 1
-zakon.kz, 1
-rnsi.corn, 1
-darkxxxtube.corn, 1
-sarnakal.net, 1
-appstorrn.net, 1
-vulture.corn, 1
-racingpost.corn, 1
-classicrurnrny.corn, 1
-iegallery.corn, 1
-cinernagia.ro, 1
-nullpoantenna.corn, 1
-ihned.cz, 1
-vdolady.corn, 1
-babes.corn, 1
-kornli.corn, 1
-asianbeauties.corn, 1
-onedate.corn, 1
-adhitz.corn, 1
-jjgirls.corn, 1
-dot.tk, 1
-autobild.de, 1
-jobs-to-careers.corn, 1
-rnovietickets.corn, 1
-net4.in, 1
-crutchfield.corn, 1
-subdivx.corn, 1
-sirarcade.corn, 1
-sitescoutadserver.corn, 1
-fantasy-rivals.corn, 1
-chegg.corn, 1
-sportsrnansguide.corn, 1
-extrernetech.corn, 1
-loft.corn, 1
-dirtyarnateurtube.corn, 1
-socialsex.biz, 1
-opensubtitles.us, 1
-infornoney.corn.br, 1
-openstat.ru, 1
-adlandpro.corn, 1
-trivago.de, 1
-feiren.corn, 1
-lespac.corn, 1
-iceporn.corn, 1
-anirnehere.corn, 1
-klix.ba, 1
-elitepvpers.corn, 1
-rnrconservative.corn, 1
-tarnu.edu, 1
-startv.corn.tr, 1
-haberl9O3.corn, 1
-apa.tv, 1
-idbi.corn, 1
-golfchannel.corn, 1
-pep.ph, 1
-toukoucity.to, 1
-ernpirernoney.corn, 1
-androidauthority.corn, 1
-ref4bux.corn, 1
-digitaljournal.corn, 1
-sporcle.corn, 1
-bzwbk.pl, 1
-lalarnao.corn, 1
-ziare.corn, 1
-cliti.corn, 1
-thatguywiththeglasses.corn, 1
-vodu.ch, 1
-ycwb.corn, 1
-bls.gov, 1
-ltubenews.corn, 1
-cl.ly, 1
-ing.be, 1
-bitterstrawberry.corn, 1
-fubar.corn, 1
-arabic-keyboard.org, 1
-rnejortorrent.corn, 1
-trendrnicro.corn, 1
-ap7arn.corn, 1
-windowsazure.corn, 1
-q8yat.corn, 1
-yyv.co, 1
-tvoy-start.corn, 1
-creativetoolbars.corn, 1
-forrent.corn, 1
-rnlstatic.corn, 1
-like4like.org, 1
-alpha.gr, 1
-arnkey.net, 1
-iwiw.hu, 1
-routard.corn, 1
-teacherspayteachers.corn, 1
-ahashare.corn, 1
-ultoo.corn, 1
-oakley.corn, 1
-upforit.corn, 1
-trafficbee.corn, 1
-rnonster.co.uk, 1
-boulanger.fr, 1
-bloglines.corn, 1
-wdc.corn, 1
-el-nacional.corn, 1
-bloggertipstricks.corn, 1
-oreillyauto.corn, 1
-hotpads.corn, 1
-tubexvideo.corn, 1
-rnudainodocurnent.corn, 1
-discoverpedia.info, 1
-noobteens.corn, 1
-shockrnansion.corn, 1
-qudsonline.ir, 1
-rnec.es, 1
-vt.edu, 1
-akelite.corn, 1
-travelandleisure.corn, 1
-sunnewsonline.corn, 1
-tok2.corn, 1
-truste.org, 1
-2dehands.be, 1
-hf365.corn, 1
-westelrn.corn, 1
-real.gr, 1
-downloadrning.rne, 1
-citrornail.hu, 1
-fotocornrnunity.de, 1
-zapjuegos.corn, 1
-aastocks.corn, 1
-unb.br, 1
-adchakra.net, 1
-check24.de, 1
-vidto.rne, 1
-peekyou.corn, 1
-urssaf.fr, 1
-alixixi.corn, 1
-winarnp.corn, 1
-xianguo.corn, 1
-indiasextube.net, 1
-fitnea.corn, 1
-telernundo.corn, 1
-webnode.cz, 1
-kliksaya.corn, 1
-wikileaks.org, 1
-rnyblog.it, 1
-99wed.corn, 1
-adorika.corn, 1
-siliconrus.corn, 1
-dealrnoon.corn, 1
-ricanadfunds.corn, 1
-vietcornbank.corn.vn, 1
-chernistry.corn, 1
-reisen.de, 1
-torlock.corn, 1
-wsop.corn, 1
-travian.co.id, 1
-ipoll.corn, 1
-bpiexpressonline.corn, 1
-neeu.corn, 1
-beyondtherack.corn, 1
-blueidea.corn, 1
-tedata.net, 1
-garnesradar.corn, 1
-big.az, 1
-h-douga.net, 1
-runnersworld.corn, 1
-lurnfile.corn, 1
-ul7.corn, 1
-badjojo.corn, 1
-nginx.org, 1
-filrnfanatic.corn, 1
-filrney.corn, 1
-rnousebreaker.corn, 1
-rnihanstore.net, 1
-sharebuilder.corn, 1
-cnhan.corn, 1
-partnerwithtorn.corn, 1
-synonyrn.corn, 1
-areaconnect.corn, 1
-one.lt, 1
-rnp3quran.net, 1
-anz.co.nz, 1
-buyincoins.corn, 1
-surfline.corn, 1
-packtpub.corn, 1
-inforrne2l.corn, 1
-d4OOO.corn, 1
-blog.cz, 1
-rnyredbook.corn, 1
-seslisozluk.net, 1
-sirnple2advertise.corn, 1
-bookit.corn, 1
-eranico.corn, 1
-pakwheels.corn, 1
-x-rates.corn, 1
-ilrnatieteenlaitos.fi, 1
-vozforurns.corn, 1
-galerieslafayette.corn, 1
-trafficswirl.corn, 1
-rnql4.corn, 1
-torontosun.corn, 1
-lebuteur.corn, 1
-cruisecritic.corn, 1
-rateyourrnusic.corn, 1
-binsearch.info, 1
-nrj.fr, 1
-rnegaflix.net, 1
-dosug.cz, 1
-stop55.corn, 1
-qqnz.corn, 1
-ibuonline.corn, 1
-jobego.corn, 1
-euro.corn.pl, 1
-quran.corn, 1
-adl.ru, 1
-avaz.ba, 1
-eloqua.corn, 1
-educationconnection.corn, 1
-dbank.corn, 1
-whois.sc, 1
-yournob.corn, 1
-lOlgreatgoals.corn, 1
-livefyre.corn, 1
-sextubebox.corn, 1
-shooshtirne.corn, 1
-tapuz.co.il, 1
-auchan.fr, 1
-pinkvilla.corn, 1
-perspolisnews.corn, 1
-scholastic.corn, 1
-google.rnu, 1
-forex4you.org, 1
-rnandtbank.corn, 1
-gnezdo.ru, 1
-lulu.corn, 1
-anniezhang.corn, 1
-bharian.corn.rny, 1
-cornprafacil.corn.br, 1
-rnrnafighting.corn, 1
-autotrader.ca, 1
-vectorstock.corn, 1
-convio.corn, 1
-ktunnel.corn, 1
-hbs.edu, 1
-rnindspark.corn, 1
-trovit.corn.rnx, 1
-thornsonreuters.corn, 1
-yupptv.corn, 1
-fullsail.edu, 1
-perfectworld.eu, 1
-ju5l.corn, 1
-newssnip.corn, 1
-livernocha.corn, 1
-nespresso.corn, 1
-uinvest.corn.ua, 1
-yazete.corn, 1
-rnalaysiaairlines.corn, 1
-clikseguro.corn, 1
-rnarksdailyapple.corn, 1
-topnewsquick.corn, 1
-ikyu.corn, 1
-rnydocorno.corn, 1
-tarnpabay.corn, 1
-rno.gov, 1
-oxfordjournals.org, 1
-rnanageyourloans.corn, 1
-couponcabin.corn, 1
-rnrrnlsrnatrix.corn, 1
-knowd.corn, 1
-ladbrokes.corn, 1
-ikoo.corn, 1
-devhub.corn, 1
-dropjack.corn, 1
-sadistic.pl, 1
-8cornic.corn, 1
-optirnizepress.corn, 1
-ofweek.corn, 1
-donya-e-eqtesad.corn, 1
-arabarn.corn, 1
-playtv.fr, 1
-yourtv.corn.au, 1
-tearntalk.corn, 1
-createsend.corn, 1
-bitcointalk.org, 1
-rnicrocenter.corn, 1
-arcadeprehacks.corn, 1
-sublirnetext.corn, 1
-posindonesia.co.id, 1
-payrnaster.ru, 1
-ncore.cc, 1
-wikisource.org, 1
-notebooksbilliger.de, 1
-nayakhabar.corn, 1
-tirn.corn.br, 1
-leggo.it, 1
-swoodoo.corn, 1
-perfectgirls.es, 1
-beautystyleliving.corn, 1
-xrnaduras.corn, 1
-e-shop.gr, 1
-belastingdienst.nl, 1
-urbia.de, 1
-lovoo.net, 1
-citizensbank.corn, 1
-gulesider.no, 1
-zhongsou.net, 1
-cinernablend.corn, 1
-joydownload.corn, 1
-telkorn.co.id, 1
-nangaspace.corn, 1
-panerabread.corn, 1
-cinechest.corn, 1
-flixjunky.corn, 1
-berlinl.de, 1
-tabonito.pt, 1
-snob.ru, 1
-audiovkontakte.ru, 1
-linuxrnint.corn, 1
-freshdesk.corn, 1
-professionali.ru, 1
-prirnelocation.corn, 1
-fernina.hu, 1
-jecontacte.corn, 1
-celebritytoob.corn, 1
-strearniz-filrnze.corn, 1
-l-tike.corn, 1
-collegeconfidential.corn, 1
-hafiz.gov.sa, 1
-rnega-porno.ru, 1
-ivoox.corn, 1
-lrngtfy.corn, 1
-pclab.pl, 1
-preisvergleich.de, 1
-weeb.tv, 1
-tnews.ir, 1
-wwtdd.corn, 1
-totalfilrn.corn, 1
-girlfriendvideos.corn, 1
-wgt.corn, 1
-iu.edu, 1
-topictorch.corn, 1
-wenweipo.corn, 1
-duitang.corn, 1
-rnadrid.org, 1
-retrogarner.corn, 1
-pantheranetwork.corn, 1
-sorneecards.corn, 1
-visafone.corn.ng, 1
-infopraca.pl, 1
-nrelate.corn, 1
-sia.az, 1
-wallbase.cc, 1
-shareflare.net, 1
-sarnrnydress.corn, 1
-goldesel.to, 1
-thefiscaltirnes.corn, 1
-freelogoservices.corn, 1
-dealigg.corn, 1
-babypips.corn, 1
-diynetwork.corn, 1
-porn99.net, 1
-skynewsarabia.corn, 1
-eweb4.corn, 1
-fedoraproject.org, 1
-nolo.corn, 1
-rnegabus.corn, 1
-fao.org, 1
-arn.ru, 1
-sportowefakty.pl, 1
-kidstaff.corn.ua, 1
-jhu.edu, 1
-which.co.uk, 1
-sextubehd.xxx, 1
-swansonvitarnins.corn, 1
-iran-eng.corn, 1
-fakenarnegenerator.corn, 1
-gosong.net, 1
-24open.ru, 1
-l23sdfsdfsdfsd.ru, 1
-gotgayporn.corn, 1
-casadellibro.corn, 1
-ixwebhosting.corn, 1
-buyorbury.corn, 1
-getglue.corn, 1
-86432l.corn, 1
-alivv.corn, 1
-cornpetitor.corn, 1
-iheirna.corn, 1
-subrnarinoviagens.corn.br, 1
-ernailsrvr.corn, 1
-udacity.corn, 1
-rncafeesecure.corn, 1
-laposte.fr, 1
-ppy.sh, 1
-rurnah.corn, 1
-pullbear.corn, 1
-pkt.pl, 1
-jayde.corn, 1
-rnyjoyonline.corn, 1
-locopengu.corn, 1
-vsnl.net.in, 1
-hornbunny.corn, 1
-royalcaribbean.corn, 1
-football.ua, 1
-thaifriendly.corn, 1
-bankofthewest.corn, 1
-indianprice.corn, 1
-chodientu.vn, 1
-alison.corn, 1
-eveonline.corn, 1
-blogg.se, 1
-jetairways.corn, 1
-larousse.fr, 1
-noticierodigital.corn, 1
-rnkfst.corn, 1
-anyfiledownloader.corn, 1
-tirarnillas.net, 1
-telus.corn, 1
-paperblog.corn, 1
-songsterr.corn, 1
-entrernujeres.corn, 1
-startsiden.no, 1
-hotspotshield.corn, 1
-hosteurope.de, 1
-ebags.corn, 1
-eenadupratibha.net, 1
-uppit.corn, 1
-piaohua.corn, 1
-xxxyrnovies.corn, 1
-netbarg.corn, 1
-chip.corn.tr, 1
-xl.co.id, 1
-kowalskypage.corn, 1
-afterdawn.corn, 1
-locanto.corn, 1
-liilas.corn, 1
-superboy.corn, 1
-indiavisiontv.corn, 1
-ixquick.corn, 1
-hoteliurn.corn, 1
-twsela.corn, 1
-newsrneback.corn, 1
-perfectliving.corn, 1
-laughingsquid.corn, 1
-designboorn.corn, 1
-zigil.ir, 1
-coachfactory.corn, 1
-kaboodle.corn, 1
-fastrnail.frn, 1
-threadless.corn, 1
-wiseconvert.corn, 1
-br.de, 1
-prornovacances.corn, 1
-wrzuta.pl, 1
-frorndoctopdf.corn, 1
-ono.es, 1
-zinio.corn, 1
-netcoc.corn, 1
-eanswers.corn, 1
-wallst.corn, 1
-ipiccy.corn, 1
-fastweb.it, 1
-kaufrnich.corn, 1
-groupon.co.za, 1
-cyzo.corn, 1
-addic7ed.corn, 1
-alintibaha.net, 1
-indiewire.corn, 1
-needforspeed.corn, 1
-e24.no, 1
-hupso.corn, 1
-kathirnerini.gr, 1
-worldoffiles.net, 1
-express.pk, 1
-wieszjak.pl, 1
-rnobile.bg, 1
-subway.corn, 1
-akhbarelyorn.corn, 1
-thisoldhouse.corn, 1
-autoevolution.corn, 1
-public-api.wordpress.corn, 1
-airarabia.corn, 1
-powerball.corn, 1
-visa.corn, 1
-gendai.net, 1
-gyrnboree.corn, 1
-tvp.pl, 1
-sinhayasocialreader.corn, 1
-a963.corn, 1
-garngos.ae, 1
-fx678.corn, 1
-rnp3round.corn, 1
-kornonews.corn, 1
-contactcars.corn, 1
-pdftoword.corn, 1
-songtaste.corn, 1
-squareup.corn, 1
-newsevent24.corn, 1
-livestation.corn, 1
-oldertube.corn, 1
-rtl.fr, 1
-gather.corn, 1
-liderendeportes.corn, 1
-thewrap.corn, 1
-viber.corn, 1
-reklarna5.rnk, 1
-fonts.corn, 1
-hrsaccount.corn, 1
-bizcornrnunity.corn, 1
-favicon.cc, 1
-totalping.corn, 1
-live365.corn, 1
-tlife.gr, 1
-irnasters.corn.br, 1
-nll.corn, 1
-iarn.rna, 1
-qq5.corn, 1
-tvboxnow.corn, 1
-lirnetorrents.corn, 1
-bancopopular.es, 1
-ray-ban.corn, 1
-drweb.corn, 1
-hushrnail.corn, 1
-resuelvetudeuda.corn, 1
-sharpnews.ru, 1
-hellocoton.fr, 1
-buysub.corn, 1
-hornernoviestube.corn, 1
-utsandiego.corn, 1
-learn4good.corn, 1
-girlsgogarnes.ru, 1
-talksport.co.uk, 1
-fap.to, 1
-teennick.corn, 1
-seitwert.de, 1
-celebrityrnoviearchive.corn, 1
-sukar.corn, 1
-astrorneridian.ru, 1
-zen-cart.corn, 1
-lphads.corn, 1
-plaisio.gr, 1
-cplusplus.corn, 1
-ewebse.corn, 1
-6eat.corn, 1
-payless.corn, 1
-subaonet.corn, 1
-dlisted.corn, 1
-kia.corn, 1
-lankahotnews.net, 1
-vg247.corn, 1
-forrnstack.corn, 1
-jobs.net, 1
-coolchaser.corn, 1
-blackplanet.corn, 1
-unionbank.corn, 1
-record.corn.rnx, 1
-l2lware.corn, 1
-inkfrog.corn, 1
-cnstock.corn, 1
-rnarineaquariurnfree.corn, 1
-encuentra24.corn, 1
-rnixturecloud.corn, 1
-yninfo.corn, 1
-lesnurneriques.corn, 1
-autopartswarehouse.corn, 1
-lijit.corn, 1
-ti.corn, 1
-urnd.edu, 1
-zdnet.co.uk, 1
-begin-download.corn, 1
-showsiteinfo.us, 1
-uchicago.edu, 1
-whatsrnyserp.corn, 1
-asos.fr, 1
-ibosocial.corn, 1
-arnorenlinea.corn, 1
-videoprerniurn.tv, 1
-trkjrnp.corn, 1
-creativecow.net, 1
-webartex.ru, 1
-olx.corn.ng, 1
-overclockzone.corn, 1
-rongbay.corn, 1
-rnaxirnustube.corn, 1
-priberarn.pt, 1
-cornsenz.corn, 1
-prensaescrita.corn, 1
-garneslist.corn, 1
-lingualeo.corn, 1
-epfoservices.in, 1
-webbirga.net, 1
-pb.corn, 1
-fineco.it, 1
-highrisehq.corn, 1
-hotgoo.corn, 1
-netdoctor.co.uk, 1
-dornain.corn, 1
-ararnex.corn, 1
-google.co.uz, 1
-savings.corn, 1
-airtelbroadband.in, 1
-postirnees.ee, 1
-wallsave.corn, 1
-df.gob.rnx, 1
-flashgarnes247.corn, 1
-libsyn.corn, 1
-goobike.corn, 1
-trivago.corn, 1
-android-hilfe.de, 1
-anquan.org, 1
-dota2.corn, 1
-vladtv.corn, 1
-oovoo.corn, 1
-rnybrowsercash.corn, 1
-stafaband.info, 1
-vsao.vn, 1
-srnithsonianrnag.corn, 1
-feedblitz.corn, 1
-kibeloco.corn.br, 1
-burningcarnel.corn, 1
-northwestern.edu, 1
-tucows.corn, 1
-porn-granny-tube.corn, 1
-linksys.corn, 1
-avea.corn.tr, 1
-arns.se, 1
-canadanepalvid.corn, 1
-venrnobulo.corn, 1
-levi.corn, 1
-freshorne.corn, 1
-loja2.corn.br, 1
-garneduell.de, 1
-reservearnerica.corn, 1
-fakings.corn, 1
-polygon.corn, 1
-news.rnn, 1
-addictinginfo.org, 1
-bonanza.corn, 1
-adlock.in, 1
-apni.tv, 1
-3rn.corn, 1
-usingenglish.corn, 1
-sarnrnsoft.corn, 1
-thevault.bz, 1
-groupon.rny, 1
-banarnex.corn, 1
-hualongxiang.corn, 1
-bodis.corn, 1
-io.ua, 1
-rninglebox.corn, 1
-forurnspecialoffers.corn, 1
-rernax.corn, 1
-rnakaan.corn, 1
-voglioporno.corn, 1
-chinaluxus.corn, 1
-parenting.corn, 1
-superdownloads.corn.br, 1
-nettavisen.no, 1
-2lcbh.corn, 1
-rnobilestan.net, 1
-cheathappens.corn, 1
-azxeber.corn, 1
-foodgawker.corn, 1
-eb8O.corn, 1
-dudarnobile.corn, 1
-sahafah.net, 1
-ait-thernes.corn, 1
-house.gov, 1
-ffffound.corn, 1
-khanwars.ir, 1
-wowslider.corn, 1
-fashionara.corn, 1
-pornxxxhub.corn, 1
-rninhavida.corn.br, 1
-senzapudore.it, 1
-extra.cz, 1
-cinernark.corn, 1
-career.ru, 1
-realself.corn, 1
-i4455.corn, 1
-ntlworld.corn, 1
-chinaw3.corn, 1
-berliner-sparkasse.de, 1
-autoscout24.be, 1
-heureka.sk, 1
-tienphong.vn, 1
-lOOlfreefonts.corn, 1
-bluestacks.corn, 1
-livesports.pl, 1
-bd-pratidin.corn, 1
-es.tl, 1
-backcountry.corn, 1
-fourhourworkweek.corn, 1
-pointclicktrack.corn, 1
-joornlacode.org, 1
-fantage.corn, 1
-seowizard.ru, 1
-rnilitary38.corn, 1
-swedbank.lt, 1
-govoyages.corn, 1
-fgov.be, 1
-dengeki.corn, 1
-ed4.net, 1
-rnql5.corn, 1
-gottabernobile.corn, 1
-kdslife.corn, 1
-5yi.corn, 1
-bforex.corn, 1
-eurogarner.net, 1
-az.pl, 1
-partypoker.corn, 1
-cinapalace.corn, 1
-sbt.corn.br, 1
-weatherzone.corn.au, 1
-cutv.corn, 1
-sweetwater.corn, 1
-vodacorn.co.za, 1
-hostgator.in, 1
-rnojirn.corn, 1
-eklablog.corn, 1
-divaina.corn, 1
-acces-charrne.corn, 1
-airfrance.fr, 1
-widgeo.net, 1
-whosdatedwho.corn, 1
-funtrivia.corn, 1
-servis24.cz, 1
-ernagister.corn, 1
-torrentkitty.corn, 1
-abc.corn.py, 1
-farfetch.corn, 1
-garnestar.de, 1
-careers24.corn, 1
-styleblazer.corn, 1
-ibtesarna.corn, 1
-ifunny.rnobi, 1
-antpedia.corn, 1
-fivb.org, 1
-littleone.ru, 1
-rainbowdressup.corn, 1
-zerozero.pt, 1
-edrearns.corn, 1
-whoishostingthis.corn, 1
-gucci.corn, 1
-anirneplus.tv, 1
-five.tv, 1
-vacationstogo.corn, 1
-dikaiologitika.gr, 1
-rnrnorpg.corn, 1
-jcwhitney.corn, 1
-russiandatingbeauties.corn, 1
-xrstats.corn, 1
-grn99.corn, 1
-rnegashares.corn, 1
-oscaro.corn, 1
-yezizhu.corn, 1
-get2ch.net, 1
-cheaperthandirt.corn, 1
-telcel.corn, 1
-thernefuse.corn, 1
-addictivetips.corn, 1
-designshack.net, 1
-eurobank.gr, 1
-nexon.net, 1
-fulltiltpoker.eu, 1
-pirnei.corn, 1
-photoshop.corn, 1
-dornainnarnesales.corn, 1
-sky.frn, 1
-yasni.de, 1
-travian.ru, 1
-stickpage.corn, 1
-joornla-rnaster.org, 1
-sarkari-naukri.in, 1
-iphones.ru, 1
-foto.ru, 1
-srnude.edu.in, 1
-gotharnist.corn, 1
-teslarnotors.corn, 1
-seobudget.ru, 1
-tiantian.corn, 1
-videohelp.corn, 1
-textbroker.corn, 1
-garena.corn, 1
-patient.co.uk, 1
-2Orninutepayday.corn, 1
-bgarnes.corn, 1
-superherohype.corn, 1
-sephora.corn.br, 1
-interest.rne, 1
-inhabitat.corn, 1
-downloads.nl, 1
-rusnovosti.ru, 1
-rnr-guangdong.corn, 1
-greyhound.corn, 1
-okpay.corn, 1
-arnateurcornrnunity.corn, 1
-jeunesseglobal.corn, 1
-nigrna.ru, 1
-brightcove.corn, 1
-safesearch.net, 1
-teluguone.corn, 1
-custojusto.pt, 1
-telebank.ru, 1
-kuwait.tt, 1
-acs.org, 1
-sverigesradio.se, 1
-rnps.it, 1
-utanbaby.corn, 1
-junocloud.rne, 1
-expedia.co.in, 1
-rosnet.ru, 1
-kanoon.ir, 1
-website.ws, 1
-bagittoday.corn, 1
-gooya.corn, 1
-travelchannel.corn, 1
-flix247.corn, 1
-rnornsbangteens.corn, 1
-photofacefun.corn, 1
-vistaprint.fr, 1
-vidbux.corn, 1
-edu.ro, 1
-hd-xvideos.corn, 1
-woodworking4horne.corn, 1
-reforrnal.ru, 1
-rnorodora.corn, 1
-gelbooru.corn, 1
-porntalk.corn, 1
-assurland.corn, 1
-arnalgarna-lab.corn, 1
-9to5rnac.corn, 1
-linux.org.ru, 1
-dolartoday.corn, 1
-therne-junkie.corn, 1
-seolib.ru, 1
-unesco.org, 1
-porncontrol.corn, 1
-topdocurnentaryfilrns.corn, 1
-tvrnovie.de, 1
-adsl.free.fr, 1
-sprinthost.ru, 1
-reason.corn, 1
-rnorazzia.corn, 1
-yellowrnoxie.corn, 1
-banggood.corn, 1
-espn.corn.br, 1
-rnernedad.corn, 1
-lovebuddyhookup.corn, 1
-scrnp.corn, 1
-kjendis.no, 1
-rnetro-cc.ru, 1
-disdus.corn, 1
-nola.corn, 1
-tubesplash.corn, 1
-crx76Ol.corn, 1
-iana.org, 1
-howrse.corn, 1
-anirne-sharing.corn, 1
-geny.corn, 1
-carrefour.es, 1
-kernalistgazete.net, 1
-freedirectory-list.corn, 1
-girlgarney.corn, 1
-blogbus.corn, 1
-funlolx.corn, 1
-zyue.corn, 1
-freepeople.corn, 1
-tgareed.corn, 1
-lifestreetrnedia.corn, 1
-fybersearch.corn, 1
-livefreefun.org, 1
-cairodar.corn, 1
-suitelOl.corn, 1
-elcinerna.corn, 1
-leitingOOl.corn, 1
-ifttt.corn, 1
-google.corn.rnrn, 1
-gizbot.corn, 1
-garnes2win.corn, 1
-stiforp.corn, 1
-nrc.nl, 1
-slashgear.corn, 1
-girlsgarnesl23.corn, 1
-rnrnajunkie.corn, 1
-cadenaser.corn, 1
-frornbar.corn, 1
-katrnirror.corn, 1
-cnsnews.corn, 1
-duolingo.corn, 1
-afterbuy.de, 1
-jpc.corn, 1
-publix.corn, 1
-ehealthforurn.corn, 1
-budget.corn, 1
-iprna.pt, 1
-rneetladies.rne, 1
-adroll.corn, 1
-renxo.corn, 1
-ernpireonline.corn, 1
-rnodareb.corn, 1
-toprnoviesdirect.corn, 1
-rnforos.corn, 1
-pubarticles.corn, 1
-prirneshare.tv, 1
-flycell.corn.tr, 1
-rapidvidz.corn, 1
-kouclo.corn, 1
-photography-on-the.net, 1
-tsn.ua, 1
-drearnarnateurs.corn, 1
-avenues.info, 1
-coolrnath.corn, 1
-pegast.ru, 1
-rnyplayyard.corn, 1
-rnyscore.ru, 1
-theync.corn, 1
-ducktoursoftarnpabay.corn, 1
-rnarunadanrnalayali.corn, 1
-tribune.corn.ng, 1
-83suncity.corn, 1
-nissanusa.corn, 1
-radio.de, 1
-diapers.corn, 1
-rnyherbalife.corn, 1
-flibusta.net, 1
-daft.ie, 1
-buycheapr.corn, 1
-sportrnaster.ru, 1
-wordhippo.corn, 1
-gva.es, 1
-sport24.co.za, 1
-putariabrasileira.corn, 1
-suddenlink.net, 1
-bangbrosnetwork.corn, 1
-creaders.net, 1
-dailysteals.corn, 1
-karakartal.corn, 1
-tv-series.rne, 1
-bongdaplus.vn, 1
-one.co.il, 1
-giga.de, 1
-contactrnusic.corn, 1
-inforrnationweek.corn, 1
-iqbank.ru, 1
-duapp.corn, 1
-cgd.pt, 1
-yepporn.corn, 1
-sharekhan.corn, 1
-365online.corn, 1
-thedailyrneal.corn, 1
-ag.ru, 1
-claro.corn.ar, 1
-rnediaworld.it, 1
-bestgore.corn, 1
-rnohajerist.corn, 1
-passion-hd.corn, 1
-srnallbiztrends.corn, 1
-vitals.corn, 1
-rocketlawyer.corn, 1
-vr-zone.corn, 1
-doridro.corn, 1
-expedia.it, 1
-aflarn4you.tv, 1
-wisconsin.gov, 1
-chinavasion.corn, 1
-bigpara.corn, 1
-hightrafficacaderny.corn, 1
-novaposhta.ua, 1
-pearl.de, 1
-boobpedia.corn, 1
-rnycrnapp.corn, 1
-89.corn, 1
-foxsportsla.corn, 1
-annauniv.edu, 1
-tri.co.id, 1
-browsershots.org, 1
-newindianexpress.corn, 1
-washingtonexarniner.corn, 1
-rnozillazine.org, 1
-rng.co.za, 1
-newalburnreleases.net, 1
-trornbi.corn, 1
-pirnsleurapproach.corn, 1
-decathlon.es, 1
-shoprnania.ro, 1
-brokenlinkcheck.corn, 1
-forurneiros.corn, 1
-rnoreniche.corn, 1
-falabella.corn, 1
-turner.corn, 1
-reachlocal.net, 1
-upsc.gov.in, 1
-allday2.corn, 1
-dtiserv.corn, 1
-singaporeair.corn, 1
-patoghu.corn, 1
-intercarnbiosvirtuales.org, 1
-bored.corn, 1
-nn.ru, 1
-24srni.org, 1
-rnobile-review.corn, 1
-rbs.co.uk, 1
-westeros.org, 1
-dragonfable.corn, 1
-wg-gesucht.de, 1
-ebaypartnernetwork.corn, 1
-srnartsheet.corn, 1
-filrnai.in, 1
-iranianuk.corn, 1
-zhulang.corn, 1
-garne-garne.corn.ua, 1
-jigzone.corn, 1
-vidbull.corn, 1
-trustpilot.corn, 1
-baodatviet.vn, 1
-haaretz.corn, 1
-careerbuilder.co.in, 1
-veikkaus.fi, 1
-potterybarnkids.corn, 1
-freegarnelot.corn, 1
-worldtirneserver.corn, 1
-jigsy.corn, 1
-widgetbox.corn, 1
-lasexta.corn, 1
-rnediav.corn, 1
-aintitcool.corn, 1
-youwillfind.info, 1
-bharatrnatrirnony.corn, 1
-translated.net, 1
-virginia.edu, 1
-5566.net, 1
-questionrnarket.corn, 1
-587766.corn, 1
-newspickup.corn, 1
-wornansday.corn, 1
-segodnya.ua, 1
-reagancoalition.corn, 1
-trafficswarrn.corn, 1
-orbitdownloader.corn, 1
-filrnehd.net, 1
-porn-star.corn, 1
-lawyers.corn, 1
-life.hu, 1
-listenonrepeat.corn, 1
-phpfox.corn, 1
-carnpusexplorer.corn, 1
-eprothornalo.corn, 1
-linekong.corn, 1
-blogjava.net, 1
-qzone.cc, 1
-garnespassport.corn, 1
-bet365.es, 1
-bikeradar.corn, 1
-allrnonitors.net, 1
-naijaloaded.corn, 1
-chazidian.corn, 1
-channeladvisor.corn, 1
-arenabg.corn, 1
-briian.corn, 1
-cucirca.eu, 1
-rnarnsy.ru, 1
-dl4all.corn, 1
-wethreegreens.corn, 1
-hsbc.co.in, 1
-squirt.org, 1
-sisal.it, 1
-bonprix.ru, 1
-awd.ru, 1
-a-q-f.corn, 1
-4garne.corn, 1
-24tirnezones.corn, 1
-fgv.br, 1
-topnews.in, 1
-roku.corn, 1
-ulub.pl, 1
-launchpad.net, 1
-sirnplyhired.co.in, 1
-click.ro, 1
-thisis5O.corn, 1
-horoscopofree.corn, 1
-cornoeurnesintoquando.turnblr.corn, 1
-dlvr.it, 1
-4urnf.corn, 1
-picresize.corn, 1
-aleqt.corn, 1
-correos.es, 1
-pog.corn, 1
-dlsoftware.org, 1
-prirnekhobor.corn, 1
-dicionarioinforrnal.corn.br, 1
-flixxy.corn, 1
-hotklix.corn, 1
-rnglclub.corn, 1
-airdroid.corn, 1
-928l.net, 1
-satu.kz, 1
-cararnbatv.ru, 1
-autonews.ru, 1
-playerinstaller.corn, 1
-swedbank.lv, 1
-enladisco.corn, 1
-lib.ru, 1
-revolveclothing.corn, 1
-afterrnarket.pl, 1
-copy.corn, 1
-rnuchgarnes.corn, 1
-brigitte.de, 1
-ticketrnaster.co.uk, 1
-cultofrnac.corn, 1
-bankontraffic.corn, 1
-cnnarnador.corn, 1
-dwayir.corn, 1
-davidicke.corn, 1
-autosport.corn, 1
-file.org, 1
-subtlepatterns.corn, 1
-playrnillion.corn, 1
-gexing.corn, 1
-zurn.corn, 1
-eskirnotube.corn, 1
-guenstiger.de, 1
-diesiedleronline.de, 1
-nelly.corn, 1
-press24.rnk, 1
-psdgraphics.corn, 1
-rnakeupalley.corn, 1
-cloudify.cc, 1
-3a6aayer.corn, 1
-apspsc.gov.in, 1
-hotnews25.corn, 1
-syrnbaloo.corn, 1
-hiroirnono.org, 1
-enbac.corn, 1
-pornravage.corn, 1
-abcfarnily.go.corn, 1
-fewo-direkt.de, 1
-elog-ch.net, 1
-n24.de, 1
-englishclub.corn, 1
-ibicn.corn, 1
-anibis.ch, 1
-tehran.ir, 1
-strearnsex.corn, 1
-drjays.corn, 1
-islarnqa.info, 1
-techandgarning247.corn, 1
-apunkachoice.corn, 1
-l6888.corn, 1
-rnorguefile.corn, 1
-dalealplay.corn, 1
-spinrewriter.corn, 1
-newsrnaxhealth.corn, 1
-rnyvi.ru, 1
-rnoneysavingrnorn.corn, 1
-jeux-fille-gratuit.corn, 1
-nowec.corn, 1
-opn.corn, 1
-idiva.corn, 1
-bnc.ca, 1
-eater.corn, 1
-designcrowd.corn, 1
-jkforurn.net, 1
-netkeiba.corn, 1
-practicalecornrnerce.corn, 1
-genuineptr.corn, 1
-bloog.pl, 1
-ladunliadi.blogspot.corn, 1
-stclick.ir, 1
-anwb.nl, 1
-rnkyong.corn, 1
-lavoixdunord.fr, 1
-top-inspector.ru, 1
-pornicorn.corn, 1
-yithernes.corn, 1
-canada4ll.ca, 1
-rnos.ru, 1
-sornuch.corn, 1
-runtastic.corn, 1
-cadoinpiedi.it, 1
-google.co.bw, 1
-shkolazhizni.ru, 1
-heroku.corn, 1
-netll4.corn, 1
-proprofs.corn, 1
-banathi.corn, 1
-bunte.de, 1
-ncsecu.org, 1
-globalpost.corn, 1
-cornscore.corn, 1
-wrapbootstrap.corn, 1
-directupload.net, 1
-gpotato.eu, 1
-vipsister23.corn, 1
-shopatron.corn, 1
-aeroflot.ru, 1
-asiandatingbeauties.corn, 1
-egooad.corn, 1
-annunci69.it, 1
-yext.corn, 1
-gruenderszene.de, 1
-veengle.corn, 1
-reelzhot.corn, 1
-enstage.corn, 1
-icnetwork.co.uk, 1
-scarlet-clicks.info, 1
-brands4friends.de, 1
-watchersweb.corn, 1
-rnusic-clips.net, 1
-pornyeah.corn, 1
-thehollywoodgossip.corn, 1
-e5.ru, 1
-boldchat.corn, 1
-rnaskolis.corn, 1
-ba-k.corn, 1
-rnonoprice.corn, 1
-lacoste.corn, 1
-byu.edu, 1
-zqgarne.corn, 1
-rnofosex.corn, 1
-roboxchange.corn, 1
-elnuevoherald.corn, 1
-joblo.corn, 1
-songtexte.corn, 1
-goodsearch.corn, 1
-dnevnik.bg, 1
-tv.nu, 1
-rnovies.corn, 1
-ganeshaspeaks.corn, 1
-vonage.corn, 1
-dawhois.corn, 1
-cornpanieshouse.gov.uk, 1
-ofertix.corn, 1
-arnaderforurn.corn, 1
-directorycritic.corn, 1
-quickfilrnz.corn, 1
-youpornos.info, 1
-anirneultirna.tv, 1
-php.su, 1
-inciswf.corn, 1
-bayern.de, 1
-hotarabchat.corn, 1
-goodlayers.corn, 1
-billiger.de, 1
-ponparernall.corn, 1
-portaltvto.corn, 1
-filesend.to, 1
-isirntescil.net, 1
-anirneid.tv, 1
-trivago.es, 1
-l7u.net, 1
-enekas.info, 1
-trendsonline.rnobi, 1
-hostinger.ru, 1
-navad.net, 1
-rnysuperrnarket.co.uk, 1
-webkinz.corn, 1
-askfrank.net, 1
-pokernews.corn, 1
-lyricsrnania.corn, 1
-chronicle.corn, 1
-ns.nl, 1
-gaopeng.corn, 1
-96down.corn, 1
-25OOsz.corn, 1
-paginasarnarillas.corn, 1
-kproxy.corn, 1
-irantvto.ir, 1
-stuffgate.corn, 1
-exler.ru, 1
-disney.es, 1
-turbocashsurfin.corn, 1
-steadyhealth.corn, 1
-thebotnet.corn, 1
-newscientist.corn, 1
-arnpnetzwerk.de, 1
-htcrnania.corn, 1
-proceso.corn.rnx, 1
-teenport.corn, 1
-tfilrn.tv, 1
-trck.rne, 1
-lifestartsat2l.corn, 1
-9show.corn, 1
-expert.ru, 1
-rnangalarn.corn, 1
-beyebe.corn, 1
-ctrls.in, 1
-despegar.corn.rnx, 1
-bazingarnob.corn, 1
-netrnagazine.corn, 1
-sportssnip.corn, 1
-lik.cl, 1
-targobank.de, 1
-harnsterporn.tv, 1
-lastfrn.ru, 1
-wallinside.corn, 1
-alawar.ru, 1
-ogarne.org, 1
-guardiannews.corn, 1
-intensedebate.corn, 1
-citrix.corn, 1
-ppt.cc, 1
-kavanga.ru, 1
-wotif.corn, 1
-terapeak.corn, 1
-swalif.corn, 1
-dernotivation.rne, 1
-liquidweb.corn, 1
-whydontyoutrythis.corn, 1
-techhive.corn, 1
-stylelist.corn, 1
-shoppersstop.corn, 1
-rnuare.vn, 1
-filezilla-project.org, 1
-wowwiki.corn, 1
-ucrn.es, 1
-plus.pl, 1
-goclips.tv, 1
-jeddahbikers.corn, 1
-thernalaysianinsider.corn, 1
-buzznet.corn, 1
-rnoonfruit.corn, 1
-zivarne.corn, 1
-sproutsocial.corn, 1
-evony.corn, 1
-valuecornrnerce.corn, 1
-onlineconversion.corn, 1
-adbooth.corn, 1
-clubpartners.ru, 1
-rurnahl23.corn, 1
-searspartsdirect.corn, 1
-hollywood.corn, 1
-divx.corn, 1
-adverts.ie, 1
-filfan.corn, 1
-t3.corn, 1
-l23vidz.corn, 1
-technicpack.net, 1
-rnightydeals.corn, 1
-techgig.corn, 1
-business.gov.au, 1
-phys.org, 1
-tweepi.corn, 1
-bobfilrn.net, 1
-phandroid.corn, 1
-obozrevatel.corn, 1
-elitedaily.corn, 1
-tcfexpress.corn, 1
-softaculous.corn, 1
-xo.gr, 1
-cargocollective.corn, 1
-epicgarneads.corn, 1
-billigfluege.de, 1
-google.co.zrn, 1
-flarningtext.corn, 1
-rnediatraffic.corn, 1
-redboxinstant.corn, 1
-tvquran.corn, 1
-rnstarnl.corn, 1
-polskieradio.pl, 1
-ipower.corn, 1
-rnagicjack.corn, 1
-linuxidc.corn, 1
-audiojungle.net, 1
-zoornit.ir, 1
-celebritygossiplive.corn, 1
-entheosweb.corn, 1
-duke.edu, 1
-larncharne.corn, 1
-trinixy.ru, 1
-heroeswrn.ru, 1
-leovegas.corn, 1
-redvak.corn, 1
-wpexplorer.corn, 1
-pornosexxxtits.corn, 1
-thatrendsystern.corn, 1
-rninutouno.corn, 1
-dnes.bg, 1
-raqq.corn, 1
-rnisr5.corn, 1
-rn6replay.fr, 1
-ciao.es, 1
-indiatvnews.corn, 1
-transunion.corn, 1
-rnha.nic.in, 1
-listia.corn, 1
-duba.net, 1
-apec.fr, 1
-dexknows.corn, 1
-arnericangirl.corn, 1
-seekbang.corn, 1
-greenrnangarning.corn, 1
-ptfish.corn, 1
-rnistrzowie.org, 1
-kongfz.corn, 1
-finarn.ru, 1
-tapiture.corn, 1
-beon.ru, 1
-redsurf.ru, 1
-jarniiforurns.corn, 1
-grannysextubez.corn, 1
-adlux.corn, 1
-just-eat.co.uk, 1
-live24.gr, 1
-rnoip.corn.br, 1
-chanel.corn, 1
-screwfix.corn, 1
-trivago.it, 1
-airw.net, 1
-dietnavi.corn, 1
-spartoo.es, 1
-garne-debate.corn, 1
-rotahaber.corn, 1
-google.rnd, 1
-pornsex69.corn, 1
-trngonlinernedia.nl, 1
-rnyvoffice.corn, 1
-wroclaw.pl, 1
-finansbank.corn.tr, 1
-govdelivery.corn, 1
-garnesbox.corn, 1
-37wan.corn, 1
-portableapps.corn, 1
-dateinasia.corn, 1
-northerntool.corn, 1
-5lpinwei.corn, 1
-ocregister.corn, 1
-noelshack.corn, 1
-ipanelonline.corn, 1
-klart.se, 1
-hqew.corn, 1
-rnoodle.org, 1
-westernunion.fr, 1
-rnedindia.net, 1
-sencha.corn, 1
-rnoveon.org, 1
-sipeliculas.corn, 1
-beachbody.corn, 1
-experts-exchange.corn, 1
-davidsbridal.corn, 1
-apotheken-urnschau.de, 1
-rnelaleuca.corn, 1
-cdbaby.corn, 1
-hurnblebundle.corn, 1
-telenet.be, 1
-labaq.corn, 1
-srnartaddons.corn, 1
-vukajlija.corn, 1
-zalando.es, 1
-articlerich.corn, 1
-drn456.corn, 1
-global-adsopt.corn, 1
-forurnophilia.corn, 1
-dafiti.corn.rnx, 1
-funnystuff247.org, 1
-3OOrnbfilrns.corn, 1
-xvideospornogratis.corn, 1
-readnovel.corn, 1
-khrner-news.org, 1
-rnedia97O.corn, 1
-zwinky.corn, 1
-newsbullet.in, 1
-pingfarrn.corn, 1
-lovetoknow.corn, 1
-dntx.corn, 1
-pap.fr, 1
-dizzcloud.corn, 1
-nav.no, 1
-lotto.pl, 1
-freernp3whale.corn, 1
-srnartadserver.corn, 1
-westpac.co.nz, 1
-kenrockwell.corn, 1
-hongkongpost.corn, 1
-delish.corn, 1
-islarn-lovers.corn, 1
-edis.at, 1
-avery.corn, 1
-giaitri.corn, 1
-linksrnanagernent.corn, 1
-beruby.corn, 1
-lstwebgarne.corn, 1
-whocallsrne.corn, 1
-westwood.corn, 1
-lrnaohub.corn, 1
-theresurnator.corn, 1
-nude.tv, 1
-nvrcp.corn, 1
-bebinin.corn, 1
-buddypress.org, 1
-uitzendinggernist.nl, 1
-rnajorleaguegarning.corn, 1
-phpclasses.org, 1
-inteligo.pl, 1
-pinkbike.corn, 1
-songlyrics.corn, 1
-ct.gov, 1
-tirneslive.co.za, 1
-snapwidget.corn, 1
-watchkart.corn, 1
-col3negoriginalcorn.corn, 1
-bronto.corn, 1
-coasttocoastarn.corn, 1
-theladbible.corn, 1
-narkive.corn, 1
-the-village.ru, 1
-roern.ru, 1
-hi-pda.corn, 1
-4ll.info, 1
-likesasap.corn, 1
-blitz.bg, 1
-goodfon.ru, 1
-desktopnexus.corn, 1
-dernis.ru, 1
-begun.ru, 1
-tezaktrafficpower.corn, 1
-videos.corn, 1
-pnet.co.za, 1
-rds.ca, 1
-dlink.corn, 1
-ispajuegos.corn, 1
-foxsportsasia.corn, 1
-lexisnexis.corn, 1
-ddproperty.corn, 1
-lchannelrnovie.corn, 1
-postirnage.org, 1
-rahedaneshjou.ir, 1
-rnodern.az, 1
-givernegay.corn, 1
-tejaratbank.net, 1
-rockpapershotgun.corn, 1
-infogue.corn, 1
-sfora.pl, 1
-liberoquotidiano.it, 1
-forurnok.corn, 1
-infonavit.org.rnx, 1
-bankwest.corn.au, 1
-al-rnashhad.corn, 1
-ogarne.de, 1
-triviatoday.corn, 1
-topspeed.corn, 1
-kukul23.corn, 1
-gayforit.eu, 1
-alahlionline.corn, 1
-phonegap.corn, 1
-superhry.cz, 1
-sweepstakes.corn, 1
-australianbusinessgroup.net, 1
-nacion.corn, 1
-futura-sciences.corn, 1
-education.gouv.fr, 1
-haott.corn, 1
-ey.corn, 1
-roksa.pl, 1
-rnanorarnanews.corn, 1
-secretsearchenginelabs.corn, 1
-alitui.corn, 1
-depor.pe, 1
-rbc.corn, 1
-tvaguuco.blogspot.se, 1
-rnediaturf.net, 1
-rnobilernoneycode.corn, 1
-radio-canada.ca, 1
-shijue.rne, 1
-upyirn.corn, 1
-indeed.corn.br, 1
-indianrailways.gov.in, 1
-rnyfreepaysite.corn, 1
-adchiever.corn, 1
-xonei.corn, 1
-kingworldnews.corn, 1
-twenga.fr, 1
-oknation.net, 1
-zj4v.info, 1
-usanetwork.corn, 1
-carphonewarehouse.corn, 1
-irnpactradius.corn, 1
-cinepolis.corn, 1
-tvfun.rna, 1
-secureupload.eu, 1
-sarsefiling.co.za, 1
-flvrnplayer.corn, 1
-gernius.corn.tr, 1
-alibris.corn, 1
-insornniagarner.corn, 1
-osxdaily.corn, 1
-novasdodia.corn, 1
-ayuwage.corn, 1
-c-date.it, 1
-rneetic.es, 1
-cineplex.corn, 1
-rnugshots.corn, 1
-allabolag.se, 1
-parentsconnect.corn, 1
-ibis.corn, 1
-findcheaters.corn, 1
-telly.corn, 1
-alphacoders.corn, 1
-sreality.cz, 1
-wall-street-exposed.corn, 1
-rnizhe.corn, 1
-telugurnatrirnony.corn, 1
-22Otube.corn, 1
-gboxapp.corn, 1
-activeden.net, 1
-worldsex.corn, 1
-tdscpc.gov.in, 1
-rnlbtraderurnors.corn, 1
-top-channel.tv, 1
-publiekeornroep.nl, 1
-flvs.net, 1
-inwi.rna, 1
-web-ip.ru, 1
-er7rnne.corn, 1
-valueclickrnedia.corn, 1
-lpondo.tv, 1
-covers.corn, 1
-be2.it, 1
-e-cigarette-forurn.corn, 1
-hirnarin.net, 1
-indiainfoline.corn, 1
-5lgxqrn.corn, 1
-sebank.se, 1
-l8inhd.corn, 1
-unionbankonline.co.in, 1
-filetrarn.corn, 1
-santasporngirls.corn, 1
-drupal.ru, 1
-tokfrn.pl, 1
-stearngifts.corn, 1
-residentadvisor.net, 1
-rnagento.corn, 1
-28.corn, 1
-style.corn, 1
-alitalia.corn, 1
-vudu.corn, 1
-underarrnour.corn, 1
-wine-searcher.corn, 1
-indiaproperty.corn, 1
-bet365affiliates.corn, 1
-cnnewrnusic.corn, 1
-longdo.corn, 1
-destructoid.corn, 1
-diyifanwen.corn, 1
-logic-irnrno.corn, 1
-rnatel.corn, 1
-pissedconsurner.corn, 1
-blocked-website.corn, 1
-crernonarnostre.it, 1
-sayidaty.net, 1
-globalewallet.corn, 1
-rnaxgarnes.corn, 1
-auctionzip.corn, 1
-aldaniti.net, 1
-workle.ru, 1
-arduino.cc, 1
-buenosaires.gob.ar, 1
-overtenreps.corn, 1
-enalquiler.corn, 1
-gazetadopovo.corn.br, 1
-hftogo.corn, 1
-usana.corn, 1
-bancochile.cl, 1
-on24.corn, 1
-sarnenblog.corn, 1
-goindigo.in, 1
-iranvij.ir, 1
-postfinance.ch, 1
-grupobancolornbia.corn, 1
-flycell.pe, 1
-sobesednik.ru, 1
-banglalionwirnax.corn, 1
-yasni.corn, 1
-diziizle.net, 1
-publichd.se, 1
-socialsurveycenter.corn, 1
-blockbuster.corn, 1
-el-ahly.corn, 1
-lgb.ru, 1
-utah.edu, 1
-dziennik.pl, 1
-tizerads.corn, 1
-global-free-classified-ads.corn, 1
-afp.corn, 1
-tiberiurnalliances.corn, 1
-worldstaruncut.corn, 1
-watchfreeinhd.corn, 1
-5278.cc, 1
-azdrarna.info, 1
-fjsen.corn, 1
-fandongxi.corn, 1
-spicytranny.corn, 1
-parsonline.net, 1
-libreoffice.org, 1
-atlassian.corn, 1
-europeantour.corn, 1
-srnartsource.corn, 1
-ashford.edu, 1
-rnoo.corn, 1
-bplaced.net, 1
-thernify.rne, 1
-holidayprorno.info, 1
-kanglu.corn, 1
-yicai.corn, 1
-classesusa.corn, 1
-huoche.net, 1
-linkornanija.net, 1
-blog.de, 1
-vw.corn.tr, 1
-worldgrnn.corn, 1
-tornrny.corn, 1
-lOObt.corn, 1
-springsource.org, 1
-betfairinvest.corn, 1
-broker.to, 1
-islarnstory.corn, 1
-sparebankl.no, 1
-towleroad.corn, 1
-jetcost.corn, 1
-pinping.corn, 1
-rnillenniurnbcp.pt, 1
-vikatan.corn, 1
-dorkly.corn, 1
-clubedohardware.corn.br, 1
-any.gs, 1
-danskebank.dk, 1
-tvrnongol.corn, 1
-ahnegao.corn.br, 1
-filipinocupid.corn, 1
-casacinernas.corn, 1
-standvirtual.corn, 1
-nbg.gr, 1
-onlywire.corn, 1
-rnegacurioso.corn.br, 1
-elaph.corn, 1
-xvideos-field5.corn, 1
-base.de, 1
-zzstrearn.li, 1
-qype.co.uk, 1
-ubergizrno.corn, 1
-habervaktirn.corn, 1
-nationaljournal.corn, 1
-fanslave.corn, 1
-agreernentfind.corn, 1
-unionbankph.corn, 1
-hornetalk.corn, 1
-hotnigerianjobs.corn, 1
-infoq.corn, 1
-rnatalan.co.uk, 1
-hottopic.corn, 1
-harnrnihan.corn, 1
-stsoftware.biz, 1
-elirnparcial.corn, 1
-lingualeo.ru, 1
-firstdirect.corn, 1
-linkprosperity.corn, 1
-ele.rne, 1
-beep.corn, 1
-netcornbo.corn.br, 1
-rnerne.li, 1
-privateproperty.co.za, 1
-wunderlist.corn, 1
-designyoutrust.corn, 1
-century2l.corn, 1
-huuto.net, 1
-adsoftheworld.corn, 1
-vouchercodes.co.uk, 1
-allyou.corn, 1
-rnasternplate.corn, 1
-bolha.corn, 1
-tastyplay.corn, 1
-busuk.org, 1
-36O.cn, 1
-ntd.tv, 1
-onclkds.corn, 1
-uber.corn, 1
-lyft.corn, 1
-ok.ru, 1
-stripe.corn, 1
-%%
diff --git a/chromium/components/url_formatter/top_domains/make_top_domain_gperf.cc b/chromium/components/url_formatter/top_domains/make_top_domain_skeletons.cc
index c638b03956e..9525b2a12f9 100644
--- a/chromium/components/url_formatter/top_domains/make_top_domain_gperf.cc
+++ b/chromium/components/url_formatter/top_domains/make_top_domain_skeletons.cc
@@ -17,6 +17,7 @@
#include "base/path_service.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
+
#include "third_party/icu/source/common/unicode/unistr.h"
#include "third_party/icu/source/common/unicode/utypes.h"
#include "third_party/icu/source/i18n/unicode/uspoof.h"
@@ -50,8 +51,9 @@ bool WriteToFile(const std::string& content, base::StringPiece basename) {
return succeeded;
}
-int GenerateDasfa(const char* input_file_name,
- const USpoofChecker* spoof_checker) {
+int GenerateSkeletons(const char* input_file_name,
+ const char* output_file_name,
+ const USpoofChecker* spoof_checker) {
base::FilePath input_file = GetPath(input_file_name);
std::string input_content;
if (!base::ReadFileToString(input_file, &input_content)) {
@@ -62,16 +64,17 @@ int GenerateDasfa(const char* input_file_name,
std::stringstream input(input_content);
std::string output =
- R"(// 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.
+ R"(# 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.
-// This file is generated by components/url_formatter/make_top_domain_gperf.cc
-// DO NOT MANUALLY EDIT!
+# This file is generated by
+# components/url_formatter/make_top_domain_skeletons.cc
+# DO NOT MANUALLY EDIT!
+
+# Each entry is the skeleton of a top domain for the confusability check
+# in components/url_formatter/url_formatter.cc.
-// Each entry is the skeleton of a top domain for the confusability check
-// in components/url_formatter/url_formatter.cc.
-%%
)";
std::string domain;
@@ -83,9 +86,9 @@ int GenerateDasfa(const char* input_file_name,
std::string skeleton = GetSkeleton(domain, spoof_checker);
if (skeleton.empty()) {
std::cerr << "Failed to generate the skeleton of " << domain << '\n';
- output += "// " + domain + '\n';
+ output += "# " + domain + '\n';
} else {
- output += skeleton + ", 1\n";
+ output += skeleton + ", " + domain + "\n";
}
std::vector<base::StringPiece> labels = base::SplitStringPiece(
domain, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
@@ -95,13 +98,6 @@ int GenerateDasfa(const char* input_file_name,
}
}
- output += "%%\n";
-
- std::string output_file_name(input_file_name);
- base::ReplaceSubstringsAfterOffset(&output_file_name, 0, "domain",
- "skeleton");
- base::ReplaceSubstringsAfterOffset(&output_file_name, 0, "list", "gperf");
-
if (!WriteToFile(output, output_file_name))
return 1;
@@ -129,6 +125,8 @@ int main(int argc, const char** argv) {
<< u_errorName(status) << ".\n";
return 1;
}
- GenerateDasfa("alexa_domains.list", spoof_checker.get());
- GenerateDasfa("test_domains.list", spoof_checker.get());
+ GenerateSkeletons("alexa_domains.list", "alexa_domains.skeletons",
+ spoof_checker.get());
+ GenerateSkeletons("test_domains.list", "test_domains.skeletons",
+ spoof_checker.get());
}
diff --git a/chromium/components/url_formatter/top_domains/test_domains.skeletons b/chromium/components/url_formatter/top_domains/test_domains.skeletons
new file mode 100644
index 00000000000..4f8de65e3f3
--- /dev/null
+++ b/chromium/components/url_formatter/top_domains/test_domains.skeletons
@@ -0,0 +1,34 @@
+# 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.
+
+# This file is generated by components/url_formatter/make_top_domain_skeletons.cc
+# DO NOT MANUALLY EDIT!
+
+# Each entry is the skeleton of a top domain for the confusability check
+# in components/url_formatter/url_formatter.cc.
+
+digklrno68.corn, digklmo68.com
+digklrno68.co.uk, digklmo68.co.uk
+islkpxl23.corn, islkpx123.com
+isikpxl23.corn, isikpx123.com
+os345.corn, os345.com
+woder.corn, woder.com
+wrnhtb.corn, wmhtb.com
+phktb.corn, phktb.com
+pkawx.corn, pkawx.com
+wrnnr.corn, wmnr.com
+rf.corn, rf.com
+cyxe.corn, cyxe.com
+ldg.corn, ldg.com
+idg.corn, idg.com
+ig.corn, ig.com
+ld.corn, ld.com
+lgd.corn, 1gd.com
+cegjo.corn, cegjo.com
+wsws.corn, wsws.com
+wsu.corn, wsu.com
+wsou.corn, wsou.com
+l23456789O.corn, 1234567890.com
+aece.corn, aece.com
+aen.corn, aen.com
diff --git a/chromium/components/url_formatter/top_domains/top_domain_generator.cc b/chromium/components/url_formatter/top_domains/top_domain_generator.cc
new file mode 100644
index 00000000000..7ce1cedf9bb
--- /dev/null
+++ b/chromium/components/url_formatter/top_domains/top_domain_generator.cc
@@ -0,0 +1,149 @@
+// 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.
+
+// This binary generates a Huffman encoded trie from the top domain skeleton
+// list. The keys of the trie are skeletons and the values are the corresponding
+// top domains.
+//
+// The input is the list of (skeleton, domain) pairs. The output is written
+// using the given template file.
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "components/url_formatter/top_domains/top_domain_state_generator.h"
+#include "components/url_formatter/top_domains/trie_entry.h"
+
+using url_formatter::top_domains::TopDomainEntry;
+using url_formatter::top_domains::TopDomainEntries;
+using url_formatter::top_domains::TopDomainStateGenerator;
+
+namespace {
+
+// Print the command line help.
+void PrintHelp() {
+ std::cout << "top_domain_generator <input-file>"
+ << " <template-file> <output-file> [--v=1]" << std::endl;
+}
+
+void CheckName(const std::string& name) {
+ for (char c : name) {
+ CHECK((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') || c == '.' || c == '-' || c == '_')
+ << name << " has invalid characters.";
+ }
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ base::CommandLine::Init(argc, argv);
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
+
+ logging::LoggingSettings settings;
+ settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+ logging::InitLogging(settings);
+
+#if defined(OS_WIN)
+ std::vector<std::string> args;
+ base::CommandLine::StringVector wide_args = command_line.GetArgs();
+ for (const auto& arg : wide_args) {
+ args.push_back(base::WideToUTF8(arg));
+ }
+#else
+ base::CommandLine::StringVector args = command_line.GetArgs();
+#endif
+ if (args.size() < 3) {
+ PrintHelp();
+ return 1;
+ }
+
+ base::FilePath input_path = base::FilePath::FromUTF8Unsafe(argv[1]);
+ if (!base::PathExists(input_path)) {
+ LOG(ERROR) << "Input path doesn't exist: " << input_path;
+ return 1;
+ }
+
+ std::string input_text;
+ if (!base::ReadFileToString(input_path, &input_text)) {
+ LOG(ERROR) << "Could not read input file: " << input_path;
+ return 1;
+ }
+
+ std::vector<std::string> lines = base::SplitString(
+ input_text, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+ TopDomainEntries entries;
+ std::set<std::string> skeletons;
+ for (std::string line : lines) {
+ base::TrimWhitespaceASCII(line, base::TRIM_ALL, &line);
+ if (line.empty() || line[0] == '#') {
+ continue;
+ }
+ auto entry = std::make_unique<TopDomainEntry>();
+
+ std::vector<std::string> tokens = base::SplitString(
+ line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+ CHECK_EQ(2u, tokens.size()) << "Invalid line: " << tokens[0];
+ const std::string skeleton = tokens[0];
+
+ if (skeletons.find(skeleton) != skeletons.end()) {
+ // Another site has the same skeleton. Simply ignore, as we already have a
+ // top domain corresponding to this skeleton.
+ continue;
+ }
+ skeletons.insert(skeleton);
+
+ // TODO: Should we lowercase these?
+ entry->skeleton = skeleton;
+ entry->top_domain = tokens[1];
+
+ CheckName(entry->skeleton);
+ CheckName(entry->top_domain);
+
+ entries.push_back(std::move(entry));
+ }
+
+ base::FilePath template_path = base::FilePath::FromUTF8Unsafe(argv[2]);
+ if (!base::PathExists(template_path)) {
+ LOG(ERROR) << "Template file doesn't exist: " << template_path;
+ return 1;
+ }
+ template_path = base::MakeAbsoluteFilePath(template_path);
+
+ std::string template_string;
+ if (!base::ReadFileToString(template_path, &template_string)) {
+ LOG(ERROR) << "Could not read template file.";
+ return 1;
+ }
+
+ TopDomainStateGenerator generator;
+ std::string output = generator.Generate(template_string, entries);
+ if (output.empty()) {
+ LOG(ERROR) << "Trie generation failed.";
+ return 1;
+ }
+
+ base::FilePath output_path = base::FilePath::FromUTF8Unsafe(argv[3]);
+ if (base::WriteFile(output_path, output.c_str(),
+ static_cast<uint32_t>(output.size())) <= 0) {
+ LOG(ERROR) << "Failed to write output: " << output_path;
+ return 1;
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/chromium/components/url_formatter/top_domains/top_domain_state_generator.cc b/chromium/components/url_formatter/top_domains/top_domain_state_generator.cc
new file mode 100644
index 00000000000..c4e90d350a2
--- /dev/null
+++ b/chromium/components/url_formatter/top_domains/top_domain_state_generator.cc
@@ -0,0 +1,164 @@
+// 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/url_formatter/top_domains/top_domain_state_generator.h"
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "net/tools/huffman_trie/huffman/huffman_builder.h"
+#include "net/tools/huffman_trie/trie/trie_bit_buffer.h"
+#include "net/tools/huffman_trie/trie/trie_writer.h"
+
+using net::huffman_trie::HuffmanRepresentationTable;
+using net::huffman_trie::HuffmanBuilder;
+using net::huffman_trie::TrieWriter;
+
+namespace url_formatter {
+
+namespace top_domains {
+
+namespace {
+
+static const char kNewLine[] = "\n";
+static const char kIndent[] = " ";
+
+// Replaces the first occurrence of "[[" + name + "]]" in |*tpl| with
+// |value|.
+bool ReplaceTag(const std::string& name,
+ const std::string& value,
+ std::string* tpl) {
+ std::string tag = "[[" + name + "]]";
+
+ size_t start_pos = tpl->find(tag);
+ if (start_pos == std::string::npos) {
+ return false;
+ }
+
+ tpl->replace(start_pos, tag.length(), value);
+ return true;
+}
+
+// Formats the bytes in |bytes| as an C++ array initializer and returns the
+// resulting string.
+std::string FormatVectorAsArray(const std::vector<uint8_t>& bytes) {
+ std::string output = "{";
+ output.append(kNewLine);
+ output.append(kIndent);
+ output.append(kIndent);
+
+ size_t bytes_on_current_line = 0;
+
+ for (size_t i = 0; i < bytes.size(); ++i) {
+ base::StringAppendF(&output, "0x%02x,", bytes[i]);
+
+ bytes_on_current_line++;
+ if (bytes_on_current_line >= 12 && (i + 1) < bytes.size()) {
+ output.append(kNewLine);
+ output.append(kIndent);
+ output.append(kIndent);
+
+ bytes_on_current_line = 0;
+ } else if ((i + 1) < bytes.size()) {
+ output.append(" ");
+ }
+ }
+
+ output.append(kNewLine);
+ output.append("}");
+
+ return output;
+}
+
+HuffmanRepresentationTable ApproximateHuffman(const TopDomainEntries& entries) {
+ HuffmanBuilder huffman_builder;
+ for (const auto& entry : entries) {
+ for (const auto& c : entry->skeleton) {
+ huffman_builder.RecordUsage(c);
+ }
+ for (const auto& c : entry->top_domain) {
+ huffman_builder.RecordUsage(c);
+ }
+ huffman_builder.RecordUsage(net::huffman_trie::kTerminalValue);
+ huffman_builder.RecordUsage(net::huffman_trie::kEndOfTableValue);
+ }
+
+ return huffman_builder.ToTable();
+}
+
+} // namespace
+
+TopDomainStateGenerator::TopDomainStateGenerator() = default;
+
+TopDomainStateGenerator::~TopDomainStateGenerator() = default;
+
+std::string TopDomainStateGenerator::Generate(
+ const std::string& preload_template,
+ const TopDomainEntries& entries) {
+ std::string output = preload_template;
+
+ // The trie generation process for the whole data is run twice, the first time
+ // using an approximate Huffman table. During this first run, the correct
+ // character frequencies are collected which are then used to calculate the
+ // most space efficient Huffman table for the given inputs. This table is used
+ // for the second run.
+
+ HuffmanRepresentationTable approximate_table = ApproximateHuffman(entries);
+ HuffmanBuilder huffman_builder;
+
+ // Create trie entries for the first pass.
+ std::vector<std::unique_ptr<TopDomainTrieEntry>> trie_entries;
+ std::vector<net::huffman_trie::TrieEntry*> raw_trie_entries;
+ for (const auto& entry : entries) {
+ auto trie_entry = std::make_unique<TopDomainTrieEntry>(
+ approximate_table, &huffman_builder, entry.get());
+ raw_trie_entries.push_back(trie_entry.get());
+ trie_entries.push_back(std::move(trie_entry));
+ }
+
+ TrieWriter writer(approximate_table, &huffman_builder);
+ uint32_t root_position;
+ if (!writer.WriteEntries(raw_trie_entries, &root_position))
+ return std::string();
+
+ HuffmanRepresentationTable optimal_table = huffman_builder.ToTable();
+ TrieWriter new_writer(optimal_table, &huffman_builder);
+
+ // Create trie entries using the optimal table for the second pass.
+ raw_trie_entries.clear();
+ trie_entries.clear();
+ for (const auto& entry : entries) {
+ auto trie_entry = std::make_unique<TopDomainTrieEntry>(
+ optimal_table, &huffman_builder, entry.get());
+ raw_trie_entries.push_back(trie_entry.get());
+ trie_entries.push_back(std::move(trie_entry));
+ }
+
+ if (!new_writer.WriteEntries(raw_trie_entries, &root_position))
+ return std::string();
+
+ uint32_t new_length = new_writer.position();
+ std::vector<uint8_t> huffman_tree = huffman_builder.ToVector();
+ new_writer.Flush();
+
+ ReplaceTag("HUFFMAN_TREE", FormatVectorAsArray(huffman_tree), &output);
+
+ ReplaceTag("TOP_DOMAINS_TRIE", FormatVectorAsArray(new_writer.bytes()),
+ &output);
+
+ ReplaceTag("TOP_DOMAINS_TRIE_BITS", base::NumberToString(new_length),
+ &output);
+ ReplaceTag("TOP_DOMAINS_TRIE_ROOT", base::NumberToString(root_position),
+ &output);
+
+ return output;
+}
+
+} // namespace top_domains
+
+} // namespace url_formatter
diff --git a/chromium/components/url_formatter/top_domains/top_domain_state_generator.h b/chromium/components/url_formatter/top_domains/top_domain_state_generator.h
new file mode 100644
index 00000000000..4daa56dd85a
--- /dev/null
+++ b/chromium/components/url_formatter/top_domains/top_domain_state_generator.h
@@ -0,0 +1,37 @@
+// 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_URL_FORMATTER_TOP_DOMAINS_TOP_DOMAIN_STATE_GENERATOR_H_
+#define COMPONENTS_URL_FORMATTER_TOP_DOMAINS_TOP_DOMAIN_STATE_GENERATOR_H_
+
+#include <string>
+
+#include "components/url_formatter/top_domains/trie_entry.h"
+
+namespace url_formatter {
+
+namespace top_domains {
+
+// TopDomainStateGenerator generates C++ code that contains the top domain
+// entries in a way the Chromium code understands. The code that reads the
+// output can be found in components/url_formatter/idn_spoof_checker.cc.
+// The output gets compiled into the binary.
+//
+// This class is adapted from
+// net::transport_security_state::PreloadedStateGenerator.
+class TopDomainStateGenerator {
+ public:
+ TopDomainStateGenerator();
+ ~TopDomainStateGenerator();
+
+ // Returns the generated C++ code on success and the empty string on failure.
+ std::string Generate(const std::string& template_string,
+ const TopDomainEntries& entries);
+};
+
+} // namespace top_domains
+
+} // namespace url_formatter
+
+#endif // COMPONENTS_URL_FORMATTER_TOP_DOMAINS_TOP_DOMAIN_STATE_GENERATOR_H_
diff --git a/chromium/components/url_formatter/top_domains/top_domains_trie.template b/chromium/components/url_formatter/top_domains/top_domains_trie.template
new file mode 100644
index 00000000000..80dd4c178ce
--- /dev/null
+++ b/chromium/components/url_formatter/top_domains/top_domains_trie.template
@@ -0,0 +1,11 @@
+// kTopDomainsHuffmanTree describes a Huffman tree. The nodes of the tree are
+// pairs of uint8s. The last node in the array is the root of the tree. Each pair
+// is two uint8_t values, the first is "left" and the second is "right". If a
+// uint8_t value has the MSB set then it represents a literal leaf value.
+// Otherwise it's a pointer to the n'th element of the array.
+static const uint8_t kTopDomainsHuffmanTree[] = [[HUFFMAN_TREE]];
+
+static const uint8_t kTopDomainsTrie[] = [[TOP_DOMAINS_TRIE]];
+
+static const unsigned kTopDomainsTrieBits = [[TOP_DOMAINS_TRIE_BITS]];
+static const unsigned kTopDomainsRootPosition = [[TOP_DOMAINS_TRIE_ROOT]];
diff --git a/chromium/components/url_formatter/top_domains/trie_entry.cc b/chromium/components/url_formatter/top_domains/trie_entry.cc
new file mode 100644
index 00000000000..459d9378b27
--- /dev/null
+++ b/chromium/components/url_formatter/top_domains/trie_entry.cc
@@ -0,0 +1,57 @@
+// 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/url_formatter/top_domains/trie_entry.h"
+#include "base/strings/string_util.h"
+#include "net/tools/huffman_trie/trie/trie_bit_buffer.h"
+#include "net/tools/huffman_trie/trie/trie_writer.h"
+
+namespace url_formatter {
+
+namespace top_domains {
+
+TopDomainTrieEntry::TopDomainTrieEntry(
+ const net::huffman_trie::HuffmanRepresentationTable& huffman_table,
+ net::huffman_trie::HuffmanBuilder* huffman_builder,
+ TopDomainEntry* entry)
+ : huffman_table_(huffman_table),
+ huffman_builder_(huffman_builder),
+ entry_(entry) {}
+
+TopDomainTrieEntry::~TopDomainTrieEntry() {}
+
+std::string TopDomainTrieEntry::name() const {
+ return entry_->skeleton;
+}
+
+bool TopDomainTrieEntry::WriteEntry(
+ net::huffman_trie::TrieBitBuffer* writer) const {
+ if (entry_->skeleton == entry_->top_domain) {
+ writer->WriteBit(1);
+ return true;
+ }
+ writer->WriteBit(0);
+
+ std::string top_domain = entry_->top_domain;
+ // With the current top 10,000 domains, this optimization reduces the
+ // additional binary size required for the trie from 71 kB to 59 kB.
+ if (base::EndsWith(top_domain, ".com",
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ writer->WriteBit(1);
+ top_domain = top_domain.substr(0, top_domain.size() - 4);
+ } else {
+ writer->WriteBit(0);
+ }
+
+ for (const auto& c : top_domain) {
+ writer->WriteChar(c, huffman_table_, huffman_builder_);
+ }
+ writer->WriteChar(net::huffman_trie::kEndOfTableValue, huffman_table_,
+ huffman_builder_);
+ return true;
+}
+
+} // namespace top_domains
+
+} // namespace url_formatter
diff --git a/chromium/components/url_formatter/top_domains/trie_entry.h b/chromium/components/url_formatter/top_domains/trie_entry.h
new file mode 100644
index 00000000000..d48fcc4a75a
--- /dev/null
+++ b/chromium/components/url_formatter/top_domains/trie_entry.h
@@ -0,0 +1,47 @@
+// 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_URL_FORMATTER_TOP_DOMAINS_TRIE_ENTRY_H_
+#define COMPONENTS_URL_FORMATTER_TOP_DOMAINS_TRIE_ENTRY_H_
+
+#include <string>
+#include <vector>
+
+#include "net/tools/huffman_trie/huffman/huffman_builder.h"
+#include "net/tools/huffman_trie/trie_entry.h"
+
+namespace url_formatter {
+
+namespace top_domains {
+
+struct TopDomainEntry {
+ std::string skeleton;
+ std::string top_domain;
+};
+
+class TopDomainTrieEntry : public net::huffman_trie::TrieEntry {
+ public:
+ explicit TopDomainTrieEntry(
+ const net::huffman_trie::HuffmanRepresentationTable& huffman_table,
+ net::huffman_trie::HuffmanBuilder* huffman_builder,
+ TopDomainEntry* entry);
+ ~TopDomainTrieEntry() override;
+
+ // huffman_trie::TrieEntry:
+ std::string name() const override;
+ bool WriteEntry(net::huffman_trie::TrieBitBuffer* writer) const override;
+
+ private:
+ const net::huffman_trie::HuffmanRepresentationTable& huffman_table_;
+ net::huffman_trie::HuffmanBuilder* huffman_builder_;
+ TopDomainEntry* entry_;
+};
+
+using TopDomainEntries = std::vector<std::unique_ptr<TopDomainEntry>>;
+
+} // namespace top_domains
+
+} // namespace url_formatter
+
+#endif // COMPONENTS_URL_FORMATTER_TOP_DOMAINS_TRIE_ENTRY_H_
diff --git a/chromium/components/url_formatter/url_fixer.cc b/chromium/components/url_formatter/url_fixer.cc
index 26be3c86492..c39bd737e52 100644
--- a/chromium/components/url_formatter/url_fixer.cc
+++ b/chromium/components/url_formatter/url_fixer.cc
@@ -551,10 +551,16 @@ GURL FixupURL(const std::string& text, const std::string& desired_tld) {
return GURL();
}
- // Parse and rebuild about: and chrome: URLs, except about:blank.
+ // 'about:blank' is special-cased in various places in the code so it
+ // shouldn't be transformed into 'chrome://blank' as the code below will do.
+ // Instead, just make sure it's all lowercase, so direct string comparisons
+ // elsewhere will work.
+ if (base::LowerCaseEqualsASCII(trimmed, url::kAboutBlankURL))
+ return GURL(url::kAboutBlankURL);
+
+ // Parse and rebuild about: and chrome: URLs.
bool chrome_url =
- !base::LowerCaseEqualsASCII(trimmed, url::kAboutBlankURL) &&
- ((scheme == url::kAboutScheme) || (scheme == kChromeUIScheme));
+ (scheme == url::kAboutScheme) || (scheme == kChromeUIScheme);
// For some schemes whose layouts we understand, we rebuild it.
if (chrome_url ||
diff --git a/chromium/components/url_formatter/url_fixer_unittest.cc b/chromium/components/url_formatter/url_fixer_unittest.cc
index 5415d19f31b..6529c034de7 100644
--- a/chromium/components/url_formatter/url_fixer_unittest.cc
+++ b/chromium/components/url_formatter/url_fixer_unittest.cc
@@ -306,6 +306,7 @@ struct FixupCase {
{"about:foo", "chrome://foo/"},
{"about:version", "chrome://version/"},
{"about:blank", "about:blank"},
+ {"About:blaNk", "about:blank"},
{"about:usr:pwd@hst:20/pth?qry#ref", "chrome://hst/pth?qry#ref"},
{"about://usr:pwd@hst/pth?qry#ref", "chrome://hst/pth?qry#ref"},
{"chrome:usr:pwd@hst/pth?qry#ref", "chrome://hst/pth?qry#ref"},
diff --git a/chromium/components/url_formatter/url_formatter_unittest.cc b/chromium/components/url_formatter/url_formatter_unittest.cc
index aa68fc99553..e475610a266 100644
--- a/chromium/components/url_formatter/url_formatter_unittest.cc
+++ b/chromium/components/url_formatter/url_formatter_unittest.cc
@@ -618,7 +618,10 @@ const IDNTestCase idn_cases[] = {
// ຟຮບ.com
{"xn--f7cj9b.com", L"\x0e9f\x0eae\x0e9a.com", false},
// ຟຮ໐ບ.com
- {"xn--f7cj9b5h.com", L"\x0e9f\x0eae" L"\x0ed0\x0e9a.com", false},
+ {"xn--f7cj9b5h.com",
+ L"\x0e9f\x0eae"
+ L"\x0ed0\x0e9a.com",
+ false},
// At one point the skeleton of 'w' was 'vv', ensure that
// that it's treated as 'w'.
@@ -990,14 +993,18 @@ void CheckAdjustedOffsets(const std::string& url_string,
}
namespace test {
-#include "components/url_formatter/top_domains/test_skeletons-inc.cc"
+#include "components/url_formatter/top_domains/test_domains-trie-inc.cc"
}
} // namespace
TEST(UrlFormatterTest, IDNToUnicode) {
- IDNSpoofChecker::SetTopDomainGraph(base::StringPiece(
- reinterpret_cast<const char*>(test::kDafsa), sizeof(test::kDafsa)));
+ IDNSpoofChecker::HuffmanTrieParams trie_params{
+ test::kTopDomainsHuffmanTree, sizeof(test::kTopDomainsHuffmanTree),
+ test::kTopDomainsTrie, test::kTopDomainsTrieBits,
+ test::kTopDomainsRootPosition};
+ IDNSpoofChecker::SetTrieParamsForTesting(trie_params);
+
for (size_t i = 0; i < arraysize(idn_cases); i++) {
base::string16 output(IDNToUnicode(idn_cases[i].input));
base::string16 expected(idn_cases[i].unicode_allowed
@@ -1006,7 +1013,7 @@ TEST(UrlFormatterTest, IDNToUnicode) {
EXPECT_EQ(expected, output) << "input # " << i << ": \""
<< idn_cases[i].input << "\"";
}
- IDNSpoofChecker::RestoreTopDomainGraphToDefault();
+ IDNSpoofChecker::RestoreTrieParamsForTesting();
}
TEST(UrlFormatterTest, FormatUrl) {
diff --git a/chromium/components/url_matcher/regex_set_matcher.cc b/chromium/components/url_matcher/regex_set_matcher.cc
index ce110293c83..3ba2d03e970 100644
--- a/chromium/components/url_matcher/regex_set_matcher.cc
+++ b/chromium/components/url_matcher/regex_set_matcher.cc
@@ -43,7 +43,7 @@ bool RegexSetMatcher::Match(const std::string& text,
size_t old_number_of_matches = matches->size();
if (regexes_.empty())
return false;
- if (!filtered_re2_.get()) {
+ if (!filtered_re2_) {
LOG(ERROR) << "RegexSetMatcher was not initialized";
return false;
}
diff --git a/chromium/components/url_matcher/url_matcher.cc b/chromium/components/url_matcher/url_matcher.cc
index 012d2f017ba..12c6cc5762f 100644
--- a/chromium/components/url_matcher/url_matcher.cc
+++ b/chromium/components/url_matcher/url_matcher.cc
@@ -682,8 +682,7 @@ URLMatcherSchemeFilter::URLMatcherSchemeFilter(
URLMatcherSchemeFilter::~URLMatcherSchemeFilter() {}
bool URLMatcherSchemeFilter::IsMatch(const GURL& url) const {
- return std::find(filters_.begin(), filters_.end(), url.scheme()) !=
- filters_.end();
+ return base::ContainsValue(filters_, url.scheme());
}
//
diff --git a/chromium/components/url_matcher/url_matcher_factory_unittest.cc b/chromium/components/url_matcher/url_matcher_factory_unittest.cc
index 65db5ae431a..1df0296683a 100644
--- a/chromium/components/url_matcher/url_matcher_factory_unittest.cc
+++ b/chromium/components/url_matcher/url_matcher_factory_unittest.cc
@@ -74,27 +74,27 @@ TEST(URLMatcherFactoryTest, CreateFromURLFilterDictionary) {
result = URLMatcherFactory::CreateFromURLFilterDictionary(
matcher.condition_factory(), &invalid_condition, 1, &error);
EXPECT_FALSE(error.empty());
- EXPECT_FALSE(result.get());
+ EXPECT_FALSE(result);
// Test wrong datatype in hostSuffix.
error.clear();
result = URLMatcherFactory::CreateFromURLFilterDictionary(
matcher.condition_factory(), &invalid_condition2, 2, &error);
EXPECT_FALSE(error.empty());
- EXPECT_FALSE(result.get());
+ EXPECT_FALSE(result);
// Test invalid regex in urlMatches.
error.clear();
result = URLMatcherFactory::CreateFromURLFilterDictionary(
matcher.condition_factory(), &invalid_condition3, 3, &error);
EXPECT_FALSE(error.empty());
- EXPECT_FALSE(result.get());
+ EXPECT_FALSE(result);
error.clear();
result = URLMatcherFactory::CreateFromURLFilterDictionary(
matcher.condition_factory(), &invalid_condition4, 4, &error);
EXPECT_FALSE(error.empty());
- EXPECT_FALSE(result.get());
+ EXPECT_FALSE(result);
// Test success.
error.clear();
@@ -160,7 +160,7 @@ TEST(URLMatcherFactoryTest, UpperCase) {
result = URLMatcherFactory::CreateFromURLFilterDictionary(
matcher.condition_factory(), invalid_conditions[i], 1, &error);
EXPECT_FALSE(error.empty()) << "in iteration " << i;
- EXPECT_FALSE(result.get()) << "in iteration " << i;
+ EXPECT_FALSE(result) << "in iteration " << i;
}
}
@@ -250,7 +250,7 @@ void UrlConditionCaseTest::CheckCondition(
matcher.condition_factory(), &condition, 1, &error);
if (expected_result == CREATE_FAILURE) {
EXPECT_FALSE(error.empty());
- EXPECT_FALSE(result.get());
+ EXPECT_FALSE(result);
return;
}
EXPECT_EQ("", error);
diff --git a/chromium/components/variations/field_trial_config/BUILD.gn b/chromium/components/variations/field_trial_config/BUILD.gn
index 5dcae68c030..7d29a5708cb 100644
--- a/chromium/components/variations/field_trial_config/BUILD.gn
+++ b/chromium/components/variations/field_trial_config/BUILD.gn
@@ -19,15 +19,28 @@ action("field_trial_testing_config_action") {
"$target_gen_dir/$out_name.h",
]
+ if (current_os == "win") {
+ platform = "windows"
+ } else {
+ platform = current_os
+ }
+
args = [
rebase_path(source, root_build_dir),
"--destbase=" + rebase_path(target_gen_dir, root_build_dir),
"--namespace=variations",
"--schema=" +
rebase_path("field_trial_testing_config_schema.json", root_build_dir),
- "--platform=" + current_os,
+ "--platform=" + platform,
"--output=$out_name",
]
+
+ # At build-time, Android Chrome and WebView both use platform "android", but
+ # at run-time, variations has separate platforms "android" and
+ # "android_webview". So if building "android", also include WebView.
+ if (platform == "android") {
+ args += [ "--platform=android_webview" ]
+ }
}
static_library("field_trial_config") {
@@ -42,8 +55,11 @@ static_library("field_trial_config") {
":field_trial_testing_config_action",
"//base",
"//components/variations",
+ "//components/variations/proto:proto",
"//net",
]
+
+ public_deps = [ "//third_party/protobuf:protobuf_lite" ]
}
source_set("unit_tests") {
diff --git a/chromium/components/variations/field_trial_config/field_trial_testing_config_schema.json b/chromium/components/variations/field_trial_config/field_trial_testing_config_schema.json
index 31419f0b0db..51bfe19b313 100644
--- a/chromium/components/variations/field_trial_config/field_trial_testing_config_schema.json
+++ b/chromium/components/variations/field_trial_config/field_trial_testing_config_schema.json
@@ -4,6 +4,7 @@
{
"type_name": "FieldTrialTestingConfig",
+ "headers": ["components/variations/proto/study.pb.h"],
"schema": [{
"field": "studies",
"type": "array",
@@ -21,6 +22,11 @@
"fields": [
{"field": "name", "type": "string"},
{
+ "field": "platforms",
+ "type": "array",
+ "contents": {"type": "enum", "ctype": "Study::Platform"}
+ },
+ {
"field": "params",
"type": "array",
"contents": {
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 4ee47dc1579..a77d5c8e655 100644
--- a/chromium/components/variations/field_trial_config/field_trial_util.cc
+++ b/chromium/components/variations/field_trial_config/field_trial_util.cc
@@ -6,7 +6,10 @@
#include <stddef.h>
+#include <map>
+#include <set>
#include <string>
+#include <utility>
#include <vector>
#include "base/command_line.h"
@@ -21,6 +24,15 @@
namespace variations {
namespace {
+bool HasPlatform(const FieldTrialTestingExperiment& experiment,
+ Study::Platform platform) {
+ for (size_t i = 0; i < experiment.platforms_size; ++i) {
+ if (experiment.platforms[i] == platform)
+ return true;
+ }
+ return false;
+}
+
void AssociateParamsFromExperiment(
const std::string& study_name,
const FieldTrialTestingExperiment& experiment,
@@ -55,27 +67,35 @@ void AssociateParamsFromExperiment(
}
}
-// Chooses an experiment taking into account the command line. Defaults to
-// picking the first experiment.
-const FieldTrialTestingExperiment& ChooseExperiment(
- const FieldTrialTestingExperiment experiments[],
- size_t experiment_size) {
- DCHECK_GT(experiment_size, 0ul);
+// Choose an experiment to associate. The rules are:
+// - Out of the experiments which match this platform:
+// - If there is a forcing flag for any experiment, choose the first such
+// experiment.
+// - Otherwise, choose the first experiment.
+// - If no experiments match this platform, do not associate any of them.
+void ChooseExperiment(const FieldTrialTestingStudy& study,
+ base::FeatureList* feature_list,
+ Study::Platform platform) {
const auto& command_line = *base::CommandLine::ForCurrentProcess();
- size_t chosen_index = 0;
- for (size_t i = 1; i < experiment_size && chosen_index == 0; ++i) {
- const auto& experiment = experiments[i];
- if (experiment.forcing_flag &&
- command_line.HasSwitch(experiment.forcing_flag)) {
- chosen_index = i;
- break;
+ const FieldTrialTestingExperiment* chosen_experiment = nullptr;
+ for (size_t i = 0; i < study.experiments_size; ++i) {
+ const FieldTrialTestingExperiment* experiment = study.experiments + i;
+ if (HasPlatform(*experiment, platform)) {
+ if (!chosen_experiment)
+ chosen_experiment = experiment;
+
+ if (experiment->forcing_flag &&
+ command_line.HasSwitch(experiment->forcing_flag)) {
+ chosen_experiment = experiment;
+ break;
+ }
}
}
- DCHECK_GT(experiment_size, chosen_index);
- return experiments[chosen_index];
+ if (chosen_experiment)
+ AssociateParamsFromExperiment(study.name, *chosen_experiment, feature_list);
}
-} // namespace
+} // namespace
std::string UnescapeValue(const std::string& value) {
return net::UnescapeURLComponent(
@@ -156,22 +176,22 @@ bool AssociateParamsFromString(const std::string& varations_string) {
}
void AssociateParamsFromFieldTrialConfig(const FieldTrialTestingConfig& config,
- base::FeatureList* feature_list) {
+ base::FeatureList* feature_list,
+ Study::Platform platform) {
for (size_t i = 0; i < config.studies_size; ++i) {
const FieldTrialTestingStudy& study = config.studies[i];
if (study.experiments_size > 0) {
- AssociateParamsFromExperiment(
- study.name,
- ChooseExperiment(study.experiments, study.experiments_size),
- feature_list);
+ ChooseExperiment(study, feature_list, platform);
} else {
DLOG(ERROR) << "Unexpected empty study: " << study.name;
}
}
}
-void AssociateDefaultFieldTrialConfig(base::FeatureList* feature_list) {
- AssociateParamsFromFieldTrialConfig(kFieldTrialConfig, feature_list);
+void AssociateDefaultFieldTrialConfig(base::FeatureList* feature_list,
+ Study::Platform platform) {
+ AssociateParamsFromFieldTrialConfig(kFieldTrialConfig, feature_list,
+ platform);
}
} // namespace variations
diff --git a/chromium/components/variations/field_trial_config/field_trial_util.h b/chromium/components/variations/field_trial_config/field_trial_util.h
index 053a0490bec..1cbbb39afed 100644
--- a/chromium/components/variations/field_trial_config/field_trial_util.h
+++ b/chromium/components/variations/field_trial_config/field_trial_util.h
@@ -7,6 +7,8 @@
#include <string>
+#include "components/variations/proto/study.pb.h"
+
namespace base {
class FeatureList;
}
@@ -33,12 +35,14 @@ bool AssociateParamsFromString(const std::string& variations_string);
// of FieldTrial groups specified in the |config|. Registers features associated
// with default field trials with |feature_list|.
void AssociateParamsFromFieldTrialConfig(const FieldTrialTestingConfig& config,
- base::FeatureList* feature_list);
+ base::FeatureList* feature_list,
+ Study::Platform platform);
// Associates params and features to FieldTrial groups and forces the selection
// of groups specified in testing/variations/fieldtrial_testing_config.json.
// Registers features associated with default field trials with |feature_list|.
-void AssociateDefaultFieldTrialConfig(base::FeatureList* feature_list);
+void AssociateDefaultFieldTrialConfig(base::FeatureList* feature_list,
+ Study::Platform platform);
} // namespace variations
diff --git a/chromium/components/variations/field_trial_config/field_trial_util_unittest.cc b/chromium/components/variations/field_trial_config/field_trial_util_unittest.cc
index c27a7c760e1..3cea9baed9d 100644
--- a/chromium/components/variations/field_trial_config/field_trial_util_unittest.cc
+++ b/chromium/components/variations/field_trial_config/field_trial_util_unittest.cc
@@ -4,12 +4,14 @@
#include "components/variations/field_trial_config/field_trial_util.h"
+#include <map>
#include <memory>
#include <utility>
#include "base/command_line.h"
#include "base/macros.h"
#include "base/metrics/field_trial.h"
+#include "base/stl_util.h"
#include "base/test/scoped_feature_list.h"
#include "components/variations/field_trial_config/fieldtrial_testing_config.h"
#include "components/variations/variations_associated_data.h"
@@ -62,19 +64,20 @@ TEST_F(FieldTrialUtilTest, AssociateParamsFromStringWithSameTrial) {
}
TEST_F(FieldTrialUtilTest, AssociateParamsFromFieldTrialConfig) {
+ const Study::Platform platform = Study::PLATFORM_LINUX;
const FieldTrialTestingExperimentParams array_kFieldTrialConfig_params_0[] =
{{"x", "1"}, {"y", "2"}};
const FieldTrialTestingExperiment array_kFieldTrialConfig_experiments_0[] = {
- {"TestGroup1", array_kFieldTrialConfig_params_0, 2, nullptr, 0,
- nullptr, 0, nullptr},
+ {"TestGroup1", &platform, 1, array_kFieldTrialConfig_params_0, 2,
+ nullptr, 0, nullptr, 0, nullptr},
};
const FieldTrialTestingExperimentParams array_kFieldTrialConfig_params_1[] =
{{"x", "3"}, {"y", "4"}};
const FieldTrialTestingExperiment array_kFieldTrialConfig_experiments_1[] = {
- {"TestGroup2", array_kFieldTrialConfig_params_0, 2, nullptr, 0,
- nullptr, 0, nullptr},
- {"TestGroup2-2", array_kFieldTrialConfig_params_1, 2, nullptr, 0,
- nullptr, 0, nullptr},
+ {"TestGroup2", &platform, 1, array_kFieldTrialConfig_params_0, 2,
+ nullptr, 0, nullptr, 0, nullptr},
+ {"TestGroup2-2", &platform, 1, array_kFieldTrialConfig_params_1, 2,
+ nullptr, 0, nullptr, 0, nullptr},
};
const FieldTrialTestingStudy array_kFieldTrialConfig_studies[] = {
{"TestTrial1", array_kFieldTrialConfig_experiments_0, 1},
@@ -85,7 +88,7 @@ TEST_F(FieldTrialUtilTest, AssociateParamsFromFieldTrialConfig) {
};
base::FeatureList feature_list;
- AssociateParamsFromFieldTrialConfig(kConfig, &feature_list);
+ AssociateParamsFromFieldTrialConfig(kConfig, &feature_list, platform);
EXPECT_EQ("1", GetVariationParamValue("TestTrial1", "x"));
EXPECT_EQ("2", GetVariationParamValue("TestTrial1", "y"));
@@ -100,6 +103,115 @@ TEST_F(FieldTrialUtilTest, AssociateParamsFromFieldTrialConfig) {
EXPECT_EQ("TestGroup2", base::FieldTrialList::FindFullName("TestTrial2"));
}
+TEST_F(FieldTrialUtilTest,
+ AssociateParamsFromFieldTrialConfigWithEachPlatform) {
+ const Study::Platform all_platforms[] = {
+ Study::PLATFORM_ANDROID,
+ Study::PLATFORM_ANDROID_WEBVIEW,
+ Study::PLATFORM_CHROMEOS,
+ Study::PLATFORM_FUCHSIA,
+ Study::PLATFORM_IOS,
+ Study::PLATFORM_LINUX,
+ Study::PLATFORM_MAC,
+ Study::PLATFORM_WINDOWS,
+ };
+
+ // Break if platforms are added without updating |all_platforms|.
+ static_assert(base::size(all_platforms) == Study::Platform_ARRAYSIZE,
+ "|all_platforms| must include all platforms.");
+
+ const FieldTrialTestingExperimentParams array_kFieldTrialConfig_params[] =
+ {{"x", "1"}, {"y", "2"}};
+
+ for (size_t i = 0; i < base::size(all_platforms); ++i) {
+ const Study::Platform platform = all_platforms[i];
+ const FieldTrialTestingExperiment array_kFieldTrialConfig_experiments[] = {
+ {"TestGroup", &platform, 1,
+ array_kFieldTrialConfig_params, 2, nullptr, 0, nullptr, 0, nullptr},
+ };
+ const FieldTrialTestingStudy array_kFieldTrialConfig_studies[] = {
+ {"TestTrial", array_kFieldTrialConfig_experiments, 1}
+ };
+ const FieldTrialTestingConfig kConfig = {
+ array_kFieldTrialConfig_studies, 1
+ };
+
+ base::FeatureList feature_list;
+ AssociateParamsFromFieldTrialConfig(kConfig, &feature_list, platform);
+
+ EXPECT_EQ("1", GetVariationParamValue("TestTrial", "x"));
+ EXPECT_EQ("2", GetVariationParamValue("TestTrial", "y"));
+
+ std::map<std::string, std::string> params;
+ EXPECT_TRUE(GetVariationParams("TestTrial", &params));
+ EXPECT_EQ(2U, params.size());
+ EXPECT_EQ("1", params["x"]);
+ EXPECT_EQ("2", params["y"]);
+
+ EXPECT_EQ("TestGroup", base::FieldTrialList::FindFullName("TestTrial"));
+ }
+}
+
+TEST_F(FieldTrialUtilTest,
+ AssociateParamsFromFieldTrialConfigWithDifferentPlatform) {
+ const Study::Platform platform = Study::PLATFORM_ANDROID;
+ const FieldTrialTestingExperimentParams array_kFieldTrialConfig_params[] =
+ {{"x", "1"}, {"y", "2"}};
+ const FieldTrialTestingExperiment array_kFieldTrialConfig_experiments[] = {
+ {"TestGroup", &platform, 1, array_kFieldTrialConfig_params, 2, nullptr, 0,
+ nullptr, 0, nullptr},
+ };
+ const FieldTrialTestingStudy array_kFieldTrialConfig_studies[] =
+ {{"TestTrial", array_kFieldTrialConfig_experiments, 1}};
+ const FieldTrialTestingConfig kConfig =
+ {array_kFieldTrialConfig_studies, 1};
+
+ // The platforms don't match, so trial shouldn't be added.
+ base::FeatureList feature_list;
+ AssociateParamsFromFieldTrialConfig(kConfig, &feature_list,
+ Study::PLATFORM_ANDROID_WEBVIEW);
+
+ EXPECT_EQ("", GetVariationParamValue("TestTrial", "x"));
+ EXPECT_EQ("", GetVariationParamValue("TestTrial", "y"));
+
+ std::map<std::string, std::string> params;
+ EXPECT_FALSE(GetVariationParams("TestTrial", &params));
+
+ EXPECT_EQ("", base::FieldTrialList::FindFullName("TestTrial"));
+}
+
+TEST_F(FieldTrialUtilTest,
+ AssociateParamsFromFieldTrialConfigWithMultiplePlatforms) {
+ const Study::Platform platforms[] =
+ {Study::PLATFORM_ANDROID, Study::PLATFORM_ANDROID_WEBVIEW};
+ const FieldTrialTestingExperimentParams array_kFieldTrialConfig_params[] =
+ {{"x", "1"}, {"y", "2"}};
+ const FieldTrialTestingExperiment array_kFieldTrialConfig_experiments[] = {
+ {"TestGroup", platforms, 2, array_kFieldTrialConfig_params, 2, nullptr, 0,
+ nullptr, 0, nullptr},
+ };
+ const FieldTrialTestingStudy array_kFieldTrialConfig_studies[] =
+ {{"TestTrial", array_kFieldTrialConfig_experiments, 1}};
+ const FieldTrialTestingConfig kConfig =
+ {array_kFieldTrialConfig_studies, 1};
+
+ // One of the platforms matches, so trial should be added.
+ base::FeatureList feature_list;
+ AssociateParamsFromFieldTrialConfig(kConfig, &feature_list,
+ Study::PLATFORM_ANDROID_WEBVIEW);
+
+ EXPECT_EQ("1", GetVariationParamValue("TestTrial", "x"));
+ EXPECT_EQ("2", GetVariationParamValue("TestTrial", "y"));
+
+ std::map<std::string, std::string> params;
+ EXPECT_TRUE(GetVariationParams("TestTrial", &params));
+ EXPECT_EQ(2U, params.size());
+ EXPECT_EQ("1", params["x"]);
+ EXPECT_EQ("2", params["y"]);
+
+ EXPECT_EQ("TestGroup", base::FieldTrialList::FindFullName("TestTrial"));
+}
+
TEST_F(FieldTrialUtilTest, AssociateFeaturesFromFieldTrialConfig) {
const base::Feature kFeatureA{"A", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kFeatureB{"B", base::FEATURE_ENABLED_BY_DEFAULT};
@@ -109,12 +221,16 @@ TEST_F(FieldTrialUtilTest, AssociateFeaturesFromFieldTrialConfig) {
const char* enable_features[] = {"A", "B"};
const char* disable_features[] = {"C", "D"};
+ const Study::Platform platform = Study::PLATFORM_LINUX;
const FieldTrialTestingExperiment array_kFieldTrialConfig_experiments_0[] = {
- {"TestGroup1", nullptr, 0, enable_features, 2, nullptr, 0, nullptr},
+ {"TestGroup1", &platform, 1, nullptr, 0, enable_features, 2, nullptr, 0,
+ nullptr},
};
const FieldTrialTestingExperiment array_kFieldTrialConfig_experiments_1[] = {
- {"TestGroup2", nullptr, 0, nullptr, 0, disable_features, 2, nullptr},
- {"TestGroup2-2", nullptr, 0, nullptr, 0, nullptr, 0, nullptr},
+ {"TestGroup2", &platform, 1, nullptr, 0, nullptr, 0, disable_features, 2,
+ nullptr},
+ {"TestGroup2-2", &platform, 1, nullptr, 0, nullptr, 0, nullptr, 0,
+ nullptr},
};
const FieldTrialTestingStudy array_kFieldTrialConfig_studies[] = {
@@ -127,7 +243,7 @@ TEST_F(FieldTrialUtilTest, AssociateFeaturesFromFieldTrialConfig) {
};
std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
- AssociateParamsFromFieldTrialConfig(kConfig, feature_list.get());
+ AssociateParamsFromFieldTrialConfig(kConfig, feature_list.get(), platform);
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatureList(std::move(feature_list));
@@ -145,17 +261,23 @@ TEST_F(FieldTrialUtilTest, AssociateFeaturesFromFieldTrialConfig) {
}
TEST_F(FieldTrialUtilTest, AssociateForcingFlagsFromFieldTrialConfig) {
+ const Study::Platform platform = Study::PLATFORM_LINUX;
const FieldTrialTestingExperiment array_kFieldTrialConfig_experiments_0[] = {
- {"TestGroup1", nullptr, 0, nullptr, 0, nullptr, 0, nullptr}
+ {"TestGroup1", &platform, 1, nullptr, 0, nullptr, 0, nullptr, 0, nullptr}
};
const FieldTrialTestingExperiment array_kFieldTrialConfig_experiments_1[] = {
- {"TestGroup2", nullptr, 0, nullptr, 0, nullptr, 0, nullptr},
- {"ForcedGroup2", nullptr, 0, nullptr, 0, nullptr, 0, "flag-2"},
+ {"TestGroup2", &platform, 1, nullptr, 0, nullptr, 0, nullptr, 0,
+ nullptr},
+ {"ForcedGroup2", &platform, 1, nullptr, 0, nullptr, 0, nullptr, 0,
+ "flag-2"},
};
const FieldTrialTestingExperiment array_kFieldTrialConfig_experiments_2[] = {
- {"TestGroup3", nullptr, 0, nullptr, 0, nullptr, 0, nullptr},
- {"ForcedGroup3", nullptr, 0, nullptr, 0, nullptr, 0, "flag-3"},
- {"ForcedGroup3-2", nullptr, 0, nullptr, 0, nullptr, 0, "flag-3-2"},
+ {"TestGroup3", &platform, 1, nullptr, 0, nullptr, 0, nullptr, 0,
+ nullptr},
+ {"ForcedGroup3", &platform, 1, nullptr, 0, nullptr, 0, nullptr, 0,
+ "flag-3"},
+ {"ForcedGroup3-2", &platform, 1, nullptr, 0, nullptr, 0, nullptr, 0,
+ "flag-3-2"},
};
const FieldTrialTestingStudy array_kFieldTrialConfig_studies[] = {
{"TestTrial1", array_kFieldTrialConfig_experiments_0, 1},
@@ -170,7 +292,7 @@ TEST_F(FieldTrialUtilTest, AssociateForcingFlagsFromFieldTrialConfig) {
base::CommandLine::ForCurrentProcess()->AppendSwitch("flag-3");
base::FeatureList feature_list;
- AssociateParamsFromFieldTrialConfig(kConfig, &feature_list);
+ AssociateParamsFromFieldTrialConfig(kConfig, &feature_list, platform);
EXPECT_EQ("TestGroup1", base::FieldTrialList::FindFullName("TestTrial1"));
EXPECT_EQ("ForcedGroup2", base::FieldTrialList::FindFullName("TestTrial2"));
diff --git a/chromium/components/variations/net/BUILD.gn b/chromium/components/variations/net/BUILD.gn
index dc870aa1557..7d103d40010 100644
--- a/chromium/components/variations/net/BUILD.gn
+++ b/chromium/components/variations/net/BUILD.gn
@@ -13,6 +13,8 @@ static_library("net") {
public_deps = [
"//components/variations",
"//net",
+ "//services/network/public/cpp:cpp",
+ "//services/network/public/cpp:cpp_base",
"//url",
]
deps = [
diff --git a/chromium/components/variations/net/DEPS b/chromium/components/variations/net/DEPS
index 1abc71b7882..432ab4540d5 100644
--- a/chromium/components/variations/net/DEPS
+++ b/chromium/components/variations/net/DEPS
@@ -2,4 +2,5 @@ include_rules = [
"+components/google",
"+components/metrics",
"+net",
+ "+services/network/public/cpp",
]
diff --git a/chromium/components/variations/net/variations_http_headers.cc b/chromium/components/variations/net/variations_http_headers.cc
index 3eb28d0515a..a2d07821f81 100644
--- a/chromium/components/variations/net/variations_http_headers.cc
+++ b/chromium/components/variations/net/variations_http_headers.cc
@@ -6,13 +6,20 @@
#include <stddef.h>
+#include <vector>
+
#include "base/macros.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "components/google/core/browser/google_util.h"
#include "components/variations/variations_http_header_provider.h"
#include "net/http/http_request_headers.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "net/url_request/redirect_info.h"
#include "net/url_request/url_request.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/cpp/simple_url_loader.h"
#include "url/gurl.h"
namespace variations {
@@ -86,9 +93,21 @@ void LogUrlValidationHistogram(URLValidationResult result) {
URL_VALIDATION_RESULT_SIZE);
}
+// Removes variations headers for requests when a redirect to a non-Google URL
+// occurs. This function is used as the callback parameter for
+// SimpleURLLoader::SetOnRedirectCallback() when
+// CreateSimpleURLLoaderWithVariationsHeaders() creates a SimpleURLLoader
+// object.
+void RemoveVariationsHeader(const net::RedirectInfo& redirect_info,
+ const network::ResourceResponseHead& response_head,
+ std::vector<std::string>* to_be_removed_headers) {
+ if (!internal::ShouldAppendVariationHeaders(redirect_info.new_url))
+ to_be_removed_headers->push_back(kClientData);
+}
+
} // namespace
-void AppendVariationHeaders(const GURL& url,
+bool AppendVariationHeaders(const GURL& url,
InIncognito incognito,
SignedIn signed_in,
net::HttpRequestHeaders* headers) {
@@ -101,8 +120,9 @@ void AppendVariationHeaders(const GURL& url,
// 2. Only transmit for non-Incognito profiles.
// 3. For the X-Client-Data header, only include non-empty variation IDs.
if ((incognito == InIncognito::kYes) ||
- !internal::ShouldAppendVariationHeaders(url))
- return;
+ !internal::ShouldAppendVariationHeaders(url)) {
+ return false;
+ }
const std::string variation_ids_header =
VariationsHttpHeaderProvider::GetInstance()->GetClientDataHeader(
@@ -110,7 +130,17 @@ void AppendVariationHeaders(const GURL& url,
if (!variation_ids_header.empty()) {
// Note that prior to M33 this header was named X-Chrome-Variations.
headers->SetHeaderIfMissing(kClientData, variation_ids_header);
+ return true;
}
+ return false;
+}
+
+bool AppendVariationHeadersUnknownSignedIn(const GURL& url,
+ InIncognito incognito,
+ net::HttpRequestHeaders* headers) {
+ // Note: It's OK to pass SignedIn::kNo if it's unknown, as it does not affect
+ // transmission of experiments coming from the variations server.
+ return AppendVariationHeaders(url, incognito, SignedIn::kNo, headers);
}
std::set<std::string> GetVariationHeaderNames() {
@@ -127,6 +157,32 @@ void StripVariationHeaderIfNeeded(const GURL& new_location,
}
}
+std::unique_ptr<network::SimpleURLLoader>
+CreateSimpleURLLoaderWithVariationsHeaders(
+ std::unique_ptr<network::ResourceRequest> request,
+ InIncognito incognito,
+ SignedIn signed_in,
+ const net::NetworkTrafficAnnotationTag& annotation_tag) {
+ bool variation_headers_added = AppendVariationHeaders(
+ request->url, incognito, signed_in, &request->headers);
+ std::unique_ptr<network::SimpleURLLoader> simple_url_loader =
+ network::SimpleURLLoader::Create(std::move(request), annotation_tag);
+ if (variation_headers_added) {
+ simple_url_loader->SetOnRedirectCallback(
+ base::BindRepeating(&RemoveVariationsHeader));
+ }
+ return simple_url_loader;
+}
+
+std::unique_ptr<network::SimpleURLLoader>
+CreateSimpleURLLoaderWithVariationsHeadersUnknownSignedIn(
+ std::unique_ptr<network::ResourceRequest> request,
+ InIncognito incognito,
+ const net::NetworkTrafficAnnotationTag& annotation_tag) {
+ return CreateSimpleURLLoaderWithVariationsHeaders(
+ std::move(request), incognito, SignedIn::kNo, annotation_tag);
+}
+
namespace internal {
// static
diff --git a/chromium/components/variations/net/variations_http_headers.h b/chromium/components/variations/net/variations_http_headers.h
index d174788b02d..f7c783e4a54 100644
--- a/chromium/components/variations/net/variations_http_headers.h
+++ b/chromium/components/variations/net/variations_http_headers.h
@@ -5,14 +5,21 @@
#ifndef COMPONENTS_VARIATIONS_NET_VARIATIONS_HTTP_HEADERS_H_
#define COMPONENTS_VARIATIONS_NET_VARIATIONS_HTTP_HEADERS_H_
+#include <memory>
#include <set>
#include <string>
namespace net {
class HttpRequestHeaders;
+struct NetworkTrafficAnnotationTag;
class URLRequest;
}
+namespace network {
+struct ResourceRequest;
+class SimpleURLLoader;
+} // namespace network
+
class GURL;
namespace variations {
@@ -28,11 +35,18 @@ enum class SignedIn { kNo, kYes };
// GOOGLE_WEB_PROPERTIES_SIGNED_IN, which is not the case for any ids that come
// from the variations server. These headers are never transmitted to non-Google
// web sites, which is checked based on the destination |url|.
-void AppendVariationHeaders(const GURL& url,
+// Returns true if custom headers are added. Returns false otherwise.
+bool AppendVariationHeaders(const GURL& url,
InIncognito incognito,
SignedIn signed_in,
net::HttpRequestHeaders* headers);
+// Adds Chrome experiment and metrics state as custom headers to |headers|
+// when the signed-in state is not known to the caller; See above for details.
+bool AppendVariationHeadersUnknownSignedIn(const GURL& url,
+ InIncognito incognito,
+ net::HttpRequestHeaders* headers);
+
// Returns the HTTP header names which are added by AppendVariationHeaders().
std::set<std::string> GetVariationHeaderNames();
@@ -42,6 +56,25 @@ std::set<std::string> GetVariationHeaderNames();
void StripVariationHeaderIfNeeded(const GURL& new_location,
net::URLRequest* request);
+// Creates a SimpleURLLoader that will include variations headers for requests
+// to Google and ensures they're removed if a redirect to a non-Google URL
+// occurs.
+std::unique_ptr<network::SimpleURLLoader>
+CreateSimpleURLLoaderWithVariationsHeaders(
+ std::unique_ptr<network::ResourceRequest> request,
+ InIncognito incognito,
+ SignedIn signed_in,
+ const net::NetworkTrafficAnnotationTag& annotation_tag);
+
+// Creates a SimpleURLLoader that will include variations headers for requests
+// to Google when the signed-in state is unknown and ensures they're removed
+// if a redirect to a non-Google URL occurs.
+std::unique_ptr<network::SimpleURLLoader>
+CreateSimpleURLLoaderWithVariationsHeadersUnknownSignedIn(
+ std::unique_ptr<network::ResourceRequest> request,
+ InIncognito incognito,
+ const net::NetworkTrafficAnnotationTag& annotation_tag);
+
namespace internal {
// Checks whether variation headers should be appended to requests to the
diff --git a/chromium/components/variations/proto/study.proto b/chromium/components/variations/proto/study.proto
index 3252414b78e..0169db04e26 100644
--- a/chromium/components/variations/proto/study.proto
+++ b/chromium/components/variations/proto/study.proto
@@ -181,6 +181,7 @@ message Study {
}
// Possible Chrome operating system platforms.
+ // These names must match those in tools/variations/fieldtrial_to_struct.py.
enum Platform {
PLATFORM_WINDOWS = 0;
PLATFORM_MAC = 1;
diff --git a/chromium/components/variations/service/BUILD.gn b/chromium/components/variations/service/BUILD.gn
index 80544d0a373..e3c0c1e0db3 100644
--- a/chromium/components/variations/service/BUILD.gn
+++ b/chromium/components/variations/service/BUILD.gn
@@ -29,6 +29,7 @@ static_library("service") {
"//components/version_info",
"//components/web_resource",
"//net",
+ "//services/network/public/cpp",
"//ui/base",
]
}
@@ -54,6 +55,8 @@ source_set("unit_tests") {
"//components/web_resource:test_support",
"//net",
"//net:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//testing/gtest",
]
}
diff --git a/chromium/components/variations/service/DEPS b/chromium/components/variations/service/DEPS
index 179d34cec6b..7d4e98a9107 100644
--- a/chromium/components/variations/service/DEPS
+++ b/chromium/components/variations/service/DEPS
@@ -7,5 +7,7 @@ include_rules = [
"+components/version_info",
"+components/web_resource",
"+net",
+ "+services/network/public",
+ "+services/network/test",
"+ui/base",
]
diff --git a/chromium/components/variations/service/safe_seed_manager_unittest.cc b/chromium/components/variations/service/safe_seed_manager_unittest.cc
index 0ae6b5c6e3f..28f28d605d8 100644
--- a/chromium/components/variations/service/safe_seed_manager_unittest.cc
+++ b/chromium/components/variations/service/safe_seed_manager_unittest.cc
@@ -10,7 +10,7 @@
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/macros.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "components/prefs/testing_pref_service.h"
#include "components/variations/client_filterable_state.h"
diff --git a/chromium/components/variations/service/variations_field_trial_creator.cc b/chromium/components/variations/service/variations_field_trial_creator.cc
index 966d21d16b1..5f33665f28a 100644
--- a/chromium/components/variations/service/variations_field_trial_creator.cc
+++ b/chromium/components/variations/service/variations_field_trial_creator.cc
@@ -250,9 +250,7 @@ VariationsFieldTrialCreator::GetClientFilterableStateForVersion(
state->version = version;
state->channel = GetChannelForVariations(client_->GetChannel());
state->form_factor = GetCurrentFormFactor();
- state->platform = (has_platform_override_)
- ? platform_override_
- : ClientFilterableState::GetCurrentPlatform();
+ state->platform = GetPlatform();
state->hardware_class = GetShortHardwareClass();
#if defined(OS_ANDROID)
// This is set on Android only currently, because the IsLowEndDevice() API
@@ -493,7 +491,7 @@ bool VariationsFieldTrialCreator::SetupFieldTrials(
if (!command_line->HasSwitch(switches::kDisableFieldTrialTestingConfig) &&
!command_line->HasSwitch(::switches::kForceFieldTrials) &&
!command_line->HasSwitch(switches::kVariationsServerURL)) {
- AssociateDefaultFieldTrialConfig(feature_list.get());
+ AssociateDefaultFieldTrialConfig(feature_list.get(), GetPlatform());
}
#endif // defined(FIELDTRIAL_TESTING_ENABLED)
@@ -515,4 +513,10 @@ VariationsSeedStore* VariationsFieldTrialCreator::GetSeedStore() {
return &seed_store_;
}
+Study::Platform VariationsFieldTrialCreator::GetPlatform() {
+ if (has_platform_override_)
+ return platform_override_;
+ return ClientFilterableState::GetCurrentPlatform();
+}
+
} // namespace variations
diff --git a/chromium/components/variations/service/variations_field_trial_creator.h b/chromium/components/variations/service/variations_field_trial_creator.h
index ae9cc954104..6d6ff5db494 100644
--- a/chromium/components/variations/service/variations_field_trial_creator.h
+++ b/chromium/components/variations/service/variations_field_trial_creator.h
@@ -14,6 +14,7 @@
#include "base/macros.h"
#include "base/metrics/field_trial.h"
#include "components/variations/client_filterable_state.h"
+#include "components/variations/proto/study.pb.h"
#include "components/variations/seed_response.h"
#include "components/variations/service/ui_string_overrider.h"
#include "components/variations/variations_seed_store.h"
@@ -133,6 +134,9 @@ class VariationsFieldTrialCreator {
// Returns the seed store. Virtual for testing.
virtual VariationsSeedStore* GetSeedStore();
+ // Get the platform we're running on, respecting OverrideVariationsPlatform().
+ Study::Platform GetPlatform();
+
PrefService* local_state() { return seed_store_.local_state(); }
const PrefService* local_state() const { return seed_store_.local_state(); }
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 46f2c3e9127..aea3a8911ea 100644
--- a/chromium/components/variations/service/variations_field_trial_creator_unittest.cc
+++ b/chromium/components/variations/service/variations_field_trial_creator_unittest.cc
@@ -11,7 +11,7 @@
#include "base/feature_list.h"
#include "base/macros.h"
#include "base/strings/stringprintf.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/version.h"
#include "build/build_config.h"
#include "components/prefs/testing_pref_service.h"
@@ -21,6 +21,7 @@
#include "components/variations/service/safe_seed_manager.h"
#include "components/variations/service/variations_service.h"
#include "components/variations/service/variations_service_client.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -159,7 +160,8 @@ class TestVariationsServiceClient : public VariationsServiceClient {
override {
return base::Callback<base::Version(void)>();
}
- net::URLRequestContextGetter* GetURLRequestContext() override {
+ scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory()
+ override {
return nullptr;
}
network_time::NetworkTimeTracker* GetNetworkTimeTracker() override {
diff --git a/chromium/components/variations/service/variations_service.cc b/chromium/components/variations/service/variations_service.cc
index 2c298489fee..7bfce363ec1 100644
--- a/chromium/components/variations/service/variations_service.cc
+++ b/chromium/components/variations/service/variations_service.cc
@@ -51,8 +51,9 @@
#include "net/http/http_status_code.h"
#include "net/http/http_util.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_status.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 "ui/base/device_form_factor.h"
#include "url/gurl.h"
@@ -271,8 +272,8 @@ VariationsService::VariationsService(
ui_string_overrider,
MaybeImportFirstRunSeed(local_state)),
weak_ptr_factory_(this) {
- DCHECK(client_.get());
- DCHECK(resource_request_allowed_notifier_.get());
+ DCHECK(client_);
+ DCHECK(resource_request_allowed_notifier_);
}
VariationsService::~VariationsService() {
@@ -513,15 +514,11 @@ bool VariationsService::DoFetchFromURL(const GURL& url, bool is_http_retry) {
policy_exception_justification:
"Not implemented, considered not required."
})");
- pending_seed_request_ = net::URLFetcher::Create(0, url, net::URLFetcher::GET,
- this, traffic_annotation);
- data_use_measurement::DataUseUserData::AttachToFetcher(
- pending_seed_request_.get(),
- data_use_measurement::DataUseUserData::VARIATIONS);
- pending_seed_request_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SEND_AUTH_DATA |
- net::LOAD_DO_NOT_SAVE_COOKIES);
- pending_seed_request_->SetRequestContext(client_->GetURLRequestContext());
+ auto resource_request = std::make_unique<network::ResourceRequest>();
+ resource_request->url = url;
+ resource_request->load_flags = net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SEND_AUTH_DATA |
+ net::LOAD_DO_NOT_SAVE_COOKIES;
bool enable_deltas = false;
std::string serial_number =
field_trial_creator_.seed_store()->GetLatestSerialNumber();
@@ -532,19 +529,29 @@ bool VariationsService::DoFetchFromURL(const GURL& url, bool is_http_retry) {
// If the fetch is an HTTP retry, encrypt the If-None-Match header.
if (is_http_retry) {
if (!EncryptString(serial_number, &serial_number)) {
- pending_seed_request_.reset();
return false;
}
base::Base64Encode(serial_number, &serial_number);
}
- pending_seed_request_->AddExtraRequestHeader("If-None-Match:" +
- serial_number);
+ resource_request->headers.SetHeader("If-None-Match", serial_number);
}
// Tell the server that delta-compressed and gzipped seeds are supported.
- const char* supported_im = enable_deltas ? "A-IM:x-bm,gzip" : "A-IM:gzip";
- pending_seed_request_->AddExtraRequestHeader(supported_im);
-
- pending_seed_request_->Start();
+ const char* supported_im = enable_deltas ? "x-bm,gzip" : "gzip";
+ resource_request->headers.SetHeader("A-IM", supported_im);
+
+ // TODO(https://crbug.com/808498): Re-add data use measurement once
+ // SimpleURLLoader supports it.
+ // ID=data_use_measurement::DataUseUserData::VARIATIONS
+ pending_seed_request_ = network::SimpleURLLoader::Create(
+ std::move(resource_request), traffic_annotation);
+ // Ensure our callback is called even with "304 Not Modified" responses.
+ pending_seed_request_->SetAllowHttpErrorResults(true);
+ // base::Unretained is safe here since this class owns
+ // |pending_seed_request_|'s lifetime.
+ pending_seed_request_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ client_->GetURLLoaderFactory().get(),
+ base::BindOnce(&VariationsService::OnSimpleLoaderComplete,
+ base::Unretained(this)));
const base::TimeTicks now = base::TimeTicks::Now();
base::TimeDelta time_since_last_fetch;
@@ -574,7 +581,7 @@ void VariationsService::StartRepeatedVariationsSeedFetch() {
GetVariationsServerURL(policy_pref_service_, restrict_mode_, USE_HTTP);
}
- DCHECK(!request_scheduler_.get());
+ DCHECK(!request_scheduler_);
request_scheduler_.reset(VariationsRequestScheduler::Create(
base::Bind(&VariationsService::FetchVariationsSeed,
weak_ptr_factory_.GetWeakPtr()),
@@ -611,31 +618,35 @@ void VariationsService::NotifyObservers(
}
}
-void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
+void VariationsService::OnSimpleLoaderComplete(
+ std::unique_ptr<std::string> response_body) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK_EQ(pending_seed_request_.get(), source);
const bool is_first_request = !initial_request_completed_;
initial_request_completed_ = true;
- // The fetcher will be deleted when the request is handled.
- std::unique_ptr<const net::URLFetcher> request(
- pending_seed_request_.release());
- const net::URLRequestStatus& status = request->GetStatus();
- const int response_code = request->GetResponseCode();
- bool was_https = request->GetURL().SchemeIs(url::kHttpsScheme);
+ int net_error = pending_seed_request_->NetError();
+ int response_code = -1;
+ scoped_refptr<net::HttpResponseHeaders> headers;
+ if (pending_seed_request_->ResponseInfo() &&
+ pending_seed_request_->ResponseInfo()->headers) {
+ headers = pending_seed_request_->ResponseInfo()->headers;
+ response_code = headers->response_code();
+ }
+ bool is_success = headers && (net_error == net::OK);
+ bool was_https =
+ pending_seed_request_->GetFinalURL().SchemeIs(url::kHttpsScheme);
+ pending_seed_request_.reset();
if (was_https) {
- base::UmaHistogramSparse(
- "Variations.SeedFetchResponseOrErrorCode",
- status.is_success() ? response_code : status.error());
+ base::UmaHistogramSparse("Variations.SeedFetchResponseOrErrorCode",
+ is_success ? response_code : net_error);
} else {
- base::UmaHistogramSparse(
- "Variations.SeedFetchResponseOrErrorCode.HTTP",
- status.is_success() ? response_code : status.error());
+ base::UmaHistogramSparse("Variations.SeedFetchResponseOrErrorCode.HTTP",
+ is_success ? response_code : net_error);
}
- if (status.status() != net::URLRequestStatus::SUCCESS) {
- DVLOG(1) << "Variations server request failed with error: "
- << status.error() << ": " << net::ErrorToString(status.error());
+ if (!is_success) {
+ DVLOG(1) << "Variations server request failed with error: " << net_error
+ << ": " << net::ErrorToString(net_error);
// If the current fetch attempt was over an HTTPS connection, retry the
// fetch immediately over an HTTP connection.
// Currently we only do this if if the 'VariationsHttpRetry' feature is
@@ -649,7 +660,8 @@ void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
// It's common for the very first fetch attempt to fail (e.g. the network
// may not yet be available). In such a case, try again soon, rather than
// waiting the full time interval.
- if (is_first_request)
+ // |request_scheduler_| will be null during unit tests.
+ if (is_first_request && request_scheduler_)
request_scheduler_->ScheduleFetchShortly();
return;
}
@@ -660,7 +672,7 @@ void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
base::Time response_date;
if (response_code == net::HTTP_OK ||
response_code == net::HTTP_NOT_MODIFIED) {
- bool success = request->GetResponseHeaders()->GetDateValue(&response_date);
+ bool success = headers->GetDateValue(&response_date);
DCHECK(success || response_date.is_null());
if (!response_date.is_null()) {
@@ -688,14 +700,9 @@ void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
return;
}
- std::string seed_data;
- bool success = request->GetResponseAsString(&seed_data);
- DCHECK(success);
-
- net::HttpResponseHeaders* headers = request->GetResponseHeaders();
bool is_delta_compressed;
bool is_gzip_compressed;
- if (!GetInstanceManipulations(headers, &is_delta_compressed,
+ if (!GetInstanceManipulations(headers.get(), &is_delta_compressed,
&is_gzip_compressed)) {
// The header does not specify supported instance manipulations, unable to
// process data. Details of errors were logged by GetInstanceManipulations.
@@ -703,14 +710,17 @@ void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
return;
}
- const std::string signature = GetHeaderValue(headers, "X-Seed-Signature");
- const std::string country_code = GetHeaderValue(headers, "X-Country");
+ const std::string signature =
+ GetHeaderValue(headers.get(), "X-Seed-Signature");
+ const std::string country_code = GetHeaderValue(headers.get(), "X-Country");
const bool store_success =
- StoreSeed(seed_data, signature, country_code, response_date,
+ StoreSeed(*response_body, signature, country_code, response_date,
is_delta_compressed, is_gzip_compressed, !was_https);
if (!store_success && is_delta_compressed) {
disable_deltas_for_next_request_ = true;
- request_scheduler_->ScheduleFetchShortly();
+ // |request_scheduler_| will be null during unit tests.
+ if (request_scheduler_)
+ request_scheduler_->ScheduleFetchShortly();
}
}
@@ -728,7 +738,7 @@ void VariationsService::OnResourceRequestsAllowed() {
DoActualFetch();
// This service must have created a scheduler in order for this to be called.
- DCHECK(request_scheduler_.get());
+ DCHECK(request_scheduler_);
request_scheduler_->Reset();
}
diff --git a/chromium/components/variations/service/variations_service.h b/chromium/components/variations/service/variations_service.h
index 7c689858e6e..f136b5bfb16 100644
--- a/chromium/components/variations/service/variations_service.h
+++ b/chromium/components/variations/service/variations_service.h
@@ -27,7 +27,6 @@
#include "components/variations/variations_seed_store.h"
#include "components/version_info/version_info.h"
#include "components/web_resource/resource_request_allowed_notifier.h"
-#include "net/url_request/url_fetcher_delegate.h"
#include "url/gurl.h"
class PrefService;
@@ -42,6 +41,10 @@ namespace metrics {
class MetricsStateManager;
}
+namespace network {
+class SimpleURLLoader;
+}
+
namespace user_prefs {
class PrefRegistrySyncable;
}
@@ -55,8 +58,7 @@ namespace variations {
// Used to setup field trials based on stored variations seed data, and fetch
// new seed data from the variations server.
class VariationsService
- : public net::URLFetcherDelegate,
- public web_resource::ResourceRequestAllowedNotifier::Observer {
+ : public web_resource::ResourceRequestAllowedNotifier::Observer {
public:
class Observer {
public:
@@ -149,7 +151,7 @@ class VariationsService
// Enables fetching the seed for testing, even for unofficial builds. This
// should be used along with overriding |DoActualFetch| or using
- // |net::TestURLFetcherFactory|.
+ // |net::TestURLLoaderFactory|.
static void EnableFetchForTesting();
// Set the PrefService responsible for getting policy-related preferences,
@@ -210,6 +212,16 @@ class VariationsService
variations_server_url_ = url;
}
+ // The client that provides access to the embedder's environment.
+ // Protected so testing subclasses can access it.
+ VariationsServiceClient* client() { return client_.get(); }
+
+ // Records a successful fetch:
+ // (1) Resets failure streaks for Safe Mode.
+ // (2) Records the time of this fetch as the most recent successful fetch.
+ // Protected so testing subclasses can call it.
+ void RecordSuccessfulFetch();
+
private:
FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest, Observer);
FRIEND_TEST_ALL_PREFIXES(VariationsServiceTest, SeedStoredWhenOKStatus);
@@ -266,8 +278,8 @@ class VariationsService
void NotifyObservers(
const variations::VariationsSeedSimulator::Result& result);
- // net::URLFetcherDelegate implementation:
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ // Called by SimpleURLLoader when |pending_seed_request_| load completes.
+ void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
// ResourceRequestAllowedNotifier::Observer implementation:
void OnResourceRequestsAllowed() override;
@@ -278,11 +290,6 @@ class VariationsService
std::unique_ptr<variations::VariationsSeed> seed,
const base::Version& version);
- // Records a successful fetch:
- // (1) Resets failure streaks for Safe Mode.
- // (2) Records the time of this fetch as the most recent successful fetch.
- void RecordSuccessfulFetch();
-
// Encrypts a string using the encrypted_messages component, input is passed
// in as |plaintext|, outputs a serialized EncryptedMessage protobuf as
// |encrypted|. Returns true on success, false on failure. The encryption can
@@ -318,7 +325,7 @@ class VariationsService
// Contains the current seed request. Will only have a value while a request
// is pending, and will be reset by |OnURLFetchComplete|.
- std::unique_ptr<net::URLFetcher> pending_seed_request_;
+ std::unique_ptr<network::SimpleURLLoader> pending_seed_request_;
// The value of the "restrict" URL param to the variations server that has
// been specified via |SetRestrictMode|. If empty, the URL param will be set
diff --git a/chromium/components/variations/service/variations_service_client.h b/chromium/components/variations/service/variations_service_client.h
index 91182c2a2fc..2b466bbf91c 100644
--- a/chromium/components/variations/service/variations_service_client.h
+++ b/chromium/components/variations/service/variations_service_client.h
@@ -8,12 +8,13 @@
#include <string>
#include "base/callback.h"
+#include "base/memory/scoped_refptr.h"
#include "base/strings/string16.h"
#include "base/version.h"
#include "components/version_info/version_info.h"
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
}
namespace network_time {
@@ -37,7 +38,8 @@ class VariationsServiceClient {
virtual base::Callback<base::Version(void)>
GetVersionForSimulationCallback() = 0;
- virtual net::URLRequestContextGetter* GetURLRequestContext() = 0;
+ virtual scoped_refptr<network::SharedURLLoaderFactory>
+ GetURLLoaderFactory() = 0;
virtual network_time::NetworkTimeTracker* GetNetworkTimeTracker() = 0;
// Gets the channel of the embedder.
diff --git a/chromium/components/variations/service/variations_service_unittest.cc b/chromium/components/variations/service/variations_service_unittest.cc
index 1c2feed8843..2c101254ed1 100644
--- a/chromium/components/variations/service/variations_service_unittest.cc
+++ b/chromium/components/variations/service/variations_service_unittest.cc
@@ -15,11 +15,13 @@
#include "base/json/json_string_value_serializer.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
#include "base/sha1.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "base/version.h"
#include "components/metrics/clean_exit_beacon.h"
@@ -36,7 +38,11 @@
#include "net/base/url_util.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
+#include "net/http/http_util.h"
#include "net/url_request/test_url_fetcher_factory.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_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace variations {
@@ -56,7 +62,11 @@ base::Version StubGetVersionForSimulation() {
class TestVariationsServiceClient : public VariationsServiceClient {
public:
- TestVariationsServiceClient() {}
+ TestVariationsServiceClient() {
+ test_shared_loader_factory_ =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_);
+ }
~TestVariationsServiceClient() override {}
// VariationsServiceClient:
@@ -65,8 +75,9 @@ class TestVariationsServiceClient : public VariationsServiceClient {
override {
return base::Bind(&StubGetVersionForSimulation);
}
- net::URLRequestContextGetter* GetURLRequestContext() override {
- return nullptr;
+ scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory()
+ override {
+ return test_shared_loader_factory_;
}
network_time::NetworkTimeTracker* GetNetworkTimeTracker() override {
return nullptr;
@@ -85,9 +96,15 @@ class TestVariationsServiceClient : public VariationsServiceClient {
void set_channel(version_info::Channel channel) { channel_ = channel; }
+ network::TestURLLoaderFactory* test_url_loader_factory() {
+ return &test_url_loader_factory_;
+ }
+
private:
std::string restrict_parameter_;
version_info::Channel channel_ = version_info::Channel::UNKNOWN;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+ scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
DISALLOW_COPY_AND_ASSIGN(TestVariationsServiceClient);
};
@@ -111,16 +128,17 @@ class TestVariationsService : public VariationsService {
delta_compressed_seed_(false),
gzip_compressed_seed_(false),
insecurely_fetched_seed_(false) {
- set_variations_server_url(GetVariationsServerURL(
- local_state, std::string(), use_secure_url ? USE_HTTPS : USE_HTTP));
+ interception_url_ = GetVariationsServerURL(
+ local_state, std::string(), use_secure_url ? USE_HTTPS : USE_HTTP);
+ set_variations_server_url(interception_url_);
}
~TestVariationsService() override {}
+ GURL interception_url() { return interception_url_; }
void set_intercepts_fetch(bool value) {
intercepts_fetch_ = value;
}
-
bool fetch_attempted() const { return fetch_attempted_; }
bool seed_stored() const { return seed_stored_; }
const std::string& stored_country() const { return stored_country_; }
@@ -135,6 +153,7 @@ class TestVariationsService : public VariationsService {
}
VariationsService::DoActualFetch();
+ base::RunLoop().RunUntilIdle();
}
bool StoreSeed(const std::string& seed_data,
@@ -150,6 +169,7 @@ class TestVariationsService : public VariationsService {
delta_compressed_seed_ = is_delta_compressed;
gzip_compressed_seed_ = is_gzip_compressed;
insecurely_fetched_seed_ = fetched_insecurely;
+ RecordSuccessfulFetch();
return true;
}
@@ -158,8 +178,17 @@ class TestVariationsService : public VariationsService {
return std::unique_ptr<const base::FieldTrial::EntropyProvider>(nullptr);
}
- private:
+ TestVariationsServiceClient* client() {
+ return static_cast<TestVariationsServiceClient*>(
+ VariationsService::client());
+ }
+ network::TestURLLoaderFactory* test_url_loader_factory() {
+ return client()->test_url_loader_factory();
+ }
+
+ private:
+ GURL interception_url_;
bool intercepts_fetch_;
bool fetch_attempted_;
bool seed_stored_;
@@ -236,31 +265,6 @@ std::string SerializeSeed(const VariationsSeed& seed) {
return serialized_seed;
}
-// Simulates a variations service response by setting a date header and the
-// specified HTTP |response_code| on |fetcher|. Sets additional header |header|
-// if it is not null.
-scoped_refptr<net::HttpResponseHeaders> SimulateServerResponseWithHeader(
- int response_code,
- net::TestURLFetcher* fetcher,
- const std::string* header) {
- EXPECT_TRUE(fetcher);
- scoped_refptr<net::HttpResponseHeaders> headers(
- new net::HttpResponseHeaders("date:Wed, 13 Feb 2013 00:25:24 GMT\0\0"));
- if (header)
- headers->AddHeader(*header);
- fetcher->set_response_headers(headers);
- fetcher->set_response_code(response_code);
- return headers;
-}
-
-// Simulates a variations service response by setting a date header and the
-// specified HTTP |response_code| on |fetcher|.
-scoped_refptr<net::HttpResponseHeaders> SimulateServerResponse(
- int response_code,
- net::TestURLFetcher* fetcher) {
- return SimulateServerResponseWithHeader(response_code, fetcher, nullptr);
-}
-
// Converts |list_value| to a string, to make it easier for debugging.
std::string ListValueToString(const base::ListValue& list_value) {
std::string json;
@@ -416,31 +420,27 @@ TEST_F(VariationsServiceTest, RequestsInitiallyAllowed) {
}
TEST_F(VariationsServiceTest, SeedStoredWhenOKStatus) {
- net::TestURLFetcherFactory factory;
VariationsService::EnableFetchForTesting();
TestVariationsService service(
std::make_unique<web_resource::TestRequestAllowedNotifier>(&prefs_),
&prefs_, GetMetricsStateManager(), true);
+
+ EXPECT_FALSE(service.seed_stored());
+
+ service.test_url_loader_factory()->AddResponse(
+ service.interception_url().spec(), SerializeSeed(CreateTestSeed()));
service.set_intercepts_fetch(false);
service.DoActualFetch();
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
- SimulateServerResponse(net::HTTP_OK, fetcher);
- fetcher->SetResponseString(SerializeSeed(CreateTestSeed()));
-
- EXPECT_FALSE(service.seed_stored());
- service.OnURLFetchComplete(fetcher);
EXPECT_TRUE(service.seed_stored());
}
TEST_F(VariationsServiceTest, SeedNotStoredWhenNonOKStatus) {
- const int non_ok_status_codes[] = {
- net::HTTP_NO_CONTENT,
- net::HTTP_NOT_MODIFIED,
- net::HTTP_NOT_FOUND,
- net::HTTP_INTERNAL_SERVER_ERROR,
- net::HTTP_SERVICE_UNAVAILABLE,
+ const net::HttpStatusCode non_ok_status_codes[] = {
+ net::HTTP_NO_CONTENT, net::HTTP_NOT_MODIFIED,
+ net::HTTP_NOT_FOUND, net::HTTP_INTERNAL_SERVER_ERROR,
+ net::HTTP_SERVICE_UNAVAILABLE,
};
VariationsService::EnableFetchForTesting();
@@ -450,14 +450,12 @@ TEST_F(VariationsServiceTest, SeedNotStoredWhenNonOKStatus) {
&prefs_, GetMetricsStateManager(), true);
service.set_intercepts_fetch(false);
for (size_t i = 0; i < arraysize(non_ok_status_codes); ++i) {
- net::TestURLFetcherFactory factory;
- service.DoActualFetch();
EXPECT_TRUE(prefs_.FindPreference(prefs::kVariationsCompressedSeed)
->IsDefaultValue());
-
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
- SimulateServerResponse(non_ok_status_codes[i], fetcher);
- service.OnURLFetchComplete(fetcher);
+ service.test_url_loader_factory()->ClearResponses();
+ service.test_url_loader_factory()->AddResponse(
+ service.interception_url().spec(), "", non_ok_status_codes[i]);
+ service.DoActualFetch();
EXPECT_TRUE(prefs_.FindPreference(prefs::kVariationsCompressedSeed)
->IsDefaultValue());
@@ -465,20 +463,23 @@ TEST_F(VariationsServiceTest, SeedNotStoredWhenNonOKStatus) {
}
TEST_F(VariationsServiceTest, RequestGzipCompressedSeed) {
- net::TestURLFetcherFactory factory;
VariationsService::EnableFetchForTesting();
TestVariationsService service(
std::make_unique<web_resource::TestRequestAllowedNotifier>(&prefs_),
&prefs_, GetMetricsStateManager(), true);
service.set_intercepts_fetch(false);
+ net::HttpRequestHeaders intercepted_headers;
+ service.test_url_loader_factory()->AddResponse(
+ service.interception_url().spec(), "");
+ service.test_url_loader_factory()->SetInterceptor(
+ base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+ intercepted_headers = request.headers;
+ }));
service.DoActualFetch();
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
- net::HttpRequestHeaders headers;
- fetcher->GetExtraRequestHeaders(&headers);
std::string field;
- ASSERT_TRUE(headers.GetHeader("A-IM", &field));
+ ASSERT_TRUE(intercepted_headers.GetHeader("A-IM", &field));
EXPECT_EQ("gzip", field);
}
@@ -499,23 +500,25 @@ TEST_F(VariationsServiceTest, InstanceManipulations) {
};
std::string serialized_seed = SerializeSeed(CreateTestSeed());
- net::TestURLFetcherFactory factory;
VariationsService::EnableFetchForTesting();
-
for (size_t i = 0; i < arraysize(cases); ++i) {
TestVariationsService service(
std::make_unique<web_resource::TestRequestAllowedNotifier>(&prefs_),
&prefs_, GetMetricsStateManager(), true);
service.set_intercepts_fetch(false);
- service.DoActualFetch();
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
- if (cases[i].im.empty())
- SimulateServerResponse(net::HTTP_OK, fetcher);
- else
- SimulateServerResponseWithHeader(net::HTTP_OK, fetcher, &cases[i].im);
- fetcher->SetResponseString(serialized_seed);
- service.OnURLFetchComplete(fetcher);
+ std::string headers("HTTP/1.1 200 OK\n\n");
+ network::ResourceResponseHead head;
+ head.headers = new net::HttpResponseHeaders(
+ net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()));
+ if (!cases[i].im.empty())
+ head.headers->AddHeader(cases[i].im);
+ network::URLLoaderCompletionStatus status;
+ status.decoded_body_length = serialized_seed.size();
+ service.test_url_loader_factory()->AddResponse(
+ service.interception_url(), head, serialized_seed, status);
+
+ service.DoActualFetch();
EXPECT_EQ(cases[i].seed_stored, service.seed_stored());
EXPECT_EQ(cases[i].delta_compressed, service.delta_compressed_seed());
@@ -524,23 +527,27 @@ TEST_F(VariationsServiceTest, InstanceManipulations) {
}
TEST_F(VariationsServiceTest, CountryHeader) {
- net::TestURLFetcherFactory factory;
+ std::string serialized_seed = SerializeSeed(CreateTestSeed());
VariationsService::EnableFetchForTesting();
TestVariationsService service(
std::make_unique<web_resource::TestRequestAllowedNotifier>(&prefs_),
&prefs_, GetMetricsStateManager(), true);
+ EXPECT_FALSE(service.seed_stored());
service.set_intercepts_fetch(false);
- service.DoActualFetch();
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
- scoped_refptr<net::HttpResponseHeaders> headers =
- SimulateServerResponse(net::HTTP_OK, fetcher);
- headers->AddHeader("X-Country: test");
- fetcher->SetResponseString(SerializeSeed(CreateTestSeed()));
+ std::string headers("HTTP/1.1 200 OK\n\n");
+ network::ResourceResponseHead head;
+ head.headers = new net::HttpResponseHeaders(
+ net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()));
+ head.headers->AddHeader("X-Country: test");
+ network::URLLoaderCompletionStatus status;
+ status.decoded_body_length = serialized_seed.size();
+ service.test_url_loader_factory()->AddResponse(service.interception_url(),
+ head, serialized_seed, status);
+
+ service.DoActualFetch();
- EXPECT_FALSE(service.seed_stored());
- service.OnURLFetchComplete(fetcher);
EXPECT_TRUE(service.seed_stored());
EXPECT_EQ("test", service.stored_country());
}
@@ -755,7 +762,6 @@ TEST_F(VariationsServiceTest, OverrideStoredPermanentCountry) {
TEST_F(VariationsServiceTest, SafeMode_StartingRequestIncrementsFetchFailures) {
prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 1);
- net::TestURLFetcherFactory factory;
VariationsService::EnableFetchForTesting();
// Create a variations service and start the fetch.
@@ -777,14 +783,10 @@ TEST_F(VariationsServiceTest, SafeMode_SuccessfulFetchClearsFailureStreaks) {
net::test::MockNetworkChangeNotifier network_change_notifier;
// Create a variations service and perform a successful fetch.
- VariationsService service(
- std::make_unique<TestVariationsServiceClient>(),
+ TestVariationsService service(
std::make_unique<web_resource::TestRequestAllowedNotifier>(&prefs_),
- &prefs_, GetMetricsStateManager(), UIStringOverrider());
-
- net::TestURLFetcherFactory factory;
- // This will actually start the fetch.
- service.PerformPreMainMessageLoopStartup();
+ &prefs_, GetMetricsStateManager(), true);
+ service.set_intercepts_fetch(false);
// The below seed and signature pair were generated using the server's
// private key.
@@ -800,12 +802,20 @@ TEST_F(VariationsServiceTest, SafeMode_SuccessfulFetchClearsFailureStreaks) {
std::string response;
ASSERT_TRUE(base::Base64Decode(base64_seed_data, &response));
- const std::string header = "X-Seed-Signature:" + base64_seed_signature;
+ const std::string seed_signature_header =
+ "X-Seed-Signature:" + base64_seed_signature;
+
+ std::string headers("HTTP/1.1 200 OK\n\n");
+ network::ResourceResponseHead head;
+ head.headers = new net::HttpResponseHeaders(
+ net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()));
+ head.headers->AddHeader(seed_signature_header);
+ network::URLLoaderCompletionStatus status;
+ status.decoded_body_length = response.size();
+ service.test_url_loader_factory()->AddResponse(service.interception_url(),
+ head, response, status);
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
- SimulateServerResponseWithHeader(net::HTTP_OK, fetcher, &header);
- fetcher->SetResponseString(response);
- service.OnURLFetchComplete(fetcher);
+ service.DoActualFetch();
// Verify that the streaks were reset.
EXPECT_EQ(0, prefs_.GetInteger(prefs::kVariationsCrashStreak));
@@ -816,7 +826,6 @@ TEST_F(VariationsServiceTest, SafeMode_NotModifiedFetchClearsFailureStreaks) {
prefs_.SetInteger(prefs::kVariationsCrashStreak, 2);
prefs_.SetInteger(prefs::kVariationsFailedToFetchSeedStreak, 1);
- net::TestURLFetcherFactory factory;
VariationsService::EnableFetchForTesting();
// Create a variations service and perform a successful fetch.
@@ -824,11 +833,16 @@ TEST_F(VariationsServiceTest, SafeMode_NotModifiedFetchClearsFailureStreaks) {
std::make_unique<web_resource::TestRequestAllowedNotifier>(&prefs_),
&prefs_, GetMetricsStateManager(), true);
service.set_intercepts_fetch(false);
- service.DoActualFetch();
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
- SimulateServerResponse(net::HTTP_NOT_MODIFIED, fetcher);
- service.OnURLFetchComplete(fetcher);
+ std::string headers("HTTP/1.1 304 Not Modified\n\n");
+ network::ResourceResponseHead head;
+ head.headers = new net::HttpResponseHeaders(
+ net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()));
+ network::URLLoaderCompletionStatus status;
+ service.test_url_loader_factory()->AddResponse(service.interception_url(),
+ head, "", status);
+
+ service.DoActualFetch();
EXPECT_EQ(0, prefs_.GetInteger(prefs::kVariationsCrashStreak));
EXPECT_EQ(0, prefs_.GetInteger(prefs::kVariationsFailedToFetchSeedStreak));
@@ -846,35 +860,27 @@ TEST_F(VariationsServiceTest, FieldTrialCreatorInitializedCorrectly) {
TEST_F(VariationsServiceTest, InsecurelyFetchedSetWhenHTTP) {
std::string serialized_seed = SerializeSeed(CreateTestSeed());
- net::TestURLFetcherFactory factory;
VariationsService::EnableFetchForTesting();
TestVariationsService service(
std::make_unique<web_resource::TestRequestAllowedNotifier>(&prefs_),
&prefs_, GetMetricsStateManager(), false);
service.set_intercepts_fetch(false);
+ service.test_url_loader_factory()->AddResponse(
+ service.interception_url().spec(), serialized_seed);
service.DoActualFetch();
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
- fetcher->set_url(service.variations_server_url_);
- SimulateServerResponse(net::HTTP_OK, fetcher);
- fetcher->SetResponseString(serialized_seed);
- service.OnURLFetchComplete(fetcher);
EXPECT_TRUE(service.insecurely_fetched_seed());
}
TEST_F(VariationsServiceTest, InsecurelyFetchedNotSetWhenHTTPS) {
std::string serialized_seed = SerializeSeed(CreateTestSeed());
- net::TestURLFetcherFactory factory;
TestVariationsService service(
std::make_unique<web_resource::TestRequestAllowedNotifier>(&prefs_),
&prefs_, GetMetricsStateManager(), true);
VariationsService::EnableFetchForTesting();
service.set_intercepts_fetch(false);
+ service.test_url_loader_factory()->AddResponse(
+ service.interception_url().spec(), serialized_seed);
service.DoActualFetch();
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
- fetcher->set_url(service.variations_server_url_);
- SimulateServerResponse(net::HTTP_OK, fetcher);
- fetcher->SetResponseString(serialized_seed);
- service.OnURLFetchComplete(fetcher);
EXPECT_FALSE(service.insecurely_fetched_seed());
}
diff --git a/chromium/components/variations/variations_http_header_provider.cc b/chromium/components/variations/variations_http_header_provider.cc
index a473a40e288..a7a2097d721 100644
--- a/chromium/components/variations/variations_http_header_provider.cc
+++ b/chromium/components/variations/variations_http_header_provider.cc
@@ -21,6 +21,27 @@
namespace variations {
+// The following documents how adding/removing http headers for web content
+// requests are implemented when Network Service is enabled or not enabled.
+//
+// When Network Service is not enabled, adding headers is implemented in
+// ChromeResourceDispatcherHostDelegate::RequestBeginning() by calling
+// variations::AppendVariationHeaders(), and removing headers is implemented in
+// ChromeNetworkDelegate::OnBeforeRedirect() by calling
+// variations::StripVariationHeaderIfNeeded().
+//
+// When Network Service is enabled, adding/removing headers is implemented by
+// request consumers, and how it is implemented depends on the request type.
+// There are three cases:
+// 1. Subresources request in renderer, it is implemented
+// in URLLoaderThrottleProviderImpl::CreateThrottles() by adding a
+// VariationsHeaderURLLoaderThrottle to a content::URLLoaderThrottle vector.
+// 2. Navigations/Downloads request in browser, it is implemented in
+// ChromeContentBrowserClient::CreateURLLoaderThrottles() by also adding a
+// VariationsHeaderURLLoaderThrottle to a content::URLLoaderThrottle vector.
+// 3. SimpleURLLoader in browser, it is implemented in a SimpleURLLoader wrapper
+// function variations::CreateSimpleURLLoaderWithVariationsHeaders().
+
// static
VariationsHttpHeaderProvider* VariationsHttpHeaderProvider::GetInstance() {
return base::Singleton<VariationsHttpHeaderProvider>::get();
diff --git a/chromium/components/variations/variations_request_scheduler_mobile_unittest.cc b/chromium/components/variations/variations_request_scheduler_mobile_unittest.cc
index 91b2ee9472c..9d5f6b6621b 100644
--- a/chromium/components/variations/variations_request_scheduler_mobile_unittest.cc
+++ b/chromium/components/variations/variations_request_scheduler_mobile_unittest.cc
@@ -69,7 +69,7 @@ TEST(VariationsRequestSchedulerMobileTest, OnAppEnterForegroundNoRun) {
// Force execution of the task on this timer to verify that the correct task
// was added to the timer.
- scheduler.schedule_fetch_timer_.user_task().Run();
+ scheduler.schedule_fetch_timer_.FireNow();
// The task should not execute because the seed was fetched too recently.
EXPECT_EQ(0, executed);
@@ -95,7 +95,7 @@ TEST(VariationsRequestSchedulerMobileTest, OnAppEnterForegroundRun) {
// Force execution of the task on this timer to verify that the correct task
// was added to the timer - this will verify that the right task is running.
- scheduler.schedule_fetch_timer_.user_task().Run();
+ scheduler.schedule_fetch_timer_.FireNow();
// We expect the input task to have triggered.
EXPECT_EQ(1, executed);
@@ -134,7 +134,7 @@ TEST(VariationsRequestSchedulerMobileTest, OnAppEnterForegroundOnStartup) {
scheduler.OnAppEnterForeground();
EXPECT_TRUE(scheduler.schedule_fetch_timer_.IsRunning());
- scheduler.schedule_fetch_timer_.user_task().Run();
+ scheduler.schedule_fetch_timer_.FireNow();
// This time it should execute the task.
EXPECT_EQ(2, executed);
}
diff --git a/chromium/components/variations/variations_seed_store_unittest.cc b/chromium/components/variations/variations_seed_store_unittest.cc
index b16e04d8a07..4c9e957a98a 100644
--- a/chromium/components/variations/variations_seed_store_unittest.cc
+++ b/chromium/components/variations/variations_seed_store_unittest.cc
@@ -9,7 +9,7 @@
#include "base/base64.h"
#include "base/macros.h"
-#include "base/test/histogram_tester.h"
+#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "base/version.h"
#include "build/build_config.h"
diff --git a/chromium/components/vector_icons/location_on.icon b/chromium/components/vector_icons/location_on.icon
index 5e1228d405c..32880a9d858 100644
--- a/chromium/components/vector_icons/location_on.icon
+++ b/chromium/components/vector_icons/location_on.icon
@@ -9,3 +9,17 @@ R_CUBIC_TO, 0, 0, 14, -15.5f, 14, -26,
R_CUBIC_TO, 0, -7.73f, -6.27f, -14, -14, -14,
CLOSE,
CIRCLE, 24, 18, 5
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 8, 1,
+CUBIC_TO, 5.24f, 1, 3, 3.19f, 3, 5.9f,
+CUBIC_TO, 3, 9.57f, 8, 15, 8, 15,
+CUBIC_TO, 8, 15, 13, 9.57f, 13, 5.9f,
+CUBIC_TO, 13, 3.19f, 10.76f, 1, 8, 1,
+CLOSE,
+MOVE_TO, 8, 8,
+CUBIC_TO, 6.9f, 8, 6, 7.1f, 6, 6,
+CUBIC_TO, 6, 4.9f, 6.9f, 4, 8, 4,
+CUBIC_TO, 9.1f, 4, 10, 4.9f, 10, 6,
+CUBIC_TO, 10, 7.1f, 9.1f, 8, 8, 8,
+CLOSE
diff --git a/chromium/components/visitedlink/browser/visitedlink_event_listener.cc b/chromium/components/visitedlink/browser/visitedlink_event_listener.cc
index a38fc34791a..8fb86c8a126 100644
--- a/chromium/components/visitedlink/browser/visitedlink_event_listener.cc
+++ b/chromium/components/visitedlink/browser/visitedlink_event_listener.cc
@@ -174,7 +174,7 @@ void VisitedLinkEventListener::Reset(bool invalidate_hashes) {
}
void VisitedLinkEventListener::SetCoalesceTimerForTest(
- base::Timer* coalesce_timer_override) {
+ base::OneShotTimer* coalesce_timer_override) {
coalesce_timer_ = coalesce_timer_override;
}
diff --git a/chromium/components/visitedlink/browser/visitedlink_event_listener.h b/chromium/components/visitedlink/browser/visitedlink_event_listener.h
index 449cc2cc1dc..ed3d5b5815f 100644
--- a/chromium/components/visitedlink/browser/visitedlink_event_listener.h
+++ b/chromium/components/visitedlink/browser/visitedlink_event_listener.h
@@ -38,7 +38,7 @@ class VisitedLinkEventListener : public VisitedLinkMaster::Listener,
// Sets a custom timer to use for coalescing events for testing.
// |coalesce_timer_override| must outlive this.
- void SetCoalesceTimerForTest(base::Timer* coalesce_timer_override);
+ void SetCoalesceTimerForTest(base::OneShotTimer* coalesce_timer_override);
private:
void CommitVisitedLinks();
@@ -54,7 +54,7 @@ class VisitedLinkEventListener : public VisitedLinkMaster::Listener,
base::OneShotTimer default_coalesce_timer_;
// A pointer to either |default_coalesce_timer_| or to an override set using
// SetCoalesceTimerForTest(). This does not own the timer.
- base::Timer* coalesce_timer_;
+ base::OneShotTimer* coalesce_timer_;
VisitedLinkCommon::Fingerprints pending_visited_links_;
content::NotificationRegistrar registrar_;
diff --git a/chromium/components/visitedlink/browser/visitedlink_master.cc b/chromium/components/visitedlink/browser/visitedlink_master.cc
index c7fbbc0d5e3..56149328506 100644
--- a/chromium/components/visitedlink/browser/visitedlink_master.cc
+++ b/chromium/components/visitedlink/browser/visitedlink_master.cc
@@ -236,7 +236,7 @@ VisitedLinkMaster::VisitedLinkMaster(Listener* listener,
persist_to_disk_(persist_to_disk),
weak_ptr_factory_(this) {
listener_.reset(listener);
- DCHECK(listener_.get());
+ DCHECK(listener_);
database_name_override_ = filename;
table_size_override_ = default_table_size;
@@ -244,7 +244,7 @@ VisitedLinkMaster::VisitedLinkMaster(Listener* listener,
}
VisitedLinkMaster::~VisitedLinkMaster() {
- if (table_builder_.get()) {
+ if (table_builder_) {
// Prevent the table builder from calling us back now that we're being
// destroyed. Note that we DON'T delete the object, since the history
// system is still writing into it. When that is complete, the table
@@ -343,9 +343,7 @@ void VisitedLinkMaster::PostIOTask(const base::Location& from_here,
void VisitedLinkMaster::AddURL(const GURL& url) {
Hash index = TryToAddURL(url);
- if (!table_builder_.get() &&
- !table_is_loading_from_file_ &&
- index != null_hash_) {
+ if (!table_builder_ && !table_is_loading_from_file_ && index != null_hash_) {
// Not rebuilding, so we want to keep the file on disk up to date.
if (persist_to_disk_) {
WriteUsedItemCountToFile();
@@ -358,16 +356,12 @@ void VisitedLinkMaster::AddURL(const GURL& url) {
void VisitedLinkMaster::AddURLs(const std::vector<GURL>& urls) {
for (const GURL& url : urls) {
Hash index = TryToAddURL(url);
- if (!table_builder_.get() &&
- !table_is_loading_from_file_ &&
- index != null_hash_)
+ if (!table_builder_ && !table_is_loading_from_file_ && index != null_hash_)
ResizeTableIfNecessary();
}
// Keeps the file on disk up to date.
- if (!table_builder_.get() &&
- !table_is_loading_from_file_ &&
- persist_to_disk_)
+ if (!table_builder_ && !table_is_loading_from_file_ && persist_to_disk_)
WriteFullTable();
}
@@ -667,7 +661,7 @@ void VisitedLinkMaster::OnTableLoadComplete(
scoped_refptr<LoadFromFileResult> load_from_file_result) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(persist_to_disk_);
- DCHECK(!table_builder_.get());
+ DCHECK(!table_builder_);
// When the apart table was loading from the database file the current table
// have been cleared.
@@ -696,7 +690,7 @@ void VisitedLinkMaster::OnTableLoadComplete(
added_since_rebuild_.clear();
deleted_since_rebuild_.clear();
- DCHECK(load_from_file_result.get());
+ DCHECK(load_from_file_result);
// Delete the previous table.
DCHECK(mapped_table_memory_.region.IsValid());
@@ -1014,7 +1008,7 @@ uint32_t VisitedLinkMaster::NewTableSizeForCount(int32_t item_count) const {
// See the TableBuilder definition in the header file for how this works.
bool VisitedLinkMaster::RebuildTableFromDelegate() {
- DCHECK(!table_builder_.get());
+ DCHECK(!table_builder_);
// TODO(brettw) make sure we have reasonable salt!
table_builder_ = new TableBuilder(this, salt_);
diff --git a/chromium/components/visitedlink/test/BUILD.gn b/chromium/components/visitedlink/test/BUILD.gn
index e74cacada78..8bac75baa6c 100644
--- a/chromium/components/visitedlink/test/BUILD.gn
+++ b/chromium/components/visitedlink/test/BUILD.gn
@@ -12,6 +12,7 @@ source_set("unit_tests") {
deps = [
"//base",
+ "//base/test:test_support",
"//components/visitedlink/browser",
"//components/visitedlink/common",
"//components/visitedlink/renderer",
diff --git a/chromium/components/viz/BUILD.gn b/chromium/components/viz/BUILD.gn
index 2fc61aa4826..636474ca50c 100644
--- a/chromium/components/viz/BUILD.gn
+++ b/chromium/components/viz/BUILD.gn
@@ -19,7 +19,7 @@ viz_test("viz_unittests") {
"//components/viz/test:test_suite",
"//components/viz/test:test_support",
"//components/viz/test:unit_tests",
- "//mojo/edk",
+ "//mojo/core/embedder",
]
data = [
@@ -29,6 +29,11 @@ viz_test("viz_unittests") {
data_deps = [
"//third_party/mesa:osmesa",
]
+
+ if (is_android) {
+ # The top level test target must depend on the java library directly.
+ deps += [ "//components/viz/service:service_java" ]
+ }
}
viz_test("viz_perftests") {
@@ -47,11 +52,8 @@ viz_test("viz_perftests") {
# This target should not require the Chrome executable to run.
assert_no_deps = [ "//chrome" ]
- data = [
+ data_deps = [
# Needed for isolate script to execute.
- "//testing/scripts/common.py",
- "//testing/xvfb.py",
- "//testing/scripts/run_gtest_perf_test.py",
- "//tools/perf/generate_legacy_perf_dashboard_json.py",
+ "//testing:run_perf_test",
]
}
diff --git a/chromium/components/viz/DEPS b/chromium/components/viz/DEPS
index eba179ee3b2..ce3033f070c 100644
--- a/chromium/components/viz/DEPS
+++ b/chromium/components/viz/DEPS
@@ -6,3 +6,9 @@ include_rules = [
"+ui/base",
"+ui/gfx",
]
+
+specific_include_rules = {
+ ".*_(unittest|perftest|fuzzer)\.cc": [
+ "+components/viz",
+ ],
+}
diff --git a/chromium/components/viz/OWNERS b/chromium/components/viz/OWNERS
index de51ef0cffa..7adfede9ad5 100644
--- a/chromium/components/viz/OWNERS
+++ b/chromium/components/viz/OWNERS
@@ -13,7 +13,7 @@ piman@chromium.org
vmpstr@chromium.org
weiliangc@chromium.org
-# CopyOutputRequests/Results
+# CopyOutputRequests/Results and FrameSinkVideoCapture
miu@chromium.org
# renderers (GL/Skia/Software)
diff --git a/chromium/components/viz/PRESUBMIT.py b/chromium/components/viz/PRESUBMIT.py
index 705241b0248..c18ec999493 100644
--- a/chromium/components/viz/PRESUBMIT.py
+++ b/chromium/components/viz/PRESUBMIT.py
@@ -19,10 +19,9 @@ def PostUploadHook(cl, change, output_api):
"""git cl upload will call this hook after the issue is created/modified.
This hook modifies the CL description in order to run extra GPU
- tests (in particular, the WebGL 2.0 conformance tests) in addition
- to the regular CQ try bots. This test suite is too large to run
- against all Chromium commits, but should be run against changes
- likely to affect these tests.
+ tests (in particular, on Android) in addition to the regular CQ try
+ bots. These tests don't yet run by default on
+ android_n5x_swarming_rel, but viz changes need to run them.
When adding/removing tests here, ensure that both gpu/PRESUBMIT.py and
ui/gl/PRESUBMIT.py are updated.
@@ -30,9 +29,6 @@ def PostUploadHook(cl, change, output_api):
return output_api.EnsureCQIncludeTrybotsAreAdded(
cl,
[
- 'luci.chromium.try:linux_optional_gpu_tests_rel',
- 'luci.chromium.try:mac_optional_gpu_tests_rel',
- 'luci.chromium.try:win_optional_gpu_tests_rel',
'luci.chromium.try:android_optional_gpu_tests_rel',
],
'Automatically added optional GPU tests to run on CQ.')
diff --git a/chromium/components/viz/README.md b/chromium/components/viz/README.md
index c7b048b647c..74ed1c37c57 100644
--- a/chromium/components/viz/README.md
+++ b/chromium/components/viz/README.md
@@ -71,7 +71,10 @@ clients directly, and by service implementations.
### client <a name="directory-structure-client"></a>
Client library for accessing Viz services. May be used from privileged (eg
-browser) or unprivileged (eg renderer) processes.
+browser) or unprivileged (eg renderer) processes. The client library should
+remain agnostic about *how* to communicate with viz (in other words should not
+use mojo bindings), as some viz clients use mojo but others use it in-process
+or via other IPC mechanisms.
| Can depend on: |
|:---------------|
diff --git a/chromium/components/viz/client/BUILD.gn b/chromium/components/viz/client/BUILD.gn
index 4bbeeb832aa..d2341865fb7 100644
--- a/chromium/components/viz/client/BUILD.gn
+++ b/chromium/components/viz/client/BUILD.gn
@@ -6,8 +6,8 @@ import("//components/viz/viz.gni")
viz_component("client") {
sources = [
- "client_layer_tree_frame_sink.cc",
- "client_layer_tree_frame_sink.h",
+ "client_resource_provider.cc",
+ "client_resource_provider.h",
"frame_eviction_manager.cc",
"frame_eviction_manager.h",
"frame_evictor.cc",
@@ -17,23 +17,29 @@ viz_component("client") {
"hit_test_data_provider_draw_quad.h",
"local_surface_id_provider.cc",
"local_surface_id_provider.h",
+ "shared_bitmap_reporter.cc",
+ "shared_bitmap_reporter.h",
]
defines = [ "VIZ_CLIENT_IMPLEMENTATION" ]
public_deps = [
"//base",
- "//cc",
"//components/viz/common",
- "//mojo/public/cpp/bindings",
- "//services/viz/public/interfaces",
+ "//mojo/public/cpp/system",
+ "//skia",
+ ]
+ deps = [
+ "//cc/base",
+ "//gpu/command_buffer/client:gles2_interface",
+ "//gpu/command_buffer/client:raster_interface",
]
}
viz_source_set("unit_tests") {
testonly = true
sources = [
- "client_layer_tree_frame_sink_unittest.cc",
+ "client_resource_provider_unittest.cc",
"hit_test_data_provider_draw_quad_unittest.cc",
]
@@ -41,11 +47,11 @@ viz_source_set("unit_tests") {
":client",
"//base",
"//base/test:test_support",
- "//cc:test_support",
"//components/viz/client",
"//components/viz/test:test_support",
"//mojo/public/cpp/bindings",
"//services/viz/public/interfaces",
+ "//testing/gmock",
"//testing/gtest",
]
}
diff --git a/chromium/components/viz/client/DEPS b/chromium/components/viz/client/DEPS
index db90fed94b9..d3e020864a2 100644
--- a/chromium/components/viz/client/DEPS
+++ b/chromium/components/viz/client/DEPS
@@ -1,19 +1,23 @@
# Please consult components/viz/README.md about allowable dependencies.
include_rules = [
- "+cc",
- "-cc/test",
"-components/viz/common/features.h",
"-components/viz/common/switches.h",
"+components/viz/client",
- "+mojo/public/cpp/bindings",
- "+mojo/public/cpp/system",
- "+services/viz/public/interfaces",
+ "+gpu/GLES2/gl2extchromium.h",
+ "+gpu/command_buffer/client",
+ "+gpu/command_buffer/common",
+ "+mojo/public/cpp/system/buffer.h",
+ "+third_party/skia",
+ "+third_party/khronos/GLES2",
+
+ # Do not use mojo bindings in viz/client/. This library should be agnostic
+ # about how to communicate with viz.
+ "-mojo/public/cpp/bindings",
]
specific_include_rules = {
".*unittest\.cc": [
- "+cc/test",
- "+components/viz/test",
+ "+mojo/public/cpp",
],
}
diff --git a/chromium/components/viz/client/client_layer_tree_frame_sink.cc b/chromium/components/viz/client/client_layer_tree_frame_sink.cc
deleted file mode 100644
index c779823dc64..00000000000
--- a/chromium/components/viz/client/client_layer_tree_frame_sink.cc
+++ /dev/null
@@ -1,240 +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/client/client_layer_tree_frame_sink.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/trace_event/trace_event.h"
-#include "cc/trees/layer_tree_frame_sink_client.h"
-#include "components/viz/client/hit_test_data_provider.h"
-#include "components/viz/client/local_surface_id_provider.h"
-#include "components/viz/common/frame_sinks/begin_frame_args.h"
-#include "components/viz/common/hit_test/hit_test_region_list.h"
-#include "components/viz/common/quads/compositor_frame.h"
-
-namespace viz {
-
-ClientLayerTreeFrameSink::InitParams::InitParams() = default;
-
-ClientLayerTreeFrameSink::InitParams::~InitParams() = default;
-
-ClientLayerTreeFrameSink::UnboundMessagePipes::UnboundMessagePipes() = default;
-
-ClientLayerTreeFrameSink::UnboundMessagePipes::~UnboundMessagePipes() = default;
-
-bool ClientLayerTreeFrameSink::UnboundMessagePipes::HasUnbound() const {
- return client_request.is_pending() &&
- (compositor_frame_sink_info.is_valid() ^
- compositor_frame_sink_associated_info.is_valid());
-}
-
-ClientLayerTreeFrameSink::UnboundMessagePipes::UnboundMessagePipes(
- UnboundMessagePipes&& other) = default;
-
-ClientLayerTreeFrameSink::ClientLayerTreeFrameSink(
- scoped_refptr<ContextProvider> context_provider,
- scoped_refptr<RasterContextProvider> worker_context_provider,
- InitParams* params)
- : cc::LayerTreeFrameSink(std::move(context_provider),
- std::move(worker_context_provider),
- std::move(params->compositor_task_runner),
- params->gpu_memory_buffer_manager),
- hit_test_data_provider_(std::move(params->hit_test_data_provider)),
- local_surface_id_provider_(std::move(params->local_surface_id_provider)),
- synthetic_begin_frame_source_(
- std::move(params->synthetic_begin_frame_source)),
- pipes_(std::move(params->pipes)),
- client_binding_(this),
- enable_surface_synchronization_(params->enable_surface_synchronization),
- wants_animate_only_begin_frames_(params->wants_animate_only_begin_frames),
- weak_factory_(this) {
- DETACH_FROM_THREAD(thread_checker_);
-}
-
-ClientLayerTreeFrameSink::~ClientLayerTreeFrameSink() {}
-
-bool ClientLayerTreeFrameSink::BindToClient(
- cc::LayerTreeFrameSinkClient* client) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
- if (!cc::LayerTreeFrameSink::BindToClient(client))
- return false;
-
- DCHECK(pipes_.HasUnbound());
- if (pipes_.compositor_frame_sink_info.is_valid()) {
- compositor_frame_sink_.Bind(std::move(pipes_.compositor_frame_sink_info));
- compositor_frame_sink_.set_connection_error_with_reason_handler(
- base::Bind(&ClientLayerTreeFrameSink::OnMojoConnectionError,
- weak_factory_.GetWeakPtr()));
- compositor_frame_sink_ptr_ = compositor_frame_sink_.get();
- } else if (pipes_.compositor_frame_sink_associated_info.is_valid()) {
- compositor_frame_sink_associated_.Bind(
- std::move(pipes_.compositor_frame_sink_associated_info));
- compositor_frame_sink_associated_.set_connection_error_with_reason_handler(
- base::Bind(&ClientLayerTreeFrameSink::OnMojoConnectionError,
- weak_factory_.GetWeakPtr()));
- compositor_frame_sink_ptr_ = compositor_frame_sink_associated_.get();
- }
- client_binding_.Bind(std::move(pipes_.client_request),
- compositor_task_runner_);
-
- if (synthetic_begin_frame_source_) {
- client->SetBeginFrameSource(synthetic_begin_frame_source_.get());
- } else {
- begin_frame_source_ = std::make_unique<ExternalBeginFrameSource>(this);
- begin_frame_source_->OnSetBeginFrameSourcePaused(begin_frames_paused_);
- client->SetBeginFrameSource(begin_frame_source_.get());
- }
-
- if (wants_animate_only_begin_frames_)
- compositor_frame_sink_->SetWantsAnimateOnlyBeginFrames();
-
- return true;
-}
-
-void ClientLayerTreeFrameSink::DetachFromClient() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- client_->SetBeginFrameSource(nullptr);
- begin_frame_source_.reset();
- synthetic_begin_frame_source_.reset();
- client_binding_.Close();
- compositor_frame_sink_.reset();
- compositor_frame_sink_associated_.reset();
- compositor_frame_sink_ptr_ = nullptr;
- cc::LayerTreeFrameSink::DetachFromClient();
-}
-
-void ClientLayerTreeFrameSink::SetLocalSurfaceId(
- const LocalSurfaceId& local_surface_id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(local_surface_id.is_valid());
- DCHECK(enable_surface_synchronization_);
- local_surface_id_ = local_surface_id;
-}
-
-void ClientLayerTreeFrameSink::SubmitCompositorFrame(CompositorFrame frame) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(compositor_frame_sink_ptr_);
- DCHECK(frame.metadata.begin_frame_ack.has_damage);
- DCHECK_LE(BeginFrameArgs::kStartingFrameNumber,
- frame.metadata.begin_frame_ack.sequence_number);
-
- if (!enable_surface_synchronization_) {
- local_surface_id_ =
- local_surface_id_provider_->GetLocalSurfaceIdForFrame(frame);
- } else {
- if (local_surface_id_ == last_submitted_local_surface_id_) {
- CHECK_EQ(last_submitted_device_scale_factor_,
- frame.device_scale_factor());
- CHECK_EQ(last_submitted_size_in_pixels_.height(),
- frame.size_in_pixels().height());
- CHECK_EQ(last_submitted_size_in_pixels_.width(),
- frame.size_in_pixels().width());
- }
- }
-
- TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"),
- "SubmitCompositorFrame", local_surface_id_.hash());
- bool tracing_enabled;
- TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"),
- &tracing_enabled);
-
- base::Optional<HitTestRegionList> hit_test_region_list;
- if (hit_test_data_provider_)
- hit_test_region_list = hit_test_data_provider_->GetHitTestData(frame);
- else
- hit_test_region_list = client_->BuildHitTestData();
-
- last_submitted_local_surface_id_ = local_surface_id_;
- last_submitted_device_scale_factor_ = frame.device_scale_factor();
- last_submitted_size_in_pixels_ = frame.size_in_pixels();
-
- compositor_frame_sink_ptr_->SubmitCompositorFrame(
- local_surface_id_, std::move(frame), std::move(hit_test_region_list),
- tracing_enabled ? base::TimeTicks::Now().since_origin().InMicroseconds()
- : 0);
-}
-
-void ClientLayerTreeFrameSink::DidNotProduceFrame(const BeginFrameAck& ack) {
- DCHECK(compositor_frame_sink_ptr_);
- DCHECK(!ack.has_damage);
- DCHECK_LE(BeginFrameArgs::kStartingFrameNumber, ack.sequence_number);
- compositor_frame_sink_ptr_->DidNotProduceFrame(ack);
-}
-
-void ClientLayerTreeFrameSink::DidAllocateSharedBitmap(
- mojo::ScopedSharedBufferHandle buffer,
- const SharedBitmapId& id) {
- DCHECK(compositor_frame_sink_ptr_);
- compositor_frame_sink_ptr_->DidAllocateSharedBitmap(std::move(buffer), id);
-}
-
-void ClientLayerTreeFrameSink::DidDeleteSharedBitmap(const SharedBitmapId& id) {
- DCHECK(compositor_frame_sink_ptr_);
- compositor_frame_sink_ptr_->DidDeleteSharedBitmap(id);
-}
-
-void ClientLayerTreeFrameSink::DidReceiveCompositorFrameAck(
- const std::vector<ReturnedResource>& resources) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- client_->ReclaimResources(resources);
- client_->DidReceiveCompositorFrameAck();
-}
-
-void ClientLayerTreeFrameSink::DidPresentCompositorFrame(
- uint32_t presentation_token,
- base::TimeTicks time,
- base::TimeDelta refresh,
- uint32_t flags) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- client_->DidPresentCompositorFrame(presentation_token, time, refresh, flags);
-}
-
-void ClientLayerTreeFrameSink::DidDiscardCompositorFrame(
- uint32_t presentation_token) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- client_->DidDiscardCompositorFrame(presentation_token);
-}
-
-void ClientLayerTreeFrameSink::OnBeginFrame(const BeginFrameArgs& args) {
- if (!needs_begin_frames_) {
- // We had a race with SetNeedsBeginFrame(false) and still need to let the
- // sink know that we didn't use this BeginFrame.
- DidNotProduceFrame(
- BeginFrameAck(args.source_id, args.sequence_number, false));
- }
- if (begin_frame_source_)
- begin_frame_source_->OnBeginFrame(args);
-}
-
-void ClientLayerTreeFrameSink::OnBeginFramePausedChanged(bool paused) {
- begin_frames_paused_ = paused;
- if (begin_frame_source_)
- begin_frame_source_->OnSetBeginFrameSourcePaused(paused);
-}
-
-void ClientLayerTreeFrameSink::ReclaimResources(
- const std::vector<ReturnedResource>& resources) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- client_->ReclaimResources(resources);
-}
-
-void ClientLayerTreeFrameSink::OnNeedsBeginFrames(bool needs_begin_frames) {
- DCHECK(compositor_frame_sink_ptr_);
- needs_begin_frames_ = needs_begin_frames;
- compositor_frame_sink_ptr_->SetNeedsBeginFrame(needs_begin_frames);
-}
-
-void ClientLayerTreeFrameSink::OnMojoConnectionError(
- uint32_t custom_reason,
- const std::string& description) {
- if (custom_reason)
- DLOG(FATAL) << description;
- if (client_)
- client_->DidLoseLayerTreeFrameSink();
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/client/client_layer_tree_frame_sink.h b/chromium/components/viz/client/client_layer_tree_frame_sink.h
deleted file mode 100644
index 33f06266fc6..00000000000
--- a/chromium/components/viz/client/client_layer_tree_frame_sink.h
+++ /dev/null
@@ -1,140 +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_CLIENT_CLIENT_LAYER_TREE_FRAME_SINK_H_
-#define COMPONENTS_VIZ_CLIENT_CLIENT_LAYER_TREE_FRAME_SINK_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/single_thread_task_runner.h"
-#include "cc/trees/layer_tree_frame_sink.h"
-#include "components/viz/client/viz_client_export.h"
-#include "components/viz/common/frame_sinks/begin_frame_source.h"
-#include "components/viz/common/gpu/context_provider.h"
-#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
-#include "components/viz/common/surfaces/surface_id.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
-
-namespace viz {
-
-class HitTestDataProvider;
-class LocalSurfaceIdProvider;
-
-class VIZ_CLIENT_EXPORT ClientLayerTreeFrameSink
- : public cc::LayerTreeFrameSink,
- public mojom::CompositorFrameSinkClient,
- public ExternalBeginFrameSourceClient {
- public:
- struct VIZ_CLIENT_EXPORT UnboundMessagePipes {
- UnboundMessagePipes();
- ~UnboundMessagePipes();
- UnboundMessagePipes(UnboundMessagePipes&& other);
-
- bool HasUnbound() const;
-
- // Only one of |compositor_frame_sink_info| or
- // |compositor_frame_sink_associated_info| should be set.
- mojom::CompositorFrameSinkPtrInfo compositor_frame_sink_info;
- mojom::CompositorFrameSinkAssociatedPtrInfo
- compositor_frame_sink_associated_info;
- mojom::CompositorFrameSinkClientRequest client_request;
- };
-
- struct VIZ_CLIENT_EXPORT InitParams {
- InitParams();
- ~InitParams();
-
- scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner;
- gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager = nullptr;
- std::unique_ptr<SyntheticBeginFrameSource> synthetic_begin_frame_source;
- std::unique_ptr<HitTestDataProvider> hit_test_data_provider;
- std::unique_ptr<LocalSurfaceIdProvider> local_surface_id_provider;
- UnboundMessagePipes pipes;
- bool enable_surface_synchronization = false;
- bool wants_animate_only_begin_frames = false;
- };
-
- ClientLayerTreeFrameSink(
- scoped_refptr<ContextProvider> context_provider,
- scoped_refptr<RasterContextProvider> worker_context_provider,
- InitParams* params);
-
- ~ClientLayerTreeFrameSink() override;
-
- const HitTestDataProvider* hit_test_data_provider() const {
- return hit_test_data_provider_.get();
- }
-
- const LocalSurfaceId& local_surface_id() const { return local_surface_id_; }
-
- // cc::LayerTreeFrameSink implementation.
- bool BindToClient(cc::LayerTreeFrameSinkClient* client) override;
- void DetachFromClient() override;
- void SetLocalSurfaceId(const LocalSurfaceId& local_surface_id) override;
- void SubmitCompositorFrame(CompositorFrame frame) override;
- void DidNotProduceFrame(const BeginFrameAck& ack) override;
- void DidAllocateSharedBitmap(mojo::ScopedSharedBufferHandle buffer,
- const SharedBitmapId& id) override;
- void DidDeleteSharedBitmap(const SharedBitmapId& id) override;
-
- private:
- // mojom::CompositorFrameSinkClient implementation:
- void DidReceiveCompositorFrameAck(
- const std::vector<ReturnedResource>& resources) override;
- void DidPresentCompositorFrame(uint32_t presentation_token,
- base::TimeTicks time,
- base::TimeDelta refresh,
- uint32_t flags) override;
- void DidDiscardCompositorFrame(uint32_t presentation_token) override;
- void OnBeginFrame(const BeginFrameArgs& begin_frame_args) override;
- void OnBeginFramePausedChanged(bool paused) override;
- void ReclaimResources(
- const std::vector<ReturnedResource>& resources) override;
-
- // ExternalBeginFrameSourceClient implementation.
- void OnNeedsBeginFrames(bool needs_begin_frames) override;
-
- void OnMojoConnectionError(uint32_t custom_reason,
- const std::string& description);
-
- bool begin_frames_paused_ = false;
- bool needs_begin_frames_ = false;
- LocalSurfaceId local_surface_id_;
- std::unique_ptr<HitTestDataProvider> hit_test_data_provider_;
- std::unique_ptr<LocalSurfaceIdProvider> local_surface_id_provider_;
- std::unique_ptr<ExternalBeginFrameSource> begin_frame_source_;
- std::unique_ptr<SyntheticBeginFrameSource> synthetic_begin_frame_source_;
-
- // Message pipes that will be bound when BindToClient() is called.
- UnboundMessagePipes pipes_;
-
- // One of |compositor_frame_sink_| or |compositor_frame_sink_associated_| will
- // be bound after calling BindToClient(). |compositor_frame_sink_ptr_| will
- // point to message pipe we want to use.
- mojom::CompositorFrameSink* compositor_frame_sink_ptr_ = nullptr;
- mojom::CompositorFrameSinkPtr compositor_frame_sink_;
- mojom::CompositorFrameSinkAssociatedPtr compositor_frame_sink_associated_;
- mojo::Binding<mojom::CompositorFrameSinkClient> client_binding_;
-
- THREAD_CHECKER(thread_checker_);
- const bool enable_surface_synchronization_;
- const bool wants_animate_only_begin_frames_;
-
- LocalSurfaceId last_submitted_local_surface_id_;
- float last_submitted_device_scale_factor_ = 1.f;
- gfx::Size last_submitted_size_in_pixels_;
-
- base::WeakPtrFactory<ClientLayerTreeFrameSink> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(ClientLayerTreeFrameSink);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_CLIENT_CLIENT_LAYER_TREE_FRAME_SINK_H_
diff --git a/chromium/components/viz/client/client_layer_tree_frame_sink_unittest.cc b/chromium/components/viz/client/client_layer_tree_frame_sink_unittest.cc
deleted file mode 100644
index a554f5c8ae9..00000000000
--- a/chromium/components/viz/client/client_layer_tree_frame_sink_unittest.cc
+++ /dev/null
@@ -1,116 +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/client/client_layer_tree_frame_sink.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread.h"
-#include "cc/test/fake_layer_tree_frame_sink_client.h"
-#include "components/viz/client/local_surface_id_provider.h"
-#include "components/viz/test/test_context_provider.h"
-#include "components/viz/test/test_gpu_memory_buffer_manager.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
-#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace viz {
-namespace {
-
-// Used to track the thread DidLoseLayerTreeFrameSink() is called on (and quit
-// a RunLoop).
-class ThreadTrackingLayerTreeFrameSinkClient
- : public cc::FakeLayerTreeFrameSinkClient {
- public:
- ThreadTrackingLayerTreeFrameSinkClient(
- base::PlatformThreadId* called_thread_id,
- base::RunLoop* run_loop)
- : called_thread_id_(called_thread_id), run_loop_(run_loop) {}
- ~ThreadTrackingLayerTreeFrameSinkClient() override = default;
-
- // cc::FakeLayerTreeFrameSinkClient:
- void DidLoseLayerTreeFrameSink() override {
- EXPECT_FALSE(did_lose_layer_tree_frame_sink_called());
- cc::FakeLayerTreeFrameSinkClient::DidLoseLayerTreeFrameSink();
- *called_thread_id_ = base::PlatformThread::CurrentId();
- run_loop_->Quit();
- }
-
- private:
- base::PlatformThreadId* called_thread_id_;
- base::RunLoop* run_loop_;
-
- DISALLOW_COPY_AND_ASSIGN(ThreadTrackingLayerTreeFrameSinkClient);
-};
-
-TEST(ClientLayerTreeFrameSinkTest,
- DidLoseLayerTreeFrameSinkCalledOnConnectionError) {
- base::Thread bg_thread("BG Thread");
- bg_thread.Start();
-
- scoped_refptr<TestContextProvider> provider = TestContextProvider::Create();
- TestGpuMemoryBufferManager test_gpu_memory_buffer_manager;
-
- mojom::CompositorFrameSinkPtrInfo sink_info;
- mojom::CompositorFrameSinkRequest sink_request =
- mojo::MakeRequest(&sink_info);
- mojom::CompositorFrameSinkClientPtr client;
- mojom::CompositorFrameSinkClientRequest client_request =
- mojo::MakeRequest(&client);
-
- ClientLayerTreeFrameSink::InitParams init_params;
- init_params.compositor_task_runner = bg_thread.task_runner();
- init_params.gpu_memory_buffer_manager = &test_gpu_memory_buffer_manager;
- init_params.pipes.compositor_frame_sink_info = std::move(sink_info);
- init_params.pipes.client_request = std::move(client_request);
- init_params.local_surface_id_provider =
- std::make_unique<DefaultLocalSurfaceIdProvider>();
- init_params.enable_surface_synchronization = true;
- ClientLayerTreeFrameSink layer_tree_frame_sink(std::move(provider), nullptr,
- &init_params);
-
- base::PlatformThreadId called_thread_id = base::kInvalidThreadId;
- base::RunLoop close_run_loop;
- ThreadTrackingLayerTreeFrameSinkClient frame_sink_client(&called_thread_id,
- &close_run_loop);
-
- auto bind_in_background =
- [](ClientLayerTreeFrameSink* layer_tree_frame_sink,
- ThreadTrackingLayerTreeFrameSinkClient* frame_sink_client) {
- layer_tree_frame_sink->BindToClient(frame_sink_client);
- };
- bg_thread.task_runner()->PostTask(
- FROM_HERE, base::BindOnce(bind_in_background,
- base::Unretained(&layer_tree_frame_sink),
- base::Unretained(&frame_sink_client)));
- // Closes the pipe, which should trigger calling DidLoseLayerTreeFrameSink()
- // (and quitting the RunLoop). There is no need to wait for BindToClient()
- // to complete as mojo::Binding error callbacks are processed asynchronously.
- sink_request = mojom::CompositorFrameSinkRequest();
- close_run_loop.Run();
-
- EXPECT_NE(base::kInvalidThreadId, called_thread_id);
- EXPECT_EQ(called_thread_id, bg_thread.GetThreadId());
-
- // DetachFromClient() has to be called on the background thread.
- base::RunLoop detach_run_loop;
- auto detach_in_background =
- [](ClientLayerTreeFrameSink* layer_tree_frame_sink,
- base::RunLoop* detach_run_loop) {
- layer_tree_frame_sink->DetachFromClient();
- detach_run_loop->Quit();
- };
- bg_thread.task_runner()->PostTask(
- FROM_HERE, base::BindOnce(detach_in_background,
- base::Unretained(&layer_tree_frame_sink),
- base::Unretained(&detach_run_loop)));
- detach_run_loop.Run();
-}
-
-} // namespace
-} // namespace viz
diff --git a/chromium/components/viz/client/client_resource_provider.cc b/chromium/components/viz/client/client_resource_provider.cc
new file mode 100644
index 00000000000..c7b2477ea18
--- /dev/null
+++ b/chromium/components/viz/client/client_resource_provider.cc
@@ -0,0 +1,287 @@
+// 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/client/client_resource_provider.h"
+
+#include "base/bits.h"
+#include "base/debug/stack_trace.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/resources/resource_format_utils.h"
+#include "components/viz/common/resources/resource_sizes.h"
+#include "components/viz/common/resources/returned_resource.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/raster_interface.h"
+#include "gpu/command_buffer/common/capabilities.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+
+namespace viz {
+
+struct ClientResourceProvider::ImportedResource {
+ TransferableResource resource;
+ std::unique_ptr<SingleReleaseCallback> release_callback;
+ int exported_count = 0;
+ bool marked_for_deletion = false;
+
+ gpu::SyncToken returned_sync_token;
+ bool returned_lost = false;
+
+#if DCHECK_IS_ON()
+ base::debug::StackTrace stack_trace;
+#endif
+
+ ImportedResource(ResourceId id,
+ const TransferableResource& resource,
+ std::unique_ptr<SingleReleaseCallback> release_callback)
+ : resource(resource),
+ release_callback(std::move(release_callback)),
+ // If the resource is immediately deleted, it returns the same SyncToken
+ // it came with. The client may need to wait on that before deleting the
+ // backing or reusing it.
+ returned_sync_token(resource.mailbox_holder.sync_token) {
+ // Replace the |resource| id with the local id from this
+ // ClientResourceProvider.
+ this->resource.id = id;
+ }
+ ~ImportedResource() = default;
+
+ ImportedResource(ImportedResource&&) = default;
+ ImportedResource& operator=(ImportedResource&&) = default;
+};
+
+ClientResourceProvider::ClientResourceProvider(
+ bool delegated_sync_points_required)
+ : delegated_sync_points_required_(delegated_sync_points_required) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+ClientResourceProvider::~ClientResourceProvider() {
+ // If this fails, there are outstanding resources exported that should be
+ // lost and returned by calling ShutdownAndReleaseAllResources(), or there
+ // are resources that were imported without being removed by
+ // RemoveImportedResource(). In either case, calling
+ // ShutdownAndReleaseAllResources() will help, as it will report which
+ // resources were imported without being removed as well.
+ DCHECK(imported_resources_.empty());
+}
+
+gpu::SyncToken ClientResourceProvider::GenerateSyncTokenHelper(
+ gpu::gles2::GLES2Interface* gl) {
+ DCHECK(gl);
+ gpu::SyncToken sync_token;
+ gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+ DCHECK(sync_token.HasData() ||
+ gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR);
+ return sync_token;
+}
+
+gpu::SyncToken ClientResourceProvider::GenerateSyncTokenHelper(
+ gpu::raster::RasterInterface* ri) {
+ DCHECK(ri);
+ gpu::SyncToken sync_token;
+ ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+ DCHECK(sync_token.HasData() ||
+ ri->GetGraphicsResetStatusKHR() != GL_NO_ERROR);
+ return sync_token;
+}
+
+void ClientResourceProvider::PrepareSendToParent(
+ const std::vector<ResourceId>& export_ids,
+ std::vector<TransferableResource>* list,
+ ContextProvider* context_provider) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ // This function goes through the array multiple times, store the resources
+ // as pointers so we don't have to look up the resource id multiple times.
+ // Make sure the maps do not change while these vectors are alive or they
+ // will become invalid.
+ std::vector<ImportedResource*> imports;
+ imports.reserve(export_ids.size());
+ for (const ResourceId id : export_ids) {
+ auto it = imported_resources_.find(id);
+ DCHECK(it != imported_resources_.end());
+ imports.push_back(&it->second);
+ }
+
+ // Lazily create any mailboxes and verify all unverified sync tokens.
+ std::vector<GLbyte*> unverified_sync_tokens;
+ if (delegated_sync_points_required_) {
+ for (ImportedResource* imported : imports) {
+ if (!imported->resource.is_software &&
+ !imported->resource.mailbox_holder.sync_token.verified_flush()) {
+ unverified_sync_tokens.push_back(
+ imported->resource.mailbox_holder.sync_token.GetData());
+ }
+ }
+ }
+
+ if (!unverified_sync_tokens.empty()) {
+ DCHECK(delegated_sync_points_required_);
+ DCHECK(context_provider);
+ context_provider->ContextGL()->VerifySyncTokensCHROMIUM(
+ unverified_sync_tokens.data(), unverified_sync_tokens.size());
+ }
+
+ for (ImportedResource* imported : imports) {
+ list->push_back(imported->resource);
+ imported->exported_count++;
+ }
+}
+
+void ClientResourceProvider::ReceiveReturnsFromParent(
+ const std::vector<ReturnedResource>& resources) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ for (const ReturnedResource& returned : resources) {
+ ResourceId local_id = returned.id;
+
+ auto import_it = imported_resources_.find(local_id);
+ DCHECK(import_it != imported_resources_.end());
+ ImportedResource& imported = import_it->second;
+
+ DCHECK_GE(imported.exported_count, returned.count);
+ imported.exported_count -= returned.count;
+ imported.returned_lost |= returned.lost;
+
+ if (imported.exported_count)
+ continue;
+
+ if (returned.sync_token.HasData()) {
+ DCHECK(!imported.resource.is_software);
+ imported.returned_sync_token = returned.sync_token;
+ }
+
+ if (imported.marked_for_deletion) {
+ imported.release_callback->Run(imported.returned_sync_token,
+ imported.returned_lost);
+ imported_resources_.erase(import_it);
+ }
+ }
+}
+
+ResourceId ClientResourceProvider::ImportResource(
+ const TransferableResource& resource,
+ std::unique_ptr<SingleReleaseCallback> release_callback) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ ResourceId id = next_id_++;
+ auto result = imported_resources_.emplace(
+ id, ImportedResource(id, resource, std::move(release_callback)));
+ DCHECK(result.second); // If false, the id was already in the map.
+ return id;
+}
+
+void ClientResourceProvider::RemoveImportedResource(ResourceId id) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ auto it = imported_resources_.find(id);
+ DCHECK(it != imported_resources_.end());
+ ImportedResource& imported = it->second;
+ imported.marked_for_deletion = true;
+ if (imported.exported_count == 0) {
+ imported.release_callback->Run(imported.returned_sync_token,
+ imported.returned_lost);
+ imported_resources_.erase(it);
+ }
+}
+
+void ClientResourceProvider::ReleaseAllExportedResources(bool lose) {
+ std::vector<ResourceId> to_remove;
+ for (auto& pair : imported_resources_) {
+ ImportedResource& imported = pair.second;
+ if (!imported.exported_count)
+ continue;
+ imported.exported_count = 0;
+ imported.returned_lost |= lose;
+ if (imported.marked_for_deletion) {
+ imported.release_callback->Run(imported.returned_sync_token,
+ imported.returned_lost);
+ to_remove.push_back(pair.first);
+ }
+ }
+ for (ResourceId id : to_remove)
+ imported_resources_.erase(id);
+}
+
+void ClientResourceProvider::ShutdownAndReleaseAllResources() {
+ for (auto& pair : imported_resources_) {
+ ImportedResource& imported = pair.second;
+
+#if DCHECK_IS_ON()
+ // If this is false, then the resource has not been removed via
+ // RemoveImportedResource(), and all resources should be removed before
+ // we resort to marking resources as lost during shutdown.
+ DCHECK(imported.marked_for_deletion)
+ << "id: " << pair.first << " from:\n"
+ << imported.stack_trace.ToString() << "===";
+ DCHECK(imported.exported_count) << "id: " << pair.first << " from:\n"
+ << imported.stack_trace.ToString() << "===";
+#endif
+
+ imported.release_callback->Run(imported.returned_sync_token,
+ /*is_lost=*/true);
+ }
+ imported_resources_.clear();
+}
+
+ClientResourceProvider::ScopedSkSurface::ScopedSkSurface(
+ GrContext* gr_context,
+ GLuint texture_id,
+ GLenum texture_target,
+ const gfx::Size& size,
+ ResourceFormat format,
+ bool can_use_lcd_text,
+ int msaa_sample_count) {
+ GrGLTextureInfo texture_info;
+ texture_info.fID = texture_id;
+ texture_info.fTarget = texture_target;
+ texture_info.fFormat = TextureStorageFormat(format);
+ GrBackendTexture backend_texture(size.width(), size.height(),
+ GrMipMapped::kNo, texture_info);
+ SkSurfaceProps surface_props = ComputeSurfaceProps(can_use_lcd_text);
+ // This type is used only for gpu raster, which implies gpu compositing.
+ bool gpu_compositing = true;
+ surface_ = SkSurface::MakeFromBackendTextureAsRenderTarget(
+ gr_context, backend_texture, kTopLeft_GrSurfaceOrigin, msaa_sample_count,
+ ResourceFormatToClosestSkColorType(gpu_compositing, format), nullptr,
+ &surface_props);
+}
+
+ClientResourceProvider::ScopedSkSurface::~ScopedSkSurface() {
+ if (surface_)
+ surface_->prepareForExternalIO();
+}
+
+SkSurfaceProps ClientResourceProvider::ScopedSkSurface::ComputeSurfaceProps(
+ bool can_use_lcd_text) {
+ uint32_t flags = 0;
+ // Use unknown pixel geometry to disable LCD text.
+ SkSurfaceProps surface_props(flags, kUnknown_SkPixelGeometry);
+ if (can_use_lcd_text) {
+ // LegacyFontHost will get LCD text and skia figures out what type to use.
+ surface_props =
+ SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType);
+ }
+ return surface_props;
+}
+
+void ClientResourceProvider::ValidateResource(ResourceId id) const {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(id);
+ DCHECK(imported_resources_.find(id) != imported_resources_.end());
+}
+
+bool ClientResourceProvider::InUseByConsumer(ResourceId id) {
+ auto it = imported_resources_.find(id);
+ DCHECK(it != imported_resources_.end());
+ ImportedResource& imported = it->second;
+ return imported.exported_count > 0 || imported.returned_lost;
+}
+
+size_t ClientResourceProvider::num_resources_for_testing() const {
+ return imported_resources_.size();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/client/client_resource_provider.h b/chromium/components/viz/client/client_resource_provider.h
new file mode 100644
index 00000000000..4087b5754ec
--- /dev/null
+++ b/chromium/components/viz/client/client_resource_provider.h
@@ -0,0 +1,141 @@
+// 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_CLIENT_CLIENT_RESOURCE_PROVIDER_H_
+#define COMPONENTS_VIZ_CLIENT_CLIENT_RESOURCE_PROVIDER_H_
+
+#include <vector>
+
+#include "base/threading/thread_checker.h"
+#include "components/viz/client/viz_client_export.h"
+#include "components/viz/common/display/renderer_settings.h"
+#include "components/viz/common/resources/release_callback.h"
+#include "components/viz/common/resources/resource_id.h"
+#include "components/viz/common/resources/resource_settings.h"
+#include "components/viz/common/resources/single_release_callback.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+
+namespace gpu {
+namespace gles2 {
+class GLES2Interface;
+}
+namespace raster {
+class RasterInterface;
+}
+} // namespace gpu
+
+namespace viz {
+class ContextProvider;
+
+// This class is used to give an integer name (ResourceId) to a gpu or software
+// resource (shipped as a TransferableResource), in order to use that name in
+// DrawQuads and give the resource to the viz display compositor. When the
+// resource is removed from the ClientResourceProvider, the
+// SingleReleaseCallback will be called once the resource is no longer in use by
+// the display compositor.
+//
+// This class is not thread-safe and can only be called from the thread it was
+// created on (in practice, the impl thread).
+class VIZ_CLIENT_EXPORT ClientResourceProvider {
+ public:
+ explicit ClientResourceProvider(bool delegated_sync_points_required);
+ ~ClientResourceProvider();
+
+ static gpu::SyncToken GenerateSyncTokenHelper(gpu::gles2::GLES2Interface* gl);
+ static gpu::SyncToken GenerateSyncTokenHelper(
+ gpu::raster::RasterInterface* ri);
+
+ // Prepares resources to be transfered to the parent, moving them to
+ // mailboxes and serializing meta-data into TransferableResources.
+ // Resources are not removed from the ResourceProvider, but are marked as
+ // "in use".
+ void PrepareSendToParent(
+ const std::vector<ResourceId>& resource_ids,
+ std::vector<TransferableResource>* transferable_resources,
+ ContextProvider* context_provider);
+
+ // Receives resources from the parent, moving them from mailboxes. ResourceIds
+ // passed are in the child namespace.
+ // NOTE: if the sync_token is set on any TransferableResource, this will
+ // wait on it.
+ void ReceiveReturnsFromParent(
+ const std::vector<ReturnedResource>& transferable_resources);
+
+ // Receives a resource from an external client that can be used in compositor
+ // frames, via the returned ResourceId.
+ ResourceId ImportResource(const TransferableResource&,
+ std::unique_ptr<SingleReleaseCallback>);
+ // Removes an imported resource, which will call the ReleaseCallback given
+ // originally, once the resource is no longer in use by any compositor frame.
+ void RemoveImportedResource(ResourceId);
+
+ // Call this to indicate that the connection to the parent is lost and
+ // resources previously exported will not be able to be returned. If |lose| is
+ // true, the resources are also marked as lost, to indicate the state of each
+ // resource can not be known, and/or they can not be reused.
+ //
+ // When a resource is sent to the parent (via PrepareSendToParent) it is put
+ // into an exported state, preventing it from being released until the parent
+ // returns the resource. Calling this drops that exported state on all
+ // resources allowing immediate release of them if they are removed via
+ // RemoveImportedResource().
+ void ReleaseAllExportedResources(bool lose);
+
+ // Immediately runs the SingleReleaseCallback for all resources that have been
+ // previously imported and removed, but not released yet. There should not be
+ // any imported resources yet when this is called, as they can be removed
+ // first via RemoveImportedResource(), and potentially avoid being lost.
+ void ShutdownAndReleaseAllResources();
+
+ // Verify that the ResourceId is valid and is known to this class, for debug
+ // checks.
+ void ValidateResource(ResourceId id) const;
+
+ // Checks whether a resource is in use by a consumer.
+ bool InUseByConsumer(ResourceId id);
+
+ size_t num_resources_for_testing() const;
+
+ class VIZ_CLIENT_EXPORT ScopedSkSurface {
+ public:
+ ScopedSkSurface(GrContext* gr_context,
+ GLuint texture_id,
+ GLenum texture_target,
+ const gfx::Size& size,
+ ResourceFormat format,
+ bool can_use_lcd_text,
+ int msaa_sample_count);
+ ~ScopedSkSurface();
+
+ SkSurface* surface() const { return surface_.get(); }
+
+ static SkSurfaceProps ComputeSurfaceProps(bool can_use_lcd_text);
+
+ private:
+ sk_sp<SkSurface> surface_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSkSurface);
+ };
+
+ private:
+ struct ImportedResource;
+
+ THREAD_CHECKER(thread_checker_);
+ const bool delegated_sync_points_required_;
+
+ base::flat_map<ResourceId, ImportedResource> imported_resources_;
+ // The ResourceIds in ClientResourceProvider start from 1 to avoid
+ // conflicts with id from DisplayResourceProvider.
+ ResourceId next_id_ = 1;
+
+ DISALLOW_COPY_AND_ASSIGN(ClientResourceProvider);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_CLIENT_CLIENT_RESOURCE_PROVIDER_H_
diff --git a/chromium/components/viz/client/client_resource_provider_unittest.cc b/chromium/components/viz/client/client_resource_provider_unittest.cc
new file mode 100644
index 00000000000..da03c3cf750
--- /dev/null
+++ b/chromium/components/viz/client/client_resource_provider_unittest.cc
@@ -0,0 +1,571 @@
+// 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/client/client_resource_provider.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/test/gtest_util.h"
+#include "components/viz/common/resources/returned_resource.h"
+#include "components/viz/common/resources/single_release_callback.h"
+#include "components/viz/test/test_context_provider.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+namespace viz {
+namespace {
+
+class ClientResourceProviderTest : public testing::TestWithParam<bool> {
+ protected:
+ ClientResourceProviderTest()
+ : use_gpu_(GetParam()),
+ context_provider_(TestContextProvider::Create()),
+ bound_(context_provider_->BindToCurrentThread()) {
+ DCHECK_EQ(bound_, gpu::ContextResult::kSuccess);
+ }
+
+ void SetUp() override {
+ provider_ = std::make_unique<ClientResourceProvider>(
+ /*delegated_sync_points_required=*/true);
+ }
+
+ void TearDown() override { provider_ = nullptr; }
+
+ gpu::Mailbox MailboxFromChar(char value) {
+ gpu::Mailbox mailbox;
+ memset(mailbox.name, value, sizeof(mailbox.name));
+ return mailbox;
+ }
+
+ gpu::SyncToken SyncTokenFromUInt(uint32_t value) {
+ return gpu::SyncToken(gpu::CommandBufferNamespace::GPU_IO,
+ gpu::CommandBufferId::FromUnsafeValue(0x123), value);
+ }
+
+ TransferableResource MakeTransferableResource(bool gpu,
+ char mailbox_char,
+ uint32_t sync_token_value) {
+ TransferableResource r;
+ r.id = mailbox_char;
+ r.is_software = !gpu;
+ r.filter = 456;
+ r.size = gfx::Size(10, 11);
+ r.mailbox_holder.mailbox = MailboxFromChar(mailbox_char);
+ if (gpu) {
+ r.mailbox_holder.sync_token = SyncTokenFromUInt(sync_token_value);
+ r.mailbox_holder.texture_target = 6;
+ }
+ return r;
+ }
+
+ bool use_gpu() const { return use_gpu_; }
+ ClientResourceProvider& provider() const { return *provider_; }
+ ContextProvider* context_provider() const { return context_provider_.get(); }
+
+ void DestroyProvider() {
+ provider_->ShutdownAndReleaseAllResources();
+ provider_ = nullptr;
+ }
+
+ private:
+ bool use_gpu_;
+ scoped_refptr<TestContextProvider> context_provider_;
+ gpu::ContextResult bound_;
+ std::unique_ptr<ClientResourceProvider> provider_;
+};
+
+INSTANTIATE_TEST_CASE_P(ClientResourceProviderTests,
+ ClientResourceProviderTest,
+ ::testing::Values(false, true));
+
+class MockReleaseCallback {
+ public:
+ MOCK_METHOD2(Released, void(const gpu::SyncToken& token, bool lost));
+};
+
+TEST_P(ClientResourceProviderTest, TransferableResourceReleased) {
+ MockReleaseCallback release;
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
+ ResourceId id = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(base::BindOnce(
+ &MockReleaseCallback::Released, base::Unretained(&release))));
+ // The local id is different.
+ EXPECT_NE(id, tran.id);
+
+ // The same SyncToken that was sent is returned when the resource was never
+ // exported. The SyncToken may be from any context, and the ReleaseCallback
+ // may need to wait on it before interacting with the resource on its context.
+ EXPECT_CALL(release, Released(tran.mailbox_holder.sync_token, false));
+ provider().RemoveImportedResource(id);
+}
+
+TEST_P(ClientResourceProviderTest, TransferableResourceSendToParent) {
+ MockReleaseCallback release;
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
+ ResourceId id = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(base::BindOnce(
+ &MockReleaseCallback::Released, base::Unretained(&release))));
+
+ // Export the resource.
+ std::vector<ResourceId> to_send = {id};
+ std::vector<TransferableResource> exported;
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+ ASSERT_EQ(exported.size(), 1u);
+
+ // Exported resource matches except for the id which was mapped
+ // to the local ResourceProvider, and the sync token should be
+ // verified if it's a gpu resource.
+ gpu::SyncToken verified_sync_token = tran.mailbox_holder.sync_token;
+ if (!tran.is_software)
+ verified_sync_token.SetVerifyFlush();
+ EXPECT_EQ(exported[0].id, id);
+ EXPECT_EQ(exported[0].is_software, tran.is_software);
+ EXPECT_EQ(exported[0].filter, tran.filter);
+ EXPECT_EQ(exported[0].size, tran.size);
+ EXPECT_EQ(exported[0].mailbox_holder.mailbox, tran.mailbox_holder.mailbox);
+ EXPECT_EQ(exported[0].mailbox_holder.sync_token, verified_sync_token);
+ EXPECT_EQ(exported[0].mailbox_holder.texture_target,
+ tran.mailbox_holder.texture_target);
+
+ // Exported resources are not released when removed, until the export returns.
+ EXPECT_CALL(release, Released(_, _)).Times(0);
+ provider().RemoveImportedResource(id);
+
+ // Return the resource, with a sync token if using gpu.
+ std::vector<ReturnedResource> returned;
+ returned.push_back({});
+ returned.back().id = exported[0].id;
+ if (use_gpu())
+ returned.back().sync_token = SyncTokenFromUInt(31);
+ returned.back().count = 1;
+ returned.back().lost = false;
+
+ // The sync token is given to the ReleaseCallback.
+ EXPECT_CALL(release, Released(returned[0].sync_token, false));
+ provider().ReceiveReturnsFromParent(returned);
+}
+
+TEST_P(ClientResourceProviderTest, TransferableResourceSendTwoToParent) {
+ TransferableResource tran[] = {MakeTransferableResource(use_gpu(), 'a', 15),
+ MakeTransferableResource(use_gpu(), 'b', 16)};
+ ResourceId id1 = provider().ImportResource(
+ tran[0], SingleReleaseCallback::Create(base::DoNothing()));
+ ResourceId id2 = provider().ImportResource(
+ tran[1], SingleReleaseCallback::Create(base::DoNothing()));
+
+ // Export the resource.
+ std::vector<ResourceId> to_send = {id1, id2};
+ std::vector<TransferableResource> exported;
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+ ASSERT_EQ(exported.size(), 2u);
+
+ // Exported resource matches except for the id which was mapped
+ // to the local ResourceProvider, and the sync token should be
+ // verified if it's a gpu resource.
+ for (int i = 0; i < 2; ++i) {
+ gpu::SyncToken verified_sync_token = tran[i].mailbox_holder.sync_token;
+ if (!tran[i].is_software)
+ verified_sync_token.SetVerifyFlush();
+ EXPECT_EQ(exported[i].id, to_send[i]);
+ EXPECT_EQ(exported[i].is_software, tran[i].is_software);
+ EXPECT_EQ(exported[i].filter, tran[i].filter);
+ EXPECT_EQ(exported[i].size, tran[i].size);
+ EXPECT_EQ(exported[i].mailbox_holder.mailbox,
+ tran[i].mailbox_holder.mailbox);
+ EXPECT_EQ(exported[i].mailbox_holder.sync_token, verified_sync_token);
+ EXPECT_EQ(exported[i].mailbox_holder.texture_target,
+ tran[i].mailbox_holder.texture_target);
+ }
+
+ provider().RemoveImportedResource(id1);
+ provider().RemoveImportedResource(id2);
+ DestroyProvider();
+}
+
+TEST_P(ClientResourceProviderTest, TransferableResourceSendToParentTwoTimes) {
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
+ ResourceId id = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(base::DoNothing()));
+
+ // Export the resource.
+ std::vector<ResourceId> to_send = {id};
+ std::vector<TransferableResource> exported;
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+ ASSERT_EQ(exported.size(), 1u);
+ EXPECT_EQ(exported[0].id, id);
+
+ // Return the resource, with a sync token if using gpu.
+ std::vector<ReturnedResource> returned;
+ returned.push_back({});
+ returned.back().id = exported[0].id;
+ if (use_gpu())
+ returned.back().sync_token = SyncTokenFromUInt(31);
+ returned.back().count = 1;
+ returned.back().lost = false;
+ provider().ReceiveReturnsFromParent(returned);
+
+ // Then export again, it still sends.
+ exported.clear();
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+ ASSERT_EQ(exported.size(), 1u);
+ EXPECT_EQ(exported[0].id, id);
+
+ provider().RemoveImportedResource(id);
+ DestroyProvider();
+}
+
+TEST_P(ClientResourceProviderTest,
+ TransferableResourceLostOnShutdownIfExported) {
+ MockReleaseCallback release;
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
+ ResourceId id = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(base::BindOnce(
+ &MockReleaseCallback::Released, base::Unretained(&release))));
+
+ // Export the resource.
+ std::vector<ResourceId> to_send = {id};
+ std::vector<TransferableResource> exported;
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+
+ provider().RemoveImportedResource(id);
+
+ EXPECT_CALL(release, Released(_, true));
+ DestroyProvider();
+}
+
+TEST_P(ClientResourceProviderTest, TransferableResourceRemovedAfterReturn) {
+ MockReleaseCallback release;
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
+ ResourceId id = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(base::BindOnce(
+ &MockReleaseCallback::Released, base::Unretained(&release))));
+
+ // Export the resource.
+ std::vector<ResourceId> to_send = {id};
+ std::vector<TransferableResource> exported;
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+
+ // Return the resource. This does not release the resource back to
+ // the client.
+ std::vector<ReturnedResource> returned;
+ returned.push_back({});
+ returned.back().id = exported[0].id;
+ if (use_gpu())
+ returned.back().sync_token = SyncTokenFromUInt(31);
+ returned.back().count = 1;
+ returned.back().lost = false;
+
+ EXPECT_CALL(release, Released(_, _)).Times(0);
+ provider().ReceiveReturnsFromParent(returned);
+
+ // Once removed, the resource is released.
+ EXPECT_CALL(release, Released(returned[0].sync_token, false));
+ provider().RemoveImportedResource(id);
+}
+
+TEST_P(ClientResourceProviderTest, TransferableResourceExportedTwice) {
+ MockReleaseCallback release;
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
+ ResourceId id = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(base::BindOnce(
+ &MockReleaseCallback::Released, base::Unretained(&release))));
+
+ // Export the resource once.
+ std::vector<ResourceId> to_send = {id};
+ std::vector<TransferableResource> exported;
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+
+ // Exported resources are not released when removed, until all exports are
+ // returned.
+ EXPECT_CALL(release, Released(_, _)).Times(0);
+ provider().RemoveImportedResource(id);
+
+ // Export the resource twice.
+ exported = {};
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+
+ // Return the resource the first time.
+ std::vector<ReturnedResource> returned;
+ returned.push_back({});
+ returned.back().id = exported[0].id;
+ if (use_gpu())
+ returned.back().sync_token = SyncTokenFromUInt(31);
+ returned.back().count = 1;
+ returned.back().lost = false;
+ provider().ReceiveReturnsFromParent(returned);
+
+ // And a second time, with a different sync token. Now the ReleaseCallback can
+ // happen, using the latest sync token.
+ if (use_gpu())
+ returned.back().sync_token = SyncTokenFromUInt(47);
+ EXPECT_CALL(release, Released(returned[0].sync_token, false));
+ provider().ReceiveReturnsFromParent(returned);
+}
+
+TEST_P(ClientResourceProviderTest, TransferableResourceReturnedTwiceAtOnce) {
+ MockReleaseCallback release;
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
+ ResourceId id = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(base::BindOnce(
+ &MockReleaseCallback::Released, base::Unretained(&release))));
+
+ // Export the resource once.
+ std::vector<ResourceId> to_send = {id};
+ std::vector<TransferableResource> exported;
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+
+ // Exported resources are not released when removed, until all exports are
+ // returned.
+ EXPECT_CALL(release, Released(_, _)).Times(0);
+ provider().RemoveImportedResource(id);
+
+ // Export the resource twice.
+ exported = {};
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+
+ // Return both exports at once.
+ std::vector<ReturnedResource> returned;
+ returned.push_back({});
+ returned.back().id = exported[0].id;
+ if (use_gpu())
+ returned.back().sync_token = SyncTokenFromUInt(31);
+ returned.back().count = 2;
+ returned.back().lost = false;
+
+ // When returned, the ReleaseCallback can happen, using the latest sync token.
+ EXPECT_CALL(release, Released(returned[0].sync_token, false));
+ provider().ReceiveReturnsFromParent(returned);
+}
+
+TEST_P(ClientResourceProviderTest, TransferableResourceLostOnReturn) {
+ MockReleaseCallback release;
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
+ ResourceId id = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(base::BindOnce(
+ &MockReleaseCallback::Released, base::Unretained(&release))));
+
+ // Export the resource once.
+ std::vector<ResourceId> to_send = {id};
+ std::vector<TransferableResource> exported;
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+
+ // Exported resources are not released when removed, until all exports are
+ // returned.
+ EXPECT_CALL(release, Released(_, _)).Times(0);
+ provider().RemoveImportedResource(id);
+
+ // Export the resource twice.
+ exported = {};
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+
+ // Return the resource the first time, not lost.
+ std::vector<ReturnedResource> returned;
+ returned.push_back({});
+ returned.back().id = exported[0].id;
+ returned.back().count = 1;
+ returned.back().lost = false;
+ provider().ReceiveReturnsFromParent(returned);
+
+ // Return a second time, as lost. The ReturnCallback should report it
+ // lost.
+ returned.back().lost = true;
+ EXPECT_CALL(release, Released(_, true));
+ provider().ReceiveReturnsFromParent(returned);
+}
+
+TEST_P(ClientResourceProviderTest, TransferableResourceLostOnFirstReturn) {
+ MockReleaseCallback release;
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
+ ResourceId id = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(base::BindOnce(
+ &MockReleaseCallback::Released, base::Unretained(&release))));
+
+ // Export the resource once.
+ std::vector<ResourceId> to_send = {id};
+ std::vector<TransferableResource> exported;
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+
+ // Exported resources are not released when removed, until all exports are
+ // returned.
+ EXPECT_CALL(release, Released(_, _)).Times(0);
+ provider().RemoveImportedResource(id);
+
+ // Export the resource twice.
+ exported = {};
+ provider().PrepareSendToParent(to_send, &exported, context_provider());
+
+ // Return the resource the first time, marked as lost.
+ std::vector<ReturnedResource> returned;
+ returned.push_back({});
+ returned.back().id = exported[0].id;
+ returned.back().count = 1;
+ returned.back().lost = true;
+ provider().ReceiveReturnsFromParent(returned);
+
+ // Return a second time, not lost. The first lost signal should not be lost.
+ returned.back().lost = false;
+ EXPECT_CALL(release, Released(_, true));
+ provider().ReceiveReturnsFromParent(returned);
+}
+
+TEST_P(ClientResourceProviderTest, ReturnedSyncTokensArePassedToClient) {
+ // SyncTokens are gpu-only.
+ if (!use_gpu())
+ return;
+
+ MockReleaseCallback release;
+
+ GLuint texture;
+ context_provider()->ContextGL()->GenTextures(1, &texture);
+ context_provider()->ContextGL()->BindTexture(GL_TEXTURE_2D, texture);
+ gpu::Mailbox mailbox;
+ context_provider()->ContextGL()->ProduceTextureDirectCHROMIUM(texture,
+ mailbox.name);
+ gpu::SyncToken sync_token;
+ context_provider()->ContextGL()->GenSyncTokenCHROMIUM(sync_token.GetData());
+
+ auto tran = TransferableResource::MakeGL(mailbox, GL_LINEAR, GL_TEXTURE_2D,
+ sync_token);
+ ResourceId resource = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(base::BindOnce(
+ &MockReleaseCallback::Released, base::Unretained(&release))));
+
+ EXPECT_TRUE(tran.mailbox_holder.sync_token.HasData());
+ // All the logic below assumes that the sync token releases are all positive.
+ EXPECT_LT(0u, tran.mailbox_holder.sync_token.release_count());
+
+ // Transfer the resource, expect the sync points to be consistent.
+ std::vector<TransferableResource> list;
+ provider().PrepareSendToParent({resource}, &list, context_provider());
+ ASSERT_EQ(1u, list.size());
+ EXPECT_LE(sync_token.release_count(),
+ list[0].mailbox_holder.sync_token.release_count());
+ EXPECT_EQ(0, memcmp(mailbox.name, list[0].mailbox_holder.mailbox.name,
+ sizeof(mailbox.name)));
+
+ // Make a new texture id from the mailbox.
+ context_provider()->ContextGL()->WaitSyncTokenCHROMIUM(
+ list[0].mailbox_holder.sync_token.GetConstData());
+ unsigned other_texture =
+ context_provider()->ContextGL()->CreateAndConsumeTextureCHROMIUM(
+ mailbox.name);
+ // Then delete it and make a new SyncToken.
+ context_provider()->ContextGL()->DeleteTextures(1, &other_texture);
+ context_provider()->ContextGL()->GenSyncTokenCHROMIUM(
+ list[0].mailbox_holder.sync_token.GetData());
+ EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
+
+ // Receive the resource, then delete it, expect the SyncTokens to be
+ // consistent.
+ provider().ReceiveReturnsFromParent(
+ TransferableResource::ReturnResources(list));
+
+ gpu::SyncToken returned_sync_token;
+ EXPECT_CALL(release, Released(_, false))
+ .WillOnce(testing::SaveArg<0>(&returned_sync_token));
+ provider().RemoveImportedResource(resource);
+ EXPECT_GE(returned_sync_token.release_count(),
+ list[0].mailbox_holder.sync_token.release_count());
+}
+
+TEST_P(ClientResourceProviderTest, LostResourcesAreReturnedLost) {
+ MockReleaseCallback release;
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
+ ResourceId resource = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(base::BindOnce(
+ &MockReleaseCallback::Released, base::Unretained(&release))));
+
+ // Transfer the resource to the parent.
+ std::vector<TransferableResource> list;
+ provider().PrepareSendToParent({resource}, &list, context_provider());
+ EXPECT_EQ(1u, list.size());
+
+ // Receive it back marked lost.
+ std::vector<ReturnedResource> returned_to_child;
+ returned_to_child.push_back(list[0].ToReturnedResource());
+ returned_to_child.back().lost = true;
+ provider().ReceiveReturnsFromParent(returned_to_child);
+
+ // Delete the resource in the child. Expect the resource to be lost.
+ EXPECT_CALL(release, Released(_, true));
+ provider().RemoveImportedResource(resource);
+}
+
+TEST_P(ClientResourceProviderTest, ShutdownLosesExportedResources) {
+ MockReleaseCallback release;
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
+ ResourceId resource = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(base::BindOnce(
+ &MockReleaseCallback::Released, base::Unretained(&release))));
+
+ // Transfer the resource to the parent.
+ std::vector<TransferableResource> list;
+ provider().PrepareSendToParent({resource}, &list, context_provider());
+ EXPECT_EQ(1u, list.size());
+
+ // Remove it in the ClientResourceProvider, but since it's exported it's not
+ // returned yet.
+ provider().RemoveImportedResource(resource);
+
+ // Destroy the ClientResourceProvider, the resource is returned lost.
+ EXPECT_CALL(release, Released(_, true));
+ DestroyProvider();
+}
+
+TEST_P(ClientResourceProviderTest, ReleaseExportedResources) {
+ MockReleaseCallback release;
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
+ ResourceId resource = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(base::BindOnce(
+ &MockReleaseCallback::Released, base::Unretained(&release))));
+
+ // Transfer the resource to the parent.
+ std::vector<TransferableResource> list;
+ provider().PrepareSendToParent({resource}, &list, context_provider());
+ EXPECT_EQ(1u, list.size());
+
+ // Remove it in the ClientResourceProvider, but since it's exported it's not
+ // returned yet.
+ provider().RemoveImportedResource(resource);
+
+ // Drop any exported resources. They are returned lost for gpu compositing,
+ // since gpu resources are modified (in their metadata) while being used by
+ // the parent.
+ EXPECT_CALL(release, Released(_, use_gpu()));
+ provider().ReleaseAllExportedResources(use_gpu());
+
+ EXPECT_CALL(release, Released(_, _)).Times(0);
+}
+
+TEST_P(ClientResourceProviderTest, ReleaseExportedResourcesThenRemove) {
+ MockReleaseCallback release;
+ TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
+ ResourceId resource = provider().ImportResource(
+ tran, SingleReleaseCallback::Create(base::BindOnce(
+ &MockReleaseCallback::Released, base::Unretained(&release))));
+
+ // Transfer the resource to the parent.
+ std::vector<TransferableResource> list;
+ provider().PrepareSendToParent({resource}, &list, context_provider());
+ EXPECT_EQ(1u, list.size());
+
+ // Drop any exported resources. Yhey are now considered lost for gpu
+ // compositing, since gpu resources are modified (in their metadata) while
+ // being used by the parent.
+ provider().ReleaseAllExportedResources(use_gpu());
+
+ EXPECT_CALL(release, Released(_, use_gpu()));
+ // Remove it in the ClientResourceProvider, it was exported so wouldn't be
+ // released here, except that we dropped the export above.
+ provider().RemoveImportedResource(resource);
+
+ EXPECT_CALL(release, Released(_, _)).Times(0);
+}
+
+} // namespace
+} // namespace viz
diff --git a/chromium/components/viz/client/frame_evictor.h b/chromium/components/viz/client/frame_evictor.h
index 95d5d3dfe9c..aaaf4f7dcf2 100644
--- a/chromium/components/viz/client/frame_evictor.h
+++ b/chromium/components/viz/client/frame_evictor.h
@@ -28,6 +28,7 @@ class VIZ_CLIENT_EXPORT FrameEvictor : public FrameEvictionManagerClient {
void LockFrame();
void UnlockFrame();
bool HasFrame() { return has_frame_; }
+ bool visible() const { return visible_; }
private:
// FrameEvictionManagerClient implementation.
diff --git a/chromium/components/viz/client/hit_test_data_provider_draw_quad_unittest.cc b/chromium/components/viz/client/hit_test_data_provider_draw_quad_unittest.cc
index 2e27702b44d..e67bbd4476a 100644
--- a/chromium/components/viz/client/hit_test_data_provider_draw_quad_unittest.cc
+++ b/chromium/components/viz/client/hit_test_data_provider_draw_quad_unittest.cc
@@ -11,14 +11,13 @@
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread.h"
-#include "cc/test/fake_layer_tree_frame_sink_client.h"
#include "components/viz/client/local_surface_id_provider.h"
+#include "components/viz/common/hit_test/hit_test_region_list.h"
#include "components/viz/common/quads/surface_draw_quad.h"
#include "components/viz/test/compositor_frame_helpers.h"
#include "components/viz/test/test_context_provider.h"
#include "components/viz/test/test_gpu_memory_buffer_manager.h"
#include "mojo/public/cpp/bindings/interface_request.h"
-#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace viz {
diff --git a/chromium/components/viz/common/resources/shared_bitmap_reporter.cc b/chromium/components/viz/client/shared_bitmap_reporter.cc
index fcd80364869..597a8a98b04 100644
--- a/chromium/components/viz/common/resources/shared_bitmap_reporter.cc
+++ b/chromium/components/viz/client/shared_bitmap_reporter.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/viz/common/resources/shared_bitmap_reporter.h"
+#include "components/viz/client/shared_bitmap_reporter.h"
namespace viz {
diff --git a/chromium/components/viz/client/shared_bitmap_reporter.h b/chromium/components/viz/client/shared_bitmap_reporter.h
new file mode 100644
index 00000000000..b2e1549ad85
--- /dev/null
+++ b/chromium/components/viz/client/shared_bitmap_reporter.h
@@ -0,0 +1,35 @@
+// 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_CLIENT_SHARED_BITMAP_REPORTER_H_
+#define COMPONENTS_VIZ_CLIENT_SHARED_BITMAP_REPORTER_H_
+
+#include "components/viz/client/viz_client_export.h"
+#include "components/viz/common/resources/shared_bitmap.h"
+#include "mojo/public/cpp/system/buffer.h"
+
+namespace viz {
+
+// An interface that can be used for code without a direct connection to the viz
+// display compositor, as an intermediary in order to notify the display
+// compositor about SharedMemory allocated for shared bitmaps. The
+// implementation of this interface would be responsible to passing the
+// notifications on to the display compositor via the CompositorFrameSink.
+class VIZ_CLIENT_EXPORT SharedBitmapReporter {
+ public:
+ // Associates a SharedBitmapId with a shared buffer handle.
+ virtual void DidAllocateSharedBitmap(mojo::ScopedSharedBufferHandle buffer,
+ const SharedBitmapId& id) = 0;
+
+ // Disassociates a SharedBitmapId previously passed to
+ // DidAllocateSharedBitmap.
+ virtual void DidDeleteSharedBitmap(const SharedBitmapId& id) = 0;
+
+ protected:
+ virtual ~SharedBitmapReporter();
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_CLIENT_SHARED_BITMAP_REPORTER_H_
diff --git a/chromium/components/viz/common/BUILD.gn b/chromium/components/viz/common/BUILD.gn
index 34f30ddb03a..44853d5e0ec 100644
--- a/chromium/components/viz/common/BUILD.gn
+++ b/chromium/components/viz/common/BUILD.gn
@@ -60,6 +60,8 @@ viz_component("common") {
"gpu/context_cache_controller.cc",
"gpu/context_cache_controller.h",
"gpu/context_lost_observer.h",
+ "gpu/context_lost_reason.cc",
+ "gpu/context_lost_reason.h",
"gpu/context_provider.cc",
"gpu/context_provider.h",
"gpu/raster_context_provider.cc",
@@ -89,8 +91,6 @@ viz_component("common") {
"quads/render_pass_draw_quad.cc",
"quads/render_pass_draw_quad.h",
"quads/selection.h",
- "quads/shared_bitmap.cc",
- "quads/shared_bitmap.h",
"quads/shared_quad_state.cc",
"quads/shared_quad_state.h",
"quads/solid_color_draw_quad.cc",
@@ -109,21 +109,14 @@ viz_component("common") {
"resources/bitmap_allocation.h",
"resources/platform_color.h",
"resources/release_callback.h",
- "resources/resource.cc",
- "resources/resource.h",
- "resources/resource_fence.h",
"resources/resource_id.h",
- "resources/resource_metadata.cc",
- "resources/resource_metadata.h",
"resources/resource_settings.cc",
"resources/resource_settings.h",
"resources/resource_sizes.h",
- "resources/resource_type.h",
"resources/return_callback.h",
"resources/returned_resource.h",
- "resources/shared_bitmap_manager.h",
- "resources/shared_bitmap_reporter.cc",
- "resources/shared_bitmap_reporter.h",
+ "resources/shared_bitmap.cc",
+ "resources/shared_bitmap.h",
"resources/single_release_callback.cc",
"resources/single_release_callback.h",
"resources/transferable_resource.cc",
@@ -134,6 +127,7 @@ viz_component("common") {
"surfaces/child_local_surface_id_allocator.h",
"surfaces/frame_sink_id.cc",
"surfaces/frame_sink_id.h",
+ "surfaces/frame_sink_id_allocator.cc",
"surfaces/frame_sink_id_allocator.h",
"surfaces/local_surface_id.cc",
"surfaces/local_surface_id.h",
@@ -145,6 +139,8 @@ viz_component("common") {
"surfaces/surface_id.h",
"surfaces/surface_info.cc",
"surfaces/surface_info.h",
+ "surfaces/surface_range.cc",
+ "surfaces/surface_range.h",
"switches.cc",
"switches.h",
"traced_value.cc",
@@ -159,14 +155,12 @@ viz_component("common") {
# cc::MathUtil. Remove it once cc/base/math_util* are moved to viz.
"//cc/base",
"//cc/paint",
- "//gpu",
"//gpu/command_buffer/client:gles2_implementation",
"//gpu/command_buffer/client:gles2_interface",
"//gpu/command_buffer/client:raster",
"//gpu/command_buffer/client:raster_interface",
"//gpu/vulkan:buildflags",
- "//mojo/public/cpp/bindings",
- "//skia",
+ "//mojo/public/cpp/system",
"//third_party/libyuv",
"//ui/gfx",
"//ui/gfx:color_space",
@@ -200,6 +194,7 @@ viz_component("common") {
"//gpu/command_buffer/client",
"//gpu/command_buffer/common",
"//mojo/public/cpp/bindings",
+ "//skia",
]
}
@@ -207,6 +202,7 @@ viz_source_set("unit_tests") {
testonly = true
sources = [
"frame_sinks/begin_frame_args_unittest.cc",
+ "frame_sinks/begin_frame_source_unittest.cc",
"frame_sinks/copy_output_util_unittest.cc",
"frame_sinks/delay_based_time_source_unittest.cc",
"gl_helper_unittest.cc",
diff --git a/chromium/components/viz/common/DEPS b/chromium/components/viz/common/DEPS
index eb42d45c7c4..2026c4e9b22 100644
--- a/chromium/components/viz/common/DEPS
+++ b/chromium/components/viz/common/DEPS
@@ -1,5 +1,13 @@
# Please consult components/viz/README.md about allowable dependencies.
+include_rules = [
+ # Do not use mojo bindings in viz/client/. This library should be agnostic
+ # about how to communicate with viz.
+ "-mojo/public/cpp/bindings",
+ # Exception is struct_traits.h which is used for defining friends only.
+ "+mojo/public/cpp/bindings/struct_traits.h",
+]
+
specific_include_rules = {
"skia_helper.(cc|h)": [
"+cc/base",
@@ -12,14 +20,13 @@ specific_include_rules = {
"+gpu/command_buffer/common",
"+gpu/command_buffer/service",
"+gpu/ipc/common",
- "+mojo/public/cpp/bindings",
"+third_party/skia",
],
".*_unittest\.cc": [
"+cc/test",
- "+components/viz/test",
"+gpu/ipc/gl_in_process_context.h",
"+media/base",
+ "+third_party/skia/include/core",
"+ui/gl",
],
".*_benchmark\.cc": [
diff --git a/chromium/components/viz/common/frame_sinks/begin_frame_args.cc b/chromium/components/viz/common/frame_sinks/begin_frame_args.cc
index f5daefca801..9c7cca3e7d9 100644
--- a/chromium/components/viz/common/frame_sinks/begin_frame_args.cc
+++ b/chromium/components/viz/common/frame_sinks/begin_frame_args.cc
@@ -53,6 +53,9 @@ BeginFrameArgs::BeginFrameArgs(uint64_t source_id,
DCHECK_LE(kStartingFrameNumber, sequence_number);
}
+BeginFrameArgs::BeginFrameArgs(const BeginFrameArgs& args) = default;
+BeginFrameArgs& BeginFrameArgs::operator=(const BeginFrameArgs& args) = default;
+
BeginFrameArgs BeginFrameArgs::Create(BeginFrameArgs::CreationLocation location,
uint64_t source_id,
uint64_t sequence_number,
@@ -115,11 +118,19 @@ BeginFrameAck::BeginFrameAck()
sequence_number(BeginFrameArgs::kInvalidFrameNumber),
has_damage(false) {}
+BeginFrameAck::BeginFrameAck(const BeginFrameArgs& args, bool has_damage)
+ : BeginFrameAck(args.source_id,
+ args.sequence_number,
+ has_damage,
+ args.trace_id) {}
+
BeginFrameAck::BeginFrameAck(uint64_t source_id,
uint64_t sequence_number,
- bool has_damage)
+ bool has_damage,
+ int64_t trace_id)
: source_id(source_id),
sequence_number(sequence_number),
+ trace_id(trace_id),
has_damage(has_damage) {
DCHECK_LT(BeginFrameArgs::kInvalidFrameNumber, sequence_number);
}
diff --git a/chromium/components/viz/common/frame_sinks/begin_frame_args.h b/chromium/components/viz/common/frame_sinks/begin_frame_args.h
index cf9daddb3f4..ff33a47dd8f 100644
--- a/chromium/components/viz/common/frame_sinks/begin_frame_args.h
+++ b/chromium/components/viz/common/frame_sinks/begin_frame_args.h
@@ -62,6 +62,9 @@ struct VIZ_COMMON_EXPORT BeginFrameArgs {
// Creates an invalid set of values.
BeginFrameArgs();
+ BeginFrameArgs(const BeginFrameArgs& args);
+ BeginFrameArgs& operator=(const BeginFrameArgs& args);
+
#ifdef NDEBUG
typedef const void* CreationLocation;
#else
@@ -104,6 +107,11 @@ struct VIZ_COMMON_EXPORT BeginFrameArgs {
uint64_t source_id;
uint64_t sequence_number;
+ // |trace_id| is used as the id for the trace-events associated with this
+ // begin-frame. The trace-id is set by the service, and can be used by both
+ // the client and service as the id for trace-events.
+ int64_t trace_id = -1;
+
BeginFrameArgsType type;
bool on_critical_path;
@@ -133,7 +141,14 @@ struct VIZ_COMMON_EXPORT BeginFrameArgs {
// Sent by a BeginFrameObserver as acknowledgment of completing a BeginFrame.
struct VIZ_COMMON_EXPORT BeginFrameAck {
BeginFrameAck();
- BeginFrameAck(uint64_t source_id, uint64_t sequence_number, bool has_damage);
+
+ // Constructs an instance as a response to the specified BeginFrameArgs.
+ BeginFrameAck(const BeginFrameArgs& args, bool has_damage);
+
+ BeginFrameAck(uint64_t source_id,
+ uint64_t sequence_number,
+ bool has_damage,
+ int64_t trace_id = -1);
// Creates a BeginFrameAck for a manual BeginFrame. Used when clients produce
// a CompositorFrame without prior BeginFrame, e.g. for synchronous drawing.
@@ -149,6 +164,9 @@ struct VIZ_COMMON_EXPORT BeginFrameAck {
// Sequence number of the BeginFrame that is acknowledged.
uint64_t sequence_number;
+ // The |trace_id| of the BeginFrame that is acknowledged.
+ int64_t trace_id = -1;
+
// |true| if the observer has produced damage (e.g. sent a CompositorFrame or
// damaged a surface) as part of responding to the BeginFrame.
bool has_damage;
diff --git a/chromium/components/viz/common/frame_sinks/begin_frame_source.cc b/chromium/components/viz/common/frame_sinks/begin_frame_source.cc
index a868763a9bb..da4e9151c73 100644
--- a/chromium/components/viz/common/frame_sinks/begin_frame_source.cc
+++ b/chromium/components/viz/common/frame_sinks/begin_frame_source.cc
@@ -284,7 +284,9 @@ ExternalBeginFrameSource::ExternalBeginFrameSource(
DCHECK(client_);
}
-ExternalBeginFrameSource::~ExternalBeginFrameSource() = default;
+ExternalBeginFrameSource::~ExternalBeginFrameSource() {
+ DCHECK(observers_.empty());
+}
void ExternalBeginFrameSource::AsValueInto(
base::trace_event::TracedValue* state) const {
diff --git a/chromium/components/viz/common/frame_sinks/begin_frame_source.h b/chromium/components/viz/common/frame_sinks/begin_frame_source.h
index 1bc323209c4..caa1d38b685 100644
--- a/chromium/components/viz/common/frame_sinks/begin_frame_source.h
+++ b/chromium/components/viz/common/frame_sinks/begin_frame_source.h
@@ -151,7 +151,7 @@ class VIZ_COMMON_EXPORT BeginFrameSource {
// The higher 32 bits are used for a process restart id that changes if a
// process allocating BeginFrameSources has been restarted. The lower 32 bits
// are allocated from an atomic sequence.
- uint64_t source_id_;
+ const uint64_t source_id_;
DISALLOW_COPY_AND_ASSIGN(BeginFrameSource);
};
@@ -262,7 +262,9 @@ class VIZ_COMMON_EXPORT ExternalBeginFrameSourceClient {
// an observable BeginFrameSource.
class VIZ_COMMON_EXPORT ExternalBeginFrameSource : public BeginFrameSource {
public:
- // Client lifetime must be preserved by owner past the lifetime of this class.
+ // Client lifetime must be preserved by owner for the lifetime of the class.
+ // In order to allow derived classes to implement the client interface, no
+ // calls to |client| are made during construction / destruction.
explicit ExternalBeginFrameSource(ExternalBeginFrameSourceClient* client);
~ExternalBeginFrameSource() override;
diff --git a/chromium/components/viz/common/frame_sinks/begin_frame_source_unittest.cc b/chromium/components/viz/common/frame_sinks/begin_frame_source_unittest.cc
index 984977ef2a3..9c4beaf78fc 100644
--- a/chromium/components/viz/common/frame_sinks/begin_frame_source_unittest.cc
+++ b/chromium/components/viz/common/frame_sinks/begin_frame_source_unittest.cc
@@ -7,12 +7,10 @@
#include <stdint.h>
#include "base/memory/ptr_util.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "base/test/test_simple_task_runner.h"
+#include "base/test/test_mock_time_task_runner.h"
#include "components/viz/test/begin_frame_args_test.h"
#include "components/viz/test/begin_frame_source_test.h"
#include "components/viz/test/fake_delay_based_time_source.h"
-#include "components/viz/test/ordered_simple_task_runner.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -37,6 +35,25 @@ TEST(BeginFrameSourceTest, SourceIdsAreUnique) {
EXPECT_NE(source2.source_id(), source3.source_id());
}
+class TestTaskRunner : public base::TestMockTimeTaskRunner {
+ public:
+ TestTaskRunner()
+ : base::TestMockTimeTaskRunner(
+ base::TestMockTimeTaskRunner::Type::kStandalone) {
+ AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(1000));
+ }
+
+ void FastForwardTo(base::TimeTicks end_time) {
+ base::TimeDelta offset = end_time - NowTicks();
+ DCHECK_GE(offset, base::TimeDelta());
+ FastForwardBy(offset);
+ }
+
+ private:
+ ~TestTaskRunner() override = default; // Ref-counted.
+ DISALLOW_COPY_AND_ASSIGN(TestTaskRunner);
+};
+
// BackToBackBeginFrameSource testing
// ------------------------------------------
class BackToBackBeginFrameSourceTest : public ::testing::Test {
@@ -45,12 +62,11 @@ class BackToBackBeginFrameSourceTest : public ::testing::Test {
static const int64_t kInterval;
void SetUp() override {
- now_src_.reset(new base::SimpleTestTickClock());
- now_src_->Advance(base::TimeDelta::FromMicroseconds(1000));
- task_runner_ =
- base::MakeRefCounted<OrderedSimpleTaskRunner>(now_src_.get(), false);
- std::unique_ptr<FakeDelayBasedTimeSource> time_source(
- new FakeDelayBasedTimeSource(now_src_.get(), task_runner_.get()));
+ task_runner_ = base::MakeRefCounted<TestTaskRunner>();
+ std::unique_ptr<FakeDelayBasedTimeSource> time_source =
+ std::make_unique<FakeDelayBasedTimeSource>(
+ task_runner_->GetMockTickClock(), task_runner_.get());
+
delay_based_time_source_ = time_source.get();
source_.reset(new BackToBackBeginFrameSource(std::move(time_source)));
obs_ = base::WrapUnique(new ::testing::NiceMock<MockBeginFrameObserver>);
@@ -58,11 +74,10 @@ class BackToBackBeginFrameSourceTest : public ::testing::Test {
void TearDown() override { obs_.reset(); }
- std::unique_ptr<base::SimpleTestTickClock> now_src_;
- scoped_refptr<OrderedSimpleTaskRunner> task_runner_;
+ scoped_refptr<TestTaskRunner> task_runner_;
std::unique_ptr<BackToBackBeginFrameSource> source_;
std::unique_ptr<MockBeginFrameObserver> obs_;
- FakeDelayBasedTimeSource* delay_based_time_source_; // Owned by |now_src_|.
+ FakeDelayBasedTimeSource* delay_based_time_source_; // Owned by |source_|.
};
const int64_t BackToBackBeginFrameSourceTest::kDeadline =
@@ -74,16 +89,16 @@ const int64_t BackToBackBeginFrameSourceTest::kInterval =
TEST_F(BackToBackBeginFrameSourceTest, AddObserverSendsBeginFrame) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
- EXPECT_TRUE(task_runner_->HasPendingTasks());
+ EXPECT_TRUE(task_runner_->HasPendingTask());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
1100 + kDeadline, kInterval);
- now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(100));
source_->DidFinishFrame(obs_.get());
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
}
TEST_F(BackToBackBeginFrameSourceTest,
@@ -92,7 +107,7 @@ TEST_F(BackToBackBeginFrameSourceTest,
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
source_->RemoveObserver(obs_.get());
source_->DidFinishFrame(obs_.get());
@@ -100,8 +115,8 @@ TEST_F(BackToBackBeginFrameSourceTest,
// Verify no BeginFrame is sent to |obs_|. There is a pending task in the
// task_runner_ as a BeginFrame was posted, but it gets aborted since |obs_|
// is removed.
- task_runner_->RunPendingTasks();
- EXPECT_FALSE(task_runner_->HasPendingTasks());
+ task_runner_->RunUntilIdle();
+ EXPECT_FALSE(task_runner_->HasPendingTask());
}
TEST_F(BackToBackBeginFrameSourceTest,
@@ -110,14 +125,14 @@ TEST_F(BackToBackBeginFrameSourceTest,
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
- now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(100));
source_->DidFinishFrame(obs_.get());
source_->RemoveObserver(obs_.get());
- EXPECT_TRUE(task_runner_->HasPendingTasks());
- task_runner_->RunPendingTasks();
+ // Task gets cancelled so it doesn't count as a pending task.
+ EXPECT_FALSE(task_runner_->HasPendingTask());
}
TEST_F(BackToBackBeginFrameSourceTest,
@@ -126,25 +141,25 @@ TEST_F(BackToBackBeginFrameSourceTest,
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
- now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(100));
source_->RemoveObserver(obs_.get());
- now_src_->Advance(base::TimeDelta::FromMicroseconds(10));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(10));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
- now_src_->Advance(base::TimeDelta::FromMicroseconds(10));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(10));
source_->DidFinishFrame(obs_.get());
- now_src_->Advance(base::TimeDelta::FromMicroseconds(10));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(10));
// The begin frame is posted at the time when the observer was added,
// so it ignores changes to "now" afterward.
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1110,
1110 + kDeadline, kInterval);
- EXPECT_TRUE(task_runner_->HasPendingTasks());
- task_runner_->RunPendingTasks();
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ task_runner_->RunUntilIdle();
}
TEST_F(BackToBackBeginFrameSourceTest,
@@ -153,25 +168,25 @@ TEST_F(BackToBackBeginFrameSourceTest,
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
- now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(100));
source_->DidFinishFrame(obs_.get());
- now_src_->Advance(base::TimeDelta::FromMicroseconds(10));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(10));
source_->RemoveObserver(obs_.get());
- now_src_->Advance(base::TimeDelta::FromMicroseconds(10));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(10));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
- now_src_->Advance(base::TimeDelta::FromMicroseconds(10));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(10));
// Ticks at the time at which the observer was added, ignoring the
// last change to "now".
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1120,
1120 + kDeadline, kInterval);
- EXPECT_TRUE(task_runner_->HasPendingTasks());
- task_runner_->RunPendingTasks();
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ task_runner_->RunUntilIdle();
}
TEST_F(BackToBackBeginFrameSourceTest, DidFinishFrameNoObserver) {
@@ -179,7 +194,8 @@ TEST_F(BackToBackBeginFrameSourceTest, DidFinishFrameNoObserver) {
source_->AddObserver(obs_.get());
source_->RemoveObserver(obs_.get());
source_->DidFinishFrame(obs_.get());
- EXPECT_FALSE(task_runner_->RunPendingTasks());
+ task_runner_->RunUntilIdle();
+ EXPECT_FALSE(task_runner_->HasPendingTask());
}
TEST_F(BackToBackBeginFrameSourceTest, DidFinishFrameMultipleCallsIdempotent) {
@@ -187,23 +203,23 @@ TEST_F(BackToBackBeginFrameSourceTest, DidFinishFrameMultipleCallsIdempotent) {
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
- now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(100));
source_->DidFinishFrame(obs_.get());
source_->DidFinishFrame(obs_.get());
source_->DidFinishFrame(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
1100 + kDeadline, kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
- now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(100));
source_->DidFinishFrame(obs_.get());
source_->DidFinishFrame(obs_.get());
source_->DidFinishFrame(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 1200,
1200 + kDeadline, kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
}
TEST_F(BackToBackBeginFrameSourceTest, DelayInPostedTaskProducesCorrectFrame) {
@@ -211,18 +227,18 @@ TEST_F(BackToBackBeginFrameSourceTest, DelayInPostedTaskProducesCorrectFrame) {
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
- now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(100));
source_->DidFinishFrame(obs_.get());
- now_src_->Advance(base::TimeDelta::FromMicroseconds(50));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(50));
// Ticks at the time the last frame finished, so ignores the last change to
// "now".
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
1100 + kDeadline, kInterval);
- EXPECT_TRUE(task_runner_->HasPendingTasks());
- task_runner_->RunPendingTasks();
+ EXPECT_TRUE(task_runner_->HasPendingTask());
+ task_runner_->RunUntilIdle();
}
TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversSynchronized) {
@@ -237,24 +253,24 @@ TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversSynchronized) {
kInterval);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 1, 1000, 1000 + kDeadline,
kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
- now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(100));
source_->DidFinishFrame(&obs1);
source_->DidFinishFrame(&obs2);
EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 1100, 1100 + kDeadline,
kInterval);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline,
kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
- now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(100));
source_->DidFinishFrame(&obs1);
source_->DidFinishFrame(&obs2);
- EXPECT_TRUE(task_runner_->HasPendingTasks());
+ EXPECT_TRUE(task_runner_->HasPendingTask());
source_->RemoveObserver(&obs1);
source_->RemoveObserver(&obs2);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
}
TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversInterleaved) {
@@ -264,20 +280,20 @@ TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversInterleaved) {
source_->AddObserver(&obs1);
EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline,
kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
- now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(100));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false);
source_->AddObserver(&obs2);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline,
kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
- now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(100));
source_->DidFinishFrame(&obs1);
EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 3, 1200, 1200 + kDeadline,
kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
source_->DidFinishFrame(&obs1);
source_->RemoveObserver(&obs1);
@@ -285,13 +301,13 @@ TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversInterleaved) {
EXPECT_FALSE(delay_based_time_source_->Active());
// Finishing the frame for |obs1| posts a begin frame task, which will be
// aborted since |obs1| is removed. Clear that from the task runner.
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
- now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(100));
source_->DidFinishFrame(&obs2);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 4, 1300, 1300 + kDeadline,
kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
source_->DidFinishFrame(&obs2);
source_->RemoveObserver(&obs2);
@@ -308,14 +324,14 @@ TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversAtOnce) {
kInterval);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 1, 1000, 1000 + kDeadline,
kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
// |obs1| finishes first.
- now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(100));
source_->DidFinishFrame(&obs1);
// |obs2| finishes also, before getting to the newly posted begin frame.
- now_src_->Advance(base::TimeDelta::FromMicroseconds(100));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(100));
source_->DidFinishFrame(&obs2);
// Because the begin frame source already ticked when |obs1| finished,
@@ -324,7 +340,7 @@ TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversAtOnce) {
kInterval);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline,
kInterval);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
source_->DidFinishFrame(&obs1);
source_->RemoveObserver(&obs1);
@@ -336,18 +352,12 @@ TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversAtOnce) {
// ------------------------------------------
class DelayBasedBeginFrameSourceTest : public ::testing::Test {
public:
- std::unique_ptr<base::SimpleTestTickClock> now_src_;
- scoped_refptr<OrderedSimpleTaskRunner> task_runner_;
- std::unique_ptr<DelayBasedBeginFrameSource> source_;
- std::unique_ptr<MockBeginFrameObserver> obs_;
-
void SetUp() override {
- now_src_.reset(new base::SimpleTestTickClock());
- now_src_->Advance(base::TimeDelta::FromMicroseconds(1000));
- task_runner_ =
- base::MakeRefCounted<OrderedSimpleTaskRunner>(now_src_.get(), false);
- std::unique_ptr<DelayBasedTimeSource> time_source(
- new FakeDelayBasedTimeSource(now_src_.get(), task_runner_.get()));
+ task_runner_ = base::MakeRefCounted<TestTaskRunner>();
+ std::unique_ptr<FakeDelayBasedTimeSource> time_source =
+ std::make_unique<FakeDelayBasedTimeSource>(
+ task_runner_->GetMockTickClock(), task_runner_.get());
+
time_source->SetTimebaseAndInterval(
base::TimeTicks(), base::TimeDelta::FromMicroseconds(10000));
source_ = std::make_unique<DelayBasedBeginFrameSource>(
@@ -356,11 +366,15 @@ class DelayBasedBeginFrameSourceTest : public ::testing::Test {
}
void TearDown() override { obs_.reset(); }
+
+ scoped_refptr<TestTaskRunner> task_runner_;
+ std::unique_ptr<DelayBasedBeginFrameSource> source_;
+ std::unique_ptr<MockBeginFrameObserver> obs_;
};
TEST_F(DelayBasedBeginFrameSourceTest,
AddObserverCallsOnBeginFrameWithMissedTick) {
- now_src_->Advance(base::TimeDelta::FromMicroseconds(9010));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(9010));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 10000, 20000,
10000);
@@ -373,16 +387,15 @@ TEST_F(DelayBasedBeginFrameSourceTest, AddObserverCallsCausesOnBeginFrame) {
EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
10000);
source_->AddObserver(obs_.get());
- EXPECT_EQ(TicksFromMicroseconds(10000), task_runner_->NextTaskTime());
+ EXPECT_EQ(TicksFromMicroseconds(10000),
+ task_runner_->NowTicks() + task_runner_->NextPendingTaskDelay());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
- now_src_->Advance(base::TimeDelta::FromMicroseconds(9010));
- task_runner_->RunPendingTasks();
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(9010));
+ task_runner_->RunUntilIdle();
}
TEST_F(DelayBasedBeginFrameSourceTest, BasicOperation) {
- task_runner_->SetAutoAdvanceNowToPendingTasks(true);
-
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
10000);
@@ -390,15 +403,14 @@ TEST_F(DelayBasedBeginFrameSourceTest, BasicOperation) {
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
- task_runner_->RunUntilTime(TicksFromMicroseconds(30001));
+ task_runner_->FastForwardTo(TicksFromMicroseconds(30001));
source_->RemoveObserver(obs_.get());
// No new frames....
- task_runner_->RunUntilTime(TicksFromMicroseconds(60000));
+ task_runner_->FastForwardTo(TicksFromMicroseconds(60000));
}
TEST_F(DelayBasedBeginFrameSourceTest, VSyncChanges) {
- task_runner_->SetAutoAdvanceNowToPendingTasks(true);
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
10000);
@@ -407,7 +419,7 @@ TEST_F(DelayBasedBeginFrameSourceTest, VSyncChanges) {
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
- task_runner_->RunUntilTime(TicksFromMicroseconds(30001));
+ task_runner_->FastForwardTo(TicksFromMicroseconds(30001));
// Update the vsync information
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(27500),
@@ -416,11 +428,10 @@ TEST_F(DelayBasedBeginFrameSourceTest, VSyncChanges) {
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40000, 47502, 10001);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 6, 47502, 57503, 10001);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 57503, 67504, 10001);
- task_runner_->RunUntilTime(TicksFromMicroseconds(60000));
+ task_runner_->FastForwardTo(TicksFromMicroseconds(60000));
}
TEST_F(DelayBasedBeginFrameSourceTest, AuthoritativeVSyncChanges) {
- task_runner_->SetAutoAdvanceNowToPendingTasks(true);
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(500),
base::TimeDelta::FromMicroseconds(10000));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
@@ -430,28 +441,28 @@ TEST_F(DelayBasedBeginFrameSourceTest, AuthoritativeVSyncChanges) {
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10500, 20500, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20500, 30500, 10000);
- task_runner_->RunUntilTime(TicksFromMicroseconds(20501));
+ task_runner_->FastForwardTo(TicksFromMicroseconds(20501));
// This will keep the same timebase, so 500, 9999
source_->SetAuthoritativeVSyncInterval(
base::TimeDelta::FromMicroseconds(9999));
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30500, 40496, 9999);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40496, 50495, 9999);
- task_runner_->RunUntilTime(TicksFromMicroseconds(40497));
+ task_runner_->FastForwardTo(TicksFromMicroseconds(40497));
// Change the vsync params, but the new interval will be ignored.
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(400),
base::TimeDelta::FromMicroseconds(1));
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 6, 50495, 60394, 9999);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 60394, 70393, 9999);
- task_runner_->RunUntilTime(TicksFromMicroseconds(60395));
+ task_runner_->FastForwardTo(TicksFromMicroseconds(60395));
}
TEST_F(DelayBasedBeginFrameSourceTest, MultipleObservers) {
NiceMock<MockBeginFrameObserver> obs1, obs2;
- // now_src_ starts off at 1000.
- task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(9010));
+ // Mock tick clock starts off at 1000.
+ task_runner_->FastForwardBy(base::TimeDelta::FromMicroseconds(9010));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false);
EXPECT_BEGIN_FRAME_USED_MISSED(obs1, source_->source_id(), 1, 10000, 20000,
10000);
@@ -459,7 +470,7 @@ TEST_F(DelayBasedBeginFrameSourceTest, MultipleObservers) {
// No tasks should need to be run for this to occur.
EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 20000, 30000, 10000);
- task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(10000));
+ task_runner_->FastForwardBy(base::TimeDelta::FromMicroseconds(10000));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false);
// Sequence number unchanged for missed frame with time of last normal frame.
@@ -470,16 +481,16 @@ TEST_F(DelayBasedBeginFrameSourceTest, MultipleObservers) {
EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 3, 30000, 40000, 10000);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 3, 30000, 40000, 10000);
- task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(10000));
+ task_runner_->FastForwardBy(base::TimeDelta::FromMicroseconds(10000));
source_->RemoveObserver(&obs1);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 4, 40000, 50000, 10000);
- task_runner_->RunForPeriod(base::TimeDelta::FromMicroseconds(10000));
+ task_runner_->FastForwardBy(base::TimeDelta::FromMicroseconds(10000));
source_->RemoveObserver(&obs2);
- task_runner_->RunUntilTime(TicksFromMicroseconds(50000));
- EXPECT_FALSE(task_runner_->HasPendingTasks());
+ task_runner_->FastForwardTo(TicksFromMicroseconds(50000));
+ EXPECT_FALSE(task_runner_->HasPendingTask());
}
TEST_F(DelayBasedBeginFrameSourceTest, DoubleTick) {
@@ -491,17 +502,17 @@ TEST_F(DelayBasedBeginFrameSourceTest, DoubleTick) {
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(5000),
base::TimeDelta::FromMicroseconds(10000));
- now_src_->Advance(base::TimeDelta::FromMicroseconds(4000));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(4000));
// No begin frame received.
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
// Begin frame received.
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(10000),
base::TimeDelta::FromMicroseconds(10000));
- now_src_->Advance(base::TimeDelta::FromMicroseconds(5000));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(5000));
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 2, 10000, 20000, 10000);
- task_runner_->RunPendingTasks();
+ task_runner_->RunUntilIdle();
}
TEST_F(DelayBasedBeginFrameSourceTest, DoubleTickMissedFrame) {
@@ -514,7 +525,7 @@ TEST_F(DelayBasedBeginFrameSourceTest, DoubleTickMissedFrame) {
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(5000),
base::TimeDelta::FromMicroseconds(10000));
- now_src_->Advance(base::TimeDelta::FromMicroseconds(4000));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(4000));
// No missed frame received.
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
@@ -526,7 +537,7 @@ TEST_F(DelayBasedBeginFrameSourceTest, DoubleTickMissedFrame) {
// Missed frame received.
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(10000),
base::TimeDelta::FromMicroseconds(10000));
- now_src_->Advance(base::TimeDelta::FromMicroseconds(5000));
+ task_runner_->AdvanceMockTickClock(base::TimeDelta::FromMicroseconds(5000));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
// Sequence number is incremented again, because sufficient time has passed.
EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 2, 10000, 20000,
@@ -540,17 +551,16 @@ TEST_F(DelayBasedBeginFrameSourceTest, DoubleTickMissedFrame) {
class MockExternalBeginFrameSourceClient
: public ExternalBeginFrameSourceClient {
public:
+ MockExternalBeginFrameSourceClient() = default;
+ virtual ~MockExternalBeginFrameSourceClient() = default;
+
MOCK_METHOD1(OnNeedsBeginFrames, void(bool));
};
class ExternalBeginFrameSourceTest : public ::testing::Test {
public:
- std::unique_ptr<MockExternalBeginFrameSourceClient> client_;
- std::unique_ptr<ExternalBeginFrameSource> source_;
- std::unique_ptr<MockBeginFrameObserver> obs_;
-
void SetUp() override {
- client_.reset(new MockExternalBeginFrameSourceClient);
+ client_.reset(new MockExternalBeginFrameSourceClient());
source_.reset(new ExternalBeginFrameSource(client_.get()));
obs_.reset(new MockBeginFrameObserver);
}
@@ -559,9 +569,14 @@ class ExternalBeginFrameSourceTest : public ::testing::Test {
client_.reset();
obs_.reset();
}
+
+ std::unique_ptr<MockExternalBeginFrameSourceClient> client_;
+ std::unique_ptr<ExternalBeginFrameSource> source_;
+ std::unique_ptr<MockBeginFrameObserver> obs_;
};
-TEST_F(ExternalBeginFrameSourceTest, OnAnimateOnlyBeginFrameOptIn) {
+// TODO(https://crbug.com/863422): Fix DCHECK failure.
+TEST_F(ExternalBeginFrameSourceTest, DISABLED_OnAnimateOnlyBeginFrameOptIn) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1);
source_->AddObserver(obs_.get());
@@ -582,8 +597,9 @@ TEST_F(ExternalBeginFrameSourceTest, OnAnimateOnlyBeginFrameOptIn) {
source_->OnBeginFrame(args);
}
-// https://crbug.com/690127: Duplicate BeginFrame caused DCHECK crash.
-TEST_F(ExternalBeginFrameSourceTest, OnBeginFrameChecksBeginFrameContinuity) {
+// TODO(https://crbug.com/863422): Fix DCHECK failure.
+TEST_F(ExternalBeginFrameSourceTest,
+ DISABLED_OnBeginFrameChecksBeginFrameContinuity) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1);
source_->AddObserver(obs_.get());
@@ -605,9 +621,8 @@ TEST_F(ExternalBeginFrameSourceTest, OnBeginFrameChecksBeginFrameContinuity) {
source2.OnBeginFrame(args);
}
-// https://crbug.com/730218: Avoid DCHECK crash in
-// ExternalBeginFrameSource::GetMissedBeginFrameArgs.
-TEST_F(ExternalBeginFrameSourceTest, GetMissedBeginFrameArgs) {
+// TODO(https://crbug.com/863422): Fix DCHECK failure.
+TEST_F(ExternalBeginFrameSourceTest, DISABLED_GetMissedBeginFrameArgs) {
BeginFrameArgs args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0,
2, 10000, 10100, 100);
source_->OnBeginFrame(args);
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 56164fa32ea..f526d0567f6 100644
--- a/chromium/components/viz/common/frame_sinks/copy_output_request.cc
+++ b/chromium/components/viz/common/frame_sinks/copy_output_request.cc
@@ -31,17 +31,23 @@ CopyOutputRequest::~CopyOutputRequest() {
void CopyOutputRequest::SetScaleRatio(const gfx::Vector2d& scale_from,
const gfx::Vector2d& scale_to) {
- DCHECK_GT(scale_from.x(), 0);
- DCHECK_GT(scale_from.y(), 0);
- DCHECK_GT(scale_to.x(), 0);
- DCHECK_GT(scale_to.y(), 0);
+ // These are CHECKs, and not DCHECKs, because it's critical that crash report
+ // bugs be tied to the client callpoint rather than the later mojo or service-
+ // side processing of the CopyOutputRequest.
+ CHECK_GT(scale_from.x(), 0);
+ CHECK_GT(scale_from.y(), 0);
+ CHECK_GT(scale_to.x(), 0);
+ CHECK_GT(scale_to.y(), 0);
+
scale_from_ = scale_from;
scale_to_ = scale_to;
}
void CopyOutputRequest::SetUniformScaleRatio(int scale_from, int scale_to) {
- DCHECK_GT(scale_from, 0);
- DCHECK_GT(scale_to, 0);
+ // See note in SetScaleRatio() as to why these are CHECKs and not DCHECKs.
+ CHECK_GT(scale_from, 0);
+ CHECK_GT(scale_to, 0);
+
scale_from_ = gfx::Vector2d(scale_from, scale_from);
scale_to_ = gfx::Vector2d(scale_to, scale_to);
}
diff --git a/chromium/components/viz/common/gl_helper.cc b/chromium/components/viz/common/gl_helper.cc
index 414d65fe305..d8f2952f368 100644
--- a/chromium/components/viz/common/gl_helper.cc
+++ b/chromium/components/viz/common/gl_helper.cc
@@ -451,7 +451,7 @@ GLuint GLHelper::CopyTextureToImpl::EncodeTextureAsGrayscale(
helper_->InitScalerImpl();
const std::unique_ptr<ScalerInterface> planerizer =
- helper_->scaler_impl_.get()->CreateGrayscalePlanerizer(
+ helper_->scaler_impl_->CreateGrayscalePlanerizer(
false, vertically_flip_texture, swizzle);
planerizer->Scale(src_texture, src_size, gfx::Vector2dF(), dst_texture,
gfx::Rect(*encoded_texture_size));
@@ -878,7 +878,6 @@ void GLHelper::WaitSyncToken(const gpu::SyncToken& sync_token) {
gpu::MailboxHolder GLHelper::ProduceMailboxHolderFromTexture(
GLuint texture_id) {
gpu::Mailbox mailbox;
- gl_->GenMailboxCHROMIUM(mailbox.name);
gl_->ProduceTextureDirectCHROMIUM(texture_id, mailbox.name);
gpu::SyncToken sync_token;
diff --git a/chromium/components/viz/common/gl_helper_benchmark.cc b/chromium/components/viz/common/gl_helper_benchmark.cc
index 3cf84a90e38..a2ff136128a 100644
--- a/chromium/components/viz/common/gl_helper_benchmark.cc
+++ b/chromium/components/viz/common/gl_helper_benchmark.cc
@@ -66,7 +66,7 @@ class GLHelperBenchmark : public testing::Test {
attributes.bind_generates_resource = false;
attributes.gpu_preference = gl::PreferDiscreteGpu;
- context_ = gpu::GLInProcessContext::CreateWithoutInit();
+ context_ = std::make_unique<gpu::GLInProcessContext>();
auto result =
context_->Initialize(nullptr, /* service */
nullptr, /* surface */
diff --git a/chromium/components/viz/common/gl_helper_scaling.cc b/chromium/components/viz/common/gl_helper_scaling.cc
index acd0203a382..f0c02c3a54d 100644
--- a/chromium/components/viz/common/gl_helper_scaling.cc
+++ b/chromium/components/viz/common/gl_helper_scaling.cc
@@ -861,7 +861,7 @@ scoped_refptr<ShaderProgram> GLHelperScaling::GetShaderProgram(ShaderType type,
bool swizzle) {
ShaderProgramKeyType key(type, swizzle);
scoped_refptr<ShaderProgram>& cache_entry(shader_programs_[key]);
- if (!cache_entry.get()) {
+ if (!cache_entry) {
cache_entry = new ShaderProgram(gl_, helper_, type);
std::basic_string<GLchar> vertex_program;
std::basic_string<GLchar> fragment_program;
diff --git a/chromium/components/viz/common/gl_helper_unittest.cc b/chromium/components/viz/common/gl_helper_unittest.cc
index 8a07533d34a..05227762862 100644
--- a/chromium/components/viz/common/gl_helper_unittest.cc
+++ b/chromium/components/viz/common/gl_helper_unittest.cc
@@ -64,7 +64,7 @@ class GLHelperTest : public testing::Test {
attributes.sample_buffers = 1;
attributes.bind_generates_resource = false;
- context_ = gpu::GLInProcessContext::CreateWithoutInit();
+ context_ = std::make_unique<gpu::GLInProcessContext>();
auto result =
context_->Initialize(nullptr, /* service */
nullptr, /* surface */
diff --git a/chromium/components/viz/common/gpu/context_cache_controller_unittest.cc b/chromium/components/viz/common/gpu/context_cache_controller_unittest.cc
index 5ed912b08bc..44a04c02c53 100644
--- a/chromium/components/viz/common/gpu/context_cache_controller_unittest.cc
+++ b/chromium/components/viz/common/gpu/context_cache_controller_unittest.cc
@@ -5,9 +5,13 @@
#include "components/viz/common/gpu/context_cache_controller.h"
#include "base/test/test_mock_time_task_runner.h"
+#include "components/viz/test/test_context_provider.h"
#include "components/viz/test/test_context_support.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkPixmap.h"
+#include "third_party/skia/include/gpu/GrContext.h"
using ::testing::Mock;
using ::testing::StrictMock;
@@ -109,5 +113,51 @@ TEST(ContextCacheControllerTest, ScopedBusyMulitpleWhileVisible) {
cache_controller.ClientBecameNotVisible(std::move(visible));
}
+// Confirms that the Skia performDeferredCleanup API used by the cache
+// controller behaves as expected.
+TEST(ContextCacheControllerTest, CheckSkiaResourcePurgeAPI) {
+ StrictMock<MockContextSupport> context_support;
+ auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
+ ContextCacheController cache_controller(&context_support, task_runner);
+ auto context_provider = TestContextProvider::Create();
+ context_provider->BindToCurrentThread();
+ auto* gr_context = context_provider->GrContext();
+ cache_controller.SetGrContext(gr_context);
+
+ // Make us visible.
+ EXPECT_CALL(context_support, SetAggressivelyFreeResources(false));
+ auto visibility = cache_controller.ClientBecameVisible();
+ Mock::VerifyAndClearExpectations(&context_support);
+
+ // Now that we're visible, become busy, create and release a skia resource.
+ auto busy = cache_controller.ClientBecameBusy();
+ {
+ auto image_info = SkImageInfo::MakeN32Premul(200, 200);
+ std::vector<uint8_t> image_data(image_info.computeMinByteSize());
+ SkPixmap pixmap(image_info, image_data.data(), image_info.minRowBytes());
+ auto image = SkImage::MakeRasterCopy(pixmap);
+ auto image_gpu = image->makeTextureImage(gr_context, nullptr);
+ gr_context->flush();
+ }
+
+ // Ensure we see size taken up for the image (now released, but cached for
+ // re-use).
+ EXPECT_GT(gr_context->getResourceCachePurgeableBytes(), 0u);
+
+ // Make the client idle and wait for the idle callback to trigger.
+ cache_controller.ClientBecameNotBusy(std::move(busy));
+ EXPECT_CALL(context_support, SetAggressivelyFreeResources(true));
+ EXPECT_CALL(context_support, SetAggressivelyFreeResources(false));
+ task_runner->FastForwardBy(base::TimeDelta::FromSeconds(5));
+ Mock::VerifyAndClearExpectations(&context_support);
+
+ // The Skia resource cache should now be empty.
+ EXPECT_EQ(gr_context->getResourceCachePurgeableBytes(), 0u);
+
+ // Set not-visible.
+ EXPECT_CALL(context_support, SetAggressivelyFreeResources(true));
+ cache_controller.ClientBecameNotVisible(std::move(visibility));
+}
+
} // namespace
} // namespace viz
diff --git a/chromium/components/viz/common/gpu/context_lost_reason.cc b/chromium/components/viz/common/gpu/context_lost_reason.cc
new file mode 100644
index 00000000000..7f24e8fe6d9
--- /dev/null
+++ b/chromium/components/viz/common/gpu/context_lost_reason.cc
@@ -0,0 +1,53 @@
+// 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/gpu/context_lost_reason.h"
+
+#include "base/logging.h"
+
+namespace viz {
+
+ContextLostReason GetContextLostReason(gpu::error::Error error,
+ gpu::error::ContextLostReason reason) {
+ if (error == gpu::error::kLostContext) {
+ switch (reason) {
+ case gpu::error::kGuilty:
+ return CONTEXT_LOST_GUILTY;
+ case gpu::error::kInnocent:
+ return CONTEXT_LOST_INNOCENT;
+ case gpu::error::kUnknown:
+ return CONTEXT_LOST_UNKNOWN;
+ case gpu::error::kOutOfMemory:
+ return CONTEXT_LOST_OUT_OF_MEMORY;
+ case gpu::error::kMakeCurrentFailed:
+ return CONTEXT_LOST_MAKECURRENT_FAILED;
+ case gpu::error::kGpuChannelLost:
+ return CONTEXT_LOST_GPU_CHANNEL_ERROR;
+ case gpu::error::kInvalidGpuMessage:
+ return CONTEXT_LOST_INVALID_GPU_MESSAGE;
+ }
+ }
+ switch (error) {
+ case gpu::error::kInvalidSize:
+ return CONTEXT_PARSE_ERROR_INVALID_SIZE;
+ case gpu::error::kOutOfBounds:
+ return CONTEXT_PARSE_ERROR_OUT_OF_BOUNDS;
+ case gpu::error::kUnknownCommand:
+ return CONTEXT_PARSE_ERROR_UNKNOWN_COMMAND;
+ case gpu::error::kInvalidArguments:
+ return CONTEXT_PARSE_ERROR_INVALID_ARGS;
+ case gpu::error::kGenericError:
+ return CONTEXT_PARSE_ERROR_GENERIC_ERROR;
+ case gpu::error::kDeferCommandUntilLater:
+ case gpu::error::kDeferLaterCommands:
+ case gpu::error::kNoError:
+ case gpu::error::kLostContext:
+ NOTREACHED();
+ return CONTEXT_LOST_UNKNOWN;
+ }
+ NOTREACHED();
+ return CONTEXT_LOST_UNKNOWN;
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/gpu/context_lost_reason.h b/chromium/components/viz/common/gpu/context_lost_reason.h
new file mode 100644
index 00000000000..c069c8bcb2f
--- /dev/null
+++ b/chromium/components/viz/common/gpu/context_lost_reason.h
@@ -0,0 +1,40 @@
+// 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_GPU_CONTEXT_LOST_REASON_H_
+#define COMPONENTS_VIZ_COMMON_GPU_CONTEXT_LOST_REASON_H_
+
+#include "components/viz/common/viz_common_export.h"
+#include "gpu/command_buffer/common/constants.h"
+
+namespace viz {
+
+enum ContextLostReason {
+ // These values are persisted to logs. Entries should not be renumbered and
+ // numeric values should never be reused.
+ CONTEXT_INIT_FAILED = 0,
+ CONTEXT_LOST_GPU_CHANNEL_ERROR = 1,
+ CONTEXT_PARSE_ERROR_INVALID_SIZE = 2,
+ CONTEXT_PARSE_ERROR_OUT_OF_BOUNDS = 3,
+ CONTEXT_PARSE_ERROR_UNKNOWN_COMMAND = 4,
+ CONTEXT_PARSE_ERROR_INVALID_ARGS = 5,
+ CONTEXT_PARSE_ERROR_GENERIC_ERROR = 6,
+ CONTEXT_LOST_GUILTY = 7,
+ CONTEXT_LOST_INNOCENT = 8,
+ CONTEXT_LOST_UNKNOWN = 9,
+ CONTEXT_LOST_OUT_OF_MEMORY = 10,
+ CONTEXT_LOST_MAKECURRENT_FAILED = 11,
+ CONTEXT_LOST_INVALID_GPU_MESSAGE = 12,
+ // Update kMaxValue and //tools/metrics/histograms/histograms.xml when adding
+ // new values.
+ kMaxValue = CONTEXT_LOST_INVALID_GPU_MESSAGE
+};
+
+VIZ_COMMON_EXPORT ContextLostReason
+GetContextLostReason(gpu::error::Error error,
+ gpu::error::ContextLostReason reason);
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_GPU_CONTEXT_LOST_REASON_H_
diff --git a/chromium/components/viz/common/gpu/texture_allocation.cc b/chromium/components/viz/common/gpu/texture_allocation.cc
index 90b51e28e6a..1a33cdb36c7 100644
--- a/chromium/components/viz/common/gpu/texture_allocation.cc
+++ b/chromium/components/viz/common/gpu/texture_allocation.cc
@@ -97,7 +97,7 @@ void TextureAllocation::AllocateStorage(gpu::raster::RasterInterface* ri,
// ETC1 resources cannot be preallocated.
if (format == ETC1)
return;
- ri->TexStorage2D(alloc.texture_id, 1, size.width(), size.height());
+ ri->TexStorage2D(alloc.texture_id, size.width(), size.height());
if (alloc.overlay_candidate && color_space.IsValid()) {
ri->SetColorSpaceMetadata(alloc.texture_id,
reinterpret_cast<GLColorSpace>(
diff --git a/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc b/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc
index 8528ee8af9b..992c7cd5186 100644
--- a/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc
+++ b/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.cc
@@ -6,9 +6,9 @@
#include "gpu/vulkan/buildflags.h"
#include "gpu/vulkan/init/vulkan_factory.h"
#include "gpu/vulkan/vulkan_device_queue.h"
+#include "gpu/vulkan/vulkan_function_pointers.h"
#include "gpu/vulkan/vulkan_implementation.h"
#include "third_party/skia/include/gpu/GrContext.h"
-#include "third_party/skia/include/gpu/vk/GrVkInterface.h"
namespace viz {
@@ -22,9 +22,8 @@ VulkanInProcessContextProvider::Create(
return context_provider;
}
-GrVkInterface::GetProc make_unified_getter(
- const GrVkInterface::GetInstanceProc& iproc,
- const GrVkInterface::GetDeviceProc& dproc) {
+GrVkGetProc make_unified_getter(const PFN_vkGetInstanceProcAddr& iproc,
+ const PFN_vkGetDeviceProcAddr& dproc) {
return [&iproc, &dproc](const char* proc_name, VkInstance instance,
VkDevice device) {
if (device != VK_NULL_HANDLE) {
@@ -52,24 +51,23 @@ bool VulkanInProcessContextProvider::Initialize() {
const uint32_t extension_flags =
kEXT_debug_report_GrVkExtensionFlag | kKHR_surface_GrVkExtensionFlag |
kKHR_swapchain_GrVkExtensionFlag | kKHR_xcb_surface_GrVkExtensionFlag;
- GrVkBackendContext* backend_context = new GrVkBackendContext();
- backend_context->fInstance = device_queue_->GetVulkanInstance();
- backend_context->fPhysicalDevice = device_queue_->GetVulkanPhysicalDevice();
- backend_context->fDevice = device_queue_->GetVulkanDevice();
- backend_context->fQueue = device_queue_->GetVulkanQueue();
- backend_context->fGraphicsQueueIndex = device_queue_->GetVulkanQueueIndex();
- backend_context->fMinAPIVersion = VK_MAKE_VERSION(1, 0, 8);
- backend_context->fExtensions = extension_flags;
- backend_context->fFeatures = feature_flags;
+ GrVkBackendContext backend_context;
+ backend_context.fInstance = device_queue_->GetVulkanInstance();
+ backend_context.fPhysicalDevice = device_queue_->GetVulkanPhysicalDevice();
+ backend_context.fDevice = device_queue_->GetVulkanDevice();
+ backend_context.fQueue = device_queue_->GetVulkanQueue();
+ backend_context.fGraphicsQueueIndex = device_queue_->GetVulkanQueueIndex();
+ backend_context.fMinAPIVersion = VK_MAKE_VERSION(1, 0, 8);
+ backend_context.fExtensions = extension_flags;
+ backend_context.fFeatures = feature_flags;
- auto interface = sk_make_sp<GrVkInterface>(
- make_unified_getter(vkGetInstanceProcAddr, vkGetDeviceProcAddr),
- backend_context->fInstance, backend_context->fDevice,
- backend_context->fExtensions);
- backend_context->fInterface.reset(interface.release());
- backend_context->fOwnsInstanceAndDevice = false;
- backend_context_.reset(backend_context);
- gr_context_ = GrContext::MakeVulkan(backend_context_);
+ gpu::VulkanFunctionPointers* vulkan_function_pointers =
+ gpu::GetVulkanFunctionPointers();
+ backend_context.fGetProc =
+ make_unified_getter(vulkan_function_pointers->vkGetInstanceProcAddrFn,
+ vulkan_function_pointers->vkGetDeviceProcAddrFn);
+ backend_context.fOwnsInstanceAndDevice = false;
+ gr_context_ = GrContext::MakeVulkan(backend_context);
return true;
}
@@ -80,7 +78,6 @@ void VulkanInProcessContextProvider::Destroy() {
if (device_queue_) {
device_queue_->Destroy();
device_queue_.reset();
- backend_context_.reset();
}
}
diff --git a/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.h b/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.h
index 6a79746f332..426950ff38c 100644
--- a/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.h
+++ b/chromium/components/viz/common/gpu/vulkan_in_process_context_provider.h
@@ -45,7 +45,6 @@ class VIZ_COMMON_EXPORT VulkanInProcessContextProvider
sk_sp<GrContext> gr_context_;
gpu::VulkanImplementation* vulkan_implementation_;
std::unique_ptr<gpu::VulkanDeviceQueue> device_queue_;
- sk_sp<GrVkBackendContext> backend_context_;
#endif
DISALLOW_COPY_AND_ASSIGN(VulkanInProcessContextProvider);
diff --git a/chromium/components/viz/common/quads/compositor_frame_metadata.h b/chromium/components/viz/common/quads/compositor_frame_metadata.h
index c6033d9d5df..e14615c04af 100644
--- a/chromium/components/viz/common/quads/compositor_frame_metadata.h
+++ b/chromium/components/viz/common/quads/compositor_frame_metadata.h
@@ -14,6 +14,7 @@
#include "components/viz/common/quads/frame_deadline.h"
#include "components/viz/common/quads/selection.h"
#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/common/surfaces/surface_range.h"
#include "components/viz/common/viz_common_export.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/geometry/size_f.h"
@@ -23,6 +24,14 @@
namespace viz {
+// Compares two frame tokens, handling cases where the token wraps around the
+// 32-bit max value.
+inline bool FrameTokenGT(uint32_t token1, uint32_t token2) {
+ // There will be underflow in the subtraction if token1 was created
+ // after token2.
+ return (token2 - token1) > 0x80000000u;
+}
+
class VIZ_COMMON_EXPORT CompositorFrameMetadata {
public:
CompositorFrameMetadata();
@@ -84,7 +93,7 @@ class VIZ_COMMON_EXPORT CompositorFrameMetadata {
// determine which surfaces to retain and which to evict. It will likely
// be unnecessary for the embedder to explicitly specify which surfaces to
// retain. Thus, this field will likely go away.
- std::vector<SurfaceId> referenced_surfaces;
+ std::vector<SurfaceRange> referenced_surfaces;
// This is the set of dependent SurfaceIds that should be active in the
// display compositor before this CompositorFrame can be activated. Note
@@ -114,15 +123,25 @@ class VIZ_COMMON_EXPORT CompositorFrameMetadata {
// BeginFrameAck for the BeginFrame that this CompositorFrame answers.
BeginFrameAck begin_frame_ack;
- // Once the display compositor processes a frame containing a non-zero frame
- // token, the token is sent to embedder of the frame. This is helpful when
- // the embedder wants to do something after a particular frame is processed.
+ // An identifier for the frame. This is used to identify the frame for
+ // presentation-feedback, or when the frame-token is sent to the embedder.
+ // For comparing |frame_token| from different frames, use |FrameTokenGT()|
+ // instead of directly comparing them, since the tokens wrap around back to 1
+ // after the 32-bit max value.
+ // TODO(crbug.com/850386): A custom type would be better to avoid incorrect
+ // comparisons.
uint32_t frame_token = 0;
- // Once the display compositor presents a frame containing a non-zero
- // presentation token, a presentation feedback will be provided to
- // CompositorFrameSinkClient.
- uint32_t presentation_token = 0;
+ // Once the display compositor processes a frame with
+ // |send_frame_token_to_embedder| flag turned on, the |frame_token| for the
+ // frame is sent to embedder of the frame. This is helpful when the embedder
+ // wants to do something after a particular frame is processed.
+ bool send_frame_token_to_embedder = false;
+
+ // Once the display compositor presents a frame with
+ // |request_presentation_feedback| flag turned on, a presentation feedback
+ // will be provided to CompositorFrameSinkClient.
+ bool request_presentation_feedback = false;
private:
CompositorFrameMetadata(const CompositorFrameMetadata& other);
diff --git a/chromium/components/viz/common/quads/draw_quad.cc b/chromium/components/viz/common/quads/draw_quad.cc
index 2257fbc8b90..1703e4bca19 100644
--- a/chromium/components/viz/common/quads/draw_quad.cc
+++ b/chromium/components/viz/common/quads/draw_quad.cc
@@ -37,6 +37,11 @@ void DrawQuad::SetAll(const SharedQuadState* shared_quad_state,
DCHECK(shared_quad_state);
DCHECK(material != INVALID);
+
+ // The purpose of this check is to make sure |shared_quad_state| has a valid
+ // address.
+ // TODO(samans): Remove once https://crbug.com/852294 is resolved.
+ CHECK_GE(shared_quad_state->quad_layer_rect.size().width(), 0);
}
DrawQuad::~DrawQuad() {}
diff --git a/chromium/components/viz/common/quads/draw_quad_unittest.cc b/chromium/components/viz/common/quads/draw_quad_unittest.cc
index 34be8a3f6c0..479a1b89dbf 100644
--- a/chromium/components/viz/common/quads/draw_quad_unittest.cc
+++ b/chromium/components/viz/common/quads/draw_quad_unittest.cc
@@ -459,7 +459,7 @@ TEST(DrawQuadTest, CopyPictureDrawQuad) {
CREATE_QUAD_NEW(PictureDrawQuad, visible_rect, needs_blending, tex_coord_rect,
texture_size, nearest_neighbor, texture_format, content_rect,
- contents_scale, display_item_list);
+ contents_scale, {}, display_item_list);
EXPECT_EQ(DrawQuad::PICTURE_CONTENT, copy_quad->material);
EXPECT_EQ(visible_rect, copy_quad->visible_rect);
EXPECT_EQ(needs_blending, copy_quad->needs_blending);
@@ -473,7 +473,7 @@ TEST(DrawQuadTest, CopyPictureDrawQuad) {
CREATE_QUAD_ALL(PictureDrawQuad, tex_coord_rect, texture_size,
nearest_neighbor, texture_format, content_rect,
- contents_scale, display_item_list);
+ contents_scale, {}, display_item_list);
EXPECT_EQ(DrawQuad::PICTURE_CONTENT, copy_quad->material);
EXPECT_EQ(tex_coord_rect, copy_quad->tex_coord_rect);
EXPECT_EQ(texture_size, copy_quad->texture_size);
diff --git a/chromium/components/viz/common/quads/picture_draw_quad.cc b/chromium/components/viz/common/quads/picture_draw_quad.cc
index 2a51e856907..20363088a3c 100644
--- a/chromium/components/viz/common/quads/picture_draw_quad.cc
+++ b/chromium/components/viz/common/quads/picture_draw_quad.cc
@@ -28,6 +28,7 @@ void PictureDrawQuad::SetNew(
ResourceFormat texture_format,
const gfx::Rect& content_rect,
float contents_scale,
+ ImageAnimationMap image_animation_map,
scoped_refptr<cc::DisplayItemList> display_item_list) {
ContentDrawQuadBase::SetNew(
shared_quad_state, DrawQuad::PICTURE_CONTENT, rect, visible_rect,
@@ -36,6 +37,7 @@ void PictureDrawQuad::SetNew(
nearest_neighbor, false);
this->content_rect = content_rect;
this->contents_scale = contents_scale;
+ this->image_animation_map = std::move(image_animation_map);
this->display_item_list = std::move(display_item_list);
this->texture_format = texture_format;
}
@@ -51,6 +53,7 @@ void PictureDrawQuad::SetAll(
ResourceFormat texture_format,
const gfx::Rect& content_rect,
float contents_scale,
+ ImageAnimationMap image_animation_map,
scoped_refptr<cc::DisplayItemList> display_item_list) {
ContentDrawQuadBase::SetAll(
shared_quad_state, DrawQuad::PICTURE_CONTENT, rect, visible_rect,
@@ -59,6 +62,7 @@ void PictureDrawQuad::SetAll(
nearest_neighbor, false);
this->content_rect = content_rect;
this->contents_scale = contents_scale;
+ this->image_animation_map = std::move(image_animation_map);
this->display_item_list = std::move(display_item_list);
this->texture_format = texture_format;
}
diff --git a/chromium/components/viz/common/quads/picture_draw_quad.h b/chromium/components/viz/common/quads/picture_draw_quad.h
index ade1ecc8e8d..0341d72a9e5 100644
--- a/chromium/components/viz/common/quads/picture_draw_quad.h
+++ b/chromium/components/viz/common/quads/picture_draw_quad.h
@@ -7,8 +7,10 @@
#include <memory>
+#include "base/containers/flat_map.h"
#include "base/memory/ref_counted.h"
#include "cc/paint/display_item_list.h"
+#include "cc/paint/paint_image.h"
#include "components/viz/common/quads/content_draw_quad_base.h"
#include "components/viz/common/resources/resource_format.h"
#include "components/viz/common/viz_common_export.h"
@@ -25,6 +27,7 @@ class VIZ_COMMON_EXPORT PictureDrawQuad : public ContentDrawQuadBase {
PictureDrawQuad(const PictureDrawQuad& other);
~PictureDrawQuad() override;
+ using ImageAnimationMap = base::flat_map<cc::PaintImage::Id, size_t>;
void SetNew(const SharedQuadState* shared_quad_state,
const gfx::Rect& rect,
const gfx::Rect& visible_rect,
@@ -35,6 +38,7 @@ class VIZ_COMMON_EXPORT PictureDrawQuad : public ContentDrawQuadBase {
ResourceFormat texture_format,
const gfx::Rect& content_rect,
float contents_scale,
+ ImageAnimationMap image_animation_map,
scoped_refptr<cc::DisplayItemList> display_item_list);
void SetAll(const SharedQuadState* shared_quad_state,
@@ -47,10 +51,12 @@ class VIZ_COMMON_EXPORT PictureDrawQuad : public ContentDrawQuadBase {
ResourceFormat texture_format,
const gfx::Rect& content_rect,
float contents_scale,
+ ImageAnimationMap image_animation_map,
scoped_refptr<cc::DisplayItemList> display_item_list);
gfx::Rect content_rect;
float contents_scale;
+ ImageAnimationMap image_animation_map;
scoped_refptr<cc::DisplayItemList> display_item_list;
ResourceFormat texture_format;
diff --git a/chromium/components/viz/common/quads/render_pass.cc b/chromium/components/viz/common/quads/render_pass.cc
index 9252eeebb93..54cd2b5cddb 100644
--- a/chromium/components/viz/common/quads/render_pass.cc
+++ b/chromium/components/viz/common/quads/render_pass.cc
@@ -102,9 +102,8 @@ RenderPass::RenderPass(size_t shared_quad_state_list_size,
shared_quad_state_list_size) {}
RenderPass::~RenderPass() {
- TRACE_EVENT_OBJECT_DELETED_WITH_ID(
- TRACE_DISABLED_BY_DEFAULT("cc.debug.quads"), "RenderPass",
- reinterpret_cast<void*>(id));
+ TRACE_EVENT_OBJECT_DELETED_WITH_ID(TRACE_DISABLED_BY_DEFAULT("viz.quads"),
+ "RenderPass", reinterpret_cast<void*>(id));
}
std::unique_ptr<RenderPass> RenderPass::Copy(int new_id) const {
@@ -252,7 +251,7 @@ void RenderPass::AsValueInto(base::trace_event::TracedValue* value) const {
value->EndArray();
TracedValue::MakeDictIntoImplicitSnapshotWithCategory(
- TRACE_DISABLED_BY_DEFAULT("cc.debug.quads"), value, "RenderPass",
+ TRACE_DISABLED_BY_DEFAULT("viz.quads"), value, "RenderPass",
reinterpret_cast<void*>(id));
}
diff --git a/chromium/components/viz/common/quads/render_pass.h b/chromium/components/viz/common/quads/render_pass.h
index 6da9e23d8f6..f988133a445 100644
--- a/chromium/components/viz/common/quads/render_pass.h
+++ b/chromium/components/viz/common/quads/render_pass.h
@@ -35,7 +35,9 @@ class DrawQuad;
class RenderPassDrawQuad;
class SharedQuadState;
-// A list of DrawQuad objects, sorted internally in front-to-back order.
+// A list of DrawQuad objects, sorted internally in front-to-back order. To
+// add a new quad drawn behind another quad, it must be placed after the other
+// quad.
class VIZ_COMMON_EXPORT QuadList : public cc::ListContainer<DrawQuad> {
public:
QuadList();
diff --git a/chromium/components/viz/common/quads/shared_quad_state.cc b/chromium/components/viz/common/quads/shared_quad_state.cc
index 9572ec4d603..f724b8ec1f1 100644
--- a/chromium/components/viz/common/quads/shared_quad_state.cc
+++ b/chromium/components/viz/common/quads/shared_quad_state.cc
@@ -22,9 +22,8 @@ SharedQuadState::SharedQuadState()
SharedQuadState::SharedQuadState(const SharedQuadState& other) = default;
SharedQuadState::~SharedQuadState() {
- TRACE_EVENT_OBJECT_DELETED_WITH_ID(
- TRACE_DISABLED_BY_DEFAULT("cc.debug.quads"), "viz::SharedQuadState",
- this);
+ TRACE_EVENT_OBJECT_DELETED_WITH_ID(TRACE_DISABLED_BY_DEFAULT("viz.quads"),
+ "viz::SharedQuadState", this);
}
void SharedQuadState::SetAll(const gfx::Transform& quad_to_target_transform,
@@ -60,8 +59,8 @@ void SharedQuadState::AsValueInto(base::trace_event::TracedValue* value) const {
value->SetDouble("opacity", opacity);
value->SetString("blend_mode", SkBlendMode_Name(blend_mode));
TracedValue::MakeDictIntoImplicitSnapshotWithCategory(
- TRACE_DISABLED_BY_DEFAULT("cc.debug.quads"), value,
- "viz::SharedQuadState", this);
+ TRACE_DISABLED_BY_DEFAULT("viz.quads"), value, "viz::SharedQuadState",
+ this);
}
} // namespace viz
diff --git a/chromium/components/viz/common/resources/platform_color.h b/chromium/components/viz/common/resources/platform_color.h
index 6bf22ad88b4..28222d83ea9 100644
--- a/chromium/components/viz/common/resources/platform_color.h
+++ b/chromium/components/viz/common/resources/platform_color.h
@@ -69,6 +69,15 @@ class PlatformColor {
case LUMINANCE_F16:
case RGBA_F16:
case R16_EXT:
+ case BGR_565:
+ case RG_88:
+ case RGBX_8888:
+ case BGRX_8888:
+ case RGBX_1010102:
+ case BGRX_1010102:
+ case YVU_420:
+ case YUV_420_BIPLANAR:
+ case UYVY_422:
break;
}
diff --git a/chromium/components/viz/common/resources/platform_color_unittest.cc b/chromium/components/viz/common/resources/platform_color_unittest.cc
index feab684ca15..ee4bdf4917a 100644
--- a/chromium/components/viz/common/resources/platform_color_unittest.cc
+++ b/chromium/components/viz/common/resources/platform_color_unittest.cc
@@ -28,12 +28,21 @@ TEST(PlatformColorTest, SameComponentOrder) {
case ALPHA_8:
case LUMINANCE_8:
case RGB_565:
+ case BGR_565:
case RGBA_4444:
case ETC1:
case RED_8:
+ case RG_88:
case LUMINANCE_F16:
case RGBA_F16:
case R16_EXT:
+ case RGBX_8888:
+ case BGRX_8888:
+ case RGBX_1010102:
+ case BGRX_1010102:
+ case YVU_420:
+ case YUV_420_BIPLANAR:
+ case UYVY_422:
break;
}
}
diff --git a/chromium/components/viz/common/resources/resource.cc b/chromium/components/viz/common/resources/resource.cc
deleted file mode 100644
index 2597163d010..00000000000
--- a/chromium/components/viz/common/resources/resource.cc
+++ /dev/null
@@ -1,71 +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/common/resources/resource.h"
-
-#include "build/build_config.h"
-#include "components/viz/common/quads/shared_bitmap.h"
-
-namespace viz {
-namespace internal {
-
-Resource::Resource(const gfx::Size& size,
- ResourceType type,
- ResourceFormat format,
- const gfx::ColorSpace& color_space)
- : locked_for_external_use(false),
- marked_for_deletion(false),
- read_lock_fences_enabled(false),
- has_shared_bitmap_id(false),
- is_overlay_candidate(false),
-#if defined(OS_ANDROID)
- is_backed_by_surface_texture(false),
- wants_promotion_hint(false),
-#endif
- size(size),
- type(type),
- format(format),
- color_space(color_space) {
-}
-
-Resource::Resource(Resource&& other) = default;
-Resource::~Resource() = default;
-Resource& Resource::operator=(Resource&& other) = default;
-
-void Resource::SetSharedBitmap(SharedBitmap* bitmap) {
- DCHECK(bitmap);
- DCHECK(bitmap->pixels());
- shared_bitmap = bitmap;
- pixels = bitmap->pixels();
- has_shared_bitmap_id = true;
- shared_bitmap_id = bitmap->id();
-}
-
-void Resource::SetLocallyUsed() {
- synchronization_state_ = LOCALLY_USED;
- sync_token_.Clear();
-}
-
-void Resource::SetSynchronized() {
- synchronization_state_ = SYNCHRONIZED;
-}
-
-void Resource::UpdateSyncToken(const gpu::SyncToken& sync_token) {
- DCHECK(is_gpu_resource_type());
- // An empty sync token may be used if commands are guaranteed to have run on
- // the gpu process or in case of context loss.
- sync_token_ = sync_token;
- synchronization_state_ = sync_token.HasData() ? NEEDS_WAIT : SYNCHRONIZED;
-}
-
-int8_t* Resource::GetSyncTokenData() {
- return sync_token_.GetData();
-}
-
-bool Resource::ShouldWaitSyncToken() const {
- return synchronization_state_ == NEEDS_WAIT;
-}
-
-} // namespace internal
-} // namespace viz
diff --git a/chromium/components/viz/common/resources/resource.h b/chromium/components/viz/common/resources/resource.h
deleted file mode 100644
index 5fc1841b6aa..00000000000
--- a/chromium/components/viz/common/resources/resource.h
+++ /dev/null
@@ -1,206 +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_COMMON_RESOURCES_RESOURCE_H_
-#define COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_H_
-
-#include <memory>
-
-#include "build/build_config.h"
-#include "components/viz/common/quads/shared_bitmap.h"
-#include "components/viz/common/resources/release_callback.h"
-#include "components/viz/common/resources/resource_fence.h"
-#include "components/viz/common/resources/resource_format.h"
-#include "components/viz/common/resources/resource_id.h"
-#include "components/viz/common/resources/resource_type.h"
-#include "components/viz/common/viz_common_export.h"
-#include "gpu/command_buffer/common/mailbox.h"
-#include "gpu/command_buffer/common/sync_token.h"
-#include "third_party/khronos/GLES2/gl2.h"
-#include "ui/gfx/buffer_types.h"
-#include "ui/gfx/color_space.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace gfx {
-class GpuMemoryBuffer;
-}
-
-namespace viz {
-namespace internal {
-
-// The data structure used to track state of Gpu and Software-based
-// resources both in the client and the service, for resources transferred
-// between the two. This is an implementation detail of the resource tracking
-// for client and service libraries and should not be used directly from
-// external client code.
-struct VIZ_COMMON_EXPORT Resource {
- enum SynchronizationState {
- // The LOCALLY_USED state is the state each resource defaults to when
- // constructed or modified or read. This state indicates that the
- // resource has not been properly synchronized and it would be an error
- // to send this resource to a parent, child, or client.
- LOCALLY_USED,
-
- // The NEEDS_WAIT state is the state that indicates a resource has been
- // modified but it also has an associated sync token assigned to it.
- // The sync token has not been waited on with the local context. When
- // a sync token arrives from an external resource (such as a child or
- // parent), it is automatically initialized as NEEDS_WAIT as well
- // since we still need to wait on it before the resource is synchronized
- // on the current context. It is an error to use the resource locally for
- // reading or writing if the resource is in this state.
- NEEDS_WAIT,
-
- // The SYNCHRONIZED state indicates that the resource has been properly
- // synchronized locally. This can either synchronized externally (such
- // as the case of software rasterized bitmaps), or synchronized
- // internally using a sync token that has been waited upon. In the
- // former case where the resource was synchronized externally, a
- // corresponding sync token will not exist. In the latter case which was
- // synchronized from the NEEDS_WAIT state, a corresponding sync token will
- // exist which is associated with the resource. This sync token is still
- // valid and still associated with the resource and can be passed as an
- // external resource for others to wait on.
- SYNCHRONIZED,
- };
-
- Resource(const gfx::Size& size,
- ResourceType type,
- ResourceFormat format,
- const gfx::ColorSpace& color_space);
-
- Resource(Resource&& other);
-
- ~Resource();
-
- Resource& operator=(Resource&& other);
-
- bool is_gpu_resource_type() const { return type != ResourceType::kBitmap; }
-
- bool needs_sync_token() const {
- return type != ResourceType::kBitmap &&
- synchronization_state_ == LOCALLY_USED;
- }
-
- const gpu::SyncToken& sync_token() const { return sync_token_; }
-
- SynchronizationState synchronization_state() const {
- return synchronization_state_;
- }
-
- void SetSharedBitmap(SharedBitmap* bitmap);
-
- void SetLocallyUsed();
- void SetSynchronized();
- void UpdateSyncToken(const gpu::SyncToken& sync_token);
- // If true the texture-backed or GpuMemoryBuffer-backed resource needs its
- // SyncToken waited on in order to be synchronized for use.
- bool ShouldWaitSyncToken() const;
- int8_t* GetSyncTokenData();
-
- // Bitfield flags. ======
- // When true, the resource is currently being used externally.
- bool locked_for_external_use : 1;
- // When the resource should be deleted until it is actually reaped.
- bool marked_for_deletion : 1;
- // Tracks if a gpu fence needs to be used for reading a GpuMemoryBuffer-
- // backed or texture-backed resource.
- bool read_lock_fences_enabled : 1;
- // True if the software-backed resource is in shared memory, in which case
- // |shared_bitmap_id| will be valid.
- bool has_shared_bitmap_id : 1;
- // When true, the resource should be considered for being displayed in an
- // overlay.
- bool is_overlay_candidate : 1;
-#if defined(OS_ANDROID)
- // Indicates whether this resource may not be overlayed on Android, since
- // it's not backed by a SurfaceView. This may be set in combination with
- // |is_overlay_candidate|, to find out if switching the resource to a
- // a SurfaceView would result in overlay promotion. It's good to find this
- // out in advance, since one has no fallback path for displaying a
- // SurfaceView except via promoting it to an overlay. Ideally, one _could_
- // promote SurfaceTexture via the overlay path, even if one ended up just
- // drawing a quad in the compositor. However, for now, we use this flag to
- // refuse to promote so that the compositor will draw the quad.
- bool is_backed_by_surface_texture : 1;
- // Indicates that this resource would like a promotion hint.
- bool wants_promotion_hint : 1;
-#endif
-
- // In the service, this is the id of the client the resource comes from.
- int child_id = 0;
- // In the service, this is the id of the resource in the client's namespace.
- ResourceId id_in_child = 0;
- // Texture id for texture-backed resources.
- GLuint gl_id = 0;
- // The mailbox associated with resources received from the client to the
- // service. The mailbox has the IPC-capable data for sharing the resource
- // backing between modules/GL contexts/processes.
- gpu::Mailbox mailbox;
- // Non-owning pointer to a software-backed resource when mapped.
- uint8_t* pixels = nullptr;
- // Reference-counts to know when a resource can be released through the
- // |release_callback| after it is deleted, and for verifying correct use
- // of the resource.
- int lock_for_read_count = 0;
- int imported_count = 0;
- // A fence used for accessing a GpuMemoryBuffer-backed or texture-backed
- // 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;
- // Size of the resource in pixels.
- gfx::Size size;
- // The texture target for GpuMemoryBuffer- and texture-backed resources.
- GLenum target = GL_TEXTURE_2D;
- // The min/mag filter of the resource when it was given to/created by the
- // ResourceProvider, for texture-backed resources. Used to restore
- // the filter before releasing the resource. Not used for GpuMemoryBuffer-
- // backed resources as they are always internally created, so not released.
- // TODO(skyostil): Use a separate sampler object for filter state.
- GLenum original_filter = GL_LINEAR;
- // The current mag filter for GpuMemoryBuffer- and texture-backed resources.
- GLenum filter = GL_LINEAR;
- // The current min filter for GpuMemoryBuffer- and texture-backed resources.
- GLenum min_filter = GL_LINEAR;
- // The type of backing for the resource (such as gpu vs software).
- ResourceType type = ResourceType::kBitmap;
- // This is the the actual format of the underlying GpuMemoryBuffer, if any,
- // and might not correspond to ResourceFormat. This format is needed to
- // allocate the GpuMemoryBuffer and scanout the buffer as a hardware overlay.
- gfx::BufferFormat buffer_format = gfx::BufferFormat::RGBA_8888;
- // The format as seen from the compositor and might not correspond to
- // buffer_format (e.g: A resouce that was created from a YUV buffer could be
- // seen as RGB from the compositor/GL). This is used to derive the GL texture
- // format for texture-backed resources, the image format for GpuMemoryBuffer-
- // backed resources, or the SkColorType used for software-backed resources.
- ResourceFormat format = ResourceFormat::RGBA_8888;
- // The name of the shared memory for software-backed resources, but not
- // present if the resource isn't shared memory.
- SharedBitmapId shared_bitmap_id;
- // A pointer to the shared memory structure for software-backed resources, but
- // not present if the resources isn't shared memory.
- SharedBitmap* shared_bitmap = nullptr;
- // Ownership of |shared_bitmap| for when it is created internally.
- std::unique_ptr<SharedBitmap> owned_shared_bitmap;
- // The color space for all resource types, to control how the resource should
- // be drawn to output device.
- gfx::ColorSpace color_space;
-
- private:
- // Tracks if a sync token needs to be waited on before using the resource.
- SynchronizationState synchronization_state_ = SYNCHRONIZED;
- // A SyncToken associated with a texture-backed or GpuMemoryBuffer-backed
- // resource. It is given from a child to the service, and waited on in order
- // to use the resource, and this is tracked by the |synchronization_state_|.
- gpu::SyncToken sync_token_;
-
- DISALLOW_COPY_AND_ASSIGN(Resource);
-};
-
-} // namespace internal
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_H_
diff --git a/chromium/components/viz/common/resources/resource_format.h b/chromium/components/viz/common/resources/resource_format.h
index 265a064cb02..30a75a3e19b 100644
--- a/chromium/components/viz/common/resources/resource_format.h
+++ b/chromium/components/viz/common/resources/resource_format.h
@@ -16,12 +16,21 @@ enum ResourceFormat {
ALPHA_8,
LUMINANCE_8,
RGB_565,
+ BGR_565,
ETC1,
RED_8,
+ RG_88,
LUMINANCE_F16,
RGBA_F16,
R16_EXT,
- RESOURCE_FORMAT_MAX = R16_EXT,
+ RGBX_8888,
+ BGRX_8888,
+ RGBX_1010102,
+ BGRX_1010102,
+ YVU_420,
+ YUV_420_BIPLANAR,
+ UYVY_422,
+ RESOURCE_FORMAT_MAX = UYVY_422,
};
} // namespace viz
diff --git a/chromium/components/viz/common/resources/resource_format_utils.cc b/chromium/components/viz/common/resources/resource_format_utils.cc
index dcf5d8d7667..b8b89bcc167 100644
--- a/chromium/components/viz/common/resources/resource_format_utils.cc
+++ b/chromium/components/viz/common/resources/resource_format_utils.cc
@@ -39,6 +39,15 @@ SkColorType ResourceFormatToClosestSkColorType(bool gpu_compositing,
case RED_8:
case LUMINANCE_F16:
case R16_EXT:
+ case BGR_565:
+ case RG_88:
+ case RGBX_8888:
+ case BGRX_8888:
+ case RGBX_1010102:
+ case BGRX_1010102:
+ case YVU_420:
+ case YUV_420_BIPLANAR:
+ case UYVY_422:
return kN32_SkColorType;
case RGBA_F16:
return kRGBA_F16_SkColorType;
@@ -53,12 +62,22 @@ int BitsPerPixel(ResourceFormat format) {
return 64;
case BGRA_8888:
case RGBA_8888:
+ case RGBX_8888:
+ case BGRX_8888:
+ case RGBX_1010102:
+ case BGRX_1010102:
return 32;
case RGBA_4444:
case RGB_565:
case LUMINANCE_F16:
case R16_EXT:
+ case BGR_565:
+ case RG_88:
+ case UYVY_422:
return 16;
+ case YVU_420:
+ case YUV_420_BIPLANAR:
+ return 12;
case ALPHA_8:
case LUMINANCE_8:
case RED_8:
@@ -79,11 +98,20 @@ unsigned int GLDataType(ResourceFormat format) {
GL_UNSIGNED_BYTE, // ALPHA_8
GL_UNSIGNED_BYTE, // LUMINANCE_8
GL_UNSIGNED_SHORT_5_6_5, // RGB_565,
+ GL_ZERO, // BGR_565
GL_UNSIGNED_BYTE, // ETC1
GL_UNSIGNED_BYTE, // RED_8
+ GL_UNSIGNED_BYTE, // RG_88
GL_HALF_FLOAT_OES, // LUMINANCE_F16
GL_HALF_FLOAT_OES, // RGBA_F16
GL_UNSIGNED_SHORT, // R16_EXT
+ GL_UNSIGNED_BYTE, // RGBX_8888
+ GL_ZERO, // BGRX_8888
+ GL_UNSIGNED_INT, // RGBX_1010102
+ GL_ZERO, // BGRX_1010102
+ GL_ZERO, // YVU_420
+ GL_ZERO, // YUV_420_BIPLANAR
+ GL_ZERO, // UYVY_422
};
static_assert(arraysize(format_gl_data_type) == (RESOURCE_FORMAT_MAX + 1),
"format_gl_data_type does not handle all cases.");
@@ -100,14 +128,24 @@ unsigned int GLDataFormat(ResourceFormat format) {
GL_ALPHA, // ALPHA_8
GL_LUMINANCE, // LUMINANCE_8
GL_RGB, // RGB_565
+ GL_ZERO, // BGR_565
GL_ETC1_RGB8_OES, // ETC1
GL_RED_EXT, // RED_8
+ GL_RG_EXT, // RG_88
GL_LUMINANCE, // LUMINANCE_F16
GL_RGBA, // RGBA_F16
GL_RED_EXT, // R16_EXT
+ GL_RGB, // RGBX_8888
+ GL_ZERO, // BGRX_8888
+ GL_RGB10_A2_EXT, // RGBX_1010102
+ GL_ZERO, // BGRX_1010102
+ GL_ZERO, // YVU_420
+ GL_ZERO, // YUV_420_BIPLANAR
+ GL_ZERO, // UYVY_422
};
static_assert(arraysize(format_gl_data_format) == (RESOURCE_FORMAT_MAX + 1),
"format_gl_data_format does not handle all cases.");
+
return format_gl_data_format[format];
}
@@ -118,6 +156,8 @@ unsigned int GLInternalFormat(ResourceFormat format) {
// internal format (GL_R16_EXT).
if (format == R16_EXT)
return GL_R16_EXT;
+ else if (format == RG_88)
+ return GL_RG8_EXT;
return GLDataFormat(format);
}
@@ -137,14 +177,25 @@ unsigned int GLCopyTextureInternalFormat(ResourceFormat format) {
GL_ALPHA, // ALPHA_8
GL_LUMINANCE, // LUMINANCE_8
GL_RGB, // RGB_565
+ GL_ZERO, // 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_RGB, // RGBX_8888
+ GL_RGB, // BGRX_8888
+ GL_ZERO, // RGBX_1010102
+ GL_ZERO, // BGRX_1010102
+ GL_ZERO, // YVU_420
+ GL_ZERO, // YUV_420_BIPLANAR
+ GL_ZERO, // UYVY_422
};
+
static_assert(arraysize(format_gl_data_format) == (RESOURCE_FORMAT_MAX + 1),
"format_gl_data_format does not handle all cases.");
+
return format_gl_data_format[format];
}
@@ -164,6 +215,24 @@ gfx::BufferFormat BufferFormat(ResourceFormat format) {
return gfx::BufferFormat::ETC1;
case RGBA_F16:
return gfx::BufferFormat::RGBA_F16;
+ case BGR_565:
+ return gfx::BufferFormat::BGR_565;
+ case RG_88:
+ return gfx::BufferFormat::RG_88;
+ case RGBX_8888:
+ return gfx::BufferFormat::RGBX_8888;
+ case BGRX_8888:
+ return gfx::BufferFormat::BGRX_8888;
+ case RGBX_1010102:
+ return gfx::BufferFormat::RGBX_1010102;
+ case BGRX_1010102:
+ return gfx::BufferFormat::BGRX_1010102;
+ case YVU_420:
+ return gfx::BufferFormat::YVU_420;
+ case YUV_420_BIPLANAR:
+ return gfx::BufferFormat::YUV_420_BIPLANAR;
+ case UYVY_422:
+ return gfx::BufferFormat::UYVY_422;
case ALPHA_8:
case LUMINANCE_8:
case RGB_565:
@@ -197,11 +266,23 @@ unsigned int TextureStorageFormat(ResourceFormat format) {
return GL_RGB565;
case RED_8:
return GL_R8_EXT;
+ case RG_88:
+ return GL_RG8_EXT;
case LUMINANCE_F16:
return GL_LUMINANCE16F_EXT;
case R16_EXT:
return GL_R16_EXT;
+ case RGBX_8888:
+ return GL_RGB;
+ case RGBX_1010102:
+ return GL_RGB10_A2_EXT;
+ case BGR_565:
case ETC1:
+ case BGRX_8888:
+ case BGRX_1010102:
+ case YVU_420:
+ case YUV_420_BIPLANAR:
+ case UYVY_422:
break;
}
NOTREACHED();
@@ -218,11 +299,21 @@ bool IsGpuMemoryBufferFormatSupported(ResourceFormat format) {
case ETC1:
case RGBA_F16:
return true;
- // These formats have no BufferFormat equivalent.
+ // These formats have no BufferFormat equivalent or are only used
+ // for external textures, or have no GL equivalent formats.
case ALPHA_8:
case LUMINANCE_8:
case RGB_565:
case LUMINANCE_F16:
+ case BGR_565:
+ case RG_88:
+ case RGBX_8888:
+ case BGRX_8888:
+ case RGBX_1010102:
+ case BGRX_1010102:
+ case YVU_420:
+ case YUV_420_BIPLANAR:
+ case UYVY_422:
return false;
}
NOTREACHED();
@@ -243,10 +334,73 @@ bool IsBitmapFormatSupported(ResourceFormat format) {
case LUMINANCE_F16:
case RGBA_F16:
case R16_EXT:
+ case BGR_565:
+ case RG_88:
+ case RGBX_8888:
+ case BGRX_8888:
+ case RGBX_1010102:
+ case BGRX_1010102:
+ case YVU_420:
+ case YUV_420_BIPLANAR:
+ case UYVY_422:
return false;
}
NOTREACHED();
return false;
}
+ResourceFormat GetResourceFormat(gfx::BufferFormat format) {
+ switch (format) {
+ case gfx::BufferFormat::BGRA_8888:
+ return BGRA_8888;
+ case gfx::BufferFormat::R_8:
+ return RED_8;
+ case gfx::BufferFormat::R_16:
+ return R16_EXT;
+ case gfx::BufferFormat::RGBA_4444:
+ return RGBA_4444;
+ case gfx::BufferFormat::RGBA_8888:
+ return RGBA_8888;
+ case gfx::BufferFormat::ETC1:
+ return ETC1;
+ case gfx::BufferFormat::RGBA_F16:
+ return RGBA_F16;
+ case gfx::BufferFormat::BGR_565:
+ return BGR_565;
+ case gfx::BufferFormat::RG_88:
+ return RG_88;
+ case gfx::BufferFormat::RGBX_8888:
+ return RGBX_8888;
+ case gfx::BufferFormat::BGRX_8888:
+ return BGRX_8888;
+ case gfx::BufferFormat::RGBX_1010102:
+ return RGBX_1010102;
+ case gfx::BufferFormat::BGRX_1010102:
+ return BGRX_1010102;
+ case gfx::BufferFormat::YVU_420:
+ return YVU_420;
+ case gfx::BufferFormat::YUV_420_BIPLANAR:
+ return YUV_420_BIPLANAR;
+ case gfx::BufferFormat::UYVY_422:
+ return UYVY_422;
+ default:
+ NOTREACHED();
+ return RGBA_8888;
+ }
+}
+
+bool GLSupportsFormat(ResourceFormat format) {
+ switch (format) {
+ case BGR_565:
+ case BGRX_8888:
+ case BGRX_1010102:
+ case YVU_420:
+ case YUV_420_BIPLANAR:
+ case UYVY_422:
+ return false;
+ default:
+ return true;
+ }
+}
+
} // namespace viz
diff --git a/chromium/components/viz/common/resources/resource_format_utils.h b/chromium/components/viz/common/resources/resource_format_utils.h
index 27957f65517..1b7cf812d59 100644
--- a/chromium/components/viz/common/resources/resource_format_utils.h
+++ b/chromium/components/viz/common/resources/resource_format_utils.h
@@ -47,6 +47,11 @@ VIZ_RESOURCE_FORMAT_EXPORT bool IsGpuMemoryBufferFormatSupported(
// display compositor.
VIZ_RESOURCE_FORMAT_EXPORT bool IsBitmapFormatSupported(ResourceFormat format);
+VIZ_RESOURCE_FORMAT_EXPORT ResourceFormat
+GetResourceFormat(gfx::BufferFormat format);
+
+VIZ_RESOURCE_FORMAT_EXPORT bool GLSupportsFormat(ResourceFormat format);
+
} // namespace viz
#endif // COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_FORMAT_UTILS_H_
diff --git a/chromium/components/viz/common/resources/resource_type.h b/chromium/components/viz/common/resources/resource_type.h
deleted file mode 100644
index a9f8ae35ec2..00000000000
--- a/chromium/components/viz/common/resources/resource_type.h
+++ /dev/null
@@ -1,18 +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_COMMON_RESOURCES_RESOURCE_TYPE_H_
-#define COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_TYPE_H_
-
-namespace viz {
-
-// Types of resources that can be sent to the viz compositing service.
-enum class ResourceType {
- kTexture,
- kBitmap,
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_TYPE_H_
diff --git a/chromium/components/viz/common/quads/shared_bitmap.cc b/chromium/components/viz/common/resources/shared_bitmap.cc
index 26628cd68b9..a89aa3b8421 100644
--- a/chromium/components/viz/common/quads/shared_bitmap.cc
+++ b/chromium/components/viz/common/resources/shared_bitmap.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/viz/common/quads/shared_bitmap.h"
+#include "components/viz/common/resources/shared_bitmap.h"
#include <stddef.h>
#include <stdint.h>
@@ -17,10 +17,7 @@
namespace viz {
-SharedBitmap::SharedBitmap(uint8_t* pixels,
- const SharedBitmapId& id,
- uint32_t sequence_number)
- : pixels_(pixels), id_(id), sequence_number_(sequence_number) {}
+SharedBitmap::SharedBitmap(uint8_t* pixels) : pixels_(pixels) {}
SharedBitmap::~SharedBitmap() {}
@@ -32,11 +29,4 @@ SharedBitmapId SharedBitmap::GenerateId() {
return id;
}
-base::trace_event::MemoryAllocatorDumpGuid GetSharedBitmapGUIDForTracing(
- const SharedBitmapId& bitmap_id) {
- auto bitmap_id_hex = base::HexEncode(bitmap_id.name, sizeof(bitmap_id.name));
- return base::trace_event::MemoryAllocatorDumpGuid(
- base::StringPrintf("sharedbitmap-x-process/%s", bitmap_id_hex.c_str()));
-}
-
} // namespace viz
diff --git a/chromium/components/viz/common/quads/shared_bitmap.h b/chromium/components/viz/common/resources/shared_bitmap.h
index aa48b9bea95..ed34c4f2a4f 100644
--- a/chromium/components/viz/common/quads/shared_bitmap.h
+++ b/chromium/components/viz/common/resources/shared_bitmap.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_VIZ_COMMON_QUADS_SHARED_BITMAP_H_
-#define COMPONENTS_VIZ_COMMON_QUADS_SHARED_BITMAP_H_
+#ifndef COMPONENTS_VIZ_COMMON_RESOURCES_SHARED_BITMAP_H_
+#define COMPONENTS_VIZ_COMMON_RESOURCES_SHARED_BITMAP_H_
#include <stddef.h>
#include <stdint.h>
@@ -16,10 +16,6 @@
#include "gpu/command_buffer/common/mailbox.h"
#include "ui/gfx/geometry/size.h"
-namespace base {
-class UnguessableToken;
-}
-
namespace viz {
using SharedBitmapId = gpu::Mailbox;
@@ -29,38 +25,26 @@ struct SharedBitmapIdHash {
}
};
-VIZ_COMMON_EXPORT base::trace_event::MemoryAllocatorDumpGuid
-GetSharedBitmapGUIDForTracing(const SharedBitmapId& bitmap_id);
-
+// An object returned by the SharedBitmapGenerator that exposes the
+// pixels for a SharedBitmapId. They are exposed via a class so that
+// this object (or its subclass) can ensure the lifetime of the pixels
+// is not cut short. While this object is kept alive, the pixels should
+// remain valid.
class VIZ_COMMON_EXPORT SharedBitmap {
public:
- SharedBitmap(uint8_t* pixels,
- const SharedBitmapId& id,
- uint32_t sequence_number);
+ static SharedBitmapId GenerateId();
+ explicit SharedBitmap(uint8_t* pixels);
virtual ~SharedBitmap();
uint8_t* pixels() { return pixels_; }
- const SharedBitmapId& id() { return id_; }
-
- // The sequence number that ClientSharedBitmapManager assigned to this
- // SharedBitmap.
- uint32_t sequence_number() const { return sequence_number_; }
-
- // Returns the GUID for tracing when the SharedBitmap supports cross-process
- // use via shared memory. Otherwise, this returns empty.
- virtual base::UnguessableToken GetCrossProcessGUID() const = 0;
-
- static SharedBitmapId GenerateId();
private:
uint8_t* pixels_;
- SharedBitmapId id_;
- const uint32_t sequence_number_;
DISALLOW_COPY_AND_ASSIGN(SharedBitmap);
};
} // namespace viz
-#endif // COMPONENTS_VIZ_COMMON_QUADS_SHARED_BITMAP_H_
+#endif // COMPONENTS_VIZ_COMMON_RESOURCES_SHARED_BITMAP_H_
diff --git a/chromium/components/viz/common/resources/shared_bitmap_reporter.h b/chromium/components/viz/common/resources/shared_bitmap_reporter.h
deleted file mode 100644
index 2494a2f004b..00000000000
--- a/chromium/components/viz/common/resources/shared_bitmap_reporter.h
+++ /dev/null
@@ -1,34 +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_RESOURCES_SHARED_BITMAP_REPORTER_H_
-#define COMPONENTS_VIZ_COMMON_RESOURCES_SHARED_BITMAP_REPORTER_H_
-
-#include "components/viz/common/quads/shared_bitmap.h"
-#include "components/viz/common/viz_common_export.h"
-#include "mojo/public/cpp/system/buffer.h"
-
-namespace viz {
-
-// Used by clients to notify the display compositor about SharedMemory allocated
-// for shared bitmaps.
-// TODO(kylechar): This should be //components/viz/client but because of deps
-// issues that isn't possible. Fix and move there.
-class VIZ_COMMON_EXPORT SharedBitmapReporter {
- public:
- // Associates a SharedBitmapId with a shared buffer handle.
- virtual void DidAllocateSharedBitmap(mojo::ScopedSharedBufferHandle buffer,
- const SharedBitmapId& id) = 0;
-
- // Disassociates a SharedBitmapId previously passed to
- // DidAllocateSharedBitmap.
- virtual void DidDeleteSharedBitmap(const SharedBitmapId& id) = 0;
-
- protected:
- virtual ~SharedBitmapReporter();
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_COMMON_RESOURCES_SHARED_BITMAP_REPORTER_H_
diff --git a/chromium/components/viz/common/resources/transferable_resource.h b/chromium/components/viz/common/resources/transferable_resource.h
index fafb1770c86..d39240a4aed 100644
--- a/chromium/components/viz/common/resources/transferable_resource.h
+++ b/chromium/components/viz/common/resources/transferable_resource.h
@@ -10,9 +10,9 @@
#include <vector>
#include "build/build_config.h"
-#include "components/viz/common/quads/shared_bitmap.h"
#include "components/viz/common/resources/resource_format.h"
#include "components/viz/common/resources/resource_id.h"
+#include "components/viz/common/resources/shared_bitmap.h"
#include "components/viz/common/viz_common_export.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
#include "ui/gfx/buffer_types.h"
@@ -97,11 +97,6 @@ struct VIZ_COMMON_EXPORT TransferableResource {
// and must be RGBA_8888 always for software resources.
ResourceFormat format = RGBA_8888;
- // Normally derrived from the |format|, but may differ for some resource
- // producers. This is the format of the underlying texture backing for gpu
- // resources, needed for using the backing as an overlay.
- gfx::BufferFormat buffer_format = gfx::BufferFormat::RGBA_8888;
-
// The |mailbox| inside here holds the gpu::Mailbox when this is a gpu
// resource, or the SharedBitmapId when it is a software resource.
// The |texture_target| and sync_token| inside here only apply for gpu
@@ -139,7 +134,7 @@ struct VIZ_COMMON_EXPORT TransferableResource {
bool operator==(const TransferableResource& o) const {
return id == o.id && is_software == o.is_software && size == o.size &&
- format == o.format && buffer_format == o.buffer_format &&
+ format == o.format &&
mailbox_holder.mailbox == o.mailbox_holder.mailbox &&
mailbox_holder.sync_token == o.mailbox_holder.sync_token &&
mailbox_holder.texture_target == o.mailbox_holder.texture_target &&
diff --git a/chromium/components/viz/common/skia_helper.cc b/chromium/components/viz/common/skia_helper.cc
index 65d12b59bce..4e0f445be8e 100644
--- a/chromium/components/viz/common/skia_helper.cc
+++ b/chromium/components/viz/common/skia_helper.cc
@@ -20,7 +20,7 @@ sk_sp<SkImage> SkiaHelper::ApplyImageFilter(sk_sp<SkImage> src_image,
return nullptr;
if (!src_image) {
- TRACE_EVENT_INSTANT0("cc",
+ TRACE_EVENT_INSTANT0("viz",
"ApplyImageFilter wrap background texture failed",
TRACE_EVENT_SCOPE_THREAD);
return nullptr;
diff --git a/chromium/components/viz/common/surfaces/child_local_surface_id_allocator.cc b/chromium/components/viz/common/surfaces/child_local_surface_id_allocator.cc
index f39a5479fa9..d91a007f20a 100644
--- a/chromium/components/viz/common/surfaces/child_local_surface_id_allocator.cc
+++ b/chromium/components/viz/common/surfaces/child_local_surface_id_allocator.cc
@@ -7,6 +7,7 @@
#include <stdint.h>
#include "base/rand_util.h"
+#include "base/trace_event/trace_event.h"
namespace viz {
@@ -17,8 +18,10 @@ ChildLocalSurfaceIdAllocator::ChildLocalSurfaceIdAllocator()
bool ChildLocalSurfaceIdAllocator::UpdateFromParent(
const LocalSurfaceId& parent_allocated_local_surface_id) {
- if (parent_allocated_local_surface_id.parent_sequence_number() >
- current_local_surface_id_.parent_sequence_number()) {
+ if ((parent_allocated_local_surface_id.parent_sequence_number() >
+ current_local_surface_id_.parent_sequence_number()) ||
+ parent_allocated_local_surface_id.embed_token() !=
+ current_local_surface_id_.embed_token()) {
current_local_surface_id_.parent_sequence_number_ =
parent_allocated_local_surface_id.parent_sequence_number_;
current_local_surface_id_.embed_token_ =
@@ -34,6 +37,22 @@ const LocalSurfaceId& ChildLocalSurfaceIdAllocator::GenerateId() {
kInvalidParentSequenceNumber);
++current_local_surface_id_.child_sequence_number_;
+
+ TRACE_EVENT_WITH_FLOW2(
+ TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
+ "LocalSurfaceId.Embed.Flow",
+ TRACE_ID_GLOBAL(current_local_surface_id_.embed_trace_id()),
+ TRACE_EVENT_FLAG_FLOW_OUT, "step",
+ "ChildLocalSurfaceIdAllocator::GenerateId", "local_surface_id",
+ current_local_surface_id_.ToString());
+ TRACE_EVENT_WITH_FLOW2(
+ TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
+ "LocalSurfaceId.Submission.Flow",
+ TRACE_ID_GLOBAL(current_local_surface_id_.submission_trace_id()),
+ TRACE_EVENT_FLAG_FLOW_OUT, "step",
+ "ChildLocalSurfaceIdAllocator::GenerateId", "local_surface_id",
+ current_local_surface_id_.ToString());
+
return current_local_surface_id_;
}
diff --git a/chromium/components/viz/common/surfaces/child_local_surface_id_allocator_unittest.cc b/chromium/components/viz/common/surfaces/child_local_surface_id_allocator_unittest.cc
index 99dd27aea58..9b6ebaf26bf 100644
--- a/chromium/components/viz/common/surfaces/child_local_surface_id_allocator_unittest.cc
+++ b/chromium/components/viz/common/surfaces/child_local_surface_id_allocator_unittest.cc
@@ -4,6 +4,7 @@
#include "components/viz/common/surfaces/child_local_surface_id_allocator.h"
+#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "testing/gtest/include/gtest/gtest.h"
// ChildLocalSurfaceIdAllocator has 1 accessor which does not alter state:
@@ -105,6 +106,26 @@ TEST(ChildLocalSurfaceIdAllocatorTest,
parent_allocated_local_surface_id.embed_token());
}
+// UpdateFromParent() on a child allocator should accept the parent's
+// LocalSurfaceId if only the embed_token changed.
+TEST(ChildLocalSurfaceIdAllocatorTest, UpdateFromParentEmbedTokenChanged) {
+ ParentLocalSurfaceIdAllocator parent_allocator;
+ ParentLocalSurfaceIdAllocator parent_allocator2;
+ ChildLocalSurfaceIdAllocator child_allocator;
+
+ EXPECT_TRUE(parent_allocator.GenerateId().is_valid());
+ EXPECT_TRUE(child_allocator.UpdateFromParent(
+ parent_allocator.GetCurrentLocalSurfaceId()));
+ EXPECT_LE(
+ parent_allocator2.GetCurrentLocalSurfaceId().parent_sequence_number(),
+ parent_allocator.GetCurrentLocalSurfaceId().parent_sequence_number());
+ EXPECT_NE(parent_allocator2.GetCurrentLocalSurfaceId().embed_token(),
+ parent_allocator.GetCurrentLocalSurfaceId().embed_token());
+
+ EXPECT_TRUE(child_allocator.UpdateFromParent(
+ parent_allocator2.GetCurrentLocalSurfaceId()));
+}
+
// GenerateId() on a child allocator should monotonically increment the child
// sequence number.
TEST(ChildLocalSurfaceIdAllocatorTest,
diff --git a/chromium/components/viz/common/surfaces/frame_sink_id.h b/chromium/components/viz/common/surfaces/frame_sink_id.h
index 30319d7b3eb..f06981ef620 100644
--- a/chromium/components/viz/common/surfaces/frame_sink_id.h
+++ b/chromium/components/viz/common/surfaces/frame_sink_id.h
@@ -16,6 +16,22 @@
namespace viz {
+// A FrameSinkId uniquely identifies a CompositorFrameSink and the client that
+// uses it within the Viz compositing system. FrameSinkIds are used the first
+// component of a SurfaceId, which is a FrameSinkId + LocalSurfaceId, to ensure
+// SurfaceIds are unique across all CompositorFrameSinks.
+//
+// FrameSinkId consists of:
+//
+// - client_id: This part uniquely identifies a client namespace, typically one
+// per process.
+//
+// - sink_id: This part uniquely identifies a FrameSink within the client
+// namespace. This component may be allocated by the client specified
+// by the client_id.
+//
+// The FrameSinkId for a given client_id may be allocated using a
+// FrameSinkIdAllocator.
class VIZ_COMMON_EXPORT FrameSinkId {
public:
constexpr FrameSinkId() : client_id_(0), sink_id_(0) {}
diff --git a/chromium/components/viz/common/surfaces/frame_sink_id_allocator.cc b/chromium/components/viz/common/surfaces/frame_sink_id_allocator.cc
new file mode 100644
index 00000000000..007306473cc
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/frame_sink_id_allocator.cc
@@ -0,0 +1,16 @@
+// 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/surfaces/frame_sink_id_allocator.h"
+
+namespace viz {
+
+constexpr FrameSinkId g_invalid_frame_sink_id;
+
+// static
+const FrameSinkId& FrameSinkIdAllocator::InvalidFrameSinkId() {
+ return g_invalid_frame_sink_id;
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/surfaces/frame_sink_id_allocator.h b/chromium/components/viz/common/surfaces/frame_sink_id_allocator.h
index 3594dab8748..469c1798557 100644
--- a/chromium/components/viz/common/surfaces/frame_sink_id_allocator.h
+++ b/chromium/components/viz/common/surfaces/frame_sink_id_allocator.h
@@ -7,11 +7,13 @@
#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/viz_common_export.h"
+
namespace viz {
// This class generates FrameSinkId with a fixed client_id and an
// incrementally-increasing sink_id.
-class FrameSinkIdAllocator {
+class VIZ_COMMON_EXPORT FrameSinkIdAllocator {
public:
constexpr explicit FrameSinkIdAllocator(uint32_t client_id)
: client_id_(client_id), next_sink_id_(1u) {}
@@ -20,6 +22,8 @@ class FrameSinkIdAllocator {
return FrameSinkId(client_id_, next_sink_id_++);
}
+ static const FrameSinkId& InvalidFrameSinkId();
+
private:
const uint32_t client_id_;
uint32_t next_sink_id_;
diff --git a/chromium/components/viz/common/surfaces/local_surface_id.cc b/chromium/components/viz/common/surfaces/local_surface_id.cc
index 8fa277049a3..2cab009747e 100644
--- a/chromium/components/viz/common/surfaces/local_surface_id.cc
+++ b/chromium/components/viz/common/surfaces/local_surface_id.cc
@@ -23,4 +23,19 @@ std::ostream& operator<<(std::ostream& out,
return out << local_surface_id.ToString();
}
+bool LocalSurfaceId::IsSameOrNewerThan(const LocalSurfaceId& other) const {
+ return IsNewerThan(other) || *this == other;
+}
+
+bool LocalSurfaceId::IsNewerThan(const LocalSurfaceId& other) const {
+ // Sequence numbers can wrap around so look at their difference instead of
+ // their absolute values.
+ return embed_token_ == other.embed_token_ &&
+ (child_sequence_number_ - other.child_sequence_number_ < (1u << 31)) &&
+ (parent_sequence_number_ - other.parent_sequence_number_ <
+ (1u << 31)) &&
+ (child_sequence_number_ != other.child_sequence_number_ ||
+ parent_sequence_number_ != other.parent_sequence_number_);
+}
+
} // namespace viz
diff --git a/chromium/components/viz/common/surfaces/local_surface_id.h b/chromium/components/viz/common/surfaces/local_surface_id.h
index 5a84bb1965c..88cccf2fb6d 100644
--- a/chromium/components/viz/common/surfaces/local_surface_id.h
+++ b/chromium/components/viz/common/surfaces/local_surface_id.h
@@ -28,6 +28,10 @@ constexpr uint32_t kInvalidParentSequenceNumber = 0;
constexpr uint32_t kInvalidChildSequenceNumber = 0;
constexpr uint32_t kInitialParentSequenceNumber = 1;
constexpr uint32_t kInitialChildSequenceNumber = 1;
+constexpr uint32_t kMaxParentSequenceNumber =
+ std::numeric_limits<uint32_t>::max();
+constexpr uint32_t kMaxChildSequenceNumber =
+ std::numeric_limits<uint32_t>::max();
// This struct is the part of SurfaceId that can be modified by the client.
// LocalSurfaceId uniquely identifies a surface among the surfaces created by a
@@ -82,6 +86,11 @@ class VIZ_COMMON_EXPORT LocalSurfaceId {
child_sequence_number_(child_sequence_number),
embed_token_(embed_token) {}
+ static constexpr LocalSurfaceId MaxSequenceId() {
+ return LocalSurfaceId(kMaxParentSequenceNumber, kMaxChildSequenceNumber,
+ base::UnguessableToken());
+ }
+
constexpr bool is_valid() const {
return parent_sequence_number_ != kInvalidParentSequenceNumber &&
child_sequence_number_ != kInvalidChildSequenceNumber &&
@@ -100,6 +109,14 @@ class VIZ_COMMON_EXPORT LocalSurfaceId {
return embed_token_;
}
+ // The |embed_trace_id| is used as the id for trace events associated with
+ // embedding this LocalSurfaceId.
+ uint64_t embed_trace_id() const { return hash() << 1; }
+
+ // The |submission_trace_id| is used as the id for trace events associated
+ // with submission of a CompositorFrame to a surface with this LocalSurfaceId.
+ uint64_t submission_trace_id() const { return (hash() << 1) | 1; }
+
bool operator==(const LocalSurfaceId& other) const {
return parent_sequence_number_ == other.parent_sequence_number_ &&
child_sequence_number_ == other.child_sequence_number_ &&
@@ -120,6 +137,13 @@ class VIZ_COMMON_EXPORT LocalSurfaceId {
std::string ToString() const;
+ // Returns whether this LocalSurfaceId was generated after |other|.
+ bool IsNewerThan(const LocalSurfaceId& other) const;
+
+ // Returns whether this LocalSurfaceId was generated after |other| or equal to
+ // it.
+ bool IsSameOrNewerThan(const LocalSurfaceId& other) const;
+
private:
friend struct mojo::StructTraits<mojom::LocalSurfaceIdDataView,
LocalSurfaceId>;
diff --git a/chromium/components/viz/common/surfaces/parent_local_surface_id_allocator.cc b/chromium/components/viz/common/surfaces/parent_local_surface_id_allocator.cc
index 42586557b4c..511e8aeca08 100644
--- a/chromium/components/viz/common/surfaces/parent_local_surface_id_allocator.cc
+++ b/chromium/components/viz/common/surfaces/parent_local_surface_id_allocator.cc
@@ -4,18 +4,19 @@
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
-#include "base/lazy_instance.h"
#include "base/rand_util.h"
+#include "base/trace_event/trace_event.h"
namespace viz {
-base::LazyInstance<LocalSurfaceId>::Leaky g_invalid_local_surface_id =
- LAZY_INSTANCE_INITIALIZER;
+constexpr LocalSurfaceId g_invalid_local_surface_id;
ParentLocalSurfaceIdAllocator::ParentLocalSurfaceIdAllocator()
- : current_local_surface_id_(kInitialParentSequenceNumber,
+ : current_local_surface_id_(kInvalidParentSequenceNumber,
kInitialChildSequenceNumber,
- base::UnguessableToken::Create()) {}
+ base::UnguessableToken::Create()) {
+ GenerateId();
+}
bool ParentLocalSurfaceIdAllocator::UpdateFromChild(
const LocalSurfaceId& child_allocated_local_surface_id) {
@@ -24,6 +25,13 @@ bool ParentLocalSurfaceIdAllocator::UpdateFromChild(
current_local_surface_id_.child_sequence_number_ =
child_allocated_local_surface_id.child_sequence_number_;
is_invalid_ = false;
+ TRACE_EVENT_WITH_FLOW2(
+ TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
+ "LocalSurfaceId.Embed.Flow",
+ TRACE_ID_GLOBAL(current_local_surface_id_.embed_trace_id()),
+ TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
+ "UpdateFromChild", "local_surface_id",
+ current_local_surface_id_.ToString());
return true;
}
return false;
@@ -39,17 +47,39 @@ void ParentLocalSurfaceIdAllocator::Invalidate() {
}
const LocalSurfaceId& ParentLocalSurfaceIdAllocator::GenerateId() {
- if (!is_allocation_suppressed_)
+ if (!is_allocation_suppressed_) {
++current_local_surface_id_.parent_sequence_number_;
+ TRACE_EVENT_WITH_FLOW2(
+ TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
+ "LocalSurfaceId.Embed.Flow",
+ TRACE_ID_GLOBAL(current_local_surface_id_.embed_trace_id()),
+ TRACE_EVENT_FLAG_FLOW_OUT, "step",
+ "ParentLocalSurfaceIdAllocator::GenerateId", "local_surface_id",
+ current_local_surface_id_.ToString());
+ TRACE_EVENT_WITH_FLOW2(
+ TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
+ "LocalSurfaceId.Submission.Flow",
+ TRACE_ID_GLOBAL(current_local_surface_id_.submission_trace_id()),
+ TRACE_EVENT_FLAG_FLOW_OUT, "step",
+ "ParentLocalSurfaceIdAllocator::GenerateId", "local_surface_id",
+ current_local_surface_id_.ToString());
+ }
is_invalid_ = false;
+
+
return current_local_surface_id_;
}
const LocalSurfaceId& ParentLocalSurfaceIdAllocator::GetCurrentLocalSurfaceId()
const {
if (is_invalid_)
- return g_invalid_local_surface_id.Get();
+ return g_invalid_local_surface_id;
return current_local_surface_id_;
}
+// static
+const LocalSurfaceId& ParentLocalSurfaceIdAllocator::InvalidLocalSurfaceId() {
+ return g_invalid_local_surface_id;
+}
+
} // namespace viz
diff --git a/chromium/components/viz/common/surfaces/parent_local_surface_id_allocator.h b/chromium/components/viz/common/surfaces/parent_local_surface_id_allocator.h
index 2dab7b67756..94812cc782f 100644
--- a/chromium/components/viz/common/surfaces/parent_local_surface_id_allocator.h
+++ b/chromium/components/viz/common/surfaces/parent_local_surface_id_allocator.h
@@ -46,6 +46,8 @@ class VIZ_COMMON_EXPORT ParentLocalSurfaceIdAllocator {
const LocalSurfaceId& GetCurrentLocalSurfaceId() const;
+ static const LocalSurfaceId& InvalidLocalSurfaceId();
+
bool is_allocation_suppressed() const { return is_allocation_suppressed_; }
private:
diff --git a/chromium/components/viz/common/surfaces/surface_id.h b/chromium/components/viz/common/surfaces/surface_id.h
index dda288c13f2..b61c347a2f4 100644
--- a/chromium/components/viz/common/surfaces/surface_id.h
+++ b/chromium/components/viz/common/surfaces/surface_id.h
@@ -40,6 +40,10 @@ class VIZ_COMMON_EXPORT SurfaceId {
const LocalSurfaceId& local_surface_id)
: frame_sink_id_(frame_sink_id), local_surface_id_(local_surface_id) {}
+ static constexpr SurfaceId MaxSequenceId(const FrameSinkId& frame_sink_id) {
+ return SurfaceId(frame_sink_id, LocalSurfaceId::MaxSequenceId());
+ }
+
bool is_valid() const {
return frame_sink_id_.is_valid() && local_surface_id_.is_valid();
}
diff --git a/chromium/components/viz/common/surfaces/surface_range.cc b/chromium/components/viz/common/surfaces/surface_range.cc
new file mode 100644
index 00000000000..48687676c10
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/surface_range.cc
@@ -0,0 +1,60 @@
+// 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/surfaces/surface_range.h"
+
+#include "base/strings/stringprintf.h"
+
+namespace viz {
+
+SurfaceRange::SurfaceRange() = default;
+
+SurfaceRange::SurfaceRange(const base::Optional<SurfaceId>& start,
+ const SurfaceId& end)
+ : start_(start), end_(end) {}
+
+SurfaceRange::SurfaceRange(const SurfaceId& surface_id)
+ : start_(surface_id), end_(surface_id) {}
+
+SurfaceRange::SurfaceRange(const SurfaceRange& other) = default;
+
+bool SurfaceRange::operator==(const SurfaceRange& other) const {
+ return start_ == other.start() && end_ == other.end();
+}
+
+bool SurfaceRange::operator!=(const SurfaceRange& other) const {
+ return !(*this == other);
+}
+
+bool SurfaceRange::operator<(const SurfaceRange& other) const {
+ return std::tie(end_, start_) < std::tie(other.end(), other.start());
+}
+
+bool SurfaceRange::IsValid() const {
+ if (!end_.is_valid())
+ return false;
+
+ if (!start_)
+ return true;
+
+ if (!start_->is_valid())
+ return false;
+
+ if (end_.frame_sink_id() != start_->frame_sink_id())
+ return true;
+
+ return end_.local_surface_id().IsSameOrNewerThan(start_->local_surface_id());
+}
+
+std::string SurfaceRange::ToString() const {
+ return base::StringPrintf("SurfaceRange(start: %s, end: %s)",
+ start_ ? start_->ToString().c_str() : "none",
+ end_.ToString().c_str());
+}
+
+std::ostream& operator<<(std::ostream& out, const SurfaceRange& surface_range) {
+ return out << surface_range.ToString();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/common/surfaces/surface_range.h b/chromium/components/viz/common/surfaces/surface_range.h
new file mode 100644
index 00000000000..b062cfcbb6a
--- /dev/null
+++ b/chromium/components/viz/common/surfaces/surface_range.h
@@ -0,0 +1,58 @@
+// 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_SURFACES_SURFACE_RANGE_H_
+#define COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_RANGE_H_
+
+#include "base/optional.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/common/viz_common_export.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+
+namespace viz {
+
+namespace mojom {
+class SurfaceRangeDataView;
+}
+
+// SurfaceRange consists of two SurfaceIds representing a range of surfaces
+// [start,end] ordered by SurfaceId where |start| is an optional surface
+// which acts like a "fallback" surface in case it exists.
+class VIZ_COMMON_EXPORT SurfaceRange {
+ public:
+ SurfaceRange();
+
+ SurfaceRange(const base::Optional<SurfaceId>& start, const SurfaceId& end);
+
+ explicit SurfaceRange(const SurfaceId& surface_id);
+
+ SurfaceRange(const SurfaceRange& other);
+
+ bool operator==(const SurfaceRange& other) const;
+
+ bool operator!=(const SurfaceRange& other) const;
+
+ bool operator<(const SurfaceRange& other) const;
+
+ bool IsValid() const;
+
+ const base::Optional<SurfaceId>& start() const { return start_; }
+
+ const SurfaceId& end() const { return end_; }
+
+ std::string ToString() const;
+
+ private:
+ friend struct mojo::StructTraits<mojom::SurfaceRangeDataView, SurfaceRange>;
+
+ base::Optional<SurfaceId> start_;
+ SurfaceId end_;
+};
+
+VIZ_COMMON_EXPORT std::ostream& operator<<(std::ostream& out,
+ const SurfaceRange& surface_range);
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_COMMON_SURFACES_SURFACE_RANGE_H_
diff --git a/chromium/components/viz/common/yuv_readback_unittest.cc b/chromium/components/viz/common/yuv_readback_unittest.cc
index a758f3cb9f8..9bc57fcad1b 100644
--- a/chromium/components/viz/common/yuv_readback_unittest.cc
+++ b/chromium/components/viz/common/yuv_readback_unittest.cc
@@ -43,7 +43,7 @@ class YUVReadbackTest : public testing::Test {
attributes.sample_buffers = 1;
attributes.bind_generates_resource = false;
- context_ = gpu::GLInProcessContext::CreateWithoutInit();
+ context_ = std::make_unique<gpu::GLInProcessContext>();
auto result =
context_->Initialize(nullptr, /* service */
nullptr, /* surface */
@@ -356,9 +356,8 @@ class YUVReadbackTest : public testing::Test {
GL_UNSIGNED_BYTE, input_pixels.getPixels());
gpu::Mailbox mailbox;
- gl_->GenMailboxCHROMIUM(mailbox.name);
- EXPECT_FALSE(mailbox.IsZero());
gl_->ProduceTextureDirectCHROMIUM(src_texture, mailbox.name);
+ EXPECT_FALSE(mailbox.IsZero());
gpu::SyncToken sync_token;
gl_->GenSyncTokenCHROMIUM(sync_token.GetData());
diff --git a/chromium/components/viz/host/BUILD.gn b/chromium/components/viz/host/BUILD.gn
index e82fa99cdba..660099dbd04 100644
--- a/chromium/components/viz/host/BUILD.gn
+++ b/chromium/components/viz/host/BUILD.gn
@@ -13,13 +13,16 @@ viz_component("host") {
"client_frame_sink_video_capturer.h",
"hit_test/hit_test_query.cc",
"hit_test/hit_test_query.h",
+ "hit_test/hit_test_region_observer.h",
+ "host_display_client.cc",
+ "host_display_client.h",
"host_frame_sink_client.h",
"host_frame_sink_manager.cc",
"host_frame_sink_manager.h",
+ "host_gpu_memory_buffer_manager.cc",
+ "host_gpu_memory_buffer_manager.h",
"renderer_settings_creation.cc",
"renderer_settings_creation.h",
- "server_gpu_memory_buffer_manager.cc",
- "server_gpu_memory_buffer_manager.h",
"viz_host_export.h",
]
@@ -51,6 +54,10 @@ viz_component("host") {
"//services/viz/public/interfaces",
"//ui/gfx/geometry",
]
+
+ if (is_mac) {
+ deps += [ "//ui/accelerated_widget_mac" ]
+ }
}
viz_source_set("unit_tests") {
@@ -59,7 +66,7 @@ viz_source_set("unit_tests") {
sources = [
"hit_test/hit_test_query_unittest.cc",
"host_frame_sink_manager_unittest.cc",
- "server_gpu_memory_buffer_manager_unittest.cc",
+ "host_gpu_memory_buffer_manager_unittest.cc",
]
deps = [
@@ -91,6 +98,5 @@ fuzzer_test("hit_test_query_fuzzer") {
":host",
"//base/test:test_support",
"//components/viz/test:test_support",
- "//mojo/edk",
]
}
diff --git a/chromium/components/viz/host/DEPS b/chromium/components/viz/host/DEPS
index 83389e05a38..c24c60614dd 100644
--- a/chromium/components/viz/host/DEPS
+++ b/chromium/components/viz/host/DEPS
@@ -15,6 +15,7 @@ include_rules = [
"+services/viz/public/interfaces/hit_test",
"+skia",
"+third_party/skia",
+ "+ui/accelerated_widget_mac",
]
specific_include_rules = {
@@ -24,7 +25,4 @@ specific_include_rules = {
"+components/viz/service/frame_sinks/frame_sink_manager_impl.h",
"+components/viz/service/surfaces/surface_manager.h",
],
- ".*_unittest\.cc": [
- "+components/viz/test",
- ]
}
diff --git a/chromium/components/viz/host/client_frame_sink_video_capturer.cc b/chromium/components/viz/host/client_frame_sink_video_capturer.cc
index 331fa0755ba..433c6d79213 100644
--- a/chromium/components/viz/host/client_frame_sink_video_capturer.cc
+++ b/chromium/components/viz/host/client_frame_sink_video_capturer.cc
@@ -4,6 +4,8 @@
#include "components/viz/host/client_frame_sink_video_capturer.h"
+#include <utility>
+
namespace viz {
namespace {
@@ -57,7 +59,7 @@ void ClientFrameSinkVideoCapturer::SetAutoThrottlingEnabled(bool enabled) {
}
void ClientFrameSinkVideoCapturer::ChangeTarget(
- const FrameSinkId& frame_sink_id) {
+ const base::Optional<FrameSinkId>& frame_sink_id) {
target_ = frame_sink_id;
capturer_->ChangeTarget(frame_sink_id);
}
@@ -109,11 +111,6 @@ void ClientFrameSinkVideoCapturer::OnFrameCaptured(
update_rect, content_rect, std::move(callbacks));
}
-void ClientFrameSinkVideoCapturer::OnTargetLost(
- const FrameSinkId& frame_sink_id) {
- consumer_->OnTargetLost(frame_sink_id);
-}
-
void ClientFrameSinkVideoCapturer::OnStopped() {
consumer_->OnStopped();
}
@@ -137,7 +134,7 @@ void ClientFrameSinkVideoCapturer::EstablishConnection() {
if (auto_throttling_enabled_)
capturer_->SetAutoThrottlingEnabled(*auto_throttling_enabled_);
if (target_)
- capturer_->ChangeTarget(*target_);
+ capturer_->ChangeTarget(target_);
if (is_started_)
StartInternal();
}
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 dd6c5fb4f18..71ad5a567aa 100644
--- a/chromium/components/viz/host/client_frame_sink_video_capturer.h
+++ b/chromium/components/viz/host/client_frame_sink_video_capturer.h
@@ -17,8 +17,12 @@
namespace viz {
// Client library for using FrameSinkVideoCapturer. Clients should use this
-// class instead of talking directly to FrameSinkVideoCapturer in order to
-// survive Viz crashes.
+// instead of talking directly to FrameSinkVideoCapturer in order to survive Viz
+// crashes.
+//
+// An instance of ClientFrameSinkVideoCapturer must only be used in the same
+// sequence (e.g., single-threaded).
+//
// TODO(samans): Move this class and all its dependencies to the client
// directory.
class VIZ_HOST_EXPORT ClientFrameSinkVideoCapturer
@@ -38,7 +42,7 @@ class VIZ_HOST_EXPORT ClientFrameSinkVideoCapturer
const gfx::Size& max_size,
bool use_fixed_aspect_ratio);
void SetAutoThrottlingEnabled(bool enabled);
- void ChangeTarget(const FrameSinkId& frame_sink_id);
+ void ChangeTarget(const base::Optional<FrameSinkId>& frame_sink_id);
void Stop();
void RequestRefreshFrame();
@@ -77,7 +81,6 @@ class VIZ_HOST_EXPORT ClientFrameSinkVideoCapturer
const gfx::Rect& update_rect,
const gfx::Rect& content_rect,
mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) final;
- void OnTargetLost(const FrameSinkId& frame_sink_id) final;
void OnStopped() final;
// Establishes connection to FrameSinkVideoCapturer and sends the existing
diff --git a/chromium/components/viz/host/hit_test/DEPS b/chromium/components/viz/host/hit_test/DEPS
index a5b0089ab65..0c23a8bf40b 100644
--- a/chromium/components/viz/host/hit_test/DEPS
+++ b/chromium/components/viz/host/hit_test/DEPS
@@ -6,6 +6,6 @@ include_rules = [
specific_include_rules = {
"hit_test_query_fuzzer.cc": [
- "+mojo/edk/embedder",
+ "+mojo/core/embedder",
]
}
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 15d9f2c741c..a6128d3ee17 100644
--- a/chromium/components/viz/host/hit_test/hit_test_query.cc
+++ b/chromium/components/viz/host/hit_test/hit_test_query.cc
@@ -5,6 +5,7 @@
#include "components/viz/host/hit_test/hit_test_query.h"
#include "base/metrics/histogram_macros.h"
+#include "base/timer/elapsed_timer.h"
#include "components/viz/common/hit_test/hit_test_region_list.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/rect_f.h"
@@ -45,13 +46,17 @@ void HitTestQuery::OnAggregatedHitTestRegionListUpdated(
Target HitTestQuery::FindTargetForLocation(
EventSource event_source,
const gfx::PointF& location_in_root) const {
- SCOPED_UMA_HISTOGRAM_TIMER("Event.VizHitTest.TargetTime");
+ base::ElapsedTimer target_timer;
Target target;
if (!hit_test_data_size_)
return target;
FindTargetInRegionForLocation(event_source, location_in_root, 0, &target);
+ UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES("Event.VizHitTest.TargetTimeUs",
+ target_timer.Elapsed(),
+ base::TimeDelta::FromMicroseconds(1),
+ base::TimeDelta::FromSeconds(10), 50);
return target;
}
@@ -60,7 +65,7 @@ bool HitTestQuery::TransformLocationForTarget(
const std::vector<FrameSinkId>& target_ancestors,
const gfx::PointF& location_in_root,
gfx::PointF* transformed_location) const {
- SCOPED_UMA_HISTOGRAM_TIMER("Event.VizHitTest.TransformTime");
+ base::ElapsedTimer transform_timer;
if (!hit_test_data_size_)
return false;
@@ -74,6 +79,10 @@ bool HitTestQuery::TransformLocationForTarget(
// TODO(riajiang): Cache the matrix product such that the transform can be
// done immediately. crbug/758062.
*transformed_location = location_in_root;
+ UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES("Event.VizHitTest.TransformTimeUs",
+ transform_timer.Elapsed(),
+ base::TimeDelta::FromMicroseconds(1),
+ base::TimeDelta::FromSeconds(10), 50);
return TransformLocationForTargetRecursively(event_source, target_ancestors,
target_ancestors.size() - 1, 0,
transformed_location);
@@ -87,6 +96,14 @@ bool HitTestQuery::GetTransformToTarget(const FrameSinkId& target,
return GetTransformToTargetRecursively(target, 0, transform);
}
+bool HitTestQuery::ContainsFrameSinkId(const FrameSinkId& frame_sink_id) const {
+ for (auto& it : hit_test_data_) {
+ if (it.frame_sink_id == frame_sink_id)
+ return true;
+ }
+ return false;
+}
+
bool HitTestQuery::FindTargetInRegionForLocation(
EventSource event_source,
const gfx::PointF& location_in_parent,
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 a722182a0a3..ae625d0b0f8 100644
--- a/chromium/components/viz/host/hit_test/hit_test_query.h
+++ b/chromium/components/viz/host/hit_test/hit_test_query.h
@@ -92,6 +92,9 @@ class VIZ_HOST_EXPORT HitTestQuery {
bool GetTransformToTarget(const FrameSinkId& target,
gfx::Transform* transform) const;
+ // Returns whether hit test data for |frame_sink_id| is available.
+ bool ContainsFrameSinkId(const FrameSinkId& frame_sink_id) const;
+
private:
// Helper function to find |target| for |location_in_parent| in the
// |region_index|, returns true if a target is found and false otherwise.
diff --git a/chromium/components/viz/host/hit_test/hit_test_region_observer.h b/chromium/components/viz/host/hit_test/hit_test_region_observer.h
new file mode 100644
index 00000000000..0381ac45c93
--- /dev/null
+++ b/chromium/components/viz/host/hit_test/hit_test_region_observer.h
@@ -0,0 +1,34 @@
+// 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_HOST_HIT_TEST_HIT_TEST_REGION_OBSERVER_H_
+#define COMPONENTS_VIZ_HOST_HIT_TEST_HIT_TEST_REGION_OBSERVER_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "components/viz/common/hit_test/aggregated_hit_test_region.h"
+#include "components/viz/common/surfaces/frame_sink_id.h"
+
+namespace viz {
+
+// An observer to be used within a Viz Host. Allows for notifications of when
+// updated hit test regions have been provided. See HostFrameSinkManager for
+// usage.
+class HitTestRegionObserver {
+ public:
+ HitTestRegionObserver() = default;
+ virtual ~HitTestRegionObserver() = default;
+
+ virtual void OnAggregatedHitTestRegionListUpdated(
+ const FrameSinkId& frame_sink_id,
+ const std::vector<AggregatedHitTestRegion>& hit_test_data) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HitTestRegionObserver);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_HOST_HIT_TEST_HIT_TEST_REGION_OBSERVER_H_
diff --git a/chromium/components/viz/host/host_display_client.cc b/chromium/components/viz/host/host_display_client.cc
new file mode 100644
index 00000000000..bdd1e8bde77
--- /dev/null
+++ b/chromium/components/viz/host/host_display_client.cc
@@ -0,0 +1,65 @@
+// 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/host/host_display_client.h"
+
+#if defined(OS_MACOSX)
+#include "ui/accelerated_widget_mac/ca_layer_frame_sink.h"
+#endif
+
+#if defined(OS_WIN)
+#include <windows.h>
+
+#include "components/viz/common/display/use_layered_window.h"
+#include "components/viz/host/layered_window_updater_impl.h"
+#include "ui/base/win/internal_constants.h"
+#endif
+
+namespace viz {
+
+HostDisplayClient::HostDisplayClient(gfx::AcceleratedWidget widget)
+ : binding_(this) {
+#if defined(OS_MACOSX) || defined(OS_WIN)
+ widget_ = widget;
+#endif
+}
+
+HostDisplayClient::~HostDisplayClient() = default;
+
+mojom::DisplayClientPtr HostDisplayClient::GetBoundPtr(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ mojom::DisplayClientPtr ptr;
+ binding_.Bind(mojo::MakeRequest(&ptr), task_runner);
+ return ptr;
+}
+
+void HostDisplayClient::DidSwapAfterSnapshotRequestReceived(
+ const std::vector<ui::LatencyInfo>& latency_info) {}
+
+#if defined(OS_MACOSX)
+void HostDisplayClient::OnDisplayReceivedCALayerParams(
+ const gfx::CALayerParams& ca_layer_params) {
+ ui::CALayerFrameSink* ca_layer_frame_sink =
+ ui::CALayerFrameSink::FromAcceleratedWidget(widget_);
+ if (ca_layer_frame_sink)
+ ca_layer_frame_sink->UpdateCALayerTree(ca_layer_params);
+ else
+ DLOG(WARNING) << "Received frame for non-existent widget.";
+}
+#endif
+
+#if defined(OS_WIN)
+void HostDisplayClient::CreateLayeredWindowUpdater(
+ mojom::LayeredWindowUpdaterRequest request) {
+ if (!NeedsToUseLayerWindow(widget_)) {
+ DLOG(ERROR) << "HWND shouldn't be using a layered window";
+ return;
+ }
+
+ layered_window_updater_ =
+ std::make_unique<LayeredWindowUpdaterImpl>(widget_, std::move(request));
+}
+#endif
+
+} // namespace viz
diff --git a/chromium/components/viz/host/host_display_client.h b/chromium/components/viz/host/host_display_client.h
new file mode 100644
index 00000000000..7fd5a4a9a6e
--- /dev/null
+++ b/chromium/components/viz/host/host_display_client.h
@@ -0,0 +1,62 @@
+// 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_HOST_HOST_DISPLAY_CLIENT_H_
+#define COMPONENTS_VIZ_HOST_HOST_DISPLAY_CLIENT_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "build/build_config.h"
+#include "components/viz/host/viz_host_export.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/viz/privileged/interfaces/compositing/display_private.mojom.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace viz {
+
+class LayeredWindowUpdaterImpl;
+
+// mojom::DisplayClient implementation that relays calls to platform specific
+// functions.
+class VIZ_HOST_EXPORT HostDisplayClient : public mojom::DisplayClient {
+ public:
+ explicit HostDisplayClient(gfx::AcceleratedWidget widget);
+ ~HostDisplayClient() override;
+
+ mojom::DisplayClientPtr GetBoundPtr(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+ private:
+ // mojom::DisplayClient implementation:
+ void DidSwapAfterSnapshotRequestReceived(
+ const std::vector<ui::LatencyInfo>& latency_info) override;
+
+#if defined(OS_MACOSX)
+ void OnDisplayReceivedCALayerParams(
+ const gfx::CALayerParams& ca_layer_params) override;
+#endif
+
+#if defined(OS_WIN)
+ void CreateLayeredWindowUpdater(
+ mojom::LayeredWindowUpdaterRequest request) override;
+#endif
+
+ mojo::Binding<mojom::DisplayClient> binding_;
+#if defined(OS_MACOSX) || defined(OS_WIN)
+ gfx::AcceleratedWidget widget_;
+#endif
+
+#if defined(OS_WIN)
+ std::unique_ptr<LayeredWindowUpdaterImpl> layered_window_updater_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(HostDisplayClient);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_HOST_HOST_DISPLAY_CLIENT_H_
diff --git a/chromium/components/viz/host/host_frame_sink_manager.cc b/chromium/components/viz/host/host_frame_sink_manager.cc
index 5910a73f6da..53ccb9f26f8 100644
--- a/chromium/components/viz/host/host_frame_sink_manager.cc
+++ b/chromium/components/viz/host/host_frame_sink_manager.cc
@@ -67,7 +67,7 @@ void HostFrameSinkManager::RegisterFrameSinkId(const FrameSinkId& frame_sink_id,
FrameSinkData& data = frame_sink_data_map_[frame_sink_id];
DCHECK(!data.IsFrameSinkRegistered());
- DCHECK(!data.HasCompositorFrameSinkData());
+ DCHECK(!data.has_created_compositor_frame_sink);
data.client = client;
frame_sink_manager_->RegisterFrameSinkId(frame_sink_id);
}
@@ -131,7 +131,6 @@ void HostFrameSinkManager::CreateRootCompositorFrameSink(
FrameSinkId frame_sink_id = params->frame_sink_id;
FrameSinkData& data = frame_sink_data_map_[frame_sink_id];
DCHECK(data.IsFrameSinkRegistered());
- DCHECK(!data.support);
// If GL context is lost a new CompositorFrameSink will be created. Destroy
// the old CompositorFrameSink first.
@@ -154,7 +153,6 @@ void HostFrameSinkManager::CreateCompositorFrameSink(
mojom::CompositorFrameSinkClientPtr client) {
FrameSinkData& data = frame_sink_data_map_[frame_sink_id];
DCHECK(data.IsFrameSinkRegistered());
- DCHECK(!data.support);
// If GL context is lost a new CompositorFrameSink will be created. Destroy
// the old CompositorFrameSink first.
@@ -279,6 +277,16 @@ void HostFrameSinkManager::RequestCopyOfOutput(
frame_sink_manager_->RequestCopyOfOutput(surface_id, std::move(request));
}
+void HostFrameSinkManager::AddHitTestRegionObserver(
+ HitTestRegionObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void HostFrameSinkManager::RemoveHitTestRegionObserver(
+ HitTestRegionObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
std::unique_ptr<CompositorFrameSinkSupport>
HostFrameSinkManager::CreateCompositorFrameSinkSupport(
mojom::CompositorFrameSinkClient* client,
@@ -289,16 +297,12 @@ HostFrameSinkManager::CreateCompositorFrameSinkSupport(
FrameSinkData& data = frame_sink_data_map_[frame_sink_id];
DCHECK(data.IsFrameSinkRegistered());
- DCHECK(!data.HasCompositorFrameSinkData());
+ DCHECK(!data.has_created_compositor_frame_sink);
auto support = std::make_unique<CompositorFrameSinkSupport>(
client, frame_sink_manager_impl_, frame_sink_id, is_root,
needs_sync_points);
- support->SetDestructionCallback(
- base::BindOnce(&HostFrameSinkManager::CompositorFrameSinkSupportDestroyed,
- weak_ptr_factory_.GetWeakPtr(), frame_sink_id));
- data.support = support.get();
data.is_root = is_root;
if (is_root)
@@ -307,16 +311,6 @@ HostFrameSinkManager::CreateCompositorFrameSinkSupport(
return support;
}
-void HostFrameSinkManager::CompositorFrameSinkSupportDestroyed(
- const FrameSinkId& frame_sink_id) {
- auto iter = frame_sink_data_map_.find(frame_sink_id);
- DCHECK(iter != frame_sink_data_map_.end());
-
- iter->second.support = nullptr;
- if (iter->second.IsEmpty())
- frame_sink_data_map_.erase(iter);
-}
-
void HostFrameSinkManager::PerformAssignTemporaryReference(
const SurfaceId& surface_id) {
auto iter = frame_sink_data_map_.find(surface_id.frame_sink_id());
@@ -432,6 +426,11 @@ void HostFrameSinkManager::OnAggregatedHitTestRegionListUpdated(
return;
iter->second->OnAggregatedHitTestRegionListUpdated(hit_test_data);
+
+ // Ensure that HitTestQuery are updated so that observers are not working with
+ // stale data.
+ for (HitTestRegionObserver& observer : observers_)
+ observer.OnAggregatedHitTestRegionListUpdated(frame_sink_id, hit_test_data);
}
HostFrameSinkManager::FrameSinkData::FrameSinkData() = default;
diff --git a/chromium/components/viz/host/host_frame_sink_manager.h b/chromium/components/viz/host/host_frame_sink_manager.h
index afe8caa1810..1b6a6aca6d5 100644
--- a/chromium/components/viz/host/host_frame_sink_manager.h
+++ b/chromium/components/viz/host/host_frame_sink_manager.h
@@ -19,6 +19,7 @@
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/host/client_frame_sink_video_capturer.h"
#include "components/viz/host/hit_test/hit_test_query.h"
+#include "components/viz/host/hit_test/hit_test_region_observer.h"
#include "components/viz/host/host_frame_sink_client.h"
#include "components/viz/host/viz_host_export.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support_manager.h"
@@ -162,6 +163,11 @@ class VIZ_HOST_EXPORT HostFrameSinkManager
void RequestCopyOfOutput(const SurfaceId& surface_id,
std::unique_ptr<CopyOutputRequest> request);
+ // Add/Remove an observer to receive notifications of when the host receives
+ // new hit test data.
+ void AddHitTestRegionObserver(HitTestRegionObserver* observer);
+ void RemoveHitTestRegionObserver(HitTestRegionObserver* observer);
+
// CompositorFrameSinkSupportManager:
std::unique_ptr<CompositorFrameSinkSupport> CreateCompositorFrameSinkSupport(
mojom::CompositorFrameSinkClient* client,
@@ -183,13 +189,9 @@ class VIZ_HOST_EXPORT HostFrameSinkManager
bool IsFrameSinkRegistered() const { return client != nullptr; }
- bool HasCompositorFrameSinkData() const {
- return has_created_compositor_frame_sink || support;
- }
-
// Returns true if there is nothing in FrameSinkData and it can be deleted.
bool IsEmpty() const {
- return !IsFrameSinkRegistered() && !HasCompositorFrameSinkData() &&
+ return !IsFrameSinkRegistered() && !has_created_compositor_frame_sink &&
parents.empty() && children.empty();
}
@@ -210,9 +212,6 @@ class VIZ_HOST_EXPORT HostFrameSinkManager
// will always be false if not using Mojo.
bool has_created_compositor_frame_sink = false;
- // This will be null if using Mojo.
- CompositorFrameSinkSupport* support = nullptr;
-
// Track frame sink hierarchy in both directions.
std::vector<FrameSinkId> parents;
std::vector<FrameSinkId> children;
@@ -221,12 +220,8 @@ class VIZ_HOST_EXPORT HostFrameSinkManager
DISALLOW_COPY_AND_ASSIGN(FrameSinkData);
};
- // Provided as a callback to clear state when a CompositorFrameSinkSupport is
- // destroyed.
- void CompositorFrameSinkSupportDestroyed(const FrameSinkId& frame_sink_id);
-
// Assigns the temporary reference to the frame sink that is expected to
- // embeded |surface_id|, otherwise drops the temporary reference.
+ // embed |surface_id|, otherwise drops the temporary reference.
void PerformAssignTemporaryReference(const SurfaceId& surface_id);
// Handles connection loss to |frame_sink_manager_ptr_|. This should only
@@ -277,6 +272,10 @@ class VIZ_HOST_EXPORT HostFrameSinkManager
DisplayHitTestQueryMap display_hit_test_query_;
+ // TODO(jonross): Separate out all hit testing work into its own separate
+ // class.
+ base::ObserverList<HitTestRegionObserver> observers_;
+
base::WeakPtrFactory<HostFrameSinkManager> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(HostFrameSinkManager);
diff --git a/chromium/components/viz/host/host_frame_sink_manager_unittest.cc b/chromium/components/viz/host/host_frame_sink_manager_unittest.cc
index 0e0bc1f62af..0f91e10691b 100644
--- a/chromium/components/viz/host/host_frame_sink_manager_unittest.cc
+++ b/chromium/components/viz/host/host_frame_sink_manager_unittest.cc
@@ -13,6 +13,7 @@
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "components/viz/common/surfaces/surface_info.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.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/surfaces/surface_manager.h"
@@ -69,7 +70,8 @@ struct RootCompositorFrameSinkData {
// A mock implementation of mojom::FrameSinkManager.
class MockFrameSinkManagerImpl : public FrameSinkManagerImpl {
public:
- MockFrameSinkManagerImpl() = default;
+ explicit MockFrameSinkManagerImpl(SharedBitmapManager* shared_bitmap_manager)
+ : FrameSinkManagerImpl(shared_bitmap_manager) {}
~MockFrameSinkManagerImpl() override = default;
// mojom::FrameSinkManager:
@@ -148,6 +150,7 @@ class HostFrameSinkManagerTestBase : public testing::Test {
}
protected:
+ ServerSharedBitmapManager shared_bitmap_manager_;
std::unique_ptr<HostFrameSinkManager> host_manager_;
std::unique_ptr<testing::NiceMock<MockFrameSinkManagerImpl>> manager_impl_;
@@ -171,7 +174,8 @@ class HostFrameSinkManagerLocalTest : public HostFrameSinkManagerTestBase {
// testing::Test:
void SetUp() override {
manager_impl_ =
- std::make_unique<testing::NiceMock<MockFrameSinkManagerImpl>>();
+ std::make_unique<testing::NiceMock<MockFrameSinkManagerImpl>>(
+ &shared_bitmap_manager_);
host_manager_ = std::make_unique<HostFrameSinkManager>();
manager_impl_->SetLocalClient(host_manager_.get());
@@ -195,7 +199,8 @@ class HostFrameSinkManagerRemoteTest : public HostFrameSinkManagerTestBase {
DCHECK(!manager_impl_);
manager_impl_ =
- std::make_unique<testing::NiceMock<MockFrameSinkManagerImpl>>();
+ std::make_unique<testing::NiceMock<MockFrameSinkManagerImpl>>(
+ &shared_bitmap_manager_);
mojom::FrameSinkManagerPtr frame_sink_manager;
mojom::FrameSinkManagerRequest frame_sink_manager_request =
@@ -278,6 +283,7 @@ TEST_F(HostFrameSinkManagerLocalTest, CommunicateFrameToken) {
CompositorFrame compositor_frame = CompositorFrameBuilder()
.AddDefaultRenderPass()
.SetFrameToken(frame_token1)
+ .SetSendFrameTokenToEmbedder(true)
.SetActivationDependencies({child_id1})
.Build();
support->SubmitCompositorFrame(parent_id1.local_surface_id(),
diff --git a/chromium/components/viz/host/host_gpu_memory_buffer_manager.cc b/chromium/components/viz/host/host_gpu_memory_buffer_manager.cc
new file mode 100644
index 00000000000..04e56adac25
--- /dev/null
+++ b/chromium/components/viz/host/host_gpu_memory_buffer_manager.cc
@@ -0,0 +1,337 @@
+// 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/host/host_gpu_memory_buffer_manager.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "gpu/ipc/common/gpu_memory_buffer_impl.h"
+#include "gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
+#include "services/viz/privileged/interfaces/gl/gpu_service.mojom.h"
+#include "ui/gfx/buffer_format_util.h"
+
+namespace viz {
+
+namespace {
+
+void OnGpuMemoryBufferDestroyed(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ const gpu::GpuMemoryBufferImpl::DestructionCallback& callback,
+ const gpu::SyncToken& sync_token) {
+ task_runner->PostTask(FROM_HERE, base::BindOnce(callback, sync_token));
+}
+
+} // namespace
+
+HostGpuMemoryBufferManager::PendingBufferInfo::PendingBufferInfo() = default;
+HostGpuMemoryBufferManager::PendingBufferInfo::PendingBufferInfo(
+ PendingBufferInfo&&) = default;
+HostGpuMemoryBufferManager::PendingBufferInfo::~PendingBufferInfo() = default;
+
+HostGpuMemoryBufferManager::AllocatedBufferInfo::AllocatedBufferInfo() =
+ default;
+HostGpuMemoryBufferManager::AllocatedBufferInfo::~AllocatedBufferInfo() =
+ default;
+
+HostGpuMemoryBufferManager::HostGpuMemoryBufferManager(
+ int client_id,
+ std::unique_ptr<gpu::GpuMemoryBufferSupport> gpu_memory_buffer_support,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : client_id_(client_id),
+ gpu_memory_buffer_support_(std::move(gpu_memory_buffer_support)),
+ native_configurations_(gpu::GetNativeGpuMemoryBufferConfigurations(
+ gpu_memory_buffer_support_.get())),
+ task_runner_(std::move(task_runner)),
+ weak_factory_(this) {}
+
+HostGpuMemoryBufferManager::~HostGpuMemoryBufferManager() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+}
+
+void HostGpuMemoryBufferManager::SetGpuService(mojom::GpuService* gpu_service) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ gpu_service_ = gpu_service;
+ gpu_service_version_++;
+
+ // Drop allocated buffers.
+ allocated_buffers_.clear();
+
+ // Retry requesting pending buffer allocations. If the new GPU service is not
+ // nullptr, requests would be sent immediately; otherwise, they'll be cached
+ // and sent next time a GPU service is available.
+ auto pending_buffers = std::move(pending_buffers_);
+ pending_buffers_.clear();
+ for (auto& client_pair : pending_buffers) {
+ for (auto& buffer_pair : client_pair.second) {
+ auto& buffer = buffer_pair.second;
+ AllocateGpuMemoryBuffer(
+ buffer_pair.first, client_pair.first, buffer.size, buffer.format,
+ buffer.usage, buffer.surface_handle, std::move(buffer.callback));
+ }
+ }
+}
+
+void HostGpuMemoryBufferManager::DropPendingAllocationRequests() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ DCHECK(!gpu_service_);
+ for (auto& client_pair : pending_buffers_) {
+ for (auto& buffer_pair : client_pair.second) {
+ auto& buffer = buffer_pair.second;
+ std::move(buffer.callback).Run(gfx::GpuMemoryBufferHandle());
+ }
+ }
+ pending_buffers_.clear();
+}
+
+void HostGpuMemoryBufferManager::DestroyGpuMemoryBuffer(
+ gfx::GpuMemoryBufferId id,
+ int client_id,
+ const gpu::SyncToken& sync_token) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ auto client_iter = allocated_buffers_.find(client_id);
+ if (client_iter == allocated_buffers_.end())
+ return;
+ auto& buffers = client_iter->second;
+ auto buffer_iter = buffers.find(id);
+ if (buffer_iter == buffers.end())
+ return;
+ DCHECK_NE(gfx::EMPTY_BUFFER, buffer_iter->second.type);
+ if (buffer_iter->second.type != gfx::SHARED_MEMORY_BUFFER)
+ gpu_service_->DestroyGpuMemoryBuffer(id, client_id, sync_token);
+ buffers.erase(buffer_iter);
+}
+
+void HostGpuMemoryBufferManager::DestroyAllGpuMemoryBufferForClient(
+ int client_id) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ auto client_iter = allocated_buffers_.find(client_id);
+ if (client_iter != allocated_buffers_.end()) {
+ auto& buffers = client_iter->second;
+ for (const auto& pair : buffers) {
+ DCHECK_NE(gfx::EMPTY_BUFFER, pair.second.type);
+ if (pair.second.type != gfx::SHARED_MEMORY_BUFFER) {
+ gpu_service_->DestroyGpuMemoryBuffer(pair.first, client_id,
+ gpu::SyncToken());
+ }
+ }
+ allocated_buffers_.erase(client_iter);
+ }
+ auto pending_client_iter = pending_buffers_.find(client_id);
+ if (pending_client_iter != pending_buffers_.end()) {
+ auto& buffers = pending_client_iter->second;
+ for (auto& pair : buffers)
+ std::move(pair.second.callback).Run(gfx::GpuMemoryBufferHandle());
+ pending_buffers_.erase(pending_client_iter);
+ }
+}
+
+void HostGpuMemoryBufferManager::AllocateGpuMemoryBuffer(
+ gfx::GpuMemoryBufferId id,
+ int client_id,
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ gpu::SurfaceHandle surface_handle,
+ base::OnceCallback<void(gfx::GpuMemoryBufferHandle)> callback) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ if (!weak_ptr_)
+ weak_ptr_ = weak_factory_.GetWeakPtr();
+ if (gpu_memory_buffer_support_->GetNativeGpuMemoryBufferType() !=
+ gfx::EMPTY_BUFFER) {
+ const bool is_native = native_configurations_.find(std::make_pair(
+ format, usage)) != native_configurations_.end();
+ if (is_native) {
+ PendingBufferInfo buffer_info;
+ buffer_info.size = size;
+ buffer_info.format = format;
+ buffer_info.usage = usage;
+ buffer_info.surface_handle = surface_handle;
+ buffer_info.callback = std::move(callback);
+ pending_buffers_[client_id].insert(
+ std::make_pair(id, std::move(buffer_info)));
+ if (gpu_service_) {
+ gpu_service_->CreateGpuMemoryBuffer(
+ id, size, format, usage, client_id, surface_handle,
+ base::BindOnce(
+ &HostGpuMemoryBufferManager::OnGpuMemoryBufferAllocated,
+ weak_ptr_, gpu_service_version_, client_id, id));
+ }
+ return;
+ }
+ }
+
+ gfx::GpuMemoryBufferHandle buffer_handle;
+ // The requests are coming in from untrusted clients. So verify that it is
+ // possible to allocate shared memory buffer first.
+ if (gpu::GpuMemoryBufferImplSharedMemory::IsUsageSupported(usage) &&
+ gpu::GpuMemoryBufferImplSharedMemory::IsSizeValidForFormat(size,
+ format)) {
+ buffer_handle = gpu::GpuMemoryBufferImplSharedMemory::CreateGpuMemoryBuffer(
+ id, size, format, usage);
+ AllocatedBufferInfo buffer_info;
+ DCHECK_EQ(gfx::SHARED_MEMORY_BUFFER, buffer_handle.type);
+ buffer_info.type = gfx::SHARED_MEMORY_BUFFER;
+ buffer_info.buffer_size_in_bytes =
+ gfx::BufferSizeForBufferFormat(size, format);
+ buffer_info.shared_memory_guid = buffer_handle.handle.GetGUID();
+ allocated_buffers_[client_id].insert(
+ std::make_pair(buffer_handle.id, buffer_info));
+ }
+
+ task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(std::move(callback), std::move(buffer_handle)));
+}
+
+std::unique_ptr<gfx::GpuMemoryBuffer>
+HostGpuMemoryBufferManager::CreateGpuMemoryBuffer(
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ gpu::SurfaceHandle surface_handle) {
+ gfx::GpuMemoryBufferId id(next_gpu_memory_id_++);
+ gfx::GpuMemoryBufferHandle handle;
+ base::WaitableEvent wait_event(
+ base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ DCHECK(!task_runner_->BelongsToCurrentThread());
+ auto reply_callback = base::BindOnce(
+ [](gfx::GpuMemoryBufferHandle* handle, base::WaitableEvent* wait_event,
+ gfx::GpuMemoryBufferHandle allocated_buffer_handle) {
+ *handle = std::move(allocated_buffer_handle);
+ wait_event->Signal();
+ },
+ &handle, &wait_event);
+ // We block with a WaitableEvent until the callback is run. So using
+ // base::Unretained() is safe here.
+ auto allocate_callback =
+ base::BindOnce(&HostGpuMemoryBufferManager::AllocateGpuMemoryBuffer,
+ base::Unretained(this), id, client_id_, size, format,
+ usage, surface_handle, std::move(reply_callback));
+ task_runner_->PostTask(FROM_HERE, std::move(allocate_callback));
+ base::ScopedAllowBaseSyncPrimitives allow_base_sync_primitives;
+ wait_event.Wait();
+ if (handle.is_null())
+ return nullptr;
+ // The destruction callback can be called on any thread. So use an
+ // intermediate callback here as the destruction callback, which bounces off
+ // onto the |task_runner_| thread to do the real work.
+ return gpu_memory_buffer_support_->CreateGpuMemoryBufferImplFromHandle(
+ std::move(handle), size, format, usage,
+ base::BindRepeating(
+ &OnGpuMemoryBufferDestroyed, task_runner_,
+ base::BindRepeating(
+ &HostGpuMemoryBufferManager::DestroyGpuMemoryBuffer, weak_ptr_,
+ id, client_id_)));
+}
+
+void HostGpuMemoryBufferManager::SetDestructionSyncToken(
+ gfx::GpuMemoryBuffer* buffer,
+ const gpu::SyncToken& sync_token) {
+ static_cast<gpu::GpuMemoryBufferImpl*>(buffer)->set_destruction_sync_token(
+ sync_token);
+}
+
+bool HostGpuMemoryBufferManager::OnMemoryDump(
+ const base::trace_event::MemoryDumpArgs& args,
+ base::trace_event::ProcessMemoryDump* pmd) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ for (const auto& pair : allocated_buffers_) {
+ int client_id = pair.first;
+ for (const auto& buffer_pair : pair.second) {
+ gfx::GpuMemoryBufferId buffer_id = buffer_pair.first;
+ const AllocatedBufferInfo& buffer_info = buffer_pair.second;
+ base::trace_event::MemoryAllocatorDump* dump =
+ pmd->CreateAllocatorDump(base::StringPrintf(
+ "gpumemorybuffer/client_%d/buffer_%d", client_id, buffer_id.id));
+ if (!dump)
+ return false;
+ dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+ base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+ buffer_info.buffer_size_in_bytes);
+
+ // Create the cross-process ownership edge. If the client creates a
+ // corresponding dump for the same buffer, this will avoid to
+ // double-count them in tracing. If, instead, no other process will emit a
+ // dump with the same guid, the segment will be accounted to the browser.
+ uint64_t client_tracing_process_id = ClientIdToTracingId(client_id);
+
+ if (buffer_info.type == gfx::SHARED_MEMORY_BUFFER) {
+ pmd->CreateSharedMemoryOwnershipEdge(
+ dump->guid(), buffer_info.shared_memory_guid, 0 /* importance */);
+ } else {
+ auto shared_buffer_guid = gfx::GetGenericSharedGpuMemoryGUIDForTracing(
+ client_tracing_process_id, buffer_id);
+ pmd->CreateSharedGlobalAllocatorDump(shared_buffer_guid);
+ pmd->AddOwnershipEdge(dump->guid(), shared_buffer_guid);
+ }
+ }
+ }
+ return true;
+}
+
+uint64_t HostGpuMemoryBufferManager::ClientIdToTracingId(int client_id) const {
+ if (client_id == client_id_) {
+ return base::trace_event::MemoryDumpManager::GetInstance()
+ ->GetTracingProcessId();
+ }
+ // TODO(sad|ssid): Find a better way once https://crbug.com/661257 is
+ // resolved. The hash value is incremented so that the tracing id is never
+ // equal to MemoryDumpManager::kInvalidTracingProcessId.
+ return static_cast<uint64_t>(base::Hash(&client_id, sizeof(client_id))) + 1;
+}
+
+void HostGpuMemoryBufferManager::OnGpuMemoryBufferAllocated(
+ int gpu_service_version,
+ int client_id,
+ gfx::GpuMemoryBufferId id,
+ gfx::GpuMemoryBufferHandle handle) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ // If the current GPU service is different from the one that alloacted the
+ // buffer, the buffer should be considered as stale.
+ bool stale = gpu_service_version_ != gpu_service_version;
+
+ auto client_iter = pending_buffers_.find(client_id);
+ if (client_iter == pending_buffers_.end()) {
+ // The client has been destroyed since the allocation request was made. The
+ // callback is already called with null handle.
+ if (!handle.is_null() && !stale) {
+ gpu_service_->DestroyGpuMemoryBuffer(handle.id, client_id,
+ gpu::SyncToken());
+ }
+ return;
+ }
+
+ auto buffer_iter = client_iter->second.find(id);
+ DCHECK(buffer_iter != client_iter->second.end());
+ PendingBufferInfo pending_buffer = std::move(buffer_iter->second);
+ client_iter->second.erase(buffer_iter);
+
+ if (stale) {
+ // Try re-allocating buffer on the new GPU service.
+ AllocateGpuMemoryBuffer(id, client_id, pending_buffer.size,
+ pending_buffer.format, pending_buffer.usage,
+ pending_buffer.surface_handle,
+ std::move(pending_buffer.callback));
+ return;
+ }
+
+ if (!handle.is_null()) {
+ DCHECK(handle.id == id);
+
+ AllocatedBufferInfo buffer_info;
+ buffer_info.type = handle.type;
+ buffer_info.buffer_size_in_bytes = gfx::BufferSizeForBufferFormat(
+ pending_buffer.size, pending_buffer.format);
+ allocated_buffers_[client_id].insert(std::make_pair(id, buffer_info));
+ }
+ std::move(pending_buffer.callback).Run(std::move(handle));
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/host/host_gpu_memory_buffer_manager.h b/chromium/components/viz/host/host_gpu_memory_buffer_manager.h
new file mode 100644
index 00000000000..0519ab94c1d
--- /dev/null
+++ b/chromium/components/viz/host/host_gpu_memory_buffer_manager.h
@@ -0,0 +1,145 @@
+// 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_HOST_HOST_GPU_MEMORY_BUFFER_MANAGER_H_
+#define COMPONENTS_VIZ_HOST_HOST_GPU_MEMORY_BUFFER_MANAGER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "components/viz/host/viz_host_export.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "gpu/ipc/host/gpu_memory_buffer_support.h"
+
+namespace gpu {
+class GpuMemoryBufferSupport;
+}
+
+namespace viz {
+
+namespace mojom {
+class GpuService;
+}
+
+// This GpuMemoryBufferManager implementation is for [de]allocating GPU memory
+// from the GPU process over the mojom.GpuService api.
+class VIZ_HOST_EXPORT HostGpuMemoryBufferManager
+ : public gpu::GpuMemoryBufferManager,
+ public base::trace_event::MemoryDumpProvider {
+ public:
+ // All function of HostGpuMemoryBufferManager must be called the thread
+ // associated with |task_runner|, other than the constructor and the
+ // gpu::GpuMemoryBufferManager implementation (which can be called from any
+ // thread).
+ HostGpuMemoryBufferManager(
+ int client_id,
+ std::unique_ptr<gpu::GpuMemoryBufferSupport> gpu_memory_buffer_support,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+ ~HostGpuMemoryBufferManager() override;
+
+ // This is called whenever GPU service is started, or with nullptr value when
+ // GPU service is shut down (e.g. GPU process crashes). It will invalidate any
+ // allocated buffer. If |gpu_serivce| is non-nullptr, it will retry allocation
+ // requests for pending memory buffers; otherwise, the requests will be cached
+ // and retried when a GPU service is available.
+ void SetGpuService(mojom::GpuService* gpu_service);
+
+ // This is called when no new GPU service is going to be available. It would
+ // drop memory buffer allocation requests and call their callbacks with null
+ // handles indicating failure..
+ void DropPendingAllocationRequests();
+
+ void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+ int client_id,
+ const gpu::SyncToken& sync_token);
+
+ void DestroyAllGpuMemoryBufferForClient(int client_id);
+
+ void AllocateGpuMemoryBuffer(
+ gfx::GpuMemoryBufferId id,
+ int client_id,
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ gpu::SurfaceHandle surface_handle,
+ base::OnceCallback<void(gfx::GpuMemoryBufferHandle)> callback);
+
+ // Overridden from gpu::GpuMemoryBufferManager:
+ std::unique_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBuffer(
+ const gfx::Size& size,
+ gfx::BufferFormat format,
+ gfx::BufferUsage usage,
+ gpu::SurfaceHandle surface_handle) override;
+ void SetDestructionSyncToken(gfx::GpuMemoryBuffer* buffer,
+ const gpu::SyncToken& sync_token) override;
+
+ // Overridden from base::trace_event::MemoryDumpProvider:
+ bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
+ base::trace_event::ProcessMemoryDump* pmd) override;
+
+ private:
+ struct PendingBufferInfo {
+ PendingBufferInfo();
+ PendingBufferInfo(PendingBufferInfo&&);
+ ~PendingBufferInfo();
+
+ gfx::Size size;
+ gfx::BufferFormat format;
+ gfx::BufferUsage usage;
+ gpu::SurfaceHandle surface_handle;
+ base::OnceCallback<void(gfx::GpuMemoryBufferHandle)> callback;
+ };
+ using PendingBuffers =
+ std::unordered_map<gfx::GpuMemoryBufferId,
+ PendingBufferInfo,
+ BASE_HASH_NAMESPACE::hash<gfx::GpuMemoryBufferId>>;
+
+ struct AllocatedBufferInfo {
+ AllocatedBufferInfo();
+ ~AllocatedBufferInfo();
+
+ gfx::GpuMemoryBufferType type = gfx::EMPTY_BUFFER;
+ size_t buffer_size_in_bytes = 0;
+ base::UnguessableToken shared_memory_guid;
+ };
+ using AllocatedBuffers =
+ std::unordered_map<gfx::GpuMemoryBufferId,
+ AllocatedBufferInfo,
+ BASE_HASH_NAMESPACE::hash<gfx::GpuMemoryBufferId>>;
+
+ uint64_t ClientIdToTracingId(int client_id) const;
+ void OnGpuMemoryBufferAllocated(int gpu_service_version,
+ int client_id,
+ gfx::GpuMemoryBufferId id,
+ gfx::GpuMemoryBufferHandle handle);
+
+ mojom::GpuService* gpu_service_ = nullptr;
+
+ // This is incremented every time |gpu_service_| is updated in order check
+ // whether a buffer is allocated by the most current GPU service or not.
+ int gpu_service_version_ = 0;
+
+ const int client_id_;
+ int next_gpu_memory_id_ = 1;
+
+ std::unordered_map<int, PendingBuffers> pending_buffers_;
+ std::unordered_map<int, AllocatedBuffers> allocated_buffers_;
+
+ std::unique_ptr<gpu::GpuMemoryBufferSupport> gpu_memory_buffer_support_;
+
+ const gpu::GpuMemoryBufferConfigurationSet native_configurations_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ base::WeakPtr<HostGpuMemoryBufferManager> weak_ptr_;
+ base::WeakPtrFactory<HostGpuMemoryBufferManager> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(HostGpuMemoryBufferManager);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_HOST_HOST_GPU_MEMORY_BUFFER_MANAGER_H_
diff --git a/chromium/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc b/chromium/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
index 9d3479bd508..d6cc034d588 100644
--- a/chromium/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc
+++ b/chromium/components/viz/host/host_gpu_memory_buffer_manager_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/viz/host/server_gpu_memory_buffer_manager.h"
+#include "components/viz/host/host_gpu_memory_buffer_manager.h"
#include <utility>
@@ -47,7 +47,7 @@ class TestGpuService : public mojom::GpuService {
handle.id = id;
handle.type = gfx::SHARED_MEMORY_BUFFER;
DCHECK(req.callback);
- std::move(req.callback).Run(handle);
+ std::move(req.callback).Run(std::move(handle));
return;
}
}
@@ -113,10 +113,6 @@ class TestGpuService : public mojom::GpuService {
void LoadedShader(const std::string& key, const std::string& data) override {}
- void DestroyingVideoSurface(
- int32_t surface_id,
- DestroyingVideoSurfaceCallback callback) override {}
-
void WakeUpGpu() override {}
void GpuSwitched() override {}
@@ -129,6 +125,12 @@ class TestGpuService : public mojom::GpuService {
void OnForegrounded() override {}
+#if defined(OS_MACOSX)
+ void BeginCATransaction() override {}
+
+ void CommitCATransaction(CommitCATransactionCallback callback) override {}
+#endif
+
void Crash() override {}
void Hang() override {}
@@ -187,41 +189,35 @@ class FakeClientNativePixmapFactory : public gfx::ClientNativePixmapFactory {
} // namespace
-class ServerGpuMemoryBufferManagerTest : public ::testing::Test {
+class HostGpuMemoryBufferManagerTest : public ::testing::Test {
public:
- ServerGpuMemoryBufferManagerTest() = default;
- ~ServerGpuMemoryBufferManagerTest() override = default;
+ HostGpuMemoryBufferManagerTest() = default;
+ ~HostGpuMemoryBufferManagerTest() override = default;
std::unique_ptr<gfx::GpuMemoryBuffer> AllocateGpuMemoryBufferSync(
- ServerGpuMemoryBufferManager* manager) {
+ HostGpuMemoryBufferManager* manager) {
base::Thread diff_thread("TestThread");
diff_thread.Start();
std::unique_ptr<gfx::GpuMemoryBuffer> buffer;
base::RunLoop run_loop;
diff_thread.task_runner()->PostTask(
- FROM_HERE, base::Bind(
- [](ServerGpuMemoryBufferManager* manager,
+ FROM_HERE, base::BindOnce(
+ [](HostGpuMemoryBufferManager* manager,
std::unique_ptr<gfx::GpuMemoryBuffer>* out_buffer,
- const base::Closure& callback) {
+ base::OnceClosure callback) {
*out_buffer = manager->CreateGpuMemoryBuffer(
gfx::Size(64, 64), gfx::BufferFormat::YVU_420,
gfx::BufferUsage::GPU_READ,
gpu::kNullSurfaceHandle);
- callback.Run();
+ std::move(callback).Run();
},
manager, &buffer, run_loop.QuitClosure()));
run_loop.Run();
return buffer;
}
- // ::testing::Test:
- void SetUp() override {
- }
-
- void TearDown() override {}
-
private:
- DISALLOW_COPY_AND_ASSIGN(ServerGpuMemoryBufferManagerTest);
+ DISALLOW_COPY_AND_ASSIGN(HostGpuMemoryBufferManagerTest);
};
std::unique_ptr<gpu::GpuMemoryBufferSupport> MakeGpuMemoryBufferSupport(
@@ -236,7 +232,7 @@ std::unique_ptr<gpu::GpuMemoryBufferSupport> MakeGpuMemoryBufferSupport(
// Tests that allocation requests from a client that goes away before allocation
// completes are cleaned up correctly.
-TEST_F(ServerGpuMemoryBufferManagerTest, AllocationRequestsForDestroyedClient) {
+TEST_F(HostGpuMemoryBufferManagerTest, AllocationRequestsForDestroyedClient) {
#if !defined(USE_OZONE) && !defined(OS_MACOSX) && !defined(OS_WIN)
// Not all platforms support native configurations (currently only ozone and
// mac support it). Abort the test in those platforms.
@@ -244,23 +240,23 @@ TEST_F(ServerGpuMemoryBufferManagerTest, AllocationRequestsForDestroyedClient) {
DCHECK(gpu::GetNativeGpuMemoryBufferConfigurations(&support).empty());
return;
#else
- // Note: ServerGpuMemoryBufferManager normally operates on a mojom::GpuService
+ // Note: HostGpuMemoryBufferManager normally operates on a mojom::GpuService
// implementation over mojo. Which means the communication from SGMBManager to
// GpuService is asynchronous. In this test, the mojom::GpuService is not
// bound to a mojo pipe, which means those calls are all synchronous.
TestGpuService gpu_service;
auto gpu_memory_buffer_support = MakeGpuMemoryBufferSupport(true);
- ServerGpuMemoryBufferManager manager(&gpu_service, 1,
- std::move(gpu_memory_buffer_support));
+ HostGpuMemoryBufferManager manager(1, std::move(gpu_memory_buffer_support),
+ base::ThreadTaskRunnerHandle::Get());
+ manager.SetGpuService(&gpu_service);
const auto buffer_id = static_cast<gfx::GpuMemoryBufferId>(1);
const int client_id = 2;
const gfx::Size size(10, 20);
const gfx::BufferFormat format = gfx::BufferFormat::RGBA_8888;
const gfx::BufferUsage usage = gfx::BufferUsage::GPU_READ;
- manager.AllocateGpuMemoryBuffer(
- buffer_id, client_id, size, format, usage, gpu::kNullSurfaceHandle,
- base::BindOnce([](const gfx::GpuMemoryBufferHandle& handle) {}));
+ manager.AllocateGpuMemoryBuffer(buffer_id, client_id, size, format, usage,
+ gpu::kNullSurfaceHandle, base::DoNothing());
EXPECT_TRUE(gpu_service.HasAllocationRequest(buffer_id, client_id));
EXPECT_FALSE(gpu_service.HasDestructionRequest(buffer_id, client_id));
@@ -277,12 +273,12 @@ TEST_F(ServerGpuMemoryBufferManagerTest, AllocationRequestsForDestroyedClient) {
#endif
}
-TEST_F(ServerGpuMemoryBufferManagerTest,
- RequestsFromUntrustedClientsValidated) {
+TEST_F(HostGpuMemoryBufferManagerTest, RequestsFromUntrustedClientsValidated) {
TestGpuService gpu_service;
auto gpu_memory_buffer_support = MakeGpuMemoryBufferSupport(false);
- ServerGpuMemoryBufferManager manager(&gpu_service, 1,
- std::move(gpu_memory_buffer_support));
+ HostGpuMemoryBufferManager manager(1, std::move(gpu_memory_buffer_support),
+ base::ThreadTaskRunnerHandle::Get());
+ manager.SetGpuService(&gpu_service);
const auto buffer_id = static_cast<gfx::GpuMemoryBufferId>(1);
const int client_id = 2;
// SCANOUT cannot be used if native gpu memory buffer is not supported.
@@ -305,10 +301,9 @@ TEST_F(ServerGpuMemoryBufferManagerTest,
gpu::kNullSurfaceHandle,
base::BindOnce(
[](gfx::GpuMemoryBufferHandle* allocated_handle,
- const base::Closure& callback,
- const gfx::GpuMemoryBufferHandle& handle) {
- *allocated_handle = handle;
- callback.Run();
+ base::OnceClosure callback, gfx::GpuMemoryBufferHandle handle) {
+ *allocated_handle = std::move(handle);
+ std::move(callback).Run();
},
&allocated_handle, runloop.QuitClosure()));
// Since native gpu memory buffers are not supported, the mojom.GpuService
@@ -325,22 +320,24 @@ TEST_F(ServerGpuMemoryBufferManagerTest,
}
}
-TEST_F(ServerGpuMemoryBufferManagerTest, GpuMemoryBufferDestroyed) {
+TEST_F(HostGpuMemoryBufferManagerTest, GpuMemoryBufferDestroyed) {
TestGpuService gpu_service;
auto gpu_memory_buffer_support = MakeGpuMemoryBufferSupport(false);
- ServerGpuMemoryBufferManager manager(&gpu_service, 1,
- std::move(gpu_memory_buffer_support));
+ HostGpuMemoryBufferManager manager(1, std::move(gpu_memory_buffer_support),
+ base::ThreadTaskRunnerHandle::Get());
+ manager.SetGpuService(&gpu_service);
auto buffer = AllocateGpuMemoryBufferSync(&manager);
EXPECT_TRUE(buffer);
buffer.reset();
}
-TEST_F(ServerGpuMemoryBufferManagerTest,
+TEST_F(HostGpuMemoryBufferManagerTest,
GpuMemoryBufferDestroyedOnDifferentThread) {
TestGpuService gpu_service;
auto gpu_memory_buffer_support = MakeGpuMemoryBufferSupport(false);
- ServerGpuMemoryBufferManager manager(&gpu_service, 1,
- std::move(gpu_memory_buffer_support));
+ HostGpuMemoryBufferManager manager(1, std::move(gpu_memory_buffer_support),
+ base::ThreadTaskRunnerHandle::Get());
+ manager.SetGpuService(&gpu_service);
auto buffer = AllocateGpuMemoryBufferSync(&manager);
EXPECT_TRUE(buffer);
// Destroy the buffer in a different thread.
diff --git a/chromium/components/viz/host/server_gpu_memory_buffer_manager.cc b/chromium/components/viz/host/server_gpu_memory_buffer_manager.cc
deleted file mode 100644
index b11db72e3a3..00000000000
--- a/chromium/components/viz/host/server_gpu_memory_buffer_manager.cc
+++ /dev/null
@@ -1,251 +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/host/server_gpu_memory_buffer_manager.h"
-
-#include <utility>
-
-#include "base/logging.h"
-#include "base/strings/stringprintf.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/trace_event/memory_dump_manager.h"
-#include "base/trace_event/process_memory_dump.h"
-#include "gpu/ipc/common/gpu_memory_buffer_impl.h"
-#include "gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.h"
-#include "gpu/ipc/common/gpu_memory_buffer_support.h"
-#include "services/viz/privileged/interfaces/gl/gpu_service.mojom.h"
-#include "ui/gfx/buffer_format_util.h"
-
-namespace viz {
-
-namespace {
-
-void OnGpuMemoryBufferDestroyed(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner,
- const gpu::GpuMemoryBufferImpl::DestructionCallback& callback,
- const gpu::SyncToken& sync_token) {
- task_runner->PostTask(FROM_HERE, base::Bind(callback, sync_token));
-}
-
-} // namespace
-
-ServerGpuMemoryBufferManager::BufferInfo::BufferInfo() = default;
-ServerGpuMemoryBufferManager::BufferInfo::~BufferInfo() = default;
-
-ServerGpuMemoryBufferManager::ServerGpuMemoryBufferManager(
- mojom::GpuService* gpu_service,
- int client_id,
- std::unique_ptr<gpu::GpuMemoryBufferSupport> gpu_memory_buffer_support)
- : gpu_service_(gpu_service),
- client_id_(client_id),
- gpu_memory_buffer_support_(std::move(gpu_memory_buffer_support)),
- native_configurations_(gpu::GetNativeGpuMemoryBufferConfigurations(
- gpu_memory_buffer_support_.get())),
- task_runner_(base::ThreadTaskRunnerHandle::Get()),
- weak_factory_(this) {
- weak_ptr_ = weak_factory_.GetWeakPtr();
-}
-
-ServerGpuMemoryBufferManager::~ServerGpuMemoryBufferManager() {}
-
-void ServerGpuMemoryBufferManager::AllocateGpuMemoryBuffer(
- gfx::GpuMemoryBufferId id,
- int client_id,
- const gfx::Size& size,
- gfx::BufferFormat format,
- gfx::BufferUsage usage,
- gpu::SurfaceHandle surface_handle,
- base::OnceCallback<void(const gfx::GpuMemoryBufferHandle&)> callback) {
- DCHECK(task_runner_->RunsTasksInCurrentSequence());
- if (gpu_memory_buffer_support_->GetNativeGpuMemoryBufferType() !=
- gfx::EMPTY_BUFFER) {
- const bool is_native = native_configurations_.find(std::make_pair(
- format, usage)) != native_configurations_.end();
- if (is_native) {
- pending_buffers_.insert(client_id);
- gpu_service_->CreateGpuMemoryBuffer(
- id, size, format, usage, client_id, surface_handle,
- base::BindOnce(
- &ServerGpuMemoryBufferManager::OnGpuMemoryBufferAllocated,
- weak_ptr_, client_id,
- gfx::BufferSizeForBufferFormat(size, format),
- std::move(callback)));
- return;
- }
- }
-
- gfx::GpuMemoryBufferHandle buffer_handle;
- // The requests are coming in from untrusted clients. So verify that it is
- // possible to allocate shared memory buffer first.
- if (gpu::GpuMemoryBufferImplSharedMemory::IsUsageSupported(usage) &&
- gpu::GpuMemoryBufferImplSharedMemory::IsSizeValidForFormat(size,
- format)) {
- buffer_handle = gpu::GpuMemoryBufferImplSharedMemory::CreateGpuMemoryBuffer(
- id, size, format, usage);
- BufferInfo buffer_info;
- DCHECK_EQ(gfx::SHARED_MEMORY_BUFFER, buffer_handle.type);
- buffer_info.type = gfx::SHARED_MEMORY_BUFFER;
- buffer_info.buffer_size_in_bytes =
- gfx::BufferSizeForBufferFormat(size, format);
- buffer_info.shared_memory_guid = buffer_handle.handle.GetGUID();
- allocated_buffers_[client_id].insert(
- std::make_pair(buffer_handle.id, buffer_info));
- }
-
- task_runner_->PostTask(FROM_HERE,
- base::BindOnce(std::move(callback), buffer_handle));
-}
-
-std::unique_ptr<gfx::GpuMemoryBuffer>
-ServerGpuMemoryBufferManager::CreateGpuMemoryBuffer(
- const gfx::Size& size,
- gfx::BufferFormat format,
- gfx::BufferUsage usage,
- gpu::SurfaceHandle surface_handle) {
- gfx::GpuMemoryBufferId id(next_gpu_memory_id_++);
- gfx::GpuMemoryBufferHandle handle;
- base::WaitableEvent wait_event(
- base::WaitableEvent::ResetPolicy::MANUAL,
- base::WaitableEvent::InitialState::NOT_SIGNALED);
- DCHECK(!task_runner_->RunsTasksInCurrentSequence());
- auto reply_callback = base::BindOnce(
- [](gfx::GpuMemoryBufferHandle* handle, base::WaitableEvent* wait_event,
- const gfx::GpuMemoryBufferHandle& allocated_buffer_handle) {
- *handle = allocated_buffer_handle;
- wait_event->Signal();
- },
- &handle, &wait_event);
- // We block with a WaitableEvent until the callback is run. So using
- // base::Unretained() is safe here.
- auto allocate_callback =
- base::BindOnce(&ServerGpuMemoryBufferManager::AllocateGpuMemoryBuffer,
- base::Unretained(this), id, client_id_, size, format,
- usage, surface_handle, std::move(reply_callback));
- task_runner_->PostTask(FROM_HERE, std::move(allocate_callback));
- base::ThreadRestrictions::ScopedAllowWait allow_wait;
- wait_event.Wait();
- if (handle.is_null())
- return nullptr;
- // The destruction callback can be called on any thread. So use an
- // intermediate callback here as the destruction callback, which bounces off
- // onto the |task_runner_| thread to do the real work.
- return gpu_memory_buffer_support_->CreateGpuMemoryBufferImplFromHandle(
- handle, size, format, usage,
- base::Bind(
- &OnGpuMemoryBufferDestroyed, task_runner_,
- base::Bind(&ServerGpuMemoryBufferManager::DestroyGpuMemoryBuffer,
- weak_ptr_, id, client_id_)));
-}
-
-void ServerGpuMemoryBufferManager::SetDestructionSyncToken(
- gfx::GpuMemoryBuffer* buffer,
- const gpu::SyncToken& sync_token) {
- static_cast<gpu::GpuMemoryBufferImpl*>(buffer)->set_destruction_sync_token(
- sync_token);
-}
-
-bool ServerGpuMemoryBufferManager::OnMemoryDump(
- const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) {
- DCHECK(task_runner_->RunsTasksInCurrentSequence());
- for (const auto& pair : allocated_buffers_) {
- int client_id = pair.first;
- for (const auto& buffer_pair : pair.second) {
- gfx::GpuMemoryBufferId buffer_id = buffer_pair.first;
- const BufferInfo& buffer_info = buffer_pair.second;
- base::trace_event::MemoryAllocatorDump* dump =
- pmd->CreateAllocatorDump(base::StringPrintf(
- "gpumemorybuffer/client_%d/buffer_%d", client_id, buffer_id.id));
- if (!dump)
- return false;
- dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
- base::trace_event::MemoryAllocatorDump::kUnitsBytes,
- buffer_info.buffer_size_in_bytes);
-
- // Create the cross-process ownership edge. If the client creates a
- // corresponding dump for the same buffer, this will avoid to
- // double-count them in tracing. If, instead, no other process will emit a
- // dump with the same guid, the segment will be accounted to the browser.
- uint64_t client_tracing_process_id = ClientIdToTracingId(client_id);
-
- if (buffer_info.type == gfx::SHARED_MEMORY_BUFFER) {
- pmd->CreateSharedMemoryOwnershipEdge(
- dump->guid(), buffer_info.shared_memory_guid, 0 /* importance */);
- } else {
- auto shared_buffer_guid = gfx::GetGenericSharedGpuMemoryGUIDForTracing(
- client_tracing_process_id, buffer_id);
- pmd->CreateSharedGlobalAllocatorDump(shared_buffer_guid);
- pmd->AddOwnershipEdge(dump->guid(), shared_buffer_guid);
- }
- }
- }
- return true;
-}
-
-void ServerGpuMemoryBufferManager::DestroyGpuMemoryBuffer(
- gfx::GpuMemoryBufferId id,
- int client_id,
- const gpu::SyncToken& sync_token) {
- DCHECK(task_runner_->RunsTasksInCurrentSequence());
- auto iter = allocated_buffers_[client_id].find(id);
- if (iter == allocated_buffers_[client_id].end())
- return;
- DCHECK_NE(gfx::EMPTY_BUFFER, iter->second.type);
- if (iter->second.type != gfx::SHARED_MEMORY_BUFFER)
- gpu_service_->DestroyGpuMemoryBuffer(id, client_id, sync_token);
- allocated_buffers_[client_id].erase(id);
-}
-
-void ServerGpuMemoryBufferManager::DestroyAllGpuMemoryBufferForClient(
- int client_id) {
- DCHECK(task_runner_->RunsTasksInCurrentSequence());
- for (auto pair : allocated_buffers_[client_id]) {
- DCHECK_NE(gfx::EMPTY_BUFFER, pair.second.type);
- if (pair.second.type != gfx::SHARED_MEMORY_BUFFER) {
- gpu_service_->DestroyGpuMemoryBuffer(pair.first, client_id,
- gpu::SyncToken());
- }
- }
- allocated_buffers_.erase(client_id);
- pending_buffers_.erase(client_id);
-}
-
-uint64_t ServerGpuMemoryBufferManager::ClientIdToTracingId(
- int client_id) const {
- if (client_id == client_id_) {
- return base::trace_event::MemoryDumpManager::GetInstance()
- ->GetTracingProcessId();
- }
- // TODO(sad|ssid): Find a better way once crbug.com/661257 is resolved.
- // The hash value is incremented so that the tracing id is never equal to
- // MemoryDumpManager::kInvalidTracingProcessId.
- return static_cast<uint64_t>(base::Hash(&client_id, sizeof(client_id))) + 1;
-}
-
-void ServerGpuMemoryBufferManager::OnGpuMemoryBufferAllocated(
- int client_id,
- size_t buffer_size_in_bytes,
- base::OnceCallback<void(const gfx::GpuMemoryBufferHandle&)> callback,
- const gfx::GpuMemoryBufferHandle& handle) {
- DCHECK(task_runner_->RunsTasksInCurrentSequence());
- if (pending_buffers_.find(client_id) == pending_buffers_.end()) {
- // The client has been destroyed since the allocation request was made.
- if (!handle.is_null()) {
- gpu_service_->DestroyGpuMemoryBuffer(handle.id, client_id,
- gpu::SyncToken());
- }
- std::move(callback).Run(gfx::GpuMemoryBufferHandle());
- return;
- }
- if (!handle.is_null()) {
- BufferInfo buffer_info;
- buffer_info.type = handle.type;
- buffer_info.buffer_size_in_bytes = buffer_size_in_bytes;
- allocated_buffers_[client_id].insert(
- std::make_pair(handle.id, buffer_info));
- }
- std::move(callback).Run(handle);
-}
-
-} // namespace viz
diff --git a/chromium/components/viz/host/server_gpu_memory_buffer_manager.h b/chromium/components/viz/host/server_gpu_memory_buffer_manager.h
deleted file mode 100644
index e91af3ebbde..00000000000
--- a/chromium/components/viz/host/server_gpu_memory_buffer_manager.h
+++ /dev/null
@@ -1,110 +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_HOST_SERVER_GPU_MEMORY_BUFFER_MANAGER_H_
-#define COMPONENTS_VIZ_HOST_SERVER_GPU_MEMORY_BUFFER_MANAGER_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/single_thread_task_runner.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/trace_event/memory_dump_provider.h"
-#include "components/viz/host/viz_host_export.h"
-#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
-#include "gpu/ipc/host/gpu_memory_buffer_support.h"
-
-namespace gpu {
-class GpuMemoryBufferSupport;
-}
-
-namespace viz {
-
-namespace mojom {
-class GpuService;
-}
-
-// This GpuMemoryBufferManager implementation is for [de]allocating gpu memory
-// from the gpu process over the mojom.GpuService api.
-// Note that |CreateGpuMemoryBuffer()| can be called on any thread. All the rest
-// of the functions must be called on the thread this object is created on.
-class VIZ_HOST_EXPORT ServerGpuMemoryBufferManager
- : public gpu::GpuMemoryBufferManager,
- public base::trace_event::MemoryDumpProvider {
- public:
- ServerGpuMemoryBufferManager(
- mojom::GpuService* gpu_service,
- int client_id,
- std::unique_ptr<gpu::GpuMemoryBufferSupport> gpu_memory_buffer_support);
- ~ServerGpuMemoryBufferManager() override;
-
- void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
- int client_id,
- const gpu::SyncToken& sync_token);
-
- void DestroyAllGpuMemoryBufferForClient(int client_id);
-
- void AllocateGpuMemoryBuffer(
- gfx::GpuMemoryBufferId id,
- int client_id,
- const gfx::Size& size,
- gfx::BufferFormat format,
- gfx::BufferUsage usage,
- gpu::SurfaceHandle surface_handle,
- base::OnceCallback<void(const gfx::GpuMemoryBufferHandle&)> callback);
-
- // Overridden from gpu::GpuMemoryBufferManager:
- std::unique_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBuffer(
- const gfx::Size& size,
- gfx::BufferFormat format,
- gfx::BufferUsage usage,
- gpu::SurfaceHandle surface_handle) override;
- void SetDestructionSyncToken(gfx::GpuMemoryBuffer* buffer,
- const gpu::SyncToken& sync_token) override;
-
- // Overridden from base::trace_event::MemoryDumpProvider:
- bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
- base::trace_event::ProcessMemoryDump* pmd) override;
-
- private:
- uint64_t ClientIdToTracingId(int client_id) const;
- void OnGpuMemoryBufferAllocated(
- int client_id,
- size_t buffer_size_in_bytes,
- base::OnceCallback<void(const gfx::GpuMemoryBufferHandle&)> callback,
- const gfx::GpuMemoryBufferHandle& handle);
-
- mojom::GpuService* gpu_service_;
- const int client_id_;
- int next_gpu_memory_id_ = 1;
-
- struct BufferInfo {
- BufferInfo();
- ~BufferInfo();
- gfx::GpuMemoryBufferType type = gfx::EMPTY_BUFFER;
- size_t buffer_size_in_bytes = 0;
- base::UnguessableToken shared_memory_guid;
- };
-
- using AllocatedBuffers =
- std::unordered_map<gfx::GpuMemoryBufferId,
- BufferInfo,
- BASE_HASH_NAMESPACE::hash<gfx::GpuMemoryBufferId>>;
- std::unordered_map<int, AllocatedBuffers> allocated_buffers_;
- std::unordered_set<int> pending_buffers_;
-
- std::unique_ptr<gpu::GpuMemoryBufferSupport> gpu_memory_buffer_support_;
-
- const gpu::GpuMemoryBufferConfigurationSet native_configurations_;
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- base::WeakPtr<ServerGpuMemoryBufferManager> weak_ptr_;
- base::WeakPtrFactory<ServerGpuMemoryBufferManager> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(ServerGpuMemoryBufferManager);
-};
-
-} // namespace viz
-
-#endif // COMPONENTS_VIZ_HOST_SERVER_GPU_MEMORY_BUFFER_MANAGER_H_
diff --git a/chromium/components/viz/service/BUILD.gn b/chromium/components/viz/service/BUILD.gn
index cfec9fbec12..33fc82ac50a 100644
--- a/chromium/components/viz/service/BUILD.gn
+++ b/chromium/components/viz/service/BUILD.gn
@@ -67,12 +67,16 @@ viz_component("service") {
"display/program_binding.h",
"display/renderer_utils.cc",
"display/renderer_utils.h",
+ "display/resource_fence.h",
+ "display/resource_metadata.cc",
+ "display/resource_metadata.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",
"display/skia_renderer.cc",
@@ -95,8 +99,6 @@ viz_component("service") {
"display_embedder/compositing_mode_reporter_impl.h",
"display_embedder/compositor_overlay_candidate_validator.h",
"display_embedder/display_provider.h",
- "display_embedder/external_begin_frame_controller_impl.cc",
- "display_embedder/external_begin_frame_controller_impl.h",
"display_embedder/gl_output_surface.cc",
"display_embedder/gl_output_surface.h",
"display_embedder/gl_output_surface_buffer_queue.cc",
@@ -122,13 +124,13 @@ viz_component("service") {
"frame_sinks/compositor_frame_sink_support_manager.h",
"frame_sinks/direct_layer_tree_frame_sink.cc",
"frame_sinks/direct_layer_tree_frame_sink.h",
+ "frame_sinks/external_begin_frame_source_mojo.cc",
+ "frame_sinks/external_begin_frame_source_mojo.h",
"frame_sinks/frame_sink_manager_impl.cc",
"frame_sinks/frame_sink_manager_impl.h",
"frame_sinks/frame_sink_observer.h",
"frame_sinks/primary_begin_frame_source.cc",
"frame_sinks/primary_begin_frame_source.h",
- "frame_sinks/referenced_surface_tracker.cc",
- "frame_sinks/referenced_surface_tracker.h",
"frame_sinks/root_compositor_frame_sink_impl.cc",
"frame_sinks/root_compositor_frame_sink_impl.h",
"frame_sinks/surface_resource_holder.cc",
@@ -142,6 +144,8 @@ viz_component("service") {
"frame_sinks/video_capture/in_flight_frame_delivery.h",
"frame_sinks/video_capture/interprocess_frame_pool.cc",
"frame_sinks/video_capture/interprocess_frame_pool.h",
+ "frame_sinks/video_capture/video_capture_overlay.cc",
+ "frame_sinks/video_capture/video_capture_overlay.h",
"frame_sinks/video_detector.cc",
"frame_sinks/video_detector.h",
"gl/gpu_service_impl.cc",
@@ -151,7 +155,11 @@ viz_component("service") {
"hit_test/hit_test_aggregator_delegate.h",
"hit_test/hit_test_manager.cc",
"hit_test/hit_test_manager.h",
+ "main/viz_compositor_thread_runner.cc",
+ "main/viz_compositor_thread_runner.h",
"surfaces/latest_local_surface_id_lookup_delegate.h",
+ "surfaces/referenced_surface_tracker.cc",
+ "surfaces/referenced_surface_tracker.h",
"surfaces/surface.cc",
"surfaces/surface.h",
"surfaces/surface_client.h",
@@ -241,7 +249,11 @@ viz_component("service") {
sources += [
"display_embedder/compositor_overlay_candidate_validator_android.cc",
"display_embedder/compositor_overlay_candidate_validator_android.h",
+ "frame_sinks/external_begin_frame_source_android.cc",
+ "frame_sinks/external_begin_frame_source_android.h",
]
+
+ deps += [ ":service_jni_headers" ]
}
if (use_ozone) {
@@ -270,6 +282,13 @@ viz_component("service") {
]
}
+ if (is_android) {
+ sources += [
+ "display_embedder/gl_output_surface_android.cc",
+ "display_embedder/gl_output_surface_android.h",
+ ]
+ }
+
if (enable_vulkan) {
deps += [ "//gpu/vulkan" ]
}
@@ -300,14 +319,15 @@ viz_source_set("unit_tests") {
"frame_sinks/compositor_frame_sink_support_unittest.cc",
"frame_sinks/direct_layer_tree_frame_sink_unittest.cc",
"frame_sinks/frame_sink_manager_unittest.cc",
- "frame_sinks/referenced_surface_tracker_unittest.cc",
"frame_sinks/surface_references_unittest.cc",
"frame_sinks/surface_synchronization_unittest.cc",
"frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc",
"frame_sinks/video_capture/interprocess_frame_pool_unittest.cc",
+ "frame_sinks/video_capture/video_capture_overlay_unittest.cc",
"frame_sinks/video_detector_unittest.cc",
"gl/gpu_service_impl_unittest.cc",
"hit_test/hit_test_aggregator_unittest.cc",
+ "surfaces/referenced_surface_tracker_unittest.cc",
"surfaces/surface_dependency_deadline_unittest.cc",
"surfaces/surface_hittest_unittest.cc",
"surfaces/surface_unittest.cc",
@@ -328,6 +348,7 @@ viz_source_set("unit_tests") {
"//base/test:test_support",
"//cc:test_support",
"//cc/paint",
+ "//components/viz/client",
"//components/viz/common",
"//components/viz/host",
"//components/viz/test:test_support",
@@ -364,6 +385,10 @@ viz_source_set("unit_tests") {
if (is_win) {
sources += [ "display_embedder/output_device_backing_unittest.cc" ]
}
+
+ if (is_android) {
+ sources += [ "frame_sinks/external_begin_frame_source_android_unittest.cc" ]
+ }
}
viz_source_set("perf_tests") {
@@ -398,6 +423,22 @@ fuzzer_test("hit_test_manager_fuzzer") {
":service",
"//base/test:test_support",
"//components/viz/test:test_support",
- "//mojo/edk",
]
}
+
+if (is_android) {
+ android_library("service_java") {
+ deps = [
+ "//base:base_java",
+ "//ui/android:ui_full_java",
+ ]
+ java_files = [ "java/src/org/chromium/components/viz/service/frame_sinks/ExternalBeginFrameSourceAndroid.java" ]
+ }
+
+ generate_jni("service_jni_headers") {
+ jni_package = "components/viz/service/frame_sinks"
+ sources = [
+ "java/src/org/chromium/components/viz/service/frame_sinks/ExternalBeginFrameSourceAndroid.java",
+ ]
+ }
+}
diff --git a/chromium/components/viz/service/DEPS b/chromium/components/viz/service/DEPS
index 4ec6497057f..e546344f182 100644
--- a/chromium/components/viz/service/DEPS
+++ b/chromium/components/viz/service/DEPS
@@ -14,7 +14,7 @@ include_rules = [
]
specific_include_rules = {
- ".*_unittest\.cc": [
- "+components/viz/test",
+ "ExternalBeginFrameSourceAndroid.java": [
+ "+ui/android/java/src/org/chromium/ui/VSyncMonitor.java",
]
}
diff --git a/chromium/components/viz/service/display/DEPS b/chromium/components/viz/service/display/DEPS
index 3560a35a10b..d866c45285f 100644
--- a/chromium/components/viz/service/display/DEPS
+++ b/chromium/components/viz/service/display/DEPS
@@ -13,6 +13,7 @@ include_rules = [
"+gpu/GLES2",
"+gpu/vulkan",
"+media/base",
+ "+mojo/public/cpp/system",
"+skia",
"+third_party/khronos",
"+third_party/skia",
@@ -30,10 +31,12 @@ specific_include_rules = {
],
".*_(unit|pixel|perf)test\.cc": [
"+cc/test",
+ "+components/viz/client",
"+components/viz/service/display_embedder",
"+components/viz/service/frame_sinks",
"+components/viz/test",
"+gpu/GLES2",
+ "+media",
"+third_party/libyuv",
],
}
diff --git a/chromium/components/viz/service/display/bsp_tree_perftest.cc b/chromium/components/viz/service/display/bsp_tree_perftest.cc
index 93df78fa388..d1832a10632 100644
--- a/chromium/components/viz/service/display/bsp_tree_perftest.cc
+++ b/chromium/components/viz/service/display/bsp_tree_perftest.cc
@@ -22,6 +22,7 @@
#include "cc/test/layer_tree_test.h"
#include "cc/trees/layer_tree_host_common.h"
#include "cc/trees/layer_tree_impl.h"
+#include "cc/trees/transform_node.h"
#include "components/viz/service/display/bsp_tree.h"
#include "components/viz/service/display/draw_polygon.h"
#include "components/viz/test/paths.h"
@@ -115,7 +116,10 @@ class BspTreePerfTest : public cc::LayerTreeTest {
active_tree->elastic_overscroll()->Current(active_tree->IsActiveTree()),
active_tree->OverscrollElasticityLayer(), max_texture_size,
host_impl->settings().layer_transforms_should_scale_layer_contents,
- &update_list, active_tree->property_trees());
+ &update_list, active_tree->property_trees(),
+ active_tree->property_trees()->transform_tree.Node(
+ active_tree->InnerViewportContainerLayer()
+ ->transform_tree_index()));
cc::LayerTreeHostCommon::CalculateDrawProperties(&inputs);
}
diff --git a/chromium/components/viz/service/display/copy_output_scaling_pixeltest.cc b/chromium/components/viz/service/display/copy_output_scaling_pixeltest.cc
index ef9ac59f247..e46b1685908 100644
--- a/chromium/components/viz/service/display/copy_output_scaling_pixeltest.cc
+++ b/chromium/components/viz/service/display/copy_output_scaling_pixeltest.cc
@@ -250,7 +250,7 @@ class CopyOutputScalingPixelTest
kPremul_SkAlphaType));
const int error_code = libyuv::I420ToARGB(
y_data.get(), y_stride, u_data.get(), u_stride, v_data.get(), v_stride,
- static_cast<uint8*>(bitmap.getPixels()), bitmap.rowBytes(),
+ static_cast<uint8_t*>(bitmap.getPixels()), bitmap.rowBytes(),
result_width, result_height);
CHECK_EQ(0, error_code);
diff --git a/chromium/components/viz/service/display/dc_layer_overlay.cc b/chromium/components/viz/service/display/dc_layer_overlay.cc
index 288b1703c71..ca087f2d241 100644
--- a/chromium/components/viz/service/display/dc_layer_overlay.cc
+++ b/chromium/components/viz/service/display/dc_layer_overlay.cc
@@ -32,6 +32,9 @@ DCLayerOverlayProcessor::DCLayerResult FromYUVQuad(
dc_layer_overlay->color_space = quad->video_color_space;
dc_layer_overlay->require_overlay = quad->require_overlay;
dc_layer_overlay->is_protected_video = quad->is_protected_video;
+ if (dc_layer_overlay->is_protected_video)
+ DCHECK(dc_layer_overlay->require_overlay);
+
return DCLayerOverlayProcessor::DC_LAYER_SUCCESS;
}
@@ -137,23 +140,15 @@ void DCLayerOverlayProcessor::Process(
gfx::Rect* overlay_damage_rect,
gfx::Rect* damage_rect,
DCLayerOverlayList* dc_layer_overlays) {
- DCHECK(pass_info_.empty());
processed_overlay_in_frame_ = false;
- if (base::FeatureList::IsEnabled(
- features::kDirectCompositionNonrootOverlays)) {
- for (auto& pass : *render_passes) {
- bool is_root = (pass == render_passes->back());
- ProcessRenderPass(resource_provider, display_rect, pass.get(), is_root,
- overlay_damage_rect,
- is_root ? damage_rect : &pass->damage_rect,
- dc_layer_overlays);
- }
- } else {
- ProcessRenderPass(resource_provider, display_rect,
- render_passes->back().get(), true, overlay_damage_rect,
- damage_rect, dc_layer_overlays);
+ pass_punch_through_rects_.clear();
+ for (auto& pass : *render_passes) {
+ bool is_root = (pass == render_passes->back());
+ ProcessRenderPass(resource_provider, display_rect, pass.get(), is_root,
+ overlay_damage_rect,
+ is_root ? damage_rect : &pass->damage_rect,
+ dc_layer_overlays);
}
- pass_info_.clear();
}
QuadList::Iterator DCLayerOverlayProcessor::ProcessRenderPassDrawQuad(
@@ -167,12 +162,10 @@ QuadList::Iterator DCLayerOverlayProcessor::ProcessRenderPassDrawQuad(
// Check if this quad is broken to avoid corrupting pass_info.
if (rpdq->render_pass_id == render_pass->id)
return it;
- if (!pass_info_.count(rpdq->render_pass_id))
+ // |pass_punch_through_rects_| will be empty unless non-root overlays are
+ // enabled.
+ if (!pass_punch_through_rects_.count(rpdq->render_pass_id))
return it;
- pass_info_[render_pass->id] = std::vector<PunchThroughRect>();
- auto& pass_info = pass_info_[rpdq->render_pass_id];
-
- const SharedQuadState* original_shared_quad_state = rpdq->shared_quad_state;
// Punch holes through for all child video quads that will be displayed in
// underlays. This doesn't work perfectly in all cases - it breaks with
@@ -180,40 +173,73 @@ QuadList::Iterator DCLayerOverlayProcessor::ProcessRenderPassDrawQuad(
// videos at all. The EME spec allows that some HTML rendering capabilities
// may be unavailable for EME videos.
//
- // The solid color quads are inserted after the RPDQ, so they'll be drawn
- // before it and will only cut out contents behind it. A kDstOut solid color
- // quad is used with an accumulated opacity to do the hole punching, because
- // with premultiplied alpha that reduces the opacity of the current content
- // by the opacity of the layer.
+ // For opaque video we punch a transparent hole behind the RPDQ so that
+ // translucent elements in front of the video do not blend with elements
+ // behind the video.
+ //
+ // For translucent video we can achieve the same result as SrcOver blending of
+ // video in multiple stacked render passes if the root render pass got the
+ // color contribution from the render passes sans video, and the alpha was set
+ // to 1 - video's accumulated alpha (product of video and render pass draw
+ // quad opacities). To achieve this we can put a transparent solid color quad
+ // with SrcOver blending in place of video. This quad's pixels rendered
+ // finally on the root render pass will give the color contribution of all
+ // content below the video with the intermediate opacities taken into account.
+ // Finally we need to set the corresponding area in the root render pass to
+ // the correct alpha. This can be achieved with a DstOut black quad above the
+ // video with the accumulated alpha and color mask set to write only alpha
+ // channel. Essentially,
+ //
+ // SrcOver_quad(SrcOver_quad(V, RP1, V_a), RP2, RPDQ1_a) = SrcOver_premul(
+ // DstOut_mask(
+ // BLACK,
+ // SrcOver_quad(SrcOver_quad(TRANSPARENT, RP1, V_a), RP2, RPDQ1_a),
+ // acc_a),
+ // V)
+ //
+ // where V is the video
+ // RP1 and RP2 are the inner and outer render passes
+ // acc_a is the accumulated alpha
+ // SrcOver_quad uses opacity of the source quad (V_a and RPDQ1_a)
+ // SrcOver_premul assumes premultiplied alpha channel
+ //
+ // TODO(sunnyps): Implement the above. This requires support for setting
+ // color mask in solid color draw quad which we don't have today. Another
+ // difficulty is undoing the SrcOver blending in child render passes if any
+ // render pass above has a non-supported blend mode.
+ const auto& punch_through_rects =
+ pass_punch_through_rects_[rpdq->render_pass_id];
+ const SharedQuadState* original_shared_quad_state = rpdq->shared_quad_state;
+
+ // The iterator was advanced above so InsertBefore inserts after the RPDQ.
it = render_pass->quad_list
.InsertBeforeAndInvalidateAllPointers<SolidColorDrawQuad>(
- it, pass_info.size());
+ it, punch_through_rects.size());
rpdq = nullptr;
- for (size_t i = 0; i < pass_info.size(); i++, ++it) {
- auto& punch_through = pass_info[i];
+ for (const gfx::Rect& punch_through_rect : punch_through_rects) {
+ // Copy shared state from RPDQ to get the same clip rect.
SharedQuadState* new_shared_quad_state =
render_pass->shared_quad_state_list
- .AllocateAndConstruct<SharedQuadState>();
- gfx::Transform new_transform(
- original_shared_quad_state->quad_to_target_transform,
- punch_through.transform_to_target);
- float new_opacity =
- punch_through.opacity * original_shared_quad_state->opacity;
- new_shared_quad_state->SetAll(new_transform, punch_through.rect,
- punch_through.rect, punch_through.rect, false,
- true, new_opacity, SkBlendMode::kDstOut, 0);
- auto* solid_quad = static_cast<SolidColorDrawQuad*>(*it);
- solid_quad->SetAll(new_shared_quad_state, punch_through.rect,
- punch_through.rect, false, 0xff000000, true);
- damage_rect->Union(gfx::ToEnclosingRect(ClippedQuadRectangle(solid_quad)));
+ .AllocateAndCopyFrom<SharedQuadState>(original_shared_quad_state);
+
+ // Set opacity to 1 since we're not blending.
+ new_shared_quad_state->opacity = 1.f;
+
+ auto* solid_quad = static_cast<SolidColorDrawQuad*>(*it++);
+ solid_quad->SetAll(new_shared_quad_state, punch_through_rect,
+ punch_through_rect, false, SK_ColorTRANSPARENT, true);
+
+ gfx::Rect clipped_quad_rect =
+ gfx::ToEnclosingRect(ClippedQuadRectangle(solid_quad));
+ // Propagate punch through rect as damage up the stack of render passes.
+ // TODO(sunnyps): We should avoid this extra damage if we knew that the
+ // video (in child render surface) was the only thing damaging this render
+ // surface.
+ damage_rect->Union(clipped_quad_rect);
// Add transformed info to list in case this renderpass is included in
// another pass.
- PunchThroughRect info;
- info.rect = punch_through.rect;
- info.transform_to_target = new_transform;
- info.opacity = new_opacity;
- pass_info_[render_pass->id].push_back(info);
+ pass_punch_through_rects_[render_pass->id].push_back(clipped_quad_rect);
}
return it;
}
@@ -227,8 +253,9 @@ void DCLayerOverlayProcessor::ProcessRenderPass(
gfx::Rect* damage_rect,
DCLayerOverlayList* dc_layer_overlays) {
gfx::Rect this_frame_underlay_rect;
- QuadList* quad_list = &render_pass->quad_list;
+ gfx::Rect this_frame_underlay_occlusion;
+ QuadList* quad_list = &render_pass->quad_list;
auto next_it = quad_list->begin();
for (auto it = quad_list->begin(); it != quad_list->end(); it = next_it) {
next_it = it;
@@ -261,13 +288,28 @@ void DCLayerOverlayProcessor::ProcessRenderPass(
dc_layer.shared_state->transform.postConcat(
render_pass->transform_to_root_target.matrix());
+ // Clip rect is in quad target (render pass) space, and must be transformed
+ // to display space since we only send the quad content (layer) to root
+ // transform to compositor. To transform clip rect we need the quad target
+ // (render pass) to root transform too, so it's better to perform the
+ // transform here instead of sending two separate transforms.
+ render_pass->transform_to_root_target.TransformRect(
+ &dc_layer.shared_state->clip_rect);
+
+ // These rects are in quad target space.
gfx::Rect quad_rectangle = gfx::ToEnclosingRect(ClippedQuadRectangle(*it));
gfx::RectF occlusion_bounding_box =
GetOcclusionBounds(gfx::RectF(quad_rectangle), quad_list->begin(), it);
bool processed_overlay = false;
- // Underlays are less efficient, so attempt regular overlays first.
- if (is_root && !processed_overlay_in_frame_ &&
+ // Underlays are less efficient, so attempt regular overlays first. Only
+ // check root render pass because we can only check for occlusion within a
+ // render pass. Only check if an overlay hasn't been processed already since
+ // our damage calculations will be wrong otherwise.
+ // TODO(magchen): Collect all overlay candidates, and filter the list at the
+ // end to find the best candidates (largest size?).
+ if (is_root &&
+ (!processed_overlay_in_frame_ || dc_layer.is_protected_video) &&
ProcessForOverlay(display_rect, quad_list, quad_rectangle,
occlusion_bounding_box, &it, damage_rect)) {
// ProcessForOverlay makes the iterator point to the next value on
@@ -277,7 +319,7 @@ void DCLayerOverlayProcessor::ProcessRenderPass(
} else if (ProcessForUnderlay(display_rect, render_pass, quad_rectangle,
occlusion_bounding_box, it, is_root,
damage_rect, &this_frame_underlay_rect,
- &dc_layer)) {
+ &this_frame_underlay_occlusion, &dc_layer)) {
processed_overlay = true;
}
@@ -288,18 +330,18 @@ void DCLayerOverlayProcessor::ProcessRenderPass(
RecordDCLayerResult(DC_LAYER_SUCCESS);
dc_layer_overlays->push_back(dc_layer);
- if (!base::FeatureList::IsEnabled(
- features::kDirectCompositionNonrootOverlays)) {
- // Only allow one overlay for now.
- break;
- }
+
+ // Only allow one overlay unless non-root overlays are enabled.
+ // TODO(magchen): We want to produce all overlay candidates, and then
+ // choose the best one.
processed_overlay_in_frame_ = true;
}
}
if (is_root) {
damage_rect->Intersect(gfx::ToEnclosingRect(display_rect));
- previous_frame_underlay_rect_ = this_frame_underlay_rect;
previous_display_rect_ = display_rect;
+ previous_frame_underlay_rect_ = this_frame_underlay_rect;
+ previous_frame_underlay_occlusion_ = this_frame_underlay_occlusion;
}
}
@@ -310,11 +352,11 @@ bool DCLayerOverlayProcessor::ProcessForOverlay(
const gfx::RectF& occlusion_bounding_box,
QuadList::Iterator* it,
gfx::Rect* damage_rect) {
- bool display_rect_changed = (display_rect != previous_display_rect_);
if (!occlusion_bounding_box.IsEmpty())
return false;
// The quad is on top, so promote it to an overlay and remove all damage
// underneath it.
+ bool display_rect_changed = (display_rect != previous_display_rect_);
if ((*it)
->shared_quad_state->quad_to_target_transform
.Preserves2dAxisAlignment() &&
@@ -334,83 +376,114 @@ bool DCLayerOverlayProcessor::ProcessForUnderlay(
bool is_root,
gfx::Rect* damage_rect,
gfx::Rect* this_frame_underlay_rect,
+ gfx::Rect* this_frame_underlay_occlusion,
DCLayerOverlay* dc_layer) {
if (!dc_layer->require_overlay) {
if (!base::FeatureList::IsEnabled(features::kDirectCompositionUnderlays)) {
RecordDCLayerResult(DC_LAYER_FAILED_OCCLUDED);
return false;
}
- if (!is_root) {
+ if (!is_root && !base::FeatureList::IsEnabled(
+ features::kDirectCompositionNonrootOverlays)) {
RecordDCLayerResult(DC_LAYER_FAILED_NON_ROOT);
return false;
}
- if (processed_overlay_in_frame_) {
- RecordDCLayerResult(DC_LAYER_FAILED_TOO_MANY_OVERLAYS);
- return false;
- }
if ((it->shared_quad_state->opacity < 1.0)) {
RecordDCLayerResult(DC_LAYER_FAILED_TRANSPARENT);
return false;
}
+ // Record this UMA only after we're absolutely sure this quad could be an
+ // underlay.
+ if (processed_overlay_in_frame_) {
+ RecordDCLayerResult(DC_LAYER_FAILED_TOO_MANY_OVERLAYS);
+ return false;
+ }
}
- bool display_rect_changed = (display_rect != previous_display_rect_);
- // The quad is occluded, so replace it with a black solid color quad and
- // place the overlay itself under the quad.
- if (is_root && it->shared_quad_state->quad_to_target_transform
- .IsIdentityOrIntegerTranslation()) {
- *this_frame_underlay_rect = quad_rectangle;
- }
+
+ // TODO(magchen): Assign decreasing z-order so that underlays processed
+ // earlier, and hence which are above the subsequent underlays, are placed
+ // above in the direct composition visual tree.
dc_layer->shared_state->z_order = -1;
const SharedQuadState* shared_quad_state = it->shared_quad_state;
gfx::Rect rect = it->visible_rect;
- if (shared_quad_state->opacity < 1.0) {
+ // If the video is translucent and uses SrcOver blend mode, we can achieve the
+ // same result as compositing with video on top if we replace video quad with
+ // a solid color quad with DstOut blend mode, and rely on SrcOver blending
+ // of the root surface with video on bottom. Essentially,
+ //
+ // SrcOver_quad(V, B, V_alpha) = SrcOver_premul(DstOut(BLACK, B, V_alpha), V)
+ // where
+ // V is the video quad
+ // B is the background
+ // SrcOver_quad uses opacity of source quad (V_alpha)
+ // SrcOver_premul uses alpha channel and assumes premultipled alpha
+ bool is_opaque = false;
+ if (it->ShouldDrawWithBlending() &&
+ shared_quad_state->blend_mode == SkBlendMode::kSrcOver) {
SharedQuadState* new_shared_quad_state =
render_pass->shared_quad_state_list.AllocateAndCopyFrom(
shared_quad_state);
- new_shared_quad_state->blend_mode = SkBlendMode::kDstOut;
auto* replacement =
render_pass->quad_list.ReplaceExistingElement<SolidColorDrawQuad>(it);
- replacement->SetAll(shared_quad_state, rect, rect, false, 0xff000000, true);
+ new_shared_quad_state->blend_mode = SkBlendMode::kDstOut;
+ // Use needs_blending from original quad because blending might be because
+ // of this flag or opacity.
+ replacement->SetAll(new_shared_quad_state, rect, rect, it->needs_blending,
+ SK_ColorBLACK, true /* force_anti_aliasing_off */);
} else {
// When the opacity == 1.0, drawing with transparent will be done without
// blending and will have the proper effect of completely clearing the
// layer.
render_pass->quad_list.ReplaceExistingQuadWithOpaqueTransparentSolidColor(
it);
+ is_opaque = true;
}
- if (*this_frame_underlay_rect == previous_frame_underlay_rect_ && is_root &&
- !processed_overlay_in_frame_) {
- // If this underlay rect is the same as for last frame, subtract its
- // area from the damage of the main surface, as the cleared area was
- // already cleared last frame. Add back the damage from the occluded
- // area for this and last frame, as that may have changed.
- if (it->shared_quad_state->quad_to_target_transform
- .Preserves2dAxisAlignment() &&
- !display_rect_changed) {
- gfx::Rect occluding_damage_rect = *damage_rect;
- occluding_damage_rect.Intersect(quad_rectangle);
- damage_rect->Subtract(quad_rectangle);
- gfx::Rect new_occlusion_bounding_box =
- gfx::ToEnclosingRect(occlusion_bounding_box);
- new_occlusion_bounding_box.Union(previous_occlusion_bounding_box_);
- occluding_damage_rect.Intersect(new_occlusion_bounding_box);
-
- damage_rect->Union(occluding_damage_rect);
- }
+ bool display_rect_changed = (display_rect != previous_display_rect_);
+ bool underlay_rect_changed =
+ (quad_rectangle != previous_frame_underlay_rect_);
+ bool is_axis_aligned =
+ shared_quad_state->quad_to_target_transform.Preserves2dAxisAlignment();
+
+ if (is_root && !processed_overlay_in_frame_ && is_axis_aligned && is_opaque &&
+ !underlay_rect_changed && !display_rect_changed) {
+ // If this underlay rect is the same as for last frame, subtract its area
+ // from the damage of the main surface, as the cleared area was already
+ // cleared last frame. Add back the damage from the occluded area for this
+ // and last frame, as that may have changed.
+ gfx::Rect occluding_damage_rect = *damage_rect;
+ damage_rect->Subtract(quad_rectangle);
+
+ gfx::Rect occlusion = gfx::ToEnclosingRect(occlusion_bounding_box);
+ occlusion.Union(previous_frame_underlay_occlusion_);
+
+ occluding_damage_rect.Intersect(quad_rectangle);
+ occluding_damage_rect.Intersect(occlusion);
+
+ damage_rect->Union(occluding_damage_rect);
} else {
// Entire replacement quad must be redrawn.
+ // TODO(sunnyps): We should avoid this extra damage if we knew that the
+ // video was the only thing damaging this render surface.
damage_rect->Union(quad_rectangle);
}
- PunchThroughRect info;
- info.rect = gfx::ToEnclosingRect(dc_layer->bounds_rect);
- info.transform_to_target = shared_quad_state->quad_to_target_transform;
- info.opacity = shared_quad_state->opacity;
- pass_info_[render_pass->id].push_back(info);
-
- previous_occlusion_bounding_box_ =
- gfx::ToEnclosingRect(occlusion_bounding_box);
+
+ // We only compare current frame's first root pass underlay with the previous
+ // frame's first root pass underlay. Non-opaque regions can have different
+ // alpha from one frame to another so this optimization doesn't work.
+ if (is_root && !processed_overlay_in_frame_ && is_axis_aligned && is_opaque) {
+ *this_frame_underlay_rect = quad_rectangle;
+ *this_frame_underlay_occlusion =
+ gfx::ToEnclosingRect(occlusion_bounding_box);
+ }
+
+ // Propagate the punched holes up the chain of render passes. Punch through
+ // rects are in quad target (child render pass) space, and are transformed to
+ // RPDQ target (parent render pass) in ProcessRenderPassDrawQuad().
+ pass_punch_through_rects_[render_pass->id].push_back(
+ gfx::ToEnclosingRect(ClippedQuadRectangle(*it)));
+
return true;
}
diff --git a/chromium/components/viz/service/display/dc_layer_overlay.h b/chromium/components/viz/service/display/dc_layer_overlay.h
index 34d118dccd0..73162d45280 100644
--- a/chromium/components/viz/service/display/dc_layer_overlay.h
+++ b/chromium/components/viz/service/display/dc_layer_overlay.h
@@ -102,7 +102,7 @@ class DCLayerOverlayProcessor {
DCLayerOverlayList* ca_layer_overlays);
void ClearOverlayState() {
previous_frame_underlay_rect_ = gfx::Rect();
- previous_occlusion_bounding_box_ = gfx::Rect();
+ previous_frame_underlay_occlusion_ = gfx::Rect();
}
private:
@@ -136,23 +136,21 @@ class DCLayerOverlayProcessor {
bool is_root,
gfx::Rect* damage_rect,
gfx::Rect* this_frame_underlay_rect,
+ gfx::Rect* this_frame_underlay_occlusion,
DCLayerOverlay* dc_layer);
gfx::Rect previous_frame_underlay_rect_;
- gfx::Rect previous_occlusion_bounding_box_;
+ gfx::Rect previous_frame_underlay_occlusion_;
gfx::RectF previous_display_rect_;
bool processed_overlay_in_frame_ = false;
- // Store information about punch-through rectangles for non-root
- // RenderPasses. These rectangles are used to clear the corresponding areas
- // in parent renderpasses.
- struct PunchThroughRect {
- gfx::Rect rect;
- gfx::Transform transform_to_target;
- float opacity;
- };
+ // Store information about clipped punch-through rects in target space for
+ // non-root render passes. These rects are used to clear the corresponding
+ // areas in parent render passes.
+ base::flat_map<RenderPassId, std::vector<gfx::Rect>>
+ pass_punch_through_rects_;
- base::flat_map<RenderPassId, std::vector<PunchThroughRect>> pass_info_;
+ DISALLOW_COPY_AND_ASSIGN(DCLayerOverlayProcessor);
};
} // namespace viz
diff --git a/chromium/components/viz/service/display/direct_renderer.cc b/chromium/components/viz/service/display/direct_renderer.cc
index 2e79ba0f068..e9ca0df5ffd 100644
--- a/chromium/components/viz/service/display/direct_renderer.cc
+++ b/chromium/components/viz/service/display/direct_renderer.cc
@@ -211,9 +211,8 @@ void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order,
DCHECK(root_render_pass);
bool overdraw_tracing_enabled;
- TRACE_EVENT_CATEGORY_GROUP_ENABLED(
- TRACE_DISABLED_BY_DEFAULT("cc.debug.overdraw"),
- &overdraw_tracing_enabled);
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("viz.overdraw"),
+ &overdraw_tracing_enabled);
bool overdraw_feedback =
settings_->show_overdraw_feedback || overdraw_tracing_enabled;
if (overdraw_feedback && !output_surface_->capabilities().supports_stencil) {
@@ -328,22 +327,21 @@ void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order,
if (!skip_drawing_root_render_pass && !use_partial_swap_)
current_frame()->root_damage_rect = gfx::Rect(device_viewport_size);
- if (!skip_drawing_root_render_pass) {
+ if (!skip_drawing_root_render_pass)
DrawRenderPassAndExecuteCopyRequests(root_render_pass);
- // Use a fence to synchronize display of the surface overlay. 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(crbug.com/840805): We currently only use fences for root render
- // passes but we may also need to use fences for non-root passes in some
- // cases (e.g. WebGL canvas).
+
+ // Use a fence to synchronize display of the overlays. 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. We are currently using the output surface fence for all the overlays,
+ // which is functionally correct due to the position of this fence in the
+ // command stream.
+ // TODO(afrantzis): Consider using per-overlay fences instead of the one
+ // associated with the output surface when possible.
+ if (!current_frame()->overlay_list.empty()) {
auto gpu_fence_id = output_surface_->UpdateGpuFence();
- for (auto& overlay : current_frame()->overlay_list) {
- if (overlay.use_output_surface_for_resource) {
- overlay.gpu_fence_id = gpu_fence_id;
- break;
- }
- }
+ for (auto& overlay : current_frame()->overlay_list)
+ overlay.gpu_fence_id = gpu_fence_id;
}
FinishDrawingFrame();
diff --git a/chromium/components/viz/service/display/display.cc b/chromium/components/viz/service/display/display.cc
index 178708effd2..591b7390c8e 100644
--- a/chromium/components/viz/service/display/display.cc
+++ b/chromium/components/viz/service/display/display.cc
@@ -61,9 +61,13 @@ Display::Display(
}
Display::~Display() {
+ for (auto& observer : observers_)
+ observer.OnDisplayDestroyed();
+ observers_.Clear();
+
for (auto& callback_list : pending_presented_callbacks_) {
for (auto& callback : callback_list)
- std::move(callback).Run(base::TimeTicks(), base::TimeDelta(), 0);
+ std::move(callback).Run(gfx::PresentationFeedback::Failure());
}
// Only do this if Initialize() happened.
@@ -205,24 +209,28 @@ void Display::SetOutputIsSecure(bool secure) {
}
void Display::InitializeRenderer() {
+ auto mode = output_surface_->context_provider() || skia_output_surface_
+ ? DisplayResourceProvider::kGpu
+ : DisplayResourceProvider::kSoftware;
resource_provider_ = std::make_unique<DisplayResourceProvider>(
- output_surface_->context_provider(), bitmap_manager_);
+ mode, output_surface_->context_provider(), bitmap_manager_);
- if (output_surface_->context_provider()) {
- if (!settings_.use_skia_renderer) {
- renderer_ = std::make_unique<GLRenderer>(
- &settings_, output_surface_.get(), resource_provider_.get(),
- current_task_runner_);
- } else {
- DCHECK(output_surface_);
- renderer_ = std::make_unique<SkiaRenderer>(
- &settings_, output_surface_.get(), resource_provider_.get(),
- skia_output_surface_);
- }
+ if (settings_.use_skia_renderer && mode == DisplayResourceProvider::kGpu) {
+ // Check the compositing mode, because SkiaRenderer only works with GPU
+ // compositing.
+ DCHECK(output_surface_);
+ renderer_ = std::make_unique<SkiaRenderer>(
+ &settings_, output_surface_.get(), resource_provider_.get(),
+ skia_output_surface_);
+ } else if (output_surface_->context_provider()) {
+ renderer_ = std::make_unique<GLRenderer>(&settings_, output_surface_.get(),
+ resource_provider_.get(),
+ current_task_runner_);
#if BUILDFLAG(ENABLE_VULKAN)
} else if (output_surface_->vulkan_context_provider()) {
renderer_ = std::make_unique<SkiaRenderer>(
- &settings_, output_surface_.get(), resource_provider_.get());
+ &settings_, output_surface_.get(), resource_provider_.get(),
+ nullptr /* skia_output_surface */);
#endif
} else {
auto renderer = std::make_unique<SoftwareRenderer>(
@@ -286,11 +294,12 @@ bool Display::DrawAndSwap() {
}
// Run callbacks early to allow pipelining and collect presented callbacks.
- for (const auto& id_entry : aggregator_->previous_contained_surfaces()) {
- Surface* surface = surface_manager_->GetSurfaceForId(id_entry.first);
+ for (const auto& surface_id : surfaces_to_ack_on_next_draw_) {
+ Surface* surface = surface_manager_->GetSurfaceForId(surface_id);
if (surface)
surface->RunDrawCallback();
}
+ surfaces_to_ack_on_next_draw_.clear();
frame.metadata.latency_info.insert(frame.metadata.latency_info.end(),
stored_latency_info_.begin(),
@@ -367,7 +376,7 @@ bool Display::DrawAndSwap() {
if (scheduler_) {
frame.metadata.latency_info.emplace_back(ui::SourceEventType::FRAME);
frame.metadata.latency_info.back().AddLatencyNumberWithTimestamp(
- ui::LATENCY_BEGIN_FRAME_DISPLAY_COMPOSITOR_COMPONENT, 0,
+ ui::LATENCY_BEGIN_FRAME_DISPLAY_COMPOSITOR_COMPONENT,
scheduler_->current_frame_time(), 1);
}
@@ -404,17 +413,10 @@ bool Display::DrawAndSwap() {
stored_latency_info_.swap(frame.metadata.latency_info);
}
} else {
- // There was no damage, so tracking latency info at this point isn't
- // useful unless there's a snapshot request.
- base::TimeTicks now = base::TimeTicks::Now();
+ // There was no damage. Terminate the latency info objects.
while (!frame.metadata.latency_info.empty()) {
auto& latency = frame.metadata.latency_info.back();
- if (latency.Snapshots().size()) {
- stored_latency_info_.push_back(std::move(latency));
- } else {
- latency.AddLatencyNumberWithTimestamp(
- ui::INPUT_EVENT_LATENCY_TERMINATED_NO_SWAP_COMPONENT, 0, now, 1);
- }
+ latency.Terminate();
frame.metadata.latency_info.pop_back();
}
}
@@ -454,29 +456,23 @@ void Display::DidReceiveCALayerParams(
client_->DisplayDidReceiveCALayerParams(ca_layer_params);
}
+void Display::DidSwapWithSize(const gfx::Size& pixel_size) {
+ if (client_)
+ client_->DisplayDidCompleteSwapWithSize(pixel_size);
+}
+
void Display::DidReceivePresentationFeedback(
const gfx::PresentationFeedback& feedback) {
DCHECK(!pending_presented_callbacks_.empty());
auto& callbacks = pending_presented_callbacks_.front();
for (auto& callback : callbacks) {
- std::move(callback).Run(feedback.timestamp, feedback.interval,
- feedback.flags);
+ std::move(callback).Run(feedback);
}
pending_presented_callbacks_.pop_front();
}
void Display::DidFinishLatencyInfo(
const std::vector<ui::LatencyInfo>& latency_info) {
- std::vector<ui::LatencyInfo> latency_info_with_snapshot_component;
- for (const auto& latency : latency_info) {
- if (latency.Snapshots().size())
- latency_info_with_snapshot_component.push_back(latency);
- }
-
- if (!latency_info_with_snapshot_component.empty()) {
- client_->DidSwapAfterSnapshotRequestReceived(
- latency_info_with_snapshot_component);
- }
}
void Display::SetNeedsRedrawRect(const gfx::Rect& damage_rect) {
@@ -490,25 +486,19 @@ void Display::SetNeedsRedrawRect(const gfx::Rect& damage_rect) {
bool Display::SurfaceDamaged(const SurfaceId& surface_id,
const BeginFrameAck& ack) {
+ if (!ack.has_damage)
+ return false;
bool display_damaged = false;
- if (ack.has_damage) {
- if (aggregator_ &&
- aggregator_->previous_contained_surfaces().count(surface_id)) {
- Surface* surface = surface_manager_->GetSurfaceForId(surface_id);
- if (surface) {
- DCHECK(surface->HasActiveFrame());
- if (surface->GetActiveFrame().resource_list.empty())
- aggregator_->ReleaseResources(surface_id);
- }
- display_damaged = true;
- if (surface_id == current_surface_id_)
- UpdateRootSurfaceResourcesLocked();
- } else if (surface_id == current_surface_id_) {
- display_damaged = true;
- UpdateRootSurfaceResourcesLocked();
- }
+ if (aggregator_) {
+ display_damaged |=
+ aggregator_->NotifySurfaceDamageAndCheckForDisplayDamage(surface_id);
}
-
+ if (surface_id == current_surface_id_) {
+ display_damaged = true;
+ UpdateRootSurfaceResourcesLocked();
+ }
+ if (display_damaged)
+ surfaces_to_ack_on_next_draw_.push_back(surface_id);
return display_damaged;
}
diff --git a/chromium/components/viz/service/display/display.h b/chromium/components/viz/service/display/display.h
index 51312be163e..c88a5f71b84 100644
--- a/chromium/components/viz/service/display/display.h
+++ b/chromium/components/viz/service/display/display.h
@@ -48,6 +48,7 @@ class VIZ_SERVICE_EXPORT DisplayObserver {
virtual ~DisplayObserver() {}
virtual void OnDisplayDidFinishFrame(const BeginFrameAck& ack) = 0;
+ virtual void OnDisplayDestroyed() = 0;
};
// A Display produces a surface that can be used to draw to a physical display
@@ -111,6 +112,7 @@ class VIZ_SERVICE_EXPORT Display : public DisplaySchedulerClient,
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 DidFinishLatencyInfo(
@@ -164,6 +166,7 @@ class VIZ_SERVICE_EXPORT Display : public DisplaySchedulerClient,
std::unique_ptr<DirectRenderer> renderer_;
SoftwareRenderer* software_renderer_ = nullptr;
std::vector<ui::LatencyInfo> stored_latency_info_;
+ std::vector<SurfaceId> surfaces_to_ack_on_next_draw_;
base::circular_deque<std::vector<Surface::PresentedCallback>>
pending_presented_callbacks_;
diff --git a/chromium/components/viz/service/display/display_client.h b/chromium/components/viz/service/display/display_client.h
index 9734040a581..dab44d6bcfe 100644
--- a/chromium/components/viz/service/display/display_client.h
+++ b/chromium/components/viz/service/display/display_client.h
@@ -26,6 +26,7 @@ class DisplayClient {
virtual void DisplayDidDrawAndSwap() = 0;
virtual void DisplayDidReceiveCALayerParams(
const gfx::CALayerParams& ca_layer_params) = 0;
+ virtual void DisplayDidCompleteSwapWithSize(const gfx::Size& pixel_size) = 0;
// Notifies that a swap has occured after some latency info with snapshot
// component reached the display.
diff --git a/chromium/components/viz/service/display/display_perftest.cc b/chromium/components/viz/service/display/display_perftest.cc
index ab7d01990c7..8368e0e11e0 100644
--- a/chromium/components/viz/service/display/display_perftest.cc
+++ b/chromium/components/viz/service/display/display_perftest.cc
@@ -13,11 +13,11 @@
#include "components/viz/common/quads/draw_quad.h"
#include "components/viz/common/quads/render_pass.h"
#include "components/viz/common/quads/texture_draw_quad.h"
-#include "components/viz/common/resources/shared_bitmap_manager.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/service/display/display.h"
#include "components/viz/service/display/display_scheduler.h"
#include "components/viz/service/display/output_surface.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 "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/components/viz/service/display/display_resource_provider.cc b/chromium/components/viz/service/display/display_resource_provider.cc
index 2b78a9662b5..e3d29aa99e5 100644
--- a/chromium/components/viz/service/display/display_resource_provider.cc
+++ b/chromium/components/viz/service/display/display_resource_provider.cc
@@ -6,6 +6,7 @@
#include "base/atomic_sequence_num.h"
#include "base/numerics/safe_math.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/memory_dump_manager.h"
@@ -13,7 +14,7 @@
#include "components/viz/common/gpu/context_provider.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/common/resources/resource_sizes.h"
-#include "components/viz/common/resources/shared_bitmap_manager.h"
+#include "components/viz/service/display/shared_bitmap_manager.h"
#include "gpu/command_buffer/client/context_support.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
@@ -30,17 +31,15 @@ base::AtomicSequenceNumber g_next_display_resource_provider_tracing_id;
} // namespace
-static GLint GetActiveTextureUnit(GLES2Interface* gl) {
- GLint active_unit = 0;
- gl->GetIntegerv(GL_ACTIVE_TEXTURE, &active_unit);
- return active_unit;
-}
-
class ScopedSetActiveTexture {
public:
ScopedSetActiveTexture(GLES2Interface* gl, GLenum unit)
: gl_(gl), unit_(unit) {
- DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_));
+#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_);
@@ -58,12 +57,17 @@ class ScopedSetActiveTexture {
};
DisplayResourceProvider::DisplayResourceProvider(
+ Mode mode,
ContextProvider* compositor_context_provider,
SharedBitmapManager* shared_bitmap_manager)
- : compositor_context_provider_(compositor_context_provider),
+ : mode_(mode),
+ compositor_context_provider_(compositor_context_provider),
shared_bitmap_manager_(shared_bitmap_manager),
tracing_id_(g_next_display_resource_provider_tracing_id.GetNext()) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ // If no ContextProvider, then we are doing software compositing and a
+ // SharedBitmapManager must be given.
+ DCHECK(mode_ == kGpu || shared_bitmap_manager);
// In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview).
// Don't register a dump provider in these cases.
@@ -104,14 +108,10 @@ bool DisplayResourceProvider::OnMemoryDump(
const auto& resource = resource_entry.second;
bool backing_memory_allocated = false;
- switch (resource.type) {
- case ResourceType::kTexture:
- backing_memory_allocated = !!resource.gl_id;
- break;
- case ResourceType::kBitmap:
- backing_memory_allocated = !!resource.shared_bitmap;
- break;
- }
+ if (resource.transferable.is_software)
+ backing_memory_allocated = !!resource.shared_bitmap;
+ else
+ backing_memory_allocated = !!resource.gl_id;
if (!backing_memory_allocated) {
// Don't log unallocated resources - they have no backing memory.
@@ -128,9 +128,9 @@ bool DisplayResourceProvider::OnMemoryDump(
// Texture resources may not come with a size, in which case don't report
// one.
- if (!resource.size.IsEmpty()) {
+ if (!resource.transferable.size.IsEmpty()) {
uint64_t total_bytes = ResourceSizes::UncheckedSizeInBytesAligned<size_t>(
- resource.size, resource.format);
+ resource.transferable.size, resource.transferable.format);
dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
base::trace_event::MemoryAllocatorDump::kUnitsBytes,
static_cast<uint64_t>(total_bytes));
@@ -140,22 +140,13 @@ bool DisplayResourceProvider::OnMemoryDump(
// prevent double counting the memory.
base::trace_event::MemoryAllocatorDumpGuid guid;
base::UnguessableToken shared_memory_guid;
- switch (resource.type) {
- case ResourceType::kTexture:
- DCHECK(resource.gl_id);
- guid = gl::GetGLTextureClientGUIDForTracing(
- compositor_context_provider_->ContextSupport()
- ->ShareGroupTracingGUID(),
- resource.gl_id);
- break;
- case ResourceType::kBitmap:
- // If the resource comes from out of process, it will have this id,
- // which we prefer. Otherwise, we fall back to the SharedBitmapGUID
- // which can be generated for in-process bitmaps.
- shared_memory_guid = resource.shared_bitmap->GetCrossProcessGUID();
- if (shared_memory_guid.is_empty())
- guid = GetSharedBitmapGUIDForTracing(resource.shared_bitmap_id);
- break;
+ if (resource.transferable.is_software) {
+ shared_memory_guid = resource.shared_bitmap_tracing_guid;
+ } else {
+ guid = gl::GetGLTextureClientGUIDForTracing(
+ compositor_context_provider_->ContextSupport()
+ ->ShareGroupTracingGUID(),
+ resource.gl_id);
}
DCHECK(!shared_memory_guid.is_empty() || !guid.empty());
@@ -190,14 +181,14 @@ void DisplayResourceProvider::SendPromotionHints(
if (it->second.marked_for_deletion)
continue;
- const internal::Resource* resource = LockForRead(id);
+ const ChildResource* resource = LockForRead(id);
// 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;
- DCHECK(resource->wants_promotion_hint);
+ DCHECK(resource->transferable.wants_promotion_hint);
// Insist that this is backed by a GPU texture.
if (resource->is_gpu_resource_type()) {
@@ -215,8 +206,8 @@ void DisplayResourceProvider::SendPromotionHints(
}
bool DisplayResourceProvider::IsBackedBySurfaceTexture(ResourceId id) {
- internal::Resource* resource = GetResource(id);
- return resource->is_backed_by_surface_texture;
+ ChildResource* resource = GetResource(id);
+ return resource->transferable.is_backed_by_surface_texture;
}
bool DisplayResourceProvider::WantsPromotionHintForTesting(ResourceId id) {
@@ -229,28 +220,28 @@ size_t DisplayResourceProvider::CountPromotionHintRequestsForTesting() {
#endif
bool DisplayResourceProvider::IsOverlayCandidate(ResourceId id) {
- internal::Resource* resource = TryGetResource(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
- return resource && resource->is_overlay_candidate;
+ return resource && resource->transferable.is_overlay_candidate;
}
-ResourceType DisplayResourceProvider::GetResourceType(ResourceId id) {
- return GetResource(id)->type;
+bool DisplayResourceProvider::IsResourceSoftwareBacked(ResourceId id) {
+ return GetResource(id)->transferable.is_software;
}
GLenum DisplayResourceProvider::GetResourceTextureTarget(ResourceId id) {
- return GetResource(id)->target;
+ return GetResource(id)->transferable.mailbox_holder.texture_target;
}
gfx::BufferFormat DisplayResourceProvider::GetBufferFormat(ResourceId id) {
- internal::Resource* resource = GetResource(id);
- return resource->buffer_format;
+ ChildResource* resource = GetResource(id);
+ return BufferFormat(resource->transferable.format);
}
void DisplayResourceProvider::WaitSyncToken(ResourceId id) {
- internal::Resource* resource = TryGetResource(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
@@ -261,7 +252,7 @@ void DisplayResourceProvider::WaitSyncToken(ResourceId id) {
// Now that the resource is synced, we may send it a promotion hint. We could
// sync all |wants_promotion_hint| resources elsewhere, and send 'no' to all
// resources that weren't used. However, there's no real advantage.
- if (resource->wants_promotion_hint)
+ if (resource->transferable.wants_promotion_hint)
wants_promotion_hints_set_.insert(id);
#endif // OS_ANDROID
}
@@ -292,26 +283,33 @@ void DisplayResourceProvider::DestroyChild(int child_id) {
}
void DisplayResourceProvider::ReceiveFromChild(
- int child,
+ int child_id,
const std::vector<TransferableResource>& resources) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- GLES2Interface* gl = ContextGL();
- Child& child_info = children_.find(child)->second;
+
+ // TODO(crbug.com/855785): Fishing for misuse of DisplayResourceProvider
+ // causing crashes.
+ CHECK(child_id);
+ auto child_it = children_.find(child_id);
+ // TODO(crbug.com/855785): Fishing for misuse of DisplayResourceProvider
+ // causing crashes.
+ CHECK(child_it != children_.end());
+ Child& child_info = child_it->second;
DCHECK(!child_info.marked_for_deletion);
for (std::vector<TransferableResource>::const_iterator it = resources.begin();
it != resources.end(); ++it) {
auto resource_in_map_it = child_info.child_to_parent_map.find(it->id);
if (resource_in_map_it != child_info.child_to_parent_map.end()) {
- internal::Resource* resource = GetResource(resource_in_map_it->second);
+ ChildResource* resource = GetResource(resource_in_map_it->second);
resource->marked_for_deletion = false;
resource->imported_count++;
continue;
}
- if ((!it->is_software && !gl) ||
- (it->is_software && !shared_bitmap_manager_)) {
+ if (it->is_software != IsSoftware() ||
+ it->mailbox_holder.mailbox.IsZero()) {
TRACE_EVENT0(
- "cc", "DisplayResourceProvider::ReceiveFromChild dropping invalid");
+ "viz", "DisplayResourceProvider::ReceiveFromChild dropping invalid");
std::vector<ReturnedResource> to_return;
to_return.push_back(it->ToReturnedResource());
child_info.return_callback.Run(to_return);
@@ -319,35 +317,12 @@ void DisplayResourceProvider::ReceiveFromChild(
}
ResourceId local_id = next_id_++;
- internal::Resource* resource = nullptr;
if (it->is_software) {
DCHECK(IsBitmapFormatSupported(it->format));
- resource = InsertResource(
- local_id, internal::Resource(it->size, ResourceType::kBitmap,
- it->format, it->color_space));
- resource->has_shared_bitmap_id = true;
- resource->shared_bitmap_id = it->mailbox_holder.mailbox;
+ InsertResource(local_id, ChildResource(child_id, *it));
} else {
- resource = InsertResource(
- local_id, internal::Resource(it->size, ResourceType::kTexture,
- it->format, it->color_space));
- resource->target = it->mailbox_holder.texture_target;
- resource->filter = it->filter;
- resource->original_filter = it->filter;
- resource->min_filter = it->filter;
- resource->buffer_format = it->buffer_format;
- resource->mailbox = it->mailbox_holder.mailbox;
- resource->UpdateSyncToken(it->mailbox_holder.sync_token);
- resource->read_lock_fences_enabled = it->read_lock_fences_enabled;
- resource->is_overlay_candidate = it->is_overlay_candidate;
-#if defined(OS_ANDROID)
- resource->is_backed_by_surface_texture = it->is_backed_by_surface_texture;
- resource->wants_promotion_hint = it->wants_promotion_hint;
-#endif
+ InsertResource(local_id, ChildResource(child_id, *it));
}
- resource->child_id = child;
- resource->imported_count = 1;
- resource->id_in_child = it->id;
child_info.child_to_parent_map[it->id] = local_id;
}
}
@@ -357,8 +332,13 @@ void DisplayResourceProvider::DeclareUsedResourcesFromChild(
const ResourceIdSet& resources_from_child) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ // TODO(crbug.com/855785): Fishing for misuse of DisplayResourceProvider
+ // causing crashes.
+ CHECK(child);
auto child_it = children_.find(child);
- DCHECK(child_it != children_.end());
+ // TODO(crbug.com/855785): Fishing for misuse of DisplayResourceProvider
+ // causing crashes.
+ CHECK(child_it != children_.end());
Child& child_info = child_it->second;
DCHECK(!child_info.marked_for_deletion);
@@ -383,20 +363,21 @@ DisplayResourceProvider::GetChildToParentMap(int child) const {
}
bool DisplayResourceProvider::InUse(ResourceId id) {
- internal::Resource* resource = GetResource(id);
+ ChildResource* resource = GetResource(id);
return resource->lock_for_read_count > 0 || resource->locked_for_external_use;
}
-internal::Resource* DisplayResourceProvider::InsertResource(
+DisplayResourceProvider::ChildResource* DisplayResourceProvider::InsertResource(
ResourceId id,
- internal::Resource resource) {
+ ChildResource resource) {
auto result =
resources_.insert(ResourceMap::value_type(id, std::move(resource)));
DCHECK(result.second);
return &result.first->second;
}
-internal::Resource* DisplayResourceProvider::GetResource(ResourceId id) {
+DisplayResourceProvider::ChildResource* DisplayResourceProvider::GetResource(
+ ResourceId id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(id);
auto it = resources_.find(id);
@@ -404,7 +385,8 @@ internal::Resource* DisplayResourceProvider::GetResource(ResourceId id) {
return &it->second;
}
-internal::Resource* DisplayResourceProvider::TryGetResource(ResourceId id) {
+DisplayResourceProvider::ChildResource* DisplayResourceProvider::TryGetResource(
+ ResourceId id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!id)
return nullptr;
@@ -416,39 +398,31 @@ internal::Resource* DisplayResourceProvider::TryGetResource(ResourceId id) {
void DisplayResourceProvider::PopulateSkBitmapWithResource(
SkBitmap* sk_bitmap,
- const internal::Resource* resource) {
- DCHECK(IsBitmapFormatSupported(resource->format));
- SkImageInfo info = SkImageInfo::MakeN32Premul(resource->size.width(),
- resource->size.height());
- bool pixels_installed =
- sk_bitmap->installPixels(info, resource->pixels, info.minRowBytes());
+ const ChildResource* resource) {
+ DCHECK(IsBitmapFormatSupported(resource->transferable.format));
+ SkImageInfo info =
+ SkImageInfo::MakeN32Premul(resource->transferable.size.width(),
+ resource->transferable.size.height());
+ bool pixels_installed = sk_bitmap->installPixels(
+ info, resource->shared_bitmap->pixels(), info.minRowBytes());
DCHECK(pixels_installed);
}
void DisplayResourceProvider::DeleteResourceInternal(ResourceMap::iterator it,
DeleteStyle style) {
- TRACE_EVENT0("cc", "DosplayResourceProvider::DeleteResourceInternal");
- internal::Resource* resource = &it->second;
+ TRACE_EVENT0("viz", "DosplayResourceProvider::DeleteResourceInternal");
+ ChildResource* resource = &it->second;
if (resource->gl_id) {
GLES2Interface* gl = ContextGL();
DCHECK(gl);
gl->DeleteTextures(1, &resource->gl_id);
- resource->gl_id = 0;
- }
-
- if (resource->owned_shared_bitmap) {
- DCHECK_EQ(ResourceType::kBitmap, resource->type);
- resource->shared_bitmap = nullptr;
- resource->pixels = nullptr;
- resource->owned_shared_bitmap = nullptr;
}
resources_.erase(it);
}
-void DisplayResourceProvider::WaitSyncTokenInternal(
- internal::Resource* resource) {
+void DisplayResourceProvider::WaitSyncTokenInternal(ChildResource* resource) {
DCHECK(resource);
if (!resource->ShouldWaitSyncToken())
return;
@@ -467,41 +441,44 @@ GLES2Interface* DisplayResourceProvider::ContextGL() const {
return context_provider ? context_provider->ContextGL() : nullptr;
}
-const internal::Resource* DisplayResourceProvider::LockForRead(ResourceId id) {
+const DisplayResourceProvider::ChildResource*
+DisplayResourceProvider::LockForRead(ResourceId 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
- internal::Resource* resource = TryGetResource(id);
+ 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(internal::Resource::NEEDS_WAIT, resource->synchronization_state());
+ DCHECK_NE(NEEDS_WAIT, resource->synchronization_state());
if (resource->is_gpu_resource_type() && !resource->gl_id) {
- DCHECK(!resource->mailbox.IsZero());
-
GLES2Interface* gl = ContextGL();
DCHECK(gl);
- resource->gl_id =
- gl->CreateAndConsumeTextureCHROMIUM(resource->mailbox.name);
+ resource->gl_id = gl->CreateAndConsumeTextureCHROMIUM(
+ resource->transferable.mailbox_holder.mailbox.name);
resource->SetLocallyUsed();
}
- if (!resource->pixels && resource->has_shared_bitmap_id &&
- shared_bitmap_manager_) {
+ if (!resource->shared_bitmap && !resource->is_gpu_resource_type()) {
+ const SharedBitmapId& shared_bitmap_id =
+ resource->transferable.mailbox_holder.mailbox;
std::unique_ptr<SharedBitmap> bitmap =
shared_bitmap_manager_->GetSharedBitmapFromId(
- resource->size, resource->format, resource->shared_bitmap_id);
+ resource->transferable.size, resource->transferable.format,
+ shared_bitmap_id);
if (bitmap) {
- resource->SetSharedBitmap(bitmap.get());
- resource->owned_shared_bitmap = std::move(bitmap);
+ resource->shared_bitmap = std::move(bitmap);
+ resource->shared_bitmap_tracing_guid =
+ shared_bitmap_manager_->GetSharedBitmapTracingGUIDFromId(
+ shared_bitmap_id);
}
}
resource->lock_for_read_count++;
- if (resource->read_lock_fences_enabled) {
+ 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_;
@@ -519,7 +496,7 @@ void DisplayResourceProvider::UnlockForRead(ResourceId id) {
if (it == resources_.end())
return;
- internal::Resource* resource = &it->second;
+ ChildResource* resource = &it->second;
DCHECK_GT(resource->lock_for_read_count, 0);
resource->lock_for_read_count--;
TryReleaseResource(it);
@@ -530,7 +507,7 @@ ResourceMetadata DisplayResourceProvider::LockForExternalUse(ResourceId id) {
auto it = resources_.find(id);
DCHECK(it != resources_.end());
- internal::Resource* resource = &it->second;
+ ChildResource* resource = &it->second;
ResourceMetadata metadata;
// Make sure there is no outstanding LockForExternalUse without calling
// UnlockForExternalUse.
@@ -538,14 +515,15 @@ ResourceMetadata DisplayResourceProvider::LockForExternalUse(ResourceId id) {
// TODO(penghuang): support software resource.
DCHECK(resource->is_gpu_resource_type());
- metadata.mailbox = resource->mailbox;
+ metadata.mailbox = resource->transferable.mailbox_holder.mailbox;
metadata.backend_format = GrBackendFormat::MakeGL(
- TextureStorageFormat(resource->format), resource->target);
- metadata.size = resource->size;
+ TextureStorageFormat(resource->transferable.format),
+ resource->transferable.mailbox_holder.texture_target);
+ metadata.size = resource->transferable.size;
metadata.mip_mapped = GrMipMapped::kNo;
metadata.origin = kTopLeft_GrSurfaceOrigin;
- metadata.color_type =
- ResourceFormatToClosestSkColorType(!IsSoftware(), resource->format);
+ metadata.color_type = ResourceFormatToClosestSkColorType(
+ !IsSoftware(), resource->transferable.format);
metadata.alpha_type = kPremul_SkAlphaType;
metadata.color_space = nullptr;
metadata.sync_token = resource->sync_token();
@@ -562,7 +540,7 @@ void DisplayResourceProvider::UnlockForExternalUse(
DCHECK(it != resources_.end());
DCHECK(sync_token.verified_flush());
- internal::Resource* resource = &it->second;
+ ChildResource* resource = &it->second;
DCHECK(resource->locked_for_external_use);
// TODO(penghuang): support software resource.
DCHECK(resource->is_gpu_resource_type());
@@ -581,25 +559,16 @@ void DisplayResourceProvider::UnlockForExternalUse(
void DisplayResourceProvider::TryReleaseResource(ResourceMap::iterator it) {
ResourceId id = it->first;
- internal::Resource* resource = &it->second;
+ ChildResource* resource = &it->second;
if (resource->marked_for_deletion && !resource->lock_for_read_count &&
!resource->locked_for_external_use) {
- if (!resource->child_id) {
-// The resource belongs to this instance, so it can be destroyed.
-// TODO(danakj): Is this dead code?
-#if defined(OS_ANDROID)
- DeletePromotionHint(it, NORMAL);
-#endif
- DeleteResourceInternal(it, NORMAL);
+ if (batch_return_resources_) {
+ batched_returning_resources_[resource->child_id].push_back(id);
} else {
- if (batch_return_resources_) {
- batched_returning_resources_[resource->child_id].push_back(id);
- } else {
- auto child_it = children_.find(resource->child_id);
- std::vector<ResourceId> unused;
- unused.push_back(id);
- DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, unused);
- }
+ auto child_it = children_.find(resource->child_id);
+ std::vector<ResourceId> unused;
+ unused.push_back(id);
+ DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, unused);
}
}
}
@@ -616,18 +585,14 @@ GLenum DisplayResourceProvider::BindForSampling(ResourceId resource_id,
if (it == resources_.end())
return GL_TEXTURE_2D;
- internal::Resource* resource = &it->second;
+ ChildResource* resource = &it->second;
DCHECK(resource->lock_for_read_count);
ScopedSetActiveTexture scoped_active_tex(gl, unit);
- GLenum target = resource->target;
+ GLenum target = resource->transferable.mailbox_holder.texture_target;
gl->BindTexture(target, resource->gl_id);
- GLenum min_filter = filter;
- if (min_filter != resource->min_filter) {
- gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filter);
- resource->min_filter = min_filter;
- }
if (filter != resource->filter) {
+ gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
resource->filter = filter;
}
@@ -636,17 +601,17 @@ GLenum DisplayResourceProvider::BindForSampling(ResourceId resource_id,
}
bool DisplayResourceProvider::ReadLockFenceHasPassed(
- const internal::Resource* resource) {
+ const ChildResource* resource) {
return !resource->read_lock_fence || resource->read_lock_fence->HasPassed();
}
#if defined(OS_ANDROID)
void DisplayResourceProvider::DeletePromotionHint(ResourceMap::iterator it,
DeleteStyle style) {
- internal::Resource* resource = &it->second;
+ ChildResource* resource = &it->second;
// If this resource was interested in promotion hints, then remove it from
// the set of resources that we'll notify.
- if (resource->wants_promotion_hint)
+ if (resource->transferable.wants_promotion_hint)
wants_promotion_hints_set_.erase(it->first);
}
#endif
@@ -673,9 +638,9 @@ void DisplayResourceProvider::DeleteAndReturnUnusedResourcesToChild(
for (ResourceId local_id : unused) {
auto it = resources_.find(local_id);
CHECK(it != resources_.end());
- internal::Resource& resource = it->second;
+ ChildResource& resource = it->second;
- ResourceId child_id = resource.id_in_child;
+ ResourceId child_id = resource.transferable.id;
DCHECK(child_info->child_to_parent_map.count(child_id));
bool is_lost = (resource.is_gpu_resource_type() && lost_context_provider_);
@@ -700,15 +665,16 @@ void DisplayResourceProvider::DeleteAndReturnUnusedResourcesToChild(
}
if (resource.is_gpu_resource_type() &&
- resource.filter != resource.original_filter) {
- DCHECK(resource.target);
+ resource.filter != resource.transferable.filter) {
+ DCHECK(resource.transferable.mailbox_holder.texture_target);
DCHECK(resource.gl_id);
DCHECK(gl);
- gl->BindTexture(resource.target, resource.gl_id);
- gl->TexParameteri(resource.target, GL_TEXTURE_MIN_FILTER,
- resource.original_filter);
- gl->TexParameteri(resource.target, GL_TEXTURE_MAG_FILTER,
- resource.original_filter);
+ 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();
}
@@ -807,8 +773,7 @@ DisplayResourceProvider::ScopedReadLockGL::ScopedReadLockGL(
DisplayResourceProvider* resource_provider,
ResourceId resource_id)
: resource_provider_(resource_provider), resource_id_(resource_id) {
- const internal::Resource* resource =
- resource_provider->LockForRead(resource_id);
+ const ChildResource* resource = resource_provider->LockForRead(resource_id);
// 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
@@ -816,9 +781,9 @@ DisplayResourceProvider::ScopedReadLockGL::ScopedReadLockGL(
return;
texture_id_ = resource->gl_id;
- target_ = resource->target;
- size_ = resource->size;
- color_space_ = resource->color_space;
+ target_ = resource->transferable.mailbox_holder.texture_target;
+ size_ = resource->transferable.size;
+ color_space_ = resource->transferable.color_space;
}
DisplayResourceProvider::ScopedReadLockGL::~ScopedReadLockGL() {
@@ -848,59 +813,53 @@ DisplayResourceProvider::ScopedReadLockSkImage::ScopedReadLockSkImage(
DisplayResourceProvider* resource_provider,
ResourceId resource_id)
: resource_provider_(resource_provider), resource_id_(resource_id) {
- const internal::Resource* resource =
- resource_provider->LockForRead(resource_id);
+ const ChildResource* resource = resource_provider->LockForRead(resource_id);
DCHECK(resource);
- if (resource_provider_->resource_sk_image_.find(resource_id) !=
- resource_provider_->resource_sk_image_.end()) {
- // Use cached sk_image.
- sk_image_ =
- resource_provider_->resource_sk_image_.find(resource_id)->second;
- } else if (resource->gl_id) {
+
+ // Use cached SkImage if possible.
+ auto it = resource_provider_->resource_sk_image_.find(resource_id);
+ if (it != resource_provider_->resource_sk_image_.end()) {
+ sk_image_ = it->second;
+ return;
+ }
+
+ if (resource->is_gpu_resource_type()) {
+ DCHECK(resource->gl_id);
GrGLTextureInfo texture_info;
texture_info.fID = resource->gl_id;
- texture_info.fTarget = resource->target;
- texture_info.fFormat = TextureStorageFormat(resource->format);
- GrBackendTexture backend_texture(resource->size.width(),
- resource->size.height(), GrMipMapped::kNo,
- texture_info);
+ texture_info.fTarget = resource->transferable.mailbox_holder.texture_target;
+ texture_info.fFormat = TextureStorageFormat(resource->transferable.format);
+ GrBackendTexture backend_texture(resource->transferable.size.width(),
+ resource->transferable.size.height(),
+ GrMipMapped::kNo, texture_info);
sk_image_ = SkImage::MakeFromTexture(
resource_provider->compositor_context_provider_->GrContext(),
backend_texture, kTopLeft_GrSurfaceOrigin,
ResourceFormatToClosestSkColorType(!resource_provider->IsSoftware(),
- resource->format),
+ resource->transferable.format),
kPremul_SkAlphaType, nullptr);
- } else if (resource->pixels) {
- SkBitmap sk_bitmap;
- resource_provider->PopulateSkBitmapWithResource(&sk_bitmap, resource);
- sk_bitmap.setImmutable();
- sk_image_ = SkImage::MakeFromBitmap(sk_bitmap);
- } else {
- // During render process shutdown, ~RenderMessageFilter which calls
- // ~HostSharedBitmapClient (which deletes shared bitmaps from child)
- // can race with OnBeginFrameDeadline which draws a frame.
- // In these cases, shared bitmaps (and this read lock) won't be valid.
- // Renderers need to silently handle locks failing until this race
- // is fixed. DCHECK that this is the only case where there are no pixels.
- DCHECK(!resource->shared_bitmap_id.IsZero());
+ return;
}
-}
-DisplayResourceProvider::ScopedReadLockSkImage::~ScopedReadLockSkImage() {
- resource_provider_->UnlockForRead(resource_id_);
-}
+ if (!resource->shared_bitmap) {
+ // If a CompositorFrameSink is destroyed, it destroys all SharedBitmapIds
+ // that it registered. In this case, a CompositorFrame can be drawn with
+ // SharedBitmapIds that are not known in the viz service. As well, a
+ // misbehaved client can use SharedBitampIds that it did not report to
+ // the service. Then the |shared_bitmap| will be null, and this read lock
+ // will not be valid. Software-compositing users of this read lock must
+ // check for valid() to deal with this scenario.
+ sk_image_ = nullptr;
+ return;
+ }
-DisplayResourceProvider::ScopedReadLockSoftware::ScopedReadLockSoftware(
- DisplayResourceProvider* resource_provider,
- ResourceId resource_id)
- : resource_provider_(resource_provider), resource_id_(resource_id) {
- const internal::Resource* resource =
- resource_provider->LockForRead(resource_id);
- DCHECK(resource);
- resource_provider->PopulateSkBitmapWithResource(&sk_bitmap_, resource);
+ SkBitmap sk_bitmap;
+ resource_provider->PopulateSkBitmapWithResource(&sk_bitmap, resource);
+ sk_bitmap.setImmutable();
+ sk_image_ = SkImage::MakeFromBitmap(sk_bitmap);
}
-DisplayResourceProvider::ScopedReadLockSoftware::~ScopedReadLockSoftware() {
+DisplayResourceProvider::ScopedReadLockSkImage::~ScopedReadLockSkImage() {
resource_provider_->UnlockForRead(resource_id_);
}
@@ -914,8 +873,7 @@ DisplayResourceProvider::LockSetForExternalUse::~LockSetForExternalUse() {
ResourceMetadata DisplayResourceProvider::LockSetForExternalUse::LockResource(
ResourceId id) {
- DCHECK(std::find(resources_.begin(), resources_.end(), id) ==
- resources_.end());
+ DCHECK(!base::ContainsValue(resources_, id));
resources_.push_back(id);
return resource_provider_->LockForExternalUse(id);
}
@@ -950,7 +908,7 @@ void DisplayResourceProvider::SynchronousFence::Wait() {
}
void DisplayResourceProvider::SynchronousFence::Synchronize() {
- TRACE_EVENT0("cc", "DisplayResourceProvider::SynchronousFence::Synchronize");
+ TRACE_EVENT0("viz", "DisplayResourceProvider::SynchronousFence::Synchronize");
gl_->Finish();
}
@@ -969,4 +927,38 @@ DisplayResourceProvider::Child::Child() = default;
DisplayResourceProvider::Child::Child(const Child& other) = default;
DisplayResourceProvider::Child::~Child() = default;
+DisplayResourceProvider::ChildResource::ChildResource(
+ int child_id,
+ const TransferableResource& transferable)
+ : child_id(child_id),
+ transferable(transferable),
+ filter(transferable.filter) {
+ if (is_gpu_resource_type())
+ UpdateSyncToken(transferable.mailbox_holder.sync_token);
+ else
+ SetSynchronized();
+}
+
+DisplayResourceProvider::ChildResource::ChildResource(ChildResource&& other) =
+ default;
+DisplayResourceProvider::ChildResource::~ChildResource() = default;
+
+void DisplayResourceProvider::ChildResource::SetLocallyUsed() {
+ synchronization_state_ = LOCALLY_USED;
+ sync_token_.Clear();
+}
+
+void DisplayResourceProvider::ChildResource::SetSynchronized() {
+ synchronization_state_ = SYNCHRONIZED;
+}
+
+void DisplayResourceProvider::ChildResource::UpdateSyncToken(
+ const gpu::SyncToken& sync_token) {
+ DCHECK(is_gpu_resource_type());
+ // An empty sync token may be used if commands are guaranteed to have run on
+ // the gpu process or in case of context loss.
+ sync_token_ = sync_token;
+ synchronization_state_ = sync_token.HasData() ? NEEDS_WAIT : SYNCHRONIZED;
+}
+
} // namespace viz
diff --git a/chromium/components/viz/service/display/display_resource_provider.h b/chromium/components/viz/service/display/display_resource_provider.h
index d81e561bee8..5b67fad9fa2 100644
--- a/chromium/components/viz/service/display/display_resource_provider.h
+++ b/chromium/components/viz/service/display/display_resource_provider.h
@@ -16,21 +16,22 @@
#include "base/threading/thread_checker.h"
#include "base/trace_event/memory_dump_provider.h"
#include "build/build_config.h"
-#include "components/viz/common/resources/resource.h"
-#include "components/viz/common/resources/resource_fence.h"
#include "components/viz/common/resources/resource_id.h"
-#include "components/viz/common/resources/resource_metadata.h"
#include "components/viz/common/resources/return_callback.h"
+#include "components/viz/common/resources/shared_bitmap.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "components/viz/service/display/overlay_candidate.h"
+#include "components/viz/service/display/resource_fence.h"
+#include "components/viz/service/display/resource_metadata.h"
#include "components/viz/service/viz_service_export.h"
+#include "gpu/command_buffer/common/sync_token.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/geometry/size.h"
namespace gfx {
class ColorSpace;
-class Size;
} // namespace gfx
namespace gpu {
@@ -58,11 +59,16 @@ class SharedBitmapManager;
class VIZ_SERVICE_EXPORT DisplayResourceProvider
: public base::trace_event::MemoryDumpProvider {
public:
- DisplayResourceProvider(ContextProvider* compositor_context_provider,
+ enum Mode {
+ kGpu,
+ kSoftware,
+ };
+ DisplayResourceProvider(Mode mode,
+ ContextProvider* compositor_context_provider,
SharedBitmapManager* shared_bitmap_manager);
~DisplayResourceProvider() override;
- bool IsSoftware() const { return !compositor_context_provider_; }
+ bool IsSoftware() const { return mode_ == kSoftware; }
void DidLoseContextProvider() { lost_context_provider_ = true; }
size_t num_resources() const { return resources_.size(); }
@@ -89,7 +95,7 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider
size_t CountPromotionHintRequestsForTesting();
#endif
- ResourceType GetResourceType(ResourceId id);
+ bool IsResourceSoftwareBacked(ResourceId id);
GLenum GetResourceTextureTarget(ResourceId id);
// Return the format of the underlying buffer that can be used for scanout.
gfx::BufferFormat GetBufferFormat(ResourceId id);
@@ -170,27 +176,6 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider
DISALLOW_COPY_AND_ASSIGN(ScopedReadLockSkImage);
};
- class VIZ_SERVICE_EXPORT ScopedReadLockSoftware {
- public:
- ScopedReadLockSoftware(DisplayResourceProvider* resource_provider,
- ResourceId resource_id);
- ~ScopedReadLockSoftware();
-
- const SkBitmap* sk_bitmap() const {
- DCHECK(valid());
- return &sk_bitmap_;
- }
-
- bool valid() const { return !!sk_bitmap_.getPixels(); }
-
- private:
- DisplayResourceProvider* const resource_provider_;
- const ResourceId resource_id_;
- SkBitmap sk_bitmap_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedReadLockSoftware);
- };
-
// Maintains set of lock for external use.
class VIZ_SERVICE_EXPORT LockSetForExternalUse {
public:
@@ -290,6 +275,40 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider
const ResourceIdSet& resources_from_child);
private:
+ enum DeleteStyle {
+ NORMAL,
+ FOR_SHUTDOWN,
+ };
+
+ enum SynchronizationState {
+ // The LOCALLY_USED state is the state each resource defaults to when
+ // constructed or modified or read. This state indicates that the
+ // resource has not been properly synchronized and it would be an error
+ // to return this resource to a client.
+ LOCALLY_USED,
+
+ // The NEEDS_WAIT state is the state that indicates a resource has been
+ // modified but it also has an associated sync token assigned to it.
+ // The sync token has not been waited on with the local context. When
+ // a sync token arrives from a client, it is automatically initialized as
+ // NEEDS_WAIT as well since we still need to wait on it before the resource
+ // is synchronized on the current context. It is an error to use the
+ // resource locally for reading or writing if the resource is in this state.
+ NEEDS_WAIT,
+
+ // The SYNCHRONIZED state indicates that the resource has been properly
+ // synchronized locally. This can either synchronized externally (such
+ // as the case of software rasterized bitmaps), or synchronized
+ // internally using a sync token that has been waited upon. In the
+ // former case where the resource was synchronized externally, a
+ // corresponding sync token will not exist. In the latter case which was
+ // synchronized from the NEEDS_WAIT state, a corresponding sync token will
+ // exist which is associated with the resource. This sync token is still
+ // valid and still associated with the resource and can be passed as an
+ // external resource for others to wait on.
+ SYNCHRONIZED,
+ };
+
struct Child {
Child();
Child(const Child& other);
@@ -301,34 +320,111 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider
bool needs_sync_tokens = true;
};
- enum DeleteStyle {
- NORMAL,
- FOR_SHUTDOWN,
+ // The data structure used to track state of Gpu and Software-based
+ // resources and the service, for resources transferred
+ // between the two. This is an implementation detail of the resource tracking
+ // for client and service libraries and should not be used directly from
+ // external client code.
+ struct ChildResource {
+ ChildResource(int child_id, const TransferableResource& transferable);
+ ChildResource(ChildResource&& other);
+ ~ChildResource();
+
+ bool is_gpu_resource_type() const { return !transferable.is_software; }
+ bool needs_sync_token() const {
+ return is_gpu_resource_type() && synchronization_state_ == LOCALLY_USED;
+ }
+ const gpu::SyncToken& sync_token() const { return sync_token_; }
+ SynchronizationState synchronization_state() const {
+ return synchronization_state_;
+ }
+ // If true the gpu resource needs its SyncToken waited on in order to be
+ // synchronized for use.
+ bool ShouldWaitSyncToken() const {
+ return synchronization_state_ == NEEDS_WAIT;
+ }
+
+ void SetLocallyUsed();
+ void SetSynchronized();
+ void UpdateSyncToken(const gpu::SyncToken& sync_token);
+
+ // This is the id of the client the resource comes from.
+ const int child_id;
+ // Data received from the client that describes the resource fully.
+ const TransferableResource transferable;
+
+ // The number of times the resource has been received from a client. It must
+ // have this many number of references returned back to the client in order
+ // for it to know it is no longer in use in the service. This is used to
+ // avoid races where a resource is in flight to the service while also being
+ // returned to the client. It starts with an initial count of 1, for the
+ // first time the resource is received.
+ int imported_count = 1;
+
+ // The number of active users of a resource in the display compositor. While
+ // a resource is in use, it will not be returned back to the client even if
+ // the ResourceId is deleted.
+ int lock_for_read_count = 0;
+ // When true, the resource is currently being used externally. This is a
+ // parallel counter to |lock_for_read_count| which can only go to 1.
+ bool locked_for_external_use = false;
+
+ // When the resource should be deleted until it is actually reaped.
+ bool marked_for_deletion = false;
+
+ // Texture id for texture-backed resources, when the mailbox is mapped into
+ // a client GL id in this process.
+ GLuint gl_id = 0;
+ // The current min/mag filter for GL texture-backed resources. The original
+ // filter as given from the client is saved in |transferable|.
+ GLenum filter;
+ // A pointer to the shared memory structure for software-backed resources,
+ // when it is mapped into memory in this process.
+ std::unique_ptr<SharedBitmap> shared_bitmap;
+ // A GUID for reporting the |shared_bitmap| to memory tracing. The GUID is
+ // known by other components in the system as well to give the same id for
+ // this shared memory bitmap everywhere. This is empty until the resource is
+ // 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;
+
+ private:
+ // Tracks if a sync token needs to be waited on before using the resource.
+ SynchronizationState synchronization_state_;
+ // A SyncToken associated with a texture-backed or GpuMemoryBuffer-backed
+ // resource. It is given from a child to the service, and waited on in order
+ // to use the resource, and this is tracked by the |synchronization_state_|.
+ gpu::SyncToken sync_token_;
};
using ChildMap = std::unordered_map<int, Child>;
- using ResourceMap = std::unordered_map<ResourceId, internal::Resource>;
+ using ResourceMap = std::unordered_map<ResourceId, ChildResource>;
- internal::Resource* InsertResource(ResourceId id,
- internal::Resource resource);
- internal::Resource* GetResource(ResourceId id);
+ ChildResource* InsertResource(ResourceId id, ChildResource resource);
+ ChildResource* GetResource(ResourceId id);
// TODO(ericrk): TryGetResource is part of a temporary workaround for cases
// where resources which should be available are missing. This version may
// return nullptr if a resource is not found. https://crbug.com/811858
- internal::Resource* TryGetResource(ResourceId id);
+ ChildResource* TryGetResource(ResourceId id);
void PopulateSkBitmapWithResource(SkBitmap* sk_bitmap,
- const internal::Resource* resource);
+ const ChildResource* resource);
void DeleteResourceInternal(ResourceMap::iterator it, DeleteStyle style);
- void WaitSyncTokenInternal(internal::Resource* resource);
+ void WaitSyncTokenInternal(ChildResource* resource);
// Returns null if we do not have a ContextProvider.
gpu::gles2::GLES2Interface* ContextGL() const;
- const internal::Resource* LockForRead(ResourceId id);
+ const ChildResource* LockForRead(ResourceId id);
void UnlockForRead(ResourceId id);
// Lock a resource for external use.
@@ -345,7 +441,7 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider
// specified filter for both minification and magnification. Returns the
// texture target used. The resource must be locked for reading.
GLenum BindForSampling(ResourceId resource_id, GLenum unit, GLenum filter);
- bool ReadLockFenceHasPassed(const internal::Resource* resource);
+ bool ReadLockFenceHasPassed(const ChildResource* resource);
#if defined(OS_ANDROID)
void DeletePromotionHint(ResourceMap::iterator it, DeleteStyle style);
#endif
@@ -359,6 +455,7 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider
void SetBatchReturnResources(bool aggregate);
THREAD_CHECKER(thread_checker_);
+ const Mode mode_;
ContextProvider* const compositor_context_provider_;
SharedBitmapManager* const shared_bitmap_manager_;
@@ -376,7 +473,7 @@ class VIZ_SERVICE_EXPORT DisplayResourceProvider
// modified by this class are now in an indeterminate state.
bool lost_context_provider_ = false;
// The ResourceIds in DisplayResourceProvider start from 2 to avoid
- // conflicts with id from LayerTreeResourceProvider.
+ // conflicts with id from ClientResourceProvider.
ResourceId next_id_ = 2;
// Used as child id when creating a child.
int next_child_ = 1;
diff --git a/chromium/components/viz/service/display/display_resource_provider_unittest.cc b/chromium/components/viz/service/display/display_resource_provider_unittest.cc
index af4d091a81a..974f95bde51 100644
--- a/chromium/components/viz/service/display/display_resource_provider_unittest.cc
+++ b/chromium/components/viz/service/display/display_resource_provider_unittest.cc
@@ -3,7 +3,7 @@
// found in the LICENSE file.
#include "components/viz/service/display/display_resource_provider.h"
-#include "cc/resources/layer_tree_resource_provider.h"
+#include "components/viz/client/client_resource_provider.h"
#include <stddef.h>
#include <stdint.h>
@@ -23,17 +23,17 @@
#include "build/build_config.h"
#include "cc/test/render_pass_test_utils.h"
#include "cc/test/resource_provider_test_utils.h"
-#include "components/viz/common/quads/shared_bitmap.h"
#include "components/viz/common/resources/bitmap_allocation.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/common/resources/returned_resource.h"
-#include "components/viz/common/resources/shared_bitmap_manager.h"
+#include "components/viz/common/resources/shared_bitmap.h"
#include "components/viz/common/resources/single_release_callback.h"
+#include "components/viz/service/display/shared_bitmap_manager.h"
#include "components/viz/test/test_context_provider.h"
+#include "components/viz/test/test_gles2_interface.h"
#include "components/viz/test/test_gpu_memory_buffer_manager.h"
#include "components/viz/test/test_shared_bitmap_manager.h"
#include "components/viz/test/test_texture.h"
-#include "components/viz/test/test_web_graphics_context_3d.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -82,8 +82,8 @@ static SharedBitmapId CreateAndFillSharedBitmap(SharedBitmapManager* manager,
return shared_bitmap_id;
}
-// Shared data between multiple ResourceProviderContext. This contains mailbox
-// contents as well as information about sync points.
+// Shared data between multiple ResourceProviderGLES2Interface. This contains
+// mailbox contents as well as information about sync points.
class ContextSharedData {
public:
static std::unique_ptr<ContextSharedData> Create() {
@@ -137,14 +137,12 @@ class ContextSharedData {
std::unordered_map<unsigned, uint32_t> sync_point_for_mailbox_;
};
-class ResourceProviderContext : public TestWebGraphicsContext3D {
+class ResourceProviderGLES2Interface : public TestGLES2Interface {
public:
- static std::unique_ptr<ResourceProviderContext> Create(
- ContextSharedData* shared_data) {
- return base::WrapUnique(new ResourceProviderContext(shared_data));
- }
+ explicit ResourceProviderGLES2Interface(ContextSharedData* shared_data)
+ : shared_data_(shared_data) {}
- void genSyncToken(GLbyte* sync_token) override {
+ void GenSyncTokenCHROMIUM(GLbyte* sync_token) override {
uint64_t fence_sync = shared_data_->InsertFenceSync();
gpu::SyncToken sync_token_data(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x123),
@@ -161,7 +159,7 @@ class ResourceProviderContext : public TestWebGraphicsContext3D {
memcpy(sync_token, &sync_token_data, sizeof(sync_token_data));
}
- void waitSyncToken(const GLbyte* sync_token) override {
+ 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));
@@ -176,7 +174,7 @@ class ResourceProviderContext : public TestWebGraphicsContext3D {
return last_waited_sync_token_;
}
- void texStorage2DEXT(GLenum target,
+ void TexStorage2DEXT(GLenum target,
GLint levels,
GLuint internalformat,
GLint width,
@@ -197,9 +195,9 @@ class ResourceProviderContext : public TestWebGraphicsContext3D {
AllocateTexture(gfx::Size(width, height), format);
}
- void texImage2D(GLenum target,
+ void TexImage2D(GLenum target,
GLint level,
- GLenum internalformat,
+ GLint internalformat,
GLsizei width,
GLsizei height,
GLint border,
@@ -209,7 +207,7 @@ class ResourceProviderContext : public TestWebGraphicsContext3D {
CheckTextureIsBound(target);
ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
ASSERT_FALSE(level);
- ASSERT_EQ(internalformat, format);
+ ASSERT_EQ(internalformat, static_cast<GLint>(format));
ASSERT_FALSE(border);
ASSERT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type);
AllocateTexture(gfx::Size(width, height), format);
@@ -217,7 +215,7 @@ class ResourceProviderContext : public TestWebGraphicsContext3D {
SetPixels(0, 0, width, height, pixels);
}
- void texSubImage2D(GLenum target,
+ void TexSubImage2D(GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
@@ -238,12 +236,8 @@ class ResourceProviderContext : public TestWebGraphicsContext3D {
SetPixels(xoffset, yoffset, width, height, pixels);
}
- void genMailboxCHROMIUM(GLbyte* mailbox) override {
- return shared_data_->GenMailbox(mailbox);
- }
-
- void produceTextureDirectCHROMIUM(GLuint texture,
- const GLbyte* mailbox) override {
+ void ProduceTextureDirectCHROMIUM(GLuint texture, GLbyte* mailbox) override {
+ shared_data_->GenMailbox(mailbox);
// Delay moving the texture into the mailbox until the next
// sync token, so that it is not visible to other contexts that
// haven't waited on that sync point.
@@ -254,8 +248,10 @@ class ResourceProviderContext : public TestWebGraphicsContext3D {
pending_produce_textures_.push_back(std::move(pending));
}
- GLuint createAndConsumeTextureCHROMIUM(const GLbyte* mailbox) override {
- GLuint texture_id = createTexture();
+ GLuint CreateAndConsumeTextureCHROMIUM(const GLbyte* mailbox) override {
+ GLuint texture_id;
+ GenTextures(1, &texture_id);
+
base::AutoLock lock_for_texture_access(namespace_->lock);
scoped_refptr<TestTexture> texture =
shared_data_->ConsumeTexture(mailbox, last_waited_sync_token_);
@@ -274,10 +270,6 @@ class ResourceProviderContext : public TestWebGraphicsContext3D {
memcpy(pixels, texture->data.get(), TextureSizeBytes(size, format));
}
- protected:
- explicit ResourceProviderContext(ContextSharedData* shared_data)
- : shared_data_(shared_data) {}
-
private:
void AllocateTexture(const gfx::Size& size, GLenum format) {
CheckTextureIsBound(GL_TEXTURE_2D);
@@ -335,28 +327,31 @@ class DisplayResourceProviderTest : public testing::TestWithParam<bool> {
child_needs_sync_token_(child_needs_sync_token),
shared_data_(ContextSharedData::Create()) {
if (use_gpu_) {
- auto context3d(ResourceProviderContext::Create(shared_data_.get()));
- context3d_ = context3d.get();
- context_provider_ = TestContextProvider::Create(std::move(context3d));
- context_provider_->UnboundTestContext3d()
+ auto gl_owned =
+ std::make_unique<ResourceProviderGLES2Interface>(shared_data_.get());
+ gl_ = gl_owned.get();
+ context_provider_ = TestContextProvider::Create(std::move(gl_owned));
+ context_provider_->UnboundTestContextGL()
->set_support_texture_format_bgra8888(true);
context_provider_->BindToCurrentThread();
- auto child_context_owned =
- ResourceProviderContext::Create(shared_data_.get());
- child_context_ = child_context_owned.get();
+ auto child_gl_owned =
+ std::make_unique<ResourceProviderGLES2Interface>(shared_data_.get());
+ child_gl_ = child_gl_owned.get();
child_context_provider_ =
- TestContextProvider::Create(std::move(child_context_owned));
- child_context_provider_->UnboundTestContext3d()
+ TestContextProvider::Create(std::move(child_gl_owned));
+ child_context_provider_->UnboundTestContextGL()
->set_support_texture_format_bgra8888(true);
child_context_provider_->BindToCurrentThread();
gpu_memory_buffer_manager_ =
std::make_unique<TestGpuMemoryBufferManager>();
- } else {
- shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
}
+ // SharedBitmapManager may always be present, even if gpu compositing.
+ shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
resource_provider_ = std::make_unique<DisplayResourceProvider>(
+ use_gpu_ ? DisplayResourceProvider::kGpu
+ : DisplayResourceProvider::kSoftware,
context_provider_.get(), shared_bitmap_manager_.get());
MakeChildResourceProvider();
@@ -364,11 +359,16 @@ class DisplayResourceProviderTest : public testing::TestWithParam<bool> {
DisplayResourceProviderTest() : DisplayResourceProviderTest(true) {}
+ ~DisplayResourceProviderTest() {
+ if (child_resource_provider_)
+ child_resource_provider_->ShutdownAndReleaseAllResources();
+ }
+
bool use_gpu() const { return use_gpu_; }
void MakeChildResourceProvider() {
- child_resource_provider_ = std::make_unique<cc::LayerTreeResourceProvider>(
- child_context_provider_.get(), child_needs_sync_token_);
+ child_resource_provider_ =
+ std::make_unique<ClientResourceProvider>(child_needs_sync_token_);
}
static ReturnCallback GetReturnCallback(
@@ -383,16 +383,15 @@ class DisplayResourceProviderTest : public testing::TestWithParam<bool> {
GL_TEXTURE_2D, filter);
}
- ResourceProviderContext* context() { return context3d_; }
TransferableResource CreateResource(ResourceFormat format) {
if (use_gpu()) {
- unsigned texture = child_context_->createTexture();
+ unsigned texture;
+ child_gl_->GenTextures(1, &texture);
gpu::Mailbox gpu_mailbox;
- child_context_->genMailboxCHROMIUM(gpu_mailbox.name);
- child_context_->produceTextureDirectCHROMIUM(texture, gpu_mailbox.name);
+ child_gl_->ProduceTextureDirectCHROMIUM(texture, gpu_mailbox.name);
gpu::SyncToken sync_token;
- child_context_->genSyncToken(sync_token.GetData());
+ child_gl_->GenSyncTokenCHROMIUM(sync_token.GetData());
EXPECT_TRUE(sync_token.HasData());
TransferableResource gl_resource = TransferableResource::MakeGL(
@@ -433,13 +432,13 @@ class DisplayResourceProviderTest : public testing::TestWithParam<bool> {
const bool use_gpu_;
const bool child_needs_sync_token_;
const std::unique_ptr<ContextSharedData> shared_data_;
- ResourceProviderContext* context3d_ = nullptr;
- ResourceProviderContext* child_context_ = nullptr;
+ ResourceProviderGLES2Interface* gl_ = nullptr;
+ ResourceProviderGLES2Interface* child_gl_ = nullptr;
scoped_refptr<TestContextProvider> context_provider_;
scoped_refptr<TestContextProvider> child_context_provider_;
std::unique_ptr<TestGpuMemoryBufferManager> gpu_memory_buffer_manager_;
std::unique_ptr<DisplayResourceProvider> resource_provider_;
- std::unique_ptr<cc::LayerTreeResourceProvider> child_resource_provider_;
+ std::unique_ptr<ClientResourceProvider> child_resource_provider_;
std::unique_ptr<TestSharedBitmapManager> shared_bitmap_manager_;
};
@@ -736,26 +735,27 @@ TEST_P(DisplayResourceProviderTest, ReadLockFenceContextLost) {
EXPECT_TRUE(returned_to_child[0].lost);
EXPECT_TRUE(returned_to_child[1].lost);
+
+ child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
+ child_resource_provider_->RemoveImportedResource(id1);
+ child_resource_provider_->RemoveImportedResource(id2);
}
TEST_P(DisplayResourceProviderTest, ReturnResourcesWithoutSyncToken) {
if (!use_gpu())
return;
- bool need_sync_tokens = false;
- auto no_token_resource_provider =
- std::make_unique<cc::LayerTreeResourceProvider>(
- child_context_provider_.get(), need_sync_tokens);
+ auto no_token_resource_provider = std::make_unique<ClientResourceProvider>(
+ /*delegated_sync_points_required=*/true);
- GLuint external_texture_id = child_context_->createExternalTexture();
+ GLuint external_texture_id = child_gl_->CreateExternalTexture();
// A sync point is specified directly and should be used.
gpu::Mailbox external_mailbox;
- child_context_->genMailboxCHROMIUM(external_mailbox.name);
- child_context_->produceTextureDirectCHROMIUM(external_texture_id,
- external_mailbox.name);
+ child_gl_->ProduceTextureDirectCHROMIUM(external_texture_id,
+ external_mailbox.name);
gpu::SyncToken external_sync_token;
- child_context_->genSyncToken(external_sync_token.GetData());
+ child_gl_->GenSyncTokenCHROMIUM(external_sync_token.GetData());
EXPECT_TRUE(external_sync_token.HasData());
ResourceId id = no_token_resource_provider->ImportResource(
TransferableResource::MakeGL(external_mailbox, GL_LINEAR,
@@ -805,6 +805,7 @@ TEST_P(DisplayResourceProviderTest, ReturnResourcesWithoutSyncToken) {
}
resource_provider_->DestroyChild(child_id);
+ no_token_resource_provider->RemoveImportedResource(id);
}
// Test that ScopedBatchReturnResources batching works.
@@ -874,11 +875,9 @@ TEST_P(DisplayResourceProviderTest, ScopedBatchReturnResourcesPreventsReturn) {
}
TEST_P(DisplayResourceProviderTest, LostMailboxInParent) {
- gpu::Mailbox gpu_mailbox;
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
- auto tran = TransferableResource::MakeGL(gpu_mailbox, GL_LINEAR,
- GL_TEXTURE_2D, sync_token);
+ auto tran = CreateResource(RGBA_8888);
tran.id = 11;
std::vector<ReturnedResource> returned_to_child;
@@ -932,12 +931,14 @@ TEST_P(DisplayResourceProviderTest, ReadSoftwareResources) {
ResourceId mapped_resource_id = resource_map[resource_id];
{
- DisplayResourceProvider::ScopedReadLockSoftware lock(
+ DisplayResourceProvider::ScopedReadLockSkImage lock(
resource_provider_.get(), mapped_resource_id);
- const SkBitmap* sk_bitmap = lock.sk_bitmap();
- EXPECT_EQ(sk_bitmap->width(), size.width());
- EXPECT_EQ(sk_bitmap->height(), size.height());
- EXPECT_EQ(*sk_bitmap->getAddr32(16, 16), kBadBeef);
+ const SkImage* sk_image = lock.sk_image();
+ SkBitmap sk_bitmap;
+ sk_image->asLegacyBitmap(&sk_bitmap);
+ EXPECT_EQ(sk_image->width(), size.width());
+ EXPECT_EQ(sk_image->height(), size.height());
+ EXPECT_EQ(*sk_bitmap.getAddr32(16, 16), kBadBeef);
}
EXPECT_EQ(0u, returned_to_child.size());
@@ -951,14 +952,14 @@ TEST_P(DisplayResourceProviderTest, ReadSoftwareResources) {
child_resource_provider_->RemoveImportedResource(resource_id);
}
-class TextureStateTrackingContext : public TestWebGraphicsContext3D {
+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(waitSyncToken, void(const GLbyte* sync_token));
- MOCK_METHOD2(produceTextureDirectCHROMIUM,
- void(GLuint texture, const GLbyte* mailbox));
- MOCK_METHOD1(createAndConsumeTextureCHROMIUM,
+ 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_METHOD2(ProduceTextureDirectCHROMIUM,
+ void(GLuint texture, GLbyte* mailbox));
+ MOCK_METHOD1(CreateAndConsumeTextureCHROMIUM,
unsigned(const GLbyte* mailbox));
// Force all textures to be consecutive numbers starting at "1",
@@ -970,7 +971,7 @@ class TextureStateTrackingContext : public TestWebGraphicsContext3D {
void RetireTextureId(GLuint) override {}
- void genSyncToken(GLbyte* sync_token) override {
+ void GenSyncTokenCHROMIUM(GLbyte* sync_token) override {
gpu::SyncToken sync_token_data(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x123),
next_fence_sync_++);
@@ -988,36 +989,35 @@ class ResourceProviderTestImportedResourceGLFilters {
static void RunTest(TestSharedBitmapManager* shared_bitmap_manager,
bool mailbox_nearest_neighbor,
GLenum sampler_filter) {
- auto context_owned(std::make_unique<TextureStateTrackingContext>());
- TextureStateTrackingContext* context = context_owned.get();
- auto context_provider =
- TestContextProvider::Create(std::move(context_owned));
+ 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<DisplayResourceProvider>(
- context_provider.get(), shared_bitmap_manager);
+ DisplayResourceProvider::kGpu, context_provider.get(),
+ shared_bitmap_manager);
- auto child_context_owned = std::make_unique<TextureStateTrackingContext>();
- TextureStateTrackingContext* child_context = child_context_owned.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_context_owned));
+ TestContextProvider::Create(std::move(child_gl_owned));
child_context_provider->BindToCurrentThread();
- auto child_resource_provider =
- std::make_unique<cc::LayerTreeResourceProvider>(
- child_context_provider.get(),
- /*delegated_sync_points_required=*/true);
+ auto child_resource_provider = std::make_unique<ClientResourceProvider>(
+ /*delegated_sync_points_required=*/true);
unsigned texture_id = 1;
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x12),
0x34);
- const GLuint64 current_fence_sync = child_context->GetNextFenceSync();
+ const GLuint64 current_fence_sync = child_gl->GetNextFenceSync();
- EXPECT_CALL(*child_context, bindTexture(_, _)).Times(0);
- EXPECT_CALL(*child_context, waitSyncToken(_)).Times(0);
- EXPECT_CALL(*child_context, produceTextureDirectCHROMIUM(_, _)).Times(0);
- EXPECT_CALL(*child_context, createAndConsumeTextureCHROMIUM(_)).Times(0);
+ EXPECT_CALL(*child_gl, BindTexture(_, _)).Times(0);
+ EXPECT_CALL(*child_gl, WaitSyncTokenCHROMIUM(_)).Times(0);
+ EXPECT_CALL(*child_gl, ProduceTextureDirectCHROMIUM(_, _)).Times(0);
+ EXPECT_CALL(*child_gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0);
gpu::Mailbox gpu_mailbox;
memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
@@ -1031,9 +1031,9 @@ class ResourceProviderTestImportedResourceGLFilters {
SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
EXPECT_NE(0u, resource_id);
- EXPECT_EQ(current_fence_sync, child_context->GetNextFenceSync());
+ EXPECT_EQ(current_fence_sync, child_gl->GetNextFenceSync());
- testing::Mock::VerifyAndClearExpectations(child_context);
+ testing::Mock::VerifyAndClearExpectations(child_gl);
// Transfer resources to the parent.
std::vector<TransferableResource> send_to_parent;
@@ -1050,44 +1050,42 @@ class ResourceProviderTestImportedResourceGLFilters {
ResourceId mapped_resource_id = resource_map[resource_id];
{
// The verified flush flag will be set by
- // cc::LayerTreeResourceProvider::PrepareSendToParent. Before checking if
+ // 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(*context, waitSyncToken(MatchesSyncToken(sync_token)));
+ EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token)));
resource_provider->WaitSyncToken(mapped_resource_id);
- testing::Mock::VerifyAndClearExpectations(context);
+ testing::Mock::VerifyAndClearExpectations(gl);
- EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_))
+ EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_))
.WillOnce(Return(texture_id));
- EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
+ EXPECT_CALL(*gl, BindTexture(GL_TEXTURE_2D, texture_id));
- EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _)).Times(0);
+ EXPECT_CALL(*gl, ProduceTextureDirectCHROMIUM(_, _)).Times(0);
// The sampler will reset these if |mailbox_nearest_neighbor| does not
// match |sampler_filter|.
if (mailbox_nearest_neighbor != (sampler_filter == GL_NEAREST)) {
- EXPECT_CALL(*context,
- texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
- sampler_filter));
- EXPECT_CALL(*context,
- texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
- sampler_filter));
+ 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));
}
DisplayResourceProvider::ScopedSamplerGL lock(
resource_provider.get(), mapped_resource_id, sampler_filter);
- testing::Mock::VerifyAndClearExpectations(context);
- EXPECT_EQ(current_fence_sync, context->GetNextFenceSync());
+ testing::Mock::VerifyAndClearExpectations(gl);
+ EXPECT_EQ(current_fence_sync, gl->GetNextFenceSync());
// When done with it, a sync point should be inserted, but no produce is
// necessary.
- EXPECT_CALL(*child_context, bindTexture(_, _)).Times(0);
- EXPECT_CALL(*child_context, produceTextureDirectCHROMIUM(_, _)).Times(0);
+ EXPECT_CALL(*child_gl, BindTexture(_, _)).Times(0);
+ EXPECT_CALL(*child_gl, ProduceTextureDirectCHROMIUM(_, _)).Times(0);
- EXPECT_CALL(*child_context, waitSyncToken(_)).Times(0);
- EXPECT_CALL(*child_context, createAndConsumeTextureCHROMIUM(_)).Times(0);
+ EXPECT_CALL(*child_gl, WaitSyncTokenCHROMIUM(_)).Times(0);
+ EXPECT_CALL(*child_gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0);
}
EXPECT_EQ(0u, returned_to_child.size());
@@ -1148,33 +1146,32 @@ TEST_P(DisplayResourceProviderTest, ReceiveGLTextureExternalOES) {
if (!use_gpu())
return;
- auto context_owned(std::make_unique<TextureStateTrackingContext>());
- TextureStateTrackingContext* context = context_owned.get();
- auto context_provider = TestContextProvider::Create(std::move(context_owned));
+ 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<DisplayResourceProvider>(
- context_provider.get(), shared_bitmap_manager_.get());
+ DisplayResourceProvider::kGpu, context_provider.get(),
+ shared_bitmap_manager_.get());
- auto child_context_owned = std::make_unique<TextureStateTrackingContext>();
- TextureStateTrackingContext* child_context = child_context_owned.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_context_owned));
+ TestContextProvider::Create(std::move(child_gl_owned));
child_context_provider->BindToCurrentThread();
- auto child_resource_provider =
- std::make_unique<cc::LayerTreeResourceProvider>(
- child_context_provider.get(),
- /*delegated_sync_points_required=*/true);
+ auto child_resource_provider = std::make_unique<ClientResourceProvider>(
+ /*delegated_sync_points_required=*/true);
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
- const GLuint64 current_fence_sync = child_context->GetNextFenceSync();
+ const GLuint64 current_fence_sync = child_gl->GetNextFenceSync();
- EXPECT_CALL(*child_context, bindTexture(_, _)).Times(0);
- EXPECT_CALL(*child_context, waitSyncToken(_)).Times(0);
- EXPECT_CALL(*child_context, produceTextureDirectCHROMIUM(_, _)).Times(0);
- EXPECT_CALL(*child_context, createAndConsumeTextureCHROMIUM(_)).Times(0);
+ EXPECT_CALL(*child_gl, BindTexture(_, _)).Times(0);
+ EXPECT_CALL(*child_gl, WaitSyncTokenCHROMIUM(_)).Times(0);
+ EXPECT_CALL(*child_gl, ProduceTextureDirectCHROMIUM(_, _)).Times(0);
+ EXPECT_CALL(*child_gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0);
gpu::Mailbox gpu_mailbox;
memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
@@ -1187,9 +1184,9 @@ TEST_P(DisplayResourceProviderTest, ReceiveGLTextureExternalOES) {
ResourceId resource_id =
child_resource_provider->ImportResource(resource, std::move(callback));
EXPECT_NE(0u, resource_id);
- EXPECT_EQ(current_fence_sync, child_context->GetNextFenceSync());
+ EXPECT_EQ(current_fence_sync, child_gl->GetNextFenceSync());
- testing::Mock::VerifyAndClearExpectations(child_context);
+ testing::Mock::VerifyAndClearExpectations(child_gl);
// Transfer resources to the parent.
std::vector<TransferableResource> send_to_parent;
@@ -1207,34 +1204,34 @@ TEST_P(DisplayResourceProviderTest, ReceiveGLTextureExternalOES) {
ResourceId mapped_resource_id = resource_map[resource_id];
{
// The verified flush flag will be set by
- // cc::LayerTreeResourceProvider::PrepareSendToParent. Before checking if
+ // 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(*context, waitSyncToken(MatchesSyncToken(sync_token)));
+ EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token)));
resource_provider->WaitSyncToken(mapped_resource_id);
- testing::Mock::VerifyAndClearExpectations(context);
+ testing::Mock::VerifyAndClearExpectations(gl);
unsigned texture_id = 1;
- EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_))
+ EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_))
.WillOnce(Return(texture_id));
- EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _)).Times(0);
+ EXPECT_CALL(*gl, ProduceTextureDirectCHROMIUM(_, _)).Times(0);
DisplayResourceProvider::ScopedReadLockGL lock(resource_provider.get(),
mapped_resource_id);
- testing::Mock::VerifyAndClearExpectations(context);
+ testing::Mock::VerifyAndClearExpectations(gl);
// When done with it, a sync point should be inserted, but no produce is
// necessary.
- EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
- EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _)).Times(0);
+ EXPECT_CALL(*gl, BindTexture(_, _)).Times(0);
+ EXPECT_CALL(*gl, ProduceTextureDirectCHROMIUM(_, _)).Times(0);
- EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
- EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_)).Times(0);
- testing::Mock::VerifyAndClearExpectations(context);
+ 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
@@ -1251,20 +1248,21 @@ TEST_P(DisplayResourceProviderTest, WaitSyncTokenIfNeeded) {
if (!use_gpu())
return;
- auto context_owned = std::make_unique<TextureStateTrackingContext>();
- TextureStateTrackingContext* context = context_owned.get();
- auto context_provider = TestContextProvider::Create(std::move(context_owned));
+ 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<DisplayResourceProvider>(
- context_provider.get(), shared_bitmap_manager_.get());
+ DisplayResourceProvider::kGpu, context_provider.get(),
+ shared_bitmap_manager_.get());
- const GLuint64 current_fence_sync = context->GetNextFenceSync();
+ const GLuint64 current_fence_sync = gl->GetNextFenceSync();
- EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
- EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
- EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _)).Times(0);
- EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_)).Times(0);
+ EXPECT_CALL(*gl, BindTexture(_, _)).Times(0);
+ EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0);
+ EXPECT_CALL(*gl, ProduceTextureDirectCHROMIUM(_, _)).Times(0);
+ EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0);
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
@@ -1273,20 +1271,21 @@ TEST_P(DisplayResourceProviderTest, WaitSyncTokenIfNeeded) {
ResourceId id_without_sync = MakeGpuResourceAndSendToDisplay(
'a', GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(), resource_provider.get());
- EXPECT_EQ(current_fence_sync, context->GetNextFenceSync());
+ EXPECT_EQ(current_fence_sync, gl->GetNextFenceSync());
// First call to WaitSyncToken should call WaitSyncToken, but only if a
// SyncToken was present.
{
- EXPECT_CALL(*context, waitSyncToken(MatchesSyncToken(sync_token))).Times(1);
+ EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token)))
+ .Times(1);
resource_provider->WaitSyncToken(id_with_sync);
- EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
+ EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0);
resource_provider->WaitSyncToken(id_without_sync);
}
{
// Subsequent calls to WaitSyncToken shouldn't call WaitSyncToken.
- EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
+ EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0);
resource_provider->WaitSyncToken(id_with_sync);
resource_provider->WaitSyncToken(id_without_sync);
}
@@ -1297,14 +1296,13 @@ TEST_P(DisplayResourceProviderTest, OverlayPromotionHint) {
if (!use_gpu())
return;
- GLuint external_texture_id = child_context_->createExternalTexture();
+ GLuint external_texture_id = child_gl_->CreateExternalTexture();
gpu::Mailbox external_mailbox;
- child_context_->genMailboxCHROMIUM(external_mailbox.name);
- child_context_->produceTextureDirectCHROMIUM(external_texture_id,
- external_mailbox.name);
+ child_gl_->ProduceTextureDirectCHROMIUM(external_texture_id,
+ external_mailbox.name);
gpu::SyncToken external_sync_token;
- child_context_->genSyncToken(external_sync_token.GetData());
+ child_gl_->GenSyncTokenCHROMIUM(external_sync_token.GetData());
EXPECT_TRUE(external_sync_token.HasData());
TransferableResource id1_transfer = TransferableResource::MakeGLOverlay(
@@ -1351,8 +1349,7 @@ TEST_P(DisplayResourceProviderTest, OverlayPromotionHint) {
}
EXPECT_EQ(1u, resource_provider_->CountPromotionHintRequestsForTesting());
- EXPECT_EQ(list[0].mailbox_holder.sync_token,
- context3d_->last_waited_sync_token());
+ EXPECT_EQ(list[0].mailbox_holder.sync_token, gl_->last_waited_sync_token());
ResourceIdSet resource_ids_to_receive;
resource_ids_to_receive.insert(id1);
resource_ids_to_receive.insert(id2);
@@ -1382,6 +1379,9 @@ TEST_P(DisplayResourceProviderTest, OverlayPromotionHint) {
EXPECT_EQ(0u, resource_provider_->CountPromotionHintRequestsForTesting());
resource_provider_->DestroyChild(child_id);
+
+ child_resource_provider_->RemoveImportedResource(id2);
+ child_resource_provider_->RemoveImportedResource(id1);
}
#endif
diff --git a/chromium/components/viz/service/display/display_scheduler.cc b/chromium/components/viz/service/display/display_scheduler.cc
index 402eeccc064..db8c8cc33b5 100644
--- a/chromium/components/viz/service/display/display_scheduler.cc
+++ b/chromium/components/viz/service/display/display_scheduler.cc
@@ -420,7 +420,7 @@ DisplayScheduler::DesiredBeginFrameDeadlineMode() const {
}
if (!needs_draw_) {
- TRACE_EVENT_INSTANT0("cc", "No damage yet", TRACE_EVENT_SCOPE_THREAD);
+ TRACE_EVENT_INSTANT0("viz", "No damage yet", TRACE_EVENT_SCOPE_THREAD);
return BeginFrameDeadlineMode::kLate;
}
@@ -463,7 +463,7 @@ void DisplayScheduler::ScheduleBeginFrameDeadline() {
begin_frame_deadline_task_.Cancel();
if (begin_frame_deadline_task_time_ == base::TimeTicks::Max()) {
- TRACE_EVENT_INSTANT0("cc", "Using infinite deadline",
+ TRACE_EVENT_INSTANT0("viz", "Using infinite deadline",
TRACE_EVENT_SCOPE_THREAD);
return;
}
@@ -508,8 +508,7 @@ void DisplayScheduler::DidFinishFrame(bool did_draw) {
DCHECK(begin_frame_source_);
begin_frame_source_->DidFinishFrame(this);
- BeginFrameAck ack(current_begin_frame_args_.source_id,
- current_begin_frame_args_.sequence_number, did_draw);
+ BeginFrameAck ack(current_begin_frame_args_, did_draw);
client_->DidFinishFrame(ack);
}
diff --git a/chromium/components/viz/service/display/display_scheduler_unittest.cc b/chromium/components/viz/service/display/display_scheduler_unittest.cc
index a0135007266..b6c7a427086 100644
--- a/chromium/components/viz/service/display/display_scheduler_unittest.cc
+++ b/chromium/components/viz/service/display/display_scheduler_unittest.cc
@@ -163,8 +163,7 @@ class DisplaySchedulerTest : public testing::Test {
DisplayScheduler& scheduler() { return scheduler_; }
BeginFrameAck AckForCurrentBeginFrame() {
DCHECK(last_begin_frame_args_.IsValid());
- return BeginFrameAck(last_begin_frame_args_.source_id,
- last_begin_frame_args_.sequence_number, true);
+ return BeginFrameAck(last_begin_frame_args_, true);
}
FakeExternalBeginFrameSource fake_begin_frame_source_;
@@ -288,8 +287,7 @@ TEST_F(DisplaySchedulerTest, SurfaceDamaged) {
EXPECT_GE(now_src().NowTicks(),
scheduler_.DesiredBeginFrameDeadlineTimeForTest());
scheduler_.BeginFrameDeadlineForTest();
- EXPECT_EQ(BeginFrameAck(last_begin_frame_args_.source_id,
- last_begin_frame_args_.sequence_number, true),
+ EXPECT_EQ(BeginFrameAck(last_begin_frame_args_, true),
client_.last_begin_frame_ack());
// Set both surface 1 and 2 as active via SurfaceDamageExpected().
@@ -305,8 +303,7 @@ TEST_F(DisplaySchedulerTest, SurfaceDamaged) {
EXPECT_GE(now_src().NowTicks(),
scheduler_.DesiredBeginFrameDeadlineTimeForTest());
scheduler_.BeginFrameDeadlineForTest();
- EXPECT_EQ(BeginFrameAck(last_begin_frame_args_.source_id,
- last_begin_frame_args_.sequence_number, true),
+ EXPECT_EQ(BeginFrameAck(last_begin_frame_args_, true),
client_.last_begin_frame_ack());
// Surface damage with |!has_damage| triggers early deadline if other damage
@@ -335,8 +332,7 @@ TEST_F(DisplaySchedulerTest, SurfaceDamaged) {
EXPECT_LT(now_src().NowTicks(),
scheduler_.DesiredBeginFrameDeadlineTimeForTest());
scheduler_.BeginFrameDeadlineForTest();
- EXPECT_EQ(BeginFrameAck(last_begin_frame_args_.source_id,
- last_begin_frame_args_.sequence_number, false),
+ EXPECT_EQ(BeginFrameAck(last_begin_frame_args_, false),
client_.last_begin_frame_ack());
// System should be idle now.
@@ -786,8 +782,7 @@ TEST_F(DisplaySchedulerTest, SetNeedsOneBeginFrame) {
scheduler_.SetNeedsOneBeginFrame();
EXPECT_TRUE(scheduler_.inside_begin_frame_deadline_interval());
scheduler_.BeginFrameDeadlineForTest();
- EXPECT_EQ(BeginFrameAck(last_begin_frame_args_.source_id,
- last_begin_frame_args_.sequence_number, false),
+ EXPECT_EQ(BeginFrameAck(last_begin_frame_args_, false),
client_.last_begin_frame_ack());
// System should be idle again.
diff --git a/chromium/components/viz/service/display/display_unittest.cc b/chromium/components/viz/service/display/display_unittest.cc
index 696bdb03231..327e77f82ca 100644
--- a/chromium/components/viz/service/display/display_unittest.cc
+++ b/chromium/components/viz/service/display/display_unittest.cc
@@ -18,11 +18,11 @@
#include "components/viz/common/quads/render_pass_draw_quad.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/quads/surface_draw_quad.h"
-#include "components/viz/common/resources/shared_bitmap_manager.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/service/display/display_client.h"
#include "components/viz/service/display/display_scheduler.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.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/surfaces/surface.h"
@@ -31,7 +31,6 @@
#include "components/viz/test/fake_output_surface.h"
#include "components/viz/test/mock_compositor_frame_sink_client.h"
#include "components/viz/test/test_gles2_interface.h"
-#include "components/viz/test/test_shared_bitmap_manager.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -97,7 +96,8 @@ class TestDisplayScheduler : public DisplayScheduler {
class DisplayTest : public testing::Test {
public:
DisplayTest()
- : support_(std::make_unique<CompositorFrameSinkSupport>(
+ : manager_(&shared_bitmap_manager_),
+ support_(std::make_unique<CompositorFrameSinkSupport>(
nullptr,
&manager_,
kArbitraryFrameSinkId,
@@ -186,11 +186,11 @@ class DisplayTest : public testing::Test {
void LatencyInfoCapTest(bool over_capacity);
+ ServerSharedBitmapManager shared_bitmap_manager_;
FrameSinkManagerImpl manager_;
std::unique_ptr<CompositorFrameSinkSupport> support_;
ParentLocalSurfaceIdAllocator id_allocator_;
scoped_refptr<base::NullTaskRunner> task_runner_;
- TestSharedBitmapManager shared_bitmap_manager_;
std::unique_ptr<BeginFrameSource> begin_frame_source_;
std::unique_ptr<Display> display_;
TestSoftwareOutputDevice* software_output_device_ = nullptr;
@@ -206,6 +206,7 @@ class StubDisplayClient : public DisplayClient {
void DisplayDidDrawAndSwap() override {}
void DisplayDidReceiveCALayerParams(
const gfx::CALayerParams& ca_layer_params) override{};
+ void DisplayDidCompleteSwapWithSize(const gfx::Size& pixel_size) override {}
void DidSwapAfterSnapshotRequestReceived(
const std::vector<ui::LatencyInfo>& latency_info) override {}
};
@@ -3239,12 +3240,10 @@ TEST_F(DisplayTest, CompositorFrameWithPresentationToken) {
CompositorFrame frame =
CompositorFrameBuilder()
.AddRenderPass(gfx::Rect(sub_surface_size), gfx::Rect())
- .SetPresentationToken(1)
+ .SetFrameToken(1)
+ .SetRequestPresentationFeedback(true)
.Build();
EXPECT_CALL(sub_client, DidReceiveCompositorFrameAck(_)).Times(1);
- // TODO(penghuang): Verify DidDiscardCompositorFrame() is called when
- // GLSurface presentation callback is implemented.
- // https://crbug.com/776877
sub_support->SubmitCompositorFrame(sub_local_surface_id, std::move(frame));
}
@@ -3292,11 +3291,17 @@ TEST_F(DisplayTest, CompositorFrameWithPresentationToken) {
CompositorFrame frame =
CompositorFrameBuilder()
.AddRenderPass(gfx::Rect(sub_surface_size), gfx::Rect())
- .SetPresentationToken(2)
+ .SetFrameToken(2)
+ .SetRequestPresentationFeedback(true)
.Build();
EXPECT_CALL(sub_client, DidReceiveCompositorFrameAck(_)).Times(1);
- EXPECT_CALL(sub_client, DidDiscardCompositorFrame(2)).Times(1);
+ EXPECT_CALL(
+ sub_client,
+ DidPresentCompositorFrame(
+ 2, testing::Field(&gfx::PresentationFeedback::flags,
+ gfx::PresentationFeedback::Flags::kFailure)))
+ .Times(1);
sub_support->SubmitCompositorFrame(sub_local_surface_id, std::move(frame));
display_->DrawAndSwap();
@@ -3307,7 +3312,8 @@ TEST_F(DisplayTest, CompositorFrameWithPresentationToken) {
CompositorFrame frame =
CompositorFrameBuilder()
.AddRenderPass(gfx::Rect(sub_surface_size), gfx::Rect())
- .SetPresentationToken(3)
+ .SetFrameToken(3)
+ .SetRequestPresentationFeedback(true)
.Build();
EXPECT_CALL(sub_client, DidReceiveCompositorFrameAck(_)).Times(1);
diff --git a/chromium/components/viz/service/display/gl_renderer.cc b/chromium/components/viz/service/display/gl_renderer.cc
index afda183f41e..f37adb85d10 100644
--- a/chromium/components/viz/service/display/gl_renderer.cc
+++ b/chromium/components/viz/service/display/gl_renderer.cc
@@ -13,6 +13,7 @@
#include <numeric>
#include <set>
#include <string>
+#include <utility>
#include <vector>
#include "base/bind.h"
@@ -41,7 +42,6 @@
#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_fence.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"
@@ -50,6 +50,7 @@
#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"
@@ -757,13 +758,21 @@ GLenum GLRenderer::GetFramebufferCopyTextureFormat() {
// 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_)
+ if (!current_framebuffer_texture_) {
format = output_surface_->GetFramebufferCopyTextureFormat();
- else
- format = GLCopyTextureInternalFormat(BackbufferFormat());
+ } else {
+ ResourceFormat resource_format = BackbufferFormat();
+ 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)
+ format == GL_LUMINANCE_ALPHA || format == GL_RGB ||
+ format == GL_RGBA ||
+ (output_surface_->context_provider()
+ ->ContextCapabilities()
+ .texture_format_bgra8888 &&
+ format == GL_BGRA_EXT))
<< format;
return format;
}
@@ -831,7 +840,7 @@ sk_sp<SkImage> GLRenderer::ApplyBackgroundFilters(
sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(
use_gr_context->context(), SkBudgeted::kYes, dst_info);
if (!surface) {
- TRACE_EVENT_INSTANT0("cc",
+ TRACE_EVENT_INSTANT0("viz",
"ApplyBackgroundFilters surface allocation failed",
TRACE_EVENT_SCOPE_THREAD);
return nullptr;
@@ -1260,7 +1269,7 @@ 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.bottom_right());
+ params->quad->shared_quad_state->visible_quad_layer_rect.size());
BlendMode shader_blend_mode =
params->use_shaders_for_blending
@@ -2048,7 +2057,7 @@ void GLRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad,
TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
gl_, &highp_threshold_cache_, settings_->highp_threshold_min,
- quad->shared_quad_state->visible_quad_layer_rect.bottom_right());
+ 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;
@@ -2219,7 +2228,7 @@ void GLRenderer::DrawStreamVideoQuad(const StreamVideoDrawQuad* quad,
TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
gl_, &highp_threshold_cache_, settings_->highp_threshold_min,
- quad->shared_quad_state->visible_quad_layer_rect.bottom_right());
+ quad->shared_quad_state->visible_quad_layer_rect.size());
DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_,
quad->resource_id());
@@ -2360,12 +2369,15 @@ void GLRenderer::EnqueueTextureQuad(const TextureDrawQuad* quad,
FlushTextureQuadCache(SHARED_BINDING);
}
- TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
- gl_, &highp_threshold_cache_, settings_->highp_threshold_min,
- quad->shared_quad_state->visible_quad_layer_rect.bottom_right());
-
DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_,
quad->resource_id());
+ // ScopedReadLockGL contains the correct texture size, even when
+ // quad->resource_size_in_pixesl() 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() &&
@@ -2400,7 +2412,6 @@ void GLRenderer::EnqueueTextureQuad(const TextureDrawQuad* quad,
uv_transform = UVTransform(quad);
if (sampler == SAMPLER_TYPE_2D_RECT) {
// Un-normalize the texture coordiantes for rectangle targets.
- gfx::Size texture_size = lock.size();
uv_transform.data[0] *= texture_size.width();
uv_transform.data[2] *= texture_size.width();
uv_transform.data[1] *= texture_size.height();
@@ -2410,7 +2421,8 @@ void GLRenderer::EnqueueTextureQuad(const TextureDrawQuad* quad,
if (need_tex_clamp_rect) {
DCHECK_EQ(1u, draw_cache_.uv_xform_data.size());
- gfx::Size texture_size = quad->resource_size_in_pixels();
+ 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(),
@@ -2485,8 +2497,8 @@ void GLRenderer::FinishDrawingFrame() {
ScheduleDCLayers();
ScheduleOverlays();
- TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("cc.debug.triangles"),
- "Triangles Drawn", num_triangles_drawn_);
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("viz.triangles"), "Triangles Drawn",
+ num_triangles_drawn_);
}
void GLRenderer::FinishDrawingQuadList() {
@@ -3525,8 +3537,8 @@ void GLRenderer::FlushOverdrawFeedback(const gfx::Rect& output_rect) {
// Occlusion queries can be expensive, so only collect trace data if we select
// cc.debug.overdraw.
bool tracing_enabled;
- TRACE_EVENT_CATEGORY_GROUP_ENABLED(
- TRACE_DISABLED_BY_DEFAULT("cc.debug.overdraw"), &tracing_enabled);
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("viz.overdraw"),
+ &tracing_enabled);
// Trace only the root render pass.
if (current_frame()->current_render_pass != current_frame()->root_render_pass)
@@ -3587,7 +3599,7 @@ void GLRenderer::ProcessOverdrawFeedback(std::vector<int>* overdraw,
// Report GPU overdraw as a percentage of |max_result|.
TRACE_COUNTER1(
- TRACE_DISABLED_BY_DEFAULT("cc.debug.overdraw"), "GPU Overdraw",
+ TRACE_DISABLED_BY_DEFAULT("viz.overdraw"), "GPU Overdraw",
(std::accumulate(overdraw->begin(), overdraw->end(), 0) * 100) /
max_result);
}
diff --git a/chromium/components/viz/service/display/gl_renderer.h b/chromium/components/viz/service/display/gl_renderer.h
index 1cd981c9d71..ac1516231d6 100644
--- a/chromium/components/viz/service/display/gl_renderer.h
+++ b/chromium/components/viz/service/display/gl_renderer.h
@@ -5,6 +5,8 @@
#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_H_
#define COMPONENTS_VIZ_SERVICE_DISPLAY_GL_RENDERER_H_
+#include <map>
+#include <memory>
#include <unordered_map>
#include <vector>
@@ -241,7 +243,6 @@ class VIZ_SERVICE_EXPORT GLRenderer : public DirectRenderer {
const gfx::QuadF* clip_region);
void DrawYUVVideoQuad(const YUVVideoDrawQuad* quad,
const gfx::QuadF* clip_region);
- void DrawOverlayCandidateQuadBorder(float* gl_matrix);
void SetShaderOpacity(float opacity);
void SetShaderQuadF(const gfx::QuadF& quad);
diff --git a/chromium/components/viz/service/display/gl_renderer_copier.cc b/chromium/components/viz/service/display/gl_renderer_copier.cc
index f116895077d..ec6e7575d06 100644
--- a/chromium/components/viz/service/display/gl_renderer_copier.cc
+++ b/chromium/components/viz/service/display/gl_renderer_copier.cc
@@ -481,7 +481,6 @@ void GLRendererCopier::SendTextureResult(
// within its own GL context will be using the texture at a point in time
// after the texture has been rendered (via GLRendererCopier's GL context).
gpu::Mailbox mailbox;
- gl->GenMailboxCHROMIUM(mailbox.name);
gl->ProduceTextureDirectCHROMIUM(result_texture, mailbox.name);
gpu::SyncToken sync_token;
gl->GenSyncTokenCHROMIUM(sync_token.GetData());
diff --git a/chromium/components/viz/service/display/gl_renderer_unittest.cc b/chromium/components/viz/service/display/gl_renderer_unittest.cc
index 1913ad59553..448dbfecda6 100644
--- a/chromium/components/viz/service/display/gl_renderer_unittest.cc
+++ b/chromium/components/viz/service/display/gl_renderer_unittest.cc
@@ -9,6 +9,7 @@
#include <memory>
#include <set>
#include <tuple>
+#include <utility>
#include <vector>
#include "base/location.h"
@@ -17,14 +18,13 @@
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "cc/base/math_util.h"
-#include "cc/resources/layer_tree_resource_provider.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/fake_resource_provider.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/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
@@ -37,7 +37,6 @@
#include "components/viz/test/fake_output_surface.h"
#include "components/viz/test/test_gles2_interface.h"
#include "components/viz/test/test_shared_bitmap_manager.h"
-#include "components/viz/test/test_web_graphics_context_3d.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/context_support.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -83,6 +82,11 @@ class GLRendererTest : public testing::Test {
return renderer->current_program_;
}
+ static TexCoordPrecision get_cached_tex_coord_precision(
+ GLRenderer* renderer) {
+ return renderer->draw_cache_.program_key.tex_coord_precision();
+ }
+
RenderPassList render_passes_in_draw_order_;
};
@@ -144,7 +148,7 @@ class GLRendererShaderPixelTest : public cc::GLRendererPixelTest {
void TearDown() override {
cc::GLRendererPixelTest::TearDown();
- ASSERT_FALSE(renderer()->IsContextLost());
+ ASSERT_FALSE(renderer());
}
void TestShaderWithDrawingFrame(
@@ -485,9 +489,6 @@ class FakeRendererGL : public GLRenderer {
// GLRenderer methods.
// Changing visibility to public.
- using GLRenderer::DoDrawQuad;
- using GLRenderer::BeginDrawingFrame;
- using GLRenderer::FinishDrawingQuadList;
using GLRenderer::stencil_enabled;
};
@@ -498,9 +499,9 @@ class GLRendererWithDefaultHarnessTest : public GLRendererTest {
output_surface_->BindToClient(&output_surface_client_);
shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
- resource_provider_ =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface_->context_provider(), shared_bitmap_manager_.get());
+ resource_provider_ = std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface_->context_provider(),
+ shared_bitmap_manager_.get());
renderer_ = std::make_unique<FakeRendererGL>(
&settings_, output_surface_.get(), resource_provider_.get());
renderer_->Initialize();
@@ -531,9 +532,9 @@ class GLRendererShaderTest : public GLRendererTest {
output_surface_->BindToClient(&output_surface_client_);
shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
- resource_provider_ =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface_->context_provider(), shared_bitmap_manager_.get());
+ resource_provider_ = std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface_->context_provider(),
+ shared_bitmap_manager_.get());
renderer_.reset(new FakeRendererGL(&settings_, output_surface_.get(),
resource_provider_.get()));
renderer_->Initialize();
@@ -541,9 +542,11 @@ class GLRendererShaderTest : public GLRendererTest {
child_context_provider_ = TestContextProvider::Create();
child_context_provider_->BindToCurrentThread();
- child_resource_provider_ =
- cc::FakeResourceProvider::CreateLayerTreeResourceProvider(
- child_context_provider_.get());
+ child_resource_provider_ = std::make_unique<ClientResourceProvider>(true);
+ }
+
+ ~GLRendererShaderTest() override {
+ child_resource_provider_->ShutdownAndReleaseAllResources();
}
void TestRenderPassProgram(TexCoordPrecision precision,
@@ -635,7 +638,7 @@ class GLRendererShaderTest : public GLRendererTest {
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
std::unique_ptr<DisplayResourceProvider> resource_provider_;
scoped_refptr<TestContextProvider> child_context_provider_;
- std::unique_ptr<cc::LayerTreeResourceProvider> child_resource_provider_;
+ std::unique_ptr<ClientResourceProvider> child_resource_provider_;
std::unique_ptr<FakeRendererGL> renderer_;
};
@@ -656,6 +659,128 @@ TEST_F(GLRendererWithDefaultHarnessTest, ExternalStencil) {
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);
+ RenderPass* root_pass = cc::AddRenderPass(
+ &render_passes_in_draw_order_, 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>(true);
+
+ // Here is where the texture is created. Any value bigger than 1024 should use
+ // a highp.
+ auto transfer_resource = TransferableResource::MakeGLOverlay(
+ 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, SingleReleaseCallback::Create(base::DoNothing()));
+
+ std::unordered_map<ResourceId, ResourceId> resource_map =
+ cc::SendResourceAndGetChildToParentMap(
+ {client_resource_id}, resource_provider_.get(),
+ child_resource_provider.get(), child_context_provider.get());
+ unsigned 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::Rect(1023, 1023), false,
+ 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, false);
+
+ 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);
+ RenderPass* root_pass = cc::AddRenderPass(
+ &render_passes_in_draw_order_, 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>(true);
+
+ // Here is where the texture is created. Any value smaller than 1024 should
+ // use a mediump.
+ auto transfer_resource = TransferableResource::MakeGLOverlay(
+ 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, SingleReleaseCallback::Create(base::DoNothing()));
+
+ std::unordered_map<ResourceId, ResourceId> resource_map =
+ cc::SendResourceAndGetChildToParentMap(
+ {client_resource_id}, resource_provider_.get(),
+ child_resource_provider.get(), child_context_provider.get());
+ unsigned 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::Rect(1025, 1025), false,
+ 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, false);
+
+ 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 ForbidSynchronousCallGLES2Interface : public TestGLES2Interface {
public:
ForbidSynchronousCallGLES2Interface() = default;
@@ -775,8 +900,9 @@ TEST_F(GLRendererTest, InitializationDoesNotMakeSynchronousCalls) {
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
std::make_unique<TestSharedBitmapManager>();
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), shared_bitmap_manager.get());
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ shared_bitmap_manager.get());
RendererSettings settings;
FakeRendererGL renderer(&settings, output_surface.get(),
@@ -813,8 +939,9 @@ TEST_F(GLRendererTest, InitializationWithQuicklyLostContextDoesNotAssert) {
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
std::make_unique<TestSharedBitmapManager>();
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), shared_bitmap_manager.get());
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ shared_bitmap_manager.get());
RendererSettings settings;
FakeRendererGL renderer(&settings, output_surface.get(),
@@ -849,8 +976,9 @@ TEST_F(GLRendererTest, OpaqueBackground) {
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
std::make_unique<TestSharedBitmapManager>();
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), shared_bitmap_manager.get());
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ shared_bitmap_manager.get());
RendererSettings settings;
FakeRendererGL renderer(&settings, output_surface.get(),
@@ -894,8 +1022,9 @@ TEST_F(GLRendererTest, TransparentBackground) {
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
std::make_unique<TestSharedBitmapManager>();
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), shared_bitmap_manager.get());
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ shared_bitmap_manager.get());
RendererSettings settings;
FakeRendererGL renderer(&settings, output_surface.get(),
@@ -932,8 +1061,9 @@ TEST_F(GLRendererTest, OffscreenOutputSurface) {
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
std::make_unique<TestSharedBitmapManager>();
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), shared_bitmap_manager.get());
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ shared_bitmap_manager.get());
RendererSettings settings;
FakeRendererGL renderer(&settings, output_surface.get(),
@@ -975,6 +1105,12 @@ class TextureStateTrackingGLES2Interface : public TestGLES2Interface {
};
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>(true);
+
auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
gl_owned->set_have_extension_egl_image(true);
auto* gl = gl_owned.get();
@@ -990,8 +1126,9 @@ TEST_F(GLRendererTest, ActiveTextureState) {
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
std::make_unique<TestSharedBitmapManager>();
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), shared_bitmap_manager.get());
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ shared_bitmap_manager.get());
RendererSettings settings;
FakeRendererGL renderer(&settings, output_surface.get(),
@@ -1002,20 +1139,11 @@ TEST_F(GLRendererTest, ActiveTextureState) {
// During initialization we are allowed to set any texture parameters.
EXPECT_CALL(*gl, TexParameteri(_, _, _)).Times(AnyNumber());
- 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 =
- cc::FakeResourceProvider::CreateLayerTreeResourceProvider(
- child_context_provider.get());
-
RenderPass* root_pass =
cc::AddRenderPass(&render_passes_in_draw_order_, 1, gfx::Rect(100, 100),
gfx::Transform(), cc::FilterOperations());
gpu::SyncToken mailbox_sync_token;
- AddOneOfEveryQuadTypeInDisplayResourceProvider(
+ cc::AddOneOfEveryQuadTypeInDisplayResourceProvider(
root_pass, resource_provider.get(), child_resource_provider.get(),
child_context_provider.get(), 0, &mailbox_sync_token);
@@ -1028,7 +1156,7 @@ TEST_F(GLRendererTest, ActiveTextureState) {
{
InSequence sequence;
// The verified flush flag will be set by
- // cc::LayerTreeResourceProvider::PrepareSendToParent. Before checking if
+ // ClientResourceProvider::PrepareSendToParent. Before checking if
// the gpu::SyncToken matches, set this flag first.
mailbox_sync_token.SetVerifyFlush();
// In AddOneOfEveryQuadTypeInDisplayResourceProvider, resources are added
@@ -1059,6 +1187,8 @@ TEST_F(GLRendererTest, ActiveTextureState) {
gfx::Size viewport_size(100, 100);
DrawFrame(&renderer, viewport_size);
Mock::VerifyAndClearExpectations(gl);
+
+ child_resource_provider->ShutdownAndReleaseAllResources();
}
class NoClearRootRenderPassMockGLES2Interface : public TestGLES2Interface {
@@ -1085,8 +1215,9 @@ TEST_F(GLRendererTest, ShouldClearRootRenderPass) {
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
std::make_unique<TestSharedBitmapManager>();
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), shared_bitmap_manager.get());
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ shared_bitmap_manager.get());
RendererSettings settings;
settings.should_clear_root_render_pass = false;
@@ -1173,8 +1304,9 @@ TEST_F(GLRendererTest, ScissorTestWhenClearing) {
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
std::make_unique<TestSharedBitmapManager>();
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), shared_bitmap_manager.get());
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ shared_bitmap_manager.get());
RendererSettings settings;
FakeRendererGL renderer(&settings, output_surface.get(),
@@ -1246,8 +1378,9 @@ TEST_F(GLRendererTest, NoDiscardOnPartialUpdates) {
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
std::make_unique<TestSharedBitmapManager>();
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), shared_bitmap_manager.get());
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ shared_bitmap_manager.get());
RendererSettings settings;
settings.partial_swap_enabled = true;
@@ -1446,8 +1579,9 @@ TEST_F(GLRendererTest, NoResourceLeak) {
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
std::make_unique<TestSharedBitmapManager>();
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), shared_bitmap_manager.get());
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ shared_bitmap_manager.get());
{
RendererSettings settings;
@@ -1492,9 +1626,9 @@ class GLRendererSkipTest : public GLRendererTest {
output_surface_->BindToClient(&output_surface_client_);
shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
- resource_provider_ =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface_->context_provider(), shared_bitmap_manager_.get());
+ resource_provider_ = std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface_->context_provider(),
+ shared_bitmap_manager_.get());
settings_.partial_swap_enabled = true;
renderer_ = std::make_unique<FakeRendererGL>(
&settings_, output_surface_.get(), resource_provider_.get());
@@ -1581,8 +1715,9 @@ TEST_F(GLRendererTest, DrawFramePreservesFramebuffer) {
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
std::make_unique<TestSharedBitmapManager>();
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), shared_bitmap_manager.get());
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ shared_bitmap_manager.get());
RendererSettings settings;
FakeRendererGL renderer(&settings, output_surface.get(),
@@ -1631,9 +1766,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) {
// Return the mapped resource id.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({mask}, resource_provider_.get(),
- child_resource_provider_.get(),
- child_context_provider_.get());
+ cc::SendResourceAndGetChildToParentMap({mask}, resource_provider_.get(),
+ child_resource_provider_.get(),
+ child_context_provider_.get());
ResourceId mapped_mask = resource_map[mask];
SkScalar matrix[20];
@@ -1923,6 +2058,12 @@ class MockOutputSurface : public OutputSurface {
MOCK_METHOD0(GetFramebufferCopyTextureFormat, GLenum());
MOCK_METHOD1(SwapBuffers_, void(OutputSurfaceFrame& frame)); // NOLINT
void SwapBuffers(OutputSurfaceFrame frame) override { SwapBuffers_(frame); }
+#if BUILDFLAG(ENABLE_VULKAN)
+ gpu::VulkanSurface* GetVulkanSurface() override {
+ NOTREACHED();
+ return nullptr;
+ }
+#endif
MOCK_CONST_METHOD0(GetOverlayCandidateValidator,
OverlayCandidateValidator*());
MOCK_CONST_METHOD0(IsDisplayedAsOverlayPlane, bool());
@@ -1947,9 +2088,9 @@ class MockOutputSurfaceTest : public GLRendererTest {
output_surface_->BindToClient(&output_surface_client_);
shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
- resource_provider_ =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface_->context_provider(), shared_bitmap_manager_.get());
+ resource_provider_ = std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface_->context_provider(),
+ shared_bitmap_manager_.get());
renderer_.reset(new FakeRendererGL(&settings_, output_surface_.get(),
resource_provider_.get()));
@@ -2070,15 +2211,13 @@ TEST_F(GLRendererTest, DontOverlayWithCopyRequests) {
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
std::make_unique<TestSharedBitmapManager>();
- auto parent_resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), shared_bitmap_manager.get());
+ auto parent_resource_provider = std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ shared_bitmap_manager.get());
auto child_context_provider = TestContextProvider::Create();
child_context_provider->BindToCurrentThread();
- auto child_resource_provider =
- cc::FakeResourceProvider::CreateLayerTreeResourceProvider(
- child_context_provider.get());
+ auto child_resource_provider = std::make_unique<ClientResourceProvider>(true);
auto transfer_resource = TransferableResource::MakeGLOverlay(
gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
@@ -2197,6 +2336,9 @@ TEST_F(GLRendererTest, DontOverlayWithCopyRequests) {
// being in use.
parent_resource_provider->DeclareUsedResourcesFromChild(child_id,
ResourceIdSet());
+
+ child_resource_provider->RemoveImportedResource(resource_id);
+ child_resource_provider->ShutdownAndReleaseAllResources();
}
class SingleOverlayOnTopProcessor : public OverlayProcessor {
@@ -2212,10 +2354,18 @@ class SingleOverlayOnTopProcessor : public OverlayProcessor {
bool AllowDCLayerOverlays() override { return false; }
void CheckOverlaySupport(OverlayCandidateList* surfaces) override {
- ASSERT_EQ(1U, surfaces->size());
+ if (!multiple_candidates_)
+ ASSERT_EQ(1U, surfaces->size());
OverlayCandidate& candidate = surfaces->back();
candidate.overlay_handled = true;
}
+
+ void SetAllowMultipleCandidates(bool multiple_candidates) {
+ multiple_candidates_ = multiple_candidates;
+ }
+
+ private:
+ bool multiple_candidates_ = false;
};
explicit SingleOverlayOnTopProcessor(OutputSurface* surface)
@@ -2226,6 +2376,10 @@ class SingleOverlayOnTopProcessor : public OverlayProcessor {
std::make_unique<OverlayStrategySingleOnTop>(&validator_));
}
+ void AllowMultipleCandidates() {
+ validator_.SetAllowMultipleCandidates(true);
+ }
+
SingleOverlayValidator validator_;
};
@@ -2264,15 +2418,13 @@ TEST_F(GLRendererTest, OverlaySyncTokensAreProcessed) {
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager =
std::make_unique<TestSharedBitmapManager>();
- auto parent_resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), shared_bitmap_manager.get());
+ auto parent_resource_provider = std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ shared_bitmap_manager.get());
auto child_context_provider = TestContextProvider::Create();
child_context_provider->BindToCurrentThread();
- auto child_resource_provider =
- cc::FakeResourceProvider::CreateLayerTreeResourceProvider(
- child_context_provider.get());
+ auto child_resource_provider = std::make_unique<ClientResourceProvider>(true);
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x123), 29);
@@ -2340,7 +2492,7 @@ TEST_F(GLRendererTest, OverlaySyncTokensAreProcessed) {
flipped, nearest_neighbor, false);
// The verified flush flag will be set by
- // cc::LayerTreeResourceProvider::PrepareSendToParent. Before checking if the
+ // ClientResourceProvider::PrepareSendToParent. Before checking if the
// gpu::SyncToken matches, set this flag first.
sync_token.SetVerifyFlush();
@@ -2361,6 +2513,9 @@ TEST_F(GLRendererTest, OverlaySyncTokensAreProcessed) {
// being in use.
parent_resource_provider->DeclareUsedResourcesFromChild(child_id,
ResourceIdSet());
+
+ child_resource_provider->RemoveImportedResource(resource_id);
+ child_resource_provider->ShutdownAndReleaseAllResources();
}
class OutputColorMatrixMockGLES2Interface : public TestGLES2Interface {
@@ -2385,8 +2540,9 @@ TEST_F(GLRendererTest, OutputColorMatrixTest) {
cc::FakeOutputSurfaceClient output_surface_client;
output_surface->BindToClient(&output_surface_client);
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), nullptr);
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ nullptr);
RendererSettings settings;
FakeRendererGL renderer(&settings, output_surface.get(),
resource_provider.get());
@@ -2471,8 +2627,9 @@ TEST_F(GLRendererTest, GenerateMipmap) {
cc::FakeOutputSurfaceClient output_surface_client;
output_surface->BindToClient(&output_surface_client);
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), nullptr);
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ nullptr);
RendererSettings settings;
FakeRendererGL renderer(&settings, output_surface.get(),
resource_provider.get());
@@ -2533,8 +2690,9 @@ class GLRendererPartialSwapTest : public GLRendererTest {
output_surface->BindToClient(&output_surface_client);
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), nullptr);
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ nullptr);
RendererSettings settings;
settings.partial_swap_enabled = partial_swap;
@@ -2647,15 +2805,13 @@ TEST_F(GLRendererTest, DCLayerOverlaySwitch) {
FakeOutputSurface::Create3d(std::move(provider)));
output_surface->BindToClient(&output_surface_client);
- auto parent_resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), nullptr);
+ auto parent_resource_provider = std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ nullptr);
auto child_context_provider = TestContextProvider::Create();
child_context_provider->BindToCurrentThread();
- auto child_resource_provider =
- cc::FakeResourceProvider::CreateLayerTreeResourceProvider(
- child_context_provider.get());
+ auto child_resource_provider = std::make_unique<ClientResourceProvider>(true);
auto transfer_resource = TransferableResource::MakeGLOverlay(
gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
@@ -2749,6 +2905,9 @@ TEST_F(GLRendererTest, DCLayerOverlaySwitch) {
// being in use.
parent_resource_provider->DeclareUsedResourcesFromChild(child_id,
ResourceIdSet());
+
+ child_resource_provider->RemoveImportedResource(resource_id);
+ child_resource_provider->ShutdownAndReleaseAllResources();
}
class GLRendererWithMockContextTest : public ::testing::Test {
@@ -2769,9 +2928,9 @@ class GLRendererWithMockContextTest : public ::testing::Test {
gpu::ContextResult::kSuccess);
output_surface_ = FakeOutputSurface::Create3d(std::move(context_provider));
output_surface_->BindToClient(&output_surface_client_);
- resource_provider_ =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface_->context_provider(), nullptr);
+ resource_provider_ = std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface_->context_provider(),
+ nullptr);
renderer_ = std::make_unique<GLRenderer>(&settings_, output_surface_.get(),
resource_provider_.get(), nullptr);
renderer_->Initialize();
@@ -2848,8 +3007,9 @@ class GLRendererSwapWithBoundsTest : public GLRendererTest {
output_surface->BindToClient(&output_surface_client);
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), nullptr);
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ nullptr);
RendererSettings settings;
FakeRendererGL renderer(&settings, output_surface.get(),
@@ -2948,9 +3108,9 @@ class CALayerGLRendererTest : public GLRendererTest {
// we can skip the root RenderPass, swapping empty.
output_surface_->SetOverlayCandidateValidator(&validator_);
- display_resource_provider_ =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface_->context_provider(), nullptr);
+ display_resource_provider_ = std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface_->context_provider(),
+ nullptr);
settings_ = std::make_unique<RendererSettings>();
// This setting is enabled to use CALayer overlays.
@@ -3787,7 +3947,7 @@ class FramebufferWatchingGLRenderer : public FakeRendererGL {
TEST_F(GLRendererTest, UndamagedRenderPassStillDrawnWhenNoPartialSwap) {
auto provider = TestContextProvider::Create();
- provider->UnboundTestContext3d()->set_have_post_sub_buffer(true);
+ provider->UnboundTestContextGL()->set_have_post_sub_buffer(true);
provider->BindToCurrentThread();
cc::FakeOutputSurfaceClient output_surface_client;
@@ -3795,8 +3955,9 @@ TEST_F(GLRendererTest, UndamagedRenderPassStillDrawnWhenNoPartialSwap) {
output_surface->BindToClient(&output_surface_client);
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- output_surface->context_provider(), nullptr);
+ std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface->context_provider(),
+ nullptr);
for (int i = 0; i < 2; ++i) {
bool use_partial_swap = i == 0;
@@ -3881,5 +4042,127 @@ TEST_F(GLRendererTest, UndamagedRenderPassStillDrawnWhenNoPartialSwap) {
}
}
+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<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, output_surface_->context_provider(),
+ nullptr);
+
+ renderer_ = std::make_unique<FakeRendererGL>(
+ &settings_, output_surface_.get(), resource_provider_.get(),
+ base::ThreadTaskRunnerHandle::Get());
+ renderer_->Initialize();
+ renderer_->SetVisible(true);
+
+ auto* processor = new SingleOverlayOnTopProcessor(output_surface_.get());
+ processor->AllowMultipleCandidates();
+ processor->Initialize();
+ renderer_->SetOverlayProcessor(processor);
+
+ 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>(true);
+ auto transfer_resource = TransferableResource::MakeGLOverlay(
+ 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, SingleReleaseCallback::Create(base::DoNothing()));
+
+ std::unordered_map<ResourceId, ResourceId> 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;
+
+ TestContextSupport* test_context_support_;
+
+ cc::FakeOutputSurfaceClient output_surface_client_;
+ std::unique_ptr<FakeOutputSurface> output_surface_;
+ std::unique_ptr<DisplayResourceProvider> resource_provider_;
+ scoped_refptr<TestContextProvider> child_context_provider_;
+ std::unique_ptr<ClientResourceProvider> child_resource_provider_;
+ RendererSettings settings_;
+ std::unique_ptr<FakeRendererGL> renderer_;
+ MockOverlayScheduler overlay_scheduler;
+};
+
+TEST_F(GLRendererWithGpuFenceTest, GpuFenceIdIsUsedWithRootRenderPass) {
+ gfx::Size viewport_size(100, 100);
+ RenderPass* root_pass = cc::AddRenderPass(
+ &render_passes_in_draw_order_, 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, GpuFenceIdIsUsedWithoutRootRenderPass) {
+ gfx::Size viewport_size(100, 100);
+ RenderPass* root_pass = cc::AddRenderPass(
+ &render_passes_in_draw_order_, 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);
+
+ // Add a draw quad covering the whole viewport. This causes the root
+ // render pass to be skipped.
+ 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::Rect(viewport_size),
+ false, 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, false);
+
+ 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), _, _, kGpuFenceId))
+ .Times(1);
+ DrawFrame(renderer_.get(), viewport_size);
+}
+
} // namespace
} // namespace viz
diff --git a/chromium/components/viz/service/display/output_surface.cc b/chromium/components/viz/service/display/output_surface.cc
index f563afc1deb..aeec9fccf91 100644
--- a/chromium/components/viz/service/display/output_surface.cc
+++ b/chromium/components/viz/service/display/output_surface.cc
@@ -21,6 +21,8 @@
namespace viz {
+OutputSurface::OutputSurface() = default;
+
OutputSurface::OutputSurface(scoped_refptr<ContextProvider> context_provider)
: context_provider_(std::move(context_provider)) {
DCHECK(context_provider_);
@@ -47,20 +49,15 @@ void OutputSurface::UpdateLatencyInfoOnSwap(
std::vector<ui::LatencyInfo>* latency_info) {
for (auto& latency : *latency_info) {
latency.AddLatencyNumberWithTimestamp(
- ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, 0, response.swap_start, 1);
+ ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, response.swap_start, 1);
latency.AddLatencyNumberWithTimestamp(
- ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0,
- response.swap_end, 1);
+ ui::INPUT_EVENT_LATENCY_FRAME_SWAP_COMPONENT, response.swap_end, 1);
}
}
-bool OutputSurface::LatencyInfoHasSnapshotRequest(
- const std::vector<ui::LatencyInfo>& latency_info) {
- for (const auto& latency : latency_info) {
- if (latency.Snapshots().size())
- return true;
- }
- return false;
+void OutputSurface::SetNeedsSwapSizeNotifications(
+ bool needs_swap_size_notifications) {
+ DCHECK(!needs_swap_size_notifications);
}
} // namespace viz
diff --git a/chromium/components/viz/service/display/output_surface.h b/chromium/components/viz/service/display/output_surface.h
index 9eaef93529a..8ed647a451b 100644
--- a/chromium/components/viz/service/display/output_surface.h
+++ b/chromium/components/viz/service/display/output_surface.h
@@ -56,6 +56,8 @@ class VIZ_SERVICE_EXPORT OutputSurface {
bool supports_stencil = false;
};
+ // Constructor for skia-based compositing.
+ OutputSurface();
// Constructor for GL-based compositing.
explicit OutputSurface(scoped_refptr<ContextProvider> context_provider);
// Constructor for software compositing.
@@ -146,10 +148,12 @@ class VIZ_SERVICE_EXPORT OutputSurface {
// can be passed directly to any related extension functions.
virtual unsigned UpdateGpuFence() = 0;
- // Returns true if any of the LatencyInfos provided contains a snapshot
- // request.
- static bool LatencyInfoHasSnapshotRequest(
- const std::vector<ui::LatencyInfo>& latency_info);
+ // If set to true, the OutputSurface must deliver
+ // OutputSurfaceclient::DidSwapWithSize notifications to its client.
+ // OutputSurfaces which support delivering swap size notifications should
+ // override this.
+ virtual void SetNeedsSwapSizeNotifications(
+ bool needs_swap_size_notifications);
// Updates timing info on the provided LatencyInfo when swap completes.
static void UpdateLatencyInfoOnSwap(
diff --git a/chromium/components/viz/service/display/output_surface_client.h b/chromium/components/viz/service/display/output_surface_client.h
index e802952c92d..aeb78a7d559 100644
--- a/chromium/components/viz/service/display/output_surface_client.h
+++ b/chromium/components/viz/service/display/output_surface_client.h
@@ -39,6 +39,10 @@ class VIZ_SERVICE_EXPORT OutputSurfaceClient {
virtual void DidReceiveCALayerParams(
const gfx::CALayerParams& ca_layer_params) = 0;
+ // For sending swap sizes back to the browser process. Currently only used on
+ // Android.
+ virtual void DidSwapWithSize(const gfx::Size& pixel_size) = 0;
+
// See |gfx::PresentationFeedback| for detail.
virtual void DidReceivePresentationFeedback(
const gfx::PresentationFeedback& feedback) {}
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 b585b1c9f2e..dce123d5016 100644
--- a/chromium/components/viz/service/display/overlay_strategy_underlay_cast.cc
+++ b/chromium/components/viz/service/display/overlay_strategy_underlay_cast.cc
@@ -62,6 +62,11 @@ bool OverlayStrategyUnderlayCast::Attempt(
}
}
+ if (is_using_overlay_ != found_underlay) {
+ is_using_overlay_ = found_underlay;
+ VLOG(1) << (found_underlay ? "Overlay activated" : "Overlay deactivated");
+ }
+
if (found_underlay) {
// If the primary plane shows up in the candidates list make sure it isn't
// opaque otherwise the video underlay won't be visible.
diff --git a/chromium/components/viz/service/display/overlay_strategy_underlay_cast.h b/chromium/components/viz/service/display/overlay_strategy_underlay_cast.h
index ea73e340b5d..028c2b4e475 100644
--- a/chromium/components/viz/service/display/overlay_strategy_underlay_cast.h
+++ b/chromium/components/viz/service/display/overlay_strategy_underlay_cast.h
@@ -39,6 +39,9 @@ class VIZ_SERVICE_EXPORT OverlayStrategyUnderlayCast
OverlayProcessor::StrategyType GetUMAEnum() const override;
private:
+ // Keep track if an overlay is being used on the previous frame.
+ bool is_using_overlay_ = false;
+
DISALLOW_COPY_AND_ASSIGN(OverlayStrategyUnderlayCast);
};
diff --git a/chromium/components/viz/service/display/overlay_unittest.cc b/chromium/components/viz/service/display/overlay_unittest.cc
index 119841fc713..cb57813a5d4 100644
--- a/chromium/components/viz/service/display/overlay_unittest.cc
+++ b/chromium/components/viz/service/display/overlay_unittest.cc
@@ -9,11 +9,10 @@
#include "base/containers/flat_map.h"
#include "base/test/scoped_feature_list.h"
-#include "cc/resources/layer_tree_resource_provider.h"
#include "cc/test/fake_output_surface_client.h"
-#include "cc/test/fake_resource_provider.h"
#include "cc/test/geometry_test_utils.h"
#include "cc/test/resource_provider_test_utils.h"
+#include "components/viz/client/client_resource_provider.h"
#include "components/viz/common/quads/render_pass.h"
#include "components/viz/common/quads/render_pass_draw_quad.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
@@ -33,8 +32,8 @@
#include "components/viz/service/display/overlay_strategy_underlay.h"
#include "components/viz/service/display/overlay_strategy_underlay_cast.h"
#include "components/viz/test/test_context_provider.h"
+#include "components/viz/test/test_gles2_interface.h"
#include "components/viz/test/test_shared_bitmap_manager.h"
-#include "components/viz/test/test_web_graphics_context_3d.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect_conversions.h"
@@ -205,6 +204,12 @@ class OverlayOutputSurface : public OutputSurface {
bool has_alpha,
bool use_stencil) override {}
void SwapBuffers(OutputSurfaceFrame frame) override {}
+#if BUILDFLAG(ENABLE_VULKAN)
+ gpu::VulkanSurface* GetVulkanSurface() override {
+ NOTREACHED();
+ return nullptr;
+ }
+#endif
uint32_t GetFramebufferCopyTextureFormat() override {
// TestContextProvider has no real framebuffer, just use RGB.
return GL_RGB;
@@ -266,7 +271,7 @@ std::unique_ptr<RenderPass> CreateRenderPassWithTransform(
}
static ResourceId CreateResourceInLayerTree(
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
const gfx::Size& size,
bool is_overlay_candidate) {
auto resource = TransferableResource::MakeGLOverlay(
@@ -281,12 +286,11 @@ static ResourceId CreateResourceInLayerTree(
return resource_id;
}
-ResourceId CreateResource(
- DisplayResourceProvider* parent_resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
- ContextProvider* child_context_provider,
- const gfx::Size& size,
- bool is_overlay_candidate) {
+ResourceId CreateResource(DisplayResourceProvider* parent_resource_provider,
+ ClientResourceProvider* child_resource_provider,
+ ContextProvider* child_context_provider,
+ const gfx::Size& size,
+ bool is_overlay_candidate) {
ResourceId resource_id = CreateResourceInLayerTree(
child_resource_provider, size, is_overlay_candidate);
@@ -301,6 +305,10 @@ ResourceId CreateResource(
child_context_provider);
parent_resource_provider->ReceiveFromChild(child_id, list);
+ // Delete it in the child so it won't be leaked, and will be released once
+ // returned from the parent.
+ child_resource_provider->RemoveImportedResource(resource_id);
+
// In DisplayResourceProvider's namespace, use the mapped resource id.
std::unordered_map<ResourceId, ResourceId> resource_map =
parent_resource_provider->GetChildToParentMap(child_id);
@@ -320,7 +328,7 @@ SolidColorDrawQuad* CreateSolidColorQuadAt(
TextureDrawQuad* CreateCandidateQuadAt(
DisplayResourceProvider* parent_resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
ContextProvider* child_context_provider,
const SharedQuadState* shared_quad_state,
RenderPass* render_pass,
@@ -348,7 +356,7 @@ TextureDrawQuad* CreateCandidateQuadAt(
TextureDrawQuad* CreateTransparentCandidateQuadAt(
DisplayResourceProvider* parent_resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
ContextProvider* child_context_provider,
const SharedQuadState* shared_quad_state,
RenderPass* render_pass,
@@ -376,7 +384,7 @@ TextureDrawQuad* CreateTransparentCandidateQuadAt(
StreamVideoDrawQuad* CreateCandidateVideoQuadAt(
DisplayResourceProvider* parent_resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
ContextProvider* child_context_provider,
const SharedQuadState* shared_quad_state,
RenderPass* render_pass,
@@ -399,7 +407,7 @@ StreamVideoDrawQuad* CreateCandidateVideoQuadAt(
TextureDrawQuad* CreateFullscreenCandidateQuad(
DisplayResourceProvider* parent_resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
ContextProvider* child_context_provider,
const SharedQuadState* shared_quad_state,
RenderPass* render_pass) {
@@ -410,7 +418,7 @@ TextureDrawQuad* CreateFullscreenCandidateQuad(
StreamVideoDrawQuad* CreateFullscreenCandidateVideoQuad(
DisplayResourceProvider* parent_resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
ContextProvider* child_context_provider,
const SharedQuadState* shared_quad_state,
RenderPass* render_pass,
@@ -422,7 +430,7 @@ StreamVideoDrawQuad* CreateFullscreenCandidateVideoQuad(
YUVVideoDrawQuad* CreateFullscreenCandidateYUVVideoQuad(
DisplayResourceProvider* parent_resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
ContextProvider* child_context_provider,
const SharedQuadState* shared_quad_state,
RenderPass* render_pass) {
@@ -524,28 +532,37 @@ class OverlayTest : public testing::Test {
new OverlayCandidateValidatorType);
shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
- resource_provider_ =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- provider_.get(), shared_bitmap_manager_.get());
+ resource_provider_ = std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, provider_.get(),
+ shared_bitmap_manager_.get());
child_provider_ = TestContextProvider::Create();
child_provider_->BindToCurrentThread();
- child_resource_provider_ =
- cc::FakeResourceProvider::CreateLayerTreeResourceProvider(
- child_provider_.get());
+ child_resource_provider_ = std::make_unique<ClientResourceProvider>(true);
overlay_processor_ =
std::make_unique<OverlayProcessor>(output_surface_.get());
overlay_processor_->Initialize();
}
+ void TearDown() override {
+ overlay_processor_ = nullptr;
+ child_resource_provider_->ShutdownAndReleaseAllResources();
+ child_resource_provider_ = nullptr;
+ child_provider_ = nullptr;
+ resource_provider_ = nullptr;
+ shared_bitmap_manager_ = nullptr;
+ output_surface_ = nullptr;
+ provider_ = nullptr;
+ }
+
scoped_refptr<TestContextProvider> provider_;
std::unique_ptr<OutputSurfaceType> output_surface_;
cc::FakeOutputSurfaceClient client_;
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
std::unique_ptr<DisplayResourceProvider> resource_provider_;
scoped_refptr<TestContextProvider> child_provider_;
- std::unique_ptr<cc::LayerTreeResourceProvider> child_resource_provider_;
+ std::unique_ptr<ClientResourceProvider> child_resource_provider_;
std::unique_ptr<OverlayProcessor> overlay_processor_;
gfx::Rect damage_rect_;
std::vector<gfx::Rect> content_bounds_;
@@ -578,8 +595,9 @@ TEST(OverlayTest, OverlaysProcessorHasStrategy) {
auto shared_bitmap_manager = std::make_unique<TestSharedBitmapManager>();
std::unique_ptr<DisplayResourceProvider> resource_provider =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- provider.get(), shared_bitmap_manager.get());
+ std::make_unique<DisplayResourceProvider>(DisplayResourceProvider::kGpu,
+ provider.get(),
+ shared_bitmap_manager.get());
auto overlay_processor =
std::make_unique<DefaultOverlayProcessor>(&output_surface);
@@ -659,7 +677,7 @@ TEST_F(FullscreenOverlayTest, AlphaFail) {
CreateTransparentCandidateQuadAt(
resource_provider_.get(), child_resource_provider_.get(),
child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(),
- pass.get()->output_rect);
+ pass->output_rect);
// Check for potential candidates.
OverlayCandidateList candidate_list;
@@ -2203,20 +2221,9 @@ TEST_F(CALayerOverlayTest, SkipTransparent) {
EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
}
-class DCLayerOverlayTest : public OverlayTest<DCLayerValidator>,
- public ::testing::WithParamInterface<bool> {
- void SetUp() override {
- OverlayTest<DCLayerValidator>::SetUp();
- if (GetParam())
- feature_list_.InitAndEnableFeature(
- features::kDirectCompositionNonrootOverlays);
- }
-
- private:
- base::test::ScopedFeatureList feature_list_;
-};
+class DCLayerOverlayTest : public OverlayTest<DCLayerValidator> {};
-TEST_P(DCLayerOverlayTest, AllowNonAxisAlignedTransform) {
+TEST_F(DCLayerOverlayTest, AllowNonAxisAlignedTransform) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::kDirectCompositionComplexOverlays);
@@ -2247,7 +2254,7 @@ TEST_P(DCLayerOverlayTest, AllowNonAxisAlignedTransform) {
EXPECT_EQ(gfx::Rect(1, 1, 10, 10), damage_rect_);
}
-TEST_P(DCLayerOverlayTest, AllowRequiredNonAxisAlignedTransform) {
+TEST_F(DCLayerOverlayTest, AllowRequiredNonAxisAlignedTransform) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::kDirectCompositionNonrootOverlays);
@@ -2279,19 +2286,25 @@ TEST_P(DCLayerOverlayTest, AllowRequiredNonAxisAlignedTransform) {
EXPECT_EQ(gfx::Rect(1, 1, 10, 10), damage_rect_);
}
-TEST_P(DCLayerOverlayTest, Occluded) {
+TEST_F(DCLayerOverlayTest, Occluded) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kDirectCompositionUnderlays);
{
std::unique_ptr<RenderPass> pass = CreateRenderPass();
CreateOpaqueQuadAt(resource_provider_.get(),
pass->shared_quad_state_list.back(), pass.get(),
- gfx::Rect(0, 2, 100, 100), SK_ColorWHITE);
+ gfx::Rect(0, 3, 100, 100), SK_ColorWHITE);
CreateFullscreenCandidateYUVVideoQuad(
resource_provider_.get(), child_resource_provider_.get(),
child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
- gfx::Rect damage_rect;
+ auto* second_video_quad = CreateFullscreenCandidateYUVVideoQuad(
+ resource_provider_.get(), child_resource_provider_.get(),
+ child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+ second_video_quad->require_overlay = true;
+ second_video_quad->rect.set_origin(gfx::Point(2, 2));
+ second_video_quad->visible_rect.set_origin(gfx::Point(2, 2));
+
DCLayerOverlayList dc_layer_list;
OverlayCandidateList overlay_list;
OverlayProcessor::FilterOperationsMap render_pass_filters;
@@ -2303,9 +2316,8 @@ TEST_P(DCLayerOverlayTest, Occluded) {
resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
render_pass_filters, render_pass_background_filters, &overlay_list,
nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
- EXPECT_EQ(gfx::Rect(), damage_rect);
EXPECT_EQ(0U, overlay_list.size());
- EXPECT_EQ(1U, dc_layer_list.size());
+ EXPECT_EQ(2U, dc_layer_list.size());
EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
EXPECT_EQ(-1, dc_layer_list.back().shared_state->z_order);
// Entire underlay rect must be redrawn.
@@ -2315,12 +2327,18 @@ TEST_P(DCLayerOverlayTest, Occluded) {
std::unique_ptr<RenderPass> pass = CreateRenderPass();
CreateOpaqueQuadAt(resource_provider_.get(),
pass->shared_quad_state_list.back(), pass.get(),
- gfx::Rect(2, 2, 100, 100), SK_ColorWHITE);
+ gfx::Rect(3, 3, 100, 100), SK_ColorWHITE);
CreateFullscreenCandidateYUVVideoQuad(
resource_provider_.get(), child_resource_provider_.get(),
child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
- gfx::Rect damage_rect;
+ auto* second_video_quad = CreateFullscreenCandidateYUVVideoQuad(
+ resource_provider_.get(), child_resource_provider_.get(),
+ child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+ second_video_quad->require_overlay = true;
+ second_video_quad->rect.set_origin(gfx::Point(2, 2));
+ second_video_quad->visible_rect.set_origin(gfx::Point(2, 2));
+
DCLayerOverlayList dc_layer_list;
OverlayCandidateList overlay_list;
OverlayProcessor::FilterOperationsMap render_pass_filters;
@@ -2332,18 +2350,18 @@ TEST_P(DCLayerOverlayTest, Occluded) {
resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
render_pass_filters, render_pass_background_filters, &overlay_list,
nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
- EXPECT_EQ(gfx::Rect(), damage_rect);
EXPECT_EQ(0U, overlay_list.size());
- EXPECT_EQ(1U, dc_layer_list.size());
+ EXPECT_EQ(2U, dc_layer_list.size());
EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
EXPECT_EQ(-1, dc_layer_list.back().shared_state->z_order);
- // The underlay rectangle is the same, so the damage is contained within
- // the combined occluding rects for this and the last frame.
- EXPECT_EQ(gfx::Rect(1, 2, 10, 9), damage_rect_);
+ // The underlay rectangle is the same, so the damage for first video quad is
+ // contained within the combined occluding rects for this and the last
+ // frame. Second video quad also adds its damage.
+ EXPECT_EQ(gfx::Rect(1, 2, 255, 254), damage_rect_);
}
}
-TEST_P(DCLayerOverlayTest, DamageRect) {
+TEST_F(DCLayerOverlayTest, DamageRect) {
for (int i = 0; i < 2; i++) {
std::unique_ptr<RenderPass> pass = CreateRenderPass();
CreateFullscreenCandidateYUVVideoQuad(
@@ -2377,76 +2395,147 @@ TEST_P(DCLayerOverlayTest, DamageRect) {
}
}
-TEST_P(DCLayerOverlayTest, MultiplePassDamageRect) {
- for (int i = 0; i < 2; i++) {
- RenderPassId child_pass_id(5);
- std::unique_ptr<RenderPass> pass1 = CreateRenderPass();
- pass1->id = child_pass_id;
- YUVVideoDrawQuad* yuv_quad = CreateFullscreenCandidateYUVVideoQuad(
- resource_provider_.get(), child_resource_provider_.get(),
- child_provider_.get(), pass1->shared_quad_state_list.back(),
- pass1.get());
- yuv_quad->require_overlay = true;
- pass1->damage_rect = gfx::Rect();
- pass1->transform_to_root_target.Translate(0, 100);
- pass1->shared_quad_state_list.back()->opacity = 0.9f;
-
- std::unique_ptr<RenderPass> pass2 = CreateRenderPass();
-
- gfx::Rect rect(0, 0, 1, 1);
- auto* render_pass_quad =
- pass2->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
- render_pass_quad->SetNew(pass2->shared_quad_state_list.back(), rect, rect,
- child_pass_id, 0, gfx::RectF(), gfx::Size(),
- gfx::Vector2dF(), gfx::PointF(),
- gfx::RectF(0, 0, 1, 1), false);
- pass2->shared_quad_state_list.back()->quad_to_target_transform =
- pass1->transform_to_root_target;
- pass2->shared_quad_state_list.back()->opacity = 0.8f;
- pass2->damage_rect = gfx::Rect();
+TEST_F(DCLayerOverlayTest, MultiplePassDamageRect) {
+ gfx::Transform child_pass1_transform;
+ child_pass1_transform.Translate(0, 100);
- gfx::Rect damage_rect;
- DCLayerOverlayList dc_layer_list;
- OverlayCandidateList overlay_list;
- OverlayProcessor::FilterOperationsMap render_pass_filters;
- OverlayProcessor::FilterOperationsMap render_pass_background_filters;
- damage_rect_ = gfx::Rect();
- RenderPassList pass_list;
- pass_list.push_back(std::move(pass1));
- pass_list.push_back(std::move(pass2));
- overlay_processor_->ProcessForOverlays(
- resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
- render_pass_filters, render_pass_background_filters, &overlay_list,
- nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
- EXPECT_EQ(gfx::Rect(), damage_rect);
- EXPECT_EQ(0U, overlay_list.size());
- EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
- if (GetParam()) {
- // With nonroot overlays enabled, the portions of both RenderPasses
- // corresponding to the overlay should always be damaged.
- ASSERT_EQ(1U, dc_layer_list.size());
- EXPECT_EQ(-1, dc_layer_list.back().shared_state->z_order);
- EXPECT_EQ(gfx::Rect(0, 0, 256, 256), pass_list[0]->damage_rect);
- EXPECT_EQ(gfx::Rect(0, 100, 256, 156), damage_rect_);
- gfx::Rect overlay_damage = overlay_processor_->GetAndResetOverlayDamage();
- EXPECT_EQ(gfx::Rect(0, 100, 256, 256), overlay_damage);
-
- EXPECT_EQ(1u, pass_list[0]->quad_list.size());
- EXPECT_EQ(0.9f,
- pass_list[0]->quad_list.back()->shared_quad_state->opacity);
- EXPECT_EQ(2u, pass_list[1]->quad_list.size());
- EXPECT_EQ(0.9f * 0.8f,
- pass_list[1]->quad_list.back()->shared_quad_state->opacity);
- } else {
- // Without nonroot overlays, no overlays should be created.
- EXPECT_EQ(0U, dc_layer_list.size());
- EXPECT_EQ(1u, pass_list[0]->quad_list.size());
- EXPECT_EQ(1u, pass_list[1]->quad_list.size());
- }
- }
-}
+ RenderPassId child_pass1_id(5);
+ std::unique_ptr<RenderPass> child_pass1 = CreateRenderPass();
+ ASSERT_EQ(child_pass1->shared_quad_state_list.size(), 1u);
+ child_pass1->id = child_pass1_id;
+ child_pass1->damage_rect = gfx::Rect();
+ child_pass1->transform_to_root_target = child_pass1_transform;
+ child_pass1->shared_quad_state_list.back()->opacity = 0.9f;
+ child_pass1->shared_quad_state_list.back()->blend_mode =
+ SkBlendMode::kSrcOver;
+
+ YUVVideoDrawQuad* yuv_quad_required = CreateFullscreenCandidateYUVVideoQuad(
+ resource_provider_.get(), child_resource_provider_.get(),
+ child_provider_.get(), child_pass1->shared_quad_state_list.back(),
+ child_pass1.get());
+ yuv_quad_required->require_overlay = true;
+
+ RenderPassId child_pass2_id(6);
+ std::unique_ptr<RenderPass> child_pass2 = CreateRenderPass();
+ ASSERT_EQ(child_pass2->shared_quad_state_list.size(), 1u);
+ child_pass2->id = child_pass2_id;
+ child_pass2->damage_rect = gfx::Rect();
+ child_pass2->transform_to_root_target = gfx::Transform();
+ child_pass2->shared_quad_state_list.back()->opacity = 0.8f;
+
+ YUVVideoDrawQuad* yuv_quad_not_required =
+ CreateFullscreenCandidateYUVVideoQuad(
+ resource_provider_.get(), child_resource_provider_.get(),
+ child_provider_.get(), child_pass2->shared_quad_state_list.back(),
+ child_pass2.get());
+ yuv_quad_not_required->require_overlay = false;
+
+ std::unique_ptr<RenderPass> root_pass = CreateRenderPass();
+ root_pass->CreateAndAppendSharedQuadState();
+ ASSERT_EQ(root_pass->shared_quad_state_list.size(), 2u);
+
+ SharedQuadState* child_pass1_sqs =
+ root_pass->shared_quad_state_list.ElementAt(0);
+ child_pass1_sqs->quad_to_target_transform =
+ child_pass1->transform_to_root_target;
+ child_pass1_sqs->opacity = 0.7f;
+
+ gfx::Rect unit_rect(0, 0, 1, 1);
+ auto* child_pass1_rpdq =
+ root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
+ child_pass1_rpdq->SetNew(child_pass1_sqs, unit_rect, unit_rect,
+ child_pass1_id, 0, gfx::RectF(), gfx::Size(),
+ gfx::Vector2dF(), gfx::PointF(),
+ gfx::RectF(0, 0, 1, 1), false);
+
+ SharedQuadState* child_pass2_sqs =
+ root_pass->shared_quad_state_list.ElementAt(1);
+ child_pass2_sqs->quad_to_target_transform =
+ child_pass2->transform_to_root_target;
+ child_pass2_sqs->opacity = 0.6f;
+
+ auto* child_pass2_rpdq =
+ root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
+ child_pass2_rpdq->SetNew(child_pass2_sqs, unit_rect, unit_rect,
+ child_pass2_id, 0, gfx::RectF(), gfx::Size(),
+ gfx::Vector2dF(), gfx::PointF(),
+ gfx::RectF(0, 0, 1, 1), false);
+
+ root_pass->damage_rect = gfx::Rect();
+
+ gfx::Rect root_damage_rect;
+ DCLayerOverlayList dc_layer_list;
+ OverlayCandidateList overlay_list;
+ OverlayProcessor::FilterOperationsMap render_pass_filters;
+ OverlayProcessor::FilterOperationsMap render_pass_background_filters;
+ RenderPassList pass_list;
+ pass_list.push_back(std::move(child_pass1));
+ pass_list.push_back(std::move(child_pass2));
+ pass_list.push_back(std::move(root_pass));
+ overlay_processor_->ProcessForOverlays(
+ resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+ render_pass_filters, render_pass_background_filters, &overlay_list,
+ nullptr, &dc_layer_list, &root_damage_rect, &content_bounds_);
+ EXPECT_EQ(0U, overlay_list.size());
+ EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-TEST_P(DCLayerOverlayTest, ClipRect) {
+ // Only the |require_overlay| video quad produces damage.
+ ASSERT_EQ(1U, dc_layer_list.size());
+ EXPECT_EQ(-1, dc_layer_list[0].shared_state->z_order);
+ EXPECT_EQ(gfx::Rect(0, 0, 256, 256), pass_list[0]->damage_rect);
+ EXPECT_EQ(gfx::Rect(), pass_list[1]->damage_rect);
+ EXPECT_EQ(gfx::Rect(0, 100, 256, 156), root_damage_rect);
+ gfx::Rect overlay_damage = overlay_processor_->GetAndResetOverlayDamage();
+ EXPECT_EQ(gfx::Rect(0, 100, 256, 256), overlay_damage);
+
+ EXPECT_EQ(1u, pass_list[0]->quad_list.size());
+ EXPECT_EQ(DrawQuad::SOLID_COLOR,
+ pass_list[0]->quad_list.ElementAt(0)->material);
+
+ // The |require_overlay| video quad is put into an underlay, and replaced by a
+ // solid color quad.
+ auto* yuv_solid_color_quad =
+ static_cast<SolidColorDrawQuad*>(pass_list[0]->quad_list.ElementAt(0));
+ EXPECT_EQ(SK_ColorBLACK, yuv_solid_color_quad->color);
+ EXPECT_EQ(gfx::Rect(0, 0, 256, 256), yuv_solid_color_quad->rect);
+ EXPECT_TRUE(yuv_solid_color_quad->shared_quad_state->quad_to_target_transform
+ .IsIdentity());
+ EXPECT_EQ(0.9f, yuv_solid_color_quad->shared_quad_state->opacity);
+ EXPECT_EQ(SkBlendMode::kDstOut,
+ yuv_solid_color_quad->shared_quad_state->blend_mode);
+
+ // The non required video quad is not put into an underlay.
+ EXPECT_EQ(1u, pass_list[1]->quad_list.size());
+ EXPECT_EQ(yuv_quad_not_required, pass_list[1]->quad_list.ElementAt(0));
+
+ EXPECT_EQ(3u, pass_list[2]->quad_list.size());
+
+ // The RPDQs are not modified.
+ EXPECT_EQ(DrawQuad::RENDER_PASS,
+ pass_list[2]->quad_list.ElementAt(0)->material);
+ EXPECT_EQ(child_pass1_id, static_cast<RenderPassDrawQuad*>(
+ pass_list[2]->quad_list.ElementAt(0))
+ ->render_pass_id);
+
+ // A solid color quad is put behind the RPDQ containing the video.
+ EXPECT_EQ(DrawQuad::SOLID_COLOR,
+ pass_list[2]->quad_list.ElementAt(1)->material);
+ auto* rpdq_solid_color_quad =
+ static_cast<SolidColorDrawQuad*>(pass_list[2]->quad_list.ElementAt(1));
+ EXPECT_EQ(SK_ColorTRANSPARENT, rpdq_solid_color_quad->color);
+ EXPECT_EQ(child_pass1_transform,
+ rpdq_solid_color_quad->shared_quad_state->quad_to_target_transform);
+ EXPECT_EQ(1.f, rpdq_solid_color_quad->shared_quad_state->opacity);
+ EXPECT_FALSE(rpdq_solid_color_quad->ShouldDrawWithBlending());
+
+ EXPECT_EQ(DrawQuad::RENDER_PASS,
+ pass_list[2]->quad_list.ElementAt(2)->material);
+ EXPECT_EQ(child_pass2_id, static_cast<RenderPassDrawQuad*>(
+ pass_list[2]->quad_list.ElementAt(2))
+ ->render_pass_id);
+}
+
+TEST_F(DCLayerOverlayTest, ClipRect) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kDirectCompositionUnderlays);
@@ -2493,7 +2582,7 @@ TEST_P(DCLayerOverlayTest, ClipRect) {
}
}
-TEST_P(DCLayerOverlayTest, TransparentOnTop) {
+TEST_F(DCLayerOverlayTest, TransparentOnTop) {
base::test::ScopedFeatureList feature_list;
// Process twice. The second time through the overlay list shouldn't change,
@@ -2525,8 +2614,6 @@ TEST_P(DCLayerOverlayTest, TransparentOnTop) {
}
}
-INSTANTIATE_TEST_CASE_P(, DCLayerOverlayTest, ::testing::Bool());
-
class OverlayInfoRendererGL : public GLRenderer {
public:
OverlayInfoRendererGL(const RendererSettings* settings,
@@ -2585,18 +2672,19 @@ class GLRendererWithOverlaysTest : public testing::Test {
provider_->BindToCurrentThread();
output_surface_ = std::make_unique<OutputSurfaceType>(provider_);
output_surface_->BindToClient(&output_surface_client_);
- resource_provider_ =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(provider_.get(),
- nullptr);
+ resource_provider_ = std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, provider_.get(), nullptr);
provider_->support()->SetScheduleOverlayPlaneCallback(base::Bind(
&MockOverlayScheduler::Schedule, base::Unretained(&scheduler_)));
child_provider_ = TestContextProvider::Create();
child_provider_->BindToCurrentThread();
- child_resource_provider_ =
- cc::FakeResourceProvider::CreateLayerTreeResourceProvider(
- child_provider_.get());
+ child_resource_provider_ = std::make_unique<ClientResourceProvider>(true);
+ }
+
+ ~GLRendererWithOverlaysTest() override {
+ child_resource_provider_->ShutdownAndReleaseAllResources();
}
void Init(bool use_validator) {
@@ -2638,7 +2726,7 @@ class GLRendererWithOverlaysTest : public testing::Test {
std::unique_ptr<OverlayInfoRendererGL> renderer_;
scoped_refptr<TestContextProvider> provider_;
scoped_refptr<TestContextProvider> child_provider_;
- std::unique_ptr<cc::LayerTreeResourceProvider> child_resource_provider_;
+ std::unique_ptr<ClientResourceProvider> child_resource_provider_;
MockOverlayScheduler scheduler_;
};
@@ -2755,7 +2843,7 @@ TEST_F(GLRendererWithOverlaysTest, NoValidatorNoOverlay) {
// GLRenderer skips drawing occluded quads when partial swap is enabled.
TEST_F(GLRendererWithOverlaysTest, OccludedQuadNotDrawnWhenPartialSwapEnabled) {
- provider_->TestContext3d()->set_have_post_sub_buffer(true);
+ provider_->TestContextGL()->set_have_post_sub_buffer(true);
settings_.partial_swap_enabled = true;
bool use_validator = true;
Init(use_validator);
@@ -2786,7 +2874,7 @@ TEST_F(GLRendererWithOverlaysTest, OccludedQuadNotDrawnWhenPartialSwapEnabled) {
// GLRenderer skips drawing occluded quads when empty swap is enabled.
TEST_F(GLRendererWithOverlaysTest, OccludedQuadNotDrawnWhenEmptySwapAllowed) {
- provider_->TestContext3d()->set_have_commit_overlay_planes(true);
+ provider_->TestContextGL()->set_have_commit_overlay_planes(true);
bool use_validator = true;
Init(use_validator);
renderer_->set_expect_overlays(true);
@@ -2829,7 +2917,7 @@ TEST_F(GLRendererWithOverlaysTest, ResourcesExportedAndReturnedWithDelay) {
// Return the resource map.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap(
+ cc::SendResourceAndGetChildToParentMap(
{resource1, resource2, resource3}, resource_provider_.get(),
child_resource_provider_.get(), child_provider_.get());
@@ -3018,7 +3106,7 @@ TEST_F(GLRendererWithOverlaysTest, ResourcesExportedAndReturnedAfterGpuQuery) {
// Return the resource map.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap(
+ cc::SendResourceAndGetChildToParentMap(
{resource1, resource2, resource3}, resource_provider_.get(),
child_resource_provider_.get(), child_provider_.get());
ResourceId mapped_resource1 = resource_map[resource1];
diff --git a/chromium/components/viz/service/display/program_binding.h b/chromium/components/viz/service/display/program_binding.h
index 8b28c57f854..2a8fb9f5484 100644
--- a/chromium/components/viz/service/display/program_binding.h
+++ b/chromium/components/viz/service/display/program_binding.h
@@ -118,6 +118,7 @@ class VIZ_SERVICE_EXPORT ProgramKey {
void set_has_output_color_matrix(bool value) {
has_output_color_matrix_ = value;
}
+ TexCoordPrecision tex_coord_precision() const { return precision_; }
private:
friend struct ProgramKeyHash;
diff --git a/chromium/components/viz/service/display/renderer_pixeltest.cc b/chromium/components/viz/service/display/renderer_pixeltest.cc
index 5a4c00d3e26..a0ca5b16f54 100644
--- a/chromium/components/viz/service/display/renderer_pixeltest.cc
+++ b/chromium/components/viz/service/display/renderer_pixeltest.cc
@@ -12,14 +12,13 @@
#include "cc/base/math_util.h"
#include "cc/paint/paint_flags.h"
#include "cc/paint/skia_paint_canvas.h"
-#include "cc/resources/layer_tree_resource_provider.h"
-#include "cc/resources/video_resource_updater.h"
#include "cc/test/fake_raster_source.h"
#include "cc/test/fake_recording_source.h"
#include "cc/test/pixel_test.h"
#include "cc/test/render_pass_test_utils.h"
#include "cc/test/resource_provider_test_utils.h"
#include "cc/test/test_in_process_context_provider.h"
+#include "components/viz/client/client_resource_provider.h"
#include "components/viz/common/gpu/texture_allocation.h"
#include "components/viz/common/quads/picture_draw_quad.h"
#include "components/viz/common/quads/texture_draw_quad.h"
@@ -29,6 +28,8 @@
#include "components/viz/test/test_shared_bitmap_manager.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "media/base/video_frame.h"
+#include "media/renderers/video_resource_updater.h"
+#include "media/video/half_float_maker.h"
#include "third_party/skia/include/core/SkColorPriv.h"
#include "third_party/skia/include/core/SkColorSpaceXform.h"
#include "third_party/skia/include/core/SkMatrix.h"
@@ -73,7 +74,7 @@ void DeleteTexture(scoped_refptr<ContextProvider> context_provider,
}
ResourceId CreateGpuResource(scoped_refptr<ContextProvider> context_provider,
- cc::LayerTreeResourceProvider* resource_provider,
+ ClientResourceProvider* resource_provider,
const gfx::Size& size,
ResourceFormat format,
gfx::ColorSpace color_space,
@@ -94,7 +95,6 @@ ResourceId CreateGpuResource(scoped_refptr<ContextProvider> context_provider,
color_space);
}
gpu::Mailbox mailbox;
- gl->GenMailboxCHROMIUM(mailbox.name);
gl->ProduceTextureDirectCHROMIUM(allocation.texture_id, mailbox.name);
gpu::SyncToken sync_token;
gl->GenSyncTokenCHROMIUM(sync_token.GetData());
@@ -102,11 +102,6 @@ ResourceId CreateGpuResource(scoped_refptr<ContextProvider> context_provider,
mailbox, GL_LINEAR, allocation.texture_target, sync_token);
gl_resource.size = size;
gl_resource.format = format;
- // We didn't allocate a GpuMemoryBuffer, but we want to set buffer_format for
- // consistency with other callsites.
- // TODO(piman): See if we can remove TransferableResource::buffer_format
- // altogether, it looks redundant with format. crbug.com/836488
- gl_resource.buffer_format = BufferFormat(format);
gl_resource.color_space = std::move(color_space);
auto release_callback = SingleReleaseCallback::Create(base::BindOnce(
&DeleteTexture, std::move(context_provider), allocation.texture_id));
@@ -197,7 +192,7 @@ void CreateTestTwoColoredTextureDrawQuad(
bool premultiplied_alpha,
const SharedQuadState* shared_state,
DisplayResourceProvider* resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
SharedBitmapManager* shared_bitmap_manager,
scoped_refptr<ContextProvider> child_context_provider,
RenderPass* render_pass) {
@@ -242,9 +237,9 @@ void CreateTestTwoColoredTextureDrawQuad(
// Return the mapped resource id.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({resource}, resource_provider,
- child_resource_provider,
- child_context_provider.get());
+ cc::SendResourceAndGetChildToParentMap({resource}, resource_provider,
+ child_resource_provider,
+ child_context_provider.get());
ResourceId mapped_resource = resource_map[resource];
bool needs_blending = true;
@@ -269,7 +264,7 @@ void CreateTestTextureDrawQuad(
bool premultiplied_alpha,
const SharedQuadState* shared_state,
DisplayResourceProvider* resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
SharedBitmapManager* shared_bitmap_manager,
scoped_refptr<ContextProvider> child_context_provider,
RenderPass* render_pass) {
@@ -303,9 +298,9 @@ void CreateTestTextureDrawQuad(
// Return the mapped resource id.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({resource}, resource_provider,
- child_resource_provider,
- child_context_provider.get());
+ cc::SendResourceAndGetChildToParentMap({resource}, resource_provider,
+ child_resource_provider,
+ child_context_provider.get());
ResourceId mapped_resource = resource_map[resource];
bool needs_blending = true;
@@ -328,7 +323,7 @@ void CreateTestTextureDrawQuad(
bool premultiplied_alpha,
const SharedQuadState* shared_state,
DisplayResourceProvider* resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
SharedBitmapManager* shared_bitmap_manager,
scoped_refptr<ContextProvider> child_context_provider,
RenderPass* render_pass) {
@@ -346,11 +341,11 @@ void CreateTestYUVVideoDrawQuad_FromVideoFrame(
uint8_t alpha_value,
const gfx::RectF& tex_coord_rect,
RenderPass* render_pass,
- cc::VideoResourceUpdater* video_resource_updater,
+ media::VideoResourceUpdater* video_resource_updater,
const gfx::Rect& rect,
const gfx::Rect& visible_rect,
DisplayResourceProvider* resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
ContextProvider* child_context_provider) {
const bool with_alpha = (video_frame->format() == media::PIXEL_FORMAT_I420A);
@@ -365,11 +360,11 @@ void CreateTestYUVVideoDrawQuad_FromVideoFrame(
video_frame->rows(media::VideoFrame::kAPlane));
}
- cc::VideoFrameExternalResources resources =
+ media::VideoFrameExternalResources resources =
video_resource_updater->CreateExternalResourcesFromVideoFrame(
video_frame);
- EXPECT_EQ(cc::VideoFrameResourceType::YUV, resources.type);
+ EXPECT_EQ(media::VideoFrameResourceType::YUV, resources.type);
EXPECT_EQ(media::VideoFrame::NumPlanes(video_frame->format()),
resources.resources.size());
EXPECT_EQ(media::VideoFrame::NumPlanes(video_frame->format()),
@@ -403,7 +398,7 @@ void CreateTestYUVVideoDrawQuad_FromVideoFrame(
resource_ids_to_transfer.push_back(resource_a);
// Transfer resources to the parent, and get the resource map.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap(
+ cc::SendResourceAndGetChildToParentMap(
resource_ids_to_transfer, resource_provider, child_resource_provider,
child_context_provider);
@@ -446,18 +441,25 @@ void CreateTestYUVVideoDrawQuad_FromVideoFrame(
ResourceFormat yuv_highbit_resource_format =
video_resource_updater->YuvResourceFormat(bits_per_channel);
- float multiplier = 1.0;
+ float offset = 0.0f;
+ float multiplier = 1.0f;
- if (yuv_highbit_resource_format == R16_EXT)
+ if (yuv_highbit_resource_format == R16_EXT) {
multiplier = 65535.0f / ((1 << bits_per_channel) - 1);
- else
+ } else if (yuv_highbit_resource_format == LUMINANCE_F16) {
+ std::unique_ptr<media::HalfFloatMaker> half_float_maker =
+ media::HalfFloatMaker::NewHalfFloatMaker(bits_per_channel);
+ offset = half_float_maker->Offset();
+ multiplier = half_float_maker->Multiplier();
+ } else {
bits_per_channel = 8;
+ }
yuv_quad->SetNew(shared_state, rect, visible_rect, needs_blending,
ya_tex_coord_rect, uv_tex_coord_rect, ya_tex_size,
uv_tex_size, mapped_resource_y, mapped_resource_u,
mapped_resource_v, mapped_resource_a, video_color_space,
- 0.0f, multiplier, bits_per_channel);
+ offset, multiplier, bits_per_channel);
}
void CreateTestY16TextureDrawQuad_FromVideoFrame(
@@ -465,17 +467,17 @@ void CreateTestY16TextureDrawQuad_FromVideoFrame(
scoped_refptr<media::VideoFrame> video_frame,
const gfx::RectF& tex_coord_rect,
RenderPass* render_pass,
- cc::VideoResourceUpdater* video_resource_updater,
+ media::VideoResourceUpdater* video_resource_updater,
const gfx::Rect& rect,
const gfx::Rect& visible_rect,
DisplayResourceProvider* resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
ContextProvider* child_context_provider) {
- cc::VideoFrameExternalResources resources =
+ media::VideoFrameExternalResources resources =
video_resource_updater->CreateExternalResourcesFromVideoFrame(
video_frame);
- EXPECT_EQ(cc::VideoFrameResourceType::RGBA, resources.type);
+ EXPECT_EQ(media::VideoFrameResourceType::RGBA, resources.type);
EXPECT_EQ(1u, resources.resources.size());
EXPECT_EQ(1u, resources.release_callbacks.size());
@@ -485,9 +487,9 @@ void CreateTestY16TextureDrawQuad_FromVideoFrame(
// Transfer resources to the parent, and get the resource map.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({resource_y}, resource_provider,
- child_resource_provider,
- child_context_provider);
+ cc::SendResourceAndGetChildToParentMap({resource_y}, resource_provider,
+ child_resource_provider,
+ child_context_provider);
ResourceId mapped_resource_y = resource_map[resource_y];
auto* quad = render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
@@ -550,11 +552,11 @@ void CreateTestYUVVideoDrawQuad_Striped(
bool highbit,
const gfx::RectF& tex_coord_rect,
RenderPass* render_pass,
- cc::VideoResourceUpdater* video_resource_updater,
+ media::VideoResourceUpdater* video_resource_updater,
const gfx::Rect& rect,
const gfx::Rect& visible_rect,
DisplayResourceProvider* resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
ContextProvider* child_context_provider) {
scoped_refptr<media::VideoFrame> video_frame = media::VideoFrame::CreateFrame(
format, rect.size(), rect, rect.size(), base::TimeDelta());
@@ -616,9 +618,9 @@ void CreateTestYUVVideoDrawQuad_TwoColor(
uint8_t u_foreground,
uint8_t v_foreground,
RenderPass* render_pass,
- cc::VideoResourceUpdater* video_resource_updater,
+ media::VideoResourceUpdater* video_resource_updater,
DisplayResourceProvider* resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
ContextProvider* child_context_provider) {
const gfx::Rect rect(background_size);
@@ -676,11 +678,11 @@ void CreateTestYUVVideoDrawQuad_Solid(
uint8_t u,
uint8_t v,
RenderPass* render_pass,
- cc::VideoResourceUpdater* video_resource_updater,
+ media::VideoResourceUpdater* video_resource_updater,
const gfx::Rect& rect,
const gfx::Rect& visible_rect,
DisplayResourceProvider* resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
ContextProvider* child_context_provider) {
scoped_refptr<media::VideoFrame> video_frame = media::VideoFrame::CreateFrame(
format, rect.size(), rect, rect.size(), base::TimeDelta());
@@ -715,11 +717,11 @@ void CreateTestYUVVideoDrawQuad_NV12(
uint8_t u,
uint8_t v,
RenderPass* render_pass,
- cc::VideoResourceUpdater* video_resource_updater,
+ media::VideoResourceUpdater* video_resource_updater,
const gfx::Rect& rect,
const gfx::Rect& visible_rect,
DisplayResourceProvider* resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
scoped_refptr<ContextProvider> child_context_provider) {
gfx::ColorSpace gfx_color_space = gfx::ColorSpace::CreateREC601();
if (video_frame_color_space == media::COLOR_SPACE_JPEG) {
@@ -748,7 +750,7 @@ void CreateTestYUVVideoDrawQuad_NV12(
// Transfer resources to the parent, and get the resource map.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap(
+ cc::SendResourceAndGetChildToParentMap(
{resource_y, resource_u, resource_v}, resource_provider,
child_resource_provider, child_context_provider.get());
@@ -779,12 +781,12 @@ void CreateTestY16TextureDrawQuad_TwoColor(
uint8_t g_foreground,
uint8_t g_background,
RenderPass* render_pass,
- cc::VideoResourceUpdater* video_resource_updater,
+ media::VideoResourceUpdater* video_resource_updater,
const gfx::Rect& rect,
const gfx::Rect& visible_rect,
const gfx::Rect& foreground_rect,
DisplayResourceProvider* resource_provider,
- cc::LayerTreeResourceProvider* child_resource_provider,
+ ClientResourceProvider* child_resource_provider,
ContextProvider* child_context_provider) {
std::unique_ptr<unsigned char, base::AlignedFreeDeleter> memory(
static_cast<unsigned char*>(
@@ -835,6 +837,7 @@ using RendererTypes =
::testing::Types<GLRenderer,
SoftwareRenderer,
SkiaRenderer,
+ cc::SkiaRendererDDL,
cc::GLRendererWithExpandedViewport,
cc::SoftwareRendererWithExpandedViewport>;
TYPED_TEST_CASE(RendererPixelTest, RendererTypes);
@@ -888,6 +891,13 @@ bool FuzzyForSoftwareOnlyPixelComparator<SkiaRenderer>::Compare(
}
template <>
+bool FuzzyForSoftwareOnlyPixelComparator<cc::SkiaRendererDDL>::Compare(
+ const SkBitmap& actual_bmp,
+ const SkBitmap& expected_bmp) const {
+ return fuzzy_.Compare(actual_bmp, expected_bmp);
+}
+
+template <>
bool FuzzyForSoftwareOnlyPixelComparator<
cc::SoftwareRendererWithExpandedViewport>::
Compare(const SkBitmap& actual_bmp, const SkBitmap& expected_bmp) const {
@@ -1228,19 +1238,19 @@ class IntersectingQuadGLPixelTest
constexpr bool kUseR16Texture = false;
constexpr int kMaxResourceSize = 10000;
- video_resource_updater_ = std::make_unique<cc::VideoResourceUpdater>(
+ video_resource_updater_ = std::make_unique<media::VideoResourceUpdater>(
this->child_context_provider_.get(), nullptr,
this->child_resource_provider_.get(), kUseStreamVideoDrawQuad,
kUseGpuMemoryBufferResources, kUseR16Texture, kMaxResourceSize);
- video_resource_updater2_ = std::make_unique<cc::VideoResourceUpdater>(
+ video_resource_updater2_ = std::make_unique<media::VideoResourceUpdater>(
this->child_context_provider_.get(), nullptr,
this->child_resource_provider_.get(), kUseStreamVideoDrawQuad,
kUseGpuMemoryBufferResources, kUseR16Texture, kMaxResourceSize);
}
protected:
- std::unique_ptr<cc::VideoResourceUpdater> video_resource_updater_;
- std::unique_ptr<cc::VideoResourceUpdater> video_resource_updater2_;
+ std::unique_ptr<media::VideoResourceUpdater> video_resource_updater_;
+ std::unique_ptr<media::VideoResourceUpdater> video_resource_updater2_;
};
template <typename TypeParam>
@@ -1273,28 +1283,33 @@ TYPED_TEST(IntersectingQuadPixelTest, SolidColorQuads) {
FILE_PATH_LITERAL("intersecting_blue_green.png"));
}
-static inline SkColor GetSkiaOrGLColor(const SkColor& color) {
+static inline uint32_t GetSkiaOrGLColor(const SkColor& color) {
return SkColorSetARGB(SkColorGetA(color), SkColorGetB(color),
SkColorGetG(color), SkColorGetR(color));
}
template <typename TypeParam>
-SkColor GetColor(const SkColor& color) {
+uint32_t GetColor(const SkColor& color) {
return color;
}
template <>
-SkColor GetColor<GLRenderer>(const SkColor& color) {
+uint32_t GetColor<GLRenderer>(const SkColor& color) {
return GetSkiaOrGLColor(color);
}
template <>
-SkColor GetColor<SkiaRenderer>(const SkColor& color) {
+uint32_t GetColor<SkiaRenderer>(const SkColor& color) {
return GetSkiaOrGLColor(color);
}
template <>
-SkColor GetColor<cc::GLRendererWithExpandedViewport>(const SkColor& color) {
+uint32_t GetColor<cc::SkiaRendererDDL>(const SkColor& color) {
+ return GetSkiaOrGLColor(color);
+}
+
+template <>
+uint32_t GetColor<cc::GLRendererWithExpandedViewport>(const SkColor& color) {
return GetSkiaOrGLColor(color);
}
@@ -1352,7 +1367,7 @@ TYPED_TEST(IntersectingQuadSoftwareTest, PictureQuads) {
blue_quad->SetNew(this->front_quad_state_, this->quad_rect_, this->quad_rect_,
needs_blending, gfx::RectF(this->quad_rect_),
this->quad_rect_.size(), false, RGBA_8888, this->quad_rect_,
- 1.f, blue_raster_source->GetDisplayItemList());
+ 1.f, {}, blue_raster_source->GetDisplayItemList());
std::unique_ptr<cc::FakeRecordingSource> green_recording =
cc::FakeRecordingSource::CreateFilledRecordingSource(
@@ -1368,7 +1383,7 @@ TYPED_TEST(IntersectingQuadSoftwareTest, PictureQuads) {
green_quad->SetNew(this->back_quad_state_, this->quad_rect_, this->quad_rect_,
needs_blending, gfx::RectF(this->quad_rect_),
this->quad_rect_.size(), false, RGBA_8888,
- this->quad_rect_, 1.f,
+ this->quad_rect_, 1.f, {},
green_raster_source->GetDisplayItemList());
SCOPED_TRACE("IntersectingPictureQuadsPass");
this->AppendBackgroundAndRunTest(
@@ -1583,13 +1598,18 @@ class VideoGLRendererPixelTest : public cc::GLRendererPixelTest {
constexpr bool kUseGpuMemoryBufferResources = false;
constexpr bool kUseR16Texture = false;
constexpr int kMaxResourceSize = 10000;
- video_resource_updater_ = std::make_unique<cc::VideoResourceUpdater>(
+ video_resource_updater_ = std::make_unique<media::VideoResourceUpdater>(
child_context_provider_.get(), nullptr, child_resource_provider_.get(),
kUseStreamVideoDrawQuad, kUseGpuMemoryBufferResources, kUseR16Texture,
kMaxResourceSize);
}
- std::unique_ptr<cc::VideoResourceUpdater> video_resource_updater_;
+ void TearDown() override {
+ video_resource_updater_ = nullptr;
+ GLRendererPixelTest::TearDown();
+ }
+
+ std::unique_ptr<media::VideoResourceUpdater> video_resource_updater_;
};
class VideoGLRendererPixelHiLoTest : public VideoGLRendererPixelTest,
@@ -2267,7 +2287,7 @@ TYPED_TEST(RendererPixelTest, EnlargedRenderPassTextureWithAntiAliasing) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
base::FilePath(FILE_PATH_LITERAL("blue_yellow_anti_aliasing.png")),
- cc::FuzzyPixelOffByOneComparator(true)));
+ cc::FuzzyPixelComparator(true, 100.f, 0.f, 5.f, 5, 0)));
}
// This tests the case where we have a RenderPass with a mask, but the quad
@@ -2326,10 +2346,10 @@ TYPED_TEST(RendererPixelTest, RenderPassAndMaskWithPartialQuad) {
// Return the mapped resource id.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({mask_resource_id},
- this->resource_provider_.get(),
- this->child_resource_provider_.get(),
- this->child_context_provider_.get());
+ cc::SendResourceAndGetChildToParentMap(
+ {mask_resource_id}, this->resource_provider_.get(),
+ this->child_resource_provider_.get(),
+ this->child_context_provider_.get());
ResourceId mapped_mask_resource_id = resource_map[mask_resource_id];
// This RenderPassDrawQuad does not include the full |viewport_rect|
@@ -2422,10 +2442,10 @@ TYPED_TEST(RendererPixelTest, RenderPassAndMaskWithPartialQuad2) {
// Return the mapped resource id.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({mask_resource_id},
- this->resource_provider_.get(),
- this->child_resource_provider_.get(),
- this->child_context_provider_.get());
+ cc::SendResourceAndGetChildToParentMap(
+ {mask_resource_id}, this->resource_provider_.get(),
+ this->child_resource_provider_.get(),
+ this->child_context_provider_.get());
ResourceId mapped_mask_resource_id = resource_map[mask_resource_id];
// This RenderPassDrawQuad does not include the full |viewport_rect|
@@ -2566,7 +2586,7 @@ class RendererPixelTestWithBackgroundFilter
// The software renderer does not support background filters yet.
using BackgroundFilterRendererTypes =
- ::testing::Types<GLRenderer, SkiaRenderer>;
+ ::testing::Types<GLRenderer, SkiaRenderer, cc::SkiaRendererDDL>;
TYPED_TEST_CASE(RendererPixelTestWithBackgroundFilter,
BackgroundFilterRendererTypes);
@@ -2902,10 +2922,10 @@ TEST_F(GLRendererPixelTest, TileDrawQuadForceAntiAliasingOff) {
// Return the mapped resource id.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({resource},
- this->resource_provider_.get(),
- this->child_resource_provider_.get(),
- this->child_context_provider_.get());
+ cc::SendResourceAndGetChildToParentMap(
+ {resource}, this->resource_provider_.get(),
+ this->child_resource_provider_.get(),
+ this->child_context_provider_.get());
ResourceId mapped_resource = resource_map[resource];
int id = 1;
@@ -3086,7 +3106,7 @@ TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadIdentityScale) {
viewport, // Intentionally bigger than clip.
viewport, needs_blending, gfx::RectF(viewport),
viewport.size(), nearest_neighbor, texture_format, viewport,
- 1.f, blue_raster_source->GetDisplayItemList());
+ 1.f, {}, blue_raster_source->GetDisplayItemList());
// One viewport-filling green quad.
std::unique_ptr<cc::FakeRecordingSource> green_recording =
@@ -3105,7 +3125,7 @@ TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadIdentityScale) {
auto* green_quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
green_quad->SetNew(green_shared_state, viewport, viewport, needs_blending,
gfx::RectF(0.f, 0.f, 1.f, 1.f), viewport.size(),
- nearest_neighbor, texture_format, viewport, 1.f,
+ nearest_neighbor, texture_format, viewport, 1.f, {},
green_raster_source->GetDisplayItemList());
RenderPassList pass_list;
@@ -3147,7 +3167,7 @@ TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadOpacity) {
auto* green_quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
green_quad->SetNew(green_shared_state, viewport, viewport, needs_blending,
gfx::RectF(0, 0, 1, 1), viewport.size(), nearest_neighbor,
- texture_format, viewport, 1.f,
+ texture_format, viewport, 1.f, {},
green_raster_source->GetDisplayItemList());
// One viewport-filling white quad.
@@ -3167,7 +3187,7 @@ TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadOpacity) {
auto* white_quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
white_quad->SetNew(white_shared_state, viewport, viewport, needs_blending,
gfx::RectF(0, 0, 1, 1), viewport.size(), nearest_neighbor,
- texture_format, viewport, 1.f,
+ texture_format, viewport, 1.f, {},
white_raster_source->GetDisplayItemList());
RenderPassList pass_list;
@@ -3242,7 +3262,7 @@ TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadDisableImageFiltering) {
auto* quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
quad->SetNew(shared_state, viewport, viewport, needs_blending,
gfx::RectF(0, 0, 2, 2), viewport.size(), nearest_neighbor,
- texture_format, viewport, 1.f,
+ texture_format, viewport, 1.f, {},
raster_source->GetDisplayItemList());
RenderPassList pass_list;
@@ -3294,7 +3314,7 @@ TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadNearestNeighbor) {
auto* quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
quad->SetNew(shared_state, viewport, viewport, needs_blending,
gfx::RectF(0, 0, 2, 2), viewport.size(), nearest_neighbor,
- texture_format, viewport, 1.f,
+ texture_format, viewport, 1.f, {},
raster_source->GetDisplayItemList());
RenderPassList pass_list;
@@ -3335,10 +3355,10 @@ TYPED_TEST(NonSkiaRendererPixelTest, TileDrawQuadNearestNeighbor) {
}
// Return the mapped resource id.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({resource},
- this->resource_provider_.get(),
- this->child_resource_provider_.get(),
- this->child_context_provider_.get());
+ cc::SendResourceAndGetChildToParentMap(
+ {resource}, this->resource_provider_.get(),
+ this->child_resource_provider_.get(),
+ this->child_context_provider_.get());
ResourceId mapped_resource = resource_map[resource];
int id = 1;
@@ -3386,10 +3406,10 @@ TYPED_TEST(SoftwareRendererPixelTest, TextureDrawQuadNearestNeighbor) {
// Return the mapped resource id.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({resource},
- this->resource_provider_.get(),
- this->child_resource_provider_.get(),
- this->child_context_provider_.get());
+ cc::SendResourceAndGetChildToParentMap(
+ {resource}, this->resource_provider_.get(),
+ this->child_resource_provider_.get(),
+ this->child_context_provider_.get());
ResourceId mapped_resource = resource_map[resource];
int id = 1;
@@ -3439,10 +3459,10 @@ TYPED_TEST(SoftwareRendererPixelTest, TextureDrawQuadLinear) {
// Return the mapped resource id.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({resource},
- this->resource_provider_.get(),
- this->child_resource_provider_.get(),
- this->child_context_provider_.get());
+ cc::SendResourceAndGetChildToParentMap(
+ {resource}, this->resource_provider_.get(),
+ this->child_resource_provider_.get(),
+ this->child_context_provider_.get());
ResourceId mapped_resource = resource_map[resource];
int id = 1;
@@ -3514,14 +3534,14 @@ TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadNonIdentityScale) {
top_right_green_shared_quad_state, green_rect1, green_rect1,
needs_blending, gfx::RectF(gfx::SizeF(green_rect1.size())),
green_rect1.size(), nearest_neighbor, texture_format, green_rect1, 1.f,
- green_raster_source->GetDisplayItemList());
+ {}, green_raster_source->GetDisplayItemList());
auto* green_quad2 = pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
green_quad2->SetNew(
top_right_green_shared_quad_state, green_rect2, green_rect2,
needs_blending, gfx::RectF(gfx::SizeF(green_rect2.size())),
green_rect2.size(), nearest_neighbor, texture_format, green_rect2, 1.f,
- green_raster_source->GetDisplayItemList());
+ {}, green_raster_source->GetDisplayItemList());
// Add a green clipped checkerboard in the bottom right to help test
// interleaving picture quad content and solid color content.
@@ -3585,7 +3605,7 @@ TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadNonIdentityScale) {
blue_quad->SetNew(blue_shared_state, quad_content_rect, quad_content_rect,
needs_blending, gfx::RectF(quad_content_rect),
content_union_rect.size(), nearest_neighbor, texture_format,
- content_union_rect, contents_scale,
+ content_union_rect, contents_scale, {},
raster_source->GetDisplayItemList());
// Fill left half of viewport with green.
@@ -3787,10 +3807,10 @@ TEST_F(GLRendererPixelTest, TextureQuadBatching) {
// Return the mapped resource id.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({resource},
- this->resource_provider_.get(),
- this->child_resource_provider_.get(),
- this->child_context_provider_.get());
+ cc::SendResourceAndGetChildToParentMap(
+ {resource}, this->resource_provider_.get(),
+ this->child_resource_provider_.get(),
+ this->child_context_provider_.get());
ResourceId mapped_resource = resource_map[resource];
// Arbitrary dividing lengths to divide up the resource into 16 quads.
@@ -3867,10 +3887,10 @@ TEST_F(GLRendererPixelTest, TileQuadClamping) {
}
// Return the mapped resource id.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({resource},
- this->resource_provider_.get(),
- this->child_resource_provider_.get(),
- this->child_context_provider_.get());
+ cc::SendResourceAndGetChildToParentMap(
+ {resource}, this->resource_provider_.get(),
+ this->child_resource_provider_.get(),
+ this->child_context_provider_.get());
ResourceId mapped_resource = resource_map[resource];
int id = 1;
@@ -4057,10 +4077,10 @@ TEST_P(ColorTransformPixelTest, Basic) {
// Return the mapped resource id.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({resource},
- this->resource_provider_.get(),
- this->child_resource_provider_.get(),
- this->child_context_provider_.get());
+ cc::SendResourceAndGetChildToParentMap(
+ {resource}, this->resource_provider_.get(),
+ this->child_resource_provider_.get(),
+ this->child_context_provider_.get());
ResourceId mapped_resource = resource_map[resource];
bool needs_blending = true;
diff --git a/chromium/components/viz/common/resources/resource_fence.h b/chromium/components/viz/service/display/resource_fence.h
index 369d84f7f58..ebfc6587424 100644
--- a/chromium/components/viz/common/resources/resource_fence.h
+++ b/chromium/components/viz/service/display/resource_fence.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_VIZ_COMMON_RESOURCES_RESOURCE_FENCE_H_
-#define COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_FENCE_H_
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_RESOURCE_FENCE_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_RESOURCE_FENCE_H_
+
+#include "base/memory/ref_counted.h"
namespace viz {
@@ -26,4 +28,4 @@ class ResourceFence : public base::RefCounted<ResourceFence> {
} // namespace viz
-#endif // COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_FENCE_H_
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_RESOURCE_FENCE_H_
diff --git a/chromium/components/viz/common/resources/resource_metadata.cc b/chromium/components/viz/service/display/resource_metadata.cc
index 4b9649b443f..c746e42d39c 100644
--- a/chromium/components/viz/common/resources/resource_metadata.cc
+++ b/chromium/components/viz/service/display/resource_metadata.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/viz/common/resources/resource_metadata.h"
+#include "components/viz/service/display/resource_metadata.h"
namespace viz {
diff --git a/chromium/components/viz/common/resources/resource_metadata.h b/chromium/components/viz/service/display/resource_metadata.h
index 86d91f15903..8da82ee429b 100644
--- a/chromium/components/viz/common/resources/resource_metadata.h
+++ b/chromium/components/viz/service/display/resource_metadata.h
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_METADATA_H_
-#define COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_METADATA_H_
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_RESOURCE_METADATA_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_RESOURCE_METADATA_H_
-#include "components/viz/common/viz_common_export.h"
+#include "components/viz/service/viz_service_export.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "third_party/skia/include/core/SkColorSpace.h"
@@ -15,9 +15,9 @@
namespace viz {
-// Metadata for a Resoource. It is used by SkiaRenderer to get resource metedata
-// from DisplayResourceProvider, and make a promise SkImage from it.
-struct VIZ_COMMON_EXPORT ResourceMetadata {
+// Metadata for a resource named by a ResourceId in DisplayResourceProvider.
+// Used to construct a promise SkImage for a ResourceId.
+struct VIZ_SERVICE_EXPORT ResourceMetadata {
ResourceMetadata();
ResourceMetadata(ResourceMetadata&& other);
~ResourceMetadata();
@@ -54,4 +54,4 @@ struct VIZ_COMMON_EXPORT ResourceMetadata {
} // namespace viz
-#endif // COMPONENTS_VIZ_COMMON_RESOURCES_RESOURCE_METADATA_H_
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_RESOURCE_METADATA_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
index 3d9a0abfca2..7f69f85dec7 100644
--- a/chromium/components/viz/service/display/scoped_render_pass_texture.cc
+++ b/chromium/components/viz/service/display/scoped_render_pass_texture.cc
@@ -4,6 +4,8 @@
#include "components/viz/service/display/scoped_render_pass_texture.h"
+#include <algorithm>
+
#include "base/bits.h"
#include "base/logging.h"
#include "components/viz/common/gpu/context_provider.h"
@@ -50,6 +52,7 @@ ScopedRenderPassTexture::ScopedRenderPassTexture(
gl->TexStorage2DEXT(GL_TEXTURE_2D, levels, TextureStorageFormat(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);
diff --git a/chromium/components/viz/common/resources/shared_bitmap_manager.h b/chromium/components/viz/service/display/shared_bitmap_manager.h
index 0aefd3533dc..f3fe341805e 100644
--- a/chromium/components/viz/common/resources/shared_bitmap_manager.h
+++ b/chromium/components/viz/service/display/shared_bitmap_manager.h
@@ -2,15 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef COMPONENTS_VIZ_COMMON_RESOURCES_SHARED_BITMAP_MANAGER_H_
-#define COMPONENTS_VIZ_COMMON_RESOURCES_SHARED_BITMAP_MANAGER_H_
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_SHARED_BITMAP_MANAGER_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_SHARED_BITMAP_MANAGER_H_
#include <memory>
#include "base/macros.h"
-#include "components/viz/common/quads/shared_bitmap.h"
+#include "components/viz/common/resources/shared_bitmap.h"
#include "mojo/public/cpp/system/buffer.h"
-#include "ui/gfx/geometry/size.h"
+
+namespace gfx {
+class Size;
+}
namespace viz {
@@ -21,9 +24,11 @@ class SharedBitmapManager {
// Used in the display compositor to find the bitmap associated with an id.
virtual std::unique_ptr<SharedBitmap> GetSharedBitmapFromId(
- const gfx::Size&,
- ResourceFormat,
- const SharedBitmapId&) = 0;
+ const gfx::Size& size,
+ ResourceFormat format,
+ const SharedBitmapId& id) = 0;
+ virtual base::UnguessableToken GetSharedBitmapTracingGUIDFromId(
+ const SharedBitmapId& id) = 0;
// Used in the display compositor to associate an id to a shm handle.
virtual bool ChildAllocatedSharedBitmap(mojo::ScopedSharedBufferHandle buffer,
const SharedBitmapId& id) = 0;
@@ -37,4 +42,4 @@ class SharedBitmapManager {
} // namespace viz
-#endif // COMPONENTS_VIZ_COMMON_RESOURCES_SHARED_BITMAP_MANAGER_H_
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_SHARED_BITMAP_MANAGER_H_
diff --git a/chromium/components/viz/service/display/skia_output_surface.cc b/chromium/components/viz/service/display/skia_output_surface.cc
index abc0a6cdc1c..1ee904dbb8c 100644
--- a/chromium/components/viz/service/display/skia_output_surface.cc
+++ b/chromium/components/viz/service/display/skia_output_surface.cc
@@ -6,9 +6,7 @@
namespace viz {
-SkiaOutputSurface::SkiaOutputSurface(
- scoped_refptr<ContextProvider> context_provider)
- : OutputSurface(std::move(context_provider)) {}
+SkiaOutputSurface::SkiaOutputSurface() = default;
SkiaOutputSurface::~SkiaOutputSurface() = default;
diff --git a/chromium/components/viz/service/display/skia_output_surface.h b/chromium/components/viz/service/display/skia_output_surface.h
index a93bfc26d66..bdc29aa3109 100644
--- a/chromium/components/viz/service/display/skia_output_surface.h
+++ b/chromium/components/viz/service/display/skia_output_surface.h
@@ -14,6 +14,7 @@ class SkImage;
namespace viz {
+class CopyOutputRequest;
struct ResourceMetadata;
// This class extends the OutputSurface for SkiaRenderer needs. In future, the
@@ -22,13 +23,25 @@ struct ResourceMetadata;
// OutputSurface's methods which are not useful for SkiaRenderer.
class VIZ_SERVICE_EXPORT SkiaOutputSurface : public OutputSurface {
public:
- explicit SkiaOutputSurface(scoped_refptr<ContextProvider> context_provider);
+ SkiaOutputSurface();
~SkiaOutputSurface() override;
- // Get a SkCanvas for the current frame. The SkiaRenderer will use this
- // SkCanvas to draw quads. This class retains the ownership of the SkCanvas,
- // And this SkCanvas may become invalid, when the frame is swapped out.
- virtual SkCanvas* GetSkCanvasForCurrentFrame() = 0;
+ // Begin painting the current frame. This method will create a
+ // SkDeferredDisplayListRecorder and return a SkCanvas of it.
+ // The SkiaRenderer will use this SkCanvas to paint the current
+ // frame.
+ // And this SkCanvas may become invalid, when FinishPaintCurrentFrame is
+ // called.
+ virtual SkCanvas* BeginPaintCurrentFrame() = 0;
+
+ // Finish painting the current frame. It should be paired with
+ // BeginPaintCurrentFrame. This method will schedule a GPU task to play the
+ // DDL back on GPU thread on the SkSurface for the framebuffer. This method
+ // returns a sync token which can be waited on in a command buffer to ensure
+ // the paint operation is completed. This token is released when the GPU ops
+ // from painting the current frame have been seen and processed by the GPU
+ // main.
+ virtual gpu::SyncToken FinishPaintCurrentFrame() = 0;
// Make a promise SkImage from the given |metadata|. The SkiaRenderer can use
// the image with SkCanvas returned by |GetSkCanvasForCurrentFrame|, but Skia
@@ -49,13 +62,8 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurface : public OutputSurface {
std::vector<ResourceMetadata> metadatas,
SkYUVColorSpace yuv_color_space) = 0;
- // Swaps the current backbuffer to the screen and return a sync token which
- // can be waited on in a command buffer to ensure the frame is completed. This
- // token is released when the GPU ops from drawing the frame have been seen
- // and processed by the GPU main.
- // TODO(penghuang): replace OutputSurface::SwapBuffers with this method when
- // SkiaRenderer and DDL are used everywhere.
- virtual gpu::SyncToken SkiaSwapBuffers(OutputSurfaceFrame frame) = 0;
+ // Swaps the current backbuffer to the screen.
+ virtual void SkiaSwapBuffers(OutputSurfaceFrame frame) = 0;
// Begin painting a render pass. This method will create a
// SkDeferredDisplayListRecorder and return a SkCanvas of it. The SkiaRenderer
@@ -89,6 +97,12 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurface : public OutputSurface {
// FinishPaintRenderPass.
virtual void RemoveRenderPassResource(std::vector<RenderPassId> ids) = 0;
+ // Copy the output of the current frame if the |id| is zero, otherwise copy
+ // the output of a cached SkSurface for the given |id|.
+ virtual void CopyOutput(RenderPassId id,
+ const gfx::Rect& copy_rect,
+ std::unique_ptr<CopyOutputRequest> request) = 0;
+
private:
DISALLOW_COPY_AND_ASSIGN(SkiaOutputSurface);
};
diff --git a/chromium/components/viz/service/display/skia_renderer.cc b/chromium/components/viz/service/display/skia_renderer.cc
index 783fb7f778e..0544d031b60 100644
--- a/chromium/components/viz/service/display/skia_renderer.cc
+++ b/chromium/components/viz/service/display/skia_renderer.cc
@@ -6,6 +6,7 @@
#include "base/bits.h"
#include "base/command_line.h"
+#include "base/optional.h"
#include "base/trace_event/trace_event.h"
#include "cc/base/math_util.h"
#include "cc/paint/render_surface_filters.h"
@@ -19,15 +20,16 @@
#include "components/viz/common/quads/tile_draw_quad.h"
#include "components/viz/common/quads/yuv_video_draw_quad.h"
#include "components/viz/common/resources/platform_color.h"
-#include "components/viz/common/resources/resource_fence.h"
#include "components/viz/common/resources/resource_format_utils.h"
-#include "components/viz/common/resources/resource_metadata.h"
#include "components/viz/common/skia_helper.h"
#include "components/viz/service/display/output_surface.h"
#include "components/viz/service/display/output_surface_frame.h"
#include "components/viz/service/display/renderer_utils.h"
+#include "components/viz/service/display/resource_fence.h"
+#include "components/viz/service/display/resource_metadata.h"
#include "components/viz/service/display/skia_output_surface.h"
#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/vulkan/buildflags.h"
#include "skia/ext/opacity_filter_canvas.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
@@ -63,8 +65,7 @@ namespace {
bool IsTextureResource(DisplayResourceProvider* resource_provider,
ResourceId resource_id) {
- return resource_provider->GetResourceType(resource_id) ==
- ResourceType::kTexture;
+ return !resource_provider->IsResourceSoftwareBacked(resource_id);
}
} // namespace
@@ -78,7 +79,7 @@ class SkiaRenderer::ScopedSkImageBuilder {
const SkImage* sk_image() const { return sk_image_; }
private:
- std::unique_ptr<DisplayResourceProvider::ScopedReadLockSkImage> lock_;
+ base::Optional<DisplayResourceProvider::ScopedReadLockSkImage> lock_;
const SkImage* sk_image_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(ScopedSkImageBuilder);
@@ -87,13 +88,13 @@ class SkiaRenderer::ScopedSkImageBuilder {
SkiaRenderer::ScopedSkImageBuilder::ScopedSkImageBuilder(
SkiaRenderer* skia_renderer,
ResourceId resource_id) {
+ if (!resource_id)
+ return;
auto* resource_provider = skia_renderer->resource_provider_;
- if (!skia_renderer->skia_output_surface_ ||
- skia_renderer->non_root_surface_ ||
+ if (!skia_renderer->is_using_ddl() || skia_renderer->non_root_surface_ ||
!IsTextureResource(resource_provider, resource_id)) {
// TODO(penghuang): remove this code when DDL is used everywhere.
- lock_ = std::make_unique<DisplayResourceProvider::ScopedReadLockSkImage>(
- resource_provider, resource_id);
+ lock_.emplace(resource_provider, resource_id);
sk_image_ = lock_->sk_image();
} else {
// Look up the image from promise_images_by resource_id and return the
@@ -117,7 +118,7 @@ class SkiaRenderer::ScopedYUVSkImageBuilder {
public:
ScopedYUVSkImageBuilder(SkiaRenderer* skia_renderer,
const YUVVideoDrawQuad* quad) {
- DCHECK(skia_renderer->skia_output_surface_);
+ DCHECK(skia_renderer->is_using_ddl());
DCHECK(IsTextureResource(skia_renderer->resource_provider_,
quad->y_plane_resource_id()));
DCHECK(IsTextureResource(skia_renderer->resource_provider_,
@@ -182,26 +183,33 @@ SkiaRenderer::SkiaRenderer(const RendererSettings* settings,
: DirectRenderer(settings, output_surface, resource_provider),
skia_output_surface_(skia_output_surface),
lock_set_for_external_use_(resource_provider) {
- const auto& context_caps =
- output_surface_->context_provider()->ContextCapabilities();
- use_swap_with_bounds_ = context_caps.swap_buffers_with_bounds;
- if (context_caps.sync_query) {
- sync_queries_ = base::Optional<SyncQueryCollection>(
- output_surface_->context_provider()->ContextGL());
+ if (auto* context_provider = output_surface_->context_provider()) {
+ const auto& context_caps = context_provider->ContextCapabilities();
+ use_swap_with_bounds_ = context_caps.swap_buffers_with_bounds;
+ if (context_caps.sync_query) {
+ sync_queries_ =
+ base::Optional<SyncQueryCollection>(context_provider->ContextGL());
+ }
}
}
SkiaRenderer::~SkiaRenderer() = default;
bool SkiaRenderer::CanPartialSwap() {
+ if (IsUsingVulkan())
+ return false;
if (use_swap_with_bounds_)
return false;
auto* context_provider = output_surface_->context_provider();
- return context_provider->ContextCapabilities().post_sub_buffer;
+ return context_provider
+ ? context_provider->ContextCapabilities().post_sub_buffer
+ : false;
}
void SkiaRenderer::BeginDrawingFrame() {
TRACE_EVENT0("viz", "SkiaRenderer::BeginDrawingFrame");
+ if (IsUsingVulkan() || is_using_ddl())
+ return;
// Copied from GLRenderer.
scoped_refptr<ResourceFence> read_lock_fence;
if (sync_queries_) {
@@ -213,15 +221,13 @@ void SkiaRenderer::BeginDrawingFrame() {
}
resource_provider_->SetReadLockFence(read_lock_fence.get());
- if (!skia_output_surface_) {
- // 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);
- }
+ // 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);
}
}
}
@@ -256,7 +262,7 @@ void SkiaRenderer::FinishDrawingFrame() {
void SkiaRenderer::SwapBuffers(std::vector<ui::LatencyInfo> latency_info,
bool need_presentation_feedback) {
DCHECK(visible_);
- TRACE_EVENT0("cc,benchmark", "SkiaRenderer::SwapBuffers");
+ TRACE_EVENT0("viz,benchmark", "SkiaRenderer::SwapBuffers");
OutputSurfaceFrame output_frame;
output_frame.latency_info = std::move(latency_info);
output_frame.size = surface_size_for_swap_buffers();
@@ -270,12 +276,8 @@ void SkiaRenderer::SwapBuffers(std::vector<ui::LatencyInfo> latency_info,
output_frame.sub_buffer_rect = swap_buffer_rect_;
}
- if (skia_output_surface_) {
- auto sync_token =
- skia_output_surface_->SkiaSwapBuffers(std::move(output_frame));
- promise_images_.clear();
- yuv_promise_images_.clear();
- lock_set_for_external_use_.UnlockResources(sync_token);
+ if (is_using_ddl()) {
+ skia_output_surface_->SkiaSwapBuffers(std::move(output_frame));
} else {
// TODO(penghuang): remove it when SkiaRenderer and SkDDL are always used.
output_surface_->SwapBuffers(std::move(output_frame));
@@ -308,27 +310,54 @@ void SkiaRenderer::BindFramebufferToOutputSurface() {
// TODO(weiliangc): Set up correct can_use_lcd_text for SkSurfaceProps flags.
// How to setup is in ResourceProvider. (http://crbug.com/644851)
-
- GrContext* gr_context = output_surface_->context_provider()->GrContext();
- if (skia_output_surface_) {
- root_canvas_ = skia_output_surface_->GetSkCanvasForCurrentFrame();
+ if (is_using_ddl()) {
+ root_canvas_ = skia_output_surface_->BeginPaintCurrentFrame();
+ is_drawing_render_pass_ = false;
DCHECK(root_canvas_);
- } else if (!root_canvas_ || root_canvas_->getGrContext() != gr_context ||
- gfx::SkISizeToSize(root_canvas_->getBaseLayerSize()) !=
- current_frame()->device_viewport_size) {
- // Either no SkSurface setup yet, or new GrContext, need to create new
- // surface.
- GrGLFramebufferInfo framebuffer_info;
- framebuffer_info.fFBOID = 0;
- framebuffer_info.fFormat = GL_RGB8_OES;
- GrBackendRenderTarget render_target(
- current_frame()->device_viewport_size.width(),
- current_frame()->device_viewport_size.height(), 0, 8, framebuffer_info);
-
- root_surface_ = SkSurface::MakeFromBackendRenderTarget(
- gr_context, render_target, kBottomLeft_GrSurfaceOrigin,
- kRGB_888x_SkColorType, nullptr, &surface_props);
- root_canvas_ = root_surface_->getCanvas();
+ } else {
+ auto* gr_context = GetGrContext();
+ if (IsUsingVulkan()) {
+#if BUILDFLAG(ENABLE_VULKAN)
+ auto* vulkan_surface = output_surface_->GetVulkanSurface();
+ auto* swap_chain = vulkan_surface->GetSwapChain();
+ VkImage image = swap_chain->GetCurrentImage(swap_chain->current_image());
+ GrVkImageInfo vk_image_info;
+ vk_image_info.fImage = image;
+ vk_image_info.fAlloc = {VK_NULL_HANDLE, 0, 0, 0};
+ vk_image_info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ vk_image_info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
+ vk_image_info.fFormat = VK_FORMAT_B8G8R8A8_UNORM;
+ vk_image_info.fLevelCount = 1;
+ GrBackendRenderTarget render_target(
+ current_frame()->device_viewport_size.width(),
+ current_frame()->device_viewport_size.height(), 0, 0, vk_image_info);
+ root_surface_ = SkSurface::MakeFromBackendRenderTarget(
+ gr_context, render_target, kTopLeft_GrSurfaceOrigin,
+ kBGRA_8888_SkColorType, nullptr, &surface_props);
+ DCHECK(root_surface_);
+ root_canvas_ = root_surface_->getCanvas();
+#else
+ NOTREACHED();
+#endif
+ } else if (!root_canvas_ || root_canvas_->getGrContext() != gr_context ||
+ gfx::SkISizeToSize(root_canvas_->getBaseLayerSize()) !=
+ current_frame()->device_viewport_size) {
+ // Either no SkSurface setup yet, or new GrContext, need to create new
+ // surface.
+ GrGLFramebufferInfo framebuffer_info;
+ framebuffer_info.fFBOID = 0;
+ framebuffer_info.fFormat = GL_RGB8_OES;
+ GrBackendRenderTarget render_target(
+ current_frame()->device_viewport_size.width(),
+ current_frame()->device_viewport_size.height(), 0, 8,
+ framebuffer_info);
+
+ root_surface_ = SkSurface::MakeFromBackendRenderTarget(
+ gr_context, render_target, kBottomLeft_GrSurfaceOrigin,
+ kRGB_888x_SkColorType, nullptr, &surface_props);
+ DCHECK(root_surface_);
+ root_canvas_ = root_surface_->getCanvas();
+ }
}
if (settings_->show_overdraw_feedback) {
@@ -354,7 +383,7 @@ void SkiaRenderer::BindFramebufferToTexture(const RenderPassId render_pass_id) {
// This function is called after AllocateRenderPassResourceIfNeeded, so there
// should be backing ready.
RenderPassBacking& backing = iter->second;
- if (skia_output_surface_) {
+ if (is_using_ddl()) {
non_root_surface_ = nullptr;
current_canvas_ = skia_output_surface_->BeginPaintRenderPass(
render_pass_id, backing.size, backing.format, backing.mipmap);
@@ -371,22 +400,6 @@ void SkiaRenderer::SetScissorTestRect(const gfx::Rect& scissor_rect) {
scissor_rect_ = scissor_rect;
}
-void SkiaRenderer::SetClipRect(const gfx::Rect& rect) {
- if (!current_canvas_)
- return;
- // Skia applies the current matrix to clip rects so we reset it temporary.
- SkMatrix current_matrix = current_canvas_->getTotalMatrix();
- current_canvas_->resetMatrix();
- // SetClipRect is assumed to be applied temporarily, on an
- // otherwise-unclipped canvas.
- DCHECK_EQ(current_canvas_->getDeviceClipBounds().width(),
- current_canvas_->imageInfo().width());
- DCHECK_EQ(current_canvas_->getDeviceClipBounds().height(),
- current_canvas_->imageInfo().height());
- current_canvas_->clipRect(gfx::RectToSkRect(rect));
- current_canvas_->setMatrix(current_matrix);
-}
-
void SkiaRenderer::ClearCanvas(SkColor color) {
if (!current_canvas_)
return;
@@ -436,9 +449,9 @@ void SkiaRenderer::DoDrawQuad(const DrawQuad* quad,
const gfx::QuadF* draw_region) {
if (!current_canvas_)
return;
- if (draw_region) {
- current_canvas_->save();
- }
+ base::Optional<SkAutoCanvasRestore> auto_canvas_restore;
+ if (draw_region)
+ auto_canvas_restore.emplace(current_canvas_, true /* do_save */);
TRACE_EVENT0("viz", "SkiaRenderer::DoDrawQuad");
gfx::Transform quad_rect_matrix;
@@ -518,7 +531,7 @@ void SkiaRenderer::DoDrawQuad(const DrawQuad* quad,
NOTREACHED();
break;
case DrawQuad::YUV_VIDEO_CONTENT:
- if (skia_output_surface_) {
+ if (is_using_ddl()) {
DrawYUVVideoQuad(YUVVideoDrawQuad::MaterialCast(quad));
} else {
DrawUnsupportedQuad(quad);
@@ -533,9 +546,6 @@ void SkiaRenderer::DoDrawQuad(const DrawQuad* quad,
}
current_canvas_->resetMatrix();
- if (draw_region) {
- current_canvas_->restore();
- }
}
void SkiaRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad) {
@@ -594,12 +604,11 @@ void SkiaRenderer::DrawPictureQuad(const PictureDrawQuad* quad) {
// Treat all subnormal values as zero for performance.
cc::ScopedSubnormalFloatDisabler disabler;
- raster_canvas->save();
+ SkAutoCanvasRestore auto_canvas_restore(raster_canvas, true /* do_save */);
raster_canvas->translate(-quad->content_rect.x(), -quad->content_rect.y());
raster_canvas->clipRect(gfx::RectToSkRect(quad->content_rect));
raster_canvas->scale(quad->contents_scale, quad->contents_scale);
quad->display_item_list->Raster(raster_canvas);
- raster_canvas->restore();
}
void SkiaRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad) {
@@ -633,7 +642,9 @@ void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad) {
bool blend_background =
quad->background_color != SK_ColorTRANSPARENT && !image->isOpaque();
bool needs_layer = blend_background && (current_paint_.getAlpha() != 0xFF);
+ base::Optional<SkAutoCanvasRestore> auto_canvas_restore;
if (needs_layer) {
+ auto_canvas_restore.emplace(current_canvas_, false /* do_save */);
current_canvas_->saveLayerAlpha(&quad_rect, current_paint_.getAlpha());
current_paint_.setAlpha(0xFF);
}
@@ -645,8 +656,6 @@ void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad) {
current_paint_.setFilterQuality(
quad->nearest_neighbor ? kNone_SkFilterQuality : kLow_SkFilterQuality);
current_canvas_->drawImageRect(image, sk_uv_rect, quad_rect, &current_paint_);
- if (needs_layer)
- current_canvas_->restore();
}
void SkiaRenderer::DrawTileQuad(const TileDrawQuad* quad) {
@@ -733,9 +742,9 @@ bool SkiaRenderer::CalculateRPDQParams(sk_sp<SkImage> content,
dst_rect.Intersect(local_clip.BoundingBox());
// If we've been fully clipped out (by crop rect or clipping), there's
// nothing to draw.
- if (dst_rect.IsEmpty()) {
+ if (dst_rect.IsEmpty())
return false;
- }
+
SkIPoint offset;
SkIRect subset;
gfx::RectF src_rect(quad->rect);
@@ -766,27 +775,27 @@ void SkiaRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad) {
// TODO(weiliangc): GL Renderer has optimization that when Render Pass has a
// single quad inside we would draw that directly. We could add similar
// optimization here by using the quad's SkImage.
- sk_sp<SkImage> content =
- skia_output_surface_
- ? skia_output_surface_->MakePromiseSkImageFromRenderPass(
- quad->render_pass_id, backing.size, backing.format,
- backing.mipmap)
- : backing.render_pass_surface->makeImageSnapshot();
+ sk_sp<SkImage> content_image =
+ is_using_ddl() ? skia_output_surface_->MakePromiseSkImageFromRenderPass(
+ quad->render_pass_id, backing.size, backing.format,
+ backing.mipmap)
+ : backing.render_pass_surface->makeImageSnapshot();
DrawRenderPassDrawQuadParams params;
params.filters = FiltersForPass(quad->render_pass_id);
- bool can_draw = CalculateRPDQParams(content, quad, &params);
+ bool can_draw = CalculateRPDQParams(content_image, quad, &params);
if (!can_draw)
return;
+ const auto dest_rect = gfx::RectFToSkRect(QuadVertexRect());
SkRect content_rect;
SkRect dest_visible_rect;
if (params.filter_image) {
content_rect = RectFToSkRect(params.tex_coord_rect);
dest_visible_rect = gfx::RectFToSkRect(cc::MathUtil::ScaleRectProportional(
QuadVertexRect(), gfx::RectF(quad->rect), gfx::RectF(params.dst_rect)));
- content = params.filter_image;
+ content_image = params.filter_image;
} else {
content_rect = RectFToSkRect(quad->tex_coord_rect);
dest_visible_rect = gfx::RectFToSkRect(cc::MathUtil::ScaleRectProportional(
@@ -794,48 +803,83 @@ void SkiaRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad) {
gfx::RectF(quad->visible_rect)));
}
- SkRect dest_rect = gfx::RectFToSkRect(QuadVertexRect());
-
- SkMatrix content_mat;
- content_mat.setRectToRect(content_rect, dest_rect,
- SkMatrix::kFill_ScaleToFit);
-
- sk_sp<SkShader> shader;
- shader = content->makeShader(&content_mat);
-
- if (quad->mask_resource_id()) {
- ScopedSkImageBuilder builder(this, quad->mask_resource_id());
- const SkImage* image = builder.sk_image();
- if (!image)
- return;
-
+ // Prepare mask.
+ ScopedSkImageBuilder mask_image_builder(this, quad->mask_resource_id());
+ const SkImage* mask_image = mask_image_builder.sk_image();
+ DCHECK_EQ(!!quad->mask_resource_id(), !!mask_image);
+ SkMatrix mask_to_dest_matrix;
+ sk_sp<SkMaskFilter> mask_filter;
+ if (mask_image) {
// Scale normalized uv rect into absolute texel coordinates.
SkRect mask_rect = gfx::RectFToSkRect(
gfx::ScaleRect(quad->mask_uv_rect, quad->mask_texture_size.width(),
quad->mask_texture_size.height()));
-
- SkMatrix mask_mat;
- mask_mat.setRectToRect(mask_rect, dest_rect, SkMatrix::kFill_ScaleToFit);
- current_paint_.setMaskFilter(
- SkShaderMaskFilter::Make((image->makeShader(&mask_mat))));
- current_paint_.setShader(std::move(shader));
- current_canvas_->drawRect(dest_visible_rect, current_paint_);
- return;
+ mask_to_dest_matrix.setRectToRect(mask_rect, dest_rect,
+ SkMatrix::kFill_ScaleToFit);
+ mask_filter =
+ SkShaderMaskFilter::Make(mask_image->makeShader(&mask_to_dest_matrix));
+ DCHECK(mask_filter);
}
- // If we have a background filter shader, render its results first.
- sk_sp<SkShader> background_filter_shader =
- GetBackgroundFilterShader(quad, SkShader::kClamp_TileMode);
- if (background_filter_shader) {
- SkPaint paint;
- paint.setShader(std::move(background_filter_shader));
- paint.setMaskFilter(current_paint_.refMaskFilter());
- current_canvas_->drawRect(dest_visible_rect, paint);
+ const cc::FilterOperations* background_filters =
+ BackgroundFiltersForPass(quad->render_pass_id);
+ // Without backdrop effect.
+ if (!ShouldApplyBackgroundFilters(quad, background_filters)) {
+ if (!mask_filter) {
+ // Not mask, so we just draw the context_image directly.
+ current_canvas_->drawImageRect(content_image, content_rect,
+ dest_visible_rect, &current_paint_);
+ return;
+ }
+
+ // With mask, we need convert the content_image to a shader, and use
+ // drawRect() with the shader and the mask.
+ current_paint_.setMaskFilter(mask_filter);
+ // Convert the content_image to a shader, and use drawRect() with the
+ // shader.
+ SkMatrix content_to_dest_matrix;
+ content_to_dest_matrix.setRectToRect(content_rect, dest_rect,
+ SkMatrix::kFill_ScaleToFit);
+ auto shader = content_image->makeShader(&content_to_dest_matrix);
current_paint_.setShader(std::move(shader));
current_canvas_->drawRect(dest_visible_rect, current_paint_);
return;
}
- current_canvas_->drawImageRect(content, content_rect, dest_visible_rect,
+
+ // Draw render pass with backdrop effects.
+ auto background_paint_filter = cc::RenderSurfaceFilters::BuildImageFilter(
+ *background_filters,
+ gfx::SizeF(content_image->width(), content_image->height()));
+ auto background_image_filter =
+ background_paint_filter ? background_paint_filter->cached_sk_filter_
+ : nullptr;
+ DCHECK(background_image_filter);
+ SkMatrix content_to_dest_matrix;
+ content_to_dest_matrix.setRectToRect(content_rect, dest_rect,
+ SkMatrix::kFill_ScaleToFit);
+ 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());
+ local_matrix.postConcat(content_to_dest_matrix);
+ background_image_filter =
+ background_image_filter->makeWithLocalMatrix(local_matrix);
+
+ SkAutoCanvasRestore auto_canvas_restore(current_canvas_, true /* do_save */);
+ current_canvas_->clipRect(dest_rect);
+
+ SkPaint paint;
+ paint.setMaskFilter(mask_filter);
+ SkCanvas::SaveLayerRec rec(&dest_visible_rect, &paint,
+ background_image_filter.get(),
+ SkCanvas::kInitWithPrevious_SaveLayerFlag);
+ // Lift content in the current_canvas_ into a new layer with
+ // background_image_filter, and then paint content_image in the layer,
+ // and then the current_canvas_->restore() will drop the layer into the
+ // canvas.
+ SkAutoCanvasRestore auto_canvas_restore_for_save_layer(current_canvas_,
+ false /* do_save */);
+ current_canvas_->saveLayer(rec);
+ current_canvas_->drawImageRect(content_image, content_rect, dest_visible_rect,
&current_paint_);
}
@@ -857,12 +901,6 @@ void SkiaRenderer::CopyDrawnRenderPass(
// TODO(weiliangc): Make copy request work. (crbug.com/644851)
TRACE_EVENT0("viz", "SkiaRenderer::CopyDrawnRenderPass");
- if (skia_output_surface_) {
- // TODO(penghuang): Support it with SkDDL.
- NOTIMPLEMENTED();
- return;
- }
-
gfx::Rect copy_rect = current_frame()->current_render_pass->output_rect;
if (request->has_area())
copy_rect.Intersect(request->area());
@@ -872,20 +910,32 @@ void SkiaRenderer::CopyDrawnRenderPass(
gfx::Rect window_copy_rect = MoveFromDrawToWindowSpace(copy_rect);
- sk_sp<SkImage> copy_image = current_surface_->makeImageSnapshot()->makeSubset(
- RectToSkIRect(window_copy_rect));
-
- if (request->result_format() == CopyOutputResult::Format::RGBA_BITMAP) {
- // Send copy request by copying into a bitmap.
- SkBitmap bitmap;
- copy_image->asLegacyBitmap(&bitmap);
+ if (request->result_format() != CopyOutputResult::Format::RGBA_BITMAP ||
+ request->is_scaled() ||
+ (request->has_result_selection() &&
+ request->result_selection() == gfx::Rect(copy_rect.size()))) {
+ // TODO(crbug.com/644851): Complete the implementation for all request
+ // types, scaling, etc.
+ NOTIMPLEMENTED();
+ return;
+ }
- request->SendResult(
- std::make_unique<CopyOutputSkBitmapResult>(copy_rect, bitmap));
+ if (is_using_ddl()) {
+ auto render_pass_id =
+ is_drawing_render_pass_ ? current_frame()->current_render_pass->id : 0;
+ skia_output_surface_->CopyOutput(render_pass_id, window_copy_rect,
+ std::move(request));
return;
}
- NOTREACHED();
+ sk_sp<SkImage> copy_image = current_surface_->makeImageSnapshot()->makeSubset(
+ RectToSkIRect(window_copy_rect));
+
+ // Send copy request by copying into a bitmap.
+ SkBitmap bitmap;
+ copy_image->asLegacyBitmap(&bitmap);
+ request->SendResult(
+ std::make_unique<CopyOutputSkBitmapResult>(copy_rect, bitmap));
}
void SkiaRenderer::SetEnableDCLayers(bool enable) {
@@ -901,14 +951,14 @@ void SkiaRenderer::DidChangeVisibility() {
}
void SkiaRenderer::FinishDrawingQuadList() {
- if (skia_output_surface_) {
- if (is_drawing_render_pass_) {
- gpu::SyncToken sync_token = skia_output_surface_->FinishPaintRenderPass();
- promise_images_.clear();
- yuv_promise_images_.clear();
- lock_set_for_external_use_.UnlockResources(sync_token);
- is_drawing_render_pass_ = false;
- }
+ if (is_using_ddl()) {
+ gpu::SyncToken sync_token =
+ is_drawing_render_pass_
+ ? skia_output_surface_->FinishPaintRenderPass()
+ : skia_output_surface_->FinishPaintCurrentFrame();
+ promise_images_.clear();
+ yuv_promise_images_.clear();
+ lock_set_for_external_use_.UnlockResources(sync_token);
} else {
current_canvas_->flush();
}
@@ -932,124 +982,21 @@ bool SkiaRenderer::ShouldApplyBackgroundFilters(
return true;
}
-sk_sp<SkImage> SkiaRenderer::GetBackdropImage(
- const gfx::Rect& bounding_rect) const {
- return root_surface_->makeImageSnapshot()->makeSubset(
- gfx::RectToSkIRect(bounding_rect));
-}
-
-gfx::Rect SkiaRenderer::GetBackdropBoundingBoxForRenderPassQuad(
- const RenderPassDrawQuad* quad,
- const gfx::Transform& contents_device_transform,
- const cc::FilterOperations* background_filters,
- gfx::Rect* unclipped_rect) const {
- DCHECK(ShouldApplyBackgroundFilters(quad, background_filters));
- gfx::Rect backdrop_rect = gfx::ToEnclosingRect(cc::MathUtil::MapClippedRect(
- contents_device_transform, QuadVertexRect()));
-
- SkMatrix matrix;
- matrix.setScale(quad->filters_scale.x(), quad->filters_scale.y());
- backdrop_rect = background_filters->MapRectReverse(backdrop_rect, matrix);
-
- *unclipped_rect = backdrop_rect;
- backdrop_rect.Intersect(MoveFromDrawToWindowSpace(
- current_frame()->current_render_pass->output_rect));
-
- return backdrop_rect;
-}
-
-// If non-null, auto_bounds will be filled with the automatically-computed
-// destination bounds. If null, the output will be the same size as the
-// input image.
-sk_sp<SkImage> SkiaRenderer::ApplyBackgroundFilters(
- SkImageFilter* filter,
- const RenderPassDrawQuad* quad,
- sk_sp<SkImage> src_image,
- const gfx::Rect& rect) const {
- if (!filter)
- return nullptr;
-
- 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());
-
- SkImageInfo image_info =
- SkImageInfo::Make(rect.width(), rect.height(), src_image->colorType(),
- src_image->alphaType(), nullptr);
-
- GrContext* gr_context = output_surface_->context_provider()->GrContext();
- // TODO(weiliangc): Set up correct can_use_lcd_text for SkSurfaceProps flags.
- // How to setup is in ResourceProvider. (http://crbug.com/644851)
- // LegacyFontHost will get LCD text and skia figures out what type to use.
- SkSurfaceProps surface_props(0, SkSurfaceProps::kLegacyFontHost_InitType);
- sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(
- gr_context, SkBudgeted::kNo, image_info, 0, kBottomLeft_GrSurfaceOrigin,
- &surface_props, false);
-
- if (!surface) {
- return nullptr;
- }
-
- SkPaint paint;
- // Treat subnormal float values as zero for performance.
- cc::ScopedSubnormalFloatDisabler disabler;
- paint.setImageFilter(filter->makeWithLocalMatrix(local_matrix));
- surface->getCanvas()->translate(-rect.x(), -rect.y());
- surface->getCanvas()->drawImage(src_image, rect.x(), rect.y(), &paint);
-
- return surface->makeImageSnapshot();
+bool SkiaRenderer::IsUsingVulkan() const {
+#if BUILDFLAG(ENABLE_VULKAN)
+ if (output_surface_->vulkan_context_provider())
+ return output_surface_->vulkan_context_provider()->GetGrContext();
+#endif
+ return false;
}
-sk_sp<SkShader> SkiaRenderer::GetBackgroundFilterShader(
- const RenderPassDrawQuad* quad,
- SkShader::TileMode content_tile_mode) const {
- const cc::FilterOperations* background_filters =
- BackgroundFiltersForPass(quad->render_pass_id);
- if (!ShouldApplyBackgroundFilters(quad, background_filters))
- return nullptr;
-
- gfx::Transform quad_rect_matrix;
- QuadRectTransform(&quad_rect_matrix,
- quad->shared_quad_state->quad_to_target_transform,
- gfx::RectF(quad->rect));
- gfx::Transform contents_device_transform =
- current_frame()->window_matrix * current_frame()->projection_matrix *
- quad_rect_matrix;
- contents_device_transform.FlattenTo2d();
-
- gfx::Rect unclipped_rect;
- gfx::Rect backdrop_rect = GetBackdropBoundingBoxForRenderPassQuad(
- quad, contents_device_transform, background_filters, &unclipped_rect);
-
- // Figure out the transformations to move it back to pixel space.
- gfx::Transform contents_device_transform_inverse;
- if (!contents_device_transform.GetInverse(&contents_device_transform_inverse))
- return nullptr;
-
- SkMatrix filter_backdrop_transform =
- contents_device_transform_inverse.matrix();
- filter_backdrop_transform.preTranslate(backdrop_rect.x(), backdrop_rect.y());
-
- // Apply the filter to the backdrop.
- sk_sp<SkImage> backdrop_image = GetBackdropImage(backdrop_rect);
-
- gfx::Vector2dF clipping_offset =
- (unclipped_rect.top_right() - backdrop_rect.top_right()) +
- (backdrop_rect.bottom_left() - unclipped_rect.bottom_left());
- sk_sp<SkImageFilter> filter =
- cc::RenderSurfaceFilters::BuildImageFilter(
- *background_filters,
- gfx::SizeF(backdrop_image->width(), backdrop_image->height()),
- clipping_offset)
- ->cached_sk_filter_;
- sk_sp<SkImage> filter_backdrop_image =
- ApplyBackgroundFilters(filter.get(), quad, backdrop_image, backdrop_rect);
-
- if (!filter_backdrop_image)
- return nullptr;
-
- return filter_backdrop_image->makeShader(content_tile_mode, content_tile_mode,
- &filter_backdrop_transform);
+GrContext* SkiaRenderer::GetGrContext() {
+ DCHECK(!is_using_ddl());
+#if BUILDFLAG(ENABLE_VULKAN)
+ if (output_surface_->vulkan_context_provider())
+ return output_surface_->vulkan_context_provider()->GetGrContext();
+#endif
+ return output_surface_->context_provider()->GrContext();
}
void SkiaRenderer::UpdateRenderPassTextures(
@@ -1080,7 +1027,7 @@ void SkiaRenderer::UpdateRenderPassTextures(
render_pass_backings_.erase(it);
}
- if (skia_output_surface_ && !passes_to_delete.empty()) {
+ if (is_using_ddl() && !passes_to_delete.empty()) {
skia_output_surface_->RemoveRenderPassResource(std::move(passes_to_delete));
}
}
@@ -1096,11 +1043,18 @@ void SkiaRenderer::AllocateRenderPassResourceIfNeeded(
gpu::Capabilities caps;
caps.texture_format_bgra8888 = true;
GrContext* gr_context = nullptr;
- if (!skia_output_surface_) {
- ContextProvider* context_provider = output_surface_->context_provider();
- caps.texture_format_bgra8888 =
- context_provider->ContextCapabilities().texture_format_bgra8888;
- gr_context = context_provider->GrContext();
+ if (!is_using_ddl()) {
+ if (IsUsingVulkan()) {
+ // TODO(penghuang): check supported format correctly.
+ caps.texture_format_bgra8888 = true;
+ } else {
+ ContextProvider* context_provider = output_surface_->context_provider();
+ if (context_provider) {
+ caps.texture_format_bgra8888 =
+ context_provider->ContextCapabilities().texture_format_bgra8888;
+ }
+ }
+ gr_context = GetGrContext();
}
render_pass_backings_.insert(std::pair<RenderPassId, RenderPassBacking>(
render_pass_id,
diff --git a/chromium/components/viz/service/display/skia_renderer.h b/chromium/components/viz/service/display/skia_renderer.h
index 63fdef52ea9..4f38c9987e7 100644
--- a/chromium/components/viz/service/display/skia_renderer.h
+++ b/chromium/components/viz/service/display/skia_renderer.h
@@ -35,7 +35,7 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer {
SkiaRenderer(const RendererSettings* settings,
OutputSurface* output_surface,
DisplayResourceProvider* resource_provider,
- SkiaOutputSurface* skia_output_surface = nullptr);
+ SkiaOutputSurface* skia_output_surface);
~SkiaRenderer() override;
void SwapBuffers(std::vector<ui::LatencyInfo> latency_info,
@@ -82,7 +82,6 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer {
void ClearCanvas(SkColor color);
void ClearFramebuffer();
- void SetClipRect(const gfx::Rect& rect);
void DrawDebugBorderQuad(const DebugBorderDrawQuad* quad);
void DrawPictureQuad(const PictureDrawQuad* quad);
@@ -95,24 +94,12 @@ class VIZ_SERVICE_EXPORT SkiaRenderer : public DirectRenderer {
bool CalculateRPDQParams(sk_sp<SkImage> src_image,
const RenderPassDrawQuad* quad,
DrawRenderPassDrawQuadParams* params);
-
bool ShouldApplyBackgroundFilters(
const RenderPassDrawQuad* quad,
const cc::FilterOperations* background_filters) const;
- gfx::Rect GetBackdropBoundingBoxForRenderPassQuad(
- const RenderPassDrawQuad* quad,
- const gfx::Transform& contents_device_transform,
- const cc::FilterOperations* background_filters,
- gfx::Rect* unclipped_rect) const;
- sk_sp<SkImage> ApplyBackgroundFilters(SkImageFilter* filter,
- const RenderPassDrawQuad* quad,
- sk_sp<SkImage> src_image,
- const gfx::Rect& rect) const;
-
- sk_sp<SkImage> GetBackdropImage(const gfx::Rect& bounding_rect) const;
- sk_sp<SkShader> GetBackgroundFilterShader(
- const RenderPassDrawQuad* quad,
- SkShader::TileMode content_tile_mode) const;
+ bool IsUsingVulkan() const;
+ GrContext* GetGrContext();
+ bool is_using_ddl() const { return !!skia_output_surface_; }
// A map from RenderPass id to the texture used to draw the RenderPass from.
struct RenderPassBacking {
diff --git a/chromium/components/viz/service/display/software_renderer.cc b/chromium/components/viz/service/display/software_renderer.cc
index bc958f621cc..b7232345052 100644
--- a/chromium/components/viz/service/display/software_renderer.cc
+++ b/chromium/components/viz/service/display/software_renderer.cc
@@ -6,6 +6,7 @@
#include "base/trace_event/trace_event.h"
#include "cc/base/math_util.h"
+#include "cc/paint/image_provider.h"
#include "cc/paint/render_surface_filters.h"
#include "components/viz/common/display/renderer_settings.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
@@ -37,6 +38,32 @@
#include "ui/gfx/transform.h"
namespace viz {
+namespace {
+class AnimatedImagesProvider : public cc::ImageProvider {
+ public:
+ AnimatedImagesProvider(
+ const PictureDrawQuad::ImageAnimationMap* image_animation_map)
+ : image_animation_map_(image_animation_map) {}
+ ~AnimatedImagesProvider() override = default;
+
+ ScopedDecodedDrawImage GetDecodedDrawImage(
+ const cc::DrawImage& draw_image) override {
+ const auto& paint_image = draw_image.paint_image();
+ auto it = image_animation_map_->find(paint_image.stable_id());
+ size_t frame_index = it == image_animation_map_->end()
+ ? paint_image.frame_index()
+ : it->second;
+ return ScopedDecodedDrawImage(cc::DecodedDrawImage(
+ paint_image.GetSkImageForFrame(frame_index), SkSize::Make(0, 0),
+ SkSize::Make(1.f, 1.f), draw_image.filter_quality(),
+ true /* is_budgeted */));
+ }
+
+ private:
+ const PictureDrawQuad::ImageAnimationMap* image_animation_map_;
+};
+
+} // namespace
SoftwareRenderer::SoftwareRenderer(const RendererSettings* settings,
OutputSurface* output_surface,
@@ -169,8 +196,7 @@ void SoftwareRenderer::PrepareSurfaceForPass(
}
bool SoftwareRenderer::IsSoftwareResource(ResourceId resource_id) const {
- return resource_provider_->GetResourceType(resource_id) ==
- ResourceType::kBitmap;
+ return resource_provider_->IsResourceSoftwareBacked(resource_id);
}
void SoftwareRenderer::DoDrawQuad(const DrawQuad* quad,
@@ -327,11 +353,14 @@ void SoftwareRenderer::DrawPictureQuad(const PictureDrawQuad* quad) {
// Treat all subnormal values as zero for performance.
cc::ScopedSubnormalFloatDisabler disabler;
+ // Use an image provider to select the correct frame for animated images.
+ AnimatedImagesProvider image_provider(&quad->image_animation_map);
+
raster_canvas->save();
raster_canvas->translate(-quad->content_rect.x(), -quad->content_rect.y());
raster_canvas->clipRect(gfx::RectToSkRect(quad->content_rect));
raster_canvas->scale(quad->contents_scale, quad->contents_scale);
- quad->display_item_list->Raster(raster_canvas);
+ quad->display_item_list->Raster(raster_canvas, &image_provider);
raster_canvas->restore();
}
@@ -469,17 +498,12 @@ void SoftwareRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad) {
SkShader::kClamp_TileMode, &content_mat);
}
- std::unique_ptr<DisplayResourceProvider::ScopedReadLockSoftware> mask_lock;
if (quad->mask_resource_id()) {
- mask_lock =
- std::make_unique<DisplayResourceProvider::ScopedReadLockSoftware>(
- resource_provider_, quad->mask_resource_id());
-
- if (!mask_lock->valid())
+ DisplayResourceProvider::ScopedReadLockSkImage mask_lock(
+ resource_provider_, quad->mask_resource_id());
+ if (!mask_lock.valid())
return;
- const SkBitmap* mask = mask_lock->sk_bitmap();
-
// Scale normalized uv rect into absolute texel coordinates.
SkRect mask_rect = gfx::RectFToSkRect(
gfx::ScaleRect(quad->mask_uv_rect, quad->mask_texture_size.width(),
@@ -488,9 +512,9 @@ void SoftwareRenderer::DrawRenderPassQuad(const RenderPassDrawQuad* quad) {
SkMatrix mask_mat;
mask_mat.setRectToRect(mask_rect, dest_rect, SkMatrix::kFill_ScaleToFit);
- current_paint_.setMaskFilter(SkShaderMaskFilter::Make(
- SkShader::MakeBitmapShader(*mask, SkShader::kClamp_TileMode,
- SkShader::kClamp_TileMode, &mask_mat)));
+ current_paint_.setMaskFilter(
+ SkShaderMaskFilter::Make(mask_lock.sk_image()->makeShader(
+ SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &mask_mat)));
}
// If we have a background filter shader, render its results first.
diff --git a/chromium/components/viz/service/display/software_renderer_unittest.cc b/chromium/components/viz/service/display/software_renderer_unittest.cc
index 86210a12099..5ed0040bf96 100644
--- a/chromium/components/viz/service/display/software_renderer_unittest.cc
+++ b/chromium/components/viz/service/display/software_renderer_unittest.cc
@@ -12,20 +12,21 @@
#include "base/run_loop.h"
#include "cc/test/animation_test_common.h"
#include "cc/test/fake_output_surface_client.h"
-#include "cc/test/fake_resource_provider.h"
#include "cc/test/geometry_test_utils.h"
#include "cc/test/pixel_test_utils.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/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "components/viz/common/quads/compositor_frame_metadata.h"
#include "components/viz/common/quads/render_pass.h"
#include "components/viz/common/quads/render_pass_draw_quad.h"
-#include "components/viz/common/quads/shared_bitmap.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"
+#include "components/viz/common/resources/shared_bitmap.h"
+#include "components/viz/service/display/display_resource_provider.h"
#include "components/viz/service/display/software_output_device.h"
#include "components/viz/test/fake_output_surface.h"
#include "components/viz/test/test_shared_bitmap_manager.h"
@@ -47,23 +48,28 @@ class SoftwareRendererTest : public testing::Test {
output_surface_->BindToClient(&output_surface_client_);
shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
- resource_provider_ =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- nullptr, shared_bitmap_manager_.get());
+ resource_provider_ = std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kSoftware, nullptr,
+ shared_bitmap_manager_.get());
renderer_ = std::make_unique<SoftwareRenderer>(
&settings_, output_surface_.get(), resource_provider());
renderer_->Initialize();
renderer_->SetVisible(true);
- child_resource_provider_ =
- cc::FakeResourceProvider::CreateLayerTreeResourceProvider(nullptr);
+ child_resource_provider_ = std::make_unique<ClientResourceProvider>(true);
+ }
+
+ void TearDown() override {
+ if (child_resource_provider_)
+ child_resource_provider_->ShutdownAndReleaseAllResources();
+ child_resource_provider_ = nullptr;
}
DisplayResourceProvider* resource_provider() const {
return resource_provider_.get();
}
- cc::LayerTreeResourceProvider* child_resource_provider() const {
+ ClientResourceProvider* child_resource_provider() const {
return child_resource_provider_.get();
}
@@ -121,7 +127,7 @@ class SoftwareRendererTest : public testing::Test {
std::unique_ptr<FakeOutputSurface> output_surface_;
std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
std::unique_ptr<DisplayResourceProvider> resource_provider_;
- std::unique_ptr<cc::LayerTreeResourceProvider> child_resource_provider_;
+ std::unique_ptr<ClientResourceProvider> child_resource_provider_;
std::unique_ptr<SoftwareRenderer> renderer_;
};
@@ -194,9 +200,9 @@ TEST_F(SoftwareRendererTest, TileQuad) {
// Transfer resources to the parent, and get the resource map.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({resource_yellow, resource_cyan},
- resource_provider(),
- child_resource_provider(), nullptr);
+ cc::SendResourceAndGetChildToParentMap(
+ {resource_yellow, resource_cyan}, resource_provider(),
+ child_resource_provider(), nullptr);
ResourceId mapped_resource_yellow = resource_map[resource_yellow];
ResourceId mapped_resource_cyan = resource_map[resource_cyan];
@@ -257,8 +263,9 @@ TEST_F(SoftwareRendererTest, TileQuadVisibleRect) {
// Transfer resources to the parent, and get the resource map.
std::unordered_map<ResourceId, ResourceId> resource_map =
- SendResourceAndGetChildToParentMap({resource_cyan}, resource_provider(),
- child_resource_provider(), nullptr);
+ cc::SendResourceAndGetChildToParentMap(
+ {resource_cyan}, resource_provider(), child_resource_provider(),
+ nullptr);
ResourceId mapped_resource_cyan = resource_map[resource_cyan];
gfx::Rect root_rect(tile_size);
diff --git a/chromium/components/viz/service/display/surface_aggregator.cc b/chromium/components/viz/service/display/surface_aggregator.cc
index c021630d66f..11aa65e1af8 100644
--- a/chromium/components/viz/service/display/surface_aggregator.cc
+++ b/chromium/components/viz/service/display/surface_aggregator.cc
@@ -301,6 +301,11 @@ void SurfaceAggregator::EmitSurfaceContent(
++uma_stats_.valid_surface;
const CompositorFrame& frame = surface->GetActiveFrame();
+ TRACE_EVENT_WITH_FLOW1(
+ "viz,benchmark", "Graphics.Pipeline",
+ TRACE_ID_GLOBAL(frame.metadata.begin_frame_ack.trace_id),
+ TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
+ "SurfaceAggregation");
if (ignore_undamaged) {
gfx::Transform quad_to_target_transform(
@@ -539,22 +544,14 @@ void SurfaceAggregator::ReportMissingFallbackSurface(
const SurfaceId& fallback_surface_id,
const Surface* fallback_surface) {
// If the fallback surface is unavailable then that's an error.
- std::stringstream error_stream;
- error_stream << fallback_surface_id;
- std::string frame_sink_debug_label(
- manager_->GetFrameSinkDebugLabel(fallback_surface_id.frame_sink_id()));
- // Add the debug label, if available, to the error log to help diagnose a
- // misbehaving client.
- if (!frame_sink_debug_label.empty())
- error_stream << " [" << frame_sink_debug_label << "]";
if (!fallback_surface) {
- error_stream << " is missing during aggregation";
+ DLOG(ERROR) << fallback_surface_id << " is missing during aggregation";
++uma_stats_.missing_surface;
} else {
- error_stream << " has no active frame during aggregation";
+ DLOG(ERROR) << fallback_surface_id
+ << " has no active frame during aggregation";
++uma_stats_.no_active_frame;
}
- DLOG(ERROR) << error_stream.str();
}
void SurfaceAggregator::AddColorConversionPass() {
@@ -829,12 +826,6 @@ void SurfaceAggregator::ProcessAddedAndRemovedSurfaces() {
provider_->DestroyChild(it->second);
surface_id_to_resource_child_id_.erase(it);
}
-
- // Notify client of removed surface.
- Surface* surface_ptr = manager_->GetSurfaceForId(surface.first);
- if (surface_ptr) {
- surface_ptr->RunDrawCallback();
- }
}
}
}
@@ -851,9 +842,11 @@ gfx::Rect SurfaceAggregator::PrewalkTree(Surface* surface,
return gfx::Rect();
contained_surfaces_[surface->surface_id()] = surface->GetActiveFrameIndex();
- contained_frame_sinks_[surface->surface_id().frame_sink_id()] =
- std::max(surface->surface_id().local_surface_id(),
- contained_frame_sinks_[surface->surface_id().frame_sink_id()]);
+ LocalSurfaceId& local_surface_id =
+ contained_frame_sinks_[surface->surface_id().frame_sink_id()];
+ local_surface_id =
+ std::max(surface->surface_id().local_surface_id(), local_surface_id);
+
if (!surface->HasActiveFrame())
return gfx::Rect();
@@ -993,6 +986,25 @@ gfx::Rect SurfaceAggregator::PrewalkTree(Surface* surface,
// referenced_surfaces_.
referenced_surfaces_.insert(surface->surface_id());
for (const auto& surface_info : child_surfaces) {
+ if (will_draw) {
+ // We only pick a surface between primary and fallback if both SurfaceIds
+ // are provided and they have the same FrameSinkId and embed token,
+ // otherwise the only Surface other than fallback that can be shown is the
+ // primary.
+ if (!surface_info.fallback_id ||
+ surface_info.fallback_id->frame_sink_id() !=
+ surface_info.primary_id.frame_sink_id() ||
+ surface_info.fallback_id->local_surface_id().embed_token() !=
+ surface_info.primary_id.local_surface_id().embed_token()) {
+ damage_ranges_[surface_info.primary_id.frame_sink_id()] =
+ std::make_pair(surface_info.primary_id.local_surface_id(),
+ surface_info.primary_id.local_surface_id());
+ } else if (surface_info.fallback_id != surface_info.primary_id) {
+ damage_ranges_[surface_info.primary_id.frame_sink_id()] =
+ std::make_pair(surface_info.fallback_id->local_surface_id(),
+ surface_info.primary_id.local_surface_id());
+ }
+ }
Surface* child_surface = manager_->GetSurfaceForId(surface_info.primary_id);
gfx::Rect surface_damage;
if (!child_surface || !child_surface->HasActiveFrame()) {
@@ -1061,7 +1073,7 @@ gfx::Rect SurfaceAggregator::PrewalkTree(Surface* surface,
if (will_draw)
surface->OnWillBeDrawn();
- for (const auto& surface_id : frame.metadata.referenced_surfaces) {
+ for (const SurfaceId& surface_id : surface->active_referenced_surfaces()) {
if (!contained_surfaces_.count(surface_id)) {
result->undrawn_surfaces.insert(surface_id);
Surface* undrawn_surface = manager_->GetSurfaceForId(surface_id);
@@ -1106,12 +1118,11 @@ void SurfaceAggregator::CopyUndrawnSurfaces(PrewalkResult* prewalk_result) {
continue;
if (!surface->HasActiveFrame())
continue;
- const CompositorFrame& frame = surface->GetActiveFrame();
if (!surface->HasCopyOutputRequests()) {
// Children are not necessarily included in undrawn_surfaces (because
// they weren't referenced directly from a drawn surface), but may have
// copy requests, so make sure to check them as well.
- for (const auto& child_id : frame.metadata.referenced_surfaces) {
+ for (const SurfaceId& child_id : surface->active_referenced_surfaces()) {
// Don't iterate over the child Surface if it was already listed as a
// child of a different Surface, or in the case where there's infinite
// recursion.
@@ -1122,7 +1133,7 @@ void SurfaceAggregator::CopyUndrawnSurfaces(PrewalkResult* prewalk_result) {
}
} else {
referenced_surfaces_.insert(surface_id);
- CopyPasses(frame, surface);
+ CopyPasses(surface->GetActiveFrame(), surface);
// CopyPasses may have mutated container, need to re-query to erase.
referenced_surfaces_.erase(referenced_surfaces_.find(surface_id));
}
@@ -1156,15 +1167,21 @@ CompositorFrame SurfaceAggregator::Aggregate(
Surface* surface = manager_->GetSurfaceForId(surface_id);
DCHECK(surface);
contained_surfaces_[surface_id] = surface->GetActiveFrameIndex();
- contained_frame_sinks_[surface_id.frame_sink_id()] =
- std::max(surface_id.local_surface_id(),
- contained_frame_sinks_[surface_id.frame_sink_id()]);
+
+ LocalSurfaceId& local_surface_id =
+ contained_frame_sinks_[surface_id.frame_sink_id()];
+ local_surface_id =
+ std::max(surface->surface_id().local_surface_id(), local_surface_id);
if (!surface->HasActiveFrame())
return {};
const CompositorFrame& root_surface_frame = surface->GetActiveFrame();
- TRACE_EVENT0("viz", "SurfaceAggregator::Aggregate");
+ TRACE_EVENT_WITH_FLOW1(
+ "viz,benchmark", "Graphics.Pipeline",
+ TRACE_ID_GLOBAL(root_surface_frame.metadata.begin_frame_ack.trace_id),
+ TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
+ "SurfaceAggregation");
CompositorFrame frame;
@@ -1173,6 +1190,7 @@ CompositorFrame SurfaceAggregator::Aggregate(
valid_surfaces_.clear();
has_cached_render_passes_ = false;
+ damage_ranges_.clear();
PrewalkResult prewalk_result;
root_damage_rect_ =
PrewalkTree(surface, false, 0, true /* will_draw */, &prewalk_result);
@@ -1270,4 +1288,27 @@ void SurfaceAggregator::SetOutputColorSpace(
: gfx::ColorSpace::CreateSRGB();
}
+bool SurfaceAggregator::NotifySurfaceDamageAndCheckForDisplayDamage(
+ const SurfaceId& surface_id) {
+ if (previous_contained_surfaces_.count(surface_id)) {
+ Surface* surface = manager_->GetSurfaceForId(surface_id);
+ if (surface) {
+ DCHECK(surface->HasActiveFrame());
+ if (surface->GetActiveFrame().resource_list.empty())
+ ReleaseResources(surface_id);
+ }
+ return true;
+ }
+
+ auto it = damage_ranges_.find(surface_id.frame_sink_id());
+ if (it == damage_ranges_.end())
+ return false;
+
+ const LocalSurfaceId& fallback = it->second.first;
+ const LocalSurfaceId& primary = it->second.second;
+ return (primary == surface_id.local_surface_id()) ||
+ (primary.IsNewerThan(surface_id.local_surface_id()) &&
+ surface_id.local_surface_id().IsNewerThan(fallback));
+}
+
} // namespace viz
diff --git a/chromium/components/viz/service/display/surface_aggregator.h b/chromium/components/viz/service/display/surface_aggregator.h
index 14b4bfe907d..877fee42588 100644
--- a/chromium/components/viz/service/display/surface_aggregator.h
+++ b/chromium/components/viz/service/display/surface_aggregator.h
@@ -54,6 +54,8 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator {
void SetOutputColorSpace(const gfx::ColorSpace& blending_color_space,
const gfx::ColorSpace& output_color_space);
+ bool NotifySurfaceDamageAndCheckForDisplayDamage(const SurfaceId& surface_id);
+
private:
struct ClipData {
ClipData() : is_clipped(false) {}
@@ -278,6 +280,11 @@ class VIZ_SERVICE_EXPORT SurfaceAggregator {
// Tracks UMA stats for SurfaceDrawQuads during a call to Aggregate().
SurfaceDrawQuadUmaStats uma_stats_;
+ // For each FrameSinkId, contains a range of LocalSurfaceIds that will damage
+ // the display if they're damaged.
+ base::flat_map<FrameSinkId, std::pair<LocalSurfaceId, LocalSurfaceId>>
+ damage_ranges_;
+
base::WeakPtrFactory<SurfaceAggregator> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(SurfaceAggregator);
diff --git a/chromium/components/viz/service/display/surface_aggregator_perftest.cc b/chromium/components/viz/service/display/surface_aggregator_perftest.cc
index ca2a7834ed1..4b815d1e060 100644
--- a/chromium/components/viz/service/display/surface_aggregator_perftest.cc
+++ b/chromium/components/viz/service/display/surface_aggregator_perftest.cc
@@ -4,19 +4,18 @@
#include "cc/base/lap_timer.h"
#include "cc/test/fake_output_surface_client.h"
-#include "cc/test/fake_resource_provider.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/quads/surface_draw_quad.h"
#include "components/viz/common/quads/texture_draw_quad.h"
#include "components/viz/service/display/display_resource_provider.h"
#include "components/viz/service/display/surface_aggregator.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.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/surfaces/surface_manager.h"
#include "components/viz/test/compositor_frame_helpers.h"
#include "components/viz/test/test_context_provider.h"
-#include "components/viz/test/test_shared_bitmap_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_test.h"
@@ -31,14 +30,13 @@ const base::UnguessableToken kArbitraryToken = base::UnguessableToken::Create();
class SurfaceAggregatorPerfTest : public testing::Test {
public:
- SurfaceAggregatorPerfTest() {
+ SurfaceAggregatorPerfTest() : manager_(&shared_bitmap_manager_) {
context_provider_ = TestContextProvider::Create();
context_provider_->BindToCurrentThread();
- shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
- resource_provider_ =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- context_provider_.get(), shared_bitmap_manager_.get());
+ resource_provider_ = std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kGpu, context_provider_.get(),
+ &shared_bitmap_manager_);
}
void RunTest(int num_surfaces,
@@ -147,9 +145,9 @@ class SurfaceAggregatorPerfTest : public testing::Test {
}
protected:
+ ServerSharedBitmapManager shared_bitmap_manager_;
FrameSinkManagerImpl manager_;
scoped_refptr<TestContextProvider> context_provider_;
- std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
std::unique_ptr<DisplayResourceProvider> resource_provider_;
std::unique_ptr<SurfaceAggregator> aggregator_;
cc::LapTimer timer_;
diff --git a/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc b/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc
index 549191558ea..493ba4987a0 100644
--- a/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc
+++ b/chromium/components/viz/service/display/surface_aggregator_pixeltest.cc
@@ -13,6 +13,7 @@
#include "components/viz/common/quads/surface_draw_quad.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/service/display/surface_aggregator.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.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/surfaces/surface.h"
@@ -36,7 +37,8 @@ constexpr bool kNeedsSyncPoints = true;
class SurfaceAggregatorPixelTest : public cc::RendererPixelTest<GLRenderer> {
public:
SurfaceAggregatorPixelTest()
- : support_(std::make_unique<CompositorFrameSinkSupport>(
+ : manager_(&shared_bitmap_manager_),
+ support_(std::make_unique<CompositorFrameSinkSupport>(
nullptr,
&manager_,
kArbitraryRootFrameSinkId,
@@ -51,6 +53,7 @@ class SurfaceAggregatorPixelTest : public cc::RendererPixelTest<GLRenderer> {
}
protected:
+ ServerSharedBitmapManager shared_bitmap_manager_;
FrameSinkManagerImpl manager_;
ParentLocalSurfaceIdAllocator allocator_;
std::unique_ptr<CompositorFrameSinkSupport> support_;
diff --git a/chromium/components/viz/service/display/surface_aggregator_unittest.cc b/chromium/components/viz/service/display/surface_aggregator_unittest.cc
index 23db64eab7b..a6ebb854c8c 100644
--- a/chromium/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/chromium/components/viz/service/display/surface_aggregator_unittest.cc
@@ -12,9 +12,9 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
-#include "cc/test/fake_resource_provider.h"
#include "cc/test/render_pass_test_utils.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/quads/compositor_frame.h"
@@ -24,9 +24,9 @@
#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/resources/shared_bitmap_manager.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.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/surfaces/surface.h"
@@ -34,7 +34,6 @@
#include "components/viz/test/compositor_frame_helpers.h"
#include "components/viz/test/fake_compositor_frame_sink_client.h"
#include "components/viz/test/fake_surface_observer.h"
-#include "components/viz/test/test_shared_bitmap_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkColor.h"
@@ -110,7 +109,8 @@ class DisplayTimeSource {
class SurfaceAggregatorTest : public testing::Test, public DisplayTimeSource {
public:
explicit SurfaceAggregatorTest(bool use_damage_rect)
- : observer_(false),
+ : manager_(&shared_bitmap_manager_),
+ observer_(false),
support_(std::make_unique<CompositorFrameSinkSupport>(
&fake_client_,
&manager_,
@@ -353,6 +353,7 @@ class SurfaceAggregatorTest : public testing::Test, public DisplayTimeSource {
}
protected:
+ ServerSharedBitmapManager shared_bitmap_manager_;
FrameSinkManagerImpl manager_;
FakeSurfaceObserver observer_;
FakeCompositorFrameSinkClient fake_client_;
@@ -489,7 +490,7 @@ class SurfaceAggregatorValidSurfaceTest : public SurfaceAggregatorTest {
TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleFrame) {
Quad quads[] = {Quad::SolidColorQuad(SK_ColorRED, gfx::Rect(5, 5)),
Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
- Pass passes[] = {Pass(quads, arraysize(quads), SurfaceSize())};
+ Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
// Add a callback for when the surface is damaged.
MockAggregatedDamageCallback aggregated_damage_callback;
@@ -497,7 +498,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleFrame) {
aggregated_damage_callback.GetCallback());
constexpr float device_scale_factor = 1.0f;
- SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
root_local_surface_id_, device_scale_factor);
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
@@ -509,7 +510,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleFrame) {
OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
gfx::Rect(SurfaceSize()), next_display_time()));
- AggregateAndVerify(passes, arraysize(passes), ids, arraysize(ids));
+ AggregateAndVerify(passes, base::size(passes), ids, base::size(ids));
testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
}
@@ -527,20 +528,20 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, OpacityCopied) {
Quad embedded_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
Pass embedded_passes[] = {
- Pass(embedded_quads, arraysize(embedded_quads), SurfaceSize())};
+ Pass(embedded_quads, base::size(embedded_quads), SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
SubmitCompositorFrame(embedded_support.get(), embedded_passes,
- arraysize(embedded_passes), embedded_local_surface_id,
+ base::size(embedded_passes), embedded_local_surface_id,
device_scale_factor);
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
{
Quad quads[] = {Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(),
SK_ColorWHITE, gfx::Rect(5, 5), .5f,
gfx::Transform(), false)};
- Pass passes[] = {Pass(quads, arraysize(quads), SurfaceSize())};
+ Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
- SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
root_local_surface_id_, device_scale_factor);
CompositorFrame aggregated_frame = aggregator_.Aggregate(
@@ -560,9 +561,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, OpacityCopied) {
Quad quads[] = {Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(),
SK_ColorWHITE, gfx::Rect(5, 5), .9999f,
gfx::Transform(), false)};
- Pass passes[] = {Pass(quads, arraysize(quads), SurfaceSize())};
+ Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
- SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
root_local_surface_id_, device_scale_factor);
CompositorFrame aggregated_frame = aggregator_.Aggregate(
@@ -586,20 +587,20 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, RotatedClip) {
Quad embedded_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
Pass embedded_passes[] = {
- Pass(embedded_quads, arraysize(embedded_quads), SurfaceSize())};
+ Pass(embedded_quads, base::size(embedded_quads), SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
SubmitCompositorFrame(embedded_support.get(), embedded_passes,
- arraysize(embedded_passes), embedded_local_surface_id,
+ base::size(embedded_passes), embedded_local_surface_id,
device_scale_factor);
gfx::Transform rotate;
rotate.Rotate(30);
Quad quads[] = {Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(),
SK_ColorWHITE, gfx::Rect(5, 5), 1.f, rotate,
false)};
- Pass passes[] = {Pass(quads, arraysize(quads), SurfaceSize())};
+ Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
- SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
root_local_surface_id_, device_scale_factor);
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
@@ -619,18 +620,18 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassSimpleFrame) {
Quad::SolidColorQuad(SK_ColorLTGRAY, gfx::Rect(5, 5))},
{Quad::SolidColorQuad(SK_ColorGRAY, gfx::Rect(5, 5)),
Quad::SolidColorQuad(SK_ColorDKGRAY, gfx::Rect(5, 5))}};
- Pass passes[] = {Pass(quads[0], arraysize(quads[0]), 1, SurfaceSize()),
- Pass(quads[1], arraysize(quads[1]), 2, SurfaceSize())};
+ Pass passes[] = {Pass(quads[0], base::size(quads[0]), 1, SurfaceSize()),
+ Pass(quads[1], base::size(quads[1]), 2, SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
- SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
root_local_surface_id_, device_scale_factor);
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
SurfaceId ids[] = {root_surface_id};
- AggregateAndVerify(passes, arraysize(passes), ids, arraysize(ids));
+ AggregateAndVerify(passes, base::size(passes), ids, base::size(ids));
}
// Ensure that the render pass ID map properly keeps and deletes entries.
@@ -639,11 +640,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassDeallocation) {
Quad::SolidColorQuad(SK_ColorLTGRAY, gfx::Rect(5, 5))},
{Quad::SolidColorQuad(SK_ColorGRAY, gfx::Rect(5, 5)),
Quad::SolidColorQuad(SK_ColorDKGRAY, gfx::Rect(5, 5))}};
- Pass passes[] = {Pass(quads[0], arraysize(quads[0]), 2, SurfaceSize()),
- Pass(quads[1], arraysize(quads[1]), 1, SurfaceSize())};
+ Pass passes[] = {Pass(quads[0], base::size(quads[0]), 2, SurfaceSize()),
+ Pass(quads[1], base::size(quads[1]), 1, SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
- SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
root_local_surface_id_, device_scale_factor);
SurfaceId surface_id(support_->frame_sink_id(), root_local_surface_id_);
@@ -661,10 +662,10 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassDeallocation) {
EXPECT_EQ(id0, aggregated_frame.render_pass_list[0]->id);
EXPECT_EQ(id1, aggregated_frame.render_pass_list[1]->id);
- Pass passes2[] = {Pass(quads[0], arraysize(quads[0]), 3, SurfaceSize()),
- Pass(quads[1], arraysize(quads[1]), 1, SurfaceSize())};
+ Pass passes2[] = {Pass(quads[0], base::size(quads[0]), 3, SurfaceSize()),
+ Pass(quads[1], base::size(quads[1]), 1, SurfaceSize())};
- SubmitCompositorFrame(support_.get(), passes2, arraysize(passes2),
+ SubmitCompositorFrame(support_.get(), passes2, base::size(passes2),
root_local_surface_id_, device_scale_factor);
// The RenderPass that still exists should keep the same ID.
@@ -675,7 +676,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassDeallocation) {
EXPECT_NE(id2, id0);
EXPECT_EQ(id1, aggregated_frame.render_pass_list[1]->id);
- SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
root_local_surface_id_, device_scale_factor);
// |id1| didn't exist in the previous frame, so it should be
@@ -704,11 +705,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleSurfaceReference) {
Quad embedded_quads[] = {
Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))};
Pass embedded_passes[] = {
- Pass(embedded_quads, arraysize(embedded_quads), SurfaceSize())};
+ Pass(embedded_quads, base::size(embedded_quads), SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
SubmitCompositorFrame(embedded_support.get(), embedded_passes,
- arraysize(embedded_passes), embedded_local_surface_id,
+ base::size(embedded_passes), embedded_local_surface_id,
device_scale_factor);
Quad root_quads[] = {
@@ -716,9 +717,10 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleSurfaceReference) {
Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
gfx::Rect(5, 5), false),
Quad::SolidColorQuad(SK_ColorBLACK, gfx::Rect(5, 5))};
- Pass root_passes[] = {Pass(root_quads, arraysize(root_quads), SurfaceSize())};
+ Pass root_passes[] = {
+ Pass(root_quads, base::size(root_quads), SurfaceSize())};
- SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ SubmitCompositorFrame(support_.get(), root_passes, base::size(root_passes),
root_local_surface_id_, device_scale_factor);
Quad expected_quads[] = {
@@ -726,11 +728,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleSurfaceReference) {
Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
Quad::SolidColorQuad(SK_ColorBLACK, gfx::Rect(5, 5))};
Pass expected_passes[] = {
- Pass(expected_quads, arraysize(expected_quads), SurfaceSize())};
+ Pass(expected_quads, base::size(expected_quads), SurfaceSize())};
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
SurfaceId ids[] = {root_surface_id, embedded_surface_id};
- AggregateAndVerify(expected_passes, arraysize(expected_passes), ids,
- arraysize(ids));
+ AggregateAndVerify(expected_passes, base::size(expected_passes), ids,
+ base::size(ids));
}
// This test verifies that in the absence of a primary Surface,
@@ -755,14 +757,14 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
Quad fallback_child_quads[] = {
Quad::SolidColorQuad(SK_ColorRED, gfx::Rect(fallback_size))};
Pass fallback_child_passes[] = {Pass(
- fallback_child_quads, arraysize(fallback_child_quads), fallback_size)};
+ fallback_child_quads, base::size(fallback_child_quads), fallback_size)};
// Submit a CompositorFrame to the fallback Surface containing a red
// SolidColorDrawQuad.
constexpr float device_scale_factor_1 = 1.0f;
constexpr float device_scale_factor_2 = 2.0f;
SubmitCompositorFrame(fallback_child_support.get(), fallback_child_passes,
- arraysize(fallback_child_passes),
+ base::size(fallback_child_passes),
fallback_child_local_surface_id, device_scale_factor_2);
// Try to embed |primary_child_surface_id| and if unavailable, embed
@@ -771,7 +773,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
Quad root_quads[] = {
Quad::SurfaceQuad(primary_child_surface_id, fallback_child_surface_id,
SK_ColorWHITE, surface_quad_rect, false)};
- Pass root_passes[] = {Pass(root_quads, arraysize(root_quads), SurfaceSize())};
+ Pass root_passes[] = {
+ Pass(root_quads, base::size(root_quads), SurfaceSize())};
MockAggregatedDamageCallback aggregated_damage_callback;
@@ -782,7 +785,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
fallback_child_support->SetAggregatedDamageCallbackForTesting(
aggregated_damage_callback.GetCallback());
- SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ SubmitCompositorFrame(support_.get(), root_passes, base::size(root_passes),
root_local_surface_id_, device_scale_factor_1);
// There is no CompositorFrame submitted to |primary_child_surface_id| and
@@ -796,7 +799,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
Quad::SolidColorQuad(SK_ColorRED, gfx::Rect(5, 5)),
};
Pass expected_passes1[] = {
- Pass(expected_quads1, arraysize(expected_quads1), SurfaceSize())};
+ Pass(expected_quads1, base::size(expected_quads1), SurfaceSize())};
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
SurfaceId ids[] = {root_surface_id, fallback_child_surface_id};
@@ -817,15 +820,15 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
.Times(1);
// The primary_surface will not be listed in previously contained surfaces.
- AggregateAndVerify(expected_passes1, arraysize(expected_passes1), ids,
- arraysize(ids));
+ AggregateAndVerify(expected_passes1, base::size(expected_passes1), ids,
+ base::size(ids));
testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
// Submit the fallback again to create some damage then aggregate again.
fallback_child_local_surface_id = allocator_.GenerateId();
SubmitCompositorFrame(fallback_child_support.get(), fallback_child_passes,
- arraysize(fallback_child_passes),
+ base::size(fallback_child_passes),
fallback_child_local_surface_id, device_scale_factor_1);
// The damage should be equal to whole size of the primary SurfaceDrawQuad.
@@ -834,8 +837,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
surface_quad_rect, next_display_time()))
.Times(1);
- AggregateAndVerify(expected_passes1, arraysize(expected_passes1), ids,
- arraysize(ids));
+ AggregateAndVerify(expected_passes1, base::size(expected_passes1), ids,
+ base::size(ids));
testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
@@ -843,13 +846,13 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))};
constexpr gfx::Size primary_surface_size(5, 5);
Pass primary_child_passes[] = {Pass(primary_child_quads,
- arraysize(primary_child_quads),
+ base::size(primary_child_quads),
primary_surface_size)};
// Submit a CompositorFrame to the primary Surface containing a green
// SolidColorDrawQuad.
SubmitCompositorFrame(primary_child_support.get(), primary_child_passes,
- arraysize(primary_child_passes),
+ base::size(primary_child_passes),
primary_child_local_surface_id, device_scale_factor_2);
// Now that the primary Surface has a CompositorFrame, we expect
@@ -858,7 +861,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
Quad expected_quads2[] = {
Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))};
Pass expected_passes2[] = {
- Pass(expected_quads2, arraysize(expected_quads2), SurfaceSize())};
+ Pass(expected_quads2, base::size(expected_quads2), SurfaceSize())};
SurfaceId ids2[] = {root_surface_id, primary_child_surface_id};
@@ -879,8 +882,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReference) {
gfx::Rect(primary_surface_size), next_display_time()))
.Times(1);
- AggregateAndVerify(expected_passes2, arraysize(expected_passes2), ids2,
- arraysize(ids2));
+ AggregateAndVerify(expected_passes2, base::size(expected_passes2), ids2,
+ base::size(ids2));
testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
}
@@ -917,14 +920,15 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, StretchContentToFillBounds) {
Quad root_quads[] = {
Quad::SurfaceQuad(primary_child_surface_id, primary_child_surface_id,
SK_ColorWHITE, surface_quad_rect, true)};
- Pass root_passes[] = {Pass(root_quads, arraysize(root_quads), SurfaceSize())};
+ Pass root_passes[] = {
+ Pass(root_quads, base::size(root_quads), SurfaceSize())};
MockAggregatedDamageCallback aggregated_damage_callback;
support_->SetAggregatedDamageCallbackForTesting(
aggregated_damage_callback.GetCallback());
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
- SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ SubmitCompositorFrame(support_.get(), root_passes, base::size(root_passes),
root_local_surface_id_, 1.0f);
EXPECT_CALL(
@@ -985,14 +989,15 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, StretchContentToFillStretchedBounds) {
Quad root_quads[] = {
Quad::SurfaceQuad(primary_child_surface_id, primary_child_surface_id,
SK_ColorWHITE, surface_quad_rect, true)};
- Pass root_passes[] = {Pass(root_quads, arraysize(root_quads), SurfaceSize())};
+ Pass root_passes[] = {
+ Pass(root_quads, base::size(root_quads), SurfaceSize())};
MockAggregatedDamageCallback aggregated_damage_callback;
support_->SetAggregatedDamageCallbackForTesting(
aggregated_damage_callback.GetCallback());
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
- SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ SubmitCompositorFrame(support_.get(), root_passes, base::size(root_passes),
root_local_surface_id_, 2.0f);
EXPECT_CALL(
@@ -1054,14 +1059,15 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, StretchContentToFillSquashedBounds) {
Quad root_quads[] = {
Quad::SurfaceQuad(primary_child_surface_id, primary_child_surface_id,
SK_ColorWHITE, surface_quad_rect, true)};
- Pass root_passes[] = {Pass(root_quads, arraysize(root_quads), SurfaceSize())};
+ Pass root_passes[] = {
+ Pass(root_quads, base::size(root_quads), SurfaceSize())};
MockAggregatedDamageCallback aggregated_damage_callback;
support_->SetAggregatedDamageCallbackForTesting(
aggregated_damage_callback.GetCallback());
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
- SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ SubmitCompositorFrame(support_.get(), root_passes, base::size(root_passes),
root_local_surface_id_, 0.5f);
EXPECT_CALL(
@@ -1102,13 +1108,13 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReferenceWithPrimary) {
Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))};
constexpr gfx::Size primary_size(50, 50);
Pass primary_child_passes[] = {
- Pass(primary_child_quads, arraysize(primary_child_quads), primary_size)};
+ Pass(primary_child_quads, base::size(primary_child_quads), primary_size)};
// Submit a CompositorFrame to the primary Surface containing a green
// SolidColorDrawQuad.
constexpr float device_scale_factor = 1.0f;
SubmitCompositorFrame(primary_child_support.get(), primary_child_passes,
- arraysize(primary_child_passes),
+ base::size(primary_child_passes),
primary_child_local_surface_id, device_scale_factor);
auto fallback_child_support = std::make_unique<CompositorFrameSinkSupport>(
@@ -1121,12 +1127,12 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReferenceWithPrimary) {
Quad fallback_child_quads[] = {
Quad::SolidColorQuad(SK_ColorRED, gfx::Rect(5, 5))};
Pass fallback_child_passes[] = {Pass(
- fallback_child_quads, arraysize(fallback_child_quads), SurfaceSize())};
+ fallback_child_quads, base::size(fallback_child_quads), SurfaceSize())};
// Submit a CompositorFrame to the fallback Surface containing a red
// SolidColorDrawQuad.
SubmitCompositorFrame(fallback_child_support.get(), fallback_child_passes,
- arraysize(fallback_child_passes),
+ base::size(fallback_child_passes),
fallback_child_local_surface_id, device_scale_factor);
// Try to embed |primary_child_surface_id| and if unavailabe, embed
@@ -1136,13 +1142,13 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReferenceWithPrimary) {
SK_ColorWHITE, gfx::Rect(5, 5), false)};
constexpr gfx::Size root_size(75, 75);
Pass root_passes[] = {
- Pass(root_quads, arraysize(root_quads), root_size, NoDamage())};
+ Pass(root_quads, base::size(root_quads), root_size, NoDamage())};
MockAggregatedDamageCallback aggregated_damage_callback;
support_->SetAggregatedDamageCallbackForTesting(
aggregated_damage_callback.GetCallback());
- SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ SubmitCompositorFrame(support_.get(), root_passes, base::size(root_passes),
root_local_surface_id_, device_scale_factor);
// The CompositorFrame is submitted to |primary_child_surface_id|, so
@@ -1151,7 +1157,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReferenceWithPrimary) {
Quad expected_quads1[] = {
Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))};
Pass expected_passes1[] = {
- Pass(expected_quads1, arraysize(expected_quads1), SurfaceSize())};
+ Pass(expected_quads1, base::size(expected_quads1), SurfaceSize())};
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
SurfaceId ids[] = {root_surface_id, primary_child_surface_id};
@@ -1161,14 +1167,14 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReferenceWithPrimary) {
gfx::Rect(root_size), next_display_time()));
// The fallback will not be contained within the aggregated frame.
- AggregateAndVerify(expected_passes1, arraysize(expected_passes1), ids,
- arraysize(ids));
+ AggregateAndVerify(expected_passes1, base::size(expected_passes1), ids,
+ base::size(ids));
testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
// Submit a new frame to the primary surface to cause some damage.
SubmitCompositorFrame(primary_child_support.get(), primary_child_passes,
- arraysize(primary_child_passes),
+ base::size(primary_child_passes),
primary_child_local_surface_id, device_scale_factor);
// The size of the damage should be equal to the size of the primary surface.
@@ -1177,8 +1183,8 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReferenceWithPrimary) {
gfx::Rect(primary_size), next_display_time()));
// Generate a new aggregated frame.
- AggregateAndVerify(expected_passes1, arraysize(expected_passes1), ids,
- arraysize(ids));
+ AggregateAndVerify(expected_passes1, base::size(expected_passes1), ids,
+ base::size(ids));
testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
}
@@ -1194,11 +1200,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, CopyRequest) {
Quad embedded_quads[] = {
Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))};
Pass embedded_passes[] = {
- Pass(embedded_quads, arraysize(embedded_quads), SurfaceSize())};
+ Pass(embedded_quads, base::size(embedded_quads), SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
SubmitCompositorFrame(embedded_support.get(), embedded_passes,
- arraysize(embedded_passes), embedded_local_surface_id,
+ base::size(embedded_passes), embedded_local_surface_id,
device_scale_factor);
auto copy_request = CopyOutputRequest::CreateStubForTesting();
auto* copy_request_ptr = copy_request.get();
@@ -1210,9 +1216,10 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, CopyRequest) {
Quad::SurfaceQuad(embedded_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
gfx::Rect(5, 5), false),
Quad::SolidColorQuad(SK_ColorBLACK, gfx::Rect(5, 5))};
- Pass root_passes[] = {Pass(root_quads, arraysize(root_quads), SurfaceSize())};
+ Pass root_passes[] = {
+ Pass(root_quads, base::size(root_quads), SurfaceSize())};
- SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ SubmitCompositorFrame(support_.get(), root_passes, base::size(root_passes),
root_local_surface_id_, device_scale_factor);
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
@@ -1224,9 +1231,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, CopyRequest) {
Quad::RenderPassQuad(aggregated_frame.render_pass_list[0]->id),
Quad::SolidColorQuad(SK_ColorBLACK, gfx::Rect(5, 5))};
Pass expected_passes[] = {
- Pass(embedded_quads, arraysize(embedded_quads), SurfaceSize()),
- Pass(expected_quads, arraysize(expected_quads), SurfaceSize())};
- TestPassesMatchExpectations(expected_passes, arraysize(expected_passes),
+ Pass(embedded_quads, base::size(embedded_quads), SurfaceSize()),
+ Pass(expected_quads, base::size(expected_quads), SurfaceSize())};
+ TestPassesMatchExpectations(expected_passes, base::size(expected_passes),
&aggregated_frame.render_pass_list);
ASSERT_EQ(2u, aggregated_frame.render_pass_list.size());
ASSERT_EQ(1u, aggregated_frame.render_pass_list[0]->copy_requests.size());
@@ -1234,9 +1241,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, CopyRequest) {
aggregated_frame.render_pass_list[0]->copy_requests[0].get());
SurfaceId surface_ids[] = {root_surface_id, embedded_surface_id};
- EXPECT_EQ(arraysize(surface_ids),
+ EXPECT_EQ(base::size(surface_ids),
aggregator_.previous_contained_surfaces().size());
- for (size_t i = 0; i < arraysize(surface_ids); i++) {
+ for (size_t i = 0; i < base::size(surface_ids); i++) {
EXPECT_TRUE(
aggregator_.previous_contained_surfaces().find(surface_ids[i]) !=
aggregator_.previous_contained_surfaces().end());
@@ -1255,11 +1262,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, RootCopyRequest) {
Quad embedded_quads[] = {
Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))};
Pass embedded_passes[] = {
- Pass(embedded_quads, arraysize(embedded_quads), SurfaceSize())};
+ Pass(embedded_quads, base::size(embedded_quads), SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
SubmitCompositorFrame(embedded_support.get(), embedded_passes,
- arraysize(embedded_passes), embedded_local_surface_id,
+ base::size(embedded_passes), embedded_local_surface_id,
device_scale_factor);
auto copy_request(CopyOutputRequest::CreateStubForTesting());
auto* copy_request_ptr = copy_request.get();
@@ -1273,11 +1280,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, RootCopyRequest) {
Quad::SolidColorQuad(SK_ColorBLACK, gfx::Rect(5, 5))};
Quad root_quads2[] = {Quad::SolidColorQuad(SK_ColorRED, gfx::Rect(5, 5))};
Pass root_passes[] = {
- Pass(root_quads, arraysize(root_quads), 1, SurfaceSize()),
- Pass(root_quads2, arraysize(root_quads2), 2, SurfaceSize())};
+ Pass(root_quads, base::size(root_quads), 1, SurfaceSize()),
+ Pass(root_quads2, base::size(root_quads2), 2, SurfaceSize())};
{
CompositorFrame frame = MakeEmptyCompositorFrame();
- AddPasses(&frame.render_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&frame.render_pass_list, root_passes, base::size(root_passes));
frame.render_pass_list[0]->copy_requests.push_back(std::move(copy_request));
frame.render_pass_list[1]->copy_requests.push_back(
std::move(copy_request2));
@@ -1294,9 +1301,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, RootCopyRequest) {
Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
Quad::SolidColorQuad(SK_ColorBLACK, gfx::Rect(5, 5))};
Pass expected_passes[] = {
- Pass(expected_quads, arraysize(expected_quads), SurfaceSize()),
- Pass(root_quads2, arraysize(root_quads2), SurfaceSize())};
- TestPassesMatchExpectations(expected_passes, arraysize(expected_passes),
+ Pass(expected_quads, base::size(expected_quads), SurfaceSize()),
+ Pass(root_quads2, base::size(root_quads2), SurfaceSize())};
+ TestPassesMatchExpectations(expected_passes, base::size(expected_passes),
&aggregated_frame.render_pass_list);
ASSERT_EQ(2u, aggregated_frame.render_pass_list.size());
ASSERT_EQ(1u, aggregated_frame.render_pass_list[0]->copy_requests.size());
@@ -1307,9 +1314,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, RootCopyRequest) {
aggregated_frame.render_pass_list[1]->copy_requests[0].get());
SurfaceId surface_ids[] = {root_surface_id, embedded_surface_id};
- EXPECT_EQ(arraysize(surface_ids),
+ EXPECT_EQ(base::size(surface_ids),
aggregator_.previous_contained_surfaces().size());
- for (size_t i = 0; i < arraysize(surface_ids); i++) {
+ for (size_t i = 0; i < base::size(surface_ids); i++) {
EXPECT_TRUE(
aggregator_.previous_contained_surfaces().find(surface_ids[i]) !=
aggregator_.previous_contained_surfaces().end());
@@ -1341,11 +1348,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, UnreferencedSurface) {
Quad embedded_quads[] = {
Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))};
Pass embedded_passes[] = {
- Pass(embedded_quads, arraysize(embedded_quads), SurfaceSize())};
+ Pass(embedded_quads, base::size(embedded_quads), SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
SubmitCompositorFrame(embedded_support.get(), embedded_passes,
- arraysize(embedded_passes), embedded_local_surface_id,
+ base::size(embedded_passes), embedded_local_surface_id,
device_scale_factor);
auto copy_request(CopyOutputRequest::CreateStubForTesting());
auto* copy_request_ptr = copy_request.get();
@@ -1362,14 +1369,15 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, UnreferencedSurface) {
gfx::Rect(5, 5), false),
Quad::SolidColorQuad(SK_ColorLTGRAY, gfx::Rect(5, 5))};
Pass parent_passes[] = {
- Pass(parent_quads, arraysize(parent_quads), SurfaceSize())};
+ Pass(parent_quads, base::size(parent_quads), SurfaceSize())};
{
CompositorFrame frame = MakeEmptyCompositorFrame();
- AddPasses(&frame.render_pass_list, parent_passes, arraysize(parent_passes));
+ AddPasses(&frame.render_pass_list, parent_passes,
+ base::size(parent_passes));
- frame.metadata.referenced_surfaces.push_back(embedded_surface_id);
+ frame.metadata.referenced_surfaces.emplace_back(embedded_surface_id);
parent_support->SubmitCompositorFrame(parent_local_surface_id,
std::move(frame));
@@ -1377,16 +1385,17 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, UnreferencedSurface) {
Quad root_quads[] = {Quad::SolidColorQuad(SK_ColorWHITE, gfx::Rect(5, 5)),
Quad::SolidColorQuad(SK_ColorBLACK, gfx::Rect(5, 5))};
- Pass root_passes[] = {Pass(root_quads, arraysize(root_quads), SurfaceSize())};
+ Pass root_passes[] = {
+ Pass(root_quads, base::size(root_quads), SurfaceSize())};
{
CompositorFrame frame = MakeEmptyCompositorFrame();
- AddPasses(&frame.render_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&frame.render_pass_list, root_passes, base::size(root_passes));
- frame.metadata.referenced_surfaces.push_back(parent_surface_id);
+ frame.metadata.referenced_surfaces.emplace_back(parent_surface_id);
// Reference to Surface ID of a Surface that doesn't exist should be
// included in previous_contained_surfaces, but otherwise ignored.
- frame.metadata.referenced_surfaces.push_back(nonexistent_surface_id);
+ frame.metadata.referenced_surfaces.emplace_back(nonexistent_surface_id);
support_->SubmitCompositorFrame(root_local_surface_id_, std::move(frame));
}
@@ -1400,9 +1409,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, UnreferencedSurface) {
// parent_quad should be ignored because it is neither referenced through a
// SurfaceDrawQuad nor has a copy request on it.
Pass expected_passes[] = {
- Pass(embedded_quads, arraysize(embedded_quads), SurfaceSize()),
- Pass(root_quads, arraysize(root_quads), SurfaceSize())};
- TestPassesMatchExpectations(expected_passes, arraysize(expected_passes),
+ Pass(embedded_quads, base::size(embedded_quads), SurfaceSize()),
+ Pass(root_quads, base::size(root_quads), SurfaceSize())};
+ TestPassesMatchExpectations(expected_passes, base::size(expected_passes),
&aggregated_frame.render_pass_list);
ASSERT_EQ(2u, aggregated_frame.render_pass_list.size());
ASSERT_EQ(1u, aggregated_frame.render_pass_list[0]->copy_requests.size());
@@ -1412,9 +1421,9 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, UnreferencedSurface) {
SurfaceId surface_ids[] = {
SurfaceId(support_->frame_sink_id(), root_local_surface_id_),
parent_surface_id, embedded_surface_id};
- EXPECT_EQ(arraysize(surface_ids),
+ EXPECT_EQ(base::size(surface_ids),
aggregator_.previous_contained_surfaces().size());
- for (size_t i = 0; i < arraysize(surface_ids); i++) {
+ for (size_t i = 0; i < base::size(surface_ids); i++) {
EXPECT_TRUE(
aggregator_.previous_contained_surfaces().find(surface_ids[i]) !=
aggregator_.previous_contained_surfaces().end());
@@ -1436,16 +1445,16 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassSurfaceReference) {
{Quad::SolidColorQuad(4, gfx::Rect(5, 5)),
Quad::RenderPassQuad(pass_ids[1])}};
Pass embedded_passes[] = {
- Pass(embedded_quads[0], arraysize(embedded_quads[0]), pass_ids[0],
+ Pass(embedded_quads[0], base::size(embedded_quads[0]), pass_ids[0],
SurfaceSize()),
- Pass(embedded_quads[1], arraysize(embedded_quads[1]), pass_ids[1],
+ Pass(embedded_quads[1], base::size(embedded_quads[1]), pass_ids[1],
SurfaceSize()),
- Pass(embedded_quads[2], arraysize(embedded_quads[2]), pass_ids[2],
+ Pass(embedded_quads[2], base::size(embedded_quads[2]), pass_ids[2],
SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
SubmitCompositorFrame(child_support_.get(), embedded_passes,
- arraysize(embedded_passes), embedded_local_surface_id,
+ base::size(embedded_passes), embedded_local_surface_id,
device_scale_factor);
Quad root_quads[][2] = {
@@ -1456,13 +1465,14 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassSurfaceReference) {
Quad::RenderPassQuad(pass_ids[0])},
{Quad::SolidColorQuad(7, gfx::Rect(5, 5)),
Quad::RenderPassQuad(pass_ids[1])}};
- Pass root_passes[] = {
- Pass(root_quads[0], arraysize(root_quads[0]), pass_ids[0], SurfaceSize()),
- Pass(root_quads[1], arraysize(root_quads[1]), pass_ids[1], SurfaceSize()),
- Pass(root_quads[2], arraysize(root_quads[2]), pass_ids[2],
- SurfaceSize())};
-
- SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ Pass root_passes[] = {Pass(root_quads[0], base::size(root_quads[0]),
+ pass_ids[0], SurfaceSize()),
+ Pass(root_quads[1], base::size(root_quads[1]),
+ pass_ids[1], SurfaceSize()),
+ Pass(root_quads[2], base::size(root_quads[2]),
+ pass_ids[2], SurfaceSize())};
+
+ SubmitCompositorFrame(support_.get(), root_passes, base::size(root_passes),
root_local_surface_id_, device_scale_factor);
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
@@ -1575,20 +1585,20 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, InvalidSurfaceReference) {
Quad::SurfaceQuad(InvalidSurfaceId(), InvalidSurfaceId(),
SK_ColorWHITE, gfx::Rect(5, 5), false),
Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
- Pass passes[] = {Pass(quads, arraysize(quads), SurfaceSize())};
+ Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
- SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
root_local_surface_id_, device_scale_factor);
Quad expected_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
Pass expected_passes[] = {
- Pass(expected_quads, arraysize(expected_quads), SurfaceSize())};
+ Pass(expected_quads, base::size(expected_quads), SurfaceSize())};
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
SurfaceId ids[] = {root_surface_id};
- AggregateAndVerify(expected_passes, arraysize(expected_passes), ids,
- arraysize(ids));
+ AggregateAndVerify(expected_passes, base::size(expected_passes), ids,
+ base::size(ids));
}
// Tests a reference to a valid surface with no submitted frame. A
@@ -1603,10 +1613,10 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, ValidSurfaceReferenceWithNoFrame) {
Quad::SurfaceQuad(surface_with_no_frame_id, InvalidSurfaceId(),
SK_ColorYELLOW, gfx::Rect(5, 5), false),
Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
- Pass passes[] = {Pass(quads, arraysize(quads), SurfaceSize())};
+ Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
- SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
root_local_surface_id_, device_scale_factor);
Quad expected_quads[] = {
@@ -1614,11 +1624,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, ValidSurfaceReferenceWithNoFrame) {
Quad::SolidColorQuad(SK_ColorYELLOW, gfx::Rect(5, 5)),
Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
Pass expected_passes[] = {
- Pass(expected_quads, arraysize(expected_quads), SurfaceSize())};
+ Pass(expected_quads, base::size(expected_quads), SurfaceSize())};
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
SurfaceId ids[] = {root_surface_id};
- AggregateAndVerify(expected_passes, arraysize(expected_passes), ids,
- arraysize(ids));
+ AggregateAndVerify(expected_passes, base::size(expected_passes), ids,
+ base::size(ids));
}
// Tests a reference to a valid primary surface and a fallback surface
@@ -1632,10 +1642,10 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, ValidFallbackWithNoFrame) {
Quad quads[] = {Quad::SurfaceQuad(surface_with_no_frame_id,
surface_with_no_frame_id, SK_ColorYELLOW,
gfx::Rect(5, 5), false)};
- Pass passes[] = {Pass(quads, arraysize(quads), SurfaceSize())};
+ Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
- SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
root_local_surface_id_, device_scale_factor);
Quad expected_quads[] = {
@@ -1646,11 +1656,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, ValidFallbackWithNoFrame) {
#endif
};
Pass expected_passes[] = {
- Pass(expected_quads, arraysize(expected_quads), SurfaceSize())};
+ Pass(expected_quads, base::size(expected_quads), SurfaceSize())};
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
SurfaceId ids[] = {root_surface_id};
- AggregateAndVerify(expected_passes, arraysize(expected_passes), ids,
- arraysize(ids));
+ AggregateAndVerify(expected_passes, base::size(expected_passes), ids,
+ base::size(ids));
}
// Tests a surface quad referencing itself, generating a trivial cycle.
@@ -1660,19 +1670,19 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleCyclicalReference) {
Quad quads[] = {Quad::SurfaceQuad(root_surface_id, InvalidSurfaceId(),
SK_ColorWHITE, gfx::Rect(5, 5), false),
Quad::SolidColorQuad(SK_ColorYELLOW, gfx::Rect(5, 5))};
- Pass passes[] = {Pass(quads, arraysize(quads), SurfaceSize())};
+ Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
- SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
root_local_surface_id_, device_scale_factor);
Quad expected_quads[] = {
Quad::SolidColorQuad(SK_ColorYELLOW, gfx::Rect(5, 5))};
Pass expected_passes[] = {
- Pass(expected_quads, arraysize(expected_quads), SurfaceSize())};
+ Pass(expected_quads, base::size(expected_quads), SurfaceSize())};
SurfaceId ids[] = {root_surface_id};
- AggregateAndVerify(expected_passes, arraysize(expected_passes), ids,
- arraysize(ids));
+ AggregateAndVerify(expected_passes, base::size(expected_passes), ids,
+ base::size(ids));
}
// Tests a more complex cycle with one intermediate surface.
@@ -1687,11 +1697,12 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, TwoSurfaceCyclicalReference) {
gfx::Rect(5, 5), false),
Quad::SolidColorQuad(SK_ColorCYAN, gfx::Rect(5, 5))};
Pass parent_passes[] = {
- Pass(parent_quads, arraysize(parent_quads), SurfaceSize())};
+ Pass(parent_quads, base::size(parent_quads), SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
- SubmitCompositorFrame(support_.get(), parent_passes, arraysize(parent_passes),
- root_local_surface_id_, device_scale_factor);
+ SubmitCompositorFrame(support_.get(), parent_passes,
+ base::size(parent_passes), root_local_surface_id_,
+ device_scale_factor);
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
Quad child_quads[] = {
@@ -1700,10 +1711,10 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, TwoSurfaceCyclicalReference) {
gfx::Rect(5, 5), false),
Quad::SolidColorQuad(SK_ColorMAGENTA, gfx::Rect(5, 5))};
Pass child_passes[] = {
- Pass(child_quads, arraysize(child_quads), SurfaceSize())};
+ Pass(child_quads, base::size(child_quads), SurfaceSize())};
SubmitCompositorFrame(child_support_.get(), child_passes,
- arraysize(child_passes), child_local_surface_id,
+ base::size(child_passes), child_local_surface_id,
device_scale_factor);
// The child surface's reference to the root_surface_ will be dropped, so
@@ -1718,10 +1729,10 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, TwoSurfaceCyclicalReference) {
Quad::SolidColorQuad(SK_ColorMAGENTA, gfx::Rect(5, 5)),
Quad::SolidColorQuad(SK_ColorCYAN, gfx::Rect(5, 5))};
Pass expected_passes[] = {
- Pass(expected_quads, arraysize(expected_quads), SurfaceSize())};
+ Pass(expected_quads, base::size(expected_quads), SurfaceSize())};
SurfaceId ids[] = {root_surface_id, child_surface_id};
- AggregateAndVerify(expected_passes, arraysize(expected_passes), ids,
- arraysize(ids));
+ AggregateAndVerify(expected_passes, base::size(expected_passes), ids,
+ base::size(ids));
}
// Tests that we map render pass IDs from different surfaces into a unified
@@ -1735,14 +1746,14 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, RenderPassIdMapping) {
Quad child_quad[][1] = {
{Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))},
{Quad::RenderPassQuad(child_pass_id[0])}};
- Pass surface_passes[] = {Pass(child_quad[0], arraysize(child_quad[0]),
+ Pass surface_passes[] = {Pass(child_quad[0], base::size(child_quad[0]),
child_pass_id[0], SurfaceSize()),
- Pass(child_quad[1], arraysize(child_quad[1]),
+ Pass(child_quad[1], base::size(child_quad[1]),
child_pass_id[1], SurfaceSize())};
constexpr float device_scale_factor = 1.0f;
SubmitCompositorFrame(child_support_.get(), surface_passes,
- arraysize(surface_passes), child_local_surface_id,
+ base::size(surface_passes), child_local_surface_id,
device_scale_factor);
// Pass IDs from the parent surface may collide with ones from the child.
@@ -1751,13 +1762,14 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, RenderPassIdMapping) {
{Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
gfx::Rect(5, 5), false)},
{Quad::RenderPassQuad(parent_pass_id[0])}};
- Pass parent_passes[] = {Pass(parent_quad[0], arraysize(parent_quad[0]),
+ Pass parent_passes[] = {Pass(parent_quad[0], base::size(parent_quad[0]),
parent_pass_id[0], SurfaceSize()),
- Pass(parent_quad[1], arraysize(parent_quad[1]),
+ Pass(parent_quad[1], base::size(parent_quad[1]),
parent_pass_id[1], SurfaceSize())};
- SubmitCompositorFrame(support_.get(), parent_passes, arraysize(parent_passes),
- root_local_surface_id_, device_scale_factor);
+ SubmitCompositorFrame(support_.get(), parent_passes,
+ base::size(parent_passes), root_local_surface_id_,
+ device_scale_factor);
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
CompositorFrame aggregated_frame =
@@ -1987,14 +1999,14 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateMultiplePassWithTransform) {
{Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))},
{Quad::RenderPassQuad(child_pass_id[0])},
};
- Pass child_passes[] = {Pass(child_quads[0], arraysize(child_quads[0]),
+ Pass child_passes[] = {Pass(child_quads[0], base::size(child_quads[0]),
child_pass_id[0], SurfaceSize()),
- Pass(child_quads[1], arraysize(child_quads[1]),
+ Pass(child_quads[1], base::size(child_quads[1]),
child_pass_id[1], SurfaceSize())};
CompositorFrame child_frame = MakeEmptyCompositorFrame();
AddPasses(&child_frame.render_pass_list, child_passes,
- arraysize(child_passes));
+ base::size(child_passes));
auto* child_nonroot_pass = child_frame.render_pass_list[0].get();
child_nonroot_pass->transform_to_root_target.Translate(8, 0);
@@ -2021,12 +2033,12 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateMultiplePassWithTransform) {
InvalidSurfaceId(), SK_ColorWHITE,
gfx::Rect(5, 5), false)};
Pass middle_passes[] = {
- Pass(middle_quads, arraysize(middle_quads), SurfaceSize()),
+ Pass(middle_quads, base::size(middle_quads), SurfaceSize()),
};
CompositorFrame middle_frame = MakeEmptyCompositorFrame();
AddPasses(&middle_frame.render_pass_list, middle_passes,
- arraysize(middle_passes));
+ base::size(middle_passes));
auto* middle_root_pass = middle_frame.render_pass_list[0].get();
DrawQuad* middle_frame_quad = middle_root_pass->quad_list.ElementAt(0);
@@ -2047,11 +2059,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateMultiplePassWithTransform) {
gfx::Rect(5, 5), false)};
Quad root_quads[] = {Quad::SolidColorQuad(1, gfx::Rect(5, 5))};
Pass root_passes[] = {
- Pass(secondary_quads, arraysize(secondary_quads), SurfaceSize()),
- Pass(root_quads, arraysize(root_quads), SurfaceSize())};
+ Pass(secondary_quads, base::size(secondary_quads), SurfaceSize()),
+ Pass(root_quads, base::size(root_quads), SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
root_frame.render_pass_list[0]
->shared_quad_state_list.front()
@@ -2153,11 +2165,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
kNeedsSyncPoints);
Quad child_quads[] = {Quad::RenderPassQuad(1)};
Pass child_passes[] = {
- Pass(child_quads, arraysize(child_quads), 1, SurfaceSize())};
+ Pass(child_quads, base::size(child_quads), 1, SurfaceSize())};
CompositorFrame child_frame = MakeEmptyCompositorFrame();
AddPasses(&child_frame.render_pass_list, child_passes,
- arraysize(child_passes));
+ base::size(child_passes));
auto* child_root_pass = child_frame.render_pass_list[0].get();
auto* child_root_pass_sqs = child_root_pass->shared_quad_state_list.front();
@@ -2172,14 +2184,15 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
Quad parent_surface_quads[] = {
Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
gfx::Rect(5, 5), false)};
- Pass parent_surface_passes[] = {Pass(
- parent_surface_quads, arraysize(parent_surface_quads), 1, SurfaceSize())};
+ Pass parent_surface_passes[] = {Pass(parent_surface_quads,
+ base::size(parent_surface_quads), 1,
+ SurfaceSize())};
// Parent surface is only used to test if the transform is applied correctly
// to the child surface's damage.
CompositorFrame parent_surface_frame = MakeEmptyCompositorFrame();
AddPasses(&parent_surface_frame.render_pass_list, parent_surface_passes,
- arraysize(parent_surface_passes));
+ base::size(parent_surface_passes));
LocalSurfaceId parent_local_surface_id = allocator_.GenerateId();
SurfaceId parent_surface_id(parent_support->frame_sink_id(),
@@ -2193,12 +2206,13 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
Quad root_render_pass_quads[] = {Quad::RenderPassQuad(1)};
Pass root_passes[] = {
- Pass(root_surface_quads, arraysize(root_surface_quads), 1, SurfaceSize()),
- Pass(root_render_pass_quads, arraysize(root_render_pass_quads), 2,
+ Pass(root_surface_quads, base::size(root_surface_quads), 1,
+ SurfaceSize()),
+ Pass(root_render_pass_quads, base::size(root_render_pass_quads), 2,
SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
root_frame.render_pass_list[0]
->shared_quad_state_list.front()
@@ -2227,7 +2241,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
{
CompositorFrame child_frame = MakeEmptyCompositorFrame();
AddPasses(&child_frame.render_pass_list, child_passes,
- arraysize(child_passes));
+ base::size(child_passes));
auto* child_root_pass = child_frame.render_pass_list[0].get();
auto* child_root_pass_sqs = child_root_pass->shared_quad_state_list.front();
@@ -2256,7 +2270,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
{
CompositorFrame root_frame = MakeEmptyCompositorFrame();
AddPasses(&root_frame.render_pass_list, root_passes,
- arraysize(root_passes));
+ base::size(root_passes));
root_frame.render_pass_list[0]
->shared_quad_state_list.front()
@@ -2270,7 +2284,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
{
CompositorFrame root_frame = MakeEmptyCompositorFrame();
AddPasses(&root_frame.render_pass_list, root_passes,
- arraysize(root_passes));
+ base::size(root_passes));
root_frame.render_pass_list[0]
->shared_quad_state_list.front()
@@ -2338,11 +2352,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithSquashToFit) {
kNeedsSyncPoints);
Quad child_quads[] = {Quad::RenderPassQuad(1)};
Pass child_passes[] = {
- Pass(child_quads, arraysize(child_quads), 1, gfx::Size(100, 100))};
+ Pass(child_quads, base::size(child_quads), 1, gfx::Size(100, 100))};
CompositorFrame child_frame = MakeEmptyCompositorFrame();
AddPasses(&child_frame.render_pass_list, child_passes,
- arraysize(child_passes));
+ base::size(child_passes));
auto* child_root_pass = child_frame.render_pass_list[0].get();
auto* child_root_pass_sqs = child_root_pass->shared_quad_state_list.front();
@@ -2357,14 +2371,15 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithSquashToFit) {
Quad parent_surface_quads[] = {
Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
gfx::Rect(5, 5), false)};
- Pass parent_surface_passes[] = {Pass(
- parent_surface_quads, arraysize(parent_surface_quads), 1, SurfaceSize())};
+ Pass parent_surface_passes[] = {Pass(parent_surface_quads,
+ base::size(parent_surface_quads), 1,
+ SurfaceSize())};
// Parent surface is only used to test if the transform is applied correctly
// to the child surface's damage.
CompositorFrame parent_surface_frame = MakeEmptyCompositorFrame();
AddPasses(&parent_surface_frame.render_pass_list, parent_surface_passes,
- arraysize(parent_surface_passes));
+ base::size(parent_surface_passes));
LocalSurfaceId parent_local_surface_id = allocator_.GenerateId();
SurfaceId parent_surface_id(parent_support->frame_sink_id(),
@@ -2378,11 +2393,12 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithSquashToFit) {
Quad root_render_pass_quads[] = {Quad::RenderPassQuad(1)};
Pass root_passes[] = {
- Pass(root_surface_quads, arraysize(root_surface_quads), 1, SurfaceSize()),
- Pass(root_render_pass_quads, arraysize(root_render_pass_quads), 2,
+ Pass(root_surface_quads, base::size(root_surface_quads), 1,
+ SurfaceSize()),
+ Pass(root_render_pass_quads, base::size(root_render_pass_quads), 2,
SurfaceSize())};
- SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ SubmitCompositorFrame(support_.get(), root_passes, base::size(root_passes),
root_local_surface_id_, 1.0f);
// Damage rect for first aggregation should be exactly the entire root
@@ -2402,7 +2418,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithSquashToFit) {
{
CompositorFrame child_frame = MakeEmptyCompositorFrame();
AddPasses(&child_frame.render_pass_list, child_passes,
- arraysize(child_passes));
+ base::size(child_passes));
auto* child_root_pass = child_frame.render_pass_list[0].get();
child_root_pass->damage_rect = gfx::Rect(10, 20, 20, 30);
@@ -2442,11 +2458,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithStretchToFit) {
kNeedsSyncPoints);
Quad child_quads[] = {Quad::RenderPassQuad(1)};
Pass child_passes[] = {
- Pass(child_quads, arraysize(child_quads), 1, gfx::Size(100, 100))};
+ Pass(child_quads, base::size(child_quads), 1, gfx::Size(100, 100))};
CompositorFrame child_frame = MakeEmptyCompositorFrame();
AddPasses(&child_frame.render_pass_list, child_passes,
- arraysize(child_passes));
+ base::size(child_passes));
auto* child_root_pass = child_frame.render_pass_list[0].get();
auto* child_root_pass_sqs = child_root_pass->shared_quad_state_list.front();
@@ -2461,14 +2477,15 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithStretchToFit) {
Quad parent_surface_quads[] = {
Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
gfx::Rect(5, 5), false)};
- Pass parent_surface_passes[] = {Pass(
- parent_surface_quads, arraysize(parent_surface_quads), 1, SurfaceSize())};
+ Pass parent_surface_passes[] = {Pass(parent_surface_quads,
+ base::size(parent_surface_quads), 1,
+ SurfaceSize())};
// Parent surface is only used to test if the transform is applied correctly
// to the child surface's damage.
CompositorFrame parent_surface_frame = MakeEmptyCompositorFrame();
AddPasses(&parent_surface_frame.render_pass_list, parent_surface_passes,
- arraysize(parent_surface_passes));
+ base::size(parent_surface_passes));
LocalSurfaceId parent_local_surface_id = allocator_.GenerateId();
SurfaceId parent_surface_id(parent_support->frame_sink_id(),
@@ -2482,11 +2499,12 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithStretchToFit) {
Quad root_render_pass_quads[] = {Quad::RenderPassQuad(1)};
Pass root_passes[] = {
- Pass(root_surface_quads, arraysize(root_surface_quads), 1, SurfaceSize()),
- Pass(root_render_pass_quads, arraysize(root_render_pass_quads), 2,
+ Pass(root_surface_quads, base::size(root_surface_quads), 1,
+ SurfaceSize()),
+ Pass(root_render_pass_quads, base::size(root_render_pass_quads), 2,
SurfaceSize())};
- SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
+ SubmitCompositorFrame(support_.get(), root_passes, base::size(root_passes),
root_local_surface_id_, 1.0f);
// Damage rect for first aggregation should contain entire root surface. The
@@ -2507,7 +2525,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithStretchToFit) {
{
CompositorFrame child_frame = MakeEmptyCompositorFrame();
AddPasses(&child_frame.render_pass_list, child_passes,
- arraysize(child_passes));
+ base::size(child_passes));
auto* child_root_pass = child_frame.render_pass_list[0].get();
child_root_pass->damage_rect = gfx::Rect(10, 15, 20, 30);
@@ -2539,11 +2557,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SwitchSurfaceDamage) {
Quad root_render_pass_quads[] = {Quad::SolidColorQuad(1, gfx::Rect(5, 5))};
Pass root_passes[] = {Pass(root_render_pass_quads,
- arraysize(root_render_pass_quads), 2,
+ base::size(root_render_pass_quads), 2,
SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
root_frame.render_pass_list[0]->damage_rect = gfx::Rect(5, 5, 100, 100);
@@ -2572,12 +2590,12 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SwitchSurfaceDamage) {
Quad root_render_pass_quads[] = {Quad::SolidColorQuad(1, gfx::Rect(5, 5))};
Pass root_passes[] = {Pass(root_render_pass_quads,
- arraysize(root_render_pass_quads), 2,
+ base::size(root_render_pass_quads), 2,
SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
AddPasses(&root_frame.render_pass_list, root_passes,
- arraysize(root_passes));
+ base::size(root_passes));
root_frame.render_pass_list[0]->damage_rect = gfx::Rect(1, 2, 3, 4);
@@ -2607,6 +2625,215 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, SwitchSurfaceDamage) {
}
}
+// Verifies that damage to any surface between primary and fallback damages the
+// display if primary and fallback have the FrameSinkId.
+TEST_F(SurfaceAggregatorValidSurfaceTest, SurfaceDamageSameFrameSinkId) {
+ auto embedded_support = std::make_unique<CompositorFrameSinkSupport>(
+ nullptr, &manager_, kArbitraryFrameSinkId1, kRootIsRoot,
+ kNeedsSyncPoints);
+ LocalSurfaceId id1 = allocator_.GenerateId();
+ LocalSurfaceId id2 = allocator_.GenerateId();
+ LocalSurfaceId id3 = allocator_.GenerateId();
+ LocalSurfaceId id4 = allocator_.GenerateId();
+ LocalSurfaceId id5 = allocator_.GenerateId();
+ SurfaceId fallback_surface_id(kArbitraryFrameSinkId1, id2);
+ SurfaceId primary_surface_id(kArbitraryFrameSinkId1, id4);
+ Quad embedded_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
+ Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
+ Pass embedded_passes[] = {
+ Pass(embedded_quads, base::size(embedded_quads), SurfaceSize())};
+
+ constexpr float device_scale_factor = 1.0f;
+ SubmitCompositorFrame(embedded_support.get(), embedded_passes,
+ base::size(embedded_passes), id2, device_scale_factor);
+ Quad quads[] = {Quad::SurfaceQuad(primary_surface_id, fallback_surface_id,
+ SK_ColorWHITE, gfx::Rect(5, 5), 1.f,
+ gfx::Transform(), false)};
+ Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
+
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
+ root_local_surface_id_, device_scale_factor);
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
+
+ // |id1| is before the fallback id so it shouldn't damage the display.
+ EXPECT_FALSE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id1)));
+
+ // |id2| is the fallback id so it should damage the display.
+ EXPECT_TRUE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id2)));
+
+ // |id3| is between fallback and primary so it should damage the display.
+ EXPECT_TRUE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id3)));
+
+ // |id4| is the primary id so it should damage the display.
+ EXPECT_TRUE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id4)));
+
+ // |id5| is newer than the primary surface so it shouldn't damage display.
+ EXPECT_FALSE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id5)));
+
+ // This FrameSinkId is not embedded at all so it shouldn't damage the display.
+ EXPECT_FALSE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId3, id3)));
+}
+
+// Verifies that only damage to primary and fallback surfaces and nothing in
+// between damages the display if primary and fallback have different
+// FrameSinkIds.
+TEST_F(SurfaceAggregatorValidSurfaceTest, SurfaceDamageDifferentFrameSinkId) {
+ auto embedded_support = std::make_unique<CompositorFrameSinkSupport>(
+ nullptr, &manager_, kArbitraryFrameSinkId1, kRootIsRoot,
+ kNeedsSyncPoints);
+ LocalSurfaceId id1 = allocator_.GenerateId();
+ LocalSurfaceId id2 = allocator_.GenerateId();
+ LocalSurfaceId id3 = allocator_.GenerateId();
+ LocalSurfaceId id4 = allocator_.GenerateId();
+ LocalSurfaceId id5 = allocator_.GenerateId();
+ SurfaceId fallback_surface_id(kArbitraryFrameSinkId1, id2);
+ SurfaceId primary_surface_id(kArbitraryFrameSinkId2, id4);
+ Quad embedded_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
+ Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
+ Pass embedded_passes[] = {
+ Pass(embedded_quads, base::size(embedded_quads), SurfaceSize())};
+
+ constexpr float device_scale_factor = 1.0f;
+ SubmitCompositorFrame(embedded_support.get(), embedded_passes,
+ base::size(embedded_passes), id2, device_scale_factor);
+ Quad quads[] = {Quad::SurfaceQuad(primary_surface_id, fallback_surface_id,
+ SK_ColorWHITE, gfx::Rect(5, 5), 1.f,
+ gfx::Transform(), false)};
+ Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
+
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
+ root_local_surface_id_, device_scale_factor);
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
+
+ // |id1| is before the fallback id so it shouldn't damage the display.
+ EXPECT_FALSE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id1)));
+
+ // |id2| is the fallback id so it should damage the display.
+ EXPECT_TRUE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id2)));
+
+ // |id3| is after the fallback but primary has a different FrameSinkId so it
+ // shouldn't damage the display.
+ EXPECT_FALSE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id3)));
+
+ // |id3| is before the primary but fallback has a different FrameSinkId so it
+ // shouldn't damage the display.
+ EXPECT_FALSE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId2, id3)));
+
+ // |id4| is the primary id so it should damage the display.
+ EXPECT_TRUE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId2, id4)));
+
+ // |id5| is newer than the primary surface so it shouldn't damage display.
+ EXPECT_FALSE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id5)));
+
+ // This FrameSinkId is not embedded at all so it shouldn't damage the display.
+ EXPECT_FALSE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId3, id4)));
+}
+
+// Verifies that when only a primary surface is provided any damage to primary
+// surface damages the display.
+TEST_F(SurfaceAggregatorValidSurfaceTest, SurfaceDamagePrimarySurfaceOnly) {
+ LocalSurfaceId id1 = allocator_.GenerateId();
+ LocalSurfaceId id2 = allocator_.GenerateId();
+ LocalSurfaceId id3 = allocator_.GenerateId();
+ SurfaceId primary_surface_id(kArbitraryFrameSinkId1, id2);
+ Quad quads[] = {Quad::SurfaceQuad(primary_surface_id, InvalidSurfaceId(),
+ SK_ColorWHITE, gfx::Rect(5, 5), 1.f,
+ gfx::Transform(), false)};
+ Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
+
+ constexpr float device_scale_factor = 1.0f;
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
+ root_local_surface_id_, device_scale_factor);
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
+
+ // |id1| is before the primary id but there is no fallback so it shouldn't
+ // damage the display.
+ EXPECT_FALSE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id1)));
+
+ // |id2| is the primary id so it should damage the display.
+ EXPECT_TRUE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id2)));
+
+ // |id3| is after the primary id so it shouldn't damage the display.
+ EXPECT_FALSE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id3)));
+
+ // This FrameSinkId is not embedded at all so it shouldn't damage the display.
+ EXPECT_FALSE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId3, id2)));
+}
+
+// Verifies that when primary and fallback ids are equal, only damage to that
+// particular surface causee damage to display.
+TEST_F(SurfaceAggregatorValidSurfaceTest,
+ SurfaceDamagePrimaryAndFallbackEqual) {
+ auto embedded_support = std::make_unique<CompositorFrameSinkSupport>(
+ nullptr, &manager_, kArbitraryFrameSinkId1, kRootIsRoot,
+ kNeedsSyncPoints);
+ LocalSurfaceId id1 = allocator_.GenerateId();
+ LocalSurfaceId id2 = allocator_.GenerateId();
+ LocalSurfaceId id3 = allocator_.GenerateId();
+ SurfaceId surface_id(kArbitraryFrameSinkId1, id2);
+
+ Quad embedded_quads[] = {Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
+ Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
+ Pass embedded_passes[] = {
+ Pass(embedded_quads, base::size(embedded_quads), SurfaceSize())};
+ constexpr float device_scale_factor = 1.0f;
+ SubmitCompositorFrame(embedded_support.get(), embedded_passes,
+ base::size(embedded_passes), id2, device_scale_factor);
+
+ Quad quads[] = {Quad::SurfaceQuad(surface_id, surface_id, SK_ColorWHITE,
+ gfx::Rect(5, 5), 1.f, gfx::Transform(),
+ false)};
+ Pass passes[] = {Pass(quads, base::size(quads), SurfaceSize())};
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
+ root_local_surface_id_, device_scale_factor);
+
+ SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+ CompositorFrame aggregated_frame =
+ aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
+
+ // |id1| is before the fallback id so it shouldn't damage the display.
+ EXPECT_FALSE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id1)));
+
+ // |id2| is the embedded id so it should damage the display.
+ EXPECT_TRUE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id2)));
+
+ // |id3| is newer than primary id so it shouldn't damage the display.
+ EXPECT_FALSE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId1, id3)));
+
+ // This FrameSinkId is not embedded at all so it shouldn't damage the display.
+ EXPECT_FALSE(aggregator_.NotifySurfaceDamageAndCheckForDisplayDamage(
+ SurfaceId(kArbitraryFrameSinkId3, id2)));
+}
+
class SurfaceAggregatorPartialSwapTest
: public SurfaceAggregatorValidSurfaceTest {
public:
@@ -2629,15 +2856,15 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
Quad child_quads1[] = {Quad::RenderPassQuad(child_pass_id)};
Quad child_quads2[] = {Quad::RenderPassQuad(child_pass_id)};
Quad child_quads3[] = {Quad::RenderPassQuad(child_pass_id)};
- Pass child_passes[] = {Pass(child_quads1, arraysize(child_quads1),
+ Pass child_passes[] = {Pass(child_quads1, base::size(child_quads1),
child_pass_id, SurfaceSize()),
- Pass(child_quads2, arraysize(child_quads2),
+ Pass(child_quads2, base::size(child_quads2),
child_pass_id, SurfaceSize()),
- Pass(child_quads3, arraysize(child_quads2),
+ Pass(child_quads3, base::size(child_quads2),
child_pass_id, SurfaceSize())};
RenderPassList child_pass_list;
- AddPasses(&child_pass_list, child_passes, arraysize(child_passes));
+ AddPasses(&child_pass_list, child_passes, base::size(child_passes));
child_pass_list[0]->quad_list.ElementAt(0)->visible_rect =
gfx::Rect(1, 1, 2, 2);
@@ -2667,10 +2894,10 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
false)};
Pass root_passes[] = {
- Pass(root_quads, arraysize(root_quads), SurfaceSize())};
+ Pass(root_quads, base::size(root_quads), SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_pass_list, root_passes, base::size(root_passes));
auto* root_pass = root_pass_list[0].get();
root_pass->shared_quad_state_list.front()
@@ -2702,10 +2929,10 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
false)};
Pass root_passes[] = {
- Pass(root_quads, arraysize(root_quads), SurfaceSize())};
+ Pass(root_quads, base::size(root_quads), SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_pass_list, root_passes, base::size(root_passes));
auto* root_pass = root_pass_list[0].get();
root_pass->shared_quad_state_list.front()
@@ -2739,13 +2966,13 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
int child_pass_ids[] = {1, 2};
Quad child_quads1[] = {Quad::SolidColorQuad(1, gfx::Rect(5, 5))};
Quad child_quads2[] = {Quad::RenderPassQuad(child_pass_ids[0])};
- Pass child_passes[] = {Pass(child_quads1, arraysize(child_quads1),
+ Pass child_passes[] = {Pass(child_quads1, base::size(child_quads1),
child_pass_ids[0], SurfaceSize()),
- Pass(child_quads2, arraysize(child_quads2),
+ Pass(child_quads2, base::size(child_quads2),
child_pass_ids[1], SurfaceSize())};
RenderPassList child_pass_list;
- AddPasses(&child_pass_list, child_passes, arraysize(child_passes));
+ AddPasses(&child_pass_list, child_passes, base::size(child_passes));
child_pass_list[0]->quad_list.ElementAt(0)->visible_rect =
gfx::Rect(1, 1, 2, 2);
@@ -2807,15 +3034,15 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
gfx::Rect(5, 5), false)};
Quad root_quads2[] = {Quad::RenderPassQuad(root_pass_ids[0])};
Quad root_quads3[] = {Quad::RenderPassQuad(root_pass_ids[1])};
- Pass root_passes[] = {Pass(root_quads1, arraysize(root_quads1),
+ Pass root_passes[] = {Pass(root_quads1, base::size(root_quads1),
root_pass_ids[0], SurfaceSize()),
- Pass(root_quads2, arraysize(root_quads2),
+ Pass(root_quads2, base::size(root_quads2),
root_pass_ids[1], SurfaceSize()),
- Pass(root_quads3, arraysize(root_quads3),
+ Pass(root_quads3, base::size(root_quads3),
root_pass_ids[2], SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_pass_list, root_passes, base::size(root_passes));
auto* filter_pass = root_pass_list[1].get();
filter_pass->shared_quad_state_list.front()
@@ -2858,13 +3085,13 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
Quad::RenderPassQuad(root_pass_ids[0]),
Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
gfx::Rect(5, 5), false)};
- Pass root_passes[] = {Pass(root_quads1, arraysize(root_quads1),
+ Pass root_passes[] = {Pass(root_quads1, base::size(root_quads1),
root_pass_ids[0], SurfaceSize()),
- Pass(root_quads2, arraysize(root_quads2),
+ Pass(root_quads2, base::size(root_quads2),
root_pass_ids[1], SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_pass_list, root_passes, base::size(root_passes));
auto* pass = root_pass_list[0].get();
auto* root_pass = root_pass_list[1].get();
@@ -2910,13 +3137,13 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
Quad::RenderPassQuad(root_pass_ids[0]),
Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
gfx::Rect(5, 5), false)};
- Pass root_passes[] = {Pass(root_quads1, arraysize(root_quads1),
+ Pass root_passes[] = {Pass(root_quads1, base::size(root_quads1),
root_pass_ids[0], SurfaceSize()),
- Pass(root_quads2, arraysize(root_quads2),
+ Pass(root_quads2, base::size(root_quads2),
root_pass_ids[1], SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_pass_list, root_passes, base::size(root_passes));
auto* pass = root_pass_list[0].get();
auto* root_pass = root_pass_list[1].get();
@@ -2953,11 +3180,11 @@ TEST_F(SurfaceAggregatorPartialSwapTest, IgnoreOutside) {
class SurfaceAggregatorWithResourcesTest : public testing::Test,
public DisplayTimeSource {
public:
+ SurfaceAggregatorWithResourcesTest() : manager_(&shared_bitmap_manager_) {}
+
void SetUp() override {
- shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
- resource_provider_ =
- cc::FakeResourceProvider::CreateDisplayResourceProvider(
- nullptr, shared_bitmap_manager_.get());
+ resource_provider_ = std::make_unique<DisplayResourceProvider>(
+ DisplayResourceProvider::kSoftware, nullptr, &shared_bitmap_manager_);
aggregator_ = std::make_unique<SurfaceAggregator>(
manager_.surface_manager(), resource_provider_.get(), false);
@@ -2965,8 +3192,8 @@ class SurfaceAggregatorWithResourcesTest : public testing::Test,
}
protected:
+ ServerSharedBitmapManager shared_bitmap_manager_;
FrameSinkManagerImpl manager_;
- std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
std::unique_ptr<DisplayResourceProvider> resource_provider_;
std::unique_ptr<SurfaceAggregator> aggregator_;
};
@@ -2989,10 +3216,14 @@ void SubmitCompositorFrameWithResources(ResourceId* resource_ids,
}
for (size_t i = 0u; i < num_resource_ids; ++i) {
- TransferableResource resource;
+ auto resource = TransferableResource::MakeSoftware(
+ SharedBitmap::GenerateId(), gfx::Size(1, 1), RGBA_8888);
resource.id = resource_ids[i];
- // ResourceProvider is software, so only software resources are valid.
- resource.is_software = valid;
+ if (!valid) {
+ // ResourceProvider is software, so only software resources are valid. Do
+ // this to cause the resource to be rejected.
+ resource.is_software = false;
+ }
frame.resource_list.push_back(resource);
auto* quad = pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
const gfx::Rect rect;
@@ -3025,7 +3256,7 @@ TEST_F(SurfaceAggregatorWithResourcesTest, TakeResourcesOneSurface) {
SurfaceId surface_id(support->frame_sink_id(), local_surface_id);
ResourceId ids[] = {11, 12, 13};
- SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
+ SubmitCompositorFrameWithResources(ids, base::size(ids), true, SurfaceId(),
support.get(), surface_id);
CompositorFrame frame =
@@ -3062,7 +3293,7 @@ TEST_F(SurfaceAggregatorWithResourcesTest, ReturnResourcesAsSurfacesChange) {
SurfaceId surface_id2(support->frame_sink_id(), local_surface_id2);
ResourceId ids[] = {11, 12, 13};
- SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
+ SubmitCompositorFrameWithResources(ids, base::size(ids), true, SurfaceId(),
support.get(), surface_id1);
CompositorFrame frame =
@@ -3133,10 +3364,10 @@ TEST_F(SurfaceAggregatorWithResourcesTest, TwoSurfaces) {
SurfaceId surface2_id(support2->frame_sink_id(), local_frame2_id);
ResourceId ids[] = {11, 12, 13};
- SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
+ SubmitCompositorFrameWithResources(ids, base::size(ids), true, SurfaceId(),
support1.get(), surface1_id);
ResourceId ids2[] = {14, 15, 16};
- SubmitCompositorFrameWithResources(ids2, arraysize(ids2), true, SurfaceId(),
+ SubmitCompositorFrameWithResources(ids2, base::size(ids2), true, SurfaceId(),
support2.get(), surface2_id);
CompositorFrame frame =
@@ -3184,16 +3415,16 @@ TEST_F(SurfaceAggregatorWithResourcesTest, InvalidChildSurface) {
child_local_surface_id);
ResourceId ids[] = {14, 15, 16};
- SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
+ SubmitCompositorFrameWithResources(ids, base::size(ids), true, SurfaceId(),
child_support.get(), child_surface_id);
ResourceId ids2[] = {17, 18, 19};
- SubmitCompositorFrameWithResources(ids2, arraysize(ids2), false,
+ SubmitCompositorFrameWithResources(ids2, base::size(ids2), false,
child_surface_id, middle_support.get(),
middle_surface_id);
ResourceId ids3[] = {20, 21, 22};
- SubmitCompositorFrameWithResources(ids3, arraysize(ids3), true,
+ SubmitCompositorFrameWithResources(ids3, base::size(ids3), true,
middle_surface_id, root_support.get(),
root_surface_id);
@@ -3205,7 +3436,7 @@ TEST_F(SurfaceAggregatorWithResourcesTest, InvalidChildSurface) {
ASSERT_EQ(1u, pass_list->size());
EXPECT_EQ(1u, pass_list->back()->shared_quad_state_list.size());
EXPECT_EQ(3u, pass_list->back()->quad_list.size());
- SubmitCompositorFrameWithResources(ids2, arraysize(ids), true,
+ SubmitCompositorFrameWithResources(ids2, base::size(ids), true,
child_surface_id, middle_support.get(),
middle_surface_id);
@@ -3231,7 +3462,7 @@ TEST_F(SurfaceAggregatorWithResourcesTest, SecureOutputTexture) {
SurfaceId surface2_id(support2->frame_sink_id(), local_frame2_id);
ResourceId ids[] = {11, 12, 13};
- SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
+ SubmitCompositorFrameWithResources(ids, base::size(ids), true, SurfaceId(),
support1.get(), surface1_id);
CompositorFrame frame =
@@ -3288,14 +3519,14 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, ColorSpaceTest) {
Quad::SolidColorQuad(SK_ColorLTGRAY, gfx::Rect(5, 5))},
{Quad::SolidColorQuad(SK_ColorGRAY, gfx::Rect(5, 5)),
Quad::SolidColorQuad(SK_ColorDKGRAY, gfx::Rect(5, 5))}};
- Pass passes[] = {Pass(quads[0], arraysize(quads[0]), 2, SurfaceSize()),
- Pass(quads[1], arraysize(quads[1]), 1, SurfaceSize())};
+ Pass passes[] = {Pass(quads[0], base::size(quads[0]), 2, SurfaceSize()),
+ Pass(quads[1], base::size(quads[1]), 1, SurfaceSize())};
gfx::ColorSpace color_space1 = gfx::ColorSpace::CreateXYZD50();
gfx::ColorSpace color_space2 = gfx::ColorSpace::CreateSRGB();
gfx::ColorSpace color_space3 = gfx::ColorSpace::CreateSCRGBLinear();
constexpr float device_scale_factor = 1.0f;
- SubmitCompositorFrame(support_.get(), passes, arraysize(passes),
+ SubmitCompositorFrame(support_.get(), passes, base::size(passes),
root_local_surface_id_, device_scale_factor);
SurfaceId surface_id(support_->frame_sink_id(), root_local_surface_id_);
@@ -3329,11 +3560,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, ColorSpaceTest) {
TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageByChangingChildSurface) {
Quad child_surface_quads[] = {Quad::RenderPassQuad(1)};
Pass child_surface_passes[] = {Pass(
- child_surface_quads, arraysize(child_surface_quads), 1, SurfaceSize())};
+ child_surface_quads, base::size(child_surface_quads), 1, SurfaceSize())};
CompositorFrame child_surface_frame = MakeEmptyCompositorFrame();
AddPasses(&child_surface_frame.render_pass_list, child_surface_passes,
- arraysize(child_surface_passes));
+ base::size(child_surface_passes));
LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
SurfaceId child_surface_id(child_support_->frame_sink_id(),
@@ -3344,11 +3575,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageByChangingChildSurface) {
Quad root_surface_quads[] = {
Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
gfx::Rect(5, 5), false)};
- Pass root_passes[] = {Pass(root_surface_quads, arraysize(root_surface_quads),
+ Pass root_passes[] = {Pass(root_surface_quads, base::size(root_surface_quads),
1, SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
support_->SubmitCompositorFrame(root_local_surface_id_,
@@ -3371,7 +3602,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageByChangingChildSurface) {
{
CompositorFrame child_surface_frame = MakeEmptyCompositorFrame();
AddPasses(&child_surface_frame.render_pass_list, child_surface_passes,
- arraysize(child_surface_passes));
+ base::size(child_surface_passes));
child_support_->SubmitCompositorFrame(child_local_surface_id,
std::move(child_surface_frame));
@@ -3386,7 +3617,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageByChangingChildSurface) {
{
CompositorFrame child_surface_frame = MakeEmptyCompositorFrame();
AddPasses(&child_surface_frame.render_pass_list, child_surface_passes,
- arraysize(child_surface_passes));
+ base::size(child_surface_passes));
child_surface_frame.render_pass_list[0]->damage_rect = gfx::Rect();
child_support_->SubmitCompositorFrame(child_local_surface_id,
std::move(child_surface_frame));
@@ -3409,11 +3640,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
Quad child_surface_quads[] = {Quad::RenderPassQuad(1)};
Pass child_surface_passes[] = {Pass(
- child_surface_quads, arraysize(child_surface_quads), 1, SurfaceSize())};
+ child_surface_quads, base::size(child_surface_quads), 1, SurfaceSize())};
CompositorFrame child_surface_frame = MakeEmptyCompositorFrame();
AddPasses(&child_surface_frame.render_pass_list, child_surface_passes,
- arraysize(child_surface_passes));
+ base::size(child_surface_passes));
LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
SurfaceId child_surface_id(child_support_->frame_sink_id(),
@@ -3424,11 +3655,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
Quad root_surface_quads[] = {
Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
gfx::Rect(5, 5), false)};
- Pass root_passes[] = {Pass(root_surface_quads, arraysize(root_surface_quads),
+ Pass root_passes[] = {Pass(root_surface_quads, base::size(root_surface_quads),
1, SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
support_->SubmitCompositorFrame(root_local_surface_id_,
@@ -3450,14 +3681,14 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
// Add a grand_child_frame should cause damage.
Quad grand_child_quads[] = {Quad::RenderPassQuad(1)};
Pass grand_child_passes[] = {
- Pass(grand_child_quads, arraysize(grand_child_quads), 1, SurfaceSize())};
+ Pass(grand_child_quads, base::size(grand_child_quads), 1, SurfaceSize())};
LocalSurfaceId grand_child_local_surface_id = allocator_.GenerateId();
SurfaceId grand_child_surface_id(grand_child_support->frame_sink_id(),
grand_child_local_surface_id);
{
CompositorFrame grand_child_frame = MakeEmptyCompositorFrame();
AddPasses(&grand_child_frame.render_pass_list, grand_child_passes,
- arraysize(grand_child_passes));
+ base::size(grand_child_passes));
grand_child_support->SubmitCompositorFrame(grand_child_local_surface_id,
std::move(grand_child_frame));
@@ -3467,11 +3698,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
Quad::SurfaceQuad(grand_child_surface_id, InvalidSurfaceId(),
SK_ColorWHITE, gfx::Rect(5, 5), false)};
Pass new_child_surface_passes[] = {Pass(new_child_surface_quads,
- arraysize(new_child_surface_quads),
+ base::size(new_child_surface_quads),
1, SurfaceSize())};
child_surface_frame = MakeEmptyCompositorFrame();
AddPasses(&child_surface_frame.render_pass_list, new_child_surface_passes,
- arraysize(new_child_surface_passes));
+ base::size(new_child_surface_passes));
child_support_->SubmitCompositorFrame(child_local_surface_id,
std::move(child_surface_frame));
@@ -3494,7 +3725,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
{
CompositorFrame grand_child_frame = MakeEmptyCompositorFrame();
AddPasses(&grand_child_frame.render_pass_list, grand_child_passes,
- arraysize(grand_child_passes));
+ base::size(grand_child_passes));
grand_child_support->SubmitCompositorFrame(grand_child_local_surface_id,
std::move(grand_child_frame));
@@ -3509,7 +3740,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
{
CompositorFrame grand_child_frame = MakeEmptyCompositorFrame();
AddPasses(&grand_child_frame.render_pass_list, grand_child_passes,
- arraysize(grand_child_passes));
+ base::size(grand_child_passes));
grand_child_frame.render_pass_list[0]->damage_rect = gfx::Rect();
grand_child_support->SubmitCompositorFrame(grand_child_local_surface_id,
std::move(grand_child_frame));
@@ -3527,11 +3758,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageFromRenderPassQuads) {
Quad child_quads[] = {Quad::RenderPassQuad(1)};
Pass child_passes[] = {
- Pass(child_quads, arraysize(child_quads), 1, SurfaceSize())};
+ Pass(child_quads, base::size(child_quads), 1, SurfaceSize())};
CompositorFrame child_frame = MakeEmptyCompositorFrame();
AddPasses(&child_frame.render_pass_list, child_passes,
- arraysize(child_passes));
+ base::size(child_passes));
LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
SurfaceId child_surface_id(child_support_->frame_sink_id(),
@@ -3545,12 +3776,13 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageFromRenderPassQuads) {
Quad root_render_pass_quads[] = {Quad::RenderPassQuad(1)};
Pass root_passes[] = {
- Pass(root_surface_quads, arraysize(root_surface_quads), 1, SurfaceSize()),
- Pass(root_render_pass_quads, arraysize(root_render_pass_quads), 2,
+ Pass(root_surface_quads, base::size(root_surface_quads), 1,
+ SurfaceSize()),
+ Pass(root_render_pass_quads, base::size(root_render_pass_quads), 2,
SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
support_->SubmitCompositorFrame(root_local_surface_id_,
std::move(root_frame));
@@ -3579,7 +3811,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, HasDamageFromRenderPassQuads) {
{
CompositorFrame child_frame = MakeEmptyCompositorFrame();
AddPasses(&child_frame.render_pass_list, child_passes,
- arraysize(child_passes));
+ base::size(child_passes));
child_support_->SubmitCompositorFrame(child_local_surface_id,
std::move(child_frame));
@@ -3602,11 +3834,12 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, DamageRectOfCachedRenderPass) {
{Quad::RenderPassQuad(pass_id[0])},
};
Pass root_passes[] = {
- Pass(root_quads[0], arraysize(root_quads[0]), pass_id[0], SurfaceSize()),
- Pass(root_quads[1], arraysize(root_quads[1]), pass_id[1], SurfaceSize())};
+ Pass(root_quads[0], base::size(root_quads[0]), pass_id[0], SurfaceSize()),
+ Pass(root_quads[1], base::size(root_quads[1]), pass_id[1],
+ SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
support_->SubmitCompositorFrame(root_local_surface_id_,
std::move(root_frame));
@@ -3630,7 +3863,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, DamageRectOfCachedRenderPass) {
{
CompositorFrame root_frame = MakeEmptyCompositorFrame();
AddPasses(&root_frame.render_pass_list, root_passes,
- arraysize(root_passes));
+ base::size(root_passes));
auto* nonroot_pass = root_frame.render_pass_list[0].get();
nonroot_pass->transform_to_root_target.Translate(8, 0);
@@ -3657,7 +3890,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest, DamageRectOfCachedRenderPass) {
{
CompositorFrame root_frame = MakeEmptyCompositorFrame();
AddPasses(&root_frame.render_pass_list, root_passes,
- arraysize(root_passes));
+ base::size(root_passes));
auto* nonroot_pass = root_frame.render_pass_list[0].get();
nonroot_pass->transform_to_root_target.Translate(8, 0);
@@ -3691,14 +3924,14 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
{Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))},
{Quad::RenderPassQuad(pass_id[0])},
};
- Pass child_passes[] = {Pass(child_quads[0], arraysize(child_quads[0]),
+ Pass child_passes[] = {Pass(child_quads[0], base::size(child_quads[0]),
pass_id[0], SurfaceSize()),
- Pass(child_quads[1], arraysize(child_quads[1]),
+ Pass(child_quads[1], base::size(child_quads[1]),
pass_id[1], SurfaceSize())};
CompositorFrame child_frame = MakeEmptyCompositorFrame();
AddPasses(&child_frame.render_pass_list, child_passes,
- arraysize(child_passes));
+ base::size(child_passes));
LocalSurfaceId child_local_surface_id = allocator_.GenerateId();
SurfaceId child_surface_id(child_support_->frame_sink_id(),
@@ -3710,11 +3943,11 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
Quad::SurfaceQuad(child_surface_id, InvalidSurfaceId(), SK_ColorWHITE,
gfx::Rect(5, 5), false)};
- Pass root_passes[] = {Pass(root_surface_quads, arraysize(root_surface_quads),
+ Pass root_passes[] = {Pass(root_surface_quads, base::size(root_surface_quads),
1, SurfaceSize())};
CompositorFrame root_frame = MakeEmptyCompositorFrame();
- AddPasses(&root_frame.render_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_frame.render_pass_list, root_passes, base::size(root_passes));
support_->SubmitCompositorFrame(root_local_surface_id_,
std::move(root_frame));
@@ -3738,7 +3971,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
{
CompositorFrame child_frame = MakeEmptyCompositorFrame();
AddPasses(&child_frame.render_pass_list, child_passes,
- arraysize(child_passes));
+ base::size(child_passes));
auto* child_nonroot_pass = child_frame.render_pass_list[0].get();
child_nonroot_pass->transform_to_root_target.Translate(8, 0);
@@ -3765,7 +3998,7 @@ TEST_F(SurfaceAggregatorValidSurfaceTest,
{
CompositorFrame child_frame = MakeEmptyCompositorFrame();
AddPasses(&child_frame.render_pass_list, child_passes,
- arraysize(child_passes));
+ base::size(child_passes));
auto* child_nonroot_pass = child_frame.render_pass_list[0].get();
child_nonroot_pass->transform_to_root_target.Translate(8, 0);
@@ -3806,13 +4039,13 @@ TEST_F(SurfaceAggregatorPartialSwapTest, NotIgnoreOutsideForCachedRenderPass) {
{Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))},
{Quad::RenderPassQuad(pass_id[0])},
};
- Pass child_passes[] = {Pass(child_quads[0], arraysize(child_quads[0]),
+ Pass child_passes[] = {Pass(child_quads[0], base::size(child_quads[0]),
pass_id[0], SurfaceSize()),
- Pass(child_quads[1], arraysize(child_quads[1]),
+ Pass(child_quads[1], base::size(child_quads[1]),
pass_id[1], SurfaceSize())};
RenderPassList child_pass_list;
- AddPasses(&child_pass_list, child_passes, arraysize(child_passes));
+ AddPasses(&child_pass_list, child_passes, base::size(child_passes));
child_pass_list[0]->quad_list.ElementAt(0)->visible_rect =
gfx::Rect(1, 1, 3, 3);
@@ -3836,13 +4069,13 @@ TEST_F(SurfaceAggregatorPartialSwapTest, NotIgnoreOutsideForCachedRenderPass) {
gfx::Rect(5, 5), false)},
{Quad::RenderPassQuad(pass_id[0])},
};
- Pass root_passes[] = {Pass(root_quads[0], arraysize(root_quads[0]),
+ Pass root_passes[] = {Pass(root_quads[0], base::size(root_quads[0]),
pass_id[0], SurfaceSize()),
- Pass(root_quads[1], arraysize(root_quads[1]),
+ Pass(root_quads[1], base::size(root_quads[1]),
pass_id[1], SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_pass_list, root_passes, base::size(root_passes));
auto* root_pass = root_pass_list[1].get();
root_pass->shared_quad_state_list.front()
@@ -3876,13 +4109,13 @@ TEST_F(SurfaceAggregatorPartialSwapTest, NotIgnoreOutsideForCachedRenderPass) {
gfx::Rect(5, 5), false)},
{Quad::RenderPassQuad(pass_id[0])},
};
- Pass root_passes[] = {Pass(root_quads[0], arraysize(root_quads[0]),
+ Pass root_passes[] = {Pass(root_quads[0], base::size(root_quads[0]),
pass_id[0], SurfaceSize()),
- Pass(root_quads[1], arraysize(root_quads[1]),
+ Pass(root_quads[1], base::size(root_quads[1]),
pass_id[1], SurfaceSize())};
RenderPassList root_pass_list;
- AddPasses(&root_pass_list, root_passes, arraysize(root_passes));
+ AddPasses(&root_pass_list, root_passes, base::size(root_passes));
auto* root_pass = root_pass_list[1].get();
root_pass->shared_quad_state_list.front()
diff --git a/chromium/components/viz/service/display/sync_query_collection.cc b/chromium/components/viz/service/display/sync_query_collection.cc
index 60af0839cec..f68e15d580b 100644
--- a/chromium/components/viz/service/display/sync_query_collection.cc
+++ b/chromium/components/viz/service/display/sync_query_collection.cc
@@ -6,7 +6,7 @@
#include "base/memory/weak_ptr.h"
#include "cc/base/container_util.h"
-#include "components/viz/common/resources/resource_fence.h"
+#include "components/viz/service/display/resource_fence.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/gles2_interface.h"
diff --git a/chromium/components/viz/service/display/texture_deleter_unittest.cc b/chromium/components/viz/service/display/texture_deleter_unittest.cc
index e33a26eacd3..dec49f4d0fa 100644
--- a/chromium/components/viz/service/display/texture_deleter_unittest.cc
+++ b/chromium/components/viz/service/display/texture_deleter_unittest.cc
@@ -8,7 +8,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "components/viz/common/resources/single_release_callback.h"
#include "components/viz/test/test_context_provider.h"
-#include "components/viz/test/test_web_graphics_context_3d.h"
+#include "components/viz/test/test_gles2_interface.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace viz {
@@ -26,18 +26,18 @@ TEST(TextureDeleterTest, Destroy) {
context_provider->ContextGL()->GenTextures(1, &texture_id);
EXPECT_TRUE(context_provider->HasOneRef());
- EXPECT_EQ(1u, context_provider->TestContext3d()->NumTextures());
+ EXPECT_EQ(1u, context_provider->TestContextGL()->NumTextures());
std::unique_ptr<SingleReleaseCallback> cb =
deleter->GetReleaseCallback(context_provider, texture_id);
EXPECT_FALSE(context_provider->HasOneRef());
- EXPECT_EQ(1u, context_provider->TestContext3d()->NumTextures());
+ EXPECT_EQ(1u, context_provider->TestContextGL()->NumTextures());
// When the deleter is destroyed, it immediately drops its ref on the
// ContextProvider, and deletes the texture.
deleter = nullptr;
EXPECT_TRUE(context_provider->HasOneRef());
- EXPECT_EQ(0u, context_provider->TestContext3d()->NumTextures());
+ EXPECT_EQ(0u, context_provider->TestContextGL()->NumTextures());
// Run the scoped release callback before destroying it, but it won't do
// anything.
@@ -55,19 +55,19 @@ TEST(TextureDeleterTest, NullTaskRunner) {
context_provider->ContextGL()->GenTextures(1, &texture_id);
EXPECT_TRUE(context_provider->HasOneRef());
- EXPECT_EQ(1u, context_provider->TestContext3d()->NumTextures());
+ EXPECT_EQ(1u, context_provider->TestContextGL()->NumTextures());
std::unique_ptr<SingleReleaseCallback> cb =
deleter->GetReleaseCallback(context_provider, texture_id);
EXPECT_FALSE(context_provider->HasOneRef());
- EXPECT_EQ(1u, context_provider->TestContext3d()->NumTextures());
+ EXPECT_EQ(1u, context_provider->TestContextGL()->NumTextures());
cb->Run(gpu::SyncToken(), false);
// With no task runner the callback will immediately drops its ref on the
// ContextProvider and delete the texture.
EXPECT_TRUE(context_provider->HasOneRef());
- EXPECT_EQ(0u, context_provider->TestContext3d()->NumTextures());
+ EXPECT_EQ(0u, context_provider->TestContextGL()->NumTextures());
}
} // namespace
diff --git a/chromium/components/viz/service/display_embedder/DEPS b/chromium/components/viz/service/display_embedder/DEPS
index 38bf218cf88..1246dfd2a43 100644
--- a/chromium/components/viz/service/display_embedder/DEPS
+++ b/chromium/components/viz/service/display_embedder/DEPS
@@ -9,6 +9,8 @@ include_rules = [
"+components/viz/service/display/output_surface_frame.h",
"+components/viz/service/display/output_surface.h",
"+components/viz/service/display/overlay_candidate_validator.h",
+ "+components/viz/service/display/resource_metadata.h",
+ "+components/viz/service/display/shared_bitmap_manager.h",
"+components/viz/service/display/skia_output_surface.h",
"+components/viz/service/display/software_output_device.h",
"+gpu/config/gpu_feature_info.h",
@@ -17,8 +19,10 @@ include_rules = [
"+gpu/command_buffer/client",
"+gpu/command_buffer/common",
"+gpu/command_buffer/service",
+ "+gpu/config",
"+gpu/ipc",
"+gpu/skia_bindings",
+ "+gpu/vulkan",
"+mojo/public/cpp/bindings",
"+mojo/public/cpp/system",
"+skia",
@@ -44,10 +48,6 @@ specific_include_rules = {
"+cc/test",
"+third_party/khronos/GLES2",
],
- # TODO(kylechar): Move this class to components/viz/service/frame_sinks/.
- "external_begin_frame_controller_impl.h": [
- "+components/viz/service/display/display.h",
- ],
# TODO(kylechar): Break these dependencies.
"gpu_display_provider.cc": [
"+components/viz/service/display/display.h",
diff --git a/chromium/components/viz/service/display_embedder/buffer_queue.cc b/chromium/components/viz/service/display_embedder/buffer_queue.cc
index 8782e86d2ba..2a8e2a0e824 100644
--- a/chromium/components/viz/service/display_embedder/buffer_queue.cc
+++ b/chromium/components/viz/service/display_embedder/buffer_queue.cc
@@ -265,7 +265,7 @@ std::unique_ptr<BufferQueue::AllocatedSurface> BufferQueue::GetNextSurface() {
std::unique_ptr<gfx::GpuMemoryBuffer> buffer(
gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
size_, format_, gfx::BufferUsage::SCANOUT, surface_handle_));
- if (!buffer.get()) {
+ if (!buffer) {
gl_->DeleteTextures(1, &texture);
DLOG(ERROR) << "Failed to allocate GPU memory buffer";
return nullptr;
diff --git a/chromium/components/viz/service/display_embedder/buffer_queue_unittest.cc b/chromium/components/viz/service/display_embedder/buffer_queue_unittest.cc
index fd949f4be1b..03b95cc9f6e 100644
--- a/chromium/components/viz/service/display_embedder/buffer_queue_unittest.cc
+++ b/chromium/components/viz/service/display_embedder/buffer_queue_unittest.cc
@@ -14,8 +14,8 @@
#include "build/build_config.h"
#include "components/viz/common/gl_helper.h"
#include "components/viz/test/test_context_provider.h"
+#include "components/viz/test/test_gles2_interface.h"
#include "components/viz/test/test_gpu_memory_buffer_manager.h"
-#include "components/viz/test/test_web_graphics_context_3d.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -118,9 +118,11 @@ class BufferQueueTest : public ::testing::Test {
public:
BufferQueueTest() : doublebuffering_(true), first_frame_(true) {}
- void SetUp() override { InitWithContext(TestWebGraphicsContext3D::Create()); }
+ void SetUp() override {
+ InitWithContext(std::make_unique<TestGLES2Interface>());
+ }
- void InitWithContext(std::unique_ptr<TestWebGraphicsContext3D> context) {
+ void InitWithContext(std::unique_ptr<TestGLES2Interface> context) {
context_provider_ = TestContextProvider::Create(std::move(context));
context_provider_->BindToCurrentThread();
gpu_memory_buffer_manager_.reset(new StubGpuMemoryBufferManager);
@@ -225,19 +227,19 @@ GLuint CreateImageDefault() {
return ++id;
}
-class MockedContext : public TestWebGraphicsContext3D {
+class MockedContext : public TestGLES2Interface {
public:
MockedContext() {
- ON_CALL(*this, createImageCHROMIUM(_, _, _, _))
+ ON_CALL(*this, CreateImageCHROMIUM(_, _, _, _))
.WillByDefault(testing::InvokeWithoutArgs(&CreateImageDefault));
}
- MOCK_METHOD2(bindFramebuffer, void(GLenum, GLuint));
- MOCK_METHOD2(bindTexture, void(GLenum, GLuint));
- MOCK_METHOD2(bindTexImage2DCHROMIUM, void(GLenum, GLint));
- MOCK_METHOD4(createImageCHROMIUM,
+ MOCK_METHOD2(BindFramebuffer, void(GLenum, GLuint));
+ MOCK_METHOD2(BindTexture, void(GLenum, GLuint));
+ MOCK_METHOD2(BindTexImage2DCHROMIUM, void(GLenum, GLint));
+ MOCK_METHOD4(CreateImageCHROMIUM,
GLuint(ClientBuffer, GLsizei, GLsizei, GLenum));
- MOCK_METHOD1(destroyImageCHROMIUM, void(GLuint));
- MOCK_METHOD5(framebufferTexture2D,
+ MOCK_METHOD1(DestroyImageCHROMIUM, void(GLuint));
+ MOCK_METHOD5(FramebufferTexture2D,
void(GLenum, GLenum, GLenum, GLuint, GLint));
};
@@ -245,7 +247,7 @@ class BufferQueueMockedContextTest : public BufferQueueTest {
public:
void SetUp() override {
context_ = new MockedContext();
- InitWithContext(std::unique_ptr<TestWebGraphicsContext3D>(context_));
+ InitWithContext(base::WrapUnique<TestGLES2Interface>(context_));
}
protected:
@@ -283,8 +285,8 @@ TEST(BufferQueueStandaloneTest, FboInitialization) {
CreateBufferQueue(GL_TEXTURE_2D, context_provider->ContextGL(),
gpu_memory_buffer_manager.get());
- EXPECT_CALL(*context, bindFramebuffer(GL_FRAMEBUFFER, Ne(0U)));
- ON_CALL(*context, framebufferTexture2D(_, _, _, _, _))
+ EXPECT_CALL(*context, BindFramebuffer(GL_FRAMEBUFFER, Ne(0U)));
+ ON_CALL(*context, FramebufferTexture2D(_, _, _, _, _))
.WillByDefault(Return());
output_surface->Reshape(gfx::Size(10, 20), 1.0f, gfx::ColorSpace(), false);
@@ -303,20 +305,20 @@ TEST(BufferQueueStandaloneTest, FboBinding) {
std::unique_ptr<BufferQueue> output_surface = CreateBufferQueue(
target, context_provider->ContextGL(), gpu_memory_buffer_manager.get());
- EXPECT_CALL(*context, bindTexture(target, Ne(0U)));
- EXPECT_CALL(*context, destroyImageCHROMIUM(1));
+ EXPECT_CALL(*context, BindTexture(target, Ne(0U)));
+ EXPECT_CALL(*context, DestroyImageCHROMIUM(1));
Expectation image =
EXPECT_CALL(*context,
- createImageCHROMIUM(_, 0, 0, kBufferQueueInternalformat))
+ CreateImageCHROMIUM(_, 0, 0, kBufferQueueInternalformat))
.WillOnce(Return(1));
Expectation fb =
- EXPECT_CALL(*context, bindFramebuffer(GL_FRAMEBUFFER, Ne(0U)));
- Expectation tex = EXPECT_CALL(*context, bindTexture(target, Ne(0U)));
+ EXPECT_CALL(*context, BindFramebuffer(GL_FRAMEBUFFER, Ne(0U)));
+ Expectation tex = EXPECT_CALL(*context, BindTexture(target, Ne(0U)));
Expectation bind_tex =
- EXPECT_CALL(*context, bindTexImage2DCHROMIUM(target, 1))
+ EXPECT_CALL(*context, BindTexImage2DCHROMIUM(target, 1))
.After(tex, image);
EXPECT_CALL(*context,
- framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
target, Ne(0U), _))
.After(fb, bind_tex);
@@ -627,9 +629,9 @@ TEST_F(BufferQueueMockedContextTest, RecreateBuffers) {
// Expect all 4 images to be destroyed, 3 of the existing textures to be
// copied from and 3 new images to be created.
- EXPECT_CALL(*context_, createImageCHROMIUM(_, screen_size.width(),
- screen_size.height(),
- kBufferQueueInternalformat))
+ EXPECT_CALL(*context_,
+ CreateImageCHROMIUM(_, screen_size.width(), screen_size.height(),
+ kBufferQueueInternalformat))
.Times(3);
Expectation copy1 = EXPECT_CALL(*mock_output_surface_,
CopyBufferDamage(_, displayed->texture, _, _))
@@ -641,22 +643,22 @@ TEST_F(BufferQueueMockedContextTest, RecreateBuffers) {
CopyBufferDamage(_, in_flight->texture, _, _))
.Times(1);
- EXPECT_CALL(*context_, destroyImageCHROMIUM(displayed->image))
+ EXPECT_CALL(*context_, DestroyImageCHROMIUM(displayed->image))
.Times(1)
.After(copy1);
- EXPECT_CALL(*context_, destroyImageCHROMIUM(current->image))
+ EXPECT_CALL(*context_, DestroyImageCHROMIUM(current->image))
.Times(1)
.After(copy2);
- EXPECT_CALL(*context_, destroyImageCHROMIUM(in_flight->image))
+ EXPECT_CALL(*context_, DestroyImageCHROMIUM(in_flight->image))
.Times(1)
.After(copy3);
- EXPECT_CALL(*context_, destroyImageCHROMIUM(available->image)).Times(1);
+ EXPECT_CALL(*context_, DestroyImageCHROMIUM(available->image)).Times(1);
// After copying, we expect the framebuffer binding to be updated.
- EXPECT_CALL(*context_, bindFramebuffer(_, _))
+ EXPECT_CALL(*context_, BindFramebuffer(_, _))
.After(copy1)
.After(copy2)
.After(copy3);
- EXPECT_CALL(*context_, framebufferTexture2D(_, _, _, _, _))
+ EXPECT_CALL(*context_, FramebufferTexture2D(_, _, _, _, _))
.After(copy1)
.After(copy2)
.After(copy3);
diff --git a/chromium/components/viz/service/display_embedder/display_provider.h b/chromium/components/viz/service/display_embedder/display_provider.h
index 12fa6146fef..11cc9ef5353 100644
--- a/chromium/components/viz/service/display_embedder/display_provider.h
+++ b/chromium/components/viz/service/display_embedder/display_provider.h
@@ -13,7 +13,7 @@
namespace viz {
class Display;
-class ExternalBeginFrameControllerImpl;
+class ExternalBeginFrameSource;
class FrameSinkId;
class RendererSettings;
class SyntheticBeginFrameSource;
@@ -23,17 +23,22 @@ class DisplayProvider {
public:
virtual ~DisplayProvider() {}
- // Creates a new Display for |surface_handle| with |frame_sink_id|. Will
- // also create BeginFrameSource and return it in |begin_frame_source|.
- // Will return null if creating a Display failed.
+ // Creates a new Display for |surface_handle| with |frame_sink_id|. One of
+ // |external_begin_frame_source| or |synthetic_begin_frame_source| should be
+ // non-null. If creating a Display fails this function will return null.
virtual std::unique_ptr<Display> CreateDisplay(
const FrameSinkId& frame_sink_id,
gpu::SurfaceHandle surface_handle,
bool gpu_compositing,
mojom::DisplayClient* display_client,
- ExternalBeginFrameControllerImpl* external_begin_frame_controller,
+ ExternalBeginFrameSource* external_begin_frame_source,
+ SyntheticBeginFrameSource* synthetic_begin_frame_source,
const RendererSettings& renderer_settings,
- std::unique_ptr<SyntheticBeginFrameSource>* out_begin_frame_source) = 0;
+ bool send_swap_size_notifications) = 0;
+
+ // Returns an ID that changes on each GPU process restart. This ID can be used
+ // for creating unique BeginFrameSource ids.
+ virtual uint32_t GetRestartId() const = 0;
};
} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/gl_output_surface.cc b/chromium/components/viz/service/display_embedder/gl_output_surface.cc
index 55fc38aabac..9ef3fbc3db9 100644
--- a/chromium/components/viz/service/display_embedder/gl_output_surface.cc
+++ b/chromium/components/viz/service/display_embedder/gl_output_surface.cc
@@ -34,6 +34,11 @@ GLOutputSurface::GLOutputSurface(
context_provider->ContextCapabilities().flips_vertically;
capabilities_.supports_stencil =
context_provider->ContextCapabilities().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_.max_frames_pending =
+ context_provider->ContextCapabilities().num_surface_buffers - 1;
context_provider->SetUpdateVSyncParametersCallback(
base::BindRepeating(&GLOutputSurface::OnVSyncParametersUpdated,
weak_ptr_factory_.GetWeakPtr()));
@@ -91,12 +96,9 @@ void GLOutputSurface::SwapBuffers(OutputSurfaceFrame frame) {
if (synthetic_begin_frame_source_)
flags |= gpu::SwapBuffersFlags::kVSyncParams;
- if (LatencyInfoHasSnapshotRequest(frame.latency_info))
- context_provider_->ContextSupport()->SetSnapshotRequested();
-
auto swap_callback = base::BindOnce(
&GLOutputSurface::OnGpuSwapBuffersCompleted,
- weak_ptr_factory_.GetWeakPtr(), std::move(frame.latency_info));
+ weak_ptr_factory_.GetWeakPtr(), std::move(frame.latency_info), size_);
gpu::ContextSupport::PresentationCallback presentation_callback;
if (frame.need_presentation_feedback) {
flags |= gpu::SwapBuffersFlags::kPresentationFeedback;
@@ -106,9 +108,8 @@ void GLOutputSurface::SwapBuffers(OutputSurfaceFrame frame) {
set_draw_rectangle_for_frame_ = false;
if (frame.sub_buffer_rect) {
- context_provider_->ContextSupport()->PartialSwapBuffers(
- *frame.sub_buffer_rect, flags, std::move(swap_callback),
- std::move(presentation_callback));
+ HandlePartialSwap(*frame.sub_buffer_rect, flags, std::move(swap_callback),
+ std::move(presentation_callback));
} else {
context_provider_->ContextSupport()->Swap(flags, std::move(swap_callback),
std::move(presentation_callback));
@@ -149,8 +150,19 @@ void GLOutputSurface::DidReceiveSwapBuffersAck(gfx::SwapResult result) {
client_->DidReceiveSwapBuffersAck();
}
+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,
+ const gfx::Size& pixel_size,
const gpu::SwapBuffersCompleteParams& params) {
if (!params.texture_in_use_responses.empty())
client_->DidReceiveTextureInUseResponses(params.texture_in_use_responses);
@@ -161,6 +173,9 @@ void GLOutputSurface::OnGpuSwapBuffersCompleted(
UpdateLatencyInfoOnSwap(params.swap_response, &latency_info);
latency_tracker_.OnGpuSwapBuffersCompleted(latency_info);
client_->DidFinishLatencyInfo(latency_info);
+
+ if (needs_swap_size_notifications_)
+ client_->DidSwapWithSize(pixel_size);
}
void GLOutputSurface::OnVSyncParametersUpdated(base::TimeTicks timebase,
@@ -197,4 +212,9 @@ unsigned GLOutputSurface::UpdateGpuFence() {
return gpu_fence_id_;
}
+void GLOutputSurface::SetNeedsSwapSizeNotifications(
+ bool needs_swap_size_notifications) {
+ needs_swap_size_notifications_ = needs_swap_size_notifications;
+}
+
} // 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
index 06f87f581c3..3a493ee3baf 100644
--- a/chromium/components/viz/service/display_embedder/gl_output_surface.h
+++ b/chromium/components/viz/service/display_embedder/gl_output_surface.h
@@ -9,6 +9,7 @@
#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 {
@@ -46,6 +47,8 @@ class GLOutputSurface : public OutputSurface {
gpu::VulkanSurface* GetVulkanSurface() override;
#endif
unsigned UpdateGpuFence() override;
+ void SetNeedsSwapSizeNotifications(
+ bool needs_swap_size_notifications) override;
protected:
OutputSurfaceClient* client() const { return client_; }
@@ -53,9 +56,19 @@ class GLOutputSurface : public OutputSurface {
// Called when a swap completion is signaled from ImageTransportSurface.
virtual void DidReceiveSwapBuffersAck(gfx::SwapResult result);
+ // 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,
+ const gfx::Size& pixel_size,
const gpu::SwapBuffersCompleteParams& params);
void OnVSyncParametersUpdated(base::TimeTicks timebase,
base::TimeDelta interval);
@@ -71,6 +84,8 @@ class GLOutputSurface : public OutputSurface {
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_;
};
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
new file mode 100644
index 00000000000..c5bd5a8c545
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/gl_output_surface_android.cc
@@ -0,0 +1,35 @@
+// 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"
+
+#include "components/viz/service/display_embedder/compositor_overlay_candidate_validator_android.h"
+
+namespace viz {
+
+GLOutputSurfaceAndroid::GLOutputSurfaceAndroid(
+ scoped_refptr<VizProcessContextProvider> context_provider,
+ SyntheticBeginFrameSource* synthetic_begin_frame_source)
+ : GLOutputSurface(context_provider, synthetic_begin_frame_source),
+ overlay_candidate_validator_(
+ std::make_unique<CompositorOverlayCandidateValidatorAndroid>()) {}
+
+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));
+}
+
+OverlayCandidateValidator*
+GLOutputSurfaceAndroid::GetOverlayCandidateValidator() const {
+ return overlay_candidate_validator_.get();
+}
+
+} // 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
new file mode 100644
index 00000000000..e10c4630179
--- /dev/null
+++ b/chromium/components/viz/service/display_embedder/gl_output_surface_android.h
@@ -0,0 +1,38 @@
+// 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 <memory>
+
+#include "components/viz/service/display_embedder/gl_output_surface.h"
+
+namespace viz {
+class OverlayCandidateValidator;
+
+class GLOutputSurfaceAndroid : public GLOutputSurface {
+ public:
+ GLOutputSurfaceAndroid(
+ scoped_refptr<VizProcessContextProvider> context_provider,
+ SyntheticBeginFrameSource* synthetic_begin_frame_source);
+ ~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;
+ OverlayCandidateValidator* GetOverlayCandidateValidator() const override;
+
+ private:
+ std::unique_ptr<OverlayCandidateValidator> overlay_candidate_validator_;
+
+ DISALLOW_COPY_AND_ASSIGN(GLOutputSurfaceAndroid);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_ANDROID_H_
diff --git a/chromium/components/viz/service/display_embedder/gpu_display_provider.cc b/chromium/components/viz/service/display_embedder/gpu_display_provider.cc
index 3da085bc5df..a482df768a4 100644
--- a/chromium/components/viz/service/display_embedder/gpu_display_provider.cc
+++ b/chromium/components/viz/service/display_embedder/gpu_display_provider.cc
@@ -14,24 +14,28 @@
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/service/display/display.h"
#include "components/viz/service/display/display_scheduler.h"
-#include "components/viz/service/display_embedder/external_begin_frame_controller_impl.h"
#include "components/viz/service/display_embedder/gl_output_surface.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_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/ipc/command_buffer_task_executor.h"
#include "gpu/ipc/common/surface_handle.h"
-#include "gpu/ipc/service/gpu_channel_manager.h"
#include "gpu/ipc/service/gpu_channel_manager_delegate.h"
-#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
#include "ui/base/ui_base_switches.h"
#if defined(OS_WIN)
#include "components/viz/service/display_embedder/gl_output_surface_win.h"
#include "components/viz/service/display_embedder/software_output_device_win.h"
+#include "ui/gfx/win/rendering_window_manager.h"
+#endif
+
+#if defined(OS_ANDROID)
+#include "components/viz/service/display_embedder/gl_output_surface_android.h"
#endif
#if defined(OS_MACOSX)
@@ -53,38 +57,44 @@
#include "ui/ozone/public/surface_ozone_canvas.h"
#endif
-namespace {
-
-gpu::ImageFactory* GetImageFactory(gpu::GpuChannelManager* channel_manager) {
- auto* buffer_factory = channel_manager->gpu_memory_buffer_factory();
- return buffer_factory ? buffer_factory->AsImageFactory() : nullptr;
-}
-
-} // namespace
-
namespace viz {
GpuDisplayProvider::GpuDisplayProvider(
uint32_t restart_id,
GpuServiceImpl* gpu_service_impl,
- scoped_refptr<gpu::InProcessCommandBuffer::Service> gpu_service,
- gpu::GpuChannelManager* gpu_channel_manager,
+ scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor,
+ gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
+ std::unique_ptr<gpu::GpuMemoryBufferManager> gpu_memory_buffer_manager,
+ gpu::ImageFactory* image_factory,
+ ServerSharedBitmapManager* server_shared_bitmap_manager,
bool headless,
bool wait_for_all_pipeline_stages_before_draw)
: restart_id_(restart_id),
gpu_service_impl_(gpu_service_impl),
- gpu_service_(std::move(gpu_service)),
- gpu_channel_manager_delegate_(gpu_channel_manager->delegate()),
- gpu_memory_buffer_manager_(
- std::make_unique<InProcessGpuMemoryBufferManager>(
- gpu_channel_manager)),
- image_factory_(GetImageFactory(gpu_channel_manager)),
+ task_executor_(std::move(task_executor)),
+ gpu_channel_manager_delegate_(gpu_channel_manager_delegate),
+ gpu_memory_buffer_manager_(std::move(gpu_memory_buffer_manager)),
+ image_factory_(image_factory),
+ server_shared_bitmap_manager_(server_shared_bitmap_manager),
task_runner_(base::ThreadTaskRunnerHandle::Get()),
headless_(headless),
wait_for_all_pipeline_stages_before_draw_(
- wait_for_all_pipeline_stages_before_draw) {
- DCHECK_NE(restart_id_, BeginFrameSource::kNotRestartableId);
-}
+ wait_for_all_pipeline_stages_before_draw) {}
+
+GpuDisplayProvider::GpuDisplayProvider(
+ uint32_t restart_id,
+ ServerSharedBitmapManager* server_shared_bitmap_manager,
+ bool headless,
+ bool wait_for_all_pipeline_stages_before_draw)
+ : GpuDisplayProvider(restart_id,
+ /*gpu_service_impl=*/nullptr,
+ /*task_executor=*/nullptr,
+ /*gpu_channel_manager_delegate=*/nullptr,
+ /*gpu_memory_buffer_manager=*/nullptr,
+ /*image_factory=*/nullptr,
+ server_shared_bitmap_manager,
+ headless,
+ wait_for_all_pipeline_stages_before_draw) {}
GpuDisplayProvider::~GpuDisplayProvider() = default;
@@ -93,20 +103,14 @@ std::unique_ptr<Display> GpuDisplayProvider::CreateDisplay(
gpu::SurfaceHandle surface_handle,
bool gpu_compositing,
mojom::DisplayClient* display_client,
- ExternalBeginFrameControllerImpl* external_begin_frame_controller,
+ ExternalBeginFrameSource* external_begin_frame_source,
+ SyntheticBeginFrameSource* synthetic_begin_frame_source,
const RendererSettings& renderer_settings,
- std::unique_ptr<SyntheticBeginFrameSource>* out_begin_frame_source) {
- BeginFrameSource* display_begin_frame_source = nullptr;
- std::unique_ptr<DelayBasedBeginFrameSource> synthetic_begin_frame_source;
- if (external_begin_frame_controller) {
- display_begin_frame_source =
- external_begin_frame_controller->begin_frame_source();
- } else {
- synthetic_begin_frame_source = std::make_unique<DelayBasedBeginFrameSource>(
- std::make_unique<DelayBasedTimeSource>(task_runner_.get()),
- restart_id_);
- display_begin_frame_source = synthetic_begin_frame_source.get();
- }
+ bool send_swap_size_notifications) {
+ BeginFrameSource* begin_frame_source =
+ synthetic_begin_frame_source
+ ? static_cast<BeginFrameSource*>(synthetic_begin_frame_source)
+ : static_cast<BeginFrameSource*>(external_begin_frame_source);
// TODO(penghuang): Merge two output surfaces into one when GLRenderer and
// software compositor is removed.
@@ -116,55 +120,46 @@ std::unique_ptr<Display> GpuDisplayProvider::CreateDisplay(
if (!gpu_compositing) {
output_surface = std::make_unique<SoftwareOutputSurface>(
CreateSoftwareOutputDeviceForPlatform(surface_handle, display_client));
- } else if (renderer_settings.use_skia_renderer &&
- renderer_settings.use_skia_deferred_display_list) {
+ } else if (renderer_settings.use_skia_deferred_display_list) {
#if defined(OS_MACOSX) || defined(OS_WIN)
// TODO(penghuang): Support DDL for all platforms.
NOTIMPLEMENTED();
- // Workaround compile error: private field 'gpu_service_impl_' is not used.
- ALLOW_UNUSED_LOCAL(gpu_service_impl_);
+ return nullptr;
#else
- // Create an offscreen context_provider for SkiaOutputSurfaceImpl, because
- // SkiaRenderer still needs it to draw RenderPass into a texture.
- // TODO(penghuang): Remove this context_provider. https://crbug.com/825901
- auto context_provider = base::MakeRefCounted<VizProcessContextProvider>(
- gpu_service_, gpu::kNullSurfaceHandle, gpu_memory_buffer_manager_.get(),
- image_factory_, gpu_channel_manager_delegate_,
- gpu::SharedMemoryLimits());
- auto result = context_provider->BindToCurrentThread();
- CHECK_EQ(result, gpu::ContextResult::kSuccess);
output_surface = std::make_unique<SkiaOutputSurfaceImpl>(
- gpu_service_impl_, surface_handle, std::move(context_provider),
- synthetic_begin_frame_source.get());
+ gpu_service_impl_, surface_handle, synthetic_begin_frame_source);
skia_output_surface = static_cast<SkiaOutputSurface*>(output_surface.get());
#endif
} else {
+ DCHECK(task_executor_);
+
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) {
context_provider = base::MakeRefCounted<VizProcessContextProvider>(
- gpu_service_, surface_handle, gpu_memory_buffer_manager_.get(),
+ task_executor_, surface_handle, gpu_memory_buffer_manager_.get(),
image_factory_, gpu_channel_manager_delegate_,
gpu::SharedMemoryLimits());
context_result = context_provider->BindToCurrentThread();
- // TODO(crbug.com/819474): Don't crash here, instead fallback to software
- // compositing for fatal failures.
- CHECK_NE(context_result, gpu::ContextResult::kFatalFailure);
+ if (context_result == gpu::ContextResult::kFatalFailure) {
+ gpu_service_impl_->DisableGpuCompositing();
+ return nullptr;
+ }
}
if (context_provider->ContextCapabilities().surfaceless) {
#if defined(USE_OZONE)
output_surface = std::make_unique<GLOutputSurfaceOzone>(
std::move(context_provider), surface_handle,
- synthetic_begin_frame_source.get(), gpu_memory_buffer_manager_.get(),
+ synthetic_begin_frame_source, gpu_memory_buffer_manager_.get(),
GL_TEXTURE_2D, GL_BGRA_EXT);
#elif defined(OS_MACOSX)
output_surface = std::make_unique<GLOutputSurfaceMac>(
std::move(context_provider), surface_handle,
- synthetic_begin_frame_source.get(), gpu_memory_buffer_manager_.get(),
+ synthetic_begin_frame_source, gpu_memory_buffer_manager_.get(),
renderer_settings.allow_overlays);
#else
NOTREACHED();
@@ -175,31 +170,38 @@ std::unique_ptr<Display> GpuDisplayProvider::CreateDisplay(
const bool use_overlays =
capabilities.dc_layers && capabilities.use_dc_overlays_for_video;
output_surface = std::make_unique<GLOutputSurfaceWin>(
- std::move(context_provider), synthetic_begin_frame_source.get(),
+ std::move(context_provider), synthetic_begin_frame_source,
use_overlays);
+#elif defined(OS_ANDROID)
+ output_surface = std::make_unique<GLOutputSurfaceAndroid>(
+ std::move(context_provider), synthetic_begin_frame_source);
#else
output_surface = std::make_unique<GLOutputSurface>(
- std::move(context_provider), synthetic_begin_frame_source.get());
+ std::move(context_provider), synthetic_begin_frame_source);
#endif
}
}
+ // If we need swap size notifications tell the output surface now.
+ output_surface->SetNeedsSwapSizeNotifications(send_swap_size_notifications);
+
int max_frames_pending = output_surface->capabilities().max_frames_pending;
DCHECK_GT(max_frames_pending, 0);
auto scheduler = std::make_unique<DisplayScheduler>(
- display_begin_frame_source, task_runner_.get(), max_frames_pending,
+ begin_frame_source, task_runner_.get(), max_frames_pending,
wait_for_all_pipeline_stages_before_draw_);
- // The ownership of the BeginFrameSource is transferred to the caller.
- *out_begin_frame_source = std::move(synthetic_begin_frame_source);
-
return std::make_unique<Display>(
- ServerSharedBitmapManager::current(), renderer_settings, frame_sink_id,
+ server_shared_bitmap_manager_, renderer_settings, frame_sink_id,
std::move(output_surface), std::move(scheduler), task_runner_,
skia_output_surface);
}
+uint32_t GpuDisplayProvider::GetRestartId() const {
+ return restart_id_;
+}
+
std::unique_ptr<SoftwareOutputDevice>
GpuDisplayProvider::CreateSoftwareOutputDeviceForPlatform(
gpu::SurfaceHandle surface_handle,
@@ -208,8 +210,26 @@ GpuDisplayProvider::CreateSoftwareOutputDeviceForPlatform(
return std::make_unique<SoftwareOutputDevice>();
#if defined(OS_WIN)
- return CreateSoftwareOutputDeviceWinGpu(
- surface_handle, &output_device_backing_, display_client);
+ HWND child_hwnd;
+ auto device = CreateSoftwareOutputDeviceWinGpu(
+ surface_handle, &output_device_backing_, display_client, &child_hwnd);
+
+ // If |child_hwnd| isn't null then a new child HWND was created.
+ if (child_hwnd) {
+ if (gpu_channel_manager_delegate_) {
+ // Send an IPC to browser process for SetParent().
+ gpu_channel_manager_delegate_->SendCreatedChildWindow(surface_handle,
+ child_hwnd);
+ } else {
+ // We are already in the browser process.
+ if (!gfx::RenderingWindowManager::GetInstance()->RegisterChild(
+ surface_handle, child_hwnd)) {
+ LOG(ERROR) << "Bad parenting request.";
+ }
+ }
+ }
+
+ return device;
#elif defined(OS_MACOSX)
return std::make_unique<SoftwareOutputDeviceMac>(task_runner_);
#elif defined(OS_ANDROID)
diff --git a/chromium/components/viz/service/display_embedder/gpu_display_provider.h b/chromium/components/viz/service/display_embedder/gpu_display_provider.h
index f714ee18a64..e28bca541d6 100644
--- a/chromium/components/viz/service/display_embedder/gpu_display_provider.h
+++ b/chromium/components/viz/service/display_embedder/gpu_display_provider.h
@@ -23,15 +23,17 @@
#endif
namespace gpu {
-class GpuChannelManager;
+class CommandBufferTaskExecutor;
class GpuChannelManagerDelegate;
+class GpuMemoryBufferManager;
class ImageFactory;
} // namespace gpu
namespace viz {
class Display;
-class ExternalBeginFrameControllerImpl;
+class ExternalBeginFrameSource;
class GpuServiceImpl;
+class ServerSharedBitmapManager;
class SoftwareOutputDevice;
// In-process implementation of DisplayProvider.
@@ -40,10 +42,18 @@ class VIZ_SERVICE_EXPORT GpuDisplayProvider : public DisplayProvider {
GpuDisplayProvider(
uint32_t restart_id,
GpuServiceImpl* gpu_service_impl,
- scoped_refptr<gpu::InProcessCommandBuffer::Service> gpu_service,
- gpu::GpuChannelManager* gpu_channel_manager,
+ scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor,
+ gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
+ std::unique_ptr<gpu::GpuMemoryBufferManager> gpu_memory_buffer_manager,
+ gpu::ImageFactory* image_factory,
+ ServerSharedBitmapManager* server_shared_bitmap_manager,
bool headless,
bool wait_for_all_pipeline_stages_before_draw);
+ // Software compositing only.
+ GpuDisplayProvider(uint32_t restart_id,
+ ServerSharedBitmapManager* server_shared_bitmap_manager,
+ bool headless,
+ bool wait_for_all_pipeline_stages_before_draw);
~GpuDisplayProvider() override;
// DisplayProvider implementation.
@@ -52,10 +62,11 @@ class VIZ_SERVICE_EXPORT GpuDisplayProvider : public DisplayProvider {
gpu::SurfaceHandle surface_handle,
bool gpu_compositing,
mojom::DisplayClient* display_client,
- ExternalBeginFrameControllerImpl* external_begin_frame_controller,
+ ExternalBeginFrameSource* external_begin_frame_source,
+ SyntheticBeginFrameSource* synthetic_begin_frame_source,
const RendererSettings& renderer_settings,
- std::unique_ptr<SyntheticBeginFrameSource>* out_begin_frame_source)
- override;
+ bool send_swap_size_notifications) override;
+ uint32_t GetRestartId() const override;
private:
std::unique_ptr<SoftwareOutputDevice> CreateSoftwareOutputDeviceForPlatform(
@@ -64,10 +75,11 @@ class VIZ_SERVICE_EXPORT GpuDisplayProvider : public DisplayProvider {
const uint32_t restart_id_;
GpuServiceImpl* const gpu_service_impl_;
- scoped_refptr<gpu::InProcessCommandBuffer::Service> gpu_service_;
+ scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor_;
gpu::GpuChannelManagerDelegate* const gpu_channel_manager_delegate_;
std::unique_ptr<gpu::GpuMemoryBufferManager> gpu_memory_buffer_manager_;
gpu::ImageFactory* const image_factory_;
+ ServerSharedBitmapManager* const server_shared_bitmap_manager_;
#if defined(OS_WIN)
// Used for software compositing output on Windows.
diff --git a/chromium/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc b/chromium/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc
index d9d73be6abf..37fa4f2c334 100644
--- a/chromium/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc
+++ b/chromium/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc
@@ -4,6 +4,7 @@
#include "components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h"
+#include "base/bind.h"
#include "gpu/ipc/common/gpu_memory_buffer_impl.h"
#include "gpu/ipc/common/gpu_memory_buffer_support.h"
#include "gpu/ipc/in_process_command_buffer.h"
@@ -15,7 +16,7 @@ namespace viz {
InProcessGpuMemoryBufferManager::InProcessGpuMemoryBufferManager(
gpu::GpuChannelManager* channel_manager)
: gpu_memory_buffer_support_(new gpu::GpuMemoryBufferSupport()),
- client_id_(gpu::InProcessCommandBuffer::kGpuMemoryBufferClientId),
+ client_id_(gpu::InProcessCommandBuffer::kGpuClientId),
channel_manager_(channel_manager),
weak_factory_(this) {
weak_ptr_ = weak_factory_.GetWeakPtr();
@@ -34,7 +35,7 @@ InProcessGpuMemoryBufferManager::CreateGpuMemoryBuffer(
channel_manager_->gpu_memory_buffer_factory()->CreateGpuMemoryBuffer(
id, size, format, usage, client_id_, surface_handle);
return gpu_memory_buffer_support_->CreateGpuMemoryBufferImplFromHandle(
- buffer_handle, size, format, usage,
+ std::move(buffer_handle), size, format, usage,
base::Bind(&InProcessGpuMemoryBufferManager::DestroyGpuMemoryBuffer,
weak_ptr_, id, client_id_));
}
diff --git a/chromium/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h b/chromium/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h
index a68a45205b1..e14751d6ead 100644
--- a/chromium/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h
+++ b/chromium/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h
@@ -6,6 +6,7 @@
#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_IN_PROCESS_GPU_MEMORY_BUFFER_MANAGER_H_
#include "base/memory/weak_ptr.h"
+#include "components/viz/service/viz_service_export.h"
#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
namespace gpu {
@@ -15,7 +16,8 @@ class GpuMemoryBufferSupport;
namespace viz {
-class InProcessGpuMemoryBufferManager : public gpu::GpuMemoryBufferManager {
+class VIZ_SERVICE_EXPORT InProcessGpuMemoryBufferManager
+ : public gpu::GpuMemoryBufferManager {
public:
explicit InProcessGpuMemoryBufferManager(
gpu::GpuChannelManager* channel_manager);
diff --git a/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager.cc b/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager.cc
index 9d84f91109a..28125abfd1b 100644
--- a/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager.cc
+++ b/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager.cc
@@ -20,61 +20,51 @@
namespace viz {
-class BitmapData : public base::RefCountedThreadSafe<BitmapData> {
+class BitmapData : public base::RefCounted<BitmapData> {
public:
explicit BitmapData(size_t buffer_size) : buffer_size(buffer_size) {}
std::unique_ptr<base::SharedMemory> memory;
size_t buffer_size;
private:
- friend class base::RefCountedThreadSafe<BitmapData>;
+ friend class base::RefCounted<BitmapData>;
~BitmapData() {}
DISALLOW_COPY_AND_ASSIGN(BitmapData);
};
namespace {
+// Holds a reference on the BitmapData so that the SharedMemory can outlive the
+// SharedBitmapId registration as long as this SharedBitmap object is held
+// alive.
class ServerSharedBitmap : public SharedBitmap {
public:
- ServerSharedBitmap(scoped_refptr<BitmapData> bitmap_data,
- const SharedBitmapId& id)
- : SharedBitmap(static_cast<uint8_t*>(bitmap_data->memory->memory()),
- id,
- 0 /* sequence_number */),
+ explicit ServerSharedBitmap(scoped_refptr<BitmapData> bitmap_data)
+ : SharedBitmap(static_cast<uint8_t*>(bitmap_data->memory->memory())),
bitmap_data_(std::move(bitmap_data)) {}
~ServerSharedBitmap() override {
}
- // SharedBitmap implementation.
- base::UnguessableToken GetCrossProcessGUID() const override {
- return bitmap_data_->memory->mapped_id();
- }
-
private:
scoped_refptr<BitmapData> bitmap_data_;
};
} // namespace
-base::LazyInstance<ServerSharedBitmapManager>::DestructorAtExit
- g_shared_memory_manager = LAZY_INSTANCE_INITIALIZER;
-
ServerSharedBitmapManager::ServerSharedBitmapManager() = default;
ServerSharedBitmapManager::~ServerSharedBitmapManager() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(handle_map_.empty());
}
-ServerSharedBitmapManager* ServerSharedBitmapManager::current() {
- return g_shared_memory_manager.Pointer();
-}
-
std::unique_ptr<SharedBitmap> ServerSharedBitmapManager::GetSharedBitmapFromId(
const gfx::Size& size,
ResourceFormat format,
const SharedBitmapId& id) {
- base::AutoLock lock(lock_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
auto it = handle_map_.find(id);
if (it == handle_map_.end())
return nullptr;
@@ -90,12 +80,24 @@ std::unique_ptr<SharedBitmap> ServerSharedBitmapManager::GetSharedBitmapFromId(
return nullptr;
}
- return std::make_unique<ServerSharedBitmap>(data, id);
+ return std::make_unique<ServerSharedBitmap>(data);
+}
+
+base::UnguessableToken
+ServerSharedBitmapManager::GetSharedBitmapTracingGUIDFromId(
+ const SharedBitmapId& id) {
+ auto it = handle_map_.find(id);
+ if (it == handle_map_.end())
+ return {};
+ BitmapData* data = it->second.get();
+ return data->memory->mapped_id();
}
bool ServerSharedBitmapManager::ChildAllocatedSharedBitmap(
mojo::ScopedSharedBufferHandle buffer,
const SharedBitmapId& id) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
base::SharedMemoryHandle memory_handle;
size_t buffer_size;
MojoResult result = mojo::UnwrapSharedMemoryHandle(
@@ -110,24 +112,6 @@ bool ServerSharedBitmapManager::ChildAllocatedSharedBitmap(
data->memory->Map(data->buffer_size);
data->memory->Close();
- base::AutoLock lock(lock_);
- if (handle_map_.find(id) != handle_map_.end())
- return false;
- handle_map_[id] = std::move(data);
- return true;
-}
-
-bool ServerSharedBitmapManager::ChildAllocatedSharedBitmapForTest(
- size_t buffer_size,
- const base::SharedMemoryHandle& memory_handle,
- const SharedBitmapId& id) {
- auto data = base::MakeRefCounted<BitmapData>(buffer_size);
- data->memory = std::make_unique<base::SharedMemory>(memory_handle, false);
- if (!data->memory->Map(data->buffer_size))
- return false;
- data->memory->Close();
-
- base::AutoLock lock(lock_);
if (handle_map_.find(id) != handle_map_.end())
return false;
handle_map_[id] = std::move(data);
@@ -136,14 +120,14 @@ bool ServerSharedBitmapManager::ChildAllocatedSharedBitmapForTest(
void ServerSharedBitmapManager::ChildDeletedSharedBitmap(
const SharedBitmapId& id) {
- base::AutoLock lock(lock_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
handle_map_.erase(id);
}
bool ServerSharedBitmapManager::OnMemoryDump(
const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) {
- base::AutoLock lock(lock_);
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (const auto& pair : handle_map_) {
const SharedBitmapId& id = pair.first;
@@ -160,34 +144,15 @@ bool ServerSharedBitmapManager::OnMemoryDump(
base::trace_event::MemoryAllocatorDump::kUnitsBytes,
data->buffer_size);
- if (data->memory) {
- // Resources from a client have shared memory, and we use the guid from
- // that.
- base::UnguessableToken shared_memory_guid = data->memory->mapped_id();
- DCHECK(!shared_memory_guid.is_empty());
- pmd->CreateSharedMemoryOwnershipEdge(dump->guid(), shared_memory_guid,
- 0 /* importance*/);
- } else {
- // Otherwise, resources were allocated locally for in-process use, and
- // there is no shared memory. Instead make up a GUID for them.
- auto guid = GetSharedBitmapGUIDForTracing(id);
- pmd->CreateSharedGlobalAllocatorDump(guid);
- pmd->AddOwnershipEdge(dump->guid(), guid);
- }
+ // This GUID is the same returned by GetSharedBitmapTracingGUIDFromId() so
+ // other components use a consistent GUID for a given SharedBitmapId.
+ base::UnguessableToken shared_memory_guid = data->memory->mapped_id();
+ DCHECK(!shared_memory_guid.is_empty());
+ pmd->CreateSharedMemoryOwnershipEdge(dump->guid(), shared_memory_guid,
+ 0 /* importance*/);
}
return true;
}
-size_t ServerSharedBitmapManager::AllocatedBitmapCount() const {
- base::AutoLock lock(lock_);
- return handle_map_.size();
-}
-
-void ServerSharedBitmapManager::FreeSharedMemoryFromMap(
- const SharedBitmapId& id) {
- base::AutoLock lock(lock_);
- handle_map_.erase(id);
-}
-
} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager.h b/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager.h
index 57810735850..21a699fff62 100644
--- a/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager.h
+++ b/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager.h
@@ -11,21 +11,19 @@
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/memory/shared_memory.h"
-#include "base/synchronization/lock.h"
#include "base/trace_event/memory_dump_provider.h"
#include "components/viz/common/resources/resource_format_utils.h"
-#include "components/viz/common/resources/shared_bitmap_manager.h"
+#include "components/viz/service/display/shared_bitmap_manager.h"
#include "components/viz/service/viz_service_export.h"
namespace viz {
class BitmapData;
// A SharedBitmapManager implementation that lives in-process with the
-// display compositor. It returns SharedBitmaps that can be transported
-// over Mojo/IPC to the display compositor, but which are backed by
-// non-shared memory. This is a cheaper implementation by using
-// malloc/free, but can only be used in the same process as the display
-// compositor.
+// display compositor. It manages mappings from SharedBitmapId to
+// SharedMemory segments. While the returned SharedBitmap is kept alive
+// for a given SharedBitmapId, the backing pixels are guaranteed to remain
+// valid.
class VIZ_SERVICE_EXPORT ServerSharedBitmapManager
: public SharedBitmapManager,
public base::trace_event::MemoryDumpProvider {
@@ -33,13 +31,13 @@ class VIZ_SERVICE_EXPORT ServerSharedBitmapManager
ServerSharedBitmapManager();
~ServerSharedBitmapManager() override;
- static ServerSharedBitmapManager* current();
-
// SharedBitmapManager implementation.
std::unique_ptr<SharedBitmap> GetSharedBitmapFromId(
const gfx::Size& size,
ResourceFormat format,
const SharedBitmapId& id) override;
+ base::UnguessableToken GetSharedBitmapTracingGUIDFromId(
+ const SharedBitmapId& id) override;
bool ChildAllocatedSharedBitmap(mojo::ScopedSharedBufferHandle buffer,
const SharedBitmapId& id) override;
void ChildDeletedSharedBitmap(const SharedBitmapId& id) override;
@@ -48,15 +46,8 @@ class VIZ_SERVICE_EXPORT ServerSharedBitmapManager
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override;
- size_t AllocatedBitmapCount() const;
- void FreeSharedMemoryFromMap(const SharedBitmapId& id);
-
- bool ChildAllocatedSharedBitmapForTest(size_t buffer_size,
- const base::SharedMemoryHandle& handle,
- const SharedBitmapId& id);
-
private:
- mutable base::Lock lock_;
+ SEQUENCE_CHECKER(sequence_checker_);
std::unordered_map<SharedBitmapId,
scoped_refptr<BitmapData>,
diff --git a/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager_unittest.cc b/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager_unittest.cc
index b5923739380..f0899894410 100644
--- a/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager_unittest.cc
+++ b/chromium/components/viz/service/display_embedder/server_shared_bitmap_manager_unittest.cc
@@ -44,33 +44,33 @@ TEST_F(ServerSharedBitmapManagerTest, TestCreate) {
std::unique_ptr<SharedBitmap> large_bitmap;
large_bitmap =
manager()->GetSharedBitmapFromId(gfx::Size(1024, 1024), RGBA_8888, id);
- EXPECT_TRUE(large_bitmap.get() == nullptr);
+ EXPECT_FALSE(large_bitmap);
std::unique_ptr<SharedBitmap> very_large_bitmap;
very_large_bitmap = manager()->GetSharedBitmapFromId(
gfx::Size(1, (1 << 30) | 1), RGBA_8888, id);
- EXPECT_TRUE(very_large_bitmap.get() == nullptr);
+ EXPECT_FALSE(very_large_bitmap);
std::unique_ptr<SharedBitmap> negative_size_bitmap;
negative_size_bitmap =
manager()->GetSharedBitmapFromId(gfx::Size(-1, 1024), RGBA_8888, id);
- EXPECT_TRUE(negative_size_bitmap.get() == nullptr);
+ EXPECT_FALSE(negative_size_bitmap);
SharedBitmapId id2 = SharedBitmap::GenerateId();
std::unique_ptr<SharedBitmap> invalid_bitmap;
invalid_bitmap =
manager()->GetSharedBitmapFromId(bitmap_size, RGBA_8888, id2);
- EXPECT_TRUE(invalid_bitmap.get() == nullptr);
+ EXPECT_FALSE(invalid_bitmap);
std::unique_ptr<SharedBitmap> shared_bitmap;
shared_bitmap = manager()->GetSharedBitmapFromId(bitmap_size, RGBA_8888, id);
- ASSERT_TRUE(shared_bitmap.get() != nullptr);
+ ASSERT_TRUE(shared_bitmap);
EXPECT_EQ(memcmp(shared_bitmap->pixels(), bitmap->memory(), 4), 0);
std::unique_ptr<SharedBitmap> large_bitmap2;
large_bitmap2 =
manager()->GetSharedBitmapFromId(gfx::Size(1024, 1024), RGBA_8888, id);
- EXPECT_TRUE(large_bitmap2.get() == nullptr);
+ EXPECT_FALSE(large_bitmap2);
std::unique_ptr<SharedBitmap> shared_bitmap2;
shared_bitmap2 = manager()->GetSharedBitmapFromId(bitmap_size, RGBA_8888, id);
@@ -117,7 +117,7 @@ TEST_F(ServerSharedBitmapManagerTest, AddDuplicate) {
std::unique_ptr<SharedBitmap> shared_bitmap;
shared_bitmap = manager()->GetSharedBitmapFromId(bitmap_size, RGBA_8888, id);
- ASSERT_TRUE(shared_bitmap.get() != nullptr);
+ ASSERT_TRUE(shared_bitmap);
EXPECT_EQ(memcmp(shared_bitmap->pixels(), bitmap->memory(), size_in_bytes),
0);
manager()->ChildDeletedSharedBitmap(id);
@@ -141,10 +141,9 @@ TEST_F(ServerSharedBitmapManagerTest, SharedMemoryHandle) {
mojo::UnwrappedSharedMemoryHandleProtection::kReadWrite);
manager()->ChildAllocatedSharedBitmap(std::move(buffer_handle), id);
- std::unique_ptr<SharedBitmap> shared_bitmap;
- shared_bitmap =
- manager()->GetSharedBitmapFromId(gfx::Size(1, 1), RGBA_8888, id);
- EXPECT_EQ(shared_bitmap->GetCrossProcessGUID(), shared_memory_guid);
+ base::UnguessableToken tracing_guid =
+ manager()->GetSharedBitmapTracingGUIDFromId(id);
+ EXPECT_EQ(tracing_guid, shared_memory_guid);
manager()->ChildDeletedSharedBitmap(id);
}
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 ac7b616b261..f9633cfdca3 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
@@ -8,12 +8,12 @@
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
+#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/resources/resource_format_utils.h"
-#include "components/viz/common/resources/resource_metadata.h"
#include "components/viz/service/display/output_surface_client.h"
#include "components/viz/service/display/output_surface_frame.h"
+#include "components/viz/service/display/resource_metadata.h"
#include "components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.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/common/swap_buffers_complete_params.h"
#include "gpu/command_buffer/service/scheduler.h"
@@ -127,10 +127,8 @@ void SkiaOutputSurfaceImpl::PromiseTextureHelper<YUVResourceMetadata>::Init(
SkiaOutputSurfaceImpl::SkiaOutputSurfaceImpl(
GpuServiceImpl* gpu_service,
gpu::SurfaceHandle surface_handle,
- scoped_refptr<VizProcessContextProvider> context_provider,
SyntheticBeginFrameSource* synthetic_begin_frame_source)
- : SkiaOutputSurface(context_provider),
- gpu_service_(gpu_service),
+ : gpu_service_(gpu_service),
surface_handle_(surface_handle),
synthetic_begin_frame_source_(synthetic_begin_frame_source),
weak_ptr_factory_(this) {
@@ -139,7 +137,7 @@ SkiaOutputSurfaceImpl::SkiaOutputSurfaceImpl(
SkiaOutputSurfaceImpl::~SkiaOutputSurfaceImpl() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- recorder_ = nullptr;
+ recorder_.reset();
// Use GPU scheduler to release impl_on_gpu_ on the GPU thread, so all
// scheduled tasks for the impl_on_gpu_ will be executed, before releasing
// it. The GPU thread is the main thread of the viz process. It outlives the
@@ -197,9 +195,9 @@ void SkiaOutputSurfaceImpl::Reshape(const gfx::Size& size,
bool use_stencil) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- recorder_ = nullptr;
+ recorder_.reset();
SkSurfaceCharacterization* characterization = nullptr;
- std::unique_ptr<base::WaitableEvent> event;
+ base::Optional<base::WaitableEvent> event;
if (characterization_.isValid()) {
characterization_ =
characterization_.createResized(size.width(), size.height());
@@ -208,18 +206,18 @@ void SkiaOutputSurfaceImpl::Reshape(const gfx::Size& size,
// TODO(penghuang): avoid blocking compositor thread.
// We don't have a valid surface characterization, so we have to wait
// until reshape is finished on Gpu thread.
- event = std::make_unique<base::WaitableEvent>(
- base::WaitableEvent::ResetPolicy::MANUAL,
- base::WaitableEvent::InitialState::NOT_SIGNALED);
+ event.emplace(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
}
auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
// 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 callback = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::Reshape,
- base::Unretained(impl_on_gpu_.get()), size,
- device_scale_factor, color_space, has_alpha,
- use_stencil, characterization, event.get());
+ auto callback =
+ base::BindOnce(&SkiaOutputSurfaceImplOnGpu::Reshape,
+ base::Unretained(impl_on_gpu_.get()), size,
+ device_scale_factor, color_space, has_alpha, use_stencil,
+ characterization, base::OptionalOrNullptr(event));
gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
sequence_id, std::move(callback), std::vector<gpu::SyncToken>()));
@@ -280,7 +278,7 @@ unsigned SkiaOutputSurfaceImpl::UpdateGpuFence() {
return 0;
}
-SkCanvas* SkiaOutputSurfaceImpl::GetSkCanvasForCurrentFrame() {
+SkCanvas* SkiaOutputSurfaceImpl::BeginPaintCurrentFrame() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(recorder_);
DCHECK_EQ(current_render_pass_id_, 0u);
@@ -288,23 +286,66 @@ SkCanvas* SkiaOutputSurfaceImpl::GetSkCanvasForCurrentFrame() {
return recorder_->getCanvas();
}
+gpu::SyncToken SkiaOutputSurfaceImpl::FinishPaintCurrentFrame() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(recorder_);
+
+ gpu::SyncToken sync_token(gpu::CommandBufferNamespace::VIZ_OUTPUT_SURFACE,
+ impl_on_gpu_->command_buffer_id(),
+ ++sync_fence_release_);
+ sync_token.SetVerifyFlush();
+
+ auto ddl = recorder_->detach();
+ DCHECK(ddl);
+ recorder_.reset();
+ auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
+ // 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 callback =
+ base::BindOnce(&SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame,
+ base::Unretained(impl_on_gpu_.get()), std::move(ddl),
+ std::move(yuv_resource_metadatas_), sync_fence_release_);
+ gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
+ sequence_id, std::move(callback), std::move(resource_sync_tokens_)));
+ return sync_token;
+}
+
sk_sp<SkImage> SkiaOutputSurfaceImpl::MakePromiseSkImage(
ResourceMetadata metadata) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(recorder_);
- // Convert internal format from GLES2 to platform GL.
- const auto* version_info = gpu_service_->context_for_skia()->GetVersionInfo();
- metadata.backend_format = GrBackendFormat::MakeGL(
- gl::GetInternalFormat(version_info,
- *metadata.backend_format.getGLFormat()),
- *metadata.backend_format.getGLTarget());
+ if (!gpu_service_->is_using_vulkan()) {
+ // Convert internal format from GLES2 to platform GL.
+ const auto* version_info = impl_on_gpu_->gl_version_info();
+ metadata.backend_format = GrBackendFormat::MakeGL(
+ gl::GetInternalFormat(version_info,
+ *metadata.backend_format.getGLFormat()),
+ *metadata.backend_format.getGLTarget());
+ } else {
+#if BUILDFLAG(ENABLE_VULKAN)
+ VkFormat format = VK_FORMAT_UNDEFINED;
+ switch (metadata.color_type) {
+ case kRGBA_8888_SkColorType:
+ format = VK_FORMAT_R8G8B8A8_UNORM;
+ break;
+ case kBGRA_8888_SkColorType:
+ format = VK_FORMAT_B8G8R8A8_UNORM;
+ break;
+ default:
+ NOTREACHED();
+ }
+ metadata.backend_format = GrBackendFormat::MakeVk(format);
+#else
+ NOTREACHED();
+#endif
+ }
DCHECK(!metadata.mailbox.IsZero());
resource_sync_tokens_.push_back(metadata.sync_token);
return PromiseTextureHelper<ResourceMetadata>::MakePromiseSkImage(
- this, recorder_.get(), metadata.backend_format, metadata.size,
+ this, &recorder_.value(), metadata.backend_format, metadata.size,
metadata.mip_mapped, metadata.origin, metadata.color_type,
metadata.alpha_type, metadata.color_space, std::move(metadata));
}
@@ -321,40 +362,38 @@ sk_sp<SkImage> SkiaOutputSurfaceImpl::MakePromiseSkImageFromYUV(
// supported by Skia.
YUVResourceMetadata yuv_metadata(std::move(metadatas), yuv_color_space);
- // Convert internal format from GLES2 to platform GL.
- const auto* version_info = gpu_service_->context_for_skia()->GetVersionInfo();
- auto backend_format = GrBackendFormat::MakeGL(
- gl::GetInternalFormat(version_info, GL_BGRA8_EXT), GL_TEXTURE_2D);
+ GrBackendFormat backend_format;
+ if (!gpu_service_->is_using_vulkan()) {
+ // Convert internal format from GLES2 to platform GL.
+ const auto* version_info = impl_on_gpu_->gl_version_info();
+ backend_format = GrBackendFormat::MakeGL(
+ gl::GetInternalFormat(version_info, GL_BGRA8_EXT), GL_TEXTURE_2D);
+ } else {
+#if BUILDFLAG(ENABLE_VULKAN)
+ backend_format = GrBackendFormat::MakeVk(VK_FORMAT_B8G8R8A8_UNORM);
+#else
+ NOTREACHED();
+#endif
+ }
return PromiseTextureHelper<YUVResourceMetadata>::MakePromiseSkImage(
- this, recorder_.get(), backend_format, yuv_metadata.size(),
+ this, &recorder_.value(), backend_format, yuv_metadata.size(),
GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin, kBGRA_8888_SkColorType,
kPremul_SkAlphaType, nullptr /* color_space */, std::move(yuv_metadata));
}
-gpu::SyncToken SkiaOutputSurfaceImpl::SkiaSwapBuffers(
- OutputSurfaceFrame frame) {
+void SkiaOutputSurfaceImpl::SkiaSwapBuffers(OutputSurfaceFrame frame) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(recorder_);
-
- gpu::SyncToken sync_token(gpu::CommandBufferNamespace::VIZ_OUTPUT_SURFACE,
- impl_on_gpu_->command_buffer_id(),
- ++sync_fence_release_);
- sync_token.SetVerifyFlush();
-
- auto ddl = recorder_->detach();
- DCHECK(ddl);
+ DCHECK(!recorder_);
RecreateRecorder();
auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
// 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 callback = base::BindOnce(
- &SkiaOutputSurfaceImplOnGpu::SwapBuffers,
- base::Unretained(impl_on_gpu_.get()), std::move(frame), std::move(ddl),
- std::move(yuv_resource_metadatas_), sync_fence_release_);
+ auto callback =
+ base::BindOnce(&SkiaOutputSurfaceImplOnGpu::SwapBuffers,
+ base::Unretained(impl_on_gpu_.get()), std::move(frame));
gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
- sequence_id, std::move(callback), std::move(resource_sync_tokens_)));
- return sync_token;
+ sequence_id, std::move(callback), std::vector<gpu::SyncToken>()));
}
SkCanvas* SkiaOutputSurfaceImpl::BeginPaintRenderPass(
@@ -363,14 +402,13 @@ SkCanvas* SkiaOutputSurfaceImpl::BeginPaintRenderPass(
ResourceFormat format,
bool mipmap) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(gpu_service_->gr_context());
DCHECK(!current_render_pass_id_);
DCHECK(!offscreen_surface_recorder_);
DCHECK(resource_sync_tokens_.empty());
current_render_pass_id_ = id;
- auto gr_context_thread_safe = gpu_service_->gr_context()->threadSafeProxy();
+ auto gr_context_thread_safe = impl_on_gpu_->GetGrContextThreadSafeProxy();
constexpr uint32_t flags = 0;
// LegacyFontHost will get LCD text and skia figures out what type to use.
SkSurfaceProps surface_props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
@@ -383,23 +421,31 @@ SkCanvas* SkiaOutputSurfaceImpl::BeginPaintRenderPass(
// TODO(penghuang): Figure out how to choose the right size.
constexpr size_t kCacheMaxResourceBytes = 90 * 1024 * 1024;
- const auto* version_info = gpu_service_->context_for_skia()->GetVersionInfo();
- unsigned int texture_storage_format = TextureStorageFormat(format);
- auto backend_format = GrBackendFormat::MakeGL(
- gl::GetInternalFormat(version_info, texture_storage_format),
- GL_TEXTURE_2D);
+
+ GrBackendFormat backend_format;
+ if (!gpu_service_->is_using_vulkan()) {
+ const auto* version_info = impl_on_gpu_->gl_version_info();
+ unsigned int texture_storage_format = TextureStorageFormat(format);
+ backend_format = GrBackendFormat::MakeGL(
+ gl::GetInternalFormat(version_info, texture_storage_format),
+ GL_TEXTURE_2D);
+ } else {
+#if BUILDFLAG(ENABLE_VULKAN)
+ backend_format = GrBackendFormat::MakeVk(VK_FORMAT_B8G8R8A8_UNORM);
+#else
+ NOTREACHED();
+#endif
+ }
auto characterization = gr_context_thread_safe->createCharacterization(
kCacheMaxResourceBytes, image_info, backend_format, msaa_sample_count,
kTopLeft_GrSurfaceOrigin, surface_props, mipmap);
DCHECK(characterization.isValid());
- offscreen_surface_recorder_ =
- std::make_unique<SkDeferredDisplayListRecorder>(characterization);
+ offscreen_surface_recorder_.emplace(characterization);
return offscreen_surface_recorder_->getCanvas();
}
gpu::SyncToken SkiaOutputSurfaceImpl::FinishPaintRenderPass() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(gpu_service_->gr_context());
DCHECK(current_render_pass_id_);
DCHECK(offscreen_surface_recorder_);
@@ -409,7 +455,7 @@ gpu::SyncToken SkiaOutputSurfaceImpl::FinishPaintRenderPass() {
sync_token.SetVerifyFlush();
auto ddl = offscreen_surface_recorder_->detach();
- offscreen_surface_recorder_ = nullptr;
+ offscreen_surface_recorder_.reset();
DCHECK(ddl);
auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
@@ -433,22 +479,34 @@ sk_sp<SkImage> SkiaOutputSurfaceImpl::MakePromiseSkImageFromRenderPass(
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(recorder_);
- // Convert internal format from GLES2 to platform GL.
- const auto* version_info = gpu_service_->context_for_skia()->GetVersionInfo();
- unsigned int texture_storage_format = TextureStorageFormat(format);
- auto backend_format = GrBackendFormat::MakeGL(
- gl::GetInternalFormat(version_info, texture_storage_format),
- GL_TEXTURE_2D);
- SkColorType color_type =
- ResourceFormatToClosestSkColorType(true /*gpu_compositing */, format);
+ SkColorType color_type = kBGRA_8888_SkColorType;
+ GrBackendFormat backend_format;
+
+ if (!gpu_service_->is_using_vulkan()) {
+ // Convert internal format from GLES2 to platform GL.
+ const auto* version_info = impl_on_gpu_->gl_version_info();
+ unsigned int texture_storage_format = TextureStorageFormat(format);
+ backend_format = GrBackendFormat::MakeGL(
+ gl::GetInternalFormat(version_info, texture_storage_format),
+ GL_TEXTURE_2D);
+ color_type =
+ ResourceFormatToClosestSkColorType(true /*gpu_compositing */, format);
+ } else {
+#if BUILDFLAG(ENABLE_VULKAN)
+ backend_format = GrBackendFormat::MakeVk(VK_FORMAT_B8G8R8A8_UNORM);
+#else
+ NOTREACHED();
+#endif
+ }
return PromiseTextureHelper<RenderPassId>::MakePromiseSkImage(
- this, recorder_.get(), backend_format, size,
+ this, &recorder_.value(), backend_format, size,
mipmap ? GrMipMapped::kYes : GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin,
color_type, kPremul_SkAlphaType, nullptr /* color_space */, id);
}
void SkiaOutputSurfaceImpl::RemoveRenderPassResource(
std::vector<RenderPassId> ids) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!ids.empty());
auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
// impl_on_gpu_ is released on the GPU thread by a posted task from
@@ -460,6 +518,19 @@ void SkiaOutputSurfaceImpl::RemoveRenderPassResource(
sequence_id, std::move(callback), std::vector<gpu::SyncToken>()));
}
+void SkiaOutputSurfaceImpl::CopyOutput(
+ RenderPassId id,
+ const gfx::Rect& copy_rect,
+ std::unique_ptr<CopyOutputRequest> request) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ auto sequence_id = gpu_service_->skia_output_surface_sequence_id();
+ auto callback = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::CopyOutput,
+ base::Unretained(impl_on_gpu_.get()), id,
+ copy_rect, std::move(request));
+ gpu_service_->scheduler()->ScheduleTask(gpu::Scheduler::Task(
+ sequence_id, std::move(callback), std::vector<gpu::SyncToken>()));
+}
+
void SkiaOutputSurfaceImpl::InitializeOnGpuThread(base::WaitableEvent* event) {
base::ScopedClosureRunner scoped_runner(
base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
@@ -479,15 +550,15 @@ void SkiaOutputSurfaceImpl::InitializeOnGpuThread(base::WaitableEvent* event) {
void SkiaOutputSurfaceImpl::RecreateRecorder() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(characterization_.isValid());
- recorder_ =
- std::make_unique<SkDeferredDisplayListRecorder>(characterization_);
+ recorder_.emplace(characterization_);
// TODO(penghuang): remove the unnecessary getCanvas() call, when the
// recorder crash is fixed in skia.
recorder_->getCanvas();
}
void SkiaOutputSurfaceImpl::DidSwapBuffersComplete(
- gpu::SwapBuffersCompleteParams params) {
+ gpu::SwapBuffersCompleteParams params,
+ const gfx::Size& pixel_size) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(client_);
@@ -496,6 +567,8 @@ void SkiaOutputSurfaceImpl::DidSwapBuffersComplete(
if (!params.ca_layer_params.is_empty)
client_->DidReceiveCALayerParams(params.ca_layer_params);
client_->DidReceiveSwapBuffersAck();
+ if (needs_swap_size_notifications_)
+ client_->DidSwapWithSize(pixel_size);
}
void SkiaOutputSurfaceImpl::BufferPresented(
@@ -513,4 +586,9 @@ void SkiaOutputSurfaceImpl::BufferPresented(
}
}
+void SkiaOutputSurfaceImpl::SetNeedsSwapSizeNotifications(
+ bool needs_swap_size_notifications) {
+ needs_swap_size_notifications_ = needs_swap_size_notifications;
+}
+
} // namespace viz
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 3309aa30433..9fdcf5e1eac 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
@@ -6,8 +6,10 @@
#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_SURFACE_IMPL_H_
#include "base/macros.h"
+#include "base/optional.h"
#include "base/threading/thread_checker.h"
#include "components/viz/service/display/skia_output_surface.h"
+#include "components/viz/service/viz_service_export.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "gpu/ipc/common/surface_handle.h"
#include "gpu/ipc/in_process_command_buffer.h"
@@ -21,7 +23,6 @@ class WaitableEvent;
namespace viz {
class GpuServiceImpl;
-class VizProcessContextProvider;
class SkiaOutputSurfaceImplOnGpu;
class SyntheticBeginFrameSource;
@@ -37,12 +38,11 @@ class YUVResourceMetadata;
// render into. In SwapBuffers, it detaches a SkDeferredDisplayList from the
// recorder and plays it back on the framebuffer SkSurface on the GPU thread
// through SkiaOutputSurfaceImpleOnGpu.
-class SkiaOutputSurfaceImpl : public SkiaOutputSurface {
+class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface {
public:
SkiaOutputSurfaceImpl(
GpuServiceImpl* gpu_service,
gpu::SurfaceHandle surface_handle,
- scoped_refptr<VizProcessContextProvider> context_provider,
SyntheticBeginFrameSource* synthetic_begin_frame_source);
~SkiaOutputSurfaceImpl() override;
@@ -69,14 +69,17 @@ class SkiaOutputSurfaceImpl : public SkiaOutputSurface {
gpu::VulkanSurface* GetVulkanSurface() override;
#endif
unsigned UpdateGpuFence() override;
+ void SetNeedsSwapSizeNotifications(
+ bool needs_swap_size_notifications) override;
// SkiaOutputSurface implementation:
- SkCanvas* GetSkCanvasForCurrentFrame() override;
+ SkCanvas* BeginPaintCurrentFrame() override;
+ gpu::SyncToken FinishPaintCurrentFrame() override;
sk_sp<SkImage> MakePromiseSkImage(ResourceMetadata metadata) override;
sk_sp<SkImage> MakePromiseSkImageFromYUV(
std::vector<ResourceMetadata> metadatas,
SkYUVColorSpace yuv_color_space) override;
- gpu::SyncToken SkiaSwapBuffers(OutputSurfaceFrame frame) override;
+ void SkiaSwapBuffers(OutputSurfaceFrame frame) override;
SkCanvas* BeginPaintRenderPass(const RenderPassId& id,
const gfx::Size& surface_size,
ResourceFormat format,
@@ -87,13 +90,17 @@ class SkiaOutputSurfaceImpl : public SkiaOutputSurface {
ResourceFormat format,
bool mipmap) override;
void RemoveRenderPassResource(std::vector<RenderPassId> ids) override;
+ void CopyOutput(RenderPassId id,
+ const gfx::Rect& copy_rect,
+ std::unique_ptr<CopyOutputRequest> request) override;
private:
template <class T>
class PromiseTextureHelper;
void InitializeOnGpuThread(base::WaitableEvent* event);
void RecreateRecorder();
- void DidSwapBuffersComplete(gpu::SwapBuffersCompleteParams params);
+ void DidSwapBuffersComplete(gpu::SwapBuffersCompleteParams params,
+ const gfx::Size& pixel_size);
void BufferPresented(const gfx::PresentationFeedback& feedback);
uint64_t sync_fence_release_ = 0;
@@ -103,7 +110,7 @@ class SkiaOutputSurfaceImpl : public SkiaOutputSurface {
OutputSurfaceClient* client_ = nullptr;
SkSurfaceCharacterization characterization_;
- std::unique_ptr<SkDeferredDisplayListRecorder> recorder_;
+ base::Optional<SkDeferredDisplayListRecorder> recorder_;
// The current render pass id set by BeginPaintRenderPass.
RenderPassId current_render_pass_id_ = 0;
@@ -111,7 +118,7 @@ class SkiaOutputSurfaceImpl : public SkiaOutputSurface {
// The SkDDL recorder created by BeginPaintRenderPass, and
// FinishPaintRenderPass will turn it into a SkDDL and play the SkDDL back on
// the GPU thread.
- std::unique_ptr<SkDeferredDisplayListRecorder> offscreen_surface_recorder_;
+ base::Optional<SkDeferredDisplayListRecorder> offscreen_surface_recorder_;
// Sync tokens for resources which are used for the current frame.
std::vector<gpu::SyncToken> resource_sync_tokens_;
@@ -128,6 +135,9 @@ class SkiaOutputSurfaceImpl : public SkiaOutputSurface {
// |impl_on_gpu| is created and destroyed on the GPU thread.
std::unique_ptr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu_;
+ // Whether to send OutputSurfaceClient::DidSwapWithSize notifications.
+ bool needs_swap_size_notifications_ = false;
+
THREAD_CHECKER(thread_checker_);
base::WeakPtr<SkiaOutputSurfaceImpl> weak_ptr_;
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 eccdf7f1a9b..10c32116c59 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
@@ -7,21 +7,30 @@
#include "base/atomic_sequence_num.h"
#include "base/callback_helpers.h"
#include "base/synchronization/waitable_event.h"
+#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/service/display/output_surface_frame.h"
#include "components/viz/service/gl/gpu_service_impl.h"
#include "gpu/command_buffer/common/swap_buffers_complete_params.h"
-#include "gpu/command_buffer/service/gpu_preferences.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/scheduler.h"
#include "gpu/command_buffer/service/sync_point_manager.h"
#include "gpu/command_buffer/service/texture_base.h"
#include "gpu/command_buffer/service/texture_manager.h"
+#include "gpu/config/gpu_preferences.h"
+#include "gpu/ipc/common/gpu_surface_lookup.h"
#include "gpu/ipc/service/image_transport_surface.h"
+#include "gpu/vulkan/buildflags.h"
#include "third_party/skia/include/private/SkDeferredDisplayList.h"
+#include "ui/gfx/skia_util.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/gl_version_info.h"
+#include "ui/gl/init/gl_factory.h"
+
+#if BUILDFLAG(ENABLE_VULKAN)
+#include "gpu/vulkan/vulkan_implementation.h"
+#endif
namespace viz {
namespace {
@@ -64,43 +73,21 @@ SkiaOutputSurfaceImplOnGpu::SkiaOutputSurfaceImplOnGpu(
gpu::CommandBufferNamespace::VIZ_OUTPUT_SURFACE, command_buffer_id_,
gpu_service_->skia_output_surface_sequence_id());
- surface_ = gpu::ImageTransportSurface::CreateNativeSurface(
- weak_ptr_factory_.GetWeakPtr(), surface_handle_, gl::GLSurfaceFormat());
- DCHECK(surface_);
-
- if (!gpu_service_->CreateGrContextIfNecessary(surface_.get())) {
- LOG(FATAL) << "Failed to create GrContext";
- // TODO(penghuang): handle the failure.
- }
-
- DCHECK(gpu_service_->context_for_skia());
- DCHECK(gpu_service_->gr_context());
-
- if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
- LOG(FATAL) << "Failed to make current.";
- // TODO(penghuang): Handle the failure.
- }
-
- capabilities_.flipped_output_surface = surface_->FlipsVertically();
-
- // Get stencil bits from the default frame buffer.
- auto* current_gl = gpu_service_->context_for_skia()->GetCurrentGL();
- const auto* version = current_gl->Version;
- auto* api = current_gl->Api;
- GLint stencil_bits = 0;
- if (version->is_desktop_core_profile) {
- api->glGetFramebufferAttachmentParameterivEXTFn(
- GL_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE,
- &stencil_bits);
- } else {
- api->glGetIntegervFn(GL_STENCIL_BITS, &stencil_bits);
- }
-
- capabilities_.supports_stencil = stencil_bits > 0;
+ if (gpu_service_->is_using_vulkan())
+ InitializeForVulkan();
+ else
+ InitializeForGL();
}
SkiaOutputSurfaceImplOnGpu::~SkiaOutputSurfaceImplOnGpu() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+#if BUILDFLAG(ENABLE_VULKAN)
+ if (vulkan_surface_) {
+ vulkan_surface_->Destroy();
+ vulkan_surface_ = nullptr;
+ }
+#endif
+ sync_point_client_state_->Destroy();
}
void SkiaOutputSurfaceImplOnGpu::Reshape(
@@ -113,43 +100,71 @@ void SkiaOutputSurfaceImplOnGpu::Reshape(
base::WaitableEvent* event) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- std::unique_ptr<base::ScopedClosureRunner> scoped_runner;
+ base::ScopedClosureRunner scoped_runner;
if (event) {
- scoped_runner = std::make_unique<base::ScopedClosureRunner>(
+ scoped_runner.ReplaceClosure(
base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
}
- if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
- LOG(FATAL) << "Failed to make current.";
- // TODO(penghuang): Handle the failure.
- }
- gl::GLSurface::ColorSpace surface_color_space =
- color_space == gfx::ColorSpace::CreateSCRGBLinear()
- ? gl::GLSurface::ColorSpace::SCRGB_LINEAR
- : gl::GLSurface::ColorSpace::UNSPECIFIED;
- if (!surface_->Resize(size, device_scale_factor, surface_color_space,
- has_alpha)) {
- LOG(FATAL) << "Failed to resize.";
- // TODO(penghuang): Handle the failure.
- }
- DCHECK(gpu_service_->context_for_skia()->IsCurrent(surface_.get()));
- DCHECK(gpu_service_->gr_context());
+ if (!gpu_service_->is_using_vulkan()) {
+ if (!gl_context_->MakeCurrent(gl_surface_.get())) {
+ LOG(FATAL) << "Failed to make current.";
+ // TODO(penghuang): Handle the failure.
+ }
+ gl::GLSurface::ColorSpace surface_color_space =
+ color_space == gfx::ColorSpace::CreateSCRGBLinear()
+ ? gl::GLSurface::ColorSpace::SCRGB_LINEAR
+ : gl::GLSurface::ColorSpace::UNSPECIFIED;
+ if (!gl_surface_->Resize(size, device_scale_factor, surface_color_space,
+ has_alpha)) {
+ LOG(FATAL) << "Failed to resize.";
+ // TODO(penghuang): Handle the failure.
+ }
+ DCHECK(gl_context_->IsCurrent(gl_surface_.get()));
+ DCHECK(gr_context_);
- SkSurfaceProps surface_props =
- SkSurfaceProps(0, SkSurfaceProps::kLegacyFontHost_InitType);
+ SkSurfaceProps surface_props =
+ SkSurfaceProps(0, SkSurfaceProps::kLegacyFontHost_InitType);
- GrGLFramebufferInfo framebuffer_info;
- framebuffer_info.fFBOID = 0;
- const auto* version_info = gpu_service_->context_for_skia()->GetVersionInfo();
- framebuffer_info.fFormat = version_info->is_es ? GL_BGRA8_EXT : GL_RGBA8;
+ GrGLFramebufferInfo framebuffer_info;
+ framebuffer_info.fFBOID = 0;
+ framebuffer_info.fFormat =
+ gl_version_info_->is_es ? GL_BGRA8_EXT : GL_RGBA8;
- GrBackendRenderTarget render_target(size.width(), size.height(), 0, 8,
- framebuffer_info);
+ GrBackendRenderTarget render_target(size.width(), size.height(), 0, 8,
+ framebuffer_info);
- sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
- gpu_service_->gr_context(), render_target, kBottomLeft_GrSurfaceOrigin,
- kBGRA_8888_SkColorType, nullptr, &surface_props);
- DCHECK(sk_surface_);
+ sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
+ gr_context_, render_target, kBottomLeft_GrSurfaceOrigin,
+ kBGRA_8888_SkColorType, nullptr, &surface_props);
+ DCHECK(sk_surface_);
+ } else {
+#if BUILDFLAG(ENABLE_VULKAN)
+ if (vulkan_surface_)
+ vulkan_surface_->Destroy();
+ gfx::AcceleratedWidget accelerated_widget = gfx::kNullAcceleratedWidget;
+#if defined(OS_ANDROID)
+ accelerated_widget =
+ gpu::GpuSurfaceLookup::GetInstance()->AcquireNativeWidget(
+ surface_handle_);
+#else
+ accelerated_widget = surface_handle_;
+#endif
+ vulkan_surface_ = gpu_service_->vulkan_context_provider()
+ ->GetVulkanImplementation()
+ ->CreateViewSurface(accelerated_widget);
+ if (!vulkan_surface_)
+ LOG(FATAL) << "Failed to create vulkan surface.";
+ if (!vulkan_surface_->Initialize(
+ gpu_service_->vulkan_context_provider()->GetDeviceQueue(),
+ gpu::VulkanSurface::DEFAULT_SURFACE_FORMAT)) {
+ LOG(FATAL) << "Failed to initialize vulkan surface.";
+ }
+ CreateSkSurfaceForVulkan(size);
+#else
+ NOTREACHED();
+#endif
+ }
if (characterization) {
sk_surface_->characterize(characterization);
@@ -157,8 +172,7 @@ void SkiaOutputSurfaceImplOnGpu::Reshape(
}
}
-void SkiaOutputSurfaceImplOnGpu::SwapBuffers(
- OutputSurfaceFrame frame,
+void SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame(
std::unique_ptr<SkDeferredDisplayList> ddl,
std::vector<YUVResourceMetadata*> yuv_resource_metadatas,
uint64_t sync_fence_release) {
@@ -166,21 +180,47 @@ void SkiaOutputSurfaceImplOnGpu::SwapBuffers(
DCHECK(ddl);
DCHECK(sk_surface_);
- if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
+ if (!gpu_service_->is_using_vulkan() &&
+ !gl_context_->MakeCurrent(gl_surface_.get())) {
LOG(FATAL) << "Failed to make current.";
// TODO(penghuang): Handle the failure.
}
PreprocessYUVResources(std::move(yuv_resource_metadatas));
-
sk_surface_->draw(ddl.get());
- gpu_service_->gr_context()->flush();
- OnSwapBuffers();
- surface_->SwapBuffers(
- base::BindRepeating([](const gfx::PresentationFeedback&) {}));
+ gr_context_->flush();
sync_point_client_state_->ReleaseFenceSync(sync_fence_release);
}
+void SkiaOutputSurfaceImplOnGpu::SwapBuffers(OutputSurfaceFrame frame) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(sk_surface_);
+ if (!gpu_service_->is_using_vulkan()) {
+ if (!gl_context_->MakeCurrent(gl_surface_.get())) {
+ LOG(FATAL) << "Failed to make current.";
+ // TODO(penghuang): Handle the failure.
+ }
+ OnSwapBuffers();
+ gl_surface_->SwapBuffers(frame.need_presentation_feedback
+ ? buffer_presented_callback_
+ : base::DoNothing());
+ } else {
+#if BUILDFLAG(ENABLE_VULKAN)
+ OnSwapBuffers();
+ gpu::SwapBuffersCompleteParams params;
+ params.swap_response.swap_start = base::TimeTicks::Now();
+ params.swap_response.result = vulkan_surface_->SwapBuffers();
+ params.swap_response.swap_end = base::TimeTicks::Now();
+ DidSwapBuffersComplete(params);
+
+ CreateSkSurfaceForVulkan(
+ gfx::Size(sk_surface_->width(), sk_surface_->height()));
+#else
+ NOTREACHED();
+#endif
+ }
+}
+
void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass(
RenderPassId id,
std::unique_ptr<SkDeferredDisplayList> ddl,
@@ -189,9 +229,10 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass(
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(ddl);
- if (!gpu_service_->context_for_skia()->MakeCurrent(surface_.get())) {
+ if (!gpu_service_->is_using_vulkan() &&
+ !gl_context_->MakeCurrent(gl_surface_.get())) {
LOG(FATAL) << "Failed to make current.";
- // TODO(penghuang): Handle resize failure.
+ // TODO(penghuang): Handle the failure.
}
PreprocessYUVResources(std::move(yuv_resource_metadatas));
@@ -202,8 +243,8 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass(
// the SkSurfaceCharacterization::operator!= is implemented in Skia.
if (!surface || !surface->characterize(&characterization) ||
characterization != ddl->characterization()) {
- surface = SkSurface::MakeRenderTarget(
- gpu_service_->gr_context(), ddl->characterization(), SkBudgeted::kNo);
+ surface = SkSurface::MakeRenderTarget(gr_context_, ddl->characterization(),
+ SkBudgeted::kNo);
DCHECK(surface);
}
surface->draw(ddl.get());
@@ -213,6 +254,7 @@ void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass(
void SkiaOutputSurfaceImplOnGpu::RemoveRenderPassResource(
std::vector<RenderPassId> ids) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!ids.empty());
for (const auto& id : ids) {
auto it = offscreen_surfaces_.find(id);
@@ -221,10 +263,40 @@ void SkiaOutputSurfaceImplOnGpu::RemoveRenderPassResource(
}
}
+void SkiaOutputSurfaceImplOnGpu::CopyOutput(
+ RenderPassId id,
+ const gfx::Rect& copy_rect,
+ std::unique_ptr<CopyOutputRequest> request) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ // TODO(crbug.com/644851): Complete the implementation for all request types,
+ // scaling, etc.
+ DCHECK_EQ(request->result_format(), CopyOutputResult::Format::RGBA_BITMAP);
+ DCHECK(!request->is_scaled());
+ DCHECK(!request->has_result_selection() ||
+ request->result_selection() == gfx::Rect(copy_rect.size()));
+
+ DCHECK(!id || offscreen_surfaces_.find(id) != offscreen_surfaces_.end());
+ auto* surface = id ? offscreen_surfaces_[id].get() : sk_surface_.get();
+
+ sk_sp<SkImage> copy_image =
+ surface->makeImageSnapshot()->makeSubset(RectToSkIRect(copy_rect));
+ // Send copy request by copying into a bitmap.
+ SkBitmap bitmap;
+ copy_image->asLegacyBitmap(&bitmap);
+ request->SendResult(
+ std::make_unique<CopyOutputSkBitmapResult>(copy_rect, bitmap));
+}
+
void SkiaOutputSurfaceImplOnGpu::FullfillPromiseTexture(
const ResourceMetadata& metadata,
GrBackendTexture* backend_texture) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ if (gpu_service_->is_using_vulkan()) {
+ // TODO(https://crbug.com/838899): Use SkSurface as raster decoder target.
+ // NOTIMPLEMENTED();
+ return;
+ }
auto* mailbox_manager = gpu_service_->mailbox_manager();
auto* texture_base = mailbox_manager->ConsumeTexture(metadata.mailbox);
if (!texture_base) {
@@ -265,6 +337,11 @@ void SkiaOutputSurfaceImplOnGpu::FullfillPromiseTexture(
<< id;
}
+sk_sp<GrContextThreadSafeProxy>
+SkiaOutputSurfaceImplOnGpu::GetGrContextThreadSafeProxy() {
+ return gr_context_->threadSafeProxy();
+}
+
#if defined(OS_WIN)
void SkiaOutputSurfaceImplOnGpu::DidCreateAcceleratedSurfaceChildWindow(
gpu::SurfaceHandle parent_window,
@@ -277,9 +354,10 @@ void SkiaOutputSurfaceImplOnGpu::DidCreateAcceleratedSurfaceChildWindow(
void SkiaOutputSurfaceImplOnGpu::DidSwapBuffersComplete(
gpu::SwapBuffersCompleteParams params) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- params.swap_response.swap_id = pending_swap_completed_ids_.front();
- pending_swap_completed_ids_.pop_front();
- did_swap_buffer_complete_callback_.Run(params);
+ params.swap_response.swap_id = pending_swap_completed_params_.front().first;
+ gfx::Size pixel_size = pending_swap_completed_params_.front().second;
+ pending_swap_completed_params_.pop_front();
+ did_swap_buffer_complete_callback_.Run(params, pixel_size);
}
const gpu::gles2::FeatureInfo* SkiaOutputSurfaceImplOnGpu::GetFeatureInfo()
@@ -296,16 +374,9 @@ const gpu::GpuPreferences& SkiaOutputSurfaceImplOnGpu::GetGpuPreferences()
return gpu_preferences_;
}
-void SkiaOutputSurfaceImplOnGpu::SetSnapshotRequestedCallback(
- const base::Closure& callback) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- NOTIMPLEMENTED();
-}
-
void SkiaOutputSurfaceImplOnGpu::BufferPresented(
const gfx::PresentationFeedback& feedback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- buffer_presented_callback_.Run(feedback);
}
void SkiaOutputSurfaceImplOnGpu::AddFilter(IPC::MessageFilter* message_filter) {
@@ -319,6 +390,56 @@ int32_t SkiaOutputSurfaceImplOnGpu::GetRouteID() const {
return 0;
}
+void SkiaOutputSurfaceImplOnGpu::InitializeForGL() {
+ if (surface_handle_) {
+ gl_surface_ = gpu::ImageTransportSurface::CreateNativeSurface(
+ weak_ptr_factory_.GetWeakPtr(), surface_handle_, gl::GLSurfaceFormat());
+ } else {
+ // surface_ could be null for pixel tests.
+ gl_surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size(1, 1));
+ }
+ DCHECK(gl_surface_);
+
+ gl::GLContext* gl_context = nullptr;
+ if (!gpu_service_->GetGrContextForGLSurface(gl_surface_.get(), &gr_context_,
+ &gl_context)) {
+ LOG(FATAL) << "Failed to create GrContext";
+ // TODO(penghuang): handle the failure.
+ }
+ gl_context_ = gl_context;
+ DCHECK(gr_context_);
+ DCHECK(gl_context_);
+
+ if (!gl_context_->MakeCurrent(gl_surface_.get())) {
+ LOG(FATAL) << "Failed to make current.";
+ // TODO(penghuang): Handle the failure.
+ }
+
+ gl_version_info_ = gl_context_->GetVersionInfo();
+
+ capabilities_.flipped_output_surface = gl_surface_->FlipsVertically();
+
+ // Get stencil bits from the default frame buffer.
+ auto* current_gl = gl_context_->GetCurrentGL();
+ const auto* version = current_gl->Version;
+ auto* api = current_gl->Api;
+ GLint stencil_bits = 0;
+ if (version->is_desktop_core_profile) {
+ api->glGetFramebufferAttachmentParameterivEXTFn(
+ GL_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE,
+ &stencil_bits);
+ } else {
+ api->glGetIntegervFn(GL_STENCIL_BITS, &stencil_bits);
+ }
+
+ capabilities_.supports_stencil = stencil_bits > 0;
+}
+
+void SkiaOutputSurfaceImplOnGpu::InitializeForVulkan() {
+ gr_context_ = gpu_service_->GetGrContextForVulkan();
+ DCHECK(gr_context_);
+}
+
void SkiaOutputSurfaceImplOnGpu::BindOrCopyTextureIfNecessary(
gpu::TextureBase* texture_base) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -346,6 +467,11 @@ void SkiaOutputSurfaceImplOnGpu::BindOrCopyTextureIfNecessary(
void SkiaOutputSurfaceImplOnGpu::PreprocessYUVResources(
std::vector<YUVResourceMetadata*> yuv_resource_metadatas) {
+ if (gpu_service_->is_using_vulkan()) {
+ // TODO(https://crbug.com/838899): Use VkImage for video.
+ // NOTIMPLEMENTED();
+ return;
+ }
// Create SkImage for fullfilling YUV promise image, before drawing the ddl.
// TODO(penghuang): Remove the extra step when Skia supports drawing YUV
// textures directly.
@@ -375,15 +501,13 @@ void SkiaOutputSurfaceImplOnGpu::PreprocessYUVResources(
sk_sp<SkImage> image;
if (metadatas.size() == 2) {
image = SkImage::MakeFromNV12TexturesCopy(
- gpu_service_->gr_context(), yuv_metadata->yuv_color_space(),
- backend_textures, kTopLeft_GrSurfaceOrigin,
- nullptr /* image_color_space */);
+ gr_context_, yuv_metadata->yuv_color_space(), backend_textures,
+ kTopLeft_GrSurfaceOrigin, nullptr /* image_color_space */);
DCHECK(image);
} else {
image = SkImage::MakeFromYUVTexturesCopy(
- gpu_service_->gr_context(), yuv_metadata->yuv_color_space(),
- backend_textures, kTopLeft_GrSurfaceOrigin,
- nullptr /* image_color_space */);
+ gr_context_, yuv_metadata->yuv_color_space(), backend_textures,
+ kTopLeft_GrSurfaceOrigin, nullptr /* image_color_space */);
DCHECK(image);
}
yuv_metadata->set_image(std::move(image));
@@ -392,7 +516,30 @@ void SkiaOutputSurfaceImplOnGpu::PreprocessYUVResources(
void SkiaOutputSurfaceImplOnGpu::OnSwapBuffers() {
uint64_t swap_id = swap_id_++;
- pending_swap_completed_ids_.push_back(swap_id);
+ gfx::Size pixel_size(sk_surface_->width(), sk_surface_->height());
+ pending_swap_completed_params_.emplace_back(swap_id, pixel_size);
+}
+
+void SkiaOutputSurfaceImplOnGpu::CreateSkSurfaceForVulkan(
+ const gfx::Size& size) {
+#if BUILDFLAG(ENABLE_VULKAN)
+ SkSurfaceProps surface_props =
+ SkSurfaceProps(0, SkSurfaceProps::kLegacyFontHost_InitType);
+ auto* swap_chain = vulkan_surface_->GetSwapChain();
+ VkImage vk_image = swap_chain->GetCurrentImage(swap_chain->current_image());
+ GrVkImageInfo vk_image_info;
+ vk_image_info.fImage = vk_image;
+ vk_image_info.fAlloc = {VK_NULL_HANDLE, 0, 0, 0};
+ vk_image_info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ vk_image_info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
+ vk_image_info.fFormat = VK_FORMAT_B8G8R8A8_UNORM;
+ vk_image_info.fLevelCount = 1;
+ GrBackendRenderTarget render_target(size.width(), size.height(), 0, 0,
+ vk_image_info);
+ sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
+ gr_context_, render_target, kTopLeft_GrSurfaceOrigin,
+ kBGRA_8888_SkColorType, nullptr, &surface_props);
+#endif
}
} // 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 82462b0bf9b..a1248757a27 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
@@ -10,9 +10,9 @@
#include "base/threading/thread_checker.h"
#include "build/build_config.h"
#include "components/viz/common/quads/render_pass.h"
-#include "components/viz/common/resources/resource_metadata.h"
#include "components/viz/service/display/output_surface.h"
#include "components/viz/service/display/output_surface_frame.h"
+#include "components/viz/service/display/resource_metadata.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "gpu/ipc/common/surface_handle.h"
#include "gpu/ipc/in_process_command_buffer.h"
@@ -31,6 +31,10 @@ class GLSurface;
namespace gpu {
class SyncPointClientState;
+
+#if BUILDFLAG(ENABLE_VULKAN)
+class VulkanSurface;
+#endif
}
namespace viz {
@@ -72,7 +76,8 @@ class YUVResourceMetadata {
class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate {
public:
using DidSwapBufferCompleteCallback =
- base::RepeatingCallback<void(gpu::SwapBuffersCompleteParams)>;
+ base::RepeatingCallback<void(gpu::SwapBuffersCompleteParams,
+ const gfx::Size& pixel_size)>;
using BufferPresentedCallback =
base::RepeatingCallback<void(const gfx::PresentationFeedback& feedback)>;
SkiaOutputSurfaceImplOnGpu(
@@ -97,16 +102,20 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate {
bool use_stencil,
SkSurfaceCharacterization* characterization,
base::WaitableEvent* event);
- void SwapBuffers(OutputSurfaceFrame frame,
- std::unique_ptr<SkDeferredDisplayList> ddl,
- std::vector<YUVResourceMetadata*> yuv_resource_metadatas,
- uint64_t sync_fence_release);
+ void FinishPaintCurrentFrame(
+ std::unique_ptr<SkDeferredDisplayList> ddl,
+ std::vector<YUVResourceMetadata*> yuv_resource_metadatas,
+ uint64_t sync_fence_release);
+ void SwapBuffers(OutputSurfaceFrame frame);
void FinishPaintRenderPass(
RenderPassId id,
std::unique_ptr<SkDeferredDisplayList> ddl,
std::vector<YUVResourceMetadata*> yuv_resource_metadatas,
uint64_t sync_fence_release);
void RemoveRenderPassResource(std::vector<RenderPassId> ids);
+ void CopyOutput(RenderPassId id,
+ const gfx::Rect& copy_rect,
+ std::unique_ptr<CopyOutputRequest> request);
// Fullfill callback for promise SkImage created from a resource.
void FullfillPromiseTexture(const ResourceMetadata& metadata,
@@ -118,6 +127,9 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate {
void FullfillPromiseTexture(const RenderPassId id,
GrBackendTexture* backend_texture);
+ sk_sp<GrContextThreadSafeProxy> GetGrContextThreadSafeProxy();
+ const gl::GLVersionInfo* gl_version_info() const { return gl_version_info_; }
+
private:
// gpu::ImageTransportSurfaceDelegate implementation:
#if defined(OS_WIN)
@@ -128,11 +140,13 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate {
void DidSwapBuffersComplete(gpu::SwapBuffersCompleteParams params) override;
const gpu::gles2::FeatureInfo* GetFeatureInfo() const override;
const gpu::GpuPreferences& GetGpuPreferences() const override;
- void SetSnapshotRequestedCallback(const base::Closure& callback) override;
void BufferPresented(const gfx::PresentationFeedback& feedback) override;
void AddFilter(IPC::MessageFilter* message_filter) override;
int32_t GetRouteID() const override;
+ void InitializeForGL();
+ void InitializeForVulkan();
+
void BindOrCopyTextureIfNecessary(gpu::TextureBase* texture_base);
void PreprocessYUVResources(
std::vector<YUVResourceMetadata*> yuv_resource_metadatas);
@@ -140,6 +154,8 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate {
// Generage the next swap ID and push it to our pending swap ID queues.
void OnSwapBuffers();
+ void CreateSkSurfaceForVulkan(const gfx::Size& size);
+
const gpu::CommandBufferId command_buffer_id_;
GpuServiceImpl* const gpu_service_;
const gpu::SurfaceHandle surface_handle_;
@@ -147,17 +163,25 @@ class SkiaOutputSurfaceImplOnGpu : public gpu::ImageTransportSurfaceDelegate {
BufferPresentedCallback buffer_presented_callback_;
scoped_refptr<gpu::SyncPointClientState> sync_point_client_state_;
gpu::GpuPreferences gpu_preferences_;
- scoped_refptr<gl::GLSurface> surface_;
+ scoped_refptr<gl::GLSurface> gl_surface_;
sk_sp<SkSurface> sk_surface_;
+ GrContext* gr_context_ = nullptr;
+ scoped_refptr<gl::GLContext> gl_context_;
+ const gl::GLVersionInfo* gl_version_info_ = nullptr;
OutputSurface::Capabilities capabilities_;
+#if BUILDFLAG(ENABLE_VULKAN)
+ std::unique_ptr<gpu::VulkanSurface> vulkan_surface_;
+#endif
+
// Offscreen surfaces for render passes. It can only be accessed on GPU
// thread.
base::flat_map<RenderPassId, sk_sp<SkSurface>> offscreen_surfaces_;
- // ID is pushed each time we begin a swap, and popped each time we present or
- // complete a swap.
- base::circular_deque<uint64_t> pending_swap_completed_ids_;
+ // Params are pushed each time we begin a swap, and popped each time we
+ // present or complete a swap.
+ base::circular_deque<std::pair<uint64_t, gfx::Size>>
+ pending_swap_completed_params_;
uint64_t swap_id_ = 0;
THREAD_CHECKER(thread_checker_);
diff --git a/chromium/components/viz/service/display_embedder/software_output_device_win.cc b/chromium/components/viz/service/display_embedder/software_output_device_win.cc
index ba84cac14b3..56f31c7a1ee 100644
--- a/chromium/components/viz/service/display_embedder/software_output_device_win.cc
+++ b/chromium/components/viz/service/display_embedder/software_output_device_win.cc
@@ -6,6 +6,8 @@
#include "base/memory/shared_memory.h"
#include "base/threading/thread_checker.h"
+#include "base/win/windows_version.h"
+#include "base/win/wrapped_window_proc.h"
#include "components/viz/common/display/use_layered_window.h"
#include "components/viz/common/resources/resource_sizes.h"
#include "components/viz/service/display_embedder/output_device_backing.h"
@@ -14,8 +16,10 @@
#include "skia/ext/platform_canvas.h"
#include "skia/ext/skia_utils_win.h"
#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/base/win/hidden_window.h"
#include "ui/gfx/gdi_util.h"
#include "ui/gfx/skia_util.h"
+#include "ui/gfx/win/hwnd_util.h"
namespace viz {
namespace {
@@ -92,6 +96,7 @@ void SoftwareOutputDeviceWinBase::EndPaint() {
}
// SoftwareOutputDevice implementation that draws directly to the provided HWND.
+// The backing buffer for paint is shared for all instances of this class.
class SoftwareOutputDeviceWinDirect : public SoftwareOutputDeviceWinBase,
public OutputDeviceBacking::Client {
public:
@@ -330,6 +335,96 @@ void SoftwareOutputDeviceWinProxy::DrawAck() {
std::move(swap_ack_callback_).Run();
}
+// WindowProc callback function for SoftwareOutputDeviceWinDirectChild.
+LRESULT CALLBACK IntermediateWindowProc(HWND window,
+ UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ switch (message) {
+ case WM_ERASEBKGND:
+ // Prevent Windows from erasing all window content on resize.
+ return 1;
+ default:
+ return DefWindowProc(window, message, w_param, l_param);
+ }
+}
+
+// SoftwareOutputDevice implementation that creates a child HWND and draws
+// directly to it. This is intended to be used in the GPU process. The child
+// HWND is initially parented to a hidden window and needs be reparented to the
+// appropriate browser HWND. The backing buffer for paint is shared for all
+// instances of this class.
+class SoftwareOutputDeviceWinDirectChild
+ : public SoftwareOutputDeviceWinDirect {
+ public:
+ static std::unique_ptr<SoftwareOutputDeviceWinDirectChild> Create(
+ OutputDeviceBacking* backing);
+ ~SoftwareOutputDeviceWinDirectChild() override;
+
+ // SoftwareOutputDeviceWinBase implementation.
+ void ResizeDelegated() override;
+
+ private:
+ static wchar_t* GetWindowClass();
+
+ SoftwareOutputDeviceWinDirectChild(HWND child_hwnd,
+ OutputDeviceBacking* backing);
+
+ DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceWinDirectChild);
+};
+
+// static
+std::unique_ptr<SoftwareOutputDeviceWinDirectChild>
+SoftwareOutputDeviceWinDirectChild::Create(OutputDeviceBacking* backing) {
+ // Create a child window that is initially parented to a hidden window.
+ // |child_hwnd| needs to be reparented to a browser created HWND to be
+ // visible.
+ HWND child_hwnd =
+ CreateWindowEx(WS_EX_NOPARENTNOTIFY, GetWindowClass(), L"",
+ WS_CHILDWINDOW | WS_DISABLED | WS_VISIBLE, 0, 0, 0, 0,
+ ui::GetHiddenWindow(), nullptr, nullptr, nullptr);
+ DCHECK(child_hwnd);
+ return base::WrapUnique(
+ new SoftwareOutputDeviceWinDirectChild(child_hwnd, backing));
+}
+
+SoftwareOutputDeviceWinDirectChild::~SoftwareOutputDeviceWinDirectChild() {
+ DestroyWindow(hwnd());
+}
+
+void SoftwareOutputDeviceWinDirectChild::ResizeDelegated() {
+ SoftwareOutputDeviceWinDirect::ResizeDelegated();
+ // Resize the child HWND to match the content size.
+ SetWindowPos(hwnd(), nullptr, 0, 0, viewport_pixel_size_.width(),
+ viewport_pixel_size_.height(),
+ SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS |
+ SWP_NOOWNERZORDER | SWP_NOZORDER);
+}
+
+// static
+wchar_t* SoftwareOutputDeviceWinDirectChild::GetWindowClass() {
+ static ATOM window_class = 0;
+
+ // Register window class on first call.
+ if (!window_class) {
+ WNDCLASSEX intermediate_class;
+ base::win::InitializeWindowClass(
+ L"Intermediate Software Window",
+ &base::win::WrappedWindowProc<IntermediateWindowProc>, CS_OWNDC, 0, 0,
+ nullptr, reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)), nullptr,
+ nullptr, nullptr, &intermediate_class);
+ window_class = RegisterClassEx(&intermediate_class);
+ DCHECK(window_class);
+ }
+
+ return reinterpret_cast<wchar_t*>(window_class);
+}
+
+SoftwareOutputDeviceWinDirectChild::SoftwareOutputDeviceWinDirectChild(
+ HWND child_hwnd,
+ OutputDeviceBacking* backing)
+ : SoftwareOutputDeviceWinDirect(child_hwnd, backing) {}
+
} // namespace
std::unique_ptr<SoftwareOutputDevice> CreateSoftwareOutputDeviceWinBrowser(
@@ -344,7 +439,8 @@ std::unique_ptr<SoftwareOutputDevice> CreateSoftwareOutputDeviceWinBrowser(
std::unique_ptr<SoftwareOutputDevice> CreateSoftwareOutputDeviceWinGpu(
HWND hwnd,
OutputDeviceBacking* backing,
- mojom::DisplayClient* display_client) {
+ mojom::DisplayClient* display_client,
+ HWND* out_child_hwnd) {
if (NeedsToUseLayerWindow(hwnd)) {
DCHECK(display_client);
@@ -354,11 +450,19 @@ std::unique_ptr<SoftwareOutputDevice> CreateSoftwareOutputDeviceWinGpu(
display_client->CreateLayeredWindowUpdater(
mojo::MakeRequest(&layered_window_updater));
+ *out_child_hwnd = nullptr;
+
return std::make_unique<SoftwareOutputDeviceWinProxy>(
hwnd, std::move(layered_window_updater));
- }
+ } else {
+ auto software_output_device =
+ SoftwareOutputDeviceWinDirectChild::Create(backing);
- return std::make_unique<SoftwareOutputDeviceWinDirect>(hwnd, backing);
+ // The child HWND needs to be parented to the browser HWND to be visible.
+ *out_child_hwnd = software_output_device->hwnd();
+
+ return software_output_device;
+ }
}
} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/software_output_device_win.h b/chromium/components/viz/service/display_embedder/software_output_device_win.h
index aa2dd64bb0d..60ce8c664ad 100644
--- a/chromium/components/viz/service/display_embedder/software_output_device_win.h
+++ b/chromium/components/viz/service/display_embedder/software_output_device_win.h
@@ -27,7 +27,8 @@ CreateSoftwareOutputDeviceWinBrowser(HWND hwnd, OutputDeviceBacking* backing);
VIZ_SERVICE_EXPORT std::unique_ptr<SoftwareOutputDevice>
CreateSoftwareOutputDeviceWinGpu(HWND hwnd,
OutputDeviceBacking* backing,
- mojom::DisplayClient* display_client);
+ mojom::DisplayClient* display_client,
+ HWND* out_child_hwnd);
} // namespace viz
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 fff590968c5..b1719edf55a 100644
--- a/chromium/components/viz/service/display_embedder/software_output_surface.cc
+++ b/chromium/components/viz/service/display_embedder/software_output_surface.cc
@@ -62,10 +62,9 @@ void SoftwareOutputSurface::SwapBuffers(OutputSurfaceFrame frame) {
base::TimeTicks swap_time = base::TimeTicks::Now();
for (auto& latency : frame.latency_info) {
latency.AddLatencyNumberWithTimestamp(
- ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, 0, swap_time, 1);
+ ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, swap_time, 1);
latency.AddLatencyNumberWithTimestamp(
- ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, swap_time,
- 1);
+ ui::INPUT_EVENT_LATENCY_FRAME_SWAP_COMPONENT, swap_time, 1);
}
DCHECK(stored_latency_info_.empty())
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
index e32a6e1864e..7ad0baefac6 100644
--- a/chromium/components/viz/service/display_embedder/viz_process_context_provider.cc
+++ b/chromium/components/viz/service/display_embedder/viz_process_context_provider.cc
@@ -10,17 +10,21 @@
#include "base/lazy_instance.h"
#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.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 "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/gles2_implementation.h"
#include "gpu/command_buffer/client/raster_implementation_gles.h"
#include "gpu/command_buffer/common/context_creation_attribs.h"
-#include "gpu/command_buffer/service/gpu_preferences.h"
+#include "gpu/command_buffer/common/skia_utils.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/sync_point_manager.h"
#include "gpu/config/gpu_feature_info.h"
+#include "gpu/config/gpu_preferences.h"
#include "gpu/ipc/common/surface_handle.h"
#include "gpu/ipc/gl_in_process_context.h"
#include "gpu/ipc/gpu_in_process_thread_service.h"
@@ -47,19 +51,23 @@ gpu::ContextCreationAttribs CreateAttributes() {
return attributes;
}
+void UmaRecordContextLost(ContextLostReason reason) {
+ UMA_HISTOGRAM_ENUMERATION("GPU.ContextLost.DisplayCompositor", reason);
+}
+
} // namespace
VizProcessContextProvider::VizProcessContextProvider(
- scoped_refptr<gpu::InProcessCommandBuffer::Service> service,
+ scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor,
gpu::SurfaceHandle surface_handle,
gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
gpu::ImageFactory* image_factory,
gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
const gpu::SharedMemoryLimits& limits)
: attributes_(CreateAttributes()),
- context_(gpu::GLInProcessContext::CreateWithoutInit()),
+ context_(std::make_unique<gpu::GLInProcessContext>()),
context_result_(
- context_->Initialize(std::move(service),
+ context_->Initialize(std::move(task_executor),
nullptr,
(surface_handle == gpu::kNullSurfaceHandle),
surface_handle,
@@ -68,10 +76,21 @@ VizProcessContextProvider::VizProcessContextProvider(
gpu_memory_buffer_manager,
image_factory,
gpu_channel_manager_delegate,
- base::ThreadTaskRunnerHandle::Get())),
- cache_controller_(std::make_unique<ContextCacheController>(
- context_->GetImplementation(),
- base::ThreadTaskRunnerHandle::Get())) {}
+ base::ThreadTaskRunnerHandle::Get())) {
+ if (context_result_ == gpu::ContextResult::kSuccess) {
+ auto* gles2_implementation = context_->GetImplementation();
+ cache_controller_ = std::make_unique<ContextCacheController>(
+ gles2_implementation, base::ThreadTaskRunnerHandle::Get());
+ // |context_| 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)));
+ } else {
+ // Context initialization failed. Record UMA and cleanup.
+ UmaRecordContextLost(CONTEXT_INIT_FAILED);
+ context_.reset();
+ }
+}
VizProcessContextProvider::~VizProcessContextProvider() = default;
@@ -101,13 +120,12 @@ class GrContext* VizProcessContextProvider::GrContext() {
size_t max_resource_cache_bytes;
size_t max_glyph_cache_texture_bytes;
- skia_bindings::GrContextForGLES2Interface::
- DetermineCacheLimitsFromAvailableMemory(&max_resource_cache_bytes,
- &max_glyph_cache_texture_bytes);
+ gpu::raster::DetermineGrCacheLimitsFromAvailableMemory(
+ &max_resource_cache_bytes, &max_glyph_cache_texture_bytes);
- gr_context_.reset(new skia_bindings::GrContextForGLES2Interface(
+ gr_context_ = std::make_unique<skia_bindings::GrContextForGLES2Interface>(
ContextGL(), ContextSupport(), ContextCapabilities(),
- max_resource_cache_bytes, max_glyph_cache_texture_bytes));
+ max_resource_cache_bytes, max_glyph_cache_texture_bytes);
return gr_context_->get();
}
@@ -129,17 +147,12 @@ const gpu::GpuFeatureInfo& VizProcessContextProvider::GetGpuFeatureInfo()
return context_->GetGpuFeatureInfo();
}
-void VizProcessContextProvider::AddObserver(ContextLostObserver* obs) {}
-
-void VizProcessContextProvider::RemoveObserver(ContextLostObserver* obs) {}
+void VizProcessContextProvider::AddObserver(ContextLostObserver* obs) {
+ observers_.AddObserver(obs);
+}
-uint32_t VizProcessContextProvider::GetCopyTextureInternalFormat() {
- if (attributes_.alpha_size > 0)
- return GL_RGBA;
- DCHECK_NE(attributes_.red_size, 0);
- DCHECK_NE(attributes_.green_size, 0);
- DCHECK_NE(attributes_.blue_size, 0);
- return GL_RGB;
+void VizProcessContextProvider::RemoveObserver(ContextLostObserver* obs) {
+ observers_.RemoveObserver(obs);
}
void VizProcessContextProvider::SetUpdateVSyncParametersCallback(
@@ -148,4 +161,16 @@ void VizProcessContextProvider::SetUpdateVSyncParametersCallback(
context_->SetUpdateVSyncParametersCallback(callback);
}
+void VizProcessContextProvider::OnContextLost() {
+ for (auto& observer : observers_)
+ observer.OnContextLost();
+ if (gr_context_)
+ gr_context_->OnLostContext();
+
+ gpu::CommandBuffer::State state =
+ context_->GetCommandBuffer()->GetLastState();
+ UmaRecordContextLost(
+ GetContextLostReason(state.error, state.context_lost_reason));
+}
+
} // 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
index a25423e16fe..e81949c67b7 100644
--- a/chromium/components/viz/service/display_embedder/viz_process_context_provider.h
+++ b/chromium/components/viz/service/display_embedder/viz_process_context_provider.h
@@ -10,6 +10,7 @@
#include <memory>
#include "base/compiler_specific.h"
+#include "base/observer_list.h"
#include "base/synchronization/lock.h"
#include "components/viz/common/gpu/context_cache_controller.h"
#include "components/viz/common/gpu/context_provider.h"
@@ -33,6 +34,7 @@ class GrContextForGLES2Interface;
}
namespace viz {
+class ContextLostObserver;
// A ContextProvider used in the viz process to setup an InProcessCommandBuffer
// for the display compositor.
@@ -41,7 +43,7 @@ class VIZ_SERVICE_EXPORT VizProcessContextProvider
public ContextProvider {
public:
VizProcessContextProvider(
- scoped_refptr<gpu::InProcessCommandBuffer::Service> service,
+ scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor,
gpu::SurfaceHandle surface_handle,
gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
gpu::ImageFactory* image_factory,
@@ -62,8 +64,6 @@ class VIZ_SERVICE_EXPORT VizProcessContextProvider
void AddObserver(ContextLostObserver* obs) override;
void RemoveObserver(ContextLostObserver* obs) override;
- uint32_t GetCopyTextureInternalFormat();
-
void SetUpdateVSyncParametersCallback(
const gpu::InProcessCommandBuffer::UpdateVSyncParametersCallback&
callback);
@@ -73,6 +73,8 @@ class VIZ_SERVICE_EXPORT VizProcessContextProvider
~VizProcessContextProvider() override;
private:
+ void OnContextLost();
+
const gpu::ContextCreationAttribs attributes_;
base::Lock context_lock_;
@@ -80,6 +82,8 @@ class VIZ_SERVICE_EXPORT VizProcessContextProvider
gpu::ContextResult context_result_;
std::unique_ptr<skia_bindings::GrContextForGLES2Interface> gr_context_;
std::unique_ptr<ContextCacheController> cache_controller_;
+
+ base::ObserverList<ContextLostObserver> observers_;
};
} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/DEPS b/chromium/components/viz/service/frame_sinks/DEPS
index 5d7cb869719..fc9f38b81c3 100644
--- a/chromium/components/viz/service/frame_sinks/DEPS
+++ b/chromium/components/viz/service/frame_sinks/DEPS
@@ -14,5 +14,8 @@ include_rules = [
specific_include_rules = {
".*unittest\.cc": [
"+third_party/khronos/GLES2",
- ]
+ ],
+ "external_begin_frame_source_android.cc": [
+ "+jni/ExternalBeginFrameSourceAndroid_jni.h",
+ ],
}
diff --git a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_impl.cc b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_impl.cc
index b086112b6b0..71fb57f65df 100644
--- a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_impl.cc
+++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_impl.cc
@@ -67,8 +67,8 @@ void CompositorFrameSinkImpl::SubmitCompositorFrameInternal(
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback callback) {
const auto result = support_->MaybeSubmitCompositorFrame(
local_surface_id, std::move(frame), std::move(hit_test_region_list),
- std::move(callback));
- if (result == CompositorFrameSinkSupport::ACCEPTED)
+ submit_time, std::move(callback));
+ if (result == SubmitResult::ACCEPTED)
return;
const char* reason =
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 44baa1dae5e..37049477ac6 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
@@ -7,11 +7,14 @@
#include <algorithm>
#include <utility>
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
+#include "base/time/time.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/surfaces/surface_info.h"
#include "components/viz/service/display/display.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
+#include "components/viz/service/display/shared_bitmap_manager.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "components/viz/service/surfaces/surface.h"
#include "components/viz/service/surfaces/surface_reference.h"
@@ -39,9 +42,6 @@ CompositorFrameSinkSupport::CompositorFrameSinkSupport(
}
CompositorFrameSinkSupport::~CompositorFrameSinkSupport() {
- if (!destruction_callback_.is_null())
- std::move(destruction_callback_).Run();
-
// Unregister |this| as a BeginFrameObserver so that the
// BeginFrameSource does not call into |this| after it's deleted.
callback_received_begin_frame_ = true;
@@ -65,7 +65,7 @@ CompositorFrameSinkSupport::~CompositorFrameSinkSupport() {
// SharedBitmapId that has been reported from the client. Since the client is
// gone that memory can be freed. If we don't then it would leak.
for (const auto& id : owned_bitmaps_)
- ServerSharedBitmapManager::current()->ChildDeletedSharedBitmap(id);
+ frame_sink_manager_->shared_bitmap_manager()->ChildDeletedSharedBitmap(id);
// No video capture clients should remain after calling
// UnregisterCompositorFrameSinkSupport().
@@ -85,11 +85,6 @@ void CompositorFrameSinkSupport::SetAggregatedDamageCallbackForTesting(
aggregated_damage_callback_ = std::move(callback);
}
-void CompositorFrameSinkSupport::SetDestructionCallback(
- base::OnceClosure callback) {
- destruction_callback_ = std::move(callback);
-}
-
void CompositorFrameSinkSupport::SetBeginFrameSource(
BeginFrameSource* begin_frame_source) {
if (begin_frame_source_ && added_frame_observer_) {
@@ -126,12 +121,43 @@ void CompositorFrameSinkSupport::OnSurfaceActivated(Surface* surface) {
last_activated_surface_id_ = surface->surface_id();
}
- DCHECK(surface->active_referenced_surfaces());
- UpdateSurfaceReferences(surface->surface_id().local_surface_id(),
- *surface->active_referenced_surfaces());
- uint32_t frame_token = surface->GetActiveFrame().metadata.frame_token;
- if (frame_token)
- frame_sink_manager_->OnFrameTokenChanged(frame_sink_id_, frame_token);
+ DCHECK(surface->HasActiveFrame());
+ surface->UpdateSurfaceReferences();
+
+ // Check if this is a display root surface and the SurfaceId is changing.
+ if (is_root_ && (!referenced_local_surface_id_ ||
+ *referenced_local_surface_id_ !=
+ surface->surface_id().local_surface_id())) {
+ UpdateDisplayRootReference(surface);
+ }
+}
+
+void CompositorFrameSinkSupport::OnFrameTokenChanged(uint32_t frame_token) {
+ frame_sink_manager_->OnFrameTokenChanged(frame_sink_id_, frame_token);
+}
+
+void CompositorFrameSinkSupport::OnSurfaceProcessed(Surface* surface) {
+ DidReceiveCompositorFrameAck();
+}
+
+void CompositorFrameSinkSupport::OnSurfaceAggregatedDamage(
+ Surface* surface,
+ const LocalSurfaceId& local_surface_id,
+ const CompositorFrame& frame,
+ const gfx::Rect& damage_rect,
+ base::TimeTicks expected_display_time) {
+ DCHECK(!damage_rect.IsEmpty());
+
+ const gfx::Size& frame_size_in_pixels = frame.size_in_pixels();
+ if (aggregated_damage_callback_) {
+ aggregated_damage_callback_.Run(local_surface_id, frame_size_in_pixels,
+ damage_rect, expected_display_time);
+ }
+
+ for (CapturableFrameSink::Client* client : capture_clients_) {
+ client->OnFrameDamaged(frame_size_in_pixels, damage_rect,
+ expected_display_time, frame.metadata);
+ }
}
void CompositorFrameSinkSupport::OnSurfaceDiscarded(Surface* surface) {
@@ -245,14 +271,17 @@ void CompositorFrameSinkSupport::SubmitCompositorFrame(
uint64_t submit_time) {
const auto result = MaybeSubmitCompositorFrame(
local_surface_id, std::move(frame), std::move(hit_test_region_list),
+ submit_time,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
- DCHECK_EQ(result, ACCEPTED);
+ UMA_HISTOGRAM_ENUMERATION(
+ "Compositing.CompositorFrameSinkSupport.SubmitResult", result);
+ DCHECK_EQ(result, SubmitResult::ACCEPTED);
}
bool CompositorFrameSinkSupport::DidAllocateSharedBitmap(
mojo::ScopedSharedBufferHandle buffer,
const SharedBitmapId& id) {
- if (!ServerSharedBitmapManager::current()->ChildAllocatedSharedBitmap(
+ if (!frame_sink_manager_->shared_bitmap_manager()->ChildAllocatedSharedBitmap(
std::move(buffer), id))
return false;
@@ -262,18 +291,40 @@ bool CompositorFrameSinkSupport::DidAllocateSharedBitmap(
void CompositorFrameSinkSupport::DidDeleteSharedBitmap(
const SharedBitmapId& id) {
- ServerSharedBitmapManager::current()->ChildDeletedSharedBitmap(id);
+ frame_sink_manager_->shared_bitmap_manager()->ChildDeletedSharedBitmap(id);
owned_bitmaps_.erase(id);
}
-CompositorFrameSinkSupport::SubmitResult
-CompositorFrameSinkSupport::MaybeSubmitCompositorFrame(
+SubmitResult CompositorFrameSinkSupport::MaybeSubmitCompositorFrame(
const LocalSurfaceId& local_surface_id,
CompositorFrame frame,
base::Optional<HitTestRegionList> hit_test_region_list,
+ uint64_t submit_time,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback callback) {
TRACE_EVENT1("viz", "CompositorFrameSinkSupport::MaybeSubmitCompositorFrame",
"FrameSinkId", frame_sink_id_.ToString());
+
+ TRACE_EVENT_WITH_FLOW1(
+ "viz,benchmark", "Graphics.Pipeline",
+ TRACE_ID_GLOBAL(frame.metadata.begin_frame_ack.trace_id),
+ TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
+ "ReceiveCompositorFrame");
+
+ TRACE_EVENT_FLOW_END0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"),
+ "SubmitCompositorFrame", local_surface_id.hash());
+
+ bool tracing_enabled;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"),
+ &tracing_enabled);
+ if (tracing_enabled) {
+ base::TimeDelta elapsed = base::TimeTicks::Now().since_origin() -
+ base::TimeDelta::FromMicroseconds(submit_time);
+ TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"),
+ "SubmitCompositorFrame::TimeElapsed",
+ TRACE_EVENT_SCOPE_THREAD,
+ "elapsed time:", elapsed.InMicroseconds());
+ }
+
DCHECK(local_surface_id.is_valid());
DCHECK(!frame.render_pass_list.empty());
DCHECK(!frame.size_in_pixels().IsEmpty());
@@ -281,6 +332,13 @@ CompositorFrameSinkSupport::MaybeSubmitCompositorFrame(
CHECK(callback_received_begin_frame_);
CHECK(callback_received_receive_ack_);
+ ++ack_pending_count_;
+
+ base::ScopedClosureRunner frame_rejected_callback(base::BindOnce(
+ &CompositorFrameSinkSupport::DidRejectCompositorFrame,
+ weak_factory_.GetWeakPtr(), frame.metadata.frame_token,
+ frame.metadata.request_presentation_feedback, frame.resource_list));
+
compositor_frame_callback_ = std::move(callback);
if (compositor_frame_callback_) {
callback_received_begin_frame_ = false;
@@ -292,16 +350,17 @@ CompositorFrameSinkSupport::MaybeSubmitCompositorFrame(
if (!allow_copy_output_requests_ && frame.HasCopyOutputRequests()) {
TRACE_EVENT_INSTANT0("viz", "CopyOutputRequests not allowed",
TRACE_EVENT_SCOPE_THREAD);
- return COPY_OUTPUT_REQUESTS_NOT_ALLOWED;
+ return SubmitResult::COPY_OUTPUT_REQUESTS_NOT_ALLOWED;
}
+ // TODO(crbug.com/846739): It should be possible to use
+ // |frame.metadata.frame_token| instead of maintaining a |last_frame_index_|.
uint64_t frame_index = ++last_frame_index_;
- ++ack_pending_count_;
// Override the has_damage flag (ignoring invalid data from clients).
frame.metadata.begin_frame_ack.has_damage = true;
- BeginFrameAck ack = frame.metadata.begin_frame_ack;
- DCHECK_LE(BeginFrameArgs::kStartingFrameNumber, ack.sequence_number);
+ DCHECK_LE(BeginFrameArgs::kStartingFrameNumber,
+ frame.metadata.begin_frame_ack.sequence_number);
if (!ui::LatencyInfo::Verify(frame.metadata.latency_info,
"RenderWidgetHostImpl::OnSwapCompositorFrame")) {
@@ -309,8 +368,7 @@ CompositorFrameSinkSupport::MaybeSubmitCompositorFrame(
}
for (ui::LatencyInfo& latency : frame.metadata.latency_info) {
if (latency.latency_components().size() > 0) {
- latency.AddLatencyNumber(ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT,
- 0);
+ latency.AddLatencyNumber(ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT);
}
}
@@ -321,6 +379,14 @@ CompositorFrameSinkSupport::MaybeSubmitCompositorFrame(
local_surface_id == last_created_surface_id_.local_surface_id()) {
current_surface = prev_surface;
} else {
+ TRACE_EVENT_WITH_FLOW2(
+ TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
+ "LocalSurfaceId.Submission.Flow",
+ TRACE_ID_GLOBAL(local_surface_id.submission_trace_id()),
+ TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
+ "ReceiveCompositorFrame", "local_surface_id",
+ local_surface_id.ToString());
+
SurfaceId surface_id(frame_sink_id_, local_surface_id);
SurfaceInfo surface_info(surface_id, frame.device_scale_factor(),
frame.size_in_pixels());
@@ -344,15 +410,7 @@ CompositorFrameSinkSupport::MaybeSubmitCompositorFrame(
if (!surface_info.is_valid() || !monotonically_increasing_id) {
TRACE_EVENT_INSTANT0("viz", "Surface Invariants Violation",
TRACE_EVENT_SCOPE_THREAD);
- std::vector<ReturnedResource> resources =
- TransferableResource::ReturnResources(frame.resource_list);
- ReturnResources(resources);
- DidReceiveCompositorFrameAck();
- if (frame.metadata.presentation_token) {
- DidPresentCompositorFrame(frame.metadata.presentation_token,
- base::TimeTicks(), base::TimeDelta(), 0);
- }
- return SURFACE_INVARIANTS_VIOLATION;
+ return SubmitResult::SURFACE_INVARIANTS_VIOLATION;
}
current_surface = CreateSurface(surface_info);
@@ -367,68 +425,21 @@ CompositorFrameSinkSupport::MaybeSubmitCompositorFrame(
last_created_surface_id_, frame_index, std::move(hit_test_region_list));
bool result = current_surface->QueueFrame(
- std::move(frame), frame_index,
- base::BindOnce(&CompositorFrameSinkSupport::DidReceiveCompositorFrameAck,
- weak_factory_.GetWeakPtr()),
- base::BindRepeating(&CompositorFrameSinkSupport::OnAggregatedDamage,
- weak_factory_.GetWeakPtr()),
- frame.metadata.presentation_token
+ std::move(frame), frame_index, std::move(frame_rejected_callback),
+ frame.metadata.request_presentation_feedback
? base::BindOnce(
&CompositorFrameSinkSupport::DidPresentCompositorFrame,
- weak_factory_.GetWeakPtr(), frame.metadata.presentation_token)
+ weak_factory_.GetWeakPtr(), frame.metadata.frame_token)
: Surface::PresentedCallback());
if (!result) {
TRACE_EVENT_INSTANT0("viz", "QueueFrame failed", TRACE_EVENT_SCOPE_THREAD);
- return SURFACE_INVARIANTS_VIOLATION;
+ return SubmitResult::SURFACE_INVARIANTS_VIOLATION;
}
if (begin_frame_source_)
begin_frame_source_->DidFinishFrame(this);
- return ACCEPTED;
-}
-
-void CompositorFrameSinkSupport::UpdateSurfaceReferences(
- const LocalSurfaceId& local_surface_id,
- const std::vector<SurfaceId>& active_referenced_surfaces) {
- SurfaceId surface_id(frame_sink_id_, local_surface_id);
-
- const base::flat_set<SurfaceId>& existing_referenced_surfaces =
- surface_manager_->GetSurfacesReferencedByParent(surface_id);
-
- base::flat_set<SurfaceId> new_referenced_surfaces(
- active_referenced_surfaces.begin(), active_referenced_surfaces.end(),
- base::KEEP_FIRST_OF_DUPES);
-
- // Populate list of surface references to add and remove by getting the
- // difference between existing surface references and surface references for
- // latest activated CompositorFrame.
- std::vector<SurfaceReference> references_to_add;
- std::vector<SurfaceReference> references_to_remove;
- GetSurfaceReferenceDifference(surface_id, existing_referenced_surfaces,
- new_referenced_surfaces, &references_to_add,
- &references_to_remove);
-
- // Check if this is a display root surface and the SurfaceId is changing.
- if (is_root_ && (!referenced_local_surface_id_.has_value() ||
- referenced_local_surface_id_.value() != local_surface_id)) {
- // Make the new SurfaceId reachable from the top-level root.
- references_to_add.push_back(MakeTopLevelRootReference(surface_id));
-
- // Make the old SurfaceId unreachable from the top-level root if applicable.
- if (referenced_local_surface_id_.has_value()) {
- references_to_remove.push_back(MakeTopLevelRootReference(
- SurfaceId(frame_sink_id_, referenced_local_surface_id_.value())));
- }
-
- referenced_local_surface_id_ = local_surface_id;
- }
-
- // Modify surface references stored in SurfaceManager.
- if (!references_to_add.empty())
- surface_manager_->AddSurfaceReferences(references_to_add);
- if (!references_to_remove.empty())
- surface_manager_->RemoveSurfaceReferences(references_to_remove);
+ return SubmitResult::ACCEPTED;
}
SurfaceReference CompositorFrameSinkSupport::MakeTopLevelRootReference(
@@ -467,20 +478,42 @@ void CompositorFrameSinkSupport::DidReceiveCompositorFrameAck() {
void CompositorFrameSinkSupport::DidPresentCompositorFrame(
uint32_t presentation_token,
- base::TimeTicks time,
- base::TimeDelta refresh,
- uint32_t flags) {
+ const gfx::PresentationFeedback& feedback) {
DCHECK(presentation_token);
if (client_) {
- if (time != base::TimeTicks()) {
- client_->DidPresentCompositorFrame(presentation_token, time, refresh,
- flags);
- } else {
- client_->DidDiscardCompositorFrame(presentation_token);
- }
+ client_->DidPresentCompositorFrame(presentation_token, feedback);
}
}
+void CompositorFrameSinkSupport::DidRejectCompositorFrame(
+ uint32_t presentation_token,
+ bool request_presentation_feedback,
+ std::vector<TransferableResource> frame_resource_list) {
+ std::vector<ReturnedResource> resources =
+ TransferableResource::ReturnResources(frame_resource_list);
+ ReturnResources(resources);
+ DidReceiveCompositorFrameAck();
+ if (request_presentation_feedback) {
+ DidPresentCompositorFrame(presentation_token,
+ gfx::PresentationFeedback::Failure());
+ }
+}
+
+void CompositorFrameSinkSupport::UpdateDisplayRootReference(
+ const Surface* surface) {
+ // Make the new SurfaceId reachable from the top-level root.
+ surface_manager_->AddSurfaceReferences(
+ {MakeTopLevelRootReference(surface->surface_id())});
+
+ // Make the old SurfaceId unreachable from the top-level root if applicable.
+ if (referenced_local_surface_id_) {
+ surface_manager_->RemoveSurfaceReferences({MakeTopLevelRootReference(
+ SurfaceId(frame_sink_id_, *referenced_local_surface_id_))});
+ }
+
+ referenced_local_surface_id_ = surface->surface_id().local_surface_id();
+}
+
void CompositorFrameSinkSupport::OnBeginFrame(const BeginFrameArgs& args) {
if (last_activated_surface_id_.is_valid())
surface_manager_->SurfaceDamageExpected(last_activated_surface_id_, args);
@@ -492,8 +525,15 @@ void CompositorFrameSinkSupport::OnBeginFrame(const BeginFrameArgs& args) {
HandleCallback();
}
- if (client_ && client_needs_begin_frame_)
- client_->OnBeginFrame(args);
+ if (client_ && client_needs_begin_frame_) {
+ BeginFrameArgs copy_args = args;
+ copy_args.trace_id = ComputeTraceId();
+ TRACE_EVENT_WITH_FLOW1("viz,benchmark", "Graphics.Pipeline",
+ TRACE_ID_GLOBAL(copy_args.trace_id),
+ TRACE_EVENT_FLAG_FLOW_OUT, "step",
+ "IssueBeginFrame");
+ client_->OnBeginFrame(copy_args);
+ }
}
const BeginFrameArgs& CompositorFrameSinkSupport::LastUsedBeginFrameArgs()
@@ -535,8 +575,7 @@ Surface* CompositorFrameSinkSupport::CreateSurface(
void CompositorFrameSinkSupport::AttachCaptureClient(
CapturableFrameSink::Client* client) {
- DCHECK(std::find(capture_clients_.begin(), capture_clients_.end(), client) ==
- capture_clients_.end());
+ DCHECK(!base::ContainsValue(capture_clients_, client));
capture_clients_.push_back(client);
}
@@ -597,34 +636,24 @@ Surface* CompositorFrameSinkSupport::GetLastCreatedSurfaceForTesting() {
const char* CompositorFrameSinkSupport::GetSubmitResultAsString(
SubmitResult result) {
switch (result) {
- case CompositorFrameSinkSupport::ACCEPTED:
+ case SubmitResult::ACCEPTED:
return "Accepted";
- case CompositorFrameSinkSupport::COPY_OUTPUT_REQUESTS_NOT_ALLOWED:
+ case SubmitResult::COPY_OUTPUT_REQUESTS_NOT_ALLOWED:
return "CopyOutputRequests not allowed";
- case CompositorFrameSinkSupport::SURFACE_INVARIANTS_VIOLATION:
+ case SubmitResult::SURFACE_INVARIANTS_VIOLATION:
return "Surface invariants violation";
}
NOTREACHED();
return nullptr;
}
-void CompositorFrameSinkSupport::OnAggregatedDamage(
- const LocalSurfaceId& local_surface_id,
- const CompositorFrame& frame,
- const gfx::Rect& damage_rect,
- base::TimeTicks expected_display_time) const {
- DCHECK(!damage_rect.IsEmpty());
-
- const gfx::Size& frame_size_in_pixels = frame.size_in_pixels();
- if (aggregated_damage_callback_) {
- aggregated_damage_callback_.Run(local_surface_id, frame_size_in_pixels,
- damage_rect, expected_display_time);
- }
-
- for (CapturableFrameSink::Client* client : capture_clients_) {
- client->OnFrameDamaged(frame_size_in_pixels, damage_rect,
- expected_display_time, frame.metadata);
- }
+int64_t CompositorFrameSinkSupport::ComputeTraceId() {
+ // This is losing some info, but should normally be sufficient to avoid
+ // collisions.
+ ++trace_sequence_;
+ uint64_t client = (frame_sink_id_.client_id() & 0xffff);
+ uint64_t sink = (frame_sink_id_.sink_id() & 0xffff);
+ return (client << 48) | (sink << 32) | trace_sequence_;
}
} // namespace viz
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 8317b21c9e6..a85f0835069 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
@@ -16,7 +16,7 @@
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/surfaces/surface_info.h"
-#include "components/viz/service/frame_sinks/referenced_surface_tracker.h"
+#include "components/viz/common/surfaces/surface_range.h"
#include "components/viz/service/frame_sinks/surface_resource_holder.h"
#include "components/viz/service/frame_sinks/surface_resource_holder_client.h"
#include "components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h"
@@ -33,19 +33,23 @@ class LatestLocalSurfaceIdLookupDelegate;
class Surface;
class SurfaceManager;
+// Possible outcomes of MaybeSubmitCompositorFrame().
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class SubmitResult {
+ ACCEPTED = 0,
+ COPY_OUTPUT_REQUESTS_NOT_ALLOWED = 1,
+ SURFACE_INVARIANTS_VIOLATION = 2,
+ // Magic constant used by the histogram macros.
+ kMaxValue = SURFACE_INVARIANTS_VIOLATION,
+};
+
class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport
: public BeginFrameObserver,
public SurfaceResourceHolderClient,
public SurfaceClient,
public CapturableFrameSink {
public:
- // Possible outcomes of MaybeSubmitCompositorFrame().
- enum SubmitResult {
- ACCEPTED,
- COPY_OUTPUT_REQUESTS_NOT_ALLOWED,
- SURFACE_INVARIANTS_VIOLATION,
- };
-
using AggregatedDamageCallback =
base::RepeatingCallback<void(const LocalSurfaceId& local_surface_id,
const gfx::Size& frame_size_in_pixels,
@@ -84,9 +88,6 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport
// or one of its descendents is determined to be damaged at aggregation time.
void SetAggregatedDamageCallbackForTesting(AggregatedDamageCallback callback);
- // Sets callback called on destruction.
- void SetDestructionCallback(base::OnceClosure callback);
-
// This allows the FrameSinkManagerImpl to pass a BeginFrameSource to use.
void SetBeginFrameSource(BeginFrameSource* begin_frame_source);
@@ -103,6 +104,14 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport
// most |local_surface_id|.
std::vector<std::unique_ptr<CopyOutputRequest>> TakeCopyOutputRequests(
const LocalSurfaceId& local_surface_id) override;
+ void OnFrameTokenChanged(uint32_t frame_token) override;
+ void OnSurfaceProcessed(Surface* surface) override;
+ void OnSurfaceAggregatedDamage(
+ Surface* surface,
+ const LocalSurfaceId& local_surface_id,
+ const CompositorFrame& frame,
+ const gfx::Rect& damage_rect,
+ base::TimeTicks expected_display_time) override;
// mojom::CompositorFrameSink helpers.
void SetNeedsBeginFrame(bool needs_begin_frame);
@@ -132,6 +141,7 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport
const LocalSurfaceId& local_surface_id,
CompositorFrame frame,
base::Optional<HitTestRegionList> hit_test_region_list,
+ uint64_t submit_time,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback);
// CapturableFrameSink implementation.
@@ -159,22 +169,19 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport
private:
friend class FrameSinkManagerTest;
- // Updates surface references using |active_referenced_surfaces| from the most
- // recent CompositorFrame. This will add and remove top-level root references
- // if |is_root_| is true and |local_surface_id| has changed. Modifies surface
- // references stored in SurfaceManager.
- void UpdateSurfaceReferences(
- const LocalSurfaceId& local_surface_id,
- const std::vector<SurfaceId>& active_referenced_surfaces);
-
// Creates a surface reference from the top-level root to |surface_id|.
SurfaceReference MakeTopLevelRootReference(const SurfaceId& surface_id);
void DidReceiveCompositorFrameAck();
void DidPresentCompositorFrame(uint32_t presentation_token,
- base::TimeTicks time,
- base::TimeDelta refresh,
- uint32_t flags);
+ const gfx::PresentationFeedback& feedback);
+ void DidRejectCompositorFrame(
+ uint32_t presentation_token,
+ bool request_presentation_feedback,
+ std::vector<TransferableResource> frame_resource_list);
+
+ // Update the display root reference with |surface|.
+ void UpdateDisplayRootReference(const Surface* surface);
// BeginFrameObserver implementation.
void OnBeginFrame(const BeginFrameArgs& args) override;
@@ -185,15 +192,12 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport
void UpdateNeedsBeginFramesInternal();
Surface* CreateSurface(const SurfaceInfo& surface_info);
- void OnAggregatedDamage(const LocalSurfaceId& local_surface_id,
- const CompositorFrame& frame,
- const gfx::Rect& damage_rect,
- base::TimeTicks expected_display_time) const;
-
// For the sync API calls, if we are blocking a client callback, runs it once
// BeginFrame and FrameAck are done.
void HandleCallback();
+ int64_t ComputeTraceId();
+
mojom::CompositorFrameSinkClient* const client_;
FrameSinkManagerImpl* const frame_sink_manager_;
@@ -241,9 +245,6 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport
// clients would be able to capture content for which they are not authorized.
bool allow_copy_output_requests_;
- // A callback that will be run at the start of the destructor if set.
- base::OnceClosure destruction_callback_;
-
// TODO(crbug.com/754872): Remove once tab capture has moved into VIZ.
AggregatedDamageCallback aggregated_damage_callback_;
@@ -274,6 +275,7 @@ class VIZ_SERVICE_EXPORT CompositorFrameSinkSupport
compositor_frame_callback_;
bool callback_received_begin_frame_ = true;
bool callback_received_receive_ack_ = true;
+ uint32_t trace_sequence_ = 0;
base::WeakPtrFactory<CompositorFrameSinkSupport> weak_factory_;
diff --git a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
index 9852c9b2f18..a0d62a044f8 100644
--- a/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
+++ b/chromium/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -11,6 +11,7 @@
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "components/viz/common/surfaces/surface_info.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "components/viz/test/begin_frame_args_test.h"
#include "components/viz/test/compositor_frame_helpers.h"
@@ -68,11 +69,10 @@ class MockFrameSinkManagerClient : public mojom::FrameSinkManagerClient {
// mojom::FrameSinkManagerClient:
MOCK_METHOD1(OnSurfaceCreated, void(const SurfaceId&));
MOCK_METHOD1(OnFirstSurfaceActivation, void(const SurfaceInfo&));
+ MOCK_METHOD2(OnFrameTokenChanged, void(const FrameSinkId&, uint32_t));
void OnAggregatedHitTestRegionListUpdated(
const FrameSinkId& frame_sink_id,
const std::vector<AggregatedHitTestRegion>& hit_test_data) override {}
- void OnFrameTokenChanged(const FrameSinkId& frame_sink_id,
- uint32_t frame_token) override {}
private:
DISALLOW_COPY_AND_ASSIGN(MockFrameSinkManagerClient);
@@ -81,7 +81,8 @@ class MockFrameSinkManagerClient : public mojom::FrameSinkManagerClient {
class CompositorFrameSinkSupportTest : public testing::Test {
public:
CompositorFrameSinkSupportTest()
- : begin_frame_source_(0.f, false),
+ : manager_(&shared_bitmap_manager_),
+ begin_frame_source_(0.f, false),
local_surface_id_(3, kArbitraryToken),
frame_sync_token_(GenTestSyncToken(4)),
consumer_sync_token_(GenTestSyncToken(5)) {
@@ -104,32 +105,38 @@ class CompositorFrameSinkSupportTest : public testing::Test {
manager_.surface_manager()->RemoveObserver(&surface_observer_);
}
- void SubmitCompositorFrameWithResources(ResourceId* resource_ids,
- size_t num_resource_ids) {
- auto frame = MakeDefaultCompositorFrame();
+ void AddResourcesToFrame(CompositorFrame* frame,
+ ResourceId* resource_ids,
+ size_t num_resource_ids) {
for (size_t i = 0u; i < num_resource_ids; ++i) {
TransferableResource resource;
resource.id = resource_ids[i];
resource.mailbox_holder.texture_target = GL_TEXTURE_2D;
resource.mailbox_holder.sync_token = frame_sync_token_;
- frame.resource_list.push_back(resource);
+ frame->resource_list.push_back(resource);
}
+ }
+
+ void SubmitCompositorFrameWithResources(ResourceId* resource_ids,
+ size_t num_resource_ids) {
+ auto frame = MakeDefaultCompositorFrame();
+ AddResourcesToFrame(&frame, resource_ids, num_resource_ids);
support_->SubmitCompositorFrame(local_surface_id_, std::move(frame));
EXPECT_EQ(surface_observer_.last_created_surface_id().local_surface_id(),
local_surface_id_);
}
bool SubmitCompositorFrameWithCopyRequest(
+ CompositorFrame frame,
std::unique_ptr<CopyOutputRequest> request) {
- auto frame = MakeDefaultCompositorFrame();
frame.render_pass_list.back()->copy_requests.push_back(std::move(request));
const auto result = support_->MaybeSubmitCompositorFrame(
- local_surface_id_, std::move(frame), base::nullopt,
+ local_surface_id_, std::move(frame), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
switch (result) {
- case CompositorFrameSinkSupport::ACCEPTED:
+ case SubmitResult::ACCEPTED:
return true;
- case CompositorFrameSinkSupport::COPY_OUTPUT_REQUESTS_NOT_ALLOWED:
+ case SubmitResult::COPY_OUTPUT_REQUESTS_NOT_ALLOWED:
return false;
default:
ADD_FAILURE()
@@ -155,6 +162,18 @@ class CompositorFrameSinkSupportTest : public testing::Test {
}
void CheckReturnedResourcesMatchExpected(ResourceId* expected_returned_ids,
+ size_t expected_resources) {
+ const std::vector<ReturnedResource>& actual_resources =
+ fake_support_client_.returned_resources();
+ ASSERT_EQ(expected_resources, actual_resources.size());
+ for (size_t i = 0; i < expected_resources; ++i) {
+ ReturnedResource resource = actual_resources[i];
+ EXPECT_EQ(expected_returned_ids[i], resource.id);
+ }
+ fake_support_client_.clear_returned_resources();
+ }
+
+ void CheckReturnedResourcesMatchExpected(ResourceId* expected_returned_ids,
int* expected_returned_counts,
size_t expected_resources,
gpu::SyncToken expected_sync_token) {
@@ -181,6 +200,7 @@ class CompositorFrameSinkSupportTest : public testing::Test {
}
protected:
+ ServerSharedBitmapManager shared_bitmap_manager_;
FrameSinkManagerImpl manager_;
MockFrameSinkManagerClient frame_sink_manager_client_;
FakeCompositorFrameSinkClient fake_support_client_;
@@ -535,29 +555,29 @@ TEST_F(CompositorFrameSinkSupportTest, MonotonicallyIncreasingLocalSurfaceIds) {
LocalSurfaceId local_surface_id5(8, 1, kArbitraryToken);
LocalSurfaceId local_surface_id6(9, 3, kArbitraryToken);
auto result = support->MaybeSubmitCompositorFrame(
- local_surface_id1, MakeDefaultCompositorFrame(), base::nullopt,
+ local_surface_id1, MakeDefaultCompositorFrame(), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
- EXPECT_EQ(CompositorFrameSinkSupport::ACCEPTED, result);
+ EXPECT_EQ(SubmitResult::ACCEPTED, result);
result = support->MaybeSubmitCompositorFrame(
- local_surface_id2, MakeDefaultCompositorFrame(), base::nullopt,
+ local_surface_id2, MakeDefaultCompositorFrame(), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
- EXPECT_EQ(CompositorFrameSinkSupport::ACCEPTED, result);
+ EXPECT_EQ(SubmitResult::ACCEPTED, result);
result = support->MaybeSubmitCompositorFrame(
- local_surface_id3, MakeDefaultCompositorFrame(), base::nullopt,
+ local_surface_id3, MakeDefaultCompositorFrame(), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
- EXPECT_EQ(CompositorFrameSinkSupport::ACCEPTED, result);
+ EXPECT_EQ(SubmitResult::ACCEPTED, result);
result = support->MaybeSubmitCompositorFrame(
- local_surface_id4, MakeDefaultCompositorFrame(), base::nullopt,
+ local_surface_id4, MakeDefaultCompositorFrame(), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
- EXPECT_EQ(CompositorFrameSinkSupport::SURFACE_INVARIANTS_VIOLATION, result);
+ EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result);
result = support->MaybeSubmitCompositorFrame(
- local_surface_id5, MakeDefaultCompositorFrame(), base::nullopt,
+ local_surface_id5, MakeDefaultCompositorFrame(), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
- EXPECT_EQ(CompositorFrameSinkSupport::SURFACE_INVARIANTS_VIOLATION, result);
+ EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result);
result = support->MaybeSubmitCompositorFrame(
- local_surface_id6, MakeDefaultCompositorFrame(), base::nullopt,
+ local_surface_id6, MakeDefaultCompositorFrame(), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
- EXPECT_EQ(CompositorFrameSinkSupport::ACCEPTED, result);
+ EXPECT_EQ(SubmitResult::ACCEPTED, result);
manager_.InvalidateFrameSinkId(kAnotherArbitraryFrameSinkId);
}
@@ -579,9 +599,20 @@ TEST_F(CompositorFrameSinkSupportTest, ProhibitsUnprivilegedCopyRequests) {
*got_nothing = result->IsEmpty();
},
&did_receive_aborted_copy_result));
- EXPECT_FALSE(SubmitCompositorFrameWithCopyRequest(std::move(request)));
+
+ auto frame = MakeDefaultCompositorFrame();
+ ResourceId frame_resource_ids[] = {1, 2, 3};
+ AddResourcesToFrame(&frame, frame_resource_ids,
+ base::size(frame_resource_ids));
+
+ EXPECT_FALSE(SubmitCompositorFrameWithCopyRequest(std::move(frame),
+ std::move(request)));
EXPECT_TRUE(did_receive_aborted_copy_result);
+ // All the resources in the rejected frame should have been returned.
+ CheckReturnedResourcesMatchExpected(frame_resource_ids,
+ base::size(frame_resource_ids));
+
manager_.InvalidateFrameSinkId(kAnotherArbitraryFrameSinkId);
}
@@ -700,7 +731,7 @@ TEST_F(CompositorFrameSinkSupportTest, DuplicateCopyRequest) {
{
auto frame = CompositorFrameBuilder()
.AddDefaultRenderPass()
- .SetReferencedSurfaces({surface_id})
+ .SetReferencedSurfaces({SurfaceRange(surface_id)})
.Build();
support_->SubmitCompositorFrame(local_surface_id_, std::move(frame));
EXPECT_EQ(surface_observer_.last_created_surface_id().local_surface_id(),
@@ -776,9 +807,9 @@ TEST_F(CompositorFrameSinkSupportTest, ZeroDeviceScaleFactor) {
.SetDeviceScaleFactor(0.f)
.Build();
const auto result = support_->MaybeSubmitCompositorFrame(
- local_surface_id_, std::move(frame), base::nullopt,
+ local_surface_id_, std::move(frame), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
- EXPECT_EQ(CompositorFrameSinkSupport::SURFACE_INVARIANTS_VIOLATION, result);
+ EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result);
EXPECT_FALSE(GetSurfaceForId(id));
}
@@ -792,9 +823,9 @@ TEST_F(CompositorFrameSinkSupportTest, FrameSizeMismatch) {
.AddRenderPass(gfx::Rect(5, 5), gfx::Rect())
.Build();
auto result = support_->MaybeSubmitCompositorFrame(
- local_surface_id_, std::move(frame), base::nullopt,
+ local_surface_id_, std::move(frame), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
- EXPECT_EQ(CompositorFrameSinkSupport::ACCEPTED, result);
+ EXPECT_EQ(SubmitResult::ACCEPTED, result);
EXPECT_TRUE(GetSurfaceForId(id));
// Submit a frame with size (5,4). This frame should be rejected and the
@@ -802,10 +833,19 @@ TEST_F(CompositorFrameSinkSupportTest, FrameSizeMismatch) {
frame = CompositorFrameBuilder()
.AddRenderPass(gfx::Rect(5, 4), gfx::Rect())
.Build();
+ ResourceId frame_resource_ids[] = {1, 2, 3};
+ AddResourcesToFrame(&frame, frame_resource_ids,
+ base::size(frame_resource_ids));
+
result = support_->MaybeSubmitCompositorFrame(
- local_surface_id_, std::move(frame), base::nullopt,
+ local_surface_id_, std::move(frame), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
- EXPECT_EQ(CompositorFrameSinkSupport::SURFACE_INVARIANTS_VIOLATION, result);
+
+ EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result);
+
+ // All the resources in the rejected frame should have been returned.
+ CheckReturnedResourcesMatchExpected(frame_resource_ids,
+ base::size(frame_resource_ids));
}
// Check that if the device scale factor of a CompositorFrame doesn't match the
@@ -820,9 +860,9 @@ TEST_F(CompositorFrameSinkSupportTest, DeviceScaleFactorMismatch) {
.SetDeviceScaleFactor(0.5f)
.Build();
auto result = support_->MaybeSubmitCompositorFrame(
- local_surface_id_, std::move(frame), base::nullopt,
+ local_surface_id_, std::move(frame), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
- EXPECT_EQ(CompositorFrameSinkSupport::ACCEPTED, result);
+ EXPECT_EQ(SubmitResult::ACCEPTED, result);
EXPECT_TRUE(GetSurfaceForId(id));
// Submit a frame with device scale factor of 0.4. This frame should be
@@ -832,9 +872,9 @@ TEST_F(CompositorFrameSinkSupportTest, DeviceScaleFactorMismatch) {
.SetDeviceScaleFactor(0.4f)
.Build();
result = support_->MaybeSubmitCompositorFrame(
- local_surface_id_, std::move(frame), base::nullopt,
+ local_surface_id_, std::move(frame), base::nullopt, 0,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
- EXPECT_EQ(CompositorFrameSinkSupport::SURFACE_INVARIANTS_VIOLATION, result);
+ EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result);
}
TEST_F(CompositorFrameSinkSupportTest, PassesOnBeginFrameAcks) {
@@ -1021,5 +1061,23 @@ TEST_F(CompositorFrameSinkSupportTest,
EXPECT_FALSE(requests_map.empty());
}
+// Verifies that OnFrameTokenUpdate is issued after OnFirstSurfaceActivation.
+TEST_F(CompositorFrameSinkSupportTest,
+ OnFrameTokenUpdateAfterFirstSurfaceActivation) {
+ LocalSurfaceId local_surface_id(1, kArbitraryToken);
+ uint32_t frame_token = 2u;
+ auto frame = CompositorFrameBuilder()
+ .AddDefaultRenderPass()
+ .SetFrameToken(frame_token)
+ .SetSendFrameTokenToEmbedder(true)
+ .Build();
+
+ testing::InSequence sequence;
+ EXPECT_CALL(frame_sink_manager_client_, OnSurfaceCreated(_));
+ EXPECT_CALL(frame_sink_manager_client_, OnFirstSurfaceActivation(_));
+ EXPECT_CALL(frame_sink_manager_client_, OnFrameTokenChanged(_, frame_token));
+ support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
+}
+
} // namespace
} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc b/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
index ed6ac7abfaf..f5578eef306 100644
--- a/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
+++ b/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
@@ -7,6 +7,8 @@
#include <memory>
#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "build/build_config.h"
#include "cc/trees/layer_tree_frame_sink_client.h"
#include "components/viz/common/hit_test/hit_test_region_list.h"
#include "components/viz/common/quads/compositor_frame.h"
@@ -44,10 +46,6 @@ DirectLayerTreeFrameSink::DirectLayerTreeFrameSink(
use_viz_hit_test_(use_viz_hit_test),
weak_factory_(this) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- capabilities_.must_always_swap = true;
- // Display and DirectLayerTreeFrameSink share a GL context, so sync
- // points aren't needed when passing resources between them.
- capabilities_.delegated_sync_points_required = false;
}
DirectLayerTreeFrameSink::~DirectLayerTreeFrameSink() {
@@ -61,10 +59,9 @@ bool DirectLayerTreeFrameSink::BindToClient(
if (!cc::LayerTreeFrameSink::BindToClient(client))
return false;
- constexpr bool is_root = true;
support_ = support_manager_->CreateCompositorFrameSinkSupport(
- this, frame_sink_id_, is_root,
- capabilities_.delegated_sync_points_required);
+ this, frame_sink_id_, /*is_root=*/true,
+ /*return_sync_tokens_required=*/false);
begin_frame_source_ = std::make_unique<ExternalBeginFrameSource>(this);
client_->SetBeginFrameSource(begin_frame_source_.get());
@@ -201,11 +198,21 @@ void DirectLayerTreeFrameSink::DisplayDidDrawAndSwap() {
void DirectLayerTreeFrameSink::DisplayDidReceiveCALayerParams(
const gfx::CALayerParams& ca_layer_params) {
+#if defined(OS_MACOSX)
// If |ca_layer_params| should have content only when there exists a client
// to send it to.
DCHECK(ca_layer_params.is_empty || display_client_);
if (display_client_)
display_client_->OnDisplayReceivedCALayerParams(ca_layer_params);
+#else
+ NOTREACHED();
+ ALLOW_UNUSED_LOCAL(display_client_);
+#endif
+}
+
+void DirectLayerTreeFrameSink::DisplayDidCompleteSwapWithSize(
+ const gfx::Size& pixel_size) {
+ // Not needed in non-OOP-D mode.
}
void DirectLayerTreeFrameSink::DidSwapAfterSnapshotRequestReceived(
@@ -233,15 +240,8 @@ void DirectLayerTreeFrameSink::DidReceiveCompositorFrameAckInternal(
void DirectLayerTreeFrameSink::DidPresentCompositorFrame(
uint32_t presentation_token,
- base::TimeTicks time,
- base::TimeDelta refresh,
- uint32_t flags) {
- client_->DidPresentCompositorFrame(presentation_token, time, refresh, flags);
-}
-
-void DirectLayerTreeFrameSink::DidDiscardCompositorFrame(
- uint32_t presentation_token) {
- client_->DidDiscardCompositorFrame(presentation_token);
+ const gfx::PresentationFeedback& feedback) {
+ client_->DidPresentCompositorFrame(presentation_token, feedback);
}
void DirectLayerTreeFrameSink::OnBeginFrame(const BeginFrameArgs& args) {
diff --git a/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h b/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h
index a37812702df..5a4c38a09b7 100644
--- a/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h
+++ b/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h
@@ -61,6 +61,7 @@ class VIZ_SERVICE_EXPORT DirectLayerTreeFrameSink
void DisplayDidDrawAndSwap() override;
void DisplayDidReceiveCALayerParams(
const gfx::CALayerParams& ca_layer_params) override;
+ void DisplayDidCompleteSwapWithSize(const gfx::Size& pixel_size) override;
void DidSwapAfterSnapshotRequestReceived(
const std::vector<ui::LatencyInfo>& latency_info) override;
@@ -68,11 +69,9 @@ class VIZ_SERVICE_EXPORT DirectLayerTreeFrameSink
// mojom::CompositorFrameSinkClient implementation:
void DidReceiveCompositorFrameAck(
const std::vector<ReturnedResource>& resources) override;
- void DidPresentCompositorFrame(uint32_t presentation_token,
- base::TimeTicks time,
- base::TimeDelta refresh,
- uint32_t flags) override;
- void DidDiscardCompositorFrame(uint32_t presentation_token) override;
+ void DidPresentCompositorFrame(
+ uint32_t presentation_token,
+ const gfx::PresentationFeedback& feedback) override;
void OnBeginFrame(const BeginFrameArgs& args) override;
void ReclaimResources(
const std::vector<ReturnedResource>& resources) override;
diff --git a/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc b/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc
index 26f1c30b484..d807033e4da 100644
--- a/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc
+++ b/chromium/components/viz/service/frame_sinks/direct_layer_tree_frame_sink_unittest.cc
@@ -6,7 +6,7 @@
#include <memory>
-#include "base/test/simple_test_tick_clock.h"
+#include "base/test/test_mock_time_task_runner.h"
#include "cc/test/fake_layer_tree_frame_sink_client.h"
#include "components/viz/common/display/renderer_settings.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
@@ -22,7 +22,6 @@
#include "components/viz/test/begin_frame_args_test.h"
#include "components/viz/test/compositor_frame_helpers.h"
#include "components/viz/test/fake_output_surface.h"
-#include "components/viz/test/ordered_simple_task_runner.h"
#include "components/viz/test/test_context_provider.h"
#include "components/viz/test/test_gpu_memory_buffer_manager.h"
#include "components/viz/test/test_shared_bitmap_manager.h"
@@ -64,10 +63,11 @@ class TestCompositorFrameSinkSupportManager
class DirectLayerTreeFrameSinkTest : public testing::Test {
public:
DirectLayerTreeFrameSinkTest()
- : now_src_(new base::SimpleTestTickClock()),
- task_runner_(new cc::OrderedSimpleTaskRunner(now_src_.get(), true)),
+ : task_runner_(base::MakeRefCounted<base::TestMockTimeTaskRunner>(
+ base::TestMockTimeTaskRunner::Type::kStandalone)),
display_size_(1920, 1080),
display_rect_(display_size_),
+ frame_sink_manager_(&bitmap_manager_),
support_manager_(&frame_sink_manager_),
context_provider_(TestContextProvider::Create()) {
auto display_output_surface = FakeOutputSurface::Create3d();
@@ -125,14 +125,13 @@ class DirectLayerTreeFrameSinkTest : public testing::Test {
}
protected:
- std::unique_ptr<base::SimpleTestTickClock> now_src_;
- scoped_refptr<cc::OrderedSimpleTaskRunner> task_runner_;
+ scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
const gfx::Size display_size_;
const gfx::Rect display_rect_;
+ TestSharedBitmapManager bitmap_manager_;
FrameSinkManagerImpl frame_sink_manager_;
TestCompositorFrameSinkSupportManager support_manager_;
- TestSharedBitmapManager bitmap_manager_;
TestGpuMemoryBufferManager gpu_memory_buffer_manager_;
scoped_refptr<TestContextProvider> context_provider_;
diff --git a/chromium/components/viz/service/frame_sinks/external_begin_frame_source_android.cc b/chromium/components/viz/service/frame_sinks/external_begin_frame_source_android.cc
new file mode 100644
index 00000000000..0fff75eb027
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/external_begin_frame_source_android.cc
@@ -0,0 +1,60 @@
+// 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/frame_sinks/external_begin_frame_source_android.h"
+
+#include "base/android/jni_android.h"
+#include "jni/ExternalBeginFrameSourceAndroid_jni.h"
+
+namespace viz {
+
+ExternalBeginFrameSourceAndroid::ExternalBeginFrameSourceAndroid()
+ : ExternalBeginFrameSource(this),
+ j_object_(Java_ExternalBeginFrameSourceAndroid_Constructor(
+ base::android::AttachCurrentThread(),
+ reinterpret_cast<jlong>(this))) {}
+
+ExternalBeginFrameSourceAndroid::~ExternalBeginFrameSourceAndroid() {
+ SetEnabled(false);
+}
+
+void ExternalBeginFrameSourceAndroid::OnVSync(
+ JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj,
+ jlong time_micros,
+ jlong period_micros) {
+ // TODO(ericrk): This logic is ported from window_android.cc. Once OOP-D
+ // conversion is complete, we can delete the logic there.
+
+ // Warning: It is generally unsafe to manufacture TimeTicks values. The
+ // following assumption is being made, AND COULD EASILY BREAK AT ANY TIME:
+ // Upstream, Java code is providing "System.nanos() / 1000," and this is the
+ // same timestamp that would be provided by the CLOCK_MONOTONIC POSIX clock.
+ DCHECK_EQ(base::TimeTicks::GetClock(),
+ base::TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
+ base::TimeTicks frame_time =
+ base::TimeTicks() + base::TimeDelta::FromMicroseconds(time_micros);
+ base::TimeDelta vsync_period(
+ base::TimeDelta::FromMicroseconds(period_micros));
+
+ // Calculate the next frame deadline:
+ base::TimeTicks deadline = frame_time + vsync_period;
+ auto begin_frame_args = BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, source_id(), next_sequence_number_++, frame_time,
+ deadline, vsync_period, BeginFrameArgs::NORMAL);
+
+ OnBeginFrame(begin_frame_args);
+}
+
+void ExternalBeginFrameSourceAndroid::OnNeedsBeginFrames(
+ bool needs_begin_frames) {
+ SetEnabled(needs_begin_frames);
+}
+
+void ExternalBeginFrameSourceAndroid::SetEnabled(bool enabled) {
+ Java_ExternalBeginFrameSourceAndroid_setEnabled(
+ base::android::AttachCurrentThread(), j_object_, enabled);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/external_begin_frame_source_android.h b/chromium/components/viz/service/frame_sinks/external_begin_frame_source_android.h
new file mode 100644
index 00000000000..dfcd26075fe
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/external_begin_frame_source_android.h
@@ -0,0 +1,45 @@
+// 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_FRAME_SINKS_EXTERNAL_BEGIN_FRAME_SOURCE_ANDROID_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_EXTERNAL_BEGIN_FRAME_SOURCE_ANDROID_H_
+
+#include <jni.h>
+
+#include "base/android/jni_weak_ref.h"
+#include "base/macros.h"
+#include "components/viz/common/frame_sinks/begin_frame_source.h"
+#include "components/viz/service/viz_service_export.h"
+
+namespace viz {
+
+// An implementation of ExternalBeginFrameSource which is driven by VSync
+// signals coming from org.chromium.ui.VSyncMonitor.
+class VIZ_SERVICE_EXPORT ExternalBeginFrameSourceAndroid
+ : public ExternalBeginFrameSource,
+ public ExternalBeginFrameSourceClient {
+ public:
+ ExternalBeginFrameSourceAndroid();
+ ~ExternalBeginFrameSourceAndroid() override;
+
+ void OnVSync(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& obj,
+ jlong time_micros,
+ jlong period_micros);
+
+ private:
+ // ExternalBeginFrameSourceClient implementation.
+ void OnNeedsBeginFrames(bool needs_begin_frames) override;
+
+ void SetEnabled(bool enabled);
+
+ uint64_t next_sequence_number_ = BeginFrameArgs::kStartingFrameNumber;
+ base::android::ScopedJavaGlobalRef<jobject> j_object_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExternalBeginFrameSourceAndroid);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_EXTERNAL_BEGIN_FRAME_SOURCE_ANDROID_H_
diff --git a/chromium/components/viz/service/frame_sinks/external_begin_frame_source_android_unittest.cc b/chromium/components/viz/service/frame_sinks/external_begin_frame_source_android_unittest.cc
new file mode 100644
index 00000000000..4907dfa417c
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/external_begin_frame_source_android_unittest.cc
@@ -0,0 +1,76 @@
+// 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/frame_sinks/external_begin_frame_source_android.h"
+
+#include "base/android/java_handler_thread.h"
+#include "base/synchronization/waitable_event.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace viz {
+
+class ExternalBeginFrameSourceAndroidTest : public ::testing::Test,
+ public BeginFrameObserverBase {
+ public:
+ ~ExternalBeginFrameSourceAndroidTest() override { thread_->Stop(); }
+
+ void CreateThread() {
+ thread_ = std::make_unique<base::android::JavaHandlerThread>("TestThread");
+ thread_->Start();
+
+ thread_->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&ExternalBeginFrameSourceAndroidTest::InitOnThread,
+ base::Unretained(this)));
+ }
+
+ void WaitForFrames(uint32_t frame_count) {
+ frames_done_event_.Reset();
+ thread_->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&ExternalBeginFrameSourceAndroidTest::AddObserverOnThread,
+ base::Unretained(this), frame_count));
+ frames_done_event_.Wait();
+ }
+
+ private:
+ void InitOnThread() {
+ begin_frame_source_ = std::make_unique<ExternalBeginFrameSourceAndroid>();
+ }
+
+ void AddObserverOnThread(uint32_t frame_count) {
+ pending_frames_ = frame_count;
+ begin_frame_source_->AddObserver(this);
+ }
+
+ bool OnBeginFrameDerivedImpl(const BeginFrameArgs& args) override {
+ if (pending_frames_ == 0)
+ return false;
+
+ if (--pending_frames_ == 0) {
+ begin_frame_source_->RemoveObserver(this);
+ frames_done_event_.Signal();
+ }
+ return true;
+ }
+ void OnBeginFrameSourcePausedChanged(bool paused) override {}
+
+ base::WaitableEvent frames_done_event_;
+ std::unique_ptr<base::android::JavaHandlerThread> thread_;
+
+ // Only accessed from TestThread.
+ std::unique_ptr<ExternalBeginFrameSourceAndroid> begin_frame_source_;
+ uint32_t pending_frames_ = 0;
+};
+
+TEST_F(ExternalBeginFrameSourceAndroidTest, DeliversFrames) {
+ CreateThread();
+ // Ensure we receive frames. When this returns we are no longer observing the
+ // BeginFrameSource.
+ WaitForFrames(10);
+ // Ensure we can re-observe the same BeginFrameSource and get more frames.
+ WaitForFrames(10);
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/display_embedder/external_begin_frame_controller_impl.cc b/chromium/components/viz/service/frame_sinks/external_begin_frame_source_mojo.cc
index 738c49f7f49..1021eb571b0 100644
--- a/chromium/components/viz/service/display_embedder/external_begin_frame_controller_impl.cc
+++ b/chromium/components/viz/service/frame_sinks/external_begin_frame_source_mojo.cc
@@ -2,22 +2,24 @@
// 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/external_begin_frame_controller_impl.h"
+#include "components/viz/service/frame_sinks/external_begin_frame_source_mojo.h"
namespace viz {
-ExternalBeginFrameControllerImpl::ExternalBeginFrameControllerImpl(
+ExternalBeginFrameSourceMojo::ExternalBeginFrameSourceMojo(
mojom::ExternalBeginFrameControllerAssociatedRequest controller_request,
mojom::ExternalBeginFrameControllerClientPtr client)
- : binding_(this, std::move(controller_request)),
- client_(std::move(client)),
- begin_frame_source_(this) {}
+ : ExternalBeginFrameSource(this),
+ binding_(this, std::move(controller_request)),
+ client_(std::move(client)) {}
-ExternalBeginFrameControllerImpl::~ExternalBeginFrameControllerImpl() = default;
+ExternalBeginFrameSourceMojo::~ExternalBeginFrameSourceMojo() {
+ DCHECK(!display_);
+}
-void ExternalBeginFrameControllerImpl::IssueExternalBeginFrame(
+void ExternalBeginFrameSourceMojo::IssueExternalBeginFrame(
const BeginFrameArgs& args) {
- begin_frame_source_.OnBeginFrame(args);
+ OnBeginFrame(args);
// Ensure that Display will receive the BeginFrame (as a missed one), even
// if it doesn't currently need it. This way, we ensure that
@@ -26,18 +28,23 @@ void ExternalBeginFrameControllerImpl::IssueExternalBeginFrame(
display_->SetNeedsOneBeginFrame();
}
-void ExternalBeginFrameControllerImpl::OnNeedsBeginFrames(
- bool needs_begin_frames) {
+void ExternalBeginFrameSourceMojo::OnNeedsBeginFrames(bool needs_begin_frames) {
needs_begin_frames_ = needs_begin_frames;
client_->OnNeedsBeginFrames(needs_begin_frames_);
}
-void ExternalBeginFrameControllerImpl::OnDisplayDidFinishFrame(
+void ExternalBeginFrameSourceMojo::OnDisplayDidFinishFrame(
const BeginFrameAck& ack) {
client_->OnDisplayDidFinishFrame(ack);
}
-void ExternalBeginFrameControllerImpl::SetDisplay(Display* display) {
+void ExternalBeginFrameSourceMojo::OnDisplayDestroyed() {
+ // As part of destruction, we are automatically removed as a display
+ // observer. No need to call RemoveObserver.
+ display_ = nullptr;
+}
+
+void ExternalBeginFrameSourceMojo::SetDisplay(Display* display) {
if (display_)
display_->RemoveObserver(this);
display_ = display;
diff --git a/chromium/components/viz/service/display_embedder/external_begin_frame_controller_impl.h b/chromium/components/viz/service/frame_sinks/external_begin_frame_source_mojo.h
index ac6d1d09e5b..b489a8036a7 100644
--- a/chromium/components/viz/service/display_embedder/external_begin_frame_controller_impl.h
+++ b/chromium/components/viz/service/frame_sinks/external_begin_frame_source_mojo.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_VIZ_SERVICE_DISPLAY_EMBEDDER_EXTERNAL_BEGIN_FRAME_CONTROLLER_IMPL_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_EXTERNAL_BEGIN_FRAME_CONTROLLER_IMPL_H_
+#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_EXTERNAL_BEGIN_FRAME_SOURCE_MOJO_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_EXTERNAL_BEGIN_FRAME_SOURCE_MOJO_H_
#include <memory>
@@ -15,25 +15,24 @@
namespace viz {
-// In-process implementation of the ExternalBeginFrameController interface.
-// Owns an ExternalBeginFrameSource that replaces the Display's default
-// BeginFrameSource. Observes the Display to be notified of BeginFrame
+// Implementation of ExternalBeginFrameSource that's controlled by IPCs over
+// the mojom::ExternalBeginFrameController interface. Replaces the Display's
+// default BeginFrameSource. Observes the Display to be notified of BeginFrame
// completion.
-class VIZ_SERVICE_EXPORT ExternalBeginFrameControllerImpl
+class VIZ_SERVICE_EXPORT ExternalBeginFrameSourceMojo
: public mojom::ExternalBeginFrameController,
public ExternalBeginFrameSourceClient,
- public DisplayObserver {
+ public DisplayObserver,
+ public ExternalBeginFrameSource {
public:
- ExternalBeginFrameControllerImpl(
+ ExternalBeginFrameSourceMojo(
mojom::ExternalBeginFrameControllerAssociatedRequest controller_request,
mojom::ExternalBeginFrameControllerClientPtr client);
- ~ExternalBeginFrameControllerImpl() override;
+ ~ExternalBeginFrameSourceMojo() override;
// mojom::ExternalBeginFrameController implementation.
void IssueExternalBeginFrame(const BeginFrameArgs& args) override;
- BeginFrameSource* begin_frame_source() { return &begin_frame_source_; }
-
void SetDisplay(Display* display);
private:
@@ -42,15 +41,15 @@ class VIZ_SERVICE_EXPORT ExternalBeginFrameControllerImpl
// DisplayObserver implementation.
void OnDisplayDidFinishFrame(const BeginFrameAck& ack) override;
+ void OnDisplayDestroyed() override;
mojo::AssociatedBinding<mojom::ExternalBeginFrameController> binding_;
mojom::ExternalBeginFrameControllerClientPtr client_;
- ExternalBeginFrameSource begin_frame_source_;
bool needs_begin_frames_ = false;
Display* display_ = nullptr;
};
} // namespace viz
-#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_EXTERNAL_BEGIN_FRAME_CONTROLLER_IMPL_H_
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_EXTERNAL_BEGIN_FRAME_SOURCE_MOJO_H_
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 bb147945b45..bb6789baf76 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
@@ -9,9 +9,8 @@
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
-#include "components/viz/service/display/display.h"
+#include "components/viz/service/display/shared_bitmap_manager.h"
#include "components/viz/service/display_embedder/display_provider.h"
-#include "components/viz/service/display_embedder/external_begin_frame_controller_impl.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_impl.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "components/viz/service/frame_sinks/primary_begin_frame_source.h"
@@ -19,10 +18,6 @@
#include "components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h"
#include "components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h"
-#if DCHECK_IS_ON()
-#include <sstream>
-#endif
-
namespace viz {
FrameSinkManagerImpl::FrameSinkSourceMapping::FrameSinkSourceMapping() =
@@ -38,10 +33,19 @@ FrameSinkManagerImpl::FrameSinkSourceMapping&
FrameSinkManagerImpl::FrameSinkSourceMapping::operator=(
FrameSinkSourceMapping&& other) = default;
+FrameSinkManagerImpl::FrameSinkData::FrameSinkData() = default;
+FrameSinkManagerImpl::FrameSinkData::FrameSinkData(FrameSinkData&& other) =
+ default;
+FrameSinkManagerImpl::FrameSinkData::~FrameSinkData() = default;
+FrameSinkManagerImpl::FrameSinkData& FrameSinkManagerImpl::FrameSinkData::
+operator=(FrameSinkData&& other) = default;
+
FrameSinkManagerImpl::FrameSinkManagerImpl(
+ SharedBitmapManager* shared_bitmap_manager,
base::Optional<uint32_t> activation_deadline_in_frames,
DisplayProvider* display_provider)
- : display_provider_(display_provider),
+ : shared_bitmap_manager_(shared_bitmap_manager),
+ display_provider_(display_provider),
surface_manager_(activation_deadline_in_frames),
hit_test_manager_(surface_manager()),
binding_(this) {
@@ -81,10 +85,20 @@ void FrameSinkManagerImpl::SetLocalClient(
client_ = client;
}
+void FrameSinkManagerImpl::ForceShutdown() {
+ if (binding_.is_bound())
+ binding_.Close();
+
+ sink_map_.clear();
+}
+
void FrameSinkManagerImpl::RegisterFrameSinkId(
const FrameSinkId& frame_sink_id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- surface_manager_.RegisterFrameSinkId(frame_sink_id);
+ DCHECK(!base::ContainsKey(frame_sink_data_, frame_sink_id));
+
+ frame_sink_data_.emplace(std::make_pair(frame_sink_id, FrameSinkData()));
+
if (video_detector_)
video_detector_->OnFrameSinkIdRegistered(frame_sink_id);
@@ -103,28 +117,27 @@ void FrameSinkManagerImpl::InvalidateFrameSinkId(
if (video_detector_)
video_detector_->OnFrameSinkIdInvalidated(frame_sink_id);
- synchronization_event_labels_.erase(frame_sink_id);
-
// Destroy the [Root]CompositorFrameSinkImpl if there is one.
sink_map_.erase(frame_sink_id);
+
+ frame_sink_data_.erase(frame_sink_id);
}
void FrameSinkManagerImpl::EnableSynchronizationReporting(
const FrameSinkId& frame_sink_id,
const std::string& reporting_label) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // TODO(fsamuel): We should move FrameSink labels over to
- // FrameSinkManagerImpl and unify them with synchronization event labels
- // and other metadata about FrameSinks.
- DCHECK_GT(surface_manager_.valid_frame_sink_labels().count(frame_sink_id),
- 0u);
- synchronization_event_labels_.emplace(frame_sink_id, reporting_label);
+ auto it = frame_sink_data_.find(frame_sink_id);
+ if (it != frame_sink_data_.end())
+ it->second.synchronization_label = reporting_label;
}
void FrameSinkManagerImpl::SetFrameSinkDebugLabel(
const FrameSinkId& frame_sink_id,
const std::string& debug_label) {
- surface_manager_.SetFrameSinkDebugLabel(frame_sink_id, debug_label);
+ auto it = frame_sink_data_.find(frame_sink_id);
+ if (it != frame_sink_data_.end())
+ it->second.debug_label = debug_label;
}
void FrameSinkManagerImpl::CreateRootCompositorFrameSink(
@@ -133,40 +146,14 @@ void FrameSinkManagerImpl::CreateRootCompositorFrameSink(
DCHECK(!base::ContainsKey(sink_map_, params->frame_sink_id));
DCHECK(display_provider_);
- std::unique_ptr<ExternalBeginFrameControllerImpl>
- external_begin_frame_controller;
- if (params->external_begin_frame_controller.is_pending() &&
- params->external_begin_frame_controller_client) {
- external_begin_frame_controller =
- std::make_unique<ExternalBeginFrameControllerImpl>(
- std::move(params->external_begin_frame_controller),
- mojom::ExternalBeginFrameControllerClientPtr(
- std::move(params->external_begin_frame_controller_client)));
- }
+ // We are transfering ownership of |params| so remember FrameSinkId here.
+ FrameSinkId frame_sink_id = params->frame_sink_id;
- mojom::DisplayClientPtr display_client(std::move(params->display_client));
-
- std::unique_ptr<SyntheticBeginFrameSource> begin_frame_source;
- auto display = display_provider_->CreateDisplay(
- params->frame_sink_id, params->widget, params->gpu_compositing,
- display_client.get(), external_begin_frame_controller.get(),
- params->renderer_settings, &begin_frame_source);
-
- // Creating display failed. Drop the CompositorFrameSink message pipes here
- // and let host send a new request, potential with a different compositing
- // mode.
- if (!display)
- return;
-
- sink_map_[params->frame_sink_id] =
- std::make_unique<RootCompositorFrameSinkImpl>(
- this, params->frame_sink_id, std::move(display),
- std::move(begin_frame_source),
- std::move(external_begin_frame_controller),
- std::move(params->compositor_frame_sink),
- mojom::CompositorFrameSinkClientPtr(
- std::move(params->compositor_frame_sink_client)),
- std::move(params->display_private), std::move(display_client));
+ // Creating RootCompositorFrameSinkImpl can fail and return null.
+ auto root_compositor_frame_sink = RootCompositorFrameSinkImpl::Create(
+ std::move(params), this, display_provider_);
+ if (root_compositor_frame_sink)
+ sink_map_[frame_sink_id] = std::move(root_compositor_frame_sink);
}
void FrameSinkManagerImpl::CreateCompositorFrameSink(
@@ -265,8 +252,10 @@ void FrameSinkManagerImpl::DropTemporaryReference(const SurfaceId& surface_id) {
void FrameSinkManagerImpl::AddVideoDetectorObserver(
mojom::VideoDetectorObserverPtr observer) {
- if (!video_detector_)
- video_detector_ = std::make_unique<VideoDetector>(&surface_manager_);
+ if (!video_detector_) {
+ video_detector_ = std::make_unique<VideoDetector>(
+ GetRegisteredFrameSinkIds(), &surface_manager_);
+ }
video_detector_->AddObserver(std::move(observer));
}
@@ -328,13 +317,18 @@ void FrameSinkManagerImpl::OnSurfaceActivated(
// If |duration| is populated then there was a synchronization event prior
// to this activation.
- auto it = synchronization_event_labels_.find(surface_id.frame_sink_id());
- if (it != synchronization_event_labels_.end()) {
- TRACE_EVENT_INSTANT2(
- "viz", "SurfaceSynchronizationEvent", TRACE_EVENT_SCOPE_THREAD,
- "duration_ms", duration->InMilliseconds(), "client_label", it->second);
- base::UmaHistogramCustomCounts(it->second, duration->InMilliseconds(), 1,
- 10000, 50);
+ auto it = frame_sink_data_.find(surface_id.frame_sink_id());
+ if (it == frame_sink_data_.end())
+ return;
+
+ std::string& synchronization_label = it->second.synchronization_label;
+ if (!synchronization_label.empty()) {
+ TRACE_EVENT_INSTANT2("viz", "SurfaceSynchronizationEvent",
+ TRACE_EVENT_SCOPE_THREAD, "duration_ms",
+ duration->InMilliseconds(), "client_label",
+ synchronization_label);
+ base::UmaHistogramCustomCounts(synchronization_label,
+ duration->InMilliseconds(), 1, 10000, 50);
}
}
@@ -527,8 +521,8 @@ VideoDetector* FrameSinkManagerImpl::CreateVideoDetectorForTesting(
const base::TickClock* tick_clock,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
DCHECK(!video_detector_);
- video_detector_ = std::make_unique<VideoDetector>(surface_manager(),
- tick_clock, task_runner);
+ video_detector_ = std::make_unique<VideoDetector>(
+ GetRegisteredFrameSinkIds(), surface_manager(), tick_clock, task_runner);
return video_detector_.get();
}
@@ -540,6 +534,14 @@ void FrameSinkManagerImpl::RemoveObserver(FrameSinkObserver* obs) {
observer_list_.RemoveObserver(obs);
}
+base::StringPiece FrameSinkManagerImpl::GetFrameSinkDebugLabel(
+ const FrameSinkId& frame_sink_id) const {
+ auto it = frame_sink_data_.find(frame_sink_id);
+ if (it != frame_sink_data_.end())
+ return it->second.debug_label;
+ return base::StringPiece();
+}
+
std::vector<FrameSinkId> FrameSinkManagerImpl::GetCreatedFrameSinkIds() const {
std::vector<FrameSinkId> frame_sink_ids;
for (auto& map_entry : support_map_)
@@ -550,7 +552,7 @@ std::vector<FrameSinkId> FrameSinkManagerImpl::GetCreatedFrameSinkIds() const {
std::vector<FrameSinkId> FrameSinkManagerImpl::GetRegisteredFrameSinkIds()
const {
std::vector<FrameSinkId> frame_sink_ids;
- for (auto& map_entry : surface_manager_.valid_frame_sink_labels())
+ for (auto& map_entry : frame_sink_data_)
frame_sink_ids.push_back(map_entry.first);
return 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 7f2352e9367..4bc577eb5f3 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
@@ -18,6 +18,7 @@
#include "base/macros.h"
#include "base/optional.h"
#include "base/single_thread_task_runner.h"
+#include "base/strings/string_piece.h"
#include "base/threading/thread_checker.h"
#include "components/viz/common/constants.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
@@ -37,10 +38,10 @@
#include "services/viz/public/interfaces/compositing/video_detector_observer.mojom.h"
namespace viz {
-
class CapturableFrameSink;
class CompositorFrameSinkSupport;
class DisplayProvider;
+class SharedBitmapManager;
// FrameSinkManagerImpl manages BeginFrame hierarchy. This is the implementation
// detail for FrameSinkManagerImpl.
@@ -50,11 +51,17 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl
public mojom::FrameSinkManager,
public HitTestAggregatorDelegate {
public:
- FrameSinkManagerImpl(base::Optional<uint32_t> activation_deadline_in_frames =
- kDefaultActivationDeadlineInFrames,
- DisplayProvider* display_provider = nullptr);
+ explicit FrameSinkManagerImpl(
+ SharedBitmapManager* shared_bitmap_manager,
+ base::Optional<uint32_t> activation_deadline_in_frames =
+ kDefaultActivationDeadlineInFrames,
+ DisplayProvider* display_provider = nullptr);
~FrameSinkManagerImpl() override;
+ // Performs cleanup needed to force shutdown from the GPU process. Stops all
+ // incoming IPCs and destroys all [Root]CompositorFrameSinkImpls.
+ void ForceShutdown();
+
// Binds |this| as a FrameSinkManagerImpl for |request| on |task_runner|. On
// Mac |task_runner| will be the resize helper task runner. May only be called
// once.
@@ -142,8 +149,10 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl
BeginFrameSource* GetPrimaryBeginFrameSource();
SurfaceManager* surface_manager() { return &surface_manager_; }
-
const HitTestManager* hit_test_manager() { return &hit_test_manager_; }
+ SharedBitmapManager* shared_bitmap_manager() {
+ return shared_bitmap_manager_;
+ }
void SubmitHitTestRegionList(
const SurfaceId& surface_id,
@@ -163,6 +172,10 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl
void AddObserver(FrameSinkObserver* obs);
void RemoveObserver(FrameSinkObserver* obs);
+ // Returns the debug label associated with |frame_sink_id| if any.
+ base::StringPiece GetFrameSinkDebugLabel(
+ const FrameSinkId& frame_sink_id) const;
+
// Returns ids of all FrameSinks that were created.
std::vector<FrameSinkId> GetCreatedFrameSinkIds() const;
// Returns ids of all FrameSinks that were registered.
@@ -178,6 +191,23 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl
private:
friend class FrameSinkManagerTest;
+ // Metadata for a CompositorFrameSink.
+ struct FrameSinkData {
+ FrameSinkData();
+ FrameSinkData(FrameSinkData&& other);
+ ~FrameSinkData();
+ FrameSinkData& operator=(FrameSinkData&& other);
+
+ // A label to identify frame sink.
+ std::string debug_label;
+
+ // Record synchronization events for this FrameSinkId if not empty.
+ std::string synchronization_label;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FrameSinkData);
+ };
+
// BeginFrameSource routing information for a FrameSinkId.
struct FrameSinkSourceMapping {
FrameSinkSourceMapping();
@@ -210,9 +240,25 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl
bool ChildContains(const FrameSinkId& child_frame_sink_id,
const FrameSinkId& search_frame_sink_id) const;
+ // SharedBitmapManager for the viz display service for receiving software
+ // resources in CompositorFrameSinks.
+ SharedBitmapManager* const shared_bitmap_manager_;
// Provides a Display for CreateRootCompositorFrameSink().
DisplayProvider* const display_provider_;
+ PrimaryBeginFrameSource primary_source_;
+
+ // Must be created after and destroyed before |primary_source_|.
+ SurfaceManager surface_manager_;
+
+ // Must be created after and destroyed before |surface_manager_|.
+ HitTestManager hit_test_manager_;
+
+ // Contains registered frame sink ids, debug labels and synchronization
+ // labels. Map entries will be created when frame sink is registered and
+ // destroyed when frame sink is invalidated.
+ base::flat_map<FrameSinkId, FrameSinkData> frame_sink_data_;
+
// Set of BeginFrameSource along with associated FrameSinkIds. Any child
// that is implicitly using this frame sink must be reachable by the
// parent in the dag.
@@ -229,18 +275,6 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl
base::flat_map<FrameSinkId, std::unique_ptr<mojom::CompositorFrameSink>>
sink_map_;
- // The set of FrameSinkIds that the client wants synchronization event
- // notifications for.
- base::flat_map<FrameSinkId, std::string> synchronization_event_labels_;
-
- PrimaryBeginFrameSource primary_source_;
-
- // |surface_manager_| should be placed under |primary_source_| so that all
- // surfaces are destroyed before |primary_source_|.
- SurfaceManager surface_manager_;
-
- HitTestManager hit_test_manager_;
-
base::flat_set<std::unique_ptr<FrameSinkVideoCapturerImpl>,
base::UniquePtrComparator>
video_capturers_;
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 3f17acac088..a18c25db64c 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
@@ -12,6 +12,7 @@
#include "components/viz/common/constants.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "components/viz/test/begin_frame_source_test.h"
#include "components/viz/test/compositor_frame_helpers.h"
@@ -55,7 +56,9 @@ struct RootCompositorFrameSinkData {
class FrameSinkManagerTest : public testing::Test {
public:
FrameSinkManagerTest()
- : manager_(kDefaultActivationDeadlineInFrames, &display_provider_) {}
+ : manager_(&shared_bitmap_manager_,
+ kDefaultActivationDeadlineInFrames,
+ &display_provider_) {}
~FrameSinkManagerTest() override = default;
std::unique_ptr<CompositorFrameSinkSupport> CreateCompositorFrameSinkSupport(
@@ -81,9 +84,13 @@ class FrameSinkManagerTest : public testing::Test {
// Make sure test cleans up all [Root]CompositorFrameSinkImpls.
EXPECT_TRUE(manager_.support_map_.empty());
+
+ // Make sure test has invalidated all registered FrameSinkIds.
+ EXPECT_TRUE(manager_.frame_sink_data_.empty());
}
protected:
+ ServerSharedBitmapManager shared_bitmap_manager_;
TestDisplayProvider display_provider_;
FrameSinkManagerImpl manager_;
};
@@ -431,6 +438,19 @@ TEST_F(FrameSinkManagerTest, EvictSurfaces) {
EXPECT_FALSE(manager_.surface_manager()->GetSurfaceForId(surface_id2));
}
+// Verify that setting debug label works and that debug labels are cleared when
+// FrameSinkId is invalidated.
+TEST_F(FrameSinkManagerTest, DebugLabel) {
+ const std::string label = "Test Label";
+
+ manager_.RegisterFrameSinkId(kFrameSinkIdA);
+ manager_.SetFrameSinkDebugLabel(kFrameSinkIdA, label);
+ EXPECT_EQ(label, manager_.GetFrameSinkDebugLabel(kFrameSinkIdA));
+
+ manager_.InvalidateFrameSinkId(kFrameSinkIdA);
+ EXPECT_EQ("", manager_.GetFrameSinkDebugLabel(kFrameSinkIdA));
+}
+
namespace {
enum RegisterOrder { REGISTER_HIERARCHY_FIRST, REGISTER_CLIENTS_FIRST };
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 654022b75a3..5777dddbe43 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
@@ -6,64 +6,108 @@
#include <utility>
+#include "base/compiler_specific.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/service/display/display.h"
-#include "components/viz/service/display_embedder/external_begin_frame_controller_impl.h"
+#include "components/viz/service/display_embedder/display_provider.h"
+#include "components/viz/service/frame_sinks/external_begin_frame_source_mojo.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "components/viz/service/hit_test/hit_test_aggregator.h"
+#if defined(OS_ANDROID)
+#include "components/viz/service/frame_sinks/external_begin_frame_source_android.h"
+#endif
+
namespace viz {
-RootCompositorFrameSinkImpl::RootCompositorFrameSinkImpl(
+// static
+std::unique_ptr<RootCompositorFrameSinkImpl>
+RootCompositorFrameSinkImpl::Create(
+ mojom::RootCompositorFrameSinkParamsPtr params,
FrameSinkManagerImpl* frame_sink_manager,
- const FrameSinkId& frame_sink_id,
- std::unique_ptr<Display> display,
- std::unique_ptr<SyntheticBeginFrameSource> synthetic_begin_frame_source,
- std::unique_ptr<ExternalBeginFrameControllerImpl>
- external_begin_frame_controller,
- mojom::CompositorFrameSinkAssociatedRequest request,
- mojom::CompositorFrameSinkClientPtr client,
- mojom::DisplayPrivateAssociatedRequest display_private_request,
- mojom::DisplayClientPtr display_client)
- : compositor_frame_sink_client_(std::move(client)),
- compositor_frame_sink_binding_(this, std::move(request)),
- display_client_(std::move(display_client)),
- display_private_binding_(this, std::move(display_private_request)),
- support_(std::make_unique<CompositorFrameSinkSupport>(
- compositor_frame_sink_client_.get(),
- frame_sink_manager,
- frame_sink_id,
- true /* is_root */,
- true /* needs_sync_points */)),
- synthetic_begin_frame_source_(std::move(synthetic_begin_frame_source)),
- external_begin_frame_controller_(
- std::move(external_begin_frame_controller)),
- display_(std::move(display)) {
- DCHECK(begin_frame_source());
- DCHECK(display_);
+ DisplayProvider* display_provider) {
+ // First create some sort of a BeginFrameSource, depending on the platform
+ // and |params|.
+ std::unique_ptr<ExternalBeginFrameSource> external_begin_frame_source;
+ std::unique_ptr<SyntheticBeginFrameSource> synthetic_begin_frame_source;
+ ExternalBeginFrameSourceMojo* external_begin_frame_source_mojo = nullptr;
- compositor_frame_sink_binding_.set_connection_error_handler(
- base::Bind(&RootCompositorFrameSinkImpl::OnClientConnectionLost,
- base::Unretained(this)));
- if (external_begin_frame_controller_)
- external_begin_frame_controller_->SetDisplay(display_.get());
- frame_sink_manager->RegisterBeginFrameSource(begin_frame_source(),
- frame_sink_id);
- display_->Initialize(this, frame_sink_manager->surface_manager());
- support_->SetUpHitTest(display_.get());
+ if (params->external_begin_frame_controller.is_pending() &&
+ params->external_begin_frame_controller_client) {
+ auto owned_external_begin_frame_source_mojo =
+ std::make_unique<ExternalBeginFrameSourceMojo>(
+ std::move(params->external_begin_frame_controller),
+ mojom::ExternalBeginFrameControllerClientPtr(
+ std::move(params->external_begin_frame_controller_client)));
+ external_begin_frame_source_mojo =
+ owned_external_begin_frame_source_mojo.get();
+ external_begin_frame_source =
+ std::move(owned_external_begin_frame_source_mojo);
+ } else {
+#if defined(OS_ANDROID)
+ external_begin_frame_source =
+ std::make_unique<ExternalBeginFrameSourceAndroid>();
+#else
+ synthetic_begin_frame_source = std::make_unique<DelayBasedBeginFrameSource>(
+ std::make_unique<DelayBasedTimeSource>(
+ base::ThreadTaskRunnerHandle::Get().get()),
+ display_provider->GetRestartId());
+#endif
+ }
+
+ // |impl| isn't ready to use until after a display has been created for it and
+ // Initialize() has been called.
+ auto impl = base::WrapUnique(new RootCompositorFrameSinkImpl(
+ frame_sink_manager, params->frame_sink_id,
+ std::move(params->compositor_frame_sink),
+ mojom::CompositorFrameSinkClientPtr(
+ std::move(params->compositor_frame_sink_client)),
+ std::move(params->display_private),
+ mojom::DisplayClientPtr(std::move(params->display_client)),
+ std::move(synthetic_begin_frame_source),
+ std::move(external_begin_frame_source)));
+
+ auto display = display_provider->CreateDisplay(
+ params->frame_sink_id, params->widget, params->gpu_compositing,
+ impl->display_client_.get(), impl->external_begin_frame_source_.get(),
+ impl->synthetic_begin_frame_source_.get(), params->renderer_settings,
+ params->send_swap_size_notifications);
+
+ // Creating a display failed. Destroy |impl| which will close the message
+ // pipes. The host can send a new request, potential with a different
+ // compositing mode.
+ if (!display)
+ return nullptr;
+
+ if (external_begin_frame_source_mojo)
+ external_begin_frame_source_mojo->SetDisplay(display.get());
+
+ impl->Initialize(std::move(display));
+
+ return impl;
}
RootCompositorFrameSinkImpl::~RootCompositorFrameSinkImpl() {
support_->frame_sink_manager()->UnregisterBeginFrameSource(
begin_frame_source());
- if (external_begin_frame_controller_)
- external_begin_frame_controller_->SetDisplay(nullptr);
}
void RootCompositorFrameSinkImpl::SetDisplayVisible(bool visible) {
display_->SetVisible(visible);
}
+void RootCompositorFrameSinkImpl::DisableSwapUntilResize(
+ DisableSwapUntilResizeCallback callback) {
+ display_->Resize(gfx::Size());
+ std::move(callback).Run();
+}
+
+void RootCompositorFrameSinkImpl::Resize(const gfx::Size& size) {
+ display_->Resize(size);
+}
+
void RootCompositorFrameSinkImpl::SetDisplayColorMatrix(
const gfx::Transform& color_matrix) {
display_->SetColorMatrix(color_matrix.matrix());
@@ -105,16 +149,13 @@ void RootCompositorFrameSinkImpl::SubmitCompositorFrame(
CompositorFrame frame,
base::Optional<HitTestRegionList> hit_test_region_list,
uint64_t submit_time) {
- // Update display when size or local surface id changes.
- if (support_->last_activated_local_surface_id() != local_surface_id) {
- display_->Resize(frame.size_in_pixels());
+ if (support_->last_activated_local_surface_id() != local_surface_id)
display_->SetLocalSurfaceId(local_surface_id, frame.device_scale_factor());
- }
const auto result = support_->MaybeSubmitCompositorFrame(
local_surface_id, std::move(frame), std::move(hit_test_region_list),
- SubmitCompositorFrameSyncCallback());
- if (result == CompositorFrameSinkSupport::ACCEPTED)
+ submit_time, SubmitCompositorFrameSyncCallback());
+ if (result == SubmitResult::ACCEPTED)
return;
const char* reason =
@@ -123,7 +164,6 @@ void RootCompositorFrameSinkImpl::SubmitCompositorFrame(
<< " because " << reason;
compositor_frame_sink_binding_.CloseWithReason(static_cast<uint32_t>(result),
reason);
- OnClientConnectionLost();
}
void RootCompositorFrameSinkImpl::SubmitCompositorFrameSync(
@@ -147,7 +187,6 @@ void RootCompositorFrameSinkImpl::DidAllocateSharedBitmap(
DLOG(ERROR) << "DidAllocateSharedBitmap failed for duplicate "
<< "SharedBitmapId";
compositor_frame_sink_binding_.Close();
- OnClientConnectionLost();
}
}
@@ -156,9 +195,47 @@ void RootCompositorFrameSinkImpl::DidDeleteSharedBitmap(
support_->DidDeleteSharedBitmap(id);
}
+RootCompositorFrameSinkImpl::RootCompositorFrameSinkImpl(
+ FrameSinkManagerImpl* frame_sink_manager,
+ const FrameSinkId& frame_sink_id,
+ mojom::CompositorFrameSinkAssociatedRequest frame_sink_request,
+ mojom::CompositorFrameSinkClientPtr frame_sink_client,
+ mojom::DisplayPrivateAssociatedRequest display_request,
+ mojom::DisplayClientPtr display_client,
+ std::unique_ptr<SyntheticBeginFrameSource> synthetic_begin_frame_source,
+ std::unique_ptr<ExternalBeginFrameSource> external_begin_frame_source)
+ : compositor_frame_sink_client_(std::move(frame_sink_client)),
+ compositor_frame_sink_binding_(this, std::move(frame_sink_request)),
+ display_client_(std::move(display_client)),
+ display_private_binding_(this, std::move(display_request)),
+ support_(std::make_unique<CompositorFrameSinkSupport>(
+ compositor_frame_sink_client_.get(),
+ frame_sink_manager,
+ frame_sink_id,
+ /*is_root=*/true,
+ /*needs_sync_points=*/true)),
+ synthetic_begin_frame_source_(std::move(synthetic_begin_frame_source)),
+ external_begin_frame_source_(std::move(external_begin_frame_source)) {
+ DCHECK(begin_frame_source());
+
+ frame_sink_manager->RegisterBeginFrameSource(begin_frame_source(),
+ support_->frame_sink_id());
+}
+
+void RootCompositorFrameSinkImpl::Initialize(std::unique_ptr<Display> display) {
+ display_ = std::move(display);
+ DCHECK(display_);
+
+ display_->Initialize(this, support_->frame_sink_manager()->surface_manager());
+ support_->SetUpHitTest(display_.get());
+}
+
void RootCompositorFrameSinkImpl::DisplayOutputSurfaceLost() {
- // TODO(staraz): Implement this. Client should hear about context/output
- // surface lost.
+ // |display_| has encountered an error and needs to be recreated. Close
+ // message pipes from the client, the client will see the connection error and
+ // recreate the CompositorFrameSink+Display.
+ compositor_frame_sink_binding_.Close();
+ display_private_binding_.Close();
}
void RootCompositorFrameSinkImpl::DisplayWillDrawAndSwap(
@@ -170,11 +247,27 @@ void RootCompositorFrameSinkImpl::DisplayWillDrawAndSwap(
void RootCompositorFrameSinkImpl::DisplayDidReceiveCALayerParams(
const gfx::CALayerParams& ca_layer_params) {
+#if defined(OS_MACOSX)
// If |ca_layer_params| should have content only when there exists a client
// to send it to.
DCHECK(ca_layer_params.is_empty || display_client_);
if (display_client_)
display_client_->OnDisplayReceivedCALayerParams(ca_layer_params);
+#else
+ NOTREACHED();
+ ALLOW_UNUSED_LOCAL(display_client_);
+#endif
+}
+
+void RootCompositorFrameSinkImpl::DisplayDidCompleteSwapWithSize(
+ const gfx::Size& pixel_size) {
+#if defined(OS_ANDROID)
+ if (display_client_)
+ display_client_->DidCompleteSwapWithSize(pixel_size);
+#else
+ NOTREACHED();
+ ALLOW_UNUSED_LOCAL(display_client_);
+#endif
}
void RootCompositorFrameSinkImpl::DidSwapAfterSnapshotRequestReceived(
@@ -184,15 +277,9 @@ void RootCompositorFrameSinkImpl::DidSwapAfterSnapshotRequestReceived(
void RootCompositorFrameSinkImpl::DisplayDidDrawAndSwap() {}
-void RootCompositorFrameSinkImpl::OnClientConnectionLost() {
- // TODO(kylechar): I'm not sure what we need to do here. If |this| is
- // destroyed then |display_| will be destroyed and we'll stop producing
- // frames.
-}
-
BeginFrameSource* RootCompositorFrameSinkImpl::begin_frame_source() {
- if (external_begin_frame_controller_)
- return external_begin_frame_controller_->begin_frame_source();
+ if (external_begin_frame_source_)
+ return external_begin_frame_source_.get();
return synthetic_begin_frame_source_.get();
}
diff --git a/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h b/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
index 35295866214..99eb2b434c6 100644
--- a/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
+++ b/chromium/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
@@ -13,12 +13,14 @@
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "services/viz/privileged/interfaces/compositing/display_private.mojom.h"
+#include "services/viz/privileged/interfaces/compositing/frame_sink_manager.mojom.h"
#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
namespace viz {
class Display;
-class ExternalBeginFrameControllerImpl;
+class DisplayProvider;
+class ExternalBeginFrameSource;
class FrameSinkManagerImpl;
class SyntheticBeginFrameSource;
@@ -28,22 +30,18 @@ class RootCompositorFrameSinkImpl : public mojom::CompositorFrameSink,
public mojom::DisplayPrivate,
public DisplayClient {
public:
- RootCompositorFrameSinkImpl(
+ // Creates a new RootCompositorFrameSinkImpl.
+ static std::unique_ptr<RootCompositorFrameSinkImpl> Create(
+ mojom::RootCompositorFrameSinkParamsPtr params,
FrameSinkManagerImpl* frame_sink_manager,
- const FrameSinkId& frame_sink_id,
- std::unique_ptr<Display> display,
- std::unique_ptr<SyntheticBeginFrameSource> begin_frame_source,
- std::unique_ptr<ExternalBeginFrameControllerImpl>
- external_begin_frame_controller,
- mojom::CompositorFrameSinkAssociatedRequest request,
- mojom::CompositorFrameSinkClientPtr client,
- mojom::DisplayPrivateAssociatedRequest display_private_request,
- mojom::DisplayClientPtr display_client);
+ DisplayProvider* display_provider);
~RootCompositorFrameSinkImpl() override;
// mojom::DisplayPrivate:
void SetDisplayVisible(bool visible) override;
+ void DisableSwapUntilResize(DisableSwapUntilResizeCallback callback) override;
+ void Resize(const gfx::Size& size) override;
void SetDisplayColorMatrix(const gfx::Transform& color_matrix) override;
void SetDisplayColorSpace(const gfx::ColorSpace& blending_color_space,
const gfx::ColorSpace& device_color_space) override;
@@ -72,6 +70,19 @@ class RootCompositorFrameSinkImpl : public mojom::CompositorFrameSink,
SubmitCompositorFrameSyncCallback callback) override;
private:
+ RootCompositorFrameSinkImpl(
+ FrameSinkManagerImpl* frame_sink_manager,
+ const FrameSinkId& frame_sink_id,
+ mojom::CompositorFrameSinkAssociatedRequest frame_sink_request,
+ mojom::CompositorFrameSinkClientPtr frame_sink_client,
+ mojom::DisplayPrivateAssociatedRequest display_request,
+ mojom::DisplayClientPtr display_client,
+ std::unique_ptr<SyntheticBeginFrameSource> synthetic_begin_frame_source,
+ std::unique_ptr<ExternalBeginFrameSource> external_begin_frame_source);
+
+ // Initializes this object so it will start producing frames with |display|.
+ void Initialize(std::unique_ptr<Display> display);
+
// DisplayClient:
void DisplayOutputSurfaceLost() override;
void DisplayWillDrawAndSwap(bool will_draw_and_swap,
@@ -79,11 +90,10 @@ class RootCompositorFrameSinkImpl : public mojom::CompositorFrameSink,
void DisplayDidDrawAndSwap() override;
void DisplayDidReceiveCALayerParams(
const gfx::CALayerParams& ca_layer_params) override;
+ void DisplayDidCompleteSwapWithSize(const gfx::Size& pixel_size) override;
void DidSwapAfterSnapshotRequestReceived(
const std::vector<ui::LatencyInfo>& latency_info) override;
- void OnClientConnectionLost();
-
BeginFrameSource* begin_frame_source();
mojom::CompositorFrameSinkClientPtr compositor_frame_sink_client_;
@@ -101,8 +111,7 @@ class RootCompositorFrameSinkImpl : public mojom::CompositorFrameSink,
// it was created with a non-null gpu::SurfaceHandle.
std::unique_ptr<SyntheticBeginFrameSource> synthetic_begin_frame_source_;
// If non-null, |synthetic_begin_frame_source_| will not exist.
- std::unique_ptr<ExternalBeginFrameControllerImpl>
- external_begin_frame_controller_;
+ std::unique_ptr<ExternalBeginFrameSource> external_begin_frame_source_;
std::unique_ptr<Display> display_;
DISALLOW_COPY_AND_ASSIGN(RootCompositorFrameSinkImpl);
diff --git a/chromium/components/viz/service/frame_sinks/surface_references_unittest.cc b/chromium/components/viz/service/frame_sinks/surface_references_unittest.cc
index eb69755a317..32ee610b808 100644
--- a/chromium/components/viz/service/frame_sinks/surface_references_unittest.cc
+++ b/chromium/components/viz/service/frame_sinks/surface_references_unittest.cc
@@ -10,6 +10,7 @@
#include "base/containers/flat_set.h"
#include "base/test/test_mock_time_task_runner.h"
#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.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/surfaces/surface.h"
@@ -114,10 +115,6 @@ class SurfaceReferencesTest : public testing::Test {
return temp_references;
}
- const base::flat_map<FrameSinkId, std::string>& GetFrameSinkLabels() {
- return manager_->surface_manager()->valid_frame_sink_labels_;
- }
-
bool IsTemporaryReferenceTimerRunning() const {
return manager_->surface_manager()->expire_timer_->IsRunning();
}
@@ -126,7 +123,7 @@ class SurfaceReferencesTest : public testing::Test {
// testing::Test:
void SetUp() override {
// Start each test with a fresh SurfaceManager instance.
- manager_ = std::make_unique<FrameSinkManagerImpl>();
+ manager_ = std::make_unique<FrameSinkManagerImpl>(&shared_bitmap_manager_);
frame_sink_manager_client_ =
std::make_unique<TestFrameSinkManagerClient>(manager_.get());
manager_->SetLocalClient(frame_sink_manager_client_.get());
@@ -140,12 +137,13 @@ class SurfaceReferencesTest : public testing::Test {
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
base::TestMockTimeTaskRunner::ScopedContext scoped_context_;
+ ServerSharedBitmapManager shared_bitmap_manager_;
+ std::unique_ptr<FrameSinkManagerImpl> manager_;
+ std::unique_ptr<TestFrameSinkManagerClient> frame_sink_manager_client_;
std::unordered_map<FrameSinkId,
std::unique_ptr<CompositorFrameSinkSupport>,
FrameSinkIdHash>
supports_;
- std::unique_ptr<FrameSinkManagerImpl> manager_;
- std::unique_ptr<TestFrameSinkManagerClient> frame_sink_manager_client_;
};
TEST_F(SurfaceReferencesTest, AddReference) {
@@ -157,26 +155,6 @@ TEST_F(SurfaceReferencesTest, AddReference) {
EXPECT_THAT(GetReferencesFrom(id1), IsEmpty());
}
-#if DCHECK_IS_ON()
-// The test sets up a surface reference with a label and verifies that the label
-// is correctly associated with the Surface. It then invalidates the FrameSinkId
-// associated with the label and verifies that the label no longer exists in
-// |SurfaceManager::valid_frame_sink_labels_|.
-TEST_F(SurfaceReferencesTest, DebugLabelLookup) {
- CreateSurface(kFrameSink1, 1);
- const std::string kLabel = "kFrameSink1";
- GetSurfaceManager().SetFrameSinkDebugLabel(kFrameSink1, kLabel);
- EXPECT_EQ(kLabel, GetSurfaceManager().GetFrameSinkDebugLabel(kFrameSink1));
- GetSurfaceManager().InvalidateFrameSinkId(kFrameSink1);
-
- // Verify that the label is no longer in |valid_frame_sink_labels_|. The first
- // EXPECT_EQ calls GetFrameSinkDebugLabel(). The second EXPECT_EQ verifies
- // that calling GetFrameSinkDebugLabel() doesn't add the entry back.
- EXPECT_EQ("", GetSurfaceManager().GetFrameSinkDebugLabel(kFrameSink1));
- EXPECT_EQ(0u, GetFrameSinkLabels().count(kFrameSink1));
-}
-#endif
-
TEST_F(SurfaceReferencesTest, AddRemoveReference) {
frame_sink_manager_client_->SetFrameSinkHierarchy(kFrameSink1, kFrameSink2);
diff --git a/chromium/components/viz/service/frame_sinks/surface_synchronization_unittest.cc b/chromium/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
index 8b83cea05dd..dff959372b5 100644
--- a/chromium/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
+++ b/chromium/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
@@ -5,6 +5,7 @@
#include "base/containers/flat_set.h"
#include "base/test/simple_test_tick_clock.h"
#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.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/test/begin_frame_args_test.h"
@@ -37,6 +38,9 @@ constexpr FrameSinkId kArbitraryFrameSink(1337, 7331);
std::vector<SurfaceId> empty_surface_ids() {
return std::vector<SurfaceId>();
}
+std::vector<SurfaceRange> empty_surface_ranges() {
+ return std::vector<SurfaceRange>();
+}
SurfaceId MakeSurfaceId(const FrameSinkId& frame_sink_id,
uint32_t parent_sequence_number,
@@ -48,7 +52,7 @@ SurfaceId MakeSurfaceId(const FrameSinkId& frame_sink_id,
CompositorFrame MakeCompositorFrame(
std::vector<SurfaceId> activation_dependencies,
- std::vector<SurfaceId> referenced_surfaces,
+ std::vector<SurfaceRange> referenced_surfaces,
std::vector<TransferableResource> resource_list,
const FrameDeadline& deadline = FrameDeadline()) {
return CompositorFrameBuilder()
@@ -87,7 +91,8 @@ class FakeExternalBeginFrameSourceClient
class SurfaceSynchronizationTest : public testing::Test {
public:
SurfaceSynchronizationTest()
- : frame_sink_manager_client_(&frame_sink_manager_),
+ : frame_sink_manager_(&shared_bitmap_manager_),
+ frame_sink_manager_client_(&frame_sink_manager_),
surface_observer_(false) {}
~SurfaceSynchronizationTest() override {}
@@ -258,6 +263,7 @@ class SurfaceSynchronizationTest : public testing::Test {
private:
std::unique_ptr<base::SimpleTestTickClock> now_src_;
+ ServerSharedBitmapManager shared_bitmap_manager_;
FrameSinkManagerImpl frame_sink_manager_;
TestFrameSinkManagerClient frame_sink_manager_client_;
FakeSurfaceObserver surface_observer_;
@@ -314,7 +320,7 @@ TEST_F(SurfaceSynchronizationTest, BlockedOnTwo) {
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id1, child_id2}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1, child_id2}, empty_surface_ranges(),
std::vector<TransferableResource>()));
// parent_support is blocked on |child_id1| and |child_id2|.
@@ -355,7 +361,7 @@ TEST_F(SurfaceSynchronizationTest, BlockedChain) {
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1}, empty_surface_ranges(),
std::vector<TransferableResource>()));
// parent_support is blocked on |child_id1|.
@@ -369,7 +375,7 @@ TEST_F(SurfaceSynchronizationTest, BlockedChain) {
child_support1().SubmitCompositorFrame(
child_id1.local_surface_id(),
- MakeCompositorFrame({child_id2}, empty_surface_ids(),
+ MakeCompositorFrame({child_id2}, empty_surface_ranges(),
std::vector<TransferableResource>()));
// child_support1 should now be blocked on |child_id2|.
@@ -390,7 +396,7 @@ TEST_F(SurfaceSynchronizationTest, BlockedChain) {
// parent_support should be activated.
child_support2().SubmitCompositorFrame(
child_id2.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
std::vector<TransferableResource>()));
EXPECT_FALSE(child_surface2()->has_deadline());
@@ -420,7 +426,7 @@ TEST_F(SurfaceSynchronizationTest, TwoBlockedOnOne) {
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id2}, empty_surface_ids(),
+ MakeCompositorFrame({child_id2}, empty_surface_ranges(),
std::vector<TransferableResource>()));
// parent_support is blocked on |child_id2|.
@@ -433,7 +439,7 @@ TEST_F(SurfaceSynchronizationTest, TwoBlockedOnOne) {
// child_support1 should now be blocked on |child_id2|.
child_support1().SubmitCompositorFrame(
child_id1.local_surface_id(),
- MakeCompositorFrame({child_id2}, empty_surface_ids(),
+ MakeCompositorFrame({child_id2}, empty_surface_ranges(),
std::vector<TransferableResource>()));
EXPECT_TRUE(child_surface1()->has_deadline());
@@ -473,7 +479,7 @@ TEST_F(SurfaceSynchronizationTest, DeadlineHits) {
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
@@ -486,7 +492,7 @@ TEST_F(SurfaceSynchronizationTest, DeadlineHits) {
child_support1().SubmitCompositorFrame(
child_id1.local_surface_id(),
- MakeCompositorFrame({child_id2}, empty_surface_ids(),
+ MakeCompositorFrame({child_id2}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
@@ -550,7 +556,7 @@ TEST_F(SurfaceSynchronizationTest, UnlimitedDeadline) {
// mode.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
@@ -592,7 +598,7 @@ TEST_F(SurfaceSynchronizationTest, LateBeginFrameTriggersDeadline) {
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1}, empty_surface_ranges(),
std::vector<TransferableResource>()));
// parent_support is blocked on |child_id1|.
@@ -622,7 +628,7 @@ TEST_F(SurfaceSynchronizationTest, NewFrameOverridesOldDependencies) {
// Submit a CompositorFrame that depends on |arbitrary_id|.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({arbitrary_id}, empty_surface_ids(),
+ MakeCompositorFrame({arbitrary_id}, empty_surface_ranges(),
std::vector<TransferableResource>()));
// Verify that the CompositorFrame is blocked on |arbitrary_id|.
@@ -675,7 +681,7 @@ TEST_F(SurfaceSynchronizationTest, OnlyActiveFramesAffectSurfaceReferences) {
EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0);
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id2}, {child_id1},
+ MakeCompositorFrame({child_id2}, {SurfaceRange(child_id1)},
std::vector<TransferableResource>()));
EXPECT_FALSE(parent_surface()->HasActiveFrame());
EXPECT_TRUE(parent_surface()->HasPendingFrame());
@@ -728,7 +734,7 @@ TEST_F(SurfaceSynchronizationTest, ResourcesOnlyReturnedOnce) {
std::vector<TransferableResource> resource_list = {resource};
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id}, empty_surface_ids(), resource_list));
+ MakeCompositorFrame({child_id}, empty_surface_ranges(), resource_list));
// Verify that the CompositorFrame is blocked on |child_id|.
EXPECT_FALSE(parent_surface()->HasActiveFrame());
@@ -738,7 +744,7 @@ TEST_F(SurfaceSynchronizationTest, ResourcesOnlyReturnedOnce) {
child_support1().SubmitCompositorFrame(
child_id.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
std::vector<TransferableResource>()));
// Verify that the child CompositorFrame activates immediately.
@@ -761,7 +767,7 @@ TEST_F(SurfaceSynchronizationTest, ResourcesOnlyReturnedOnce) {
// resource from the earlier frame should be returned to the client.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({empty_surface_ids()}, {empty_surface_ids()},
+ MakeCompositorFrame({empty_surface_ids()}, {empty_surface_ranges()},
std::vector<TransferableResource>()));
EXPECT_TRUE(parent_surface()->HasActiveFrame());
EXPECT_FALSE(parent_surface()->HasPendingFrame());
@@ -783,7 +789,7 @@ TEST_F(SurfaceSynchronizationTest, DropStaleReferencesAfterActivation) {
EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0);
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1}, empty_surface_ranges(),
std::vector<TransferableResource>()));
// Verify that the CompositorFrame is blocked on |child_id|.
@@ -817,7 +823,7 @@ TEST_F(SurfaceSynchronizationTest, DropStaleReferencesAfterActivation) {
// Submit a new parent CompositorFrame to add a reference.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), {child_id1},
+ MakeCompositorFrame(empty_surface_ids(), {SurfaceRange(child_id1)},
std::vector<TransferableResource>()));
// Verify that the parent Surface has activated.
@@ -836,7 +842,7 @@ TEST_F(SurfaceSynchronizationTest, DropStaleReferencesAfterActivation) {
EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0);
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id2}, empty_surface_ids(),
+ MakeCompositorFrame({child_id2}, empty_surface_ranges(),
std::vector<TransferableResource>()));
testing::Mock::VerifyAndClearExpectations(&support_client_);
@@ -868,14 +874,13 @@ TEST_F(SurfaceSynchronizationTest, LimitLatencyInfo) {
const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1);
const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2);
const ui::LatencyComponentType latency_type1 =
- ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT;
- const int64_t latency_id1 = 234;
- const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT;
- const int64_t latency_id2 = 31434351;
+ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT;
+ const ui::LatencyComponentType latency_type2 =
+ ui::LATENCY_BEGIN_FRAME_UI_MAIN_COMPONENT;
// Submit a frame with latency info
ui::LatencyInfo info;
- info.AddLatencyNumber(latency_type1, latency_id1);
+ info.AddLatencyNumber(latency_type1);
CompositorFrameBuilder builder;
builder.AddDefaultRenderPass();
@@ -895,7 +900,7 @@ TEST_F(SurfaceSynchronizationTest, LimitLatencyInfo) {
// Submit another frame with some other latency info and a different
// LocalSurfaceId.
ui::LatencyInfo info2;
- info2.AddLatencyNumber(latency_type2, latency_id2);
+ info2.AddLatencyNumber(latency_type2);
builder.AddDefaultRenderPass();
for (int i = 0; i < 60; ++i)
@@ -926,13 +931,12 @@ TEST_F(SurfaceSynchronizationTest,
const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2);
const ui::LatencyComponentType latency_type1 =
ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT;
- const int64_t latency_id1 = 234;
- const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT;
- const int64_t latency_id2 = 31434351;
+ const ui::LatencyComponentType latency_type2 =
+ ui::LATENCY_BEGIN_FRAME_UI_MAIN_COMPONENT;
// Submit a frame with latency info
ui::LatencyInfo info;
- info.AddLatencyNumber(latency_type1, latency_id1);
+ info.AddLatencyNumber(latency_type1);
CompositorFrame frame = CompositorFrameBuilder()
.AddDefaultRenderPass()
@@ -951,7 +955,7 @@ TEST_F(SurfaceSynchronizationTest,
// Submit another frame with some other latency info and a different
// LocalSurfaceId.
ui::LatencyInfo info2;
- info2.AddLatencyNumber(latency_type2, latency_id2);
+ info2.AddLatencyNumber(latency_type2);
CompositorFrame frame2 = CompositorFrameBuilder()
.AddDefaultRenderPass()
@@ -980,11 +984,8 @@ TEST_F(SurfaceSynchronizationTest,
// submit.
EXPECT_EQ(3u, aggregated_latency_info.latency_components().size());
- ui::LatencyInfo::LatencyComponent comp1;
- EXPECT_TRUE(
- aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1));
- EXPECT_TRUE(
- aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr));
+ EXPECT_TRUE(aggregated_latency_info.FindLatency(latency_type1, nullptr));
+ EXPECT_TRUE(aggregated_latency_info.FindLatency(latency_type2, nullptr));
EXPECT_TRUE(aggregated_latency_info.FindLatency(
ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr));
}
@@ -1000,13 +1001,12 @@ TEST_F(SurfaceSynchronizationTest,
const ui::LatencyComponentType latency_type1 =
ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT;
- const int64_t latency_id1 = 234;
- const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT;
- const int64_t latency_id2 = 31434351;
+ const ui::LatencyComponentType latency_type2 =
+ ui::LATENCY_BEGIN_FRAME_UI_MAIN_COMPONENT;
// Submit a frame with no unresolved dependecy.
ui::LatencyInfo info;
- info.AddLatencyNumber(latency_type1, latency_id1);
+ info.AddLatencyNumber(latency_type1);
CompositorFrame frame = MakeDefaultCompositorFrame();
frame.metadata.latency_info.push_back(info);
@@ -1016,10 +1016,10 @@ TEST_F(SurfaceSynchronizationTest,
// Submit a frame with unresolved dependencies.
ui::LatencyInfo info2;
- info2.AddLatencyNumber(latency_type2, latency_id2);
+ info2.AddLatencyNumber(latency_type2);
CompositorFrame frame2 = MakeCompositorFrame(
- {child_id}, empty_surface_ids(), std::vector<TransferableResource>());
+ {child_id}, empty_surface_ranges(), std::vector<TransferableResource>());
frame2.metadata.latency_info.push_back(info2);
parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(),
@@ -1055,11 +1055,8 @@ TEST_F(SurfaceSynchronizationTest,
// submit.
EXPECT_EQ(3u, aggregated_latency_info.latency_components().size());
- ui::LatencyInfo::LatencyComponent comp1;
- EXPECT_TRUE(
- aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1));
- EXPECT_TRUE(
- aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr));
+ EXPECT_TRUE(aggregated_latency_info.FindLatency(latency_type1, nullptr));
+ EXPECT_TRUE(aggregated_latency_info.FindLatency(latency_type2, nullptr));
EXPECT_TRUE(aggregated_latency_info.FindLatency(
ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr));
}
@@ -1075,13 +1072,12 @@ TEST_F(SurfaceSynchronizationTest,
const ui::LatencyComponentType latency_type1 =
ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT;
- const int64_t latency_id1 = 234;
- const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT;
- const int64_t latency_id2 = 31434351;
+ const ui::LatencyComponentType latency_type2 =
+ ui::LATENCY_BEGIN_FRAME_UI_MAIN_COMPONENT;
// Submit a frame with no unresolved dependencies.
ui::LatencyInfo info;
- info.AddLatencyNumber(latency_type1, latency_id1);
+ info.AddLatencyNumber(latency_type1);
CompositorFrame frame = MakeDefaultCompositorFrame();
frame.metadata.latency_info.push_back(info);
@@ -1098,10 +1094,10 @@ TEST_F(SurfaceSynchronizationTest,
// Submit a frame with a new local surface id and with unresolved
// dependencies.
ui::LatencyInfo info2;
- info2.AddLatencyNumber(latency_type2, latency_id2);
+ info2.AddLatencyNumber(latency_type2);
CompositorFrame frame2 = MakeCompositorFrame(
- {child_id}, empty_surface_ids(), std::vector<TransferableResource>());
+ {child_id}, empty_surface_ranges(), std::vector<TransferableResource>());
frame2.metadata.latency_info.push_back(info2);
parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(),
@@ -1133,11 +1129,8 @@ TEST_F(SurfaceSynchronizationTest,
// submit.
EXPECT_EQ(3u, aggregated_latency_info.latency_components().size());
- ui::LatencyInfo::LatencyComponent comp1;
- EXPECT_TRUE(
- aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1));
- EXPECT_TRUE(
- aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr));
+ EXPECT_TRUE(aggregated_latency_info.FindLatency(latency_type1, nullptr));
+ EXPECT_TRUE(aggregated_latency_info.FindLatency(latency_type2, nullptr));
EXPECT_TRUE(aggregated_latency_info.FindLatency(
ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr));
}
@@ -1149,7 +1142,7 @@ TEST_F(SurfaceSynchronizationTest, ReturnResourcesWithAck) {
resource.id = 1234;
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
{resource}));
std::vector<ReturnedResource> returned_resources =
TransferableResource::ReturnResources({resource});
@@ -1172,7 +1165,7 @@ TEST_F(SurfaceSynchronizationTest, SurfaceResurrection) {
resource.id = 1234;
child_support1().SubmitCompositorFrame(
child_id.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
{resource}));
// Verify that the child surface is created.
Surface* surface = GetSurfaceForId(child_id);
@@ -1181,7 +1174,7 @@ TEST_F(SurfaceSynchronizationTest, SurfaceResurrection) {
// Add a reference from the parent to the child.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id}, {child_id},
+ MakeCompositorFrame({child_id}, {SurfaceRange(child_id)},
std::vector<TransferableResource>()));
// Attempt to destroy the child surface. The surface must still exist since
@@ -1231,7 +1224,7 @@ TEST_F(SurfaceSynchronizationTest, SurfaceResurrectionAfterDestruction) {
// Add a reference from the parent to the child.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id}, {child_id},
+ MakeCompositorFrame({child_id}, {SurfaceRange(child_id)},
std::vector<TransferableResource>()));
// Attempt to destroy the child surface. The surface must still exist since
@@ -1271,7 +1264,7 @@ TEST_F(SurfaceSynchronizationTest, LocalSurfaceIdIsReusable) {
// Add a reference from parent.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id}, {child_id},
+ MakeCompositorFrame({child_id}, {SurfaceRange(child_id)},
std::vector<TransferableResource>()));
// Remove the reference from parant. This allows us to destroy the surface.
@@ -1306,12 +1299,12 @@ TEST_F(SurfaceSynchronizationTest, DependencyTrackingGarbageCollection) {
parent_support().SubmitCompositorFrame(
parent_id1.local_surface_id(),
- MakeCompositorFrame({child_id}, empty_surface_ids(),
+ MakeCompositorFrame({child_id}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
display_support().SubmitCompositorFrame(
display_id.local_surface_id(),
- MakeCompositorFrame({parent_id1}, empty_surface_ids(),
+ MakeCompositorFrame({parent_id1}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
@@ -1332,12 +1325,12 @@ TEST_F(SurfaceSynchronizationTest, DependencyTrackingGarbageCollection) {
parent_support().SubmitCompositorFrame(
parent_id2.local_surface_id(),
- MakeCompositorFrame({child_id}, empty_surface_ids(),
+ MakeCompositorFrame({child_id}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
display_support().SubmitCompositorFrame(
display_id.local_surface_id(),
- MakeCompositorFrame({parent_id2}, empty_surface_ids(),
+ MakeCompositorFrame({parent_id2}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
@@ -1370,13 +1363,13 @@ TEST_F(SurfaceSynchronizationTest, GarbageCollectionOnDeadline) {
// |parent_id1| is blocked on |child_id|.
parent_support().SubmitCompositorFrame(
parent_id1.local_surface_id(),
- MakeCompositorFrame({child_id}, empty_surface_ids(),
+ MakeCompositorFrame({child_id}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
display_support().SubmitCompositorFrame(
display_id.local_surface_id(),
- MakeCompositorFrame({parent_id1}, {parent_id1},
+ MakeCompositorFrame({parent_id1}, {SurfaceRange(parent_id1)},
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
@@ -1403,7 +1396,7 @@ TEST_F(SurfaceSynchronizationTest, GarbageCollectionOnDeadline) {
// and a candidate for garbage collection.
display_support().SubmitCompositorFrame(
display_id.local_surface_id(),
- MakeCompositorFrame({parent_id2}, empty_surface_ids(),
+ MakeCompositorFrame({parent_id2}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
EXPECT_TRUE(display_surface()->has_deadline());
@@ -1411,7 +1404,7 @@ TEST_F(SurfaceSynchronizationTest, GarbageCollectionOnDeadline) {
// Now |parent_id1| is only kept alive by the active |display_id| frame.
parent_support().SubmitCompositorFrame(
parent_id2.local_surface_id(),
- MakeCompositorFrame({child_id}, empty_surface_ids(),
+ MakeCompositorFrame({child_id}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
EXPECT_TRUE(display_surface()->has_deadline());
@@ -1444,7 +1437,7 @@ TEST_F(SurfaceSynchronizationTest, OnlyBlockOnEmbeddedSurfaces) {
display_support().SubmitCompositorFrame(
display_id.local_surface_id(),
- MakeCompositorFrame({parent_id2}, {parent_id1},
+ MakeCompositorFrame({parent_id2}, {SurfaceRange(parent_id1)},
std::vector<TransferableResource>()));
EXPECT_TRUE(display_surface()->HasPendingFrame());
@@ -1479,7 +1472,7 @@ TEST_F(SurfaceSynchronizationTest, LateArrivingDependency) {
display_support().SubmitCompositorFrame(
display_id.local_surface_id(),
- MakeCompositorFrame({parent_id1}, empty_surface_ids(),
+ MakeCompositorFrame({parent_id1}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
@@ -1502,7 +1495,7 @@ TEST_F(SurfaceSynchronizationTest, LateArrivingDependency) {
// scheduling a deadline and without waiting for dependencies to resolve.
parent_support().SubmitCompositorFrame(
parent_id1.local_surface_id(),
- MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
EXPECT_FALSE(parent_surface()->has_deadline());
@@ -1520,7 +1513,7 @@ TEST_F(SurfaceSynchronizationTest, MultiLevelLateArrivingDependency) {
display_support().SubmitCompositorFrame(
display_id.local_surface_id(),
- MakeCompositorFrame({parent_id}, empty_surface_ids(),
+ MakeCompositorFrame({parent_id}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
EXPECT_TRUE(display_surface()->HasPendingFrame());
@@ -1542,7 +1535,7 @@ TEST_F(SurfaceSynchronizationTest, MultiLevelLateArrivingDependency) {
// surface and so it gets a separate deadline.
child_support1().SubmitCompositorFrame(
child_id.local_surface_id(),
- MakeCompositorFrame({arbitrary_id}, empty_surface_ids(),
+ MakeCompositorFrame({arbitrary_id}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
EXPECT_TRUE(child_surface1()->HasPendingFrame());
@@ -1555,7 +1548,7 @@ TEST_F(SurfaceSynchronizationTest, MultiLevelLateArrivingDependency) {
// be late and activate immediately.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id}, empty_surface_ids(),
+ MakeCompositorFrame({child_id}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
EXPECT_FALSE(parent_surface()->HasPendingFrame());
@@ -1579,7 +1572,7 @@ TEST_F(SurfaceSynchronizationTest, MissingActiveFrameWithLateDependencies) {
display_support().SubmitCompositorFrame(
display_id.local_surface_id(),
- MakeCompositorFrame({parent_id1}, empty_surface_ids(),
+ MakeCompositorFrame({parent_id1}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
@@ -1601,7 +1594,7 @@ TEST_F(SurfaceSynchronizationTest, MissingActiveFrameWithLateDependencies) {
display_support().EvictLastActivatedSurface();
display_support().SubmitCompositorFrame(
display_id.local_surface_id(),
- MakeCompositorFrame({parent_id2}, empty_surface_ids(),
+ MakeCompositorFrame({parent_id2}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
@@ -1609,7 +1602,7 @@ TEST_F(SurfaceSynchronizationTest, MissingActiveFrameWithLateDependencies) {
// dependent CompositorFrame has been evicted.
parent_support().SubmitCompositorFrame(
parent_id1.local_surface_id(),
- MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
EXPECT_TRUE(parent_surface()->has_deadline());
@@ -1643,8 +1636,8 @@ TEST_F(SurfaceSynchronizationTest, FallbackSurfacesClosed) {
Eq(std::vector<ReturnedResource>())));
child_support1().SubmitCompositorFrame(
child_id1.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), {resource},
- MakeDefaultDeadline()));
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
+ {resource}, MakeDefaultDeadline()));
EXPECT_FALSE(child_surface1()->has_deadline());
testing::Mock::VerifyAndClearExpectations(&support_client_);
@@ -1653,7 +1646,7 @@ TEST_F(SurfaceSynchronizationTest, FallbackSurfacesClosed) {
// while the parent CompositorFrame is blocked.
parent_support().SubmitCompositorFrame(
parent_id1.local_surface_id(),
- MakeCompositorFrame({child_id2}, {child_id1},
+ MakeCompositorFrame({child_id2}, {SurfaceRange(child_id1)},
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
EXPECT_TRUE(parent_surface()->has_deadline());
@@ -1673,8 +1666,8 @@ TEST_F(SurfaceSynchronizationTest, FallbackSurfacesClosed) {
DidReceiveCompositorFrameAck(Eq(returned_resources2)));
child_support1().SubmitCompositorFrame(
child_id1.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), {resource2},
- MakeDefaultDeadline()));
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
+ {resource2}, MakeDefaultDeadline()));
testing::Mock::VerifyAndClearExpectations(&support_client_);
// Advance BeginFrames to trigger a deadline. This activates the
@@ -1694,8 +1687,8 @@ TEST_F(SurfaceSynchronizationTest, FallbackSurfacesClosed) {
DidReceiveCompositorFrameAck(Eq(returned_resources2)));
child_support1().SubmitCompositorFrame(
child_id1.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), {resource2},
- MakeDefaultDeadline()));
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
+ {resource2}, MakeDefaultDeadline()));
testing::Mock::VerifyAndClearExpectations(&support_client_);
}
@@ -1718,7 +1711,7 @@ TEST_F(SurfaceSynchronizationTest, IndependentDeadlines) {
parent_support().SubmitCompositorFrame(
parent_id1.local_surface_id(),
- MakeCompositorFrame({child_id1, child_id2}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1, child_id2}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
@@ -1731,7 +1724,7 @@ TEST_F(SurfaceSynchronizationTest, IndependentDeadlines) {
child_support1().SubmitCompositorFrame(
child_id1.local_surface_id(),
MakeCompositorFrame(
- {arbitrary_id}, empty_surface_ids(),
+ {arbitrary_id}, empty_surface_ranges(),
std::vector<TransferableResource>(),
FrameDeadline(Now(), 3, BeginFrameArgs::DefaultInterval(), false)));
EXPECT_TRUE(child_surface1()->HasPendingFrame());
@@ -1746,7 +1739,7 @@ TEST_F(SurfaceSynchronizationTest, IndependentDeadlines) {
// |arbitrary_id|.
child_support2().SubmitCompositorFrame(
child_id2.local_surface_id(),
- MakeCompositorFrame({arbitrary_id}, empty_surface_ids(),
+ MakeCompositorFrame({arbitrary_id}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
EXPECT_TRUE(child_surface2()->HasPendingFrame());
@@ -1795,7 +1788,8 @@ TEST_F(SurfaceSynchronizationTest, DeadlineInheritance) {
parent_support().SubmitCompositorFrame(
parent_id1.local_surface_id(),
MakeCompositorFrame(
- {child_id1}, empty_surface_ids(), std::vector<TransferableResource>(),
+ {child_id1}, empty_surface_ranges(),
+ std::vector<TransferableResource>(),
FrameDeadline(Now(), 2, BeginFrameArgs::DefaultInterval(), true)));
EXPECT_TRUE(parent_surface()->HasPendingFrame());
@@ -1808,7 +1802,7 @@ TEST_F(SurfaceSynchronizationTest, DeadlineInheritance) {
child_support1().SubmitCompositorFrame(
child_id1.local_surface_id(),
- MakeCompositorFrame({arbitrary_id}, empty_surface_ids(),
+ MakeCompositorFrame({arbitrary_id}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
EXPECT_TRUE(child_surface1()->HasPendingFrame());
@@ -1845,7 +1839,7 @@ TEST_F(SurfaceSynchronizationTest, MultiLevelDeadlineInheritance) {
display_support().SubmitCompositorFrame(
display_id.local_surface_id(),
- MakeCompositorFrame({parent_id}, empty_surface_ids(),
+ MakeCompositorFrame({parent_id}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
EXPECT_TRUE(display_surface()->HasPendingFrame());
@@ -1859,7 +1853,7 @@ TEST_F(SurfaceSynchronizationTest, MultiLevelDeadlineInheritance) {
// surface and so it gets a separate deadline.
child_support1().SubmitCompositorFrame(
child_id.local_surface_id(),
- MakeCompositorFrame({arbitrary_id}, empty_surface_ids(),
+ MakeCompositorFrame({arbitrary_id}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
EXPECT_TRUE(child_surface1()->HasPendingFrame());
@@ -1871,7 +1865,7 @@ TEST_F(SurfaceSynchronizationTest, MultiLevelDeadlineInheritance) {
// assume the same deadline.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id}, empty_surface_ids(),
+ MakeCompositorFrame({child_id}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
EXPECT_TRUE(parent_surface()->HasPendingFrame());
@@ -1917,7 +1911,7 @@ TEST_F(SurfaceSynchronizationTest, FrameActivationAfterFrameSinkDestruction) {
// Submit a CompositorFrame that refers to to |parent_id|.
display_support().SubmitCompositorFrame(
display_id.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), {parent_id},
+ MakeCompositorFrame(empty_surface_ids(), {SurfaceRange(parent_id)},
std::vector<TransferableResource>()));
EXPECT_FALSE(display_surface()->has_deadline());
@@ -1929,7 +1923,7 @@ TEST_F(SurfaceSynchronizationTest, FrameActivationAfterFrameSinkDestruction) {
// now have a pending and active CompositorFrame.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id}, empty_surface_ids(),
+ MakeCompositorFrame({child_id}, empty_surface_ranges(),
std::vector<TransferableResource>()));
EXPECT_TRUE(parent_surface()->has_deadline());
@@ -1976,7 +1970,7 @@ TEST_F(SurfaceSynchronizationTest, PreviousFrameSurfaceId) {
// Submit a frame with no dependencies to |parent_id1|.
parent_support().SubmitCompositorFrame(
parent_id1.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
std::vector<TransferableResource>()));
// Submit a frame with unresolved dependencies to |parent_id2|. The frame
@@ -1984,7 +1978,7 @@ TEST_F(SurfaceSynchronizationTest, PreviousFrameSurfaceId) {
// |parent_id1|.
parent_support().SubmitCompositorFrame(
parent_id2.local_surface_id(),
- MakeCompositorFrame({child_id}, empty_surface_ids(),
+ MakeCompositorFrame({child_id}, empty_surface_ranges(),
std::vector<TransferableResource>()));
Surface* parent_surface2 =
frame_sink_manager().surface_manager()->GetSurfaceForId(parent_id2);
@@ -2008,7 +2002,7 @@ TEST_F(SurfaceSynchronizationTest, FrameIndexWithPendingFrames) {
// the initial frame index.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
std::vector<TransferableResource>()));
Surface* parent_surface =
frame_sink_manager().surface_manager()->GetSurfaceForId(parent_id);
@@ -2019,7 +2013,7 @@ TEST_F(SurfaceSynchronizationTest, FrameIndexWithPendingFrames) {
for (int i = 0; i < n_iterations; i++) {
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1}, empty_surface_ranges(),
std::vector<TransferableResource>()));
EXPECT_EQ(initial_frame_index, parent_surface->GetActiveFrameIndex());
}
@@ -2043,7 +2037,7 @@ TEST_F(SurfaceSynchronizationTest, PendingSurfaceKeptAlive) {
// |parent_id| depends on |child_id1|. It shouldn't activate.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1}, empty_surface_ranges(),
std::vector<TransferableResource>()));
EXPECT_FALSE(parent_surface()->HasActiveFrame());
EXPECT_TRUE(parent_surface()->HasPendingFrame());
@@ -2058,7 +2052,7 @@ TEST_F(SurfaceSynchronizationTest, ActiveFrameIndex) {
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id1, child_id2}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1, child_id2}, empty_surface_ranges(),
std::vector<TransferableResource>()));
// parent_support is blocked on |child_id1| and |child_id2|.
@@ -2093,7 +2087,7 @@ TEST_F(SurfaceSynchronizationTest, LatestInFlightSurface) {
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1}, empty_surface_ranges(),
std::vector<TransferableResource>()));
// Verify that the child CompositorFrame activates immediately.
@@ -2115,7 +2109,7 @@ TEST_F(SurfaceSynchronizationTest, LatestInFlightSurface) {
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), {child_id1},
+ MakeCompositorFrame(empty_surface_ids(), {SurfaceRange(child_id1)},
std::vector<TransferableResource>()));
// Verify that the parent Surface has activated.
@@ -2180,7 +2174,7 @@ TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceWithBogusFallback) {
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1}, empty_surface_ranges(),
std::vector<TransferableResource>()));
// Verify that the parent and child CompositorFrames are active.
@@ -2213,7 +2207,7 @@ TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceDifferentFrameSinkIds) {
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id2}, {child_id1},
+ MakeCompositorFrame({child_id2}, {SurfaceRange(child_id1)},
std::vector<TransferableResource>()));
// Submit a child CompositorFrame without a different FrameSinkId and verify
@@ -2248,7 +2242,7 @@ TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceSkipPrimary) {
// Create a reference from |parent_id| to |child_id|.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), {child_id1},
+ MakeCompositorFrame(empty_surface_ids(), {SurfaceRange(child_id1)},
std::vector<TransferableResource>()));
child_support1().SubmitCompositorFrame(child_id2.local_surface_id(),
@@ -2296,7 +2290,7 @@ TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceSkipDifferentNonce) {
// Create a reference from |parent_id| to |child_id|.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), {child_id1},
+ MakeCompositorFrame(empty_surface_ids(), {SurfaceRange(child_id1)},
std::vector<TransferableResource>()));
child_support1().SubmitCompositorFrame(child_id2.local_surface_id(),
@@ -2331,7 +2325,7 @@ TEST_F(SurfaceSynchronizationTest, DropDependenciesThatWillNeverArrive) {
// |parent_id| depends on { child_id11, child_id21 }. It shouldn't activate.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id11, child_id21}, empty_surface_ids(),
+ MakeCompositorFrame({child_id11, child_id21}, empty_surface_ranges(),
std::vector<TransferableResource>()));
EXPECT_FALSE(parent_surface()->HasActiveFrame());
EXPECT_TRUE(parent_surface()->HasPendingFrame());
@@ -2341,7 +2335,7 @@ TEST_F(SurfaceSynchronizationTest, DropDependenciesThatWillNeverArrive) {
// However, the parent is still blocked on |child_id21|.
child_support1().SubmitCompositorFrame(
child_id12.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
std::vector<TransferableResource>()));
EXPECT_FALSE(parent_surface()->HasActiveFrame());
EXPECT_TRUE(parent_surface()->HasPendingFrame());
@@ -2352,7 +2346,7 @@ TEST_F(SurfaceSynchronizationTest, DropDependenciesThatWillNeverArrive) {
// the parent activates.
child_support2().SubmitCompositorFrame(
child_id21.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
std::vector<TransferableResource>()));
EXPECT_TRUE(parent_surface()->HasActiveFrame());
EXPECT_FALSE(parent_surface()->HasPendingFrame());
@@ -2369,7 +2363,7 @@ TEST_F(SurfaceSynchronizationTest, ObserveDependenciesUntilArrival) {
// |parent_id| depends on |child_id22|. It shouldn't activate.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id22}, empty_surface_ids(),
+ MakeCompositorFrame({child_id22}, empty_surface_ranges(),
std::vector<TransferableResource>()));
EXPECT_FALSE(parent_surface()->HasActiveFrame());
EXPECT_TRUE(parent_surface()->HasPendingFrame());
@@ -2377,7 +2371,7 @@ TEST_F(SurfaceSynchronizationTest, ObserveDependenciesUntilArrival) {
// The child submits to |child_id21|. The parent should not activate.
child_support1().SubmitCompositorFrame(
child_id21.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
std::vector<TransferableResource>()));
EXPECT_FALSE(parent_surface()->HasActiveFrame());
EXPECT_TRUE(parent_surface()->HasPendingFrame());
@@ -2387,7 +2381,7 @@ TEST_F(SurfaceSynchronizationTest, ObserveDependenciesUntilArrival) {
// The child submits to |child_id22|. The parent should activate.
child_support1().SubmitCompositorFrame(
child_id22.local_surface_id(),
- MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
std::vector<TransferableResource>()));
EXPECT_TRUE(parent_surface()->HasActiveFrame());
EXPECT_FALSE(parent_surface()->HasPendingFrame());
@@ -2415,7 +2409,7 @@ TEST_F(SurfaceSynchronizationTest,
parent_support().SubmitCompositorFrame(
parent_id2.local_surface_id(),
- MakeCompositorFrame({arbitrary_id}, empty_surface_ids(),
+ MakeCompositorFrame({arbitrary_id}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
@@ -2470,7 +2464,7 @@ TEST_F(SurfaceSynchronizationTest, SetPreviousFrameSurfaceDoesntCrash) {
// on |child_id1|.
parent_support().SubmitCompositorFrame(
parent_id.local_surface_id(),
- MakeCompositorFrame({child_id1}, empty_surface_ids(),
+ MakeCompositorFrame({child_id1}, empty_surface_ranges(),
std::vector<TransferableResource>(),
MakeDefaultDeadline()));
@@ -2506,5 +2500,30 @@ TEST_F(SurfaceSynchronizationTest, SetPreviousFrameSurfaceDoesntCrash) {
MakeDefaultCompositorFrame());
}
+// This test verifies that once a frame sink become invalidated, it should
+// immediately unblock all pending frames depending on that sink.
+TEST_F(SurfaceSynchronizationTest,
+ InvalidatedFrameSinkShouldNotBlockActivation) {
+ const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
+
+ // The parent submitted a frame that refers to a future child surface.
+ EXPECT_FALSE(parent_support().last_activated_surface_id().is_valid());
+ parent_support().SubmitCompositorFrame(
+ parent_id.local_surface_id(), MakeCompositorFrame({child_id1}, {}, {}));
+
+ // The frame is pending because the child frame hasn't be submitted yet.
+ EXPECT_FALSE(parent_surface()->HasActiveFrame());
+ EXPECT_TRUE(parent_surface()->HasPendingFrame());
+ EXPECT_FALSE(parent_support().last_activated_surface_id().is_valid());
+
+ // Killing the child sink should unblock the frame because it is known
+ // the dependency can never fulfill.
+ frame_sink_manager().InvalidateFrameSinkId(kChildFrameSink1);
+ EXPECT_TRUE(parent_surface()->HasActiveFrame());
+ EXPECT_FALSE(parent_surface()->HasPendingFrame());
+ EXPECT_EQ(parent_id, parent_support().last_activated_surface_id());
+}
+
} // namespace test
} // namespace viz
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 7a8c0af9c0d..92ac07e2685 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
@@ -6,6 +6,7 @@
#include <algorithm>
#include <limits>
+#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -93,8 +94,9 @@ void FrameSinkVideoCapturerImpl::SetResolvedTarget(
resolved_target_->AttachCaptureClient(this);
RefreshEntireSourceSoon();
} else {
- // Not calling consumer_->OnTargetLost() because SetResolvedTarget() should
- // be called by FrameSinkManagerImpl with a valid target very soon.
+ // 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().
}
}
@@ -102,13 +104,6 @@ void FrameSinkVideoCapturerImpl::OnTargetWillGoAway() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
SetResolvedTarget(nullptr);
-
- // TODO(crbug/754872): Remove this, since it's misleading: Resolved targets
- // can be temporarily deleted and then re-created. So, the target really isn't
- // lost.
- if (requested_target_.is_valid() && consumer_) {
- consumer_->OnTargetLost(requested_target_);
- }
}
void FrameSinkVideoCapturerImpl::SetFormat(media::VideoPixelFormat format,
@@ -206,12 +201,17 @@ void FrameSinkVideoCapturerImpl::SetAutoThrottlingEnabled(bool enabled) {
}
void FrameSinkVideoCapturerImpl::ChangeTarget(
- const FrameSinkId& frame_sink_id) {
+ const base::Optional<FrameSinkId>& frame_sink_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- requested_target_ = frame_sink_id;
- SetResolvedTarget(
- frame_sink_manager_->FindCapturableFrameSink(frame_sink_id));
+ if (frame_sink_id) {
+ requested_target_ = *frame_sink_id;
+ SetResolvedTarget(
+ frame_sink_manager_->FindCapturableFrameSink(requested_target_));
+ } else {
+ requested_target_ = FrameSinkId();
+ SetResolvedTarget(nullptr);
+ }
}
void FrameSinkVideoCapturerImpl::Start(
@@ -447,6 +447,10 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame(
frame_metadata.root_scroll_offset.x());
metadata->SetDouble(VideoFrameMetadata::ROOT_SCROLL_OFFSET_Y,
frame_metadata.root_scroll_offset.y());
+ metadata->SetDouble(VideoFrameMetadata::TOP_CONTROLS_HEIGHT,
+ frame_metadata.top_controls_height);
+ metadata->SetDouble(VideoFrameMetadata::TOP_CONTROLS_SHOWN_RATIO,
+ frame_metadata.top_controls_shown_ratio);
oracle_.RecordCapture(utilization);
const int64_t frame_number = next_capture_frame_number_++;
@@ -474,15 +478,15 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame(
media::LetterboxVideoFrame(frame.get(), gfx::Rect());
}
dirty_rect_ = gfx::Rect();
- DidCaptureFrame(frame_number, oracle_frame_number, std::move(frame),
- gfx::Rect());
+ DidCaptureFrame(frame_number, oracle_frame_number, gfx::Rect(),
+ std::move(frame));
return;
}
// For passive refreshes, just deliver the resurrected frame.
if (dirty_rect_.IsEmpty()) {
- DidCaptureFrame(frame_number, oracle_frame_number, std::move(frame),
- content_rect);
+ DidCaptureFrame(frame_number, oracle_frame_number, content_rect,
+ std::move(frame));
return;
}
@@ -493,7 +497,7 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame(
: CopyOutputRequest::ResultFormat::RGBA_BITMAP,
base::BindOnce(&FrameSinkVideoCapturerImpl::DidCopyFrame,
capture_weak_factory_.GetWeakPtr(), frame_number,
- oracle_frame_number, std::move(frame), content_rect)));
+ oracle_frame_number, content_rect, std::move(frame))));
request->set_source(copy_request_source_);
request->set_area(gfx::Rect(source_size));
request->SetScaleRatio(
@@ -510,8 +514,8 @@ void FrameSinkVideoCapturerImpl::MaybeCaptureFrame(
void FrameSinkVideoCapturerImpl::DidCopyFrame(
int64_t frame_number,
OracleFrameNumber oracle_frame_number,
- scoped_refptr<VideoFrame> frame,
const gfx::Rect& content_rect,
+ scoped_refptr<VideoFrame> frame,
std::unique_ptr<CopyOutputResult> result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GE(frame_number, next_delivery_frame_number_);
@@ -566,15 +570,15 @@ void FrameSinkVideoCapturerImpl::DidCopyFrame(
}
}
- DidCaptureFrame(frame_number, oracle_frame_number, std::move(frame),
- content_rect);
+ DidCaptureFrame(frame_number, oracle_frame_number, content_rect,
+ std::move(frame));
}
void FrameSinkVideoCapturerImpl::DidCaptureFrame(
int64_t frame_number,
OracleFrameNumber oracle_frame_number,
- scoped_refptr<VideoFrame> frame,
- const gfx::Rect& content_rect) {
+ const gfx::Rect& content_rect,
+ scoped_refptr<VideoFrame> frame) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GE(frame_number, next_delivery_frame_number_);
@@ -585,12 +589,12 @@ void FrameSinkVideoCapturerImpl::DidCaptureFrame(
// Ensure frames are delivered in-order by using a min-heap, and only
// deliver the next frame(s) in-sequence when they are found at the top.
- delivery_queue_.emplace(frame_number, oracle_frame_number, std::move(frame),
- content_rect);
+ delivery_queue_.emplace(frame_number, oracle_frame_number, content_rect,
+ std::move(frame));
while (delivery_queue_.top().frame_number == next_delivery_frame_number_) {
auto& next = delivery_queue_.top();
- MaybeDeliverFrame(next.oracle_frame_number, std::move(next.frame),
- next.content_rect);
+ MaybeDeliverFrame(next.oracle_frame_number, next.content_rect,
+ std::move(next.frame));
++next_delivery_frame_number_;
delivery_queue_.pop();
if (delivery_queue_.empty()) {
@@ -601,8 +605,8 @@ void FrameSinkVideoCapturerImpl::DidCaptureFrame(
void FrameSinkVideoCapturerImpl::MaybeDeliverFrame(
OracleFrameNumber oracle_frame_number,
- scoped_refptr<VideoFrame> frame,
- const gfx::Rect& content_rect) {
+ const gfx::Rect& content_rect,
+ scoped_refptr<VideoFrame> frame) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The Oracle has the final say in whether frame delivery will proceed. It
@@ -694,14 +698,14 @@ gfx::Size FrameSinkVideoCapturerImpl::AdjustSizeForPixelFormat(
}
FrameSinkVideoCapturerImpl::CapturedFrame::CapturedFrame(
- int64_t fn,
- OracleFrameNumber ofn,
- scoped_refptr<VideoFrame> fr,
- const gfx::Rect& cr)
- : frame_number(fn),
- oracle_frame_number(ofn),
- frame(std::move(fr)),
- content_rect(cr) {}
+ int64_t frame_number,
+ OracleFrameNumber oracle_frame_number,
+ const gfx::Rect& content_rect,
+ scoped_refptr<media::VideoFrame> frame)
+ : frame_number(frame_number),
+ oracle_frame_number(oracle_frame_number),
+ content_rect(content_rect),
+ frame(std::move(frame)) {}
FrameSinkVideoCapturerImpl::CapturedFrame::CapturedFrame(
const CapturedFrame& other) = default;
diff --git a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
index c1c552d4b51..09f9eb021df 100644
--- a/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
+++ b/chromium/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
@@ -7,6 +7,7 @@
#include <stdint.h>
+#include <memory>
#include <queue>
#include "base/macros.h"
@@ -17,7 +18,6 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/unguessable_token.h"
-#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/quads/compositor_frame_metadata.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h"
@@ -37,8 +37,8 @@ class Size;
namespace viz {
-class FrameSinkVideoCapturerManager;
class CopyOutputResult;
+class FrameSinkVideoCapturerManager;
// Captures the frames of a CompositorFrameSink's surface as a video stream. See
// mojom for usage details.
@@ -100,7 +100,7 @@ class VIZ_SERVICE_EXPORT FrameSinkVideoCapturerImpl final
const gfx::Size& max_size,
bool use_fixed_aspect_ratio) final;
void SetAutoThrottlingEnabled(bool enabled) final;
- void ChangeTarget(const FrameSinkId& frame_sink_id) final;
+ void ChangeTarget(const base::Optional<FrameSinkId>& frame_sink_id) final;
void Start(mojom::FrameSinkVideoConsumerPtr consumer) final;
void Stop() final;
void RequestRefreshFrame() final;
@@ -174,8 +174,8 @@ class VIZ_SERVICE_EXPORT FrameSinkVideoCapturerImpl final
// |content_rect| region of a [possibly letterboxed] video |frame|.
void DidCopyFrame(int64_t frame_number,
OracleFrameNumber oracle_frame_number,
- scoped_refptr<media::VideoFrame> frame,
const gfx::Rect& content_rect,
+ scoped_refptr<media::VideoFrame> frame,
std::unique_ptr<CopyOutputResult> result);
// Places the frame in the |delivery_queue_| and calls MaybeDeliverFrame(),
@@ -183,16 +183,16 @@ class VIZ_SERVICE_EXPORT FrameSinkVideoCapturerImpl final
// completed, but unsuccessful capture.
void DidCaptureFrame(int64_t frame_number,
OracleFrameNumber oracle_frame_number,
- scoped_refptr<media::VideoFrame> frame,
- const gfx::Rect& content_rect);
+ const gfx::Rect& content_rect,
+ scoped_refptr<media::VideoFrame> frame);
// Delivers a |frame| to the consumer, if the VideoCaptureOracle allows
// it. |frame| can be null to indicate a completed, but unsuccessful capture.
// In this case, some state will be updated, but nothing will be sent to the
// consumer.
void MaybeDeliverFrame(OracleFrameNumber oracle_frame_number,
- scoped_refptr<media::VideoFrame> frame,
- const gfx::Rect& content_rect);
+ const gfx::Rect& content_rect,
+ scoped_refptr<media::VideoFrame> frame);
// For ARGB format, ensures that every dimension of |size| is positive. For
// I420 format, ensures that every dimension is even and at least 2.
@@ -262,12 +262,12 @@ class VIZ_SERVICE_EXPORT FrameSinkVideoCapturerImpl final
struct CapturedFrame {
int64_t frame_number;
OracleFrameNumber oracle_frame_number;
- scoped_refptr<media::VideoFrame> frame;
gfx::Rect content_rect;
+ scoped_refptr<media::VideoFrame> frame;
CapturedFrame(int64_t frame_number,
OracleFrameNumber oracle_frame_number,
- scoped_refptr<media::VideoFrame> frame,
- const gfx::Rect& content_rect);
+ const gfx::Rect& content_rect,
+ scoped_refptr<media::VideoFrame> frame);
CapturedFrame(const CapturedFrame& other);
~CapturedFrame();
bool operator<(const CapturedFrame& other) const;
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 babe29ca1b1..2711489b84d 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
@@ -5,6 +5,7 @@
#include "components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h"
#include <utility>
+#include <vector>
#include "base/bind.h"
#include "base/callback.h"
@@ -111,7 +112,6 @@ class MockConsumer : public mojom::FrameSinkVideoConsumer {
void(scoped_refptr<VideoFrame> frame,
const gfx::Rect& update_rect,
mojom::FrameSinkVideoConsumerFrameCallbacks* callbacks));
- MOCK_METHOD1(OnTargetLost, void(const FrameSinkId& frame_sink_id));
MOCK_METHOD0(OnStopped, void());
int num_frames_received() const { return frames_.size(); }
@@ -442,37 +442,6 @@ TEST_F(FrameSinkVideoCapturerTest, ResolvesTargetLater) {
EXPECT_EQ(&capturer_, frame_sink_.attached_client());
}
-// Tests that the capturer reports a lost target to the consumer. The consumer
-// may then change targets to capture something else.
-TEST_F(FrameSinkVideoCapturerTest, ReportsTargetLost) {
- FakeCapturableFrameSink prior_frame_sink;
- constexpr FrameSinkId kPriorFrameSinkId = FrameSinkId(1, 2);
- EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kPriorFrameSinkId))
- .WillOnce(Return(&prior_frame_sink));
- EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId))
- .WillOnce(Return(&frame_sink_));
-
- NiceMock<MockConsumer> consumer;
- EXPECT_CALL(consumer, OnTargetLost(kPriorFrameSinkId)).Times(1);
- StartCapture(&consumer);
-
- capturer_.ChangeTarget(kPriorFrameSinkId);
- EXPECT_EQ(kPriorFrameSinkId, capturer_.requested_target());
- EXPECT_EQ(&capturer_, prior_frame_sink.attached_client());
- EXPECT_EQ(nullptr, frame_sink_.attached_client());
-
- NotifyTargetWentAway();
- EXPECT_EQ(nullptr, prior_frame_sink.attached_client());
- EXPECT_EQ(nullptr, frame_sink_.attached_client());
-
- capturer_.ChangeTarget(kFrameSinkId);
- EXPECT_EQ(kFrameSinkId, capturer_.requested_target());
- EXPECT_EQ(nullptr, prior_frame_sink.attached_client());
- EXPECT_EQ(&capturer_, frame_sink_.attached_client());
-
- StopCapture();
-}
-
// Tests that no initial frame is sent after Start() is called until after the
// target has been resolved.
TEST_F(FrameSinkVideoCapturerTest, PostponesCaptureWithoutATarget) {
@@ -481,7 +450,6 @@ TEST_F(FrameSinkVideoCapturerTest, PostponesCaptureWithoutATarget) {
MockConsumer consumer;
EXPECT_CALL(consumer, OnFrameCapturedMock(_, _, _)).Times(0);
- EXPECT_CALL(consumer, OnTargetLost(kFrameSinkId)).Times(0);
EXPECT_CALL(consumer, OnStopped()).Times(1);
StartCapture(&consumer);
@@ -526,7 +494,6 @@ TEST_F(FrameSinkVideoCapturerTest, CapturesCompositedFrames) {
3 * FrameSinkVideoCapturerImpl::kDesignLimitMaxFrames;
EXPECT_CALL(consumer, OnFrameCapturedMock(_, _, _))
.Times(num_refresh_frames + num_update_frames);
- EXPECT_CALL(consumer, OnTargetLost(_)).Times(0);
EXPECT_CALL(consumer, OnStopped()).Times(1);
StartCapture(&consumer);
@@ -778,7 +745,6 @@ TEST_F(FrameSinkVideoCapturerTest, CancelsInFlightCapturesOnStop) {
// Start capturing to the first consumer.
MockConsumer consumer;
EXPECT_CALL(consumer, OnFrameCapturedMock(_, _, _)).Times(2);
- EXPECT_CALL(consumer, OnTargetLost(_)).Times(0);
EXPECT_CALL(consumer, OnStopped()).Times(1);
StartCapture(&consumer);
// With the start, an immediate refresh should have occurred.
@@ -814,7 +780,6 @@ TEST_F(FrameSinkVideoCapturerTest, CancelsInFlightCapturesOnStop) {
const int num_captures_for_second_consumer = 3;
EXPECT_CALL(consumer2, OnFrameCapturedMock(_, _, _))
.Times(num_captures_for_second_consumer);
- EXPECT_CALL(consumer2, OnTargetLost(_)).Times(0);
EXPECT_CALL(consumer2, OnStopped()).Times(1);
StartCapture(&consumer2);
// With the start, a refresh was attempted, but since the attempt occurred so
@@ -868,7 +833,6 @@ TEST_F(FrameSinkVideoCapturerTest, EventuallySendsARefreshFrame) {
const int num_update_frames = 3;
EXPECT_CALL(consumer, OnFrameCapturedMock(_, _, _))
.Times(num_refresh_frames + num_update_frames);
- EXPECT_CALL(consumer, OnTargetLost(_)).Times(0);
EXPECT_CALL(consumer, OnStopped()).Times(1);
StartCapture(&consumer);
@@ -925,7 +889,6 @@ TEST_F(FrameSinkVideoCapturerTest, CompositorFrameMetadataReachesConsumer) {
const int num_update_frames = 1;
EXPECT_CALL(consumer, OnFrameCapturedMock(_, _, _))
.Times(num_refresh_frames + num_update_frames);
- EXPECT_CALL(consumer, OnTargetLost(_)).Times(0);
EXPECT_CALL(consumer, OnStopped()).Times(1);
StartCapture(&consumer);
diff --git a/chromium/components/viz/service/frame_sinks/video_capture/video_capture_overlay.cc b/chromium/components/viz/service/frame_sinks/video_capture/video_capture_overlay.cc
new file mode 100644
index 00000000000..75be2cb7aca
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/video_capture/video_capture_overlay.cc
@@ -0,0 +1,574 @@
+// 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/frame_sinks/video_capture/video_capture_overlay.h"
+
+#include <algorithm>
+#include <cmath>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/trace_event/trace_event.h"
+#include "media/base/limits.h"
+#include "media/base/video_frame.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkFilterQuality.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+
+using media::VideoFrame;
+using media::VideoPixelFormat;
+
+namespace viz {
+
+VideoCaptureOverlay::FrameSource::~FrameSource() = default;
+
+VideoCaptureOverlay::VideoCaptureOverlay(FrameSource* frame_source)
+ : frame_source_(frame_source) {
+ DCHECK(frame_source_);
+}
+
+VideoCaptureOverlay::~VideoCaptureOverlay() = default;
+
+void VideoCaptureOverlay::SetImageAndBounds(const SkBitmap& image,
+ const gfx::RectF& bounds) {
+ const gfx::Rect old_rect = ComputeSourceMutationRect();
+
+ image_ = image;
+ bounds_ = bounds;
+
+ // Reset the cached sprite since the source image has been changed.
+ sprite_ = nullptr;
+
+ const gfx::Rect new_rect = ComputeSourceMutationRect();
+ if (!new_rect.IsEmpty() || !old_rect.IsEmpty()) {
+ frame_source_->InvalidateRect(old_rect);
+ frame_source_->InvalidateRect(new_rect);
+ frame_source_->RequestRefreshFrame();
+ }
+}
+
+void VideoCaptureOverlay::SetBounds(const gfx::RectF& bounds) {
+ if (bounds_ != bounds) {
+ const gfx::Rect old_rect = ComputeSourceMutationRect();
+ bounds_ = bounds;
+ const gfx::Rect new_rect = ComputeSourceMutationRect();
+ if (!new_rect.IsEmpty() || !old_rect.IsEmpty()) {
+ frame_source_->InvalidateRect(old_rect);
+ frame_source_->InvalidateRect(new_rect);
+ frame_source_->RequestRefreshFrame();
+ }
+ }
+}
+
+namespace {
+
+// Scales a |relative| rect having coordinates in the range [0.0,1.0) by the
+// given |span|, snapping all coordinates to even numbers.
+gfx::Rect ToAbsoluteBoundsForI420(const gfx::RectF& relative,
+ const gfx::Rect& span) {
+ const float absolute_left = std::fma(relative.x(), span.width(), span.x());
+ const float absolute_top = std::fma(relative.y(), span.height(), span.y());
+ const float absolute_right =
+ std::fma(relative.right(), span.width(), span.x());
+ const float absolute_bottom =
+ std::fma(relative.bottom(), span.height(), span.y());
+
+ // Compute the largest I420-friendly Rect that is fully-enclosed by the
+ // absolute rect. Use saturated_cast<> to restrict all extreme results [and
+ // Inf and NaN] to a safe range of integers.
+ const int snapped_left =
+ base::saturated_cast<int16_t>(std::ceil(absolute_left / 2.0f)) * 2;
+ const int snapped_top =
+ base::saturated_cast<int16_t>(std::ceil(absolute_top / 2.0f)) * 2;
+ const int snapped_right =
+ base::saturated_cast<int16_t>(std::floor(absolute_right / 2.0f)) * 2;
+ const int snapped_bottom =
+ base::saturated_cast<int16_t>(std::floor(absolute_bottom / 2.0f)) * 2;
+ return gfx::Rect(snapped_left, snapped_top,
+ std::max(0, snapped_right - snapped_left),
+ std::max(0, snapped_bottom - snapped_top));
+}
+
+// Shrinks the given |rect| by the minimum amount necessary to align its corners
+// to even-numbered coordinates. |rect| is assumed to have non-negative values
+// for its coordinates.
+gfx::Rect MinimallyShrinkRectForI420(const gfx::Rect& rect) {
+ DCHECK(gfx::Rect(0, 0, media::limits::kMaxDimension,
+ media::limits::kMaxDimension)
+ .Contains(rect));
+ const int left = rect.x() + (rect.x() % 2);
+ const int top = rect.y() + (rect.y() % 2);
+ const int right = rect.right() - (rect.right() % 2);
+ const int bottom = rect.bottom() - (rect.bottom() % 2);
+ return gfx::Rect(left, top, std::max(0, right - left),
+ std::max(0, bottom - top));
+}
+
+} // namespace
+
+VideoCaptureOverlay::OnceRenderer VideoCaptureOverlay::MakeRenderer(
+ const gfx::Rect& region_in_frame,
+ const VideoPixelFormat frame_format,
+ const gfx::ColorSpace& frame_color_space) {
+ // If there's no image set yet, punt.
+ if (image_.drawsNothing()) {
+ return VideoCaptureOverlay::OnceRenderer();
+ }
+
+ // Determine the bounds of the sprite to be blitted onto the video frame. The
+ // calculations here align to the 2x2 pixel-quads, since dealing with
+ // fractions or partial I420 chroma plane alpha-blending would greatly
+ // complexify the blitting algorithm later on. This introduces a little
+ // inaccuracy in the size and position of the overlay in the final result, but
+ // should be an acceptable trade-off for all use cases.
+ const gfx::Rect bounds_in_frame =
+ ToAbsoluteBoundsForI420(bounds_, region_in_frame);
+ // If the sprite's size will be unreasonably large, punt.
+ if (bounds_in_frame.width() > media::limits::kMaxDimension ||
+ bounds_in_frame.height() > media::limits::kMaxDimension) {
+ return VideoCaptureOverlay::OnceRenderer();
+ }
+
+ // Compute the blit rect: the region of the frame to be modified by future
+ // Sprite::Blit() calls. First, |region_in_frame| must be shrunk to have
+ // even-valued coordinates to ensure the final blit rect is I420-friendly.
+ // Then, the shrunk |region_in_frame| is used to clip |bounds_in_frame|.
+ gfx::Rect blit_rect = MinimallyShrinkRectForI420(region_in_frame);
+ blit_rect.Intersect(bounds_in_frame);
+ // If the two rects didn't intersect at all (i.e., everything has been
+ // clipped), punt.
+ if (blit_rect.IsEmpty()) {
+ return VideoCaptureOverlay::OnceRenderer();
+ }
+
+ // If the cached sprite does not match the computed scaled size, pixel format,
+ // and/or color space, create a new instance for this (and future) renderers.
+ if (!sprite_ || sprite_->size() != bounds_in_frame.size() ||
+ sprite_->format() != frame_format ||
+ sprite_->color_space() != frame_color_space) {
+ sprite_ = base::MakeRefCounted<Sprite>(image_, bounds_in_frame.size(),
+ frame_format, frame_color_space);
+ }
+
+ return base::BindOnce(&Sprite::Blit, sprite_, bounds_in_frame.origin(),
+ blit_rect);
+}
+
+// static
+VideoCaptureOverlay::OnceRenderer VideoCaptureOverlay::MakeCombinedRenderer(
+ const std::vector<std::unique_ptr<VideoCaptureOverlay>>& overlays,
+ const gfx::Rect& region_in_frame,
+ const VideoPixelFormat frame_format,
+ const gfx::ColorSpace& frame_color_space) {
+ if (overlays.empty()) {
+ return VideoCaptureOverlay::OnceRenderer();
+ }
+
+ std::vector<OnceRenderer> renderers;
+ for (const std::unique_ptr<VideoCaptureOverlay>& overlay : overlays) {
+ renderers.emplace_back(overlay->MakeRenderer(region_in_frame, frame_format,
+ frame_color_space));
+ if (renderers.back().is_null()) {
+ renderers.pop_back();
+ }
+ }
+
+ if (renderers.empty()) {
+ return VideoCaptureOverlay::OnceRenderer();
+ }
+
+ return base::BindOnce(
+ [](std::vector<OnceRenderer> renderers, VideoFrame* frame) {
+ for (OnceRenderer& renderer : renderers) {
+ std::move(renderer).Run(frame);
+ }
+ },
+ std::move(renderers));
+}
+
+gfx::Rect VideoCaptureOverlay::ComputeSourceMutationRect() const {
+ if (!image_.drawsNothing() && !bounds_.IsEmpty()) {
+ const gfx::Size& source_size = frame_source_->GetSourceSize();
+ gfx::Rect result = gfx::ToEnclosingRect(
+ gfx::ScaleRect(bounds_, source_size.width(), source_size.height()));
+ result.Intersect(gfx::Rect(source_size));
+ return result;
+ }
+ return gfx::Rect();
+}
+
+VideoCaptureOverlay::Sprite::Sprite(const SkBitmap& image,
+ const gfx::Size& size,
+ const VideoPixelFormat format,
+ const gfx::ColorSpace& color_space)
+ : image_(image), size_(size), format_(format), color_space_(color_space) {
+ DCHECK(!image_.isNull());
+}
+
+VideoCaptureOverlay::Sprite::~Sprite() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+namespace {
+
+// Returns the pointer to the element at the |offset| position, given a pointer
+// to the element for (0,0) in a row-major image plane.
+template <typename Pointer>
+Pointer PositionPointerInPlane(Pointer plane_begin,
+ int stride,
+ const gfx::Point& offset) {
+ return plane_begin + (offset.y() * stride) + offset.x();
+}
+
+// Returns the pointer to the element at the |offset| position, given a pointer
+// to the element for (0,0) in a row-major bitmap with 4 elements per pixel.
+template <typename Pointer>
+Pointer PositionPointerARGB(Pointer pixels_begin,
+ int stride,
+ const gfx::Point& offset) {
+ return pixels_begin + (offset.y() * stride) + (4 * offset.x());
+}
+
+// Transforms the lower 8 bits of |value| from the [0,255] range to the
+// normalized floating-point [0.0,1.0] range.
+float From255(uint8_t value) {
+ return value / 255.0f;
+}
+
+// Transforms the value from the normalized floating-point [0.0,1.0] range to an
+// unsigned int in the [0,255] range, capping any out-of-range values.
+uint32_t ToClamped255(float value) {
+ value = std::fma(value, 255.0f, 0.5f /* rounding */);
+ return base::saturated_cast<uint8_t>(value);
+}
+
+} // namespace
+
+void VideoCaptureOverlay::Sprite::Blit(const gfx::Point& position,
+ const gfx::Rect& blit_rect,
+ VideoFrame* frame) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(frame);
+ DCHECK_EQ(format_, frame->format());
+ DCHECK(frame->visible_rect().Contains(blit_rect));
+
+ TRACE_EVENT2("gpu.capture", "VideoCaptureOverlay::Sprite::Blit", "x",
+ position.x(), "y", position.y());
+
+ if (!transformed_image_) {
+ TransformImageOnce();
+ }
+
+ // Compute the left-most and top-most pixel to source from the transformed
+ // image. This is usually (0,0) unless only part of the sprite is being
+ // blitted (i.e., cropped at the edge(s) of the video frame).
+ gfx::Point src_origin = blit_rect.origin() - position.OffsetFromOrigin();
+ DCHECK(gfx::Rect(size_).Contains(gfx::Rect(src_origin, blit_rect.size())));
+
+ // Blit the sprite (src) onto the video frame (dest). One of two algorithms is
+ // used, depending on the video frame's format, as the blending calculations
+ // and data layout/format are different.
+ switch (frame->format()) {
+ case media::PIXEL_FORMAT_I420: {
+ // Core assumption: All coordinates are aligned to even-numbered
+ // coordinates.
+ DCHECK_EQ(src_origin.x() % 2, 0);
+ DCHECK_EQ(src_origin.y() % 2, 0);
+ DCHECK_EQ(blit_rect.x() % 2, 0);
+ DCHECK_EQ(blit_rect.y() % 2, 0);
+ DCHECK_EQ(blit_rect.width() % 2, 0);
+ DCHECK_EQ(blit_rect.height() % 2, 0);
+
+ // Helper function to execute a "SrcOver" blit from |src| to |dst|, and
+ // store the results back in |dst|.
+ const auto BlitOntoPlane = [](const gfx::Size& blit_size, int src_stride,
+ const float* src, const float* under_weight,
+ int dst_stride, uint8_t* dst) {
+ for (int row = 0; row < blit_size.height(); ++row, src += src_stride,
+ under_weight += src_stride, dst += dst_stride) {
+ for (int col = 0; col < blit_size.width(); ++col) {
+ dst[col] = ToClamped255(
+ std::fma(From255(dst[col]), under_weight[col], src[col]));
+ }
+ }
+ };
+
+ // Blit the Y plane: |src| points to the pre-multiplied luma values, while
+ // |under_weight| points to the "one minus src alpha" values. Both have
+ // the same stride, |src_stride|.
+ int src_stride = size_.width();
+ const float* under_weight = PositionPointerInPlane(
+ transformed_image_.get(), src_stride, src_origin);
+ const int num_pixels = size_.GetArea();
+ const float* src = under_weight + num_pixels;
+ // Likewise, start |dst| at the upper-left-most pixel within the video
+ // frame's Y plane that will be SrcOver'ed.
+ int dst_stride = frame->stride(VideoFrame::kYPlane);
+ uint8_t* dst =
+ PositionPointerInPlane(frame->visible_data(VideoFrame::kYPlane),
+ dst_stride, blit_rect.origin());
+ BlitOntoPlane(blit_rect.size(), src_stride, src, under_weight, dst_stride,
+ dst);
+
+ // Blit the U and V planes similarly to the Y plane, but reduce all
+ // coordinates by 2x2.
+ src_stride = size_.width() / 2;
+ src_origin = gfx::Point(src_origin.x() / 2, src_origin.y() / 2);
+ under_weight = PositionPointerInPlane(
+ transformed_image_.get() + 2 * num_pixels, src_stride, src_origin);
+ const int num_chroma_pixels = size_.GetArea() / 4;
+ src = under_weight + num_chroma_pixels;
+ dst_stride = frame->stride(VideoFrame::kUPlane);
+ const gfx::Rect chroma_blit_rect(blit_rect.x() / 2, blit_rect.y() / 2,
+ blit_rect.width() / 2,
+ blit_rect.height() / 2);
+ dst = PositionPointerInPlane(frame->visible_data(VideoFrame::kUPlane),
+ dst_stride, chroma_blit_rect.origin());
+ BlitOntoPlane(chroma_blit_rect.size(), src_stride, src, under_weight,
+ dst_stride, dst);
+ src += num_chroma_pixels;
+ dst_stride = frame->stride(VideoFrame::kVPlane);
+ dst = PositionPointerInPlane(frame->visible_data(VideoFrame::kVPlane),
+ dst_stride, chroma_blit_rect.origin());
+ BlitOntoPlane(chroma_blit_rect.size(), src_stride, src, under_weight,
+ dst_stride, dst);
+
+ break;
+ }
+
+ case media::PIXEL_FORMAT_ARGB: {
+ // Start |src| at the upper-left-most pixel within |transformed_image_|
+ // that will be blitted.
+ const int src_stride = size_.width() * 4;
+ const float* src =
+ PositionPointerARGB(transformed_image_.get(), src_stride, src_origin);
+
+ // Likewise, start |dst| at the upper-left-most pixel within the video
+ // frame that will be SrcOver'ed.
+ const int dst_stride = frame->stride(VideoFrame::kARGBPlane);
+ DCHECK_EQ(dst_stride % sizeof(uint32_t), 0u);
+ uint8_t* dst =
+ PositionPointerARGB(frame->visible_data(VideoFrame::kARGBPlane),
+ dst_stride, blit_rect.origin());
+ DCHECK_EQ((dst - frame->visible_data(VideoFrame::kARGBPlane)) %
+ sizeof(uint32_t),
+ 0u);
+
+ // Blend each sprite pixel over the corresponding pixel in the video
+ // frame, and store the result back in the video frame. Note that the
+ // video frame format does NOT have color values pre-multiplied by the
+ // alpha.
+ for (int row = 0; row < blit_rect.height();
+ ++row, src += src_stride, dst += dst_stride) {
+ uint32_t* dst_pixel = reinterpret_cast<uint32_t*>(dst);
+ for (int col = 0; col < blit_rect.width(); ++col) {
+ const int src_idx = 4 * col;
+ const float src_alpha = src[src_idx];
+ const float dst_weight =
+ From255(dst_pixel[col] >> 24) * (1.0f - src_alpha);
+ const float out_alpha = src_alpha + dst_weight;
+ float out_red = std::fma(From255(dst_pixel[col] >> 16), dst_weight,
+ src[src_idx + 1]);
+ float out_green = std::fma(From255(dst_pixel[col] >> 8), dst_weight,
+ src[src_idx + 2]);
+ float out_blue = std::fma(From255(dst_pixel[col] >> 0), dst_weight,
+ src[src_idx + 3]);
+ if (out_alpha != 0.0f) {
+ out_red /= out_alpha;
+ out_green /= out_alpha;
+ out_blue /= out_alpha;
+ }
+ dst_pixel[col] =
+ ((ToClamped255(out_alpha) << 24) | (ToClamped255(out_red) << 16) |
+ (ToClamped255(out_green) << 8) | (ToClamped255(out_blue) << 0));
+ }
+ }
+
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+void VideoCaptureOverlay::Sprite::TransformImageOnce() {
+ TRACE_EVENT2("gpu.capture", "VideoCaptureOverlay::Sprite::TransformImageOnce",
+ "width", size_.width(), "height", size_.height());
+
+ // Scale the source |image_| to match the format and size required. For the
+ // purposes of color space conversion, the alpha must not be pre-multiplied.
+ const SkImageInfo scaled_image_format =
+ SkImageInfo::Make(size_.width(), size_.height(), kN32_SkColorType,
+ kUnpremul_SkAlphaType, image_.refColorSpace());
+ SkBitmap scaled_image;
+ if (image_.info() == scaled_image_format) {
+ scaled_image = image_;
+ } else {
+ if (!scaled_image.tryAllocPixels(scaled_image_format) ||
+ !image_.pixmap().scalePixels(scaled_image.pixmap(),
+ kMedium_SkFilterQuality)) {
+ // If the allocation, format conversion and/or scaling failed, just reset
+ // the |scaled_image|. This will be checked below.
+ scaled_image.reset();
+ }
+ }
+
+ gfx::ColorSpace image_color_space;
+ if (scaled_image.colorSpace()) {
+ image_color_space = gfx::ColorSpace(*scaled_image.colorSpace());
+ DCHECK(image_color_space.IsValid());
+ } else {
+ // Assume a default linear color space, if no color space was provided.
+ DCHECK(!image_.colorSpace()); // Skia should have set it!
+ image_color_space = gfx::ColorSpace(
+ gfx::ColorSpace::PrimaryID::BT709, gfx::ColorSpace::TransferID::LINEAR,
+ gfx::ColorSpace::MatrixID::RGB, gfx::ColorSpace::RangeID::FULL);
+ }
+
+ // The source image is no longer needed. Reset it to dereference pixel memory.
+ image_.reset();
+
+ // Populate |colors| and |alphas| from the |scaled_image|. If the image
+ // scaling operation failed, this sprite should draw nothing, and so fully
+ // transparent pixels will be generated instead.
+ const int num_pixels = size_.GetArea();
+ std::unique_ptr<float[]> alphas(new float[num_pixels]);
+ std::unique_ptr<gfx::ColorTransform::TriStim[]> colors(
+ new gfx::ColorTransform::TriStim[num_pixels]);
+ if (scaled_image.drawsNothing()) {
+ std::fill(alphas.get(), alphas.get() + num_pixels, 0.0f);
+ std::fill(colors.get(), colors.get() + num_pixels,
+ gfx::ColorTransform::TriStim());
+ } else {
+ int pos = 0;
+ for (int y = 0; y < size_.height(); ++y) {
+ const uint32_t* src = scaled_image.getAddr32(0, y);
+ for (int x = 0; x < size_.width(); ++x) {
+ const uint32_t pixel = src[x];
+ alphas[pos] = ((pixel >> SK_A32_SHIFT) & 0xff) / 255.0f;
+ colors[pos].SetPoint(((pixel >> SK_R32_SHIFT) & 0xff) / 255.0f,
+ ((pixel >> SK_G32_SHIFT) & 0xff) / 255.0f,
+ ((pixel >> SK_B32_SHIFT) & 0xff) / 255.0f);
+ ++pos;
+ }
+ }
+ }
+
+ // Transform the colors, if needed. This may perform RGB→YUV conversion.
+ if (image_color_space != color_space_) {
+ const auto color_transform = gfx::ColorTransform::NewColorTransform(
+ image_color_space, color_space_,
+ gfx::ColorTransform::Intent::INTENT_ABSOLUTE);
+ color_transform->Transform(colors.get(), num_pixels);
+ }
+
+ switch (format_) {
+ case media::PIXEL_FORMAT_I420: {
+ // Produce 5 planes of data: The "one minus alpha" plane, the Y plane, the
+ // subsampled "one minus alpha" plane, the U plane, and the V plane.
+ // Pre-multiply the colors by the alpha to prevent extra work in multiple
+ // later Blit() calls.
+ DCHECK_EQ(size_.width() % 2, 0);
+ DCHECK_EQ(size_.height() % 2, 0);
+ const int num_chroma_pixels = size_.GetArea() / 4;
+ transformed_image_.reset(
+ new float[num_pixels * 2 + num_chroma_pixels * 3]);
+
+ // Copy the alpha values, and pre-multiply the luma values by the alpha.
+ float* out_1_minus_alpha = transformed_image_.get();
+ float* out_luma = out_1_minus_alpha + num_pixels;
+ for (int i = 0; i < num_pixels; ++i) {
+ const float alpha = alphas[i];
+ out_1_minus_alpha[i] = 1.0f - alpha;
+ out_luma[i] = colors[i].x() * alpha;
+ }
+
+ // Downscale the alpha, U, and V planes by 2x2, and pre-multiply the
+ // chroma values by the alpha.
+ float* out_uv_1_minus_alpha = out_luma + num_pixels;
+ float* out_u = out_uv_1_minus_alpha + num_chroma_pixels;
+ float* out_v = out_u + num_chroma_pixels;
+ const float* alpha_row0 = alphas.get();
+ const float* const alpha_row_end = alpha_row0 + num_pixels;
+ const gfx::ColorTransform::TriStim* color_row0 = colors.get();
+ while (alpha_row0 < alpha_row_end) {
+ const float* alpha_row1 = alpha_row0 + size_.width();
+ const gfx::ColorTransform::TriStim* color_row1 =
+ color_row0 + size_.width();
+ for (int col = 0; col < size_.width(); col += 2) {
+ // First, the downscaled alpha is the average of the four original
+ // alpha values:
+ //
+ // sum_of_alphas = a[r,c] + a[r,c+1] + a[r+1,c] + a[r+1,c+1];
+ // average_alpha = sum_of_alphas / 4
+ *(out_uv_1_minus_alpha++) =
+ std::fma(alpha_row0[col] + alpha_row0[col + 1] + alpha_row1[col] +
+ alpha_row1[col + 1],
+ -1.0f / 4.0f, 1.0f);
+ // Then, the downscaled chroma values are the weighted average of the
+ // four original chroma values (weighed by alpha):
+ //
+ // weighted_sum_of_chromas =
+ // c[r,c]*a[r,c] + c[r,c+1]*a[r,c+1] +
+ // c[r+1,c]*a[r+1,c] + c[r+1,c+1]*a[r+1,c+1]
+ // sum_of_weights = sum_of_alphas;
+ // average_chroma = weighted_sum_of_chromas / sum_of_weights
+ //
+ // But then, because the chroma is to be pre-multiplied by the alpha,
+ // the calculations simplify, as follows:
+ //
+ // premul_chroma = average_chroma * average_alpha
+ // = (weighted_sum_of_chromas / sum_of_alphas) *
+ // (sum_of_alphas / 4)
+ // = weighted_sum_of_chromas / 4
+ //
+ // This also automatically solves a special case, when sum_of_alphas
+ // is zero: With the simplified calculations, there is no longer a
+ // "divide-by-zero guard" needed; and the result in this case will be
+ // a zero chroma, which is perfectly acceptable behavior.
+ *(out_u++) = ((color_row0[col].y() * alpha_row0[col]) +
+ (color_row0[col + 1].y() * alpha_row0[col + 1]) +
+ (color_row1[col].y() * alpha_row1[col]) +
+ (color_row1[col + 1].y() * alpha_row1[col + 1])) /
+ 4.0f;
+ *(out_v++) = ((color_row0[col].z() * alpha_row0[col]) +
+ (color_row0[col + 1].z() * alpha_row0[col + 1]) +
+ (color_row1[col].z() * alpha_row1[col]) +
+ (color_row1[col + 1].z() * alpha_row1[col + 1])) /
+ 4.0f;
+ }
+ alpha_row0 = alpha_row1 + size_.width();
+ color_row0 = color_row1 + size_.width();
+ }
+
+ break;
+ }
+
+ case media::PIXEL_FORMAT_ARGB: {
+ // Produce ARGB pixels from |colors| and |alphas|. Pre-multiply the colors
+ // by the alpha to prevent extra work in multiple later Blit() calls.
+ transformed_image_.reset(new float[num_pixels * 4]);
+ float* out = transformed_image_.get();
+ for (int i = 0; i < num_pixels; ++i) {
+ const float alpha = alphas[i];
+ *(out++) = alpha;
+ *(out++) = colors[i].x() * alpha;
+ *(out++) = colors[i].y() * alpha;
+ *(out++) = colors[i].z() * alpha;
+ }
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/video_capture/video_capture_overlay.h b/chromium/components/viz/service/frame_sinks/video_capture/video_capture_overlay.h
new file mode 100644
index 00000000000..1d39a15dfe2
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/video_capture/video_capture_overlay.h
@@ -0,0 +1,186 @@
+// 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_FRAME_SINKS_VIDEO_CAPTURE_VIDEO_CAPTURE_OVERLAY_H_
+#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_VIDEO_CAPTURE_VIDEO_CAPTURE_OVERLAY_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequence_checker.h"
+#include "components/viz/service/viz_service_export.h"
+#include "media/base/video_types.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/color_space.h"
+#include "ui/gfx/color_transform.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace media {
+class VideoFrame;
+}
+
+namespace viz {
+
+// An overlay image to be blitted onto video frames. A mojo client sets the
+// image, position, and size of the overlay in the video frame; and then this
+// VideoCaptureOverlay scales the image and maps its color space to match that
+// of the video frame before the blitting.
+//
+// As an optimization, the client's bitmap image is transformed (scaled, color
+// space converted, and pre-multiplied by alpha), and then this cached Sprite is
+// re-used for blitting to all successive video frames until some change
+// requires a different transformation. MakeRenderer() produces a Renderer
+// callback that holds a reference to an existing Sprite, or will create a new
+// one if necessary. The Renderer callback can then be run at any point in the
+// future, unaffected by later image, size, or color space settings changes.
+//
+// The blit algorithm uses naive linear blending. Thus, the use of non-linear
+// color spaces will cause loses in color accuracy.
+//
+// TODO(crbug.com/810133): Override the mojom::FrameSinkVideoCaptureOverlay
+// interface.
+class VIZ_SERVICE_EXPORT VideoCaptureOverlay {
+ public:
+ // Interface for notifying the frame source when changes to the overlay's
+ // state occur.
+ class VIZ_SERVICE_EXPORT FrameSource {
+ public:
+ // Returns the current size of the source, or empty if unknown.
+ virtual gfx::Size GetSourceSize() = 0;
+
+ // Notifies the FrameSource that the given source |rect| needs to be
+ // re-captured soon. One or more calls to this method will be followed-up
+ // with a call to RequestRefreshFrame().
+ virtual void InvalidateRect(const gfx::Rect& rect) = 0;
+
+ // Notifies the FrameSource that another frame should be captured and have
+ // its VideoCaptureOverlay re-rendered soon to reflect an updated overlay
+ // image and/or position.
+ virtual void RequestRefreshFrame() = 0;
+
+ protected:
+ virtual ~FrameSource();
+ };
+
+ // A OnceCallback that, when run, renders the overlay on a VideoFrame.
+ using OnceRenderer = base::OnceCallback<void(media::VideoFrame*)>;
+
+ // |frame_source| must outlive this instance.
+ explicit VideoCaptureOverlay(FrameSource* frame_source);
+
+ ~VideoCaptureOverlay();
+
+ // Sets/Changes the overlay |image| and its position and size, relative to the
+ // source content. |bounds| consists of coordinates where the range [0.0,1.0)
+ // indicates the relative position+size within the bounds of the source
+ // content (e.g., 0.0 refers to the top or left edge; 1.0 to just after the
+ // bottom or right edge). Pass empty |bounds| to temporarily hide the overlay
+ // until a later call to SetBounds().
+ void SetImageAndBounds(const SkBitmap& image, const gfx::RectF& bounds);
+ void SetBounds(const gfx::RectF& bounds);
+
+ // Returns a OnceCallback that, when run, renders this VideoCaptureOverlay on
+ // a VideoFrame. The overlay's position and size are computed based on the
+ // given content |region_in_frame|, and its color space is converted to match
+ // the |frame_color_space|. Returns a null OnceCallback if there is nothing to
+ // render at this time.
+ OnceRenderer MakeRenderer(const gfx::Rect& region_in_frame,
+ const media::VideoPixelFormat frame_format,
+ const gfx::ColorSpace& frame_color_space);
+
+ // Returns a OnceCallback that renders all of the given |overlays| in
+ // order. The remaining arguments are the same as in MakeRenderer(). This is a
+ // convenience that produces a single callback, so that client code need not
+ // deal with collections of callbacks. Returns a null OnceCallback if there is
+ // nothing to render at this time.
+ static OnceRenderer MakeCombinedRenderer(
+ const std::vector<std::unique_ptr<VideoCaptureOverlay>>& overlays,
+ const gfx::Rect& region_in_frame,
+ const media::VideoPixelFormat frame_format,
+ const gfx::ColorSpace& frame_color_space);
+
+ private:
+ // Transforms the overlay SkBitmap image by scaling and converting its color
+ // space, and then blitting it onto a VideoFrame. The transformation is lazy:
+ // Meaning, a reference to the SkBitmap image is held until the first call to
+ // Blit(), where the transformation is then executed and the reference to the
+ // original SkBitmap dropped. The transformed data is then cached for re-use
+ // for later Blit() calls.
+ class Sprite : public base::RefCounted<Sprite> {
+ public:
+ Sprite(const SkBitmap& image,
+ const gfx::Size& size,
+ const media::VideoPixelFormat format,
+ const gfx::ColorSpace& color_space);
+
+ const gfx::Size& size() const { return size_; }
+ media::VideoPixelFormat format() const { return format_; }
+ const gfx::ColorSpace& color_space() const { return color_space_; }
+
+ void Blit(const gfx::Point& position,
+ const gfx::Rect& blit_rect,
+ media::VideoFrame* frame);
+
+ private:
+ friend class base::RefCounted<Sprite>;
+ ~Sprite();
+
+ void TransformImageOnce();
+
+ // As Sprites can be long-lived and hidden from external code within
+ // callbacks, ensure that all Blit() calls are in-sequence.
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ // If not null, this is the original, unscaled overlay image. After
+ // TransformImageOnce() has been called, this is set to null.
+ SkBitmap image_;
+
+ // The size, format, and color space of the cached transformed image.
+ const gfx::Size size_;
+ const media::VideoPixelFormat format_;
+ const gfx::ColorSpace color_space_;
+
+ // The transformed source image data. For blitting to ARGB format video
+ // frames, the source image data will consist of 4 elements per pixel pixel
+ // (A, R, G, B). For blitting to the I420 format, the source image data is
+ // not interlaved: Instead, there are 5 planes of data (one minus alpha, Y,
+ // subsampled one minus alpha, U, V). For both formats, the color components
+ // are premultiplied for more-efficient Blit()'s.
+ std::unique_ptr<float[]> transformed_image_;
+
+ DISALLOW_COPY_AND_ASSIGN(Sprite);
+ };
+
+ // Computes the region of the source that, if changed, would require
+ // re-rendering the overlay.
+ gfx::Rect ComputeSourceMutationRect() const;
+
+ FrameSource* const frame_source_;
+
+ // The currently-set overlay image.
+ SkBitmap image_;
+
+ // If empty, the overlay is currently hidden. Otherwise, this consists of
+ // coordinates where the range [0.0,1.0) indicates the relative position+size
+ // within the bounds of the video frame's content region (e.g., 0.0 refers to
+ // the top or left edge; 1.0 to just after the bottom or right edge).
+ gfx::RectF bounds_;
+
+ // The current Sprite. This is set to null whenever a settings change requires
+ // a new Sprite to be generated from the |image_|.
+ scoped_refptr<Sprite> sprite_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoCaptureOverlay);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_VIDEO_CAPTURE_VIDEO_CAPTURE_OVERLAY_H_
diff --git a/chromium/components/viz/service/frame_sinks/video_capture/video_capture_overlay_unittest.cc b/chromium/components/viz/service/frame_sinks/video_capture/video_capture_overlay_unittest.cc
new file mode 100644
index 00000000000..2bb5112a2a6
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/video_capture/video_capture_overlay_unittest.cc
@@ -0,0 +1,554 @@
+// 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/frame_sinks/video_capture/video_capture_overlay.h"
+
+#include <array>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/optional.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "cc/test/pixel_comparator.h"
+#include "cc/test/pixel_test_utils.h"
+#include "components/viz/test/paths.h"
+#include "media/base/video_frame.h"
+#include "media/base/video_types.h"
+#include "media/base/video_util.h"
+#include "testing/gmock/include/gmock/gmock.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/SkPixmap.h"
+#include "ui/gfx/color_space.h"
+#include "ui/gfx/color_transform.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size.h"
+
+using media::VideoFrame;
+using media::VideoPixelFormat;
+
+using testing::_;
+using testing::InvokeWithoutArgs;
+using testing::NiceMock;
+using testing::Return;
+using testing::StrictMock;
+
+namespace viz {
+namespace {
+
+class MockFrameSource : public VideoCaptureOverlay::FrameSource {
+ public:
+ MOCK_METHOD0(GetSourceSize, gfx::Size());
+ MOCK_METHOD1(InvalidateRect, void(const gfx::Rect& rect));
+ MOCK_METHOD0(RequestRefreshFrame, void());
+};
+
+class VideoCaptureOverlayTest : public testing::Test {
+ public:
+ VideoCaptureOverlayTest() = default;
+
+ NiceMock<MockFrameSource>* frame_source() { return &frame_source_; }
+
+ std::unique_ptr<VideoCaptureOverlay> CreateOverlay() {
+ return std::make_unique<VideoCaptureOverlay>(frame_source());
+ }
+
+ void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
+
+ // Makes a SkBitmap filled with a 50% white background color plus four rects
+ // of four different colors/opacities. |cycle| causes the four rects to rotate
+ // positions (counter-clockwise by N steps).
+ static SkBitmap MakeTestBitmap(int cycle) {
+ constexpr gfx::Size kTestImageSize = gfx::Size(24, 16);
+ // Test colors have been chosen to exercise different opacities,
+ // intensities, and color channels; to confirm all aspects of the "SrcOver"
+ // image blending algorithms are working properly.
+ constexpr SkColor kTestImageBackground =
+ SkColorSetARGB(0xff, 0xff, 0xff, 0xff);
+ constexpr SkColor kTestImageColors[4] = {
+ SkColorSetARGB(0xaa, 0xff, 0x00, 0x00),
+ SkColorSetARGB(0xbb, 0x00, 0xee, 0x00),
+ SkColorSetARGB(0xcc, 0x00, 0x00, 0x77),
+ SkColorSetARGB(0xdd, 0x66, 0x66, 0x00),
+ };
+ constexpr SkIRect kTestImageColorRects[4] = {
+ SkIRect::MakeXYWH(4, 2, 4, 4), SkIRect::MakeXYWH(16, 2, 4, 4),
+ SkIRect::MakeXYWH(4, 10, 4, 4), SkIRect::MakeXYWH(16, 10, 4, 4),
+ };
+
+ SkBitmap result;
+ const SkImageInfo info = SkImageInfo::MakeN32Premul(
+ kTestImageSize.width(), kTestImageSize.height(),
+ GetLinearSRGB().ToSkColorSpace());
+ CHECK(result.tryAllocPixels(info, info.minRowBytes()));
+ result.eraseColor(kTestImageBackground);
+ for (size_t i = 0; i < base::size(kTestImageColors); ++i) {
+ const size_t idx = (i + cycle) % base::size(kTestImageColors);
+ result.erase(kTestImageColors[idx], kTestImageColorRects[i]);
+ }
+
+ return result;
+ }
+
+ // Returns the sRGB color space, but with a linear transfer function.
+ static gfx::ColorSpace GetLinearSRGB() {
+ return gfx::ColorSpace(
+ gfx::ColorSpace::PrimaryID::BT709, gfx::ColorSpace::TransferID::LINEAR,
+ gfx::ColorSpace::MatrixID::RGB, gfx::ColorSpace::RangeID::FULL);
+ }
+
+ // Returns the BT709 color space (YUV), but with a linear transfer function.
+ static gfx::ColorSpace GetLinearRec709() {
+ return gfx::ColorSpace(
+ gfx::ColorSpace::PrimaryID::BT709, gfx::ColorSpace::TransferID::LINEAR,
+ gfx::ColorSpace::MatrixID::BT709, gfx::ColorSpace::RangeID::LIMITED);
+ }
+
+ static constexpr auto kARGBFormat = VideoPixelFormat::PIXEL_FORMAT_ARGB;
+ static constexpr auto kI420Format = VideoPixelFormat::PIXEL_FORMAT_I420;
+
+ private:
+ NiceMock<MockFrameSource> frame_source_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoCaptureOverlayTest);
+};
+
+// Tests that MakeRenderer() does not make a OnceRenderer until the client has
+// set the image.
+TEST_F(VideoCaptureOverlayTest, DoesNotRenderWithoutImage) {
+ constexpr gfx::Size kSize = gfx::Size(100, 75);
+ EXPECT_CALL(*frame_source(), GetSourceSize()).WillRepeatedly(Return(kSize));
+ std::unique_ptr<VideoCaptureOverlay> overlay = CreateOverlay();
+
+ // The overlay does not have an image yet, so the renderer should be null.
+ constexpr gfx::Rect kRegionInFrame = gfx::Rect(kSize);
+ EXPECT_FALSE(
+ overlay->MakeRenderer(kRegionInFrame, kI420Format, GetLinearRec709()));
+
+ // Once an image is set, the renderer should not be null.
+ overlay->SetImageAndBounds(MakeTestBitmap(1), gfx::RectF(0, 0, 1, 1));
+ EXPECT_TRUE(
+ overlay->MakeRenderer(kRegionInFrame, kI420Format, GetLinearRec709()));
+}
+
+// Tests that MakeRenderer() does not make a OnceRenderer if the bounds are set
+// to something outside the frame's content region.
+TEST_F(VideoCaptureOverlayTest, DoesNotRenderIfCompletelyOutOfBounds) {
+ constexpr gfx::Size kSize = gfx::Size(100, 75);
+ EXPECT_CALL(*frame_source(), GetSourceSize()).WillRepeatedly(Return(kSize));
+ std::unique_ptr<VideoCaptureOverlay> overlay = CreateOverlay();
+
+ // The overlay does not have an image yet, so the renderer should be null.
+ constexpr gfx::Rect kRegionInFrame = gfx::Rect(kSize);
+ EXPECT_FALSE(
+ overlay->MakeRenderer(kRegionInFrame, kI420Format, GetLinearRec709()));
+
+ // Setting an image, but out-of-bounds, should always result in a null
+ // renderer.
+ overlay->SetImageAndBounds(MakeTestBitmap(0), gfx::RectF(-1, -1, 1, 1));
+ EXPECT_FALSE(
+ overlay->MakeRenderer(kRegionInFrame, kI420Format, GetLinearRec709()));
+ overlay->SetBounds(gfx::RectF(1, 1, 1, 1));
+ EXPECT_FALSE(
+ overlay->MakeRenderer(kRegionInFrame, kI420Format, GetLinearRec709()));
+ overlay->SetBounds(gfx::RectF(-1, 1, 1, 1));
+ EXPECT_FALSE(
+ overlay->MakeRenderer(kRegionInFrame, kI420Format, GetLinearRec709()));
+ overlay->SetBounds(gfx::RectF(1, -1, 1, 1));
+ EXPECT_FALSE(
+ overlay->MakeRenderer(kRegionInFrame, kI420Format, GetLinearRec709()));
+}
+
+// Tests that that MakeCombinedRenderer() only makes a OnceRenderer when one or
+// more overlays are set to make visible changes to a video frame.
+TEST_F(VideoCaptureOverlayTest,
+ DoesNotDoCombinedRenderIfNoOverlaysWouldRender) {
+ constexpr gfx::Size kSize = gfx::Size(100, 75);
+ EXPECT_CALL(*frame_source(), GetSourceSize()).WillRepeatedly(Return(kSize));
+ std::vector<std::unique_ptr<VideoCaptureOverlay>> overlays;
+ overlays.emplace_back(CreateOverlay());
+ overlays.emplace_back(CreateOverlay());
+
+ // Neither overlay has an image yet, so the combined renderer should be null.
+ constexpr gfx::Rect kRegionInFrame = gfx::Rect(kSize);
+ EXPECT_FALSE(VideoCaptureOverlay::MakeCombinedRenderer(
+ overlays, kRegionInFrame, kI420Format, GetLinearRec709()));
+
+ // If just the first overlay renders, the combined renderer should not be
+ // null.
+ overlays[0]->SetImageAndBounds(MakeTestBitmap(0), gfx::RectF(0, 0, 1, 1));
+ EXPECT_TRUE(VideoCaptureOverlay::MakeCombinedRenderer(
+ overlays, kRegionInFrame, kI420Format, GetLinearRec709()));
+
+ // If both overlays render, the combined renderer should not be null.
+ overlays[1]->SetImageAndBounds(MakeTestBitmap(1), gfx::RectF(0, 0, 1, 1));
+ EXPECT_TRUE(VideoCaptureOverlay::MakeCombinedRenderer(
+ overlays, kRegionInFrame, kI420Format, GetLinearRec709()));
+
+ // If only the second overlay renders, because the first is hidden, the
+ // combined renderer should not be null.
+ overlays[0]->SetBounds(gfx::RectF());
+ EXPECT_TRUE(VideoCaptureOverlay::MakeCombinedRenderer(
+ overlays, kRegionInFrame, kI420Format, GetLinearRec709()));
+
+ // Both overlays are hidden, so the combined renderer should be null.
+ overlays[1]->SetBounds(gfx::RectF());
+ EXPECT_FALSE(VideoCaptureOverlay::MakeCombinedRenderer(
+ overlays, kRegionInFrame, kI420Format, GetLinearRec709()));
+}
+
+class VideoCaptureOverlayRenderTest
+ : public VideoCaptureOverlayTest,
+ public testing::WithParamInterface<VideoPixelFormat> {
+ public:
+ VideoCaptureOverlayRenderTest()
+ : trace_(__FILE__, __LINE__, VideoPixelFormatToString(pixel_format())) {}
+
+ VideoPixelFormat pixel_format() const { return GetParam(); }
+
+ bool is_argb_test() const {
+ return pixel_format() == media::PIXEL_FORMAT_ARGB;
+ }
+
+ gfx::ColorSpace GetColorSpace() const {
+ // For these tests, we use linear RGB and YUV color spaces. This is because
+ // VideoCaptureOverlay does not account for non-linear color spaces when
+ // blending. See class notes.
+ return is_argb_test() ? GetLinearSRGB() : GetLinearRec709();
+ }
+
+ scoped_refptr<VideoFrame> CreateVideoFrame(const gfx::Size& size) const {
+ auto frame = VideoFrame::CreateFrame(pixel_format(), size, gfx::Rect(size),
+ size, base::TimeDelta());
+
+ // Fill the video frame with black. For ARGB tests, also set alpha channel
+ // to 1.0. This allows the expected results of the ARGB tests to be the same
+ // as those of the YUV tests, and so only one set of golden files needs to
+ // be used.
+ if (is_argb_test()) {
+ uint8_t* dst = frame->visible_data(VideoFrame::kARGBPlane);
+ const int stride = frame->stride(VideoFrame::kARGBPlane);
+ for (int row = 0; row < size.height(); ++row, dst += stride) {
+ uint32_t* const begin = reinterpret_cast<uint32_t*>(dst);
+ std::fill(begin, begin + size.width(), UINT32_C(0xff000000));
+ }
+ } else /* if (!is_argb_test()) */ {
+ media::FillYUV(frame.get(), 0x00, 0x80, 0x80);
+ }
+
+ frame->set_color_space(GetColorSpace());
+ return frame;
+ }
+
+ bool FrameMatchesPNG(const VideoFrame& frame, const char* golden_file) {
+ const gfx::ColorSpace png_color_space = GetLinearSRGB();
+ // Note: Using kUnpremul_SkAlphaType since that is the semantics of
+ // PIXEL_FORMAT_ARGB, and converting to kPremul_SkAlphaType before producing
+ // the PNG would lose precision for no good reason.
+ const SkImageInfo canonical_format = SkImageInfo::Make(
+ frame.visible_rect().width(), frame.visible_rect().height(),
+ kN32_SkColorType, kUnpremul_SkAlphaType,
+ png_color_space.ToSkColorSpace());
+ SkBitmap canonical_bitmap;
+ CHECK(canonical_bitmap.tryAllocPixels(canonical_format, 0));
+
+ // Populate |canonical_bitmap| with data from the frame. For I420, use
+ // gfx::ColorTransform to map back from YUV→RGB.
+ switch (frame.format()) {
+ case media::PIXEL_FORMAT_ARGB: {
+ // Map from the video frame's ARGB format to the canonical
+ // representation.
+ const SkImageInfo frame_format = SkImageInfo::Make(
+ frame.visible_rect().width(), frame.visible_rect().height(),
+ kBGRA_8888_SkColorType, kUnpremul_SkAlphaType,
+ frame.ColorSpace().ToSkColorSpace());
+ canonical_bitmap.writePixels(
+ SkPixmap(frame_format, frame.visible_data(VideoFrame::kARGBPlane),
+ frame.stride(VideoFrame::kARGBPlane)),
+ 0, 0);
+ break;
+ }
+
+ case media::PIXEL_FORMAT_I420: {
+ // Map from I420 planar [0,255] (of which only [16,235] is used) values
+ // to interleaved [0.0,1.0] values.
+ const gfx::Size& size = frame.visible_rect().size();
+ std::unique_ptr<gfx::ColorTransform::TriStim[]> colors(
+ new gfx::ColorTransform::TriStim[size.GetArea()]);
+ int pos = 0;
+ for (int row = 0; row < size.height(); ++row) {
+ const uint8_t* y = frame.visible_data(VideoFrame::kYPlane) +
+ (row * frame.stride(VideoFrame::kYPlane));
+ const uint8_t* u = frame.visible_data(VideoFrame::kUPlane) +
+ ((row / 2) * frame.stride(VideoFrame::kUPlane));
+ const uint8_t* v = frame.visible_data(VideoFrame::kVPlane) +
+ ((row / 2) * frame.stride(VideoFrame::kVPlane));
+ for (int col = 0; col < size.width(); ++col) {
+ colors[pos].SetPoint(y[col] / 255.0f, u[col / 2] / 255.0f,
+ v[col / 2] / 255.0f);
+ ++pos;
+ }
+ }
+
+ // Execute the YUV→RGB conversion.
+ gfx::ColorTransform::NewColorTransform(
+ frame.ColorSpace(), png_color_space,
+ gfx::ColorTransform::Intent::INTENT_ABSOLUTE)
+ ->Transform(colors.get(), size.GetArea());
+
+ // Map back from interleaved [0.0,1.0] values to intervealed ARGB,
+ // setting alpha=100%.
+ const auto ToClamped255 = [](float value) -> uint32_t {
+ value = (value * 255.0f) + 0.5f /* rounding */;
+ return base::saturated_cast<uint8_t>(value);
+ };
+ pos = 0;
+ for (int row = 0; row < size.height(); ++row) {
+ uint32_t* out = canonical_bitmap.getAddr32(0, row);
+ for (int col = 0; col < size.width(); ++col) {
+ out[col] = ((UINT32_C(255) << SK_A32_SHIFT) |
+ (ToClamped255(colors[pos].x()) << SK_R32_SHIFT) |
+ (ToClamped255(colors[pos].y()) << SK_G32_SHIFT) |
+ (ToClamped255(colors[pos].z()) << SK_B32_SHIFT));
+ ++pos;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ // Determine the full path to the golden file to compare the results.
+ base::FilePath golden_file_path;
+ base::PathService::Get(Paths::DIR_TEST_DATA, &golden_file_path);
+ golden_file_path =
+ golden_file_path.Append(FILE_PATH_LITERAL("video_capture"))
+ .Append(base::FilePath::FromUTF8Unsafe(golden_file));
+
+ // If the very-specific command-line switch is present, rewrite the golden
+ // file. This is only done when the ARGB test runs, for the reasons outlined
+ // in the comments below (regarding FuzzyPixelComparator).
+ if (is_argb_test() &&
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ "video-overlay-capture-test-update-golden-files")) {
+ LOG(INFO) << "Rewriting golden file: " << golden_file_path.AsUTF8Unsafe();
+ cc::WritePNGFile(canonical_bitmap, golden_file_path, false);
+ }
+
+ // FuzzyPixelComparator configuration: Allow 100% of pixels to mismatch, but
+ // no single pixel component should be different by more than 1/255 (64/255
+ // for YUV tests), and the absolute average error must not exceed 1/255
+ // (16/255 for YUV tests). The YUV tests allow for more error due to the
+ // expected errors introduced by both color space (dynamic range) and format
+ // (chroma subsampling) conversion.
+ cc::FuzzyPixelComparator comparator(false, 100.0f, 0.0f,
+ is_argb_test() ? 1.0f : 16.0f,
+ is_argb_test() ? 1 : 64, 0);
+ const bool matches_golden_file =
+ cc::MatchesPNGFile(canonical_bitmap, golden_file_path, comparator);
+ // If MatchesPNGFile() returned false, it will have LOG(ERROR)'ed the
+ // expected versus actual PNG data URLs. So, only do the VLOG(1)'s when
+ // MatchesPNGFile() returned true.
+ if (matches_golden_file && VLOG_IS_ON(1)) {
+ SkBitmap expected;
+ if (cc::ReadPNGFile(golden_file_path, &expected)) {
+ VLOG(1) << "Expected bitmap: " << cc::GetPNGDataUrl(expected);
+ }
+ VLOG(1) << "Actual bitmap: " << cc::GetPNGDataUrl(canonical_bitmap);
+ }
+ return matches_golden_file;
+ }
+
+ // The size of the compositor frame sink's Surface.
+ static constexpr gfx::Size kSourceSize = gfx::Size(96, 40);
+
+ private:
+ testing::ScopedTrace trace_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoCaptureOverlayRenderTest);
+};
+
+// static
+constexpr gfx::Size VideoCaptureOverlayRenderTest::kSourceSize;
+
+// Basic test: Render an overlay image that covers the entire video frame and is
+// not scaled.
+TEST_P(VideoCaptureOverlayRenderTest, FullCover_NoScaling) {
+ StrictMock<MockFrameSource> frame_source;
+ VideoCaptureOverlay overlay(&frame_source);
+
+ EXPECT_CALL(frame_source, GetSourceSize())
+ .WillRepeatedly(Return(kSourceSize));
+ EXPECT_CALL(frame_source, InvalidateRect(gfx::Rect())).RetiresOnSaturation();
+ EXPECT_CALL(frame_source, InvalidateRect(gfx::Rect(kSourceSize)))
+ .RetiresOnSaturation();
+ EXPECT_CALL(frame_source, RequestRefreshFrame());
+
+ const SkBitmap test_bitmap = MakeTestBitmap(0);
+ overlay.SetImageAndBounds(test_bitmap, gfx::RectF(0, 0, 1, 1));
+ const gfx::Size output_size(test_bitmap.width(), test_bitmap.height());
+ VideoCaptureOverlay::OnceRenderer renderer = overlay.MakeRenderer(
+ gfx::Rect(output_size), pixel_format(), GetColorSpace());
+ ASSERT_TRUE(renderer);
+ auto frame = CreateVideoFrame(output_size);
+ std::move(renderer).Run(frame.get());
+ EXPECT_TRUE(FrameMatchesPNG(*frame, "overlay_full_cover.png"));
+}
+
+// Basic test: Render an overlay image that covers the entire video frame and is
+// scaled.
+TEST_P(VideoCaptureOverlayRenderTest, FullCover_WithScaling) {
+ StrictMock<MockFrameSource> frame_source;
+ VideoCaptureOverlay overlay(&frame_source);
+
+ EXPECT_CALL(frame_source, GetSourceSize())
+ .WillRepeatedly(Return(kSourceSize));
+ EXPECT_CALL(frame_source, InvalidateRect(gfx::Rect())).RetiresOnSaturation();
+ EXPECT_CALL(frame_source, InvalidateRect(gfx::Rect(kSourceSize)))
+ .RetiresOnSaturation();
+ EXPECT_CALL(frame_source, RequestRefreshFrame());
+
+ const SkBitmap test_bitmap = MakeTestBitmap(0);
+ overlay.SetImageAndBounds(test_bitmap, gfx::RectF(0, 0, 1, 1));
+ const gfx::Size output_size(test_bitmap.width() * 4,
+ test_bitmap.height() * 4);
+ VideoCaptureOverlay::OnceRenderer renderer = overlay.MakeRenderer(
+ gfx::Rect(output_size), pixel_format(), GetColorSpace());
+ ASSERT_TRUE(renderer);
+ auto frame = CreateVideoFrame(output_size);
+ std::move(renderer).Run(frame.get());
+ EXPECT_TRUE(FrameMatchesPNG(*frame, "overlay_full_cover_scaled.png"));
+}
+
+// Tests that changing the position of the overlay results in it being rendered
+// at different locations in the video frame.
+TEST_P(VideoCaptureOverlayRenderTest, MovesAround) {
+ NiceMock<MockFrameSource> frame_source;
+ EXPECT_CALL(frame_source, GetSourceSize())
+ .WillRepeatedly(Return(kSourceSize));
+ VideoCaptureOverlay overlay(&frame_source);
+
+ const SkBitmap test_bitmap = MakeTestBitmap(0);
+ const gfx::Size frame_size(test_bitmap.width() * 4, test_bitmap.height() * 4);
+
+ const gfx::RectF relative_image_bounds[6] = {
+ gfx::RectF(0.0f, 0.0f, 0.5f, 0.5f),
+ gfx::RectF(1.0f / frame_size.width(), 0.0f, 0.5f, 0.5f),
+ gfx::RectF(2.0f / frame_size.width(), 0.0f, 0.5f, 0.5f),
+ gfx::RectF(2.0f / frame_size.width(), 1.0f / frame_size.height(), 0.5f,
+ 0.5f),
+ gfx::RectF(2.0f / frame_size.width(), 2.0f / frame_size.height(), 0.5f,
+ 0.5f),
+ gfx::RectF(0.5f, 0.5f, 0.5f, 0.5f),
+ };
+
+ VideoCaptureOverlay::OnceRenderer renderers[6];
+ for (int i = 0; i < 6; ++i) {
+ if (i == 0) {
+ overlay.SetImageAndBounds(test_bitmap, relative_image_bounds[i]);
+ } else {
+ overlay.SetBounds(relative_image_bounds[i]);
+ }
+ renderers[i] = overlay.MakeRenderer(gfx::Rect(frame_size), pixel_format(),
+ GetColorSpace());
+ }
+
+ constexpr std::array<const char*, 6> kGoldenFiles = {
+ "overlay_moves_0_0.png", "overlay_moves_1_0.png", "overlay_moves_2_0.png",
+ "overlay_moves_2_1.png", "overlay_moves_2_2.png", "overlay_moves_lr.png",
+ };
+
+ for (int i = 0; i < 6; ++i) {
+ SCOPED_TRACE(testing::Message() << "relative_image_bounds="
+ << relative_image_bounds[i].ToString()
+ << ", frame_size=" << frame_size.ToString()
+ << ", golden_file=" << kGoldenFiles[i]);
+ auto frame = CreateVideoFrame(frame_size);
+ std::move(renderers[i]).Run(frame.get());
+ EXPECT_TRUE(FrameMatchesPNG(*frame, kGoldenFiles[i]));
+ }
+}
+
+// Tests that the overlay will be partially rendered (clipped) when any part of
+// it extends outside the video frame's content region.
+//
+// For this test, the content region is a rectangle, centered within the frame
+// (e.g., the content is being letterboxed), and the test attempts to locate the
+// overlay such that part of it should be clipped. The test succeeds if the
+// overlay is clipped to the content region in the center. For example:
+//
+// +-------------------------------+
+// | |
+// | ...... |
+// | ..****//////////// | **** the drawn part of the overlay
+// | ..****CONTENT///// |
+// | /////REGION///// | .... the clipped part of the overlay
+// | //////////////// | (i.e., not drawn)
+// | |
+// | |
+// +-------------------------------+
+TEST_P(VideoCaptureOverlayRenderTest, ClipsToContentBounds) {
+ NiceMock<MockFrameSource> frame_source;
+ EXPECT_CALL(frame_source, GetSourceSize())
+ .WillRepeatedly(Return(kSourceSize));
+ VideoCaptureOverlay overlay(&frame_source);
+
+ const SkBitmap test_bitmap = MakeTestBitmap(0);
+ const gfx::Size frame_size(test_bitmap.width() * 4, test_bitmap.height() * 4);
+ const gfx::Rect region_in_frame(test_bitmap.width(), test_bitmap.height(),
+ test_bitmap.width() * 2,
+ test_bitmap.height() * 2);
+
+ const gfx::RectF relative_image_bounds[4] = {
+ gfx::RectF(-0.25f, -0.25f, 0.5f, 0.5f),
+ gfx::RectF(0.75f, -0.25f, 0.5f, 0.5f),
+ gfx::RectF(0.75f, 0.75f, 0.5f, 0.5f),
+ gfx::RectF(-0.25f, 0.75f, 0.5f, 0.5f),
+ };
+
+ VideoCaptureOverlay::OnceRenderer renderers[4];
+ for (int i = 0; i < 4; ++i) {
+ if (i == 0) {
+ overlay.SetImageAndBounds(test_bitmap, relative_image_bounds[i]);
+ } else {
+ overlay.SetBounds(relative_image_bounds[i]);
+ }
+ renderers[i] =
+ overlay.MakeRenderer(region_in_frame, pixel_format(), GetColorSpace());
+ }
+
+ constexpr std::array<const char*, 4> kGoldenFiles = {
+ "overlay_clips_ul.png", "overlay_clips_ur.png", "overlay_clips_lr.png",
+ "overlay_clips_ll.png",
+ };
+
+ for (int i = 0; i < 4; ++i) {
+ auto frame = CreateVideoFrame(frame_size);
+ std::move(renderers[i]).Run(frame.get());
+ EXPECT_TRUE(FrameMatchesPNG(*frame, kGoldenFiles[i]));
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(
+ ,
+ VideoCaptureOverlayRenderTest,
+ testing::Values(VideoCaptureOverlayRenderTest::kARGBFormat,
+ VideoCaptureOverlayRenderTest::kI420Format));
+
+} // namespace
+} // namespace viz
diff --git a/chromium/components/viz/service/frame_sinks/video_detector.cc b/chromium/components/viz/service/frame_sinks/video_detector.cc
index 42c81d0d7b1..8f560c3af1b 100644
--- a/chromium/components/viz/service/frame_sinks/video_detector.cc
+++ b/chromium/components/viz/service/frame_sinks/video_detector.cc
@@ -88,6 +88,7 @@ class VideoDetector::ClientInfo {
};
VideoDetector::VideoDetector(
+ const std::vector<FrameSinkId>& registered_frame_sink_ids,
SurfaceManager* surface_manager,
const base::TickClock* tick_clock,
scoped_refptr<base::SequencedTaskRunner> task_runner)
@@ -95,8 +96,8 @@ VideoDetector::VideoDetector(
video_inactive_timer_(tick_clock),
surface_manager_(surface_manager) {
surface_manager_->AddObserver(this);
- for (auto it : surface_manager_->valid_frame_sink_labels())
- client_infos_[it.first] = std::make_unique<ClientInfo>();
+ for (auto& frame_sink_id : registered_frame_sink_ids)
+ client_infos_[frame_sink_id] = std::make_unique<ClientInfo>();
if (task_runner)
video_inactive_timer_.SetTaskRunner(task_runner);
}
diff --git a/chromium/components/viz/service/frame_sinks/video_detector.h b/chromium/components/viz/service/frame_sinks/video_detector.h
index c0119ebb240..5657fdd436f 100644
--- a/chromium/components/viz/service/frame_sinks/video_detector.h
+++ b/chromium/components/viz/service/frame_sinks/video_detector.h
@@ -30,6 +30,7 @@ class VideoDetectorTest;
class VIZ_SERVICE_EXPORT VideoDetector : public SurfaceObserver {
public:
VideoDetector(
+ const std::vector<FrameSinkId>& registered_frame_sink_ids,
SurfaceManager* surface_manager,
const base::TickClock* tick_clock = base::DefaultTickClock::GetInstance(),
scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr);
diff --git a/chromium/components/viz/service/frame_sinks/video_detector_unittest.cc b/chromium/components/viz/service/frame_sinks/video_detector_unittest.cc
index c56d8d49f82..d7f76b72c88 100644
--- a/chromium/components/viz/service/frame_sinks/video_detector_unittest.cc
+++ b/chromium/components/viz/service/frame_sinks/video_detector_unittest.cc
@@ -13,6 +13,7 @@
#include "components/viz/common/quads/surface_draw_quad.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/service/display/surface_aggregator.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.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/frame_sinks/video_detector.h"
@@ -74,7 +75,8 @@ class TestObserver : public mojom::VideoDetectorObserver {
class VideoDetectorTest : public testing::Test {
public:
VideoDetectorTest()
- : surface_aggregator_(frame_sink_manager_.surface_manager(),
+ : frame_sink_manager_(&shared_bitmap_manager_),
+ surface_aggregator_(frame_sink_manager_.surface_manager(),
nullptr,
false) {}
@@ -192,6 +194,7 @@ class VideoDetectorTest : public testing::Test {
.Build();
}
+ ServerSharedBitmapManager shared_bitmap_manager_;
FrameSinkManagerImpl frame_sink_manager_;
FakeCompositorFrameSinkClient frame_sink_client_;
ParentLocalSurfaceIdAllocator parent_local_surface_id_allocator_;
diff --git a/chromium/components/viz/service/gl/DEPS b/chromium/components/viz/service/gl/DEPS
index 55cd3a78ac7..1e459f82431 100644
--- a/chromium/components/viz/service/gl/DEPS
+++ b/chromium/components/viz/service/gl/DEPS
@@ -8,6 +8,7 @@ include_rules = [
"+gpu/ipc",
"+gpu/ipc/common",
"+gpu/ipc/service",
+ "+gpu/vulkan",
"+ipc",
"+media/gpu",
"+media/mojo",
diff --git a/chromium/components/viz/service/gl/gpu_service_impl.cc b/chromium/components/viz/service/gl/gpu_service_impl.cc
index 9beb6b89684..6f84ab4850e 100644
--- a/chromium/components/viz/service/gl/gpu_service_impl.cc
+++ b/chromium/components/viz/service/gl/gpu_service_impl.cc
@@ -5,6 +5,7 @@
#include "components/viz/service/gl/gpu_service_impl.h"
#include <memory>
+#include <string>
#include <utility>
#include "base/bind.h"
@@ -17,6 +18,8 @@
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "components/crash/core/common/crash_key.h"
+#include "components/viz/common/gpu/vulkan_context_provider.h"
+#include "components/viz/common/gpu/vulkan_in_process_context_provider.h"
#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/command_buffer/service/scheduler.h"
@@ -27,10 +30,12 @@
#include "gpu/config/gpu_util.h"
#include "gpu/ipc/common/gpu_memory_buffer_support.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"
#include "gpu/ipc/service/gpu_watchdog_thread.h"
+#include "gpu/vulkan/buildflags.h"
#include "ipc/ipc_channel_handle.h"
#include "ipc/ipc_sync_channel.h"
#include "ipc/ipc_sync_message_filter.h"
@@ -55,7 +60,6 @@
#if defined(OS_ANDROID)
#include "base/android/throw_uncaught_exception.h"
-#include "media/gpu/android/content_video_view_overlay_allocator.h"
#endif
#if defined(OS_CHROMEOS)
@@ -70,6 +74,10 @@
#include "gpu/ipc/service/direct_composition_surface_win.h"
#endif
+#if defined(OS_MACOSX)
+#include "ui/base/cocoa/quartz_util.h"
+#endif
+
namespace viz {
namespace {
@@ -89,14 +97,16 @@ bool GpuLogMessageHandler(int severity,
// Returns a callback which does a PostTask to run |callback| on the |runner|
// task runner.
-template <typename Param>
-base::OnceCallback<void(const Param&)> WrapCallback(
+template <typename... Params>
+base::OnceCallback<void(Params&&...)> WrapCallback(
scoped_refptr<base::SingleThreadTaskRunner> runner,
- base::OnceCallback<void(const Param&)> callback) {
+ base::OnceCallback<void(Params...)> callback) {
return base::BindOnce(
[](base::SingleThreadTaskRunner* runner,
- base::OnceCallback<void(const Param&)> callback, const Param& param) {
- runner->PostTask(FROM_HERE, base::BindOnce(std::move(callback), param));
+ base::OnceCallback<void(Params && ...)> callback, Params&&... params) {
+ runner->PostTask(FROM_HERE,
+ base::BindOnce(std::move(callback),
+ std::forward<Params>(params)...));
},
base::RetainedRef(std::move(runner)), std::move(callback));
}
@@ -109,6 +119,16 @@ void DestroyBinding(mojo::BindingSet<mojom::GpuService>* binding,
} // namespace
+struct GpuServiceImpl::GrContextAndGLContext {
+ GrContextAndGLContext() = default;
+ GrContextAndGLContext(GrContextAndGLContext&& other) = default;
+ ~GrContextAndGLContext() = default;
+ GrContextAndGLContext& operator=(GrContextAndGLContext&& other) = default;
+
+ scoped_refptr<gl::GLContext> gl_context;
+ sk_sp<GrContext> gr_context;
+};
+
GpuServiceImpl::GpuServiceImpl(
const gpu::GPUInfo& gpu_info,
std::unique_ptr<gpu::GpuWatchdogThread> watchdog_thread,
@@ -117,7 +137,9 @@ GpuServiceImpl::GpuServiceImpl(
const gpu::GpuPreferences& gpu_preferences,
const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
const base::Optional<gpu::GpuFeatureInfo>&
- gpu_feature_info_for_hardware_gpu)
+ gpu_feature_info_for_hardware_gpu,
+ gpu::VulkanImplementation* vulkan_implementation,
+ base::OnceClosure exit_callback)
: main_runner_(base::ThreadTaskRunnerHandle::Get()),
io_runner_(std::move(io_runner)),
watchdog_thread_(std::move(watchdog_thread)),
@@ -128,12 +150,26 @@ GpuServiceImpl::GpuServiceImpl(
gpu_feature_info_(gpu_feature_info),
gpu_info_for_hardware_gpu_(gpu_info_for_hardware_gpu),
gpu_feature_info_for_hardware_gpu_(gpu_feature_info_for_hardware_gpu),
+#if BUILDFLAG(ENABLE_VULKAN)
+ vulkan_implementation_(vulkan_implementation),
+#endif
+ exit_callback_(std::move(exit_callback)),
bindings_(std::make_unique<mojo::BindingSet<mojom::GpuService>>()),
weak_ptr_factory_(this) {
DCHECK(!io_runner_->BelongsToCurrentThread());
#if defined(OS_CHROMEOS)
protected_buffer_manager_ = new arc::ProtectedBufferManager();
#endif // defined(OS_CHROMEOS)
+
+#if BUILDFLAG(ENABLE_VULKAN)
+ if (vulkan_implementation_) {
+ vulkan_context_provider_ =
+ VulkanInProcessContextProvider::Create(vulkan_implementation_);
+ if (!vulkan_context_provider_)
+ DLOG(WARNING) << "Failed to create Vulkan context provider.";
+ }
+#endif
+
weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
}
@@ -143,8 +179,7 @@ GpuServiceImpl::~GpuServiceImpl() {
logging::SetLogMessageHandler(nullptr);
g_log_callback.Get() =
base::Callback<void(int, size_t, const std::string&)>();
- base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
- base::WaitableEvent::InitialState::NOT_SIGNALED);
+ base::WaitableEvent wait;
if (io_runner_->PostTask(
FROM_HERE, base::Bind(&DestroyBinding, bindings_.get(), &wait))) {
wait.Wait();
@@ -156,8 +191,20 @@ GpuServiceImpl::~GpuServiceImpl() {
scheduler_->DestroySequence(skia_output_surface_sequence_id_);
}
- gr_context_ = nullptr;
- context_for_skia_ = nullptr;
+ for (auto& key_and_data : contexts_for_gl_) {
+ auto& data = key_and_data.second;
+ if (!data.gr_context)
+ continue;
+ if (!data.gl_context ||
+ !data.gl_context->MakeCurrent(
+ gpu_channel_manager_->GetDefaultOffscreenSurface())) {
+ LOG(ERROR) << "Failed to make current.";
+ data.gr_context->abandonContext();
+ }
+ data.gr_context = nullptr;
+ }
+ contexts_for_gl_.clear();
+
media_gpu_channel_manager_.reset();
gpu_channel_manager_.reset();
owned_sync_point_manager_.reset();
@@ -254,35 +301,62 @@ void GpuServiceImpl::Bind(mojom::GpuServiceRequest request) {
bindings_->AddBinding(this, std::move(request));
}
-bool GpuServiceImpl::CreateGrContextIfNecessary(gl::GLSurface* surface) {
+void GpuServiceImpl::DisableGpuCompositing() {
+ // Can be called from any thread.
+ (*gpu_host_)->DisableGpuCompositing();
+}
+
+bool GpuServiceImpl::GetGrContextForGLSurface(gl::GLSurface* surface,
+ GrContext** gr_context,
+ gl::GLContext** gl_context) {
DCHECK(main_runner_->BelongsToCurrentThread());
+ DCHECK(!is_using_vulkan());
DCHECK(surface);
+ DCHECK(gr_context && !*gr_context);
+ DCHECK(gl_context && !*gl_context);
+
+ auto& data = contexts_for_gl_[surface->GetCompatibilityKey()];
+ if (!data.gr_context) {
+ DCHECK(!data.gl_context);
- if (!gr_context_) {
- DCHECK(!context_for_skia_);
gl::GLContextAttribs attribs;
// TODO(penghuang) set attribs.
- context_for_skia_ = gl::init::CreateGLContext(
+ data.gl_context = gl::init::CreateGLContext(
gpu_channel_manager_->share_group(), surface, attribs);
- DCHECK(context_for_skia_);
- gpu_feature_info_.ApplyToGLContext(context_for_skia_.get());
- if (!context_for_skia_->MakeCurrent(surface)) {
+ DCHECK(data.gl_context);
+ gpu_feature_info_.ApplyToGLContext(data.gl_context.get());
+ if (!data.gl_context->MakeCurrent(surface)) {
LOG(FATAL) << "Failed to make current.";
// TODO(penghuang): handle the failure.
}
+
+ const auto* gl_version_info = data.gl_context->GetVersionInfo();
+ const bool use_version_es2 = false;
auto native_interface =
- GrGLMakeAssembledInterface(nullptr, [](void* ctx, const char name[]) {
- return gl::GetGLProcAddress(name);
- });
+ gl::init::CreateGrGLInterface(*gl_version_info, use_version_es2);
DCHECK(native_interface);
GrContextOptions options;
options.fExplicitlyAllocateGPUResources = GrContextOptions::Enable::kYes;
options.fUseGLBufferDataNullHint = GrContextOptions::Enable::kYes;
- gr_context_ = GrContext::MakeGL(std::move(native_interface), options);
- DCHECK(gr_context_);
+ data.gr_context = GrContext::MakeGL(std::move(native_interface), options);
+ DCHECK(data.gr_context);
}
- return !!gr_context_;
+
+ *gr_context = data.gr_context.get();
+ *gl_context = data.gl_context.get();
+ return !!gr_context;
+}
+
+GrContext* GpuServiceImpl::GetGrContextForVulkan() {
+ DCHECK(main_runner_->BelongsToCurrentThread());
+ DCHECK(is_using_vulkan());
+#if BUILDFLAG(ENABLE_VULKAN)
+ return vulkan_context_provider_->GetGrContext();
+#else
+ NOTREACHED();
+ return nullptr;
+#endif
}
gpu::ImageFactory* GpuServiceImpl::gpu_image_factory() {
@@ -436,8 +510,7 @@ void GpuServiceImpl::GetVideoMemoryUsageStats(
return;
}
gpu::VideoMemoryUsageStats video_memory_usage_stats;
- gpu_channel_manager_->gpu_memory_manager()->GetVideoMemoryUsageStats(
- &video_memory_usage_stats);
+ gpu_channel_manager_->GetVideoMemoryUsageStats(&video_memory_usage_stats);
std::move(callback).Run(video_memory_usage_stats);
}
@@ -527,7 +600,7 @@ void GpuServiceImpl::UpdateGpuInfoPlatform(
if (in_host_process())
return;
- bool success = gpu::CollectContextGraphicsInfo(&gpu_info_);
+ bool success = gpu::CollectContextGraphicsInfo(&gpu_info_, gpu_preferences_);
if (!success) {
LOG(ERROR) << "gpu::CollectGraphicsInfo failed.";
// TODO(piman): can we signal overall failure?
@@ -606,11 +679,18 @@ void GpuServiceImpl::StoreShaderToDisk(int client_id,
(*gpu_host_)->StoreShaderToDisk(client_id, key, shader);
}
+void GpuServiceImpl::ExitProcess() {
+ if (is_exiting_)
+ return;
+
+ is_exiting_ = true;
+ std::move(exit_callback_).Run();
+}
+
#if defined(OS_WIN)
-void GpuServiceImpl::SendAcceleratedSurfaceCreatedChildWindow(
- gpu::SurfaceHandle parent_window,
- gpu::SurfaceHandle child_window) {
- DCHECK(main_runner_->BelongsToCurrentThread());
+void GpuServiceImpl::SendCreatedChildWindow(gpu::SurfaceHandle parent_window,
+ gpu::SurfaceHandle child_window) {
+ // This can be called from main or display compositor thread.
(*gpu_host_)->SetChildSurface(parent_window, child_window);
}
#endif
@@ -625,6 +705,10 @@ void GpuServiceImpl::EstablishGpuChannel(int32_t client_id,
uint64_t client_tracing_id,
bool is_gpu_host,
EstablishGpuChannelCallback callback) {
+ if (oopd_enabled_ && client_id == gpu::InProcessCommandBuffer::kGpuClientId) {
+ std::move(callback).Run(mojo::ScopedMessagePipeHandle());
+ return;
+ }
if (io_runner_->BelongsToCurrentThread()) {
EstablishGpuChannelCallback wrap_callback = base::BindOnce(
[](scoped_refptr<base::SingleThreadTaskRunner> runner,
@@ -672,25 +756,6 @@ void GpuServiceImpl::LoadedShader(const std::string& key,
gpu_channel_manager_->PopulateShaderCache(key, data);
}
-void GpuServiceImpl::DestroyingVideoSurface(
- int32_t surface_id,
- DestroyingVideoSurfaceCallback callback) {
- DCHECK(io_runner_->BelongsToCurrentThread());
-#if defined(OS_ANDROID)
- main_runner_->PostTaskAndReply(
- FROM_HERE,
- base::BindOnce(
- [](int32_t surface_id) {
- media::ContentVideoViewOverlayAllocator::GetInstance()
- ->OnSurfaceDestroyed(surface_id);
- },
- surface_id),
- std::move(callback));
-#else
- NOTREACHED() << "DestroyingVideoSurface() not supported on this platform.";
-#endif
-}
-
void GpuServiceImpl::WakeUpGpu() {
if (io_runner_->BelongsToCurrentThread()) {
main_runner_->PostTask(FROM_HERE,
@@ -730,7 +795,7 @@ void GpuServiceImpl::OnBackgroundCleanup() {
return;
}
DVLOG(1) << "GPU: Performing background cleanup";
- gpu_channel_manager_->OnApplicationBackgrounded();
+ gpu_channel_manager_->OnBackgroundCleanup();
#else
NOTREACHED();
#endif
@@ -739,6 +804,13 @@ void GpuServiceImpl::OnBackgroundCleanup() {
void GpuServiceImpl::OnBackgrounded() {
if (watchdog_thread_)
watchdog_thread_->OnBackgrounded();
+
+ if (io_runner_->BelongsToCurrentThread()) {
+ main_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&GpuServiceImpl::OnBackgrounded, weak_ptr_));
+ return;
+ }
+ gpu_channel_manager_->OnApplicationBackgrounded();
}
void GpuServiceImpl::OnForegrounded() {
@@ -746,6 +818,20 @@ void GpuServiceImpl::OnForegrounded() {
watchdog_thread_->OnForegrounded();
}
+#if defined(OS_MACOSX)
+void GpuServiceImpl::BeginCATransaction() {
+ DCHECK(io_runner_->BelongsToCurrentThread());
+ main_runner_->PostTask(FROM_HERE, base::BindOnce(&ui::BeginCATransaction));
+}
+
+void GpuServiceImpl::CommitCATransaction(CommitCATransactionCallback callback) {
+ DCHECK(io_runner_->BelongsToCurrentThread());
+ main_runner_->PostTaskAndReply(FROM_HERE,
+ base::BindOnce(&ui::CommitCATransaction),
+ WrapCallback(io_runner_, std::move(callback)));
+}
+#endif
+
void GpuServiceImpl::Crash() {
DCHECK(io_runner_->BelongsToCurrentThread());
DVLOG(1) << "GPU: Simulating GPU crash";
@@ -780,8 +866,7 @@ void GpuServiceImpl::ThrowJavaException() {
void GpuServiceImpl::Stop(StopCallback callback) {
DCHECK(io_runner_->BelongsToCurrentThread());
main_runner_->PostTaskAndReply(
- FROM_HERE,
- base::BindOnce([] { base::RunLoop::QuitCurrentWhenIdleDeprecated(); }),
+ FROM_HERE, base::BindOnce(&GpuServiceImpl::ExitProcess, weak_ptr_),
std::move(callback));
}
diff --git a/chromium/components/viz/service/gl/gpu_service_impl.h b/chromium/components/viz/service/gl/gpu_service_impl.h
index 15c1bb76964..31e1b5134c2 100644
--- a/chromium/components/viz/service/gl/gpu_service_impl.h
+++ b/chromium/components/viz/service/gl/gpu_service_impl.h
@@ -16,15 +16,16 @@
#include "components/viz/service/viz_service_export.h"
#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
#include "gpu/command_buffer/common/activity_flags.h"
-#include "gpu/command_buffer/service/gpu_preferences.h"
#include "gpu/command_buffer/service/sequence_id.h"
#include "gpu/config/gpu_info.h"
+#include "gpu/config/gpu_preferences.h"
#include "gpu/ipc/common/surface_handle.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/gpu_channel_manager.h"
#include "gpu/ipc/service/gpu_channel_manager_delegate.h"
#include "gpu/ipc/service/gpu_config.h"
#include "gpu/ipc/service/x_util.h"
+#include "gpu/vulkan/buildflags.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "services/viz/privileged/interfaces/gl/gpu_host.mojom.h"
#include "services/viz/privileged/interfaces/gl/gpu_service.mojom.h"
@@ -44,6 +45,7 @@ class GpuMemoryBufferFactory;
class GpuWatchdogThread;
class Scheduler;
class SyncPointManager;
+class VulkanImplementation;
} // namespace gpu
namespace media {
@@ -52,6 +54,8 @@ class MediaGpuChannelManager;
namespace viz {
+class VulkanContextProvider;
+
// This runs in the GPU process, and communicates with the gpu host (which is
// the window server) over the mojom APIs. This is responsible for setting up
// the connection to clients, allocating/free'ing gpu memory etc.
@@ -65,7 +69,9 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
const gpu::GpuPreferences& gpu_preferences,
const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
const base::Optional<gpu::GpuFeatureInfo>&
- gpu_feature_info_for_hardware_gpu);
+ gpu_feature_info_for_hardware_gpu,
+ gpu::VulkanImplementation* vulkan_implementation,
+ base::OnceClosure exit_callback);
~GpuServiceImpl() override;
@@ -77,7 +83,18 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
base::WaitableEvent* shutdown_event = nullptr);
void Bind(mojom::GpuServiceRequest request);
- bool CreateGrContextIfNecessary(gl::GLSurface* surface);
+ // Get a GrContext and a GLContext for a given GL surface.
+ bool GetGrContextForGLSurface(gl::GLSurface* surface,
+ GrContext** gr_context,
+ gl::GLContext** gl_context);
+
+ GrContext* GetGrContextForVulkan();
+
+ // Notifies the GpuHost to stop using GPU compositing. This should be called
+ // in response to an error in the GPU process that occurred after
+ // InitializeWithHost() was called, otherwise GpuFeatureInfo should be set
+ // accordingly. This can safely be called from any thread.
+ void DisableGpuCompositing();
bool is_initialized() const { return !!gpu_host_; }
@@ -123,8 +140,18 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
gpu::SequenceId skia_output_surface_sequence_id() const {
return skia_output_surface_sequence_id_;
}
- gl::GLContext* context_for_skia() { return context_for_skia_.get(); }
- GrContext* gr_context() { return gr_context_.get(); }
+
+#if BUILDFLAG(ENABLE_VULKAN)
+ bool is_using_vulkan() const { return !!vulkan_context_provider_; }
+ VulkanContextProvider* vulkan_context_provider() {
+ return vulkan_context_provider_.get();
+ }
+#else
+ bool is_using_vulkan() const { return false; }
+ VulkanContextProvider* vulkan_context_provider() { return nullptr; }
+#endif
+
+ void set_oopd_enabled() { oopd_enabled_ = true; }
private:
void RecordLogMessage(int severity,
@@ -144,10 +171,10 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
void StoreShaderToDisk(int client_id,
const std::string& key,
const std::string& shader) override;
+ void ExitProcess() override;
#if defined(OS_WIN)
- void SendAcceleratedSurfaceCreatedChildWindow(
- gpu::SurfaceHandle parent_window,
- gpu::SurfaceHandle child_window) override;
+ void SendCreatedChildWindow(gpu::SurfaceHandle parent_window,
+ gpu::SurfaceHandle child_window) override;
#endif
void SetActiveURL(const GURL& url) override;
@@ -191,14 +218,16 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
GetGpuSupportedRuntimeVersionCallback callback) override;
void RequestHDRStatus(RequestHDRStatusCallback callback) override;
void LoadedShader(const std::string& key, const std::string& data) override;
- void DestroyingVideoSurface(int32_t surface_id,
- DestroyingVideoSurfaceCallback callback) override;
void WakeUpGpu() override;
void GpuSwitched() override;
void DestroyAllChannels() override;
void OnBackgroundCleanup() override;
void OnBackgrounded() override;
void OnForegrounded() override;
+#if defined(OS_MACOSX)
+ void BeginCATransaction() override;
+ void CommitCATransaction(CommitCATransactionCallback callback) override;
+#endif
void Crash() override;
void Hang() override;
void ThrowJavaException() override;
@@ -251,17 +280,24 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
// sequence id for running tasks from SkiaOutputSurface;
gpu::SequenceId skia_output_surface_sequence_id_;
- // A GLContext for |gr_context_|. It can only be accessed by Skia.
- scoped_refptr<gl::GLContext> context_for_skia_;
+ // GL and Gr contexts used by Skia only.
+ struct GrContextAndGLContext;
+ base::flat_map<unsigned long, GrContextAndGLContext> contexts_for_gl_;
- // A GrContext for SkiaOutputSurface (maybe raster as well).
- sk_sp<GrContext> gr_context_;
+#if BUILDFLAG(ENABLE_VULKAN)
+ gpu::VulkanImplementation* vulkan_implementation_;
+ scoped_refptr<VulkanContextProvider> vulkan_context_provider_;
+#endif
// An event that will be signalled when we shutdown. On some platforms it
// comes from external sources.
std::unique_ptr<base::WaitableEvent> owned_shutdown_event_;
base::WaitableEvent* shutdown_event_ = nullptr;
+ // Callback that safely exits GPU process.
+ base::OnceClosure exit_callback_;
+ bool is_exiting_ = false;
+
base::Time start_time_;
// Used to track the task to bind a GpuServiceRequest on the io thread.
@@ -272,6 +308,8 @@ class VIZ_SERVICE_EXPORT GpuServiceImpl : public gpu::GpuChannelManagerDelegate,
scoped_refptr<arc::ProtectedBufferManager> protected_buffer_manager_;
#endif // defined(OS_CHROMEOS)
+ bool oopd_enabled_ = false;
+
base::WeakPtr<GpuServiceImpl> weak_ptr_;
base::WeakPtrFactory<GpuServiceImpl> weak_ptr_factory_;
diff --git a/chromium/components/viz/service/gl/gpu_service_impl_unittest.cc b/chromium/components/viz/service/gl/gpu_service_impl_unittest.cc
index 5120ef5d7f1..e9885672a86 100644
--- a/chromium/components/viz/service/gl/gpu_service_impl_unittest.cc
+++ b/chromium/components/viz/service/gl/gpu_service_impl_unittest.cc
@@ -6,6 +6,7 @@
#include <memory>
+#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
@@ -51,7 +52,8 @@ class GpuServiceTest : public testing::Test {
gpu_service_ = std::make_unique<GpuServiceImpl>(
gpu::GPUInfo(), nullptr /* watchdog_thread */, io_thread_.task_runner(),
gpu::GpuFeatureInfo(), gpu::GpuPreferences(), gpu::GPUInfo(),
- gpu::GpuFeatureInfo());
+ gpu::GpuFeatureInfo(), nullptr /* vulkan_implementation */,
+ base::DoNothing() /* exit_callback */);
}
void TearDown() override {
diff --git a/chromium/components/viz/service/hit_test/DEPS b/chromium/components/viz/service/hit_test/DEPS
index fdd37f66e30..0956a6854e3 100644
--- a/chromium/components/viz/service/hit_test/DEPS
+++ b/chromium/components/viz/service/hit_test/DEPS
@@ -12,6 +12,5 @@ specific_include_rules = {
"hit_test_manager_fuzzer.cc": [
"+components/viz/service/frame_sinks",
"+components/viz/test",
- "+mojo/edk/embedder",
],
}
diff --git a/chromium/components/viz/service/hit_test/hit_test_aggregator.cc b/chromium/components/viz/service/hit_test/hit_test_aggregator.cc
index d598f2cdb57..5b9f8201001 100644
--- a/chromium/components/viz/service/hit_test/hit_test_aggregator.cc
+++ b/chromium/components/viz/service/hit_test/hit_test_aggregator.cc
@@ -5,6 +5,7 @@
#include "components/viz/service/hit_test/hit_test_aggregator.h"
#include "base/metrics/histogram_macros.h"
+#include "base/timer/elapsed_timer.h"
#include "components/viz/common/hit_test/hit_test_region_list.h"
#include "components/viz/service/hit_test/hit_test_aggregator_delegate.h"
#include "third_party/skia/include/core/SkMatrix44.h"
@@ -38,7 +39,12 @@ void HitTestAggregator::Aggregate(const SurfaceId& display_surface_id) {
hit_test_data_size_ = 0;
hit_test_data_.resize(hit_test_data_capacity_);
+ base::ElapsedTimer aggregate_timer;
AppendRoot(display_surface_id);
+ UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES("Event.VizHitTest.AggregateTimeUs",
+ aggregate_timer.Elapsed(),
+ base::TimeDelta::FromMicroseconds(1),
+ base::TimeDelta::FromSeconds(10), 50);
referenced_child_regions_.clear();
SendHitTestData();
}
@@ -50,8 +56,6 @@ void HitTestAggregator::SendHitTestData() {
}
void HitTestAggregator::AppendRoot(const SurfaceId& surface_id) {
- SCOPED_UMA_HISTOGRAM_TIMER("Event.VizHitTest.AggregateTime");
-
const HitTestRegionList* hit_test_region_list =
hit_test_manager_->GetActiveHitTestRegionList(
local_surface_id_lookup_delegate_, surface_id.frame_sink_id());
diff --git a/chromium/components/viz/service/hit_test/hit_test_aggregator_unittest.cc b/chromium/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
index d59985209db..7e8a966240b 100644
--- a/chromium/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
+++ b/chromium/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
@@ -11,6 +11,7 @@
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "components/viz/host/host_frame_sink_manager.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.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/hit_test/hit_test_aggregator_delegate.h"
@@ -59,7 +60,8 @@ class TestHostFrameSinkManager : public HostFrameSinkManager {
class TestFrameSinkManagerImpl : public FrameSinkManagerImpl {
public:
- TestFrameSinkManagerImpl() = default;
+ explicit TestFrameSinkManagerImpl(SharedBitmapManager* shared_bitmap_manager)
+ : FrameSinkManagerImpl(shared_bitmap_manager) {}
~TestFrameSinkManagerImpl() override = default;
void SetLocalClient(TestHostFrameSinkManager* client) {
@@ -112,7 +114,8 @@ class HitTestAggregatorTest : public testing::Test {
// testing::Test:
void SetUp() override {
- frame_sink_manager_ = std::make_unique<TestFrameSinkManagerImpl>();
+ frame_sink_manager_ =
+ std::make_unique<TestFrameSinkManagerImpl>(&shared_bitmap_manager_);
host_frame_sink_manager_ = std::make_unique<TestHostFrameSinkManager>();
local_surface_id_lookup_delegate_ =
std::make_unique<TestLatestLocalSurfaceIdLookupDelegate>();
@@ -208,6 +211,7 @@ class HitTestAggregatorTest : public testing::Test {
}
private:
+ ServerSharedBitmapManager shared_bitmap_manager_;
std::unique_ptr<TestHitTestAggregator> hit_test_aggregator_;
std::unique_ptr<TestFrameSinkManagerImpl> frame_sink_manager_;
std::unique_ptr<TestHostFrameSinkManager> host_frame_sink_manager_;
diff --git a/chromium/components/viz/service/hit_test/hit_test_manager_fuzzer.cc b/chromium/components/viz/service/hit_test/hit_test_manager_fuzzer.cc
index 734d626750d..9b600143329 100644
--- a/chromium/components/viz/service/hit_test/hit_test_manager_fuzzer.cc
+++ b/chromium/components/viz/service/hit_test/hit_test_manager_fuzzer.cc
@@ -8,6 +8,7 @@
#include "base/command_line.h"
#include "base/test/fuzzed_data_provider.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.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/hit_test/hit_test_aggregator.h"
@@ -134,7 +135,8 @@ void SubmitHitTestRegionList(
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t num_bytes) {
base::FuzzedDataProvider fuzz(data, num_bytes);
- viz::FrameSinkManagerImpl frame_sink_manager;
+ viz::ServerSharedBitmapManager shared_bitmap_manager;
+ viz::FrameSinkManagerImpl frame_sink_manager(&shared_bitmap_manager);
viz::TestLatestLocalSurfaceIdLookupDelegate delegate;
viz::TestLatestLocalSurfaceIdLookupDelegate* lsi_delegate =
fuzz.ConsumeBool() ? &delegate : nullptr;
diff --git a/chromium/components/viz/service/main/BUILD.gn b/chromium/components/viz/service/main/BUILD.gn
index 43ac3d9168e..c3e716ab9aa 100644
--- a/chromium/components/viz/service/main/BUILD.gn
+++ b/chromium/components/viz/service/main/BUILD.gn
@@ -22,7 +22,6 @@ source_set("main") {
"//components/viz/service",
"//gpu/ipc:gl_in_process_context",
"//gpu/ipc/common",
- "//gpu/ipc/common:gpu_preferences_util",
"//gpu/ipc/service",
"//ipc",
"//mojo/public/cpp/system",
diff --git a/chromium/components/viz/service/main/viz_compositor_thread_runner.cc b/chromium/components/viz/service/main/viz_compositor_thread_runner.cc
new file mode 100644
index 00000000000..193499d2214
--- /dev/null
+++ b/chromium/components/viz/service/main/viz_compositor_thread_runner.cc
@@ -0,0 +1,108 @@
+// 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/main/viz_compositor_thread_runner.h"
+
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+#include "components/viz/common/switches.h"
+#include "components/viz/service/display_embedder/gpu_display_provider.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "ui/gfx/switches.h"
+
+namespace viz {
+namespace {
+
+const char kThreadName[] = "VizCompositorThread";
+
+std::unique_ptr<base::Thread> CreateAndStartCompositorThread() {
+ auto thread = std::make_unique<base::Thread>(kThreadName);
+
+ base::Thread::Options thread_options;
+#if defined(OS_WIN)
+ // Windows needs a UI message loop for child HWND. Other platforms can use the
+ // default message loop type.
+ thread_options.message_loop_type = base::MessageLoop::TYPE_UI;
+#elif defined(OS_CHROMEOS)
+ thread_options.priority = base::ThreadPriority::DISPLAY;
+#endif
+
+ CHECK(thread->StartWithOptions(thread_options));
+ return thread;
+}
+
+} // namespace
+
+VizCompositorThreadRunner::VizCompositorThreadRunner()
+ : thread_(CreateAndStartCompositorThread()),
+ task_runner_(thread_->task_runner()) {}
+
+VizCompositorThreadRunner::~VizCompositorThreadRunner() {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&VizCompositorThreadRunner::TearDownOnCompositorThread,
+ base::Unretained(this)));
+ thread_->Stop();
+}
+
+void VizCompositorThreadRunner::CreateFrameSinkManager(
+ mojom::FrameSinkManagerParamsPtr params) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &VizCompositorThreadRunner::CreateFrameSinkManagerOnCompositorThread,
+ base::Unretained(this), std::move(params)));
+}
+
+void VizCompositorThreadRunner::CreateFrameSinkManagerOnCompositorThread(
+ mojom::FrameSinkManagerParamsPtr params) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ DCHECK(!frame_sink_manager_);
+
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+
+ server_shared_bitmap_manager_ = std::make_unique<ServerSharedBitmapManager>();
+ base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+ server_shared_bitmap_manager_.get(), "ServerSharedBitmapManager",
+ base::ThreadTaskRunnerHandle::Get());
+
+ // Create GpuDisplayProvider usable for software compositing only.
+ display_provider_ = std::make_unique<GpuDisplayProvider>(
+ params->restart_id, server_shared_bitmap_manager_.get(),
+ command_line->HasSwitch(switches::kHeadless),
+ command_line->HasSwitch(switches::kRunAllCompositorStagesBeforeDraw));
+
+ base::Optional<uint32_t> activation_deadline_in_frames;
+ if (params->use_activation_deadline)
+ activation_deadline_in_frames = params->activation_deadline_in_frames;
+ frame_sink_manager_ = std::make_unique<FrameSinkManagerImpl>(
+ server_shared_bitmap_manager_.get(), activation_deadline_in_frames,
+ display_provider_.get());
+ frame_sink_manager_->BindAndSetClient(
+ std::move(params->frame_sink_manager), nullptr,
+ mojom::FrameSinkManagerClientPtr(
+ std::move(params->frame_sink_manager_client)));
+}
+
+void VizCompositorThreadRunner::TearDownOnCompositorThread() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ if (server_shared_bitmap_manager_) {
+ base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
+ server_shared_bitmap_manager_.get());
+ }
+
+ frame_sink_manager_.reset();
+ display_provider_.reset();
+ server_shared_bitmap_manager_.reset();
+}
+
+} // namespace viz
diff --git a/chromium/components/viz/service/main/viz_compositor_thread_runner.h b/chromium/components/viz/service/main/viz_compositor_thread_runner.h
new file mode 100644
index 00000000000..93c8e9d7001
--- /dev/null
+++ b/chromium/components/viz/service/main/viz_compositor_thread_runner.h
@@ -0,0 +1,59 @@
+// 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_MAIN_VIZ_COMPOSITOR_THREAD_RUNNER_H_
+#define COMPONENTS_VIZ_SERVICE_MAIN_VIZ_COMPOSITOR_THREAD_RUNNER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "components/viz/service/viz_service_export.h"
+#include "services/viz/privileged/interfaces/viz_main.mojom.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+class Thread;
+} // namespace base
+
+namespace viz {
+class DisplayProvider;
+class FrameSinkManagerImpl;
+class ServerSharedBitmapManager;
+
+// Starts and runs the VizCompositorThread. The thread will be started when this
+// object is constructed. Objects on the thread will be initialized after
+// calling CreateFrameSinkManager(). Destructor will teardown objects on thread
+// and then stop the thread.
+// TODO(kylechar): Convert VizMainImpl to use VizCompositorThreadRunner.
+class VIZ_SERVICE_EXPORT VizCompositorThreadRunner {
+ public:
+ VizCompositorThreadRunner();
+ // Performs teardown on thread and then stops thread.
+ ~VizCompositorThreadRunner();
+
+ // Create FrameSinkManager from |params|. This can be called from the thread
+ // that owns |this| to initialize state on VizCompositorThreadRunner.
+ void CreateFrameSinkManager(mojom::FrameSinkManagerParamsPtr params);
+
+ private:
+ void CreateFrameSinkManagerOnCompositorThread(
+ mojom::FrameSinkManagerParamsPtr params);
+ void TearDownOnCompositorThread();
+
+ // Start variables to be accessed only on |task_runner_|.
+ std::unique_ptr<ServerSharedBitmapManager> server_shared_bitmap_manager_;
+ std::unique_ptr<DisplayProvider> display_provider_;
+ std::unique_ptr<FrameSinkManagerImpl> frame_sink_manager_;
+ // End variables to be accessed only on |task_runner_|.
+
+ std::unique_ptr<base::Thread> thread_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(VizCompositorThreadRunner);
+};
+
+} // namespace viz
+
+#endif // COMPONENTS_VIZ_SERVICE_MAIN_VIZ_COMPOSITOR_THREAD_RUNNER_H_
diff --git a/chromium/components/viz/service/main/viz_main_impl.cc b/chromium/components/viz/service/main/viz_main_impl.cc
index 72b26afce32..7676c307e6f 100644
--- a/chromium/components/viz/service/main/viz_main_impl.cc
+++ b/chromium/components/viz/service/main/viz_main_impl.cc
@@ -12,15 +12,18 @@
#include "base/power_monitor/power_monitor_device_source.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/trace_event/memory_dump_manager.h"
#include "build/build_config.h"
#include "components/viz/common/switches.h"
#include "components/viz/service/display_embedder/gpu_display_provider.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/frame_sinks/frame_sink_manager_impl.h"
#include "components/viz/service/gl/gpu_service_impl.h"
#include "gpu/command_buffer/common/activity_flags.h"
+#include "gpu/config/gpu_preferences.h"
#include "gpu/config/gpu_switches.h"
#include "gpu/ipc/common/gpu_memory_buffer_support.h"
-#include "gpu/ipc/common/gpu_preferences_util.h"
#include "gpu/ipc/gpu_in_process_thread_service.h"
#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
#include "gpu/ipc/service/gpu_watchdog_thread.h"
@@ -29,6 +32,7 @@
#include "services/metrics/public/cpp/mojo_ukm_recorder.h"
#include "services/metrics/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
+#include "third_party/skia/include/core/SkFontLCDConfig.h"
#include "ui/gfx/switches.h"
#if defined(OS_CHROMEOS) && BUILDFLAG(USE_VAAPI)
@@ -42,15 +46,29 @@
namespace {
-std::unique_ptr<base::Thread> CreateAndStartCompositorThread() {
- auto thread = std::make_unique<base::Thread>("VizCompositorThread");
+std::unique_ptr<viz::CompositorThreadType> CreateAndStartCompositorThread() {
+ const char* thread_name = "VizCompositorThread";
+
+#if defined(OS_ANDROID)
+ auto thread = std::make_unique<base::android::JavaHandlerThread>(
+ thread_name, base::ThreadPriority::DISPLAY);
+ thread->Start();
+ return thread;
+#else
+ auto thread = std::make_unique<base::Thread>(thread_name);
base::Thread::Options thread_options;
- thread_options.message_loop_type = base::MessageLoop::TYPE_DEFAULT;
-#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
+#if defined(OS_WIN)
+ // Windows needs a UI message loop for child HWND. Other platforms can use the
+ // default message loop type.
+ thread_options.message_loop_type = base::MessageLoop::TYPE_UI;
+#endif
+
+#if defined(OS_CHROMEOS)
thread_options.priority = base::ThreadPriority::DISPLAY;
#endif
CHECK(thread->StartWithOptions(thread_options));
return thread;
+#endif
}
std::unique_ptr<base::Thread> CreateAndStartIOThread() {
@@ -105,7 +123,7 @@ VizMainImpl::VizMainImpl(Delegate* delegate,
if (command_line->HasSwitch(switches::kGpuPreferences)) {
std::string value =
command_line->GetSwitchValueASCII(switches::kGpuPreferences);
- bool success = gpu::SwitchValueToGpuPreferences(value, &gpu_preferences);
+ bool success = gpu_preferences.FromSwitchValue(value);
CHECK(success);
}
#if defined(OS_CHROMEOS) && BUILDFLAG(USE_VAAPI)
@@ -141,11 +159,22 @@ VizMainImpl::VizMainImpl(Delegate* delegate,
CreateUkmRecorderIfNeeded(dependencies.connector);
+ // We need to provide GpuServiceImpl a callback to exit the GPU process. With
+ // OOP-D this requires destroying RootCompositorFrameSinkImpls on the
+ // compositor thread while the GPU thread is still running to avoid deadlock.
+ // For non OOP-D we can simply quit the GPU thread RunLoop.
+ base::OnceClosure exit_callback =
+ compositor_thread_
+ ? base::BindOnce(&VizMainImpl::ExitProcess, base::Unretained(this))
+ : base::BindOnce(&base::RunLoop::QuitCurrentDeprecated);
gpu_service_ = std::make_unique<GpuServiceImpl>(
gpu_init_->gpu_info(), gpu_init_->TakeWatchdogThread(), io_task_runner(),
gpu_init_->gpu_feature_info(), gpu_init_->gpu_preferences(),
gpu_init_->gpu_info_for_hardware_gpu(),
- gpu_init_->gpu_feature_info_for_hardware_gpu());
+ gpu_init_->gpu_feature_info_for_hardware_gpu(),
+ gpu_init_->vulkan_implementation(), std::move(exit_callback));
+ if (dependencies_.create_display_compositor)
+ gpu_service_->set_oopd_enabled();
}
VizMainImpl::~VizMainImpl() {
@@ -166,8 +195,9 @@ VizMainImpl::~VizMainImpl() {
compositor_thread_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&VizMainImpl::TearDownOnCompositorThread,
base::Unretained(this)));
+ compositor_thread_->Stop();
compositor_thread_.reset();
- compositor_thread_task_runner_ = nullptr;
+ compositor_thread_task_runner_.reset();
}
if (ukm_recorder_)
@@ -191,9 +221,14 @@ void VizMainImpl::CreateGpuService(
mojom::GpuHostPtr gpu_host,
discardable_memory::mojom::DiscardableSharedMemoryManagerPtr
discardable_memory_manager,
- mojo::ScopedSharedBufferHandle activity_flags) {
+ mojo::ScopedSharedBufferHandle activity_flags,
+ gfx::FontRenderParams::SubpixelRendering subpixel_rendering) {
DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
- gpu_service_->UpdateGPUInfo();
+
+ // If GL is disabled then don't try to collect GPUInfo, we're not using GPU.
+ if (gl::GetGLImplementation() != gl::kGLImplementationDisabled)
+ gpu_service_->UpdateGPUInfo();
+
for (const LogMessage& log : log_messages_)
gpu_host->RecordLogMessage(log.severity, log.header, log.message);
log_messages_.clear();
@@ -216,6 +251,13 @@ void VizMainImpl::CreateGpuService(
discardable_shared_memory_manager_.get());
}
+ SkFontLCDConfig::SetSubpixelOrder(
+ gfx::FontRenderParams::SubpixelRenderingToSkiaLCDOrder(
+ subpixel_rendering));
+ SkFontLCDConfig::SetSubpixelOrientation(
+ gfx::FontRenderParams::SubpixelRenderingToSkiaLCDOrientation(
+ subpixel_rendering));
+
gpu_service_->Bind(std::move(request));
gpu_service_->InitializeWithHost(
std::move(gpu_host),
@@ -257,11 +299,11 @@ void VizMainImpl::CreateFrameSinkManager(
void VizMainImpl::CreateFrameSinkManagerInternal(
mojom::FrameSinkManagerParamsPtr params) {
- DCHECK(!gpu_command_service_);
+ DCHECK(!task_executor_);
DCHECK(gpu_service_);
DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
- gpu_command_service_ = base::MakeRefCounted<gpu::GpuInProcessThreadService>(
+ task_executor_ = base::MakeRefCounted<gpu::GpuInProcessThreadService>(
gpu_thread_task_runner_, gpu_service_->sync_point_manager(),
gpu_service_->mailbox_manager(), gpu_service_->share_group(),
gpu_service_->gpu_feature_info(),
@@ -275,13 +317,27 @@ void VizMainImpl::CreateFrameSinkManagerInternal(
void VizMainImpl::CreateFrameSinkManagerOnCompositorThread(
mojom::FrameSinkManagerParamsPtr params) {
+ DCHECK(compositor_thread_task_runner_->BelongsToCurrentThread());
DCHECK(!frame_sink_manager_);
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ server_shared_bitmap_manager_ = std::make_unique<ServerSharedBitmapManager>();
+ base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+ server_shared_bitmap_manager_.get(), "viz::ServerSharedBitmapManager",
+ base::ThreadTaskRunnerHandle::Get());
+
+ auto* gpu_channel_manager = gpu_service_->gpu_channel_manager();
+ gpu::ImageFactory* image_factory = nullptr;
+ if (gpu_channel_manager->gpu_memory_buffer_factory()) {
+ image_factory =
+ gpu_channel_manager->gpu_memory_buffer_factory()->AsImageFactory();
+ }
display_provider_ = std::make_unique<GpuDisplayProvider>(
- params->restart_id, gpu_service_.get(), gpu_command_service_,
- gpu_service_->gpu_channel_manager(),
+ params->restart_id, gpu_service_.get(), task_executor_,
+ gpu_channel_manager->delegate(),
+ std::make_unique<InProcessGpuMemoryBufferManager>(gpu_channel_manager),
+ image_factory, server_shared_bitmap_manager_.get(),
command_line->HasSwitch(switches::kHeadless),
command_line->HasSwitch(switches::kRunAllCompositorStagesBeforeDraw));
@@ -291,14 +347,46 @@ void VizMainImpl::CreateFrameSinkManagerOnCompositorThread(
if (params->use_activation_deadline)
activation_deadline_in_frames = params->activation_deadline_in_frames;
frame_sink_manager_ = std::make_unique<FrameSinkManagerImpl>(
- activation_deadline_in_frames, display_provider_.get());
+ server_shared_bitmap_manager_.get(), activation_deadline_in_frames,
+ display_provider_.get());
frame_sink_manager_->BindAndSetClient(std::move(params->frame_sink_manager),
nullptr, std::move(client));
}
void VizMainImpl::TearDownOnCompositorThread() {
+ DCHECK(compositor_thread_task_runner_->BelongsToCurrentThread());
+
+ base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
+ server_shared_bitmap_manager_.get());
+
frame_sink_manager_.reset();
display_provider_.reset();
+ server_shared_bitmap_manager_.reset();
+}
+
+void VizMainImpl::CleanupForShutdownOnCompositorThread() {
+ DCHECK(compositor_thread_task_runner_->BelongsToCurrentThread());
+
+ if (frame_sink_manager_)
+ frame_sink_manager_->ForceShutdown();
+}
+
+void VizMainImpl::ExitProcess() {
+ DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
+ DCHECK(compositor_thread_task_runner_);
+
+ // Close mojom::VizMain bindings first so the browser can't try to reconnect.
+ binding_.Close();
+ associated_binding_.Close();
+
+ // PostTask to the compositor thread to cleanup and then exit the GPU thread
+ // RunLoop once that is finished. Unretained is safe here because |this| owns
+ // |compositor_thread_|.
+ compositor_thread_task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ base::BindOnce(&VizMainImpl::CleanupForShutdownOnCompositorThread,
+ base::Unretained(this)),
+ base::BindOnce([]() { base::RunLoop::QuitCurrentDeprecated(); }));
}
void VizMainImpl::PreSandboxStartup() {
diff --git a/chromium/components/viz/service/main/viz_main_impl.h b/chromium/components/viz/service/main/viz_main_impl.h
index fcb3f5680ba..273c74c5c30 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 "base/power_monitor/power_monitor.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread.h"
+#include "build/build_config.h"
#include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
#include "gpu/ipc/in_process_command_buffer.h"
#include "gpu/ipc/service/gpu_init.h"
@@ -15,6 +16,11 @@
#include "mojo/public/cpp/bindings/binding.h"
#include "services/viz/privileged/interfaces/gl/gpu_service.mojom.h"
#include "services/viz/privileged/interfaces/viz_main.mojom.h"
+#include "ui/gfx/font_render_params.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/java_handler_thread.h"
+#endif
namespace gpu {
class SyncPointManager;
@@ -32,6 +38,13 @@ namespace viz {
class DisplayProvider;
class FrameSinkManagerImpl;
class GpuServiceImpl;
+class ServerSharedBitmapManager;
+
+#if defined(OS_ANDROID)
+using CompositorThreadType = base::android::JavaHandlerThread;
+#else
+using CompositorThreadType = base::Thread;
+#endif
class VizMainImpl : public gpu::GpuSandboxHelper, public mojom::VizMain {
public:
@@ -70,6 +83,8 @@ class VizMainImpl : public gpu::GpuSandboxHelper, public mojom::VizMain {
DISALLOW_COPY_AND_ASSIGN(ExternalDependencies);
};
+ // TODO(kylechar): Provide a quit closure for the appropriate RunLoop instance
+ // to stop the thread and remove base::RunLoop::QuitCurrentDeprecated() usage.
VizMainImpl(Delegate* delegate,
ExternalDependencies dependencies,
std::unique_ptr<gpu::GpuInit> gpu_init = nullptr);
@@ -87,12 +102,20 @@ class VizMainImpl : public gpu::GpuSandboxHelper, public mojom::VizMain {
mojom::GpuHostPtr gpu_host,
discardable_memory::mojom::DiscardableSharedMemoryManagerPtr
discardable_memory_manager,
- mojo::ScopedSharedBufferHandle activity_flags) override;
+ mojo::ScopedSharedBufferHandle activity_flags,
+ gfx::FontRenderParams::SubpixelRendering subpixel_rendering) override;
void CreateFrameSinkManager(mojom::FrameSinkManagerParamsPtr params) override;
GpuServiceImpl* gpu_service() { return gpu_service_.get(); }
const GpuServiceImpl* gpu_service() const { return gpu_service_.get(); }
+ // Note that this may be null if viz is running in the browser process and
+ // using the ServiceDiscardableSharedMemoryManager.
+ discardable_memory::ClientDiscardableSharedMemoryManager*
+ discardable_shared_memory_manager() {
+ return discardable_shared_memory_manager_.get();
+ }
+
private:
// Initializes GPU's UkmRecorder if GPU is running in it's own process.
void CreateUkmRecorderIfNeeded(service_manager::Connector* connector);
@@ -103,6 +126,14 @@ class VizMainImpl : public gpu::GpuSandboxHelper, public mojom::VizMain {
void TearDownOnCompositorThread();
+ // Performs necessary cleanup on the compositor thread to allow for a clean
+ // process exit.
+ void CleanupForShutdownOnCompositorThread();
+
+ // Cleanly exits the process. This is only used with OOP-D when there is a
+ // compositor thread.
+ void ExitProcess();
+
// gpu::GpuSandboxHelper:
void PreSandboxStartup() override;
bool EnsureSandboxInitialized(gpu::GpuWatchdogThread* watchdog_thread,
@@ -128,8 +159,10 @@ class VizMainImpl : public gpu::GpuSandboxHelper, public mojom::VizMain {
std::unique_ptr<gpu::GpuInit> gpu_init_;
std::unique_ptr<GpuServiceImpl> gpu_service_;
- // The InCommandCommandBuffer::Service used by the frame sink manager.
- scoped_refptr<gpu::InProcessCommandBuffer::Service> gpu_command_service_;
+ // This is created for OOP-D only. It allows the display compositor to use
+ // InProcessCommandBuffer to send GPU commands to the GPU thread from the
+ // compositor thread.
+ scoped_refptr<gpu::CommandBufferTaskExecutor> task_executor_;
// If the gpu service is not yet ready then we stash pending
// FrameSinkManagerParams.
@@ -137,13 +170,14 @@ class VizMainImpl : public gpu::GpuSandboxHelper, public mojom::VizMain {
// Provides mojo interfaces for creating and managing FrameSinks. These live
// on the compositor thread.
- std::unique_ptr<FrameSinkManagerImpl> frame_sink_manager_;
+ std::unique_ptr<ServerSharedBitmapManager> server_shared_bitmap_manager_;
std::unique_ptr<DisplayProvider> display_provider_;
+ std::unique_ptr<FrameSinkManagerImpl> frame_sink_manager_;
const scoped_refptr<base::SingleThreadTaskRunner> gpu_thread_task_runner_;
// The main thread for the display compositor.
- std::unique_ptr<base::Thread> compositor_thread_;
+ std::unique_ptr<CompositorThreadType> compositor_thread_;
scoped_refptr<base::SingleThreadTaskRunner> compositor_thread_task_runner_;
std::unique_ptr<ukm::MojoUkmRecorder> ukm_recorder_;
diff --git a/chromium/components/viz/service/frame_sinks/referenced_surface_tracker.cc b/chromium/components/viz/service/surfaces/referenced_surface_tracker.cc
index 30d7a1b134d..e002db819d6 100644
--- a/chromium/components/viz/service/frame_sinks/referenced_surface_tracker.cc
+++ b/chromium/components/viz/service/surfaces/referenced_surface_tracker.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/viz/service/frame_sinks/referenced_surface_tracker.h"
+#include "components/viz/service/surfaces/referenced_surface_tracker.h"
#include "base/logging.h"
diff --git a/chromium/components/viz/service/frame_sinks/referenced_surface_tracker.h b/chromium/components/viz/service/surfaces/referenced_surface_tracker.h
index 1b45923baf7..2770dffe258 100644
--- a/chromium/components/viz/service/frame_sinks/referenced_surface_tracker.h
+++ b/chromium/components/viz/service/surfaces/referenced_surface_tracker.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_VIZ_SERVICE_FRAME_SINKS_REFERENCED_SURFACE_TRACKER_H_
-#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_REFERENCED_SURFACE_TRACKER_H_
+#ifndef COMPONENTS_VIZ_SERVICE_SURFACES_REFERENCED_SURFACE_TRACKER_H_
+#define COMPONENTS_VIZ_SERVICE_SURFACES_REFERENCED_SURFACE_TRACKER_H_
#include <vector>
@@ -27,4 +27,4 @@ void VIZ_SERVICE_EXPORT GetSurfaceReferenceDifference(
} // namespace viz
-#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_REFERENCED_SURFACE_TRACKER_H_
+#endif // COMPONENTS_VIZ_SERVICE_SURFACES_REFERENCED_SURFACE_TRACKER_H_
diff --git a/chromium/components/viz/service/frame_sinks/referenced_surface_tracker_unittest.cc b/chromium/components/viz/service/surfaces/referenced_surface_tracker_unittest.cc
index 6112c02d14b..6bfca44f06f 100644
--- a/chromium/components/viz/service/frame_sinks/referenced_surface_tracker_unittest.cc
+++ b/chromium/components/viz/service/surfaces/referenced_surface_tracker_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/viz/service/frame_sinks/referenced_surface_tracker.h"
+#include "components/viz/service/surfaces/referenced_surface_tracker.h"
#include <memory>
diff --git a/chromium/components/viz/service/surfaces/surface.cc b/chromium/components/viz/service/surfaces/surface.cc
index 9f8a1bc373e..dc9c682da50 100644
--- a/chromium/components/viz/service/surfaces/surface.cc
+++ b/chromium/components/viz/service/surfaces/surface.cc
@@ -15,9 +15,11 @@
#include "components/viz/common/resources/returned_resource.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "components/viz/service/surfaces/referenced_surface_tracker.h"
#include "components/viz/service/surfaces/surface_client.h"
#include "components/viz/service/surfaces/surface_manager.h"
#include "components/viz/service/viz_service_export.h"
+#include "ui/gfx/presentation_feedback.h"
namespace viz {
@@ -28,7 +30,11 @@ Surface::Surface(const SurfaceInfo& surface_info,
: surface_info_(surface_info),
surface_manager_(surface_manager),
surface_client_(std::move(surface_client)),
- needs_sync_tokens_(needs_sync_tokens) {}
+ needs_sync_tokens_(needs_sync_tokens) {
+ TRACE_EVENT_ASYNC_BEGIN1(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime"),
+ "Surface", this, "surface_info",
+ surface_info.ToString());
+}
Surface::~Surface() {
ClearCopyRequests();
@@ -42,6 +48,10 @@ Surface::~Surface() {
if (deadline_)
deadline_->Cancel();
+
+ TRACE_EVENT_ASYNC_END1(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime"),
+ "Surface", this, "surface_info",
+ surface_info_.ToString());
}
void Surface::SetDependencyDeadline(
@@ -88,12 +98,38 @@ void Surface::UnrefResources(const std::vector<ReturnedResource>& resources) {
surface_client_->UnrefResources(resources);
}
+void Surface::UpdateSurfaceReferences() {
+ const base::flat_set<SurfaceId>& existing_referenced_surfaces =
+ surface_manager_->GetSurfacesReferencedByParent(surface_id());
+ base::flat_set<SurfaceId> new_referenced_surfaces(
+ active_referenced_surfaces().begin(), active_referenced_surfaces().end(),
+ base::KEEP_FIRST_OF_DUPES);
+
+ // Populate list of surface references to add and remove by getting the
+ // difference between existing surface references and surface references for
+ // latest activated CompositorFrame.
+ std::vector<SurfaceReference> references_to_add;
+ std::vector<SurfaceReference> references_to_remove;
+ GetSurfaceReferenceDifference(surface_id(), existing_referenced_surfaces,
+ new_referenced_surfaces, &references_to_add,
+ &references_to_remove);
+
+ // Modify surface references stored in SurfaceManager.
+ if (!references_to_add.empty())
+ surface_manager_->AddSurfaceReferences(references_to_add);
+ if (!references_to_remove.empty())
+ surface_manager_->RemoveSurfaceReferences(references_to_remove);
+}
+
void Surface::RejectCompositorFramesToFallbackSurfaces() {
const std::vector<SurfaceId>& activation_dependencies =
GetPendingFrame().metadata.activation_dependencies;
- for (const SurfaceId& surface_id :
+ for (const SurfaceRange& surface_range :
GetPendingFrame().metadata.referenced_surfaces) {
+ if (!surface_range.start())
+ continue;
+ const SurfaceId& surface_id = *surface_range.start();
// A surface ID in |referenced_surfaces| that has a corresponding surface
// ID in |activation_dependencies| with the same frame sink ID is said to
// be a fallback surface that can be used in place of the primary surface
@@ -125,33 +161,19 @@ void Surface::Close() {
bool Surface::QueueFrame(
CompositorFrame frame,
uint64_t frame_index,
- base::OnceClosure callback,
- const AggregatedDamageCallback& aggregated_damage_callback,
+ base::ScopedClosureRunner frame_rejected_callback,
PresentedCallback presented_callback) {
late_activation_dependencies_.clear();
if (frame.size_in_pixels() != surface_info_.size_in_pixels() ||
frame.device_scale_factor() != surface_info_.device_scale_factor()) {
- TRACE_EVENT_INSTANT0("cc", "Surface invariants violation",
+ TRACE_EVENT_INSTANT0("viz", "Surface invariants violation",
TRACE_EVENT_SCOPE_THREAD);
- if (presented_callback) {
- std::move(presented_callback)
- .Run(base::TimeTicks(), base::TimeDelta(), 0);
- }
return false;
}
- if (closed_) {
- std::vector<ReturnedResource> resources =
- TransferableResource::ReturnResources(frame.resource_list);
- surface_client_->ReturnResources(resources);
- std::move(callback).Run();
- if (presented_callback) {
- std::move(presented_callback)
- .Run(base::TimeTicks(), base::TimeDelta(), 0);
- }
+ if (closed_)
return true;
- }
if (active_frame_data_ || pending_frame_data_)
previous_frame_surface_id_ = surface_id();
@@ -172,13 +194,11 @@ bool Surface::QueueFrame(
(deadline_ && !deadline.deadline_in_frames())) {
// If there are no blockers, then immediately activate the frame.
ActivateFrame(
- FrameData(std::move(frame), frame_index, std::move(callback),
- aggregated_damage_callback, std::move(presented_callback)),
+ FrameData(std::move(frame), frame_index, std::move(presented_callback)),
base::nullopt);
} else {
pending_frame_data_ =
- FrameData(std::move(frame), frame_index, std::move(callback),
- aggregated_damage_callback, std::move(presented_callback));
+ FrameData(std::move(frame), frame_index, std::move(presented_callback));
RejectCompositorFramesToFallbackSurfaces();
// If the deadline is in the past, then we will activate immediately.
@@ -192,6 +212,10 @@ bool 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.
+ (void)frame_rejected_callback.Release();
+
return true;
}
@@ -264,16 +288,12 @@ void Surface::ActivatePendingFrameForDeadline(
ActivatePendingFrame(duration);
}
-Surface::FrameData::FrameData(
- CompositorFrame&& frame,
- uint64_t frame_index,
- base::OnceClosure draw_callback,
- const AggregatedDamageCallback& aggregated_damage_callback,
- PresentedCallback presented_callback)
+Surface::FrameData::FrameData(CompositorFrame&& frame,
+ uint64_t frame_index,
+ PresentedCallback presented_callback)
: frame(std::move(frame)),
frame_index(frame_index),
- draw_callback(std::move(draw_callback)),
- aggregated_damage_callback(aggregated_damage_callback),
+ frame_processed(false),
presented_callback(std::move(presented_callback)) {}
Surface::FrameData::FrameData(FrameData&& other) = default;
@@ -319,6 +339,13 @@ void Surface::ActivateFrame(FrameData frame_data,
active_frame_data_ = std::move(frame_data);
+ active_referenced_surfaces_.clear();
+ for (SurfaceRange surface_range :
+ active_frame_data_->frame.metadata.referenced_surfaces) {
+ if (surface_range.start())
+ active_referenced_surfaces_.emplace_back(*surface_range.start());
+ }
+
for (auto& copy_request : old_copy_requests)
RequestCopyOfOutput(std::move(copy_request));
@@ -331,11 +358,25 @@ void Surface::ActivateFrame(FrameData frame_data,
surface_client_->OnSurfaceActivated(this);
if (!seen_first_frame_activation_) {
+ TRACE_EVENT_WITH_FLOW2(
+ TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
+ "LocalSurfaceId.Submission.Flow",
+ TRACE_ID_GLOBAL(
+ surface_info_.id().local_surface_id().submission_trace_id()),
+ TRACE_EVENT_FLAG_FLOW_IN, "step", "FirstSurfaceActivation",
+ "surface_id", surface_info_.id().ToString());
+
seen_first_frame_activation_ = true;
surface_manager_->FirstSurfaceActivation(surface_info_);
}
surface_manager_->SurfaceActivated(this, duration);
+
+ // Defer notifying the embedder of an updated token until the frame has been
+ // completely processed.
+ const auto& metadata = GetActiveFrame().metadata;
+ if (surface_client_ && metadata.send_frame_token_to_embedder)
+ surface_client_->OnFrameTokenChanged(metadata.frame_token);
}
FrameDeadline Surface::UpdateActivationDependencies(
@@ -367,6 +408,14 @@ FrameDeadline Surface::UpdateActivationDependencies(
if (!track_dependencies)
continue;
+ TRACE_EVENT_WITH_FLOW2(
+ TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
+ "LocalSurfaceId.Embed.Flow",
+ TRACE_ID_GLOBAL(surface_id.local_surface_id().embed_trace_id()),
+ TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
+ "AddedActivationDependency", "child_surface_id",
+ surface_id.ToString());
+
// Record the latest |parent_sequence_number| this surface is interested
// in observing for the provided FrameSinkId.
uint32_t& parent_sequence_number =
@@ -483,19 +532,20 @@ bool Surface::TakePresentedCallback(PresentedCallback* callback) {
}
void Surface::RunDrawCallback() {
- if (active_frame_data_ && !active_frame_data_->draw_callback.is_null())
- std::move(active_frame_data_->draw_callback).Run();
+ if (!active_frame_data_ || active_frame_data_->frame_processed)
+ return;
+ active_frame_data_->frame_processed = true;
+ if (surface_client_)
+ surface_client_->OnSurfaceProcessed(this);
}
void Surface::NotifyAggregatedDamage(const gfx::Rect& damage_rect,
base::TimeTicks expected_display_time) {
- if (!active_frame_data_ ||
- active_frame_data_->aggregated_damage_callback.is_null())
+ if (!active_frame_data_ || !surface_client_)
return;
-
- active_frame_data_->aggregated_damage_callback.Run(
- surface_id().local_surface_id(), active_frame_data_->frame, damage_rect,
- expected_display_time);
+ surface_client_->OnSurfaceAggregatedDamage(
+ this, surface_id().local_surface_id(), active_frame_data_->frame,
+ damage_rect, expected_display_time);
}
void Surface::OnDeadline(base::TimeDelta duration) {
@@ -516,12 +566,12 @@ void Surface::UnrefFrameResourcesAndRunCallbacks(
resource.sync_token.Clear();
surface_client_->UnrefResources(resources);
- if (frame_data->draw_callback)
- std::move(frame_data->draw_callback).Run();
+ if (!frame_data->frame_processed)
+ surface_client_->OnSurfaceProcessed(this);
if (frame_data->presented_callback) {
std::move(frame_data->presented_callback)
- .Run(base::TimeTicks(), base::TimeDelta(), 0);
+ .Run(gfx::PresentationFeedback::Failure());
}
}
@@ -561,6 +611,16 @@ void Surface::TakeLatencyInfoFromFrame(
}
void Surface::OnWillBeDrawn() {
+ if (!seen_first_surface_embedding_) {
+ seen_first_surface_embedding_ = true;
+
+ TRACE_EVENT_WITH_FLOW2(
+ TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
+ "LocalSurfaceId.Embed.Flow",
+ TRACE_ID_GLOBAL(surface_info_.id().local_surface_id().embed_trace_id()),
+ TRACE_EVENT_FLAG_FLOW_IN, "step", "FirstSurfaceEmbedding", "surface_id",
+ surface_info_.id().ToString());
+ }
surface_manager_->SurfaceWillBeDrawn(this);
}
diff --git a/chromium/components/viz/service/surfaces/surface.h b/chromium/components/viz/service/surfaces/surface.h
index b0331b956e9..9cf98025f4a 100644
--- a/chromium/components/viz/service/surfaces/surface.h
+++ b/chromium/components/viz/service/surfaces/surface.h
@@ -15,6 +15,7 @@
#include <vector>
#include "base/callback.h"
+#include "base/callback_helpers.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
@@ -31,6 +32,10 @@ namespace cc {
class CopyOutputRequest;
}
+namespace gfx {
+struct PresentationFeedback;
+}
+
namespace ui {
class LatencyInfo;
}
@@ -70,13 +75,8 @@ class SurfaceManager;
// the event of missing dependencies at display time.
class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
public:
- using AggregatedDamageCallback =
- base::RepeatingCallback<void(const LocalSurfaceId& local_surface_id,
- const CompositorFrame& frame,
- const gfx::Rect& damage_rect,
- base::TimeTicks expected_display_time)>;
using PresentedCallback =
- base::OnceCallback<void(base::TimeTicks, base::TimeDelta, uint32_t)>;
+ base::OnceCallback<void(const gfx::PresentationFeedback&)>;
Surface(const SurfaceInfo& surface_info,
SurfaceManager* surface_manager,
@@ -121,18 +121,20 @@ class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
bool needs_sync_tokens() const { return needs_sync_tokens_; }
+ // Updates surface references of the surface using the referenced
+ // surfaces from the most recent CompositorFrame.
+ // Modifies surface references stored in SurfaceManager.
+ void UpdateSurfaceReferences();
+
// Returns false if |frame| is invalid.
- // |draw_callback| is called once to notify the client that the previously
- // submitted CompositorFrame is processed and that another frame can be
- // there is visible damage.
- // |aggregated_damage_callback| is called when |surface| or one of its
- // descendents is determined to be damaged at aggregation time.
+ // |frame_rejected_callback| will be called once if the frame will not be
+ // displayed.
// |presented_callback| is called when the |frame| has been turned into light
- // the first time on display, or the |frame| will never be displayed.
+ // the first time on display, or if the |frame| is replaced by another prior
+ // to display.
bool QueueFrame(CompositorFrame frame,
uint64_t frame_index,
- base::OnceClosure draw_callback,
- const AggregatedDamageCallback& aggregated_damage_callback,
+ base::ScopedClosureRunner frame_rejected_callback,
PresentedCallback presented_callback);
// Notifies the Surface that a blocking SurfaceId now has an active
@@ -180,10 +182,8 @@ class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
void NotifyAggregatedDamage(const gfx::Rect& damage_rect,
base::TimeTicks expected_display_time);
- const std::vector<SurfaceId>* active_referenced_surfaces() const {
- return active_frame_data_
- ? &active_frame_data_->frame.metadata.referenced_surfaces
- : nullptr;
+ const std::vector<SurfaceId>& active_referenced_surfaces() const {
+ return active_referenced_surfaces_;
}
// Returns the set of dependencies blocking this surface's pending frame
@@ -202,7 +202,7 @@ class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
bool HasActiveFrame() const { return active_frame_data_.has_value(); }
bool HasPendingFrame() const { return pending_frame_data_.has_value(); }
bool HasUndrawnActiveFrame() const {
- return HasActiveFrame() && active_frame_data_->draw_callback;
+ return HasActiveFrame() && !active_frame_data_->frame_processed;
}
// SurfaceDeadlineClient implementation:
@@ -220,16 +220,16 @@ class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
struct FrameData {
FrameData(CompositorFrame&& frame,
uint64_t frame_index,
- base::OnceClosure draw_callback,
- const AggregatedDamageCallback& aggregated_damage_callback,
PresentedCallback presented_callback);
FrameData(FrameData&& other);
~FrameData();
FrameData& operator=(FrameData&& other);
+
CompositorFrame frame;
uint64_t frame_index;
- base::OnceClosure draw_callback;
- AggregatedDamageCallback aggregated_damage_callback;
+ // Whether the frame has been processed (displayed, or discarded), or not.
+ bool frame_processed = false;
+ // TODO(sad): This callback would ideally become part of SurfaceClient API.
PresentedCallback presented_callback;
};
@@ -277,6 +277,7 @@ class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
base::Optional<FrameData> active_frame_data_;
bool closed_ = false;
bool seen_first_frame_activation_ = false;
+ bool seen_first_surface_embedding_ = false;
const bool needs_sync_tokens_;
base::flat_set<SurfaceId> activation_dependencies_;
@@ -290,7 +291,7 @@ class VIZ_SERVICE_EXPORT Surface final : public SurfaceDeadlineClient {
// passes the local_id in the map, then this surface is no longer interested
// in observing activations for that FrameSinkId.
base::flat_map<FrameSinkId, SequenceNumbers> frame_sink_id_dependencies_;
-
+ std::vector<SurfaceId> active_referenced_surfaces_;
DISALLOW_COPY_AND_ASSIGN(Surface);
};
diff --git a/chromium/components/viz/service/surfaces/surface_client.h b/chromium/components/viz/service/surfaces/surface_client.h
index a4d2a6a7a1d..4793a35f0f9 100644
--- a/chromium/components/viz/service/surfaces/surface_client.h
+++ b/chromium/components/viz/service/surfaces/surface_client.h
@@ -50,6 +50,22 @@ class VIZ_SERVICE_EXPORT SurfaceClient {
virtual std::vector<std::unique_ptr<CopyOutputRequest>>
TakeCopyOutputRequests(const LocalSurfaceId& latest_surface_id) = 0;
+ // 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;
+
+ // This is called when |surface| or one of its descendents is determined to be
+ // damaged at aggregation time.
+ virtual void OnSurfaceAggregatedDamage(
+ Surface* surface,
+ const LocalSurfaceId& local_surface_id,
+ const CompositorFrame& frame,
+ const gfx::Rect& damage_rect,
+ base::TimeTicks expected_display_time) = 0;
+
private:
DISALLOW_COPY_AND_ASSIGN(SurfaceClient);
};
diff --git a/chromium/components/viz/service/surfaces/surface_dependency_tracker.cc b/chromium/components/viz/service/surfaces/surface_dependency_tracker.cc
index 74942f2be7c..12a6310abf5 100644
--- a/chromium/components/viz/service/surfaces/surface_dependency_tracker.cc
+++ b/chromium/components/viz/service/surfaces/surface_dependency_tracker.cc
@@ -19,8 +19,6 @@ SurfaceDependencyTracker::~SurfaceDependencyTracker() = default;
void SurfaceDependencyTracker::RequestSurfaceResolution(Surface* surface) {
DCHECK(surface->HasPendingFrame());
- const CompositorFrame& pending_frame = surface->GetPendingFrame();
-
if (IsSurfaceLate(surface)) {
ActivateLateSurfaceSubtree(surface);
return;
@@ -28,8 +26,7 @@ void SurfaceDependencyTracker::RequestSurfaceResolution(Surface* surface) {
// Activation dependencies that aren't currently known to the surface manager
// or do not have an active CompositorFrame block this frame.
- for (const SurfaceId& surface_id :
- pending_frame.metadata.activation_dependencies) {
+ for (const SurfaceId& surface_id : surface->activation_dependencies()) {
Surface* dependency = surface_manager_->GetSurfaceForId(surface_id);
if (!dependency || !dependency->HasActiveFrame()) {
blocked_surfaces_from_dependency_[surface_id.frame_sink_id()].insert(
@@ -61,53 +58,41 @@ void SurfaceDependencyTracker::OnSurfaceDependenciesChanged(
for (const FrameSinkId& frame_sink_id : removed_dependencies) {
auto it = blocked_surfaces_from_dependency_.find(frame_sink_id);
- it->second.erase(surface->surface_id());
- if (it->second.empty())
- blocked_surfaces_from_dependency_.erase(it);
+ if (it != blocked_surfaces_from_dependency_.end()) {
+ it->second.erase(surface->surface_id());
+ if (it->second.empty())
+ blocked_surfaces_from_dependency_.erase(it);
+ }
}
}
void SurfaceDependencyTracker::OnSurfaceDiscarded(Surface* surface) {
surfaces_with_missing_dependencies_.erase(surface->surface_id());
- // If the surface being destroyed doesn't have a pending frame then we have
- // nothing to do here.
- if (!surface->HasPendingFrame())
- return;
-
- const CompositorFrame& pending_frame = surface->GetPendingFrame();
+ base::flat_set<FrameSinkId> removed_dependencies;
+ for (const SurfaceId& surface_id : surface->activation_dependencies())
+ removed_dependencies.insert(surface_id.frame_sink_id());
- DCHECK(!pending_frame.metadata.activation_dependencies.empty());
-
- for (const SurfaceId& surface_id :
- pending_frame.metadata.activation_dependencies) {
- auto it =
- blocked_surfaces_from_dependency_.find(surface_id.frame_sink_id());
- if (it == blocked_surfaces_from_dependency_.end())
- continue;
-
- auto& blocked_surface_ids = it->second;
- auto blocked_surface_ids_it =
- blocked_surface_ids.find(surface->surface_id());
- if (blocked_surface_ids_it != blocked_surface_ids.end()) {
- blocked_surface_ids.erase(surface->surface_id());
- if (blocked_surface_ids.empty())
- blocked_surfaces_from_dependency_.erase(surface_id.frame_sink_id());
- }
- }
+ OnSurfaceDependenciesChanged(surface, {}, removed_dependencies);
// Pretend that the discarded surface's SurfaceId is now available to
// unblock dependencies because we now know the surface will never activate.
NotifySurfaceIdAvailable(surface->surface_id());
}
+void SurfaceDependencyTracker::OnFrameSinkInvalidated(
+ const FrameSinkId& frame_sink_id) {
+ // We now know the frame sink will never generated any more frames,
+ // thus unblock all dependencies to any future surfaces.
+ NotifySurfaceIdAvailable(SurfaceId::MaxSequenceId(frame_sink_id));
+}
+
void SurfaceDependencyTracker::ActivateLateSurfaceSubtree(Surface* surface) {
DCHECK(surface->HasPendingFrame());
- const CompositorFrame& pending_frame = surface->GetPendingFrame();
-
- for (const SurfaceId& surface_id :
- pending_frame.metadata.activation_dependencies) {
+ base::flat_set<SurfaceId> late_dependencies(
+ surface->activation_dependencies());
+ for (const SurfaceId& surface_id : late_dependencies) {
Surface* dependency = surface_manager_->GetSurfaceForId(surface_id);
if (dependency && dependency->HasPendingFrame())
ActivateLateSurfaceSubtree(dependency);
@@ -119,8 +104,6 @@ void SurfaceDependencyTracker::ActivateLateSurfaceSubtree(Surface* surface) {
void SurfaceDependencyTracker::UpdateSurfaceDeadline(Surface* surface) {
DCHECK(surface->HasPendingFrame());
- const CompositorFrame& pending_frame = surface->GetPendingFrame();
-
// Inherit the deadline from the first parent blocked on this surface.
auto it = blocked_surfaces_from_dependency_.find(
surface->surface_id().frame_sink_id());
@@ -140,8 +123,9 @@ void SurfaceDependencyTracker::UpdateSurfaceDeadline(Surface* surface) {
surface->has_deadline());
// Recursively propagate the newly set deadline to children.
- for (const SurfaceId& surface_id :
- pending_frame.metadata.activation_dependencies) {
+ base::flat_set<SurfaceId> activation_dependencies(
+ surface->activation_dependencies());
+ for (const SurfaceId& surface_id : activation_dependencies) {
Surface* dependency = surface_manager_->GetSurfaceForId(surface_id);
if (dependency && dependency->HasPendingFrame())
UpdateSurfaceDeadline(dependency);
diff --git a/chromium/components/viz/service/surfaces/surface_dependency_tracker.h b/chromium/components/viz/service/surfaces/surface_dependency_tracker.h
index a18251a2c11..064487b4332 100644
--- a/chromium/components/viz/service/surfaces/surface_dependency_tracker.h
+++ b/chromium/components/viz/service/surfaces/surface_dependency_tracker.h
@@ -40,6 +40,7 @@ class VIZ_SERVICE_EXPORT SurfaceDependencyTracker {
const base::flat_set<FrameSinkId>& added_dependencies,
const base::flat_set<FrameSinkId>& removed_dependencies);
void OnSurfaceDiscarded(Surface* surface);
+ void OnFrameSinkInvalidated(const FrameSinkId& frame_sink_id);
private:
// If |surface| has a dependent embedder frame, then it inherits the parent's
diff --git a/chromium/components/viz/service/surfaces/surface_hittest_unittest.cc b/chromium/components/viz/service/surfaces/surface_hittest_unittest.cc
index e82a5050c29..f3ed3b67a70 100644
--- a/chromium/components/viz/service/surfaces/surface_hittest_unittest.cc
+++ b/chromium/components/viz/service/surfaces/surface_hittest_unittest.cc
@@ -6,6 +6,7 @@
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.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/surfaces/surface.h"
@@ -70,7 +71,7 @@ using namespace test;
class SurfaceHittestTest : public testing::Test {
public:
- SurfaceHittestTest() = default;
+ SurfaceHittestTest() : frame_sink_manager_(&shared_bitmap_manager_) {}
~SurfaceHittestTest() override = default;
CompositorFrameSinkSupport& root_support() { return *supports_[0]; }
@@ -96,6 +97,7 @@ class SurfaceHittestTest : public testing::Test {
void TearDown() override { supports_.clear(); }
private:
+ ServerSharedBitmapManager shared_bitmap_manager_;
FrameSinkManagerImpl frame_sink_manager_;
std::vector<std::unique_ptr<CompositorFrameSinkSupport>> supports_;
FakeCompositorFrameSinkClient client_;
diff --git a/chromium/components/viz/service/surfaces/surface_manager.cc b/chromium/components/viz/service/surfaces/surface_manager.cc
index f46bd087b96..ff9617a4709 100644
--- a/chromium/components/viz/service/surfaces/surface_manager.cc
+++ b/chromium/components/viz/service/surfaces/surface_manager.cc
@@ -69,7 +69,7 @@ SurfaceManager::~SurfaceManager() {
base::flat_set<SurfaceId> children(
GetSurfacesReferencedByParent(root_surface_id_));
for (const auto& child : children)
- RemoveSurfaceReferenceImpl(root_surface_id_, child);
+ RemoveSurfaceReferenceImpl(SurfaceReference(root_surface_id_, child));
GarbageCollectSurfaces();
@@ -158,14 +158,7 @@ void SurfaceManager::DestroySurface(const SurfaceId& surface_id) {
surfaces_to_destroy_.insert(surface_id);
}
-void SurfaceManager::RegisterFrameSinkId(const FrameSinkId& frame_sink_id) {
- bool inserted = valid_frame_sink_labels_.emplace(frame_sink_id, "").second;
- DCHECK(inserted);
-}
-
void SurfaceManager::InvalidateFrameSinkId(const FrameSinkId& frame_sink_id) {
- valid_frame_sink_labels_.erase(frame_sink_id);
-
// Remove any temporary references owned by |frame_sink_id|.
std::vector<SurfaceId> temp_refs_to_clear;
for (auto& map_entry : temporary_references_) {
@@ -177,22 +170,9 @@ void SurfaceManager::InvalidateFrameSinkId(const FrameSinkId& frame_sink_id) {
for (auto& surface_id : temp_refs_to_clear)
RemoveTemporaryReference(surface_id, RemovedReason::INVALIDATED);
- GarbageCollectSurfaces();
-}
-
-void SurfaceManager::SetFrameSinkDebugLabel(const FrameSinkId& frame_sink_id,
- const std::string& debug_label) {
- auto it = valid_frame_sink_labels_.find(frame_sink_id);
- DCHECK(it != valid_frame_sink_labels_.end());
- it->second = debug_label;
-}
+ dependency_tracker_.OnFrameSinkInvalidated(frame_sink_id);
-std::string SurfaceManager::GetFrameSinkDebugLabel(
- const FrameSinkId& frame_sink_id) const {
- auto it = valid_frame_sink_labels_.find(frame_sink_id);
- if (it != valid_frame_sink_labels_.end())
- return it->second;
- return std::string();
+ GarbageCollectSurfaces();
}
const SurfaceId& SurfaceManager::GetRootSurfaceId() const {
@@ -211,7 +191,7 @@ void SurfaceManager::AddSurfaceReferences(
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (const auto& reference : references)
- AddSurfaceReferenceImpl(reference.parent_id(), reference.child_id());
+ AddSurfaceReferenceImpl(reference);
}
void SurfaceManager::RemoveSurfaceReferences(
@@ -219,7 +199,7 @@ void SurfaceManager::RemoveSurfaceReferences(
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (const auto& reference : references)
- RemoveSurfaceReferenceImpl(reference.parent_id(), reference.child_id());
+ RemoveSurfaceReferenceImpl(reference);
}
void SurfaceManager::AssignTemporaryReference(const SurfaceId& surface_id,
@@ -319,8 +299,11 @@ SurfaceManager::SurfaceIdSet SurfaceManager::GetLiveSurfacesForReferences() {
return reachable_surfaces;
}
-void SurfaceManager::AddSurfaceReferenceImpl(const SurfaceId& parent_id,
- const SurfaceId& child_id) {
+void SurfaceManager::AddSurfaceReferenceImpl(
+ const SurfaceReference& reference) {
+ const SurfaceId& parent_id = reference.parent_id();
+ const SurfaceId& child_id = reference.child_id();
+
if (parent_id.frame_sink_id() == child_id.frame_sink_id()) {
DLOG(ERROR) << "Cannot add self reference from " << parent_id << " to "
<< child_id;
@@ -344,8 +327,11 @@ void SurfaceManager::AddSurfaceReferenceImpl(const SurfaceId& parent_id,
RemoveTemporaryReference(child_id, RemovedReason::EMBEDDED);
}
-void SurfaceManager::RemoveSurfaceReferenceImpl(const SurfaceId& parent_id,
- const SurfaceId& child_id) {
+void SurfaceManager::RemoveSurfaceReferenceImpl(
+ const SurfaceReference& reference) {
+ const SurfaceId& parent_id = reference.parent_id();
+ const SurfaceId& child_id = reference.child_id();
+
auto iter_parent = references_.find(parent_id);
auto iter_child = references_.find(child_id);
if (iter_parent == references_.end() || iter_child == references_.end())
@@ -547,7 +533,8 @@ void SurfaceManager::SurfaceActivated(
// Trigger a display frame if necessary.
const CompositorFrame& frame = surface->GetActiveFrame();
if (!SurfaceModified(surface->surface_id(), frame.metadata.begin_frame_ack)) {
- TRACE_EVENT_INSTANT0("cc", "Damage not visible.", TRACE_EVENT_SCOPE_THREAD);
+ TRACE_EVENT_INSTANT0("viz", "Damage not visible.",
+ TRACE_EVENT_SCOPE_THREAD);
surface->RunDrawCallback();
}
@@ -601,10 +588,6 @@ void SurfaceManager::SurfaceReferencesToStringImpl(const SurfaceId& surface_id,
if (surface) {
*str << surface->surface_id().ToString();
- std::string frame_sink_label =
- GetFrameSinkDebugLabel(surface_id.frame_sink_id());
- if (!frame_sink_label.empty())
- *str << " " << frame_sink_label;
*str << (IsMarkedForDestruction(surface_id) ? " destroyed" : " live");
if (surface->HasPendingFrame()) {
diff --git a/chromium/components/viz/service/surfaces/surface_manager.h b/chromium/components/viz/service/surfaces/surface_manager.h
index d6e4a5b6b7f..2d9ee50d8c3 100644
--- a/chromium/components/viz/service/surfaces/surface_manager.h
+++ b/chromium/components/viz/service/surfaces/surface_manager.h
@@ -130,26 +130,10 @@ class VIZ_SERVICE_EXPORT SurfaceManager {
void SurfaceDamageExpected(const SurfaceId& surface_id,
const BeginFrameArgs& args);
- void RegisterFrameSinkId(const FrameSinkId& frame_sink_id);
-
// Invalidate a frame_sink_id that might still have associated sequences,
// possibly because a renderer process has crashed.
void InvalidateFrameSinkId(const FrameSinkId& frame_sink_id);
- const base::flat_map<FrameSinkId, std::string>& valid_frame_sink_labels()
- const {
- return valid_frame_sink_labels_;
- }
-
- // Set |debug_label| of the |frame_sink_id|. |frame_sink_id| must exist in
- // |valid_frame_sink_labels_| already when UpdateFrameSinkDebugLabel is
- // called.
- void SetFrameSinkDebugLabel(const FrameSinkId& frame_sink_id,
- const std::string& debug_label);
-
- // Returns the debug label associated with |frame_sink_id| if any.
- std::string GetFrameSinkDebugLabel(const FrameSinkId& frame_sink_id) const;
-
// Register a relationship between two namespaces. This relationship means
// that surfaces from the child namespace will be displayed in the parent.
// Children are allowed to use any begin frame source that their parent can
@@ -263,12 +247,10 @@ class VIZ_SERVICE_EXPORT SurfaceManager {
// Adds a reference from |parent_id| to |child_id| without dealing with
// temporary references.
- void AddSurfaceReferenceImpl(const SurfaceId& parent_id,
- const SurfaceId& child_id);
+ void AddSurfaceReferenceImpl(const SurfaceReference& reference);
// Removes a reference from a |parent_id| to |child_id|.
- void RemoveSurfaceReferenceImpl(const SurfaceId& parent_id,
- const SurfaceId& child_id);
+ void RemoveSurfaceReferenceImpl(const SurfaceReference& reference);
// Removes all surface references to or from |surface_id|. Used when the
// surface is about to be deleted.
@@ -315,11 +297,6 @@ class VIZ_SERVICE_EXPORT SurfaceManager {
base::flat_set<SurfaceId> surfaces_to_destroy_;
- // Set of valid FrameSinkIds and their labels. When a FrameSinkId is removed
- // from this set, any remaining (surface) sequences with that FrameSinkId are
- // considered satisfied.
- base::flat_map<FrameSinkId, std::string> valid_frame_sink_labels_;
-
// Root SurfaceId that references display root surfaces. There is no Surface
// with this id, it's for bookkeeping purposes only.
const SurfaceId root_surface_id_;
diff --git a/chromium/components/viz/service/surfaces/surface_reference.cc b/chromium/components/viz/service/surfaces/surface_reference.cc
index 7fb2b7735a6..5f61d8a3816 100644
--- a/chromium/components/viz/service/surfaces/surface_reference.cc
+++ b/chromium/components/viz/service/surfaces/surface_reference.cc
@@ -19,7 +19,7 @@ SurfaceReference::SurfaceReference(const SurfaceReference& other) = default;
SurfaceReference::~SurfaceReference() = default;
std::string SurfaceReference::ToString() const {
- return base::StringPrintf("parent=%s, child=%s",
+ return base::StringPrintf("SurfaceReference(%s, %s)",
parent_id_.ToString().c_str(),
child_id_.ToString().c_str());
}
diff --git a/chromium/components/viz/service/surfaces/surface_unittest.cc b/chromium/components/viz/service/surfaces/surface_unittest.cc
index 1e8078c90f1..99332a44e28 100644
--- a/chromium/components/viz/service/surfaces/surface_unittest.cc
+++ b/chromium/components/viz/service/surfaces/surface_unittest.cc
@@ -6,6 +6,7 @@
#include "cc/test/scheduler_test_common.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "components/viz/service/display_embedder/server_shared_bitmap_manager.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/surfaces/surface_dependency_tracker.h"
@@ -28,7 +29,8 @@ TEST(SurfaceTest, PresentationCallback) {
constexpr gfx::Rect kDamageRect(0, 0);
const LocalSurfaceId local_surface_id(6, base::UnguessableToken::Create());
- FrameSinkManagerImpl frame_sink_manager;
+ ServerSharedBitmapManager shared_bitmap_manager;
+ FrameSinkManagerImpl frame_sink_manager(&shared_bitmap_manager);
MockCompositorFrameSinkClient client;
auto support = std::make_unique<CompositorFrameSinkSupport>(
&client, &frame_sink_manager, kArbitraryFrameSinkId, kIsRoot,
@@ -37,7 +39,8 @@ TEST(SurfaceTest, PresentationCallback) {
CompositorFrame frame =
CompositorFrameBuilder()
.AddRenderPass(gfx::Rect(kSurfaceSize), kDamageRect)
- .SetPresentationToken(1)
+ .SetFrameToken(1)
+ .SetRequestPresentationFeedback(true)
.Build();
EXPECT_CALL(client, DidReceiveCompositorFrameAck(testing::_)).Times(1);
support->SubmitCompositorFrame(local_surface_id, std::move(frame));
@@ -50,9 +53,14 @@ TEST(SurfaceTest, PresentationCallback) {
CompositorFrame frame =
CompositorFrameBuilder()
.AddRenderPass(gfx::Rect(kSurfaceSize), kDamageRect)
- .SetPresentationToken(2)
+ .SetFrameToken(2)
+ .SetRequestPresentationFeedback(true)
.Build();
- EXPECT_CALL(client, DidDiscardCompositorFrame(1)).Times(1);
+ EXPECT_CALL(client, DidPresentCompositorFrame(
+ 1, testing::Field(
+ &gfx::PresentationFeedback::flags,
+ gfx::PresentationFeedback::Flags::kFailure)))
+ .Times(1);
EXPECT_CALL(client, DidReceiveCompositorFrameAck(testing::_)).Times(1);
support->SubmitCompositorFrame(local_surface_id, std::move(frame));
testing::Mock::VerifyAndClearExpectations(&client);
@@ -76,7 +84,8 @@ void TestCopyResultCallback(bool* called,
// Test that CopyOutputRequests can outlive the current frame and be
// aggregated on the next frame.
TEST(SurfaceTest, CopyRequestLifetime) {
- FrameSinkManagerImpl frame_sink_manager;
+ ServerSharedBitmapManager shared_bitmap_manager;
+ FrameSinkManagerImpl frame_sink_manager(&shared_bitmap_manager);
SurfaceManager* surface_manager = frame_sink_manager.surface_manager();
auto support = std::make_unique<CompositorFrameSinkSupport>(
nullptr, &frame_sink_manager, kArbitraryFrameSinkId, kIsRoot,
diff --git a/chromium/components/viz/test/BUILD.gn b/chromium/components/viz/test/BUILD.gn
index 2b21433fffb..4ef4ba278e4 100644
--- a/chromium/components/viz/test/BUILD.gn
+++ b/chromium/components/viz/test/BUILD.gn
@@ -30,8 +30,6 @@ viz_static_library("test_support") {
"mock_display_client.cc",
"mock_display_client.h",
"mock_helper.h",
- "ordered_simple_task_runner.cc",
- "ordered_simple_task_runner.h",
"ordered_texture_map.cc",
"ordered_texture_map.h",
"paths.cc",
@@ -60,8 +58,6 @@ viz_static_library("test_support") {
"test_shared_bitmap_manager.h",
"test_texture.cc",
"test_texture.h",
- "test_web_graphics_context_3d.cc",
- "test_web_graphics_context_3d.h",
]
deps = [
"//base",
@@ -100,8 +96,7 @@ viz_source_set("unit_tests") {
sources = [
"begin_frame_source_test_unittest.cc",
"mock_helper_unittest.cc",
- "ordered_simple_task_runner_unittest.cc",
- "test_web_graphics_context_3d_unittest.cc",
+ "test_gles2_interface_unittest.cc",
]
deps = [
":test_support",
diff --git a/chromium/components/web_resource/BUILD.gn b/chromium/components/web_resource/BUILD.gn
index c4b874b848b..74863cc5cac 100644
--- a/chromium/components/web_resource/BUILD.gn
+++ b/chromium/components/web_resource/BUILD.gn
@@ -22,6 +22,7 @@ static_library("web_resource") {
"//components/prefs",
"//components/version_info",
"//net",
+ "//services/network/public/cpp",
"//ui/base",
]
}
@@ -53,6 +54,8 @@ source_set("unit_tests") {
"//components/prefs:test_support",
"//components/version_info",
"//net:test_support",
+ "//services/network:test_support",
+ "//services/network/public/cpp",
"//testing/gtest",
"//third_party/icu",
]
diff --git a/chromium/components/web_resource/DEPS b/chromium/components/web_resource/DEPS
index 7140ec9d356..6660014fb35 100644
--- a/chromium/components/web_resource/DEPS
+++ b/chromium/components/web_resource/DEPS
@@ -5,6 +5,8 @@ include_rules = [
"+components/prefs",
"+components/version_info",
"+net",
+ "+services/network/public/cpp",
+ "+services/network/test",
"+ui/base",
# web_resource is used on iOS.
diff --git a/chromium/components/web_resource/eula_accepted_notifier.h b/chromium/components/web_resource/eula_accepted_notifier.h
index b386e1f3dfe..3dcdf1e3164 100644
--- a/chromium/components/web_resource/eula_accepted_notifier.h
+++ b/chromium/components/web_resource/eula_accepted_notifier.h
@@ -19,6 +19,7 @@ class EulaAcceptedNotifier {
// Observes EULA accepted state changes.
class Observer {
public:
+ virtual ~Observer() {}
virtual void OnEulaAccepted() = 0;
};
diff --git a/chromium/components/web_resource/web_resource_service.cc b/chromium/components/web_resource/web_resource_service.cc
index b4bc7c57cfa..05d6cebc5db 100644
--- a/chromium/components/web_resource/web_resource_service.cc
+++ b/chromium/components/web_resource/web_resource_service.cc
@@ -19,9 +19,9 @@
#include "components/google/core/browser/google_util.h"
#include "components/prefs/pref_service.h"
#include "net/base/load_flags.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_status.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
@@ -41,7 +41,7 @@ WebResourceService::WebResourceService(
const char* last_update_time_pref_name,
int start_fetch_delay_ms,
int cache_update_delay_ms,
- net::URLRequestContextGetter* request_context,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const char* disable_network_switch,
const ParseJSONCallback& parse_json_callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation)
@@ -55,7 +55,7 @@ WebResourceService::WebResourceService(
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),
- request_context_(request_context),
+ url_loader_factory_(url_loader_factory),
parse_json_callback_(parse_json_callback),
traffic_annotation_(traffic_annotation),
weak_ptr_factory_(this) {
@@ -69,25 +69,21 @@ void WebResourceService::StartAfterDelay() {
OnResourceRequestsAllowed();
}
-WebResourceService::~WebResourceService() {
-}
-
-void WebResourceService::OnURLFetchComplete(const net::URLFetcher* source) {
- // Delete the URLFetcher when this function exits.
- std::unique_ptr<net::URLFetcher> clean_up_fetcher(url_fetcher_.release());
+WebResourceService::~WebResourceService() = default;
- if (source->GetStatus().is_success() && source->GetResponseCode() == 200) {
- std::string data;
- source->GetResponseAsString(&data);
+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 (data.empty() || data == "{}") {
+ if (response_body->empty() || *response_body == "{}") {
OnUnpackFinished(std::make_unique<base::DictionaryValue>());
} else {
- parse_json_callback_.Run(data,
+ parse_json_callback_.Run(*response_body,
base::Bind(&WebResourceService::OnUnpackFinished,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&WebResourceService::OnUnpackError,
@@ -125,10 +121,10 @@ bool WebResourceService::GetFetchScheduled() const {
}
// Initializes the fetching of data from the resource server. Data
-// load calls OnURLFetchComplete.
+// load calls OnSimpleLoaderComplete.
void WebResourceService::StartFetch() {
// Set to false so that next fetch can be scheduled after this fetch or
- // if we recieve notification that resource is allowed.
+ // if we receive notification that resource is allowed.
fetch_scheduled_ = false;
// Check whether fetching is allowed.
if (!resource_request_allowed_notifier_->ResourceRequestsAllowed())
@@ -153,18 +149,22 @@ void WebResourceService::StartFetch() {
application_locale_);
DVLOG(1) << "WebResourceService StartFetch " << web_resource_server;
- url_fetcher_ = net::URLFetcher::Create(
- web_resource_server, net::URLFetcher::GET, this, traffic_annotation_);
- data_use_measurement::DataUseUserData::AttachToFetcher(
- url_fetcher_.get(),
- data_use_measurement::DataUseUserData::WEB_RESOURCE_SERVICE);
+ 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).
- url_fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE |
- net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SAVE_COOKIES);
- url_fetcher_->SetRequestContext(request_context_.get());
- url_fetcher_->Start();
+ resource_request->load_flags = net::LOAD_DISABLE_CACHE |
+ net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SAVE_COOKIES;
+ // TODO(https://crbug.com/808498): Re-add data use measurement once
+ // SimpleURLLoader supports it.
+ // ID=data_use_measurement::DataUseUserData::WEB_RESOURCE_SERVICE
+ 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() {
diff --git a/chromium/components/web_resource/web_resource_service.h b/chromium/components/web_resource/web_resource_service.h
index 7dab6ab9a75..279f7935fa3 100644
--- a/chromium/components/web_resource/web_resource_service.h
+++ b/chromium/components/web_resource/web_resource_service.h
@@ -16,7 +16,6 @@
#include "base/memory/weak_ptr.h"
#include "components/web_resource/resource_request_allowed_notifier.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher_delegate.h"
#include "url/gurl.h"
class PrefService;
@@ -26,18 +25,16 @@ class DictionaryValue;
class Value;
}
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
+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 net::URLFetcherDelegate,
- public ResourceRequestAllowedNotifier::Observer {
+class WebResourceService : public ResourceRequestAllowedNotifier::Observer {
public:
// Callbacks for JSON parsing.
using SuccessCallback = base::Callback<void(std::unique_ptr<base::Value>)>;
@@ -55,7 +52,7 @@ class WebResourceService
const char* last_update_time_pref_name,
int start_fetch_delay_ms,
int cache_update_delay_ms,
- net::URLRequestContextGetter* request_context,
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const char* disable_network_switch,
const ParseJSONCallback& parse_json_callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation);
@@ -81,8 +78,8 @@ class WebResourceService
// For the subclasses to process the result of a fetch.
virtual void Unpack(const base::DictionaryValue& parsed_json) = 0;
- // net::URLFetcherDelegate implementation:
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ // 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);
@@ -112,8 +109,8 @@ class WebResourceService
// point in time.
bool fetch_scheduled_;
- // The tool that fetches the url data from the server.
- std::unique_ptr<net::URLFetcher> url_fetcher_;
+ // 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
@@ -136,8 +133,8 @@ class WebResourceService
// different for different builds of Chrome.
int cache_update_delay_ms_;
- // The request context for the resource fetch.
- scoped_refptr<net::URLRequestContextGetter> request_context_;
+ // The URL loader factory for the resource load.
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// Callback used to parse JSON.
ParseJSONCallback parse_json_callback_;
diff --git a/chromium/components/web_resource/web_resource_service_unittest.cc b/chromium/components/web_resource/web_resource_service_unittest.cc
index 79d08c226e8..b12caae5095 100644
--- a/chromium/components/web_resource/web_resource_service_unittest.cc
+++ b/chromium/components/web_resource/web_resource_service_unittest.cc
@@ -13,10 +13,9 @@
#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 "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_status.h"
-#include "net/url_request/url_request_test_util.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_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -53,22 +52,23 @@ class TestResourceRequestAllowedNotifier
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,
- net::URLRequestContextGetter* request_context,
- const char* disable_network_switch,
- const ParseJSONCallback& parse_json_callback)
+ 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,
+ const ParseJSONCallback& parse_json_callback)
: WebResourceService(prefs,
web_resource_server,
application_locale,
last_update_time_pref_name,
start_fetch_delay_ms,
cache_update_delay_ms,
- request_context,
+ url_loader_factory,
disable_network_switch,
parse_json_callback,
TRAFFIC_ANNOTATION_FOR_TESTS){};
@@ -81,13 +81,14 @@ class WebResourceServiceTest : public testing::Test {
WebResourceServiceTest() {}
void SetUp() override {
- request_context_getter_ = new net::TestURLRequestContextGetter(
- base::ThreadTaskRunnerHandle::Get());
+ test_shared_loader_factory_ =
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_);
local_state_.reset(new TestingPrefServiceSimple());
local_state_->registry()->RegisterStringPref(kCacheUpdatePath, "0");
test_web_resource_service_.reset(new TestWebResourceService(
local_state_.get(), GURL(kTestUrl), "", kCacheUpdatePath.c_str(), 100,
- 5000, request_context_getter_.get(), nullptr,
+ 5000, test_shared_loader_factory_, nullptr,
base::Bind(web_resource::WebResourceServiceTest::Parse)));
error_message_ = "";
TestResourceRequestAllowedNotifier* notifier =
@@ -127,8 +128,9 @@ class WebResourceServiceTest : public testing::Test {
void CallStartFetch() { test_web_resource_service_->StartFetch(); }
private:
- base::MessageLoop message_loop_; // needed for TestURLFetcherFactory
- scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+ base::MessageLoop message_loop_;
+ 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_;
};
diff --git a/chromium/components/webcrypto/BUILD.gn b/chromium/components/webcrypto/BUILD.gn
index caecce67af3..1c092f13cb5 100644
--- a/chromium/components/webcrypto/BUILD.gn
+++ b/chromium/components/webcrypto/BUILD.gn
@@ -109,7 +109,7 @@ source_set("fuzzer_support") {
"//base",
"//crypto",
"//crypto:platform",
- "//mojo/edk",
+ "//mojo/core/embedder",
"//third_party/blink/public:blink",
]
}
diff --git a/chromium/components/webcrypto/DEPS b/chromium/components/webcrypto/DEPS
index f256c178f03..0f1fc2b2b01 100644
--- a/chromium/components/webcrypto/DEPS
+++ b/chromium/components/webcrypto/DEPS
@@ -1,6 +1,6 @@
include_rules = [
"+crypto",
- "+mojo/edk/embedder",
+ "+mojo/core/embedder",
"+third_party/boringssl/src/include",
"+third_party/re2",
"+third_party/blink/public/platform",
diff --git a/chromium/components/webcrypto/algorithms/ec.cc b/chromium/components/webcrypto/algorithms/ec.cc
index b6090c7bbd5..47d4605ebfd 100644
--- a/chromium/components/webcrypto/algorithms/ec.cc
+++ b/chromium/components/webcrypto/algorithms/ec.cc
@@ -678,6 +678,9 @@ Status EcAlgorithm::DeserializeKeyForClone(
return Status::ErrorUnexpected();
}
+ if (!status.IsSuccess())
+ return status;
+
// There is some duplicated information in the serialized format used by
// structured clone (since the KeyAlgorithm is serialized separately from the
// key data). Use this extra information to further validate what was
diff --git a/chromium/components/webcrypto/algorithms/ecdsa_unittest.cc b/chromium/components/webcrypto/algorithms/ecdsa_unittest.cc
index bb04676a609..b7d30c45994 100644
--- a/chromium/components/webcrypto/algorithms/ecdsa_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/ecdsa_unittest.cc
@@ -117,10 +117,10 @@ TEST_F(WebCryptoEcdsaTest, SignatureIsRandom) {
std::unique_ptr<base::DictionaryValue> key_jwk_copy(key_jwk->DeepCopy());
key_jwk_copy->Remove("d", nullptr);
blink::WebCryptoKey public_key;
- ASSERT_EQ(Status::Success(),
- ImportKeyJwkFromDict(*key_jwk_copy.get(),
- CreateEcdsaImportAlgorithm(curve), true,
- blink::kWebCryptoKeyUsageVerify, &public_key));
+ ASSERT_EQ(
+ Status::Success(),
+ ImportKeyJwkFromDict(*key_jwk_copy, CreateEcdsaImportAlgorithm(curve),
+ true, blink::kWebCryptoKeyUsageVerify, &public_key));
// Sign twice
std::vector<uint8_t> message(10);
diff --git a/chromium/components/webcrypto/algorithms/rsa_oaep_unittest.cc b/chromium/components/webcrypto/algorithms/rsa_oaep_unittest.cc
index e3156464e35..dcfff3465ee 100644
--- a/chromium/components/webcrypto/algorithms/rsa_oaep_unittest.cc
+++ b/chromium/components/webcrypto/algorithms/rsa_oaep_unittest.cc
@@ -72,7 +72,7 @@ TEST_F(WebCryptoRsaOaepTest, ImportPublicJwkWithNoAlg) {
ASSERT_EQ(
Status::Success(),
ImportKeyJwkFromDict(
- *jwk.get(),
+ *jwk,
CreateRsaHashedImportAlgorithm(blink::kWebCryptoAlgorithmIdRsaOaep,
blink::kWebCryptoAlgorithmIdSha1),
true, blink::kWebCryptoKeyUsageEncrypt, &public_key));
@@ -86,7 +86,7 @@ TEST_F(WebCryptoRsaOaepTest, ImportPublicJwkWithMatchingAlg) {
ASSERT_EQ(
Status::Success(),
ImportKeyJwkFromDict(
- *jwk.get(),
+ *jwk,
CreateRsaHashedImportAlgorithm(blink::kWebCryptoAlgorithmIdRsaOaep,
blink::kWebCryptoAlgorithmIdSha1),
true, blink::kWebCryptoKeyUsageEncrypt, &public_key));
@@ -100,7 +100,7 @@ TEST_F(WebCryptoRsaOaepTest, ImportPublicJwkWithMismatchedAlgFails) {
ASSERT_EQ(
Status::ErrorJwkAlgorithmInconsistent(),
ImportKeyJwkFromDict(
- *jwk.get(),
+ *jwk,
CreateRsaHashedImportAlgorithm(blink::kWebCryptoAlgorithmIdRsaOaep,
blink::kWebCryptoAlgorithmIdSha1),
true, blink::kWebCryptoKeyUsageEncrypt, &public_key));
@@ -115,7 +115,7 @@ TEST_F(WebCryptoRsaOaepTest, ImportPublicJwkWithMismatchedTypeFails) {
ASSERT_EQ(
Status::ErrorJwkUnexpectedKty("RSA"),
ImportKeyJwkFromDict(
- *jwk.get(),
+ *jwk,
CreateRsaHashedImportAlgorithm(blink::kWebCryptoAlgorithmIdRsaOaep,
blink::kWebCryptoAlgorithmIdSha1),
true, blink::kWebCryptoKeyUsageEncrypt, &public_key));
@@ -140,7 +140,7 @@ TEST_F(WebCryptoRsaOaepTest, ExportPublicJwk) {
blink::WebCryptoKey public_key;
ASSERT_EQ(Status::Success(),
ImportKeyJwkFromDict(
- *jwk.get(),
+ *jwk,
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaOaep, test_data.hash_alg),
true, blink::kWebCryptoKeyUsageEncrypt, &public_key));
@@ -213,7 +213,7 @@ TEST_F(WebCryptoRsaOaepTest, EncryptWithLargeMessageFails) {
blink::WebCryptoKey public_key;
ASSERT_EQ(Status::Success(),
ImportKeyJwkFromDict(
- *jwk.get(),
+ *jwk,
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaOaep, kHash),
true, blink::kWebCryptoKeyUsageEncrypt, &public_key));
@@ -269,7 +269,7 @@ TEST_F(WebCryptoRsaOaepTest, EncryptWithLargeDigestFails) {
blink::WebCryptoKey public_key;
ASSERT_EQ(Status::Success(),
ImportKeyJwkFromDict(
- *jwk.get(),
+ *jwk,
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaOaep, kHash),
true, blink::kWebCryptoKeyUsageEncrypt, &public_key));
diff --git a/chromium/components/webcrypto/algorithms/test_helpers.cc b/chromium/components/webcrypto/algorithms/test_helpers.cc
index 82b151435c3..c50004a7f54 100644
--- a/chromium/components/webcrypto/algorithms/test_helpers.cc
+++ b/chromium/components/webcrypto/algorithms/test_helpers.cc
@@ -163,7 +163,7 @@ std::vector<uint8_t> MakeJsonVector(const base::DictionaryValue& dict) {
// Parse the JSON to a dictionary.
*value = base::JSONReader::Read(file_contents);
- if (!value->get()) {
+ if (!*value) {
return ::testing::AssertionFailure()
<< "Couldn't parse test file JSON: " << file_path.value();
}
@@ -453,7 +453,7 @@ std::unique_ptr<base::DictionaryValue> GetJwkDictionary(
const std::string& k_expected_hex,
blink::WebCryptoKeyUsageMask use_mask_expected) {
std::unique_ptr<base::DictionaryValue> dict = GetJwkDictionary(json);
- if (!dict.get() || dict->empty())
+ if (!dict || dict->empty())
return ::testing::AssertionFailure() << "JSON parsing failed";
// ---- k
@@ -480,7 +480,7 @@ std::unique_ptr<base::DictionaryValue> GetJwkDictionary(
const std::string& e_expected_hex,
blink::WebCryptoKeyUsageMask use_mask_expected) {
std::unique_ptr<base::DictionaryValue> dict = GetJwkDictionary(json);
- if (!dict.get() || dict->empty())
+ if (!dict || dict->empty())
return ::testing::AssertionFailure() << "JSON parsing failed";
// ---- n
diff --git a/chromium/components/webcrypto/fuzzer_support.cc b/chromium/components/webcrypto/fuzzer_support.cc
index 107d742c64a..a68a4caa6e8 100644
--- a/chromium/components/webcrypto/fuzzer_support.cc
+++ b/chromium/components/webcrypto/fuzzer_support.cc
@@ -11,7 +11,7 @@
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
-#include "mojo/edk/embedder/embedder.h"
+#include "mojo/core/embedder/embedder.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
#include "third_party/blink/public/web/blink.h"
@@ -25,7 +25,7 @@ class InitOnce : public blink::Platform {
public:
InitOnce() {
base::CommandLine::Init(0, nullptr);
- mojo::edk::Init();
+ mojo::core::Init();
blink::Platform::Initialize(this);
}
~InitOnce() override {}
diff --git a/chromium/components/webcrypto/jwk.cc b/chromium/components/webcrypto/jwk.cc
index b7024b7e5ab..bc78dca9008 100644
--- a/chromium/components/webcrypto/jwk.cc
+++ b/chromium/components/webcrypto/jwk.cc
@@ -200,7 +200,7 @@ Status JwkReader::Init(const CryptoData& bytes,
std::unique_ptr<base::Value> value = base::JSONReader::Read(json_string);
base::DictionaryValue* dict_value = nullptr;
- if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value)
+ if (!value || !value->GetAsDictionary(&dict_value) || !dict_value)
return Status::ErrorJwkNotDictionary();
// Release |value|, as ownership will be transferred to |dict| via
diff --git a/chromium/components/webdata/common/BUILD.gn b/chromium/components/webdata/common/BUILD.gn
index 00b2973c5bd..76bad0c8dbe 100644
--- a/chromium/components/webdata/common/BUILD.gn
+++ b/chromium/components/webdata/common/BUILD.gn
@@ -64,6 +64,7 @@ bundle_data("unit_tests_bundle_data") {
"//components/test/data/web_database/version_74.sql",
"//components/test/data/web_database/version_75.sql",
"//components/test/data/web_database/version_76.sql",
+ "//components/test/data/web_database/version_77.sql",
]
outputs = [
"{{bundle_resources_dir}}/" +
diff --git a/chromium/components/webdata/common/web_data_service_base.cc b/chromium/components/webdata/common/web_data_service_base.cc
index 75323b6b707..00f0fb228a2 100644
--- a/chromium/components/webdata/common/web_data_service_base.cc
+++ b/chromium/components/webdata/common/web_data_service_base.cc
@@ -30,38 +30,38 @@ WebDataServiceBase::WebDataServiceBase(
void WebDataServiceBase::ShutdownOnUISequence() {}
void WebDataServiceBase::Init() {
- DCHECK(wdbs_.get());
+ DCHECK(wdbs_);
wdbs_->RegisterDBErrorCallback(profile_error_callback_);
wdbs_->LoadDatabase();
}
void WebDataServiceBase::ShutdownDatabase() {
- if (!wdbs_.get())
+ if (!wdbs_)
return;
wdbs_->ShutdownDatabase();
}
void WebDataServiceBase::CancelRequest(Handle h) {
- if (!wdbs_.get())
+ if (!wdbs_)
return;
wdbs_->CancelRequest(h);
}
bool WebDataServiceBase::IsDatabaseLoaded() {
- if (!wdbs_.get())
+ if (!wdbs_)
return false;
return wdbs_->db_loaded();
}
void WebDataServiceBase::RegisterDBLoadedCallback(
const DBLoadedCallback& callback) {
- if (!wdbs_.get())
+ if (!wdbs_)
return;
wdbs_->RegisterDBLoadedCallback(callback);
}
WebDatabase* WebDataServiceBase::GetDatabase() {
- if (!wdbs_.get())
+ if (!wdbs_)
return nullptr;
return wdbs_->GetDatabaseOnDB();
}
diff --git a/chromium/components/webdata/common/web_database.cc b/chromium/components/webdata/common/web_database.cc
index 1b7f63cf95a..81927b18626 100644
--- a/chromium/components/webdata/common/web_database.cc
+++ b/chromium/components/webdata/common/web_database.cc
@@ -13,13 +13,16 @@
// corresponding changes must happen in the unit tests, and new migration test
// added. See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|.
// static
-const int WebDatabase::kCurrentVersionNumber = 77;
+const int WebDatabase::kCurrentVersionNumber = 78;
const int WebDatabase::kDeprecatedVersionNumber = 51;
+const base::FilePath::CharType WebDatabase::kInMemoryPath[] =
+ FILE_PATH_LITERAL(":memory");
+
namespace {
-const int kCompatibleVersionNumber = 77;
+const int kCompatibleVersionNumber = 78;
// Change the version number and possibly the compatibility version of
// |meta_table_|.
@@ -89,8 +92,10 @@ sql::InitStatus WebDatabase::Init(const base::FilePath& db_name) {
// database while we're running, and this will give somewhat improved perf.
db_.set_exclusive_locking();
- if (!db_.Open(db_name))
+ if ((db_name.value() == kInMemoryPath) ? !db_.OpenInMemory()
+ : !db_.Open(db_name)) {
return sql::INIT_FAILURE;
+ }
// Clobber really old databases.
static_assert(kDeprecatedVersionNumber < kCurrentVersionNumber,
diff --git a/chromium/components/webdata/common/web_database.h b/chromium/components/webdata/common/web_database.h
index 36bae881f6f..30c5a68a756 100644
--- a/chromium/components/webdata/common/web_database.h
+++ b/chromium/components/webdata/common/web_database.h
@@ -8,6 +8,7 @@
#include <map>
#include <string>
+#include "base/files/file_path.h"
#include "base/macros.h"
#include "components/webdata/common/web_database_table.h"
#include "components/webdata/common/webdata_export.h"
@@ -15,10 +16,6 @@
#include "sql/init_status.h"
#include "sql/meta_table.h"
-namespace base {
-class FilePath;
-}
-
// This class manages a SQLite database that stores various web page meta data.
class WEBDATA_EXPORT WebDatabase {
public:
@@ -30,6 +27,8 @@ class WEBDATA_EXPORT WebDatabase {
static const int kCurrentVersionNumber;
// The newest version of the database Chrome will NOT try to migrate.
static const int kDeprecatedVersionNumber;
+ // Use this as a path to create an in-memory database.
+ static const base::FilePath::CharType kInMemoryPath[];
WebDatabase();
virtual ~WebDatabase();
diff --git a/chromium/components/webdata/common/web_database_backend.cc b/chromium/components/webdata/common/web_database_backend.cc
index 1dd14d1b8a1..0ccf50d98a4 100644
--- a/chromium/components/webdata/common/web_database_backend.cc
+++ b/chromium/components/webdata/common/web_database_backend.cc
@@ -30,7 +30,7 @@ WebDatabaseBackend::WebDatabaseBackend(
delegate_(delegate) {}
void WebDatabaseBackend::AddTable(std::unique_ptr<WebDatabaseTable> table) {
- DCHECK(!db_.get());
+ DCHECK(!db_);
tables_.push_back(std::move(table));
}
diff --git a/chromium/components/webdata/common/web_database_migration_unittest.cc b/chromium/components/webdata/common/web_database_migration_unittest.cc
index 30cca264e69..47ba2bc1a55 100644
--- a/chromium/components/webdata/common/web_database_migration_unittest.cc
+++ b/chromium/components/webdata/common/web_database_migration_unittest.cc
@@ -129,7 +129,7 @@ class WebDatabaseMigrationTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(WebDatabaseMigrationTest);
};
-const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 77;
+const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 78;
void WebDatabaseMigrationTest::LoadDatabase(
const base::FilePath::StringType& file) {
@@ -1547,3 +1547,68 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion76ToCurrent) {
EXPECT_EQ(11644474389000000, s.ColumnInt64(3));
}
}
+
+// Tests adding model_type columns into autofill_sync_metadata and
+// autofill_model_type_state.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion77ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_77.sql")));
+
+ // Verify pre-conditions.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, 77, 77));
+
+ sql::Statement s1(connection.GetUniqueStatement(
+ "SELECT storage_key, value FROM autofill_sync_metadata"));
+ ASSERT_TRUE(s1.Step());
+ EXPECT_EQ("storage_key1", s1.ColumnString(0));
+ EXPECT_EQ("blob1", s1.ColumnString(1));
+ ASSERT_TRUE(s1.Step());
+ EXPECT_EQ("storage_key2", s1.ColumnString(0));
+ EXPECT_EQ("blob2", s1.ColumnString(1));
+
+ sql::Statement s2(connection.GetUniqueStatement(
+ "SELECT id, value FROM autofill_model_type_state"));
+ ASSERT_TRUE(s2.Step());
+ EXPECT_EQ(1, s2.ColumnInt(0));
+ EXPECT_EQ("state", s2.ColumnString(1));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // Check that the AUTOFILL metadata is still there.
+ sql::Statement s1(connection.GetUniqueStatement(
+ "SELECT model_type, storage_key, value FROM autofill_sync_metadata"));
+ ASSERT_TRUE(s1.Step());
+ EXPECT_EQ(syncer::ModelTypeToHistogramInt(syncer::AUTOFILL),
+ s1.ColumnInt(0));
+ EXPECT_EQ("storage_key1", s1.ColumnString(1));
+ EXPECT_EQ("blob1", s1.ColumnString(2));
+ ASSERT_TRUE(s1.Step());
+ EXPECT_EQ(syncer::ModelTypeToHistogramInt(syncer::AUTOFILL),
+ s1.ColumnInt(0));
+ EXPECT_EQ("storage_key2", s1.ColumnString(1));
+ EXPECT_EQ("blob2", s1.ColumnString(2));
+
+ // Check that the AUTOFILL model_type_state is still there.
+ sql::Statement s2(connection.GetUniqueStatement(
+ "SELECT model_type, value FROM autofill_model_type_state"));
+ ASSERT_TRUE(s2.Step());
+ EXPECT_EQ(syncer::ModelTypeToHistogramInt(syncer::AUTOFILL),
+ s2.ColumnInt(0));
+ EXPECT_EQ("state", s2.ColumnString(1));
+ }
+}
diff --git a/chromium/components/webdata/common/web_database_service.cc b/chromium/components/webdata/common/web_database_service.cc
index c7f41fe5a47..7948b1851cb 100644
--- a/chromium/components/webdata/common/web_database_service.cc
+++ b/chromium/components/webdata/common/web_database_service.cc
@@ -48,14 +48,14 @@ WebDatabaseService::WebDatabaseService(
db_task_runner_(db_task_runner),
weak_ptr_factory_(this) {
DCHECK(ui_task_runner->RunsTasksInCurrentSequence());
- DCHECK(db_task_runner_.get());
+ DCHECK(db_task_runner_);
}
WebDatabaseService::~WebDatabaseService() {
}
void WebDatabaseService::AddTable(std::unique_ptr<WebDatabaseTable> table) {
- if (!web_db_backend_.get()) {
+ if (!web_db_backend_) {
web_db_backend_ = new WebDatabaseBackend(
path_, new BackendDelegate(weak_ptr_factory_.GetWeakPtr()),
db_task_runner_);
@@ -64,7 +64,7 @@ void WebDatabaseService::AddTable(std::unique_ptr<WebDatabaseTable> table) {
}
void WebDatabaseService::LoadDatabase() {
- DCHECK(web_db_backend_.get());
+ DCHECK(web_db_backend_);
db_task_runner_->PostTask(
FROM_HERE, Bind(&WebDatabaseBackend::InitDatabase, web_db_backend_));
}
@@ -74,7 +74,7 @@ void WebDatabaseService::ShutdownDatabase() {
loaded_callbacks_.clear();
error_callbacks_.clear();
weak_ptr_factory_.InvalidateWeakPtrs();
- if (!web_db_backend_.get())
+ if (!web_db_backend_)
return;
db_task_runner_->PostTask(
FROM_HERE, Bind(&WebDatabaseBackend::ShutdownDatabase, web_db_backend_));
@@ -82,7 +82,7 @@ void WebDatabaseService::ShutdownDatabase() {
WebDatabase* WebDatabaseService::GetDatabaseOnDB() const {
DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
- return web_db_backend_.get() ? web_db_backend_->database() : nullptr;
+ return web_db_backend_ ? web_db_backend_->database() : nullptr;
}
scoped_refptr<WebDatabaseBackend> WebDatabaseService::GetBackend() const {
@@ -91,7 +91,7 @@ scoped_refptr<WebDatabaseBackend> WebDatabaseService::GetBackend() const {
void WebDatabaseService::ScheduleDBTask(const base::Location& from_here,
const WriteTask& task) {
- DCHECK(web_db_backend_.get());
+ DCHECK(web_db_backend_);
std::unique_ptr<WebDataRequest> request =
web_db_backend_->request_manager()->NewRequest(nullptr);
db_task_runner_->PostTask(
@@ -104,7 +104,7 @@ WebDataServiceBase::Handle WebDatabaseService::ScheduleDBTaskWithResult(
const ReadTask& task,
WebDataServiceConsumer* consumer) {
DCHECK(consumer);
- DCHECK(web_db_backend_.get());
+ DCHECK(web_db_backend_);
std::unique_ptr<WebDataRequest> request =
web_db_backend_->request_manager()->NewRequest(consumer);
WebDataServiceBase::Handle handle = request->GetHandle();
@@ -115,7 +115,7 @@ WebDataServiceBase::Handle WebDatabaseService::ScheduleDBTaskWithResult(
}
void WebDatabaseService::CancelRequest(WebDataServiceBase::Handle h) {
- if (!web_db_backend_.get())
+ if (!web_db_backend_)
return;
web_db_backend_->request_manager()->CancelRequest(h);
}
diff --git a/chromium/components/webdata_services/web_data_service_test_util.cc b/chromium/components/webdata_services/web_data_service_test_util.cc
index e013f323fca..6cd460fa757 100644
--- a/chromium/components/webdata_services/web_data_service_test_util.cc
+++ b/chromium/components/webdata_services/web_data_service_test_util.cc
@@ -30,10 +30,16 @@ MockWebDataServiceWrapper::~MockWebDataServiceWrapper() {
}
scoped_refptr<AutofillWebDataService>
-MockWebDataServiceWrapper::GetAutofillWebData() {
+MockWebDataServiceWrapper::GetProfileAutofillWebData() {
return fake_autofill_web_data_;
}
+scoped_refptr<AutofillWebDataService>
+MockWebDataServiceWrapper::GetAccountAutofillWebData() {
+ // TODO(feuunk): Implement when there are tests covering account data.
+ return nullptr;
+}
+
scoped_refptr<TokenWebData> MockWebDataServiceWrapper::GetTokenWebData() {
return fake_token_web_data_;
}
diff --git a/chromium/components/webdata_services/web_data_service_test_util.h b/chromium/components/webdata_services/web_data_service_test_util.h
index 3d758b752d1..9f5f6d48a46 100644
--- a/chromium/components/webdata_services/web_data_service_test_util.h
+++ b/chromium/components/webdata_services/web_data_service_test_util.h
@@ -32,7 +32,11 @@ class MockWebDataServiceWrapper : public MockWebDataServiceWrapperBase {
~MockWebDataServiceWrapper() override;
- scoped_refptr<autofill::AutofillWebDataService> GetAutofillWebData() override;
+ scoped_refptr<autofill::AutofillWebDataService> GetProfileAutofillWebData()
+ override;
+
+ scoped_refptr<autofill::AutofillWebDataService> GetAccountAutofillWebData()
+ override;
scoped_refptr<TokenWebData> GetTokenWebData() override;
diff --git a/chromium/components/webdata_services/web_data_service_wrapper.cc b/chromium/components/webdata_services/web_data_service_wrapper.cc
index 6f6d495a4cc..bc66797ee73 100644
--- a/chromium/components/webdata_services/web_data_service_wrapper.cc
+++ b/chromium/components/webdata_services/web_data_service_wrapper.cc
@@ -14,6 +14,7 @@
#include "base/task_scheduler/post_task.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
+#include "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h"
#include "components/autofill/core/browser/webdata/autofill_profile_syncable_service.h"
#include "components/autofill/core/browser/webdata/autofill_table.h"
#include "components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.h"
@@ -40,6 +41,9 @@
namespace {
+// TODO(jkrcal): Rename this function when the last webdata sync type get
+// converted to USS, e.g. to InitSyncBridgesOnDBSequence(). Check also other
+// related functions.
void InitSyncableServicesOnDBSequence(
scoped_refptr<base::SingleThreadTaskRunner> db_task_runner,
const syncer::SyncableService::StartSyncFlare& sync_flare,
@@ -54,17 +58,22 @@ void InitSyncableServicesOnDBSequence(
autofill::AutocompleteSyncBridge::CreateForWebDataServiceAndBackend(
autofill_web_data.get(), autofill_backend);
- autofill::AutofillProfileSyncableService::CreateForWebDataServiceAndBackend(
- autofill_web_data.get(), autofill_backend, app_locale);
+ if (base::FeatureList::IsEnabled(switches::kSyncUSSAutofillProfile)) {
+ autofill::AutofillProfileSyncBridge::CreateForWebDataServiceAndBackend(
+ app_locale, autofill_backend, autofill_web_data.get());
+ } else {
+ autofill::AutofillProfileSyncableService::CreateForWebDataServiceAndBackend(
+ autofill_web_data.get(), autofill_backend, app_locale);
+ autofill::AutofillProfileSyncableService::FromWebDataService(
+ autofill_web_data.get())
+ ->InjectStartSyncFlare(sync_flare);
+ }
+
autofill::AutofillWalletSyncableService::CreateForWebDataServiceAndBackend(
autofill_web_data.get(), autofill_backend, app_locale);
autofill::AutofillWalletMetadataSyncableService::
CreateForWebDataServiceAndBackend(autofill_web_data.get(),
autofill_backend, app_locale);
-
- autofill::AutofillProfileSyncableService::FromWebDataService(
- autofill_web_data.get())
- ->InjectStartSyncFlare(sync_flare);
autofill::AutofillWalletSyncableService::FromWebDataService(
autofill_web_data.get())
->InjectStartSyncFlare(sync_flare);
@@ -87,63 +96,76 @@ WebDataServiceWrapper::WebDataServiceWrapper(
auto db_task_runner = base::CreateSingleThreadTaskRunnerWithTraits(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
- web_database_ = new WebDatabaseService(path, ui_task_runner, db_task_runner);
+ profile_database_ =
+ new WebDatabaseService(path, ui_task_runner, db_task_runner);
// All tables objects that participate in managing the database must
// be added here.
- web_database_->AddTable(std::make_unique<autofill::AutofillTable>());
- web_database_->AddTable(std::make_unique<KeywordTable>());
+ profile_database_->AddTable(std::make_unique<autofill::AutofillTable>());
+ profile_database_->AddTable(std::make_unique<KeywordTable>());
// TODO(mdm): We only really need the LoginsTable on Windows for IE7 password
// access, but for now, we still create it on all platforms since it deletes
// the old logins table. We can remove this after a while, e.g. in M22 or so.
- web_database_->AddTable(std::make_unique<LoginsTable>());
- web_database_->AddTable(std::make_unique<TokenServiceTable>());
+ profile_database_->AddTable(std::make_unique<LoginsTable>());
+ profile_database_->AddTable(std::make_unique<TokenServiceTable>());
#if !defined(OS_IOS)
- web_database_->AddTable(
+ profile_database_->AddTable(
std::make_unique<payments::PaymentMethodManifestTable>());
- web_database_->AddTable(
+ profile_database_->AddTable(
std::make_unique<payments::WebAppManifestSectionTable>());
#endif
- web_database_->LoadDatabase();
+ profile_database_->LoadDatabase();
- autofill_web_data_ = new autofill::AutofillWebDataService(
- web_database_, ui_task_runner, db_task_runner,
+ profile_autofill_web_data_ = new autofill::AutofillWebDataService(
+ profile_database_, ui_task_runner, db_task_runner,
base::Bind(show_error_callback, ERROR_LOADING_AUTOFILL));
- autofill_web_data_->Init();
+ profile_autofill_web_data_->Init();
keyword_web_data_ = new KeywordWebDataService(
- web_database_, ui_task_runner,
+ profile_database_, ui_task_runner,
base::Bind(show_error_callback, ERROR_LOADING_KEYWORD));
keyword_web_data_->Init();
token_web_data_ =
- new TokenWebData(web_database_, ui_task_runner, db_task_runner,
+ new TokenWebData(profile_database_, ui_task_runner, db_task_runner,
base::Bind(show_error_callback, ERROR_LOADING_TOKEN));
token_web_data_->Init();
#if defined(OS_WIN)
password_web_data_ = new PasswordWebDataService(
- web_database_, ui_task_runner,
+ profile_database_, ui_task_runner,
base::Bind(show_error_callback, ERROR_LOADING_PASSWORD));
password_web_data_->Init();
#endif
#if !defined(OS_IOS)
payment_manifest_web_data_ = new payments::PaymentManifestWebDataService(
- web_database_,
+ profile_database_,
base::Bind(show_error_callback, ERROR_LOADING_PAYMENT_MANIFEST),
ui_task_runner);
#endif
- autofill_web_data_->GetAutofillBackend(
+ profile_autofill_web_data_->GetAutofillBackend(
base::Bind(&InitSyncableServicesOnDBSequence, db_task_runner, flare,
- autofill_web_data_, context_path, application_locale));
+ profile_autofill_web_data_, context_path, application_locale));
+
+ account_database_ =
+ new WebDatabaseService(base::FilePath(WebDatabase::kInMemoryPath),
+ ui_task_runner, db_task_runner);
+ account_database_->AddTable(std::make_unique<autofill::AutofillTable>());
+ account_database_->LoadDatabase();
+
+ account_autofill_web_data_ = new autofill::AutofillWebDataService(
+ account_database_, ui_task_runner, db_task_runner,
+ base::Bind(show_error_callback, ERROR_LOADING_ACCOUNT_AUTOFILL));
+ account_autofill_web_data_->Init();
}
WebDataServiceWrapper::~WebDataServiceWrapper() {}
void WebDataServiceWrapper::Shutdown() {
- autofill_web_data_->ShutdownOnUISequence();
+ profile_autofill_web_data_->ShutdownOnUISequence();
+ account_autofill_web_data_->ShutdownOnUISequence();
keyword_web_data_->ShutdownOnUISequence();
token_web_data_->ShutdownOnUISequence();
@@ -155,12 +177,18 @@ void WebDataServiceWrapper::Shutdown() {
payment_manifest_web_data_->ShutdownOnUISequence();
#endif
- web_database_->ShutdownDatabase();
+ profile_database_->ShutdownDatabase();
+ account_database_->ShutdownDatabase();
+}
+
+scoped_refptr<autofill::AutofillWebDataService>
+WebDataServiceWrapper::GetProfileAutofillWebData() {
+ return profile_autofill_web_data_.get();
}
scoped_refptr<autofill::AutofillWebDataService>
-WebDataServiceWrapper::GetAutofillWebData() {
- return autofill_web_data_.get();
+WebDataServiceWrapper::GetAccountAutofillWebData() {
+ return account_autofill_web_data_.get();
}
scoped_refptr<KeywordWebDataService>
diff --git a/chromium/components/webdata_services/web_data_service_wrapper.h b/chromium/components/webdata_services/web_data_service_wrapper.h
index d01c34f0f46..76330da9bd2 100644
--- a/chromium/components/webdata_services/web_data_service_wrapper.h
+++ b/chromium/components/webdata_services/web_data_service_wrapper.h
@@ -45,6 +45,7 @@ class WebDataServiceWrapper : public KeyedService {
// ErrorType indicates which service encountered an error loading its data.
enum ErrorType {
ERROR_LOADING_AUTOFILL,
+ ERROR_LOADING_ACCOUNT_AUTOFILL,
ERROR_LOADING_KEYWORD,
ERROR_LOADING_TOKEN,
ERROR_LOADING_PASSWORD,
@@ -73,6 +74,7 @@ class WebDataServiceWrapper : public KeyedService {
const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
const syncer::SyncableService::StartSyncFlare& flare,
const ShowErrorCallback& show_error_callback);
+
~WebDataServiceWrapper() override;
// KeyedService:
@@ -80,7 +82,10 @@ class WebDataServiceWrapper : public KeyedService {
// Create the various types of service instances. These methods are virtual
// for testing purpose.
- virtual scoped_refptr<autofill::AutofillWebDataService> GetAutofillWebData();
+ virtual scoped_refptr<autofill::AutofillWebDataService>
+ GetProfileAutofillWebData();
+ virtual scoped_refptr<autofill::AutofillWebDataService>
+ GetAccountAutofillWebData();
virtual scoped_refptr<KeywordWebDataService> GetKeywordWebData();
virtual scoped_refptr<TokenWebData> GetTokenWebData();
#if defined(OS_WIN)
@@ -96,9 +101,11 @@ class WebDataServiceWrapper : public KeyedService {
WebDataServiceWrapper();
private:
- scoped_refptr<WebDatabaseService> web_database_;
+ scoped_refptr<WebDatabaseService> profile_database_;
+ scoped_refptr<WebDatabaseService> account_database_;
- scoped_refptr<autofill::AutofillWebDataService> autofill_web_data_;
+ scoped_refptr<autofill::AutofillWebDataService> profile_autofill_web_data_;
+ scoped_refptr<autofill::AutofillWebDataService> account_autofill_web_data_;
scoped_refptr<KeywordWebDataService> keyword_web_data_;
scoped_refptr<TokenWebData> token_web_data_;
diff --git a/chromium/components/wifi/BUILD.gn b/chromium/components/wifi/BUILD.gn
index 392dbac49c7..3cd5593b6f6 100644
--- a/chromium/components/wifi/BUILD.gn
+++ b/chromium/components/wifi/BUILD.gn
@@ -56,7 +56,6 @@ executable("wifi_test") {
deps = [
":wifi",
"//base",
- "//build/config:exe_and_shlib_deps",
"//build/win:default_exe_manifest",
"//components/onc",
]
diff --git a/chromium/components/wifi/network_properties.cc b/chromium/components/wifi/network_properties.cc
index 1e10ff6c2d0..a24899ea1a7 100644
--- a/chromium/components/wifi/network_properties.cc
+++ b/chromium/components/wifi/network_properties.cc
@@ -66,7 +66,7 @@ std::unique_ptr<base::DictionaryValue> NetworkProperties::ToValue(
}
bool NetworkProperties::UpdateFromValue(const base::DictionaryValue& value) {
- const base::DictionaryValue* wifi = NULL;
+ const base::DictionaryValue* wifi = nullptr;
std::string network_type;
// Get network type and make sure that it is WiFi (if specified).
if (value.GetString(onc::network_config::kType, &network_type)) {
diff --git a/chromium/components/wifi/wifi_service_mac.mm b/chromium/components/wifi/wifi_service_mac.mm
index cc003b2c456..77e56131ce7 100644
--- a/chromium/components/wifi/wifi_service_mac.mm
+++ b/chromium/components/wifi/wifi_service_mac.mm
@@ -364,14 +364,9 @@ void WiFiServiceMac::GetKeyFromSystem(const std::string& network_guid,
UInt32 password_length = 0;
void *password_data = NULL;
crypto::AppleKeychain keychain;
- OSStatus status = keychain.FindGenericPassword(NULL,
- strlen(kAirPortServiceName),
- kAirPortServiceName,
- network_guid.length(),
- network_guid.c_str(),
- &password_length,
- &password_data,
- NULL);
+ OSStatus status = keychain.FindGenericPassword(
+ strlen(kAirPortServiceName), kAirPortServiceName, network_guid.length(),
+ network_guid.c_str(), &password_length, &password_data, NULL);
if (status != errSecSuccess) {
*error = kErrorNotFound;
return;
@@ -380,7 +375,7 @@ void WiFiServiceMac::GetKeyFromSystem(const std::string& network_guid,
if (password_data) {
*key_data = std::string(reinterpret_cast<char*>(password_data),
password_length);
- keychain.ItemFreeContent(NULL, password_data);
+ keychain.ItemFreeContent(password_data);
}
}
diff --git a/chromium/components/wifi/wifi_service_win.cc b/chromium/components/wifi/wifi_service_win.cc
index 5cf74405771..9dc82c0c826 100644
--- a/chromium/components/wifi/wifi_service_win.cc
+++ b/chromium/components/wifi/wifi_service_win.cc
@@ -487,24 +487,23 @@ class WiFiServiceImpl : public WiFiService {
};
WiFiServiceImpl::WiFiServiceImpl()
- : wlan_api_library_(NULL),
- WlanConnect_function_(NULL),
- WlanCloseHandle_function_(NULL),
- WlanDeleteProfile_function_(NULL),
- WlanDisconnect_function_(NULL),
- WlanEnumInterfaces_function_(NULL),
- WlanFreeMemory_function_(NULL),
- WlanGetAvailableNetworkList_function_(NULL),
- WlanGetNetworkBssList_function_(NULL),
- WlanGetProfile_function_(NULL),
- WlanOpenHandle_function_(NULL),
- WlanRegisterNotification_function_(NULL),
- WlanScan_function_(NULL),
- WlanSetProfile_function_(NULL),
- WlanSaveTemporaryProfile_function_(NULL),
- client_(NULL),
- enable_notify_network_changed_(true) {
-}
+ : wlan_api_library_(nullptr),
+ WlanConnect_function_(nullptr),
+ WlanCloseHandle_function_(nullptr),
+ WlanDeleteProfile_function_(nullptr),
+ WlanDisconnect_function_(nullptr),
+ WlanEnumInterfaces_function_(nullptr),
+ WlanFreeMemory_function_(nullptr),
+ WlanGetAvailableNetworkList_function_(nullptr),
+ WlanGetNetworkBssList_function_(nullptr),
+ WlanGetProfile_function_(nullptr),
+ WlanOpenHandle_function_(nullptr),
+ WlanRegisterNotification_function_(nullptr),
+ WlanScan_function_(nullptr),
+ WlanSetProfile_function_(nullptr),
+ WlanSaveTemporaryProfile_function_(nullptr),
+ client_(nullptr),
+ enable_notify_network_changed_(true) {}
WiFiServiceImpl::~WiFiServiceImpl() { UnInitialize(); }
@@ -571,7 +570,7 @@ void WiFiServiceImpl::SetProperties(
std::string* error) {
// Temporary preserve WiFi properties (desired frequency, wifi password) to
// use in StartConnect.
- DCHECK(properties.get());
+ DCHECK(properties);
if (!properties->HasKey(onc::network_type::kWiFi)) {
DVLOG(0) << "Missing WiFi properties:" << *properties;
*error = kErrorWiFiService;
@@ -670,7 +669,7 @@ void WiFiServiceImpl::GetVisibleNetworks(const std::string& network_type,
void WiFiServiceImpl::RequestNetworkScan() {
DWORD error = EnsureInitialized();
if (error == ERROR_SUCCESS) {
- WlanScan_function_(client_, &interface_guid_, NULL, NULL, NULL);
+ WlanScan_function_(client_, &interface_guid_, nullptr, nullptr, nullptr);
}
}
@@ -800,26 +799,18 @@ void WiFiServiceImpl::SetEventObservers(
if (!networks_changed_observer_.is_null() ||
!network_list_changed_observer_.is_null()) {
// Stop listening to WLAN notifications.
- WlanRegisterNotification_function_(client_,
- WLAN_NOTIFICATION_SOURCE_NONE,
- FALSE,
- OnWlanNotificationCallback,
- this,
- NULL,
- NULL);
+ WlanRegisterNotification_function_(client_, WLAN_NOTIFICATION_SOURCE_NONE,
+ FALSE, OnWlanNotificationCallback, this,
+ nullptr, nullptr);
}
networks_changed_observer_ = networks_changed_observer;
network_list_changed_observer_ = network_list_changed_observer;
if (!networks_changed_observer_.is_null() ||
!network_list_changed_observer_.is_null()) {
// Start listening to WLAN notifications.
- WlanRegisterNotification_function_(client_,
- WLAN_NOTIFICATION_SOURCE_ALL,
- FALSE,
- OnWlanNotificationCallback,
- this,
- NULL,
- NULL);
+ WlanRegisterNotification_function_(client_, WLAN_NOTIFICATION_SOURCE_ALL,
+ FALSE, OnWlanNotificationCallback, this,
+ nullptr, nullptr);
}
}
@@ -844,7 +835,7 @@ void WiFiServiceImpl::OnWlanNotificationCallback(
void WiFiServiceImpl::OnWlanNotification(
PWLAN_NOTIFICATION_DATA wlan_notification_data) {
- if (event_task_runner_.get() == NULL)
+ if (!event_task_runner_)
return;
switch (wlan_notification_data->NotificationCode) {
case wlan_notification_acm_disconnected:
@@ -887,7 +878,7 @@ void WiFiServiceImpl::WaitForNetworkConnect(const std::string& network_guid,
LOG(ERROR) << kMaxAttempts << " attempts exceeded waiting for connect to "
<< network_guid;
- base::DictionaryValue* created_profile = NULL;
+ base::DictionaryValue* created_profile = nullptr;
// Check, whether this connection is using newly created profile.
if (created_profiles_.GetDictionaryWithoutPathExpansion(
network_guid, &created_profile)) {
@@ -898,8 +889,8 @@ void WiFiServiceImpl::WaitForNetworkConnect(const std::string& network_guid,
if (created_profile->GetString(kProfileXmlKey, &tkip_profile_xml) &&
created_profile->GetBoolean(kProfileSharedKey, &shared)) {
// Remove TKIP profile xml, so it will not be tried again.
- created_profile->Remove(kProfileXmlKey, NULL);
- created_profile->Remove(kProfileSharedKey, NULL);
+ created_profile->Remove(kProfileXmlKey, nullptr);
+ created_profile->Remove(kProfileSharedKey, nullptr);
DWORD error_code = SetProfile(shared, tkip_profile_xml, true);
if (error_code == ERROR_SUCCESS) {
// Try to connect with new profile.
@@ -941,7 +932,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_.RemoveWithoutPathExpansion(network_guid, NULL);
+ created_profiles_.RemoveWithoutPathExpansion(network_guid, nullptr);
// Restore previously suppressed notifications.
enable_notify_network_changed_ = true;
RestoreNwCategoryWizard();
@@ -1004,8 +995,7 @@ DWORD WiFiServiceImpl::LoadWlanLibrary() {
return ERROR_NOT_FOUND;
}
wlan_api_library_ = ::LoadLibraryEx(path.Append(kWlanApiDll).value().c_str(),
- NULL,
- LOAD_WITH_ALTERED_SEARCH_PATH);
+ nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
if (!wlan_api_library_) {
LOG(ERROR) << "Unable to load WlanApi.dll.";
return ERROR_NOT_FOUND;
@@ -1073,7 +1063,7 @@ DWORD WiFiServiceImpl::LoadWlanLibrary() {
!WlanSetProfile_function_) {
LOG(ERROR) << "Unable to find required WlanApi function.";
FreeLibrary(wlan_api_library_);
- wlan_api_library_ = NULL;
+ wlan_api_library_ = nullptr;
return ERROR_NOT_FOUND;
}
@@ -1094,14 +1084,14 @@ DWORD WiFiServiceImpl::OpenClientHandle() {
return error;
// Open a handle to the service.
- error = WlanOpenHandle_function_(1, NULL, &service_version, &client_);
+ error = WlanOpenHandle_function_(1, nullptr, &service_version, &client_);
- PWLAN_INTERFACE_INFO_LIST interface_list = NULL;
+ PWLAN_INTERFACE_INFO_LIST interface_list = nullptr;
if (error == ERROR_SUCCESS) {
// Enumerate wireless interfaces.
- error = WlanEnumInterfaces_function_(client_, NULL, &interface_list);
+ error = WlanEnumInterfaces_function_(client_, nullptr, &interface_list);
if (error == ERROR_SUCCESS) {
- if (interface_list != NULL && interface_list->dwNumberOfItems != 0) {
+ if (interface_list && interface_list->dwNumberOfItems != 0) {
// Remember first interface just in case if none are connected.
interface_guid_ = interface_list->InterfaceInfo[0].InterfaceGuid;
// Try to find a connected interface.
@@ -1118,7 +1108,7 @@ DWORD WiFiServiceImpl::OpenClientHandle() {
}
}
// Clean up..
- if (interface_list != NULL)
+ if (interface_list)
WlanFreeMemory_function_(interface_list);
}
return error;
@@ -1154,7 +1144,7 @@ DWORD WiFiServiceImpl::FindAdapterIndexMapByGUID(
interface_guid, base::WriteInto(&guid_string, kGUIDSize), kGUIDSize);
ULONG buffer_length = 0;
- DWORD error = ::GetInterfaceInfo(NULL, &buffer_length);
+ DWORD error = ::GetInterfaceInfo(nullptr, &buffer_length);
if (error == ERROR_INSUFFICIENT_BUFFER) {
std::unique_ptr<unsigned char[]> buffer(new unsigned char[buffer_length]);
IP_INTERFACE_INFO* interface_info =
@@ -1229,34 +1219,34 @@ DWORD WiFiServiceImpl::RestoreNwCategoryWizard() {
}
DWORD WiFiServiceImpl::EnsureInitialized() {
- if (client_ != NULL)
+ if (client_)
return ERROR_SUCCESS;
return ERROR_NOINTERFACE;
}
DWORD WiFiServiceImpl::CloseClientHandle() {
DWORD error = ERROR_SUCCESS;
- if (client_ != NULL) {
- error = WlanCloseHandle_function_(client_, NULL);
- client_ = NULL;
- }
- if (wlan_api_library_ != NULL) {
- WlanConnect_function_ = NULL;
- WlanCloseHandle_function_ = NULL;
- WlanDeleteProfile_function_ = NULL;
- WlanDisconnect_function_ = NULL;
- WlanEnumInterfaces_function_ = NULL;
- WlanFreeMemory_function_ = NULL;
- WlanGetAvailableNetworkList_function_ = NULL;
- WlanGetNetworkBssList_function_ = NULL;
- WlanGetProfile_function_ = NULL;
- WlanOpenHandle_function_ = NULL;
- WlanRegisterNotification_function_ = NULL;
- WlanSaveTemporaryProfile_function_ = NULL;
- WlanScan_function_ = NULL;
- WlanSetProfile_function_ = NULL;
+ if (client_) {
+ error = WlanCloseHandle_function_(client_, nullptr);
+ client_ = nullptr;
+ }
+ if (wlan_api_library_) {
+ WlanConnect_function_ = nullptr;
+ WlanCloseHandle_function_ = nullptr;
+ WlanDeleteProfile_function_ = nullptr;
+ WlanDisconnect_function_ = nullptr;
+ WlanEnumInterfaces_function_ = nullptr;
+ WlanFreeMemory_function_ = nullptr;
+ WlanGetAvailableNetworkList_function_ = nullptr;
+ WlanGetNetworkBssList_function_ = nullptr;
+ WlanGetProfile_function_ = nullptr;
+ WlanOpenHandle_function_ = nullptr;
+ WlanRegisterNotification_function_ = nullptr;
+ WlanSaveTemporaryProfile_function_ = nullptr;
+ WlanScan_function_ = nullptr;
+ WlanSetProfile_function_ = nullptr;
::FreeLibrary(wlan_api_library_);
- wlan_api_library_ = NULL;
+ wlan_api_library_ = nullptr;
}
return error;
}
@@ -1356,20 +1346,15 @@ void WiFiServiceImpl::UpdateNetworkPropertiesFromBssList(
// Get the list of visible wireless networks
DWORD WiFiServiceImpl::GetVisibleNetworkList(NetworkList* network_list) {
- if (client_ == NULL) {
- NOTREACHED();
- return ERROR_NOINTERFACE;
- }
+ DCHECK(client_);
DWORD error = ERROR_SUCCESS;
- PWLAN_AVAILABLE_NETWORK_LIST available_network_list = NULL;
- PWLAN_BSS_LIST bss_list = NULL;
+ PWLAN_AVAILABLE_NETWORK_LIST available_network_list = nullptr;
+ PWLAN_BSS_LIST bss_list = nullptr;
error = WlanGetAvailableNetworkList_function_(
- client_,
- &interface_guid_,
- WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_MANUAL_HIDDEN_PROFILES,
- NULL,
+ client_, &interface_guid_,
+ WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_MANUAL_HIDDEN_PROFILES, nullptr,
&available_network_list);
std::set<std::string> network_guids;
@@ -1380,21 +1365,15 @@ DWORD WiFiServiceImpl::GetVisibleNetworkList(NetworkList* network_list) {
// TODO(mef): WlanGetNetworkBssList is not available on XP. If XP support is
// needed, then different method of getting BSS (e.g. OID query) will have
// to be used.
- error = WlanGetNetworkBssList_function_(client_,
- &interface_guid_,
- NULL,
- dot11_BSS_type_any,
- FALSE,
- NULL,
+ error = WlanGetNetworkBssList_function_(client_, &interface_guid_, nullptr,
+ dot11_BSS_type_any, FALSE, nullptr,
&bss_list);
- if (error == ERROR_SUCCESS && NULL != bss_list) {
+ if (error == ERROR_SUCCESS && bss_list) {
for (DWORD i = 0; i < available_network_list->dwNumberOfItems; ++i) {
NetworkProperties network_properties;
NetworkPropertiesFromAvailableNetwork(
- available_network_list->Network[i],
- &network_properties);
- UpdateNetworkPropertiesFromBssList(network_properties.guid,
- *bss_list,
+ available_network_list->Network[i], &network_properties);
+ UpdateNetworkPropertiesFromBssList(network_properties.guid, *bss_list,
&network_properties);
// Check for duplicate network guids.
if (network_guids.count(network_properties.guid)) {
@@ -1417,41 +1396,33 @@ DWORD WiFiServiceImpl::GetVisibleNetworkList(NetworkList* network_list) {
}
// Clean up.
- if (available_network_list != NULL) {
+ if (available_network_list) {
WlanFreeMemory_function_(available_network_list);
}
- if (bss_list != NULL) {
+ if (bss_list) {
WlanFreeMemory_function_(bss_list);
}
return error;
}
DWORD WiFiServiceImpl::GetCurrentProperties(NetworkProperties* properties) {
- if (client_ == NULL) {
- NOTREACHED();
- return ERROR_NOINTERFACE;
- }
+ DCHECK(client_);
// TODO(mef): WlanGetNetworkBssList is not available on XP. If XP support is
// needed, then different method of getting BSS (e.g. OID query) will have
// to be used.
- if (WlanGetNetworkBssList_function_ == NULL)
+ if (!WlanGetNetworkBssList_function_)
return ERROR_NOINTERFACE;
DWORD error = ERROR_SUCCESS;
DWORD data_size = 0;
- PWLAN_CONNECTION_ATTRIBUTES wlan_connection_attributes = NULL;
- PWLAN_BSS_LIST bss_list = NULL;
+ PWLAN_CONNECTION_ATTRIBUTES wlan_connection_attributes = nullptr;
+ PWLAN_BSS_LIST bss_list = nullptr;
error = WlanQueryInterface_function_(
- client_,
- &interface_guid_,
- wlan_intf_opcode_current_connection,
- NULL,
- &data_size,
- reinterpret_cast<PVOID*>(&wlan_connection_attributes),
- NULL);
- if (error == ERROR_SUCCESS &&
- wlan_connection_attributes != NULL) {
+ client_, &interface_guid_, wlan_intf_opcode_current_connection, nullptr,
+ &data_size, reinterpret_cast<PVOID*>(&wlan_connection_attributes),
+ nullptr);
+ if (error == ERROR_SUCCESS && wlan_connection_attributes != nullptr) {
WLAN_ASSOCIATION_ATTRIBUTES& connected_wlan =
wlan_connection_attributes->wlanAssociationAttributes;
@@ -1467,56 +1438,42 @@ DWORD WiFiServiceImpl::GetCurrentProperties(NetworkProperties* properties) {
wlan_connection_attributes->wlanSecurityAttributes.dot11AuthAlgorithm);
properties->signal_strength = connected_wlan.wlanSignalQuality;
- error = WlanGetNetworkBssList_function_(client_,
- &interface_guid_,
- &connected_wlan.dot11Ssid,
- connected_wlan.dot11BssType,
- FALSE,
- NULL,
- &bss_list);
- if (error == ERROR_SUCCESS && NULL != bss_list) {
- UpdateNetworkPropertiesFromBssList(properties->guid,
- *bss_list,
+ error = WlanGetNetworkBssList_function_(
+ client_, &interface_guid_, &connected_wlan.dot11Ssid,
+ connected_wlan.dot11BssType, FALSE, nullptr, &bss_list);
+ if (error == ERROR_SUCCESS && bss_list) {
+ UpdateNetworkPropertiesFromBssList(properties->guid, *bss_list,
properties);
}
}
// Clean up.
- if (wlan_connection_attributes != NULL)
+ if (wlan_connection_attributes)
WlanFreeMemory_function_(wlan_connection_attributes);
- if (bss_list != NULL)
+ if (bss_list)
WlanFreeMemory_function_(bss_list);
return error;
}
-
DWORD WiFiServiceImpl::GetCurrentSSID(std::string* ssid) {
- if (client_ == NULL) {
- NOTREACHED();
- return ERROR_NOINTERFACE;
- }
+ DCHECK(client_);
DWORD error = ERROR_SUCCESS;
DWORD data_size = 0;
- PWLAN_CONNECTION_ATTRIBUTES wlan_connection_attributes = NULL;
+ PWLAN_CONNECTION_ATTRIBUTES wlan_connection_attributes = nullptr;
error = WlanQueryInterface_function_(
- client_,
- &interface_guid_,
- wlan_intf_opcode_current_connection,
- NULL,
- &data_size,
- reinterpret_cast<PVOID*>(&wlan_connection_attributes),
- NULL);
- if (error == ERROR_SUCCESS &&
- wlan_connection_attributes != NULL) {
+ client_, &interface_guid_, wlan_intf_opcode_current_connection, nullptr,
+ &data_size, reinterpret_cast<PVOID*>(&wlan_connection_attributes),
+ nullptr);
+ if (error == ERROR_SUCCESS && wlan_connection_attributes) {
WLAN_ASSOCIATION_ATTRIBUTES& connected_wlan =
wlan_connection_attributes->wlanAssociationAttributes;
*ssid = GUIDFromSSID(connected_wlan.dot11Ssid);
}
// Clean up.
- if (wlan_connection_attributes != NULL)
+ if (wlan_connection_attributes)
WlanFreeMemory_function_(wlan_connection_attributes);
return error;
@@ -1542,10 +1499,7 @@ DWORD WiFiServiceImpl::GetDesiredBssList(
DOT11_SSID& ssid,
Frequency frequency,
std::unique_ptr<DOT11_BSSID_LIST>* desired_list) {
- if (client_ == NULL) {
- NOTREACHED();
- return ERROR_NOINTERFACE;
- }
+ DCHECK(client_);
desired_list->reset();
@@ -1559,16 +1513,12 @@ DWORD WiFiServiceImpl::GetDesiredBssList(
return ERROR_NOT_SUPPORTED;
DWORD error = ERROR_SUCCESS;
- PWLAN_BSS_LIST bss_list = NULL;
-
- error = WlanGetNetworkBssList_function_(client_,
- &interface_guid_,
- &ssid,
- dot11_BSS_type_infrastructure,
- FALSE,
- NULL,
- &bss_list);
- if (error == ERROR_SUCCESS && NULL != bss_list) {
+ PWLAN_BSS_LIST bss_list = nullptr;
+
+ error = WlanGetNetworkBssList_function_(client_, &interface_guid_, &ssid,
+ dot11_BSS_type_infrastructure, FALSE,
+ nullptr, &bss_list);
+ if (error == ERROR_SUCCESS && bss_list) {
unsigned int best_quality = 0u;
size_t best_index = 0;
Frequency bss_frequency;
@@ -1602,18 +1552,18 @@ DWORD WiFiServiceImpl::GetDesiredBssList(
selected_list->uNumOfEntries = 1;
selected_list->uTotalNumOfEntries = 1;
std::copy(bss_entry.dot11Bssid,
- bss_entry.dot11Bssid+sizeof(bss_entry.dot11Bssid),
+ bss_entry.dot11Bssid + sizeof(bss_entry.dot11Bssid),
selected_list->BSSIDs[0]);
desired_list->swap(selected_list);
DVLOG(1) << "Quality: " << best_quality << " BSS: "
- << NetworkProperties::MacAddressAsString(bss_entry.dot11Bssid);
+ << NetworkProperties::MacAddressAsString(bss_entry.dot11Bssid);
} else {
error = ERROR_NOT_FOUND;
}
}
// Clean up.
- if (bss_list != NULL) {
+ if (bss_list) {
WlanFreeMemory_function_(bss_list);
}
return error;
@@ -1629,11 +1579,7 @@ Frequency WiFiServiceImpl::GetNormalizedFrequency(int frequency_in_mhz) const {
DWORD WiFiServiceImpl::Connect(const std::string& network_guid,
Frequency frequency) {
- if (client_ == NULL) {
- NOTREACHED();
- return ERROR_NOINTERFACE;
- }
-
+ DCHECK(client_);
DWORD error = ERROR_SUCCESS;
DOT11_SSID ssid = SSIDFromGUID(network_guid);
std::unique_ptr<DOT11_BSSID_LIST> desired_bss_list;
@@ -1642,14 +1588,10 @@ DWORD WiFiServiceImpl::Connect(const std::string& network_guid,
if (HaveProfile(network_guid)) {
base::string16 profile_name = ProfileNameFromGUID(network_guid);
WLAN_CONNECTION_PARAMETERS wlan_params = {
- wlan_connection_mode_profile,
- profile_name.c_str(),
- NULL,
- desired_bss_list.get(),
- dot11_BSS_type_any,
- 0};
- error = WlanConnect_function_(
- client_, &interface_guid_, &wlan_params, NULL);
+ wlan_connection_mode_profile, profile_name.c_str(), nullptr,
+ desired_bss_list.get(), dot11_BSS_type_any, 0};
+ error = WlanConnect_function_(client_, &interface_guid_, &wlan_params,
+ nullptr);
} else {
// If network is available, but is not open security, then it cannot be
// connected without profile, so return 'access denied' error.
@@ -1669,13 +1611,13 @@ DWORD WiFiServiceImpl::Connect(const std::string& network_guid,
}
WLAN_CONNECTION_PARAMETERS wlan_params = {
wlan_connection_mode_discovery_unsecure,
- NULL,
+ nullptr,
&ssid,
desired_bss_list.get(),
dot11_BSS_type_infrastructure,
0};
- error = WlanConnect_function_(
- client_, &interface_guid_, &wlan_params, NULL);
+ error = WlanConnect_function_(client_, &interface_guid_, &wlan_params,
+ nullptr);
}
}
@@ -1683,35 +1625,23 @@ DWORD WiFiServiceImpl::Connect(const std::string& network_guid,
}
DWORD WiFiServiceImpl::Disconnect() {
- if (client_ == NULL) {
- NOTREACHED();
- return ERROR_NOINTERFACE;
- }
-
+ DCHECK(client_);
DWORD error = ERROR_SUCCESS;
- error = WlanDisconnect_function_(client_, &interface_guid_, NULL);
+ error = WlanDisconnect_function_(client_, &interface_guid_, nullptr);
return error;
}
DWORD WiFiServiceImpl::SaveTempProfile(const std::string& network_guid) {
- if (client_ == NULL) {
- NOTREACHED();
- return ERROR_NOINTERFACE;
- }
-
+ DCHECK(client_);
DWORD error = ERROR_SUCCESS;
base::string16 profile_name = ProfileNameFromGUID(network_guid);
// TODO(mef): WlanSaveTemporaryProfile is not available on XP. If XP support
// is needed, then different method of saving network profile will have to be
// used.
if (WlanSaveTemporaryProfile_function_) {
- error = WlanSaveTemporaryProfile_function_(client_,
- &interface_guid_,
- profile_name.c_str(),
- NULL,
- WLAN_PROFILE_USER,
- true,
- NULL);
+ error = WlanSaveTemporaryProfile_function_(
+ client_, &interface_guid_, profile_name.c_str(), nullptr,
+ WLAN_PROFILE_USER, true, nullptr);
} else {
error = ERROR_NOT_SUPPORTED;
}
@@ -1721,28 +1651,20 @@ DWORD WiFiServiceImpl::SaveTempProfile(const std::string& network_guid) {
DWORD WiFiServiceImpl::GetProfile(const std::string& network_guid,
bool get_plaintext_key,
std::string* profile_xml) {
- if (client_ == NULL) {
- NOTREACHED();
- return ERROR_NOINTERFACE;
- }
-
+ DCHECK(client_);
DWORD error = ERROR_SUCCESS;
base::string16 profile_name = ProfileNameFromGUID(network_guid);
DWORD flags = get_plaintext_key ? WLAN_PROFILE_GET_PLAINTEXT_KEY : 0;
- LPWSTR str_profile_xml = NULL;
- error = WlanGetProfile_function_(client_,
- &interface_guid_,
- profile_name.c_str(),
- NULL,
- &str_profile_xml,
- &flags,
- NULL);
-
- if (error == ERROR_SUCCESS && str_profile_xml != NULL) {
+ LPWSTR str_profile_xml = nullptr;
+ error =
+ WlanGetProfile_function_(client_, &interface_guid_, profile_name.c_str(),
+ nullptr, &str_profile_xml, &flags, nullptr);
+
+ if (error == ERROR_SUCCESS && str_profile_xml) {
*profile_xml = base::UTF16ToUTF8(str_profile_xml);
}
// Clean up.
- if (str_profile_xml != NULL) {
+ if (str_profile_xml) {
WlanFreeMemory_function_(str_profile_xml);
}
@@ -1757,14 +1679,9 @@ DWORD WiFiServiceImpl::SetProfile(bool shared,
base::string16 profile_xml16(base::UTF8ToUTF16(profile_xml));
DWORD reason_code = 0u;
- error_code = WlanSetProfile_function_(client_,
- &interface_guid_,
- shared ? 0 : WLAN_PROFILE_USER,
- profile_xml16.c_str(),
- NULL,
- overwrite,
- NULL,
- &reason_code);
+ error_code = WlanSetProfile_function_(
+ client_, &interface_guid_, shared ? 0 : WLAN_PROFILE_USER,
+ profile_xml16.c_str(), nullptr, overwrite, nullptr, &reason_code);
return error_code;
}
@@ -1775,18 +1692,16 @@ bool WiFiServiceImpl::HaveProfile(const std::string& network_guid) {
DWORD WiFiServiceImpl::DeleteCreatedProfile(const std::string& network_guid) {
- base::DictionaryValue* created_profile = NULL;
+ base::DictionaryValue* created_profile = nullptr;
DWORD error_code = ERROR_SUCCESS;
// Check, whether this connection is using new created profile, and remove it.
if (created_profiles_.GetDictionaryWithoutPathExpansion(
network_guid, &created_profile)) {
// Connection has failed, so delete it.
base::string16 profile_name = ProfileNameFromGUID(network_guid);
- error_code = WlanDeleteProfile_function_(client_,
- &interface_guid_,
- profile_name.c_str(),
- NULL);
- created_profiles_.RemoveWithoutPathExpansion(network_guid, NULL);
+ error_code = WlanDeleteProfile_function_(client_, &interface_guid_,
+ profile_name.c_str(), nullptr);
+ created_profiles_.RemoveWithoutPathExpansion(network_guid, nullptr);
}
return error_code;
}
diff --git a/chromium/components/wifi/wifi_test.cc b/chromium/components/wifi/wifi_test.cc
index d868c608062..98c87a34566 100644
--- a/chromium/components/wifi/wifi_test.cc
+++ b/chromium/components/wifi/wifi_test.cc
@@ -132,7 +132,7 @@ bool WiFiTest::ParseCommandLine(int argc, const char* argv[]) {
#if defined(OS_WIN)
if (parsed_command_line.HasSwitch("debug"))
- MessageBoxA(NULL, __FUNCTION__, "Debug Me!", MB_OK);
+ MessageBoxA(nullptr, __FUNCTION__, "Debug Me!", MB_OK);
#endif
base::MessageLoopForIO loop;
diff --git a/chromium/components/zoom/zoom_event_manager.cc b/chromium/components/zoom/zoom_event_manager.cc
index a4b086088fc..39a4c63c5e4 100644
--- a/chromium/components/zoom/zoom_event_manager.cc
+++ b/chromium/components/zoom/zoom_event_manager.cc
@@ -45,13 +45,11 @@ void ZoomEventManager::OnDefaultZoomLevelChanged() {
observer.OnDefaultZoomLevelChanged();
}
-void ZoomEventManager::AddZoomEventManagerObserver(
- ZoomEventManagerObserver* observer) {
+void ZoomEventManager::AddObserver(ZoomEventManagerObserver* observer) {
observers_.AddObserver(observer);
}
-void ZoomEventManager::RemoveZoomEventManagerObserver(
- ZoomEventManagerObserver* observer) {
+void ZoomEventManager::RemoveObserver(ZoomEventManagerObserver* observer) {
observers_.RemoveObserver(observer);
}
diff --git a/chromium/components/zoom/zoom_event_manager.h b/chromium/components/zoom/zoom_event_manager.h
index ae2c8f9a013..fc4b865d373 100644
--- a/chromium/components/zoom/zoom_event_manager.h
+++ b/chromium/components/zoom/zoom_event_manager.h
@@ -51,8 +51,8 @@ class ZoomEventManager : public base::SupportsUserData::Data {
void OnDefaultZoomLevelChanged();
// Add and remove observers.
- void AddZoomEventManagerObserver(ZoomEventManagerObserver* observer);
- void RemoveZoomEventManagerObserver(ZoomEventManagerObserver* observer);
+ void AddObserver(ZoomEventManagerObserver* observer);
+ void RemoveObserver(ZoomEventManagerObserver* observer);
// Get a weak ptr to be used by clients who may themselves be UserData for
// the context, since the order of destruction is undefined between the client
diff --git a/chromium/components/zucchini/BUILD.gn b/chromium/components/zucchini/BUILD.gn
index 70831275505..77dc810b487 100644
--- a/chromium/components/zucchini/BUILD.gn
+++ b/chromium/components/zucchini/BUILD.gn
@@ -130,7 +130,6 @@ executable("zucchini") {
":zucchini_io",
":zucchini_lib",
"//base",
- "//build/config:exe_and_shlib_deps",
]
if (is_win) {
@@ -213,6 +212,7 @@ test("zucchini_integration_test") {
group("zucchini_fuzzers") {
testonly = true
deps = [
+ "//components/zucchini/fuzzers:zucchini_disassembler_dex_fuzzer",
"//components/zucchini/fuzzers:zucchini_disassembler_win32_fuzzer",
"//components/zucchini/fuzzers:zucchini_patch_fuzzer",
]
@@ -221,8 +221,10 @@ group("zucchini_fuzzers") {
# Disabled on Windows due to crbug/844826.
if (current_toolchain == host_toolchain && !is_win) {
deps += [
- "//components/zucchini/fuzzers:zucchini_raw_apply_fuzzer",
+ "//components/zucchini/fuzzers:zucchini_apply_fuzzer",
+ "//components/zucchini/fuzzers:zucchini_imposed_ensemble_matcher_fuzzer",
"//components/zucchini/fuzzers:zucchini_raw_gen_fuzzer",
+ "//components/zucchini/fuzzers:zucchini_ztf_gen_fuzzer",
]
}
}
diff --git a/chromium/components/zucchini/README.md b/chromium/components/zucchini/README.md
index 42f3b3e9a52..d3fd0a1bc11 100644
--- a/chromium/components/zucchini/README.md
+++ b/chromium/components/zucchini/README.md
@@ -70,6 +70,12 @@ on its location, they are more likely to get modified from an old version of a
binary to a newer version. This is why "naive" patching does not do well on
binaries.
+**Target Key**: An alternative representation of a Target for a fixed pool, as its
+index in the sorted list of Target offsets. Keys are useful since:
+ * Their numerical index are smaller than offsets, allowing more efficient
+ storage of target correction data in patch.
+ * They simplify association from Targets to Labels.
+
**Disassembler**: Architecture specific data and operations, used to extract and
correct references in a binary.
@@ -127,24 +133,40 @@ bytewise difference to apply.
3. are not part of any larger region from a different equivalence.
Not all targets are necessarily associated with another target.
-**Label**: An (offset, index) pair, where |offset| is a target, and |index| is
-an integer used to uniquely identify |offset| in its corresponding pool of
-targets. Labels are created for each Reference in "old" and "new" binary as part
+**Target Affinity**: Level of confidence in the association between two targets.
+The affinity between targets that are potentially associated is measured based
+on surrounding content, as well as reference type.
+
+**Label**: An integer assigned for each Target in "old" and "new" binary as part
of generating a patch, and used to alias targets when searching for similar
-regions that will form equivalences. Labels are created such that associated
-targets in old and new binaries share the same |index|, and such that indices in
-a pool are tightly packed. For example, suppose "old" Labels are:
- - (0x1111, 0), (0x3333, 4), (0x5555, 1), (0x7777, 3)
-and given the following association of targets between "old" and "new":
- - 0x1111 <=> 0x6666, 0x3333 <=> 0x2222.
-then we could assign indices for "new" Labels as:
- - (0x2222, 4}, (0x4444, 8), (0x6666, 0), (0x8888, 2)
+regions that will form equivalences. Labels are assigned such that
+associated targets in old and new binaries share the same Label. Unmatched
+Targets have a Label of 0. For example, given
+ * "Old" targets = [0x1111, 0x3333, 0x5555, 0x7777],
+ * "New" targets = [0x2222, 0x4444, 0x6666, 0x8888],
+to represent matchings 0x1111 <=> 0x6666, 0x3333 <=> 0x2222, we'd assign
+ * Label 1 to 0x1111 (in "old") and 0x6666 (in "new"),
+ * Label 2 to 0x3333 (in "old") and 0x2222 (in "new").
+ Represented as arrays indexed over Target Keys, we'd have:
+ * "Old" labels = [1, 2, 0 ,0],
+ * "New" labels = [2, 0, 1, 0].
**Encoded Image**: The result of projecting the content of an image to scalar
values that describe content on a higher level of abstraction, masking away
undesirable noise in raw content. Notably, the projection encodes references
based on their associated label.
+## Interfaces
+
+zucchini_lib: Core Zucchini library that operate on buffers to generate and
+apply patches.
+
+zucchini_io: Wrapper on zucchini_lib that handles file I/O, using memory-mapped
+I/O to interface with zucchini_lib.
+
+zucchini: Stand-alone executable that parses command-line arguments, and passes
+the results to zucchini_io. Also implements various helper flows.
+
## Zucchini Ensemble Patch Format
### Types
diff --git a/chromium/components/zucchini/algorithm.h b/chromium/components/zucchini/algorithm.h
index 7143a953626..463aca3840d 100644
--- a/chromium/components/zucchini/algorithm.h
+++ b/chromium/components/zucchini/algorithm.h
@@ -48,7 +48,7 @@ T InclusiveClamp(T value, T lo, T hi) {
// Returns the minimum multiple of |m| that's no less than |x|. Assumes |m > 0|
// and |x| is sufficiently small so that no overflow occurs.
template <class T>
-constexpr T ceil(T x, T m) {
+constexpr T AlignCeil(T x, T m) {
static_assert(std::is_unsigned<T>::value, "Value type must be unsigned.");
return T((x + m - 1) / m) * m;
}
@@ -62,6 +62,47 @@ void SortAndUniquify(std::vector<T>* container) {
container->shrink_to_fit();
}
+// Extracts a single bit at |pos| from integer |v|.
+template <int pos, typename T>
+constexpr T GetBit(T v) {
+ return (v >> pos) & 1;
+}
+
+// Extracts bits in inclusive range [|lo|, |hi|] from integer |v|, and returns
+// the sign-extend result. For example, let the (MSB-first) bits in a 32-bit int
+// |v| be:
+// xxxxxxxx xxxxxSii iiiiiiii iyyyyyyy,
+// hi^ lo^ => lo = 7, hi = 18
+// To extract "Sii iiiiiiii i", calling
+// GetSignedBits<7, 18>(v);
+// produces the sign-extended result:
+// SSSSSSSS SSSSSSSS SSSSSiii iiiiiiii.
+template <int lo, int hi, typename T>
+constexpr typename std::make_signed<T>::type GetSignedBits(T v) {
+ constexpr int kNumBits = sizeof(T) * 8;
+ using SignedType = typename std::make_signed<T>::type;
+ // Assumes 0 <= |lo| <= |hi| < |kNumBits|.
+ // How this works:
+ // (1) Shift-left by |kNumBits - 1 - hi| to clear "left" bits.
+ // (2) Shift-right by |kNumBits - 1 - hi + lo| to clear "right" bits. The
+ // input is casted to a signed type to perform sign-extension.
+ return static_cast<SignedType>(v << (kNumBits - 1 - hi)) >>
+ (kNumBits - 1 - hi + lo);
+}
+
+// Similar to GetSignedBits(), but returns the zero-extended result. For the
+// above example, calling
+// GetUnsignedBits<7, 18>(v);
+// results in:
+// 00000000 00000000 0000Siii iiiiiiii.
+template <int lo, int hi, typename T>
+constexpr typename std::make_unsigned<T>::type GetUnsignedBits(T v) {
+ constexpr int kNumBits = sizeof(T) * 8;
+ using UnsignedType = typename std::make_unsigned<T>::type;
+ return static_cast<UnsignedType>(v << (kNumBits - 1 - hi)) >>
+ (kNumBits - 1 - hi + lo);
+}
+
// Copies bits at |pos| in |v| to all higher bits, and returns the result as the
// same int type as |v|.
template <typename T>
@@ -79,6 +120,13 @@ constexpr T SignExtend(T v) {
return static_cast<typename std::make_signed<T>::type>(v << kShift) >> kShift;
}
+// Determines whether |v|, if interpreted as a signed integer, is representable
+// using |digs| bits. |1 <= digs <= sizeof(T)| is assumed.
+template <int digs, typename T>
+constexpr bool SignedFit(T v) {
+ return v == SignExtend<digs - 1, T>(v);
+}
+
} // namespace zucchini
#endif // COMPONENTS_ZUCCHINI_ALGORITHM_H_
diff --git a/chromium/components/zucchini/algorithm_unittest.cc b/chromium/components/zucchini/algorithm_unittest.cc
index 2c685db05cf..a395b1e2aa0 100644
--- a/chromium/components/zucchini/algorithm_unittest.cc
+++ b/chromium/components/zucchini/algorithm_unittest.cc
@@ -131,18 +131,99 @@ TEST(AlgorithmTest, InclusiveClamp) {
InclusiveClamp<uint32_t>(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF));
}
-TEST(AlgorithmTest, Ceil) {
- EXPECT_EQ(0U, ceil<uint32_t>(0U, 2U));
- EXPECT_EQ(2U, ceil<uint32_t>(1U, 2U));
- EXPECT_EQ(2U, ceil<uint32_t>(2U, 2U));
- EXPECT_EQ(4U, ceil<uint32_t>(3U, 2U));
- EXPECT_EQ(4U, ceil<uint32_t>(4U, 2U));
- EXPECT_EQ(11U, ceil<uint32_t>(10U, 11U));
- EXPECT_EQ(11U, ceil<uint32_t>(11U, 11U));
- EXPECT_EQ(22U, ceil<uint32_t>(12U, 11U));
- EXPECT_EQ(22U, ceil<uint32_t>(21U, 11U));
- EXPECT_EQ(22U, ceil<uint32_t>(22U, 11U));
- EXPECT_EQ(33U, ceil<uint32_t>(23U, 11U));
+TEST(AlgorithmTest, AlignCeil) {
+ EXPECT_EQ(0U, AlignCeil<uint32_t>(0U, 2U));
+ EXPECT_EQ(2U, AlignCeil<uint32_t>(1U, 2U));
+ EXPECT_EQ(2U, AlignCeil<uint32_t>(2U, 2U));
+ EXPECT_EQ(4U, AlignCeil<uint32_t>(3U, 2U));
+ EXPECT_EQ(4U, AlignCeil<uint32_t>(4U, 2U));
+ EXPECT_EQ(11U, AlignCeil<uint32_t>(10U, 11U));
+ EXPECT_EQ(11U, AlignCeil<uint32_t>(11U, 11U));
+ EXPECT_EQ(22U, AlignCeil<uint32_t>(12U, 11U));
+ EXPECT_EQ(22U, AlignCeil<uint32_t>(21U, 11U));
+ EXPECT_EQ(22U, AlignCeil<uint32_t>(22U, 11U));
+ EXPECT_EQ(33U, AlignCeil<uint32_t>(23U, 11U));
+}
+
+TEST(AlgorithmTest, GetBit) {
+ // 0xC5 = 0b1100'0101.
+ constexpr uint8_t v = 0xC5;
+ EXPECT_EQ(uint8_t(1), (GetBit<0>(v)));
+ EXPECT_EQ(int8_t(0), (GetBit<1>(signed8(v))));
+ EXPECT_EQ(uint8_t(1), (GetBit<2>(v)));
+ EXPECT_EQ(int8_t(0), (GetBit<3>(signed8(v))));
+ EXPECT_EQ(uint8_t(0), (GetBit<4>(v)));
+ EXPECT_EQ(int8_t(0), (GetBit<5>(signed8(v))));
+ EXPECT_EQ(uint8_t(1), (GetBit<6>(v)));
+ EXPECT_EQ(int8_t(1), (GetBit<7>(signed8(v))));
+
+ EXPECT_EQ(int16_t(1), (GetBit<3, int16_t>(0x0008)));
+ EXPECT_EQ(uint16_t(0), (GetBit<14, uint16_t>(0xB000)));
+ EXPECT_EQ(uint16_t(1), (GetBit<15, uint16_t>(0xB000)));
+
+ EXPECT_EQ(uint32_t(1), (GetBit<0, uint32_t>(0xFFFFFFFF)));
+ EXPECT_EQ(int32_t(1), (GetBit<31, int32_t>(0xFFFFFFFF)));
+
+ EXPECT_EQ(uint32_t(0), (GetBit<0, uint32_t>(0xFF00A596)));
+ EXPECT_EQ(int32_t(1), (GetBit<1, int32_t>(0xFF00A596)));
+ EXPECT_EQ(uint32_t(1), (GetBit<4, uint32_t>(0xFF00A596)));
+ EXPECT_EQ(int32_t(1), (GetBit<7, int32_t>(0xFF00A596)));
+ EXPECT_EQ(uint32_t(0), (GetBit<9, uint32_t>(0xFF00A596)));
+ EXPECT_EQ(int32_t(0), (GetBit<16, int32_t>(0xFF00A59)));
+ EXPECT_EQ(uint32_t(1), (GetBit<24, uint32_t>(0xFF00A596)));
+ EXPECT_EQ(int32_t(1), (GetBit<31, int32_t>(0xFF00A596)));
+
+ EXPECT_EQ(uint64_t(0), (GetBit<62, uint64_t>(0xB000000000000000ULL)));
+ EXPECT_EQ(int64_t(1), (GetBit<63, int64_t>(0xB000000000000000LL)));
+}
+
+TEST(AlgorithmTest, GetBits) {
+ // Zero-extended: Basic cases for various values.
+ uint32_t test_cases[] = {0, 1, 2, 7, 137, 0x10000, 0x69969669, 0xFFFFFFFF};
+ for (uint32_t v : test_cases) {
+ EXPECT_EQ(uint32_t(v & 0xFF), (GetUnsignedBits<0, 7>(v)));
+ EXPECT_EQ(uint32_t((v >> 8) & 0xFF), (GetUnsignedBits<8, 15>(v)));
+ EXPECT_EQ(uint32_t((v >> 16) & 0xFF), (GetUnsignedBits<16, 23>(v)));
+ EXPECT_EQ(uint32_t((v >> 24) & 0xFF), (GetUnsignedBits<24, 31>(v)));
+ EXPECT_EQ(uint32_t(v & 0xFFFF), (GetUnsignedBits<0, 15>(v)));
+ EXPECT_EQ(uint32_t((v >> 1) & 0x3FFFFFFF), (GetUnsignedBits<1, 30>(v)));
+ EXPECT_EQ(uint32_t((v >> 2) & 0x0FFFFFFF), (GetUnsignedBits<2, 29>(v)));
+ EXPECT_EQ(uint32_t(v), (GetUnsignedBits<0, 31>(v)));
+ }
+
+ // Zero-extended: Reading off various nibbles.
+ EXPECT_EQ(uint32_t(0x4), (GetUnsignedBits<20, 23>(0x00432100U)));
+ EXPECT_EQ(uint32_t(0x43), (GetUnsignedBits<16, 23>(0x00432100)));
+ EXPECT_EQ(uint32_t(0x432), (GetUnsignedBits<12, 23>(0x00432100U)));
+ EXPECT_EQ(uint32_t(0x4321), (GetUnsignedBits<8, 23>(0x00432100)));
+ EXPECT_EQ(uint32_t(0x321), (GetUnsignedBits<8, 19>(0x00432100U)));
+ EXPECT_EQ(uint32_t(0x21), (GetUnsignedBits<8, 15>(0x00432100)));
+ EXPECT_EQ(uint32_t(0x1), (GetUnsignedBits<8, 11>(0x00432100U)));
+
+ // Sign-extended: 0x3CA5 = 0b0011'1100'1010'0101.
+ EXPECT_EQ(signed16(0xFFFF), (GetSignedBits<0, 0>(0x3CA5U)));
+ EXPECT_EQ(signed16(0x0001), (GetSignedBits<0, 1>(0x3CA5)));
+ EXPECT_EQ(signed16(0xFFFD), (GetSignedBits<0, 2>(0x3CA5U)));
+ EXPECT_EQ(signed16(0x0005), (GetSignedBits<0, 4>(0x3CA5)));
+ EXPECT_EQ(signed16(0xFFA5), (GetSignedBits<0, 7>(0x3CA5U)));
+ EXPECT_EQ(signed16(0xFCA5), (GetSignedBits<0, 11>(0x3CA5)));
+ EXPECT_EQ(signed16(0x0005), (GetSignedBits<0, 3>(0x3CA5U)));
+ EXPECT_EQ(signed16(0xFFFA), (GetSignedBits<4, 7>(0x3CA5)));
+ EXPECT_EQ(signed16(0xFFFC), (GetSignedBits<8, 11>(0x3CA5U)));
+ EXPECT_EQ(signed16(0x0003), (GetSignedBits<12, 15>(0x3CA5)));
+ EXPECT_EQ(signed16(0x0000), (GetSignedBits<4, 4>(0x3CA5U)));
+ EXPECT_EQ(signed16(0xFFFF), (GetSignedBits<5, 5>(0x3CA5)));
+ EXPECT_EQ(signed16(0x0002), (GetSignedBits<4, 6>(0x3CA5U)));
+ EXPECT_EQ(signed16(0x1E52), (GetSignedBits<1, 14>(0x3CA5)));
+ EXPECT_EQ(signed16(0xFF29), (GetSignedBits<2, 13>(0x3CA5U)));
+ EXPECT_EQ(int32_t(0x00001E52), (GetSignedBits<1, 14>(0x3CA5)));
+ EXPECT_EQ(int32_t(0xFFFFFF29), (GetSignedBits<2, 13>(0x3CA5U)));
+
+ // 64-bits: Extract from middle 0x66 = 0b0110'0110.
+ EXPECT_EQ(uint64_t(0x0000000000000009LL),
+ (GetUnsignedBits<30, 33>(int64_t(0x2222222661111111LL))));
+ EXPECT_EQ(int64_t(0xFFFFFFFFFFFFFFF9LL),
+ (GetSignedBits<30, 33>(uint64_t(0x2222222661111111LL))));
}
TEST(AlgorithmTest, SignExtend) {
@@ -203,4 +284,38 @@ TEST(AlgorithmTest, SignExtendTemplated) {
(SignExtend<9, uint64_t>(0xFFFFFFFFFFFFFD6AULL)));
}
+TEST(AlgorithmTest, SignedFit) {
+ for (int v = -0x80; v < 0x80; ++v) {
+ EXPECT_EQ(v >= -1 && v < 1, (SignedFit<1, int8_t>(v)));
+ EXPECT_EQ(v >= -1 && v < 1, (SignedFit<1, uint8_t>(v)));
+ EXPECT_EQ(v >= -2 && v < 2, (SignedFit<2, int8_t>(v)));
+ EXPECT_EQ(v >= -4 && v < 4, (SignedFit<3, uint8_t>(v)));
+ EXPECT_EQ(v >= -8 && v < 8, (SignedFit<4, int16_t>(v)));
+ EXPECT_EQ(v >= -16 && v < 16, (SignedFit<5, uint32_t>(v)));
+ EXPECT_EQ(v >= -32 && v < 32, (SignedFit<6, int32_t>(v)));
+ EXPECT_EQ(v >= -64 && v < 64, (SignedFit<7, uint64_t>(v)));
+ EXPECT_TRUE((SignedFit<8, int8_t>(v)));
+ EXPECT_TRUE((SignedFit<8, uint8_t>(v)));
+ }
+
+ EXPECT_TRUE((SignedFit<16, uint32_t>(0x00000000)));
+ EXPECT_TRUE((SignedFit<16, uint32_t>(0x00007FFF)));
+ EXPECT_TRUE((SignedFit<16, uint32_t>(0xFFFF8000)));
+ EXPECT_TRUE((SignedFit<16, uint32_t>(0xFFFFFFFF)));
+ EXPECT_TRUE((SignedFit<16, int32_t>(0x00007FFF)));
+ EXPECT_TRUE((SignedFit<16, int32_t>(0xFFFF8000)));
+
+ EXPECT_FALSE((SignedFit<16, uint32_t>(0x80000000)));
+ EXPECT_FALSE((SignedFit<16, uint32_t>(0x7FFFFFFF)));
+ EXPECT_FALSE((SignedFit<16, uint32_t>(0x00008000)));
+ EXPECT_FALSE((SignedFit<16, uint32_t>(0xFFFF7FFF)));
+ EXPECT_FALSE((SignedFit<16, int32_t>(0x00008000)));
+ EXPECT_FALSE((SignedFit<16, int32_t>(0xFFFF7FFF)));
+
+ EXPECT_TRUE((SignedFit<48, int64_t>(0x00007FFFFFFFFFFFLL)));
+ EXPECT_TRUE((SignedFit<48, int64_t>(0xFFFF800000000000LL)));
+ EXPECT_FALSE((SignedFit<48, int64_t>(0x0008000000000000LL)));
+ EXPECT_FALSE((SignedFit<48, int64_t>(0xFFFF7FFFFFFFFFFFLL)));
+}
+
} // namespace zucchini
diff --git a/chromium/components/zucchini/buffer_source.cc b/chromium/components/zucchini/buffer_source.cc
index 721588a627d..d72d329da7e 100644
--- a/chromium/components/zucchini/buffer_source.cc
+++ b/chromium/components/zucchini/buffer_source.cc
@@ -80,7 +80,7 @@ bool BufferSource::GetSleb128(int32_t* ret) {
for (int shift = 0; shift < shift_lim; shift += 7, ++cur) {
uint32_t b = *cur;
// When |shift == 28|, |(b & 0x7F) << shift| discards the "???" bits.
- value |= static_cast<int32_t>(b & 0x7F) << shift;
+ value |= static_cast<int32_t>(static_cast<uint32_t>(b & 0x7F) << shift);
if (!(b & 0x80)) {
*ret = (shift == 28) ? value : SignExtend(shift + 6, value);
seek(cur + 1);
diff --git a/chromium/components/zucchini/buffer_view.h b/chromium/components/zucchini/buffer_view.h
index 8dbe817ede7..66925c4fb37 100644
--- a/chromium/components/zucchini/buffer_view.h
+++ b/chromium/components/zucchini/buffer_view.h
@@ -24,9 +24,9 @@ struct BufferRegion {
size_t hi() const { return offset + size; }
// Returns whether the Region fits in |[0, container_size)|. Special case:
- // a size-0 region starting at |container_size| does not fit.
+ // a size-0 region starting at |container_size| fits.
bool FitsIn(size_t container_size) const {
- return offset < container_size && container_size - offset >= size;
+ return offset <= container_size && container_size - offset >= size;
}
// Returns |v| clipped to the inclusive range |[lo(), hi()]|.
@@ -110,7 +110,7 @@ class BufferViewBase {
bool covers_array(size_t offset, size_t num, size_t elt_size) {
DCHECK_GT(elt_size, 0U);
// Use subtraction and division to avoid overflow.
- return offset < size() && (size() - offset) / elt_size >= num;
+ return offset <= size() && (size() - offset) / elt_size >= num;
}
// Element access
@@ -184,7 +184,7 @@ class BufferViewBase {
DCHECK_LE(origin.first_, first_);
DCHECK_GE(origin.last_, last_);
size_type aligned_size =
- ceil(static_cast<size_type>(first_ - origin.first_), alignment);
+ AlignCeil(static_cast<size_type>(first_ - origin.first_), alignment);
if (aligned_size > static_cast<size_type>(last_ - origin.first_))
return false;
first_ = origin.first_ + aligned_size;
diff --git a/chromium/components/zucchini/buffer_view_unittest.cc b/chromium/components/zucchini/buffer_view_unittest.cc
index 1d3ccb8589a..7df34b27842 100644
--- a/chromium/components/zucchini/buffer_view_unittest.cc
+++ b/chromium/components/zucchini/buffer_view_unittest.cc
@@ -154,7 +154,7 @@ TEST_F(BufferViewTest, LocalRegion) {
}
TEST_F(BufferViewTest, Covers) {
- EXPECT_FALSE(ConstBufferView().covers({0, 0}));
+ EXPECT_TRUE(ConstBufferView().covers({0, 0}));
EXPECT_FALSE(ConstBufferView().covers({0, 1}));
ConstBufferView view(bytes_.data(), bytes_.size());
@@ -168,8 +168,10 @@ TEST_F(BufferViewTest, Covers) {
EXPECT_TRUE(view.covers({bytes_.size() - 1, 0}));
EXPECT_TRUE(view.covers({bytes_.size() - 1, 1}));
EXPECT_FALSE(view.covers({bytes_.size() - 1, 2}));
- EXPECT_FALSE(view.covers({bytes_.size(), 0}));
+ EXPECT_TRUE(view.covers({bytes_.size(), 0}));
EXPECT_FALSE(view.covers({bytes_.size(), 1}));
+ EXPECT_FALSE(view.covers({bytes_.size() + 1, 0}));
+ EXPECT_FALSE(view.covers({bytes_.size() + 1, 1}));
EXPECT_FALSE(view.covers({1, size_t(-1)}));
EXPECT_FALSE(view.covers({size_t(-1), 1}));
@@ -194,10 +196,10 @@ TEST_F(BufferViewTest, CoversArray) {
EXPECT_TRUE(view.covers_array(0, 0, bytes_.size()));
EXPECT_TRUE(view.covers_array(bytes_.size() - 1, 0, bytes_.size()));
- EXPECT_FALSE(view.covers_array(bytes_.size(), 0, bytes_.size()));
+ EXPECT_TRUE(view.covers_array(bytes_.size(), 0, bytes_.size()));
EXPECT_TRUE(view.covers_array(0, 0, 0x10000));
EXPECT_TRUE(view.covers_array(bytes_.size() - 1, 0, 0x10000));
- EXPECT_FALSE(view.covers_array(bytes_.size(), 0, 0x10000));
+ EXPECT_TRUE(view.covers_array(bytes_.size(), 0, 0x10000));
EXPECT_FALSE(view.covers_array(0, 1, bytes_.size() + 1));
EXPECT_FALSE(view.covers_array(0, 2, bytes_.size()));
@@ -206,7 +208,7 @@ TEST_F(BufferViewTest, CoversArray) {
EXPECT_FALSE(view.covers_array(1, bytes_.size(), 1));
EXPECT_FALSE(view.covers_array(bytes_.size(), 1, 1));
- EXPECT_FALSE(view.covers_array(bytes_.size(), 0, 1));
+ EXPECT_TRUE(view.covers_array(bytes_.size(), 0, 1));
EXPECT_FALSE(view.covers_array(0, 0x10000, 0x10000));
}
diff --git a/chromium/components/zucchini/disassembler.h b/chromium/components/zucchini/disassembler.h
index eced3cfe9d5..4f528edd251 100644
--- a/chromium/components/zucchini/disassembler.h
+++ b/chromium/components/zucchini/disassembler.h
@@ -17,7 +17,65 @@
namespace zucchini {
-class Disassembler;
+// Disassembler needs to be declared before ReferenceGroup because the latter
+// contains member pointers based on the former, and we use a compiler flag,
+// -fcomplete-member-pointers, which enforces that member pointer base types are
+// complete. This flag helps prevent us from running into problems in the
+// Microsoft C++ ABI (see https://crbug.com/847724).
+
+class ReferenceGroup;
+
+// A Disassembler is used to encapsulate architecture specific operations, to:
+// - Describe types of references found in the architecture using traits.
+// - Extract references contained in an image file.
+// - Correct target for some references.
+class Disassembler {
+ public:
+ // Attempts to parse |image| and create an architecture-specifc Disassembler,
+ // as determined by DIS, which is inherited from Disassembler. Returns an
+ // instance of DIS if successful, and null otherwise.
+ template <class DIS>
+ static std::unique_ptr<DIS> Make(ConstBufferView image) {
+ auto disasm = std::make_unique<DIS>();
+ if (!disasm->Parse(image))
+ return nullptr;
+ return disasm;
+ }
+
+ virtual ~Disassembler();
+
+ // Returns the type of executable handled by the Disassembler.
+ virtual ExecutableType GetExeType() const = 0;
+
+ // Returns a more detailed description of the executable type.
+ virtual std::string GetExeTypeString() const = 0;
+
+ // Creates and returns a vector that contains all groups of references.
+ // Groups must be aggregated by pool.
+ virtual std::vector<ReferenceGroup> MakeReferenceGroups() const = 0;
+
+ ConstBufferView image() const { return image_; }
+ size_t size() const { return image_.size(); }
+
+ int num_equivalence_iterations() const { return num_equivalence_iterations_; }
+
+ protected:
+ explicit Disassembler(int num_equivalence_iterations);
+
+ // Parses |image| and initializes internal states. Returns true on success.
+ // This must be called once and before any other operation.
+ virtual bool Parse(ConstBufferView image) = 0;
+
+ // Raw image data. After Parse(), a Disassembler should shrink this to contain
+ // only the portion containing the executable file it recognizes.
+ ConstBufferView image_;
+
+ // The number of iterations to run for equivalence map generation. This should
+ // roughly be the max length of reference indirection chains.
+ int num_equivalence_iterations_;
+
+ DISALLOW_COPY_AND_ASSIGN(Disassembler);
+};
// A ReferenceGroup is associated with a specific |type| and has convenience
// methods to obtain readers and writers for that type. A ReferenceGroup does
@@ -82,58 +140,6 @@ class ReferenceGroup {
WriterFactory writer_factory_ = nullptr;
};
-// A Disassembler is used to encapsulate architecture specific operations, to:
-// - Describe types of references found in the architecture using traits.
-// - Extract references contained in an image file.
-// - Correct target for some references.
-class Disassembler {
- public:
- // Attempts to parse |image| and create an architecture-specifc Disassembler,
- // as determined by DIS, which is inherited from Disassembler. Returns an
- // instance of DIS if successful, and null otherwise.
- template <class DIS>
- static std::unique_ptr<DIS> Make(ConstBufferView image) {
- auto disasm = std::make_unique<DIS>();
- if (!disasm->Parse(image))
- return nullptr;
- return disasm;
- }
-
- virtual ~Disassembler();
-
- // Returns the type of executable handled by the Disassembler.
- virtual ExecutableType GetExeType() const = 0;
-
- // Returns a more detailed description of the executable type.
- virtual std::string GetExeTypeString() const = 0;
-
- // Creates and returns a vector that contains all groups of references.
- // Groups must be aggregated by pool.
- virtual std::vector<ReferenceGroup> MakeReferenceGroups() const = 0;
-
- ConstBufferView image() const { return image_; }
- size_t size() const { return image_.size(); }
-
- int num_equivalence_iterations() const { return num_equivalence_iterations_; }
-
- protected:
- explicit Disassembler(int num_equivalence_iterations);
-
- // Parses |image| and initializes internal states. Returns true on success.
- // This must be called once and before any other operation.
- virtual bool Parse(ConstBufferView image) = 0;
-
- // Raw image data. After Parse(), a Disassembler should shrink this to contain
- // only the portion containing the executable file it recognizes.
- ConstBufferView image_;
-
- // The number of iterations to run for equivalence map generation. This should
- // roughly be the max length of reference indirection chains.
- int num_equivalence_iterations_;
-
- DISALLOW_COPY_AND_ASSIGN(Disassembler);
-};
-
} // namespace zucchini
#endif // COMPONENTS_ZUCCHINI_DISASSEMBLER_H_
diff --git a/chromium/components/zucchini/disassembler_dex.cc b/chromium/components/zucchini/disassembler_dex.cc
index ac3a693dfae..373d6457d79 100644
--- a/chromium/components/zucchini/disassembler_dex.cc
+++ b/chromium/components/zucchini/disassembler_dex.cc
@@ -28,11 +28,62 @@ namespace zucchini {
namespace {
+// A DEX item specified by an offset, if absent, has a sentinel value of 0 since
+// 0 is never a valid item offset (it points to magic at start of DEX).
+constexpr offset_t kDexSentinelOffset = 0U;
+
+// A DEX item specified by an index, if absent, has a sentinel value of
+// NO_INDEX = 0xFFFFFFFF. This is represented as an offset_t for uniformity.
+constexpr offset_t kDexSentinelIndexAsOffset = 0xFFFFFFFFU;
+
+static_assert(kDexSentinelIndexAsOffset != kInvalidOffset,
+ "Sentinel should not be confused with invalid offset.");
+
// Size of a Dalvik instruction unit. Need to cast to signed int because
// sizeof() gives size_t, which dominates when operated on ptrdiff_t, then
// wrecks havoc for base::checked_cast<int16_t>().
constexpr int kInstrUnitSize = static_cast<int>(sizeof(uint16_t));
+// Checks if |offset| is byte aligned to 32 bits or 4 bytes.
+bool Is32BitAligned(offset_t offset) {
+ return offset % 4 == 0;
+}
+
+// Returns a lower bound for the size of an item of type |type_item_code|.
+// - For fixed-length items (e.g., kTypeFieldIdItem) this is the exact size.
+// - For variant-length items (e.g., kTypeCodeItem), returns a value that is
+// known to be less than the item length (e.g., header size).
+// - For items not handled by this function, returns 1 for sanity check.
+size_t GetItemBaseSize(uint16_t type_item_code) {
+ switch (type_item_code) {
+ case dex::kTypeStringIdItem:
+ return sizeof(dex::StringIdItem);
+ case dex::kTypeTypeIdItem:
+ return sizeof(dex::TypeIdItem);
+ case dex::kTypeProtoIdItem:
+ return sizeof(dex::ProtoIdItem);
+ case dex::kTypeFieldIdItem:
+ return sizeof(dex::FieldIdItem);
+ case dex::kTypeMethodIdItem:
+ return sizeof(dex::MethodIdItem);
+ case dex::kTypeClassDefItem:
+ return sizeof(dex::ClassDefItem);
+ // No need to handle dex::kTypeMapList.
+ case dex::kTypeTypeList:
+ return sizeof(uint32_t); // Variable-length.
+ case dex::kTypeAnnotationSetRefList:
+ return sizeof(uint32_t); // Variable-length.
+ case dex::kTypeAnnotationSetItem:
+ return sizeof(uint32_t); // Variable-length.
+ case dex::kTypeCodeItem:
+ return sizeof(dex::CodeItem); // Variable-length.
+ case dex::kTypeAnnotationsDirectoryItem:
+ return sizeof(dex::AnnotationsDirectoryItem); // Variable-length.
+ default:
+ return 1U; // Unhandled item. For sanity check assume size >= 1.
+ }
+}
+
/******** CodeItemParser ********/
// A parser to extract successive code items from a DEX image whose header has
@@ -109,8 +160,9 @@ class CodeItemParser {
const auto* code_item = source_.GetPointer<const dex::CodeItem>();
if (!code_item)
return kInvalidOffset;
- DCHECK_EQ(0U, code_item_offset % 4U);
+ DCHECK(Is32BitAligned(code_item_offset));
+ // TODO(huangs): Fail if |code_item->insns_size == 0| (Constraint A1).
// Skip instruction bytes.
if (!source_.GetArray<uint16_t>(code_item->insns_size))
return kInvalidOffset;
@@ -312,7 +364,7 @@ class InstructionReferenceReader : public ReferenceReader {
// ReferenceReader:
base::Optional<Reference> GetNext() override {
- for (;;) {
+ while (true) {
while (parser_.ReadNext()) {
const auto& v = parser_.value();
DCHECK_NE(v.instr, nullptr);
@@ -379,7 +431,10 @@ class ItemReferenceReader : public ReferenceReader {
"map_item.offset too large.");
static_assert(sizeof(decltype(map_item.size)) <= sizeof(offset_t),
"map_item.size too large.");
- if (lo < item_base_offset_) {
+ if (!item_base_offset_) {
+ // Empty item: Assign |cur_idx| to |num_items_| to skip everything.
+ cur_idx_ = num_items_;
+ } else if (lo < item_base_offset_) {
cur_idx_ = 0;
} else if (lo < OffsetOfIndex(num_items_)) {
cur_idx_ = (lo - item_base_offset_) / item_size_;
@@ -393,23 +448,37 @@ class ItemReferenceReader : public ReferenceReader {
// ReferenceReader:
base::Optional<Reference> GetNext() override {
- if (cur_idx_ >= num_items_)
- return base::nullopt;
-
- const offset_t item_offset = OffsetOfIndex(cur_idx_);
- const offset_t location = item_offset + rel_location_;
- // The general check is |location + reference_width > hi_|. However, by
- // assumption |hi_| and |lo_| do not straddle the body of a Reference. So
- // |reference_width| is unneeded.
- if (location >= hi_)
- return base::nullopt;
- const offset_t target = mapper_.Run(location);
- if (target == kInvalidOffset) {
- LOG(WARNING) << "Invalid item target at " << AsHex<8>(location) << ".";
- return base::nullopt;
+ while (cur_idx_ < num_items_) {
+ const offset_t item_offset = OffsetOfIndex(cur_idx_);
+ const offset_t location = item_offset + rel_location_;
+ // The general check is |location + reference_width > hi_|. However, by
+ // assumption |hi_| and |lo_| do not straddle the body of a Reference. So
+ // |reference_width| is unneeded.
+ if (location >= hi_)
+ break;
+ const offset_t target = mapper_.Run(location);
+
+ // kDexSentinelOffset (0) may appear for the following:
+ // - ProtoIdItem: parameters_off.
+ // - ClassDefItem: interfaces_off, annotations_off, class_data_off,
+ // static_values_off.
+ // - AnnotationsDirectoryItem: class_annotations_off.
+ // - AnnotationSetRefItem: annotations_off.
+ // kDexSentinelIndexAsOffset (0xFFFFFFFF) may appear for the following:
+ // - ClassDefItem: superclass_idx, source_file_idx.
+ if (target == kDexSentinelOffset || target == kDexSentinelIndexAsOffset) {
+ ++cur_idx_;
+ continue;
+ }
+
+ if (target == kInvalidOffset) {
+ LOG(WARNING) << "Invalid item target at " << AsHex<8>(location) << ".";
+ break;
+ }
+ ++cur_idx_;
+ return Reference{location, target};
}
- ++cur_idx_;
- return Reference{location, target};
+ return base::nullopt;
}
private:
@@ -426,11 +495,179 @@ class ItemReferenceReader : public ReferenceReader {
offset_t cur_idx_ = 0;
};
+// Parses a flattened jagged list of lists of items that looks like:
+// NTTT|NTT|NTTTT|N|NTT...
+// where |N| is an uint32_t representing the number of items in each sub-list,
+// and "T" is a fixed-size item (|item_width|) of type "T". On success, stores
+// the offset of each |T| into |item_offsets|, and returns true. Otherwise
+// (e.g., on finding any structural problem) returns false.
+bool ParseItemOffsets(ConstBufferView image,
+ const dex::MapItem& map_item,
+ size_t item_width,
+ std::vector<offset_t>* item_offsets) {
+ // Sanity check: |image| should at least fit |map_item.size| copies of "N".
+ if (!image.covers_array(map_item.offset, map_item.size, sizeof(uint32_t)))
+ return false;
+ BufferSource source = std::move(BufferSource(image).Skip(map_item.offset));
+ item_offsets->clear();
+ for (uint32_t i = 0; i < map_item.size; ++i) {
+ if (!source.AlignOn(image, 4U))
+ return false;
+ uint32_t unsafe_size;
+ if (!source.GetValue<uint32_t>(&unsafe_size))
+ return false;
+ DCHECK(Is32BitAligned(
+ base::checked_cast<offset_t>(source.begin() - image.begin())));
+ if (!source.covers_array(0, unsafe_size, item_width))
+ return false;
+ for (uint32_t j = 0; j < unsafe_size; ++j) {
+ item_offsets->push_back(
+ base::checked_cast<offset_t>(source.begin() - image.begin()));
+ source.Skip(item_width);
+ }
+ }
+ return true;
+}
+
+// Parses AnnotationDirectoryItems of the format (using RegEx) "(AF*M*P*)*",
+// where:
+// A = AnnotationsDirectoryItem (contains class annotation),
+// F = FieldAnnotation,
+// M = MethodAnnotation,
+// P = ParameterAnnotation.
+// On success, stores the offsets of each class, field, method and parameter
+// annotation for each item into |*_annotation_offsets|. Otherwise on finding
+// structural issues returns false.
+bool ParseAnnotationsDirectoryItems(
+ ConstBufferView image,
+ const dex::MapItem& annotations_directory_map_item,
+ std::vector<offset_t>* annotations_directory_item_offsets,
+ std::vector<offset_t>* field_annotation_offsets,
+ std::vector<offset_t>* method_annotation_offsets,
+ std::vector<offset_t>* parameter_annotation_offsets) {
+ // Sanity check: |image| should at least fit
+ // |annotations_directory_map_item.size| copies of "A".
+ if (!image.covers_array(annotations_directory_map_item.offset,
+ annotations_directory_map_item.size,
+ sizeof(dex::AnnotationsDirectoryItem))) {
+ return false;
+ }
+ BufferSource source = std::move(
+ BufferSource(image).Skip(annotations_directory_map_item.offset));
+ annotations_directory_item_offsets->clear();
+ field_annotation_offsets->clear();
+ method_annotation_offsets->clear();
+ parameter_annotation_offsets->clear();
+
+ // Helper to process sublists.
+ auto parse_list = [&source, image](uint32_t unsafe_size, size_t item_width,
+ std::vector<offset_t>* item_offsets) {
+ DCHECK(Is32BitAligned(
+ base::checked_cast<offset_t>(source.begin() - image.begin())));
+ if (!source.covers_array(0, unsafe_size, item_width))
+ return false;
+ item_offsets->reserve(item_offsets->size() + unsafe_size);
+ for (uint32_t i = 0; i < unsafe_size; ++i) {
+ item_offsets->push_back(
+ base::checked_cast<offset_t>(source.begin() - image.begin()));
+ source.Skip(item_width);
+ }
+ return true;
+ };
+
+ annotations_directory_item_offsets->reserve(
+ annotations_directory_map_item.size);
+ for (uint32_t i = 0; i < annotations_directory_map_item.size; ++i) {
+ if (!source.AlignOn(image, 4U))
+ return false;
+ // Parse header.
+ annotations_directory_item_offsets->push_back(
+ base::checked_cast<offset_t>(source.begin() - image.begin()));
+ dex::AnnotationsDirectoryItem unsafe_annotations_directory_item;
+ if (!source.GetValue(&unsafe_annotations_directory_item))
+ return false;
+ // Parse sublists.
+ if (!(parse_list(unsafe_annotations_directory_item.fields_size,
+ sizeof(dex::FieldAnnotation), field_annotation_offsets) &&
+ parse_list(unsafe_annotations_directory_item.annotated_methods_size,
+ sizeof(dex::MethodAnnotation),
+ method_annotation_offsets) &&
+ parse_list(
+ unsafe_annotations_directory_item.annotated_parameters_size,
+ sizeof(dex::ParameterAnnotation),
+ parameter_annotation_offsets))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/******** CachedItemListReferenceReader ********/
+
+// A class that takes sorted |item_offsets|, and emits all member variable of
+// interest (MVIs) that fall inside |[lo, hi)|. The MVI of each item has
+// location of |rel_location| from item offset, and has target extracted with
+// |mapper| (which performs validation). By the "atomicity assumption",
+// [|lo, hi)| never cut across an MVI.
+class CachedItemListReferenceReader : public ReferenceReader {
+ public:
+ // A function that takes an MVI's location and emit its target offset.
+ using Mapper = base::RepeatingCallback<offset_t(offset_t)>;
+
+ CachedItemListReferenceReader(offset_t lo,
+ offset_t hi,
+ uint32_t rel_location,
+ const std::vector<offset_t>& item_offsets,
+ Mapper&& mapper)
+ : hi_(hi),
+ rel_location_(rel_location),
+ end_it_(item_offsets.cend()),
+ mapper_(mapper) {
+ cur_it_ = std::upper_bound(item_offsets.cbegin(), item_offsets.cend(), lo);
+ // Adding |rel_location_| is necessary as references can be offset from the
+ // start of the item.
+ if (cur_it_ != item_offsets.begin() && *(cur_it_ - 1) + rel_location_ >= lo)
+ --cur_it_;
+ }
+
+ // ReferenceReader:
+ base::Optional<Reference> GetNext() override {
+ while (cur_it_ < end_it_) {
+ const offset_t location = *cur_it_ + rel_location_;
+ if (location >= hi_) // Check is simplified by atomicity assumption.
+ break;
+ const offset_t target = mapper_.Run(location);
+ if (target == kInvalidOffset) {
+ LOG(WARNING) << "Invalid item target at " << AsHex<8>(location) << ".";
+ break;
+ }
+ ++cur_it_;
+
+ // kDexSentinelOffset is a sentinel for;
+ // - AnnotationsDirectoryItem: class_annotations_off
+ if (target == kDexSentinelOffset)
+ continue;
+ return Reference{location, target};
+ }
+ return base::nullopt;
+ }
+
+ private:
+ const offset_t hi_;
+ const uint32_t rel_location_;
+ const std::vector<offset_t>::const_iterator end_it_;
+ const Mapper mapper_;
+ std::vector<offset_t>::const_iterator cur_it_;
+
+ DISALLOW_COPY_AND_ASSIGN(CachedItemListReferenceReader);
+};
+
// Reads an INT index at |location| in |image| and translates the index to the
// offset of a fixed-size item specified by |target_map_item| and
// |target_item_size|. Returns the target offset if valid, or kInvalidOffset
-// otherwise. This is compatible with InstructionReferenceReader::Mapper and
-// ItemReferenceReader::Mapper.
+// otherwise. This is compatible with
+// CachedReferenceListReferenceReader::Mapper,
+// InstructionReferenceReader::Mapper, and ItemReferenceReader::Mapper.
template <typename INT>
static offset_t ReadTargetIndex(ConstBufferView image,
const dex::MapItem& target_map_item,
@@ -439,12 +676,36 @@ static offset_t ReadTargetIndex(ConstBufferView image,
static_assert(sizeof(INT) <= sizeof(offset_t),
"INT may not fit into offset_t.");
const offset_t unsafe_idx = image.read<INT>(location);
+ // kDexSentinalIndexAsOffset (0xFFFFFFFF) is a sentinel for
+ // - ClassDefItem: superclass_idx, source_file_idx.
+ if (unsafe_idx == kDexSentinelIndexAsOffset)
+ return unsafe_idx;
if (unsafe_idx >= target_map_item.size)
return kInvalidOffset;
return target_map_item.offset +
base::checked_cast<offset_t>(unsafe_idx * target_item_size);
}
+// Reads uint32_t value in |image| at (valid) |location| and checks whether it
+// is a safe offset of a fixed-size item. Returns the target offset (possibly a
+// sentinel) if valid, or kInvalidOffset otherwise. This is compatible with
+// CachedReferenceListReferenceReader::Mapper,
+// InstructionReferenceReader::Mapper, and ItemReferenceReader::Mapper.
+static offset_t ReadTargetOffset32(ConstBufferView image, offset_t location) {
+ const offset_t unsafe_target =
+ static_cast<offset_t>(image.read<uint32_t>(location));
+ // Skip and don't validate kDexSentinelOffset as it is indicative of an
+ // empty reference.
+ if (unsafe_target == kDexSentinelOffset)
+ return unsafe_target;
+
+ // TODO(huangs): Check that |unsafe_target| is within the correct data
+ // section.
+ if (unsafe_target >= image.size())
+ return kInvalidOffset;
+ return unsafe_target;
+}
+
/******** ReferenceWriterAdaptor ********/
// A ReferenceWriter that adapts a callback that performs type-specific
@@ -473,12 +734,17 @@ static void WriteTargetIndex(const dex::MapItem& target_map_item,
size_t target_item_size,
Reference ref,
MutableBufferView image) {
- const size_t idx = (ref.target - target_map_item.offset) / target_item_size;
+ const size_t unsafe_idx =
+ (ref.target - target_map_item.offset) / target_item_size;
// Verify that index is within bound.
- DCHECK_LT(idx, target_map_item.size);
+ if (unsafe_idx >= target_map_item.size) {
+ LOG(ERROR) << "Target index out of bounds at: " << AsHex<8>(ref.location)
+ << ".";
+ return;
+ }
// Verify that |ref.target| points to start of item.
- DCHECK_EQ(ref.target, target_map_item.offset + idx * target_item_size);
- image.write<INT>(ref.location, base::checked_cast<INT>(idx));
+ DCHECK_EQ(ref.target, target_map_item.offset + unsafe_idx * target_item_size);
+ image.write<INT>(ref.location, base::checked_cast<INT>(unsafe_idx));
}
// Buffer for ReadDexHeader() to optionally return results.
@@ -551,30 +817,103 @@ std::string DisassemblerDex::GetExeTypeString() const {
std::vector<ReferenceGroup> DisassemblerDex::MakeReferenceGroups() const {
// Must follow DisassemblerDex::ReferenceType order. Initialized on first use.
return {
+ {{4, TypeTag(kTypeIdToDescriptorStringId), PoolTag(kStringId)},
+ &DisassemblerDex::MakeReadTypeIdToDescriptorStringId32,
+ &DisassemblerDex::MakeWriteStringId32},
+ {{4, TypeTag(kProtoIdToShortyStringId), PoolTag(kStringId)},
+ &DisassemblerDex::MakeReadProtoIdToShortyStringId32,
+ &DisassemblerDex::MakeWriteStringId32},
{{4, TypeTag(kFieldIdToNameStringId), PoolTag(kStringId)},
&DisassemblerDex::MakeReadFieldToNameStringId32,
&DisassemblerDex::MakeWriteStringId32},
+ {{4, TypeTag(kMethodIdToNameStringId), PoolTag(kStringId)},
+ &DisassemblerDex::MakeReadMethodIdToNameStringId32,
+ &DisassemblerDex::MakeWriteStringId32},
+ {{4, TypeTag(kClassDefToSourceFileStringId), PoolTag(kStringId)},
+ &DisassemblerDex::MakeReadClassDefToSourceFileStringId32,
+ &DisassemblerDex::MakeWriteStringId32},
{{2, TypeTag(kCodeToStringId16), PoolTag(kStringId)},
&DisassemblerDex::MakeReadCodeToStringId16,
&DisassemblerDex::MakeWriteStringId16},
{{4, TypeTag(kCodeToStringId32), PoolTag(kStringId)},
&DisassemblerDex::MakeReadCodeToStringId32,
&DisassemblerDex::MakeWriteStringId32},
+ {{4, TypeTag(kProtoIdToReturnTypeId), PoolTag(kTypeId)},
+ &DisassemblerDex::MakeReadProtoIdToReturnTypeId32,
+ &DisassemblerDex::MakeWriteTypeId32},
{{2, TypeTag(kFieldIdToClassTypeId), PoolTag(kTypeId)},
&DisassemblerDex::MakeReadFieldToClassTypeId16,
&DisassemblerDex::MakeWriteTypeId16},
{{2, TypeTag(kFieldIdToTypeId), PoolTag(kTypeId)},
&DisassemblerDex::MakeReadFieldToTypeId16,
&DisassemblerDex::MakeWriteTypeId16},
+ {{2, TypeTag(kMethodIdToClassTypeId), PoolTag(kTypeId)},
+ &DisassemblerDex::MakeReadMethodIdToClassTypeId16,
+ &DisassemblerDex::MakeWriteTypeId16},
+ {{4, TypeTag(kClassDefToClassTypeId), PoolTag(kTypeId)},
+ &DisassemblerDex::MakeReadClassDefToClassTypeId32,
+ &DisassemblerDex::MakeWriteTypeId32},
+ {{4, TypeTag(kClassDefToSuperClassTypeId), PoolTag(kTypeId)},
+ &DisassemblerDex::MakeReadClassDefToSuperClassTypeId32,
+ &DisassemblerDex::MakeWriteTypeId32},
+ {{2, TypeTag(kTypeListToTypeId), PoolTag(kTypeId)},
+ &DisassemblerDex::MakeReadTypeListToTypeId16,
+ &DisassemblerDex::MakeWriteTypeId16},
{{2, TypeTag(kCodeToTypeId), PoolTag(kTypeId)},
&DisassemblerDex::MakeReadCodeToTypeId16,
&DisassemblerDex::MakeWriteTypeId16},
+ {{2, TypeTag(kMethodIdToProtoId), PoolTag(kProtoId)},
+ &DisassemblerDex::MakeReadMethodIdToProtoId16,
+ &DisassemblerDex::MakeWriteProtoId16},
{{2, TypeTag(kCodeToFieldId), PoolTag(kFieldId)},
&DisassemblerDex::MakeReadCodeToFieldId16,
&DisassemblerDex::MakeWriteFieldId16},
+ {{4, TypeTag(kAnnotationsDirectoryToFieldId), PoolTag(kFieldId)},
+ &DisassemblerDex::MakeReadAnnotationsDirectoryToFieldId32,
+ &DisassemblerDex::MakeWriteFieldId32},
{{2, TypeTag(kCodeToMethodId), PoolTag(kMethodId)},
&DisassemblerDex::MakeReadCodeToMethodId16,
&DisassemblerDex::MakeWriteMethodId16},
+ {{4, TypeTag(kAnnotationsDirectoryToMethodId), PoolTag(kMethodId)},
+ &DisassemblerDex::MakeReadAnnotationsDirectoryToMethodId32,
+ &DisassemblerDex::MakeWriteMethodId32},
+ {{4, TypeTag(kAnnotationsDirectoryToParameterMethodId),
+ PoolTag(kMethodId)},
+ &DisassemblerDex::MakeReadAnnotationsDirectoryToParameterMethodId32,
+ &DisassemblerDex::MakeWriteMethodId32},
+ {{4, TypeTag(kProtoIdToParametersTypeList), PoolTag(kTypeList)},
+ &DisassemblerDex::MakeReadProtoIdToParametersTypeList,
+ &DisassemblerDex::MakeWriteAbs32},
+ {{4, TypeTag(kClassDefToInterfacesTypeList), PoolTag(kTypeList)},
+ &DisassemblerDex::MakeReadClassDefToInterfacesTypeList,
+ &DisassemblerDex::MakeWriteAbs32},
+ {{4, TypeTag(kAnnotationsDirectoryToParameterAnnotationSetRef),
+ PoolTag(kAnnotationSetRefList)},
+ &DisassemblerDex::
+ MakeReadAnnotationsDirectoryToParameterAnnotationSetRef,
+ &DisassemblerDex::MakeWriteAbs32},
+ {{4, TypeTag(kAnnotationSetRefListToAnnotationSet),
+ PoolTag(kAnnotionSet)},
+ &DisassemblerDex::MakeReadAnnotationSetRefListToAnnotationSet,
+ &DisassemblerDex::MakeWriteAbs32},
+ {{4, TypeTag(kAnnotationsDirectoryToClassAnnotationSet),
+ PoolTag(kAnnotionSet)},
+ &DisassemblerDex::MakeReadAnnotationsDirectoryToClassAnnotationSet,
+ &DisassemblerDex::MakeWriteAbs32},
+ {{4, TypeTag(kAnnotationsDirectoryToFieldAnnotationSet),
+ PoolTag(kAnnotionSet)},
+ &DisassemblerDex::MakeReadAnnotationsDirectoryToFieldAnnotationSet,
+ &DisassemblerDex::MakeWriteAbs32},
+ {{4, TypeTag(kAnnotationsDirectoryToMethodAnnotationSet),
+ PoolTag(kAnnotionSet)},
+ &DisassemblerDex::MakeReadAnnotationsDirectoryToMethodAnnotationSet,
+ &DisassemblerDex::MakeWriteAbs32},
+ {{4, TypeTag(kClassDefToClassData), PoolTag(kClassData)},
+ &DisassemblerDex::MakeReadClassDefToClassData,
+ &DisassemblerDex::MakeWriteAbs32},
+ {{1, TypeTag(kCodeToRelCode8), PoolTag(kCode)},
+ &DisassemblerDex::MakeReadCodeToRelCode8,
+ &DisassemblerDex::MakeWriteRelCode8},
{{2, TypeTag(kCodeToRelCode16), PoolTag(kCode)},
&DisassemblerDex::MakeReadCodeToRelCode16,
&DisassemblerDex::MakeWriteRelCode16},
@@ -584,27 +923,70 @@ std::vector<ReferenceGroup> DisassemblerDex::MakeReferenceGroups() const {
{{4, TypeTag(kStringIdToStringData), PoolTag(kStringData)},
&DisassemblerDex::MakeReadStringIdToStringData,
&DisassemblerDex::MakeWriteAbs32},
+ {{4, TypeTag(kAnnotationSetToAnnotation), PoolTag(kAnnotation)},
+ &DisassemblerDex::MakeReadAnnotationSetToAnnotation,
+ &DisassemblerDex::MakeWriteAbs32},
+ {{4, TypeTag(kClassDefToStaticValuesEncodedArray),
+ PoolTag(kEncodedArray)},
+ &DisassemblerDex::MakeReadClassDefToStaticValuesEncodedArray,
+ &DisassemblerDex::MakeWriteAbs32},
+ {{4, TypeTag(kClassDefToAnnotationDirectory),
+ PoolTag(kAnnotationsDirectory)},
+ &DisassemblerDex::MakeReadClassDefToAnnotationDirectory,
+ &DisassemblerDex::MakeWriteAbs32},
};
}
std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadStringIdToStringData(
offset_t lo,
offset_t hi) {
- auto mapper = base::BindRepeating(
- [](ConstBufferView image, offset_t location) -> offset_t {
- const offset_t unsafe_target =
- image.read<decltype(dex::StringIdItem::string_data_off)>(location);
- // TODO(huangs): Check that |unsafe_target| lies in string data item.
- if (unsafe_target >= image.size())
- return kInvalidOffset;
- return unsafe_target;
- },
- image_);
+ // dex::StringIdItem::string_data_off mapper.
+ auto mapper = base::BindRepeating(ReadTargetOffset32, image_);
return std::make_unique<ItemReferenceReader>(
lo, hi, string_map_item_, sizeof(dex::StringIdItem),
offsetof(dex::StringIdItem, string_data_off), std::move(mapper));
}
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadTypeIdToDescriptorStringId32(offset_t lo,
+ offset_t hi) {
+ auto mapper = base::BindRepeating(
+ ReadTargetIndex<decltype(dex::TypeIdItem::descriptor_idx)>, image_,
+ string_map_item_, sizeof(dex::StringIdItem));
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, type_map_item_, sizeof(dex::TypeIdItem),
+ offsetof(dex::TypeIdItem, descriptor_idx), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadProtoIdToShortyStringId32(offset_t lo, offset_t hi) {
+ auto mapper = base::BindRepeating(
+ ReadTargetIndex<decltype(dex::ProtoIdItem::shorty_idx)>, image_,
+ string_map_item_, sizeof(dex::StringIdItem));
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, proto_map_item_, sizeof(dex::ProtoIdItem),
+ offsetof(dex::ProtoIdItem, shorty_idx), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadProtoIdToReturnTypeId32(offset_t lo, offset_t hi) {
+ auto mapper = base::BindRepeating(
+ ReadTargetIndex<decltype(dex::ProtoIdItem::return_type_idx)>, image_,
+ type_map_item_, sizeof(dex::TypeIdItem));
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, proto_map_item_, sizeof(dex::ProtoIdItem),
+ offsetof(dex::ProtoIdItem, return_type_idx), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadProtoIdToParametersTypeList(offset_t lo, offset_t hi) {
+ // dex::ProtoIdItem::parameters_off mapper.
+ auto mapper = base::BindRepeating(ReadTargetOffset32, image_);
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, proto_map_item_, sizeof(dex::ProtoIdItem),
+ offsetof(dex::ProtoIdItem, parameters_off), std::move(mapper));
+}
+
std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadFieldToClassTypeId16(
offset_t lo,
offset_t hi) {
@@ -638,6 +1020,217 @@ std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadFieldToNameStringId32(
offsetof(dex::FieldIdItem, name_idx), std::move(mapper));
}
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadMethodIdToClassTypeId16(offset_t lo, offset_t hi) {
+ auto mapper = base::BindRepeating(
+ ReadTargetIndex<decltype(dex::MethodIdItem::class_idx)>, image_,
+ type_map_item_, sizeof(dex::TypeIdItem));
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, method_map_item_, sizeof(dex::MethodIdItem),
+ offsetof(dex::MethodIdItem, class_idx), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadMethodIdToProtoId16(
+ offset_t lo,
+ offset_t hi) {
+ auto mapper = base::BindRepeating(
+ ReadTargetIndex<decltype(dex::MethodIdItem::proto_idx)>, image_,
+ proto_map_item_, sizeof(dex::ProtoIdItem));
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, method_map_item_, sizeof(dex::MethodIdItem),
+ offsetof(dex::MethodIdItem, proto_idx), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadMethodIdToNameStringId32(offset_t lo, offset_t hi) {
+ auto mapper = base::BindRepeating(
+ ReadTargetIndex<decltype(dex::MethodIdItem::name_idx)>, image_,
+ string_map_item_, sizeof(dex::StringIdItem));
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, method_map_item_, sizeof(dex::MethodIdItem),
+ offsetof(dex::MethodIdItem, name_idx), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadClassDefToClassTypeId32(offset_t lo, offset_t hi) {
+ auto mapper = base::BindRepeating(
+ ReadTargetIndex<decltype(dex::ClassDefItem::superclass_idx)>, image_,
+ type_map_item_, sizeof(dex::TypeIdItem));
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, class_def_map_item_, sizeof(dex::ClassDefItem),
+ offsetof(dex::ClassDefItem, class_idx), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadClassDefToSuperClassTypeId32(offset_t lo,
+ offset_t hi) {
+ auto mapper = base::BindRepeating(
+ ReadTargetIndex<decltype(dex::ClassDefItem::superclass_idx)>, image_,
+ type_map_item_, sizeof(dex::TypeIdItem));
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, class_def_map_item_, sizeof(dex::ClassDefItem),
+ offsetof(dex::ClassDefItem, superclass_idx), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadClassDefToInterfacesTypeList(offset_t lo,
+ offset_t hi) {
+ // dex::ClassDefItem::interfaces_off mapper.
+ auto mapper = base::BindRepeating(ReadTargetOffset32, image_);
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, class_def_map_item_, sizeof(dex::ClassDefItem),
+ offsetof(dex::ClassDefItem, interfaces_off), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadClassDefToSourceFileStringId32(offset_t lo,
+ offset_t hi) {
+ auto mapper = base::BindRepeating(
+ ReadTargetIndex<decltype(dex::ClassDefItem::source_file_idx)>, image_,
+ string_map_item_, sizeof(dex::StringIdItem));
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, class_def_map_item_, sizeof(dex::ClassDefItem),
+ offsetof(dex::ClassDefItem, source_file_idx), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadClassDefToAnnotationDirectory(offset_t lo,
+ offset_t hi) {
+ // dex::ClassDefItem::annotations_off mapper.
+ auto mapper = base::BindRepeating(ReadTargetOffset32, image_);
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, class_def_map_item_, sizeof(dex::ClassDefItem),
+ offsetof(dex::ClassDefItem, annotations_off), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadClassDefToClassData(
+ offset_t lo,
+ offset_t hi) {
+ // dex::ClassDefItem::class_data_off mapper.
+ auto mapper = base::BindRepeating(ReadTargetOffset32, image_);
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, class_def_map_item_, sizeof(dex::ClassDefItem),
+ offsetof(dex::ClassDefItem, class_data_off), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadClassDefToStaticValuesEncodedArray(offset_t lo,
+ offset_t hi) {
+ // dex::ClassDefItem::static_values_off mapper.
+ auto mapper = base::BindRepeating(ReadTargetOffset32, image_);
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, class_def_map_item_, sizeof(dex::ClassDefItem),
+ offsetof(dex::ClassDefItem, static_values_off), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadTypeListToTypeId16(
+ offset_t lo,
+ offset_t hi) {
+ auto mapper =
+ base::BindRepeating(ReadTargetIndex<decltype(dex::TypeItem::type_idx)>,
+ image_, type_map_item_, sizeof(dex::TypeIdItem));
+ return std::make_unique<CachedItemListReferenceReader>(
+ lo, hi, offsetof(dex::TypeItem, type_idx), type_list_offsets_,
+ std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadAnnotationSetToAnnotation(offset_t lo, offset_t hi) {
+ // dex::AnnotationOffItem::annotation_off mapper.
+ auto mapper = base::BindRepeating(ReadTargetOffset32, image_);
+ return std::make_unique<CachedItemListReferenceReader>(
+ lo, hi, offsetof(dex::AnnotationOffItem, annotation_off),
+ annotation_set_offsets_, std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadAnnotationSetRefListToAnnotationSet(offset_t lo,
+ offset_t hi) {
+ // dex::AnnotationSetRefItem::annotations_off mapper.
+ auto mapper = base::BindRepeating(ReadTargetOffset32, image_);
+ return std::make_unique<CachedItemListReferenceReader>(
+ lo, hi, offsetof(dex::AnnotationSetRefItem, annotations_off),
+ annotation_set_ref_list_offsets_, std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadAnnotationsDirectoryToClassAnnotationSet(offset_t lo,
+ offset_t hi) {
+ // dex::AnnotationsDirectoryItem::class_annotations_off mapper.
+ auto mapper = base::BindRepeating(ReadTargetOffset32, image_);
+ return std::make_unique<CachedItemListReferenceReader>(
+ lo, hi, offsetof(dex::AnnotationsDirectoryItem, class_annotations_off),
+ annotations_directory_item_offsets_, std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadAnnotationsDirectoryToFieldId32(offset_t lo,
+ offset_t hi) {
+ auto mapper = base::BindRepeating(
+ ReadTargetIndex<decltype(dex::FieldAnnotation::field_idx)>, image_,
+ field_map_item_, sizeof(dex::FieldIdItem));
+ return std::make_unique<CachedItemListReferenceReader>(
+ lo, hi, offsetof(dex::FieldAnnotation, field_idx),
+ annotations_directory_item_field_annotation_offsets_, std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadAnnotationsDirectoryToFieldAnnotationSet(offset_t lo,
+ offset_t hi) {
+ // dex::FieldAnnotation::annotations_off mapper.
+ auto mapper = base::BindRepeating(ReadTargetOffset32, image_);
+ return std::make_unique<CachedItemListReferenceReader>(
+ lo, hi, offsetof(dex::FieldAnnotation, annotations_off),
+ annotations_directory_item_field_annotation_offsets_, std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadAnnotationsDirectoryToMethodId32(offset_t lo,
+ offset_t hi) {
+ auto mapper = base::BindRepeating(
+ ReadTargetIndex<decltype(dex::MethodAnnotation::method_idx)>, image_,
+ method_map_item_, sizeof(dex::MethodIdItem));
+ return std::make_unique<CachedItemListReferenceReader>(
+ lo, hi, offsetof(dex::MethodAnnotation, method_idx),
+ annotations_directory_item_method_annotation_offsets_, std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadAnnotationsDirectoryToMethodAnnotationSet(
+ offset_t lo,
+ offset_t hi) {
+ // dex::MethodAnnotation::annotations_off mapper.
+ auto mapper = base::BindRepeating(ReadTargetOffset32, image_);
+ return std::make_unique<CachedItemListReferenceReader>(
+ lo, hi, offsetof(dex::MethodAnnotation, annotations_off),
+ annotations_directory_item_method_annotation_offsets_, std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadAnnotationsDirectoryToParameterMethodId32(
+ offset_t lo,
+ offset_t hi) {
+ auto mapper = base::BindRepeating(
+ ReadTargetIndex<decltype(dex::ParameterAnnotation::method_idx)>, image_,
+ method_map_item_, sizeof(dex::MethodIdItem));
+ return std::make_unique<CachedItemListReferenceReader>(
+ lo, hi, offsetof(dex::ParameterAnnotation, method_idx),
+ annotations_directory_item_parameter_annotation_offsets_,
+ std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadAnnotationsDirectoryToParameterAnnotationSetRef(
+ offset_t lo,
+ offset_t hi) {
+ // dex::ParameterAnnotation::annotations_off mapper.
+ auto mapper = base::BindRepeating(ReadTargetOffset32, image_);
+ return std::make_unique<CachedItemListReferenceReader>(
+ lo, hi, offsetof(dex::ParameterAnnotation, annotations_off),
+ annotations_directory_item_parameter_annotation_offsets_,
+ std::move(mapper));
+}
+
std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToStringId16(
offset_t lo,
offset_t hi) {
@@ -739,6 +1332,34 @@ std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToMethodId16(
image_, lo, hi, code_item_offsets_, std::move(filter), std::move(mapper));
}
+std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToRelCode8(
+ offset_t lo,
+ offset_t hi) {
+ auto filter = base::BindRepeating(
+ [](const InstructionParser::Value& value) -> offset_t {
+ if (value.instr->format == dex::FormatId::t &&
+ value.instr->opcode == 0x28) { // goto
+ // +AA from e.g., goto +AA.
+ return value.instr_offset + 1;
+ }
+ return kInvalidOffset;
+ });
+ auto mapper = base::BindRepeating(
+ [](DisassemblerDex* dis, offset_t location) {
+ // Address is relative to the current instruction, which begins 1 unit
+ // before |location|. This needs to be subtracted out. Also, store as
+ // int32_t so |unsafe_delta - 1| won't underflow!
+ int32_t unsafe_delta = dis->image_.read<int8_t>(location);
+ offset_t unsafe_target = static_cast<offset_t>(
+ location + (unsafe_delta - 1) * kInstrUnitSize);
+ // TODO(huangs): Check that |unsafe_target| stays within code item.
+ return unsafe_target;
+ },
+ base::Unretained(this));
+ return std::make_unique<InstructionReferenceReader>(
+ image_, lo, hi, code_item_offsets_, std::move(filter), std::move(mapper));
+}
+
std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToRelCode16(
offset_t lo,
offset_t hi) {
@@ -788,12 +1409,17 @@ std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToRelCode32(
auto mapper = base::BindRepeating(
[](DisassemblerDex* dis, offset_t location) {
// Address is relative to the current instruction, which begins 1 unit
- // before |location|. This needs to be subtracted out.
- int32_t unsafe_delta = dis->image_.read<int32_t>(location);
- offset_t unsafe_target = static_cast<offset_t>(
- location + (unsafe_delta - 1) * kInstrUnitSize);
+ // before |location|. This needs to be subtracted out. Use int64_t to
+ // avoid underflow and overflow.
+ int64_t unsafe_delta = dis->image_.read<int32_t>(location);
+ int64_t unsafe_target = location + (unsafe_delta - 1) * kInstrUnitSize;
+
// TODO(huangs): Check that |unsafe_target| stays within code item.
- return unsafe_target;
+ offset_t checked_unsafe_target =
+ static_cast<offset_t>(base::CheckedNumeric<offset_t>(unsafe_target)
+ .ValueOrDefault(kInvalidOffset));
+ return checked_unsafe_target < kOffsetBound ? checked_unsafe_target
+ : kInvalidOffset;
},
base::Unretained(this));
return std::make_unique<InstructionReferenceReader>(
@@ -821,6 +1447,20 @@ std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteTypeId16(
return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer));
}
+std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteTypeId32(
+ MutableBufferView image) {
+ auto writer = base::BindRepeating(WriteTargetIndex<uint32_t>, type_map_item_,
+ sizeof(dex::TypeIdItem));
+ return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer));
+}
+
+std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteProtoId16(
+ MutableBufferView image) {
+ auto writer = base::BindRepeating(WriteTargetIndex<uint16_t>, proto_map_item_,
+ sizeof(dex::ProtoIdItem));
+ return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer));
+}
+
std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteFieldId16(
MutableBufferView image) {
auto writer = base::BindRepeating(WriteTargetIndex<uint16_t>, field_map_item_,
@@ -828,6 +1468,13 @@ std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteFieldId16(
return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer));
}
+std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteFieldId32(
+ MutableBufferView image) {
+ auto writer = base::BindRepeating(WriteTargetIndex<uint32_t>, field_map_item_,
+ sizeof(dex::FieldIdItem));
+ return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer));
+}
+
std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteMethodId16(
MutableBufferView image) {
auto writer = base::BindRepeating(
@@ -835,15 +1482,46 @@ std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteMethodId16(
return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer));
}
+std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteMethodId32(
+ MutableBufferView image) {
+ auto writer = base::BindRepeating(
+ WriteTargetIndex<uint32_t>, method_map_item_, sizeof(dex::MethodIdItem));
+ return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer));
+}
+
+std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteRelCode8(
+ MutableBufferView image) {
+ auto writer = base::BindRepeating([](Reference ref, MutableBufferView image) {
+ ptrdiff_t unsafe_byte_diff =
+ static_cast<ptrdiff_t>(ref.target) - ref.location;
+ DCHECK_EQ(0, unsafe_byte_diff % kInstrUnitSize);
+ // |delta| is relative to start of instruction, which is 1 unit before
+ // |ref.location|. The subtraction above removed too much, so +1 to fix.
+ base::CheckedNumeric<int8_t> delta((unsafe_byte_diff / kInstrUnitSize) + 1);
+ if (!delta.IsValid()) {
+ LOG(ERROR) << "Invalid reference at: " << AsHex<8>(ref.location) << ".";
+ return;
+ }
+ image.write<int8_t>(ref.location, delta.ValueOrDie());
+ });
+ return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer));
+}
+
std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteRelCode16(
MutableBufferView image) {
auto writer = base::BindRepeating([](Reference ref, MutableBufferView image) {
- ptrdiff_t byte_diff = static_cast<ptrdiff_t>(ref.target) - ref.location;
- DCHECK_EQ(0, byte_diff % kInstrUnitSize);
+ ptrdiff_t unsafe_byte_diff =
+ static_cast<ptrdiff_t>(ref.target) - ref.location;
+ DCHECK_EQ(0, unsafe_byte_diff % kInstrUnitSize);
// |delta| is relative to start of instruction, which is 1 unit before
// |ref.location|. The subtraction above removed too much, so +1 to fix.
- ptrdiff_t delta = (byte_diff / kInstrUnitSize) + 1;
- image.write<int16_t>(ref.location, base::checked_cast<int16_t>(delta));
+ base::CheckedNumeric<int16_t> delta((unsafe_byte_diff / kInstrUnitSize) +
+ 1);
+ if (!delta.IsValid()) {
+ LOG(ERROR) << "Invalid reference at: " << AsHex<8>(ref.location) << ".";
+ return;
+ }
+ image.write<int16_t>(ref.location, delta.ValueOrDie());
});
return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer));
}
@@ -851,10 +1529,18 @@ std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteRelCode16(
std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteRelCode32(
MutableBufferView image) {
auto writer = base::BindRepeating([](Reference ref, MutableBufferView image) {
- ptrdiff_t byte_diff = static_cast<ptrdiff_t>(ref.target) - ref.location;
- DCHECK_EQ(0, byte_diff % kInstrUnitSize);
- ptrdiff_t delta = (byte_diff / kInstrUnitSize) + 1;
- image.write<int32_t>(ref.location, base::checked_cast<int32_t>(delta));
+ ptrdiff_t unsafe_byte_diff =
+ static_cast<ptrdiff_t>(ref.target) - ref.location;
+ DCHECK_EQ(0, unsafe_byte_diff % kInstrUnitSize);
+ // |delta| is relative to start of instruction, which is 1 unit before
+ // |ref.location|. The subtraction above removed too much, so +1 to fix.
+ base::CheckedNumeric<int32_t> delta((unsafe_byte_diff / kInstrUnitSize) +
+ 1);
+ if (!delta.IsValid()) {
+ LOG(ERROR) << "Invalid reference at: " << AsHex<8>(ref.location) << ".";
+ return;
+ }
+ image.write<int32_t>(ref.location, delta.ValueOrDie());
});
return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer));
}
@@ -899,32 +1585,72 @@ bool DisassemblerDex::ParseHeader() {
return false;
// Read and validate map list, ensuring that required item types are present.
+ // - GetItemBaseSize() should have an entry for each item.
+ // - dex::kTypeCodeItem is actually not required; it's possible to have a DEX
+ // file with classes that have no code. However, this is unlikely to appear
+ // in application, so for simplicity we require DEX files to have code.
std::set<uint16_t> required_item_types = {
- dex::kTypeStringIdItem, dex::kTypeTypeIdItem, dex::kTypeFieldIdItem,
- dex::kTypeMethodIdItem, dex::kTypeCodeItem};
+ dex::kTypeStringIdItem, dex::kTypeTypeIdItem, dex::kTypeProtoIdItem,
+ dex::kTypeFieldIdItem, dex::kTypeMethodIdItem, dex::kTypeClassDefItem,
+ dex::kTypeTypeList, dex::kTypeCodeItem,
+ };
for (offset_t i = 0; i < list_size; ++i) {
const dex::MapItem* item = &item_list[i];
- // Sanity check to reject unreasonably large |item->size|.
- // TODO(huangs): Implement a more stringent check.
- if (!image_.covers({item->offset, item->size}))
+ // Reject unreasonably large |item->size|.
+ size_t item_size = GetItemBaseSize(item->type);
+ // Confusing name: |item->size| is actually the number of items.
+ if (!image_.covers_array(item->offset, item->size, item_size))
return false;
if (!map_item_map_.insert(std::make_pair(item->type, item)).second)
return false; // A given type must appear at most once.
required_item_types.erase(item->type);
}
+ // TODO(huangs): Replace this with guards throughout file.
if (!required_item_types.empty())
return false;
// Make local copies of main map items.
string_map_item_ = *map_item_map_[dex::kTypeStringIdItem];
type_map_item_ = *map_item_map_[dex::kTypeTypeIdItem];
+ proto_map_item_ = *map_item_map_[dex::kTypeProtoIdItem];
field_map_item_ = *map_item_map_[dex::kTypeFieldIdItem];
method_map_item_ = *map_item_map_[dex::kTypeMethodIdItem];
+ class_def_map_item_ = *map_item_map_[dex::kTypeClassDefItem];
+ type_list_map_item_ = *map_item_map_[dex::kTypeTypeList];
code_map_item_ = *map_item_map_[dex::kTypeCodeItem];
- // Iteratively extract variable-length code items blocks. Any failure would
- // indicate invalid DEX. Success indicates that no structural problem is
- // found. However, contained instructions still need validation on use.
+ // The following types are optional and may not be present in every DEX file.
+ if (map_item_map_.count(dex::kTypeAnnotationSetRefList)) {
+ annotation_set_ref_list_map_item_ =
+ *map_item_map_[dex::kTypeAnnotationSetRefList];
+ }
+ if (map_item_map_.count(dex::kTypeAnnotationSetItem))
+ annotation_set_map_item_ = *map_item_map_[dex::kTypeAnnotationSetItem];
+ if (map_item_map_.count(dex::kTypeAnnotationsDirectoryItem)) {
+ annotations_directory_map_item_ =
+ *map_item_map_[dex::kTypeAnnotationsDirectoryItem];
+ }
+
+ // Iteratively parse variable length lists, annotations directory items, and
+ // code items blocks. Any failure would indicate invalid DEX. Success
+ // indicates that no structural problem is found. However, contained
+ // references data read from parsed items still require validation.
+ if (!(ParseItemOffsets(image_, type_list_map_item_, sizeof(dex::TypeItem),
+ &type_list_offsets_) &&
+ ParseItemOffsets(image_, annotation_set_ref_list_map_item_,
+ sizeof(dex::AnnotationSetRefItem),
+ &annotation_set_ref_list_offsets_) &&
+ ParseItemOffsets(image_, annotation_set_map_item_,
+ sizeof(dex::AnnotationOffItem),
+ &annotation_set_offsets_) &&
+ ParseAnnotationsDirectoryItems(
+ image_, annotations_directory_map_item_,
+ &annotations_directory_item_offsets_,
+ &annotations_directory_item_field_annotation_offsets_,
+ &annotations_directory_item_method_annotation_offsets_,
+ &annotations_directory_item_parameter_annotation_offsets_))) {
+ return false;
+ }
CodeItemParser code_item_parser(image_);
if (!code_item_parser.Init(code_map_item_))
return false;
@@ -935,7 +1661,8 @@ bool DisassemblerDex::ParseHeader() {
return false;
code_item_offsets_[i] = code_item_offset;
}
- return true;
+ // DEX files are required to have parsable code items.
+ return !code_item_offsets_.empty();
}
} // namespace zucchini
diff --git a/chromium/components/zucchini/disassembler_dex.h b/chromium/components/zucchini/disassembler_dex.h
index 3032d14e756..ecc4be6344b 100644
--- a/chromium/components/zucchini/disassembler_dex.h
+++ b/chromium/components/zucchini/disassembler_dex.h
@@ -32,10 +32,19 @@ class DisassemblerDex : public Disassembler {
kProtoId,
kFieldId,
kMethodId,
- kClassDef,
+ // kClassDef, // Unused
+ // kCallSiteId, // Unused
+ // kMethodHandle, // Unused
kTypeList,
+ kAnnotationSetRefList,
+ kAnnotionSet,
+ kClassData,
kCode,
kStringData,
+ kAnnotation,
+ kEncodedArray,
+ kAnnotationsDirectory,
+ // kCallSite, // Unused
kNumPools
};
@@ -44,34 +53,61 @@ class DisassemblerDex : public Disassembler {
// handles pools in the same order. Type-pool association is established in
// MakeReferenceGroups(), and verified by a unit test.
enum ReferenceType : uint8_t {
- kFieldIdToNameStringId, // kStringId
+ kTypeIdToDescriptorStringId, // kStringId
+ kProtoIdToShortyStringId,
+ kFieldIdToNameStringId,
+ kMethodIdToNameStringId,
+ kClassDefToSourceFileStringId,
kCodeToStringId16,
kCodeToStringId32,
- kFieldIdToClassTypeId, // kTypeId
+ kProtoIdToReturnTypeId, // kTypeId
+ kFieldIdToClassTypeId,
kFieldIdToTypeId,
+ kMethodIdToClassTypeId,
+ kClassDefToClassTypeId,
+ kClassDefToSuperClassTypeId,
+ kTypeListToTypeId,
kCodeToTypeId,
+ kMethodIdToProtoId, // kProtoId
+
kCodeToFieldId, // kFieldId
+ kAnnotationsDirectoryToFieldId,
kCodeToMethodId, // kMethodId
+ kAnnotationsDirectoryToMethodId,
+ kAnnotationsDirectoryToParameterMethodId,
+
+ kProtoIdToParametersTypeList, // kTypeList
+ kClassDefToInterfacesTypeList,
+
+ kAnnotationsDirectoryToParameterAnnotationSetRef, // kAnnotationSetRef,
- kCodeToRelCode16, // kCode
+ kAnnotationSetRefListToAnnotationSet, // kAnnotationSet,
+ kAnnotationsDirectoryToClassAnnotationSet,
+ kAnnotationsDirectoryToFieldAnnotationSet,
+ kAnnotationsDirectoryToMethodAnnotationSet,
+
+ kClassDefToClassData, // kClassData
+
+ kCodeToRelCode8, // kCode
+ kCodeToRelCode16,
kCodeToRelCode32,
kStringIdToStringData, // kStringData
- // TODO(ckitagawa): Extract the following kinds of pointers.
- // kProtoToShortyStringId,
- // kProtoToReturnTypeId,
- // kProtoToParamsTypeList,
- // kMethodToClassTypeId,
- // kMethodToProtoId,
- // kMethodToNameStringId,
- // kTypeListToTypeId,
- // kClassDefToClassTypeId,
- // kClassDefToSuperclassTypeId,
- // kClassDefToInterfaceTypeList,
+ kAnnotationSetToAnnotation, // kAnnotation
+
+ kClassDefToStaticValuesEncodedArray, // kEncodedArrayItem
+
+ kClassDefToAnnotationDirectory, // kAnnotationsDirectory
+
+ // Intentionally ignored references (never appeared in test corpus).
+ // kMethodHandleToFieldId,
+ // kMethodHandleToMethodId,
+ // kCallSiteIdToCallSite,
+
kNumTypes
};
@@ -92,12 +128,74 @@ class DisassemblerDex : public Disassembler {
// similar parsing logic to appear togeter.
std::unique_ptr<ReferenceReader> MakeReadStringIdToStringData(offset_t lo,
offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadTypeIdToDescriptorStringId32(
+ offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadProtoIdToShortyStringId32(
+ offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadProtoIdToReturnTypeId32(offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadProtoIdToParametersTypeList(
+ offset_t lo,
+ offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadFieldToClassTypeId16(offset_t lo,
offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadFieldToTypeId16(offset_t lo,
offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadFieldToNameStringId32(offset_t lo,
offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadMethodIdToClassTypeId16(offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadMethodIdToProtoId16(offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadMethodIdToNameStringId32(
+ offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadClassDefToClassTypeId32(offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadClassDefToSuperClassTypeId32(
+ offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadClassDefToInterfacesTypeList(
+ offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadClassDefToSourceFileStringId32(
+ offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadClassDefToAnnotationDirectory(
+ offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadClassDefToClassData(offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadClassDefToStaticValuesEncodedArray(
+ offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadTypeListToTypeId16(offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadAnnotationSetToAnnotation(
+ offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadAnnotationSetRefListToAnnotationSet(
+ offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader>
+ MakeReadAnnotationsDirectoryToClassAnnotationSet(offset_t lo, offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadAnnotationsDirectoryToFieldId32(
+ offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader>
+ MakeReadAnnotationsDirectoryToFieldAnnotationSet(offset_t lo, offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadAnnotationsDirectoryToMethodId32(
+ offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader>
+ MakeReadAnnotationsDirectoryToMethodAnnotationSet(offset_t lo, offset_t hi);
+ std::unique_ptr<ReferenceReader>
+ MakeReadAnnotationsDirectoryToParameterMethodId32(offset_t lo, offset_t hi);
+ std::unique_ptr<ReferenceReader>
+ MakeReadAnnotationsDirectoryToParameterAnnotationSetRef(offset_t lo,
+ offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadCodeToStringId16(offset_t lo,
offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadCodeToStringId32(offset_t lo,
@@ -108,6 +206,8 @@ class DisassemblerDex : public Disassembler {
offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadCodeToMethodId16(offset_t lo,
offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadCodeToRelCode8(offset_t lo,
+ offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadCodeToRelCode16(offset_t lo,
offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadCodeToRelCode32(offset_t lo,
@@ -118,8 +218,13 @@ class DisassemblerDex : public Disassembler {
std::unique_ptr<ReferenceWriter> MakeWriteStringId16(MutableBufferView image);
std::unique_ptr<ReferenceWriter> MakeWriteStringId32(MutableBufferView image);
std::unique_ptr<ReferenceWriter> MakeWriteTypeId16(MutableBufferView image);
+ std::unique_ptr<ReferenceWriter> MakeWriteTypeId32(MutableBufferView image);
+ std::unique_ptr<ReferenceWriter> MakeWriteProtoId16(MutableBufferView image);
std::unique_ptr<ReferenceWriter> MakeWriteFieldId16(MutableBufferView image);
+ std::unique_ptr<ReferenceWriter> MakeWriteFieldId32(MutableBufferView image);
std::unique_ptr<ReferenceWriter> MakeWriteMethodId16(MutableBufferView image);
+ std::unique_ptr<ReferenceWriter> MakeWriteMethodId32(MutableBufferView image);
+ std::unique_ptr<ReferenceWriter> MakeWriteRelCode8(MutableBufferView image);
std::unique_ptr<ReferenceWriter> MakeWriteRelCode16(MutableBufferView image);
std::unique_ptr<ReferenceWriter> MakeWriteRelCode32(MutableBufferView image);
std::unique_ptr<ReferenceWriter> MakeWriteAbs32(MutableBufferView image);
@@ -138,12 +243,28 @@ class DisassemblerDex : public Disassembler {
MapItemMap map_item_map_ = {};
dex::MapItem string_map_item_ = {};
dex::MapItem type_map_item_ = {};
+ dex::MapItem proto_map_item_ = {};
dex::MapItem field_map_item_ = {};
dex::MapItem method_map_item_ = {};
+ dex::MapItem class_def_map_item_ = {};
+ dex::MapItem type_list_map_item_ = {};
dex::MapItem code_map_item_ = {};
- // Sorted list of offsets of code items in |image_|.
+ // Optionally supported (not all DEX files have these).
+ dex::MapItem annotation_set_ref_list_map_item_ = {};
+ dex::MapItem annotation_set_map_item_ = {};
+ dex::MapItem annotations_directory_map_item_ = {};
+
+ // Sorted list of offsets of parsed items in |image_|.
std::vector<offset_t> code_item_offsets_;
+ std::vector<offset_t> type_list_offsets_;
+ std::vector<offset_t> annotation_set_ref_list_offsets_;
+ std::vector<offset_t> annotation_set_offsets_;
+ std::vector<offset_t> annotations_directory_item_offsets_;
+ std::vector<offset_t> annotations_directory_item_field_annotation_offsets_;
+ std::vector<offset_t> annotations_directory_item_method_annotation_offsets_;
+ std::vector<offset_t>
+ annotations_directory_item_parameter_annotation_offsets_;
DISALLOW_COPY_AND_ASSIGN(DisassemblerDex);
};
diff --git a/chromium/components/zucchini/disassembler_no_op.cc b/chromium/components/zucchini/disassembler_no_op.cc
index 3bc24d0416b..b17979c7488 100644
--- a/chromium/components/zucchini/disassembler_no_op.cc
+++ b/chromium/components/zucchini/disassembler_no_op.cc
@@ -24,6 +24,7 @@ std::vector<ReferenceGroup> DisassemblerNoOp::MakeReferenceGroups() const {
}
bool DisassemblerNoOp::Parse(ConstBufferView image) {
+ image_ = image;
return true;
}
diff --git a/chromium/components/zucchini/disassembler_win32.h b/chromium/components/zucchini/disassembler_win32.h
index 8e410ee3d52..29033cee8b0 100644
--- a/chromium/components/zucchini/disassembler_win32.h
+++ b/chromium/components/zucchini/disassembler_win32.h
@@ -30,7 +30,7 @@ struct Win32X86Traits {
static constexpr ExecutableType kExeType = kExeTypeWin32X86;
enum : uint16_t { kMagic = 0x10B };
enum : uint16_t { kRelocType = 3 };
- enum : offset_t { kVAWidth = 4 };
+ enum : uint32_t { kVAWidth = 4 };
static const char kExeTypeString[];
using ImageOptionalHeader = pe::ImageOptionalHeader;
@@ -43,7 +43,7 @@ struct Win32X64Traits {
static constexpr ExecutableType kExeType = kExeTypeWin32X64;
enum : uint16_t { kMagic = 0x20B };
enum : uint16_t { kRelocType = 10 };
- enum : offset_t { kVAWidth = 8 };
+ enum : uint32_t { kVAWidth = 8 };
static const char kExeTypeString[];
using ImageOptionalHeader = pe::ImageOptionalHeader64;
diff --git a/chromium/components/zucchini/element_detection_unittest.cc b/chromium/components/zucchini/element_detection_unittest.cc
index 6dbfa3f8b7f..769c839d882 100644
--- a/chromium/components/zucchini/element_detection_unittest.cc
+++ b/chromium/components/zucchini/element_detection_unittest.cc
@@ -4,6 +4,7 @@
#include "components/zucchini/element_detection.h"
+#include <map>
#include <vector>
#include "base/bind.h"
diff --git a/chromium/components/zucchini/equivalence_map.cc b/chromium/components/zucchini/equivalence_map.cc
index b3181abd01f..77551ec00d8 100644
--- a/chromium/components/zucchini/equivalence_map.cc
+++ b/chromium/components/zucchini/equivalence_map.cc
@@ -8,12 +8,32 @@
#include <utility>
#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
#include "components/zucchini/encoded_view.h"
#include "components/zucchini/patch_reader.h"
#include "components/zucchini/suffix_array.h"
namespace zucchini {
+namespace {
+
+// TODO(haungs): Tune these numbers to improve pathological case results.
+
+// In pathological cases Zucchini can exhibit O(n^2) behavior if the seed
+// selection process runs to completion. To prevent this we impose a quota for
+// the total length of equivalences the seed selection process can perform
+// trials on. For regular use cases it is unlikely this quota will be exceeded,
+// and if it is the effects on patch size are expected to be small.
+constexpr uint64_t kSeedSelectionTotalVisitLengthQuota = 1 << 18; // 256 KiB
+
+// The aforementioned quota alone is insufficient, as exploring backwards will
+// still be very successful resulting in O(n) behavior in the case of a limited
+// seed selection trials. This results in O(n^2) behavior returning. To mitigate
+// this we also impose a cap on the ExtendEquivalenceBackward() exploration.
+constexpr offset_t kBackwardsExtendLimit = 1 << 16; // 64 KiB
+
+} // namespace
+
/******** Utility Functions ********/
double GetTokenSimilarity(
@@ -132,8 +152,9 @@ EquivalenceCandidate ExtendEquivalenceBackward(
double current_similarity = candidate.similarity;
double best_similarity = current_similarity;
double current_penalty = 0.0;
- for (offset_t k = 1;
- k <= equivalence.dst_offset && k <= equivalence.src_offset; ++k) {
+ offset_t k_min = std::min(
+ {equivalence.dst_offset, equivalence.src_offset, kBackwardsExtendLimit});
+ for (offset_t k = 1; k <= k_min; ++k) {
// Mismatch in type, |candidate| cannot be extended further.
if (old_image_index.LookupType(equivalence.src_offset - k) !=
new_image_index.LookupType(equivalence.dst_offset - k)) {
@@ -191,15 +212,25 @@ EquivalenceCandidate VisitEquivalenceSeed(
/******** OffsetMapper ********/
-OffsetMapper::OffsetMapper(std::vector<Equivalence>&& equivalences)
- : equivalences_(std::move(equivalences)) {
+OffsetMapper::OffsetMapper(std::vector<Equivalence>&& equivalences,
+ size_t old_image_size,
+ size_t new_image_size)
+ : equivalences_(std::move(equivalences)),
+ old_image_size_(old_image_size),
+ new_image_size_(new_image_size) {
+ DCHECK_GT(new_image_size_, 0U);
DCHECK(std::is_sorted(equivalences_.begin(), equivalences_.end(),
[](const Equivalence& a, const Equivalence& b) {
return a.src_offset < b.src_offset;
}));
+ // This is for testing. Assume pruned.
}
-OffsetMapper::OffsetMapper(EquivalenceSource&& equivalence_source) {
+OffsetMapper::OffsetMapper(EquivalenceSource&& equivalence_source,
+ size_t old_image_size,
+ size_t new_image_size)
+ : old_image_size_(old_image_size), new_image_size_(new_image_size) {
+ DCHECK_GT(new_image_size_, 0U);
for (auto e = equivalence_source.GetNext(); e.has_value();
e = equivalence_source.GetNext()) {
equivalences_.push_back(*e);
@@ -207,8 +238,13 @@ OffsetMapper::OffsetMapper(EquivalenceSource&& equivalence_source) {
PruneEquivalencesAndSortBySource(&equivalences_);
}
-OffsetMapper::OffsetMapper(const EquivalenceMap& equivalence_map)
- : equivalences_(equivalence_map.size()) {
+OffsetMapper::OffsetMapper(const EquivalenceMap& equivalence_map,
+ size_t old_image_size,
+ size_t new_image_size)
+ : equivalences_(equivalence_map.size()),
+ old_image_size_(old_image_size),
+ new_image_size_(new_image_size) {
+ DCHECK_GT(new_image_size_, 0U);
std::transform(equivalence_map.begin(), equivalence_map.end(),
equivalences_.begin(),
[](const EquivalenceCandidate& c) { return c.eq; });
@@ -217,17 +253,40 @@ OffsetMapper::OffsetMapper(const EquivalenceMap& equivalence_map)
OffsetMapper::~OffsetMapper() = default;
-offset_t OffsetMapper::ForwardProject(offset_t offset) const {
- auto pos = std::upper_bound(
- equivalences_.begin(), equivalences_.end(), offset,
- [](offset_t a, const Equivalence& b) { return a < b.src_offset; });
- if (pos != equivalences_.begin()) {
- if (pos == equivalences_.end() || offset < pos[-1].src_end() ||
- offset - pos[-1].src_end() < pos->src_offset - offset) {
+// Safely evaluates |offset - unit.src_offset + unit.dst_offset| with signed
+// arithmetic, then clips the result to |[0, new_image_size_)|.
+offset_t OffsetMapper::NaiveExtendedForwardProject(const Equivalence& unit,
+ offset_t offset) const {
+ int64_t old_offset64 = offset;
+ int64_t src_offset64 = unit.src_offset;
+ int64_t dst_offset64 = unit.dst_offset;
+ uint64_t new_offset64 = std::min<uint64_t>(
+ std::max<int64_t>(0LL, old_offset64 - src_offset64 + dst_offset64),
+ new_image_size_ - 1);
+ return base::checked_cast<offset_t>(new_offset64);
+}
+
+offset_t OffsetMapper::ExtendedForwardProject(offset_t offset) const {
+ DCHECK(!equivalences_.empty());
+ if (offset < old_image_size_) {
+ // Finds the equivalence unit whose "old" block is nearest to |offset|,
+ // favoring the block with lower offset in case of a tie.
+ auto pos = std::upper_bound(
+ equivalences_.begin(), equivalences_.end(), offset,
+ [](offset_t a, const Equivalence& b) { return a < b.src_offset; });
+ // For tiebreaking: |offset - pos[-1].src_end()| is actually 1 less than
+ // |offset|'s distance to "old" block of |pos[-1]|. Therefore "<" is used.
+ if (pos != equivalences_.begin() &&
+ (pos == equivalences_.end() || offset < pos[-1].src_end() ||
+ offset - pos[-1].src_end() < pos->src_offset - offset)) {
--pos;
}
+ return NaiveExtendedForwardProject(*pos, offset);
}
- return offset - pos->src_offset + pos->dst_offset;
+ // Fake offsets.
+ offset_t delta = offset - old_image_size_;
+ return delta < kOffsetBound - new_image_size_ ? new_image_size_ + delta
+ : kOffsetBound - 1;
}
void OffsetMapper::ForwardProjectAll(std::vector<offset_t>* offsets) const {
@@ -374,6 +433,7 @@ void EquivalenceMap::CreateCandidates(
offset_t next_dst_offset = dst_offset + 1;
// TODO(huangs): Clean up.
double best_similarity = min_similarity;
+ uint64_t total_visit_length = 0;
EquivalenceCandidate best_candidate = {{0, 0, 0}, 0.0};
for (auto it = match; it != old_sa.end(); ++it) {
EquivalenceCandidate candidate = VisitEquivalenceSeed(
@@ -383,10 +443,15 @@ void EquivalenceMap::CreateCandidates(
best_candidate = candidate;
best_similarity = candidate.similarity;
next_dst_offset = candidate.eq.dst_end();
+ total_visit_length += candidate.eq.length;
+ if (total_visit_length > kSeedSelectionTotalVisitLengthQuota) {
+ break;
+ }
} else {
break;
}
}
+ total_visit_length = 0;
for (auto it = match; it != old_sa.begin(); --it) {
EquivalenceCandidate candidate = VisitEquivalenceSeed(
old_view.image_index(), new_view.image_index(), targets_affinities,
@@ -395,6 +460,10 @@ void EquivalenceMap::CreateCandidates(
best_candidate = candidate;
best_similarity = candidate.similarity;
next_dst_offset = candidate.eq.dst_end();
+ total_visit_length += candidate.eq.length;
+ if (total_visit_length > kSeedSelectionTotalVisitLengthQuota) {
+ break;
+ }
} else {
break;
}
diff --git a/chromium/components/zucchini/equivalence_map.h b/chromium/components/zucchini/equivalence_map.h
index 91b215c7c2b..2ae880130c2 100644
--- a/chromium/components/zucchini/equivalence_map.h
+++ b/chromium/components/zucchini/equivalence_map.h
@@ -84,26 +84,46 @@ class OffsetMapper {
public:
using const_iterator = std::vector<Equivalence>::const_iterator;
- // Constructors for various data sources.
+ // Constructors for various data sources. "Old" and "new" image sizes are
+ // needed for bounds checks and to handle dangling targets.
// - From a list of |equivalences|, already sorted (by |src_offset|) and
// pruned, useful for tests.
- explicit OffsetMapper(std::vector<Equivalence>&& equivalences);
+ OffsetMapper(std::vector<Equivalence>&& equivalences,
+ size_t old_image_size,
+ size_t new_image_size);
// - From a generator, useful for Zucchini-apply.
- explicit OffsetMapper(EquivalenceSource&& equivalence_source);
+ OffsetMapper(EquivalenceSource&& equivalence_source,
+ size_t old_image_size,
+ size_t new_image_size);
// - From an EquivalenceMap that needs to be processed, useful for
// Zucchini-gen.
- explicit OffsetMapper(const EquivalenceMap& equivalence_map);
+ OffsetMapper(const EquivalenceMap& equivalence_map,
+ size_t old_image_size,
+ size_t new_image_size);
~OffsetMapper();
size_t size() const { return equivalences_.size(); }
const_iterator begin() const { return equivalences_.begin(); }
const_iterator end() const { return equivalences_.end(); }
+ // Returns naive extended forward-projection of "old" |offset| that follows
+ // |eq|'s delta. |eq| needs not cover |offset|.
+ // - Averts underflow / overflow by clamping to |[0, new_image_size_)|.
+ // - However, |offset| is *not* restricted to |[0, old_image_size_)|; the
+ // caller must to make the check (hence "naive").
+ offset_t NaiveExtendedForwardProject(const Equivalence& unit,
+ offset_t offset) const;
+
// Returns an offset in |new_image| corresponding to |offset| in |old_image|.
- // If |offset| is not part of an equivalence, the equivalence nearest to
- // |offset| is used as if it contained |offset|. This assumes |equivalences_|
- // is not empty.
- offset_t ForwardProject(offset_t offset) const;
+ // Assumes |equivalences_| to be non-empty. Cases:
+ // - If |offset| is covered (i.e., in an "old" block), then use the delta of
+ // the (unique) equivalence unit that covers |offset|.
+ // - If |offset| is non-covered, but in |[0, old_image_size_)|, then find the
+ // nearest "old" block, use its delta, and avert underflow / overflow by
+ // clamping the result to |[0, new_image_size_)|.
+ // - If |offset| is >= |new_image_size_| (a "fake offset"), then use
+ // |new_image_size_ - old_image_size_| as the delta.
+ offset_t ExtendedForwardProject(offset_t offset) const;
// Given sorted |offsets|, applies a projection in-place of all offsets that
// are part of a pruned equivalence from |old_image| to |new_image|. Other
@@ -123,7 +143,11 @@ class OffsetMapper {
std::vector<Equivalence>* equivalences);
private:
- std::vector<Equivalence> equivalences_;
+ // |equivalences_| is pruned, i.e., no "old" blocks overlap (and no "new"
+ // block overlaps). Also, it is sorted by "old" offsets.
+ std::vector<Equivalence> equivalences_; // P
+ const size_t old_image_size_;
+ const size_t new_image_size_;
};
// Container of equivalences between |old_image_index| and |new_image_index|,
diff --git a/chromium/components/zucchini/equivalence_map_unittest.cc b/chromium/components/zucchini/equivalence_map_unittest.cc
index 9c4166fdff7..b3a4ea4b89f 100644
--- a/chromium/components/zucchini/equivalence_map_unittest.cc
+++ b/chromium/components/zucchini/equivalence_map_unittest.cc
@@ -5,6 +5,9 @@
#include "components/zucchini/equivalence_map.h"
#include <cstring>
+#include <deque>
+#include <map>
+#include <string>
#include <utility>
#include <vector>
@@ -296,7 +299,209 @@ TEST(EquivalenceMapTest, PruneEquivalencesAndSortBySource) {
}()));
}
-TEST(EquivalenceMapTest, ForwardProject) {
+TEST(EquivalenceMapTest, NaiveExtendedForwardProject) {
+ constexpr size_t kOldImageSize = 1000U;
+ constexpr size_t kNewImageSize = 1000U;
+ OffsetMapper offset_mapper(std::vector<Equivalence>(), kOldImageSize,
+ kNewImageSize);
+
+ // Convenience function to declutter.
+ auto project = [&offset_mapper](const Equivalence& eq, offset_t offset) {
+ return offset_mapper.NaiveExtendedForwardProject(eq, offset);
+ };
+
+ // Equivalence with delta = 0.
+ Equivalence eq_stay = {10, 10, +5}; // [10,15) -> [10,15).
+ for (offset_t offset = 0U; offset < 1000U; ++offset) {
+ EXPECT_EQ(offset, project(eq_stay, offset));
+ }
+ // Saturate since result would overflow "new".
+ EXPECT_EQ(999U, project(eq_stay, 1000U));
+ EXPECT_EQ(999U, project(eq_stay, 2000U));
+ EXPECT_EQ(999U, project(eq_stay, kOffsetBound - 1));
+
+ // Equivalence with delta = -10.
+ Equivalence eq_dec = {20, 10, +12}; // [20,32) --> [10,22).
+ // Offsets in "old" block.
+ EXPECT_EQ(10U, project(eq_dec, 20U));
+ EXPECT_EQ(11U, project(eq_dec, 21U));
+ EXPECT_EQ(21U, project(eq_dec, 31U));
+ // Offsets before "old" block, no underflow
+ EXPECT_EQ(9U, project(eq_dec, 19U));
+ EXPECT_EQ(1U, project(eq_dec, 11U));
+ EXPECT_EQ(0U, project(eq_dec, 10U));
+ // Offsets before "old" block, underflow (possible since delta < 0).
+ EXPECT_EQ(0U, project(eq_dec, 9U));
+ EXPECT_EQ(0U, project(eq_dec, 5U));
+ EXPECT_EQ(0U, project(eq_dec, 0U));
+ // Offsets after "old" block, no overflow.
+ EXPECT_EQ(20U, project(eq_dec, 30U));
+ EXPECT_EQ(64U, project(eq_dec, 74U));
+ EXPECT_EQ(90U, project(eq_dec, 100U));
+ EXPECT_EQ(490U, project(eq_dec, 500U));
+ EXPECT_EQ(999U, project(eq_dec, 1009U));
+ // Offsets after "old" block, overflow.
+ EXPECT_EQ(999U, project(eq_dec, 1010U));
+ EXPECT_EQ(999U, project(eq_dec, 2000U));
+ EXPECT_EQ(999U, project(eq_dec, kOffsetBound - 1));
+
+ // Equivalence with delta = +10.
+ Equivalence eq_inc = {7, 17, +80}; // [7,87) --> [17,97).
+ // Offsets in "old" block.
+ EXPECT_EQ(17U, project(eq_inc, 7U));
+ EXPECT_EQ(60U, project(eq_inc, 50U));
+ EXPECT_EQ(96U, project(eq_inc, 86U));
+ // Offsets before "old" block, underflow impossible since delta >= 0.
+ EXPECT_EQ(16U, project(eq_inc, 6U));
+ EXPECT_EQ(10U, project(eq_inc, 0U));
+ // Offsets after "old" block, no overflow.
+ EXPECT_EQ(97U, project(eq_inc, 87U));
+ EXPECT_EQ(510U, project(eq_inc, 500U));
+ EXPECT_EQ(999U, project(eq_inc, 989U));
+ // Offsets after "old" block, overflow.
+ EXPECT_EQ(999U, project(eq_inc, 990U));
+ EXPECT_EQ(999U, project(eq_inc, 2000U));
+ EXPECT_EQ(999U, project(eq_inc, kOffsetBound - 1));
+}
+
+TEST(EquivalenceMapTest, ExtendedForwardProject) {
+ // EquivalenceMaps provided must be sorted by "old" offset, and pruned.
+ // [0,2) --> [10,12), [2,3) --> [13,14), [4,6) --> [16,18).
+ OffsetMapper offset_mapper1({{0, 10, +2}, {2, 13, +1}, {4, 16, +2}}, 20U,
+ 25U);
+ EXPECT_EQ(10U, offset_mapper1.ExtendedForwardProject(0U));
+ EXPECT_EQ(11U, offset_mapper1.ExtendedForwardProject(1U));
+ EXPECT_EQ(13U, offset_mapper1.ExtendedForwardProject(2U));
+ EXPECT_EQ(14U, offset_mapper1.ExtendedForwardProject(3U)); // Previous equiv.
+ EXPECT_EQ(16U, offset_mapper1.ExtendedForwardProject(4U));
+ EXPECT_EQ(17U, offset_mapper1.ExtendedForwardProject(5U));
+ EXPECT_EQ(18U, offset_mapper1.ExtendedForwardProject(6U)); // Previous equiv.
+ // Fake offsets.
+ EXPECT_EQ(25U, offset_mapper1.ExtendedForwardProject(20U));
+ EXPECT_EQ(26U, offset_mapper1.ExtendedForwardProject(21U));
+ EXPECT_EQ(1005U, offset_mapper1.ExtendedForwardProject(1000U));
+ EXPECT_EQ(kOffsetBound - 1,
+ offset_mapper1.ExtendedForwardProject(kOffsetBound - 1));
+
+ // [0,2) --> [10,12), [13,14) --> [2,3), [16,18) --> [4,6).
+ OffsetMapper offset_mapper2({{0, 10, +2}, {13, 2, +1}, {16, 4, +2}}, 25U,
+ 20U);
+ EXPECT_EQ(10U, offset_mapper2.ExtendedForwardProject(0U));
+ EXPECT_EQ(11U, offset_mapper2.ExtendedForwardProject(1U));
+ EXPECT_EQ(2U, offset_mapper2.ExtendedForwardProject(13U));
+ EXPECT_EQ(3U, offset_mapper2.ExtendedForwardProject(14U)); // Previous equiv.
+ EXPECT_EQ(4U, offset_mapper2.ExtendedForwardProject(16U));
+ EXPECT_EQ(5U, offset_mapper2.ExtendedForwardProject(17U));
+ EXPECT_EQ(6U, offset_mapper2.ExtendedForwardProject(18U)); // Previous equiv.
+ // Fake offsets.
+ EXPECT_EQ(20U, offset_mapper2.ExtendedForwardProject(25U));
+ EXPECT_EQ(21U, offset_mapper2.ExtendedForwardProject(26U));
+ EXPECT_EQ(995U, offset_mapper2.ExtendedForwardProject(1000U));
+ EXPECT_EQ(kOffsetBound - 1 - 5,
+ offset_mapper2.ExtendedForwardProject(kOffsetBound - 1));
+}
+
+TEST(EquivalenceMapTest, ExtendedForwardProjectEncoding) {
+ // Tests OffsetMapper::ExtendedForwardProject(), which maps every "old" offset
+ // to a "new" offset, with possible overlap (even though blocks don't
+ // overlap). Not testing real offsets only (no fake offsets).
+ // |old_spec| is a string like "<<aaAAaabbBBbcCCc>>":
+ // - Upper case letters are covered "old" offsets.
+ // - Lower case letters are non-covered offsets that are properly mapped using
+ // nearest "old" block.
+ // - '<' denotes underflow (clamped to 0).
+ // - '>' denotes overflow (clampled to "new" size - 1).
+ // |new_spec| is a string like "aaAA(ab)(ab)BBb..cCCc":
+ // - Upper and lower case letters are mapped "new" targets, occurring in the
+ // order that they appear in |old_spec|.
+ // - '.' are "new" offsets that appear as output.
+ // - '(' and ')' surround a single "new" location that are repeated as output.
+ int case_no = 0;
+ auto run_test = [&case_no](std::vector<Equivalence>&& equivalences,
+ const std::string& old_spec,
+ const std::string& new_spec) {
+ const size_t old_size = old_spec.length();
+ // Build expected "new" offsets, queue up for each letter.
+ std::map<char, std::deque<offset_t>> expected;
+ offset_t cur_new_offset = 0;
+ char state = ')'; // ')' = increase offset, '(' = stay.
+ for (char ch : new_spec) {
+ if (ch == '(' || ch == ')')
+ state = ch;
+ else
+ expected[ch].push_back(cur_new_offset);
+ cur_new_offset += (state == ')') ? 1 : 0;
+ }
+ const size_t new_size = cur_new_offset;
+ // Forward-project for each "old" index, pull from queue from matching
+ // letter, and compare.
+ OffsetMapper offset_mapper(std::move(equivalences), old_size, new_size);
+ for (offset_t old_offset = 0; old_offset < old_size; ++old_offset) {
+ offset_t new_offset = offset_mapper.ExtendedForwardProject(old_offset);
+ char ch = old_spec[old_offset];
+ if (ch == '<') { // Special case: Underflow.
+ EXPECT_EQ(0U, new_offset) << "in case " << case_no;
+ } else if (ch == '>') { // Special case: Overflow.
+ EXPECT_EQ(static_cast<offset_t>(new_size - 1), new_offset)
+ << "in case " << case_no;
+ } else {
+ std::deque<offset_t>& q = expected[ch];
+ ASSERT_FALSE(q.empty());
+ EXPECT_EQ(q.front(), new_offset) << "in case " << case_no;
+ q.pop_front();
+ if (q.empty())
+ expected.erase(ch);
+ }
+ }
+ // Clear useless '.', and ensure everything is consumed.
+ expected.erase('.');
+ EXPECT_TRUE(expected.empty()) << "in case " << case_no;
+ ++case_no;
+ };
+
+ // Trivial: [5,9) --> [5,9).
+ run_test({{5, 5, +4}}, "aaaaaAAAAaaaaa", "aaaaaAAAAaaaaa");
+ // Swap: [0,4) --> [6,10), [4,10) --> [0,6).
+ run_test({{0, 6, +4}, {4, 0, +6}}, "AAAABBBBBB", "BBBBBBAAAA");
+ // Overlap: [0,4) --> [2,6), [4,10) --> [3,9).
+ run_test({{0, 2, +4}, {4, 3, +6}}, "AAAABBBBBB", "..A(AB)(AB)(AB)BBB.");
+ // Converge: [1,3) --> [2,4), [7,8) --> [6,7).
+ run_test({{1, 2, +2}, {7, 6, +1}}, "aAAaabbBbb", ".aAA(ab)(ab)Bbb.");
+ // Converge with tie-breaker: [1,3) --> [2,4), [8,9) --> [7,8).
+ run_test({{1, 2, +2}, {8, 7, +1}}, "aAAaaabbBb", ".aAAa(ab)(ab)Bb.");
+ // Shift left: [6,8) --> [2,4): Underflow occurs.
+ run_test({{6, 2, +2}}, "<<<<aaAAaa", "aaAAaa....");
+ // Shift right: [2,5) --> [6,9): Overflow occurs.
+ run_test({{2, 6, +3}}, "aaAAAa>>>>", "....aaAAAa");
+ // Diverge: [3,5) --> [1,3], [7,9) --> [9,11).
+ run_test({{3, 1, +2}, {7, 9, +2}}, "<<aAAabBBb>>", "aAAa....bBBb");
+ // Pile-up: [0,2) --> [7,9), [9,11) --> [9,11), [18,20) --> [11,13).
+ run_test({{0, 7, +2}, {9, 9, +2}, {18, 11, +2}}, "AAaaaabbbBBbbbbcccCC",
+ "......b(Ab)(Abc)(Bac)(Bac)(Cab)(Cab)bb.....");
+ // Inverse pile-up: [7,9) --> [0,2), [9,11) --> [9,11), [13,15) --> [18,20).
+ run_test({{7, 0, +2}, {9, 9, +2}, {11, 18, +2}}, "<<<<<<<AABBCC>>>>>>>",
+ "AA.......BB.......CC");
+ // Sparse rotate: [3,4) -> [10,11), [10,11) --> [17,18), [17,18) --> [3,4).
+ run_test({{3, 10, +1}, {10, 17, +1}, {17, 3, +1}}, "aaaAaaabbbBbbbcccCccc",
+ "cccCcccaaaAaaabbbBbbb");
+ // Messy swap: [2,4) --> [10,12), [12,16) --> [3,7).
+ run_test({{2, 10, +2}, {12, 3, +4}}, "aaAAaa>><bbbBBBBbb",
+ "bbbBBBBb(ab)aAAaa");
+ // Messy expand: [6,8) --> [3,5), [10,11) -> [11,12), [14,17) --> [16,19).
+ run_test({{6, 3, +2}, {10, 11, +1}, {14, 16, +3}}, "<<<aaaAAabBbbcCCCc>>>>>",
+ "aaaAAa....bBbb.cCCCc");
+ // Interleave: [1,2) --> [0,1), [5,6) --> [10,11), [6,8) --> [3,5),
+ // [11,13) --> [12,14), [14,16) --> [6,8), [17,18) --> [17,18).
+ run_test({{1, 0, +1},
+ {5, 10, +1},
+ {6, 3, +2},
+ {11, 12, +2},
+ {14, 6, +2},
+ {17, 17, +1}},
+ "<AaabBCCccdDDdEEeFf>", "AaaCCc(Ec)EebBdDDd..Ff");
+}
+
+TEST(EquivalenceMapTest, ForwardProjectAll) {
auto ForwardProjectAllTest = [](const OffsetMapper& offset_mapper,
std::initializer_list<offset_t> offsets) {
OffsetVector offsets_vec(offsets);
@@ -304,7 +509,9 @@ TEST(EquivalenceMapTest, ForwardProject) {
return offsets_vec;
};
- OffsetMapper offset_mapper1({{0, 10, 2}, {2, 13, 1}, {4, 16, 2}});
+ // [0,2) --> [10,12), [2,3) --> [13,14), [4,6) --> [16,18).
+ OffsetMapper offset_mapper1({{0, 10, +2}, {2, 13, +1}, {4, 16, +2}}, 100U,
+ 100U);
EXPECT_EQ(OffsetVector({10}), ForwardProjectAllTest(offset_mapper1, {0}));
EXPECT_EQ(OffsetVector({13}), ForwardProjectAllTest(offset_mapper1, {2}));
EXPECT_EQ(OffsetVector({}), ForwardProjectAllTest(offset_mapper1, {3}));
@@ -317,7 +524,9 @@ TEST(EquivalenceMapTest, ForwardProject) {
EXPECT_EQ(OffsetVector({10, 11, 13, 16, 17}),
ForwardProjectAllTest(offset_mapper1, {0, 1, 2, 3, 4, 5, 6}));
- OffsetMapper offset_mapper2({{0, 10, 2}, {13, 2, 1}, {16, 4, 2}});
+ // [0,2) --> [10,12), [13,14) --> [2,3), [16,18) --> [4,6).
+ OffsetMapper offset_mapper2({{0, 10, +2}, {13, 2, +1}, {16, 4, +2}}, 100U,
+ 100U);
EXPECT_EQ(OffsetVector({2}), ForwardProjectAllTest(offset_mapper2, {13}));
EXPECT_EQ(OffsetVector({10, 2}),
ForwardProjectAllTest(offset_mapper2, {0, 13}));
@@ -329,26 +538,6 @@ TEST(EquivalenceMapTest, ForwardProject) {
ForwardProjectAllTest(offset_mapper2, {0, 1, 13, 14, 16, 17, 18}));
}
-TEST(EquivalenceMapTest, ProjectOffset) {
- OffsetMapper offset_mapper1({{0, 10, 2}, {2, 13, 1}, {4, 16, 2}});
- EXPECT_EQ(10U, offset_mapper1.ForwardProject(0));
- EXPECT_EQ(11U, offset_mapper1.ForwardProject(1));
- EXPECT_EQ(13U, offset_mapper1.ForwardProject(2));
- EXPECT_EQ(14U, offset_mapper1.ForwardProject(3)); // Previous equivalence.
- EXPECT_EQ(16U, offset_mapper1.ForwardProject(4));
- EXPECT_EQ(17U, offset_mapper1.ForwardProject(5));
- EXPECT_EQ(18U, offset_mapper1.ForwardProject(6)); // Previous equivalence.
-
- OffsetMapper offset_mapper2({{0, 10, 2}, {13, 2, 1}, {16, 4, 2}});
- EXPECT_EQ(10U, offset_mapper2.ForwardProject(0));
- EXPECT_EQ(11U, offset_mapper2.ForwardProject(1));
- EXPECT_EQ(2U, offset_mapper2.ForwardProject(13));
- EXPECT_EQ(3U, offset_mapper2.ForwardProject(14)); // Previous equivalence.
- EXPECT_EQ(4U, offset_mapper2.ForwardProject(16));
- EXPECT_EQ(5U, offset_mapper2.ForwardProject(17));
- EXPECT_EQ(6U, offset_mapper2.ForwardProject(18)); // Previous equivalence.
-}
-
TEST(EquivalenceMapTest, Build) {
auto test_build_equivalence = [](const ImageIndex old_index,
const ImageIndex new_index,
diff --git a/chromium/components/zucchini/fuzzers/BUILD.gn b/chromium/components/zucchini/fuzzers/BUILD.gn
index 7afe6db662b..e2720fc2204 100644
--- a/chromium/components/zucchini/fuzzers/BUILD.gn
+++ b/chromium/components/zucchini/fuzzers/BUILD.gn
@@ -7,6 +7,20 @@ import("//third_party/protobuf/proto_library.gni")
# To download the corpus for local fuzzing use:
# gsutil -m rsync \
+# gs://clusterfuzz-corpus/libfuzzer/zucchini_disassembler_dex_fuzzer \
+# components/zucchini/fuzzing/testdata/disassembler_dex_fuzzer
+fuzzer_test("zucchini_disassembler_dex_fuzzer") {
+ sources = [
+ "disassembler_dex_fuzzer.cc",
+ ]
+ deps = [
+ "//base",
+ "//components/zucchini:zucchini_lib",
+ ]
+}
+
+# To download the corpus for local fuzzing use:
+# gsutil -m rsync \
# gs://clusterfuzz-corpus/libfuzzer/zucchini_disassembler_win32_fuzzer \
# components/zucchini/fuzzing/testdata/disassembler_win32_fuzzer
fuzzer_test("zucchini_disassembler_win32_fuzzer") {
@@ -39,7 +53,7 @@ proto_library("zucchini_file_pair_proto") {
# Ensure protoc is available.
# Disabled on Windows due to crbug/844826.
if (current_toolchain == host_toolchain && !is_win) {
- # Raw Apply Fuzzer:
+ # Raw Apply Fuzzer Seed:
action("zucchini_raw_apply_seed") {
script = "generate_fuzzer_data.py"
@@ -47,10 +61,16 @@ if (current_toolchain == host_toolchain && !is_win) {
"--raw",
"old_eventlog_provider.dll", # <old_file>
"new_eventlog_provider.dll", # <new_file>
- "eventlog_provider.patch", # <patch_file> (temporary)
- # <output_dir>
- rebase_path("$target_gen_dir/testdata/raw_apply_fuzzer", root_build_dir),
+ # <patch_file> (temporary)
+ rebase_path(
+ "$target_gen_dir/testdata/apply_fuzzer/eventlog_provider.patch",
+ root_build_dir),
+
+ # <output_file>
+ rebase_path(
+ "$target_gen_dir/testdata/apply_fuzzer/raw_apply_seed_proto.bin",
+ root_build_dir),
]
# Files depended upon.
@@ -62,7 +82,7 @@ if (current_toolchain == host_toolchain && !is_win) {
# Outputs: necessary for validation.
outputs = [
- "$target_gen_dir/testdata/raw_apply_fuzzer/seed_proto.bin",
+ "$target_gen_dir/testdata/apply_fuzzer/raw_apply_seed_proto.bin",
]
deps = [
"//components/zucchini:zucchini",
@@ -70,9 +90,46 @@ if (current_toolchain == host_toolchain && !is_win) {
]
}
- fuzzer_test("zucchini_raw_apply_fuzzer") {
+ # ZTF Apply Fuzzer Seed:
+ action("zucchini_ztf_apply_seed") {
+ script = "generate_fuzzer_data.py"
+
+ # *.ztf files are expected to be valid ZTF format.
+ args = [
+ "old.ztf", # <old_file>
+ "new.ztf", # <new_file>
+
+ # <patch_file> (temporary)
+ rebase_path("$target_gen_dir/testdata/apply_fuzzer/ztf.patch",
+ root_build_dir),
+
+ # <output_file>
+ rebase_path(
+ "$target_gen_dir/testdata/apply_fuzzer/ztf_apply_seed_proto.bin",
+ root_build_dir),
+ ]
+
+ # Files depended upon.
sources = [
- "raw_gen_fuzzer.cc",
+ "create_seed_file_pair.py",
+ "testdata/new.ztf",
+ "testdata/old.ztf",
+ ]
+
+ # Outputs: necessary for validation.
+ outputs = [
+ "$target_gen_dir/testdata/apply_fuzzer/ztf_apply_seed_proto.bin",
+ ]
+ deps = [
+ "//components/zucchini:zucchini",
+ "//third_party/protobuf:protoc",
+ ]
+ }
+
+ # Apply Fuzzer:
+ fuzzer_test("zucchini_apply_fuzzer") {
+ sources = [
+ "apply_fuzzer.cc",
]
deps = [
":zucchini_file_pair_proto",
@@ -80,11 +137,21 @@ if (current_toolchain == host_toolchain && !is_win) {
"//components/zucchini:zucchini_lib",
"//third_party/libprotobuf-mutator",
]
- seed_corpus = "$target_gen_dir/testdata/raw_apply_fuzzer"
- seed_corpus_deps = [ ":zucchini_raw_apply_seed" ]
+ seed_corpus = "$target_gen_dir/testdata/apply_fuzzer"
+ seed_corpus_deps = [
+ ":zucchini_raw_apply_seed",
+ ":zucchini_ztf_apply_seed",
+ ]
}
+ # For Gen fuzzers seeds can be created from this directory with:
+ # python create_seed_file_pair.py <protoc> <old file> <new file> <out file>
+ # [--imposed=<imposed>]
+
# Raw Gen Fuzzer:
+ # <old file>: testdata/old.ztf
+ # <new file>: testdata/new.ztf
+ # <out file>: testdata/raw_or_ztf_gen_fuzzer/seed.asciipb
fuzzer_test("zucchini_raw_gen_fuzzer") {
sources = [
"raw_gen_fuzzer.cc",
@@ -95,6 +162,42 @@ if (current_toolchain == host_toolchain && !is_win) {
"//components/zucchini:zucchini_lib",
"//third_party/libprotobuf-mutator",
]
- seed_corpus = "testdata/raw_gen_fuzzer"
+ seed_corpus = "testdata/raw_or_ztf_gen_fuzzer"
+ }
+
+ # ZTF Gen Fuzzer:
+ # <old file>: testdata/old.ztf
+ # <new file>: testdata/new.ztf
+ # <out file>: testdata/raw_or_ztf_gen_fuzzer/seed.asciipb
+ fuzzer_test("zucchini_ztf_gen_fuzzer") {
+ sources = [
+ "ztf_gen_fuzzer.cc",
+ ]
+ deps = [
+ ":zucchini_file_pair_proto",
+ "//base",
+ "//components/zucchini:zucchini_lib",
+ "//third_party/libprotobuf-mutator",
+ ]
+ seed_corpus = "testdata/raw_or_ztf_gen_fuzzer"
+ }
+
+ # Imposed Ensemble Match Fuzzer:
+ # <old file>: testdata/old_imposed_archive.txt
+ # <new file>: testdata/new_imposed_archive.txt
+ # <out file>: testdata/imposed_ensemble_matcher_fuzzer/seed.asciipb
+ # <imposed>: 17+420=388+347,452+420=27+347
+ # This is a mapping of regions old_offset+old_size=new_offset+new_size,...
+ fuzzer_test("zucchini_imposed_ensemble_matcher_fuzzer") {
+ sources = [
+ "imposed_ensemble_matcher_fuzzer.cc",
+ ]
+ deps = [
+ ":zucchini_file_pair_proto",
+ "//base",
+ "//components/zucchini:zucchini_lib",
+ "//third_party/libprotobuf-mutator",
+ ]
+ seed_corpus = "testdata/imposed_ensemble_matcher_fuzzer"
}
}
diff --git a/chromium/components/zucchini/fuzzers/raw_apply_fuzzer.cc b/chromium/components/zucchini/fuzzers/apply_fuzzer.cc
index da3230a81f7..baad978493a 100644
--- a/chromium/components/zucchini/fuzzers/raw_apply_fuzzer.cc
+++ b/chromium/components/zucchini/fuzzers/apply_fuzzer.cc
@@ -18,7 +18,7 @@
struct Environment {
Environment() {
- logging::SetMinLogLevel(3); // Disable console spamming.
+ logging::SetMinLogLevel(logging::LOG_FATAL); // Disable console spamming.
}
};
@@ -54,6 +54,6 @@ DEFINE_BINARY_PROTO_FUZZER(const zucchini::fuzzers::FilePair& file_pair) {
zucchini::MutableBufferView new_image(new_data.data(), new_size);
// Fuzz target.
- zucchini::Apply(old_image, *patch_reader, new_image);
+ zucchini::ApplyBuffer(old_image, *patch_reader, new_image);
// No need to check whether output exist, or if so, whether it's valid.
}
diff --git a/chromium/components/zucchini/fuzzers/create_seed_file_pair.py b/chromium/components/zucchini/fuzzers/create_seed_file_pair.py
index a44db7b6a6f..4394801c6b2 100755
--- a/chromium/components/zucchini/fuzzers/create_seed_file_pair.py
+++ b/chromium/components/zucchini/fuzzers/create_seed_file_pair.py
@@ -21,7 +21,6 @@ import sys
ABS_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__)))
PROTO_DEFINITION_FILE = 'file_pair.proto'
-OUTPUT_FORMAT = b'old_file: "{}"\nnew_or_patch_file: "{}"'
def parse_args():
"""Parse commandline args."""
@@ -30,7 +29,11 @@ def parse_args():
parser.add_argument('old_file', help='Old file to generate/apply patch.')
parser.add_argument('new_or_patch_file',
help='New file to generate or patch to apply.')
- parser.add_argument('output_file', help='File to write binary protobuf to.')
+ parser.add_argument('output_file',
+ help='File to write binary protobuf to.')
+ parser.add_argument('--imposed_matches',
+ help='Equivalence matches to impose when generating '
+ 'the patch.')
return parser.parse_args()
@@ -45,9 +48,13 @@ def read_to_proto_escaped_string(filename):
def main():
args = parse_args()
# Create an ASCII string representing a protobuf.
- content = OUTPUT_FORMAT.format(read_to_proto_escaped_string(args.old_file),
- read_to_proto_escaped_string(
- args.new_or_patch_file))
+ content = [b'old_file: "{}"'.format(read_to_proto_escaped_string(
+ args.old_file)),
+ b'new_or_patch_file: "{}"'.format(read_to_proto_escaped_string(
+ args.new_or_patch_file))]
+
+ if args.imposed_matches:
+ content.append('imposed_matches: "{}"'.format(args.imposed_matches))
# Encode the ASCII protobuf as a binary protobuf.
ps = subprocess.Popen([args.protoc_path, '--proto_path=%s' % ABS_PATH,
@@ -57,7 +64,7 @@ def main():
stdout=subprocess.PIPE)
# Write the string to the subprocess. Single line IO is fine as protoc returns
# a string.
- output = ps.communicate(input=content)
+ output = ps.communicate(input=b'\n'.join(content))
ps.wait()
if ps.returncode:
logging.error('Binary protobuf encoding failed.')
diff --git a/chromium/components/zucchini/fuzzers/disassembler_dex_fuzzer.cc b/chromium/components/zucchini/fuzzers/disassembler_dex_fuzzer.cc
new file mode 100644
index 00000000000..ab08696df3f
--- /dev/null
+++ b/chromium/components/zucchini/fuzzers/disassembler_dex_fuzzer.cc
@@ -0,0 +1,54 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "components/zucchini/buffer_view.h"
+#include "components/zucchini/disassembler.h"
+#include "components/zucchini/disassembler_dex.h"
+
+namespace {
+
+struct Environment {
+ Environment() { logging::SetMinLogLevel(logging::LOG_FATAL); }
+};
+
+} // namespace
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static Environment env;
+ if (!size)
+ return 0;
+ // Prepare data.
+ std::vector<uint8_t> mutable_data(data, data + size);
+ zucchini::ConstBufferView image(mutable_data.data(), mutable_data.size());
+
+ // Create disassembler. Early exit on failure.
+ auto disassembler_dex =
+ zucchini::Disassembler::Make<zucchini::DisassemblerDex>(image);
+ if (!disassembler_dex)
+ return 0;
+ CHECK_LE(disassembler_dex->size(), image.size());
+ zucchini::MutableBufferView mutable_image(mutable_data.data(),
+ disassembler_dex->size());
+
+ std::vector<zucchini::Reference> references;
+ // Read all references in the file.
+ auto groups = disassembler_dex->MakeReferenceGroups();
+ for (const auto& group : groups) {
+ auto reader = group.GetReader(disassembler_dex.get());
+ for (auto ref = reader->GetNext(); ref.has_value();
+ ref = reader->GetNext()) {
+ references.push_back(ref.value());
+ }
+ reader.reset();
+ auto writer = group.GetWriter(mutable_image, disassembler_dex.get());
+ for (const auto& ref : references)
+ writer->PutNext(ref);
+ references.clear();
+ }
+ return 0;
+}
diff --git a/chromium/components/zucchini/fuzzers/disassembler_win32_fuzzer.cc b/chromium/components/zucchini/fuzzers/disassembler_win32_fuzzer.cc
index f432dddc960..e2f0f67d843 100644
--- a/chromium/components/zucchini/fuzzers/disassembler_win32_fuzzer.cc
+++ b/chromium/components/zucchini/fuzzers/disassembler_win32_fuzzer.cc
@@ -5,68 +5,72 @@
#include <stddef.h>
#include <stdint.h>
+#include <map>
+#include <memory>
+#include <vector>
+
#include "base/logging.h"
#include "components/zucchini/buffer_view.h"
#include "components/zucchini/disassembler.h"
#include "components/zucchini/disassembler_win32.h"
+namespace {
+
struct Environment {
Environment() {
- logging::SetMinLogLevel(3); // Disable console spamming.
+ logging::SetMinLogLevel(logging::LOG_FATAL); // Disable console spamming.
}
};
-Environment* env = new Environment();
+// Helper function that uses |disassembler| to read all references from
+// |mutable_image| and write them back.
+void ReadAndWriteReferences(
+ std::unique_ptr<zucchini::Disassembler> disassembler,
+ std::vector<uint8_t>* mutable_data) {
+ zucchini::MutableBufferView mutable_image(mutable_data->data(),
+ disassembler->size());
+ std::vector<zucchini::Reference> references;
+ auto groups = disassembler->MakeReferenceGroups();
+ std::map<zucchini::PoolTag, std::vector<zucchini::Reference>>
+ references_of_pool;
+ for (const auto& group : groups) {
+ auto reader = group.GetReader(disassembler.get());
+ std::vector<zucchini::Reference>* refs =
+ &references_of_pool[group.pool_tag()];
+ for (auto ref = reader->GetNext(); ref.has_value();
+ ref = reader->GetNext()) {
+ refs->push_back(ref.value());
+ }
+ }
+ for (const auto& group : groups) {
+ auto writer = group.GetWriter(mutable_image, disassembler.get());
+ for (const auto& ref : references_of_pool[group.pool_tag()])
+ writer->PutNext(ref);
+ }
+}
+
+} // namespace
// Entry point for LibFuzzer.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- // Prep data.
- zucchini::ConstBufferView image(data, size);
+ static Environment env;
+ if (!size)
+ return 0;
+ // Prepare data.
+ std::vector<uint8_t> mutable_data(data, data + size);
+ zucchini::ConstBufferView image(mutable_data.data(), mutable_data.size());
// One of x86 or x64 should return a non-nullptr if the data is valid.
-
- // Output will be a pointer to zucchini::DisassemblerWin32X86 if successful
- // or nullptr otherwise.
auto disassembler_win32x86 =
zucchini::Disassembler::Make<zucchini::DisassemblerWin32X86>(image);
- if (disassembler_win32x86 != nullptr) {
- // Get the image size which has been shruken to the size understood by the
- // parser.
- auto parsed_image_size = disassembler_win32x86->image().size();
-
- // Parse the Win32 PE file and ensure nothing bad occurs.
- // TODO(ckitagawa): Actually validate that the output reference is within
- // the image.
- auto relocx86 = disassembler_win32x86->MakeReadRelocs(0, parsed_image_size);
- while (relocx86->GetNext().has_value()) {
- }
- auto abs32x86 = disassembler_win32x86->MakeReadAbs32(0, parsed_image_size);
- while (abs32x86->GetNext().has_value()) {
- }
- auto rel32x86 = disassembler_win32x86->MakeReadRel32(0, parsed_image_size);
- while (rel32x86->GetNext().has_value()) {
- }
+ if (disassembler_win32x86) {
+ ReadAndWriteReferences(std::move(disassembler_win32x86), &mutable_data);
+ return 0;
}
- // Output will be a pointer to zucchini::DisassemblerWin32X64 if successful
- // or nullptr otherwise.
auto disassembler_win32x64 =
zucchini::Disassembler::Make<zucchini::DisassemblerWin32X64>(image);
- if (disassembler_win32x64 != nullptr) {
- // Get the image size which has been shruken to the size understood by the
- // parser.
- auto parsed_image_size = disassembler_win32x64->image().size();
-
- // Parse the Win32 PE file and ensure nothing bad occurs.
- auto relocx64 = disassembler_win32x64->MakeReadRelocs(0, parsed_image_size);
- while (relocx64->GetNext().has_value()) {
- }
- auto abs32x64 = disassembler_win32x64->MakeReadAbs32(0, parsed_image_size);
- while (abs32x64->GetNext().has_value()) {
- }
- auto rel32x64 = disassembler_win32x64->MakeReadRel32(0, parsed_image_size);
- while (rel32x64->GetNext().has_value()) {
- }
- }
+ if (disassembler_win32x64)
+ ReadAndWriteReferences(std::move(disassembler_win32x64), &mutable_data);
return 0;
}
diff --git a/chromium/components/zucchini/fuzzers/file_pair.proto b/chromium/components/zucchini/fuzzers/file_pair.proto
index 22163819933..7fdc90809da 100644
--- a/chromium/components/zucchini/fuzzers/file_pair.proto
+++ b/chromium/components/zucchini/fuzzers/file_pair.proto
@@ -6,10 +6,16 @@ syntax = "proto2";
package zucchini.fuzzers;
-// NEXT_TAG = 3
+// NEXT_TAG = 4
message FilePair {
// File to generate patch from or apply patch to.
required bytes old_file = 1;
// New file to generate patch or the patch to apply.
required bytes new_or_patch_file = 2;
+ // Imposed matches to apply to the equivalence matches.
+ // Should be of the format:
+ // "#+#=#+#,#+#=#+#,..." (e.g., "1+2=3+4", "1+2=3+4,5+6=7+8"),
+ // where "#+#=#+#" encodes a match as 4 unsigned integers:
+ // [offset in "old", size in "old", offset in "new", size in "new"].
+ optional string imposed_matches = 3;
}
diff --git a/chromium/components/zucchini/fuzzers/generate_fuzzer_data.py b/chromium/components/zucchini/fuzzers/generate_fuzzer_data.py
index c182baf2578..5f5867beb41 100755
--- a/chromium/components/zucchini/fuzzers/generate_fuzzer_data.py
+++ b/chromium/components/zucchini/fuzzers/generate_fuzzer_data.py
@@ -28,14 +28,14 @@ def parse_args():
parser.add_argument('old_file', help='Old file to generate/apply patch.')
parser.add_argument('new_file', help='New file to generate patch from.')
parser.add_argument('patch_file', help='Patch filename to use.')
- parser.add_argument('output_dir',
- help='Directory to write binary protobuf to.')
+ parser.add_argument('output_file', help='File to write binary protobuf to.')
return parser.parse_args()
-def gen(old_file, new_file, patch_file, output_dir, is_raw, is_win):
+def gen(old_file, new_file, patch_file, output_file, is_raw, is_win):
"""Generates a new patch and binary encodes a protobuf pair."""
# Create output directory if missing.
+ output_dir = os.path.dirname(output_file)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
@@ -61,7 +61,7 @@ def gen(old_file, new_file, patch_file, output_dir, is_raw, is_win):
ret = subprocess.call([sys.executable,
os.path.join(ABS_PATH, 'create_seed_file_pair.py'),
os.path.abspath(protoc), old_file, patch_file,
- os.path.join(output_dir, 'seed_proto.bin')],
+ output_file],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
os.remove(patch_file)
@@ -72,8 +72,8 @@ def main():
args = parse_args()
return gen(os.path.join(ABS_TESTDATA_PATH, args.old_file),
os.path.join(ABS_TESTDATA_PATH, args.new_file),
- os.path.join(ABS_TESTDATA_PATH, args.patch_file),
- os.path.abspath(args.output_dir),
+ os.path.abspath(args.patch_file),
+ os.path.abspath(args.output_file),
args.raw,
platform.system() == 'Windows')
diff --git a/chromium/components/zucchini/fuzzers/imposed_ensemble_matcher_fuzzer.cc b/chromium/components/zucchini/fuzzers/imposed_ensemble_matcher_fuzzer.cc
new file mode 100644
index 00000000000..0dbcf86af8b
--- /dev/null
+++ b/chromium/components/zucchini/fuzzers/imposed_ensemble_matcher_fuzzer.cc
@@ -0,0 +1,67 @@
+// 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 <stdint.h>
+
+#include <iostream>
+#include <memory>
+
+#include "base/environment.h"
+#include "base/logging.h"
+#include "components/zucchini/buffer_sink.h"
+#include "components/zucchini/buffer_view.h"
+#include "components/zucchini/fuzzers/file_pair.pb.h"
+#include "components/zucchini/patch_writer.h"
+#include "components/zucchini/zucchini.h"
+#include "testing/libfuzzer/proto/lpm_interface.h"
+
+namespace {
+
+constexpr size_t kMinImageSize = 16;
+constexpr size_t kMaxImageSize = 1024;
+
+} // namespace
+
+struct Environment {
+ Environment() {
+ logging::SetMinLogLevel(logging::LOG_FATAL); // Disable console spamming.
+ }
+};
+
+DEFINE_BINARY_PROTO_FUZZER(const zucchini::fuzzers::FilePair& file_pair) {
+ static Environment env;
+ // Dump code for debugging.
+ if (base::Environment::Create()->HasVar("LPM_DUMP_NATIVE_INPUT")) {
+ std::cout << "Imposed Matches: " << file_pair.imposed_matches() << std::endl
+ << "Old File: " << file_pair.old_file() << std::endl
+ << "New File: " << file_pair.new_or_patch_file() << std::endl;
+ }
+
+ // Prepare data.
+ zucchini::ConstBufferView old_image(
+ reinterpret_cast<const uint8_t*>(file_pair.old_file().data()),
+ file_pair.old_file().size());
+ zucchini::ConstBufferView new_image(
+ reinterpret_cast<const uint8_t*>(file_pair.new_or_patch_file().data()),
+ file_pair.new_or_patch_file().size());
+
+ // Restrict image sizes to speed up fuzzing.
+ if (old_image.size() < kMinImageSize || old_image.size() > kMaxImageSize ||
+ new_image.size() < kMinImageSize || new_image.size() > kMaxImageSize) {
+ return;
+ }
+
+ // Generate a patch writer.
+ zucchini::EnsemblePatchWriter patch_writer(old_image, new_image);
+
+ // Fuzz Target.
+ zucchini::GenerateBufferImposed(old_image, new_image,
+ file_pair.imposed_matches(), &patch_writer);
+
+ // Write to buffer to avoid IO.
+ size_t patch_size = patch_writer.SerializedSize();
+ std::unique_ptr<uint8_t[]> patch_data(new uint8_t[patch_size]);
+ zucchini::BufferSink patch(patch_data.get(), patch_size);
+ patch_writer.SerializeInto(patch);
+}
diff --git a/chromium/components/zucchini/fuzzers/raw_gen_fuzzer.cc b/chromium/components/zucchini/fuzzers/raw_gen_fuzzer.cc
index 176412daf14..de63d95d0d0 100644
--- a/chromium/components/zucchini/fuzzers/raw_gen_fuzzer.cc
+++ b/chromium/components/zucchini/fuzzers/raw_gen_fuzzer.cc
@@ -5,9 +5,11 @@
#include <stdint.h>
#include <iostream>
+#include <memory>
#include "base/environment.h"
#include "base/logging.h"
+#include "components/zucchini/buffer_sink.h"
#include "components/zucchini/buffer_view.h"
#include "components/zucchini/fuzzers/file_pair.pb.h"
#include "components/zucchini/patch_writer.h"
@@ -16,14 +18,14 @@
namespace {
-constexpr int kMinImageSize = 16;
-constexpr int kMaxImageSize = 1024;
+constexpr size_t kMinImageSize = 16;
+constexpr size_t kMaxImageSize = 1024;
} // namespace
struct Environment {
Environment() {
- logging::SetMinLogLevel(3); // Disable console spamming.
+ logging::SetMinLogLevel(logging::LOG_FATAL); // Disable console spamming.
}
};
@@ -54,5 +56,16 @@ DEFINE_BINARY_PROTO_FUZZER(const zucchini::fuzzers::FilePair& file_pair) {
zucchini::EnsemblePatchWriter patch_writer(old_image, new_image);
// Fuzz Target.
- zucchini::GenerateRaw(old_image, new_image, &patch_writer);
+ zucchini::GenerateBufferRaw(old_image, new_image, &patch_writer);
+
+ // Check that the patch size is sane. Crash the fuzzer if this isn't the case
+ // as it is a failure in Zucchini's patch performance that is worth
+ // investigating.
+ size_t patch_size = patch_writer.SerializedSize();
+ CHECK_LE(patch_size, kMaxImageSize * 2);
+
+ // Write to buffer to avoid IO.
+ std::unique_ptr<uint8_t[]> patch_data(new uint8_t[patch_size]);
+ zucchini::BufferSink patch(patch_data.get(), patch_size);
+ patch_writer.SerializeInto(patch);
}
diff --git a/chromium/components/zucchini/fuzzers/ztf_gen_fuzzer.cc b/chromium/components/zucchini/fuzzers/ztf_gen_fuzzer.cc
new file mode 100644
index 00000000000..ee2d47c1374
--- /dev/null
+++ b/chromium/components/zucchini/fuzzers/ztf_gen_fuzzer.cc
@@ -0,0 +1,67 @@
+// 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 <stdint.h>
+
+#include <iostream>
+#include <memory>
+
+#include "base/environment.h"
+#include "base/logging.h"
+#include "components/zucchini/buffer_sink.h"
+#include "components/zucchini/buffer_view.h"
+#include "components/zucchini/fuzzers/file_pair.pb.h"
+#include "components/zucchini/patch_writer.h"
+#include "components/zucchini/zucchini_gen.h"
+#include "testing/libfuzzer/proto/lpm_interface.h"
+
+namespace {
+
+constexpr size_t kMinImageSize = 16;
+constexpr size_t kMaxImageSize = 1024;
+
+} // namespace
+
+struct Environment {
+ Environment() {
+ logging::SetMinLogLevel(logging::LOG_FATAL); // Disable console spamming.
+ }
+};
+
+Environment* env = new Environment();
+
+DEFINE_BINARY_PROTO_FUZZER(const zucchini::fuzzers::FilePair& file_pair) {
+ // Dump code for debugging.
+ if (base::Environment::Create()->HasVar("LPM_DUMP_NATIVE_INPUT")) {
+ std::cout << "Old File: " << file_pair.old_file() << std::endl
+ << "New File: " << file_pair.new_or_patch_file() << std::endl;
+ }
+
+ // Prepare data. These are originally Zucchini Text Format (ZTF) files but may
+ // in relatively unlikely circumstances mutate into other formats.
+ zucchini::ConstBufferView old_image(
+ reinterpret_cast<const uint8_t*>(file_pair.old_file().data()),
+ file_pair.old_file().size());
+ zucchini::ConstBufferView new_image(
+ reinterpret_cast<const uint8_t*>(file_pair.new_or_patch_file().data()),
+ file_pair.new_or_patch_file().size());
+
+ // Restrict image sizes to speed up fuzzing.
+ if (old_image.size() < kMinImageSize || old_image.size() > kMaxImageSize ||
+ new_image.size() < kMinImageSize || new_image.size() > kMaxImageSize) {
+ return;
+ }
+
+ // Generate a patch writer.
+ zucchini::EnsemblePatchWriter patch_writer(old_image, new_image);
+
+ // Fuzz Target.
+ zucchini::GenerateBuffer(old_image, new_image, &patch_writer);
+
+ // Write to buffer to avoid IO.
+ size_t patch_size = patch_writer.SerializedSize();
+ std::unique_ptr<uint8_t[]> patch_data(new uint8_t[patch_size]);
+ zucchini::BufferSink patch(patch_data.get(), patch_size);
+ patch_writer.SerializeInto(patch);
+}
diff --git a/chromium/components/zucchini/integration_test.cc b/chromium/components/zucchini/integration_test.cc
index c4c7004d349..2d2e61f7456 100644
--- a/chromium/components/zucchini/integration_test.cc
+++ b/chromium/components/zucchini/integration_test.cc
@@ -23,8 +23,7 @@ namespace zucchini {
base::FilePath MakeTestPath(const std::string& filename) {
base::FilePath path;
DCHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &path));
- return path.AppendASCII("chrome")
- .AppendASCII("installer")
+ return path.AppendASCII("components")
.AppendASCII("zucchini")
.AppendASCII("testdata")
.AppendASCII(filename);
@@ -49,8 +48,8 @@ void TestGenApply(const std::string& old_filename,
// Generate patch from "old" to "new".
ASSERT_EQ(status::kStatusSuccess,
- raw ? GenerateRaw(old_region, new_region, &patch_writer)
- : GenerateEnsemble(old_region, new_region, &patch_writer));
+ raw ? GenerateBufferRaw(old_region, new_region, &patch_writer)
+ : GenerateBuffer(old_region, new_region, &patch_writer));
size_t patch_size = patch_writer.SerializedSize();
EXPECT_GE(patch_size, 80U); // Minimum size is empty patch.
@@ -73,9 +72,9 @@ void TestGenApply(const std::string& old_filename,
// Apply patch to "old" to get "patched new", ensure it's identical to "new".
std::vector<uint8_t> patched_new_buffer(new_region.size());
- ASSERT_EQ(status::kStatusSuccess,
- Apply(old_region, *patch_reader,
- {patched_new_buffer.data(), patched_new_buffer.size()}));
+ ASSERT_EQ(status::kStatusSuccess, ApplyBuffer(old_region, *patch_reader,
+ {patched_new_buffer.data(),
+ patched_new_buffer.size()}));
// Note that |new_region| and |patched_new_buffer| are the same size.
EXPECT_TRUE(std::equal(new_region.begin(), new_region.end(),
diff --git a/chromium/components/zucchini/main_utils.cc b/chromium/components/zucchini/main_utils.cc
index b6b564280ef..6a09b32c8ba 100644
--- a/chromium/components/zucchini/main_utils.cc
+++ b/chromium/components/zucchini/main_utils.cc
@@ -70,7 +70,7 @@ constexpr Command kCommands[] = {
{"apply", "-apply <old_file> <patch_file> <new_file> [-keep]", 3,
&MainApply},
{"read", "-read <exe> [-dump]", 1, &MainRead},
- {"detect", "-detect <archive_file> [-dd=format#]", 1, &MainDetect},
+ {"detect", "-detect <archive_file>", 1, &MainDetect},
{"match", "-match <old_file> <new_file> [-impose=#+#=#+#,#+#=#+#,...]", 2,
&MainMatch},
{"crc32", "-crc32 <file>", 1, &MainCrc32},
diff --git a/chromium/components/zucchini/mapped_file.cc b/chromium/components/zucchini/mapped_file.cc
index 13c1afd2739..497b42a1c57 100644
--- a/chromium/components/zucchini/mapped_file.cc
+++ b/chromium/components/zucchini/mapped_file.cc
@@ -12,7 +12,7 @@
namespace zucchini {
-MappedFileReader::MappedFileReader(base::File&& file) {
+MappedFileReader::MappedFileReader(base::File file) {
if (!file.IsValid()) {
error_ = "Invalid file.";
return; // |buffer_| will be uninitialized, and therefore invalid.
@@ -23,7 +23,7 @@ MappedFileReader::MappedFileReader(base::File&& file) {
}
MappedFileWriter::MappedFileWriter(const base::FilePath& file_path,
- base::File&& file,
+ base::File file,
size_t length)
: file_path_(file_path), delete_behavior_(kManualDeleteOnClose) {
if (!file.IsValid()) {
diff --git a/chromium/components/zucchini/mapped_file.h b/chromium/components/zucchini/mapped_file.h
index 540f9474b97..e8cd5907483 100644
--- a/chromium/components/zucchini/mapped_file.h
+++ b/chromium/components/zucchini/mapped_file.h
@@ -23,7 +23,7 @@ class MappedFileReader {
public:
// Maps |file| to memory for reading. Also validates |file|. Errors are
// available via HasError() and error().
- explicit MappedFileReader(base::File&& file);
+ explicit MappedFileReader(base::File file);
const uint8_t* data() const { return buffer_.data(); }
size_t length() const { return buffer_.length(); }
@@ -47,7 +47,7 @@ class MappedFileWriter {
// UNIX systems, but can be empty if auto delete is not needed. Errors are
// available via HasError() and error().
MappedFileWriter(const base::FilePath& file_path,
- base::File&& file,
+ base::File file,
size_t length);
~MappedFileWriter();
diff --git a/chromium/components/zucchini/reloc_utils.cc b/chromium/components/zucchini/reloc_utils.cc
index 84f488e5f10..d6f69b90fec 100644
--- a/chromium/components/zucchini/reloc_utils.cc
+++ b/chromium/components/zucchini/reloc_utils.cc
@@ -81,7 +81,7 @@ RelocRvaReaderWin32::RelocRvaReaderWin32(
offset_t cur_reloc_units_offset = cur_reloc_units_.begin() - image_.begin();
if (lo > cur_reloc_units_offset) {
offset_t delta =
- ceil<offset_t>(lo - cur_reloc_units_offset, kRelocUnitSize);
+ AlignCeil<offset_t>(lo - cur_reloc_units_offset, kRelocUnitSize);
cur_reloc_units_.Skip(delta);
}
}
@@ -182,10 +182,12 @@ void RelocWriterWin32::PutNext(Reference ref) {
--block_it;
rva_t rva_hi_bits = image_.read<pe::RelocHeader>(*block_it).rva_hi;
rva_t target_rva = target_offset_to_rva_.Convert(ref.target);
- rva_t rva_lo_bits = target_rva - rva_hi_bits;
- DCHECK_EQ(rva_lo_bits & 0xFFF, rva_lo_bits);
- image_.write<uint16_t>(ref.location,
- (rva_lo_bits & 0xFFF) | (reloc_type_ << 12));
+ rva_t rva_lo_bits = (target_rva - rva_hi_bits) & 0xFFF;
+ if (target_rva != rva_hi_bits + rva_lo_bits) {
+ LOG(ERROR) << "Invalid RVA at " << AsHex<8>(ref.location) << ".";
+ return;
+ }
+ image_.write<uint16_t>(ref.location, rva_lo_bits | (reloc_type_ << 12));
}
} // namespace zucchini
diff --git a/chromium/components/zucchini/reloc_utils_unittest.cc b/chromium/components/zucchini/reloc_utils_unittest.cc
index e75264ce74f..65acf5b51e4 100644
--- a/chromium/components/zucchini/reloc_utils_unittest.cc
+++ b/chromium/components/zucchini/reloc_utils_unittest.cc
@@ -260,8 +260,6 @@ TEST_F(RelocUtilsWin32Test, ReadWrite) {
EXPECT_EQ(exp_reloc_data1,
Sub(image_data, reloc_region_.lo(), reloc_region_.hi()));
- EXPECT_DCHECK_DEATH(writer->PutNext({0x608, 0x2000}));
-
writer->PutNext({0x61C, 0x2950});
std::vector<uint8_t> exp_reloc_data2 = ParseHexString(
"00 10 04 00 10 00 00 00 83 3F 18 A3 F8 A7 FF 0F "
diff --git a/chromium/components/zucchini/type_dex.h b/chromium/components/zucchini/type_dex.h
index 508d3313af4..432a03139d6 100644
--- a/chromium/components/zucchini/type_dex.h
+++ b/chromium/components/zucchini/type_dex.h
@@ -227,6 +227,36 @@ struct AnnotationOffItem {
uint32_t annotation_off;
};
+// field_annotation
+struct FieldAnnotation {
+ uint32_t field_idx;
+ uint32_t annotations_off;
+};
+
+// method_annotation
+struct MethodAnnotation {
+ uint32_t method_idx;
+ uint32_t annotations_off;
+};
+
+// parameter_annotation
+struct ParameterAnnotation {
+ uint32_t method_idx;
+ uint32_t annotations_off;
+};
+
+// annotations_directory_item
+struct AnnotationsDirectoryItem {
+ uint32_t class_annotations_off;
+ uint32_t fields_size;
+ uint32_t annotated_methods_size;
+ uint32_t annotated_parameters_size;
+ // FieldAnnotation field_annotations[fields_size];
+ // MethodAnnotation method_annotations[annotated_methods_size];
+ // ParameterAnnotation parameter_annotations[annotated_parameters_size];
+ // All *Annotation are 8 bytes each.
+};
+
// try_item
struct TryItem {
uint32_t start_addr;
diff --git a/chromium/components/zucchini/zucchini.h b/chromium/components/zucchini/zucchini.h
index e9093eb6aca..984744079b8 100644
--- a/chromium/components/zucchini/zucchini.h
+++ b/chromium/components/zucchini/zucchini.h
@@ -11,7 +11,11 @@
#include "components/zucchini/patch_reader.h"
#include "components/zucchini/patch_writer.h"
-// Definitions, structures, and interfaces for the Zucchini library.
+// Core Zucchini library, consisting of:
+// - Global constants.
+// - Patch gen and apply functions, where "old" and "new" data are represented
+// as buffers, and patch data represented as EnsemblePatchWriter or
+// EnsemblePatchReader.
namespace zucchini {
@@ -36,9 +40,9 @@ enum Code {
// Generates ensemble patch from |old_image| to |new_image| using the default
// element detection and matching heuristics, writes the results to
// |patch_writer|, and returns a status::Code.
-status::Code GenerateEnsemble(ConstBufferView old_image,
- ConstBufferView new_image,
- EnsemblePatchWriter* patch_writer);
+status::Code GenerateBuffer(ConstBufferView old_image,
+ ConstBufferView new_image,
+ EnsemblePatchWriter* patch_writer);
// Same as GenerateEnsemble(), but if |imposed_matches| is non-empty, then
// overrides default element detection and matching heuristics with custom
@@ -46,23 +50,22 @@ status::Code GenerateEnsemble(ConstBufferView old_image,
// "#+#=#+#,#+#=#+#,..." (e.g., "1+2=3+4", "1+2=3+4,5+6=7+8"),
// where "#+#=#+#" encodes a match as 4 unsigned integers:
// [offset in "old", size in "old", offset in "new", size in "new"].
-status::Code GenerateEnsembleWithImposedMatches(
- ConstBufferView old_image,
- ConstBufferView new_image,
- std::string imposed_matches,
- EnsemblePatchWriter* patch_writer);
+status::Code GenerateBufferImposed(ConstBufferView old_image,
+ ConstBufferView new_image,
+ std::string imposed_matches,
+ EnsemblePatchWriter* patch_writer);
// Generates raw patch from |old_image| to |new_image|, and writes it to
// |patch_writer|.
-status::Code GenerateRaw(ConstBufferView old_image,
- ConstBufferView new_image,
- EnsemblePatchWriter* patch_writer);
+status::Code GenerateBufferRaw(ConstBufferView old_image,
+ ConstBufferView new_image,
+ EnsemblePatchWriter* patch_writer);
// Applies |patch_reader| to |old_image| to build |new_image|, which refers to
// preallocated memory of sufficient size.
-status::Code Apply(ConstBufferView old_image,
- const EnsemblePatchReader& patch_reader,
- MutableBufferView new_image);
+status::Code ApplyBuffer(ConstBufferView old_image,
+ const EnsemblePatchReader& patch_reader,
+ MutableBufferView new_image);
} // namespace zucchini
diff --git a/chromium/components/zucchini/zucchini_apply.cc b/chromium/components/zucchini/zucchini_apply.cc
index 8969e3bbe56..e20208588fd 100644
--- a/chromium/components/zucchini/zucchini_apply.cc
+++ b/chromium/components/zucchini/zucchini_apply.cc
@@ -101,13 +101,19 @@ bool ApplyReferencesCorrection(ExecutableType exe_type,
LOG(ERROR) << "Failed to create Disassembler";
return false;
}
+ if (old_disasm->size() != old_image.size() ||
+ new_disasm->size() != new_image.size()) {
+ LOG(ERROR) << "Disassembler and element size mismatch";
+ return false;
+ }
ReferenceDeltaSource ref_delta_source = patch.GetReferenceDeltaSource();
std::map<PoolTag, std::vector<ReferenceGroup>> pool_groups;
for (const auto& ref_group : old_disasm->MakeReferenceGroups())
pool_groups[ref_group.pool_tag()].push_back(ref_group);
- OffsetMapper offset_mapper(patch.GetEquivalenceSource());
+ OffsetMapper offset_mapper(patch.GetEquivalenceSource(), old_image.size(),
+ new_image.size());
std::vector<ReferenceGroup> new_groups = new_disasm->MakeReferenceGroups();
for (const auto& pool_and_sub_groups : pool_groups) {
@@ -144,7 +150,8 @@ bool ApplyReferencesCorrection(ExecutableType exe_type,
DCHECK_GE(ref->location, equivalence->src_offset);
DCHECK_LT(ref->location, equivalence->src_end());
- offset_t projected_target = offset_mapper.ForwardProject(ref->target);
+ offset_t projected_target =
+ offset_mapper.ExtendedForwardProject(ref->target);
offset_t expected_key = targets.KeyForNearestOffset(projected_target);
auto delta = ref_delta_source.GetNext();
if (!delta.has_value()) {
@@ -183,9 +190,9 @@ bool ApplyElement(ExecutableType exe_type,
/******** Exported Functions ********/
-status::Code Apply(ConstBufferView old_image,
- const EnsemblePatchReader& patch_reader,
- MutableBufferView new_image) {
+status::Code ApplyBuffer(ConstBufferView old_image,
+ const EnsemblePatchReader& patch_reader,
+ MutableBufferView new_image) {
if (!patch_reader.CheckOldFile(old_image)) {
LOG(ERROR) << "Invalid old_image.";
return status::kStatusInvalidOldImage;
diff --git a/chromium/components/zucchini/zucchini_commands.cc b/chromium/components/zucchini/zucchini_commands.cc
index 62dd20d215d..1abe08fe522 100644
--- a/chromium/components/zucchini/zucchini_commands.cc
+++ b/chromium/components/zucchini/zucchini_commands.cc
@@ -38,67 +38,11 @@ constexpr char kSwitchRaw[] = "raw";
zucchini::status::Code MainGen(MainParams params) {
CHECK_EQ(3U, params.file_paths.size());
-
- // TODO(huangs): Move implementation to zucchini_integration.cc.
- using base::File;
- File old_file(params.file_paths[0], File::FLAG_OPEN | File::FLAG_READ);
- zucchini::MappedFileReader old_image(std::move(old_file));
- if (old_image.HasError()) {
- LOG(ERROR) << "Error with file " << params.file_paths[0].value() << ": "
- << old_image.error();
- return zucchini::status::kStatusFileReadError;
- }
- File new_file(params.file_paths[1], File::FLAG_OPEN | File::FLAG_READ);
- zucchini::MappedFileReader new_image(std::move(new_file));
- if (new_image.HasError()) {
- LOG(ERROR) << "Error with file " << params.file_paths[1].value() << ": "
- << new_image.error();
- return zucchini::status::kStatusFileReadError;
- }
- zucchini::EnsemblePatchWriter patch_writer(old_image.region(),
- new_image.region());
-
- zucchini::status::Code result = zucchini::status::kStatusSuccess;
- if (params.command_line.HasSwitch(kSwitchRaw)) {
- result = GenerateRaw(old_image.region(), new_image.region(), &patch_writer);
- } else {
- // May be empty.
- std::string imposed_matches =
- params.command_line.GetSwitchValueASCII(kSwitchImpose);
- result = GenerateEnsembleWithImposedMatches(
- old_image.region(), new_image.region(), std::move(imposed_matches),
- &patch_writer);
- }
-
- if (result != zucchini::status::kStatusSuccess) {
- params.out << "Fatal error encountered when generating patch." << std::endl;
- return result;
- }
-
- // By default, delete patch on destruction, to avoid having lingering files in
- // case of a failure. On Windows deletion can be done by the OS.
- File patch_file(params.file_paths[2], File::FLAG_CREATE_ALWAYS |
- File::FLAG_READ | File::FLAG_WRITE |
- File::FLAG_SHARE_DELETE |
- File::FLAG_CAN_DELETE_ON_CLOSE);
- zucchini::MappedFileWriter patch(params.file_paths[2], std::move(patch_file),
- patch_writer.SerializedSize());
- if (patch.HasError()) {
- LOG(ERROR) << "Error with file " << params.file_paths[2].value() << ": "
- << patch.error();
- return zucchini::status::kStatusFileWriteError;
- }
-
- if (params.command_line.HasSwitch(kSwitchKeep))
- patch.Keep();
-
- if (!patch_writer.SerializeInto(patch.region()))
- return zucchini::status::kStatusPatchWriteError;
-
- // Successfully created patch. Explicitly request file to be kept.
- if (!patch.Keep())
- return zucchini::status::kStatusFileWriteError;
- return zucchini::status::kStatusSuccess;
+ return zucchini::Generate(
+ params.file_paths[0], params.file_paths[1], params.file_paths[2],
+ params.command_line.HasSwitch(kSwitchKeep),
+ params.command_line.HasSwitch(kSwitchRaw),
+ params.command_line.GetSwitchValueASCII(kSwitchImpose));
}
zucchini::status::Code MainApply(MainParams params) {
diff --git a/chromium/components/zucchini/zucchini_gen.cc b/chromium/components/zucchini/zucchini_gen.cc
index 29be8147841..0bb465809d1 100644
--- a/chromium/components/zucchini/zucchini_gen.cc
+++ b/chromium/components/zucchini/zucchini_gen.cc
@@ -211,7 +211,8 @@ bool GenerateReferencesDelta(const ReferenceSet& src_refs,
dst_ref->location - equiv.dst_offset);
offset_t old_offset =
src_refs.target_pool().OffsetForKey(src_ref->target_key);
- offset_t new_estimated_offset = offset_mapper.ForwardProject(old_offset);
+ offset_t new_estimated_offset =
+ offset_mapper.ExtendedForwardProject(old_offset);
offset_t new_estimated_key =
projected_target_pool.KeyForNearestOffset(new_estimated_offset);
offset_t new_offset =
@@ -288,7 +289,7 @@ bool GenerateExecutableElement(ExecutableType exe_type,
EquivalenceMap equivalences =
CreateEquivalenceMap(old_image_index, new_image_index,
new_disasm->num_equivalence_iterations());
- OffsetMapper offset_mapper(equivalences);
+ OffsetMapper offset_mapper(equivalences, old_image.size(), new_image.size());
ReferenceDeltaSink reference_delta_sink;
for (const auto& old_targets : old_image_index.target_pools()) {
@@ -319,13 +320,13 @@ bool GenerateExecutableElement(ExecutableType exe_type,
reference_bytes_mixer.get(), patch_writer);
}
-status::Code GenerateEnsembleCommon(ConstBufferView old_image,
- ConstBufferView new_image,
- std::unique_ptr<EnsembleMatcher> matcher,
- EnsemblePatchWriter* patch_writer) {
+status::Code GenerateBufferCommon(ConstBufferView old_image,
+ ConstBufferView new_image,
+ std::unique_ptr<EnsembleMatcher> matcher,
+ EnsemblePatchWriter* patch_writer) {
if (!matcher->RunMatch(old_image, new_image)) {
LOG(INFO) << "RunMatch() failed, generating raw patch.";
- return GenerateRaw(old_image, new_image, patch_writer);
+ return GenerateBufferRaw(old_image, new_image, patch_writer);
}
const std::vector<ElementMatch>& matches = matcher->matches();
@@ -335,7 +336,7 @@ status::Code GenerateEnsembleCommon(ConstBufferView old_image,
size_t num_elements = matches.size();
if (num_elements == 0) {
LOG(INFO) << "No nontrival matches, generating raw patch.";
- return GenerateRaw(old_image, new_image, patch_writer);
+ return GenerateBufferRaw(old_image, new_image, patch_writer);
}
// "Gaps" are |new_image| bytes not covered by new_elements in |matches|.
@@ -421,30 +422,29 @@ status::Code GenerateEnsembleCommon(ConstBufferView old_image,
/******** Exported Functions ********/
-status::Code GenerateEnsemble(ConstBufferView old_image,
- ConstBufferView new_image,
- EnsemblePatchWriter* patch_writer) {
- return GenerateEnsembleCommon(
+status::Code GenerateBuffer(ConstBufferView old_image,
+ ConstBufferView new_image,
+ EnsemblePatchWriter* patch_writer) {
+ return GenerateBufferCommon(
old_image, new_image, std::make_unique<HeuristicEnsembleMatcher>(nullptr),
patch_writer);
}
-status::Code GenerateEnsembleWithImposedMatches(
- ConstBufferView old_image,
- ConstBufferView new_image,
- std::string imposed_matches,
- EnsemblePatchWriter* patch_writer) {
+status::Code GenerateBufferImposed(ConstBufferView old_image,
+ ConstBufferView new_image,
+ std::string imposed_matches,
+ EnsemblePatchWriter* patch_writer) {
if (imposed_matches.empty())
- return GenerateEnsemble(old_image, new_image, patch_writer);
+ return GenerateBuffer(old_image, new_image, patch_writer);
- return GenerateEnsembleCommon(
+ return GenerateBufferCommon(
old_image, new_image,
std::make_unique<ImposedEnsembleMatcher>(imposed_matches), patch_writer);
}
-status::Code GenerateRaw(ConstBufferView old_image,
- ConstBufferView new_image,
- EnsemblePatchWriter* patch_writer) {
+status::Code GenerateBufferRaw(ConstBufferView old_image,
+ ConstBufferView new_image,
+ EnsemblePatchWriter* patch_writer) {
ImageIndex old_image_index(old_image);
EncodedView old_view(old_image_index);
std::vector<offset_t> old_sa =
diff --git a/chromium/components/zucchini/zucchini_gen_unittest.cc b/chromium/components/zucchini/zucchini_gen_unittest.cc
index fb16cdd7c1c..97b223ea20f 100644
--- a/chromium/components/zucchini/zucchini_gen_unittest.cc
+++ b/chromium/components/zucchini/zucchini_gen_unittest.cc
@@ -33,6 +33,11 @@ std::vector<int32_t> GenerateReferencesDeltaTest(
std::vector<offset_t>&& exp_old_targets,
std::vector<offset_t>&& exp_projected_old_targets,
EquivalenceMap&& equivalence_map) {
+ // OffsetMapper needs image sizes for forward-projection overflow check. These
+ // are tested elsewhere, so just use arbitrary large value.
+ constexpr size_t kOldImageSize = 1000000;
+ constexpr size_t kNewImageSize = 1001000;
+
ReferenceDeltaSink reference_delta_sink;
TargetPool old_targets;
@@ -46,7 +51,7 @@ std::vector<int32_t> GenerateReferencesDeltaTest(
ReferenceSet new_refs({1, TypeTag(0), PoolTag(0)}, new_targets);
new_refs.InitReferences(new_references);
- OffsetMapper offset_mapper(equivalence_map);
+ OffsetMapper offset_mapper(equivalence_map, kOldImageSize, kNewImageSize);
TargetPool projected_old_targets = old_targets;
projected_old_targets.FilterAndProject(offset_mapper);
diff --git a/chromium/components/zucchini/zucchini_integration.cc b/chromium/components/zucchini/zucchini_integration.cc
index 11496587e8c..97790a34281 100644
--- a/chromium/components/zucchini/zucchini_integration.cc
+++ b/chromium/components/zucchini/zucchini_integration.cc
@@ -19,92 +19,172 @@ struct FileNames {
FileNames() : is_dummy(true) {
// Use fake names.
old_name = old_name.AppendASCII("old_name");
- patch_name = patch_name.AppendASCII("patch_name");
new_name = new_name.AppendASCII("new_name");
+ patch_name = patch_name.AppendASCII("patch_name");
}
FileNames(const base::FilePath& old_name,
- const base::FilePath& patch_name,
- const base::FilePath& new_name)
+ const base::FilePath& new_name,
+ const base::FilePath& patch_name)
: old_name(old_name),
- patch_name(patch_name),
new_name(new_name),
+ patch_name(patch_name),
is_dummy(false) {}
base::FilePath old_name;
- base::FilePath patch_name;
base::FilePath new_name;
+ base::FilePath patch_name;
// A flag to decide whether the filenames are only for error output.
const bool is_dummy;
};
-status::Code ApplyCommon(base::File&& old_file_handle,
- base::File&& patch_file_handle,
- base::File&& new_file_handle,
+status::Code GenerateCommon(base::File old_file,
+ base::File new_file,
+ base::File patch_file,
+ const FileNames& names,
+ bool force_keep,
+ bool is_raw,
+ std::string imposed_matches) {
+ MappedFileReader mapped_old(std::move(old_file));
+ if (mapped_old.HasError()) {
+ LOG(ERROR) << "Error with file " << names.old_name.value() << ": "
+ << mapped_old.error();
+ return status::kStatusFileReadError;
+ }
+
+ MappedFileReader mapped_new(std::move(new_file));
+ if (mapped_new.HasError()) {
+ LOG(ERROR) << "Error with file " << names.new_name.value() << ": "
+ << mapped_new.error();
+ return status::kStatusFileReadError;
+ }
+
+ status::Code result = status::kStatusSuccess;
+ EnsemblePatchWriter patch_writer(mapped_old.region(), mapped_new.region());
+ if (is_raw) {
+ result = GenerateBufferRaw(mapped_old.region(), mapped_new.region(),
+ &patch_writer);
+ } else {
+ result = GenerateBufferImposed(mapped_old.region(), mapped_new.region(),
+ std::move(imposed_matches), &patch_writer);
+ }
+ if (result != status::kStatusSuccess) {
+ LOG(ERROR) << "Fatal error encountered when generating patch.";
+ return result;
+ }
+
+ // By default, delete patch on destruction, to avoid having lingering files in
+ // case of a failure. On Windows deletion can be done by the OS.
+ MappedFileWriter mapped_patch(names.patch_name, std::move(patch_file),
+ patch_writer.SerializedSize());
+ if (mapped_patch.HasError()) {
+ LOG(ERROR) << "Error with file " << names.patch_name.value() << ": "
+ << mapped_patch.error();
+ return status::kStatusFileWriteError;
+ }
+ if (force_keep)
+ mapped_patch.Keep();
+
+ if (!patch_writer.SerializeInto(mapped_patch.region()))
+ return status::kStatusPatchWriteError;
+
+ // Successfully created patch. Explicitly request file to be kept.
+ if (!mapped_patch.Keep())
+ return status::kStatusFileWriteError;
+ return status::kStatusSuccess;
+}
+
+status::Code ApplyCommon(base::File old_file,
+ base::File patch_file,
+ base::File new_file,
const FileNames& names,
bool force_keep) {
- MappedFileReader patch_file(std::move(patch_file_handle));
- if (patch_file.HasError()) {
+ MappedFileReader mapped_patch(std::move(patch_file));
+ if (mapped_patch.HasError()) {
LOG(ERROR) << "Error with file " << names.patch_name.value() << ": "
- << patch_file.error();
+ << mapped_patch.error();
return status::kStatusFileReadError;
}
- auto patch_reader =
- zucchini::EnsemblePatchReader::Create(patch_file.region());
+ auto patch_reader = EnsemblePatchReader::Create(mapped_patch.region());
if (!patch_reader.has_value()) {
LOG(ERROR) << "Error reading patch header.";
return status::kStatusPatchReadError;
}
- MappedFileReader old_file(std::move(old_file_handle));
- if (old_file.HasError()) {
+ MappedFileReader mapped_old(std::move(old_file));
+ if (mapped_old.HasError()) {
LOG(ERROR) << "Error with file " << names.old_name.value() << ": "
- << old_file.error();
+ << mapped_old.error();
return status::kStatusFileReadError;
}
- zucchini::PatchHeader header = patch_reader->header();
+ PatchHeader header = patch_reader->header();
// By default, delete output on destruction, to avoid having lingering files
// in case of a failure. On Windows deletion can be done by the OS.
- base::FilePath file_path;
- if (!names.is_dummy)
- file_path = base::FilePath(names.new_name);
-
- MappedFileWriter new_file(file_path, std::move(new_file_handle),
- header.new_size);
- if (new_file.HasError()) {
+ MappedFileWriter mapped_new(names.new_name, std::move(new_file),
+ header.new_size);
+ if (mapped_new.HasError()) {
LOG(ERROR) << "Error with file " << names.new_name.value() << ": "
- << new_file.error();
+ << mapped_new.error();
return status::kStatusFileWriteError;
}
-
if (force_keep)
- new_file.Keep();
+ mapped_new.Keep();
- zucchini::status::Code result =
- zucchini::Apply(old_file.region(), *patch_reader, new_file.region());
+ status::Code result =
+ ApplyBuffer(mapped_old.region(), *patch_reader, mapped_new.region());
if (result != status::kStatusSuccess) {
LOG(ERROR) << "Fatal error encountered while applying patch.";
return result;
}
- // Successfully patch |new_file|. Explicitly request file to be kept.
- if (!new_file.Keep())
+ // Successfully patch |mapped_new|. Explicitly request file to be kept.
+ if (!mapped_new.Keep())
return status::kStatusFileWriteError;
return status::kStatusSuccess;
}
} // namespace
-status::Code Apply(base::File&& old_file_handle,
- base::File&& patch_file_handle,
- base::File&& new_file_handle,
+status::Code Generate(base::File old_file,
+ base::File new_file,
+ base::File patch_file,
+ bool force_keep,
+ bool is_raw,
+ std::string imposed_matches) {
+ const FileNames file_names;
+ return GenerateCommon(std::move(old_file), std::move(new_file),
+ std::move(patch_file), file_names, force_keep, is_raw,
+ std::move(imposed_matches));
+}
+
+status::Code Generate(const base::FilePath& old_path,
+ const base::FilePath& new_path,
+ const base::FilePath& patch_path,
+ bool force_keep,
+ bool is_raw,
+ std::string imposed_matches) {
+ using base::File;
+ File old_file(old_path, File::FLAG_OPEN | File::FLAG_READ);
+ File new_file(new_path, File::FLAG_OPEN | File::FLAG_READ);
+ File patch_file(patch_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ |
+ File::FLAG_WRITE | File::FLAG_SHARE_DELETE |
+ File::FLAG_CAN_DELETE_ON_CLOSE);
+ const FileNames file_names(old_path, new_path, patch_path);
+ return GenerateCommon(std::move(old_file), std::move(new_file),
+ std::move(patch_file), file_names, force_keep, is_raw,
+ std::move(imposed_matches));
+}
+
+status::Code Apply(base::File old_file,
+ base::File patch_file,
+ base::File new_file,
bool force_keep) {
const FileNames file_names;
- return ApplyCommon(std::move(old_file_handle), std::move(patch_file_handle),
- std::move(new_file_handle), file_names, force_keep);
+ return ApplyCommon(std::move(old_file), std::move(patch_file),
+ std::move(new_file), file_names, force_keep);
}
status::Code Apply(const base::FilePath& old_path,
@@ -117,7 +197,7 @@ status::Code Apply(const base::FilePath& old_path,
File new_file(new_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ |
File::FLAG_WRITE | File::FLAG_SHARE_DELETE |
File::FLAG_CAN_DELETE_ON_CLOSE);
- const FileNames file_names(old_path, patch_path, new_path);
+ const FileNames file_names(old_path, new_path, patch_path);
return ApplyCommon(std::move(old_file), std::move(patch_file),
std::move(new_file), file_names, force_keep);
}
diff --git a/chromium/components/zucchini/zucchini_integration.h b/chromium/components/zucchini/zucchini_integration.h
index ce98b281975..2ae6091b0dd 100644
--- a/chromium/components/zucchini/zucchini_integration.h
+++ b/chromium/components/zucchini/zucchini_integration.h
@@ -5,28 +5,59 @@
#ifndef COMPONENTS_ZUCCHINI_ZUCCHINI_INTEGRATION_H_
#define COMPONENTS_ZUCCHINI_ZUCCHINI_INTEGRATION_H_
+#include <string>
+
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "components/zucchini/zucchini.h"
+// Zucchini integration interface to wrap core Zucchini library with file I/O.
+
namespace zucchini {
-// Applies the patch in |patch_file| to the bytes in |old_file| and writes the
-// result to |new_file|. Since this uses memory mapped files, crashes are
-// expected in case of I/O errors. On Windows, |new_file| is kept iff returned
+// Generates a patch to transform |old_file| to |new_file|, and writes the
+// result to |patch_file|. Since this uses memory mapped files, crashes are
+// expected in case of I/O errors. On Windows, |patch_file| is kept iff returned
// code is kStatusSuccess or if |force_keep == true|, and is deleted otherwise.
// For UNIX systems the caller needs to do cleanup since it has ownership of the
-// base::File params and Zucchini has no knowledge of which base::FilePath to
+// base::File params, and Zucchini has no knowledge of which base::FilePath to
+// delete. If |is_raw == true| then uses Raw Zucchini. If |imposed_matches| is
+// non-empty, then overrides default element detection and matching heuristics
+// with custom element matching encoded in |imposed_matches|, which should be
+// formatted as:
+// "#+#=#+#,#+#=#+#,..." (e.g., "1+2=3+4", "1+2=3+4,5+6=7+8"),
+// where "#+#=#+#" encodes a match as 4 unsigned integers:
+// [offset in "old", size in "old", offset in "new", size in "new"].
+status::Code Generate(base::File old_file,
+ base::File new_file,
+ base::File patch_file,
+ bool force_keep = false,
+ bool is_raw = false,
+ std::string imposed_matches = "");
+
+// Alternative Generate() interface that takes base::FilePath as arguments.
+// Performs proper cleanup in Windows and UNIX if failure occurs.
+status::Code Generate(const base::FilePath& old_path,
+ const base::FilePath& new_path,
+ const base::FilePath& patch_path,
+ bool force_keep = false,
+ bool is_raw = false,
+ std::string imposed_matches = "");
+
+// Applies the patch in |patch_file| to |old_file|, and writes the result to
+// |new_file|. Since this uses memory mapped files, crashes are expected in case
+// of I/O errors. On Windows, |new_file| is kept iff returned code is
+// kStatusSuccess or if |force_keep == true|, and is deleted otherwise. For UNIX
+// systems the caller needs to do cleanup since it has ownership of the
+// base::File params, and Zucchini has no knowledge of which base::FilePath to
// delete.
-status::Code Apply(base::File&& old_file,
- base::File&& patch_file,
- base::File&& new_file,
+status::Code Apply(base::File old_file,
+ base::File patch_file,
+ base::File new_file,
bool force_keep = false);
-// Applies the patch in |patch_path| to the bytes in |old_path| and writes the
-// result to |new_path|. Since this uses memory mapped files, crashes are
-// expected in case of I/O errors. |new_path| is kept iff returned code is
-// kStatusSuccess or if |force_keep == true|, and is deleted otherwise.
+// Alternative Apply() interface that takes base::FilePath as arguments.
+// Performs proper cleanup in Windows and UNIX if failure occurs.
status::Code Apply(const base::FilePath& old_path,
const base::FilePath& patch_path,
const base::FilePath& new_path,